From 5ef8b721c6e6a3c1b03a08dc94c4ea014e657398 Mon Sep 17 00:00:00 2001 From: iamlinjunhong <1030420200@qq.com> Date: Wed, 9 Sep 2026 16:17:41 +0800 Subject: [PATCH 001/202] refactor(udf): replace legacy Python service with Arrow runtime --- cmd/mo-service/config.go | 4 +- cmd/mo-service/main.go | 4 +- docs/cn/stream_transport_fast_upload_plan.md | 2 +- .../python-udf-server.toml | 2 +- .../python-udf-server.toml | 2 +- optools/images/pythonserver/Dockerfile | 13 +- pkg/cnservice/options.go | 2 +- pkg/cnservice/server.go | 12 +- pkg/cnservice/server_test.go | 8 +- pkg/cnservice/types.go | 8 +- pkg/config/configuration.go | 2 +- pkg/embed/config.go | 4 +- pkg/frontend/authenticate.go | 54 +- pkg/frontend/clone_database_source.go | 28 +- pkg/sql/compile/Remote_Run_DEV_GUIDE.md | 2 +- pkg/sql/compile/remoterunServer.go | 6 +- pkg/sql/compile/sql_executor.go | 4 +- pkg/sql/plan/function/list_builtIn.go | 2 +- pkg/sql/plan/function/python_udf.go | 290 ++++- pkg/sql/plan/function/udf.go | 710 +---------- pkg/udf/Makefile | 26 - pkg/udf/protocol/protocol.go | 614 ++++++++++ pkg/udf/protocol/protocol_test.go | 175 +++ pkg/udf/pythonruntime/arrow.go | 771 ++++++++++++ pkg/udf/pythonruntime/arrow_test.go | 75 ++ pkg/udf/pythonruntime/config.go | 56 + pkg/udf/pythonruntime/gateway.go | 536 +++++++++ .../pythonruntime/pyserver/requirements.txt | 3 + pkg/udf/pythonruntime/pyserver/test_worker.py | 87 ++ pkg/udf/pythonruntime/pyserver/worker.py | 642 ++++++++++ pkg/udf/pythonruntime/supervisor.go | 113 ++ pkg/udf/pythonservice/cfg.go | 50 - pkg/udf/pythonservice/client.go | 160 --- pkg/udf/pythonservice/demo/README.md | 295 ----- pkg/udf/pythonservice/demo/README_CN.md | 293 ----- pkg/udf/pythonservice/demo/credit/__init__.py | 3 - .../pythonservice/demo/credit/detection.py | 62 - .../demo/credit/model_with_scaler | Bin 1497 -> 0 bytes pkg/udf/pythonservice/demo/ddl.sql | 1006 ---------------- .../demo/dist/detect-1.0.0-py3-none-any.whl | Bin 4152 -> 0 bytes pkg/udf/pythonservice/demo/setup.py | 12 - .../pyserver/requirements/base.txt | 12 - .../pyserver/requirements/pytorch.txt | 5 - pkg/udf/pythonservice/pyserver/server.py | 417 ------- pkg/udf/pythonservice/pyserver/udf_pb2.py | 49 - pkg/udf/pythonservice/pyserver/udf_pb2.pyi | 173 --- .../pythonservice/pyserver/udf_pb2_grpc.py | 72 -- pkg/udf/pythonservice/service.go | 122 -- pkg/udf/pythonservice/types.go | 20 - pkg/udf/runtime.go | 104 ++ pkg/udf/service.go | 50 - pkg/udf/types.go | 40 - pkg/udf/udf.pb.go | 1068 ----------------- pkg/udf/udf.proto | 126 -- pkg/udf/udf_grpc.pb.go | 137 --- pkg/vm/engine/disttae/types.go | 2 +- pkg/vm/process/process2.go | 2 +- pkg/vm/process/process_codec.go | 4 +- pkg/vm/process/types.go | 2 +- 59 files changed, 3534 insertions(+), 5009 deletions(-) delete mode 100644 pkg/udf/Makefile create mode 100644 pkg/udf/protocol/protocol.go create mode 100644 pkg/udf/protocol/protocol_test.go create mode 100644 pkg/udf/pythonruntime/arrow.go create mode 100644 pkg/udf/pythonruntime/arrow_test.go create mode 100644 pkg/udf/pythonruntime/config.go create mode 100644 pkg/udf/pythonruntime/gateway.go create mode 100644 pkg/udf/pythonruntime/pyserver/requirements.txt create mode 100644 pkg/udf/pythonruntime/pyserver/test_worker.py create mode 100644 pkg/udf/pythonruntime/pyserver/worker.py create mode 100644 pkg/udf/pythonruntime/supervisor.go delete mode 100644 pkg/udf/pythonservice/cfg.go delete mode 100644 pkg/udf/pythonservice/client.go delete mode 100644 pkg/udf/pythonservice/demo/README.md delete mode 100644 pkg/udf/pythonservice/demo/README_CN.md delete mode 100644 pkg/udf/pythonservice/demo/credit/__init__.py delete mode 100644 pkg/udf/pythonservice/demo/credit/detection.py delete mode 100644 pkg/udf/pythonservice/demo/credit/model_with_scaler delete mode 100644 pkg/udf/pythonservice/demo/ddl.sql delete mode 100644 pkg/udf/pythonservice/demo/dist/detect-1.0.0-py3-none-any.whl delete mode 100644 pkg/udf/pythonservice/demo/setup.py delete mode 100644 pkg/udf/pythonservice/pyserver/requirements/base.txt delete mode 100644 pkg/udf/pythonservice/pyserver/requirements/pytorch.txt delete mode 100644 pkg/udf/pythonservice/pyserver/server.py delete mode 100644 pkg/udf/pythonservice/pyserver/udf_pb2.py delete mode 100644 pkg/udf/pythonservice/pyserver/udf_pb2.pyi delete mode 100644 pkg/udf/pythonservice/pyserver/udf_pb2_grpc.py delete mode 100644 pkg/udf/pythonservice/service.go delete mode 100644 pkg/udf/pythonservice/types.go create mode 100644 pkg/udf/runtime.go delete mode 100644 pkg/udf/service.go delete mode 100644 pkg/udf/types.go delete mode 100644 pkg/udf/udf.pb.go delete mode 100644 pkg/udf/udf.proto delete mode 100644 pkg/udf/udf_grpc.pb.go diff --git a/cmd/mo-service/config.go b/cmd/mo-service/config.go index 228412712533f..437a1e11a0974 100644 --- a/cmd/mo-service/config.go +++ b/cmd/mo-service/config.go @@ -45,7 +45,7 @@ import ( "github.com/matrixorigin/matrixone/pkg/perfcounter" "github.com/matrixorigin/matrixone/pkg/proxy" "github.com/matrixorigin/matrixone/pkg/tnservice" - "github.com/matrixorigin/matrixone/pkg/udf/pythonservice" + "github.com/matrixorigin/matrixone/pkg/udf/pythonruntime" "github.com/matrixorigin/matrixone/pkg/util/debug/goroutine" tomlutil "github.com/matrixorigin/matrixone/pkg/util/toml" "github.com/matrixorigin/matrixone/pkg/version" @@ -124,7 +124,7 @@ type Config struct { // ProxyConfig is the config of proxy. ProxyConfig proxy.Config `toml:"proxy"` // PythonUdfServerConfig is the config of python udf server - PythonUdfServerConfig pythonservice.Config `toml:"python-udf-server"` + PythonUdfServerConfig pythonruntime.Config `toml:"python-udf-server"` // Observability parameters for the metric/trace Observability config.ObservabilityParameters `toml:"observability"` diff --git a/cmd/mo-service/main.go b/cmd/mo-service/main.go index 4f562b26c23ab..602bd8be3218a 100644 --- a/cmd/mo-service/main.go +++ b/cmd/mo-service/main.go @@ -52,7 +52,7 @@ import ( qclient "github.com/matrixorigin/matrixone/pkg/queryservice/client" "github.com/matrixorigin/matrixone/pkg/sql/compile" "github.com/matrixorigin/matrixone/pkg/tnservice" - "github.com/matrixorigin/matrixone/pkg/udf/pythonservice" + "github.com/matrixorigin/matrixone/pkg/udf/pythonruntime" "github.com/matrixorigin/matrixone/pkg/util/debug/goroutine" "github.com/matrixorigin/matrixone/pkg/util/export" "github.com/matrixorigin/matrixone/pkg/util/export/table" @@ -757,7 +757,7 @@ func startPythonUdfService(cfg *Config, stopper *stopper.Stopper) error { defer func() { finishTask(closeErr) }() roleCtx, cancelRole := serviceLifecycle.roleContext(ctx, serviceRolePython) defer cancelRole() - s, err := pythonservice.NewService(cfg.PythonUdfServerConfig) + s, err := pythonruntime.NewSupervisor(cfg.PythonUdfServerConfig) if err != nil { panic(err) } diff --git a/docs/cn/stream_transport_fast_upload_plan.md b/docs/cn/stream_transport_fast_upload_plan.md index 8e3bdb6ff4bb4..cc7227e17f513 100644 --- a/docs/cn/stream_transport_fast_upload_plan.md +++ b/docs/cn/stream_transport_fast_upload_plan.md @@ -20,7 +20,7 @@ Phase 0 — Protobuf / gRPC contract New file `proto/datastream/v1/datastream.proto` (proto3, **not** gogoproto — this proto is shared with Java, so use standard `protoc-gen-go` + -`protoc-gen-go-grpc`, same toolchain as `pkg/udf/udf.proto`, which is the +`protoc-gen-go-grpc`, using the repository's current protobuf toolchain, which is the existing real-gRPC precedent in the repo; the main `proto/*.proto` files are gogo-generated and Java-hostile). diff --git a/etc/launch-with-python-udf-server/python-udf-server.toml b/etc/launch-with-python-udf-server/python-udf-server.toml index d1a7b10adb498..53726382e8222 100644 --- a/etc/launch-with-python-udf-server/python-udf-server.toml +++ b/etc/launch-with-python-udf-server/python-udf-server.toml @@ -3,4 +3,4 @@ service-type = "PYTHON_UDF" [python-udf-server] uuid = "dd1dccb4-4d3c-41f8-b482-5251dc7a41b2" address = "127.0.0.1:50051" -path = "pkg/udf/pythonservice/pyserver" +path = "pkg/udf/pythonruntime/pyserver" diff --git a/etc/v1/launch-with-python-udf-server/python-udf-server.toml b/etc/v1/launch-with-python-udf-server/python-udf-server.toml index d1a7b10adb498..53726382e8222 100644 --- a/etc/v1/launch-with-python-udf-server/python-udf-server.toml +++ b/etc/v1/launch-with-python-udf-server/python-udf-server.toml @@ -3,4 +3,4 @@ service-type = "PYTHON_UDF" [python-udf-server] uuid = "dd1dccb4-4d3c-41f8-b482-5251dc7a41b2" address = "127.0.0.1:50051" -path = "pkg/udf/pythonservice/pyserver" +path = "pkg/udf/pythonruntime/pyserver" diff --git a/optools/images/pythonserver/Dockerfile b/optools/images/pythonserver/Dockerfile index f729b5f7718df..8871edc0006c1 100644 --- a/optools/images/pythonserver/Dockerfile +++ b/optools/images/pythonserver/Dockerfile @@ -1,8 +1,8 @@ # run from root path for debug: -# docker build -t mo-python-udf-server -f ./optools/images/pythonserver/Dockerfile ./pkg/udf/pythonservice/pyserver/ -# docker run -d --network host mo-python-udf-server +# docker build -t mo-python-udf-runtime -f ./optools/images/pythonserver/Dockerfile ./pkg/udf/pythonruntime/pyserver/ +# docker run -d --network host mo-python-udf-runtime -FROM python:3.9-bookworm +FROM python:3.12-bookworm RUN useradd -m -s /bin/bash mo @@ -14,13 +14,12 @@ WORKDIR /home/mo # && apt-get update \ # && apt-get install -y vim -COPY . . +COPY requirements.txt worker.py ./ -RUN pip install -r requirements/base.txt -RUN pip install -r requirements/pytorch.txt +RUN pip install --no-cache-dir -r requirements.txt USER mo EXPOSE 50051 -CMD [ "/bin/bash", "-c", "python -u server.py >> server.log 2>&1" ] \ No newline at end of file +CMD [ "python", "-u", "worker.py", "--address=grpc://0.0.0.0:50051" ] diff --git a/pkg/cnservice/options.go b/pkg/cnservice/options.go index 047edb1924633..3818760d65f2c 100644 --- a/pkg/cnservice/options.go +++ b/pkg/cnservice/options.go @@ -107,7 +107,7 @@ func WithMessageHandle(f func(ctx context.Context, lockService lockservice.LockService, queryClient qclient.QueryClient, hakeeper logservice.CNHAKeeperClient, - udfService udf.Service, + udfService udf.Runtime, cli client.TxnClient, aicm *defines.AutoIncrCacheManager, mAcquirer func() morpc.Message) error) Option { diff --git a/pkg/cnservice/server.go b/pkg/cnservice/server.go index 4972d264b965f..34f7725bc1e7c 100644 --- a/pkg/cnservice/server.go +++ b/pkg/cnservice/server.go @@ -71,7 +71,7 @@ import ( "github.com/matrixorigin/matrixone/pkg/txn/rpc" "github.com/matrixorigin/matrixone/pkg/txn/trace" "github.com/matrixorigin/matrixone/pkg/udf" - "github.com/matrixorigin/matrixone/pkg/udf/pythonservice" + "github.com/matrixorigin/matrixone/pkg/udf/pythonruntime" "github.com/matrixorigin/matrixone/pkg/util/address" "github.com/matrixorigin/matrixone/pkg/util/executor" v2 "github.com/matrixorigin/matrixone/pkg/util/metric/v2" @@ -179,7 +179,7 @@ func NewService( lockService lockservice.LockService, queryClient qclient.QueryClient, hakeeper logservice.CNHAKeeperClient, - udfService udf.Service, + udfService udf.Runtime, cli client.TxnClient, aicm *defines.AutoIncrCacheManager, messageAcquirer func() morpc.Message) error { @@ -240,17 +240,17 @@ func NewService( } // init UdfService - var udfServices []udf.Service + var udfServices []udf.Runtime // add python client to handle python udf if srv.cfg.PythonUdfClient.ServerAddress != "" { - var pc *pythonservice.Client - pc, err = pythonservice.NewClient(srv.cfg.PythonUdfClient) + var pc *pythonruntime.Gateway + pc, err = pythonruntime.NewGateway(srv.cfg.PythonUdfClient) if err != nil { panic(err) } udfServices = append(udfServices, pc) } - srv.udfService, err = udf.NewService(udfServices...) + srv.udfService, err = udf.NewRuntime(udfServices...) if err != nil { panic(err) } diff --git a/pkg/cnservice/server_test.go b/pkg/cnservice/server_test.go index 478fc8e933292..50dd1fc1fda0a 100644 --- a/pkg/cnservice/server_test.go +++ b/pkg/cnservice/server_test.go @@ -403,7 +403,7 @@ func Test_InitServer(t *testing.T) { lockService lockservice.LockService, queryClient qclient.QueryClient, hakeeper logservice.CNHAKeeperClient, - udfService udf.Service, + udfService udf.Runtime, cli client.TxnClient, aicm *defines.AutoIncrCacheManager, mAcquirer func() morpc.Message) error { @@ -1250,7 +1250,7 @@ func TestPipelineAdmissionRejectsRequestAlreadyReadDuringClose(t *testing.T) { lockservice.LockService, qclient.QueryClient, logservice.CNHAKeeperClient, - udf.Service, + udf.Runtime, client.TxnClient, *defines.AutoIncrCacheManager, func() morpc.Message, @@ -1350,7 +1350,7 @@ func TestFragmentedPipelineCacheSurvivesHandlerRelease(t *testing.T) { _ lockservice.LockService, _ qclient.QueryClient, _ logservice.CNHAKeeperClient, - _ udf.Service, + _ udf.Runtime, _ client.TxnClient, _ *defines.AutoIncrCacheManager, _ func() morpc.Message, @@ -1476,7 +1476,7 @@ func TestServiceCloseCancelsAdmittedPipeline(t *testing.T) { _ lockservice.LockService, _ qclient.QueryClient, _ logservice.CNHAKeeperClient, - _ udf.Service, + _ udf.Runtime, _ client.TxnClient, _ *defines.AutoIncrCacheManager, _ func() morpc.Message, diff --git a/pkg/cnservice/types.go b/pkg/cnservice/types.go index d81776f7d5b01..c7ea467797dbb 100644 --- a/pkg/cnservice/types.go +++ b/pkg/cnservice/types.go @@ -55,7 +55,7 @@ import ( "github.com/matrixorigin/matrixone/pkg/txn/rpc" "github.com/matrixorigin/matrixone/pkg/txn/trace" "github.com/matrixorigin/matrixone/pkg/udf" - "github.com/matrixorigin/matrixone/pkg/udf/pythonservice" + "github.com/matrixorigin/matrixone/pkg/udf/pythonruntime" "github.com/matrixorigin/matrixone/pkg/util" "github.com/matrixorigin/matrixone/pkg/util/address" "github.com/matrixorigin/matrixone/pkg/util/executor" @@ -330,7 +330,7 @@ type Config struct { // "working", "draining" and "drained". InitWorkState string `toml:"init-work-state"` - PythonUdfClient pythonservice.ClientConfig `toml:"python-udf-client"` + PythonUdfClient pythonruntime.ClientConfig `toml:"python-udf-client"` // LogtailUpdateWorkerFactor is the times of CPU number of this node // to start update workers. @@ -770,7 +770,7 @@ type service struct { lockService lockservice.LockService, queryClient qclient.QueryClient, hakeeper logservice.CNHAKeeperClient, - udfService udf.Service, + udfService udf.Runtime, cli client.TxnClient, aicm *defines.AutoIncrCacheManager, messageAcquirer func() morpc.Message) error @@ -803,7 +803,7 @@ type service struct { queryClient qclient.QueryClient queryWork queryWorkLifecycle // udfService is used to handle non-sql udf - udfService udf.Service + udfService udf.Runtime bootstrapMu sync.RWMutex bootstrapService bootstrap.Service diff --git a/pkg/config/configuration.go b/pkg/config/configuration.go index 390c5dd26ebcc..f57a5ab606b3a 100644 --- a/pkg/config/configuration.go +++ b/pkg/config/configuration.go @@ -1452,7 +1452,7 @@ type ParameterUnit struct { // QueryClient instance QueryClient qclient.QueryClient - UdfService udf.Service + UdfService udf.Runtime // HAKeeper client, which is used to get connection ID // from HAKeeper currently. diff --git a/pkg/embed/config.go b/pkg/embed/config.go index 36ae8c0ebd5e5..c1fcd1c3c9ff0 100644 --- a/pkg/embed/config.go +++ b/pkg/embed/config.go @@ -40,7 +40,7 @@ import ( "github.com/matrixorigin/matrixone/pkg/perfcounter" "github.com/matrixorigin/matrixone/pkg/proxy" "github.com/matrixorigin/matrixone/pkg/tnservice" - "github.com/matrixorigin/matrixone/pkg/udf/pythonservice" + "github.com/matrixorigin/matrixone/pkg/udf/pythonruntime" "github.com/matrixorigin/matrixone/pkg/util" "github.com/matrixorigin/matrixone/pkg/util/debug/goroutine" tomlutil "github.com/matrixorigin/matrixone/pkg/util/toml" @@ -95,7 +95,7 @@ type ServiceConfig struct { // ProxyConfig is the config of proxy. ProxyConfig proxy.Config `toml:"proxy"` // PythonUdfServerConfig is the config of python udf server - PythonUdfServerConfig pythonservice.Config `toml:"python-udf-server"` + PythonUdfServerConfig pythonruntime.Config `toml:"python-udf-server"` // Observability parameters for the metric/trace Observability config.ObservabilityParameters `toml:"observability"` diff --git a/pkg/frontend/authenticate.go b/pkg/frontend/authenticate.go index 333dd2781bf71..6232e9d05b87a 100644 --- a/pkg/frontend/authenticate.go +++ b/pkg/frontend/authenticate.go @@ -66,6 +66,7 @@ import ( "github.com/matrixorigin/matrixone/pkg/stage" "github.com/matrixorigin/matrixone/pkg/stage/stageutil" "github.com/matrixorigin/matrixone/pkg/taskservice" + "github.com/matrixorigin/matrixone/pkg/udf" "github.com/matrixorigin/matrixone/pkg/util/metric/mometric" v2 "github.com/matrixorigin/matrixone/pkg/util/metric/v2" "github.com/matrixorigin/matrixone/pkg/util/sysview" @@ -4917,7 +4918,6 @@ type rmPkg func(path string) error func doDropFunction(ctx context.Context, ses *Session, df *tree.DropFunction, rm rmPkg) (err error) { var sql string var argstr string - var bodyStr string var checkDatabase string var dbName string var dbExists bool @@ -4988,10 +4988,6 @@ func doDropFunction(ctx context.Context, ses *Session, df *tree.DropFunction, rm if err != nil { return err } - bodyStr, err = erArray[0].GetString(ctx, i, 2) - if err != nil { - return err - } argList := make([]*function.Arg, 0) json.Unmarshal([]byte(argstr), &argList) if len(argList) == len(df.Args) { @@ -5013,11 +5009,6 @@ func doDropFunction(ctx context.Context, ses *Session, df *tree.DropFunction, rm if rtnErr != nil { return rtnErr } - u := &function.NonSqlUdfBody{} - if json.Unmarshal([]byte(bodyStr), u) == nil && u.Import { - rm(u.Body) - } - return rtnErr } err = handleArgMatch() @@ -5040,7 +5031,6 @@ func doDropFunction(ctx context.Context, ses *Session, df *tree.DropFunction, rm func doDropFunctionWithDB(ctx context.Context, ses *Session, stmt tree.Statement, rm rmPkg) (err error) { var sql string - var bodyStr string var funcId int64 var erArray []ExecResult var dbName string @@ -5081,11 +5071,6 @@ func doDropFunctionWithDB(ctx context.Context, ses *Session, stmt tree.Statement if err != nil { return err } - bodyStr, err = erArray[0].GetString(ctx, i, 1) - if err != nil { - return err - } - handleArgMatch := func() (rtnErr error) { sql = fmt.Sprintf(deleteUserDefinedFunctionFormat, funcId) @@ -5094,11 +5079,6 @@ func doDropFunctionWithDB(ctx context.Context, ses *Session, stmt tree.Statement return rtnErr } - u := &function.NonSqlUdfBody{} - if json.Unmarshal([]byte(bodyStr), u) == nil && u.Import { - rm(u.Body) - } - return rtnErr } @@ -11605,31 +11585,17 @@ func InitFunction(ses *Session, execCtx *ExecCtx, tenant *TenantInfo, cf *tree.C body = cf.Body } else { if cf.Import { - // check - if cf.Language == string(tree.PYTHON) { - if !strings.HasSuffix(cf.Body, ".py") && - !strings.HasSuffix(cf.Body, ".whl") { - return moerr.NewInvalidInput(execCtx.reqCtx, "file '"+cf.Body+"', only support '*.py', '*.whl'") - } - if strings.HasSuffix(cf.Body, ".whl") { - dotIdx := strings.LastIndex(cf.Handler, ".") - if dotIdx < 1 { - return moerr.NewInvalidInput(execCtx.reqCtx, "handler '"+cf.Handler+"', when you import a *.whl, the handler should be in the format of '.'") - } - } - } - // upload - storageDir := string(cf.Name.Name.ObjectName) + "_" + strings.Join(typeList, "-") + "_" - cf.Body, err = Upload(ses, execCtx, cf.Body, storageDir) - if err != nil { - return err - } + return moerr.NewNotSupportedNoCtx("Python artifact import requires the immutable artifact catalog") } - nb := function.NonSqlUdfBody{ - Handler: cf.Handler, - Import: cf.Import, - Body: cf.Body, + nb := function.PythonRoutineBody{ + Handler: cf.Handler, + Source: cf.Body, + Mode: "SCALAR", + NullPolicy: udf.NullCallHandler, + ABIContract: udf.PythonABIContract, + AdapterVersion: udf.PythonAdapterVersion, + SDKVersion: udf.PythonSDKVersion, } var byt []byte byt, err = json.Marshal(nb) diff --git a/pkg/frontend/clone_database_source.go b/pkg/frontend/clone_database_source.go index c6ed89b3c4e37..944fb95f3e05a 100644 --- a/pkg/frontend/clone_database_source.go +++ b/pkg/frontend/clone_database_source.go @@ -31,6 +31,7 @@ import ( "github.com/matrixorigin/matrixone/pkg/sql/parsers/dialect/mysql" "github.com/matrixorigin/matrixone/pkg/sql/parsers/tree" "github.com/matrixorigin/matrixone/pkg/sql/plan/function" + "github.com/matrixorigin/matrixone/pkg/udf" ) type cloneDatabaseSource struct { @@ -659,14 +660,37 @@ func validateCloneUserDefinedFunctions(functions []userDefinedFunctionDefinition continue } - var body function.NonSqlUdfBody - if json.Unmarshal([]byte(definition.body), &body) == nil && body.Import { + var raw map[string]json.RawMessage + if json.Unmarshal([]byte(definition.body), &raw) != nil { + return moerr.NewNotSupportedNoCtxf( + "CREATE DATABASE CLONE with malformed %s function %s is not supported", + definition.lang, + definition.name, + ) + } + var importedBody bool + if value, ok := raw["import"]; ok { + _ = json.Unmarshal(value, &importedBody) + } + if importedBody { return moerr.NewNotSupportedNoCtxf( "CREATE DATABASE CLONE with imported %s function %s is not supported: imported UDF packages are not snapshot-versioned", definition.lang, definition.name, ) } + if strings.EqualFold(definition.lang, string(tree.PYTHON)) { + var body function.PythonRoutineBody + if err := json.Unmarshal([]byte(definition.body), &body); err != nil || + body.Handler == "" || (body.Source == "" && body.ArtifactDigest == "") || + body.ABIContract != udf.PythonABIContract || body.AdapterVersion != udf.PythonAdapterVersion { + return moerr.NewNotSupportedNoCtxf( + "CREATE DATABASE CLONE with incomplete %s function %s is not supported", + definition.lang, + definition.name, + ) + } + } } return nil } diff --git a/pkg/sql/compile/Remote_Run_DEV_GUIDE.md b/pkg/sql/compile/Remote_Run_DEV_GUIDE.md index 5e1571e7fbb1a..6b722e922a37a 100644 --- a/pkg/sql/compile/Remote_Run_DEV_GUIDE.md +++ b/pkg/sql/compile/Remote_Run_DEV_GUIDE.md @@ -166,7 +166,7 @@ func CnServerMessageHandler( lockService lockservice.LockService, queryClient qclient.QueryClient, HaKeeper logservice.CNHAKeeperClient, - udfService udf.Service, + udfService udf.Runtime, txnClient client.TxnClient, autoIncreaseCM *defines.AutoIncrCacheManager, messageAcquirer func() morpc.Message) (err error) { diff --git a/pkg/sql/compile/remoterunServer.go b/pkg/sql/compile/remoterunServer.go index 9fa9fe9a87510..16da59abefa04 100644 --- a/pkg/sql/compile/remoterunServer.go +++ b/pkg/sql/compile/remoterunServer.go @@ -81,7 +81,7 @@ func CnServerMessageHandler( cs morpc.ClientSession, storageEngine engine.Engine, fileService fileservice.FileService, lockService lockservice.LockService, queryClient qclient.QueryClient, - HaKeeper logservice.CNHAKeeperClient, udfService udf.Service, txnClient client.TxnClient, + HaKeeper logservice.CNHAKeeperClient, udfService udf.Runtime, txnClient client.TxnClient, autoIncreaseCM *defines.AutoIncrCacheManager, messageAcquirer func() morpc.Message) (err error) { @@ -799,7 +799,7 @@ type cnInformation struct { lockService lockservice.LockService queryClient qclient.QueryClient hakeeper logservice.CNHAKeeperClient - udfService udf.Service + udfService udf.Runtime aicm *defines.AutoIncrCacheManager } @@ -880,7 +880,7 @@ func newMessageReceiverOnServer( lockService lockservice.LockService, queryClient qclient.QueryClient, hakeeper logservice.CNHAKeeperClient, - udfService udf.Service, + udfService udf.Runtime, txnClient client.TxnClient, aicm *defines.AutoIncrCacheManager, colexecServer *colexec.Server) (messageReceiverOnServer, error) { diff --git a/pkg/sql/compile/sql_executor.go b/pkg/sql/compile/sql_executor.go index c91d4a002ce05..244793384eca7 100644 --- a/pkg/sql/compile/sql_executor.go +++ b/pkg/sql/compile/sql_executor.go @@ -60,7 +60,7 @@ type sqlExecutor struct { ls lockservice.LockService qc qclient.QueryClient hakeeper logservice.CNHAKeeperClient - us udf.Service + us udf.Runtime buf *buffer.Buffer taskservice taskservice.TaskService } @@ -111,7 +111,7 @@ func NewSQLExecutor( fs fileservice.FileService, qc qclient.QueryClient, hakeeper logservice.CNHAKeeperClient, - us udf.Service, + us udf.Runtime, taskService taskservice.TaskService, ) executor.SQLExecutor { v, ok := runtime.ServiceRuntime(qc.ServiceID()).GetGlobalVariables(runtime.LockService) diff --git a/pkg/sql/plan/function/list_builtIn.go b/pkg/sql/plan/function/list_builtIn.go index af94c6949f76b..aff09b7cfdffe 100644 --- a/pkg/sql/plan/function/list_builtIn.go +++ b/pkg/sql/plan/function/list_builtIn.go @@ -16229,7 +16229,7 @@ var supportedOthersBuiltIns = []FuncNew{ // function `python_user_defined_function` { functionId: PYTHON_UDF, - class: plan.Function_INTERNAL | plan.Function_STRICT, + class: plan.Function_INTERNAL, layout: STANDARD_FUNCTION, checkFn: checkPythonUdf, Overloads: []overload{ diff --git a/pkg/sql/plan/function/python_udf.go b/pkg/sql/plan/function/python_udf.go index c26bdf63d7375..3e45aafe8c590 100644 --- a/pkg/sql/plan/function/python_udf.go +++ b/pkg/sql/plan/function/python_udf.go @@ -1,25 +1,25 @@ -// Copyright 2023 Matrix Origin +// Copyright 2026 Matrix Origin // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. +// http://www.apache.org/licenses/LICENSE-2.0 package function import ( "encoding/json" + "fmt" + "strconv" + "github.com/google/uuid" + "github.com/matrixorigin/matrixone/pkg/common/mpool" "github.com/matrixorigin/matrixone/pkg/container/types" "github.com/matrixorigin/matrixone/pkg/container/vector" + "github.com/matrixorigin/matrixone/pkg/defines" "github.com/matrixorigin/matrixone/pkg/udf" + "github.com/matrixorigin/matrixone/pkg/udf/protocol" "github.com/matrixorigin/matrixone/pkg/vm/process" ) @@ -70,57 +70,253 @@ func pythonUdfRetType(parameters []types.Type) types.Type { // param parameters has two parts: // 1. parameters[0]: const vector udf // 2. parameters[1:]: data vectors +// +// The SQL executor normally applies CASE/selection compaction before this +// function is called. The NULL policy is still enforced here because it is +// part of the persisted Python routine contract and must not be inferred from +// the generic builtin STRICT bit. func runPythonUdf(parameters []*vector.Vector, result vector.FunctionResultWrapper, proc *process.Process, length int, selectList *FunctionSelectList) error { - // udf with context - u := &UdfWithContext{} - bytes, _ := vector.GenerateFunctionStrParameter(parameters[0]).GetStrValue(0) - err := json.Unmarshal(bytes, u) + if len(parameters) == 0 || parameters[0] == nil { + return fmt.Errorf("python runtime: missing routine descriptor") + } + if length < 0 { + return fmt.Errorf("python runtime: negative input length %d", length) + } + + routine := &UdfWithContext{} + encoded, isNull := vector.GenerateFunctionStrParameter(parameters[0]).GetStrValue(0) + if isNull { + return fmt.Errorf("python runtime: routine descriptor is NULL") + } + if err := json.Unmarshal(encoded, routine); err != nil { + return fmt.Errorf("python runtime: decode routine descriptor: %w", err) + } + if routine.Udf == nil { + return fmt.Errorf("python runtime: routine descriptor has no function") + } + + body := PythonRoutineBody{} + if err := json.Unmarshal([]byte(routine.Body), &body); err != nil { + return fmt.Errorf("python runtime: decode Python routine body: %w", err) + } + if body.Handler == "" || body.Source == "" { + return fmt.Errorf("python runtime: routine handler and source are required") + } + if body.ABIContract != udf.PythonABIContract || body.AdapterVersion != udf.PythonAdapterVersion { + return fmt.Errorf("python runtime: unsupported Python ABI contract %q/%q", body.ABIContract, body.AdapterVersion) + } + argTypes, err := routineArgumentTypes(routine) if err != nil { return err } + if len(parameters)-1 != len(argTypes) { + return fmt.Errorf("python runtime: routine has %d arguments, received %d", len(argTypes), len(parameters)-1) + } + + if length == 0 { + return result.PreExtendAndReset(0) + } + for index, input := range parameters[1:] { + if input == nil || (!input.IsConst() && input.Length() < length) { + return fmt.Errorf("python runtime: input vector %d is shorter than invocation length", index) + } + } + if selectList != nil && len(selectList.SelectList) < length { + return fmt.Errorf("python runtime: selection list is shorter than invocation length") + } + + selected := make([]int64, 0, length) + for row := 0; row < length; row++ { + if selectList != nil && len(selectList.SelectList) > row && !selectList.SelectList[row] { + continue + } + if body.NullPolicy == udf.NullReturnNull && hasNullInput(parameters[1:], row) { + continue + } + selected = append(selected, int64(row)) + } + if len(selected) == 0 { + if err := result.PreExtendAndReset(length); err != nil { + return err + } + result.GetResultVector().SetAllNulls(length) + result.GetResultVector().SetLength(length) + return nil + } + + inputs := parameters[1:] + var compacted []*vector.Vector + if len(selected) != length { + compacted = make([]*vector.Vector, len(inputs)) + for i, input := range inputs { + if input == nil { + return fmt.Errorf("python runtime: input vector %d is nil", i) + } + compacted[i] = vector.NewOffHeapVecWithType(*input.GetType()) + compacted[i].SetIsBin(input.GetIsBin()) + if err := compacted[i].Union(input, selected, proc.Mp()); err != nil { + freeVectors(compacted, proc.Mp()) + return fmt.Errorf("python runtime: compact input %d: %w", i, err) + } + } + inputs = compacted + defer freeVectors(compacted, proc.Mp()) + } - // request - body := &NonSqlUdfBody{} - err = json.Unmarshal([]byte(u.Body), body) + callResult := result + var temporary vector.FunctionResultWrapper + if len(selected) != length { + temporary = vector.NewFunctionResultWrapper(routine.GetRetType(), proc.Mp()) + callResult = temporary + defer temporary.Free() + } + + accountID, err := defines.GetAccountId(proc.Ctx) if err != nil { - return err + return fmt.Errorf("python runtime: resolve account: %w", err) } - request := &udf.Request{ - Udf: &udf.Udf{ - Handler: body.Handler, - IsImport: body.Import, - Body: body.Body, - RetType: t2DataType[u.GetRetType().Oid], - Language: udf.LanguagePython, - Db: u.Db, - ModifiedTime: u.ModifiedTime, - }, - Vectors: make([]*udf.DataVector, len(parameters)-1), - Length: int64(length), - Type: udf.RequestType_DataRequest, - Context: u.Context, - } - for i := 1; i < len(parameters); i++ { - dataVector, _ := vector2DataVector(parameters[i]) - request.Vectors[i-1] = dataVector - } - - // getPkg - reader := &DefaultPkgReader{ - Proc: proc, - } - - // run - response, err := proc.Base.UdfService.Run(proc.Ctx, request, reader) + tuple, err := invocationTuple(routine.Context, proc.QueryId(), accountID) if err != nil { return err } - - // response - err = writeResponse(response, result) - if err != nil { + invocation := &udf.Invocation{ + Language: udf.LanguagePython, + Handler: body.Handler, + Source: body.Source, + Args: argTypes, + ReturnType: routine.GetRetType(), + Inputs: inputs, + Length: len(selected), + Mode: body.Mode, + NullPolicy: body.NullPolicy, + ABIContract: body.ABIContract, + AdapterVersion: body.AdapterVersion, + SDKVersion: body.SDKVersion, + Context: cloneContext(routine.Context), + Tuple: tuple, + } + if err := proc.Base.UdfService.Execute(proc.Ctx, invocation, callResult, proc.Mp()); err != nil { return err } + if len(selected) == length { + return nil + } + if err := result.PreExtendAndReset(length); err != nil { + return err + } + full := result.GetResultVector() + full.ResetWithSameType() + nullResult := vector.NewConstNull(routine.GetRetType(), 1, proc.Mp()) + defer nullResult.Free(proc.Mp()) + selectedRow := int64(0) + for row := 0; row < length; row++ { + if selectedRow < int64(len(selected)) && selected[selectedRow] == int64(row) { + if err := full.UnionOne(callResult.GetResultVector(), selectedRow, proc.Mp()); err != nil { + return err + } + selectedRow++ + continue + } + if err := full.UnionOne(nullResult, 0, proc.Mp()); err != nil { + return err + } + } return nil } + +func hasNullInput(inputs []*vector.Vector, row int) bool { + for _, input := range inputs { + if input == nil || input.IsNull(uint64(row)) { + return true + } + } + return false +} + +func routineArgumentTypes(routine *UdfWithContext) ([]types.Type, error) { + if len(routine.ArgsType) != 0 || len(routine.Args) == 0 { + return append([]types.Type(nil), routine.ArgsType...), nil + } + args := make([]types.Type, len(routine.Args)) + for i, arg := range routine.Args { + if arg == nil { + return nil, fmt.Errorf("python runtime: routine argument %d is nil", i) + } + typ, ok := types.Types[arg.Type] + if !ok { + return nil, fmt.Errorf("python runtime: unknown routine argument type %q", arg.Type) + } + args[i] = typ.ToType() + } + return args, nil +} + +func cloneContext(input map[string]string) map[string]string { + if len(input) == 0 { + return nil + } + output := make(map[string]string, len(input)) + for key, value := range input { + output[key] = value + } + return output +} + +func freeVectors(vectors []*vector.Vector, mp *mpool.MPool) { + for _, vector := range vectors { + if vector != nil { + vector.Free(mp) + } + } +} + +func invocationTuple(context map[string]string, queryID string, accountID uint32) (protocol.FencingTuple, error) { + statementID := context["statement_id"] + if statementID == "" { + statementID = queryID + } + if statementID == "" { + id, err := uuid.NewV7() + if err != nil { + return protocol.FencingTuple{}, fmt.Errorf("python runtime: create statement fence: %w", err) + } + statementID = id.String() + } + groupID := context["group_id"] + if groupID == "" { + groupID = statementID + "/python" + } + invocationID, err := uuid.NewV7() + if err != nil { + return protocol.FencingTuple{}, fmt.Errorf("python runtime: create invocation fence: %w", err) + } + groupEpoch, err := positiveContextUint(context, "group_epoch", 1) + if err != nil { + return protocol.FencingTuple{}, err + } + leaseEpoch, err := positiveContextUint(context, "lease_epoch", 1) + if err != nil { + return protocol.FencingTuple{}, err + } + return protocol.FencingTuple{ + AccountID: uint64(accountID), + StatementID: statementID, + GroupID: groupID, + GroupEpoch: groupEpoch, + InvocationID: invocationID.String(), + LeaseEpoch: leaseEpoch, + }, nil +} + +func positiveContextUint(context map[string]string, key string, fallback uint64) (uint64, error) { + value := context[key] + if value == "" { + return fallback, nil + } + parsed, err := strconv.ParseUint(value, 10, 64) + if err != nil || parsed == 0 { + return 0, fmt.Errorf("python runtime: invalid %s %q", key, value) + } + return parsed, nil +} diff --git a/pkg/sql/plan/function/udf.go b/pkg/sql/plan/function/udf.go index feb44e2f46227..2f427b4349005 100644 --- a/pkg/sql/plan/function/udf.go +++ b/pkg/sql/plan/function/udf.go @@ -1,62 +1,52 @@ -// Copyright 2023 Matrix Origin +// Copyright 2026 Matrix Origin // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. +// http://www.apache.org/licenses/LICENSE-2.0 package function import ( - "context" "encoding/json" - "io" - "time" "github.com/google/uuid" - "github.com/matrixorigin/matrixone/pkg/common/moerr" - "github.com/matrixorigin/matrixone/pkg/container/bytejson" "github.com/matrixorigin/matrixone/pkg/container/types" - "github.com/matrixorigin/matrixone/pkg/container/vector" - "github.com/matrixorigin/matrixone/pkg/fileservice" "github.com/matrixorigin/matrixone/pkg/pb/plan" - "github.com/matrixorigin/matrixone/pkg/udf" - "github.com/matrixorigin/matrixone/pkg/vm/process" - "golang.org/x/sync/errgroup" ) type Udf struct { - // sql string, or json string of NonSqlUdfBody - Body string `json:"body"` - Language string `json:"language"` - RetType string `json:"rettype"` - Args []*Arg `json:"args"` - Db string `json:"db"` - ModifiedTime string `json:"modified_time"` - SQLMode *string `json:"-"` - - ArgsType []types.Type `json:"-"` + Body string `json:"body"` + Language string `json:"language"` + RetType string `json:"rettype"` + Args []*Arg `json:"args"` + Db string `json:"db"` + ModifiedTime string `json:"modified_time"` + SQLMode *string `json:"-"` + ArgsType []types.Type `json:"-"` } type UdfWithContext struct { *Udf `json:"udf"` - Context map[string]string `json:"context"` // context map + Context map[string]string `json:"context"` } -type NonSqlUdfBody struct { - Handler string `json:"handler"` - Import bool `json:"import"` - Body string `json:"body"` +// PythonRoutineBody is the single supported Python routine definition. The +// catalog must persist mode and NULL policy with the source so execution does +// not infer either from a generic SQL function class bit. +type PythonRoutineBody struct { + Handler string `json:"handler"` + Source string `json:"source,omitempty"` + Mode string `json:"mode"` + NullPolicy string `json:"null_policy"` + ABIContract string `json:"abi_contract"` + AdapterVersion string `json:"adapter_version"` + ArtifactDigest string `json:"artifact_digest,omitempty"` + EnvironmentDigest string `json:"environment_digest,omitempty"` + SDKVersion string `json:"sdk_version,omitempty"` } -// Arg of Udf type Arg struct { Name string `json:"name"` Type string `json:"type"` @@ -64,61 +54,28 @@ type Arg struct { func (u *Udf) GetPlanExpr() *plan.Expr { uid, _ := uuid.NewV7() - bytes, _ := json.Marshal(&UdfWithContext{ - Udf: u, - Context: map[string]string{ - "queryId": uid.String(), - }, - }) - return &plan.Expr{ - Typ: *type2PlanType(types.T_text.ToType()), - Expr: &plan.Expr_Lit{ - Lit: &plan.Literal{ - Isnull: false, - Value: &plan.Literal_Sval{ - Sval: string(bytes), - }, - }, - }, - } + data, _ := json.Marshal(&UdfWithContext{Udf: u, Context: map[string]string{"statement_id": uid.String()}}) + return &plan.Expr{Typ: *type2PlanType(types.T_text.ToType()), Expr: &plan.Expr_Lit{Lit: &plan.Literal{Isnull: false, Value: &plan.Literal_Sval{Sval: string(data)}}}} } func (u *Udf) GetArgsPlanType() []*plan.Type { - typ := u.GetArgsType() - ptyp := make([]*plan.Type, len(typ)) - for i, t := range typ { - ptyp[i] = type2PlanType(t) + result := make([]*plan.Type, len(u.ArgsType)) + for i, typ := range u.ArgsType { + result[i] = type2PlanType(typ) } - return ptyp + return result } - -func (u *Udf) GetRetPlanType() *plan.Type { - typ := u.GetRetType() - return type2PlanType(typ) -} - -func (u *Udf) GetArgsType() []types.Type { - return u.ArgsType -} - -func (u *Udf) GetRetType() types.Type { - return types.Types[u.RetType].ToType() -} - +func (u *Udf) GetRetPlanType() *plan.Type { return type2PlanType(u.GetRetType()) } +func (u *Udf) GetArgsType() []types.Type { return u.ArgsType } +func (u *Udf) GetRetType() types.Type { return types.Types[u.RetType].ToType() } func type2PlanType(typ types.Type) *plan.Type { - return &plan.Type{ - Id: int32(typ.Oid), - Width: typ.Width, - Scale: typ.Scale, - Charset: uint32(typ.Charset), - } + return &plan.Type{Id: int32(typ.Oid), Width: typ.Width, Scale: typ.Scale, Charset: uint32(typ.Charset)} } func UdfArgTypeMatch(from []types.Type, to []types.T) (bool, int) { - sta, cost := tryToMatch(from, to) - return sta != matchFailed, cost + status, cost := tryToMatch(from, to) + return status != matchFailed, cost } - func UdfArgTypeCast(from []types.Type, to []types.T) []types.Type { castType := make([]types.Type, len(from)) for i := range castType { @@ -131,598 +88,3 @@ func UdfArgTypeCast(from []types.Type, to []types.T) []types.Type { } return castType } - -func getDataFromDataVector(v *udf.DataVector, i int) *udf.Data { - if v == nil { - return nil - } - if v.Const { - return v.Data[0] - } - return v.Data[i] -} - -func vector2DataVector(v *vector.Vector) (*udf.DataVector, error) { - if v == nil { - return nil, nil - } - dv := &udf.DataVector{ - Const: v.IsConst(), - Length: int64(v.Length()), - Type: t2DataType[v.GetType().Oid], - Scale: v.GetType().Scale, - } - size := v.Length() - if dv.Const { - size = 1 - } - dv.Data = make([]*udf.Data, size) - switch v.GetType().Oid { - case types.T_bool: - p := vector.GenerateFunctionFixedTypeParameter[bool](v) - for i := 0; i < size; i++ { - val, isNull := p.GetValue(uint64(i)) - if !isNull { - dv.Data[i] = &udf.Data{Val: &udf.Data_BoolVal{BoolVal: val}} - } - } - case types.T_int8: - p := vector.GenerateFunctionFixedTypeParameter[int8](v) - for i := 0; i < size; i++ { - val, isNull := p.GetValue(uint64(i)) - if !isNull { - dv.Data[i] = &udf.Data{Val: &udf.Data_IntVal{IntVal: int32(val)}} - } - } - case types.T_int16: - p := vector.GenerateFunctionFixedTypeParameter[int16](v) - for i := 0; i < size; i++ { - val, isNull := p.GetValue(uint64(i)) - if !isNull { - dv.Data[i] = &udf.Data{Val: &udf.Data_IntVal{IntVal: int32(val)}} - } - } - case types.T_int32: - p := vector.GenerateFunctionFixedTypeParameter[int32](v) - for i := 0; i < size; i++ { - val, isNull := p.GetValue(uint64(i)) - if !isNull { - dv.Data[i] = &udf.Data{Val: &udf.Data_IntVal{IntVal: val}} - } - } - case types.T_int64: - p := vector.GenerateFunctionFixedTypeParameter[int64](v) - for i := 0; i < size; i++ { - val, isNull := p.GetValue(uint64(i)) - if !isNull { - dv.Data[i] = &udf.Data{Val: &udf.Data_Int64Val{Int64Val: val}} - } - } - case types.T_uint8: - p := vector.GenerateFunctionFixedTypeParameter[uint8](v) - for i := 0; i < size; i++ { - val, isNull := p.GetValue(uint64(i)) - if !isNull { - dv.Data[i] = &udf.Data{Val: &udf.Data_UintVal{UintVal: uint32(val)}} - } - } - case types.T_uint16: - p := vector.GenerateFunctionFixedTypeParameter[uint16](v) - for i := 0; i < size; i++ { - val, isNull := p.GetValue(uint64(i)) - if !isNull { - dv.Data[i] = &udf.Data{Val: &udf.Data_UintVal{UintVal: uint32(val)}} - } - } - case types.T_uint32: - p := vector.GenerateFunctionFixedTypeParameter[uint32](v) - for i := 0; i < size; i++ { - val, isNull := p.GetValue(uint64(i)) - if !isNull { - dv.Data[i] = &udf.Data{Val: &udf.Data_UintVal{UintVal: val}} - } - } - case types.T_uint64: - p := vector.GenerateFunctionFixedTypeParameter[uint64](v) - for i := 0; i < size; i++ { - val, isNull := p.GetValue(uint64(i)) - if !isNull { - dv.Data[i] = &udf.Data{Val: &udf.Data_Uint64Val{Uint64Val: val}} - } - } - case types.T_float32: - p := vector.GenerateFunctionFixedTypeParameter[float32](v) - for i := 0; i < size; i++ { - val, isNull := p.GetValue(uint64(i)) - if !isNull { - dv.Data[i] = &udf.Data{Val: &udf.Data_FloatVal{FloatVal: val}} - } - } - case types.T_float64: - p := vector.GenerateFunctionFixedTypeParameter[float64](v) - for i := 0; i < size; i++ { - val, isNull := p.GetValue(uint64(i)) - if !isNull { - dv.Data[i] = &udf.Data{Val: &udf.Data_DoubleVal{DoubleVal: val}} - } - } - case types.T_char, types.T_varchar, types.T_text, types.T_datalink: - p := vector.GenerateFunctionStrParameter(v) - for i := 0; i < size; i++ { - val, isNull := p.GetStrValue(uint64(i)) - if !isNull { - dv.Data[i] = &udf.Data{Val: &udf.Data_StringVal{StringVal: string(val)}} - } - } - case types.T_json: - p := vector.GenerateFunctionStrParameter(v) - for i := 0; i < size; i++ { - val, isNull := p.GetStrValue(uint64(i)) - if !isNull { - dv.Data[i] = &udf.Data{Val: &udf.Data_StringVal{StringVal: types.DecodeJson(val).String()}} - } - } - case types.T_uuid: - p := vector.GenerateFunctionFixedTypeParameter[types.Uuid](v) - for i := 0; i < size; i++ { - val, isNull := p.GetValue(uint64(i)) - if !isNull { - dv.Data[i] = &udf.Data{Val: &udf.Data_StringVal{StringVal: val.String()}} - } - } - case types.T_time: - p := vector.GenerateFunctionFixedTypeParameter[types.Time](v) - for i := 0; i < size; i++ { - val, isNull := p.GetValue(uint64(i)) - if !isNull { - dv.Data[i] = &udf.Data{Val: &udf.Data_StringVal{StringVal: val.String2(v.GetType().Scale)}} - } - } - case types.T_date: - p := vector.GenerateFunctionFixedTypeParameter[types.Date](v) - for i := 0; i < size; i++ { - val, isNull := p.GetValue(uint64(i)) - if !isNull { - dv.Data[i] = &udf.Data{Val: &udf.Data_StringVal{StringVal: val.String()}} - } - } - case types.T_datetime: - p := vector.GenerateFunctionFixedTypeParameter[types.Datetime](v) - for i := 0; i < size; i++ { - val, isNull := p.GetValue(uint64(i)) - if !isNull { - dv.Data[i] = &udf.Data{Val: &udf.Data_StringVal{StringVal: val.String2(v.GetType().Scale)}} - } - } - case types.T_timestamp: - p := vector.GenerateFunctionFixedTypeParameter[types.Timestamp](v) - for i := 0; i < size; i++ { - val, isNull := p.GetValue(uint64(i)) - if !isNull { - dv.Data[i] = &udf.Data{Val: &udf.Data_StringVal{StringVal: val.String2(time.Local, v.GetType().Scale)}} - } - } - case types.T_decimal64: - p := vector.GenerateFunctionFixedTypeParameter[types.Decimal64](v) - for i := 0; i < size; i++ { - val, isNull := p.GetValue(uint64(i)) - if !isNull { - dv.Data[i] = &udf.Data{Val: &udf.Data_StringVal{StringVal: val.Format(v.GetType().Scale)}} - } - } - case types.T_decimal128: - p := vector.GenerateFunctionFixedTypeParameter[types.Decimal128](v) - for i := 0; i < size; i++ { - val, isNull := p.GetValue(uint64(i)) - if !isNull { - dv.Data[i] = &udf.Data{Val: &udf.Data_StringVal{StringVal: val.Format(v.GetType().Scale)}} - } - } - case types.T_binary, types.T_varbinary, types.T_blob: - p := vector.GenerateFunctionStrParameter(v) - for i := 0; i < size; i++ { - val, isNull := p.GetStrValue(uint64(i)) - if !isNull { - dv.Data[i] = &udf.Data{Val: &udf.Data_BytesVal{BytesVal: val}} - } - } - default: - return nil, moerr.NewInvalidArgNoCtx("python udf arg type", v.GetType().String()) - } - return dv, nil -} - -func writeResponse(response *udf.Response, result vector.FunctionResultWrapper) error { - var err error - retType := dataType2T[response.Vector.Type] - length := int(response.Vector.Length) - scale := response.Vector.Scale - - result.GetResultVector().SetTypeScale(scale) - - switch retType { - case types.T_bool: - res := vector.MustFunctionResult[bool](result) - for i := 0; i < length; i++ { - data := getDataFromDataVector(response.Vector, i) - if data == nil || data.Val == nil { - err = res.Append(false, true) - } else { - err = res.Append(data.GetBoolVal(), false) - } - if err != nil { - return err - } - } - case types.T_int8: - res := vector.MustFunctionResult[int8](result) - for i := 0; i < length; i++ { - data := getDataFromDataVector(response.Vector, i) - if data == nil || data.Val == nil { - err = res.Append(0, true) - } else { - err = res.Append(int8(data.GetIntVal()), false) - } - if err != nil { - return err - } - } - case types.T_int16: - res := vector.MustFunctionResult[int16](result) - for i := 0; i < length; i++ { - data := getDataFromDataVector(response.Vector, i) - if data == nil || data.Val == nil { - err = res.Append(0, true) - } else { - err = res.Append(int16(data.GetIntVal()), false) - } - if err != nil { - return err - } - } - case types.T_int32: - res := vector.MustFunctionResult[int32](result) - for i := 0; i < length; i++ { - data := getDataFromDataVector(response.Vector, i) - if data == nil || data.Val == nil { - err = res.Append(0, true) - } else { - err = res.Append(data.GetIntVal(), false) - } - if err != nil { - return err - } - } - case types.T_int64: - res := vector.MustFunctionResult[int64](result) - for i := 0; i < length; i++ { - data := getDataFromDataVector(response.Vector, i) - if data == nil || data.Val == nil { - err = res.Append(0, true) - } else { - err = res.Append(data.GetInt64Val(), false) - } - if err != nil { - return err - } - } - case types.T_uint8: - res := vector.MustFunctionResult[uint8](result) - for i := 0; i < length; i++ { - data := getDataFromDataVector(response.Vector, i) - if data == nil || data.Val == nil { - err = res.Append(0, true) - } else { - err = res.Append(uint8(data.GetUintVal()), false) - } - if err != nil { - return err - } - } - case types.T_uint16: - res := vector.MustFunctionResult[uint16](result) - for i := 0; i < length; i++ { - data := getDataFromDataVector(response.Vector, i) - if data == nil || data.Val == nil { - err = res.Append(0, true) - } else { - err = res.Append(uint16(data.GetUintVal()), false) - } - if err != nil { - return err - } - } - case types.T_uint32: - res := vector.MustFunctionResult[uint32](result) - for i := 0; i < length; i++ { - data := getDataFromDataVector(response.Vector, i) - if data == nil || data.Val == nil { - err = res.Append(0, true) - } else { - err = res.Append(data.GetUintVal(), false) - } - if err != nil { - return err - } - } - case types.T_uint64: - res := vector.MustFunctionResult[uint64](result) - for i := 0; i < length; i++ { - data := getDataFromDataVector(response.Vector, i) - if data == nil || data.Val == nil { - err = res.Append(0, true) - } else { - err = res.Append(data.GetUint64Val(), false) - } - if err != nil { - return err - } - } - case types.T_float32: - res := vector.MustFunctionResult[float32](result) - for i := 0; i < length; i++ { - data := getDataFromDataVector(response.Vector, i) - if data == nil || data.Val == nil { - err = res.Append(0, true) - } else { - err = res.Append(data.GetFloatVal(), false) - } - if err != nil { - return err - } - } - case types.T_float64: - res := vector.MustFunctionResult[float64](result) - for i := 0; i < length; i++ { - data := getDataFromDataVector(response.Vector, i) - if data == nil || data.Val == nil { - err = res.Append(0, true) - } else { - err = res.Append(data.GetDoubleVal(), false) - } - if err != nil { - return err - } - } - case types.T_char, types.T_varchar, types.T_text, types.T_datalink: - res := vector.MustFunctionResult[types.Varlena](result) - for i := 0; i < length; i++ { - data := getDataFromDataVector(response.Vector, i) - if data == nil || data.Val == nil { - err = res.AppendBytes(nil, true) - } else { - err = res.AppendBytes([]byte(data.GetStringVal()), false) - } - if err != nil { - return err - } - } - case types.T_json: - res := vector.MustFunctionResult[types.Varlena](result) - for i := 0; i < length; i++ { - data := getDataFromDataVector(response.Vector, i) - if data == nil || data.Val == nil { - err = res.AppendBytes(nil, true) - } else { - bytes, err2 := bytejson.ParseJsonByteFromString(data.GetStringVal()) - if err2 != nil { - return err2 - } - err = res.AppendBytes(bytes, false) - } - if err != nil { - return err - } - } - case types.T_uuid: - res := vector.MustFunctionResult[types.Uuid](result) - for i := 0; i < length; i++ { - data := getDataFromDataVector(response.Vector, i) - if data == nil || data.Val == nil { - err = res.AppendBytes(nil, true) - } else { - uuid, err2 := types.ParseUuid(data.GetStringVal()) - if err2 != nil { - return err2 - } - err = res.Append(uuid, false) - } - if err != nil { - return err - } - } - case types.T_time: - res := vector.MustFunctionResult[types.Time](result) - for i := 0; i < length; i++ { - data := getDataFromDataVector(response.Vector, i) - if data == nil || data.Val == nil { - err = res.AppendBytes(nil, true) - } else { - t, err2 := types.ParseTime(data.GetStringVal(), scale) - if err2 != nil { - return err2 - } - err = res.Append(t, false) - } - if err != nil { - return err - } - } - case types.T_date: - res := vector.MustFunctionResult[types.Date](result) - for i := 0; i < length; i++ { - data := getDataFromDataVector(response.Vector, i) - if data == nil || data.Val == nil { - err = res.AppendBytes(nil, true) - } else { - d, err2 := types.ParseDateCast(data.GetStringVal()) - if err2 != nil { - return err2 - } - err = res.Append(d, false) - } - if err != nil { - return err - } - } - case types.T_datetime: - res := vector.MustFunctionResult[types.Datetime](result) - for i := 0; i < length; i++ { - data := getDataFromDataVector(response.Vector, i) - if data == nil || data.Val == nil { - err = res.AppendBytes(nil, true) - } else { - dt, err2 := types.ParseDatetime(data.GetStringVal(), scale) - if err2 != nil { - return err2 - } - err = res.Append(dt, false) - } - if err != nil { - return err - } - } - case types.T_timestamp: - res := vector.MustFunctionResult[types.Timestamp](result) - for i := 0; i < length; i++ { - data := getDataFromDataVector(response.Vector, i) - if data == nil || data.Val == nil { - err = res.AppendBytes(nil, true) - } else { - ts, err2 := types.ParseTimestamp(time.Local, data.GetStringVal(), scale) - if err2 != nil { - return err2 - } - err = res.Append(ts, false) - } - if err != nil { - return err - } - } - case types.T_decimal64, types.T_decimal128: - res := vector.MustFunctionResult[types.Decimal128](result) - for i := 0; i < length; i++ { - data := getDataFromDataVector(response.Vector, i) - if data == nil || data.Val == nil { - err = res.AppendBytes(nil, true) - } else { - decimal, err2 := types.ParseDecimal128(data.GetStringVal(), 38, scale) - if err2 != nil { - return err2 - } - err = res.Append(decimal, false) - } - if err != nil { - return err - } - } - case types.T_binary, types.T_varbinary, types.T_blob: - res := vector.MustFunctionResult[types.Varlena](result) - for i := 0; i < length; i++ { - data := getDataFromDataVector(response.Vector, i) - if data == nil || data.Val == nil { - err = res.AppendBytes(nil, true) - } else { - err = res.AppendBytes(data.GetBytesVal(), false) - } - if err != nil { - return err - } - } - default: - return moerr.NewNotSupportedNoCtxf("python udf return type: %v", retType.String()) - } - - return nil -} - -var ( - t2DataType = map[types.T]udf.DataType{ - types.T_bool: udf.DataType_BOOL, - types.T_int8: udf.DataType_INT8, - types.T_int16: udf.DataType_INT16, - types.T_int32: udf.DataType_INT32, - types.T_int64: udf.DataType_INT64, - types.T_uint8: udf.DataType_UINT8, - types.T_uint16: udf.DataType_UINT16, - types.T_uint32: udf.DataType_UINT32, - types.T_uint64: udf.DataType_UINT64, - types.T_float32: udf.DataType_FLOAT32, - types.T_float64: udf.DataType_FLOAT64, - types.T_char: udf.DataType_CHAR, - types.T_varchar: udf.DataType_VARCHAR, - types.T_text: udf.DataType_TEXT, - types.T_json: udf.DataType_JSON, - types.T_uuid: udf.DataType_UUID, - types.T_time: udf.DataType_TIME, - types.T_date: udf.DataType_DATE, - types.T_datetime: udf.DataType_DATETIME, - types.T_timestamp: udf.DataType_TIMESTAMP, - types.T_decimal64: udf.DataType_DECIMAL64, - types.T_decimal128: udf.DataType_DECIMAL128, - types.T_binary: udf.DataType_BINARY, - types.T_varbinary: udf.DataType_VARBINARY, - types.T_blob: udf.DataType_BLOB, - } - - dataType2T = map[udf.DataType]types.T{ - udf.DataType_BOOL: types.T_bool, - udf.DataType_INT8: types.T_int8, - udf.DataType_INT16: types.T_int16, - udf.DataType_INT32: types.T_int32, - udf.DataType_INT64: types.T_int64, - udf.DataType_UINT8: types.T_uint8, - udf.DataType_UINT16: types.T_uint16, - udf.DataType_UINT32: types.T_uint32, - udf.DataType_UINT64: types.T_uint64, - udf.DataType_FLOAT32: types.T_float32, - udf.DataType_FLOAT64: types.T_float64, - udf.DataType_CHAR: types.T_char, - udf.DataType_VARCHAR: types.T_varchar, - udf.DataType_TEXT: types.T_text, - udf.DataType_JSON: types.T_json, - udf.DataType_UUID: types.T_uuid, - udf.DataType_TIME: types.T_time, - udf.DataType_DATE: types.T_date, - udf.DataType_DATETIME: types.T_datetime, - udf.DataType_TIMESTAMP: types.T_timestamp, - udf.DataType_DECIMAL64: types.T_decimal64, - udf.DataType_DECIMAL128: types.T_decimal128, - udf.DataType_BINARY: types.T_binary, - udf.DataType_VARBINARY: types.T_varbinary, - udf.DataType_BLOB: types.T_blob, - } -) - -type DefaultPkgReader struct { - Proc *process.Process -} - -func (d *DefaultPkgReader) Get(ctx context.Context, path string) (io.ReadCloser, error) { - reader, writer := io.Pipe() - var errGroup *errgroup.Group - defer func() { - if errGroup == nil { - writer.Close() - } - }() - - ioVector := &fileservice.IOVector{ - FilePath: path, - Entries: []fileservice.IOEntry{ - { - Offset: 0, - Size: -1, - WriterForRead: writer, - }, - }, - } - - errGroup = new(errgroup.Group) - errGroup.Go(func() error { - defer writer.Close() - return d.Proc.GetFileService().Read(ctx, ioVector) - }) - - return reader, nil -} diff --git a/pkg/udf/Makefile b/pkg/udf/Makefile deleted file mode 100644 index 21ee463ed71e4..0000000000000 --- a/pkg/udf/Makefile +++ /dev/null @@ -1,26 +0,0 @@ -# Copyright 2023 Matrix Origin -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -.PHONY: clean -clean: - rm -f udf.pb.go - rm -f udf_grpc.pb.go - rm -f pythonservice/pyserver/python_udf_pb2.py - rm -f pythonservice/pyserver/python_udf_pb2.pyi - rm -f pythonservice/pyserver/python_udf_pb2_grpc.py - -.PHONY: generate -generate: - protoc --go_out=. --go_opt=paths=source_relative --go-grpc_out=. --go-grpc_opt=paths=source_relative ./udf.proto - @cd pythonservice/pyserver && python -m grpc_tools.protoc -I ../../ --python_out=. --pyi_out=. --grpc_python_out=. ../../udf.proto diff --git a/pkg/udf/protocol/protocol.go b/pkg/udf/protocol/protocol.go new file mode 100644 index 0000000000000..226c7bf300802 --- /dev/null +++ b/pkg/udf/protocol/protocol.go @@ -0,0 +1,614 @@ +// Copyright 2026 Matrix Origin +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Package protocol contains the transport-independent contracts shared by the +// Python UDF gateway and its callers. It deliberately does not know about +// gRPC, Arrow, or a particular scheduler implementation. +package protocol + +import ( + "bytes" + "crypto/sha256" + "encoding/hex" + "encoding/json" + "errors" + "fmt" + "sync" + "time" +) + +const ( + // Version identifies the control envelope understood by this runtime. It is + // a wire compatibility field, not a language or package name. + Version = 1 + + // MaxControlBytes is a trust-boundary ceiling. A runtime may negotiate a + // smaller limit, but a caller must never silently raise it. + MaxControlBytes = 1 << 20 +) + +var ( + ErrProtocol = errors.New("python udf protocol violation") + ErrSequence = errors.New("python udf sequence violation") + ErrGroupClosed = errors.New("python udf execution group is closed") + ErrDuplicate = errors.New("python udf identity is already present") + ErrLedgerFull = errors.New("python udf terminal ledger is full") + ErrUnknownIdentity = errors.New("python udf identity is unknown") +) + +// FencingTuple is carried by every invocation control and data-plane +// application message. IDs are never reused while their fence/tombstone is +// retained. The tuple is a deduplication fence, not an authentication token. +type FencingTuple struct { + AccountID uint64 `json:"account_id"` + StatementID string `json:"statement_id"` + GroupID string `json:"group_id"` + GroupEpoch uint64 `json:"group_epoch"` + InvocationID string `json:"invocation_id"` + LeaseEpoch uint64 `json:"lease_epoch"` +} + +func (t FencingTuple) Validate() error { + if t.AccountID == 0 || t.StatementID == "" || t.GroupID == "" || + t.GroupEpoch == 0 || t.InvocationID == "" || t.LeaseEpoch == 0 { + return fmt.Errorf("%w: incomplete fencing tuple", ErrProtocol) + } + return nil +} + +// Control is the small, canonical application envelope carried in Flight +// app_metadata. Payload is a versioned descriptor or error body owned by the +// message kind; it is bounded before JSON decoding. +type Control struct { + Version int `json:"version"` + Kind string `json:"kind"` + Tuple FencingTuple `json:"tuple"` + Sequence uint64 `json:"sequence,omitempty"` + LastSequence uint64 `json:"last_sequence,omitempty"` + AckSequence uint64 `json:"ack_sequence,omitempty"` + FinishID string `json:"finish_id,omitempty"` + Status string `json:"status,omitempty"` + Reason string `json:"reason,omitempty"` + Payload json.RawMessage `json:"payload,omitempty"` +} + +// MarshalControl produces the byte representation used in the Flight +// application metadata field. Struct-field order is intentional: the same +// bytes are used when a descriptor fingerprint is calculated. +func MarshalControl(control Control) ([]byte, error) { + if control.Version == 0 { + control.Version = Version + } + if control.Version != Version || control.Kind == "" { + return nil, fmt.Errorf("%w: unsupported control version or empty kind", ErrProtocol) + } + if err := control.Tuple.Validate(); err != nil { + return nil, err + } + encoded, err := json.Marshal(control) + if err != nil { + return nil, fmt.Errorf("%w: encode control: %v", ErrProtocol, err) + } + if len(encoded) > MaxControlBytes { + return nil, fmt.Errorf("%w: control is %d bytes, limit is %d", ErrProtocol, len(encoded), MaxControlBytes) + } + return encoded, nil +} + +func UnmarshalControl(data []byte) (Control, error) { + if len(data) == 0 || len(data) > MaxControlBytes { + return Control{}, fmt.Errorf("%w: control size %d is outside the allowed range", ErrProtocol, len(data)) + } + var control Control + decoder := json.NewDecoder(bytes.NewReader(data)) + if err := decoder.Decode(&control); err != nil { + return Control{}, fmt.Errorf("%w: decode control: %v", ErrProtocol, err) + } + if control.Version != Version || control.Kind == "" { + return Control{}, fmt.Errorf("%w: unsupported control version %d", ErrProtocol, control.Version) + } + if err := control.Tuple.Validate(); err != nil { + return Control{}, err + } + return control, nil +} + +// Sequence validates the independent input and result directions of one +// invocation. EndInput is a normal half-close; it does not make results +// terminal until all results have been acknowledged. +type Sequence struct { + mu sync.Mutex + nextInput uint64 + lastInput uint64 + inputEnded bool + nextResult uint64 + lastResult uint64 + ackedResult uint64 +} + +func (s *Sequence) AcceptInput(sequence uint64) error { + s.mu.Lock() + defer s.mu.Unlock() + if s.inputEnded { + return fmt.Errorf("%w: input arrived after EndInput", ErrSequence) + } + if sequence != s.nextInput+1 { + return fmt.Errorf("%w: input sequence %d, expected %d", ErrSequence, sequence, s.nextInput+1) + } + s.nextInput = sequence + s.lastInput = sequence + return nil +} + +func (s *Sequence) EndInput(lastSequence uint64) error { + s.mu.Lock() + defer s.mu.Unlock() + if s.inputEnded { + if s.lastInput == lastSequence { + return nil + } + return fmt.Errorf("%w: EndInput changed from %d to %d", ErrSequence, s.lastInput, lastSequence) + } + if lastSequence != s.lastInput { + return fmt.Errorf("%w: EndInput last sequence %d, observed %d", ErrSequence, lastSequence, s.lastInput) + } + s.inputEnded = true + return nil +} + +func (s *Sequence) AcceptResult(sequence uint64) error { + s.mu.Lock() + defer s.mu.Unlock() + if sequence != s.nextResult+1 || sequence > s.lastInput { + return fmt.Errorf("%w: result sequence %d, expected %d and at most input %d", ErrSequence, sequence, s.nextResult+1, s.lastInput) + } + s.nextResult = sequence + s.lastResult = sequence + return nil +} + +func (s *Sequence) AcknowledgeResults(sequence uint64) error { + s.mu.Lock() + defer s.mu.Unlock() + if sequence < s.ackedResult || sequence > s.lastResult { + return fmt.Errorf("%w: result ACK %d outside [%d,%d]", ErrSequence, sequence, s.ackedResult, s.lastResult) + } + s.ackedResult = sequence + return nil +} + +func (s *Sequence) ReadyToFinish() bool { + s.mu.Lock() + defer s.mu.Unlock() + return s.inputEnded && s.lastResult == s.lastInput && s.ackedResult == s.lastResult +} + +func (s *Sequence) State() (lastInput, lastResult, acked uint64, inputEnded bool) { + s.mu.Lock() + defer s.mu.Unlock() + return s.lastInput, s.lastResult, s.ackedResult, s.inputEnded +} + +// OutputSnapshot is the only representation that may cross the trusted +// validation/publication boundary. Freeze copies the worker-owned bytes +// before validation. The snapshot's backing is private, so later worker +// writes cannot change the bytes validated or published by the caller. +type OutputSnapshot struct { + backing []byte + digest [sha256.Size]byte +} + +func FreezeOutput(source []byte, maxBytes int64) (*OutputSnapshot, error) { + if maxBytes <= 0 || int64(len(source)) > maxBytes { + return nil, fmt.Errorf("%w: output snapshot size %d exceeds limit %d", ErrProtocol, len(source), maxBytes) + } + backing := make([]byte, len(source)) + copy(backing, source) + return &OutputSnapshot{backing: backing, digest: sha256.Sum256(backing)}, nil +} + +func (s *OutputSnapshot) Len() int { + if s == nil { + return 0 + } + return len(s.backing) +} + +func (s *OutputSnapshot) Digest() string { + if s == nil { + return "" + } + return hex.EncodeToString(s.digest[:]) +} + +func (s *OutputSnapshot) Validate(expectedLength int, expectedDigest string) error { + if s == nil || s.backing == nil { + return fmt.Errorf("%w: missing output snapshot", ErrProtocol) + } + if expectedLength >= 0 && len(s.backing) != expectedLength { + return fmt.Errorf("%w: output snapshot length %d, expected %d", ErrProtocol, len(s.backing), expectedLength) + } + if expectedDigest != "" && !equalFoldHex(expectedDigest, s.Digest()) { + return fmt.Errorf("%w: output snapshot digest changed", ErrProtocol) + } + return nil +} + +// Bytes returns a private copy for an API that cannot consume an immutable +// view. Validation and publication in a trusted consumer should operate on +// the same snapshot object, rather than refetching the worker buffer. +func (s *OutputSnapshot) Bytes() []byte { + if s == nil { + return nil + } + return append([]byte(nil), s.backing...) +} + +// TrustedBytes exposes the private frozen backing to a validator/consumer +// that is part of the same trusted boundary. The caller must treat the +// returned slice as read-only. It exists so validation and decoding can use +// exactly the bytes that were frozen, rather than refetching worker memory or +// making a second mutable copy between the two steps. +func (s *OutputSnapshot) TrustedBytes() []byte { + if s == nil { + return nil + } + return s.backing +} + +func equalFoldHex(left, right string) bool { + if len(left) != len(right) { + return false + } + for i := range left { + l, r := left[i], right[i] + if l >= 'A' && l <= 'F' { + l += 'a' - 'A' + } + if r >= 'A' && r <= 'F' { + r += 'a' - 'A' + } + if l != r { + return false + } + } + return true +} + +type GroupState string + +const ( + GroupReserved GroupState = "RESERVED" + GroupOpen GroupState = "OPEN" + GroupDraining GroupState = "DRAINING" + GroupReleased GroupState = "RELEASED" +) + +// CloseReason is deliberately a string so the scheduler can add a reason in +// a newer minor protocol without changing the lifetime rules. +type CloseReason string + +const ( + ReasonInputEOF CloseReason = "INPUT_EOF" + ReasonEmptyInput CloseReason = "EMPTY_INPUT" + ReasonAllNull CloseReason = "ALL_NULL" + ReasonNoSelectedRows CloseReason = "NO_SELECTED_ROWS" + ReasonPartialOpenError CloseReason = "PARTIAL_OPEN_FAILURE" + ReasonCancel CloseReason = "CANCEL" + ReasonIdle CloseReason = "IDLE" +) + +// ExecutionGroup is a one-shot admission cohort. Member terminal cleanup +// only removes that member. The release callback belongs to the group owner +// and runs after close, all members terminal, and all in-flight opens finish. +type ExecutionGroup struct { + mu sync.Mutex + id string + epoch uint64 + maxMembers int + registered int + inFlight int + members map[string]bool + closing bool + releaseBusy bool + released bool + release func() error + reason CloseReason +} + +func NewExecutionGroup(id string, epoch uint64, maxMembers int, release func() error) (*ExecutionGroup, error) { + if id == "" || epoch == 0 || maxMembers <= 0 || release == nil { + return nil, fmt.Errorf("%w: invalid execution group", ErrProtocol) + } + return &ExecutionGroup{ + id: id, epoch: epoch, maxMembers: maxMembers, + members: make(map[string]bool), release: release, + }, nil +} + +// BeginOpen reserves one cumulative member-registration slot. The returned +// token must be committed or aborted exactly once by the Open owner. +func (g *ExecutionGroup) BeginOpen(memberID string) (*OpenToken, error) { + if memberID == "" { + return nil, fmt.Errorf("%w: empty member id", ErrProtocol) + } + g.mu.Lock() + defer g.mu.Unlock() + if g.closing || g.released { + return nil, ErrGroupClosed + } + if g.members[memberID] { + return nil, ErrDuplicate + } + if g.registered+g.inFlight >= g.maxMembers { + return nil, fmt.Errorf("%w: member limit %d reached", ErrLedgerFull, g.maxMembers) + } + g.inFlight++ + return &OpenToken{group: g, memberID: memberID}, nil +} + +type OpenToken struct { + group *ExecutionGroup + memberID string + once sync.Once +} + +func (t *OpenToken) Commit() error { + if t == nil || t.group == nil { + return ErrUnknownIdentity + } + var err error + var release bool + t.once.Do(func() { + g := t.group + g.mu.Lock() + g.inFlight-- + if g.closing || g.released { + err = ErrGroupClosed + release = g.canReleaseLocked() + g.mu.Unlock() + return + } + g.members[t.memberID] = true + g.registered++ + g.mu.Unlock() + }) + if release { + if releaseErr := t.group.releaseIfReady(); err == nil { + err = releaseErr + } + } + return err +} + +func (t *OpenToken) Abort() error { + if t == nil || t.group == nil { + return ErrUnknownIdentity + } + var release bool + t.once.Do(func() { + g := t.group + g.mu.Lock() + g.inFlight-- + release = g.canReleaseLocked() + g.mu.Unlock() + }) + if release { + return t.group.releaseIfReady() + } + return nil +} + +func (g *ExecutionGroup) Close(reason CloseReason) error { + if reason == "" { + return fmt.Errorf("%w: close reason is empty", ErrProtocol) + } + g.mu.Lock() + if g.released { + g.mu.Unlock() + return nil + } + if !g.closing { + g.closing = true + g.reason = reason + } + release := g.canReleaseLocked() + g.mu.Unlock() + if release { + return g.releaseIfReady() + } + return nil +} + +func (g *ExecutionGroup) MemberTerminal(memberID string) error { + g.mu.Lock() + if !g.members[memberID] { + g.mu.Unlock() + return ErrUnknownIdentity + } + delete(g.members, memberID) + release := g.canReleaseLocked() + g.mu.Unlock() + if release { + return g.releaseIfReady() + } + return nil +} + +func (g *ExecutionGroup) canReleaseLocked() bool { + return g.closing && len(g.members) == 0 && g.inFlight == 0 && !g.released && !g.releaseBusy +} + +func (g *ExecutionGroup) releaseIfReady() error { + g.mu.Lock() + if !g.canReleaseLocked() { + g.mu.Unlock() + return nil + } + g.releaseBusy = true + g.mu.Unlock() + err := g.release() + g.mu.Lock() + g.releaseBusy = false + if err == nil { + g.released = true + } + g.mu.Unlock() + return err +} + +func (g *ExecutionGroup) State() GroupState { + g.mu.Lock() + defer g.mu.Unlock() + if g.released { + return GroupReleased + } + if g.closing { + return GroupDraining + } + if g.registered > 0 { + return GroupOpen + } + return GroupReserved +} + +func (g *ExecutionGroup) Reason() CloseReason { + g.mu.Lock() + defer g.mu.Unlock() + return g.reason +} + +func (g *ExecutionGroup) Counts() (registered, active, inFlight int) { + g.mu.Lock() + defer g.mu.Unlock() + return g.registered, len(g.members), g.inFlight +} + +type ledgerEntry struct { + key string + bytes int64 + expiresAt time.Time + tombstone bool +} + +// TerminalLedger is an admission-reserved bounded deduplication ledger. A +// terminal entry is retained until expiry; it is never deleted merely to make +// room for a new invocation. +type TerminalLedger struct { + mu sync.Mutex + maxEntries int + maxBytes int64 + entries map[string]ledgerEntry + reservedN int + reservedB int64 +} + +type LedgerCredit struct { + ledger *TerminalLedger + groupID string + remainingN int + remainingB int64 + closed bool +} + +func NewTerminalLedger(maxEntries int, maxBytes int64) (*TerminalLedger, error) { + if maxEntries <= 0 || maxBytes <= 0 { + return nil, fmt.Errorf("%w: invalid terminal ledger limit", ErrProtocol) + } + return &TerminalLedger{maxEntries: maxEntries, maxBytes: maxBytes, entries: make(map[string]ledgerEntry)}, nil +} + +func (l *TerminalLedger) Reserve(groupID string, entries int, bytes int64) (*LedgerCredit, error) { + if groupID == "" || entries <= 0 || bytes <= 0 { + return nil, fmt.Errorf("%w: invalid terminal ledger reservation", ErrProtocol) + } + l.mu.Lock() + defer l.mu.Unlock() + if l.reservedN > l.maxEntries-entries || l.reservedB > l.maxBytes-bytes { + return nil, ErrLedgerFull + } + l.reservedN += entries + l.reservedB += bytes + return &LedgerCredit{ledger: l, groupID: groupID, remainingN: entries, remainingB: bytes}, nil +} + +func (c *LedgerCredit) Add(key string, bytes int64, expiry time.Time) error { + if c == nil || c.ledger == nil || key == "" || bytes <= 0 || expiry.IsZero() { + return fmt.Errorf("%w: invalid terminal ledger entry", ErrProtocol) + } + c.ledger.mu.Lock() + defer c.ledger.mu.Unlock() + if c.closed || c.remainingN == 0 || c.remainingB < bytes { + return ErrLedgerFull + } + if _, exists := c.ledger.entries[key]; exists { + return ErrDuplicate + } + c.ledger.entries[key] = ledgerEntry{key: key, bytes: bytes, expiresAt: expiry} + c.remainingN-- + c.remainingB -= bytes + return nil +} + +func (c *LedgerCredit) ReleaseUnused() { + if c == nil || c.ledger == nil { + return + } + c.ledger.mu.Lock() + defer c.ledger.mu.Unlock() + if c.closed { + return + } + c.ledger.reservedN -= c.remainingN + c.ledger.reservedB -= c.remainingB + c.remainingN = 0 + c.remainingB = 0 + c.closed = true +} + +func (l *TerminalLedger) Complete(key string) error { + l.mu.Lock() + defer l.mu.Unlock() + entry, ok := l.entries[key] + if !ok { + return ErrUnknownIdentity + } + if entry.tombstone { + return nil + } + entry.tombstone = true + l.entries[key] = entry + return nil +} + +func (l *TerminalLedger) Expire(now time.Time) int { + l.mu.Lock() + defer l.mu.Unlock() + removed := 0 + for key, entry := range l.entries { + if !now.Before(entry.expiresAt) { + delete(l.entries, key) + l.reservedN-- + l.reservedB -= entry.bytes + removed++ + } + } + return removed +} + +func (l *TerminalLedger) Counts() (entries int, bytes int64) { + l.mu.Lock() + defer l.mu.Unlock() + return len(l.entries), l.reservedB +} diff --git a/pkg/udf/protocol/protocol_test.go b/pkg/udf/protocol/protocol_test.go new file mode 100644 index 0000000000000..8696d59997ec4 --- /dev/null +++ b/pkg/udf/protocol/protocol_test.go @@ -0,0 +1,175 @@ +// Copyright 2026 Matrix Origin +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package protocol + +import ( + "bytes" + "errors" + "sync/atomic" + "testing" + "time" + + "github.com/stretchr/testify/require" +) + +func testTuple() FencingTuple { + return FencingTuple{ + AccountID: 1, StatementID: "statement", GroupID: "group", + GroupEpoch: 2, InvocationID: "invocation", LeaseEpoch: 3, + } +} + +func TestControlRoundTripIsBoundedAndVersioned(t *testing.T) { + wire, err := MarshalControl(Control{Kind: "OpenInvocation", Tuple: testTuple(), Payload: []byte(`{"mode":"VECTOR"}`)}) + require.NoError(t, err) + got, err := UnmarshalControl(wire) + require.NoError(t, err) + require.Equal(t, Version, got.Version) + require.Equal(t, "OpenInvocation", got.Kind) + require.JSONEq(t, `{"mode":"VECTOR"}`, string(got.Payload)) + + _, err = UnmarshalControl(bytes.Repeat([]byte{'x'}, MaxControlBytes+1)) + require.ErrorIs(t, err, ErrProtocol) + _, err = MarshalControl(Control{Kind: "", Tuple: testTuple()}) + require.ErrorIs(t, err, ErrProtocol) +} + +func TestSequenceKeepsHalfCloseIndependentFromResults(t *testing.T) { + var sequence Sequence + require.NoError(t, sequence.AcceptInput(1)) + require.NoError(t, sequence.AcceptInput(2)) + require.NoError(t, sequence.EndInput(2)) + require.NoError(t, sequence.AcceptResult(1)) + require.False(t, sequence.ReadyToFinish()) + require.NoError(t, sequence.AcknowledgeResults(1)) + require.NoError(t, sequence.AcceptResult(2)) + require.NoError(t, sequence.AcknowledgeResults(2)) + require.True(t, sequence.ReadyToFinish()) + + require.ErrorIs(t, sequence.AcceptInput(3), ErrSequence) + require.ErrorIs(t, sequence.AcknowledgeResults(3), ErrSequence) + require.NoError(t, sequence.EndInput(2)) +} + +func TestOutputSnapshotFreezesBeforeWorkerCanRewrite(t *testing.T) { + worker := []byte("trusted-before-barrier") + snapshot, err := FreezeOutput(worker, 1024) + require.NoError(t, err) + worker[0] = 'X' + + require.NoError(t, snapshot.Validate(len("trusted-before-barrier"), snapshot.Digest())) + require.Equal(t, "trusted-before-barrier", string(snapshot.Bytes())) + + empty, err := FreezeOutput(nil, 1024) + require.NoError(t, err) + require.NoError(t, empty.Validate(0, empty.Digest())) +} + +func TestExecutionGroupReleasesOnlyAfterCloseAndAllMembersTerminal(t *testing.T) { + var releases atomic.Int32 + group, err := NewExecutionGroup("g", 1, 2, func() error { + releases.Add(1) + return nil + }) + require.NoError(t, err) + a, err := group.BeginOpen("a") + require.NoError(t, err) + require.NoError(t, a.Commit()) + b, err := group.BeginOpen("b") + require.NoError(t, err) + require.NoError(t, b.Commit()) + + require.NoError(t, group.MemberTerminal("a")) + require.NoError(t, group.Close(ReasonInputEOF)) + require.Equal(t, int32(0), releases.Load()) + require.NoError(t, group.MemberTerminal("b")) + require.Equal(t, int32(1), releases.Load()) + require.Equal(t, GroupReleased, group.State()) + + _, err = group.BeginOpen("late") + require.ErrorIs(t, err, ErrGroupClosed) + require.NoError(t, group.Close(ReasonCancel)) + require.Equal(t, int32(1), releases.Load()) +} + +func TestExecutionGroupZeroMemberAndAbortedOpenRelease(t *testing.T) { + var releases atomic.Int32 + group, err := NewExecutionGroup("g", 1, 1, func() error { + releases.Add(1) + return nil + }) + require.NoError(t, err) + open, err := group.BeginOpen("will-abort") + require.NoError(t, err) + require.NoError(t, group.Close(ReasonPartialOpenError)) + require.Equal(t, int32(0), releases.Load()) + require.NoError(t, open.Abort()) + require.Equal(t, int32(1), releases.Load()) + + group, err = NewExecutionGroup("empty", 1, 1, func() error { + releases.Add(1) + return nil + }) + require.NoError(t, err) + require.NoError(t, group.Close(ReasonEmptyInput)) + require.Equal(t, int32(2), releases.Load()) +} + +func TestTerminalLedgerRetainsTombstonesUntilExpiry(t *testing.T) { + ledger, err := NewTerminalLedger(2, 200) + require.NoError(t, err) + now := time.Unix(100, 0) + credit, err := ledger.Reserve("g1", 1, 100) + require.NoError(t, err) + require.NoError(t, credit.Add("i1", 100, now.Add(time.Hour))) + require.NoError(t, ledger.Complete("i1")) + credit.ReleaseUnused() + + second, err := ledger.Reserve("g2", 1, 100) + require.NoError(t, err) + require.NoError(t, second.Add("i2", 100, now.Add(time.Hour))) + second.ReleaseUnused() + _, err = ledger.Reserve("g3", 1, 1) + require.ErrorIs(t, err, ErrLedgerFull) + + require.Equal(t, 2, func() int { n, _ := ledger.Counts(); return n }()) + require.Equal(t, 0, ledger.Expire(now.Add(time.Minute))) + require.Equal(t, 2, func() int { n, _ := ledger.Counts(); return n }()) + require.Equal(t, 2, ledger.Expire(now.Add(2*time.Hour))) + third, err := ledger.Reserve("g3", 1, 1) + require.NoError(t, err) + require.NoError(t, third.Add("i3", 1, now.Add(3*time.Hour))) + + require.ErrorIs(t, ledger.Complete("missing"), ErrUnknownIdentity) + require.NoError(t, ledger.Complete("i3")) + require.NoError(t, ledger.Complete("i3")) +} + +func TestExecutionGroupReleaseErrorCanBeRetried(t *testing.T) { + var attempts atomic.Int32 + group, err := NewExecutionGroup("g", 1, 1, func() error { + if attempts.Add(1) == 1 { + return errors.New("temporary release failure") + } + return nil + }) + require.NoError(t, err) + require.Error(t, group.Close(ReasonCancel)) + require.Equal(t, GroupDraining, group.State()) + // A second close is the scheduler's retry-safe release trigger. + require.NoError(t, group.Close(ReasonCancel)) + require.Equal(t, GroupReleased, group.State()) + require.Equal(t, int32(2), attempts.Load()) +} diff --git a/pkg/udf/pythonruntime/arrow.go b/pkg/udf/pythonruntime/arrow.go new file mode 100644 index 0000000000000..c65bb9aea92f1 --- /dev/null +++ b/pkg/udf/pythonruntime/arrow.go @@ -0,0 +1,771 @@ +// Copyright 2026 Matrix Origin +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package pythonruntime + +import ( + "bytes" + "context" + "crypto/sha256" + "encoding/binary" + "encoding/hex" + "encoding/json" + "errors" + "fmt" + "math" + + "github.com/apache/arrow-go/v18/arrow" + "github.com/apache/arrow-go/v18/arrow/array" + "github.com/apache/arrow-go/v18/arrow/decimal128" + "github.com/apache/arrow-go/v18/arrow/flight" + "github.com/apache/arrow-go/v18/arrow/ipc" + "github.com/apache/arrow-go/v18/arrow/memory" + "github.com/matrixorigin/matrixone/pkg/common/mpool" + "github.com/matrixorigin/matrixone/pkg/container/arrowipc" + "github.com/matrixorigin/matrixone/pkg/container/bytejson" + "github.com/matrixorigin/matrixone/pkg/container/types" + "github.com/matrixorigin/matrixone/pkg/container/vector" +) + +const ( + TypeMetadataKey = "mo.udf.type" + TypeFingerprintKey = "mo.udf.type_fingerprint" + DefaultMaxBatchBytes = 16 << 20 + DefaultMaxBatchRows = 65536 + DefaultMaxControlBytes = 1 << 20 +) + +type TypeDescriptor struct { + TypeID int32 `json:"type_id"` + Width int32 `json:"width,omitempty"` + Scale int32 `json:"scale,omitempty"` + Charset uint8 `json:"charset,omitempty"` + OffsetWidth int32 `json:"offset_width,omitempty"` + JSONEncoding string `json:"json_encoding,omitempty"` + TemporalEncoding string `json:"temporal_encoding,omitempty"` +} + +func NewTypeDescriptor(typ types.Type) (TypeDescriptor, error) { + d := TypeDescriptor{TypeID: int32(typ.Oid), Width: typ.Width, Scale: typ.Scale, Charset: typ.Charset, OffsetWidth: 32} + switch typ.Oid { + case types.T_json: + d.JSONEncoding = "canonical_text" + case types.T_date, types.T_datetime, types.T_timestamp: + d.TemporalEncoding = "sql_zero_struct" + case types.T_bool, types.T_int8, types.T_int16, types.T_int32, types.T_int64, + types.T_uint8, types.T_uint16, types.T_uint32, types.T_uint64, + types.T_float32, types.T_float64, types.T_decimal64, types.T_decimal128, + types.T_char, types.T_varchar, types.T_text, types.T_uuid, types.T_time, + types.T_binary, types.T_varbinary, types.T_blob, + types.T_array_float32, types.T_array_float64: + default: + return TypeDescriptor{}, fmt.Errorf("python runtime does not support MatrixOne type %s", typ.String()) + } + if typ.Oid == types.T_array_float32 || typ.Oid == types.T_array_float64 { + if typ.Width <= 0 { + return TypeDescriptor{}, fmt.Errorf("python runtime requires a positive vector dimension for %s", typ.String()) + } + d.OffsetWidth = 0 + } + if typ.Oid == types.T_uuid { + d.OffsetWidth = 0 + } + return d, nil +} + +func (d TypeDescriptor) Type() types.Type { + return types.NewWithCharset(types.T(d.TypeID), d.Width, d.Scale, d.Charset) +} +func (d TypeDescriptor) canonical() ([]byte, error) { return json.Marshal(d) } +func (d TypeDescriptor) Fingerprint() (string, error) { + canonical, err := d.canonical() + if err != nil { + return "", err + } + physical, err := d.arrowType() + if err != nil { + return "", err + } + h := sha256.New() + h.Write([]byte("matrixone-python-udf-type\x00")) + h.Write(canonical) + h.Write([]byte{0}) + h.Write([]byte(physical.Fingerprint())) + return hex.EncodeToString(h.Sum(nil)), nil +} + +func (d TypeDescriptor) arrowType() (arrow.DataType, error) { + switch types.T(d.TypeID) { + case types.T_bool: + return arrow.FixedWidthTypes.Boolean, nil + case types.T_int8: + return arrow.PrimitiveTypes.Int8, nil + case types.T_int16: + return arrow.PrimitiveTypes.Int16, nil + case types.T_int32: + return arrow.PrimitiveTypes.Int32, nil + case types.T_int64: + return arrow.PrimitiveTypes.Int64, nil + case types.T_uint8: + return arrow.PrimitiveTypes.Uint8, nil + case types.T_uint16: + return arrow.PrimitiveTypes.Uint16, nil + case types.T_uint32: + return arrow.PrimitiveTypes.Uint32, nil + case types.T_uint64: + return arrow.PrimitiveTypes.Uint64, nil + case types.T_float32: + return arrow.PrimitiveTypes.Float32, nil + case types.T_float64: + return arrow.PrimitiveTypes.Float64, nil + case types.T_decimal64, types.T_decimal128: + precision := d.Width + if precision == 0 { + precision = 18 + if types.T(d.TypeID) == types.T_decimal128 { + precision = 38 + } + } + if precision < 1 || precision > decimal128.MaxPrecision { + return nil, fmt.Errorf("decimal precision %d is outside Arrow's supported range", precision) + } + return &arrow.Decimal128Type{Precision: precision, Scale: d.Scale}, nil + case types.T_char, types.T_varchar, types.T_text, types.T_json: + if d.OffsetWidth == 64 { + return arrow.BinaryTypes.LargeString, nil + } + return arrow.BinaryTypes.String, nil + case types.T_binary, types.T_varbinary, types.T_blob: + if d.OffsetWidth == 64 { + return arrow.BinaryTypes.LargeBinary, nil + } + return arrow.BinaryTypes.Binary, nil + case types.T_uuid: + return &arrow.FixedSizeBinaryType{ByteWidth: 16}, nil + case types.T_time: + return &arrow.DurationType{Unit: arrow.Microsecond}, nil + case types.T_array_float32: + if d.Width <= 0 { + return nil, fmt.Errorf("invalid vecf32 dimension %d", d.Width) + } + return arrow.FixedSizeListOf(d.Width, arrow.PrimitiveTypes.Float32), nil + case types.T_array_float64: + if d.Width <= 0 { + return nil, fmt.Errorf("invalid vecf64 dimension %d", d.Width) + } + return arrow.FixedSizeListOf(d.Width, arrow.PrimitiveTypes.Float64), nil + case types.T_date: + return sqlTemporalType(arrow.PrimitiveTypes.Date32), nil + case types.T_datetime: + return sqlTemporalType(&arrow.TimestampType{Unit: arrow.Microsecond}), nil + case types.T_timestamp: + return sqlTemporalType(&arrow.TimestampType{Unit: arrow.Microsecond, TimeZone: "UTC"}), nil + default: + return nil, fmt.Errorf("unsupported MatrixOne type id %d", d.TypeID) + } +} +func sqlTemporalType(value arrow.DataType) *arrow.StructType { + return arrow.StructOf(arrow.Field{Name: "is_zero", Type: arrow.FixedWidthTypes.Boolean, Nullable: false}, arrow.Field{Name: "value", Type: value, Nullable: false}) +} +func (d TypeDescriptor) Field(name string) (arrow.Field, error) { + typ, err := d.arrowType() + if err != nil { + return arrow.Field{}, err + } + fingerprint, err := d.Fingerprint() + if err != nil { + return arrow.Field{}, err + } + descriptor, err := d.canonical() + if err != nil { + return arrow.Field{}, err + } + return arrow.Field{Name: name, Type: typ, Nullable: true, Metadata: arrow.NewMetadata([]string{TypeMetadataKey, TypeFingerprintKey}, []string{string(descriptor), fingerprint})}, nil +} +func (d TypeDescriptor) ValidateField(field arrow.Field) error { + want, err := d.Field(field.Name) + if err != nil { + return err + } + if field.Type == nil || field.Type.Fingerprint() != want.Type.Fingerprint() || field.Nullable != want.Nullable { + return fmt.Errorf("TYPE_CONTRACT: Arrow type %s does not match %s", field.Type, want.Type) + } + if !field.Metadata.Equal(want.Metadata) { + return fmt.Errorf("TYPE_CONTRACT: Arrow logical metadata does not match the frozen descriptor") + } + return nil +} + +type ArrowFrame struct { + Header []byte + Body []byte +} + +type encodedRecordBatch struct { + Frames []ArrowFrame + Rows int64 +} + +var errArrowBatchTooLarge = errors.New("Arrow record batch exceeds the configured limit") + +func EncodeRecordBatch(record arrow.RecordBatch, maxBytes int64) ([]ArrowFrame, error) { + if record == nil || record.Schema() == nil || maxBytes <= 0 { + return nil, fmt.Errorf("invalid Arrow record batch") + } + collector := &flightFrameCollector{maxBytes: maxBytes} + writer := flight.NewRecordWriter(collector, ipc.WithSchema(record.Schema()), ipc.WithAllocator(memory.NewGoAllocator())) + if err := writer.Write(record); err != nil { + return nil, fmt.Errorf("encode Arrow record batch: %w", err) + } + if err := writer.Close(); err != nil { + return nil, fmt.Errorf("close Arrow record batch: %w", err) + } + if len(collector.frames) < 2 { + return nil, fmt.Errorf("Arrow stream did not contain schema and record batch") + } + return collector.frames, nil +} + +// flightFrameCollector preserves the exact Flight representation: DataHeader +// is the unframed IPC FlatBuffer and DataBody contains the body plus its IPC +// alignment padding. A stream-file frame cannot be copied directly into +// FlightData because its eight-byte continuation prefix is not part of +// FlightData.DataHeader. +type flightFrameCollector struct { + frames []ArrowFrame + maxBytes int64 + total int64 +} + +func (c *flightFrameCollector) Send(data *flight.FlightData) error { + if data == nil || len(data.DataHeader) == 0 { + return fmt.Errorf("invalid Arrow Flight frame") + } + if data.FlightDescriptor != nil || len(data.AppMetadata) != 0 { + return fmt.Errorf("unexpected metadata on Arrow payload frame") + } + header := append([]byte(nil), data.DataHeader...) + body := append([]byte(nil), data.DataBody...) + c.total += int64(len(header) + len(body)) + if c.total > c.maxBytes { + return fmt.Errorf("%w: stream is %d bytes, limit is %d", errArrowBatchTooLarge, c.total, c.maxBytes) + } + c.frames = append(c.frames, ArrowFrame{Header: header, Body: body}) + return nil +} + +func BuildInputRecord(inputs []*vector.Vector, args []types.Type, length int) (arrow.RecordBatch, *arrow.Schema, error) { + return BuildInputRecordRange(inputs, args, 0, length) +} + +func BuildInputRecordRange(inputs []*vector.Vector, args []types.Type, start, length int) (arrow.RecordBatch, *arrow.Schema, error) { + if start < 0 || length <= 0 || len(inputs) != len(args) { + return nil, nil, fmt.Errorf("invalid Python UDF input shape") + } + fields := make([]arrow.Field, len(args)) + for i, typ := range args { + descriptor, err := NewTypeDescriptor(typ) + if err != nil { + return nil, nil, err + } + fields[i], err = descriptor.Field(fmt.Sprintf("arg_%d", i)) + if err != nil { + return nil, nil, err + } + if inputs[i] == nil || (!inputs[i].IsConst() && inputs[i].Length() < start+length) { + return nil, nil, fmt.Errorf("input column %d is shorter than batch range", i) + } + } + schema := arrow.NewSchema(fields, nil) + if len(fields) == 0 { + return array.NewRecordBatch(schema, nil, int64(length)), schema, nil + } + builder := array.NewRecordBuilder(memory.NewGoAllocator(), schema) + defer builder.Release() + for column, typ := range args { + for row := 0; row < length; row++ { + if err := appendInputValue(builder.Field(column), inputs[column], typ, start, row); err != nil { + return nil, nil, fmt.Errorf("input column %d row %d: %w", column, row, err) + } + } + } + return builder.NewRecordBatch(), schema, nil +} +func sourceRow(v *vector.Vector, start, row int) int { + if v.IsConst() { + return 0 + } + return start + row +} +func appendInputValue(builder array.Builder, v *vector.Vector, typ types.Type, start, row int) error { + index := sourceRow(v, start, row) + null := v.IsNull(uint64(index)) + if typ.Oid == types.T_date || typ.Oid == types.T_datetime || typ.Oid == types.T_timestamp { + return appendTemporalInput(builder.(*array.StructBuilder), v, typ, index, null) + } + if typ.Oid == types.T_time { + b := builder.(*array.DurationBuilder) + if null { + b.AppendNull() + } else { + b.Append(arrow.Duration(vector.GetFixedAtNoTypeCheck[types.Time](v, index))) + } + return nil + } + switch typ.Oid { + case types.T_bool: + b := builder.(*array.BooleanBuilder) + if null { + b.AppendNull() + } else { + b.Append(vector.GetFixedAtNoTypeCheck[bool](v, index)) + } + case types.T_int8: + b := builder.(*array.Int8Builder) + if null { + b.AppendNull() + } else { + b.Append(vector.GetFixedAtNoTypeCheck[int8](v, index)) + } + case types.T_int16: + b := builder.(*array.Int16Builder) + if null { + b.AppendNull() + } else { + b.Append(vector.GetFixedAtNoTypeCheck[int16](v, index)) + } + case types.T_int32: + b := builder.(*array.Int32Builder) + if null { + b.AppendNull() + } else { + b.Append(vector.GetFixedAtNoTypeCheck[int32](v, index)) + } + case types.T_int64: + b := builder.(*array.Int64Builder) + if null { + b.AppendNull() + } else { + b.Append(vector.GetFixedAtNoTypeCheck[int64](v, index)) + } + case types.T_uint8: + b := builder.(*array.Uint8Builder) + if null { + b.AppendNull() + } else { + b.Append(vector.GetFixedAtNoTypeCheck[uint8](v, index)) + } + case types.T_uint16: + b := builder.(*array.Uint16Builder) + if null { + b.AppendNull() + } else { + b.Append(vector.GetFixedAtNoTypeCheck[uint16](v, index)) + } + case types.T_uint32: + b := builder.(*array.Uint32Builder) + if null { + b.AppendNull() + } else { + b.Append(vector.GetFixedAtNoTypeCheck[uint32](v, index)) + } + case types.T_uint64: + b := builder.(*array.Uint64Builder) + if null { + b.AppendNull() + } else { + b.Append(vector.GetFixedAtNoTypeCheck[uint64](v, index)) + } + case types.T_float32: + b := builder.(*array.Float32Builder) + if null { + b.AppendNull() + } else { + b.Append(vector.GetFixedAtNoTypeCheck[float32](v, index)) + } + case types.T_float64: + b := builder.(*array.Float64Builder) + if null { + b.AppendNull() + } else { + b.Append(vector.GetFixedAtNoTypeCheck[float64](v, index)) + } + case types.T_decimal64: + b := builder.(*array.Decimal128Builder) + if null { + b.AppendNull() + } else { + b.Append(decimal64ToArrow(vector.GetFixedAtNoTypeCheck[types.Decimal64](v, index))) + } + case types.T_decimal128: + b := builder.(*array.Decimal128Builder) + if null { + b.AppendNull() + } else { + value := vector.GetFixedAtNoTypeCheck[types.Decimal128](v, index) + b.Append(decimal128.New(int64(value.B64_127), value.B0_63)) + } + case types.T_char, types.T_varchar, types.T_text, types.T_json: + b := builder.(*array.StringBuilder) + if null { + b.AppendNull() + } else { + p := vector.GenerateFunctionStrParameter(v) + value, _ := p.GetStrValue(uint64(index)) + if typ.Oid == types.T_json { + value = []byte(types.DecodeJson(value).String()) + } + b.Append(string(value)) + } + case types.T_binary, types.T_varbinary, types.T_blob: + b := builder.(*array.BinaryBuilder) + if null { + b.AppendNull() + } else { + p := vector.GenerateFunctionStrParameter(v) + value, _ := p.GetStrValue(uint64(index)) + b.Append(value) + } + case types.T_uuid: + b := builder.(*array.FixedSizeBinaryBuilder) + if null { + b.AppendNull() + } else { + value := vector.GetFixedAtNoTypeCheck[types.Uuid](v, index) + b.Append(value[:]) + } + case types.T_array_float32: + b := builder.(*array.FixedSizeListBuilder) + if null { + b.AppendNull() + } else { + b.Append(true) + b.ValueBuilder().(*array.Float32Builder).AppendValues(vector.GetArrayAt[float32](v, index), nil) + } + case types.T_array_float64: + b := builder.(*array.FixedSizeListBuilder) + if null { + b.AppendNull() + } else { + b.Append(true) + b.ValueBuilder().(*array.Float64Builder).AppendValues(vector.GetArrayAt[float64](v, index), nil) + } + default: + return fmt.Errorf("unsupported input type %s", typ.String()) + } + return nil +} +func appendTemporalInput(builder *array.StructBuilder, v *vector.Vector, typ types.Type, index int, null bool) error { + zero := false + var raw int64 + switch typ.Oid { + case types.T_date: + if !null { + value := vector.GetFixedAtNoTypeCheck[types.Date](v, index) + zero = value == types.ZeroDate + if !zero { + raw = int64(value.DaysSinceUnixEpoch()) + } + } + builder.Append(!null) + builder.FieldBuilder(0).(*array.BooleanBuilder).Append(zero) + builder.FieldBuilder(1).(*array.Date32Builder).Append(arrow.Date32(raw)) + case types.T_datetime: + if !null { + value := vector.GetFixedAtNoTypeCheck[types.Datetime](v, index) + zero = value == types.ZeroDatetime + if !zero { + raw = int64(value) - types.GetUnixEpochSecs() + } + } + builder.Append(!null) + builder.FieldBuilder(0).(*array.BooleanBuilder).Append(zero) + builder.FieldBuilder(1).(*array.TimestampBuilder).Append(arrow.Timestamp(raw)) + case types.T_timestamp: + if !null { + value := vector.GetFixedAtNoTypeCheck[types.Timestamp](v, index) + zero = value == types.ZeroTimestamp + if !zero { + raw = int64(value) - types.GetUnixEpochSecs() + } + } + builder.Append(!null) + builder.FieldBuilder(0).(*array.BooleanBuilder).Append(zero) + builder.FieldBuilder(1).(*array.TimestampBuilder).Append(arrow.Timestamp(raw)) + default: + return fmt.Errorf("unsupported temporal type %s", typ.String()) + } + return nil +} +func decimal64ToArrow(value types.Decimal64) decimal128.Num { + if value.Sign() { + return decimal128.New(-1, uint64(value)) + } + return decimal128.New(0, uint64(value)) +} + +func DecodeRecordBatch(schemaFrame, batchFrame ArrowFrame, maxBytes int64) (arrow.RecordBatch, error) { + if len(schemaFrame.Header) == 0 || len(batchFrame.Header) == 0 || maxBytes <= 0 { + return nil, fmt.Errorf("invalid Arrow output frames") + } + if _, err := arrowipc.InspectMessage(context.Background(), schemaFrame.Header, arrowipc.ValidationOptions{MaxMetadataBytes: arrowipc.DefaultMaxMetadataBytes, MaxBodyBytes: 0, BodyEnvelopeBytes: 0, MaxDecodedRecordBytes: 1}); err != nil { + return nil, err + } + if _, err := arrowipc.InspectMessage(context.Background(), batchFrame.Header, arrowipc.ValidationOptions{MaxMetadataBytes: arrowipc.DefaultMaxMetadataBytes, MaxBodyBytes: maxBytes, BodyEnvelopeBytes: int64(len(batchFrame.Body)), Body: batchFrame.Body, ValidateBody: true, MaxDecodedRecordBytes: maxBytes}); err != nil { + return nil, err + } + stream := make([]byte, 0, len(schemaFrame.Header)+len(batchFrame.Header)+len(batchFrame.Body)+32) + stream = appendIPCFrame(stream, schemaFrame.Header, schemaFrame.Body) + stream = appendIPCFrame(stream, batchFrame.Header, batchFrame.Body) + stream = append(stream, 0, 0, 0, 0, 0, 0, 0, 0) + reader, err := ipc.NewReader(bytes.NewReader(stream), ipc.WithAllocator(memory.NewGoAllocator())) + if err != nil { + return nil, fmt.Errorf("decode Arrow output: %w", err) + } + defer reader.Release() + if !reader.Next() { + return nil, fmt.Errorf("decode Arrow output: %v", reader.Err()) + } + record := reader.RecordBatch() + if record == nil { + return nil, fmt.Errorf("decode Arrow output: missing record batch") + } + record.Retain() + return record, nil +} +func appendIPCFrame(dst, header, body []byte) []byte { + metadataLength := (len(header) + 7) &^ 7 + var prefix [8]byte + binary.LittleEndian.PutUint32(prefix[:4], math.MaxUint32) + binary.LittleEndian.PutUint32(prefix[4:], uint32(metadataLength)) + dst = append(dst, prefix[:]...) + dst = append(dst, header...) + for len(dst)%8 != 0 { + dst = append(dst, 0) + } + dst = append(dst, body...) + for len(dst)%8 != 0 { + dst = append(dst, 0) + } + return dst +} + +func AppendArrowResult(descriptor TypeDescriptor, input arrow.Array, result vector.FunctionResultWrapper, mp *mpool.MPool) error { + if input == nil || result == nil || input.Len() == 0 { + return nil + } + if mp == nil { + return fmt.Errorf("python runtime: missing memory pool for Arrow result") + } + if err := validateArrayType(descriptor, input); err != nil { + return err + } + result.GetResultVector().SetTypeScale(descriptor.Scale) + typ := types.T(descriptor.TypeID) + for i := 0; i < input.Len(); i++ { + null := input.IsNull(i) + switch typ { + case types.T_bool: + if err := vector.MustFunctionResult[bool](result).Append(input.(*array.Boolean).Value(i), null); err != nil { + return err + } + case types.T_int8: + if err := vector.MustFunctionResult[int8](result).Append(input.(*array.Int8).Value(i), null); err != nil { + return err + } + case types.T_int16: + if err := vector.MustFunctionResult[int16](result).Append(input.(*array.Int16).Value(i), null); err != nil { + return err + } + case types.T_int32: + if err := vector.MustFunctionResult[int32](result).Append(input.(*array.Int32).Value(i), null); err != nil { + return err + } + case types.T_int64: + if err := vector.MustFunctionResult[int64](result).Append(input.(*array.Int64).Value(i), null); err != nil { + return err + } + case types.T_uint8: + if err := vector.MustFunctionResult[uint8](result).Append(input.(*array.Uint8).Value(i), null); err != nil { + return err + } + case types.T_uint16: + if err := vector.MustFunctionResult[uint16](result).Append(input.(*array.Uint16).Value(i), null); err != nil { + return err + } + case types.T_uint32: + if err := vector.MustFunctionResult[uint32](result).Append(input.(*array.Uint32).Value(i), null); err != nil { + return err + } + case types.T_uint64: + if err := vector.MustFunctionResult[uint64](result).Append(input.(*array.Uint64).Value(i), null); err != nil { + return err + } + case types.T_float32: + if err := vector.MustFunctionResult[float32](result).Append(input.(*array.Float32).Value(i), null); err != nil { + return err + } + case types.T_float64: + if err := vector.MustFunctionResult[float64](result).Append(input.(*array.Float64).Value(i), null); err != nil { + return err + } + case types.T_decimal64: + value, err := decimalFromArray(input.(*array.Decimal128).Value(i), true) + if err != nil { + return err + } + if err = vector.MustFunctionResult[types.Decimal64](result).Append(value.(types.Decimal64), null); err != nil { + return err + } + case types.T_decimal128: + value, err := decimalFromArray(input.(*array.Decimal128).Value(i), false) + if err != nil { + return err + } + if err = vector.MustFunctionResult[types.Decimal128](result).Append(value.(types.Decimal128), null); err != nil { + return err + } + case types.T_char, types.T_varchar, types.T_text: + if err := vector.MustFunctionResult[types.Varlena](result).AppendBytes([]byte(input.(*array.String).Value(i)), null); err != nil { + return err + } + case types.T_json: + if null { + if err := vector.MustFunctionResult[types.Varlena](result).AppendBytes(nil, true); err != nil { + return err + } + continue + } + encoded, err := bytejson.ParseJsonByteFromString(input.(*array.String).Value(i)) + if err != nil { + return fmt.Errorf("TYPE_CONTRACT: invalid JSON result: %w", err) + } + if err = vector.MustFunctionResult[types.Varlena](result).AppendBytes(encoded, false); err != nil { + return err + } + case types.T_binary, types.T_varbinary, types.T_blob: + if err := vector.MustFunctionResult[types.Varlena](result).AppendBytes(input.(*array.Binary).Value(i), null); err != nil { + return err + } + case types.T_uuid: + var value types.Uuid + if !null { + copy(value[:], input.(*array.FixedSizeBinary).Value(i)) + } + if err := vector.MustFunctionResult[types.Uuid](result).Append(value, null); err != nil { + return err + } + case types.T_time: + if err := vector.MustFunctionResult[types.Time](result).Append(types.Time(input.(*array.Duration).Value(i)), null); err != nil { + return err + } + case types.T_array_float32: + if err := appendFloat32ArrayResult(input.(*array.FixedSizeList), i, null, result, mp); err != nil { + return err + } + case types.T_array_float64: + if err := appendFloat64ArrayResult(input.(*array.FixedSizeList), i, null, result, mp); err != nil { + return err + } + case types.T_date, types.T_datetime, types.T_timestamp: + if err := appendTemporalResult(input.(*array.Struct), typ, i, result); err != nil { + return err + } + default: + return fmt.Errorf("unsupported output type %s", typ.String()) + } + } + return nil +} + +func appendFloat32ArrayResult(input *array.FixedSizeList, row int, null bool, result vector.FunctionResultWrapper, mp *mpool.MPool) error { + if null { + return vector.AppendArray[float32](result.GetResultVector(), nil, true, mp) + } + start, end := input.ValueOffsets(row) + values := input.ListValues().(*array.Float32) + items := make([]float32, end-start) + for i := start; i < end; i++ { + items[i-start] = values.Value(int(i)) + } + return vector.AppendArray(result.GetResultVector(), items, false, mp) +} + +func appendFloat64ArrayResult(input *array.FixedSizeList, row int, null bool, result vector.FunctionResultWrapper, mp *mpool.MPool) error { + if null { + return vector.AppendArray[float64](result.GetResultVector(), nil, true, mp) + } + start, end := input.ValueOffsets(row) + values := input.ListValues().(*array.Float64) + items := make([]float64, end-start) + for i := start; i < end; i++ { + items[i-start] = values.Value(int(i)) + } + return vector.AppendArray(result.GetResultVector(), items, false, mp) +} +func validateArrayType(descriptor TypeDescriptor, input arrow.Array) error { + want, err := descriptor.arrowType() + if err != nil { + return err + } + if input.DataType().Fingerprint() != want.Fingerprint() { + return fmt.Errorf("TYPE_CONTRACT: output Arrow type %s does not match %s", input.DataType(), want) + } + return nil +} +func decimalFromArray(value decimal128.Num, narrow bool) (any, error) { + high, low := uint64(value.HighBits()), value.LowBits() + if narrow && high != 0 && high != math.MaxUint64 { + return nil, fmt.Errorf("TYPE_CONTRACT: Decimal128 result does not fit Decimal64") + } + if narrow { + return types.Decimal64(low), nil + } + return types.Decimal128{B0_63: low, B64_127: high}, nil +} +func appendTemporalResult(input *array.Struct, typ types.T, index int, result vector.FunctionResultWrapper) error { + if input.IsNull(index) { + return appendTemporalNull(typ, result) + } + zero := input.Field(0).(*array.Boolean).Value(index) + if typ == types.T_date { + value := types.ZeroDate + if !zero { + value = types.DaysFromUnixEpochToDate(int32(input.Field(1).(*array.Date32).Value(index))) + } + return vector.MustFunctionResult[types.Date](result).Append(value, false) + } + if typ == types.T_datetime { + value := types.ZeroDatetime + if !zero { + value = types.Datetime(input.Field(1).(*array.Timestamp).Value(index)) + types.Datetime(types.GetUnixEpochSecs()) + } + return vector.MustFunctionResult[types.Datetime](result).Append(value, false) + } + value := types.ZeroTimestamp + if !zero { + value = types.Timestamp(input.Field(1).(*array.Timestamp).Value(index)) + types.Timestamp(types.GetUnixEpochSecs()) + } + return vector.MustFunctionResult[types.Timestamp](result).Append(value, false) +} +func appendTemporalNull(typ types.T, result vector.FunctionResultWrapper) error { + switch typ { + case types.T_date: + return vector.MustFunctionResult[types.Date](result).Append(0, true) + case types.T_datetime: + return vector.MustFunctionResult[types.Datetime](result).Append(0, true) + default: + return vector.MustFunctionResult[types.Timestamp](result).Append(0, true) + } +} diff --git a/pkg/udf/pythonruntime/arrow_test.go b/pkg/udf/pythonruntime/arrow_test.go new file mode 100644 index 0000000000000..97ed943333f54 --- /dev/null +++ b/pkg/udf/pythonruntime/arrow_test.go @@ -0,0 +1,75 @@ +// Copyright 2026 Matrix Origin +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. + +package pythonruntime + +import ( + "testing" + + "github.com/apache/arrow-go/v18/arrow" + "github.com/apache/arrow-go/v18/arrow/array" + "github.com/apache/arrow-go/v18/arrow/memory" + "github.com/matrixorigin/matrixone/pkg/container/types" + "github.com/stretchr/testify/require" +) + +func TestArrowFieldMetadataIsExact(t *testing.T) { + descriptor, err := NewTypeDescriptor(types.T_varchar.ToType()) + require.NoError(t, err) + field, err := descriptor.Field("result") + require.NoError(t, err) + require.Equal(t, []string{TypeMetadataKey, TypeFingerprintKey}, field.Metadata.Keys()) + require.NoError(t, descriptor.ValidateField(field)) + + withExtra := field + withExtra.Metadata = arrow.MetadataFrom(map[string]string{ + TypeMetadataKey: field.Metadata.ToMap()[TypeMetadataKey], + TypeFingerprintKey: field.Metadata.ToMap()[TypeFingerprintKey], + "untrusted": "must be rejected", + }) + require.Error(t, descriptor.ValidateField(withExtra)) +} + +func TestEncodeRecordBatchUsesFlightPayloadFrames(t *testing.T) { + descriptor, err := NewTypeDescriptor(types.T_int64.ToType()) + require.NoError(t, err) + field, err := descriptor.Field("arg_0") + require.NoError(t, err) + builder := array.NewInt64Builder(memory.NewGoAllocator()) + builder.AppendValues([]int64{1, 2, 3}, nil) + values := builder.NewInt64Array() + defer values.Release() + record := array.NewRecordBatch(arrow.NewSchema([]arrow.Field{field}, nil), []arrow.Array{values}, 3) + defer record.Release() + + frames, err := EncodeRecordBatch(record, DefaultMaxBatchBytes) + require.NoError(t, err) + require.Len(t, frames, 2) + // FlightData.DataHeader is the raw IPC FlatBuffer, without the stream + // continuation prefix. DecodeRecordBatch adds that prefix back only for + // the local bounded decoder. + require.NotEqual(t, []byte{0xff, 0xff, 0xff, 0xff}, frames[0].Header[:4]) + decoded, err := DecodeRecordBatch(frames[0], frames[1], DefaultMaxBatchBytes) + require.NoError(t, err) + defer decoded.Release() + require.Equal(t, int64(3), decoded.NumRows()) + decodedValues := decoded.Column(0).(*array.Int64) + require.Equal(t, int64(1), decodedValues.Value(0)) + require.Equal(t, int64(2), decodedValues.Value(1)) + require.Equal(t, int64(3), decodedValues.Value(2)) +} + +func TestZeroArgumentRecordPreservesRows(t *testing.T) { + record := array.NewRecordBatch(arrow.NewSchema(nil, nil), nil, 3) + defer record.Release() + frames, err := EncodeRecordBatch(record, DefaultMaxBatchBytes) + require.NoError(t, err) + require.Len(t, frames, 2) + decoded, err := DecodeRecordBatch(frames[0], frames[1], DefaultMaxBatchBytes) + require.NoError(t, err) + defer decoded.Release() + require.Equal(t, int64(3), decoded.NumRows()) + require.Equal(t, int64(0), decoded.NumCols()) +} diff --git a/pkg/udf/pythonruntime/config.go b/pkg/udf/pythonruntime/config.go new file mode 100644 index 0000000000000..c6b6ae301d25b --- /dev/null +++ b/pkg/udf/pythonruntime/config.go @@ -0,0 +1,56 @@ +// Copyright 2026 Matrix Origin +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 + +package pythonruntime + +import ( + "context" + "time" + + "github.com/matrixorigin/matrixone/pkg/common/moerr" +) + +type Config struct { + UUID string `toml:"uuid"` + Address string `toml:"address"` + Path string `toml:"path"` + Python string `toml:"python"` +} + +func (c *Config) Validate() error { + if c.Address == "" { + return moerr.NewInternalError(context.Background(), "missing python runtime address") + } + if c.Path == "" { + return moerr.NewInternalError(context.Background(), "missing python runtime path") + } + return nil +} + +type ClientConfig struct { + ServerAddress string `toml:"server-address"` + MaxBatchBytes int64 `toml:"max-batch-bytes"` + MaxBatchRows int64 `toml:"max-batch-rows"` + RequestTimeout time.Duration `toml:"request-timeout"` +} + +func (c *ClientConfig) Validate() error { + if c.ServerAddress == "" { + return moerr.NewInternalError(context.Background(), "missing python runtime address") + } + if c.MaxBatchBytes < 0 || c.MaxBatchBytes > 1<<30 { + return moerr.NewInternalError(context.Background(), "invalid python runtime max batch bytes") + } + if c.MaxBatchRows < 0 || c.MaxBatchRows > 1<<30 { + return moerr.NewInternalError(context.Background(), "invalid python runtime max batch rows") + } + if c.RequestTimeout < 0 { + return moerr.NewInternalError(context.Background(), "invalid python runtime request timeout") + } + return nil +} diff --git a/pkg/udf/pythonruntime/gateway.go b/pkg/udf/pythonruntime/gateway.go new file mode 100644 index 0000000000000..433393d1f330e --- /dev/null +++ b/pkg/udf/pythonruntime/gateway.go @@ -0,0 +1,536 @@ +// Copyright 2026 Matrix Origin +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 + +package pythonruntime + +import ( + "bytes" + "context" + "encoding/json" + "errors" + "fmt" + "io" + "sync" + "time" + + "github.com/apache/arrow-go/v18/arrow/flight/gen/flight" + "github.com/matrixorigin/matrixone/pkg/common/mpool" + "github.com/matrixorigin/matrixone/pkg/container/types" + "github.com/matrixorigin/matrixone/pkg/container/vector" + "github.com/matrixorigin/matrixone/pkg/udf" + "github.com/matrixorigin/matrixone/pkg/udf/protocol" + "google.golang.org/grpc" + "google.golang.org/grpc/credentials/insecure" +) + +const ( + ModeScalar = "SCALAR" + ModeVector = "VECTOR" + NullCallHandler = "CALLED_ON_NULL_INPUT" + NullReturnNull = "RETURNS_NULL_ON_NULL_INPUT" + statusOK = "OK" +) + +type Gateway struct { + cfg ClientConfig + conn *grpc.ClientConn + flight flight.FlightServiceClient + mu sync.Mutex + lifecycle sync.RWMutex +} + +func NewGateway(cfg ClientConfig) (*Gateway, error) { + if err := cfg.Validate(); err != nil { + return nil, err + } + if cfg.MaxBatchBytes == 0 { + cfg.MaxBatchBytes = DefaultMaxBatchBytes + } + if cfg.MaxBatchRows == 0 { + cfg.MaxBatchRows = DefaultMaxBatchRows + } + if cfg.RequestTimeout == 0 { + cfg.RequestTimeout = 30 * time.Second + } + return &Gateway{cfg: cfg}, nil +} + +func (g *Gateway) Language() string { return udf.LanguagePython } + +func (g *Gateway) connect() error { + g.mu.Lock() + defer g.mu.Unlock() + if g.flight != nil { + return nil + } + conn, err := grpc.NewClient(g.cfg.ServerAddress, + grpc.WithTransportCredentials(insecure.NewCredentials()), + grpc.WithDefaultCallOptions( + grpc.MaxCallSendMsgSize(int(g.cfg.MaxBatchBytes)+1<<20), + grpc.MaxCallRecvMsgSize(int(g.cfg.MaxBatchBytes)+1<<20), + ), + ) + if err != nil { + return err + } + g.conn = conn + g.flight = flight.NewFlightServiceClient(conn) + return nil +} + +func (g *Gateway) Close() error { + g.lifecycle.Lock() + defer g.lifecycle.Unlock() + g.mu.Lock() + defer g.mu.Unlock() + if g.conn == nil { + return nil + } + err := g.conn.Close() + g.conn = nil + g.flight = nil + return err +} + +type openPayload struct { + Handler string `json:"handler"` + Source string `json:"source"` + Mode string `json:"mode"` + NullPolicy string `json:"null_policy"` + ABIContract string `json:"abi_contract"` + AdapterVersion string `json:"adapter_version"` + SDKVersion string `json:"sdk_version"` + Context map[string]string `json:"context,omitempty"` + Args []TypeDescriptor `json:"args"` + Return TypeDescriptor `json:"return"` + MaxBatchBytes int64 `json:"max_batch_bytes"` + MaxBatchRows int64 `json:"max_batch_rows"` +} + +func (g *Gateway) Execute(ctx context.Context, invocation *udf.Invocation, result vector.FunctionResultWrapper, mp *mpool.MPool) error { + g.lifecycle.RLock() + defer g.lifecycle.RUnlock() + if ctx == nil { + ctx = context.Background() + } + if invocation == nil || result == nil || mp == nil { + return fmt.Errorf("python runtime: nil invocation, result, or memory pool") + } + if err := validateInvocation(invocation); err != nil { + return err + } + if invocation.Length == 0 { + return result.PreExtendAndReset(0) + } + if err := g.connect(); err != nil { + return err + } + + args := make([]TypeDescriptor, len(invocation.Args)) + for i, typ := range invocation.Args { + var err error + args[i], err = NewTypeDescriptor(typ) + if err != nil { + return err + } + } + returnDescriptor, err := NewTypeDescriptor(invocation.ReturnType) + if err != nil { + return err + } + openBody, err := json.Marshal(openPayload{Handler: invocation.Handler, Source: invocation.Source, Mode: invocation.Mode, NullPolicy: invocation.NullPolicy, ABIContract: invocation.ABIContract, AdapterVersion: invocation.AdapterVersion, SDKVersion: invocation.SDKVersion, Context: cloneMap(invocation.Context), Args: args, Return: returnDescriptor, MaxBatchBytes: g.cfg.MaxBatchBytes, MaxBatchRows: g.cfg.MaxBatchRows}) + if err != nil { + return fmt.Errorf("python runtime: encode open payload: %w", err) + } + open, err := protocol.MarshalControl(protocol.Control{Kind: "OpenInvocation", Tuple: invocation.Tuple, Payload: openBody}) + if err != nil { + return err + } + + stream, err := g.flight.DoExchange(ctx) + if err != nil { + return fmt.Errorf("python runtime: open Arrow Flight exchange: %w", err) + } + // Flight carries the invocation command in the descriptor. The descriptor + // is delivered to the server separately from the Arrow IPC reader; putting + // the JSON command in AppMetadata on this message makes PyArrow try to + // decode it as an IPC message. + if err := stream.Send(&flight.FlightData{FlightDescriptor: &flight.FlightDescriptor{Type: flight.FlightDescriptor_CMD, Cmd: open}}); err != nil { + return err + } + if err := result.PreExtendAndReset(invocation.Length); err != nil { + return err + } + + var sequence protocol.Sequence + var schemaFrame *ArrowFrame + var inputSchema []byte + rows := 0 + var start int64 + var batchIndex uint64 + var lastSequence uint64 + var inputEnded bool + for start < int64(invocation.Length) { + batch, err := encodeInputBatch(invocation.Inputs, invocation.Args, start, int64(invocation.Length)-start, g.cfg.MaxBatchBytes, g.cfg.MaxBatchRows) + if err != nil { + return err + } + if len(batch.Frames) != 2 { + return fmt.Errorf("python runtime: input batch contains %d Arrow frames", len(batch.Frames)) + } + if batchIndex == 0 { + inputSchema = append([]byte(nil), batch.Frames[0].Header...) + if err := stream.Send(&flight.FlightData{DataHeader: batch.Frames[0].Header}); err != nil { + return err + } + } else if !bytes.Equal(inputSchema, batch.Frames[0].Header) { + return fmt.Errorf("python runtime: input batches do not share one Arrow schema") + } + sequenceNumber := batchIndex + 1 + if err := sequence.AcceptInput(sequenceNumber); err != nil { + return err + } + inputControl, err := protocol.MarshalControl(protocol.Control{Kind: "InputBatch", Tuple: invocation.Tuple, Sequence: sequenceNumber}) + if err != nil { + return err + } + if err := stream.Send(&flight.FlightData{DataHeader: batch.Frames[1].Header, DataBody: batch.Frames[1].Body, AppMetadata: inputControl}); err != nil { + return err + } + lastSequence = sequenceNumber + isLastBatch := start+batch.Rows == int64(invocation.Length) + if isLastBatch { + // EndInput is an application-level half-close. Send it before + // waiting for the final result so the runtime must keep its result + // direction alive when that result is delayed on the wire. + endInput, err := protocol.MarshalControl(protocol.Control{Kind: "EndInput", Tuple: invocation.Tuple, LastSequence: lastSequence}) + if err != nil { + return err + } + if err := stream.Send(&flight.FlightData{AppMetadata: endInput}); err != nil { + return err + } + if err := sequence.EndInput(lastSequence); err != nil { + return err + } + if err := stream.CloseSend(); err != nil { + return err + } + inputEnded = true + } + batchRows, err := g.receiveResultBatch(ctx, stream, invocation.Tuple, sequenceNumber, batch.Rows, &schemaFrame, returnDescriptor, result, mp, &sequence) + if err != nil { + return err + } + rows += batchRows + start += batch.Rows + batchIndex++ + if isLastBatch { + break + } + } + if !inputEnded || lastSequence != batchIndex { + return fmt.Errorf("python runtime: input stream did not reach EndInput") + } + return g.receiveFinish(ctx, stream, invocation.Tuple, invocation.Length, rows, &sequence) +} + +func encodeInputBatch(inputs []*vector.Vector, args []types.Type, start, remaining, maxBytes, maxRows int64) (encodedRecordBatch, error) { + if start < 0 || remaining <= 0 { + return encodedRecordBatch{}, fmt.Errorf("invalid Python UDF input batch range") + } + if maxRows <= 0 { + return encodedRecordBatch{}, fmt.Errorf("invalid Python UDF max batch rows") + } + if remaining > maxRows { + remaining = maxRows + } + tryEncode := func(rows int64) ([]ArrowFrame, error) { + record, _, err := BuildInputRecordRange(inputs, args, int(start), int(rows)) + if err != nil { + return nil, err + } + frames, encodeErr := EncodeRecordBatch(record, maxBytes) + record.Release() + return frames, encodeErr + } + best, probe := int64(0), int64(1) + var bestFrames []ArrowFrame + failedAt := int64(0) + for { + frames, err := tryEncode(probe) + if err == nil { + best, bestFrames = probe, frames + if probe == remaining { + return encodedRecordBatch{Frames: bestFrames, Rows: best}, nil + } + if probe > remaining/2 { + probe = remaining + } else { + probe *= 2 + } + continue + } + if !errors.Is(err, errArrowBatchTooLarge) { + return encodedRecordBatch{}, err + } + failedAt = probe + break + } + low, high := best+1, failedAt-1 + for low <= high { + middle := low + (high-low)/2 + frames, err := tryEncode(middle) + if err == nil { + best, bestFrames = middle, frames + low = middle + 1 + continue + } + if !errors.Is(err, errArrowBatchTooLarge) { + return encodedRecordBatch{}, err + } + high = middle - 1 + } + if best == 0 { + return encodedRecordBatch{}, fmt.Errorf("%w: one Arrow row exceeds %d bytes", errArrowBatchTooLarge, maxBytes) + } + return encodedRecordBatch{Frames: bestFrames, Rows: best}, nil +} + +func (g *Gateway) receiveResultBatch( + ctx context.Context, + stream flight.FlightService_DoExchangeClient, + tuple protocol.FencingTuple, + sequenceNumber uint64, + expectedRows int64, + schemaFrame **ArrowFrame, + returnDescriptor TypeDescriptor, + result vector.FunctionResultWrapper, + mp *mpool.MPool, + sequence *protocol.Sequence, +) (int, error) { + for { + data, err := stream.Recv() + if err != nil { + if err == io.EOF { + return 0, fmt.Errorf("python runtime: exchange ended before result sequence %d", sequenceNumber) + } + return 0, fmt.Errorf("python runtime: receive result: %w", err) + } + if data == nil || (len(data.AppMetadata) == 0 && len(data.DataHeader) == 0) { + return 0, fmt.Errorf("python runtime: empty Flight result") + } + if len(data.DataHeader) > 0 { + if *schemaFrame == nil { + if len(data.DataBody) != 0 { + return 0, fmt.Errorf("python runtime: result schema contains a body") + } + *schemaFrame = &ArrowFrame{Header: append([]byte(nil), data.DataHeader...)} + if len(data.AppMetadata) > 0 { + control, err := protocol.UnmarshalControl(data.AppMetadata) + if err != nil || control.Kind != "ResultSchema" { + return 0, fmt.Errorf("python runtime: invalid result schema metadata") + } + } + continue + } + control, err := protocol.UnmarshalControl(data.AppMetadata) + if err != nil { + return 0, err + } + if control.Tuple != tuple || control.Kind != "ResultBatch" || control.Sequence != sequenceNumber { + return 0, fmt.Errorf("python runtime: unexpected result sequence %d", control.Sequence) + } + if err := sequence.AcceptResult(control.Sequence); err != nil { + return 0, err + } + snapshot, err := protocol.FreezeOutput(data.DataBody, g.cfg.MaxBatchBytes) + if err != nil { + return 0, err + } + if err := snapshot.Validate(len(data.DataBody), snapshot.Digest()); err != nil { + return 0, err + } + decoded, err := DecodeRecordBatch(**schemaFrame, ArrowFrame{Header: append([]byte(nil), data.DataHeader...), Body: snapshot.TrustedBytes()}, g.cfg.MaxBatchBytes) + if err != nil { + return 0, fmt.Errorf("python runtime: decode result: %w", err) + } + if decoded.NumCols() != 1 || decoded.NumRows() <= 0 || decoded.NumRows() != expectedRows { + decoded.Release() + return 0, fmt.Errorf("python runtime: result batch row count %d, expected %d", decoded.NumRows(), expectedRows) + } + if decoded.Schema().Field(0).Name != "result" { + decoded.Release() + return 0, fmt.Errorf("python runtime: result field has unexpected name %q", decoded.Schema().Field(0).Name) + } + if err := returnDescriptor.ValidateField(decoded.Schema().Field(0)); err != nil { + decoded.Release() + return 0, err + } + if err := AppendArrowResult(returnDescriptor, decoded.Column(0), result, mp); err != nil { + decoded.Release() + return 0, err + } + decoded.Release() + if err := g.action(ctx, "AcknowledgeResults", protocol.Control{Kind: "AcknowledgeResults", Tuple: tuple, AckSequence: control.Sequence}); err != nil { + return 0, err + } + if err := sequence.AcknowledgeResults(control.Sequence); err != nil { + return 0, err + } + return int(expectedRows), nil + } + control, err := protocol.UnmarshalControl(data.AppMetadata) + if err != nil { + return 0, err + } + if control.Tuple != tuple { + return 0, fmt.Errorf("python runtime: result tuple changed") + } + switch control.Kind { + case "ResultSchema": + continue + case "InputConsumed": + if control.Sequence != sequenceNumber { + return 0, fmt.Errorf("python runtime: input sequence %d was consumed while waiting for result %d", control.Sequence, sequenceNumber) + } + continue + case "Error": + return 0, fmt.Errorf("python runtime: worker error: %s", control.Reason) + case "Finish": + return 0, fmt.Errorf("python runtime: Finish arrived before result sequence %d", sequenceNumber) + default: + return 0, fmt.Errorf("python runtime: unexpected control %q", control.Kind) + } + } +} + +func (g *Gateway) receiveFinish( + ctx context.Context, + stream flight.FlightService_DoExchangeClient, + tuple protocol.FencingTuple, + expectedRows, rows int, + sequence *protocol.Sequence, +) error { + for { + data, err := stream.Recv() + if err != nil { + if err == io.EOF { + return fmt.Errorf("python runtime: exchange ended before Finish") + } + return fmt.Errorf("python runtime: receive Finish: %w", err) + } + if data == nil || len(data.DataHeader) > 0 { + return fmt.Errorf("python runtime: unexpected Arrow data while finishing") + } + control, err := protocol.UnmarshalControl(data.AppMetadata) + if err != nil { + return err + } + if control.Tuple != tuple { + return fmt.Errorf("python runtime: Finish tuple changed") + } + switch control.Kind { + case "InputConsumed", "ResultSchema": + continue + case "Error": + return fmt.Errorf("python runtime: worker error: %s", control.Reason) + case "Finish": + lastInput, lastResult, acked, inputEnded := sequence.State() + if control.Status != statusOK || control.FinishID == "" || rows != expectedRows || + !inputEnded || control.LastSequence != lastInput || lastResult != lastInput || acked != lastInput || !sequence.ReadyToFinish() { + return fmt.Errorf("python runtime: invalid Finish status=%q rows=%d", control.Status, rows) + } + return g.action(ctx, "AcknowledgeFinish", protocol.Control{Kind: "AcknowledgeFinish", Tuple: tuple, FinishID: control.FinishID}) + default: + return fmt.Errorf("python runtime: unexpected control %q", control.Kind) + } + } +} + +func validateInvocation(invocation *udf.Invocation) error { + if invocation.Language != "" && invocation.Language != udf.LanguagePython { + return fmt.Errorf("python runtime: unsupported language %q", invocation.Language) + } + if invocation.Handler == "" || invocation.Source == "" { + return fmt.Errorf("python runtime: handler and source are required") + } + if invocation.Length < 0 || len(invocation.Args) != len(invocation.Inputs) { + return fmt.Errorf("python runtime: invalid input shape") + } + if invocation.Mode == "" { + invocation.Mode = ModeScalar + } + if invocation.Mode != ModeScalar && invocation.Mode != ModeVector { + return fmt.Errorf("python runtime: unsupported mode %q", invocation.Mode) + } + if invocation.NullPolicy == "" { + invocation.NullPolicy = NullCallHandler + } + if invocation.NullPolicy != NullCallHandler && invocation.NullPolicy != NullReturnNull { + return fmt.Errorf("python runtime: unsupported NULL policy %q", invocation.NullPolicy) + } + if invocation.ABIContract == "" { + invocation.ABIContract = udf.PythonABIContract + } + if invocation.AdapterVersion == "" { + invocation.AdapterVersion = udf.PythonAdapterVersion + } + if invocation.SDKVersion == "" { + invocation.SDKVersion = udf.PythonSDKVersion + } + if invocation.ABIContract != udf.PythonABIContract || invocation.AdapterVersion != udf.PythonAdapterVersion { + return fmt.Errorf("python runtime: unsupported Python ABI contract %q/%q", invocation.ABIContract, invocation.AdapterVersion) + } + return invocation.Tuple.Validate() +} + +func (g *Gateway) action(ctx context.Context, kind string, control protocol.Control) error { + body, err := protocol.MarshalControl(control) + if err != nil { + return err + } + actionCtx, cancel := context.WithTimeout(ctx, g.cfg.RequestTimeout) + defer cancel() + stream, err := g.flight.DoAction(actionCtx, &flight.Action{Type: kind, Body: body}) + if err != nil { + return fmt.Errorf("python runtime: %s: %w", kind, err) + } + result, err := stream.Recv() + if err != nil { + return err + } + if result == nil { + return fmt.Errorf("python runtime: empty %s response", kind) + } + ack, err := protocol.UnmarshalControl(result.Body) + if err != nil { + return err + } + if ack.Status != statusOK { + return fmt.Errorf("python runtime: %s rejected: %s", kind, ack.Reason) + } + if _, err := stream.Recv(); err != io.EOF { + if err == nil { + return fmt.Errorf("python runtime: %s returned multiple results", kind) + } + return err + } + return nil +} + +func cloneMap(input map[string]string) map[string]string { + if len(input) == 0 { + return nil + } + out := make(map[string]string, len(input)) + for k, v := range input { + out[k] = v + } + return out +} diff --git a/pkg/udf/pythonruntime/pyserver/requirements.txt b/pkg/udf/pythonruntime/pyserver/requirements.txt new file mode 100644 index 0000000000000..d2a628aab1355 --- /dev/null +++ b/pkg/udf/pythonruntime/pyserver/requirements.txt @@ -0,0 +1,3 @@ +# The worker is a separate sandbox process. The runtime image must pin the +# complete environment lock used when the routine revision was published. +pyarrow==24.0.0 diff --git a/pkg/udf/pythonruntime/pyserver/test_worker.py b/pkg/udf/pythonruntime/pyserver/test_worker.py new file mode 100644 index 0000000000000..147808c6a7db6 --- /dev/null +++ b/pkg/udf/pythonruntime/pyserver/test_worker.py @@ -0,0 +1,87 @@ +#!/usr/bin/env python3 + +import datetime +import importlib.util +import pathlib +import sys +import unittest + +import pyarrow as pa + + +WORKER_PATH = pathlib.Path(__file__).with_name("worker.py") +spec = importlib.util.spec_from_file_location("matrixone_python_worker", WORKER_PATH) +worker = importlib.util.module_from_spec(spec) +sys.modules[spec.name] = worker +spec.loader.exec_module(worker) + + +class WorkerContractTest(unittest.TestCase): + def test_go_python_fingerprint_vectors(self): + cases = [ + ({"type_id": worker.INT64, "offset_width": 32}, "482d3c55b8a9d662c2b41326b3ca55cbae65153e662c47c4919ba020d006262b"), + ({"type_id": worker.VARCHAR, "width": 65535, "charset": 3, "offset_width": 32}, "e35cb66adb4bce519a6f3ec9cfa8a33adad6202cbe678f5fecffdeb28d65b824"), + ({"type_id": worker.JSON, "offset_width": 32, "json_encoding": "canonical_text"}, "b156af5241a070ff80a287da19c46d1593dabee4fb5429f964452e711cd275eb"), + ({"type_id": worker.DATE, "offset_width": 32, "temporal_encoding": "sql_zero_struct"}, "8c401bde992eb173ca8aedca97dbd9f47453b7a860fae199ae0439307e858b4b"), + ] + for descriptor, expected in cases: + self.assertEqual(expected, worker._fingerprint(descriptor)) + + def test_metadata_is_owned_by_adapter(self): + descriptor = {"type_id": worker.VARCHAR, "width": 65535, "charset": 3, "offset_width": 32} + field = worker._field("result", descriptor) + self.assertEqual(field.metadata, { + worker.TypeMetadataKey: b'{"type_id":61,"width":65535,"charset":3,"offset_width":32}', + worker.TypeFingerprintKey: b"e35cb66adb4bce519a6f3ec9cfa8a33adad6202cbe678f5fecffdeb28d65b824", + }) + with_extra = pa.field("result", field.type, metadata={**field.metadata, b"untrusted": b"1"}) + with self.assertRaisesRegex(ValueError, "logical metadata"): + worker._validate_field(with_extra, "result", descriptor) + with self.assertRaisesRegex(ValueError, "field name"): + worker._validate_field(pa.field("other", field.type, metadata=field.metadata), "result", descriptor) + + def test_zero_temporal_is_distinct_from_null(self): + descriptor = {"type_id": worker.DATE, "offset_width": 32, "temporal_encoding": "sql_zero_struct"} + zero = worker.SqlDate(True, None) + array = worker._output_array([zero, None, worker.SqlDate(False, datetime.date(2024, 1, 2))], descriptor, 3) + self.assertTrue(array[0].is_valid) + self.assertFalse(array[1].is_valid) + self.assertTrue(array[2].is_valid) + self.assertEqual(True, array.field("is_zero")[0].as_py()) + self.assertEqual(datetime.date(1970, 1, 1), array.field("value")[0].as_py()) + + def test_zero_argument_vector_uses_context_rows(self): + descriptor = {"type_id": worker.INT64, "offset_width": 32} + output = pa.array([7, 7, 7], type=pa.int64()) + self.assertEqual(3, len(worker._output_array(output, descriptor, 3))) + with self.assertRaisesRegex(ValueError, "result length"): + worker._output_array(pa.array([7], type=pa.int64()), descriptor, 3) + + def test_terminal_admission_does_not_evict_live_tombstones(self): + old_records = worker.MAX_TERMINAL_RECORDS + old_bytes = worker.MAX_TERMINAL_BYTES + try: + worker.MAX_TERMINAL_RECORDS = 1 + worker.MAX_TERMINAL_BYTES = 1 << 20 + server = worker.RoutineFlightServer("grpc://127.0.0.1:0") + first = (1, "statement", "group", 1, "invocation", 1) + state = server._admit(first) + with server._lock: + server._active.pop(first) + server._active_bytes -= state.terminal_bytes + server._remember_terminal_locked(first, state) + with self.assertRaisesRegex(ValueError, "entries are full"): + server._admit((1, "statement", "group", 1, "second", 1)) + self.assertIn(first, server._terminal) + with server._lock: + server._terminal[first] = (0.0, state.terminal_bytes) + server._purge_terminal_locked(1.0) + second = server._admit((1, "statement", "group", 1, "second", 1)) + self.assertIsNotNone(second) + finally: + worker.MAX_TERMINAL_RECORDS = old_records + worker.MAX_TERMINAL_BYTES = old_bytes + + +if __name__ == "__main__": + unittest.main() diff --git a/pkg/udf/pythonruntime/pyserver/worker.py b/pkg/udf/pythonruntime/pyserver/worker.py new file mode 100644 index 0000000000000..ef12c8ea37f30 --- /dev/null +++ b/pkg/udf/pythonruntime/pyserver/worker.py @@ -0,0 +1,642 @@ +# Copyright 2026 Matrix Origin +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. + +"""Arrow Flight worker for the MatrixOne Python routine contract. + +The process is deliberately a small protocol endpoint. It does not install +packages, read SQL data, or interpret user supplied paths. Production +launchers must place it in the sandbox class selected by the routine policy. +""" + +from __future__ import annotations + +import argparse +import datetime as _datetime +import decimal as _decimal +import hashlib +import importlib +import inspect +import json +import logging +import threading +import time +import uuid as _uuid +from collections import OrderedDict +from dataclasses import dataclass +from typing import Any, Dict, Iterable, Optional + +import pyarrow as pa +import pyarrow.flight as flight + +PROTOCOL_VERSION = 1 +MAX_CONTROL_BYTES = 1 << 20 +MAX_TERMINAL_RECORDS = 10000 +MAX_TERMINAL_BYTES = 16 << 20 +TERMINAL_TTL_SECONDS = 300.0 +ACK_TIMEOUT_SECONDS = 60.0 +DEFAULT_MAX_BATCH_ROWS = 65536 + +BOOL = 10 +INT8, INT16, INT32, INT64 = 20, 21, 22, 23 +UINT8, UINT16, UINT32, UINT64 = 25, 26, 27, 28 +FLOAT32, FLOAT64 = 30, 31 +DECIMAL64, DECIMAL128 = 32, 33 +DATE, TIME, DATETIME, TIMESTAMP = 50, 51, 52, 53 +CHAR, VARCHAR, JSON, UUID = 60, 61, 62, 63 +BINARY, VARBINARY = 64, 65 +BLOB, TEXT = 70, 71 +VECF32, VECF64 = 224, 225 + +MODE_SCALAR = "SCALAR" +MODE_VECTOR = "VECTOR" +NULL_CALL = "CALLED_ON_NULL_INPUT" +NULL_RETURN = "RETURNS_NULL_ON_NULL_INPUT" +ABI_CONTRACT = "PYTHON_ARROW" +ADAPTER_VERSION = "2026-09" +SDK_VERSION = "1.0" + +log = logging.getLogger("matrixone.python.runtime") + + +@dataclass(frozen=True, slots=True) +class SqlDate: + is_zero: bool + value: Optional[_datetime.date] + + +@dataclass(frozen=True, slots=True) +class SqlDatetime: + is_zero: bool + value: Optional[_datetime.datetime] + + +@dataclass(frozen=True, slots=True) +class SqlTimestamp: + is_zero: bool + value: Optional[_datetime.datetime] + + +class _CallLogger: + __slots__ = () + + def info(self, message: str, *args: Any) -> None: + log.info("user: " + str(message), *args) + + def warning(self, message: str, *args: Any) -> None: + log.warning("user: " + str(message), *args) + + def error(self, message: str, *args: Any) -> None: + log.error("user: " + str(message), *args) + + +@dataclass(frozen=True, slots=True) +class ScalarContext: + sdk_version: str + logger: _CallLogger + statement: Any = None + + +@dataclass(frozen=True, slots=True) +class VectorContext: + sdk_version: str + logger: _CallLogger + statement: Any = None + num_rows: int = 0 + + +def _tuple_key(value: Dict[str, Any]) -> tuple: + required = ("account_id", "statement_id", "group_id", "group_epoch", "invocation_id", "lease_epoch") + if any(not value.get(k) for k in required): + raise ValueError("PROTOCOL: incomplete fencing tuple") + return tuple(value[k] for k in required) + + +def _decode_control(data: bytes) -> Dict[str, Any]: + if not data or len(data) > MAX_CONTROL_BYTES: + raise ValueError("PROTOCOL: invalid control size") + value = json.loads(bytes(data).decode("utf-8")) + if value.get("version") != PROTOCOL_VERSION or not value.get("kind"): + raise ValueError("PROTOCOL: unsupported control") + _tuple_key(value.get("tuple") or {}) + return value + + +def _encode_control(value: Dict[str, Any]) -> bytes: + value = dict(value) + value.setdefault("version", PROTOCOL_VERSION) + _tuple_key(value["tuple"]) + data = json.dumps(value, separators=(",", ":"), ensure_ascii=True).encode("utf-8") + if len(data) > MAX_CONTROL_BYTES: + raise ValueError("PROTOCOL: control is too large") + return data + + +def _canonical_descriptor(descriptor: Dict[str, Any]) -> bytes: + order = ("type_id", "width", "scale", "charset", "offset_width", "json_encoding", "temporal_encoding") + result = {} + for key in order: + value = descriptor.get(key) + if key == "type_id" or value not in (None, 0, ""): + result[key] = value + return json.dumps(result, separators=(",", ":"), ensure_ascii=True).encode("utf-8") + + +def _physical_fingerprint(descriptor: Dict[str, Any]) -> str: + type_id = int(descriptor["type_id"]) + primitive = { + BOOL: "@B", INT8: "@D", INT16: "@F", INT32: "@H", INT64: "@J", + UINT8: "@C", UINT16: "@E", UINT32: "@G", UINT64: "@I", + FLOAT32: "@L", FLOAT64: "@M", BINARY: "@O", BLOB: "@O", VARBINARY: "@O", + TIME: "@bu", + } + if type_id in primitive: + return primitive[type_id] + if type_id in (CHAR, VARCHAR, TEXT, JSON): + return "@d" if int(descriptor.get("offset_width", 32)) == 64 else "@N" + if type_id == UUID: + return "@P" + if type_id in (DECIMAL64, DECIMAL128): + precision = int(descriptor.get("width") or (18 if type_id == DECIMAL64 else 38)) + return f"@X[128,{precision},{int(descriptor.get('scale') or 0)}]" + if type_id == DATE: + return "@[{FNis_zero{@B};FNvalue{@Q};}" + if type_id == DATETIME: + return "@[{FNis_zero{@B};FNvalue{@Su0:};}" + if type_id == TIMESTAMP: + return "@[{FNis_zero{@B};FNvalue{@Su3:UTC};}" + if type_id == VECF32: + return f"@a[{int(descriptor.get('width') or 0)}]{{@L}}" + if type_id == VECF64: + return f"@a[{int(descriptor.get('width') or 0)}]{{@M}}" + raise ValueError(f"TYPE_CONTRACT: unsupported type id {type_id}") + + +def _fingerprint(descriptor: Dict[str, Any]) -> str: + data = b"matrixone-python-udf-type\x00" + _canonical_descriptor(descriptor) + b"\x00" + _physical_fingerprint(descriptor).encode() + return hashlib.sha256(data).hexdigest() + + +def _arrow_type(descriptor: Dict[str, Any]) -> pa.DataType: + type_id = int(descriptor["type_id"]) + simple = { + BOOL: pa.bool_, INT8: pa.int8, INT16: pa.int16, INT32: pa.int32, INT64: pa.int64, + UINT8: pa.uint8, UINT16: pa.uint16, UINT32: pa.uint32, UINT64: pa.uint64, + FLOAT32: pa.float32, FLOAT64: pa.float64, + } + if type_id in simple: + return simple[type_id]() + if type_id in (DECIMAL64, DECIMAL128): + precision = int(descriptor.get("width") or (18 if type_id == DECIMAL64 else 38)) + return pa.decimal128(precision, int(descriptor.get("scale") or 0)) + if type_id in (CHAR, VARCHAR, TEXT, JSON): + return pa.large_string() if int(descriptor.get("offset_width", 32)) == 64 else pa.string() + if type_id in (BINARY, VARBINARY, BLOB): + return pa.large_binary() if int(descriptor.get("offset_width", 32)) == 64 else pa.binary() + if type_id == UUID: + return pa.binary(16) + if type_id == TIME: + return pa.duration("us") + if type_id in (VECF32, VECF64): + width = int(descriptor.get("width") or 0) + if width <= 0: + raise ValueError("TYPE_CONTRACT: vector dimension must be positive") + return pa.list_(pa.float32() if type_id == VECF32 else pa.float64(), width) + if type_id == DATE: + child = pa.date32() + elif type_id == DATETIME: + child = pa.timestamp("us") + elif type_id == TIMESTAMP: + child = pa.timestamp("us", tz="UTC") + else: + raise ValueError(f"TYPE_CONTRACT: unsupported type id {type_id}") + return pa.struct([pa.field("is_zero", pa.bool_(), nullable=False), pa.field("value", child, nullable=False)]) + + +def _field(name: str, descriptor: Dict[str, Any]) -> pa.Field: + metadata = { + TypeMetadataKey: _canonical_descriptor(descriptor), + TypeFingerprintKey: _fingerprint(descriptor).encode("ascii"), + } + return pa.field(name, _arrow_type(descriptor), nullable=True, metadata=metadata) + + +TypeMetadataKey = b"mo.udf.type" +TypeFingerprintKey = b"mo.udf.type_fingerprint" + + +def _validate_field(field: pa.Field, name: str, descriptor: Dict[str, Any]) -> None: + expected = _field(name, descriptor) + if field.name != name: + raise ValueError(f"TYPE_CONTRACT: Arrow field name {field.name!r} does not match {name!r}") + if field.type != expected.type: + raise ValueError(f"TYPE_CONTRACT: Arrow type {field.type} does not match {expected.type}") + if field.metadata != expected.metadata: + raise ValueError("TYPE_CONTRACT: Arrow logical metadata does not match the frozen descriptor") + + +def _validate_schema(schema: pa.Schema, descriptors: Iterable[Dict[str, Any]]) -> None: + descriptors = list(descriptors) + if len(schema) != len(descriptors): + raise ValueError("TYPE_CONTRACT: input column count does not match the routine signature") + for index, descriptor in enumerate(descriptors): + _validate_field(schema.field(index), f"arg_{index}", descriptor) + + +def _load_handler(source: str, handler: str): + namespace: Dict[str, Any] = {"__name__": "__matrixone_routine__"} + exec(compile(source, "", "exec"), namespace, namespace) + if ":" in handler: + module_name, function_name = handler.split(":", 1) + function = namespace.get(function_name) + if function is None and module_name: + function = getattr(importlib.import_module(module_name), function_name, None) + else: + function = namespace.get(handler) + if not callable(function) or inspect.iscoroutinefunction(function): + raise ValueError("USER_CODE: handler must be a synchronous callable") + return function + + +def _scalar_input(array: pa.Array, row: int, descriptor: Dict[str, Any]): + value = array[row].as_py() + if value is None: + return None + type_id = int(descriptor["type_id"]) + if type_id in (VECF32, VECF64): + # A scalar vector is exposed as read-only bytes. Consumers can use + # memoryview.cast("f"/"d") without receiving a mutable Arrow buffer. + import struct + values = value + fmt = "f" if type_id == VECF32 else "d" + return memoryview(struct.pack("<" + fmt * len(values), *values)) + if type_id in (DATE, DATETIME, TIMESTAMP): + zero = bool(value["is_zero"]) + child = value["value"] + if type_id == DATE: + return SqlDate(zero, None if zero else child) + if type_id == DATETIME: + return SqlDatetime(zero, None if zero else child) + return SqlTimestamp(zero, None if zero else child) + if type_id == UUID: + return _uuid.UUID(bytes=bytes(value)) + return value + + +def _check_json(value: str) -> str: + if not isinstance(value, str): + raise ValueError("TYPE_CONTRACT: JSON result must be canonical text") + try: + parsed = json.loads(value) + except Exception as exc: + raise ValueError("TYPE_CONTRACT: invalid JSON result") from exc + canonical = json.dumps(parsed, ensure_ascii=False, separators=(",", ":")) + if canonical != value: + raise ValueError("TYPE_CONTRACT: JSON result is not canonical text") + return value + + +def _check_scalar(value: Any, descriptor: Dict[str, Any]): + if value is None: + return None + type_id = int(descriptor["type_id"]) + if type_id == BOOL and type(value) is not bool: raise ValueError("TYPE_CONTRACT: expected bool") + if type_id in (INT8, INT16, INT32, INT64, UINT8, UINT16, UINT32, UINT64) and (type(value) is not int): raise ValueError("TYPE_CONTRACT: expected integer") + if type_id in (FLOAT32, FLOAT64) and type(value) is not float: raise ValueError("TYPE_CONTRACT: expected float") + if type_id in (CHAR, VARCHAR, TEXT) and type(value) is not str: raise ValueError("TYPE_CONTRACT: expected string") + if type_id == JSON: return _check_json(value) + if type_id in (BINARY, VARBINARY, BLOB) and type(value) is not bytes: raise ValueError("TYPE_CONTRACT: expected bytes") + if type_id == UUID: + if not isinstance(value, _uuid.UUID): raise ValueError("TYPE_CONTRACT: expected uuid.UUID") + return value.bytes + if type_id == TIME and not isinstance(value, _datetime.timedelta): raise ValueError("TYPE_CONTRACT: expected datetime.timedelta") + if type_id == DATE: + if not isinstance(value, SqlDate) or (value.is_zero and value.value is not None) or (not value.is_zero and type(value.value) is not _datetime.date): raise ValueError("TYPE_CONTRACT: invalid SqlDate") + return value + if type_id == DATETIME: + if not isinstance(value, SqlDatetime) or (value.is_zero and value.value is not None) or (not value.is_zero and (not isinstance(value.value, _datetime.datetime) or value.value.tzinfo is not None)): raise ValueError("TYPE_CONTRACT: invalid SqlDatetime") + return value + if type_id == TIMESTAMP: + if not isinstance(value, SqlTimestamp) or (value.is_zero and value.value is not None) or (not value.is_zero and (not isinstance(value.value, _datetime.datetime) or value.value.tzinfo is None or value.value.utcoffset() != _datetime.timedelta(0))): raise ValueError("TYPE_CONTRACT: invalid SqlTimestamp") + if not value.is_zero and value.value.tzinfo is not _datetime.timezone.utc: + return SqlTimestamp(False, value.value.astimezone(_datetime.timezone.utc)) + return value + if type_id in (VECF32, VECF64): + if not isinstance(value, memoryview) or not value.readonly: + raise ValueError("TYPE_CONTRACT: vector scalar result must be a read-only memoryview") + width = int(descriptor.get("width") or 0) + fmt = "f" if type_id == VECF32 else "d" + try: + values = list(value.cast(fmt)) + except (TypeError, ValueError) as exc: + raise ValueError("TYPE_CONTRACT: invalid vector memoryview") from exc + if len(values) != width: + raise ValueError("TYPE_CONTRACT: vector dimension mismatch") + return values + if type_id in (DECIMAL64, DECIMAL128): + if not isinstance(value, _decimal.Decimal): raise ValueError("TYPE_CONTRACT: expected decimal.Decimal") + scale = int(descriptor.get("scale") or 0) + if -value.as_tuple().exponent != scale: raise ValueError("TYPE_CONTRACT: decimal scale mismatch") + return value + + +def _temporal_array(values: list, descriptor: Dict[str, Any]) -> pa.Array: + type_id = int(descriptor["type_id"]) + valid = [] + zeroes = [] + child_values = [] + for value in values: + if value is None: + valid.append(False); zeroes.append(False); child_values.append(0); continue + value = _check_scalar(value, descriptor) + valid.append(True); zeroes.append(bool(value.is_zero)); child_values.append(0 if value.is_zero else value.value) + if type_id == DATE: child_type = pa.date32() + elif type_id == DATETIME: child_type = pa.timestamp("us") + else: child_type = pa.timestamp("us", tz="UTC") + children = [pa.array(zeroes, type=pa.bool_()), pa.array(child_values, type=child_type)] + return pa.StructArray.from_arrays(children, names=["is_zero", "value"], mask=pa.array([not item for item in valid])) + + +def _output_array(values: Any, descriptor: Dict[str, Any], rows: int) -> pa.Array: + if isinstance(values, pa.ChunkedArray): + values = values.combine_chunks() + if isinstance(values, pa.Array): + if len(values) != rows: raise ValueError("TYPE_CONTRACT: result length does not match input rows") + if values.type != _arrow_type(descriptor): raise ValueError("TYPE_CONTRACT: result Arrow type does not match the return descriptor") + _validate_array_values(values, descriptor) + return values + if not isinstance(values, (list, tuple)): + raise ValueError("TYPE_CONTRACT: VECTOR handler must return an Arrow Array or ChunkedArray") + if len(values) != rows: raise ValueError("TYPE_CONTRACT: result length does not match input rows") + type_id = int(descriptor["type_id"]) + if type_id in (DATE, DATETIME, TIMESTAMP): return _temporal_array(list(values), descriptor) + if type_id in (VECF32, VECF64): + width = int(descriptor.get("width") or 0) + checked = [] + for value in values: + if value is None: + checked.append(None) + continue + checked.append(_check_scalar(value, descriptor)) + try: + child_type = pa.float32() if type_id == VECF32 else pa.float64() + flat = [item for row in checked if row is not None for item in row] + child = pa.array(flat, type=child_type) + mask = pa.array([row is None for row in checked]) + return pa.FixedSizeListArray.from_arrays(child, width, mask=mask) + except (TypeError, ValueError, pa.ArrowException) as exc: + raise ValueError("TYPE_CONTRACT: vector result is outside the declared SQL domain") from exc + checked = [_check_scalar(value, descriptor) for value in values] + if type_id == UUID: checked = [None if value is None else value.bytes for value in checked] + try: return pa.array(checked, type=_arrow_type(descriptor)) + except (TypeError, ValueError, pa.ArrowException) as exc: raise ValueError("TYPE_CONTRACT: result value is outside the declared SQL domain") from exc + + +def _validate_array_values(array: pa.Array, descriptor: Dict[str, Any]) -> None: + type_id = int(descriptor["type_id"]) + if type_id == JSON: + for index in range(len(array)): + if not array.is_null(index): _check_json(array[index].as_py()) + elif type_id in (DATE, DATETIME, TIMESTAMP): + struct = array + for index in range(len(struct)): + if struct.is_null(index): continue + is_zero = bool(struct.field("is_zero")[index].as_py()) + value = struct.field("value")[index].as_py() + if value is None: raise ValueError("TYPE_CONTRACT: temporal child is null") + if is_zero and ((type_id == DATE and value != _datetime.date(1970, 1, 1)) or (type_id == DATETIME and value != _datetime.datetime(1970, 1, 1)) or (type_id == TIMESTAMP and value != _datetime.datetime(1970, 1, 1, tzinfo=_datetime.timezone.utc))): + raise ValueError("TYPE_CONTRACT: temporal zero placeholder is invalid") + + +class _InvocationState: + def __init__(self, tuple_value: Dict[str, Any], terminal_bytes: int): + self.tuple = tuple_value + self.terminal_bytes = terminal_bytes + self.condition = threading.Condition() + self.last_result = 0 + self.acked_result = 0 + self.finish_id: Optional[str] = None + self.finish_acked = False + + def ack_result(self, sequence: int) -> None: + with self.condition: + if sequence < self.acked_result or sequence > self.last_result: + raise ValueError("PROTOCOL: result ACK is outside the received range") + self.acked_result = sequence + self.condition.notify_all() + + def wait_result_ack(self, sequence: int) -> None: + deadline = time.monotonic() + ACK_TIMEOUT_SECONDS + with self.condition: + while self.acked_result < sequence: + remaining = deadline - time.monotonic() + if remaining <= 0: raise TimeoutError("DEADLINE_EXCEEDED: result ACK timeout") + self.condition.wait(remaining) + + def ack_finish(self, finish_id: str) -> None: + with self.condition: + if not self.finish_id or finish_id != self.finish_id: raise ValueError("PROTOCOL: invalid finish id") + self.finish_acked = True + self.condition.notify_all() + + def wait_finish_ack(self) -> None: + deadline = time.monotonic() + ACK_TIMEOUT_SECONDS + with self.condition: + while not self.finish_acked: + remaining = deadline - time.monotonic() + if remaining <= 0: raise TimeoutError("DEADLINE_EXCEEDED: Finish ACK timeout") + self.condition.wait(remaining) + + +class RoutineFlightServer(flight.FlightServerBase): + def __init__(self, location: str): + super().__init__(location) + self._lock = threading.RLock() + self._active: Dict[tuple, _InvocationState] = {} + self._terminal: OrderedDict[tuple, tuple[float, int]] = OrderedDict() + self._terminal_bytes = 0 + self._active_bytes = 0 + + @staticmethod + def _entry_bytes(key: tuple) -> int: + # The admission estimate is deliberately conservative. The tuple is + # already bounded by MAX_CONTROL_BYTES, and its serialized size is + # stable for the lifetime of the fence. + encoded = json.dumps(key, separators=(",", ":"), ensure_ascii=True).encode("utf-8") + return len(encoded) + 128 + + def _purge_terminal_locked(self, now: float) -> None: + expired = [key for key, (deadline, _) in self._terminal.items() if deadline <= now] + for key in expired: + _, size = self._terminal.pop(key) + self._terminal_bytes -= size + + def _admit(self, key: tuple) -> _InvocationState: + size = self._entry_bytes(key) + with self._lock: + self._purge_terminal_locked(time.monotonic()) + if key in self._terminal: + raise ValueError("PROTOCOL: invocation fence is terminal") + if len(self._active) + len(self._terminal) >= MAX_TERMINAL_RECORDS: + raise ValueError("RESOURCE_EXHAUSTED: terminal ledger entries are full") + if self._active_bytes + self._terminal_bytes + size > MAX_TERMINAL_BYTES: + raise ValueError("RESOURCE_EXHAUSTED: terminal ledger bytes are full") + state = _InvocationState({}, size) + self._active[key] = state + self._active_bytes += size + return state + + def _remember_terminal_locked(self, key: tuple, state: _InvocationState) -> None: + deadline = time.monotonic() + TERMINAL_TTL_SECONDS + self._terminal[key] = (deadline, state.terminal_bytes) + self._terminal.move_to_end(key) + self._terminal_bytes += state.terminal_bytes + + def do_action(self, context, action): + control = _decode_control(action.body) + key = _tuple_key(control["tuple"]) + with self._lock: + self._purge_terminal_locked(time.monotonic()) + state = self._active.get(key) + terminal = key in self._terminal + if state is None: + if terminal: + yield _encode_control({"kind": "Ack", "tuple": control["tuple"], "status": "OK"}) + return + raise ValueError("PROTOCOL: unknown invocation") + if action.type == "AcknowledgeResults": + state.ack_result(int(control.get("ack_sequence", 0))) + elif action.type == "AcknowledgeFinish": + state.ack_finish(str(control.get("finish_id", ""))) + else: + raise ValueError("PROTOCOL: unknown action") + yield _encode_control({"kind": "Ack", "tuple": control["tuple"], "status": "OK", "ack_sequence": control.get("ack_sequence", 0)}) + + def do_exchange(self, context, descriptor, reader, writer): + state = None + key = None + writer_started = False + try: + command = getattr(descriptor, "command", None) + if command is None: + raise ValueError("PROTOCOL: exchange is missing the invocation descriptor") + open_control = _decode_control(command) + if open_control["kind"] != "OpenInvocation": raise ValueError("PROTOCOL: first message must open an invocation") + key = _tuple_key(open_control["tuple"]) + payload = open_control.get("payload") or {} + if isinstance(payload, str): + payload = json.loads(payload) + args = list(payload.get("args", [])); result_descriptor = payload["return"] + mode = payload.get("mode", MODE_SCALAR); null_policy = payload.get("null_policy", NULL_CALL) + abi_contract = payload.get("abi_contract", ABI_CONTRACT) + adapter_version = payload.get("adapter_version", ADAPTER_VERSION) + sdk_version = payload.get("sdk_version", SDK_VERSION) + max_batch_rows = int(payload.get("max_batch_rows", DEFAULT_MAX_BATCH_ROWS)) + if mode not in (MODE_SCALAR, MODE_VECTOR) or null_policy not in (NULL_CALL, NULL_RETURN): raise ValueError("PROTOCOL: unsupported call mode or NULL policy") + if abi_contract != ABI_CONTRACT or adapter_version != ADAPTER_VERSION: raise ValueError("PROTOCOL: unsupported Python ABI contract") + if max_batch_rows <= 0: raise ValueError("PROTOCOL: invalid max batch rows") + state = self._admit(key) + state.tuple = open_control["tuple"] + handler = _load_handler(payload["source"], payload["handler"]) + schema = None + result_field = _field("result", result_descriptor) + result_schema = pa.schema([result_field]) + writer.begin(result_schema) + writer_started = True + writer.write_metadata(_encode_control({"kind": "ResultSchema", "tuple": open_control["tuple"]})) + ended = False + while True: + try: + chunk = reader.read_chunk() + except StopIteration: + break + if chunk.data is not None: + if schema is None: + schema = reader.schema + _validate_schema(schema, args) + control = _decode_control(chunk.app_metadata) + if control["kind"] != "InputBatch": raise ValueError("PROTOCOL: data batch is missing InputBatch") + sequence = int(control.get("sequence", 0)) + if sequence != state.last_result + 1: raise ValueError("PROTOCOL: input sequence is not contiguous") + batch = chunk.data + if batch.num_rows <= 0: raise ValueError("PROTOCOL: empty input batch") + if batch.num_rows > max_batch_rows: raise ValueError("RESOURCE_EXHAUSTED: input batch has too many rows") + if mode == MODE_VECTOR: + call_context = VectorContext(sdk_version, _CallLogger(), None, batch.num_rows) + try: + output = handler(call_context, *[batch.column(index) for index in range(batch.num_columns)]) + except Exception as exc: + raise ValueError("USER_CODE: handler failed") from exc + if not isinstance(output, (pa.Array, pa.ChunkedArray)): + raise ValueError("TYPE_CONTRACT: VECTOR handler must return an Arrow Array or ChunkedArray") + else: + call_context = ScalarContext(sdk_version, _CallLogger(), None) + values = [] + for row in range(batch.num_rows): + params = [_scalar_input(batch.column(index), row, args[index]) for index in range(batch.num_columns)] + if null_policy == NULL_RETURN and any(value is None for value in params): values.append(None) + else: + try: + values.append(handler(call_context, *params)) + except Exception as exc: + raise ValueError("USER_CODE: handler failed") from exc + output = values + output_array = _output_array(output, result_descriptor, batch.num_rows) + output_batch = pa.RecordBatch.from_arrays([output_array], schema=result_schema) + state.last_result = sequence + writer.write_metadata(_encode_control({"kind": "InputConsumed", "tuple": open_control["tuple"], "sequence": sequence})) + writer.write_with_metadata(output_batch, _encode_control({"kind": "ResultBatch", "tuple": open_control["tuple"], "sequence": sequence})) + state.wait_result_ack(sequence) + elif chunk.app_metadata: + control = _decode_control(chunk.app_metadata) + if control["kind"] == "EndInput": + if ended or int(control.get("last_sequence", -1)) != state.last_result: raise ValueError("PROTOCOL: invalid EndInput") + ended = True + elif control["kind"] != "OpenInvocation": + raise ValueError("PROTOCOL: unexpected control without Arrow data") + if not ended: raise ValueError("PROTOCOL: input stream ended before EndInput") + if state.acked_result != state.last_result: raise ValueError("PROTOCOL: result is not acknowledged") + state.finish_id = _uuid.uuid4().hex + writer.write_metadata(_encode_control({"kind": "Finish", "tuple": open_control["tuple"], "status": "OK", "finish_id": state.finish_id, "last_sequence": state.last_result})) + state.wait_finish_ack() + except Exception as exc: + if writer_started and key is not None: + try: + writer.write_metadata(_encode_control({"kind": "Error", "tuple": open_control["tuple"], "status": "ERROR", "reason": _safe_error(exc)})) + except Exception: + pass + raise + finally: + if key is not None: + with self._lock: + current = self._active.pop(key, None) + if current is not None: + self._active_bytes -= current.terminal_bytes + # A started invocation is terminal even when the worker + # reports an error. Retaining the fence prevents a late + # retry from running user code a second time; the caller's + # error path remains responsible for reporting failure. + self._remember_terminal_locked(key, current) + + +def _safe_error(exc: Exception) -> str: + message = str(exc) + categories = ("PROTOCOL:", "TYPE_CONTRACT:", "USER_CODE:", "RESOURCE_EXHAUSTED:", "DEADLINE_EXCEEDED:") + if not message.startswith(categories): + return "USER_CODE: handler or runtime failed" + return message[:512] + + +def main() -> None: + parser = argparse.ArgumentParser() + parser.add_argument("--address", required=True) + args = parser.parse_args() + logging.basicConfig(level=logging.INFO, format="[%(asctime)s] %(levelname)s %(message)s") + address = args.address if "://" in args.address else "grpc://" + args.address + server = RoutineFlightServer(address) + server.serve() + + +if __name__ == "__main__": + main() diff --git a/pkg/udf/pythonruntime/supervisor.go b/pkg/udf/pythonruntime/supervisor.go new file mode 100644 index 0000000000000..90c171368a75a --- /dev/null +++ b/pkg/udf/pythonruntime/supervisor.go @@ -0,0 +1,113 @@ +// Copyright 2026 Matrix Origin +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +package pythonruntime + +import ( + "errors" + "fmt" + "io" + "os" + "os/exec" + "path/filepath" + "strconv" + "sync" + "sync/atomic" + + "github.com/matrixorigin/matrixone/pkg/logutil" +) + +type Supervisor struct { + cfg Config + mu sync.Mutex + cmd *exec.Cmd + log io.WriteCloser +} + +var supervisorNumber atomic.Int32 + +func NewSupervisor(cfg Config) (*Supervisor, error) { + if err := cfg.Validate(); err != nil { + return nil, err + } + return &Supervisor{cfg: cfg}, nil +} + +func (s *Supervisor) Start() error { + s.mu.Lock() + defer s.mu.Unlock() + if s.cmd != nil { + return nil + } + workerPath := s.cfg.Path + if !filepath.IsAbs(workerPath) { + cwd, err := os.Getwd() + if err != nil { + return err + } + workerPath = filepath.Join(cwd, workerPath) + } + workerPath = filepath.Join(workerPath, "worker.py") + if _, err := os.Stat(workerPath); err != nil { + return err + } + executable := s.cfg.Python + if executable == "" { + executable = "python" + } + exePath, err := os.Executable() + if err != nil { + return err + } + logPath := filepath.Join(filepath.Dir(exePath), "python-runtime-"+strconv.Itoa(int(supervisorNumber.Add(1))+0)+".log") + logFile, err := os.OpenFile(logPath, os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0644) + if err != nil { + return err + } + cmd := exec.Command(executable, "-u", workerPath, "--address="+s.cfg.Address) + cmd.Stdout, cmd.Stderr = logFile, logFile + if err := cmd.Start(); err != nil { + _ = logFile.Close() + return err + } + s.cmd, s.log = cmd, logFile + go s.wait(cmd, logFile) + logutil.Infof("started Python runtime worker: %s", cmd.String()) + return nil +} + +func (s *Supervisor) wait(cmd *exec.Cmd, log io.WriteCloser) { + err := cmd.Wait() + _ = log.Close() + s.mu.Lock() + if s.cmd == cmd { + s.cmd, s.log = nil, nil + } + s.mu.Unlock() + if err != nil { + logutil.Errorf("Python runtime worker exited: %v", err) + } +} + +func (s *Supervisor) Close() error { + s.mu.Lock() + cmd, log := s.cmd, s.log + s.cmd, s.log = nil, nil + s.mu.Unlock() + if cmd == nil { + if log != nil { + return log.Close() + } + return nil + } + if err := cmd.Process.Kill(); err != nil && !errors.Is(err, os.ErrProcessDone) { + return fmt.Errorf("stop Python runtime worker: %w", err) + } + if log != nil { + return log.Close() + } + return nil +} diff --git a/pkg/udf/pythonservice/cfg.go b/pkg/udf/pythonservice/cfg.go deleted file mode 100644 index 30df376f7c0ce..0000000000000 --- a/pkg/udf/pythonservice/cfg.go +++ /dev/null @@ -1,50 +0,0 @@ -// Copyright 2023 Matrix Origin -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package pythonservice - -import ( - "context" - "github.com/matrixorigin/matrixone/pkg/common/moerr" -) - -type Config struct { - UUID string `toml:"uuid"` - Address string `toml:"address"` - // Path code path - Path string `toml:"path"` - // Python is the python executable path, optional, default to 'python' - Python string `toml:"python"` -} - -func (c *Config) Validate() error { - if c.Address == "" { - return moerr.NewInternalError(context.Background(), "missing python udf server address") - } - if c.Path == "" { - return moerr.NewInternalError(context.Background(), "missing python udf server path") - } - return nil -} - -type ClientConfig struct { - ServerAddress string `toml:"server-address"` -} - -func (c *ClientConfig) Validate() error { - if c.ServerAddress == "" { - return moerr.NewInternalError(context.Background(), "missing python udf server address") - } - return nil -} diff --git a/pkg/udf/pythonservice/client.go b/pkg/udf/pythonservice/client.go deleted file mode 100644 index 0abaae0e42bc6..0000000000000 --- a/pkg/udf/pythonservice/client.go +++ /dev/null @@ -1,160 +0,0 @@ -// Copyright 2023 Matrix Origin -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package pythonservice - -import ( - "context" - "io" - "math" - "sync" - - "google.golang.org/grpc" - "google.golang.org/grpc/credentials/insecure" - - "github.com/matrixorigin/matrixone/pkg/common/moerr" - "github.com/matrixorigin/matrixone/pkg/udf" -) - -type Client struct { - cfg ClientConfig - sc udf.ServiceClient - mutex sync.Mutex -} - -func NewClient(cfg ClientConfig) (*Client, error) { - err := cfg.Validate() - if err != nil { - return nil, err - } - return &Client{cfg: cfg}, nil -} - -func (c *Client) init() error { - if c.sc == nil { - err := func() error { - c.mutex.Lock() - defer c.mutex.Unlock() - if c.sc == nil { - conn, err := grpc.NewClient(c.cfg.ServerAddress, - grpc.WithTransportCredentials(insecure.NewCredentials()), - grpc.WithDefaultCallOptions( - grpc.MaxCallSendMsgSize(math.MaxInt32), - grpc.MaxCallRecvMsgSize(math.MaxInt32), - ), - ) - if err != nil { - return err - } - c.sc = udf.NewServiceClient(conn) - } - return nil - }() - if err != nil { - return err - } - } - return nil -} - -func (c *Client) Run(ctx context.Context, request *udf.Request, pkgReader udf.PkgReader) (*udf.Response, error) { - if request.Udf.Language != udf.LanguagePython { - return nil, moerr.NewInvalidArg(ctx, "udf language", request.Udf.Language) - } - - if request.Type != udf.RequestType_DataRequest { - return nil, moerr.NewInvalidInput(ctx, "type of the first udf request must be 'DataRequest'") - } - - err := c.init() - if err != nil { - return nil, err - } - - stream, err := c.sc.Run(ctx) - if err != nil { - return nil, err - } - defer stream.CloseSend() - err = stream.Send(request) - if err != nil { - return nil, err - } - response, err := stream.Recv() - if err != nil { - return nil, err - } - switch response.Type { - case udf.ResponseType_DataResponse: - return response, nil - case udf.ResponseType_PkgRequest: - var reader io.ReadCloser - reader, err = pkgReader.Get(ctx, request.Udf.Body) - if reader != nil { - defer reader.Close() - } - if err != nil { - return nil, err - } - - pkgRequest := &udf.Request{ - Udf: request.Udf, - Type: udf.RequestType_PkgResponse, - Context: request.Context, - } - for { - pkg := &udf.Package{} - buffer := make([]byte, 5*1024*1024) - var offset int - for { - var n int - n, err = reader.Read(buffer[offset:]) - if err != nil { - if err == io.EOF { - pkg.Last = true - } else { - return nil, err - } - } - offset += n - - if pkg.Last || len(buffer) == offset { - pkg.Data = buffer[:offset] - break - } - } - - pkgRequest.Udf.ImportPkg = pkg - err = stream.Send(pkgRequest) - if err != nil { - return nil, err - } - - response, err = stream.Recv() - if err != nil { - return nil, err - } - - if pkg.Last { - return response, nil - } - } - default: - return nil, moerr.NewInternalError(ctx, "error udf response type") - } -} - -func (c *Client) Language() string { - return udf.LanguagePython -} diff --git a/pkg/udf/pythonservice/demo/README.md b/pkg/udf/pythonservice/demo/README.md deleted file mode 100644 index c68dce2404808..0000000000000 --- a/pkg/udf/pythonservice/demo/README.md +++ /dev/null @@ -1,295 +0,0 @@ -# MO Python UDF Demo - -This document briefly describes the use of python UDFs in MO, showing the flexibility and convenience of python UDFs. - -## Standalone Deployment - -1. Before the deployment, we need to make sure we have [python3](https://www.python.org/downloads/) locally and install the grpc package in python: - -``` -pip install protobuf -pip install grpcio -``` - -2. Follow the steps in [Getting Started](https://docs.matrixorigin.cn/en/1.0.0-rc1/MatrixOne/Get-Started/install-standalone-matrixone/) to complete the installation, and modify the configuration of mo_ctl by running the following command before starting MO: - -```shell -mo_ctl set_conf MO_CONF_FILE="${MO_PATH}/matrixone/etc/launch-with-python-udf-server/launch.toml" -``` - -3. Launch and connect to MO. - -## "Hello world" - -Let's start with a simple function: a python UDF that adds two numbers together. - -Input the following SQL in the client to create the function: - -```sql -create or replace function py_add(a int, b int) returns int language python as -$$ -def add(a, b): - return a + b -$$ -handler 'add'; -``` - -> Note: currently, matrixone does not perform syntax checking on the python script in UDF body. - -This SQL defines a function called py_add that takes two parameters of type `int` and returns the sum of the two parameters. Python code comes after `as`. `handler` represents the name of the python function that will be called. - -Now we can use the function: - -```sql -select py_add(1,2); -``` - -Output: - -``` -+--------------+ -| py_add(1, 2) | -+--------------+ -| 3 | -+--------------+ -``` - -We can remove the function when we no longer need it: - -```sql -drop function py_add(int, int); -``` - -We now have a basic understanding of how to use python UDFs. - -## Advancement - -In this section, we'll look at some more advanced uses of python UDFs. - -### Imported Python UDF - -In the previous section, the py_add function is written directly in SQL, and the python code is written after the keyword `as`. We call this form **embedded UDF**. However, if the function logic is very complex, writing it directly in SQL is not a good choice. It bloats the SQL statements and makes the code less maintainable. In this case, we can choose another way to create python UDFs: importing python code from an external file, known as **imported UDF**. There are two types of files that are supported: (1) a single python file, and (2) the wheel package. Let's continue with the example of the py_add function from the previous section, and write the python code to a file. - -The code for ./add func.py is as follows: - -```python -def add(a, b): - return a + b -``` - -Input the following SQL in the client to create the function(if you connect to matrixone via `mysql` cli, `--local-infile` flag is required to allow the cli reading local python files): - -```sql -create or replace function py_add(a int, b int) returns int language python import -'./add_func.py' -handler 'add'; -``` - -We use the keyword `import` to import the flie add_func.py from the current working directory. - -Call the function: - -```sql -select py_add(1,2); -``` - -We still get the same output as in the previous section: - -``` -+--------------+ -| py_add(1, 2) | -+--------------+ -| 3 | -+--------------+ -``` - -Importing a wheel package and a single python file share the same syntax, so we won't go into detail here. In the final example, we will create a python UDF by importing a wheel package. - -### Vector Option - -In some scenarios, we may want a python function to receive multiple tuples at once to improve the efficiency. For example, in model inference, we typically process data in batches, where each batch is a vector of tuples. The vector option is designed to handle this situation. We'll continue to use the py_add function as an example to demonstrate the usage of the vector option. - -Input the following SQL in the client to create the function: - -```sql -create or replace function py_add(a int, b int) returns int language python as -$$ -def add(a, b): # a, b are list - return [a[i] + b[i] for i in range(len(a))] -add.vector = True -$$ -handler 'add'; -``` - -We use `add.vector = True` to indicate that the python function add should receive two integer lists (vectors) as input, rather than individual integer values. - -Call the function: - -```sql -select py_add(1,2); -``` - -We still get the same output as in the previous section: - -``` -+--------------+ -| py_add(1, 2) | -+--------------+ -| 3 | -+--------------+ -``` - -With the vector option, we have the freedom to choose how the function processes its input, whether it's one tuple at a time or multiple tuples at once. - -### Type Mapping - -The correspondence between MO types and python types is as follows: - -| MO Types | Python Types | -| -------------------------------------------------------- | --------------------------- | -| bool | bool | -| int8, int16, int32, int64, uint8, uint16, uint32, uint64 | int | -| float32, float64 | float | -| char, varchar, text, uuid | str | -| json | str, int, float, list, dict | -| time | datetime.timedelta | -| date | datetime.date | -| datetime, timestamp | datetime.datetime | -| decimal64, decimal128 | decimal.Decimal | -| binary, varbinary, blob | bytes | - -### Function Overloading and Matching - -Overloading is not supported in MO UDF yet, all function names must be unique across an MO cluster. - -## Example: Credit Card Fraud Detection - -In this section, we use the example "Credit card fraud detection" to illustrate how python UDFs can be used in a machine learning inference pipeline. (Detailed code in [github](https://github.com/matrixorigin/matrixone/tree/main/pkg/udf/pythonservice/demo)) - -We need to ensure that the local python environment has numpy and scikit-learn installed. - -### Background - -It is important that credit card companies are able to recognize fraudulent credit card transactions so that customers are not charged for items that they did not purchase. (Details in kaggle [Credit Card Fraud Detection](https://www.kaggle.com/datasets/mlg-ulb/creditcardfraud)) - -### Data - -The dataset contains transactions made by credit cards in September 2013 by European cardholders. The data format is as follows: - -| Column Name | Type | Explanation | -| ----------- | ------ | ------------------------------------------------------------ | -| Time | int | Number of seconds elapsed between this transaction and the first transaction in the dataset | -| V1 ~ V28 | double | May be result of a PCA Dimensionality reduction to protect user identities and sensitive features | -| Amount | double | Transaction amount | -| Class | int | 1 for fraudulent transactions, 0 otherwise | - -We split the data into training, validation, and test sets in an 8:1:1 ratio. Since the training process is not the focus. we won't go into much detail here. We treat the test set as new data appearing in the production process, and store it in MO. The DDL is as follows: - -```sql -create table credit_card_transaction( - id int auto_increment primary key, - time int, - features json, -- store V1 ~ V28, using json list - amount decimal(20,2) -); -``` - -### Inference - -After the model is trained, we can write the inference function. The core code is as follows: - -```python -import decimal -import os -from typing import List - -import joblib -import numpy as np - -# model path -model_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'model_with_scaler') - - -def detect(featuresList: List[List[int]], amountList: List[decimal.Decimal]) -> List[bool]: - # load model - model_with_scaler = joblib.load(model_path) - - columns_features = np.array(featuresList) - column_amount = np.array(amountList, dtype='float').reshape(-1, 1) - # normalize the amount - column_amount = model_with_scaler['amount_scaler'].transform(column_amount) - data = np.concatenate((columns_features, column_amount), axis=1) - # model inference - predictions = model_with_scaler['model'].predict(data) - return [pred == 1 for pred in predictions.tolist()] - -# enable vector option -detect.vector = True -``` - -Then write setup.py to build the wheel package: - -```python -from setuptools import setup, find_packages - -setup( - name="detect", - version="1.0.0", - packages=find_packages(), - package_data={ - 'credit': ['model_with_scaler'] # 模型 - }, -) -``` - -The project directory structure is as follows: - -``` -|-- demo/ - |-- credit/ - |-- __init__.py - |-- detection.py # inference function - |-- model_with_scaler # model - |-- setup.py -``` - -Build the wheel package detect-1.0.0-py3-none-any.whl with the command `python setup.py bdist_wheel`. - -Create the function: - -```sql -create or replace function py_detect(features json, amount decimal) returns bool language python import --- replace with the directory where the wheel package is located -'your_code_path/detect-1.0.0-py3-none-any.whl' --- the function detect in the module credit -handler 'credit.detect'; -``` - -Call the function: - -```sql -select id, py_detect(features, amount) as is_fraud from credit_card_transaction limit 10; -``` - -Output: - -``` -+---------+----------+ -| id | is_fraud | -+---------+----------+ -| 1 | false | -| 2 | false | -| 3 | true | -| 4 | false | -| 5 | false | -| 6 | false | -| 7 | false | -| 8 | true | -| 9 | false | -| 10 | false | -+---------+----------+ -``` - -Now, we have completed the inference of task credit card fraud detection in MO. - -From this example, it's clear that we can easily use Python UDFs to tackle tasks that SQL alone cannot handle. Python UDFs not only extend the semantics of SQL but also spare us from writing data movement and transformation programs manually, greatly enhancing the development efficiency. diff --git a/pkg/udf/pythonservice/demo/README_CN.md b/pkg/udf/pythonservice/demo/README_CN.md deleted file mode 100644 index 392c90c6d5b67..0000000000000 --- a/pkg/udf/pythonservice/demo/README_CN.md +++ /dev/null @@ -1,293 +0,0 @@ -# MO Python UDF Demo - -本文简述了 MO 中 python UDF 的使用方式,展示了 python UDF 的灵活与便捷。 - -## 单机快速部署 - -1. 在部署之前,我们需要确保本地拥有 [python3](https://www.python.org/downloads/) 环境,并在 python 环境下安装 grpc 相关的 package: - -``` -pip install protobuf -pip install grpcio -``` - -2. 按照[快速开始](https://docs.matrixorigin.cn/1.0.0-rc1/MatrixOne/Get-Started/install-standalone-matrixone/)中的步骤完成相关安装,在启动 MO 之前,执行以下命令修改 mo_ctl 的配置: - -```shell -mo_ctl set_conf MO_CONF_FILE="${MO_PATH}/matrixone/etc/launch-with-python-udf-server/launch.toml" -``` - -3. 启动并连接 MO。 - -## "Hello world" - -我们从一个简单的函数开始说起:用 python UDF 实现两数之和。 - -在客户端中输入以下 SQL 来创建函数: - -```sql -create or replace function py_add(a int, b int) returns int language python as -$$ -def add(a, b): - return a + b -$$ -handler 'add'; -``` - -这段 SQL 定义了一个名为 py_add 的函数,接收两个类型为 `int` 的参数,其功能是返回两个参数的和,具体逻辑在 `as` 后的 python 代码中,`handler` 表示调用的 python 函数名称。 - -> 注意: 当前版本 matrixone 不会在创建 UDF 时检查 python 语法,用户需要自行保证 python 语法的正确性,否则在执行后续执行函数时会报错。 - -现在便可以使用该函数了: - -```sql -select py_add(1,2); -``` - -输出: - -``` -+--------------+ -| py_add(1, 2) | -+--------------+ -| 3 | -+--------------+ -``` - -当我们不再需要该函数时,可以将其删除: - -```sql -drop function py_add(int, int); -``` - -至此,我们已经掌握了 python UDF 的基本用法。 - -## 进阶 - -本节主要介绍 python UDF 的一些进阶用法。 - -### 导入式 python UDF - -在上一节中,py_add 函数是直接写在 SQL 中的,python 代码写在 `as` 关键字之后,我们称这种形式为**嵌入式 UDF**。然而,如果函数逻辑十分复杂,直接写在 SQL 中并不是一个好的选择,这会让 SQL 语句膨胀而且不利于代码的维护。这时,我们可以选择另一种创建 python UDF 的方式:从外部文件中导入 python 代码,即**导入式 UDF**。支持导入的文件有两种:(1) 单个 python 文件,(2) wheel 包。我们仍旧以上一节的 py_add 函数为例,把 python 代码写到文件中。 - -./add_func.py 代码如下: - -```python -def add(a, b): - return a + b -``` - -在客户端中输入以下 SQL 来创建函数(使用 `mysql` 命令行工具连接 matrixone 时,需要添加 `--local-infile` 允许工具读取本地的 Python 文件): - -```sql -create or replace function py_add(a int, b int) returns int language python import -'./add_func.py' -handler 'add'; -``` - -其中,我们使用 `import` 关键字来导入当前工作目录下的 add_func.py 文件。 - -调用函数: - -```sql -select py_add(1,2); -``` - -仍旧可以得到与上一节相同的输出: - -``` -+--------------+ -| py_add(1, 2) | -+--------------+ -| 3 | -+--------------+ -``` - -导入 wheel 包与单个 python 文件具有相同的语法,这里不再赘述。在最后的案例中,我们将会以导入 wheel 包的形式来创建 python UDF。 - -### Vector 选项 - -在某些场景下,我们会希望 python 函数一次性接收多个元组来提高运行效率。如在模型推理时,我们通常是以一个 batch 为单位进行的,这里的 batch 即为元组的 vector。而 vector 选项正是用来处理这种情况的。我们仍旧以 py_add 函数为例,展示 vector 选项的用法。 - -在客户端中输入以下 SQL 来创建函数: - -```sql -create or replace function py_add(a int, b int) returns int language python as -$$ -def add(a, b): # a, b are list - return [a[i] + b[i] for i in range(len(a))] -add.vector = True -$$ -handler 'add'; -``` - -其中,我们使用 `add.vector = True` 来标记 python 函数 add 接收两个 int 列表(vector)而不是 int 值。 - -调用函数: - -```sql -select py_add(1,2); -``` - -仍旧可以得到与上一节相同的输出: - -``` -+--------------+ -| py_add(1, 2) | -+--------------+ -| 3 | -+--------------+ -``` - -有了 vector 选项,我们就可以自由地选择函数的处理形式:一次一元组,或者一次多元组。 - -### 类型映射 - -MO 类型与 python 类型的对应关系如下: - -| MO 类型 | Python 类型 | -| -------------------------------------------------------- | --------------------------- | -| bool | bool | -| int8, int16, int32, int64, uint8, uint16, uint32, uint64 | int | -| float32, float64 | float | -| char, varchar, text, uuid | str | -| json | str, int, float, list, dict | -| time | datetime.timedelta | -| date | datetime.date | -| datetime, timestamp | datetime.datetime | -| decimal64, decimal128 | decimal.Decimal | -| binary, varbinary, blob | bytes | - -### 函数重载与匹配 - -MO UDF 目前不支持重载,函数名在一个 matrixone 集群要求是唯一的。 - -## 案例:信用卡欺诈检测 - -本节以“信用卡欺诈检测”为例,讲述了 python UDF 在机器学习推理流水线中的应用。(相关代码详见 [github](https://github.com/matrixorigin/matrixone/tree/main/pkg/udf/pythonservice/demo)) - -在本节中,我们需要确保本地 python 环境已安装了 numpy 和 scikit-learn。 - -### 背景 - -信用卡公司需要识别欺诈交易,以防止客户的信用卡被他人恶意使用。(详见 kaggle [Credit Card Fraud Detection](https://www.kaggle.com/datasets/mlg-ulb/creditcardfraud)) - -### 数据 - -数据集包含了2013年9月欧洲持卡人使用信用卡进行的交易记录。数据格式如下: - -| 列名 | 类型 | 含义 | -| -------- | ------ | ----------------------------------------------- | -| Time | int | 此交易与数据集中的第一个交易之间经过的秒数 | -| V1 ~ V28 | double | 使用 PCA 提取的特征(以保护用户身份和敏感特征) | -| Amount | double | 交易金额 | -| Class | int | 1:欺诈交易,0:非欺诈交易 | - -我们把数据按照8: 1: 1的比例划分为训练集、验证集和测试集。由于训练过程不是本文的重点,不在此处进行过多的介绍。我们把测试集当作生产过程中新出现的数据存储到 MO 中,DDL 如下: - -```sql -create table credit_card_transaction( - id int auto_increment primary key, - time int, - features json, -- 对应 V1 ~ V28,使用 json list 存储 - amount decimal(20,2) -); -``` - -### 推理 - -训练好模型之后,我们就可以编写推理函数了。其核心代码如下: - -```python -import decimal -import os -from typing import List - -import joblib -import numpy as np - -# 模型路径 -model_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'model_with_scaler') - - -def detect(featuresList: List[List[int]], amountList: List[decimal.Decimal]) -> List[bool]: - # 加载模型 - model_with_scaler = joblib.load(model_path) - - columns_features = np.array(featuresList) - column_amount = np.array(amountList, dtype='float').reshape(-1, 1) - # 对金额进行归一化 - column_amount = model_with_scaler['amount_scaler'].transform(column_amount) - data = np.concatenate((columns_features, column_amount), axis=1) - # 模型推理 - predictions = model_with_scaler['model'].predict(data) - return [pred == 1 for pred in predictions.tolist()] - -# 开启 vector 选项 -detect.vector = True -``` - -然后编写 setup.py 用于构建 wheel 包: - -```python -from setuptools import setup, find_packages - -setup( - name="detect", - version="1.0.0", - packages=find_packages(), - package_data={ - 'credit': ['model_with_scaler'] # 模型 - }, -) -``` - -整个项目的目录结构如下: - -``` -|-- demo/ - |-- credit/ - |-- __init__.py - |-- detection.py # 推理函数 - |-- model_with_scaler # 模型 - |-- setup.py -``` - -使用命令 `python setup.py bdist_wheel` 构建 wheel 包 detect-1.0.0-py3-none-any.whl。 - -创建函数: - -```sql -create or replace function py_detect(features json, amount decimal) returns bool language python import -'your_code_path/detect-1.0.0-py3-none-any.whl' -- 替换为 wheel 包所在目录 -handler 'credit.detect'; -- credit module 下的 detect 函数 -``` - -调用函数: - -```sql -select id, py_detect(features, amount) as is_fraud from credit_card_transaction limit 10; -``` - -输出: - -``` -+---------+----------+ -| id | is_fraud | -+---------+----------+ -| 1 | false | -| 2 | false | -| 3 | true | -| 4 | false | -| 5 | false | -| 6 | false | -| 7 | false | -| 8 | true | -| 9 | false | -| 10 | false | -+---------+----------+ -``` - -至此,我们已经在 MO 中完成了信用卡欺诈检测任务的推理。 - -通过该案例可以看出,我们可以方便地使用 python UDF 来处理 SQL 解决不了的任务。Python UDF 既扩展了 SQL 的语义,又避免了我们手动编写数据移动和转换的程序,极大地提高了开发效率。 diff --git a/pkg/udf/pythonservice/demo/credit/__init__.py b/pkg/udf/pythonservice/demo/credit/__init__.py deleted file mode 100644 index f59e717ed6d0c..0000000000000 --- a/pkg/udf/pythonservice/demo/credit/__init__.py +++ /dev/null @@ -1,3 +0,0 @@ -# coding = utf-8 -# -*- coding:utf-8 -*- -from .detection import detect diff --git a/pkg/udf/pythonservice/demo/credit/detection.py b/pkg/udf/pythonservice/demo/credit/detection.py deleted file mode 100644 index 4a027ec163dc1..0000000000000 --- a/pkg/udf/pythonservice/demo/credit/detection.py +++ /dev/null @@ -1,62 +0,0 @@ -# coding = utf-8 -# -*- coding:utf-8 -*- -import decimal -import os -from typing import List - -import joblib -import numpy as np - -model_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'model_with_scaler') - - -def detect(featuresList: List[List[int]], amountList: List[decimal.Decimal]) -> List[bool]: - model_with_scaler = joblib.load(model_path) - - columns_features = np.array(featuresList) - column_amount = np.array(amountList, dtype='float').reshape(-1, 1) - column_amount = model_with_scaler['amount_scaler'].transform(column_amount) - data = np.concatenate((columns_features, column_amount), axis=1) - predictions = model_with_scaler['model'].predict(data) - return [pred == 1 for pred in predictions.tolist()] - - -detect.vector = True - -if __name__ == '__main__': - ret = detect( - [ - [-1.39598357648026, 1.07394393065875, 0.213508735697434, -1.00551307370986, 1.187300373936, - -1.07111854041464, 0.753203638755905, 0.30971495602621, -0.983991867539784, -1.70071141514904, - 0.823426013946253, 0.755747784301563, 0.57208601947088, -0.493305378300063, -0.470447091684077, - 0.748161458746014, -0.0446257458090844, 1.15426779428755, -0.580978900127479, -0.04190738904369, - 0.313836892986802, 0.629705007512215, -0.702875042416293, -0.378664198371815, 1.17715716992152, - -0.0670218758532906, -0.108995491056784, -0.0807276510778096], - [-1.99458047217904, -1.63531939810522, 1.00669502032082, -1.43318273761075, 3.25919383016879, - 4.00823861971185, -1.66001967781548, 1.09946561236069, 1.91968533357403, -0.518543946411732, - -1.05471184392788, 0.446539472296582, -0.468234843104227, -1.40433618221015, -1.8103263742068, - -0.277012303056273, -0.290667580529318, -0.608743967003329, -0.218456639889371, -0.677311451910878, - -0.258971403019176, 0.0809710102853903, -0.579907932919128, 0.830053981653551, -0.462078172323048, - 0.437226392974709, -0.170400933244463, 0.0175635565102101], - [-3.0435406239976, -3.15730712090228, 1.08846277997285, 2.2886436183814, 1.35980512966107, - -1.06482252298131, 0.325574266158614, -0.0677936531906277, -0.270952836226548, -0.838586564582682, - -0.414575448285725, -0.503140859566824, 0.676501544635863, -1.69202893305906, 2.00063483909015, - 0.666779695901966, 0.599717413841732, 1.72532100745514, 0.283344830149495, 2.10233879259444, - 0.661695924845707, 0.435477208966341, 1.37596574254306, -0.293803152734021, 0.279798031841214, - -0.145361714815161, -0.252773122530705, 0.0357642251788156], - [-2.30334956758553, 1.759247460267, -0.359744743330052, 2.33024305053917, -0.821628328375422, - -0.0757875706194599, 0.562319782266954, -0.399146578487216, -0.238253367661746, -1.52541162656194, - 2.03291215755072, -6.56012429505962, 0.0229373234890961, -1.47010153611197, -0.698826068579047, - -2.28219382856251, -4.78183085597533, -2.61566494476124, -1.33444106667307, -0.430021867171611, - -0.294166317554753, -0.932391057274991, 0.172726295799422, -0.0873295379700724, -0.156114264651172, - -0.542627889040196, 0.0395659889264757, -0.153028796529788], - ], - [ - decimal.Decimal('2.35'), - decimal.Decimal('17.6'), - decimal.Decimal('529'), - decimal.Decimal('239.93'), - ] - ) - - print(ret) diff --git a/pkg/udf/pythonservice/demo/credit/model_with_scaler b/pkg/udf/pythonservice/demo/credit/model_with_scaler deleted file mode 100644 index ef63da8c1de5bc23b2dc5564becf828fc2acba32..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1497 zcma)*Uu+ab9LMjj*WP)w<%&fUOO0X*l(rWOS~0O~O;yArM=VKO9?Up$YZQV@bn8G0j$~0xb(>IjeHrL5m;ZtkOFozA2hDk-HO6w(!Fv zhjt0Rk}vSeYfv1WL1%4o+dDrq^O;< zx`gZEQTOUT3!0{AOR3Hr=<^EU%s;V&vG!2@fR}FbTo;HB0lF>CJB9gNd5gw1)@(Q=S!ZX$lANvDQJD6ijDW_|Da8;nkq%WV&0c&ZVa3AL{haI&&R2HdW7m=8QKrZ~rMf>U{g;p*OD#Uv&PwV=s9f zpO*fZk6&xqIVD|K|M*7@C*PAkS$}Y9X#bDS^s6&E{`I00erDHO{{%8axNDx=-~%(hb`N&e*pIyaDV^+ diff --git a/pkg/udf/pythonservice/demo/ddl.sql b/pkg/udf/pythonservice/demo/ddl.sql deleted file mode 100644 index a840f98c3d498..0000000000000 --- a/pkg/udf/pythonservice/demo/ddl.sql +++ /dev/null @@ -1,1006 +0,0 @@ -drop table if exists credit_card_transaction; -create table credit_card_transaction( - id int auto_increment primary key, - time int, - features json, -- 对应 V1 ~ V28,使用 json list 存储 - amount decimal(20,2) -); -insert into credit_card_transaction (time, features, amount) values (157638, '[-1.39598357648026, 1.07394393065875, 0.213508735697434, -1.00551307370986, 1.187300373936, -1.07111854041464, 0.753203638755905, 0.30971495602621, -0.983991867539784, -1.70071141514904, 0.823426013946253, 0.755747784301563, 0.57208601947088, -0.493305378300063, -0.470447091684077, 0.748161458746014, -0.0446257458090844, 1.15426779428755, -0.580978900127479, -0.04190738904369, 0.313836892986802, 0.629705007512215, -0.702875042416293, -0.378664198371815, 1.17715716992152, -0.0670218758532906, -0.108995491056784, -0.0807276510778096]', 2.35); -insert into credit_card_transaction (time, features, amount) values (157639, '[-1.99458047217904, -1.63531939810522, 1.00669502032082, -1.43318273761075, 3.25919383016879, 4.00823861971185, -1.66001967781548, 1.09946561236069, 1.91968533357403, -0.518543946411732, -1.05471184392788, 0.446539472296582, -0.468234843104227, -1.40433618221015, -1.8103263742068, -0.277012303056273, -0.290667580529318, -0.608743967003329, -0.218456639889371, -0.677311451910878, -0.258971403019176, 0.0809710102853903, -0.579907932919128, 0.830053981653551, -0.462078172323048, 0.437226392974709, -0.170400933244463, 0.0175635565102101]', 17.6); -insert into credit_card_transaction (time, features, amount) values (157639, '[1.91995030623265, -0.5295747029094, -0.291774125621835, 0.644989215446892, -0.76675678904966, -0.384374141379217, -0.616882341317413, -0.0303795762697624, 1.25072392843323, 0.0132744068850985, -1.10026154824521, 0.331734538658172, 0.193348358893277, -0.257782871763926, 0.390828644791368, 0.283302715730695, -0.504784359906921, 0.057997610280608, -0.378145951606318, -0.135899240310776, 0.239401040560321, 0.864998262232592, 0.0783537317280094, -0.109595024583041, -0.197548684246792, 0.455488380685572, -0.0082640199045086, -0.0414176548014362]', 41.94); -insert into credit_card_transaction (time, features, amount) values (157639, '[-0.356158491296727, 1.19140675503969, -0.437619198206388, 1.17389858526241, 0.525367907476951, -0.471524075492547, 0.19921791891019, 0.239205320969324, -0.757000018757229, -0.433462393457057, -0.626163240442246, -0.170415717117371, 0.289081031012259, -0.445256858118694, 1.38237075501963, -0.697927739044338, 1.61687269021561, 0.214470276102167, 2.76077271804357, 0.149752362112721, -0.182841920481592, -0.552937665163497, 0.0335953721199883, 0.614989258141114, -0.986867753602889, 0.524161795846751, -0.0198743916937031, 0.100141172352533]', 1.98); -insert into credit_card_transaction (time, features, amount) values (157639, '[2.02583456089301, -0.0460704553298835, -1.24939316418736, 0.274412261852914, -0.0275606053390044, -1.09971415141943, 0.154687080161978, -0.263754573657412, 0.33531332049879, 0.185334849098208, 1.08210981362046, 1.00130451318474, -0.0654466963955989, 0.69565703650254, 0.0364246570962769, -0.0461271446812628, -0.718075126924868, 0.403463206316367, 0.0230679539589328, -0.241081814272209, 0.302147569082114, 1.00340980337905, -5.88882811997649e-05, 0.11396159664657, 0.224937903068446, -0.133708994176919, -0.0233532686686872, -0.0680550254990753]', 0.89); -insert into credit_card_transaction (time, features, amount) values (157640, '[-1.25788044139779, 0.577768020042278, 0.456236866366804, -1.01285260644017, -1.56884105409177, 0.939294972711302, -2.09066900408235, 1.69062535105188, -1.06172905614274, 0.10065118670415, -0.572698298175859, 0.112910775422312, 0.159424394271634, 0.623588180799913, 0.68303613010055, -0.309407447179201, -0.164418821746834, 2.86081287192568, 0.157059825067622, -0.736265048870775, 0.127880379623038, 0.390366351325565, 0.107342480466096, 0.0309931910367953, -1.12833104910195, 0.685512178738179, -0.39438011099865, -0.0225425710328445]', 7.31); -insert into credit_card_transaction (time, features, amount) values (157641, '[1.98510586726557, 0.398792922511515, -2.45150499127779, 1.46248689674201, 1.03546637873498, -0.846499564070512, 0.679494343853913, -0.305204032502528, 0.198211935930933, -0.363112191003191, -0.798523200163691, -0.274237040248291, -0.662816737354039, -0.917638619957321, -0.260083274524396, -0.145069895449114, 0.953649350221411, 0.156306942085275, -0.361630039377152, -0.184153341133051, -0.0386108447781726, 0.0084067645958914, -0.037845829943511, 0.383466256565816, 0.48272284915951, -0.5115343341548, -0.0099082407947613, -0.0198086265817112]', 38.17); -insert into credit_card_transaction (time, features, amount) values (157641, '[-0.374887370619266, -4.11729339378491, -4.1875224037996, 1.18373794632254, -0.310090131427654, -0.899450472361004, 2.7252532041357, -0.949894054704977, -0.171053824310508, -0.383950298690729, -1.43292386006332, -0.233954787781866, -0.173060967229491, 1.15579858379694, 0.0609922403369292, -0.437634662704189, -0.227380513674285, -0.285718698521211, -0.113894844752243, 2.46723269810961, 1.01510422331704, 0.0863033514135896, -1.42676810402777, -0.596595689077286, 0.204409984529966, 0.927533302336789, -0.39802548901435, 0.125554710154175]', 1336.0); -insert into credit_card_transaction (time, features, amount) values (157642, '[-0.0416732511933412, 0.71244420568974, 0.33535085867153, -0.670760190626758, 0.428872888558093, -0.811847554634009, 0.810955098031995, 0.0078032655071081, -0.190866214880937, -0.223122386109244, 0.766145047980819, 0.468258261730657, -0.861865139996295, 0.474579604584876, -0.976567712386884, 0.0548392794470782, -0.527432188772319, -0.274295661265464, 0.107782136692494, -0.0774532911198698, -0.203471813529004, -0.485264248271819, 0.0890083834450539, -0.0100748971629493, -0.570358006667116, 0.123357366885259, 0.240495687692584, 0.0866455092013529]', 1.98); -insert into credit_card_transaction (time, features, amount) values (157643, '[-0.832503466188826, 0.682723784631944, 1.02218856923446, -0.717397382886135, 0.427838532191477, 1.58550406037139, -0.335734551790415, 1.11338845257033, 0.019252243360449, -1.05141052489124, 0.457362240420781, 0.342512098801101, -1.10803892260668, 0.693400052583313, 0.766083459291204, -0.356980260432214, 0.172193840621312, -0.434130513898625, -0.166894804664431, -0.309253450215673, 1.02093015277999e-05, -0.0325676095011079, -0.0331243170780324, -1.69920795655913, -0.504059406166493, -0.412186968702842, 0.0735610937939236, 0.0523765924198769]', 3.99); -insert into credit_card_transaction (time, features, amount) values (157643, '[2.25420752750295, -1.24481322779075, -1.72863974578842, -1.68526914781163, -0.633272382837569, -1.07473988257085, -0.382061279894191, -0.511529894450943, -1.84369298764842, 1.61314870246791, -1.20851073789814, -0.580659560773602, 1.46325147523553, -0.196419847373187, 0.0428856326032823, -0.486149866833118, 0.160598658923804, -0.303179983132317, 0.137190343227818, -0.223003202600284, -0.303673405745144, -0.567555864976534, 0.138123576608639, -0.671257746323221, -0.0802396437891242, -0.282735378147483, -0.0305796850857915, -0.0499709148950415]', 85.0); -insert into credit_card_transaction (time, features, amount) values (157643, '[0.177244382040837, 0.917210791001869, -0.688700744979568, -0.679315255223694, 1.12326209092676, -0.432961532866439, 0.867039513116872, 0.0248384990656834, 0.182589944278121, -0.806064057014407, -1.40091161071351, -0.968885386690624, -1.14843099452001, -0.797624886028567, -0.0091049245692412, 0.510401354176316, 0.272376089211884, 0.0492453250291156, -0.0623830038520425, -0.0452201113723892, -0.389600056638222, -1.02675925702742, 0.0450413635222858, -0.113254034570941, -0.396547137570523, 0.159356219756419, 0.216028690279438, 0.0750202864404438]', 4.49); -insert into credit_card_transaction (time, features, amount) values (157644, '[-0.897403215679839, 0.699772862540808, 0.80409201072632, 0.635410858336631, 1.05363675650451, -0.818910252944362, 0.802695333475396, -0.150121994229663, -0.428486343256276, -1.06826373992494, -0.46973288624574, 0.347080934734465, 1.04875667172493, -1.46560256644374, 0.0509824543224673, 0.340349019538857, 0.362220016825516, 0.273746339761634, -0.704288412154429, 0.0121626544218576, -0.0603507325616821, -0.173919888747679, -0.323116082374684, -0.18831404209253, 0.984769914817444, -0.556450923893259, -0.202612462787745, -0.0024301912341971]', 50.0); -insert into credit_card_transaction (time, features, amount) values (157644, '[-1.32028971061182, -0.0967140071443226, 2.75444126359053, -2.10382883379794, -1.08911993985864, 1.17039076965806, -0.951504207644949, 0.442544482316705, 0.951322010621573, -0.0946106074903396, -2.0514309395451, -0.348748182659254, 0.446338569120425, -2.25011118237956, -2.33194561730455, 0.865589675341509, 0.291234687426737, -1.0016284014087, 0.454522241322133, 0.193914179861337, 0.200579799638371, 1.10369423135335, -0.545784798376071, 0.557538500117808, 0.706163403205832, -0.0325177396845115, -0.511315211437245, -0.383131262714821]', 9.6); -insert into credit_card_transaction (time, features, amount) values (157644, '[-0.839236815165994, 0.772772938354519, 0.896315790067565, -0.291204774756721, 1.10995788514525, -0.259922685295173, 1.02692579501918, -0.194909979465381, -0.707221666850287, -0.610249324986453, -1.00473075743882, 0.317539398659417, 1.0162701878275, -0.0636537611734492, 0.0062630658661567, -0.576085456788288, -0.0627513392625493, -0.614960936410469, 1.22269613107285, 0.165602349613131, -0.232444304473928, -0.666007271329866, -0.301846328581569, 0.531258279316342, 0.585379090020058, 0.507216145949509, -0.166378809200635, 0.101108142387477]', 31.39); -insert into credit_card_transaction (time, features, amount) values (157645, '[2.03473237043037, -0.438923466039102, -0.666964426144476, -0.0249050094282948, -0.215106953813965, 0.175390728144177, -0.646125813386846, 0.130326350894405, 0.919428287998534, 0.168865611671255, -0.074021206703936, 0.689784879203225, 0.146746265738576, 0.0419583431391672, -0.0793888274891199, 0.956846855573017, -1.14999454649998, 0.431947350122071, 0.623283175304275, -0.152084909882827, -0.237369120539355, -0.598430501807229, 0.278486347413146, -1.10562331840212, -0.526518996006121, 0.32141927610625, -0.0401344861298193, -0.0641254152450784]', 6.99); -insert into credit_card_transaction (time, features, amount) values (157645, '[-0.183479931137426, 0.857937614653596, 0.0807566218636643, 0.412551185793813, 1.03282928618833, -1.07274869321084, 1.40937765576693, -0.266740689201988, -0.487884963951038, -0.508335397186214, -1.26858618466222, -0.427358127869716, -1.09741794115717, 0.547713652967518, -0.825632826803471, -0.661245769623629, -0.181485880516096, -0.438938108956117, -0.425318700589288, -0.215632752764924, 0.0889922590906744, 0.283658660084031, -0.323451201392851, -0.0356676340499028, 0.706606250660853, -0.481030364345376, -0.0327263043071444, -0.035265296126595]', 21.56); -insert into credit_card_transaction (time, features, amount) values (157646, '[-1.03994732344747, 1.97440929932459, -1.4413951020941, -0.296659217416401, 0.5776944419145, -1.75100214096989, 0.51803695000682, 0.561321718994645, -0.47425509408744, -2.33512697140002, -0.445701875227705, -0.728294232624045, -0.884311269891526, -1.84691675181435, 0.60350717072587, 1.00089348707433, 2.06025981589296, 1.50450712172025, -1.01934337751317, -0.399906552122338, 0.299071601393581, 0.626380461177943, -0.295862513568879, -0.250576327442362, -0.0798821861819715, -0.202848250235844, -0.18622360787109, 0.0927429477928861]', 1.0); -insert into credit_card_transaction (time, features, amount) values (157646, '[-2.69353886870738, 3.10044368971283, -2.55889744137267, -0.673985560453876, -0.9599197741292, -1.05020493531582, -0.847844018299585, 2.02481930629227, -0.231623935584156, 0.295327097438162, -0.106038118675719, 1.6857008031583, 0.900093210603649, 1.70460318180633, -0.407768316490628, 0.338110148596447, -0.101297811120048, 0.816532937868269, 0.294764224236611, 0.136757173994931, 0.466567982537171, 1.26862048621514, 0.127235361448291, -0.374555932835656, -0.371099953601298, -0.182618771753876, 0.409186278840597, 0.297486784826629]', 2.69); -insert into credit_card_transaction (time, features, amount) values (157647, '[2.05260047957892, -0.110828707060066, -1.24252955680428, 0.188362507635046, 0.149966334997958, -0.615491388148278, 0.0749179612326886, -0.16005763276635, 0.307527691571433, 0.241426971185178, 0.539575444856533, 0.994490117937812, 0.108495817934143, 0.434396258110717, -0.63063017978852, 0.227930779482464, -0.714481796968903, -0.224519558391214, 0.614696884032326, -0.194597509214057, -0.268227892903336, -0.670642669547294, 0.268215499852199, -0.491410166198354, -0.263485465875815, 0.20782111444266, -0.0749112758380428, -0.0748118791225046]', 1.98); -insert into credit_card_transaction (time, features, amount) values (157647, '[-1.27199882743545, 0.170024393750088, -0.088251722094101, -2.11291307127448, 2.75153062500867, 3.66687082623167, -0.146953385279325, 0.950536072153524, 0.489146197127268, -0.622995755433168, -0.196149436731275, 0.0707399586978106, -0.203820118487718, -0.0268143762172014, 0.719533000167805, 0.41010495628507, -1.06715197027243, -0.140932915582162, -0.918224341480068, -0.253938392801881, -0.0269174966606696, -0.300689448818157, 0.213908698152699, 0.641920715206624, -0.40112906350585, -0.76218027414501, -0.467643723395709, 0.418321624672595]', 1.18); -insert into credit_card_transaction (time, features, amount) values (157648, '[2.11751020965837, 0.0235774478087018, -1.9870043554948, 0.293039239050815, 0.557169624192171, -0.579877938926374, 0.0576564317318973, -0.102645605597837, 0.789471792393416, -0.327531662600769, -1.388329399547, -0.944194688986781, -1.51183387023545, -0.545963751991944, 0.433139290855922, 0.569220418192005, 0.352482971760458, 0.0930677139455327, 0.289491593096733, -0.246063991259267, -0.423721871213864, -1.19176114420418, 0.286955353814581, -0.108455477801213, -0.234985090743966, 0.214397040406983, -0.0743007330473323, -0.0402667266075482]', 0.89); -insert into credit_card_transaction (time, features, amount) values (157648, '[1.2256294168653, -2.05483045588278, 0.581187780359728, 0.992412322822088, -1.98712886707541, 0.801471953821846, -1.23315068801493, 0.387753146721514, 2.27265675996996, -0.326349517961391, 0.0448312599642679, 1.58334135387118, 0.0022237043746793, -1.08403078522063, -2.18352446960988, 0.0524856989296345, -0.103699713770269, 0.410027617945241, 0.385379033868296, 0.447661826986291, 0.322247458766696, 0.750138445722837, -0.10597983595467, 0.19113939238615, -0.548493821296221, 0.679922959833611, -0.0208893379416632, 0.0102587672514416]', 328.0); -insert into credit_card_transaction (time, features, amount) values (157648, '[-0.248509726495573, 1.07946868252229, -0.393704058864527, -0.717591356587321, 0.433266436288412, -0.742173056932365, 0.650636809594189, 0.271991343687211, -0.147823288876947, -0.634919406057096, -1.7640334565014, -0.0047852664362168, 0.104355478852626, 0.40333711619458, -0.376267347676382, 0.253744161776144, -0.508267605605149, -0.450619902765913, 0.194131536965775, -0.11870850845706, -0.265742678361742, -0.729147274943867, 0.0467918131182337, -0.727072224534445, -0.365658411742183, 0.187573122607184, 0.118165813854475, 0.02770642473007]', 9.42); -insert into credit_card_transaction (time, features, amount) values (157649, '[1.97557156420932, 0.257052006508714, -2.30602086566742, 0.710884424036468, 0.692095080411303, -0.652265232920129, 0.0815479580842565, -0.0723374784955656, 0.434517554214779, -0.909660163633121, 1.46953564433281, 0.606872423893538, -0.252219110287855, -2.46321159652085, -1.01268702234767, 0.489980229475144, 1.82543420356317, 1.04599683077151, -0.0550521360391502, -0.103584896278749, 0.0385742696680821, 0.372966181302742, -0.0156695797948929, 0.567601948187327, 0.176486311786696, 0.675621228331706, -0.0423392384959142, -0.0146088068254063]', 12.31); -insert into credit_card_transaction (time, features, amount) values (157649, '[-0.923490195175723, -0.411541403361359, 1.31870999103651, -0.807033963370218, 1.07094881607993, -0.566722373692538, 0.130075177826392, 0.0337314567968639, 0.252200442683106, -0.169607973034594, 0.363178350489606, 0.547406517051136, 0.0437651361614005, -0.180551484396432, -0.560353267086069, 0.613677406482808, -1.12055820949404, 0.304506409314633, 0.26068587480448, 0.416315140021198, -0.149954952111745, -0.454977516408151, 0.243535026473874, -0.512818109939499, -0.825786410745404, 0.153323797882963, 0.164884187285001, 0.0275399485708718]', 40.47); -insert into credit_card_transaction (time, features, amount) values (157649, '[2.00855016015528, -1.00830627696618, -0.317348840213955, -0.0696998055948962, -1.27863546083863, -0.576883570660935, -0.886241531559988, -0.056933909460289, 0.116562119974472, 0.8426102410503, -1.31226379337613, -0.339061579495805, -0.313155831453531, -0.0866695618949391, 0.456310113353515, -0.995510666165627, -0.300401516461393, 1.13850279602969, -0.884743665061387, -0.551588310312965, -0.55429928472897, -1.15899745384567, 0.436957785315452, -0.193691591275635, -0.70936766810153, -0.0159983045075576, -0.0008288727590658, -0.0275011483844943]', 59.4); -insert into credit_card_transaction (time, features, amount) values (157649, '[0.223594544683996, 0.715958414102107, -2.13564557647109, -1.97535185861463, 0.496395482356357, -0.490560608050714, -0.5226307455962, -3.43752318061339, -1.01490684402817, 0.166872296900074, 1.04629208304285, -0.22376177865544, -0.96001081019648, -0.647368337467912, -1.84529756839946, 1.16804781165731, 1.16554201444276, -0.814668252428307, -0.271418265558344, -0.788696677113423, 3.36872714339997, -0.180131599083971, 0.0633492381088427, 0.604496607271027, 0.817962052808668, -0.0646537278128118, 0.223178530922308, 0.20813541732301]', 19.46); -insert into credit_card_transaction (time, features, amount) values (157650, '[2.04260022801273, -0.132571727934578, -1.20797226400629, 0.421332838027051, 0.015662256932516, -0.290900369965955, -0.469889792653869, -0.0631863606594573, 1.33273001135079, -0.642606146036098, -1.42330911291295, 0.0776184721728813, 0.686817593859346, -1.50219536621149, 0.824119868486107, 0.801319377845985, -0.0140382552155231, 1.12448350938074, -0.123450200949616, -0.115296834771437, 0.151440034559825, 0.709881626237922, -0.0839973191934584, -1.06186213184746, 0.120787015914271, -0.0784249578623302, 0.0525212587573732, -0.0203316678171085]', 12.58); -insert into credit_card_transaction (time, features, amount) values (157651, '[-0.131054840467345, 0.892623535809587, -0.482924297493091, -0.186360677836607, 0.803878161265486, -1.23191087867137, 0.751297125173002, -0.231255815536925, -0.138538859354719, -0.574688901380832, 1.20122831247042, 0.147984564660915, -0.69271123910182, -0.666342460960936, 0.0723310883844761, 0.24150976314228, 0.320811665517914, 1.25279506457288, 0.0952951751238245, -0.242764588140141, 0.366922538820285, 1.090995504712, -0.259874269506971, -0.0006046999046569, -0.486225886870085, -0.203213961396791, -0.0903803670810949, 0.105680510684779]', 7.37); -insert into credit_card_transaction (time, features, amount) values (157651, '[-0.210002232493935, 1.50789103972255, -1.86428166573953, -0.51623060411041, 0.751880968406418, -0.638984108486165, -0.126320115866028, -2.58409084090286, -0.819442409091646, -1.07986480071802, -1.32333628597076, 0.640977040282002, 0.257433530731629, 1.03499701651991, -1.00783228749532, -0.263062714568735, -0.0569792761702533, -0.582582294663413, -0.311308997111532, 0.326594219796808, -1.01533216687948, 1.11474741796973, 0.167665203226668, 0.729468154695518, -0.331569304619855, 0.499900829983917, -0.198667100462482, -0.0346444636175124]', 0.76); -insert into credit_card_transaction (time, features, amount) values (157651, '[-2.30131222984748, 1.47011403271157, 0.969135997155183, 2.18320065748936, -0.863390844215648, 1.15195530858573, -2.29451663092486, -4.7762607704627, -1.40541108966046, -1.25200599328173, -0.779001242062094, 0.913414366964245, 0.839656714002994, 1.09018386793027, 1.96595047392444, -0.184044207837079, 0.71272603988305, 0.379455125264061, 1.89719409549571, 1.28788467912956, -2.64102324260032, 0.663854666086816, 0.358991144686073, -0.0392489078526365, -0.024862449689254, -0.377762483156487, 0.213168112797435, -0.199975925427775]', 51.0); -insert into credit_card_transaction (time, features, amount) values (157652, '[0.884763508441952, -3.8156762988978, -1.39965199924788, -1.07414994019818, -2.11764507211758, 0.0562262021216791, -0.575881512021307, -0.240249343504635, -1.02828758751354, 1.22542348314424, -1.22168488615554, -0.845611984500319, 0.992455838261884, -0.533670049811058, 0.559956335227874, 0.0766888284180032, 0.109772507881296, 0.650536918075264, -0.591966248606822, 1.11981291980484, 0.456787832968756, 0.143628838729152, -0.434296991619562, 0.509650570790717, -0.515992421348443, -0.160519556864417, -0.100654882997993, 0.0925007709077297]', 746.5); -insert into credit_card_transaction (time, features, amount) values (157653, '[0.731815773882491, 0.543391502795749, -0.0859453295498783, 1.26866038121961, -0.159981828861613, -0.619255064874644, 0.283255994615255, -0.108725800739492, 0.169950171490531, 0.451790719569398, -1.15817984538723, -1.3323848087332, -2.39520068200804, 0.75274975621883, 1.3251194281676, -1.19737871621193, 0.904446729970673, -0.209928873922578, 2.71798863286453, -0.0391638608249803, -0.193578426863593, -0.480362852922719, 0.168862959676893, -0.115902546091977, -0.827008953700782, 0.651625087736028, -0.154819440325894, -0.0656439642523445]', 1.0); -insert into credit_card_transaction (time, features, amount) values (157653, '[-0.242040291626059, 1.54914595234036, 1.47683333183473, 4.33927115234629, 0.308702290845302, 1.41422764612945, -0.188777403569096, 0.404470258555776, -1.56737273147991, 1.47564283478422, -1.46080551454826, -0.0466398267479782, 1.70359273145409, -0.393949877881024, 0.521185486043533, 0.150451806218905, -0.105424504585828, 0.513143478221745, 1.09606425914925, 0.215796943394874, 0.293115506032373, 1.05999152274052, -0.0976773042211085, 0.523527300220911, -0.89178463188075, 0.329421726372358, -0.133824701440268, -0.0581249766516207]', 0.76); -insert into credit_card_transaction (time, features, amount) values (157654, '[2.02864556574127, 0.390124397604779, -2.51097464221226, 1.26910008229917, 1.16460309329568, -0.417938470651313, 0.460249755768631, -0.0804069495275281, 0.0999638743893789, -0.149746806899267, 0.524412944037156, -0.16077415109147, -1.63969292573602, -0.559080106857778, -0.845525001405328, 0.160838401458979, 0.666700429762474, 0.837262194107146, 0.0371473224590694, -0.298526703002147, -0.035499392468754, 0.0193301415358212, -0.0371677287624363, 0.0600083711771599, 0.466886186083478, -0.506978598611515, -0.0132610376470388, -0.0407528843936862]', 1.91); -insert into credit_card_transaction (time, features, amount) values (157654, '[2.14415001735535, 0.0323729942785834, -2.48038703801522, -0.305775718260858, 1.01461584760383, -0.736586782691403, 0.61602968093617, -0.316018599242666, 0.0389752158700674, 0.196791265489431, 0.416213235107388, 0.469909539025883, -0.478685155427874, 1.04357831397847, -0.192120689854796, -0.216764594182215, -0.732967431993865, 0.241137805611009, 0.648505448182082, -0.209892287114361, 0.147317831783046, 0.482182665992543, -0.0922224335220843, 0.137528052007268, 0.498098720879354, 0.227611828144205, -0.0983984352723302, -0.0892664175038227]', 3.78); -insert into credit_card_transaction (time, features, amount) values (157654, '[-0.139570481420866, 0.986570540500794, -0.352421192173833, -0.623364626928538, 0.618639655181002, -0.691549848922661, 0.879960407436095, -0.0403334258719745, 0.105569639449889, 0.218615667103726, 0.350883965660528, -0.0448306532287092, -1.16732851369709, 0.76285829516748, -0.0341861611004669, -0.177702251619848, -0.731183949857996, 0.79652734944385, 0.356429851713179, 0.04708212875973, 0.362065348150772, 1.30296761613408, -0.156491362883465, -0.390287934492053, -0.779148194860942, -0.20579303693408, 0.608550201729674, 0.365622820007491]', 4.49); -insert into credit_card_transaction (time, features, amount) values (157655, '[-0.130650298013811, 0.391344166392019, -0.331644691033737, -0.914679131632785, 1.38356940698963, 0.893025265018253, 0.479004213082131, 0.316064292401913, -0.0094241345888292, -0.23071615668249, 0.687518444925768, 0.480511468523812, -0.773674725661566, 0.471457820690428, -0.245536895411608, -0.545160760336742, -0.0637737445794801, -1.30513596786077, -0.627754983305407, -0.368571775583073, -0.0781987507884595, -0.1947205650754, 0.231814152107424, -1.6599896006415, -1.05404526517416, 0.155443787188482, -0.207936416520938, 0.0409139679759314]', 1.29); -insert into credit_card_transaction (time, features, amount) values (157656, '[-0.714511714551263, 1.50830994194155, 0.50711822904093, -1.24805680461767, 1.35371701530256, -0.603719964734428, 2.51880797015708, -2.86665068532995, 2.14978087024674, 3.01726208671731, 0.50966761251635, 0.149514548378927, 1.03636642956818, -2.0082960794986, 0.497353337135643, -1.09386434323394, -1.42678343693498, -0.499209413042881, -0.75665693577444, 1.00438014445258, 0.514784736124527, 0.930591811787739, -0.342807675489497, 1.17927051560503, 0.164575056265144, -0.450424307262987, -0.71880343224745, -1.41543862183007]', 42.99); -insert into credit_card_transaction (time, features, amount) values (157656, '[-1.93162261550149, 2.59026458697747, -1.8635646963847, -0.78332639978214, 0.379243940523466, 2.01992521624267, -3.01241825666795, -9.30458305757629, -0.787083562192785, -1.46885648770044, -0.365148521542741, 0.89438014400985, -1.14325014731445, 1.92977715679965, -0.307119593585105, 0.528734748496315, -0.448653051895754, 1.11454285856003, 0.0767803133493333, 1.5493102077872, -1.89057320899291, 1.90035699107097, 0.694374997983098, -0.27225631246636, -0.88546484624186, -0.248580812580304, 0.487251246055333, 0.420299091534414]', 38.64); -insert into credit_card_transaction (time, features, amount) values (157656, '[-0.800762106912369, 0.641563618170274, -0.0542473432852957, -1.0353570227287, 1.05592140175839, 1.07685907759499, 0.404842864611541, 0.770551737727229, -0.309806663592078, -1.19832613584598, 0.703326066909501, 1.38521700403863, 0.259350543174613, 0.504158091478425, -0.868675618133526, -1.34371022300666, 0.571809780605375, -1.10482893317612, -0.515841706785354, -0.249907083791907, 0.423954505602875, 1.39683331985651, -0.270520166105858, -1.32386067169, -0.0799291067324985, 0.330705686921022, 0.0522321466482027, 0.0515707723195421]', 25.0); -insert into credit_card_transaction (time, features, amount) values (157656, '[-1.17972115342466, -0.715127721335505, 1.67847011483334, -2.51098657441316, -1.24320788858571, -0.355373467080695, -0.864014438483664, 0.602989356914037, -2.47130798944278, 0.449604051744373, 0.489932842914422, -0.54730730063812, -0.184492950366788, -0.0405632074931372, -0.789191100339196, -0.0462904298812544, 0.440081558068594, 0.507653898408722, 0.0981222104587931, -0.197840306613389, -0.239980636750593, -0.751914143143501, 0.0160181820558147, -0.0663703562011932, 0.148629349059861, -0.368713114256337, -0.0730746117330672, -0.0699371961925373]', 59.96); -insert into credit_card_transaction (time, features, amount) values (157656, '[-0.69925598832763, -0.352236464281865, 0.0057390750347395, -0.983922604023834, -1.47290368141819, 1.60438496397337, 2.14727671584301, -0.127730161743354, 0.423111261231837, -1.01599018293627, 0.0893037300841674, 0.644260363278789, 0.508249572525891, -0.286350822451644, -0.631106400047552, 0.628145055706801, -1.18271140508126, 1.00884901354628, 0.124126316280315, -0.0118866067613299, 0.328426207501013, 1.15568876565384, 0.358355812487624, 0.254846543156037, -0.809282201023793, -0.0674812107974641, 0.309106848985119, 0.128859987728555]', 500.0); -insert into credit_card_transaction (time, features, amount) values (157657, '[2.06065529120707, 0.0967682056317964, -1.84226514689002, 0.211719824847831, 0.655903660553579, -0.339267947329515, 0.0699097157998885, -0.0619455045999982, 0.26813553873579, -0.20688200725904, 0.780746407568512, 0.682695415456113, -0.0034561093824345, -0.661007033492326, -0.461955681876083, 0.626440722693654, 0.0867145742532093, 0.318225520469863, 0.486963768605476, -0.137470281028489, -0.339135124586196, -0.906144506841237, 0.281518992263576, 0.0982559045615988, -0.238615132433342, 0.178137055100331, -0.0671767366379961, -0.0447616832511111]', 0.89); -insert into credit_card_transaction (time, features, amount) values (157657, '[2.18818042484161, -0.773103238611821, -1.4501833660093, -1.08207684948781, -0.368355221346198, -0.728290645915194, -0.492612969549987, -0.168127631239908, -0.736081705459052, 1.05511923991411, 1.00837446023667, -0.210900880556817, -0.338492584560634, 0.306743550511566, -0.28171069360509, 1.11616365402975, -0.0865683066920131, -0.726171215691844, 0.714158081883484, -0.0217439467718453, 0.476267341474473, 1.2971038383396, 0.0191471334909879, 0.81171806437714, 0.171883774885656, 0.014216218174642, -0.0510154264298121, -0.0691723278268449]', 15.0); -insert into credit_card_transaction (time, features, amount) values (157657, '[2.02442612169502, -0.0457922137781754, -1.43728460562818, 0.137082080800018, 0.335117866082995, -0.54306817915896, 0.117418943478694, -0.164191461024606, 0.256384714142543, 0.154148678368035, 1.1322321838556, 1.19917839679656, 0.41896394774377, 0.592872046790169, 0.0837148882758289, -0.0782461678256967, -0.736411751248883, 0.317841249329, -0.0158626041352688, -0.176989447382873, 0.305489566640198, 0.992712047650541, 0.0251382007297824, 0.75248129260826, 0.251567520522297, -0.160579805414128, -0.0230530651597679, -0.0605606285556164]', 4.99); -insert into credit_card_transaction (time, features, amount) values (157658, '[1.40377169825774, -2.6309976417531, -3.06950406574649, -1.31511593176869, -0.653294210746356, -1.02472723679102, 0.537218955237356, -0.577322920377267, -2.30212136312151, 1.70686405080516, 0.619954822557503, -0.781639459350143, -0.720345309229526, 0.76773377284444, -1.18999428159289, -1.12709492309739, 0.617054629765677, 0.311603378387027, 0.12207349892807, 0.520588679292731, 0.45710051518232, 0.622242865329314, -0.550089665267033, 0.823799289029333, 0.42325250167685, 0.195507946667961, -0.178863067156689, -0.0056633826135274]', 513.5); -insert into credit_card_transaction (time, features, amount) values (157659, '[-0.766915012176013, 1.65810941095098, -2.20100292491025, -0.246884619046052, -0.472517200427092, -1.45627254343434, -0.223736534089503, 1.00315110200219, 0.611621497164623, -2.36505638695177, -0.979797925932203, 0.0818948713568789, -0.879830170176104, -1.30539038393444, -0.136204243327807, -0.0487513144691142, 2.40772721663277, 1.061014723443, 0.24995414382334, -0.575533528285327, 0.21957818327074, 0.50324071851885, 0.015525413458478, -0.202375052408002, -0.150324880057374, -0.607555815736408, -0.503800295003373, -0.176655700919763]', 25.0); -insert into credit_card_transaction (time, features, amount) values (157659, '[-2.86729243066438, 0.607702172395742, -1.54311656498544, 0.71970488741748, -0.482233181388535, 0.204499494885432, -0.430172239036267, 1.67406922039689, -0.969590970810925, -0.853290717655412, -0.292248510897408, 0.987725504776081, -0.0283780201316124, 1.72683865118839, -0.275243807120314, 0.961481825249245, -0.315459373695069, 0.446754440883145, 0.458406229556622, -0.5483134616846, -0.0799156665710331, -1.11372449678703, -0.101893504982621, 0.0705310890160139, -1.16127275795735, -1.30826306705791, -0.541450426343595, -0.253504882654566]', 102.6); -insert into credit_card_transaction (time, features, amount) values (157659, '[-0.538717496528732, 0.497037247301723, 0.678574861145991, -1.66156672531294, 1.35582470242373, -0.824378382058554, 1.54588846887594, -0.533860218763007, 0.114142222333549, -0.413710109051273, 0.410442320047211, -0.232262754628378, -1.35256320792588, 0.275493423859727, -0.801663455465844, 0.310926579720657, -1.49093744802186, 0.552536599983858, -1.12831386911552, -0.255665169044622, 0.252038677323505, 0.99794654015748, -0.408265262271822, -0.403829516815616, 0.231246814594198, -0.387908191550835, -0.363963138761858, -0.33481089709042]', 1.0); -insert into credit_card_transaction (time, features, amount) values (157659, '[1.28868138797952, -1.87745285398862, -0.89979356052631, -0.373122036184729, -1.39458052839306, -0.91918362589843, -0.119944597218343, -0.24001131761432, 1.57950648194834, -0.542047035919234, -0.734635500763708, 0.214412021312011, -0.168677256237429, 0.105188736301048, 1.26772220691624, 0.302431217392923, -0.47286451385324, 0.201804213817699, -0.0565754042736389, 0.608199921604214, 0.411625477866091, 0.45787862405848, -0.159463773342714, 0.127795983631398, -0.498388019500921, 0.661999777707197, -0.114554399600257, 0.0156815597426271]', 400.0); -insert into credit_card_transaction (time, features, amount) values (157659, '[-0.399494835410856, 1.03678213917812, 0.0958452793138646, -0.919340951160011, 1.53262179125972, -0.123531537012885, 1.03494485214295, 0.0125673649789998, -0.777497152791399, -1.09231360828425, 0.581234860664014, 0.565161806922879, 0.520946612139756, -0.879500423824476, -0.928242724237109, 0.596289782032917, -0.0575109390421886, 0.437489196465249, 0.424729097397431, 0.026485214028216, -0.264700237411464, -0.788094069292817, -0.189154496819594, 0.0151889061197037, -0.116856695569056, 0.174762746705519, 0.0539158967715802, 0.140703700454984]', 1.29); -insert into credit_card_transaction (time, features, amount) values (157660, '[-1.9567996593669, -2.61014489166794, 1.33272373896791, -1.01638194532143, 0.49053715865399, -0.609377767688863, 0.071716736356296, -0.0261187425380272, -0.688233454409254, -0.0174830023455464, 0.197270848481793, 0.135105988862823, 0.135958540287801, -0.110333294172564, -0.0261327986481843, -1.13032240766272, -0.934574881359157, 2.64977752159329, -0.839338093193207, 0.677501031001426, 0.229884723910273, 0.29604673555042, 0.724878431810796, -0.426134003251837, 0.142261361839809, -0.597141874309556, -0.142115960786366, -0.0299325237997882]', 321.78); -insert into credit_card_transaction (time, features, amount) values (157660, '[2.09192847571684, -0.0946758072668112, -2.45451002739755, -0.173259692153288, 1.01942073652235, -0.483378422047769, 0.548368189548462, -0.264754665359746, -0.128042967414441, 0.37405293739716, 0.190773391424566, 0.269543868715931, -0.427628256181308, 0.957051225445838, -0.257671975956153, 0.163545488271148, -0.855595560493752, 0.169884936741724, 0.624972171863987, -0.116971736899008, 0.0759643734215985, 0.158294087591208, -0.0657696186901157, -0.0926934922241813, 0.289969275434798, 0.727969117192366, -0.143462014620172, -0.0896026228280068]', 36.7); -insert into credit_card_transaction (time, features, amount) values (157661, '[1.92496176830725, 0.308673497576981, -2.72488000282893, 1.20478745318954, 1.49367033156339, -0.0970418316058132, 0.688096346803086, -0.191729105711688, -0.20027895445151, -0.244098815822883, 0.504211207165045, 0.829453155235187, 0.570107064376773, -0.991268285938651, -1.03823497603221, 0.1954577888838, 0.39177616380819, 0.73100188100781, 0.161882074282815, -0.0059346452102432, 0.0032070607362702, 0.0772601889936939, -0.177371312751213, -0.3073120609512, 0.570127314194052, -0.495001888391882, -0.0131919231316632, -0.0283351288818505]', 72.0); -insert into credit_card_transaction (time, features, amount) values (157661, '[2.04310396825651, -0.0859443838594674, -1.17091417962632, 0.222069200560359, 0.097185342511995, -0.699205323107439, 0.0946682806176633, -0.184407289503525, 0.258221620067478, 0.234019295341422, 0.723036001459005, 1.18190212787914, 0.336335850770012, 0.392804210203451, -0.670174007828493, 0.170237327831798, -0.672222852365139, -0.30703095874152, 0.55423090260773, -0.184710890802614, -0.25542417995737, -0.620554894896932, 0.285097165097284, -0.327772411360968, -0.27546984721082, 0.19733769485619, -0.0729064908898449, -0.0727970843061939]', 1.29); -insert into credit_card_transaction (time, features, amount) values (157662, '[-0.678902755094102, 0.592388492368539, 0.100561205962415, -1.40332108804488, 1.67898871739388, 1.74566201609404, 0.740599839331643, 0.13396285679591, 0.499064022339229, -0.289074302162002, 0.483023610141462, 0.606980436560739, -0.44042658430394, -0.118821931598012, -0.649106157094929, -1.26494395650635, 0.0453481753827441, -0.895894269254039, -0.310048242709214, -0.221182325284272, 0.277358447755035, 1.06572393103763, -0.302255594341194, -0.888293840164624, -0.354553976096486, 0.121208274405332, -0.521254557947981, -0.0645931583109889]', 10.44); -insert into credit_card_transaction (time, features, amount) values (157662, '[-0.155784861380603, -0.014453524776931, 0.648109446414884, -1.04630158196848, 0.514031585861522, 0.298764243143644, 0.345469986911851, 0.210707596451789, 0.199614419128464, -0.809836977320697, 0.537598149506386, 1.20320406735625, 0.933408419800931, -0.153402823057145, -0.448885976553377, 0.512858151528217, -1.05614175203572, 0.273370542841903, 0.176577032873555, 0.149488999169619, -0.0969845931346009, -0.448259430882757, 0.302062651944207, 0.193430129736736, -0.442036587431237, -0.402538634393625, -0.0214182072052525, -0.0131542618320503]', 77.7); -insert into credit_card_transaction (time, features, amount) values (157662, '[2.14364255916549, -0.893865129537971, -0.655476327481577, -0.463836586080127, -0.841514151023904, -0.178681944358383, -0.957572075390224, -0.0693714184127005, -0.0008757656583835, 0.833030353579198, -1.28500043797636, 0.103234640521625, 0.93938707092135, -0.403614458875594, 0.334883657485709, -1.16486027280389, -0.362283847780942, 1.46161290714914, -0.996949775731555, -0.522496056978568, -0.073605417022869, 0.455536053520713, 0.148188823555128, 0.632236642007225, -0.178765618853877, 0.813242635002735, -0.0171298894691852, -0.0436872684495654]', 12.1); -insert into credit_card_transaction (time, features, amount) values (157662, '[-1.90736565426464, 0.67384060201129, -0.152918028105094, -1.5241369471231, 1.68595396552452, 4.28649383843176, -0.03899793397186, 1.26990047620504, 0.0201745479735995, -0.687288009662468, -0.130267154798492, 0.0665532139980587, -0.0349481760360964, 0.274178499755284, 1.47977466989759, 0.163948001003363, -0.460332045625155, 0.0050472781613322, 0.244590625448951, -0.335402788979408, -0.106067870886694, -0.494800653404582, 0.0607691121906984, 0.667957103302721, 0.36208419408435, -0.438986051355533, -0.706788421465791, 0.205657311481346]', 109.22); -insert into credit_card_transaction (time, features, amount) values (157664, '[-0.862387024902042, 1.78470367281811, -1.38775787378779, -1.66026708473102, 1.70641506304972, -0.95413573763879, 1.98703979262836, -0.727195563587107, 0.94144518719367, 1.4911580478853, -1.37360420398336, -0.763227897888498, -0.602794546296498, -0.0215747263190106, 0.0963476394298945, -0.675334286704451, -0.922752038319437, -0.187712852635465, 0.0172104283533561, 0.737292715221469, -0.0642561715728267, 0.642652554241579, -0.31861455430985, 0.0531787821033688, 0.0001807567220916, 0.0656196875104861, 0.585735804463068, 0.121318387442835]', 2.16); -insert into credit_card_transaction (time, features, amount) values (157665, '[2.04817471547879, 0.160891509841538, -1.75975319561615, 0.240847031281068, 0.656705375413303, -0.407588627071841, 0.134090819475397, -0.121984321631118, 0.124731830243019, -0.233330753248871, 1.06997705824216, 1.19995800815063, 0.844771627467875, -0.825597786123648, -0.569307935069556, 0.53989580273333, 0.0935927474354178, 0.160003637859814, 0.42174281232985, -0.0842305360986474, -0.318123549525181, -0.806213849790965, 0.289389090640184, 0.278961984560853, -0.230272669700398, 0.165385894702223, -0.0614547261018728, -0.0411052472494336]', 1.98); -insert into credit_card_transaction (time, features, amount) values (157665, '[1.85870279773918, 0.396051041629609, -0.455366911681645, 3.86098983917909, 0.208507579286698, 0.126162728018291, 0.0131965552961598, 0.0489802730795498, -0.845638070131801, 1.55991100586878, 0.22577243084112, 0.414947272421794, -0.580585720132935, 0.266577297786502, -2.19883837974612, 0.631700207118974, -0.702440354452217, -0.0189354878139345, -1.08856866097233, -0.323787309691886, 0.130189017963616, 0.504362094254044, 0.0845574433770186, 0.0639351766871012, 0.0776383528141651, 0.0730375839484188, -0.032659310900174, -0.0604215347892459]', 7.58); -insert into credit_card_transaction (time, features, amount) values (157666, '[1.95279101351902, -0.443646680213216, -1.64373558608181, 0.157513597855708, 0.319796654027065, -0.200328722480518, 0.0958716466424794, -0.0623234262433549, 0.543482715761223, 0.219799115017581, -0.178400816130077, 0.261489094592664, -0.953867646087732, 0.608799293077844, -0.678692386227105, 0.233118444711131, -0.69303495574473, -0.0104506235005991, 0.797291024078636, -0.123507591532171, -0.231262239014541, -0.717605279304208, 0.104836156578914, -1.10714070345973, -0.161960492044155, 0.322044482786744, -0.0946842408649669, -0.0750659926959481]', 69.64); -insert into credit_card_transaction (time, features, amount) values (157666, '[0.0666260477973693, 0.593815427888797, -0.522878691313413, -0.679975879636316, 0.732897383075077, -1.21219306455882, 1.49501223350433, -0.289458227392707, -0.631534221749399, -0.361204025767523, 0.557276348023702, 0.0202015857971261, -1.46658320951785, 0.895822663843126, -1.35555825630229, -0.392276220018851, -0.342557203264131, -0.0493754067049264, 0.215628991956632, -0.15331249096827, 0.246035914963297, 0.660660434964352, -0.173448608693566, 0.126564700747486, 0.0360134286035453, 0.729353876823891, -0.0760040018886952, 0.0011735561200836]', 49.95); -insert into credit_card_transaction (time, features, amount) values (157667, '[-2.33353507264481, 2.46354962218188, 0.0941195136921072, 4.46483394314277, -0.265535547392461, 0.319256387897496, -0.208615935390274, 0.155277515285382, -2.95938211453577, 1.04504589444477, -1.22265002761061, 0.160343713770289, 1.54656272337205, 1.12680738289013, 0.894257121352872, 0.134212690824433, 0.585336201924697, 0.247568522109529, 1.11462971591331, -0.37267298666156, 1.13845278449457, 0.492590933528634, -0.374825397091449, 0.0436122629170256, 0.633610686193592, 0.67739750885263, -0.582974717540913, -0.165175977580806]', 55.38); -insert into credit_card_transaction (time, features, amount) values (157668, '[2.03804669121107, 0.149228928096936, -1.49356362147525, 0.541991577091293, 0.136141242468747, -1.18376465959481, 0.175941280497561, -0.252148730657828, 0.505357460827131, -0.369960062729377, -0.159077562004647, 0.152705745134623, -0.334666217716109, -0.751982994863708, 0.20138899583308, 0.180483677724108, 0.67865827130496, -0.438075952430741, -0.139525195530629, -0.192007415169572, -0.334124293304863, -0.875054160258706, 0.407086197716554, 1.04365772082047, -0.337475787869709, 0.140677126123062, -0.0640799319838407, -0.0258456228731323]', 1.98); -insert into credit_card_transaction (time, features, amount) values (157669, '[2.30348772822127, -1.1003422751185, -2.6773253235167, -1.63428927304118, -0.0076793156786795, -1.2481723022086, 0.128023315710728, -0.643828712842836, -2.17526438795832, 1.6953077447938, -1.03249807539356, -0.848094200122647, 0.647195121863868, 0.199617782777387, -0.624026683784342, -1.42910298247797, 0.744921928840637, -0.443190944165041, -0.0141116922689533, -0.297007244682514, 0.144357919686483, 0.82706600686136, -0.191150560531695, 0.590109588043703, 0.677329811589187, 0.275917322692467, -0.0807080346350161, -0.0720901555637475]', 75.0); -insert into credit_card_transaction (time, features, amount) values (157669, '[1.91473786768401, -0.0493338141702305, -1.89540357698028, 0.0689704013070788, 1.23065499278398, 1.13876055425523, -0.172044228191091, 0.374879112286872, 0.231875454738994, -0.353585847129736, 1.59432167700619, 1.05129269147745, 0.0388187267979768, -0.588217656042168, 0.334877726145528, -0.351687516401723, 1.01270899353266, -1.17906725069728, -0.707203197243012, -0.235150494224008, -0.242574615962248, -0.509926924150119, 0.386134956007147, -0.821519981542935, -0.481882566963798, 0.268681463861382, -0.0128784758553372, -0.0546758025084535]', 0.99); -insert into credit_card_transaction (time, features, amount) values (157669, '[1.87446856530342, -0.46441843689523, -0.472121973473791, 0.18103869914988, -0.494605836015767, -0.237281592565409, -0.480886140400147, 0.0672599443036567, 1.05899007418193, -0.0889467223184553, 0.75020210451754, 1.14212531307948, 0.124141605150623, 0.263727736684343, 0.425507127067417, 0.449210717218909, -0.967536247730269, 0.509108683604654, 0.187854309406977, -0.126026761734618, -0.0618707295394016, -0.177110991339604, 0.266813923131493, -0.451860693176587, -0.386324119133951, -0.932109986929829, 0.0484066610755568, -0.0323440170864113]', 50.95); -insert into credit_card_transaction (time, features, amount) values (157670, '[2.12279133771394, -1.18741824192205, -0.916483921875039, -1.09236487905358, -0.727711739679387, 0.0764459281895712, -1.08103442973963, 0.0659246517250018, 0.0032208902291789, 0.906362620330379, -0.156255400131866, -0.344783537242717, -0.0507919872810802, -0.270931078890298, -0.405206919807005, 1.68154856766522, -0.483672636320703, -0.25218225156518, 1.13003290229964, 0.0654318639110971, 0.322088813490681, 0.887408335253001, -0.0388214523566199, -1.06834387231041, -0.0628157073385791, -0.0430772833385128, -0.0042268595413406, -0.063204173508402]', 49.99); -insert into credit_card_transaction (time, features, amount) values (157670, '[-2.59576480783823, 2.8840239391573, -2.3126776019042, -0.760320433945951, 0.213651620948756, 1.81649581536989, -3.55896540541555, -8.85262475130317, -0.741416240269013, -1.35215352304068, -0.489738719913259, 1.08574992463212, -1.07560466846554, 2.18203764850392, -0.321870391369415, 0.628842473016798, -0.284181179852542, 1.1275605561027, 0.0483897622914708, 1.53518079653712, -1.88674720866848, 1.85104279840616, 0.664449259513719, -0.268815204921672, -0.831607600369412, -0.242287134379948, 0.498574656340234, 0.372368425344521]', 15.45); -insert into credit_card_transaction (time, features, amount) values (157671, '[-0.0956813316911167, -0.367757271273178, 0.236132981001182, -2.65804257142269, -1.20649595496618, -1.13068285237342, -0.446167242708761, 0.301906524881184, 0.247123113323113, -0.877548922001801, -1.25464054481686, 0.148066269919556, 0.0053011356888609, 0.219864998355756, 0.828511274946982, -1.89657531109896, 0.180259751140201, 1.42362583442616, 0.304961802426908, -0.527425357706104, -0.408399754013747, -0.791635121453671, 0.403884811673834, -0.0966960227895864, -1.13719124258596, -0.610808696584691, 0.0634683348824682, 0.0389644225985704]', 41.06); -insert into credit_card_transaction (time, features, amount) values (157671, '[-1.86488219894914, 1.23613748194336, 1.55203146229971, -0.277503018029693, -1.68020467427744, 0.414109989733868, -1.35741923537803, 1.58293596192924, 0.899501508123272, -1.56880333199636, -1.95541618155083, 1.6023183750899, 1.2427244222821, -0.717529357684178, -2.16123285093702, -0.101621920209923, 0.64694513014031, -0.503990163032675, 0.207679299806106, -0.259278542066298, -0.025948886346506, 0.030922895620315, -0.177984159200742, 0.0824577060844309, 0.612711713176754, 0.696564590661485, -0.432156230597208, -0.188617923395142]', 3.71); -insert into credit_card_transaction (time, features, amount) values (157672, '[2.00553228161842, 0.0070868966527725, -3.65259548241003, -0.203742162034209, 2.88371007913055, 3.06387126009357, -0.407470730758041, 0.787168176369652, 0.573959954868234, -1.0425561174637, 0.458201855114249, -0.123987721389439, -0.470081890499791, -2.42916601060841, 0.0321352652214023, 0.266431399506244, 1.95537235613427, 0.506609128807379, -0.398342592881336, -0.067841254193996, -0.018586847672948, 0.151813633128278, -0.0081306451001703, 0.562138291560673, 0.239086126042579, 0.747061487131281, -0.0184026193243048, -0.0180547635727395]', 12.31); -insert into credit_card_transaction (time, features, amount) values (157672, '[-0.501119701945683, 0.631898598931087, 0.261426345840006, 0.81898535891173, 0.807476569727662, 0.0163751766080137, 1.50818509883941, -0.212048811020143, -0.99179495398615, -0.347735979589194, -0.93995294639717, -0.006084666561543, 0.869016128406528, 0.337219881241216, 0.817204393257627, -0.484879958652308, -0.36580688516666, 0.283787894216577, 0.41421824194594, 0.429927128156805, 0.437560512581943, 1.05161420378429, -0.0122425995554489, 0.54232587914934, 0.293018758630902, -0.293144286742353, 0.112615866328799, 0.173152642655114]', 158.5); -insert into credit_card_transaction (time, features, amount) values (157672, '[0.177812185476632, 1.46402086483703, -2.41533855299721, 0.548454242435985, 0.907647046294263, -1.94616322869331, 0.766631154276994, 0.116071532794883, -0.0094249948102138, -1.75700434669279, 0.0036210606576855, -0.506859554160035, -0.593178329700087, -2.49446747850741, 0.79627149882876, 0.313731670138587, 2.66063662293421, 1.3602437645134, -0.0683051398364217, 0.0029849606116793, 0.0188340227238714, 0.156009308911535, -0.0128163986020703, -0.306026483328676, -0.138182011250051, -0.492175744525951, 0.168295228347058, -0.0357874469552025]', 20.0); -insert into credit_card_transaction (time, features, amount) values (157672, '[-0.984714008596551, 1.56751031976119, -1.34659360423239, 0.932186496465468, 1.5619731259302, -0.202129516421906, 0.964876317929437, 0.303446779651333, -0.989685608936685, -0.441089866550192, 0.537792017461151, 0.349048521469868, -0.0945585411994336, -0.662439489588188, -0.801901696895235, -0.261812157806986, 1.04597684216332, 0.9804587818583, 1.03748134356874, -0.0838336144950998, 0.081395669810669, 0.447138350637323, -0.0206412258029941, 0.108025054320145, 0.155234851373501, -0.407761236733805, 0.0995427684657036, 0.201292004298201]', 16.1); -insert into credit_card_transaction (time, features, amount) values (157673, '[-0.271252199321979, 0.803480528970401, 0.339312365067873, -0.993036392768877, 0.779804591946232, -0.390229098889444, 0.9326941083428, -0.0824337036130174, 0.342150121047779, -0.811070527239764, -1.41191358715847, 0.304282769781088, 0.299826531694597, -0.190595085177309, -0.910852500493808, -0.6199802179378, -0.283804840994717, -0.246043401456233, 0.174958196440685, 0.0212521699194917, 0.120213411956863, 0.712492547992129, -0.301738269516433, 0.649183995098063, -0.0077483684214828, 0.102924365930774, 0.391400113145998, 0.236470496907201]', 1.0); -insert into credit_card_transaction (time, features, amount) values (157673, '[-0.308966639631638, -1.40872178095524, -0.521433406405505, -1.96452516548817, -0.564035272006543, 1.19137991184367, -1.24775422064385, -2.53898718407234, -0.930550087548313, -0.584813358832481, -0.144109497073928, 0.354564613694285, 0.816924593463184, -0.0839757171361925, -1.08355086812859, 2.24730058482718, -0.76715329599421, -0.355362415127438, -0.141825512813973, 1.49539640097667, -0.788734400135575, 0.784697714610051, -0.592602136549449, -0.293926035081469, 1.1089170315443, -0.111220282199828, -0.146147083718468, 0.108102173061073]', 399.0); -insert into credit_card_transaction (time, features, amount) values (157673, '[-0.2383175921796, 0.551722978569067, -0.721888519755237, -2.40560801327523, 0.323621573194478, -0.943134115400195, 0.328054953996721, 0.138674382541974, -1.3751938628724, 0.0109708633360267, -1.20412977253443, -0.24642176715472, 1.40941626022518, -0.0655490438594682, -0.685958598166706, 1.24115934821103, -0.257649430141542, -1.18147828205379, -0.251127628843804, 0.0673948514172616, 0.582465253570036, 1.64412188320386, -0.107894123892428, 0.555038375233325, -0.581955127130999, -0.227846664554553, 0.337196233986138, 0.239471066939677]', 10.0); -insert into credit_card_transaction (time, features, amount) values (157673, '[2.02733083232336, 0.472540119973513, -2.49909513199218, 0.595279769559177, 0.670424724319215, -1.32398899994942, 0.21240769510333, -0.198721762173749, 0.391219659381951, -1.0064326585289, 1.47347467682044, -0.0723978308964411, -0.848235290020886, -2.19794608656666, 0.480786051529934, 1.09826616855598, 1.44843783671005, 1.96794307367733, -0.283040482073196, -0.183546836461205, 0.118972959031012, 0.473359732833711, -0.0899100621352102, -0.572288530117652, 0.243524799222156, -0.0976006654556805, 0.0106651446114544, -0.0072909786351266]', 1.0); -insert into credit_card_transaction (time, features, amount) values (157674, '[-0.309060267470029, 1.40998804346571, -0.674802194830392, -0.826464566512148, 1.27308883545268, -0.444900952041377, 1.22664047897167, -0.357044211671821, 0.416302982399144, 0.0606290087698471, -0.727247656947094, 0.239219136408626, 1.12149426481706, -1.63690077262252, -0.205240668057125, 0.224180893099813, 0.0642954790335486, -0.352067182983902, -0.186528397092605, 0.396806760393592, -0.452377650507409, -0.826049342792934, 0.0277310671309127, 0.0917732201887042, -0.271723828858603, 0.110899403364912, 0.149591080916801, -0.0226316518603748]', 10.76); -insert into credit_card_transaction (time, features, amount) values (157674, '[1.79012266278276, -0.676921329244653, -1.40738218221979, -0.10049693190564, 0.537654789886719, 1.07062950945947, -0.264393915983108, 0.340203741043811, 0.930112260600053, -0.189151878545155, 0.739739623780943, 1.06863004378843, -0.564679386284994, 0.442783800471179, -0.117090281782435, -0.975382654142485, 0.273966009506599, -1.2301065076826, -0.203450383688819, -0.203481730838543, -0.0408136794039017, 0.0483738927955093, 0.157158489169186, -1.62587796029627, -0.264068945587754, 0.0179205357942158, 0.0054895478935852, -0.072756306716618]', 70.0); -insert into credit_card_transaction (time, features, amount) values (157675, '[-0.999120757496684, 0.33152744973671, 2.37847185055143, -0.704231858794303, -0.137632522636944, 1.35792088902069, -0.245055118411236, 0.484115233576339, 1.36945492230933, -1.06648172069811, -2.4246690433919, 0.33485920584977, 0.397434948615654, -1.49925829613848, -1.9101083786839, -0.156274875845579, -0.141511411827937, -0.110376653386028, 0.675050143296975, 0.112131386868392, -0.219962073864621, -0.145418099079366, -0.446534467167365, 0.116279437710557, 0.563633592063988, 0.70855665509494, 0.175310618175618, 0.109633450314114]', 23.27); -insert into credit_card_transaction (time, features, amount) values (157675, '[2.0304377142877, -1.41477713947346, -0.25903707134831, -1.17903472604184, -0.946317219385446, 1.00061424458798, -1.66715848128407, 0.29955333760075, 0.254010524064565, 0.781880588790689, 0.0481709786595812, 0.474727259731515, 1.72594701531832, -0.984881838997233, -0.0765731306232688, 2.14774777302976, -0.833583330444377, 0.0676312102237355, 0.858257028127041, 0.272345802905228, 0.454295899969593, 1.25694910062995, 0.0132059006657339, -0.280174883787165, -0.270952807161646, -0.0892141912019501, 0.0415328397080597, -0.0311625185996005]', 76.0); -insert into credit_card_transaction (time, features, amount) values (157675, '[2.12264482184131, -0.678740227022123, -1.05568228588984, -0.534485241748948, -0.897008993793866, -1.04365353618049, -0.895160818776703, -0.145750317348732, 0.0778511248864166, 0.0763734628989035, -0.303660247551544, -0.791394485163903, 0.0689112455356252, -2.08011692844464, 0.388627944326622, 1.60905088018795, 1.43918498708263, -0.529993883680797, 0.128261484400098, 0.0239760026349763, 0.246097426609964, 0.800440812696472, 0.109213635295877, -0.113855335450338, -0.169836820298458, -0.0848353024173462, 0.030943893607115, -0.0114638765188567]', 21.99); -insert into credit_card_transaction (time, features, amount) values (157675, '[1.25738607770783, -1.29260115196379, -3.22749020870842, 0.315520354300516, 0.671152232786627, -1.1507446947082, 1.68824376778646, -0.777889908238491, -0.0868015817041932, -0.231069023926174, -0.855760992723037, 0.676351025069804, 0.982964320579347, 0.742552658332368, -0.0877854130292076, -0.890234187740606, -0.236943864219838, -0.501607557726412, 0.167703332569304, 0.825103607664235, 0.55628119504328, 0.750961721329793, -0.642426748823071, 0.624327789779637, 0.673763407883049, 0.388715763678561, -0.191759113813372, -0.0007451054957198]', 484.0); -insert into credit_card_transaction (time, features, amount) values (157677, '[2.20105339451337, -0.963279089455179, -2.79248687914929, -1.93965348419363, 1.97958446680884, 3.13262435263015, -0.844146857732279, 0.695954207281895, -0.634243222529645, 0.808695051449459, 0.0648416225096195, -0.413990889130083, -0.0847463254003369, 0.170565689831317, 0.101520462835394, 0.550523496552585, 0.307036072719545, -1.9955337740532, 0.700393138125186, 0.0301484386094088, 0.0261588158206121, -0.0075976667386555, 0.229173154036864, 0.712063278795965, 0.0236787144634407, -0.258540944938817, -0.0213502786505819, -0.0692189199705032]', 15.27); -insert into credit_card_transaction (time, features, amount) values (157677, '[-1.03468314179644, 1.12458635099361, 0.18547384505549, -0.65481970320812, 0.605563910604064, -1.04087157747382, 1.42362613744374, -1.06360088065318, 1.48186258006072, 1.53581185157729, -0.909805106781413, -1.62689023797206, -2.49061273132976, -0.114108911533506, 1.04740100412106, -0.715824957737676, -0.643625714952924, 0.245933609089612, 0.0205159220166089, 0.0044864241323308, 0.25961802057698, 1.14778729624083, -0.206160246789341, -0.0459455336265481, -0.620283460249351, -0.314296758864627, -1.04463567602418, -0.26698846629468]', 5.35); -insert into credit_card_transaction (time, features, amount) values (157677, '[-2.46602084188543, 0.365723836271079, -2.30764260745798, -0.674368364383094, 1.01115842968562, -0.81897883400823, 1.80355231398438, 0.0620849442454661, -1.22065163483867, -0.79602351024818, -1.66869247696958, 0.349921892681564, 1.18920821934105, 0.988393454596161, -0.559883289196942, -0.203888976718025, -0.217321788687955, -0.440729857765989, 0.264828423235882, -0.525086575607077, 0.176500837880444, 0.733180271609274, -1.25388318491376, 0.276956931686362, 0.852186166428873, 1.02716117049555, 0.227956062865521, -0.532015077707887]', 272.06); -insert into credit_card_transaction (time, features, amount) values (157678, '[1.82186028194341, -0.475662654961392, -2.22583472408642, 0.181785080811274, 0.639870312294077, -0.573706321453177, 0.664033577364905, -0.383928673782837, 0.604239078891329, -0.202434955872931, -1.37575162360916, 0.28451823716913, 0.383417799546334, 0.508343291916738, 0.472608268238342, -0.358855882877338, -0.6248861321501, -0.023829956158792, 0.284247492490685, 0.142566118194225, 0.154674094376264, 0.244923812684508, -0.190888394420315, 0.059833210715731, 0.441620969366864, -0.527119234872603, -0.0433197593798314, -0.0300237462207368]', 169.43); -insert into credit_card_transaction (time, features, amount) values (157678, '[2.31202561817784, -1.43008187190908, -1.20891062947418, -1.61985678933988, -1.07513640536755, -0.531039007752024, -1.10615124517161, -0.13871647842916, -1.05917680067469, 1.60326176406705, -1.48243248764555, -1.51490719220309, -0.643148729886864, -0.196704397908706, -0.0335873360107412, -0.461923055781142, 0.45428201030941, 0.118643480079771, -0.0006470356543341, -0.458251153374536, -0.201594624709316, -0.163630838616283, 0.232024347937327, 0.425256114582135, -0.149498661972579, -0.175277515428491, -0.0056173654975395, -0.048045932013292]', 25.0); -insert into credit_card_transaction (time, features, amount) values (157679, '[2.20112909336745, -0.671994779399452, -1.56060650598055, -1.02066990214565, -0.165324456280594, -0.861499661758035, -0.233625889151165, -0.413477930348158, -0.748462267577397, 0.819564035196897, -0.583569100220292, 0.0252543571183071, 1.67915136623939, -0.274034906742261, 0.232529082197224, 0.904766285315661, -0.0303924449195594, -1.36931320807433, 0.494652766574586, 0.146747338706366, 0.445574837917572, 1.2815932381753, -0.0428471427190114, 0.676814548688191, 0.288769753895877, 0.0376195241325399, -0.0399049520300122, -0.0530318451753298]', 40.0); -insert into credit_card_transaction (time, features, amount) values (157679, '[-0.406861632704899, 1.10220169200469, -0.163043851415343, -0.661329325297498, 1.02786186290888, -0.968687671861143, 0.954720798735252, -0.158791795883965, -0.249384945471057, -0.781910763051732, -0.592107956210078, -0.6143390453661, -0.278095878217383, -0.957396974501664, 0.508244372519525, 0.481660377253925, 0.308440184253551, 0.499830392681288, -0.577671483960768, 0.059481737552085, 0.155641381215255, 0.498982403643476, -0.32859453268426, 0.688342290723948, 0.404862498063351, 0.673761844550901, 0.0536463672545795, 0.10084281463515]', 1.5); -insert into credit_card_transaction (time, features, amount) values (157680, '[-0.365258778754858, 1.09728199885184, -0.372540084111885, -1.06162026009461, 0.797928615474386, -0.200416513681132, 0.868955043094719, 0.0573705218747252, 0.238613491055849, 0.385692613538063, -0.251298058189558, 0.162960457538497, -0.489465178570015, 0.222562619918289, -0.927959870474759, 0.408291156254974, -1.0368217693522, 0.141706729388943, 0.555428694950459, 0.220457608847449, -0.34902776292063, -0.750426240223662, -0.0470001451392357, -1.12898625261378, -0.292612186439175, 0.199217963684531, 0.454366054245722, 0.193341298948532]', 11.99); -insert into credit_card_transaction (time, features, amount) values (157680, '[1.98748459885835, -0.434937303357228, -0.43341360311259, -0.0383420335854398, -0.523485718247296, -0.192562136340337, -0.637596396099461, 0.115756207070385, 1.14372335228563, -0.0343432858817313, 0.562351696642149, 0.929150227432415, -0.238828896944658, 0.18321496789006, 0.0899702366987793, 0.480247175915945, -0.821162619106004, 0.299516950287604, 0.403448718212386, -0.226331055578317, -0.169646031086914, -0.366638281020488, 0.342715598198295, -0.501028277311011, -0.502246487965202, -0.271937122006483, 0.0073283650548859, -0.0524708628887952]', 0.85); -insert into credit_card_transaction (time, features, amount) values (157681, '[2.08335683194978, -0.760338518270083, -0.371132090587612, -0.630822813589613, -1.3671809627947, -1.70339374271083, -0.508651960074439, -0.336673061775539, -0.365120189346657, 0.747818588413896, 0.260146545325104, -0.0770551286316976, 0.0642745890328091, -0.0940110683821129, 0.212964182981821, 0.90547226398528, 0.465813899807659, -2.16925578439858, 0.412732159942784, -0.0241646354436584, -0.185053891904558, -0.649661538271287, 0.579829694705514, 0.87068513905863, -0.681755054997547, -0.748243257534186, -0.0151101008788047, -0.0312777487143151]', 31.95); -insert into credit_card_transaction (time, features, amount) values (157682, '[0.0548147668971734, 0.825636497730477, 0.221687065106836, -0.61480115061977, 0.43311391019309, -1.05478616463575, 0.971882266465343, -0.168226855354062, 0.0452097219706133, -0.394859199061759, -1.14789246664647, -0.18925791226864, -0.487394675747123, 0.207573131665409, -0.33511851176873, -0.0257877595878572, -0.432272753596909, -0.636294099209896, -0.0246573610170176, -0.0684436629534681, -0.276508015776315, -0.641373787315523, 0.0566548487015433, -0.131552031704074, -0.487460608298759, 0.149207347522262, 0.243716617644422, 0.094767943885897]', 2.69); -insert into credit_card_transaction (time, features, amount) values (157682, '[1.97815112277616, -0.24728564812647, -2.13688593485985, 0.0920593681792395, 0.891761011954678, -0.0849775237016036, 0.444558484459444, -0.197438271892641, 0.0650292261484287, 0.216567992098539, 0.237830701218962, 1.06522121128682, 0.540828413389754, 0.450929826651405, -1.05398962261634, -0.192009139397746, -0.608695084413949, -0.205417182587548, 0.701731255892005, -0.0079903924132831, 0.0105629178345177, 0.0626463306121614, -0.0546621656227497, -0.034918420975161, 0.253965989808443, 0.552405424313774, -0.11182523678573, -0.0759104676085585]', 69.95); -insert into credit_card_transaction (time, features, amount) values (157682, '[1.98583259518274, -0.259051352902079, -0.692348311932495, 0.361386052723655, -0.430187227578805, -0.941094018329212, -0.108181136733131, -0.291570204601908, 0.69826882035104, 0.0005145104525627, -0.626561186482978, 0.969559024312258, 1.25942662543269, -0.181713860593075, 0.175809847585325, 0.0772102693876024, -0.46290452188066, -0.531296305237457, -0.100887214246555, -0.0762455840461724, 0.0250749381393894, 0.243929235310853, 0.184098376326277, 0.123152522278537, -0.233393907004938, 0.548058544265512, -0.0537910669822665, -0.0496008488653154]', 31.35); -insert into credit_card_transaction (time, features, amount) values (157683, '[-3.16328184013955, 3.30039052844423, -2.77108528214923, -0.774925331364977, -0.12228461688672, 1.72993972423332, -3.94206270118423, -8.40276042188811, -0.722225194451139, -1.26939693037988, -0.617674334146216, 1.25513464917228, -1.01420387800534, 2.42874856807674, -0.349008499899204, 0.725585693395977, -0.123937983598227, 1.15485998360559, 0.0138188905995267, 1.58077264938073, -1.86600751904257, 1.82392818504639, 0.800225135943605, -0.27872286542676, -0.740382785431581, -0.235398596748473, 0.458054920068973, 0.375028578541356]', 0.77); -insert into credit_card_transaction (time, features, amount) values (157683, '[2.15141771776466, -0.572687933540587, -2.19490001110557, -2.27449752038785, 0.235487027659246, -1.14793810822466, 0.205527924172439, -0.407403624405586, 1.92451536766178, -1.18073467514183, -1.01956015373171, 0.645776862256435, 0.38041217529306, 0.484741927744875, 1.63248818661826, -0.892661783497144, -0.497274476588846, 0.385455342637485, 1.12514563405842, -0.092617328475195, 0.241415021656626, 0.884928358638999, -0.14581791913272, 0.147068591709136, 0.552138765196511, -0.605838871616567, 0.0136012174868917, -0.0527951405655972]', 33.54); -insert into credit_card_transaction (time, features, amount) values (157684, '[-0.768054680867553, 0.0544425442061756, 1.11236705232835, -2.31840678155671, -0.717744301700017, -0.0757738689131079, -0.35279141220918, 0.115016295618592, -2.66113341120407, 1.18682487069941, 0.425232653218657, -0.414970093981548, 0.759419015772579, -0.301353667246672, -0.647906344331232, -0.785350288542679, 0.565545413217258, 0.56250487331489, 1.10099706167775, -0.231786203981882, -0.102711243122863, 0.100135871623601, -0.428667642712239, -0.386025736665794, 0.501307542474657, 0.0559295274635997, -0.497363476696936, -0.240561050421874]', 10.0); -insert into credit_card_transaction (time, features, amount) values (157684, '[-0.831138228215365, 1.14852007511413, 0.571786141489337, 0.178836874627494, 1.57174575602333, -1.24903023653625, 1.66338741423807, -0.366841436620378, -1.29988414641891, -0.607598854896702, -0.897159907506269, -0.239456854267468, -0.104188317524345, 0.698709530477251, -0.004005074812894, -0.532818716750015, -0.390070859711166, -0.327872503534486, -0.357254136361973, -0.0332765109940069, 0.153395034330664, 0.343013699027422, -0.597100448017311, -0.076525364097104, 1.27645122145004, -0.342032665942805, -0.0129367764806701, 0.0433930007748864]', 9.9); -insert into credit_card_transaction (time, features, amount) values (157684, '[-0.0250774700344481, 0.851425305288206, -0.946901059815186, -0.0645370016103051, 0.335786276738546, -0.541042636445767, 0.29537895482137, 0.48269266514075, -0.0008526268067678, -0.953783465388788, -0.0052319131367756, -0.472561116629127, -2.14555664892262, -0.375987891069494, -1.20760673911253, 0.179255438421801, 0.894662839785164, 0.994980110131881, 0.567165265951081, -0.326458078516148, 0.21987387570077, 0.666558953715943, -0.152096474022838, -0.535522341965088, -0.444908437928033, 0.69131410042931, -0.0687392946558427, 0.0064094603276568]', 12.36); -insert into credit_card_transaction (time, features, amount) values (157687, '[-0.370194207833387, 1.27909213989218, 0.945288137363863, 2.83067592394674, 1.37941087888421, 1.16744982708709, 0.857187084483609, 0.27411306059047, -1.79202538449822, 0.861824140274844, -0.528946980317489, -0.26900603137309, -0.522116760445392, 0.164956595912568, -2.2372637814448, 0.613902617378961, -0.844956908419263, 0.454526026728535, -0.429974301437446, -0.087295194272989, 0.179749206194917, 0.520532003388364, -0.304826470346445, 0.18575176099613, 0.0110417580682294, 0.118954084264323, 0.117320690806495, 0.151824908667014]', 18.9); -insert into credit_card_transaction (time, features, amount) values (157688, '[1.90351822823552, -0.174347941074012, -1.9565640939925, 0.403944756157283, 0.281224002868475, -0.787302383100343, 0.139341069407735, -0.175846993248174, 1.0817296429073, -0.922491702073184, -0.718960420910587, -0.0596976599110652, -0.529626021162883, -1.51300169992354, -8.66407752302123e-05, 0.141296953794059, 1.12224317650492, 0.246586305806003, 0.171273375756559, -0.0253883487909423, -0.234627900521977, -0.632259699414274, 0.135908874516382, 0.438758782112635, -0.0597170130518357, -0.11831946976014, -0.0267978184381739, -0.0013352219699531]', 82.61); -insert into credit_card_transaction (time, features, amount) values (157688, '[1.87799654523266, -0.473803808836681, -0.66857116800568, 0.298629813234874, -0.278078652370265, -0.121642974434615, -0.373670479726424, -0.102155331636264, 1.03035871665514, -0.184689214484112, -0.848368220097319, 1.0418687554502, 1.76631717017878, -0.331297315294243, 0.901283988764296, 0.337129265866278, -0.89284775594053, 0.251560821698716, -0.28689677835906, 0.0758677742036345, 0.252609760104486, 0.769788906716097, 0.0535174484528977, 0.531526164267654, -0.0583329769790265, -0.274533806722435, 0.0240981308406351, -0.0156128519274166]', 84.25); -insert into credit_card_transaction (time, features, amount) values (157688, '[-0.497431419039788, 0.678158594150144, -1.52093761765639, -1.00057109348182, -0.741601751122412, -0.8880497645695, 0.653593600814509, 0.429175601746033, -2.04751794857141, 0.220195098849719, 0.198467265200732, 0.287960706676123, -0.505580363749793, 1.28022325503367, -1.34127503651789, -1.89669886757904, 0.413207003209608, 1.27917851518974, -0.162331104460548, -0.813198949777903, -0.0048115234563083, 0.276262681274769, 0.0646805409594056, 0.0510604957659754, -0.429059736311705, 0.750026861225639, -0.282958609902457, -0.101002720882118]', 126.0); -insert into credit_card_transaction (time, features, amount) values (157690, '[2.10569868219245, -0.0967341893236175, -1.64637823073991, -0.0712262205681997, 0.753233496880488, 0.162771060999685, 0.0411018812322155, -0.055833507931254, 0.254505475721204, 0.21895283587267, 0.0753605084485017, 1.04057323138976, 0.77393034560624, 0.276187834182368, -0.601383565555542, 0.405819598685524, -0.988483696543147, -0.0579625329510764, 0.84989447180861, -0.109865865366591, -0.30743447433671, -0.794361214071003, 0.223205182004414, -0.335628695742678, -0.146090365282371, 0.209131844522297, -0.0771602400682416, -0.0727125420356728]', 1.98); -insert into credit_card_transaction (time, features, amount) values (157691, '[-3.30902736815795, -0.953121412523735, -0.448618081451785, 3.5023951721798, 2.14139189337699, -1.37620845509008, 0.488301380445685, -0.265554628402911, -0.939523909408261, 1.80528296431614, -1.43812541897342, -0.492360425384969, -0.255901876217212, 0.161015764691412, -1.2096456937157, 0.788597852789847, -0.889976251973906, -0.704400025320373, -1.42680914839219, -1.07671823617417, -0.281753058007557, -0.158238551218931, -0.379411208693719, 0.0446265889886949, -1.26913373260076, -0.572879453061116, -1.16291772745959, -0.0036116114707116]', 13.25); -insert into credit_card_transaction (time, features, amount) values (157691, '[-0.0126004838572641, 0.73265898823155, 0.277481257823411, -0.555236383221767, 0.386345733850758, -1.18033219686887, 0.940785865562634, -0.165603213793502, 0.0611968178410471, -0.390086281953597, -1.03551882213013, -0.217852090642622, -0.669022442977876, 0.251836072596179, -0.326387728710055, -0.0642738287234888, -0.365673685253362, -0.663584046836528, -0.0835716088750195, -0.0525552170138068, -0.258908609117544, -0.629871016249881, 0.105406562237157, 0.019805478196573, -0.508187704649129, 0.139853899653454, 0.238327664123373, 0.0977955842172623]', 8.99); -insert into credit_card_transaction (time, features, amount) values (157691, '[0.0072074549137557, 0.506412857381131, -1.187777952089, -1.7201221079315, 2.95558446318271, 3.384701432179, 0.323179659950747, 0.927407014119902, -0.163996549189058, -0.386041544504147, -0.199912764343157, 0.0340563096040058, -0.373073285963966, 0.355618969599158, 0.0425347460166415, -0.155055439291267, -0.482053766676739, -0.840693748107076, -0.206669805212565, 0.0358000410316673, -0.26221323015063, -0.718377299056042, 0.119794752199664, 0.688138959149158, -0.457919325901241, 0.161878548498329, 0.26346451928513, 0.0910398510019692]', 3.99); -insert into credit_card_transaction (time, features, amount) values (157692, '[-0.183148622511039, 0.422058503815131, -0.516862849799115, -0.383962158478075, 0.151738777463743, -0.822269913840261, 1.65138899283605, -0.276715199828938, 0.211899129402947, -1.75552713758327, -0.737879873289243, -0.0095584536728181, 0.0745717919974543, -1.75412351984956, -0.570204375017391, 0.116325118786952, 0.993393888293093, 0.300789476296047, -0.0874035566704642, 0.294071528076684, -0.073486376957242, -0.332171784408773, 0.354501767760105, -0.25990384183988, -0.208296139769667, -0.162983285771797, 0.0041081635285877, 0.0895003573280478]', 199.84); -insert into credit_card_transaction (time, features, amount) values (157693, '[1.91233520948932, -0.324378484828003, 0.164227910469996, 0.734400107600484, -1.19095366311683, -1.11349783853967, -0.524113727540421, -0.167828630380022, 1.2869620015841, -0.190254858100758, -0.0525446793200477, 1.20689500909608, 0.759856854735814, -0.303850812650683, 0.320556360171216, -0.225328133898422, -0.218471042023068, -0.193916287281662, -0.575815821194575, -0.199342982001773, 0.261943335858652, 1.04885485994248, 0.21988385835477, 0.978375380296514, -0.189089862695267, -0.273237983507367, 0.0481668651611472, -0.0254678895942394]', 9.99); -insert into credit_card_transaction (time, features, amount) values (157693, '[0.125100991330419, 0.950423596874578, -1.17866956423642, -0.172309894546629, 0.327005313828198, -0.908479920173051, 0.537657262722291, 0.304716455930898, -0.292246357853814, -0.95961065030589, 0.625694749541833, 0.0958006427745616, -0.765713934822204, -0.186833306118324, 0.0565127869880954, 0.539591403875453, 0.262691903845127, 1.419044357893, 0.155611662665759, -0.199875566720641, 0.369074488441783, 0.935643468552348, -0.0567391482100247, -0.430663423295707, -0.269341650630469, -0.148048808969989, -0.0892946229079656, -0.0593834545086696]', 42.81); -insert into credit_card_transaction (time, features, amount) values (157694, '[1.8824171634852, 0.28181077542448, -0.658378959822149, 3.77873271941921, 0.289269831813546, 0.33174689991353, -0.0824723599738074, 0.150085705177846, -0.617053992964987, 1.59836123512556, -0.36930754539064, -0.42565261984391, -1.83244552615348, 0.50593257542553, -2.02303895760946, 0.814524675848935, -0.776755179203492, 0.278722612394197, -0.924761156263669, -0.390652969232265, 0.0891931321422175, 0.318661261565337, 0.0474246692619383, -0.385665076256504, 0.0870804730004726, 0.102833912486932, -0.0422883043322641, -0.0670574842254138]', 9.85); -insert into credit_card_transaction (time, features, amount) values (157695, '[-2.80215418212591, -2.7869047683594, 3.05095292866455, 5.77386384485884, 0.543900927419285, 0.991193080329851, -1.06331364091894, 0.337127485308067, 0.211592026075768, 1.36870691945478, -2.00112935232413, -0.515544643619281, -0.163465836607423, -1.43403075730352, -0.880210876288146, -0.35056250383487, 0.518871086612674, 0.574731152620137, 1.63838276079299, 0.25950095398015, 0.0675068180942435, 1.0299061746425, 1.43175029691381, 0.0626079452730134, -0.145390386130068, 0.560129173734992, 0.116706439391599, 0.0672966499526748]', 225.11); -insert into credit_card_transaction (time, features, amount) values (157695, '[-1.20052857055263, 2.06956117066816, 1.54249848003991, 4.06951578807093, 1.25860204261309, 2.03720406920658, 0.552052338677142, -3.01239496262701, -1.31567440802053, 2.3349350253327, -1.34324328146888, -0.645751421787565, 0.628918275042762, -0.762605829356297, -0.0092085669427935, -0.861022299008366, 0.22885864455477, -0.0800248266179748, 1.61102522817546, -0.17411614573861, 2.20908849297382, -0.27551614012747, -0.186757237077152, 0.280788458678256, 0.6167612575981, 0.569937993867196, -0.631744374284451, -0.61848721961052]', 26.47); -insert into credit_card_transaction (time, features, amount) values (157695, '[-0.747566889023111, -0.295613044590678, 0.882569257490564, -0.706035712156273, 0.821425378758939, -0.590230373512158, -0.114434501357268, 0.112892588435563, 0.234506406967808, -0.313818526216938, 0.403412207573683, 0.695627020972726, -0.0336128745771185, 0.0433901711592438, -0.62721702731617, 0.63183043531218, -0.967881890778821, 0.163616578316934, 0.129621126114372, -0.23752482801854, -0.08164455039653, -0.216467221982534, 0.174573672927888, -0.390367704142342, -1.56236256646698, 0.0477902022226219, 0.153212355226033, 0.238237867403128]', 28.98); -insert into credit_card_transaction (time, features, amount) values (157696, '[2.06417245105617, 0.0262741413560445, -1.03884773307114, 0.4079913068747, -0.057143436448956, -1.19535537979282, 0.25232488558759, -0.392823157803556, 0.387873931111568, 0.0440001964101087, -0.560664490661123, 0.979468251405491, 1.07381064942554, 0.0744500547386839, -0.0717344300594716, -0.155365494320654, -0.34265608761729, -0.996828203769396, 0.136225708011739, -0.161735394712951, -0.277599973794466, -0.61413263534856, 0.329974491975154, 0.0739573445065143, -0.265017247208815, 0.192089639971415, -0.0639380514763603, -0.0578317776844607]', 0.89); -insert into credit_card_transaction (time, features, amount) values (157696, '[2.18011199902051, -0.967724081051841, -0.748450150433237, -0.480115308871446, -1.02003401418616, -0.78621548538612, -0.702128549715161, -0.149157424291228, 0.460352447510598, 0.570241443648345, -1.39586858789816, -0.45004450292577, -0.878760249058134, -0.570342484716198, -1.87734640083947, 0.102914335800314, 0.981895667468984, -2.11510668706233, 1.34123896003801, -0.156751045388834, -0.180766077125796, -0.228780803673372, 0.253092159972917, 0.0459570283641028, -0.0770882699502772, -0.322389684373911, -0.0041442743389422, -0.0617400328564477]', 0.2); -insert into credit_card_transaction (time, features, amount) values (157696, '[-1.29366289121131, 0.587144129527716, -1.35042368691883, 0.648442940807596, 1.89618071818851, -1.08198367652383, 1.63132894798224, -0.346551603364527, -0.14479418821806, -0.39343575126975, -0.791160942723461, -0.358248354941365, -0.315744544147362, -1.28862979086232, -0.746758716501068, -0.26220060990532, 0.764263179280418, 0.208719689162283, -0.547925953517981, -0.124505503733382, -0.0166214136605226, 0.540370064764475, 0.357230254063475, 0.540021236971326, -0.25432266022078, -0.741160901511905, -0.23563190886077, -0.0223299672875634]', 63.63); -insert into credit_card_transaction (time, features, amount) values (157696, '[1.93841191654503, -0.0320575662446708, -1.67337056033162, 1.45690097664205, 0.202849628664248, -1.25844508173853, 0.621296659079062, -0.371486643161649, 0.430576415026785, 0.288463450609331, -1.27738247303727, -0.511866166776465, -1.79594635881729, 0.847395927243455, -0.390959755352812, -0.604372103063895, -0.0900317558060284, -0.374819511558848, -0.160802636804536, -0.292130069530574, 0.0574357532573195, 0.1992480283501, -0.0333390889117323, 0.0121749010430797, 0.401185168579891, -0.495352328221495, -0.0374353128509507, -0.0559118612493898]', 59.0); -insert into credit_card_transaction (time, features, amount) values (157696, '[1.33473632275155, -1.58763016713364, -1.02569410650842, 0.25379149555934, -0.60670918266514, 0.418695737500983, -0.323887159554186, 0.112219099472646, 0.830097954281041, 0.0616573781019407, 0.485783072472291, 0.63753336878135, 0.0017892716595311, 0.206937164805428, 0.370246493890324, 0.887087221608036, -1.00079171386902, 0.749801976115915, -0.0851027861020506, 0.552745197711853, 0.405513593254625, 0.450194208084305, -0.134891957970788, 0.283332471483262, -0.451711809042609, 0.595549365392638, -0.10862618303707, 0.0028521698388558]', 352.32); -insert into credit_card_transaction (time, features, amount) values (157696, '[2.04927003795927, 0.0645459871560661, -1.78718097904547, 0.253351380515144, 0.534806548058547, -0.440764642262327, 0.0391582806908901, -0.0434188425893922, 0.333500167462273, -0.19235193646676, 0.834973459983887, 0.467074741710023, -0.533356824572028, -0.551618580822264, -0.418262060509893, 0.604388127883473, 0.172301639328543, 0.328836269841847, 0.431394192637855, -0.174735227201026, -0.334626914625222, -0.918488216701021, 0.306889095554294, 0.241179411844752, -0.27649559602302, 0.170119700673963, -0.070256665391431, -0.0442543947274936]', 0.89); -insert into credit_card_transaction (time, features, amount) values (157697, '[2.24170373812058, -1.52863710501241, -3.4612833931453, -2.5600712995897, 1.81676128890144, 2.91799989848321, -0.781818025309443, 0.494429218049466, -1.97660239439925, 1.66089819522385, -0.231850586381931, -0.820594427585329, 0.104303441874373, 0.250024620372057, -0.353087035685302, -1.36766811713977, 0.720521730222131, -0.58281455413751, -0.089708244745716, -0.271189305390558, -0.0092914716432653, 0.291892348510361, -0.0199695342571815, 0.73671683485852, 0.402799228519784, 0.125049499438836, -0.0375678278414892, -0.0730665772868435]', 73.98); -insert into credit_card_transaction (time, features, amount) values (157697, '[1.86144855003728, -0.501020929125723, 0.0549596879548766, 1.40273427561333, -0.999425755305492, -0.268013523351534, -0.820440233388664, 0.164347168969064, 1.48562432407977, 0.188318603823692, -1.46196729472281, -0.727340119860169, -1.79644608778964, 0.170632510266604, 0.871014666036483, 0.704854834050186, -0.709423486621322, 0.556651276208876, -0.612475852108047, -0.29092200430964, 0.0433333602301394, 0.114506287833258, 0.249650990037826, -0.207080312716461, -0.400872871230871, -0.745224754823563, 0.0580520191097033, -0.018903233862605]', 45.0); -insert into credit_card_transaction (time, features, amount) values (157699, '[2.06595479339357, 0.0258352306608864, -1.04936762856547, 0.402022192854845, -0.0445321214318404, -1.18081073680204, 0.252006807035553, -0.391389697539309, 0.389111429711548, 0.043941196376033, -0.58271399489019, 0.972864564057108, 1.08150507818577, 0.0722835449537861, -0.0702447135701681, -0.148147453356016, -0.352031438005429, -0.98898269971448, 0.145730000014999, -0.160706986392169, -0.27922424291845, -0.618519771489969, 0.326810791699705, 0.0487322437381688, -0.261533481723199, 0.193646095162812, -0.0639498189254104, -0.0580810858959237]', 0.89); -insert into credit_card_transaction (time, features, amount) values (157699, '[-0.961170410577063, 1.46309028803722, 1.55268031820307, 0.706805935270864, 0.0646371213022817, 0.387172836699019, 0.112217299785725, 0.612269678541625, -0.534185124288492, -0.56721604421424, -1.15682279530021, 0.301710940783101, 0.835034758440593, 0.0899018287136239, 0.778124678165192, 0.0454175960602848, -0.207433175509048, 0.161854416392188, 0.750948178679016, 0.135036325322671, -0.207667716509745, -0.553304877168348, -0.190083724179341, 0.531734095918253, 0.422462340667078, -0.663253222126027, 0.234961328071738, 0.0923940242556723]', 1.0); -insert into credit_card_transaction (time, features, amount) values (157699, '[-0.279928388173511, 0.619999449424073, 1.0263955819205, -0.774108084310589, 0.755108665204555, 0.163028610367474, 0.838642625359413, -0.290017832467131, 0.653121895901266, -0.718816860546924, -2.02594691432994, 0.208482450565325, 0.984389432875468, -0.768979064259398, -0.574619970502369, -0.229136135852393, -0.855672775632481, 0.332483267000258, 0.299359255306081, -0.0113001160687074, 0.18462568676788, 0.976093228855646, -0.540673687770616, -0.927323527217863, 0.144865132719321, -0.128357957598767, -0.116259451364477, -0.0727088097768788]', 11.85); -insert into credit_card_transaction (time, features, amount) values (157699, '[2.09160914546749, -1.00995910849633, 0.170358701536119, -0.511143276446138, -1.31283938833623, 0.213132734141014, -1.50670033281606, 0.123578973573852, 0.236397032186797, 0.774325899300191, -0.952294583534851, 0.487494504679989, 1.7394447803671, -0.766720773362034, 1.05419985509144, -0.617592834846138, -0.644177350116239, 1.58405288384592, -1.34422109876055, -0.474510581153769, -0.134397849745711, 0.250838889663636, 0.355760431002774, 0.633134365127095, -0.643505559237741, 0.587137754355221, 0.030688734760933, -0.0222320919878286]', 7.42); -insert into credit_card_transaction (time, features, amount) values (157699, '[0.445502455063417, 0.441206899343513, -1.79335577647796, -1.40354797680064, 2.47354163318364, 3.91100368654346, -1.21346890922391, -1.76790562414076, -0.302014202034587, -0.970749621933665, -0.16486069812016, 0.467767686109843, -0.577183678525144, 0.919730943022661, 0.101808491480805, 0.139439772650569, -0.379625121407643, -0.955146047631811, -0.578596722466404, 0.512102064758497, -1.57179172519841, -0.594307856310312, 0.221080387498554, 0.683637229979168, 0.444506583525832, 0.240451151026066, -0.0241958254843618, 0.132578177675354]', 1.98); -insert into credit_card_transaction (time, features, amount) values (157700, '[-0.778314826642336, 1.72993024502851, -0.258396338933288, -0.194311254990339, -0.235396433746089, -1.46162203083859, 0.280118106071631, -1.5696202959191, -0.292711537286667, -0.400571359085391, -0.0960459829911923, 1.16535437263189, 1.05619839496666, 0.609733662953219, 0.185129994042777, -0.6273019192378, 0.0783806681844065, -0.212377280792267, -0.342990248790862, -0.413938503064859, 2.01081816497982, 1.07253788522846, 0.245392046393652, 0.974014134536094, -0.806681699383522, -0.266518540670567, 0.463797042020522, 0.28760665923028]', 3.98); -insert into credit_card_transaction (time, features, amount) values (157700, '[-0.78919879993444, -0.416736346487244, -0.198520979218314, 0.295601470193659, 1.62379745452543, -1.43349595465227, 0.838249395999532, -0.295617070261255, -0.400770095019371, -0.528221660735976, -0.391921175426066, 0.687325631876845, 0.908058193895269, 0.466426331278157, 0.520970267329246, -1.262946574866, 0.0951342866481577, -0.213945184783983, 0.795968265045825, 0.515790162774528, 0.439677766775589, 0.966468728902323, 0.33650485198642, 1.12358219507074, -0.408354231592567, -0.547478581691296, 0.181221703758972, 0.269179293032383]', 107.82); -insert into credit_card_transaction (time, features, amount) values (157700, '[0.0011514535944527, 0.818830970460008, -0.519976151425248, -0.699751825899174, 1.47175169238694, 0.677274378599747, 0.67065343686339, 0.380221061881251, 0.0491134954626667, -0.956094893359527, -0.116736608730502, -0.224154958305256, -0.757007880549536, -0.786365964447261, 0.656533441434471, -0.575715704100711, 1.29931824790608, -1.57553761614036, -1.37576763279459, -0.125113324344339, -0.260968811700973, -0.527005321306448, 0.199131813851019, -0.46956343661467, -0.676830470551731, 0.21234956953088, 0.270672353221625, 0.0717518660745382]', 3.58); -insert into credit_card_transaction (time, features, amount) values (157702, '[1.43561776429555, -1.3229850129845, -0.755773567009584, 0.581365373818886, -1.03826421150534, -0.639218106554318, -0.17035808527814, -0.133353080917126, 1.49274302682945, -0.311605000441295, -1.08763506331906, -0.0711767339866791, -0.66714000262842, 0.248882390087451, 1.21212917781835, 0.281200129825784, -0.662662353415927, 0.587721112530432, -0.363962529740911, 0.314137403959905, 0.385398648755634, 0.600442790365861, -0.143226882583135, -0.117903415977569, -0.182467443650411, -0.583288776218958, -0.002304274966521, 0.0153667326756732]', 300.0); -insert into credit_card_transaction (time, features, amount) values (157704, '[1.7385205854353, -0.892089846408635, -0.61171539902473, 0.214020009687275, -0.64809072813626, -0.130109607815714, -0.459933969339803, -0.0454743695308479, 1.40011554525562, -0.302020291231469, -0.966504956944146, 0.74423598986218, 0.829572661663642, -0.337060700238174, 0.50280920824159, 0.143860563204753, -0.555491381748058, 0.14197372420986, -0.118810916136672, 0.156232377859632, 0.25915801212865, 0.676207405921907, 0.0247114373201877, 0.730089253803407, -0.16529347165433, 0.0790929660675291, -0.0114539596994003, -0.0099156687582736]', 150.0); -insert into credit_card_transaction (time, features, amount) values (157704, '[1.99804907676376, -0.416684210336494, -0.365259376344715, 0.377805253207431, -0.534316282093149, -0.144293305533666, -0.665363366175666, 0.0025426026534573, 1.16150414882564, -0.0183673557570947, -0.93648091444336, 0.635485381423196, 0.79948513793616, -0.402962625971965, 0.3256125017865, 0.328423299664652, -0.558640380306779, -0.0319153926594563, -0.242588483289539, -0.116674521623804, 0.160746311744555, 0.676485992930321, 0.185419554057296, 0.807383586807175, -0.235660871090008, 0.533024124211108, -0.0203608390533247, -0.0384520533541354]', 11.5); -insert into credit_card_transaction (time, features, amount) values (157704, '[-0.426755527272792, 0.87375703451161, -0.370965525053918, -0.589003899281591, 0.724109827828868, -0.723269029563194, 0.874320693865869, -0.13848863585707, 0.182387427672555, 0.343861184576477, 0.391221322670837, -0.0387654246370595, -1.08177341278863, 0.641555855325174, -0.0087081119607021, -0.22147022619051, -0.783225973136488, 0.773809334859463, 0.412408848820866, -0.125923283487632, 0.34690611903494, 1.38592177108063, -0.0552657054979906, -0.38776313500652, -0.846908272920435, -0.283997506332344, -0.19544936037924, -0.220173256802794]', 0.74); -insert into credit_card_transaction (time, features, amount) values (157704, '[2.0651932618778, -1.1133051640027, -0.758239557258063, -0.603335436981097, -0.83828658474467, 0.201902871002653, -1.10136637624334, 0.181306293096023, -0.0379267757307635, 1.02569626044458, 0.0044176153622693, -0.121250948052904, -0.808075289134137, 0.158309806432348, -0.172617925133499, -0.800373864732649, -0.578523463849313, 2.01955161007762, -0.521029078351097, -0.565877911842106, -0.236844112114906, -0.226516812459746, 0.238495073906172, 0.302224807766046, -0.417963928207025, 0.604016645213068, -0.0377025351992958, -0.0518885790613157]', 36.0); -insert into credit_card_transaction (time, features, amount) values (157704, '[-0.103933265119944, 1.209354932723, -0.184922929079553, -0.372333131208527, 0.459848115677374, -1.58685114955114, 1.19871437637383, -0.450368832499871, -0.0750559885514598, -0.958610109709599, -0.0902023786748899, 0.0879390448586052, 0.259108626471398, -0.98088058817972, 0.389091016453283, 0.0617705455616881, 0.393985628250334, 0.429674156855048, -0.787203815563598, -0.186923315765165, 0.337512051836843, 0.999047912364985, -0.236832940804817, 0.402073846782658, -0.0679695428062781, -0.231883308856569, -0.190325710529559, 0.0610671054534179]', 18.0); -insert into credit_card_transaction (time, features, amount) values (157705, '[-4.31903759961312, -4.70952140601699, 1.12861385827668, 0.165809424471442, 4.34356761635449, -1.10312534185627, -1.32330525494534, 0.129066260018699, 1.13256615166833, -0.750491642893958, -0.29813060910041, 0.0749898227300266, -0.394691095355937, -2.08329138349009, -1.57193690191482, 0.601866778905089, -0.100920505177039, 1.28916411282091, 0.0480174010419019, 0.7866861825676, 0.196861701589146, 0.36435493800736, 0.0714996313938601, -0.362333540144417, 1.1795929661975, 0.7653815873745, -0.79849911820282, 0.471025790525291]', 207.2); -insert into credit_card_transaction (time, features, amount) values (157706, '[-0.172747946874229, -0.686847233914274, 1.0979329031162, -2.7476623710753, -0.710979464903696, 0.0741178072164428, -0.409460945238638, 0.174863203750307, -2.10976277740161, 0.867873859150692, 0.361301831485438, -0.93862112277913, -0.549630150619263, -0.276569335761426, -1.03611097637925, -0.265241679910156, 0.187724736737298, 0.717054061915971, -0.126908933986245, -0.298806242899799, 0.0046350612985474, 0.292609682787268, 0.0099350907963515, 0.657560582393858, -0.425619050528873, -0.266950985248151, 0.147780630978695, 0.157570204601165]', 43.0); -insert into credit_card_transaction (time, features, amount) values (157707, '[-1.15127124158764, 1.66458167394539, 0.0036623437285402, 0.281059733350196, 0.766176110074641, -0.974487004641719, 1.3108231084057, -0.110230358770562, -0.34067950817555, 0.0981539401246805, -1.04580893562627, 0.0807023459056578, -0.177251947197928, 0.279813333502844, -0.71955623944703, -0.741838638083559, -0.106741312707914, -0.592163145852269, -0.330201816277138, -0.0049480675168586, -0.0506040216688384, 0.10542159431311, -0.590988271766406, -0.0094311608269884, 1.4745663927463, -0.377401698361993, -0.309128908701368, -0.324334208567247]', 8.97); -insert into credit_card_transaction (time, features, amount) values (157708, '[1.97527625198455, -0.846033656631418, 0.384854217421799, 0.461664204970547, -1.1624911894556, 0.570286567300143, -1.42871781139834, 0.311990905828702, 2.22191319028075, -0.210037546057482, -1.99492577390665, 0.343047323309382, 0.12381058831076, -0.997185684025171, -0.364299356008724, 0.405655211477404, -0.437684380170837, 0.226548018695815, 0.126005918082996, -0.201311797992715, 0.0826914072643253, 0.677138702809898, 0.136258248725671, -0.641000214449053, -0.389197549764031, 0.738866747537939, 0.0292859102214093, -0.0447056383655524]', 1.0); -insert into credit_card_transaction (time, features, amount) values (157708, '[2.06184246253878, -0.307976232912722, -2.78654770060288, -0.777438171502785, 2.49992452962194, 3.16343650481725, -0.400265931460217, 0.715920455141033, 0.448457069112648, 0.0332289061611847, 0.0620942339066804, 0.284944315835478, -0.144093725987975, 0.69457080684155, 1.16583098500761, -0.286162234455405, -0.586906588784058, -0.180099963058657, -0.35555288268917, -0.171442519139442, 0.241950060121122, 0.745452959879616, 0.0403287392996927, 0.737381238667803, 0.30397657803223, -0.0875596152439227, -0.0006145205229113, -0.0654411548550868]', 1.0); -insert into credit_card_transaction (time, features, amount) values (157709, '[1.39734563916074, -1.49666653917257, -0.926199928524948, 0.525728520799198, -0.850909798735179, -0.138555206704582, -0.26685816855018, -0.023938809782132, 1.17816731927994, -0.0525822713403886, -1.18375816544728, -0.402384860858606, -0.74122517503701, 0.178581124984375, 1.07883934888212, 0.703397934828545, -0.685042233601466, 0.380328547428183, -0.42274061021906, 0.447771864063222, 0.349256391843608, 0.319950582970188, -0.0783430406347819, 0.61035570287275, -0.435303728163138, 0.482615948332673, -0.100702291734328, 0.0131511758867353]', 334.6); -insert into credit_card_transaction (time, features, amount) values (157710, '[2.23340713356783, -1.37192147951363, -0.662185960901715, -1.44407956818064, -1.43500442015814, -0.859502029018155, -1.07227743807364, -0.290630911200932, -1.25809865207345, 1.51204896355531, -0.784033967454927, -0.179726731694636, 1.4968017288422, -0.69797890293753, -0.1939576162424, -0.609519131014492, 0.469094690059027, -0.267291828769043, -0.27401873807399, -0.335068902867859, -0.126926224341511, 0.177992740497453, 0.229271651581459, -0.0028900285861614, -0.240273710372358, -0.180751787218209, 0.0272440540426017, -0.0408419406325964]', 37.5); -insert into credit_card_transaction (time, features, amount) values (157710, '[1.85452083383711, -0.367229672173789, -0.558217212120127, 1.33917841078138, -0.283128657092972, 0.139370569410365, -0.424068366305265, 0.106446578742232, 0.858487586130464, 0.280552945921937, 0.182855318680134, 0.944450381959201, -0.22312532242186, 0.0478652831009078, -0.996492409062232, 0.0758328868565778, -0.663522768999519, 0.576392185708508, -0.0762727275393594, -0.191770733428559, 0.262117485114304, 0.95653564613792, -0.0491150454648378, -0.379492678973047, 0.157120066641214, -0.42739859258935, 0.042324453427808, -0.04737974698263]', 44.9); -insert into credit_card_transaction (time, features, amount) values (157710, '[-0.608063721908058, 0.912658989359966, -1.68698466830791, -0.564921834768725, -0.637321632710033, 0.243791128636799, 2.74647743630849, -0.469374315370517, -0.086448018987414, -1.28725128196881, -1.36157896621658, -0.483980069363268, 0.610044103662388, -1.12179091923494, 0.582617926322752, 0.755898538812674, -0.0710757460065687, 0.987920106520247, -0.260664740090848, -0.413269369007386, 0.0956233176669785, 0.768612521503877, -0.166612682928383, -1.04555472127441, 0.197047268656897, -0.14606771205411, -0.195285666404242, -0.383768351332183]', 413.35); -insert into credit_card_transaction (time, features, amount) values (157711, '[1.79060927605258, -0.824626627039168, -0.865917190186543, 0.0593884655954448, -0.27868277420771, 0.315084217011918, -0.505078132646384, 0.179579729708479, 0.892411692830389, 0.129949155600898, 0.222490317106021, 0.466814111042066, -0.534066282682201, 0.2575576340882, -0.0028567739821765, 0.868357420215412, -0.958437692105656, 0.34433087643255, 0.473005535827133, 0.0623647849020547, -0.196232031434245, -0.834103924999991, 0.292884659055228, 0.0641609509919477, -0.641114450737523, 0.208286826622184, -0.0757212673419488, -0.0342823253482439]', 125.0); -insert into credit_card_transaction (time, features, amount) values (157711, '[1.74875641304087, -0.246317377803323, -1.68177109880674, 1.39865404499842, 0.265813314546957, -0.846160902738432, 0.65625034802818, -0.270986067387164, 0.0234580983533103, 0.390881552808123, 0.689536727549109, 0.690268603022208, -1.00964229258842, 0.895489604908673, -1.13696356557025, -0.520512204403745, -0.304995946477436, -0.0862909072981619, 0.0004766380394439, -0.0932346926990085, 0.171824048462299, 0.384562417920369, -0.101023210021308, 0.0113124810225169, 0.350266467128189, -0.522651514896705, -0.0452836003006522, -0.051998269987168]', 127.95); -insert into credit_card_transaction (time, features, amount) values (157711, '[2.06290940705931, -0.0985073059880559, -1.0746602463899, 0.424728512536093, -0.21086073160664, -1.23633508011534, 0.135467223548292, -0.294969951159633, 0.648673869006551, 0.0945891140263695, -0.854399467747339, 0.063420287986235, -0.648607000104597, 0.417232525936087, 0.117244314898733, -0.0745703815737302, -0.244229578582521, -0.785508254889115, 0.147854764196914, -0.27209158146865, -0.297403599398062, -0.755194705224479, 0.350648591297059, 0.026799041861862, -0.323296181699123, 0.197861583694769, -0.0751979158080538, -0.0615309766170867]', 0.89); -insert into credit_card_transaction (time, features, amount) values (157711, '[2.01469705680187, -0.0822841976412569, -2.06152853530467, -0.0010730450306624, 0.357811866172648, -1.48654202579575, 0.68098509760149, -0.412160117626251, 0.0728540232536272, 0.195227874227898, 1.04494421308875, 0.504948037007703, -1.12521918830388, 1.21392985109342, -0.183097468845714, -0.485913916504491, -0.360351800404257, -0.0518389907986588, 0.286273256842784, -0.240041823051097, 0.219247260541673, 0.638572335051831, -0.0531043384039009, 0.0536513585829779, 0.339733673069798, 0.220614263234906, -0.0983463906170769, -0.0853045674918219]', 34.36); -insert into credit_card_transaction (time, features, amount) values (157711, '[-3.41224398629134, -1.15634281487342, -1.1205957337612, -0.69389202939277, -1.773805847031, -0.94935266737923, -1.32371444761529, 1.38071046304581, -0.953248914776966, -0.547431335206752, -1.68187956043691, -0.267408074185896, -0.191121431983989, 0.778995155445901, -0.452838870960759, 1.36880023809223, 1.15791831104715, -1.5421865482288, 1.23568931295594, -0.801838038511645, -0.0341809209545198, -0.545396052728094, -0.713613503038132, -0.0061310096297611, -1.23002879473872, -0.636972710381375, -0.197637834622837, -0.651053013080145]', 171.88); -insert into credit_card_transaction (time, features, amount) values (157712, '[0.0989804200521075, 0.853935639147031, -0.236748881500504, -1.03244010873371, 1.22347408047963, 0.0052854001696519, 0.947374030212105, -0.0304612684121577, -0.364118609574942, -0.269466042620061, -0.278048124214595, 0.916028378088138, 1.18237770942887, 0.0243347766881615, -1.1153730129135, 0.454286229560289, -1.21473713886705, 0.0682362047219116, 0.714442535680621, 0.0808983492522834, -0.287335225251765, -0.627481003337142, -0.125146296898898, -1.36496809782038, -0.302565519024166, 0.202264965204935, 0.248370739635226, 0.0755224212851905]', 2.69); -insert into credit_card_transaction (time, features, amount) values (157714, '[0.799779680799791, -3.23074821302674, -2.28827118797305, -0.432111858440381, -0.93011288573827, 0.0287034662815749, 0.372309467041956, -0.42399801937249, -0.322418073218563, 0.413262899668769, -1.37974737180012, -0.267197050486103, 1.46891159565811, -0.39543282007967, -0.0222218945499173, 1.07631048062601, -0.0624082406389561, -0.798928566142645, 0.47905229608856, 1.62047576291256, 0.993055037648376, 1.30520536588664, -0.845388765729923, 0.193618863485148, 0.171818739730658, 0.176384445243742, -0.15874447596759, 0.069465861141501]', 770.0); -insert into credit_card_transaction (time, features, amount) values (157715, '[-1.15151070369599, -0.261184827920678, -0.54378669207494, -0.949367828432265, -0.220211373747866, -0.457553307770774, 0.392175117118435, 0.212560057251552, -1.38168995534741, 0.37997212568231, 0.149402385483674, 0.591435861213132, 0.678457976847136, 0.549668547885028, -0.476683180184847, -1.62122837242952, -0.295232680743798, 2.08670492351661, -0.327476871922351, -0.426557273072765, 0.129674271633178, 0.897669401514398, -0.283329037528044, -0.345531298927138, -0.412491704871043, -0.0617587000550677, 0.371075362656271, 0.0273624101654262]', 153.0); -insert into credit_card_transaction (time, features, amount) values (157716, '[2.13960676437663, 0.116314882509173, -2.2819265844949, -0.0810395758204253, 0.780268441848252, -1.2559407469731, 0.748974820294992, -0.496697118637002, 0.191627186788527, 0.0042763925631347, -0.697814341080745, 0.216595363613753, -0.0213330778164765, 0.790909074054887, 0.467679897828941, -0.641676011049803, -0.267535820278356, -0.565824539282567, 0.0917940032598002, -0.210624964856565, 0.141256955030142, 0.524243514598024, -0.017205946916704, 0.662285358564918, 0.460442001483553, 0.216937072860783, -0.0880022269442688, -0.0732120136568121]', 5.39); -insert into credit_card_transaction (time, features, amount) values (157716, '[-0.687395606131064, -0.811675380751586, 1.96335985136443, -2.49551595277461, -1.08395152683164, -0.558470787697042, -0.319757055166589, 0.0239113583875154, -1.9842499166027, 0.514947782574436, -1.30669011925027, -1.68552629451437, -0.523964589558547, -0.502899145340056, -0.0520431138770475, -0.248509036206257, 0.429806543402249, 0.160811730261178, -0.0538654149151179, -0.129093071018592, -0.359877068987999, -0.912692432213343, 0.0477705694209291, -0.125178925383502, 0.199419520983958, -0.344678143816341, 0.0272673024529501, 0.0726550050875739]', 77.58); -insert into credit_card_transaction (time, features, amount) values (157717, '[2.05486050579718, 0.608076882322144, -3.17165341934081, 0.607542320367241, 1.27546110085412, -1.27431158894089, 0.676767124479248, -0.28872995640852, -0.16685232294003, -0.87156134900345, 1.7361261023187, 0.143819087736308, -0.795832813327849, -2.11951531861391, -0.603184155860302, 0.478242839221899, 2.02264315294918, 1.00410596120624, -0.0943829193737678, -0.153632704118881, -0.015600559170337, 0.109013569114515, -0.0383344695606118, 0.521952099439397, 0.335767226064341, 0.666045537228504, -0.085322148836183, -0.0242474384588215]', 0.76); -insert into credit_card_transaction (time, features, amount) values (157718, '[2.16196377374032, -1.4400880269279, -1.32130950188601, -1.6702221988234, -0.979168406378975, -0.725135505574236, -0.744821420680758, -0.218022576862253, -1.76248322081587, 1.74926473957811, 0.609433797135966, -0.245613014986551, 0.21860596828053, 0.0800327582767899, -0.684628808297289, -0.374112064338889, 0.201473937162067, 0.0517300210665242, 0.307962485896333, -0.307618793782886, -0.345554440726133, -0.743668220021808, 0.275319264420758, -0.458215897296709, -0.35406018325631, -0.42570115101492, -0.0229948358790974, -0.0541570166511318]', 81.5); -insert into credit_card_transaction (time, features, amount) values (157719, '[-1.74480963314594, 1.12726528430504, 1.09339226637976, -0.836972192575929, 0.0770859331943493, 0.72414609078766, -0.575297902802208, 0.0673251672315034, 0.0064165241038514, -1.17401423913389, 0.525389633115017, 0.412649204532315, 0.0773802499643181, -1.16002150261714, 0.00369476765225, 1.38484404901185, 0.120386476087222, 1.30701852894999, -0.11005286556072, -0.127813990336697, 0.729634494678331, -0.427830768772551, -0.26300066131953, 0.133098437885287, 0.336508329003728, 0.469959801403511, 0.0852398532309527, 0.0573786524842632]', 27.02); -insert into credit_card_transaction (time, features, amount) values (157719, '[-0.621228353327562, 0.624285992459337, 1.40357257893024, -0.188062673974394, 0.762374543008539, -0.598681506226526, 0.460633794708798, -0.25527526139679, 0.0129298142927818, -0.863764865688883, 0.0524628174940379, 0.547316935854133, 1.42623370880741, -1.87387754478618, 0.453129739791528, 0.343257295132107, 0.427527456785175, 0.0390628398482318, 0.0294508887580651, 0.138188538767803, -0.213793104385074, -0.382271377209789, -0.02378105518954, 1.08981290907663, -0.663834862340947, 0.125313910780929, -0.134568347558121, -0.0230695956062728]', 7.5); -insert into credit_card_transaction (time, features, amount) values (157720, '[1.87534467196814, -0.386283525782356, -1.73251581845083, 0.564615543003386, 0.00078828664008, -1.38584969024051, 0.616833862423922, -0.462968354299756, 0.456580455626643, 0.0760778997072881, -0.976862974506888, 0.0399588558403184, -0.698534820557059, 0.555526913628406, -0.369302212248358, -0.530012881497882, -0.0303145275859608, -0.756133405018857, 0.163803364331775, -0.0529892035259458, 0.0252536741780427, 0.0110866789883752, -0.01139968605374, 0.0974125841804097, 0.117138482703166, 0.548415462399308, -0.121336716684905, -0.0587772356392768]', 114.99); -insert into credit_card_transaction (time, features, amount) values (157720, '[-0.137070397494505, 1.25698306906897, -0.822171092975724, -0.0507160720804286, 0.498625466200844, -1.40223614741898, 0.839519597149465, 0.0057310439489969, -0.53563836934527, -1.30220621077778, -0.376733780561397, 0.71488289694574, 1.6049256330661, -0.89988367398027, 0.478850845857217, 0.114826762717223, 0.600108245416873, 0.598087809678427, -0.207522455345316, -0.0461676360383316, 0.385814581558305, 1.13069404737389, -0.112240056504673, 0.015275852447655, -0.303029101762543, -0.163807832594741, 0.0242145500945038, 0.0630719288032126]', 31.35); -insert into credit_card_transaction (time, features, amount) values (157721, '[-1.63625299258691, 0.18503739257352, 0.630537397018246, 0.342683922397841, 3.05213921295319, 0.944062070973113, 2.13470692271031, -1.04603666123174, -0.232853622191206, 1.65431531065051, 1.40933146862519, -0.429179045224098, -0.88915230197759, -0.164246412962473, 1.04559726919722, -1.76384070132233, -0.232887162383348, -0.885468461010331, 0.323687933577685, -0.0809489242731169, -0.172936767851691, 0.869627246708934, -0.280258508832121, -1.63249946894961, 0.986196885860962, -0.225896634445978, -1.20041479355825, -0.69416222803821]', 1.0); -insert into credit_card_transaction (time, features, amount) values (157721, '[2.35224064673288, -1.24037467615084, -1.3148670520734, -1.75256118002705, -0.621643905937809, -0.16410659540082, -0.976440352585635, -0.223861532002158, -1.40291655909312, 1.52811722297215, -1.37001896173841, -0.324703648573353, 1.86838678825219, -0.704246399702155, -0.226962866015586, -0.520779340370145, 0.226069139015708, -0.143013760719467, 0.0638645446621194, -0.315189119534527, -0.199750330303654, -0.0074321677632379, 0.169072197896752, 0.043259563711516, -0.0250523177470883, -0.152650500287121, 0.0149259061887248, -0.0491188837030023]', 15.0); -insert into credit_card_transaction (time, features, amount) values (157722, '[1.96612703219513, -0.253117465158608, -0.900693154517135, 0.138114281612753, -0.351624445304147, -0.32944868351956, -0.677134280490194, 0.212898699560324, 1.21981683998678, -0.502937296029852, 1.06255855136034, 0.194328068382722, -1.45382274336648, -0.827850694646634, 0.300911725673378, 0.886717056639332, 0.269383643435912, 0.95683251264678, 0.147620682377368, -0.237755602273712, -0.232869634876307, -0.639475941132792, 0.419241966330101, 0.592417743229822, -0.562535830679615, -0.333907151781833, 0.0074331860198138, -0.0157852338409259]', 0.69); -insert into credit_card_transaction (time, features, amount) values (157722, '[1.95375607836766, 0.208453312769648, -1.38612953073745, 0.762022764930532, -0.226580458208283, -1.95441439845862, 0.316022890373539, -0.440065071390843, 0.501610081971117, -0.535540136948992, 0.546542253716424, 0.431528432416829, -0.0236207837854936, -0.798231176974282, 0.877639497850422, -0.0849552462026133, 0.84652490413895, 0.153834193265087, -0.859052061640282, -0.195836123234694, 0.265465312759295, 0.915349515684248, 0.0776750935802523, 0.803664389673934, 0.119128893702822, -0.155292044684752, 0.001039654412365, -0.0172745351522763]', 19.89); -insert into credit_card_transaction (time, features, amount) values (157723, '[1.88306046955227, -0.142990985836102, -0.24634313570694, 1.18354323212451, -0.287394228437906, 0.0042976561488086, -0.405181766596472, 0.0493816253524111, 0.509614968574173, 0.284344795506559, 0.686133511252082, 1.60096000434168, 1.18420653539479, -0.067904883853454, -0.365663834377753, 0.609023892298469, -1.09161337575391, 0.325901154568324, -0.0634538563040106, -0.115409496495687, -0.111626716315311, -0.221475587175289, 0.282086983367242, -0.377408422015035, -0.333193008279494, -0.945847537368155, 0.0569227736079488, -0.0309590263476874]', 30.0); -insert into credit_card_transaction (time, features, amount) values (157724, '[-2.60358387513266, -0.169809858854303, -2.33526433253578, 0.090731200255946, -0.0903069094103162, -0.620193663359932, 2.24707625757752, -0.460744914136078, -0.492196569237174, 0.81591512685564, 1.03441930448471, 0.732297276928022, 0.657380500257113, 0.896921654903982, 0.856432775557493, -1.67446008643761, 0.68919577795076, 0.169969270707623, 3.69560303475718, -0.464350760990591, -0.0381503117327855, 1.50279391273233, 0.416231898221532, -0.183821737121722, 0.64549520214581, 1.53625234150466, 0.154956667416565, 0.851587915478829]', 291.85); -insert into credit_card_transaction (time, features, amount) values (157724, '[0.723360752807003, -2.5006202500231, -1.1272152166997, 0.906035703967983, -1.25003730663717, -0.0107589032435551, 0.154625195446812, -0.0647829644728568, 0.817117518756416, -0.0127280791944832, 0.482318179552464, 0.511091711191271, -0.391017689336516, 0.391254497037432, 0.325454421233024, 0.918887684982675, -0.98758000000924, 0.937779728883236, -0.264996163739257, 1.10162581431703, 0.605630955457955, 0.352784547002129, -0.436919060781049, -0.283796328404269, -0.534874213647396, 0.289655548216429, -0.144085425228715, 0.0543768383394672]', 656.0); -insert into credit_card_transaction (time, features, amount) values (157725, '[1.97245874637588, -0.311235658811216, -1.13765858487174, 0.385527717274655, -0.246557106389888, -0.875436518945476, -0.004623364600778, -0.0730387120821265, 0.599334852411939, 0.266414118645752, 0.674123090070554, 0.183700143770359, -1.99069223219973, 0.829735061147462, -0.675110329327211, -0.0590788516441056, -0.242419820694527, -0.281789626339758, 0.399815628301293, -0.316908337738648, -0.198612595258991, -0.553933597218353, 0.29739483161382, -0.0180660779202193, -0.319900024206561, 0.261274573246603, -0.0890224826893526, -0.074460103514258]', 18.58); -insert into credit_card_transaction (time, features, amount) values (157725, '[-0.363514016478533, 0.177053591125749, 0.0119787509115585, -0.654651934101109, 1.74636972698075, -0.577205226160921, 0.732924569627344, -0.147999997205572, -0.328573023544414, -0.678551327187185, -1.4310647524126, 0.634034242041797, 1.50822815607622, -0.188387541197768, -0.684226452633179, -0.102837961548682, -0.634986148957864, -0.644916980359248, 0.312134555344841, 0.170493961997501, -0.15719425672895, -0.537560578490608, 0.0416626985177572, 0.129975425106479, -0.113123378375355, 0.231346216192709, -0.0254380704660875, 0.0552388238530175]', 19.24); -insert into credit_card_transaction (time, features, amount) values (157726, '[0.878917111334939, -2.03105550284133, -1.0214159002434, 1.84564504009851, -0.889114340972146, 0.367653194121688, 0.220444743696283, 0.125665856615481, 1.1612700663397, -0.0401190716268564, -0.163421181559374, 0.358867873321331, -2.02238933237572, 0.408486005834388, -1.57356771772049, -0.0353815386718897, -0.238734131927209, -0.0330241836447003, 0.396363947137648, 0.681179274924865, -0.188443387922137, -1.56621113018766, -0.0531936586490342, -0.504442290001158, -0.578608602432217, -1.1495294333512, -0.0588114995148, 0.0441367849168055]', 538.34); -insert into credit_card_transaction (time, features, amount) values (157726, '[1.91326975684577, 0.380030166067447, -0.015331785212037, 3.68769972344405, 0.218804311904375, 0.820744149532908, -0.46075938563668, 0.130471223516256, -0.480682277600088, 1.33704065314851, -1.47307500188708, 0.354497904649734, 1.46298044027489, -0.542150052295379, -0.699154114868896, 1.20037593508322, -1.10887513397289, 0.0158273540369675, -1.6223648785, -0.170714747967421, 0.192106576834552, 0.719308000405804, 0.142393587394244, 0.491913339925366, -0.0711378521400837, 0.0524469483587269, 0.0172914589640989, -0.0272715204093887]', 0.0); -insert into credit_card_transaction (time, features, amount) values (157729, '[-0.135079150460308, -0.312016260053282, 0.32433692486917, -1.99219567343341, 0.0253562877594597, -0.638739490716042, -0.304824885852845, 0.217916859014027, -0.58002394823955, -0.592081314606204, 0.413093287112477, -1.47092519501279, -1.71797833081587, -1.76140179604005, -0.844481461544482, 2.33758961879167, 0.749342245065009, 0.477205584569342, -0.0770147909790259, 0.0236968959713333, 0.291533244084363, 0.60327816471401, -0.204558674856077, -0.656885219622128, 0.127082276128294, -0.160031518660253, -0.0348545419555524, -0.0287506589618432]', 29.95); -insert into credit_card_transaction (time, features, amount) values (157729, '[-1.18220190362248, -0.0257817403316293, 3.18345036000015, 0.534384530963497, -1.40506692714005, 1.40711225131491, -0.93655540513724, 0.490005511322857, 0.903806171820324, 0.991590472112133, -1.8486838999968, -0.628378535219499, -1.10835989254774, -1.5796298923857, -1.08481573031768, -2.40679428713002, 0.737113749818529, 1.72873040730758, -0.0377727446071612, -0.0096650774240008, -0.401191505256539, 0.195966139211129, -0.298086923915537, 1.04636675893324, 0.477508551473099, -0.21721721020614, 0.502342147650274, 0.114366489452585]', 33.0); -insert into credit_card_transaction (time, features, amount) values (157729, '[1.86066292512071, -0.060755652031458, -1.15116404660628, 1.10595588795016, 0.597370028072106, 0.484111521979414, -0.0562541298916691, 0.148761300708121, 0.350064269840274, 0.176097055722756, -0.174499668337228, 0.316006448366575, -0.260018955524417, 0.587528898130697, 1.46145696582153, -0.837498533998299, 0.232027835375126, -1.41997535424651, -1.75208375550648, -0.358817633771942, 0.267541085459971, 0.964837804430686, 0.149570845986861, -1.04992465275647, -0.061440261679628, -0.434968541876182, 0.0604239207426233, -0.0565229157493051]', 16.5); -insert into credit_card_transaction (time, features, amount) values (157729, '[0.0430874744276437, 0.539609416337611, -0.206477371587916, 0.626103030723981, 0.0535734494235362, 0.420719466519524, 0.0194679208874651, 0.667442584700973, 0.375197638166617, -0.537318696250242, -0.240778798205031, 0.886039532380885, -0.736713495098328, 0.139043945391565, -2.53904541056196, -0.364136969855616, -0.175893336013086, 0.483131627821184, -0.234845531635576, -0.363992028753882, 0.460313492158909, 1.49047034113996, -0.0307372184666412, 0.733725597282153, -0.251439728448248, -0.461292835372154, -0.0205447788505751, -0.0465251563198618]', 33.72); -insert into credit_card_transaction (time, features, amount) values (157729, '[2.05392096730951, -0.485047366996594, -0.421359083552135, -0.567622981936313, -0.788849048074937, -0.850626070675346, -0.576219174661075, -0.17757858193835, 1.27368729638622, -0.286011765480416, -0.534563377854391, 0.894949803577948, 1.21925877666629, -0.290118178572024, 1.027042043393, 0.262284908789234, -0.557635011113767, -0.176165265043644, -0.0678702266938465, -0.111311705226173, 0.17856957630193, 0.755374978682321, 0.18581094850823, 0.021985553249536, -0.358174437579049, 0.905651567245641, -0.0422885503453461, -0.0541647381111306]', 1.0); -insert into credit_card_transaction (time, features, amount) values (157729, '[1.99046452117127, -1.55935315707384, 0.700413255597818, 0.039152107808816, -1.93450523802281, 0.909402373194253, -2.03394680766219, 0.429783747591807, 1.56562500166477, 0.458970546925944, -2.09209777217274, 0.311779096581483, 0.023306107902371, -1.45718615019637, -1.58093429602757, -1.60340142061484, 0.337737270295003, 1.32106370468724, -0.499558191726185, -0.579002925987237, -0.296019127949799, 0.11348467052891, 0.280493054138748, 0.776709265077171, -0.49041054453738, 0.70745646506897, 0.0592593016328184, -0.0231873729001059]', 22.5); -insert into credit_card_transaction (time, features, amount) values (157729, '[-1.25454842323468, 0.941880830428566, 1.52544457153431, -0.455663961649055, 0.160774639369195, -0.0194390053972152, 0.0742802108485071, 0.461239401103857, -0.212571015686181, -1.17288418247576, -0.94364535973804, 0.829411037380491, 1.4691697707377, -0.112771398277907, 0.565695675474951, 0.0660019687455348, -0.338204514221051, -0.031531892366414, 0.493315191054204, 0.0066155181174142, -0.110423117219574, -0.45272172129073, -0.159572853270231, 0.664744134165357, 0.406529115273121, -0.460189311363227, -0.178883034994962, 0.0134448350286316]', 5.0); -insert into credit_card_transaction (time, features, amount) values (157730, '[0.0839327239416598, 0.683834233418001, -1.67189102298397, -0.699370715900215, 3.44092275887205, 3.45003343802652, 0.91268547303285, 0.752580964206175, -0.657073893458826, -0.417852972972361, -0.474010861608173, -0.0497824462278548, -0.56034955603052, 0.616863925698081, -0.586137546968355, -0.723522687660101, -0.385048316612948, -0.542379653671504, -0.477242003478422, -0.132839107203323, 0.129922194016745, 0.366524131873971, -0.176436163791804, 0.697153406778408, 0.265947243636046, -0.546936375418111, 0.067196823917898, 0.0282515202634015]', 30.0); -insert into credit_card_transaction (time, features, amount) values (157731, '[0.1995078300392, 0.0481512741523652, 1.17324213839455, -0.54029192338214, -0.648228375847309, 0.0184542537667365, -0.56417786027129, -0.578783758329997, 0.638947251460841, -0.454726480564647, 0.500445620656671, 0.203219858451849, -1.46238988012587, 0.275061180240275, -0.469610560114083, 0.590888191075948, -0.619956271325532, 0.584559796668071, -0.451570470637419, -0.345685919619283, 1.0168020784624, 0.493985120670883, -0.240630898609875, 0.0554064859293341, 0.523822333637476, 0.791519563884681, 0.140001719244784, 0.226700364449408]', 17.95); -insert into credit_card_transaction (time, features, amount) values (157731, '[2.0507343376702, -0.364009836098765, -2.54284268573131, -0.729357245980242, 2.38845513580892, 3.318014826674, -0.479137991434965, 0.791558699015965, 0.403373925785878, 0.0932322962383584, 0.0043906366679776, 0.390166351524176, -0.212555237880757, 0.495543631087541, 0.472633049946723, -0.171594983536925, -0.368260801549244, -0.9765716778526, 0.0228694906165437, -0.154977952804284, -0.293362990419633, -0.834801882577702, 0.3817404029926, 0.698179490919462, -0.264798455762679, 0.219274980948542, -0.0521311602761514, -0.0669400159437571]', 0.89); -insert into credit_card_transaction (time, features, amount) values (157731, '[1.53136924224897, -0.310048806894437, -0.287028298184486, 4.1018803345775, -0.276467100656946, 0.477880622857202, -0.137269115972041, 0.177404812366639, -0.360909919670694, 1.42106600905119, -0.111357941324161, 0.128795464003687, -1.33697038752733, 0.135516083601316, -2.5196368719342, 0.616586085476324, -0.534845988771062, 0.159013162523121, -1.05393044119171, -0.0651423817638953, 0.224870428483919, 0.487209990936455, -0.0476636286017667, 0.0621805674558569, -0.0352279747441221, 0.0960941777092523, -0.0439263310190957, -0.0308992979487691]', 160.55); -insert into credit_card_transaction (time, features, amount) values (157734, '[1.53265689060905, -0.835334625878746, -1.33941998281553, 0.773041324756797, -0.302611341229472, -0.98896900195442, 0.569470538780951, -0.39708183007438, 0.41550962555782, -0.047659427057156, -0.62971105879701, 0.709196057718519, 0.547269389829577, 0.26434253095437, 0.0567838681342358, -0.160474675874906, -0.27168188006874, -0.916221387420979, -0.070981231542146, 0.329230836593397, -0.110218889804579, -0.756679478084036, 0.0992391241467011, 0.0024401347059051, -0.356666846853665, -0.0099416276904652, -0.101002089506618, -0.0104919701237508]', 267.32); -insert into credit_card_transaction (time, features, amount) values (157735, '[1.63444532898914, -0.0197332889722868, -3.12483542444803, 0.685038292748997, 0.896015232235635, -1.19555554770097, 0.753084895225058, -0.32752091878664, -0.192446738558224, -1.15091655004252, 2.06903756625235, 0.543792983936332, 0.276692563921989, -2.71468245230332, -0.161956353283861, 1.0419110466672, 1.98903855929372, 1.33303264881618, -0.279098148144303, 0.375653482384625, 0.0550718321301796, -0.188810866533899, -0.129843576750681, 0.568200212000063, 0.0150531596875646, 0.616775933946625, -0.107157415777995, 0.0326259473521055]', 212.5); -insert into credit_card_transaction (time, features, amount) values (157735, '[1.96109203223554, -0.528540137753307, -0.325544351035125, 0.224395381762427, -0.652669097723302, -0.106513722314842, -0.744279728802693, 0.0757925003794997, 0.99019154416596, 0.151985502949062, 0.49836346179682, 1.14690496931221, 0.532910843380806, -0.178796555657022, -0.366146632611469, 0.581938741253262, -0.857587282177263, 0.573283930199769, 0.144008717332649, -0.126042543165891, 0.251555975560306, 0.932609360914507, 0.0708070497223712, -0.249851283209036, -0.232101252490165, 0.739863104208191, -0.0305572209268441, -0.0609666562208546]', 15.99); -insert into credit_card_transaction (time, features, amount) values (157735, '[-0.235250554447461, 0.906977219661731, -1.94102702441792, -1.48449225641093, 2.75372616762362, 3.22248526596363, 0.0538252946420797, 1.24510812037575, -0.325851713944973, -0.483693641675058, -0.331382826136371, 0.0763572678096864, -0.191125772385029, 0.955619825184088, 0.846018111597045, -0.390388122008495, -0.386121326856718, 0.0868194108651453, -0.0898286001610832, -0.0660120592086755, 0.406731389793851, 1.19745494433062, -0.0439231917210208, 0.738458428989925, -0.73324634393742, -0.196474887371765, 0.404481030693308, 0.253279151880028]', 0.81); -insert into credit_card_transaction (time, features, amount) values (157736, '[1.81312385735221, -0.323789478825606, -0.609382898331711, 1.06485127359648, 0.0294101757378602, 0.533087326374859, -0.394093458833934, 0.18524075531703, 0.541328562945295, 0.274353850807245, 0.499172146392197, 1.16182872490071, 0.510330714660039, 0.117324054018575, -0.199994186427157, 0.625323067452073, -1.052620444365, 0.333366185066629, -0.0426189679779955, -0.0432900869287134, -0.148302521511433, -0.508033618773403, 0.29720407996019, 0.153994711429784, -0.360920244871966, -1.00971462654726, 0.0371480590935368, -0.0205512077274272]', 74.4); -insert into credit_card_transaction (time, features, amount) values (157737, '[2.34191332652484, -1.29235434188819, -1.1663059791641, -1.64409219156798, -0.949666254165145, -0.506931627177632, -1.04191844161893, -0.20527747904874, -1.24518596371226, 1.57382420240768, -1.27115821133562, -0.855970489346708, 0.594317678474899, -0.446903717398437, -0.17153902421874, -0.522073955042361, 0.382970217363685, -0.0365801921350226, -0.0042291025315354, -0.409341603008864, -0.196424976896256, -0.0541493273329784, 0.230359988947385, 0.458440405325111, -0.102030196988762, -0.177833530601189, 0.0053239410753613, -0.0479942748179367]', 10.0); -insert into credit_card_transaction (time, features, amount) values (157738, '[0.0418645194958908, 0.92642488095757, 0.312047533160246, -0.602698595487112, 0.514088007938104, -1.02275759922992, 1.04823208390876, -0.227917837461599, -0.16951620507941, -0.441848430369793, -0.73626984921396, 0.594195376622629, 0.826146092782206, -0.0456577888195562, -0.446203737272811, -0.190700467636849, -0.39897925379227, -0.938995428517715, -0.160833898722656, 0.0019267572841458, -0.246776039875847, -0.48019386356188, 0.0607875512709743, 0.0026076814316677, -0.477604907738661, 0.140735178967626, 0.256590546370483, 0.0983938085356448]', 0.99); -insert into credit_card_transaction (time, features, amount) values (157738, '[2.11420225937483, -2.18804432139882, 0.603545538263212, -1.10575212867619, -2.36094882925138, 0.946849439520807, -2.49509350836066, 0.387735712267693, 0.580118177842292, 1.15042903272111, -2.11858105612205, -0.342310050588501, 0.507294307432643, -1.92203685311568, -1.90383067304198, -0.725858196270718, 0.882391049995452, 0.322210072266206, 0.206341972051273, -0.352456535827324, -0.0707520365150876, 0.646794956511348, 0.183200136361677, 0.615049630458998, -0.316643784242375, -0.00382870027308, 0.109398590620118, -0.0168357117620624]', 46.2); -insert into credit_card_transaction (time, features, amount) values (157739, '[-0.345947018532829, 0.221689609855178, 1.68866949367616, -0.881681084033478, -0.0193693793565869, -0.0021479558732642, 0.240629369384862, -0.0974860189554045, 0.852488363208922, -0.479817384954774, -1.65483960293721, -0.586081860084165, -0.293989674328486, -0.597674819896937, 0.433662931191321, 0.753257698300604, -1.05143706003626, 0.411892847152629, -0.207315148877616, -0.0697681503251412, 0.0502553219555294, 0.342093796755629, -0.117140651695368, -0.566784186775397, -0.868227067863053, 0.401627715456505, -0.131879961206479, -0.0371630755784455]', 27.99); -insert into credit_card_transaction (time, features, amount) values (157739, '[1.90355890908553, -1.72086079221897, 0.139063963238674, -0.194423737837215, -1.66098100337126, 0.760001538073809, -1.71104921299269, 0.283424192302374, 1.23407901022462, 0.566179019715467, -2.63631122614157, -0.266874356715388, 0.149689695445292, -1.08147333397146, -0.486787168481125, -0.91032676753664, -0.338034134929352, 1.83555613708758, -0.468569836431988, -0.385395594783029, -0.24407481846269, -0.0703035333086816, 0.0756449551659965, -0.977724624414079, -0.492004296135284, 0.745733241340718, 0.0283172755923351, -0.0241675265688537]', 112.74); -insert into credit_card_transaction (time, features, amount) values (157740, '[1.83846371707358, -1.00789570371663, -0.475508888942729, -1.32241750189768, -1.3575364935935, -1.28861338371384, -0.537460563976151, -0.027337566127183, 2.03862295720513, -0.773385130811969, 1.3799174976278, 0.917158064264836, -1.62565139490795, 0.592932743942844, 0.617070516813047, -0.273747394990348, -0.245000707848843, 0.267194762712138, 0.852888653005438, -0.155358878853214, -0.11243721178139, -0.384967829765341, 0.384549492012338, 0.495076451362179, -0.599735008164689, -0.617085396006646, -0.0001982873753459, -0.0335827192839419]', 77.73); -insert into credit_card_transaction (time, features, amount) values (157740, '[2.06304902165685, -0.687040539353543, -1.07566541064391, -0.541476227587332, -0.783895137294314, -0.882228198054091, -0.82677879116441, -0.161548779248274, -0.109924804690685, 0.0350148975111852, 0.120669987063703, -0.210782213372381, 1.01565898594868, -2.26763046041853, 0.254984057925325, 1.5469928679413, 1.43530834125397, -0.637877975337297, 0.0596527698877201, 0.168821729448001, 0.286460219525427, 0.852281711381202, 0.146902166622929, 1.07279124793666, -0.161508660978008, -0.152964826764879, 0.0243990126920294, 0.0067917277746852]', 49.9); -insert into credit_card_transaction (time, features, amount) values (157740, '[2.03203087028484, -0.484093814216839, -0.646584353768229, 0.174146147077712, -0.308441001330428, 0.001793199991257, -0.592710224077965, -0.068557470292307, 1.19057544389679, -0.0422091194455687, -1.72321280829425, 0.597794654617644, 1.45394737054517, -0.556781851323801, 0.31076062538391, 0.563820111399627, -0.908643305742023, 0.228343469943537, 0.11718609565058, -0.043987799920069, 0.167939082254307, 0.739235374817959, -0.024812900116628, -0.897527829394105, -0.0863666409083893, 0.798311927228787, -0.0253205952498289, -0.0544788653764115]', 27.99); -insert into credit_card_transaction (time, features, amount) values (157740, '[1.87981862142844, -0.535896887213884, -1.70839444438536, 0.217682555348215, 1.90809314874955, 3.98317403315355, -1.07652420881567, 1.04053296138806, 0.738002701842881, 0.224059767541061, -0.153416613926394, 0.291789101906111, 0.019368121995729, 0.195937671029803, 1.19368443162048, 0.389562242192401, -0.878161575454054, 0.223283119984314, -0.898038370048889, -0.0993954220481979, 0.243559039895981, 0.667075332234168, 0.14697623583966, 0.689132283677662, -0.0623642465308243, -0.54464459981507, 0.0701932712231592, -0.0303286742065872]', 40.0); -insert into credit_card_transaction (time, features, amount) values (157742, '[2.12849030125647, -0.747185954583879, -1.5447927961167, -0.615499644492191, -0.193327180236505, -0.270710002960345, -0.43820758834029, -0.0812152822731884, -0.30877312042114, 0.861168178256535, 0.183825371984796, 0.227208364690515, -0.413833453052153, 0.53995742891835, -0.112469672186877, -1.5536603858097, -0.541578198005987, 2.17722981763222, -0.481800030352361, -0.619650883567572, -0.0148642805005191, 0.515364471776275, -0.0201613311500474, 0.32885944613271, 0.304806248191858, -0.366445790617166, 0.0211505264380046, -0.0553336098042886]', 21.4); -insert into credit_card_transaction (time, features, amount) values (157742, '[2.01273222402779, -0.322355556749874, -1.5004392466741, 0.24660282704128, 0.344595674329681, -0.0243476754177271, -0.0361029272935881, -0.0626035717793579, 0.551757787173089, 0.18236156449223, 0.218477613311911, 1.01743034484664, 0.131678020318633, 0.170105776722132, -1.31341047544094, -0.180723904964872, -0.485994215597178, 0.0982299926297986, 0.590988472091311, -0.124208750743618, 0.158227865516044, 0.664813434578162, -0.0520136886658377, 0.377909825948252, 0.253263259708681, 0.696729598701705, -0.0800659004959113, -0.0752443898421573]', 25.75); -insert into credit_card_transaction (time, features, amount) values (157742, '[-0.318071742166367, 1.02745601434487, -0.260435051717774, -0.766340115013417, 0.355795791655387, -0.572323874948388, 0.56294197477809, 0.398685457019097, -0.432997936487005, -0.469724666742441, 0.187955940265917, 0.794956120303546, 0.0102613143156877, 0.609379768082371, -1.10074059300705, 0.368434422171477, -0.66099246645221, -0.0488361268993704, 0.378681775893287, -0.107260525079444, -0.193564244582984, -0.56329531031382, 0.0740159009143445, -0.503168842407909, -0.43029942841597, 0.152743046722348, 0.114140563149223, 0.0199384851920048]', 11.99); -insert into credit_card_transaction (time, features, amount) values (157742, '[2.31429888671426, -1.04338614715792, -2.50423600176869, -1.67635179225787, -0.150439713563213, -1.37344964681271, 0.0752761681906443, -0.616690509065882, -2.43981859914671, 1.88616946629869, 0.572240577176754, -0.0706863960929765, 0.751883137816819, 0.355195051253356, -1.43279667251668, -1.18345696329319, 0.412356820601461, 0.110319025292818, 0.381389523170668, -0.344839579464768, 0.17638753114187, 0.982407266486812, -0.239708766846984, -0.257360899172639, 0.673004377785231, 0.296020567106736, -0.0769356247549224, -0.0936969434992459]', 50.34); -insert into credit_card_transaction (time, features, amount) values (157742, '[2.14277774795616, -0.802705307196821, -1.2032447205099, -0.32762349364375, -0.5009994773843, -0.475825533194848, -0.452286890513846, -0.206864593196201, -0.168463323362039, 0.795645060166528, -1.36769074191324, 0.117501342180503, 0.356005512597422, -0.19190146490817, -0.700827462347246, -1.8838364574366, 0.14223679111842, 0.798231714167276, -0.560141462893536, -0.558892578312831, -0.304805987842465, -0.178735935872318, 0.161754724574926, 0.750326714511842, -0.0197054230873036, 0.719905494998099, -0.0515041600611123, -0.054912525315581]', 22.45); -insert into credit_card_transaction (time, features, amount) values (157743, '[1.90915326979068, 0.381884263482279, -0.653980010932487, 3.85651108274668, 0.15046036460817, -0.250349935297848, 0.0488580822050828, -0.0409825875221328, -0.579401823091845, 1.51659745039793, -1.56805619390379, -1.18557838785057, -1.75435392553198, 0.523729864223883, -0.608844395046113, 0.730330343856242, -0.592790372678641, -0.0777003608436466, -1.70337045808353, -0.405359719438246, 0.275231998581727, 0.822341964597943, 0.0188386894419778, -0.0471152062090989, 0.141006773457772, 0.252459225232092, -0.0425743072171133, -0.0557621330041325]', 10.62); -insert into credit_card_transaction (time, features, amount) values (157743, '[2.04610332332394, -0.0437500638027864, -1.81848571745892, 0.268296048565753, 0.404609011354702, -0.474795335600776, -0.057555746269053, 0.0384949372649159, 0.55244372839079, -0.150364928358203, 0.588251695820762, -0.302360651856953, -1.97999331776155, -0.263434533359907, -0.2593827933381, 0.672399787638286, 0.255012222252439, 0.506568354023475, 0.440814464473537, -0.265214682377994, -0.350601495515078, -1.03755564438747, 0.323294613528108, 0.201622368223623, -0.325850122264503, 0.174851882408524, -0.0799209036058502, -0.0471719334989403]', 1.98); -insert into credit_card_transaction (time, features, amount) values (157743, '[2.06831161827872, 0.0853584659959945, -2.02475176147264, 0.433383049421526, 0.497033458408612, -1.51080109726167, 0.826759722503537, -0.585385438752121, 0.314635194859999, -0.0249038755415932, -0.90372178264667, 0.54142026658436, 0.255035024111003, 0.568638993545291, -0.242669668034902, -0.868153447529269, -0.178154155510276, -0.573743017275659, 0.204830288816604, -0.193983325283011, 0.141583871185518, 0.617302731362218, -0.121423998512596, -0.0467513421837677, 0.585775550442182, -0.0652624726304511, -0.0545523497600951, -0.0708393278458775]', 27.49); -insert into credit_card_transaction (time, features, amount) values (157744, '[2.23571001383584, -0.68403269277342, -1.83367914028272, -0.784598653319528, 0.130118073020245, -0.195954773585233, -0.278490618770863, -0.138349075822692, -0.654243278575267, 1.08316924183954, -0.622011388178176, -0.0526806265667618, -0.248988782715607, 0.457074860284552, -0.700035079331822, -1.10200788540102, -0.680950159881122, 1.50283359586339, 0.218872242457627, -0.60917267879173, -0.601706295127028, -1.19060008887046, 0.158435948878832, -1.42170496138364, -0.140845673410488, 0.508018611708571, -0.0755261717006566, -0.087813075060984]', 7.45); -insert into credit_card_transaction (time, features, amount) values (157745, '[1.65313779544536, -0.711007814982481, -0.925334352603764, 0.743301747943678, -0.498871318330668, -0.87424547442454, 0.200317409990387, -0.337106866934903, 0.604324757490942, -0.0317452379676312, -0.663058534302706, 0.914893511293375, 1.1222509992426, -0.0550347603174709, 0.145678065297836, 0.0492930673370552, -0.463805890579845, -0.49597221845402, -0.20153375313579, 0.23840996740054, 0.0868596244520178, 0.02814092313606, 0.0459056113483164, 0.104573206944879, -0.246952123497116, 0.192894233459285, -0.0680055170817587, -0.0169334563682491]', 198.85); -insert into credit_card_transaction (time, features, amount) values (157746, '[-1.19625512501413, 0.361336493557756, 0.35012187229708, 1.01481725243179, 0.368568691551676, -0.0962190287971712, 0.290258812587897, 0.511279692890145, -0.25481046823163, -0.386163197926171, -1.94513134037998, -0.495976845991979, -0.395877064916596, 0.530389354663688, 0.627041179194693, -0.0819567498954667, -0.210200150495589, 0.522374627354505, 0.730362671711535, 0.373691561757597, 0.182057884893051, 0.252754667651089, -0.0223804859044217, -0.811654668692753, 0.0046797607188867, -0.439189007326529, 0.298907045272588, 0.0684735103026787]', 104.5); -insert into credit_card_transaction (time, features, amount) values (157746, '[-1.55640515826511, 0.277250057131305, 0.279203818863053, -0.905677045540747, -1.83503504320863, 0.792850091318565, -0.485137799379526, -2.26993589950136, -1.62601845120763, -0.699273926701926, 0.0544874981189315, -0.473159046831929, -1.5860735720041, 0.551110079544066, -1.73441789430872, 0.251688377314208, 1.31969700769844, -0.494231562168939, 2.10783667253359, 0.689377165749752, -0.729807312383886, 2.0054679590148, -0.370282168801813, 0.170553984741293, 1.01876690359243, 0.566498491287749, 0.0116801819572213, -0.11427870154068]', 253.0); -insert into credit_card_transaction (time, features, amount) values (157747, '[2.06080364938134, -0.101986941177864, -1.0758824736156, 0.425613927831132, -0.211935740378031, -1.23594282518287, 0.13691363270136, -0.29525286488622, 0.648545482103282, 0.0940814313372673, -0.854383851158043, 0.0634651836914334, -0.648412667688802, 0.417479297592626, 0.117381436305634, -0.0744266165263341, -0.244197263247831, -0.785284927766923, 0.147506628837049, -0.269871839770831, -0.296743134780897, -0.755769994215157, 0.349687865840473, 0.0268549724622407, -0.323696402968748, 0.197745332701712, -0.0754038679838504, -0.0613411882851275]', 1.98); -insert into credit_card_transaction (time, features, amount) values (157748, '[2.00351548108285, 1.29863412519987, -4.31527728207764, 1.97602283621219, 1.75293830820954, -2.24497222694047, 1.11766605069805, -0.36313840741697, -0.348439676032932, -1.56998016249089, 2.0778789671457, -1.63681061226303, -3.43617789447646, -3.77640102690597, -0.14803122963879, 1.12250281454358, 3.97354020868844, 2.62459597355864, -0.894324918438769, -0.328026190143838, 0.0362068634765769, 0.203869854082124, -0.270469316503627, -0.47223785876609, 0.73443359963732, -0.202782516800411, -0.006616185819499, 0.0244298732988913]', 1.0); -insert into credit_card_transaction (time, features, amount) values (157748, '[-0.650930709532351, 0.263964467540254, 3.55770916797245, 4.76773434963167, -0.202304811467762, 2.49383079864348, -0.8192891254114, 0.592894380813968, -0.123213243627121, 1.06620638546009, -2.38147616099625, -0.989561898628565, -0.695760441119307, -1.3300913954694, -0.859409667122229, -0.346072323192032, 0.529398058632442, 0.692845154870888, 1.70370316356604, 0.451397341056591, 0.0709598979063375, 0.612585293657667, -0.250158348977302, 0.491312935145328, 0.13995672748693, 0.613300895030037, -0.0029253736821857, -0.101570815095981]', 50.12); -insert into credit_card_transaction (time, features, amount) values (157748, '[-0.757335255177685, 1.0909000225444, 0.624266126570755, 0.103105894843957, 1.19497463887191, -0.294548440388125, 1.64648205753637, -0.108406552708409, -1.12908028458702, -0.584121598872455, 0.202519779350007, 0.146319758312773, -0.902308278626179, 0.709994096310675, -1.37243994011026, -0.486067169112586, -0.4523417540132, 0.184860614880998, 0.256269589668875, -0.0601424907495721, 0.100984605028546, 0.255919320168421, -0.59146646132216, -0.403009601270806, 1.26426042679079, -0.35045354357559, -0.0092566107235634, 0.0278198448265206]', 46.0); -insert into credit_card_transaction (time, features, amount) values (157748, '[2.04632034459966, -0.0277642367175021, -1.05622536419919, 0.418738870335234, -0.0968716018175335, -1.20027293230247, 0.24104214894528, -0.375617052299735, 0.438960886866, 0.0498748704859101, -0.619280965777002, 0.796633887964169, 0.73095131062767, 0.145069016676394, -0.0327926480248298, -0.138004912521931, -0.322700700765357, -0.952697690981258, 0.135641874176874, -0.165254478974978, -0.27604066894971, -0.647153198594462, 0.326079762381277, 0.0649931406468035, -0.280018002699793, 0.192272426379622, -0.0679113310596779, -0.0569854048983729]', 10.0); -insert into credit_card_transaction (time, features, amount) values (157748, '[2.19091521079005, -0.317362279223724, -1.7662852128836, -0.663390000862614, -0.11971372843457, -1.35958632204678, -0.0895239106883937, -0.426572707816931, -0.746343483478287, 0.248284108086772, 0.178394282251264, 0.0546052685741178, 1.16300457117473, -1.4928431253211, -0.306709604523775, 0.88305621485888, 1.38405602849814, -1.56463468882138, 0.508103691093454, 0.0827774774478032, -0.0072936520044746, 0.044828725293702, 0.236942252731653, 1.08312110937402, -0.0017934804411545, -0.341545119485686, -0.0223368558942935, -0.022303647117555]', 15.0); -insert into credit_card_transaction (time, features, amount) values (157748, '[2.20287935086538, -0.83917755281413, -1.51623822048021, -0.737539644031996, -0.665990096744758, -1.42158411857157, -0.289538545708148, -0.396269099406977, -0.25257585171814, 0.870033660700008, -1.03394556186747, -1.1769294601708, -1.0684426879714, 0.160989790522125, -0.224718238898465, 0.529784456382618, 0.470406278595387, -1.20821519860039, 0.563084909444508, -0.0983967532299143, 0.550626585402461, 1.6480286354807, -0.165041847378357, 0.119959540547246, 0.444186408439166, 0.290681093294727, -0.0537918831560525, -0.0747253897895706]', 27.0); -insert into credit_card_transaction (time, features, amount) values (157749, '[-1.15657377664651, 0.9964339929977, 0.838178465622397, -0.867772681400813, 0.642522043447302, -0.488090907415734, 0.631533367652051, 0.421273319802395, -1.00700572772504, -0.999795566635754, 0.421285870719857, 0.766778146960382, 0.0333212664749036, 0.665431558398277, -0.91050575948488, 0.211177738668658, -0.445418411118552, -0.165153940034023, 0.4651198229711, -0.103477236494009, -0.189673574203013, -0.784538961403902, -0.318408026743572, -0.387255181204249, 0.521605677066274, 0.325484583863894, -0.166371101041145, -0.0714860119645089]', 1.29); -insert into credit_card_transaction (time, features, amount) values (157749, '[-0.578991320013255, 0.176269869115641, 1.72959747436657, -1.05165833444821, 0.123865694886083, -0.767387046780066, 0.761666346221852, -0.168170447961388, 0.455547597365137, -0.793369567202865, -1.14947578586987, -0.72147540237967, -1.28613927867143, -0.135356071790557, -0.0372385716594587, 0.559436931657113, -0.800872143159333, -0.269226802238125, -0.664825037061927, -0.0347064489860529, -0.222606563308873, -0.670341991988361, 0.0996946645491959, -0.0088786388499015, -0.564961672152797, 0.108489709354273, -0.123975908065529, -0.0811182362070439]', 47.49); -insert into credit_card_transaction (time, features, amount) values (157749, '[-1.31218649527957, 1.47794604528064, 0.835260708483131, -0.973668550293296, 0.583924407310809, -0.233249287567359, 0.566023278091484, -2.8751659798451, 0.155457543985153, 0.458986603345045, 0.918786611148297, 0.432169731130655, -0.86575920634809, 0.0642431161311722, -0.945861892243984, -0.13633792796123, -0.557665163671291, -0.204176920851281, 0.294373282180676, -0.405861928525427, 1.9969671851274, -1.27526037649752, 0.165312730640243, 0.0183607371704786, -0.238080822288125, 0.129610418114815, -0.204347970948289, -0.244018252578356]', 3.59); -insert into credit_card_transaction (time, features, amount) values (157750, '[0.37707341751061, -0.785761319816824, 1.55634358016483, -0.848884021971634, -1.34457735242435, 0.657358226915269, -1.31655931351197, 0.153508217919353, -1.84383387865382, 1.47856470797897, -1.00188191922223, -0.772117045497867, 1.66728344900969, -1.00577065799995, 1.26534710137343, -1.2614443156487, 1.22110051287671, 0.670924631931016, 2.35747901502321, 0.166483286792319, 0.008564787294346, 0.613056734357945, 0.21735821930664, 0.628199165157873, -1.26958849953935, 0.0809728039354757, 0.330339839062508, 0.248804047174878]', 34.0); -insert into credit_card_transaction (time, features, amount) values (157750, '[-1.1444852175499, 1.46634408900666, -0.365784503068063, -0.793915880823082, 0.434679597323906, -0.488924822882275, 1.30281343071596, -0.101746728101585, -0.323897576430386, -0.788616361937625, 0.821229290531026, 0.0679061110252029, -0.486895558605613, -0.604478444778123, -0.0255427173172512, 0.441752999679591, 0.0722325252297329, 1.20462834075134, -0.127632979870371, -0.297017271636033, 0.316754219071224, 0.891904945925574, -0.530348541022318, -0.458556915769809, 0.26186228984748, -0.168980246885882, -0.653336386174846, -0.176615450812176]', 89.95); -insert into credit_card_transaction (time, features, amount) values (157750, '[-0.30657833666707, 1.04174718337986, -0.090966703914278, -0.530341836381778, -0.0004691658636179, -1.19600942803246, 0.618016490422182, 0.264137686380941, -0.0911828639183154, -0.615284265736217, -1.2060001223976, -0.135571452693822, -0.74704131854644, 0.592268067299415, -0.352859432943153, 0.0656562981048191, -0.190546542596523, -0.610394694738725, -0.0880134438732053, -0.186913442561252, -0.223257384024339, -0.648286658547223, 0.150784919608977, 0.0153364519812683, -0.491333449163797, 0.142731071980553, 0.114344205630448, 0.0332636525000347]', 9.99); -insert into credit_card_transaction (time, features, amount) values (157751, '[0.995536448723758, 1.8604287052582, -4.89863812941707, 2.04548218978533, 1.47310361810392, -2.24144023999241, 1.22333008906408, 0.129188612508083, -0.520690411302041, -1.50489855497871, 1.80005730340127, -1.6259847497, -3.31685510771455, -3.58410239272137, -0.217957576241076, 0.998067520421887, 4.25638249464842, 2.88418104171532, -0.418596476532229, -0.16591430638907, 0.2210992914627, 0.682204514751018, 0.223213986092436, -0.493809168008279, -0.855078470331945, -0.352004451994275, 0.179849970939297, 0.0192266340873271]', 59.02); -insert into credit_card_transaction (time, features, amount) values (157751, '[1.93574284930168, -1.22394751772485, -1.27473771655157, -0.491858458306682, -0.525888734919106, -0.0525375139868245, -0.517635371114252, -0.132763676164761, -0.165891062908025, 0.781954952045084, -1.69043305006766, -0.180301217109999, 0.620426933067886, -0.147970583197821, 0.254132470196078, -1.12743992287832, -0.421785323641085, 1.23684133396203, -0.613006316566133, -0.266509198998845, -0.332732352487289, -0.666949738268772, 0.127400319468649, 0.092216133515229, -0.309739217214739, 0.550797522229459, -0.0624281922962205, -0.0274398920709259]', 145.72); -insert into credit_card_transaction (time, features, amount) values (157751, '[2.00616333354732, -0.102957997721127, -1.00872363502309, 0.311893178388144, -0.0922913523546295, -0.852106113930458, 0.0697096663335968, -0.168770834380666, 0.270529123777551, 0.233122176556197, 1.08803451066019, 1.1894353773142, -0.0123713796248276, 0.477161435652408, -0.622302488258895, 0.0066721871032153, -0.453360851093921, -0.494180543124355, 0.332054381450122, -0.222234989389338, -0.226317932674189, -0.54482707297985, 0.345302753808434, 0.018631093559939, -0.353597166337901, 0.178367162491684, -0.0709379948780563, -0.069901327375337]', 0.99); -insert into credit_card_transaction (time, features, amount) values (157751, '[-7.21819546147783, -8.96771828144094, 1.50273907674583, 1.85796380927918, 8.99912602103606, -6.57097391729028, -5.14779023684865, 0.644873625992101, 0.469401243435839, -0.181275576912098, -1.35565956782952, 0.467249738500158, -0.281846967894806, 0.434323948713366, -0.516682797275072, -0.818545579208959, 0.196367774095126, -0.156594276196618, 0.108601452575295, 2.43520143919706, 0.925196875291596, -0.0170445588984718, 1.27785279442287, 0.03193065278178, 1.4085126985899, 0.852911369574687, -0.395061133282322, 0.217884998199604]', 57.5); -insert into credit_card_transaction (time, features, amount) values (157751, '[1.89768816736501, -0.4325048790701, -0.238286022657638, 0.553589326356288, -0.746598094038173, -0.579279604091999, -0.449418149578351, -0.155511820550268, 1.25247685356112, -0.22511706333054, -0.740371551341499, 1.23423281811443, 1.51819281578446, -0.466492861783479, 0.32474220132332, 0.0096020134140664, -0.572519465194055, 0.0293176274116009, -0.259668978023597, -0.0510208700828914, 0.234962279960646, 0.913901902739597, 0.0682567360571461, 0.111997459198014, -0.0657023641164369, -0.224722067025397, 0.0433485789588105, -0.0261791630404535]', 50.0); -insert into credit_card_transaction (time, features, amount) values (157752, '[0.0368117672889375, 0.839535024590251, 0.285488093283135, -0.590370231245683, 0.408437057001676, -1.04481259773251, 0.982371471217317, -0.166127010520899, -0.0036327386541445, -0.41177538679722, -0.924264405572009, 0.0072830840230801, -0.275692752978534, 0.173974207688459, -0.325089052220534, -0.138448856284179, -0.336194481283671, -0.802727435216233, -0.154029898467275, -0.0630528251062092, -0.257930872404855, -0.570648997882921, 0.0800862928457901, -0.0278834331507814, -0.513841647897647, 0.144346911921666, 0.248486090529327, 0.0962140465716221]', 3.57); -insert into credit_card_transaction (time, features, amount) values (157752, '[1.89170739500416, -0.535418234207353, -0.649055233453485, 0.160692492875326, -0.229655852274538, 0.340856330051236, -0.605532253459684, 0.219567501257327, 1.09393164260772, -0.016871538841787, 0.443548139481764, 0.830369798386695, -0.293535774962598, 0.214764634703597, 0.125780314135634, 0.51515258968659, -0.902328507273473, 0.454757093599476, 0.315176349262703, -0.116516418227511, -0.137147320123324, -0.425352543363503, 0.313525952982681, 0.165615719482378, -0.423358015771402, -0.645636348660543, 0.0182145704959304, -0.0323859078184513]', 50.0); -insert into credit_card_transaction (time, features, amount) values (157753, '[1.73278263686518, -0.592270260647071, -0.462045902778294, 1.19644582723498, -0.484116556270241, 0.250928230474426, -0.615395716587068, 0.255479023979069, 0.831429075065553, 0.375551689481227, 0.588540625141442, 0.182356444065303, -1.33669613816033, 0.490835719529378, 0.330270556765314, 0.695356248222205, -0.915768513950396, 0.969378411002267, -0.563212595820842, -0.114479139554471, 0.31885667216782, 0.726783492232213, 0.10557350243377, 0.654289050515933, -0.200611209120971, -0.617173807621627, 0.0245607598500652, -0.019838916332728]', 100.0); -insert into credit_card_transaction (time, features, amount) values (157754, '[-1.08437373698997, -0.0412353933328802, -0.165797624743433, -2.19547962682919, 0.191909781395609, -0.969693391039072, 0.389501989851879, 0.329852914326939, -1.26946382957439, -0.270440447911511, -1.30066986172269, -1.19657995857087, -0.953882290809789, 0.342889906964146, -0.859442939984685, 0.958405612856348, 0.349146044454139, -1.62221774675246, 0.097285102745393, 0.230069657182812, 0.15479247526566, 0.0121206326513846, 0.0309908285512829, 0.642558408052816, 0.129755664567465, -0.398887950359597, 0.172752596264637, 0.0639834429117401]', 76.19); -insert into credit_card_transaction (time, features, amount) values (157754, '[-0.945582711723325, -0.799151969050314, 0.644264103232057, -4.05352188828289, 0.381395552695166, -0.0687993504504064, 1.24520247283181, -0.191101683834918, 1.57213210266073, -2.28272915743526, -0.210813469179539, 0.887805747164436, 0.571868901293711, -0.256948330898479, 0.139600225916565, 0.315878658552191, -1.76638697434972, 1.4558766012445, 0.234058703096384, 0.446352683720084, 0.35439702931146, 0.878347311892487, -0.106315024204261, -1.3765006838409, 0.59924746466712, -0.888200056265963, -0.0967348639529727, -0.0993864296509226]', 213.64); -insert into credit_card_transaction (time, features, amount) values (157755, '[1.94745075040267, -0.166135946239098, -2.1789988559468, 0.259675948304535, 0.600266187134912, -0.385792777166616, 0.111954875304485, -0.119851953489704, 1.09993863711154, -0.930524920925639, -1.14133905144436, -0.155739453659387, -0.312837252818048, -1.56701368196073, 0.073052246972743, 0.237006697353214, 0.970423184107843, 0.321068653985394, 0.309033400867493, -0.020293814296173, -0.267960397036744, -0.696340579812742, 0.079140772639746, -0.162074004683936, 0.0058090256302605, -0.0785920908076026, -0.0221786968919089, -0.0084732558167042]', 75.46); -insert into credit_card_transaction (time, features, amount) values (157756, '[2.22937174280281, -0.564662323451588, -1.56304440884967, -1.1327568991883, -0.12369719813225, -0.950634618370871, -0.172291840174059, -0.337928002082741, -1.04225945626249, 1.01046242461322, 0.894189186048819, 0.421124238419084, 0.938854512454626, 0.168420574924453, -0.519795733394962, 1.1469081134977, -0.264848109965856, -1.22501638871071, 1.1313725279866, 0.0286812615289434, 0.0778540199652142, 0.18988439574898, 0.13716523706319, -0.472203387827786, -0.0044498094611528, -0.30689356629706, -0.0466223127278476, -0.0759521611455766]', 9.99); -insert into credit_card_transaction (time, features, amount) values (157756, '[-0.536399250777087, 1.09269093884251, 0.0314068708488752, -0.718972340435008, 0.86351894985858, -1.11813068621713, 1.28231479015933, -0.382034069909047, 0.0097074625160727, -0.436452431732871, 1.16241012433618, -0.135405498674783, -1.08061221308623, -0.758039036086932, -0.143115331017543, 0.278893564734146, -0.0059484932873254, 1.18267790897185, -0.339699527625088, -0.0185012536458631, 0.278959224317972, 1.04513464711954, -0.308366330102825, -0.0130010893263757, -0.151276944374993, -0.246861157028185, -0.0241853849781984, 0.0452097936799999]', 13.32); -insert into credit_card_transaction (time, features, amount) values (157756, '[-2.18880795479537, -0.497743923716989, 1.31560172552563, 4.86630879729476, 1.40941850566254, 0.253830145565313, -0.634590462763959, 0.749229751785257, -2.1148307841804, 1.02267086527344, -1.58850860407147, -0.959609663901933, -0.235106252236773, 0.658680217144038, 0.931018786180936, 0.127629182179435, 0.350181111152167, 0.484112244497463, 1.29947445980354, 0.869868835415983, 0.272937762982112, -0.181974140222543, -0.0962407741040469, 0.561695162292163, 0.289202182753139, 0.432880230335186, -0.0289126039611331, -0.270890277589851]', 88.95); -insert into credit_card_transaction (time, features, amount) values (157756, '[1.95400584549428, 0.655397195128564, -0.800645554137467, 3.71691852853449, 0.45298297826303, -0.483443968354456, 0.341575095868091, -0.233377861442085, -1.13049220745049, 1.54884478647581, -1.15433181399121, -0.38523453330898, 0.0705418460128302, 0.408296538734284, -0.315414160681798, 0.922552807035497, -0.944061897356317, -0.206842979000685, -1.71940675789533, -0.288283970433082, 0.224559510942943, 0.66151409853066, 0.0541920838425218, -0.0346133499052405, 0.134809410587815, 0.13401380020373, -0.0460666971361718, -0.0525580321456387]', 4.58); -insert into credit_card_transaction (time, features, amount) values (157757, '[-0.458621773689576, 0.682699395919958, -0.717700493667341, -2.7412829900963, 0.79994570700212, -0.651982955144735, 0.84297048631917, -0.432881763454238, -0.388895723073424, 1.38716248869094, -1.36141631435332, -0.845326268377232, 0.970517011252105, -0.747652380526367, -0.488045428227949, 1.12751682724628, -0.695389929559789, -1.10286498145696, -0.160340186601634, 0.580389473761996, 0.350170815111158, 1.50218057323091, -0.225763658542937, 0.0927971874095637, -0.43076290248475, -0.215199122019883, 0.606190882808856, 0.299291556796428]', 15.0); -insert into credit_card_transaction (time, features, amount) values (157757, '[-0.729037821210149, 0.639110463060512, 0.0316307195375617, -0.620396638876, 0.487170188648724, -0.175842768907904, 0.471087167016775, 0.500278982513446, -0.388546378299453, -0.816431294178666, 0.150579394898356, 0.484026317539133, -0.900453179060494, 0.627579538526255, -1.67824069490274, -0.254329596249963, -0.121784321136374, 0.182580007309519, 0.249926019906321, -0.178579942704754, 0.344371573679307, 0.899385461601915, -0.199641932441146, 0.776438831980956, -0.111418145282334, 0.6104960266223, -0.0377101235726492, 0.0624342373798584]', 29.56); -insert into credit_card_transaction (time, features, amount) values (157757, '[-5.7748067363417, -4.32227757549895, -1.29937262597976, 1.04887404906239, 4.12690082549974, -2.8397358767187, -2.04707532077296, 0.123781007629177, 0.762231195781618, 0.617482960411276, 0.621354556794564, -0.0458515743778481, -1.4657956555301, -0.307403443610212, 0.198119137378741, 0.837804586557949, 0.41367559863984, 0.379498094551388, 0.965424104963162, -1.48223402012487, -0.966762699248118, -1.50803320720952, -3.62521359507269, 0.596769572394591, -1.0543500073088, 0.217528109401039, 1.92434252218339, -2.05302930063958]', 8.97); -insert into credit_card_transaction (time, features, amount) values (157758, '[2.17084214383082, -0.913143597442236, -3.56163511718779, -1.99265008070707, 2.39205038258576, 2.70454314926379, -0.306117896979413, 0.465751272850535, -1.13777398376195, 0.927655372553512, 0.183075924351834, -0.627282681614006, -0.110632219055303, 0.582352837510263, 0.350668114039655, 0.363132051491306, 0.273091745774191, -1.82478072814313, 0.513075600093103, 0.117849693666259, 0.373891598093233, 0.870959869816497, -0.0701333838731601, 0.769021113031478, 0.469472131277971, 0.0955363493514126, -0.0685508903513939, -0.0793720094232542]', 59.9); -insert into credit_card_transaction (time, features, amount) values (157759, '[-1.30269411219782, -0.0418640727284351, 0.963828036033088, -1.08506627923451, 0.0742958691779834, -0.407220420858539, -0.0977229389862071, 0.617105780011204, -0.137341527112833, -1.01796292832634, 0.0563638067164112, 0.401193677069014, -0.279475281888258, 0.39915021104632, -0.50418464359173, 1.17020342005531, -1.1178191581226, 0.900995151620355, -0.606936051254229, 0.0281600872063651, 0.403782591298034, 0.693021517532399, -0.104615423876459, -0.370779028277872, -0.265745952914952, 0.53962925255222, -0.0571931587943226, -0.023851507204078]', 65.93); -insert into credit_card_transaction (time, features, amount) values (157759, '[-1.5608254226094, -1.06785779081051, 0.138003773072091, 0.780004173790235, 2.45151437918082, -1.1362534046161, 0.0973771954255916, -0.066839990531662, 0.151997929506751, -0.26895512947376, -1.55927588304372, 0.450376703513502, 0.608360569183959, -0.177370648596257, -1.1696379952774, -0.451448883067062, -0.516868903126989, -0.0903405180474666, -0.258703699149118, -0.120842383187683, 0.263528658179447, 0.973515977305121, 0.606723976833951, 0.597607777707733, -0.303055409059802, -0.65947536289348, 0.114351187137116, 0.422444170159428]', 1.0); -insert into credit_card_transaction (time, features, amount) values (157759, '[-0.915149111878821, -0.732103700070389, 1.49069621661704, -0.450923280227081, -0.704878215076738, -0.494060725888955, 0.0237870140553342, -0.801321529221471, 0.163585727916949, -0.973935235585285, 1.39319968260154, 1.10467325534667, -0.423014185587626, 0.414208599710605, -0.156794493042451, 0.351135605962524, -0.479560017678668, 0.033509649172744, -0.2518095821509, 0.441220008946705, 0.726163557561348, -1.30439193978001, -0.964258204639311, 0.555589713594712, 0.988762713663006, -0.187225454717361, 0.0067962712424468, 0.210200157442145]', 360.0); -insert into credit_card_transaction (time, features, amount) values (157760, '[1.99318002048534, -0.558485868603381, -0.682291230278836, 0.162872636222983, -0.276326482085174, 0.394895499457525, -0.784860404581807, 0.219108104691434, 1.39517808299051, 0.01699714643554, -0.339379115077556, 0.439812518642154, -0.526328463447657, 0.0877231197409678, -0.0176908779296751, 0.563341226883815, -1.04489341263432, 1.04929510691301, 0.398211276012977, -0.22320691538389, 0.173279557567715, 0.691856395487233, -0.0149100689340387, -1.15247816715018, -0.0231690647988001, -0.157821234151418, 0.034256502430304, -0.0590990039260461]', 12.99); -insert into credit_card_transaction (time, features, amount) values (157760, '[-0.387016308294202, 0.885546810509896, 0.576911056022643, -0.246196704250171, 1.26220352242242, 0.0357934725830215, 0.537904841618394, 0.12271423549909, -0.98282935093223, -0.82859659698616, 1.06275462327465, 0.473996961307179, 0.809562502699716, -0.905139917757283, 0.680285294166137, 0.747654974068223, 0.187758406700493, 0.659629050222666, 1.40458637656597, 0.28609258449808, -0.501688991089838, -1.64453150156937, -0.0484504877013835, -0.038074699512936, 0.159200464028261, 0.198745385469487, -0.0901895402829525, -0.0293581212943189]', 9.0); -insert into credit_card_transaction (time, features, amount) values (157760, '[-1.67074797375828, 1.82082365145551, -2.52200953466509, -2.6746545795997, 3.74073522355887, 2.70667631305782, 1.62417230504786, -0.0285580020193143, 1.42983393139238, 2.65812801040098, 0.326584457209074, -0.355215597577748, -0.550939707199183, -0.33648457701367, 0.479446702920683, -1.17822281667817, -0.908659786991288, -0.724692013381181, -0.431888271899474, 1.01650279941969, -0.202766425390474, 0.785905869092135, -0.163050436529736, 0.72764151824788, -0.0017167587024862, -0.0022157418584732, 0.129194628697034, -0.531097752272958]', 0.69); -insert into credit_card_transaction (time, features, amount) values (157761, '[1.40694462058248, -2.22964420616156, -1.09278924554608, -0.521431442785045, -1.25080215662766, -0.145204768685222, -0.441124278894459, -0.0812557289632714, 0.211282577839796, 0.468449312292831, -1.05534025997817, -0.667440778259441, -0.490713485359621, -0.309137851540799, -0.241440792750203, 1.00508435713639, 0.418800533413061, -1.70792073070499, 0.78011860545921, 0.730794486274656, 0.0095547290122769, -0.84834440789254, 0.115268189565705, 0.567331085956412, -0.627161900049319, -0.635384902508728, -0.0716444352603119, 0.0290791235794769]', 403.48); -insert into credit_card_transaction (time, features, amount) values (157761, '[-0.401122422432255, -0.645339960165853, 0.0915963509265908, -3.31662642631649, 2.03008831501402, 3.90081139626387, -0.124758562493921, 0.813170426774759, -0.798233272590948, -0.118267596569829, -0.24322379818147, -0.978460792265181, -0.140870122605956, -0.611342374486742, -0.09413022967914, 1.18331663128513, -0.332136026863269, -1.169642821445, -0.324964079485232, 0.327934203295884, 0.385193222800044, 0.983422473423648, -0.107336115640603, 0.724395613925288, 0.02425207045917, -0.193409887623481, -0.0559692497362724, -0.0898981718065926]', 89.0); -insert into credit_card_transaction (time, features, amount) values (157761, '[1.70549287992302, -0.776769851154756, -1.269321913709, 0.193283838194526, -0.18941776641426, -0.411500022692395, 0.140278014498494, -0.142612548607431, 0.790211108372426, -0.131043037576414, 0.561370173341116, 1.35234409712455, 0.205757307188476, 0.254033378410913, -0.927709356641389, -0.273981225749802, -0.425864520181834, -0.148659817772306, 0.642261157640657, 0.11586753552278, -0.0297770086494233, -0.197822780702313, 0.0250761451731495, -0.364332749618645, -0.114156009804988, -0.100753282589354, -0.0535844740592357, -0.0414337906923724]', 164.41); -insert into credit_card_transaction (time, features, amount) values (157764, '[2.1175155782907, -1.11663247596756, -2.2302878588157, -1.98034002010056, 1.60353246599434, 3.34578886250108, -1.17050146507107, 0.837020898369754, -0.463790988167026, 0.760909132933108, 0.230911381352299, -0.3472023221394, 0.0817113559187495, 0.047503131605078, 0.697017465171006, 1.09409654181243, 0.053715276269157, -1.999946168881, 0.622025169270762, 0.101041804799662, -0.240856650468923, -0.943249384387394, 0.501566369683911, 0.641977508817422, -0.527746218925885, -0.670975082353041, 0.0015298525834196, -0.0454870254824985]', 39.95); -insert into credit_card_transaction (time, features, amount) values (157764, '[-1.30989059677414, 1.12980327572915, 0.441725781261312, -0.316067183384615, -0.753078173327791, 0.139722555312353, -0.883371840005158, 0.251249945812242, 0.575485443335261, -1.05559536288271, -1.89117130004564, -0.557081509676945, -1.50534156809895, 0.683612756139346, 0.489841289333537, 0.218167442861782, 0.0287942119084175, 0.84373675120483, 0.191045063431661, -0.571350397854006, 1.16538560368293, 0.695205033807072, -0.0321006105602973, 0.644284207832754, -0.253610962005684, -0.148318943098187, -0.137651443771097, 0.0349502998053518]', 9.95); -insert into credit_card_transaction (time, features, amount) values (157764, '[1.96168886365896, -0.0228905791480068, -1.81956766671407, 0.605365686281901, -0.0054948676190132, -1.5652948499908, 0.245925701512115, -0.327583095814266, 0.800732913460871, -0.493734836383028, -0.557510075341906, -0.723631300420433, -1.45515625526132, -0.531886059536065, 1.07311761562143, 0.307636523447466, 0.564281440561343, 0.730176787626216, -0.456946507715766, -0.198696834567125, 0.203000673387383, 0.583347524573185, -0.0460066359361122, -0.0635872915782479, 0.185293797337547, -0.104233871821745, -0.019703249051687, -0.0234773131254189]', 51.65); -insert into credit_card_transaction (time, features, amount) values (157764, '[-1.24408848958698, 1.19330632520761, -1.19635638526568, -0.168698167981753, 3.20348483542277, 3.62516663905731, 0.43212868247803, 1.23732426531801, -1.17622634555151, -0.30698692682965, -0.421755300569328, -0.0590431987713753, -0.422845756572558, 0.956501653798671, 0.245497178313069, -1.11597558271264, 0.314007277995835, -0.370367867076112, 0.834382394202446, 0.099246194259702, 0.0593075084841779, 0.112485890832437, -0.576128289794068, 0.728286525022359, 1.22087694806438, -0.161937834991758, 0.0745908720937911, -0.0046648865197071]', 19.12); -insert into credit_card_transaction (time, features, amount) values (157765, '[2.21734135992744, -0.988823844878078, -2.60643504663937, -2.02269347283352, 1.81733862530113, 3.16650688706906, -1.07694334578937, 0.732534918485502, -0.611046856597784, 0.881106027037353, 0.161659516476641, -0.57133579392004, 0.0780645293858644, 0.162916827005175, 0.827917211894789, 0.921990505112027, 0.0189876950808539, -1.44369894700329, 0.304113364825286, 0.031259645334043, 0.34595602029342, 0.905648238529103, 0.109967126755195, 0.745062187685227, 0.105037562025788, -0.0157633632639415, -0.009606430433617, -0.0685059262213962]', 5.37); -insert into credit_card_transaction (time, features, amount) values (157765, '[2.02474001199973, -1.04195921618869, 0.240668717474758, -0.417004602762899, -1.55363320107346, -0.340650650908301, -1.30683824786951, -0.059122860943785, 0.0677928111987486, 0.79385286667541, -0.832432824646551, 0.69533042600112, 2.22398524543296, -0.746864357433475, 1.18544880018828, -0.491384534831706, -0.798937860617149, 1.64298213992037, -1.37062654735142, -0.385600953949756, -0.0920158826773632, 0.298817268433962, 0.279405417422832, -0.0692194183538692, -0.662983361241718, 0.599295531685619, 0.0239562682798574, -0.0210275953423005]', 45.9); -insert into credit_card_transaction (time, features, amount) values (157765, '[-2.36249893049779, 2.18652491201584, 0.0330869364420679, -0.952162083735602, 0.598479014534101, 1.49417117857969, -0.820652969460173, -3.70344863256169, 0.195603957686532, -0.0086798130551488, 0.150725825780715, 1.60067612447849, 1.19811076312405, 0.0533424827453554, -0.636337482247557, 0.0262667795701083, -0.461270412659054, 0.112405351075221, 1.1882148974455, -0.914347039579022, 3.45567765074469, -2.38163198521093, 0.494036750132676, -0.311692503509576, -0.17960891565747, -0.793594976492951, -0.491144259234298, -0.129088806269323]', 12.99); -insert into credit_card_transaction (time, features, amount) values (157765, '[-0.228741091321712, 0.694153443914698, -0.791232524212178, -0.929637300935088, 1.88417030489634, 1.21197483188609, 0.567927138447901, 0.478190109588234, -0.147375221399862, -0.67235194853831, 1.06036785686647, 0.443567916522042, -0.344114631006769, -0.747466317874685, -0.0780803212328297, -0.160021125877982, 0.732612868267374, -0.831640211180586, -0.763777512680677, -0.104103868884938, -0.245614316569418, -0.508031449750812, 0.240274235610244, -1.06050131633507, -0.574463794093697, 0.218103156784518, 0.150053421906105, 0.0386370668648158]', 4.86); -insert into credit_card_transaction (time, features, amount) values (157766, '[-0.536936892625797, 1.12873968672868, -2.18683861650872, -2.3947487266753, 3.74597388045451, 2.77304915682203, 1.46934944014706, 0.198257237649481, 0.313032990338939, 0.887241898400141, 0.102475656430612, -0.277539631826574, -0.529640915976931, 0.296937398684015, 0.351415725713019, -0.962592229745583, -0.719892136158157, -0.604207711043482, -0.339360635534281, 0.507733528465347, 0.0436167750436431, 0.789218352956048, -0.224979024632596, 0.743067172153576, -0.163175356615064, 0.0674699471166676, 0.408460422708027, 0.0032633063709663]', 7.7); -insert into credit_card_transaction (time, features, amount) values (157766, '[-1.02152956085945, 0.336425145137876, -0.313553263385373, -2.37574074632575, -1.24365276004966, 0.25072928007119, -0.479263773057312, 0.910717504560184, -0.789249144668004, -0.0861725498597684, -0.572896643127133, -0.363761155508383, -0.0836958283477829, 0.0598091934281915, -1.1521057990177, 2.18782899237745, -0.466857216681224, -0.170132197777258, 0.31677566113259, -0.192849979100279, 0.366116021961412, 0.814691254502319, -0.256614347607256, -1.01829855377451, 0.309920150079677, -0.132524476187797, -0.232437087988535, -0.191173238902927]', 119.97); -insert into credit_card_transaction (time, features, amount) values (157767, '[1.44596827015342, -0.967781351868595, -1.45052837548686, 0.859817986090373, -0.314611724834731, -1.03451471947213, 0.713042867716276, -0.470117187930282, 0.384601111304867, -0.0902765207823949, -0.683802476267424, 0.941012053085625, 0.933540925050364, 0.150203612318974, -0.298497933226988, -0.276476616968022, -0.247571086525235, -0.900238859984641, 0.0389941203971027, 0.457884035586482, -0.0409693487055782, -0.611986036326481, -0.0112993831159174, 0.0914627236314364, -0.241331359081248, 0.0559144286062685, -0.113001746193072, -0.0036653972797491]', 317.5); -insert into credit_card_transaction (time, features, amount) values (157767, '[1.99978188271239, -1.38106930564719, -0.510861091272523, -2.0334754298425, -0.817134643662615, 0.731285945721773, -1.16423827559669, 0.268414131815501, 1.38908881781905, -0.513332926146742, -0.386251771271756, 1.74748777966454, 1.30880640066818, -0.700658610339719, 0.242186558412984, -3.50840044413948, 1.29663521662554, -0.858386689124356, -0.634919772398698, -0.65061098961387, -0.635277574546991, -0.764227923660287, 0.509443120589639, -1.04269213223713, -0.618404149446524, -0.467387544686102, 0.130478795615713, -0.0443650156202587]', 9.15); -insert into credit_card_transaction (time, features, amount) values (157767, '[0.086425106619282, 0.900032642421067, -0.358129728643541, -0.625827628318449, 0.833285800838617, -0.522932863536605, 0.788003376137708, 0.118301220595865, -0.145030997053161, -0.641489464184196, 0.972461060906132, 0.0234535335960295, -1.24452866506912, -0.580632861822568, -0.776102448827641, 0.487809510777499, 0.281032961543489, 0.291091160971207, -0.0494196163843757, -0.0526716531419151, -0.289089643609941, -0.769640915104281, 0.119523023251071, 0.569886482808782, -0.520803820326821, 0.0961276399165735, 0.214281526167865, 0.0723993197254135]', 3.87); -insert into credit_card_transaction (time, features, amount) values (157767, '[-0.528298223645267, -0.731875642714243, 0.0178543165502528, -3.4910795918773, 0.853715479730304, -0.596612102169269, 0.0981282849859685, -0.022334028448117, -0.23732224578101, -0.708153693457288, -0.797961026021, -0.162965234096047, -0.459914517077923, 0.324375749658801, -0.280650827893792, -1.8774694642922, -0.600028323277671, 2.55465599667619, 0.652943761494375, -0.353736123644125, 0.0071644185832811, 0.487273284646131, -0.522417351147492, -1.35478446297144, 0.558119179490178, 0.162605532681229, 0.0954202515621783, 0.102829359269462]', 15.17); -insert into credit_card_transaction (time, features, amount) values (157767, '[1.91063965823867, -1.12820485024079, -0.794145211838559, 0.297596684602411, -1.00485718522828, -0.560816933295807, -0.393137877086222, -0.188708712358157, 0.207397810631509, 0.638946493340846, -1.61061811516296, 0.35464422560531, -0.109596143999008, -0.401732138754131, -1.80422238642467, -2.27700097132786, 0.551392037660403, 0.262110056612717, -0.208856659952647, -0.471477772869692, -0.676112328650823, -1.32260451760321, 0.272120914217736, 0.0228504525002846, -0.293902191779638, 0.0877073042282331, -0.0249263159662662, -0.035345281816463]', 108.9); -insert into credit_card_transaction (time, features, amount) values (157768, '[1.83021175323441, 0.0152069672643427, -2.337987428892, 1.17606911527496, 1.00728618638305, -0.874858328889014, 1.11264192547235, -0.562675866580313, -0.401655965885629, 0.276676251999552, -0.84560550220951, 0.678838781192171, 1.22564592848784, 0.598545546316918, 0.0720918356915171, -0.410685883465121, -0.615007090362719, -0.401477390442439, -0.312609959816499, 0.128320855141149, 0.2494033398132, 0.540885516556525, -0.208752897926926, 0.525472970176081, 0.613520470159627, -0.452394555852107, -0.0621459464856337, -0.0347463364019328]', 150.0); -insert into credit_card_transaction (time, features, amount) values (157768, '[2.07148002858802, -0.643035393459739, -2.59335508082669, -0.398018505419956, 0.267754188032876, -1.23944320818091, 0.551255819226493, -0.466540216859111, -1.03476959056667, 1.04756650721258, 0.226693704045929, -0.124641254042228, -1.23941849872157, 1.19880417453292, -0.759988216402427, -2.2433346505424, -0.078821883309461, 1.50061431277259, -0.342761955659529, -0.595440943188553, -0.0019115227017201, 0.431267975970345, -0.23578935281118, -0.390004613573788, 0.644798870225799, 0.126206360261486, -0.0745242507924395, -0.0840601472188135]', 80.21); -insert into credit_card_transaction (time, features, amount) values (157769, '[-0.654893457602449, -0.504969417750165, 2.08852462863789, -0.692081273411401, -1.99432552649763, 1.03533540953023, -1.03860994444184, -1.7882274597354, -1.53681301435887, 1.4773042682504, -1.24789793325666, -1.01294020173202, 0.621546642790805, -1.22212913002582, 0.327444475381256, -1.76898815726383, 1.76964675353452, 0.239060159131878, 3.0274197236083, -0.384622425125271, 1.00365701809442, -0.950172790165122, -0.352600795956006, -0.0946933663432337, -0.113930247167629, 0.201831976959137, 0.265017235009149, -0.187115088634017]', 176.13); -insert into credit_card_transaction (time, features, amount) values (157769, '[2.10677907595987, -0.119487773947513, -1.65276702993052, -0.0684392716869086, 0.72317054798864, 0.15432681276692, 0.0168147320210374, -0.0360837744269233, 0.306746735743554, 0.229391996511042, 0.0166036273004197, 0.857335218488859, 0.429323797932416, 0.344588115171379, -0.563674618188453, 0.421887614003072, -0.968818851232465, -0.0158399153899857, 0.852440662310147, -0.133342260306259, -0.31181329173802, -0.822209454281445, 0.227948167524799, -0.345095761881023, -0.157492801101496, 0.210359823345141, -0.0792818395388925, -0.0735725230598602]', 1.29); -insert into credit_card_transaction (time, features, amount) values (157769, '[2.31215661584518, -1.40036098579913, -1.09906188853936, -1.67884149524907, -0.966691773178993, -0.350210581537372, -1.08150490735582, -0.247101917064222, -1.17686243079677, 1.52295180968422, -1.77393893238389, -0.573102149496182, 1.62585310802493, -0.754227811090483, -0.158839511812196, -0.244882397804565, 0.0381553547513713, 0.168134925252155, 0.189485022007953, -0.295320135055354, -0.200778942097377, -0.0426424916506479, 0.0932433132652524, -0.994819511082684, -0.0890488350507472, -0.121408565045101, 0.0220127748438834, -0.0506505427129274]', 39.2); -insert into credit_card_transaction (time, features, amount) values (157769, '[-0.98560150441428, 0.0027488429299808, 1.06398763313686, -2.93741998475694, -0.966534532472438, -0.928180918496612, -0.296156196043063, 0.355769372472044, -2.25370181420864, 0.397292158322868, -1.42034818687006, -1.45139916962189, -0.490115405947989, -0.0904981188342745, -0.716188135153326, -0.374637836388364, 0.581737115273459, -0.027376643610651, -0.780769860884393, -0.368406694636751, -0.173621169056687, -0.195741431847051, -0.326487494513507, -0.05778834345978, 0.756139210150513, -0.0963683707021458, 0.134642617991058, 0.0270221470189308]', 15.0); -insert into credit_card_transaction (time, features, amount) values (157769, '[-0.832518298863673, 0.779776243218614, -1.25021769149223, 0.961168379063914, 1.93679892041653, -1.19328782988975, 0.845158865908254, 0.0706803968453716, -1.23824382522858, -0.0062286526330954, -1.16245367190074, 0.490039718266208, 1.25663827558915, 0.914221198488144, 0.180629025683277, -0.775171509715917, -0.146259103831779, -0.0662089028429569, 0.640991832388317, 0.301384998669512, 0.389279156438792, 0.95396381857295, -0.21373248279491, 0.535825596604664, -0.220067889657704, -0.413149049450775, 0.373665734168359, 0.0943797392282521]', 3.6); -insert into credit_card_transaction (time, features, amount) values (157769, '[-2.24225907610366, 0.977886673797831, -1.22136121561303, -1.91164763208369, 2.92000132625384, 3.41693631151189, 0.287434910569005, 0.761615914295969, 0.207793947926626, 0.430248601163945, -0.158192744778151, -0.0103354642255143, -0.320903268512022, 0.120033822318762, 0.209387155372166, -0.214302216025041, -0.463829350602869, -0.89815290716544, 0.0015722748020685, -0.517524715349068, -0.290602901102974, -0.720429751187986, 0.27567779167481, 0.701451965160183, -0.036015299838611, 0.132265813579513, -1.05160084839892, -0.161274287214602]', 4.49); -insert into credit_card_transaction (time, features, amount) values (157769, '[-1.2221713764659, 0.256941101585903, -0.743576255788344, -0.601450809623127, 2.4756253937397, -0.768980289437268, 2.08519893250867, -0.221183811784298, -1.32773901054289, -0.7220193253328, 0.0272323899598352, 0.0693511147515338, -0.830731252709335, 1.02292781258321, -1.90873380604927, -0.122744517207548, -1.0721971135384, 0.264200877792174, -1.14931582354859, 0.14299419329196, 0.50681432700793, 0.98057983240688, -0.311384871205432, 0.229188047898153, 1.30658218342333, -0.391158707195793, -0.059376548670031, 0.0978467781656899]', 137.54); -insert into credit_card_transaction (time, features, amount) values (157770, '[-0.812429320224001, 1.01094065506864, -1.22323553988197, -0.319925505906064, 4.16993705425487, 3.21989130036166, 0.800424413193717, 0.824317820458038, -1.17795923808752, -1.15659480780641, 0.0776764992766642, -0.480303274122247, -0.641185168738057, -0.829568714054231, 0.0508342884666212, -0.516964150269976, 1.08644291843415, 0.19915410528376, 0.0228717890396188, 0.110572089496427, -0.0141152138482455, -0.117176016387318, -0.573328357231758, 0.586956111590302, 1.25379125432594, -0.264792519161459, 0.0505003491334401, 0.0761262644996659]', 1.0); -insert into credit_card_transaction (time, features, amount) values (157771, '[0.124285182683536, 0.621313516802911, 0.350324027154793, -0.998057583130743, 0.520489298431237, -0.353371956029866, 0.469196209606206, -0.891390678398288, -0.307398652962039, -0.621586459245705, 0.587906819480283, 0.931623598798837, -0.0826423948246412, 0.470920781576085, -1.17892410952888, 0.426387123073848, -0.850344724417253, -0.417858245962064, -0.394843256504613, -0.343103939319599, 0.473240431842171, -1.12580857951561, -0.182608034210562, -0.399055991484434, 0.915010313947072, 0.232855311864915, 0.0576369964961767, 0.179180014402578]', 1.98); -insert into credit_card_transaction (time, features, amount) values (157771, '[-0.242990654164359, 0.640742823570124, 1.00165439830479, -0.822652932678954, 0.84558966048581, -0.21138764956955, 0.957826044524522, -0.250031630632065, -0.10575091123484, -0.542821347036603, 0.539499045144091, 0.97056336348757, 0.781834905462046, -0.158656243510937, -0.764025845259596, -0.208713315184926, -0.937673937296159, 0.657149482437415, 0.0542891562586466, -0.0049543680482565, 0.431838598694042, 1.61971093967762, -0.561585227500615, -0.304102255831023, 0.183391913169409, -0.0396757235859728, -0.0062036655976332, -0.0828611499443617]', 1.0); -insert into credit_card_transaction (time, features, amount) values (157772, '[1.31017480351477, -1.34463362363981, -2.13841767197186, 0.610094475184947, -0.412229024092678, -0.755911572302768, 0.279784998895369, -0.0400181586177361, 1.2155656482334, -0.741152491080024, 0.405338330641871, -0.73481892670317, -3.20303846507807, -0.572248959400507, -0.425405982234838, 0.45817094636224, 0.902172376877998, 1.06194764710972, 0.440674580545006, 0.361808133132404, -0.0354075927229494, -0.828468730435045, -0.142090624340617, -0.517739190017701, -0.287125493332519, -0.115937241277087, -0.0983546900626692, 0.0207458635426034]', 365.91); -insert into credit_card_transaction (time, features, amount) values (157773, '[-0.491711818696287, 0.534614466216042, 0.801587359080287, -2.25190653017621, -1.16736521547938, -0.218524878215519, -1.17231908620379, -3.21271110354301, 1.57431297826684, -1.24875448961119, 1.43833237166925, 2.12610554967541, 0.524117103252259, 0.0462122543072184, -0.133160554497505, -0.389566134565573, -0.210055440174481, 0.556853288423154, 0.258460915946185, -0.749468599916947, 3.37960379836302, 0.169932521631683, -0.0133275965242375, 0.604530093223917, 0.819350075028855, -0.0290593306772211, 0.348824864675366, 0.257084004095118]', 30.31); -insert into credit_card_transaction (time, features, amount) values (157773, '[2.0599937503275, 0.0355488102271656, -1.69847308189705, 0.447573486628858, 0.254865658468333, -0.889713872963706, 0.0600810766893002, -0.122942939283161, 0.735524976185817, -0.338016005434688, -0.714079190036607, -0.678416199495559, -1.6034190902689, -0.505481778399181, 0.426328403214653, 0.307532972835965, 0.658180215739715, -0.225415738505961, -0.0430830889905617, -0.27258947427812, -0.372020093494021, -1.03813778904758, 0.378703926164148, 0.533972862782642, -0.341145201602175, 0.176557519300914, -0.0700032604061238, -0.033746549328102]', 0.89); -insert into credit_card_transaction (time, features, amount) values (157773, '[-2.46066235781784, -0.134260617371438, -1.64608133812644, -0.485049444169885, 1.13843816735465, -0.493130984360106, 0.976381643565979, -0.110134773943901, 0.3604456995989, 0.275240349643357, 0.0815082547074308, 0.513358872635824, -0.460554660788366, 0.423908929381195, -1.11279765633499, -0.156506737291257, -0.672055956515526, 0.140807802598068, 0.653163998795033, -1.37758569756536, -0.226354310213918, 0.34222638500683, 0.242484209495559, 0.241107489527039, 0.48952718920409, -0.377624950790441, -1.28852956108261, -0.443494617639548]', 71.99); -insert into credit_card_transaction (time, features, amount) values (157775, '[-1.51787229288128, 1.89601597169539, 1.08803947675864, 0.689745988369079, -0.293459936611964, 0.315342515617058, -0.259726742242229, 1.05150253689085, -0.512157031426186, -0.482111628081265, -1.28214288095566, 0.471790495853741, 0.896301657672484, 0.335454796464131, 0.753107171666111, 0.145411947450018, -0.0475694327752496, 0.183734442637367, 0.715834581137235, 0.154769893602717, -0.194721850894505, -0.585940874906564, -0.123096943110259, 0.526648157153492, 0.498110570224223, -0.656244307470282, 0.21441600250756, 0.0905057901084075]', 1.0); -insert into credit_card_transaction (time, features, amount) values (157775, '[-1.35631757734504, 1.55650247881456, -1.83155808432243, -0.737107497251717, 0.822091233696138, -0.133242338211227, 0.442238408681287, 0.546426023550378, -0.378576140842973, 0.064692793780443, -0.335981643984759, 1.00806295492729, 0.570816779482257, 0.688531898419825, -1.54964789933688, 0.0841975508565132, -0.547707708124814, 0.0903187076919027, 0.57070597718863, -0.388329648903317, 0.138911138179736, 0.179375784011847, -0.0626948032717035, -0.0933471487199331, 0.12408719211319, 0.251553264379651, -0.831383174732354, -0.0689882418601661]', 14.95); -insert into credit_card_transaction (time, features, amount) values (157776, '[-0.483629039672677, 0.862919343428215, 0.241969230306266, -0.785441760986528, 0.427981560738354, -0.787576862182843, 1.06808034766767, -0.206278838255919, 0.351421462401874, 0.599191666340965, 0.89603893745247, 0.351590452139754, -0.950416420246187, 0.130918104595845, -0.896827140899966, -0.0498459459317213, -0.652963902994332, -0.324376971493484, 0.0618234413974431, 0.190307376232285, -0.339269120006709, -0.496306277363497, 0.151789235330665, -0.0185433136120239, -0.472956756917792, 0.109604324311234, 0.318189299839678, -0.0226858177003829]', 21.99); -insert into credit_card_transaction (time, features, amount) values (157776, '[2.12235345137732, -0.590116036627406, -1.35125081044742, -0.330565288598222, -0.467085628600007, -1.29288906259661, 0.0346033821373705, -0.479632204910543, -0.73427294431221, 0.862830726147338, -0.856820268734154, 0.448003786093931, 0.989909143442742, 0.0944840712058948, -0.281778213092926, -1.80481687129195, 0.0073501375003945, 0.39910583600684, -0.610811832591531, -0.508364741113887, -0.49637218815194, -0.858987613790647, 0.265824292385938, 0.0573870872907708, -0.206568543964115, 0.471945846077107, -0.0702951930329385, -0.0567715704560007]', 40.95); -insert into credit_card_transaction (time, features, amount) values (157776, '[2.08494970576506, -0.350520653731993, -0.708783601505585, -0.0709942367123143, -0.249444003029974, -0.222100250449101, -0.494786145421839, -0.0488541750505503, 1.36175685642087, -0.210628763092686, -1.66155916746123, 0.315050183415565, 0.537106050349446, -0.171950001653259, 0.72676082867972, 0.502210335618381, -0.892504925497154, 0.031446896116047, 0.423359658417319, -0.173698078452189, -0.262775165326991, -0.573536093596569, 0.262426807108464, -1.0228692974591, -0.352104585883983, -0.221802570044444, 0.0109086796631646, -0.0470074534292583]', 0.77); -insert into credit_card_transaction (time, features, amount) values (157776, '[2.09390858165358, 0.53401185029948, -2.91531234609916, 0.326214432532907, 1.33883326834842, -0.601534842835176, 0.267619528846932, -0.182491703530762, 0.255959522738661, -1.05337498207531, 0.810535711317133, 0.322869703221466, 0.694755780535056, -2.52670388487826, 0.456073493953549, 1.28414677040754, 1.0426426006074, 2.05689383215808, 0.0343607354792053, -0.0683086403386421, 0.0709972308706514, 0.413801551095538, -0.230228264253773, -1.6445166599445, 0.415637690784945, -0.0314731412132949, 0.0213549677806067, -0.0156481366162287]', 1.0); -insert into credit_card_transaction (time, features, amount) values (157777, '[0.258839065931237, 1.1453971320348, -1.57573269457828, -0.45300092427609, 1.13293192656881, -0.269520574963918, 0.455688213924199, 0.333666102250306, -0.415155350819534, -1.0118162930503, -0.0062858202328939, 0.481708589418944, 0.763384283018362, -0.62344775425611, 0.0396604258591865, 0.75003680173846, -0.0597849403509406, 1.53696423233389, 0.46779224766643, -0.172033539850104, 0.291105530387901, 0.863549010613311, -0.295847639480309, -1.50680307363793, -0.11416885136106, -0.0805547294325712, -0.0629772132992395, -0.0684703785402445]', 1.5); -insert into credit_card_transaction (time, features, amount) values (157778, '[-0.102318316251731, 0.355017385142264, 1.4729474047879, -0.267198469329434, 0.432963313649831, 0.864003581834754, 0.170403656950197, 0.0894079849153625, 0.0681694778015647, -0.13876815989909, 0.390471602207007, 0.544438214826899, 0.521064806462692, -0.399394273993292, -0.0070969403901762, 0.0413247041760433, -0.646901948601163, 0.810754270786257, 1.17321864421709, 0.20274326752777, 0.231805215223454, 0.980055521347744, -0.252267747871963, 0.304314999476033, -0.513289491251356, 0.658486645372563, -0.0088058684746288, -0.060263478647028]', 11.5); -insert into credit_card_transaction (time, features, amount) values (157778, '[1.78885650099444, -0.620721353303257, -1.29683818060075, 0.312485231876388, 0.160377841021539, 0.19213061552681, -0.0826120878409747, -0.0104654682919457, 0.451343753398159, 0.180742863611422, 0.351711310538646, 1.04488788405264, 0.609121115143989, 0.196760470711252, -0.392266646646408, 0.480538783404065, -0.949356935866486, 0.307028283308058, 0.359981850445685, 0.137656758835248, 0.0530947014703609, -0.0287710389485747, 0.046576215739148, 0.130467585542789, -0.158868745309286, 0.196682086122215, -0.0703939656537369, -0.0391379534370372]', 135.87); -insert into credit_card_transaction (time, features, amount) values (157778, '[2.06909916910199, 0.0397052035519738, -1.06398693868696, 0.389424959053613, -0.0036415044140161, -1.14985812850832, 0.265593848041661, -0.400595677414124, 0.360015730537229, 0.0377184852461005, -0.586878624187003, 1.07099011665861, 1.30196708505851, 0.0272995760690688, -0.0902466627413603, -0.144927176995156, -0.380598420791667, -1.00029649221806, 0.161291828303106, -0.145422282146189, -0.279690652576102, -0.609487261059133, 0.31858653504276, 0.0093056222660505, -0.248349374596992, 0.195725014727467, -0.0626385646269889, -0.0580654173897355]', 0.99); -insert into credit_card_transaction (time, features, amount) values (157778, '[-0.280386396725125, 2.05643735762257, 0.534355930918401, 4.18256033813513, 0.914642401455556, 0.715753461101099, 0.538351359728649, 0.338910769066097, -2.75509007784256, 1.24137802040785, -1.24904654255637, -0.247220129209848, 1.55446248616795, 0.546797647532527, 0.838776137080321, -0.0421295629723352, 0.0423417610365934, 0.300246569601367, 1.14222589817074, 0.168664606962584, 0.337966688586995, 0.941663240063254, -0.299339986619958, 0.53320250153497, -0.042557735542329, 0.55670703594055, 0.039420864571738, 0.103543010269652]', 0.76); -insert into credit_card_transaction (time, features, amount) values (157779, '[1.0134097597409, -1.9655360588414, -0.708390208894882, 0.78124302946241, -0.194990511214701, 2.36979931220596, -0.581227199737161, 0.632245206200184, 1.47045899796536, -0.516921847812739, -0.357660982930948, 1.09700220655567, 0.282846558500045, -0.403394496130535, 0.480205938337181, -1.20073431686844, 0.985619728378594, -2.35268009309668, -1.4600415689333, 0.499063633916394, 0.102240451946455, -0.167278676850877, 0.156677992105042, -0.655062378629036, -0.898178946869809, 0.112042163128384, 0.0023162637562903, 0.0225791241904814]', 406.5); -insert into credit_card_transaction (time, features, amount) values (157779, '[2.03677345725835, 0.193002540918551, -1.75160139762415, 0.54922211534906, 0.18191310139789, -1.46146921927532, 0.257708311470463, -0.359733982392946, 0.619170569916506, -0.518355848745698, -0.306505743754919, -0.0562179477206154, -0.238917061163892, -0.780443127329162, 0.981247727886079, 0.188504094327591, 0.546085695896767, 0.484974419641384, -0.520129596065853, -0.213010033011811, 0.196540228077392, 0.728276767612655, -0.017493670483, -0.0917350122553697, 0.228576764241645, -0.0981131052530824, -0.0004629771078641, -0.0289234351047625]', 8.98); -insert into credit_card_transaction (time, features, amount) values (157779, '[-0.953238505615653, 2.01717562940193, -2.16473841210852, -0.590454640775954, 0.450714815121361, -0.684606111148835, -0.422010485683253, -2.1476043732179, -0.893040890683037, -1.05089177491157, -1.47208397708579, 0.775888968278593, 0.306591591088268, 1.30680464378162, -1.03888670278806, -0.202829941659205, 0.117920147436086, -0.538876393285495, -0.286637497058584, 0.366296835496053, -0.982736814549142, 1.13839618665175, 0.198279944588642, 0.728502527744802, -0.326472430753723, 0.503456319711958, -0.154502789382597, 0.0494869112409223]', 0.76); -insert into credit_card_transaction (time, features, amount) values (157780, '[2.06781851306589, 0.104644818473505, -1.88233553368743, 0.186163653052426, 0.719439201649155, -0.276746819297447, 0.0780962269578774, -0.0639645409492846, 0.25229724103368, -0.211215868042542, 0.714489892750947, 0.729101792575819, 0.165676782665825, -0.697226186149773, -0.470997224178535, 0.64937272645024, 0.0406792521941312, 0.333277045739408, 0.524690552764447, -0.124251806292644, -0.34410214116787, -0.912771001091965, 0.266898474215621, -0.0006499218590423, -0.219808198103312, 0.184000484438893, -0.0663427438499875, -0.045463192563996]', 0.99); -insert into credit_card_transaction (time, features, amount) values (157780, '[-1.00095108167436, 0.0926641167751102, 0.993433700312233, -2.37293470003817, -0.828563658732537, 0.447451717943579, -0.0306836712000791, 0.229574327294367, -0.716573063136824, 0.123317126768489, 0.65320676846542, 0.306755402265459, 0.861406145904901, -0.771620096732248, -1.36128080040317, 1.50862175205157, -0.369610905379354, -0.565533709315208, -0.034173917044718, -0.127527282854108, 0.574577495609956, 1.69848764543759, -0.245973485269818, 0.786508030681239, -0.233665344307878, -0.224636046667381, -0.465867239980456, -0.145249429858136]', 99.63); -insert into credit_card_transaction (time, features, amount) values (157780, '[1.80402729010402, -2.04594918249489, -2.03183687980939, -1.50208853905351, -0.896096915629037, -0.971482678299379, -0.117744364374252, -0.529923446606477, -1.76406653919984, 1.52379208510727, -1.37582485362172, -0.953328937904946, 0.834740156702428, -0.011332020539433, 0.151632174441215, -0.405426877640654, 0.184321218702203, -0.151580958715886, 0.0897142959038938, 0.213882804399551, -0.173125159325849, -0.758588070440682, -0.0683581935558691, -0.738910045975062, -0.181430394348902, -0.301734407697735, -0.0795126595948357, -0.0111360382367686]', 319.99); -insert into credit_card_transaction (time, features, amount) values (157781, '[-3.60040535002477, 3.37524390240019, 1.16154086116526, 2.16935186158576, 0.0972442717562901, 3.48277723336278, -3.49208787302232, -8.98538373659828, -1.010034161261, 0.716370984091628, -0.701455945783577, -0.295982297681442, -2.25665180292854, 0.791144257934859, -1.48954231088906, 1.75718070250106, -0.499024229491665, 1.16459520090701, -1.66334581247123, -1.03857031888746, 5.79689883638001, -1.6312361405125, 0.668684566928378, 0.0057695045405777, 0.613965220646946, 0.171247477194366, 0.304495914014083, 0.169033240489043]', 8.31); -insert into credit_card_transaction (time, features, amount) values (157781, '[-0.926530016935426, 0.552819758126288, 1.02101606870697, -0.56145619530983, 0.286978089461931, 0.429921046548355, -0.240833171475264, 0.779094719217207, -0.0889682025711841, -0.949481719104565, 0.129912320302033, 0.519085559886705, -0.369264909172681, 0.492552884286289, 0.0182917238995829, 0.402471482886646, -0.592370314465078, 0.72549363504051, 0.764600577427886, -0.0564104546495315, -0.0324616641790198, -0.342450227274175, -0.0493663571994011, 0.172209458445337, -0.28670858188023, -0.549882533672813, 0.029676237606545, 0.0293976301892023]', 10.0); -insert into credit_card_transaction (time, features, amount) values (157782, '[1.99420007382194, 0.20328209088513, -2.22167546044555, 0.919224564867899, 0.334464973210506, -1.20236454726703, 0.0854832415120591, -0.15169093557666, 0.908378668820332, -1.03308819688495, -0.201889286682662, -0.804728288805386, -1.78826429436411, -2.32900233159897, -0.164745181451475, 0.27107971396419, 2.28493581907534, 0.635223873172831, -0.457834732566073, -0.225464982597318, -0.0094999376291866, 0.192975393388965, 0.0561449983873097, 0.907103269554833, 0.109863848070806, 0.677949478246875, -0.048309398780374, -0.0044567957004425]', 12.31); -insert into credit_card_transaction (time, features, amount) values (157782, '[2.06611903766989, 0.146688440940063, -2.77421847595329, 0.284229499356089, 1.08884530586723, -0.736788000680978, 0.549751660944165, -0.212392867022638, 0.0362142805102205, -0.174926365627297, 0.684277478770633, -0.0661757510162737, -1.30999300941029, -0.321066017856137, -0.660527649495515, 0.114580030676273, 0.589319219530518, 0.514893253061197, 0.337266642486896, -0.194768736004111, 0.0475876530702867, 0.202438262493501, -0.0791494940933178, 0.163797400828135, 0.369052099496389, 0.695610417284528, -0.114180681306726, -0.0643989958084699]', 19.0); -insert into credit_card_transaction (time, features, amount) values (157783, '[-0.979878896179132, 0.522062312058962, 2.01258301551263, -1.18247142890477, -0.0170107201378416, 0.127065921655057, 0.376307500997139, 0.217552591188037, 0.104611886870281, -0.876101264409803, 0.122635490710218, 0.13327557076538, -0.708813119843512, -0.13586137394032, -0.840978782361654, 0.798200430969386, -1.0323014880277, 0.758217704127001, -0.394200712799383, -0.145651159958244, 0.19794762160684, 0.521943775921732, -0.511829845422854, -0.297383447825327, 0.575334423118718, 0.597587947747104, -0.15037960011195, 0.0398573096008523]', 28.75); -insert into credit_card_transaction (time, features, amount) values (157783, '[-2.43447321443071, -1.49630092261442, 0.988123243933406, -0.185585085496054, 1.63736629712212, -1.28897188033997, -1.05892166209401, 0.69146823642827, 0.163342353480053, -1.16002343833251, -1.16989599544257, 0.330159212543466, 0.25895474896615, 0.386305625295146, 0.734111911105668, 0.438046735243131, -0.52953227760122, 0.15030146155238, -0.333336569232114, 0.475120614509924, 0.166263692749722, -0.493615282569969, -0.0437927036812678, 0.579518966227291, -0.295912368361664, -0.660305404762921, 0.0638148727303666, -0.278890680506288]', 1.18); -insert into credit_card_transaction (time, features, amount) values (157783, '[2.07439814554867, -1.20023839307236, -1.00340453983047, -1.15969893963708, -0.473629259057983, 0.497460234902729, -1.06717287054404, 0.123267623806147, -0.152889132789216, 0.856702417553234, 0.193237104707309, 0.142038244838355, 0.769863559805138, -0.430140926075868, -0.41684473448237, 1.55715196616014, -0.427964325276582, -0.466826410708433, 0.984986838831341, 0.175708428553576, 0.356062279085961, 0.948192103751228, 0.0010807285451168, -0.156149082121745, -0.0638014486663824, -0.0890644644358541, -0.0039194555651569, -0.0496404707551375]', 69.0); -insert into credit_card_transaction (time, features, amount) values (157784, '[-6.83494585506618, 6.01149920932796, -3.76166846494157, -2.38438038184422, -0.951454362918691, -0.69396007181302, 0.0287032185130134, 1.11973462385617, 4.66964640367519, 7.81788492428989, 0.19585355042168, 1.12951707766327, 0.461511601028897, -1.17332423240942, -0.695849051709386, 0.0494065408354222, -1.01078774988656, -0.303315732077302, -0.213116414489196, 3.28374639857706, -1.07213122092326, -1.21565617827113, 0.314256724601256, -1.20985429374393, 1.05034170829954, 0.257051456492317, 2.30558304027547, 1.25372599380128]', 10.72); -insert into credit_card_transaction (time, features, amount) values (157784, '[-1.61453549413703, 2.23655506897636, -1.40665957136483, -1.07459976320833, 0.713048441531505, -0.412666008627848, 0.697932203774938, 0.257758782622531, 0.831307090790867, 1.26618973989421, 0.86969423441911, 0.85338194994886, 0.54029910203655, -1.29729730689968, -0.877221954184634, 0.513154755554104, -0.0400221598274527, 0.236200752589882, 0.0124765900635108, 0.773529114025214, -0.486337679659915, -0.863372486405895, 0.121597789108536, 0.166925261406621, -0.0885963882884426, 0.103197289248606, 0.418249661372906, 0.186256617321987]', 9.84); -insert into credit_card_transaction (time, features, amount) values (157788, '[-1.23806870081621, -0.241370910316551, 1.78951666107227, -1.95849788995376, -0.630024063624882, 0.1826377184353, -0.68559739383919, 0.599707185311589, -1.05219984102774, -0.625705991193626, -1.20483792716033, -0.242677147569136, 1.5731595832371, -0.759474973418733, 0.125146765617839, 1.68037468315652, -0.0737421236078771, -0.759565502043703, 0.413325963181539, 0.371066566625343, 0.359032963552828, 0.628953747373009, -0.205918640016089, 0.5177493434165, 0.499011592427931, -0.133753572686875, -0.0696998679487452, -0.0483639740316609]', 66.09); -insert into credit_card_transaction (time, features, amount) values (157788, '[-0.0538441777189786, 0.516663067015787, 0.702899966237766, -0.425265521708626, 0.0466963615380727, 0.151970801546386, 0.0979226019228953, 0.0479308608149707, 0.72430240930352, -0.266566681916688, -1.5719018814677, -0.330003472536231, -0.189827166107374, -0.326443219904772, 0.172896927417679, 0.206757051068396, -0.580001578083732, 0.387909419609355, 0.171883669721597, -0.14041993430956, 0.334035812950463, 1.0387651788967, 0.0387459363526379, 0.525989467303559, -1.37831327929168, 0.398160260245502, -0.007013475458329, 0.268774433185507]', 11.5); -insert into credit_card_transaction (time, features, amount) values (157788, '[1.90483460877212, -0.680000949574439, -0.516134896317598, 0.396361237267193, -0.619690794280313, -0.0645270298795779, -0.680489386135686, 0.0679856103737888, 1.350639076842, -0.0072222218139201, -1.28367658489775, -0.0627387235976184, -0.38349524510082, -0.157458684592997, 0.47762663827315, 0.438326046107281, -0.544215113179838, 0.179753226817695, -0.192474034989205, -0.080835502191962, 0.168845716888357, 0.51953624932574, 0.133577490755637, 0.620046481296115, -0.275699558240373, 0.541108591518084, -0.0386475470016383, -0.0333449831255197]', 64.99); -insert into credit_card_transaction (time, features, amount) values (157788, '[2.02060192477424, -0.113312080659788, -1.18116849832287, 0.230234223145931, 0.0979436411472906, -0.691716504228536, 0.119542569810986, -0.195272326402994, 0.235979527083364, 0.224522743893134, 0.746702427235777, 1.25566787298096, 0.47621521618637, 0.368030445885188, -0.683820453791438, 0.165316884917122, -0.679750102064181, -0.32153937361557, 0.549563712323539, -0.152055810665382, -0.246750499064289, -0.615445049665521, 0.273130862984504, -0.32339939116368, -0.275103485529216, 0.19562810759799, -0.0742163810614929, -0.0704639708833981]', 12.99); -insert into credit_card_transaction (time, features, amount) values (157788, '[1.97681586145809, -0.300227574124501, -0.202509829270536, 0.514450296628804, -0.688378361578636, -0.577494048178286, -0.498154426432531, -0.147607737908881, 1.2479949784983, -0.208564405031669, -0.751244885604305, 1.262623088601, 1.58765070285988, -0.491428774983362, 0.313639531784825, 0.0083111493676174, -0.587018056256389, 0.0205127939159817, -0.237851044829026, -0.127056989823762, 0.20988686289498, 0.936274049867588, 0.0995308658620479, 0.0866056783142172, -0.0451967504276541, -0.21912933157175, 0.0513469735634152, -0.0332469539016636]', 9.99); -insert into credit_card_transaction (time, features, amount) values (157789, '[-2.07189825678866, -2.89725197851471, 0.915629533410171, -1.03671855444872, -0.0615598270327908, -0.856319052934606, -1.86418245203782, 0.98893820487952, 0.0179712896745276, -0.206299713917169, -0.600998459503273, -1.24978947256139, -2.91337643219801, 0.152993940439434, -1.62757341889876, 1.44907395301715, 0.424859254000954, -0.380859073037304, 1.08037602445019, 0.577281686862415, 0.278026934451293, -0.329678654841556, 0.172955274048497, -0.41193745945975, -0.18795114859601, -0.37306535214685, -0.105579519748476, -0.438733444508114]', 101.79); -insert into credit_card_transaction (time, features, amount) values (157790, '[-1.33847170533031, -0.111245308432117, 0.321350739432179, -0.105076259064268, -2.74654469384719, 1.14779886831328, 2.37939175304536, -0.129443494240711, -0.0036474432747553, -0.581719463774343, 0.73225500347074, 0.0065964417418837, -0.668367515770877, 0.154513694170835, 0.229227555569845, 0.888000487683456, -0.842977026522441, 0.908178014533206, 0.0311094077957141, -0.502696107127784, 0.112415724077736, 0.840739034158676, 0.222557740208872, 0.126270742747746, -0.0423189404819935, 0.390042672122973, -0.179813204908807, -0.426970486753851]', 572.9); -insert into credit_card_transaction (time, features, amount) values (157791, '[-2.70324614823207, -0.340501685859222, 0.728988015887111, -0.0787558038947305, -0.214525421514046, -0.16414946891801, -0.0414199275384106, 0.903150308388252, -0.266103449304912, -1.43049553170244, -1.15783365854529, 0.245259202785765, 0.374256667601856, 0.561935615802697, 1.14336551538312, 0.459566952317976, -0.191384396005968, 0.230225487995711, 0.359369728730184, -0.218679023315111, -0.0856197166348758, -0.453883543919644, 0.535629709988353, 0.575731744543518, 0.496849461577905, -0.419972793971856, -0.0157496148585376, -0.59232023806864]', 68.82); -insert into credit_card_transaction (time, features, amount) values (157791, '[1.97054751776093, -0.760623421427038, -1.67157026037189, -0.637687109696004, 1.6652902680225, 3.92653078274165, -1.30440982698839, 1.05872763846903, 1.11058953814946, 0.0237403500193585, -0.216871341471088, 0.371081672934286, -0.0223005581197026, -0.0622073906825891, 0.76694010262227, 0.244276330096594, -0.566969378451654, -0.127845508391831, -0.393274463401349, -0.110734387951164, 0.160351657608665, 0.564189165496309, 0.202586142725311, 0.759792872726705, -0.245286156758252, 0.589136426517134, -0.0002525065755098, -0.0529491958647838]', 11.5); -insert into credit_card_transaction (time, features, amount) values (157791, '[-10.283840326559, 8.73711360390633, -6.25330974661393, -2.45024877678851, -2.56093879594621, -1.33378879234573, -1.25946016721643, 2.73580670425344, 5.88774663796836, 9.67169006809798, 1.30871552526723, 1.94769160921491, 0.548987944031479, -1.91738077821551, -0.547095584663062, 0.237957570618949, 0.704478801108747, -0.299044465112427, -1.17118782944075, 4.12650511602013, -1.28080026741558, -1.41057724201119, 0.720097726241932, 0.460297611367704, 1.44526002111981, 0.125872714560615, 2.09049364024244, 0.856336026652095]', 9.84); -insert into credit_card_transaction (time, features, amount) values (157793, '[1.70426112012253, -1.21723015184258, -2.6590138769852, -0.16962221378476, -0.0995270934332791, -1.65210398193167, 0.970277658633988, -0.768445643973197, -1.22169456048433, 0.897693011540047, -0.918536993788862, -0.0791270371168364, 0.256962111023529, 0.720436402586334, -0.328788215028114, -2.32369095262563, 0.248242692450853, 0.539097376352781, -0.83078590128427, -0.0647737252828582, 0.0743359531383701, 0.242295510706484, -0.333876428269169, 0.0808861447671155, 0.39209091536391, 0.992057881423392, -0.170347997344765, -0.0431583663526275]', 289.99); -insert into credit_card_transaction (time, features, amount) values (157793, '[-0.941075928255147, 1.28633024228262, -1.07687293442658, -1.39046125326578, 1.43061052775681, -1.23053906433383, 1.73407122127421, -0.542389222190825, 0.212248655442623, 0.959118897606692, 0.621014855315113, 0.269938275195855, -0.546320069292819, 0.475047431052903, -0.671344860128032, -0.536315480872384, -0.875355875867887, 0.0920936944304808, 0.185843559174571, 0.195725437872443, 0.089002743620273, 0.964125006335455, -0.12043648313724, -0.427629160702855, -0.137407325198694, 0.0866793615809219, 0.494364656059916, 0.163389410493054]', 2.99); -insert into credit_card_transaction (time, features, amount) values (157793, '[1.40881559177793, -1.55713284586253, -1.13491976241115, 0.345679637936459, -0.654766466863484, 0.006810677876549, -0.188424483665475, -0.0829263009312008, 1.17067106218679, -0.0934368723754421, -1.75493489590746, -0.301603380671381, -0.0141160580170873, 0.0261619745824533, 1.08963463844386, 0.843983925732166, -0.93005246277163, 0.433402569407785, -0.162612788749123, 0.518393435909588, 0.318678698796014, 0.270905737949466, -0.22917623162061, -1.00418347723432, -0.367749434467483, 0.684483858935735, -0.101795841969133, -0.0004027601922978]', 352.32); -insert into credit_card_transaction (time, features, amount) values (157793, '[-1.1899796712283, 1.89133152014838, -1.50382624216855, -0.992897074673071, 0.740751862618331, 0.570557901130913, -0.287722069688846, -2.0111499604055, 0.248511132178709, -1.04981077617008, 0.233052089272971, 1.19617566027916, 0.81630393050234, -1.3566394103766, -1.33717529319061, 0.566678551320692, 0.643196984946578, 0.889065393449315, 0.481156217036166, -0.713867019432395, 2.18901942970767, -1.13963188370464, 0.260885070471903, -0.290936007890761, -0.0941916505802669, -0.179866204141953, -0.377582941100181, -0.0309089405483861]', 29.5); -insert into credit_card_transaction (time, features, amount) values (157793, '[-0.0354484927244249, 0.730175148017973, 0.330132311329335, -0.674381883359044, 0.407005295234552, -0.786659113463449, 0.852747280344624, -0.0035221742108129, -0.215483630309981, -0.233704399734699, 0.789823718839538, 0.538536864697287, -0.723863511660823, 0.44676274091715, -0.992120607045315, 0.0499755865673419, -0.536108856638944, -0.289741615224721, 0.105397280446734, -0.0627986867173875, -0.200565479906558, -0.473075582106466, 0.0954852428414791, -0.0068473172003214, -0.564490397539484, 0.122724199658887, 0.240348445434052, 0.0868966699629609]', 7.16); -insert into credit_card_transaction (time, features, amount) values (157794, '[1.5054188142728, -0.663141131019519, -1.82721902533205, 1.50291579164055, 0.123218519731032, -0.805778420080681, 0.809267781049699, -0.291916324702562, 0.0399271703654445, 0.338321858789789, 0.656092031455265, 0.585527683531508, -1.193889690807, 0.965122231842391, -1.09845048067001, -0.494213906317943, -0.289452788942982, -0.0351413041283715, -0.0383327184291764, 0.149872184084339, 0.245722187709739, 0.301197007932837, -0.209492887345876, 0.0121126993617299, 0.29705294597988, -0.53538427156362, -0.0704194261207085, -0.030524233283834]', 253.83); -insert into credit_card_transaction (time, features, amount) values (157795, '[-1.09524449799202, -2.08486258833577, -1.61282104198312, -1.65028590912733, -0.69943692831602, 0.513547047736151, 2.73329184994019, -0.284772443308236, -1.5750877294259, -0.246401417558659, -0.181472804964862, -1.02655085763717, -0.94510976484046, 0.371276828671496, -1.75649263483503, 0.854378647728466, -0.0874402403379363, -0.579409792095228, 1.06566347983326, 1.50284547037691, 0.750287653327273, 0.839512686258899, 1.44625649225825, 0.0320225941007407, -0.263077226122788, -0.259622976586242, -0.0652480312764561, 0.265176912943714]', 675.0); -insert into credit_card_transaction (time, features, amount) values (157795, '[-1.1000160779094, 0.291211950319425, 0.157204771254036, -0.630805393549851, 0.0453442932722407, 0.370754662351859, 0.988364209672035, 0.476960215105395, -0.688994122996438, -0.996703117078864, 0.175292946428921, 0.742694436065353, 0.354052102776272, 0.474695881146295, -0.864716846831595, 0.534585823528542, -0.78082901526066, 0.50235661240969, 0.131142532676325, 0.320292751086516, 0.247227514668375, 0.179540077546935, 0.319905550315546, 0.35679127720391, -0.126108599448645, 0.151868724601958, -0.097839476874566, 0.0690080900724724]', 209.85); -insert into credit_card_transaction (time, features, amount) values (157798, '[1.88636805509399, -1.69106924878415, 0.172131498017553, -1.03945748508987, -2.10528535901864, -0.479979542022299, -1.48133707242017, 0.0412689582262737, 0.939901653906973, 0.43224136958161, -1.12868754288559, -0.104254024427807, 0.116612533517988, -0.522228783375368, 1.05070826482395, -0.839808201109878, -0.319475822040389, 1.71351266618556, -0.912773601270047, -0.355056414475206, -0.100139912822088, 0.119542699214534, 0.242156321689805, 0.0709727951370877, -0.776636067064766, 0.864146376838571, -0.0119149596761949, -0.0168288622922416]', 120.75); -insert into credit_card_transaction (time, features, amount) values (157798, '[1.89969467743245, -0.683079288017006, -0.43764357746394, 0.454744961831374, -0.749998570682082, -0.285773891366412, -0.639655171261453, 0.0085190773210286, 1.37061128932421, -0.0088333309115908, -1.38817600090682, -0.0496881503828339, -0.341784204535597, -0.155226560725719, 0.491720089117549, 0.408714051724439, -0.537683192278867, 0.115614878232876, -0.201478015447254, -0.109308416806466, 0.167824668261193, 0.566092185530666, 0.0794297783880152, -0.373060634318798, -0.281931873520492, 0.592690630312245, -0.0305438387986692, -0.0429024290883929]', 64.99); -insert into credit_card_transaction (time, features, amount) values (157798, '[-1.56614371337096, -1.10693239810506, 1.77284847898343, -2.28493973369918, -0.264700990447496, -0.161627137716695, 0.127037332024835, -0.0861226204207469, -0.563170732863897, 0.250466538012205, 0.323031405000328, -1.49108959090539, -1.75437162498233, -0.363816133964697, -0.313715336960086, 1.83562204227253, -0.589120866101788, -0.149860091600641, 0.135215891538, -0.119417289762808, 0.324400436745769, 0.981932507551466, -0.262115931098626, -0.403542801772277, 0.658971419543815, -0.0541286457657929, -0.402749386959748, -0.0046031863982481]', 127.75); -insert into credit_card_transaction (time, features, amount) values (157798, '[0.0717710443982317, 0.753516692410123, -0.984974259008293, -1.24756611258667, 1.71319288379423, -0.63812538392805, 1.60438741913532, -0.5405278809027, -0.404328116091301, -0.0401934976728908, 0.616694543219629, 0.377847497308172, -0.172795688280997, 0.553395785357984, -0.595742280806656, -0.406635308584287, -0.881831437583427, 0.121085223740003, 0.300454949134101, -0.16062321024601, 0.204559542765417, 0.728217503866964, -0.263298198562384, 0.270649307683319, 0.228751199132963, 0.0973390325057113, -0.467004343031063, -0.266497894160752]', 7.7); -insert into credit_card_transaction (time, features, amount) values (157798, '[-0.444765598861338, 1.4627002885693, -0.880433959796999, -0.556376131927674, 0.616283347546082, -0.884929262646801, 0.65443010354292, 0.291004051506747, -0.114848220369264, -0.7694764811407, -0.732539713278964, 0.369788145204632, 0.685989688752277, -0.893586952353969, -0.325634003133353, 0.414216351739519, 0.481519585730879, -0.260239286706494, -0.259291156266131, 0.081566767767721, -0.323589916959774, -0.825590009077581, 0.14828898750374, 0.571969365975727, -0.321968248331121, 0.118447678763248, 0.210471038850441, 0.0790120613342076]', 9.42); -insert into credit_card_transaction (time, features, amount) values (157799, '[2.03366408140585, -0.0833393815847641, -1.17305539330393, 0.211189610999452, 0.14959928275766, -0.604363683345264, 0.0938741840653013, -0.169100508228718, 0.228032031805312, 0.219556109328809, 0.79842057185103, 1.30147264544246, 0.526854962992368, 0.359733156346715, -0.643201984182282, 0.105300913566589, -0.630077193524295, -0.416557445454839, 0.484176487231363, -0.176936883853522, -0.247493639344885, -0.583147784274602, 0.287797107760436, -0.381990204604314, -0.283074969671472, 0.202297184485535, -0.0686148773088367, -0.072912980623324]', 1.98); -insert into credit_card_transaction (time, features, amount) values (157799, '[2.00145510449199, -0.3640913493235, -0.505150794759056, 0.219068606484759, -0.456185848574338, -0.369052650094353, -0.487073857691969, -0.015589597393387, 1.02205274237803, -0.0093018226952011, -0.836523616955133, 0.32769854422222, 0.105261622254801, -0.0629508169127828, 0.566749597818622, 0.454491906522139, -0.535872111010472, -0.448028196133696, -0.0081160894693764, -0.144538038084023, -0.267001452049833, -0.725826540128248, 0.465148517872478, 0.719175806945812, -0.626860750943678, 0.19855587433563, -0.046282506461219, -0.0355115776401416]', 16.9); -insert into credit_card_transaction (time, features, amount) values (157799, '[2.13414538455416, -0.661133756271348, -1.62020309454371, -1.80997787279354, -0.493490957186542, -1.65088868752532, 0.0492398231276711, -0.394477536561428, 2.4796692887107, -1.28128021128026, -1.00824035250406, 0.612818792606274, -0.921740936016974, 0.350522934108258, 0.361087432136657, -1.6197206563663, 0.26256794374308, -0.0363243555034819, 1.28656592455884, -0.301006891099887, 0.169722443010636, 0.93537256888383, -0.103470706404273, 0.0331165628042509, 0.550642274269616, -0.551365312090925, 0.0323452695913875, -0.0645029207216555]', 0.23); -insert into credit_card_transaction (time, features, amount) values (157800, '[-4.71915049006679, 1.59252490416968, -2.77258664723401, -0.498435936843101, 0.0862327867792849, -1.28217641634285, 0.110770674355989, 0.621997425154314, 1.78121060829476, 1.89354129619428, -0.89978516738163, -0.049430419132411, -0.256056578737117, -1.12021664474182, 0.136701657768248, 0.646386275540903, 0.509462863953576, -0.276863394154898, -0.144206613551039, -0.840948552959527, -0.969393619172675, -0.848938034479947, 1.01278059333581, 0.478191586938237, 0.169007371363003, 0.0875961383361338, -0.324330891455179, -0.515202678666404]', 8.96); -insert into credit_card_transaction (time, features, amount) values (157800, '[2.12667843180652, 0.133713443950872, -2.42777775999825, -0.096190961163517, 1.24382599776436, -0.371659310695292, 0.626161813595055, -0.288448829506422, -0.092206427167988, 0.191698543086133, 0.388735065111448, 0.954648513131259, 0.551570612664994, 0.822799917368543, -0.207282566708548, -0.194168910125332, -0.892265401023494, 0.182727892336413, 0.557631096372231, -0.155880921197694, 0.111172616944129, 0.420920095209807, -0.0957957580094313, -0.165403955024978, 0.531199584254473, -0.162307976902683, -0.0582324650055861, -0.0812866001290752]', 5.9); -insert into credit_card_transaction (time, features, amount) values (157801, '[0.0887065212412666, 0.671882530527959, -0.328513081669109, -1.05325255415979, 1.12358263889934, 0.196045567863838, 0.716812566827932, 0.192173702280555, -0.0114290786690494, -0.201183642288358, -0.469416836360925, -0.379908560318137, -1.38654065868403, 0.532065928907626, -0.813704804217099, 0.516036796028196, -0.988442457520956, 0.320782290568175, 0.634688866187384, -0.0637802489863149, -0.304158150648714, -0.838992116765941, -0.0266499361434722, -0.399958478397064, -0.408975086103619, 0.158634024864416, 0.227705074155744, 0.0799647221145074]', 0.99); -insert into credit_card_transaction (time, features, amount) values (157801, '[1.9735382184824, -0.147425138707456, -0.599246028867224, 1.08804878544682, 0.0732754100694228, 0.383364645579075, -0.451485731702857, 0.0982676112933362, 0.90663549383973, 0.128761867448397, -1.4651525474147, 0.32735165650902, 0.533903566628463, -0.136347095302895, 0.543279076679071, 0.531401513975966, -0.902928034118601, 0.0022829410542289, -0.258475490943602, -0.162518793580638, -0.204209320059014, -0.500164386275119, 0.308062419687576, -0.0408613540802333, -0.271705466889993, -0.929683420196922, 0.0549855011854161, -0.0217298188919691]', 19.9); -insert into credit_card_transaction (time, features, amount) values (157801, '[2.01830670288122, 0.0931911700577121, -1.60942403841763, 0.348380177241888, 0.358500129930782, -0.669727481531726, 0.0691712544751756, -0.0866844517300812, 0.261000291569162, -0.202013781975235, 1.25386058917496, 0.758184250024602, -0.314348565226374, -0.584543685131868, -0.4802515494999, 0.470483892452355, 0.305771983743395, 0.158658560182585, 0.273490970439248, -0.167240773196018, -0.303477276538758, -0.819197993835738, 0.353464826937627, 0.662657093125669, -0.32213878650529, 0.143428186684445, -0.0680184523112714, -0.039252929058807]', 1.98); -insert into credit_card_transaction (time, features, amount) values (157801, '[1.97560988443601, -0.817668328450572, -0.525335964834477, -0.904969880073108, -0.554565913008623, 0.129656949366575, -0.827775847769124, 0.0236044256365399, 1.69438094609917, -0.521669296698851, -1.20404430539154, 0.978779468346732, 1.75375048894336, -0.637687818930429, 1.01691665173731, 0.551238775272759, -0.879625304752796, 0.159658354835614, 0.40476816657131, 0.0910090554521392, 0.0261771051875942, 0.188393994522829, 0.189203096617308, 0.118325879901473, -0.439152455777008, 0.534854161742983, -0.0207366959673776, -0.0298431563004567]', 60.76); -insert into credit_card_transaction (time, features, amount) values (157801, '[1.91287333121634, -0.416397195209376, -1.83478074831675, 0.355999626944455, 0.166662748657346, -0.905560750342791, 0.404800849619557, -0.234917067784546, 0.361806013944991, 0.286331550014571, 0.271153631848679, 0.131214427717345, -1.64812143327027, 0.908130262815438, -0.948889902075486, -0.198725558743612, -0.348971679440399, -0.0472083879472373, 0.592916702819985, -0.151136198935177, 0.026626676054661, 0.003787006106764, -0.0269927621737257, -0.311536580442517, 0.110858216809369, 0.557599446584875, -0.125910463060418, -0.0794813664768505]', 84.98); -insert into credit_card_transaction (time, features, amount) values (157802, '[2.0579682372007, -0.0684551655947941, -1.09797955689948, 0.39351780275777, -0.0867946578808905, -1.0909706649568, 0.162363103157028, -0.299255940455945, 0.559294751777712, 0.0676338175553091, -0.769976535487005, 0.384450783029092, -0.023187848319652, 0.295945015670599, 0.103064396304832, -0.138724323681403, -0.251828539871413, -0.923799957736315, 0.101375165298171, -0.234150781460144, -0.289005523242351, -0.696373108347065, 0.339215265258751, -0.0863960835613664, -0.307350112052131, 0.205731819147201, -0.0683123157429526, -0.0613843734468539]', 1.98); -insert into credit_card_transaction (time, features, amount) values (157802, '[-0.662579830545148, 0.353075290813157, 1.41182994209554, -0.392374918692448, 0.158478536668967, 0.543043141948079, -0.437423860066864, 0.276350030346687, 1.61112203379215, -1.4217206739337, -2.1439523399203, -0.0207896149251316, 0.456672346458041, -2.43470155534823, -0.638659850456144, 0.644993285478948, 0.156328940462853, 1.27748852679236, 0.0121014679009533, -0.169687510623998, 0.147664043963046, 0.789870322417577, -0.38565441956634, -1.05334016819136, -0.440951870893653, -0.243626478490085, 0.0443777701082882, 0.222243042419404]', 7.7); -insert into credit_card_transaction (time, features, amount) values (157803, '[-1.01015212072886, 0.633830094908954, 1.68437076850686, 0.299068887075505, 0.711967035645827, 0.572947214952501, 0.407069893075968, 0.266185254763064, 0.0115740094057204, -0.554820141501952, 0.401217734016973, 1.15595885756949, -0.0667284508338286, -0.361890919936152, -2.10600352397657, -0.277522586916977, -0.328805007685352, -0.384637047355829, 0.350736881494707, -0.209975958521421, -0.481706181869206, -1.31370812660583, -0.0847678629843969, 0.629921569170175, 0.552263283413985, -1.05212396800231, -0.267668691111145, -0.0188005550464329]', 10.0); -insert into credit_card_transaction (time, features, amount) values (157804, '[0.862941187980347, -1.88749019335918, -2.17087155786798, 0.852340177395539, -0.621354181195796, -0.781277508071769, 0.682628938147886, -0.201053970899531, 0.87937039968048, -0.904076081002724, 1.13762261571622, 0.417394820666489, -1.37793888553869, -0.86467663176151, -0.585048920415896, 0.222538462574613, 1.00530597135217, 0.656441242794341, 0.135987224791507, 0.895278671772378, 0.147433546394276, -0.690343079415677, -0.295426478503889, -0.101920082758047, -0.371265073573484, -0.164802184968382, -0.122705562608091, 0.0653487872328319]', 578.8); -insert into credit_card_transaction (time, features, amount) values (157804, '[0.169367883595559, 0.956147953074134, -0.556427123686125, 0.936963561208608, 0.930524383977687, 0.0636670684875959, 0.41622741991107, -0.0466919099127169, -0.538375101643974, 0.196328088871118, 0.79058152210962, -0.0181310328141218, -0.361565946321907, -0.504925910185467, 0.707689218194274, -0.515066597432359, 1.05275932766738, 0.847558427187433, 3.12387907619402, 0.180782302865973, -0.135378894098173, -0.335589119619996, 0.202964448648445, 0.206551350161792, -1.56237916096899, 0.414619289668535, -0.010551588799948, 0.305325512713878]', 1.29); -insert into credit_card_transaction (time, features, amount) values (157804, '[1.7006940593433, -1.42123237639736, -1.236457695055, -0.451099156233925, -1.20180416536041, -0.810533600090655, -0.789689274938437, -0.0233491361941443, -0.0546303091436498, 0.191947700884504, 1.25585753821169, -0.629191490336688, -1.01847302417451, -1.63492786820819, -0.228735136051851, 1.91095573571045, 1.23038232679234, 0.190069856999646, 0.398541286980192, 0.37380933899007, 0.405629138174191, 0.718489472418009, -0.0597655152051037, -0.0411395882353453, -0.304357932886922, -0.130779229930871, -0.0198535948597151, 0.0119640120314064]', 219.0); -insert into credit_card_transaction (time, features, amount) values (157804, '[1.93023662261023, -0.196151849026805, -0.598748101949689, 1.24976328085023, 0.139448992847324, 0.717800771260176, -0.41239489264628, 0.2553959189401, 0.918409464006159, 0.142431299306595, 0.0358968765579994, 1.31270067658047, -0.0803377023810168, -0.131581267851333, -1.80560602428491, -0.134041026321317, -0.43969189855515, -0.308555520479416, 0.613804168172047, -0.242471693522345, -0.50302574730202, -1.20216131086347, 0.412954529689502, 0.137592601043651, -0.279280342348862, -1.12554099041351, 0.0498527649449709, -0.0387168199646039]', 4.94); -insert into credit_card_transaction (time, features, amount) values (157804, '[1.93023662261023, -0.196151849026805, -0.598748101949689, 1.24976328085023, 0.139448992847324, 0.717800771260176, -0.41239489264628, 0.2553959189401, 0.918409464006159, 0.142431299306595, 0.0358968765579994, 1.31270067658047, -0.0803377023810168, -0.131581267851333, -1.80560602428491, -0.134041026321317, -0.43969189855515, -0.308555520479416, 0.613804168172047, -0.242471693522345, -0.50302574730202, -1.20216131086347, 0.412954529689502, 0.137592601043651, -0.279280342348862, -1.12554099041351, 0.0498527649449709, -0.0387168199646039]', 4.94); -insert into credit_card_transaction (time, features, amount) values (157804, '[1.93014002822133, -0.196311465319916, -0.598804167418757, 1.24980389623073, 0.139399680518361, 0.717818764605702, -0.412328543602562, 0.25538294124622, 0.91840357469867, 0.142408011109847, 0.0358975929153066, 1.31270273601648, -0.0803287880500169, -0.131569948050575, -1.80559973431211, -0.134034431594372, -0.439690416200348, -0.308545276116013, 0.613788198660126, -0.242369870508684, -0.502995450759948, -1.20218770026672, 0.412910459714429, 0.137595166667521, -0.279298701122698, -1.12554632302787, 0.0498433175974572, -0.0387081140778168]', 4.99); -insert into credit_card_transaction (time, features, amount) values (157804, '[1.93014002822133, -0.196311465319916, -0.598804167418757, 1.24980389623073, 0.139399680518361, 0.717818764605702, -0.412328543602562, 0.25538294124622, 0.91840357469867, 0.142408011109847, 0.0358975929153066, 1.31270273601648, -0.0803287880500169, -0.131569948050575, -1.80559973431211, -0.134034431594372, -0.439690416200348, -0.308545276116013, 0.613788198660126, -0.242369870508684, -0.502995450759948, -1.20218770026672, 0.412910459714429, 0.137595166667521, -0.279298701122698, -1.12554632302787, 0.0498433175974572, -0.0387081140778168]', 4.99); -insert into credit_card_transaction (time, features, amount) values (157804, '[2.03293896236003, -0.133499827984758, -0.89097044694298, 0.469413420697929, -0.239498274872574, -0.905814587957579, -0.0608221756817656, -0.293101114214716, 0.897950341445993, -0.107902136797229, -0.726465882732101, 0.901733083294368, 0.977775761845263, -0.0510636643707597, 0.289582133657649, -0.372094322914973, -0.396585488713015, -0.0899310999854533, -0.294323200071065, -0.196368066383948, 0.365756138437933, 1.39092070870144, -0.0637909019249169, 0.0756799428703881, 0.312066658713002, 0.0203421735598049, 0.0157803308645931, -0.0532324129855432]', 1.0); -insert into credit_card_transaction (time, features, amount) values (157805, '[1.98715784052101, 0.135969609181324, -2.75632867292445, 0.972901390868871, 1.40648405286108, -0.453710983919198, 0.979853505673817, -0.331547606140305, -0.447530670756135, 0.537492962382802, 0.176339587035396, 0.323719651741669, -0.696354651086258, 1.17025753255851, -0.845682284316742, -0.479041764873774, -0.638627595659541, 0.254659839309911, 0.167354635807938, -0.158992273661776, 0.343683627668722, 0.970225724655249, -0.321650658135891, 0.0707611653510068, 0.901520460578426, -0.208591996229026, -0.0749462597584223, -0.0794109844745718]', 64.0); -insert into credit_card_transaction (time, features, amount) values (157807, '[0.14328828221439, 1.03347205192401, -0.413954804730453, -0.546000253454251, 0.927088521052604, -0.824445563093275, 0.980871727808579, -0.113080498625127, -0.0551913781455736, -0.836754858765189, -0.702518607586338, -0.0945402718921675, 0.0702091679663794, -1.03012421184268, -0.243739030537978, 0.33615651199416, 0.353881746644832, -0.214583603729398, -0.220268149322069, 0.0273538045715858, -0.341512651035965, -0.834778728151865, 0.0956055015074986, 0.560463369200917, -0.414990455926793, 0.113468812126446, 0.222112471808535, 0.0844250605859257]', 4.46); -insert into credit_card_transaction (time, features, amount) values (157808, '[1.89020976926467, -0.718621455617211, -0.494607816018931, 0.41125257367279, -0.664836358728663, -0.0341650686895274, -0.723185802641591, 0.113822222940725, 1.4102089553693, -0.0029513111663952, -1.23903740947893, -0.250509146300211, -0.839344788770907, -0.0599141230617934, 0.567476418025163, 0.377830180221047, -0.43476284183696, 0.119471329861976, -0.290453776516448, -0.117693369815635, 0.174754378194728, 0.521449562973754, 0.155090705247644, 0.634628275709373, -0.315882989950332, 0.542924745528679, -0.0382390801003175, -0.0340646234220689]', 64.99); -insert into credit_card_transaction (time, features, amount) values (157808, '[2.08484113647936, 0.0153232723818007, -1.32095765528647, 0.30363706764646, 0.320449667392797, -0.679532519123371, 0.239355660538362, -0.309144985942832, 0.392165630475432, 0.0154146291929246, -0.744364814841996, 1.02210715677496, 1.26734856338744, -0.0236232077167782, -0.306773778112924, -0.258168858213166, -0.325246078299231, -0.97113190068104, 0.255659549359951, -0.120214069555673, -0.25017179844612, -0.520295794938215, 0.291670547472232, 0.70955009787486, -0.116828793769917, 0.243480492738206, -0.0699730067167858, -0.0550860593735453]', 1.18); -insert into credit_card_transaction (time, features, amount) values (157808, '[2.20272194782546, -0.783839579807829, -1.4386409678936, -1.05101659983573, -0.469859293163585, -0.94967760996934, -0.452134650379234, -0.233771115500973, -0.6779648342407, 1.06726679220494, 0.685310397860821, -0.360276894457644, -0.437209850146088, 0.328133579657708, -0.284828213386965, 1.19845319670586, -0.183045318178142, -0.629692830783504, 0.836709217242646, -0.0499072046662009, 0.457185805373731, 1.27739833230359, -0.0588538258339816, -0.290948207325745, 0.195626880085859, 0.0708728312446743, -0.047410123274501, -0.0801127492014495]', 15.0); -insert into credit_card_transaction (time, features, amount) values (157808, '[2.10265459972427, -0.974301947985372, -1.17943178479541, -0.807064648785807, -0.840413029339322, -1.04973445657136, -0.489679937276198, -0.131114196078993, -0.316009410432336, 0.964188458030938, 0.685427354188882, -0.660510342424728, -1.9476774145923, 0.444591211282609, -1.0022474381471, 0.854999705174846, 0.343969200464974, -1.22155520937397, 1.15501656588183, -0.115045606390623, 0.0209352790007677, -0.0610606754782581, 0.226101669435722, 0.0312980285661253, -0.191185143054015, -0.349560367261045, -0.0528551377558542, -0.0689431140232731]', 40.0); -insert into credit_card_transaction (time, features, amount) values (157809, '[1.72749825858346, -0.460940688670449, -1.10648329544574, 0.55067129546286, -0.163783824672603, -0.336237705326993, -0.180240691292153, -0.081110999858166, 0.791214209032195, -0.577537884750753, -0.337726488684758, 0.650499645419312, 1.09754995477988, -1.41880756136142, 0.63528400304427, 0.762605823463408, 0.339199479665297, -0.0634259065005024, -0.312694006485262, 0.217658078488462, -0.234254970478256, -0.864549526831725, 0.30834230683611, 0.515998894161786, -0.620018086431606, 0.0773377808843559, -0.0386484696811715, 0.0163597096084855]', 150.0); -insert into credit_card_transaction (time, features, amount) values (157809, '[1.94799262008175, -0.045237038198725, -0.455975703893429, 1.43124399296622, 0.0385352593135862, 0.237105147166421, -0.210081916637175, 0.0112467661869301, 0.95394411441597, -0.066708049639737, -1.14203108587611, 1.44012438494246, 1.27755510901435, -0.572895640717578, -1.27495348576189, -0.488472894711816, -0.145500865222528, -1.07402841693146, 0.190989702384496, -0.175796872667835, -0.516890670781023, -1.14598589288621, 0.448670451783649, 0.55639940519416, -0.24856331810995, -1.13307491699875, 0.0625063089648343, -0.0220802721424277]', 6.49); -insert into credit_card_transaction (time, features, amount) values (157810, '[1.857446099099, -0.546959335002011, -0.45363278102496, 0.277492259171764, -0.495370106695829, 0.0562012445094075, -0.618161061893275, 0.20781873519935, 1.11199973853353, -0.0076445690233954, 0.818138680551881, 0.810064741507163, -0.716242473757146, 0.311031406135812, 0.127532157161612, 0.39006896945034, -0.707323114611947, 0.338559572852083, 0.13531737049558, -0.153857021883862, -0.109265804241523, -0.364034803493023, 0.377332824637121, 0.640375817891629, -0.49928884766645, -0.674471318625301, 0.0166398762373841, -0.0282127279240626]', 49.99); -insert into credit_card_transaction (time, features, amount) values (157810, '[-0.247917116688247, -0.655768904879766, 0.11677152910076, -2.64983844936901, -0.488275194960327, 1.11764833890607, -0.947184257728264, 0.865315283554798, -1.88325495693079, 0.478701126344802, -0.951172724747645, -1.29665431350972, -0.613563920957981, -0.0233874820861105, 0.380776913911904, -1.20181009178415, 1.4235580770564, -1.11568158427419, -1.46981278259855, -0.549178272623337, 0.0451194729197998, 0.412136430443857, 0.242727717729859, -0.404977631617722, -0.641704622729318, -0.16781994916912, 0.0075579600766935, -0.0163945189241929]', 46.0); -insert into credit_card_transaction (time, features, amount) values (157810, '[1.88549683468638, -1.20153555374825, 0.696203433229769, -0.314601472163345, -1.76011788319106, 0.156165304977569, -1.46510675707153, 0.193588945694349, 2.98475414773138, -0.925686714455559, -1.38330250650493, 1.74580023538335, 1.29135053134651, -1.5713346337322, -1.28254485308116, -0.608165647578597, 0.179888563198066, -0.208673785749401, 0.732285609926405, -0.0654036540181713, 0.107793832550697, 0.952347034241795, 0.117622289199077, 0.194246945884365, -0.303082320873311, 0.623667829519586, 0.0573531882575415, -0.0309188039903835]', 33.04); -insert into credit_card_transaction (time, features, amount) values (157811, '[2.03960974435102, 0.0757104311597996, -1.9008910923745, 1.11392897225594, 0.731730634367903, -0.435411066015483, 0.438702510998587, -0.154061725916454, 0.18207149997809, 0.465546657668439, -0.143222193545841, 0.105168923946084, -1.47117971098552, 0.935108495939136, -1.02838087113208, -0.270569056054109, -0.572602029878565, 0.227337964069095, 0.351324886948925, -0.350427644162902, 0.038047120955391, 0.250009126680236, -0.0885182568475055, -0.830053254689188, 0.484104664051276, -0.457074483557609, -0.0273133228961275, -0.0826069572342486]', 6.99); -insert into credit_card_transaction (time, features, amount) values (157811, '[1.9719159070628, -0.388731121104135, -0.624006943055621, 0.17015573536754, -0.277733696648881, -0.191131386335047, -0.40097827954714, -0.0543587408742976, 0.906612703241837, -0.0458665498407903, -0.921401238098533, 0.700679833045854, 1.01543716433756, -0.2440392102851, 0.494478197168139, 0.493074498195497, -0.678565391240785, -0.463829779018159, 0.0758096096619381, -0.0299614617424366, -0.258970150409411, -0.716360744113209, 0.400668459063837, 0.484286742120343, -0.572348252538824, 0.209080917242179, -0.045735406548267, -0.031928751743242]', 41.97); -insert into credit_card_transaction (time, features, amount) values (157812, '[1.95178559994952, -0.409727009211516, -0.444015475328114, 0.125487611536859, -0.486976047716096, -0.0577029616634854, -0.699150214488992, 0.232274347938193, 1.23743810020455, -0.0365047003412201, 0.82261019499935, 0.518472881862301, -1.22851738167275, 0.510042677246535, 0.590608682650595, 0.437274570592855, -0.794885215131684, 0.598339258265772, 0.0809074332997959, -0.289030394758501, -0.0786696716460054, -0.218988266371108, 0.400863418781075, 0.610056507052829, -0.439500751304051, -0.970243130806716, 0.0454609463993414, -0.0327784475264763]', 2.35); -insert into credit_card_transaction (time, features, amount) values (157812, '[-0.422421521095763, 0.658543922031277, -0.542993291613436, -1.91077933609488, 2.55752848754234, 3.67851431842003, -0.183898937905112, 1.12543274032514, 0.467184839533209, -1.20056879741915, 0.196317532303265, -0.0996175290741946, -0.288940846422521, -1.28628731988351, 0.855903906145092, 0.545086615514229, 0.269021030996727, 0.261788803371768, -0.482030433066122, 0.0960908874996435, -0.26443143843773, -0.752090922759935, -0.0086320332130002, 0.561731359228306, -0.145211291283595, -0.245972410243061, 0.168815933695936, 0.100460460840373]', 5.78); -insert into credit_card_transaction (time, features, amount) values (157812, '[0.0024960324417067, 0.788164180599921, -1.25484607760832, -0.82336568428351, 1.70747188308121, 0.619559406227495, 0.671560922026512, 0.217821956147169, 0.562239179129043, -1.22713229708576, -0.43150171228519, -0.0249860060926184, -0.495942732420654, -1.74682180509996, -0.584917494202639, -0.595252253685278, 1.63427367867747, -1.08957455661534, -2.07621588234904, -0.406714901289419, 0.322887103476518, 1.1882984618713, 0.234853074605478, -0.432318258813192, -1.97366146694542, 0.23056355011325, 0.0945152146477225, 0.37637794258061]', 1.0); -insert into credit_card_transaction (time, features, amount) values (157812, '[2.01652460331248, 0.197597603262843, -1.70558625054696, 0.548291330260255, 0.190817636093764, -1.50755986197748, 0.349012810877867, -0.44121785950865, 0.484656779092686, -0.501663018213626, -0.224559439906617, 0.430782072646178, 0.681551107047013, -0.859250949937578, 0.824812492899406, 0.170613393918332, 0.361442475553509, 0.417222682587251, -0.447246309176938, -0.120208967695477, 0.216763843195825, 0.787944084515085, -0.0460610578341789, 0.0023672939038673, 0.2674714614548, -0.108543027645682, -0.001856078329216, -0.0258953665999172]', 24.49); -insert into credit_card_transaction (time, features, amount) values (157813, '[0.0245406853862598, 0.834158696854512, 0.279423515963669, -0.581056858872866, 0.413832824945075, -1.11715639703404, 1.02181159037646, -0.207648220991299, -0.0459097877986485, -0.414935763033553, -0.939626144379386, 0.137872662303856, 0.025706194958167, 0.110417921229033, -0.402328086924192, -0.0881568947645665, -0.415336033575582, -0.741678608888256, -0.0791318668810542, -0.0204920378985217, -0.25672151736005, -0.574843814940882, 0.0843517814733371, 0.0136256383933616, -0.483594183991414, 0.139086611652184, 0.244614495231356, 0.098022680591708]', 9.0); -insert into credit_card_transaction (time, features, amount) values (157813, '[-1.89941235074885, -0.835026396217829, 1.66793132486525, -0.646436489043085, 1.52527136759406, 1.13379438858827, -0.310950049331796, 0.638651130892026, -0.195195301251167, -0.602018053632396, 0.918502152812633, 0.378459805344627, -0.255858020847809, 0.165080231517051, 1.13534214789979, -0.0900824216782614, -0.10831140545711, -0.652537748075668, -0.130096452458055, -0.135957644236223, -0.104949842418508, -0.138522232200461, 0.613932943698787, -1.65136356235412, 0.106802237640361, 0.628331759968377, -0.0047200532136992, 0.0265065304979407]', 25.3); -insert into credit_card_transaction (time, features, amount) values (157814, '[-0.0408645436674328, 0.643505132346518, 0.682483895219545, -0.537851306680335, 0.785663820022569, -0.219362528377173, 0.545235620103251, -0.155021156498491, 0.606893027977926, -1.14690155385672, -0.726292299540116, 0.0127270458053618, 0.605513711344683, -1.77213207624616, 0.34676268617161, 0.370041022616633, 0.116644742572105, 0.895540889827852, -0.657126267725782, 0.0091098227613336, 0.29973447644084, 1.22805640499734, -0.19605763451332, 0.549346072211329, -0.710797168706551, -0.33221154212398, 0.113629117667617, 0.0459507976560507]', 2.49); -insert into credit_card_transaction (time, features, amount) values (157814, '[1.91413522555332, -0.66584263691139, -0.571756504768202, -0.913025199012016, -0.570311477436205, -0.317848716721277, -0.549136756749172, 0.0048753584101016, 1.03215036139995, -0.294236720406826, 1.33056615424238, 1.7792247340108, 1.25076632785714, -0.0399209339802863, 0.379142524285099, 0.453145372958297, -0.82357018985906, 0.0811629568394416, 0.526705137750816, 0.0490124103568653, -0.0831337901014837, -0.258884089061158, 0.380226371330108, 0.80867285876941, -0.639265957036621, 0.268833577781513, -0.049688075238884, -0.0372744770147582]', 57.0); -insert into credit_card_transaction (time, features, amount) values (157814, '[2.06536702630595, -0.185842664413203, -1.10932923221241, 0.373668790959369, -0.279027561019407, -1.22946369548719, 0.0857156000797023, -0.261914967063858, 0.811973016089996, 0.073986754873404, -1.01550221237828, -0.201476239390462, -1.23751330241331, 0.464629116766291, -0.052178453682874, -0.163304074751581, -0.112220188082995, -0.777765299745355, 0.248967369192527, -0.30684345847883, -0.294656869850248, -0.736116650117601, 0.331539811061915, 0.0276121178569083, -0.306344804488189, 0.357923693419442, -0.086919074750387, -0.066287276404903]', 1.0); -insert into credit_card_transaction (time, features, amount) values (157814, '[1.9021008785613, -0.482844154348986, -0.601523633155915, 0.179886971764206, -0.25909496971808, 0.219205258493644, -0.565538099369571, 0.16577228248986, 1.02711701744355, -0.0191377865785395, 0.535331378508032, 1.0588679928049, 0.114062426483948, 0.129832519933771, 0.0262672930768413, 0.530508960416592, -0.953857493848836, 0.465758951057107, 0.355454632729674, -0.0931921046465438, -0.135135632861565, -0.398254373317797, 0.314696620216382, 0.306981137172287, -0.403444488315781, -0.656710585289073, 0.0185440301190065, -0.0307360977269626]', 46.48); -insert into credit_card_transaction (time, features, amount) values (157814, '[-0.309006417742835, 1.69743830986966, -0.334570089891847, 2.52065973486873, 1.83324100755956, 1.24449837402443, 0.023365102972311, -2.32096984629601, -1.85417027631769, 1.13929124043572, -0.330898624029878, 0.393039701634436, 0.0541494658972232, 0.609635506751284, -2.30118885101429, 0.800796913914809, -0.957152785599905, -0.132212089011764, -1.17598131222284, -0.681050820855386, 2.25325709977215, -0.990421283568725, -0.586470236991348, -0.130776394990222, 1.19526883195869, 0.0985717764493531, 0.206530037645392, 0.23280725745383]', 21.18); -insert into credit_card_transaction (time, features, amount) values (157815, '[-0.256069392576177, 0.641016316788403, 1.23599786105643, 1.10967000219345, 0.739315250937195, 0.408433003634605, 0.380705187350078, 0.135026748531129, 0.0498645175526142, -0.487835994233637, -1.34097169353132, 0.809816864589273, 0.880417348629801, -0.489823046627348, -1.02980463783317, -0.943606131881048, 0.206346698570494, -0.716394253633113, 1.20623923845022, 0.0723482312025159, -0.390153119584736, -0.881605479674927, 0.191987178757877, 0.560352244906919, -0.750145770646455, -1.03510609069657, 0.263701914394897, 0.225430223978651]', 5.55); -insert into credit_card_transaction (time, features, amount) values (157816, '[-1.19466168797146, 0.904479617259957, -0.727543282784896, 0.948610388342598, 0.754179739501868, -0.501770345120198, 0.956482175709518, 0.33811944114813, -0.710580137749843, 0.259129942299583, 0.0445229092162285, 0.156054594517973, -1.13606430995869, 1.11066638372623, -0.871198669710597, -0.736082606751366, 0.0279779127761554, 0.393553299816004, 1.14282044030211, 0.0961437774020731, 0.149109517059488, 0.584798720001557, -0.116917287999219, -0.403244916259447, 0.122852144837373, -0.359362507005129, 0.446956737832394, 0.195110981693878]', 76.4); -insert into credit_card_transaction (time, features, amount) values (157817, '[-2.33567210288528, 0.733138221649288, 2.0120099315408, 0.570452636629583, -0.560033894628143, 0.864410513456987, 0.0170526509325223, 0.284058903226262, 1.36797757028668, 1.63002234763906, 0.271161287916639, -0.24497861983797, -0.802494768393077, -0.699874500415324, 0.857624610180712, 0.530786442562383, -1.00186667451541, 1.55474026574662, 0.610348673619972, 0.631512187804047, -0.0912926770852709, 0.713324206954873, -0.451620578010056, -0.523147501320451, 0.68667927757969, -0.252686619350527, 0.663833119056286, 0.675868508138676]', 100.0); -insert into credit_card_transaction (time, features, amount) values (157817, '[2.01225660541128, -0.12090862323187, -1.01133122217169, 0.325476421186976, -0.158659570466063, -0.949621553308758, 0.0633844777917638, -0.176685409638332, 0.321384826339672, 0.250849992567385, 0.989175211706786, 0.996650219727563, -0.340384382288002, 0.538035432510428, -0.633944869222583, 0.0782937932135224, -0.487582582090626, -0.367404249890262, 0.402502545720778, -0.235416238019861, -0.23481479952135, -0.59440583314119, 0.34277598792859, 0.0691624275537647, -0.351271213437363, 0.173690252517699, -0.0764478284820569, -0.0697888491767698]', 1.98); -insert into credit_card_transaction (time, features, amount) values (157817, '[-0.0030630410061332, -0.684657807221811, 0.297792231561736, -3.3466535165441, 0.153821265574126, 0.301944521007295, -0.148805741060795, 0.176905609705918, -1.83221510082318, 0.450191859109921, -0.72430532294448, -0.675000939775357, 0.373065372112177, -0.485818938217281, -0.703747314953181, -1.14945106379088, 0.793464752663303, -1.58607543310855, -2.64946542349511, -0.64826280462242, 0.0875770886152101, 0.798256808757288, 0.18761843034172, -1.06091216151852, -1.27522786620147, -0.456043138582043, 0.295467161130535, 0.240630561325634]', 15.0); -insert into credit_card_transaction (time, features, amount) values (157818, '[-2.36265172021935, 2.24444032102664, -2.41785970342538, 0.791624671803851, -0.076004460222897, 0.16295223379335, -0.688874149737515, 2.01735520530894, -1.33602291698205, -0.689971180803214, -1.1022944104057, 0.448983502356223, -0.992706533888524, 2.30972276728927, -0.892585639219672, -0.0332048747267003, 0.463520645926414, 0.751234593579852, 1.41440509219953, -0.718632830516321, 0.372483320536492, 0.278604871368275, -0.218364088002813, -0.219181241038021, 0.0019911974490226, -0.478007450983075, -0.953881444756312, -0.210779472515315]', 16.89); -insert into credit_card_transaction (time, features, amount) values (157819, '[-0.964045125612063, 0.401958802950009, 2.1977831516738, -0.729470190516652, 0.195106370886337, -0.397534278096216, 0.239500366757542, 0.238780214698133, -0.0068377775341286, -0.9648974451456, 0.701341534165569, 0.434148218466311, -0.798178403269352, 0.201861370393168, -0.257776554436292, 0.348178115392445, -0.728458630145008, 0.51417832786665, 0.172123004385327, -0.0406260900446285, -0.105658283079042, -0.438823990466515, -0.212498998001795, 0.0179382099396533, 0.320945596480672, -0.523091236594453, 0.0420783011102279, 0.0648088152694283]', 4.6); -insert into credit_card_transaction (time, features, amount) values (157820, '[2.00704675985152, -0.279405405346904, -1.28956087290228, 0.100786611413697, 0.156186742923983, -0.440342552080132, 0.0814742665626667, -0.226649935964445, 0.949801853207362, -0.260853903166807, -1.07452115842414, 0.962385050693463, 0.965162274969219, -0.124460783657848, -0.321463255860613, -0.438023978708979, -0.291000561790328, -0.633590345164846, 0.464355644488598, -0.068970173114129, -0.15951091658087, -0.266323232999436, 0.170644556868241, 0.515265340319667, 0.0157499850284986, -0.112843979380258, -0.0299753353603384, -0.0436620922839824]', 40.0); -insert into credit_card_transaction (time, features, amount) values (157820, '[-1.10782746910529, 0.818007001151512, 1.23441133451061, -1.33078828029731, 1.05881250675329, -0.716022442446408, 1.18402602137007, -0.194652589029391, -0.324783546652541, -1.23023561554114, -0.802934841360688, 0.732481356758106, 0.850399254752658, -0.396186122192683, -1.67508080947407, -0.326969899836686, -0.394945247275416, -0.86993893333513, -1.22496458554483, -0.176013488544165, 0.170286296549614, 0.593383379883429, -0.620246922024202, 1.20563069766128, 1.14130427420137, 0.598097375634103, -0.192605467790794, 0.034651388695736]', 3.07); -insert into credit_card_transaction (time, features, amount) values (157820, '[0.0271775324813036, 0.821080821077469, 0.182819999066708, -0.778501091963439, 0.626265555479052, -0.611722792794885, 0.914758999117007, -0.0475013734472113, -0.301344931788006, -0.24575141705346, 0.507308892531776, 0.786300341155683, 0.10528223119812, 0.264853615542356, -1.09416566728214, 0.190699597846666, -0.782707158846096, -0.164111144534707, 0.325160299296116, -0.010412459617805, -0.228765806691467, -0.507309429988439, 0.0155310222655124, -0.339498398397163, -0.472696739737868, 0.140226979307561, 0.243889958184265, 0.0841729877243635]', 3.27); -insert into credit_card_transaction (time, features, amount) values (157821, '[1.88665705922934, -0.360965320950786, -0.297727339493862, 0.46446755871876, -0.604808765772919, -0.508171188799265, -0.383475047512767, -0.0788128803541868, 1.10480129068373, -0.216085071905604, -0.352940380035797, 1.07437593976372, 0.933254840791105, -0.193385705382018, 0.578190435918073, 0.0778374544524609, -0.482862659152504, -0.376732625623661, -0.220424140042421, -0.0658078315218247, -0.126037353687638, -0.305315218837071, 0.405318059829234, 1.12656061129541, -0.444332828399621, -0.688489158115425, 0.0283170630663477, -0.0105742493363594]', 49.99); -insert into credit_card_transaction (time, features, amount) values (157821, '[2.02297796313133, -0.451698797647335, -0.523431233028171, 0.373860239825938, -0.523903322869473, -0.235145877911942, -0.637525594159163, 0.0002517286694999, 1.57312390824529, -0.153479726626306, -1.66880347964446, 0.060374746238083, -0.136066884867294, -0.165343839309445, 0.565061269109967, 0.291475463048405, -0.727039997022602, 0.466614024085402, 0.0288750143179468, -0.22320424240174, 0.144349596930845, 0.661177114183402, 0.0382360573204955, -0.63955439142185, -0.017475740445534, -0.171117731230469, 0.0384218127231883, -0.0440449312862853]', 9.99); -insert into credit_card_transaction (time, features, amount) values (157822, '[1.47213000036966, -1.10965201447505, -0.530079772016693, 1.30211685261894, -0.469214861607331, 0.747243422225682, -0.501553862352727, 0.156071341288157, 1.18891394692465, 0.0034108379361629, -1.60958876428604, 0.212803127547787, 0.557309080691805, -0.283026605536064, 0.821924342590273, 0.803487214498176, -1.03105218868005, 0.531108329437095, -0.554660950355263, 0.350410663691309, 0.191638037473539, 0.11349418416183, -0.0181016987719031, -0.0507676437024086, -0.319582805849277, -0.744618829527683, 0.02989188895939, 0.025596850448959]', 269.0); -insert into credit_card_transaction (time, features, amount) values (157822, '[1.95260941642706, -0.463763496156111, -0.787384780096095, 1.39114631147901, 0.193293893885378, 1.20077942517405, -0.843212462361275, 0.36325570364224, -0.170777712518457, 1.30984483645386, -0.761064455868926, -0.708803351997812, -0.644543464519214, 0.0825555562704867, -0.311669570304863, 2.23122765817023, -1.54486702198862, 0.732286707866648, -0.348632120451662, -0.0385540299102912, 0.0759815094916214, 0.0019616327078082, 0.128106059372384, -0.635350697384354, -0.641170558351484, 2.27263580909368, -0.189125635497311, -0.0765970137726111]', 53.96); -insert into credit_card_transaction (time, features, amount) values (157823, '[1.94486901534407, 0.393022084545182, -0.981243860593355, 3.67363032923613, 0.837687690005821, 0.770438248184241, 0.0458714848508149, 0.0658161152500346, -0.566099758843042, 1.36884214413366, -2.1326432260674, -0.489444585331139, -0.2064169321907, -0.011502791251268, -1.3616144522607, 0.632279967763316, -0.717537153627847, -0.320688816331692, -1.14004217303203, -0.260791705400167, 0.0320644712281213, 0.218062292145084, 0.0325631841399894, 0.0139196239470554, 0.219852720839999, 0.109739818288953, -0.0322666704626435, -0.050269144580269]', 15.12); -insert into credit_card_transaction (time, features, amount) values (157823, '[-3.06761475038232, -2.45757422381283, -2.37781833374031, -2.75327430744293, 0.405270219564038, -0.587300907165915, 0.985344454363531, 0.444995319920586, -1.44856299433554, -0.954020409832174, -0.617750097436486, 0.860476183269492, 1.33205170099883, 0.809959856510232, -1.40672634850991, -0.640053638097413, -0.913410026106095, 1.72412664100913, -0.947931624535411, -0.572463091197429, -0.0876827194099806, -0.156815339512873, 0.589958048172099, -0.268956803656156, -0.213857626134977, 0.430112980760767, 0.132307165833751, -0.722747290729469]', 261.06); -insert into credit_card_transaction (time, features, amount) values (157823, '[-0.191673063048596, 0.821384159145986, 0.787674730152658, -0.0922151874145524, 0.681725373574366, -0.722273978632257, 1.26231684222704, -0.739932271562768, 0.152280334918435, 0.388642660435366, -0.774156086293229, -0.485026817886725, -0.549312350960892, -0.14034120075895, 0.434589447227881, -0.787100501008745, -0.127915916983925, -0.642284630683373, 1.13529016002422, 0.0358413872948787, -0.236158337446056, -0.374780192492369, -0.0119837966207439, -0.0847080906541573, -0.906443609943872, 0.208797558300003, -0.59845703660515, -0.252546698431979]', 1.79); -insert into credit_card_transaction (time, features, amount) values (157824, '[2.14725144561267, -0.911386646510524, -1.70743781131686, -1.08121823771349, -0.123056751918487, -0.526472948782039, -0.294571891453705, -0.242186844703195, -0.406899172991187, 0.779200245089816, -1.33341809792709, -0.92799832254967, -0.0587987196156513, 0.0236448955414936, 0.214033993508898, 1.15067640098965, -0.0281973819969767, -1.50455814329727, 1.02405841178102, 0.122064764920196, -0.0779838009596802, -0.452922814833994, 0.186934782075766, 0.0066968744621801, -0.12824301356534, -0.422302825655525, -0.0545148178467393, -0.0461967776619663]', 78.76); -insert into credit_card_transaction (time, features, amount) values (157824, '[-1.48323702696132, 2.10624752649122, -2.16123474882007, -1.22838859489731, 0.233052200224787, -1.36326729699469, 0.43310605472374, 1.04871855353305, -0.609369507776305, -0.337206237993234, -0.0827354737797539, 0.432264304119711, -1.11153317007751, 1.90577260521751, -0.785213429118827, -0.0455624209302278, -0.193326016872693, 0.354370197799988, 0.164834257011197, -0.203439962648355, 0.332130148034254, 0.83334654767392, -0.10917360155125, -0.449728325179691, -0.160171131847402, 0.118467146575708, 0.222562436856777, 0.185344428381212]', 0.92); -insert into credit_card_transaction (time, features, amount) values (157824, '[1.94385824857617, -0.324216926499365, -0.378751671816814, 0.243042148973371, -0.386030995318921, -0.0200066019164586, -0.576057723689871, 0.124435047076755, 0.938180727889361, -0.0153386791813126, 0.977178748442479, 1.42171305419448, 0.464162303746381, 0.0609745917180072, -0.0555692970560797, 0.385183200248637, -0.829375704497138, 0.27097393308082, 0.208626965098023, -0.153419697371748, -0.125178645056365, -0.267839631504996, 0.392750230913669, 0.730302646638874, -0.430623237868935, -0.679854795300319, 0.0287855482064301, -0.0319837818302215]', 9.98); -insert into credit_card_transaction (time, features, amount) values (157824, '[2.06249030426725, -0.0648454310765915, -1.0650812664132, 0.420367017994954, -0.168214387594192, -1.22471681726083, 0.168718161269045, -0.322472670570973, 0.575602771936041, 0.080237911519834, -0.772147943304741, 0.319930193231773, -0.166258743588161, 0.321343992406884, 0.0643805860927848, -0.0971402553273107, -0.271777142273838, -0.844595885668367, 0.144470872369702, -0.240377265067763, -0.291616211892481, -0.715908440885158, 0.344507283686344, 0.0400238915933294, -0.307124950232525, 0.196202778537347, -0.0721207325752885, -0.0604255538216545]', 1.29); -insert into credit_card_transaction (time, features, amount) values (157826, '[1.52888870056684, -1.96676854409923, -0.664912635773967, -0.557305595239503, -1.56125111879195, -0.642749040643673, -0.629031621413586, -0.233352569887686, -0.0045228312455695, 0.593968700843232, -0.578246524436214, -0.166290706760199, 0.991319476045783, -0.523114973812103, 0.592208550164067, 1.3299480960183, 0.009074238013735, -1.05785885405177, 0.0975332152883663, 0.668302342788377, 0.606375699207481, 1.10871855185019, -0.149626859529165, 0.0589351673001042, -0.31441138802195, -0.0995421601121922, -0.0373538621589966, 0.0124885141114627]', 334.28); -insert into credit_card_transaction (time, features, amount) values (157826, '[-0.551296409572373, 1.50583905262851, 0.514153108628084, 2.42072420626781, 1.66512928232429, 0.463940384200157, 2.27057447197262, -1.05359343127532, -0.562582716083645, 2.55930820895058, -1.33880035562573, -1.36858822142472, -0.800853632259143, -0.742586864441154, -1.07784886470889, 0.38648719370457, -1.32925767874308, -0.611923707768003, -1.37718713089342, 0.0746974523978003, -0.379669295897999, -0.341695726300356, -0.248587197565725, 0.404802531019539, -0.293272599619504, -0.393227629181931, -0.740158693792147, -0.701280472714805]', 86.15); -insert into credit_card_transaction (time, features, amount) values (157828, '[0.0094132298917694, -1.5071776819449, -1.88671933901564, -3.64719484766952, 2.53673608073762, -0.338748956282057, 0.130848644830295, -0.0967668282929549, 0.0185210137290804, -0.284888707522018, 0.787329209712129, 0.650563221330971, -0.279868673012532, 0.869180307172842, 0.894726642338023, -3.74327236595601, 0.430318531798971, 0.777385064737291, 0.0434022369398565, -0.311852013670493, 0.142299576141314, 1.03209694412214, 0.320208704504647, -0.953743428336158, -1.60903702675943, -0.893601388814555, 0.247073030425973, 0.129056000939828]', 11.37); -insert into credit_card_transaction (time, features, amount) values (157828, '[-0.91575425733639, 1.5912851193345, -2.02032190594613, -0.240148772738088, 3.00067127247173, 3.52860342148369, 0.410435665027943, 1.24915376531265, -0.593411389384587, 0.420328443148663, -0.478967948023834, 0.0485772389851593, -0.405192086342592, 0.86586695923979, 0.0514365493960024, -1.14373561685891, 0.204696306460171, -0.390598506121877, 0.637884797230501, 0.262606328344763, 0.0809618975278517, 0.455974620149795, -0.206969355646798, 0.711395683193602, 0.198817419056023, -0.345422922504724, 0.551754430877551, 0.288847270792079]', 9.99); -insert into credit_card_transaction (time, features, amount) values (157829, '[1.93463403830276, -0.222941544714367, -0.332789948576418, 1.11929077754318, -0.370950738860409, 0.138379275970264, -0.694966719371524, 0.222893127365693, 0.758564033189333, 0.414010768123185, 0.640976615783874, 0.461756578586964, -0.787057128821181, 0.350599097792385, 0.207654741374768, 0.71164436622105, -1.00416608987488, 0.965576765616621, -0.46655632058962, -0.275937294065399, 0.259464734957981, 0.803858325074834, 0.181308740558508, 0.724160654791181, -0.130899158891553, -0.614081313846642, 0.0439878082065253, -0.0353870048218169]', 0.0); -insert into credit_card_transaction (time, features, amount) values (157829, '[2.34570314489825, -1.38909737166812, -1.09221934822197, -1.6871127151492, -1.00467205964082, -0.371296931278659, -1.14692852907896, -0.207306614426596, -1.08090141691831, 1.5493612652813, -1.87993568190705, -0.903604337805547, 1.00264490968216, -0.634810691326469, -0.0930212340506033, -0.218117501100234, 0.073067109113477, 0.240604090931093, 0.199292750430603, -0.370890063096231, -0.218572630123933, -0.0841357668647441, 0.116198620246752, -1.0126995996372, -0.103566963076992, -0.117453585049837, 0.0212846900092629, -0.0550467264777384]', 21.6); -insert into credit_card_transaction (time, features, amount) values (157830, '[2.08851206565192, -0.489526696539034, -1.10668908538124, 0.665678158825648, -0.16048498916161, -0.115652656338969, -0.308425897709755, -0.17693717496671, -0.451164184313014, 0.929227532604979, -1.36499582905341, 0.533496210440932, 1.06685420505759, -0.259032857193185, -0.83276036010968, -2.03429229024651, -0.0137131424432421, 0.920494999457383, -1.00033677801498, -0.583494962686243, -0.247506752386339, 0.0474518194247801, 0.0993180659549081, 0.597725798717517, 0.261089616371313, -0.487430972718392, 0.0494598147442813, -0.0359975418316735]', 19.98); -insert into credit_card_transaction (time, features, amount) values (157830, '[-0.447925927972273, -0.172584932009424, 0.0569341138869356, -1.40284384610808, -2.20502462878886, 0.0560163277368336, 0.756950475513314, 0.0430077622008576, -0.916479273185834, -0.102806922331421, -1.01255478601743, -0.417674051501705, 1.19933857798349, -0.418871024632366, 0.213944634942227, 1.3755474737578, 0.0503882581384439, -0.729183993452069, 0.568356471353435, 0.0261563791144963, 0.607656684982254, 1.75503241951108, 0.362728777856288, 0.0458702212168817, -1.20206160102013, -0.197355221958857, 0.185480909057384, 0.124347807162022]', 325.0); -insert into credit_card_transaction (time, features, amount) values (157830, '[-0.198524600413521, 1.07056611697248, 0.210865437832398, -0.63896971205222, 0.453348373731048, -1.14898655562123, 1.03046033510157, -0.240133778605682, 0.0334370374083989, -0.120377506966644, -0.765167289228136, 0.559653885117001, 0.774966736423035, -0.109575083169424, -0.47116797480918, -0.152494552607784, -0.457694608353885, -0.862655827940222, -0.120953560944248, 0.130283385701381, -0.283558228376962, -0.530419173642584, 0.0643493886011424, 0.057564507479873, -0.414701799134791, 0.140122709890796, 0.363297935304445, 0.157527602339208]', 2.69); -insert into credit_card_transaction (time, features, amount) values (157830, '[-1.41637994242257, 2.08581438831372, -1.20812762489462, -0.435652715006095, -0.836699173966815, -0.0757787047767257, -2.00204219158296, -1.34089451588921, -0.346342947706735, -1.98849207010635, -1.04601064829146, 0.745948023749714, 0.789587384068126, -0.30440165784537, 0.602529274210554, 1.76141266795973, 0.569673760996769, 0.785720589100461, -0.982234241197868, 0.198847099619588, -1.08790808796609, 0.405200890137176, 0.644970369212119, 0.534240058657476, -1.38144870584727, 0.0115216224049188, -0.547496784347021, -0.0041537578676155]', 9.8); -insert into credit_card_transaction (time, features, amount) values (157831, '[-0.426382615533359, 0.429368652813297, 1.40631158426411, -0.58929268894478, 0.206332502253387, 0.713655068586189, 0.215282272103876, 0.322875560685921, 0.235229778166525, -0.754793868097043, 0.0693717581290611, 0.299751782010992, -0.360197998290926, -0.0164509199494985, -0.185155491332744, 0.219218321267268, -0.780262196487368, 1.16024794941629, 0.677787668726897, 0.0385416709853648, 0.283305303501857, 0.860771986892383, -0.294678575203266, 0.29815791126435, 0.0522178086006876, -0.146656606508687, 0.14424944156103, 0.135031904463973]', 35.97); -insert into credit_card_transaction (time, features, amount) values (157831, '[1.91761659516388, -1.27989042468339, -0.550512879866041, -0.675503386937241, -1.23135480129488, -0.715860319329705, -0.755444386509895, -0.231467593215809, -0.0488695654291568, 0.64396613210052, -0.582051893581756, 0.141068825560873, 1.34962686603566, -0.675067367142543, 0.110420966351565, 1.05256119777113, 0.133430385368821, -1.3548709396005, 0.324134472209228, 0.28187162439205, 0.415992878265722, 1.07503062012215, 0.0210676667911795, 0.0617928605364573, -0.172640661241608, -0.101567033826545, -0.0021142918020167, -0.0246200169536936]', 134.99); -insert into credit_card_transaction (time, features, amount) values (157832, '[-1.80346507043129, 1.94336351248285, -0.477192108231044, -0.401702597457705, -0.364354050082577, -1.36866894683339, 0.387747076722669, 0.344476113557209, 0.445387497102268, 0.731469924962325, -0.685099722598294, 0.335507199868123, -0.251346974320909, 0.397305842955965, -0.0994221366011376, -0.432827203638135, 0.182755154865333, -0.868298803648426, 0.150896667377805, 0.136729912408897, -0.188720269612872, -0.356520984447129, 0.187783520495935, 0.390557329551228, -0.933427342866825, 0.0844267878051226, -0.0667968389304196, 0.0853278684349329]', 4.95); -insert into credit_card_transaction (time, features, amount) values (157834, '[-4.37223272149791, 4.21387369062807, -3.97500050717615, -0.727923467186792, -1.70898981519024, -1.21635706382299, -1.51576250232597, 2.96065803610176, 0.434915678768808, 0.338122672548367, -1.42546779386574, 1.30812689981799, 0.847538384339796, 0.488138763051702, -0.354454893435288, 0.953268790918532, 1.48251852711152, -0.223059074778375, -0.600471602033495, 0.406914971149439, -0.320077450954875, -1.07793786137694, 0.506073576883736, 0.476447505830663, 0.201744331521787, 0.171836693000445, 0.298014787747813, 0.202332772727528]', 17.99); -insert into credit_card_transaction (time, features, amount) values (157834, '[2.19012899669411, -1.46153591009937, -0.803229671372471, -1.66107890407783, -1.15247172132322, -0.132293613880988, -1.24160020822153, -0.0372063708667921, -1.49851074512902, 1.67789309453922, 0.881536323757477, 0.355461625117194, 1.20325870082014, -0.468643047196173, -0.826559645452109, -0.381824106478064, 0.216441188084143, 0.284603079373507, 0.0178000099483304, -0.299460416591788, -0.0792238528330689, 0.217324469894423, 0.25464672620821, 0.707688528935559, -0.264319048131056, -0.233752008525689, 0.0161476018440291, -0.0434131033766562]', 42.0); -insert into credit_card_transaction (time, features, amount) values (157834, '[-0.637324313396194, -1.57689847025112, 0.917671048143363, -2.83681618149499, -0.838599902326643, -0.115953339092963, 0.436287160180054, -0.198810273679015, -1.59661557388642, 0.621985198992814, -1.67892172728516, -2.21434231290518, -1.17819702725974, -0.561841765249186, -0.52350256779129, -0.149958156740237, 0.0363420997301917, 0.470333103166933, -0.806909986909746, 0.165940258356955, -0.002061610294654, -0.0126027785330679, 0.431931858852278, 0.478634109326586, -0.152987133751504, -0.338863984576134, -0.12514648623781, -0.0540566219742693]', 239.0); -insert into credit_card_transaction (time, features, amount) values (157835, '[-0.402507052578237, 0.985184942134102, 0.782167914865014, 0.65501469914171, 1.01980221604131, 0.162967988863067, 1.30684286163706, -0.544255201902881, 0.553265972609925, 1.35127056602887, 0.194178629796895, 0.0226360633595435, -0.71437628656684, -0.463729726724478, -0.96954692983593, -0.900845681999007, -0.609141226227762, 0.581341873668803, 1.19092769318587, 0.508880555870567, -0.0421708374308417, 0.923073375288087, -0.416535325621101, -0.485350248747449, -0.0445354734478045, -0.407512914278499, 0.193432392494004, -0.296088418703337]', 10.0); -insert into credit_card_transaction (time, features, amount) values (157836, '[2.06600812011579, 0.212733685158645, -1.67667616274652, 0.40715771961777, 0.510978546137638, -0.790015457180319, 0.228000247297557, -0.259959557826988, 0.363375521745164, -0.411214817032647, -0.352558855664839, 0.622301334087091, 0.898382128729849, -1.00503720024482, 0.158401967024696, 0.211361309697192, 0.490323784768256, -0.50776489329618, -0.0334631399351485, -0.109995257111866, -0.34778820722648, -0.847448818941393, 0.339761738276403, 0.531585493776343, -0.247659288106161, 0.172541932655976, -0.0538974367239986, -0.0290450111215878]', 1.29); -insert into credit_card_transaction (time, features, amount) values (157836, '[0.0417576258490837, 0.849666970892494, 0.23128169516166, -0.615711165721009, 0.493059001875554, -1.04411168366748, 1.01420179368104, -0.203190219732137, -0.0486971447772289, -0.4142776289848, -1.04226921328416, 0.140920141067685, 0.133708925595141, 0.0849909491972045, -0.402495913302537, -0.0546273603100446, -0.467683543137562, -0.710936730621702, -0.0294000190500372, -0.0194206134345929, -0.266725840713801, -0.591554247389514, 0.0580324998549543, -0.114944660786308, -0.46480139489786, 0.147047769954302, 0.246367806791632, 0.0965567545530596]', 5.37); -insert into credit_card_transaction (time, features, amount) values (157836, '[1.97465908888964, -0.0223951560658185, -1.41708789062216, 0.648787448766615, -0.114031128747409, -1.20724263274786, 0.0206231328480823, -0.209847329147051, 1.02409979572353, -0.58655098654999, -0.569907075300941, -0.237431690507807, -0.96599306826236, -0.82753312955599, 0.614083941341488, 0.155052361083398, 0.676323649846123, 0.299558425306579, -0.283785985590054, -0.232739726316218, -0.0521000212794944, -0.0193475348600889, 0.138452581639282, -0.138786133224291, -0.0454942656006256, -0.335580434307477, 0.0104167148469378, -0.0205564520783589]', 24.99); -insert into credit_card_transaction (time, features, amount) values (157837, '[-0.387011219770642, 0.494090691748053, 0.961992096965214, -0.611490948924778, 0.919883229892197, -1.15211907325743, 1.10392321468602, -0.397080168441054, -0.132434878390282, -0.4965585330485, -1.0314215493946, -0.407010679765922, -0.694604638725308, 0.11989302211298, -0.222157549874694, -0.207746127831496, -0.422062492404337, -0.61908361178042, 0.155466705472628, -0.0168831621875002, -0.251108179557846, -0.620449103204238, -0.0753213384575987, -0.0388695516359171, -0.367655193695854, 0.158839095708461, -0.110907244005624, -0.0818975687330509]', 1.98); -insert into credit_card_transaction (time, features, amount) values (157837, '[2.03782407479185, -0.16442298678413, -1.22529997810387, 0.205262308720011, 0.0863746951306857, -0.590046990996929, 0.0181986252820673, -0.102478635402143, 0.398393562646139, 0.251768551946654, 0.548966423025011, 0.696793939624909, -0.55404384367956, 0.573074716185609, -0.518102950641522, 0.17713032712586, -0.593218344541852, -0.2594430614813, 0.518112629247304, -0.244698119248414, -0.264695666669397, -0.68565680428439, 0.292209606262834, -0.482487368102455, -0.310662369724611, 0.210329901700057, -0.0758539926565251, -0.0759754232909689]', 1.98); -insert into credit_card_transaction (time, features, amount) values (157837, '[-0.531707408435599, 1.45227755432358, 0.025451742188867, -0.575801075336073, 0.402596786634284, -1.24531875260899, 1.22318857142778, -0.436403220795202, 0.254630048132528, 0.993262139497165, 1.59124577093176, 1.27573391576437, 0.752235053637642, 0.044505244536199, -0.319422116170718, -0.605807466352912, -0.703385056832718, 0.277164199461892, 0.020604482519279, 0.492960320494032, 0.330366686815289, 1.59817069089154, -0.0949656118602944, 0.603040508441781, -0.736443525022912, -0.29290976784478, 0.664431490309938, 0.276975750995135]', 0.89); -insert into credit_card_transaction (time, features, amount) values (157838, '[1.91012764654507, -0.214911460242795, -0.149054703791262, 1.23587509910457, -0.614024091646997, -0.222327147820015, -0.640327487495576, 0.141235652060546, 0.745379305996641, 0.408467254876142, 0.776924880566786, 0.613102707751832, -0.683076204345837, 0.34695027642404, 0.1920990820685, 0.604685794990127, -0.913239041756053, 0.807549535948923, -0.570318211677253, -0.303663272429458, 0.276614316427436, 0.9043343788319, 0.155751838809788, -0.0169447489238734, -0.167111441901268, -0.578380903105959, 0.0529191329244133, -0.0420215725506592]', 1.0); -insert into credit_card_transaction (time, features, amount) values (157839, '[1.96256285463695, 0.19629847035573, -2.00081401799851, 0.617643909717091, 0.478082770385361, -1.44344180426208, 0.672101220623945, -0.509841259818088, 0.372268261545081, -0.625001033223497, -0.348249648813358, 0.845402080653515, 1.11002660882389, -1.0075363431364, 0.0183935235966256, -0.180997038667057, 0.610558728570152, -0.179821284582034, 0.0206455926374934, -0.0279161232273599, -0.130514499886407, -0.235961815726805, 0.0432290091998405, -0.179386744023229, 0.196669418587758, -0.379590227879475, -0.0151225713585166, -0.0214028136966656]', 59.74); -insert into credit_card_transaction (time, features, amount) values (157839, '[2.02722978880361, 0.462557604185985, -2.50196013305768, 0.596618746012089, 0.6581273407066, -1.32726737597522, 0.203059082140186, -0.190893505642236, 0.41208365441355, -1.0023855451196, 1.44997587865354, -0.145681667969982, -0.986028701983297, -2.17052348887086, 0.49590435112659, 1.10472977757573, 1.45631195743283, 1.98484866958775, -0.282110157578382, -0.192375331401661, 0.117388668982725, 0.462074767243637, -0.0882561341894579, -0.576061194329224, 0.238862484462931, -0.0971389099578121, 0.0097643554649189, -0.0075869145497367]', 1.0); -insert into credit_card_transaction (time, features, amount) values (157840, '[-0.289766221469332, 0.628559424479173, 0.943144284726303, -0.389692867287835, 0.560809599404991, -0.0491961641241497, 0.588693955780259, 0.110346030595749, -0.763375402564385, -0.281196127532713, 1.06492894781139, 0.443670878478515, -0.144314638995957, 0.553301548272612, 0.51528607876118, 0.139913017084407, -0.436221209582252, -0.0249041546814199, 1.30047059498911, 0.146298291760619, -0.301832802100491, -1.07255262772272, 0.266429405671112, 0.610975875614744, -0.926387177309548, 0.125821169573456, 0.0838071829234286, 0.16018698855591]', 27.56); -insert into credit_card_transaction (time, features, amount) values (157840, '[1.77258351226467, -0.788988000485366, -0.592611225620543, 0.22580449713795, -0.650874285009313, -0.247781807412575, -0.444965604621342, 0.0654538568418677, 0.879930744083094, 0.137735801635657, 0.471242560725537, 0.621001173219971, -0.541254696136626, 0.274544439610745, -0.072302719445212, 0.765234185927878, -0.847630157838773, 0.219210734096822, 0.369042484467334, 0.0171948669582646, -0.176508775391131, -0.724164406200276, 0.291294998619352, -0.430730860425196, -0.68914217577619, 0.227501640378244, -0.0690895260050541, -0.0395666601470801]', 120.0); -insert into credit_card_transaction (time, features, amount) values (157841, '[-0.555276477106862, 0.855721993047392, 0.0918589883442275, -0.760091342228719, 0.0955813626546279, -0.884911601097234, 0.69803317791985, 0.253334566973163, 0.17967836388236, -1.20377641331705, -1.63385983881794, -0.213543161280976, -1.09950980251242, 0.514944666376774, -0.873934511057197, -0.642116336359251, 0.133597304402471, -0.179060455128908, 0.0757904719549056, -0.324517073813025, 0.286660610399304, 0.903979079926945, -0.284215609380972, -0.0327359491682181, 0.0214474605373953, 0.218645838542426, 0.0029078782304432, 0.0951700228296631]', 20.0); -insert into credit_card_transaction (time, features, amount) values (157842, '[1.87447457125147, -1.92605834951993, 0.462380772373881, -0.553780253603811, -1.76137985895345, 1.42200572201284, -2.15937290954825, 0.590852496654097, 1.41873386665477, 0.488476630788313, -0.346948345993439, 0.463015783122129, -0.100675271030776, -1.51393894342423, -2.35623452105639, 0.988065123384293, 0.382699744529149, -0.513633277351094, 1.24038606625674, 0.141293007651812, 0.361983634352254, 1.27709675697346, 0.0354027462051676, 0.368429912863443, -0.253272111779542, -0.0106380542718407, 0.0720308983438213, -0.0266888704510744]', 92.2); -insert into credit_card_transaction (time, features, amount) values (157842, '[1.89162577093522, -0.35717416722083, 0.667425678294614, 1.76515630006475, -0.755774232959439, 1.00248611929123, -1.34007998469617, 0.408196992336103, -0.023652807642118, 1.14023012053801, 0.7844327245034, 1.16309792796846, 1.50231874819735, -0.782873925069329, -0.798034020530616, 1.8282734867967, -1.10357658763516, 0.106351448949116, -1.00204437285227, -0.0440496325347035, 0.18852040310767, 0.646036076974909, 0.385829138989342, 0.831254282443608, -0.953098909022139, 2.17010738401089, -0.115299821991652, -0.0533127892216617]', 0.0); -insert into credit_card_transaction (time, features, amount) values (157842, '[1.97801371722969, -1.06003704155414, -0.0375962080544792, -0.356999933077957, -1.26337356667985, 0.05145658230032, -1.21134001319506, 0.114724054557106, 0.146326136958788, 0.766527474030122, 0.413861570573846, 1.12890909235027, 1.10433409285051, -0.335595946595136, -0.0383281006369141, -0.852525595496914, -0.800280789107831, 2.07625418689173, -0.657695272378165, -0.479890601607901, -0.255169539606108, -0.162339328066228, 0.268953004531386, -0.374446015247298, -0.481871448033971, -0.439245416646459, 0.0744215342111052, -0.0267779011751512]', 46.66); -insert into credit_card_transaction (time, features, amount) values (157843, '[-0.16476058666961, 0.571057429709443, 0.392732635190925, -0.722876478495109, -0.0043006944316656, 0.486237771544567, -0.312000103369092, 0.711764617945534, 0.377263813522055, -0.887539949180069, -0.0915024469963657, 0.913623951596695, 0.333117455302897, 0.0903995810545895, -0.650510595106977, 0.606132797928513, -0.93443806251958, 1.03779619183735, 0.0366614903365086, -0.229734109188045, 0.315207281265638, 0.903593838959301, -0.10630733505883, 0.214837046813191, -0.220446428834252, -0.304785647216578, -0.0510936813387178, -0.0546688222497397]', 9.99); -insert into credit_card_transaction (time, features, amount) values (157843, '[-1.30590884627008, 1.16343275633271, -0.442157013142903, -0.271323551678816, 0.749967454794187, -0.303754881254632, 0.817414039737156, 0.353370181868569, -0.370981728836364, -1.15070060988245, -1.45977383726823, -1.34859152038967, -1.25696966300599, -0.296333337969729, 1.40422773993092, 0.272468046251167, 0.658451690604524, 1.25877604402342, 0.714697813669478, 0.112372918947992, 0.15349443436303, 0.296697885691122, -0.588842036983424, -0.0546777787648191, 1.089016112025, 0.182222970870572, 0.0399234061789595, 0.0503712947333781]', 81.43); -insert into credit_card_transaction (time, features, amount) values (157843, '[2.03298120480541, -0.519250867766739, -0.755020951694331, 0.229512970384677, -0.225139840441982, 0.328881590905993, -0.755790511466087, 0.16696279533742, 1.66457522598513, -0.150521675280538, -1.85991709700497, -0.344120069654245, -0.736174343122989, -0.0549022323684959, 0.736375549012799, 0.327935590851161, -0.705120023593535, 0.534321382323816, 0.0216223944853121, -0.235608640060191, 0.128614965884603, 0.55178110464381, 0.0726469668071797, -0.0922732879710547, -0.0182358331002292, -0.190102810175813, 0.0322620006240155, -0.0402332748670264]', 9.99); -insert into credit_card_transaction (time, features, amount) values (157843, '[1.48741837869265, -1.32344086118531, -0.798170060422109, 0.302493493566117, -0.742597188599083, -0.0343754304195434, -0.289082446434825, -0.0712278515600484, 1.39908116739226, -0.362185965833303, -1.05135663865608, 0.690611131336777, 0.810579406231439, -0.300588494738193, 0.532178798076625, 0.189200896874319, -0.579502115119682, 0.20438418829632, -0.12852123507699, 0.4263491179056, 0.333483684599355, 0.585397235160658, -0.102529488772589, 0.649220117098237, -0.204640769318829, 0.0704243163493087, -0.0370913349684176, 0.0122615838603845]', 283.09); -insert into credit_card_transaction (time, features, amount) values (157844, '[1.98847926751272, -0.438674011257909, -0.0853955437465157, 0.339162822529171, -0.712007567103061, -0.0219103836659887, -0.890245150236702, 0.124223712050773, 1.55566333698797, -0.179120280159013, -1.00061918408885, 0.426288727247951, 0.283524830289165, -0.291086678837846, 0.958926433354009, 0.485204224850785, -0.748190526941709, 0.334781867920445, -0.26839213221904, -0.187479727437693, 0.0618863200466343, 0.3494765019992, 0.293805171014466, 0.651020057739376, -0.350113351336363, -0.438066533233105, 0.054396555577505, -0.0206531008868494]', 2.29); -insert into credit_card_transaction (time, features, amount) values (157844, '[2.05891199440156, -0.179774461961558, -1.12970484192705, 0.408030616671213, -0.227139560654568, -1.12906424418599, 0.0534076449278054, -0.208948076616949, 0.799359081544365, 0.114683304451371, -1.04022833099562, -0.458358239021822, -1.60800641830317, 0.611058117515671, 0.276787720259479, -0.0645365850016284, -0.161308466894115, -0.729608931888649, 0.112422032348397, -0.337898214973206, -0.307885323414823, -0.825574923642232, 0.35919616209149, -0.129837652594825, -0.36056651051359, 0.211158258365743, -0.0784654387523142, -0.064977424796829]', 0.89); -insert into credit_card_transaction (time, features, amount) values (157844, '[-1.2804131924784, 1.82233396280854, -0.0693940062742576, -1.00979846792662, 0.426852296880116, -1.13599059328136, 1.42991556542049, -0.614385394880862, 1.46929046712057, 2.0517432726412, -0.531295713538944, 0.169108808414783, 0.310969373121468, -0.897334666222061, -0.265542726149647, -0.437431147680413, -0.73406559765094, -0.954282341496572, -0.250579647401289, 0.921442306108557, -0.605066642313551, -0.586242535331929, 0.0192424465724631, 0.0005771814038475, -0.249587598326457, 0.0537393513124151, 0.0973312234037515, -0.473180603798492]', 8.95); -insert into credit_card_transaction (time, features, amount) values (157845, '[-1.04017342964516, 0.478068994061985, 1.26254653096111, -1.77784110314035, -0.284508102016783, 0.346007986891355, -0.287310625711571, 0.623377864022016, -1.15174920832849, -0.263121442448326, -1.95459919552996, -0.363526536476605, 0.749634019000826, -0.191738518222399, 0.139910818313873, -0.747937170382772, -0.413011868328195, 1.36049292428697, -0.598319118240791, -0.296736319363136, -0.622118074869156, -1.42517410458178, -0.10634005038137, -0.0119490791002042, 0.273939050943132, 0.42561643216254, 0.127248590380951, 0.0493072559584417]', 21.0); -insert into credit_card_transaction (time, features, amount) values (157846, '[-1.16998501632616, 0.450392812276972, 1.86993548881047, -1.22240944985834, 0.160216720822717, -0.0700201136657804, 0.100492276009131, 0.354659236748992, 0.360202361660083, -0.568240058415776, 0.0069072836545446, -0.583522980993074, -2.0072168154799, 0.178300966880409, -0.437900452625466, 0.838860810803269, -0.966590537413451, 0.920042823709935, -0.568776764468839, -0.133647107239914, 0.215592166131897, 0.638381882539009, -0.58632569625624, -0.381093332856378, 0.633625722529762, 0.700466445548409, 0.104079898614449, 0.0820819296957852]', 2.77); -insert into credit_card_transaction (time, features, amount) values (157847, '[-1.25936237205995, -0.245868072798358, 0.362838361121548, -1.34375786830269, -0.256033687416538, -0.560010425778525, -0.144129097344281, -1.80519185491725, -1.44904261184901, 0.626208886322148, 0.877302394194352, -0.825970454488623, -1.04263154150753, 0.422574823030625, -0.0287707908931669, 0.876279947115107, 0.347987026493651, -0.400915416221153, 1.48851079444722, -0.595862914429193, 1.93485990558038, 0.93219870281267, -0.451096017674422, 0.14402131723528, 0.189719424833817, 0.237770477575723, 0.44963235365984, -0.006833134629177]', 159.8); -insert into credit_card_transaction (time, features, amount) values (157848, '[1.70101712255528, -0.653911951709174, -1.19885043851807, 0.241982152960315, 0.534503896198341, 1.21156062970239, -0.274383046338503, 0.303670847714445, 0.411365996134159, 0.0579316524007709, 1.05771714372146, 1.49604098097081, 0.840306369121232, 0.227411909894642, 0.290174272435812, -0.438222566629529, -0.11552551201505, -1.11745243468877, -0.715074260002397, -0.0207298651157425, 0.127857039855905, 0.406288381680372, 0.114498315645619, -1.58349984636476, -0.372882132761873, 0.324656715163677, -0.0087143897082049, -0.0607143585767801]', 109.95); -insert into credit_card_transaction (time, features, amount) values (157849, '[-2.0689537918788, -2.67806913519051, -0.704399244439648, -3.72072408996058, -1.64253375492401, 2.59784738931079, 3.22197415324644, 0.352375836843928, 1.42130918110889, -3.17499235007923, 0.564008794547129, 0.195029931505474, -1.365736813362, 0.511969001674109, 1.070918834197, -0.394278112992076, -0.588150511207407, 0.467622902134886, -1.27545557469269, 1.61422117378547, 0.920723225101146, 1.15347683423775, 1.83756159463081, -1.07430103913248, 0.274722706086566, -0.909587618782527, -0.0988205827759531, 0.200475636877625]', 915.71); -insert into credit_card_transaction (time, features, amount) values (157850, '[1.9979595211613, -0.236238202694561, -0.299657024454352, 0.382220683158352, -0.622272150638863, -0.69921522019942, -0.446888471611756, -0.0874257918847648, 1.16027938204007, -0.135330853777436, -0.696472899012873, 0.560858325517991, 0.334576542650276, 0.0108274677307566, 1.00701545551332, 0.285868766692241, -0.637068755041453, -0.186410347953965, -0.23243494339709, -0.225710489377349, -0.138284402123124, -0.257183326029473, 0.372755385286338, -0.125934917026405, -0.443633781884186, -0.607855010509986, 0.0381713352632872, -0.0318873278536854]', 1.0); -insert into credit_card_transaction (time, features, amount) values (157850, '[2.0337549284767, 0.134559064027543, -1.70784396766942, 0.762003346736829, 0.175958487238388, -1.15208225189517, 0.130662628191557, -0.307477388906162, 0.763405022150723, -0.686822353751845, -0.627820949394655, 0.307568570219323, 0.324824038472356, -1.80148364270555, -0.466084469214962, 0.134304715945167, 1.16100782967738, 0.165183677772925, -0.071483074691605, -0.140154529127182, -0.0337851948508485, 0.238191571841133, 0.0096459702594858, -0.165512702652204, 0.124603020028329, 0.643740795109804, -0.0385589476695535, -0.0298154750404508]', 9.11); -insert into credit_card_transaction (time, features, amount) values (157851, '[1.85775908884859, -0.031628155917328, -1.81128960400676, 1.05731771669384, 1.17500541309081, 0.793123282521759, 0.287902315119428, 0.187470889368437, 0.00245578781301, 0.298001298167532, 1.04978303171865, 0.987706737891137, -0.680943255646487, 0.865422745965599, -0.389177453163675, -1.3217602820902, 0.370927126768223, -1.38377230755514, -0.903672981803826, -0.377746986204501, 0.166477914664873, 0.742394227957657, 0.0301588518667023, -1.3481952293484, 0.234932931562616, -0.395908981632443, 0.0272869981967712, -0.0849084209723135]', 18.63); -insert into credit_card_transaction (time, features, amount) values (157851, '[0.133841777275129, 0.91340732317306, -0.586068888298282, -0.752408102671662, 1.09916983386924, -0.279001883907169, 0.812982984656937, 0.111391493274789, -0.149918873336896, -0.641433939672887, 0.470984292055473, -0.02513641809735, -0.848115166541602, -0.678400181419487, -0.814746273681456, 0.696155460703694, 0.0040787440014157, 0.527011768958075, 0.231507646321294, -0.007882666267082, -0.328024520616025, -0.872410982177272, 0.0416231537260778, 0.067256898394634, -0.420720022763395, 0.12474165322144, 0.212056680035392, 0.0679584688667059]', 4.56); -insert into credit_card_transaction (time, features, amount) values (157853, '[-0.418279890450697, 0.81579536323124, 0.368985412027566, -0.637428195883281, 0.850412176076468, 0.0572675513367951, 0.310865439387002, 0.0978606567568951, 0.58870205999343, -1.10848075352022, -1.23894810448455, 0.0652587725700526, 1.12378477233819, -1.80700428143635, 0.45155641578147, 0.694948606653118, -0.0428357439733203, 1.1253942564681, -0.338690994174335, 0.111952288788801, 0.171215003939959, 0.780982657540377, -0.273413739226443, -0.0210120211562227, 0.327827801321158, -0.119782180410774, 0.212917048824943, 0.114382596885579]', 2.49); -insert into credit_card_transaction (time, features, amount) values (157853, '[1.47544036344737, -1.58256845976234, -2.89705666475184, -0.30023943531686, 0.0922802918639253, -0.966930393751996, 0.982769244532117, -0.595733424446083, -1.54815582093274, 1.01063410891185, 0.790592481784231, 0.707077874113169, 0.47990785366851, 0.868639336480863, -1.05122411892755, -2.05003115053192, -0.0829270823298786, 1.13672333431214, -0.505883458584193, 0.213272643794415, 0.183343376120601, 0.247238083150039, -0.408659751586596, 0.869738126179322, 0.361807787372725, 0.920997248054669, -0.200351659289703, -0.0265149795267364]', 394.99); -insert into credit_card_transaction (time, features, amount) values (157853, '[1.92287448702174, -0.554943829608252, -1.63975639957667, -0.44165434257599, 1.60555989801129, 3.87214829745946, -1.58918894332324, 1.16181376627752, 1.09369688167302, -0.403028952943393, 0.374688967611876, 0.0267785971742734, 0.164138842084068, -1.31148323379252, 2.00852008253198, 1.34273225417583, -0.0890269972747848, 0.916779918391951, -1.04710167378957, -0.0406077881672269, 0.186853826891385, 0.520044260158824, 0.26562660330476, 0.621437877059116, -0.510364496039619, 0.324701271059333, 0.0504415899514766, -0.0075721997728669]', 16.26); -insert into credit_card_transaction (time, features, amount) values (157854, '[2.21536640244687, -0.761832351460431, -1.44009502509636, -0.982544007461661, -0.441309728945228, -0.926601129697856, -0.393720326013322, -0.186882566749054, -0.538050863033201, 1.00159439142156, 0.513079455713455, -0.465511856216402, -1.12261178229244, 0.380478446107364, -0.868930711070512, 0.965109852963288, 0.093271830946926, -1.13402915369833, 1.2462731890581, -0.125468256071979, 0.038631357864001, 0.08318456191179, 0.162091625555105, -0.357887959147389, -0.0362178780142174, -0.273892481450678, -0.050224878924179, -0.0805616994704347]', 3.71); -insert into credit_card_transaction (time, features, amount) values (157855, '[2.05111836839243, 0.15740855574506, -1.77858292410485, 1.30581548131097, 0.554152759147043, -0.934726134885325, 0.588798593382001, -0.351999236803142, 0.342446185020113, 0.27932995848784, -1.4621011097146, -0.206990119069699, -0.939526774434236, 0.658990785611951, -0.406651200617979, -0.585834138272509, -0.231079702688799, -0.436103086097665, -0.0668402513658939, -0.3307773289533, 0.0164457752436528, 0.236933609967714, -0.0454524536230938, -0.43373260337407, 0.48577564734282, -0.462149301834284, -0.0206162388502714, -0.0672376011113651]', 11.46); -insert into credit_card_transaction (time, features, amount) values (157855, '[-3.71532525934512, -2.07197333799974, -2.04610422655669, -0.396167587198657, -1.03145282756029, -0.0103205086914879, 0.488058836771038, 0.820881967706672, -1.88922686639007, -0.477766366478084, -0.663032389232418, 0.818952495741138, 1.41412180788548, 1.14596253861254, -0.294753297910607, -0.913476925981963, -0.263414539003976, 2.46725934422878, 0.0130169566719335, -0.416706840325311, 0.208040654412326, 0.318879740222887, -1.09262117812668, -0.897391142591055, 0.137042600655579, 0.0064575342940457, -0.247580678883342, -0.536546546537076]', 334.0); -insert into credit_card_transaction (time, features, amount) values (157855, '[-0.530909875155746, 1.17435827853442, 0.336783716800536, -0.614669457642115, -0.0192581354577398, -0.806302148792159, 0.337612201591243, -1.61704742305679, -0.0850360782366578, -0.08048384622935, 1.12032887671022, 0.699680935363251, -1.03575772016956, 0.558804302624249, -1.08897693923071, -0.0095915894878863, -0.281210448171338, -0.308834647498908, -0.0332952070081408, -0.430407538232863, 1.34410810446113, -0.943107703618564, 0.363276105804988, 0.490827843034998, -0.584133869196096, 0.101312190916888, 0.311724568192207, 0.105158942451035]', 7.18); -insert into credit_card_transaction (time, features, amount) values (157856, '[2.01540437435644, -0.111465787280573, -2.08860495970329, 0.457634705801435, 0.368061511330875, -1.45072604984734, 0.733388380109151, -0.473974788176933, 0.55310744822025, 0.0030824437059599, -1.13350897009547, -0.290800359929977, -1.34187000512968, 0.897001670784938, -0.0164148055050148, -0.846185005698976, -0.032998027637546, -0.459026116964284, 0.139900996870749, -0.256544189397996, 0.14214005213038, 0.496488953738721, -0.116455129676418, -0.148894131920432, 0.509548567209587, -0.0566273144356917, -0.0660938175389855, -0.0710031420590403]', 50.0); -insert into credit_card_transaction (time, features, amount) values (157856, '[2.11410663455505, 0.221262303751733, -1.90895918004813, 0.279334454292127, 0.776150447661988, -0.544063738201821, 0.246480735696864, -0.26240861999487, 0.369526832558714, -0.408771110807906, -0.872743867506429, 0.535175038487399, 1.22828334169484, -1.08973177434185, 0.127805899621275, 0.425140590827894, 0.214414741259754, -0.261083703055955, 0.250943509101405, -0.0697879472999849, -0.388131869207295, -0.957464582459925, 0.25962582353087, 0.0192075506697944, -0.149273411842119, 0.201847314090332, -0.0564306681947466, -0.0336757521987126]', 1.79); -insert into credit_card_transaction (time, features, amount) values (157856, '[2.05534908604041, 0.0841374965825322, -1.29722359923244, 0.33111646993233, 0.153078401225327, -1.12754270891507, 0.29442090303109, -0.403542072735935, 0.374511579862079, -0.0294502273364214, -0.179856858308834, 1.02373714614744, 1.24148684657356, 0.252718846228885, 0.624932005265181, -0.351854302151727, -0.464742147968007, -0.296263238405889, -0.365638899418296, -0.144354978991445, 0.278485159894634, 0.984355184443863, 0.064104682998156, 1.21596852576972, 0.278990261805842, -0.171699129172392, -0.0165538768696907, -0.0449067067384646]', 3.79); -insert into credit_card_transaction (time, features, amount) values (157857, '[2.062958371039, 0.0374118687130256, -1.23238631328565, 0.354402294981913, 0.0255049866263097, -1.23013794186386, 0.249955473863987, -0.412144622270941, 0.504484687246529, -0.021886362923122, -0.503478024728184, 0.774307206790292, 0.933662823360504, 0.291939208034237, 0.727805958460171, -0.27854796170213, -0.513597148924842, -0.216909366100751, -0.332601982233066, -0.196549557490007, 0.274938407751407, 1.01393494641999, -0.0032273479077474, 0.0485636061013697, 0.268049133291928, -0.101561794485043, -0.0072822780080278, -0.0563001426893159]', 1.0); -insert into credit_card_transaction (time, features, amount) values (157858, '[1.18157870250369, -2.26084423770886, 0.328917436013441, 0.359712601878124, -2.46558566741515, -0.623433771763082, -0.817246592064876, -0.164288768593195, 0.385780619264421, 0.38947186814969, -0.124518005204928, 1.21158132789166, 1.86252347165147, -0.690367423879642, 0.747558107269647, -1.20363557096104, -0.246565966172785, 1.53137017520168, -1.72640925552612, 0.303274489442358, 0.212195378393862, 0.415929782511567, 0.0063310035918036, 0.958262657320884, -0.697765868671362, -0.375895419118922, 0.0304260329009418, 0.0674369928222688]', 427.8); -insert into credit_card_transaction (time, features, amount) values (157858, '[-0.285060437222363, 0.398444681342833, -1.63777311632013, -1.25444027336329, 3.52089269019313, 3.04248046652764, 0.343326673841843, 0.818680942487868, -0.39610984935726, -1.04047177138057, 0.185781787589992, -0.344012476237531, -0.298991365523662, -0.538194721490538, 1.13177946103248, -0.0926321684099298, 0.391015259888771, 0.638623515519903, -0.201123728209062, 0.130945753989379, 0.364238611494246, 0.920123282286803, -0.121531199871469, 0.638238398796436, -0.320486640294529, -0.122157560964199, 0.216835034408679, 0.233610744325548]', 31.33); -insert into credit_card_transaction (time, features, amount) values (157859, '[2.25531756741648, -0.728737266335447, -1.61407454275529, -1.04719983638802, -0.255346188428555, -0.855547645554183, -0.394301485905557, -0.295472444662657, -0.458816204961659, 0.885603993615658, -0.990917799826062, -0.991988740209498, -0.154195209329196, 0.0816910672084263, 0.439545317703883, 1.0181100669324, 0.0370028413909377, -1.11427321253212, 0.553885950892409, -0.0191625160524406, 0.402426751515702, 1.12458222329607, -0.0113625780670281, 0.521917148016722, 0.249189228682813, 0.052855380599109, -0.0473898334149382, -0.0623947804171946]', 15.0); -insert into credit_card_transaction (time, features, amount) values (157859, '[-1.76156073693198, 2.24809372244951, -1.62956269742749, -1.61315396238162, 0.795771625258495, -1.40296619787448, 1.52316929236213, -0.207766560200248, 1.02074902604269, 2.08673774061626, 0.469382157875629, -0.203142738431112, -1.64396640553965, 0.573774804015273, -0.583698249471286, -0.504210017428086, -0.790736305530579, 0.24938292184699, 0.0739926413176555, 0.782990121796363, -0.010993774693265, 0.757758644884818, -0.237946347399714, -0.3461125366316, -0.0115550313594168, 0.069825569513537, 0.660851089908105, 0.188171830007109]', 7.69); -insert into credit_card_transaction (time, features, amount) values (157859, '[-1.49690673173603, 1.21086579197235, -0.0045701849654747, -0.258420767092212, -0.0725201006481231, -0.402729968702352, -0.384362184970498, 0.935465162541433, -0.0116594093433838, -1.31028519583209, -0.803422742830355, 0.395364963809889, 0.689080294918112, -1.03297507055313, 0.304272957686309, 1.00151092125707, 0.648408728057791, 0.274869037765567, -0.514391647279716, -0.0982329000933663, -0.162758470401473, -0.732194481053031, 0.0734308530124817, 0.560831525503217, -0.204672591618141, -0.0322806502465715, -0.254617060241025, -0.0232005549027079]', 10.0); -insert into credit_card_transaction (time, features, amount) values (157859, '[0.132328260110561, 0.559162285845658, 0.463002797535602, -0.317521049404263, 0.808976543454281, -0.982835849392863, 1.23199499936075, -0.753896960395423, 0.264605008197533, 0.227898100067521, -0.662527026864023, 0.0057162107925079, 0.0801521555170468, -0.30278503733417, -0.0589371603046416, -0.528320150031622, -0.415717435275568, -0.843314888906276, 0.362569532263927, -0.047478146484045, -0.245992985421662, -0.326829587263855, 0.102511555820814, -0.0073609724267186, -1.02420721871642, 0.0643554156859474, -0.512804210146063, -0.330712800041969]', 1.98); -insert into credit_card_transaction (time, features, amount) values (157860, '[1.77515295239732, -0.991510743470274, -1.96380871022228, -0.883603012511522, 1.71194224603874, 3.70009539285151, -0.910823445845902, 0.962892631161506, 1.09011834416348, -0.238640606497804, 0.019527909849207, 0.556805701145316, -0.0295507095468021, 0.211939250823184, 1.09147637936223, 0.121754209402216, -0.569456967259642, -0.457012463675216, -0.0907126191866462, 0.0955213154367886, -0.174089094668087, -0.73644068780735, 0.34900319133562, 0.67518345755326, -0.514468017508317, -0.316209291717666, 0.0028495279318687, -0.0238627050949399]', 119.0); -insert into credit_card_transaction (time, features, amount) values (157860, '[-0.142051582617379, 1.14096330176463, -0.442775972771893, -0.689439971531945, 0.879699015057055, -0.515459217509872, 0.849827736770779, 0.0695165633625453, -0.110815299190136, -0.363991587525768, 1.17155245741182, 0.582184104103802, -0.210818676307783, -0.85694528816157, -0.873943846180469, 0.419066586515102, 0.212774650613408, 0.148185277579467, -0.0860482474064571, 0.138711262148833, -0.308359558058872, -0.706423117840218, 0.122292249135052, 0.594315940972221, -0.433115110251284, 0.0975585641889865, 0.32854355269546, 0.134657321110325]', 4.97); -insert into credit_card_transaction (time, features, amount) values (157861, '[-2.96357080997129, 2.1650067956824, 0.103935712419153, -0.232358239340725, -1.52231571253481, -0.532397029409427, -1.29355065858134, 1.99123436226992, -0.625109756177384, -1.73787167366151, -1.45388287367673, 1.36329608350479, 1.34786629257774, 1.13657125749964, 0.472572444135707, 0.443900769910772, 0.433047559881013, -0.0106435985743426, 0.379947648506217, -0.586620813757666, 0.106928927356735, -0.447181571140975, -0.0638718333482507, -0.0865347151317248, 0.386370510086327, -0.454903224457373, -1.05082280836792, -0.311628524991711]', 0.12); -insert into credit_card_transaction (time, features, amount) values (157861, '[2.21823721149997, -1.46315018810705, -0.785747596251717, -1.62152252214866, -1.29816006129773, -0.447642348865608, -1.19845341566177, -0.121523105986679, -1.42038775438825, 1.70346303804077, 0.494806859794691, 0.123070103989853, 0.98254284422534, -0.428370958633208, -0.864473775530415, -0.238108251523691, 0.0738189020231212, 0.481722840904335, 0.210614311839494, -0.335280365141519, -0.10672229859758, 0.166967968847933, 0.174978613195283, -0.338981886745532, -0.22986492283471, -0.182060726507927, 0.016310364056674, -0.054457048899242]', 39.2); -insert into credit_card_transaction (time, features, amount) values (157861, '[-0.456886723145933, 1.05737971736289, -0.0166428949712578, -0.867822511754261, 0.553752975439729, -0.541404530688917, 0.848166160282426, -0.0619542776405086, 0.0902244542089866, 0.28671470250161, 0.515820739541806, 0.584191238398215, -0.319280721913853, 0.213434091443719, -0.967656370746243, 0.109949544499459, -0.727922515335695, -0.229542248742426, 0.235348489617812, 0.0528061154988361, -0.223781080349237, -0.545599860580502, 0.0466600665834584, -0.415477587760806, -0.473034983292476, 0.1258925391657, 0.0736249192326495, 0.152394338664803]', 7.14); -insert into credit_card_transaction (time, features, amount) values (157864, '[2.03894121293921, 0.241511138776091, -2.79413442153445, 1.15723274777169, 1.32688267168467, -0.109779510107214, 0.38911098925061, 0.0188212010951561, 0.314203110815138, -0.121782438684363, -0.190421950114032, -0.970514118290818, -2.70138656250686, -0.3582982054163, -0.673517343462302, 0.385869847159292, 0.523906864519133, 1.17171606333828, 0.257701273252827, -0.323399717409358, -0.0776003431449847, -0.190486871599213, -0.107993634449155, -0.54925052392386, 0.498880489443572, -0.468905203526214, -0.0245221037817681, -0.0464512070867046]', 17.18); -insert into credit_card_transaction (time, features, amount) values (157865, '[-0.313323463958786, 0.632376259311107, 0.29512940271073, -0.633751526769824, 0.521940269990254, -0.927914785959757, 0.872894367941366, -0.103640554904512, -0.22899830031809, -0.125025558359861, 0.866704035821042, 0.859562676711219, -0.0711443171864997, 0.271862000867153, -1.08838932474487, 0.0745732129293522, -0.639493412401333, -0.283686108094917, 0.192923938815524, -0.0788723236117352, -0.201694313601081, -0.422014083402148, 0.277153520229165, 0.0661532314380647, -0.492290471915714, 0.110904549820421, 0.139240068148655, 0.0612397718041035]', 7.14); -insert into credit_card_transaction (time, features, amount) values (157865, '[-0.283381145646162, 0.865941939843279, -0.304250232515033, -0.477897404666189, 0.33665478710437, -1.15208798032244, 0.698611426428284, 0.166240510927027, -0.212691520738045, -0.782183846435798, -1.46238731323645, 0.0651593562090485, -0.325187834913675, 0.562219757664704, -0.767158714520289, -0.130305346440044, -0.187905707397729, -0.585587193521494, 0.112876711725679, -0.282981722857364, -0.0363362290512662, -0.160842678520698, 0.191091025307775, -0.104403114298763, -1.22209681434188, 0.0862144881157604, 0.0720594421031196, 0.169780033801031]', 10.74); -insert into credit_card_transaction (time, features, amount) values (157866, '[0.0213854350252852, 0.0779246208762383, 0.758548415858107, -0.42678718817739, -0.336747025765528, -0.524122517788203, 0.543094355937069, -0.017708618858103, 0.605688140202642, -0.764610436739245, -1.03918633964772, -0.0929879626083142, -0.42364553523217, 0.0329404635775625, 0.556115631785696, 0.0101720039707508, -0.388122197905019, -0.121009201269123, 0.272664946939963, 0.0603466582067189, -0.0707435185515384, -0.300752074705935, 0.463839356256913, -0.140340455531774, -1.13261415329168, -0.37629378405746, 0.117633760642754, 0.129313289839772]', 96.85); -insert into credit_card_transaction (time, features, amount) values (157867, '[-0.68779198262862, 0.996084243399009, 1.02123215795639, -0.392809937789044, -0.16316483379291, 0.0301552356689265, -0.0988483202813573, 0.55614497632988, 0.317956847278456, -0.898342596399979, -1.23568736479504, 0.608120812575767, 0.976925263312046, -0.156951160775036, 0.268495717458668, -0.0357724986714881, -0.319629018399072, 0.405532513172817, 0.177933348426048, 0.0292873229780241, 0.268655452237953, 0.977792758049683, -0.201545123771574, 0.759883645495179, -0.129958835774833, -0.18557041036972, 0.325164390155835, 0.176550972309605]', 0.01); -insert into credit_card_transaction (time, features, amount) values (157868, '[-0.364398348967928, 1.59733315853756, 1.75161321849181, 4.29005549101201, 0.264393178921372, 1.36451601362395, 0.0807976973315875, 0.269726675523837, -1.40074952428764, 1.8134334456855, -1.16197755162496, -0.0040626198491432, 1.74467935667803, -0.592694831930135, 0.567361756319281, 0.0406961523874426, -0.0962061966950021, 0.413457025747264, 0.990609721824906, 0.555365594380113, 0.192101834579288, 0.974530389567436, -0.0768549533097639, 0.708801447086545, -0.509850513824215, 0.427342092313316, 0.614883397179661, 0.327002375779003]', 20.0); -insert into credit_card_transaction (time, features, amount) values (157869, '[2.06980685354973, -0.863579693969263, -1.42740541359924, -0.845321005129525, -0.413993431215116, -0.0351673461050645, -1.05471785618858, 0.140981924657615, -0.11025526985459, 0.236675832460463, 0.905396069794825, -0.491467755021709, -0.280190576610429, -1.84597484185882, -0.18998139156363, 1.98741978365491, 1.01975257463699, 0.201878094427111, 0.596009066557203, 0.111845716423449, 0.274006710999891, 0.72708219119874, 0.0567390972955766, 0.109913329663389, -0.142530876045586, -0.109784085979704, 0.0140180672730919, -0.0153263458042373]', 49.0); -insert into credit_card_transaction (time, features, amount) values (157870, '[2.00980304896714, -0.0611619152510945, -0.994514023349943, 0.318080889495356, -0.0803144339766726, -0.927732723415108, 0.126286901259942, -0.227986958836113, 0.185579222933123, 0.22379387547165, 1.14194046649691, 1.47306147454536, 0.555559836925861, 0.360153045072208, -0.732011279476894, 0.0364926847927436, -0.538716634964072, -0.476960754806422, 0.395941218220649, -0.174752319866999, -0.223541365552769, -0.521903295590109, 0.330606403083856, 0.0937673580176166, -0.321557320019921, 0.17051713159915, -0.0708969036195145, -0.067584936177259]', 3.59); -insert into credit_card_transaction (time, features, amount) values (157871, '[1.46500439134067, -1.02205465396596, -0.962494360832829, 0.951331138850521, 0.388866090669564, 2.07794617347438, -0.659606104718875, 0.624189304870921, 0.738709058529508, 0.179711775348874, 0.668109972797724, 0.605067416568192, -0.430424249114796, 0.387392943427837, 1.02383503769648, 0.0668671309150085, -0.449804739256787, -0.129312814604111, -1.32718686161072, 0.0834228851888532, 0.418729406050595, 0.920927985217283, -0.0067786229360114, -0.967583994709863, -0.315062701360656, -0.499281977467194, 0.0559284370542651, -0.0174922161053317]', 203.65); -insert into credit_card_transaction (time, features, amount) values (157872, '[1.99647328097931, -0.0381841365560539, -1.63037686768932, 0.510740886050215, 0.306297274192582, -0.373240479551309, -0.118707476599749, -0.0066143975628803, 0.596754410795014, -0.344505737253383, 1.0215691905776, 0.750525324571754, -0.425349883522695, -1.06072807181574, -1.05617363268476, 0.148392899669498, 0.727410554509579, 0.453266133022171, 0.160628028154977, -0.17994528471532, 0.039707920133854, 0.358452898449485, 0.082079749014033, 0.66443916446072, 0.0546860860072242, 0.589264082461755, -0.0519493845136182, -0.0434301095203548]', 2.0); -insert into credit_card_transaction (time, features, amount) values (157874, '[2.07667621413615, -1.14266363000882, -0.196127468714129, -0.661195101757663, -1.09178595073599, 0.399910473942942, -1.44816337694613, 0.135126214003721, 0.254706538211903, 0.817920535569862, -1.5668839399739, -0.101350586892645, 1.30479553553733, -0.589712907860213, 1.3885558597828, -0.239164211901475, -0.982387371879407, 2.00395351833037, -1.12473022788478, -0.402397001917534, -0.149441799666046, 0.0453216196244074, 0.253415270951199, 0.0431764136687273, -0.599204880379358, 0.605916449990743, 0.0113482288645007, -0.0223315212734847]', 45.9); -insert into credit_card_transaction (time, features, amount) values (157874, '[2.18843802436568, -0.412798189654439, -1.6295927808053, -0.211671821073954, -0.184440128614853, -1.36486559600937, 0.237835818561819, -0.574413031439371, -0.889479299724334, 0.887774784741999, -0.909480538058839, 0.680776627888251, 1.35767112811994, 0.048290968809378, -0.768194125342138, -2.18238506188074, 0.138753144521003, 0.353218149564336, -0.578375843433238, -0.551831265129919, -0.342896043061234, -0.254675497843972, 0.105143081167337, 0.105657412126341, 0.165450240296441, 0.601038343499216, -0.068177613463495, -0.0700240680016269]', 14.0); -insert into credit_card_transaction (time, features, amount) values (157876, '[0.05766828044699, 0.795595532573441, 0.212103643328337, -0.611327658013335, 0.3878452830345, -1.06458305943904, 0.940253876951564, -0.141010009339799, 0.118105158088936, -0.380658366271332, -1.23010504338887, -0.445946319623032, -0.969892946930854, 0.303356897373207, -0.282364792772973, -0.0031631419261157, -0.404746746645889, -0.577389969340814, -0.0213295548251179, -0.101194628914302, -0.282593449988528, -0.680626109535534, 0.0607788818246599, -0.14469382272303, -0.504107662577845, 0.15083550838898, 0.240837185041486, 0.0936294135667924]', 2.28); -insert into credit_card_transaction (time, features, amount) values (157876, '[1.54785715827941, -0.985304866879443, -0.338721628084299, 1.59768003853413, -0.677484352390009, 0.464078604060804, -0.479724304549656, 0.167239802283361, 1.50822093207687, -0.0863835190177967, -1.62791013508354, 0.273970136809261, -0.493270440442393, -0.354579688486155, -0.616648974019689, 0.0470318673341477, -0.323158342674437, -0.106907556534061, -0.0988073573640446, 0.135468914465149, -0.116289824802597, -0.526088358581744, 0.161008144658474, 0.523077344490894, -0.327561547912714, -0.905578537885612, 0.033064247033536, 0.0144412227555499]', 207.97); -insert into credit_card_transaction (time, features, amount) values (157877, '[-0.234077557803502, 1.05382611960802, -0.29533586182078, -0.931633798689611, 0.984728925396521, -0.242332234035449, 0.946296152054668, -0.184674576244223, 0.17194563917938, -0.0957683505043199, -1.56559722765138, -0.0745298580635738, 0.321109682303334, -0.0848462704632371, -0.29970536622435, 0.10814934943764, -0.734561282503127, -0.509340662270577, 0.217451078670974, -0.0174220321554876, -0.259844100523559, -0.755727625854643, 0.0308405743776666, 0.025659380870398, -0.400666345891702, 0.11108107565499, -0.167821624370546, 0.0921694873115362]', 5.99); -insert into credit_card_transaction (time, features, amount) values (157877, '[0.0397928079975763, 0.74938020001181, 0.146912561105988, 1.80354074417267, 0.796011893126402, 0.62870877871757, 0.835704966313316, -0.0601162510144385, -0.712171607054627, 0.193352872838765, -1.52422517913491, -0.477492078900505, 0.28451354668182, 0.358936115673517, 1.33482255639452, -1.17258987588461, 0.313593799999029, 0.559912710790312, 2.49465459614725, 0.411515052395084, 0.288991006705053, 0.920210390402821, -0.129488800757688, 0.0600154896854977, -0.156255769008756, -0.0638682267914006, 0.225413282865286, 0.201849964297183]', 83.5); -insert into credit_card_transaction (time, features, amount) values (157877, '[2.11129378499563, 0.317328116684567, -2.49533074047541, 1.18132367306935, 1.03109789401673, -1.15133128671849, 0.931180264508032, -0.431307889682383, -0.0403003450373583, 0.431813061611664, -1.22131147221688, -0.809473791630871, -1.90150265231977, 1.22799810095014, -0.0648155960119166, -0.79248207213154, -0.117799651163266, -0.292387768620937, -0.306107816557538, -0.392207011700612, 0.277229503225996, 0.897965696195175, -0.169973589842068, 0.651934146411564, 0.839757062608298, -0.215342812256031, -0.0662014272622437, -0.0770319699431879]', 1.0); -insert into credit_card_transaction (time, features, amount) values (157878, '[1.88014049625136, -0.237915221632302, -2.68383509619243, 0.291842672945627, 0.817611167690785, -1.21352252150818, 1.07424358470135, -0.501471875920381, -0.0981340095280535, 0.195731610420852, 0.523539217310717, 0.555860311059213, -0.749224032964665, 1.20601245536685, -0.680564876462848, -0.748008799248941, -0.402836685949778, 0.0955972112171333, 0.425646799639706, -0.0479670438834034, 0.391203211452122, 1.03111629250031, -0.37490208150116, -0.393956676004386, 0.774109915388234, 0.121629744713442, -0.104018722567514, -0.0801718998545801]', 124.33); -insert into credit_card_transaction (time, features, amount) values (157879, '[2.17249892419831, -1.19096549444029, -0.0820010124180848, -1.03231844185029, -1.14909029170875, 0.452251201616478, -1.67737188615546, 0.177969627761094, 0.578871096444642, 0.657675214838819, -1.44792273988426, -0.389860158659066, 1.21203122059043, -1.07071820375731, 0.649993197850087, 1.87199488665078, -0.435020210952003, -0.482944058225307, 0.468778278027942, 0.0873642012900552, 0.379627259603342, 1.19511009675924, 0.128584887597207, 0.0843630768514963, -0.279292267411652, -0.0835766872830982, 0.054316729364895, -0.0298474643347014]', 12.5); -insert into credit_card_transaction (time, features, amount) values (157879, '[2.05274728685734, -0.0921879761644723, -1.07581748328731, 0.413504357792781, -0.152692598186371, -1.13971030557606, 0.139878225827251, -0.283681119701624, 0.608005168768671, 0.0779160657351227, -0.767259767413408, 0.219649199574464, -0.389119867253037, 0.370540731637524, 0.136707508528965, -0.142685842533258, -0.20600912126465, -0.903405584650418, 0.0772074304777765, -0.25908874294004, -0.288438541424859, -0.712356227033048, 0.352169010186753, -0.0255118942847336, -0.328717016970851, 0.202547534660297, -0.0705314864338868, -0.0614292578826149]', 1.98); -insert into credit_card_transaction (time, features, amount) values (157879, '[0.0499921391158288, 0.876540077253547, 0.234221984885402, -0.620767325755901, 0.497596967404754, -1.0297796169993, 1.03939385135476, -0.216264705987973, -0.0812897635797646, -0.422088681926887, -1.00690501267434, 0.249624464111733, 0.340016138971994, 0.043358866927684, -0.425613056898751, -0.0638752124109122, -0.479783771655713, -0.736418897207415, -0.0310561164433962, -0.0082480248844283, -0.26505874450633, -0.57388835489194, 0.0544918040615365, -0.109312303386803, -0.458128631712838, 0.146331532986451, 0.247972565389031, 0.0967836731969802]', 5.99); -insert into credit_card_transaction (time, features, amount) values (157880, '[2.18412950325262, -1.17588049487899, -2.49646100543981, -3.05990952731444, 0.0497895904672459, -0.676950084196004, -0.102980791487013, -0.279683520269764, 0.587137340424869, -0.232910913897533, 0.218496124850071, 0.852689399447933, 0.114945152661132, 0.674168499828891, 0.452429361741913, -2.45808301404821, -0.298995865623851, 1.92353413931694, 0.875548245488397, -0.473957268907759, -0.217498156183065, -0.0575001281047381, -0.0744074632049275, -0.210324042823218, 0.429109206351033, -0.698235426900213, 0.0185080178312527, -0.0615865586600772]', 59.99); -insert into credit_card_transaction (time, features, amount) values (157880, '[-1.06141414204234, 0.728974303142099, 1.08044604195431, -0.53029453873708, -0.385430298477668, -0.485635702052261, 0.0165347810990226, 0.395749255245726, 0.244397134188002, -0.748882156252739, -1.22066208931087, 0.659063861137433, 1.18112203101784, -0.318163036737898, -0.195313630606613, 0.344152256796273, -0.484808107220988, 0.137076343601191, -0.387393530591788, -0.0080358257694072, 0.324178699193292, 1.0769959217569, -0.133239824117183, 0.0721063166568859, -0.265658562316117, 0.589497114652848, 0.140525620305627, 0.149831329425266]', 29.99); -insert into credit_card_transaction (time, features, amount) values (157880, '[-0.19940084273604, 0.612922008795064, -1.21906689030795, -1.76966119605003, 3.4850369490411, 3.41450792570283, 0.962793590572686, 0.0625071342201286, 1.06783712241049, 0.758181154263826, 0.460265311078421, -0.594123157223443, -0.476472059383078, -1.98053908513163, 0.437586324694517, -0.0491812386625616, -0.115456079970591, -0.0363571101002415, -0.661538697121491, 0.542658964373006, -0.261391738675452, 0.204753248298129, -0.0592533914170301, 0.624832210996268, -0.621098875236332, 0.274658083866873, -0.220523290165708, -0.5748785245172]', 13.32); -insert into credit_card_transaction (time, features, amount) values (157880, '[-0.41985234564337, 0.618955524398471, 0.507638907398175, -1.09150550964316, 0.817598217643477, -1.04373945707786, 0.959073952698126, -0.0872551994018337, -0.303732294801804, -0.732927476835559, 0.692179944556287, 0.288896432098005, -0.923384603637609, 0.703249973220436, -0.589076403835129, 0.143202589420198, -0.946366646308019, 0.551598952429705, -0.739042484138003, -0.248954097149514, 0.430508818458542, 1.19171137287465, -0.315690086574632, 0.0305521026729413, -0.0996383991449203, -0.287753956974471, 0.134165628875969, 0.162470442333951]', 1.0); -insert into credit_card_transaction (time, features, amount) values (157881, '[-0.939028004949903, 1.26138096622124, 0.723069981579476, -0.110048815111285, 1.13007185102484, -0.901947848892352, 1.81466311432403, -0.333335789704342, -0.786318984402208, -0.809032920158632, -1.22744283894911, -0.25843558856076, -0.635038510079284, 0.390143033393132, -1.07381130681591, -0.557902140313106, -0.34731126441343, -0.539255518277191, -0.750070846515459, -0.252976166214478, 0.0562515738915695, 0.185962275177467, -0.603705345473185, -0.0412560131521469, 1.32848405878253, -0.459214701431163, -0.132159646216888, 0.0312294103684668]', 28.74); -insert into credit_card_transaction (time, features, amount) values (157881, '[2.11304230886494, 0.530154513613536, -2.81664193554979, 0.527859691099781, 1.04269007921069, -1.13524227705307, 0.318297877090455, -0.300986443269425, 0.62550066218622, -1.19703858237365, -0.743395228863856, -0.722311824109756, -0.152322343729143, -2.52960760843535, 1.22842383686647, 1.03292820979781, 1.46277361250566, 1.56159289500729, -0.373073483521788, -0.146046251954931, 0.030844473814821, 0.290235591132129, -0.166683325800332, -1.28615201751914, 0.372326800865178, -0.0314536687874676, 0.0198887532288243, -0.0040164459182145]', 1.0); -insert into credit_card_transaction (time, features, amount) values (157881, '[-1.13257586532279, -0.878240414827419, -3.36433682323203, 1.45537351412798, -1.19158444676292, 0.264668874952401, 4.15572534209539, -0.210224185199998, -1.0089792070866, -0.490214283065661, -1.61796369469165, -1.17253055092891, -1.68772444390201, 1.62876467981738, -0.113151668758188, -0.950419955938666, 0.176847490290452, 0.38877382340554, 0.276706570500428, 1.58712438193028, 1.02239095805706, 1.60690233967136, 1.98535538806822, 0.557287375867389, -0.782758603481381, -0.406985188241554, 0.175998836780809, 0.407587130052964]', 847.67); -insert into credit_card_transaction (time, features, amount) values (157881, '[2.21854226765637, -1.51776618124299, -0.370169379322724, -1.61668373152127, -1.44618001177003, 0.0739980299342651, -1.60974220899998, 0.0433879464774352, -1.20543494083244, 1.64435907351827, 0.768865512009266, 0.572682191305892, 1.69032513727449, -0.846688981875068, -0.924081503617257, -0.228677729536057, 0.0959709836855245, 0.674474124257206, -0.0599748096083292, -0.309485728629236, 0.112441076739675, 0.913536019091172, 0.173551124535822, 0.810038836595524, -0.184917731097518, -0.0488764379725436, 0.0450803192590963, -0.0416526848595496]', 17.2); -insert into credit_card_transaction (time, features, amount) values (157881, '[1.82456579954111, 0.285269548500017, 0.721219666472839, 3.84627996628275, -0.407771752722293, 0.654994177842359, -0.747951656119178, 0.213241618586786, -0.30370546555363, 1.28570448264253, -0.838475557612555, 0.638621441149359, 1.418532399009, -0.669545064432435, -0.51392676989271, 1.31036460244074, -0.98117578004632, -0.185822807704918, -1.89856788105034, -0.171442513117518, 0.0761419676572011, 0.33092497993392, 0.394075743354188, 1.12578339663129, -0.496763384046174, -0.22215459320539, 0.0403964704519625, -0.0065449801991931]', 5.41); -insert into credit_card_transaction (time, features, amount) values (157882, '[1.23183702604858, -2.3078022901259, -1.46566047318755, -0.120914649468717, -1.04590363843117, -0.126741700326513, -0.040480625007596, -0.260674376550962, -0.274983403741572, 0.604619216280449, -1.2727916342833, 0.160966559348715, 0.983578199102177, -0.128526538470212, 0.244778249089618, -1.19130775237141, -0.298535638461135, 1.13434557497372, -0.872896188152936, 0.456180095676669, -0.105465685527946, -0.793743990144628, -0.123276601661981, 0.505905936216097, -0.502236456307837, 0.469469837392391, -0.12403113641271, 0.039003616767987]', 495.0); -insert into credit_card_transaction (time, features, amount) values (157882, '[-1.2074073866793, -0.693632629991395, 1.21953508532021, -1.92585716825867, 1.3050415283645, -0.87537314165601, 0.342976430480777, -0.170424620383533, -1.70606109434515, 0.246901365806081, 0.615140335756935, -0.134258103399695, -0.177955097810702, 0.175511548311642, -0.645184135638645, -1.1368849200887, -0.77757928605749, 1.75489480925424, -1.26297698678264, -0.13944922453755, -0.0560940438463554, 0.0373952142745269, -0.302662454293229, 0.779572627772124, 0.800720336393893, 0.750291881855443, -0.267820190775824, -0.175181399637481]', 32.0); -insert into credit_card_transaction (time, features, amount) values (157882, '[0.119800421480171, 0.788073645063828, 0.365200372933441, -0.387607948010135, 0.452526275847308, -1.03502750517791, 1.1210342297086, -0.278813741248679, -0.264081156877578, -0.612390949116546, -0.880933733434474, 0.352244037536818, 0.428150073716353, 0.0775234136321686, -0.472085098476475, -0.114799458206887, -0.450837275804333, -0.74537237603181, -0.118907046814831, -0.126732439265967, -0.189770144541781, -0.493129438473522, 0.0888197297521361, 0.0419827537903228, -0.494250829041994, -0.0631196228278945, 0.0146975914765788, 0.040745871605246]', 19.75); -insert into credit_card_transaction (time, features, amount) values (157882, '[2.04325553353614, -0.0709706101781754, -1.16661667802807, 0.220060735880992, 0.115631417930917, -0.694287759068737, 0.108691200062379, -0.196149674300795, 0.22692562752008, 0.227948625227471, 0.758284198709351, 1.29182788348945, 0.543025968713628, 0.351670313659763, -0.692851457223477, 0.160541914302167, -0.684034033449312, -0.332389352607154, 0.552835415865509, -0.17146814839193, -0.253047744884938, -0.603627446511822, 0.282616273178655, -0.32211341504361, -0.268476375071983, 0.196645061609387, -0.0715553071700417, -0.0723531804342788]', 1.29); -insert into credit_card_transaction (time, features, amount) values (157883, '[-1.15873971181859, 0.70325899919763, 1.54610220629851, -1.08340041232099, 1.20845991340509, 2.63648193395707, 0.28264858555381, 0.570527981334167, 0.399324870764302, -0.307130721692404, 0.900324061134677, 0.446975555786788, -0.391625613440374, -0.129585035298459, 1.24788507873968, -0.922136717336832, 0.119427491464606, -1.10374659599658, -0.451841929258245, 0.0144621585395087, -0.132966311302209, -0.0661382497524874, -0.280035365996672, -2.43289635816613, 0.0853859945829929, -0.291643109994768, -0.181032211023036, -0.120684777548576]', 23.42); -insert into credit_card_transaction (time, features, amount) values (157883, '[2.06725387544557, 0.0300951826532658, -1.90371091074055, 0.196124745688249, 0.627307449212471, -0.301370626182012, 0.0078489316468607, -0.0052266615751703, 0.408788982385648, -0.18081594107929, 0.538247473784603, 0.179468895652229, -0.867791635714255, -0.491579343032849, -0.3576225571492, 0.697836604644505, 0.0997321929053881, 0.46004852634077, 0.531699925499393, -0.190669164373386, -0.356044909614171, -0.997355464211032, 0.279391073758909, -0.0289500346935749, -0.254738841249825, 0.187474315901626, -0.0730797677539765, -0.0477001236971459]', 0.89); -insert into credit_card_transaction (time, features, amount) values (157883, '[-0.553637154246517, 1.21389938862376, -0.359671774201268, -0.820613714985574, 0.353400974743767, -0.59014161163548, 0.561825996181848, 0.387409362455909, -0.290824487729718, -0.167423003157977, 0.270549964694574, 0.9898573857443, 0.355775410991013, 0.470274803042924, -1.12093161117634, 0.333805786052442, -0.689613009746339, -0.110766666521385, 0.348006157600673, 0.0203625537091664, -0.226731201940427, -0.559406329794625, 0.0492251612222921, -0.494787794265399, -0.373623993288484, 0.156420833069395, 0.235836878624974, 0.0679559755008272]', 10.28); -insert into credit_card_transaction (time, features, amount) values (157883, '[2.19798276313271, -1.58615272836699, -1.1173872536323, -1.72439481773938, -1.05246030475932, -0.071600589496316, -1.21775824272381, 0.0079993281303919, -1.32949335558517, 1.72459878727408, 0.247030596991558, -0.528296458657413, -0.122653750525107, -0.167458001112183, -0.747549278610277, -0.197636255103138, 0.122837522672345, 0.595521156608868, 0.290476888892315, -0.334810510876988, -0.152303944037387, -0.112013772295834, 0.20649138723979, 0.313267500045442, -0.235181154980195, -0.234744920451181, -0.0056608967489571, -0.0484749320583119]', 60.0); -insert into credit_card_transaction (time, features, amount) values (157883, '[1.98589471424635, 0.903630293991162, -1.61326937640463, 3.49413030231042, 1.29863196619582, -0.347734717657882, 0.89673525355298, -0.336033345619095, -1.98468921250142, 1.73790385951012, 0.486091102613236, 0.77967383115114, 1.02681871009254, 0.702829814471268, -1.5178560305061, 0.709613555899111, -1.17010809272141, -0.153768877280432, -1.08406870959026, -0.217098222537903, 0.182073636741283, 0.555706480199687, -0.0666210603501569, -0.382129610727419, 0.432963752803149, 0.144288870372413, -0.082562071875591, -0.0791433501985024]', 2.73); -insert into credit_card_transaction (time, features, amount) values (157884, '[-0.817271394514991, -0.163029128651324, 1.24480415599954, -3.08176098544972, -1.17504789106182, 0.346712011175051, -1.68980352451094, -2.45047461701233, -2.22539903041487, 0.229526532325601, -0.03440632043014, -0.818319871117663, -0.583682311857783, -0.0239378390487275, -0.951436277240565, 0.545308693100736, -0.216297078845865, 1.2148932386441, -0.851938245102277, 0.185441497729212, -1.15044068178039, 1.04346059582974, -0.162060584547279, -0.380489077429246, -0.171248726852229, -0.154039406505358, 0.113180930810204, 0.141991689109871]', 34.6); -insert into credit_card_transaction (time, features, amount) values (157885, '[2.29474835971221, -1.26796842649129, -1.23807448235125, -1.63360450164355, -1.17992909166558, -1.14541413271088, -0.881508095050717, -0.259722463768442, -1.71942612040821, 1.88549170627129, 0.781681308914434, -0.656584797827918, -0.473477209427936, 0.24943691670009, -0.439981701637185, -0.415335812832912, 0.237622010055678, 0.609449646741765, -0.0766511117066568, -0.496330599236015, 0.116112987340357, 0.761466611628846, 0.0820151942167367, 0.0717760964135275, 0.0234135512209999, 0.0483479863054602, -0.0215447093937162, -0.0726288848855631]', 10.0); -insert into credit_card_transaction (time, features, amount) values (157885, '[2.06889405405631, 0.184193167469339, -1.65314657892988, 0.438584356464676, 0.391986155446804, -0.933348429393487, 0.207225083323461, -0.25987061052302, 0.442194254554936, -0.386790759955339, -0.425216772252428, 0.337957653286203, 0.342054015341905, -0.897214217170686, 0.16515985722717, 0.272427212342383, 0.494018001030447, -0.377702661281999, 0.0122031613563231, -0.141302067952325, -0.354733673740883, -0.901203222014449, 0.349407374885248, 0.646722881905736, -0.261674421643166, 0.164324568461553, -0.0605385943916286, -0.0288538580025564]', 1.29); -insert into credit_card_transaction (time, features, amount) values (157885, '[1.85600394494833, -1.09198857180893, -1.09452599576508, 0.536984316414389, -0.460546864408321, 0.257929760240599, -0.548791010118107, -0.0067318584342177, -0.136464831543382, 0.953864575021698, -2.00699492880703, -0.609613117944753, -0.204192272236451, 0.0363150765498185, 0.317903266841066, -1.06335257707425, -0.57955330004991, 1.69862339527506, -1.00779835134191, -0.356007437504851, -0.29101238164635, -0.599191844879728, 0.0851439804199295, -0.0533491535277418, -0.12722555891233, -0.67490374347964, 0.0296650758442623, -0.0061755116305968]', 156.0); -insert into credit_card_transaction (time, features, amount) values (157885, '[-0.320619269371897, 0.605209835079518, 1.27959785240153, -0.122606532184038, 0.196272125471117, -0.628688169009073, 0.591003640009898, -0.193100826403913, 0.55248086100423, -1.12153121083429, -0.734540102060566, -0.657912052841648, -0.623511178077178, -1.49391293570279, 0.788533961520794, 0.230325382547438, 0.444602184834493, 1.1159653865034, -0.0807412751598658, 0.0727742081825356, 0.246302955526387, 0.987062232491391, -0.335734973749354, -0.034227588355407, -0.0978577793765973, -0.100826259237219, 0.0166484845321075, -0.0392570362947684]', 26.77); -insert into credit_card_transaction (time, features, amount) values (157885, '[0.483280098868548, 0.795439601343825, -0.787744160317465, -0.77142686815956, 1.62362135468515, -0.273073210990403, 1.16077119070475, -0.331007229898111, 0.118464561062453, -0.751754380624121, -1.58918979013476, -0.576608871950142, 0.0711928396996683, -1.19970581592359, -0.230940190534974, 0.526751513073209, -0.127094580160387, 0.123352835631878, 0.172251335948822, -0.0480667848623967, -0.347461892671758, -0.766125074399781, 0.0415310529735597, -0.417390729964411, -0.972197386366985, 0.0290584659413162, -0.0633765164996542, -0.0805398822458552]', 1.98); -insert into credit_card_transaction (time, features, amount) values (157886, '[0.312862549298874, -0.743958604005151, 0.559306442736998, -2.31491459505117, -0.690616264717074, 0.03095845816574, -0.282043849795607, 0.0666929473563158, -1.64248409393381, 0.817090839830718, -0.272797965580411, -0.137186894210367, -0.0438920043070187, -0.790076129259981, -3.17544443480289, -1.1693509999806, 0.765536567619508, -0.191493197410778, 0.716991959901502, -0.407079063325381, -0.407974167768931, -0.531850928873852, 0.0582023203039284, -0.42890224334183, -0.412079547241503, -0.390500422480475, 0.0907146866101243, 0.0278977358144258]', 27.25); -insert into credit_card_transaction (time, features, amount) values (157887, '[1.69354481695103, -0.93352272348415, 0.110547292142508, 1.30452360939845, -0.0342203356701667, 3.0311426200953, -1.54263452733846, 1.00249082852558, 1.98406402390909, -0.146762258712861, -0.165041245376622, 1.39042989753347, -0.38849658401662, -0.803075239996122, -1.6046422502356, -0.834430731671016, 0.372854156108188, -0.734465838337716, -0.472053814670556, -0.280424008213518, 0.0109601422966865, 0.547668653413227, 0.205219540403822, -0.951180525077399, -0.313632647362343, -0.56230188901431, 0.150110799094366, -0.038028802810816]', 35.3); -insert into credit_card_transaction (time, features, amount) values (157887, '[0.135027430350424, 0.920278625154535, -0.583663051473421, -0.753509924737852, 1.10726333133621, -0.278478465931337, 0.815269492379029, 0.107700739220223, -0.160075808549985, -0.642841346782861, 0.482734806645038, 0.0116770900703293, -0.779354441357915, -0.692178665658762, -0.822350430810221, 0.692778505013856, 0.0001973632364672, 0.518285317532906, 0.231213271296817, -0.0049790312859904, -0.32763875025139, -0.866721587512347, 0.0391735349368955, 0.069226084226781, -0.418672829244721, 0.124532788887214, 0.212747275554986, 0.0680562312063862]', 3.87); -insert into credit_card_transaction (time, features, amount) values (157887, '[2.10988271481442, -1.78093153732935, -0.953829775470035, -1.59271108628439, -1.42800109805144, -0.374947423416119, -1.23143406139603, -0.0345887024517971, -1.16956543890746, 1.72317244519138, 0.0772248273219857, -0.794689182879176, -0.612448533457307, -0.0988605027703316, -0.666138600432693, -0.107801727963083, 0.112094221016041, 0.745681782331502, 0.261066816593294, -0.310286815632649, -0.0987222147364173, -0.0278900838648617, 0.121329185559705, -0.537242905064787, -0.286710461923956, -0.173410082273102, -0.0061298239093359, -0.0489005622039551]', 100.37); -insert into credit_card_transaction (time, features, amount) values (157888, '[-2.08715181913104, 0.594585924108475, 2.19073193370848, 4.65141314809625, 0.722306275989519, 0.521332136187854, 0.54503775832058, -0.237098441290528, -1.11072352586371, 2.27297768082269, -0.950129942446287, -0.659912694556202, 0.230523623624335, -0.636668203937824, 0.481824828118569, -0.272821003876924, 0.138479472318522, -0.395195128691066, 1.72235797047574, -0.223231024062605, -0.73138037330872, -0.895055696367621, 0.658519224960902, -0.0144153578355398, 0.070155837783745, -0.0030616055276322, -0.594921655033415, -0.508382033286906]', 18.96); -insert into credit_card_transaction (time, features, amount) values (157889, '[-0.677666306601202, 0.110744746525366, -0.306879494512597, -1.06898574099887, -0.0303695995332362, -0.461027109485463, 0.174475281538574, 0.258792034495589, -1.44446230969727, 0.243078418546412, -0.0126966446653426, 0.329523841637215, 0.247774396740235, 0.638390390702429, -0.477942958571961, -1.62777822765838, -0.291827732396814, 2.16689203345562, -0.241512768047151, -0.643773536293986, 0.162818422764468, 0.946824362808555, 0.0014413512349498, -0.452470295312771, -0.404402450445984, -0.063531492107149, 0.0230944434218155, 0.103892980337779]', 51.34); -insert into credit_card_transaction (time, features, amount) values (157889, '[1.89832533749866, -1.50920037414178, -0.743811571585612, -0.840210417716038, -1.17375244960787, -0.282244172485379, -0.995042019649838, -0.0313071823039281, 0.214075191635019, 0.714255078858815, -1.05289002752096, -0.988559388526399, -0.267175182301721, -0.339013334273561, 0.723691653550387, 1.51366788459664, -0.0780876753099101, -0.743078088641473, 0.323372516792785, 0.274473491894428, 0.460710730993695, 0.976621736748005, 0.0231359798900434, 0.602953667107258, -0.23264225192727, -0.103203442124721, -0.0212740003387244, -0.0151065589080708]', 160.0); -insert into credit_card_transaction (time, features, amount) values (157889, '[1.95966876231186, -0.828504027471752, -1.39050658530932, -0.941804233909333, 1.44708931414929, 3.96280004868419, -1.50330977432456, 1.12702195176584, 1.45673537366681, -0.2061528865365, -0.0845074484335585, 0.511729493811233, 0.0672989827932338, -0.0909342624561307, 1.32603246917828, 0.356977723407008, -0.712068001582958, 0.0457410723398313, -0.330862191108304, -0.11531205683397, 0.0552452696231311, 0.233811887470577, 0.324486562816044, 0.7095618749032, -0.423206101977045, -0.0555100711134175, 0.0513990978232612, -0.0374459161865824]', 8.49); -insert into credit_card_transaction (time, features, amount) values (157889, '[0.124200454762395, 0.961385463800489, -1.22065370215572, -0.156712971783243, 0.335791329590982, -0.928452441102013, 0.534084240774119, 0.31189098323403, -0.275499079615578, -0.997715464436744, 0.633087562237307, 0.0028842216291599, -0.909259088274663, -0.263244998077908, 0.0821858523371013, 0.579309946941751, 0.350550903244017, 1.48130258403016, 0.141096397827299, -0.205660517976569, 0.361826442680131, 0.909013633507647, -0.0567278710658409, -0.443308024596524, -0.273943264791258, -0.147538431744579, -0.0889485518362817, -0.0574033679750877]', 42.81); -insert into credit_card_transaction (time, features, amount) values (157890, '[-0.381967377763217, 0.569619565849526, 0.689143181347447, -1.18691951640229, 1.28858030932289, -0.699361649484901, 1.23611353496332, -0.334581244735284, -0.0327251827347307, -0.695167516152613, -1.66777738795247, -0.603358539317978, -0.6407052516771, 0.0386840741073267, -0.481894171200271, 0.175568244102361, -0.849904449637641, -0.556947081263705, -0.264980756741651, -0.108607157814486, -0.306789395675268, -0.77077544629133, -0.201127985159892, -0.797093352843907, -0.187317767980845, 0.115491985589697, -0.126420214119023, -0.100664223940674]', 0.0); -insert into credit_card_transaction (time, features, amount) values (157890, '[-0.0617282834441294, 1.18210247746459, -1.29807569371835, -0.528633954866264, 1.0324392910159, -0.92275296454125, 1.13589380425663, 0.0577158771232668, -0.851292723437338, -0.506591687736834, -1.3471054926443, -0.339845877015665, -0.314175926086869, 1.06013648217101, 0.0884715904890232, -0.367590665016897, -0.168996827991677, -0.318279922833008, 0.321508232511393, -0.221378357510041, 0.27428672742906, 0.69213355377427, -0.0084564022039689, 0.469134436708823, -0.820371533316482, 0.477782169365201, 0.0372189912326145, 0.180105421031703]', 16.2); -insert into credit_card_transaction (time, features, amount) values (157891, '[-0.79761184988351, 0.0890264281673536, -0.297355996257082, -0.672519014708262, 2.26215957335878, -1.54555949606719, 1.02827726991579, -0.334731493287736, -0.742249673570023, -0.607282103333396, -1.08240579390628, 0.186773289705327, 0.465186993112988, 0.385069701442077, -0.772324410384925, -0.660851439777217, -0.256849610911999, -0.62242517190866, 0.0906999534808683, 0.162904944617692, 0.259202148843272, 0.584549138378743, -0.210682980573219, 0.768047874143922, 0.249427547046301, 0.737383729361696, 0.0008452648953466, 0.15554389046145]', 6.0); -insert into credit_card_transaction (time, features, amount) values (157891, '[-0.21529713964505, 0.655655366022243, 0.465820799990057, -0.645669022634821, 0.348884523446231, 0.0037917485997152, 0.177035958170428, 0.233975497918728, 0.299115785401724, -0.894793011162356, 0.788972426797644, -0.0669656099925482, -0.914533925519109, -1.23018903362898, -0.404244817601481, 0.950536395477052, 0.130238906166253, 1.35474175645286, -0.531856693329135, -0.167360701415443, 0.382150226981118, 1.12546067563067, -0.0525686284305769, 0.637486687098099, -1.09023955654171, 0.413035652651385, 0.086689462116785, 0.257176971635437]', 10.78); -insert into credit_card_transaction (time, features, amount) values (157891, '[2.20412820462168, -0.285749296036878, -2.55387738827129, -1.70072885662244, 0.490468153989234, -1.68100610858093, 0.668865942421141, -0.576324764876292, 1.23437045843367, -0.7480413924731, -1.13102117065232, 0.128575266774618, -0.435326457508107, 0.882100136919085, 1.10931593149754, -0.959382367993489, -0.27389628937389, -0.228101887266599, 1.04494510040716, -0.215852085745831, 0.0671675009353499, 0.374811742063742, -0.121130958918484, -0.722964058804574, 0.513003162531862, -0.0256384117699995, -0.0606470169656779, -0.0841856587340256]', 13.72); -insert into credit_card_transaction (time, features, amount) values (157891, '[0.000747765737877, 1.11258043473596, -0.472999174504701, -0.835378697263397, 1.25833100037577, -0.263414792300138, 1.1267300955721, -0.188293900419061, -0.0369267104743596, -0.167414796216749, 0.906065799959952, 0.68199836534875, 0.542786247626516, -1.23783323290705, -0.932426104829955, 0.502215137468959, -0.159845637322139, 0.279609766389852, 0.162726749666034, 0.276108812109608, -0.389427142446033, -0.734536016790253, 0.0052900648809089, 0.189303903840698, -0.359045659764754, 0.0909492289920093, 0.168152356267895, -0.0936801181472963]', 10.11); -insert into credit_card_transaction (time, features, amount) values (157894, '[-0.0909708413648566, 0.767258537189037, 0.23445132084025, -0.492644222263285, 0.807029961359451, -0.500788323759771, 1.20594889939791, -0.260792231891838, -0.424607203763469, -0.601780033042193, -1.49364096026904, 0.152425183572876, 0.844011032816837, 0.050818922799333, -0.162537601658165, -0.138165159657597, -0.519663623391699, -0.465912722540509, 0.791060542804409, 0.0733000171961986, -0.188137902727651, -0.497838683792118, 0.0276260829840275, -0.693375002885843, -0.643201013833703, 0.22434123740446, 0.119941654930354, 0.183733065973594]', 43.7); -insert into credit_card_transaction (time, features, amount) values (157894, '[-0.980127534041516, 0.334524767225136, -0.775471440271509, -4.05868614407756, 0.493698867524761, 0.223560637089089, 0.446767008689881, 0.33266987755895, -0.195251080158663, -1.29107894209803, -0.403931849182272, 0.558896564335421, 0.265450401515436, 0.648017632065046, 1.43684116275618, -3.73657399858987, 1.14293309060415, -0.0555478780289331, -0.814627514192272, -0.791197286683024, -0.0205511900092023, 0.462587010669406, -0.182166015557391, -0.340585942815003, 0.246240469169606, -0.665176279740618, -0.30475790021127, 0.0504671763727687]', 30.0); -insert into credit_card_transaction (time, features, amount) values (157894, '[-2.03204091182433, -1.06488792677621, 1.02900746725203, -0.707950691745042, 0.921131059237985, -0.77234864813297, -0.867647702078217, 0.843596594834265, 0.0949623344275737, -1.04288654525445, 0.482036228689107, 0.304144667496677, -1.19286413491159, 0.795855206935447, 0.167435868142797, 0.674906847650112, -0.621758240840736, 0.668349841791895, 0.0176207113035838, 0.267229856490416, 0.15267108561428, -0.441585457934236, 0.0531446505084496, 0.655022346858257, -0.447528111827238, -0.338537270276676, 0.0057926074152947, -0.189421873949658]', 23.85); -insert into credit_card_transaction (time, features, amount) values (157895, '[0.874137749654705, -2.2585948557799, -1.53758419552037, 0.305294548961614, 0.0392540004228224, 1.98373419044859, -0.0733113384410723, 0.474959724258854, 0.869009997415201, -0.267781267194504, 0.714247155242689, 0.992399281590358, -0.34522000815664, 0.300383581950972, 0.103198348605188, -0.287871898232923, 0.101774560729862, -1.2766824339752, -0.414501438393064, 0.804052177639246, -0.0357181775517071, -1.1187224497687, 0.0451053007809415, -0.950751522504831, -0.970777039077199, 0.171680070039731, -0.105172879023415, 0.0233110152140974]', 534.34); -insert into credit_card_transaction (time, features, amount) values (157895, '[-2.95145750903908, -8.73573413946963, -3.19142730477141, 2.28958951295271, -3.19028213455878, 0.942239616805806, 2.6483306329096, -0.456285154587222, 1.37406078363675, -1.37141284801514, 0.0219770585922259, 1.00977234527812, -0.572858438392427, 0.479282493500472, -1.07714292909117, 0.243019395645019, -0.233661718597804, 0.518359489946215, 0.179549377710051, 4.89736148889273, 1.16752516858805, -2.13662574484399, -1.89536603613023, -0.316139015785521, -1.35926095720466, -0.578141115222792, -0.469111571143466, 0.387144266192967]', 2547.47); -insert into credit_card_transaction (time, features, amount) values (157895, '[1.99524471543149, 0.0478028354206297, -1.78389631026649, 0.566291262184279, 0.390627709715454, -0.439593497864376, -0.0972031322100111, -0.008939082182975, 0.580598520383246, -0.510669564244049, 1.14367161541455, 0.672875123793974, -0.448265216525295, -1.47141885745689, -1.01457568657872, 0.279720143058648, 1.04356686596404, 0.632343443819284, 0.0998898966523721, -0.169950398648955, 0.0167127569916756, 0.298321317672987, 0.0764676321552158, 0.629316448748177, 0.0552932153004491, 0.589562939923034, -0.0468315097484694, -0.0346069473780067]', 1.0); -insert into credit_card_transaction (time, features, amount) values (157895, '[-0.381916014389752, 0.904582120168873, -0.141546969460458, -0.537496749583165, 0.763007312860442, -0.529913958651372, 0.860120857325146, -0.206775159620225, -0.1701873695429, 0.277578285045535, 0.612721978771451, 1.05399594426673, 0.644246431463654, 0.155585390697352, -0.919692041278245, -0.154930793040093, -0.638860823210908, -0.242201227004461, 0.762955401945503, -0.0537200116866628, -0.0841940078416719, -0.0924552780241062, 0.141017451594184, -0.368833443433274, -1.44413410105477, 0.0414779176896924, 0.0890699726447463, 0.32658336770486]', 17.99); -insert into credit_card_transaction (time, features, amount) values (157896, '[2.15499611722595, -1.6336642382552, -0.911913532218405, -1.86491212066969, -0.688111109642097, 1.20093794276144, -1.61756801083601, 0.406422249064719, -1.2056725611387, 1.60232254488955, 0.684554724030332, -0.218634802186758, 0.0930094051838177, -0.236352664284653, 0.0739279098975766, -0.918477765891507, 0.808858365675431, -0.594233105021545, -0.757435930028644, -0.52720565298939, -0.0578634466504834, 0.430217390450558, 0.259312026867836, -1.66060489541335, -0.45468499426435, -0.0627993097062929, 0.0699963425232515, -0.0727435156063523]', 13.7); -insert into credit_card_transaction (time, features, amount) values (157896, '[2.30676850320387, -1.30544402277144, -0.884869842725682, -1.61545749090099, -1.07619691380622, -0.410873067101839, -1.14073858781718, -0.206782284230105, -1.26625373362318, 1.52023902956032, -1.0214862603368, -0.262823041307623, 1.64365810654171, -0.753288061143543, -0.194312068498528, -0.470152690442343, 0.307750699937535, -0.0900594583797713, -0.106598512380145, -0.327445943540135, -0.163568247276504, 0.0775823326844607, 0.249953596209345, 0.586745946113069, -0.168345671495382, -0.205431612936236, 0.0232755453153838, -0.0387018291035679]', 17.0); -insert into credit_card_transaction (time, features, amount) values (157896, '[0.599062585536189, -2.87642397545265, -0.938185941587078, 1.26624325159145, -1.61356731465813, 0.291096359269465, 0.0068046430870135, 0.0729261293467636, 1.82107574441951, -0.443454710302831, 0.0752076867270332, 0.785705698936285, -1.45568139258644, -0.0039056074544981, -1.60529947612682, -0.454145535411903, 0.021332959933459, 0.622705206907382, 0.186118015368104, 1.02421562322345, 0.689605472870998, 0.896439257457367, -0.619434577662689, 0.097221582502764, -0.106915488698315, -0.0585614518598048, -0.0843976412828654, 0.0581543793712038]', 680.3); -insert into credit_card_transaction (time, features, amount) values (157896, '[-0.537065414805345, -0.436391405163059, 1.97920180154175, -2.2681625856484, -0.488086801026272, -0.0243374073113559, -0.531373467429205, 0.277016609949402, -0.653601613245138, -0.146999956905202, 0.0992599209231778, 0.577345420634845, 1.02564549211825, -0.632632207113674, -0.819813950865385, 0.100690388830211, -1.5079140583366, 2.28593289352485, -2.21626737288044, -0.500515475485959, 0.0242742189580927, 0.458293092387654, -0.0014609892151069, -0.332477668001832, -0.831409923505809, 0.34799265915513, 0.152945955806711, 0.183869935178162]', 21.2); -insert into credit_card_transaction (time, features, amount) values (157897, '[0.084622911456161, 0.0497336642116673, 0.235022065158614, -2.5743832009014, 0.689250369714968, -0.873735137381891, 0.835462806417917, -0.355359498272619, 1.27080607314137, -1.39946764777834, -1.05853907204533, 0.0652953424594523, -0.287463591564612, -0.0590608656504366, 0.599634976273762, -0.721137529430174, -0.46728648182462, 0.050837576778913, 0.771678661662824, -0.0044508951762856, 0.0098029393964264, 0.290247620543975, -0.185970191790329, 0.500470672471646, -0.049905916607372, -0.328566826245707, -0.108169680621951, -0.169769219289147]', 10.47); -insert into credit_card_transaction (time, features, amount) values (157898, '[0.249983446354362, -3.28997149534248, -1.89985683045391, 0.567734891641382, -0.279382544403865, 2.0999999918999, 0.35540964245177, 0.39110305748287, 0.830955648141821, -0.41826027930015, 0.718875969618624, 1.00570653336605, -0.287619166968485, 0.373527606533198, 0.143841636808564, -0.245259410606551, 0.111352944520433, -1.21048745540841, -0.517690036618029, 1.46199176271398, 0.1600459586992, -1.28924021775814, -0.239657250149244, -0.934173487302693, -1.08940409209579, 0.137222849088636, -0.166217859717328, 0.0795649732777415]', 857.42); -insert into credit_card_transaction (time, features, amount) values (157899, '[-8.01482331115438, 7.06255482350825, -6.60054410151113, -1.56961768644062, -1.96823317337275, -1.30580242265623, -1.91254738585737, 3.55887351724071, 1.54153780042878, 3.51910523550793, -0.310078945845208, 1.70228017627906, 0.122048900593556, 0.812923974849694, -1.30596206148944, 0.60784968333206, 1.64837377776102, 0.513783260919317, -0.348658187630747, 1.14407788174426, 0.186234207062, 0.106917799127988, 0.390380028899803, 0.112047885592719, 0.84931575223108, 0.566270499626512, -0.263478408468529, 0.747256758963994]', 2.55); -insert into credit_card_transaction (time, features, amount) values (157900, '[0.0737887856018367, 0.795153712567147, -0.110337248221287, -0.941471202551368, 0.96976346592985, -0.216539312867019, 0.890923219723065, 0.0006700998799452, -0.245552313057108, -0.242328691330827, -0.126860567482349, 0.530713441320792, 0.180656812324312, 0.232442820942043, -1.03763957801116, 0.396802218280224, -1.03432418851456, 0.0699724508962404, 0.589487716098665, 0.0092937964706779, -0.275256012098953, -0.640359765422046, -0.0708344479374029, -1.04178597261983, -0.380898915949113, 0.183812881804712, 0.242684774697795, 0.0770338395992733]', 2.67); -insert into credit_card_transaction (time, features, amount) values (157900, '[0.835191573593701, -2.8578949808554, -2.26582845379784, 0.0084519614756108, -0.993843041083543, -0.537559899793885, 0.697331722824314, -0.39486071271835, -0.823185773502289, 0.768634832349307, 0.245595317638252, 0.256056311382268, -0.366730736338625, 0.70651263164887, -0.710182494158098, -1.33680321462496, -0.285545667357253, 1.33849758707585, -0.371243297505876, 0.762247863441567, -0.0889472786487298, -1.28680630021755, -0.358427737986008, -0.401662520776188, -0.474425709385888, 0.413932352749525, -0.208511736774225, 0.0404490489521142]', 699.0); -insert into credit_card_transaction (time, features, amount) values (157900, '[2.18673115914591, -0.65137035084956, -1.21936289949389, -0.426831404124117, -0.687905141355369, -1.23040952136611, -0.342454819867699, -0.330182618372767, -0.475585490190968, 0.994912922190698, -1.00765374006895, -0.562818152374183, -0.458949771571066, 0.410604467131052, 0.646075357328223, -1.45258338028467, -0.128593402446772, 1.1075089576064, -1.0809696921502, -0.673528987164475, -0.142707317397195, 0.157637210506489, 0.148560404935555, -0.0023299875079604, -0.110416645359042, 0.801492577077862, -0.0624217380014489, -0.0670655990384319]', 2.99); -insert into credit_card_transaction (time, features, amount) values (157900, '[-0.493903185082797, 1.48024304290096, -0.460867342096709, 0.817704257181372, 0.786579133296784, -0.346017270911799, 1.02607370130255, 0.286036551507853, -1.104905623776, -0.0721878333927018, 0.209835127771765, 0.753678252825354, -0.0087216633462644, 0.94060922218128, -1.05818950355916, -0.808935770108734, -0.0203992186404635, 0.251467925973858, 1.15799961647567, 0.0062237131161779, 0.195696889100679, 0.725050858504305, -0.262785766225978, -0.374240225624988, 0.0764665506635068, -0.370815342321736, 0.307725945972628, 0.168930825376137]', 22.4); -insert into credit_card_transaction (time, features, amount) values (157900, '[-0.381217565562118, 0.583593116883996, -0.6116031647165, 0.392167901063612, 0.308642390743848, -1.21689699370856, 0.312991288622845, 0.288781213860821, 0.102971114997442, -1.15577174030823, -1.23470889581905, -0.478156602148045, -1.099423691853, -0.755898134039938, -0.622237070212195, -0.0482957761582344, 1.13419890349787, 0.388192378611406, 0.144331105123874, -0.131533414007584, 0.19261780510878, 0.486821369368834, -0.0275773925489235, -0.0180121172454527, -0.517637181374241, 0.400478218276118, -0.0177141513596138, 0.0010334262797165]', 29.89); -insert into credit_card_transaction (time, features, amount) values (157900, '[-1.91755261697316, 1.33017455482758, 0.042396986871526, -0.419234186100371, -0.758114714975666, -0.179526096022429, -0.709981115280485, 1.32279944762951, -0.07330803555802, -0.93892900978983, -1.3089400501036, 0.775350568166637, 0.822592297930393, 0.473230609007015, 0.146304286898183, 0.723312319860541, -0.146715433712617, -0.231926829091941, -0.327051394185408, -0.294985291265425, -0.049569753753946, -0.542340134411873, 0.236195705569596, 0.661619992444514, -0.173667857384025, -0.0404071249013867, -0.522678304330722, -0.125644985718057]', 10.0); -insert into credit_card_transaction (time, features, amount) values (157901, '[-0.658512660952247, 1.04334176746059, -0.0166830967394197, -0.842387962417706, 0.725381920476646, -0.639462994618298, 0.867608236650307, 0.0736767438617071, 0.0062162473671651, -0.384489774063838, -1.75702069831662, -0.43537894268419, -0.505452948334033, 0.316215343476969, -0.231962922119078, 0.0669014760318342, -0.511063706778201, -0.466199103133965, 0.245638923575681, -0.107831147607011, -0.281945719889646, -0.625309249130629, 0.0465811149370318, -0.796971367459351, -0.530516218757201, 0.152770937066802, -0.0584772396019007, -0.0716419957741303]', 1.98); -insert into credit_card_transaction (time, features, amount) values (157901, '[2.0349323910287, -0.712674693723369, -0.979103212262256, -0.382544655899642, -0.858342592361684, -1.26688783855819, -0.274602223694948, -0.330947970473472, -0.97784037632193, 1.11346689831595, 1.37137792364028, 0.998370993506269, 0.69396395814166, 0.380894093213844, -0.28323105315914, -1.41876712058075, -0.305269237011596, 1.35367295353032, -0.954066258753647, -0.506235517053859, -0.0197954186517223, 0.437240923389281, 0.147126281291046, 0.598042673594229, -0.170892489379524, 0.735138005900876, -0.0677417091212256, -0.0609969483276765]', 43.9); -insert into credit_card_transaction (time, features, amount) values (157901, '[-1.71619744078076, 2.19590069722008, -1.43133986110361, 0.826001188809166, 0.0809214640325466, -0.523320201620366, 0.251037184169182, 1.16411936715498, -1.01633330137333, 0.110172104493274, -0.0704250578982231, 0.99270405150433, -0.0812494106723119, 1.47442938168691, -1.07058596912208, -0.583757163705439, 0.309390186332102, 0.313037614177102, 1.09320727774172, -0.0195425979073825, 0.199890206005182, 0.629591421397521, -0.214343368362719, -0.380159132547274, 0.203338776521147, -0.356621893747678, 0.305434316400816, 0.118843965167719]', 21.82); -insert into credit_card_transaction (time, features, amount) values (157902, '[-0.785322122467692, 0.961483115517616, -1.92215447137287, -2.25379214859201, 3.63205140074275, 2.77544765865689, 1.06789336768814, 0.605739377249651, -0.454833872481111, -0.168550879214234, -0.0555833800674775, -0.16659713584695, -0.481114008226962, 0.804271379305383, 0.400025182901024, -0.77099718131868, -0.442948647755501, -0.531110459917082, -0.198971034096133, 0.140356002520428, 0.157532147291346, 0.547436017700413, -0.378912271650549, 0.755593623838874, 0.473436244817106, 0.228935463942859, 0.293699304408411, 0.27292853275079]', 13.79); -insert into credit_card_transaction (time, features, amount) values (157902, '[-0.649231350061541, 1.28898913178982, -1.13708877120126, -1.66612376719745, 1.60602367924697, -0.828609476026074, 2.1546782608748, -0.833475221302612, 0.893431366057737, 1.60440472319773, -0.071330939352789, -1.00320106825351, -2.13524526236887, 0.317781119890721, -0.441229674090099, -0.40261306843108, -1.26667299149117, 0.525064075793738, 0.445182647564266, 0.525663353383508, -0.0848057714050158, 0.69446935503082, -0.422523743328681, -1.04620806039527, -0.131994015251356, 0.0736582098049211, 0.329804864260251, -0.172873221772078]', 30.05); -insert into credit_card_transaction (time, features, amount) values (157902, '[-1.33559506120005, 0.539863142113922, 0.601209660607156, -0.628999427060293, -0.557872953649539, -0.618379492703623, 1.55312653603153, 0.220532686903824, -0.579231812548526, -1.36822916768202, -1.33243072236119, -0.627569760882967, -1.35277731986473, 0.710444704928356, -0.352577602715176, -0.0259568811120971, 0.0297635357991977, -0.484393066721101, 0.174041202371641, 0.222026247676947, -0.118827557633524, -0.903552034161335, 0.258719943017483, 0.002270339249504, 0.60619085086954, 0.474990545994021, -0.266212284906561, -0.0111088856415367]', 229.41); -insert into credit_card_transaction (time, features, amount) values (157902, '[-0.117021325480892, 0.438512439697097, 0.33625718764148, -0.0538105876965149, 0.632122863666363, -0.757626441193409, 1.49883055414855, -0.604552453069684, -1.74778602208135, 0.551229707525331, -1.21502999923672, -0.260624234451585, 0.391720041016906, 0.0402246504199353, -0.82560137560621, -2.56786644657313, 0.102153392566023, 0.851032810724188, -0.591164816821375, -0.32466052812295, -0.228643010483369, 0.0674866065063492, -0.184743430515323, -0.0376716780976729, 0.126002627109249, -0.488867924247838, -0.0015505696851462, -0.0570840012001833]', 62.95); -insert into credit_card_transaction (time, features, amount) values (157903, '[-1.01895693569342, 0.573433181253949, 0.806061738436092, -0.90562051608275, 0.216796894077958, 0.643680805053632, -0.171086510897476, 0.80431242309621, -0.0131796943757115, -0.733141121111047, -0.178652511630448, 0.550619795933532, 0.128705660648942, 0.118426796199606, -0.65170628130358, 0.692855609208508, -0.802817538525621, 0.864414933655868, 0.111227628789411, 0.104702070849897, 0.295614104529386, 0.80424733658981, -0.239934782738573, 0.0931908163348545, 0.015715681517997, 0.623179386616816, 0.204840217414147, 0.094269198265361]', 34.3); -insert into credit_card_transaction (time, features, amount) values (157903, '[2.11250674460611, 0.124453365564863, -2.48815948601055, 0.320593939176355, 0.889049805139394, -1.13833176480141, 0.796966062689835, -0.381165478141393, -0.260768068822949, 0.469902937431072, 0.723915101344203, 0.182422991632372, -1.41394249440071, 1.25447120089002, -0.806249882919377, -0.543653217828135, -0.300349862713011, -0.0315710926320397, 0.298066252560275, -0.297376700865476, 0.300420853055377, 0.941334195588568, -0.138177245356098, 0.834716891972212, 0.629566386284558, 0.671163374349601, -0.139195997549872, -0.0986853729154215]', 0.0); -insert into credit_card_transaction (time, features, amount) values (157903, '[0.245458649760057, 1.06542020571345, -1.46650845140007, -0.252925553679658, 0.75862951975372, -0.753788439277694, 0.570667673876605, 0.20926954423767, -0.0495699752016178, -1.1314106169566, -1.59114110895577, -0.54887613648666, -0.07395135895906, -0.520545442976051, 0.802776870326066, 0.469823178668022, 0.278727117758228, 1.00434179940077, 0.0707913842822678, -0.208481502534964, 0.268542741878845, 0.753951158705625, -0.183038658877185, -1.14199807087522, -0.14920671987347, -0.0812299246007023, -0.0727396636679443, -0.0576412845557827]', 21.75); -insert into credit_card_transaction (time, features, amount) values (157903, '[-1.56117308741516, 1.64990103489131, -1.16256107048878, -0.803774238594438, 0.198197452056755, -0.409049821905381, -0.142759543233837, -0.24593674464463, -0.24879054174963, -0.19866395879008, 0.699638635500335, 1.30250140877807, 0.529999734258827, 0.877815546237471, -0.400597621683801, 0.151565009014956, -0.512720316843324, 0.469426157330296, -0.227981875872939, -0.734471750018317, 1.28374654881286, 0.845108478223971, 0.107712061009612, 0.73104144487092, 0.0595796084275727, -0.292639139864585, -0.973545660286161, -0.138012663241048]', 3.49); -insert into credit_card_transaction (time, features, amount) values (157904, '[-2.25703474764583, -2.452066486735, -0.952998791848426, 0.689593231867508, 4.56921498529072, 2.64676856927825, -0.895836003994044, 0.916504135325729, -0.517579058722481, 0.388894323189424, 0.100947883275083, -0.0553359506868724, 0.0875441404530024, 0.528294027463233, 1.88135595237381, -1.22045162364543, 0.550988509182496, -0.394821120746533, 2.96309373574462, 0.0825354145041859, -0.291520376442678, -0.321498871608976, 2.03403196225141, 0.700295746018922, -0.321293600937591, 0.68342797032638, 0.266453219757414, 0.101977896714882]', 4.47); -insert into credit_card_transaction (time, features, amount) values (157904, '[-0.980911170983152, 1.37121012118107, -0.559525112034686, -0.550990041471289, -0.0463310330961797, -1.16101399545253, 0.383224959502767, 0.613515412348663, -0.275316848960592, -0.778752087659786, -1.5606500434433, 0.201408672782287, -0.27826762746971, 0.794198461237844, -0.748719796481105, -0.0154493167152493, -0.0132394393918369, -0.573763458656846, 0.0869651585900584, -0.280637287993738, -0.0693480758469666, -0.305566138030473, 0.144366834523028, -0.107082592256806, -0.785703155543112, 0.153121773333652, 0.0139600775815906, 0.119783998280052]', 6.48); -insert into credit_card_transaction (time, features, amount) values (157905, '[-0.254038986112246, -0.150368410161423, -0.38415410346338, -0.930157156013426, -1.30222271559157, -0.645647368229031, 1.34086792948199, -0.494491463326635, -1.24404062286028, 0.8237262368369, 1.12030559072902, -0.962106496776634, -1.88481725306932, 0.360155215525693, -0.912561789425541, 0.115748489947065, 0.869571731892819, -1.13739692101667, 1.90406045091532, -0.283630185679304, 0.211310138416589, 0.764197801992011, -0.127018977972646, 0.580006029754394, 0.261898153975394, 0.0679359153045664, -0.333060788963968, -0.103230700439352]', 238.0); -insert into credit_card_transaction (time, features, amount) values (157905, '[1.97999336554606, -0.142044948535131, -0.703849781227801, 1.34345121368236, 0.158572533861169, 0.331878234956497, -0.199008369791474, 0.0060108194440058, 1.12184362928976, -0.0449915160102269, -2.05189365373729, 0.850441438260074, 0.801719391694577, -0.487676042620765, -1.1828602401452, -0.217452384437668, -0.419751095203964, -0.732192108229198, 0.526117340526063, -0.193106806878799, -0.571728070667003, -1.31492180434125, 0.296523228838472, -1.12473755996578, -0.169760500752762, -1.04098692168015, 0.0600951513983694, -0.0377968242276434]', 17.87); -insert into credit_card_transaction (time, features, amount) values (157905, '[-0.0516352638968041, 0.98050290589871, -2.27477364037747, -0.532868040044273, 0.949273408797316, -1.11834903705716, 1.56498170085685, 0.0839099515100263, -0.80827030136609, -0.0852607048162529, 0.214571835651573, 0.0448990005906746, -1.46489610801555, 1.51398107985028, -1.52859233736862, -0.449187891811337, -0.345523466333364, 0.222036585485296, -0.0027806810655068, -0.11884690761192, 0.591864478964928, 1.62604788111902, 0.204959343085669, 0.817311778977049, -1.16485461322391, 0.335357324996459, 0.334548097294036, 0.321496487977308]', 88.21); -insert into credit_card_transaction (time, features, amount) values (157905, '[-0.585138574634772, 0.554392797846965, -0.533852624863726, 0.152928746715669, 2.0517956790642, 4.57433519490335, -0.177844862220581, 1.56702391162216, -0.153262839347354, -0.742601713873033, -0.82778006764288, 0.417979090672179, -0.359725465815216, 0.258317361925088, -0.632132314125478, -0.824807511083985, 0.355036177820579, -0.626668377868553, 1.07352996770334, 0.0597885070590277, -0.325538016716953, -1.04380728227758, 0.423007943933086, 0.619722085246048, -0.723452126711582, -0.987658492063803, 0.131846603450899, 0.150504208891102]', 89.4); -insert into credit_card_transaction (time, features, amount) values (157906, '[0.0465931987457306, 0.885543809065956, 0.271404015853055, -0.614044460656422, 0.500334810654438, -0.995489108580857, 1.00960491918688, -0.192576617686994, -0.0825707902631356, -0.425735144346444, -0.891728643704836, 0.282676887161694, 0.296401226597962, 0.0579836432229254, -0.38158442492019, -0.144746526559487, -0.393608818194222, -0.849543088109048, -0.130594302914531, -0.0307394276951737, -0.257699801885398, -0.537556222389651, 0.0583491706256812, -0.0827868174337551, -0.486584202937646, 0.146924064540632, 0.252989399340288, 0.096507833349973]', 0.89); -insert into credit_card_transaction (time, features, amount) values (157906, '[2.01390375331608, 0.0827776541425267, -2.10430779002471, 0.39780737967145, 0.559252419012978, -0.999448147451114, 0.328952259605552, -0.350386143413494, 0.589149768178417, -0.550796847357519, -0.89925134354605, -0.033535800935122, 0.413198410553575, -0.918931053995973, 0.985744562636534, 0.391661587847706, 0.241392602529502, 0.678598975780997, -0.249491068783377, -0.0745737561188518, 0.176491501605903, 0.609064519323158, -0.152753553596954, -0.837791091597327, 0.33284171041776, -0.0568820407150537, -0.0056292950502836, -0.0286165182927772]', 48.75); -insert into credit_card_transaction (time, features, amount) values (157906, '[1.13797666981808, -1.69344624399708, -1.09067182058295, 1.35595681014061, -0.55215539585825, 0.60490969933164, -0.0994259144511083, 0.190002739783328, 0.969441322934145, 0.175525796279448, -0.460429269359792, -0.22031243001196, -1.62805353933732, 0.584155864826226, 0.120235445140114, 0.970238933912401, -1.13066305708445, 0.941754580081028, 0.0448603287374758, 0.540912032776538, 0.0561830879830368, -0.801958021126795, -0.129766475313135, -1.26068484036493, -0.497367357776644, -0.926388566398691, -0.0377187998129315, 0.0252369570474452]', 438.0); -insert into credit_card_transaction (time, features, amount) values (157907, '[0.0247705606032034, 1.09097958001963, -0.528967932008397, -0.605520807628633, 1.02890270141006, -0.707369624669997, 0.989771564310705, -0.152346114348203, -0.0086363886519808, -0.733513882828902, -0.823778851166164, -0.0354061215341754, 0.337785215169639, -1.12423299976171, -0.24647426636738, 0.369449482882631, 0.264252339235132, -0.188641832553441, -0.145504473084704, 0.0256222258437457, -0.336562644950979, -0.848612460962225, 0.0955877022829466, 0.376690305251935, -0.388507719895016, 0.112511254005237, 0.0916862327139131, 0.077890608279672]', 5.49); -insert into credit_card_transaction (time, features, amount) values (157908, '[-2.42765447777068, 2.30887834156243, -1.29472055152809, -0.49192177545785, 0.34129639487533, -0.42506860103783, 0.0609060051655076, 1.09704104437298, -1.05140079101845, -1.63900965993603, -1.32666148942587, 0.663720001185731, 1.88986714769002, -0.258809432019079, 0.780034642769215, 0.520700225331663, 0.734846299552925, 0.893135989976126, 0.366015593703541, -0.311140729671428, 0.328644142901115, 0.531112322474409, -0.661398635144403, 0.0312696916656398, 1.15118947030638, 0.035724955713893, -1.11011498388287, -0.473302418468432]', 25.0); -insert into credit_card_transaction (time, features, amount) values (157908, '[0.0926357201473769, 1.24502659579819, -1.32996795922569, 0.0162000757520552, 0.881710593584294, -0.243087876272779, -0.426771099182833, -2.76779067460179, -0.166681792207206, -2.24643613704431, -0.578056093020906, -0.148209488146708, -0.581185345290101, -2.19342043502059, -0.662685601756719, 0.586875430226292, 2.04227132520997, 0.663702830144735, -0.780632966414863, 0.586372262257694, -1.27882194169557, 0.536998872601288, -0.264142204116178, 0.456993611287235, 0.893745113546679, 0.731997964039419, 0.0860093874833085, 0.324464391838056]', 12.31); -insert into credit_card_transaction (time, features, amount) values (157908, '[1.99324833203318, -0.83319912010525, -2.68769780748939, -0.369608362338447, 0.186569506050947, -1.48068293354212, 0.748629385505938, -0.679114881138996, -1.10250175989748, 0.975818606981075, -1.36787389593569, -0.483666560715026, -0.19495642539827, 0.764836450181156, -0.261422163946881, -2.19995350949636, 0.125469277514611, 0.70480493868789, -0.631609819621673, -0.358546973689111, -0.0398415642938354, 0.198095705248245, -0.258970463341371, -0.345437776889043, 0.478851486383427, 1.0332507476481, -0.148630033752764, -0.0718456343220761]', 154.98); -insert into credit_card_transaction (time, features, amount) values (157908, '[-0.25080783252098, 1.07246180826257, -0.873690545826931, -0.151194103426197, 0.273120150895431, -1.05780137900395, 0.558517846599771, 0.256869034454488, -0.34560288207957, -0.897386537832912, 1.03803808108836, 0.253692091136194, -0.7765158281827, -0.188952254036643, 0.0753340571846981, 0.313864801674154, 0.46639741027085, 1.19456704633898, 0.0093102442504786, -0.307477743381429, 0.407393820533666, 1.10717297727466, -0.0195888452209443, -0.0676259598157408, -0.408615198007465, -0.17906945006243, -0.118683052997159, 0.0079366148534008]', 29.18); -insert into credit_card_transaction (time, features, amount) values (157909, '[-0.0947391909592723, 0.375172534723615, 1.24680005456955, -0.195607113359925, -0.153787639501474, -0.457432176308868, 0.179266175082623, 0.0340459979987299, 0.701563939674269, -0.720415346832911, -1.32736972550344, -0.111173961760158, -0.304984083930414, -0.163316268766993, 0.29430112621322, -0.1181198013219, -0.433636407590132, 0.494517554519687, 0.140064425456671, -0.109058163407614, 0.331100771425887, 1.14184928527388, -0.0870346754838411, -0.0032903403959216, -0.830756209934015, -0.269584981811816, 0.270869434257416, 0.238445158218471]', 9.99); -insert into credit_card_transaction (time, features, amount) values (157909, '[2.13594297883431, -0.0004015969276476, -1.47783967272336, 0.161963278578655, 0.458716579522584, -0.593329307546439, 0.23590393813479, -0.329735834085398, 0.44950568363661, 0.0430689063464959, -1.4869529095715, 0.668967887294012, 1.32425266224343, -0.0014529232428345, -0.002248691477363, 0.147395166233377, -0.727551244947571, -0.662741491954791, 0.530555422274653, -0.12127426577678, -0.345102006055858, -0.802317115185071, 0.198618879646868, -0.974142969559776, -0.123251323607733, 0.256761877496172, -0.0650824806319844, -0.0681305970956919]', 1.98); -insert into credit_card_transaction (time, features, amount) values (157909, '[1.99469208315303, 0.336003820218472, -1.54330430092413, 1.27080614832155, 0.313781509756749, -1.51172076884198, 0.70880536285969, -0.407782017595857, -0.398269940354268, 0.555721220112759, 1.38814040080625, 0.859398105678084, -0.640914719390795, 1.10846021273329, -0.524689089065201, -0.366261164020548, -0.464614005399254, -0.0712609541476403, -0.30372901102756, -0.323066353087508, 0.203998944889394, 0.658310826604845, 0.05093690518782, 0.539239907299545, 0.358090224890124, -0.537288169666052, -0.0366296700876039, -0.0691747883797682]', 1.0); -insert into credit_card_transaction (time, features, amount) values (157910, '[1.77247027069035, -0.439409688459886, -1.84459850519985, 0.352019683927497, 0.100150031056696, -0.500590546853668, -0.010740575037245, 0.0356881143434952, 0.969075825741137, -0.687168719048782, 1.01167659471481, 0.183333884318202, -1.71845411001624, -0.930393156517682, -0.562986990470135, 0.219635324291018, 0.962479424289317, 0.652316535065338, 0.319939712853861, -0.0344384266060296, -0.146370970177332, -0.53718995005491, 0.145167736881368, 0.551771519521099, -0.184263721389302, -0.144994843560502, -0.0420902455317882, -0.0095115280963436]', 115.36); -insert into credit_card_transaction (time, features, amount) values (157910, '[-0.657531455719813, 1.39973864909325, -0.425651200869176, -0.287011476856892, 0.529657440979324, -1.09706836262121, 0.690547761082501, 0.254158968786737, -0.134009358843976, -1.48376798697574, -1.01556634581355, 0.0528000428356162, 0.205399860468476, -1.58427434761282, -0.940263713599507, 0.207665179725233, 1.24256466406546, 0.372761739266948, -0.20294651667422, -0.0390709726029234, 0.0645482040580051, 0.428463629146299, -0.265972285428651, -0.169294895830524, -0.129878069924786, 0.588831963203196, 0.258116027570852, 0.192858204681211]', 4.74); -insert into credit_card_transaction (time, features, amount) values (157912, '[0.211131284422336, 0.286675685766003, 0.0627090090937991, -1.88897655137908, 0.0633350820659715, -1.15404286427866, 0.604951089749858, -0.425154905151124, -1.32637468946505, 0.266804134201603, -0.653672270888109, -0.395185926813384, 1.18042111203098, -0.339730459160403, -0.17790111927084, 0.804348776440014, -0.0054832817301665, -1.37502408904382, 0.178865706371488, 0.0334164029520285, 0.532915014563483, 1.52217277363081, -0.28529728601461, 0.0014425489403848, -0.0286294926824956, -0.014257600977176, -0.0836128641165237, 0.0243603678246047]', 15.0); -insert into credit_card_transaction (time, features, amount) values (157912, '[0.0197874103213681, 1.30621737962263, -0.81197185216138, -0.0874597716901431, 0.458784793550026, -1.33311873394097, 0.67514398179196, 0.0827573678323825, -0.376122901940923, -1.20335807844291, -0.488359432384472, 0.354392365742054, 0.830973026365255, -0.653366328511352, 0.597686388817002, 0.0684912877349065, 0.613308869388673, 0.540309298210897, -0.266879867386791, -0.198053942608, 0.357228200169644, 1.09518069874693, -0.152568891454972, -0.0550575896138856, -0.330932413592807, -0.152717490463572, 0.0083527485961637, 0.0395816766884335]', 1.5); -insert into credit_card_transaction (time, features, amount) values (157913, '[1.91066126162459, -0.211159238566408, -0.528439855371583, 0.979040826479544, -0.0110939115680447, 0.465083511553869, -0.535637622860092, 0.203841985114149, 0.582293313531761, 0.344121502099147, 0.393468961284806, 0.880351764181356, 0.275166470433681, 0.182067909767296, 0.140468161598655, 0.898058819091774, -1.25602942439591, 0.675330411934491, -0.0855681488377849, -0.133652281124354, -0.0903288336337876, -0.292904086229648, 0.297358275889729, 0.122598194707521, -0.336875554665426, -0.948067664208927, 0.0432490617214345, -0.0272122038307616]', 31.0); -insert into credit_card_transaction (time, features, amount) values (157913, '[1.79225832698467, -0.0849760704575839, -1.95970148529606, 1.36185163991992, 0.431960880304172, -1.37463637986497, 0.983158255406124, -0.53694791270602, -0.136749052862529, 0.309013907912058, -0.741518976703376, 0.0752642666322282, -0.314208931214831, 0.89186980199522, 0.269010953586832, -0.443688593783608, -0.365164040989799, -0.457252889660962, -0.499139910365335, -0.0231352174738422, 0.209158832699302, 0.40013374941257, -0.127093396910719, -0.0120829018603704, 0.399993131958951, -0.49539645335707, -0.0566006465537482, -0.0401141192067391]', 144.0); -insert into credit_card_transaction (time, features, amount) values (157913, '[0.94216456027253, -1.79992339818104, -2.40273944625159, 0.740017248261139, -0.33933215380822, -0.538472437740158, 0.643437851418059, -0.160503872502217, 0.92095826997913, -0.948998183305172, 0.735444899300075, 0.189370656023334, -1.38094644573399, -1.0294960514447, -0.529687943281462, 0.41040836774365, 0.945202783414781, 0.881937170295514, 0.304679432272243, 0.861499829155589, 0.0923205937190821, -0.794328805016838, -0.335409139091891, -0.601652322434349, -0.300420312178591, -0.131953148870835, -0.117550815061413, 0.0592940295343889]', 554.8); -insert into credit_card_transaction (time, features, amount) values (157914, '[1.33955399554668, -1.57009902692414, -0.736502129989807, 0.58064038521, -1.17958785297662, -0.56575375497292, -0.204163927508693, -0.114901819941184, 1.18538447595711, -0.0790128327249399, -0.972340417482769, -0.265959559841319, -0.725192191260264, 0.19225159442834, 1.07735270662446, 0.58411020319591, -0.543699684376732, 0.18057838709201, -0.527540956582624, 0.452119040508396, 0.376768473567353, 0.406788253940842, -0.101811522002397, -0.0395474071137182, -0.516522562974818, 0.625650187892035, -0.104042929177585, 0.0083551164989929]', 352.32); -insert into credit_card_transaction (time, features, amount) values (157914, '[-0.297960415038452, 1.72596629591149, -0.586200685572207, 3.19595486162759, 1.04206429765504, -0.688006075746141, 1.12231290877593, 0.14132723035833, -1.97230618978181, 0.961474058720724, -1.83953404836843, -1.13671157175163, -1.38444086389626, 1.02815320061198, -1.37918673772152, 0.241865804025122, -0.266971574155994, -0.33600812567418, -1.05456642448721, -0.422516830755513, 0.272682124344865, 0.581673074901658, -0.142270982893622, -0.0530857165943762, -0.245326696296474, 0.111658227514345, -0.20147868455937, 0.0236174975751608]', 7.02); -insert into credit_card_transaction (time, features, amount) values (157915, '[-1.25411039501246, -0.0649692910440273, -0.701484926917165, -2.0137089669258, 3.91257117737602, 3.0084350491042, 0.317902205279048, 0.631811715004565, 0.0796131568708577, -0.187705811660144, -0.230528196910831, -0.0486252227695603, -0.501636393505328, 0.0379061536368099, -0.402998046344604, -0.242755884930695, -0.663790464917861, -0.971644772332455, -0.534067215422928, -0.13102200029208, -0.310168123391017, -0.634984691782627, -0.273068506205573, 0.729149660763609, -0.0950745162621681, 0.162974959299789, -0.0490179350343842, 0.0073919212221497]', 13.35); -insert into credit_card_transaction (time, features, amount) values (157915, '[2.07314050495194, 0.160444533888299, -1.92773981403612, 0.463771784916458, 0.452010631212701, -1.07165320911927, 0.289097027096728, -0.265643615317733, 0.480442465197333, -0.373277032869777, -0.575307344560624, -0.23564207659069, -0.662466926432632, -0.719435986571659, 0.203848788235053, 0.219958539336777, 0.666670663070596, -0.192796974171176, -0.0076825016110446, -0.195157443255181, -0.270750900837503, -0.698427563731026, 0.263745395503125, 0.633327298257517, -0.129101270497182, 0.294110340159281, -0.0772520743222654, -0.0341864203657847]', 5.94); -insert into credit_card_transaction (time, features, amount) values (157916, '[1.9897483178861, -0.223330504600318, -1.04491162019632, 0.331111915571817, -0.217006469308533, -0.880371750891401, -0.0084863098212759, -0.0964788283521086, 0.467794787379438, 0.267843642464905, 0.864910545243809, 0.493568434878492, -1.31998250030777, 0.739487281883822, -0.477672246443677, 0.0691316291020421, -0.378319527459128, -0.331938283830813, 0.338337342243589, -0.28981400913784, -0.23652124140144, -0.656256550604291, 0.353963873281439, -0.016798716630704, -0.400826560364293, 0.181900621423771, -0.0810070673723202, -0.0713197766781938]', 8.99); -insert into credit_card_transaction (time, features, amount) values (157916, '[1.56062905603918, -0.716724649050804, -1.82197651429573, 0.592674213883093, -0.286196644253949, -1.02625855477543, 0.222704049916073, -0.128578787229013, 0.935391202205827, -0.784466686853299, 1.11212858932586, 0.292280905200238, -1.57122910201697, -1.07148191194073, -0.649688231404452, 0.283744027659032, 1.06063654114159, 0.747910067762574, 0.297911804491932, 0.169550971911224, -0.0838371998722528, -0.555272928223719, 0.0132387961735952, -0.0765468985238263, -0.230231921303395, -0.131869871524722, -0.0573828158375352, 0.0066724989919832]', 221.3); -insert into credit_card_transaction (time, features, amount) values (157916, '[2.00047993671758, -0.121883269346704, -1.11192409826635, 0.238849362300104, 0.117110730008115, 0.0610137947047535, -0.534223473268728, 0.158218895397177, 0.723379207723015, -0.304064935245308, 0.781764833705914, 0.668316513646892, 0.101123908745036, -1.09368976154752, 0.0043578023505527, 1.1619110379149, -0.0647448170286013, 0.741493049892432, 0.25422219765741, -0.107685118270648, -0.31370866711294, -0.852833437382745, 0.377936192955871, 0.0733888448462453, -0.560395101943095, 0.098532375664288, -0.0251509086342257, -0.0245952961988179]', 5.5); -insert into credit_card_transaction (time, features, amount) values (157916, '[2.06744234907365, 0.180664572524214, -1.81408279048141, 1.26178223504015, 0.638117857640997, -0.769467932568533, 0.527781532476666, -0.287250068673222, 0.340185903898516, 0.289371450333883, -1.30622006713612, -0.271229657284828, -1.14680752510323, 0.689518362231553, -0.411823821434058, -0.575497632807276, -0.198110208043693, -0.383588248893993, -0.0863699877141649, -0.332918812285031, 0.015966793058212, 0.198756619411277, 0.0290265846303137, 0.640953044200207, 0.478791639732923, -0.517606673028781, -0.0278839615022978, -0.0586915594560799]', 2.48); -insert into credit_card_transaction (time, features, amount) values (157917, '[-1.3811801670671, -0.0110199570090237, 1.6187258326839, -0.789666963552105, 1.75572953441951, 1.11029397124941, 0.521056442426509, 0.288928458325404, -0.438860244480903, -0.555567676392488, 1.33844299885313, 0.895514146501095, 0.0942530042715619, 0.0346926867792677, 0.211127686494395, -0.622843429443675, -0.0105354554558779, -1.57012871041032, -0.537415763506381, 0.0162029388516364, -0.379769697713028, -1.00975114510212, 0.0447805992393896, -1.46136013684093, -0.0212727913427164, -0.054734924590897, -0.29572457100272, -0.0787510504711053]', 32.5); -insert into credit_card_transaction (time, features, amount) values (157918, '[1.90834206460806, -1.74202025372547, -1.23501217970743, -2.20059012917225, -1.09521156185242, -0.497750772699442, -0.698003889949556, -0.144100917330474, 1.1888409238861, -0.215154540491498, -1.76412617679087, -0.0336870769428297, 0.0670911911982854, -0.0285279905038496, 1.23092512837873, -1.59816069013561, -0.323164742123862, 1.44743613353295, 0.299701357477478, -0.261284069498962, -0.555165891807501, -1.31279071130434, 0.187838635043034, -1.127406407645, -0.481465675897489, -0.506746764482113, 0.0188074774386466, -0.0162776564189558]', 176.11); -insert into credit_card_transaction (time, features, amount) values (157918, '[2.08429755832433, 0.103235162466998, -1.61361555890563, 0.485971291788574, 0.264824231190334, -1.41185653162505, 0.598777333538508, -0.504130253158685, 0.496313007988332, -0.0788293085333469, -0.784750615478738, 0.783872174148628, 0.424062917113364, 0.35956500549993, -0.384584739945419, -0.843042744261262, -0.117171998634464, -0.71984337622377, 0.226709340552929, -0.23967285370069, 0.0020007712082624, 0.29396484734589, 0.0296908966250725, 0.0905369947593913, 0.38323670117106, -0.224995693625922, -0.0312610900509016, -0.0657430711526309]', 1.0); -insert into credit_card_transaction (time, features, amount) values (157919, '[-0.26959871730005, 1.00204601186873, 0.353983119582744, -0.524324689068921, 0.195451095588173, -1.37294833640204, 1.07074845419106, -0.259101703364395, 0.0423116891689439, -0.123173436746835, -0.438670575873667, 0.554314605572607, 0.447463335944319, -0.0338829236437851, -0.469034604365884, -0.254854134946186, -0.295459358150351, -0.961104566821315, -0.27532016019649, 0.11314411278435, -0.257930502730128, -0.474437897055473, 0.161304648570382, 0.462782454030735, -0.467900470489219, 0.116095262676174, 0.360871923068357, 0.167584895059721]', 16.84); -insert into credit_card_transaction (time, features, amount) values (157919, '[2.09197169136571, 0.0687219450774321, -3.8188375150935, -0.652842000157968, 3.14274578217089, 2.63383431381525, 0.139801174064295, 0.53522830440046, -0.196245790875915, -0.239075344722302, 0.473904382455054, -0.0718214917982835, -0.325591434711489, -0.248767140037801, 0.815179843740933, -0.0375959810913961, 0.528569745432951, -0.280729046744247, -0.338025403835938, -0.111579824373858, 0.0123578893869716, 0.0263296010496553, 0.0550755644958474, 0.66103493935876, 0.271985819138783, 0.636178815286168, -0.083020013827179, -0.0604894970669956]', 7.5); -insert into credit_card_transaction (time, features, amount) values (157920, '[1.62483403246282, -2.24269797395034, -2.82386739165679, -1.17351838422963, -0.838943532483226, -1.78340421968327, 0.58913379142438, -0.810782632237686, -2.06130494180128, 1.58184664513257, -1.03518416701662, -1.28397525989883, -0.374307944265847, 0.503858184859234, -0.576524952121808, -1.37015604972209, 0.863043538542929, -0.267593956372813, -0.140826509408091, 0.302040095349845, 0.354444651285977, 0.637110120692926, -0.49520294973277, 0.110386315691532, 0.483409140673369, 0.258089300649623, -0.149933100959815, -0.0189630836004605]', 417.0); -insert into credit_card_transaction (time, features, amount) values (157920, '[-1.32974466339768, -1.4508295166275, 2.18600925993499, -1.82624161451296, 0.451298905998421, 1.08660586997725, -1.18945840609869, 0.614540247518562, -0.261847050172193, -0.372205095453535, -0.74079450700664, 0.0993901203691936, 0.722639145458476, -1.36804070025507, -2.17309050851372, 1.25990733608638, -0.115023249708138, -0.335576326065161, 1.55720387641174, 0.589540958165436, 0.223916754100598, 0.427397538252894, -0.199027786387784, -0.302409062568658, 0.697672589540845, -0.046943915672199, 0.0278676827698158, 0.0745410246254846]', 70.34); -insert into credit_card_transaction (time, features, amount) values (157920, '[2.08133949677623, -0.761712376602396, -0.858707345032282, -0.0374845362501132, -0.84299267684175, -0.892698700724695, -0.463015834331284, -0.319803279023863, -0.348509267104929, 0.896894591549453, -1.0544663244373, 0.329699652559688, 1.04416168263655, -0.200348195745272, -0.0203110902126511, -1.5713502021745, -0.183547090235792, 1.21848062108527, -1.08939192477731, -0.513842752573541, -0.0063431778331387, 0.649222366426924, 0.0187114313492941, 0.109604300807661, 0.047182392895006, 0.613509827906607, -0.0174451581753065, -0.0491872826849357]', 39.96); -insert into credit_card_transaction (time, features, amount) values (157921, '[-2.37570629382808, -1.04995863193441, 2.07604531045681, -0.807229985379467, 1.08474989966145, -1.0960588121922, 0.493954715192259, -0.318310909199382, 0.273134776809935, -0.61459747117965, -0.610760381329007, 0.140840810078379, 0.655169145397799, -0.626378873556277, 0.441671901985843, 0.670600034263735, -0.994564066909666, -0.403395298258754, -0.651618397975362, -0.49852969328415, -0.368564851607881, -0.35590977213292, 0.81138039220605, -0.0084219094805577, 0.482435792051295, 0.335093259566536, -0.240414267605951, -0.155891831117611]', 32.0); -insert into credit_card_transaction (time, features, amount) values (157921, '[1.38834229189561, -0.388268819883058, -0.401905090977022, 4.24114276500166, -0.119271577596902, 0.38792100919243, 0.25364013450123, -0.111083582731924, -0.50288165969927, 1.12245784571971, -1.39644097799082, 0.821426950025301, 1.38728501374022, -0.558687497890179, -2.03926870637376, 0.279001420849993, -0.40790763529645, -0.695422717220419, -1.42850628482773, 0.283336656765931, 0.261126085557606, 0.544270143098555, -0.15599877337842, 0.0478875801794117, 0.045308984122726, 0.105473464231047, -0.0381406711443492, 0.0001012789782845]', 256.59); -insert into credit_card_transaction (time, features, amount) values (157921, '[-0.502112973235413, 1.35186603328416, -0.412836920357391, -0.2615558778689, 0.467122897046593, -1.23791813112278, 1.16580854006253, 0.0446847769772345, -0.444110246444329, -0.7393545570311, -1.127309041561, 0.512542557742862, 0.360845245890352, 0.609044759599614, -0.592587851281732, -0.949769044025727, 0.135711529408129, -0.448722187933098, 0.56145111836586, -0.0468475951403699, 0.12007664745922, 0.543318160639334, -0.183263483858848, 0.0909354118908938, -0.0115440124805144, -0.219742904983597, 0.283016279869387, 0.181147342922975]', 19.99); -insert into credit_card_transaction (time, features, amount) values (157921, '[2.14977318513032, 0.0918546589971934, -2.48539435685465, 0.011082315857333, 1.20234423792827, -0.485703100158384, 0.696580403794684, -0.37595348220969, 0.0849770975413368, 0.144088582594547, -0.233900185769754, 1.02298046458883, 0.750042246861589, 0.614021397682603, -0.896903161861583, -0.335743698856402, -0.894274044783687, 0.270438328350434, 0.958145900928726, -0.159903610467402, 0.105323062181276, 0.54569712210562, -0.2717591164204, -1.31324847552732, 0.738292679928578, -0.0061735505435438, -0.0538232332469688, -0.0960391310016814]', 7.74); -insert into credit_card_transaction (time, features, amount) values (157923, '[0.144854668144045, 0.942795884614655, -0.616418344695841, -0.781397453720165, 1.19497757998435, -0.219874196555399, 0.81984880343284, 0.0991589007847242, -0.195286383124, -0.648184510692283, 0.439947920968067, 0.132401190305614, -0.472892716264366, -0.755977814134993, -0.846583646033655, 0.708518590146123, -0.0534080723981094, 0.515331742586739, 0.268809084576554, 0.0112938211942947, -0.332538187196344, -0.862024914655354, 0.0166797699176292, -0.0255676789916286, -0.396233036781039, 0.130041264196013, 0.215407740467439, 0.0674893382702629]', 0.89); -insert into credit_card_transaction (time, features, amount) values (157923, '[1.76794676949225, -1.3317884743186, -1.81390309537759, -0.778302902112833, -0.36067864548818, -0.12158355832951, -0.513622636667698, -0.017555021146143, -0.401912016961426, 0.320425716721317, 0.894189184261683, -0.250111873563193, -0.0412585705092759, -1.29250076137025, -0.440902737498159, 1.66897950273209, 0.826840167764128, -0.28890062993788, 0.772643032446014, 0.452530429634523, 0.243860284072686, 0.237728354398629, -0.0429106778502691, 0.140346897973324, -0.173091873199418, -0.244046219434171, -0.041094952254558, 0.0009445960115419]', 218.35); -insert into credit_card_transaction (time, features, amount) values (157925, '[2.06612409234861, 0.149124262884596, -1.69485014591972, 0.415536143517643, 0.431440051287345, -0.811468848112239, 0.166703470687372, -0.208972068821107, 0.499038603910471, -0.384722274298209, -0.505306774608129, 0.145939917620932, 0.0026536363261812, -0.826880873628236, 0.256620594620613, 0.253322010510034, 0.541493710627918, -0.39796047478566, -0.0272882746234939, -0.168195058334121, -0.358328464876924, -0.920589980050934, 0.350864829724375, 0.507042651410159, -0.277817463850434, 0.175586004307004, -0.0596769873963704, -0.0310382416608503]', 0.89); -insert into credit_card_transaction (time, features, amount) values (157925, '[-0.469927368026209, 0.681071126186689, -1.21057057165811, -1.64019103503306, -0.0452686023708123, -1.13371102044959, 0.243489695007196, -3.59311561575316, 0.0704217064755982, 1.50230778198256, -0.585439928322057, -0.540175022475834, -0.515417190945755, 0.424215596731098, 2.00601585834365, -3.90967508691696, 1.05942396178825, 1.43159240382367, 2.68701634861656, -0.88618933102057, 2.15417831768358, -0.151035698785869, 0.279019326375889, 0.0465086732777118, -1.268932962529, 0.0616609012282296, -0.530081562099901, -0.103633299281977]', 22.18); -insert into credit_card_transaction (time, features, amount) values (157925, '[2.03798409859368, -0.107904287351189, -2.2134741931746, 0.284251169697136, 0.861885585767768, 0.105492458142275, 0.0152618285931339, -0.0266753694179184, 0.562015102087528, -0.315493098591407, 0.0470049034370809, 0.523385053513287, 0.0328599834904463, -1.07213506524223, -1.13083891487648, 0.389608705876798, 0.262801718585189, 0.842614443417707, 0.624837980719691, -0.0716681060532197, 0.0532330445778965, 0.363661241709029, -0.143960173513846, -0.309169799901015, 0.325451888604953, 0.733226556750844, -0.0674276729432825, -0.0537980248281564]', 28.75); -insert into credit_card_transaction (time, features, amount) values (157926, '[-0.149720031210442, -1.29947671081083, 2.02159688784832, -2.51844685789081, -1.04558831530504, 1.90806545577602, -1.61774428375973, 0.624424924848313, -0.638222959495195, 0.496819186791492, -1.27469442222772, 0.204533175024581, 1.47255254524403, -2.06406183073973, -3.12707780329266, -0.349028465809798, 0.149612869201718, 1.14652588975338, 0.682683957223623, -0.177374548345525, 0.0366306720757109, 0.911385983475427, -0.178041636891683, -0.288273405218505, -0.469451968225097, -0.0448956568338422, 0.277215773089255, 0.191103641067075]', 31.8); -insert into credit_card_transaction (time, features, amount) values (157926, '[1.9795778736571, -0.0628243311654117, -0.801145033525763, 0.429915507498614, -0.331972236085698, -1.20402428218077, 0.116049582576774, -0.242515096543534, 0.194024894151914, 0.231712898076633, 1.51719058007952, 1.4860179666106, 0.205326508986029, 0.44125888752274, -0.737272159311528, -0.0884109523365345, -0.352401335637593, -0.598001143221396, 0.220898416506869, -0.210420842369034, -0.196652433595654, -0.456300566777828, 0.39340865192979, 0.557670916520259, -0.39282097127563, 0.142403130544455, -0.0717247944041733, -0.06366750739952]', 1.98); -insert into credit_card_transaction (time, features, amount) values (157926, '[1.67225192237509, -1.67184066639985, -0.78690565921556, -0.71283191897065, -1.07475689136153, -0.149468662167204, -0.602240865497721, -0.0581022270524489, -0.220055242376447, 0.676282310115501, 0.724108066847037, 0.751219767809571, 0.84039135057236, -0.429802304306782, -1.14830086393876, 1.14431773520427, 0.0334859208353034, -1.3913084441712, 1.17342778428317, 0.489415862483088, -0.024771797436085, -0.515639856585539, 0.16354849555215, -0.398703372753287, -0.543406467511388, -0.594296156357309, -0.0317175746725137, -0.0144454796375164]', 246.42); -insert into credit_card_transaction (time, features, amount) values (157926, '[2.06327052064031, 0.6332167225651, -3.16169090593985, 0.614965472820401, 1.25925880130661, -1.3590697924574, 0.70652267672436, -0.327700599453414, -0.199335992216013, -0.869560880333858, 1.7312478121582, 0.244128501610669, -0.572848671895177, -2.16855584459408, -0.67542427667193, 0.523879433659684, 1.95689558843626, 1.06305703240325, -0.0273398567463443, -0.133516068658077, -0.0183601373573682, 0.105097181497664, -0.0466043617170252, 0.587523290926874, 0.35710594172373, 0.659627191027381, -0.0870417683732725, -0.0231235931601989]', 0.76); -insert into credit_card_transaction (time, features, amount) values (157926, '[1.89249428551278, -0.126636355157507, -0.269777503092203, 1.59414794585428, -0.346948353572951, -0.262272167246693, -0.157656720106602, -0.0709524219453199, 1.02794768206607, -0.0539594756059052, -1.09023926843527, 1.27120612926327, 0.759433012836665, -0.45104598593821, -1.27398935659502, -0.528824285411609, -0.0464234198283567, -1.08995304056593, 0.127766751761212, -0.203585489958346, -0.496868851816582, -1.11414829401264, 0.418152845346167, -0.0705564610178097, -0.307551134633643, -1.10722847425256, 0.0610376203425599, -0.0256228531566949]', 25.0); -insert into credit_card_transaction (time, features, amount) values (157927, '[-1.49950278065785, 0.198544987027778, 1.24902345373812, -0.228124591498957, -1.28602824063477, 0.175765469678459, 1.32019922670457, 0.359833114676594, -0.334021624874887, -1.52211713677469, -0.957003189839956, 0.0888477030312176, 0.298160977009357, 0.300753694659534, 1.02372674417847, 0.241951134912847, -0.304956838512993, 0.213557377093191, 0.197082971472968, 0.613954136944857, 0.105898619377152, -0.394461650590835, 0.57259484395786, -0.157633209706208, 0.444455062193257, -0.410752007972983, -0.163469144579092, 0.0364531341705004]', 340.0); -insert into credit_card_transaction (time, features, amount) values (157927, '[2.00999773155784, 0.0780338456022838, -1.61652155075309, 0.339618048335816, 0.386999813090905, -0.581690901989414, 0.0487643151932074, -0.0555420652167005, 0.272619965813547, -0.208061364054128, 1.28223767750235, 0.731158673223781, -0.399539294696636, -0.562925756847489, -0.423129728284929, 0.418383688994817, 0.36364542753353, 0.0828018930751454, 0.205517583317011, -0.178528913716366, -0.299133408303439, -0.803996640628812, 0.360080791148288, 0.600858565849764, -0.338815187405455, 0.1493847773877, -0.0653980436276464, -0.0400808384428196]', 1.98); -insert into credit_card_transaction (time, features, amount) values (157927, '[2.07242834951376, 0.170784467937744, -1.68610985974446, 0.423844711265842, 0.414162742968445, -0.895834796696281, 0.197905432085552, -0.248225625592589, 0.466426547731218, -0.38322948831772, -0.510169448179327, 0.246294227200491, 0.225832110174649, -0.875674627951868, 0.184517595215887, 0.299102369995215, 0.475778461449691, -0.338786076466451, 0.0394066526440644, -0.145858681175497, -0.36042757844679, -0.925081656658464, 0.341634212111376, 0.572669773498015, -0.256878969460669, 0.169051407112824, -0.0616025591092564, -0.0297246080302685]', 1.98); -insert into credit_card_transaction (time, features, amount) values (157928, '[1.22668843949932, -2.44462223269125, -0.893435787102717, -0.647262341835603, -1.09395247475253, 0.851845331530816, -0.783713700846096, 0.200736534870435, 0.059334956177401, 0.432998865157126, -0.0574662532598882, -0.210869192014289, 0.424701382728348, -0.314012591159239, 1.43569115866562, 0.496097419483148, 0.86516185477645, -2.31918846005751, -0.9936610104215, 0.724262791781626, 0.734734972051411, 1.35268564644745, -0.141984034597083, -0.97126974001279, -0.578356232595922, -0.0124460184139842, -0.0078255562590056, 0.016010141695998]', 425.0); -insert into credit_card_transaction (time, features, amount) values (157928, '[2.30177519907839, -1.25629502042441, -1.08308622456662, -1.61083860571318, -0.926888538614509, -0.497754813926663, -0.971889813375486, -0.250068274408717, -1.41090337045908, 1.53118295388869, -0.88988163800387, -0.246967156747601, 1.55576017518623, -0.626213276958146, -0.245237573021508, -0.678501193569506, 0.448204858123719, -0.304258605922677, -0.153860201316035, -0.337322004793529, -0.161210061954408, 0.0788369253966963, 0.238571415146495, 0.615831920613947, -0.111112415612536, -0.188268182255441, 0.0133491261760834, -0.0428505536525869]', 20.0); -insert into credit_card_transaction (time, features, amount) values (157929, '[-0.996633663542127, -1.53229451325185, 1.62348838460276, -2.63569910929782, -0.209817581830176, -1.10360305331492, -0.644968275106295, -0.0210492973107727, -1.94672763390834, 0.480837015985327, -1.04008182540377, -0.599111544266612, 1.14441585333425, -0.82533057089345, -0.65210905684967, -0.17732662692614, 0.133720385012858, -0.204826628163015, -0.852801296886538, 0.0284644717629053, -0.210680180576877, -0.598114124687074, 0.361205503811889, -0.0802641885349423, -0.364965441564858, -0.574159276451151, 0.0914888529202736, 0.166600535479948]', 79.95); -insert into credit_card_transaction (time, features, amount) values (157930, '[1.88667080042368, -0.337838076250316, -0.543728986890066, 1.05161430647845, -0.17983096641759, 0.396881738709101, -0.628385290693623, 0.220266513283861, 0.732336098545478, 0.386322363202457, 0.328422780778863, 0.502116781623136, -0.37866923712037, 0.269425124734399, 0.206780981821633, 0.821881907666493, -1.17185177836571, 1.06809811152382, -0.325963303513875, -0.160025368211387, 0.260342100363387, 0.733663635780487, 0.0910736279044898, 0.321768392159181, -0.0793627923311834, -0.593850042078899, 0.0380393160990945, -0.0319022611131042]', 40.0); -insert into credit_card_transaction (time, features, amount) values (157930, '[-0.748864800183258, 1.21581870775893, 0.705935748097067, -1.08183091938693, 1.24553069749271, -0.588250921000645, 1.34620588621373, -0.120517533308124, -0.680443752040808, -1.58025959317457, -0.613394596538767, 0.0006792452833486, 0.416059829725375, -1.08656005646751, -0.401013749240097, 0.393404291661908, 0.295977691832879, -0.374662895282829, -0.576872091533141, -0.0202856534745011, -0.34510109111359, -1.04574096880025, -0.303818005533999, 0.520637078608609, 0.615090837935169, 0.211693664237025, -0.0505382067459184, 0.0673700622401467]', 11.28); -insert into credit_card_transaction (time, features, amount) values (157930, '[-1.21657246910893, 0.568843673348446, 1.64876509383322, -0.557792472045414, -0.273298988445341, 1.78012172857568, -0.314914711962459, 1.02305282145287, 0.548210456703622, -1.1787380420447, -0.681266913112478, 1.05521178688015, 0.70408647872069, -0.515858965926757, -1.13395623276089, 0.165666011204449, -0.463233693722475, 0.945998889103085, 1.19095111017846, 0.275452775193575, 0.0029476309373415, 0.141420541878362, -0.258955781536889, -0.333517955042215, 0.694632028148933, -0.128919900332844, 0.208011628336672, 0.0701955704622153]', 83.57); -insert into credit_card_transaction (time, features, amount) values (157931, '[-1.58155892330988, -2.77860440658372, -0.510734456627402, 0.603838683866386, 3.89006771657355, 0.0235024593207987, -0.812579162743547, 0.403818636148781, 0.104050386646725, 0.166804211343967, 1.24375204069339, 1.54249834202393, 0.531271557425058, 0.252255191565789, -0.33017965465238, -1.95937488442042, 0.750646956754927, -1.76834461224154, -0.420762944125979, 0.765336181770929, 0.584801954267739, 1.33307390203306, 1.03633300114502, -1.63511855907707, -2.0942093176399, 0.200851894259616, 0.125664788857475, 0.167508926127598]', 111.75); -insert into credit_card_transaction (time, features, amount) values (157931, '[-0.999705470695706, 0.661781016640732, 1.01828728393128, -0.805451545376523, -0.755428099574746, -0.493924324642999, 0.0950675491652153, 0.362861604732441, 0.703607190076796, -1.03793544696967, -1.44266150499005, -0.183107631747373, -0.651545299893335, 0.0643111205055434, 0.118615058128725, 0.215812897625593, -0.366418166689673, 0.447597698769094, -0.272673444317152, -0.398274096834139, 0.359274398785639, 1.0693916355755, -0.0893035191721659, 0.0816305850691395, -0.2779978596893, 0.114368958483053, -0.17717216923088, 0.0730964430666244]', 49.94); -insert into credit_card_transaction (time, features, amount) values (157931, '[0.100148282414153, 0.676166286838082, 1.04349439642276, 0.962489552506196, 0.365100836667361, -0.261914242227575, 1.47077152220227, -0.639380199211605, 0.544084425402585, -0.0411579775358732, -0.923983833654127, 0.888923002165709, 0.552844354748019, -0.902308723167735, -2.07147922941979, -1.45113845494642, 0.124450560151414, -1.07566358426091, 0.37047770840819, -0.0508854197968171, -0.210678083560212, 0.155222398984881, 0.108502729656684, 0.402514909981623, -1.15401443881916, -1.04309538805417, -0.107650934684726, -0.176566228168035]', 55.0); -insert into credit_card_transaction (time, features, amount) values (157931, '[-2.36984159154914, -2.06708959177853, -1.672846441851, 0.207226862282187, 2.09357274384398, -1.92823856607067, -0.0183104403338608, 0.286660233107773, 0.720045768536136, -1.33038276403666, -1.0858394197759, -0.0851601176976521, -0.545261412278518, -1.15329237694464, -0.569572615417847, 0.222130835308653, 0.978158714747765, 0.342694602056529, -0.0428267414226355, -0.617810009419051, -0.0987121863144065, 0.340048359096145, 2.43002557793095, 0.5041642980143, -1.43884015852876, -0.40594440083711, 0.425397261169659, 0.0821332529257892]', 59.15); -insert into credit_card_transaction (time, features, amount) values (157931, '[1.56316419522887, -1.92237851048437, -0.286649942076551, -0.289824700789892, -1.6573304828314, 0.0923729497343368, -1.06135356222741, 0.123180954115099, 0.105007234380892, 0.858124473367227, 0.242249897991142, 0.382766851217087, 0.0108929719067542, -0.118073309622936, 0.028971629509992, -0.504475324346855, -0.706804464266619, 2.04814054847755, -0.730959283968476, -0.0638560757163427, -0.164109422940277, -0.502639121143212, 0.135547486134662, -0.355356367384818, -0.813823143364726, 0.439412438879461, -0.0449778542810493, -0.0024370835543427]', 270.0); -insert into credit_card_transaction (time, features, amount) values (157932, '[-0.160297074648054, 0.709094115878877, 0.148480596231733, -0.516943573360998, 0.311319747315188, -0.965599269386475, 1.06040374899812, -0.279752378143138, 0.15143058394144, -0.399156586790789, -1.12079995666747, -0.359456328175633, -1.00125455513815, 0.232275012925142, -0.49926874390349, -0.265317902442354, -0.215536334494666, -0.751266053430272, -0.0432339711763388, -0.326989621419416, -0.160986762101509, -0.439158590079785, 0.109031352133516, -0.0284253278190202, -0.499343484232979, 0.186041583443561, -0.240397385572595, 0.005166309357732]', 28.29); -insert into credit_card_transaction (time, features, amount) values (157932, '[1.99399610750816, -0.441073379295851, -0.550121635450938, 0.17594495130319, -0.40664065146016, -0.162611661834131, -0.556174657533116, 0.0240606336825749, 1.10933758232747, -0.0185502218286165, -1.09789316881293, 0.221083335413362, 0.132876082197357, -0.120905585088131, 0.610275311059643, 0.565137812534681, -0.643151468420928, -0.276170973023189, 0.0482427268632735, -0.114947435540659, -0.231593211683458, -0.635637013098799, 0.403971896481946, 0.46582289294961, -0.587699144423881, 0.254587372305099, -0.0440856276239011, -0.0354787063757335]', 27.5); -insert into credit_card_transaction (time, features, amount) values (157932, '[-0.923743711077661, -0.0815436012816398, 1.01890830747789, -0.631155831821731, 0.728647896683211, -0.545908250536857, -0.0939054900012241, 0.0480129080617804, 0.405543215330192, -0.343967733273209, 0.413113050146179, 0.803599670305774, 0.377403376991828, -0.0429060416387811, -0.157090461258343, 0.504934899689507, -1.1877927210154, 0.995809183479603, -0.0581101183273862, -0.241519417589196, 0.378695637017941, 1.18094221007507, -0.0907052791267418, -0.30363944506113, -0.741254881426649, -0.391510645595408, -0.0277062511770515, 0.348112927089877]', 0.01); -insert into credit_card_transaction (time, features, amount) values (157933, '[2.01048079100994, -0.163105237744151, -1.02358735609447, 0.331409065401812, -0.208549339987795, -0.962479551905495, 0.0269321823599938, -0.145556666765375, 0.404757178299718, 0.26670775381077, 0.895190191312959, 0.70354411542486, -0.891431446637449, 0.64788656446437, -0.573382353222288, 0.104241874415154, -0.456065049761307, -0.299636396288245, 0.405997076630764, -0.269284330987692, -0.240721748817081, -0.639920425027522, 0.348765906065566, 0.0541082025664383, -0.370181167062733, 0.175461551385254, -0.0801851374028922, -0.070848969242833]', 2.69); -insert into credit_card_transaction (time, features, amount) values (157933, '[-3.21445000084446, 2.91673744080652, -1.8096405745232, -1.02585432790887, -1.27548416232014, -0.547802031402771, -1.37006921340876, 2.08578685226851, 0.707730213691018, -0.96840079874447, -2.1415280814966, 0.347935421516118, 0.331449363036786, -0.313308457981825, 0.40258689024827, 1.42739849619037, 0.766457367589872, 0.835274228448777, -0.0676473357997088, -0.198901135849425, -0.148731851764974, -0.949253091373133, 0.129468696040402, -1.16027957012113, 0.179760019683015, -0.23109155793543, -0.713593054679059, -0.0136291798134445]', 15.33); -insert into credit_card_transaction (time, features, amount) values (157933, '[1.94031432169939, -0.544233624273246, -0.49048247028825, 0.421451359399117, -0.572816447416609, -0.127636909255188, -0.656312416187755, 0.0523169392621075, 1.53107533206318, -0.169331051888316, -1.19524283021061, 0.179578229085371, -0.234706939293981, -0.141673839022606, 0.573752806042991, 0.145934777337158, -0.542353763513583, 0.383136279159721, -0.212045315922458, -0.15593370018846, 0.247900267122131, 0.862970103588059, 0.0837531939331197, 0.686706300720079, -0.0359092549126385, -0.184701218054395, 0.0287115852983715, -0.0275239764750578]', 37.78); -insert into credit_card_transaction (time, features, amount) values (157933, '[1.49409021870911, -2.71278067203835, -0.834707535825328, -1.39735154620108, -1.84857565145707, 0.067364911155527, -1.1105904628073, -0.0455454425153687, -1.32691992068007, 1.54581476644946, 0.843089267257774, 0.140911334245054, 1.15913838191924, -0.470123964905989, -0.250684384426538, 0.109188499842649, -0.0477974859861319, 0.916211583361146, -0.32269850765727, 0.43095587827675, 0.323305498570045, 0.534731173237921, -0.10025923838944, 0.792810112691794, -0.450799514016043, -0.163215685259759, -0.0354466759780227, 0.0256698237320813]', 396.5); -insert into credit_card_transaction (time, features, amount) values (157934, '[2.05051487153934, -0.045836353913454, -1.65988411541739, 0.512492674387287, 0.1312427574889, -1.43688696506041, 0.53251091544426, -0.434171636489779, 0.692519774103628, -0.0482950603998944, -1.00774577985129, 0.0883755183090037, -0.881945406855815, 0.623926151907753, -0.238823561021906, -0.779397570357765, -0.0418641476062158, -0.555759180390315, 0.230120983103104, -0.288944095592865, -0.0027552192544852, 0.177790555018511, 0.0304282345799942, 0.0555684837406786, 0.332706399608959, -0.222421038755834, -0.0430287956281118, -0.0655962020111639]', 17.99); -insert into credit_card_transaction (time, features, amount) values (157934, '[-0.852475802121942, 0.590264681919447, 0.871489937947575, -0.308073286188237, -0.207866690565924, -0.0318295487406609, -0.19742958178507, 0.631285062995786, 0.541966296567461, -0.862467179835533, -1.52030087729121, -0.100929432409838, -0.299295041043022, 0.108546750818018, 0.414708459726021, 0.0343875692949917, -0.266055585255675, 0.655586153914652, 0.157511855513372, 0.0673548312953131, 0.347247701556557, 1.0317434120918, -0.142737614428176, 0.686748511239331, -0.169087002484128, -0.133578656190316, 0.317826877483763, 0.150153259257448]', 29.96); -insert into credit_card_transaction (time, features, amount) values (157934, '[-1.64937504465849, 0.912264588784204, 1.25975654945296, 3.02071593705283, 0.434721587872998, 0.137396270373949, -0.290408942202668, 0.42950993834176, -0.970614790780346, 0.870068741642854, -1.61961161665048, -0.105361275079481, 0.560566436468923, -0.273708808065161, -0.972386978305047, 1.24352826411273, -0.854588187637739, 0.0165976480759262, -1.9511340864707, -0.607226772013721, 0.290573653321462, 0.727866317439068, -0.619974484563451, -0.0428794870960244, -0.211598561201863, 0.0072911947834407, -0.400901041151647, -0.23138405620086]', 8.27); -insert into credit_card_transaction (time, features, amount) values (157935, '[2.00676454566572, -0.710237833925383, -0.755293639141048, -1.62640302113185, -0.625136090845934, -0.546715596537143, -0.572969286306591, 0.0222630902156835, 1.81197066995996, -0.801281967783048, 1.17818691204433, 1.60266326364236, 0.17779544035028, 0.193881783687797, 0.50573184799523, -0.179786831224149, -0.560710817796427, 0.254741303998895, 1.07250517903783, -0.124507887887863, -0.170966816749237, -0.367460029516793, 0.389125186637656, 0.752546762498424, -0.430053917412824, -0.617594779104801, 0.0169297836474716, -0.0390176525159983]', 15.88); -insert into credit_card_transaction (time, features, amount) values (157935, '[1.8152957925141, -0.678746107553952, -2.4025418562889, -1.43217797689056, 0.189442851410343, -1.79984882565986, 0.966507796699274, -0.687095186406237, 0.879442466848056, -0.88253682092583, 0.128477328269856, 1.32638365201651, 1.04606747098471, 0.648284921621648, 0.914678732102231, -1.31133661582044, 0.0192164467973231, -0.721059530122926, 0.572362750797546, 0.22209128467019, 0.254297739029505, 0.56845770814564, -0.11372004482326, 1.19104973989717, 0.376835687297965, -0.152228177039608, -0.0850118732729521, -0.03333362981648]', 180.29); -insert into credit_card_transaction (time, features, amount) values (157935, '[1.40763673596149, -1.15439092717661, -0.708486205639558, 1.34639535383586, -0.682613754301022, 0.108297439154889, -0.281614886873305, 0.095803598225612, 0.801085416968342, 0.294608217861467, 0.173787872122009, 0.246645864162601, -0.856057353747963, 0.43704032539123, 0.257295442992941, 0.879562777910484, -1.14625461407603, 1.17766454346156, -0.376059919468366, 0.280278052594373, 0.402834049353601, 0.600409279993243, -0.167526867690392, -0.521844412501431, -0.192937035480397, -0.578062878500199, -0.0069689442486937, 0.0017792140438377]', 286.0); -insert into credit_card_transaction (time, features, amount) values (157936, '[-1.99960160045057, -1.03097007345002, 0.868237358401827, 0.159978909198401, 1.17580569884783, -0.990404086795604, -0.685854705757231, 0.514693219225114, 0.056373920060499, -1.07163812635335, -1.50081370185409, 0.595907505912696, 1.29591216281519, 0.243759142194074, 1.18529877612739, 0.144926263315029, -0.504452084329012, 0.494566931743183, 0.912768704477751, 0.59130621640204, 0.133479871242145, -0.35503856343963, -0.0808384568878395, -0.723466007608803, -0.169443315890385, -0.760528326090835, 0.0952557361345498, -0.206094254441784]', 42.69); -insert into credit_card_transaction (time, features, amount) values (157936, '[2.12568821011582, -2.03436515358675, 0.900202268696414, -0.968166346990035, -2.6112160839556, 0.422304123376147, -2.38345416590547, 0.220293158533413, 0.414358841966527, 1.13416758777087, -1.7220770903612, 0.352467934575303, 1.54098711939885, -2.11660112855519, -2.08647577744429, -0.870003571468066, 0.92170646437955, 0.0775580207928043, 0.111972634866761, -0.357992637821618, -0.0495320504133747, 0.842973027979845, 0.17722166441416, 0.115382047613715, -0.320203625457979, 0.015381260417291, 0.125359724286661, -0.0226058418312986]', 27.5); -insert into credit_card_transaction (time, features, amount) values (157936, '[-2.36520761815987, 1.96212555829729, -1.0475985119509, -0.225612525608671, -1.0468606891665, -0.377309573552944, -0.930375761538982, 1.72723574494781, -0.0404905057234974, -1.33488885852872, -1.04455162856296, 0.726296637077188, 0.82939946816477, -0.539656098018437, 0.258189525507556, 1.26277629478531, 0.947665603120058, 0.310601639128592, -0.585832049133921, -0.302597616939814, -0.13703350222999, -0.794172260731257, 0.273110299296714, 0.553542125236304, -0.0545949643094026, -0.0293110286785042, -0.541870079163803, -0.173437350956433]', 35.0); -insert into credit_card_transaction (time, features, amount) values (157937, '[2.07073180799526, 0.227550669120164, -1.31574927009334, 1.7266136652626, 0.600045853741118, -0.313731494787029, 0.271924383258325, -0.241764100953671, -0.879137142565208, 1.14052757613183, -1.1357505243856, -0.0246882636798341, 0.703436711202168, 0.0737396200929519, -0.81072903270696, 0.825323047645772, -0.556323780519571, -1.25861827629337, -0.508339505980047, -0.103105497707805, -0.251511327456123, -0.729932964108987, 0.326644059692305, 0.560588901806596, -0.442805340472907, 2.04715086625208, -0.223480358423593, -0.0800168737441402]', 8.63); -insert into credit_card_transaction (time, features, amount) values (157937, '[0.0358113165050554, 0.740719159308204, 0.0944803876090328, -0.804778045505724, 0.608162459433124, -0.543976232764819, 0.847261450713584, 0.0196422143850203, -0.137734274500655, -0.216900516730717, 0.194597805908088, 0.195452382433387, -0.880324521923666, 0.45721000670016, -0.971497775211918, 0.284121323720517, -0.781786981714732, 0.0117086063544979, 0.390657152543995, -0.0677282260485649, -0.250026015762826, -0.619151811566777, 0.0111216423588114, -0.524065779205352, -0.485622877828517, 0.153284567765541, 0.23665543092961, 0.0804886050444655]', 4.49); -insert into credit_card_transaction (time, features, amount) values (157937, '[-0.476661841557924, 0.815895421808081, 0.678821390969839, -0.773410961827341, -0.656031551252051, -0.720742936161877, 0.0194006714821521, 0.34139181427534, 0.642837592119663, -0.891209223898404, -1.16651077440555, 0.153350383618923, -0.28678857807104, 0.13576682716239, 0.248841608411489, 0.351327041714304, -0.440560201340703, -0.0763517143944299, -0.16485121004736, -0.354658954760584, -0.0511691387550822, -0.295775297330079, 0.200184229821621, 0.0039487510590451, -0.765040643508011, -0.384960260404724, -0.302855669408198, -0.0165209291067946]', 19.95); -insert into credit_card_transaction (time, features, amount) values (157938, '[1.93484338219189, -0.389694760094884, -1.44706099169778, 0.0081580551269274, 0.654437771578433, 0.877317935558023, -0.283677918179636, 0.371407993418918, 0.575884941463277, 0.159969186058513, 0.731405267405812, 0.252166929923869, -1.67358051629264, 0.857794902005322, 0.338035963070511, -0.515780456701549, 0.155230586091055, -1.30115197132758, -0.381920279310698, -0.396723132694109, -0.21795942486648, -0.505271720674369, 0.354032768254475, -1.67830397851102, -0.516607247183201, 0.315750393161434, -0.0368376229698279, -0.0909043683195387]', 1.98); -insert into credit_card_transaction (time, features, amount) values (157938, '[2.06253949386552, -0.0822292638845887, -1.13743899601745, 0.375135616510582, -0.0569003504536342, -1.04451125851927, 0.152887861239511, -0.286738147441077, 0.584283724077538, 0.0711448717282764, -0.868474100617354, 0.288731855052136, -0.134677609621868, 0.316160479489972, 0.123341061964316, -0.107611065615564, -0.275826598985267, -0.880057623368549, 0.134407694463371, -0.23801453525169, -0.295679061276576, -0.722962455760098, 0.329472155146491, -0.175943785007295, -0.300425549242861, 0.211411610435611, -0.0693891777777187, -0.062403053465718]', 2.7); -insert into credit_card_transaction (time, features, amount) values (157938, '[1.91797632096915, -0.101480181368795, -1.88891369173568, 0.219718858249453, 0.485723915735256, -0.445284860351535, 0.133246981730833, -0.142217479149238, 0.6567373613872, -0.728637586332209, 0.78093714116065, 1.31836526330095, 0.914956352571586, -1.47653195752055, -0.836017995056432, 0.28794654937039, 0.564584930214796, 0.542679419618553, 0.593760076707005, 0.015706653401832, -0.192899092760535, -0.385347462359433, 0.0308762411111002, -0.886188461346622, 0.0029976297248518, -0.0666178805549832, -0.011731425286745, -0.0278809193377484]', 63.35); -insert into credit_card_transaction (time, features, amount) values (157938, '[-0.379371239477643, 1.42202910728327, 0.555737702645311, 2.65069604526932, 1.82774672661635, -0.826461077005383, 1.86598897579944, -0.495449616913309, -2.16549802584117, 1.01202000210432, -1.29621767841865, -1.18180443670675, -0.857239426525845, 0.563673244455897, -1.01374778800874, 0.44347054819045, -0.896588812741444, -0.425102964327577, -1.63692782825568, -0.122771351962183, 0.149774019942872, 0.40317545822151, -0.289864964219639, 0.0104582479333766, 0.0495879528771649, -0.0128977106344409, -0.102305218786961, -0.0675495373159021]', 13.72); -insert into credit_card_transaction (time, features, amount) values (157940, '[-0.0973246006848609, 0.592255028709971, -0.0593528667094778, -0.999360298185324, 1.34393876660967, 0.972694936639742, 0.598442885728545, 0.433777452146986, -0.188697472685601, -0.354892657515838, 0.750370890438897, 0.592604956044477, -0.526043600274106, 0.456610998569303, -0.236193949937416, -0.561329838420221, -0.0332325350564239, -1.32745966807357, -0.619219806360956, -0.132283383013645, -0.174622051990059, -0.280580073186863, 0.0691620640734305, -1.65217111766122, -0.666513702622147, 0.254181093996747, 0.288765340549955, 0.0693795945108346]', 0.89); -insert into credit_card_transaction (time, features, amount) values (157941, '[1.66346628332977, 0.0161565033823855, -3.07944715005947, 0.963218641136904, 0.563971985156584, -1.90183911484916, 0.850783612408749, -0.448028586100193, 0.649786689981896, -1.53742228341976, 0.11071062410117, -0.820379309597694, -1.42190781779088, -2.48126093499951, 0.926818814135502, 0.483620732209474, 2.39707423193273, 1.25805342436726, -0.580975052675977, 0.148274702822015, 0.0344739992572245, -0.168687215772104, -0.18997868982085, -0.309280568531209, 0.248661464461843, -0.531051579455465, -0.0093241156849915, 0.0457075792705235]', 200.24); -insert into credit_card_transaction (time, features, amount) values (157941, '[0.135284262260185, 1.04293772891261, -0.416184305175535, -0.558121056674973, 0.978586839717608, -0.723364610572361, 0.991677983344454, -0.102323404973826, -0.0966378332773202, -0.854621436384597, -0.615363971553324, 0.0609620225525676, 0.329660925254782, -1.07707145820707, -0.224448681588369, 0.268308657069071, 0.391878960842543, -0.332201804502183, -0.290976293642326, 0.0405190764221664, -0.332610248695567, -0.791248653198165, 0.100974025454756, 0.507926277898433, -0.419543953441283, 0.118221122502486, 0.226584727952845, 0.0843743373490887]', 5.99); -insert into credit_card_transaction (time, features, amount) values (157942, '[1.97490836803606, 0.393454453047863, -2.41811749805605, 1.48453125920707, 0.990454022478434, -0.895208127169753, 0.683416138108996, -0.310635893075828, 0.193750090274313, -0.363908216620832, -0.72348345756227, -0.25168414960463, -0.688610488281047, -0.909783993477505, -0.264881854143427, -0.169342326113957, 0.985606320752739, 0.1300561812146, -0.394645700129386, -0.183293805900527, -0.0317886345857304, 0.0222016725534007, -0.0289695944596609, 0.46938804748557, 0.470085849727597, -0.517057395486755, -0.010272556184309, -0.018587905753234]', 40.31); -insert into credit_card_transaction (time, features, amount) values (157942, '[1.72709379859789, -1.01136616711475, -0.747801514904899, -0.797741731916275, -0.633602209930809, -0.19572117697175, -0.424757716464845, 0.0378939284983705, 1.14748749498058, -0.359052658044124, 1.19424267713631, 1.52570547620699, 0.56033600530047, 0.081696641711565, 0.165045661037726, 0.265515219958242, -0.600558439255539, -0.0876576340650845, 0.583289876599488, 0.180549874235881, -0.11807198142173, -0.562324397426176, 0.333897501956626, 0.731700219345021, -0.700264284981191, 0.219022163514518, -0.0703824020252954, -0.0248764219847886]', 149.0); -insert into credit_card_transaction (time, features, amount) values (157943, '[2.06256465300189, -0.113800312255569, -1.07906987892629, 0.426818207976473, -0.229405431683488, -1.24121665746299, 0.121577002191013, -0.283253521750122, 0.67995808293897, 0.100613207746825, -0.88964623228307, -0.046501348752039, -0.855279289386214, 0.458389062081293, 0.139934344239305, -0.0648617785902098, -0.232415432788744, -0.760129372296675, 0.149218311915294, -0.285130677852012, -0.29971944138635, -0.772174932416073, 0.353041343265542, 0.021145176792245, -0.330326371385632, 0.198543551712851, -0.0765679942228843, -0.0619574687154276]', 0.99); -insert into credit_card_transaction (time, features, amount) values (157943, '[-1.00298903988335, 0.448279245453895, 0.363189892687094, -1.11913961953647, 0.0746410757383499, 0.826992641139967, 0.225250396939471, 0.195542176646352, -0.625701615546153, 0.337394336729985, -0.569133572553786, 0.854696114843583, 0.50249519787009, -0.451760679167143, -2.41962636921048, -2.35598949395017, 0.159175299773122, 1.26834020851451, 0.899528515021976, -0.601018693779465, -0.594246112531821, -1.02125511451609, -0.157182992757536, 0.0687629632825386, -0.106747921894385, -0.525962391932538, -0.62686298493347, -0.139486810535574]', 41.47); -insert into credit_card_transaction (time, features, amount) values (157943, '[2.035978765894, -0.139826291843228, -1.17926292158945, 0.477305222606146, -0.387395038548044, -1.56998722096494, 0.114790812664978, -0.292384620741237, 0.872958808373716, 0.0659956478481336, -0.61968559757071, -0.620533016547673, -2.03117327131294, 0.920031668107667, 0.979279036220855, -0.300974116881926, -0.181015951699458, -0.0409106241044171, -0.445349442843656, -0.396402736873195, 0.254432267665787, 0.810429066219608, 0.0825417299561031, 0.382230677710144, 0.129997938573399, -0.124265498722336, -0.0289474352578498, -0.0594777282540789]', 0.85); -insert into credit_card_transaction (time, features, amount) values (157944, '[0.788862936884592, -3.04565148607129, -3.39551671570616, -0.482369484962375, -0.598364148176146, -1.12007271042563, 1.3323266198968, -0.658607050266915, -1.19469017018191, 0.806631125091535, 0.29598207068673, -0.304522907977219, -1.19973554188776, 1.27035457607765, -0.725079557234848, -1.85953981174207, 0.0565422537298613, 1.28198069724227, -0.363226343805397, 0.862955734283772, 0.351615251353681, -0.16012130109623, -0.760862009229204, -0.284160038152073, 0.0445157762607068, 1.56899718527254, -0.322092913734916, 0.0135365458158489]', 767.42); -insert into credit_card_transaction (time, features, amount) values (157944, '[2.2247339060952, -0.547757734233112, -2.1876093230173, -1.08099387281946, 0.158683403528739, -1.28431658845409, 0.223246873894394, -0.494699838214966, -1.26559358718774, 1.08394834699888, 0.828088341186577, 0.108196337468257, 0.38558685467575, 0.480652090502045, -0.784113081226796, 0.724452198781507, -0.0539360213942737, -1.13092079134102, 1.08075385437449, 0.0269009463208562, 0.388756782939088, 1.08279926971293, -0.14894357746658, -0.330636426748462, 0.476938941938762, 0.0812935262547588, -0.0842732720707869, -0.0911736507510116]', 31.0); -insert into credit_card_transaction (time, features, amount) values (157944, '[0.105594051612785, 0.703077089454517, -0.328690847034737, -0.682187079645388, -0.0861378796993685, -0.313565198905864, -0.183136736480975, 0.628059185903104, 0.386986591404143, -1.29363774431946, 0.900224828758362, 0.763906945607677, -0.210321236195683, -0.893366097248943, -0.407593744762373, 1.02207620774566, 0.188579028076001, 0.964624918268661, -0.19199331175244, -0.24644719675246, -0.0603887340285353, -0.293264608984256, 0.406177975164764, 0.612798137079515, -1.20682641480751, -0.507086803089229, -0.0384005111085055, -0.005160054617516]', 19.95); -insert into credit_card_transaction (time, features, amount) values (157944, '[2.15882107276873, 0.0672033902971921, -2.46745610957006, -0.286997913078949, 0.987287139411816, -0.925136528460553, 0.704506589363936, -0.419539556456763, -0.0060082426456556, 0.188074732557845, 0.186389684363328, 0.679733592925371, 0.120249946394557, 0.925744525543234, -0.269037696793083, -0.158688303474839, -0.879099015902365, 0.261972000266964, 0.776480272122734, -0.190316941159601, 0.135103021022454, 0.513434259404438, -0.183041943693169, -0.973979138027272, 0.548543844484155, 0.283543870034046, -0.0905307651193947, -0.0987909008606479]', 4.99); -insert into credit_card_transaction (time, features, amount) values (157945, '[-0.378964511848937, 0.826849377938675, -0.683916714340929, -1.74996169087068, 1.85603070551767, -0.483341574532047, 1.66067194888337, -0.170798784997141, -0.778342401148782, -0.81830346188037, -0.011086770877865, -0.139930258272346, -0.971479480849696, 0.991653123975052, -0.829308733069321, -0.0352480560805225, -0.994535364203421, 0.354237020103077, -0.0074373443191504, -0.148893695903613, 0.231692108956715, 0.536859567938602, -0.475605140891819, -0.0847553463209244, 0.536033739382578, 0.130203141173249, 0.012593976191889, 0.102923599818434]', 24.6); -insert into credit_card_transaction (time, features, amount) values (157945, '[-0.313217955594952, 0.73747532025499, 0.45966750648723, -0.340453267413719, 0.59865698394617, -0.956593916617101, 1.02301582668903, -0.300513051001248, -0.274660334685805, -0.434122820760681, -1.04733175980579, 0.0321632366816343, 0.114594309367895, 0.172399395131392, -0.11931600673934, -0.294355305389584, -0.270491634474764, -0.610391943020039, 0.585111184091816, -0.109356005005994, -0.171266819503274, -0.443359488337592, 0.0140958882189854, -0.0958245961127248, -0.77365117926379, 0.179071073955915, 0.0205661989546631, 0.179934830072784]', 26.99); -insert into credit_card_transaction (time, features, amount) values (157946, '[2.10658433499552, -0.899434286531931, -0.877984999888084, -1.03265077789218, -0.724032863863578, -0.602462192582267, -0.672341208515899, -0.0639997775455318, -0.482232422855861, 0.919249073841108, 0.848649182371891, 0.0143281499956217, -0.21296551615959, 0.116553905808525, -0.305879426040542, 1.41899864306025, -0.149151050417918, -1.31387141470207, 1.0914498227478, 0.0055989985215785, -0.209032647860257, -0.774348324618356, 0.423174130265085, -0.466670353837505, -0.579679277076974, -0.684108963916703, -0.02103524243581, -0.0543011098382413]', 37.47); -insert into credit_card_transaction (time, features, amount) values (157947, '[-0.886016715016289, 1.37676521094932, -0.967773147172531, -0.904033884025408, 0.322858772695137, -0.269374647688134, 0.132164386455579, 0.902942499970542, -0.356171419899533, -0.38480796604756, -0.393523952634104, 0.764554241551988, 0.0866108210986516, 0.845551573490893, -1.02410273463972, 0.578351120336471, -0.640543464134904, 0.0717209241285589, 0.481366328710058, -0.0927208518264027, -0.21618508964891, -0.687300522260755, 0.0449990537437834, -1.1109991324545, -0.305811756696405, 0.198852461770972, 0.106255680250153, -0.0027566255583733]', 8.99); -insert into credit_card_transaction (time, features, amount) values (157947, '[-3.04079644739058, -2.236770540545, 0.221671143534587, -0.0223217929811176, 3.36689327699981, -1.52448292942163, 1.65245264676324, -1.70586190070209, 1.07314411119735, 0.794209586086788, -0.168956074815494, -0.547423030918042, 0.341870228912209, -2.44462270380443, 0.265810903169252, 0.133944121257501, -0.376111227882183, -0.436333649630669, 0.474778301666447, -2.0942098833859, -1.27759556866483, -0.195109489005391, 1.68154120174616, 0.557845376761826, -0.156721250881129, 0.078611137865343, -0.258690439189911, -1.45710991217636]', 1.79); -insert into credit_card_transaction (time, features, amount) values (157947, '[1.8607942516299, -0.540762000644369, -1.78069408615946, 0.476085632546504, 2.06270874946893, 4.16965961371771, -0.845752554916739, 1.07479626766169, 1.01977291183612, -0.0071215774271098, -0.541614402276259, 0.746139305286182, -0.370388649394624, -0.0621618996325374, -0.792569331100199, -0.494367868934238, -0.133430100928321, -0.999469298942959, 0.0775175617555802, -0.153063237494011, -0.507833864657946, -1.35164933118721, 0.450330143050021, 0.618057630715561, -0.293384462976927, -1.08770027024535, 0.0680708084576619, -0.0313558716761439]', 38.78); -insert into credit_card_transaction (time, features, amount) values (157948, '[2.09948742710708, -0.0723304809880295, -2.08452148675059, -0.520152956159295, 0.596570568297551, -1.02271375857664, 0.512217819561838, -0.354910915775572, -0.216298732010906, 0.271063819155825, 1.24351245140305, 0.984810216881798, 0.191941620162796, 0.817835823376287, -0.312040765470398, -0.160812422555638, -0.439005920473822, -0.458838054389003, 0.412550065172733, -0.125723367880543, 0.109359660080548, 0.348307565097075, 0.095340788520112, 0.815200867628221, 0.05608979192927, 1.29720130956434, -0.176303261775073, -0.0936412265282741]', 9.99); -insert into credit_card_transaction (time, features, amount) values (157948, '[-0.558444816726526, 1.02516109411149, 0.11585061564408, -0.641842931385032, 0.402920192709823, -1.03338293601231, 1.0365551230057, -0.314423869004569, 0.283755428912727, 0.113365296509313, -0.805620334099738, 0.16398871231851, 0.0857674997778871, -0.107885804488577, -0.308866429747884, -0.226577245095246, -0.419994285110336, -0.883678609482764, -0.133281839548949, -0.0958132770093471, -0.279363621856357, -0.493078638216871, 0.153631437270877, -0.0214390210804245, -0.54254505191404, 0.0544298113000256, -0.597997409807539, -0.406484082294649]', 9.99); -insert into credit_card_transaction (time, features, amount) values (157949, '[1.77401029907377, -0.964541473301166, 0.471470389702876, 1.66119607871983, -1.01594689476908, 1.26782693096402, -1.35782671154769, 0.408293153893965, 2.4357518644739, -0.221120039690317, -2.76182225870542, 0.63773448994186, 0.407277779996956, -1.36646829462487, -1.55528102709713, 0.0716012595424843, -0.433852312127229, 0.493508941781845, 0.216290013899203, -0.0823922580907394, 0.0957696866487306, 0.727275421454148, -0.0727016677224375, -0.965205684093965, -0.0064972251142685, -0.420145574707014, 0.122265786307215, -0.0150236979468001]', 79.5); -insert into credit_card_transaction (time, features, amount) values (157950, '[-0.423520377918017, 0.827751763827349, 0.948301268576159, -1.20928507363995, 0.320333829310019, -0.0444008599529219, 0.406071163704863, 0.0098824855236115, 0.712587553372695, -0.621659039475971, -1.74973522159798, 0.118643627542081, 0.764636326825837, -0.474384134273564, 0.320929265933132, 0.479082881079385, -1.01556340238723, 0.0956355901290112, 0.216058885550763, 0.0422920732578327, -0.236627688890152, -0.581785389944286, -0.161793290786122, -1.02145993254389, -0.0358385006275781, -0.220274198166634, -0.0008060829022785, 0.0663980010078263]', 0.77); -insert into credit_card_transaction (time, features, amount) values (157951, '[0.496037026629713, 0.65944480111628, 0.22668038602162, 1.05837304294214, -0.162900031337561, 0.631368206594806, -0.757595651202159, -1.48091716673546, -0.236372645807891, 0.522093629394776, 0.961021609917019, 0.337796040866756, -0.808596924758335, 0.803022455486797, 1.03773155684655, -0.780132452285254, 0.547147392967212, 0.438028027649036, 2.62941151283694, -0.131786434079001, 1.55033451739738, -0.349278574787741, -0.0217778029763379, 0.740996443917679, 0.274548606407144, 0.915365458808303, 0.233196440969967, 0.260685844148833]', 1.18); -insert into credit_card_transaction (time, features, amount) values (157952, '[0.0417179464419774, 0.991665811300549, -0.315676676833978, -0.589918417564405, 1.1703273966254, -0.748375845411038, 1.07304584806938, -0.169409496686861, -0.274405125870813, -1.0565427914781, -0.735179755577625, -0.0076517233896974, 0.25461376404232, -0.959900525227061, -0.32929290469182, 0.198832868820715, 0.385929856406705, -0.266800149892477, -0.190746182526778, -0.10223891267919, -0.194172906451181, -0.488502645051792, 0.158430944209381, 0.517364179975973, -1.17174831563608, -0.0031964190520522, 0.193261241754412, 0.267822880912105]', 1.98); -insert into credit_card_transaction (time, features, amount) values (157953, '[2.06406885643051, 0.150635885588552, -1.69463987261267, 0.415752070586226, 0.436513734322261, -0.809437405166869, 0.172824186322012, -0.213169110813451, 0.488478219491403, -0.387253513691962, -0.493541758935384, 0.182626731862901, 0.0717446747231818, -0.840345400819593, 0.249198566229186, 0.250233971047554, 0.537588965601217, -0.406189945618679, -0.0281015722307661, -0.161561069166073, -0.356875855235615, -0.915522786246576, 0.349077140294913, 0.508984914116324, -0.275886527740446, 0.175238875565013, -0.0594325449988992, -0.030700485371586]', 1.98); -insert into credit_card_transaction (time, features, amount) values (157953, '[0.714940572199113, -3.14668263623907, -1.34434487660362, 0.217049747142615, -1.56005133529814, 0.301011658371958, -0.0488747600953272, 0.0439457304276764, 0.126920201375223, 0.465165165688082, 0.321647290647655, 0.194042720768047, -1.35226912355935, 0.463556765165538, -0.545864333206366, -1.35919762120867, -0.107326869757454, 1.50118620927985, -0.496764743355698, 0.687190515731497, -0.302260626621068, -1.92147881169346, -0.0435509802378034, 0.590779965747819, -0.870201179977087, -0.867746078635534, -0.0945408734752064, 0.0839035920931845]', 700.4); -insert into credit_card_transaction (time, features, amount) values (157954, '[0.155418307516308, 1.06706434723187, -0.48481027577188, -0.595345271948942, 1.05995324376574, -0.69618112444826, 1.02719156490691, -0.134820316180118, -0.130704881846982, -0.855978375030594, -0.773260307513653, 0.148318348731243, 0.679235782005899, -1.15591524647323, -0.293050756379983, 0.36486091932644, 0.252055949109237, -0.222681079951058, -0.153624697657035, 0.0749240763359663, -0.346126150982433, -0.8223736016878, 0.0705879624032651, 0.386768119529358, -0.369429957544625, 0.123170670555335, 0.224879452927972, 0.083855946494632]', 6.99); -insert into credit_card_transaction (time, features, amount) values (157954, '[-0.196502757734091, 1.16898915678427, -0.849597998100463, -0.509445858436981, 0.511294864528861, -0.85440863008484, 0.541155971886057, 0.399803873512338, -0.0019894003933657, -1.02216226206132, -1.17181220211394, -0.726982413256965, -1.28736000983712, -0.433439881473624, -0.118932597226696, 0.551503052980536, 0.572686532779407, 0.0294828625111178, -0.184168060374324, -0.160983721261086, -0.318611723597352, -0.979355941307693, 0.168741647778025, 0.433562019792299, -0.425941530016154, 0.125898939143942, 0.0856947812225393, 0.0178164666878912]', 10.06); -insert into credit_card_transaction (time, features, amount) values (157956, '[0.0377772803522289, 0.70962218327457, 0.0864418754332224, -0.800975092723469, 0.569095784878146, -0.557730600670006, 0.809661890486252, 0.0474975725899271, -0.064148208054088, -0.201476531495999, 0.112354291836447, -0.0606904076434513, -1.36287933759573, 0.553051806748703, -0.918675698124949, 0.306447342008864, -0.754113779140093, 0.0703189871241063, 0.394262288253137, -0.101714862122309, -0.256401448714307, -0.65855332774574, 0.0135945303269278, -0.537100794157204, -0.502521253462503, 0.154945695942483, 0.233993514328793, 0.0793502352551405]', 3.08); -insert into credit_card_transaction (time, features, amount) values (157957, '[-1.1533613827591, 0.932987101708906, 0.48045437717297, -0.870664604454561, 1.15532047793528, -0.272714026478393, 0.653182141793233, 0.341698259938587, -0.832812406520562, -1.1585319859078, -1.60089314184079, 0.237305386151913, 0.896601615072072, 0.30486901147015, -0.198617376087359, 0.160272508089655, -0.431583996078477, -0.464461210289953, 0.497237386445524, 0.035288240450506, -0.258800648208347, -1.00785916201138, -0.330518975359237, 0.0592199476554792, 0.652257881414818, 0.33950538883829, -0.162612073504426, -0.0771535111803993]', 3.59); -insert into credit_card_transaction (time, features, amount) values (157957, '[2.2180935602798, -1.53035153822686, -0.317128421398909, -1.46561865678762, -1.75563905259586, -0.690470025280416, -1.40183164203569, -0.13050866808013, -0.892344207175662, 1.5027266981516, -1.10143004252507, -0.737906927183276, 0.53962226703376, -0.649660619510824, 0.0979250652193446, -0.17015743484654, 0.313826503900443, -0.0380751087507016, -0.133131167343187, -0.377336804247485, -0.351987830707386, -0.578394351495228, 0.405817594026537, -0.173512728876944, -0.591207504129514, -0.438335884957371, 0.0377173735606557, -0.0308092751175787]', 40.0); -insert into credit_card_transaction (time, features, amount) values (157957, '[-1.82881711077753, 2.11312659067489, -2.70565184336161, -0.54153561530829, 0.731924581784389, -0.374141156646679, 0.15535042930654, 0.927011680491262, -0.527661852500732, -0.551689726799319, -0.107838426519603, 0.934997174046555, 0.567860760022302, -0.265117418402258, -1.45447591940341, 0.591221396737469, 0.564917860578733, 0.658942234065919, 0.383968912323998, -0.478203261993345, 0.130738242552403, -0.018221886983316, -0.0412233035868095, -0.199102001432269, 0.132807536795369, 0.240592185964573, -1.07951201656348, -0.106838544641023]', 14.95); -insert into credit_card_transaction (time, features, amount) values (157958, '[-4.02657350667, -5.13560536462968, -1.5864618861875, 0.0941755372627182, 0.974338929331611, -0.966912739299797, 0.244006478523435, 0.528526651618191, -1.30475522306458, -0.0350002009185114, -0.385043193117391, -0.0986670882749319, -0.317581123418741, 1.07350350509879, -0.195445048951483, -1.34910305933578, -0.329507186230072, 2.77815919288323, -0.354919578805565, 1.95093331041854, 0.780323837289314, 0.346111473754255, 1.22927528135008, 0.14114986513322, -0.151104723972879, -0.12736453442329, 0.199719304433133, -0.342208805188658]', 622.0); -insert into credit_card_transaction (time, features, amount) values (157959, '[1.91953715091255, -0.471146959708282, -0.320106645181777, 0.601097301413454, -0.835077125809063, -0.734047928354754, -0.518424895960432, -0.0957156123104241, 1.07059091091028, 0.0907413834248014, -0.868845943698411, 0.100389024563389, -0.106496000192297, 0.0037175001806409, 0.885153359040691, 0.529199247915558, -0.656365226688879, 0.16559305974002, -0.486358526050581, -0.136452357176805, 0.210846112570921, 0.652100651225731, 0.148336542646621, 0.0772832556770693, -0.312687840507206, 0.350449133613549, -0.0224136341360181, -0.0373309252509812]', 47.2); -insert into credit_card_transaction (time, features, amount) values (157960, '[-0.0167360401228981, 0.810738944735208, 0.355029310089414, -0.676065908875726, 0.440162878835009, -0.857533498939415, 0.916115351854028, -0.0736257898032115, -0.330993667562361, -0.246225940656005, 0.879015707445893, 0.932000089670094, 0.0494625929457461, 0.28743401008896, -1.12529848962295, 0.069387888935094, -0.633282014640039, -0.2996954348626, 0.169315109125263, -0.0152865792779525, -0.19921028254512, -0.431198443657408, 0.0727977822393492, 0.0741640559815099, -0.525949544897579, 0.114541701178317, 0.243445371604533, 0.0888514582812736]', 4.49); -insert into credit_card_transaction (time, features, amount) values (157961, '[-0.288430758943871, 1.1761184792378, -0.274976284059367, -0.487969202837398, 0.380054252799194, -0.996524999108536, 0.820886750713392, -0.0051980429193138, -0.266273361801721, -0.0182501803760934, 0.832854461447437, 0.846211681348274, 0.13430664824925, 0.659119532552508, -0.276540910368016, -0.25971396022472, -0.642692576994192, 0.602162377021445, 0.292947547820401, -0.0566156281481256, 0.438254825919884, 1.50260277667472, -0.121849611352243, 0.121701483434302, -0.842153712356565, -0.28523646197213, 0.0813906084501782, 0.0266894948907803]', 0.99); -insert into credit_card_transaction (time, features, amount) values (157961, '[2.09290324082901, -0.730497275359928, -1.50531614308008, -0.543655223555024, -0.269337145186302, -0.592985462827389, -0.214852690835621, -0.165536108814146, -0.817315789199893, 1.06254472540993, 0.63240086627314, 0.433252390015448, -0.338430538278368, 0.527780119751134, -0.801942781219551, -1.52964058287603, -0.186471294628776, 1.07624581460366, -0.309604254558122, -0.554345241954112, -0.465126769858491, -0.900308326429664, 0.310258392841819, 0.739283254021243, -0.262809067567763, 0.423214015772059, -0.0867210143559728, -0.0628768414644909]', 37.9); -insert into credit_card_transaction (time, features, amount) values (157961, '[1.98651690903976, -0.139920953249173, -1.22167073159544, 1.10572731956866, 0.380750700607988, 0.159883856957599, -0.121391119674426, -0.0291394706956704, 0.740649189462826, 0.204143454685721, -1.67260884543788, -0.0618065517600697, -0.033166497338101, 0.145482938341498, 0.326379639625274, 0.183373879224712, -0.756952042628467, 0.201046317285517, -0.284100890073826, -0.173399734571779, 0.120733016065792, 0.449711546875324, 0.0016483431145024, -0.0361652431070216, 0.227096215905187, -0.511281513506969, 0.0211840151418421, -0.0386575984148595]', 35.0); -insert into credit_card_transaction (time, features, amount) values (157963, '[1.92586657185659, -0.529199897825763, -0.145934081922038, 0.407499013270104, -0.852249746320965, -0.253461272631271, -0.783740706196698, 0.121908830054698, 1.04034849985091, 0.175818146958595, 0.796274705305816, 0.950495068631654, -0.235514764413005, 0.0016555673681376, -0.282653116738965, 0.441921138342107, -0.628450030537591, 0.405241925340875, -0.0812666600594878, -0.204680555384908, 0.222583252125102, 0.820361857130425, 0.171198417328548, 0.0689213514847251, -0.348035094063476, 0.549624539782602, -0.0217428139219938, -0.0568157268137682]', 11.5); -insert into credit_card_transaction (time, features, amount) values (157963, '[2.00197341186197, -0.0172307442925724, -1.64275823561917, 0.366268061048751, 0.2460725341237, -0.694711143797763, 0.0003371610921903, -0.0222232977718137, 0.437400782277952, -0.171344087115177, 1.05423556519694, 0.135601556549636, -1.4841644910807, -0.349638172636094, -0.35073834928678, 0.526481044376817, 0.372939483126367, 0.303997272438385, 0.278840412835517, -0.225970933401332, -0.312090235909307, -0.919347783750754, 0.360463204469865, 0.631000460271372, -0.364709537527224, 0.146498823595791, -0.0771886251285026, -0.0403737012696964]', 9.99); -insert into credit_card_transaction (time, features, amount) values (157963, '[2.23329085170967, -1.69144957004062, -2.38852352523168, -2.57775518701614, 1.14357603227901, 3.44120524904476, -1.59216074049841, 0.792470005925062, -1.40654405612625, 1.58306681617355, -0.219105097606072, -0.72149325368834, 0.319497581175934, -0.199904882985085, 0.190592814035743, -0.705043949806864, 0.436370723424263, -0.28236885133011, -0.267079526457362, -0.307510351202207, -0.153174906605346, -0.0879417958761224, 0.24366905982448, 0.688319130860332, -0.138447433908701, -0.143262405698555, 0.0234166575446106, -0.0528976874640977]', 42.76); -insert into credit_card_transaction (time, features, amount) values (157963, '[2.06061496870053, -0.156284236095448, -1.09142693069339, 0.432823959876221, -0.279383963397353, -1.25404226803778, 0.0852441350379371, -0.25214813872615, 0.763319834145534, 0.116429050236064, -0.983629963233745, -0.339603746069908, -1.40631030793986, 0.568260569676601, 0.200508182190628, -0.0389018268800775, -0.200895232220781, -0.692343078840531, 0.152684097703824, -0.318815489395252, -0.305571856906352, -0.817737025228242, 0.358951935447387, 0.0060955699278855, -0.349269370803907, 0.200305251874558, -0.0803223083692439, -0.0630019181852741]', 1.79); -insert into credit_card_transaction (time, features, amount) values (157964, '[-0.683709087568444, 0.296121340959055, -0.0955113777931461, -1.2550420516208, 1.7980614388856, -1.07052776194258, 0.723038003165735, 0.0645548494880929, -0.304991075599898, -1.19891304728934, 0.864145353564404, 0.429619901952159, -0.673540572341577, -0.668428435856265, -1.6088100795599, 0.828233303204743, -0.190711716532288, 0.137954253253225, -1.12743751521058, -0.132893476778697, -0.117760205640354, -0.584333003295222, 0.20305618239447, 0.58267152100511, -0.875528720569339, -0.18218308689086, 0.103228961477245, 0.236036279574716]', 0.99); -insert into credit_card_transaction (time, features, amount) values (157964, '[2.02130568911583, -0.106107709910944, -1.20084756442329, 0.281772587584589, -0.121881897198451, -1.07775051389328, 0.0659526767523445, -0.21512396209524, 0.457800287680914, 0.1975893681161, 0.98272077547269, 0.708544100597675, -0.602225929242185, 0.774793782408752, 0.131014026070172, 0.0167631777718205, -0.70461822143883, 0.513506321629264, 0.0033918647002959, -0.274404744385071, 0.310342814099609, 1.00210823602601, 0.0063998012102415, 0.0998522895067879, 0.191878317094332, -0.125108888402155, -0.0235576952413561, -0.0680629970014868]', 1.0); -insert into credit_card_transaction (time, features, amount) values (157964, '[1.94366108760167, 0.0703544863727542, -0.234683907704162, 1.213840155994, -0.273840024699517, -0.388226066015964, -0.298323876406533, -0.0644292612161531, 0.484245148416809, 0.185720446368747, -0.192325810062568, 0.973848991690798, 1.1882329159931, -0.0289223593377505, 0.947882302676674, 0.410145675180216, -0.755566268841414, -0.338091720088588, -0.824687681992613, -0.167115353117578, -0.102525251501971, -0.232049153569127, 0.459978035806903, 1.03121996132453, -0.423349117850612, -1.0229454956366, 0.0552017116479644, -0.0114940290400824]', 5.0); -insert into credit_card_transaction (time, features, amount) values (157965, '[0.0618368245157055, 0.4483744464616, 1.04829352135028, -0.689349628492269, 0.027718517500259, -0.139601613945344, 0.256464318295674, 0.176728361401527, 0.386447036418693, -0.666355004345517, 0.340995702239376, 0.60887760941688, -0.346742018435612, 0.0838006682968273, -0.438480531382187, 0.518189637476565, -1.00700481776491, 0.577671547860521, 0.139963577180324, -0.188523239862826, -0.0775430078048705, -0.188789911476871, 0.0603362051988515, -0.394962657111677, -0.679616108439498, -0.695610561994836, 0.102996861051272, 0.0501463876644225]', 6.95); -insert into credit_card_transaction (time, features, amount) values (157966, '[-8.20861607046392, 7.37438337575459, -7.27861422233577, -1.58731815284072, -2.54426450916029, -1.46068366809213, -2.4790315506221, 4.01317742453994, 1.67973389284029, 3.77148782546918, -0.208375008960203, 1.8976982770544, 0.349559564072912, 1.10875832305077, -0.679037513878558, 1.18956829557332, 1.49829449008219, 0.584858967553385, -0.657513312446231, 1.1255989106054, 0.0926279389971332, -0.322768577173434, 0.729107876787504, 0.0616525555932529, 0.431632698418571, 0.315398120433926, -0.317099171194962, 0.671549730637482]', 9.91); -insert into credit_card_transaction (time, features, amount) values (157966, '[2.03074519293165, -1.82421226674575, -0.842893521766777, -1.3591542575788, -1.64055091794564, -0.816550692012471, -1.0341445802757, -0.228270578295579, -1.03773647555012, 1.50661258175424, -1.10245293885893, -0.999853874525731, 0.0215815259605607, -0.378167815989221, -0.0100557220788719, -0.504199364188025, 0.532745175777142, -0.0375582081079738, -0.271434439059458, -0.211618019875968, -0.0841071148614551, -0.0147917204142604, 0.144424327215541, -0.109219880883719, -0.321099113164504, -0.182734933012596, -0.0028958577287816, -0.0261790050932126]', 144.4); -insert into credit_card_transaction (time, features, amount) values (157967, '[-1.3784842823001, 0.836253834230049, 1.63717317041987, -1.20071491939441, 0.527644893227459, -1.77579789806105, 1.25498691340279, -0.354095145686627, -0.402204168980457, -1.02661290907531, -0.153022395433992, 0.446609211033295, -0.0676794093851433, 0.0222814426804166, -0.969469984042873, 0.0121020226413929, -0.43827060073736, -1.19207121977984, -1.32918557326521, -0.322040407099317, -0.22033880481446, -0.734717698602182, -0.180544963056035, 0.932185359473056, 0.535121113540519, 0.0597898872485901, -0.333964586234657, 0.0766867456795813]', 1.98); -insert into credit_card_transaction (time, features, amount) values (157968, '[-1.24653037207163, 1.61597217458955, 0.244828166687947, -1.13583620890234, 0.290600998547033, -0.277997466652597, 0.208438651740384, 0.437277772839563, 0.553872373950403, -1.11505185844921, -1.53997855517165, 0.0067690854324076, 0.740821830023757, -1.42730475314938, 0.413160125865243, 0.91978816710802, 0.115996718396721, 0.686650231945729, 0.0798408241824044, 0.12430123539442, -0.264394638855443, -0.719232726523527, -0.166029629130133, -1.12622350196224, -0.0570402514170426, -0.217427583838379, 0.0832706734666287, 0.209989193164973]', 0.77); -insert into credit_card_transaction (time, features, amount) values (157968, '[-2.80578689320652, 3.18227555712877, -1.47614502675081, -1.53660983322885, 0.141134715594812, -2.47624569791166, 1.81158285016513, -0.621928403233836, 2.20391805995782, 3.65204796924155, 0.156020230434811, -0.219408848739783, -1.21050585621525, -0.225078188425433, 0.0632504934267331, -1.27663910838827, -0.278438787722901, -0.82023297146283, -0.825669868224927, 1.50573809404652, -0.168393595649716, 0.832980234782807, -0.0583151977485832, 0.91537600363269, 0.107405315360701, 0.0036909808853293, 1.02379478709671, 0.292022168207763]', 0.77); -insert into credit_card_transaction (time, features, amount) values (157968, '[-3.32785612880507, 3.74284510416295, 0.102989548867145, 3.97111691483664, -0.728104965006497, 3.79597592120345, -4.29874619762918, -8.58254391834837, -1.09042034656957, 1.79912538035272, -0.594137276188149, -0.15270804588819, -1.85649493482985, 1.23201794284558, 0.0014976443443785, 0.765055733710974, 0.656003827904937, 1.53779613655893, 1.07317578536359, -0.704741094863117, 5.90984726047862, -1.15689810111434, 1.16848513492925, 0.0298377843500019, -0.567869957203603, 0.468192279368689, 0.484931877775832, 0.294533594512196]', 7.75); -insert into credit_card_transaction (time, features, amount) values (157969, '[1.83888766917069, -0.0764128151632818, -1.91552240711566, 0.589749279799011, 0.251281511505436, -1.30615268042018, 0.474585548635681, -0.441664967747948, 0.447131099126887, -0.601661262658675, -0.253432849433953, 0.493651440339286, 0.930408803323264, -0.988655718223063, 0.883178547264484, 0.19578891969138, 0.42828357631568, 0.420531050493752, -0.503419484606504, 0.0850612676544874, 0.264941175479925, 0.734106150308426, -0.145991179407594, -0.212111048038646, 0.247577074495147, -0.103751822866401, -0.0138415781165598, -0.0091967906204431]', 117.54); -insert into credit_card_transaction (time, features, amount) values (157970, '[1.99906299189977, -0.182999504001492, -1.41118544263677, 0.17129269748625, 0.142312903104677, -0.618549789669981, 0.0267283870013384, -0.115330456666407, 0.45939086039813, 0.183746195372574, 0.898934223749624, 0.604277946676819, -0.650047353156469, 0.777154534868393, 0.191082906139417, 0.0677892325764169, -0.74495448692418, 0.582858789277018, 0.0318782089146311, -0.208511029149992, 0.311209711200393, 0.917589129172324, 0.019334819169837, 0.783955514081871, 0.208751363202549, -0.157442177067247, -0.0327373137870122, -0.0584867539139868]', 19.95); -insert into credit_card_transaction (time, features, amount) values (157973, '[-0.964697326988786, 1.41240741952048, 0.317744622511331, 0.640112635012405, 0.672533533660404, 0.0832064691547012, 0.922455970627585, -0.103988682643461, 0.577303800940569, 1.43903553953967, 0.0691064181769118, 0.194498194238766, -0.652630172459585, -0.217654061733043, -0.993600691000042, -0.800421001015771, -0.448893335028301, 0.601793037202568, 1.1559904295511, 0.523317155288731, -0.0308664772711088, 0.888297928902851, -0.366397755654084, -0.489442935283755, 0.0277478050148657, -0.401094304549291, 0.17191200784603, -0.297837266020055]', 5.0); -insert into credit_card_transaction (time, features, amount) values (157973, '[0.896239917425564, -2.3524076549197, -1.09776116060742, -0.0536660982707264, -1.32098979070395, -0.512299813897651, 0.277680586957759, -0.169480141078878, 1.11208257624998, -0.471410049385005, 0.982463638752311, 1.4066503863401, 0.0216716761713687, 0.191734635425939, -0.595136392285055, 0.129464931569796, -0.32928816935155, -0.313355953724628, 0.691015763444587, 0.974251588541518, -0.0388073775816171, -1.2387639008878, -0.0153672863972512, 0.112121008085071, -0.916627170365452, 0.308516962925865, -0.17090229324426, 0.0369939533772284]', 572.84); -insert into credit_card_transaction (time, features, amount) values (157973, '[-0.773645036856289, 1.14187839052569, -2.00940865038627, -1.87509281164653, 3.26322750501122, 3.13732617124828, 0.876309721177129, 0.375236025125563, 0.765330580112903, 0.921625349615873, 0.48336524899578, -0.597282768638666, -0.369529597464575, -1.48685750262332, 0.870007051112978, 0.117532727040786, -0.0878655833859465, 0.192498517247581, -0.899118828825117, 0.552102325160159, -0.0467304889982395, 0.724459417401952, -0.192384890643433, 0.651937193841015, -0.270887785737543, 0.493429791322173, -0.25446941428238, -0.599785151013234]', 28.21); -insert into credit_card_transaction (time, features, amount) values (157973, '[2.03536273310752, -0.943719083880541, -1.6543236034095, -1.12360489314325, -0.369077687889891, -0.896880377877599, -0.0853312327084431, -0.306179388538316, -0.967566809935332, 0.998889513736933, 0.666910758864691, 0.549159250972956, 0.385138853715296, 0.546512740454178, -0.18584784488346, -1.20046824382935, -0.422553520043883, 0.980838397363322, -0.212556147899113, -0.385025651205767, -0.473531876927823, -1.07479683932682, 0.25306790939445, -0.455122496945265, -0.48017084553966, 1.02592757733018, -0.137747205086342, -0.070883453110854]', 93.0); -insert into credit_card_transaction (time, features, amount) values (157973, '[-1.2231026879908, 0.747107929053307, 2.80214650407355, 3.99627623969058, 0.357225429950914, 2.1725594914511, -0.390945064091217, 0.405706616293283, -0.491755197952139, 2.65627889114601, -0.186385481189706, -1.60905952691208, -1.63618327367767, -0.484837363250144, 1.12867741863864, 0.878685400346291, -0.749518725460162, 1.79198609953037, 1.39285173420542, 0.50646901803822, -0.0762430850511763, 0.611070310549528, -0.0051384965965158, 0.0282711363089857, 0.152619508290004, 0.531819296340161, 0.391843575457727, -0.233223744015689]', 18.15); -insert into credit_card_transaction (time, features, amount) values (157974, '[-0.688928417572515, 0.52099901390324, 0.395567619354803, -0.331300364117948, 1.39362783310617, -0.622543630287477, 0.565202622116405, 0.130661531453614, -0.61684536408945, -0.878499649972557, 0.997136930107938, -0.085437364104921, -1.21433079640909, -0.449622649788725, -0.404991670390028, 0.208240284489818, 0.549792261933334, 0.413011220123731, 0.658948663453891, 0.0734436553507974, -0.205214246670432, -0.793544288280305, -0.0106664964016563, 0.588260901841865, -0.245564503640268, 0.243204839633853, 0.0371667603083719, 0.146906238567966]', 1.98); -insert into credit_card_transaction (time, features, amount) values (157976, '[2.05255860617653, -0.146485271082057, -1.0913619403651, 0.42071438983787, -0.220140821205693, -1.15780974843096, 0.0882087281638287, -0.240576393541553, 0.722779520810923, 0.100263684633919, -0.89650587948911, -0.183419730186877, -1.1470175075041, 0.521322003721498, 0.219834254413959, -0.107161052887001, -0.1627070902376, -0.810463735724028, 0.0823848993445511, -0.308032392564461, -0.297267263550314, -0.774323258046133, 0.361433079793668, -0.0462712968190888, -0.354289984806009, 0.205107453833142, -0.0754499268192803, -0.0630899877827614]', 1.79); -insert into credit_card_transaction (time, features, amount) values (157976, '[1.95902759736612, -0.133766212551384, -0.869202857218849, 0.111340955201187, -0.157188983100066, -0.197125238276084, -0.57122878629977, 0.13833885858806, 0.949763158412604, -0.563658170249103, 1.40817056111254, 1.15663758717233, 0.321211851585402, -1.17641972357424, 0.153946566289958, 0.747368682978227, 0.220958829236181, 0.652766691730082, 0.0670623633560689, -0.129696144276046, -0.20708937652179, -0.471969775505063, 0.403459391312989, 0.581554287808335, -0.516300356368384, -0.334192792482777, 0.0221991324256461, -0.0126040789588429]', 0.77); -insert into credit_card_transaction (time, features, amount) values (157976, '[-2.66383205908704, 0.174647496723316, 2.11245438541986, 4.51203303814439, -0.520728886617058, 1.50565562360304, 0.0284231801402955, 0.368720147951529, -0.260473043667203, 2.52234222111307, -1.76959789252947, -1.66403270284149, -0.564439767882363, -0.661016234185204, 1.78953233942014, 0.869842683271945, -0.42794206813361, 1.1634192063946, 1.13521894413423, -0.0003465240527101, -0.322476804007028, 0.670453093518308, 0.502409326852764, -0.601407228296095, 0.428083813958631, 0.628127647596657, 0.661857951182953, -0.275026934135342]', 174.6); -insert into credit_card_transaction (time, features, amount) values (157977, '[-0.745517800584415, 1.48280525331285, -0.987091490228214, -1.28141472894969, 0.865436916990643, -1.5046921777319, 1.4916251334676, -0.235320134468621, 0.103140170151472, 0.647988136404069, 0.757329236023915, -0.0617024406297733, -1.71943731917457, 0.984448574219067, -0.649475788029373, -0.555900121039057, -0.513164650122404, 0.109545096436624, -0.0303703315452879, 0.218888229966171, 0.203379493314934, 0.906310389151378, -0.182634344980966, 0.0231816475550947, -0.253059055927106, 0.0871014257313095, 0.700361236264471, 0.442312947133834]', 0.15); -insert into credit_card_transaction (time, features, amount) values (157977, '[-2.99301653467851, 2.29490924392415, 1.98918498607115, 3.83052499870375, -0.392687116576311, 1.50772049443603, -0.483294472730568, 0.434057551711502, 0.924517829992485, 4.62133005658301, -1.22245913790506, -1.47827211800812, -0.622738361654396, -1.21444578536491, 1.80050666717643, 0.415035298030138, -0.383297527182699, 0.900670634466261, 0.687245561219846, 1.38455285253594, -0.310005896107521, 0.425598385458306, -0.531065600998571, 0.543647263529668, 0.36163297872204, 0.499858522066366, 0.789114965482187, 0.0533434675252892]', 18.15); -insert into credit_card_transaction (time, features, amount) values (157977, '[-0.661593711822078, 0.652793423475606, 0.7673908873748, -0.606041778157361, 1.40739059966363, -1.2556945740505, 1.96355758509504, -2.56736973479898, 1.16884489287547, 0.677118757460659, 0.406201562086944, -0.896522251183681, -0.841017956570658, -2.16037242723745, 0.816026837925164, -0.43528020829686, -0.0744183346191714, 0.26507889395609, -0.810455329336394, -0.129294645207376, 0.73764004562111, 0.681506765324107, -0.314302195307962, 0.302195877665518, 0.281824623896969, -0.339694125950599, -1.70572620737748, -1.09107489559943]', 8.6); -insert into credit_card_transaction (time, features, amount) values (157977, '[-0.890719367974028, 1.37084429023364, -0.296669602897132, -0.993757582426446, 0.368370299867898, -0.686870802913754, 0.766229821121271, 0.0826705604294247, 0.685632517923621, 1.03123275061918, 0.290090616041666, 0.0360645619834591, -1.31930082364347, 0.264438250192654, -0.86949543196661, 0.195337341623331, -0.7624748554422, -0.0283915466226299, 0.242330694479769, 0.410957294622519, -0.371288575262615, -0.724627709889998, 0.0907228117984926, -0.466184143965356, -0.29930323241112, 0.16799226402743, 0.64474601567674, 0.349551873089223]', 2.69); -insert into credit_card_transaction (time, features, amount) values (157977, '[1.82325499195386, 0.19622352726535, -0.11935235707208, 3.62620320746891, 0.573495535185885, 1.79681532344717, -0.501690293526354, 0.394622872162896, -0.459783497446441, 1.29552595513065, -0.446200520787739, 1.36698946963065, 1.51713164579045, -0.672789746850831, -2.62440287030406, 1.00977208070635, -1.05743830841439, -0.272972863692372, -0.521953696778539, -0.106977397750954, -0.369304399521242, -0.889433673932337, 0.327546338061398, -0.248345886348167, -0.348104763227304, -0.412827696020526, 0.0207524755124976, -0.0336437819003199]', 26.47); -insert into credit_card_transaction (time, features, amount) values (157978, '[-0.666067754181954, 1.18819791522328, 0.311066547972187, -0.374017287707467, 0.516044074148531, -0.301691499397619, 0.80992397446302, 0.0482586188691898, -0.156417828254936, 0.28019302509348, 0.6889287221767, -0.406857836977868, -1.70949565200734, 0.847823975052059, 0.58010173439227, -0.436476684801031, -0.319676483862379, 0.929367317338259, 1.07758737140569, 0.191012188096813, 0.277619435010711, 0.90350995007144, -0.375449413549478, 0.7636470160597, 0.316156815676701, 0.0403100759112164, 0.318440459403248, 0.241267991241012]', 7.99); -insert into credit_card_transaction (time, features, amount) values (157978, '[-0.462584526224977, 0.74980261149664, -1.01508453157879, 0.0471592679975883, 0.918350659413925, -0.846999475015829, 0.4355738020709, 0.16722179466712, -0.0780622681935225, -1.08599154534344, -1.43280450884057, 0.250677382842168, 0.782693910771039, -1.1629523038356, -0.76490146091398, -0.0950920608610851, 0.89819571376613, 0.239944985049191, 0.386180275585579, -0.161847461013527, 0.202388589803986, 0.753929336889137, -0.0711966664037763, -0.635777010745609, -0.287629921024362, 0.704843516717623, -0.140024528649442, 0.0270525121906098]', 5.69); -insert into credit_card_transaction (time, features, amount) values (157979, '[2.00442329518763, -0.103757942606191, -2.02206078759673, 0.493910355657102, 0.296153933286302, -1.53910924067694, 0.740031396852728, -0.486761152292382, 0.535016096820338, 0.001424060861036, -0.985286725012501, -0.213285685381904, -1.32059707246963, 0.89669973267183, -0.0331943825254506, -0.894092036588173, 0.0210925545466615, -0.516037751233991, 0.0806101229238717, -0.258495155808709, 0.152985422284505, 0.529285106239231, -0.0977007381687096, 0.0091200322090107, 0.490317362656807, -0.0664916908535324, -0.0655705897062785, -0.0693121098769825]', 50.0); -insert into credit_card_transaction (time, features, amount) values (157979, '[1.92666675492338, -0.195582478342884, -1.17826293848127, 0.238473764956231, 0.508606096230559, 0.350103136695233, -0.0557243483417705, 0.107414532314776, 0.505204557931785, -0.0752001297508144, -0.0480394705165628, 0.794246688733908, 0.199331878944025, 0.326012387379621, 0.840272043822156, -1.04344266830493, 0.580458884801121, -2.32702012885371, -0.992919471009605, -0.312833762940224, -0.201582849105492, -0.324585991677623, 0.425714010351967, -1.02830422375004, -0.519722026843405, 0.295394250122115, -0.0162052054596585, -0.0711881360104416]', 1.98); -insert into credit_card_transaction (time, features, amount) values (157980, '[-1.61297691007714, 1.56104420562487, 0.748769507905986, -0.993980974046741, 0.126316760589102, 0.283377439558068, -0.162779713371469, -0.913077734158791, -0.458297961851394, -0.627040582657327, 0.46751348110298, 0.312584660367103, -1.09484198169594, 0.758289258416562, -0.617531945652306, 0.287521058187268, -0.223316086482307, 0.29843455483529, 0.131557372829886, -0.391505520770848, 1.53766889713242, -0.57742478319376, -0.118904999269506, 0.686669242741798, 0.679646169784791, 0.557079975757182, 0.145107669563328, 0.051238754166178]', 3.59); -insert into credit_card_transaction (time, features, amount) values (157980, '[1.94575336427642, -1.10746611063971, -0.38161402794336, -0.266821928565136, -1.32610828898409, -0.749354577663574, -0.745672665731535, -0.144669424091363, -0.0129254778314704, 0.773805669913565, -1.01501425895293, 0.0320462724809392, 0.260857726053945, -0.15606274123976, 0.388821724832114, -1.07884244765892, -0.224936714930868, 0.871012493723425, -0.827646651099286, -0.430201391688745, -0.573028442322667, -1.28328081191214, 0.45058302463433, -0.0089054532443411, -0.796885802682008, 0.222794674116536, -0.0309006496499365, -0.0237329612937065]', 96.0); -insert into credit_card_transaction (time, features, amount) values (157980, '[1.87184930947792, -0.259915838579638, -2.54104246908075, 0.177836240074178, 0.778426112810201, -0.535660063214527, 0.419192095398964, -0.178353484440636, 0.132389738566986, -0.221411927841926, 0.634866606916482, 0.0643745711098544, -0.727815293623028, -0.437267074972362, -0.203059123566239, 0.61236047665476, 0.198037174417328, 0.768672979084602, 0.323025256602774, 0.0734234274875721, 0.0562649931241585, -0.0553143629936731, -0.0853529003818045, 0.0359183772413752, 0.0954082854036176, 0.671223854473251, -0.124085298098747, -0.0384948090849152]', 122.0); -insert into credit_card_transaction (time, features, amount) values (157980, '[-0.297038125811158, 0.762610438101774, 0.730339890280684, -0.596276261475649, 0.615714601295896, -1.12566428119967, 1.03930759137958, -0.227584485706682, -0.317098420128097, -0.702442833356434, -1.00172687761946, -0.0750492055055783, -0.273768606535085, 0.247807207178697, -0.338469149433701, -0.158404063215292, -0.333861519089508, -0.668446522429342, 0.100800293527853, -0.120964948204753, -0.188363961228007, -0.535614912774125, -0.0176234596026, 0.0428371776117388, -0.516012917906981, 0.150407374551543, 0.0842337861392922, 0.159141028830275]', 1.98); -insert into credit_card_transaction (time, features, amount) values (157981, '[1.86926884946311, 0.413058711140049, -0.572303743423866, 3.7604492874234, 0.218277694004166, -0.0405909268757412, 0.0284560119484586, 0.0190435833520761, -0.991206135710436, 1.66524869677438, 0.329371574217789, 0.0311256547437708, -0.878857968792483, 0.526688978922034, -1.4702065703776, 0.912528325484498, -0.921608557841306, 0.356867398453975, -1.38365728980941, -0.328716244875331, 0.338361480774963, 1.00800215203024, -0.0004779004906996, 0.0701951008988325, 0.146426465546607, 0.220831410102284, -0.0430859143860707, -0.062346108810627]', 10.62); -insert into credit_card_transaction (time, features, amount) values (157981, '[0.206060594953811, 0.807815773425009, 0.769807708980104, 2.41107272681782, -0.0917582319751275, -0.776406818271336, 0.572877006997071, -0.18775055589271, -0.358332025591123, 0.307231218582176, -0.477880868581443, -0.138682849846432, -0.877309754605858, 0.450803643362897, 0.44343798106244, -1.99696931449746, 1.18821908834196, -0.0708335785398571, 2.06098119594949, 0.0750030647814684, 0.389942538651507, 1.47005256021261, -0.0715339655219718, 0.91708637455169, -0.753174959470877, -0.116267216186615, 0.315513952991209, 0.252121847375306]', 10.0); -insert into credit_card_transaction (time, features, amount) values (157981, '[2.02436237603537, -0.293730788550126, -2.08411219154586, 0.121534138179134, 0.670296415672217, -0.227201023315928, 0.271028793534234, -0.0844098921308814, 0.363953056051184, 0.289564356796266, 0.0238461231945178, 0.0394588634398212, -1.5069048219135, 0.854261674667014, -0.84714069480227, -0.143876083822952, -0.441334312016314, -0.0116841999157678, 0.669983520000323, -0.203029062441929, -0.0212187036159191, -0.0598477695060261, 0.0140094625483717, 0.0543326869656147, 0.177283920207652, 0.553323077445849, -0.119150398247176, -0.0839871028600665]', 39.95); -insert into credit_card_transaction (time, features, amount) values (157982, '[1.03454846822727, 0.915873955029731, -0.778864844472036, 1.166481929245, 0.827127599192127, -0.273605125167718, 0.731468634595371, -0.474851711119359, -0.184661164380683, 0.230231303659996, -0.404474446770238, 0.129227567034488, 1.18580843506561, -1.22244286123264, 1.23599123409444, -0.796675003349985, 1.12357016816963, 0.0541532284841868, 2.60387364690589, 0.302229671207166, -0.257466115784235, -0.35618742097819, 0.287616617200605, 0.429186323516502, -1.50328057885806, 0.382681936043292, -0.154550355993643, -0.1224949312475]', 5.99); -insert into credit_card_transaction (time, features, amount) values (157983, '[-0.130366442031847, 0.712577670059276, -0.297719730900554, -1.0054853332461, 1.22166908103954, -0.0659941373862591, 0.849569199725631, -0.0177541513489372, -0.161807880520032, -0.12806524967738, -0.409825401339099, 0.477956091357462, 0.366440037390018, 0.14942209065554, -1.00892632704129, 0.48565073238296, -1.17801356454006, 0.166181143543691, 0.735403726729126, -0.0084641466526045, -0.289105858444239, -0.68323603186344, 0.013317154422042, -1.39079583478784, -0.311320000147051, 0.197458816019289, 0.124741613879059, 0.0568751015590167]', 0.99); -insert into credit_card_transaction (time, features, amount) values (157984, '[2.05269311103296, 0.0832291750212749, -1.29963681382918, 0.319329160730435, 0.190586891271607, -1.03891055540037, 0.274840025695008, -0.375561108243263, 0.376040836425049, -0.0361706507085913, -0.139771919621809, 1.03323404059433, 1.22467579188652, 0.259968927221364, 0.674129858259793, -0.407568804281939, -0.410891741117796, -0.381166876545931, -0.433151217096556, -0.157134606833929, 0.281863973713935, 1.00672960583387, 0.0724497417901615, 1.15590752441511, 0.265709827167777, -0.165664124585168, -0.0129351274570088, -0.046091589598825]', 0.89); -insert into credit_card_transaction (time, features, amount) values (157984, '[0.81583530027683, -0.135741872269979, 0.410949138436555, -1.80124886333432, -0.974179003182342, -0.915719732679837, -0.295548566414395, -0.936574701573116, 1.2708519660587, -1.14499402886989, 1.77626595190362, 2.23927946535372, 0.946650207071043, 0.125260831791892, 0.237268823166167, -0.432256428436072, -0.335418471019247, -0.0157564977461659, 0.842744825885943, -0.160982196995703, 0.659120484482723, -0.419533642855097, 0.320547791407024, 0.557733796674746, -0.0336864066761128, -0.553766936135689, 0.172173027814975, 0.168069916805165]', 32.74); -insert into credit_card_transaction (time, features, amount) values (157984, '[-1.1499923777044, 0.59625694895982, 1.74801909207187, 1.05768707484915, 0.490737741281965, 0.473117785397453, 0.44844040725259, 0.25666885341858, 0.209536022831864, -0.370252654103923, -1.43731632747189, -0.154487374945779, -0.809666313030499, -0.262462484169826, -0.616880806821511, -0.893279132813805, 0.401463388114945, -0.550878637613737, 1.31146800622303, 0.114973040826894, -0.568831258861779, -1.36939612294812, 0.10587011625502, 0.58298796794886, 0.433680213662218, -0.813375616852775, 0.235455766292334, 0.162920778082918]', 34.96); -insert into credit_card_transaction (time, features, amount) values (157985, '[-1.038324774473, -1.04420226793028, -1.68550813350863, -1.98020284108057, 1.30026242196773, 1.52857894655716, 2.24569625500273, 0.266297135236602, -0.488183289920301, -1.23386295035756, 1.17415300662843, -0.126389608625717, -1.78268755461073, 1.36742723965976, 0.727297107708839, -1.52428315117596, 0.599308925335549, -1.40121348069582, -0.815527972818355, 0.656271834043813, 0.645011675347473, 1.12862593403377, 0.86919482378899, -1.06325491003779, -0.459294682096277, 0.82408723326341, -0.0195542401536002, 0.187682143489534]', 415.6); -insert into credit_card_transaction (time, features, amount) values (157985, '[2.0377230312721, -0.174405502571658, -1.22816497916937, 0.206601285172922, 0.074077311518071, -0.593325367022731, 0.0088500123189233, -0.0946503788706294, 0.419257557677737, 0.255815665355955, 0.525467624858113, 0.623510102551369, -0.691837255641971, 0.600497313881401, -0.502984651044866, 0.183593936145614, -0.585344223819071, -0.242537465570877, 0.519042953742118, -0.25352661418887, -0.266279956717685, -0.696941769874464, 0.293863534208587, -0.486260032314027, -0.315324684483836, 0.210791657197925, -0.0767547818030606, -0.076271359205579]', 1.98); -insert into credit_card_transaction (time, features, amount) values (157985, '[2.33711541867659, -1.32241285641572, -1.5076316497485, -1.93901752550013, -0.414174669647993, 0.269720384779131, -1.05899156802788, -0.0808658994993204, -1.617465734983, 1.71725026880438, -0.104273546944298, 0.0941080483826006, 1.63945724094246, -0.483336059308375, -0.944640291093014, -0.181981381030383, -0.194555936984752, 0.630365977235737, 0.545655194623276, -0.288842100983413, -0.122488349226662, 0.162934022046668, 0.0594114496811898, -0.326713086903993, 0.0665098816809244, -0.0883627524016086, 0.0029763057924077, -0.0639523456880933]', 20.45); -insert into credit_card_transaction (time, features, amount) values (157985, '[0.272183044850092, 0.804097011214754, -0.236366736323444, -0.262365483658319, 1.16912262938236, -0.678473710241877, 1.12331938626959, -0.317212171216718, -0.152698317271657, -0.734420029626425, 1.0381150524124, 0.702601803491696, 0.23333273080958, -1.06398771710766, -0.561929474229747, 0.0087268526745382, 0.25282793348098, 0.520090073955388, 0.605443161418944, 0.0167722477592292, -0.211633701238631, -0.33743240145056, 0.024118167848894, -0.523397041819117, -0.766463522219622, -0.662480266592223, -0.0143824717134726, -0.0961654418816045]', 9.99); -insert into credit_card_transaction (time, features, amount) values (157985, '[-0.141331897398899, -0.205877050453299, -2.73231436952907, -1.33800297970994, 3.85317203835303, 2.54885158789366, 0.0039433333060001, -0.267734900098729, -0.661319767070645, -0.858174576856784, 0.381171445672259, -0.140006854855413, -0.482621300868651, -0.116694741736131, 0.547170237186007, 0.0943155603040334, 0.547478157470998, -0.283419381095407, -0.810195930667418, 0.238404045325131, 0.923845311542132, -0.635754497915462, -1.63346067045926, 0.733959578965911, 0.633797618105617, 0.739604654364438, 0.114416244960481, 0.319937286330274]', 252.9); -insert into credit_card_transaction (time, features, amount) values (157986, '[0.147051840027449, 1.0830396660897, -0.402870389789097, -0.553268602776946, 0.97253869603613, -0.803215528516796, 1.03177008707257, -0.149294644474044, -0.150102084846622, -0.85660535615362, -0.596710198204915, 0.234367756322204, 0.690211237125984, -1.15373282921449, -0.311969388429648, 0.307477317658837, 0.318226280880149, -0.29043368300994, -0.224784459518234, 0.0675472943503132, -0.334343624340973, -0.78363297823318, 0.0893149404561686, 0.577336269654958, -0.393884221899291, 0.11135399453266, 0.226049670491634, 0.085689181091412]', 5.55); -insert into credit_card_transaction (time, features, amount) values (157987, '[2.11236262467254, 0.654754740525139, -2.81924754830598, 0.476959073914732, 1.27080918013566, -0.932040025725309, 0.390901979939392, -0.341114630529116, 0.350278023810418, -1.24376005266223, -0.269915077847392, 0.170098147999943, 1.42912959605036, -2.85236305446325, 1.02703319295645, 0.956498495682944, 1.39874219383979, 1.39191006984878, -0.412732080257681, -0.0156889288911087, 0.0572854712568602, 0.396408456467406, -0.119900536060807, -0.148248625557752, 0.420761880114656, -0.0947176854940052, 0.0226409426344049, 0.0100758634306431]', 1.0); -insert into credit_card_transaction (time, features, amount) values (157987, '[2.04109782099017, 0.350940189056597, -2.10090814768339, 1.08312542190863, 1.04431954194898, -0.866813357501284, 0.887324124816537, -0.499160372491717, -0.327876686419373, 0.318842067036512, -0.846729877011878, 0.657916710982758, 1.15757045599222, 0.547625404122294, 0.12020511562162, -0.347763444754248, -0.642043189599288, -0.426426187636703, -0.274215912853205, -0.103771952899864, 0.128494173257471, 0.434767724325055, -0.0586906490835362, 0.506267126674545, 0.562184550941427, -0.508893151213243, -0.0357005330978196, -0.0512525870505582]', 36.92); -insert into credit_card_transaction (time, features, amount) values (157987, '[-0.394788448107399, -0.894404876691071, 0.979298291075818, -2.53714149861398, -0.483413827960082, -0.840846478443589, -0.245646929680381, -0.0436200901853771, -2.3458008188704, 0.921203537073684, 0.48372448980412, -0.654391319780736, -0.192391706438135, -0.177761989410496, -1.31899854386112, -0.470671553570291, 0.243554912097002, 0.542999885746448, -0.0071167943493756, -0.205083088356636, 0.0201852017064449, 0.277830546908711, 0.0185608934538624, 0.0447062630395892, -0.291131078889448, -0.22834421975755, 0.120528098915956, 0.152882165428069]', 50.0); -insert into credit_card_transaction (time, features, amount) values (157988, '[2.04712004532606, -0.0178533596278358, -2.30606043977286, 0.12799513631335, 0.968767552806877, -0.510501224883777, 0.623160392803822, -0.277374653477536, 0.112727612633072, 0.137937194438245, 0.493305890164093, 0.927393102116149, -0.0937839584347398, 0.804422500414814, -0.791809467196635, -0.596497685073425, -0.490340506852817, 0.0114111308590317, 0.553261186536863, -0.168578834552216, 0.169514320625628, 0.611486035994919, -0.12613908189907, 0.273767721234261, 0.589176448678518, -0.0915406984112753, -0.0629072823343511, -0.0782769675353617]', 28.16); -insert into credit_card_transaction (time, features, amount) values (157989, '[1.80864033170131, 0.0383821145193868, -2.10464260599799, 1.32557117246225, 0.679213923068398, -1.38480715246024, 1.18298160916691, -0.677137221825468, -0.420948059601586, 0.279909812451714, -0.660041306284505, 0.847408958726595, 1.31442093840839, 0.595582894060089, 0.0007835281641163, -0.491826764589974, -0.539881906722786, -0.507336674768713, -0.386302958973705, 0.101245975785915, 0.267422876895862, 0.63890420341079, -0.226338950259919, -0.0530879289663113, 0.577839269028708, -0.428649055843689, -0.0560485764108666, -0.0398602234387406]', 150.0); -insert into credit_card_transaction (time, features, amount) values (157990, '[-0.854089720396774, 0.802951390473658, -1.01687286859822, 1.11067043625121, 1.60872665710188, -1.70323665346098, 0.915498549602817, -0.0437809583997831, -1.25753591894454, -0.0029950921809338, -0.976889475975735, 0.658609895800632, 1.34541328550971, 0.911258546231316, 0.109320718155876, -0.85631239084077, -0.0711339201918452, -0.172068187169231, 0.567298833231111, 0.274310119314279, 0.407298693521455, 1.05198250542721, -0.231318535127903, -0.0427353025377282, -0.255749090788623, -0.389403549442357, 0.379763104243126, 0.0892658521914443]', 3.6); -insert into credit_card_transaction (time, features, amount) values (157990, '[1.83886779621712, -1.26584212554491, -0.713061595464002, -0.344034471235008, -0.773010627780886, 0.386865708550394, -0.887792599528705, 0.0665193044653516, -0.419159788590151, 0.979460795527683, 0.383055652385181, 0.952046783923038, 1.43898769303676, -0.215104118489459, -0.0110305587787033, -0.662098721303236, -0.908310963582548, 2.06090999612596, -0.813681582319438, -0.205022834033805, -0.0611147386381165, 0.0968051302793067, 0.0949946174797474, 0.304822366746747, -0.368648957472589, 0.314097076469585, -0.0166541633355801, -0.0203895822173477]', 147.0); -insert into credit_card_transaction (time, features, amount) values (157990, '[1.93090892696115, -0.480163126614668, -0.850093142552679, 0.107091373620853, -0.131368959289603, 0.0223979395098992, -0.402617937018541, 0.0103154252520142, 1.32766637867618, -0.266015098899084, -1.38553406656163, 0.276500735832294, 0.354160197554477, 0.0168838879056778, 1.15126696307505, 0.462141538725997, -0.952514217800872, 0.27471647162802, 0.134714480553858, -0.0464755268238872, -0.14137610620748, -0.45383383126495, 0.239722571272779, -0.0235600514082925, -0.271392155578471, -0.92997705778748, 0.0403439789231055, -0.0155516589263575]', 69.24); -insert into credit_card_transaction (time, features, amount) values (157990, '[0.143560596001966, 0.587314009216495, -0.0358333786338847, -0.869775533398348, 1.48280698016015, -0.007579073573562, 1.25983492837551, -0.521463698895298, 0.112014393995214, 0.168550814434153, -0.380956644934996, -0.417639703496914, -0.606826944035918, 0.226212668865572, 0.0974728408286391, 0.0484235646200726, -1.37795844984772, 1.17687285786284, 0.914286199545073, 0.0059582263924113, 0.265857825739496, 1.13844429340141, -0.469287571340013, -1.36081072102547, -0.416918454332601, -0.163319329918721, -0.254136657921484, -0.251555960560955]', 5.55); -insert into credit_card_transaction (time, features, amount) values (157991, '[0.259173746741467, 1.21386157141849, -1.45677103146647, -0.309308665209166, 1.01141101130299, -0.564735445666399, 0.612991719993012, 0.171974000960478, -0.321304504666245, -1.17031859600834, -1.1176468120284, 0.345709791087465, 1.50577618664901, -0.84415287766274, 0.600815481283695, 0.391552779300015, 0.2154017809313, 0.831188730963809, 0.0332985675473074, -0.0972804972681595, 0.289830437479287, 0.860718920941663, -0.156833013547339, -0.0030439976012819, -0.10437263161415, -0.144214785685948, -0.066941447769097, -0.0441856406478188]', 13.0); -insert into credit_card_transaction (time, features, amount) values (157991, '[0.0526809203754026, -0.190089867648071, 1.16208526769314, -1.43366348623236, -0.0801315553798603, 0.25010675315447, 0.0718004188821336, -0.198956884811732, -0.736003702781692, 0.39457143822218, -1.71664956559432, -1.35836540867604, 0.379508741991065, -0.766917053915528, 0.583058223186984, 0.843678275913349, 0.0619489268911854, -0.617608384467588, 1.81773342259392, 0.380878279362272, 0.306566709153659, 1.07361667105095, -0.341761480134881, -1.01211296537181, -0.156709849468961, 0.115647155119122, -0.0008530734809258, -0.0792043979122315]', 39.9); -insert into credit_card_transaction (time, features, amount) values (157993, '[1.85288475952639, 0.473710003884223, 0.310970769467465, 3.81760877567988, -0.125462595975344, 0.129680799942635, -0.248698938000325, -0.0166052221847358, -0.701846642724368, 1.32082671729537, -0.925746094513927, 0.744912574080614, 1.73230665990834, -0.437725567136392, -0.590151872929491, 1.23354929136605, -1.05131375888487, -0.561850926201894, -1.60242488288064, -0.159511743907691, -0.174755285657351, -0.432877685499437, 0.412669118182352, -0.0008895695501572, -0.517112847841033, -0.389886619043593, 0.0188498459005466, -0.0196453401288847]', 13.99); -insert into credit_card_transaction (time, features, amount) values (157993, '[-0.188385880285044, 0.213025385938357, 0.78071255521591, -1.37766341933504, -0.493586682173149, -0.0939470703203497, -0.318464839691432, -0.158508131738335, -0.592652387483716, 0.808193806973832, -0.82214943402255, -0.68537288238289, 0.805566419124682, -0.680552987090326, 0.722933748676302, 0.977471777736693, 0.150010963232088, -1.03705837254047, 1.49019012883415, 0.0752863788038008, 0.244302949786818, 0.55967743528884, 0.156345833461641, 0.672306340526743, -1.47409896473772, -0.495173717129364, -0.213566937468262, 0.239556787718746]', 24.15); -insert into credit_card_transaction (time, features, amount) values (157994, '[2.05167392990316, 0.272887523213804, -1.57395460342605, 1.39371941781976, 0.40762053361259, -1.23997685023916, 0.656515123923081, -0.45037159663884, 0.187040071335283, 0.26691239792518, -0.996227869510808, 0.365150932324821, -0.152766039228104, 0.50519378601389, -0.579675718953309, -0.689585294556884, -0.18624073605008, -0.58957012403175, -0.153441896914621, -0.304302826092369, 0.0413032450335258, 0.365279323191869, -0.0020650531763521, 0.0544707478920734, 0.478429123340327, -0.494707561537863, -0.0156669199767425, -0.0624594589471261]', 1.0); -insert into credit_card_transaction (time, features, amount) values (157994, '[-1.42147860971256, 2.21360707933308, -1.85054206422684, -1.67631304380085, 1.35780249285282, -1.03240924232746, 1.60596892880081, -0.286426300030681, 0.964090810678693, 1.57745524909955, -1.49921721436163, -0.59217689062349, -0.541604865101477, 0.224648757261934, 0.071242482005685, -0.576106969027422, -0.762409271701829, -0.165780929679948, -0.0181828757511213, 0.760434519614119, -0.0500193611678655, 0.608940100226333, -0.254804956349742, 0.0481006932642992, 0.0748600603920638, 0.0723379210297193, 0.563302480702816, 0.120452282676504]', 0.77); -insert into credit_card_transaction (time, features, amount) values (157994, '[-0.389578141229863, 0.965798289868215, 1.06405285907439, 1.12011687424355, 0.627132063843454, 0.188575024004209, 0.786271785520629, 0.0250913248703534, 0.0554194135958932, -0.366664888836034, -1.69891931248837, 0.660891202826879, 0.679885960339591, -0.490851304205345, -1.51273988200752, -1.30982957197075, 0.378128000333287, -0.676583540883496, 1.39176799240482, 0.167438521860015, -0.309836716429563, -0.379238572723551, -0.194083477413766, -0.413646925559684, 0.0752375390139664, -0.612144503906447, 0.449281857159036, 0.217958986364404]', 10.78); -insert into credit_card_transaction (time, features, amount) values (157994, '[-1.03834629001417, 0.980823096831625, 1.13169386902647, -0.965596494322759, 0.977842027054246, -0.554693848572675, 0.99023266502701, -0.0615763088584925, -0.718198697228029, -0.456005664710077, 0.649272018854922, 0.900761122073184, 0.537575177052452, 0.151056722133844, -0.965661561623348, 0.102450898101333, -0.71373745664673, -0.213664917369033, 0.511309133552619, 0.0842441648458802, -0.289983643887679, -0.761898723191137, -0.397129864457753, -0.311402125020374, 0.581507724734275, 0.323495541398553, 0.0872730595077866, 0.0833171739683947]', 1.78); -insert into credit_card_transaction (time, features, amount) values (157994, '[-2.18655778922673, 2.55338991628287, -4.05731999713177, -1.22917092542734, 1.5410592698886, 2.85245864086152, -1.47630610751728, 2.8507407794835, -0.5190090362882, -1.1609868002415, -0.428427490095631, 0.493050112478783, 0.0100472096973589, 0.640308697444481, 0.971030816304602, 0.522734621982998, 1.24385878671091, 0.76951887452415, -0.380507516641941, -0.378266202672716, 0.506290602255772, 0.784137868585688, 0.145798851350069, 0.612597581596902, -0.167759567091009, -0.153299530316839, -0.663251731511976, -0.0723573504426223]', 2.58); -insert into credit_card_transaction (time, features, amount) values (157995, '[-3.24087328373638, -3.04250014375305, -1.52905280617537, -1.54341703522962, 1.75569789099543, -1.42438211500702, -0.224384618618197, 0.240723645552957, -1.16172489175815, 0.205560279822645, -0.259810707826992, -0.546038513640202, -0.863699060298001, 0.604588956066458, -1.80197983685328, 1.23191819343331, -0.0385130490162624, -0.906240308679344, 0.736034445375585, -0.0603431288503041, 0.248804759089803, 0.377166624157001, -1.11842850205233, 0.232663664758924, -0.13663541370527, -0.241819312812789, 0.674763536286375, -0.669739164955096]', 219.0); -insert into credit_card_transaction (time, features, amount) values (157995, '[2.02410564755048, -0.239977221069602, -1.17552969284413, 0.546774190209576, -0.136380867800517, -0.829512139643842, 0.0355426306707878, -0.232416679923116, 0.826196917864315, 0.031921019907592, -0.831811809453729, 0.354451700025848, -0.382637426253395, 0.109604038786624, -0.624148423976021, -0.594347279076859, 0.0854363644874066, -0.609000878785836, 0.0028679005035407, -0.218079905313213, 0.146532568407121, 0.671720718941477, 0.0780787130449502, 1.23109509337488, 0.164595435721947, 0.6668067861417, -0.0756149114581401, -0.0601502729104189]', 12.31); -insert into credit_card_transaction (time, features, amount) values (157995, '[1.96531979162672, -0.604376981009271, -1.73674372868077, -0.678321855171807, 1.81561788620994, 3.77925610393906, -1.09947973164136, 1.02682028217507, 1.1205364396116, -0.14822086025553, -0.0410258631561172, 0.553902400274783, -0.0455777581470326, 0.157776530378819, 1.06905477711207, 0.119232218242606, -0.629752334109814, -0.318907944379756, -0.201247779928948, -0.142166209012562, -0.180360210617679, -0.490728830618942, 0.409368393747652, 0.657382492668504, -0.391700640161588, -0.60176744559643, 0.0539876134028559, -0.0366980694787675]', 10.0); -insert into credit_card_transaction (time, features, amount) values (157995, '[0.153383664773214, 1.13745490472237, -0.386275232320806, -0.56152742735665, 1.0334692725835, -0.791439789543417, 1.06601991271845, -0.183836682480236, -0.243090973724566, -0.87261561561599, -0.490942401876628, 0.564639350036639, 1.30967196749328, -1.27748556035014, -0.380253988916206, 0.277878452396739, 0.282970449360879, -0.367631373813257, -0.228324854474877, 0.100855389972614, -0.3289660062317, -0.732542521944843, 0.075163675853285, 0.594642778470109, -0.374099619013697, 0.109360979469292, 0.231111886956425, 0.0867836173839464]', 2.87); -insert into credit_card_transaction (time, features, amount) values (157997, '[-0.336239426170053, 0.958429815680492, -0.24889173869767, -0.746848195911093, 0.274269650318516, -0.546923737773364, 0.503795181688903, 0.463445249433104, -0.322065352417541, -0.4571219134258, 0.173844886916435, 0.42347874366809, -0.789677612021243, 0.775675564117922, -0.972964608233147, 0.324518306892487, -0.532016283436993, -0.0660608356153877, 0.282532810174294, -0.161811043129225, -0.190438410833539, -0.589730387075449, 0.104367282564373, -0.498258864252165, -0.481315563369098, 0.155649782478461, 0.111600822557173, 0.0186245286382775]', 13.99); -insert into credit_card_transaction (time, features, amount) values (157997, '[2.07227678423414, 0.155810694256452, -1.69040736134271, 0.425853175945209, 0.395716667549523, -0.900752360734983, 0.183882512640836, -0.236483240795318, 0.497722540278616, -0.377158818203769, -0.545417645429673, 0.136368471590181, 0.019141992231033, -0.834540731408179, 0.207195044610871, 0.308797783524846, 0.487589642533864, -0.313427682600817, 0.0408021393862854, -0.159101423586181, -0.362804013519222, -0.942009105043574, 0.344115104030004, 0.567010777180656, -0.263872441599506, 0.169744040359626, -0.0629537428290597, -0.0301685119021836]', 1.98); -insert into credit_card_transaction (time, features, amount) values (157997, '[1.89142394167909, 0.461925233243559, -0.74498642849913, 3.60437892143354, 0.523475632235626, 0.23278700238057, 0.112270521822615, 0.103716691947064, -1.01752051307632, 1.63106591073464, 0.0118734508196438, -0.235196702459739, -1.33029764070338, 0.709285044875633, -1.36882642356874, 1.09084761541496, -0.985950338437737, -0.125841497872216, -0.95488056362038, -0.356430951462129, -0.254410668869074, -0.825294015065832, 0.313420004207921, -0.583386295319196, -0.294627049217404, -0.328505470675697, -0.0471442294175663, -0.0596523887074605]', 9.85); -insert into credit_card_transaction (time, features, amount) values (157997, '[1.95581549901846, 0.0064200337213412, -2.63556579695858, 0.0458810376539879, 1.5554127316891, 0.198887846133665, 0.742241558151422, -0.0905190989182302, -0.202215426164499, 0.0905837486484391, 1.31443995868746, 1.18110135867995, -0.132397172420877, 1.12959218828524, -0.0021701535247633, -1.68622804870277, 0.4026567302194, -1.38025858756923, -0.637270442447588, -0.318025060572124, 0.418556274317098, 1.49322107311686, -0.199073001579511, -1.33003623239184, 0.615005377374104, 0.221316570880241, -0.0289957001045035, -0.107714970044056]', 17.35); -insert into credit_card_transaction (time, features, amount) values (157997, '[1.72986506814428, 0.37174933012196, -1.61297138354966, 3.7959286431451, 0.769966836114099, -0.748735049608872, 1.01194750424307, -0.413043568570005, -1.46281207666999, 1.53791798356379, -1.25782145683423, -0.664832404867863, -0.505758065995271, 0.836359792172352, -0.584720931689256, 0.61175336738986, -0.761991805660756, -0.510663749344267, -1.49740032747962, -0.049354581416787, 0.161755965916549, 0.120367296777598, -0.0797963948768653, -0.134588628952614, 0.233421470219938, 0.068273542838009, -0.110208180292015, -0.0420379583817925]', 144.12); -insert into credit_card_transaction (time, features, amount) values (157998, '[-0.0640958562133124, 0.663898024301168, -0.760477066502158, -0.922439695087993, 1.84110396284019, 1.2280774707675, 0.581541966751942, 0.525113793607584, -0.202841666650212, -0.788730243186031, 1.02623748963988, 0.372535496055676, -0.488542777952157, -0.675080252364888, -0.0763698193630831, -0.147262441497555, 0.752348722389591, -0.804238380819083, -0.781884659841249, -0.0228732512448712, -0.23864346591827, -0.538224598375529, 0.147990662418589, -1.06297512853967, -0.59258266054486, 0.225142908524695, 0.256604536177576, 0.0591223459757539]', 16.99); -insert into credit_card_transaction (time, features, amount) values (157999, '[-1.02574938375413, 1.15776291744236, -0.033327477821379, -0.442243813863839, 0.393003389774765, -1.19693665319056, 1.0989318096207, -0.371292483490231, 0.338062078062157, 0.418121441759003, -0.668703480545418, 0.106382715853071, 0.358559522590487, 0.0683863671740823, 0.693155822516832, -0.452353661315082, -0.465190389979203, 0.0241815230822891, 0.0434485216146917, 0.150053624184731, 0.170571854526715, 0.91016263242133, -0.292805521683459, 0.0921582959904475, 0.511682271103957, -0.0324269308502219, 0.239172073534195, 0.286926278190003]', 38.09); -insert into credit_card_transaction (time, features, amount) values (157999, '[2.05603140576344, 0.342600840903561, -1.81955947683389, 1.24637060988893, 0.572197989525541, -1.41054327263024, 0.798174184572388, -0.496394436217941, -0.109103043389559, 0.37415468472009, -0.777261214118751, 0.0264318227166965, -0.399594005572001, 0.872706235560097, 0.26099638167988, -0.451128923564884, -0.374623663124239, -0.468745496691316, -0.445848364176961, -0.302746528084032, 0.124553959218894, 0.461693303591412, -0.0098771048358922, -0.0461543591706105, 0.450949421727211, -0.479142804179872, -0.0314340442411572, -0.0641287250516018]', 8.36); -insert into credit_card_transaction (time, features, amount) values (157999, '[-1.42656259993647, -2.36732850168801, -2.1491077454988, -2.85642449287857, -1.33558729099318, 0.67084246937659, 3.92924406221627, -0.650406479028878, -3.42991822394087, 0.153531209322498, -0.838723151709099, -1.66398574297931, -0.264681323464446, 0.475410635409852, -1.89291189835722, -0.474357040668973, -0.242155089010834, 1.02470967746216, -0.0377785154810256, 1.41999492602488, 0.632862819046121, 0.713253419575556, 1.2247375972255, -1.44879582442195, 1.29660721484043, 0.252188828697769, -0.271000932427871, 0.125407173302402]', 857.55); -insert into credit_card_transaction (time, features, amount) values (157999, '[2.01102378066906, -0.624258780548357, -2.07613122346912, -0.268794071745131, 0.253604296813077, -0.646627503228843, 0.329745824921249, -0.384418114296321, -1.35704233085159, 1.08694744094524, 0.850578581032546, 1.18835482670936, 1.12220661786516, 0.449006919426211, -1.22143952013622, -2.04755549121025, -0.118250700942601, 0.929476645845765, -0.507935782020169, -0.383266167731841, -0.137817695259622, 0.0699408663907496, -0.0127426989762834, 0.767071428464348, 0.279433698780856, 0.470825965126254, -0.0884967725903225, -0.0634472867099299]', 89.44); -insert into credit_card_transaction (time, features, amount) values (157999, '[2.29899706749088, -1.3459062126824, -0.563147550902457, -1.71377958314276, -1.33590068528017, -0.393882056259288, -1.40187624254848, -0.0626831870150649, -1.15675184430066, 1.57568781032651, -0.848992992853496, -0.843739454096345, 0.657899532899061, -0.463988023154065, 0.738780145102113, 0.0043906068621889, 0.160528140633593, -0.0235764467531043, -0.346203611862752, -0.408380227731442, -0.348380201969361, -0.616430363616836, 0.500609454683835, 0.526165859287702, -0.619592133825937, -0.500084928398665, 0.03254746173764, -0.0296132921820711]', 6.7); -insert into credit_card_transaction (time, features, amount) values (158001, '[2.0804458012968, 0.157797221564784, -1.72497222941489, 0.40466173647078, 0.439693464897851, -0.851665937379694, 0.181354048303846, -0.231323911793502, 0.502060710477959, -0.376851844717831, -0.620442344527644, 0.113858829102904, 0.0451229441090357, -0.842157642072063, 0.212125713658569, 0.33320870345553, 0.455663801453384, -0.286961790098674, 0.0734824403881914, -0.157822675531815, -0.368989996328157, -0.956358190469169, 0.334313399069626, 0.481142864362185, -0.251620976418148, 0.175155116790014, -0.0627878217372978, -0.0312064091081313]', 0.89); -insert into credit_card_transaction (time, features, amount) values (158002, '[-0.549098556120708, 0.766195858129804, 1.01909289642756, -1.07510196402487, -0.129909471934689, 0.126445190701562, -0.104323067052091, 0.680171682259812, -0.0127904295950648, -1.10224461713379, 0.705173783476582, 1.12638195017102, 0.265629205980796, 0.258916255338938, -0.392073865317991, 0.505319582445916, -0.64616793178304, 0.290283934207098, 0.0693128702455883, -0.201792112756794, -0.0654568866809415, -0.319780426971331, 0.0443886396399804, 0.681193508833636, -0.229043111350763, -0.320790261779217, -0.10031657898665, -0.0461826232346844]', 3.7); -insert into credit_card_transaction (time, features, amount) values (158004, '[2.06550290251705, 0.162821106221004, -1.69100116807402, 0.413852601882327, 0.449491628074564, -0.806407337309328, 0.181257182481838, -0.220818275169419, 0.467695496903158, -0.390979249986143, -0.470052846499326, 0.255882148719389, 0.209415068917796, -0.867924211765855, 0.233993465007978, 0.243679354795962, 0.529694388382164, -0.423236913744068, -0.0288115174610784, -0.154137731814145, -0.355709657467918, -0.903873646891761, 0.348031378005165, 0.512722172718482, -0.270970861902284, 0.174850710145318, -0.058401382456676, -0.0305246906946382]', 1.29); -insert into credit_card_transaction (time, features, amount) values (158004, '[0.109324109169265, -0.341359999690875, -0.184801742480943, -2.34348578412266, -0.724844167576597, -0.394404191913993, -0.437892621320415, 0.101288947662588, -1.69032272176358, 1.21765313336653, -1.85018687214858, -2.01620345223279, -1.05993224039229, -0.0826300686653481, -0.284471370283958, -0.632545908880372, 0.562731195431606, 0.400662124109114, 0.314626330728009, -0.504081803445869, -0.0419308672436013, 0.321584296839844, 0.175852729743953, 0.417841022498596, -1.28200230406287, -0.338280669456561, -0.131838307432044, -0.0284202718310015]', 20.0); -insert into credit_card_transaction (time, features, amount) values (158004, '[-1.15506028408787, 2.4189020055203, 0.917719176127805, 4.34800584015966, 0.0286157856885355, 0.553499890025478, 0.479632581514223, 0.725492752682277, -3.05477649771925, 0.866651919786396, -0.686530212856898, 0.221366203142337, 1.42214479836464, 0.819436764218728, 0.634016613620267, -0.114883441465671, 0.598338924424487, -0.421850268280038, 1.28734383701138, 0.0908511058084369, -0.112713270597994, -0.644786218760235, -0.0045105816774112, 1.12899939306351, 0.18643712593013, 0.138407088167937, -0.291010968855825, -0.0449216077722927]', 56.69); -insert into credit_card_transaction (time, features, amount) values (158004, '[-1.15506028408787, 2.4189020055203, 0.917719176127805, 4.34800584015966, 0.0286157856885355, 0.553499890025478, 0.479632581514223, 0.725492752682277, -3.05477649771925, 0.866651919786396, -0.686530212856898, 0.221366203142337, 1.42214479836464, 0.819436764218728, 0.634016613620267, -0.114883441465671, 0.598338924424487, -0.421850268280038, 1.28734383701138, 0.0908511058084369, -0.112713270597994, -0.644786218760235, -0.0045105816774112, 1.12899939306351, 0.18643712593013, 0.138407088167937, -0.291010968855825, -0.0449216077722927]', 56.69); -insert into credit_card_transaction (time, features, amount) values (158004, '[-1.21856080377954, 2.3188899092973, 0.962124579511793, 4.37257807777186, 0.254430020526787, 0.399950438575389, 0.268947913997004, 0.768127780257851, -3.04230859762087, 0.892319101728426, -0.69453243044046, 0.242375499576354, 1.42125658230275, 0.834256083331126, 0.631472523161664, -0.132702840500678, 0.608662777267431, -0.417839058065723, 1.2830589929023, 0.173004066676641, -0.0828315589305315, -0.662974331983671, -0.022599086032618, 1.12577946680195, 0.178329541926154, 0.131827203077857, -0.349199246859752, -0.0203315741999646]', 6.01); -insert into credit_card_transaction (time, features, amount) values (158004, '[-1.21856080377954, 2.3188899092973, 0.962124579511793, 4.37257807777186, 0.254430020526787, 0.399950438575389, 0.268947913997004, 0.768127780257851, -3.04230859762087, 0.892319101728426, -0.69453243044046, 0.242375499576354, 1.42125658230275, 0.834256083331126, 0.631472523161664, -0.132702840500678, 0.608662777267431, -0.417839058065723, 1.2830589929023, 0.173004066676641, -0.0828315589305315, -0.662974331983671, -0.022599086032618, 1.12577946680195, 0.178329541926154, 0.131827203077857, -0.349199246859752, -0.0203315741999646]', 6.01); -insert into credit_card_transaction (time, features, amount) values (158005, '[-1.32810970955363, 0.107338014270061, 1.73762146291356, -0.331052776733906, 0.986896555632258, 0.510835874288767, 0.445982754162208, 0.340067045879952, -0.438485084066442, -0.82268729994518, 0.0904337560501687, 0.371077421739853, 0.180131863029724, 0.0033190296924099, -0.430258921904993, 1.00371254724598, -1.47809191533409, 1.17085240148857, -0.821312878215421, 0.233601153819319, 0.331077019310538, 0.572049383968115, -0.256214299632852, 0.167230162790563, 0.908312883877749, -0.546693084577415, 0.00781965182604, 0.0744328428619158]', 95.0); -insert into credit_card_transaction (time, features, amount) values (158005, '[1.95863121859227, -0.270358858428833, -0.361758061353839, 0.232941035614787, -0.341751857602434, -0.01286687699886, -0.557950971549502, 0.10289433602546, 0.876470961056475, -0.0239914475363933, 1.04756783261856, 1.64125606189957, 0.876207172849847, -0.0229889075230185, -0.101866433770536, 0.364804483093047, -0.853220123414868, 0.218722539711737, 0.208228224499259, -0.142187299996863, -0.124964196913852, -0.230031602129102, 0.394390129342312, 0.741236308817778, -0.413886149270631, -0.680441236162737, 0.0329031283035746, -0.0324001159271016]', 2.49); -insert into credit_card_transaction (time, features, amount) values (158006, '[1.96807082007987, -0.473547486337158, -0.224402524425328, 0.388142689028991, -0.790983742207614, -0.404441367273001, -0.69333792227015, -0.0120263556123078, 1.20784562090276, -0.0215718960470008, -0.626773151403427, 0.580835533992384, 0.404442051912913, -0.326718394006489, 0.341163082442498, 0.219212502839639, -0.376442449141442, -0.0947754607243288, -0.389843707015761, -0.137925129929002, 0.237008128981077, 0.880059040207286, 0.205791671246351, 1.22810530739388, -0.277807655716904, 0.681254716110786, -0.0313862405242558, -0.0372369502682329]', 14.99); -insert into credit_card_transaction (time, features, amount) values (158007, '[-0.174917483310695, -0.3802532173463, 0.425592259603839, -1.60065326921161, -1.79280612435935, 0.269107756756679, 1.4934269467715, -0.383368658178788, -0.762060463385778, -0.232361892163579, -0.81829070606579, -0.0287198029042257, 1.32830763279617, -0.949920527356444, -0.938435376070552, 0.967722921045715, 0.1632538044246, -1.90953822667882, 0.629253080361338, 0.0985351199276307, -0.128633755803298, -0.259236286080482, 0.402771704425359, -0.0815274099926616, -0.652753682019415, -0.65023886510379, 0.107371044334943, -0.0367400964881512]', 357.4); -insert into credit_card_transaction (time, features, amount) values (158008, '[2.12824860039992, -1.37484398762585, -1.70766623468932, -1.85208377750572, 1.13351130900808, 3.84281520229695, -1.82825993170034, 1.02281227216907, 0.186772577126798, 0.72245308449633, -0.15763866001492, -0.405617405802214, 0.140457860683641, -0.47816376057519, 0.471718261364598, 1.08269133281213, 0.0640828220124807, -1.11484502193072, 0.269356364811938, 0.0566471381801107, 0.431020544885627, 1.26910275923978, 0.114499845078129, 0.761774542353535, -0.0696130410945029, 0.0444011539559604, 0.04322762241014, -0.0520447600128112]', 14.95); -insert into credit_card_transaction (time, features, amount) values (158008, '[2.06696679914538, -0.0586455101676325, -1.28892839968384, 0.350483457381838, -0.0561656764195863, -1.22073881355422, 0.160256995636037, -0.333780364647096, 0.706142210634753, 0.0163967498086748, -0.788180370804549, 0.0597027453624339, -0.35392610125504, 0.546414664416819, 0.875582441395916, -0.197023134613227, -0.464927118858226, -0.0344365882615781, -0.297270353474904, -0.277553535295582, 0.255359945354464, 0.894498478025095, 0.0036660425132795, -0.0575925537141293, 0.233468261099952, -0.0928364440237184, -0.0158725770755196, -0.0598064892288759]', 1.0); -insert into credit_card_transaction (time, features, amount) values (158008, '[-1.33284248995339, 2.03119003550903, -0.720939425737272, -0.487277891938185, -0.308116808038665, -0.180411271129577, -1.68137885135292, -4.94871354211716, -0.711354391180041, -1.61145321731141, -1.25576550413049, 0.764544520372453, -0.42883323502723, 1.25129923642293, -0.602596501061529, 0.358616297603198, 0.0431355847483786, -0.62057891106073, -0.420707862018223, 1.10795399788182, -2.72715468874493, 0.217475512910371, 0.571787386257881, -0.0043502116861516, -0.607924730244165, 0.106827705284383, 0.0495643883022036, 0.10757800505176]', 1.98); -insert into credit_card_transaction (time, features, amount) values (158008, '[-0.298673843353843, 0.183739852526517, 0.647865001229045, -0.579165559700988, -0.480181607968308, 0.395464748465713, -0.985381660681913, -2.5118133667157, -0.11385244582456, -1.1114961500072, 0.405144750230186, 0.815051661879033, -0.61666004527177, 0.670727717013591, -0.466202706326449, 0.99276738290918, -0.819217516807982, 0.205792765178059, -0.178956660036883, 0.767749613593986, -1.44020998875816, -0.46315404139436, -0.025509281700941, -0.357899501214852, 0.236179176597933, 0.287736425353656, 0.0214301897309842, 0.26461403946828]', 130.0); -insert into credit_card_transaction (time, features, amount) values (158009, '[-0.187065738562102, 0.938949796372722, 0.158719491986008, -0.635957028283626, 0.267082977201802, -1.08421006871295, 0.90458768576091, -0.0974889838836657, 0.343124081719593, -0.0737261282824574, -1.07776962765302, -0.533308770675161, -1.30940543824619, 0.30919167449721, -0.19663321750018, -0.11058920508987, -0.286106658185972, -0.692350235470158, -0.177221975014794, -0.0036473758111797, -0.300598304711216, -0.677266238419372, 0.10110312439087, -0.0598839760112864, -0.497809069737035, 0.152464734696248, 0.348868155923537, 0.153910093277228]', 4.49); -insert into credit_card_transaction (time, features, amount) values (158009, '[1.77605423354843, -0.643944145159802, -1.16405697582766, 0.546213485659379, -0.175210218972686, -0.389591746147173, 0.0051497998833683, -0.153794950607152, 0.77260234122879, 0.0232261679929539, -1.06528075396715, 0.173686120412681, 0.0086044950463042, 0.150746727001186, 0.345403216716222, 0.188709383093358, -0.506428144770819, -0.251381530104047, -0.0921158114932612, 0.0996194116509905, 0.0305268134628971, -0.118706296972696, 0.108339322874396, 0.599722082980315, -0.21176556296923, 0.188068516027917, -0.0724443369684532, -0.0236568974723718]', 148.75); -insert into credit_card_transaction (time, features, amount) values (158009, '[-0.961235578939082, 1.33817544647597, 0.657262789394844, -0.497486396693, -0.730714410453866, 0.722049906975452, -1.60756288287889, -4.78567532423441, 0.828029747227186, -0.319231028145593, -0.933227880147206, 1.16983614590807, 1.16989282968414, -0.312015090251343, -0.108449106648623, 0.326413109547181, -0.255271208278592, -0.0871572715591543, -0.764758183230007, -1.04888230191168, 4.79202047276388, -0.720301638569118, 0.218132920417395, -0.0470787182790086, 0.763649384130085, 0.735270054982837, 0.36915484211879, 0.273405387017759]', 29.99); -insert into credit_card_transaction (time, features, amount) values (158010, '[-0.359846842014646, -1.72306592785694, 1.04928893390916, -2.31785954793245, -0.932436687283213, -0.135419624087709, -1.19918557691809, 0.18223634351014, -1.01464488811998, 1.00168115114308, -1.50766584860569, -1.58777813764519, -0.312910934496231, -0.853604094235265, 0.0756591824448081, 0.0901607754723302, 0.118832783990921, 0.616825711553791, -0.597274273125863, -0.448238221523313, -0.0215206771846747, 0.371009154709892, 0.637866626028848, 0.534466539068606, -1.04342233194524, -0.38195944404197, 0.0955265517133887, 0.152541512702937]', 67.0); -insert into credit_card_transaction (time, features, amount) values (158010, '[-1.2542814057946, 1.20096307133381, -0.627200807531874, 0.671479997644877, 2.30684078562474, 1.47195996916669, 0.980373130511881, 0.596089708960187, -1.14954783298045, -0.871663207720353, 0.876826509380834, -0.301796587205867, -1.27869630236082, -0.53208179407442, 0.268003910341743, -1.02942905140882, 1.79907100553185, -0.0706723660211838, 0.297031260110904, -0.148100374181437, 0.0220516010513153, 0.304048937035024, -0.308586510007307, -1.08225409006223, 1.01559518865203, -0.12965653868741, -0.278113051461387, -0.267037202557645]', 30.21); -insert into credit_card_transaction (time, features, amount) values (158011, '[2.10114312153406, -0.131633889284133, -2.09444043711982, 0.202531844329187, 0.736451528296812, -0.501846626088548, 0.413759428167791, -0.274836296105806, 0.49347684412016, 0.10139639854721, -1.57536354832683, -0.191761002854347, -0.516013680327826, 0.471888064322938, -0.24276731060148, -0.36696166759536, -0.264130483177789, -0.600831418464043, 0.401751198253923, -0.185175887839243, -0.0756203432825797, -0.101839504363857, 0.0214987019545058, 0.0714925771207879, 0.25129259337617, 0.573086957506978, -0.106701465881401, -0.0751004318541749]', 24.94); -insert into credit_card_transaction (time, features, amount) values (158011, '[2.13568645962781, -0.0495722266166086, -2.42561905467689, -0.250896037581784, 0.80652073476973, -0.877499997940161, 0.525600500430951, -0.251580495866916, 0.228290445740574, 0.237081672505878, 0.357697356019724, -0.177064565613102, -1.84575614751461, 1.31960087365334, -0.0600074678519535, -0.209694419357159, -0.588194831094848, 0.342498404005647, 0.587283682180758, -0.307577557991362, 0.142580642397469, 0.409347190412612, -0.0502162249690347, 0.290028895538371, 0.428879347821436, 0.220688853255967, -0.106299269096564, -0.0907389372021451]', 0.77); -insert into credit_card_transaction (time, features, amount) values (158012, '[-0.356112107485342, -0.176792234424064, 0.667073479330746, -0.832209868478933, 0.708046902364393, -0.556373890222478, 0.84391850402528, -0.140145781725371, 0.396220800911885, -0.598178788054038, 0.164846278986967, 0.0477260492905488, -1.50444726395879, 0.202073972005627, -1.1673127114867, -0.200155807497776, -0.593045242449931, 0.228665426373271, 0.559031887936197, 0.076144266555552, -0.103606206643723, -0.289182382264244, -0.0357451605996312, -0.370219303277823, 0.18072485751449, -0.108404918136018, -0.239703048695415, -0.257559850365974]', 68.45); -insert into credit_card_transaction (time, features, amount) values (158012, '[-0.115623294131462, 0.232952497287794, 1.54391073546632, 0.74175325796478, -0.408977914119821, 0.875809943582535, 0.524788492741967, -0.067408140031601, -0.510123806832224, 0.233755923793785, 1.2133921555289, 0.591021490450145, 0.943774719526892, -0.138164975040216, 1.45782503054464, -0.47189508525955, -0.0484905554283102, 1.01933839962322, 2.65449992273842, 0.727916336802697, 0.334267314456189, 1.06185604724004, 0.0773724916852188, 0.858019182195634, -0.523388914381481, 1.01915778703403, -0.0535869079019798, -0.0628129239951077]', 139.0); -insert into credit_card_transaction (time, features, amount) values (158012, '[-1.36319602377002, 1.46744250567976, -1.80801440252905, 0.758068498733319, 1.91363288695622, 1.26347372708645, 0.534051079649939, 0.971553749657575, -0.831576539740343, -0.551426446125783, 0.665632218797952, -0.0221423444694377, -1.1769746116895, -0.263322643684293, 0.0524271682594856, -0.905754215946917, 1.86084049587924, -0.0554157576697577, 0.128724314473812, -0.276268762354191, 0.1178831286758, 0.62066415939755, -0.080968923661723, -1.07924656287613, -0.0869949100703554, -0.332134232365957, -0.12029706944578, -0.146111970160301]', 32.79); -insert into credit_card_transaction (time, features, amount) values (158012, '[2.06804013207811, -0.854566234202144, -1.48438270459897, -0.952948646598933, -0.293969763362184, -0.761562206676964, -0.172659366138343, -0.29596589214731, -0.858394409898638, 0.898922909951742, 0.906730502052593, 0.646359344803653, 0.940969290991741, -0.006479051868868, -1.03698810719985, 0.822421675294992, 0.0312538227132215, -1.45701291092015, 1.14063774461727, 0.148278489067593, 0.111625309179023, 0.234895744286238, 0.0789322648857008, -0.357476182162683, -0.0073477595872901, -0.282854187904493, -0.0473919080405555, -0.0641320545480188]', 76.48); -insert into credit_card_transaction (time, features, amount) values (158013, '[2.0298433000459, -0.0880601458885333, -0.902333329835496, 0.522586204628432, -0.406609334198378, -1.46985201784382, 0.152797528570979, -0.32675532491943, 0.607351021649553, 0.0905275170575396, -0.470746065389009, 0.244645641298072, -0.636108789216364, 0.425669978145497, 0.0780593327541418, -0.198645989990932, -0.0989181095164906, -0.930120634541996, -0.008975555400264, -0.275783044998366, -0.26801840623418, -0.673364996689796, 0.398815001102551, 0.442669552427606, -0.376303691551718, 0.171754984141973, -0.0745017228674723, -0.0567975559072154]', 2.99); -insert into credit_card_transaction (time, features, amount) values (158014, '[2.19059184936535, -0.667482517987159, -1.42159182025015, -1.10895976920009, -0.20580177735733, -0.607630452654572, -0.421494377090815, -0.217884168919408, -0.942945048297514, 1.00890578671234, 1.28341729358577, 0.53134788831271, 1.02225607847444, 0.0392922229142589, -0.383960015434374, 0.995536083170316, -0.111521365971314, -0.98056107619905, 0.638013238757741, 0.0494848082164501, 0.494216601009636, 1.43343627056651, 0.0128038517785256, 0.789275948935685, 0.205997556340949, 0.0158576375863187, -0.0379919969562041, -0.0677634987861634]', 10.0); -insert into credit_card_transaction (time, features, amount) values (158014, '[2.15772500725235, -0.565010808070048, -2.25210028245371, -0.656838100190328, 0.417949337253706, -0.540404973914572, 0.221306383120983, -0.358830724316406, -1.22462923514464, 1.08842683843672, 0.348654895460532, 0.82697007233589, 0.874521347191276, 0.451709193508456, -1.17860675540703, -1.8235023524484, -0.292778023880146, 1.13519833862308, -0.146309785478595, -0.464203163115534, -0.219075435481153, -0.0801340219061155, -0.0188162703987775, 0.347536337871168, 0.299916502243819, 0.796660422332797, -0.108067714557822, -0.0812456414352052]', 42.13); -insert into credit_card_transaction (time, features, amount) values (158015, '[-3.36075457165147, 3.61812371452389, -1.94380003937942, -1.55487797308058, -0.232721270330603, -2.53741807883311, 1.45757625128927, -0.183662836138109, 2.22376282070789, 3.73364101164388, 0.0306710420874961, -0.0515998645066788, -1.14900357586303, 0.019489090941739, 0.0378903246767697, -1.17582778113969, -0.119461564605101, -0.796937153800969, -0.860334658983379, 1.52647615757724, -0.155660517363271, 0.803622552577433, 0.03170527728366, 0.90911506442161, 0.18856085901733, 0.0112478529725959, 1.00255268080285, 0.289222071208658]', 1.54); -insert into credit_card_transaction (time, features, amount) values (158015, '[1.3286221109967, -3.0036285044689, -2.17104255519545, -1.20034493691271, -1.45192935372842, -0.685638827183179, -0.165426013547936, -0.360504694585152, -1.56723281385972, 1.6220540043684, 0.11072479290007, -0.991455493328809, -0.93758475467081, 0.342389105533479, -0.966093536062319, -0.60627681384665, 0.383972832245193, 0.867719706872176, 0.0571431139179858, 0.520741209728053, 0.518875265687279, 0.844124527807451, -0.573745259072927, -0.286280404572821, 0.156454063926388, 0.242699655877684, -0.127925786452998, 0.0030608193830294]', 527.0); -insert into credit_card_transaction (time, features, amount) values (158016, '[2.05188001302592, 0.0348865835982961, -1.82489028521084, 0.240945123039728, 0.538142561529563, -0.408024626839614, 0.0163465023580155, -0.0201352944127314, 0.388981338726039, -0.182906300289172, 0.714778317665099, 0.265502035502147, -0.856197533034902, -0.488854536653689, -0.376176553343842, 0.640811456881494, 0.165885139670378, 0.393193203430254, 0.459865414572454, -0.191719998725591, -0.342454882066987, -0.959505214956084, 0.301244264898468, 0.161487832110637, -0.278840486170047, 0.175496511657153, -0.0727473926089709, -0.0454994015328238]', 1.98); -insert into credit_card_transaction (time, features, amount) values (158017, '[-0.932059273777712, -0.134426462911196, 0.772692057775689, -0.747332254458257, 1.19363988055783, -0.754138038330834, 0.631763782720432, -0.0203658352550088, -0.080884974934663, -0.888336491273951, -1.22232470732149, 0.0678166073914077, 0.232688959664018, -0.0374107628237998, -0.458266111392729, 0.300523120014312, -0.789381105832364, -0.211169653621913, -0.750798062898426, 0.169673219492678, 0.140619500448844, 0.104309922915188, 0.144435330750445, 0.673637250217338, -0.228817445798648, 0.0526938306279889, 0.050418939379092, 0.183032605262705]', 70.9); -insert into credit_card_transaction (time, features, amount) values (158018, '[-2.89215053450099, -1.39625746956052, 1.53102495742112, 0.822701727139747, 3.16967084037415, -1.54518216175689, -1.1539309234839, 0.214378508190957, -0.221002770060266, -0.708907597819677, 0.703805330201808, 0.662673290680547, 0.373053554687829, -1.2312832915632, -0.261327853556574, 1.05033507632874, -0.148900721263542, 1.00884113579705, -0.0149744683411427, -0.452646117639742, -0.38385290241954, -0.89687542010848, -0.730306754353318, -0.579237341308403, 0.384479012881505, -0.832912674670711, 0.361005313543467, -0.0674958404769695]', 34.95); -insert into credit_card_transaction (time, features, amount) values (158019, '[-0.692113938530945, 1.31322905999001, -0.521201209439012, -0.483767039913747, 0.67528577528705, -0.341005212892133, 0.531521123077797, 0.544426676252537, -0.324382576289273, -1.32184205551676, 0.756413946595868, 0.412216372909969, -0.566762992698048, -1.25437059857952, -1.4893040650211, 0.356076111939148, 1.12360142067351, 0.844035830406707, -0.0358623447215515, -0.0457538598457172, 0.121183903330983, 0.488303121618516, -0.183195340392929, 0.624329253229232, -0.197622719380915, 0.533996954477075, 0.244057268454374, 0.197915897790865]', 14.39); -insert into credit_card_transaction (time, features, amount) values (158020, '[-1.12200628366799, -0.457950088985587, 1.67237441053119, -0.466082206773037, 1.0509684781632, 5.62615888306642, -1.64109782647114, 1.65423636512179, 0.273081740198956, 0.462746963666576, -1.34905623067453, -0.0081170819182522, -0.0293042269574189, -1.34096663940576, -0.899291556406563, -2.29331780809303, 0.536157017254091, 1.6854308044934, 0.0458828036695601, -0.0109095521174638, -0.314833804457018, 0.120776261416867, -0.287328935456076, 0.696121200950272, 0.580222236646483, -0.123866190255205, 0.585458586983351, 0.254846368895795]', 50.0); -insert into credit_card_transaction (time, features, amount) values (158020, '[1.69234571013859, -0.105039263484382, -1.91526204863659, 1.19088481394566, 1.18460904173727, 1.57559755895921, -0.42723589515588, 0.562465879341146, 0.522988426717505, -0.681035776184852, 1.30158377716841, 0.372918968991889, -0.495313231239861, -1.78386619476624, 0.711942108589096, 0.425689857371131, 1.42515544808223, 0.392682245258996, -1.35049741039887, -0.0994592504385012, 0.0480323632818154, 0.174183546075954, 0.107162479626003, -1.1691702782335, -0.232120048291807, -0.597588176590653, 0.0913160732796459, 0.0047357337573496]', 84.0); -insert into credit_card_transaction (time, features, amount) values (158020, '[2.27111082192535, -1.28664547506791, -1.28222705350878, -1.65792731670975, -0.834495361397583, -0.42737892521792, -0.90080684881993, -0.105961962166683, -1.62191843396148, 1.72129681576365, 0.706430219452338, -0.134978372766328, -0.0003340866483964, -0.0520930181934239, -1.24007007633913, -0.740913234887491, 0.51370308298751, -0.114014316456132, 0.356540648380577, -0.442595728371339, -0.342447581123677, -0.533457228659934, 0.345727790021545, 0.657011162039137, -0.224177716487093, -0.375518392670327, -0.0132068316186918, -0.0593780392408658]', 14.4); -insert into credit_card_transaction (time, features, amount) values (158021, '[-0.702790428181329, 0.720406636512539, 0.9342701622712, -1.14531501058955, 0.818452719276262, -0.946817662562218, 1.1817011012598, -0.273955036034273, -0.144021027767585, -0.201863325800114, 0.766480234566862, 0.345900909890859, -0.888688645175973, 0.209018387790344, -1.2726555744136, 0.171937737654069, -0.859793134888601, -0.273147939101213, -0.251707212339735, -0.0685826156312848, -0.25784613905826, -0.538555366533331, -0.153338765103057, 0.0576671923723828, -0.302110249263983, 0.0421762436224188, 0.0132382102031736, -0.0540165180394867]', 8.7); -insert into credit_card_transaction (time, features, amount) values (158023, '[2.07386596664826, -0.0574474764833047, -1.32266276285972, 0.378656051692918, -0.117837413212535, -1.39583096601079, 0.200316364078324, -0.358865810237295, 0.725053004880719, 0.0406563940631472, -0.831944067758982, -0.151727503312774, -0.786835606155431, 0.657393598233784, 0.833119699354609, -0.179442218962579, -0.451064184733163, 0.0363218537704614, -0.229172122366579, -0.301798890214492, 0.235533016070261, 0.80813268884773, 0.0117178195227804, 0.0603417932928446, 0.238485379956948, -0.108280379250893, -0.025032459774617, -0.0604760194003316]', 1.0); -insert into credit_card_transaction (time, features, amount) values (158024, '[1.3812998852291, -1.6855638790775, -1.08201980232882, -0.768956734741289, -0.343957063833239, 1.0631943685801, -0.569929512753637, 0.335112250965508, 1.55728818689712, -0.644782790504745, -0.131422013604171, 0.469188663830721, -0.0807469103033629, 0.123956521667, 2.11002619275931, -0.600274914180664, 0.346997049869918, -1.21251173073995, -1.12261428814145, 0.323980394680132, 0.421475242229477, 0.816282520366422, 0.0515997744700914, -0.268750315521372, -0.653431458245139, 0.743398326844738, -0.0446368027267562, -0.0085561300393639]', 289.8); -insert into credit_card_transaction (time, features, amount) values (158025, '[0.376337000790453, 2.34401765468442, -0.576319562992614, 4.9045885987752, 1.0852926797056, -0.317089445118758, 0.824636000873547, -0.112581024954758, -1.86334386252492, 0.989373105390264, 0.0655255369114273, -0.288828616498574, 1.40109531595254, -2.85582001679919, 0.469506442302996, 0.561961800837084, 2.24822993276018, 0.632002797602366, 0.71821352973162, 0.507300542421562, -0.422439867129738, -0.851232057797204, 0.157989934594824, -0.217006902765562, -0.623896764547443, 0.0459039196578555, 0.425447231332348, 0.147727991755193]', 2.5); -insert into credit_card_transaction (time, features, amount) values (158026, '[-8.96971251912099, 7.53597195525541, -6.72151102801756, -1.51089577092286, -3.46695390039357, -0.948142590776108, -3.5320288922753, 4.72719293847749, 2.28413423822467, 3.67262139954074, -0.524159638289884, 2.1812120584612, 0.483838812579013, 0.822944498549129, -0.795942554609621, 1.52218941426229, 1.66586784234829, 0.871112553522731, -0.705198513309069, 1.18506615693331, 0.0955160015746943, -0.329154839461986, 0.744203148916278, 0.0583801618215842, 0.580245760318318, 0.353083252758906, -0.336489738492317, 0.651645091340483]', 5.09); -insert into credit_card_transaction (time, features, amount) values (158026, '[-0.0411362706306996, -0.220560148922629, 1.01235482098296, 1.11926344898811, -0.191347078861543, -0.293937627356436, -0.315027408081702, 0.174625180849426, -0.0948211408189453, 0.351563338187937, 0.838693633451771, 0.150544230236316, -0.814295047056348, 0.496159370442463, 1.03577209695395, -0.492802671998233, 0.269642775324203, 0.62934993386594, 2.87077127932348, 0.395722073464704, 0.0301042715185565, -0.0356016327614129, 0.569941764458777, 0.064809932666515, -1.9540522843997, 0.474514196656517, 0.249768333200403, 0.282966840241029]', 59.9); -insert into credit_card_transaction (time, features, amount) values (158026, '[1.8845408705184, -1.57198622407464, -2.25834829501493, -2.73018100845195, -0.588552889794093, -1.25244283080556, 0.0726867763310602, -0.375652965145536, 0.568206332600148, -0.273289832238356, 0.928453011429209, 0.939874123010966, -0.399409675738461, 0.827767322163664, 0.395219513341256, -2.63354758679693, 0.0046985048432499, 1.78449470472491, 0.56281535280024, -0.2652998474271, -0.0908898959612222, -0.0091651937167342, -0.0738109272361255, 0.758319483504186, 0.262225115533907, -0.772610466459783, -0.009741585541382, -0.0305728896846595]', 185.96); -insert into credit_card_transaction (time, features, amount) values (158026, '[-1.40186451573321, 0.574414179400537, -0.699218351415311, -1.6998275291308, 2.89966487009745, 3.47599109876195, 0.167104800364399, 1.29806857954787, -0.776928707942542, -1.03902118088366, -0.379846397582771, 0.0569042573111709, -0.390457880345495, 0.666057129070605, -0.0462687491159509, -0.255127371692401, -0.0959822346000761, -0.792042207035525, 0.113453649669977, -0.0557637264872856, -0.17485256670988, -0.858522868763138, -0.258559338410444, 0.720468300050424, 0.646053055551408, 0.413605380465705, -0.290968039114206, -0.038218878870042]', 29.87); -insert into credit_card_transaction (time, features, amount) values (158027, '[1.98334362190362, -1.07499759743561, -1.23808722499258, -0.638798384443381, -0.369728599694053, 0.182272906699272, -0.594504544950383, -0.0131013261854548, -0.518791494926067, 0.966542363373287, 0.123198938808012, 0.679302664092877, 0.771246526746198, 0.0291464366636196, -0.528324940289719, -1.0063492969552, -0.645043921748145, 1.58412516262599, -0.287481492210106, -0.337297625911408, -0.352038911253586, -0.649017700078523, 0.181594043650614, 0.0971591189742047, -0.33270292384924, 0.49130019440509, -0.0597336253818767, -0.0453469733843929]', 99.0); -insert into credit_card_transaction (time, features, amount) values (158027, '[-0.479561088303501, 2.09727296588127, -1.83172350683907, 0.803337039943055, 0.864218865013255, -1.59468961077119, 1.03345438393617, 0.290785266154311, -1.35233075259922, -0.977477489635804, -0.687076195962851, 0.354528577870922, 0.696307980295894, -0.0219360327428018, -0.0845441177817256, -0.13141615423882, 0.923423744674992, 0.20292233394646, -0.386314441592495, -0.362055153651599, 0.334092880653461, 0.807311684264609, -0.166293985290652, -0.0853953199903552, -0.0303548168950653, -0.477985353791553, -0.252130120765253, -0.0055098425400003]', 3.99); -insert into credit_card_transaction (time, features, amount) values (158027, '[-2.26937494863406, 2.46896387958507, -0.552642600075627, -0.988644659117133, 0.160638344611072, 0.236830491934834, -0.922422071570921, -4.4312266201294, 0.149168549443, -0.227034492872218, -1.82019661818979, 0.158328577270348, -0.35385758462076, 0.630045288321782, -0.409353791676874, 0.201282263236349, -0.0468619105025272, -0.327261322718866, 0.208884307203662, -1.09095008228502, 4.34236306228897, -2.30014012987148, 0.592053677644645, -0.752815715798393, -0.145816859928819, 0.249402378844361, 0.261954109699018, 0.179065190459022]', 1.29); -insert into credit_card_transaction (time, features, amount) values (158028, '[0.218982140449111, 1.09790666776565, -1.74492772602432, -0.946471343041764, 1.698527879749, -0.906198693069468, 1.47825586101997, -0.182782704675715, -0.668852024240003, -0.519143300728539, 0.534119705382849, -0.407273312819681, -1.12592269750011, -0.275903173647229, -0.71293008682192, 0.431733235906333, 0.100543417670423, 0.789486965485517, 0.0046360903011481, -0.0180003806083725, 0.137315102116296, 0.455681217692153, -0.185570914500965, 0.13173064984024, -0.311413742189966, 0.633974270852576, 0.257281702739795, 0.161364179377333]', 15.5); -insert into credit_card_transaction (time, features, amount) values (158028, '[-1.37985804980739, 0.793973966478004, -2.1841182593307, -1.58662976300357, 2.17592277366205, 3.43477240992112, 0.0423904108320659, 1.61455694935254, -0.402392264638262, -0.711041771121262, -0.612084855682658, 0.334480768591291, -0.267668714270779, 0.927676030968783, -0.316998046893873, -0.100010013617603, -0.0788829651085887, -0.761822095513528, -0.093670397730602, -0.227448774184604, -0.0631493809295078, -0.402987665249253, 0.249758537636261, 0.716907766776778, -0.758521617301839, 0.154248383742078, -0.0614147152215703, 0.0987632617967819]', 83.14); -insert into credit_card_transaction (time, features, amount) values (158029, '[-0.362211311239705, 0.429058175354956, 1.45910877608664, -0.415063083819598, 0.376753437707893, 0.247101576037765, 0.463892835130038, -0.168525855002821, 0.60254735519351, -0.549286374314341, -1.00851993805448, 0.371509925091531, 0.969895138476047, -0.696979819763335, 0.383786008987526, -0.289351076966226, -0.572426595982439, 0.322956910629434, 0.136455979419392, 0.0475111121403775, 0.300434754479481, 1.23722991434846, -0.30200509706785, 0.673191650282243, -0.268942091685096, -0.189464925216867, -0.0615280978292815, -0.041777109568563]', 29.95); -insert into credit_card_transaction (time, features, amount) values (158031, '[-1.17359609832539, 1.31219367933074, -3.45825634278, -0.619697079736893, 2.66309870331589, 2.91628165771074, -0.0232599555609482, 1.82143301701985, -1.21791988415161, -0.649555761964068, -0.67266631753441, 0.328805727364899, -0.219406734016554, 1.78852807794834, 0.0005214058168281, -0.0687095149492227, -0.202580910778195, -0.295119678913572, -0.759983936367472, -0.657311385180601, 0.365553495274159, 0.61929703867544, 0.188888054422954, 0.697252809401397, -0.199772198646894, -0.645708812586734, -0.407238142613834, -0.268139018826796]', 27.0); -insert into credit_card_transaction (time, features, amount) values (158031, '[2.10703262019669, -1.11519976824871, -0.405904513230341, -0.835567684152946, -1.35262247270329, -0.631244740815227, -1.16879948577003, 0.0554114562586086, 0.0750603357167312, 0.949686885904241, 0.726186662830209, -0.479265705127159, -1.24110687068457, -0.0137108318703954, -0.388079653768245, 1.38052206022577, -0.0020924332498371, -0.522249754710248, 0.705432581462696, -0.10657250734388, 0.366803408279533, 1.04190123558376, 0.14886721567695, 0.078629065065693, -0.239717780927028, -0.108852687044709, -0.002471993601708, -0.0594965189145242]', 14.99); -insert into credit_card_transaction (time, features, amount) values (158031, '[2.03536201944366, -0.154987712698983, -2.9587481513792, -0.576317086765721, 2.60688279192775, 3.1435447970342, -0.413552119246551, 0.783741468086949, 0.35960673565204, -0.352244168884697, 0.329686844837848, 0.183611504312773, -0.272471045586356, -0.596868411176443, 0.584212566926748, 0.17900689176916, 0.473972692508747, -0.498335531622195, -0.140900354260615, -0.11560251899467, -0.350812914111748, -0.998261522870848, 0.361272593229977, 0.604955364070343, -0.265481196698076, 0.219402950201712, -0.0396837084348813, -0.0423499198869141]', 4.49); -insert into credit_card_transaction (time, features, amount) values (158032, '[-1.05024221264022, 0.0025401599929001, -0.85418081044805, -2.48765266834124, 0.219093791364313, -0.603868852709712, 0.293886967238672, 0.305268281916422, -1.23659347166536, 0.416070428206051, -0.27696033239687, -0.436862379417214, 0.213481272883072, 0.25674137351867, -1.08280971519994, 1.73422355424759, -0.674230652189816, -0.297489644820476, 0.285521398692345, 0.216416453083641, 0.535336523504374, 1.44054410815674, -0.317221551372596, -0.978726662973967, -0.155548364571756, -0.0847672490390446, 0.428207540143884, 0.192653197490518]', 92.8); -insert into credit_card_transaction (time, features, amount) values (158032, '[1.90167182036578, -0.0647195001622905, -1.63861508134985, 1.43640591990439, 0.261252439525088, -1.07592324896262, 0.61446466869174, -0.355135298819051, 0.383589825877924, 0.248684797271174, -1.19628967061793, -0.213227125200212, -1.22287295610631, 0.716743752258148, -0.35906352334014, -0.61338219693596, -0.111828183856228, -0.508697174021479, -0.210014328323246, -0.228711088928778, 0.0387730037056437, 0.13760484705351, -0.0228478330306491, -0.109929886045315, 0.348948123430657, -0.534909903086685, -0.0298987755607366, -0.0512705504925316]', 73.91); -insert into credit_card_transaction (time, features, amount) values (158033, '[-1.6449835220238, 0.676857125738824, -0.641803229754665, -0.979810092173884, 2.13515893526295, 1.54144440260202, 0.668270006419728, -2.04957136291005, 0.897611610687022, 1.43824335174584, 1.44553893561281, 0.675702938312644, 0.0612233185862741, -0.240336194394959, 0.813795754964867, -1.29579082087597, -0.286346784597244, -0.766463811282719, -0.604375934118076, -0.544029249675438, 1.74304874744952, 1.29832922272758, 0.451478315058192, -0.713430341074749, -0.885625926989528, -0.255318895472629, -0.394642415030952, -0.0821596998051062]', 2.21); -insert into credit_card_transaction (time, features, amount) values (158033, '[2.0570677358863, -0.973300264074425, -1.54151461408459, -1.14665295726593, -0.19470576155852, -0.312563887359672, -0.412593350862231, -0.189196216674687, -0.842893009744399, 0.958963045754405, 0.830056670552292, 0.393755910846297, 1.16428631465264, -0.0372803419834995, -0.302300341671561, 1.31838452480115, -0.380857623001207, -0.736159049996304, 0.864286623153643, 0.248723554463887, 0.429156000647478, 1.03119374123013, -0.0511204869894362, 0.351594557933091, 0.113794501239562, -0.0750378095262471, -0.0492702168473531, -0.0520609687206832]', 95.0); -insert into credit_card_transaction (time, features, amount) values (158035, '[1.59584958501531, -2.35921708123694, 0.483229406165788, -0.335229768345118, -2.16421323035442, 1.13455323929928, -1.98493022637826, 0.525184293765458, 1.43379378232765, 0.445542841269327, -0.0837875030561063, 0.38353532505258, -0.53076877397851, -1.38772587152528, -2.37556323741882, 0.959248718006741, 0.505523951697418, -0.487885396684577, 1.11334643248188, 0.38075929272823, 0.460185925483974, 1.22933945583353, -0.0320707110079302, 0.837740490895204, -0.356425902314491, -0.0548470026180815, 0.0417136861389353, 0.0005329629137676]', 225.5); -insert into credit_card_transaction (time, features, amount) values (158035, '[0.0566021745191798, 0.951098353437786, -0.324917580352524, -0.615017590159592, 0.963199567285392, -0.609298195137936, 0.869264722914242, 0.0165292763594911, -0.351598965802727, -0.671140867571411, 1.16686148455664, 0.750322686533213, 0.152092828571542, -0.86037437195677, -0.974831883207936, 0.477710990373779, 0.149119160237306, 0.208631321480512, 0.0096100439429732, 0.0605345008608042, -0.272931553934858, -0.680832370963663, 0.112028300919858, 0.667083758743021, -0.456710585780856, 0.0857316915837108, 0.217644616221604, 0.0772180147410612]', 5.99); -insert into credit_card_transaction (time, features, amount) values (158036, '[1.37166443259712, -1.11021453135428, -2.12965919689448, 0.577863463863569, -0.189423993257986, -0.669839314585188, 0.317641621828778, -0.0826023233275789, 1.00660845749559, -0.831872204858817, 0.723187944050703, -0.0213423674863225, -1.85589857447857, -0.992139459844658, -0.51679684880317, 0.379995787423229, 0.988287183417761, 0.864674461658353, 0.352855632853052, 0.373842235715342, -0.0442693053525782, -0.697175702504967, -0.123269472388493, -0.554202338304981, -0.241485533441161, -0.11090445837269, -0.0776978691668765, 0.019915980712503]', 329.75); -insert into credit_card_transaction (time, features, amount) values (158036, '[-0.848132112847811, -1.37741691535354, -0.483287887917608, -2.8677557216457, 0.160810180395217, -1.35355120274205, 0.955147119570841, -0.33043950181277, -2.45035025166664, 0.64430088257854, -2.1846429107193, -3.11501515284984, -2.56219291840295, 0.639089315121884, -0.469331325840908, -0.461257390784567, 0.232887105661091, 0.398929582677185, -0.486526548409494, 0.0068392871085084, 0.0776555965243466, -0.0950931954148426, 0.134249179747713, -0.756083218207173, 0.596374994164938, -0.0176637952921578, -0.0671055523432867, 0.107939417872924]', 212.0); -insert into credit_card_transaction (time, features, amount) values (158036, '[-3.05661614363048, -0.925224110668674, -0.0263192523562169, -0.786261554545787, -0.107190115723014, -0.755997836327521, 0.35656638166816, 0.935692834050907, 0.0134966500442507, -1.56590009666917, -0.543473301292723, -0.61088280554071, -3.18765283312174, 1.39810193041292, -1.14833698987461, 0.463934357611409, -0.193650888427685, 0.548328515952565, 0.244120941268683, -0.402671632722606, -0.0613502519687377, -0.687308147826808, 0.0312355883625691, -0.38691925716432, 0.833368448478289, -0.0444888432940726, -0.184565333095616, -0.600208999178454]', 120.75); -insert into credit_card_transaction (time, features, amount) values (158037, '[-0.0739888344128354, 1.4086829888292, -0.714582542706148, 0.997097935627811, 0.476138902435877, -1.32199346330527, 1.10559822160598, 0.0665682378070395, -1.34542812726404, 0.0300651504594178, 1.03830567842148, 0.624854959132702, -0.613251955221646, 1.44707453390369, -0.504046184324323, -0.699419998362678, -0.0366453172997912, 0.283501885756301, 0.605847345023067, -0.256643935477219, 0.437758138153104, 1.20560255392758, 0.0314273144179835, 0.549898660497841, -0.918290936224458, -0.583786191478743, 0.152652206651162, 0.206205322688217]', 13.56); -insert into credit_card_transaction (time, features, amount) values (158039, '[0.0027886393703686, -0.246172130247548, 0.432182731679725, -2.19237956717741, 0.321394057013514, 0.594958072348222, -0.16572393463533, -0.147374491780634, -0.493200471846075, 0.775866716544824, 0.188588081878574, -0.043540043924062, 1.07454308236404, -0.979852863359727, -0.844954857473175, 1.46500106484545, -0.705396584600766, -0.46431381343931, 0.797086909295227, 0.0664926982253571, 0.366367093987032, 1.15076631765794, -0.263078949164154, -0.14935882692341, -0.311047239134237, -0.222004127980555, -0.495862493857952, -0.212741970218743]', 24.99); -insert into credit_card_transaction (time, features, amount) values (158039, '[2.16419493082148, -2.00104284658433, 0.37760078887389, -1.47306988582828, -2.03670395092928, 1.03648682131037, -2.46071020717002, 0.454148486037529, -0.0892711512372141, 1.46798399922231, -0.225823228621129, 0.0726147480521411, 0.559564067320394, -1.37269953202828, -1.55722493210918, -0.0699723044720499, 0.216558915704659, 1.05860125324494, 0.26704623134624, -0.37299309833219, 0.0114172849385955, 0.743688007541658, 0.204592219008939, 0.368546668543463, -0.390333943608661, -0.0559280122208667, 0.093931236134012, -0.0327800085965102]', 18.95); -insert into credit_card_transaction (time, features, amount) values (158039, '[-1.42571176165928, -0.761333263622105, -1.66947983257245, -2.51419912602983, 3.50558649833876, 2.40104651024928, -0.427789419682642, 1.16632274532313, -1.93349121646105, -0.244198425191946, -0.440168854260717, -0.199515450211599, -0.0258042250628068, 1.07450571891906, 0.403130376925301, -1.92707807057653, -0.0249710598933956, 0.888529135329965, -0.895422608450692, -0.230184592841364, -0.138303207387573, -0.594791209485464, -0.226214123717853, 0.698407015559469, 0.267324663082692, 0.0742725849543881, -0.0150263606499974, -0.1504438264428]', 0.95); -insert into credit_card_transaction (time, features, amount) values (158039, '[2.13248934754282, 0.185056806998615, -2.13576503203818, 0.291459747202838, 0.65576440856261, -0.933549359540901, 0.245727499169464, -0.325141942179795, 0.674203962620894, -0.4675105621698, -1.29217289433744, -0.32037648033182, 0.101098181372744, -0.775868465336214, 0.982090739679764, 0.5090970742339, 0.0231045264026084, 0.847991634117938, -0.0399279883164512, -0.179944322138216, 0.125704534431823, 0.544289216361839, -0.147053448049202, -1.06955894178303, 0.389778295805663, -0.0386269905933658, -0.0040235625872427, -0.0420075917906744]', 2.39); -insert into credit_card_transaction (time, features, amount) values (158040, '[1.93071818096019, -2.1262985915603, -0.562190042262186, -1.4455218532364, -1.82450252361202, -0.124037055005639, -1.45575427286702, 0.0155392181642128, -0.957051580803472, 1.62432324368829, 0.213048359491319, -0.303107164840523, 0.241484681918027, -0.515757461364103, -0.756392570310974, -0.0611778675400904, 0.0828598299394405, 0.971968833215396, 0.0220578874096559, -0.108266497907202, 0.172035792111545, 0.698651196919579, -0.0254000635636389, -0.322349843871649, -0.265073476103063, -0.0042389602053724, 0.0102038961761064, -0.0283918397818033]', 173.19); -insert into credit_card_transaction (time, features, amount) values (158040, '[0.162509218802761, 1.08802975471127, -0.479260243527288, -0.599334149389565, 1.07314605318904, -0.692843559943088, 1.03308275381776, -0.142439804949978, -0.151174590384665, -0.858677568184693, -0.74971150340878, 0.221683454733099, 0.816425535030794, -1.18375945087461, -0.308493788149635, 0.358094766225378, 0.244251032482099, -0.240543305875597, -0.154092859470752, 0.077918170415628, -0.346172244771247, -0.810638330925273, 0.0631315141210395, 0.390804827771353, -0.365837069660957, 0.122772471859586, 0.226677785884346, 0.0838988207694406]', 4.95); -insert into credit_card_transaction (time, features, amount) values (158040, '[-1.46807680106721, 0.467623126420621, 2.45083236371981, 3.20817626336865, 1.56031755775091, -0.929101125469001, 0.790742331611271, -0.208926196557915, -1.6909262621155, 0.637981618249925, -0.688561439677695, -0.0625944634063719, 0.0782913373552517, -0.322584496777146, -1.86626015516544, 0.022072553466161, -0.308665818393793, -0.716260989603198, -1.68950103181656, 0.199631019272556, 0.178867090541067, 0.441549696551428, -0.277427413056125, 0.939775631601582, 0.854239509749194, 0.131670153407918, -0.200910210928901, -0.142277778306537]', 18.96); -insert into credit_card_transaction (time, features, amount) values (158040, '[-0.554663867441918, 1.69598933859823, -1.11751215491975, 1.02949087125493, 0.177269366320355, -1.39145947157606, 0.711667832128233, 0.45522170735753, -1.43940361356781, -0.0763629328989788, 0.89993116461054, 0.789421333501349, -0.546852991522995, 1.72789128520947, -0.531925837041507, -0.587260606350829, 0.125424636881326, 0.309455955225973, 0.606178578326564, -0.405067450045686, 0.490366604813421, 1.18907777001833, 0.0497586618628577, 0.552244794856709, -0.91496791959085, -0.59421363851772, -0.091770616237237, 0.123215519009794]', 11.92); -insert into credit_card_transaction (time, features, amount) values (158041, '[-1.76293392713379, -0.0420766603235875, -0.585828673472461, 0.419884423094998, 1.45235514549013, -1.07393606458466, 0.378920224620948, 0.157588740953506, -0.49489869800209, -0.770181841878987, -0.739469022666234, 0.195870829186762, 0.842697240381394, -0.786023912161699, 0.419795631327136, 0.0091587169612489, 0.850120653607785, -0.0105339190085261, 1.21130647772201, -0.349613764650356, -0.314036678559929, -0.492210717579355, 1.20930297670168, 0.494998852989626, -0.410117916050452, 0.298732601406856, -0.13987742712738, 0.227970504647912]', 1.98); -insert into credit_card_transaction (time, features, amount) values (158041, '[-0.0441639798911216, 1.09760257862115, -0.926787159178486, -0.0654949702765529, 0.260679461168122, -1.25691893862939, 0.728401977120678, 0.147748308551425, -0.190933239023631, -1.20196357405327, -0.773128563327046, -0.370485092284141, -0.448474787024962, -0.394868511957962, 0.748157441057619, 0.158706252858903, 0.658645386224814, 0.739165590316672, -0.241773195410174, -0.188618207477022, 0.361979211952857, 0.972935637098266, -0.0485278626837927, -0.166174166003259, -0.348512492892967, -0.145310073569738, -0.0146182766926823, 0.0390811186475384]', 42.81); -insert into credit_card_transaction (time, features, amount) values (158041, '[-0.824361656394776, 0.738153775557966, 0.877998518925298, -1.40715195623649, 0.346294930358104, 0.458448638273054, 0.553576376107043, -0.0527763649874379, 0.981085546524477, 0.17871846817522, -0.0439714728804402, 0.313895261533906, -0.0903441662407898, -0.448423124156698, -0.174655441094708, 0.576735499437365, -1.31456583384812, 0.557798182305857, 0.443348021724407, 0.103890871339873, -0.288169801469596, -0.507147311558589, -0.161148185186428, -1.19487435638729, -0.0739794417946125, -0.258828922296431, -0.158334102168078, -0.109956373101282]', 38.52); -insert into credit_card_transaction (time, features, amount) values (158041, '[1.96625483088915, -1.53752348140964, 0.0243053303304094, -0.822982318869676, -1.60397314416749, 0.144384982394338, -1.63053698969458, 0.187319170048801, 0.708983410720329, 0.651032277238713, -1.16052174914491, -0.830562476265344, -0.0403551176648998, -0.78516558576119, 0.749786415317184, 1.77989603443373, -0.167855432634068, -0.498686097418137, 0.244213521840766, 0.170775184372445, 0.454294206283303, 1.15351758828884, 0.13360739886429, 0.601060331973767, -0.423266284386191, -0.122578046037241, 0.0310195519724147, -0.0117926489430764]', 99.0); -insert into credit_card_transaction (time, features, amount) values (158042, '[1.99629600233142, 0.0351417045860207, -1.75608476523766, 0.561395464021629, 0.05403683856977, -1.46016650143182, 0.214359028075343, -0.30973308887363, 0.766861742785519, -0.457655169974517, -0.501588071716564, -0.548707260035861, -1.19397518439081, -0.479896337484598, 1.07916284788365, 0.203264208736131, 0.521842251297097, 0.562263593416986, -0.504878930275455, -0.235087375541727, 0.203838501122883, 0.653543328575067, -0.0227829135917265, -0.108194162621486, 0.188169175317524, -0.0971691365269092, -0.0119832473075463, -0.0296145365046353]', 29.98); -insert into credit_card_transaction (time, features, amount) values (158043, '[-0.457777589303788, 0.798486572321378, -0.57750546940168, -0.747448840222225, 2.10762988654929, 1.05723194085689, 1.02837833793358, 0.405471526952467, -0.384498732900626, -1.34184348350663, -0.177522764625974, -0.799508659531795, -1.84487697413551, -0.45616078819072, 0.593325848387299, -1.38026916518166, 2.0117863066503, -1.83451224024327, -1.4970597069453, -0.275185231506541, 0.0827588213761465, 0.38856542006845, -0.144280108382553, -0.766350530839447, -0.2640333501296, 0.637221123841715, 0.116934620305455, 0.145000633495933]', 12.99); -insert into credit_card_transaction (time, features, amount) values (158043, '[2.04927596201142, -0.0490032104957632, -1.32290356639624, 0.382513045283908, -0.0691618382140238, -1.37476441471411, 0.264296910452165, -0.401378697764927, 0.619202987636985, 0.014370553501562, -0.714263967296096, 0.215226723532476, -0.0955526031496355, 0.523221493991925, 0.759162336303115, -0.210046954001096, -0.490049672569432, -0.0455446401694726, -0.237972624037573, -0.231202796562971, 0.251325507941943, 0.857701549835788, -0.0080011997298862, 0.079871663432282, 0.257027344310482, -0.111974569951064, -0.0229829349259743, -0.0567345504399878]', 13.99); -insert into credit_card_transaction (time, features, amount) values (158044, '[1.96174359236974, 0.007208771153347, -1.84440280072034, 0.250383138783817, 0.677941074473482, -0.263530471153249, 0.169870672522184, -0.0980095268189798, 0.134735092773077, -0.259575306826791, 1.03750659363432, 1.1562959489835, 0.788697494550978, -0.800281786954105, -0.502622842489987, 0.513588924794929, 0.126607196026686, 0.114828788842056, 0.366525997111099, -0.0050637961386726, -0.292246302737941, -0.825941956542132, 0.249277137229631, 0.149054557132857, -0.253030169833617, 0.171094043764246, -0.0669937279248558, -0.0351393081699311]', 44.99); -insert into credit_card_transaction (time, features, amount) values (158044, '[0.0369544338564517, 0.928230745931255, 0.278472478307022, -0.6181713562497, 0.552089585121511, -0.962948636926338, 1.09179132781791, -0.242033601750856, -0.210762353668994, -0.456567184108027, -0.750728493546413, 0.720437166366282, 1.12448339638087, -0.105948757158573, -0.471907240253493, -0.181979237867702, -0.441466642457278, -0.948212500306135, -0.137955858214439, 0.0372664862704724, -0.244174050641105, -0.470204602245057, 0.0647634158430095, -0.0609980753713009, -0.455773018004239, 0.143925761934615, 0.255991249852423, 0.0987592388082234]', 8.01); -insert into credit_card_transaction (time, features, amount) values (158045, '[-0.309247483098739, 1.00180543151555, -0.268309483740652, -0.764525748135804, 0.30736749719437, -0.584977839035211, 0.52192956943199, 0.430220407997166, -0.349112661676105, -0.451880996120532, 0.0940258508189762, 0.501754560402765, -0.541727087117274, 0.718390767281084, -1.04075934202735, 0.393813694690724, -0.629476587820414, 0.017703039556694, 0.383052416101266, -0.149658251674726, -0.201894687053612, -0.607547921314458, 0.0755701453189772, -0.518071279415494, -0.449754933102611, 0.154741641550326, 0.111154019729223, 0.0198264595129384]', 9.81); -insert into credit_card_transaction (time, features, amount) values (158045, '[-0.861206293696744, 1.03840094345116, -0.0331518723369128, -0.227256311042534, 0.769609492432418, -1.28917532680993, 1.08322060697039, -0.262949962431265, 0.0191922594439277, -0.771674877911098, -0.568950376387317, -0.927775252572413, -1.11075281664584, -0.794593032513192, 1.01384092783818, -0.0120384563152488, 0.685024783604102, 0.787607003775575, -0.0814872069408404, -0.11698872077387, 0.199215877877277, 0.815216801855523, -0.203098080565496, -0.10876863759699, 0.261067269158557, -0.0240388438237309, 0.17339120067972, 0.198801141968144]', 20.33); -insert into credit_card_transaction (time, features, amount) values (158045, '[-0.241626849940373, 1.28959858180472, -0.675589653785654, -0.593099148127863, 0.650708632277234, -0.633474241596401, 0.549143106783324, 0.422089887184249, -0.573524333475638, -0.904355130127726, 1.07846894840802, 1.09184884458219, 0.556076384963949, -0.615621009992703, -1.05386681770079, 0.583516991260149, 0.297428484314067, 0.201185829310297, 0.0033033562518628, -0.0490551327529463, -0.233169533810241, -0.66051166144719, 0.143389430904265, 0.676353174484033, -0.424787340651846, 0.0871579523421106, 0.0978955170621409, 0.0137272179804553]', 1.98); -insert into credit_card_transaction (time, features, amount) values (158045, '[-0.360654838129332, 0.860239140077136, 0.833754660217246, -0.813738756419872, 0.174019334423401, -1.78717448907624, 1.28810029926903, -0.394136592612964, -0.166884145380937, -1.02443143989489, -0.0930819371778737, 0.276586912712384, -0.220502040222885, 0.482987242076331, 0.365797234073995, -0.669498693569452, -0.236544429575547, -0.114863857577038, -0.550372372572943, -0.152980827438772, 0.410894791332264, 1.24272994902686, -0.277647658458909, 0.963613843902349, 0.113509161216559, -0.454053889841847, 0.140073907812585, 0.160139791806169]', 17.49); -insert into credit_card_transaction (time, features, amount) values (158045, '[-3.53061264689577, -2.88036025067832, 2.48195109539194, 0.303782237096385, 0.106190848692327, 1.59604326223454, -0.65241274174616, -0.071531605664762, 1.0583663740712, 1.13919412381556, -0.725774724313279, 0.229251375990574, 0.0095391553411392, -2.09053091334511, -2.18587516878431, -1.43005759226523, -0.589997329663525, 2.25432865405336, -0.169066252644082, -1.21209790907249, -0.667016139235043, 0.202018602813361, -0.576249320925147, 0.250359610108243, 0.568988161594735, -0.511194775072067, -1.25501508515865, -0.177785734716368]', 120.0); -insert into credit_card_transaction (time, features, amount) values (158045, '[-7.04967734500612, 6.22346067143712, -3.16210885614043, -2.93078263669895, 0.819725256439663, 0.907167432014108, 1.40949515131412, -0.316346097659255, 6.97153568748172, 11.2507301329479, 2.2539836102297, 0.286961107779601, -0.59256113251204, -2.82900272905902, 1.36281708448014, -2.26253952911196, -0.893503406472064, -1.25592328826542, -1.60373299502508, 4.58795856941289, -0.881544079669902, 1.20504394844206, 0.0899640482110283, -0.840246947557245, 0.43855777180815, -0.311546096646831, 1.71309115838051, -0.41100974427325]', 0.79); -insert into credit_card_transaction (time, features, amount) values (158046, '[2.03280521148798, -0.168190765778752, -1.19740790236069, 0.222570910849199, 0.0450715220504348, -0.632229879564578, 0.0144109738785781, -0.10256032771085, 0.4053759895739, 0.253956573307866, 0.598680787432403, 0.678560030317362, -0.644389038688123, 0.592825236760949, -0.514696437610703, 0.160241590234497, -0.563147167380652, -0.272859880216248, 0.492084245437282, -0.251979090847398, -0.26096010475533, -0.679069991790227, 0.301855495299331, -0.414057850402678, -0.322704645124882, 0.206222106217416, -0.0762715850543882, -0.0754284358975098]', 1.98); -insert into credit_card_transaction (time, features, amount) values (158046, '[-0.167328708744615, 0.502076965985162, -0.709248280147804, 2.25579829433559, 3.6076305178543, 4.32442509830559, 0.0167230489635649, 1.11206742536693, -1.45782426171965, 0.923083977421996, -0.93825461579807, -0.458739710733163, -0.398904425311608, 0.152294141167741, -1.30553933866091, 0.200734665852775, -0.538628486484938, -0.419112797255344, -1.20236012728672, -0.0119454230980116, 0.227399764661435, 0.539107130658707, 0.0571044265359236, 0.69271393766991, -0.450046241716516, 0.0627919859708296, 0.120056861862893, 0.111686189344893]', 23.18); -insert into credit_card_transaction (time, features, amount) values (158046, '[-0.93680125297717, 0.859082963929476, 1.72689963450359, -0.0290095443872069, -0.637650877659658, 0.390964874060823, -0.349630542134048, 0.652628937018306, -1.87298405265036, 0.438414407580365, 0.300511483751131, 0.622764293069369, 1.08783249743318, 0.190976536834424, 0.225631470433147, -1.09033863337755, -0.343087110161399, 2.16838063655658, 0.167883088615166, -0.238110339353733, -0.432455351083333, -0.797510216120741, -0.148756224400264, -0.421963063761307, 0.273705990076248, -0.601171207492508, 0.257915205607349, 0.0774009332947655]', 17.0); -insert into credit_card_transaction (time, features, amount) values (158046, '[2.22127820372406, -0.606952121788273, -1.08163741102949, -0.812094985770578, -0.605064504217847, -1.27659776047806, -0.285841705864116, -0.405988683176092, -0.491704396296431, 0.747800729002468, -0.566780159868413, 0.0276296278021145, 0.898440698637403, -0.267750013754306, -0.327325580132385, 0.718004819829924, 0.299590474079629, -1.99125619691934, 0.839432489506881, -0.0143138408481991, -0.0674357498789766, -0.11256806132735, 0.279088617023285, 0.0316343741291712, -0.147783149954189, -0.408919015437785, -0.0227827353980988, -0.0567106255960488]', 4.95); -insert into credit_card_transaction (time, features, amount) values (158047, '[0.140334742414108, 0.988536716225315, -0.39913377609429, -0.523617346642668, 0.826930136581668, -0.878958297900041, 0.937666113612492, -0.0782640919640309, 0.0453708461548768, -0.816716583438785, -0.758515857628307, -0.442860148532183, -0.640343034449782, -0.887126737244217, -0.172433734618616, 0.34844984073013, 0.419316581874039, -0.152017683604469, -0.242147742294824, -0.0205571594334353, -0.345192008527678, -0.878755970715596, 0.112053935430627, 0.611925790189547, -0.448163343997131, 0.111436880051032, 0.217763378815883, 0.0835685400772794]', 4.49); -insert into credit_card_transaction (time, features, amount) values (158047, '[1.98741635856846, 0.128297718328412, -2.17222660211076, 1.11094384247555, 0.884337849263309, -0.814837086085613, 0.770939534850372, -0.368249160134696, -0.0372762477405685, 0.356820413544879, -1.13526019154846, -0.357502387260932, -0.783769965767086, 0.944605702525518, 0.384289692996234, -0.309600368849068, -0.477193755203672, -0.269389977986895, -0.336905628926395, -0.187854210092217, 0.125253230408802, 0.285599029948867, -0.0498249382483703, 0.394706530777852, 0.474202643432056, -0.499132400391359, -0.0495449894196223, -0.0521092292615957]', 59.7); -insert into credit_card_transaction (time, features, amount) values (158048, '[-0.300214503768063, -0.162933334967322, 1.32280376377343, -2.1491562582558, 0.381065863994089, 0.420695885651061, 0.219582064290766, 0.0225290248874589, -0.986105706442055, 0.181843257650334, -0.0621327705881322, 0.125501422241847, 0.364053736803903, -0.409741505762836, -0.970637702110409, -0.596337336482429, -1.14063895094659, 1.64412959112626, -0.899804032264457, -0.381196919135105, -0.575682127897389, -1.15056464537517, 0.0713363197876743, 0.0299455931663197, -0.705442277995971, 0.0704700906509997, -0.0692445110013047, -0.07111458521595]', 21.95); -insert into credit_card_transaction (time, features, amount) values (158049, '[2.09644507153469, -0.97628501915331, -1.46967099413564, -0.933683538460257, -0.487283459784679, -0.822467214792926, -0.36957442003371, -0.195519694443509, -0.29032898064024, 0.801543153267885, -0.908640949075622, -1.26695290798688, -1.23151808465943, 0.277169586233122, 0.332156084126636, 0.960644268401542, 0.314319576797943, -1.66619766296907, 0.725841466824367, 0.0250899189457903, -0.0442838792750437, -0.401111799441588, 0.284259300269653, 0.548560319077711, -0.261505852847418, -0.451968848527957, -0.0572070505788601, -0.0429688784613449]', 77.44); -insert into credit_card_transaction (time, features, amount) values (158050, '[-2.59067914020312, -2.35241895396614, -1.7619579783047, 1.51625524879955, 4.53203680261981, -2.30853748647356, -0.965623926731461, -1.31056649496078, 0.243869709406191, 1.55446584399786, 0.138538627788593, -0.810841745165796, -2.80156387027333, 1.18790756444083, -0.277481165236819, -0.685625513197026, -0.354364602696192, 0.430311388125896, 0.87935192358108, -1.70295753996574, 0.713315849279896, 0.712502154921543, -1.28165113119589, 0.401344074019227, 0.0172723168803012, -0.237773722664883, -0.277590496289562, 0.487405951462239]', 1.0); -insert into credit_card_transaction (time, features, amount) values (158050, '[-0.513597802594165, -0.900232839465239, 0.948939260084391, -2.99525546951477, -0.410127710515561, -0.233223606751585, -0.0610858069325511, 0.0590393167763066, -2.32183656275408, 0.664965989769748, 0.410524041434846, -0.571771712453661, 0.0659077712868453, -0.343547555643415, -1.54247591534139, -0.157607814718721, -0.0308884208053827, 0.538091076306178, -0.502213441166136, -0.150921299869733, -0.0128573233440718, 0.0798718559156074, 0.102609192659194, 0.719569923844345, -0.110603106604199, -0.359399596808665, 0.0799190816076589, 0.143022081784996]', 91.6); -insert into credit_card_transaction (time, features, amount) values (158051, '[-2.13023270905677, -1.5787610410514, -3.18169207767389, 1.58934127730561, 2.34468346679763, -1.22881617450677, 0.56420619447076, -1.06631556579953, 0.179431262640725, 1.38374889535392, -0.0690767390556154, -0.819164044414501, -2.65579886961081, 1.36685207688252, -0.415556780821799, -0.564754087769728, -0.308750130676465, 0.640065275555576, 1.06815107645791, -1.97138383146265, 0.792487847206179, 1.52070117244493, 1.87644932367662, 0.259372910667783, -0.514925411724223, -0.35050775745542, -0.442688880779016, 0.420591471807596]', 1.0); -insert into credit_card_transaction (time, features, amount) values (158051, '[2.25128933702913, -1.56747961665041, -1.1070598754971, -1.78379937635581, -1.0040913843179, 0.0232423583873219, -1.28339247667621, -0.0007860213519153, -1.23932920190308, 1.72031788081058, -0.240128720740062, -0.563022207333322, 0.2154570346622, -0.287643782320593, -0.667349709543604, -0.047801968736155, -0.0766481917969821, 0.733799510311714, 0.410680159250432, -0.367735088827374, -0.155355252890769, -0.0146831024915894, 0.101382290890269, -1.12367561552489, -0.181764454384488, -0.130404525197396, 0.012031764639004, -0.0634510771239426]', 43.0); -insert into credit_card_transaction (time, features, amount) values (158052, '[-0.34115724595417, 0.964644552473398, -0.218134661888991, -0.730878570234816, 0.24526386085088, -0.585828250315211, 0.509356143248557, 0.455535300592884, -0.335946920521378, -0.458981005473889, 0.247058049490724, 0.478528671434084, -0.742229395067395, 0.76800348699747, -0.984676394798984, 0.30116596098137, -0.509819226998575, -0.0963832502607585, 0.255574101869458, -0.160263519787752, -0.185118558871185, -0.571858608991212, 0.112359243655117, -0.426056682340816, -0.488695524010144, 0.151080231497952, 0.112084019305846, 0.0194674519463468]', 13.99); -insert into credit_card_transaction (time, features, amount) values (158052, '[-0.119789318628698, 1.06133329649585, -0.603303593353997, 0.0259494895005489, 1.7279710086789, -1.73570033401657, 1.36606629010028, -0.794981045981071, 0.113322269027701, -1.18145363212693, 0.0783111965234852, -0.900299646256578, -0.319989168432693, -2.98287980655373, 0.958912337451257, 0.399436759779982, 1.61168549559879, 1.47471792424704, -0.300211695540034, 0.0238040231780335, 0.12766158215869, 0.857785088548046, -0.425815139879602, -0.227174403446773, -0.555734458726766, -0.224835852779666, -0.162686508077223, -0.183632786561089]', 1.0); -insert into credit_card_transaction (time, features, amount) values (158052, '[2.36926716042377, -1.24609902495935, -2.17382021749752, -1.84850321222334, -0.149322087315866, -0.329846187341586, -0.521771353353859, -0.288142582932259, -1.77465044320002, 1.77356208363264, -0.563013273032314, -0.468343011404381, 0.496816147126186, -0.0320190736121438, -1.49347090499992, -0.626719522782918, 0.0694869130661255, 0.395437286676759, 0.926968070919673, -0.37836931823471, -0.20718148422151, -0.0732488462453094, -0.0921750665800468, -1.37413090758979, 0.338942565013294, 0.0217975539250091, -0.0367176045092703, -0.0903231506057273]', 29.9); -insert into credit_card_transaction (time, features, amount) values (158052, '[2.0963637444315, -0.978955904771515, -0.379211111571828, -0.77742130947897, -1.20045750269603, -0.828603717892373, -0.867580597075723, -0.176196392995108, 0.0071312609131175, 0.681277843756132, -0.680377929643361, -0.125414108550753, 0.752852365944866, -0.544928384025883, 0.166535667978341, 1.25307051954704, 0.07382959889101, -1.5550449940279, 0.628577477890057, 0.068995989408971, -0.0213429643283343, -0.0941116585998622, 0.343448406248042, -0.0358042594702178, -0.485729513290804, -0.48864291418283, 0.0095653100134527, -0.0335264656714153]', 45.0); -insert into credit_card_transaction (time, features, amount) values (158053, '[-3.14198314601995, -0.795747772990656, -3.22872410771548, 1.38515758391949, 1.94493796528915, -1.09048597715142, 0.564201149815337, -0.6840797584683, -0.0635237340593889, 1.30770870918202, -0.246503874820104, -0.760471640699039, -2.62596286208287, 1.66760940504194, -0.446721804837327, -0.549807923718244, -0.117407677681424, 0.70025020216097, 1.21216567392072, -2.08886258992153, 0.795509070343903, 1.65601578542278, 1.69612661033994, 0.280425838319691, -0.65924286915628, -0.348070025252755, -0.19979202404733, 0.62481946422239]', 20.0); -insert into credit_card_transaction (time, features, amount) values (158053, '[1.78848211880527, -0.351577290238326, -0.285186406391459, 1.39775584209901, -0.616029821188238, -0.505693978321213, -0.300441301143996, -0.133206128840839, 0.779853952625029, 0.161292991484048, -0.810229646947379, 0.656901182838524, 0.909853966274774, -0.132602841182037, 0.703080536966557, 0.439654799071828, -0.83667925945936, 0.272857685597335, -0.78995335027845, -0.0196046254298708, 0.286271113531241, 0.820474202985322, 0.0485314358039791, 0.03118923011929, -0.0932808679886006, -0.574754094903624, 0.0452801628693278, -0.0127810764367538]', 95.0); -insert into credit_card_transaction (time, features, amount) values (158053, '[1.96968336046084, -0.0964962250022813, -0.710070699281692, 1.30421340111195, 0.304598537857831, 0.569519045247275, -0.213703634641616, 0.0492712198067681, 1.00191685868978, -0.0685682023356797, -1.66895410745299, 1.21642940014383, 1.31723175726522, -0.592735171005588, -1.22458160561029, -0.315873103242682, -0.351449862334455, -0.875942097212711, 0.40585131572622, -0.141201729848692, -0.549637802082017, -1.26263090270532, 0.369443191798886, -0.0229593685904975, -0.177258375063635, -1.0981111113625, 0.0594869852898803, -0.0263652727104336]', 16.28); -insert into credit_card_transaction (time, features, amount) values (158054, '[0.018341766123318, 0.727123659597603, 0.156352441826882, -0.776670951115936, 0.567948774391502, -0.5571198064077, 0.80772482580987, 0.0474891874312141, -0.121445590483331, -0.216823082825509, 0.347652735291864, 0.17402196719854, -1.08252320043251, 0.50584355611331, -0.916061024947777, 0.189681001620597, -0.661553281171852, -0.105519382949307, 0.265261373748907, -0.0961708359053289, -0.238069028621332, -0.5825375987544, 0.0307865328597195, -0.431211539235901, -0.527421246713587, 0.14995507065977, 0.23994518841151, 0.0809090456634147]', 0.89); -insert into credit_card_transaction (time, features, amount) values (158054, '[-1.01267295348477, 0.4682509695406, -1.02986952527514, -0.398862739545357, 1.98892162336445, -1.16420592840461, 0.623420039709719, -0.0217103407875031, 0.543361210122182, -1.48821877937244, -1.78226040854642, -1.87683133103201, -2.29543278829302, -2.43758977392769, -0.598346139188743, 0.756892243380234, 1.62278982462605, 1.23721813155852, -0.340814767872601, -0.175754753631895, -0.0700599332267562, 0.0716661135428761, -0.545344261056474, -1.24491362176541, 0.0679041714645703, 0.699314925456353, 0.305419867918879, 0.243688356985179]', 29.56); -insert into credit_card_transaction (time, features, amount) values (158054, '[1.52080142915274, -0.77820140092093, -2.33515777704217, 0.415627654807271, 0.472697155083894, -0.328008502571347, 0.541534652446861, -0.306690027602327, 0.806618660709988, -1.02148643126813, -0.915990997275203, 0.824147158203123, 1.54318501257345, -1.73477787851352, -0.159348204685902, 0.191607828026595, 0.708843071553992, 0.16848678962857, 0.31470047402869, 0.558102939924943, -0.107399848638681, -0.668475244865543, -0.145380535231515, -0.0290789200772619, -0.0024418139805457, -0.114532648235638, -0.0580588562981461, 0.0322838287423411]', 301.84); -insert into credit_card_transaction (time, features, amount) values (158054, '[1.87614518394264, 0.585367206769706, -0.693006909770134, 3.58682140007763, 0.687824651650574, 0.36097388059056, 0.19477600225076, 0.051334862224966, -1.31009188811732, 1.58139678231463, 0.614799679320102, 0.729202748739479, 0.279466933339481, 0.384981246767101, -1.58539088098162, 0.973020679561193, -1.00381206025566, -0.345296589289656, -1.04656984314445, -0.221730095974606, -0.216898472520202, -0.691472344504252, 0.373913158281579, 0.69114381887658, -0.263427253334316, -0.40058947549215, -0.044381451930199, -0.0436186223175986]', 12.51); -insert into credit_card_transaction (time, features, amount) values (158055, '[1.80888580120365, -0.699901907453964, -1.41192659426633, -0.101255637515896, 0.479881645809919, 1.04899977221234, -0.329081096024142, 0.385854690743167, 1.04604209477172, -0.162235115444438, 0.610352962401577, 0.6651570526803, -1.32432601827821, 0.591344127646303, -0.0351976285595531, -0.941151749922794, 0.316977202521436, -1.13917460285594, -0.195139696583259, -0.272403055743338, -0.0555865830838366, -0.0084155373014661, 0.175069087885377, -1.64714073823403, -0.286039921996293, 0.0215267139045696, 0.0024246770903611, -0.0761251316043972]', 60.0); -insert into credit_card_transaction (time, features, amount) values (158055, '[2.00736633378888, 0.197605490107496, -2.31292767230526, 1.01068461374767, 1.18453116200442, -0.560293343350268, 0.855922216171058, -0.417799102964725, -0.206546422783952, 0.317477735664009, -1.26250777273801, 0.200762021983395, 0.575368638578868, 0.665491142647104, 0.271658486354423, -0.256295239005763, -0.691988437367095, -0.300106202806184, -0.200621409248385, -0.0837983790833267, 0.117171975793909, 0.317473732195776, -0.117129341848675, 0.0412893239555221, 0.568106602898064, -0.479833437225246, -0.0424349490636987, -0.0525243288229536]', 64.02); -insert into credit_card_transaction (time, features, amount) values (158055, '[1.94395372643321, -1.66526597629775, -0.160880656915558, -0.650764232756074, -1.27218776786536, 0.825805683241033, -1.54479923020897, 0.233190415799586, 1.06182590455868, 0.398137307327396, -1.9860487793936, -0.0694452487071388, 0.825185005314729, -1.45179724494334, -1.23351732279866, 1.02952045707233, 0.261188656245176, -1.12018048572968, 1.13093310692688, 0.249748720111618, 0.142528963631199, 0.508749001765518, 0.0689424769735744, 0.0915231880054894, -0.22785765814095, -0.181669889198793, 0.042885410709068, -0.0170944627421974]', 116.43); -insert into credit_card_transaction (time, features, amount) values (158056, '[-0.913222756375777, -0.510744913327451, 1.27929140325769, -2.82017773729711, 1.31111267119318, 4.72016057949832, -0.411020771708426, 1.00301961695429, -0.326959949696871, -0.284111492030938, -0.517665514849398, -0.440042813876752, 0.140448639739117, -0.758424656728543, 0.779337500016846, -0.895773707612941, -0.919148299692307, 1.86279131492079, -1.90481348258028, -0.391045563574394, -0.168519199521533, 0.0904561733189935, -0.238458982635033, 0.666540047732917, 0.430271781181312, -0.24519088338482, -0.124393824530429, -0.177661275531115]', 112.7); -insert into credit_card_transaction (time, features, amount) values (158056, '[1.0708531479055, -1.75410024912809, -2.0721382931966, 0.548071494560761, -0.296132616690594, -0.711412209990822, 0.877419896822178, -0.470178538313481, 0.270720998143061, -0.0967304851571395, -0.677405440108196, 0.364450705523664, 0.675799371290164, 0.430161777139185, 0.690577100014718, 0.132336723466881, -0.533521141748383, -0.17964920225141, -0.364182532350579, 0.943922454178608, 0.527668292118363, 0.417766437467591, -0.401101369370746, 0.823041813051571, -0.0940491035346733, 0.696898546170088, -0.19268318358273, 0.0298726965962113]', 538.0); -insert into credit_card_transaction (time, features, amount) values (158056, '[2.11366540972298, -0.0625313670485518, -1.37761680582221, 0.239114892693589, 0.234679690120205, -0.779785813488236, 0.179683970482505, -0.295334897237603, 0.570966597658399, 0.0697200461409319, -1.38894221713175, 0.267747679856097, 0.341220675691468, 0.20158958437351, 0.0791675373234989, 0.107418734809747, -0.569733970464571, -0.641929306966707, 0.428297921497103, -0.188928828401778, -0.336504854106543, -0.826149148836059, 0.244729441383997, -0.711774224843256, -0.193430124019134, 0.241988913356279, -0.0709360557128, -0.0670972116220714]', 2.68); -insert into credit_card_transaction (time, features, amount) values (158056, '[2.17748950437641, -0.736223118899005, -1.26581931845296, -0.898160562192686, -0.576158295092153, -1.06449012422919, -0.375654755733691, -0.206660144809882, -0.619716142993168, 0.982149912261311, 0.983832872210899, -0.128171684071749, -0.851120292490032, 0.341596987056181, -0.889000964077639, 0.772553435713818, 0.276721634874781, -1.39710640084506, 1.01968024670217, -0.121797876029927, 0.0753031805703097, 0.209314721577517, 0.214219930869322, 0.0055294797093293, -0.0936291470680254, -0.295017703202381, -0.0443388735571768, -0.0762088661540979]', 4.13); -insert into credit_card_transaction (time, features, amount) values (158057, '[1.55662218761062, -0.943101169547192, -1.6440226162826, 0.273656608242931, -0.311297015730152, -1.08585405552726, 0.539228810860325, -0.2902085829919, 0.525913776016586, -0.0270908697932851, 0.871407119825074, 0.670776196540878, -1.11794847948101, 0.88940814684043, -0.473580664871551, -0.249273005358224, -0.352611506546353, -0.0707406827157482, 0.458629282352921, 0.201190725903527, 0.0527787623877599, -0.309905402533756, -0.0161881089425985, 0.0747400754226593, -0.136448051652766, -0.126490659219331, -0.10688022896079, -0.0314186477557069]', 249.0); -insert into credit_card_transaction (time, features, amount) values (158057, '[1.83604359298373, 0.361213698697015, -0.655592756317554, 3.73259504183759, 0.56033733833686, 0.6867672362945, -0.0094747917690079, 0.145688293058722, -0.925862324135712, 1.52360143185409, 0.276052399683832, 0.613274231957642, -0.0942139231736152, 0.166282664241039, -2.15016435455197, 0.601032023902391, -0.720450860719725, -0.102303684852575, -1.13101251168902, -0.237293879797034, 0.140196254777488, 0.487858669812262, 0.100059137871992, 0.703019309900319, 0.1002290400241, 0.044993597551924, -0.0344375238442478, -0.0510118427526211]', 22.68); -insert into credit_card_transaction (time, features, amount) values (158057, '[2.07213838086927, -0.0747559805958977, -1.1323991419929, 0.384651860390274, -0.0969873196893065, -1.13608531826296, 0.162990761328786, -0.309865398631069, 0.593612850892599, 0.0815749172496382, -0.920360302656867, 0.242443938900753, -0.187408658480414, 0.321802143770461, 0.0812469647377727, -0.0491422172062759, -0.325847267961774, -0.787442879183695, 0.203541367052077, -0.237021141068522, -0.302043489766016, -0.74906876715041, 0.325144726522631, -0.117954866926699, -0.288147096758683, 0.205993564877014, -0.0727743338036832, -0.0619964447660501]', 1.98); -insert into credit_card_transaction (time, features, amount) values (158057, '[1.98859021571532, -0.462435002608779, -0.587557230924636, 0.11017123090759, -0.117172508641378, 0.616844724306851, -0.767758002745185, 0.190371909919723, 0.878871498545837, 0.130784657849819, 0.349101127865736, 1.3660354858958, 1.22693772158787, -0.318401736341083, -0.328976516550753, 0.62099013357787, -0.993467394838053, 0.518741133882817, 0.197178081450755, -0.0661589877002234, 0.183824942046755, 0.736386058590645, 0.100992558019131, 0.117090970567576, -0.187269627297211, 0.556315857716198, -0.0190973181804629, -0.0543573824058145]', 11.5); -insert into credit_card_transaction (time, features, amount) values (158058, '[-0.765962670291498, 1.53491809806818, -0.868302627406612, -0.930869604809474, 0.780307847797014, -0.331711301740271, 0.811608847633188, 0.0645062745036926, 0.64882673174714, 1.13787882635058, 0.266847934858503, -0.0408876551033626, -0.976929475114732, 0.495991378236091, -0.0023198067971015, -0.128953415183282, -0.889554091869874, 0.895297132480328, 0.392475696785222, 0.490764814004673, 0.257964406088212, 1.14669726595361, -0.108220757836914, 0.275280807377137, -0.570435216468638, -0.224848989302734, 0.914426309723186, 0.552461059934263]', 2.69); -insert into credit_card_transaction (time, features, amount) values (158058, '[-0.245899782509229, -0.0827620938579728, 1.21420928337861, 0.287399623540103, -1.57178725891245, 1.0741216709421, -1.35279799970126, 0.771873177463041, -0.641025094870666, 0.63282187978159, 0.485118569179367, 0.571774518284022, 0.709486244611973, 0.0496629865314147, 1.49834619529287, -1.88355984025355, 0.382591003833595, 2.97505797957449, 1.69411614050303, -0.26789931266379, 0.16462551505184, 1.00510898765884, 0.209462737583158, 0.697287078574199, -1.44870822571487, -0.341444893716906, 0.182026131180365, 0.201904513138414]', 62.85); -insert into credit_card_transaction (time, features, amount) values (158058, '[1.91698236752409, -0.623713912063827, -0.577232823699824, 0.366288295344461, -0.469307786772858, -0.0456202979662577, -0.561924202810063, -0.0449573694570374, 1.20875443245996, -0.0437298815327138, -1.50245897451322, 0.502459563158338, 1.00901514846953, -0.43862546121085, 0.376987553939646, 0.441316037009794, -0.74125092120754, 0.132597874519823, -0.106590822227025, -0.0050327428122797, 0.217536387989648, 0.773685050414691, -0.0211668749904259, -0.684280116678274, -0.1427207964521, 0.668831911840868, -0.0236013138704864, -0.0439777340677026]', 69.99); -insert into credit_card_transaction (time, features, amount) values (158059, '[1.94798365241909, -0.266252073363287, -1.69035458571125, 0.534072776862618, 0.0378711580201823, -1.39938068607612, 0.566939381547449, -0.453209128501882, 0.461009214858657, 0.0935906236616408, -0.977401675201871, 0.0384101599545751, -0.705238397468891, 0.547014423457886, -0.374032271789174, -0.534972116160354, -0.0314292583972938, -0.763837166298141, 0.175812437295928, -0.129560109799346, 0.0024706745400765, 0.0309315102264677, 0.02174093520098, 0.0954832350297339, 0.130944280627825, 0.552425588398322, -0.114232311354674, -0.0653240625031898]', 77.39); -insert into credit_card_transaction (time, features, amount) values (158059, '[2.1106679202672, -0.0642292754328239, -1.35288875262305, 0.253614204403093, 0.198800901987926, -0.816760867435516, 0.174848032722424, -0.294696812142355, 0.578496004849875, 0.0722119549318383, -1.34751478425621, 0.247006058377405, 0.253639000630061, 0.220368367261592, 0.0830451977836086, 0.0931471667437671, -0.543202848135033, -0.652544515840614, 0.406060814585815, -0.197249280159364, -0.333803158951552, -0.820839719829356, 0.253804726054176, -0.6528495780223, -0.203907647075743, 0.238540033774892, -0.0712258024889534, -0.0667656915036206]', 1.98); -insert into credit_card_transaction (time, features, amount) values (158059, '[2.03858190118996, -0.08955411837767, -1.20381247011261, 0.195219985323175, 0.178605072225296, -0.565459170803417, 0.0883132225056465, -0.161190559388497, 0.241913599909149, 0.221415201376897, 0.72520740927674, 1.24642271767646, 0.479406746038521, 0.367405233467167, -0.631490197616445, 0.128653259477706, -0.652274249962714, -0.386235030809468, 0.511135195536199, -0.178484407194994, -0.25281349130724, -0.601019562358839, 0.279805146669692, -0.454192386515664, -0.275695009030426, 0.206866735466045, -0.069098074057509, -0.0737559039313933]', 1.98); -insert into credit_card_transaction (time, features, amount) values (158060, '[1.80177445180494, 0.392900146650338, -0.764728756168735, 3.65117947615325, 0.62361486211524, 0.485783941645229, 0.16928057973791, 0.118861011346272, -1.13431680009952, 1.55885926903576, 0.44707433228444, 0.366778481764997, -0.530725938020933, 0.496382585451187, -1.68792662937989, 0.836182813663618, -0.819173081929585, -0.362880374319654, -1.0591322778914, -0.215820446157879, -0.187039833837802, -0.6716725808213, 0.328963596879575, 0.621648001150195, -0.25515828386024, -0.351783951244471, -0.0518704877629955, -0.0420217892639217]', 45.38); -insert into credit_card_transaction (time, features, amount) values (158060, '[-0.397301829938534, 0.937969128694766, 0.19260164696609, -0.447221668743905, 0.763687410290789, -1.22371604276296, 1.41308353320128, -0.350034773814132, -0.323134838637991, -0.544366180893735, -1.30680428146014, -0.667290392605218, -1.38903101562744, 0.533267581613254, -0.73779222116137, -0.583628554593284, -0.102225363787679, -0.50798479494302, 0.0805805951811145, -0.254050466861325, 0.0582550960812433, 0.19986997785849, -0.31617354426051, 0.0701338211751449, 0.0519364843360323, 0.334074414529849, -0.071607107490044, 0.127574872131995]', 13.4); -insert into credit_card_transaction (time, features, amount) values (158060, '[2.06469236749068, 0.193408694015494, -2.76101478000716, 0.278346924701658, 1.14934597735104, -0.720036253641448, 0.597821706634258, -0.251793703557811, -0.0682234807975602, -0.195627696608758, 0.801785796751263, 0.300284623071794, -0.620847662978237, -0.457952610319925, -0.735993348022924, 0.0823938801163987, 0.549978263012657, 0.43057016077715, 0.332295629774418, -0.148589801028603, 0.0561150341531602, 0.258335302379025, -0.0883005333235351, 0.182712034363408, 0.391996497815793, 0.693194987507978, -0.109865682524321, -0.0627451984996771]', 20.0); -insert into credit_card_transaction (time, features, amount) values (158062, '[-1.42370397819228, 0.572465457584779, 1.09822708350118, -0.774047164402353, 1.09377763731995, 1.29415399308902, 0.204210977388848, 0.556054069929106, 0.231303860868837, -0.320949405870751, 0.768519073282095, -0.228708504845823, -2.04109709353267, 0.584103697229902, 0.822126111875452, -0.952882123879487, 0.341498131629051, -0.632408542276245, -0.206573077950201, -0.203519389899039, 0.0507363947725749, 0.253125237298422, -0.417934861266197, -1.54279990752579, 0.425507457955472, -0.0815234616494057, -0.0752547263517826, 0.163206416254446]', 9.83); -insert into credit_card_transaction (time, features, amount) values (158062, '[1.98845313775173, 0.0155463785550837, -2.05446862423836, 0.307062427764176, 0.482226554682777, -0.871033238214819, 0.270083021873564, -0.150762185734681, 0.222280835617012, -0.230374981805227, 1.15404728632482, 0.303632083267801, -1.06481994610466, -0.472607323584188, -0.541608360213101, 0.334733683039823, 0.551883735050448, 0.314664717040862, 0.27859282243093, -0.150125268137629, -0.151170981428364, -0.445142444705659, 0.190413040587063, 0.674193311206083, -0.108955445695942, 0.463384737267867, -0.101600061491616, -0.0443616975130006]', 30.95); -insert into credit_card_transaction (time, features, amount) values (158062, '[-0.090258092684463, 1.14139039856269, -0.638816930492043, -0.795632925050198, 1.06888714359649, -0.33206393095434, 0.84108370763587, 0.0825098789077009, -0.0572781518477523, -0.350121192504979, 0.674263778390358, 0.336462026838403, -0.255058276464882, -0.865091270593329, -0.873234947528042, 0.621130256041031, -0.0082325993800895, 0.40733973503873, 0.165414535188785, 0.150704167252117, -0.346932075109601, -0.827931366051785, 0.0534278827694109, 0.166250725681542, -0.360467649257552, 0.122152712203135, 0.323381732011365, 0.129386280724004]', 2.69); -insert into credit_card_transaction (time, features, amount) values (158062, '[1.78393840767865, -0.190894371893229, -1.06989673977674, 1.76946048833352, -0.118343566898157, -0.206228223305486, -0.194602220264359, 0.0343387299776153, 0.902740599165868, -0.307545997657614, 0.655545797908997, 1.07348912312364, -0.33565060708333, -1.3443237652253, -1.9374670387508, -0.225069983946257, 0.804970598945215, 0.754039698224413, -0.141720615895689, -0.144712320557656, 0.279606828890742, 1.14032378579156, -0.173734321596993, -0.0253093151548956, 0.397831435388674, -0.271393142154207, 0.0498101941227625, -0.0230016886453852]', 62.58); -insert into credit_card_transaction (time, features, amount) values (158063, '[1.82781261500287, -0.834576905584231, -0.621645544756949, 0.16909041609066, -0.660666282262516, -0.122512148342058, -0.593528596264158, 0.0333186916722233, 1.57636098632024, -0.249583361989633, -1.23012765564523, 0.133708837155878, -0.254354817711652, -0.134898284339224, 0.623351119138974, 0.214386707898909, -0.5256815903896, 0.294995114956702, -0.0651426791653565, 0.0005168862027209, 0.214600181948143, 0.593962087605712, 0.0655213087825487, 0.611863865125332, -0.174768612579088, 0.0927212099813462, -0.0104811116188821, -0.0207053864539358]', 106.5); -insert into credit_card_transaction (time, features, amount) values (158063, '[-0.208471673121375, 0.765746531432683, -2.43969216049611, -0.510489315057049, 1.59345592155506, 0.259208779185712, 1.42664875534026, 0.380298244346266, -0.79833639199878, -0.3393056050257, 0.851408499599706, 1.03116905210376, -0.12992712774525, 1.42114026616392, -0.461100156271367, -1.76148787938286, 0.52445934133305, -1.06760294003354, -0.492754264682229, -0.0470819988630124, 0.722246689473042, 2.20751418768393, 0.211029943326324, -1.34368487580256, -1.22555093758533, -0.0442763383692967, 0.457259147355167, 0.298485093693931]', 106.07); -insert into credit_card_transaction (time, features, amount) values (158063, '[1.9685750052288, -0.503187605694809, -0.186967160575634, 0.29692013050899, -0.608110998506472, 0.177803938604807, -0.90062500722356, 0.166651359270377, 1.58544636024093, -0.189579567560013, -1.1485729326818, 0.313805005133515, 0.189228879354687, -0.269033068560807, 1.03635841116761, 0.49197041691924, -0.75605615645576, 0.328640749985723, -0.268989696142519, -0.169288048191564, 0.0610989552812056, 0.31945968456597, 0.266263607045252, 0.399499985843925, -0.347813876971312, -0.421621004214798, 0.0540468896903258, -0.0212510488442206]', 15.16); -insert into credit_card_transaction (time, features, amount) values (158063, '[2.0421643018548, -0.349952391977515, -1.79074160699123, 0.395536913114032, 0.229197672466133, -0.707037798969294, 0.277954114644534, -0.347905142354436, -1.43480805202343, 1.28713879464262, 0.505586637786463, 0.727090373221375, 0.662904726803939, 0.694210139191587, -0.55268002082255, -1.55977061151737, -0.590340582084389, 1.39443525732114, -0.794836091535283, -0.531325754453925, -0.25375492460605, -0.289369065379604, 0.0395283232949813, -0.512523696355468, 0.222686158605791, -0.589403963487044, -0.0055160621338437, -0.0576773706489091]', 58.8); -insert into credit_card_transaction (time, features, amount) values (158064, '[-0.0307276060395299, 0.876928609719564, -0.859382625797533, -0.0627757362236442, 0.514044629885717, -1.32224687914059, 1.08011991984497, -0.43249730701895, 0.306170850395402, -0.482532125877761, -0.523779170219644, -0.348308141918262, -0.116448035449065, -1.00437643432236, 0.764250406646827, 0.0712868989842776, 0.336916405415481, 0.677530937479771, -0.189737276243736, -0.257430504093161, 0.206011154390673, 1.00345028088615, 0.0032128536867709, -0.0953780211001562, -0.293453871503418, -0.218245211970163, -0.589215794727545, -0.54663140666535]', 42.81); -insert into credit_card_transaction (time, features, amount) values (158066, '[-0.25478041067097, 1.10524619985592, -0.5041980143308, -0.31638040561217, 0.0819042645406688, -1.30576703194993, 0.720781474538327, 0.164112699387785, -0.0569017129101983, -0.493393326564251, -1.22545213563873, -0.365084245716588, -0.830087780528719, 0.920310175716388, 0.514557899274165, -0.279633324440794, -0.251826654196782, 0.310848000960627, 0.0318047703994647, -0.15437778167266, 0.411610383379771, 1.25883065929227, -0.0274290109519171, 0.0591158901778585, -0.790844169762277, -0.217807560500606, 0.373386201368361, 0.259639857981223]', 19.99); -insert into credit_card_transaction (time, features, amount) values (158066, '[2.0694111124574, -0.0944565161602246, -1.89463696576991, 0.0333005463213146, 0.552174164354481, -0.554415897363521, 0.254390621332373, -0.137200720847121, 0.553618938654337, 0.0842019321579898, 0.41046765719381, 0.333264020715734, -1.21659894502071, 0.97278022368361, -0.119789745118491, -0.225798825351016, -0.664271103213415, 0.491002510592234, 0.495878432087348, -0.284287436872967, 0.114826476814383, 0.396209068273423, 0.0045715343401154, 0.331236501786823, 0.374232304679169, -0.54531726230948, -0.0248717439015936, -0.0681164080490862]', 5.0); -insert into credit_card_transaction (time, features, amount) values (158066, '[1.92735618586021, -0.0320639091326363, -1.36972351849436, 1.07445917969652, 0.346600915782068, -0.341138405522301, 0.126181722807939, -0.0628143989569896, 0.0709944348559103, 0.495481881522249, 0.82366872738408, 0.465617792954891, -0.758408801833574, 0.842605102860724, -0.0027271488203465, 0.158410886047471, -0.787686662501673, 0.523570494051303, -0.400355259322564, -0.217466696982898, 0.321507040308478, 0.889613100159995, 0.0072119869514688, 0.693271132477487, 0.253799410151988, -0.477652488325716, -0.0135646390466795, -0.0510600274123866]', 35.5); -insert into credit_card_transaction (time, features, amount) values (158067, '[2.16114266476253, -0.854060029930769, -1.22422292720539, -0.755991920097876, -0.225583767532568, 0.208141568471526, -0.690957575795242, -0.0456654080011426, -0.45340033873382, 0.995486125308165, -0.462429821423128, 0.67711357509785, 1.27488392911981, -0.135983046893044, -0.52952782834414, -0.809933413618608, -0.933493430017945, 1.80881437328479, -0.0489505322286981, -0.465241130752253, -0.384093423101951, -0.503541853432988, 0.0935468918116679, -1.36470949389581, -0.202806044100518, 0.619664509245038, -0.0352184736221549, -0.0708506934753829]', 26.2); -insert into credit_card_transaction (time, features, amount) values (158069, '[-0.638750327733038, 1.16379757576115, -1.06638605171706, -0.738521186552809, 4.16902485877022, 3.31734792244742, 1.24586922992769, 0.584440792328399, -0.990152789182, -1.11964181485828, 0.113218483163353, -0.573182957050857, -0.737492764826597, -1.08083879630009, -0.237173110355923, -0.389626388984832, 0.77971922075232, 0.102585895454119, -0.506880070036051, 0.0503806091341384, -0.0886119424821012, -0.114731513291954, -0.671233043014259, 0.581011832384235, 1.28659545749539, -0.376848877928385, -0.120629980274248, -0.156792428479784]', 1.0); -insert into credit_card_transaction (time, features, amount) values (158069, '[1.52011867589579, -0.814491196215313, -1.69389648892886, 0.760903870377673, -0.0591250834615059, -1.09519862443895, 0.837516059259168, -0.533962953651456, 0.192359873345928, -0.0367122909388949, -0.72170418976513, 0.93513768610503, 1.18830936872834, 0.229681241210634, -0.157729244777629, -0.258794490953418, -0.359128436135173, -0.824553440898034, 0.0319933362507131, 0.430454126709364, 0.0254389537284521, -0.376030651530863, -0.0775235410776442, -0.0476972733639176, -0.0898828548764882, 0.156176611097369, -0.11902760346036, -0.0135240795679878]', 293.9); -insert into credit_card_transaction (time, features, amount) values (158070, '[2.08418395137894, 0.303908521995735, -2.98154514900366, 0.142018048669677, 1.37375102175253, -1.07533452220695, 0.955685956751364, -0.57587323352673, -0.277849590624981, -0.351621067503912, -0.701548425374395, 0.421199001457474, 1.52572019036536, -0.815979727463438, 0.290003317186343, 0.160865859035089, 0.307766856026333, -0.0557114593722326, 0.0818417065144045, 0.071418576700575, 0.0525267997822637, 0.197623751689773, -0.149178734450972, 0.0493895756270135, 0.453386015104436, 0.798005231408597, -0.121284959648873, -0.0464489281458786]', 58.0); -insert into credit_card_transaction (time, features, amount) values (158070, '[-0.343731551410044, 0.720867903950207, 0.459598122969709, -0.571910013492466, 0.771714901891993, -1.09997983358582, 1.02608611814309, -0.304518713012099, -0.275976038499654, -0.540643284882029, -0.830411287002669, 0.448358829680354, 0.626856135685181, 0.0464331210729753, -0.441222051332081, -0.259514363693083, -0.374452166012208, -0.866936769643486, -0.008213954349943, -0.134927240608947, -0.128718353942374, -0.310205112947141, 0.159531270113385, 0.004758201745015, -0.887340975274899, 0.0716782780087929, 0.0095292541970001, 0.222699848849322]', 0.89); -insert into credit_card_transaction (time, features, amount) values (158072, '[2.00016959883351, 0.766128039256629, -1.87039526394277, 3.53350417145155, 1.57286743145024, 0.174578087654259, 0.848128074497978, -0.282346483776415, -1.4621501632082, 1.48323505388731, -1.69599978968366, -0.0810134852943552, 0.827041786527786, 0.356109222849145, -1.22241275006295, 0.362259678641628, -0.794762423877421, -0.664584385582423, -1.15606939783326, -0.182054802131374, 0.0890507823409251, 0.32018157466165, -0.0846731790870023, 0.0516898867832329, 0.541006046331634, 0.180406004228705, -0.0789793657516306, -0.063674129496317]', 22.69); -insert into credit_card_transaction (time, features, amount) values (158072, '[1.88150027085014, 0.273816112432466, -2.60402128251999, 1.44802207078666, 1.09693138228262, -0.971803461812934, 0.960368734316088, -0.442158676032975, -0.101192741630322, -0.251601201601777, -0.613569203676707, 0.126107895882707, 0.0943376381598297, -0.626439463326391, -0.224659104293593, -0.316860960561607, 0.700827091061394, -0.0093983856381431, -0.450862367048816, -0.0207693115617597, 0.129221670511681, 0.366761198841948, -0.163769301175009, 0.531805799474019, 0.610572001205469, -0.420042708008352, -0.033006231301453, -0.0180038541198166]', 100.0); -insert into credit_card_transaction (time, features, amount) values (158072, '[-1.97814595416767, 2.64635049402622, -2.31529335583235, -1.6934941590907, 0.998281714705924, -1.10323942055503, 1.23622345402664, 0.153035173749095, 0.985647782188868, 1.66191523226598, -1.62466237781549, -0.422458941754717, -0.480340536985045, 0.470140914997481, 0.0460411152679054, -0.476200480815713, -0.602633337229558, -0.143433243743065, -0.0532807904455965, 0.781890858909877, -0.0365929267987142, 0.576927879032573, -0.181862881930305, 0.0426483217350015, 0.151830428737418, 0.0794291250441214, 0.5419842410313, 0.118671334779544]', 0.77); -insert into credit_card_transaction (time, features, amount) values (158072, '[1.91913455184454, -0.213459991046471, -1.29897589824821, 1.30483076904466, 0.175382146084449, -0.242623831918883, 0.0732764315539103, -0.0620597292290035, 0.5718617525157, 0.360673329287559, -0.0872083753252122, 0.491637975341428, -0.998848267287836, 0.474534681160918, -1.32646294288266, -0.212724882459056, -0.545179281644989, 0.465986841273086, 0.242646491713901, -0.24327106785197, 0.204190637063395, 0.75462977338881, -0.144758718493471, -0.483972030793564, 0.432671542928282, -0.365060270669454, -0.0018003018270572, -0.0646560560494987]', 43.89); -insert into credit_card_transaction (time, features, amount) values (158072, '[1.97043653588596, 0.0232671897061891, -1.57889189507548, 1.33066416293783, 0.223783578160951, -1.00691567359934, 0.435916090049396, -0.195043194787799, 0.233549103466842, 0.489086092842113, 0.424581774145237, -0.0201149032113373, -2.32217014896317, 1.12297448777842, -1.05686235643686, -0.418412247729906, -0.287418121287991, 0.135885378776211, 0.113196004865995, -0.396217498103411, 0.0843971342700311, 0.315080050368324, 0.0067090079020514, 0.0296902205643354, 0.361065183087688, -0.512155346309828, -0.0359444573779361, -0.0734515648845868]', 16.98); -insert into credit_card_transaction (time, features, amount) values (158073, '[1.8703414374326, 0.421265426411168, -0.523970931317917, 3.95531508819241, 0.254851417215812, -0.0437141511002858, 0.151889361530053, -0.0971800236987923, -0.654692521237274, 1.36169942980138, -1.42470061372104, -0.0316516480124279, -0.0065462432417887, -0.0208846651672863, -1.49430997243393, 0.383635170938346, -0.455850690431738, -0.62790386431443, -1.42289538635865, -0.276838485035133, 0.0928865521419743, 0.429187500573849, 0.0731369594422189, -0.0129837459169884, 0.115602913715351, 0.0981990511536669, -0.0255557429282221, -0.0480888658281339]', 22.75); -insert into credit_card_transaction (time, features, amount) values (158073, '[-0.243050688451976, 1.40789375870162, 1.50611478005943, 4.28067568232745, 0.985938562843297, 1.05146027138761, 0.669648609317906, 0.0889721494610659, -2.30187124816434, 1.25793456662633, -1.08240898120638, -0.0242207706812204, 1.62127712468875, -0.0545595966274106, 0.517243082801085, -0.0211377009360716, -0.0224980643624611, -0.40099231883392, 1.58519159637501, 0.444234359177754, -0.391641649438725, -1.22105025553885, 0.264886021441823, 0.411839545020749, -0.692307047366379, -0.193484821393766, 0.160741111803187, 0.183927157667793]', 46.9); -insert into credit_card_transaction (time, features, amount) values (158073, '[-0.0883906696269832, 1.0457325897812, -0.703413615304326, -0.802783837805217, 0.994594745702955, -0.308077915207444, 0.763648874495042, 0.156553735102884, 0.13425396978603, -0.315291542416744, 0.388011205225123, -0.346180582074604, -1.46851520304134, -0.62593342224556, -0.731689613945576, 0.704744590810923, 0.0303386309075489, 0.586521127271906, 0.206203366823669, 0.0743541965947302, -0.367136894899187, -0.943967800912328, 0.0618545021742439, 0.0464609637696135, -0.3895541474266, 0.131772696794208, 0.31657972699435, 0.124739613247839]', 4.49); -insert into credit_card_transaction (time, features, amount) values (158075, '[2.1165195274224, -0.060824070946816, -1.59491954141603, -0.0311329714902188, 0.675114162028371, -0.080999692209259, 0.124207479264702, -0.16030811165515, 0.215602891262901, 0.213127238952929, -0.0857668796331095, 1.23779664779035, 1.27563973429416, 0.179654670094019, -0.676353988908631, 0.440602125509111, -1.09650610509599, -0.057593109967187, 0.944097434637234, -0.101308798725368, -0.31536226117377, -0.752003151824819, 0.145991938746094, -1.35703967873107, -0.11017571628235, 0.259764079741058, -0.0694354956121309, -0.0817189536084626]', 1.79); -insert into credit_card_transaction (time, features, amount) values (158076, '[-0.297797045258832, 0.908675905559729, -0.314027921639516, -0.880273125101702, 1.68567934063896, -0.300726760338133, 1.2749065617148, -0.23921972170733, -0.369280654454933, -0.652169598075945, 0.376737695129232, -0.492210325985142, -1.26615698604864, -0.765064903838997, -0.818380932972477, 0.42673380767152, 0.0577295086447829, 0.691376745131895, 0.43605503811391, -0.0606303064950935, -0.175582969518696, -0.43264404855109, -0.255331958667567, -0.0115791058952027, -0.287894346560759, 0.262798076181858, -0.228093532681901, -0.0501784306677713]', 5.87); -insert into credit_card_transaction (time, features, amount) values (158076, '[-2.6602074511247, 1.8936211706316, -1.29260377955572, -3.38408034817405, -1.02316069888704, -0.657291959032157, -1.08973688801533, 1.90157454288868, 0.711239768530032, -2.71582721739333, -2.10997949575661, 1.62089995588236, 1.47057941860962, 0.996047820814807, -0.127820270233685, 0.774403128642233, -0.371125034994948, 0.0682067767622025, -0.214434082831887, -0.771323929651732, 0.112696894014598, -0.501869296098083, 0.168446636222551, 0.0290380643673965, -0.252142454911719, -0.847440858536893, -1.15541013660752, -0.158711687418066]', 9.85); -insert into credit_card_transaction (time, features, amount) values (158077, '[-0.265969438077506, 0.906718581276279, -1.82266370491374, -1.60895604663072, 3.11120817025141, 3.17050054404624, 0.313669688109596, 0.895223997339977, 0.10309419996328, -0.378379842304796, 0.168465923596973, -0.145475534840891, -0.424501219311756, -0.85875304093318, 0.181240747075413, 0.182828159042802, 0.335994641364543, -0.399791927265376, -0.422706284088929, 0.150024489293798, -0.351020705071461, -0.928440494534973, 0.144588705603171, 0.588682307504071, -0.379282074993107, 0.156865587563356, 0.217273441936228, 0.135748289526853]', 1.29); -insert into credit_card_transaction (time, features, amount) values (158077, '[1.55511747553783, -2.03532233318157, -2.22530657582974, -1.09876359849239, 1.09358606277707, 3.91484244215921, -1.15030983155469, 0.894398897319769, 0.0642651585179147, 0.582680805128733, -0.407682345636573, 0.128119077495033, 0.293379831260656, 0.0423498599023209, 0.897102511659099, -1.51683906365488, -0.346855717470443, 1.57853658310192, -1.2977198694016, 0.016532780104685, 0.142422277162276, 0.382624341664947, -0.111227147305119, 0.721073965123793, -0.0949422908509532, -0.0662146927717048, 0.0218665949151095, 0.0039704643905569]', 287.94); -insert into credit_card_transaction (time, features, amount) values (158077, '[-0.221261638764708, -0.103304153828853, 2.20542754773743, -1.12218827926237, -1.09267219125482, -0.0903064941492987, -0.471941550428012, 0.0721799600464207, -0.754543535878988, 0.0859490568391077, -1.02097872806516, 0.168427594790715, 1.72406017388393, -0.816449678763223, 0.813874879619495, -0.857463465109312, -0.522431136623004, 1.81671006312581, -0.929585623798286, -0.287392453056418, -0.0210638136205978, 0.505404435123536, 0.0070878667240081, 0.0857237645986269, -0.82712709270363, 0.648192267383244, 0.185237867923584, 0.185278236661975]', 29.89); -insert into credit_card_transaction (time, features, amount) values (158078, '[2.08402094416765, 0.789079638479605, -3.01718047834247, 0.795187108140692, 1.15987374997072, -1.84032321026247, 0.906778832378184, -0.5714474436963, -0.163618773274032, -1.07797829518101, 0.553297642647569, 0.371488367456094, 0.784767795239195, -2.61022113128385, -0.144966727305513, 0.169243128733897, 2.25104066877001, 0.297266560685705, -0.449659267664362, -0.0699977612270718, -0.0331642536405961, 0.162090670975431, -0.0095222703956224, 1.0062505607374, 0.388392087951558, 0.652258575487315, -0.0740953565804871, -0.0067569278284233]', 0.76); -insert into credit_card_transaction (time, features, amount) values (158078, '[1.99838560018673, 0.129986208297545, -1.64931100825586, 0.792388306861566, 0.161079372199674, -1.13954206370383, 0.156197342357292, -0.314807152984846, 0.693342533350202, -0.710619337071993, -0.380603727284313, 0.578258459535896, 0.675798348086938, -1.86051560741299, -0.470030344424595, 0.0160251202290032, 1.24960090085785, -0.0166639673022884, -0.204246738466864, -0.109638968119896, -0.0087047927811704, 0.315568331528921, 0.0225802743682575, -0.0575439433555003, 0.0995687108104761, 0.637543331506091, -0.0343152138519227, -0.02658802609948]', 17.59); -insert into credit_card_transaction (time, features, amount) values (158078, '[2.25976613595617, -1.47062749804758, -1.76727811214294, -1.8755881625177, -0.629435402687654, -0.293176926530501, -0.902011090475073, -0.118126255936916, -1.65728848385508, 1.86168826791702, 0.0767373089456427, -1.04510715235444, -0.517837924013802, 0.242347988403254, -0.313713833092114, -0.15857239143508, -0.039297671491883, 0.919154773701658, 0.196057687326123, -0.353099462234909, 0.0902240288026091, 0.526966788356112, -0.0140674327621695, 0.0810120330396643, 0.114534781799811, 0.0547860003552184, -0.038255241777681, -0.064719948751202]', 60.0); -insert into credit_card_transaction (time, features, amount) values (158079, '[-0.545212398316568, 0.491961263810459, 1.2245020289817, -0.347667522942493, 0.211771284121254, 0.33272698266724, 0.397083139847578, 0.194546122755747, 0.538195781139998, -1.60139881848552, -1.33765801215261, -0.0918011458980561, 0.0918307416066358, -2.00899752954312, -1.07420365607947, 0.177373735538439, 0.741228180955831, 0.543247690028306, -0.13400975214764, 0.0987896564955963, 0.13123878238793, 0.578831795655899, -0.460271252770885, 0.577854293320336, 0.976243116080739, 0.873923657778245, -0.0836754640305318, -0.0372597441362773]', 54.95); -insert into credit_card_transaction (time, features, amount) values (158079, '[-0.0008328572451291, 0.888961854898189, 0.372249860341718, -0.689232099351591, 0.494920556445377, -0.830088688313869, 0.975007535745541, -0.117785847499354, -0.446620811082917, -0.268802585677266, 1.00842668372035, 1.33393298770981, 0.806313977910539, 0.135673436601211, -1.20922589467188, 0.0339658565903343, -0.676804343775477, -0.393800746772186, 0.164423332039843, 0.0251051969422241, -0.192908302687729, -0.367926142102051, 0.0565732292012525, 0.0951637003831646, -0.501745069841462, 0.112045143118432, 0.249591320647151, 0.0899986122328242]', 2.97); -insert into credit_card_transaction (time, features, amount) values (158080, '[2.2583215130857, -1.34833411064532, -0.953865411782146, -1.61666133000974, -1.07946743349742, -0.627391155843994, -0.924967770849296, -0.273676714205855, -1.31159929281736, 1.48582189684475, -1.34701686789439, -0.397738911540485, 1.42243897106487, -0.607554671730647, -0.212843837572286, -0.293257978634325, 0.202675156242394, -0.460574134810278, 0.32422423267761, -0.289554492702726, -0.64625848559179, -1.45958472786715, 0.429094624499299, -0.729472130497656, -0.538527342702467, -0.639309157327278, 0.0150640406131446, -0.0374173325297163]', 52.95); -insert into credit_card_transaction (time, features, amount) values (158081, '[1.88270978276207, -0.203215603343795, -1.43003350113993, 0.491799248504603, 0.0694321963626393, -0.638357338202461, -0.0132510104773134, -0.082357905032932, 0.761526372827605, -0.488805909914426, -0.538535782427391, -0.152671488216229, -0.603813733709077, -0.901787142223031, 0.547011316250756, 0.600411416644901, 0.560009514153027, -0.331867502266493, -0.0357466366417133, -0.0372225662242964, -0.545341601500347, -1.72293844054031, 0.489095317382395, 0.436285184517016, -0.687529931590287, -0.116639320969008, -0.0584642948198295, -0.0038387702189186]', 81.0); -insert into credit_card_transaction (time, features, amount) values (158082, '[2.2039336854423, -0.747797057211488, -1.43918339058904, -0.994530400254695, -0.371544446983618, -0.826442473626299, -0.379018554350413, -0.183606275075657, -0.599667186469029, 0.980543537327192, 0.623728126488045, -0.235969863563173, -0.725204653978271, 0.306524795283782, -0.864496499423267, 0.890624428106622, 0.123639216980209, -1.26868660940973, 1.17446876377488, -0.10219103580892, 0.0496109167829102, 0.136933276167255, 0.161332322853014, -0.40638979922345, -0.0372370931151909, -0.269744009106936, -0.0447918127381696, -0.0800404212289757]', 5.51); -insert into credit_card_transaction (time, features, amount) values (158082, '[2.07432126412485, 0.14389739720641, -2.05346938698305, 0.362237265782979, 0.521694220635563, -1.08337985450324, 0.339651669920655, -0.293159185617915, 0.447426178736587, -0.406858354032851, -0.595006445323535, -0.202282673748731, -0.512597423293254, -0.780991692294195, 0.175817754080364, 0.212145726062603, 0.697601501036405, -0.187807040765836, 0.0441634374939348, -0.163964045824354, -0.246453351188729, -0.630256214714182, 0.222933119945107, 0.576435318904607, -0.0890735156663121, 0.451587233586819, -0.0890503053245467, -0.0361410535144938]', 12.7); -insert into credit_card_transaction (time, features, amount) values (158083, '[-0.548143851180123, 0.822260253758586, -0.380725156211691, -0.682493347641176, 0.503978106908539, -1.34706401890499, 0.761621572994474, -0.342549564947644, 1.26698005213032, 1.05502280950522, -1.20314655853046, -0.855941272632263, -1.78943689137258, -0.0142708841722056, -0.163404957666389, -0.0430205210036624, -0.560831109808515, -0.605741094524832, -0.226660389533922, 0.083310601085726, -0.455610981057382, -0.752369861696202, 0.0765051728663474, -0.167718863728392, -0.704190643707102, 0.0645098258752825, 0.30764451080339, -0.0031405174135306]', 8.99); -insert into credit_card_transaction (time, features, amount) values (158084, '[-0.407301501389798, 0.599187496319106, 0.67014941968811, -0.236033023222735, 0.797438884695857, -1.06932663520237, 1.07532557870956, -0.30724107859846, -0.541546651686777, -0.644759885572147, -0.803582917792047, 0.666352269529717, 1.11119947206464, -0.0240481612471989, -0.48261291388063, -0.551613881843007, -0.141960115790906, -0.796515933601656, 0.59561054762285, 0.130111707736977, -0.101881060031207, -0.255756487387776, 0.0141964899946217, 0.097553341006981, -0.370973367667514, 0.299048898807788, 0.0839627756264165, 0.16489980398556]', 30.79); -insert into credit_card_transaction (time, features, amount) values (158084, '[-0.237072882987776, 0.597910832266842, -0.172596091390882, -0.606132166698665, 1.67357890769623, -0.929743957599525, 1.40736556776356, -0.747654791675128, 0.150455311198472, -0.374031785970019, -0.494571992489758, 0.116972511950436, 0.789700774113428, -1.57591091457866, -0.392035642928224, 0.0756915077459882, 0.0382627569813209, -0.322707594717668, -0.150661570295444, -0.0489648439377985, -0.28326946069883, -0.349862651198065, 0.324385183627055, 0.589434932039139, -1.30042191590005, -0.118187287630564, -0.364489361775515, -0.12876546121133]', 0.99); -insert into credit_card_transaction (time, features, amount) values (158084, '[2.09824945538098, 0.228234723328982, -2.16714040318854, 0.245655702719036, 0.868877702880564, -0.645849790913187, 0.291724262842977, -0.317915728773504, 0.461236833290222, -0.516188816332361, -0.848877257139733, 0.362331304334737, 1.2551828906387, -1.00551992440391, 0.877245510935354, 0.398432894544184, 0.0370154867566652, 0.647367372876822, -0.14936707062255, -0.0565945810103014, 0.160369420076634, 0.630621443369791, -0.100925183783467, -0.0022217541687746, 0.405129936588162, -0.0961601084484463, -0.003347910900897, -0.0272299481118484]', 15.33); -insert into credit_card_transaction (time, features, amount) values (158084, '[-0.387600347784762, 0.175436361873595, 0.345720488165518, -0.634227690451022, 1.287967898755, -0.913605260526702, 0.651514658233317, -0.1074617050902, -0.153378237427606, -0.674477593947801, -1.51776112899619, -0.179975316044408, -0.0575058802032664, 0.155591225932977, -0.216837950599842, 0.080193421082645, -0.565630783915332, -0.570894103737566, 0.136256232065958, 0.0125142187678312, -0.252416753156908, -0.874697327263612, -0.0207816826802341, -0.726277594346789, 0.0591533396491715, 0.250699878065437, -0.0949525735234002, -0.0310354979088665]', 10.99); -insert into credit_card_transaction (time, features, amount) values (158085, '[-1.24768990124209, 0.258574818653987, 0.664601588398163, -0.706924067292257, 0.62724172827434, -1.06361941360236, 0.559785144542227, 0.0601835690905438, 0.258546847187433, -0.966875767056081, -1.26764806683635, 0.110571436150021, -0.433278256365815, 0.161993867279446, -0.567670691421919, -0.525199549629115, -0.0591756436558982, -0.513929503159043, 0.211420875090599, -0.0966974159226946, -0.0652445868956963, -0.193173736444832, -0.254527014630197, -0.0670897576134426, 0.0358721855019896, -0.125803706387639, -0.352185941632468, -0.166473857351358]', 10.0); -insert into credit_card_transaction (time, features, amount) values (158085, '[1.09005817592847, -1.61517462149508, -0.711285400444173, 1.711553924973, -1.12510981628332, -0.42838785782095, 0.0700808431066272, -0.138054034929766, 0.976149382964895, 0.0402187791408546, -1.05325854902609, -0.164431316827223, -0.618281516854788, 0.2664261075669, 0.920739069953, 0.554250295836756, -0.72607856618264, 0.533185951016063, -0.90373939641835, 0.610964972796443, 0.487811150016614, 0.505080531497447, -0.246588513068335, 0.0315011843227289, -0.282564424824381, -0.609395185264343, -0.0330880469647394, 0.0467473536239455]', 455.0); -insert into credit_card_transaction (time, features, amount) values (158085, '[1.94517387006848, -0.241569175062956, -0.246097421368926, 0.457002592176409, -0.577646756759342, -0.63147775701274, -0.34280872823463, -0.153340451065074, 1.0525046454637, -0.221808596889395, -0.507017816242199, 1.33657557323354, 1.57133815042497, -0.320969427532141, 0.538119804326634, 0.0657169606370537, -0.56950401100442, -0.463025217706063, -0.162884649896958, -0.106763572602661, -0.147477931271728, -0.227991247391173, 0.347586933704342, -0.0166408111847024, -0.399470291541983, -0.625793538646539, 0.0450018407042757, -0.0252246140384243]', 23.24); -insert into credit_card_transaction (time, features, amount) values (158085, '[-0.176098329170012, 1.15046129671737, -0.15812831391767, 0.756227431753437, 1.22377334394024, -0.265806068705763, 1.28243795759551, -0.101030561181443, -0.858309542063943, 0.17577875023774, 0.149867593381854, 0.339391446164429, -0.435790394360737, 0.695769257534579, -0.96250561109521, -0.869890683401475, -0.221390506695121, 0.31123458859643, 1.22686901128535, 0.094042016136484, 0.138920777072542, 0.671516801536024, -0.341435481249146, -0.539935988797998, 0.058850427388073, -0.362016657506103, 0.431183689553437, 0.229091353584809]', 13.27); -insert into credit_card_transaction (time, features, amount) values (158086, '[2.03415287317897, -0.0563999721090738, -0.892816077470864, 0.517532917514887, -0.373794757350399, -1.46241179829141, 0.173382401142653, -0.345780903105248, 0.55543838498511, 0.0813878377976955, -0.412029156978667, 0.427768737669155, -0.291999661212327, 0.356638052274153, 0.0399994049051681, -0.215081991071998, -0.118665670225141, -0.972814887580991, -0.0106306471366415, -0.257988374221006, -0.265330135880475, -0.644044227778443, 0.396531120191226, 0.451993456753972, -0.363876836152545, 0.170824565200438, -0.0718529614055622, -0.0564233633657493]', 0.89); -insert into credit_card_transaction (time, features, amount) values (158087, '[-1.67347859224627, 2.09736441055119, -1.15218250364996, -0.677685201245432, 0.984340414172702, -1.07562266222881, 1.05655515569714, 0.576071087840273, -1.10565562242107, -1.33014217241647, -0.889691880621656, -0.0532001770339381, 0.0978200420671897, -0.351065480929806, -0.440178978538398, -0.0761450901418398, 1.21090066505197, -0.0447371829138738, -0.12006948406886, 0.0681471398743792, 0.0359491468679062, 0.0651283022173351, -0.569491164576101, 0.582832254352365, 1.36086012745298, 0.84488460334357, 0.0365064488336446, 0.0246598386572959]', 16.43); -insert into credit_card_transaction (time, features, amount) values (158088, '[2.04898285988623, -0.809157412263293, -1.19863065081856, -0.707368616270271, -0.683809072666342, -0.514626119253209, -0.950765730680637, 0.0007066637476348, -0.201098871251505, 0.22521234099967, 1.14492711769896, -0.0748373523805822, 0.300909258462651, -1.94747469079178, -0.312168923631464, 1.89304740953025, 1.05531734065522, 0.0471170297000639, 0.530942430854705, 0.118985910066886, 0.296080882700273, 0.858451639569599, 0.0284410005525877, -0.487760730585029, -0.157783588388586, -0.0857620726217122, 0.0230679318613714, -0.0195537234091875]', 49.98); -insert into credit_card_transaction (time, features, amount) values (158089, '[-1.16198629433283, -0.487939610289333, 0.904127925716778, -2.22301250157102, 1.38003811967009, -1.16433447448122, 0.280990577022542, -0.104294490043446, -1.53165603409977, -0.323412650519439, -0.968318252230365, -0.784159614612916, 0.152972323596845, -0.188819828172465, -0.80056586662978, 0.765583228731952, 0.171859054565474, -1.79014479266086, 0.277835752558299, 0.300254554501421, 0.0910332076619541, -0.140182209560325, -0.252457996905965, 0.610698495978681, 0.93019298021847, -0.26327404898223, -0.0645582105115289, 0.0548755703683155]', 12.55); -insert into credit_card_transaction (time, features, amount) values (158089, '[-0.433889077243946, 0.800554457693849, -0.305057486031491, -0.970588032953791, 1.01851606966106, -0.262851475986909, 0.833047888990124, -0.0180597856725609, 0.0644846972925898, 0.193718585024979, -0.178957026390183, 0.416089939296449, 0.0095756058837613, 0.153093968542073, -0.972174670074616, 0.404863444829806, -1.06875207180648, 0.0912901858579476, 0.607229444378598, 0.0387884814494773, -0.323018518986299, -0.672736826166993, 0.0807835559952665, -1.11258777438451, -0.30034589338388, 0.1885896803278, 0.252166149381054, 0.116147280603481]', 7.23); -insert into credit_card_transaction (time, features, amount) values (158089, '[2.0972824569959, -0.122837377581949, -1.49433508003744, 0.0453884386749687, 0.446506575346497, -0.269671173220791, 0.0611900357318454, -0.121577760337433, 0.347684260640149, 0.242547157747946, 0.000471490289366, 0.799868619863082, 0.223425783169, 0.396046242933982, -0.587579889438837, 0.403646467333126, -0.934799205605278, -0.0286559466527968, 0.842816120488545, -0.17663594525776, -0.308526743594896, -0.780633207367095, 0.194339703658428, -1.0966393660842, -0.182098193310612, 0.245392637009632, -0.0754371501457788, -0.081112124469335]', 0.89); -insert into credit_card_transaction (time, features, amount) values (158090, '[2.04022698036619, 0.658167032476465, -2.3487897119355, 0.760713881935295, 0.654877582400217, -1.70245314609606, 0.434325669809334, -0.446553374295066, 0.344668574926125, -1.23910966579679, 0.429646187388238, 0.357713724979096, 1.04426080147407, -2.74139513460311, 1.00033307392637, 0.662443419583762, 1.76502281758142, 1.05022075325544, -0.777519740340114, -0.0915884423184959, 0.115642016200398, 0.596336920673733, -0.0536546823590322, -0.19838279863096, 0.27911496096633, -0.101090590032899, 0.0302855162472742, 0.0095794889283817]', 1.0); -insert into credit_card_transaction (time, features, amount) values (158091, '[1.99373726003452, -0.128551236105866, -1.21210779884686, 0.277867324408519, -0.072599845457297, -0.977883338490194, 0.0823197767094163, -0.206173711074434, 0.416070334233448, 0.176719786770898, 1.06998956339337, 0.865144122556398, -0.341132433944465, 0.730141816206901, 0.151610672797818, -0.050163913392258, -0.666130643785583, 0.397455026153235, -0.0701331750669008, -0.243053398794669, 0.324767308954143, 1.0401913437532, -2.11894081769692e-05, 0.0480036787816706, 0.183149230777361, -0.12138387454437, -0.0205936778891407, -0.0663924774679762]', 11.1); -insert into credit_card_transaction (time, features, amount) values (158091, '[-11.6373442487818, 9.0848328835584, -10.0612756405108, -1.09557785929482, -5.55419239170499, -0.0561926905982396, -8.49487653967082, -0.1933009134856, -1.9676981611826, -1.98497239267709, -2.5323207435467, 4.0255418758943, 0.411374104930579, 6.88131039796648, -1.5039953417143, 1.93370903532687, 3.11611940879729, 0.990513274007641, 0.0652677529649987, -4.23828977636867, 9.05864041259056, -1.99644529309411, 1.79782625063198, 0.111193497118115, 0.0961089492494726, -0.0714180539927687, -4.28261248876486, -1.05808292112014]', 38.52); -insert into credit_card_transaction (time, features, amount) values (158091, '[-1.06892760141129, 0.679531804648469, 0.496769394920854, -1.10897952955767, 1.78120525080318, 1.42859514307248, 0.565155374231507, 0.488640447428433, -0.442491627784629, -0.36558883735647, 1.45487714890164, 1.16339016572397, 0.223867215645962, 0.304549993892942, 0.0805396832700647, -1.1533923770563, 0.433267526309248, -2.09889428723114, -1.03189287730293, -0.143834738902689, -0.101692991122473, -0.0732590155041425, -0.0778990427242523, -1.73826612603025, -0.466285170297431, 0.324106203458366, 0.106841904127213, 0.183326479889934]', 3.57); -insert into credit_card_transaction (time, features, amount) values (158091, '[1.98983911549471, -0.167762601128678, -0.220172748471614, 0.438222040230277, -0.554844735846896, -0.639797879983998, -0.373488526050164, -0.147339565414892, 1.05522786124679, -0.211040134713208, -0.507349059861029, 1.33562329001869, 1.56721616377068, -0.32620370340295, 0.535211320906856, 0.0626675588977888, -0.570189451865006, -0.467762211343751, -0.155500347584958, -0.153846534119709, -0.161487052325711, -0.215788787332005, 0.367964890177989, -0.0178271556624584, -0.390981194520225, -0.623327737766295, 0.0493702941945669, -0.0292502160887878]', 0.12); -insert into credit_card_transaction (time, features, amount) values (158092, '[-0.945571368518438, 0.345248661102196, -0.104713006087022, -0.375220681723907, 1.29294656348924, 1.5020201731981, -0.268593298222835, 0.897182453812541, -0.578976354767741, -0.452630233812574, 0.969874257297121, 1.04823606402067, 0.41313439818138, 0.621214788828823, 0.565115248463854, -0.921622321105943, 0.577927384066225, -0.987319320929827, 0.288758252417057, -0.16256167796487, 0.250844899541348, 0.721966946636435, -0.131584611825962, -0.643500804193784, -0.963155428449924, 0.697784785409688, 0.0226496159612859, -0.0186477525200934]', 10.0); -insert into credit_card_transaction (time, features, amount) values (158092, '[-0.209248039182748, 1.21046148850354, -0.622695514067446, -0.902893326213596, 1.18818944175993, -0.281837134425201, 0.996983919657194, -0.0990396653633745, 0.337140466220848, 0.181288498260055, 0.652507690444982, 0.0486284040640577, -0.604548129186652, -1.08134386226685, -0.785732542280408, 0.556438402618319, -0.131996816515832, 0.433140761214962, 0.173297280670453, 0.335195668110087, -0.436383202740261, -0.867710038422543, 0.0157292701385595, 0.0668860011035545, -0.336403674983663, 0.104436032475606, 0.26002898699554, -0.032387403453054]', 0.99); -insert into credit_card_transaction (time, features, amount) values (158092, '[2.12688561612748, -1.12033650643079, 0.0412171574653195, -0.520921338665569, -1.43041239998944, 0.0318491089562395, -1.53857563764103, 0.119997478410251, 0.504754086055302, 0.83086289711949, -1.78090165980089, -0.440107733713972, 0.433077761028875, -0.515802819316194, 1.16190874333842, -0.338854218444129, -0.822684668921011, 2.01228175000271, -1.04100016339986, -0.56189718643089, -0.189582605382326, 0.053209387922822, 0.250125297174141, -0.7224056769836, -0.612606122269372, 0.659214522437881, 0.021935615860717, -0.0374890291062133]', 10.6); -insert into credit_card_transaction (time, features, amount) values (158092, '[1.96935560368262, -0.389883230467109, -0.860295217933778, -0.0691540389844665, 0.135502656346451, 0.569185284517156, -0.570246987805748, 0.128296357910764, 0.968137439068462, -0.0965435611373025, 0.354805148800534, 1.51083945074064, 1.54646829219618, -0.106936781341768, 0.382057082656561, 0.523140069573143, -1.28349787732976, 0.946080774368326, 0.215247563958677, -0.0353247654014468, 0.250311428018148, 0.866260100202517, 0.0072383603202719, -0.170872031855957, 0.063280242421168, -0.562632724884825, 0.0564978371889291, -0.0380977033897141]', 30.0); -insert into credit_card_transaction (time, features, amount) values (158093, '[0.144284134251589, 1.09701381060509, -1.27984817797986, -0.248399162918321, 0.506294047342374, -1.26029698830937, 0.969009356706899, 0.0716723841132748, -0.486967991748855, -1.19718257169359, -0.302282031039785, 0.677959653353355, 0.931052162282815, -0.820926142919542, -0.612156053806736, 0.177681206447504, 0.7250286079839, -0.354932607331892, -0.512536008426331, -0.12969062150067, -0.0199763616884816, -0.147434002706866, 0.349884271557535, 1.08153178203605, -0.746222866277082, 0.135989146805322, -0.133632375042151, -0.0189512121768577]', 52.9); -insert into credit_card_transaction (time, features, amount) values (158093, '[-0.902479634210226, 1.06456754642278, 1.73902148032602, -0.480557081508535, 0.0314875589730367, -0.363817899495278, 0.781053376846971, -0.439609582800063, 0.607835262754474, -0.0817150309896642, -0.602260850649336, 0.566248284545627, 1.14575037981274, -0.777811370224999, 0.525559254174712, -0.27114738682877, -0.64226912850371, -0.191716059596236, 0.382167300429845, 0.0241779080986937, -0.158733980848731, -0.0930980463870523, -0.112271607997907, 0.0392800682048323, -0.383921818964599, -0.669129153950408, -0.981054582144627, -0.593218626625402]', 10.0); -insert into credit_card_transaction (time, features, amount) values (158093, '[-2.3082093081445, 0.516493102417341, -0.465689627415325, -0.361627055738481, 1.20385129752107, -1.69198013493389, 0.831126471614565, -0.220078299250809, 0.133947881463859, -0.45390238400603, -0.321428030253902, 0.278990305672809, 0.870206623696915, -1.09812131966602, 0.584610964931516, 0.13356764643819, 0.33816186420952, 0.403840288740456, -0.623775450638294, -0.10110594924905, 0.144765254720549, 0.846619836996749, -0.57427240524202, -0.0222217951323605, 0.384077794559862, -0.160721315925312, -0.221768432854791, 0.23606970982294]', 21.71); -insert into credit_card_transaction (time, features, amount) values (158094, '[-1.67552857998417, 0.789513581650327, -0.144200820367981, -0.177578885583882, 0.367189376223954, -1.63713296563304, 0.618681684342823, 0.0472270149307917, 0.0309280894020112, -0.0555025112303673, -0.550045375047246, 0.188680932587619, -0.0345510673779806, 0.578769003699882, 0.66328298375021, -0.484036855709808, -0.0959406586240212, -0.0627669500446129, -0.0382383674992371, -0.432645476035113, 0.226635517892696, 1.27325858221429, 0.364075160135979, 0.440989083181606, 0.0390955556747681, -0.0957686566466375, 0.35789741608757, 0.107298306242012]', 0.89); -insert into credit_card_transaction (time, features, amount) values (158094, '[-0.0728698912035648, 0.794483211035932, 0.554977612465192, -0.557569649482135, 0.248454464677606, -1.14635090102269, 0.908535971754675, -0.105277483184297, -0.372240093731409, -0.246169446628157, 1.31273959452789, 1.13093504214468, 0.0452788551300183, 0.301762261814226, -1.16693675415454, -0.0722654900478796, -0.465999033855831, -0.461733859289495, -0.0082963914979424, -0.0169258088447094, -0.164664198163728, -0.340209712214373, 0.136421982557808, 0.547460822469341, -0.584690323268993, 0.0851112834151908, 0.243376587885302, 0.0944527549640905]', 4.49); -insert into credit_card_transaction (time, features, amount) values (158094, '[0.0076762342675182, 0.602594305469068, -0.346944258362736, -0.630931423287438, 0.537102720279461, -0.639662617786215, 0.953878169041618, 0.008705010827957, 0.240763866096902, -1.36100304416301, 0.521679806275906, -0.069937066555374, -1.418475364914, -1.15305078755537, -1.12579890375648, 0.353552293399419, 0.660586113180797, 0.908240870549429, 0.332585135742902, -0.0856111947097896, -0.122386560819535, -0.337306762812071, 0.0158253349827007, -0.495349777803418, -0.320215450071142, -0.160658216895598, 0.0369745959354867, 0.0614416220363263]', 61.16); -insert into credit_card_transaction (time, features, amount) values (158094, '[2.15929692637919, -0.920283439608135, -0.637066089279967, -0.912802464072116, -0.787560956950413, -0.261056085866641, -0.964008885733249, -0.100728899683998, -0.0489112509454606, 0.688319090935912, -0.705723104940489, 0.0861596228425579, 1.43715871889795, -0.736503236179224, 0.142668314499269, 1.0995716356906, 0.0551134679616648, -1.30800729622118, 0.420979136030512, 0.0865952466864251, 0.332727685032672, 1.06028550637764, 0.141628550941401, 0.67247754066917, -0.0921963402278394, -0.115733064794957, 0.0148910784904178, -0.0387728287144674]', 19.95); -insert into credit_card_transaction (time, features, amount) values (158096, '[-0.760820454007006, 0.771373118508915, -1.18205937887548, -0.840363740520401, 0.899014319077187, -1.84189477764295, 1.40246651726575, -0.0703127636718442, -0.614653352231739, -0.758855184747824, -0.963621443258745, 0.216288463779397, 0.0667266735314359, 1.02384298191477, -0.178536956902159, -0.601902716048616, -0.186093646034804, -0.430376500503685, -0.235896501288051, 0.082547212738367, 0.372180077851166, 0.918660692502304, -0.0422342629547716, 0.043406045696035, -0.269458277180909, 0.0864687733086281, 0.260017702307211, 0.14680080513015]', 68.8); -insert into credit_card_transaction (time, features, amount) values (158096, '[1.97897161253861, -0.12271942589058, -0.818335039918767, 0.437949366216083, -0.405756537761386, -1.22369453833558, 0.0599579047979109, -0.195545557354451, 0.319208864341505, 0.255995578532438, 1.37619779107814, 1.04631494416936, -0.621433962788435, 0.605794473697494, -0.64656236173159, -0.0496292982180108, -0.305156611300904, -0.496567567758861, 0.226480363475753, -0.26339181201177, -0.20615817388538, -0.524010360318269, 0.403332219604305, 0.535034931250825, -0.420794859830978, 0.145173663531665, -0.0771295292833862, -0.0654431228871805]', 1.98); -insert into credit_card_transaction (time, features, amount) values (158097, '[1.93684448474331, -0.459957053368673, -0.436654368362665, 0.266731859007182, -0.531146053282273, -0.135797893216158, -0.619705804906207, 0.154217241718442, 1.17721755098334, 0.0187772005945139, 0.433166713154755, 0.641003868045781, -0.799038135535103, 0.319363937084133, 0.12466749101519, 0.488390322821929, -0.830853302875314, 0.450556069140534, 0.294263149302829, -0.242283824119552, -0.151658903364968, -0.379608174808109, 0.317836324793646, -0.534196990416578, -0.454452184667279, -0.610169811420252, 0.0260697326757262, -0.0452457544574447]', 18.99); -insert into credit_card_transaction (time, features, amount) values (158097, '[2.13724038835429, -0.0767820880380639, -1.49953745402167, 0.171789674906908, 0.361412519393058, -0.619948570685321, 0.159668625276571, -0.2668268681067, 0.616546030792667, 0.0759534963100051, -1.67495891149597, 0.0826522950004897, 0.221711034128348, 0.217681086666965, 0.118560583888986, 0.198960273344012, -0.664590594500009, -0.527720051793603, 0.53834615359303, -0.194121966998247, -0.358436791059324, -0.89202155091498, 0.212811028669473, -1.00438021385273, -0.160149620411906, 0.260572172472175, -0.0720828416284717, -0.0706878727445317]', 0.89); -insert into credit_card_transaction (time, features, amount) values (158099, '[2.04387672336769, -0.0846674535145838, -1.17046565587378, 0.221744277516308, 0.0975798411436982, -0.699349269871648, 0.0941374882679135, -0.184303467952483, 0.258268734527393, 0.234205600915405, 0.723030270600547, 1.181885652391, 0.336264536122014, 0.392713651797382, -0.670224327610842, 0.17018457001624, -0.672234711203558, -0.307112913648747, 0.554358658703094, -0.185525474911905, -0.255666552293944, -0.620343779670996, 0.285449724897866, -0.327792936351933, -0.275322977020132, 0.197380355771073, -0.0728309121097359, -0.0728667314004909]', 0.89); -insert into credit_card_transaction (time, features, amount) values (158099, '[2.11661697421739, 0.13988800206923, -1.95384656993448, 0.277856936910916, 0.703663218772156, -0.540145171684285, 0.171286196442692, -0.19686759770259, 0.538975465110964, -0.376604706201481, -1.10632820495494, -0.0647437483990968, 0.141881405729279, -0.874788152684911, 0.251856817102947, 0.491800875002354, 0.25802583842177, -0.109576109857345, 0.2779795637991, -0.137902310275977, -0.404049926017248, -1.05691684611398, 0.266147493008966, -0.0631275766619684, -0.179437534902895, 0.208739723439506, -0.0636972154647032, -0.0365257073002679]', 1.98); -insert into credit_card_transaction (time, features, amount) values (158100, '[0.342793110429886, -0.287261030687008, -0.0933252341703406, -1.98418595427963, -0.439231421928649, -1.30291887895992, 0.953927075161979, -0.725135954950892, -0.24768179211501, -0.200636040787097, -0.604255406676331, -0.286895491866822, -0.101875221684159, 0.483180366917527, 2.2764595567812, -4.07312753230281, 1.1296607636659, 1.7207327520418, 3.05152389374649, 0.042896461353378, -0.139282440121017, 0.425724045137724, -0.0981341315100914, 0.0641253377323846, -0.189590036827337, -0.33438345492012, 0.0804654432333014, -0.0350896019737029]', 84.98); -insert into credit_card_transaction (time, features, amount) values (158100, '[-0.0193845230578541, 0.0813437116183215, -0.123084684200039, -1.58005600867001, 0.93049143996304, -0.103879633803591, 1.14701778970959, -0.320478472201595, -1.5751004504366, 0.500310713256511, 0.0489999403243191, -0.0850124452179171, -0.321662537639655, 0.216845786602834, -1.57096092005955, -1.89677815738097, -0.35518412949643, 1.26978681811476, -0.295770640168619, -0.375383949353751, -0.285242347633549, -0.204706377164344, -0.0997544452359128, 0.294668911114002, -0.346887294312847, 0.419339624073865, -0.0716772597504023, -0.0581369955977684]', 47.61); -insert into credit_card_transaction (time, features, amount) values (158100, '[0.077338945991989, 0.78043299830265, -0.640003489208689, -0.143651965877503, -0.0497355010155829, -1.02703426150194, 0.8402653211067, 0.050636154370072, -0.308546339817244, -0.747207653142655, -0.772159076419779, 0.74030875278531, 1.20104953565085, 0.463585501741128, 0.457891359104055, -0.26833922711374, -0.340616230476837, -0.0014976789631764, -0.172396361044441, -0.0498765453656851, 0.455633963603872, 1.23155707232782, 0.0981427884553025, 0.0398010447751219, -0.216981162458766, -0.15344982684261, -0.0987349005751729, -0.0655036905744334]', 85.25); -insert into credit_card_transaction (time, features, amount) values (158100, '[1.93948896381013, -0.469871527616659, -0.355205262153049, 0.312484966808565, -0.530844012474013, 0.102405750625639, -0.761942355112931, 0.179919771659607, 1.20508337731698, -0.0021177589180198, 0.713071855093997, 1.10803333132093, -0.0234264573552982, -0.000430513415361, -0.177394465297379, 0.289598893202039, -0.755625472908917, 0.724243366446333, 0.0547387499636053, -0.185511685032334, 0.241436059282215, 0.875160788592062, 0.147748254005445, 0.786338120445995, -0.115365554239363, -0.268819395548443, 0.0325550531980302, -0.0391637294338968]', 9.99); -insert into credit_card_transaction (time, features, amount) values (158101, '[-0.265021807974238, 0.697274956114369, -0.688388679669962, -0.869974136845275, 1.64982583027218, -1.17166941315847, 1.11046674296122, -0.162572723194628, -0.153901002016783, -1.22908199068775, -1.1797801547767, -0.824489707332643, -1.09346658575399, -0.84617004621226, -0.619360685699456, 0.476813991349286, 0.348184745220955, 0.0947197243395556, -0.648285495929034, -0.152868525317181, -0.0981157713555551, -0.38299439991256, 0.0410077863271335, 0.354661677272148, -0.745871456918168, 0.104786055970378, 0.124732442157054, 0.246742116830861]', 7.32); -insert into credit_card_transaction (time, features, amount) values (158101, '[-1.53465908150274, 1.83862678673387, -0.225463045383317, 0.627528112515424, 0.0814690901189145, 0.158890236656975, 0.778377446016933, 0.292274706503078, 0.587254266549266, 1.48982586201722, -0.0474177881095055, 0.347026558632778, -0.582561172291179, 0.018768806241311, -1.00934320804571, -0.67532281831666, -0.299188674044467, 0.622987891314272, 1.11996841402817, 0.492633683372837, -0.0403318229394232, 0.861166426769615, -0.302101170870214, -0.489750077901004, 0.105817068631284, -0.391771931245495, 0.190892164946714, -0.32077338487077]', 50.0); -insert into credit_card_transaction (time, features, amount) values (158101, '[2.09053781353743, -0.547143517118784, -3.29258777410779, -0.537067214986212, 2.5665972963547, 3.09317919064836, -0.188776529132601, 0.555901723556236, -1.36214212417098, 1.18274884948291, -0.223201049388393, -0.12555819244218, 0.0041256952931892, 0.868090946799658, 0.454891850807344, -1.9839575136078, -0.266557366563513, 0.809412645089907, -1.28246339681873, -0.533037281324756, -0.218750585962745, -0.255323010400983, 0.0725633230485925, 0.659471849256448, 0.384969116239264, -0.466116121958146, 0.0054405674281493, -0.0582073378255668]', 45.2); -insert into credit_card_transaction (time, features, amount) values (158101, '[2.06360645255844, -0.0080988959583393, -2.1840391122884, 0.253442875798909, 0.981182324095781, 0.206991589632901, 0.0280561470070728, -0.0380252177049686, 0.450482617226158, -0.337788933248111, 0.216127637494887, 0.935351993727404, 0.771354281599726, -1.21895805439434, -1.16659693185526, 0.296451667504787, 0.27291902563184, 0.661789895473593, 0.556792128241113, -0.0651142166440278, 0.0566303460881829, 0.455676732574935, -0.132063635577595, -0.349217482142836, 0.343083153233551, 0.738252366421223, -0.0561431944882507, -0.0558538496660642]', 11.5); -insert into credit_card_transaction (time, features, amount) values (158102, '[-1.27296375104223, 1.88699037994696, -0.756176621991244, 0.957220735304354, 0.385955420695301, -0.805606952243749, 1.00840441500542, 0.145876223297351, -0.634180010522493, 0.187680483544885, -1.23468672343011, 0.338744163777208, 0.319722725289077, 0.664042348234363, -0.365579616732331, -1.08212770506513, 0.338923039821917, -0.386382316018162, 0.799782946402919, -0.228475712430853, 0.177829637118788, 0.707880143356651, -0.142214885799575, -0.0535052591581526, 0.0497312265199262, -0.44020018233763, -0.463285623672708, -0.137061202222681]', 28.4); -insert into credit_card_transaction (time, features, amount) values (158103, '[2.08843181991689, -0.597663861725287, -1.60705924591156, -0.525817581719043, -0.141958731875097, -0.82804042526325, -0.0067765119945119, -0.295739523510996, -0.859592029815763, 0.951878959883511, 0.46821198409591, 0.650193689374901, 0.364914501521249, 0.632864186690209, -0.169142137730976, -1.41299775757809, -0.616786348629523, 1.45928476554466, -0.296017349636417, -0.530784952308861, -0.449200137458304, -0.870255972295054, 0.195789250071528, -0.557604037544169, -0.0947448225683981, -0.485496188727246, -0.0158361016860141, -0.0568455100591142]', 49.6); -insert into credit_card_transaction (time, features, amount) values (158104, '[-1.39764319371123, 0.682598729893661, 0.919836071529467, -1.71342789904981, -0.587311399409774, 0.322204267331025, -0.651363349910599, 0.959821384620919, -1.36961163945888, -0.570251734356641, -2.10664646002848, -0.211185450556423, 0.818009377711385, 0.122351819547098, 0.104790088977273, -0.62483644112955, -0.249740803889989, 1.39273780442764, -0.565480027598169, -0.601613790850221, -0.526418710070052, -1.42073393434623, -0.113685596832628, -0.005276844422918, 0.210579362064958, 0.397052768195102, -0.361447905737923, -0.0898720733709888]', 18.0); diff --git a/pkg/udf/pythonservice/demo/dist/detect-1.0.0-py3-none-any.whl b/pkg/udf/pythonservice/demo/dist/detect-1.0.0-py3-none-any.whl deleted file mode 100644 index 9e78c923f4ebb68700ea6bfd29e77908e6147dab..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4152 zcmai%cT`i`w#EZUZ_|iUBDh z6hW#~=~4v*9^Uib!|OecyZ4x5jrGU;eRGVxzcts?ff8Jz2LJ#h009G8L!Ih{JM!l{ z$a&D7hqZ^T4Z=&r!UExf@UpNFc6)f&0Ni&6^AT$JMxRdzf~%QXfEx@94@Sfohju98 zH<}_qhjnEBz!L5f40r0`L$cI5!b1=@*=0O)VSvv6YyI$(HGDPHo#9Lb08CQ?0CX2x zHnv{2)?Nr#mvga*6sRjFhBo3L`NuWQ6~gaiDdJqyLWi&IWP8SH1ofP^oYj>$_@+}& z4|YI2KA$P`P(MO$h_mx*r}G7`WOfeQowaP_{FR!yxwGC|v~j9X;CY+$FJjzs$hxiV#(Fex&n2B+5an{x!L70ta- zRCv}k`2)RxL=THn3S||!pGj)1Dk$z8_AdzCRxdO|Pp9F~j>b`Q{xuI@Nza{nDU|rD ziQA*=A{C|;UPr&#Y+lxwM624%B~-9C^qw{?1+l)Hk!<-ZpdbRCd#2c;zW(W2SBm$- zn&&%H{>fzMo{WjGFWRofwC{EcVf&F*u}{8*MNnh8>!$OjCf`^DeV{b$R~6R;fhh)i zs}DfITew*7*7a;?1<$;kaQEzDkKM;mjOGZWJ_f?;xuaoQu9siWm#XEN<4 zv2lDkFIh3E3FRUj39;{^|H37j0i`v{q1{4$;w!zMLn^*05=#U!E^)I$IdsDuTUEA6 ziTgfXUeyI#i5qnACVEmX#(MJ9z9?xgPRit-I|NGGxx(w8BnP0d{sY^7JA1T-kJE2T zcGzqRD23+3`OV*cc~E$*$n;5-iqAs8Fr(YTP=k>$T6VQMwvMD0CsA=j6T$<~e8Y*V z(^f|_i%f013q-V8wFmlO>}t45UO0iJliNUb%U8at$zwl1M;MA#-KyqEp$(|wX&<4d zKtd9%-h`;#`{wSBLBZ*1vf6o^)Az;eXPn%*=b-tt1MNm(p;vY_O5=U|>_VMrHs{y< zNh3^lqfzh0N{I{MsTvAVQonyrL{V#J`WgCEmht5e)7 zN_hM!zj%*jK?sNzTL~cKXLxKv5}BdL<`oyV&vWFLuL&e1V5JmiluNzi-Y_z zvi{DoJt*zhs$7&v{oL(MR)XhuXud;B`V6?~7}#c2LL!z(v0r_44_zd!R!O;u?&p@& z4Q`v?@0lQrzfWW2S-|_)0o+O_#bs?TCKe~W5E9@~qUmJA&XKGVsXE%3P2yapV%T-> zsMMQ1gOJ|H5Br>1%H6Jd`G+3B_L2Zeo4JtE+meLBPl<}X?1kd0{~ zDJsa4C*cQ#4~HR>H)nEemo=4k(~gKp zdP!H>^d7gh3gm=mt6HV$Gw$A!YyMhksy|Op>z_`svWQSyCuY-Fy`J;HJ}+$p=0y_N zN3y}flLGwMO1`pRMav1u<1XgEUl&5^zpRG5YGlkOZ^$`2a9yNW@@cWaV}uGkou5sz z)X3N@qtwyT3Gy04IgWmyZsSd0+Lxf6Ra~v=Bwc*;O~C^%7J%BK&lXJ1tg}i|<}?U< z3(K|H8R0X@Kw#e+q!@;m?T(t$y=x14LM0Sxn?uS%yXaL@)hEyV;Nb`TFY)=soe0D6 zw!En}oCT--Xa0ET&{507T(F@ zQAGvZ$?o*~03uA=n#(tGfwZIVjsoI`H8h_>XNViR_P5s+W6|-!c8l`)9^2Fx-RZi8 ztYKp1`}Jc20AP#|0ARl8PR_12woVql2rmZdzbWx(|D-3SkIxtXkM8c8|CSxh~SUxmP9E` z03r1_UjajMS{;TaqB%r|Ub-kv4!{Q_ZKbZz;X(lFS5P$Bg`x3N2DeU$94FCisd66T z%DMQV-Rce>%wFE;ojw7=`giJ-86+-W?g=U6E8~RveTC~whUQu~TdRCHSoRo(?*<4K zu!t5Kz5MWAR4Ef{+;!Eh8yoUhct0XxGzU4CDD&oo>)1%dB(XSWs>?_`HK6cA)G^qP zB+*~x+ac>gx6{k4JiIQxO5Y_aSX(u{rxeIc`&)y=_)D+kXL1F{l-a8?QxZ1Da54g% zjRTPKR)rhIt!AxDwOQf5BEh3i-r}IH)T%T?;K0-?Y`F`uCs@(ne&VCIe##8g^GV4OwFW~hQ6qr%*mqivG{Jx zY}R5q)4Q}5>N?fE`DrWuim7oseP=5#dr6qjX;#lyQK}0h|_w^ zoM(4>g;ROmS+)?ka_IzfxnnDyMX@rLFmw$y;l#=zGL9UApb$bHk%ByaB> zi%)Yx6=}hkXXDd~O`GfFX#4e+&2dC?bR2nbFRyToa|Uf+dC+#^GL|~L-(WL?b^HXW zaB}n{CP>jSD$c3$grRRduu>kohhEm5-745;C?YD1XnT~z9#NU+D|hw@b|~B+EnT%e zxmBW4Tpq=)ZkBA*aY}G;V2C}GH5Ab|UJ*Y(C}hs#+Ijqaj};OV7CnD$5T0H_2p2n7 z5g15MSxs3_c?Tv0(Sis0Mx+`J*2D-3 zM~e`b=njYuNT3EJu=8Zy{UX>s!4XP6Au5k~$a} z-FJ$^T7Zqk_aep0QrrB1JOh)_UjvStE8~Qmn`U&FtdeFVGvQ%v8dUo4y;HX+WB3v# zc-H}@j=BLV-1={I%1c_nZlWKZ0tRoT`MHe;54Af1!*tV3dkdLvdu~VBzu36$WGNdb zX3_{mL=YEl)Czz>3luzIHiu+YU}VEOUa6FerTKHUH^S%0B&r~vFXl7y zVQBfMWMT+x1zt@sn`9*#;|nz93w`Bc*R07RGL7|^mQ>RD^H#(ahY^ap@S)n{V% z-x`t8AVWZDag9Ql8>@b9qSEqMVe{WI)e2K}A%yNxbLN)&%A>)%ZEJMnin{U7lyUhJQV Ze|t*@N_ak{|MWC5;P~8-yi`AT{{u=1Lk<7{ diff --git a/pkg/udf/pythonservice/demo/setup.py b/pkg/udf/pythonservice/demo/setup.py deleted file mode 100644 index 8db958bbbf793..0000000000000 --- a/pkg/udf/pythonservice/demo/setup.py +++ /dev/null @@ -1,12 +0,0 @@ -# coding = utf-8 -# -*- coding:utf-8 -*- -from setuptools import setup, find_packages - -setup( - name="detect", - version="1.0.0", - packages=find_packages(), - package_data={ - 'credit': ['model_with_scaler'] - }, -) diff --git a/pkg/udf/pythonservice/pyserver/requirements/base.txt b/pkg/udf/pythonservice/pyserver/requirements/base.txt deleted file mode 100644 index c3d724172e0cd..0000000000000 --- a/pkg/udf/pythonservice/pyserver/requirements/base.txt +++ /dev/null @@ -1,12 +0,0 @@ -#-i https://pypi.org/simple --i https://mirrors.aliyun.com/pypi/simple - -grpcio==1.59.0 -protobuf==4.24.4 - -numpy==1.26.0 -scipy==1.11.3 -pandas==2.1.1 -scikit-learn==1.3.1 -tensorflow-cpu==2.14.0 -onnx==1.14.1 diff --git a/pkg/udf/pythonservice/pyserver/requirements/pytorch.txt b/pkg/udf/pythonservice/pyserver/requirements/pytorch.txt deleted file mode 100644 index d23b156a670c2..0000000000000 --- a/pkg/udf/pythonservice/pyserver/requirements/pytorch.txt +++ /dev/null @@ -1,5 +0,0 @@ --i https://download.pytorch.org/whl/cpu - -torch==2.1.0 -torchvision==0.16.0 -torchaudio==2.1.0 \ No newline at end of file diff --git a/pkg/udf/pythonservice/pyserver/server.py b/pkg/udf/pythonservice/pyserver/server.py deleted file mode 100644 index 262df4861f08f..0000000000000 --- a/pkg/udf/pythonservice/pyserver/server.py +++ /dev/null @@ -1,417 +0,0 @@ -# coding = utf-8 -# -*- coding:utf-8 -*- -# Copyright 2023 Matrix Origin -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -import argparse -import datetime -import decimal -import enum -import importlib -import json -import logging -import os -import shutil -import subprocess -import threading -from concurrent import futures -from typing import Any, Callable, Optional, Iterator, Dict - -import grpc - -import udf_pb2 as pb2 -import udf_pb2_grpc as pb2_grpc - -DEFAULT_DECIMAL_SCALE = 16 - -DATE_FORMAT = '%Y-%m-%d' -DATETIME_FORMAT = '%Y-%m-%d %H:%M:%S' -DATETIME_FORMAT_WITH_PRECISION = '%Y-%m-%d %H:%M:%S.%f' - -ROOT_PATH = os.path.dirname(os.path.abspath(__file__)) -INSTALLED_LABEL = 'installed' - -OPTION_VECTOR = 'vector' -OPTION_DECIMAL_PRECISION = 'decimal_precision' - -logging.basicConfig( - level=logging.INFO, - format='[%(asctime)s] - [%(name)s] - [%(levelname)s] - [%(threadName)s] : %(message)s' -) -log = logging.getLogger('Server') - - -class Server(pb2_grpc.ServiceServicer): - - def run(self, requestIterator: Iterator[pb2.Request], context) -> pb2.Response: - firstRequest: Optional[pb2.Request] = None - path: Optional[str] = None - filename: Optional[str] = None - item: Optional[InstallingItem] = None - - firstBlock = True - lastBlock = False - - try: - for request in requestIterator: - # check - checkUdf(request.udf) - - # the first request - if request.type == pb2.DataRequest: - log.info('receive data') - - if request.udf.isImport: - path, filename, item, status = functionStatus(request.udf) - - if status == FunctionStatus.NotExist: - firstRequest = request - yield pb2.Response(type=pb2.PkgRequest) - - elif status == FunctionStatus.Installing: - with item.condition: - # block and waiting - if not item.installed: - log.info('waiting') - item.condition.wait() - log.info('finish waiting') - yield self.calculate(request, path, filename) - - else: - yield self.calculate(request, path, filename) - - else: - yield self.calculate(request, "", "") - - # the second request (optional) - # var firstRequest, path, filename and item are not null - elif request.type == pb2.PkgResponse: - log.info('receive pkg') - - # install pkg, do not need write lock - absPath = os.path.join(ROOT_PATH, path) - lastBlock = request.udf.importPkg.last - try: - if firstBlock: - if os.path.exists(absPath): - shutil.rmtree(absPath) - os.makedirs(absPath, exist_ok=True) - firstBlock = False - - file = os.path.join(absPath, filename) - - with open(file, 'ab+') as f: - f.write(request.udf.importPkg.data) - - if lastBlock: - if request.udf.body.endswith('.whl'): - subprocess.check_call(['pip', 'install', '--no-index', file, '-t', absPath]) - os.remove(file) - - # mark the pkg is installed without error - open(os.path.join(absPath, INSTALLED_LABEL), 'w').close() - - except Exception as e: - shutil.rmtree(absPath, ignore_errors=True) - raise e - - finally: - if lastBlock: - with item.condition: - item.installed = True - item.condition.notifyAll() - - with INSTALLING_MAP_LOCK: - INSTALLING_MAP[path] = None - - if not lastBlock: - yield pb2.Response(type=pb2.PkgRequest) - else: - yield self.calculate(firstRequest, path, filename) - - else: - raise Exception('error udf request type') - # notify all - finally: - if item is None: - with INSTALLING_MAP_LOCK: - item = INSTALLING_MAP.get(path) - - if item is not None: - with item.condition: - item.installed = True - item.condition.notifyAll() - - with INSTALLING_MAP_LOCK: - INSTALLING_MAP[path] = None - - def calculate(self, request: pb2.Request, filepath: str, filename: str) -> pb2.Response: - log.info('calculating') - - # load function - func = loadFunction(request.udf, filepath, filename) - - # set precision - if hasattr(func, OPTION_DECIMAL_PRECISION): - prec = getattr(func, OPTION_DECIMAL_PRECISION) - if type(prec) is int and prec >= 0: - decimal.getcontext().prec = prec - else: - decimal.getcontext().prec = DEFAULT_DECIMAL_SCALE - else: - decimal.getcontext().prec = DEFAULT_DECIMAL_SCALE - - # scalar or vector - vector = False - if hasattr(func, OPTION_VECTOR): - vector = getattr(func, OPTION_VECTOR) is True - - # init result - result = pb2.DataVector( - const=False, - data=[], - length=request.length, - type=request.udf.retType, - scale=defaultScale(request.udf.retType) - ) - - # calculate - if vector: - params = [] - for i in range(len(request.vectors)): - params.append([getValueFromDataVector(request.vectors[i], j) for j in range(request.length)]) - values = func(*params) - assert len(values) == request.length, f'request length {request.length} is not same with result length {len(values)}' - for value in values: - data = value2Data(value, result.type) - result.data.append(data) - else: - for i in range(request.length): - params = [getValueFromDataVector(request.vectors[j], i) for j in range(len(request.vectors))] - value = func(*params) - data = value2Data(value, result.type) - result.data.append(data) - log.info('finish calculating') - return pb2.Response(vector=result, type=pb2.DataResponse) - - -def checkUdf(udf: pb2.Udf): - assert udf.handler != "", "udf handler should not be null" - assert udf.body != "", "udf body should not be null" - assert udf.language == "python", "udf language should be python" - assert udf.modifiedTime != "", "udf modifiedTime should not be null" - assert udf.db != "", "udf db should not be null" - - -class FunctionStatus(enum.Enum): - NotExist = 0 - Installing = 1 - Installed = 2 - - -class InstallingItem: - condition = threading.Condition() - installed = False - - -# key: db/func/modified_time, value: InstallingItem -INSTALLING_MAP: Dict[str, Optional[InstallingItem]] = {} -INSTALLING_MAP_LOCK = threading.RLock() - - -def functionStatus(udf: pb2.Udf) -> (str, str, Optional[InstallingItem], FunctionStatus): - with INSTALLING_MAP_LOCK: - filepath, filename = os.path.split(udf.body) - path = os.path.join('udf', udf.db, filepath[filepath.rfind('/') + 1:], udf.modifiedTime) - item = INSTALLING_MAP.get(path) - if item is None: - if os.path.isfile(os.path.join(ROOT_PATH, path, INSTALLED_LABEL)): - return path, filename, item, FunctionStatus.Installed - else: - item = InstallingItem() - item.installed = False - INSTALLING_MAP[path] = item - return path, filename, item, FunctionStatus.NotExist - else: - if item.installed: - return path, filename, item, FunctionStatus.Installed - else: - return path, filename, item, FunctionStatus.Installing - - -def loadFunction(udf: pb2.Udf, filepath: str, filename: str) -> Callable: - # load function - if not udf.isImport: - exec(udf.body, locals()) - # get function object - return locals()[udf.handler] - else: - if udf.body.endswith('.py'): - file = importlib.import_module(f'.{filename[:-3]}', package=filepath.replace("/", ".")) - return getattr(file, udf.handler) - elif udf.body.endswith('.whl'): - i = udf.handler.rfind('.') - if i < 1: - raise Exception( - "when you import a *.whl, the handler should be in the format of '.'") - file = importlib.import_module(f'.{udf.handler[:i]}', package=filepath.replace("/", ".")) - return getattr(file, udf.handler[i + 1:]) - - -def getDataFromDataVector(v: pb2.DataVector, i: int) -> pb2.Data: - if v is None: - return pb2.Data() - if v.const: - return v.data[0] - return v.data[i] - - -def getValueFromDataVector(v: pb2.DataVector, i: int) -> Any: - data = getDataFromDataVector(v, i) - - if data.WhichOneof("val") is None: - return None - - if v.type == pb2.BOOL: - return data.boolVal - if v.type in [pb2.INT8, pb2.INT16, pb2.INT32]: - return data.intVal - if v.type == pb2.INT64: - return data.int64Val - if v.type in [pb2.UINT8, pb2.INT16, pb2.INT32]: - return data.uintVal - if v.type == pb2.UINT64: - return data.uint64Val - if v.type == pb2.FLOAT32: - return data.floatVal - if v.type == pb2.FLOAT64: - return data.doubleVal - if v.type in [pb2.CHAR, pb2.VARCHAR, pb2.TEXT, pb2.UUID]: - return data.stringVal - if v.type == pb2.JSON: - return json.loads(data.stringVal) - if v.type == pb2.TIME: - sign = 1 if data.stringVal[0] == '-' else 0 - h, m, s = data.stringVal[sign:].split(':') - if sign == 0: - return datetime.timedelta(hours=int(h), minutes=int(m), seconds=float(s)) - return datetime.timedelta(hours=-int(h), minutes=-int(m), seconds=-float(s)) - if v.type == pb2.DATE: - return datetime.datetime.strptime(data.stringVal, DATE_FORMAT).date() - if v.type in [pb2.DATETIME, pb2.TIMESTAMP]: - formatStr = DATETIME_FORMAT if v.scale == 0 else DATETIME_FORMAT_WITH_PRECISION - return datetime.datetime.strptime(data.stringVal, formatStr) - if v.type in [pb2.DECIMAL64, pb2.DECIMAL128]: - return decimal.Decimal(data.stringVal) - if v.type in [pb2.BINARY, pb2.VARBINARY, pb2.BLOB]: - return data.bytesVal - else: - raise Exception("vector type error") - - -def defaultScale(typ: pb2.DataType, scale: Optional[int] = None) -> int: - if scale is not None: - return scale - if typ in [pb2.FLOAT32, pb2.FLOAT64]: - return -1 - if typ in [pb2.DECIMAL64, pb2.DECIMAL128]: - return decimal.getcontext().prec - if typ in [pb2.TIME, pb2.DATETIME, pb2.TIMESTAMP]: - return 6 - return 0 - - -def value2Data(value: Any, typ: pb2.DataType) -> pb2.Data: - if value is None: - return pb2.Data() - - if typ == pb2.BOOL: - assert type(value) is bool, f'return type error, required {bool}, received {type(value)}' - return pb2.Data(boolVal=value) - if typ in [pb2.INT8, pb2.INT16, pb2.INT32]: - assert type(value) is int, f'return type error, required {int}, received {type(value)}' - return pb2.Data(intVal=value) - if typ == pb2.INT64: - assert type(value) is int, f'return type error, required {int}, received {type(value)}' - return pb2.Data(int64Val=value) - if typ in [pb2.UINT8, pb2.INT16, pb2.INT32]: - assert type(value) is int, f'return type error, required {int}, received {type(value)}' - return pb2.Data(uintVal=value) - if typ == pb2.UINT64: - assert type(value) is int, f'return type error, required {int}, received {type(value)}' - return pb2.Data(uint64Val=value) - if typ == pb2.FLOAT32: - assert type(value) is float, f'return type error, required {float}, received {type(value)}' - return pb2.Data(floatVal=value) - if typ == pb2.FLOAT64: - assert type(value) is float, f'return type error, required {float}, received {type(value)}' - return pb2.Data(doubleVal=value) - if typ in [pb2.CHAR, pb2.VARCHAR, pb2.TEXT, pb2.UUID]: - assert type(value) is str, f'return type error, required {str}, received {type(value)}' - return pb2.Data(stringVal=value) - if typ == pb2.JSON: - return pb2.Data(stringVal=json.dumps(value, ensure_ascii=False)) - if typ == pb2.TIME: - assert type( - value) is datetime.timedelta, f'return type error, required {datetime.timedelta}, received {type(value)}' - r = '' - t: datetime.timedelta = value - if t.days < 0: - t = t * -1 - r += '-' - h = t.days * 24 + t.seconds // 3600 - m = t.seconds % 3600 // 60 - s = t.seconds % 3600 % 60 - r += f'{h:02d}:{m:02d}:{s:02d}.{t.microseconds:06d}' - return pb2.Data(stringVal=str(r)) - if typ == pb2.DATE: - assert type(value) is datetime.date, f'return type error, required {datetime.date}, received {type(value)}' - return pb2.Data(stringVal=str(value)) - if typ in [pb2.DATETIME, pb2.TIMESTAMP]: - assert type( - value) is datetime.datetime, f'return type error, required {datetime.datetime}, received {type(value)}' - return pb2.Data(stringVal=str(value)) - if typ in [pb2.DECIMAL64, pb2.DECIMAL128]: - assert type(value) is decimal.Decimal, f'return type error, required {decimal.Decimal}, received {type(value)}' - return pb2.Data(stringVal=str(value)) - if typ in [pb2.BINARY, pb2.VARBINARY, pb2.BLOB]: - assert type(value) is bytes, f'return type error, required {bytes}, received {type(value)}' - return pb2.Data(bytesVal=value) - else: - raise Exception(f'unsupported return type: {type(value)}') - - -def run(): - server = grpc.server(futures.ThreadPoolExecutor(), options=[ - ('grpc.max_send_message_length', 0x7fffffff), - ('grpc.max_receive_message_length', 0x7fffffff) - ]) - pb2_grpc.add_ServiceServicer_to_server(Server(), server) - server.add_insecure_port(ARGS.address) - server.start() - server.wait_for_termination() - - -ARGS = None - -if __name__ == '__main__': - parser = argparse.ArgumentParser( - prog='python udf server', - epilog='Copyright(r), 2023' - ) - parser.add_argument('--address', default='[::]:50051', help='address') - ARGS = parser.parse_args() - - log.info('python server start') - - run() diff --git a/pkg/udf/pythonservice/pyserver/udf_pb2.py b/pkg/udf/pythonservice/pyserver/udf_pb2.py deleted file mode 100644 index 52e791a8a7ff0..0000000000000 --- a/pkg/udf/pythonservice/pyserver/udf_pb2.py +++ /dev/null @@ -1,49 +0,0 @@ -# -*- coding: utf-8 -*- -# Generated by the protocol buffer compiler. DO NOT EDIT! -# source: udf.proto -"""Generated protocol buffer code.""" -from google.protobuf import descriptor as _descriptor -from google.protobuf import descriptor_pool as _descriptor_pool -from google.protobuf import symbol_database as _symbol_database -from google.protobuf.internal import builder as _builder -# @@protoc_insertion_point(imports) - -_sym_db = _symbol_database.Default() - - - - -DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\tudf.proto\x12\x03udf\"\xce\x01\n\x07Request\x12\x15\n\x03udf\x18\x01 \x01(\x0b\x32\x08.udf.Udf\x12 \n\x07vectors\x18\x02 \x03(\x0b\x32\x0f.udf.DataVector\x12\x0e\n\x06length\x18\x03 \x01(\x03\x12\x1e\n\x04type\x18\x04 \x01(\x0e\x32\x10.udf.RequestType\x12*\n\x07\x63ontext\x18\x05 \x03(\x0b\x32\x19.udf.Request.ContextEntry\x1a.\n\x0c\x43ontextEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01\"L\n\x08Response\x12\x1f\n\x06vector\x18\x01 \x01(\x0b\x32\x0f.udf.DataVector\x12\x1f\n\x04type\x18\x02 \x01(\x0e\x32\x11.udf.ResponseType\"\xab\x01\n\x03Udf\x12\x0f\n\x07handler\x18\x01 \x01(\t\x12\x10\n\x08isImport\x18\x02 \x01(\x08\x12\x0c\n\x04\x62ody\x18\x03 \x01(\t\x12\x1f\n\timportPkg\x18\x04 \x01(\x0b\x32\x0c.udf.Package\x12\x1e\n\x07retType\x18\x05 \x01(\x0e\x32\r.udf.DataType\x12\x10\n\x08language\x18\x06 \x01(\t\x12\x14\n\x0cmodifiedTime\x18\x07 \x01(\t\x12\n\n\x02\x64\x62\x18\x08 \x01(\t\"%\n\x07Package\x12\x0c\n\x04\x64\x61ta\x18\x01 \x01(\x0c\x12\x0c\n\x04last\x18\x02 \x01(\x08\"p\n\nDataVector\x12\x17\n\x04\x64\x61ta\x18\x01 \x03(\x0b\x32\t.udf.Data\x12\r\n\x05\x63onst\x18\x02 \x01(\x08\x12\x0e\n\x06length\x18\x03 \x01(\x03\x12\x1b\n\x04type\x18\x04 \x01(\x0e\x32\r.udf.DataType\x12\r\n\x05scale\x18\x05 \x01(\x05\"\xc0\x01\n\x04\x44\x61ta\x12\x11\n\x07\x62oolVal\x18\x01 \x01(\x08H\x00\x12\x10\n\x06intVal\x18\x02 \x01(\x11H\x00\x12\x12\n\x08int64Val\x18\x03 \x01(\x12H\x00\x12\x11\n\x07uintVal\x18\x04 \x01(\rH\x00\x12\x13\n\tuint64Val\x18\x05 \x01(\x04H\x00\x12\x12\n\x08\x66loatVal\x18\x06 \x01(\x02H\x00\x12\x13\n\tdoubleVal\x18\x07 \x01(\x01H\x00\x12\x13\n\tstringVal\x18\x08 \x01(\tH\x00\x12\x12\n\x08\x62ytesVal\x18\t \x01(\x0cH\x00\x42\x05\n\x03val*C\n\x0bRequestType\x12\x12\n\x0eUnknownRequest\x10\x00\x12\x0f\n\x0b\x44\x61taRequest\x10\x01\x12\x0f\n\x0bPkgResponse\x10\x02*E\n\x0cResponseType\x12\x13\n\x0fUnknownResponse\x10\x00\x12\x10\n\x0c\x44\x61taResponse\x10\x01\x12\x0e\n\nPkgRequest\x10\x02*\xbf\x02\n\x08\x44\x61taType\x12\x0b\n\x07UNKNOWN\x10\x00\x12\x08\n\x04\x42OOL\x10\n\x12\x08\n\x04INT8\x10\x14\x12\t\n\x05INT16\x10\x15\x12\t\n\x05INT32\x10\x16\x12\t\n\x05INT64\x10\x17\x12\t\n\x05UINT8\x10\x1e\x12\n\n\x06UINT16\x10\x1f\x12\n\n\x06UINT32\x10 \x12\n\n\x06UINT64\x10!\x12\x0b\n\x07\x46LOAT32\x10(\x12\x0b\n\x07\x46LOAT64\x10)\x12\x08\n\x04\x43HAR\x10\x32\x12\x0b\n\x07VARCHAR\x10\x33\x12\x08\n\x04TEXT\x10\x34\x12\x08\n\x04JSON\x10\x35\x12\x08\n\x04UUID\x10\x36\x12\x08\n\x04TIME\x10<\x12\x08\n\x04\x44\x41TE\x10=\x12\x0c\n\x08\x44\x41TETIME\x10>\x12\r\n\tTIMESTAMP\x10?\x12\r\n\tDECIMAL64\x10\x46\x12\x0e\n\nDECIMAL128\x10G\x12\n\n\x06\x42INARY\x10P\x12\r\n\tVARBINARY\x10Q\x12\x08\n\x04\x42LOB\x10R23\n\x07Service\x12(\n\x03run\x12\x0c.udf.Request\x1a\r.udf.Response\"\x00(\x01\x30\x01\x42+Z)github.com/matrixorigin/matrixone/pkg/udfb\x06proto3') - -_globals = globals() -_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) -_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'udf_pb2', _globals) -if _descriptor._USE_C_DESCRIPTORS == False: - - DESCRIPTOR._options = None - DESCRIPTOR._serialized_options = b'Z)github.com/matrixorigin/matrixone/pkg/udf' - _REQUEST_CONTEXTENTRY._options = None - _REQUEST_CONTEXTENTRY._serialized_options = b'8\001' - _globals['_REQUESTTYPE']._serialized_start=827 - _globals['_REQUESTTYPE']._serialized_end=894 - _globals['_RESPONSETYPE']._serialized_start=896 - _globals['_RESPONSETYPE']._serialized_end=965 - _globals['_DATATYPE']._serialized_start=968 - _globals['_DATATYPE']._serialized_end=1287 - _globals['_REQUEST']._serialized_start=19 - _globals['_REQUEST']._serialized_end=225 - _globals['_REQUEST_CONTEXTENTRY']._serialized_start=179 - _globals['_REQUEST_CONTEXTENTRY']._serialized_end=225 - _globals['_RESPONSE']._serialized_start=227 - _globals['_RESPONSE']._serialized_end=303 - _globals['_UDF']._serialized_start=306 - _globals['_UDF']._serialized_end=477 - _globals['_PACKAGE']._serialized_start=479 - _globals['_PACKAGE']._serialized_end=516 - _globals['_DATAVECTOR']._serialized_start=518 - _globals['_DATAVECTOR']._serialized_end=630 - _globals['_DATA']._serialized_start=633 - _globals['_DATA']._serialized_end=825 - _globals['_SERVICE']._serialized_start=1289 - _globals['_SERVICE']._serialized_end=1340 -# @@protoc_insertion_point(module_scope) diff --git a/pkg/udf/pythonservice/pyserver/udf_pb2.pyi b/pkg/udf/pythonservice/pyserver/udf_pb2.pyi deleted file mode 100644 index b3438382c2537..0000000000000 --- a/pkg/udf/pythonservice/pyserver/udf_pb2.pyi +++ /dev/null @@ -1,173 +0,0 @@ -from google.protobuf.internal import containers as _containers -from google.protobuf.internal import enum_type_wrapper as _enum_type_wrapper -from google.protobuf import descriptor as _descriptor -from google.protobuf import message as _message -from typing import ClassVar as _ClassVar, Iterable as _Iterable, Mapping as _Mapping, Optional as _Optional, Union as _Union - -DESCRIPTOR: _descriptor.FileDescriptor - -class RequestType(int, metaclass=_enum_type_wrapper.EnumTypeWrapper): - __slots__ = [] - UnknownRequest: _ClassVar[RequestType] - DataRequest: _ClassVar[RequestType] - PkgResponse: _ClassVar[RequestType] - -class ResponseType(int, metaclass=_enum_type_wrapper.EnumTypeWrapper): - __slots__ = [] - UnknownResponse: _ClassVar[ResponseType] - DataResponse: _ClassVar[ResponseType] - PkgRequest: _ClassVar[ResponseType] - -class DataType(int, metaclass=_enum_type_wrapper.EnumTypeWrapper): - __slots__ = [] - UNKNOWN: _ClassVar[DataType] - BOOL: _ClassVar[DataType] - INT8: _ClassVar[DataType] - INT16: _ClassVar[DataType] - INT32: _ClassVar[DataType] - INT64: _ClassVar[DataType] - UINT8: _ClassVar[DataType] - UINT16: _ClassVar[DataType] - UINT32: _ClassVar[DataType] - UINT64: _ClassVar[DataType] - FLOAT32: _ClassVar[DataType] - FLOAT64: _ClassVar[DataType] - CHAR: _ClassVar[DataType] - VARCHAR: _ClassVar[DataType] - TEXT: _ClassVar[DataType] - JSON: _ClassVar[DataType] - UUID: _ClassVar[DataType] - TIME: _ClassVar[DataType] - DATE: _ClassVar[DataType] - DATETIME: _ClassVar[DataType] - TIMESTAMP: _ClassVar[DataType] - DECIMAL64: _ClassVar[DataType] - DECIMAL128: _ClassVar[DataType] - BINARY: _ClassVar[DataType] - VARBINARY: _ClassVar[DataType] - BLOB: _ClassVar[DataType] -UnknownRequest: RequestType -DataRequest: RequestType -PkgResponse: RequestType -UnknownResponse: ResponseType -DataResponse: ResponseType -PkgRequest: ResponseType -UNKNOWN: DataType -BOOL: DataType -INT8: DataType -INT16: DataType -INT32: DataType -INT64: DataType -UINT8: DataType -UINT16: DataType -UINT32: DataType -UINT64: DataType -FLOAT32: DataType -FLOAT64: DataType -CHAR: DataType -VARCHAR: DataType -TEXT: DataType -JSON: DataType -UUID: DataType -TIME: DataType -DATE: DataType -DATETIME: DataType -TIMESTAMP: DataType -DECIMAL64: DataType -DECIMAL128: DataType -BINARY: DataType -VARBINARY: DataType -BLOB: DataType - -class Request(_message.Message): - __slots__ = ["udf", "vectors", "length", "type", "context"] - class ContextEntry(_message.Message): - __slots__ = ["key", "value"] - KEY_FIELD_NUMBER: _ClassVar[int] - VALUE_FIELD_NUMBER: _ClassVar[int] - key: str - value: str - def __init__(self, key: _Optional[str] = ..., value: _Optional[str] = ...) -> None: ... - UDF_FIELD_NUMBER: _ClassVar[int] - VECTORS_FIELD_NUMBER: _ClassVar[int] - LENGTH_FIELD_NUMBER: _ClassVar[int] - TYPE_FIELD_NUMBER: _ClassVar[int] - CONTEXT_FIELD_NUMBER: _ClassVar[int] - udf: Udf - vectors: _containers.RepeatedCompositeFieldContainer[DataVector] - length: int - type: RequestType - context: _containers.ScalarMap[str, str] - def __init__(self, udf: _Optional[_Union[Udf, _Mapping]] = ..., vectors: _Optional[_Iterable[_Union[DataVector, _Mapping]]] = ..., length: _Optional[int] = ..., type: _Optional[_Union[RequestType, str]] = ..., context: _Optional[_Mapping[str, str]] = ...) -> None: ... - -class Response(_message.Message): - __slots__ = ["vector", "type"] - VECTOR_FIELD_NUMBER: _ClassVar[int] - TYPE_FIELD_NUMBER: _ClassVar[int] - vector: DataVector - type: ResponseType - def __init__(self, vector: _Optional[_Union[DataVector, _Mapping]] = ..., type: _Optional[_Union[ResponseType, str]] = ...) -> None: ... - -class Udf(_message.Message): - __slots__ = ["handler", "isImport", "body", "importPkg", "retType", "language", "modifiedTime", "db"] - HANDLER_FIELD_NUMBER: _ClassVar[int] - ISIMPORT_FIELD_NUMBER: _ClassVar[int] - BODY_FIELD_NUMBER: _ClassVar[int] - IMPORTPKG_FIELD_NUMBER: _ClassVar[int] - RETTYPE_FIELD_NUMBER: _ClassVar[int] - LANGUAGE_FIELD_NUMBER: _ClassVar[int] - MODIFIEDTIME_FIELD_NUMBER: _ClassVar[int] - DB_FIELD_NUMBER: _ClassVar[int] - handler: str - isImport: bool - body: str - importPkg: Package - retType: DataType - language: str - modifiedTime: str - db: str - def __init__(self, handler: _Optional[str] = ..., isImport: bool = ..., body: _Optional[str] = ..., importPkg: _Optional[_Union[Package, _Mapping]] = ..., retType: _Optional[_Union[DataType, str]] = ..., language: _Optional[str] = ..., modifiedTime: _Optional[str] = ..., db: _Optional[str] = ...) -> None: ... - -class Package(_message.Message): - __slots__ = ["data", "last"] - DATA_FIELD_NUMBER: _ClassVar[int] - LAST_FIELD_NUMBER: _ClassVar[int] - data: bytes - last: bool - def __init__(self, data: _Optional[bytes] = ..., last: bool = ...) -> None: ... - -class DataVector(_message.Message): - __slots__ = ["data", "const", "length", "type", "scale"] - DATA_FIELD_NUMBER: _ClassVar[int] - CONST_FIELD_NUMBER: _ClassVar[int] - LENGTH_FIELD_NUMBER: _ClassVar[int] - TYPE_FIELD_NUMBER: _ClassVar[int] - SCALE_FIELD_NUMBER: _ClassVar[int] - data: _containers.RepeatedCompositeFieldContainer[Data] - const: bool - length: int - type: DataType - scale: int - def __init__(self, data: _Optional[_Iterable[_Union[Data, _Mapping]]] = ..., const: bool = ..., length: _Optional[int] = ..., type: _Optional[_Union[DataType, str]] = ..., scale: _Optional[int] = ...) -> None: ... - -class Data(_message.Message): - __slots__ = ["boolVal", "intVal", "int64Val", "uintVal", "uint64Val", "floatVal", "doubleVal", "stringVal", "bytesVal"] - BOOLVAL_FIELD_NUMBER: _ClassVar[int] - INTVAL_FIELD_NUMBER: _ClassVar[int] - INT64VAL_FIELD_NUMBER: _ClassVar[int] - UINTVAL_FIELD_NUMBER: _ClassVar[int] - UINT64VAL_FIELD_NUMBER: _ClassVar[int] - FLOATVAL_FIELD_NUMBER: _ClassVar[int] - DOUBLEVAL_FIELD_NUMBER: _ClassVar[int] - STRINGVAL_FIELD_NUMBER: _ClassVar[int] - BYTESVAL_FIELD_NUMBER: _ClassVar[int] - boolVal: bool - intVal: int - int64Val: int - uintVal: int - uint64Val: int - floatVal: float - doubleVal: float - stringVal: str - bytesVal: bytes - def __init__(self, boolVal: bool = ..., intVal: _Optional[int] = ..., int64Val: _Optional[int] = ..., uintVal: _Optional[int] = ..., uint64Val: _Optional[int] = ..., floatVal: _Optional[float] = ..., doubleVal: _Optional[float] = ..., stringVal: _Optional[str] = ..., bytesVal: _Optional[bytes] = ...) -> None: ... diff --git a/pkg/udf/pythonservice/pyserver/udf_pb2_grpc.py b/pkg/udf/pythonservice/pyserver/udf_pb2_grpc.py deleted file mode 100644 index 2707b06019282..0000000000000 --- a/pkg/udf/pythonservice/pyserver/udf_pb2_grpc.py +++ /dev/null @@ -1,72 +0,0 @@ -# Generated by the gRPC Python protocol compiler plugin. DO NOT EDIT! -"""Client and server classes corresponding to protobuf-defined services.""" -import grpc - -import udf_pb2 as udf__pb2 - - -class ServiceStub(object): - """protoc --go_out=. --go_opt=paths=source_relative --go-grpc_out=. --go-grpc_opt=paths=source_relative ./udf.proto - - """ - - def __init__(self, channel): - """Constructor. - - Args: - channel: A grpc.Channel. - """ - self.run = channel.stream_stream( - '/udf.Service/run', - request_serializer=udf__pb2.Request.SerializeToString, - response_deserializer=udf__pb2.Response.FromString, - ) - - -class ServiceServicer(object): - """protoc --go_out=. --go_opt=paths=source_relative --go-grpc_out=. --go-grpc_opt=paths=source_relative ./udf.proto - - """ - - def run(self, request_iterator, context): - """Missing associated documentation comment in .proto file.""" - context.set_code(grpc.StatusCode.UNIMPLEMENTED) - context.set_details('Method not implemented!') - raise NotImplementedError('Method not implemented!') - - -def add_ServiceServicer_to_server(servicer, server): - rpc_method_handlers = { - 'run': grpc.stream_stream_rpc_method_handler( - servicer.run, - request_deserializer=udf__pb2.Request.FromString, - response_serializer=udf__pb2.Response.SerializeToString, - ), - } - generic_handler = grpc.method_handlers_generic_handler( - 'udf.Service', rpc_method_handlers) - server.add_generic_rpc_handlers((generic_handler,)) - - - # This class is part of an EXPERIMENTAL API. -class Service(object): - """protoc --go_out=. --go_opt=paths=source_relative --go-grpc_out=. --go-grpc_opt=paths=source_relative ./udf.proto - - """ - - @staticmethod - def run(request_iterator, - target, - options=(), - channel_credentials=None, - call_credentials=None, - insecure=False, - compression=None, - wait_for_ready=None, - timeout=None, - metadata=None): - return grpc.experimental.stream_stream(request_iterator, target, '/udf.Service/run', - udf__pb2.Request.SerializeToString, - udf__pb2.Response.FromString, - options, channel_credentials, - insecure, call_credentials, compression, wait_for_ready, timeout, metadata) diff --git a/pkg/udf/pythonservice/service.go b/pkg/udf/pythonservice/service.go deleted file mode 100644 index c0d990d00ff60..0000000000000 --- a/pkg/udf/pythonservice/service.go +++ /dev/null @@ -1,122 +0,0 @@ -// Copyright 2023 Matrix Origin -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package pythonservice - -import ( - "fmt" - "github.com/matrixorigin/matrixone/pkg/logutil" - "io" - "os" - "os/exec" - "path" - "path/filepath" - "strconv" - "sync" - "sync/atomic" -) - -type service struct { - cfg Config - process *os.Process - log io.WriteCloser - mutex sync.Mutex -} - -func NewService(cfg Config) (PythonUdfServer, error) { - err := cfg.Validate() - if err != nil { - return nil, err - } - return &service{cfg: cfg}, nil -} - -var severNo int32 = 0 - -func (s *service) Start() error { - s.mutex.Lock() - defer s.mutex.Unlock() - - if s.process == nil { - var err error - var file string - - ex, err := os.Executable() - if err != nil { - return err - } - exePath := filepath.Dir(ex) - - if path.IsAbs(file) { - file = path.Join(s.cfg.Path, "server.py") - } else { - file = path.Join(exePath, s.cfg.Path, "server.py") - } - _, err = os.Stat(file) - if err != nil { - return err - } - - no := strconv.Itoa(int(atomic.AddInt32(&severNo, 1))) - s.log, err = os.OpenFile(path.Join(exePath, "pyserver"+no+".log"), os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0644) - if err != nil { - return err - } - defer func() { - if err != nil { - s.log.Close() - } - }() - executable := s.cfg.Python - if executable == "" { - executable = "python" - } - - cmd := exec.Command(executable, "-u", file, "--address="+s.cfg.Address) - cmd.Stdout = s.log - cmd.Stderr = s.log - go func(cmd *exec.Cmd) { - logutil.Infof("start python server process, command: %s", cmd.String()) - err := cmd.Run() - // panic the launch process if python server exit with an error - if err != nil { - panic(fmt.Sprintf("python server exit with error: %v", err)) - } - }(cmd) - - s.process = cmd.Process - } - - return nil -} - -func (s *service) Close() error { - s.mutex.Lock() - defer func() { - s.mutex.Unlock() - if s.log != nil { - s.log.Close() - } - }() - - if s.process != nil { - err := s.process.Kill() - if err != nil { - return err - } - s.process = nil - } - - return nil -} diff --git a/pkg/udf/pythonservice/types.go b/pkg/udf/pythonservice/types.go deleted file mode 100644 index 541418c2b0616..0000000000000 --- a/pkg/udf/pythonservice/types.go +++ /dev/null @@ -1,20 +0,0 @@ -// Copyright 2023 Matrix Origin -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package pythonservice - -type PythonUdfServer interface { - Start() error - Close() error -} diff --git a/pkg/udf/runtime.go b/pkg/udf/runtime.go new file mode 100644 index 0000000000000..33b2b90280ecf --- /dev/null +++ b/pkg/udf/runtime.go @@ -0,0 +1,104 @@ +// Copyright 2026 Matrix Origin +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Package udf contains the execution contract shared by SQL routine binders, +// CN operators, and language runtimes. Wire formats and language-specific +// adapters live below this package; the SQL engine depends only on this +// contract. +package udf + +import ( + "context" + + "github.com/matrixorigin/matrixone/pkg/common/moerr" + "github.com/matrixorigin/matrixone/pkg/common/mpool" + "github.com/matrixorigin/matrixone/pkg/container/types" + "github.com/matrixorigin/matrixone/pkg/container/vector" + "github.com/matrixorigin/matrixone/pkg/udf/protocol" +) + +const ( + LanguagePython = "python" + PythonABIContract = "PYTHON_ARROW" + PythonAdapterVersion = "2026-09" + PythonSDKVersion = "1.0" + NullCallHandler = "CALLED_ON_NULL_INPUT" + NullReturnNull = "RETURNS_NULL_ON_NULL_INPUT" +) + +// Invocation is the language-neutral routine call handed to an admitted +// runtime. The runtime owns transport, worker lifecycle, and Arrow +// conversion; CN owns the SQL vectors, result wrapper, and fencing identity. +type Invocation struct { + Language string + Handler string + Source string + Args []types.Type + ReturnType types.Type + Inputs []*vector.Vector + Length int + Mode string + NullPolicy string + ABIContract string + AdapterVersion string + SDKVersion string + Context map[string]string + Tuple protocol.FencingTuple +} + +// Runtime executes one invocation. Implementations must not retain the +// vectors or result after Execute returns. +type Runtime interface { + Language() string + Execute(context.Context, *Invocation, vector.FunctionResultWrapper, *mpool.MPool) error +} + +// Registry dispatches to the one runtime selected by a routine's language. +// A registry is immutable after construction, so plan execution cannot race +// service registration or observe a partially initialized adapter. +func NewRuntime(runtimes ...Runtime) (Runtime, error) { + registry := &runtimeRegistry{byLanguage: make(map[string]Runtime, len(runtimes))} + for _, runtime := range runtimes { + if runtime == nil || runtime.Language() == "" { + return nil, moerr.NewInternalErrorNoCtx("invalid udf runtime") + } + if _, exists := registry.byLanguage[runtime.Language()]; exists { + return nil, moerr.NewInternalErrorNoCtx("too many " + runtime.Language() + " runtimes") + } + registry.byLanguage[runtime.Language()] = runtime + } + return registry, nil +} + +type runtimeRegistry struct { + byLanguage map[string]Runtime +} + +func (r *runtimeRegistry) Language() string { return "multiple" } + +func (r *runtimeRegistry) Execute( + ctx context.Context, + invocation *Invocation, + result vector.FunctionResultWrapper, + mp *mpool.MPool, +) error { + if invocation == nil { + return moerr.NewInternalError(ctx, "nil udf invocation") + } + runtime := r.byLanguage[invocation.Language] + if runtime == nil { + return moerr.NewInternalError(ctx, "missing "+invocation.Language+" udf runtime") + } + return runtime.Execute(ctx, invocation, result, mp) +} diff --git a/pkg/udf/service.go b/pkg/udf/service.go deleted file mode 100644 index 0342bf9340a9e..0000000000000 --- a/pkg/udf/service.go +++ /dev/null @@ -1,50 +0,0 @@ -// Copyright 2023 Matrix Origin -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package udf - -import ( - "context" - - "github.com/matrixorigin/matrixone/pkg/common/moerr" -) - -func NewService(services ...Service) (Service, error) { - srv := &service{} - srv.mapping = make(map[string]Service, len(services)) - for _, s := range services { - if srv.mapping[s.Language()] == nil { - srv.mapping[s.Language()] = s - } else { - return nil, moerr.NewInternalErrorNoCtx("too many " + s.Language() + " udf service") - } - } - return srv, nil -} - -type service struct { - mapping map[string]Service -} - -func (s *service) Language() string { - return "multiple" -} - -func (s *service) Run(ctx context.Context, request *Request, pkgReader PkgReader) (*Response, error) { - srv := s.mapping[request.Udf.Language] - if srv == nil { - return nil, moerr.NewInternalError(ctx, "missing "+request.Udf.Language+" udf service") - } - return srv.Run(ctx, request, pkgReader) -} diff --git a/pkg/udf/types.go b/pkg/udf/types.go deleted file mode 100644 index 7adc0833377fb..0000000000000 --- a/pkg/udf/types.go +++ /dev/null @@ -1,40 +0,0 @@ -// Copyright 2023 Matrix Origin -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package udf - -import ( - "context" - "io" -) - -const ( - LanguagePython = "python" -) - -// Service handle non-sql udf in cn -type Service interface { - Language() string - Run(ctx context.Context, request *Request, pkgReader PkgReader) (*Response, error) -} - -// PkgReader read udf package from storage -type PkgReader interface { - Get(ctx context.Context, path string) (reader io.ReadCloser, err error) -} - -// PkgUploader upload udf package to storage -type PkgUploader interface { - Upload(ctx context.Context, localPath string, storageDir string) (storagePath string, err error) -} diff --git a/pkg/udf/udf.pb.go b/pkg/udf/udf.pb.go deleted file mode 100644 index 4db1253b016ca..0000000000000 --- a/pkg/udf/udf.pb.go +++ /dev/null @@ -1,1068 +0,0 @@ -// Copyright 2023 Matrix Origin -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -// Code generated by protoc-gen-go. DO NOT EDIT. -// versions: -// protoc-gen-go v1.28.1 -// protoc v3.17.0 -// source: udf.proto - -package udf - -import ( - protoreflect "google.golang.org/protobuf/reflect/protoreflect" - protoimpl "google.golang.org/protobuf/runtime/protoimpl" - reflect "reflect" - sync "sync" -) - -const ( - // Verify that this generated code is sufficiently up-to-date. - _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) - // Verify that runtime/protoimpl is sufficiently up-to-date. - _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) -) - -type RequestType int32 - -const ( - RequestType_UnknownRequest RequestType = 0 - RequestType_DataRequest RequestType = 1 // request with data for function result - RequestType_PkgResponse RequestType = 2 // response with package -) - -// Enum value maps for RequestType. -var ( - RequestType_name = map[int32]string{ - 0: "UnknownRequest", - 1: "DataRequest", - 2: "PkgResponse", - } - RequestType_value = map[string]int32{ - "UnknownRequest": 0, - "DataRequest": 1, - "PkgResponse": 2, - } -) - -func (x RequestType) Enum() *RequestType { - p := new(RequestType) - *p = x - return p -} - -func (x RequestType) String() string { - return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) -} - -func (RequestType) Descriptor() protoreflect.EnumDescriptor { - return file_udf_proto_enumTypes[0].Descriptor() -} - -func (RequestType) Type() protoreflect.EnumType { - return &file_udf_proto_enumTypes[0] -} - -func (x RequestType) Number() protoreflect.EnumNumber { - return protoreflect.EnumNumber(x) -} - -// Deprecated: Use RequestType.Descriptor instead. -func (RequestType) EnumDescriptor() ([]byte, []int) { - return file_udf_proto_rawDescGZIP(), []int{0} -} - -type ResponseType int32 - -const ( - ResponseType_UnknownResponse ResponseType = 0 - ResponseType_DataResponse ResponseType = 1 // response with function result - ResponseType_PkgRequest ResponseType = 2 // request for package -) - -// Enum value maps for ResponseType. -var ( - ResponseType_name = map[int32]string{ - 0: "UnknownResponse", - 1: "DataResponse", - 2: "PkgRequest", - } - ResponseType_value = map[string]int32{ - "UnknownResponse": 0, - "DataResponse": 1, - "PkgRequest": 2, - } -) - -func (x ResponseType) Enum() *ResponseType { - p := new(ResponseType) - *p = x - return p -} - -func (x ResponseType) String() string { - return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) -} - -func (ResponseType) Descriptor() protoreflect.EnumDescriptor { - return file_udf_proto_enumTypes[1].Descriptor() -} - -func (ResponseType) Type() protoreflect.EnumType { - return &file_udf_proto_enumTypes[1] -} - -func (x ResponseType) Number() protoreflect.EnumNumber { - return protoreflect.EnumNumber(x) -} - -// Deprecated: Use ResponseType.Descriptor instead. -func (ResponseType) EnumDescriptor() ([]byte, []int) { - return file_udf_proto_rawDescGZIP(), []int{1} -} - -type DataType int32 - -const ( - DataType_UNKNOWN DataType = 0 - DataType_BOOL DataType = 10 - DataType_INT8 DataType = 20 - DataType_INT16 DataType = 21 - DataType_INT32 DataType = 22 - DataType_INT64 DataType = 23 - DataType_UINT8 DataType = 30 - DataType_UINT16 DataType = 31 - DataType_UINT32 DataType = 32 - DataType_UINT64 DataType = 33 - DataType_FLOAT32 DataType = 40 - DataType_FLOAT64 DataType = 41 - DataType_CHAR DataType = 50 - DataType_VARCHAR DataType = 51 - DataType_TEXT DataType = 52 - DataType_JSON DataType = 53 - DataType_UUID DataType = 54 - DataType_TIME DataType = 60 - DataType_DATE DataType = 61 - DataType_DATETIME DataType = 62 - DataType_TIMESTAMP DataType = 63 - DataType_DECIMAL64 DataType = 70 - DataType_DECIMAL128 DataType = 71 - DataType_BINARY DataType = 80 - DataType_VARBINARY DataType = 81 - DataType_BLOB DataType = 82 -) - -// Enum value maps for DataType. -var ( - DataType_name = map[int32]string{ - 0: "UNKNOWN", - 10: "BOOL", - 20: "INT8", - 21: "INT16", - 22: "INT32", - 23: "INT64", - 30: "UINT8", - 31: "UINT16", - 32: "UINT32", - 33: "UINT64", - 40: "FLOAT32", - 41: "FLOAT64", - 50: "CHAR", - 51: "VARCHAR", - 52: "TEXT", - 53: "JSON", - 54: "UUID", - 60: "TIME", - 61: "DATE", - 62: "DATETIME", - 63: "TIMESTAMP", - 70: "DECIMAL64", - 71: "DECIMAL128", - 80: "BINARY", - 81: "VARBINARY", - 82: "BLOB", - } - DataType_value = map[string]int32{ - "UNKNOWN": 0, - "BOOL": 10, - "INT8": 20, - "INT16": 21, - "INT32": 22, - "INT64": 23, - "UINT8": 30, - "UINT16": 31, - "UINT32": 32, - "UINT64": 33, - "FLOAT32": 40, - "FLOAT64": 41, - "CHAR": 50, - "VARCHAR": 51, - "TEXT": 52, - "JSON": 53, - "UUID": 54, - "TIME": 60, - "DATE": 61, - "DATETIME": 62, - "TIMESTAMP": 63, - "DECIMAL64": 70, - "DECIMAL128": 71, - "BINARY": 80, - "VARBINARY": 81, - "BLOB": 82, - } -) - -func (x DataType) Enum() *DataType { - p := new(DataType) - *p = x - return p -} - -func (x DataType) String() string { - return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) -} - -func (DataType) Descriptor() protoreflect.EnumDescriptor { - return file_udf_proto_enumTypes[2].Descriptor() -} - -func (DataType) Type() protoreflect.EnumType { - return &file_udf_proto_enumTypes[2] -} - -func (x DataType) Number() protoreflect.EnumNumber { - return protoreflect.EnumNumber(x) -} - -// Deprecated: Use DataType.Descriptor instead. -func (DataType) EnumDescriptor() ([]byte, []int) { - return file_udf_proto_rawDescGZIP(), []int{2} -} - -type Request struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Udf *Udf `protobuf:"bytes,1,opt,name=udf,proto3" json:"udf,omitempty"` - Vectors []*DataVector `protobuf:"bytes,2,rep,name=vectors,proto3" json:"vectors,omitempty"` - Length int64 `protobuf:"varint,3,opt,name=length,proto3" json:"length,omitempty"` - Type RequestType `protobuf:"varint,4,opt,name=type,proto3,enum=udf.RequestType" json:"type,omitempty"` - Context map[string]string `protobuf:"bytes,5,rep,name=context,proto3" json:"context,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` // currently just transfer, not used -} - -func (x *Request) Reset() { - *x = Request{} - if protoimpl.UnsafeEnabled { - mi := &file_udf_proto_msgTypes[0] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *Request) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*Request) ProtoMessage() {} - -func (x *Request) ProtoReflect() protoreflect.Message { - mi := &file_udf_proto_msgTypes[0] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use Request.ProtoReflect.Descriptor instead. -func (*Request) Descriptor() ([]byte, []int) { - return file_udf_proto_rawDescGZIP(), []int{0} -} - -func (x *Request) GetUdf() *Udf { - if x != nil { - return x.Udf - } - return nil -} - -func (x *Request) GetVectors() []*DataVector { - if x != nil { - return x.Vectors - } - return nil -} - -func (x *Request) GetLength() int64 { - if x != nil { - return x.Length - } - return 0 -} - -func (x *Request) GetType() RequestType { - if x != nil { - return x.Type - } - return RequestType_UnknownRequest -} - -func (x *Request) GetContext() map[string]string { - if x != nil { - return x.Context - } - return nil -} - -type Response struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Vector *DataVector `protobuf:"bytes,1,opt,name=vector,proto3" json:"vector,omitempty"` - Type ResponseType `protobuf:"varint,2,opt,name=type,proto3,enum=udf.ResponseType" json:"type,omitempty"` -} - -func (x *Response) Reset() { - *x = Response{} - if protoimpl.UnsafeEnabled { - mi := &file_udf_proto_msgTypes[1] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *Response) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*Response) ProtoMessage() {} - -func (x *Response) ProtoReflect() protoreflect.Message { - mi := &file_udf_proto_msgTypes[1] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use Response.ProtoReflect.Descriptor instead. -func (*Response) Descriptor() ([]byte, []int) { - return file_udf_proto_rawDescGZIP(), []int{1} -} - -func (x *Response) GetVector() *DataVector { - if x != nil { - return x.Vector - } - return nil -} - -func (x *Response) GetType() ResponseType { - if x != nil { - return x.Type - } - return ResponseType_UnknownResponse -} - -type Udf struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Handler string `protobuf:"bytes,1,opt,name=handler,proto3" json:"handler,omitempty"` - IsImport bool `protobuf:"varint,2,opt,name=isImport,proto3" json:"isImport,omitempty"` - Body string `protobuf:"bytes,3,opt,name=body,proto3" json:"body,omitempty"` - ImportPkg *Package `protobuf:"bytes,4,opt,name=importPkg,proto3" json:"importPkg,omitempty"` - RetType DataType `protobuf:"varint,5,opt,name=retType,proto3,enum=udf.DataType" json:"retType,omitempty"` - Language string `protobuf:"bytes,6,opt,name=language,proto3" json:"language,omitempty"` - ModifiedTime string `protobuf:"bytes,7,opt,name=modifiedTime,proto3" json:"modifiedTime,omitempty"` - Db string `protobuf:"bytes,8,opt,name=db,proto3" json:"db,omitempty"` -} - -func (x *Udf) Reset() { - *x = Udf{} - if protoimpl.UnsafeEnabled { - mi := &file_udf_proto_msgTypes[2] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *Udf) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*Udf) ProtoMessage() {} - -func (x *Udf) ProtoReflect() protoreflect.Message { - mi := &file_udf_proto_msgTypes[2] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use Udf.ProtoReflect.Descriptor instead. -func (*Udf) Descriptor() ([]byte, []int) { - return file_udf_proto_rawDescGZIP(), []int{2} -} - -func (x *Udf) GetHandler() string { - if x != nil { - return x.Handler - } - return "" -} - -func (x *Udf) GetIsImport() bool { - if x != nil { - return x.IsImport - } - return false -} - -func (x *Udf) GetBody() string { - if x != nil { - return x.Body - } - return "" -} - -func (x *Udf) GetImportPkg() *Package { - if x != nil { - return x.ImportPkg - } - return nil -} - -func (x *Udf) GetRetType() DataType { - if x != nil { - return x.RetType - } - return DataType_UNKNOWN -} - -func (x *Udf) GetLanguage() string { - if x != nil { - return x.Language - } - return "" -} - -func (x *Udf) GetModifiedTime() string { - if x != nil { - return x.ModifiedTime - } - return "" -} - -func (x *Udf) GetDb() string { - if x != nil { - return x.Db - } - return "" -} - -type Package struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Data []byte `protobuf:"bytes,1,opt,name=data,proto3" json:"data,omitempty"` - Last bool `protobuf:"varint,2,opt,name=last,proto3" json:"last,omitempty"` -} - -func (x *Package) Reset() { - *x = Package{} - if protoimpl.UnsafeEnabled { - mi := &file_udf_proto_msgTypes[3] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *Package) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*Package) ProtoMessage() {} - -func (x *Package) ProtoReflect() protoreflect.Message { - mi := &file_udf_proto_msgTypes[3] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use Package.ProtoReflect.Descriptor instead. -func (*Package) Descriptor() ([]byte, []int) { - return file_udf_proto_rawDescGZIP(), []int{3} -} - -func (x *Package) GetData() []byte { - if x != nil { - return x.Data - } - return nil -} - -func (x *Package) GetLast() bool { - if x != nil { - return x.Last - } - return false -} - -type DataVector struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Data []*Data `protobuf:"bytes,1,rep,name=data,proto3" json:"data,omitempty"` - Const bool `protobuf:"varint,2,opt,name=const,proto3" json:"const,omitempty"` - Length int64 `protobuf:"varint,3,opt,name=length,proto3" json:"length,omitempty"` - Type DataType `protobuf:"varint,4,opt,name=type,proto3,enum=udf.DataType" json:"type,omitempty"` - Scale int32 `protobuf:"varint,5,opt,name=scale,proto3" json:"scale,omitempty"` -} - -func (x *DataVector) Reset() { - *x = DataVector{} - if protoimpl.UnsafeEnabled { - mi := &file_udf_proto_msgTypes[4] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *DataVector) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*DataVector) ProtoMessage() {} - -func (x *DataVector) ProtoReflect() protoreflect.Message { - mi := &file_udf_proto_msgTypes[4] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use DataVector.ProtoReflect.Descriptor instead. -func (*DataVector) Descriptor() ([]byte, []int) { - return file_udf_proto_rawDescGZIP(), []int{4} -} - -func (x *DataVector) GetData() []*Data { - if x != nil { - return x.Data - } - return nil -} - -func (x *DataVector) GetConst() bool { - if x != nil { - return x.Const - } - return false -} - -func (x *DataVector) GetLength() int64 { - if x != nil { - return x.Length - } - return 0 -} - -func (x *DataVector) GetType() DataType { - if x != nil { - return x.Type - } - return DataType_UNKNOWN -} - -func (x *DataVector) GetScale() int32 { - if x != nil { - return x.Scale - } - return 0 -} - -type Data struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - // Types that are assignable to Val: - // - // *Data_BoolVal - // *Data_IntVal - // *Data_Int64Val - // *Data_UintVal - // *Data_Uint64Val - // *Data_FloatVal - // *Data_DoubleVal - // *Data_StringVal - // *Data_BytesVal - Val isData_Val `protobuf_oneof:"val"` -} - -func (x *Data) Reset() { - *x = Data{} - if protoimpl.UnsafeEnabled { - mi := &file_udf_proto_msgTypes[5] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *Data) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*Data) ProtoMessage() {} - -func (x *Data) ProtoReflect() protoreflect.Message { - mi := &file_udf_proto_msgTypes[5] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use Data.ProtoReflect.Descriptor instead. -func (*Data) Descriptor() ([]byte, []int) { - return file_udf_proto_rawDescGZIP(), []int{5} -} - -func (m *Data) GetVal() isData_Val { - if m != nil { - return m.Val - } - return nil -} - -func (x *Data) GetBoolVal() bool { - if x, ok := x.GetVal().(*Data_BoolVal); ok { - return x.BoolVal - } - return false -} - -func (x *Data) GetIntVal() int32 { - if x, ok := x.GetVal().(*Data_IntVal); ok { - return x.IntVal - } - return 0 -} - -func (x *Data) GetInt64Val() int64 { - if x, ok := x.GetVal().(*Data_Int64Val); ok { - return x.Int64Val - } - return 0 -} - -func (x *Data) GetUintVal() uint32 { - if x, ok := x.GetVal().(*Data_UintVal); ok { - return x.UintVal - } - return 0 -} - -func (x *Data) GetUint64Val() uint64 { - if x, ok := x.GetVal().(*Data_Uint64Val); ok { - return x.Uint64Val - } - return 0 -} - -func (x *Data) GetFloatVal() float32 { - if x, ok := x.GetVal().(*Data_FloatVal); ok { - return x.FloatVal - } - return 0 -} - -func (x *Data) GetDoubleVal() float64 { - if x, ok := x.GetVal().(*Data_DoubleVal); ok { - return x.DoubleVal - } - return 0 -} - -func (x *Data) GetStringVal() string { - if x, ok := x.GetVal().(*Data_StringVal); ok { - return x.StringVal - } - return "" -} - -func (x *Data) GetBytesVal() []byte { - if x, ok := x.GetVal().(*Data_BytesVal); ok { - return x.BytesVal - } - return nil -} - -type isData_Val interface { - isData_Val() -} - -type Data_BoolVal struct { - BoolVal bool `protobuf:"varint,1,opt,name=boolVal,proto3,oneof"` // bool -} - -type Data_IntVal struct { - IntVal int32 `protobuf:"zigzag32,2,opt,name=intVal,proto3,oneof"` // int8, int16, int32 -} - -type Data_Int64Val struct { - Int64Val int64 `protobuf:"zigzag64,3,opt,name=int64Val,proto3,oneof"` // int64 -} - -type Data_UintVal struct { - UintVal uint32 `protobuf:"varint,4,opt,name=uintVal,proto3,oneof"` // uint8, uint16, uint32 -} - -type Data_Uint64Val struct { - Uint64Val uint64 `protobuf:"varint,5,opt,name=uint64Val,proto3,oneof"` // uint64 -} - -type Data_FloatVal struct { - FloatVal float32 `protobuf:"fixed32,6,opt,name=floatVal,proto3,oneof"` // float32 -} - -type Data_DoubleVal struct { - DoubleVal float64 `protobuf:"fixed64,7,opt,name=doubleVal,proto3,oneof"` // float64 -} - -type Data_StringVal struct { - StringVal string `protobuf:"bytes,8,opt,name=stringVal,proto3,oneof"` // char, varchar, text, json, uuid | time, date, datetime, timestamp | Decimal64, Decimal128 -} - -type Data_BytesVal struct { - BytesVal []byte `protobuf:"bytes,9,opt,name=bytesVal,proto3,oneof"` // binary, varbinary, blob -} - -func (*Data_BoolVal) isData_Val() {} - -func (*Data_IntVal) isData_Val() {} - -func (*Data_Int64Val) isData_Val() {} - -func (*Data_UintVal) isData_Val() {} - -func (*Data_Uint64Val) isData_Val() {} - -func (*Data_FloatVal) isData_Val() {} - -func (*Data_DoubleVal) isData_Val() {} - -func (*Data_StringVal) isData_Val() {} - -func (*Data_BytesVal) isData_Val() {} - -var File_udf_proto protoreflect.FileDescriptor - -var file_udf_proto_rawDesc = []byte{ - 0x0a, 0x09, 0x75, 0x64, 0x66, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x03, 0x75, 0x64, 0x66, - 0x22, 0xff, 0x01, 0x0a, 0x07, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x03, - 0x75, 0x64, 0x66, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x08, 0x2e, 0x75, 0x64, 0x66, 0x2e, - 0x55, 0x64, 0x66, 0x52, 0x03, 0x75, 0x64, 0x66, 0x12, 0x29, 0x0a, 0x07, 0x76, 0x65, 0x63, 0x74, - 0x6f, 0x72, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x75, 0x64, 0x66, 0x2e, - 0x44, 0x61, 0x74, 0x61, 0x56, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x52, 0x07, 0x76, 0x65, 0x63, 0x74, - 0x6f, 0x72, 0x73, 0x12, 0x16, 0x0a, 0x06, 0x6c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x18, 0x03, 0x20, - 0x01, 0x28, 0x03, 0x52, 0x06, 0x6c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x12, 0x24, 0x0a, 0x04, 0x74, - 0x79, 0x70, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x10, 0x2e, 0x75, 0x64, 0x66, 0x2e, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x54, 0x79, 0x70, 0x65, 0x52, 0x04, 0x74, 0x79, 0x70, - 0x65, 0x12, 0x33, 0x0a, 0x07, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x18, 0x05, 0x20, 0x03, - 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x75, 0x64, 0x66, 0x2e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x2e, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x07, 0x63, - 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x1a, 0x3a, 0x0a, 0x0c, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x78, - 0x74, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, - 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, - 0x38, 0x01, 0x22, 0x5a, 0x0a, 0x08, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x27, - 0x0a, 0x06, 0x76, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0f, - 0x2e, 0x75, 0x64, 0x66, 0x2e, 0x44, 0x61, 0x74, 0x61, 0x56, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x52, - 0x06, 0x76, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x12, 0x25, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, - 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x11, 0x2e, 0x75, 0x64, 0x66, 0x2e, 0x52, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x54, 0x79, 0x70, 0x65, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x22, 0xf4, - 0x01, 0x0a, 0x03, 0x55, 0x64, 0x66, 0x12, 0x18, 0x0a, 0x07, 0x68, 0x61, 0x6e, 0x64, 0x6c, 0x65, - 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x68, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x72, - 0x12, 0x1a, 0x0a, 0x08, 0x69, 0x73, 0x49, 0x6d, 0x70, 0x6f, 0x72, 0x74, 0x18, 0x02, 0x20, 0x01, - 0x28, 0x08, 0x52, 0x08, 0x69, 0x73, 0x49, 0x6d, 0x70, 0x6f, 0x72, 0x74, 0x12, 0x12, 0x0a, 0x04, - 0x62, 0x6f, 0x64, 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x62, 0x6f, 0x64, 0x79, - 0x12, 0x2a, 0x0a, 0x09, 0x69, 0x6d, 0x70, 0x6f, 0x72, 0x74, 0x50, 0x6b, 0x67, 0x18, 0x04, 0x20, - 0x01, 0x28, 0x0b, 0x32, 0x0c, 0x2e, 0x75, 0x64, 0x66, 0x2e, 0x50, 0x61, 0x63, 0x6b, 0x61, 0x67, - 0x65, 0x52, 0x09, 0x69, 0x6d, 0x70, 0x6f, 0x72, 0x74, 0x50, 0x6b, 0x67, 0x12, 0x27, 0x0a, 0x07, - 0x72, 0x65, 0x74, 0x54, 0x79, 0x70, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x0d, 0x2e, - 0x75, 0x64, 0x66, 0x2e, 0x44, 0x61, 0x74, 0x61, 0x54, 0x79, 0x70, 0x65, 0x52, 0x07, 0x72, 0x65, - 0x74, 0x54, 0x79, 0x70, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x6c, 0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, - 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x6c, 0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, - 0x65, 0x12, 0x22, 0x0a, 0x0c, 0x6d, 0x6f, 0x64, 0x69, 0x66, 0x69, 0x65, 0x64, 0x54, 0x69, 0x6d, - 0x65, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x6d, 0x6f, 0x64, 0x69, 0x66, 0x69, 0x65, - 0x64, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x0e, 0x0a, 0x02, 0x64, 0x62, 0x18, 0x08, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x02, 0x64, 0x62, 0x22, 0x31, 0x0a, 0x07, 0x50, 0x61, 0x63, 0x6b, 0x61, 0x67, 0x65, - 0x12, 0x12, 0x0a, 0x04, 0x64, 0x61, 0x74, 0x61, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, - 0x64, 0x61, 0x74, 0x61, 0x12, 0x12, 0x0a, 0x04, 0x6c, 0x61, 0x73, 0x74, 0x18, 0x02, 0x20, 0x01, - 0x28, 0x08, 0x52, 0x04, 0x6c, 0x61, 0x73, 0x74, 0x22, 0x92, 0x01, 0x0a, 0x0a, 0x44, 0x61, 0x74, - 0x61, 0x56, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x12, 0x1d, 0x0a, 0x04, 0x64, 0x61, 0x74, 0x61, 0x18, - 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x09, 0x2e, 0x75, 0x64, 0x66, 0x2e, 0x44, 0x61, 0x74, 0x61, - 0x52, 0x04, 0x64, 0x61, 0x74, 0x61, 0x12, 0x14, 0x0a, 0x05, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x18, - 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x05, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x12, 0x16, 0x0a, 0x06, - 0x6c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x18, 0x03, 0x20, 0x01, 0x28, 0x03, 0x52, 0x06, 0x6c, 0x65, - 0x6e, 0x67, 0x74, 0x68, 0x12, 0x21, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x04, 0x20, 0x01, - 0x28, 0x0e, 0x32, 0x0d, 0x2e, 0x75, 0x64, 0x66, 0x2e, 0x44, 0x61, 0x74, 0x61, 0x54, 0x79, 0x70, - 0x65, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x73, 0x63, 0x61, 0x6c, 0x65, - 0x18, 0x05, 0x20, 0x01, 0x28, 0x05, 0x52, 0x05, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x22, 0x99, 0x02, - 0x0a, 0x04, 0x44, 0x61, 0x74, 0x61, 0x12, 0x1a, 0x0a, 0x07, 0x62, 0x6f, 0x6f, 0x6c, 0x56, 0x61, - 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x48, 0x00, 0x52, 0x07, 0x62, 0x6f, 0x6f, 0x6c, 0x56, - 0x61, 0x6c, 0x12, 0x18, 0x0a, 0x06, 0x69, 0x6e, 0x74, 0x56, 0x61, 0x6c, 0x18, 0x02, 0x20, 0x01, - 0x28, 0x11, 0x48, 0x00, 0x52, 0x06, 0x69, 0x6e, 0x74, 0x56, 0x61, 0x6c, 0x12, 0x1c, 0x0a, 0x08, - 0x69, 0x6e, 0x74, 0x36, 0x34, 0x56, 0x61, 0x6c, 0x18, 0x03, 0x20, 0x01, 0x28, 0x12, 0x48, 0x00, - 0x52, 0x08, 0x69, 0x6e, 0x74, 0x36, 0x34, 0x56, 0x61, 0x6c, 0x12, 0x1a, 0x0a, 0x07, 0x75, 0x69, - 0x6e, 0x74, 0x56, 0x61, 0x6c, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0d, 0x48, 0x00, 0x52, 0x07, 0x75, - 0x69, 0x6e, 0x74, 0x56, 0x61, 0x6c, 0x12, 0x1e, 0x0a, 0x09, 0x75, 0x69, 0x6e, 0x74, 0x36, 0x34, - 0x56, 0x61, 0x6c, 0x18, 0x05, 0x20, 0x01, 0x28, 0x04, 0x48, 0x00, 0x52, 0x09, 0x75, 0x69, 0x6e, - 0x74, 0x36, 0x34, 0x56, 0x61, 0x6c, 0x12, 0x1c, 0x0a, 0x08, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x56, - 0x61, 0x6c, 0x18, 0x06, 0x20, 0x01, 0x28, 0x02, 0x48, 0x00, 0x52, 0x08, 0x66, 0x6c, 0x6f, 0x61, - 0x74, 0x56, 0x61, 0x6c, 0x12, 0x1e, 0x0a, 0x09, 0x64, 0x6f, 0x75, 0x62, 0x6c, 0x65, 0x56, 0x61, - 0x6c, 0x18, 0x07, 0x20, 0x01, 0x28, 0x01, 0x48, 0x00, 0x52, 0x09, 0x64, 0x6f, 0x75, 0x62, 0x6c, - 0x65, 0x56, 0x61, 0x6c, 0x12, 0x1e, 0x0a, 0x09, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x56, 0x61, - 0x6c, 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x09, 0x73, 0x74, 0x72, 0x69, 0x6e, - 0x67, 0x56, 0x61, 0x6c, 0x12, 0x1c, 0x0a, 0x08, 0x62, 0x79, 0x74, 0x65, 0x73, 0x56, 0x61, 0x6c, - 0x18, 0x09, 0x20, 0x01, 0x28, 0x0c, 0x48, 0x00, 0x52, 0x08, 0x62, 0x79, 0x74, 0x65, 0x73, 0x56, - 0x61, 0x6c, 0x42, 0x05, 0x0a, 0x03, 0x76, 0x61, 0x6c, 0x2a, 0x43, 0x0a, 0x0b, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x54, 0x79, 0x70, 0x65, 0x12, 0x12, 0x0a, 0x0e, 0x55, 0x6e, 0x6b, 0x6e, - 0x6f, 0x77, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x10, 0x00, 0x12, 0x0f, 0x0a, 0x0b, - 0x44, 0x61, 0x74, 0x61, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x10, 0x01, 0x12, 0x0f, 0x0a, - 0x0b, 0x50, 0x6b, 0x67, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x10, 0x02, 0x2a, 0x45, - 0x0a, 0x0c, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x54, 0x79, 0x70, 0x65, 0x12, 0x13, - 0x0a, 0x0f, 0x55, 0x6e, 0x6b, 0x6e, 0x6f, 0x77, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x10, 0x00, 0x12, 0x10, 0x0a, 0x0c, 0x44, 0x61, 0x74, 0x61, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x10, 0x01, 0x12, 0x0e, 0x0a, 0x0a, 0x50, 0x6b, 0x67, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x10, 0x02, 0x2a, 0xbf, 0x02, 0x0a, 0x08, 0x44, 0x61, 0x74, 0x61, 0x54, 0x79, - 0x70, 0x65, 0x12, 0x0b, 0x0a, 0x07, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x10, 0x00, 0x12, - 0x08, 0x0a, 0x04, 0x42, 0x4f, 0x4f, 0x4c, 0x10, 0x0a, 0x12, 0x08, 0x0a, 0x04, 0x49, 0x4e, 0x54, - 0x38, 0x10, 0x14, 0x12, 0x09, 0x0a, 0x05, 0x49, 0x4e, 0x54, 0x31, 0x36, 0x10, 0x15, 0x12, 0x09, - 0x0a, 0x05, 0x49, 0x4e, 0x54, 0x33, 0x32, 0x10, 0x16, 0x12, 0x09, 0x0a, 0x05, 0x49, 0x4e, 0x54, - 0x36, 0x34, 0x10, 0x17, 0x12, 0x09, 0x0a, 0x05, 0x55, 0x49, 0x4e, 0x54, 0x38, 0x10, 0x1e, 0x12, - 0x0a, 0x0a, 0x06, 0x55, 0x49, 0x4e, 0x54, 0x31, 0x36, 0x10, 0x1f, 0x12, 0x0a, 0x0a, 0x06, 0x55, - 0x49, 0x4e, 0x54, 0x33, 0x32, 0x10, 0x20, 0x12, 0x0a, 0x0a, 0x06, 0x55, 0x49, 0x4e, 0x54, 0x36, - 0x34, 0x10, 0x21, 0x12, 0x0b, 0x0a, 0x07, 0x46, 0x4c, 0x4f, 0x41, 0x54, 0x33, 0x32, 0x10, 0x28, - 0x12, 0x0b, 0x0a, 0x07, 0x46, 0x4c, 0x4f, 0x41, 0x54, 0x36, 0x34, 0x10, 0x29, 0x12, 0x08, 0x0a, - 0x04, 0x43, 0x48, 0x41, 0x52, 0x10, 0x32, 0x12, 0x0b, 0x0a, 0x07, 0x56, 0x41, 0x52, 0x43, 0x48, - 0x41, 0x52, 0x10, 0x33, 0x12, 0x08, 0x0a, 0x04, 0x54, 0x45, 0x58, 0x54, 0x10, 0x34, 0x12, 0x08, - 0x0a, 0x04, 0x4a, 0x53, 0x4f, 0x4e, 0x10, 0x35, 0x12, 0x08, 0x0a, 0x04, 0x55, 0x55, 0x49, 0x44, - 0x10, 0x36, 0x12, 0x08, 0x0a, 0x04, 0x54, 0x49, 0x4d, 0x45, 0x10, 0x3c, 0x12, 0x08, 0x0a, 0x04, - 0x44, 0x41, 0x54, 0x45, 0x10, 0x3d, 0x12, 0x0c, 0x0a, 0x08, 0x44, 0x41, 0x54, 0x45, 0x54, 0x49, - 0x4d, 0x45, 0x10, 0x3e, 0x12, 0x0d, 0x0a, 0x09, 0x54, 0x49, 0x4d, 0x45, 0x53, 0x54, 0x41, 0x4d, - 0x50, 0x10, 0x3f, 0x12, 0x0d, 0x0a, 0x09, 0x44, 0x45, 0x43, 0x49, 0x4d, 0x41, 0x4c, 0x36, 0x34, - 0x10, 0x46, 0x12, 0x0e, 0x0a, 0x0a, 0x44, 0x45, 0x43, 0x49, 0x4d, 0x41, 0x4c, 0x31, 0x32, 0x38, - 0x10, 0x47, 0x12, 0x0a, 0x0a, 0x06, 0x42, 0x49, 0x4e, 0x41, 0x52, 0x59, 0x10, 0x50, 0x12, 0x0d, - 0x0a, 0x09, 0x56, 0x41, 0x52, 0x42, 0x49, 0x4e, 0x41, 0x52, 0x59, 0x10, 0x51, 0x12, 0x08, 0x0a, - 0x04, 0x42, 0x4c, 0x4f, 0x42, 0x10, 0x52, 0x32, 0x33, 0x0a, 0x07, 0x53, 0x65, 0x72, 0x76, 0x69, - 0x63, 0x65, 0x12, 0x28, 0x0a, 0x03, 0x72, 0x75, 0x6e, 0x12, 0x0c, 0x2e, 0x75, 0x64, 0x66, 0x2e, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x0d, 0x2e, 0x75, 0x64, 0x66, 0x2e, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x28, 0x01, 0x30, 0x01, 0x42, 0x2b, 0x5a, 0x29, - 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x6d, 0x61, 0x74, 0x72, 0x69, - 0x78, 0x6f, 0x72, 0x69, 0x67, 0x69, 0x6e, 0x2f, 0x6d, 0x61, 0x74, 0x72, 0x69, 0x78, 0x6f, 0x6e, - 0x65, 0x2f, 0x70, 0x6b, 0x67, 0x2f, 0x75, 0x64, 0x66, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, - 0x33, -} - -var ( - file_udf_proto_rawDescOnce sync.Once - file_udf_proto_rawDescData = file_udf_proto_rawDesc -) - -func file_udf_proto_rawDescGZIP() []byte { - file_udf_proto_rawDescOnce.Do(func() { - file_udf_proto_rawDescData = protoimpl.X.CompressGZIP(file_udf_proto_rawDescData) - }) - return file_udf_proto_rawDescData -} - -var file_udf_proto_enumTypes = make([]protoimpl.EnumInfo, 3) -var file_udf_proto_msgTypes = make([]protoimpl.MessageInfo, 7) -var file_udf_proto_goTypes = []interface{}{ - (RequestType)(0), // 0: udf.RequestType - (ResponseType)(0), // 1: udf.ResponseType - (DataType)(0), // 2: udf.DataType - (*Request)(nil), // 3: udf.Request - (*Response)(nil), // 4: udf.Response - (*Udf)(nil), // 5: udf.Udf - (*Package)(nil), // 6: udf.Package - (*DataVector)(nil), // 7: udf.DataVector - (*Data)(nil), // 8: udf.Data - nil, // 9: udf.Request.ContextEntry -} -var file_udf_proto_depIdxs = []int32{ - 5, // 0: udf.Request.udf:type_name -> udf.Udf - 7, // 1: udf.Request.vectors:type_name -> udf.DataVector - 0, // 2: udf.Request.type:type_name -> udf.RequestType - 9, // 3: udf.Request.context:type_name -> udf.Request.ContextEntry - 7, // 4: udf.Response.vector:type_name -> udf.DataVector - 1, // 5: udf.Response.type:type_name -> udf.ResponseType - 6, // 6: udf.Udf.importPkg:type_name -> udf.Package - 2, // 7: udf.Udf.retType:type_name -> udf.DataType - 8, // 8: udf.DataVector.data:type_name -> udf.Data - 2, // 9: udf.DataVector.type:type_name -> udf.DataType - 3, // 10: udf.Service.run:input_type -> udf.Request - 4, // 11: udf.Service.run:output_type -> udf.Response - 11, // [11:12] is the sub-list for method output_type - 10, // [10:11] is the sub-list for method input_type - 10, // [10:10] is the sub-list for extension type_name - 10, // [10:10] is the sub-list for extension extendee - 0, // [0:10] is the sub-list for field type_name -} - -func init() { file_udf_proto_init() } -func file_udf_proto_init() { - if File_udf_proto != nil { - return - } - if !protoimpl.UnsafeEnabled { - file_udf_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*Request); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_udf_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*Response); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_udf_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*Udf); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_udf_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*Package); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_udf_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*DataVector); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_udf_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*Data); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - } - file_udf_proto_msgTypes[5].OneofWrappers = []interface{}{ - (*Data_BoolVal)(nil), - (*Data_IntVal)(nil), - (*Data_Int64Val)(nil), - (*Data_UintVal)(nil), - (*Data_Uint64Val)(nil), - (*Data_FloatVal)(nil), - (*Data_DoubleVal)(nil), - (*Data_StringVal)(nil), - (*Data_BytesVal)(nil), - } - type x struct{} - out := protoimpl.TypeBuilder{ - File: protoimpl.DescBuilder{ - GoPackagePath: reflect.TypeOf(x{}).PkgPath(), - RawDescriptor: file_udf_proto_rawDesc, - NumEnums: 3, - NumMessages: 7, - NumExtensions: 0, - NumServices: 1, - }, - GoTypes: file_udf_proto_goTypes, - DependencyIndexes: file_udf_proto_depIdxs, - EnumInfos: file_udf_proto_enumTypes, - MessageInfos: file_udf_proto_msgTypes, - }.Build() - File_udf_proto = out.File - file_udf_proto_rawDesc = nil - file_udf_proto_goTypes = nil - file_udf_proto_depIdxs = nil -} diff --git a/pkg/udf/udf.proto b/pkg/udf/udf.proto deleted file mode 100644 index e21c500fab7f6..0000000000000 --- a/pkg/udf/udf.proto +++ /dev/null @@ -1,126 +0,0 @@ -// Copyright 2023 Matrix Origin -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -syntax = "proto3"; - -package udf; - -option go_package = "github.com/matrixorigin/matrixone/pkg/udf"; - -// protoc --go_out=. --go_opt=paths=source_relative --go-grpc_out=. --go-grpc_opt=paths=source_relative ./udf.proto - -service Service { - rpc run(stream Request) returns (stream Response) {} -} - -enum RequestType { - UnknownRequest = 0; - DataRequest = 1; // request with data for function result - PkgResponse = 2; // response with package -} - -message Request { - Udf udf = 1; - repeated DataVector vectors = 2; - int64 length = 3; - RequestType type = 4; - map context = 5; // currently just transfer, not used -} - -enum ResponseType { - UnknownResponse = 0; - DataResponse = 1; // response with function result - PkgRequest = 2; // request for package -} - -message Response { - DataVector vector = 1; - ResponseType type = 2; -} - -message Udf { - string handler = 1; - bool isImport = 2; - string body = 3; - Package importPkg = 4; - DataType retType = 5; - string language = 6; - string modifiedTime = 7; - string db = 8; -} - -message Package { - bytes data = 1; - bool last = 2; -} - -message DataVector { - repeated Data data = 1; - bool const = 2; - int64 length = 3; - DataType type = 4; - int32 scale = 5; -} - -enum DataType { - UNKNOWN = 0; - - BOOL = 10; - - INT8 = 20; - INT16 = 21; - INT32 = 22; - INT64 = 23; - - UINT8 = 30; - UINT16 = 31; - UINT32 = 32; - UINT64 = 33; - - FLOAT32 = 40; - FLOAT64 = 41; - - CHAR = 50; - VARCHAR = 51; - TEXT = 52; - JSON = 53; - UUID = 54; - - TIME = 60; - DATE = 61; - DATETIME = 62; - TIMESTAMP = 63; - - DECIMAL64 = 70; - DECIMAL128 = 71; - - - BINARY = 80; - VARBINARY = 81; - BLOB = 82; -} - -message Data { - oneof val { - bool boolVal = 1; // bool - sint32 intVal = 2; // int8, int16, int32 - sint64 int64Val = 3; // int64 - uint32 uintVal = 4; // uint8, uint16, uint32 - uint64 uint64Val = 5; // uint64 - float floatVal = 6; // float32 - double doubleVal = 7; // float64 - string stringVal = 8; // char, varchar, text, json, uuid | time, date, datetime, timestamp | Decimal64, Decimal128 - bytes bytesVal = 9; // binary, varbinary, blob - } -} \ No newline at end of file diff --git a/pkg/udf/udf_grpc.pb.go b/pkg/udf/udf_grpc.pb.go deleted file mode 100644 index 5e863134bba82..0000000000000 --- a/pkg/udf/udf_grpc.pb.go +++ /dev/null @@ -1,137 +0,0 @@ -// Code generated by protoc-gen-go-grpc. DO NOT EDIT. -// versions: -// - protoc-gen-go-grpc v1.2.0 -// - protoc v3.17.0 -// source: udf.proto - -package udf - -import ( - context "context" - grpc "google.golang.org/grpc" - codes "google.golang.org/grpc/codes" - status "google.golang.org/grpc/status" -) - -// This is a compile-time assertion to ensure that this generated file -// is compatible with the grpc package it is being compiled against. -// Requires gRPC-Go v1.32.0 or later. -const _ = grpc.SupportPackageIsVersion7 - -// ServiceClient is the client API for Service service. -// -// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream. -type ServiceClient interface { - Run(ctx context.Context, opts ...grpc.CallOption) (Service_RunClient, error) -} - -type serviceClient struct { - cc grpc.ClientConnInterface -} - -func NewServiceClient(cc grpc.ClientConnInterface) ServiceClient { - return &serviceClient{cc} -} - -func (c *serviceClient) Run(ctx context.Context, opts ...grpc.CallOption) (Service_RunClient, error) { - stream, err := c.cc.NewStream(ctx, &Service_ServiceDesc.Streams[0], "/udf.Service/run", opts...) - if err != nil { - return nil, err - } - x := &serviceRunClient{stream} - return x, nil -} - -type Service_RunClient interface { - Send(*Request) error - Recv() (*Response, error) - grpc.ClientStream -} - -type serviceRunClient struct { - grpc.ClientStream -} - -func (x *serviceRunClient) Send(m *Request) error { - return x.ClientStream.SendMsg(m) -} - -func (x *serviceRunClient) Recv() (*Response, error) { - m := new(Response) - if err := x.ClientStream.RecvMsg(m); err != nil { - return nil, err - } - return m, nil -} - -// ServiceServer is the server API for Service service. -// All implementations must embed UnimplementedServiceServer -// for forward compatibility -type ServiceServer interface { - Run(Service_RunServer) error - mustEmbedUnimplementedServiceServer() -} - -// UnimplementedServiceServer must be embedded to have forward compatible implementations. -type UnimplementedServiceServer struct { -} - -func (UnimplementedServiceServer) Run(Service_RunServer) error { - return status.Errorf(codes.Unimplemented, "method Run not implemented") -} -func (UnimplementedServiceServer) mustEmbedUnimplementedServiceServer() {} - -// UnsafeServiceServer may be embedded to opt out of forward compatibility for this service. -// Use of this interface is not recommended, as added methods to ServiceServer will -// result in compilation errors. -type UnsafeServiceServer interface { - mustEmbedUnimplementedServiceServer() -} - -func RegisterServiceServer(s grpc.ServiceRegistrar, srv ServiceServer) { - s.RegisterService(&Service_ServiceDesc, srv) -} - -func _Service_Run_Handler(srv interface{}, stream grpc.ServerStream) error { - return srv.(ServiceServer).Run(&serviceRunServer{stream}) -} - -type Service_RunServer interface { - Send(*Response) error - Recv() (*Request, error) - grpc.ServerStream -} - -type serviceRunServer struct { - grpc.ServerStream -} - -func (x *serviceRunServer) Send(m *Response) error { - return x.ServerStream.SendMsg(m) -} - -func (x *serviceRunServer) Recv() (*Request, error) { - m := new(Request) - if err := x.ServerStream.RecvMsg(m); err != nil { - return nil, err - } - return m, nil -} - -// Service_ServiceDesc is the grpc.ServiceDesc for Service service. -// It's only intended for direct use with grpc.RegisterService, -// and not to be introspected or modified (even as a copy) -var Service_ServiceDesc = grpc.ServiceDesc{ - ServiceName: "udf.Service", - HandlerType: (*ServiceServer)(nil), - Methods: []grpc.MethodDesc{}, - Streams: []grpc.StreamDesc{ - { - StreamName: "run", - Handler: _Service_Run_Handler, - ServerStreams: true, - ClientStreams: true, - }, - }, - Metadata: "udf.proto", -} diff --git a/pkg/vm/engine/disttae/types.go b/pkg/vm/engine/disttae/types.go index b3538baf90278..2f9b83bfaf431 100644 --- a/pkg/vm/engine/disttae/types.go +++ b/pkg/vm/engine/disttae/types.go @@ -261,7 +261,7 @@ type Engine struct { ls lockservice.LockService qc qclient.QueryClient hakeeper logservice.CNHAKeeperClient - us udf.Service + us udf.Runtime cli client.TxnClient idGen IDGenerator tnID string diff --git a/pkg/vm/process/process2.go b/pkg/vm/process/process2.go index 99345d2666305..9c76210084671 100644 --- a/pkg/vm/process/process2.go +++ b/pkg/vm/process/process2.go @@ -56,7 +56,7 @@ func NewTopProcess( fileService fileservice.FileService, lockService lockservice.LockService, queryClient qclient.QueryClient, HAKeeper logservice.CNHAKeeperClient, - udfService udf.Service, + udfService udf.Runtime, autoIncrease *defines.AutoIncrCacheManager, taskService taskservice.TaskService) *Process { diff --git a/pkg/vm/process/process_codec.go b/pkg/vm/process/process_codec.go index ae0172b45c19e..cb91f2e1e8d87 100644 --- a/pkg/vm/process/process_codec.go +++ b/pkg/vm/process/process_codec.go @@ -207,7 +207,7 @@ func NewCodecService( partitionService partitionservice.PartitionService, queryClient qclient.QueryClient, hakeeper logservice.CNHAKeeperClient, - udfService udf.Service, + udfService udf.Runtime, engine engine.Engine, ) ProcessCodecService { mp, err := mpool.NewMPool("codec", 1<<40, mpool.NoFixed) @@ -234,7 +234,7 @@ type codecService struct { partitionService partitionservice.PartitionService queryClient qclient.QueryClient hakeeper logservice.CNHAKeeperClient - udfService udf.Service + udfService udf.Runtime mp *mpool.MPool engine engine.Engine } diff --git a/pkg/vm/process/types.go b/pkg/vm/process/types.go index 39f6b3f38391c..a01a916ab5ce9 100644 --- a/pkg/vm/process/types.go +++ b/pkg/vm/process/types.go @@ -471,7 +471,7 @@ type BaseProcess struct { prepareParamsOwned bool QueryClient qclient.QueryClient Hakeeper logservice.CNHAKeeperClient - UdfService udf.Service + UdfService udf.Runtime WaitPolicy lock.WaitPolicy messageBoard *message.MessageBoard executionResourceBudgetMu sync.Mutex From ce1c5cc03c9ea43537b9dd331ef3d9858c3c0458 Mon Sep 17 00:00:00 2001 From: iamlinjunhong <1030420200@qq.com> Date: Wed, 9 Sep 2026 16:23:19 +0800 Subject: [PATCH 002/202] fix(udf): tighten runtime naming and lifecycle --- .../python-udf-server.toml | 2 +- .../python-udf-server.toml | 2 +- optools/images/pythonserver/Dockerfile | 2 +- pkg/cnservice/server.go | 3 + pkg/udf/protocol/protocol.go | 4 +- pkg/udf/protocol/protocol_test.go | 2 + pkg/udf/pythonruntime/gateway.go | 8 +- .../{pyserver => worker}/requirements.txt | 0 .../{pyserver => worker}/test_worker.py | 33 +++++ .../{pyserver => worker}/worker.py | 119 +++++++++++++++++- pkg/udf/runtime.go | 29 +++++ pkg/udf/runtime_test.go | 48 +++++++ 12 files changed, 239 insertions(+), 13 deletions(-) rename pkg/udf/pythonruntime/{pyserver => worker}/requirements.txt (100%) rename pkg/udf/pythonruntime/{pyserver => worker}/test_worker.py (73%) rename pkg/udf/pythonruntime/{pyserver => worker}/worker.py (86%) create mode 100644 pkg/udf/runtime_test.go diff --git a/etc/launch-with-python-udf-server/python-udf-server.toml b/etc/launch-with-python-udf-server/python-udf-server.toml index 53726382e8222..4c351d3d923c5 100644 --- a/etc/launch-with-python-udf-server/python-udf-server.toml +++ b/etc/launch-with-python-udf-server/python-udf-server.toml @@ -3,4 +3,4 @@ service-type = "PYTHON_UDF" [python-udf-server] uuid = "dd1dccb4-4d3c-41f8-b482-5251dc7a41b2" address = "127.0.0.1:50051" -path = "pkg/udf/pythonruntime/pyserver" +path = "pkg/udf/pythonruntime/worker" diff --git a/etc/v1/launch-with-python-udf-server/python-udf-server.toml b/etc/v1/launch-with-python-udf-server/python-udf-server.toml index 53726382e8222..4c351d3d923c5 100644 --- a/etc/v1/launch-with-python-udf-server/python-udf-server.toml +++ b/etc/v1/launch-with-python-udf-server/python-udf-server.toml @@ -3,4 +3,4 @@ service-type = "PYTHON_UDF" [python-udf-server] uuid = "dd1dccb4-4d3c-41f8-b482-5251dc7a41b2" address = "127.0.0.1:50051" -path = "pkg/udf/pythonruntime/pyserver" +path = "pkg/udf/pythonruntime/worker" diff --git a/optools/images/pythonserver/Dockerfile b/optools/images/pythonserver/Dockerfile index 8871edc0006c1..cf1b3844f391f 100644 --- a/optools/images/pythonserver/Dockerfile +++ b/optools/images/pythonserver/Dockerfile @@ -1,5 +1,5 @@ # run from root path for debug: -# docker build -t mo-python-udf-runtime -f ./optools/images/pythonserver/Dockerfile ./pkg/udf/pythonruntime/pyserver/ +# docker build -t mo-python-udf-runtime -f ./optools/images/pythonserver/Dockerfile ./pkg/udf/pythonruntime/worker/ # docker run -d --network host mo-python-udf-runtime FROM python:3.12-bookworm diff --git a/pkg/cnservice/server.go b/pkg/cnservice/server.go index 34f7725bc1e7c..55bc5bed200fa 100644 --- a/pkg/cnservice/server.go +++ b/pkg/cnservice/server.go @@ -784,6 +784,9 @@ func (s *service) stopRPCs() error { if s.queryClient != nil { err = errors.Join(err, s.queryClient.Close()) } + if closer, ok := s.udfService.(udf.RuntimeCloser); ok { + err = errors.Join(err, closer.Close()) + } if s.timestampWaiter != nil { s.timestampWaiter.Close() } diff --git a/pkg/udf/protocol/protocol.go b/pkg/udf/protocol/protocol.go index 226c7bf300802..047b23ea6ad6a 100644 --- a/pkg/udf/protocol/protocol.go +++ b/pkg/udf/protocol/protocol.go @@ -18,7 +18,6 @@ package protocol import ( - "bytes" "crypto/sha256" "encoding/hex" "encoding/json" @@ -111,8 +110,7 @@ func UnmarshalControl(data []byte) (Control, error) { return Control{}, fmt.Errorf("%w: control size %d is outside the allowed range", ErrProtocol, len(data)) } var control Control - decoder := json.NewDecoder(bytes.NewReader(data)) - if err := decoder.Decode(&control); err != nil { + if err := json.Unmarshal(data, &control); err != nil { return Control{}, fmt.Errorf("%w: decode control: %v", ErrProtocol, err) } if control.Version != Version || control.Kind == "" { diff --git a/pkg/udf/protocol/protocol_test.go b/pkg/udf/protocol/protocol_test.go index 8696d59997ec4..33938086ad5c4 100644 --- a/pkg/udf/protocol/protocol_test.go +++ b/pkg/udf/protocol/protocol_test.go @@ -42,6 +42,8 @@ func TestControlRoundTripIsBoundedAndVersioned(t *testing.T) { _, err = UnmarshalControl(bytes.Repeat([]byte{'x'}, MaxControlBytes+1)) require.ErrorIs(t, err, ErrProtocol) + _, err = UnmarshalControl(append(wire, []byte(" trailing")...)) + require.ErrorIs(t, err, ErrProtocol) _, err = MarshalControl(Control{Kind: "", Tuple: testTuple()}) require.ErrorIs(t, err, ErrProtocol) } diff --git a/pkg/udf/pythonruntime/gateway.go b/pkg/udf/pythonruntime/gateway.go index 433393d1f330e..6cbcd829a5535 100644 --- a/pkg/udf/pythonruntime/gateway.go +++ b/pkg/udf/pythonruntime/gateway.go @@ -118,6 +118,8 @@ func (g *Gateway) Execute(ctx context.Context, invocation *udf.Invocation, resul if ctx == nil { ctx = context.Background() } + streamCtx, cancel := context.WithTimeout(ctx, g.cfg.RequestTimeout) + defer cancel() if invocation == nil || result == nil || mp == nil { return fmt.Errorf("python runtime: nil invocation, result, or memory pool") } @@ -152,7 +154,7 @@ func (g *Gateway) Execute(ctx context.Context, invocation *udf.Invocation, resul return err } - stream, err := g.flight.DoExchange(ctx) + stream, err := g.flight.DoExchange(streamCtx) if err != nil { return fmt.Errorf("python runtime: open Arrow Flight exchange: %w", err) } @@ -223,7 +225,7 @@ func (g *Gateway) Execute(ctx context.Context, invocation *udf.Invocation, resul } inputEnded = true } - batchRows, err := g.receiveResultBatch(ctx, stream, invocation.Tuple, sequenceNumber, batch.Rows, &schemaFrame, returnDescriptor, result, mp, &sequence) + batchRows, err := g.receiveResultBatch(streamCtx, stream, invocation.Tuple, sequenceNumber, batch.Rows, &schemaFrame, returnDescriptor, result, mp, &sequence) if err != nil { return err } @@ -237,7 +239,7 @@ func (g *Gateway) Execute(ctx context.Context, invocation *udf.Invocation, resul if !inputEnded || lastSequence != batchIndex { return fmt.Errorf("python runtime: input stream did not reach EndInput") } - return g.receiveFinish(ctx, stream, invocation.Tuple, invocation.Length, rows, &sequence) + return g.receiveFinish(streamCtx, stream, invocation.Tuple, invocation.Length, rows, &sequence) } func encodeInputBatch(inputs []*vector.Vector, args []types.Type, start, remaining, maxBytes, maxRows int64) (encodedRecordBatch, error) { diff --git a/pkg/udf/pythonruntime/pyserver/requirements.txt b/pkg/udf/pythonruntime/worker/requirements.txt similarity index 100% rename from pkg/udf/pythonruntime/pyserver/requirements.txt rename to pkg/udf/pythonruntime/worker/requirements.txt diff --git a/pkg/udf/pythonruntime/pyserver/test_worker.py b/pkg/udf/pythonruntime/worker/test_worker.py similarity index 73% rename from pkg/udf/pythonruntime/pyserver/test_worker.py rename to pkg/udf/pythonruntime/worker/test_worker.py index 147808c6a7db6..ef15c3643a7b0 100644 --- a/pkg/udf/pythonruntime/pyserver/test_worker.py +++ b/pkg/udf/pythonruntime/worker/test_worker.py @@ -57,6 +57,39 @@ def test_zero_argument_vector_uses_context_rows(self): with self.assertRaisesRegex(ValueError, "result length"): worker._output_array(pa.array([7], type=pa.int64()), descriptor, 3) + def test_statement_context_is_whitelisted_and_frozen(self): + context = worker._statement_context( + { + "statement_id": "fence-only", + "statement_timestamp_utc": "1704067200123456", + "session_timezone_kind": "FIXED_OFFSET", + "session_timezone_offset_minutes": "+510", + "sql_mode": '["ANSI", "STRICT"]', + "current_database": "app", + "current_user": "alice", + "current_role": "writer", + "connection_collation": "utf8mb4_bin", + } + ) + self.assertIsInstance(context, worker.StatementContext) + self.assertEqual(510, context.session_timezone.offset_minutes) + self.assertEqual(("ANSI", "STRICT"), context.sql_mode) + self.assertEqual("alice", context.current_user) + self.assertIsNone(worker._statement_context({"statement_id": "fence-only"})) + with self.assertRaisesRegex(ValueError, "unsupported statement context"): + worker._statement_context({"unexpected": "value"}) + with self.assertRaisesRegex(ValueError, "not canonical"): + worker._statement_context( + { + "statement_timestamp_utc": "1704067200000000", + "session_timezone_kind": "FIXED_OFFSET", + "session_timezone_offset_minutes": "0", + "sql_mode": '["STRICT", "ANSI"]', + "current_user": "alice", + "connection_collation": "utf8mb4_bin", + } + ) + def test_terminal_admission_does_not_evict_live_tombstones(self): old_records = worker.MAX_TERMINAL_RECORDS old_bytes = worker.MAX_TERMINAL_BYTES diff --git a/pkg/udf/pythonruntime/pyserver/worker.py b/pkg/udf/pythonruntime/worker/worker.py similarity index 86% rename from pkg/udf/pythonruntime/pyserver/worker.py rename to pkg/udf/pythonruntime/worker/worker.py index ef12c8ea37f30..1eb732105fab5 100644 --- a/pkg/udf/pythonruntime/pyserver/worker.py +++ b/pkg/udf/pythonruntime/worker/worker.py @@ -78,6 +78,25 @@ class SqlTimestamp: value: Optional[_datetime.datetime] +@dataclass(frozen=True, slots=True) +class TimezoneContext: + kind: str + name: Optional[str] = None + offset_minutes: Optional[int] = None + tzdb_version: Optional[str] = None + + +@dataclass(frozen=True, slots=True) +class StatementContext: + statement_timestamp_utc: _datetime.datetime + session_timezone: TimezoneContext + sql_mode: tuple[str, ...] + current_database: Optional[str] + current_user: str + current_role: Optional[str] + connection_collation: str + + class _CallLogger: __slots__ = () @@ -95,17 +114,108 @@ def error(self, message: str, *args: Any) -> None: class ScalarContext: sdk_version: str logger: _CallLogger - statement: Any = None + statement: Optional[StatementContext] = None @dataclass(frozen=True, slots=True) class VectorContext: sdk_version: str logger: _CallLogger - statement: Any = None + statement: Optional[StatementContext] = None num_rows: int = 0 +_FENCING_CONTEXT_KEYS = frozenset( + {"account_id", "statement_id", "group_id", "group_epoch", "invocation_id", "lease_epoch"} +) +_STATEMENT_CONTEXT_KEYS = frozenset( + { + "statement_timestamp_utc", + "session_timezone_kind", + "session_timezone_name", + "session_timezone_offset_minutes", + "session_timezone_tzdb_version", + "sql_mode", + "current_database", + "current_user", + "current_role", + "connection_collation", + } +) + + +def _required_context_value(context: Dict[str, Any], key: str) -> str: + value = context.get(key) + if not isinstance(value, str) or not value: + raise ValueError(f"PROTOCOL: missing statement context field {key}") + return value + + +def _statement_context(raw: Any) -> Optional[StatementContext]: + if raw is None: + return None + if not isinstance(raw, dict): + raise ValueError("PROTOCOL: statement context must be an object") + unknown = set(raw) - _FENCING_CONTEXT_KEYS - _STATEMENT_CONTEXT_KEYS + if unknown: + raise ValueError("PROTOCOL: unsupported statement context field") + if not (set(raw) & _STATEMENT_CONTEXT_KEYS): + # The fencing fields are transport identity, not user-visible context. + return None + + timestamp_text = _required_context_value(raw, "statement_timestamp_utc") + try: + timestamp_micros = int(timestamp_text, 10) + seconds, micros = divmod(timestamp_micros, 1_000_000) + timestamp = _datetime.datetime(1970, 1, 1, tzinfo=_datetime.timezone.utc) + _datetime.timedelta(seconds=seconds, microseconds=micros) + except (TypeError, ValueError, OverflowError) as exc: + raise ValueError("PROTOCOL: invalid statement_timestamp_utc") from exc + + timezone_kind = _required_context_value(raw, "session_timezone_kind").upper() + if timezone_kind == "IANA": + timezone_name = _required_context_value(raw, "session_timezone_name") + tzdb_version = _required_context_value(raw, "session_timezone_tzdb_version") + if raw.get("session_timezone_offset_minutes"): + raise ValueError("PROTOCOL: IANA timezone cannot carry a fixed offset") + timezone = TimezoneContext("IANA", name=timezone_name, tzdb_version=tzdb_version) + elif timezone_kind == "FIXED_OFFSET": + offset_text = _required_context_value(raw, "session_timezone_offset_minutes") + try: + offset_minutes = int(offset_text, 10) + except (TypeError, ValueError) as exc: + raise ValueError("PROTOCOL: invalid fixed timezone offset") from exc + if offset_minutes < -839 or offset_minutes > 840: + raise ValueError("PROTOCOL: fixed timezone offset is outside SQL range") + if raw.get("session_timezone_name") or raw.get("session_timezone_tzdb_version"): + raise ValueError("PROTOCOL: fixed timezone cannot carry IANA identity") + timezone = TimezoneContext("FIXED_OFFSET", offset_minutes=offset_minutes) + else: + raise ValueError("PROTOCOL: unsupported timezone kind") + + sql_mode_text = _required_context_value(raw, "sql_mode") + try: + sql_mode_value = json.loads(sql_mode_text) + except (TypeError, ValueError) as exc: + raise ValueError("PROTOCOL: sql_mode must be canonical JSON array") from exc + if not isinstance(sql_mode_value, list) or any(not isinstance(item, str) for item in sql_mode_value): + raise ValueError("PROTOCOL: sql_mode must be an array of strings") + sql_mode = tuple(sorted(set(sql_mode_value))) + if list(sql_mode) != sql_mode_value: + raise ValueError("PROTOCOL: sql_mode is not canonical") + + current_user = _required_context_value(raw, "current_user") + connection_collation = _required_context_value(raw, "connection_collation") + return StatementContext( + statement_timestamp_utc=timestamp, + session_timezone=timezone, + sql_mode=sql_mode, + current_database=raw.get("current_database") or None, + current_user=current_user, + current_role=raw.get("current_role") or None, + connection_collation=connection_collation, + ) + + def _tuple_key(value: Dict[str, Any]) -> tuple: required = ("account_id", "statement_id", "group_id", "group_epoch", "invocation_id", "lease_epoch") if any(not value.get(k) for k in required): @@ -528,6 +638,7 @@ def do_exchange(self, context, descriptor, reader, writer): if isinstance(payload, str): payload = json.loads(payload) args = list(payload.get("args", [])); result_descriptor = payload["return"] + statement_context = _statement_context(payload.get("context")) mode = payload.get("mode", MODE_SCALAR); null_policy = payload.get("null_policy", NULL_CALL) abi_contract = payload.get("abi_contract", ABI_CONTRACT) adapter_version = payload.get("adapter_version", ADAPTER_VERSION) @@ -563,7 +674,7 @@ def do_exchange(self, context, descriptor, reader, writer): if batch.num_rows <= 0: raise ValueError("PROTOCOL: empty input batch") if batch.num_rows > max_batch_rows: raise ValueError("RESOURCE_EXHAUSTED: input batch has too many rows") if mode == MODE_VECTOR: - call_context = VectorContext(sdk_version, _CallLogger(), None, batch.num_rows) + call_context = VectorContext(sdk_version, _CallLogger(), statement_context, batch.num_rows) try: output = handler(call_context, *[batch.column(index) for index in range(batch.num_columns)]) except Exception as exc: @@ -571,7 +682,7 @@ def do_exchange(self, context, descriptor, reader, writer): if not isinstance(output, (pa.Array, pa.ChunkedArray)): raise ValueError("TYPE_CONTRACT: VECTOR handler must return an Arrow Array or ChunkedArray") else: - call_context = ScalarContext(sdk_version, _CallLogger(), None) + call_context = ScalarContext(sdk_version, _CallLogger(), statement_context) values = [] for row in range(batch.num_rows): params = [_scalar_input(batch.column(index), row, args[index]) for index in range(batch.num_columns)] diff --git a/pkg/udf/runtime.go b/pkg/udf/runtime.go index 33b2b90280ecf..5df11f7a374de 100644 --- a/pkg/udf/runtime.go +++ b/pkg/udf/runtime.go @@ -20,6 +20,9 @@ package udf import ( "context" + "errors" + "sort" + "sync" "github.com/matrixorigin/matrixone/pkg/common/moerr" "github.com/matrixorigin/matrixone/pkg/common/mpool" @@ -64,6 +67,14 @@ type Runtime interface { Execute(context.Context, *Invocation, vector.FunctionResultWrapper, *mpool.MPool) error } +// RuntimeCloser is an optional lifecycle contract for runtimes that own a +// transport connection or a worker process. Execution callers only need +// Runtime; service shutdown uses this interface to close such resources. +type RuntimeCloser interface { + Runtime + Close() error +} + // Registry dispatches to the one runtime selected by a routine's language. // A registry is immutable after construction, so plan execution cannot race // service registration or observe a partially initialized adapter. @@ -83,6 +94,8 @@ func NewRuntime(runtimes ...Runtime) (Runtime, error) { type runtimeRegistry struct { byLanguage map[string]Runtime + closeOnce sync.Once + closeErr error } func (r *runtimeRegistry) Language() string { return "multiple" } @@ -102,3 +115,19 @@ func (r *runtimeRegistry) Execute( } return runtime.Execute(ctx, invocation, result, mp) } + +func (r *runtimeRegistry) Close() error { + r.closeOnce.Do(func() { + languages := make([]string, 0, len(r.byLanguage)) + for language := range r.byLanguage { + languages = append(languages, language) + } + sort.Strings(languages) + for _, language := range languages { + if closer, ok := r.byLanguage[language].(RuntimeCloser); ok { + r.closeErr = errors.Join(r.closeErr, closer.Close()) + } + } + }) + return r.closeErr +} diff --git a/pkg/udf/runtime_test.go b/pkg/udf/runtime_test.go new file mode 100644 index 0000000000000..908d7764f225b --- /dev/null +++ b/pkg/udf/runtime_test.go @@ -0,0 +1,48 @@ +// Copyright 2026 Matrix Origin +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. + +package udf + +import ( + "context" + "sync/atomic" + "testing" + + "github.com/matrixorigin/matrixone/pkg/common/mpool" + "github.com/matrixorigin/matrixone/pkg/container/vector" + "github.com/stretchr/testify/require" +) + +type closableRuntime struct { + language string + closed atomic.Int32 +} + +func (r *closableRuntime) Language() string { return r.language } + +func (r *closableRuntime) Execute( + context.Context, + *Invocation, + vector.FunctionResultWrapper, + *mpool.MPool, +) error { + return nil +} + +func (r *closableRuntime) Close() error { + r.closed.Add(1) + return nil +} + +func TestRuntimeRegistryClosesOwnedRuntimesOnce(t *testing.T) { + python := &closableRuntime{language: LanguagePython} + runtime, err := NewRuntime(python) + require.NoError(t, err) + closer, ok := runtime.(RuntimeCloser) + require.True(t, ok) + require.NoError(t, closer.Close()) + require.NoError(t, closer.Close()) + require.Equal(t, int32(1), python.closed.Load()) +} From bbb368f1cdc1b44c8b4fdaf21242cf7c44622711 Mon Sep 17 00:00:00 2001 From: iamlinjunhong <1030420200@qq.com> Date: Wed, 9 Sep 2026 16:31:11 +0800 Subject: [PATCH 003/202] fix(udf): rename Python adapter and fence duplicate calls --- cmd/mo-service/config.go | 4 +- cmd/mo-service/main.go | 4 +- .../python-udf-server.toml | 2 +- .../python-udf-server.toml | 2 +- optools/images/pythonserver/Dockerfile | 2 +- pkg/cnservice/server.go | 6 +-- pkg/cnservice/types.go | 4 +- pkg/embed/config.go | 4 +- pkg/udf/protocol/protocol.go | 38 ++++++++-------- pkg/udf/protocol/protocol_test.go | 14 ++++++ pkg/udf/{pythonruntime => python}/arrow.go | 2 +- .../{pythonruntime => python}/arrow_test.go | 2 +- pkg/udf/{pythonruntime => python}/config.go | 2 +- pkg/udf/{pythonruntime => python}/gateway.go | 2 +- .../{pythonruntime => python}/supervisor.go | 2 +- .../worker/requirements.txt | 0 .../worker/test_worker.py | 12 ++++++ .../worker/worker.py | 43 ++++++++++++++++--- 18 files changed, 102 insertions(+), 43 deletions(-) rename pkg/udf/{pythonruntime => python}/arrow.go (99%) rename pkg/udf/{pythonruntime => python}/arrow_test.go (99%) rename pkg/udf/{pythonruntime => python}/config.go (98%) rename pkg/udf/{pythonruntime => python}/gateway.go (99%) rename pkg/udf/{pythonruntime => python}/supervisor.go (99%) rename pkg/udf/{pythonruntime => python}/worker/requirements.txt (100%) rename pkg/udf/{pythonruntime => python}/worker/test_worker.py (89%) rename pkg/udf/{pythonruntime => python}/worker/worker.py (95%) diff --git a/cmd/mo-service/config.go b/cmd/mo-service/config.go index 437a1e11a0974..dcba9c7d889bf 100644 --- a/cmd/mo-service/config.go +++ b/cmd/mo-service/config.go @@ -45,7 +45,7 @@ import ( "github.com/matrixorigin/matrixone/pkg/perfcounter" "github.com/matrixorigin/matrixone/pkg/proxy" "github.com/matrixorigin/matrixone/pkg/tnservice" - "github.com/matrixorigin/matrixone/pkg/udf/pythonruntime" + "github.com/matrixorigin/matrixone/pkg/udf/python" "github.com/matrixorigin/matrixone/pkg/util/debug/goroutine" tomlutil "github.com/matrixorigin/matrixone/pkg/util/toml" "github.com/matrixorigin/matrixone/pkg/version" @@ -124,7 +124,7 @@ type Config struct { // ProxyConfig is the config of proxy. ProxyConfig proxy.Config `toml:"proxy"` // PythonUdfServerConfig is the config of python udf server - PythonUdfServerConfig pythonruntime.Config `toml:"python-udf-server"` + PythonUdfServerConfig python.Config `toml:"python-udf-server"` // Observability parameters for the metric/trace Observability config.ObservabilityParameters `toml:"observability"` diff --git a/cmd/mo-service/main.go b/cmd/mo-service/main.go index 602bd8be3218a..4086df7cb9fa0 100644 --- a/cmd/mo-service/main.go +++ b/cmd/mo-service/main.go @@ -52,7 +52,7 @@ import ( qclient "github.com/matrixorigin/matrixone/pkg/queryservice/client" "github.com/matrixorigin/matrixone/pkg/sql/compile" "github.com/matrixorigin/matrixone/pkg/tnservice" - "github.com/matrixorigin/matrixone/pkg/udf/pythonruntime" + "github.com/matrixorigin/matrixone/pkg/udf/python" "github.com/matrixorigin/matrixone/pkg/util/debug/goroutine" "github.com/matrixorigin/matrixone/pkg/util/export" "github.com/matrixorigin/matrixone/pkg/util/export/table" @@ -757,7 +757,7 @@ func startPythonUdfService(cfg *Config, stopper *stopper.Stopper) error { defer func() { finishTask(closeErr) }() roleCtx, cancelRole := serviceLifecycle.roleContext(ctx, serviceRolePython) defer cancelRole() - s, err := pythonruntime.NewSupervisor(cfg.PythonUdfServerConfig) + s, err := python.NewSupervisor(cfg.PythonUdfServerConfig) if err != nil { panic(err) } diff --git a/etc/launch-with-python-udf-server/python-udf-server.toml b/etc/launch-with-python-udf-server/python-udf-server.toml index 4c351d3d923c5..37efe868191bc 100644 --- a/etc/launch-with-python-udf-server/python-udf-server.toml +++ b/etc/launch-with-python-udf-server/python-udf-server.toml @@ -3,4 +3,4 @@ service-type = "PYTHON_UDF" [python-udf-server] uuid = "dd1dccb4-4d3c-41f8-b482-5251dc7a41b2" address = "127.0.0.1:50051" -path = "pkg/udf/pythonruntime/worker" +path = "pkg/udf/python/worker" diff --git a/etc/v1/launch-with-python-udf-server/python-udf-server.toml b/etc/v1/launch-with-python-udf-server/python-udf-server.toml index 4c351d3d923c5..37efe868191bc 100644 --- a/etc/v1/launch-with-python-udf-server/python-udf-server.toml +++ b/etc/v1/launch-with-python-udf-server/python-udf-server.toml @@ -3,4 +3,4 @@ service-type = "PYTHON_UDF" [python-udf-server] uuid = "dd1dccb4-4d3c-41f8-b482-5251dc7a41b2" address = "127.0.0.1:50051" -path = "pkg/udf/pythonruntime/worker" +path = "pkg/udf/python/worker" diff --git a/optools/images/pythonserver/Dockerfile b/optools/images/pythonserver/Dockerfile index cf1b3844f391f..88022f7fafd1f 100644 --- a/optools/images/pythonserver/Dockerfile +++ b/optools/images/pythonserver/Dockerfile @@ -1,5 +1,5 @@ # run from root path for debug: -# docker build -t mo-python-udf-runtime -f ./optools/images/pythonserver/Dockerfile ./pkg/udf/pythonruntime/worker/ +# docker build -t mo-python-udf-runtime -f ./optools/images/pythonserver/Dockerfile ./pkg/udf/python/worker/ # docker run -d --network host mo-python-udf-runtime FROM python:3.12-bookworm diff --git a/pkg/cnservice/server.go b/pkg/cnservice/server.go index 55bc5bed200fa..fd4d7ceac89b9 100644 --- a/pkg/cnservice/server.go +++ b/pkg/cnservice/server.go @@ -71,7 +71,7 @@ import ( "github.com/matrixorigin/matrixone/pkg/txn/rpc" "github.com/matrixorigin/matrixone/pkg/txn/trace" "github.com/matrixorigin/matrixone/pkg/udf" - "github.com/matrixorigin/matrixone/pkg/udf/pythonruntime" + "github.com/matrixorigin/matrixone/pkg/udf/python" "github.com/matrixorigin/matrixone/pkg/util/address" "github.com/matrixorigin/matrixone/pkg/util/executor" v2 "github.com/matrixorigin/matrixone/pkg/util/metric/v2" @@ -243,8 +243,8 @@ func NewService( var udfServices []udf.Runtime // add python client to handle python udf if srv.cfg.PythonUdfClient.ServerAddress != "" { - var pc *pythonruntime.Gateway - pc, err = pythonruntime.NewGateway(srv.cfg.PythonUdfClient) + var pc *python.Gateway + pc, err = python.NewGateway(srv.cfg.PythonUdfClient) if err != nil { panic(err) } diff --git a/pkg/cnservice/types.go b/pkg/cnservice/types.go index c7ea467797dbb..b4b92ee3a66bc 100644 --- a/pkg/cnservice/types.go +++ b/pkg/cnservice/types.go @@ -55,7 +55,7 @@ import ( "github.com/matrixorigin/matrixone/pkg/txn/rpc" "github.com/matrixorigin/matrixone/pkg/txn/trace" "github.com/matrixorigin/matrixone/pkg/udf" - "github.com/matrixorigin/matrixone/pkg/udf/pythonruntime" + "github.com/matrixorigin/matrixone/pkg/udf/python" "github.com/matrixorigin/matrixone/pkg/util" "github.com/matrixorigin/matrixone/pkg/util/address" "github.com/matrixorigin/matrixone/pkg/util/executor" @@ -330,7 +330,7 @@ type Config struct { // "working", "draining" and "drained". InitWorkState string `toml:"init-work-state"` - PythonUdfClient pythonruntime.ClientConfig `toml:"python-udf-client"` + PythonUdfClient python.ClientConfig `toml:"python-udf-client"` // LogtailUpdateWorkerFactor is the times of CPU number of this node // to start update workers. diff --git a/pkg/embed/config.go b/pkg/embed/config.go index c1fcd1c3c9ff0..c0603e39cc4a6 100644 --- a/pkg/embed/config.go +++ b/pkg/embed/config.go @@ -40,7 +40,7 @@ import ( "github.com/matrixorigin/matrixone/pkg/perfcounter" "github.com/matrixorigin/matrixone/pkg/proxy" "github.com/matrixorigin/matrixone/pkg/tnservice" - "github.com/matrixorigin/matrixone/pkg/udf/pythonruntime" + "github.com/matrixorigin/matrixone/pkg/udf/python" "github.com/matrixorigin/matrixone/pkg/util" "github.com/matrixorigin/matrixone/pkg/util/debug/goroutine" tomlutil "github.com/matrixorigin/matrixone/pkg/util/toml" @@ -95,7 +95,7 @@ type ServiceConfig struct { // ProxyConfig is the config of proxy. ProxyConfig proxy.Config `toml:"proxy"` // PythonUdfServerConfig is the config of python udf server - PythonUdfServerConfig pythonruntime.Config `toml:"python-udf-server"` + PythonUdfServerConfig python.Config `toml:"python-udf-server"` // Observability parameters for the metric/trace Observability config.ObservabilityParameters `toml:"observability"` diff --git a/pkg/udf/protocol/protocol.go b/pkg/udf/protocol/protocol.go index 047b23ea6ad6a..ea7a61941cb00 100644 --- a/pkg/udf/protocol/protocol.go +++ b/pkg/udf/protocol/protocol.go @@ -318,6 +318,7 @@ type ExecutionGroup struct { registered int inFlight int members map[string]bool + terminal map[string]bool closing bool releaseBusy bool released bool @@ -331,7 +332,7 @@ func NewExecutionGroup(id string, epoch uint64, maxMembers int, release func() e } return &ExecutionGroup{ id: id, epoch: epoch, maxMembers: maxMembers, - members: make(map[string]bool), release: release, + members: make(map[string]bool), terminal: make(map[string]bool), release: release, }, nil } @@ -346,7 +347,7 @@ func (g *ExecutionGroup) BeginOpen(memberID string) (*OpenToken, error) { if g.closing || g.released { return nil, ErrGroupClosed } - if g.members[memberID] { + if g.members[memberID] || g.terminal[memberID] { return nil, ErrDuplicate } if g.registered+g.inFlight >= g.maxMembers { @@ -360,52 +361,48 @@ type OpenToken struct { group *ExecutionGroup memberID string once sync.Once + result error } func (t *OpenToken) Commit() error { if t == nil || t.group == nil { return ErrUnknownIdentity } - var err error - var release bool t.once.Do(func() { g := t.group g.mu.Lock() g.inFlight-- if g.closing || g.released { - err = ErrGroupClosed - release = g.canReleaseLocked() + t.result = ErrGroupClosed + release := g.canReleaseLocked() g.mu.Unlock() + if release { + _ = t.group.releaseIfReady() + } return } g.members[t.memberID] = true g.registered++ g.mu.Unlock() }) - if release { - if releaseErr := t.group.releaseIfReady(); err == nil { - err = releaseErr - } - } - return err + return t.result } func (t *OpenToken) Abort() error { if t == nil || t.group == nil { return ErrUnknownIdentity } - var release bool t.once.Do(func() { g := t.group g.mu.Lock() g.inFlight-- - release = g.canReleaseLocked() + release := g.canReleaseLocked() g.mu.Unlock() + if release { + t.result = t.group.releaseIfReady() + } }) - if release { - return t.group.releaseIfReady() - } - return nil + return t.result } func (g *ExecutionGroup) Close(reason CloseReason) error { @@ -431,11 +428,16 @@ func (g *ExecutionGroup) Close(reason CloseReason) error { func (g *ExecutionGroup) MemberTerminal(memberID string) error { g.mu.Lock() + if g.terminal[memberID] { + g.mu.Unlock() + return nil + } if !g.members[memberID] { g.mu.Unlock() return ErrUnknownIdentity } delete(g.members, memberID) + g.terminal[memberID] = true release := g.canReleaseLocked() g.mu.Unlock() if release { diff --git a/pkg/udf/protocol/protocol_test.go b/pkg/udf/protocol/protocol_test.go index 33938086ad5c4..f535bf43df98f 100644 --- a/pkg/udf/protocol/protocol_test.go +++ b/pkg/udf/protocol/protocol_test.go @@ -94,6 +94,9 @@ func TestExecutionGroupReleasesOnlyAfterCloseAndAllMembersTerminal(t *testing.T) require.NoError(t, b.Commit()) require.NoError(t, group.MemberTerminal("a")) + require.NoError(t, group.MemberTerminal("a")) + _, err = group.BeginOpen("a") + require.ErrorIs(t, err, ErrDuplicate) require.NoError(t, group.Close(ReasonInputEOF)) require.Equal(t, int32(0), releases.Load()) require.NoError(t, group.MemberTerminal("b")) @@ -127,6 +130,17 @@ func TestExecutionGroupZeroMemberAndAbortedOpenRelease(t *testing.T) { require.NoError(t, err) require.NoError(t, group.Close(ReasonEmptyInput)) require.Equal(t, int32(2), releases.Load()) + + group, err = NewExecutionGroup("late", 1, 1, func() error { + releases.Add(1) + return nil + }) + require.NoError(t, err) + open, err = group.BeginOpen("late-member") + require.NoError(t, err) + require.NoError(t, group.Close(ReasonCancel)) + require.ErrorIs(t, open.Commit(), ErrGroupClosed) + require.ErrorIs(t, open.Commit(), ErrGroupClosed) } func TestTerminalLedgerRetainsTombstonesUntilExpiry(t *testing.T) { diff --git a/pkg/udf/pythonruntime/arrow.go b/pkg/udf/python/arrow.go similarity index 99% rename from pkg/udf/pythonruntime/arrow.go rename to pkg/udf/python/arrow.go index c65bb9aea92f1..65a66dec35d61 100644 --- a/pkg/udf/pythonruntime/arrow.go +++ b/pkg/udf/python/arrow.go @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -package pythonruntime +package python import ( "bytes" diff --git a/pkg/udf/pythonruntime/arrow_test.go b/pkg/udf/python/arrow_test.go similarity index 99% rename from pkg/udf/pythonruntime/arrow_test.go rename to pkg/udf/python/arrow_test.go index 97ed943333f54..f4ed3027bd689 100644 --- a/pkg/udf/pythonruntime/arrow_test.go +++ b/pkg/udf/python/arrow_test.go @@ -3,7 +3,7 @@ // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. -package pythonruntime +package python import ( "testing" diff --git a/pkg/udf/pythonruntime/config.go b/pkg/udf/python/config.go similarity index 98% rename from pkg/udf/pythonruntime/config.go rename to pkg/udf/python/config.go index c6b6ae301d25b..4c4d191639fe0 100644 --- a/pkg/udf/pythonruntime/config.go +++ b/pkg/udf/python/config.go @@ -6,7 +6,7 @@ // // http://www.apache.org/licenses/LICENSE-2.0 -package pythonruntime +package python import ( "context" diff --git a/pkg/udf/pythonruntime/gateway.go b/pkg/udf/python/gateway.go similarity index 99% rename from pkg/udf/pythonruntime/gateway.go rename to pkg/udf/python/gateway.go index 6cbcd829a5535..00442d64eafed 100644 --- a/pkg/udf/pythonruntime/gateway.go +++ b/pkg/udf/python/gateway.go @@ -6,7 +6,7 @@ // // http://www.apache.org/licenses/LICENSE-2.0 -package pythonruntime +package python import ( "bytes" diff --git a/pkg/udf/pythonruntime/supervisor.go b/pkg/udf/python/supervisor.go similarity index 99% rename from pkg/udf/pythonruntime/supervisor.go rename to pkg/udf/python/supervisor.go index 90c171368a75a..901aa8bdc0526 100644 --- a/pkg/udf/pythonruntime/supervisor.go +++ b/pkg/udf/python/supervisor.go @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at -package pythonruntime +package python import ( "errors" diff --git a/pkg/udf/pythonruntime/worker/requirements.txt b/pkg/udf/python/worker/requirements.txt similarity index 100% rename from pkg/udf/pythonruntime/worker/requirements.txt rename to pkg/udf/python/worker/requirements.txt diff --git a/pkg/udf/pythonruntime/worker/test_worker.py b/pkg/udf/python/worker/test_worker.py similarity index 89% rename from pkg/udf/pythonruntime/worker/test_worker.py rename to pkg/udf/python/worker/test_worker.py index ef15c3643a7b0..af802a9ae7fff 100644 --- a/pkg/udf/pythonruntime/worker/test_worker.py +++ b/pkg/udf/python/worker/test_worker.py @@ -115,6 +115,18 @@ def test_terminal_admission_does_not_evict_live_tombstones(self): worker.MAX_TERMINAL_RECORDS = old_records worker.MAX_TERMINAL_BYTES = old_bytes + def test_active_fence_cannot_be_admitted_twice(self): + server = worker.RoutineFlightServer("grpc://127.0.0.1:0") + key = (1, "statement", "group", 1, "invocation", 1) + server._admit(key) + with self.assertRaisesRegex(ValueError, "fence is active"): + server._admit(key) + with self.assertRaisesRegex(ValueError, "fencing tuple changed"): + worker._require_tuple( + {"account_id": 1, "statement_id": "other", "group_id": "group", "group_epoch": 1, "invocation_id": "invocation", "lease_epoch": 1}, + {"account_id": 1, "statement_id": "statement", "group_id": "group", "group_epoch": 1, "invocation_id": "invocation", "lease_epoch": 1}, + ) + if __name__ == "__main__": unittest.main() diff --git a/pkg/udf/pythonruntime/worker/worker.py b/pkg/udf/python/worker/worker.py similarity index 95% rename from pkg/udf/pythonruntime/worker/worker.py rename to pkg/udf/python/worker/worker.py index 1eb732105fab5..3a24237de18e3 100644 --- a/pkg/udf/pythonruntime/worker/worker.py +++ b/pkg/udf/python/worker/worker.py @@ -217,22 +217,43 @@ def _statement_context(raw: Any) -> Optional[StatementContext]: def _tuple_key(value: Dict[str, Any]) -> tuple: - required = ("account_id", "statement_id", "group_id", "group_epoch", "invocation_id", "lease_epoch") - if any(not value.get(k) for k in required): + string_fields = ("statement_id", "group_id", "invocation_id") + numeric_fields = ("account_id", "group_epoch", "lease_epoch") + if not isinstance(value, dict) or any(not isinstance(value.get(k), str) or not value[k] for k in string_fields): raise ValueError("PROTOCOL: incomplete fencing tuple") - return tuple(value[k] for k in required) + if any( + isinstance(value.get(k), bool) + or not isinstance(value.get(k), int) + or value[k] <= 0 + or value[k] > (1 << 64) - 1 + for k in numeric_fields + ): + raise ValueError("PROTOCOL: incomplete fencing tuple") + return ( + value["account_id"], + value["statement_id"], + value["group_id"], + value["group_epoch"], + value["invocation_id"], + value["lease_epoch"], + ) def _decode_control(data: bytes) -> Dict[str, Any]: if not data or len(data) > MAX_CONTROL_BYTES: raise ValueError("PROTOCOL: invalid control size") value = json.loads(bytes(data).decode("utf-8")) - if value.get("version") != PROTOCOL_VERSION or not value.get("kind"): + if not isinstance(value, dict) or value.get("version") != PROTOCOL_VERSION or not isinstance(value.get("kind"), str) or not value["kind"]: raise ValueError("PROTOCOL: unsupported control") _tuple_key(value.get("tuple") or {}) return value +def _require_tuple(value: Dict[str, Any], expected: Dict[str, Any]) -> None: + if _tuple_key(value) != _tuple_key(expected): + raise ValueError("PROTOCOL: fencing tuple changed") + + def _encode_control(value: Dict[str, Any]) -> bytes: value = dict(value) value.setdefault("version", PROTOCOL_VERSION) @@ -586,6 +607,8 @@ def _admit(self, key: tuple) -> _InvocationState: size = self._entry_bytes(key) with self._lock: self._purge_terminal_locked(time.monotonic()) + if key in self._active: + raise ValueError("PROTOCOL: invocation fence is active") if key in self._terminal: raise ValueError("PROTOCOL: invocation fence is terminal") if len(self._active) + len(self._terminal) >= MAX_TERMINAL_RECORDS: @@ -606,6 +629,10 @@ def _remember_terminal_locked(self, key: tuple, state: _InvocationState) -> None def do_action(self, context, action): control = _decode_control(action.body) key = _tuple_key(control["tuple"]) + if action.type not in ("AcknowledgeResults", "AcknowledgeFinish"): + raise ValueError("PROTOCOL: unknown action") + if control["kind"] != action.type: + raise ValueError("PROTOCOL: action kind does not match action type") with self._lock: self._purge_terminal_locked(time.monotonic()) state = self._active.get(key) @@ -619,8 +646,6 @@ def do_action(self, context, action): state.ack_result(int(control.get("ack_sequence", 0))) elif action.type == "AcknowledgeFinish": state.ack_finish(str(control.get("finish_id", ""))) - else: - raise ValueError("PROTOCOL: unknown action") yield _encode_control({"kind": "Ack", "tuple": control["tuple"], "status": "OK", "ack_sequence": control.get("ack_sequence", 0)}) def do_exchange(self, context, descriptor, reader, writer): @@ -633,10 +658,14 @@ def do_exchange(self, context, descriptor, reader, writer): raise ValueError("PROTOCOL: exchange is missing the invocation descriptor") open_control = _decode_control(command) if open_control["kind"] != "OpenInvocation": raise ValueError("PROTOCOL: first message must open an invocation") + if not isinstance(open_control.get("payload"), (dict, str)): + raise ValueError("PROTOCOL: invocation payload must be an object") key = _tuple_key(open_control["tuple"]) payload = open_control.get("payload") or {} if isinstance(payload, str): payload = json.loads(payload) + if not isinstance(payload, dict): + raise ValueError("PROTOCOL: invocation payload must be an object") args = list(payload.get("args", [])); result_descriptor = payload["return"] statement_context = _statement_context(payload.get("context")) mode = payload.get("mode", MODE_SCALAR); null_policy = payload.get("null_policy", NULL_CALL) @@ -667,6 +696,7 @@ def do_exchange(self, context, descriptor, reader, writer): schema = reader.schema _validate_schema(schema, args) control = _decode_control(chunk.app_metadata) + _require_tuple(control["tuple"], open_control["tuple"]) if control["kind"] != "InputBatch": raise ValueError("PROTOCOL: data batch is missing InputBatch") sequence = int(control.get("sequence", 0)) if sequence != state.last_result + 1: raise ValueError("PROTOCOL: input sequence is not contiguous") @@ -701,6 +731,7 @@ def do_exchange(self, context, descriptor, reader, writer): state.wait_result_ack(sequence) elif chunk.app_metadata: control = _decode_control(chunk.app_metadata) + _require_tuple(control["tuple"], open_control["tuple"]) if control["kind"] == "EndInput": if ended or int(control.get("last_sequence", -1)) != state.last_result: raise ValueError("PROTOCOL: invalid EndInput") ended = True From d9d37f08c14364cf63aa13261ac29172a3b3e2af Mon Sep 17 00:00:00 2001 From: iamlinjunhong <1030420200@qq.com> Date: Wed, 9 Sep 2026 16:59:27 +0800 Subject: [PATCH 004/202] fix(udf): close remaining Python worker contracts --- cmd/mo-service/config.go | 6 +- cmd/mo-service/main.go | 12 +- .../cn.toml | 0 .../launch.toml | 5 +- .../python-udf-worker.toml} | 2 +- .../cn.toml | 0 .../launch.toml | 4 +- .../python-udf-worker.toml} | 2 +- .../{pythonserver => pythonworker}/Dockerfile | 4 +- pkg/embed/config.go | 4 +- pkg/frontend/clone_database_source.go | 7 +- pkg/sql/plan/function/python_udf.go | 47 +++--- pkg/udf/protocol/protocol.go | 10 +- pkg/udf/protocol/protocol_test.go | 31 ++++ pkg/udf/python/arrow.go | 103 +++++++++++- pkg/udf/python/arrow_test.go | 17 ++ pkg/udf/python/config.go | 12 +- pkg/udf/python/gateway.go | 99 ++++++------ pkg/udf/python/gateway_test.go | 60 +++++++ pkg/udf/python/supervisor.go | 9 +- pkg/udf/python/worker/test_worker.py | 25 ++- pkg/udf/python/worker/worker.py | 150 ++++++++++++++---- 22 files changed, 464 insertions(+), 145 deletions(-) rename etc/{launch-with-python-udf-server => launch-with-python-udf-worker}/cn.toml (100%) rename etc/{launch-with-python-udf-server => launch-with-python-udf-worker}/launch.toml (54%) rename etc/{launch-with-python-udf-server/python-udf-server.toml => launch-with-python-udf-worker/python-udf-worker.toml} (87%) rename etc/v1/{launch-with-python-udf-server => launch-with-python-udf-worker}/cn.toml (100%) rename etc/v1/{launch-with-python-udf-server => launch-with-python-udf-worker}/launch.toml (55%) rename etc/v1/{launch-with-python-udf-server/python-udf-server.toml => launch-with-python-udf-worker/python-udf-worker.toml} (87%) rename optools/images/{pythonserver => pythonworker}/Dockerfile (77%) create mode 100644 pkg/udf/python/gateway_test.go diff --git a/cmd/mo-service/config.go b/cmd/mo-service/config.go index dcba9c7d889bf..3a4b07c5c8ce5 100644 --- a/cmd/mo-service/config.go +++ b/cmd/mo-service/config.go @@ -123,8 +123,8 @@ type Config struct { CN cnservice.Config `toml:"cn"` // ProxyConfig is the config of proxy. ProxyConfig proxy.Config `toml:"proxy"` - // PythonUdfServerConfig is the config of python udf server - PythonUdfServerConfig python.Config `toml:"python-udf-server"` + // PythonUdfWorkerConfig is the config of the Python UDF worker. + PythonUdfWorkerConfig python.Config `toml:"python-udf-worker"` // Observability parameters for the metric/trace Observability config.ObservabilityParameters `toml:"observability"` @@ -601,7 +601,7 @@ func (c *Config) mustGetServiceUUID() string { case metadata.ServiceType_PROXY: return c.ProxyConfig.UUID case metadata.ServiceType_PYTHON_UDF: - return c.PythonUdfServerConfig.UUID + return c.PythonUdfWorkerConfig.UUID } panic("impossible") } diff --git a/cmd/mo-service/main.go b/cmd/mo-service/main.go index 4086df7cb9fa0..15c4fcb0e68ca 100644 --- a/cmd/mo-service/main.go +++ b/cmd/mo-service/main.go @@ -300,7 +300,7 @@ func startService( case metadata.ServiceType_LOG: return startLogService(cfg, stopper, fs, shutdownC) case metadata.ServiceType_PYTHON_UDF: - return startPythonUdfService(cfg, stopper) + return startPythonUdfWorker(cfg, stopper) default: panic("unknown service type") } @@ -738,8 +738,8 @@ func waitProxyFileServiceRetry(ctx context.Context, delay time.Duration) error { } } -// startPythonUdfService starts the python udf service. -func startPythonUdfService(cfg *Config, stopper *stopper.Stopper) error { +// startPythonUdfWorker starts the Python UDF worker. +func startPythonUdfWorker(cfg *Config, stopper *stopper.Stopper) error { if err := waitClusterCondition(cfg.mustGetServiceUUID(), cfg.HAKeeperClient, waitHAKeeperRunning); err != nil { return err } @@ -752,12 +752,12 @@ func startPythonUdfService(cfg *Config, stopper *stopper.Stopper) error { finish(err) }) } - err := stopper.RunNamedTask("python-udf-service", func(ctx context.Context) { + err := stopper.RunNamedTask("python-udf-worker", func(ctx context.Context) { var closeErr error defer func() { finishTask(closeErr) }() roleCtx, cancelRole := serviceLifecycle.roleContext(ctx, serviceRolePython) defer cancelRole() - s, err := python.NewSupervisor(cfg.PythonUdfServerConfig) + s, err := python.NewSupervisor(cfg.PythonUdfWorkerConfig) if err != nil { panic(err) } @@ -767,7 +767,7 @@ func startPythonUdfService(cfg *Config, stopper *stopper.Stopper) error { <-roleCtx.Done() if err := s.Close(); err != nil { closeErr = err - logutil.GetGlobalLogger().Error("failed to close python udf service", zap.Error(err)) + logutil.GetGlobalLogger().Error("failed to close Python UDF worker", zap.Error(err)) } }) if err != nil { diff --git a/etc/launch-with-python-udf-server/cn.toml b/etc/launch-with-python-udf-worker/cn.toml similarity index 100% rename from etc/launch-with-python-udf-server/cn.toml rename to etc/launch-with-python-udf-worker/cn.toml diff --git a/etc/launch-with-python-udf-server/launch.toml b/etc/launch-with-python-udf-worker/launch.toml similarity index 54% rename from etc/launch-with-python-udf-server/launch.toml rename to etc/launch-with-python-udf-worker/launch.toml index 1b75cdaf1d3b1..610a771783707 100644 --- a/etc/launch-with-python-udf-server/launch.toml +++ b/etc/launch-with-python-udf-worker/launch.toml @@ -7,10 +7,9 @@ tnservices = [ ] cnservices = [ - "./etc/launch-with-python-udf-server/cn.toml" + "./etc/launch-with-python-udf-worker/cn.toml" ] python-udf-services = [ - "./etc/launch-with-python-udf-server/python-udf-server.toml" + "./etc/launch-with-python-udf-worker/python-udf-worker.toml" ] - diff --git a/etc/launch-with-python-udf-server/python-udf-server.toml b/etc/launch-with-python-udf-worker/python-udf-worker.toml similarity index 87% rename from etc/launch-with-python-udf-server/python-udf-server.toml rename to etc/launch-with-python-udf-worker/python-udf-worker.toml index 37efe868191bc..40a0d22bb0ade 100644 --- a/etc/launch-with-python-udf-server/python-udf-server.toml +++ b/etc/launch-with-python-udf-worker/python-udf-worker.toml @@ -1,6 +1,6 @@ service-type = "PYTHON_UDF" -[python-udf-server] +[python-udf-worker] uuid = "dd1dccb4-4d3c-41f8-b482-5251dc7a41b2" address = "127.0.0.1:50051" path = "pkg/udf/python/worker" diff --git a/etc/v1/launch-with-python-udf-server/cn.toml b/etc/v1/launch-with-python-udf-worker/cn.toml similarity index 100% rename from etc/v1/launch-with-python-udf-server/cn.toml rename to etc/v1/launch-with-python-udf-worker/cn.toml diff --git a/etc/v1/launch-with-python-udf-server/launch.toml b/etc/v1/launch-with-python-udf-worker/launch.toml similarity index 55% rename from etc/v1/launch-with-python-udf-server/launch.toml rename to etc/v1/launch-with-python-udf-worker/launch.toml index 64d2458f05442..8171c1ead1cd7 100644 --- a/etc/v1/launch-with-python-udf-server/launch.toml +++ b/etc/v1/launch-with-python-udf-worker/launch.toml @@ -7,9 +7,9 @@ tnservices = [ ] cnservices = [ - "./etc/v1/launch-with-python-udf-server/cn.toml" + "./etc/v1/launch-with-python-udf-worker/cn.toml" ] python-udf-services = [ - "./etc/v1/launch-with-python-udf-server/python-udf-server.toml" + "./etc/v1/launch-with-python-udf-worker/python-udf-worker.toml" ] diff --git a/etc/v1/launch-with-python-udf-server/python-udf-server.toml b/etc/v1/launch-with-python-udf-worker/python-udf-worker.toml similarity index 87% rename from etc/v1/launch-with-python-udf-server/python-udf-server.toml rename to etc/v1/launch-with-python-udf-worker/python-udf-worker.toml index 37efe868191bc..40a0d22bb0ade 100644 --- a/etc/v1/launch-with-python-udf-server/python-udf-server.toml +++ b/etc/v1/launch-with-python-udf-worker/python-udf-worker.toml @@ -1,6 +1,6 @@ service-type = "PYTHON_UDF" -[python-udf-server] +[python-udf-worker] uuid = "dd1dccb4-4d3c-41f8-b482-5251dc7a41b2" address = "127.0.0.1:50051" path = "pkg/udf/python/worker" diff --git a/optools/images/pythonserver/Dockerfile b/optools/images/pythonworker/Dockerfile similarity index 77% rename from optools/images/pythonserver/Dockerfile rename to optools/images/pythonworker/Dockerfile index 88022f7fafd1f..d273cfd41d450 100644 --- a/optools/images/pythonserver/Dockerfile +++ b/optools/images/pythonworker/Dockerfile @@ -1,6 +1,6 @@ # run from root path for debug: -# docker build -t mo-python-udf-runtime -f ./optools/images/pythonserver/Dockerfile ./pkg/udf/python/worker/ -# docker run -d --network host mo-python-udf-runtime +# docker build -t mo-python-udf-worker -f ./optools/images/pythonworker/Dockerfile ./pkg/udf/python/worker/ +# docker run -d --network host mo-python-udf-worker FROM python:3.12-bookworm diff --git a/pkg/embed/config.go b/pkg/embed/config.go index c0603e39cc4a6..258722d101dc6 100644 --- a/pkg/embed/config.go +++ b/pkg/embed/config.go @@ -94,8 +94,8 @@ type ServiceConfig struct { CN cnservice.Config `toml:"cn"` // ProxyConfig is the config of proxy. ProxyConfig proxy.Config `toml:"proxy"` - // PythonUdfServerConfig is the config of python udf server - PythonUdfServerConfig python.Config `toml:"python-udf-server"` + // PythonUdfWorkerConfig is the config of the Python UDF worker. + PythonUdfWorkerConfig python.Config `toml:"python-udf-worker"` // Observability parameters for the metric/trace Observability config.ObservabilityParameters `toml:"observability"` diff --git a/pkg/frontend/clone_database_source.go b/pkg/frontend/clone_database_source.go index 944fb95f3e05a..cdbd9cdd6af61 100644 --- a/pkg/frontend/clone_database_source.go +++ b/pkg/frontend/clone_database_source.go @@ -682,8 +682,11 @@ func validateCloneUserDefinedFunctions(functions []userDefinedFunctionDefinition if strings.EqualFold(definition.lang, string(tree.PYTHON)) { var body function.PythonRoutineBody if err := json.Unmarshal([]byte(definition.body), &body); err != nil || - body.Handler == "" || (body.Source == "" && body.ArtifactDigest == "") || - body.ABIContract != udf.PythonABIContract || body.AdapterVersion != udf.PythonAdapterVersion { + body.Handler == "" || body.Source == "" || + (body.Mode != "SCALAR" && body.Mode != "VECTOR") || + (body.NullPolicy != udf.NullCallHandler && body.NullPolicy != udf.NullReturnNull) || + body.ABIContract != udf.PythonABIContract || body.AdapterVersion != udf.PythonAdapterVersion || + body.SDKVersion != udf.PythonSDKVersion { return moerr.NewNotSupportedNoCtxf( "CREATE DATABASE CLONE with incomplete %s function %s is not supported", definition.lang, diff --git a/pkg/sql/plan/function/python_udf.go b/pkg/sql/plan/function/python_udf.go index 3e45aafe8c590..e4cdcd9ee0ab2 100644 --- a/pkg/sql/plan/function/python_udf.go +++ b/pkg/sql/plan/function/python_udf.go @@ -77,40 +77,49 @@ func pythonUdfRetType(parameters []types.Type) types.Type { // the generic builtin STRICT bit. func runPythonUdf(parameters []*vector.Vector, result vector.FunctionResultWrapper, proc *process.Process, length int, selectList *FunctionSelectList) error { if len(parameters) == 0 || parameters[0] == nil { - return fmt.Errorf("python runtime: missing routine descriptor") + return fmt.Errorf("python udf: missing routine descriptor") } if length < 0 { - return fmt.Errorf("python runtime: negative input length %d", length) + return fmt.Errorf("python udf: negative input length %d", length) } routine := &UdfWithContext{} encoded, isNull := vector.GenerateFunctionStrParameter(parameters[0]).GetStrValue(0) if isNull { - return fmt.Errorf("python runtime: routine descriptor is NULL") + return fmt.Errorf("python udf: routine descriptor is NULL") } if err := json.Unmarshal(encoded, routine); err != nil { - return fmt.Errorf("python runtime: decode routine descriptor: %w", err) + return fmt.Errorf("python udf: decode routine descriptor: %w", err) } if routine.Udf == nil { - return fmt.Errorf("python runtime: routine descriptor has no function") + return fmt.Errorf("python udf: routine descriptor has no function") } body := PythonRoutineBody{} if err := json.Unmarshal([]byte(routine.Body), &body); err != nil { - return fmt.Errorf("python runtime: decode Python routine body: %w", err) + return fmt.Errorf("python udf: decode Python routine body: %w", err) } if body.Handler == "" || body.Source == "" { - return fmt.Errorf("python runtime: routine handler and source are required") + return fmt.Errorf("python udf: routine handler and source are required") + } + if body.Mode != "SCALAR" && body.Mode != "VECTOR" { + return fmt.Errorf("python udf: unsupported mode %q", body.Mode) + } + if body.NullPolicy != udf.NullCallHandler && body.NullPolicy != udf.NullReturnNull { + return fmt.Errorf("python udf: unsupported NULL policy %q", body.NullPolicy) } if body.ABIContract != udf.PythonABIContract || body.AdapterVersion != udf.PythonAdapterVersion { - return fmt.Errorf("python runtime: unsupported Python ABI contract %q/%q", body.ABIContract, body.AdapterVersion) + return fmt.Errorf("python udf: unsupported Python ABI contract %q/%q", body.ABIContract, body.AdapterVersion) + } + if body.SDKVersion != udf.PythonSDKVersion { + return fmt.Errorf("python udf: unsupported Python SDK %q", body.SDKVersion) } argTypes, err := routineArgumentTypes(routine) if err != nil { return err } if len(parameters)-1 != len(argTypes) { - return fmt.Errorf("python runtime: routine has %d arguments, received %d", len(argTypes), len(parameters)-1) + return fmt.Errorf("python udf: routine has %d arguments, received %d", len(argTypes), len(parameters)-1) } if length == 0 { @@ -118,11 +127,11 @@ func runPythonUdf(parameters []*vector.Vector, result vector.FunctionResultWrapp } for index, input := range parameters[1:] { if input == nil || (!input.IsConst() && input.Length() < length) { - return fmt.Errorf("python runtime: input vector %d is shorter than invocation length", index) + return fmt.Errorf("python udf: input vector %d is shorter than invocation length", index) } } if selectList != nil && len(selectList.SelectList) < length { - return fmt.Errorf("python runtime: selection list is shorter than invocation length") + return fmt.Errorf("python udf: selection list is shorter than invocation length") } selected := make([]int64, 0, length) @@ -150,13 +159,13 @@ func runPythonUdf(parameters []*vector.Vector, result vector.FunctionResultWrapp compacted = make([]*vector.Vector, len(inputs)) for i, input := range inputs { if input == nil { - return fmt.Errorf("python runtime: input vector %d is nil", i) + return fmt.Errorf("python udf: input vector %d is nil", i) } compacted[i] = vector.NewOffHeapVecWithType(*input.GetType()) compacted[i].SetIsBin(input.GetIsBin()) if err := compacted[i].Union(input, selected, proc.Mp()); err != nil { freeVectors(compacted, proc.Mp()) - return fmt.Errorf("python runtime: compact input %d: %w", i, err) + return fmt.Errorf("python udf: compact input %d: %w", i, err) } } inputs = compacted @@ -173,7 +182,7 @@ func runPythonUdf(parameters []*vector.Vector, result vector.FunctionResultWrapp accountID, err := defines.GetAccountId(proc.Ctx) if err != nil { - return fmt.Errorf("python runtime: resolve account: %w", err) + return fmt.Errorf("python udf: resolve account: %w", err) } tuple, err := invocationTuple(routine.Context, proc.QueryId(), accountID) if err != nil { @@ -241,11 +250,11 @@ func routineArgumentTypes(routine *UdfWithContext) ([]types.Type, error) { args := make([]types.Type, len(routine.Args)) for i, arg := range routine.Args { if arg == nil { - return nil, fmt.Errorf("python runtime: routine argument %d is nil", i) + return nil, fmt.Errorf("python udf: routine argument %d is nil", i) } typ, ok := types.Types[arg.Type] if !ok { - return nil, fmt.Errorf("python runtime: unknown routine argument type %q", arg.Type) + return nil, fmt.Errorf("python udf: unknown routine argument type %q", arg.Type) } args[i] = typ.ToType() } @@ -279,7 +288,7 @@ func invocationTuple(context map[string]string, queryID string, accountID uint32 if statementID == "" { id, err := uuid.NewV7() if err != nil { - return protocol.FencingTuple{}, fmt.Errorf("python runtime: create statement fence: %w", err) + return protocol.FencingTuple{}, fmt.Errorf("python udf: create statement fence: %w", err) } statementID = id.String() } @@ -289,7 +298,7 @@ func invocationTuple(context map[string]string, queryID string, accountID uint32 } invocationID, err := uuid.NewV7() if err != nil { - return protocol.FencingTuple{}, fmt.Errorf("python runtime: create invocation fence: %w", err) + return protocol.FencingTuple{}, fmt.Errorf("python udf: create invocation fence: %w", err) } groupEpoch, err := positiveContextUint(context, "group_epoch", 1) if err != nil { @@ -316,7 +325,7 @@ func positiveContextUint(context map[string]string, key string, fallback uint64) } parsed, err := strconv.ParseUint(value, 10, 64) if err != nil || parsed == 0 { - return 0, fmt.Errorf("python runtime: invalid %s %q", key, value) + return 0, fmt.Errorf("python udf: invalid %s %q", key, value) } return parsed, nil } diff --git a/pkg/udf/protocol/protocol.go b/pkg/udf/protocol/protocol.go index ea7a61941cb00..b2a9403d32b39 100644 --- a/pkg/udf/protocol/protocol.go +++ b/pkg/udf/protocol/protocol.go @@ -237,7 +237,11 @@ func (s *OutputSnapshot) Validate(expectedLength int, expectedDigest string) err if expectedLength >= 0 && len(s.backing) != expectedLength { return fmt.Errorf("%w: output snapshot length %d, expected %d", ErrProtocol, len(s.backing), expectedLength) } - if expectedDigest != "" && !equalFoldHex(expectedDigest, s.Digest()) { + currentDigest := sha256.Sum256(s.backing) + if currentDigest != s.digest { + return fmt.Errorf("%w: output snapshot backing changed", ErrProtocol) + } + if expectedDigest != "" && !equalFoldHex(expectedDigest, hex.EncodeToString(currentDigest[:])) { return fmt.Errorf("%w: output snapshot digest changed", ErrProtocol) } return nil @@ -377,7 +381,9 @@ func (t *OpenToken) Commit() error { release := g.canReleaseLocked() g.mu.Unlock() if release { - _ = t.group.releaseIfReady() + if err := t.group.releaseIfReady(); err != nil { + t.result = errors.Join(t.result, err) + } } return } diff --git a/pkg/udf/protocol/protocol_test.go b/pkg/udf/protocol/protocol_test.go index f535bf43df98f..c1676b4351b1d 100644 --- a/pkg/udf/protocol/protocol_test.go +++ b/pkg/udf/protocol/protocol_test.go @@ -79,6 +79,13 @@ func TestOutputSnapshotFreezesBeforeWorkerCanRewrite(t *testing.T) { require.NoError(t, empty.Validate(0, empty.Digest())) } +func TestOutputSnapshotDetectsTrustedBackingMutation(t *testing.T) { + snapshot, err := FreezeOutput([]byte("trusted"), 1024) + require.NoError(t, err) + snapshot.TrustedBytes()[0] = 'X' + require.ErrorContains(t, snapshot.Validate(snapshot.Len(), snapshot.Digest()), "backing changed") +} + func TestExecutionGroupReleasesOnlyAfterCloseAndAllMembersTerminal(t *testing.T) { var releases atomic.Int32 group, err := NewExecutionGroup("g", 1, 2, func() error { @@ -189,3 +196,27 @@ func TestExecutionGroupReleaseErrorCanBeRetried(t *testing.T) { require.Equal(t, GroupReleased, group.State()) require.Equal(t, int32(2), attempts.Load()) } + +func TestExecutionGroupCommitReportsReleaseFailure(t *testing.T) { + var attempts atomic.Int32 + group, err := NewExecutionGroup("g", 1, 1, func() error { + if attempts.Add(1) == 1 { + return errors.New("temporary release failure") + } + return nil + }) + require.NoError(t, err) + open, err := group.BeginOpen("member") + require.NoError(t, err) + require.NoError(t, group.Close(ReasonCancel)) + + err = open.Commit() + require.ErrorIs(t, err, ErrGroupClosed) + require.ErrorContains(t, err, "temporary release failure") + require.ErrorIs(t, open.Commit(), ErrGroupClosed) + require.ErrorContains(t, open.Commit(), "temporary release failure") + require.Equal(t, GroupDraining, group.State()) + require.NoError(t, group.Close(ReasonCancel)) + require.Equal(t, GroupReleased, group.State()) + require.Equal(t, int32(2), attempts.Load()) +} diff --git a/pkg/udf/python/arrow.go b/pkg/udf/python/arrow.go index 65a66dec35d61..df82f10317a0c 100644 --- a/pkg/udf/python/arrow.go +++ b/pkg/udf/python/arrow.go @@ -70,11 +70,11 @@ func NewTypeDescriptor(typ types.Type) (TypeDescriptor, error) { types.T_binary, types.T_varbinary, types.T_blob, types.T_array_float32, types.T_array_float64: default: - return TypeDescriptor{}, fmt.Errorf("python runtime does not support MatrixOne type %s", typ.String()) + return TypeDescriptor{}, fmt.Errorf("python udf does not support MatrixOne type %s", typ.String()) } if typ.Oid == types.T_array_float32 || typ.Oid == types.T_array_float64 { if typ.Width <= 0 { - return TypeDescriptor{}, fmt.Errorf("python runtime requires a positive vector dimension for %s", typ.String()) + return TypeDescriptor{}, fmt.Errorf("python udf requires a positive vector dimension for %s", typ.String()) } d.OffsetWidth = 0 } @@ -566,11 +566,14 @@ func AppendArrowResult(descriptor TypeDescriptor, input arrow.Array, result vect return nil } if mp == nil { - return fmt.Errorf("python runtime: missing memory pool for Arrow result") + return fmt.Errorf("python udf: missing memory pool for Arrow result") } if err := validateArrayType(descriptor, input); err != nil { return err } + if err := validateArrowValueDomain(descriptor, input); err != nil { + return err + } result.GetResultVector().SetTypeScale(descriptor.Scale) typ := types.T(descriptor.TypeID) for i := 0; i < input.Len(); i++ { @@ -724,6 +727,100 @@ func validateArrayType(descriptor TypeDescriptor, input arrow.Array) error { } return nil } + +func validateArrowValueDomain(descriptor TypeDescriptor, input arrow.Array) error { + if input == nil { + return fmt.Errorf("TYPE_CONTRACT: missing Arrow result") + } + for row := 0; row < input.Len(); row++ { + if input.IsNull(row) { + continue + } + switch types.T(descriptor.TypeID) { + case types.T_char, types.T_varchar, types.T_text: + if descriptor.Width > 0 && int32(len([]byte(input.(*array.String).Value(row)))) > descriptor.Width { + return fmt.Errorf("TYPE_CONTRACT: string row %d exceeds width %d", row, descriptor.Width) + } + case types.T_binary, types.T_varbinary, types.T_blob: + if descriptor.Width > 0 && int32(len(input.(*array.Binary).Value(row))) > descriptor.Width { + return fmt.Errorf("TYPE_CONTRACT: binary row %d exceeds width %d", row, descriptor.Width) + } + case types.T_time: + value := types.Time(input.(*array.Duration).Value(row)) + if !types.IsMySQLTime(value) || !hasExactMicrosecondScale(int64(value), descriptor.Scale) { + return fmt.Errorf("TYPE_CONTRACT: TIME row %d is outside the declared SQL domain", row) + } + case types.T_date, types.T_datetime, types.T_timestamp: + structValue := input.(*array.Struct) + zeroField, valueField := structValue.Field(0), structValue.Field(1) + if zeroField.IsNull(row) || valueField.IsNull(row) { + return fmt.Errorf("TYPE_CONTRACT: temporal row %d has a null child", row) + } + zero := zeroField.(*array.Boolean).Value(row) + switch types.T(descriptor.TypeID) { + case types.T_date: + value := valueField.(*array.Date32).Value(row) + if zero && value != 0 { + return fmt.Errorf("TYPE_CONTRACT: DATE zero row %d has a non-zero placeholder", row) + } + if !zero { + date := types.DaysFromUnixEpochToDate(int32(value)) + year, month, day, _ := date.Calendar(true) + if !types.ValidDate(year, month, day) { + return fmt.Errorf("TYPE_CONTRACT: DATE row %d is outside the SQL domain", row) + } + } + case types.T_datetime: + value := valueField.(*array.Timestamp).Value(row) + if zero && value != 0 { + return fmt.Errorf("TYPE_CONTRACT: DATETIME zero row %d has a non-zero placeholder", row) + } + if !zero { + absolute, ok := addUnixEpoch(int64(value)) + if !ok || !hasExactMicrosecondScale(absolute, descriptor.Scale) { + return fmt.Errorf("TYPE_CONTRACT: DATETIME row %d is outside the declared SQL domain", row) + } + datetime := types.Datetime(absolute) + year, month, day, _ := datetime.ToDate().Calendar(true) + if !types.ValidDatetime(year, month, day) { + return fmt.Errorf("TYPE_CONTRACT: DATETIME row %d is outside the SQL domain", row) + } + } + case types.T_timestamp: + value := valueField.(*array.Timestamp).Value(row) + if zero && value != 0 { + return fmt.Errorf("TYPE_CONTRACT: TIMESTAMP zero row %d has a non-zero placeholder", row) + } + if !zero { + absolute, ok := addUnixEpoch(int64(value)) + if !ok || absolute < int64(types.TimestampMinValue) || absolute > int64(types.TimestampMaxValue) || !hasExactMicrosecondScale(absolute, descriptor.Scale) { + return fmt.Errorf("TYPE_CONTRACT: TIMESTAMP row %d is outside the declared SQL domain", row) + } + } + } + } + } + return nil +} + +func addUnixEpoch(value int64) (int64, bool) { + epoch := types.GetUnixEpochSecs() + if value > math.MaxInt64-epoch || value < math.MinInt64+epoch { + return 0, false + } + return value + epoch, true +} + +func hasExactMicrosecondScale(value int64, scale int32) bool { + if scale < 0 || scale > 6 { + return false + } + quantum := int64(1) + for i := scale; i < 6; i++ { + quantum *= 10 + } + return value%quantum == 0 +} func decimalFromArray(value decimal128.Num, narrow bool) (any, error) { high, low := uint64(value.HighBits()), value.LowBits() if narrow && high != 0 && high != math.MaxUint64 { diff --git a/pkg/udf/python/arrow_test.go b/pkg/udf/python/arrow_test.go index f4ed3027bd689..3898dce4413f8 100644 --- a/pkg/udf/python/arrow_test.go +++ b/pkg/udf/python/arrow_test.go @@ -73,3 +73,20 @@ func TestZeroArgumentRecordPreservesRows(t *testing.T) { require.Equal(t, int64(3), decoded.NumRows()) require.Equal(t, int64(0), decoded.NumCols()) } + +func TestArrowValueDomainRejectsWidthAndTimeOverflow(t *testing.T) { + allocator := memory.NewGoAllocator() + stringBuilder := array.NewStringBuilder(allocator) + stringBuilder.Append("abcd") + strings := stringBuilder.NewStringArray() + defer strings.Release() + stringDescriptor := TypeDescriptor{TypeID: int32(types.T_varchar), Width: 3, OffsetWidth: 32} + require.ErrorContains(t, validateArrowValueDomain(stringDescriptor, strings), "exceeds width") + + timeBuilder := array.NewDurationBuilder(allocator, &arrow.DurationType{Unit: arrow.Microsecond}) + timeBuilder.Append(arrow.Duration(types.MySQLTimeMax + 1)) + times := timeBuilder.NewDurationArray() + defer times.Release() + timeDescriptor := TypeDescriptor{TypeID: int32(types.T_time), Scale: 6, OffsetWidth: 32} + require.ErrorContains(t, validateArrowValueDomain(timeDescriptor, times), "outside the declared SQL domain") +} diff --git a/pkg/udf/python/config.go b/pkg/udf/python/config.go index 4c4d191639fe0..937736bb8a958 100644 --- a/pkg/udf/python/config.go +++ b/pkg/udf/python/config.go @@ -24,10 +24,10 @@ type Config struct { func (c *Config) Validate() error { if c.Address == "" { - return moerr.NewInternalError(context.Background(), "missing python runtime address") + return moerr.NewInternalError(context.Background(), "missing python udf address") } if c.Path == "" { - return moerr.NewInternalError(context.Background(), "missing python runtime path") + return moerr.NewInternalError(context.Background(), "missing python udf path") } return nil } @@ -41,16 +41,16 @@ type ClientConfig struct { func (c *ClientConfig) Validate() error { if c.ServerAddress == "" { - return moerr.NewInternalError(context.Background(), "missing python runtime address") + return moerr.NewInternalError(context.Background(), "missing python udf address") } if c.MaxBatchBytes < 0 || c.MaxBatchBytes > 1<<30 { - return moerr.NewInternalError(context.Background(), "invalid python runtime max batch bytes") + return moerr.NewInternalError(context.Background(), "invalid python udf max batch bytes") } if c.MaxBatchRows < 0 || c.MaxBatchRows > 1<<30 { - return moerr.NewInternalError(context.Background(), "invalid python runtime max batch rows") + return moerr.NewInternalError(context.Background(), "invalid python udf max batch rows") } if c.RequestTimeout < 0 { - return moerr.NewInternalError(context.Background(), "invalid python runtime request timeout") + return moerr.NewInternalError(context.Background(), "invalid python udf request timeout") } return nil } diff --git a/pkg/udf/python/gateway.go b/pkg/udf/python/gateway.go index 00442d64eafed..25860017e7ba3 100644 --- a/pkg/udf/python/gateway.go +++ b/pkg/udf/python/gateway.go @@ -121,7 +121,7 @@ func (g *Gateway) Execute(ctx context.Context, invocation *udf.Invocation, resul streamCtx, cancel := context.WithTimeout(ctx, g.cfg.RequestTimeout) defer cancel() if invocation == nil || result == nil || mp == nil { - return fmt.Errorf("python runtime: nil invocation, result, or memory pool") + return fmt.Errorf("python udf: nil invocation, result, or memory pool") } if err := validateInvocation(invocation); err != nil { return err @@ -147,7 +147,7 @@ func (g *Gateway) Execute(ctx context.Context, invocation *udf.Invocation, resul } openBody, err := json.Marshal(openPayload{Handler: invocation.Handler, Source: invocation.Source, Mode: invocation.Mode, NullPolicy: invocation.NullPolicy, ABIContract: invocation.ABIContract, AdapterVersion: invocation.AdapterVersion, SDKVersion: invocation.SDKVersion, Context: cloneMap(invocation.Context), Args: args, Return: returnDescriptor, MaxBatchBytes: g.cfg.MaxBatchBytes, MaxBatchRows: g.cfg.MaxBatchRows}) if err != nil { - return fmt.Errorf("python runtime: encode open payload: %w", err) + return fmt.Errorf("python udf: encode open payload: %w", err) } open, err := protocol.MarshalControl(protocol.Control{Kind: "OpenInvocation", Tuple: invocation.Tuple, Payload: openBody}) if err != nil { @@ -156,7 +156,7 @@ func (g *Gateway) Execute(ctx context.Context, invocation *udf.Invocation, resul stream, err := g.flight.DoExchange(streamCtx) if err != nil { - return fmt.Errorf("python runtime: open Arrow Flight exchange: %w", err) + return fmt.Errorf("python udf: open Arrow Flight exchange: %w", err) } // Flight carries the invocation command in the descriptor. The descriptor // is delivered to the server separately from the Arrow IPC reader; putting @@ -183,7 +183,7 @@ func (g *Gateway) Execute(ctx context.Context, invocation *udf.Invocation, resul return err } if len(batch.Frames) != 2 { - return fmt.Errorf("python runtime: input batch contains %d Arrow frames", len(batch.Frames)) + return fmt.Errorf("python udf: input batch contains %d Arrow frames", len(batch.Frames)) } if batchIndex == 0 { inputSchema = append([]byte(nil), batch.Frames[0].Header...) @@ -191,7 +191,7 @@ func (g *Gateway) Execute(ctx context.Context, invocation *udf.Invocation, resul return err } } else if !bytes.Equal(inputSchema, batch.Frames[0].Header) { - return fmt.Errorf("python runtime: input batches do not share one Arrow schema") + return fmt.Errorf("python udf: input batches do not share one Arrow schema") } sequenceNumber := batchIndex + 1 if err := sequence.AcceptInput(sequenceNumber); err != nil { @@ -237,7 +237,7 @@ func (g *Gateway) Execute(ctx context.Context, invocation *udf.Invocation, resul } } if !inputEnded || lastSequence != batchIndex { - return fmt.Errorf("python runtime: input stream did not reach EndInput") + return fmt.Errorf("python udf: input stream did not reach EndInput") } return g.receiveFinish(streamCtx, stream, invocation.Tuple, invocation.Length, rows, &sequence) } @@ -320,25 +320,25 @@ func (g *Gateway) receiveResultBatch( data, err := stream.Recv() if err != nil { if err == io.EOF { - return 0, fmt.Errorf("python runtime: exchange ended before result sequence %d", sequenceNumber) + return 0, fmt.Errorf("python udf: exchange ended before result sequence %d", sequenceNumber) } - return 0, fmt.Errorf("python runtime: receive result: %w", err) + return 0, fmt.Errorf("python udf: receive result: %w", err) } if data == nil || (len(data.AppMetadata) == 0 && len(data.DataHeader) == 0) { - return 0, fmt.Errorf("python runtime: empty Flight result") + return 0, fmt.Errorf("python udf: empty Flight result") } if len(data.DataHeader) > 0 { if *schemaFrame == nil { if len(data.DataBody) != 0 { - return 0, fmt.Errorf("python runtime: result schema contains a body") + return 0, fmt.Errorf("python udf: result schema contains a body") } - *schemaFrame = &ArrowFrame{Header: append([]byte(nil), data.DataHeader...)} if len(data.AppMetadata) > 0 { control, err := protocol.UnmarshalControl(data.AppMetadata) - if err != nil || control.Kind != "ResultSchema" { - return 0, fmt.Errorf("python runtime: invalid result schema metadata") + if err != nil || control.Tuple != tuple || control.Kind != "ResultSchema" { + return 0, fmt.Errorf("python udf: invalid result schema metadata") } } + *schemaFrame = &ArrowFrame{Header: append([]byte(nil), data.DataHeader...)} continue } control, err := protocol.UnmarshalControl(data.AppMetadata) @@ -346,7 +346,7 @@ func (g *Gateway) receiveResultBatch( return 0, err } if control.Tuple != tuple || control.Kind != "ResultBatch" || control.Sequence != sequenceNumber { - return 0, fmt.Errorf("python runtime: unexpected result sequence %d", control.Sequence) + return 0, fmt.Errorf("python udf: unexpected result sequence %d", control.Sequence) } if err := sequence.AcceptResult(control.Sequence); err != nil { return 0, err @@ -360,15 +360,15 @@ func (g *Gateway) receiveResultBatch( } decoded, err := DecodeRecordBatch(**schemaFrame, ArrowFrame{Header: append([]byte(nil), data.DataHeader...), Body: snapshot.TrustedBytes()}, g.cfg.MaxBatchBytes) if err != nil { - return 0, fmt.Errorf("python runtime: decode result: %w", err) + return 0, fmt.Errorf("python udf: decode result: %w", err) } if decoded.NumCols() != 1 || decoded.NumRows() <= 0 || decoded.NumRows() != expectedRows { decoded.Release() - return 0, fmt.Errorf("python runtime: result batch row count %d, expected %d", decoded.NumRows(), expectedRows) + return 0, fmt.Errorf("python udf: result batch row count %d, expected %d", decoded.NumRows(), expectedRows) } if decoded.Schema().Field(0).Name != "result" { decoded.Release() - return 0, fmt.Errorf("python runtime: result field has unexpected name %q", decoded.Schema().Field(0).Name) + return 0, fmt.Errorf("python udf: result field has unexpected name %q", decoded.Schema().Field(0).Name) } if err := returnDescriptor.ValidateField(decoded.Schema().Field(0)); err != nil { decoded.Release() @@ -392,22 +392,22 @@ func (g *Gateway) receiveResultBatch( return 0, err } if control.Tuple != tuple { - return 0, fmt.Errorf("python runtime: result tuple changed") + return 0, fmt.Errorf("python udf: result tuple changed") } switch control.Kind { case "ResultSchema": continue case "InputConsumed": if control.Sequence != sequenceNumber { - return 0, fmt.Errorf("python runtime: input sequence %d was consumed while waiting for result %d", control.Sequence, sequenceNumber) + return 0, fmt.Errorf("python udf: input sequence %d was consumed while waiting for result %d", control.Sequence, sequenceNumber) } continue case "Error": - return 0, fmt.Errorf("python runtime: worker error: %s", control.Reason) + return 0, fmt.Errorf("python udf: worker error: %s", control.Reason) case "Finish": - return 0, fmt.Errorf("python runtime: Finish arrived before result sequence %d", sequenceNumber) + return 0, fmt.Errorf("python udf: Finish arrived before result sequence %d", sequenceNumber) default: - return 0, fmt.Errorf("python runtime: unexpected control %q", control.Kind) + return 0, fmt.Errorf("python udf: unexpected control %q", control.Kind) } } } @@ -423,71 +423,59 @@ func (g *Gateway) receiveFinish( data, err := stream.Recv() if err != nil { if err == io.EOF { - return fmt.Errorf("python runtime: exchange ended before Finish") + return fmt.Errorf("python udf: exchange ended before Finish") } - return fmt.Errorf("python runtime: receive Finish: %w", err) + return fmt.Errorf("python udf: receive Finish: %w", err) } if data == nil || len(data.DataHeader) > 0 { - return fmt.Errorf("python runtime: unexpected Arrow data while finishing") + return fmt.Errorf("python udf: unexpected Arrow data while finishing") } control, err := protocol.UnmarshalControl(data.AppMetadata) if err != nil { return err } if control.Tuple != tuple { - return fmt.Errorf("python runtime: Finish tuple changed") + return fmt.Errorf("python udf: Finish tuple changed") } switch control.Kind { case "InputConsumed", "ResultSchema": continue case "Error": - return fmt.Errorf("python runtime: worker error: %s", control.Reason) + return fmt.Errorf("python udf: worker error: %s", control.Reason) case "Finish": lastInput, lastResult, acked, inputEnded := sequence.State() if control.Status != statusOK || control.FinishID == "" || rows != expectedRows || !inputEnded || control.LastSequence != lastInput || lastResult != lastInput || acked != lastInput || !sequence.ReadyToFinish() { - return fmt.Errorf("python runtime: invalid Finish status=%q rows=%d", control.Status, rows) + return fmt.Errorf("python udf: invalid Finish status=%q rows=%d", control.Status, rows) } return g.action(ctx, "AcknowledgeFinish", protocol.Control{Kind: "AcknowledgeFinish", Tuple: tuple, FinishID: control.FinishID}) default: - return fmt.Errorf("python runtime: unexpected control %q", control.Kind) + return fmt.Errorf("python udf: unexpected control %q", control.Kind) } } } func validateInvocation(invocation *udf.Invocation) error { if invocation.Language != "" && invocation.Language != udf.LanguagePython { - return fmt.Errorf("python runtime: unsupported language %q", invocation.Language) + return fmt.Errorf("python udf: unsupported language %q", invocation.Language) } if invocation.Handler == "" || invocation.Source == "" { - return fmt.Errorf("python runtime: handler and source are required") + return fmt.Errorf("python udf: handler and source are required") } if invocation.Length < 0 || len(invocation.Args) != len(invocation.Inputs) { - return fmt.Errorf("python runtime: invalid input shape") - } - if invocation.Mode == "" { - invocation.Mode = ModeScalar + return fmt.Errorf("python udf: invalid input shape") } if invocation.Mode != ModeScalar && invocation.Mode != ModeVector { - return fmt.Errorf("python runtime: unsupported mode %q", invocation.Mode) - } - if invocation.NullPolicy == "" { - invocation.NullPolicy = NullCallHandler + return fmt.Errorf("python udf: unsupported mode %q", invocation.Mode) } if invocation.NullPolicy != NullCallHandler && invocation.NullPolicy != NullReturnNull { - return fmt.Errorf("python runtime: unsupported NULL policy %q", invocation.NullPolicy) - } - if invocation.ABIContract == "" { - invocation.ABIContract = udf.PythonABIContract - } - if invocation.AdapterVersion == "" { - invocation.AdapterVersion = udf.PythonAdapterVersion - } - if invocation.SDKVersion == "" { - invocation.SDKVersion = udf.PythonSDKVersion + return fmt.Errorf("python udf: unsupported NULL policy %q", invocation.NullPolicy) } if invocation.ABIContract != udf.PythonABIContract || invocation.AdapterVersion != udf.PythonAdapterVersion { - return fmt.Errorf("python runtime: unsupported Python ABI contract %q/%q", invocation.ABIContract, invocation.AdapterVersion) + return fmt.Errorf("python udf: unsupported Python ABI contract %q/%q", invocation.ABIContract, invocation.AdapterVersion) + } + if invocation.SDKVersion != udf.PythonSDKVersion { + return fmt.Errorf("python udf: unsupported Python SDK %q", invocation.SDKVersion) } return invocation.Tuple.Validate() } @@ -501,25 +489,28 @@ func (g *Gateway) action(ctx context.Context, kind string, control protocol.Cont defer cancel() stream, err := g.flight.DoAction(actionCtx, &flight.Action{Type: kind, Body: body}) if err != nil { - return fmt.Errorf("python runtime: %s: %w", kind, err) + return fmt.Errorf("python udf: %s: %w", kind, err) } result, err := stream.Recv() if err != nil { return err } if result == nil { - return fmt.Errorf("python runtime: empty %s response", kind) + return fmt.Errorf("python udf: empty %s response", kind) } ack, err := protocol.UnmarshalControl(result.Body) if err != nil { return err } + if ack.Tuple != control.Tuple || ack.Kind != "Ack" { + return fmt.Errorf("python udf: %s returned an invalid ACK", kind) + } if ack.Status != statusOK { - return fmt.Errorf("python runtime: %s rejected: %s", kind, ack.Reason) + return fmt.Errorf("python udf: %s rejected: %s", kind, ack.Reason) } if _, err := stream.Recv(); err != io.EOF { if err == nil { - return fmt.Errorf("python runtime: %s returned multiple results", kind) + return fmt.Errorf("python udf: %s returned multiple results", kind) } return err } diff --git a/pkg/udf/python/gateway_test.go b/pkg/udf/python/gateway_test.go new file mode 100644 index 0000000000000..341b84dc750fc --- /dev/null +++ b/pkg/udf/python/gateway_test.go @@ -0,0 +1,60 @@ +// Copyright 2026 Matrix Origin +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. + +package python + +import ( + "testing" + + "github.com/matrixorigin/matrixone/pkg/container/types" + "github.com/matrixorigin/matrixone/pkg/container/vector" + "github.com/matrixorigin/matrixone/pkg/udf" + "github.com/matrixorigin/matrixone/pkg/udf/protocol" + "github.com/stretchr/testify/require" +) + +func validInvocation() *udf.Invocation { + return &udf.Invocation{ + Language: udf.LanguagePython, + Handler: "add", + Source: "def add(ctx, value): return value", + Args: []types.Type{types.T_int64.ToType()}, + ReturnType: types.T_int64.ToType(), + Length: 0, + Inputs: []*vector.Vector{nil}, + Mode: ModeScalar, + NullPolicy: NullCallHandler, + ABIContract: udf.PythonABIContract, + AdapterVersion: udf.PythonAdapterVersion, + SDKVersion: udf.PythonSDKVersion, + Tuple: protocol.FencingTuple{ + AccountID: 1, StatementID: "statement", GroupID: "group", + GroupEpoch: 1, InvocationID: "invocation", LeaseEpoch: 1, + }, + } +} + +func TestValidateInvocationRequiresFrozenContract(t *testing.T) { + base := validInvocation() + require.NoError(t, validateInvocation(base)) + + cases := []struct { + name string + mutate func(*udf.Invocation) + }{ + {name: "mode", mutate: func(invocation *udf.Invocation) { invocation.Mode = "" }}, + {name: "null policy", mutate: func(invocation *udf.Invocation) { invocation.NullPolicy = "" }}, + {name: "ABI", mutate: func(invocation *udf.Invocation) { invocation.ABIContract = "" }}, + {name: "adapter", mutate: func(invocation *udf.Invocation) { invocation.AdapterVersion = "" }}, + {name: "SDK", mutate: func(invocation *udf.Invocation) { invocation.SDKVersion = "" }}, + } + for _, test := range cases { + t.Run(test.name, func(t *testing.T) { + invocation := validInvocation() + test.mutate(invocation) + require.Error(t, validateInvocation(invocation)) + }) + } +} diff --git a/pkg/udf/python/supervisor.go b/pkg/udf/python/supervisor.go index 901aa8bdc0526..14674b2b376c2 100644 --- a/pkg/udf/python/supervisor.go +++ b/pkg/udf/python/supervisor.go @@ -13,7 +13,6 @@ import ( "os" "os/exec" "path/filepath" - "strconv" "sync" "sync/atomic" @@ -62,7 +61,7 @@ func (s *Supervisor) Start() error { if err != nil { return err } - logPath := filepath.Join(filepath.Dir(exePath), "python-runtime-"+strconv.Itoa(int(supervisorNumber.Add(1))+0)+".log") + logPath := filepath.Join(filepath.Dir(exePath), fmt.Sprintf("python-udf-worker-%d.log", supervisorNumber.Add(1))) logFile, err := os.OpenFile(logPath, os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0644) if err != nil { return err @@ -75,7 +74,7 @@ func (s *Supervisor) Start() error { } s.cmd, s.log = cmd, logFile go s.wait(cmd, logFile) - logutil.Infof("started Python runtime worker: %s", cmd.String()) + logutil.Infof("started Python UDF worker: %s", cmd.String()) return nil } @@ -88,7 +87,7 @@ func (s *Supervisor) wait(cmd *exec.Cmd, log io.WriteCloser) { } s.mu.Unlock() if err != nil { - logutil.Errorf("Python runtime worker exited: %v", err) + logutil.Errorf("Python UDF worker exited: %v", err) } } @@ -104,7 +103,7 @@ func (s *Supervisor) Close() error { return nil } if err := cmd.Process.Kill(); err != nil && !errors.Is(err, os.ErrProcessDone) { - return fmt.Errorf("stop Python runtime worker: %w", err) + return fmt.Errorf("stop Python UDF worker: %w", err) } if log != nil { return log.Close() diff --git a/pkg/udf/python/worker/test_worker.py b/pkg/udf/python/worker/test_worker.py index af802a9ae7fff..752a1469e6f5a 100644 --- a/pkg/udf/python/worker/test_worker.py +++ b/pkg/udf/python/worker/test_worker.py @@ -2,6 +2,7 @@ import datetime import importlib.util +import json import pathlib import sys import unittest @@ -50,6 +51,14 @@ def test_zero_temporal_is_distinct_from_null(self): self.assertEqual(True, array.field("is_zero")[0].as_py()) self.assertEqual(datetime.date(1970, 1, 1), array.field("value")[0].as_py()) + def test_scalar_value_domain_is_checked_before_arrow_encoding(self): + string_descriptor = {"type_id": worker.VARCHAR, "width": 3, "offset_width": 32} + with self.assertRaisesRegex(ValueError, "string exceeds"): + worker._output_array(["abcd"], string_descriptor, 1) + time_descriptor = {"type_id": worker.TIME, "scale": 6, "offset_width": 32} + with self.assertRaisesRegex(ValueError, "TIME is outside"): + worker._output_array([datetime.timedelta(hours=839)], time_descriptor, 1) + def test_zero_argument_vector_uses_context_rows(self): descriptor = {"type_id": worker.INT64, "offset_width": 32} output = pa.array([7, 7, 7], type=pa.int64()) @@ -64,7 +73,7 @@ def test_statement_context_is_whitelisted_and_frozen(self): "statement_timestamp_utc": "1704067200123456", "session_timezone_kind": "FIXED_OFFSET", "session_timezone_offset_minutes": "+510", - "sql_mode": '["ANSI", "STRICT"]', + "sql_mode": '["ANSI","STRICT"]', "current_database": "app", "current_user": "alice", "current_role": "writer", @@ -127,6 +136,20 @@ def test_active_fence_cannot_be_admitted_twice(self): {"account_id": 1, "statement_id": "statement", "group_id": "group", "group_epoch": 1, "invocation_id": "invocation", "lease_epoch": 1}, ) + def test_control_numeric_fields_are_strict(self): + tuple_value = { + "account_id": 1, + "statement_id": "statement", + "group_id": "group", + "group_epoch": 1, + "invocation_id": "invocation", + "lease_epoch": 1, + } + with self.assertRaisesRegex(ValueError, "unsupported control"): + worker._decode_control(json.dumps({"version": True, "kind": "InputBatch", "tuple": tuple_value}).encode()) + with self.assertRaisesRegex(ValueError, "invalid control field sequence"): + worker._required_uint64({"sequence": "1"}, "sequence") + if __name__ == "__main__": unittest.main() diff --git a/pkg/udf/python/worker/worker.py b/pkg/udf/python/worker/worker.py index 3a24237de18e3..3257a02d95240 100644 --- a/pkg/udf/python/worker/worker.py +++ b/pkg/udf/python/worker/worker.py @@ -36,7 +36,10 @@ MAX_TERMINAL_BYTES = 16 << 20 TERMINAL_TTL_SECONDS = 300.0 ACK_TIMEOUT_SECONDS = 60.0 -DEFAULT_MAX_BATCH_ROWS = 65536 +_MICROS_PER_SECOND = 1_000_000 +_MAX_TIME_MICROS = (838 * 60 * 60 + 59 * 60 + 59) * _MICROS_PER_SECOND +_MIN_TIMESTAMP = _datetime.datetime(1970, 1, 1, 0, 0, 1, tzinfo=_datetime.timezone.utc) +_MAX_TIMESTAMP = _datetime.datetime(9999, 12, 31, 23, 59, 59, 999999, tzinfo=_datetime.timezone.utc) BOOL = 10 INT8, INT16, INT32, INT64 = 20, 21, 22, 23 @@ -57,7 +60,7 @@ ADAPTER_VERSION = "2026-09" SDK_VERSION = "1.0" -log = logging.getLogger("matrixone.python.runtime") +log = logging.getLogger("matrixone.python.udf.worker") @dataclass(frozen=True, slots=True) @@ -151,6 +154,15 @@ def _required_context_value(context: Dict[str, Any], key: str) -> str: return value +def _optional_context_value(context: Dict[str, Any], key: str) -> Optional[str]: + value = context.get(key) + if value is None: + return None + if not isinstance(value, str): + raise ValueError(f"PROTOCOL: statement context field {key} must be text") + return value or None + + def _statement_context(raw: Any) -> Optional[StatementContext]: if raw is None: return None @@ -175,7 +187,7 @@ def _statement_context(raw: Any) -> Optional[StatementContext]: if timezone_kind == "IANA": timezone_name = _required_context_value(raw, "session_timezone_name") tzdb_version = _required_context_value(raw, "session_timezone_tzdb_version") - if raw.get("session_timezone_offset_minutes"): + if "session_timezone_offset_minutes" in raw: raise ValueError("PROTOCOL: IANA timezone cannot carry a fixed offset") timezone = TimezoneContext("IANA", name=timezone_name, tzdb_version=tzdb_version) elif timezone_kind == "FIXED_OFFSET": @@ -186,7 +198,7 @@ def _statement_context(raw: Any) -> Optional[StatementContext]: raise ValueError("PROTOCOL: invalid fixed timezone offset") from exc if offset_minutes < -839 or offset_minutes > 840: raise ValueError("PROTOCOL: fixed timezone offset is outside SQL range") - if raw.get("session_timezone_name") or raw.get("session_timezone_tzdb_version"): + if "session_timezone_name" in raw or "session_timezone_tzdb_version" in raw: raise ValueError("PROTOCOL: fixed timezone cannot carry IANA identity") timezone = TimezoneContext("FIXED_OFFSET", offset_minutes=offset_minutes) else: @@ -200,7 +212,7 @@ def _statement_context(raw: Any) -> Optional[StatementContext]: if not isinstance(sql_mode_value, list) or any(not isinstance(item, str) for item in sql_mode_value): raise ValueError("PROTOCOL: sql_mode must be an array of strings") sql_mode = tuple(sorted(set(sql_mode_value))) - if list(sql_mode) != sql_mode_value: + if list(sql_mode) != sql_mode_value or json.dumps(sql_mode_value, separators=(",", ":"), ensure_ascii=True) != sql_mode_text: raise ValueError("PROTOCOL: sql_mode is not canonical") current_user = _required_context_value(raw, "current_user") @@ -209,9 +221,9 @@ def _statement_context(raw: Any) -> Optional[StatementContext]: statement_timestamp_utc=timestamp, session_timezone=timezone, sql_mode=sql_mode, - current_database=raw.get("current_database") or None, + current_database=_optional_context_value(raw, "current_database"), current_user=current_user, - current_role=raw.get("current_role") or None, + current_role=_optional_context_value(raw, "current_role"), connection_collation=connection_collation, ) @@ -243,7 +255,13 @@ def _decode_control(data: bytes) -> Dict[str, Any]: if not data or len(data) > MAX_CONTROL_BYTES: raise ValueError("PROTOCOL: invalid control size") value = json.loads(bytes(data).decode("utf-8")) - if not isinstance(value, dict) or value.get("version") != PROTOCOL_VERSION or not isinstance(value.get("kind"), str) or not value["kind"]: + if ( + not isinstance(value, dict) + or type(value.get("version")) is not int + or value["version"] != PROTOCOL_VERSION + or not isinstance(value.get("kind"), str) + or not value["kind"] + ): raise ValueError("PROTOCOL: unsupported control") _tuple_key(value.get("tuple") or {}) return value @@ -254,6 +272,28 @@ def _require_tuple(value: Dict[str, Any], expected: Dict[str, Any]) -> None: raise ValueError("PROTOCOL: fencing tuple changed") +def _required_string(value: Dict[str, Any], key: str) -> str: + item = value.get(key) + if not isinstance(item, str) or not item: + raise ValueError(f"PROTOCOL: missing invocation field {key}") + return item + + +def _required_positive_int(value: Dict[str, Any], key: str, maximum: int) -> int: + item = value.get(key) + if isinstance(item, bool) or not isinstance(item, int) or item <= 0 or item > maximum: + raise ValueError(f"PROTOCOL: invalid invocation field {key}") + return item + + +def _required_uint64(value: Dict[str, Any], key: str, allow_zero: bool = False) -> int: + item = value.get(key) + minimum = 0 if allow_zero else 1 + if isinstance(item, bool) or not isinstance(item, int) or item < minimum or item > (1 << 64) - 1: + raise ValueError(f"PROTOCOL: invalid control field {key}") + return item + + def _encode_control(value: Dict[str, Any]) -> bytes: value = dict(value) value.setdefault("version", PROTOCOL_VERSION) @@ -428,6 +468,19 @@ def _check_json(value: str) -> str: return value +def _check_microsecond_scale(value: int, descriptor: Dict[str, Any]) -> None: + scale = int(descriptor.get("scale") or 0) + if scale < 0 or scale > 6: + raise ValueError("TYPE_CONTRACT: temporal scale is outside the supported range") + quantum = 10 ** (6 - scale) + if value % quantum != 0: + raise ValueError("TYPE_CONTRACT: temporal value exceeds the declared scale") + + +def _timedelta_micros(value: _datetime.timedelta) -> int: + return (value.days * 24 * 60 * 60 + value.seconds) * _MICROS_PER_SECOND + value.microseconds + + def _check_scalar(value: Any, descriptor: Dict[str, Any]): if value is None: return None @@ -435,21 +488,37 @@ def _check_scalar(value: Any, descriptor: Dict[str, Any]): if type_id == BOOL and type(value) is not bool: raise ValueError("TYPE_CONTRACT: expected bool") if type_id in (INT8, INT16, INT32, INT64, UINT8, UINT16, UINT32, UINT64) and (type(value) is not int): raise ValueError("TYPE_CONTRACT: expected integer") if type_id in (FLOAT32, FLOAT64) and type(value) is not float: raise ValueError("TYPE_CONTRACT: expected float") - if type_id in (CHAR, VARCHAR, TEXT) and type(value) is not str: raise ValueError("TYPE_CONTRACT: expected string") + if type_id in (CHAR, VARCHAR, TEXT): + if type(value) is not str: raise ValueError("TYPE_CONTRACT: expected string") + width = int(descriptor.get("width") or 0) + if width > 0 and len(value.encode("utf-8")) > width: raise ValueError("TYPE_CONTRACT: string exceeds the declared width") + return value if type_id == JSON: return _check_json(value) - if type_id in (BINARY, VARBINARY, BLOB) and type(value) is not bytes: raise ValueError("TYPE_CONTRACT: expected bytes") + if type_id in (BINARY, VARBINARY, BLOB): + if type(value) is not bytes: raise ValueError("TYPE_CONTRACT: expected bytes") + width = int(descriptor.get("width") or 0) + if width > 0 and len(value) > width: raise ValueError("TYPE_CONTRACT: binary value exceeds the declared width") + return value if type_id == UUID: if not isinstance(value, _uuid.UUID): raise ValueError("TYPE_CONTRACT: expected uuid.UUID") return value.bytes - if type_id == TIME and not isinstance(value, _datetime.timedelta): raise ValueError("TYPE_CONTRACT: expected datetime.timedelta") + if type_id == TIME: + if not isinstance(value, _datetime.timedelta): raise ValueError("TYPE_CONTRACT: expected datetime.timedelta") + micros = _timedelta_micros(value) + if abs(micros) > _MAX_TIME_MICROS: raise ValueError("TYPE_CONTRACT: TIME is outside the SQL range") + _check_microsecond_scale(micros, descriptor) + return value if type_id == DATE: if not isinstance(value, SqlDate) or (value.is_zero and value.value is not None) or (not value.is_zero and type(value.value) is not _datetime.date): raise ValueError("TYPE_CONTRACT: invalid SqlDate") return value if type_id == DATETIME: if not isinstance(value, SqlDatetime) or (value.is_zero and value.value is not None) or (not value.is_zero and (not isinstance(value.value, _datetime.datetime) or value.value.tzinfo is not None)): raise ValueError("TYPE_CONTRACT: invalid SqlDatetime") + if not value.is_zero: _check_microsecond_scale(value.value.microsecond, descriptor) return value if type_id == TIMESTAMP: if not isinstance(value, SqlTimestamp) or (value.is_zero and value.value is not None) or (not value.is_zero and (not isinstance(value.value, _datetime.datetime) or value.value.tzinfo is None or value.value.utcoffset() != _datetime.timedelta(0))): raise ValueError("TYPE_CONTRACT: invalid SqlTimestamp") + if not value.is_zero and not (_MIN_TIMESTAMP <= value.value <= _MAX_TIMESTAMP): raise ValueError("TYPE_CONTRACT: TIMESTAMP is outside the SQL range") + if not value.is_zero: _check_microsecond_scale(value.value.microsecond, descriptor) if not value.is_zero and value.value.tzinfo is not _datetime.timezone.utc: return SqlTimestamp(False, value.value.astimezone(_datetime.timezone.utc)) return value @@ -526,10 +595,7 @@ def _output_array(values: Any, descriptor: Dict[str, Any], rows: int) -> pa.Arra def _validate_array_values(array: pa.Array, descriptor: Dict[str, Any]) -> None: type_id = int(descriptor["type_id"]) - if type_id == JSON: - for index in range(len(array)): - if not array.is_null(index): _check_json(array[index].as_py()) - elif type_id in (DATE, DATETIME, TIMESTAMP): + if type_id in (DATE, DATETIME, TIMESTAMP): struct = array for index in range(len(struct)): if struct.is_null(index): continue @@ -538,6 +604,18 @@ def _validate_array_values(array: pa.Array, descriptor: Dict[str, Any]) -> None: if value is None: raise ValueError("TYPE_CONTRACT: temporal child is null") if is_zero and ((type_id == DATE and value != _datetime.date(1970, 1, 1)) or (type_id == DATETIME and value != _datetime.datetime(1970, 1, 1)) or (type_id == TIMESTAMP and value != _datetime.datetime(1970, 1, 1, tzinfo=_datetime.timezone.utc))): raise ValueError("TYPE_CONTRACT: temporal zero placeholder is invalid") + if not is_zero: + if type_id == DATE: _check_scalar(SqlDate(False, value), descriptor) + elif type_id == DATETIME: _check_scalar(SqlDatetime(False, value), descriptor) + else: _check_scalar(SqlTimestamp(False, value), descriptor) + elif type_id in (CHAR, VARCHAR, TEXT, JSON, BINARY, VARBINARY, BLOB, UUID, TIME, DECIMAL64, DECIMAL128): + for index in range(len(array)): + if array.is_null(index): continue + value = array[index].as_py() + if type_id == UUID: + if not isinstance(value, (bytes, bytearray)) or len(value) != 16: raise ValueError("TYPE_CONTRACT: UUID must contain 16 bytes") + else: + _check_scalar(value, descriptor) class _InvocationState: @@ -643,9 +721,9 @@ def do_action(self, context, action): return raise ValueError("PROTOCOL: unknown invocation") if action.type == "AcknowledgeResults": - state.ack_result(int(control.get("ack_sequence", 0))) + state.ack_result(_required_uint64(control, "ack_sequence")) elif action.type == "AcknowledgeFinish": - state.ack_finish(str(control.get("finish_id", ""))) + state.ack_finish(_required_string(control, "finish_id")) yield _encode_control({"kind": "Ack", "tuple": control["tuple"], "status": "OK", "ack_sequence": control.get("ack_sequence", 0)}) def do_exchange(self, context, descriptor, reader, writer): @@ -658,27 +736,30 @@ def do_exchange(self, context, descriptor, reader, writer): raise ValueError("PROTOCOL: exchange is missing the invocation descriptor") open_control = _decode_control(command) if open_control["kind"] != "OpenInvocation": raise ValueError("PROTOCOL: first message must open an invocation") - if not isinstance(open_control.get("payload"), (dict, str)): + if not isinstance(open_control.get("payload"), dict): raise ValueError("PROTOCOL: invocation payload must be an object") key = _tuple_key(open_control["tuple"]) - payload = open_control.get("payload") or {} - if isinstance(payload, str): - payload = json.loads(payload) - if not isinstance(payload, dict): - raise ValueError("PROTOCOL: invocation payload must be an object") - args = list(payload.get("args", [])); result_descriptor = payload["return"] + payload = open_control["payload"] + args = payload.get("args") + result_descriptor = payload.get("return") + if not isinstance(args, list) or any(not isinstance(item, dict) for item in args): + raise ValueError("PROTOCOL: invocation args must be an array of descriptors") + if not isinstance(result_descriptor, dict): + raise ValueError("PROTOCOL: invocation return must be a descriptor") statement_context = _statement_context(payload.get("context")) - mode = payload.get("mode", MODE_SCALAR); null_policy = payload.get("null_policy", NULL_CALL) - abi_contract = payload.get("abi_contract", ABI_CONTRACT) - adapter_version = payload.get("adapter_version", ADAPTER_VERSION) - sdk_version = payload.get("sdk_version", SDK_VERSION) - max_batch_rows = int(payload.get("max_batch_rows", DEFAULT_MAX_BATCH_ROWS)) + mode = _required_string(payload, "mode") + null_policy = _required_string(payload, "null_policy") + abi_contract = _required_string(payload, "abi_contract") + adapter_version = _required_string(payload, "adapter_version") + sdk_version = _required_string(payload, "sdk_version") + max_batch_bytes = _required_positive_int(payload, "max_batch_bytes", 1 << 30) + max_batch_rows = _required_positive_int(payload, "max_batch_rows", 1 << 30) if mode not in (MODE_SCALAR, MODE_VECTOR) or null_policy not in (NULL_CALL, NULL_RETURN): raise ValueError("PROTOCOL: unsupported call mode or NULL policy") if abi_contract != ABI_CONTRACT or adapter_version != ADAPTER_VERSION: raise ValueError("PROTOCOL: unsupported Python ABI contract") - if max_batch_rows <= 0: raise ValueError("PROTOCOL: invalid max batch rows") + if sdk_version != SDK_VERSION: raise ValueError("PROTOCOL: unsupported Python SDK") state = self._admit(key) state.tuple = open_control["tuple"] - handler = _load_handler(payload["source"], payload["handler"]) + handler = _load_handler(_required_string(payload, "source"), _required_string(payload, "handler")) schema = None result_field = _field("result", result_descriptor) result_schema = pa.schema([result_field]) @@ -698,11 +779,12 @@ def do_exchange(self, context, descriptor, reader, writer): control = _decode_control(chunk.app_metadata) _require_tuple(control["tuple"], open_control["tuple"]) if control["kind"] != "InputBatch": raise ValueError("PROTOCOL: data batch is missing InputBatch") - sequence = int(control.get("sequence", 0)) + sequence = _required_uint64(control, "sequence") if sequence != state.last_result + 1: raise ValueError("PROTOCOL: input sequence is not contiguous") batch = chunk.data if batch.num_rows <= 0: raise ValueError("PROTOCOL: empty input batch") if batch.num_rows > max_batch_rows: raise ValueError("RESOURCE_EXHAUSTED: input batch has too many rows") + if batch.nbytes > max_batch_bytes: raise ValueError("RESOURCE_EXHAUSTED: input batch exceeds byte limit") if mode == MODE_VECTOR: call_context = VectorContext(sdk_version, _CallLogger(), statement_context, batch.num_rows) try: @@ -725,6 +807,8 @@ def do_exchange(self, context, descriptor, reader, writer): output = values output_array = _output_array(output, result_descriptor, batch.num_rows) output_batch = pa.RecordBatch.from_arrays([output_array], schema=result_schema) + if pa.ipc.get_record_batch_size(output_batch) > max_batch_bytes: + raise ValueError("RESOURCE_EXHAUSTED: output batch exceeds byte limit") state.last_result = sequence writer.write_metadata(_encode_control({"kind": "InputConsumed", "tuple": open_control["tuple"], "sequence": sequence})) writer.write_with_metadata(output_batch, _encode_control({"kind": "ResultBatch", "tuple": open_control["tuple"], "sequence": sequence})) @@ -733,7 +817,7 @@ def do_exchange(self, context, descriptor, reader, writer): control = _decode_control(chunk.app_metadata) _require_tuple(control["tuple"], open_control["tuple"]) if control["kind"] == "EndInput": - if ended or int(control.get("last_sequence", -1)) != state.last_result: raise ValueError("PROTOCOL: invalid EndInput") + if ended or _required_uint64(control, "last_sequence", allow_zero=True) != state.last_result: raise ValueError("PROTOCOL: invalid EndInput") ended = True elif control["kind"] != "OpenInvocation": raise ValueError("PROTOCOL: unexpected control without Arrow data") From 7f3fb06ea77bbed28147f2e4ac7a5f5690c577df Mon Sep 17 00:00:00 2001 From: iamlinjunhong <1030420200@qq.com> Date: Wed, 9 Sep 2026 17:35:15 +0800 Subject: [PATCH 005/202] fix(udf): align SQL selection and system account fences --- pkg/sql/plan/function/python_udf.go | 7 ++++++- pkg/udf/protocol/protocol.go | 5 ++++- pkg/udf/protocol/protocol_test.go | 8 ++++++++ pkg/udf/python/worker/test_worker.py | 6 +++++- pkg/udf/python/worker/worker.py | 11 +++++++++-- 5 files changed, 32 insertions(+), 5 deletions(-) diff --git a/pkg/sql/plan/function/python_udf.go b/pkg/sql/plan/function/python_udf.go index e4cdcd9ee0ab2..c63a872febb43 100644 --- a/pkg/sql/plan/function/python_udf.go +++ b/pkg/sql/plan/function/python_udf.go @@ -130,7 +130,12 @@ func runPythonUdf(parameters []*vector.Vector, result vector.FunctionResultWrapp return fmt.Errorf("python udf: input vector %d is shorter than invocation length", index) } } - if selectList != nil && len(selectList.SelectList) < length { + // FunctionExpressionExecutor passes an empty, non-nil select list when the + // caller selected every row. In that state AnyNull is false and the + // missing bitmap means "all rows", not a truncated selection. A partial + // selection must still carry one entry per invocation row so that a row + // cannot accidentally be evaluated after CASE/short-circuit filtering. + if selectList != nil && !selectList.ShouldEvalAllRow() && len(selectList.SelectList) < length { return fmt.Errorf("python udf: selection list is shorter than invocation length") } diff --git a/pkg/udf/protocol/protocol.go b/pkg/udf/protocol/protocol.go index b2a9403d32b39..419e1fb31acd3 100644 --- a/pkg/udf/protocol/protocol.go +++ b/pkg/udf/protocol/protocol.go @@ -59,7 +59,10 @@ type FencingTuple struct { } func (t FencingTuple) Validate() error { - if t.AccountID == 0 || t.StatementID == "" || t.GroupID == "" || + // Account zero is the reserved system account in MatrixOne and is a valid + // execution identity. The remaining fields are generation fences and must + // never be zero or empty. + if t.StatementID == "" || t.GroupID == "" || t.GroupEpoch == 0 || t.InvocationID == "" || t.LeaseEpoch == 0 { return fmt.Errorf("%w: incomplete fencing tuple", ErrProtocol) } diff --git a/pkg/udf/protocol/protocol_test.go b/pkg/udf/protocol/protocol_test.go index c1676b4351b1d..a9268fa7bd1d9 100644 --- a/pkg/udf/protocol/protocol_test.go +++ b/pkg/udf/protocol/protocol_test.go @@ -48,6 +48,14 @@ func TestControlRoundTripIsBoundedAndVersioned(t *testing.T) { require.ErrorIs(t, err, ErrProtocol) } +func TestSystemAccountIsValidFencingIdentity(t *testing.T) { + tuple := testTuple() + tuple.AccountID = 0 + require.NoError(t, tuple.Validate()) + _, err := MarshalControl(Control{Kind: "OpenInvocation", Tuple: tuple}) + require.NoError(t, err) +} + func TestSequenceKeepsHalfCloseIndependentFromResults(t *testing.T) { var sequence Sequence require.NoError(t, sequence.AcceptInput(1)) diff --git a/pkg/udf/python/worker/test_worker.py b/pkg/udf/python/worker/test_worker.py index 752a1469e6f5a..aa26968e6bf02 100644 --- a/pkg/udf/python/worker/test_worker.py +++ b/pkg/udf/python/worker/test_worker.py @@ -138,13 +138,17 @@ def test_active_fence_cannot_be_admitted_twice(self): def test_control_numeric_fields_are_strict(self): tuple_value = { - "account_id": 1, + "account_id": 0, "statement_id": "statement", "group_id": "group", "group_epoch": 1, "invocation_id": "invocation", "lease_epoch": 1, } + self.assertEqual( + (0, "statement", "group", 1, "invocation", 1), + worker._tuple_key(tuple_value), + ) with self.assertRaisesRegex(ValueError, "unsupported control"): worker._decode_control(json.dumps({"version": True, "kind": "InputBatch", "tuple": tuple_value}).encode()) with self.assertRaisesRegex(ValueError, "invalid control field sequence"): diff --git a/pkg/udf/python/worker/worker.py b/pkg/udf/python/worker/worker.py index 3257a02d95240..9dd4b68e2c3ef 100644 --- a/pkg/udf/python/worker/worker.py +++ b/pkg/udf/python/worker/worker.py @@ -230,15 +230,22 @@ def _statement_context(raw: Any) -> Optional[StatementContext]: def _tuple_key(value: Dict[str, Any]) -> tuple: string_fields = ("statement_id", "group_id", "invocation_id") - numeric_fields = ("account_id", "group_epoch", "lease_epoch") if not isinstance(value, dict) or any(not isinstance(value.get(k), str) or not value[k] for k in string_fields): raise ValueError("PROTOCOL: incomplete fencing tuple") + account_id = value.get("account_id") + if ( + isinstance(account_id, bool) + or not isinstance(account_id, int) + or account_id < 0 + or account_id > (1 << 64) - 1 + ): + raise ValueError("PROTOCOL: incomplete fencing tuple") if any( isinstance(value.get(k), bool) or not isinstance(value.get(k), int) or value[k] <= 0 or value[k] > (1 << 64) - 1 - for k in numeric_fields + for k in ("group_epoch", "lease_epoch") ): raise ValueError("PROTOCOL: incomplete fencing tuple") return ( From 35e40735d1e92433a05fa2a83e63aa2cefc46120 Mon Sep 17 00:00:00 2001 From: iamlinjunhong <1030420200@qq.com> Date: Wed, 9 Sep 2026 18:16:59 +0800 Subject: [PATCH 006/202] test(udf): add Python cases to CI BVT --- .github/workflows/python-udf-bvt.yaml | 112 ++++++++++++++++++ .../python-udf-worker.toml | 1 + optools/bvt_ut/entrypoint.sh | 4 +- optools/run_bvt.sh | 6 +- optools/run_bvt_group.sh | 4 + optools/run_python_udf_bvt.sh | 36 ++++++ test/distributed/cases/udf_python/README.md | 24 ++++ .../cases/udf_python/udf_python.result | 19 +++ .../cases/udf_python/udf_python.sql | 14 +++ 9 files changed, 217 insertions(+), 3 deletions(-) create mode 100644 .github/workflows/python-udf-bvt.yaml create mode 100755 optools/run_python_udf_bvt.sh create mode 100644 test/distributed/cases/udf_python/README.md create mode 100644 test/distributed/cases/udf_python/udf_python.result create mode 100644 test/distributed/cases/udf_python/udf_python.sql diff --git a/.github/workflows/python-udf-bvt.yaml b/.github/workflows/python-udf-bvt.yaml new file mode 100644 index 0000000000000..56a2985b51eea --- /dev/null +++ b/.github/workflows/python-udf-bvt.yaml @@ -0,0 +1,112 @@ +name: MatrixOne Python UDF BVT + +on: + workflow_call: + inputs: + head_repository: + description: "Repository containing the PR head" + required: true + type: string + head_sha: + description: "Exact PR head commit" + required: true + type: string + build_artifact: + description: "Optional shared MatrixOne build artifact" + required: false + type: string + default: "" + +permissions: + contents: read + +jobs: + bvt: + name: Python UDF SQL cases + runs-on: ${{ vars.RUNNER_LABEL || 'ubuntu-22.04' }} + timeout-minutes: 45 + steps: + - name: Checkout MatrixOne head + uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 + with: + persist-credentials: false + fetch-depth: 3 + repository: ${{ inputs.head_repository }} + ref: ${{ inputs.head_sha }} + + - name: Set up Go and Java + uses: matrixorigin/CI/actions/setup-env@main + with: + go-version-file: "${{ github.workspace }}/go.mod" + + - name: Set up Python + uses: actions/setup-python@v5 + with: + python-version: "3.12" + + - name: Install the locked worker dependency + run: >- + python3 -m pip install --no-cache-dir --only-binary=:all: + -r pkg/udf/python/worker/requirements.txt + + - name: Download shared MatrixOne build + if: ${{ inputs.build_artifact != '' }} + continue-on-error: true + uses: actions/download-artifact@v4 + with: + name: ${{ inputs.build_artifact }} + path: ${{ runner.temp }}/mo-prebuilt + + - name: Restore the shared runtime or build locally + env: + EXPECTED_HEAD_SHA: ${{ inputs.head_sha }} + run: | + set -euo pipefail + artifact="${RUNNER_TEMP}/mo-prebuilt/mo-build.tar.gz" + if [ -s "${artifact}" ]; then + unpacked="${RUNNER_TEMP}/mo-prebuilt/unpacked" + rm -rf "${unpacked}" + mkdir -p "${unpacked}" + if tar -xzf "${artifact}" -C "${unpacked}"; then + artifact_head_sha=$(sed -n 's/^head_sha=//p' "${unpacked}/BUILD_INFO" 2>/dev/null || true) + artifact_build_opt=$(sed -n 's/^gobuild_opt=//p' "${unpacked}/BUILD_INFO" 2>/dev/null || true) + if [ "${artifact_head_sha}" = "${EXPECTED_HEAD_SHA}" ] && [ "${artifact_build_opt}" = "-cover" ]; then + cp -a "${unpacked}/BUILD_INFO" "${unpacked}/mo-service" "${unpacked}/lib" "${unpacked}/dict" "${GITHUB_WORKSPACE}/" + chmod +x mo-service + else + echo "::warning::shared build metadata mismatch; falling back to a local build" + fi + else + echo "::warning::shared build artifact could not be extracted; falling back to a local build" + fi + fi + if [ ! -x mo-service ]; then + make build + fi + mkdir -p "${RUNNER_TEMP}/gocoverdir" + + - name: Checkout mo-tester + uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 + with: + repository: matrixorigin/mo-tester + ref: main + path: mo-tester + persist-credentials: false + + - name: Run Python UDF BVT cases + env: + GOCOVERDIR: ${{ runner.temp }}/gocoverdir + run: | + set -euo pipefail + ./optools/run_python_udf_bvt.sh "${GITHUB_WORKSPACE}" "${GITHUB_WORKSPACE}/mo-tester" + + - name: Upload Python UDF BVT diagnostics + if: ${{ always() }} + uses: actions/upload-artifact@v4 + with: + name: python-udf-bvt-${{ github.run_id }}-${{ github.run_attempt }} + if-no-files-found: ignore + path: | + mo-service.log + python-udf-worker-*.log + mo-tester/report diff --git a/etc/launch-with-python-udf-worker/python-udf-worker.toml b/etc/launch-with-python-udf-worker/python-udf-worker.toml index 40a0d22bb0ade..21a661af1af09 100644 --- a/etc/launch-with-python-udf-worker/python-udf-worker.toml +++ b/etc/launch-with-python-udf-worker/python-udf-worker.toml @@ -4,3 +4,4 @@ service-type = "PYTHON_UDF" uuid = "dd1dccb4-4d3c-41f8-b482-5251dc7a41b2" address = "127.0.0.1:50051" path = "pkg/udf/python/worker" +python = "python3" diff --git a/optools/bvt_ut/entrypoint.sh b/optools/bvt_ut/entrypoint.sh index d57a599fd34f1..fe3741ab456f3 100755 --- a/optools/bvt_ut/entrypoint.sh +++ b/optools/bvt_ut/entrypoint.sh @@ -45,7 +45,7 @@ function run_bvt() { echo ">>>>>>>>>>>>>>>>>>>>>>>> start bvt" # use test/distributed/cases as default test cases echo "> test case: test/distributed/cases" - cd mo-tester && ./run.sh -n -g -o -p /matrixone-test/test/distributed/cases -e optimistic 2>&1 + cd mo-tester && ./run.sh -n -g -o -p /matrixone-test/test/distributed/cases -e optimistic,udf_python 2>&1 } function bvt_ut() { @@ -63,4 +63,4 @@ function bvt_ut() { run_bvt } -bvt_ut \ No newline at end of file +bvt_ut diff --git a/optools/run_bvt.sh b/optools/run_bvt.sh index 0a46252e8912f..5d53fb370bda5 100755 --- a/optools/run_bvt.sh +++ b/optools/run_bvt.sh @@ -72,6 +72,10 @@ function launch_jstfu() { } launch_mo -launch_jstfu || exit $? +if [[ "${SKIP_JSTFU:-false}" == "true" ]]; then + echo "skip jstfu for this BVT suite" +else + launch_jstfu || exit $? +fi wait_system_init exit $? diff --git a/optools/run_bvt_group.sh b/optools/run_bvt_group.sh index 1984aac8bfb6b..ccf53d4c0fb4c 100644 --- a/optools/run_bvt_group.sh +++ b/optools/run_bvt_group.sh @@ -36,6 +36,10 @@ while IFS= read -r script_path; do relative_path=${script_path#"${case_root}/"} [[ "${relative_path}" == *optimistic* ]] && continue top_level=${relative_path%%/*} + # Python UDF needs an explicit worker-enabled launch and Python/pyarrow + # dependencies. Keep it out of the ordinary two-group BVT runs; the + # dedicated runner executes this suite with the worker enabled. + [[ "${top_level}" == "udf_python" ]] && continue case "${top_level}" in array|auto_increment|benchmark|dataXtest|ddl|disttae|fake_pk|function|git4data|hint|join|keyword|load_data|mo_cloud|optimizer|pg_cast|plugin|prepare|procedure|query_result|sample|save_query_result|sequence|snapshot|sql_inject|stage|system|system_variable|temporary|tenant|tenxcloud_xx|time_window|union|util|vector|view) diff --git a/optools/run_python_udf_bvt.sh b/optools/run_python_udf_bvt.sh new file mode 100755 index 0000000000000..670c4b7293caf --- /dev/null +++ b/optools/run_python_udf_bvt.sh @@ -0,0 +1,36 @@ +#!/usr/bin/env bash + +# Copyright 2026 Matrix Origin +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +set -euo pipefail + +workspace=$(cd "${1:-.}" && pwd) +tester_dir=${2:-"${workspace}/mo-tester"} +python_bin=${PYTHON_UDF_PYTHON:-python3} + +if [[ ! -x "${workspace}/mo-service" ]]; then + (cd "${workspace}" && make build) +fi + +if [[ ! -d "${tester_dir}" ]]; then + git clone --depth=1 https://github.com/matrixorigin/mo-tester.git "${tester_dir}" +fi + +"${python_bin}" -c 'import pyarrow; assert pyarrow.__version__ == "24.0.0", pyarrow.__version__' + +SKIP_JSTFU=true "${workspace}/optools/run_bvt.sh" "${workspace}" launch-with-python-udf-worker + +cd "${tester_dir}" +./run.sh -n -g -o -p "${workspace}/test/distributed/cases/udf_python" 2>&1 diff --git a/test/distributed/cases/udf_python/README.md b/test/distributed/cases/udf_python/README.md new file mode 100644 index 0000000000000..00b87dbcb14ec --- /dev/null +++ b/test/distributed/cases/udf_python/README.md @@ -0,0 +1,24 @@ +# Python UDF BVT cases + +Put SQL cases that require the Python UDF worker in this directory. Each case +needs a matching `.result` file generated by `mo-tester`. + +Run the complete Python UDF suite locally with: + +```bash +python3 -m pip install -r pkg/udf/python/worker/requirements.txt +./optools/run_python_udf_bvt.sh "$(pwd)" /path/to/mo-tester +``` + +To create or refresh a result file after reviewing the expected output, start +the worker-enabled launch and use `mo-tester`'s `genrs` mode: + +```bash +SKIP_JSTFU=true ./optools/run_bvt.sh . launch-with-python-udf-worker +cd /path/to/mo-tester +./run.sh -p /path/to/matrixone/test/distributed/cases/udf_python/example.sql -m genrs -g +``` + +The ordinary BVT jobs intentionally skip this directory because their launch +configuration does not start a Python worker. The dedicated Python UDF CI job +runs every case here with the worker and the pinned `pyarrow` dependency. diff --git a/test/distributed/cases/udf_python/udf_python.result b/test/distributed/cases/udf_python/udf_python.result new file mode 100644 index 0000000000000..8534ba2ba2c2d --- /dev/null +++ b/test/distributed/cases/udf_python/udf_python.result @@ -0,0 +1,19 @@ +drop database if exists udf_python_bvt; +create database udf_python_bvt; +use udf_python_bvt; +create function python_bvt_add (x int) returns int language python as 'def python_bvt_add(ctx, x): return None if x is None else x + 1' handler 'python_bvt_add'; +create table input_values (v int); +insert into input_values values (1), (null), (3); +select v, python_bvt_add(v) as plus_one from input_values order by v is null, v; +➤ v[4,32,0] ¦ plus_one[4,32,0] 𝄀 +1 ¦ 2 𝄀 +3 ¦ 4 𝄀 +null ¦ null +select case when v is null then 99 else python_bvt_add(v) end as guarded from input_values order by v is null, v; +➤ guarded[-5,64,0] 𝄀 +2 𝄀 +4 𝄀 +99 +drop function python_bvt_add(int); +drop table input_values; +drop database udf_python_bvt; diff --git a/test/distributed/cases/udf_python/udf_python.sql b/test/distributed/cases/udf_python/udf_python.sql new file mode 100644 index 0000000000000..26799d1531eea --- /dev/null +++ b/test/distributed/cases/udf_python/udf_python.sql @@ -0,0 +1,14 @@ +drop database if exists udf_python_bvt; +create database udf_python_bvt; +use udf_python_bvt; + +create function python_bvt_add (x int) returns int language python as 'def python_bvt_add(ctx, x): return None if x is None else x + 1' handler 'python_bvt_add'; +create table input_values (v int); +insert into input_values values (1), (null), (3); + +select v, python_bvt_add(v) as plus_one from input_values order by v is null, v; +select case when v is null then 99 else python_bvt_add(v) end as guarded from input_values order by v is null, v; + +drop function python_bvt_add(int); +drop table input_values; +drop database udf_python_bvt; From 9f0e60dd93c6da5e102ed42889f058448879b740 Mon Sep 17 00:00:00 2001 From: iamlinjunhong <1030420200@qq.com> Date: Wed, 9 Sep 2026 18:17:35 +0800 Subject: [PATCH 007/202] chore(udf): remove stale Python service ignore rules --- .gitignore | 2 -- 1 file changed, 2 deletions(-) diff --git a/.gitignore b/.gitignore index 1d3bf8abe35ac..fa0b1458299ac 100644 --- a/.gitignore +++ b/.gitignore @@ -69,8 +69,6 @@ node-*-data/ pkg/catalog/test/ pkg/config/system_vars.go pkg/config/system_vars_test.go -pkg/udf/pythonservice/pyserver/udf/ -pkg/udf/pythonservice/pyserver/__pycache__/ path_to_file tester-log/ ut-report/ From de5e734e7b25463739769f20bb5b9226878363ce Mon Sep 17 00:00:00 2001 From: iamlinjunhong <1030420200@qq.com> Date: Wed, 9 Sep 2026 18:18:14 +0800 Subject: [PATCH 008/202] ci(udf): harden Python BVT build fallback --- .github/workflows/python-udf-bvt.yaml | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/.github/workflows/python-udf-bvt.yaml b/.github/workflows/python-udf-bvt.yaml index 56a2985b51eea..25c9a9565cabc 100644 --- a/.github/workflows/python-udf-bvt.yaml +++ b/.github/workflows/python-udf-bvt.yaml @@ -70,11 +70,15 @@ jobs: if tar -xzf "${artifact}" -C "${unpacked}"; then artifact_head_sha=$(sed -n 's/^head_sha=//p' "${unpacked}/BUILD_INFO" 2>/dev/null || true) artifact_build_opt=$(sed -n 's/^gobuild_opt=//p' "${unpacked}/BUILD_INFO" 2>/dev/null || true) - if [ "${artifact_head_sha}" = "${EXPECTED_HEAD_SHA}" ] && [ "${artifact_build_opt}" = "-cover" ]; then + if [ "${artifact_head_sha}" = "${EXPECTED_HEAD_SHA}" ] \ + && [ "${artifact_build_opt}" = "-cover" ] \ + && [ -x "${unpacked}/mo-service" ] \ + && [ -d "${unpacked}/lib" ] \ + && [ -d "${unpacked}/dict" ]; then cp -a "${unpacked}/BUILD_INFO" "${unpacked}/mo-service" "${unpacked}/lib" "${unpacked}/dict" "${GITHUB_WORKSPACE}/" chmod +x mo-service else - echo "::warning::shared build metadata mismatch; falling back to a local build" + echo "::warning::shared build artifact is incomplete or mismatched; falling back to a local build" fi else echo "::warning::shared build artifact could not be extracted; falling back to a local build" From ee9b4da9fbbd33ac1bc878126e3b6ee3615f5ea2 Mon Sep 17 00:00:00 2001 From: iamlinjunhong <1030420200@qq.com> Date: Wed, 9 Sep 2026 20:21:55 +0800 Subject: [PATCH 009/202] test(udf): run Python cases in ordinary BVT --- .github/workflows/python-udf-bvt.yaml | 116 ------------------ etc/launch-tae-compose/README.md | 5 + etc/launch-tae-compose/compose.yaml | 59 ++++++++- etc/launch-tae-compose/config/cn-0.toml | 3 + etc/launch-tae-compose/config/cn-1.toml | 3 + etc/launch/cn.toml | 3 + etc/launch/launch.toml | 3 + optools/bvt_ut/Dockerfile | 11 ++ optools/bvt_ut/entrypoint.sh | 2 +- optools/images/Dockerfile | 4 +- optools/run_bvt_group.sh | 15 +-- optools/run_python_udf_bvt.sh | 36 ------ .../udf/python/worker}/Dockerfile | 4 +- test/distributed/cases/udf_python/README.md | 22 ++-- 14 files changed, 109 insertions(+), 177 deletions(-) delete mode 100644 .github/workflows/python-udf-bvt.yaml delete mode 100755 optools/run_python_udf_bvt.sh rename {optools/images/pythonworker => pkg/udf/python/worker}/Dockerfile (80%) diff --git a/.github/workflows/python-udf-bvt.yaml b/.github/workflows/python-udf-bvt.yaml deleted file mode 100644 index 25c9a9565cabc..0000000000000 --- a/.github/workflows/python-udf-bvt.yaml +++ /dev/null @@ -1,116 +0,0 @@ -name: MatrixOne Python UDF BVT - -on: - workflow_call: - inputs: - head_repository: - description: "Repository containing the PR head" - required: true - type: string - head_sha: - description: "Exact PR head commit" - required: true - type: string - build_artifact: - description: "Optional shared MatrixOne build artifact" - required: false - type: string - default: "" - -permissions: - contents: read - -jobs: - bvt: - name: Python UDF SQL cases - runs-on: ${{ vars.RUNNER_LABEL || 'ubuntu-22.04' }} - timeout-minutes: 45 - steps: - - name: Checkout MatrixOne head - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 - with: - persist-credentials: false - fetch-depth: 3 - repository: ${{ inputs.head_repository }} - ref: ${{ inputs.head_sha }} - - - name: Set up Go and Java - uses: matrixorigin/CI/actions/setup-env@main - with: - go-version-file: "${{ github.workspace }}/go.mod" - - - name: Set up Python - uses: actions/setup-python@v5 - with: - python-version: "3.12" - - - name: Install the locked worker dependency - run: >- - python3 -m pip install --no-cache-dir --only-binary=:all: - -r pkg/udf/python/worker/requirements.txt - - - name: Download shared MatrixOne build - if: ${{ inputs.build_artifact != '' }} - continue-on-error: true - uses: actions/download-artifact@v4 - with: - name: ${{ inputs.build_artifact }} - path: ${{ runner.temp }}/mo-prebuilt - - - name: Restore the shared runtime or build locally - env: - EXPECTED_HEAD_SHA: ${{ inputs.head_sha }} - run: | - set -euo pipefail - artifact="${RUNNER_TEMP}/mo-prebuilt/mo-build.tar.gz" - if [ -s "${artifact}" ]; then - unpacked="${RUNNER_TEMP}/mo-prebuilt/unpacked" - rm -rf "${unpacked}" - mkdir -p "${unpacked}" - if tar -xzf "${artifact}" -C "${unpacked}"; then - artifact_head_sha=$(sed -n 's/^head_sha=//p' "${unpacked}/BUILD_INFO" 2>/dev/null || true) - artifact_build_opt=$(sed -n 's/^gobuild_opt=//p' "${unpacked}/BUILD_INFO" 2>/dev/null || true) - if [ "${artifact_head_sha}" = "${EXPECTED_HEAD_SHA}" ] \ - && [ "${artifact_build_opt}" = "-cover" ] \ - && [ -x "${unpacked}/mo-service" ] \ - && [ -d "${unpacked}/lib" ] \ - && [ -d "${unpacked}/dict" ]; then - cp -a "${unpacked}/BUILD_INFO" "${unpacked}/mo-service" "${unpacked}/lib" "${unpacked}/dict" "${GITHUB_WORKSPACE}/" - chmod +x mo-service - else - echo "::warning::shared build artifact is incomplete or mismatched; falling back to a local build" - fi - else - echo "::warning::shared build artifact could not be extracted; falling back to a local build" - fi - fi - if [ ! -x mo-service ]; then - make build - fi - mkdir -p "${RUNNER_TEMP}/gocoverdir" - - - name: Checkout mo-tester - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 - with: - repository: matrixorigin/mo-tester - ref: main - path: mo-tester - persist-credentials: false - - - name: Run Python UDF BVT cases - env: - GOCOVERDIR: ${{ runner.temp }}/gocoverdir - run: | - set -euo pipefail - ./optools/run_python_udf_bvt.sh "${GITHUB_WORKSPACE}" "${GITHUB_WORKSPACE}/mo-tester" - - - name: Upload Python UDF BVT diagnostics - if: ${{ always() }} - uses: actions/upload-artifact@v4 - with: - name: python-udf-bvt-${{ github.run_id }}-${{ github.run_attempt }} - if-no-files-found: ignore - path: | - mo-service.log - python-udf-worker-*.log - mo-tester/report diff --git a/etc/launch-tae-compose/README.md b/etc/launch-tae-compose/README.md index 0939446a1ecf5..eba459e079429 100644 --- a/etc/launch-tae-compose/README.md +++ b/etc/launch-tae-compose/README.md @@ -43,6 +43,11 @@ MatrixOne's trusted main-branch cache warmer keeps the Maven repository and wrapper distribution available to PR CI. Pull-request jobs restore that cache read-only; they never publish dependencies resolved from an untrusted head. +The compose BVT starts one Python UDF worker per CN. The worker image is shared +by both services, while the worker processes, addresses, and failure domains +remain separate (`cn-0` uses `python-udf-worker-cn0`, and `cn-1` uses +`python-udf-worker-cn1`). + ## Check log ```shell diff --git a/etc/launch-tae-compose/compose.yaml b/etc/launch-tae-compose/compose.yaml index 9b2136fa74139..2b30ae4e3f4c4 100644 --- a/etc/launch-tae-compose/compose.yaml +++ b/etc/launch-tae-compose/compose.yaml @@ -28,6 +28,18 @@ x-mo-common: &mo-common - NET_ADMIN tty: true +x-python-udf-worker: &python-udf-worker + image: matrixorigin/mo-python-udf-worker:latest + restart: on-failure + profiles: + - launch + - launch-multi-cn + healthcheck: + test: ["CMD", "python", "-c", "import socket; s=socket.create_connection(('127.0.0.1', 50051), 1); s.close()"] + interval: 1s + timeout: 2s + retries: 30 + services: cn-0: @@ -40,7 +52,10 @@ services: - launch - launch-multi-cn depends_on: - - tn + tn: + condition: service_started + python-udf-worker-cn0: + condition: service_healthy ports: - "6002" - "12345:12345" @@ -59,7 +74,10 @@ services: <<: *mo-common entrypoint: ["/bin/bash","-c","set -euo pipefail; exec /mo-service -debug-http :12345 -cfg /config/cn-1.toml > >(tee -a /log/cn-1.log) 2>&1"] depends_on: - - tn + tn: + condition: service_started + python-udf-worker-cn1: + condition: service_healthy ports: - "6002" - "22345:12345" @@ -146,6 +164,43 @@ services: aliases: - "logservice" + # Build the worker image once. The two CN-scoped worker services below use + # the resulting image and keep their process and failure domains separate. + python-udf-worker-image: + container_name: python-udf-worker-image + build: + context: ../../pkg/udf/python/worker + dockerfile: Dockerfile + image: matrixorigin/mo-python-udf-worker:latest + pull_policy: build + command: ["true"] + restart: "no" + profiles: + - launch + - launch-multi-cn + + python-udf-worker-cn0: + <<: *python-udf-worker + container_name: python-udf-worker-cn0 + depends_on: + python-udf-worker-image: + condition: service_completed_successfully + networks: + monet: + aliases: + - "python-udf-worker-cn0" + + python-udf-worker-cn1: + <<: *python-udf-worker + container_name: python-udf-worker-cn1 + depends_on: + python-udf-worker-image: + condition: service_completed_successfully + networks: + monet: + aliases: + - "python-udf-worker-cn1" + # --------------------------------------------------------------------- # jstfu: the datastream external table gRPC server (xtool/jstfu), needed # by test/distributed/cases/datastream. The BVT cases address it as diff --git a/etc/launch-tae-compose/config/cn-0.toml b/etc/launch-tae-compose/config/cn-0.toml index 215f9967f8938..662553a5a8806 100644 --- a/etc/launch-tae-compose/config/cn-0.toml +++ b/etc/launch-tae-compose/config/cn-0.toml @@ -66,3 +66,6 @@ enable-write = true enable-delete = true enable-dml = true enable-maintenance = true + +[cn.python-udf-client] +server-address = "python-udf-worker-cn0:50051" diff --git a/etc/launch-tae-compose/config/cn-1.toml b/etc/launch-tae-compose/config/cn-1.toml index d53336c99a27c..49b1cad684d43 100644 --- a/etc/launch-tae-compose/config/cn-1.toml +++ b/etc/launch-tae-compose/config/cn-1.toml @@ -66,3 +66,6 @@ enable-write = true enable-delete = true enable-dml = true enable-maintenance = true + +[cn.python-udf-client] +server-address = "python-udf-worker-cn1:50051" diff --git a/etc/launch/cn.toml b/etc/launch/cn.toml index f989f16a9bff6..8c315e594bb32 100644 --- a/etc/launch/cn.toml +++ b/etc/launch/cn.toml @@ -49,3 +49,6 @@ enable-write = true enable-delete = true enable-dml = true enable-maintenance = true + +[cn.python-udf-client] +server-address = "127.0.0.1:50051" diff --git a/etc/launch/launch.toml b/etc/launch/launch.toml index ddc12393f2e9c..19f97c824f70e 100644 --- a/etc/launch/launch.toml +++ b/etc/launch/launch.toml @@ -10,3 +10,6 @@ cnservices = [ "./etc/launch/cn.toml" ] +python-udf-services = [ + "./etc/launch-with-python-udf-worker/python-udf-worker.toml" +] diff --git a/optools/bvt_ut/Dockerfile b/optools/bvt_ut/Dockerfile index 0cacfeda66ab7..f0f32188e2d78 100644 --- a/optools/bvt_ut/Dockerfile +++ b/optools/bvt_ut/Dockerfile @@ -10,6 +10,17 @@ ENV ENABLE_UT="false" RUN go env -w GOPROXY="${GOPROXY}" +# The ordinary launch BVT starts the Python UDF worker together with the +# MatrixOne services. Keep its environment pinned to the same contract used by +# the compose worker image. +RUN apt-get update && \ + apt-get install -y --no-install-recommends python3-pip && \ + rm -rf /var/lib/apt/lists/* + +COPY pkg/udf/python/worker/requirements.txt /tmp/python-udf-requirements.txt +RUN python3 -m pip install --break-system-packages --no-cache-dir --only-binary=:all: \ + -r /tmp/python-udf-requirements.txt + RUN mkdir -p /matrixone-test WORKDIR /matrixone-test diff --git a/optools/bvt_ut/entrypoint.sh b/optools/bvt_ut/entrypoint.sh index fe3741ab456f3..e048892438fc9 100755 --- a/optools/bvt_ut/entrypoint.sh +++ b/optools/bvt_ut/entrypoint.sh @@ -45,7 +45,7 @@ function run_bvt() { echo ">>>>>>>>>>>>>>>>>>>>>>>> start bvt" # use test/distributed/cases as default test cases echo "> test case: test/distributed/cases" - cd mo-tester && ./run.sh -n -g -o -p /matrixone-test/test/distributed/cases -e optimistic,udf_python 2>&1 + cd mo-tester && ./run.sh -n -g -o -p /matrixone-test/test/distributed/cases -e optimistic 2>&1 } function bvt_ut() { diff --git a/optools/images/Dockerfile b/optools/images/Dockerfile index 7dce55c96b564..2398f0d5c139c 100644 --- a/optools/images/Dockerfile +++ b/optools/images/Dockerfile @@ -57,8 +57,8 @@ FROM matrixorigin/ubuntu:22.04 COPY --from=builder /go/src/github.com/matrixorigin/matrixone/mo-service /mo-service COPY --from=builder /go/src/github.com/matrixorigin/matrixone/etc /etc -COPY --from=builder /go/src/github.com/matrixorigin/matrixone/thirdparties/install/lib/*.so /usr/local/lib -COPY --from=builder /go/src/github.com/matrixorigin/matrixone/cgo/*.so /usr/local/lib +COPY --from=builder /go/src/github.com/matrixorigin/matrixone/thirdparties/install/lib/*.so /usr/local/lib/ +COPY --from=builder /go/src/github.com/matrixorigin/matrixone/cgo/*.so /usr/local/lib/ COPY --from=builder /go/src/github.com/matrixorigin/matrixone/pkg/monlp/tokenizer/dict /usr/local/share/jieba ENV MO_JIEBA_DICT_DIR=/usr/local/share/jieba diff --git a/optools/run_bvt_group.sh b/optools/run_bvt_group.sh index ccf53d4c0fb4c..17566dd4e5bbf 100644 --- a/optools/run_bvt_group.sh +++ b/optools/run_bvt_group.sh @@ -26,26 +26,19 @@ if ! [[ "${group_id}" =~ ^[01]$ ]]; then fi declare -a included_scripts=() -declare -a resource_args=() if [[ -n "${resource_dir}" ]]; then resource_dir=$(cd "${resource_dir}" && pwd) - resource_args=(-s "${resource_dir}") fi while IFS= read -r script_path; do relative_path=${script_path#"${case_root}/"} [[ "${relative_path}" == *optimistic* ]] && continue top_level=${relative_path%%/*} - # Python UDF needs an explicit worker-enabled launch and Python/pyarrow - # dependencies. Keep it out of the ordinary two-group BVT runs; the - # dedicated runner executes this suite with the worker enabled. - [[ "${top_level}" == "udf_python" ]] && continue - case "${top_level}" in array|auto_increment|benchmark|dataXtest|ddl|disttae|fake_pk|function|git4data|hint|join|keyword|load_data|mo_cloud|optimizer|pg_cast|plugin|prepare|procedure|query_result|sample|save_query_result|sequence|snapshot|sql_inject|stage|system|system_variable|temporary|tenant|tenxcloud_xx|time_window|union|util|vector|view) assigned_group=0 ;; - analyze|charset_collation|comment|cte|database|distinct|dml|dtype|expression|feature_limit|foreign_key|fulltext|geo|iceberg|log|metadata|operator|pessimistic_transaction|pitr|plan_cache|publication_subscription|qexec|recursive_cte|replace_statement|result_count|security|set|sql_source_type|statement_query_type|subquery|table|task|udf|window|zz_accesscontrol|zz_statement_query_type) + analyze|charset_collation|comment|cte|database|distinct|dml|dtype|expression|feature_limit|foreign_key|fulltext|geo|iceberg|log|metadata|operator|pessimistic_transaction|pitr|plan_cache|publication_subscription|qexec|recursive_cte|replace_statement|result_count|security|set|sql_source_type|statement_query_type|subquery|table|task|udf|udf_python|window|zz_accesscontrol|zz_statement_query_type) assigned_group=1 ;; *) @@ -73,4 +66,8 @@ fi printf '%s\n' "${included_scripts[@]}" cd "${tester_dir}" -./run.sh -n -g -o -p "${case_root}" -i "${include_list}" "${resource_args[@]}" +if [[ -n "${resource_dir}" ]]; then + ./run.sh -n -g -o -p "${case_root}" -i "${include_list}" -s "${resource_dir}" +else + ./run.sh -n -g -o -p "${case_root}" -i "${include_list}" +fi diff --git a/optools/run_python_udf_bvt.sh b/optools/run_python_udf_bvt.sh deleted file mode 100755 index 670c4b7293caf..0000000000000 --- a/optools/run_python_udf_bvt.sh +++ /dev/null @@ -1,36 +0,0 @@ -#!/usr/bin/env bash - -# Copyright 2026 Matrix Origin -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -set -euo pipefail - -workspace=$(cd "${1:-.}" && pwd) -tester_dir=${2:-"${workspace}/mo-tester"} -python_bin=${PYTHON_UDF_PYTHON:-python3} - -if [[ ! -x "${workspace}/mo-service" ]]; then - (cd "${workspace}" && make build) -fi - -if [[ ! -d "${tester_dir}" ]]; then - git clone --depth=1 https://github.com/matrixorigin/mo-tester.git "${tester_dir}" -fi - -"${python_bin}" -c 'import pyarrow; assert pyarrow.__version__ == "24.0.0", pyarrow.__version__' - -SKIP_JSTFU=true "${workspace}/optools/run_bvt.sh" "${workspace}" launch-with-python-udf-worker - -cd "${tester_dir}" -./run.sh -n -g -o -p "${workspace}/test/distributed/cases/udf_python" 2>&1 diff --git a/optools/images/pythonworker/Dockerfile b/pkg/udf/python/worker/Dockerfile similarity index 80% rename from optools/images/pythonworker/Dockerfile rename to pkg/udf/python/worker/Dockerfile index d273cfd41d450..3fdb7d5528114 100644 --- a/optools/images/pythonworker/Dockerfile +++ b/pkg/udf/python/worker/Dockerfile @@ -1,8 +1,8 @@ # run from root path for debug: -# docker build -t mo-python-udf-worker -f ./optools/images/pythonworker/Dockerfile ./pkg/udf/python/worker/ +# docker build -t mo-python-udf-worker -f ./pkg/udf/python/worker/Dockerfile ./pkg/udf/python/worker/ # docker run -d --network host mo-python-udf-worker -FROM python:3.12-bookworm +FROM python:3.12-slim-bookworm RUN useradd -m -s /bin/bash mo diff --git a/test/distributed/cases/udf_python/README.md b/test/distributed/cases/udf_python/README.md index 00b87dbcb14ec..8c717c7021153 100644 --- a/test/distributed/cases/udf_python/README.md +++ b/test/distributed/cases/udf_python/README.md @@ -1,24 +1,28 @@ # Python UDF BVT cases Put SQL cases that require the Python UDF worker in this directory. Each case -needs a matching `.result` file generated by `mo-tester`. +needs a matching `.result` file generated by `mo-tester`. These cases are part +of the ordinary BVT suite and run with the other SQL cases. -Run the complete Python UDF suite locally with: +For a local launch BVT, install the locked worker dependency once and use the +normal BVT commands: ```bash python3 -m pip install -r pkg/udf/python/worker/requirements.txt -./optools/run_python_udf_bvt.sh "$(pwd)" /path/to/mo-tester +./optools/run_bvt.sh . launch +cd /path/to/mo-tester +./run.sh -n -g -o -p /path/to/matrixone/test/distributed/cases ``` -To create or refresh a result file after reviewing the expected output, start -the worker-enabled launch and use `mo-tester`'s `genrs` mode: +To create or refresh a result file after reviewing the expected output, keep +the normal worker-enabled launch running and use `mo-tester`'s `genrs` mode: ```bash -SKIP_JSTFU=true ./optools/run_bvt.sh . launch-with-python-udf-worker +SKIP_JSTFU=true ./optools/run_bvt.sh . launch cd /path/to/mo-tester ./run.sh -p /path/to/matrixone/test/distributed/cases/udf_python/example.sql -m genrs -g ``` -The ordinary BVT jobs intentionally skip this directory because their launch -configuration does not start a Python worker. The dedicated Python UDF CI job -runs every case here with the worker and the pinned `pyarrow` dependency. +The ordinary launch manifest starts one worker for the single CN. Compose BVT +starts one isolated worker service per CN from the same pinned image, so adding +a SQL case here does not require another CI workflow. From 24ce9c6bffcefb6712b879f9d3b4b704ac45fd21 Mon Sep 17 00:00:00 2001 From: iamlinjunhong <1030420200@qq.com> Date: Wed, 9 Sep 2026 21:02:43 +0800 Subject: [PATCH 010/202] test(udf): expand Python boundary BVT coverage --- pkg/frontend/authenticate.go | 27 +++++++ pkg/frontend/compiler_context.go | 3 + pkg/sql/plan/build_util.go | 11 +++ pkg/sql/plan/function/python_udf.go | 10 +++ pkg/sql/plan/function/udf.go | 80 ++++++++++++++++--- pkg/sql/plan/function/udf_test.go | 56 +++++++++++++ pkg/udf/python/worker/test_worker.py | 18 +++++ pkg/udf/python/worker/worker.py | 19 +++-- test/distributed/cases/udf_python/README.md | 19 +++++ .../cases/udf_python/control_flow.result | 51 ++++++++++++ .../cases/udf_python/control_flow.sql | 49 ++++++++++++ .../udf_python/numeric_boundaries.result | 70 ++++++++++++++++ .../cases/udf_python/numeric_boundaries.sql | 72 +++++++++++++++++ .../udf_python/temporal_boundaries.result | 41 ++++++++++ .../cases/udf_python/temporal_boundaries.sql | 43 ++++++++++ .../cases/udf_python/value_types.result | 72 +++++++++++++++++ .../cases/udf_python/value_types.sql | 64 +++++++++++++++ 17 files changed, 687 insertions(+), 18 deletions(-) create mode 100644 pkg/sql/plan/function/udf_test.go create mode 100644 test/distributed/cases/udf_python/control_flow.result create mode 100644 test/distributed/cases/udf_python/control_flow.sql create mode 100644 test/distributed/cases/udf_python/numeric_boundaries.result create mode 100644 test/distributed/cases/udf_python/numeric_boundaries.sql create mode 100644 test/distributed/cases/udf_python/temporal_boundaries.result create mode 100644 test/distributed/cases/udf_python/temporal_boundaries.sql create mode 100644 test/distributed/cases/udf_python/value_types.result create mode 100644 test/distributed/cases/udf_python/value_types.sql diff --git a/pkg/frontend/authenticate.go b/pkg/frontend/authenticate.go index 6232e9d05b87a..fdfb5d528b051 100644 --- a/pkg/frontend/authenticate.go +++ b/pkg/frontend/authenticate.go @@ -11495,6 +11495,8 @@ func InitFunction(ses *Session, execCtx *ExecCtx, tenant *TenantInfo, cf *tree.C var argList []*function.Arg var typeList []string var erArray []ExecResult + var pythonArgTypes []function.PythonTypeDescriptor + var pythonReturnType *function.PythonTypeDescriptor // a database must be selected or specified as qualifier when create a function if cf.Name.HasNoNameQualifier() { @@ -11538,6 +11540,18 @@ func InitFunction(ses *Session, execCtx *ExecCtx, tenant *TenantInfo, cf *tree.C if err != nil { return err } + if cf.Language == string(tree.PYTHON) { + returnType, typeErr := plan2.GetFunctionTypeFromAst(cf.ReturnType.Type) + if typeErr != nil { + return typeErr + } + descriptor, typeErr := function.NewPythonTypeDescriptor(returnType) + if typeErr != nil { + return typeErr + } + pythonReturnType = &descriptor + pythonArgTypes = make([]function.PythonTypeDescriptor, len(cf.Args)) + } // build argmap and marshal as json argList = make([]*function.Arg, len(cf.Args)) @@ -11552,6 +11566,17 @@ func InitFunction(ses *Session, execCtx *ExecCtx, tenant *TenantInfo, cf *tree.C } argList[i].Type = typ typeList[i] = typ + if cf.Language == string(tree.PYTHON) { + argType, typeErr := plan2.GetFunctionTypeFromAst(cf.Args[i].(*tree.FunctionArgDecl).Type) + if typeErr != nil { + return typeErr + } + descriptor, typeErr := function.NewPythonTypeDescriptor(argType) + if typeErr != nil { + return typeErr + } + pythonArgTypes[i] = descriptor + } } argsJson, err = json.Marshal(argList) if err != nil { @@ -11596,6 +11621,8 @@ func InitFunction(ses *Session, execCtx *ExecCtx, tenant *TenantInfo, cf *tree.C ABIContract: udf.PythonABIContract, AdapterVersion: udf.PythonAdapterVersion, SDKVersion: udf.PythonSDKVersion, + ArgTypes: pythonArgTypes, + ReturnType: pythonReturnType, } var byt []byte byt, err = json.Marshal(nb) diff --git a/pkg/frontend/compiler_context.go b/pkg/frontend/compiler_context.go index a398929b1ef37..fc3065f2cd232 100644 --- a/pkg/frontend/compiler_context.go +++ b/pkg/frontend/compiler_context.go @@ -898,6 +898,9 @@ func (tcc *TxnCompilerContext) ResolveUdf(name string, args []*plan.Expr) (udf * return nil, getErr } udf.SQLMode = &mode + if err = udf.LoadPythonTypeContract(); err != nil { + return nil, err + } // arg type check argList := make([]*function.Arg, 0) err = json.Unmarshal([]byte(argstr), &argList) diff --git a/pkg/sql/plan/build_util.go b/pkg/sql/plan/build_util.go index 7138830a7cb81..138c9138c8b41 100644 --- a/pkg/sql/plan/build_util.go +++ b/pkg/sql/plan/build_util.go @@ -128,6 +128,17 @@ func GetFunctionArgTypeStrFromAst(arg tree.FunctionArg) (string, error) { return GetFunctionTypeStrFromAst(argDecl.Type) } +// GetFunctionTypeFromAst retains the full type attributes needed by external +// routine ABIs. GetFunctionTypeStrFromAst intentionally returns only the +// logical catalog name for legacy UDF overload identity. +func GetFunctionTypeFromAst(typRef tree.ResolvableTypeReference) (types.Type, error) { + typ, err := getTypeFromAst(moerr.Context(), typRef) + if err != nil { + return types.Type{}, err + } + return types.NewWithCharset(types.T(typ.Id), typ.Width, typ.Scale, uint8(typ.Charset)), nil +} + func GetFunctionTypeStrFromAst(typRef tree.ResolvableTypeReference) (string, error) { typ, err := getTypeFromAst(moerr.Context(), typRef) if err != nil { diff --git a/pkg/sql/plan/function/python_udf.go b/pkg/sql/plan/function/python_udf.go index c63a872febb43..5531541f2850a 100644 --- a/pkg/sql/plan/function/python_udf.go +++ b/pkg/sql/plan/function/python_udf.go @@ -94,6 +94,9 @@ func runPythonUdf(parameters []*vector.Vector, result vector.FunctionResultWrapp if routine.Udf == nil { return fmt.Errorf("python udf: routine descriptor has no function") } + if err := routine.LoadPythonTypeContract(); err != nil { + return fmt.Errorf("python udf: decode Python type contract: %w", err) + } body := PythonRoutineBody{} if err := json.Unmarshal([]byte(routine.Body), &body); err != nil { @@ -249,6 +252,13 @@ func hasNullInput(inputs []*vector.Vector, row int) bool { } func routineArgumentTypes(routine *UdfWithContext) ([]types.Type, error) { + if len(routine.PythonArgTypes) != 0 { + args := make([]types.Type, len(routine.PythonArgTypes)) + for i, descriptor := range routine.PythonArgTypes { + args[i] = descriptor.Type() + } + return args, nil + } if len(routine.ArgsType) != 0 || len(routine.Args) == 0 { return append([]types.Type(nil), routine.ArgsType...), nil } diff --git a/pkg/sql/plan/function/udf.go b/pkg/sql/plan/function/udf.go index 2f427b4349005..47d0fb22800b4 100644 --- a/pkg/sql/plan/function/udf.go +++ b/pkg/sql/plan/function/udf.go @@ -14,6 +14,7 @@ import ( "github.com/google/uuid" "github.com/matrixorigin/matrixone/pkg/container/types" "github.com/matrixorigin/matrixone/pkg/pb/plan" + pythonudf "github.com/matrixorigin/matrixone/pkg/udf/python" ) type Udf struct { @@ -25,6 +26,12 @@ type Udf struct { ModifiedTime string `json:"modified_time"` SQLMode *string `json:"-"` ArgsType []types.Type `json:"-"` + // PythonArgTypes and PythonReturnType preserve the exact SQL declaration + // for Python's Arrow contract. The legacy catalog's args/rettype columns + // intentionally keep only logical type names, which is insufficient for + // width and scale validation. + PythonArgTypes []PythonTypeDescriptor `json:"-"` + PythonReturnType *PythonTypeDescriptor `json:"-"` } type UdfWithContext struct { @@ -36,15 +43,26 @@ type UdfWithContext struct { // catalog must persist mode and NULL policy with the source so execution does // not infer either from a generic SQL function class bit. type PythonRoutineBody struct { - Handler string `json:"handler"` - Source string `json:"source,omitempty"` - Mode string `json:"mode"` - NullPolicy string `json:"null_policy"` - ABIContract string `json:"abi_contract"` - AdapterVersion string `json:"adapter_version"` - ArtifactDigest string `json:"artifact_digest,omitempty"` - EnvironmentDigest string `json:"environment_digest,omitempty"` - SDKVersion string `json:"sdk_version,omitempty"` + Handler string `json:"handler"` + Source string `json:"source,omitempty"` + Mode string `json:"mode"` + NullPolicy string `json:"null_policy"` + ABIContract string `json:"abi_contract"` + AdapterVersion string `json:"adapter_version"` + ArtifactDigest string `json:"artifact_digest,omitempty"` + EnvironmentDigest string `json:"environment_digest,omitempty"` + SDKVersion string `json:"sdk_version,omitempty"` + ArgTypes []PythonTypeDescriptor `json:"arg_types,omitempty"` + ReturnType *PythonTypeDescriptor `json:"return_type,omitempty"` +} + +// PythonTypeDescriptor is the single Arrow ABI descriptor used by both plan +// resolution and the Python adapter. The UDF package keeps it as an alias so +// the catalog body and the Arrow metadata cannot drift independently. +type PythonTypeDescriptor = pythonudf.TypeDescriptor + +func NewPythonTypeDescriptor(typ types.Type) (PythonTypeDescriptor, error) { + return pythonudf.NewTypeDescriptor(typ) } type Arg struct { @@ -59,15 +77,51 @@ func (u *Udf) GetPlanExpr() *plan.Expr { } func (u *Udf) GetArgsPlanType() []*plan.Type { - result := make([]*plan.Type, len(u.ArgsType)) - for i, typ := range u.ArgsType { + args := u.GetArgsType() + result := make([]*plan.Type, len(args)) + for i, typ := range args { result[i] = type2PlanType(typ) } return result } func (u *Udf) GetRetPlanType() *plan.Type { return type2PlanType(u.GetRetType()) } -func (u *Udf) GetArgsType() []types.Type { return u.ArgsType } -func (u *Udf) GetRetType() types.Type { return types.Types[u.RetType].ToType() } +func (u *Udf) GetArgsType() []types.Type { + if len(u.PythonArgTypes) != 0 { + args := make([]types.Type, len(u.PythonArgTypes)) + for i, descriptor := range u.PythonArgTypes { + args[i] = descriptor.Type() + } + return args + } + return u.ArgsType +} +func (u *Udf) GetRetType() types.Type { + if u.PythonReturnType != nil { + return u.PythonReturnType.Type() + } + return types.Types[u.RetType].ToType() +} + +// LoadPythonTypeContract copies exact type metadata persisted in the Python +// routine body into the plan object. Older catalog rows have no such fields +// and intentionally retain the legacy logical-type fallback. +func (u *Udf) LoadPythonTypeContract() error { + if u.Language != "python" { + return nil + } + body := PythonRoutineBody{} + if err := json.Unmarshal([]byte(u.Body), &body); err != nil { + return err + } + if len(body.ArgTypes) != 0 { + u.PythonArgTypes = append([]PythonTypeDescriptor(nil), body.ArgTypes...) + } + if body.ReturnType != nil { + returnType := *body.ReturnType + u.PythonReturnType = &returnType + } + return nil +} func type2PlanType(typ types.Type) *plan.Type { return &plan.Type{Id: int32(typ.Oid), Width: typ.Width, Scale: typ.Scale, Charset: uint32(typ.Charset)} } diff --git a/pkg/sql/plan/function/udf_test.go b/pkg/sql/plan/function/udf_test.go new file mode 100644 index 0000000000000..1217fa5e59dd9 --- /dev/null +++ b/pkg/sql/plan/function/udf_test.go @@ -0,0 +1,56 @@ +// Copyright 2026 Matrix Origin +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package function + +import ( + "encoding/json" + "testing" + + "github.com/matrixorigin/matrixone/pkg/container/types" + "github.com/stretchr/testify/require" +) + +func TestPythonTypeContractPreservesDeclaredShape(t *testing.T) { + argument := types.New(types.T_decimal64, 18, 6) + returnType := types.New(types.T_varchar, 64, 0) + argumentDescriptor, err := NewPythonTypeDescriptor(argument) + require.NoError(t, err) + returnDescriptor, err := NewPythonTypeDescriptor(returnType) + require.NoError(t, err) + body, err := json.Marshal(PythonRoutineBody{ + ArgTypes: []PythonTypeDescriptor{argumentDescriptor}, + ReturnType: &returnDescriptor, + }) + require.NoError(t, err) + + routine := &Udf{Language: "python", Body: string(body), ArgsType: []types.Type{types.T_decimal64.ToType()}} + require.NoError(t, routine.LoadPythonTypeContract()) + require.Equal(t, argument, routine.GetArgsType()[0]) + require.Equal(t, returnType, routine.GetRetType()) + require.Equal(t, int32(6), routine.GetArgsPlanType()[0].Scale) + require.Equal(t, int32(64), routine.GetRetPlanType().Width) +} + +func TestPythonTypeContractKeepsLegacyFallback(t *testing.T) { + routine := &Udf{ + Language: "python", + Body: `{"handler":"legacy","source":"def legacy(ctx, x): return x"}`, + ArgsType: []types.Type{types.T_int32.ToType()}, + RetType: "int", + } + require.NoError(t, routine.LoadPythonTypeContract()) + require.Equal(t, types.T_int32.ToType(), routine.GetArgsType()[0]) + require.Equal(t, types.T_int32.ToType(), routine.GetRetType()) +} diff --git a/pkg/udf/python/worker/test_worker.py b/pkg/udf/python/worker/test_worker.py index aa26968e6bf02..6a8a31a9beeca 100644 --- a/pkg/udf/python/worker/test_worker.py +++ b/pkg/udf/python/worker/test_worker.py @@ -6,6 +6,7 @@ import pathlib import sys import unittest +import uuid import pyarrow as pa @@ -51,6 +52,23 @@ def test_zero_temporal_is_distinct_from_null(self): self.assertEqual(True, array.field("is_zero")[0].as_py()) self.assertEqual(datetime.date(1970, 1, 1), array.field("value")[0].as_py()) + def test_json_input_is_canonicalized_before_handler(self): + descriptor = {"type_id": worker.JSON, "offset_width": 32, "json_encoding": "canonical_text"} + array = pa.array(['{"b": [true, null, "中"], "a": 1}'], type=pa.string()) + self.assertEqual( + '{"b":[true,null,"中"],"a":1}', + worker._scalar_input(array, 0, descriptor), + ) + + def test_uuid_scalar_round_trip_keeps_uuid_object_until_array_encoding(self): + descriptor = {"type_id": worker.UUID, "offset_width": 0} + value = uuid.UUID("123e4567-e89b-12d3-a456-426614174000") + checked = worker._check_scalar(value, descriptor) + self.assertIsInstance(checked, uuid.UUID) + array = worker._output_array([checked, None], descriptor, 2) + self.assertEqual(value.bytes, array[0].as_py()) + self.assertIsNone(array[1].as_py()) + def test_scalar_value_domain_is_checked_before_arrow_encoding(self): string_descriptor = {"type_id": worker.VARCHAR, "width": 3, "offset_width": 32} with self.assertRaisesRegex(ValueError, "string exceeds"): diff --git a/pkg/udf/python/worker/worker.py b/pkg/udf/python/worker/worker.py index 9dd4b68e2c3ef..9c3a8d6fdfa3d 100644 --- a/pkg/udf/python/worker/worker.py +++ b/pkg/udf/python/worker/worker.py @@ -457,19 +457,28 @@ def _scalar_input(array: pa.Array, row: int, descriptor: Dict[str, Any]): if type_id == DATETIME: return SqlDatetime(zero, None if zero else child) return SqlTimestamp(zero, None if zero else child) + if type_id == JSON: + return _canonical_json_text(value) if type_id == UUID: return _uuid.UUID(bytes=bytes(value)) return value -def _check_json(value: str) -> str: +def _canonical_json_text(value: str) -> str: if not isinstance(value, str): - raise ValueError("TYPE_CONTRACT: JSON result must be canonical text") + raise ValueError("TYPE_CONTRACT: JSON value must be text") try: parsed = json.loads(value) except Exception as exc: - raise ValueError("TYPE_CONTRACT: invalid JSON result") from exc - canonical = json.dumps(parsed, ensure_ascii=False, separators=(",", ":")) + raise ValueError("TYPE_CONTRACT: invalid JSON input") from exc + return json.dumps(parsed, ensure_ascii=False, separators=(",", ":")) + + +def _check_json(value: str) -> str: + try: + canonical = _canonical_json_text(value) + except ValueError as exc: + raise ValueError("TYPE_CONTRACT: JSON result must be canonical text") from exc if canonical != value: raise ValueError("TYPE_CONTRACT: JSON result is not canonical text") return value @@ -508,7 +517,7 @@ def _check_scalar(value: Any, descriptor: Dict[str, Any]): return value if type_id == UUID: if not isinstance(value, _uuid.UUID): raise ValueError("TYPE_CONTRACT: expected uuid.UUID") - return value.bytes + return value if type_id == TIME: if not isinstance(value, _datetime.timedelta): raise ValueError("TYPE_CONTRACT: expected datetime.timedelta") micros = _timedelta_micros(value) diff --git a/test/distributed/cases/udf_python/README.md b/test/distributed/cases/udf_python/README.md index 8c717c7021153..2efd69b2cbbd4 100644 --- a/test/distributed/cases/udf_python/README.md +++ b/test/distributed/cases/udf_python/README.md @@ -26,3 +26,22 @@ cd /path/to/mo-tester The ordinary launch manifest starts one worker for the single CN. Compose BVT starts one isolated worker service per CN from the same pinned image, so adding a SQL case here does not require another CI workflow. + +The cases keep the data sets small and target contract boundaries: + +- `control_flow.sql` checks NULL delivery, handler-produced NULL, multiple + arguments, zero-argument scalar calls, CASE selection, and empty input. +- `numeric_boundaries.sql` checks signed and unsigned integer limits, floating + point values, booleans, decimal scale, and NULL. +- `value_types.sql` checks empty and Unicode text, binary values, canonical JSON, + UUID round trips, and NULL. +- `temporal_boundaries.sql` checks leap days, microseconds, TIME limits, SQL + zero-date values, and NULL. + +The shape follows established correctness coverage from +[DuckDB's scalar UDF tests](https://github.com/duckdb/duckdb-go/blob/main/scalar_udf_test.go) +(NULLs, multiple rows, multiple arguments, and errors) and +[Apache Arrow's extension type tests](https://github.com/apache/arrow/blob/main/python/pyarrow/tests/test_extension_type.py) +(null-aware arrays, fixed-size binary values, and type validation). The SQL +cases adapt those ideas to MatrixOne's Python Arrow boundary rather than +copying another project's implementation. diff --git a/test/distributed/cases/udf_python/control_flow.result b/test/distributed/cases/udf_python/control_flow.result new file mode 100644 index 0000000000000..c43e9bf1c0fc0 --- /dev/null +++ b/test/distributed/cases/udf_python/control_flow.result @@ -0,0 +1,51 @@ +drop database if exists udf_python_control_bvt; +create database udf_python_control_bvt; +use udf_python_control_bvt; +create function python_bvt_mark_null (x varchar(32)) returns varchar(64) language python as 'def python_bvt_mark_null(ctx, x): return "" if x is None else "<" + x + ">"' handler 'python_bvt_mark_null'; +create function python_bvt_add_one (x int) returns int language python as 'def python_bvt_add_one(ctx, x): return None if x is None else x + 1' handler 'python_bvt_add_one'; +create function python_bvt_add_pair (x int, y int) returns int language python as 'def python_bvt_add_pair(ctx, x, y): return None if x is None or y is None else x + y' handler 'python_bvt_add_pair'; +create function python_bvt_zero_arg () returns int language python as 'def python_bvt_zero_arg(ctx): return 7' handler 'python_bvt_zero_arg'; +create function python_bvt_must_not_run (x int) returns int language python as 'def python_bvt_must_not_run(ctx, x): raise RuntimeError("unselected Python branch was executed")' handler 'python_bvt_must_not_run'; +create function python_bvt_empty_input (x int) returns int language python as 'def python_bvt_empty_input(ctx, x): raise RuntimeError("empty input was executed")' handler 'python_bvt_empty_input'; +create table control_values (id int, label varchar(32), value int); +insert into control_values values +(1, 'empty', 1), +(2, '中😀', 2), +(3, null, 3); +select id, python_bvt_mark_null(label) as marked +from control_values order by id; +➤ id[4,32,0] ¦ marked[12,64,0] 𝄀 +1 ¦ 𝄀 +2 ¦ <中😀> 𝄀 +3 ¦ +select id, python_bvt_zero_arg() as constant +from control_values order by id; +➤ id[4,32,0] ¦ constant[4,32,0] 𝄀 +1 ¦ 7 𝄀 +2 ¦ 7 𝄀 +3 ¦ 7 +select id, python_bvt_add_pair(value, id) as pair_sum +from control_values order by id; +➤ id[4,32,0] ¦ pair_sum[4,32,0] 𝄀 +1 ¦ 2 𝄀 +2 ¦ 4 𝄀 +3 ¦ 6 +select id, +case when value < 0 then python_bvt_must_not_run(value) +else 99 end as guarded +from control_values order by id; +➤ id[4,32,0] ¦ guarded[-5,64,0] 𝄀 +1 ¦ 99 𝄀 +2 ¦ 99 𝄀 +3 ¦ 99 +select python_bvt_empty_input(value) as never_returned +from control_values where 1 = 0; +➤ never_returned[4,32,0] +drop function python_bvt_mark_null(varchar(32)); +drop function python_bvt_add_one(int); +drop function python_bvt_add_pair(int, int); +drop function python_bvt_zero_arg(); +drop function python_bvt_must_not_run(int); +drop function python_bvt_empty_input(int); +drop table control_values; +drop database udf_python_control_bvt; diff --git a/test/distributed/cases/udf_python/control_flow.sql b/test/distributed/cases/udf_python/control_flow.sql new file mode 100644 index 0000000000000..956e83e0988f3 --- /dev/null +++ b/test/distributed/cases/udf_python/control_flow.sql @@ -0,0 +1,49 @@ +-- Correctness boundaries for scalar invocation, NULL handling, empty input, +-- zero-argument calls, and CASE selection masks. +drop database if exists udf_python_control_bvt; +create database udf_python_control_bvt; +use udf_python_control_bvt; + +create function python_bvt_mark_null (x varchar(32)) returns varchar(64) language python as 'def python_bvt_mark_null(ctx, x): return "" if x is None else "<" + x + ">"' handler 'python_bvt_mark_null'; +create function python_bvt_add_one (x int) returns int language python as 'def python_bvt_add_one(ctx, x): return None if x is None else x + 1' handler 'python_bvt_add_one'; +create function python_bvt_add_pair (x int, y int) returns int language python as 'def python_bvt_add_pair(ctx, x, y): return None if x is None or y is None else x + y' handler 'python_bvt_add_pair'; +create function python_bvt_zero_arg () returns int language python as 'def python_bvt_zero_arg(ctx): return 7' handler 'python_bvt_zero_arg'; +create function python_bvt_must_not_run (x int) returns int language python as 'def python_bvt_must_not_run(ctx, x): raise RuntimeError("unselected Python branch was executed")' handler 'python_bvt_must_not_run'; +create function python_bvt_empty_input (x int) returns int language python as 'def python_bvt_empty_input(ctx, x): raise RuntimeError("empty input was executed")' handler 'python_bvt_empty_input'; + +create table control_values (id int, label varchar(32), value int); +insert into control_values values + (1, 'empty', 1), + (2, '中😀', 2), + (3, null, 3); + +-- The default policy passes NULL to the handler and preserves a handler NULL. +select id, python_bvt_mark_null(label) as marked +from control_values order by id; + +-- A zero-argument scalar still produces one result per input row. +select id, python_bvt_zero_arg() as constant +from control_values order by id; + +-- Multiple arguments preserve row order and NULLs independently. +select id, python_bvt_add_pair(value, id) as pair_sum +from control_values order by id; + +-- A CASE branch with no selected rows must not invoke its Python handler. +select id, + case when value < 0 then python_bvt_must_not_run(value) + else 99 end as guarded +from control_values order by id; + +-- An empty input batch must finish without opening a worker invocation. +select python_bvt_empty_input(value) as never_returned +from control_values where 1 = 0; + +drop function python_bvt_mark_null(varchar(32)); +drop function python_bvt_add_one(int); +drop function python_bvt_add_pair(int, int); +drop function python_bvt_zero_arg(); +drop function python_bvt_must_not_run(int); +drop function python_bvt_empty_input(int); +drop table control_values; +drop database udf_python_control_bvt; diff --git a/test/distributed/cases/udf_python/numeric_boundaries.result b/test/distributed/cases/udf_python/numeric_boundaries.result new file mode 100644 index 0000000000000..27cddaf47fda9 --- /dev/null +++ b/test/distributed/cases/udf_python/numeric_boundaries.result @@ -0,0 +1,70 @@ +drop database if exists udf_python_numeric_bvt; +create database udf_python_numeric_bvt; +use udf_python_numeric_bvt; +create function python_bvt_tinyint (x tinyint) returns tinyint language python as 'def python_bvt_tinyint(ctx, x): return x' handler 'python_bvt_tinyint'; +create function python_bvt_smallint (x smallint) returns smallint language python as 'def python_bvt_smallint(ctx, x): return x' handler 'python_bvt_smallint'; +create function python_bvt_int (x int) returns int language python as 'def python_bvt_int(ctx, x): return x' handler 'python_bvt_int'; +create function python_bvt_bigint (x bigint) returns bigint language python as 'def python_bvt_bigint(ctx, x): return x' handler 'python_bvt_bigint'; +create function python_bvt_tinyuint (x tinyint unsigned) returns tinyint unsigned language python as 'def python_bvt_tinyuint(ctx, x): return x' handler 'python_bvt_tinyuint'; +create function python_bvt_smalluint (x smallint unsigned) returns smallint unsigned language python as 'def python_bvt_smalluint(ctx, x): return x' handler 'python_bvt_smalluint'; +create function python_bvt_uint (x int unsigned) returns int unsigned language python as 'def python_bvt_uint(ctx, x): return x' handler 'python_bvt_uint'; +create function python_bvt_biguint (x bigint unsigned) returns bigint unsigned language python as 'def python_bvt_biguint(ctx, x): return x' handler 'python_bvt_biguint'; +create function python_bvt_float (x float) returns float language python as 'def python_bvt_float(ctx, x): return x' handler 'python_bvt_float'; +create function python_bvt_double (x double) returns double language python as 'def python_bvt_double(ctx, x): return x' handler 'python_bvt_double'; +create function python_bvt_bool (x bool) returns bool language python as 'def python_bvt_bool(ctx, x): return x' handler 'python_bvt_bool'; +create function python_bvt_decimal64 (x decimal(18,6)) returns decimal(18,6) language python as 'def python_bvt_decimal64(ctx, x): return x' handler 'python_bvt_decimal64'; +create function python_bvt_decimal128 (x decimal(38,10)) returns decimal(38,10) language python as 'def python_bvt_decimal128(ctx, x): return x' handler 'python_bvt_decimal128'; +create table numeric_values ( +tiny_v tinyint, +small_v smallint, +int_v int, +big_v bigint, +tiny_u tinyint unsigned, +small_u smallint unsigned, +int_u int unsigned, +big_u bigint unsigned, +float_v float, +double_v double, +bool_v bool, +decimal64_v decimal(18,6), +decimal128_v decimal(38,10) +); +insert into numeric_values values +(-128, -32768, -2147483648, -9223372036854775808, +0, 0, 0, 0, -0.0, -0.0, false, -0.000001, -9999999999999999999999999999.9999999999), +(127, 32767, 2147483647, 9223372036854775807, +255, 65535, 4294967295, 18446744073709551615, 3.25, 1.0, true, 999999999999.999999, 9999999999999999999999999999.9999999999), +(null, null, null, null, null, null, null, null, null, null, null, null, null); +select python_bvt_tinyint(tiny_v) as tiny_v, +python_bvt_smallint(small_v) as small_v, +python_bvt_int(int_v) as int_v, +python_bvt_bigint(big_v) as big_v, +python_bvt_tinyuint(tiny_u) as tiny_u, +python_bvt_smalluint(small_u) as small_u, +python_bvt_uint(int_u) as int_u, +python_bvt_biguint(big_u) as big_u, +python_bvt_float(float_v) as float_v, +python_bvt_double(double_v) as double_v, +python_bvt_bool(bool_v) as bool_v, +python_bvt_decimal64(decimal64_v) as decimal64_v, +python_bvt_decimal128(decimal128_v) as decimal128_v +from numeric_values order by big_v is null, big_v; +➤ tiny_v[-6,8,0] ¦ small_v[5,16,0] ¦ int_v[4,32,0] ¦ big_v[-5,64,0] ¦ tiny_u[-6,8,0] ¦ small_u[5,16,0] ¦ int_u[4,32,0] ¦ big_u[-5,64,0] ¦ float_v[7,23,31] ¦ double_v[8,53,31] ¦ bool_v[-7,1,0] ¦ decimal64_v[3,18,6] ¦ decimal128_v[3,38,10] 𝄀 +-128 ¦ -32768 ¦ -2147483648 ¦ -9223372036854775808 ¦ 0 ¦ 0 ¦ 0 ¦ 0 ¦ -0.0 ¦ -0.0 ¦ 0 ¦ -0.000001 ¦ -9999999999999999999999999999.9999999999 𝄀 +127 ¦ 32767 ¦ 2147483647 ¦ 9223372036854775807 ¦ 255 ¦ 65535 ¦ 4294967295 ¦ 18446744073709551615 ¦ 3.25 ¦ 1.0 ¦ 1 ¦ 999999999999.999999 ¦ 9999999999999999999999999999.9999999999 𝄀 +null ¦ null ¦ null ¦ null ¦ null ¦ null ¦ null ¦ null ¦ null ¦ null ¦ null ¦ null ¦ null +drop function python_bvt_tinyint(tinyint); +drop function python_bvt_smallint(smallint); +drop function python_bvt_int(int); +drop function python_bvt_bigint(bigint); +drop function python_bvt_tinyuint(tinyint unsigned); +drop function python_bvt_smalluint(smallint unsigned); +drop function python_bvt_uint(int unsigned); +drop function python_bvt_biguint(bigint unsigned); +drop function python_bvt_float(float); +drop function python_bvt_double(double); +drop function python_bvt_bool(bool); +drop function python_bvt_decimal64(decimal(18,6)); +drop function python_bvt_decimal128(decimal(38,10)); +drop table numeric_values; +drop database udf_python_numeric_bvt; diff --git a/test/distributed/cases/udf_python/numeric_boundaries.sql b/test/distributed/cases/udf_python/numeric_boundaries.sql new file mode 100644 index 0000000000000..0c00e1f813be5 --- /dev/null +++ b/test/distributed/cases/udf_python/numeric_boundaries.sql @@ -0,0 +1,72 @@ +-- Small, exact boundary values for every scalar numeric family supported by +-- the Python Arrow adapter. +drop database if exists udf_python_numeric_bvt; +create database udf_python_numeric_bvt; +use udf_python_numeric_bvt; + +create function python_bvt_tinyint (x tinyint) returns tinyint language python as 'def python_bvt_tinyint(ctx, x): return x' handler 'python_bvt_tinyint'; +create function python_bvt_smallint (x smallint) returns smallint language python as 'def python_bvt_smallint(ctx, x): return x' handler 'python_bvt_smallint'; +create function python_bvt_int (x int) returns int language python as 'def python_bvt_int(ctx, x): return x' handler 'python_bvt_int'; +create function python_bvt_bigint (x bigint) returns bigint language python as 'def python_bvt_bigint(ctx, x): return x' handler 'python_bvt_bigint'; +create function python_bvt_tinyuint (x tinyint unsigned) returns tinyint unsigned language python as 'def python_bvt_tinyuint(ctx, x): return x' handler 'python_bvt_tinyuint'; +create function python_bvt_smalluint (x smallint unsigned) returns smallint unsigned language python as 'def python_bvt_smalluint(ctx, x): return x' handler 'python_bvt_smalluint'; +create function python_bvt_uint (x int unsigned) returns int unsigned language python as 'def python_bvt_uint(ctx, x): return x' handler 'python_bvt_uint'; +create function python_bvt_biguint (x bigint unsigned) returns bigint unsigned language python as 'def python_bvt_biguint(ctx, x): return x' handler 'python_bvt_biguint'; +create function python_bvt_float (x float) returns float language python as 'def python_bvt_float(ctx, x): return x' handler 'python_bvt_float'; +create function python_bvt_double (x double) returns double language python as 'def python_bvt_double(ctx, x): return x' handler 'python_bvt_double'; +create function python_bvt_bool (x bool) returns bool language python as 'def python_bvt_bool(ctx, x): return x' handler 'python_bvt_bool'; +create function python_bvt_decimal64 (x decimal(18,6)) returns decimal(18,6) language python as 'def python_bvt_decimal64(ctx, x): return x' handler 'python_bvt_decimal64'; +create function python_bvt_decimal128 (x decimal(38,10)) returns decimal(38,10) language python as 'def python_bvt_decimal128(ctx, x): return x' handler 'python_bvt_decimal128'; + +create table numeric_values ( + tiny_v tinyint, + small_v smallint, + int_v int, + big_v bigint, + tiny_u tinyint unsigned, + small_u smallint unsigned, + int_u int unsigned, + big_u bigint unsigned, + float_v float, + double_v double, + bool_v bool, + decimal64_v decimal(18,6), + decimal128_v decimal(38,10) +); +insert into numeric_values values + (-128, -32768, -2147483648, -9223372036854775808, + 0, 0, 0, 0, -0.0, -0.0, false, -0.000001, -9999999999999999999999999999.9999999999), + (127, 32767, 2147483647, 9223372036854775807, + 255, 65535, 4294967295, 18446744073709551615, 3.25, 1.0, true, 999999999999.999999, 9999999999999999999999999999.9999999999), + (null, null, null, null, null, null, null, null, null, null, null, null, null); + +select python_bvt_tinyint(tiny_v) as tiny_v, + python_bvt_smallint(small_v) as small_v, + python_bvt_int(int_v) as int_v, + python_bvt_bigint(big_v) as big_v, + python_bvt_tinyuint(tiny_u) as tiny_u, + python_bvt_smalluint(small_u) as small_u, + python_bvt_uint(int_u) as int_u, + python_bvt_biguint(big_u) as big_u, + python_bvt_float(float_v) as float_v, + python_bvt_double(double_v) as double_v, + python_bvt_bool(bool_v) as bool_v, + python_bvt_decimal64(decimal64_v) as decimal64_v, + python_bvt_decimal128(decimal128_v) as decimal128_v +from numeric_values order by big_v is null, big_v; + +drop function python_bvt_tinyint(tinyint); +drop function python_bvt_smallint(smallint); +drop function python_bvt_int(int); +drop function python_bvt_bigint(bigint); +drop function python_bvt_tinyuint(tinyint unsigned); +drop function python_bvt_smalluint(smallint unsigned); +drop function python_bvt_uint(int unsigned); +drop function python_bvt_biguint(bigint unsigned); +drop function python_bvt_float(float); +drop function python_bvt_double(double); +drop function python_bvt_bool(bool); +drop function python_bvt_decimal64(decimal(18,6)); +drop function python_bvt_decimal128(decimal(38,10)); +drop table numeric_values; +drop database udf_python_numeric_bvt; diff --git a/test/distributed/cases/udf_python/temporal_boundaries.result b/test/distributed/cases/udf_python/temporal_boundaries.result new file mode 100644 index 0000000000000..1c90a1f65d00a --- /dev/null +++ b/test/distributed/cases/udf_python/temporal_boundaries.result @@ -0,0 +1,41 @@ +drop database if exists udf_python_temporal_bvt; +create database udf_python_temporal_bvt; +use udf_python_temporal_bvt; +create function python_bvt_date (x date) returns date language python as 'def python_bvt_date(ctx, x): return x' handler 'python_bvt_date'; +create function python_bvt_datetime (x datetime(6)) returns datetime(6) language python as 'def python_bvt_datetime(ctx, x): return x' handler 'python_bvt_datetime'; +create function python_bvt_timestamp (x timestamp(6)) returns timestamp(6) language python as 'def python_bvt_timestamp(ctx, x): return x' handler 'python_bvt_timestamp'; +create function python_bvt_time (x time(6)) returns time(6) language python as 'def python_bvt_time(ctx, x): return x' handler 'python_bvt_time'; +create table temporal_values ( +id int, +date_v date, +datetime_v datetime(6), +timestamp_v timestamp(6), +time_v time(6) +); +insert into temporal_values values +(1, '2024-02-29', '2024-02-29 23:59:59.123456', '2024-02-29 23:59:59.123456', '-838:59:59'), +(2, '2024-03-01', '2024-03-01 00:00:00.000000', '2024-03-01 00:00:00.000000', '838:59:59'), +(3, null, null, null, null); +select id, python_bvt_date(date_v) as date_v, +python_bvt_datetime(datetime_v) as datetime_v, +python_bvt_timestamp(timestamp_v) as timestamp_v, +python_bvt_time(time_v) as time_v +from temporal_values order by id; +➤ id[4,32,0] ¦ date_v[91,10,0] ¦ datetime_v[93,26,0] ¦ timestamp_v[93,26,0] ¦ time_v[92,17,0] 𝄀 +1 ¦ 2024-02-29 ¦ 2024-02-29 23:59:59.123456000 ¦ 2024-02-29 23:59:59.123456000 ¦ -838:59:59 𝄀 +2 ¦ 2024-03-01 ¦ 2024-03-01 00:00:00 ¦ 2024-03-01 00:00:00 ¦ 838:59:59 𝄀 +3 ¦ null ¦ null ¦ null ¦ null +set @udf_python_saved_sql_mode = @@sql_mode; +set session sql_mode = ''; +select python_bvt_date(cast('0000-00-00' as date)) as zero_date, +python_bvt_datetime(cast('0000-00-00 00:00:00' as datetime(6))) as zero_datetime, +python_bvt_timestamp(cast('0000-00-00 00:00:00' as timestamp(6))) as zero_timestamp; +➤ zero_date[91,10,0] ¦ zero_datetime[93,26,0] ¦ zero_timestamp[93,26,0] 𝄀 +0000-00-00 ¦ 0000-00-00 00:00:00 ¦ 0000-00-00 00:00:00 +set session sql_mode = @udf_python_saved_sql_mode; +drop function python_bvt_date(date); +drop function python_bvt_datetime(datetime(6)); +drop function python_bvt_timestamp(timestamp(6)); +drop function python_bvt_time(time(6)); +drop table temporal_values; +drop database udf_python_temporal_bvt; diff --git a/test/distributed/cases/udf_python/temporal_boundaries.sql b/test/distributed/cases/udf_python/temporal_boundaries.sql new file mode 100644 index 0000000000000..502107777c7bb --- /dev/null +++ b/test/distributed/cases/udf_python/temporal_boundaries.sql @@ -0,0 +1,43 @@ +-- SQL temporal values, including the explicit zero-date representation that +-- cannot be represented by Python's datetime.date/datetime classes. +drop database if exists udf_python_temporal_bvt; +create database udf_python_temporal_bvt; +use udf_python_temporal_bvt; + +create function python_bvt_date (x date) returns date language python as 'def python_bvt_date(ctx, x): return x' handler 'python_bvt_date'; +create function python_bvt_datetime (x datetime(6)) returns datetime(6) language python as 'def python_bvt_datetime(ctx, x): return x' handler 'python_bvt_datetime'; +create function python_bvt_timestamp (x timestamp(6)) returns timestamp(6) language python as 'def python_bvt_timestamp(ctx, x): return x' handler 'python_bvt_timestamp'; +create function python_bvt_time (x time(6)) returns time(6) language python as 'def python_bvt_time(ctx, x): return x' handler 'python_bvt_time'; + +create table temporal_values ( + id int, + date_v date, + datetime_v datetime(6), + timestamp_v timestamp(6), + time_v time(6) +); +insert into temporal_values values + (1, '2024-02-29', '2024-02-29 23:59:59.123456', '2024-02-29 23:59:59.123456', '-838:59:59'), + (2, '2024-03-01', '2024-03-01 00:00:00.000000', '2024-03-01 00:00:00.000000', '838:59:59'), + (3, null, null, null, null); + +select id, python_bvt_date(date_v) as date_v, + python_bvt_datetime(datetime_v) as datetime_v, + python_bvt_timestamp(timestamp_v) as timestamp_v, + python_bvt_time(time_v) as time_v +from temporal_values order by id; + +-- Zero temporal values are valid values and must stay distinct from SQL NULL. +set @udf_python_saved_sql_mode = @@sql_mode; +set session sql_mode = ''; +select python_bvt_date(cast('0000-00-00' as date)) as zero_date, + python_bvt_datetime(cast('0000-00-00 00:00:00' as datetime(6))) as zero_datetime, + python_bvt_timestamp(cast('0000-00-00 00:00:00' as timestamp(6))) as zero_timestamp; +set session sql_mode = @udf_python_saved_sql_mode; + +drop function python_bvt_date(date); +drop function python_bvt_datetime(datetime(6)); +drop function python_bvt_timestamp(timestamp(6)); +drop function python_bvt_time(time(6)); +drop table temporal_values; +drop database udf_python_temporal_bvt; diff --git a/test/distributed/cases/udf_python/value_types.result b/test/distributed/cases/udf_python/value_types.result new file mode 100644 index 0000000000000..26b59820c098e --- /dev/null +++ b/test/distributed/cases/udf_python/value_types.result @@ -0,0 +1,72 @@ +drop database if exists udf_python_values_bvt; +create database udf_python_values_bvt; +use udf_python_values_bvt; +create function python_bvt_varchar (x varchar(64)) returns varchar(64) language python as 'def python_bvt_varchar(ctx, x): return x' handler 'python_bvt_varchar'; +create function python_bvt_text (x text) returns text language python as 'def python_bvt_text(ctx, x): return x' handler 'python_bvt_text'; +create function python_bvt_varbinary (x varbinary(8)) returns varbinary(8) language python as 'def python_bvt_varbinary(ctx, x): return x' handler 'python_bvt_varbinary'; +create function python_bvt_blob (x blob) returns blob language python as 'def python_bvt_blob(ctx, x): return x' handler 'python_bvt_blob'; +create function python_bvt_json (x json) returns json language python as 'def python_bvt_json(ctx, x): return None if x is None else __import__("json").dumps(__import__("json").loads(x), ensure_ascii=False, separators=(",", ":"))' handler 'python_bvt_json'; +create function python_bvt_uuid (x uuid) returns uuid language python as 'def python_bvt_uuid(ctx, x): return x' handler 'python_bvt_uuid'; +create table text_values (id int, value varchar(64), long_value text); +insert into text_values values +(1, '', repeat('a', 64)), +(2, '中😀', '数据库'), +(3, repeat('v', 64), repeat('b', 64)), +(4, null, null); +select id, python_bvt_varchar(value) as value, +python_bvt_text(long_value) as long_value +from text_values order by id; +➤ id[4,32,0] ¦ value[12,64,0] ¦ long_value[-1,16383,0] 𝄀 +1 ¦ ¦ aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa 𝄀 +2 ¦ 中😀 ¦ 数据库 𝄀 +3 ¦ vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv ¦ bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb 𝄀 +4 ¦ null ¦ null +create table binary_values (id int, var_value varbinary(8), blob_value blob); +insert into binary_values values +(1, unhex(''), unhex('')), +(2, unhex('00010203ff'), unhex('00ff10')), +(3, unhex('00010203ff060708'), unhex('00010203ff060708')), +(4, null, null); +select id, hex(python_bvt_varbinary(var_value)) as var_value, +hex(python_bvt_blob(blob_value)) as blob_value +from binary_values order by id; +➤ id[4,32,0] ¦ var_value[12,16,0] ¦ blob_value[-1,16383,0] 𝄀 +1 ¦ ¦ 𝄀 +2 ¦ 00010203FF ¦ 00FF10 𝄀 +3 ¦ 00010203FF060708 ¦ 00010203FF060708 𝄀 +4 ¦ null ¦ null +create table json_values (id int, value json); +insert into json_values values +(1, '{}'), +(2, '{"a":1,"b":[true,null,"中"]}'), +(3, '1'), +(4, null); +select id, python_bvt_json(value) as value +from json_values order by id; +➤ id[4,32,0] ¦ value[-1,2147483647,0] 𝄀 +1 ¦ {} 𝄀 +2 ¦ {"a": 1, "b": [true, null, "中"]} 𝄀 +3 ¦ 1 𝄀 +4 ¦ null +create table uuid_values (id int, value uuid); +insert into uuid_values values +(1, cast('00000000-0000-0000-0000-000000000000' as uuid)), +(2, cast('123e4567-e89b-12d3-a456-426614174000' as uuid)), +(3, null); +select id, python_bvt_uuid(value) as value +from uuid_values order by id; +➤ id[4,32,0] ¦ value[12,-1,0] 𝄀 +1 ¦ 00000000-0000-0000-0000-000000000000 𝄀 +2 ¦ 123e4567-e89b-12d3-a456-426614174000 𝄀 +3 ¦ null +drop function python_bvt_varchar(varchar(64)); +drop function python_bvt_text(text); +drop function python_bvt_varbinary(varbinary(8)); +drop function python_bvt_blob(blob); +drop function python_bvt_json(json); +drop function python_bvt_uuid(uuid); +drop table text_values; +drop table binary_values; +drop table json_values; +drop table uuid_values; +drop database udf_python_values_bvt; diff --git a/test/distributed/cases/udf_python/value_types.sql b/test/distributed/cases/udf_python/value_types.sql new file mode 100644 index 0000000000000..a9497376d4fb0 --- /dev/null +++ b/test/distributed/cases/udf_python/value_types.sql @@ -0,0 +1,64 @@ +-- Boundary values for variable-width text, binary payloads, JSON, and UUID. +drop database if exists udf_python_values_bvt; +create database udf_python_values_bvt; +use udf_python_values_bvt; + +create function python_bvt_varchar (x varchar(64)) returns varchar(64) language python as 'def python_bvt_varchar(ctx, x): return x' handler 'python_bvt_varchar'; +create function python_bvt_text (x text) returns text language python as 'def python_bvt_text(ctx, x): return x' handler 'python_bvt_text'; +create function python_bvt_varbinary (x varbinary(8)) returns varbinary(8) language python as 'def python_bvt_varbinary(ctx, x): return x' handler 'python_bvt_varbinary'; +create function python_bvt_blob (x blob) returns blob language python as 'def python_bvt_blob(ctx, x): return x' handler 'python_bvt_blob'; +create function python_bvt_json (x json) returns json language python as 'def python_bvt_json(ctx, x): return None if x is None else __import__("json").dumps(__import__("json").loads(x), ensure_ascii=False, separators=(",", ":"))' handler 'python_bvt_json'; +create function python_bvt_uuid (x uuid) returns uuid language python as 'def python_bvt_uuid(ctx, x): return x' handler 'python_bvt_uuid'; + +create table text_values (id int, value varchar(64), long_value text); +insert into text_values values + (1, '', repeat('a', 64)), + (2, '中😀', '数据库'), + (3, repeat('v', 64), repeat('b', 64)), + (4, null, null); + +select id, python_bvt_varchar(value) as value, + python_bvt_text(long_value) as long_value +from text_values order by id; + +create table binary_values (id int, var_value varbinary(8), blob_value blob); +insert into binary_values values + (1, unhex(''), unhex('')), + (2, unhex('00010203ff'), unhex('00ff10')), + (3, unhex('00010203ff060708'), unhex('00010203ff060708')), + (4, null, null); + +select id, hex(python_bvt_varbinary(var_value)) as var_value, + hex(python_bvt_blob(blob_value)) as blob_value +from binary_values order by id; + +create table json_values (id int, value json); +insert into json_values values + (1, '{}'), + (2, '{"a":1,"b":[true,null,"中"]}'), + (3, '1'), + (4, null); + +select id, python_bvt_json(value) as value +from json_values order by id; + +create table uuid_values (id int, value uuid); +insert into uuid_values values + (1, cast('00000000-0000-0000-0000-000000000000' as uuid)), + (2, cast('123e4567-e89b-12d3-a456-426614174000' as uuid)), + (3, null); + +select id, python_bvt_uuid(value) as value +from uuid_values order by id; + +drop function python_bvt_varchar(varchar(64)); +drop function python_bvt_text(text); +drop function python_bvt_varbinary(varbinary(8)); +drop function python_bvt_blob(blob); +drop function python_bvt_json(json); +drop function python_bvt_uuid(uuid); +drop table text_values; +drop table binary_values; +drop table json_values; +drop table uuid_values; +drop database udf_python_values_bvt; From 693d0902fdae0925a397cd97b8458d9de8965e9b Mon Sep 17 00:00:00 2001 From: iamlinjunhong <1030420200@qq.com> Date: Wed, 9 Sep 2026 21:04:30 +0800 Subject: [PATCH 011/202] test(udf): cover null multi-argument calls --- test/distributed/cases/udf_python/control_flow.result | 4 ++-- test/distributed/cases/udf_python/control_flow.sql | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/test/distributed/cases/udf_python/control_flow.result b/test/distributed/cases/udf_python/control_flow.result index c43e9bf1c0fc0..1c55957986b2c 100644 --- a/test/distributed/cases/udf_python/control_flow.result +++ b/test/distributed/cases/udf_python/control_flow.result @@ -24,12 +24,12 @@ from control_values order by id; 1 ¦ 7 𝄀 2 ¦ 7 𝄀 3 ¦ 7 -select id, python_bvt_add_pair(value, id) as pair_sum +select id, python_bvt_add_pair(value, case when id = 3 then null else id end) as pair_sum from control_values order by id; ➤ id[4,32,0] ¦ pair_sum[4,32,0] 𝄀 1 ¦ 2 𝄀 2 ¦ 4 𝄀 -3 ¦ 6 +3 ¦ null select id, case when value < 0 then python_bvt_must_not_run(value) else 99 end as guarded diff --git a/test/distributed/cases/udf_python/control_flow.sql b/test/distributed/cases/udf_python/control_flow.sql index 956e83e0988f3..c60cb08619d8a 100644 --- a/test/distributed/cases/udf_python/control_flow.sql +++ b/test/distributed/cases/udf_python/control_flow.sql @@ -26,7 +26,7 @@ select id, python_bvt_zero_arg() as constant from control_values order by id; -- Multiple arguments preserve row order and NULLs independently. -select id, python_bvt_add_pair(value, id) as pair_sum +select id, python_bvt_add_pair(value, case when id = 3 then null else id end) as pair_sum from control_values order by id; -- A CASE branch with no selected rows must not invoke its Python handler. From e72ae50a167b1a649a553e08b652830d3d9ffd3a Mon Sep 17 00:00:00 2001 From: iamlinjunhong <1030420200@qq.com> Date: Wed, 9 Sep 2026 22:01:39 +0800 Subject: [PATCH 012/202] fix(udf): close Python execution and type contracts --- etc/launch-tae-compose/config/cn-0.toml | 2 + etc/launch-tae-compose/config/cn-1.toml | 2 + etc/launch-with-python-udf-worker/cn.toml | 2 + etc/launch/cn.toml | 1 + etc/launch/launch.toml | 4 - etc/v1/launch-with-python-udf-worker/cn.toml | 2 + optools/run_bvt.sh | 9 +- pkg/cnservice/server.go | 2 +- pkg/sql/plan/function/list_builtIn.go | 4 + pkg/sql/plan/function/python_udf.go | 16 +- pkg/sql/plan/function/udf_test.go | 25 ++ pkg/udf/python/arrow.go | 55 ++- pkg/udf/python/arrow_test.go | 44 +++ pkg/udf/python/config.go | 19 +- pkg/udf/python/config_test.go | 25 ++ pkg/udf/python/gateway.go | 3 +- pkg/udf/python/worker/test_worker.py | 65 ++++ pkg/udf/python/worker/worker.py | 323 +++++++++++++++--- test/distributed/cases/udf_python/README.md | 10 +- .../cases/udf_python/numeric_boundaries.sql | 6 + 20 files changed, 555 insertions(+), 64 deletions(-) create mode 100644 pkg/udf/python/config_test.go diff --git a/etc/launch-tae-compose/config/cn-0.toml b/etc/launch-tae-compose/config/cn-0.toml index 662553a5a8806..d75de7326e2f2 100644 --- a/etc/launch-tae-compose/config/cn-0.toml +++ b/etc/launch-tae-compose/config/cn-0.toml @@ -68,4 +68,6 @@ enable-dml = true enable-maintenance = true [cn.python-udf-client] +enabled = true +allow-unisolated = true server-address = "python-udf-worker-cn0:50051" diff --git a/etc/launch-tae-compose/config/cn-1.toml b/etc/launch-tae-compose/config/cn-1.toml index 49b1cad684d43..479e8784e4c1d 100644 --- a/etc/launch-tae-compose/config/cn-1.toml +++ b/etc/launch-tae-compose/config/cn-1.toml @@ -68,4 +68,6 @@ enable-dml = true enable-maintenance = true [cn.python-udf-client] +enabled = true +allow-unisolated = true server-address = "python-udf-worker-cn1:50051" diff --git a/etc/launch-with-python-udf-worker/cn.toml b/etc/launch-with-python-udf-worker/cn.toml index 424dc2b13c7c8..cf5b576cbc864 100644 --- a/etc/launch-with-python-udf-worker/cn.toml +++ b/etc/launch-with-python-udf-worker/cn.toml @@ -9,4 +9,6 @@ uuid = "dd1dccb4-4d3c-41f8-b482-5251dc7a41bf" port-base = 18000 [cn.python-udf-client] +enabled = true +allow-unisolated = true server-address = "127.0.0.1:50051" diff --git a/etc/launch/cn.toml b/etc/launch/cn.toml index 8c315e594bb32..6348ab7e7361d 100644 --- a/etc/launch/cn.toml +++ b/etc/launch/cn.toml @@ -51,4 +51,5 @@ enable-dml = true enable-maintenance = true [cn.python-udf-client] +enabled = false server-address = "127.0.0.1:50051" diff --git a/etc/launch/launch.toml b/etc/launch/launch.toml index 19f97c824f70e..08b71744ce4e2 100644 --- a/etc/launch/launch.toml +++ b/etc/launch/launch.toml @@ -9,7 +9,3 @@ tnservices = [ cnservices = [ "./etc/launch/cn.toml" ] - -python-udf-services = [ - "./etc/launch-with-python-udf-worker/python-udf-worker.toml" -] diff --git a/etc/v1/launch-with-python-udf-worker/cn.toml b/etc/v1/launch-with-python-udf-worker/cn.toml index 424dc2b13c7c8..cf5b576cbc864 100644 --- a/etc/v1/launch-with-python-udf-worker/cn.toml +++ b/etc/v1/launch-with-python-udf-worker/cn.toml @@ -9,4 +9,6 @@ uuid = "dd1dccb4-4d3c-41f8-b482-5251dc7a41bf" port-base = 18000 [cn.python-udf-client] +enabled = true +allow-unisolated = true server-address = "127.0.0.1:50051" diff --git a/optools/run_bvt.sh b/optools/run_bvt.sh index 5d53fb370bda5..0929047397241 100755 --- a/optools/run_bvt.sh +++ b/optools/run_bvt.sh @@ -22,7 +22,14 @@ PROXY=${3:-} function launch_mo() { cd $MO_WORKSPACE - ./mo-service -debug-http=:12345 -launch ./etc/$LAUNCH/launch.toml $PROXY &>mo-service.log & + # Ordinary launch BVT remains the single CI entry point, but its Python + # cases opt into the worker-enabled manifest explicitly. The generic + # etc/launch manifest stays safe for users who start mo-service directly. + local launch_config=$LAUNCH + if [[ "$LAUNCH" == "launch" ]]; then + launch_config=launch-with-python-udf-worker + fi + ./mo-service -debug-http=:12345 -launch ./etc/$launch_config/launch.toml $PROXY &>mo-service.log & } # this will wait mo all system init completed diff --git a/pkg/cnservice/server.go b/pkg/cnservice/server.go index fd4d7ceac89b9..fd04c9f2a77ce 100644 --- a/pkg/cnservice/server.go +++ b/pkg/cnservice/server.go @@ -242,7 +242,7 @@ func NewService( // init UdfService var udfServices []udf.Runtime // add python client to handle python udf - if srv.cfg.PythonUdfClient.ServerAddress != "" { + if srv.cfg.PythonUdfClient.Enabled { var pc *python.Gateway pc, err = python.NewGateway(srv.cfg.PythonUdfClient) if err != nil { diff --git a/pkg/sql/plan/function/list_builtIn.go b/pkg/sql/plan/function/list_builtIn.go index aff09b7cfdffe..e16d5d3b417c5 100644 --- a/pkg/sql/plan/function/list_builtIn.go +++ b/pkg/sql/plan/function/list_builtIn.go @@ -16236,6 +16236,10 @@ var supportedOthersBuiltIns = []FuncNew{ { overloadId: 0, retType: pythonUdfRetType, + // A Python routine is user code. It may observe invocation + // context or maintain process-local state, so planning must never + // execute it once and broadcast the result to all rows. + volatile: true, newOp: func() executeLogicOfOverload { return runPythonUdf }, diff --git a/pkg/sql/plan/function/python_udf.go b/pkg/sql/plan/function/python_udf.go index 5531541f2850a..3b27d6f3e5ddb 100644 --- a/pkg/sql/plan/function/python_udf.go +++ b/pkg/sql/plan/function/python_udf.go @@ -42,8 +42,15 @@ func checkPythonUdf(overloads []overload, inputs []types.Type) checkResult { requiredArgs := inputs[size+1 : 2*size+1] needCast := false for i := 0; i < size; i++ { - if receivedArgs[i].Oid != requiredArgs[i].Oid { + if !pythonTypesEqual(receivedArgs[i], requiredArgs[i]) { canCast, _ := fixedImplicitTypeCast(receivedArgs[i], requiredArgs[i].Oid) + if receivedArgs[i].Oid == requiredArgs[i].Oid { + // SQL's cast executor is also the type normalizer for Python's + // frozen descriptor. In particular, DECIMAL scale and precision + // must be normalized before Arrow encoding; comparing only OID + // would reinterpret the same coefficient at the wrong scale. + canCast = true + } if !canCast { return newCheckResultWithFailure(failedFunctionParametersWrong) } @@ -62,6 +69,13 @@ func checkPythonUdf(overloads []overload, inputs []types.Type) checkResult { return newCheckResultWithSuccess(0) } +func pythonTypesEqual(left, right types.Type) bool { + return left.Oid == right.Oid && + left.Width == right.Width && + left.Scale == right.Scale && + left.Charset == right.Charset +} + // param parameters is same with param inputs in function checkPythonUdf func pythonUdfRetType(parameters []types.Type) types.Type { return parameters[len(parameters)-1] diff --git a/pkg/sql/plan/function/udf_test.go b/pkg/sql/plan/function/udf_test.go index 1217fa5e59dd9..2a6ee085556c6 100644 --- a/pkg/sql/plan/function/udf_test.go +++ b/pkg/sql/plan/function/udf_test.go @@ -15,6 +15,7 @@ package function import ( + "context" "encoding/json" "testing" @@ -54,3 +55,27 @@ func TestPythonTypeContractKeepsLegacyFallback(t *testing.T) { require.Equal(t, types.T_int32.ToType(), routine.GetArgsType()[0]) require.Equal(t, types.T_int32.ToType(), routine.GetRetType()) } + +func TestPythonBindingNormalizesDecimalMetadata(t *testing.T) { + received := types.New(types.T_decimal64, 18, 2) + required := types.New(types.T_decimal64, 18, 6) + inputs := []types.Type{ + types.T_text.ToType(), + received, + required, + types.T_decimal64.ToType(), + } + result := checkPythonUdf(nil, inputs) + require.Equal(t, succeedWithCast, result.status) + require.Equal(t, required, result.finalType[1]) +} + +func TestPythonRoutineIsVolatile(t *testing.T) { + resolved, err := GetFunctionByName(context.Background(), "python_user_defined_function", []types.Type{ + types.T_text.ToType(), + types.T_text.ToType(), + }) + require.NoError(t, err) + _, overloadID := DecodeOverloadID(resolved.GetEncodedOverloadID()) + require.True(t, allSupportedFunctions[PYTHON_UDF].Overloads[overloadID].CannotFold()) +} diff --git a/pkg/udf/python/arrow.go b/pkg/udf/python/arrow.go index df82f10317a0c..707483a71e37e 100644 --- a/pkg/udf/python/arrow.go +++ b/pkg/udf/python/arrow.go @@ -24,6 +24,8 @@ import ( "errors" "fmt" "math" + "reflect" + "unicode/utf8" "github.com/apache/arrow-go/v18/arrow" "github.com/apache/arrow-go/v18/arrow/array" @@ -198,7 +200,7 @@ func (d TypeDescriptor) ValidateField(field arrow.Field) error { if err != nil { return err } - if field.Type == nil || field.Type.Fingerprint() != want.Type.Fingerprint() || field.Nullable != want.Nullable { + if field.Type == nil || !sameArrowType(field.Type, want.Type) || field.Nullable != want.Nullable { return fmt.Errorf("TYPE_CONTRACT: Arrow type %s does not match %s", field.Type, want.Type) } if !field.Metadata.Equal(want.Metadata) { @@ -207,6 +209,18 @@ func (d TypeDescriptor) ValidateField(field arrow.Field) error { return nil } +// sameArrowType intentionally compares the complete concrete Arrow type. +// Arrow-Go's Fingerprint is a physical layout fingerprint and, for some +// FixedSizeBinary versions, omits ByteWidth. A UUID contract must reject +// fixed_size_binary(1) even when that fingerprint collides with the expected +// fixed_size_binary(16). +func sameArrowType(left, right arrow.DataType) bool { + if left == nil || right == nil { + return left == right + } + return reflect.DeepEqual(left, right) +} + type ArrowFrame struct { Header []byte Body []byte @@ -700,6 +714,9 @@ func appendFloat32ArrayResult(input *array.FixedSizeList, row int, null bool, re values := input.ListValues().(*array.Float32) items := make([]float32, end-start) for i := start; i < end; i++ { + if values.IsNull(int(i)) { + return fmt.Errorf("TYPE_CONTRACT: vector child at row %d is null", row) + } items[i-start] = values.Value(int(i)) } return vector.AppendArray(result.GetResultVector(), items, false, mp) @@ -713,6 +730,9 @@ func appendFloat64ArrayResult(input *array.FixedSizeList, row int, null bool, re values := input.ListValues().(*array.Float64) items := make([]float64, end-start) for i := start; i < end; i++ { + if values.IsNull(int(i)) { + return fmt.Errorf("TYPE_CONTRACT: vector child at row %d is null", row) + } items[i-start] = values.Value(int(i)) } return vector.AppendArray(result.GetResultVector(), items, false, mp) @@ -722,7 +742,7 @@ func validateArrayType(descriptor TypeDescriptor, input arrow.Array) error { if err != nil { return err } - if input.DataType().Fingerprint() != want.Fingerprint() { + if !sameArrowType(input.DataType(), want) { return fmt.Errorf("TYPE_CONTRACT: output Arrow type %s does not match %s", input.DataType(), want) } return nil @@ -738,7 +758,7 @@ func validateArrowValueDomain(descriptor TypeDescriptor, input arrow.Array) erro } switch types.T(descriptor.TypeID) { case types.T_char, types.T_varchar, types.T_text: - if descriptor.Width > 0 && int32(len([]byte(input.(*array.String).Value(row)))) > descriptor.Width { + if descriptor.Width > 0 && int32(utf8.RuneCountInString(input.(*array.String).Value(row))) > descriptor.Width { return fmt.Errorf("TYPE_CONTRACT: string row %d exceeds width %d", row, descriptor.Width) } case types.T_binary, types.T_varbinary, types.T_blob: @@ -750,6 +770,35 @@ func validateArrowValueDomain(descriptor TypeDescriptor, input arrow.Array) erro if !types.IsMySQLTime(value) || !hasExactMicrosecondScale(int64(value), descriptor.Scale) { return fmt.Errorf("TYPE_CONTRACT: TIME row %d is outside the declared SQL domain", row) } + case types.T_decimal64, types.T_decimal128: + precision := descriptor.Width + if precision == 0 { + precision = 18 + if types.T(descriptor.TypeID) == types.T_decimal128 { + precision = 38 + } + } + if !input.(*array.Decimal128).Value(row).FitsInPrecision(precision) { + return fmt.Errorf("TYPE_CONTRACT: decimal row %d exceeds precision %d", row, precision) + } + case types.T_array_float32: + list := input.(*array.FixedSizeList) + values := list.ListValues().(*array.Float32) + start, end := list.ValueOffsets(row) + for child := start; child < end; child++ { + if values.IsNull(int(child)) { + return fmt.Errorf("TYPE_CONTRACT: vector child at row %d is null", row) + } + } + case types.T_array_float64: + list := input.(*array.FixedSizeList) + values := list.ListValues().(*array.Float64) + start, end := list.ValueOffsets(row) + for child := start; child < end; child++ { + if values.IsNull(int(child)) { + return fmt.Errorf("TYPE_CONTRACT: vector child at row %d is null", row) + } + } case types.T_date, types.T_datetime, types.T_timestamp: structValue := input.(*array.Struct) zeroField, valueField := structValue.Field(0), structValue.Field(1) diff --git a/pkg/udf/python/arrow_test.go b/pkg/udf/python/arrow_test.go index 3898dce4413f8..7e388e4816c03 100644 --- a/pkg/udf/python/arrow_test.go +++ b/pkg/udf/python/arrow_test.go @@ -10,6 +10,7 @@ import ( "github.com/apache/arrow-go/v18/arrow" "github.com/apache/arrow-go/v18/arrow/array" + "github.com/apache/arrow-go/v18/arrow/decimal128" "github.com/apache/arrow-go/v18/arrow/memory" "github.com/matrixorigin/matrixone/pkg/container/types" "github.com/stretchr/testify/require" @@ -90,3 +91,46 @@ func TestArrowValueDomainRejectsWidthAndTimeOverflow(t *testing.T) { timeDescriptor := TypeDescriptor{TypeID: int32(types.T_time), Scale: 6, OffsetWidth: 32} require.ErrorContains(t, validateArrowValueDomain(timeDescriptor, times), "outside the declared SQL domain") } + +func TestArrowTypeValidationIncludesFixedBinaryWidth(t *testing.T) { + descriptor, err := NewTypeDescriptor(types.T_uuid.ToType()) + require.NoError(t, err) + builder := array.NewFixedSizeBinaryBuilder(memory.NewGoAllocator(), &arrow.FixedSizeBinaryType{ByteWidth: 1}) + builder.Append([]byte{0xff}) + values := builder.NewFixedSizeBinaryArray() + defer values.Release() + require.ErrorContains(t, validateArrayType(descriptor, values), "does not match") + + field, err := descriptor.Field("uuid") + require.NoError(t, err) + field.Type = &arrow.FixedSizeBinaryType{ByteWidth: 1} + require.ErrorContains(t, descriptor.ValidateField(field), "does not match") +} + +func TestArrowValueDomainRejectsDecimalOverflowAndNullableVectorChild(t *testing.T) { + decimalType := &arrow.Decimal128Type{Precision: 3, Scale: 0} + decimalBuilder := array.NewDecimal128Builder(memory.NewGoAllocator(), decimalType) + decimalBuilder.Append(decimal128.FromI64(1000)) + decimals := decimalBuilder.NewDecimal128Array() + defer decimals.Release() + decimalDescriptor := TypeDescriptor{TypeID: int32(types.T_decimal128), Width: 3, OffsetWidth: 32} + require.ErrorContains(t, validateArrowValueDomain(decimalDescriptor, decimals), "exceeds precision") + + listBuilder := array.NewFixedSizeListBuilder(memory.NewGoAllocator(), 2, arrow.PrimitiveTypes.Float32) + listBuilder.Append(true) + listBuilder.ValueBuilder().(*array.Float32Builder).Append(1) + listBuilder.ValueBuilder().(*array.Float32Builder).AppendNull() + lists := listBuilder.NewArray() + defer lists.Release() + vectorDescriptor := TypeDescriptor{TypeID: int32(types.T_array_float32), Width: 2} + require.ErrorContains(t, validateArrowValueDomain(vectorDescriptor, lists), "vector child") +} + +func TestArrowStringWidthCountsUnicodeCharacters(t *testing.T) { + builder := array.NewStringBuilder(memory.NewGoAllocator()) + builder.Append("中") + values := builder.NewStringArray() + defer values.Release() + descriptor := TypeDescriptor{TypeID: int32(types.T_varchar), Width: 1, OffsetWidth: 32} + require.NoError(t, validateArrowValueDomain(descriptor, values)) +} diff --git a/pkg/udf/python/config.go b/pkg/udf/python/config.go index 937736bb8a958..13ccc64588969 100644 --- a/pkg/udf/python/config.go +++ b/pkg/udf/python/config.go @@ -33,13 +33,24 @@ func (c *Config) Validate() error { } type ClientConfig struct { - ServerAddress string `toml:"server-address"` - MaxBatchBytes int64 `toml:"max-batch-bytes"` - MaxBatchRows int64 `toml:"max-batch-rows"` - RequestTimeout time.Duration `toml:"request-timeout"` + // Enabled is deliberately false by default. The Python worker is an + // unisolated development adapter until the production sandbox contract is + // deployed, so a generic CN launch must never attach it implicitly. + Enabled bool `toml:"enabled"` + AllowUnisolated bool `toml:"allow-unisolated"` + ServerAddress string `toml:"server-address"` + MaxBatchBytes int64 `toml:"max-batch-bytes"` + MaxBatchRows int64 `toml:"max-batch-rows"` + RequestTimeout time.Duration `toml:"request-timeout"` } func (c *ClientConfig) Validate() error { + if !c.Enabled { + return nil + } + if !c.AllowUnisolated { + return moerr.NewInternalError(context.Background(), "python udf requires explicit allow-unisolated until the sandbox is configured") + } if c.ServerAddress == "" { return moerr.NewInternalError(context.Background(), "missing python udf address") } diff --git a/pkg/udf/python/config_test.go b/pkg/udf/python/config_test.go new file mode 100644 index 0000000000000..090963b6709ea --- /dev/null +++ b/pkg/udf/python/config_test.go @@ -0,0 +1,25 @@ +// Copyright 2026 Matrix Origin +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. + +package python + +import ( + "testing" + "time" + + "github.com/stretchr/testify/require" +) + +func TestClientConfigRequiresExplicitUnisolatedOptIn(t *testing.T) { + require.NoError(t, (&ClientConfig{}).Validate()) + require.NoError(t, (&ClientConfig{ServerAddress: "127.0.0.1:50051"}).Validate()) + + config := &ClientConfig{Enabled: true, ServerAddress: "127.0.0.1:50051"} + require.ErrorContains(t, config.Validate(), "allow-unisolated") + + config.AllowUnisolated = true + config.RequestTimeout = time.Second + require.NoError(t, config.Validate()) +} diff --git a/pkg/udf/python/gateway.go b/pkg/udf/python/gateway.go index 25860017e7ba3..73084ee4561e8 100644 --- a/pkg/udf/python/gateway.go +++ b/pkg/udf/python/gateway.go @@ -110,6 +110,7 @@ type openPayload struct { Return TypeDescriptor `json:"return"` MaxBatchBytes int64 `json:"max_batch_bytes"` MaxBatchRows int64 `json:"max_batch_rows"` + HandlerTimeout float64 `json:"handler_timeout_seconds"` } func (g *Gateway) Execute(ctx context.Context, invocation *udf.Invocation, result vector.FunctionResultWrapper, mp *mpool.MPool) error { @@ -145,7 +146,7 @@ func (g *Gateway) Execute(ctx context.Context, invocation *udf.Invocation, resul if err != nil { return err } - openBody, err := json.Marshal(openPayload{Handler: invocation.Handler, Source: invocation.Source, Mode: invocation.Mode, NullPolicy: invocation.NullPolicy, ABIContract: invocation.ABIContract, AdapterVersion: invocation.AdapterVersion, SDKVersion: invocation.SDKVersion, Context: cloneMap(invocation.Context), Args: args, Return: returnDescriptor, MaxBatchBytes: g.cfg.MaxBatchBytes, MaxBatchRows: g.cfg.MaxBatchRows}) + openBody, err := json.Marshal(openPayload{Handler: invocation.Handler, Source: invocation.Source, Mode: invocation.Mode, NullPolicy: invocation.NullPolicy, ABIContract: invocation.ABIContract, AdapterVersion: invocation.AdapterVersion, SDKVersion: invocation.SDKVersion, Context: cloneMap(invocation.Context), Args: args, Return: returnDescriptor, MaxBatchBytes: g.cfg.MaxBatchBytes, MaxBatchRows: g.cfg.MaxBatchRows, HandlerTimeout: g.cfg.RequestTimeout.Seconds()}) if err != nil { return fmt.Errorf("python udf: encode open payload: %w", err) } diff --git a/pkg/udf/python/worker/test_worker.py b/pkg/udf/python/worker/test_worker.py index 6a8a31a9beeca..e43c07f8a49ca 100644 --- a/pkg/udf/python/worker/test_worker.py +++ b/pkg/udf/python/worker/test_worker.py @@ -4,7 +4,9 @@ import importlib.util import json import pathlib +import struct import sys +import time import unittest import uuid @@ -77,6 +79,69 @@ def test_scalar_value_domain_is_checked_before_arrow_encoding(self): with self.assertRaisesRegex(ValueError, "TIME is outside"): worker._output_array([datetime.timedelta(hours=839)], time_descriptor, 1) + def test_sql_string_width_counts_characters(self): + descriptor = {"type_id": worker.VARCHAR, "width": 1, "offset_width": 32} + array = worker._output_array(["中"], descriptor, 1) + self.assertEqual(["中"], array.to_pylist()) + + def test_decimal_precision_and_vector_child_validity_are_checked(self): + decimal_descriptor = {"type_id": worker.DECIMAL128, "width": 3, "scale": 0, "offset_width": 32} + with self.assertRaisesRegex(ValueError, "precision exceeded"): + worker._output_array([__import__("decimal").Decimal("1000")], decimal_descriptor, 1) + + vector_descriptor = {"type_id": worker.VECF32, "width": 2, "offset_width": 0} + child = pa.array([1.0, None, 3.0, 4.0], type=pa.float32()) + vector = pa.FixedSizeListArray.from_arrays(child, 2) + with self.assertRaisesRegex(ValueError, "child validity"): + worker._output_array(vector, vector_descriptor, 2) + + def test_scalar_vector_null_keeps_fixed_size_child_slots(self): + descriptor = {"type_id": worker.VECF32, "width": 2, "offset_width": 0} + array = worker._output_array( + [memoryview(struct.pack(" int return item +def _required_positive_float(value: Dict[str, Any], key: str, maximum: float) -> float: + item = value.get(key) + if isinstance(item, bool) or not isinstance(item, (int, float)) or item <= 0 or item > maximum: + raise ValueError(f"PROTOCOL: invalid invocation field {key}") + return float(item) + + def _required_uint64(value: Dict[str, Any], key: str, allow_zero: bool = False) -> int: item = value.get(key) minimum = 0 if allow_zero else 1 @@ -507,7 +523,7 @@ def _check_scalar(value: Any, descriptor: Dict[str, Any]): if type_id in (CHAR, VARCHAR, TEXT): if type(value) is not str: raise ValueError("TYPE_CONTRACT: expected string") width = int(descriptor.get("width") or 0) - if width > 0 and len(value.encode("utf-8")) > width: raise ValueError("TYPE_CONTRACT: string exceeds the declared width") + if width > 0 and len(value) > width: raise ValueError("TYPE_CONTRACT: string exceeds the declared width") return value if type_id == JSON: return _check_json(value) if type_id in (BINARY, VARBINARY, BLOB): @@ -552,8 +568,12 @@ def _check_scalar(value: Any, descriptor: Dict[str, Any]): return values if type_id in (DECIMAL64, DECIMAL128): if not isinstance(value, _decimal.Decimal): raise ValueError("TYPE_CONTRACT: expected decimal.Decimal") + if not value.is_finite(): raise ValueError("TYPE_CONTRACT: decimal must be finite") scale = int(descriptor.get("scale") or 0) - if -value.as_tuple().exponent != scale: raise ValueError("TYPE_CONTRACT: decimal scale mismatch") + exponent = value.as_tuple().exponent + if not isinstance(exponent, int) or -exponent != scale: raise ValueError("TYPE_CONTRACT: decimal scale mismatch") + precision = int(descriptor.get("width") or (18 if type_id == DECIMAL64 else 38)) + if len(value.as_tuple().digits) > precision: raise ValueError("TYPE_CONTRACT: decimal precision exceeded") return value @@ -597,7 +617,10 @@ def _output_array(values: Any, descriptor: Dict[str, Any], rows: int) -> pa.Arra checked.append(_check_scalar(value, descriptor)) try: child_type = pa.float32() if type_id == VECF32 else pa.float64() - flat = [item for row in checked if row is not None for item in row] + # A NULL parent still owns exactly `width` child slots in a + # FixedSizeListArray. Leaving those slots out changes the row + # layout and makes the following non-NULL rows shift left. + flat = [item for row in checked for item in (row if row is not None else [0] * width)] child = pa.array(flat, type=child_type) mask = pa.array([row is None for row in checked]) return pa.FixedSizeListArray.from_arrays(child, width, mask=mask) @@ -614,7 +637,7 @@ def _validate_array_values(array: pa.Array, descriptor: Dict[str, Any]) -> None: if type_id in (DATE, DATETIME, TIMESTAMP): struct = array for index in range(len(struct)): - if struct.is_null(index): continue + if not struct[index].is_valid: continue is_zero = bool(struct.field("is_zero")[index].as_py()) value = struct.field("value")[index].as_py() if value is None: raise ValueError("TYPE_CONTRACT: temporal child is null") @@ -624,9 +647,14 @@ def _validate_array_values(array: pa.Array, descriptor: Dict[str, Any]) -> None: if type_id == DATE: _check_scalar(SqlDate(False, value), descriptor) elif type_id == DATETIME: _check_scalar(SqlDatetime(False, value), descriptor) else: _check_scalar(SqlTimestamp(False, value), descriptor) + elif type_id in (VECF32, VECF64): + values = array.values + for index in range(len(values)): + if not values[index].is_valid: + raise ValueError("TYPE_CONTRACT: vector child validity must be non-null") elif type_id in (CHAR, VARCHAR, TEXT, JSON, BINARY, VARBINARY, BLOB, UUID, TIME, DECIMAL64, DECIMAL128): for index in range(len(array)): - if array.is_null(index): continue + if not array[index].is_valid: continue value = array[index].as_py() if type_id == UUID: if not isinstance(value, (bytes, bytearray)) or len(value) != 16: raise ValueError("TYPE_CONTRACT: UUID must contain 16 bytes") @@ -634,6 +662,191 @@ def _validate_array_values(array: pa.Array, descriptor: Dict[str, Any]) -> None: _check_scalar(value, descriptor) +class _DiscardText: + def write(self, value: str) -> int: + return len(value) + + def flush(self) -> None: + return None + + +def _serialize_record_batch(record: pa.RecordBatch) -> bytes: + sink = pa.BufferOutputStream() + with pa.ipc.new_stream(sink, record.schema) as stream: + stream.write_batch(record) + return sink.getvalue().to_pybytes() + + +def _deserialize_record_batch(data: bytes) -> pa.RecordBatch: + if not isinstance(data, (bytes, bytearray)) or not data: + raise ValueError("PROTOCOL: execution payload is missing an Arrow batch") + reader = pa.ipc.open_stream(pa.py_buffer(data)) + try: + try: + record = reader.read_next_batch() + except StopIteration as exc: + raise ValueError("PROTOCOL: execution payload contains no Arrow batch") from exc + try: + reader.read_next_batch() + except StopIteration: + return record + raise ValueError("PROTOCOL: execution payload contains multiple Arrow batches") + finally: + reader.close() + + +def _execute_handler_batch(request: Dict[str, Any]) -> bytes: + batch = _deserialize_record_batch(request["input"]) + args = request["args"] + result_descriptor = request["return"] + mode = request["mode"] + null_policy = request["null_policy"] + sdk_version = request["sdk_version"] + statement_context = _statement_context(request.get("context")) + + # User code has no stdout/stderr channel in the Flight protocol. Discard + # it so a print() cannot corrupt the length-prefixed response frame. + with contextlib.redirect_stdout(_DiscardText()), contextlib.redirect_stderr(_DiscardText()): + handler = _load_handler(request["source"], request["handler"]) + if mode == MODE_VECTOR: + call_context = VectorContext(sdk_version, _CallLogger(), statement_context, batch.num_rows) + output = handler(call_context, *[batch.column(index) for index in range(batch.num_columns)]) + if not isinstance(output, (pa.Array, pa.ChunkedArray)): + raise ValueError("TYPE_CONTRACT: VECTOR handler must return an Arrow Array or ChunkedArray") + else: + call_context = ScalarContext(sdk_version, _CallLogger(), statement_context) + values = [] + for row in range(batch.num_rows): + params = [_scalar_input(batch.column(index), row, args[index]) for index in range(batch.num_columns)] + if null_policy == NULL_RETURN and any(value is None for value in params): + values.append(None) + else: + values.append(handler(call_context, *params)) + output = values + output_array = _output_array(output, result_descriptor, batch.num_rows) + result_field = _field("result", result_descriptor) + result_batch = pa.RecordBatch.from_arrays( + [output_array], schema=pa.schema([result_field]) + ) + if pa.ipc.get_record_batch_size(result_batch) > request["max_batch_bytes"]: + raise ValueError("RESOURCE_EXHAUSTED: output batch exceeds byte limit") + return _serialize_record_batch(result_batch) + + +def _read_exact(stream, size: int) -> bytes: + result = bytearray() + while len(result) < size: + chunk = stream.read(size - len(result)) + if not chunk: + raise EOFError("execution frame ended unexpectedly") + result.extend(chunk) + return bytes(result) + + +def _write_execution_frame(stream, payload: bytes) -> None: + if len(payload) > MAX_EXECUTION_FRAME_BYTES: + raise ValueError("RESOURCE_EXHAUSTED: execution frame is too large") + stream.write(struct.pack(">Q", len(payload))) + stream.write(payload) + stream.flush() + + +def _execute_handler_subprocess() -> None: + try: + size = struct.unpack(">Q", _read_exact(sys.stdin.buffer, 8))[0] + if size > MAX_EXECUTION_FRAME_BYTES: + raise ValueError("RESOURCE_EXHAUSTED: execution request is too large") + request = pickle.loads(_read_exact(sys.stdin.buffer, size)) + response = {"ok": True, "payload": _execute_handler_batch(request)} + except Exception as exc: + response = {"ok": False, "error": _safe_error(exc)} + _write_execution_frame(sys.stdout.buffer, pickle.dumps(response, protocol=5)) + + +def _context_is_cancelled(context) -> bool: + if context is None: + return False + try: + return bool(context.is_cancelled()) + except Exception: + return False + + +def _kill_execution_process(process: subprocess.Popen) -> None: + if process.poll() is None: + if os.name == "posix": + try: + os.killpg(process.pid, signal.SIGKILL) + except ProcessLookupError: + pass + else: + process.kill() + try: + process.wait(timeout=1.0) + except subprocess.TimeoutExpired: + process.kill() + process.wait() + + +def _run_handler_process(context, request: Dict[str, Any], timeout_seconds: float) -> bytes: + request_wire = pickle.dumps(request, protocol=5) + if len(request_wire) > MAX_EXECUTION_FRAME_BYTES: + raise ValueError("RESOURCE_EXHAUSTED: execution request is too large") + popen_kwargs = { + "stdin": subprocess.PIPE, + "stdout": subprocess.PIPE, + "stderr": subprocess.DEVNULL, + "close_fds": True, + } + if os.name == "posix": + popen_kwargs["start_new_session"] = True + process = subprocess.Popen([sys.executable, os.path.abspath(__file__), "--execute-handler"], **popen_kwargs) + selector = selectors.DefaultSelector() + response = bytearray() + expected = None + try: + _write_execution_frame(process.stdin, request_wire) + process.stdin.close() + selector.register(process.stdout, selectors.EVENT_READ) + deadline = time.monotonic() + timeout_seconds + while True: + if _context_is_cancelled(context): + raise TimeoutError("DEADLINE_EXCEEDED: handler execution cancelled") + remaining = deadline - time.monotonic() + if remaining <= 0: + raise TimeoutError("DEADLINE_EXCEEDED: handler execution timeout") + events = selector.select(min(remaining, 0.1)) + if not events: + continue + chunk = os.read(process.stdout.fileno(), 65536) + if not chunk: + if expected is None or len(response) < expected: + raise ValueError("USER_CODE: handler process exited without a response") + break + response.extend(chunk) + if expected is None and len(response) >= 8: + expected = struct.unpack(">Q", response[:8])[0] + if expected > MAX_EXECUTION_FRAME_BYTES: + raise ValueError("RESOURCE_EXHAUSTED: execution response is too large") + if expected is not None and len(response) >= expected + 8: + break + payload = pickle.loads(bytes(response[8 : expected + 8])) + if not isinstance(payload, dict) or payload.get("ok") is not True: + raise ValueError(str(payload.get("error", "USER_CODE: handler process failed"))) + result = payload.get("payload") + if not isinstance(result, bytes): + raise ValueError("PROTOCOL: handler process returned an invalid Arrow payload") + return result + finally: + selector.close() + if process.poll() is None: + _kill_execution_process(process) + else: + process.wait() + process.stdout.close() + + + class _InvocationState: def __init__(self, tuple_value: Dict[str, Any], terminal_bytes: int): self.tuple = tuple_value @@ -651,13 +864,15 @@ def ack_result(self, sequence: int) -> None: self.acked_result = sequence self.condition.notify_all() - def wait_result_ack(self, sequence: int) -> None: + def wait_result_ack(self, sequence: int, context=None) -> None: deadline = time.monotonic() + ACK_TIMEOUT_SECONDS with self.condition: while self.acked_result < sequence: + if context is not None and context.is_cancelled(): + raise TimeoutError("DEADLINE_EXCEEDED: result ACK wait cancelled") remaining = deadline - time.monotonic() if remaining <= 0: raise TimeoutError("DEADLINE_EXCEEDED: result ACK timeout") - self.condition.wait(remaining) + self.condition.wait(min(remaining, 0.2)) def ack_finish(self, finish_id: str) -> None: with self.condition: @@ -665,13 +880,15 @@ def ack_finish(self, finish_id: str) -> None: self.finish_acked = True self.condition.notify_all() - def wait_finish_ack(self) -> None: + def wait_finish_ack(self, context=None) -> None: deadline = time.monotonic() + ACK_TIMEOUT_SECONDS with self.condition: while not self.finish_acked: + if context is not None and context.is_cancelled(): + raise TimeoutError("DEADLINE_EXCEEDED: Finish ACK wait cancelled") remaining = deadline - time.monotonic() if remaining <= 0: raise TimeoutError("DEADLINE_EXCEEDED: Finish ACK timeout") - self.condition.wait(remaining) + self.condition.wait(min(remaining, 0.2)) class RoutineFlightServer(flight.FlightServerBase): @@ -720,6 +937,22 @@ def _remember_terminal_locked(self, key: tuple, state: _InvocationState) -> None self._terminal.move_to_end(key) self._terminal_bytes += state.terminal_bytes + def _finish_invocation(self, key: tuple, state: Optional[_InvocationState]) -> None: + # A rejected duplicate Open has no ownership of the existing state. + # Cleanup must therefore be identity based, not key based. + if key is None or state is None: + return + with self._lock: + current = self._active.get(key) + if current is not state: + return + self._active.pop(key, None) + self._active_bytes -= state.terminal_bytes + # A started invocation is terminal even when the worker reports an + # error. Retaining the fence prevents a late retry from running + # user code a second time. + self._remember_terminal_locked(key, state) + def do_action(self, context, action): control = _decode_control(action.body) key = _tuple_key(control["tuple"]) @@ -770,12 +1003,16 @@ def do_exchange(self, context, descriptor, reader, writer): sdk_version = _required_string(payload, "sdk_version") max_batch_bytes = _required_positive_int(payload, "max_batch_bytes", 1 << 30) max_batch_rows = _required_positive_int(payload, "max_batch_rows", 1 << 30) + handler_timeout_seconds = _required_positive_float( + payload, "handler_timeout_seconds", 3600.0 + ) if mode not in (MODE_SCALAR, MODE_VECTOR) or null_policy not in (NULL_CALL, NULL_RETURN): raise ValueError("PROTOCOL: unsupported call mode or NULL policy") if abi_contract != ABI_CONTRACT or adapter_version != ADAPTER_VERSION: raise ValueError("PROTOCOL: unsupported Python ABI contract") if sdk_version != SDK_VERSION: raise ValueError("PROTOCOL: unsupported Python SDK") state = self._admit(key) state.tuple = open_control["tuple"] - handler = _load_handler(_required_string(payload, "source"), _required_string(payload, "handler")) + source = _required_string(payload, "source") + handler_name = _required_string(payload, "handler") schema = None result_field = _field("result", result_descriptor) result_schema = pa.schema([result_field]) @@ -801,34 +1038,33 @@ def do_exchange(self, context, descriptor, reader, writer): if batch.num_rows <= 0: raise ValueError("PROTOCOL: empty input batch") if batch.num_rows > max_batch_rows: raise ValueError("RESOURCE_EXHAUSTED: input batch has too many rows") if batch.nbytes > max_batch_bytes: raise ValueError("RESOURCE_EXHAUSTED: input batch exceeds byte limit") - if mode == MODE_VECTOR: - call_context = VectorContext(sdk_version, _CallLogger(), statement_context, batch.num_rows) - try: - output = handler(call_context, *[batch.column(index) for index in range(batch.num_columns)]) - except Exception as exc: - raise ValueError("USER_CODE: handler failed") from exc - if not isinstance(output, (pa.Array, pa.ChunkedArray)): - raise ValueError("TYPE_CONTRACT: VECTOR handler must return an Arrow Array or ChunkedArray") - else: - call_context = ScalarContext(sdk_version, _CallLogger(), statement_context) - values = [] - for row in range(batch.num_rows): - params = [_scalar_input(batch.column(index), row, args[index]) for index in range(batch.num_columns)] - if null_policy == NULL_RETURN and any(value is None for value in params): values.append(None) - else: - try: - values.append(handler(call_context, *params)) - except Exception as exc: - raise ValueError("USER_CODE: handler failed") from exc - output = values - output_array = _output_array(output, result_descriptor, batch.num_rows) - output_batch = pa.RecordBatch.from_arrays([output_array], schema=result_schema) + execution_request = { + "source": source, + "handler": handler_name, + "mode": mode, + "null_policy": null_policy, + "sdk_version": sdk_version, + "context": payload.get("context"), + "args": args, + "return": result_descriptor, + "max_batch_bytes": max_batch_bytes, + "input": _serialize_record_batch(batch), + } + output_wire = _run_handler_process( + context, execution_request, handler_timeout_seconds + ) + output_batch = _deserialize_record_batch(output_wire) + if output_batch.num_rows != batch.num_rows or output_batch.num_columns != 1: + raise ValueError("TYPE_CONTRACT: handler result has the wrong shape") + _validate_field(output_batch.schema.field(0), "result", result_descriptor) + output_array = output_batch.column(0) + _validate_array_values(output_array, result_descriptor) if pa.ipc.get_record_batch_size(output_batch) > max_batch_bytes: raise ValueError("RESOURCE_EXHAUSTED: output batch exceeds byte limit") state.last_result = sequence writer.write_metadata(_encode_control({"kind": "InputConsumed", "tuple": open_control["tuple"], "sequence": sequence})) writer.write_with_metadata(output_batch, _encode_control({"kind": "ResultBatch", "tuple": open_control["tuple"], "sequence": sequence})) - state.wait_result_ack(sequence) + state.wait_result_ack(sequence, context) elif chunk.app_metadata: control = _decode_control(chunk.app_metadata) _require_tuple(control["tuple"], open_control["tuple"]) @@ -841,7 +1077,7 @@ def do_exchange(self, context, descriptor, reader, writer): if state.acked_result != state.last_result: raise ValueError("PROTOCOL: result is not acknowledged") state.finish_id = _uuid.uuid4().hex writer.write_metadata(_encode_control({"kind": "Finish", "tuple": open_control["tuple"], "status": "OK", "finish_id": state.finish_id, "last_sequence": state.last_result})) - state.wait_finish_ack() + state.wait_finish_ack(context) except Exception as exc: if writer_started and key is not None: try: @@ -850,16 +1086,7 @@ def do_exchange(self, context, descriptor, reader, writer): pass raise finally: - if key is not None: - with self._lock: - current = self._active.pop(key, None) - if current is not None: - self._active_bytes -= current.terminal_bytes - # A started invocation is terminal even when the worker - # reports an error. Retaining the fence prevents a late - # retry from running user code a second time; the caller's - # error path remains responsible for reporting failure. - self._remember_terminal_locked(key, current) + self._finish_invocation(key, state) def _safe_error(exc: Exception) -> str: @@ -872,8 +1099,14 @@ def _safe_error(exc: Exception) -> str: def main() -> None: parser = argparse.ArgumentParser() - parser.add_argument("--address", required=True) + parser.add_argument("--execute-handler", action="store_true") + parser.add_argument("--address") args = parser.parse_args() + if args.execute_handler: + _execute_handler_subprocess() + return + if not args.address: + parser.error("--address is required for the Flight server") logging.basicConfig(level=logging.INFO, format="[%(asctime)s] %(levelname)s %(message)s") address = args.address if "://" in args.address else "grpc://" + args.address server = RoutineFlightServer(address) diff --git a/test/distributed/cases/udf_python/README.md b/test/distributed/cases/udf_python/README.md index 2efd69b2cbbd4..2e34d22d10070 100644 --- a/test/distributed/cases/udf_python/README.md +++ b/test/distributed/cases/udf_python/README.md @@ -5,7 +5,9 @@ needs a matching `.result` file generated by `mo-tester`. These cases are part of the ordinary BVT suite and run with the other SQL cases. For a local launch BVT, install the locked worker dependency once and use the -normal BVT commands: +normal BVT commands. `run_bvt.sh . launch` selects the explicit +worker-enabled manifest for this ordinary BVT entry point; starting +`etc/launch/launch.toml` directly leaves Python disabled. ```bash python3 -m pip install -r pkg/udf/python/worker/requirements.txt @@ -23,9 +25,9 @@ cd /path/to/mo-tester ./run.sh -p /path/to/matrixone/test/distributed/cases/udf_python/example.sql -m genrs -g ``` -The ordinary launch manifest starts one worker for the single CN. Compose BVT -starts one isolated worker service per CN from the same pinned image, so adding -a SQL case here does not require another CI workflow. +The ordinary BVT launch starts one worker for the single CN. Compose BVT starts +one isolated worker service per CN from the same pinned image, so adding a SQL +case here does not require another CI workflow. The cases keep the data sets small and target contract boundaries: diff --git a/test/distributed/cases/udf_python/numeric_boundaries.sql b/test/distributed/cases/udf_python/numeric_boundaries.sql index 0c00e1f813be5..9d63739488d49 100644 --- a/test/distributed/cases/udf_python/numeric_boundaries.sql +++ b/test/distributed/cases/udf_python/numeric_boundaries.sql @@ -17,6 +17,7 @@ create function python_bvt_double (x double) returns double language python as ' create function python_bvt_bool (x bool) returns bool language python as 'def python_bvt_bool(ctx, x): return x' handler 'python_bvt_bool'; create function python_bvt_decimal64 (x decimal(18,6)) returns decimal(18,6) language python as 'def python_bvt_decimal64(ctx, x): return x' handler 'python_bvt_decimal64'; create function python_bvt_decimal128 (x decimal(38,10)) returns decimal(38,10) language python as 'def python_bvt_decimal128(ctx, x): return x' handler 'python_bvt_decimal128'; +create function python_bvt_decimal_rescale (x decimal(18,6)) returns decimal(18,6) language python as 'def python_bvt_decimal_rescale(ctx, x): return x' handler 'python_bvt_decimal_rescale'; create table numeric_values ( tiny_v tinyint, @@ -55,6 +56,10 @@ select python_bvt_tinyint(tiny_v) as tiny_v, python_bvt_decimal128(decimal128_v) as decimal128_v from numeric_values order by big_v is null, big_v; +-- The planner must normalize DECIMAL(18,2) to the declared scale before the +-- Arrow encoder sees the coefficient; 1.23 must remain 1.230000. +select python_bvt_decimal_rescale(cast('1.23' as decimal(18,2))) as decimal_rescaled; + drop function python_bvt_tinyint(tinyint); drop function python_bvt_smallint(smallint); drop function python_bvt_int(int); @@ -68,5 +73,6 @@ drop function python_bvt_double(double); drop function python_bvt_bool(bool); drop function python_bvt_decimal64(decimal(18,6)); drop function python_bvt_decimal128(decimal(38,10)); +drop function python_bvt_decimal_rescale(decimal(18,6)); drop table numeric_values; drop database udf_python_numeric_bvt; From 5e0e2add503e8ddc57eb30de30bfbe6772f36316 Mon Sep 17 00:00:00 2001 From: iamlinjunhong <1030420200@qq.com> Date: Wed, 9 Sep 2026 22:01:43 +0800 Subject: [PATCH 013/202] fix(udf): separate active and terminal reservations --- pkg/udf/protocol/protocol.go | 37 ++++++++++++++++++++++++- pkg/udf/protocol/protocol_test.go | 45 +++++++++++++++++++++++++++++++ 2 files changed, 81 insertions(+), 1 deletion(-) diff --git a/pkg/udf/protocol/protocol.go b/pkg/udf/protocol/protocol.go index 419e1fb31acd3..3b3722d908514 100644 --- a/pkg/udf/protocol/protocol.go +++ b/pkg/udf/protocol/protocol.go @@ -325,6 +325,7 @@ type ExecutionGroup struct { registered int inFlight int members map[string]bool + pending map[string]struct{} terminal map[string]bool closing bool releaseBusy bool @@ -339,7 +340,8 @@ func NewExecutionGroup(id string, epoch uint64, maxMembers int, release func() e } return &ExecutionGroup{ id: id, epoch: epoch, maxMembers: maxMembers, - members: make(map[string]bool), terminal: make(map[string]bool), release: release, + members: make(map[string]bool), pending: make(map[string]struct{}), + terminal: make(map[string]bool), release: release, }, nil } @@ -357,9 +359,13 @@ func (g *ExecutionGroup) BeginOpen(memberID string) (*OpenToken, error) { if g.members[memberID] || g.terminal[memberID] { return nil, ErrDuplicate } + if _, exists := g.pending[memberID]; exists { + return nil, ErrDuplicate + } if g.registered+g.inFlight >= g.maxMembers { return nil, fmt.Errorf("%w: member limit %d reached", ErrLedgerFull, g.maxMembers) } + g.pending[memberID] = struct{}{} g.inFlight++ return &OpenToken{group: g, memberID: memberID}, nil } @@ -378,6 +384,7 @@ func (t *OpenToken) Commit() error { t.once.Do(func() { g := t.group g.mu.Lock() + delete(g.pending, t.memberID) g.inFlight-- if g.closing || g.released { t.result = ErrGroupClosed @@ -404,6 +411,7 @@ func (t *OpenToken) Abort() error { t.once.Do(func() { g := t.group g.mu.Lock() + delete(g.pending, t.memberID) g.inFlight-- release := g.canReleaseLocked() g.mu.Unlock() @@ -606,6 +614,13 @@ func (l *TerminalLedger) Expire(now time.Time) int { defer l.mu.Unlock() removed := 0 for key, entry := range l.entries { + // Active entries represent work that may still produce a result. Their + // admission credit belongs to the running execution and can only be + // released by an explicit termination/recovery path. Expiry is solely + // for completed deduplication tombstones. + if !entry.tombstone { + continue + } if !now.Before(entry.expiresAt) { delete(l.entries, key) l.reservedN-- @@ -616,6 +631,26 @@ func (l *TerminalLedger) Expire(now time.Time) int { return removed } +// Abandon removes an active execution after its owner has confirmed +// cancellation, crash recovery, or another terminal failure. It is separate +// from Expire so a clock cannot reclaim a live invocation's deduplication +// entry. +func (l *TerminalLedger) Abandon(key string) error { + l.mu.Lock() + defer l.mu.Unlock() + entry, ok := l.entries[key] + if !ok { + return ErrUnknownIdentity + } + if entry.tombstone { + return ErrProtocol + } + delete(l.entries, key) + l.reservedN-- + l.reservedB -= entry.bytes + return nil +} + func (l *TerminalLedger) Counts() (entries int, bytes int64) { l.mu.Lock() defer l.mu.Unlock() diff --git a/pkg/udf/protocol/protocol_test.go b/pkg/udf/protocol/protocol_test.go index a9268fa7bd1d9..624e4035f1c29 100644 --- a/pkg/udf/protocol/protocol_test.go +++ b/pkg/udf/protocol/protocol_test.go @@ -158,6 +158,30 @@ func TestExecutionGroupZeroMemberAndAbortedOpenRelease(t *testing.T) { require.ErrorIs(t, open.Commit(), ErrGroupClosed) } +func TestExecutionGroupReservesPendingMemberIdentity(t *testing.T) { + group, err := NewExecutionGroup("g", 1, 2, func() error { return nil }) + require.NoError(t, err) + first, err := group.BeginOpen("member") + require.NoError(t, err) + _, err = group.BeginOpen("member") + require.ErrorIs(t, err, ErrDuplicate) + registered, active, inFlight := group.Counts() + require.Equal(t, 0, registered) + require.Equal(t, 0, active) + require.Equal(t, 1, inFlight) + require.NoError(t, first.Abort()) + _, _, inFlight = group.Counts() + require.Zero(t, inFlight) + + second, err := group.BeginOpen("member") + require.NoError(t, err) + require.NoError(t, second.Commit()) + registered, active, inFlight = group.Counts() + require.Equal(t, 1, registered) + require.Equal(t, 1, active) + require.Zero(t, inFlight) +} + func TestTerminalLedgerRetainsTombstonesUntilExpiry(t *testing.T) { ledger, err := NewTerminalLedger(2, 200) require.NoError(t, err) @@ -171,6 +195,7 @@ func TestTerminalLedgerRetainsTombstonesUntilExpiry(t *testing.T) { second, err := ledger.Reserve("g2", 1, 100) require.NoError(t, err) require.NoError(t, second.Add("i2", 100, now.Add(time.Hour))) + require.NoError(t, ledger.Complete("i2")) second.ReleaseUnused() _, err = ledger.Reserve("g3", 1, 1) require.ErrorIs(t, err, ErrLedgerFull) @@ -188,6 +213,26 @@ func TestTerminalLedgerRetainsTombstonesUntilExpiry(t *testing.T) { require.NoError(t, ledger.Complete("i3")) } +func TestTerminalLedgerDoesNotExpireActiveEntries(t *testing.T) { + ledger, err := NewTerminalLedger(1, 100) + require.NoError(t, err) + now := time.Unix(100, 0) + credit, err := ledger.Reserve("g", 1, 100) + require.NoError(t, err) + require.NoError(t, credit.Add("active", 100, now.Add(time.Second))) + credit.ReleaseUnused() + require.Equal(t, 0, ledger.Expire(now.Add(time.Hour))) + entries, bytes := ledger.Counts() + require.Equal(t, 1, entries) + require.Equal(t, int64(100), bytes) + _, err = ledger.Reserve("other", 1, 1) + require.ErrorIs(t, err, ErrLedgerFull) + require.NoError(t, ledger.Abandon("active")) + entries, bytes = ledger.Counts() + require.Zero(t, entries) + require.Zero(t, bytes) +} + func TestExecutionGroupReleaseErrorCanBeRetried(t *testing.T) { var attempts atomic.Int32 group, err := NewExecutionGroup("g", 1, 1, func() error { From 0c44e0e0b7cfe6fdee3f91b29faa194433829e40 Mon Sep 17 00:00:00 2001 From: iamlinjunhong <1030420200@qq.com> Date: Wed, 9 Sep 2026 22:09:30 +0800 Subject: [PATCH 014/202] test(udf): update decimal normalization baseline --- test/distributed/cases/udf_python/numeric_boundaries.result | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/test/distributed/cases/udf_python/numeric_boundaries.result b/test/distributed/cases/udf_python/numeric_boundaries.result index 27cddaf47fda9..c368b8dac5cac 100644 --- a/test/distributed/cases/udf_python/numeric_boundaries.result +++ b/test/distributed/cases/udf_python/numeric_boundaries.result @@ -14,6 +14,7 @@ create function python_bvt_double (x double) returns double language python as ' create function python_bvt_bool (x bool) returns bool language python as 'def python_bvt_bool(ctx, x): return x' handler 'python_bvt_bool'; create function python_bvt_decimal64 (x decimal(18,6)) returns decimal(18,6) language python as 'def python_bvt_decimal64(ctx, x): return x' handler 'python_bvt_decimal64'; create function python_bvt_decimal128 (x decimal(38,10)) returns decimal(38,10) language python as 'def python_bvt_decimal128(ctx, x): return x' handler 'python_bvt_decimal128'; +create function python_bvt_decimal_rescale (x decimal(18,6)) returns decimal(18,6) language python as 'def python_bvt_decimal_rescale(ctx, x): return x' handler 'python_bvt_decimal_rescale'; create table numeric_values ( tiny_v tinyint, small_v smallint, @@ -53,6 +54,9 @@ from numeric_values order by big_v is null, big_v; -128 ¦ -32768 ¦ -2147483648 ¦ -9223372036854775808 ¦ 0 ¦ 0 ¦ 0 ¦ 0 ¦ -0.0 ¦ -0.0 ¦ 0 ¦ -0.000001 ¦ -9999999999999999999999999999.9999999999 𝄀 127 ¦ 32767 ¦ 2147483647 ¦ 9223372036854775807 ¦ 255 ¦ 65535 ¦ 4294967295 ¦ 18446744073709551615 ¦ 3.25 ¦ 1.0 ¦ 1 ¦ 999999999999.999999 ¦ 9999999999999999999999999999.9999999999 𝄀 null ¦ null ¦ null ¦ null ¦ null ¦ null ¦ null ¦ null ¦ null ¦ null ¦ null ¦ null ¦ null +select python_bvt_decimal_rescale(cast('1.23' as decimal(18,2))) as decimal_rescaled; +➤ decimal_rescaled[3,18,6] 𝄀 +1.230000 drop function python_bvt_tinyint(tinyint); drop function python_bvt_smallint(smallint); drop function python_bvt_int(int); @@ -66,5 +70,6 @@ drop function python_bvt_double(double); drop function python_bvt_bool(bool); drop function python_bvt_decimal64(decimal(18,6)); drop function python_bvt_decimal128(decimal(38,10)); +drop function python_bvt_decimal_rescale(decimal(18,6)); drop table numeric_values; drop database udf_python_numeric_bvt; From a6ec69d4d7a72184458241620c88a1326930ccc7 Mon Sep 17 00:00:00 2001 From: iamlinjunhong <1030420200@qq.com> Date: Wed, 9 Sep 2026 22:13:25 +0800 Subject: [PATCH 015/202] fix(udf): harden handler response ownership --- pkg/udf/protocol/protocol_test.go | 2 ++ pkg/udf/python/gateway.go | 5 +++-- pkg/udf/python/worker/test_worker.py | 18 ++++++++++++++++++ pkg/udf/python/worker/worker.py | 27 +++++++++++++++++---------- 4 files changed, 40 insertions(+), 12 deletions(-) diff --git a/pkg/udf/protocol/protocol_test.go b/pkg/udf/protocol/protocol_test.go index 624e4035f1c29..7ae2198ecb2aa 100644 --- a/pkg/udf/protocol/protocol_test.go +++ b/pkg/udf/protocol/protocol_test.go @@ -80,7 +80,9 @@ func TestOutputSnapshotFreezesBeforeWorkerCanRewrite(t *testing.T) { worker[0] = 'X' require.NoError(t, snapshot.Validate(len("trusted-before-barrier"), snapshot.Digest())) + worker[1] = 'Y' require.Equal(t, "trusted-before-barrier", string(snapshot.Bytes())) + require.Equal(t, "trusted-before-barrier", string(snapshot.TrustedBytes())) empty, err := FreezeOutput(nil, 1024) require.NoError(t, err) diff --git a/pkg/udf/python/gateway.go b/pkg/udf/python/gateway.go index 73084ee4561e8..cfcffad2689b0 100644 --- a/pkg/udf/python/gateway.go +++ b/pkg/udf/python/gateway.go @@ -363,9 +363,10 @@ func (g *Gateway) receiveResultBatch( if err != nil { return 0, fmt.Errorf("python udf: decode result: %w", err) } - if decoded.NumCols() != 1 || decoded.NumRows() <= 0 || decoded.NumRows() != expectedRows { + decodedRows := decoded.NumRows() + if decoded.NumCols() != 1 || decodedRows <= 0 || decodedRows != expectedRows { decoded.Release() - return 0, fmt.Errorf("python udf: result batch row count %d, expected %d", decoded.NumRows(), expectedRows) + return 0, fmt.Errorf("python udf: result batch row count %d, expected %d", decodedRows, expectedRows) } if decoded.Schema().Field(0).Name != "result" { decoded.Release() diff --git a/pkg/udf/python/worker/test_worker.py b/pkg/udf/python/worker/test_worker.py index e43c07f8a49ca..4754fd24fff39 100644 --- a/pkg/udf/python/worker/test_worker.py +++ b/pkg/udf/python/worker/test_worker.py @@ -142,6 +142,24 @@ def test_handler_process_is_fresh_and_can_be_terminated(self): worker._run_handler_process(None, request, 0.1) self.assertLess(time.monotonic() - started, 2) + def test_handler_stdout_cannot_inject_parent_protocol(self): + descriptor = {"type_id": worker.INT64, "offset_width": 32} + batch = pa.RecordBatch.from_arrays([pa.array([1], type=pa.int64())], ["arg_0"]) + request = { + "source": "import os\ndef f(ctx, x): os.write(1, b'\\x00' * 8); return x", + "handler": "f", + "mode": worker.MODE_SCALAR, + "null_policy": worker.NULL_CALL, + "sdk_version": worker.SDK_VERSION, + "context": None, + "args": [descriptor], + "return": descriptor, + "max_batch_bytes": 1 << 20, + "input": worker._serialize_record_batch(batch), + } + with self.assertRaisesRegex(ValueError, "handler process"): + worker._run_handler_process(None, request, 3) + def test_zero_argument_vector_uses_context_rows(self): descriptor = {"type_id": worker.INT64, "offset_width": 32} output = pa.array([7, 7, 7], type=pa.int64()) diff --git a/pkg/udf/python/worker/worker.py b/pkg/udf/python/worker/worker.py index 908ceb97a7f39..583ab1df8aabf 100644 --- a/pkg/udf/python/worker/worker.py +++ b/pkg/udf/python/worker/worker.py @@ -45,6 +45,8 @@ TERMINAL_TTL_SECONDS = 300.0 ACK_TIMEOUT_SECONDS = 60.0 MAX_EXECUTION_FRAME_BYTES = 1 << 30 +_HANDLER_RESPONSE_ERROR = 0 +_HANDLER_RESPONSE_OK = 1 _MICROS_PER_SECOND = 1_000_000 _MAX_TIME_MICROS = (838 * 60 * 60 + 59 * 60 + 59) * _MICROS_PER_SECOND _MIN_TIMESTAMP = _datetime.datetime(1970, 1, 1, 0, 0, 1, tzinfo=_datetime.timezone.utc) @@ -757,10 +759,10 @@ def _execute_handler_subprocess() -> None: if size > MAX_EXECUTION_FRAME_BYTES: raise ValueError("RESOURCE_EXHAUSTED: execution request is too large") request = pickle.loads(_read_exact(sys.stdin.buffer, size)) - response = {"ok": True, "payload": _execute_handler_batch(request)} + response = bytes([_HANDLER_RESPONSE_OK]) + _execute_handler_batch(request) except Exception as exc: - response = {"ok": False, "error": _safe_error(exc)} - _write_execution_frame(sys.stdout.buffer, pickle.dumps(response, protocol=5)) + response = bytes([_HANDLER_RESPONSE_ERROR]) + _safe_error(exc).encode("utf-8") + _write_execution_frame(sys.stdout.buffer, response) def _context_is_cancelled(context) -> bool: @@ -830,13 +832,18 @@ def _run_handler_process(context, request: Dict[str, Any], timeout_seconds: floa raise ValueError("RESOURCE_EXHAUSTED: execution response is too large") if expected is not None and len(response) >= expected + 8: break - payload = pickle.loads(bytes(response[8 : expected + 8])) - if not isinstance(payload, dict) or payload.get("ok") is not True: - raise ValueError(str(payload.get("error", "USER_CODE: handler process failed"))) - result = payload.get("payload") - if not isinstance(result, bytes): - raise ValueError("PROTOCOL: handler process returned an invalid Arrow payload") - return result + payload = bytes(response[8 : expected + 8]) + if not payload: + raise ValueError("PROTOCOL: handler process returned an empty response") + if payload[0] == _HANDLER_RESPONSE_ERROR: + try: + message = payload[1:].decode("utf-8") + except UnicodeDecodeError as exc: + raise ValueError("PROTOCOL: handler process returned an invalid error") from exc + raise ValueError(message or "USER_CODE: handler process failed") + if payload[0] != _HANDLER_RESPONSE_OK: + raise ValueError("PROTOCOL: handler process returned an unknown status") + return payload[1:] finally: selector.close() if process.poll() is None: From 7a149c87bfcffa7ad40543e42ad211db901c7d40 Mon Sep 17 00:00:00 2001 From: iamlinjunhong <1030420200@qq.com> Date: Wed, 9 Sep 2026 22:18:10 +0800 Subject: [PATCH 016/202] refactor(udf): clarify execution sequence ownership --- pkg/udf/python/gateway.go | 28 ++++++++++++++-------------- pkg/udf/python/worker/test_worker.py | 12 ++++++------ pkg/udf/python/worker/worker.py | 19 ++++++++++++------- 3 files changed, 32 insertions(+), 27 deletions(-) diff --git a/pkg/udf/python/gateway.go b/pkg/udf/python/gateway.go index cfcffad2689b0..3c7fdbd6b9ced 100644 --- a/pkg/udf/python/gateway.go +++ b/pkg/udf/python/gateway.go @@ -98,19 +98,19 @@ func (g *Gateway) Close() error { } type openPayload struct { - Handler string `json:"handler"` - Source string `json:"source"` - Mode string `json:"mode"` - NullPolicy string `json:"null_policy"` - ABIContract string `json:"abi_contract"` - AdapterVersion string `json:"adapter_version"` - SDKVersion string `json:"sdk_version"` - Context map[string]string `json:"context,omitempty"` - Args []TypeDescriptor `json:"args"` - Return TypeDescriptor `json:"return"` - MaxBatchBytes int64 `json:"max_batch_bytes"` - MaxBatchRows int64 `json:"max_batch_rows"` - HandlerTimeout float64 `json:"handler_timeout_seconds"` + Handler string `json:"handler"` + Source string `json:"source"` + Mode string `json:"mode"` + NullPolicy string `json:"null_policy"` + ABIContract string `json:"abi_contract"` + AdapterVersion string `json:"adapter_version"` + SDKVersion string `json:"sdk_version"` + Context map[string]string `json:"context,omitempty"` + Args []TypeDescriptor `json:"args"` + Return TypeDescriptor `json:"return"` + MaxBatchBytes int64 `json:"max_batch_bytes"` + MaxBatchRows int64 `json:"max_batch_rows"` + HandlerTimeoutSeconds float64 `json:"handler_timeout_seconds"` } func (g *Gateway) Execute(ctx context.Context, invocation *udf.Invocation, result vector.FunctionResultWrapper, mp *mpool.MPool) error { @@ -146,7 +146,7 @@ func (g *Gateway) Execute(ctx context.Context, invocation *udf.Invocation, resul if err != nil { return err } - openBody, err := json.Marshal(openPayload{Handler: invocation.Handler, Source: invocation.Source, Mode: invocation.Mode, NullPolicy: invocation.NullPolicy, ABIContract: invocation.ABIContract, AdapterVersion: invocation.AdapterVersion, SDKVersion: invocation.SDKVersion, Context: cloneMap(invocation.Context), Args: args, Return: returnDescriptor, MaxBatchBytes: g.cfg.MaxBatchBytes, MaxBatchRows: g.cfg.MaxBatchRows, HandlerTimeout: g.cfg.RequestTimeout.Seconds()}) + openBody, err := json.Marshal(openPayload{Handler: invocation.Handler, Source: invocation.Source, Mode: invocation.Mode, NullPolicy: invocation.NullPolicy, ABIContract: invocation.ABIContract, AdapterVersion: invocation.AdapterVersion, SDKVersion: invocation.SDKVersion, Context: cloneMap(invocation.Context), Args: args, Return: returnDescriptor, MaxBatchBytes: g.cfg.MaxBatchBytes, MaxBatchRows: g.cfg.MaxBatchRows, HandlerTimeoutSeconds: g.cfg.RequestTimeout.Seconds()}) if err != nil { return fmt.Errorf("python udf: encode open payload: %w", err) } diff --git a/pkg/udf/python/worker/test_worker.py b/pkg/udf/python/worker/test_worker.py index 4754fd24fff39..75349c4767681 100644 --- a/pkg/udf/python/worker/test_worker.py +++ b/pkg/udf/python/worker/test_worker.py @@ -201,11 +201,11 @@ def test_statement_context_is_whitelisted_and_frozen(self): ) def test_terminal_admission_does_not_evict_live_tombstones(self): - old_records = worker.MAX_TERMINAL_RECORDS - old_bytes = worker.MAX_TERMINAL_BYTES + old_records = worker.MAX_LEDGER_ENTRIES + old_bytes = worker.MAX_LEDGER_BYTES try: - worker.MAX_TERMINAL_RECORDS = 1 - worker.MAX_TERMINAL_BYTES = 1 << 20 + worker.MAX_LEDGER_ENTRIES = 1 + worker.MAX_LEDGER_BYTES = 1 << 20 server = worker.RoutineFlightServer("grpc://127.0.0.1:0") first = (1, "statement", "group", 1, "invocation", 1) state = server._admit(first) @@ -222,8 +222,8 @@ def test_terminal_admission_does_not_evict_live_tombstones(self): second = server._admit((1, "statement", "group", 1, "second", 1)) self.assertIsNotNone(second) finally: - worker.MAX_TERMINAL_RECORDS = old_records - worker.MAX_TERMINAL_BYTES = old_bytes + worker.MAX_LEDGER_ENTRIES = old_records + worker.MAX_LEDGER_BYTES = old_bytes def test_active_fence_cannot_be_admitted_twice(self): server = worker.RoutineFlightServer("grpc://127.0.0.1:0") diff --git a/pkg/udf/python/worker/worker.py b/pkg/udf/python/worker/worker.py index 583ab1df8aabf..e29caf8a02906 100644 --- a/pkg/udf/python/worker/worker.py +++ b/pkg/udf/python/worker/worker.py @@ -40,8 +40,11 @@ PROTOCOL_VERSION = 1 MAX_CONTROL_BYTES = 1 << 20 -MAX_TERMINAL_RECORDS = 10000 -MAX_TERMINAL_BYTES = 16 << 20 +# Admission bounds cover active fences and retained terminal tombstones. The +# names deliberately describe the whole ledger so a future cleanup change +# cannot mistake active work for reclaimable terminal state. +MAX_LEDGER_ENTRIES = 10000 +MAX_LEDGER_BYTES = 16 << 20 TERMINAL_TTL_SECONDS = 300.0 ACK_TIMEOUT_SECONDS = 60.0 MAX_EXECUTION_FRAME_BYTES = 1 << 30 @@ -859,6 +862,7 @@ def __init__(self, tuple_value: Dict[str, Any], terminal_bytes: int): self.tuple = tuple_value self.terminal_bytes = terminal_bytes self.condition = threading.Condition() + self.last_input = 0 self.last_result = 0 self.acked_result = 0 self.finish_id: Optional[str] = None @@ -929,9 +933,9 @@ def _admit(self, key: tuple) -> _InvocationState: raise ValueError("PROTOCOL: invocation fence is active") if key in self._terminal: raise ValueError("PROTOCOL: invocation fence is terminal") - if len(self._active) + len(self._terminal) >= MAX_TERMINAL_RECORDS: + if len(self._active) + len(self._terminal) >= MAX_LEDGER_ENTRIES: raise ValueError("RESOURCE_EXHAUSTED: terminal ledger entries are full") - if self._active_bytes + self._terminal_bytes + size > MAX_TERMINAL_BYTES: + if self._active_bytes + self._terminal_bytes + size > MAX_LEDGER_BYTES: raise ValueError("RESOURCE_EXHAUSTED: terminal ledger bytes are full") state = _InvocationState({}, size) self._active[key] = state @@ -1040,7 +1044,7 @@ def do_exchange(self, context, descriptor, reader, writer): _require_tuple(control["tuple"], open_control["tuple"]) if control["kind"] != "InputBatch": raise ValueError("PROTOCOL: data batch is missing InputBatch") sequence = _required_uint64(control, "sequence") - if sequence != state.last_result + 1: raise ValueError("PROTOCOL: input sequence is not contiguous") + if sequence != state.last_input + 1: raise ValueError("PROTOCOL: input sequence is not contiguous") batch = chunk.data if batch.num_rows <= 0: raise ValueError("PROTOCOL: empty input batch") if batch.num_rows > max_batch_rows: raise ValueError("RESOURCE_EXHAUSTED: input batch has too many rows") @@ -1068,6 +1072,7 @@ def do_exchange(self, context, descriptor, reader, writer): _validate_array_values(output_array, result_descriptor) if pa.ipc.get_record_batch_size(output_batch) > max_batch_bytes: raise ValueError("RESOURCE_EXHAUSTED: output batch exceeds byte limit") + state.last_input = sequence state.last_result = sequence writer.write_metadata(_encode_control({"kind": "InputConsumed", "tuple": open_control["tuple"], "sequence": sequence})) writer.write_with_metadata(output_batch, _encode_control({"kind": "ResultBatch", "tuple": open_control["tuple"], "sequence": sequence})) @@ -1076,14 +1081,14 @@ def do_exchange(self, context, descriptor, reader, writer): control = _decode_control(chunk.app_metadata) _require_tuple(control["tuple"], open_control["tuple"]) if control["kind"] == "EndInput": - if ended or _required_uint64(control, "last_sequence", allow_zero=True) != state.last_result: raise ValueError("PROTOCOL: invalid EndInput") + if ended or _required_uint64(control, "last_sequence", allow_zero=True) != state.last_input: raise ValueError("PROTOCOL: invalid EndInput") ended = True elif control["kind"] != "OpenInvocation": raise ValueError("PROTOCOL: unexpected control without Arrow data") if not ended: raise ValueError("PROTOCOL: input stream ended before EndInput") if state.acked_result != state.last_result: raise ValueError("PROTOCOL: result is not acknowledged") state.finish_id = _uuid.uuid4().hex - writer.write_metadata(_encode_control({"kind": "Finish", "tuple": open_control["tuple"], "status": "OK", "finish_id": state.finish_id, "last_sequence": state.last_result})) + writer.write_metadata(_encode_control({"kind": "Finish", "tuple": open_control["tuple"], "status": "OK", "finish_id": state.finish_id, "last_sequence": state.last_input})) state.wait_finish_ack(context) except Exception as exc: if writer_started and key is not None: From bd60a90bb088ac9deabdeb2300e89bdd43cbd52a Mon Sep 17 00:00:00 2001 From: iamlinjunhong <1030420200@qq.com> Date: Wed, 9 Sep 2026 22:20:09 +0800 Subject: [PATCH 017/202] fix(udf): encode UUID width in the ABI fingerprint --- pkg/udf/python/arrow.go | 9 ++++++++- pkg/udf/python/arrow_test.go | 3 +++ pkg/udf/python/worker/test_worker.py | 1 + pkg/udf/python/worker/worker.py | 2 +- 4 files changed, 13 insertions(+), 2 deletions(-) diff --git a/pkg/udf/python/arrow.go b/pkg/udf/python/arrow.go index 707483a71e37e..ba7812ea489be 100644 --- a/pkg/udf/python/arrow.go +++ b/pkg/udf/python/arrow.go @@ -99,11 +99,18 @@ func (d TypeDescriptor) Fingerprint() (string, error) { if err != nil { return "", err } + physicalFingerprint := physical.Fingerprint() + if types.T(d.TypeID) == types.T_uuid { + // Arrow-Go's FixedSizeBinary fingerprint omits ByteWidth. Keep the + // UUID width in the cross-language fingerprint as part of the frozen + // physical contract instead of relying only on the local type check. + physicalFingerprint = "@P[16]" + } h := sha256.New() h.Write([]byte("matrixone-python-udf-type\x00")) h.Write(canonical) h.Write([]byte{0}) - h.Write([]byte(physical.Fingerprint())) + h.Write([]byte(physicalFingerprint)) return hex.EncodeToString(h.Sum(nil)), nil } diff --git a/pkg/udf/python/arrow_test.go b/pkg/udf/python/arrow_test.go index 7e388e4816c03..b658c67e2ecfb 100644 --- a/pkg/udf/python/arrow_test.go +++ b/pkg/udf/python/arrow_test.go @@ -95,6 +95,9 @@ func TestArrowValueDomainRejectsWidthAndTimeOverflow(t *testing.T) { func TestArrowTypeValidationIncludesFixedBinaryWidth(t *testing.T) { descriptor, err := NewTypeDescriptor(types.T_uuid.ToType()) require.NoError(t, err) + fingerprint, err := descriptor.Fingerprint() + require.NoError(t, err) + require.Equal(t, "370e2939c178acee55aaae27a7bcf92937a203e2788e9e0140d84884be042780", fingerprint) builder := array.NewFixedSizeBinaryBuilder(memory.NewGoAllocator(), &arrow.FixedSizeBinaryType{ByteWidth: 1}) builder.Append([]byte{0xff}) values := builder.NewFixedSizeBinaryArray() diff --git a/pkg/udf/python/worker/test_worker.py b/pkg/udf/python/worker/test_worker.py index 75349c4767681..ce331a65fab90 100644 --- a/pkg/udf/python/worker/test_worker.py +++ b/pkg/udf/python/worker/test_worker.py @@ -26,6 +26,7 @@ def test_go_python_fingerprint_vectors(self): ({"type_id": worker.INT64, "offset_width": 32}, "482d3c55b8a9d662c2b41326b3ca55cbae65153e662c47c4919ba020d006262b"), ({"type_id": worker.VARCHAR, "width": 65535, "charset": 3, "offset_width": 32}, "e35cb66adb4bce519a6f3ec9cfa8a33adad6202cbe678f5fecffdeb28d65b824"), ({"type_id": worker.JSON, "offset_width": 32, "json_encoding": "canonical_text"}, "b156af5241a070ff80a287da19c46d1593dabee4fb5429f964452e711cd275eb"), + ({"type_id": worker.UUID, "offset_width": 0}, "370e2939c178acee55aaae27a7bcf92937a203e2788e9e0140d84884be042780"), ({"type_id": worker.DATE, "offset_width": 32, "temporal_encoding": "sql_zero_struct"}, "8c401bde992eb173ca8aedca97dbd9f47453b7a860fae199ae0439307e858b4b"), ] for descriptor, expected in cases: diff --git a/pkg/udf/python/worker/worker.py b/pkg/udf/python/worker/worker.py index e29caf8a02906..d5ded28d22e9f 100644 --- a/pkg/udf/python/worker/worker.py +++ b/pkg/udf/python/worker/worker.py @@ -355,7 +355,7 @@ def _physical_fingerprint(descriptor: Dict[str, Any]) -> str: if type_id in (CHAR, VARCHAR, TEXT, JSON): return "@d" if int(descriptor.get("offset_width", 32)) == 64 else "@N" if type_id == UUID: - return "@P" + return "@P[16]" if type_id in (DECIMAL64, DECIMAL128): precision = int(descriptor.get("width") or (18 if type_id == DECIMAL64 else 38)) return f"@X[128,{precision},{int(descriptor.get('scale') or 0)}]" From 37f85dc40074925bc398560ad53eac222e301604 Mon Sep 17 00:00:00 2001 From: iamlinjunhong <1030420200@qq.com> Date: Wed, 9 Sep 2026 23:06:54 +0800 Subject: [PATCH 018/202] fix(udf): close Python execution channel contracts --- pkg/udf/python/worker/test_worker.py | 218 ++++++++++++++++++++++++++- pkg/udf/python/worker/worker.py | 178 +++++++++++++++++----- 2 files changed, 355 insertions(+), 41 deletions(-) diff --git a/pkg/udf/python/worker/test_worker.py b/pkg/udf/python/worker/test_worker.py index ce331a65fab90..3fb9472245144 100644 --- a/pkg/udf/python/worker/test_worker.py +++ b/pkg/udf/python/worker/test_worker.py @@ -3,12 +3,19 @@ import datetime import importlib.util import json +import os import pathlib +import signal import struct +import subprocess import sys +import tempfile +import threading import time +import types import unittest import uuid +from unittest import mock import pyarrow as pa @@ -96,6 +103,11 @@ def test_decimal_precision_and_vector_child_validity_are_checked(self): with self.assertRaisesRegex(ValueError, "child validity"): worker._output_array(vector, vector_descriptor, 2) + valid = pa.array([None, [1.0, 2.0]], type=pa.list_(pa.float32(), 2)) + self.assertEqual(valid.to_pylist(), worker._output_array(valid, vector_descriptor, 2).to_pylist()) + visible = valid.slice(1, 1) + self.assertEqual(visible.to_pylist(), worker._output_array(visible, vector_descriptor, 1).to_pylist()) + def test_scalar_vector_null_keeps_fixed_size_child_slots(self): descriptor = {"type_id": worker.VECF32, "width": 2, "offset_width": 0} array = worker._output_array( @@ -158,8 +170,212 @@ def test_handler_stdout_cannot_inject_parent_protocol(self): "max_batch_bytes": 1 << 20, "input": worker._serialize_record_batch(batch), } - with self.assertRaisesRegex(ValueError, "handler process"): + result = worker._deserialize_record_batch( worker._run_handler_process(None, request, 3) + ) + self.assertEqual([1], result.column(0).to_pylist()) + + def test_handler_cannot_forge_completion_on_stdout(self): + descriptor = {"type_id": worker.INT64, "offset_width": 32} + batch = pa.RecordBatch.from_arrays([pa.array([77], type=pa.int64())], ["arg_0"]) + result_batch = pa.RecordBatch.from_arrays( + [pa.array([77], type=pa.int64())], ["result"] + ) + frame = struct.pack(">Q", len(bytes([worker._HANDLER_RESPONSE_OK]) + worker._serialize_record_batch(result_batch))) + frame += bytes([worker._HANDLER_RESPONSE_OK]) + worker._serialize_record_batch(result_batch) + request = { + "source": ( + "import os,time\n" + f"def f(ctx, x):\n os.write(1, {frame!r})\n" + " time.sleep(5)\n" + " raise RuntimeError('handler never returned success')\n" + ), + "handler": "f", + "mode": worker.MODE_SCALAR, + "null_policy": worker.NULL_CALL, + "sdk_version": worker.SDK_VERSION, + "context": None, + "args": [descriptor], + "return": descriptor, + "max_batch_bytes": 1 << 20, + "input": worker._serialize_record_batch(batch), + } + with self.assertRaisesRegex(TimeoutError, "handler execution timeout"): + worker._run_handler_process(None, request, 0.1) + + def test_end_input_rejects_late_batch_without_running_handler(self): + server = worker.RoutineFlightServer("grpc://127.0.0.1:0") + fence = { + "account_id": 1, + "statement_id": "review", + "group_id": "group", + "group_epoch": 1, + "invocation_id": "late-input", + "lease_epoch": 1, + } + key = worker._tuple_key(fence) + descriptor = {"type_id": worker.INT64, "offset_width": 32} + batch = pa.RecordBatch.from_arrays( + [pa.array([1], type=pa.int64())], + schema=pa.schema([worker._field("arg_0", descriptor)]), + ) + payload = { + "mode": worker.MODE_SCALAR, + "null_policy": worker.NULL_CALL, + "abi_contract": worker.ABI_CONTRACT, + "adapter_version": worker.ADAPTER_VERSION, + "sdk_version": worker.SDK_VERSION, + "args": [descriptor], + "return": descriptor, + "source": "def f(ctx, x): return x", + "handler": "f", + "max_batch_bytes": 1 << 20, + "max_batch_rows": 1024, + "handler_timeout_seconds": 2, + } + messages = [] + + def control(kind, **fields): + return worker._encode_control(dict(kind=kind, tuple=fence, **fields)) + + chunks = iter( + [ + types.SimpleNamespace( + data=None, + app_metadata=control("EndInput", last_sequence=0), + ), + types.SimpleNamespace( + data=batch, + app_metadata=control("InputBatch", sequence=1), + ), + ] + ) + reader = types.SimpleNamespace(schema=batch.schema, read_chunk=lambda: next(chunks)) + + class Writer: + def begin(self, schema): + pass + + def write_metadata(self, data): + messages.append(worker._decode_control(data)) + + def write_with_metadata(self, record, data): + messages.append(worker._decode_control(data)) + + handler = mock.Mock() + try: + with mock.patch.object(worker, "_run_handler_process", handler): + with self.assertRaisesRegex(ValueError, "after EndInput"): + server.do_exchange( + types.SimpleNamespace(is_cancelled=lambda: False), + types.SimpleNamespace( + command=control("OpenInvocation", payload=payload) + ), + reader, + Writer(), + ) + finally: + server.shutdown() + self.assertEqual(0, handler.call_count) + + @unittest.skipUnless(os.name == "posix", "process-group test") + def test_descendant_is_killed_after_handler_leader_exits(self): + with tempfile.TemporaryDirectory(prefix="mo-udf-owned-child-") as artifact: + witness = pathlib.Path(artifact) / "owned-child.json" + descendant = "import os,time; time.sleep(0.15); os.close(1); time.sleep(10)" + source = ( + "import json,os,pathlib,subprocess,sys\n" + "def f(ctx,x):\n" + f" child=subprocess.Popen([sys.executable,'-c',{descendant!r}],stdin=subprocess.DEVNULL)\n" + f" pathlib.Path({str(witness)!r}).write_text(json.dumps(dict(pid=child.pid,pgid=os.getpgrp())))\n" + " os._exit(7)\n" + ) + descriptor = {"type_id": worker.INT64, "offset_width": 32} + batch = pa.RecordBatch.from_arrays([pa.array([1], type=pa.int64())], ["arg_0"]) + request = { + "source": source, + "handler": "f", + "mode": worker.MODE_SCALAR, + "null_policy": worker.NULL_CALL, + "sdk_version": worker.SDK_VERSION, + "context": None, + "args": [descriptor], + "return": descriptor, + "max_batch_bytes": 1 << 20, + "input": worker._serialize_record_batch(batch), + } + try: + with self.assertRaisesRegex(ValueError, "handler process"): + worker._run_handler_process(None, request, 2) + observed = json.loads(witness.read_text()) + try: + survived = os.getpgid(observed["pid"]) == observed["pgid"] + except ProcessLookupError: + survived = False + self.assertFalse(survived) + finally: + if witness.exists(): + owned = json.loads(witness.read_text()) + try: + if os.getpgid(owned["pid"]) == owned["pgid"]: + os.kill(owned["pid"], signal.SIGKILL) + except ProcessLookupError: + pass + + @unittest.skipUnless(os.name == "posix", "non-blocking pipe test") + def test_request_write_observes_handler_deadline(self): + created = [] + entered = threading.Event() + real_popen = subprocess.Popen + + def no_reader(argv, **kwargs): + process = real_popen( + [sys.executable, "-c", "import time; time.sleep(10)"], **kwargs + ) + created.append(process) + entered.set() + return process + + descriptor = {"type_id": worker.INT64, "offset_width": 32} + batch = pa.RecordBatch.from_arrays([pa.array([1], type=pa.int64())], ["arg_0"]) + request = { + "source": "def f(ctx, x): return x", + "handler": "f", + "mode": worker.MODE_SCALAR, + "null_policy": worker.NULL_CALL, + "sdk_version": worker.SDK_VERSION, + "context": None, + "args": [descriptor], + "return": descriptor, + "max_batch_bytes": 1 << 20, + "input": worker._serialize_record_batch(batch), + "probe_padding": b"x" * (1 << 20), + } + outcome = [] + + def invoke(): + try: + worker._run_handler_process(None, request, 0.05) + except Exception as exc: + outcome.append(type(exc).__name__) + + thread = threading.Thread(target=invoke, daemon=True) + try: + with mock.patch.object(worker.subprocess, "Popen", side_effect=no_reader): + thread.start() + self.assertTrue(entered.wait(1)) + thread.join(0.5) + self.assertFalse(thread.is_alive()) + self.assertEqual(["TimeoutError"], outcome) + finally: + for process in created: + if process.poll() is None: + os.killpg(process.pid, signal.SIGKILL) + thread.join(2) + for process in created: + process.wait(timeout=1) + if process.stdin: + process.stdin.close() def test_zero_argument_vector_uses_context_rows(self): descriptor = {"type_id": worker.INT64, "offset_width": 32} diff --git a/pkg/udf/python/worker/worker.py b/pkg/udf/python/worker/worker.py index d5ded28d22e9f..5ce252a138f3e 100644 --- a/pkg/udf/python/worker/worker.py +++ b/pkg/udf/python/worker/worker.py @@ -50,6 +50,7 @@ MAX_EXECUTION_FRAME_BYTES = 1 << 30 _HANDLER_RESPONSE_ERROR = 0 _HANDLER_RESPONSE_OK = 1 +_HANDLER_RESPONSE_FD_ENV = "MATRIXONE_HANDLER_RESPONSE_FD" _MICROS_PER_SECOND = 1_000_000 _MAX_TIME_MICROS = (838 * 60 * 60 + 59 * 60 + 59) * _MICROS_PER_SECOND _MIN_TIMESTAMP = _datetime.datetime(1970, 1, 1, 0, 0, 1, tzinfo=_datetime.timezone.utc) @@ -653,10 +654,18 @@ def _validate_array_values(array: pa.Array, descriptor: Dict[str, Any]) -> None: elif type_id == DATETIME: _check_scalar(SqlDatetime(False, value), descriptor) else: _check_scalar(SqlTimestamp(False, value), descriptor) elif type_id in (VECF32, VECF64): - values = array.values - for index in range(len(values)): - if not values[index].is_valid: - raise ValueError("TYPE_CONTRACT: vector child validity must be non-null") + # A FixedSizeListArray may have both null parents and a non-zero row + # offset. Its backing child array can therefore contain nulls that + # are either hidden behind a null parent or outside the visible slice. + # Only the children belonging to visible, non-null rows are part of + # the SQL value domain. + for row in range(len(array)): + value = array[row] + if not value.is_valid: + continue + for child in value.values: + if not child.is_valid: + raise ValueError("TYPE_CONTRACT: vector child validity must be non-null") elif type_id in (CHAR, VARCHAR, TEXT, JSON, BINARY, VARBINARY, BLOB, UUID, TIME, DECIMAL64, DECIMAL128): for index in range(len(array)): if not array[index].is_valid: continue @@ -757,6 +766,14 @@ def _write_execution_frame(stream, payload: bytes) -> None: def _execute_handler_subprocess() -> None: + response_fd_text = os.environ.pop(_HANDLER_RESPONSE_FD_ENV, None) + if response_fd_text is None: + raise ValueError("PROTOCOL: handler response channel is missing") + try: + response_fd = int(response_fd_text) + response_stream = os.fdopen(response_fd, "wb", buffering=0, closefd=True) + except (OSError, TypeError, ValueError) as exc: + raise ValueError("PROTOCOL: handler response channel is invalid") from exc try: size = struct.unpack(">Q", _read_exact(sys.stdin.buffer, 8))[0] if size > MAX_EXECUTION_FRAME_BYTES: @@ -765,7 +782,12 @@ def _execute_handler_subprocess() -> None: response = bytes([_HANDLER_RESPONSE_OK]) + _execute_handler_batch(request) except Exception as exc: response = bytes([_HANDLER_RESPONSE_ERROR]) + _safe_error(exc).encode("utf-8") - _write_execution_frame(sys.stdout.buffer, response) + try: + # This descriptor is created by the adapter and is distinct from the + # process stdout/stderr descriptors available to handler code. + _write_execution_frame(response_stream, response) + finally: + response_stream.close() def _context_is_cancelled(context) -> bool: @@ -778,14 +800,16 @@ def _context_is_cancelled(context) -> bool: def _kill_execution_process(process: subprocess.Popen) -> None: - if process.poll() is None: - if os.name == "posix": - try: - os.killpg(process.pid, signal.SIGKILL) - except ProcessLookupError: - pass - else: - process.kill() + # The leader can already have exited while one of its descendants keeps + # the execution alive. Always address the process group first; checking + # poll() before killpg() leaves that descendant outside the cleanup path. + if os.name == "posix": + try: + os.killpg(process.pid, signal.SIGKILL) + except ProcessLookupError: + pass + elif process.poll() is None: + process.kill() try: process.wait(timeout=1.0) except subprocess.TimeoutExpired: @@ -793,27 +817,63 @@ def _kill_execution_process(process: subprocess.Popen) -> None: process.wait() +def _execution_group_alive(process: subprocess.Popen) -> bool: + if os.name != "posix": + return process.poll() is None + try: + os.killpg(process.pid, 0) + except ProcessLookupError: + return False + except PermissionError: + return True + return True + + def _run_handler_process(context, request: Dict[str, Any], timeout_seconds: float) -> bytes: + deadline = time.monotonic() + timeout_seconds request_wire = pickle.dumps(request, protocol=5) if len(request_wire) > MAX_EXECUTION_FRAME_BYTES: raise ValueError("RESOURCE_EXHAUSTED: execution request is too large") + request_frame = struct.pack(">Q", len(request_wire)) + request_wire + response_read_fd, response_write_fd = os.pipe() + process = None + selector = None popen_kwargs = { "stdin": subprocess.PIPE, - "stdout": subprocess.PIPE, + # Handler output is diagnostic-only. It must never share the + # adapter response channel, even when user code writes directly to + # file descriptor 1. + "stdout": subprocess.DEVNULL, "stderr": subprocess.DEVNULL, "close_fds": True, } if os.name == "posix": popen_kwargs["start_new_session"] = True - process = subprocess.Popen([sys.executable, os.path.abspath(__file__), "--execute-handler"], **popen_kwargs) - selector = selectors.DefaultSelector() + popen_kwargs["pass_fds"] = (response_write_fd,) + child_env = os.environ.copy() + child_env[_HANDLER_RESPONSE_FD_ENV] = str(response_write_fd) + # The extra descriptor is a protocol-channel separation mechanism, not a + # sandbox boundary. An unisolated handler with arbitrary OS access can + # still inspect inherited descriptors; production isolation must enforce + # the stronger policy that handler code cannot write this channel. + popen_kwargs["env"] = child_env response = bytearray() expected = None + response_eof = False + request_offset = 0 try: - _write_execution_frame(process.stdin, request_wire) - process.stdin.close() - selector.register(process.stdout, selectors.EVENT_READ) - deadline = time.monotonic() + timeout_seconds + process = subprocess.Popen( + [sys.executable, os.path.abspath(__file__), "--execute-handler"], + **popen_kwargs, + ) + os.close(response_write_fd) + response_write_fd = -1 + selector = selectors.DefaultSelector() + stdin_fd = process.stdin.fileno() + os.set_blocking(stdin_fd, False) + os.set_blocking(response_read_fd, False) + selector.register(stdin_fd, selectors.EVENT_WRITE, "request") + selector.register(response_read_fd, selectors.EVENT_READ, "response") while True: if _context_is_cancelled(context): raise TimeoutError("DEADLINE_EXCEEDED: handler execution cancelled") @@ -821,20 +881,47 @@ def _run_handler_process(context, request: Dict[str, Any], timeout_seconds: floa if remaining <= 0: raise TimeoutError("DEADLINE_EXCEEDED: handler execution timeout") events = selector.select(min(remaining, 0.1)) - if not events: - continue - chunk = os.read(process.stdout.fileno(), 65536) - if not chunk: - if expected is None or len(response) < expected: - raise ValueError("USER_CODE: handler process exited without a response") - break - response.extend(chunk) - if expected is None and len(response) >= 8: - expected = struct.unpack(">Q", response[:8])[0] - if expected > MAX_EXECUTION_FRAME_BYTES: - raise ValueError("RESOURCE_EXHAUSTED: execution response is too large") - if expected is not None and len(response) >= expected + 8: - break + for event, _ in events: + if event.data == "request": + try: + written = os.write(stdin_fd, request_frame[request_offset:]) + except BlockingIOError: + continue + except BrokenPipeError as exc: + raise ValueError("USER_CODE: handler process closed its request channel") from exc + request_offset += written + if request_offset == len(request_frame): + selector.unregister(stdin_fd) + process.stdin.close() + else: + try: + chunk = os.read(response_read_fd, 65536) + except BlockingIOError: + continue + if not chunk: + response_eof = True + selector.unregister(response_read_fd) + if expected is None or len(response) < expected + 8: + raise ValueError("USER_CODE: handler process exited without a response") + else: + response.extend(chunk) + if expected is None and len(response) >= 8: + expected = struct.unpack(">Q", response[:8])[0] + if expected > MAX_EXECUTION_FRAME_BYTES: + raise ValueError("RESOURCE_EXHAUSTED: execution response is too large") + if expected is not None and len(response) > expected + 8: + raise ValueError("PROTOCOL: handler process returned trailing response data") + + # A complete frame is not enough. The adapter wrapper must have + # returned normally, and all of its process-group descendants + # must be gone before the adapter accepts the result. + if process.poll() is not None: + if process.returncode != 0: + raise ValueError("USER_CODE: handler process exited abnormally") + if _execution_group_alive(process): + raise ValueError("USER_CODE: handler process left descendant processes") + if response_eof and expected is not None and len(response) == expected + 8: + break payload = bytes(response[8 : expected + 8]) if not payload: raise ValueError("PROTOCOL: handler process returned an empty response") @@ -848,12 +935,15 @@ def _run_handler_process(context, request: Dict[str, Any], timeout_seconds: floa raise ValueError("PROTOCOL: handler process returned an unknown status") return payload[1:] finally: - selector.close() - if process.poll() is None: + if selector is not None: + selector.close() + if process is not None: _kill_execution_process(process) - else: - process.wait() - process.stdout.close() + if process.stdin is not None: + process.stdin.close() + if response_write_fd >= 0: + os.close(response_write_fd) + os.close(response_read_fd) @@ -1037,6 +1127,8 @@ def do_exchange(self, context, descriptor, reader, writer): except StopIteration: break if chunk.data is not None: + if ended: + raise ValueError("PROTOCOL: input arrived after EndInput") if schema is None: schema = reader.schema _validate_schema(schema, args) @@ -1081,7 +1173,13 @@ def do_exchange(self, context, descriptor, reader, writer): control = _decode_control(chunk.app_metadata) _require_tuple(control["tuple"], open_control["tuple"]) if control["kind"] == "EndInput": - if ended or _required_uint64(control, "last_sequence", allow_zero=True) != state.last_input: raise ValueError("PROTOCOL: invalid EndInput") + last_sequence = _required_uint64(control, "last_sequence", allow_zero=True) + if ended: + if last_sequence != state.last_input: + raise ValueError("PROTOCOL: EndInput changed after input was closed") + continue + if last_sequence != state.last_input: + raise ValueError("PROTOCOL: invalid EndInput") ended = True elif control["kind"] != "OpenInvocation": raise ValueError("PROTOCOL: unexpected control without Arrow data") From c83d402227f212a34099c0cae3afe33cbf0de944 Mon Sep 17 00:00:00 2001 From: iamlinjunhong <1030420200@qq.com> Date: Thu, 10 Sep 2026 10:47:36 +0800 Subject: [PATCH 019/202] fix(udf): close Python gateway response contracts --- pkg/udf/python/arrow.go | 23 +++++++++++++--- pkg/udf/python/arrow_test.go | 32 ++++++++++++++++++++++ pkg/udf/python/gateway.go | 47 +++++++++++++++++++++++---------- pkg/udf/python/gateway_test.go | 18 +++++++++++++ pkg/udf/python/worker/worker.py | 20 ++++++++++++-- 5 files changed, 121 insertions(+), 19 deletions(-) diff --git a/pkg/udf/python/arrow.go b/pkg/udf/python/arrow.go index ba7812ea489be..484d16aff3b09 100644 --- a/pkg/udf/python/arrow.go +++ b/pkg/udf/python/arrow.go @@ -540,12 +540,23 @@ func DecodeRecordBatch(schemaFrame, batchFrame ArrowFrame, maxBytes int64) (arro if len(schemaFrame.Header) == 0 || len(batchFrame.Header) == 0 || maxBytes <= 0 { return nil, fmt.Errorf("invalid Arrow output frames") } - if _, err := arrowipc.InspectMessage(context.Background(), schemaFrame.Header, arrowipc.ValidationOptions{MaxMetadataBytes: arrowipc.DefaultMaxMetadataBytes, MaxBodyBytes: 0, BodyEnvelopeBytes: 0, MaxDecodedRecordBytes: 1}); err != nil { + if len(schemaFrame.Body) != 0 { + return nil, fmt.Errorf("invalid Arrow schema frame body") + } + schemaInfo, err := arrowipc.InspectMessage(context.Background(), schemaFrame.Header, arrowipc.ValidationOptions{MaxMetadataBytes: arrowipc.DefaultMaxMetadataBytes, MaxBodyBytes: 0, BodyEnvelopeBytes: 0, MaxDecodedRecordBytes: 1}) + if err != nil { return nil, err } - if _, err := arrowipc.InspectMessage(context.Background(), batchFrame.Header, arrowipc.ValidationOptions{MaxMetadataBytes: arrowipc.DefaultMaxMetadataBytes, MaxBodyBytes: maxBytes, BodyEnvelopeBytes: int64(len(batchFrame.Body)), Body: batchFrame.Body, ValidateBody: true, MaxDecodedRecordBytes: maxBytes}); err != nil { + if schemaInfo.HeaderType != arrowipc.MessageHeaderSchema { + return nil, fmt.Errorf("invalid Arrow schema frame header %d", schemaInfo.HeaderType) + } + batchInfo, err := arrowipc.InspectMessage(context.Background(), batchFrame.Header, arrowipc.ValidationOptions{MaxMetadataBytes: arrowipc.DefaultMaxMetadataBytes, MaxBodyBytes: maxBytes, BodyEnvelopeBytes: int64(len(batchFrame.Body)), Body: batchFrame.Body, ValidateBody: true, MaxDecodedRecordBytes: maxBytes}) + if err != nil { return nil, err } + if batchInfo.HeaderType != arrowipc.MessageHeaderRecordBatch { + return nil, fmt.Errorf("invalid Arrow record frame header %d", batchInfo.HeaderType) + } stream := make([]byte, 0, len(schemaFrame.Header)+len(batchFrame.Header)+len(batchFrame.Body)+32) stream = appendIPCFrame(stream, schemaFrame.Header, schemaFrame.Body) stream = appendIPCFrame(stream, batchFrame.Header, batchFrame.Body) @@ -583,7 +594,13 @@ func appendIPCFrame(dst, header, body []byte) []byte { } func AppendArrowResult(descriptor TypeDescriptor, input arrow.Array, result vector.FunctionResultWrapper, mp *mpool.MPool) error { - if input == nil || result == nil || input.Len() == 0 { + if input == nil { + return fmt.Errorf("TYPE_CONTRACT: missing Arrow result") + } + if result == nil { + return fmt.Errorf("python udf: missing result wrapper") + } + if input.Len() == 0 { return nil } if mp == nil { diff --git a/pkg/udf/python/arrow_test.go b/pkg/udf/python/arrow_test.go index b658c67e2ecfb..716290debabf8 100644 --- a/pkg/udf/python/arrow_test.go +++ b/pkg/udf/python/arrow_test.go @@ -62,6 +62,38 @@ func TestEncodeRecordBatchUsesFlightPayloadFrames(t *testing.T) { require.Equal(t, int64(3), decodedValues.Value(2)) } +func TestDecodeRecordBatchRequiresSchemaAndRecordHeaders(t *testing.T) { + descriptor, err := NewTypeDescriptor(types.T_int64.ToType()) + require.NoError(t, err) + field, err := descriptor.Field("arg_0") + require.NoError(t, err) + builder := array.NewInt64Builder(memory.NewGoAllocator()) + builder.Append(1) + values := builder.NewInt64Array() + defer values.Release() + record := array.NewRecordBatch(arrow.NewSchema([]arrow.Field{field}, nil), []arrow.Array{values}, 1) + defer record.Release() + frames, err := EncodeRecordBatch(record, DefaultMaxBatchBytes) + require.NoError(t, err) + + _, err = DecodeRecordBatch(ArrowFrame{Header: frames[1].Header}, frames[1], DefaultMaxBatchBytes) + require.Error(t, err) + _, err = DecodeRecordBatch(frames[0], frames[0], DefaultMaxBatchBytes) + require.ErrorContains(t, err, "record frame header") + _, err = DecodeRecordBatch(ArrowFrame{Header: frames[0].Header, Body: []byte{1}}, frames[1], DefaultMaxBatchBytes) + require.ErrorContains(t, err, "schema frame body") +} + +func TestAppendArrowResultRejectsMissingConsumer(t *testing.T) { + builder := array.NewInt64Builder(memory.NewGoAllocator()) + builder.Append(1) + values := builder.NewInt64Array() + defer values.Release() + descriptor, err := NewTypeDescriptor(types.T_int64.ToType()) + require.NoError(t, err) + require.ErrorContains(t, AppendArrowResult(descriptor, values, nil, nil), "missing result wrapper") +} + func TestZeroArgumentRecordPreservesRows(t *testing.T) { record := array.NewRecordBatch(arrow.NewSchema(nil, nil), nil, 3) defer record.Release() diff --git a/pkg/udf/python/gateway.go b/pkg/udf/python/gateway.go index 3c7fdbd6b9ced..d7cf2df554aea 100644 --- a/pkg/udf/python/gateway.go +++ b/pkg/udf/python/gateway.go @@ -127,13 +127,6 @@ func (g *Gateway) Execute(ctx context.Context, invocation *udf.Invocation, resul if err := validateInvocation(invocation); err != nil { return err } - if invocation.Length == 0 { - return result.PreExtendAndReset(0) - } - if err := g.connect(); err != nil { - return err - } - args := make([]TypeDescriptor, len(invocation.Args)) for i, typ := range invocation.Args { var err error @@ -146,6 +139,12 @@ func (g *Gateway) Execute(ctx context.Context, invocation *udf.Invocation, resul if err != nil { return err } + if invocation.Length == 0 { + return result.PreExtendAndReset(0) + } + if err := g.connect(); err != nil { + return err + } openBody, err := json.Marshal(openPayload{Handler: invocation.Handler, Source: invocation.Source, Mode: invocation.Mode, NullPolicy: invocation.NullPolicy, ABIContract: invocation.ABIContract, AdapterVersion: invocation.AdapterVersion, SDKVersion: invocation.SDKVersion, Context: cloneMap(invocation.Context), Args: args, Return: returnDescriptor, MaxBatchBytes: g.cfg.MaxBatchBytes, MaxBatchRows: g.cfg.MaxBatchRows, HandlerTimeoutSeconds: g.cfg.RequestTimeout.Seconds()}) if err != nil { return fmt.Errorf("python udf: encode open payload: %w", err) @@ -333,11 +332,12 @@ func (g *Gateway) receiveResultBatch( if len(data.DataBody) != 0 { return 0, fmt.Errorf("python udf: result schema contains a body") } - if len(data.AppMetadata) > 0 { - control, err := protocol.UnmarshalControl(data.AppMetadata) - if err != nil || control.Tuple != tuple || control.Kind != "ResultSchema" { - return 0, fmt.Errorf("python udf: invalid result schema metadata") - } + if len(data.AppMetadata) == 0 { + return 0, fmt.Errorf("python udf: result schema is missing metadata") + } + control, err := protocol.UnmarshalControl(data.AppMetadata) + if err != nil || control.Tuple != tuple || control.Kind != "ResultSchema" { + return 0, fmt.Errorf("python udf: invalid result schema metadata") } *schemaFrame = &ArrowFrame{Header: append([]byte(nil), data.DataHeader...)} continue @@ -504,8 +504,8 @@ func (g *Gateway) action(ctx context.Context, kind string, control protocol.Cont if err != nil { return err } - if ack.Tuple != control.Tuple || ack.Kind != "Ack" { - return fmt.Errorf("python udf: %s returned an invalid ACK", kind) + if err := validateActionAck(kind, control, ack); err != nil { + return err } if ack.Status != statusOK { return fmt.Errorf("python udf: %s rejected: %s", kind, ack.Reason) @@ -519,6 +519,25 @@ func (g *Gateway) action(ctx context.Context, kind string, control protocol.Cont return nil } +func validateActionAck(kind string, request, ack protocol.Control) error { + if ack.Tuple != request.Tuple || ack.Kind != "Ack" { + return fmt.Errorf("python udf: %s returned an invalid ACK", kind) + } + switch kind { + case "AcknowledgeResults": + if ack.AckSequence != request.AckSequence { + return fmt.Errorf("python udf: %s ACK sequence %d, expected %d", kind, ack.AckSequence, request.AckSequence) + } + case "AcknowledgeFinish": + if ack.FinishID != request.FinishID { + return fmt.Errorf("python udf: %s ACK finish ID does not match the request", kind) + } + default: + return fmt.Errorf("python udf: unsupported action %q", kind) + } + return nil +} + func cloneMap(input map[string]string) map[string]string { if len(input) == 0 { return nil diff --git a/pkg/udf/python/gateway_test.go b/pkg/udf/python/gateway_test.go index 341b84dc750fc..818e82a7106e9 100644 --- a/pkg/udf/python/gateway_test.go +++ b/pkg/udf/python/gateway_test.go @@ -58,3 +58,21 @@ func TestValidateInvocationRequiresFrozenContract(t *testing.T) { }) } } + +func TestValidateActionAckCorrelatesTheRequestedFence(t *testing.T) { + request := protocol.Control{ + Kind: "AcknowledgeResults", + Tuple: validInvocation().Tuple, + AckSequence: 3, + } + ack := protocol.Control{Kind: "Ack", Tuple: request.Tuple, Status: statusOK, AckSequence: 3} + require.NoError(t, validateActionAck(request.Kind, request, ack)) + ack.AckSequence = 2 + require.ErrorContains(t, validateActionAck(request.Kind, request, ack), "ACK sequence") + + finish := protocol.Control{Kind: "AcknowledgeFinish", Tuple: request.Tuple, FinishID: "finish-1"} + finishAck := protocol.Control{Kind: "Ack", Tuple: request.Tuple, Status: statusOK, FinishID: "finish-1"} + require.NoError(t, validateActionAck(finish.Kind, finish, finishAck)) + finishAck.FinishID = "finish-2" + require.ErrorContains(t, validateActionAck(finish.Kind, finish, finishAck), "finish ID") +} diff --git a/pkg/udf/python/worker/worker.py b/pkg/udf/python/worker/worker.py index 5ce252a138f3e..d839d6e90bd81 100644 --- a/pkg/udf/python/worker/worker.py +++ b/pkg/udf/python/worker/worker.py @@ -1067,14 +1067,30 @@ def do_action(self, context, action): terminal = key in self._terminal if state is None: if terminal: - yield _encode_control({"kind": "Ack", "tuple": control["tuple"], "status": "OK"}) + yield _encode_control( + { + "kind": "Ack", + "tuple": control["tuple"], + "status": "OK", + "ack_sequence": control.get("ack_sequence", 0), + "finish_id": control.get("finish_id", ""), + } + ) return raise ValueError("PROTOCOL: unknown invocation") if action.type == "AcknowledgeResults": state.ack_result(_required_uint64(control, "ack_sequence")) elif action.type == "AcknowledgeFinish": state.ack_finish(_required_string(control, "finish_id")) - yield _encode_control({"kind": "Ack", "tuple": control["tuple"], "status": "OK", "ack_sequence": control.get("ack_sequence", 0)}) + yield _encode_control( + { + "kind": "Ack", + "tuple": control["tuple"], + "status": "OK", + "ack_sequence": control.get("ack_sequence", 0), + "finish_id": control.get("finish_id", ""), + } + ) def do_exchange(self, context, descriptor, reader, writer): state = None From efb8e4557b979da0a39d3f393c529620b8c172d2 Mon Sep 17 00:00:00 2001 From: iamlinjunhong <1030420200@qq.com> Date: Thu, 10 Sep 2026 10:51:51 +0800 Subject: [PATCH 020/202] fix(udf): make Python runtime shutdown terminal --- pkg/udf/python/gateway.go | 16 +++++++ pkg/udf/python/gateway_test.go | 8 ++++ pkg/udf/python/supervisor.go | 34 +++++++------ pkg/udf/python/supervisor_process_other.go | 14 ++++++ pkg/udf/python/supervisor_process_unix.go | 26 ++++++++++ pkg/udf/python/supervisor_test.go | 45 ++++++++++++++++++ pkg/udf/python/worker/test_worker.py | 46 +++++++++++++++++- pkg/udf/python/worker/worker.py | 55 +++++++++++++++------- 8 files changed, 211 insertions(+), 33 deletions(-) create mode 100644 pkg/udf/python/supervisor_process_other.go create mode 100644 pkg/udf/python/supervisor_process_unix.go create mode 100644 pkg/udf/python/supervisor_test.go diff --git a/pkg/udf/python/gateway.go b/pkg/udf/python/gateway.go index d7cf2df554aea..8cba52c105cee 100644 --- a/pkg/udf/python/gateway.go +++ b/pkg/udf/python/gateway.go @@ -36,12 +36,15 @@ const ( statusOK = "OK" ) +var errGatewayClosed = errors.New("python udf gateway is closed") + type Gateway struct { cfg ClientConfig conn *grpc.ClientConn flight flight.FlightServiceClient mu sync.Mutex lifecycle sync.RWMutex + closed bool } func NewGateway(cfg ClientConfig) (*Gateway, error) { @@ -65,6 +68,9 @@ func (g *Gateway) Language() string { return udf.LanguagePython } func (g *Gateway) connect() error { g.mu.Lock() defer g.mu.Unlock() + if g.closed { + return errGatewayClosed + } if g.flight != nil { return nil } @@ -88,6 +94,10 @@ func (g *Gateway) Close() error { defer g.lifecycle.Unlock() g.mu.Lock() defer g.mu.Unlock() + if g.closed { + return nil + } + g.closed = true if g.conn == nil { return nil } @@ -116,6 +126,12 @@ type openPayload struct { func (g *Gateway) Execute(ctx context.Context, invocation *udf.Invocation, result vector.FunctionResultWrapper, mp *mpool.MPool) error { g.lifecycle.RLock() defer g.lifecycle.RUnlock() + g.mu.Lock() + closed := g.closed + g.mu.Unlock() + if closed { + return errGatewayClosed + } if ctx == nil { ctx = context.Background() } diff --git a/pkg/udf/python/gateway_test.go b/pkg/udf/python/gateway_test.go index 818e82a7106e9..f83abcca7e31b 100644 --- a/pkg/udf/python/gateway_test.go +++ b/pkg/udf/python/gateway_test.go @@ -76,3 +76,11 @@ func TestValidateActionAckCorrelatesTheRequestedFence(t *testing.T) { finishAck.FinishID = "finish-2" require.ErrorContains(t, validateActionAck(finish.Kind, finish, finishAck), "finish ID") } + +func TestGatewayCloseIsTerminal(t *testing.T) { + gateway, err := NewGateway(ClientConfig{}) + require.NoError(t, err) + require.NoError(t, gateway.Close()) + require.ErrorIs(t, gateway.connect(), errGatewayClosed) + require.NoError(t, gateway.Close()) +} diff --git a/pkg/udf/python/supervisor.go b/pkg/udf/python/supervisor.go index 14674b2b376c2..30d85fc2233f4 100644 --- a/pkg/udf/python/supervisor.go +++ b/pkg/udf/python/supervisor.go @@ -20,10 +20,11 @@ import ( ) type Supervisor struct { - cfg Config - mu sync.Mutex - cmd *exec.Cmd - log io.WriteCloser + cfg Config + mu sync.Mutex + cmd *exec.Cmd + log io.WriteCloser + done chan struct{} } var supervisorNumber atomic.Int32 @@ -67,25 +68,28 @@ func (s *Supervisor) Start() error { return err } cmd := exec.Command(executable, "-u", workerPath, "--address="+s.cfg.Address) + prepareSupervisorCommand(cmd) cmd.Stdout, cmd.Stderr = logFile, logFile if err := cmd.Start(); err != nil { _ = logFile.Close() return err } - s.cmd, s.log = cmd, logFile - go s.wait(cmd, logFile) + done := make(chan struct{}) + s.cmd, s.log, s.done = cmd, logFile, done + go s.wait(cmd, logFile, done) logutil.Infof("started Python UDF worker: %s", cmd.String()) return nil } -func (s *Supervisor) wait(cmd *exec.Cmd, log io.WriteCloser) { +func (s *Supervisor) wait(cmd *exec.Cmd, log io.WriteCloser, done chan struct{}) { err := cmd.Wait() _ = log.Close() s.mu.Lock() if s.cmd == cmd { - s.cmd, s.log = nil, nil + s.cmd, s.log, s.done = nil, nil, nil } s.mu.Unlock() + close(done) if err != nil { logutil.Errorf("Python UDF worker exited: %v", err) } @@ -93,8 +97,7 @@ func (s *Supervisor) wait(cmd *exec.Cmd, log io.WriteCloser) { func (s *Supervisor) Close() error { s.mu.Lock() - cmd, log := s.cmd, s.log - s.cmd, s.log = nil, nil + cmd, log, done := s.cmd, s.log, s.done s.mu.Unlock() if cmd == nil { if log != nil { @@ -102,11 +105,12 @@ func (s *Supervisor) Close() error { } return nil } - if err := cmd.Process.Kill(); err != nil && !errors.Is(err, os.ErrProcessDone) { - return fmt.Errorf("stop Python UDF worker: %w", err) + var closeErr error + if err := killSupervisorProcess(cmd); err != nil && !errors.Is(err, os.ErrProcessDone) { + closeErr = fmt.Errorf("stop Python UDF worker: %w", err) } - if log != nil { - return log.Close() + if done != nil { + <-done } - return nil + return closeErr } diff --git a/pkg/udf/python/supervisor_process_other.go b/pkg/udf/python/supervisor_process_other.go new file mode 100644 index 0000000000000..6ec19f6431cc9 --- /dev/null +++ b/pkg/udf/python/supervisor_process_other.go @@ -0,0 +1,14 @@ +//go:build !(aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris) + +package python + +import "os/exec" + +func prepareSupervisorCommand(*exec.Cmd) {} + +func killSupervisorProcess(cmd *exec.Cmd) error { + if cmd == nil || cmd.Process == nil { + return nil + } + return cmd.Process.Kill() +} diff --git a/pkg/udf/python/supervisor_process_unix.go b/pkg/udf/python/supervisor_process_unix.go new file mode 100644 index 0000000000000..20bbb1f545436 --- /dev/null +++ b/pkg/udf/python/supervisor_process_unix.go @@ -0,0 +1,26 @@ +//go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris + +package python + +import ( + "errors" + "os/exec" + "syscall" +) + +func prepareSupervisorCommand(cmd *exec.Cmd) { + cmd.SysProcAttr = &syscall.SysProcAttr{Setpgid: true} +} + +func killSupervisorProcess(cmd *exec.Cmd) error { + if cmd == nil || cmd.Process == nil { + return nil + } + if err := syscall.Kill(-cmd.Process.Pid, syscall.SIGKILL); err != nil { + if errors.Is(err, syscall.ESRCH) { + return cmd.Process.Kill() + } + return err + } + return nil +} diff --git a/pkg/udf/python/supervisor_test.go b/pkg/udf/python/supervisor_test.go new file mode 100644 index 0000000000000..b5f17f2cfc6cb --- /dev/null +++ b/pkg/udf/python/supervisor_test.go @@ -0,0 +1,45 @@ +// Copyright 2026 Matrix Origin +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. + +package python + +import ( + "os" + "path/filepath" + "runtime" + "testing" + "time" + + "github.com/stretchr/testify/require" +) + +func TestSupervisorCloseStopsTheCurrentWorkerBeforeReturning(t *testing.T) { + if runtime.GOOS == "windows" { + t.Skip("worker supervisor test uses a POSIX executable script") + } + dir := t.TempDir() + workerPath := filepath.Join(dir, "worker.py") + require.NoError(t, os.WriteFile(workerPath, []byte("#!/bin/sh\nwhile :; do sleep 1; done\n"), 0o755)) + + supervisor, err := NewSupervisor(Config{Address: "127.0.0.1:0", Path: dir, Python: "/bin/sh"}) + require.NoError(t, err) + require.NoError(t, supervisor.Start()) + require.Eventually(t, func() bool { + supervisor.mu.Lock() + defer supervisor.mu.Unlock() + return supervisor.cmd != nil + }, time.Second, time.Millisecond) + require.NoError(t, supervisor.Close()) + require.Eventually(t, func() bool { + supervisor.mu.Lock() + defer supervisor.mu.Unlock() + return supervisor.cmd == nil && supervisor.done == nil + }, time.Second, time.Millisecond) + + // A second start is only safe after the first wait has completed. This + // catches a Close implementation that clears cmd and returns before Wait. + require.NoError(t, supervisor.Start()) + require.NoError(t, supervisor.Close()) +} diff --git a/pkg/udf/python/worker/test_worker.py b/pkg/udf/python/worker/test_worker.py index 3fb9472245144..a49d25373dec6 100644 --- a/pkg/udf/python/worker/test_worker.py +++ b/pkg/udf/python/worker/test_worker.py @@ -434,7 +434,9 @@ def test_terminal_admission_does_not_evict_live_tombstones(self): server._admit((1, "statement", "group", 1, "second", 1)) self.assertIn(first, server._terminal) with server._lock: - server._terminal[first] = (0.0, state.terminal_bytes) + server._terminal[first] = worker._TerminalRecord( + 0.0, state.terminal_bytes, state.last_result, state.finish_id + ) server._purge_terminal_locked(1.0) second = server._admit((1, "statement", "group", 1, "second", 1)) self.assertIsNotNone(second) @@ -442,6 +444,48 @@ def test_terminal_admission_does_not_evict_live_tombstones(self): worker.MAX_LEDGER_ENTRIES = old_records worker.MAX_LEDGER_BYTES = old_bytes + def test_terminal_ack_requires_the_completed_fence(self): + server = worker.RoutineFlightServer("grpc://127.0.0.1:0") + key = (1, "statement", "group", 1, "completed", 1) + state = server._admit(key) + state.last_result = 2 + state.finish_id = "finish-2" + server._finish_invocation(key, state) + + tuple_value = { + "account_id": key[0], + "statement_id": key[1], + "group_id": key[2], + "group_epoch": key[3], + "invocation_id": key[4], + "lease_epoch": key[5], + } + result_action = types.SimpleNamespace( + type="AcknowledgeResults", + body=worker._encode_control( + { + "kind": "AcknowledgeResults", + "tuple": tuple_value, + "ack_sequence": 2, + } + ), + ) + ack = worker._decode_control(next(server.do_action(None, result_action))) + self.assertEqual(2, ack["ack_sequence"]) + + wrong_action = types.SimpleNamespace( + type="AcknowledgeResults", + body=worker._encode_control( + { + "kind": "AcknowledgeResults", + "tuple": tuple_value, + "ack_sequence": 1, + } + ), + ) + with self.assertRaisesRegex(ValueError, "completed result"): + next(server.do_action(None, wrong_action)) + def test_active_fence_cannot_be_admitted_twice(self): server = worker.RoutineFlightServer("grpc://127.0.0.1:0") key = (1, "statement", "group", 1, "invocation", 1) diff --git a/pkg/udf/python/worker/worker.py b/pkg/udf/python/worker/worker.py index d839d6e90bd81..7d581e47b9d45 100644 --- a/pkg/udf/python/worker/worker.py +++ b/pkg/udf/python/worker/worker.py @@ -143,6 +143,14 @@ class VectorContext: num_rows: int = 0 +@dataclass(frozen=True, slots=True) +class _TerminalRecord: + expires_at: float + bytes: int + last_result: int + finish_id: Optional[str] + + _FENCING_CONTEXT_KEYS = frozenset( {"account_id", "statement_id", "group_id", "group_epoch", "invocation_id", "lease_epoch"} ) @@ -997,7 +1005,7 @@ def __init__(self, location: str): super().__init__(location) self._lock = threading.RLock() self._active: Dict[tuple, _InvocationState] = {} - self._terminal: OrderedDict[tuple, tuple[float, int]] = OrderedDict() + self._terminal: OrderedDict[tuple, _TerminalRecord] = OrderedDict() self._terminal_bytes = 0 self._active_bytes = 0 @@ -1010,10 +1018,10 @@ def _entry_bytes(key: tuple) -> int: return len(encoded) + 128 def _purge_terminal_locked(self, now: float) -> None: - expired = [key for key, (deadline, _) in self._terminal.items() if deadline <= now] + expired = [key for key, record in self._terminal.items() if record.expires_at <= now] for key in expired: - _, size = self._terminal.pop(key) - self._terminal_bytes -= size + record = self._terminal.pop(key) + self._terminal_bytes -= record.bytes def _admit(self, key: tuple) -> _InvocationState: size = self._entry_bytes(key) @@ -1034,7 +1042,9 @@ def _admit(self, key: tuple) -> _InvocationState: def _remember_terminal_locked(self, key: tuple, state: _InvocationState) -> None: deadline = time.monotonic() + TERMINAL_TTL_SECONDS - self._terminal[key] = (deadline, state.terminal_bytes) + self._terminal[key] = _TerminalRecord( + deadline, state.terminal_bytes, state.last_result, state.finish_id + ) self._terminal.move_to_end(key) self._terminal_bytes += state.terminal_bytes @@ -1064,19 +1074,30 @@ def do_action(self, context, action): with self._lock: self._purge_terminal_locked(time.monotonic()) state = self._active.get(key) - terminal = key in self._terminal + terminal = self._terminal.get(key) if state is None: - if terminal: - yield _encode_control( - { - "kind": "Ack", - "tuple": control["tuple"], - "status": "OK", - "ack_sequence": control.get("ack_sequence", 0), - "finish_id": control.get("finish_id", ""), - } - ) - return + if terminal is None: + raise ValueError("PROTOCOL: unknown invocation") + if not terminal.finish_id: + raise ValueError("PROTOCOL: invocation did not complete successfully") + if action.type == "AcknowledgeResults": + sequence = _required_uint64(control, "ack_sequence") + if sequence != terminal.last_result: + raise ValueError("PROTOCOL: terminal result ACK does not match the completed result") + else: + finish_id = _required_string(control, "finish_id") + if finish_id != terminal.finish_id: + raise ValueError("PROTOCOL: terminal Finish ACK does not match the completed Finish") + yield _encode_control( + { + "kind": "Ack", + "tuple": control["tuple"], + "status": "OK", + "ack_sequence": control.get("ack_sequence", 0), + "finish_id": control.get("finish_id", ""), + } + ) + return raise ValueError("PROTOCOL: unknown invocation") if action.type == "AcknowledgeResults": state.ack_result(_required_uint64(control, "ack_sequence")) From 16b512d1187256398e38670e3b03c36c45195e1b Mon Sep 17 00:00:00 2001 From: iamlinjunhong <1030420200@qq.com> Date: Thu, 10 Sep 2026 11:49:45 +0800 Subject: [PATCH 021/202] fix(udf): close Python SQL and runtime contracts --- etc/v1/launch-with-python-udf-worker/cn.toml | 14 - .../launch-with-python-udf-worker/launch.toml | 15 - .../python-udf-worker.toml | 6 - pkg/frontend/authenticate.go | 12 +- pkg/sql/parsers/dialect/mysql/keywords.go | 4 + pkg/sql/parsers/dialect/mysql/mysql_sql.go | 27203 ++++++++-------- pkg/sql/parsers/dialect/mysql/mysql_sql.y | 58 +- .../parsers/dialect/mysql/mysql_sql_test.go | 38 + pkg/sql/parsers/tree/function.go | 33 + pkg/udf/protocol/protocol.go | 2 +- pkg/udf/protocol/protocol_test.go | 1 + pkg/udf/python/arrow.go | 12 +- pkg/udf/python/arrow_test.go | 20 + pkg/udf/python/gateway.go | 93 +- pkg/udf/python/supervisor.go | 52 +- pkg/udf/python/worker/test_worker.py | 6 + pkg/udf/python/worker/worker.py | 3 +- .../cases/udf_python/vector_mode.result | 31 + .../cases/udf_python/vector_mode.sql | 31 + 19 files changed, 13984 insertions(+), 13650 deletions(-) delete mode 100644 etc/v1/launch-with-python-udf-worker/cn.toml delete mode 100644 etc/v1/launch-with-python-udf-worker/launch.toml delete mode 100644 etc/v1/launch-with-python-udf-worker/python-udf-worker.toml create mode 100644 test/distributed/cases/udf_python/vector_mode.result create mode 100644 test/distributed/cases/udf_python/vector_mode.sql diff --git a/etc/v1/launch-with-python-udf-worker/cn.toml b/etc/v1/launch-with-python-udf-worker/cn.toml deleted file mode 100644 index cf5b576cbc864..0000000000000 --- a/etc/v1/launch-with-python-udf-worker/cn.toml +++ /dev/null @@ -1,14 +0,0 @@ -service-type = "CN" -data-dir = "./mo-data" - -[log] -level = "info" - -[cn] -uuid = "dd1dccb4-4d3c-41f8-b482-5251dc7a41bf" -port-base = 18000 - -[cn.python-udf-client] -enabled = true -allow-unisolated = true -server-address = "127.0.0.1:50051" diff --git a/etc/v1/launch-with-python-udf-worker/launch.toml b/etc/v1/launch-with-python-udf-worker/launch.toml deleted file mode 100644 index 8171c1ead1cd7..0000000000000 --- a/etc/v1/launch-with-python-udf-worker/launch.toml +++ /dev/null @@ -1,15 +0,0 @@ -logservices = [ - "./etc/v1/launch/log.toml", -] - -tnservices = [ - "./etc/v1/launch/tn.toml" -] - -cnservices = [ - "./etc/v1/launch-with-python-udf-worker/cn.toml" -] - -python-udf-services = [ - "./etc/v1/launch-with-python-udf-worker/python-udf-worker.toml" -] diff --git a/etc/v1/launch-with-python-udf-worker/python-udf-worker.toml b/etc/v1/launch-with-python-udf-worker/python-udf-worker.toml deleted file mode 100644 index 40a0d22bb0ade..0000000000000 --- a/etc/v1/launch-with-python-udf-worker/python-udf-worker.toml +++ /dev/null @@ -1,6 +0,0 @@ -service-type = "PYTHON_UDF" - -[python-udf-worker] -uuid = "dd1dccb4-4d3c-41f8-b482-5251dc7a41b2" -address = "127.0.0.1:50051" -path = "pkg/udf/python/worker" diff --git a/pkg/frontend/authenticate.go b/pkg/frontend/authenticate.go index fdfb5d528b051..95e37d4a0ba86 100644 --- a/pkg/frontend/authenticate.go +++ b/pkg/frontend/authenticate.go @@ -11612,12 +11612,20 @@ func InitFunction(ses *Session, execCtx *ExecCtx, tenant *TenantInfo, cf *tree.C if cf.Import { return moerr.NewNotSupportedNoCtx("Python artifact import requires the immutable artifact catalog") } + mode := cf.Mode + if mode == "" { + mode = "SCALAR" + } + nullPolicy := cf.NullPolicy + if nullPolicy == "" { + nullPolicy = udf.NullCallHandler + } nb := function.PythonRoutineBody{ Handler: cf.Handler, Source: cf.Body, - Mode: "SCALAR", - NullPolicy: udf.NullCallHandler, + Mode: mode, + NullPolicy: nullPolicy, ABIContract: udf.PythonABIContract, AdapterVersion: udf.PythonAdapterVersion, SDKVersion: udf.PythonSDKVersion, diff --git a/pkg/sql/parsers/dialect/mysql/keywords.go b/pkg/sql/parsers/dialect/mysql/keywords.go index be1c6eca1b8e2..6747d2d378847 100644 --- a/pkg/sql/parsers/dialect/mysql/keywords.go +++ b/pkg/sql/parsers/dialect/mysql/keywords.go @@ -73,6 +73,7 @@ func init() { "bit_and": BIT_AND, "bit_xor": BIT_XOR, "call": CALL, + "called": CALLED, "cancel": CANCEL, "catalog": CATALOG, "catalogs": CATALOGS, @@ -265,6 +266,7 @@ func init() { "mongodb_path": MONGODB_PATH, "if": IF, "ignore": IGNORE, + "input": INPUT, "in": IN, "index": INDEX, "indexes": INDEXES, @@ -477,6 +479,7 @@ func init() { "row_number": ROW_NUMBER, "rtree": RTREE, "schema": SCHEMA, + "scalar": SCALAR, "schemas": SCHEMAS, "schedule": SCHEDULE, "second": SECOND, @@ -721,6 +724,7 @@ func init() { "array": ARRAY, "array_agg": JSON_ARRAYAGG, "vecf32": VECF32, + "vector": VECTOR, "vecf64": VECF64, "vecbf16": VECBF16, "vecf16": VECF16, diff --git a/pkg/sql/parsers/dialect/mysql/mysql_sql.go b/pkg/sql/parsers/dialect/mysql/mysql_sql.go index d017063e58337..8327b8f8bf07e 100644 --- a/pkg/sql/parsers/dialect/mysql/mysql_sql.go +++ b/pkg/sql/parsers/dialect/mysql/mysql_sql.go @@ -541,309 +541,313 @@ const TEMPORARY = 57798 const ROUTINE = 57799 const EVENT = 57800 const SHUTDOWN = 57801 -const NULLX = 57802 -const AUTO_INCREMENT = 57803 -const APPROXNUM = 57804 -const ENGINES = 57805 -const LOW_CARDINALITY = 57806 -const AUTOEXTEND_SIZE = 57807 -const ADMIN_NAME = 57808 -const RANDOM = 57809 -const SUSPEND = 57810 -const ATTRIBUTE = 57811 -const HISTORY = 57812 -const REUSE = 57813 -const CURRENT = 57814 -const OPTIONAL = 57815 -const FAILED_LOGIN_ATTEMPTS = 57816 -const PASSWORD_LOCK_TIME = 57817 -const UNBOUNDED = 57818 -const SECONDARY = 57819 -const RESTRICTED = 57820 -const USER = 57821 -const IDENTIFIED = 57822 -const CIPHER = 57823 -const ISSUER = 57824 -const X509 = 57825 -const SUBJECT = 57826 -const SAN = 57827 -const REQUIRE = 57828 -const SSL = 57829 -const NONE = 57830 -const PASSWORD = 57831 -const SHARED = 57832 -const EXCLUSIVE = 57833 -const MAX_QUERIES_PER_HOUR = 57834 -const MAX_UPDATES_PER_HOUR = 57835 -const MAX_CONNECTIONS_PER_HOUR = 57836 -const MAX_USER_CONNECTIONS = 57837 -const FORMAT = 57838 -const VERBOSE = 57839 -const CONNECTION = 57840 -const TRIGGERS = 57841 -const PROFILES = 57842 -const PROFILE = 57843 -const LOAD = 57844 -const INLINE = 57845 -const INFILE = 57846 -const TERMINATED = 57847 -const OPTIONALLY = 57848 -const ENCLOSED = 57849 -const ESCAPED = 57850 -const STARTING = 57851 -const LINES = 57852 -const ROWS = 57853 -const IMPORT = 57854 -const DISCARD = 57855 -const JSONTYPE = 57856 -const MODUMP = 57857 -const OVER = 57858 -const PRECEDING = 57859 -const FOLLOWING = 57860 -const GROUPS = 57861 -const DATABASES = 57862 -const TABLES = 57863 -const SEQUENCES = 57864 -const EXTENDED = 57865 -const FULL = 57866 -const PROCESSLIST = 57867 -const FIELDS = 57868 -const COLUMNS = 57869 -const OPEN = 57870 -const ERRORS = 57871 -const WARNINGS = 57872 -const INDEXES = 57873 -const SCHEMAS = 57874 -const NODE = 57875 -const LOCKS = 57876 -const ROLES = 57877 -const RULE = 57878 -const RULES = 57879 -const TABLE_NUMBER = 57880 -const COLUMN_NUMBER = 57881 -const TABLE_VALUES = 57882 -const TABLE_SIZE = 57883 -const TASKS = 57884 -const RUNS = 57885 -const NAMES = 57886 -const GLOBAL = 57887 -const PERSIST = 57888 -const SESSION = 57889 -const ISOLATION = 57890 -const LEVEL = 57891 -const READ = 57892 -const WRITE = 57893 -const ONLY = 57894 -const REPEATABLE = 57895 -const COMMITTED = 57896 -const UNCOMMITTED = 57897 -const SERIALIZABLE = 57898 -const LOCAL = 57899 -const EVENTS = 57900 -const PLUGINS = 57901 -const CURRENT_TIMESTAMP = 57902 -const DATABASE = 57903 -const CURRENT_TIME = 57904 -const LOCALTIME = 57905 -const LOCALTIMESTAMP = 57906 -const UTC_DATE = 57907 -const UTC_TIME = 57908 -const UTC_TIMESTAMP = 57909 -const REPLACE = 57910 -const CONVERT = 57911 -const SEPARATOR = 57912 -const TIMESTAMPDIFF = 57913 -const TIMESTAMPADD = 57914 -const CURRENT_DATE = 57915 -const CURRENT_USER = 57916 -const CURRENT_ROLE = 57917 -const SECOND_MICROSECOND = 57918 -const MINUTE_MICROSECOND = 57919 -const MINUTE_SECOND = 57920 -const HOUR_MICROSECOND = 57921 -const HOUR_SECOND = 57922 -const HOUR_MINUTE = 57923 -const DAY_MICROSECOND = 57924 -const DAY_SECOND = 57925 -const DAY_MINUTE = 57926 -const DAY_HOUR = 57927 -const YEAR_MONTH = 57928 -const SQL_TSI_HOUR = 57929 -const SQL_TSI_DAY = 57930 -const SQL_TSI_WEEK = 57931 -const SQL_TSI_MONTH = 57932 -const SQL_TSI_QUARTER = 57933 -const SQL_TSI_YEAR = 57934 -const SQL_TSI_SECOND = 57935 -const SQL_TSI_MINUTE = 57936 -const RECURSIVE = 57937 -const CONFIG = 57938 -const DRAINER = 57939 -const DAEMON = 57940 -const PAUSE = 57941 -const CANCEL = 57942 -const RESUME = 57943 -const SCHEDULE = 57944 -const TIMEZONE = 57945 -const TIMEOUT = 57946 -const TASK = 57947 -const MATCH = 57948 -const AGAINST = 57949 -const BOOLEAN = 57950 -const LANGUAGE = 57951 -const QUERY = 57952 -const EXPANSION = 57953 -const WITHOUT = 57954 -const VALIDATION = 57955 -const UPGRADE = 57956 -const RETRY = 57957 -const ADDDATE = 57958 -const BIT_AND = 57959 -const BIT_OR = 57960 -const BIT_XOR = 57961 -const CAST = 57962 -const COUNT = 57963 -const APPROX_COUNT = 57964 -const APPROX_COUNT_DISTINCT = 57965 -const SERIAL_EXTRACT = 57966 -const APPROX_PERCENTILE = 57967 -const CURDATE = 57968 -const CURTIME = 57969 -const DATE_ADD = 57970 -const DATE_SUB = 57971 -const EXTRACT = 57972 -const GROUP_CONCAT = 57973 -const MAX = 57974 -const MID = 57975 -const MIN = 57976 -const NOW = 57977 -const POSITION = 57978 -const SESSION_USER = 57979 -const STD = 57980 -const STDDEV = 57981 -const MEDIAN = 57982 -const CLUSTER_CENTERS = 57983 -const KMEANS = 57984 -const STDDEV_POP = 57985 -const STDDEV_SAMP = 57986 -const SUBDATE = 57987 -const SUBSTR = 57988 -const SUBSTRING = 57989 -const SUM = 57990 -const SYSDATE = 57991 -const SYSTEM_USER = 57992 -const TRANSLATE = 57993 -const TRIM = 57994 -const VARIANCE = 57995 -const VAR_POP = 57996 -const VAR_SAMP = 57997 -const AVG = 57998 -const RANK = 57999 -const ROW_NUMBER = 58000 -const DENSE_RANK = 58001 -const CUME_DIST = 58002 -const BIT_CAST = 58003 -const LAG = 58004 -const LEAD = 58005 -const FIRST_VALUE = 58006 -const LAST_VALUE = 58007 -const NTH_VALUE = 58008 -const NTILE = 58009 -const PERCENT_RANK = 58010 -const BITMAP_BIT_POSITION = 58011 -const BITMAP_BUCKET_NUMBER = 58012 -const BITMAP_COUNT = 58013 -const BITMAP_CONSTRUCT_AGG = 58014 -const BITMAP_OR_AGG = 58015 -const JSON_ARRAYAGG = 58016 -const JSON_OBJECTAGG = 58017 -const GET_FORMAT = 58018 -const SRID = 58019 -const NEXTVAL = 58020 -const SETVAL = 58021 -const CURRVAL = 58022 -const LASTVAL = 58023 -const ROW = 58024 -const OUTFILE = 58025 -const HEADER = 58026 -const MAX_FILE_SIZE = 58027 -const FORCE_QUOTE = 58028 -const PARALLEL = 58029 -const STRICT = 58030 -const SPLITSIZE = 58031 -const UNUSED = 58032 -const BINDINGS = 58033 -const GENERATED = 58034 -const ALWAYS = 58035 -const STORED = 58036 -const VIRTUAL = 58037 -const DO = 58038 -const PERFORM = 58039 -const DECLARE = 58040 -const LOOP = 58041 -const WHILE = 58042 -const LEAVE = 58043 -const ITERATE = 58044 -const UNTIL = 58045 -const CALL = 58046 -const PREV = 58047 -const SLIDING = 58048 -const FILL = 58049 -const GAPFILL = 58050 -const SPBEGIN = 58051 -const BACKEND = 58052 -const SERVERS = 58053 -const HANDLER = 58054 -const PERCENT = 58055 -const SAMPLE = 58056 -const MO_TS = 58057 -const PITR = 58058 -const RECOVERY_WINDOW = 58059 -const INTERNAL = 58060 -const CDC_TASK_NAME = 58061 -const CDC = 58062 -const ICEBERG = 58063 -const CATALOG = 58064 -const CATALOGS = 58065 -const NAMESPACE = 58066 -const NAMESPACES = 58067 -const REF = 58068 -const FOR_ICEBERG = 58069 -const MONGODB = 58070 -const MONGODB_PATH = 58071 -const MONGODB_CONVERT = 58072 -const CONNECTIONS = 58073 -const DATASTREAM = 58074 -const ESQL = 58075 -const KAFKA = 58076 -const GROUPING = 58077 -const SETS = 58078 -const CUBE = 58079 -const ROLLUP = 58080 -const LOGSERVICE = 58081 -const REPLICAS = 58082 -const STORES = 58083 -const SETTINGS = 58084 -const DUMP = 58085 -const METADATA = 58086 -const KILL = 58087 -const BACKUP = 58088 -const FILESYSTEM = 58089 -const PARALLELISM = 58090 -const RESTORE = 58091 -const QUERY_RESULT = 58092 -const RETURNING = 58093 -const ARRAY = 58094 -const WITHIN = 58095 -const PERCENTILE_CONT = 58096 -const PERCENTILE_DISC = 58097 -const ASOF = 58098 -const TOLERANCE = 58099 -const WINDOW = 58100 -const WINDOW_NAME_EMPTY = 58101 -const RESPECT = 58102 -const MEMBER = 58103 -const AUTO_ID_CACHE = 58104 +const CALLED = 57802 +const INPUT = 57803 +const SCALAR = 57804 +const VECTOR = 57805 +const NULLX = 57806 +const AUTO_INCREMENT = 57807 +const APPROXNUM = 57808 +const ENGINES = 57809 +const LOW_CARDINALITY = 57810 +const AUTOEXTEND_SIZE = 57811 +const ADMIN_NAME = 57812 +const RANDOM = 57813 +const SUSPEND = 57814 +const ATTRIBUTE = 57815 +const HISTORY = 57816 +const REUSE = 57817 +const CURRENT = 57818 +const OPTIONAL = 57819 +const FAILED_LOGIN_ATTEMPTS = 57820 +const PASSWORD_LOCK_TIME = 57821 +const UNBOUNDED = 57822 +const SECONDARY = 57823 +const RESTRICTED = 57824 +const USER = 57825 +const IDENTIFIED = 57826 +const CIPHER = 57827 +const ISSUER = 57828 +const X509 = 57829 +const SUBJECT = 57830 +const SAN = 57831 +const REQUIRE = 57832 +const SSL = 57833 +const NONE = 57834 +const PASSWORD = 57835 +const SHARED = 57836 +const EXCLUSIVE = 57837 +const MAX_QUERIES_PER_HOUR = 57838 +const MAX_UPDATES_PER_HOUR = 57839 +const MAX_CONNECTIONS_PER_HOUR = 57840 +const MAX_USER_CONNECTIONS = 57841 +const FORMAT = 57842 +const VERBOSE = 57843 +const CONNECTION = 57844 +const TRIGGERS = 57845 +const PROFILES = 57846 +const PROFILE = 57847 +const LOAD = 57848 +const INLINE = 57849 +const INFILE = 57850 +const TERMINATED = 57851 +const OPTIONALLY = 57852 +const ENCLOSED = 57853 +const ESCAPED = 57854 +const STARTING = 57855 +const LINES = 57856 +const ROWS = 57857 +const IMPORT = 57858 +const DISCARD = 57859 +const JSONTYPE = 57860 +const MODUMP = 57861 +const OVER = 57862 +const PRECEDING = 57863 +const FOLLOWING = 57864 +const GROUPS = 57865 +const DATABASES = 57866 +const TABLES = 57867 +const SEQUENCES = 57868 +const EXTENDED = 57869 +const FULL = 57870 +const PROCESSLIST = 57871 +const FIELDS = 57872 +const COLUMNS = 57873 +const OPEN = 57874 +const ERRORS = 57875 +const WARNINGS = 57876 +const INDEXES = 57877 +const SCHEMAS = 57878 +const NODE = 57879 +const LOCKS = 57880 +const ROLES = 57881 +const RULE = 57882 +const RULES = 57883 +const TABLE_NUMBER = 57884 +const COLUMN_NUMBER = 57885 +const TABLE_VALUES = 57886 +const TABLE_SIZE = 57887 +const TASKS = 57888 +const RUNS = 57889 +const NAMES = 57890 +const GLOBAL = 57891 +const PERSIST = 57892 +const SESSION = 57893 +const ISOLATION = 57894 +const LEVEL = 57895 +const READ = 57896 +const WRITE = 57897 +const ONLY = 57898 +const REPEATABLE = 57899 +const COMMITTED = 57900 +const UNCOMMITTED = 57901 +const SERIALIZABLE = 57902 +const LOCAL = 57903 +const EVENTS = 57904 +const PLUGINS = 57905 +const CURRENT_TIMESTAMP = 57906 +const DATABASE = 57907 +const CURRENT_TIME = 57908 +const LOCALTIME = 57909 +const LOCALTIMESTAMP = 57910 +const UTC_DATE = 57911 +const UTC_TIME = 57912 +const UTC_TIMESTAMP = 57913 +const REPLACE = 57914 +const CONVERT = 57915 +const SEPARATOR = 57916 +const TIMESTAMPDIFF = 57917 +const TIMESTAMPADD = 57918 +const CURRENT_DATE = 57919 +const CURRENT_USER = 57920 +const CURRENT_ROLE = 57921 +const SECOND_MICROSECOND = 57922 +const MINUTE_MICROSECOND = 57923 +const MINUTE_SECOND = 57924 +const HOUR_MICROSECOND = 57925 +const HOUR_SECOND = 57926 +const HOUR_MINUTE = 57927 +const DAY_MICROSECOND = 57928 +const DAY_SECOND = 57929 +const DAY_MINUTE = 57930 +const DAY_HOUR = 57931 +const YEAR_MONTH = 57932 +const SQL_TSI_HOUR = 57933 +const SQL_TSI_DAY = 57934 +const SQL_TSI_WEEK = 57935 +const SQL_TSI_MONTH = 57936 +const SQL_TSI_QUARTER = 57937 +const SQL_TSI_YEAR = 57938 +const SQL_TSI_SECOND = 57939 +const SQL_TSI_MINUTE = 57940 +const RECURSIVE = 57941 +const CONFIG = 57942 +const DRAINER = 57943 +const DAEMON = 57944 +const PAUSE = 57945 +const CANCEL = 57946 +const RESUME = 57947 +const SCHEDULE = 57948 +const TIMEZONE = 57949 +const TIMEOUT = 57950 +const TASK = 57951 +const MATCH = 57952 +const AGAINST = 57953 +const BOOLEAN = 57954 +const LANGUAGE = 57955 +const QUERY = 57956 +const EXPANSION = 57957 +const WITHOUT = 57958 +const VALIDATION = 57959 +const UPGRADE = 57960 +const RETRY = 57961 +const ADDDATE = 57962 +const BIT_AND = 57963 +const BIT_OR = 57964 +const BIT_XOR = 57965 +const CAST = 57966 +const COUNT = 57967 +const APPROX_COUNT = 57968 +const APPROX_COUNT_DISTINCT = 57969 +const SERIAL_EXTRACT = 57970 +const APPROX_PERCENTILE = 57971 +const CURDATE = 57972 +const CURTIME = 57973 +const DATE_ADD = 57974 +const DATE_SUB = 57975 +const EXTRACT = 57976 +const GROUP_CONCAT = 57977 +const MAX = 57978 +const MID = 57979 +const MIN = 57980 +const NOW = 57981 +const POSITION = 57982 +const SESSION_USER = 57983 +const STD = 57984 +const STDDEV = 57985 +const MEDIAN = 57986 +const CLUSTER_CENTERS = 57987 +const KMEANS = 57988 +const STDDEV_POP = 57989 +const STDDEV_SAMP = 57990 +const SUBDATE = 57991 +const SUBSTR = 57992 +const SUBSTRING = 57993 +const SUM = 57994 +const SYSDATE = 57995 +const SYSTEM_USER = 57996 +const TRANSLATE = 57997 +const TRIM = 57998 +const VARIANCE = 57999 +const VAR_POP = 58000 +const VAR_SAMP = 58001 +const AVG = 58002 +const RANK = 58003 +const ROW_NUMBER = 58004 +const DENSE_RANK = 58005 +const CUME_DIST = 58006 +const BIT_CAST = 58007 +const LAG = 58008 +const LEAD = 58009 +const FIRST_VALUE = 58010 +const LAST_VALUE = 58011 +const NTH_VALUE = 58012 +const NTILE = 58013 +const PERCENT_RANK = 58014 +const BITMAP_BIT_POSITION = 58015 +const BITMAP_BUCKET_NUMBER = 58016 +const BITMAP_COUNT = 58017 +const BITMAP_CONSTRUCT_AGG = 58018 +const BITMAP_OR_AGG = 58019 +const JSON_ARRAYAGG = 58020 +const JSON_OBJECTAGG = 58021 +const GET_FORMAT = 58022 +const SRID = 58023 +const NEXTVAL = 58024 +const SETVAL = 58025 +const CURRVAL = 58026 +const LASTVAL = 58027 +const ROW = 58028 +const OUTFILE = 58029 +const HEADER = 58030 +const MAX_FILE_SIZE = 58031 +const FORCE_QUOTE = 58032 +const PARALLEL = 58033 +const STRICT = 58034 +const SPLITSIZE = 58035 +const UNUSED = 58036 +const BINDINGS = 58037 +const GENERATED = 58038 +const ALWAYS = 58039 +const STORED = 58040 +const VIRTUAL = 58041 +const DO = 58042 +const PERFORM = 58043 +const DECLARE = 58044 +const LOOP = 58045 +const WHILE = 58046 +const LEAVE = 58047 +const ITERATE = 58048 +const UNTIL = 58049 +const CALL = 58050 +const PREV = 58051 +const SLIDING = 58052 +const FILL = 58053 +const GAPFILL = 58054 +const SPBEGIN = 58055 +const BACKEND = 58056 +const SERVERS = 58057 +const HANDLER = 58058 +const PERCENT = 58059 +const SAMPLE = 58060 +const MO_TS = 58061 +const PITR = 58062 +const RECOVERY_WINDOW = 58063 +const INTERNAL = 58064 +const CDC_TASK_NAME = 58065 +const CDC = 58066 +const ICEBERG = 58067 +const CATALOG = 58068 +const CATALOGS = 58069 +const NAMESPACE = 58070 +const NAMESPACES = 58071 +const REF = 58072 +const FOR_ICEBERG = 58073 +const MONGODB = 58074 +const MONGODB_PATH = 58075 +const MONGODB_CONVERT = 58076 +const CONNECTIONS = 58077 +const DATASTREAM = 58078 +const ESQL = 58079 +const KAFKA = 58080 +const GROUPING = 58081 +const SETS = 58082 +const CUBE = 58083 +const ROLLUP = 58084 +const LOGSERVICE = 58085 +const REPLICAS = 58086 +const STORES = 58087 +const SETTINGS = 58088 +const DUMP = 58089 +const METADATA = 58090 +const KILL = 58091 +const BACKUP = 58092 +const FILESYSTEM = 58093 +const PARALLELISM = 58094 +const RESTORE = 58095 +const QUERY_RESULT = 58096 +const RETURNING = 58097 +const ARRAY = 58098 +const WITHIN = 58099 +const PERCENTILE_CONT = 58100 +const PERCENTILE_DISC = 58101 +const ASOF = 58102 +const TOLERANCE = 58103 +const WINDOW = 58104 +const WINDOW_NAME_EMPTY = 58105 +const RESPECT = 58106 +const MEMBER = 58107 +const AUTO_ID_CACHE = 58108 var yyToknames = [...]string{ "$end", @@ -1322,6 +1326,10 @@ var yyToknames = [...]string{ "ROUTINE", "EVENT", "SHUTDOWN", + "CALLED", + "INPUT", + "SCALAR", + "VECTOR", "NULLX", "AUTO_INCREMENT", "APPROXNUM", @@ -1638,7 +1646,7 @@ const yyEofCode = 1 const yyErrCode = 2 const yyInitialStackSize = 16 -//line mysql_sql.y:16591 +//line mysql_sql.y:16643 //line yacctab:1 var yyExca = [...]int{ @@ -1650,8872 +1658,8916 @@ var yyExca = [...]int{ 280, 743, 307, 750, 308, 750, - 548, 743, + 552, 743, -2, 783, -1, 172, 11, 994, 24, 994, -2, 980, -1, 198, - 278, 1569, - 280, 1394, - -2, 1472, + 278, 1578, + 280, 1403, + -2, 1481, -1, 275, - 783, 2551, + 787, 2563, -2, 617, - -1, 659, - 783, 2678, + -1, 663, + 787, 2691, -2, 454, - -1, 719, - 783, 2739, + -1, 723, + 787, 2752, -2, 452, - -1, 720, - 783, 2740, + -1, 724, + 787, 2753, -2, 453, - -1, 721, - 783, 2741, + -1, 725, + 787, 2754, -2, 455, - -1, 875, - 543, 783, - 544, 783, + -1, 879, + 547, 783, + 548, 783, -2, 744, - -1, 947, + -1, 951, 359, 212, - 520, 212, - 521, 212, - -2, 2394, - -1, 1017, - 90, 2130, - -2, 2613, - -1, 1018, - 90, 2149, - -2, 2582, + 524, 212, + 525, 212, + -2, 2404, + -1, 1021, + 90, 2139, + -2, 2626, -1, 1022, - 90, 2150, - -2, 2612, - -1, 1070, - 90, 2051, - -2, 2833, - -1, 1071, - 90, 2052, - -2, 2832, - -1, 1072, - 90, 2053, - -2, 2822, - -1, 1073, - 90, 2793, - -2, 2813, + 90, 2158, + -2, 2594, + -1, 1026, + 90, 2159, + -2, 2625, -1, 1074, - 90, 2794, - -2, 2814, + 90, 2060, + -2, 2846, -1, 1075, - 90, 2795, - -2, 2824, + 90, 2061, + -2, 2845, -1, 1076, - 90, 2796, - -2, 2802, + 90, 2062, + -2, 2835, -1, 1077, - 90, 2797, - -2, 2811, - -1, 1078, - 90, 2798, + 90, 2806, -2, 2826, + -1, 1078, + 90, 2807, + -2, 2827, -1, 1079, - 90, 2799, - -2, 2831, + 90, 2808, + -2, 2837, -1, 1080, - 90, 2800, - -2, 2836, + 90, 2809, + -2, 2815, -1, 1081, - 90, 2801, - -2, 2837, + 90, 2810, + -2, 2824, -1, 1082, - 90, 2126, - -2, 2651, + 90, 2811, + -2, 2839, -1, 1083, - 90, 2127, - -2, 2377, + 90, 2812, + -2, 2844, -1, 1084, - 90, 2128, - -2, 2661, + 90, 2813, + -2, 2849, -1, 1085, - 90, 2129, - -2, 2388, + 90, 2814, + -2, 2850, + -1, 1086, + 90, 2135, + -2, 2664, -1, 1087, - 90, 2132, - -2, 2396, + 90, 2136, + -2, 2387, + -1, 1088, + 90, 2137, + -2, 2674, -1, 1089, - 90, 2134, - -2, 2686, + 90, 2138, + -2, 2398, -1, 1091, - 90, 2136, - -2, 2420, + 90, 2141, + -2, 2406, -1, 1093, - 90, 2138, - -2, 2698, - -1, 1094, - 90, 2139, - -2, 2697, + 90, 2143, + -2, 2699, -1, 1095, - 90, 2140, - -2, 2506, - -1, 1096, - 90, 2141, - -2, 2608, - -1, 1099, - 90, 2144, - -2, 2709, - -1, 1100, 90, 2145, - -2, 2550, - -1, 1102, + -2, 2430, + -1, 1097, 90, 2147, - -2, 2712, - -1, 1103, + -2, 2711, + -1, 1098, 90, 2148, - -2, 2714, + -2, 2710, + -1, 1099, + 90, 2149, + -2, 2517, + -1, 1100, + 90, 2150, + -2, 2621, + -1, 1103, + 90, 2153, + -2, 2722, -1, 1104, - 90, 2151, - -2, 2721, - -1, 1105, - 90, 2152, - -2, 2591, + 90, 2154, + -2, 2562, -1, 1106, - 90, 2153, - -2, 2638, + 90, 2156, + -2, 2725, -1, 1107, - 90, 2154, - -2, 2602, + 90, 2157, + -2, 2727, -1, 1108, - 90, 2155, - -2, 2628, - -1, 1119, - 90, 2027, - -2, 2827, - -1, 1120, - 90, 2028, - -2, 2828, - -1, 1121, - 90, 2029, - -2, 2829, - -1, 1250, - 132, 2377, - 143, 2377, - 176, 2377, - -2, 2343, - -1, 1399, + 90, 2160, + -2, 2734, + -1, 1109, + 90, 2161, + -2, 2603, + -1, 1110, + 90, 2162, + -2, 2651, + -1, 1111, + 90, 2163, + -2, 2615, + -1, 1112, + 90, 2164, + -2, 2641, + -1, 1123, + 90, 2036, + -2, 2840, + -1, 1124, + 90, 2037, + -2, 2841, + -1, 1125, + 90, 2038, + -2, 2842, + -1, 1254, + 132, 2387, + 143, 2387, + 176, 2387, + -2, 2352, + -1, 1403, 24, 1029, -2, 956, - -1, 1602, + -1, 1606, 11, 994, 24, 994, - -2, 1868, - -1, 1704, + -2, 1877, + -1, 1708, 24, 1029, -2, 956, - -1, 2032, + -1, 2036, 46, 743, 280, 743, 307, 750, 308, 750, - 548, 743, + 552, 743, -2, 783, - -1, 2141, - 90, 2213, - -2, 2610, - -1, 2142, - 90, 2214, - -2, 2611, - -1, 2451, + -1, 2145, + 90, 2222, + -2, 2623, + -1, 2146, + 90, 2223, + -2, 2624, + -1, 2455, 11, 994, 24, 994, -2, 1175, - -1, 2925, + -1, 2929, 91, 1250, -2, 1256, - -1, 2942, - 115, 1467, - 163, 1467, - 211, 1467, - 214, 1467, - 320, 1467, + -1, 2946, + 115, 1476, + 163, 1476, + 211, 1476, + 214, 1476, + 320, 1476, + -2, 1469, + -1, 2949, + 38, 1512, -2, 1460, - -1, 2945, - 38, 1503, - -2, 1451, - -1, 3442, - 91, 2329, - 177, 2329, - -2, 2593, - -1, 3443, - 91, 2329, - 177, 2329, - -2, 2592, - -1, 3445, - 91, 2325, - 177, 2325, - -2, 2579, -1, 3446, - 91, 2281, - 177, 2281, - -2, 2584, + 91, 2338, + 177, 2338, + -2, 2605, -1, 3447, - 91, 2282, - 177, 2282, - -2, 2492, - -1, 3448, - 91, 2283, - 177, 2283, - -2, 2485, + 91, 2338, + 177, 2338, + -2, 2604, -1, 3449, - 91, 2325, - 177, 2325, - -2, 2362, + 91, 2334, + 177, 2334, + -2, 2591, -1, 3450, - 91, 2285, - 177, 2285, - -2, 2581, + 91, 2290, + 177, 2290, + -2, 2596, -1, 3451, - 91, 2286, - 177, 2286, - -2, 2490, + 91, 2291, + 177, 2291, + -2, 2503, -1, 3452, - 91, 2287, - 177, 2287, - -2, 2484, + 91, 2292, + 177, 2292, + -2, 2496, -1, 3453, - 91, 2288, - 177, 2288, - -2, 2460, + 91, 2334, + 177, 2334, + -2, 2371, -1, 3454, - 91, 2329, - 177, 2329, - -2, 2461, + 91, 2294, + 177, 2294, + -2, 2593, -1, 3455, - 91, 2329, - 177, 2329, - -2, 2462, + 91, 2295, + 177, 2295, + -2, 2501, -1, 3456, - 91, 2329, - 177, 2329, - -2, 2463, + 91, 2296, + 177, 2296, + -2, 2495, -1, 3457, - 91, 2329, - 177, 2329, - -2, 2464, + 91, 2297, + 177, 2297, + -2, 2471, -1, 3458, - 91, 2329, - 177, 2329, - -2, 2465, + 91, 2338, + 177, 2338, + -2, 2472, -1, 3459, - 91, 2329, - 177, 2329, - -2, 2466, + 91, 2338, + 177, 2338, + -2, 2473, + -1, 3460, + 91, 2338, + 177, 2338, + -2, 2474, -1, 3461, - 91, 2297, - 177, 2297, - -2, 2628, + 91, 2338, + 177, 2338, + -2, 2475, -1, 3462, - 91, 2268, - 177, 2268, - -2, 2613, + 91, 2338, + 177, 2338, + -2, 2476, -1, 3463, - 91, 2327, - 177, 2327, - -2, 2582, - -1, 3464, - 91, 2327, - 177, 2327, - -2, 2612, + 91, 2338, + 177, 2338, + -2, 2477, -1, 3465, - 91, 2327, - 177, 2327, - -2, 2397, + 91, 2306, + 177, 2306, + -2, 2641, -1, 3466, - 91, 2325, - 177, 2325, - -2, 2602, + 91, 2277, + 177, 2277, + -2, 2626, -1, 3467, - 90, 2248, - 91, 2248, - 166, 2248, - 167, 2248, - 169, 2248, - 177, 2248, - -2, 2361, + 91, 2336, + 177, 2336, + -2, 2594, -1, 3468, - 90, 2249, - 91, 2249, - 166, 2249, - 167, 2249, - 169, 2249, - 177, 2249, - -2, 2363, + 91, 2336, + 177, 2336, + -2, 2625, -1, 3469, - 90, 2250, - 91, 2250, - 166, 2250, - 167, 2250, - 169, 2250, - 177, 2250, - -2, 2657, + 91, 2336, + 177, 2336, + -2, 2407, -1, 3470, - 90, 2252, - 91, 2252, - 166, 2252, - 167, 2252, - 169, 2252, - 177, 2252, - -2, 2583, + 91, 2334, + 177, 2334, + -2, 2615, -1, 3471, - 90, 2254, - 91, 2254, - 166, 2254, - 167, 2254, - 169, 2254, - 177, 2254, - -2, 2561, + 90, 2257, + 91, 2257, + 166, 2257, + 167, 2257, + 169, 2257, + 177, 2257, + -2, 2370, -1, 3472, - 90, 2256, - 91, 2256, - 166, 2256, - 167, 2256, - 169, 2256, - 177, 2256, - -2, 2491, - -1, 3473, 90, 2258, 91, 2258, 166, 2258, 167, 2258, 169, 2258, 177, 2258, - -2, 2446, - -1, 3474, + -2, 2372, + -1, 3473, 90, 2259, 91, 2259, 166, 2259, 167, 2259, 169, 2259, 177, 2259, - -2, 2447, - -1, 3475, + -2, 2670, + -1, 3474, 90, 2261, 91, 2261, 166, 2261, 167, 2261, 169, 2261, 177, 2261, - -2, 2360, + -2, 2595, + -1, 3475, + 90, 2263, + 91, 2263, + 166, 2263, + 167, 2263, + 169, 2263, + 177, 2263, + -2, 2573, -1, 3476, - 91, 2332, - 166, 2332, - 167, 2332, - 169, 2332, - 177, 2332, - -2, 2404, + 90, 2265, + 91, 2265, + 166, 2265, + 167, 2265, + 169, 2265, + 177, 2265, + -2, 2502, -1, 3477, - 91, 2332, - 166, 2332, - 167, 2332, - 169, 2332, - 177, 2332, - -2, 2421, + 90, 2267, + 91, 2267, + 166, 2267, + 167, 2267, + 169, 2267, + 177, 2267, + -2, 2457, -1, 3478, - 91, 2335, - 166, 2335, - 167, 2335, - 169, 2335, - 177, 2335, - -2, 2398, + 90, 2268, + 91, 2268, + 166, 2268, + 167, 2268, + 169, 2268, + 177, 2268, + -2, 2458, -1, 3479, - 91, 2335, - 166, 2335, - 167, 2335, - 169, 2335, - 177, 2335, - -2, 2511, + 90, 2270, + 91, 2270, + 166, 2270, + 167, 2270, + 169, 2270, + 177, 2270, + -2, 2369, -1, 3480, - 91, 2332, - 166, 2332, - 167, 2332, - 169, 2332, - 177, 2332, - -2, 2541, + 91, 2341, + 166, 2341, + 167, 2341, + 169, 2341, + 177, 2341, + -2, 2414, -1, 3481, - 91, 2305, - 177, 2305, - -2, 2425, + 91, 2341, + 166, 2341, + 167, 2341, + 169, 2341, + 177, 2341, + -2, 2431, -1, 3482, - 91, 2306, - 177, 2306, - -2, 2526, + 91, 2344, + 166, 2344, + 167, 2344, + 169, 2344, + 177, 2344, + -2, 2408, -1, 3483, - 91, 2307, - 177, 2307, - -2, 2481, + 91, 2344, + 166, 2344, + 167, 2344, + 169, 2344, + 177, 2344, + -2, 2522, -1, 3484, - 91, 2308, - 177, 2308, - -2, 2527, + 91, 2341, + 166, 2341, + 167, 2341, + 169, 2341, + 177, 2341, + -2, 2553, -1, 3485, - 91, 2309, - 177, 2309, - -2, 2426, - -1, 3486, - 91, 2310, - 177, 2310, - -2, 2498, - -1, 3487, - 91, 2311, - 177, 2311, - -2, 2497, - -1, 3488, - 91, 2312, - 177, 2312, - -2, 2499, - -1, 3489, - 91, 2313, - 177, 2313, - -2, 2428, - -1, 3490, 91, 2314, 177, 2314, - -2, 2427, - -1, 3491, + -2, 2435, + -1, 3486, 91, 2315, 177, 2315, - -2, 2429, - -1, 3492, + -2, 2537, + -1, 3487, 91, 2316, 177, 2316, - -2, 2430, - -1, 3493, + -2, 2492, + -1, 3488, 91, 2317, 177, 2317, - -2, 2431, - -1, 3494, + -2, 2538, + -1, 3489, 91, 2318, 177, 2318, - -2, 2432, - -1, 3495, + -2, 2436, + -1, 3490, 91, 2319, 177, 2319, - -2, 2433, - -1, 3496, + -2, 2509, + -1, 3491, 91, 2320, 177, 2320, - -2, 2434, - -1, 3497, + -2, 2508, + -1, 3492, 91, 2321, 177, 2321, - -2, 2435, - -1, 3498, + -2, 2510, + -1, 3493, 91, 2322, 177, 2322, - -2, 2436, - -1, 3534, - 115, 1467, - 163, 1467, - 211, 1467, - 214, 1467, - 320, 1467, - -2, 1461, - -1, 3550, - 38, 1504, - -2, 1506, - -1, 3551, - 38, 1505, - -2, 1507, - -1, 3584, + -2, 2438, + -1, 3494, + 91, 2323, + 177, 2323, + -2, 2437, + -1, 3495, + 91, 2324, + 177, 2324, + -2, 2439, + -1, 3496, + 91, 2325, + 177, 2325, + -2, 2440, + -1, 3497, + 91, 2326, + 177, 2326, + -2, 2441, + -1, 3498, + 91, 2327, + 177, 2327, + -2, 2442, + -1, 3499, + 91, 2328, + 177, 2328, + -2, 2443, + -1, 3500, + 91, 2329, + 177, 2329, + -2, 2444, + -1, 3501, + 91, 2330, + 177, 2330, + -2, 2445, + -1, 3502, + 91, 2331, + 177, 2331, + -2, 2446, + -1, 3538, + 115, 1476, + 163, 1476, + 211, 1476, + 214, 1476, + 320, 1476, + -2, 1470, + -1, 3554, + 38, 1513, + -2, 1515, + -1, 3555, + 38, 1514, + -2, 1516, + -1, 3588, 87, 849, 177, 849, - -2, 1729, - -1, 4120, - 214, 1467, - 344, 1830, - -2, 1794, - -1, 4152, + -2, 1738, + -1, 4124, + 214, 1476, + 344, 1839, + -2, 1803, + -1, 4156, 11, 994, 24, 994, - -2, 1868, - -1, 4339, - 38, 1503, - 115, 1467, - 163, 1467, - 211, 1467, - 214, 1467, - -2, 1660, - -1, 4348, - 115, 1467, - 163, 1467, - 211, 1467, - 214, 1467, - -2, 1660, - -1, 4366, + -2, 1877, + -1, 4343, + 38, 1512, + 115, 1476, + 163, 1476, + 211, 1476, + 214, 1476, + -2, 1669, + -1, 4352, + 115, 1476, + 163, 1476, + 211, 1476, + 214, 1476, + -2, 1669, + -1, 4370, 87, 849, 177, 849, - -2, 1729, - -1, 4417, - 214, 1467, - 344, 1830, - -2, 1795, - -1, 4604, - 115, 1467, - 163, 1467, - 211, 1467, - 214, 1467, - -2, 1661, - -1, 4689, - 91, 1587, - 177, 1587, - -2, 1467, - -1, 4899, - 91, 1587, - 177, 1587, - -2, 1467, - -1, 5136, - 91, 1591, - 177, 1591, - -2, 1467, - -1, 5203, - 91, 1592, - 177, 1592, - -2, 1467, + -2, 1738, + -1, 4421, + 214, 1476, + 344, 1839, + -2, 1804, + -1, 4608, + 115, 1476, + 163, 1476, + 211, 1476, + 214, 1476, + -2, 1670, + -1, 4693, + 91, 1596, + 177, 1596, + -2, 1476, + -1, 4903, + 91, 1596, + 177, 1596, + -2, 1476, + -1, 5140, + 91, 1600, + 177, 1600, + -2, 1476, + -1, 5207, + 91, 1601, + 177, 1601, + -2, 1476, } const yyPrivate = 57344 -const yyLast = 82870 +const yyLast = 83265 var yyAct = [...]int{ - 983, 958, 5272, 5252, 985, 5245, 5303, 5290, 3645, 5249, - 263, 5262, 5228, 5146, 5244, 3762, 5140, 4901, 2120, 5150, - 4390, 5091, 1975, 968, 4400, 4500, 3907, 5151, 4109, 5139, - 4899, 4090, 3162, 5041, 5080, 4723, 4929, 4758, 3957, 4198, - 4434, 1861, 3421, 4803, 3216, 3634, 1974, 3959, 4946, 4495, - 4199, 1641, 4898, 4855, 4987, 1486, 774, 4593, 3417, 2068, - 3541, 4314, 44, 5092, 1014, 2195, 5094, 1878, 4196, 3637, - 4637, 4902, 1915, 4833, 796, 1249, 4328, 4508, 4322, 3799, - 3041, 1461, 1493, 2182, 3339, 4418, 813, 823, 3365, 833, - 4115, 4622, 833, 250, 5, 3587, 4371, 3340, 1405, 1877, - 4600, 4027, 1457, 3542, 3543, 3419, 4606, 4009, 960, 2959, - 3553, 1258, 4576, 3209, 4179, 3763, 2123, 2567, 4013, 910, - 4312, 1256, 2452, 2422, 2570, 1440, 3731, 3664, 4274, 3761, - 3333, 4117, 1879, 4125, 4105, 4349, 3250, 3623, 3758, 2445, - 3429, 4149, 3617, 3437, 2619, 248, 3621, 4266, 3615, 829, - 2178, 3749, 2073, 170, 3520, 901, 3978, 2201, 1854, 3991, - 4028, 4124, 3030, 2980, 3989, 2733, 4030, 950, 4029, 3157, - 1985, 4040, 2965, 2316, 2968, 905, 4025, 1891, 3535, 2886, - 1958, 2960, 2776, 3939, 1166, 1967, 1789, 2885, 2729, 3189, - 951, 2624, 3168, 955, 1963, 3435, 2584, 3508, 1706, 3249, - 813, 1210, 2563, 2728, 3502, 3666, 1243, 3132, 2446, 3042, - 2465, 3045, 1948, 2454, 3650, 237, 3556, 3014, 2179, 3591, - 3590, 2979, 2453, 2957, 2963, 259, 7, 4, 885, 2385, - 1757, 2942, 2196, 1787, 774, 258, 6, 1322, 2115, 2730, - 959, 1496, 1988, 1916, 817, 1833, 2970, 813, 2933, 2967, - 1870, 1801, 1980, 795, 2428, 1839, 2412, 2189, 2936, 1475, - 2888, 2119, 773, 263, 2106, 263, 949, 1312, 1313, 2772, - 1438, 1497, 2157, 2072, 1240, 813, 26, 1421, 2641, 1923, - 898, 2376, 1443, 244, 1838, 2380, 1242, 1806, 38, 2384, - 2114, 1835, 956, 1264, 77, 1794, 1209, 969, 1127, 5229, - 951, 1732, 243, 77, 2406, 1130, 86, 1265, 1790, 907, - 54, 42, 1132, 908, 3133, 2202, 29, 814, 87, 249, - 1131, 27, 1291, 1129, 28, 236, 19, 1207, 1642, 10, - 832, 1471, 1189, 1788, 904, 870, 78, 4972, 153, 1202, - 81, 5, 80, 76, 2708, 4829, 3389, 1126, 2433, 2437, - 2438, 2439, 2434, 2444, 2435, 2441, 3389, 3389, 2436, 1565, - 2442, 1566, 1564, 1565, 2457, 1566, 1564, 1565, 2344, 1566, - 1564, 1309, 4212, 4369, 3949, 825, 4065, 16, 3948, 3814, - 830, 3813, 2745, 1406, 4556, 4331, 3134, 1733, 1407, 4191, - 3282, 3195, 1260, 3193, 1734, 2433, 2437, 2438, 2439, 2434, - 2444, 2435, 2441, 805, 2333, 2436, 3192, 2442, 1308, 3589, - 1310, 3190, 1930, 1305, 803, 247, 799, 1926, 800, 1304, - 801, 802, 837, 3366, 2433, 2437, 2438, 2439, 2434, 2444, - 2435, 2441, 4750, 4074, 2436, 3334, 2442, 2849, 4372, 2958, - 3217, 798, 2884, 1727, 2197, 3208, 1453, 1454, 1455, 1837, - 1406, 5069, 1452, 1684, 1262, 2666, 1345, 1989, 2697, 4217, - 2698, 2693, 1124, 1895, 827, 1816, 1305, 1761, 32, 4347, - 3163, 4540, 1305, 7, 1153, 1303, 2667, 3950, 1150, 1451, - 1592, 3946, 1598, 6, 2899, 2433, 2437, 2438, 2439, 2434, - 2444, 2435, 2441, 2891, 1123, 2436, 4999, 2442, 1595, 1597, - 1591, 2340, 1596, 1578, 1577, 1587, 1588, 1589, 1590, 1580, - 1581, 1582, 1583, 1584, 1585, 1586, 1579, 1736, 3925, 1593, - 3922, 5240, 1943, 1513, 3932, 3930, 9, 17, 3923, 3920, - 2327, 2880, 1715, 1928, 4493, 2796, 3794, 3792, 1994, 2629, - 4715, 77, 1565, 4205, 1566, 1564, 5148, 5147, 4941, 1254, - 1257, 15, 4766, 4759, 4496, 828, 77, 1255, 77, 1345, - 1253, 2433, 2437, 2438, 2439, 2434, 2444, 2435, 2441, 3934, - 4197, 2436, 1124, 2442, 5097, 1565, 2425, 1566, 1564, 2962, - 1636, 1069, 1128, 2324, 3885, 3976, 4545, 2237, 2180, 2181, - 2648, 3112, 2686, 1363, 1364, 1325, 3077, 5277, 4955, 1141, - 4543, 5236, 4775, 2047, 1123, 4772, 4952, 4815, 4303, 3314, - 2906, 1228, 5053, 1892, 824, 1444, 1353, 1357, 1359, 1361, - 1366, 1742, 1371, 1367, 1368, 1369, 1370, 1740, 2558, 1348, - 1349, 1350, 1351, 1323, 1324, 1354, 1739, 1326, 826, 1327, - 1329, 1330, 1331, 1332, 1328, 1333, 1334, 1335, 1336, 1337, - 1344, 1346, 1338, 1339, 1340, 1341, 1342, 1343, 1372, 1373, - 1374, 1375, 1376, 1377, 1378, 1379, 1381, 1380, 1382, 1383, - 1384, 1385, 1386, 1387, 1388, 1389, 1356, 1358, 1360, 1362, - 1365, 1449, 4298, 1450, 3979, 957, 939, 1992, 2923, 941, - 1154, 1151, 1267, 2526, 940, 903, 1363, 1364, 1325, 3880, - 1154, 2743, 1314, 1783, 1151, 3756, 2937, 2353, 1991, 3416, - 939, 1142, 3153, 941, 2351, 2065, 4817, 1347, 940, 1353, - 1357, 1359, 1361, 1366, 1561, 1371, 1367, 1368, 1369, 1370, - 3924, 3921, 1348, 1349, 1350, 1351, 1323, 1324, 1354, 3412, - 1326, 3798, 1327, 1329, 1330, 1331, 1332, 1328, 1333, 1334, - 1335, 1336, 1337, 1344, 1346, 1338, 1339, 1340, 1341, 1342, - 1343, 1372, 1373, 1374, 1375, 1376, 1377, 1378, 1379, 1381, - 1380, 1382, 1383, 1384, 1385, 1386, 1387, 1388, 1389, 1356, - 1358, 1360, 1362, 1365, 3414, 3154, 3140, 1221, 1219, 3139, - 1220, 3767, 3141, 3768, 3769, 2546, 1929, 1927, 813, 2547, - 2548, 2580, 1541, 2358, 2359, 1542, 3069, 3935, 813, 1840, - 3118, 1842, 1448, 813, 3409, 1409, 1749, 2440, 1224, 3117, - 1347, 3956, 1152, 1592, 1148, 1598, 1149, 200, 245, 199, - 232, 201, 4094, 1544, 3210, 833, 833, 1435, 2044, 813, - 813, 1595, 1597, 1591, 894, 1596, 1578, 1577, 1587, 1588, - 1589, 1590, 1580, 1581, 1582, 1583, 1584, 1585, 1586, 1579, - 3261, 3260, 1593, 2107, 2440, 3413, 2111, 939, 4092, 4783, - 941, 2063, 2122, 1492, 3504, 940, 1483, 1491, 1494, 1495, - 5101, 829, 829, 829, 3505, 1494, 1495, 5182, 2088, 1559, - 2110, 1229, 1252, 2440, 1413, 3410, 5154, 5155, 1251, 5101, - 5188, 1823, 1114, 238, 1113, 1115, 1116, 5100, 1117, 1118, - 1534, 5100, 5187, 1536, 1509, 5099, 5186, 1510, 1399, 3001, - 5099, 2438, 2439, 5082, 5098, 1490, 2856, 2343, 1400, 4774, - 5098, 5185, 5113, 1463, 3503, 1487, 1554, 1487, 5085, 1401, - 4547, 1537, 4971, 1225, 5231, 5232, 4944, 3800, 1726, 813, - 4828, 4224, 1460, 3239, 2440, 4200, 4762, 4200, 1539, 5082, - 1469, 3843, 3388, 3933, 3801, 1896, 3802, 1479, 1611, 200, - 245, 199, 232, 201, 1817, 1260, 1499, 1500, 1407, 1502, - 1503, 1407, 1762, 1505, 1604, 3805, 1410, 2747, 1407, 2042, - 4983, 1315, 4947, 4948, 4949, 4950, 200, 245, 199, 232, - 201, 200, 245, 2744, 4221, 1227, 3547, 2354, 4313, 3415, - 2564, 2668, 3685, 2739, 2352, 2064, 1409, 4585, 1512, 2112, - 1540, 2554, 1546, 3815, 1826, 1547, 4005, 3812, 3127, 2102, - 2440, 2781, 4320, 4544, 2707, 3750, 822, 1262, 3839, 3411, - 1305, 3120, 1305, 2109, 1305, 821, 2931, 1355, 810, 2443, - 3426, 4782, 3549, 1549, 4447, 1407, 5115, 3511, 4077, 4784, - 3837, 1305, 3382, 1412, 1414, 1417, 1530, 2746, 1305, 1556, - 1305, 229, 238, 4819, 4820, 1557, 1558, 3540, 1521, 4619, - 4635, 3274, 2126, 1145, 3111, 3075, 3114, 1226, 1529, 2222, - 4494, 246, 1532, 3793, 3744, 5153, 2443, 3113, 2557, 1429, - 3123, 3124, 1543, 2578, 2579, 1535, 1538, 825, 825, 825, - 1481, 3122, 830, 830, 830, 1260, 4933, 2126, 1254, 1424, - 1427, 3217, 1477, 1478, 1645, 2443, 1255, 3544, 1531, 1398, - 1734, 1223, 1472, 1476, 1476, 1476, 1594, 3191, 2042, 200, - 245, 199, 232, 201, 2524, 1729, 1731, 1402, 1735, 1734, - 1355, 4773, 4825, 4582, 4541, 4011, 805, 1768, 1146, 1472, - 1472, 4010, 3391, 2108, 1753, 1433, 1434, 803, 1756, 799, - 1931, 800, 4347, 801, 802, 1765, 4409, 1262, 1545, 4994, - 3931, 1766, 3130, 4995, 4998, 4997, 2443, 2654, 1551, 1128, - 1428, 1320, 1552, 1553, 794, 4875, 827, 827, 827, 1504, - 4546, 4315, 1445, 2038, 813, 813, 813, 3999, 1498, 1210, - 1533, 1501, 1911, 1518, 1511, 238, 4096, 1815, 1550, 1514, - 1515, 1741, 813, 1738, 1822, 4405, 4818, 1828, 819, 820, - 4975, 813, 1993, 246, 4806, 774, 774, 1520, 4610, 1147, - 1548, 1704, 1222, 2125, 2124, 774, 774, 1217, 1701, 1865, - 1865, 1705, 813, 4557, 1218, 1419, 4003, 4107, 4108, 1408, - 246, 4041, 2443, 4106, 1708, 246, 4337, 4185, 1612, 4024, - 3550, 3507, 942, 943, 944, 945, 946, 2525, 2125, 2124, - 1507, 1508, 833, 3557, 796, 1494, 1495, 828, 828, 828, - 1919, 1867, 5011, 5001, 1320, 4278, 942, 943, 944, 945, - 946, 4098, 4862, 4099, 4101, 4100, 263, 4845, 1863, 1863, - 4000, 4001, 3033, 4280, 1155, 774, 2218, 4011, 3032, 3035, - 835, 1494, 1495, 2215, 834, 3754, 4002, 2217, 2214, 2216, - 2220, 2221, 831, 4102, 1767, 2219, 2944, 2939, 3997, 2132, - 2136, 2137, 4453, 3940, 1824, 4988, 824, 824, 824, 5013, - 2133, 4391, 2043, 3546, 5042, 1657, 1658, 4091, 3639, 1426, - 1425, 4398, 200, 245, 199, 232, 201, 2918, 1470, 1748, - 826, 826, 826, 4812, 4565, 1872, 3034, 4289, 1482, 1827, - 4549, 4550, 4551, 1144, 1888, 1889, 3969, 3110, 2440, 2134, - 1198, 1971, 1199, 4411, 1130, 4011, 1976, 4292, 79, 4410, - 1759, 1760, 4982, 246, 1763, 1859, 1860, 1990, 1771, 1773, - 1774, 1775, 1776, 4006, 1778, 4711, 1711, 5297, 1782, 3126, - 2565, 3751, 3088, 5114, 3635, 3636, 3840, 3639, 3552, 2000, - 1805, 2013, 1737, 4700, 3087, 1709, 1254, 2016, 238, 4511, - 1712, 1844, 1846, 3029, 1255, 1431, 2045, 1699, 3510, 3517, - 1700, 1857, 1858, 942, 943, 944, 945, 946, 2039, 2058, - 1646, 4821, 1850, 4586, 1849, 4291, 1865, 1519, 1983, 2681, - 3108, 3109, 1828, 3686, 831, 3687, 3688, 823, 829, 1594, - 2739, 829, 829, 2225, 2226, 2227, 2228, 2229, 2230, 2223, - 2224, 2555, 4863, 1468, 4097, 2030, 1418, 4846, 2087, 2103, - 4706, 831, 2046, 3514, 3515, 1828, 4111, 1485, 1484, 1770, - 2096, 2096, 2042, 1828, 1487, 1828, 1828, 1467, 3513, 1466, - 1979, 1932, 5043, 5138, 4904, 4834, 1865, 4116, 1865, 1409, - 1960, 1961, 1981, 4245, 1938, 1793, 1800, 1908, 1875, 2059, - 79, 1882, 1986, 1796, 1797, 4112, 1949, 2755, 2757, 2758, - 5265, 1848, 2050, 2053, 2054, 2057, 1969, 1873, 3384, 3078, - 1881, 2078, 1876, 1966, 1962, 1795, 1970, 79, 4350, 3032, - 2070, 2071, 3551, 4107, 4108, 1257, 1890, 4491, 2086, 3033, - 1758, 2089, 2090, 5079, 2091, 3032, 3035, 2080, 2081, 903, - 805, 1836, 3016, 3018, 1599, 1744, 2751, 2753, 2754, 77, - 1984, 3998, 1865, 4275, 200, 245, 199, 232, 201, 2093, - 4153, 1769, 1906, 1920, 4133, 3270, 4358, 2268, 2270, 2269, - 1409, 2199, 5095, 1526, 3145, 1921, 246, 3116, 3073, 2889, - 3048, 4963, 1772, 4964, 2736, 1746, 2135, 2250, 1601, 1600, - 2553, 2541, 1977, 3034, 831, 1755, 1604, 1777, 1606, 1607, - 1608, 1609, 1610, 3715, 2240, 2241, 2242, 2092, 2336, 3554, - 1999, 2183, 4306, 2002, 2003, 2099, 3051, 2256, 4469, 3398, - 2257, 1416, 2231, 2232, 3169, 3170, 2236, 2036, 1197, 4168, - 238, 4155, 3842, 2943, 2251, 4903, 1902, 2919, 4103, 1781, - 1998, 2278, 2279, 2001, 1780, 1779, 797, 2258, 1230, 2260, - 4375, 2261, 2262, 2263, 825, 2267, 5266, 825, 825, 830, - 79, 3738, 830, 830, 4714, 839, 2118, 2074, 4702, 2076, - 2077, 2311, 4701, 838, 3683, 1409, 830, 2066, 1525, 4110, - 4965, 200, 245, 5137, 2084, 1130, 1211, 2345, 1472, 2031, - 2079, 2348, 2098, 2029, 2027, 4267, 813, 813, 813, 2028, - 3058, 2019, 1476, 2085, 2024, 2363, 2365, 2018, 2366, 2023, - 2369, 2370, 2017, 2138, 2373, 1476, 1184, 796, 2377, 4707, - 4708, 2773, 2381, 2322, 1865, 2387, 2388, 1260, 2390, 1828, - 2748, 2749, 1266, 813, 4211, 2234, 1865, 2116, 2100, 2101, - 3237, 1804, 813, 827, 2075, 3017, 827, 827, 2069, 3047, - 1210, 2314, 3406, 2423, 3049, 2910, 808, 238, 809, 1752, - 2021, 2448, 2448, 1815, 2448, 2335, 2361, 2458, 2459, 813, - 813, 2312, 2523, 2362, 2095, 2097, 2381, 2534, 2317, 3052, - 1865, 2538, 2539, 1167, 2113, 4282, 1815, 2321, 774, 1262, - 2117, 2360, 4961, 2909, 2756, 1743, 1941, 2341, 1944, 1945, - 2404, 4963, 774, 4964, 1865, 5263, 5264, 2414, 3050, 2912, - 2911, 1950, 1951, 1952, 1953, 1954, 1955, 831, 246, 4957, - 2143, 2144, 2145, 2146, 2147, 2148, 2149, 2150, 2151, 2152, - 2153, 2154, 4359, 1156, 828, 200, 245, 828, 828, 2159, - 2175, 2176, 2475, 2752, 2596, 2598, 4210, 1758, 813, 2381, - 1865, 2025, 2605, 3100, 813, 901, 901, 2325, 2377, 200, - 245, 2612, 1157, 2614, 2615, 2616, 2200, 2337, 1158, 2620, - 4375, 2235, 1946, 895, 4104, 1947, 263, 2477, 3399, 263, - 263, 2391, 263, 79, 4623, 895, 2249, 1791, 3716, 3718, - 3719, 3720, 3717, 824, 2259, 3585, 824, 824, 1745, 1747, - 4965, 2450, 2389, 2456, 1750, 1751, 2532, 2320, 2543, 2323, - 2022, 238, 1965, 2530, 2528, 5333, 1260, 826, 2529, 1231, - 826, 826, 2581, 2121, 2405, 2264, 2265, 2573, 2574, 1213, - 1214, 1215, 5299, 4020, 2020, 238, 5327, 2656, 2586, 2587, - 1424, 1427, 2371, 2372, 3057, 246, 2674, 5312, 3055, 2378, - 2379, 2315, 4932, 2550, 5332, 2552, 3706, 3707, 4175, 1277, - 2392, 2393, 2394, 1254, 4958, 1259, 2571, 2572, 5093, 897, - 2375, 1255, 896, 2601, 2313, 895, 2559, 774, 1262, 2607, - 2334, 897, 2338, 5089, 896, 2386, 3135, 2342, 1462, 813, - 5067, 2423, 2566, 813, 2544, 5325, 2618, 2400, 2661, 2542, - 2690, 1563, 2692, 1160, 1563, 1565, 2696, 1566, 1564, 2935, - 5323, 1428, 2582, 3564, 2701, 2702, 2703, 829, 2706, 1259, - 1828, 2628, 5314, 3072, 2631, 2632, 2604, 2634, 1964, 2652, - 3524, 3530, 3531, 3532, 3525, 3529, 3526, 3528, 3527, 2669, - 1563, 2537, 2418, 2419, 829, 2424, 2420, 2407, 2411, 1828, - 2042, 1165, 2407, 3586, 1415, 4170, 1865, 2732, 885, 831, - 2010, 2531, 1526, 1565, 3877, 1566, 1564, 1164, 3432, 2536, - 2873, 897, 1162, 1161, 896, 2817, 2007, 2008, 2549, 2816, - 2551, 1563, 2691, 2735, 5310, 2694, 2695, 2105, 2560, 2699, - 4021, 245, 4174, 1462, 2060, 2061, 1563, 5287, 3705, 246, - 200, 245, 2710, 2711, 3433, 3434, 2638, 4340, 5311, 2645, - 5284, 2116, 2713, 2714, 2684, 1563, 2790, 1462, 2617, 2329, - 2416, 5281, 3876, 246, 2591, 79, 2595, 4263, 2700, 3009, - 2653, 1702, 2657, 2760, 2602, 2608, 2609, 2718, 2721, 2610, - 2603, 1266, 2934, 2589, 2723, 3009, 2725, 1565, 2590, 1566, - 1564, 2413, 2625, 4724, 4725, 4726, 4730, 4728, 4729, 4731, - 4732, 4733, 4734, 4727, 1163, 1563, 238, 3048, 3051, 77, - 5311, 4064, 77, 77, 5279, 77, 2415, 200, 245, 245, - 1426, 1425, 2012, 5288, 4958, 1563, 2643, 2658, 4959, 200, - 245, 2011, 5274, 1160, 4223, 5259, 5285, 2663, 5220, 2677, - 2271, 2726, 3009, 2274, 2275, 2276, 2789, 5282, 2280, 2281, - 2282, 2283, 2285, 2286, 2287, 2288, 2289, 2290, 2291, 2292, - 2294, 2295, 2296, 2297, 2298, 2155, 2156, 2676, 2660, 1884, - 3269, 2173, 2174, 2678, 5205, 2177, 200, 245, 5168, 1982, - 3549, 200, 245, 825, 2827, 1526, 1526, 2830, 830, 2832, - 4175, 2680, 2735, 2662, 238, 2104, 2839, 1159, 4136, 2683, - 2741, 3586, 1162, 1161, 1260, 238, 3048, 3051, 4134, 2852, - 825, 2712, 4085, 4082, 4175, 830, 3985, 2719, 5275, 3915, - 2720, 5206, 200, 245, 5221, 1458, 2890, 4207, 2892, 1459, - 2894, 2895, 4051, 2709, 2898, 1523, 3982, 1135, 1136, 1137, - 1138, 2722, 813, 1828, 813, 1828, 1565, 3938, 1566, 1564, - 3570, 5167, 238, 1476, 2779, 2913, 1133, 238, 1286, 2727, - 5206, 3247, 2920, 2921, 5169, 950, 1262, 4923, 2738, 813, - 813, 3052, 827, 1266, 2932, 5166, 3047, 3032, 3046, 3385, - 3043, 3049, 3912, 813, 813, 813, 1524, 2855, 2857, 2858, - 2859, 1851, 2861, 2735, 246, 3214, 2734, 5156, 238, 827, - 2793, 3156, 2871, 2864, 246, 3916, 2966, 2966, 2972, 1565, - 1702, 1566, 1564, 2545, 2618, 2981, 1815, 1815, 1815, 1815, - 1815, 1815, 1815, 1815, 2761, 2872, 1524, 2759, 3027, 2766, - 1565, 2883, 1566, 1564, 2774, 3050, 2783, 1563, 2448, 1135, - 1136, 1137, 1138, 1287, 4581, 2870, 2716, 2764, 2765, 2159, - 2869, 2826, 2769, 2770, 2159, 3269, 2825, 2824, 1133, 2950, - 2682, 1563, 5134, 828, 2955, 3064, 2576, 2540, 3913, 5133, - 3052, 2414, 2414, 1799, 2186, 3047, 3032, 3046, 4744, 3043, - 3049, 246, 246, 2791, 5064, 2862, 986, 996, 4375, 2865, - 828, 3036, 4369, 246, 3019, 2903, 987, 2905, 988, 992, - 995, 991, 989, 990, 4362, 3873, 1139, 4319, 5014, 3784, - 2875, 2876, 1565, 5005, 1566, 1564, 1299, 1300, 1301, 4919, - 3588, 3044, 824, 1565, 1887, 1566, 1564, 1135, 1136, 1137, - 1138, 813, 2096, 1280, 3050, 1272, 1276, 1278, 3569, 1565, - 246, 1566, 1564, 3071, 4918, 246, 826, 4742, 5135, 824, - 3394, 1298, 774, 774, 1295, 2741, 2734, 3272, 3271, 1260, - 1409, 1283, 4917, 3238, 993, 1275, 1865, 813, 2878, 4462, - 2791, 2863, 2927, 826, 1130, 1565, 2812, 1566, 1564, 2969, - 2795, 2637, 3361, 4916, 2724, 4915, 246, 4866, 2045, 2893, - 3164, 3165, 4865, 2897, 2741, 994, 4831, 813, 2715, 1563, - 2679, 2623, 796, 2339, 2575, 2791, 3310, 3311, 1139, 1919, - 1995, 4800, 3180, 3304, 3182, 4797, 1281, 263, 1620, 1430, - 3044, 1262, 3038, 3053, 3054, 3151, 3040, 4642, 3039, 2982, - 2791, 2924, 2951, 1565, 2868, 1566, 1564, 3115, 1284, 3175, - 1371, 1367, 1368, 1369, 1370, 1285, 3008, 3309, 2791, 3308, - 3307, 3305, 4641, 2966, 2966, 3223, 1395, 1579, 3142, 1390, - 3143, 4069, 3234, 4567, 4566, 2973, 2974, 3303, 1645, 2791, - 3240, 2791, 3834, 2741, 5002, 4464, 2083, 4412, 2741, 3148, - 3149, 4385, 2791, 4382, 3245, 4356, 3002, 3007, 3248, 3251, - 1273, 4256, 2787, 3136, 3253, 4486, 1139, 1563, 1865, 2778, - 2777, 2977, 4252, 3026, 2983, 2984, 2985, 2986, 2987, 2988, - 2989, 4144, 4046, 4643, 1282, 3266, 1936, 1935, 2732, 3864, - 5003, 3810, 3167, 3190, 3808, 1865, 4189, 1865, 3574, 1196, - 3572, 5300, 1409, 3566, 829, 3306, 5289, 3359, 2977, 3346, - 3283, 3338, 1292, 1293, 1294, 1297, 5286, 1296, 1506, 4085, - 4082, 3284, 1274, 2367, 1601, 1600, 3242, 3255, 1844, 1846, - 3218, 2042, 3185, 4413, 3205, 3203, 1473, 4085, 3201, 4082, - 3155, 1563, 3199, 3010, 2976, 3125, 3131, 4257, 2874, 2835, - 2834, 1865, 1409, 2815, 3257, 3244, 3317, 2806, 4253, 3246, - 3262, 3137, 2368, 2805, 2804, 2792, 5283, 4145, 3567, 2740, - 2004, 5235, 3256, 3326, 4515, 1563, 1260, 3573, 1865, 3318, - 3567, 4042, 3263, 3264, 3575, 3267, 3573, 3152, 1599, 3567, - 3848, 2626, 3312, 1563, 4624, 1563, 2969, 1563, 4847, 2239, - 2238, 1458, 952, 3825, 1279, 1459, 3295, 1563, 2762, 2763, - 1863, 3211, 2977, 2767, 2768, 4353, 1853, 3362, 1599, 3327, - 3206, 3204, 3179, 1458, 3200, 3172, 3070, 1459, 3200, 3009, - 2977, 4351, 1601, 1600, 2873, 1563, 1563, 1863, 1262, 1563, - 4625, 3254, 1458, 1563, 2239, 2238, 1459, 3219, 1269, 1563, - 1563, 2791, 3392, 4973, 1855, 2741, 2005, 3396, 2170, 1474, - 3400, 4354, 1601, 1600, 4043, 1856, 3212, 813, 813, 813, - 3215, 3222, 3232, 3221, 2167, 2169, 2166, 4352, 2168, 3341, - 77, 4892, 4891, 3241, 4830, 3347, 1412, 1409, 3499, 4770, - 4848, 4704, 3184, 4703, 3280, 1865, 3958, 3298, 1828, 3300, - 3275, 4687, 4514, 4652, 1828, 2534, 4589, 4363, 4330, 3332, - 4044, 4176, 3336, 3368, 2966, 3370, 3371, 2284, 3364, 4166, - 3335, 4158, 1852, 3315, 3265, 4146, 3342, 3268, 5131, 3293, - 825, 4087, 3378, 3273, 4084, 830, 4849, 2055, 3581, 3584, - 4015, 1578, 1577, 1587, 1588, 1589, 1590, 1580, 1581, 1582, - 1583, 1584, 1585, 1586, 1579, 2051, 2116, 3747, 3379, 1288, - 3746, 3562, 2277, 1268, 1270, 3576, 3571, 3522, 3548, 3390, - 1815, 1271, 1815, 2819, 1646, 3281, 3147, 2896, 3577, 3578, - 2672, 3619, 3625, 2671, 3313, 2670, 1785, 3302, 1784, 1411, - 3640, 1409, 3285, 3286, 3642, 3643, 3644, 3288, 3289, 1409, - 1582, 1583, 1584, 1585, 1586, 1579, 2190, 3654, 2784, 3536, - 1565, 5130, 1566, 1564, 1409, 1409, 1409, 2096, 4372, 827, - 1409, 3669, 3961, 3670, 3671, 1924, 3672, 2626, 3674, 5, - 3377, 1792, 3424, 2408, 3539, 5007, 2190, 1792, 3785, 3669, - 1580, 1581, 1582, 1583, 1584, 1585, 1586, 1579, 3961, 1791, - 3518, 5184, 2448, 2165, 3321, 4856, 3537, 5173, 1458, 2475, - 2056, 4799, 1459, 1306, 1307, 4798, 3728, 1564, 1311, 2162, - 2164, 2161, 3328, 2163, 1566, 1564, 1458, 4718, 2052, 1565, - 1459, 1566, 1564, 774, 1565, 4717, 1566, 1564, 3380, 3381, - 4192, 2534, 3866, 4190, 2477, 1409, 1409, 1565, 2405, 1566, - 1564, 1565, 3958, 1566, 1564, 3439, 1865, 4047, 3194, 1924, - 828, 3765, 1587, 1588, 1589, 1590, 1580, 1581, 1582, 1583, - 1584, 1585, 1586, 1579, 3516, 3675, 3960, 813, 3558, 3733, - 3673, 3657, 1409, 1792, 3655, 3781, 4693, 3545, 3560, 1815, - 3500, 3501, 1565, 4692, 1566, 1564, 3278, 263, 3561, 5033, - 5034, 3297, 263, 5031, 5032, 3851, 2386, 3865, 3579, 3647, - 3583, 7, 5132, 3580, 1622, 3594, 3595, 3596, 3597, 824, - 3373, 6, 3374, 1565, 3659, 1566, 1564, 1621, 1919, 3806, - 3325, 5296, 3186, 5208, 1565, 3607, 1566, 1564, 3611, 3612, - 3613, 3608, 3606, 826, 5126, 5037, 3739, 3226, 3630, 3817, - 1565, 3627, 1566, 1564, 3628, 4896, 4897, 3225, 3251, 2954, - 1565, 3649, 1566, 1564, 3766, 3651, 3651, 5036, 3782, 3652, - 3651, 3158, 3159, 3656, 3662, 1565, 2254, 1566, 1564, 77, - 1865, 2808, 4583, 3836, 1925, 1565, 3653, 1566, 1564, 3752, - 1565, 2255, 1566, 1564, 3689, 4590, 4591, 5035, 5295, 3076, - 3807, 5030, 3079, 3080, 3081, 3082, 3083, 3084, 3085, 3086, - 3702, 3783, 3089, 3090, 3091, 3092, 3093, 3094, 3095, 3096, - 3097, 3098, 3099, 4317, 3101, 3102, 3103, 3104, 3105, 1565, - 3106, 1566, 1564, 3748, 3742, 5029, 3819, 5143, 3821, 3745, - 5028, 3727, 3786, 5027, 4584, 3725, 5050, 3790, 3826, 1565, - 5026, 1566, 1564, 2807, 3723, 3828, 3829, 4791, 3820, 3770, - 3773, 3823, 3824, 3775, 1565, 5025, 1566, 1564, 3521, 5023, - 3779, 3827, 3712, 1565, 3909, 1566, 1564, 3780, 3830, 4529, - 1565, 3440, 1566, 1564, 1565, 4318, 1566, 1564, 3788, 3787, - 1570, 1571, 1572, 1573, 1574, 1575, 1576, 1568, 5022, 5021, - 3439, 5020, 3944, 3726, 5019, 3947, 1565, 3724, 1566, 1564, - 3951, 5018, 813, 1828, 2799, 3928, 3722, 3869, 3774, 5016, - 5015, 3963, 3965, 3966, 3968, 5004, 3970, 3971, 3881, 3418, - 3883, 4527, 3886, 3887, 3711, 4518, 4907, 3888, 3889, 3890, - 3891, 3892, 1409, 3893, 3894, 3895, 3896, 3897, 3898, 3899, - 3900, 3838, 3902, 3903, 3904, 3905, 3833, 3822, 1565, 4517, - 1566, 1564, 1565, 4869, 1566, 1564, 4850, 3291, 3832, 3292, - 4824, 4516, 4796, 3910, 3803, 4760, 3914, 4713, 3917, 3918, - 3845, 4870, 4627, 4626, 4392, 3641, 1565, 2788, 1566, 1564, - 77, 3846, 4457, 3648, 3847, 77, 3863, 4241, 1565, 3857, - 1566, 1564, 4355, 3859, 3860, 4316, 4299, 4323, 3648, 3660, - 3661, 4004, 1987, 1565, 3665, 1566, 1564, 3993, 3995, 1565, - 3818, 1566, 1564, 3797, 1565, 3796, 1566, 1564, 4008, 3736, - 3856, 3710, 3858, 3709, 813, 1578, 1577, 1587, 1588, 1589, - 1590, 1580, 1581, 1582, 1583, 1584, 1585, 1586, 1579, 4023, - 3708, 1409, 3700, 3937, 3694, 813, 3693, 813, 2534, 3692, - 4329, 3691, 3690, 1409, 4231, 3954, 1409, 1565, 3386, 1566, - 1564, 3243, 3207, 3144, 2882, 2687, 2647, 2646, 2966, 3942, - 4055, 2966, 3941, 2644, 4058, 2640, 4059, 3927, 2639, 3764, - 3764, 1565, 4045, 1566, 1564, 2593, 1815, 2981, 2350, 4068, - 1577, 1587, 1588, 1589, 1590, 1580, 1581, 1582, 1583, 1584, - 1585, 1586, 1579, 4031, 2347, 3548, 1996, 1725, 3919, 3990, - 1393, 3229, 4019, 3878, 3231, 5278, 3440, 3619, 1069, 3230, - 3871, 4822, 4823, 3625, 4061, 5316, 5276, 4012, 4501, 4052, - 3926, 5233, 2786, 5192, 3064, 1565, 4022, 1566, 1564, 3870, - 1565, 3536, 1566, 1564, 3011, 1960, 1961, 1565, 5110, 1566, - 1564, 4122, 5108, 4122, 4126, 4804, 4126, 4126, 3867, 3986, - 3996, 1409, 1949, 1847, 5077, 1969, 1565, 4985, 1566, 1564, - 3360, 1392, 1966, 4113, 4137, 1970, 3358, 3539, 4054, 1962, - 3357, 5328, 1865, 1865, 4594, 1565, 4979, 1566, 1564, 4970, - 2317, 4968, 4089, 4951, 1260, 4942, 4057, 1565, 4906, 1566, - 1564, 4905, 4884, 1565, 3355, 1566, 1564, 1565, 4066, 1566, - 1564, 4883, 1565, 4018, 1566, 1564, 4649, 4874, 4093, 4095, - 4873, 5063, 3354, 4138, 4139, 4827, 4811, 4036, 4809, 4037, - 813, 1565, 4039, 1566, 1564, 4795, 4761, 3353, 3506, 4050, - 4053, 1863, 1863, 2969, 3993, 4056, 4048, 4697, 2405, 1565, - 4067, 1566, 1564, 4063, 4696, 4695, 1262, 1828, 4598, 4587, - 2534, 2534, 4572, 4571, 1565, 4083, 1566, 1564, 4569, 4086, - 1578, 1577, 1587, 1588, 1589, 1590, 1580, 1581, 1582, 1583, - 1584, 1585, 1586, 1579, 4121, 3317, 4123, 4562, 4560, 4539, - 4538, 4131, 4537, 4193, 3038, 3352, 1567, 4533, 3040, 4088, - 3039, 4532, 3351, 4505, 1603, 4503, 4499, 3350, 4497, 4475, - 4472, 4466, 3349, 3732, 1614, 4311, 4301, 4127, 4128, 3348, - 4284, 4132, 1565, 3345, 1566, 1564, 4268, 3854, 3855, 1565, - 3344, 1566, 1564, 4239, 1565, 4135, 1566, 1564, 4236, 1565, - 1624, 1566, 1564, 813, 4173, 4206, 1565, 4177, 1566, 1564, - 1565, 4164, 1566, 1564, 3343, 4143, 4163, 1565, 4161, 1566, - 1564, 4160, 4147, 4229, 4142, 4141, 4016, 4120, 3980, 3972, - 3962, 4208, 3952, 3945, 4232, 4647, 3983, 3943, 3681, 3682, - 5326, 1565, 2887, 1566, 1564, 3844, 3337, 3841, 4129, 4151, - 3816, 4148, 3795, 3322, 3698, 3699, 3772, 3740, 4171, 4172, - 4162, 3737, 4213, 4157, 4159, 4215, 4216, 4156, 4165, 4169, - 3734, 3721, 4218, 1565, 3713, 1566, 1564, 3703, 4203, 4209, - 1565, 3701, 1566, 1564, 3697, 4214, 3696, 3743, 3316, 1578, - 1577, 1587, 1588, 1589, 1590, 1580, 1581, 1582, 1583, 1584, - 1585, 1586, 1579, 3695, 3600, 3425, 3407, 4270, 3395, 3387, - 3224, 4271, 3213, 2620, 3173, 1565, 2914, 1566, 1564, 2901, - 3294, 1069, 1068, 5061, 2867, 4285, 1945, 4287, 2900, 5270, - 2750, 2651, 4293, 2650, 3619, 3625, 1713, 1950, 1951, 1952, - 1953, 1954, 1955, 2642, 2383, 3648, 1409, 1565, 2866, 1566, - 1564, 1565, 2860, 1566, 1564, 2349, 2346, 3648, 4281, 4220, - 3648, 4246, 4225, 2829, 4248, 4294, 2332, 4219, 2828, 2331, - 2094, 813, 2534, 4240, 1997, 1565, 2185, 1566, 1564, 1565, - 1653, 1566, 1564, 1649, 1648, 4338, 4288, 1397, 4290, 1143, - 1565, 5268, 1566, 1564, 5045, 1565, 4344, 1566, 1564, 200, - 245, 4260, 4801, 1565, 4790, 1566, 1564, 5258, 4309, 4310, - 3548, 3548, 5256, 4276, 2239, 2238, 4789, 4777, 245, 199, - 232, 201, 4771, 4570, 2448, 1815, 4366, 245, 199, 232, - 201, 4536, 5257, 2423, 4522, 4512, 4467, 5255, 5294, 4377, - 4360, 4348, 4346, 3619, 4339, 3251, 3625, 4264, 4387, 4269, - 4262, 4327, 4259, 4341, 4258, 3548, 4265, 4273, 4374, 4255, - 4254, 202, 4242, 4238, 4235, 3648, 4195, 4194, 4184, 4178, - 2293, 902, 4075, 4062, 3984, 238, 5292, 4414, 3981, 4300, - 1409, 4296, 4295, 4304, 4401, 4402, 4403, 4404, 3936, 4307, - 4122, 4151, 1918, 238, 1409, 1409, 4324, 4461, 3874, 3861, - 3853, 1865, 238, 2952, 3852, 3850, 3614, 3202, 4367, 3198, - 4326, 4336, 3197, 3196, 3161, 3160, 4470, 2840, 4345, 2833, - 2823, 4361, 2822, 2821, 4368, 2820, 2818, 2814, 2953, 813, - 2813, 2534, 2811, 2802, 2798, 1815, 1409, 2797, 2717, 2649, - 4261, 2304, 4463, 2302, 2301, 2300, 4487, 4365, 2299, 2253, - 2252, 263, 2243, 3875, 245, 1886, 4373, 3646, 5269, 5198, - 1863, 4448, 4305, 4364, 3439, 5197, 5196, 5172, 5165, 5163, - 5070, 5049, 1643, 5044, 4936, 4935, 4934, 4914, 4507, 4908, - 4882, 4383, 4483, 4384, 4389, 4381, 4738, 4441, 4716, 4710, - 4651, 4628, 4455, 4454, 4451, 4450, 4449, 4399, 4406, 4396, - 4394, 4332, 4283, 4279, 4237, 4078, 4476, 1578, 1577, 1587, - 1588, 1589, 1590, 1580, 1581, 1582, 1583, 1584, 1585, 1586, - 1579, 4072, 4460, 4456, 4458, 1965, 1968, 1940, 1959, 238, - 1956, 1942, 4465, 4468, 1937, 3977, 3974, 4473, 4471, 3973, - 2423, 3901, 3862, 4552, 4474, 1130, 3735, 4558, 3729, 3658, - 3565, 4481, 3559, 4564, 4477, 4479, 4506, 4480, 4510, 3555, - 4478, 4482, 1802, 3383, 3171, 2062, 4554, 3146, 3107, 2994, - 2975, 2926, 2925, 2881, 2771, 2160, 238, 4504, 4502, 2664, - 2417, 2328, 2040, 4521, 1957, 1724, 4523, 1703, 1698, 1697, - 1696, 2082, 1695, 1409, 1865, 1865, 4492, 1694, 4599, 4530, - 1693, 1409, 1692, 1691, 4526, 1690, 4528, 1689, 4561, 4520, - 4563, 1688, 1687, 1686, 1685, 1684, 1683, 1682, 1681, 1409, - 1680, 4038, 1679, 4630, 1678, 4524, 4519, 1677, 1676, 1675, - 1674, 1673, 1672, 4640, 4629, 4596, 4534, 1671, 1670, 1669, - 1668, 1667, 1666, 1665, 1664, 4393, 1663, 4395, 1662, 1661, - 1660, 1659, 1656, 1863, 2183, 1409, 1655, 1409, 4595, 4645, - 4601, 1654, 3548, 4548, 1652, 4542, 2128, 2129, 2130, 2131, - 1651, 4605, 4634, 4648, 1650, 4650, 1647, 4575, 4597, 1640, - 4605, 998, 172, 4578, 1639, 1637, 1636, 1635, 1634, 1865, - 3440, 172, 4588, 4686, 1633, 1632, 1631, 4555, 1630, 1629, - 1628, 1627, 4607, 4602, 4603, 4611, 4632, 4633, 813, 4621, - 1409, 1409, 1409, 4620, 2187, 1409, 1409, 4613, 2191, 2192, - 2193, 2194, 4368, 4618, 1626, 2669, 1603, 4636, 1625, 2233, - 4743, 4608, 1619, 1618, 1617, 1616, 1615, 1528, 2244, 1489, - 1394, 4644, 3592, 3593, 77, 5059, 4740, 5056, 2183, 4735, - 4452, 2941, 2705, 1436, 4756, 4757, 5212, 5210, 4448, 806, - 5152, 4154, 3741, 4691, 4688, 3523, 2272, 2273, 172, 3166, - 2958, 1865, 4698, 4752, 1527, 4720, 4721, 4722, 1456, 2992, - 4736, 4737, 3006, 3604, 4441, 4574, 2999, 2997, 3605, 4076, - 2991, 3000, 2998, 3603, 2303, 4746, 2305, 2306, 2307, 2308, - 2309, 4792, 4793, 82, 813, 2995, 2318, 1487, 4785, 3602, - 2996, 4745, 4769, 3004, 3601, 3005, 4080, 3598, 4805, 3003, - 4807, 2990, 4747, 1813, 4415, 5090, 4081, 4954, 4953, 4927, - 1863, 4484, 4748, 5324, 5320, 4690, 4357, 3809, 4459, 3665, - 3568, 1786, 4763, 2402, 2403, 2397, 2398, 2399, 3677, 4014, - 1125, 3021, 4768, 4808, 4343, 4810, 3678, 3679, 3680, 4118, - 4531, 4119, 3911, 3777, 4776, 3074, 4180, 4778, 4182, 4183, - 4764, 4840, 3023, 3022, 4407, 4308, 4786, 4079, 4787, 2515, - 3764, 804, 4485, 1933, 4813, 4181, 3563, 1982, 4788, 1133, - 3158, 3159, 2916, 1791, 3279, 2410, 1919, 2908, 2907, 4640, - 4640, 1978, 1216, 5068, 4859, 2915, 1982, 2611, 2527, 811, - 1522, 4851, 1409, 4911, 4836, 4568, 4032, 3988, 3987, 3629, - 1261, 4832, 4814, 4835, 3227, 3548, 3025, 4826, 2949, 172, - 4868, 2421, 2374, 2239, 2238, 5225, 4838, 5199, 4853, 1722, - 1723, 4910, 4839, 4140, 172, 3128, 172, 1720, 1721, 1718, - 1719, 3121, 4653, 3020, 4510, 4843, 4841, 2318, 4685, 2535, - 1125, 1517, 2318, 2318, 1716, 1717, 1516, 4609, 4854, 4857, - 4858, 1555, 3811, 2673, 2172, 4694, 4864, 1135, 1136, 1137, - 1138, 3610, 1259, 1488, 1133, 5179, 2917, 2675, 2585, 2049, - 1465, 1464, 1447, 1133, 1488, 4580, 1133, 3277, 2689, 1409, - 5177, 5116, 1865, 5087, 4579, 4924, 3276, 5086, 5084, 4989, - 4937, 4930, 4646, 4525, 4852, 4498, 4490, 4380, 4379, 4202, - 4201, 1707, 3059, 1206, 3028, 1885, 4489, 2627, 4187, 3882, - 2630, 3356, 1462, 2633, 5214, 5213, 2635, 4909, 5128, 5129, - 5201, 4559, 4286, 4921, 4272, 3648, 4251, 3831, 3402, 3401, - 3393, 4925, 4926, 3178, 3174, 2800, 2127, 1432, 1403, 4969, - 5213, 1863, 5214, 3764, 4920, 4962, 4712, 4974, 5200, 5183, - 1302, 5125, 1135, 1136, 1137, 1138, 4981, 1259, 5123, 5121, - 2171, 3422, 3420, 4577, 2877, 1973, 4333, 4334, 4335, 1259, - 4250, 807, 4938, 3290, 1135, 1136, 1137, 1138, 811, 2121, - 1480, 2121, 4976, 91, 4977, 4956, 4939, 2, 4960, 5238, - 5239, 1, 3929, 2879, 2326, 1714, 4986, 1578, 1577, 1587, - 1588, 1589, 1590, 1580, 1581, 1582, 1583, 1584, 1585, 1586, - 1579, 4978, 1134, 1841, 3138, 2577, 5017, 1869, 2330, 1140, - 3012, 3013, 3609, 1409, 4966, 4967, 3015, 4984, 2594, 1442, - 3408, 2737, 3753, 3119, 2930, 4007, 1803, 4990, 5000, 3548, - 1212, 5024, 4993, 2245, 2009, 1423, 2006, 1422, 5006, 4991, - 1420, 5009, 5010, 2188, 2266, 1409, 1000, 5039, 2961, 3730, - 1487, 3704, 4376, 1865, 5066, 5224, 5271, 5171, 5227, 2655, - 2035, 984, 5078, 4204, 3804, 4943, 5175, 5074, 4945, 4767, - 5055, 5057, 5058, 5060, 5062, 2742, 1560, 4049, 865, 1046, - 1012, 1638, 5038, 2659, 3884, 3879, 1011, 5071, 5072, 4321, - 3512, 5047, 4931, 5075, 5065, 4844, 866, 2636, 4940, 4765, - 1934, 1939, 3024, 4861, 5040, 4689, 5109, 4114, 3638, 1972, - 5054, 5096, 1863, 921, 920, 927, 917, 5012, 5076, 4397, - 4781, 4779, 4780, 1865, 5083, 5081, 924, 925, 3908, 926, - 930, 4928, 4749, 911, 5106, 909, 2556, 772, 5102, 5104, - 4739, 2956, 5111, 935, 2704, 4654, 5107, 5103, 5105, 4913, - 1182, 4302, 2940, 1183, 1175, 3534, 3533, 2139, 1569, 2780, - 3287, 2158, 4243, 2785, 5118, 5117, 4244, 5119, 1613, 954, - 2782, 2794, 3509, 5136, 4435, 3771, 90, 89, 88, 1865, - 271, 5127, 1863, 1003, 1578, 1577, 1587, 1588, 1589, 1590, - 1580, 1581, 1582, 1583, 1584, 1585, 1586, 1579, 4930, 270, - 4802, 4592, 5073, 981, 980, 979, 978, 977, 976, 2431, - 2803, 2430, 2432, 2429, 2427, 2426, 1810, 1809, 2810, 4186, - 5144, 4488, 5157, 3176, 3177, 5158, 2121, 5159, 3668, 5160, - 4408, 3663, 2466, 2463, 5162, 5161, 1832, 2464, 1863, 3031, - 5178, 3061, 5180, 5181, 5170, 2462, 5176, 1409, 2831, 5149, - 5174, 4226, 4513, 2836, 2837, 2838, 5051, 5052, 2841, 2842, - 2843, 2844, 2845, 2846, 2847, 2848, 2513, 2850, 2851, 4709, - 2853, 2854, 5189, 5191, 5190, 3714, 4509, 2801, 2396, 5195, - 1578, 1577, 1587, 1588, 1589, 1590, 1580, 1581, 1582, 1583, - 1584, 1585, 1586, 1579, 3909, 3056, 2483, 5203, 5204, 5202, - 5223, 5211, 5207, 2250, 2515, 3684, 2480, 5230, 5209, 2479, - 3676, 4705, 5215, 5216, 5217, 5218, 4699, 2512, 4996, 5248, - 5219, 5122, 5251, 5124, 4617, 1409, 4616, 5246, 5246, 5246, - 1409, 5243, 5120, 5250, 5253, 5234, 5237, 4615, 5096, 4386, - 3624, 4614, 5222, 5241, 4604, 5254, 5242, 4416, 4417, 4424, - 4900, 5039, 5261, 5260, 1352, 2948, 5267, 5273, 1321, 1316, - 2490, 1318, 1319, 1317, 3301, 4167, 912, 914, 913, 3037, - 172, 172, 172, 1261, 4026, 3431, 3430, 3428, 919, 3427, - 1764, 5280, 806, 4980, 5112, 4573, 5291, 3438, 5291, 3436, - 923, 1446, 2041, 2037, 1437, 5293, 1391, 938, 2971, 4741, - 3955, 1730, 1728, 3599, 916, 2993, 2250, 2583, 3776, 1811, - 5230, 1808, 4073, 5246, 5304, 1807, 5246, 5304, 5301, 5246, - 5304, 5250, 5308, 5307, 5253, 5306, 5309, 5302, 1290, 1289, - 2588, 5273, 5317, 1263, 239, 1880, 240, 171, 1122, 4277, - 4342, 2506, 5321, 5322, 3755, 5313, 5298, 5315, 3129, 3228, - 3618, 4816, 2401, 1176, 2938, 126, 48, 2121, 5330, 144, - 125, 1818, 1819, 218, 1821, 69, 217, 1825, 68, 1829, - 1830, 1831, 20, 142, 216, 67, 53, 52, 214, 5329, - 121, 5331, 120, 119, 118, 141, 213, 66, 255, 254, - 257, 1396, 256, 253, 3187, 3188, 252, 1922, 251, 5088, - 4612, 1206, 5164, 51, 50, 219, 1404, 49, 127, 70, - 1893, 1894, 47, 1602, 1897, 1898, 1899, 1900, 1901, 46, - 1903, 1904, 1905, 2318, 1907, 2318, 1910, 2688, 1912, 1913, - 1914, 3975, 1439, 1441, 2048, 4297, 3423, 2922, 45, 40, - 2494, 14, 12, 43, 2318, 2318, 41, 25, 24, 2014, - 23, 2500, 31, 30, 13, 39, 4842, 37, 36, 918, - 922, 928, 163, 929, 931, 162, 35, 932, 933, 934, - 3868, 2488, 2522, 936, 937, 2489, 2491, 2493, 161, 2495, - 2496, 2497, 2501, 2502, 2503, 2505, 2508, 2509, 2510, 160, - 159, 1918, 4871, 4872, 158, 157, 2498, 2507, 2499, 4876, - 4877, 4878, 4879, 4880, 4881, 156, 155, 34, 4885, 4886, - 4887, 4888, 4889, 4890, 22, 61, 60, 59, 58, 4893, - 4894, 4895, 57, 56, 1578, 1577, 1587, 1588, 1589, 1590, - 1580, 1581, 1582, 1583, 1584, 1585, 1586, 1579, 2775, 8, - 4370, 148, 1562, 3233, 147, 3236, 149, 145, 140, 138, - 2514, 33, 139, 136, 137, 130, 129, 128, 122, 102, - 101, 100, 1578, 1577, 1587, 1588, 1589, 1590, 1580, 1581, - 1582, 1583, 1584, 1585, 1586, 1579, 1125, 115, 114, 4922, - 113, 112, 111, 110, 1710, 108, 109, 864, 3258, 3259, - 99, 5193, 1578, 1577, 1587, 1588, 1589, 1590, 1580, 1581, - 1582, 1583, 1584, 1585, 1586, 1579, 98, 97, 96, 95, - 135, 134, 131, 117, 124, 123, 106, 116, 107, 105, - 2511, 104, 103, 94, 93, 92, 133, 132, 146, 915, - 220, 71, 197, 196, 195, 194, 193, 191, 2486, 192, - 190, 189, 2485, 188, 187, 186, 185, 62, 63, 64, - 65, 209, 208, 210, 222, 221, 212, 3296, 215, 211, - 3299, 206, 204, 207, 205, 203, 2504, 83, 1814, 2121, - 11, 143, 3319, 3320, 3764, 2492, 4992, 21, 18, 3, - 0, 3323, 3324, 0, 0, 2513, 0, 0, 0, 0, - 2473, 0, 0, 2521, 0, 0, 0, 3329, 3330, 3331, - 0, 0, 0, 0, 0, 0, 1874, 0, 0, 1883, - 0, 0, 1345, 0, 0, 0, 806, 0, 0, 0, - 0, 0, 0, 2515, 2482, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 2516, 2517, 3367, 0, 3369, 0, - 0, 3372, 0, 2128, 0, 0, 2318, 5048, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 172, 0, 0, - 2481, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 2490, - 0, 0, 0, 2513, 0, 0, 0, 0, 2473, 0, - 0, 2521, 0, 0, 0, 0, 0, 1206, 1206, 1798, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 1820, 0, 0, 0, 0, - 0, 2515, 2482, 0, 1834, 0, 0, 0, 0, 0, - 0, 0, 2516, 2517, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 1871, 0, 0, 0, 1363, - 1364, 1325, 0, 0, 0, 0, 0, 0, 2481, 0, - 2506, 0, 0, 0, 0, 0, 0, 172, 0, 0, - 172, 172, 1353, 1357, 1359, 1361, 1366, 2490, 1371, 1367, - 1368, 1369, 1370, 0, 172, 1348, 1349, 1350, 1351, 1323, - 1324, 1354, 0, 1326, 0, 1327, 1329, 1330, 1331, 1332, - 1328, 1333, 1334, 1335, 1336, 1337, 1344, 1346, 1338, 1339, - 1340, 1341, 1342, 1343, 1372, 1373, 1374, 1375, 1376, 1377, - 1378, 1379, 1381, 1380, 1382, 1383, 1384, 1385, 1386, 1387, - 1388, 1389, 1356, 1358, 1360, 1362, 1365, 0, 0, 2487, - 0, 0, 0, 0, 0, 0, 0, 0, 2468, 2474, - 2472, 0, 0, 0, 2469, 0, 0, 0, 2506, 2494, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 2500, 0, 0, 1347, 0, 0, 0, 0, 2484, 0, - 2467, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 2488, 2522, 0, 0, 2489, 2491, 2493, 0, 2495, 2496, - 2497, 2501, 2502, 2503, 2505, 2508, 2509, 2510, 0, 2318, - 0, 0, 0, 0, 0, 2498, 2507, 2499, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 2476, 0, 2585, - 0, 0, 0, 0, 0, 0, 0, 2599, 2600, 2513, - 0, 0, 0, 0, 2606, 0, 3632, 3633, 2472, 0, - 0, 0, 3631, 2613, 0, 0, 0, 2494, 0, 0, - 0, 0, 0, 3789, 0, 3791, 0, 0, 2500, 2514, - 0, 0, 0, 0, 0, 1602, 0, 2515, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 2488, 2522, - 1918, 0, 2489, 2491, 2493, 0, 2495, 2496, 2497, 2501, - 2502, 2503, 2505, 2508, 2509, 2510, 0, 0, 0, 0, - 0, 0, 0, 2498, 2507, 2499, 0, 0, 0, 0, - 0, 2470, 2471, 0, 0, 2476, 0, 0, 0, 2665, - 0, 0, 0, 2490, 0, 0, 0, 0, 0, 2511, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 2486, 0, 0, - 0, 2485, 0, 0, 0, 0, 0, 2514, 0, 0, - 3849, 0, 200, 245, 199, 232, 201, 0, 0, 0, + 987, 5311, 5284, 962, 5276, 5249, 5318, 989, 5256, 5253, + 263, 5232, 5299, 5266, 3649, 5248, 5150, 5144, 4905, 3766, + 2124, 4394, 1979, 1978, 972, 5095, 4504, 3911, 5155, 5154, + 4113, 4903, 5143, 5045, 3166, 4094, 4438, 5084, 3961, 4202, + 4933, 3425, 4727, 4807, 4762, 3220, 1865, 3638, 3963, 4499, + 1645, 4902, 1490, 4950, 4859, 4203, 778, 4991, 4597, 3545, + 5096, 4906, 3421, 4318, 4200, 2199, 5098, 1882, 1018, 3641, + 4641, 4837, 4512, 1497, 800, 1253, 4326, 3803, 4404, 4332, + 1461, 1919, 3045, 4422, 3343, 2186, 817, 827, 3369, 837, + 4626, 4119, 837, 4375, 1881, 1409, 3591, 4604, 2072, 3344, + 250, 5, 3557, 4031, 4278, 3423, 4610, 964, 4580, 4013, + 2127, 2426, 4183, 1262, 3213, 3767, 2571, 1444, 170, 4017, + 2574, 1260, 2963, 4316, 3735, 3546, 3668, 3547, 3337, 248, + 3765, 4121, 2456, 4109, 3627, 1883, 4129, 4353, 914, 3254, + 2449, 3762, 3621, 3441, 4153, 2623, 3625, 2182, 3433, 3619, + 2737, 4270, 2205, 3524, 3995, 905, 1984, 3993, 1858, 3753, + 3982, 4032, 3034, 3161, 4128, 4034, 4033, 954, 2984, 1989, + 2972, 4044, 2077, 2320, 1793, 2969, 3539, 2964, 2183, 4029, + 3943, 909, 2890, 2889, 1170, 1268, 77, 1971, 1895, 3439, + 955, 1967, 2733, 959, 2780, 77, 2628, 3193, 1710, 237, + 817, 1214, 3253, 2588, 2567, 3172, 3512, 3506, 3136, 2732, + 3046, 3654, 1952, 2450, 3049, 2469, 3560, 2458, 2457, 44, + 3018, 3595, 1962, 2983, 2961, 4, 3594, 2967, 889, 3670, + 259, 7, 258, 6, 778, 1791, 2946, 2200, 1326, 2123, + 1805, 2389, 2119, 2734, 38, 1992, 963, 817, 2974, 16, + 2971, 1920, 1874, 799, 1837, 2432, 1843, 1500, 2193, 2416, + 2937, 2940, 953, 263, 2110, 263, 2892, 1316, 1317, 1442, + 2645, 1425, 834, 1244, 2776, 817, 2161, 32, 2076, 1927, + 2380, 902, 1810, 17, 2118, 1269, 1246, 1447, 821, 2388, + 1501, 1213, 960, 15, 973, 2384, 1479, 1839, 1794, 1736, + 955, 26, 1842, 1131, 2206, 1798, 833, 912, 1134, 1135, + 2410, 5233, 911, 249, 818, 1295, 244, 1206, 3137, 86, + 54, 243, 1475, 1133, 1193, 236, 42, 836, 1136, 29, + 1211, 829, 1247, 87, 1792, 908, 831, 27, 1646, 874, + 78, 2712, 153, 4976, 2461, 961, 81, 4833, 5, 28, + 80, 76, 19, 1313, 1128, 10, 2348, 1264, 1130, 1569, + 4216, 1570, 1568, 1569, 832, 1570, 1568, 3393, 3393, 1569, + 828, 1570, 1568, 1761, 3393, 4373, 3953, 3952, 3818, 3817, + 830, 2749, 1737, 4560, 4335, 1411, 4195, 3286, 777, 3196, + 3199, 3197, 1312, 3194, 1314, 4069, 1410, 1738, 2337, 1930, + 1308, 1309, 247, 809, 3370, 1934, 4754, 4078, 3338, 2853, + 4376, 2962, 3221, 802, 2888, 3212, 1731, 1841, 807, 841, + 803, 1457, 1458, 1459, 804, 5073, 1688, 1456, 805, 806, + 1410, 1993, 2701, 77, 2702, 4221, 2697, 1899, 1266, 1349, + 1820, 1765, 4351, 3167, 2201, 4544, 3954, 3950, 77, 1309, + 77, 1157, 2903, 2670, 2895, 2344, 2437, 2441, 2442, 2443, + 2438, 2448, 2439, 2445, 1128, 1309, 2440, 1740, 2446, 1154, + 3929, 3926, 5244, 1307, 2671, 1517, 9, 1596, 7, 1602, + 6, 1455, 2331, 2884, 3936, 3934, 1719, 4497, 3798, 3796, + 1569, 3927, 1570, 1568, 1127, 1599, 1601, 1595, 2633, 1600, + 1582, 1581, 1591, 1592, 1593, 1594, 1584, 1585, 1586, 1587, + 1588, 1589, 1590, 1583, 5003, 4719, 1597, 1932, 5152, 5151, + 4209, 1569, 4945, 1570, 1568, 4770, 4763, 4500, 1349, 3938, + 4201, 1073, 2800, 1640, 5101, 2966, 1132, 2328, 2437, 2441, + 2442, 2443, 2438, 2448, 2439, 2445, 1259, 3138, 2440, 3889, + 2446, 3924, 3593, 3116, 3980, 2652, 2690, 3081, 5281, 2241, + 1257, 4959, 5240, 4779, 2051, 1258, 1261, 4956, 1145, 4549, + 4819, 2184, 2185, 4307, 3318, 2910, 1367, 1368, 1329, 5057, + 1896, 1746, 1448, 4547, 1744, 2437, 2441, 2442, 2443, 2438, + 2448, 2439, 2445, 4776, 1743, 2440, 4302, 2446, 3983, 1357, + 1361, 1363, 1365, 1370, 1127, 1375, 1371, 1372, 1373, 1374, + 2530, 2927, 1352, 1353, 1354, 1355, 1327, 1328, 1358, 1158, + 1330, 1155, 1331, 1333, 1334, 1335, 1336, 1332, 1337, 1338, + 1339, 1340, 1341, 1348, 1350, 1342, 1343, 1344, 1345, 1346, + 1347, 1376, 1377, 1378, 1379, 1380, 1381, 1382, 1383, 1385, + 1384, 1386, 1387, 1388, 1389, 1390, 1391, 1392, 1393, 1360, + 1362, 1364, 1366, 1369, 1998, 1367, 1368, 1329, 3884, 1271, + 1787, 1318, 2437, 2441, 2442, 2443, 2438, 2448, 2439, 2445, + 1146, 1158, 2440, 1453, 2446, 1454, 1349, 2429, 1357, 1361, + 1363, 1365, 1370, 3928, 1375, 1371, 1372, 1373, 1374, 1155, + 1351, 1352, 1353, 1354, 1355, 1327, 1328, 1358, 2747, 1330, + 3760, 1331, 1333, 1334, 1335, 1336, 1332, 1337, 1338, 1339, + 1340, 1341, 1348, 1350, 1342, 1343, 1344, 1345, 1346, 1347, + 1376, 1377, 1378, 1379, 1380, 1381, 1382, 1383, 1385, 1384, + 1386, 1387, 1388, 1389, 1390, 1391, 1392, 1393, 1360, 1362, + 1364, 1366, 1369, 3925, 1947, 2069, 2437, 2441, 2442, 2443, + 2438, 2448, 2439, 2445, 2941, 907, 2440, 3939, 2446, 200, + 245, 199, 232, 201, 1118, 2357, 1117, 1119, 1120, 2355, + 1121, 1122, 3420, 4821, 1933, 1931, 3157, 1565, 3144, 1351, + 943, 3143, 3073, 945, 3145, 3802, 3772, 3773, 944, 2551, + 2552, 1545, 817, 1156, 1546, 2362, 2363, 3158, 1844, 1753, + 1846, 1513, 817, 1996, 1514, 3771, 2550, 817, 2584, 1413, + 1452, 1153, 3122, 1367, 1368, 1329, 3121, 943, 1487, 3960, + 945, 2048, 1548, 3214, 1995, 944, 826, 3265, 3264, 837, + 837, 1439, 898, 817, 817, 825, 1357, 1361, 1363, 1365, + 1370, 4098, 1375, 1371, 1372, 1373, 1374, 3418, 3508, 1352, + 1353, 1354, 1355, 1327, 1328, 1358, 4096, 1330, 3509, 1331, + 1333, 1334, 1335, 1336, 1332, 1337, 1338, 1339, 1340, 1341, + 1348, 1350, 1342, 1343, 1344, 1345, 1346, 1347, 1376, 1377, + 1378, 1379, 1380, 1381, 1382, 1383, 1385, 1384, 1386, 1387, + 1388, 1389, 1390, 1391, 1392, 1393, 1360, 1362, 1364, 1366, + 1369, 2067, 1498, 1499, 5105, 1516, 1496, 4778, 3507, 2347, + 1495, 1498, 1499, 4551, 1403, 1730, 1494, 3937, 2126, 2444, + 1152, 1558, 1404, 3005, 1464, 2442, 2443, 2092, 3417, 1491, + 3416, 1491, 1473, 1563, 1264, 4787, 1405, 1351, 1256, 1483, + 5158, 5159, 4975, 817, 1255, 1319, 4832, 1543, 1503, 1504, + 5104, 1506, 1507, 5086, 5103, 1509, 5102, 1596, 2860, 1602, + 5105, 5192, 1615, 5104, 5191, 4948, 4228, 3847, 5103, 5190, + 5186, 1827, 2046, 3392, 1411, 1599, 1601, 1595, 1608, 1600, + 1582, 1581, 1591, 1592, 1593, 1594, 1584, 1585, 1586, 1587, + 1588, 1589, 1590, 1583, 1411, 1411, 1597, 3804, 834, 834, + 834, 2444, 5117, 2672, 2748, 3413, 4548, 5102, 5189, 5089, + 1413, 5235, 5236, 1544, 4766, 1266, 4204, 3819, 823, 824, + 5086, 3816, 4204, 246, 1359, 2711, 3805, 2785, 3806, 1411, + 3243, 1309, 833, 833, 833, 1309, 1309, 1309, 1900, 1821, + 3115, 1766, 3118, 1414, 1309, 1309, 4639, 4623, 2444, 2068, + 3809, 2751, 1485, 3117, 2568, 2528, 4987, 829, 829, 829, + 2750, 2130, 831, 831, 831, 4951, 4952, 4953, 4954, 2358, + 4225, 4317, 2111, 2356, 1264, 2115, 3419, 2743, 1525, 3195, + 4324, 1428, 1431, 4937, 3754, 2130, 3414, 3221, 1830, 2558, + 832, 832, 832, 4009, 4589, 1547, 828, 828, 828, 2114, + 2106, 1416, 1418, 1421, 3843, 1515, 830, 830, 830, 1259, + 1935, 3124, 1738, 1359, 2582, 2583, 2935, 1649, 1433, 1406, + 5286, 4786, 3689, 1402, 5312, 5313, 5350, 1598, 1258, 4788, + 814, 2658, 5345, 4777, 3935, 2444, 1132, 3430, 1738, 1733, + 1735, 3515, 1739, 5157, 4451, 1476, 1480, 1480, 1480, 5119, + 809, 2447, 1432, 1481, 1482, 1266, 1437, 1438, 1757, 3841, + 4081, 3386, 1760, 1449, 1560, 807, 3278, 803, 1324, 1769, + 3079, 804, 1476, 1476, 5283, 805, 806, 4550, 229, 1149, + 1533, 1556, 1557, 4498, 4351, 3797, 3131, 3748, 2529, 3126, + 4007, 4998, 1561, 1562, 4545, 4999, 5002, 5001, 817, 817, + 817, 1522, 4829, 1214, 1518, 1519, 1502, 1538, 4586, 1505, + 1540, 1819, 4823, 4824, 3127, 3128, 817, 1772, 1826, 2444, + 2046, 1832, 4003, 4015, 4014, 817, 2129, 2128, 2116, 778, + 778, 3395, 1524, 2447, 3415, 3134, 1555, 1708, 1541, 778, + 778, 1770, 1705, 1869, 1869, 1709, 817, 798, 4004, 4005, + 2129, 2128, 2113, 1412, 1150, 4413, 4879, 1324, 2042, 1616, + 4409, 1712, 4319, 2685, 4006, 1508, 4111, 4112, 835, 1511, + 1512, 1359, 4110, 1915, 1745, 4100, 837, 1742, 800, 1550, + 2447, 4979, 1551, 4045, 1923, 4810, 4614, 4822, 4561, 4341, + 1871, 4102, 4189, 4103, 4105, 4104, 4028, 3511, 1498, 1499, + 263, 3037, 1610, 1611, 1612, 1613, 1614, 3036, 3039, 778, + 1553, 5015, 4866, 1498, 1499, 5005, 4849, 1465, 3561, 4282, + 4284, 5285, 2948, 4106, 1486, 1430, 1429, 1159, 839, 200, + 245, 199, 232, 201, 79, 1151, 4553, 4554, 4555, 2047, + 838, 1867, 1867, 4457, 3758, 2943, 3944, 200, 245, 199, + 232, 201, 1997, 4001, 4395, 4992, 1752, 1828, 5017, 1661, + 1662, 5046, 4095, 1534, 3643, 3038, 4402, 2447, 2922, 1876, + 946, 947, 948, 949, 950, 1474, 2112, 4816, 1892, 1893, + 4015, 5286, 4569, 4293, 1748, 1975, 2136, 2140, 2141, 1536, + 1980, 1134, 3973, 1831, 1771, 5099, 2444, 2137, 3114, 1863, + 1864, 1994, 1539, 1542, 4967, 238, 4968, 946, 947, 948, + 949, 950, 1716, 4296, 1420, 1324, 4115, 4415, 200, 245, + 199, 232, 201, 238, 1750, 2017, 4986, 1259, 1809, 1535, + 4414, 1741, 3033, 4715, 2020, 1549, 2138, 2759, 2761, 2762, + 2049, 1703, 5306, 1715, 1704, 1202, 1258, 1203, 1713, 3639, + 3640, 2447, 3643, 3556, 1650, 4116, 3092, 3037, 3091, 4515, + 1869, 3052, 1987, 3036, 3039, 1435, 1832, 1472, 2569, 3112, + 3113, 827, 3521, 4379, 3755, 1554, 2062, 4015, 2755, 2757, + 2758, 4295, 1854, 1148, 4010, 77, 5047, 1471, 1853, 2046, + 1523, 1470, 2091, 2050, 238, 1774, 3844, 5118, 5142, 1832, + 4838, 1552, 4867, 4969, 2100, 2100, 4850, 1832, 1491, 1832, + 1832, 1537, 4354, 2043, 1983, 1800, 1801, 2562, 1489, 1488, + 1869, 3038, 1869, 1413, 2054, 2057, 2058, 2061, 1942, 1985, + 1797, 1912, 3514, 1804, 1880, 4120, 5269, 1763, 1764, 1852, + 1953, 1767, 2074, 2075, 4590, 1775, 1777, 1778, 1779, 1780, + 4704, 1782, 1973, 4101, 1970, 1786, 4908, 1974, 1966, 2084, + 2085, 1848, 1850, 2559, 4249, 1879, 1964, 1965, 1886, 2095, + 1877, 1861, 1862, 1885, 2107, 943, 3036, 3130, 945, 1990, + 4710, 2097, 5285, 944, 809, 1261, 1894, 3690, 1423, 3691, + 3692, 3518, 3519, 246, 1988, 834, 1869, 3388, 834, 834, + 1924, 3082, 1799, 1910, 4825, 4495, 3517, 1762, 4111, 4112, + 5083, 246, 834, 1925, 1413, 2203, 4002, 1598, 4728, 4729, + 4730, 4734, 4732, 4733, 4735, 4736, 4737, 4738, 4731, 833, + 3051, 2254, 833, 833, 1747, 3053, 2272, 2274, 2273, 4114, + 1608, 1936, 2743, 2235, 2236, 4362, 2034, 2240, 2244, 2245, + 2246, 3173, 3174, 1776, 829, 2255, 907, 829, 829, 831, + 1840, 2260, 831, 831, 2261, 4279, 1773, 1781, 2262, 2947, + 2264, 2096, 2265, 2266, 2267, 4157, 2025, 2040, 4137, 2103, + 3274, 1603, 246, 2139, 3558, 2282, 2283, 832, 2923, 3054, + 832, 832, 5270, 828, 2187, 1530, 828, 828, 2003, 2070, + 3719, 2006, 2007, 830, 2029, 3149, 830, 830, 5141, 2002, + 2026, 4473, 2005, 3120, 2122, 2315, 3062, 4907, 3077, 1413, + 2024, 2893, 2740, 2557, 2271, 1605, 1604, 2078, 2545, 2080, + 2081, 2349, 1134, 1201, 2760, 2352, 1264, 1749, 1751, 1981, + 817, 817, 817, 1759, 2088, 4172, 2035, 2033, 899, 2367, + 2369, 1476, 2370, 2031, 2373, 2374, 2032, 2142, 2377, 2120, + 2023, 800, 2381, 4159, 2028, 1480, 2385, 2326, 1869, 2391, + 2392, 2083, 2394, 1832, 2238, 2756, 2022, 817, 1480, 2027, + 1869, 3055, 2021, 2073, 2089, 2079, 817, 2104, 2105, 3402, + 2318, 4962, 3020, 3022, 1214, 5097, 4024, 2427, 2064, 2065, + 1529, 200, 245, 3846, 1906, 2452, 2452, 1819, 2452, 1785, + 1784, 2462, 2463, 817, 817, 2316, 2527, 1266, 2321, 1783, + 2385, 2538, 2099, 2101, 1869, 2542, 2543, 801, 2121, 1234, + 1819, 2117, 778, 1422, 2325, 4706, 843, 842, 835, 4705, + 4310, 5267, 5268, 4107, 901, 2340, 778, 900, 1869, 4711, + 4712, 1419, 3553, 245, 3742, 2324, 835, 2327, 2777, 2752, + 2753, 200, 245, 1188, 2147, 2148, 2149, 2150, 2151, 2152, + 2153, 2154, 2155, 2156, 2157, 2158, 2479, 238, 4271, 2102, + 2163, 2534, 2532, 4718, 2179, 2180, 2533, 1215, 2600, 2602, + 200, 245, 817, 2385, 1869, 3687, 2609, 245, 817, 905, + 905, 2329, 2381, 1270, 79, 2616, 3241, 2618, 2619, 2620, + 2204, 4363, 1164, 2624, 812, 2239, 813, 1808, 1762, 3410, + 263, 2481, 79, 263, 263, 1264, 263, 835, 238, 2159, + 2160, 1217, 1218, 1219, 2914, 2177, 2178, 238, 2263, 2181, + 899, 4211, 2253, 1756, 3056, 2395, 2454, 1888, 2460, 4215, + 2365, 2536, 2393, 4025, 1950, 1969, 2409, 1951, 2268, 2269, + 3061, 2585, 2547, 2366, 3059, 1171, 238, 2125, 2916, 2915, + 2577, 2578, 238, 1754, 1755, 4286, 1168, 2590, 2591, 3710, + 3711, 1166, 1165, 2913, 2660, 3720, 3722, 3723, 3724, 3721, + 2678, 2561, 2345, 79, 2364, 3021, 2014, 2554, 1160, 2556, + 2319, 2408, 2339, 1162, 1259, 3104, 1266, 2390, 2418, 2605, + 2575, 2576, 2011, 2012, 2379, 2611, 1161, 4627, 2317, 2404, + 3436, 778, 2338, 1258, 2342, 1569, 2563, 1570, 1568, 2346, + 4936, 200, 245, 817, 1462, 2427, 901, 817, 1463, 900, + 2570, 200, 245, 5308, 2694, 5355, 2696, 2546, 3403, 2632, + 2700, 2420, 2635, 2636, 2739, 2638, 3437, 3438, 2705, 2706, + 2707, 4214, 2710, 2541, 1832, 246, 2621, 2586, 2656, 1795, + 3251, 1968, 1945, 1167, 1948, 1949, 2608, 200, 245, 4108, + 2673, 2411, 2417, 4179, 3881, 2424, 2411, 1954, 1955, 1956, + 1957, 1958, 1959, 1832, 5093, 2428, 2422, 2423, 2415, 2821, + 1869, 2736, 889, 2820, 2341, 77, 1281, 2419, 77, 77, + 2642, 77, 5354, 2540, 5344, 2535, 246, 238, 2016, 5342, + 1235, 3709, 1232, 4178, 1466, 246, 5071, 2015, 4055, 2375, + 2376, 1567, 2564, 2120, 4965, 2662, 2382, 2383, 2622, 1164, + 2553, 1263, 2555, 4967, 1567, 4968, 2688, 2396, 2397, 2398, + 2649, 1466, 1263, 238, 246, 1139, 1140, 1141, 1142, 2722, + 246, 4961, 3568, 2333, 2548, 946, 947, 948, 949, 950, + 2594, 1466, 2108, 1467, 2657, 2599, 2661, 2764, 2607, 2725, + 2612, 2613, 2614, 2046, 834, 2727, 1169, 2729, 2606, 1567, + 3139, 2704, 5329, 1567, 4068, 5348, 2629, 5325, 1567, 2665, + 1567, 2595, 4174, 1163, 5296, 1567, 2593, 1530, 1166, 1165, + 1986, 834, 4379, 200, 245, 2877, 2275, 1530, 833, 2278, + 2279, 2280, 2647, 2939, 2284, 2285, 2286, 2287, 2289, 2290, + 2291, 2292, 2293, 2294, 2295, 2296, 2298, 2299, 2300, 2301, + 2302, 5293, 4969, 829, 4344, 833, 200, 245, 831, 200, + 245, 199, 232, 201, 3528, 3534, 3535, 3536, 3529, 3533, + 3530, 3532, 3531, 1528, 1270, 2730, 2667, 5290, 4267, 899, + 829, 2682, 5288, 1264, 5278, 831, 832, 1569, 5326, 1570, + 1568, 4179, 828, 5326, 3590, 4227, 5263, 2681, 2831, 238, + 5297, 2834, 830, 2836, 2684, 246, 2680, 2664, 1225, 1223, + 2843, 1224, 2687, 832, 5224, 246, 5209, 1706, 3013, 828, + 200, 245, 2226, 2856, 5172, 3551, 1417, 3076, 1567, 830, + 3052, 3055, 2666, 3013, 1143, 238, 1527, 5294, 4179, 1228, + 2894, 2716, 2896, 3013, 2898, 2899, 3880, 2723, 2902, 3589, + 2724, 246, 2713, 2739, 5171, 2622, 817, 1832, 817, 1832, + 2794, 2109, 5170, 5291, 1266, 3273, 2938, 4140, 2745, 2917, + 5279, 3553, 4138, 2731, 4089, 901, 2924, 2925, 900, 954, + 2783, 2720, 5210, 817, 817, 2742, 1480, 5160, 2936, 5138, + 2739, 1569, 2726, 1570, 1568, 5137, 3544, 817, 817, 817, + 5225, 4086, 5210, 2859, 2861, 2862, 2863, 3989, 2865, 3986, + 5173, 2875, 1233, 3942, 2797, 200, 245, 199, 232, 201, + 2970, 2970, 2976, 3574, 3052, 3055, 3389, 1528, 3919, 2985, + 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 2763, 2876, + 1567, 3916, 2765, 1855, 3218, 2738, 3548, 2770, 1567, 3160, + 2793, 2787, 2452, 2778, 1706, 1569, 5068, 1570, 1568, 2549, + 2163, 2768, 2769, 3031, 1229, 2163, 2773, 2774, 1569, 5018, + 1570, 1568, 2887, 2795, 1569, 5139, 1570, 1568, 2874, 3068, + 2873, 2745, 2004, 200, 245, 3023, 2882, 246, 2830, 2829, + 5009, 238, 2828, 1569, 3056, 1570, 1568, 4923, 2686, 3051, + 3036, 3050, 2580, 3047, 3053, 2544, 1569, 3590, 1570, 1568, + 1803, 2190, 2766, 2767, 3920, 3040, 3273, 2771, 2772, 1530, + 246, 2868, 4922, 246, 2879, 2880, 4585, 3917, 4921, 2907, + 1231, 2909, 4920, 2719, 1270, 1303, 1304, 1305, 1264, 4919, + 4962, 4870, 2795, 4869, 4963, 817, 2100, 4835, 1569, 2222, + 1570, 1568, 3365, 2738, 4379, 2745, 2219, 3075, 3054, 238, + 2221, 2218, 2220, 2224, 2225, 2872, 778, 778, 2223, 5327, + 1302, 4804, 4927, 1299, 1413, 1569, 1567, 1570, 1568, 4748, + 1869, 817, 4323, 2795, 246, 4373, 4366, 3877, 3056, 3554, + 3788, 1134, 3592, 3051, 3036, 3050, 2973, 3047, 3053, 3573, + 3398, 3276, 2049, 4801, 3168, 3169, 4746, 2869, 2795, 2897, + 4646, 817, 1230, 2901, 2795, 3275, 800, 4466, 2795, 1266, + 3242, 2931, 2816, 1923, 2866, 2795, 3184, 2745, 3186, 2745, + 2954, 263, 2641, 2795, 2791, 2959, 3042, 3057, 3058, 3044, + 3043, 2928, 2418, 2418, 3048, 4645, 2799, 2986, 2955, 4571, + 4570, 1569, 3054, 1570, 1568, 3179, 1227, 1567, 1139, 1140, + 1141, 1142, 4468, 4416, 4389, 3012, 4386, 2970, 2970, 3227, + 3119, 3146, 3550, 3147, 2728, 2683, 3238, 1137, 3155, 246, + 2627, 1649, 2977, 2978, 3244, 1139, 1140, 1141, 1142, 2981, + 3011, 2343, 3152, 3153, 4360, 2579, 4647, 4260, 3249, 1999, + 4256, 3006, 3252, 3255, 1137, 4148, 3030, 3140, 3257, 1583, + 2867, 4050, 1869, 2987, 2988, 2989, 2990, 2991, 2992, 2993, + 2229, 2230, 2231, 2232, 2233, 2234, 2227, 2228, 3171, 3270, + 2371, 2981, 2736, 3868, 3814, 4089, 4086, 1624, 3048, 1869, + 1434, 1869, 1428, 1431, 3812, 1399, 1413, 246, 2046, 4417, + 4089, 4073, 4086, 3578, 3287, 1394, 2782, 2781, 3576, 3570, + 3189, 1296, 1297, 1298, 1301, 3838, 1300, 1226, 835, 2372, + 1891, 3363, 1221, 3350, 3342, 5006, 3288, 3248, 3246, 1222, + 1567, 3250, 2087, 4261, 3159, 1264, 4257, 3222, 4490, 2063, + 3194, 4149, 1940, 1939, 3260, 1869, 1413, 3571, 1462, 3135, + 3321, 3129, 1463, 3209, 3267, 3268, 3141, 1605, 1604, 5309, + 3259, 2082, 2630, 1432, 3216, 4193, 77, 3330, 3219, 1567, + 3577, 5007, 1869, 3322, 3207, 5298, 3271, 1143, 2090, 3205, + 3571, 2093, 2094, 3156, 79, 3203, 990, 1000, 5295, 3579, + 3014, 3316, 956, 2973, 3577, 3571, 991, 1477, 992, 996, + 999, 995, 993, 994, 1143, 3215, 1859, 1567, 2980, 1567, + 1567, 3366, 1567, 4519, 2981, 2878, 1266, 1860, 3331, 3176, + 3789, 834, 3299, 5292, 3183, 1582, 1581, 1591, 1592, 1593, + 1594, 1584, 1585, 1586, 1587, 1588, 1589, 1590, 1583, 3210, + 2839, 3258, 1603, 1867, 3223, 3555, 3396, 4628, 1848, 1850, + 5239, 3400, 2838, 2819, 3404, 833, 2810, 2823, 2809, 2808, + 3208, 817, 817, 817, 997, 3204, 2796, 3226, 3225, 3236, + 1867, 3204, 2744, 3345, 835, 2008, 3013, 3852, 3074, 3351, + 829, 1413, 3503, 3245, 3284, 831, 1605, 1604, 3302, 1869, + 3304, 4977, 1832, 4629, 2981, 998, 4046, 4357, 1832, 2538, + 3279, 2877, 4896, 3336, 4895, 1603, 3340, 3372, 2970, 3374, + 3375, 3319, 3339, 832, 3368, 1857, 4355, 4834, 3346, 828, + 1478, 3382, 1510, 3297, 1416, 3272, 1567, 4851, 2120, 830, + 4774, 3277, 3585, 3588, 3269, 4708, 1430, 1429, 1567, 1567, + 79, 4518, 1567, 4358, 1567, 1567, 4707, 4691, 3383, 1605, + 1604, 4656, 2795, 4593, 2243, 2242, 3566, 4367, 2745, 4334, + 4180, 2009, 4356, 1462, 1819, 3829, 1819, 1463, 1650, 2243, + 2242, 4170, 4162, 3581, 3582, 3623, 3629, 4150, 4091, 4047, + 4088, 4019, 3306, 3317, 3644, 1413, 3384, 3385, 3646, 3647, + 3648, 3289, 3290, 1413, 3751, 2059, 3292, 3293, 3750, 3580, + 3575, 3658, 1569, 3540, 1570, 1568, 3526, 3394, 1413, 1413, + 1413, 2100, 3285, 4196, 1413, 3673, 3962, 3674, 3675, 4852, + 3676, 1856, 3678, 3151, 3381, 4048, 2900, 1462, 2676, 3543, + 5, 1463, 2675, 3673, 1584, 1585, 1586, 1587, 1588, 1589, + 1590, 1583, 2674, 2479, 1789, 3188, 2452, 1788, 3325, 1415, + 2194, 3965, 2788, 1310, 1311, 1928, 3428, 2630, 1315, 3443, + 3732, 3314, 3315, 5135, 3522, 4853, 3332, 1796, 3308, 2412, + 3541, 3295, 2288, 3296, 5134, 2055, 5011, 778, 2194, 1796, + 2409, 1569, 3562, 1570, 1568, 2538, 1795, 2281, 2481, 1413, + 1413, 4722, 3198, 3965, 2174, 1375, 1371, 1372, 1373, 1374, + 1869, 5188, 3313, 4860, 3312, 3311, 3309, 1570, 1568, 3769, + 2171, 2173, 2170, 4803, 2172, 77, 1462, 4802, 2060, 3552, + 1463, 817, 3307, 3504, 3505, 1568, 1413, 3737, 2390, 3785, + 3520, 3962, 4721, 1819, 4051, 3564, 3549, 3679, 3677, 3661, + 3282, 263, 3659, 5346, 3565, 5337, 263, 1586, 1587, 1588, + 1589, 1590, 1583, 3583, 3651, 3584, 3587, 2169, 3632, 5316, + 7, 4697, 6, 3600, 3601, 3598, 3599, 5037, 5038, 3663, + 1796, 3964, 1923, 2166, 2168, 2165, 3810, 2167, 5035, 5036, + 4696, 3855, 3610, 3611, 3230, 3615, 3616, 3617, 3612, 1569, + 3310, 1570, 1568, 3821, 3229, 3631, 1462, 3634, 2056, 5177, + 1463, 5212, 3255, 4900, 4901, 5136, 3653, 3770, 5130, 3743, + 3656, 4587, 3655, 3655, 3660, 4594, 4595, 3655, 1569, 3666, + 1570, 1568, 5041, 3377, 1869, 3378, 4376, 3840, 1569, 3080, + 1570, 1568, 3083, 3084, 3085, 3086, 3087, 3088, 3089, 3090, + 5305, 3756, 3093, 3094, 3095, 3096, 3097, 3098, 3099, 3100, + 3101, 3102, 3103, 3811, 3105, 3106, 3107, 3108, 3109, 3706, + 3110, 1569, 3693, 1570, 1568, 3787, 3778, 3162, 3163, 1569, + 3790, 1570, 1568, 4588, 1569, 3794, 1570, 1568, 4194, 5040, + 3824, 3746, 1928, 3827, 3828, 5039, 3752, 1581, 1591, 1592, + 1593, 1594, 1584, 1585, 1586, 1587, 1588, 1589, 1590, 1583, + 3834, 3329, 4321, 1626, 3443, 2258, 3777, 5304, 1569, 3774, + 1570, 1568, 3831, 3731, 3779, 3784, 1625, 3301, 3913, 5034, + 2259, 1569, 3807, 1570, 1568, 3444, 77, 3783, 5033, 2812, + 3190, 77, 2695, 3729, 5032, 2698, 2699, 5031, 3791, 2703, + 3792, 1569, 5030, 1570, 1568, 1569, 3948, 1570, 1568, 3951, + 2958, 3727, 2714, 2715, 3955, 5029, 817, 1832, 5027, 3932, + 5026, 3873, 2717, 2718, 4322, 3967, 3969, 3970, 3972, 5025, + 3974, 3975, 3885, 3657, 3887, 3730, 3890, 3891, 2803, 5024, + 3842, 3892, 3893, 3894, 3895, 3896, 1413, 3897, 3898, 3899, + 3900, 3901, 3902, 3903, 3904, 3728, 3906, 3907, 3908, 3909, + 1569, 2811, 1570, 1568, 3826, 1569, 3837, 1570, 1568, 3749, + 5023, 3716, 5022, 3726, 1929, 5020, 3836, 3870, 5019, 3914, + 3849, 5008, 3918, 2792, 3921, 3922, 3422, 4911, 1569, 3645, + 1570, 1568, 4873, 4854, 3867, 3850, 4828, 3652, 3851, 1591, + 1592, 1593, 1594, 1584, 1585, 1586, 1587, 1588, 1589, 1590, + 1583, 4800, 3652, 3664, 3665, 4764, 4717, 5147, 3669, 3863, + 3864, 3997, 3999, 1574, 1575, 1576, 1577, 1578, 1579, 1580, + 1572, 4631, 4012, 3715, 3860, 4630, 3862, 1569, 817, 1570, + 1568, 4396, 3869, 4359, 1569, 3861, 1570, 1568, 3930, 5054, + 4320, 1991, 3941, 4027, 4303, 1413, 4008, 4327, 3822, 817, + 3801, 817, 2538, 1569, 3525, 1570, 1568, 1413, 4795, 1569, + 1413, 1570, 1568, 3800, 3740, 4533, 1569, 3714, 1570, 1568, + 4531, 3713, 2970, 3945, 4059, 2970, 3946, 3712, 4062, 3958, + 4063, 3931, 4522, 3768, 3768, 1569, 4049, 1570, 1568, 3704, + 1819, 2985, 1569, 4072, 1570, 1568, 3698, 1569, 3697, 1570, + 1568, 4521, 3696, 3695, 3694, 4520, 4035, 3390, 3247, 1569, + 3211, 1570, 1568, 3148, 2886, 2691, 4023, 4461, 2651, 2650, + 3444, 3623, 4245, 3990, 2648, 2644, 2643, 3629, 1569, 4065, + 1570, 1568, 1569, 4016, 1570, 1568, 2597, 4026, 3068, 2354, + 4235, 2351, 2000, 4874, 1569, 3540, 1570, 1568, 1729, 1569, + 4333, 1570, 1568, 3994, 5287, 4126, 3233, 4126, 4130, 3235, + 4130, 4130, 5280, 1264, 3234, 1413, 1953, 1569, 1397, 1570, + 1568, 1973, 4000, 1970, 4826, 4827, 1974, 4505, 4141, 4117, + 2790, 5237, 3543, 1966, 5196, 5114, 1869, 1869, 5112, 3923, + 2321, 1964, 1965, 4808, 3882, 4058, 4061, 1582, 1581, 1591, + 1592, 1593, 1594, 1584, 1585, 1586, 1587, 1588, 1589, 1590, + 1583, 5081, 4989, 4097, 4099, 4022, 1569, 4598, 1570, 1568, + 4983, 1569, 4070, 1570, 1568, 3875, 4974, 4972, 3510, 1396, + 1073, 4955, 4142, 4143, 817, 4946, 4041, 1817, 5331, 4043, + 2409, 4910, 4040, 4054, 1266, 4060, 4052, 4057, 3997, 3874, + 2973, 4909, 1569, 3871, 1570, 1568, 4067, 4888, 3015, 4071, + 1569, 1832, 1570, 1568, 2538, 2538, 3552, 4887, 4878, 4877, + 4087, 4831, 4815, 4090, 4813, 4799, 1569, 3364, 1570, 1568, + 1569, 4765, 1570, 1568, 1867, 1867, 3362, 4701, 4700, 3321, + 4056, 4125, 3361, 4127, 4699, 4135, 4602, 4197, 3042, 4591, + 1571, 3044, 3043, 4092, 1569, 3359, 1570, 1568, 1607, 3358, + 1851, 4576, 4575, 1569, 5274, 1570, 1568, 4573, 1618, 1569, + 4566, 1570, 1568, 815, 4131, 4132, 4136, 3357, 4564, 4543, + 4542, 4124, 1569, 4139, 1570, 1568, 1569, 4541, 1570, 1568, + 4537, 4536, 4509, 3356, 1628, 4507, 4503, 817, 5272, 4501, + 4479, 4147, 3355, 4093, 1569, 4476, 1570, 1568, 4155, 3354, + 4470, 3736, 4315, 3353, 5049, 4305, 4288, 4233, 3685, 3686, + 1569, 3352, 1570, 1568, 4272, 4243, 4805, 4240, 4236, 1569, + 3987, 1570, 1568, 4210, 3702, 3703, 1569, 4168, 1570, 1568, + 1569, 4167, 1570, 1568, 3349, 4165, 4164, 4151, 1569, 4152, + 1570, 1568, 4161, 4146, 4160, 4175, 4176, 4218, 4163, 4145, + 4020, 4169, 4166, 3348, 3984, 3976, 3966, 3747, 3347, 3956, + 4207, 1569, 3949, 1570, 1568, 3947, 2891, 3848, 3845, 3341, + 3820, 3799, 3776, 4173, 3326, 3744, 4213, 1210, 3320, 5349, + 1569, 3741, 1570, 1568, 3738, 1569, 3725, 1570, 1568, 3717, + 3707, 4274, 3705, 3701, 3700, 4275, 1569, 2624, 1570, 1568, + 3699, 1569, 3604, 1570, 1568, 1569, 3298, 1570, 1568, 4289, + 2871, 4291, 3429, 3411, 3399, 4794, 4297, 3391, 3623, 3629, + 1717, 1073, 1072, 4793, 1306, 3228, 3217, 3177, 4224, 3652, + 1413, 2870, 2918, 1569, 2905, 1570, 1568, 1569, 2864, 1570, + 1568, 3652, 4285, 4229, 3652, 4250, 2904, 2754, 4252, 2655, + 2654, 4298, 815, 2646, 2387, 817, 2538, 4223, 1569, 4244, + 1570, 1568, 2353, 2350, 2336, 1569, 2833, 1570, 1568, 4342, + 2832, 4781, 4212, 2335, 4292, 4133, 4294, 2098, 2001, 1657, + 4348, 1653, 1652, 4264, 3261, 2189, 1401, 1147, 2243, 2242, + 3266, 4313, 4314, 1569, 4280, 1570, 1568, 1569, 4775, 1570, + 1568, 200, 245, 3858, 3859, 4574, 5262, 4540, 2452, 1819, + 4370, 4526, 1569, 5260, 1570, 1568, 4516, 2427, 4471, 245, + 199, 232, 201, 4381, 4364, 4352, 4350, 3623, 4265, 3255, + 3629, 5261, 4391, 4331, 4343, 4269, 4345, 4273, 5259, 5347, + 4155, 4268, 4277, 4378, 2297, 4266, 4263, 4262, 4259, 3652, + 4258, 245, 199, 232, 201, 4246, 4242, 4239, 4199, 4198, + 4188, 4418, 202, 4182, 1413, 4300, 4304, 4299, 4079, 4066, + 3988, 4340, 4328, 4311, 4126, 4308, 1922, 238, 1413, 1413, + 3985, 4465, 3940, 3878, 3865, 1869, 3857, 3856, 3854, 906, + 3618, 3206, 3202, 3201, 238, 5338, 3200, 4330, 3165, 3164, + 4474, 2844, 4349, 2837, 2956, 2827, 4309, 2826, 3443, 4372, + 2825, 2824, 2822, 817, 2818, 2538, 2817, 2815, 4365, 1819, + 1413, 2806, 4405, 4406, 4407, 4408, 238, 245, 4377, 2957, + 4491, 4467, 2802, 2801, 2721, 263, 2653, 2308, 2306, 2305, + 2304, 4369, 1949, 2303, 2257, 2256, 2247, 4452, 4368, 1890, + 5303, 3552, 3552, 1954, 1955, 1956, 1957, 1958, 1959, 5301, + 3650, 5273, 4511, 5202, 4388, 5201, 5200, 4487, 4387, 4385, + 4393, 4445, 5176, 5169, 5167, 5074, 5053, 1647, 5048, 4940, + 4939, 4410, 4938, 1867, 4918, 4912, 4886, 4742, 4480, 4720, + 4714, 4655, 4632, 4459, 4458, 4455, 3552, 4454, 4453, 4403, + 4400, 4398, 238, 4336, 4287, 4464, 4283, 4241, 4462, 4082, + 4460, 4076, 1969, 1972, 4469, 1944, 1963, 1960, 1946, 1941, + 4477, 3981, 3978, 3977, 2427, 4475, 3905, 4556, 3866, 4478, + 4481, 4562, 1134, 3739, 3733, 4485, 4472, 4568, 3662, 4483, + 3569, 4486, 4514, 3563, 3559, 4482, 1806, 3387, 3175, 2066, + 4558, 4484, 3150, 3111, 2998, 2979, 2930, 2929, 2885, 2775, + 2164, 238, 2668, 4506, 4496, 2421, 2332, 4525, 2044, 1961, + 4527, 1728, 1707, 1702, 4508, 2086, 1701, 1413, 1869, 1869, + 1700, 1699, 4603, 4534, 1698, 1413, 1697, 1696, 1695, 1694, + 4530, 4042, 4532, 1693, 1692, 4524, 4565, 1691, 4567, 1690, + 1689, 1688, 1687, 1413, 1686, 1685, 1684, 4634, 4523, 4528, + 1683, 1682, 1681, 1680, 1679, 1678, 1677, 4644, 4538, 1676, + 77, 1675, 4633, 1674, 4600, 4546, 1673, 1672, 1671, 4397, + 1670, 4399, 1669, 1668, 1667, 4552, 1666, 1665, 1664, 1413, + 1663, 1413, 4599, 4605, 1660, 4609, 4559, 1659, 1658, 4649, + 2132, 2133, 2134, 2135, 4609, 1656, 1655, 4638, 1654, 4652, + 4579, 4654, 1651, 4582, 4601, 1644, 1643, 1641, 1640, 1002, + 172, 1639, 1638, 1869, 3444, 1637, 1867, 2187, 4592, 172, + 1636, 1635, 1634, 1633, 4615, 1632, 1631, 1630, 4611, 1629, + 4625, 4612, 817, 4617, 1413, 1413, 1413, 4624, 2191, 1413, + 1413, 4622, 2195, 2196, 2197, 2198, 2673, 4372, 4640, 4606, + 1607, 4607, 1623, 2237, 4747, 1622, 1621, 1620, 1619, 1532, + 1493, 1398, 2248, 5067, 4648, 3596, 3597, 5065, 5063, 4578, + 5060, 4690, 4744, 4456, 2945, 2709, 1440, 5315, 4760, 4761, + 4739, 5216, 5214, 5156, 4452, 4695, 4158, 810, 3786, 4692, + 2276, 2277, 3745, 3527, 3170, 1869, 172, 4756, 4702, 4724, + 4725, 4726, 2962, 1531, 4740, 4741, 1460, 3010, 4445, 3608, + 3003, 2187, 3001, 4080, 3609, 3004, 4750, 3002, 2307, 1400, + 2309, 2310, 2311, 2312, 2313, 4796, 4797, 82, 817, 1210, + 2322, 1491, 4789, 3607, 1408, 2996, 3606, 3008, 4749, 3009, + 2999, 4773, 4809, 3552, 4811, 3000, 2995, 4084, 4419, 3605, + 3602, 3007, 2994, 5314, 5094, 4958, 3823, 4085, 3825, 4957, + 1443, 1445, 4463, 3669, 4767, 4931, 4488, 4772, 3830, 4752, + 5343, 5339, 4768, 4694, 4361, 3832, 3833, 3813, 1129, 4812, + 3572, 4814, 1790, 2406, 2407, 2401, 2402, 2403, 4780, 3025, + 4018, 4782, 4347, 1867, 4122, 4844, 4123, 4184, 4186, 4187, + 4790, 4535, 4791, 3915, 3768, 808, 3781, 3078, 4083, 4411, + 3027, 3026, 4817, 4312, 4792, 3567, 4185, 4489, 2519, 2414, + 1923, 3681, 1937, 4644, 4644, 1137, 1986, 1795, 4863, 3682, + 3683, 3684, 3162, 3163, 3815, 4855, 1413, 3283, 2912, 2911, + 1982, 1220, 1986, 2920, 2919, 2615, 4839, 4836, 1265, 4830, + 4842, 2531, 1526, 4818, 5072, 4915, 4872, 172, 4840, 4572, + 4036, 3992, 3991, 4857, 3633, 3231, 4843, 3029, 2953, 2425, + 1566, 2378, 172, 5229, 172, 4845, 4657, 4847, 4514, 2243, + 2242, 2322, 4689, 1726, 1727, 5203, 2322, 2322, 1129, 1724, + 1725, 1722, 1723, 4861, 4862, 1720, 1721, 4914, 4144, 4698, + 4868, 4858, 3132, 3125, 3024, 2539, 1521, 1520, 4613, 1559, + 2677, 2176, 2589, 1139, 1140, 1141, 1142, 3614, 1263, 1492, + 1137, 1492, 2921, 1413, 2679, 2053, 1869, 1469, 1468, 4928, + 1451, 1137, 1137, 4584, 3281, 4934, 2693, 5183, 4856, 5181, + 5120, 5091, 4583, 3280, 5090, 5088, 4993, 4941, 4650, 4529, + 4502, 2631, 4494, 4384, 2634, 4383, 4206, 2637, 4205, 1711, + 2639, 3063, 3032, 1889, 4493, 4913, 4191, 3886, 3360, 3652, + 1466, 4563, 4925, 5218, 5217, 5217, 4929, 4930, 4290, 4653, + 5132, 5133, 5187, 4973, 4276, 4255, 3835, 3768, 4924, 3406, + 4966, 4978, 3405, 3397, 3182, 3178, 4337, 4338, 4339, 2804, + 4985, 2131, 1436, 1407, 5218, 4716, 3552, 5205, 5204, 5129, + 5127, 5125, 4942, 1139, 1140, 1141, 1142, 3426, 1263, 3424, + 4581, 1263, 2881, 2125, 1867, 2125, 4960, 1977, 4980, 2175, + 4981, 4254, 4964, 1582, 1581, 1591, 1592, 1593, 1594, 1584, + 1585, 1586, 1587, 1588, 1589, 1590, 1583, 1139, 1140, 1141, + 1142, 811, 1484, 91, 2, 5242, 5243, 1, 3933, 4982, + 5021, 2883, 2330, 1718, 1138, 1845, 3142, 1413, 2581, 1873, + 4970, 4971, 4988, 2334, 1144, 3016, 3017, 3613, 3019, 2598, + 4994, 1446, 3412, 4995, 2741, 5004, 3757, 5028, 4997, 5010, + 3123, 2934, 4011, 1807, 4990, 1216, 5013, 5014, 2249, 1413, + 2013, 1427, 5043, 2010, 1491, 1426, 1424, 1869, 5070, 2192, + 2270, 1004, 2965, 3734, 3708, 4380, 5228, 5275, 5175, 5231, + 2659, 5078, 2039, 988, 5082, 1210, 1210, 1802, 4208, 3808, + 4947, 5179, 4949, 4771, 2746, 5042, 1564, 4053, 869, 1050, + 1016, 5075, 5076, 1824, 5051, 1642, 2663, 3888, 5079, 3883, + 1015, 4325, 1838, 5069, 3516, 4935, 4848, 870, 2640, 4944, + 5113, 4769, 1938, 1943, 5058, 3028, 5100, 4865, 5044, 4693, + 4118, 3642, 5080, 1875, 1976, 5016, 4401, 1869, 5059, 5061, + 5062, 5064, 5066, 5087, 5085, 4785, 4783, 4784, 3912, 4932, + 5110, 4753, 5106, 5108, 913, 2560, 776, 4743, 5115, 2960, + 5111, 2708, 5107, 5109, 4658, 1867, 4917, 1186, 4306, 2944, + 1187, 1179, 3538, 2784, 3537, 2143, 1573, 2789, 2162, 4247, + 5123, 5121, 4248, 5122, 1617, 2798, 958, 5140, 2786, 3513, + 4439, 3775, 90, 1869, 89, 88, 5131, 271, 1007, 270, + 3552, 4806, 4596, 5077, 985, 984, 983, 982, 981, 980, + 2435, 2434, 4934, 2436, 2433, 2431, 2430, 1814, 1813, 4190, + 4492, 3180, 3181, 3672, 2807, 4412, 3667, 2470, 2467, 1836, + 2468, 3035, 2814, 3065, 4177, 1867, 2466, 4181, 5153, 5148, + 2125, 4230, 4517, 5161, 5055, 5165, 5162, 5056, 5163, 4713, + 5164, 3718, 5166, 4513, 2400, 5182, 3060, 5184, 5185, 2487, + 3688, 1413, 2835, 5180, 5178, 5174, 2484, 2840, 2841, 2842, + 2483, 3680, 2845, 2846, 2847, 2848, 2849, 2850, 2851, 2852, + 4709, 2854, 2855, 4703, 2857, 2858, 5193, 2516, 5194, 5195, + 5000, 1867, 4217, 5252, 5126, 4219, 4220, 5199, 5255, 5128, + 4621, 4620, 4222, 5247, 5124, 4619, 4390, 3628, 3913, 4618, + 4608, 4420, 5207, 5206, 5227, 5208, 4421, 2254, 5211, 4428, + 1356, 2952, 1325, 5213, 5234, 5215, 1320, 1322, 1323, 1321, + 3305, 4171, 3041, 5226, 4030, 5223, 3435, 3434, 3432, 1413, + 3431, 5250, 5250, 5250, 1413, 5282, 1768, 5254, 5257, 4984, + 5238, 5116, 4577, 5100, 5241, 3442, 3440, 1450, 2045, 5245, + 2041, 5246, 1441, 5258, 1395, 2975, 5043, 3959, 5264, 5265, + 1734, 1732, 5271, 5277, 3603, 2997, 2587, 3780, 1815, 1812, + 5219, 5220, 5221, 5222, 4077, 1811, 1294, 1293, 2592, 1267, + 239, 1884, 172, 172, 172, 1265, 240, 171, 1126, 4745, + 5300, 5289, 5300, 4281, 810, 4346, 3759, 3133, 3232, 3622, + 4820, 2405, 1180, 2942, 5302, 126, 48, 144, 125, 1202, + 2254, 1203, 218, 69, 217, 68, 20, 5234, 142, 216, + 67, 5310, 5250, 5319, 53, 5250, 5319, 5307, 5250, 5319, + 5254, 4651, 5322, 5257, 5321, 5324, 5323, 5317, 52, 214, + 121, 120, 5277, 5332, 5335, 119, 5336, 118, 141, 213, + 66, 1184, 255, 254, 5328, 257, 5330, 5340, 5341, 256, + 253, 2125, 3191, 3192, 252, 1197, 1926, 1192, 251, 5092, + 4616, 5168, 51, 50, 219, 49, 127, 70, 47, 5352, + 46, 2692, 3979, 2052, 4301, 1582, 1581, 1591, 1592, 1593, + 1594, 1584, 1585, 1586, 1587, 1588, 1589, 1590, 1583, 3427, + 5351, 2926, 5353, 1822, 1823, 45, 1825, 40, 14, 1829, + 12, 1833, 1834, 1835, 43, 41, 25, 24, 2018, 23, + 31, 30, 13, 39, 37, 1606, 36, 2322, 4371, 2322, + 163, 162, 35, 161, 160, 159, 1173, 158, 157, 156, + 155, 34, 22, 61, 60, 59, 4846, 58, 2322, 2322, + 57, 56, 1897, 1898, 8, 4374, 1901, 1902, 1903, 1904, + 1905, 148, 1907, 1908, 1909, 147, 1911, 149, 1914, 145, + 1916, 1917, 1918, 140, 138, 33, 139, 136, 137, 130, + 129, 128, 4875, 4876, 122, 102, 101, 100, 115, 4880, + 4881, 4882, 4883, 4884, 4885, 1922, 114, 113, 4889, 4890, + 4891, 4892, 4893, 4894, 112, 111, 110, 2517, 108, 4897, + 4898, 4899, 2477, 109, 868, 2525, 99, 2359, 2360, 2361, + 98, 97, 96, 95, 135, 134, 1199, 131, 1191, 117, + 124, 123, 106, 116, 107, 105, 104, 1196, 1194, 103, + 94, 93, 92, 133, 132, 2519, 2486, 3237, 146, 3240, + 220, 71, 197, 196, 2399, 195, 2520, 2521, 1185, 194, + 193, 191, 192, 2413, 190, 189, 188, 187, 186, 4926, + 185, 62, 63, 64, 65, 209, 4510, 208, 3879, 210, + 222, 221, 2485, 212, 215, 211, 206, 204, 1129, 207, + 2464, 2465, 3262, 3263, 205, 5197, 1714, 1201, 203, 83, + 11, 2494, 1190, 143, 21, 18, 1189, 3, 0, 0, + 0, 0, 1178, 0, 0, 0, 0, 0, 0, 0, + 2517, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 1183, 1582, 1581, 1591, 1592, 1593, 1594, 1584, 1585, + 1586, 1587, 1588, 1589, 1590, 1583, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 2519, 0, + 0, 3300, 0, 0, 3303, 0, 4996, 0, 0, 1445, + 0, 0, 0, 2125, 0, 1445, 3323, 3324, 3768, 1195, + 1818, 0, 2510, 1181, 0, 3327, 3328, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 3872, 0, 0, + 0, 3333, 3334, 3335, 4904, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 2494, 0, 0, 0, 1878, 0, + 0, 1887, 1200, 0, 0, 0, 4636, 4637, 810, 0, + 0, 0, 0, 0, 0, 0, 0, 5052, 0, 0, + 3371, 0, 3373, 0, 0, 3376, 0, 2132, 0, 1182, + 2322, 1582, 1581, 1591, 1592, 1593, 1594, 1584, 1585, 1586, + 1587, 1588, 1589, 1590, 1583, 0, 0, 0, 0, 172, + 2472, 2478, 2476, 0, 0, 0, 2473, 0, 0, 0, + 0, 2498, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 2504, 0, 0, 2510, 0, 0, 0, 0, + 2488, 0, 2471, 0, 0, 0, 0, 0, 0, 0, + 1443, 0, 2492, 2526, 2689, 0, 2493, 2495, 2497, 0, + 2499, 2500, 2501, 2505, 2506, 2507, 2509, 2512, 2513, 2514, + 3294, 1198, 0, 0, 0, 0, 0, 2502, 2511, 2503, + 0, 0, 4751, 0, 0, 0, 0, 0, 0, 2480, + 0, 0, 0, 0, 1582, 1581, 1591, 1592, 1593, 1594, + 1584, 1585, 1586, 1587, 1588, 1589, 1590, 1583, 0, 0, + 0, 0, 0, 2517, 0, 1177, 0, 0, 2477, 172, + 0, 2525, 172, 172, 0, 0, 0, 0, 0, 0, + 0, 2518, 0, 0, 2498, 0, 172, 0, 0, 0, 0, 0, 0, 0, 0, 2504, 0, 0, 0, 0, - 0, 0, 2026, 0, 2492, 0, 0, 0, 3872, 225, - 0, 0, 0, 0, 0, 0, 0, 0, 2519, 2518, - 4837, 0, 0, 0, 2506, 0, 0, 0, 0, 2470, - 2471, 0, 200, 245, 199, 232, 201, 0, 0, 0, - 0, 0, 0, 0, 3906, 0, 0, 2511, 0, 172, - 0, 0, 233, 0, 0, 0, 172, 0, 238, 225, - 0, 0, 1069, 234, 2015, 2486, 0, 0, 0, 2485, - 0, 0, 0, 0, 2451, 2478, 202, 0, 0, 0, - 0, 0, 0, 169, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 2504, 0, 0, 0, 1814, 154, 0, - 0, 0, 2492, 0, 0, 0, 0, 0, 238, 0, - 0, 0, 0, 1355, 0, 0, 2519, 2518, 2520, 2513, - 0, 0, 0, 2494, 0, 0, 0, 200, 245, 0, - 0, 0, 0, 0, 2500, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 2355, - 2356, 2357, 2592, 0, 2488, 2522, 0, 2515, 2489, 2491, - 2493, 0, 2495, 2496, 2497, 2501, 2502, 2503, 2505, 2508, - 2509, 2510, 0, 2478, 0, 0, 0, 0, 3549, 2498, - 2507, 2499, 0, 0, 0, 0, 2395, 172, 0, 0, - 172, 172, 0, 172, 0, 2409, 0, 0, 179, 180, - 0, 181, 182, 238, 0, 0, 183, 0, 0, 184, - 0, 0, 0, 2490, 0, 0, 2520, 0, 0, 0, - 0, 0, 2460, 2461, 0, 2513, 0, 0, 0, 0, - 0, 0, 0, 2514, 0, 0, 198, 230, 242, 231, - 0, 150, 0, 0, 0, 0, 246, 172, 2318, 0, - 0, 0, 0, 0, 2318, 0, 0, 1320, 0, 4060, - 2032, 224, 223, 2515, 0, 0, 172, 0, 2487, 0, - 0, 0, 4070, 0, 0, 4071, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 178, 198, 230, 242, 231, - 84, 150, 0, 172, 2506, 0, 246, 0, 0, 0, - 0, 1441, 0, 2511, 0, 0, 0, 1441, 0, 0, - 152, 224, 223, 0, 0, 0, 0, 85, 0, 2490, - 0, 2486, 0, 0, 0, 2485, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 178, 0, 0, 0, 0, - 0, 4130, 0, 0, 0, 0, 0, 0, 0, 2504, - 0, 0, 0, 0, 0, 0, 0, 0, 2492, 0, - 0, 0, 151, 0, 0, 0, 2487, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 226, 227, - 228, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 246, 0, 2494, 0, 0, 3550, 0, 0, 0, - 2506, 0, 0, 0, 2500, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 4150, 0, - 0, 0, 1602, 0, 2488, 2522, 0, 0, 2489, 2491, - 2493, 0, 2495, 2496, 2497, 2501, 2502, 2503, 2505, 2508, - 2509, 2510, 1439, 0, 0, 0, 2685, 0, 0, 2498, - 2507, 2499, 0, 0, 235, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 2519, 2486, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 2520, 2521, 0, 2492, 2526, 0, 0, 2493, + 2495, 2497, 0, 2499, 2500, 2501, 2505, 2506, 2507, 2509, + 2512, 2513, 2514, 2474, 2475, 3291, 0, 0, 2485, 0, + 2502, 2511, 2503, 0, 0, 0, 0, 0, 0, 0, + 0, 2515, 0, 0, 0, 0, 0, 2494, 0, 1582, + 1581, 1591, 1592, 1593, 1594, 1584, 1585, 1586, 1587, 1588, + 1589, 1590, 1583, 2490, 0, 0, 1174, 2489, 0, 0, + 1172, 1175, 0, 0, 0, 0, 0, 0, 1176, 0, + 0, 0, 0, 0, 2518, 0, 0, 0, 0, 0, + 0, 2508, 0, 2322, 0, 0, 0, 0, 0, 0, + 2496, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 2589, 2523, 2522, 0, 0, 0, 0, + 0, 0, 1290, 0, 0, 0, 2805, 0, 2510, 1582, + 1581, 1591, 1592, 1593, 1594, 1584, 1585, 1586, 1587, 1588, + 1589, 1590, 1583, 0, 0, 0, 0, 3793, 0, 3795, + 0, 0, 0, 0, 2515, 0, 0, 2779, 0, 2603, + 2604, 0, 0, 0, 0, 0, 2610, 1606, 0, 0, + 0, 2482, 0, 0, 1922, 2617, 2490, 0, 0, 0, + 2489, 1582, 1581, 1591, 1592, 1593, 1594, 1584, 1585, 1586, + 1587, 1588, 1589, 1590, 1583, 0, 0, 0, 0, 0, + 0, 0, 0, 2906, 2508, 2908, 4943, 1291, 0, 0, + 0, 0, 0, 2496, 2524, 0, 3636, 3637, 2476, 0, + 0, 0, 3635, 0, 0, 0, 0, 2498, 0, 0, + 2932, 2933, 0, 0, 0, 0, 0, 0, 2504, 0, + 0, 0, 0, 0, 2949, 2950, 2951, 0, 0, 0, + 0, 2669, 0, 0, 3853, 0, 0, 0, 2492, 2526, + 0, 0, 2493, 2495, 2497, 0, 2499, 2500, 2501, 2505, + 2506, 2507, 2509, 2512, 2513, 2514, 0, 0, 0, 0, + 0, 0, 3876, 2502, 2511, 2503, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 2480, 0, 1284, 0, 1276, + 1280, 1282, 1582, 1581, 1591, 1592, 1593, 1594, 1584, 1585, + 1586, 1587, 1588, 1589, 1590, 1583, 0, 0, 3910, 1569, + 0, 1570, 1568, 0, 0, 1287, 0, 0, 0, 1279, + 0, 172, 200, 245, 199, 232, 201, 2518, 172, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 233, 0, 0, 0, 2455, 0, 0, 225, + 0, 0, 0, 234, 2491, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 202, 0, 0, 1818, + 1285, 0, 0, 169, 0, 0, 0, 0, 0, 2474, + 2475, 0, 1838, 0, 0, 0, 0, 0, 154, 0, + 0, 0, 1288, 0, 0, 0, 0, 2515, 238, 1289, + 0, 2226, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 1875, 2490, + 0, 0, 0, 2489, 2596, 925, 924, 931, 921, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 928, 929, + 0, 930, 934, 0, 1277, 915, 0, 2508, 1445, 0, + 0, 0, 0, 0, 0, 939, 2496, 0, 0, 172, + 0, 0, 172, 172, 0, 172, 0, 2491, 1286, 0, + 2523, 2522, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 1200, 0, 0, 0, 0, 179, 180, + 0, 181, 182, 0, 0, 0, 183, 0, 0, 184, + 0, 0, 0, 943, 0, 0, 945, 0, 0, 0, + 1278, 944, 2322, 0, 0, 0, 0, 0, 2322, 172, + 0, 0, 0, 4064, 0, 0, 0, 2482, 0, 0, + 0, 0, 0, 0, 0, 0, 4074, 2517, 172, 4075, + 0, 0, 0, 0, 0, 200, 245, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 172, 0, 0, 0, 0, + 2524, 0, 0, 0, 0, 2519, 198, 230, 242, 231, + 84, 150, 0, 0, 0, 0, 246, 0, 0, 0, + 0, 0, 1283, 0, 0, 0, 3553, 0, 0, 0, + 152, 224, 223, 0, 0, 4134, 0, 85, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 238, 0, 0, 0, 178, 0, 0, 2222, 0, + 0, 2494, 0, 0, 0, 2219, 1273, 0, 0, 2221, + 2218, 2220, 2224, 2225, 0, 0, 0, 2223, 0, 0, + 0, 0, 151, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 2517, 0, 0, 0, 0, 0, 226, 227, + 228, 0, 0, 0, 0, 0, 0, 0, 916, 918, + 917, 0, 4154, 0, 0, 0, 0, 0, 0, 0, + 923, 0, 0, 0, 1606, 0, 0, 0, 0, 0, + 2519, 0, 927, 0, 0, 0, 0, 0, 0, 942, + 0, 0, 2510, 0, 0, 0, 920, 0, 0, 0, + 2491, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 2517, 0, + 0, 0, 0, 0, 235, 0, 0, 1292, 3407, 3408, + 3409, 1272, 1274, 0, 0, 0, 2494, 0, 0, 1275, 0, 0, 0, 0, 0, 164, 0, 0, 0, 229, - 0, 165, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 2494, - 0, 0, 853, 2514, 0, 0, 0, 0, 0, 0, - 2500, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 4443, 0, 0, - 2488, 2522, 0, 4422, 2489, 2491, 2493, 166, 2495, 2496, - 2497, 2501, 2502, 2503, 2505, 2508, 2509, 2510, 0, 4230, - 75, 0, 0, 79, 0, 2498, 2507, 2499, 0, 4233, - 4234, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 2511, 0, 4436, 849, 850, 0, 0, - 0, 0, 0, 0, 2033, 241, 2034, 895, 0, 4425, - 0, 2486, 0, 0, 0, 2485, 4247, 0, 0, 4249, - 4419, 0, 0, 79, 0, 4445, 4446, 0, 0, 2514, - 0, 4420, 0, 0, 0, 0, 0, 0, 0, 2504, - 0, 0, 2487, 0, 0, 0, 0, 0, 2492, 0, - 0, 0, 0, 0, 176, 241, 177, 0, 0, 0, - 0, 921, 920, 927, 917, 0, 0, 0, 0, 72, - 4421, 0, 4428, 0, 924, 925, 0, 926, 930, 0, - 4150, 911, 0, 0, 0, 0, 0, 0, 172, 0, - 0, 935, 0, 172, 0, 0, 0, 1261, 0, 2511, - 172, 172, 168, 897, 0, 0, 896, 1814, 1814, 1814, - 1814, 1814, 1814, 1814, 1814, 0, 0, 2486, 3551, 0, - 0, 2485, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 939, - 0, 0, 941, 0, 0, 2504, 0, 940, 0, 0, - 0, 167, 168, 55, 2492, 0, 0, 0, 0, 73, - 882, 0, 0, 0, 9, 2222, 0, 0, 0, 0, - 854, 0, 0, 0, 0, 2902, 0, 2904, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 4444, 0, 3046, 856, 74, 0, - 173, 174, 2928, 2929, 175, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 2945, 2946, 2947, 0, - 0, 4432, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 853, 0, 0, 0, 0, - 0, 0, 0, 4429, 4433, 4431, 4430, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 2318, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 878, 0, 880, 877, 0, 2318, - 0, 881, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 4439, 4440, 0, 0, 0, 0, 0, 172, 849, - 850, 0, 2487, 0, 912, 914, 913, 0, 0, 0, - 895, 0, 0, 0, 0, 0, 919, 876, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 923, 848, - 0, 0, 0, 0, 1261, 938, 0, 0, 0, 0, - 855, 890, 916, 0, 0, 0, 906, 0, 0, 0, - 0, 0, 0, 172, 1834, 0, 4535, 0, 0, 0, - 0, 0, 886, 0, 4447, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 4423, 0, 0, - 4438, 0, 2218, 0, 0, 0, 0, 0, 0, 2215, - 1871, 0, 0, 2217, 2214, 2216, 2220, 2221, 887, 891, - 841, 2219, 0, 0, 0, 0, 897, 0, 2487, 896, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 873, - 1441, 871, 875, 894, 0, 0, 0, 872, 869, 867, - 0, 874, 858, 859, 857, 0, 847, 860, 861, 862, - 863, 842, 0, 0, 892, 0, 893, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 4631, 888, 889, 0, - 0, 0, 0, 882, 0, 0, 0, 0, 1624, 0, - 0, 0, 0, 854, 0, 1198, 0, 1199, 0, 0, - 1565, 0, 1566, 1564, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 884, 0, 918, 922, 928, - 856, 929, 931, 0, 843, 932, 933, 934, 0, 0, - 0, 936, 937, 879, 0, 0, 0, 1180, 0, 0, - 868, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 1193, 0, 1188, 0, 0, 0, 0, 2203, 2204, - 2205, 2206, 2207, 2208, 2209, 2210, 2211, 2212, 2213, 2225, - 2226, 2227, 2228, 2229, 2230, 2223, 2224, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 2222, 0, 0, 0, 0, 878, 0, 880, - 877, 0, 4751, 0, 881, 0, 0, 0, 0, 0, - 0, 0, 0, 4437, 0, 0, 0, 0, 0, 0, - 0, 0, 1169, 0, 0, 0, 0, 0, 4442, 883, - 0, 0, 0, 0, 0, 851, 852, 172, 0, 840, - 844, 0, 0, 0, 0, 0, 0, 845, 0, 0, - 876, 0, 0, 0, 0, 0, 0, 0, 846, 0, - 0, 0, 848, 0, 0, 4426, 4427, 172, 0, 0, - 0, 0, 0, 855, 890, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 915, 0, 0, - 4659, 0, 0, 0, 0, 886, 0, 0, 0, 0, - 0, 1814, 0, 1814, 0, 0, 0, 0, 0, 0, - 0, 0, 1195, 0, 1187, 0, 0, 0, 0, 0, - 0, 0, 0, 1192, 1190, 0, 0, 0, 0, 0, - 0, 887, 891, 0, 4680, 942, 943, 944, 945, 946, - 3403, 3404, 3405, 0, 1181, 0, 0, 0, 1918, 0, - 0, 0, 873, 0, 871, 875, 894, 0, 0, 0, - 872, 869, 867, 0, 874, 858, 859, 857, 0, 847, - 860, 861, 862, 863, 842, 0, 4658, 892, 0, 893, - 0, 0, 0, 1197, 0, 0, 0, 0, 1186, 0, - 888, 889, 1185, 921, 920, 927, 917, 0, 1174, 0, - 0, 0, 0, 0, 0, 0, 924, 925, 0, 926, - 930, 0, 3582, 911, 0, 0, 0, 1179, 0, 2218, - 0, 0, 0, 935, 0, 0, 2215, 0, 884, 4912, - 2217, 2214, 2216, 2220, 2221, 0, 0, 843, 2219, 0, - 0, 0, 0, 0, 0, 0, 879, 0, 0, 0, - 0, 0, 0, 868, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 1191, 0, 0, 0, 1177, - 1814, 939, 0, 0, 941, 0, 0, 0, 172, 940, - 0, 0, 0, 172, 0, 0, 0, 0, 0, 0, + 0, 165, 0, 0, 0, 0, 2519, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 2207, 2208, + 2209, 2210, 2211, 2212, 2213, 2214, 2215, 2216, 2217, 2229, + 2230, 2231, 2232, 2233, 2234, 2227, 2228, 0, 0, 246, + 0, 2498, 0, 4234, 3554, 0, 0, 0, 0, 0, + 3586, 0, 2504, 4237, 4238, 0, 0, 0, 0, 0, + 0, 166, 2494, 4841, 0, 0, 0, 2510, 0, 0, + 0, 0, 2492, 2526, 75, 0, 2493, 2495, 2497, 0, + 2499, 2500, 2501, 2505, 2506, 2507, 2509, 2512, 2513, 2514, + 4251, 0, 0, 4253, 0, 0, 0, 2502, 2511, 2503, + 0, 922, 926, 932, 0, 933, 935, 0, 0, 936, + 937, 938, 0, 0, 0, 940, 941, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 79, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 2510, 0, 0, 0, 0, 0, 0, + 0, 2518, 0, 0, 4154, 0, 0, 0, 176, 241, + 177, 0, 0, 0, 0, 0, 2498, 0, 0, 0, + 172, 0, 0, 72, 0, 172, 0, 2504, 0, 1265, + 0, 0, 172, 172, 0, 0, 0, 0, 0, 1818, + 1818, 1818, 1818, 1818, 1818, 1818, 1818, 2492, 2526, 0, + 0, 2493, 2495, 2497, 0, 2499, 2500, 2501, 2505, 2506, + 2507, 2509, 2512, 2513, 2514, 0, 0, 0, 0, 0, + 0, 2515, 2502, 2511, 2503, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 3782, 0, + 0, 0, 2498, 2490, 0, 0, 0, 2489, 0, 0, + 0, 0, 0, 2504, 0, 167, 168, 55, 0, 0, + 0, 0, 0, 73, 0, 0, 0, 0, 9, 0, + 0, 2508, 0, 2492, 2526, 919, 2518, 2493, 2495, 2497, + 2496, 2499, 2500, 2501, 2505, 2506, 2507, 2509, 2512, 2513, + 2514, 0, 0, 0, 0, 857, 0, 0, 2502, 2511, + 2503, 0, 74, 0, 173, 174, 0, 0, 175, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 946, 947, 948, 949, 950, 0, 0, + 0, 0, 0, 0, 0, 0, 2322, 0, 0, 857, + 0, 0, 0, 0, 0, 0, 2515, 0, 0, 0, + 3555, 0, 2518, 2322, 0, 0, 200, 245, 199, 232, + 201, 0, 0, 0, 0, 0, 0, 0, 2490, 853, + 854, 0, 2489, 0, 0, 0, 2030, 0, 0, 0, + 899, 0, 0, 225, 0, 0, 0, 0, 0, 0, + 172, 0, 0, 0, 0, 0, 2508, 0, 0, 0, + 0, 0, 0, 0, 0, 2496, 0, 0, 0, 0, + 0, 0, 0, 853, 854, 0, 0, 0, 0, 0, + 0, 0, 2515, 0, 899, 0, 1265, 0, 0, 0, + 0, 0, 238, 0, 0, 0, 1073, 0, 2019, 0, + 4539, 0, 0, 0, 2490, 172, 0, 0, 2489, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 2508, 3957, 0, 0, 901, 0, 0, 900, + 0, 2496, 0, 4447, 0, 0, 0, 0, 0, 4426, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 901, 0, 0, 900, 0, 0, 0, 0, 0, 0, + 0, 4440, 0, 886, 0, 0, 0, 0, 0, 0, + 4635, 0, 0, 858, 2491, 4429, 0, 0, 0, 0, + 0, 0, 1628, 0, 0, 0, 4423, 0, 0, 0, + 0, 4449, 4450, 0, 0, 0, 0, 4424, 0, 0, + 860, 0, 0, 0, 0, 0, 0, 886, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 858, 0, 0, + 0, 0, 0, 0, 0, 4021, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 4425, 0, 4432, 0, + 198, 230, 242, 231, 860, 150, 4037, 0, 4038, 0, + 246, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 2036, 224, 223, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 882, 0, 884, + 881, 0, 0, 0, 885, 0, 0, 0, 0, 178, + 0, 0, 0, 0, 0, 0, 4755, 0, 0, 2491, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 882, 0, 884, 881, 0, 0, 0, 885, 0, + 880, 0, 0, 0, 0, 0, 0, 2226, 0, 172, + 0, 0, 852, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 859, 894, 0, 0, 0, 0, 0, + 4448, 0, 3050, 0, 0, 0, 0, 0, 0, 172, + 0, 0, 0, 0, 880, 2491, 0, 0, 0, 890, + 0, 0, 0, 0, 0, 0, 852, 4436, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 859, 894, 0, + 0, 0, 0, 1818, 0, 1818, 0, 0, 0, 4433, + 4437, 4435, 4434, 0, 0, 891, 895, 845, 0, 0, + 0, 0, 0, 890, 0, 0, 0, 0, 0, 0, + 0, 1445, 0, 0, 0, 0, 877, 0, 875, 879, + 898, 0, 1922, 0, 876, 873, 871, 0, 878, 862, + 863, 861, 0, 851, 864, 865, 866, 867, 846, 891, + 895, 896, 0, 897, 0, 0, 0, 4443, 4444, 0, + 0, 0, 0, 0, 892, 893, 0, 0, 0, 0, + 877, 0, 875, 879, 898, 0, 0, 0, 876, 873, + 871, 0, 878, 862, 863, 861, 0, 851, 864, 865, + 866, 867, 846, 0, 0, 896, 0, 897, 0, 4663, + 0, 0, 888, 0, 0, 0, 0, 0, 892, 893, + 0, 847, 0, 4916, 0, 0, 0, 0, 0, 0, + 883, 0, 0, 0, 0, 0, 0, 872, 0, 0, + 4451, 0, 0, 0, 4226, 0, 0, 0, 0, 0, + 0, 0, 0, 4684, 0, 0, 888, 4427, 0, 0, + 4442, 79, 0, 0, 2222, 847, 0, 0, 0, 0, + 0, 2219, 1818, 0, 883, 2221, 2218, 2220, 2224, 2225, + 172, 872, 0, 2223, 0, 172, 0, 925, 924, 931, + 921, 0, 2037, 241, 2038, 4662, 0, 0, 0, 0, + 928, 929, 0, 930, 934, 0, 0, 915, 925, 924, + 931, 921, 0, 0, 0, 0, 0, 939, 0, 0, + 0, 928, 929, 0, 930, 934, 887, 0, 915, 0, + 0, 0, 855, 856, 0, 0, 844, 848, 939, 0, + 0, 0, 0, 0, 849, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 850, 0, 0, 0, 0, + 0, 0, 0, 5012, 0, 943, 0, 0, 945, 0, + 887, 0, 0, 944, 0, 0, 855, 856, 0, 0, + 844, 848, 0, 0, 0, 0, 0, 0, 849, 0, + 168, 0, 0, 0, 0, 0, 0, 0, 0, 850, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 1445, 0, 0, 5050, 0, 0, 0, 0, + 0, 0, 0, 0, 2207, 2208, 2209, 2210, 2211, 2212, + 2213, 2214, 2215, 2216, 2217, 2229, 2230, 2231, 2232, 2233, + 2234, 2227, 2228, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 4441, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 4659, 0, 4446, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 4430, 4431, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 916, 918, 917, 0, 0, 0, 0, 0, 0, 0, + 0, 5145, 923, 0, 0, 0, 5149, 0, 0, 0, + 0, 916, 918, 917, 927, 0, 0, 0, 0, 0, + 1445, 942, 0, 923, 0, 0, 0, 0, 920, 0, + 0, 0, 910, 0, 0, 927, 0, 4664, 4665, 0, + 0, 0, 942, 0, 0, 0, 0, 0, 0, 920, + 0, 0, 0, 0, 4660, 4661, 0, 4668, 4667, 4666, + 4682, 4683, 4685, 4669, 4670, 4673, 4675, 4674, 4671, 4672, + 4676, 4678, 4679, 4680, 4677, 0, 4681, 0, 0, 0, + 0, 0, 4686, 0, 1265, 0, 172, 0, 0, 4688, + 0, 0, 0, 4687, 0, 5145, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 1196, 0, + 172, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 1818, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 1178, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 883, 0, 0, 0, 0, 0, 851, 852, - 0, 0, 840, 844, 0, 0, 0, 0, 0, 0, - 845, 0, 0, 0, 0, 0, 0, 4655, 0, 5008, - 0, 846, 0, 0, 0, 2203, 2204, 2205, 2206, 2207, - 2208, 2209, 2210, 2211, 2212, 2213, 2225, 2226, 2227, 2228, - 2229, 2230, 2223, 2224, 0, 0, 0, 0, 0, 0, - 3778, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 1194, 0, 0, 0, 0, 0, 0, - 0, 5046, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 912, 914, 913, 0, - 0, 0, 0, 0, 0, 0, 0, 1173, 919, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 4660, 4661, - 923, 0, 0, 0, 0, 0, 0, 938, 0, 0, - 0, 0, 0, 0, 916, 4656, 4657, 0, 4664, 4663, - 4662, 4678, 4679, 4681, 4665, 4666, 4669, 4671, 4670, 4667, - 4668, 4672, 4674, 4675, 4676, 4673, 0, 4677, 0, 0, - 0, 0, 0, 4682, 0, 0, 0, 0, 0, 0, - 4684, 0, 0, 0, 4683, 0, 0, 0, 0, 0, + 0, 0, 0, 5145, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 172, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 922, 926, 932, 0, 933, 935, 0, + 0, 936, 937, 938, 0, 0, 0, 940, 941, 0, + 0, 0, 0, 0, 922, 926, 932, 0, 933, 935, + 0, 0, 936, 937, 938, 0, 0, 0, 940, 941, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 5141, 0, 0, - 0, 0, 5145, 0, 0, 0, 0, 0, 1170, 0, - 0, 0, 1168, 1171, 0, 0, 0, 0, 0, 0, - 1172, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 5334, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 4156, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 4723, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 1261, 0, 172, 3953, 0, 0, 0, 918, - 922, 928, 0, 929, 931, 0, 0, 932, 933, 934, - 0, 5141, 0, 936, 937, 0, 0, 0, 172, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 1814, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 5141, - 0, 172, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 172, 0, 0, 0, 0, 919, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 919, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 4017, 0, 0, + 0, 0, 0, 0, 0, 4798, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 946, 947, 948, 949, 950, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 4033, 0, - 4034, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 915, - 0, 0, 0, 5319, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 4152, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 942, 943, 944, - 945, 946, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 172, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 4156, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 172, 172, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 1441, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 172, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 1818, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 4152, - 0, 0, 0, 0, 0, 0, 4222, 0, 0, 172, - 172, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 1818, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 172, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 1814, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 1441, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 1814, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 172, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 4152, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 1441, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 200, 245, 1019, 0, 0, 0, 0, 0, 0, - 0, 172, 500, 0, 0, 643, 677, 666, 755, 630, - 0, 0, 0, 0, 0, 0, 970, 0, 0, 0, - 394, 0, 0, 463, 681, 662, 673, 663, 506, 648, - 649, 650, 657, 406, 651, 787, 653, 622, 654, 623, - 655, 656, 1605, 680, 629, 542, 482, 0, 697, 0, - 0, 1092, 1101, 0, 0, 0, 0, 0, 0, 0, - 0, 1088, 0, 0, 0, 0, 0, 961, 0, 0, - 999, 1069, 1068, 986, 996, 0, 0, 365, 269, 624, - 751, 626, 625, 987, 0, 988, 992, 995, 991, 989, - 990, 0, 1083, 0, 0, 0, 0, 0, 0, 953, - 965, 0, 971, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 962, 963, - 0, 0, 0, 0, 1020, 0, 964, 0, 966, 0, - 0, 0, 0, 543, 571, 0, 584, 0, 432, 433, - 1015, 993, 997, 0, 0, 0, 0, 350, 550, 568, - 366, 537, 582, 371, 545, 561, 361, 499, 533, 0, - 0, 352, 566, 544, 477, 351, 0, 526, 391, 408, - 388, 497, 994, 0, 1018, 1022, 387, 1107, 1016, 576, - 354, 0, 575, 496, 469, 562, 567, 478, 471, 0, - 353, 564, 476, 470, 446, 398, 1108, 447, 448, 449, - 450, 451, 452, 412, 512, 467, 513, 413, 484, 483, - 485, 414, 415, 416, 417, 418, 419, 420, 421, 422, - 423, 0, 0, 0, 0, 0, 606, 607, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 4719, 0, 743, 1013, 246, 748, 0, 578, 0, - 0, 1090, 0, 0, 172, 548, 0, 0, 453, 0, - 0, 0, 1017, 0, 529, 502, 1104, 0, 0, 527, - 465, 563, 514, 569, 551, 577, 521, 515, 344, 552, - 390, 479, 362, 364, 776, 392, 395, 399, 400, 490, - 491, 508, 536, 555, 556, 557, 389, 376, 528, 377, - 409, 378, 345, 381, 380, 382, 538, 383, 347, 509, - 560, 0, 405, 524, 474, 348, 473, 510, 559, 558, - 363, 586, 593, 594, 685, 0, 599, 790, 791, 792, - 608, 0, 516, 359, 356, 0, 0, 4794, 385, 511, - 372, 374, 375, 373, 505, 507, 613, 614, 615, 617, - 0, 618, 619, 0, 0, 0, 0, 620, 686, 704, - 670, 639, 601, 694, 636, 640, 641, 426, 427, 428, - 429, 707, 0, 0, 0, 592, 454, 455, 0, 397, - 396, 475, 349, 0, 0, 443, 425, 517, 355, 393, - 445, 431, 456, 457, 458, 459, 460, 0, 461, 462, - 403, 339, 340, 784, 1087, 498, 709, 746, 747, 631, - 0, 1103, 1082, 1084, 1085, 1089, 1093, 1094, 1095, 1096, - 1097, 1099, 1102, 1106, 783, 0, 687, 701, 788, 700, - 780, 504, 0, 535, 698, 645, 0, 691, 664, 665, - 0, 692, 660, 696, 0, 633, 0, 602, 605, 635, - 722, 723, 724, 346, 604, 726, 727, 728, 729, 730, - 731, 732, 725, 1105, 668, 644, 671, 583, 647, 646, - 0, 0, 682, 1021, 683, 684, 486, 487, 488, 489, - 1091, 710, 370, 603, 519, 520, 0, 669, 172, 0, - 0, 0, 0, 0, 0, 0, 674, 675, 672, 793, - 0, 733, 734, 0, 0, 597, 598, 402, 0, 616, - 410, 369, 503, 404, 581, 434, 0, 609, 676, 610, - 522, 523, 737, 742, 738, 739, 741, 762, 493, 424, - 430, 539, 444, 466, 525, 580, 501, 530, 367, 570, - 541, 472, 661, 689, 1114, 1086, 1113, 1115, 1116, 1112, - 1117, 1118, 1098, 975, 0, 1028, 1029, 1110, 1109, 1111, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 718, 717, 716, 715, 714, 713, 712, 711, 0, - 0, 658, 384, 781, 775, 782, 763, 766, 765, 740, - 982, 341, 638, 464, 518, 401, 705, 706, 0, 761, - 1076, 1039, 1040, 1041, 972, 1042, 1036, 1037, 973, 1038, - 1077, 1026, 1073, 1074, 1001, 1031, 1043, 1072, 1044, 1075, - 1002, 1078, 1119, 1120, 1050, 1034, 302, 1121, 1047, 1079, - 1071, 1070, 1045, 1027, 1080, 1081, 1009, 1004, 1048, 1049, - 1035, 1056, 1057, 1058, 1061, 974, 1062, 1063, 1064, 1065, - 1066, 1060, 1059, 1023, 1024, 1025, 1051, 1052, 1054, 1055, - 1030, 549, 1005, 1006, 1007, 1008, 0, 0, 587, 588, - 589, 612, 0, 590, 572, 637, 411, 342, 553, 579, - 778, 779, 0, 0, 0, 0, 0, 0, 0, 688, - 699, 735, 736, 0, 749, 750, 752, 754, 1067, 756, - 546, 547, 764, 0, 0, 435, 357, 358, 494, 495, - 532, 0, 436, 437, 438, 442, 439, 440, 441, 1053, - 759, 760, 757, 468, 531, 554, 540, 777, 481, 0, - 785, 627, 628, 786, 745, 744, 343, 0, 1032, 1033, - 702, 703, 1019, 0, 1100, 480, 634, 0, 967, 0, - 0, 500, 0, 0, 643, 677, 666, 755, 630, 0, - 0, 0, 0, 0, 0, 970, 0, 0, 0, 394, - 0, 0, 463, 681, 662, 673, 663, 506, 648, 649, - 650, 657, 406, 651, 787, 653, 622, 654, 623, 655, - 656, 1010, 680, 629, 542, 482, 0, 697, 0, 0, - 1092, 1101, 0, 0, 0, 0, 0, 0, 0, 0, - 1088, 0, 0, 0, 0, 0, 961, 0, 0, 999, - 1069, 1068, 986, 996, 0, 0, 365, 269, 624, 751, - 626, 625, 987, 0, 988, 992, 995, 991, 989, 990, - 0, 1083, 0, 0, 0, 0, 0, 0, 953, 965, - 0, 971, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 962, 963, 0, - 0, 0, 0, 1020, 0, 964, 0, 966, 0, 0, - 0, 0, 543, 571, 0, 584, 0, 432, 433, 1015, - 993, 997, 0, 0, 0, 0, 350, 550, 568, 366, - 537, 582, 371, 545, 561, 361, 499, 533, 0, 0, - 352, 566, 544, 477, 351, 0, 526, 391, 408, 388, - 497, 994, 0, 1018, 1022, 387, 1107, 1016, 576, 354, - 0, 575, 496, 469, 562, 567, 478, 471, 0, 353, - 564, 476, 470, 446, 398, 1108, 447, 448, 449, 450, - 451, 452, 412, 512, 467, 513, 413, 484, 483, 485, - 414, 415, 416, 417, 418, 419, 420, 421, 422, 423, - 0, 0, 0, 0, 0, 606, 607, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 743, 1013, 0, 748, 0, 578, 0, 0, - 1090, 0, 0, 0, 548, 0, 0, 453, 0, 0, - 0, 1017, 0, 529, 502, 1104, 0, 0, 527, 465, - 563, 514, 569, 551, 577, 521, 515, 344, 552, 390, - 479, 362, 364, 776, 392, 395, 399, 400, 490, 491, - 508, 536, 555, 556, 557, 389, 376, 528, 377, 409, - 378, 345, 381, 380, 382, 538, 383, 347, 509, 560, - 0, 405, 524, 474, 348, 473, 510, 559, 558, 363, - 586, 593, 594, 685, 0, 599, 790, 791, 792, 608, - 0, 516, 359, 356, 0, 0, 0, 385, 511, 372, - 374, 375, 373, 505, 507, 613, 614, 615, 617, 0, - 618, 619, 0, 0, 0, 0, 620, 686, 704, 670, - 639, 601, 694, 636, 640, 641, 426, 427, 428, 429, - 707, 2247, 2246, 2248, 592, 454, 455, 0, 397, 396, - 475, 349, 0, 0, 443, 425, 517, 355, 393, 445, - 431, 456, 457, 458, 459, 460, 0, 461, 462, 403, - 339, 340, 784, 1087, 498, 709, 746, 747, 631, 0, - 1103, 1082, 1084, 1085, 1089, 1093, 1094, 1095, 1096, 1097, - 1099, 1102, 1106, 783, 0, 687, 701, 788, 700, 780, - 504, 0, 535, 698, 645, 0, 691, 664, 665, 0, - 692, 660, 696, 0, 633, 0, 602, 605, 635, 722, - 723, 724, 346, 604, 726, 727, 728, 729, 730, 731, - 732, 725, 1105, 668, 644, 671, 583, 647, 646, 0, - 0, 682, 1021, 683, 684, 486, 487, 488, 489, 1091, - 710, 370, 603, 519, 520, 0, 669, 0, 0, 0, - 0, 0, 0, 0, 0, 674, 675, 672, 793, 0, - 733, 734, 0, 0, 597, 598, 402, 0, 616, 410, - 369, 503, 404, 581, 434, 0, 609, 676, 610, 522, - 523, 737, 742, 738, 739, 741, 762, 493, 424, 430, - 539, 444, 466, 525, 580, 501, 530, 367, 570, 541, - 472, 661, 689, 1114, 1086, 1113, 1115, 1116, 1112, 1117, - 1118, 1098, 975, 0, 1028, 1029, 1110, 1109, 1111, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 718, 717, 716, 715, 714, 713, 712, 711, 0, 0, - 658, 384, 781, 775, 782, 763, 766, 765, 740, 982, - 341, 638, 464, 518, 401, 705, 706, 0, 761, 1076, - 1039, 1040, 1041, 972, 1042, 1036, 1037, 973, 1038, 1077, - 1026, 1073, 1074, 1001, 1031, 1043, 1072, 1044, 1075, 1002, - 1078, 1119, 1120, 1050, 1034, 302, 1121, 1047, 1079, 1071, - 1070, 1045, 1027, 1080, 1081, 1009, 1004, 1048, 1049, 1035, - 1056, 1057, 1058, 1061, 974, 1062, 1063, 1064, 1065, 1066, - 1060, 1059, 1023, 1024, 1025, 1051, 1052, 1054, 1055, 1030, - 549, 1005, 1006, 1007, 1008, 0, 0, 587, 588, 589, - 612, 0, 590, 572, 637, 411, 342, 553, 579, 778, - 779, 0, 0, 0, 0, 0, 0, 0, 688, 699, - 735, 736, 0, 749, 750, 752, 754, 1067, 756, 546, - 547, 764, 0, 0, 435, 357, 358, 494, 495, 532, - 0, 436, 437, 438, 442, 439, 440, 441, 1053, 759, - 760, 757, 468, 531, 554, 540, 777, 481, 0, 785, - 627, 628, 786, 745, 744, 343, 0, 1032, 1033, 702, - 703, 0, 0, 1100, 480, 634, 0, 967, 200, 245, - 1019, 0, 0, 0, 0, 0, 0, 0, 0, 500, - 0, 0, 643, 677, 666, 755, 630, 0, 0, 0, - 0, 0, 0, 970, 0, 0, 0, 394, 0, 0, - 463, 681, 662, 673, 663, 506, 648, 649, 650, 657, - 406, 651, 787, 653, 622, 654, 623, 655, 656, 1010, - 680, 629, 542, 482, 0, 697, 0, 0, 1092, 1101, - 0, 0, 0, 0, 0, 0, 0, 0, 1088, 0, - 0, 0, 0, 0, 961, 0, 0, 999, 1069, 1068, - 986, 996, 0, 0, 365, 269, 624, 751, 626, 625, - 987, 0, 988, 992, 995, 991, 989, 990, 0, 1083, - 0, 0, 0, 0, 0, 0, 953, 965, 0, 971, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 962, 963, 0, 0, 0, - 0, 1020, 0, 964, 0, 966, 0, 0, 0, 0, - 543, 571, 0, 584, 0, 432, 433, 1015, 993, 997, - 0, 0, 0, 0, 350, 550, 568, 366, 537, 582, - 371, 545, 561, 361, 499, 533, 0, 0, 352, 566, - 544, 477, 351, 0, 526, 391, 408, 388, 497, 994, - 0, 1018, 1022, 387, 1107, 1016, 576, 354, 0, 575, - 496, 469, 562, 567, 478, 471, 0, 353, 564, 476, - 470, 446, 398, 1108, 447, 448, 449, 450, 451, 452, - 412, 512, 467, 513, 413, 484, 483, 485, 414, 415, - 416, 417, 418, 419, 420, 421, 422, 423, 0, 0, - 0, 0, 0, 606, 607, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 743, 1013, 0, 748, 0, 578, 0, 0, 1090, 0, - 0, 0, 548, 0, 0, 453, 0, 0, 0, 1017, - 0, 529, 502, 1104, 0, 0, 527, 465, 563, 514, - 569, 551, 577, 521, 515, 344, 552, 390, 479, 362, - 364, 776, 392, 395, 399, 400, 490, 491, 508, 536, - 555, 556, 557, 389, 376, 528, 377, 409, 378, 345, - 381, 380, 382, 538, 383, 347, 509, 560, 0, 405, - 524, 474, 348, 473, 510, 559, 558, 363, 586, 593, - 594, 685, 0, 599, 790, 791, 792, 608, 0, 516, - 359, 356, 0, 0, 0, 385, 511, 372, 374, 375, - 373, 505, 507, 613, 614, 615, 617, 0, 618, 619, - 0, 0, 0, 0, 620, 686, 704, 670, 639, 601, - 694, 636, 640, 641, 426, 427, 428, 429, 707, 0, - 0, 0, 592, 454, 455, 0, 397, 396, 475, 349, - 0, 0, 443, 425, 517, 355, 393, 445, 431, 456, - 457, 458, 459, 460, 0, 461, 462, 403, 339, 340, - 784, 1087, 498, 709, 746, 747, 631, 0, 1103, 1082, - 1084, 1085, 1089, 1093, 1094, 1095, 1096, 1097, 1099, 1102, - 1106, 783, 0, 687, 701, 788, 700, 780, 504, 0, - 535, 698, 645, 0, 691, 664, 665, 0, 692, 660, - 696, 0, 633, 0, 602, 605, 635, 722, 723, 724, - 346, 604, 726, 727, 728, 729, 730, 731, 732, 725, - 1105, 668, 644, 671, 583, 647, 646, 0, 0, 682, - 1021, 683, 684, 486, 487, 488, 489, 1091, 710, 370, - 603, 519, 520, 0, 669, 0, 0, 0, 0, 0, - 0, 0, 0, 674, 675, 672, 793, 0, 733, 734, - 0, 0, 597, 598, 402, 0, 616, 410, 369, 503, - 404, 581, 434, 0, 609, 676, 610, 522, 523, 737, - 742, 738, 739, 741, 762, 493, 424, 430, 539, 444, - 466, 525, 580, 501, 530, 367, 570, 541, 472, 661, - 689, 1114, 1086, 1113, 1115, 1116, 1112, 1117, 1118, 1098, - 975, 0, 1028, 1029, 1110, 1109, 1111, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 718, 717, - 716, 715, 714, 713, 712, 711, 0, 0, 658, 384, - 781, 775, 782, 763, 766, 765, 740, 982, 341, 638, - 464, 518, 401, 705, 706, 0, 761, 1076, 1039, 1040, - 1041, 972, 1042, 1036, 1037, 973, 1038, 1077, 1026, 1073, - 1074, 1001, 1031, 1043, 1072, 1044, 1075, 1002, 1078, 1119, - 1120, 1050, 1034, 302, 1121, 1047, 1079, 1071, 1070, 1045, - 1027, 1080, 1081, 1009, 1004, 1048, 1049, 1035, 1056, 1057, - 1058, 1061, 974, 1062, 1063, 1064, 1065, 1066, 1060, 1059, - 1023, 1024, 1025, 1051, 1052, 1054, 1055, 1030, 549, 1005, - 1006, 1007, 1008, 0, 0, 587, 588, 589, 612, 0, - 590, 572, 637, 411, 342, 553, 579, 778, 779, 0, - 0, 0, 0, 0, 0, 0, 688, 699, 735, 736, - 0, 749, 750, 752, 754, 1067, 756, 546, 547, 764, - 0, 0, 435, 357, 358, 494, 495, 532, 0, 436, - 437, 438, 442, 439, 440, 441, 1053, 759, 760, 757, - 468, 531, 554, 540, 777, 481, 0, 785, 627, 628, - 786, 745, 744, 343, 0, 1032, 1033, 702, 703, 1019, - 0, 1100, 480, 634, 0, 967, 0, 0, 500, 0, - 0, 643, 677, 666, 755, 630, 0, 0, 0, 0, - 0, 0, 970, 0, 0, 0, 394, 2319, 0, 463, - 681, 662, 673, 663, 506, 648, 649, 650, 657, 406, - 651, 787, 653, 622, 654, 623, 655, 656, 1010, 680, - 629, 542, 482, 0, 697, 0, 0, 1092, 1101, 0, - 0, 0, 0, 0, 0, 0, 0, 1088, 0, 2568, - 0, 0, 0, 961, 0, 0, 999, 1069, 1068, 986, - 996, 0, 0, 365, 269, 624, 751, 626, 625, 987, - 0, 988, 992, 995, 991, 989, 990, 0, 1083, 0, - 0, 0, 0, 0, 0, 953, 965, 0, 971, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 962, 963, 0, 0, 0, 0, - 1020, 0, 964, 0, 966, 0, 0, 0, 0, 543, - 571, 0, 584, 0, 432, 433, 2569, 993, 997, 0, - 0, 0, 0, 350, 550, 568, 366, 537, 582, 371, - 545, 561, 361, 499, 533, 0, 0, 352, 566, 544, - 477, 351, 0, 526, 391, 408, 388, 497, 994, 0, - 1018, 1022, 387, 1107, 1016, 576, 354, 0, 575, 496, - 469, 562, 567, 478, 471, 0, 353, 564, 476, 470, - 446, 398, 1108, 447, 448, 449, 450, 451, 452, 412, - 512, 467, 513, 413, 484, 483, 485, 414, 415, 416, - 417, 418, 419, 420, 421, 422, 423, 0, 0, 0, - 0, 0, 606, 607, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 743, - 1013, 0, 748, 0, 578, 0, 0, 1090, 0, 0, - 0, 548, 0, 0, 453, 0, 0, 0, 1017, 0, - 529, 502, 1104, 0, 0, 527, 465, 563, 514, 569, - 551, 577, 521, 515, 344, 552, 390, 479, 362, 364, - 776, 392, 395, 399, 400, 490, 491, 508, 536, 555, - 556, 557, 389, 376, 528, 377, 409, 378, 345, 381, - 380, 382, 538, 383, 347, 509, 560, 0, 405, 524, - 474, 348, 473, 510, 559, 558, 363, 586, 593, 594, - 685, 0, 599, 790, 791, 792, 608, 0, 516, 359, - 356, 0, 0, 0, 385, 511, 372, 374, 375, 373, - 505, 507, 613, 614, 615, 617, 0, 618, 619, 0, - 0, 0, 0, 620, 686, 704, 670, 639, 601, 694, - 636, 640, 641, 426, 427, 428, 429, 707, 0, 0, - 0, 592, 454, 455, 0, 397, 396, 475, 349, 0, - 0, 443, 425, 517, 355, 393, 445, 431, 456, 457, - 458, 459, 460, 0, 461, 462, 403, 339, 340, 784, - 1087, 498, 709, 746, 747, 631, 0, 1103, 1082, 1084, - 1085, 1089, 1093, 1094, 1095, 1096, 1097, 1099, 1102, 1106, - 783, 0, 687, 701, 788, 700, 780, 504, 0, 535, - 698, 645, 0, 691, 664, 665, 0, 692, 660, 696, - 0, 633, 0, 602, 605, 635, 722, 723, 724, 346, - 604, 726, 727, 728, 729, 730, 731, 732, 725, 1105, - 668, 644, 671, 583, 647, 646, 0, 0, 682, 1021, - 683, 684, 486, 487, 488, 489, 1091, 710, 370, 603, - 519, 520, 0, 669, 0, 0, 0, 0, 0, 0, - 0, 0, 674, 675, 672, 793, 0, 733, 734, 0, - 0, 597, 598, 402, 0, 616, 410, 369, 503, 404, - 581, 434, 0, 609, 676, 610, 522, 523, 737, 742, - 738, 739, 741, 762, 493, 424, 430, 539, 444, 466, - 525, 580, 501, 530, 367, 570, 541, 472, 661, 689, - 1114, 1086, 1113, 1115, 1116, 1112, 1117, 1118, 1098, 975, - 0, 1028, 1029, 1110, 1109, 1111, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 718, 717, 716, - 715, 714, 713, 712, 711, 0, 0, 658, 384, 781, - 775, 782, 763, 766, 765, 740, 982, 341, 638, 464, - 518, 401, 705, 706, 0, 761, 1076, 1039, 1040, 1041, - 972, 1042, 1036, 1037, 973, 1038, 1077, 1026, 1073, 1074, - 1001, 1031, 1043, 1072, 1044, 1075, 1002, 1078, 1119, 1120, - 1050, 1034, 302, 1121, 1047, 1079, 1071, 1070, 1045, 1027, - 1080, 1081, 1009, 1004, 1048, 1049, 1035, 1056, 1057, 1058, - 1061, 974, 1062, 1063, 1064, 1065, 1066, 1060, 1059, 1023, - 1024, 1025, 1051, 1052, 1054, 1055, 1030, 549, 1005, 1006, - 1007, 1008, 0, 0, 587, 588, 589, 612, 0, 590, - 572, 637, 411, 342, 553, 579, 778, 779, 0, 0, - 0, 0, 0, 0, 0, 688, 699, 735, 736, 0, - 749, 750, 752, 754, 1067, 756, 546, 547, 764, 0, - 0, 435, 357, 358, 494, 495, 532, 0, 436, 437, - 438, 442, 439, 440, 441, 1053, 759, 760, 757, 468, - 531, 554, 540, 777, 481, 0, 785, 627, 628, 786, - 745, 744, 343, 0, 1032, 1033, 702, 703, 1019, 0, - 1100, 480, 634, 0, 967, 0, 0, 500, 0, 0, - 643, 677, 666, 755, 630, 0, 0, 0, 0, 0, - 0, 970, 0, 0, 0, 394, 5318, 0, 463, 681, - 662, 673, 663, 506, 648, 649, 650, 657, 406, 651, - 787, 653, 622, 654, 623, 655, 656, 1010, 680, 629, - 542, 482, 0, 697, 0, 0, 1092, 1101, 0, 0, - 0, 0, 0, 0, 0, 0, 1088, 0, 0, 0, - 0, 0, 961, 0, 0, 999, 1069, 1068, 986, 996, - 0, 0, 365, 269, 624, 751, 626, 625, 987, 0, - 988, 992, 995, 991, 989, 990, 0, 1083, 0, 0, - 0, 0, 0, 0, 953, 965, 0, 971, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 962, 963, 0, 0, 0, 0, 1020, - 0, 964, 0, 966, 0, 0, 0, 0, 543, 571, - 0, 584, 0, 432, 433, 1015, 993, 997, 0, 0, - 0, 0, 350, 550, 568, 366, 537, 582, 371, 545, - 561, 361, 499, 533, 0, 0, 352, 566, 544, 477, - 351, 0, 526, 391, 408, 388, 497, 994, 0, 1018, - 1022, 387, 1107, 1016, 576, 354, 0, 575, 496, 469, - 562, 567, 478, 471, 0, 353, 564, 476, 470, 446, - 398, 1108, 447, 448, 449, 450, 451, 452, 412, 512, - 467, 513, 413, 484, 483, 485, 414, 415, 416, 417, - 418, 419, 420, 421, 422, 423, 0, 0, 0, 0, - 0, 606, 607, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 743, 1013, - 0, 748, 0, 578, 0, 0, 1090, 0, 0, 0, - 548, 0, 0, 453, 0, 0, 0, 1017, 0, 529, - 502, 1104, 0, 0, 527, 465, 563, 514, 569, 551, - 577, 521, 515, 344, 552, 390, 479, 362, 364, 776, - 392, 395, 399, 400, 490, 491, 508, 536, 555, 556, - 557, 389, 376, 528, 377, 409, 378, 345, 381, 380, - 382, 538, 383, 347, 509, 560, 0, 405, 524, 474, - 348, 473, 510, 559, 558, 363, 586, 593, 594, 685, - 0, 599, 790, 791, 792, 608, 0, 516, 359, 356, - 0, 0, 0, 385, 511, 372, 374, 375, 373, 505, - 507, 613, 614, 615, 617, 0, 618, 619, 0, 0, - 0, 0, 620, 686, 704, 670, 639, 601, 694, 636, - 640, 641, 426, 427, 428, 429, 707, 0, 0, 0, - 592, 454, 455, 0, 397, 396, 475, 349, 0, 0, - 443, 425, 517, 355, 393, 445, 431, 456, 457, 458, - 459, 460, 0, 461, 462, 403, 339, 340, 784, 1087, - 498, 709, 746, 747, 631, 0, 1103, 1082, 1084, 1085, - 1089, 1093, 1094, 1095, 1096, 1097, 1099, 1102, 1106, 783, - 0, 687, 701, 788, 700, 780, 504, 0, 535, 698, - 645, 0, 691, 664, 665, 0, 692, 660, 696, 0, - 633, 0, 602, 605, 635, 722, 723, 724, 346, 604, - 726, 727, 728, 729, 730, 731, 732, 725, 1105, 668, - 644, 671, 583, 647, 646, 0, 0, 682, 1021, 683, - 684, 486, 487, 488, 489, 1091, 710, 370, 603, 519, - 520, 0, 669, 0, 0, 0, 0, 0, 0, 0, - 0, 674, 675, 672, 793, 0, 733, 734, 0, 0, - 597, 598, 402, 0, 616, 410, 369, 503, 404, 581, - 434, 0, 609, 676, 610, 522, 523, 737, 742, 738, - 739, 741, 762, 493, 424, 430, 539, 444, 466, 525, - 580, 501, 530, 367, 570, 541, 472, 661, 689, 1114, - 1086, 1113, 1115, 1116, 1112, 1117, 1118, 1098, 975, 0, - 1028, 1029, 1110, 1109, 1111, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 718, 717, 716, 715, - 714, 713, 712, 711, 0, 0, 658, 384, 781, 775, - 782, 763, 766, 765, 740, 982, 341, 638, 464, 518, - 401, 705, 706, 0, 761, 1076, 1039, 1040, 1041, 972, - 1042, 1036, 1037, 973, 1038, 1077, 1026, 1073, 1074, 1001, - 1031, 1043, 1072, 1044, 1075, 1002, 1078, 1119, 1120, 1050, - 1034, 302, 1121, 1047, 1079, 1071, 1070, 1045, 1027, 1080, - 1081, 1009, 1004, 1048, 1049, 1035, 1056, 1057, 1058, 1061, - 974, 1062, 1063, 1064, 1065, 1066, 1060, 1059, 1023, 1024, - 1025, 1051, 1052, 1054, 1055, 1030, 549, 1005, 1006, 1007, - 1008, 0, 0, 587, 588, 589, 612, 0, 590, 572, - 637, 411, 342, 553, 579, 778, 779, 0, 0, 0, - 0, 0, 0, 0, 688, 699, 735, 736, 0, 749, - 750, 752, 754, 1067, 756, 546, 547, 764, 0, 0, - 435, 357, 358, 494, 495, 532, 0, 436, 437, 438, - 442, 439, 440, 441, 1053, 759, 760, 757, 468, 531, - 554, 540, 777, 481, 0, 785, 627, 628, 786, 745, - 744, 343, 0, 1032, 1033, 702, 703, 1019, 0, 1100, - 480, 634, 0, 967, 0, 0, 500, 0, 0, 643, - 677, 666, 755, 630, 0, 0, 0, 0, 0, 0, - 970, 0, 0, 0, 394, 0, 0, 463, 681, 662, - 673, 663, 506, 648, 649, 650, 657, 406, 651, 787, - 653, 622, 654, 623, 655, 656, 1010, 680, 629, 542, - 482, 0, 697, 0, 0, 1092, 1101, 0, 0, 0, - 0, 0, 0, 0, 0, 1088, 0, 0, 0, 0, - 0, 961, 0, 0, 999, 1069, 1068, 986, 996, 0, - 0, 365, 269, 624, 751, 626, 625, 987, 0, 988, - 992, 995, 991, 989, 990, 0, 1083, 0, 0, 0, - 0, 0, 0, 953, 965, 0, 971, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 962, 963, 0, 0, 0, 0, 1020, 0, - 964, 0, 966, 0, 0, 0, 0, 543, 571, 0, - 584, 0, 432, 433, 1015, 993, 997, 0, 0, 0, - 0, 350, 550, 568, 366, 537, 582, 371, 545, 561, - 361, 499, 533, 0, 0, 352, 566, 544, 477, 351, - 0, 526, 391, 408, 388, 497, 994, 0, 1018, 1022, - 387, 1107, 1016, 576, 354, 0, 575, 496, 469, 562, - 567, 478, 471, 0, 353, 564, 476, 470, 446, 398, - 1108, 447, 448, 449, 450, 451, 452, 412, 512, 467, - 513, 413, 484, 483, 485, 414, 415, 416, 417, 418, - 419, 420, 421, 422, 423, 0, 0, 0, 0, 0, - 606, 607, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 743, 1013, 0, - 748, 0, 578, 0, 0, 1090, 0, 0, 0, 548, - 0, 0, 453, 0, 0, 0, 1017, 0, 529, 502, - 1104, 5142, 0, 527, 465, 563, 514, 569, 551, 577, - 521, 515, 344, 552, 390, 479, 362, 364, 776, 392, - 395, 399, 400, 490, 491, 508, 536, 555, 556, 557, - 389, 376, 528, 377, 409, 378, 345, 381, 380, 382, - 538, 383, 347, 509, 560, 0, 405, 524, 474, 348, - 473, 510, 559, 558, 363, 586, 593, 594, 685, 0, - 599, 790, 791, 792, 608, 0, 516, 359, 356, 0, - 0, 0, 385, 511, 372, 374, 375, 373, 505, 507, - 613, 614, 615, 617, 0, 618, 619, 0, 0, 0, - 0, 620, 686, 704, 670, 639, 601, 694, 636, 640, - 641, 426, 427, 428, 429, 707, 0, 0, 0, 592, - 454, 455, 0, 397, 396, 475, 349, 0, 0, 443, - 425, 517, 355, 393, 445, 431, 456, 457, 458, 459, - 460, 0, 461, 462, 403, 339, 340, 784, 1087, 498, - 709, 746, 747, 631, 0, 1103, 1082, 1084, 1085, 1089, - 1093, 1094, 1095, 1096, 1097, 1099, 1102, 1106, 783, 0, - 687, 701, 788, 700, 780, 504, 0, 535, 698, 645, - 0, 691, 664, 665, 0, 692, 660, 696, 0, 633, - 0, 602, 605, 635, 722, 723, 724, 346, 604, 726, - 727, 728, 729, 730, 731, 732, 725, 1105, 668, 644, - 671, 583, 647, 646, 0, 0, 682, 1021, 683, 684, - 486, 487, 488, 489, 1091, 710, 370, 603, 519, 520, - 0, 669, 0, 0, 0, 0, 0, 0, 0, 0, - 674, 675, 672, 793, 0, 733, 734, 0, 0, 597, - 598, 402, 0, 616, 410, 369, 503, 404, 581, 434, - 0, 609, 676, 610, 522, 523, 737, 742, 738, 739, - 741, 762, 493, 424, 430, 539, 444, 466, 525, 580, - 501, 530, 367, 570, 541, 472, 661, 689, 1114, 1086, - 1113, 1115, 1116, 1112, 1117, 1118, 1098, 975, 0, 1028, - 1029, 1110, 1109, 1111, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 718, 717, 716, 715, 714, - 713, 712, 711, 0, 0, 658, 384, 781, 775, 782, - 763, 766, 765, 740, 982, 341, 638, 464, 518, 401, - 705, 706, 0, 761, 1076, 1039, 1040, 1041, 972, 1042, - 1036, 1037, 973, 1038, 1077, 1026, 1073, 1074, 1001, 1031, - 1043, 1072, 1044, 1075, 1002, 1078, 1119, 1120, 1050, 1034, - 302, 1121, 1047, 1079, 1071, 1070, 1045, 1027, 1080, 1081, - 1009, 1004, 1048, 1049, 1035, 1056, 1057, 1058, 1061, 974, - 1062, 1063, 1064, 1065, 1066, 1060, 1059, 1023, 1024, 1025, - 1051, 1052, 1054, 1055, 1030, 549, 1005, 1006, 1007, 1008, - 0, 0, 587, 588, 589, 612, 0, 590, 572, 637, - 411, 342, 553, 579, 778, 779, 0, 0, 0, 0, - 0, 0, 0, 688, 699, 735, 736, 0, 749, 750, - 752, 754, 1067, 756, 546, 547, 764, 0, 0, 435, - 357, 358, 494, 495, 532, 0, 436, 437, 438, 442, - 439, 440, 441, 1053, 759, 760, 757, 468, 531, 554, - 540, 777, 481, 0, 785, 627, 628, 786, 745, 744, - 343, 0, 1032, 1033, 702, 703, 1019, 0, 1100, 480, - 634, 0, 967, 0, 0, 500, 0, 0, 643, 677, - 666, 755, 630, 0, 0, 0, 0, 0, 0, 970, - 0, 0, 0, 394, 0, 0, 463, 681, 662, 673, - 663, 506, 648, 649, 650, 657, 406, 651, 787, 653, - 622, 654, 623, 655, 656, 1010, 680, 629, 542, 482, - 0, 697, 0, 0, 1092, 1101, 0, 0, 0, 0, - 0, 0, 0, 0, 1088, 0, 0, 0, 0, 0, - 961, 0, 0, 999, 1069, 1068, 986, 996, 0, 0, - 365, 269, 624, 751, 626, 625, 987, 0, 988, 992, - 995, 991, 989, 990, 0, 1083, 0, 0, 0, 0, - 0, 0, 953, 965, 0, 971, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 962, 963, 1917, 0, 0, 0, 1020, 0, 964, - 0, 966, 0, 0, 0, 0, 543, 571, 0, 584, - 0, 432, 433, 1015, 993, 997, 0, 0, 0, 0, - 350, 550, 568, 366, 537, 582, 371, 545, 561, 361, - 499, 533, 0, 0, 352, 566, 544, 477, 351, 0, - 526, 391, 408, 388, 497, 994, 0, 1018, 1022, 387, - 1107, 1016, 576, 354, 0, 575, 496, 469, 562, 567, - 478, 471, 0, 353, 564, 476, 470, 446, 398, 1108, - 447, 448, 449, 450, 451, 452, 412, 512, 467, 513, - 413, 484, 483, 485, 414, 415, 416, 417, 418, 419, - 420, 421, 422, 423, 0, 0, 0, 0, 0, 606, - 607, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 743, 1013, 0, 748, - 0, 578, 0, 0, 1090, 0, 0, 0, 548, 0, - 0, 453, 0, 0, 0, 1017, 0, 529, 502, 1104, - 0, 0, 527, 465, 563, 514, 569, 551, 577, 521, - 515, 344, 552, 390, 479, 362, 364, 776, 392, 395, - 399, 400, 490, 491, 508, 536, 555, 556, 557, 389, - 376, 528, 377, 409, 378, 345, 381, 380, 382, 538, - 383, 347, 509, 560, 0, 405, 524, 474, 348, 473, - 510, 559, 558, 363, 586, 593, 594, 685, 0, 599, - 790, 791, 792, 608, 0, 516, 359, 356, 0, 0, - 0, 385, 511, 372, 374, 375, 373, 505, 507, 613, - 614, 615, 617, 0, 618, 619, 0, 0, 0, 0, - 620, 686, 704, 670, 639, 601, 694, 636, 640, 641, - 426, 427, 428, 429, 707, 0, 0, 0, 592, 454, - 455, 0, 397, 396, 475, 349, 0, 0, 443, 425, - 517, 355, 393, 445, 431, 456, 457, 458, 459, 460, - 0, 461, 462, 403, 339, 340, 784, 1087, 498, 709, - 746, 747, 631, 0, 1103, 1082, 1084, 1085, 1089, 1093, - 1094, 1095, 1096, 1097, 1099, 1102, 1106, 783, 0, 687, - 701, 788, 700, 780, 504, 0, 535, 698, 645, 0, - 691, 664, 665, 0, 692, 660, 696, 0, 633, 0, - 602, 605, 635, 722, 723, 724, 346, 604, 726, 727, - 728, 729, 730, 731, 732, 725, 1105, 668, 644, 671, - 583, 647, 646, 0, 0, 682, 1021, 683, 684, 486, - 487, 488, 489, 1091, 710, 370, 603, 519, 520, 0, - 669, 0, 0, 0, 0, 0, 0, 0, 0, 674, - 675, 672, 793, 0, 733, 734, 0, 0, 597, 598, - 402, 0, 616, 410, 369, 503, 404, 581, 434, 0, - 609, 676, 610, 522, 523, 737, 742, 738, 739, 741, - 762, 493, 424, 430, 539, 444, 466, 525, 580, 501, - 530, 367, 570, 541, 472, 661, 689, 1114, 1086, 1113, - 1115, 1116, 1112, 1117, 1118, 1098, 975, 0, 1028, 1029, - 1110, 1109, 1111, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 718, 717, 716, 715, 714, 713, - 712, 711, 0, 0, 658, 384, 781, 775, 782, 763, - 766, 765, 740, 982, 341, 638, 464, 518, 401, 705, - 706, 0, 761, 1076, 1039, 1040, 1041, 972, 1042, 1036, - 1037, 973, 1038, 1077, 1026, 1073, 1074, 1001, 1031, 1043, - 1072, 1044, 1075, 1002, 1078, 1119, 1120, 1050, 1034, 302, - 1121, 1047, 1079, 1071, 1070, 1045, 1027, 1080, 1081, 1009, - 1004, 1048, 1049, 1035, 1056, 1057, 1058, 1061, 974, 1062, - 1063, 1064, 1065, 1066, 1060, 1059, 1023, 1024, 1025, 1051, - 1052, 1054, 1055, 1030, 549, 1005, 1006, 1007, 1008, 0, - 0, 587, 588, 589, 612, 0, 590, 572, 637, 411, - 342, 553, 579, 778, 779, 0, 0, 0, 0, 0, - 0, 0, 688, 699, 735, 736, 0, 749, 750, 752, - 754, 1067, 756, 546, 547, 764, 0, 0, 435, 357, - 358, 494, 495, 532, 0, 436, 437, 438, 442, 439, - 440, 441, 1053, 759, 760, 757, 468, 531, 554, 540, - 777, 481, 0, 785, 627, 628, 786, 745, 744, 343, - 0, 1032, 1033, 702, 703, 1019, 0, 1100, 480, 634, - 0, 967, 0, 0, 500, 0, 0, 643, 677, 666, - 755, 630, 0, 0, 0, 0, 0, 0, 970, 0, - 0, 0, 394, 2319, 0, 463, 681, 662, 673, 663, - 506, 648, 649, 650, 657, 406, 651, 787, 653, 622, - 654, 623, 655, 656, 1010, 680, 629, 542, 482, 0, - 697, 0, 0, 1092, 1101, 0, 0, 0, 0, 0, - 0, 0, 0, 1088, 0, 0, 0, 0, 0, 961, - 0, 0, 999, 1069, 1068, 986, 996, 0, 0, 365, - 269, 624, 751, 626, 625, 987, 0, 988, 992, 995, - 991, 989, 990, 0, 1083, 0, 0, 0, 0, 0, - 0, 953, 965, 0, 971, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 962, 963, 0, 0, 0, 0, 1020, 0, 964, 0, - 966, 0, 0, 0, 0, 543, 571, 0, 584, 0, - 432, 433, 1015, 993, 997, 0, 0, 0, 0, 350, - 550, 568, 366, 537, 582, 371, 545, 561, 361, 499, - 533, 0, 0, 352, 566, 544, 477, 351, 0, 526, - 391, 408, 388, 497, 994, 0, 1018, 1022, 387, 1107, - 1016, 576, 354, 0, 575, 496, 469, 562, 567, 478, - 471, 0, 353, 564, 476, 470, 446, 398, 1108, 447, - 448, 449, 450, 451, 452, 412, 512, 467, 513, 413, - 484, 483, 485, 414, 415, 416, 417, 418, 419, 420, - 421, 422, 423, 0, 0, 0, 0, 0, 606, 607, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 743, 1013, 0, 748, 0, - 578, 0, 0, 1090, 0, 0, 0, 548, 0, 0, - 453, 0, 0, 0, 1017, 0, 529, 502, 1104, 0, - 0, 527, 465, 563, 514, 569, 551, 577, 521, 515, - 344, 552, 390, 479, 362, 364, 776, 392, 395, 399, - 400, 490, 491, 508, 536, 555, 556, 557, 389, 376, - 528, 377, 409, 378, 345, 381, 380, 382, 538, 383, - 347, 509, 560, 0, 405, 524, 474, 348, 473, 510, - 559, 558, 363, 586, 593, 594, 685, 0, 599, 790, - 791, 792, 608, 0, 516, 359, 356, 0, 0, 0, - 385, 511, 372, 374, 375, 373, 505, 507, 613, 614, - 615, 617, 0, 618, 619, 0, 0, 0, 0, 620, - 686, 704, 670, 639, 601, 694, 636, 640, 641, 426, - 427, 428, 429, 707, 0, 0, 0, 592, 454, 455, - 0, 397, 396, 475, 349, 0, 0, 443, 425, 517, - 355, 393, 445, 431, 456, 457, 458, 459, 460, 0, - 461, 462, 403, 339, 340, 784, 1087, 498, 709, 746, - 747, 631, 0, 1103, 1082, 1084, 1085, 1089, 1093, 1094, - 1095, 1096, 1097, 1099, 1102, 1106, 783, 0, 687, 701, - 788, 700, 780, 504, 0, 535, 698, 645, 0, 691, - 664, 665, 0, 692, 660, 696, 0, 633, 0, 602, - 605, 635, 722, 723, 724, 346, 604, 726, 727, 728, - 729, 730, 731, 732, 725, 1105, 668, 644, 671, 583, - 647, 646, 0, 0, 682, 1021, 683, 684, 486, 487, - 488, 489, 1091, 710, 370, 603, 519, 520, 0, 669, - 0, 0, 0, 0, 0, 0, 0, 0, 674, 675, - 672, 793, 0, 733, 734, 0, 0, 597, 598, 402, - 0, 616, 410, 369, 503, 404, 581, 434, 0, 609, - 676, 610, 522, 523, 737, 742, 738, 739, 741, 762, - 493, 424, 430, 539, 444, 466, 525, 580, 501, 530, - 367, 570, 541, 472, 661, 689, 1114, 1086, 1113, 1115, - 1116, 1112, 1117, 1118, 1098, 975, 0, 1028, 1029, 1110, - 1109, 1111, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 718, 717, 716, 715, 714, 713, 712, - 711, 0, 0, 658, 384, 781, 775, 782, 763, 766, - 765, 740, 982, 341, 638, 464, 518, 401, 705, 706, - 0, 761, 1076, 1039, 1040, 1041, 972, 1042, 1036, 1037, - 973, 1038, 1077, 1026, 1073, 1074, 1001, 1031, 1043, 1072, - 1044, 1075, 1002, 1078, 1119, 1120, 1050, 1034, 302, 1121, - 1047, 1079, 1071, 1070, 1045, 1027, 1080, 1081, 1009, 1004, - 1048, 1049, 1035, 1056, 1057, 1058, 1061, 974, 1062, 1063, - 1064, 1065, 1066, 1060, 1059, 1023, 1024, 1025, 1051, 1052, - 1054, 1055, 1030, 549, 1005, 1006, 1007, 1008, 0, 0, - 587, 588, 589, 612, 0, 590, 572, 637, 411, 342, - 553, 579, 778, 779, 0, 0, 0, 0, 0, 0, - 0, 688, 699, 735, 736, 0, 749, 750, 752, 754, - 1067, 756, 546, 547, 764, 0, 0, 435, 357, 358, - 494, 495, 532, 0, 436, 437, 438, 442, 439, 440, - 441, 1053, 759, 760, 757, 468, 531, 554, 540, 777, - 481, 0, 785, 627, 628, 786, 745, 744, 343, 0, - 1032, 1033, 702, 703, 0, 0, 1100, 480, 634, 1019, - 967, 0, 2809, 0, 0, 0, 0, 0, 500, 0, - 0, 643, 677, 666, 755, 630, 0, 0, 0, 0, - 0, 0, 970, 0, 0, 0, 394, 0, 0, 463, - 681, 662, 673, 663, 506, 648, 649, 650, 657, 406, - 651, 787, 653, 622, 654, 623, 655, 656, 1010, 680, - 629, 542, 482, 0, 697, 0, 0, 1092, 1101, 0, - 0, 0, 0, 0, 0, 0, 0, 1088, 0, 0, - 0, 0, 0, 961, 0, 0, 999, 1069, 1068, 986, - 996, 0, 0, 365, 269, 624, 751, 626, 625, 987, - 0, 988, 992, 995, 991, 989, 990, 0, 1083, 0, - 0, 0, 0, 0, 0, 953, 965, 0, 971, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 962, 963, 0, 0, 0, 0, - 1020, 0, 964, 0, 966, 0, 0, 0, 0, 543, - 571, 0, 584, 0, 432, 433, 1015, 993, 997, 0, - 0, 0, 0, 350, 550, 568, 366, 537, 582, 371, - 545, 561, 361, 499, 533, 0, 0, 352, 566, 544, - 477, 351, 0, 526, 391, 408, 388, 497, 994, 0, - 1018, 1022, 387, 1107, 1016, 576, 354, 0, 575, 496, - 469, 562, 567, 478, 471, 0, 353, 564, 476, 470, - 446, 398, 1108, 447, 448, 449, 450, 451, 452, 412, - 512, 467, 513, 413, 484, 483, 485, 414, 415, 416, - 417, 418, 419, 420, 421, 422, 423, 0, 0, 0, - 0, 0, 606, 607, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 743, - 1013, 0, 748, 0, 578, 0, 0, 1090, 0, 0, - 0, 548, 0, 0, 453, 0, 0, 0, 1017, 0, - 529, 502, 1104, 0, 0, 527, 465, 563, 514, 569, - 551, 577, 521, 515, 344, 552, 390, 479, 362, 364, - 776, 392, 395, 399, 400, 490, 491, 508, 536, 555, - 556, 557, 389, 376, 528, 377, 409, 378, 345, 381, - 380, 382, 538, 383, 347, 509, 560, 0, 405, 524, - 474, 348, 473, 510, 559, 558, 363, 586, 593, 594, - 685, 0, 599, 790, 791, 792, 608, 0, 516, 359, - 356, 0, 0, 0, 385, 511, 372, 374, 375, 373, - 505, 507, 613, 614, 615, 617, 0, 618, 619, 0, - 0, 0, 0, 620, 686, 704, 670, 639, 601, 694, - 636, 640, 641, 426, 427, 428, 429, 707, 0, 0, - 0, 592, 454, 455, 0, 397, 396, 475, 349, 0, - 0, 443, 425, 517, 355, 393, 445, 431, 456, 457, - 458, 459, 460, 0, 461, 462, 403, 339, 340, 784, - 1087, 498, 709, 746, 747, 631, 0, 1103, 1082, 1084, - 1085, 1089, 1093, 1094, 1095, 1096, 1097, 1099, 1102, 1106, - 783, 0, 687, 701, 788, 700, 780, 504, 0, 535, - 698, 645, 0, 691, 664, 665, 0, 692, 660, 696, - 0, 633, 0, 602, 605, 635, 722, 723, 724, 346, - 604, 726, 727, 728, 729, 730, 731, 732, 725, 1105, - 668, 644, 671, 583, 647, 646, 0, 0, 682, 1021, - 683, 684, 486, 487, 488, 489, 1091, 710, 370, 603, - 519, 520, 0, 669, 0, 0, 0, 0, 0, 0, - 0, 0, 674, 675, 672, 793, 0, 733, 734, 0, - 0, 597, 598, 402, 0, 616, 410, 369, 503, 404, - 581, 434, 0, 609, 676, 610, 522, 523, 737, 742, - 738, 739, 741, 762, 493, 424, 430, 539, 444, 466, - 525, 580, 501, 530, 367, 570, 541, 472, 661, 689, - 1114, 1086, 1113, 1115, 1116, 1112, 1117, 1118, 1098, 975, - 0, 1028, 1029, 1110, 1109, 1111, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 718, 717, 716, - 715, 714, 713, 712, 711, 0, 0, 658, 384, 781, - 775, 782, 763, 766, 765, 740, 982, 341, 638, 464, - 518, 401, 705, 706, 0, 761, 1076, 1039, 1040, 1041, - 972, 1042, 1036, 1037, 973, 1038, 1077, 1026, 1073, 1074, - 1001, 1031, 1043, 1072, 1044, 1075, 1002, 1078, 1119, 1120, - 1050, 1034, 302, 1121, 1047, 1079, 1071, 1070, 1045, 1027, - 1080, 1081, 1009, 1004, 1048, 1049, 1035, 1056, 1057, 1058, - 1061, 974, 1062, 1063, 1064, 1065, 1066, 1060, 1059, 1023, - 1024, 1025, 1051, 1052, 1054, 1055, 1030, 549, 1005, 1006, - 1007, 1008, 0, 0, 587, 588, 589, 612, 0, 590, - 572, 637, 411, 342, 553, 579, 778, 779, 0, 0, - 0, 0, 0, 0, 0, 688, 699, 735, 736, 0, - 749, 750, 752, 754, 1067, 756, 546, 547, 764, 0, - 0, 435, 357, 358, 494, 495, 532, 0, 436, 437, - 438, 442, 439, 440, 441, 1053, 759, 760, 757, 468, - 531, 554, 540, 777, 481, 0, 785, 627, 628, 786, - 745, 744, 343, 0, 1032, 1033, 702, 703, 1019, 0, - 1100, 480, 634, 0, 967, 0, 0, 500, 0, 0, - 643, 677, 666, 755, 630, 0, 0, 0, 0, 0, - 0, 970, 0, 0, 0, 394, 0, 0, 463, 681, - 662, 673, 663, 506, 648, 649, 650, 657, 406, 651, - 787, 653, 622, 654, 623, 655, 656, 1010, 680, 629, - 542, 482, 0, 697, 0, 0, 1092, 1101, 0, 0, - 0, 0, 0, 0, 0, 0, 1088, 0, 0, 0, - 0, 0, 961, 0, 0, 999, 1069, 1068, 986, 996, - 0, 0, 365, 269, 624, 751, 626, 625, 987, 0, - 988, 992, 995, 991, 989, 990, 0, 1083, 0, 0, - 0, 0, 0, 0, 953, 965, 0, 971, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 962, 963, 2310, 0, 0, 0, 1020, - 0, 964, 0, 966, 0, 0, 0, 0, 543, 571, - 0, 584, 0, 432, 433, 1015, 993, 997, 0, 0, - 0, 0, 350, 550, 568, 366, 537, 582, 371, 545, - 561, 361, 499, 533, 0, 0, 352, 566, 544, 477, - 351, 0, 526, 391, 408, 388, 497, 994, 0, 1018, - 1022, 387, 1107, 1016, 576, 354, 0, 575, 496, 469, - 562, 567, 478, 471, 0, 353, 564, 476, 470, 446, - 398, 1108, 447, 448, 449, 450, 451, 452, 412, 512, - 467, 513, 413, 484, 483, 485, 414, 415, 416, 417, - 418, 419, 420, 421, 422, 423, 0, 0, 0, 0, - 0, 606, 607, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 743, 1013, - 0, 748, 0, 578, 0, 0, 1090, 0, 0, 0, - 548, 0, 0, 453, 0, 0, 0, 1017, 0, 529, - 502, 1104, 0, 0, 527, 465, 563, 514, 569, 551, - 577, 521, 515, 344, 552, 390, 479, 362, 364, 776, - 392, 395, 399, 400, 490, 491, 508, 536, 555, 556, - 557, 389, 376, 528, 377, 409, 378, 345, 381, 380, - 382, 538, 383, 347, 509, 560, 0, 405, 524, 474, - 348, 473, 510, 559, 558, 363, 586, 593, 594, 685, - 0, 599, 790, 791, 792, 608, 0, 516, 359, 356, - 0, 0, 0, 385, 511, 372, 374, 375, 373, 505, - 507, 613, 614, 615, 617, 0, 618, 619, 0, 0, - 0, 0, 620, 686, 704, 670, 639, 601, 694, 636, - 640, 641, 426, 427, 428, 429, 707, 0, 0, 0, - 592, 454, 455, 0, 397, 396, 475, 349, 0, 0, - 443, 425, 517, 355, 393, 445, 431, 456, 457, 458, - 459, 460, 0, 461, 462, 403, 339, 340, 784, 1087, - 498, 709, 746, 747, 631, 0, 1103, 1082, 1084, 1085, - 1089, 1093, 1094, 1095, 1096, 1097, 1099, 1102, 1106, 783, - 0, 687, 701, 788, 700, 780, 504, 0, 535, 698, - 645, 0, 691, 664, 665, 0, 692, 660, 696, 0, - 633, 0, 602, 605, 635, 722, 723, 724, 346, 604, - 726, 727, 728, 729, 730, 731, 732, 725, 1105, 668, - 644, 671, 583, 647, 646, 0, 0, 682, 1021, 683, - 684, 486, 487, 488, 489, 1091, 710, 370, 603, 519, - 520, 0, 669, 0, 0, 0, 0, 0, 0, 0, - 0, 674, 675, 672, 793, 0, 733, 734, 0, 0, - 597, 598, 402, 0, 616, 410, 369, 503, 404, 581, - 434, 0, 609, 676, 610, 522, 523, 737, 742, 738, - 739, 741, 762, 493, 424, 430, 539, 444, 466, 525, - 580, 501, 530, 367, 570, 541, 472, 661, 689, 1114, - 1086, 1113, 1115, 1116, 1112, 1117, 1118, 1098, 975, 0, - 1028, 1029, 1110, 1109, 1111, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 718, 717, 716, 715, - 714, 713, 712, 711, 0, 0, 658, 384, 781, 775, - 782, 763, 766, 765, 740, 982, 341, 638, 464, 518, - 401, 705, 706, 0, 761, 1076, 1039, 1040, 1041, 972, - 1042, 1036, 1037, 973, 1038, 1077, 1026, 1073, 1074, 1001, - 1031, 1043, 1072, 1044, 1075, 1002, 1078, 1119, 1120, 1050, - 1034, 302, 1121, 1047, 1079, 1071, 1070, 1045, 1027, 1080, - 1081, 1009, 1004, 1048, 1049, 1035, 1056, 1057, 1058, 1061, - 974, 1062, 1063, 1064, 1065, 1066, 1060, 1059, 1023, 1024, - 1025, 1051, 1052, 1054, 1055, 1030, 549, 1005, 1006, 1007, - 1008, 0, 0, 587, 588, 589, 612, 0, 590, 572, - 637, 411, 342, 553, 579, 778, 779, 0, 0, 0, - 0, 0, 0, 0, 688, 699, 735, 736, 0, 749, - 750, 752, 754, 1067, 756, 546, 547, 764, 0, 0, - 435, 357, 358, 494, 495, 532, 0, 436, 437, 438, - 442, 439, 440, 441, 1053, 759, 760, 757, 468, 531, - 554, 540, 777, 481, 0, 785, 627, 628, 786, 745, - 744, 343, 0, 1032, 1033, 702, 703, 1019, 0, 1100, - 480, 634, 0, 967, 0, 0, 500, 0, 0, 643, - 677, 666, 755, 630, 0, 0, 0, 0, 0, 0, - 970, 0, 0, 0, 394, 0, 0, 463, 681, 662, - 673, 663, 506, 648, 649, 650, 657, 406, 651, 787, - 653, 622, 654, 623, 655, 656, 1010, 680, 629, 542, - 482, 0, 697, 0, 0, 1092, 1101, 0, 0, 0, - 0, 0, 0, 0, 0, 1088, 0, 0, 0, 0, - 0, 961, 0, 0, 999, 1069, 1068, 986, 996, 0, - 0, 365, 269, 624, 751, 626, 625, 987, 0, 988, - 992, 995, 991, 989, 990, 0, 1083, 0, 0, 0, - 0, 0, 0, 953, 965, 0, 971, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 962, 963, 0, 0, 0, 0, 1020, 0, - 964, 0, 966, 0, 0, 0, 0, 543, 571, 0, - 584, 0, 432, 433, 1015, 993, 997, 0, 0, 0, - 0, 350, 550, 568, 366, 537, 582, 371, 545, 561, - 361, 499, 533, 0, 0, 352, 566, 544, 477, 351, - 0, 526, 391, 408, 388, 497, 994, 0, 1018, 1022, - 387, 1107, 1016, 576, 354, 0, 575, 496, 469, 562, - 567, 478, 471, 0, 353, 564, 476, 470, 446, 398, - 1108, 447, 448, 449, 450, 451, 452, 412, 512, 467, - 513, 413, 484, 483, 485, 414, 415, 416, 417, 418, - 419, 420, 421, 422, 423, 0, 0, 0, 0, 0, - 606, 607, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 743, 1013, 0, - 748, 0, 578, 0, 0, 1090, 0, 0, 0, 548, - 0, 0, 453, 0, 0, 0, 1017, 0, 529, 502, - 1104, 0, 0, 527, 465, 563, 514, 569, 551, 577, - 521, 515, 344, 552, 390, 479, 362, 364, 776, 392, - 395, 399, 400, 490, 491, 508, 536, 555, 556, 557, - 389, 376, 528, 377, 409, 378, 345, 381, 380, 382, - 538, 383, 347, 509, 560, 0, 405, 524, 474, 348, - 473, 510, 559, 558, 363, 586, 593, 594, 685, 0, - 599, 790, 791, 792, 608, 0, 516, 359, 356, 0, - 0, 0, 385, 511, 372, 374, 375, 373, 505, 507, - 613, 614, 615, 617, 0, 618, 619, 0, 0, 0, - 0, 620, 686, 704, 670, 639, 601, 694, 636, 640, - 641, 426, 427, 428, 429, 707, 0, 0, 0, 592, - 454, 455, 0, 397, 396, 475, 349, 0, 0, 443, - 425, 517, 355, 393, 445, 431, 456, 457, 458, 459, - 460, 0, 461, 462, 403, 339, 340, 784, 1087, 498, - 709, 746, 747, 631, 0, 1103, 1082, 1084, 1085, 1089, - 1093, 1094, 1095, 1096, 1097, 1099, 1102, 1106, 783, 0, - 687, 701, 788, 700, 780, 504, 0, 535, 698, 645, - 0, 691, 664, 665, 0, 692, 660, 696, 0, 633, - 0, 602, 605, 635, 722, 723, 724, 346, 604, 726, - 727, 728, 729, 730, 731, 732, 725, 1105, 668, 644, - 671, 583, 647, 646, 0, 0, 682, 1021, 683, 684, - 486, 487, 488, 489, 1091, 710, 370, 603, 519, 520, - 0, 669, 0, 0, 0, 0, 0, 0, 0, 0, - 674, 675, 672, 793, 0, 733, 734, 0, 0, 597, - 598, 402, 0, 616, 410, 369, 503, 404, 581, 434, - 0, 609, 676, 610, 522, 523, 737, 742, 738, 739, - 741, 762, 493, 424, 430, 539, 444, 466, 525, 580, - 501, 530, 367, 570, 541, 472, 661, 689, 1114, 1086, - 1113, 1115, 1116, 1112, 1117, 1118, 1098, 975, 0, 1028, - 1029, 1110, 1109, 1111, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 718, 717, 716, 715, 714, - 713, 712, 711, 0, 0, 658, 384, 781, 775, 782, - 763, 766, 765, 740, 982, 341, 638, 464, 518, 401, - 705, 706, 0, 761, 1076, 1039, 1040, 1041, 972, 1042, - 1036, 1037, 973, 1038, 1077, 1026, 1073, 1074, 1001, 1031, - 1043, 1072, 1044, 1075, 1002, 1078, 1119, 1120, 1050, 1034, - 302, 1121, 1047, 1079, 1071, 1070, 1045, 1027, 1080, 1081, - 1009, 1004, 1048, 1049, 1035, 1056, 1057, 1058, 1061, 974, - 1062, 1063, 1064, 1065, 1066, 1060, 1059, 1023, 1024, 1025, - 1051, 1052, 1054, 1055, 1030, 549, 1005, 1006, 1007, 1008, - 0, 0, 587, 588, 589, 612, 0, 590, 572, 637, - 411, 342, 553, 579, 778, 779, 0, 0, 0, 0, - 0, 0, 0, 688, 699, 735, 736, 0, 749, 750, - 752, 754, 1067, 756, 546, 547, 764, 0, 0, 435, - 357, 358, 494, 495, 532, 0, 436, 437, 438, 442, - 439, 440, 441, 1053, 759, 760, 757, 468, 531, 554, - 540, 777, 481, 0, 785, 627, 628, 786, 745, 744, - 343, 0, 1032, 1033, 702, 703, 1019, 0, 1100, 480, - 634, 0, 967, 0, 0, 500, 0, 0, 643, 677, - 666, 755, 630, 0, 0, 0, 0, 0, 0, 970, - 0, 0, 0, 394, 0, 0, 463, 681, 662, 673, - 663, 506, 648, 649, 650, 657, 406, 651, 787, 653, - 622, 654, 623, 655, 656, 1010, 680, 629, 542, 482, - 0, 697, 0, 0, 1092, 1101, 0, 0, 0, 0, - 0, 0, 0, 0, 1088, 0, 0, 0, 0, 0, - 961, 0, 0, 999, 1069, 1068, 986, 996, 0, 0, - 365, 269, 624, 751, 626, 625, 987, 0, 988, 992, - 995, 991, 989, 990, 0, 1083, 0, 0, 0, 0, - 0, 0, 953, 965, 0, 971, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 962, 963, 0, 0, 0, 0, 1020, 0, 964, - 0, 966, 0, 0, 0, 0, 543, 571, 0, 584, - 0, 432, 433, 1015, 993, 997, 0, 0, 0, 0, - 350, 550, 568, 366, 537, 582, 371, 545, 561, 361, - 499, 533, 0, 0, 352, 566, 544, 477, 351, 0, - 526, 391, 408, 388, 497, 994, 0, 1018, 1022, 387, - 1107, 1016, 576, 354, 0, 575, 496, 469, 562, 567, - 478, 471, 0, 353, 564, 476, 470, 446, 398, 1108, - 447, 448, 449, 450, 451, 452, 412, 512, 467, 513, - 413, 484, 483, 485, 414, 415, 416, 417, 418, 419, - 420, 421, 422, 423, 0, 0, 0, 0, 0, 606, - 607, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 743, 1013, 0, 748, - 0, 578, 0, 0, 1090, 0, 0, 0, 548, 0, - 0, 453, 0, 0, 0, 1017, 0, 529, 502, 1104, - 0, 0, 527, 465, 563, 514, 569, 551, 577, 521, - 515, 344, 552, 390, 479, 362, 364, 776, 392, 395, - 399, 400, 490, 491, 508, 536, 555, 556, 557, 389, - 376, 528, 377, 409, 378, 345, 381, 380, 382, 538, - 383, 347, 509, 560, 0, 405, 524, 474, 348, 473, - 510, 559, 558, 363, 586, 593, 594, 685, 0, 599, - 790, 791, 792, 608, 0, 516, 359, 356, 0, 0, - 0, 385, 511, 372, 374, 375, 373, 505, 507, 613, - 614, 615, 617, 0, 618, 619, 0, 0, 0, 0, - 620, 686, 704, 670, 639, 601, 694, 636, 640, 641, - 426, 427, 428, 429, 707, 0, 0, 0, 592, 454, - 455, 0, 397, 396, 475, 349, 0, 0, 443, 425, - 517, 355, 393, 445, 431, 456, 457, 458, 459, 460, - 0, 461, 462, 403, 339, 340, 784, 1087, 498, 709, - 746, 747, 631, 0, 1103, 1082, 1084, 1085, 1089, 1093, - 1094, 1095, 1096, 1097, 1099, 1102, 1106, 783, 0, 687, - 701, 788, 700, 780, 504, 0, 535, 698, 645, 0, - 691, 664, 665, 0, 692, 660, 696, 0, 633, 0, - 602, 605, 635, 722, 723, 724, 346, 604, 726, 727, - 728, 729, 730, 731, 732, 725, 1105, 668, 644, 671, - 583, 647, 646, 0, 0, 682, 1021, 683, 684, 486, - 487, 488, 489, 1091, 710, 370, 603, 519, 520, 0, - 669, 0, 0, 0, 0, 0, 0, 0, 0, 674, - 675, 672, 793, 0, 733, 734, 0, 0, 597, 598, - 402, 0, 616, 410, 369, 503, 404, 581, 434, 0, - 609, 676, 610, 522, 523, 737, 742, 738, 739, 741, - 762, 493, 424, 430, 539, 444, 466, 525, 580, 501, - 530, 367, 570, 541, 472, 661, 689, 1114, 1086, 1113, - 1115, 1116, 1112, 1117, 1118, 1098, 975, 0, 1028, 1029, - 1110, 1109, 1111, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 718, 717, 716, 715, 714, 713, - 712, 711, 0, 0, 658, 384, 781, 775, 782, 763, - 766, 765, 740, 982, 341, 638, 464, 518, 401, 705, - 706, 0, 761, 1076, 1039, 1040, 1041, 972, 1042, 1036, - 1037, 973, 1038, 1077, 1026, 1073, 1074, 1001, 1031, 1043, - 1072, 1044, 1075, 1002, 1078, 1119, 1120, 1050, 1034, 302, - 1121, 1047, 1079, 1071, 1070, 1045, 1027, 1080, 1081, 1009, - 1004, 1048, 1049, 1035, 1056, 1057, 1058, 1061, 974, 1062, - 1063, 1064, 1065, 1066, 1060, 1059, 1023, 1024, 1025, 1051, - 1052, 1054, 1055, 1030, 549, 1005, 1006, 1007, 1008, 0, - 0, 587, 588, 589, 612, 0, 590, 572, 637, 411, - 342, 553, 579, 778, 779, 0, 0, 0, 0, 0, - 0, 0, 688, 699, 735, 736, 0, 749, 750, 752, - 754, 1067, 756, 546, 547, 764, 0, 0, 435, 357, - 358, 494, 495, 532, 0, 436, 437, 438, 442, 439, - 440, 441, 4753, 759, 4754, 4755, 468, 531, 554, 540, - 777, 481, 0, 785, 627, 628, 786, 745, 744, 343, - 0, 1032, 1033, 702, 703, 1019, 0, 1100, 480, 634, - 0, 967, 0, 0, 500, 0, 0, 643, 677, 666, - 755, 630, 0, 0, 0, 0, 0, 0, 970, 0, - 0, 0, 394, 0, 0, 463, 681, 662, 673, 663, - 506, 648, 649, 650, 657, 406, 651, 787, 653, 622, - 654, 623, 655, 656, 1010, 680, 629, 542, 482, 0, - 697, 0, 0, 1092, 1101, 0, 0, 0, 0, 0, - 0, 0, 0, 1088, 0, 0, 0, 0, 0, 961, - 0, 0, 999, 1069, 1068, 986, 996, 0, 0, 365, - 269, 624, 751, 626, 625, 3375, 0, 3376, 992, 995, - 991, 989, 990, 0, 1083, 0, 0, 0, 0, 0, - 0, 953, 965, 0, 971, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 962, 963, 0, 0, 0, 0, 1020, 0, 964, 0, - 966, 0, 0, 0, 0, 543, 571, 0, 584, 0, - 432, 433, 1015, 993, 997, 0, 0, 0, 0, 350, - 550, 568, 366, 537, 582, 371, 545, 561, 361, 499, - 533, 0, 0, 352, 566, 544, 477, 351, 0, 526, - 391, 408, 388, 497, 994, 0, 1018, 1022, 387, 1107, - 1016, 576, 354, 0, 575, 496, 469, 562, 567, 478, - 471, 0, 353, 564, 476, 470, 446, 398, 1108, 447, - 448, 449, 450, 451, 452, 412, 512, 467, 513, 413, - 484, 483, 485, 414, 415, 416, 417, 418, 419, 420, - 421, 422, 423, 0, 0, 0, 0, 0, 606, 607, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 743, 1013, 0, 748, 0, - 578, 0, 0, 1090, 0, 0, 0, 548, 0, 0, - 453, 0, 0, 0, 1017, 0, 529, 502, 1104, 0, - 0, 527, 465, 563, 514, 569, 551, 577, 521, 515, - 344, 552, 390, 479, 362, 364, 776, 392, 395, 399, - 400, 490, 491, 508, 536, 555, 556, 557, 389, 376, - 528, 377, 409, 378, 345, 381, 380, 382, 538, 383, - 347, 509, 560, 0, 405, 524, 474, 348, 473, 510, - 559, 558, 363, 586, 593, 594, 685, 0, 599, 790, - 791, 792, 608, 0, 516, 359, 356, 0, 0, 0, - 385, 511, 372, 374, 375, 373, 505, 507, 613, 614, - 615, 617, 0, 618, 619, 0, 0, 0, 0, 620, - 686, 704, 670, 639, 601, 694, 636, 640, 641, 426, - 427, 428, 429, 707, 0, 0, 0, 592, 454, 455, - 0, 397, 396, 475, 349, 0, 0, 443, 425, 517, - 355, 393, 445, 431, 456, 457, 458, 459, 460, 0, - 461, 462, 403, 339, 340, 784, 1087, 498, 709, 746, - 747, 631, 0, 1103, 1082, 1084, 1085, 1089, 1093, 1094, - 1095, 1096, 1097, 1099, 1102, 1106, 783, 0, 687, 701, - 788, 700, 780, 504, 0, 535, 698, 645, 0, 691, - 664, 665, 0, 692, 660, 696, 0, 633, 0, 602, - 605, 635, 722, 723, 724, 346, 604, 726, 727, 728, - 729, 730, 731, 732, 725, 1105, 668, 644, 671, 583, - 647, 646, 0, 0, 682, 1021, 683, 684, 486, 487, - 488, 489, 1091, 710, 370, 603, 519, 520, 0, 669, - 0, 0, 0, 0, 0, 0, 0, 0, 674, 675, - 672, 793, 0, 733, 734, 0, 0, 597, 598, 402, - 0, 616, 410, 369, 503, 404, 581, 434, 0, 609, - 676, 610, 522, 523, 737, 742, 738, 739, 741, 762, - 493, 424, 430, 539, 444, 466, 525, 580, 501, 530, - 367, 570, 541, 472, 661, 689, 1114, 1086, 1113, 1115, - 1116, 1112, 1117, 1118, 1098, 975, 0, 1028, 1029, 1110, - 1109, 1111, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 718, 717, 716, 715, 714, 713, 712, - 711, 0, 0, 658, 384, 781, 775, 782, 763, 766, - 765, 740, 982, 341, 638, 464, 518, 401, 705, 706, - 0, 761, 1076, 1039, 1040, 1041, 972, 1042, 1036, 1037, - 973, 1038, 1077, 1026, 1073, 1074, 1001, 1031, 1043, 1072, - 1044, 1075, 1002, 1078, 1119, 1120, 1050, 1034, 302, 1121, - 1047, 1079, 1071, 1070, 1045, 1027, 1080, 1081, 1009, 1004, - 1048, 1049, 1035, 1056, 1057, 1058, 1061, 974, 1062, 1063, - 1064, 1065, 1066, 1060, 1059, 1023, 1024, 1025, 1051, 1052, - 1054, 1055, 1030, 549, 1005, 1006, 1007, 1008, 0, 0, - 587, 588, 589, 612, 0, 590, 572, 637, 411, 342, - 553, 579, 778, 779, 0, 0, 0, 0, 0, 0, - 0, 688, 699, 735, 736, 0, 749, 750, 752, 754, - 1067, 756, 546, 547, 764, 0, 0, 435, 357, 358, - 494, 495, 532, 0, 436, 437, 438, 442, 439, 440, - 441, 1053, 759, 760, 757, 468, 531, 554, 540, 777, - 481, 0, 785, 627, 628, 786, 745, 744, 343, 0, - 1032, 1033, 702, 703, 1019, 0, 1100, 480, 634, 0, - 967, 0, 0, 500, 0, 0, 643, 677, 666, 755, - 630, 0, 0, 2140, 0, 0, 0, 970, 0, 0, - 0, 394, 0, 0, 463, 681, 662, 673, 663, 506, - 648, 649, 650, 657, 406, 651, 787, 653, 622, 654, - 623, 655, 656, 1010, 680, 629, 542, 482, 0, 697, - 0, 0, 1092, 1101, 0, 0, 0, 0, 0, 0, - 0, 0, 1088, 0, 0, 0, 0, 0, 961, 0, - 0, 999, 1069, 1068, 986, 996, 0, 0, 365, 269, - 624, 751, 626, 625, 987, 0, 988, 992, 995, 991, - 989, 990, 0, 1083, 0, 0, 0, 0, 0, 0, - 0, 965, 0, 971, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 962, - 963, 0, 0, 0, 0, 1020, 0, 964, 0, 966, - 0, 0, 0, 0, 543, 571, 0, 584, 0, 432, - 433, 1015, 993, 997, 0, 0, 0, 0, 350, 550, - 568, 366, 537, 582, 371, 545, 561, 361, 499, 533, - 0, 0, 352, 566, 544, 477, 351, 0, 526, 391, - 408, 388, 497, 994, 0, 1018, 1022, 387, 1107, 1016, - 576, 354, 0, 575, 496, 469, 562, 567, 478, 471, - 0, 353, 564, 476, 470, 446, 398, 1108, 447, 448, - 449, 450, 451, 452, 412, 512, 467, 513, 413, 484, - 483, 485, 414, 415, 416, 417, 418, 419, 420, 421, - 422, 423, 0, 0, 0, 0, 0, 606, 607, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 743, 1013, 0, 748, 0, 578, - 0, 0, 1090, 0, 0, 0, 548, 0, 0, 453, - 0, 0, 0, 1017, 0, 529, 502, 1104, 0, 0, - 527, 465, 563, 514, 569, 551, 577, 521, 515, 344, - 552, 390, 479, 362, 364, 776, 392, 395, 399, 400, - 490, 491, 508, 536, 555, 556, 557, 389, 376, 528, - 377, 409, 378, 345, 381, 380, 382, 538, 383, 347, - 509, 560, 0, 405, 524, 474, 348, 473, 510, 559, - 558, 363, 586, 2141, 2142, 685, 0, 599, 790, 791, - 792, 608, 0, 516, 359, 356, 0, 0, 0, 385, - 511, 372, 374, 375, 373, 505, 507, 613, 614, 615, - 617, 0, 618, 619, 0, 0, 0, 0, 620, 686, - 704, 670, 639, 601, 694, 636, 640, 641, 426, 427, - 428, 429, 707, 0, 0, 0, 592, 454, 455, 0, - 397, 396, 475, 349, 0, 0, 443, 425, 517, 355, - 393, 445, 431, 456, 457, 458, 459, 460, 0, 461, - 462, 403, 339, 340, 784, 1087, 498, 709, 746, 747, - 631, 0, 1103, 1082, 1084, 1085, 1089, 1093, 1094, 1095, - 1096, 1097, 1099, 1102, 1106, 783, 0, 687, 701, 788, - 700, 780, 504, 0, 535, 698, 645, 0, 691, 664, - 665, 0, 692, 660, 696, 0, 633, 0, 602, 605, - 635, 722, 723, 724, 346, 604, 726, 727, 728, 729, - 730, 731, 732, 725, 1105, 668, 644, 671, 583, 647, - 646, 0, 0, 682, 1021, 683, 684, 486, 487, 488, - 489, 1091, 710, 370, 603, 519, 520, 0, 669, 0, - 0, 0, 0, 0, 0, 0, 0, 674, 675, 672, - 793, 0, 733, 734, 0, 0, 597, 598, 402, 0, - 616, 410, 369, 503, 404, 581, 434, 0, 609, 676, - 610, 522, 523, 737, 742, 738, 739, 741, 762, 493, - 424, 430, 539, 444, 466, 525, 580, 501, 530, 367, - 570, 541, 472, 661, 689, 1114, 1086, 1113, 1115, 1116, - 1112, 1117, 1118, 1098, 975, 0, 1028, 1029, 1110, 1109, - 1111, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 718, 717, 716, 715, 714, 713, 712, 711, - 0, 0, 658, 384, 781, 775, 782, 763, 766, 765, - 740, 982, 341, 638, 464, 518, 401, 705, 706, 0, - 761, 1076, 1039, 1040, 1041, 972, 1042, 1036, 1037, 973, - 1038, 1077, 1026, 1073, 1074, 1001, 1031, 1043, 1072, 1044, - 1075, 1002, 1078, 1119, 1120, 1050, 1034, 302, 1121, 1047, - 1079, 1071, 1070, 1045, 1027, 1080, 1081, 1009, 1004, 1048, - 1049, 1035, 1056, 1057, 1058, 1061, 974, 1062, 1063, 1064, - 1065, 1066, 1060, 1059, 1023, 1024, 1025, 1051, 1052, 1054, - 1055, 1030, 549, 1005, 1006, 1007, 1008, 0, 0, 587, - 588, 589, 612, 0, 590, 572, 637, 411, 342, 553, - 579, 778, 779, 0, 0, 0, 0, 0, 0, 0, - 688, 699, 735, 736, 0, 749, 750, 752, 754, 1067, - 756, 546, 547, 764, 0, 0, 435, 357, 358, 494, - 495, 532, 0, 436, 437, 438, 442, 439, 440, 441, - 1053, 759, 760, 757, 468, 531, 554, 540, 777, 481, - 0, 785, 627, 628, 786, 745, 744, 343, 0, 1032, - 1033, 702, 703, 1019, 0, 1100, 480, 634, 0, 967, - 0, 0, 500, 0, 0, 643, 677, 666, 755, 630, - 0, 0, 0, 0, 0, 0, 970, 0, 0, 0, - 394, 0, 0, 463, 681, 662, 673, 663, 506, 648, - 649, 650, 657, 406, 651, 787, 653, 622, 654, 623, - 655, 656, 1010, 680, 629, 542, 482, 0, 697, 0, - 0, 1092, 1101, 0, 0, 0, 0, 0, 0, 0, - 0, 1088, 0, 0, 0, 0, 0, 1623, 0, 0, - 999, 1069, 1068, 986, 996, 0, 0, 365, 269, 624, - 751, 626, 625, 987, 0, 988, 992, 995, 991, 989, - 990, 0, 1083, 0, 0, 0, 0, 0, 0, 953, - 965, 0, 971, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 962, 963, - 0, 0, 0, 0, 1020, 0, 964, 0, 966, 0, - 0, 0, 0, 543, 571, 0, 584, 0, 432, 433, - 1015, 993, 997, 0, 0, 0, 0, 350, 550, 568, - 366, 537, 582, 371, 545, 561, 361, 499, 533, 0, - 0, 352, 566, 544, 477, 351, 0, 526, 391, 408, - 388, 497, 994, 0, 1018, 1022, 387, 1107, 1016, 576, - 354, 0, 575, 496, 469, 562, 567, 478, 471, 0, - 353, 564, 476, 470, 446, 398, 1108, 447, 448, 449, - 450, 451, 452, 412, 512, 467, 513, 413, 484, 483, - 485, 414, 415, 416, 417, 418, 419, 420, 421, 422, - 423, 0, 0, 0, 0, 0, 606, 607, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 743, 1013, 0, 748, 0, 578, 0, - 0, 1090, 0, 0, 0, 548, 0, 0, 453, 0, - 0, 0, 1017, 0, 529, 502, 1104, 0, 0, 527, - 465, 563, 514, 569, 551, 577, 521, 515, 344, 552, - 390, 479, 362, 364, 776, 392, 395, 399, 400, 490, - 491, 508, 536, 555, 556, 557, 389, 376, 528, 377, - 409, 378, 345, 381, 380, 382, 538, 383, 347, 509, - 560, 0, 405, 524, 474, 348, 473, 510, 559, 558, - 363, 586, 593, 594, 685, 0, 599, 790, 791, 792, - 608, 0, 516, 359, 356, 0, 0, 0, 385, 511, - 372, 374, 375, 373, 505, 507, 613, 614, 615, 617, - 0, 618, 619, 0, 0, 0, 0, 620, 686, 704, - 670, 639, 601, 694, 636, 640, 641, 426, 427, 428, - 429, 707, 0, 0, 0, 592, 454, 455, 0, 397, - 396, 475, 349, 0, 0, 443, 425, 517, 355, 393, - 445, 431, 456, 457, 458, 459, 460, 0, 461, 462, - 403, 339, 340, 784, 1087, 498, 709, 746, 747, 631, - 0, 1103, 1082, 1084, 1085, 1089, 1093, 1094, 1095, 1096, - 1097, 1099, 1102, 1106, 783, 0, 687, 701, 788, 700, - 780, 504, 0, 535, 698, 645, 0, 691, 664, 665, - 0, 692, 660, 696, 0, 633, 0, 602, 605, 635, - 722, 723, 724, 346, 604, 726, 727, 728, 729, 730, - 731, 732, 725, 1105, 668, 644, 671, 583, 647, 646, - 0, 0, 682, 1021, 683, 684, 486, 487, 488, 489, - 1091, 710, 370, 603, 519, 520, 0, 669, 0, 0, - 0, 0, 0, 0, 0, 0, 674, 675, 672, 793, - 0, 733, 734, 0, 0, 597, 598, 402, 0, 616, - 410, 369, 503, 404, 581, 434, 0, 609, 676, 610, - 522, 523, 737, 742, 738, 739, 741, 762, 493, 424, - 430, 539, 444, 466, 525, 580, 501, 530, 367, 570, - 541, 472, 661, 689, 1114, 1086, 1113, 1115, 1116, 1112, - 1117, 1118, 1098, 975, 0, 1028, 1029, 1110, 1109, 1111, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 718, 717, 716, 715, 714, 713, 712, 711, 0, - 0, 658, 384, 781, 775, 782, 763, 766, 765, 740, - 982, 341, 638, 464, 518, 401, 705, 706, 0, 761, - 1076, 1039, 1040, 1041, 972, 1042, 1036, 1037, 973, 1038, - 1077, 1026, 1073, 1074, 1001, 1031, 1043, 1072, 1044, 1075, - 1002, 1078, 1119, 1120, 1050, 1034, 302, 1121, 1047, 1079, - 1071, 1070, 1045, 1027, 1080, 1081, 1009, 1004, 1048, 1049, - 1035, 1056, 1057, 1058, 1061, 974, 1062, 1063, 1064, 1065, - 1066, 1060, 1059, 1023, 1024, 1025, 1051, 1052, 1054, 1055, - 1030, 549, 1005, 1006, 1007, 1008, 0, 0, 587, 588, - 589, 612, 0, 590, 572, 637, 411, 342, 553, 579, - 778, 779, 0, 0, 0, 0, 0, 0, 0, 688, - 699, 735, 736, 0, 749, 750, 752, 754, 1067, 756, - 546, 547, 764, 0, 0, 435, 357, 358, 494, 495, - 532, 0, 436, 437, 438, 442, 439, 440, 441, 1053, - 759, 760, 757, 468, 531, 554, 540, 777, 481, 0, - 785, 627, 628, 786, 745, 744, 343, 0, 1032, 1033, - 702, 703, 1019, 0, 1100, 480, 634, 0, 967, 0, - 0, 500, 0, 0, 643, 677, 666, 755, 630, 0, - 0, 0, 0, 0, 0, 970, 0, 0, 0, 394, - 0, 0, 463, 681, 662, 673, 663, 506, 648, 649, - 650, 657, 406, 651, 787, 653, 622, 654, 623, 655, - 656, 1010, 680, 629, 542, 482, 0, 697, 0, 0, - 1092, 1101, 0, 0, 0, 0, 0, 0, 0, 0, - 1088, 0, 0, 0, 0, 0, 961, 0, 0, 999, - 1069, 1068, 986, 996, 0, 0, 365, 269, 624, 751, - 626, 625, 987, 0, 988, 992, 995, 991, 989, 990, - 0, 1083, 0, 0, 0, 0, 0, 0, 0, 965, - 0, 971, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 962, 963, 0, - 0, 0, 0, 1020, 0, 964, 0, 966, 0, 0, - 0, 0, 543, 571, 0, 584, 0, 432, 433, 1015, - 993, 997, 0, 0, 0, 0, 350, 550, 568, 366, - 537, 582, 371, 545, 561, 361, 499, 533, 0, 0, - 352, 566, 544, 477, 351, 0, 526, 391, 408, 388, - 497, 994, 0, 1018, 1022, 387, 1107, 1016, 576, 354, - 0, 575, 496, 469, 562, 567, 478, 471, 0, 353, - 564, 476, 470, 446, 398, 1108, 447, 448, 449, 450, - 451, 452, 412, 512, 467, 513, 413, 484, 483, 485, - 414, 415, 416, 417, 418, 419, 420, 421, 422, 423, - 0, 0, 0, 0, 0, 606, 607, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 743, 1013, 0, 748, 0, 578, 0, 0, - 1090, 0, 0, 0, 548, 0, 0, 453, 0, 0, - 0, 1017, 0, 529, 502, 1104, 0, 0, 527, 465, - 563, 514, 569, 551, 577, 521, 515, 344, 552, 390, - 479, 362, 364, 776, 392, 395, 399, 400, 490, 491, - 508, 536, 555, 556, 557, 389, 376, 528, 377, 409, - 378, 345, 381, 380, 382, 538, 383, 347, 509, 560, - 0, 405, 524, 474, 348, 473, 510, 559, 558, 363, - 586, 593, 594, 685, 0, 599, 790, 791, 792, 608, - 0, 516, 359, 356, 0, 0, 0, 385, 511, 372, - 374, 375, 373, 505, 507, 613, 614, 615, 617, 0, - 618, 619, 0, 0, 0, 0, 620, 686, 704, 670, - 639, 601, 694, 636, 640, 641, 426, 427, 428, 429, - 707, 0, 0, 0, 592, 454, 455, 0, 397, 396, - 475, 349, 0, 0, 443, 425, 517, 355, 393, 445, - 431, 456, 457, 458, 459, 460, 0, 461, 462, 403, - 339, 340, 784, 1087, 498, 709, 746, 747, 631, 0, - 1103, 1082, 1084, 1085, 1089, 1093, 1094, 1095, 1096, 1097, - 1099, 1102, 1106, 783, 0, 687, 701, 788, 700, 780, - 504, 0, 535, 698, 645, 0, 691, 664, 665, 0, - 692, 660, 696, 0, 633, 0, 602, 605, 635, 722, - 723, 724, 346, 604, 726, 727, 728, 729, 730, 731, - 732, 725, 1105, 668, 644, 671, 583, 647, 646, 0, - 0, 682, 1021, 683, 684, 486, 487, 488, 489, 1091, - 710, 370, 603, 519, 520, 0, 669, 0, 0, 0, - 0, 0, 0, 0, 0, 674, 675, 672, 793, 0, - 733, 734, 0, 0, 597, 598, 402, 0, 616, 410, - 369, 503, 404, 581, 434, 0, 609, 676, 610, 522, - 523, 737, 742, 738, 739, 741, 762, 493, 424, 430, - 539, 444, 466, 525, 580, 501, 530, 367, 570, 541, - 472, 661, 689, 1114, 1086, 1113, 1115, 1116, 1112, 1117, - 1118, 1098, 975, 0, 1028, 1029, 1110, 1109, 1111, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 718, 717, 716, 715, 714, 713, 712, 711, 0, 0, - 658, 384, 781, 775, 782, 763, 766, 765, 740, 982, - 341, 638, 464, 518, 401, 705, 706, 0, 761, 1076, - 1039, 1040, 1041, 972, 1042, 1036, 1037, 973, 1038, 1077, - 1026, 1073, 1074, 1001, 1031, 1043, 1072, 1044, 1075, 1002, - 1078, 1119, 1120, 1050, 1034, 302, 1121, 1047, 1079, 1071, - 1070, 1045, 1027, 1080, 1081, 1009, 1004, 1048, 1049, 1035, - 1056, 1057, 1058, 1061, 974, 1062, 1063, 1064, 1065, 1066, - 1060, 1059, 1023, 1024, 1025, 1051, 1052, 1054, 1055, 1030, - 549, 1005, 1006, 1007, 1008, 0, 0, 587, 588, 589, - 612, 0, 590, 572, 637, 411, 342, 553, 579, 778, - 779, 0, 0, 0, 0, 0, 0, 0, 688, 699, - 735, 736, 0, 749, 750, 752, 754, 1067, 756, 546, - 547, 764, 0, 0, 435, 357, 358, 494, 495, 532, - 0, 436, 437, 438, 442, 439, 440, 441, 1053, 759, - 760, 757, 468, 531, 554, 540, 777, 481, 0, 785, - 627, 628, 786, 745, 744, 343, 0, 1032, 1033, 702, - 703, 0, 0, 1100, 480, 634, 0, 967, 200, 245, - 199, 232, 201, 0, 0, 0, 0, 0, 0, 500, - 0, 0, 643, 677, 666, 755, 630, 0, 233, 0, - 0, 0, 0, 0, 0, 225, 0, 394, 0, 234, - 463, 681, 662, 673, 663, 506, 648, 649, 650, 657, - 406, 651, 652, 653, 622, 654, 623, 655, 656, 169, - 680, 629, 542, 482, 0, 697, 0, 0, 0, 0, - 0, 0, 0, 0, 154, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 238, 0, 0, 268, 0, 0, - 0, 0, 0, 0, 365, 269, 624, 751, 626, 625, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 368, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 260, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 543, 571, 0, 584, 0, 432, 433, 0, 0, 0, - 0, 0, 0, 0, 350, 550, 568, 366, 537, 582, - 371, 545, 561, 361, 499, 533, 0, 0, 352, 566, - 544, 477, 351, 0, 526, 391, 408, 388, 497, 0, - 0, 565, 595, 387, 585, 0, 576, 354, 0, 575, - 496, 469, 562, 567, 478, 471, 0, 353, 564, 476, - 470, 446, 398, 611, 447, 448, 449, 450, 451, 452, - 412, 512, 467, 513, 413, 484, 483, 485, 414, 415, - 416, 417, 418, 419, 420, 421, 422, 423, 0, 0, - 0, 0, 0, 606, 607, 0, 0, 0, 0, 0, - 0, 0, 198, 230, 242, 231, 84, 150, 0, 0, - 743, 0, 246, 748, 0, 578, 0, 0, 261, 0, - 0, 0, 548, 0, 0, 453, 152, 224, 223, 596, - 0, 529, 502, 274, 0, 0, 527, 465, 563, 514, - 569, 551, 577, 521, 515, 344, 552, 390, 479, 362, - 364, 283, 392, 395, 399, 400, 490, 491, 508, 536, - 555, 556, 557, 389, 376, 528, 377, 409, 378, 345, - 381, 380, 382, 538, 383, 347, 509, 560, 151, 405, - 524, 474, 348, 473, 510, 559, 558, 363, 586, 593, - 594, 685, 0, 599, 719, 720, 721, 608, 0, 516, - 359, 356, 0, 0, 0, 385, 511, 372, 374, 375, - 373, 505, 507, 613, 614, 615, 617, 0, 618, 619, - 0, 0, 0, 0, 620, 686, 704, 670, 639, 601, - 694, 636, 640, 641, 426, 427, 428, 429, 707, 0, - 0, 0, 592, 454, 455, 0, 397, 396, 475, 349, - 0, 0, 443, 425, 517, 355, 393, 445, 431, 456, - 457, 458, 459, 460, 0, 461, 462, 403, 339, 340, - 573, 386, 498, 709, 746, 747, 631, 0, 695, 632, - 642, 379, 667, 679, 678, 492, 591, 264, 690, 693, - 621, 275, 0, 687, 701, 659, 700, 276, 504, 0, - 535, 698, 645, 0, 691, 664, 665, 0, 692, 660, - 696, 0, 633, 0, 602, 605, 635, 722, 723, 724, - 346, 604, 726, 727, 728, 729, 730, 731, 732, 725, - 574, 668, 644, 671, 583, 647, 646, 0, 0, 682, - 600, 683, 684, 486, 487, 488, 489, 407, 710, 370, - 603, 519, 520, 166, 669, 0, 0, 0, 0, 0, - 0, 0, 0, 674, 675, 672, 273, 0, 733, 734, - 0, 0, 597, 598, 402, 0, 616, 410, 369, 503, - 404, 581, 434, 0, 609, 676, 610, 522, 523, 737, - 742, 738, 739, 741, 762, 493, 424, 430, 539, 444, - 466, 525, 580, 501, 530, 367, 570, 541, 472, 661, - 689, 0, 0, 0, 0, 0, 0, 0, 0, 79, - 0, 0, 325, 326, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 718, 717, - 716, 715, 714, 713, 712, 711, 0, 0, 658, 384, - 281, 360, 282, 763, 766, 765, 740, 0, 341, 638, - 464, 518, 401, 705, 706, 72, 761, 284, 285, 286, - 287, 288, 289, 290, 291, 332, 292, 293, 294, 295, - 296, 297, 298, 303, 304, 305, 306, 307, 308, 309, - 310, 708, 301, 302, 311, 312, 313, 314, 315, 316, - 317, 318, 319, 320, 321, 322, 323, 324, 0, 0, - 0, 0, 333, 767, 768, 769, 770, 771, 0, 0, - 334, 335, 336, 0, 0, 299, 300, 327, 549, 328, - 329, 330, 331, 0, 0, 587, 588, 589, 612, 0, - 590, 572, 637, 411, 342, 553, 579, 277, 278, 55, - 262, 265, 267, 266, 0, 73, 688, 699, 735, 736, - 9, 749, 750, 752, 754, 753, 756, 546, 547, 764, - 0, 0, 435, 357, 358, 494, 495, 532, 0, 436, - 437, 438, 442, 439, 440, 441, 758, 759, 760, 757, - 468, 531, 554, 540, 272, 481, 173, 279, 627, 628, - 280, 745, 744, 343, 0, 337, 338, 702, 703, 200, - 245, 534, 480, 634, 0, 0, 0, 0, 0, 0, - 500, 0, 0, 643, 677, 666, 755, 630, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 394, 0, - 0, 463, 681, 662, 673, 663, 506, 648, 649, 650, - 657, 406, 651, 787, 653, 622, 654, 623, 655, 656, - 1266, 680, 629, 542, 482, 0, 697, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 238, 0, 0, 268, 0, - 0, 0, 0, 0, 0, 365, 269, 624, 751, 626, - 625, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 368, 3048, 3051, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 543, 571, 0, 584, 0, 432, 433, 0, 0, - 0, 0, 0, 0, 0, 350, 550, 568, 366, 537, - 582, 371, 545, 561, 361, 499, 533, 0, 0, 352, - 566, 544, 477, 351, 0, 526, 391, 408, 388, 497, - 0, 0, 565, 595, 387, 585, 0, 576, 354, 0, - 575, 496, 469, 562, 567, 478, 471, 0, 353, 564, - 476, 470, 446, 398, 611, 447, 448, 449, 450, 451, - 452, 412, 512, 467, 513, 413, 484, 483, 485, 414, - 415, 416, 417, 418, 419, 420, 421, 422, 423, 0, - 0, 0, 0, 0, 606, 607, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 743, 0, 246, 748, 3052, 578, 0, 0, 0, - 3047, 0, 3046, 548, 3043, 3049, 453, 0, 0, 0, - 596, 0, 529, 502, 789, 0, 0, 527, 465, 563, - 514, 569, 551, 577, 521, 515, 344, 552, 390, 479, - 362, 364, 776, 392, 395, 399, 400, 490, 491, 508, - 536, 555, 556, 557, 389, 376, 528, 377, 409, 378, - 345, 381, 380, 382, 538, 383, 347, 509, 560, 3050, - 405, 524, 474, 348, 473, 510, 559, 558, 363, 586, - 593, 594, 685, 0, 599, 790, 791, 792, 608, 0, - 516, 359, 356, 0, 0, 0, 385, 511, 372, 374, - 375, 373, 505, 507, 613, 614, 615, 617, 0, 618, - 619, 0, 0, 0, 0, 620, 686, 704, 670, 639, - 601, 694, 636, 640, 641, 426, 427, 428, 429, 707, - 0, 0, 0, 592, 454, 455, 0, 397, 396, 475, - 349, 0, 0, 443, 425, 517, 355, 393, 445, 431, - 456, 457, 458, 459, 460, 3044, 461, 462, 403, 339, - 340, 784, 386, 498, 709, 746, 747, 631, 0, 695, - 632, 642, 379, 667, 679, 678, 492, 591, 0, 690, - 693, 621, 783, 0, 687, 701, 788, 700, 780, 504, - 0, 535, 698, 645, 0, 691, 664, 665, 0, 692, - 660, 696, 0, 633, 0, 602, 605, 635, 722, 723, - 724, 346, 604, 726, 727, 728, 729, 730, 731, 732, - 725, 574, 668, 644, 671, 583, 647, 646, 0, 0, - 682, 600, 683, 684, 486, 487, 488, 489, 407, 710, - 370, 603, 519, 520, 0, 669, 0, 0, 0, 0, - 0, 0, 0, 0, 674, 675, 672, 793, 0, 733, - 734, 0, 0, 597, 598, 402, 0, 616, 410, 369, - 503, 404, 581, 434, 0, 609, 676, 610, 522, 523, - 737, 742, 738, 739, 741, 762, 493, 424, 430, 539, - 444, 466, 525, 580, 501, 530, 367, 570, 541, 472, - 661, 689, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 325, 326, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 718, - 717, 716, 715, 714, 713, 712, 711, 0, 0, 658, - 384, 781, 775, 782, 763, 766, 765, 740, 0, 341, - 638, 464, 518, 401, 705, 706, 0, 761, 284, 285, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 4156, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 200, 245, 1023, 0, 0, 0, + 0, 0, 0, 0, 0, 502, 0, 0, 647, 681, + 670, 759, 634, 172, 0, 0, 0, 0, 0, 974, + 0, 0, 0, 395, 0, 0, 465, 685, 666, 677, + 667, 508, 652, 653, 654, 661, 407, 655, 791, 657, + 626, 658, 627, 659, 660, 1609, 684, 633, 545, 484, + 0, 701, 0, 0, 1096, 1105, 0, 0, 0, 0, + 0, 0, 0, 0, 1092, 0, 0, 0, 0, 0, + 965, 0, 0, 1003, 1073, 1072, 990, 1000, 0, 0, + 366, 269, 628, 755, 630, 629, 991, 0, 992, 996, + 999, 995, 993, 994, 0, 1087, 0, 0, 0, 0, + 0, 0, 957, 969, 0, 975, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 966, 967, 0, 0, 0, 0, 1024, 0, 968, + 0, 970, 0, 0, 0, 0, 546, 574, 0, 588, + 0, 434, 435, 1019, 997, 1001, 0, 0, 0, 0, + 350, 553, 571, 367, 540, 586, 372, 548, 564, 362, + 501, 536, 0, 0, 352, 569, 547, 479, 351, 0, + 529, 392, 409, 389, 499, 998, 0, 1022, 1026, 388, + 1111, 1020, 579, 354, 0, 578, 498, 471, 565, 570, + 480, 473, 0, 353, 567, 478, 472, 448, 399, 1112, + 449, 450, 451, 452, 453, 454, 413, 514, 469, 515, + 414, 486, 485, 487, 415, 416, 417, 418, 419, 420, + 421, 422, 423, 424, 0, 0, 0, 0, 0, 610, + 611, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 747, 1017, 246, 752, + 0, 582, 0, 0, 1094, 0, 0, 0, 551, 0, + 0, 455, 0, 0, 0, 1021, 172, 532, 504, 1108, + 0, 0, 530, 467, 566, 516, 572, 554, 580, 523, + 517, 344, 555, 391, 481, 363, 365, 780, 393, 396, + 400, 401, 492, 493, 510, 539, 558, 559, 560, 390, + 377, 531, 378, 410, 379, 345, 382, 381, 383, 541, + 384, 347, 511, 563, 0, 406, 527, 476, 348, 475, + 512, 562, 561, 364, 590, 597, 598, 689, 0, 603, + 794, 795, 796, 612, 0, 518, 360, 357, 0, 0, + 0, 386, 513, 373, 375, 376, 374, 507, 509, 617, + 618, 619, 621, 0, 622, 623, 0, 0, 0, 0, + 624, 690, 708, 674, 643, 605, 698, 640, 644, 645, + 427, 428, 429, 430, 711, 0, 0, 0, 596, 456, + 457, 0, 398, 397, 477, 349, 0, 0, 445, 426, + 519, 355, 394, 447, 432, 458, 459, 460, 461, 462, + 0, 463, 464, 404, 339, 340, 788, 1091, 500, 713, + 750, 751, 635, 0, 1107, 1086, 1088, 1089, 1093, 1097, + 1098, 1099, 1100, 1101, 1103, 1106, 1110, 787, 0, 691, + 705, 792, 704, 784, 506, 0, 538, 702, 649, 0, + 695, 668, 669, 0, 696, 664, 700, 356, 433, 524, + 581, 0, 637, 0, 606, 609, 639, 726, 727, 728, + 346, 608, 730, 731, 732, 733, 734, 735, 736, 729, + 1109, 672, 648, 675, 587, 651, 650, 0, 0, 686, + 1025, 687, 688, 488, 489, 490, 491, 1095, 714, 371, + 607, 521, 522, 0, 673, 0, 0, 0, 0, 0, + 172, 0, 0, 678, 679, 676, 797, 0, 737, 738, + 0, 0, 601, 602, 403, 0, 620, 411, 370, 505, + 405, 585, 436, 0, 613, 680, 614, 525, 526, 741, + 746, 742, 743, 745, 766, 495, 425, 431, 542, 446, + 468, 528, 584, 503, 533, 368, 573, 544, 474, 665, + 693, 1118, 1090, 1117, 1119, 1120, 1116, 1121, 1122, 1102, + 979, 0, 1032, 1033, 1114, 1113, 1115, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 722, 721, + 720, 719, 718, 717, 716, 715, 0, 0, 662, 385, + 785, 779, 786, 767, 770, 769, 744, 986, 341, 642, + 466, 520, 402, 709, 710, 0, 765, 1080, 1043, 1044, + 1045, 976, 1046, 1040, 1041, 977, 1042, 1081, 1030, 1077, + 1078, 1005, 1035, 1047, 1076, 1048, 1079, 1006, 1082, 1123, + 1124, 1054, 1038, 302, 1125, 1051, 1083, 1075, 1074, 1049, + 1031, 1084, 1085, 1013, 1008, 1052, 1053, 1039, 1060, 1061, + 1062, 1065, 978, 1066, 1067, 1068, 1069, 1070, 1064, 1063, + 1027, 1028, 1029, 1055, 1056, 1058, 1059, 1034, 552, 1009, + 1010, 1011, 1012, 0, 0, 591, 592, 593, 616, 0, + 594, 575, 641, 412, 342, 556, 583, 782, 783, 0, + 0, 0, 0, 0, 0, 0, 692, 703, 739, 740, + 0, 753, 754, 756, 758, 1071, 760, 549, 550, 768, + 0, 0, 437, 358, 359, 496, 497, 535, 0, 438, + 439, 440, 444, 441, 442, 443, 1057, 763, 764, 761, + 470, 534, 557, 543, 781, 483, 0, 789, 631, 632, + 790, 749, 748, 343, 0, 1036, 1037, 706, 707, 1023, + 0, 1104, 482, 638, 0, 971, 0, 0, 502, 0, + 0, 647, 681, 670, 759, 634, 0, 0, 0, 0, + 0, 0, 974, 0, 0, 0, 395, 0, 0, 465, + 685, 666, 677, 667, 508, 652, 653, 654, 661, 407, + 655, 791, 657, 626, 658, 627, 659, 660, 1014, 684, + 633, 545, 484, 0, 701, 0, 0, 1096, 1105, 0, + 0, 0, 0, 0, 0, 0, 0, 1092, 0, 0, + 0, 0, 0, 965, 0, 0, 1003, 1073, 1072, 990, + 1000, 0, 0, 366, 269, 628, 755, 630, 629, 991, + 0, 992, 996, 999, 995, 993, 994, 0, 1087, 0, + 0, 0, 0, 0, 0, 957, 969, 0, 975, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 966, 967, 0, 0, 0, 0, + 1024, 0, 968, 0, 970, 0, 0, 0, 0, 546, + 574, 0, 588, 0, 434, 435, 1019, 997, 1001, 0, + 0, 0, 0, 350, 553, 571, 367, 540, 586, 372, + 548, 564, 362, 501, 536, 0, 0, 352, 569, 547, + 479, 351, 0, 529, 392, 409, 389, 499, 998, 0, + 1022, 1026, 388, 1111, 1020, 579, 354, 0, 578, 498, + 471, 565, 570, 480, 473, 0, 353, 567, 478, 472, + 448, 399, 1112, 449, 450, 451, 452, 453, 454, 413, + 514, 469, 515, 414, 486, 485, 487, 415, 416, 417, + 418, 419, 420, 421, 422, 423, 424, 0, 0, 0, + 0, 0, 610, 611, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 747, + 1017, 0, 752, 0, 582, 0, 0, 1094, 0, 0, + 0, 551, 0, 0, 455, 0, 0, 0, 1021, 0, + 532, 504, 1108, 0, 0, 530, 467, 566, 516, 572, + 554, 580, 523, 517, 344, 555, 391, 481, 363, 365, + 780, 393, 396, 400, 401, 492, 493, 510, 539, 558, + 559, 560, 390, 377, 531, 378, 410, 379, 345, 382, + 381, 383, 541, 384, 347, 511, 563, 0, 406, 527, + 476, 348, 475, 512, 562, 561, 364, 590, 597, 598, + 689, 0, 603, 794, 795, 796, 612, 0, 518, 360, + 357, 0, 0, 0, 386, 513, 373, 375, 376, 374, + 507, 509, 617, 618, 619, 621, 0, 622, 623, 0, + 0, 0, 0, 624, 690, 708, 674, 643, 605, 698, + 640, 644, 645, 427, 428, 429, 430, 711, 2251, 2250, + 2252, 596, 456, 457, 0, 398, 397, 477, 349, 0, + 0, 445, 426, 519, 355, 394, 447, 432, 458, 459, + 460, 461, 462, 0, 463, 464, 404, 339, 340, 788, + 1091, 500, 713, 750, 751, 635, 0, 1107, 1086, 1088, + 1089, 1093, 1097, 1098, 1099, 1100, 1101, 1103, 1106, 1110, + 787, 0, 691, 705, 792, 704, 784, 506, 0, 538, + 702, 649, 0, 695, 668, 669, 0, 696, 664, 700, + 356, 433, 524, 581, 0, 637, 0, 606, 609, 639, + 726, 727, 728, 346, 608, 730, 731, 732, 733, 734, + 735, 736, 729, 1109, 672, 648, 675, 587, 651, 650, + 0, 0, 686, 1025, 687, 688, 488, 489, 490, 491, + 1095, 714, 371, 607, 521, 522, 0, 673, 0, 0, + 0, 0, 0, 0, 0, 0, 678, 679, 676, 797, + 0, 737, 738, 0, 0, 601, 602, 403, 0, 620, + 411, 370, 505, 405, 585, 436, 0, 613, 680, 614, + 525, 526, 741, 746, 742, 743, 745, 766, 495, 425, + 431, 542, 446, 468, 528, 584, 503, 533, 368, 573, + 544, 474, 665, 693, 1118, 1090, 1117, 1119, 1120, 1116, + 1121, 1122, 1102, 979, 0, 1032, 1033, 1114, 1113, 1115, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 722, 721, 720, 719, 718, 717, 716, 715, 0, + 0, 662, 385, 785, 779, 786, 767, 770, 769, 744, + 986, 341, 642, 466, 520, 402, 709, 710, 0, 765, + 1080, 1043, 1044, 1045, 976, 1046, 1040, 1041, 977, 1042, + 1081, 1030, 1077, 1078, 1005, 1035, 1047, 1076, 1048, 1079, + 1006, 1082, 1123, 1124, 1054, 1038, 302, 1125, 1051, 1083, + 1075, 1074, 1049, 1031, 1084, 1085, 1013, 1008, 1052, 1053, + 1039, 1060, 1061, 1062, 1065, 978, 1066, 1067, 1068, 1069, + 1070, 1064, 1063, 1027, 1028, 1029, 1055, 1056, 1058, 1059, + 1034, 552, 1009, 1010, 1011, 1012, 0, 0, 591, 592, + 593, 616, 0, 594, 575, 641, 412, 342, 556, 583, + 782, 783, 0, 0, 0, 0, 0, 0, 0, 692, + 703, 739, 740, 0, 753, 754, 756, 758, 1071, 760, + 549, 550, 768, 0, 0, 437, 358, 359, 496, 497, + 535, 0, 438, 439, 440, 444, 441, 442, 443, 1057, + 763, 764, 761, 470, 534, 557, 543, 781, 483, 0, + 789, 631, 632, 790, 749, 748, 343, 0, 1036, 1037, + 706, 707, 0, 0, 1104, 482, 638, 0, 971, 200, + 245, 1023, 0, 0, 0, 0, 0, 0, 0, 0, + 502, 0, 0, 647, 681, 670, 759, 634, 0, 0, + 0, 0, 0, 0, 974, 0, 0, 0, 395, 0, + 0, 465, 685, 666, 677, 667, 508, 652, 653, 654, + 661, 407, 655, 791, 657, 626, 658, 627, 659, 660, + 1014, 684, 633, 545, 484, 0, 701, 0, 0, 1096, + 1105, 0, 0, 0, 0, 0, 0, 0, 0, 1092, + 0, 0, 0, 0, 0, 965, 0, 0, 1003, 1073, + 1072, 990, 1000, 0, 0, 366, 269, 628, 755, 630, + 629, 991, 0, 992, 996, 999, 995, 993, 994, 0, + 1087, 0, 0, 0, 0, 0, 0, 957, 969, 0, + 975, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 966, 967, 0, 0, + 0, 0, 1024, 0, 968, 0, 970, 0, 0, 0, + 0, 546, 574, 0, 588, 0, 434, 435, 1019, 997, + 1001, 0, 0, 0, 0, 350, 553, 571, 367, 540, + 586, 372, 548, 564, 362, 501, 536, 0, 0, 352, + 569, 547, 479, 351, 0, 529, 392, 409, 389, 499, + 998, 0, 1022, 1026, 388, 1111, 1020, 579, 354, 0, + 578, 498, 471, 565, 570, 480, 473, 0, 353, 567, + 478, 472, 448, 399, 1112, 449, 450, 451, 452, 453, + 454, 413, 514, 469, 515, 414, 486, 485, 487, 415, + 416, 417, 418, 419, 420, 421, 422, 423, 424, 0, + 0, 0, 0, 0, 610, 611, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 747, 1017, 0, 752, 0, 582, 0, 0, 1094, + 0, 0, 0, 551, 0, 0, 455, 0, 0, 0, + 1021, 0, 532, 504, 1108, 0, 0, 530, 467, 566, + 516, 572, 554, 580, 523, 517, 344, 555, 391, 481, + 363, 365, 780, 393, 396, 400, 401, 492, 493, 510, + 539, 558, 559, 560, 390, 377, 531, 378, 410, 379, + 345, 382, 381, 383, 541, 384, 347, 511, 563, 0, + 406, 527, 476, 348, 475, 512, 562, 561, 364, 590, + 597, 598, 689, 0, 603, 794, 795, 796, 612, 0, + 518, 360, 357, 0, 0, 0, 386, 513, 373, 375, + 376, 374, 507, 509, 617, 618, 619, 621, 0, 622, + 623, 0, 0, 0, 0, 624, 690, 708, 674, 643, + 605, 698, 640, 644, 645, 427, 428, 429, 430, 711, + 0, 0, 0, 596, 456, 457, 0, 398, 397, 477, + 349, 0, 0, 445, 426, 519, 355, 394, 447, 432, + 458, 459, 460, 461, 462, 0, 463, 464, 404, 339, + 340, 788, 1091, 500, 713, 750, 751, 635, 0, 1107, + 1086, 1088, 1089, 1093, 1097, 1098, 1099, 1100, 1101, 1103, + 1106, 1110, 787, 0, 691, 705, 792, 704, 784, 506, + 0, 538, 702, 649, 0, 695, 668, 669, 0, 696, + 664, 700, 356, 433, 524, 581, 0, 637, 0, 606, + 609, 639, 726, 727, 728, 346, 608, 730, 731, 732, + 733, 734, 735, 736, 729, 1109, 672, 648, 675, 587, + 651, 650, 0, 0, 686, 1025, 687, 688, 488, 489, + 490, 491, 1095, 714, 371, 607, 521, 522, 0, 673, + 0, 0, 0, 0, 0, 0, 0, 0, 678, 679, + 676, 797, 0, 737, 738, 0, 0, 601, 602, 403, + 0, 620, 411, 370, 505, 405, 585, 436, 0, 613, + 680, 614, 525, 526, 741, 746, 742, 743, 745, 766, + 495, 425, 431, 542, 446, 468, 528, 584, 503, 533, + 368, 573, 544, 474, 665, 693, 1118, 1090, 1117, 1119, + 1120, 1116, 1121, 1122, 1102, 979, 0, 1032, 1033, 1114, + 1113, 1115, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 722, 721, 720, 719, 718, 717, 716, + 715, 0, 0, 662, 385, 785, 779, 786, 767, 770, + 769, 744, 986, 341, 642, 466, 520, 402, 709, 710, + 0, 765, 1080, 1043, 1044, 1045, 976, 1046, 1040, 1041, + 977, 1042, 1081, 1030, 1077, 1078, 1005, 1035, 1047, 1076, + 1048, 1079, 1006, 1082, 1123, 1124, 1054, 1038, 302, 1125, + 1051, 1083, 1075, 1074, 1049, 1031, 1084, 1085, 1013, 1008, + 1052, 1053, 1039, 1060, 1061, 1062, 1065, 978, 1066, 1067, + 1068, 1069, 1070, 1064, 1063, 1027, 1028, 1029, 1055, 1056, + 1058, 1059, 1034, 552, 1009, 1010, 1011, 1012, 0, 0, + 591, 592, 593, 616, 0, 594, 575, 641, 412, 342, + 556, 583, 782, 783, 0, 0, 0, 0, 0, 0, + 0, 692, 703, 739, 740, 0, 753, 754, 756, 758, + 1071, 760, 549, 550, 768, 0, 0, 437, 358, 359, + 496, 497, 535, 0, 438, 439, 440, 444, 441, 442, + 443, 1057, 763, 764, 761, 470, 534, 557, 543, 781, + 483, 0, 789, 631, 632, 790, 749, 748, 343, 0, + 1036, 1037, 706, 707, 1023, 0, 1104, 482, 638, 0, + 971, 0, 0, 502, 0, 0, 647, 681, 670, 759, + 634, 0, 0, 0, 0, 0, 0, 974, 0, 0, + 0, 395, 2323, 0, 465, 685, 666, 677, 667, 508, + 652, 653, 654, 661, 407, 655, 791, 657, 626, 658, + 627, 659, 660, 1014, 684, 633, 545, 484, 0, 701, + 0, 0, 1096, 1105, 0, 0, 0, 0, 0, 0, + 0, 0, 1092, 0, 2572, 0, 0, 0, 965, 0, + 0, 1003, 1073, 1072, 990, 1000, 0, 0, 366, 269, + 628, 755, 630, 629, 991, 0, 992, 996, 999, 995, + 993, 994, 0, 1087, 0, 0, 0, 0, 0, 0, + 957, 969, 0, 975, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 966, + 967, 0, 0, 0, 0, 1024, 0, 968, 0, 970, + 0, 0, 0, 0, 546, 574, 0, 588, 0, 434, + 435, 2573, 997, 1001, 0, 0, 0, 0, 350, 553, + 571, 367, 540, 586, 372, 548, 564, 362, 501, 536, + 0, 0, 352, 569, 547, 479, 351, 0, 529, 392, + 409, 389, 499, 998, 0, 1022, 1026, 388, 1111, 1020, + 579, 354, 0, 578, 498, 471, 565, 570, 480, 473, + 0, 353, 567, 478, 472, 448, 399, 1112, 449, 450, + 451, 452, 453, 454, 413, 514, 469, 515, 414, 486, + 485, 487, 415, 416, 417, 418, 419, 420, 421, 422, + 423, 424, 0, 0, 0, 0, 0, 610, 611, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 747, 1017, 0, 752, 0, 582, + 0, 0, 1094, 0, 0, 0, 551, 0, 0, 455, + 0, 0, 0, 1021, 0, 532, 504, 1108, 0, 0, + 530, 467, 566, 516, 572, 554, 580, 523, 517, 344, + 555, 391, 481, 363, 365, 780, 393, 396, 400, 401, + 492, 493, 510, 539, 558, 559, 560, 390, 377, 531, + 378, 410, 379, 345, 382, 381, 383, 541, 384, 347, + 511, 563, 0, 406, 527, 476, 348, 475, 512, 562, + 561, 364, 590, 597, 598, 689, 0, 603, 794, 795, + 796, 612, 0, 518, 360, 357, 0, 0, 0, 386, + 513, 373, 375, 376, 374, 507, 509, 617, 618, 619, + 621, 0, 622, 623, 0, 0, 0, 0, 624, 690, + 708, 674, 643, 605, 698, 640, 644, 645, 427, 428, + 429, 430, 711, 0, 0, 0, 596, 456, 457, 0, + 398, 397, 477, 349, 0, 0, 445, 426, 519, 355, + 394, 447, 432, 458, 459, 460, 461, 462, 0, 463, + 464, 404, 339, 340, 788, 1091, 500, 713, 750, 751, + 635, 0, 1107, 1086, 1088, 1089, 1093, 1097, 1098, 1099, + 1100, 1101, 1103, 1106, 1110, 787, 0, 691, 705, 792, + 704, 784, 506, 0, 538, 702, 649, 0, 695, 668, + 669, 0, 696, 664, 700, 356, 433, 524, 581, 0, + 637, 0, 606, 609, 639, 726, 727, 728, 346, 608, + 730, 731, 732, 733, 734, 735, 736, 729, 1109, 672, + 648, 675, 587, 651, 650, 0, 0, 686, 1025, 687, + 688, 488, 489, 490, 491, 1095, 714, 371, 607, 521, + 522, 0, 673, 0, 0, 0, 0, 0, 0, 0, + 0, 678, 679, 676, 797, 0, 737, 738, 0, 0, + 601, 602, 403, 0, 620, 411, 370, 505, 405, 585, + 436, 0, 613, 680, 614, 525, 526, 741, 746, 742, + 743, 745, 766, 495, 425, 431, 542, 446, 468, 528, + 584, 503, 533, 368, 573, 544, 474, 665, 693, 1118, + 1090, 1117, 1119, 1120, 1116, 1121, 1122, 1102, 979, 0, + 1032, 1033, 1114, 1113, 1115, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 722, 721, 720, 719, + 718, 717, 716, 715, 0, 0, 662, 385, 785, 779, + 786, 767, 770, 769, 744, 986, 341, 642, 466, 520, + 402, 709, 710, 0, 765, 1080, 1043, 1044, 1045, 976, + 1046, 1040, 1041, 977, 1042, 1081, 1030, 1077, 1078, 1005, + 1035, 1047, 1076, 1048, 1079, 1006, 1082, 1123, 1124, 1054, + 1038, 302, 1125, 1051, 1083, 1075, 1074, 1049, 1031, 1084, + 1085, 1013, 1008, 1052, 1053, 1039, 1060, 1061, 1062, 1065, + 978, 1066, 1067, 1068, 1069, 1070, 1064, 1063, 1027, 1028, + 1029, 1055, 1056, 1058, 1059, 1034, 552, 1009, 1010, 1011, + 1012, 0, 0, 591, 592, 593, 616, 0, 594, 575, + 641, 412, 342, 556, 583, 782, 783, 0, 0, 0, + 0, 0, 0, 0, 692, 703, 739, 740, 0, 753, + 754, 756, 758, 1071, 760, 549, 550, 768, 0, 0, + 437, 358, 359, 496, 497, 535, 0, 438, 439, 440, + 444, 441, 442, 443, 1057, 763, 764, 761, 470, 534, + 557, 543, 781, 483, 0, 789, 631, 632, 790, 749, + 748, 343, 0, 1036, 1037, 706, 707, 1023, 0, 1104, + 482, 638, 0, 971, 0, 0, 502, 0, 0, 647, + 681, 670, 759, 634, 0, 0, 0, 0, 0, 0, + 974, 0, 0, 0, 395, 5333, 0, 465, 685, 666, + 677, 667, 508, 652, 653, 654, 661, 407, 655, 791, + 657, 626, 658, 627, 659, 660, 1014, 684, 633, 545, + 484, 0, 701, 0, 0, 1096, 1105, 0, 0, 0, + 0, 0, 0, 0, 0, 1092, 0, 0, 0, 0, + 0, 965, 0, 0, 1003, 1073, 1072, 990, 1000, 0, + 0, 366, 269, 628, 755, 630, 629, 991, 0, 992, + 996, 999, 995, 993, 994, 0, 1087, 0, 0, 0, + 0, 0, 0, 957, 969, 0, 975, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 966, 967, 0, 0, 0, 0, 1024, 0, + 968, 0, 970, 0, 0, 0, 0, 546, 574, 0, + 588, 0, 434, 435, 1019, 997, 1001, 0, 0, 0, + 0, 350, 553, 571, 367, 540, 586, 372, 548, 564, + 362, 501, 536, 0, 0, 352, 569, 547, 479, 351, + 0, 529, 392, 409, 389, 499, 998, 0, 1022, 1026, + 388, 1111, 1020, 579, 354, 0, 578, 498, 471, 565, + 570, 480, 473, 0, 353, 567, 478, 472, 448, 399, + 1112, 449, 450, 451, 452, 453, 454, 413, 514, 469, + 515, 414, 486, 485, 487, 415, 416, 417, 418, 419, + 420, 421, 422, 423, 424, 0, 0, 0, 0, 0, + 610, 611, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 747, 1017, 0, + 752, 0, 582, 0, 0, 1094, 0, 0, 0, 551, + 0, 0, 455, 0, 0, 0, 1021, 0, 532, 504, + 1108, 0, 0, 530, 467, 566, 516, 572, 554, 580, + 523, 517, 344, 555, 391, 481, 363, 365, 780, 393, + 396, 400, 401, 492, 493, 510, 539, 558, 559, 560, + 390, 377, 531, 378, 410, 379, 345, 382, 381, 383, + 541, 384, 347, 511, 563, 0, 406, 527, 476, 348, + 475, 512, 562, 561, 364, 590, 597, 598, 689, 0, + 603, 794, 795, 796, 612, 0, 518, 360, 357, 0, + 0, 0, 386, 513, 373, 375, 376, 374, 507, 509, + 617, 618, 619, 621, 0, 622, 623, 0, 0, 0, + 0, 624, 690, 708, 674, 643, 605, 698, 640, 644, + 645, 427, 428, 429, 430, 711, 0, 0, 0, 596, + 456, 457, 0, 398, 397, 477, 349, 0, 0, 445, + 426, 519, 355, 394, 447, 432, 458, 459, 460, 461, + 462, 0, 463, 464, 404, 339, 340, 788, 1091, 500, + 713, 750, 751, 635, 0, 1107, 1086, 1088, 1089, 1093, + 1097, 1098, 1099, 1100, 1101, 1103, 1106, 1110, 787, 0, + 691, 705, 792, 704, 784, 506, 0, 538, 702, 649, + 0, 695, 668, 669, 0, 696, 664, 700, 356, 433, + 524, 581, 0, 637, 0, 606, 609, 639, 726, 727, + 728, 346, 608, 730, 731, 732, 733, 734, 735, 736, + 729, 1109, 672, 648, 675, 587, 651, 650, 0, 0, + 686, 1025, 687, 688, 488, 489, 490, 491, 1095, 714, + 371, 607, 521, 522, 0, 673, 0, 0, 0, 0, + 0, 0, 0, 0, 678, 679, 676, 797, 0, 737, + 738, 0, 0, 601, 602, 403, 0, 620, 411, 370, + 505, 405, 585, 436, 0, 613, 680, 614, 525, 526, + 741, 746, 742, 743, 745, 766, 495, 425, 431, 542, + 446, 468, 528, 584, 503, 533, 368, 573, 544, 474, + 665, 693, 1118, 1090, 1117, 1119, 1120, 1116, 1121, 1122, + 1102, 979, 0, 1032, 1033, 1114, 1113, 1115, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 722, + 721, 720, 719, 718, 717, 716, 715, 0, 0, 662, + 385, 785, 779, 786, 767, 770, 769, 744, 986, 341, + 642, 466, 520, 402, 709, 710, 0, 765, 1080, 1043, + 1044, 1045, 976, 1046, 1040, 1041, 977, 1042, 1081, 1030, + 1077, 1078, 1005, 1035, 1047, 1076, 1048, 1079, 1006, 1082, + 1123, 1124, 1054, 1038, 302, 1125, 1051, 1083, 1075, 1074, + 1049, 1031, 1084, 1085, 1013, 1008, 1052, 1053, 1039, 1060, + 1061, 1062, 1065, 978, 1066, 1067, 1068, 1069, 1070, 1064, + 1063, 1027, 1028, 1029, 1055, 1056, 1058, 1059, 1034, 552, + 1009, 1010, 1011, 1012, 0, 0, 591, 592, 593, 616, + 0, 594, 575, 641, 412, 342, 556, 583, 782, 783, + 0, 0, 0, 0, 0, 0, 0, 692, 703, 739, + 740, 0, 753, 754, 756, 758, 1071, 760, 549, 550, + 768, 0, 0, 437, 358, 359, 496, 497, 535, 0, + 438, 439, 440, 444, 441, 442, 443, 1057, 763, 764, + 761, 470, 534, 557, 543, 781, 483, 0, 789, 631, + 632, 790, 749, 748, 343, 0, 1036, 1037, 706, 707, + 1023, 0, 1104, 482, 638, 0, 971, 0, 0, 502, + 0, 0, 647, 681, 670, 759, 634, 0, 0, 0, + 0, 0, 0, 974, 0, 0, 0, 395, 0, 0, + 465, 685, 666, 677, 667, 508, 652, 653, 654, 661, + 407, 655, 791, 657, 626, 658, 627, 659, 660, 1014, + 684, 633, 545, 484, 0, 701, 0, 0, 1096, 1105, + 0, 0, 0, 0, 0, 0, 0, 0, 1092, 0, + 0, 0, 0, 0, 965, 0, 0, 1003, 1073, 1072, + 990, 1000, 0, 0, 366, 269, 628, 755, 630, 629, + 991, 0, 992, 996, 999, 995, 993, 994, 0, 1087, + 0, 0, 0, 0, 0, 0, 957, 969, 0, 975, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 966, 967, 0, 0, 0, + 0, 1024, 0, 968, 0, 970, 0, 0, 0, 0, + 546, 574, 0, 588, 0, 434, 435, 1019, 997, 1001, + 0, 0, 0, 0, 350, 553, 571, 367, 540, 586, + 372, 548, 564, 362, 501, 536, 0, 0, 352, 569, + 547, 479, 351, 0, 529, 392, 409, 389, 499, 998, + 0, 1022, 1026, 388, 1111, 1020, 579, 354, 0, 578, + 498, 471, 565, 570, 480, 473, 0, 353, 567, 478, + 472, 448, 399, 1112, 449, 450, 451, 452, 453, 454, + 413, 514, 469, 515, 414, 486, 485, 487, 415, 416, + 417, 418, 419, 420, 421, 422, 423, 424, 0, 0, + 0, 0, 0, 610, 611, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 747, 1017, 0, 752, 0, 582, 0, 0, 1094, 0, + 0, 0, 551, 0, 0, 455, 0, 0, 0, 1021, + 0, 532, 504, 1108, 5146, 0, 530, 467, 566, 516, + 572, 554, 580, 523, 517, 344, 555, 391, 481, 363, + 365, 780, 393, 396, 400, 401, 492, 493, 510, 539, + 558, 559, 560, 390, 377, 531, 378, 410, 379, 345, + 382, 381, 383, 541, 384, 347, 511, 563, 0, 406, + 527, 476, 348, 475, 512, 562, 561, 364, 590, 597, + 598, 689, 0, 603, 794, 795, 796, 612, 0, 518, + 360, 357, 0, 0, 0, 386, 513, 373, 375, 376, + 374, 507, 509, 617, 618, 619, 621, 0, 622, 623, + 0, 0, 0, 0, 624, 690, 708, 674, 643, 605, + 698, 640, 644, 645, 427, 428, 429, 430, 711, 0, + 0, 0, 596, 456, 457, 0, 398, 397, 477, 349, + 0, 0, 445, 426, 519, 355, 394, 447, 432, 458, + 459, 460, 461, 462, 0, 463, 464, 404, 339, 340, + 788, 1091, 500, 713, 750, 751, 635, 0, 1107, 1086, + 1088, 1089, 1093, 1097, 1098, 1099, 1100, 1101, 1103, 1106, + 1110, 787, 0, 691, 705, 792, 704, 784, 506, 0, + 538, 702, 649, 0, 695, 668, 669, 0, 696, 664, + 700, 356, 433, 524, 581, 0, 637, 0, 606, 609, + 639, 726, 727, 728, 346, 608, 730, 731, 732, 733, + 734, 735, 736, 729, 1109, 672, 648, 675, 587, 651, + 650, 0, 0, 686, 1025, 687, 688, 488, 489, 490, + 491, 1095, 714, 371, 607, 521, 522, 0, 673, 0, + 0, 0, 0, 0, 0, 0, 0, 678, 679, 676, + 797, 0, 737, 738, 0, 0, 601, 602, 403, 0, + 620, 411, 370, 505, 405, 585, 436, 0, 613, 680, + 614, 525, 526, 741, 746, 742, 743, 745, 766, 495, + 425, 431, 542, 446, 468, 528, 584, 503, 533, 368, + 573, 544, 474, 665, 693, 1118, 1090, 1117, 1119, 1120, + 1116, 1121, 1122, 1102, 979, 0, 1032, 1033, 1114, 1113, + 1115, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 722, 721, 720, 719, 718, 717, 716, 715, + 0, 0, 662, 385, 785, 779, 786, 767, 770, 769, + 744, 986, 341, 642, 466, 520, 402, 709, 710, 0, + 765, 1080, 1043, 1044, 1045, 976, 1046, 1040, 1041, 977, + 1042, 1081, 1030, 1077, 1078, 1005, 1035, 1047, 1076, 1048, + 1079, 1006, 1082, 1123, 1124, 1054, 1038, 302, 1125, 1051, + 1083, 1075, 1074, 1049, 1031, 1084, 1085, 1013, 1008, 1052, + 1053, 1039, 1060, 1061, 1062, 1065, 978, 1066, 1067, 1068, + 1069, 1070, 1064, 1063, 1027, 1028, 1029, 1055, 1056, 1058, + 1059, 1034, 552, 1009, 1010, 1011, 1012, 0, 0, 591, + 592, 593, 616, 0, 594, 575, 641, 412, 342, 556, + 583, 782, 783, 0, 0, 0, 0, 0, 0, 0, + 692, 703, 739, 740, 0, 753, 754, 756, 758, 1071, + 760, 549, 550, 768, 0, 0, 437, 358, 359, 496, + 497, 535, 0, 438, 439, 440, 444, 441, 442, 443, + 1057, 763, 764, 761, 470, 534, 557, 543, 781, 483, + 0, 789, 631, 632, 790, 749, 748, 343, 0, 1036, + 1037, 706, 707, 1023, 0, 1104, 482, 638, 0, 971, + 0, 0, 502, 0, 0, 647, 681, 670, 759, 634, + 0, 0, 0, 0, 0, 0, 974, 0, 0, 0, + 395, 0, 0, 465, 685, 666, 677, 667, 508, 652, + 653, 654, 661, 407, 655, 791, 657, 626, 658, 627, + 659, 660, 1014, 684, 633, 545, 484, 0, 701, 0, + 0, 1096, 1105, 0, 0, 0, 0, 0, 0, 0, + 0, 1092, 0, 0, 0, 0, 0, 965, 0, 0, + 1003, 1073, 1072, 990, 1000, 0, 0, 366, 269, 628, + 755, 630, 629, 991, 0, 992, 996, 999, 995, 993, + 994, 0, 1087, 0, 0, 0, 0, 0, 0, 957, + 969, 0, 975, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 966, 967, + 1921, 0, 0, 0, 1024, 0, 968, 0, 970, 0, + 0, 0, 0, 546, 574, 0, 588, 0, 434, 435, + 1019, 997, 1001, 0, 0, 0, 0, 350, 553, 571, + 367, 540, 586, 372, 548, 564, 362, 501, 536, 0, + 0, 352, 569, 547, 479, 351, 0, 529, 392, 409, + 389, 499, 998, 0, 1022, 1026, 388, 1111, 1020, 579, + 354, 0, 578, 498, 471, 565, 570, 480, 473, 0, + 353, 567, 478, 472, 448, 399, 1112, 449, 450, 451, + 452, 453, 454, 413, 514, 469, 515, 414, 486, 485, + 487, 415, 416, 417, 418, 419, 420, 421, 422, 423, + 424, 0, 0, 0, 0, 0, 610, 611, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 747, 1017, 0, 752, 0, 582, 0, + 0, 1094, 0, 0, 0, 551, 0, 0, 455, 0, + 0, 0, 1021, 0, 532, 504, 1108, 0, 0, 530, + 467, 566, 516, 572, 554, 580, 523, 517, 344, 555, + 391, 481, 363, 365, 780, 393, 396, 400, 401, 492, + 493, 510, 539, 558, 559, 560, 390, 377, 531, 378, + 410, 379, 345, 382, 381, 383, 541, 384, 347, 511, + 563, 0, 406, 527, 476, 348, 475, 512, 562, 561, + 364, 590, 597, 598, 689, 0, 603, 794, 795, 796, + 612, 0, 518, 360, 357, 0, 0, 0, 386, 513, + 373, 375, 376, 374, 507, 509, 617, 618, 619, 621, + 0, 622, 623, 0, 0, 0, 0, 624, 690, 708, + 674, 643, 605, 698, 640, 644, 645, 427, 428, 429, + 430, 711, 0, 0, 0, 596, 456, 457, 0, 398, + 397, 477, 349, 0, 0, 445, 426, 519, 355, 394, + 447, 432, 458, 459, 460, 461, 462, 0, 463, 464, + 404, 339, 340, 788, 1091, 500, 713, 750, 751, 635, + 0, 1107, 1086, 1088, 1089, 1093, 1097, 1098, 1099, 1100, + 1101, 1103, 1106, 1110, 787, 0, 691, 705, 792, 704, + 784, 506, 0, 538, 702, 649, 0, 695, 668, 669, + 0, 696, 664, 700, 356, 433, 524, 581, 0, 637, + 0, 606, 609, 639, 726, 727, 728, 346, 608, 730, + 731, 732, 733, 734, 735, 736, 729, 1109, 672, 648, + 675, 587, 651, 650, 0, 0, 686, 1025, 687, 688, + 488, 489, 490, 491, 1095, 714, 371, 607, 521, 522, + 0, 673, 0, 0, 0, 0, 0, 0, 0, 0, + 678, 679, 676, 797, 0, 737, 738, 0, 0, 601, + 602, 403, 0, 620, 411, 370, 505, 405, 585, 436, + 0, 613, 680, 614, 525, 526, 741, 746, 742, 743, + 745, 766, 495, 425, 431, 542, 446, 468, 528, 584, + 503, 533, 368, 573, 544, 474, 665, 693, 1118, 1090, + 1117, 1119, 1120, 1116, 1121, 1122, 1102, 979, 0, 1032, + 1033, 1114, 1113, 1115, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 722, 721, 720, 719, 718, + 717, 716, 715, 0, 0, 662, 385, 785, 779, 786, + 767, 770, 769, 744, 986, 341, 642, 466, 520, 402, + 709, 710, 0, 765, 1080, 1043, 1044, 1045, 976, 1046, + 1040, 1041, 977, 1042, 1081, 1030, 1077, 1078, 1005, 1035, + 1047, 1076, 1048, 1079, 1006, 1082, 1123, 1124, 1054, 1038, + 302, 1125, 1051, 1083, 1075, 1074, 1049, 1031, 1084, 1085, + 1013, 1008, 1052, 1053, 1039, 1060, 1061, 1062, 1065, 978, + 1066, 1067, 1068, 1069, 1070, 1064, 1063, 1027, 1028, 1029, + 1055, 1056, 1058, 1059, 1034, 552, 1009, 1010, 1011, 1012, + 0, 0, 591, 592, 593, 616, 0, 594, 575, 641, + 412, 342, 556, 583, 782, 783, 0, 0, 0, 0, + 0, 0, 0, 692, 703, 739, 740, 0, 753, 754, + 756, 758, 1071, 760, 549, 550, 768, 0, 0, 437, + 358, 359, 496, 497, 535, 0, 438, 439, 440, 444, + 441, 442, 443, 1057, 763, 764, 761, 470, 534, 557, + 543, 781, 483, 0, 789, 631, 632, 790, 749, 748, + 343, 0, 1036, 1037, 706, 707, 1023, 0, 1104, 482, + 638, 0, 971, 0, 0, 502, 0, 0, 647, 681, + 670, 759, 634, 0, 0, 0, 0, 0, 0, 974, + 0, 0, 0, 395, 2323, 0, 465, 685, 666, 677, + 667, 508, 652, 653, 654, 661, 407, 655, 791, 657, + 626, 658, 627, 659, 660, 1014, 684, 633, 545, 484, + 0, 701, 0, 0, 1096, 1105, 0, 0, 0, 0, + 0, 0, 0, 0, 1092, 0, 0, 0, 0, 0, + 965, 0, 0, 1003, 1073, 1072, 990, 1000, 0, 0, + 366, 269, 628, 755, 630, 629, 991, 0, 992, 996, + 999, 995, 993, 994, 0, 1087, 0, 0, 0, 0, + 0, 0, 957, 969, 0, 975, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 966, 967, 0, 0, 0, 0, 1024, 0, 968, + 0, 970, 0, 0, 0, 0, 546, 574, 0, 588, + 0, 434, 435, 1019, 997, 1001, 0, 0, 0, 0, + 350, 553, 571, 367, 540, 586, 372, 548, 564, 362, + 501, 536, 0, 0, 352, 569, 547, 479, 351, 0, + 529, 392, 409, 389, 499, 998, 0, 1022, 1026, 388, + 1111, 1020, 579, 354, 0, 578, 498, 471, 565, 570, + 480, 473, 0, 353, 567, 478, 472, 448, 399, 1112, + 449, 450, 451, 452, 453, 454, 413, 514, 469, 515, + 414, 486, 485, 487, 415, 416, 417, 418, 419, 420, + 421, 422, 423, 424, 0, 0, 0, 0, 0, 610, + 611, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 747, 1017, 0, 752, + 0, 582, 0, 0, 1094, 0, 0, 0, 551, 0, + 0, 455, 0, 0, 0, 1021, 0, 532, 504, 1108, + 0, 0, 530, 467, 566, 516, 572, 554, 580, 523, + 517, 344, 555, 391, 481, 363, 365, 780, 393, 396, + 400, 401, 492, 493, 510, 539, 558, 559, 560, 390, + 377, 531, 378, 410, 379, 345, 382, 381, 383, 541, + 384, 347, 511, 563, 0, 406, 527, 476, 348, 475, + 512, 562, 561, 364, 590, 597, 598, 689, 0, 603, + 794, 795, 796, 612, 0, 518, 360, 357, 0, 0, + 0, 386, 513, 373, 375, 376, 374, 507, 509, 617, + 618, 619, 621, 0, 622, 623, 0, 0, 0, 0, + 624, 690, 708, 674, 643, 605, 698, 640, 644, 645, + 427, 428, 429, 430, 711, 0, 0, 0, 596, 456, + 457, 0, 398, 397, 477, 349, 0, 0, 445, 426, + 519, 355, 394, 447, 432, 458, 459, 460, 461, 462, + 0, 463, 464, 404, 339, 340, 788, 1091, 500, 713, + 750, 751, 635, 0, 1107, 1086, 1088, 1089, 1093, 1097, + 1098, 1099, 1100, 1101, 1103, 1106, 1110, 787, 0, 691, + 705, 792, 704, 784, 506, 0, 538, 702, 649, 0, + 695, 668, 669, 0, 696, 664, 700, 356, 433, 524, + 581, 0, 637, 0, 606, 609, 639, 726, 727, 728, + 346, 608, 730, 731, 732, 733, 734, 735, 736, 729, + 1109, 672, 648, 675, 587, 651, 650, 0, 0, 686, + 1025, 687, 688, 488, 489, 490, 491, 1095, 714, 371, + 607, 521, 522, 0, 673, 0, 0, 0, 0, 0, + 0, 0, 0, 678, 679, 676, 797, 0, 737, 738, + 0, 0, 601, 602, 403, 0, 620, 411, 370, 505, + 405, 585, 436, 0, 613, 680, 614, 525, 526, 741, + 746, 742, 743, 745, 766, 495, 425, 431, 542, 446, + 468, 528, 584, 503, 533, 368, 573, 544, 474, 665, + 693, 1118, 1090, 1117, 1119, 1120, 1116, 1121, 1122, 1102, + 979, 0, 1032, 1033, 1114, 1113, 1115, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 722, 721, + 720, 719, 718, 717, 716, 715, 0, 0, 662, 385, + 785, 779, 786, 767, 770, 769, 744, 986, 341, 642, + 466, 520, 402, 709, 710, 0, 765, 1080, 1043, 1044, + 1045, 976, 1046, 1040, 1041, 977, 1042, 1081, 1030, 1077, + 1078, 1005, 1035, 1047, 1076, 1048, 1079, 1006, 1082, 1123, + 1124, 1054, 1038, 302, 1125, 1051, 1083, 1075, 1074, 1049, + 1031, 1084, 1085, 1013, 1008, 1052, 1053, 1039, 1060, 1061, + 1062, 1065, 978, 1066, 1067, 1068, 1069, 1070, 1064, 1063, + 1027, 1028, 1029, 1055, 1056, 1058, 1059, 1034, 552, 1009, + 1010, 1011, 1012, 0, 0, 591, 592, 593, 616, 0, + 594, 575, 641, 412, 342, 556, 583, 782, 783, 0, + 0, 0, 0, 0, 0, 0, 692, 703, 739, 740, + 0, 753, 754, 756, 758, 1071, 760, 549, 550, 768, + 0, 0, 437, 358, 359, 496, 497, 535, 0, 438, + 439, 440, 444, 441, 442, 443, 1057, 763, 764, 761, + 470, 534, 557, 543, 781, 483, 0, 789, 631, 632, + 790, 749, 748, 343, 0, 1036, 1037, 706, 707, 0, + 0, 1104, 482, 638, 1023, 971, 0, 2813, 0, 0, + 0, 0, 0, 502, 0, 0, 647, 681, 670, 759, + 634, 0, 0, 0, 0, 0, 0, 974, 0, 0, + 0, 395, 0, 0, 465, 685, 666, 677, 667, 508, + 652, 653, 654, 661, 407, 655, 791, 657, 626, 658, + 627, 659, 660, 1014, 684, 633, 545, 484, 0, 701, + 0, 0, 1096, 1105, 0, 0, 0, 0, 0, 0, + 0, 0, 1092, 0, 0, 0, 0, 0, 965, 0, + 0, 1003, 1073, 1072, 990, 1000, 0, 0, 366, 269, + 628, 755, 630, 629, 991, 0, 992, 996, 999, 995, + 993, 994, 0, 1087, 0, 0, 0, 0, 0, 0, + 957, 969, 0, 975, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 966, + 967, 0, 0, 0, 0, 1024, 0, 968, 0, 970, + 0, 0, 0, 0, 546, 574, 0, 588, 0, 434, + 435, 1019, 997, 1001, 0, 0, 0, 0, 350, 553, + 571, 367, 540, 586, 372, 548, 564, 362, 501, 536, + 0, 0, 352, 569, 547, 479, 351, 0, 529, 392, + 409, 389, 499, 998, 0, 1022, 1026, 388, 1111, 1020, + 579, 354, 0, 578, 498, 471, 565, 570, 480, 473, + 0, 353, 567, 478, 472, 448, 399, 1112, 449, 450, + 451, 452, 453, 454, 413, 514, 469, 515, 414, 486, + 485, 487, 415, 416, 417, 418, 419, 420, 421, 422, + 423, 424, 0, 0, 0, 0, 0, 610, 611, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 747, 1017, 0, 752, 0, 582, + 0, 0, 1094, 0, 0, 0, 551, 0, 0, 455, + 0, 0, 0, 1021, 0, 532, 504, 1108, 0, 0, + 530, 467, 566, 516, 572, 554, 580, 523, 517, 344, + 555, 391, 481, 363, 365, 780, 393, 396, 400, 401, + 492, 493, 510, 539, 558, 559, 560, 390, 377, 531, + 378, 410, 379, 345, 382, 381, 383, 541, 384, 347, + 511, 563, 0, 406, 527, 476, 348, 475, 512, 562, + 561, 364, 590, 597, 598, 689, 0, 603, 794, 795, + 796, 612, 0, 518, 360, 357, 0, 0, 0, 386, + 513, 373, 375, 376, 374, 507, 509, 617, 618, 619, + 621, 0, 622, 623, 0, 0, 0, 0, 624, 690, + 708, 674, 643, 605, 698, 640, 644, 645, 427, 428, + 429, 430, 711, 0, 0, 0, 596, 456, 457, 0, + 398, 397, 477, 349, 0, 0, 445, 426, 519, 355, + 394, 447, 432, 458, 459, 460, 461, 462, 0, 463, + 464, 404, 339, 340, 788, 1091, 500, 713, 750, 751, + 635, 0, 1107, 1086, 1088, 1089, 1093, 1097, 1098, 1099, + 1100, 1101, 1103, 1106, 1110, 787, 0, 691, 705, 792, + 704, 784, 506, 0, 538, 702, 649, 0, 695, 668, + 669, 0, 696, 664, 700, 356, 433, 524, 581, 0, + 637, 0, 606, 609, 639, 726, 727, 728, 346, 608, + 730, 731, 732, 733, 734, 735, 736, 729, 1109, 672, + 648, 675, 587, 651, 650, 0, 0, 686, 1025, 687, + 688, 488, 489, 490, 491, 1095, 714, 371, 607, 521, + 522, 0, 673, 0, 0, 0, 0, 0, 0, 0, + 0, 678, 679, 676, 797, 0, 737, 738, 0, 0, + 601, 602, 403, 0, 620, 411, 370, 505, 405, 585, + 436, 0, 613, 680, 614, 525, 526, 741, 746, 742, + 743, 745, 766, 495, 425, 431, 542, 446, 468, 528, + 584, 503, 533, 368, 573, 544, 474, 665, 693, 1118, + 1090, 1117, 1119, 1120, 1116, 1121, 1122, 1102, 979, 0, + 1032, 1033, 1114, 1113, 1115, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 722, 721, 720, 719, + 718, 717, 716, 715, 0, 0, 662, 385, 785, 779, + 786, 767, 770, 769, 744, 986, 341, 642, 466, 520, + 402, 709, 710, 0, 765, 1080, 1043, 1044, 1045, 976, + 1046, 1040, 1041, 977, 1042, 1081, 1030, 1077, 1078, 1005, + 1035, 1047, 1076, 1048, 1079, 1006, 1082, 1123, 1124, 1054, + 1038, 302, 1125, 1051, 1083, 1075, 1074, 1049, 1031, 1084, + 1085, 1013, 1008, 1052, 1053, 1039, 1060, 1061, 1062, 1065, + 978, 1066, 1067, 1068, 1069, 1070, 1064, 1063, 1027, 1028, + 1029, 1055, 1056, 1058, 1059, 1034, 552, 1009, 1010, 1011, + 1012, 0, 0, 591, 592, 593, 616, 0, 594, 575, + 641, 412, 342, 556, 583, 782, 783, 0, 0, 0, + 0, 0, 0, 0, 692, 703, 739, 740, 0, 753, + 754, 756, 758, 1071, 760, 549, 550, 768, 0, 0, + 437, 358, 359, 496, 497, 535, 0, 438, 439, 440, + 444, 441, 442, 443, 1057, 763, 764, 761, 470, 534, + 557, 543, 781, 483, 0, 789, 631, 632, 790, 749, + 748, 343, 0, 1036, 1037, 706, 707, 1023, 0, 1104, + 482, 638, 0, 971, 0, 0, 502, 0, 0, 647, + 681, 670, 759, 634, 0, 0, 0, 0, 0, 0, + 974, 0, 0, 0, 395, 0, 0, 465, 685, 666, + 677, 667, 508, 652, 653, 654, 661, 407, 655, 791, + 657, 626, 658, 627, 659, 660, 1014, 684, 633, 545, + 484, 0, 701, 0, 0, 1096, 1105, 0, 0, 0, + 0, 0, 0, 0, 0, 1092, 0, 0, 0, 0, + 0, 965, 0, 0, 1003, 1073, 1072, 990, 1000, 0, + 0, 366, 269, 628, 755, 630, 629, 991, 0, 992, + 996, 999, 995, 993, 994, 0, 1087, 0, 0, 0, + 0, 0, 0, 957, 969, 0, 975, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 966, 967, 2314, 0, 0, 0, 1024, 0, + 968, 0, 970, 0, 0, 0, 0, 546, 574, 0, + 588, 0, 434, 435, 1019, 997, 1001, 0, 0, 0, + 0, 350, 553, 571, 367, 540, 586, 372, 548, 564, + 362, 501, 536, 0, 0, 352, 569, 547, 479, 351, + 0, 529, 392, 409, 389, 499, 998, 0, 1022, 1026, + 388, 1111, 1020, 579, 354, 0, 578, 498, 471, 565, + 570, 480, 473, 0, 353, 567, 478, 472, 448, 399, + 1112, 449, 450, 451, 452, 453, 454, 413, 514, 469, + 515, 414, 486, 485, 487, 415, 416, 417, 418, 419, + 420, 421, 422, 423, 424, 0, 0, 0, 0, 0, + 610, 611, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 747, 1017, 0, + 752, 0, 582, 0, 0, 1094, 0, 0, 0, 551, + 0, 0, 455, 0, 0, 0, 1021, 0, 532, 504, + 1108, 0, 0, 530, 467, 566, 516, 572, 554, 580, + 523, 517, 344, 555, 391, 481, 363, 365, 780, 393, + 396, 400, 401, 492, 493, 510, 539, 558, 559, 560, + 390, 377, 531, 378, 410, 379, 345, 382, 381, 383, + 541, 384, 347, 511, 563, 0, 406, 527, 476, 348, + 475, 512, 562, 561, 364, 590, 597, 598, 689, 0, + 603, 794, 795, 796, 612, 0, 518, 360, 357, 0, + 0, 0, 386, 513, 373, 375, 376, 374, 507, 509, + 617, 618, 619, 621, 0, 622, 623, 0, 0, 0, + 0, 624, 690, 708, 674, 643, 605, 698, 640, 644, + 645, 427, 428, 429, 430, 711, 0, 0, 0, 596, + 456, 457, 0, 398, 397, 477, 349, 0, 0, 445, + 426, 519, 355, 394, 447, 432, 458, 459, 460, 461, + 462, 0, 463, 464, 404, 339, 340, 788, 1091, 500, + 713, 750, 751, 635, 0, 1107, 1086, 1088, 1089, 1093, + 1097, 1098, 1099, 1100, 1101, 1103, 1106, 1110, 787, 0, + 691, 705, 792, 704, 784, 506, 0, 538, 702, 649, + 0, 695, 668, 669, 0, 696, 664, 700, 356, 433, + 524, 581, 0, 637, 0, 606, 609, 639, 726, 727, + 728, 346, 608, 730, 731, 732, 733, 734, 735, 736, + 729, 1109, 672, 648, 675, 587, 651, 650, 0, 0, + 686, 1025, 687, 688, 488, 489, 490, 491, 1095, 714, + 371, 607, 521, 522, 0, 673, 0, 0, 0, 0, + 0, 0, 0, 0, 678, 679, 676, 797, 0, 737, + 738, 0, 0, 601, 602, 403, 0, 620, 411, 370, + 505, 405, 585, 436, 0, 613, 680, 614, 525, 526, + 741, 746, 742, 743, 745, 766, 495, 425, 431, 542, + 446, 468, 528, 584, 503, 533, 368, 573, 544, 474, + 665, 693, 1118, 1090, 1117, 1119, 1120, 1116, 1121, 1122, + 1102, 979, 0, 1032, 1033, 1114, 1113, 1115, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 722, + 721, 720, 719, 718, 717, 716, 715, 0, 0, 662, + 385, 785, 779, 786, 767, 770, 769, 744, 986, 341, + 642, 466, 520, 402, 709, 710, 0, 765, 1080, 1043, + 1044, 1045, 976, 1046, 1040, 1041, 977, 1042, 1081, 1030, + 1077, 1078, 1005, 1035, 1047, 1076, 1048, 1079, 1006, 1082, + 1123, 1124, 1054, 1038, 302, 1125, 1051, 1083, 1075, 1074, + 1049, 1031, 1084, 1085, 1013, 1008, 1052, 1053, 1039, 1060, + 1061, 1062, 1065, 978, 1066, 1067, 1068, 1069, 1070, 1064, + 1063, 1027, 1028, 1029, 1055, 1056, 1058, 1059, 1034, 552, + 1009, 1010, 1011, 1012, 0, 0, 591, 592, 593, 616, + 0, 594, 575, 641, 412, 342, 556, 583, 782, 783, + 0, 0, 0, 0, 0, 0, 0, 692, 703, 739, + 740, 0, 753, 754, 756, 758, 1071, 760, 549, 550, + 768, 0, 0, 437, 358, 359, 496, 497, 535, 0, + 438, 439, 440, 444, 441, 442, 443, 1057, 763, 764, + 761, 470, 534, 557, 543, 781, 483, 0, 789, 631, + 632, 790, 749, 748, 343, 0, 1036, 1037, 706, 707, + 1023, 0, 1104, 482, 638, 0, 971, 0, 0, 502, + 0, 0, 647, 681, 670, 759, 634, 0, 0, 0, + 0, 0, 0, 974, 0, 0, 0, 395, 0, 0, + 465, 685, 666, 677, 667, 508, 652, 653, 654, 661, + 407, 655, 791, 657, 626, 658, 627, 659, 660, 1014, + 684, 633, 545, 484, 0, 701, 0, 0, 1096, 1105, + 0, 0, 0, 0, 0, 0, 0, 0, 1092, 0, + 0, 0, 0, 0, 965, 0, 0, 1003, 1073, 1072, + 990, 1000, 0, 0, 366, 269, 628, 755, 630, 629, + 991, 0, 992, 996, 999, 995, 993, 994, 0, 1087, + 0, 0, 0, 0, 0, 0, 957, 969, 0, 975, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 966, 967, 0, 0, 0, + 0, 1024, 0, 968, 0, 970, 0, 0, 0, 0, + 546, 574, 0, 588, 0, 434, 435, 1019, 997, 1001, + 0, 0, 0, 0, 350, 553, 571, 367, 540, 586, + 372, 548, 564, 362, 501, 536, 0, 0, 352, 569, + 547, 479, 351, 0, 529, 392, 409, 389, 499, 998, + 0, 1022, 1026, 388, 1111, 1020, 579, 354, 0, 578, + 498, 471, 565, 570, 480, 473, 0, 353, 567, 478, + 472, 448, 399, 1112, 449, 450, 451, 452, 453, 454, + 413, 514, 469, 515, 414, 486, 485, 487, 415, 416, + 417, 418, 419, 420, 421, 422, 423, 424, 0, 0, + 0, 0, 0, 610, 611, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 747, 1017, 0, 752, 0, 582, 0, 0, 1094, 0, + 0, 0, 551, 0, 0, 455, 0, 0, 0, 1021, + 0, 532, 504, 1108, 0, 0, 530, 467, 566, 516, + 572, 554, 580, 523, 517, 344, 555, 391, 481, 363, + 365, 780, 393, 396, 400, 401, 492, 493, 510, 539, + 558, 559, 560, 390, 377, 531, 378, 410, 379, 345, + 382, 381, 383, 541, 384, 347, 511, 563, 0, 406, + 527, 476, 348, 475, 512, 562, 561, 364, 590, 597, + 598, 689, 0, 603, 794, 795, 796, 612, 0, 518, + 360, 357, 0, 0, 0, 386, 513, 373, 375, 376, + 374, 507, 509, 617, 618, 619, 621, 0, 622, 623, + 0, 0, 0, 0, 624, 690, 708, 674, 643, 605, + 698, 640, 644, 645, 427, 428, 429, 430, 711, 0, + 0, 0, 596, 456, 457, 0, 398, 397, 477, 349, + 0, 0, 445, 426, 519, 355, 394, 447, 432, 458, + 459, 460, 461, 462, 0, 463, 464, 404, 339, 340, + 788, 1091, 500, 713, 750, 751, 635, 0, 1107, 1086, + 1088, 1089, 1093, 1097, 1098, 1099, 1100, 1101, 1103, 1106, + 1110, 787, 0, 691, 705, 792, 704, 784, 506, 0, + 538, 702, 649, 0, 695, 668, 669, 0, 696, 664, + 700, 356, 433, 524, 581, 0, 637, 0, 606, 609, + 639, 726, 727, 728, 346, 608, 730, 731, 732, 733, + 734, 735, 736, 729, 1109, 672, 648, 675, 587, 651, + 650, 0, 0, 686, 1025, 687, 688, 488, 489, 490, + 491, 1095, 714, 371, 607, 521, 522, 0, 673, 0, + 0, 0, 0, 0, 0, 0, 0, 678, 679, 676, + 797, 0, 737, 738, 0, 0, 601, 602, 403, 0, + 620, 411, 370, 505, 405, 585, 436, 0, 613, 680, + 614, 525, 526, 741, 746, 742, 743, 745, 766, 495, + 425, 431, 542, 446, 468, 528, 584, 503, 533, 368, + 573, 544, 474, 665, 693, 1118, 1090, 1117, 1119, 1120, + 1116, 1121, 1122, 1102, 979, 0, 1032, 1033, 1114, 1113, + 1115, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 722, 721, 720, 719, 718, 717, 716, 715, + 0, 0, 662, 385, 785, 779, 786, 767, 770, 769, + 744, 986, 341, 642, 466, 520, 402, 709, 710, 0, + 765, 1080, 1043, 1044, 1045, 976, 1046, 1040, 1041, 977, + 1042, 1081, 1030, 1077, 1078, 1005, 1035, 1047, 1076, 1048, + 1079, 1006, 1082, 1123, 1124, 1054, 1038, 302, 1125, 1051, + 1083, 1075, 1074, 1049, 1031, 1084, 1085, 1013, 1008, 1052, + 1053, 1039, 1060, 1061, 1062, 1065, 978, 1066, 1067, 1068, + 1069, 1070, 1064, 1063, 1027, 1028, 1029, 1055, 1056, 1058, + 1059, 1034, 552, 1009, 1010, 1011, 1012, 0, 0, 591, + 592, 593, 616, 0, 594, 575, 641, 412, 342, 556, + 583, 782, 783, 0, 0, 0, 0, 0, 0, 0, + 692, 703, 739, 740, 0, 753, 754, 756, 758, 1071, + 760, 549, 550, 768, 0, 0, 437, 358, 359, 496, + 497, 535, 0, 438, 439, 440, 444, 441, 442, 443, + 1057, 763, 764, 761, 470, 534, 557, 543, 781, 483, + 0, 789, 631, 632, 790, 749, 748, 343, 0, 1036, + 1037, 706, 707, 1023, 0, 1104, 482, 638, 0, 971, + 0, 0, 502, 0, 0, 647, 681, 670, 759, 634, + 0, 0, 0, 0, 0, 0, 974, 0, 0, 0, + 395, 0, 0, 465, 685, 666, 677, 667, 508, 652, + 653, 654, 661, 407, 655, 791, 657, 626, 658, 627, + 659, 660, 1014, 684, 633, 545, 484, 0, 701, 0, + 0, 1096, 1105, 0, 0, 0, 0, 0, 0, 0, + 0, 1092, 0, 0, 0, 0, 0, 965, 0, 0, + 1003, 1073, 1072, 990, 1000, 0, 0, 366, 269, 628, + 755, 630, 629, 991, 0, 992, 996, 999, 995, 993, + 994, 0, 1087, 0, 0, 0, 0, 0, 0, 957, + 969, 0, 975, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 966, 967, + 0, 0, 0, 0, 1024, 0, 968, 0, 970, 0, + 0, 0, 0, 546, 574, 0, 588, 0, 434, 435, + 1019, 997, 1001, 0, 0, 0, 0, 350, 553, 571, + 367, 540, 586, 372, 548, 564, 362, 501, 536, 0, + 0, 352, 569, 547, 479, 351, 0, 529, 392, 409, + 389, 499, 998, 0, 1022, 1026, 388, 1111, 1020, 579, + 354, 0, 578, 498, 471, 565, 570, 480, 473, 0, + 353, 567, 478, 472, 448, 399, 1112, 449, 450, 451, + 452, 453, 454, 413, 514, 469, 515, 414, 486, 485, + 487, 415, 416, 417, 418, 419, 420, 421, 422, 423, + 424, 0, 0, 0, 0, 0, 610, 611, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 747, 1017, 0, 752, 0, 582, 0, + 0, 1094, 0, 0, 0, 551, 0, 0, 455, 0, + 0, 0, 1021, 0, 532, 504, 1108, 0, 0, 530, + 467, 566, 516, 572, 554, 580, 523, 517, 344, 555, + 391, 481, 363, 365, 780, 393, 396, 400, 401, 492, + 493, 510, 539, 558, 559, 560, 390, 377, 531, 378, + 410, 379, 345, 382, 381, 383, 541, 384, 347, 511, + 563, 0, 406, 527, 476, 348, 475, 512, 562, 561, + 364, 590, 597, 598, 689, 0, 603, 794, 795, 796, + 612, 0, 518, 360, 357, 0, 0, 0, 386, 513, + 373, 375, 376, 374, 507, 509, 617, 618, 619, 621, + 0, 622, 623, 0, 0, 0, 0, 624, 690, 708, + 674, 643, 605, 698, 640, 644, 645, 427, 428, 429, + 430, 711, 0, 0, 0, 596, 456, 457, 0, 398, + 397, 477, 349, 0, 0, 445, 426, 519, 355, 394, + 447, 432, 458, 459, 460, 461, 462, 0, 463, 464, + 404, 339, 340, 788, 1091, 500, 713, 750, 751, 635, + 0, 1107, 1086, 1088, 1089, 1093, 1097, 1098, 1099, 1100, + 1101, 1103, 1106, 1110, 787, 0, 691, 705, 792, 704, + 784, 506, 0, 538, 702, 649, 0, 695, 668, 669, + 0, 696, 664, 700, 356, 433, 524, 581, 0, 637, + 0, 606, 609, 639, 726, 727, 728, 346, 608, 730, + 731, 732, 733, 734, 735, 736, 729, 1109, 672, 648, + 675, 587, 651, 650, 0, 0, 686, 1025, 687, 688, + 488, 489, 490, 491, 1095, 714, 371, 607, 521, 522, + 0, 673, 0, 0, 0, 0, 0, 0, 0, 0, + 678, 679, 676, 797, 0, 737, 738, 0, 0, 601, + 602, 403, 0, 620, 411, 370, 505, 405, 585, 436, + 0, 613, 680, 614, 525, 526, 741, 746, 742, 743, + 745, 766, 495, 425, 431, 542, 446, 468, 528, 584, + 503, 533, 368, 573, 544, 474, 665, 693, 1118, 1090, + 1117, 1119, 1120, 1116, 1121, 1122, 1102, 979, 0, 1032, + 1033, 1114, 1113, 1115, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 722, 721, 720, 719, 718, + 717, 716, 715, 0, 0, 662, 385, 785, 779, 786, + 767, 770, 769, 744, 986, 341, 642, 466, 520, 402, + 709, 710, 0, 765, 1080, 1043, 1044, 1045, 976, 1046, + 1040, 1041, 977, 1042, 1081, 1030, 1077, 1078, 1005, 1035, + 1047, 1076, 1048, 1079, 1006, 1082, 1123, 1124, 1054, 1038, + 302, 1125, 1051, 1083, 1075, 1074, 1049, 1031, 1084, 1085, + 1013, 1008, 1052, 1053, 1039, 1060, 1061, 1062, 1065, 978, + 1066, 1067, 1068, 1069, 1070, 1064, 1063, 1027, 1028, 1029, + 1055, 1056, 1058, 1059, 1034, 552, 1009, 1010, 1011, 1012, + 0, 0, 591, 592, 593, 616, 0, 594, 575, 641, + 412, 342, 556, 583, 782, 783, 0, 0, 0, 0, + 0, 0, 0, 692, 703, 739, 740, 0, 753, 754, + 756, 758, 1071, 760, 549, 550, 768, 0, 0, 437, + 358, 359, 496, 497, 535, 0, 438, 439, 440, 444, + 441, 442, 443, 4757, 763, 4758, 4759, 470, 534, 557, + 543, 781, 483, 0, 789, 631, 632, 790, 749, 748, + 343, 0, 1036, 1037, 706, 707, 1023, 0, 1104, 482, + 638, 0, 971, 0, 0, 502, 0, 0, 647, 681, + 670, 759, 634, 0, 0, 0, 0, 0, 0, 974, + 0, 0, 0, 395, 0, 0, 465, 685, 666, 677, + 667, 508, 652, 653, 654, 661, 407, 655, 791, 657, + 626, 658, 627, 659, 660, 1014, 684, 633, 545, 484, + 0, 701, 0, 0, 1096, 1105, 0, 0, 0, 0, + 0, 0, 0, 0, 1092, 0, 0, 0, 0, 0, + 965, 0, 0, 1003, 1073, 1072, 990, 1000, 0, 0, + 366, 269, 628, 755, 630, 629, 3379, 0, 3380, 996, + 999, 995, 993, 994, 0, 1087, 0, 0, 0, 0, + 0, 0, 957, 969, 0, 975, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 966, 967, 0, 0, 0, 0, 1024, 0, 968, + 0, 970, 0, 0, 0, 0, 546, 574, 0, 588, + 0, 434, 435, 1019, 997, 1001, 0, 0, 0, 0, + 350, 553, 571, 367, 540, 586, 372, 548, 564, 362, + 501, 536, 0, 0, 352, 569, 547, 479, 351, 0, + 529, 392, 409, 389, 499, 998, 0, 1022, 1026, 388, + 1111, 1020, 579, 354, 0, 578, 498, 471, 565, 570, + 480, 473, 0, 353, 567, 478, 472, 448, 399, 1112, + 449, 450, 451, 452, 453, 454, 413, 514, 469, 515, + 414, 486, 485, 487, 415, 416, 417, 418, 419, 420, + 421, 422, 423, 424, 0, 0, 0, 0, 0, 610, + 611, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 747, 1017, 0, 752, + 0, 582, 0, 0, 1094, 0, 0, 0, 551, 0, + 0, 455, 0, 0, 0, 1021, 0, 532, 504, 1108, + 0, 0, 530, 467, 566, 516, 572, 554, 580, 523, + 517, 344, 555, 391, 481, 363, 365, 780, 393, 396, + 400, 401, 492, 493, 510, 539, 558, 559, 560, 390, + 377, 531, 378, 410, 379, 345, 382, 381, 383, 541, + 384, 347, 511, 563, 0, 406, 527, 476, 348, 475, + 512, 562, 561, 364, 590, 597, 598, 689, 0, 603, + 794, 795, 796, 612, 0, 518, 360, 357, 0, 0, + 0, 386, 513, 373, 375, 376, 374, 507, 509, 617, + 618, 619, 621, 0, 622, 623, 0, 0, 0, 0, + 624, 690, 708, 674, 643, 605, 698, 640, 644, 645, + 427, 428, 429, 430, 711, 0, 0, 0, 596, 456, + 457, 0, 398, 397, 477, 349, 0, 0, 445, 426, + 519, 355, 394, 447, 432, 458, 459, 460, 461, 462, + 0, 463, 464, 404, 339, 340, 788, 1091, 500, 713, + 750, 751, 635, 0, 1107, 1086, 1088, 1089, 1093, 1097, + 1098, 1099, 1100, 1101, 1103, 1106, 1110, 787, 0, 691, + 705, 792, 704, 784, 506, 0, 538, 702, 649, 0, + 695, 668, 669, 0, 696, 664, 700, 356, 433, 524, + 581, 0, 637, 0, 606, 609, 639, 726, 727, 728, + 346, 608, 730, 731, 732, 733, 734, 735, 736, 729, + 1109, 672, 648, 675, 587, 651, 650, 0, 0, 686, + 1025, 687, 688, 488, 489, 490, 491, 1095, 714, 371, + 607, 521, 522, 0, 673, 0, 0, 0, 0, 0, + 0, 0, 0, 678, 679, 676, 797, 0, 737, 738, + 0, 0, 601, 602, 403, 0, 620, 411, 370, 505, + 405, 585, 436, 0, 613, 680, 614, 525, 526, 741, + 746, 742, 743, 745, 766, 495, 425, 431, 542, 446, + 468, 528, 584, 503, 533, 368, 573, 544, 474, 665, + 693, 1118, 1090, 1117, 1119, 1120, 1116, 1121, 1122, 1102, + 979, 0, 1032, 1033, 1114, 1113, 1115, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 722, 721, + 720, 719, 718, 717, 716, 715, 0, 0, 662, 385, + 785, 779, 786, 767, 770, 769, 744, 986, 341, 642, + 466, 520, 402, 709, 710, 0, 765, 1080, 1043, 1044, + 1045, 976, 1046, 1040, 1041, 977, 1042, 1081, 1030, 1077, + 1078, 1005, 1035, 1047, 1076, 1048, 1079, 1006, 1082, 1123, + 1124, 1054, 1038, 302, 1125, 1051, 1083, 1075, 1074, 1049, + 1031, 1084, 1085, 1013, 1008, 1052, 1053, 1039, 1060, 1061, + 1062, 1065, 978, 1066, 1067, 1068, 1069, 1070, 1064, 1063, + 1027, 1028, 1029, 1055, 1056, 1058, 1059, 1034, 552, 1009, + 1010, 1011, 1012, 0, 0, 591, 592, 593, 616, 0, + 594, 575, 641, 412, 342, 556, 583, 782, 783, 0, + 0, 0, 0, 0, 0, 0, 692, 703, 739, 740, + 0, 753, 754, 756, 758, 1071, 760, 549, 550, 768, + 0, 0, 437, 358, 359, 496, 497, 535, 0, 438, + 439, 440, 444, 441, 442, 443, 1057, 763, 764, 761, + 470, 534, 557, 543, 781, 483, 0, 789, 631, 632, + 790, 749, 748, 343, 0, 1036, 1037, 706, 707, 1023, + 0, 1104, 482, 638, 0, 971, 0, 0, 502, 0, + 0, 647, 681, 670, 759, 634, 0, 0, 2144, 0, + 0, 0, 974, 0, 0, 0, 395, 0, 0, 465, + 685, 666, 677, 667, 508, 652, 653, 654, 661, 407, + 655, 791, 657, 626, 658, 627, 659, 660, 1014, 684, + 633, 545, 484, 0, 701, 0, 0, 1096, 1105, 0, + 0, 0, 0, 0, 0, 0, 0, 1092, 0, 0, + 0, 0, 0, 965, 0, 0, 1003, 1073, 1072, 990, + 1000, 0, 0, 366, 269, 628, 755, 630, 629, 991, + 0, 992, 996, 999, 995, 993, 994, 0, 1087, 0, + 0, 0, 0, 0, 0, 0, 969, 0, 975, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 966, 967, 0, 0, 0, 0, + 1024, 0, 968, 0, 970, 0, 0, 0, 0, 546, + 574, 0, 588, 0, 434, 435, 1019, 997, 1001, 0, + 0, 0, 0, 350, 553, 571, 367, 540, 586, 372, + 548, 564, 362, 501, 536, 0, 0, 352, 569, 547, + 479, 351, 0, 529, 392, 409, 389, 499, 998, 0, + 1022, 1026, 388, 1111, 1020, 579, 354, 0, 578, 498, + 471, 565, 570, 480, 473, 0, 353, 567, 478, 472, + 448, 399, 1112, 449, 450, 451, 452, 453, 454, 413, + 514, 469, 515, 414, 486, 485, 487, 415, 416, 417, + 418, 419, 420, 421, 422, 423, 424, 0, 0, 0, + 0, 0, 610, 611, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 747, + 1017, 0, 752, 0, 582, 0, 0, 1094, 0, 0, + 0, 551, 0, 0, 455, 0, 0, 0, 1021, 0, + 532, 504, 1108, 0, 0, 530, 467, 566, 516, 572, + 554, 580, 523, 517, 344, 555, 391, 481, 363, 365, + 780, 393, 396, 400, 401, 492, 493, 510, 539, 558, + 559, 560, 390, 377, 531, 378, 410, 379, 345, 382, + 381, 383, 541, 384, 347, 511, 563, 0, 406, 527, + 476, 348, 475, 512, 562, 561, 364, 590, 2145, 2146, + 689, 0, 603, 794, 795, 796, 612, 0, 518, 360, + 357, 0, 0, 0, 386, 513, 373, 375, 376, 374, + 507, 509, 617, 618, 619, 621, 0, 622, 623, 0, + 0, 0, 0, 624, 690, 708, 674, 643, 605, 698, + 640, 644, 645, 427, 428, 429, 430, 711, 0, 0, + 0, 596, 456, 457, 0, 398, 397, 477, 349, 0, + 0, 445, 426, 519, 355, 394, 447, 432, 458, 459, + 460, 461, 462, 0, 463, 464, 404, 339, 340, 788, + 1091, 500, 713, 750, 751, 635, 0, 1107, 1086, 1088, + 1089, 1093, 1097, 1098, 1099, 1100, 1101, 1103, 1106, 1110, + 787, 0, 691, 705, 792, 704, 784, 506, 0, 538, + 702, 649, 0, 695, 668, 669, 0, 696, 664, 700, + 356, 433, 524, 581, 0, 637, 0, 606, 609, 639, + 726, 727, 728, 346, 608, 730, 731, 732, 733, 734, + 735, 736, 729, 1109, 672, 648, 675, 587, 651, 650, + 0, 0, 686, 1025, 687, 688, 488, 489, 490, 491, + 1095, 714, 371, 607, 521, 522, 0, 673, 0, 0, + 0, 0, 0, 0, 0, 0, 678, 679, 676, 797, + 0, 737, 738, 0, 0, 601, 602, 403, 0, 620, + 411, 370, 505, 405, 585, 436, 0, 613, 680, 614, + 525, 526, 741, 746, 742, 743, 745, 766, 495, 425, + 431, 542, 446, 468, 528, 584, 503, 533, 368, 573, + 544, 474, 665, 693, 1118, 1090, 1117, 1119, 1120, 1116, + 1121, 1122, 1102, 979, 0, 1032, 1033, 1114, 1113, 1115, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 722, 721, 720, 719, 718, 717, 716, 715, 0, + 0, 662, 385, 785, 779, 786, 767, 770, 769, 744, + 986, 341, 642, 466, 520, 402, 709, 710, 0, 765, + 1080, 1043, 1044, 1045, 976, 1046, 1040, 1041, 977, 1042, + 1081, 1030, 1077, 1078, 1005, 1035, 1047, 1076, 1048, 1079, + 1006, 1082, 1123, 1124, 1054, 1038, 302, 1125, 1051, 1083, + 1075, 1074, 1049, 1031, 1084, 1085, 1013, 1008, 1052, 1053, + 1039, 1060, 1061, 1062, 1065, 978, 1066, 1067, 1068, 1069, + 1070, 1064, 1063, 1027, 1028, 1029, 1055, 1056, 1058, 1059, + 1034, 552, 1009, 1010, 1011, 1012, 0, 0, 591, 592, + 593, 616, 0, 594, 575, 641, 412, 342, 556, 583, + 782, 783, 0, 0, 0, 0, 0, 0, 0, 692, + 703, 739, 740, 0, 753, 754, 756, 758, 1071, 760, + 549, 550, 768, 0, 0, 437, 358, 359, 496, 497, + 535, 0, 438, 439, 440, 444, 441, 442, 443, 1057, + 763, 764, 761, 470, 534, 557, 543, 781, 483, 0, + 789, 631, 632, 790, 749, 748, 343, 0, 1036, 1037, + 706, 707, 1023, 0, 1104, 482, 638, 0, 971, 0, + 0, 502, 0, 0, 647, 681, 670, 759, 634, 0, + 0, 0, 0, 0, 0, 974, 0, 0, 0, 395, + 0, 0, 465, 685, 666, 677, 667, 508, 652, 653, + 654, 661, 407, 655, 791, 657, 626, 658, 627, 659, + 660, 1014, 684, 633, 545, 484, 0, 701, 0, 0, + 1096, 1105, 0, 0, 0, 0, 0, 0, 0, 0, + 1092, 0, 0, 0, 0, 0, 1627, 0, 0, 1003, + 1073, 1072, 990, 1000, 0, 0, 366, 269, 628, 755, + 630, 629, 991, 0, 992, 996, 999, 995, 993, 994, + 0, 1087, 0, 0, 0, 0, 0, 0, 957, 969, + 0, 975, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 966, 967, 0, + 0, 0, 0, 1024, 0, 968, 0, 970, 0, 0, + 0, 0, 546, 574, 0, 588, 0, 434, 435, 1019, + 997, 1001, 0, 0, 0, 0, 350, 553, 571, 367, + 540, 586, 372, 548, 564, 362, 501, 536, 0, 0, + 352, 569, 547, 479, 351, 0, 529, 392, 409, 389, + 499, 998, 0, 1022, 1026, 388, 1111, 1020, 579, 354, + 0, 578, 498, 471, 565, 570, 480, 473, 0, 353, + 567, 478, 472, 448, 399, 1112, 449, 450, 451, 452, + 453, 454, 413, 514, 469, 515, 414, 486, 485, 487, + 415, 416, 417, 418, 419, 420, 421, 422, 423, 424, + 0, 0, 0, 0, 0, 610, 611, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 747, 1017, 0, 752, 0, 582, 0, 0, + 1094, 0, 0, 0, 551, 0, 0, 455, 0, 0, + 0, 1021, 0, 532, 504, 1108, 0, 0, 530, 467, + 566, 516, 572, 554, 580, 523, 517, 344, 555, 391, + 481, 363, 365, 780, 393, 396, 400, 401, 492, 493, + 510, 539, 558, 559, 560, 390, 377, 531, 378, 410, + 379, 345, 382, 381, 383, 541, 384, 347, 511, 563, + 0, 406, 527, 476, 348, 475, 512, 562, 561, 364, + 590, 597, 598, 689, 0, 603, 794, 795, 796, 612, + 0, 518, 360, 357, 0, 0, 0, 386, 513, 373, + 375, 376, 374, 507, 509, 617, 618, 619, 621, 0, + 622, 623, 0, 0, 0, 0, 624, 690, 708, 674, + 643, 605, 698, 640, 644, 645, 427, 428, 429, 430, + 711, 0, 0, 0, 596, 456, 457, 0, 398, 397, + 477, 349, 0, 0, 445, 426, 519, 355, 394, 447, + 432, 458, 459, 460, 461, 462, 0, 463, 464, 404, + 339, 340, 788, 1091, 500, 713, 750, 751, 635, 0, + 1107, 1086, 1088, 1089, 1093, 1097, 1098, 1099, 1100, 1101, + 1103, 1106, 1110, 787, 0, 691, 705, 792, 704, 784, + 506, 0, 538, 702, 649, 0, 695, 668, 669, 0, + 696, 664, 700, 356, 433, 524, 581, 0, 637, 0, + 606, 609, 639, 726, 727, 728, 346, 608, 730, 731, + 732, 733, 734, 735, 736, 729, 1109, 672, 648, 675, + 587, 651, 650, 0, 0, 686, 1025, 687, 688, 488, + 489, 490, 491, 1095, 714, 371, 607, 521, 522, 0, + 673, 0, 0, 0, 0, 0, 0, 0, 0, 678, + 679, 676, 797, 0, 737, 738, 0, 0, 601, 602, + 403, 0, 620, 411, 370, 505, 405, 585, 436, 0, + 613, 680, 614, 525, 526, 741, 746, 742, 743, 745, + 766, 495, 425, 431, 542, 446, 468, 528, 584, 503, + 533, 368, 573, 544, 474, 665, 693, 1118, 1090, 1117, + 1119, 1120, 1116, 1121, 1122, 1102, 979, 0, 1032, 1033, + 1114, 1113, 1115, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 722, 721, 720, 719, 718, 717, + 716, 715, 0, 0, 662, 385, 785, 779, 786, 767, + 770, 769, 744, 986, 341, 642, 466, 520, 402, 709, + 710, 0, 765, 1080, 1043, 1044, 1045, 976, 1046, 1040, + 1041, 977, 1042, 1081, 1030, 1077, 1078, 1005, 1035, 1047, + 1076, 1048, 1079, 1006, 1082, 1123, 1124, 1054, 1038, 302, + 1125, 1051, 1083, 1075, 1074, 1049, 1031, 1084, 1085, 1013, + 1008, 1052, 1053, 1039, 1060, 1061, 1062, 1065, 978, 1066, + 1067, 1068, 1069, 1070, 1064, 1063, 1027, 1028, 1029, 1055, + 1056, 1058, 1059, 1034, 552, 1009, 1010, 1011, 1012, 0, + 0, 591, 592, 593, 616, 0, 594, 575, 641, 412, + 342, 556, 583, 782, 783, 0, 0, 0, 0, 0, + 0, 0, 692, 703, 739, 740, 0, 753, 754, 756, + 758, 1071, 760, 549, 550, 768, 0, 0, 437, 358, + 359, 496, 497, 535, 0, 438, 439, 440, 444, 441, + 442, 443, 1057, 763, 764, 761, 470, 534, 557, 543, + 781, 483, 0, 789, 631, 632, 790, 749, 748, 343, + 0, 1036, 1037, 706, 707, 1023, 0, 1104, 482, 638, + 0, 971, 0, 0, 502, 0, 0, 647, 681, 670, + 759, 634, 0, 0, 0, 0, 0, 0, 974, 0, + 0, 0, 395, 0, 0, 465, 685, 666, 677, 667, + 508, 652, 653, 654, 661, 407, 655, 791, 657, 626, + 658, 627, 659, 660, 1014, 684, 633, 545, 484, 0, + 701, 0, 0, 1096, 1105, 0, 0, 0, 0, 0, + 0, 0, 0, 1092, 0, 0, 0, 0, 0, 965, + 0, 0, 1003, 1073, 1072, 990, 1000, 0, 0, 366, + 269, 628, 755, 630, 629, 991, 0, 992, 996, 999, + 995, 993, 994, 0, 1087, 0, 0, 0, 0, 0, + 0, 0, 969, 0, 975, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 966, 967, 0, 0, 0, 0, 1024, 0, 968, 0, + 970, 0, 0, 0, 0, 546, 574, 0, 588, 0, + 434, 435, 1019, 997, 1001, 0, 0, 0, 0, 350, + 553, 571, 367, 540, 586, 372, 548, 564, 362, 501, + 536, 0, 0, 352, 569, 547, 479, 351, 0, 529, + 392, 409, 389, 499, 998, 0, 1022, 1026, 388, 1111, + 1020, 579, 354, 0, 578, 498, 471, 565, 570, 480, + 473, 0, 353, 567, 478, 472, 448, 399, 1112, 449, + 450, 451, 452, 453, 454, 413, 514, 469, 515, 414, + 486, 485, 487, 415, 416, 417, 418, 419, 420, 421, + 422, 423, 424, 0, 0, 0, 0, 0, 610, 611, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 747, 1017, 0, 752, 0, + 582, 0, 0, 1094, 0, 0, 0, 551, 0, 0, + 455, 0, 0, 0, 1021, 0, 532, 504, 1108, 0, + 0, 530, 467, 566, 516, 572, 554, 580, 523, 517, + 344, 555, 391, 481, 363, 365, 780, 393, 396, 400, + 401, 492, 493, 510, 539, 558, 559, 560, 390, 377, + 531, 378, 410, 379, 345, 382, 381, 383, 541, 384, + 347, 511, 563, 0, 406, 527, 476, 348, 475, 512, + 562, 561, 364, 590, 597, 598, 689, 0, 603, 794, + 795, 796, 612, 0, 518, 360, 357, 0, 0, 0, + 386, 513, 373, 375, 376, 374, 507, 509, 617, 618, + 619, 621, 0, 622, 623, 0, 0, 0, 0, 624, + 690, 708, 674, 643, 605, 698, 640, 644, 645, 427, + 428, 429, 430, 711, 0, 0, 0, 596, 456, 457, + 0, 398, 397, 477, 349, 0, 0, 445, 426, 519, + 355, 394, 447, 432, 458, 459, 460, 461, 462, 0, + 463, 464, 404, 339, 340, 788, 1091, 500, 713, 750, + 751, 635, 0, 1107, 1086, 1088, 1089, 1093, 1097, 1098, + 1099, 1100, 1101, 1103, 1106, 1110, 787, 0, 691, 705, + 792, 704, 784, 506, 0, 538, 702, 649, 0, 695, + 668, 669, 0, 696, 664, 700, 356, 433, 524, 581, + 0, 637, 0, 606, 609, 639, 726, 727, 728, 346, + 608, 730, 731, 732, 733, 734, 735, 736, 729, 1109, + 672, 648, 675, 587, 651, 650, 0, 0, 686, 1025, + 687, 688, 488, 489, 490, 491, 1095, 714, 371, 607, + 521, 522, 0, 673, 0, 0, 0, 0, 0, 0, + 0, 0, 678, 679, 676, 797, 0, 737, 738, 0, + 0, 601, 602, 403, 0, 620, 411, 370, 505, 405, + 585, 436, 0, 613, 680, 614, 525, 526, 741, 746, + 742, 743, 745, 766, 495, 425, 431, 542, 446, 468, + 528, 584, 503, 533, 368, 573, 544, 474, 665, 693, + 1118, 1090, 1117, 1119, 1120, 1116, 1121, 1122, 1102, 979, + 0, 1032, 1033, 1114, 1113, 1115, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 722, 721, 720, + 719, 718, 717, 716, 715, 0, 0, 662, 385, 785, + 779, 786, 767, 770, 769, 744, 986, 341, 642, 466, + 520, 402, 709, 710, 0, 765, 1080, 1043, 1044, 1045, + 976, 1046, 1040, 1041, 977, 1042, 1081, 1030, 1077, 1078, + 1005, 1035, 1047, 1076, 1048, 1079, 1006, 1082, 1123, 1124, + 1054, 1038, 302, 1125, 1051, 1083, 1075, 1074, 1049, 1031, + 1084, 1085, 1013, 1008, 1052, 1053, 1039, 1060, 1061, 1062, + 1065, 978, 1066, 1067, 1068, 1069, 1070, 1064, 1063, 1027, + 1028, 1029, 1055, 1056, 1058, 1059, 1034, 552, 1009, 1010, + 1011, 1012, 0, 0, 591, 592, 593, 616, 0, 594, + 575, 641, 412, 342, 556, 583, 782, 783, 0, 0, + 0, 0, 0, 0, 0, 692, 703, 739, 740, 0, + 753, 754, 756, 758, 1071, 760, 549, 550, 768, 0, + 0, 437, 358, 359, 496, 497, 535, 0, 438, 439, + 440, 444, 441, 442, 443, 1057, 763, 764, 761, 470, + 534, 557, 543, 781, 483, 0, 789, 631, 632, 790, + 749, 748, 343, 0, 1036, 1037, 706, 707, 0, 0, + 1104, 482, 638, 0, 971, 200, 245, 199, 232, 201, + 0, 0, 0, 0, 0, 0, 502, 0, 0, 647, + 681, 670, 759, 634, 0, 233, 0, 0, 0, 0, + 0, 0, 225, 0, 395, 0, 234, 465, 685, 666, + 677, 667, 508, 652, 653, 654, 661, 407, 655, 656, + 657, 626, 658, 627, 659, 660, 169, 684, 633, 545, + 484, 0, 701, 0, 0, 0, 0, 0, 0, 0, + 0, 154, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 238, 0, 0, 268, 0, 0, 0, 0, 0, + 0, 366, 269, 628, 755, 630, 629, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 369, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 260, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 546, 574, 0, + 588, 0, 434, 435, 0, 0, 0, 0, 0, 0, + 0, 350, 553, 571, 367, 540, 586, 372, 548, 564, + 362, 501, 536, 0, 0, 352, 569, 547, 479, 351, + 0, 529, 392, 409, 389, 499, 0, 0, 568, 599, + 388, 589, 0, 579, 354, 0, 578, 498, 471, 565, + 570, 480, 473, 0, 353, 567, 478, 472, 448, 399, + 615, 449, 450, 451, 452, 453, 454, 413, 514, 469, + 515, 414, 486, 485, 487, 415, 416, 417, 418, 419, + 420, 421, 422, 423, 424, 0, 0, 0, 0, 0, + 610, 611, 0, 0, 0, 0, 0, 0, 0, 198, + 230, 242, 231, 84, 150, 0, 0, 747, 0, 246, + 752, 0, 582, 0, 0, 261, 0, 0, 0, 551, + 0, 0, 455, 152, 224, 223, 600, 0, 532, 504, + 274, 0, 0, 530, 467, 566, 516, 572, 554, 580, + 523, 517, 344, 555, 391, 481, 363, 365, 283, 393, + 396, 400, 401, 492, 493, 510, 539, 558, 559, 560, + 390, 377, 531, 378, 410, 379, 345, 382, 381, 383, + 541, 384, 347, 511, 563, 151, 406, 527, 476, 348, + 475, 512, 562, 561, 364, 590, 597, 598, 689, 0, + 603, 723, 724, 725, 612, 0, 518, 360, 357, 0, + 0, 0, 386, 513, 373, 375, 376, 374, 507, 509, + 617, 618, 619, 621, 0, 622, 623, 0, 0, 0, + 0, 624, 690, 708, 674, 643, 605, 698, 640, 644, + 645, 427, 428, 429, 430, 711, 0, 0, 0, 596, + 456, 457, 0, 398, 397, 477, 349, 0, 0, 445, + 426, 519, 355, 394, 447, 432, 458, 459, 460, 461, + 462, 0, 463, 464, 404, 339, 340, 576, 387, 500, + 713, 750, 751, 635, 0, 699, 636, 646, 380, 671, + 683, 682, 494, 595, 264, 694, 697, 625, 275, 0, + 691, 705, 663, 704, 276, 506, 0, 538, 702, 649, + 0, 695, 668, 669, 0, 696, 664, 700, 356, 433, + 524, 581, 0, 637, 0, 606, 609, 639, 726, 727, + 728, 346, 608, 730, 731, 732, 733, 734, 735, 736, + 729, 577, 672, 648, 675, 587, 651, 650, 0, 0, + 686, 604, 687, 688, 488, 489, 490, 491, 408, 714, + 371, 607, 521, 522, 166, 673, 0, 0, 0, 0, + 0, 0, 0, 0, 678, 679, 676, 273, 0, 737, + 738, 0, 0, 601, 602, 403, 0, 620, 411, 370, + 505, 405, 585, 436, 0, 613, 680, 614, 525, 526, + 741, 746, 742, 743, 745, 766, 495, 425, 431, 542, + 446, 468, 528, 584, 503, 533, 368, 573, 544, 474, + 665, 693, 0, 0, 0, 0, 0, 0, 0, 0, + 79, 0, 0, 325, 326, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 722, + 721, 720, 719, 718, 717, 716, 715, 0, 0, 662, + 385, 281, 361, 282, 767, 770, 769, 744, 0, 341, + 642, 466, 520, 402, 709, 710, 72, 765, 284, 285, 286, 287, 288, 289, 290, 291, 332, 292, 293, 294, 295, 296, 297, 298, 303, 304, 305, 306, 307, 308, - 309, 310, 708, 301, 302, 311, 312, 313, 314, 315, + 309, 310, 712, 301, 302, 311, 312, 313, 314, 315, 316, 317, 318, 319, 320, 321, 322, 323, 324, 0, - 0, 0, 0, 333, 767, 768, 769, 770, 771, 0, - 0, 334, 335, 336, 0, 0, 299, 300, 327, 549, - 328, 329, 330, 331, 0, 0, 587, 588, 589, 612, - 0, 590, 572, 637, 411, 342, 553, 579, 778, 779, - 0, 0, 0, 0, 0, 0, 0, 688, 699, 735, - 736, 0, 749, 750, 752, 754, 753, 756, 546, 547, - 764, 0, 0, 435, 357, 358, 494, 495, 532, 0, - 436, 437, 438, 442, 439, 440, 441, 758, 759, 760, - 757, 468, 531, 554, 540, 777, 481, 0, 785, 627, - 628, 786, 745, 744, 343, 0, 337, 338, 702, 703, - 0, 0, 534, 480, 634, 200, 245, 199, 232, 201, - 0, 0, 0, 0, 0, 0, 500, 815, 0, 643, - 677, 666, 755, 630, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 394, 0, 0, 463, 681, 662, - 673, 663, 506, 648, 649, 650, 657, 406, 651, 787, - 653, 622, 654, 623, 655, 656, 0, 680, 629, 542, - 482, 0, 697, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 822, 0, 0, 0, 0, 0, 0, 0, - 0, 821, 0, 0, 268, 0, 0, 0, 0, 0, - 0, 365, 269, 624, 751, 626, 625, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 368, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 543, 571, 0, - 584, 0, 432, 433, 0, 0, 0, 0, 0, 0, - 0, 350, 550, 568, 366, 537, 582, 371, 545, 561, - 361, 499, 533, 0, 0, 352, 566, 544, 477, 351, - 0, 526, 391, 408, 388, 497, 0, 0, 565, 595, - 387, 585, 0, 576, 354, 0, 575, 496, 469, 562, - 567, 478, 471, 0, 353, 564, 476, 470, 446, 398, - 611, 447, 448, 449, 450, 451, 452, 412, 512, 467, - 513, 413, 484, 483, 485, 414, 415, 416, 417, 418, - 419, 420, 421, 422, 423, 0, 0, 0, 0, 0, - 606, 607, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 819, 820, 0, 743, 0, 246, - 748, 0, 578, 0, 0, 0, 0, 0, 0, 548, - 0, 0, 453, 0, 0, 0, 596, 0, 529, 502, - 789, 0, 0, 527, 465, 563, 514, 569, 551, 577, - 521, 515, 344, 552, 390, 479, 362, 364, 776, 392, - 395, 399, 400, 490, 491, 508, 536, 555, 556, 557, - 389, 376, 528, 377, 409, 378, 345, 381, 380, 382, - 538, 383, 347, 509, 560, 0, 405, 524, 474, 348, - 473, 510, 559, 558, 363, 586, 593, 594, 685, 0, - 599, 790, 791, 792, 608, 0, 516, 359, 356, 0, - 0, 0, 385, 511, 372, 374, 375, 373, 505, 507, - 613, 614, 615, 617, 0, 618, 619, 0, 0, 0, - 0, 620, 686, 704, 670, 639, 601, 694, 636, 640, - 641, 426, 427, 428, 429, 707, 0, 0, 0, 592, - 454, 455, 0, 397, 396, 475, 349, 0, 0, 443, - 425, 517, 355, 393, 445, 431, 456, 457, 458, 459, - 460, 0, 461, 462, 403, 339, 340, 784, 386, 498, - 709, 746, 747, 631, 0, 695, 632, 642, 379, 667, - 679, 678, 492, 591, 0, 690, 693, 621, 783, 0, - 687, 701, 788, 700, 780, 504, 0, 535, 698, 645, - 0, 691, 664, 665, 0, 692, 660, 696, 0, 633, - 0, 602, 605, 635, 722, 723, 724, 346, 604, 726, - 727, 728, 729, 730, 731, 732, 725, 574, 668, 644, - 671, 583, 647, 646, 0, 0, 682, 600, 683, 684, - 486, 487, 488, 489, 816, 818, 370, 603, 519, 520, - 831, 669, 0, 0, 0, 0, 0, 0, 0, 0, - 674, 675, 672, 793, 0, 733, 734, 0, 0, 597, - 598, 402, 0, 616, 410, 369, 503, 404, 581, 434, - 0, 609, 676, 610, 522, 523, 737, 742, 738, 739, - 741, 762, 493, 424, 430, 539, 444, 466, 525, 580, - 501, 530, 367, 570, 541, 472, 661, 689, 0, 0, - 0, 0, 0, 0, 0, 0, 79, 0, 0, 325, - 326, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 718, 717, 716, 715, 714, - 713, 712, 711, 0, 0, 658, 384, 781, 775, 782, - 763, 766, 765, 740, 0, 341, 638, 464, 518, 401, - 705, 706, 0, 761, 284, 285, 286, 287, 288, 289, - 290, 291, 332, 292, 293, 294, 295, 296, 297, 298, - 303, 304, 305, 306, 307, 308, 309, 310, 708, 301, - 302, 311, 312, 313, 314, 315, 316, 317, 318, 319, - 320, 321, 322, 323, 324, 0, 0, 0, 0, 333, - 767, 768, 769, 770, 771, 0, 0, 334, 335, 336, - 0, 0, 299, 300, 327, 549, 328, 329, 330, 331, - 0, 0, 587, 588, 589, 612, 0, 590, 572, 637, - 411, 342, 553, 579, 778, 779, 0, 0, 0, 0, - 0, 0, 0, 688, 699, 735, 736, 0, 749, 750, - 752, 754, 753, 756, 546, 547, 764, 0, 0, 435, - 357, 358, 494, 495, 532, 0, 436, 437, 438, 442, - 439, 440, 441, 758, 759, 760, 757, 468, 531, 554, - 540, 777, 481, 0, 785, 627, 628, 786, 745, 744, - 343, 0, 337, 338, 702, 703, 0, 0, 534, 480, - 634, 500, 0, 0, 643, 677, 666, 755, 630, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 394, - 0, 0, 463, 681, 662, 673, 663, 506, 648, 649, - 650, 657, 406, 651, 787, 653, 622, 654, 623, 655, - 656, 0, 680, 629, 542, 482, 0, 697, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 1644, 0, 0, 268, - 0, 0, 986, 996, 0, 0, 365, 269, 624, 751, - 626, 625, 987, 0, 988, 992, 995, 991, 989, 990, - 0, 368, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 543, 571, 0, 584, 0, 432, 433, 0, - 993, 0, 0, 0, 0, 0, 350, 550, 568, 366, - 537, 582, 371, 545, 561, 361, 499, 533, 0, 0, - 352, 566, 544, 477, 351, 0, 526, 391, 408, 388, - 497, 994, 0, 565, 595, 387, 585, 0, 576, 354, - 0, 575, 496, 469, 562, 567, 478, 471, 0, 353, - 564, 476, 470, 446, 398, 611, 447, 448, 449, 450, - 451, 452, 412, 512, 467, 513, 413, 484, 483, 485, - 414, 415, 416, 417, 418, 419, 420, 421, 422, 423, - 0, 0, 0, 0, 0, 606, 607, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 743, 0, 0, 748, 0, 578, 0, 0, - 0, 0, 0, 0, 548, 0, 0, 453, 0, 0, - 0, 596, 0, 529, 502, 789, 0, 0, 527, 465, - 563, 514, 569, 551, 577, 521, 515, 344, 552, 390, - 479, 362, 364, 776, 392, 395, 399, 400, 490, 491, - 508, 536, 555, 556, 557, 389, 376, 528, 377, 409, - 378, 345, 381, 380, 382, 538, 383, 347, 509, 560, - 0, 405, 524, 474, 348, 473, 510, 559, 558, 363, - 586, 593, 594, 685, 0, 599, 790, 791, 792, 608, - 0, 516, 359, 356, 0, 0, 0, 385, 511, 372, - 374, 375, 373, 505, 507, 613, 614, 615, 617, 0, - 618, 619, 0, 0, 0, 0, 620, 686, 704, 670, - 639, 601, 694, 636, 640, 641, 426, 427, 428, 429, - 707, 0, 0, 0, 592, 454, 455, 0, 397, 396, - 475, 349, 0, 0, 443, 425, 517, 355, 393, 445, - 431, 456, 457, 458, 459, 460, 0, 461, 462, 403, - 339, 340, 784, 386, 498, 709, 746, 747, 631, 0, - 695, 632, 642, 379, 667, 679, 678, 492, 591, 0, - 690, 693, 621, 783, 0, 687, 701, 788, 700, 780, - 504, 0, 535, 698, 645, 0, 691, 664, 665, 0, - 692, 660, 696, 0, 633, 0, 602, 605, 635, 722, - 723, 724, 346, 604, 726, 727, 728, 729, 730, 731, - 732, 725, 574, 668, 644, 671, 583, 647, 646, 0, - 0, 682, 600, 683, 684, 486, 487, 488, 489, 407, - 710, 370, 603, 519, 520, 0, 669, 0, 0, 0, - 0, 0, 0, 0, 0, 674, 675, 672, 793, 0, - 733, 734, 0, 0, 597, 598, 402, 0, 616, 410, - 369, 503, 404, 581, 434, 0, 609, 676, 610, 522, - 523, 737, 742, 738, 739, 741, 762, 493, 424, 430, - 539, 444, 466, 525, 580, 501, 530, 367, 570, 541, - 472, 661, 689, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 325, 326, 0, 0, 0, 0, + 0, 0, 0, 333, 771, 772, 773, 774, 775, 0, + 0, 334, 335, 336, 0, 0, 299, 300, 327, 552, + 328, 329, 330, 331, 0, 0, 591, 592, 593, 616, + 0, 594, 575, 641, 412, 342, 556, 583, 277, 278, + 55, 262, 265, 267, 266, 0, 73, 692, 703, 739, + 740, 9, 753, 754, 756, 758, 757, 760, 549, 550, + 768, 0, 0, 437, 358, 359, 496, 497, 535, 0, + 438, 439, 440, 444, 441, 442, 443, 762, 763, 764, + 761, 470, 534, 557, 543, 272, 483, 173, 279, 631, + 632, 280, 749, 748, 343, 0, 337, 338, 706, 707, + 200, 245, 537, 482, 638, 0, 0, 0, 0, 0, + 0, 502, 0, 0, 647, 681, 670, 759, 634, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 395, + 0, 0, 465, 685, 666, 677, 667, 508, 652, 653, + 654, 661, 407, 655, 791, 657, 626, 658, 627, 659, + 660, 1270, 684, 633, 545, 484, 0, 701, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 238, 0, 0, 268, + 0, 0, 0, 0, 0, 0, 366, 269, 628, 755, + 630, 629, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 369, 3052, 3055, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 546, 574, 0, 588, 0, 434, 435, 0, + 0, 0, 0, 0, 0, 0, 350, 553, 571, 367, + 540, 586, 372, 548, 564, 362, 501, 536, 0, 0, + 352, 569, 547, 479, 351, 0, 529, 392, 409, 389, + 499, 0, 0, 568, 599, 388, 589, 0, 579, 354, + 0, 578, 498, 471, 565, 570, 480, 473, 0, 353, + 567, 478, 472, 448, 399, 615, 449, 450, 451, 452, + 453, 454, 413, 514, 469, 515, 414, 486, 485, 487, + 415, 416, 417, 418, 419, 420, 421, 422, 423, 424, + 0, 0, 0, 0, 0, 610, 611, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 747, 0, 246, 752, 3056, 582, 0, 0, + 0, 3051, 0, 3050, 551, 3047, 3053, 455, 0, 0, + 0, 600, 0, 532, 504, 793, 0, 0, 530, 467, + 566, 516, 572, 554, 580, 523, 517, 344, 555, 391, + 481, 363, 365, 780, 393, 396, 400, 401, 492, 493, + 510, 539, 558, 559, 560, 390, 377, 531, 378, 410, + 379, 345, 382, 381, 383, 541, 384, 347, 511, 563, + 3054, 406, 527, 476, 348, 475, 512, 562, 561, 364, + 590, 597, 598, 689, 0, 603, 794, 795, 796, 612, + 0, 518, 360, 357, 0, 0, 0, 386, 513, 373, + 375, 376, 374, 507, 509, 617, 618, 619, 621, 0, + 622, 623, 0, 0, 0, 0, 624, 690, 708, 674, + 643, 605, 698, 640, 644, 645, 427, 428, 429, 430, + 711, 0, 0, 0, 596, 456, 457, 0, 398, 397, + 477, 349, 0, 0, 445, 426, 519, 355, 394, 447, + 432, 458, 459, 460, 461, 462, 3048, 463, 464, 404, + 339, 340, 788, 387, 500, 713, 750, 751, 635, 0, + 699, 636, 646, 380, 671, 683, 682, 494, 595, 0, + 694, 697, 625, 787, 0, 691, 705, 792, 704, 784, + 506, 0, 538, 702, 649, 0, 695, 668, 669, 0, + 696, 664, 700, 356, 433, 524, 581, 0, 637, 0, + 606, 609, 639, 726, 727, 728, 346, 608, 730, 731, + 732, 733, 734, 735, 736, 729, 577, 672, 648, 675, + 587, 651, 650, 0, 0, 686, 604, 687, 688, 488, + 489, 490, 491, 408, 714, 371, 607, 521, 522, 0, + 673, 0, 0, 0, 0, 0, 0, 0, 0, 678, + 679, 676, 797, 0, 737, 738, 0, 0, 601, 602, + 403, 0, 620, 411, 370, 505, 405, 585, 436, 0, + 613, 680, 614, 525, 526, 741, 746, 742, 743, 745, + 766, 495, 425, 431, 542, 446, 468, 528, 584, 503, + 533, 368, 573, 544, 474, 665, 693, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 325, 326, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 718, 717, 716, 715, 714, 713, 712, 711, 0, 0, - 658, 384, 781, 775, 782, 763, 766, 765, 740, 0, - 341, 638, 464, 518, 401, 705, 706, 0, 761, 284, - 285, 286, 287, 288, 289, 290, 291, 332, 292, 293, - 294, 295, 296, 297, 298, 303, 304, 305, 306, 307, - 308, 309, 310, 708, 301, 302, 311, 312, 313, 314, - 315, 316, 317, 318, 319, 320, 321, 322, 323, 324, - 0, 0, 0, 0, 333, 767, 768, 769, 770, 771, - 0, 0, 334, 335, 336, 0, 0, 299, 300, 327, - 549, 328, 329, 330, 331, 0, 0, 587, 588, 589, - 612, 0, 590, 572, 637, 411, 342, 553, 579, 778, - 779, 0, 0, 0, 0, 0, 0, 0, 688, 699, - 735, 736, 0, 749, 750, 752, 754, 753, 756, 546, - 547, 764, 0, 0, 435, 357, 358, 494, 495, 532, - 0, 436, 437, 438, 442, 439, 440, 441, 758, 759, - 760, 757, 468, 531, 554, 540, 777, 481, 0, 785, - 627, 628, 786, 745, 744, 343, 0, 337, 338, 702, - 703, 0, 0, 534, 480, 634, 500, 0, 0, 643, - 677, 666, 755, 630, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 394, 0, 0, 463, 681, 662, - 673, 663, 506, 648, 649, 650, 657, 406, 651, 787, - 653, 622, 654, 623, 655, 656, 0, 680, 629, 542, - 482, 0, 697, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 722, 721, 720, 719, 718, 717, + 716, 715, 0, 0, 662, 385, 785, 779, 786, 767, + 770, 769, 744, 0, 341, 642, 466, 520, 402, 709, + 710, 0, 765, 284, 285, 286, 287, 288, 289, 290, + 291, 332, 292, 293, 294, 295, 296, 297, 298, 303, + 304, 305, 306, 307, 308, 309, 310, 712, 301, 302, + 311, 312, 313, 314, 315, 316, 317, 318, 319, 320, + 321, 322, 323, 324, 0, 0, 0, 0, 333, 771, + 772, 773, 774, 775, 0, 0, 334, 335, 336, 0, + 0, 299, 300, 327, 552, 328, 329, 330, 331, 0, + 0, 591, 592, 593, 616, 0, 594, 575, 641, 412, + 342, 556, 583, 782, 783, 0, 0, 0, 0, 0, + 0, 0, 692, 703, 739, 740, 0, 753, 754, 756, + 758, 757, 760, 549, 550, 768, 0, 0, 437, 358, + 359, 496, 497, 535, 0, 438, 439, 440, 444, 441, + 442, 443, 762, 763, 764, 761, 470, 534, 557, 543, + 781, 483, 0, 789, 631, 632, 790, 749, 748, 343, + 0, 337, 338, 706, 707, 0, 0, 537, 482, 638, + 200, 245, 199, 232, 201, 0, 0, 0, 0, 0, + 0, 502, 819, 0, 647, 681, 670, 759, 634, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 395, + 0, 0, 465, 685, 666, 677, 667, 508, 652, 653, + 654, 661, 407, 655, 791, 657, 626, 658, 627, 659, + 660, 0, 684, 633, 545, 484, 0, 701, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 826, 0, 0, + 0, 0, 0, 0, 0, 0, 825, 0, 0, 268, + 0, 0, 0, 0, 0, 0, 366, 269, 628, 755, + 630, 629, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 369, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 546, 574, 0, 588, 0, 434, 435, 0, + 0, 0, 0, 0, 0, 0, 350, 553, 571, 367, + 540, 586, 372, 548, 564, 362, 501, 536, 0, 0, + 352, 569, 547, 479, 351, 0, 529, 392, 409, 389, + 499, 0, 0, 568, 599, 388, 589, 0, 579, 354, + 0, 578, 498, 471, 565, 570, 480, 473, 0, 353, + 567, 478, 472, 448, 399, 615, 449, 450, 451, 452, + 453, 454, 413, 514, 469, 515, 414, 486, 485, 487, + 415, 416, 417, 418, 419, 420, 421, 422, 423, 424, + 0, 0, 0, 0, 0, 610, 611, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 823, + 824, 0, 747, 0, 246, 752, 0, 582, 0, 0, + 0, 0, 0, 0, 551, 0, 0, 455, 0, 0, + 0, 600, 0, 532, 504, 793, 0, 0, 530, 467, + 566, 516, 572, 554, 580, 523, 517, 344, 555, 391, + 481, 363, 365, 780, 393, 396, 400, 401, 492, 493, + 510, 539, 558, 559, 560, 390, 377, 531, 378, 410, + 379, 345, 382, 381, 383, 541, 384, 347, 511, 563, + 0, 406, 527, 476, 348, 475, 512, 562, 561, 364, + 590, 597, 598, 689, 0, 603, 794, 795, 796, 612, + 0, 518, 360, 357, 0, 0, 0, 386, 513, 373, + 375, 376, 374, 507, 509, 617, 618, 619, 621, 0, + 622, 623, 0, 0, 0, 0, 624, 690, 708, 674, + 643, 605, 698, 640, 644, 645, 427, 428, 429, 430, + 711, 0, 0, 0, 596, 456, 457, 0, 398, 397, + 477, 349, 0, 0, 445, 426, 519, 355, 394, 447, + 432, 458, 459, 460, 461, 462, 0, 463, 464, 404, + 339, 340, 788, 387, 500, 713, 750, 751, 635, 0, + 699, 636, 646, 380, 671, 683, 682, 494, 595, 0, + 694, 697, 625, 787, 0, 691, 705, 792, 704, 784, + 506, 0, 538, 702, 649, 0, 695, 668, 669, 0, + 696, 664, 700, 356, 433, 524, 581, 0, 637, 0, + 606, 609, 639, 726, 727, 728, 346, 608, 730, 731, + 732, 733, 734, 735, 736, 729, 577, 672, 648, 675, + 587, 651, 650, 0, 0, 686, 604, 687, 688, 488, + 489, 490, 491, 820, 822, 371, 607, 521, 522, 835, + 673, 0, 0, 0, 0, 0, 0, 0, 0, 678, + 679, 676, 797, 0, 737, 738, 0, 0, 601, 602, + 403, 0, 620, 411, 370, 505, 405, 585, 436, 0, + 613, 680, 614, 525, 526, 741, 746, 742, 743, 745, + 766, 495, 425, 431, 542, 446, 468, 528, 584, 503, + 533, 368, 573, 544, 474, 665, 693, 0, 0, 0, + 0, 0, 0, 0, 0, 79, 0, 0, 325, 326, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 722, 721, 720, 719, 718, 717, + 716, 715, 0, 0, 662, 385, 785, 779, 786, 767, + 770, 769, 744, 0, 341, 642, 466, 520, 402, 709, + 710, 0, 765, 284, 285, 286, 287, 288, 289, 290, + 291, 332, 292, 293, 294, 295, 296, 297, 298, 303, + 304, 305, 306, 307, 308, 309, 310, 712, 301, 302, + 311, 312, 313, 314, 315, 316, 317, 318, 319, 320, + 321, 322, 323, 324, 0, 0, 0, 0, 333, 771, + 772, 773, 774, 775, 0, 0, 334, 335, 336, 0, + 0, 299, 300, 327, 552, 328, 329, 330, 331, 0, + 0, 591, 592, 593, 616, 0, 594, 575, 641, 412, + 342, 556, 583, 782, 783, 0, 0, 0, 0, 0, + 0, 0, 692, 703, 739, 740, 0, 753, 754, 756, + 758, 757, 760, 549, 550, 768, 0, 0, 437, 358, + 359, 496, 497, 535, 0, 438, 439, 440, 444, 441, + 442, 443, 762, 763, 764, 761, 470, 534, 557, 543, + 781, 483, 0, 789, 631, 632, 790, 749, 748, 343, + 0, 337, 338, 706, 707, 0, 0, 537, 482, 638, + 502, 0, 0, 647, 681, 670, 759, 634, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 395, 0, + 0, 465, 685, 666, 677, 667, 508, 652, 653, 654, + 661, 407, 655, 791, 657, 626, 658, 627, 659, 660, + 0, 684, 633, 545, 484, 0, 701, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 1648, 0, 0, 268, 0, + 0, 990, 1000, 0, 0, 366, 269, 628, 755, 630, + 629, 991, 0, 992, 996, 999, 995, 993, 994, 0, + 369, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 546, 574, 0, 588, 0, 434, 435, 0, 997, + 0, 0, 0, 0, 0, 350, 553, 571, 367, 540, + 586, 372, 548, 564, 362, 501, 536, 0, 0, 352, + 569, 547, 479, 351, 0, 529, 392, 409, 389, 499, + 998, 0, 568, 599, 388, 589, 0, 579, 354, 0, + 578, 498, 471, 565, 570, 480, 473, 0, 353, 567, + 478, 472, 448, 399, 615, 449, 450, 451, 452, 453, + 454, 413, 514, 469, 515, 414, 486, 485, 487, 415, + 416, 417, 418, 419, 420, 421, 422, 423, 424, 0, + 0, 0, 0, 0, 610, 611, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 747, 0, 0, 752, 0, 582, 0, 0, 0, + 0, 0, 0, 551, 0, 0, 455, 0, 0, 0, + 600, 0, 532, 504, 793, 0, 0, 530, 467, 566, + 516, 572, 554, 580, 523, 517, 344, 555, 391, 481, + 363, 365, 780, 393, 396, 400, 401, 492, 493, 510, + 539, 558, 559, 560, 390, 377, 531, 378, 410, 379, + 345, 382, 381, 383, 541, 384, 347, 511, 563, 0, + 406, 527, 476, 348, 475, 512, 562, 561, 364, 590, + 597, 598, 689, 0, 603, 794, 795, 796, 612, 0, + 518, 360, 357, 0, 0, 0, 386, 513, 373, 375, + 376, 374, 507, 509, 617, 618, 619, 621, 0, 622, + 623, 0, 0, 0, 0, 624, 690, 708, 674, 643, + 605, 698, 640, 644, 645, 427, 428, 429, 430, 711, + 0, 0, 0, 596, 456, 457, 0, 398, 397, 477, + 349, 0, 0, 445, 426, 519, 355, 394, 447, 432, + 458, 459, 460, 461, 462, 0, 463, 464, 404, 339, + 340, 788, 387, 500, 713, 750, 751, 635, 0, 699, + 636, 646, 380, 671, 683, 682, 494, 595, 0, 694, + 697, 625, 787, 0, 691, 705, 792, 704, 784, 506, + 0, 538, 702, 649, 0, 695, 668, 669, 0, 696, + 664, 700, 356, 433, 524, 581, 0, 637, 0, 606, + 609, 639, 726, 727, 728, 346, 608, 730, 731, 732, + 733, 734, 735, 736, 729, 577, 672, 648, 675, 587, + 651, 650, 0, 0, 686, 604, 687, 688, 488, 489, + 490, 491, 408, 714, 371, 607, 521, 522, 0, 673, + 0, 0, 0, 0, 0, 0, 0, 0, 678, 679, + 676, 797, 0, 737, 738, 0, 0, 601, 602, 403, + 0, 620, 411, 370, 505, 405, 585, 436, 0, 613, + 680, 614, 525, 526, 741, 746, 742, 743, 745, 766, + 495, 425, 431, 542, 446, 468, 528, 584, 503, 533, + 368, 573, 544, 474, 665, 693, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 325, 326, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 268, 0, 0, 0, 0, 0, - 0, 365, 269, 624, 751, 626, 625, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 368, 3048, 3051, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 543, 571, 0, - 584, 0, 432, 433, 0, 0, 0, 0, 0, 0, - 0, 350, 550, 568, 366, 537, 582, 371, 545, 561, - 361, 499, 533, 0, 0, 352, 566, 544, 477, 351, - 0, 526, 391, 408, 388, 497, 0, 0, 565, 595, - 387, 585, 0, 576, 354, 0, 575, 496, 469, 562, - 567, 478, 471, 0, 353, 564, 476, 470, 446, 398, - 611, 447, 448, 449, 450, 451, 452, 412, 512, 467, - 513, 413, 484, 483, 485, 414, 415, 416, 417, 418, - 419, 420, 421, 422, 423, 0, 0, 0, 0, 0, - 606, 607, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 743, 0, 0, - 748, 3052, 578, 0, 0, 0, 3047, 0, 3046, 548, - 3043, 3049, 453, 0, 0, 0, 596, 0, 529, 502, - 789, 0, 0, 527, 465, 563, 514, 569, 551, 577, - 521, 515, 344, 552, 390, 479, 362, 364, 776, 392, - 395, 399, 400, 490, 491, 508, 536, 555, 556, 557, - 389, 376, 528, 377, 409, 378, 345, 381, 380, 382, - 538, 383, 347, 509, 560, 3050, 405, 524, 474, 348, - 473, 510, 559, 558, 363, 586, 593, 594, 685, 0, - 599, 790, 791, 792, 608, 0, 516, 359, 356, 0, - 0, 0, 385, 511, 372, 374, 375, 373, 505, 507, - 613, 614, 615, 617, 0, 618, 619, 0, 0, 0, - 0, 620, 686, 704, 670, 639, 601, 694, 636, 640, - 641, 426, 427, 428, 429, 707, 0, 0, 0, 592, - 454, 455, 0, 397, 396, 475, 349, 0, 0, 443, - 425, 517, 355, 393, 445, 431, 456, 457, 458, 459, - 460, 3044, 461, 462, 403, 339, 340, 784, 386, 498, - 709, 746, 747, 631, 0, 695, 632, 642, 379, 667, - 679, 678, 492, 591, 0, 690, 693, 621, 783, 0, - 687, 701, 788, 700, 780, 504, 0, 535, 698, 645, - 0, 691, 664, 665, 0, 692, 660, 696, 0, 633, - 0, 602, 605, 635, 722, 723, 724, 346, 604, 726, - 727, 728, 729, 730, 731, 732, 725, 574, 668, 644, - 671, 583, 647, 646, 0, 0, 682, 600, 683, 684, - 486, 487, 488, 489, 407, 710, 370, 603, 519, 520, - 0, 669, 0, 0, 0, 0, 0, 0, 0, 0, - 674, 675, 672, 793, 0, 733, 734, 0, 0, 597, - 598, 402, 0, 616, 410, 369, 503, 404, 581, 434, - 0, 609, 676, 610, 522, 523, 737, 742, 738, 739, - 741, 762, 493, 424, 430, 539, 444, 466, 525, 580, - 501, 530, 367, 570, 541, 472, 661, 689, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 325, - 326, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 718, 717, 716, 715, 714, - 713, 712, 711, 0, 0, 658, 384, 781, 775, 782, - 763, 766, 765, 740, 0, 341, 638, 464, 518, 401, - 705, 706, 0, 761, 284, 285, 286, 287, 288, 289, - 290, 291, 332, 292, 293, 294, 295, 296, 297, 298, - 303, 304, 305, 306, 307, 308, 309, 310, 708, 301, - 302, 311, 312, 313, 314, 315, 316, 317, 318, 319, - 320, 321, 322, 323, 324, 0, 0, 0, 0, 333, - 767, 768, 769, 770, 771, 0, 0, 334, 335, 336, - 0, 0, 299, 300, 327, 549, 328, 329, 330, 331, - 0, 0, 587, 588, 589, 612, 0, 590, 572, 637, - 411, 342, 553, 579, 778, 779, 0, 0, 0, 0, - 0, 0, 0, 688, 699, 735, 736, 0, 749, 750, - 752, 754, 753, 756, 546, 547, 764, 0, 0, 435, - 357, 358, 494, 495, 532, 0, 436, 437, 438, 442, - 439, 440, 441, 758, 759, 760, 757, 468, 531, 554, - 540, 777, 481, 0, 785, 627, 628, 786, 745, 744, - 343, 0, 337, 338, 702, 703, 0, 0, 534, 480, - 634, 500, 0, 0, 643, 677, 666, 755, 630, 0, - 1345, 0, 0, 0, 0, 0, 0, 0, 0, 394, - 0, 0, 463, 681, 662, 673, 663, 506, 648, 649, - 650, 657, 406, 651, 787, 653, 622, 654, 623, 655, - 656, 0, 680, 629, 542, 482, 0, 697, 0, 0, + 0, 0, 0, 722, 721, 720, 719, 718, 717, 716, + 715, 0, 0, 662, 385, 785, 779, 786, 767, 770, + 769, 744, 0, 341, 642, 466, 520, 402, 709, 710, + 0, 765, 284, 285, 286, 287, 288, 289, 290, 291, + 332, 292, 293, 294, 295, 296, 297, 298, 303, 304, + 305, 306, 307, 308, 309, 310, 712, 301, 302, 311, + 312, 313, 314, 315, 316, 317, 318, 319, 320, 321, + 322, 323, 324, 0, 0, 0, 0, 333, 771, 772, + 773, 774, 775, 0, 0, 334, 335, 336, 0, 0, + 299, 300, 327, 552, 328, 329, 330, 331, 0, 0, + 591, 592, 593, 616, 0, 594, 575, 641, 412, 342, + 556, 583, 782, 783, 0, 0, 0, 0, 0, 0, + 0, 692, 703, 739, 740, 0, 753, 754, 756, 758, + 757, 760, 549, 550, 768, 0, 0, 437, 358, 359, + 496, 497, 535, 0, 438, 439, 440, 444, 441, 442, + 443, 762, 763, 764, 761, 470, 534, 557, 543, 781, + 483, 0, 789, 631, 632, 790, 749, 748, 343, 0, + 337, 338, 706, 707, 0, 0, 537, 482, 638, 502, + 0, 0, 647, 681, 670, 759, 634, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 395, 0, 0, + 465, 685, 666, 677, 667, 508, 652, 653, 654, 661, + 407, 655, 791, 657, 626, 658, 627, 659, 660, 0, + 684, 633, 545, 484, 0, 701, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 268, 0, 0, + 0, 0, 0, 0, 366, 269, 628, 755, 630, 629, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 369, + 3052, 3055, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 546, 574, 0, 588, 0, 434, 435, 0, 0, 0, + 0, 0, 0, 0, 350, 553, 571, 367, 540, 586, + 372, 548, 564, 362, 501, 536, 0, 0, 352, 569, + 547, 479, 351, 0, 529, 392, 409, 389, 499, 0, + 0, 568, 599, 388, 589, 0, 579, 354, 0, 578, + 498, 471, 565, 570, 480, 473, 0, 353, 567, 478, + 472, 448, 399, 615, 449, 450, 451, 452, 453, 454, + 413, 514, 469, 515, 414, 486, 485, 487, 415, 416, + 417, 418, 419, 420, 421, 422, 423, 424, 0, 0, + 0, 0, 0, 610, 611, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 747, 0, 0, 752, 3056, 582, 0, 0, 0, 3051, + 0, 3050, 551, 3047, 3053, 455, 0, 0, 0, 600, + 0, 532, 504, 793, 0, 0, 530, 467, 566, 516, + 572, 554, 580, 523, 517, 344, 555, 391, 481, 363, + 365, 780, 393, 396, 400, 401, 492, 493, 510, 539, + 558, 559, 560, 390, 377, 531, 378, 410, 379, 345, + 382, 381, 383, 541, 384, 347, 511, 563, 3054, 406, + 527, 476, 348, 475, 512, 562, 561, 364, 590, 597, + 598, 689, 0, 603, 794, 795, 796, 612, 0, 518, + 360, 357, 0, 0, 0, 386, 513, 373, 375, 376, + 374, 507, 509, 617, 618, 619, 621, 0, 622, 623, + 0, 0, 0, 0, 624, 690, 708, 674, 643, 605, + 698, 640, 644, 645, 427, 428, 429, 430, 711, 0, + 0, 0, 596, 456, 457, 0, 398, 397, 477, 349, + 0, 0, 445, 426, 519, 355, 394, 447, 432, 458, + 459, 460, 461, 462, 3048, 463, 464, 404, 339, 340, + 788, 387, 500, 713, 750, 751, 635, 0, 699, 636, + 646, 380, 671, 683, 682, 494, 595, 0, 694, 697, + 625, 787, 0, 691, 705, 792, 704, 784, 506, 0, + 538, 702, 649, 0, 695, 668, 669, 0, 696, 664, + 700, 356, 433, 524, 581, 0, 637, 0, 606, 609, + 639, 726, 727, 728, 346, 608, 730, 731, 732, 733, + 734, 735, 736, 729, 577, 672, 648, 675, 587, 651, + 650, 0, 0, 686, 604, 687, 688, 488, 489, 490, + 491, 408, 714, 371, 607, 521, 522, 0, 673, 0, + 0, 0, 0, 0, 0, 0, 0, 678, 679, 676, + 797, 0, 737, 738, 0, 0, 601, 602, 403, 0, + 620, 411, 370, 505, 405, 585, 436, 0, 613, 680, + 614, 525, 526, 741, 746, 742, 743, 745, 766, 495, + 425, 431, 542, 446, 468, 528, 584, 503, 533, 368, + 573, 544, 474, 665, 693, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 325, 326, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 268, - 0, 0, 0, 0, 0, 0, 365, 269, 624, 751, - 626, 625, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 368, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 543, 571, 0, 584, 0, 3473, 3474, 1325, - 0, 0, 0, 0, 0, 0, 350, 550, 568, 366, - 537, 582, 371, 545, 561, 361, 499, 533, 0, 0, - 3467, 3470, 3471, 3472, 3475, 0, 3480, 3476, 3477, 3478, - 3479, 0, 0, 3463, 3464, 3465, 3466, 1323, 3442, 3468, - 0, 3443, 496, 3444, 3445, 3446, 3447, 3448, 1328, 3449, - 3450, 3451, 3452, 3453, 3460, 3461, 3454, 3455, 3456, 3457, - 3458, 3459, 3481, 3482, 3483, 3484, 3485, 3486, 3487, 3488, - 3490, 3489, 3491, 3492, 3493, 3494, 3495, 3496, 3497, 3498, - 1356, 1358, 1360, 1362, 1365, 606, 607, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 743, 0, 0, 748, 0, 578, 0, 0, - 0, 0, 0, 0, 548, 0, 0, 453, 0, 0, - 0, 3462, 0, 529, 502, 789, 0, 0, 527, 465, - 563, 514, 569, 551, 577, 521, 515, 344, 552, 390, - 479, 362, 364, 776, 392, 395, 399, 400, 490, 491, - 508, 536, 555, 556, 557, 389, 376, 528, 377, 409, - 378, 345, 381, 380, 382, 538, 383, 347, 509, 560, - 0, 405, 524, 474, 348, 473, 510, 559, 558, 363, - 586, 593, 594, 685, 0, 599, 790, 791, 792, 608, - 0, 516, 359, 356, 0, 0, 0, 385, 511, 372, - 374, 375, 373, 505, 507, 613, 614, 615, 617, 0, - 618, 619, 0, 0, 0, 0, 620, 686, 704, 670, - 639, 601, 694, 636, 640, 641, 426, 427, 428, 429, - 707, 0, 0, 0, 592, 454, 455, 0, 397, 396, - 475, 349, 0, 0, 443, 425, 517, 355, 393, 445, - 431, 456, 457, 458, 459, 460, 0, 461, 462, 403, - 339, 340, 784, 386, 498, 709, 746, 747, 631, 0, - 695, 632, 642, 379, 667, 679, 678, 492, 591, 0, - 690, 693, 621, 783, 0, 687, 701, 788, 700, 780, - 504, 0, 535, 698, 645, 0, 691, 664, 665, 0, - 692, 660, 696, 0, 633, 0, 602, 605, 635, 722, - 723, 724, 346, 604, 726, 727, 728, 729, 730, 731, - 732, 725, 574, 668, 644, 671, 583, 647, 646, 0, - 0, 682, 600, 683, 684, 486, 487, 488, 489, 407, - 710, 370, 603, 519, 520, 0, 669, 0, 0, 0, - 0, 0, 0, 0, 0, 674, 675, 672, 793, 0, - 733, 734, 0, 0, 597, 598, 402, 0, 616, 410, - 369, 503, 404, 581, 434, 0, 609, 676, 610, 522, - 523, 737, 742, 738, 739, 741, 762, 493, 424, 430, - 539, 444, 466, 525, 580, 501, 530, 367, 570, 541, - 472, 661, 689, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 325, 326, 0, 0, 0, 0, + 0, 0, 722, 721, 720, 719, 718, 717, 716, 715, + 0, 0, 662, 385, 785, 779, 786, 767, 770, 769, + 744, 0, 341, 642, 466, 520, 402, 709, 710, 0, + 765, 284, 285, 286, 287, 288, 289, 290, 291, 332, + 292, 293, 294, 295, 296, 297, 298, 303, 304, 305, + 306, 307, 308, 309, 310, 712, 301, 302, 311, 312, + 313, 314, 315, 316, 317, 318, 319, 320, 321, 322, + 323, 324, 0, 0, 0, 0, 333, 771, 772, 773, + 774, 775, 0, 0, 334, 335, 336, 0, 0, 299, + 300, 327, 552, 328, 329, 330, 331, 0, 0, 591, + 592, 593, 616, 0, 594, 575, 641, 412, 342, 556, + 583, 782, 783, 0, 0, 0, 0, 0, 0, 0, + 692, 703, 739, 740, 0, 753, 754, 756, 758, 757, + 760, 549, 550, 768, 0, 0, 437, 358, 359, 496, + 497, 535, 0, 438, 439, 440, 444, 441, 442, 443, + 762, 763, 764, 761, 470, 534, 557, 543, 781, 483, + 0, 789, 631, 632, 790, 749, 748, 343, 0, 337, + 338, 706, 707, 0, 0, 537, 482, 638, 502, 0, + 0, 647, 681, 670, 759, 634, 0, 1349, 0, 0, + 0, 0, 0, 0, 0, 0, 395, 0, 0, 465, + 685, 666, 677, 667, 508, 652, 653, 654, 661, 407, + 655, 791, 657, 626, 658, 627, 659, 660, 0, 684, + 633, 545, 484, 0, 701, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 718, 717, 716, 715, 714, 713, 712, 711, 0, 0, - 658, 384, 781, 775, 782, 763, 766, 765, 740, 0, - 341, 3469, 464, 518, 401, 705, 706, 0, 761, 284, - 285, 286, 287, 288, 289, 290, 291, 332, 292, 293, - 294, 295, 296, 297, 298, 303, 304, 305, 306, 307, - 308, 309, 310, 708, 301, 302, 311, 312, 313, 314, - 315, 316, 317, 318, 319, 320, 321, 322, 323, 324, - 0, 0, 0, 0, 333, 767, 768, 769, 770, 771, - 0, 0, 334, 335, 336, 0, 0, 299, 300, 327, - 549, 328, 329, 330, 331, 0, 0, 587, 588, 589, - 612, 0, 590, 572, 637, 411, 342, 553, 579, 778, - 779, 0, 0, 0, 0, 0, 0, 0, 688, 699, - 735, 736, 0, 749, 750, 752, 754, 753, 756, 546, - 547, 764, 0, 0, 435, 357, 358, 494, 495, 532, - 0, 436, 437, 438, 442, 439, 440, 441, 758, 759, - 760, 757, 468, 531, 554, 540, 777, 481, 0, 785, - 627, 628, 786, 745, 744, 3441, 0, 337, 338, 702, - 703, 200, 245, 534, 480, 634, 0, 0, 0, 0, - 0, 0, 500, 0, 0, 643, 677, 666, 755, 630, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 394, 0, 0, 463, 681, 662, 673, 663, 506, 648, - 649, 650, 657, 406, 651, 787, 653, 622, 654, 623, - 655, 656, 1266, 680, 629, 542, 482, 0, 697, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 1812, 0, 0, - 268, 0, 0, 0, 0, 0, 0, 365, 269, 624, - 751, 626, 625, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 368, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 543, 571, 0, 584, 0, 432, 433, - 0, 0, 0, 0, 0, 0, 0, 350, 550, 568, - 366, 537, 582, 371, 545, 561, 361, 499, 533, 0, - 0, 352, 566, 544, 477, 351, 0, 526, 391, 408, - 388, 497, 0, 0, 565, 595, 387, 585, 0, 576, - 354, 0, 575, 496, 469, 562, 567, 478, 471, 0, - 353, 564, 476, 470, 446, 398, 611, 447, 448, 449, - 450, 451, 452, 412, 512, 467, 513, 413, 484, 483, - 485, 414, 415, 416, 417, 418, 419, 420, 421, 422, - 423, 0, 0, 0, 0, 0, 606, 607, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 743, 0, 246, 748, 0, 578, 0, - 0, 0, 0, 0, 0, 548, 0, 0, 453, 0, - 0, 0, 596, 0, 529, 502, 789, 0, 0, 527, - 465, 563, 514, 569, 551, 577, 521, 515, 344, 552, - 390, 479, 362, 364, 776, 392, 395, 399, 400, 490, - 491, 508, 536, 555, 556, 557, 389, 376, 528, 377, - 409, 378, 345, 381, 380, 382, 538, 383, 347, 509, - 560, 0, 405, 524, 474, 348, 473, 510, 559, 558, - 363, 586, 593, 594, 685, 0, 599, 790, 791, 792, - 608, 0, 516, 359, 356, 0, 0, 0, 385, 511, - 372, 374, 375, 373, 505, 507, 613, 614, 615, 617, - 0, 618, 619, 0, 0, 0, 0, 620, 686, 704, - 670, 639, 601, 694, 636, 640, 641, 426, 427, 428, - 429, 707, 0, 0, 0, 592, 454, 455, 0, 397, - 396, 475, 349, 0, 0, 443, 425, 517, 355, 393, - 445, 431, 456, 457, 458, 459, 460, 0, 461, 462, - 403, 339, 340, 784, 386, 498, 709, 746, 747, 631, - 0, 695, 632, 642, 379, 667, 679, 678, 492, 591, - 0, 690, 693, 621, 783, 0, 687, 701, 788, 700, - 780, 504, 0, 535, 698, 645, 0, 691, 664, 665, - 0, 692, 660, 696, 0, 633, 0, 602, 605, 635, - 722, 723, 724, 346, 604, 726, 727, 728, 729, 730, - 731, 732, 725, 574, 668, 644, 671, 583, 647, 646, - 0, 0, 682, 600, 683, 684, 486, 487, 488, 489, - 407, 710, 370, 603, 519, 520, 0, 669, 0, 0, - 0, 0, 0, 0, 0, 0, 674, 675, 672, 793, - 0, 733, 734, 0, 0, 597, 598, 402, 0, 616, - 410, 369, 503, 404, 581, 434, 0, 609, 676, 610, - 522, 523, 737, 742, 738, 739, 741, 762, 493, 424, - 430, 539, 444, 466, 525, 580, 501, 530, 367, 570, - 541, 472, 661, 689, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 268, 0, 0, 0, + 0, 0, 0, 366, 269, 628, 755, 630, 629, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 369, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 546, + 574, 0, 588, 0, 3477, 3478, 1329, 0, 0, 0, + 0, 0, 0, 350, 553, 571, 367, 540, 586, 372, + 548, 564, 362, 501, 536, 0, 0, 3471, 3474, 3475, + 3476, 3479, 0, 3484, 3480, 3481, 3482, 3483, 0, 0, + 3467, 3468, 3469, 3470, 1327, 3446, 3472, 0, 3447, 498, + 3448, 3449, 3450, 3451, 3452, 1332, 3453, 3454, 3455, 3456, + 3457, 3464, 3465, 3458, 3459, 3460, 3461, 3462, 3463, 3485, + 3486, 3487, 3488, 3489, 3490, 3491, 3492, 3494, 3493, 3495, + 3496, 3497, 3498, 3499, 3500, 3501, 3502, 1360, 1362, 1364, + 1366, 1369, 610, 611, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 747, + 0, 0, 752, 0, 582, 0, 0, 0, 0, 0, + 0, 551, 0, 0, 455, 0, 0, 0, 3466, 0, + 532, 504, 793, 0, 0, 530, 467, 566, 516, 572, + 554, 580, 523, 517, 344, 555, 391, 481, 363, 365, + 780, 393, 396, 400, 401, 492, 493, 510, 539, 558, + 559, 560, 390, 377, 531, 378, 410, 379, 345, 382, + 381, 383, 541, 384, 347, 511, 563, 0, 406, 527, + 476, 348, 475, 512, 562, 561, 364, 590, 597, 598, + 689, 0, 603, 794, 795, 796, 612, 0, 518, 360, + 357, 0, 0, 0, 386, 513, 373, 375, 376, 374, + 507, 509, 617, 618, 619, 621, 0, 622, 623, 0, + 0, 0, 0, 624, 690, 708, 674, 643, 605, 698, + 640, 644, 645, 427, 428, 429, 430, 711, 0, 0, + 0, 596, 456, 457, 0, 398, 397, 477, 349, 0, + 0, 445, 426, 519, 355, 394, 447, 432, 458, 459, + 460, 461, 462, 0, 463, 464, 404, 339, 340, 788, + 387, 500, 713, 750, 751, 635, 0, 699, 636, 646, + 380, 671, 683, 682, 494, 595, 0, 694, 697, 625, + 787, 0, 691, 705, 792, 704, 784, 506, 0, 538, + 702, 649, 0, 695, 668, 669, 0, 696, 664, 700, + 356, 433, 524, 581, 0, 637, 0, 606, 609, 639, + 726, 727, 728, 346, 608, 730, 731, 732, 733, 734, + 735, 736, 729, 577, 672, 648, 675, 587, 651, 650, + 0, 0, 686, 604, 687, 688, 488, 489, 490, 491, + 408, 714, 371, 607, 521, 522, 0, 673, 0, 0, + 0, 0, 0, 0, 0, 0, 678, 679, 676, 797, + 0, 737, 738, 0, 0, 601, 602, 403, 0, 620, + 411, 370, 505, 405, 585, 436, 0, 613, 680, 614, + 525, 526, 741, 746, 742, 743, 745, 766, 495, 425, + 431, 542, 446, 468, 528, 584, 503, 533, 368, 573, + 544, 474, 665, 693, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 325, 326, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 718, 717, 716, 715, 714, 713, 712, 711, 0, - 0, 658, 384, 781, 775, 782, 763, 766, 765, 740, - 0, 341, 638, 464, 518, 401, 705, 706, 0, 761, + 0, 722, 721, 720, 719, 718, 717, 716, 715, 0, + 0, 662, 385, 785, 779, 786, 767, 770, 769, 744, + 0, 341, 3473, 466, 520, 402, 709, 710, 0, 765, 284, 285, 286, 287, 288, 289, 290, 291, 332, 292, 293, 294, 295, 296, 297, 298, 303, 304, 305, 306, - 307, 308, 309, 310, 708, 301, 302, 311, 312, 313, + 307, 308, 309, 310, 712, 301, 302, 311, 312, 313, 314, 315, 316, 317, 318, 319, 320, 321, 322, 323, - 324, 0, 0, 0, 0, 333, 767, 768, 769, 770, - 771, 0, 0, 334, 335, 336, 0, 0, 299, 300, - 327, 549, 328, 329, 330, 331, 0, 0, 587, 588, - 589, 612, 0, 590, 572, 637, 411, 342, 553, 579, - 778, 779, 0, 0, 0, 0, 0, 0, 0, 688, - 699, 735, 736, 0, 749, 750, 752, 754, 753, 756, - 546, 547, 764, 0, 0, 435, 357, 358, 494, 495, - 532, 0, 436, 437, 438, 442, 439, 440, 441, 758, - 759, 760, 757, 468, 531, 554, 540, 777, 481, 0, - 785, 627, 628, 786, 745, 744, 343, 0, 337, 338, - 702, 703, 0, 0, 534, 480, 634, 500, 2455, 0, - 643, 677, 666, 755, 630, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 394, 0, 0, 463, 681, - 662, 673, 663, 506, 648, 649, 650, 657, 406, 651, - 787, 653, 622, 654, 623, 655, 656, 0, 680, 629, - 542, 482, 0, 697, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 268, 0, 0, 0, 0, - 0, 0, 365, 269, 624, 751, 626, 625, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 368, 0, 3063, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 543, 571, - 0, 584, 0, 432, 433, 0, 0, 0, 0, 0, - 0, 0, 350, 550, 568, 366, 537, 582, 371, 545, - 561, 361, 499, 533, 0, 0, 352, 566, 544, 477, - 351, 0, 526, 391, 408, 388, 497, 0, 0, 565, - 595, 387, 585, 0, 576, 354, 0, 575, 496, 469, - 562, 567, 478, 471, 0, 353, 564, 476, 470, 446, - 398, 611, 447, 448, 449, 450, 451, 452, 412, 512, - 467, 513, 413, 484, 483, 485, 414, 415, 416, 417, - 418, 419, 420, 421, 422, 423, 0, 0, 0, 0, - 0, 606, 607, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 743, 0, - 0, 748, 3062, 578, 0, 0, 0, 3068, 3065, 3067, - 548, 0, 3066, 453, 0, 0, 0, 596, 0, 529, - 502, 789, 0, 3060, 527, 465, 563, 514, 569, 551, - 577, 521, 515, 344, 552, 390, 479, 362, 364, 776, - 392, 395, 399, 400, 490, 491, 508, 536, 555, 556, - 557, 389, 376, 528, 377, 409, 378, 345, 381, 380, - 382, 538, 383, 347, 509, 560, 0, 405, 524, 474, - 348, 473, 510, 559, 558, 363, 586, 593, 594, 685, - 0, 599, 790, 791, 792, 608, 0, 516, 359, 356, - 0, 0, 0, 385, 511, 372, 374, 375, 373, 505, - 507, 613, 614, 615, 617, 0, 618, 619, 0, 0, - 0, 0, 620, 686, 704, 670, 639, 601, 694, 636, - 640, 641, 426, 427, 428, 429, 707, 0, 0, 0, - 592, 454, 455, 0, 397, 396, 475, 349, 0, 0, - 443, 425, 517, 355, 393, 445, 431, 456, 457, 458, - 459, 460, 0, 461, 462, 403, 339, 340, 784, 386, - 498, 709, 746, 747, 631, 0, 695, 632, 642, 379, - 667, 679, 678, 492, 591, 0, 690, 693, 621, 783, - 0, 687, 701, 788, 700, 780, 504, 0, 535, 698, - 645, 0, 691, 664, 665, 0, 692, 660, 696, 0, - 633, 0, 602, 605, 635, 722, 723, 724, 346, 604, - 726, 727, 728, 729, 730, 731, 732, 725, 574, 668, - 644, 671, 583, 647, 646, 0, 0, 682, 600, 683, - 684, 486, 487, 488, 489, 407, 710, 370, 603, 519, - 520, 0, 669, 0, 0, 0, 0, 0, 0, 0, - 0, 674, 675, 672, 793, 0, 733, 734, 0, 0, - 597, 598, 402, 0, 616, 410, 369, 503, 404, 581, - 434, 0, 609, 676, 610, 522, 523, 737, 742, 738, - 739, 741, 762, 493, 424, 430, 539, 444, 466, 525, - 580, 501, 530, 367, 570, 541, 472, 661, 689, 0, + 324, 0, 0, 0, 0, 333, 771, 772, 773, 774, + 775, 0, 0, 334, 335, 336, 0, 0, 299, 300, + 327, 552, 328, 329, 330, 331, 0, 0, 591, 592, + 593, 616, 0, 594, 575, 641, 412, 342, 556, 583, + 782, 783, 0, 0, 0, 0, 0, 0, 0, 692, + 703, 739, 740, 0, 753, 754, 756, 758, 757, 760, + 549, 550, 768, 0, 0, 437, 358, 359, 496, 497, + 535, 0, 438, 439, 440, 444, 441, 442, 443, 762, + 763, 764, 761, 470, 534, 557, 543, 781, 483, 0, + 789, 631, 632, 790, 749, 748, 3445, 0, 337, 338, + 706, 707, 200, 245, 537, 482, 638, 0, 0, 0, + 0, 0, 0, 502, 0, 0, 647, 681, 670, 759, + 634, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 395, 0, 0, 465, 685, 666, 677, 667, 508, + 652, 653, 654, 661, 407, 655, 791, 657, 626, 658, + 627, 659, 660, 1270, 684, 633, 545, 484, 0, 701, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 1816, 0, + 0, 268, 0, 0, 0, 0, 0, 0, 366, 269, + 628, 755, 630, 629, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 369, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 546, 574, 0, 588, 0, 434, + 435, 0, 0, 0, 0, 0, 0, 0, 350, 553, + 571, 367, 540, 586, 372, 548, 564, 362, 501, 536, + 0, 0, 352, 569, 547, 479, 351, 0, 529, 392, + 409, 389, 499, 0, 0, 568, 599, 388, 589, 0, + 579, 354, 0, 578, 498, 471, 565, 570, 480, 473, + 0, 353, 567, 478, 472, 448, 399, 615, 449, 450, + 451, 452, 453, 454, 413, 514, 469, 515, 414, 486, + 485, 487, 415, 416, 417, 418, 419, 420, 421, 422, + 423, 424, 0, 0, 0, 0, 0, 610, 611, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 747, 0, 246, 752, 0, 582, + 0, 0, 0, 0, 0, 0, 551, 0, 0, 455, + 0, 0, 0, 600, 0, 532, 504, 793, 0, 0, + 530, 467, 566, 516, 572, 554, 580, 523, 517, 344, + 555, 391, 481, 363, 365, 780, 393, 396, 400, 401, + 492, 493, 510, 539, 558, 559, 560, 390, 377, 531, + 378, 410, 379, 345, 382, 381, 383, 541, 384, 347, + 511, 563, 0, 406, 527, 476, 348, 475, 512, 562, + 561, 364, 590, 597, 598, 689, 0, 603, 794, 795, + 796, 612, 0, 518, 360, 357, 0, 0, 0, 386, + 513, 373, 375, 376, 374, 507, 509, 617, 618, 619, + 621, 0, 622, 623, 0, 0, 0, 0, 624, 690, + 708, 674, 643, 605, 698, 640, 644, 645, 427, 428, + 429, 430, 711, 0, 0, 0, 596, 456, 457, 0, + 398, 397, 477, 349, 0, 0, 445, 426, 519, 355, + 394, 447, 432, 458, 459, 460, 461, 462, 0, 463, + 464, 404, 339, 340, 788, 387, 500, 713, 750, 751, + 635, 0, 699, 636, 646, 380, 671, 683, 682, 494, + 595, 0, 694, 697, 625, 787, 0, 691, 705, 792, + 704, 784, 506, 0, 538, 702, 649, 0, 695, 668, + 669, 0, 696, 664, 700, 356, 433, 524, 581, 0, + 637, 0, 606, 609, 639, 726, 727, 728, 346, 608, + 730, 731, 732, 733, 734, 735, 736, 729, 577, 672, + 648, 675, 587, 651, 650, 0, 0, 686, 604, 687, + 688, 488, 489, 490, 491, 408, 714, 371, 607, 521, + 522, 0, 673, 0, 0, 0, 0, 0, 0, 0, + 0, 678, 679, 676, 797, 0, 737, 738, 0, 0, + 601, 602, 403, 0, 620, 411, 370, 505, 405, 585, + 436, 0, 613, 680, 614, 525, 526, 741, 746, 742, + 743, 745, 766, 495, 425, 431, 542, 446, 468, 528, + 584, 503, 533, 368, 573, 544, 474, 665, 693, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 325, 326, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 718, 717, 716, 715, - 714, 713, 712, 711, 0, 0, 658, 384, 781, 775, - 782, 763, 766, 765, 740, 0, 341, 638, 464, 518, - 401, 705, 706, 0, 761, 284, 285, 286, 287, 288, + 0, 0, 0, 0, 0, 0, 722, 721, 720, 719, + 718, 717, 716, 715, 0, 0, 662, 385, 785, 779, + 786, 767, 770, 769, 744, 0, 341, 642, 466, 520, + 402, 709, 710, 0, 765, 284, 285, 286, 287, 288, 289, 290, 291, 332, 292, 293, 294, 295, 296, 297, - 298, 303, 304, 305, 306, 307, 308, 309, 310, 708, + 298, 303, 304, 305, 306, 307, 308, 309, 310, 712, 301, 302, 311, 312, 313, 314, 315, 316, 317, 318, 319, 320, 321, 322, 323, 324, 0, 0, 0, 0, - 333, 767, 768, 769, 770, 771, 0, 0, 334, 335, - 336, 0, 0, 299, 300, 327, 549, 328, 329, 330, - 331, 0, 0, 587, 588, 589, 612, 0, 590, 572, - 637, 411, 342, 553, 579, 778, 779, 0, 0, 0, - 0, 0, 0, 0, 688, 699, 735, 736, 0, 749, - 750, 752, 754, 753, 756, 546, 547, 764, 0, 0, - 435, 357, 358, 494, 495, 532, 0, 436, 437, 438, - 442, 439, 440, 441, 758, 759, 760, 757, 468, 531, - 554, 540, 777, 481, 0, 785, 627, 628, 786, 745, - 744, 343, 0, 337, 338, 702, 703, 0, 0, 534, - 480, 634, 500, 0, 0, 643, 677, 666, 755, 630, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 394, 0, 0, 463, 681, 662, 673, 663, 506, 648, - 649, 650, 657, 406, 651, 787, 653, 622, 654, 623, - 655, 656, 0, 680, 629, 542, 482, 0, 697, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 268, 0, 0, 0, 0, 0, 0, 365, 269, 624, - 751, 626, 625, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 368, 0, 3063, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 543, 571, 0, 584, 0, 432, 433, - 0, 0, 0, 0, 0, 0, 0, 350, 550, 568, - 366, 537, 582, 371, 545, 561, 361, 499, 533, 0, - 0, 352, 566, 544, 477, 351, 0, 526, 391, 408, - 388, 497, 0, 0, 565, 595, 387, 585, 0, 576, - 354, 0, 575, 496, 469, 562, 567, 478, 471, 0, - 353, 564, 476, 470, 446, 398, 611, 447, 448, 449, - 450, 451, 452, 412, 512, 467, 513, 413, 484, 483, - 485, 414, 415, 416, 417, 418, 419, 420, 421, 422, - 423, 0, 0, 0, 0, 0, 606, 607, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 743, 0, 0, 748, 3062, 578, 0, - 0, 0, 3068, 3065, 3067, 548, 0, 3066, 453, 0, - 0, 0, 596, 0, 529, 502, 789, 0, 0, 527, - 465, 563, 514, 569, 551, 577, 521, 515, 344, 552, - 390, 479, 362, 364, 776, 392, 395, 399, 400, 490, - 491, 508, 536, 555, 556, 557, 389, 376, 528, 377, - 409, 378, 345, 381, 380, 382, 538, 383, 347, 509, - 560, 0, 405, 524, 474, 348, 473, 510, 559, 558, - 363, 586, 593, 594, 685, 0, 599, 790, 791, 792, - 608, 0, 516, 359, 356, 0, 0, 0, 385, 511, - 372, 374, 375, 373, 505, 507, 613, 614, 615, 617, - 0, 618, 619, 0, 0, 0, 0, 620, 686, 704, - 670, 639, 601, 694, 636, 640, 641, 426, 427, 428, - 429, 707, 0, 0, 0, 592, 454, 455, 0, 397, - 396, 475, 349, 0, 0, 443, 425, 517, 355, 393, - 445, 431, 456, 457, 458, 459, 460, 0, 461, 462, - 403, 339, 340, 784, 386, 498, 709, 746, 747, 631, - 0, 695, 632, 642, 379, 667, 679, 678, 492, 591, - 0, 690, 693, 621, 783, 0, 687, 701, 788, 700, - 780, 504, 0, 535, 698, 645, 0, 691, 664, 665, - 0, 692, 660, 696, 0, 633, 0, 602, 605, 635, - 722, 723, 724, 346, 604, 726, 727, 728, 729, 730, - 731, 732, 725, 574, 668, 644, 671, 583, 647, 646, - 0, 0, 682, 600, 683, 684, 486, 487, 488, 489, - 407, 710, 370, 603, 519, 520, 0, 669, 0, 0, - 0, 0, 0, 0, 0, 0, 674, 675, 672, 793, - 0, 733, 734, 0, 0, 597, 598, 402, 0, 616, - 410, 369, 503, 404, 581, 434, 0, 609, 676, 610, - 522, 523, 737, 742, 738, 739, 741, 762, 493, 424, - 430, 539, 444, 466, 525, 580, 501, 530, 367, 570, - 541, 472, 661, 689, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 325, 326, 0, 0, 0, + 333, 771, 772, 773, 774, 775, 0, 0, 334, 335, + 336, 0, 0, 299, 300, 327, 552, 328, 329, 330, + 331, 0, 0, 591, 592, 593, 616, 0, 594, 575, + 641, 412, 342, 556, 583, 782, 783, 0, 0, 0, + 0, 0, 0, 0, 692, 703, 739, 740, 0, 753, + 754, 756, 758, 757, 760, 549, 550, 768, 0, 0, + 437, 358, 359, 496, 497, 535, 0, 438, 439, 440, + 444, 441, 442, 443, 762, 763, 764, 761, 470, 534, + 557, 543, 781, 483, 0, 789, 631, 632, 790, 749, + 748, 343, 0, 337, 338, 706, 707, 0, 0, 537, + 482, 638, 502, 2459, 0, 647, 681, 670, 759, 634, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 395, 0, 0, 465, 685, 666, 677, 667, 508, 652, + 653, 654, 661, 407, 655, 791, 657, 626, 658, 627, + 659, 660, 0, 684, 633, 545, 484, 0, 701, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 268, 0, 0, 0, 0, 0, 0, 366, 269, 628, + 755, 630, 629, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 369, 0, 3067, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 546, 574, 0, 588, 0, 434, 435, + 0, 0, 0, 0, 0, 0, 0, 350, 553, 571, + 367, 540, 586, 372, 548, 564, 362, 501, 536, 0, + 0, 352, 569, 547, 479, 351, 0, 529, 392, 409, + 389, 499, 0, 0, 568, 599, 388, 589, 0, 579, + 354, 0, 578, 498, 471, 565, 570, 480, 473, 0, + 353, 567, 478, 472, 448, 399, 615, 449, 450, 451, + 452, 453, 454, 413, 514, 469, 515, 414, 486, 485, + 487, 415, 416, 417, 418, 419, 420, 421, 422, 423, + 424, 0, 0, 0, 0, 0, 610, 611, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 747, 0, 0, 752, 3066, 582, 0, + 0, 0, 3072, 3069, 3071, 551, 0, 3070, 455, 0, + 0, 0, 600, 0, 532, 504, 793, 0, 3064, 530, + 467, 566, 516, 572, 554, 580, 523, 517, 344, 555, + 391, 481, 363, 365, 780, 393, 396, 400, 401, 492, + 493, 510, 539, 558, 559, 560, 390, 377, 531, 378, + 410, 379, 345, 382, 381, 383, 541, 384, 347, 511, + 563, 0, 406, 527, 476, 348, 475, 512, 562, 561, + 364, 590, 597, 598, 689, 0, 603, 794, 795, 796, + 612, 0, 518, 360, 357, 0, 0, 0, 386, 513, + 373, 375, 376, 374, 507, 509, 617, 618, 619, 621, + 0, 622, 623, 0, 0, 0, 0, 624, 690, 708, + 674, 643, 605, 698, 640, 644, 645, 427, 428, 429, + 430, 711, 0, 0, 0, 596, 456, 457, 0, 398, + 397, 477, 349, 0, 0, 445, 426, 519, 355, 394, + 447, 432, 458, 459, 460, 461, 462, 0, 463, 464, + 404, 339, 340, 788, 387, 500, 713, 750, 751, 635, + 0, 699, 636, 646, 380, 671, 683, 682, 494, 595, + 0, 694, 697, 625, 787, 0, 691, 705, 792, 704, + 784, 506, 0, 538, 702, 649, 0, 695, 668, 669, + 0, 696, 664, 700, 356, 433, 524, 581, 0, 637, + 0, 606, 609, 639, 726, 727, 728, 346, 608, 730, + 731, 732, 733, 734, 735, 736, 729, 577, 672, 648, + 675, 587, 651, 650, 0, 0, 686, 604, 687, 688, + 488, 489, 490, 491, 408, 714, 371, 607, 521, 522, + 0, 673, 0, 0, 0, 0, 0, 0, 0, 0, + 678, 679, 676, 797, 0, 737, 738, 0, 0, 601, + 602, 403, 0, 620, 411, 370, 505, 405, 585, 436, + 0, 613, 680, 614, 525, 526, 741, 746, 742, 743, + 745, 766, 495, 425, 431, 542, 446, 468, 528, 584, + 503, 533, 368, 573, 544, 474, 665, 693, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 325, + 326, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 722, 721, 720, 719, 718, + 717, 716, 715, 0, 0, 662, 385, 785, 779, 786, + 767, 770, 769, 744, 0, 341, 642, 466, 520, 402, + 709, 710, 0, 765, 284, 285, 286, 287, 288, 289, + 290, 291, 332, 292, 293, 294, 295, 296, 297, 298, + 303, 304, 305, 306, 307, 308, 309, 310, 712, 301, + 302, 311, 312, 313, 314, 315, 316, 317, 318, 319, + 320, 321, 322, 323, 324, 0, 0, 0, 0, 333, + 771, 772, 773, 774, 775, 0, 0, 334, 335, 336, + 0, 0, 299, 300, 327, 552, 328, 329, 330, 331, + 0, 0, 591, 592, 593, 616, 0, 594, 575, 641, + 412, 342, 556, 583, 782, 783, 0, 0, 0, 0, + 0, 0, 0, 692, 703, 739, 740, 0, 753, 754, + 756, 758, 757, 760, 549, 550, 768, 0, 0, 437, + 358, 359, 496, 497, 535, 0, 438, 439, 440, 444, + 441, 442, 443, 762, 763, 764, 761, 470, 534, 557, + 543, 781, 483, 0, 789, 631, 632, 790, 749, 748, + 343, 0, 337, 338, 706, 707, 0, 0, 537, 482, + 638, 502, 0, 0, 647, 681, 670, 759, 634, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 395, + 0, 0, 465, 685, 666, 677, 667, 508, 652, 653, + 654, 661, 407, 655, 791, 657, 626, 658, 627, 659, + 660, 0, 684, 633, 545, 484, 0, 701, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 718, 717, 716, 715, 714, 713, 712, 711, 0, - 0, 658, 384, 781, 775, 782, 763, 766, 765, 740, - 0, 341, 638, 464, 518, 401, 705, 706, 0, 761, - 284, 285, 286, 287, 288, 289, 290, 291, 332, 292, - 293, 294, 295, 296, 297, 298, 303, 304, 305, 306, - 307, 308, 309, 310, 708, 301, 302, 311, 312, 313, - 314, 315, 316, 317, 318, 319, 320, 321, 322, 323, - 324, 0, 0, 0, 0, 333, 767, 768, 769, 770, - 771, 0, 0, 334, 335, 336, 0, 0, 299, 300, - 327, 549, 328, 329, 330, 331, 0, 0, 587, 588, - 589, 612, 0, 590, 572, 637, 411, 342, 553, 579, - 778, 779, 0, 0, 0, 0, 0, 0, 0, 688, - 699, 735, 736, 0, 749, 750, 752, 754, 753, 756, - 546, 547, 764, 0, 0, 435, 357, 358, 494, 495, - 532, 0, 436, 437, 438, 442, 439, 440, 441, 758, - 759, 760, 757, 468, 531, 554, 540, 777, 481, 0, - 785, 627, 628, 786, 745, 744, 343, 0, 337, 338, - 702, 703, 200, 245, 534, 480, 634, 0, 0, 0, - 0, 0, 0, 500, 0, 0, 643, 677, 666, 755, - 630, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 394, 0, 0, 463, 681, 662, 673, 663, 506, - 648, 649, 650, 657, 406, 651, 787, 653, 622, 654, - 623, 655, 656, 1266, 680, 629, 542, 482, 0, 697, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 238, 3220, - 0, 268, 0, 0, 0, 0, 0, 0, 365, 269, - 624, 751, 626, 625, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 368, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 543, 571, 0, 584, 0, 432, - 433, 0, 0, 0, 0, 0, 0, 0, 350, 550, - 568, 366, 537, 582, 371, 545, 561, 361, 499, 533, - 0, 0, 352, 566, 544, 477, 351, 0, 526, 391, - 408, 388, 497, 0, 0, 565, 595, 387, 585, 0, - 576, 354, 0, 575, 496, 469, 562, 567, 478, 471, - 0, 353, 564, 476, 470, 446, 398, 611, 447, 448, - 449, 450, 451, 452, 412, 512, 467, 513, 413, 484, - 483, 485, 414, 415, 416, 417, 418, 419, 420, 421, - 422, 423, 0, 0, 0, 0, 0, 606, 607, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 743, 0, 246, 748, 0, 578, - 0, 0, 0, 0, 0, 0, 548, 0, 0, 453, - 0, 0, 0, 596, 0, 529, 502, 789, 0, 0, - 527, 465, 563, 514, 569, 551, 577, 521, 515, 344, - 552, 390, 479, 362, 364, 776, 392, 395, 399, 400, - 490, 491, 508, 536, 555, 556, 557, 389, 376, 528, - 377, 409, 378, 345, 381, 380, 382, 538, 383, 347, - 509, 560, 0, 405, 524, 474, 348, 473, 510, 559, - 558, 363, 586, 593, 594, 685, 0, 599, 790, 791, - 792, 608, 0, 516, 359, 356, 0, 0, 0, 385, - 511, 372, 374, 375, 373, 505, 507, 613, 614, 615, - 617, 0, 618, 619, 0, 0, 0, 0, 620, 686, - 704, 670, 639, 601, 694, 636, 640, 641, 426, 427, - 428, 429, 707, 0, 0, 0, 592, 454, 455, 0, - 397, 396, 475, 349, 0, 0, 443, 425, 517, 355, - 393, 445, 431, 456, 457, 458, 459, 460, 0, 461, - 462, 403, 339, 340, 784, 386, 498, 709, 746, 747, - 631, 0, 695, 632, 642, 379, 667, 679, 678, 492, - 591, 0, 690, 693, 621, 783, 0, 687, 701, 788, - 700, 780, 504, 0, 535, 698, 645, 0, 691, 664, - 665, 0, 692, 660, 696, 0, 633, 0, 602, 605, - 635, 722, 723, 724, 346, 604, 726, 727, 728, 729, - 730, 731, 732, 725, 574, 668, 644, 671, 583, 647, - 646, 0, 0, 682, 600, 683, 684, 486, 487, 488, - 489, 407, 710, 370, 603, 519, 520, 0, 669, 0, - 0, 0, 0, 0, 0, 0, 0, 674, 675, 672, - 793, 0, 733, 734, 0, 0, 597, 598, 402, 0, - 616, 410, 369, 503, 404, 581, 434, 0, 609, 676, - 610, 522, 523, 737, 742, 738, 739, 741, 762, 493, - 424, 430, 539, 444, 466, 525, 580, 501, 530, 367, - 570, 541, 472, 661, 689, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 325, 326, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 268, + 0, 0, 0, 0, 0, 0, 366, 269, 628, 755, + 630, 629, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 369, 0, 3067, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 546, 574, 0, 588, 0, 434, 435, 0, + 0, 0, 0, 0, 0, 0, 350, 553, 571, 367, + 540, 586, 372, 548, 564, 362, 501, 536, 0, 0, + 352, 569, 547, 479, 351, 0, 529, 392, 409, 389, + 499, 0, 0, 568, 599, 388, 589, 0, 579, 354, + 0, 578, 498, 471, 565, 570, 480, 473, 0, 353, + 567, 478, 472, 448, 399, 615, 449, 450, 451, 452, + 453, 454, 413, 514, 469, 515, 414, 486, 485, 487, + 415, 416, 417, 418, 419, 420, 421, 422, 423, 424, + 0, 0, 0, 0, 0, 610, 611, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 747, 0, 0, 752, 3066, 582, 0, 0, + 0, 3072, 3069, 3071, 551, 0, 3070, 455, 0, 0, + 0, 600, 0, 532, 504, 793, 0, 0, 530, 467, + 566, 516, 572, 554, 580, 523, 517, 344, 555, 391, + 481, 363, 365, 780, 393, 396, 400, 401, 492, 493, + 510, 539, 558, 559, 560, 390, 377, 531, 378, 410, + 379, 345, 382, 381, 383, 541, 384, 347, 511, 563, + 0, 406, 527, 476, 348, 475, 512, 562, 561, 364, + 590, 597, 598, 689, 0, 603, 794, 795, 796, 612, + 0, 518, 360, 357, 0, 0, 0, 386, 513, 373, + 375, 376, 374, 507, 509, 617, 618, 619, 621, 0, + 622, 623, 0, 0, 0, 0, 624, 690, 708, 674, + 643, 605, 698, 640, 644, 645, 427, 428, 429, 430, + 711, 0, 0, 0, 596, 456, 457, 0, 398, 397, + 477, 349, 0, 0, 445, 426, 519, 355, 394, 447, + 432, 458, 459, 460, 461, 462, 0, 463, 464, 404, + 339, 340, 788, 387, 500, 713, 750, 751, 635, 0, + 699, 636, 646, 380, 671, 683, 682, 494, 595, 0, + 694, 697, 625, 787, 0, 691, 705, 792, 704, 784, + 506, 0, 538, 702, 649, 0, 695, 668, 669, 0, + 696, 664, 700, 356, 433, 524, 581, 0, 637, 0, + 606, 609, 639, 726, 727, 728, 346, 608, 730, 731, + 732, 733, 734, 735, 736, 729, 577, 672, 648, 675, + 587, 651, 650, 0, 0, 686, 604, 687, 688, 488, + 489, 490, 491, 408, 714, 371, 607, 521, 522, 0, + 673, 0, 0, 0, 0, 0, 0, 0, 0, 678, + 679, 676, 797, 0, 737, 738, 0, 0, 601, 602, + 403, 0, 620, 411, 370, 505, 405, 585, 436, 0, + 613, 680, 614, 525, 526, 741, 746, 742, 743, 745, + 766, 495, 425, 431, 542, 446, 468, 528, 584, 503, + 533, 368, 573, 544, 474, 665, 693, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 325, 326, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 718, 717, 716, 715, 714, 713, 712, 711, - 0, 0, 658, 384, 781, 775, 782, 763, 766, 765, - 740, 0, 341, 638, 464, 518, 401, 705, 706, 0, - 761, 284, 285, 286, 287, 288, 289, 290, 291, 332, - 292, 293, 294, 295, 296, 297, 298, 303, 304, 305, - 306, 307, 308, 309, 310, 708, 301, 302, 311, 312, - 313, 314, 315, 316, 317, 318, 319, 320, 321, 322, - 323, 324, 0, 0, 0, 0, 333, 767, 768, 769, - 770, 771, 0, 0, 334, 335, 336, 0, 0, 299, - 300, 327, 549, 328, 329, 330, 331, 0, 0, 587, - 588, 589, 612, 0, 590, 572, 637, 411, 342, 553, - 579, 778, 779, 0, 0, 0, 0, 0, 0, 0, - 688, 699, 735, 736, 0, 749, 750, 752, 754, 753, - 756, 546, 547, 764, 0, 0, 435, 357, 358, 494, - 495, 532, 0, 436, 437, 438, 442, 439, 440, 441, - 758, 759, 760, 757, 468, 531, 554, 540, 777, 481, - 0, 785, 627, 628, 786, 745, 744, 343, 0, 337, - 338, 702, 703, 200, 245, 534, 480, 634, 0, 0, - 0, 0, 0, 0, 500, 0, 0, 643, 677, 666, - 755, 630, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 394, 0, 0, 463, 681, 662, 673, 663, - 506, 648, 649, 650, 657, 406, 651, 787, 653, 622, - 654, 623, 655, 656, 1266, 680, 629, 542, 482, 0, - 697, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 238, - 2964, 0, 268, 0, 0, 0, 0, 0, 0, 365, - 269, 624, 751, 626, 625, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 368, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 543, 571, 0, 584, 0, - 432, 433, 0, 0, 0, 0, 0, 0, 0, 350, - 550, 568, 366, 537, 582, 371, 545, 561, 361, 499, - 533, 0, 0, 352, 566, 544, 477, 351, 0, 526, - 391, 408, 388, 497, 0, 0, 565, 595, 387, 585, - 0, 576, 354, 0, 575, 496, 469, 562, 567, 478, - 471, 0, 353, 564, 476, 470, 446, 398, 611, 447, - 448, 449, 450, 451, 452, 412, 512, 467, 513, 413, - 484, 483, 485, 414, 415, 416, 417, 418, 419, 420, - 421, 422, 423, 0, 0, 0, 0, 0, 606, 607, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 743, 0, 246, 748, 0, - 578, 0, 0, 0, 0, 0, 0, 548, 0, 0, - 453, 0, 0, 0, 596, 0, 529, 502, 789, 0, - 0, 527, 465, 563, 514, 569, 551, 577, 521, 515, - 344, 552, 390, 479, 362, 364, 776, 392, 395, 399, - 400, 490, 491, 508, 536, 555, 556, 557, 389, 376, - 528, 377, 409, 378, 345, 381, 380, 382, 538, 383, - 347, 509, 560, 0, 405, 524, 474, 348, 473, 510, - 559, 558, 363, 586, 593, 594, 685, 0, 599, 790, - 791, 792, 608, 0, 516, 359, 356, 0, 0, 0, - 385, 511, 372, 374, 375, 373, 505, 507, 613, 614, - 615, 617, 0, 618, 619, 0, 0, 0, 0, 620, - 686, 704, 670, 639, 601, 694, 636, 640, 641, 426, - 427, 428, 429, 707, 0, 0, 0, 592, 454, 455, - 0, 397, 396, 475, 349, 0, 0, 443, 425, 517, - 355, 393, 445, 431, 456, 457, 458, 459, 460, 0, - 461, 462, 403, 339, 340, 784, 386, 498, 709, 746, - 747, 631, 0, 695, 632, 642, 379, 667, 679, 678, - 492, 591, 0, 690, 693, 621, 783, 0, 687, 701, - 788, 700, 780, 504, 0, 535, 698, 645, 0, 691, - 664, 665, 0, 692, 660, 696, 0, 633, 0, 602, - 605, 635, 722, 723, 724, 346, 604, 726, 727, 728, - 729, 730, 731, 732, 725, 574, 668, 644, 671, 583, - 647, 646, 0, 0, 682, 600, 683, 684, 486, 487, - 488, 489, 407, 710, 370, 603, 519, 520, 0, 669, - 0, 0, 0, 0, 0, 0, 0, 0, 674, 675, - 672, 793, 0, 733, 734, 0, 0, 597, 598, 402, - 0, 616, 410, 369, 503, 404, 581, 434, 0, 609, - 676, 610, 522, 523, 737, 742, 738, 739, 741, 762, - 493, 424, 430, 539, 444, 466, 525, 580, 501, 530, - 367, 570, 541, 472, 661, 689, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 325, 326, 0, + 0, 0, 0, 0, 722, 721, 720, 719, 718, 717, + 716, 715, 0, 0, 662, 385, 785, 779, 786, 767, + 770, 769, 744, 0, 341, 642, 466, 520, 402, 709, + 710, 0, 765, 284, 285, 286, 287, 288, 289, 290, + 291, 332, 292, 293, 294, 295, 296, 297, 298, 303, + 304, 305, 306, 307, 308, 309, 310, 712, 301, 302, + 311, 312, 313, 314, 315, 316, 317, 318, 319, 320, + 321, 322, 323, 324, 0, 0, 0, 0, 333, 771, + 772, 773, 774, 775, 0, 0, 334, 335, 336, 0, + 0, 299, 300, 327, 552, 328, 329, 330, 331, 0, + 0, 591, 592, 593, 616, 0, 594, 575, 641, 412, + 342, 556, 583, 782, 783, 0, 0, 0, 0, 0, + 0, 0, 692, 703, 739, 740, 0, 753, 754, 756, + 758, 757, 760, 549, 550, 768, 0, 0, 437, 358, + 359, 496, 497, 535, 0, 438, 439, 440, 444, 441, + 442, 443, 762, 763, 764, 761, 470, 534, 557, 543, + 781, 483, 0, 789, 631, 632, 790, 749, 748, 343, + 0, 337, 338, 706, 707, 200, 245, 537, 482, 638, + 0, 0, 0, 0, 0, 0, 502, 0, 0, 647, + 681, 670, 759, 634, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 395, 0, 0, 465, 685, 666, + 677, 667, 508, 652, 653, 654, 661, 407, 655, 791, + 657, 626, 658, 627, 659, 660, 1270, 684, 633, 545, + 484, 0, 701, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 238, 3224, 0, 268, 0, 0, 0, 0, 0, + 0, 366, 269, 628, 755, 630, 629, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 369, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 546, 574, 0, + 588, 0, 434, 435, 0, 0, 0, 0, 0, 0, + 0, 350, 553, 571, 367, 540, 586, 372, 548, 564, + 362, 501, 536, 0, 0, 352, 569, 547, 479, 351, + 0, 529, 392, 409, 389, 499, 0, 0, 568, 599, + 388, 589, 0, 579, 354, 0, 578, 498, 471, 565, + 570, 480, 473, 0, 353, 567, 478, 472, 448, 399, + 615, 449, 450, 451, 452, 453, 454, 413, 514, 469, + 515, 414, 486, 485, 487, 415, 416, 417, 418, 419, + 420, 421, 422, 423, 424, 0, 0, 0, 0, 0, + 610, 611, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 747, 0, 246, + 752, 0, 582, 0, 0, 0, 0, 0, 0, 551, + 0, 0, 455, 0, 0, 0, 600, 0, 532, 504, + 793, 0, 0, 530, 467, 566, 516, 572, 554, 580, + 523, 517, 344, 555, 391, 481, 363, 365, 780, 393, + 396, 400, 401, 492, 493, 510, 539, 558, 559, 560, + 390, 377, 531, 378, 410, 379, 345, 382, 381, 383, + 541, 384, 347, 511, 563, 0, 406, 527, 476, 348, + 475, 512, 562, 561, 364, 590, 597, 598, 689, 0, + 603, 794, 795, 796, 612, 0, 518, 360, 357, 0, + 0, 0, 386, 513, 373, 375, 376, 374, 507, 509, + 617, 618, 619, 621, 0, 622, 623, 0, 0, 0, + 0, 624, 690, 708, 674, 643, 605, 698, 640, 644, + 645, 427, 428, 429, 430, 711, 0, 0, 0, 596, + 456, 457, 0, 398, 397, 477, 349, 0, 0, 445, + 426, 519, 355, 394, 447, 432, 458, 459, 460, 461, + 462, 0, 463, 464, 404, 339, 340, 788, 387, 500, + 713, 750, 751, 635, 0, 699, 636, 646, 380, 671, + 683, 682, 494, 595, 0, 694, 697, 625, 787, 0, + 691, 705, 792, 704, 784, 506, 0, 538, 702, 649, + 0, 695, 668, 669, 0, 696, 664, 700, 356, 433, + 524, 581, 0, 637, 0, 606, 609, 639, 726, 727, + 728, 346, 608, 730, 731, 732, 733, 734, 735, 736, + 729, 577, 672, 648, 675, 587, 651, 650, 0, 0, + 686, 604, 687, 688, 488, 489, 490, 491, 408, 714, + 371, 607, 521, 522, 0, 673, 0, 0, 0, 0, + 0, 0, 0, 0, 678, 679, 676, 797, 0, 737, + 738, 0, 0, 601, 602, 403, 0, 620, 411, 370, + 505, 405, 585, 436, 0, 613, 680, 614, 525, 526, + 741, 746, 742, 743, 745, 766, 495, 425, 431, 542, + 446, 468, 528, 584, 503, 533, 368, 573, 544, 474, + 665, 693, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 325, 326, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 722, + 721, 720, 719, 718, 717, 716, 715, 0, 0, 662, + 385, 785, 779, 786, 767, 770, 769, 744, 0, 341, + 642, 466, 520, 402, 709, 710, 0, 765, 284, 285, + 286, 287, 288, 289, 290, 291, 332, 292, 293, 294, + 295, 296, 297, 298, 303, 304, 305, 306, 307, 308, + 309, 310, 712, 301, 302, 311, 312, 313, 314, 315, + 316, 317, 318, 319, 320, 321, 322, 323, 324, 0, + 0, 0, 0, 333, 771, 772, 773, 774, 775, 0, + 0, 334, 335, 336, 0, 0, 299, 300, 327, 552, + 328, 329, 330, 331, 0, 0, 591, 592, 593, 616, + 0, 594, 575, 641, 412, 342, 556, 583, 782, 783, + 0, 0, 0, 0, 0, 0, 0, 692, 703, 739, + 740, 0, 753, 754, 756, 758, 757, 760, 549, 550, + 768, 0, 0, 437, 358, 359, 496, 497, 535, 0, + 438, 439, 440, 444, 441, 442, 443, 762, 763, 764, + 761, 470, 534, 557, 543, 781, 483, 0, 789, 631, + 632, 790, 749, 748, 343, 0, 337, 338, 706, 707, + 200, 245, 537, 482, 638, 0, 0, 0, 0, 0, + 0, 502, 0, 0, 647, 681, 670, 759, 634, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 395, + 0, 0, 465, 685, 666, 677, 667, 508, 652, 653, + 654, 661, 407, 655, 791, 657, 626, 658, 627, 659, + 660, 1270, 684, 633, 545, 484, 0, 701, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 238, 2968, 0, 268, + 0, 0, 0, 0, 0, 0, 366, 269, 628, 755, + 630, 629, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 369, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 546, 574, 0, 588, 0, 434, 435, 0, + 0, 0, 0, 0, 0, 0, 350, 553, 571, 367, + 540, 586, 372, 548, 564, 362, 501, 536, 0, 0, + 352, 569, 547, 479, 351, 0, 529, 392, 409, 389, + 499, 0, 0, 568, 599, 388, 589, 0, 579, 354, + 0, 578, 498, 471, 565, 570, 480, 473, 0, 353, + 567, 478, 472, 448, 399, 615, 449, 450, 451, 452, + 453, 454, 413, 514, 469, 515, 414, 486, 485, 487, + 415, 416, 417, 418, 419, 420, 421, 422, 423, 424, + 0, 0, 0, 0, 0, 610, 611, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 747, 0, 246, 752, 0, 582, 0, 0, + 0, 0, 0, 0, 551, 0, 0, 455, 0, 0, + 0, 600, 0, 532, 504, 793, 0, 0, 530, 467, + 566, 516, 572, 554, 580, 523, 517, 344, 555, 391, + 481, 363, 365, 780, 393, 396, 400, 401, 492, 493, + 510, 539, 558, 559, 560, 390, 377, 531, 378, 410, + 379, 345, 382, 381, 383, 541, 384, 347, 511, 563, + 0, 406, 527, 476, 348, 475, 512, 562, 561, 364, + 590, 597, 598, 689, 0, 603, 794, 795, 796, 612, + 0, 518, 360, 357, 0, 0, 0, 386, 513, 373, + 375, 376, 374, 507, 509, 617, 618, 619, 621, 0, + 622, 623, 0, 0, 0, 0, 624, 690, 708, 674, + 643, 605, 698, 640, 644, 645, 427, 428, 429, 430, + 711, 0, 0, 0, 596, 456, 457, 0, 398, 397, + 477, 349, 0, 0, 445, 426, 519, 355, 394, 447, + 432, 458, 459, 460, 461, 462, 0, 463, 464, 404, + 339, 340, 788, 387, 500, 713, 750, 751, 635, 0, + 699, 636, 646, 380, 671, 683, 682, 494, 595, 0, + 694, 697, 625, 787, 0, 691, 705, 792, 704, 784, + 506, 0, 538, 702, 649, 0, 695, 668, 669, 0, + 696, 664, 700, 356, 433, 524, 581, 0, 637, 0, + 606, 609, 639, 726, 727, 728, 346, 608, 730, 731, + 732, 733, 734, 735, 736, 729, 577, 672, 648, 675, + 587, 651, 650, 0, 0, 686, 604, 687, 688, 488, + 489, 490, 491, 408, 714, 371, 607, 521, 522, 0, + 673, 0, 0, 0, 0, 0, 0, 0, 0, 678, + 679, 676, 797, 0, 737, 738, 0, 0, 601, 602, + 403, 0, 620, 411, 370, 505, 405, 585, 436, 0, + 613, 680, 614, 525, 526, 741, 746, 742, 743, 745, + 766, 495, 425, 431, 542, 446, 468, 528, 584, 503, + 533, 368, 573, 544, 474, 665, 693, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 325, 326, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 718, 717, 716, 715, 714, 713, 712, - 711, 0, 0, 658, 384, 781, 775, 782, 763, 766, - 765, 740, 0, 341, 638, 464, 518, 401, 705, 706, - 0, 761, 284, 285, 286, 287, 288, 289, 290, 291, - 332, 292, 293, 294, 295, 296, 297, 298, 303, 304, - 305, 306, 307, 308, 309, 310, 708, 301, 302, 311, - 312, 313, 314, 315, 316, 317, 318, 319, 320, 321, - 322, 323, 324, 0, 0, 0, 0, 333, 767, 768, - 769, 770, 771, 0, 0, 334, 335, 336, 0, 0, - 299, 300, 327, 549, 328, 329, 330, 331, 0, 0, - 587, 588, 589, 612, 0, 590, 572, 637, 411, 342, - 553, 579, 778, 779, 0, 0, 0, 0, 0, 0, - 0, 688, 699, 735, 736, 0, 749, 750, 752, 754, - 753, 756, 546, 547, 764, 0, 0, 435, 357, 358, - 494, 495, 532, 0, 436, 437, 438, 442, 439, 440, - 441, 758, 759, 760, 757, 468, 531, 554, 540, 777, - 481, 0, 785, 627, 628, 786, 745, 744, 343, 0, - 337, 338, 702, 703, 0, 0, 534, 480, 634, 500, - 0, 0, 643, 677, 666, 755, 630, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 394, 1239, 0, - 463, 681, 662, 673, 663, 506, 648, 649, 650, 657, - 406, 651, 787, 653, 622, 654, 623, 655, 656, 0, - 680, 629, 542, 482, 0, 697, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 268, 1246, 1247, - 0, 0, 0, 0, 365, 269, 624, 751, 626, 625, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 1250, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 543, 571, 0, 584, 0, 432, 433, 0, 0, 0, - 0, 0, 0, 0, 350, 550, 1233, 366, 537, 582, - 371, 545, 561, 361, 499, 533, 0, 0, 352, 566, - 544, 477, 351, 0, 526, 391, 408, 388, 497, 0, - 0, 565, 595, 387, 585, 897, 576, 354, 896, 575, - 496, 469, 562, 567, 478, 471, 0, 353, 564, 476, - 470, 446, 398, 611, 447, 448, 449, 450, 451, 452, - 412, 512, 467, 513, 413, 484, 483, 485, 414, 415, - 416, 417, 418, 419, 420, 421, 422, 423, 0, 0, - 0, 0, 0, 606, 607, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 743, 0, 0, 748, 0, 578, 0, 0, 0, 0, - 0, 0, 548, 0, 0, 453, 0, 0, 0, 596, - 0, 529, 502, 789, 0, 0, 527, 465, 563, 514, - 569, 551, 577, 1237, 515, 344, 552, 390, 479, 362, - 364, 776, 392, 395, 399, 400, 490, 491, 508, 536, - 555, 556, 557, 389, 376, 528, 377, 409, 378, 345, - 381, 380, 382, 538, 383, 347, 509, 560, 0, 405, - 524, 474, 348, 473, 510, 559, 558, 363, 586, 593, - 594, 685, 0, 599, 790, 791, 792, 608, 0, 516, - 359, 356, 0, 0, 0, 385, 511, 372, 374, 375, - 373, 505, 507, 613, 614, 615, 617, 0, 618, 619, - 0, 0, 0, 0, 620, 686, 704, 670, 639, 601, - 694, 636, 640, 641, 426, 427, 428, 429, 707, 0, - 0, 0, 592, 454, 455, 0, 397, 396, 475, 349, - 0, 0, 443, 425, 517, 355, 393, 445, 431, 456, - 457, 458, 459, 460, 0, 461, 462, 403, 339, 340, - 784, 386, 498, 709, 746, 747, 631, 0, 695, 632, - 642, 379, 667, 679, 678, 492, 591, 0, 690, 693, - 621, 783, 0, 687, 701, 788, 700, 780, 504, 0, - 535, 698, 645, 0, 691, 664, 665, 0, 692, 660, - 696, 0, 633, 0, 602, 605, 635, 722, 723, 724, - 346, 604, 726, 727, 728, 729, 730, 731, 1238, 725, - 574, 668, 644, 671, 583, 647, 646, 0, 0, 682, - 1241, 683, 684, 486, 487, 488, 489, 407, 710, 1236, - 603, 519, 520, 0, 669, 0, 0, 0, 0, 0, - 0, 0, 0, 674, 675, 672, 793, 0, 733, 734, - 0, 0, 597, 598, 402, 0, 616, 410, 369, 503, - 404, 581, 434, 0, 609, 676, 610, 522, 523, 737, - 742, 738, 739, 741, 762, 1248, 1234, 1244, 1235, 444, - 466, 525, 580, 501, 530, 367, 570, 541, 1245, 661, - 689, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 325, 326, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 718, 717, - 716, 715, 714, 713, 712, 711, 0, 0, 658, 384, - 781, 775, 782, 763, 766, 765, 740, 0, 341, 638, - 464, 518, 401, 705, 706, 0, 761, 284, 285, 286, - 287, 288, 289, 290, 291, 332, 292, 293, 294, 295, - 296, 297, 298, 303, 304, 305, 306, 307, 308, 309, - 310, 708, 301, 302, 311, 312, 313, 314, 315, 316, - 317, 318, 319, 320, 321, 322, 323, 324, 0, 0, - 0, 0, 333, 767, 768, 769, 770, 771, 0, 0, - 334, 335, 336, 0, 0, 299, 300, 327, 549, 328, - 329, 330, 331, 0, 0, 587, 588, 589, 612, 0, - 590, 572, 637, 411, 342, 553, 579, 778, 779, 0, - 0, 0, 0, 0, 0, 0, 688, 699, 735, 736, - 0, 749, 750, 752, 754, 753, 756, 546, 547, 764, - 0, 0, 435, 357, 358, 494, 495, 532, 0, 436, - 437, 438, 442, 439, 440, 441, 758, 759, 760, 757, - 1232, 531, 554, 540, 777, 481, 0, 785, 627, 628, - 786, 745, 744, 343, 0, 337, 338, 702, 703, 0, - 0, 534, 480, 634, 500, 0, 0, 643, 677, 666, - 755, 630, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 394, 0, 0, 463, 681, 662, 673, 663, - 506, 648, 649, 650, 657, 406, 651, 787, 653, 622, - 654, 623, 655, 656, 0, 680, 629, 542, 482, 0, - 697, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 1812, - 0, 0, 268, 0, 0, 0, 0, 0, 0, 365, - 269, 624, 751, 626, 625, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 368, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 543, 571, 0, 584, 0, - 432, 433, 0, 0, 0, 0, 0, 0, 0, 350, - 550, 568, 366, 537, 582, 371, 545, 561, 361, 499, - 533, 0, 0, 352, 566, 544, 477, 351, 0, 526, - 391, 408, 388, 497, 0, 0, 565, 595, 387, 585, - 0, 576, 354, 0, 575, 496, 469, 562, 567, 478, - 471, 0, 353, 564, 476, 470, 446, 398, 611, 447, - 448, 449, 450, 451, 452, 412, 512, 467, 513, 413, - 484, 483, 485, 414, 415, 416, 417, 418, 419, 420, - 421, 422, 423, 0, 0, 0, 0, 0, 606, 607, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 743, 0, 0, 748, 0, - 578, 0, 0, 0, 0, 0, 0, 548, 0, 0, - 453, 0, 0, 0, 596, 0, 529, 502, 789, 0, - 0, 527, 465, 563, 514, 569, 551, 577, 521, 515, - 344, 552, 390, 479, 362, 364, 776, 392, 395, 399, - 400, 490, 491, 508, 536, 555, 556, 557, 389, 376, - 528, 377, 409, 378, 345, 381, 380, 382, 538, 383, - 347, 509, 560, 0, 405, 524, 474, 348, 473, 510, - 559, 558, 363, 586, 593, 594, 685, 0, 599, 790, - 791, 792, 608, 0, 516, 359, 356, 0, 0, 0, - 385, 511, 372, 374, 375, 373, 505, 507, 613, 614, - 615, 617, 0, 618, 619, 0, 0, 0, 0, 620, - 686, 704, 670, 639, 601, 694, 636, 640, 641, 426, - 427, 428, 429, 707, 0, 0, 0, 592, 454, 455, - 0, 397, 396, 475, 349, 0, 0, 443, 425, 517, - 355, 393, 445, 431, 456, 457, 458, 459, 460, 0, - 461, 462, 403, 339, 340, 784, 386, 498, 709, 746, - 747, 631, 0, 695, 632, 642, 379, 667, 679, 678, - 492, 591, 0, 690, 693, 621, 783, 0, 687, 701, - 788, 700, 780, 504, 0, 535, 698, 645, 0, 691, - 664, 665, 0, 692, 660, 696, 0, 633, 0, 602, - 605, 635, 722, 723, 724, 346, 604, 726, 727, 728, - 729, 730, 731, 732, 725, 574, 668, 644, 671, 583, - 647, 646, 0, 0, 682, 600, 683, 684, 486, 487, - 488, 489, 407, 710, 370, 603, 519, 520, 0, 669, - 0, 0, 0, 0, 0, 0, 0, 0, 674, 675, - 672, 793, 0, 733, 734, 0, 0, 597, 598, 402, - 0, 616, 410, 369, 503, 404, 581, 434, 0, 609, - 676, 610, 522, 523, 737, 742, 738, 739, 741, 762, - 493, 424, 430, 539, 444, 466, 525, 580, 501, 530, - 367, 570, 541, 472, 661, 689, 0, 0, 0, 0, + 0, 0, 0, 0, 722, 721, 720, 719, 718, 717, + 716, 715, 0, 0, 662, 385, 785, 779, 786, 767, + 770, 769, 744, 0, 341, 642, 466, 520, 402, 709, + 710, 0, 765, 284, 285, 286, 287, 288, 289, 290, + 291, 332, 292, 293, 294, 295, 296, 297, 298, 303, + 304, 305, 306, 307, 308, 309, 310, 712, 301, 302, + 311, 312, 313, 314, 315, 316, 317, 318, 319, 320, + 321, 322, 323, 324, 0, 0, 0, 0, 333, 771, + 772, 773, 774, 775, 0, 0, 334, 335, 336, 0, + 0, 299, 300, 327, 552, 328, 329, 330, 331, 0, + 0, 591, 592, 593, 616, 0, 594, 575, 641, 412, + 342, 556, 583, 782, 783, 0, 0, 0, 0, 0, + 0, 0, 692, 703, 739, 740, 0, 753, 754, 756, + 758, 757, 760, 549, 550, 768, 0, 0, 437, 358, + 359, 496, 497, 535, 0, 438, 439, 440, 444, 441, + 442, 443, 762, 763, 764, 761, 470, 534, 557, 543, + 781, 483, 0, 789, 631, 632, 790, 749, 748, 343, + 0, 337, 338, 706, 707, 0, 0, 537, 482, 638, + 502, 0, 0, 647, 681, 670, 759, 634, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 395, 1243, + 0, 465, 685, 666, 677, 667, 508, 652, 653, 654, + 661, 407, 655, 791, 657, 626, 658, 627, 659, 660, + 0, 684, 633, 545, 484, 0, 701, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 268, 1250, + 1251, 0, 0, 0, 0, 366, 269, 628, 755, 630, + 629, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 1254, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 546, 574, 0, 588, 0, 434, 435, 0, 0, + 0, 0, 0, 0, 0, 350, 553, 1237, 367, 540, + 586, 372, 548, 564, 362, 501, 536, 0, 0, 352, + 569, 547, 479, 351, 0, 529, 392, 409, 389, 499, + 0, 0, 568, 599, 388, 589, 901, 579, 354, 900, + 578, 498, 471, 565, 570, 480, 473, 0, 353, 567, + 478, 472, 448, 399, 615, 449, 450, 451, 452, 453, + 454, 413, 514, 469, 515, 414, 486, 485, 487, 415, + 416, 417, 418, 419, 420, 421, 422, 423, 424, 0, + 0, 0, 0, 0, 610, 611, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 747, 0, 0, 752, 0, 582, 0, 0, 0, + 0, 0, 0, 551, 0, 0, 455, 0, 0, 0, + 600, 0, 532, 504, 793, 0, 0, 530, 467, 566, + 516, 572, 554, 580, 1241, 517, 344, 555, 391, 481, + 363, 365, 780, 393, 396, 400, 401, 492, 493, 510, + 539, 558, 559, 560, 390, 377, 531, 378, 410, 379, + 345, 382, 381, 383, 541, 384, 347, 511, 563, 0, + 406, 527, 476, 348, 475, 512, 562, 561, 364, 590, + 597, 598, 689, 0, 603, 794, 795, 796, 612, 0, + 518, 360, 357, 0, 0, 0, 386, 513, 373, 375, + 376, 374, 507, 509, 617, 618, 619, 621, 0, 622, + 623, 0, 0, 0, 0, 624, 690, 708, 674, 643, + 605, 698, 640, 644, 645, 427, 428, 429, 430, 711, + 0, 0, 0, 596, 456, 457, 0, 398, 397, 477, + 349, 0, 0, 445, 426, 519, 355, 394, 447, 432, + 458, 459, 460, 461, 462, 0, 463, 464, 404, 339, + 340, 788, 387, 500, 713, 750, 751, 635, 0, 699, + 636, 646, 380, 671, 683, 682, 494, 595, 0, 694, + 697, 625, 787, 0, 691, 705, 792, 704, 784, 506, + 0, 538, 702, 649, 0, 695, 668, 669, 0, 696, + 664, 700, 356, 433, 524, 581, 0, 637, 0, 606, + 609, 639, 726, 727, 728, 346, 608, 730, 731, 732, + 733, 734, 735, 1242, 729, 577, 672, 648, 675, 587, + 651, 650, 0, 0, 686, 1245, 687, 688, 488, 489, + 490, 491, 408, 714, 1240, 607, 521, 522, 0, 673, + 0, 0, 0, 0, 0, 0, 0, 0, 678, 679, + 676, 797, 0, 737, 738, 0, 0, 601, 602, 403, + 0, 620, 411, 370, 505, 405, 585, 436, 0, 613, + 680, 614, 525, 526, 741, 746, 742, 743, 745, 766, + 1252, 1238, 1248, 1239, 446, 468, 528, 584, 503, 533, + 368, 573, 544, 1249, 665, 693, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 325, 326, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 718, 717, 716, 715, 714, 713, 712, - 711, 0, 0, 658, 384, 781, 775, 782, 763, 766, - 765, 740, 0, 341, 638, 464, 518, 401, 705, 706, - 0, 761, 284, 285, 286, 287, 288, 289, 290, 291, + 0, 0, 0, 722, 721, 720, 719, 718, 717, 716, + 715, 0, 0, 662, 385, 785, 779, 786, 767, 770, + 769, 744, 0, 341, 642, 466, 520, 402, 709, 710, + 0, 765, 284, 285, 286, 287, 288, 289, 290, 291, 332, 292, 293, 294, 295, 296, 297, 298, 303, 304, - 305, 306, 307, 308, 309, 310, 708, 301, 302, 311, + 305, 306, 307, 308, 309, 310, 712, 301, 302, 311, 312, 313, 314, 315, 316, 317, 318, 319, 320, 321, - 322, 323, 324, 0, 0, 0, 0, 333, 767, 768, - 769, 770, 771, 0, 0, 334, 335, 336, 0, 0, - 299, 300, 327, 549, 328, 329, 330, 331, 0, 0, - 587, 588, 589, 612, 0, 590, 572, 637, 411, 342, - 553, 579, 778, 779, 0, 0, 0, 0, 0, 0, - 0, 688, 699, 735, 736, 0, 749, 750, 752, 754, - 753, 756, 546, 547, 764, 0, 0, 435, 357, 358, - 494, 495, 532, 0, 436, 437, 438, 442, 439, 440, - 441, 758, 759, 760, 757, 468, 531, 554, 540, 777, - 481, 0, 785, 627, 628, 786, 745, 744, 343, 0, - 337, 338, 702, 703, 0, 0, 534, 480, 634, 500, - 2455, 0, 643, 677, 666, 755, 630, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 394, 0, 0, - 463, 681, 662, 673, 663, 506, 648, 649, 650, 657, - 406, 651, 787, 653, 622, 654, 623, 655, 656, 0, - 680, 629, 542, 482, 0, 697, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 268, 1246, 1247, - 0, 0, 0, 0, 365, 269, 624, 751, 626, 625, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 1250, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 543, 571, 0, 584, 0, 432, 433, 0, 0, 0, - 0, 0, 0, 0, 350, 550, 568, 366, 537, 582, - 371, 545, 561, 361, 499, 533, 0, 0, 352, 566, - 544, 477, 351, 0, 526, 391, 408, 388, 497, 0, - 0, 565, 595, 387, 585, 897, 576, 354, 896, 575, - 496, 469, 562, 567, 478, 471, 0, 353, 564, 476, - 470, 446, 398, 611, 447, 448, 449, 450, 451, 452, - 412, 512, 467, 513, 413, 484, 483, 485, 414, 415, - 416, 417, 418, 419, 420, 421, 422, 423, 0, 0, - 0, 0, 0, 606, 607, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 743, 0, 0, 748, 0, 578, 0, 0, 0, 0, - 0, 0, 548, 0, 0, 453, 0, 0, 0, 596, - 0, 529, 502, 789, 0, 0, 527, 465, 563, 514, - 569, 551, 577, 521, 515, 344, 552, 390, 479, 362, - 364, 776, 392, 395, 399, 400, 490, 491, 508, 536, - 555, 556, 557, 389, 376, 528, 377, 409, 378, 345, - 381, 380, 382, 538, 383, 347, 509, 560, 0, 405, - 524, 474, 348, 473, 510, 559, 558, 363, 586, 593, - 594, 685, 0, 599, 790, 791, 792, 608, 0, 516, - 359, 356, 0, 0, 0, 385, 511, 372, 374, 375, - 373, 505, 507, 613, 614, 615, 617, 0, 618, 619, - 0, 0, 0, 0, 620, 686, 704, 670, 639, 601, - 694, 636, 640, 641, 426, 427, 428, 429, 707, 0, - 0, 0, 592, 454, 455, 0, 397, 396, 475, 349, - 0, 0, 443, 425, 517, 355, 393, 445, 431, 456, - 457, 458, 459, 460, 0, 461, 462, 403, 339, 340, - 784, 386, 498, 709, 746, 747, 631, 0, 695, 632, - 642, 379, 667, 679, 678, 492, 591, 0, 690, 693, - 621, 783, 0, 687, 701, 788, 700, 780, 504, 0, - 535, 698, 645, 0, 691, 664, 665, 0, 692, 660, - 696, 0, 633, 0, 602, 605, 635, 722, 723, 724, - 346, 604, 726, 727, 728, 729, 730, 731, 732, 725, - 574, 668, 644, 671, 583, 647, 646, 0, 0, 682, - 600, 683, 684, 486, 487, 488, 489, 407, 710, 370, - 603, 519, 520, 0, 669, 0, 0, 0, 0, 0, - 0, 0, 0, 674, 675, 672, 793, 0, 733, 734, - 0, 0, 597, 598, 402, 0, 616, 410, 369, 503, - 404, 581, 434, 0, 609, 676, 610, 522, 523, 737, - 742, 738, 739, 741, 762, 1248, 2561, 1244, 2562, 444, - 466, 525, 580, 501, 530, 367, 570, 541, 1245, 661, - 689, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 325, 326, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 718, 717, - 716, 715, 714, 713, 712, 711, 0, 0, 658, 384, - 781, 775, 782, 763, 766, 765, 740, 0, 341, 638, - 464, 518, 401, 705, 706, 0, 761, 284, 285, 286, - 287, 288, 289, 290, 291, 332, 292, 293, 294, 295, - 296, 297, 298, 303, 304, 305, 306, 307, 308, 309, - 310, 708, 301, 302, 311, 312, 313, 314, 315, 316, - 317, 318, 319, 320, 321, 322, 323, 324, 0, 0, - 0, 0, 333, 767, 768, 769, 770, 771, 0, 0, - 334, 335, 336, 0, 0, 299, 300, 327, 549, 328, - 329, 330, 331, 0, 0, 587, 588, 589, 612, 0, - 590, 572, 637, 411, 342, 553, 579, 778, 779, 0, - 0, 0, 0, 0, 0, 0, 688, 699, 735, 736, - 0, 749, 750, 752, 754, 753, 756, 546, 547, 764, - 0, 0, 435, 357, 358, 494, 495, 532, 0, 436, - 437, 438, 442, 439, 440, 441, 758, 759, 760, 757, - 468, 531, 554, 540, 777, 481, 0, 785, 627, 628, - 786, 745, 744, 343, 0, 337, 338, 702, 703, 0, - 0, 534, 480, 634, 500, 0, 0, 643, 677, 666, - 755, 630, 0, 0, 3757, 0, 0, 0, 0, 0, - 0, 0, 394, 0, 0, 463, 681, 662, 673, 663, - 506, 648, 649, 650, 657, 406, 651, 787, 653, 622, - 654, 623, 655, 656, 0, 680, 629, 542, 482, 0, - 697, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 268, 0, 0, 0, 0, 0, 0, 365, - 269, 624, 751, 626, 625, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 368, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 543, 571, 0, 584, 0, - 432, 433, 0, 0, 0, 0, 0, 0, 0, 350, - 550, 568, 366, 537, 582, 371, 545, 561, 361, 499, - 533, 0, 0, 352, 566, 544, 477, 351, 0, 526, - 391, 408, 388, 497, 0, 0, 565, 595, 387, 585, - 0, 576, 354, 0, 575, 496, 469, 562, 567, 478, - 471, 0, 353, 564, 476, 470, 446, 398, 611, 447, - 448, 449, 450, 451, 452, 412, 512, 467, 513, 413, - 484, 483, 485, 414, 415, 416, 417, 418, 419, 420, - 421, 422, 423, 0, 0, 0, 0, 0, 606, 607, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 3760, - 0, 0, 0, 0, 3759, 743, 0, 0, 748, 0, - 578, 0, 0, 0, 0, 0, 0, 548, 0, 0, - 453, 0, 0, 0, 596, 0, 529, 502, 789, 0, - 0, 527, 465, 563, 514, 569, 551, 577, 521, 515, - 344, 552, 390, 479, 362, 364, 776, 392, 395, 399, - 400, 490, 491, 508, 536, 555, 556, 557, 389, 376, - 528, 377, 409, 378, 345, 381, 380, 382, 538, 383, - 347, 509, 560, 0, 405, 524, 474, 348, 473, 510, - 559, 558, 363, 586, 593, 594, 685, 0, 599, 790, - 791, 792, 608, 0, 516, 359, 356, 0, 0, 0, - 385, 511, 372, 374, 375, 373, 505, 507, 613, 614, - 615, 617, 0, 618, 619, 0, 0, 0, 0, 620, - 686, 704, 670, 639, 601, 694, 636, 640, 641, 426, - 427, 428, 429, 707, 0, 0, 0, 592, 454, 455, - 0, 397, 396, 475, 349, 0, 0, 443, 425, 517, - 355, 393, 445, 431, 456, 457, 458, 459, 460, 0, - 461, 462, 403, 339, 340, 784, 386, 498, 709, 746, - 747, 631, 0, 695, 632, 642, 379, 667, 679, 678, - 492, 591, 0, 690, 693, 621, 783, 0, 687, 701, - 788, 700, 780, 504, 0, 535, 698, 645, 0, 691, - 664, 665, 0, 692, 660, 696, 0, 633, 0, 602, - 605, 635, 722, 723, 724, 346, 604, 726, 727, 728, - 729, 730, 731, 732, 725, 574, 668, 644, 671, 583, - 647, 646, 0, 0, 682, 600, 683, 684, 486, 487, - 488, 489, 407, 710, 370, 603, 519, 520, 0, 669, - 0, 0, 0, 0, 0, 0, 0, 0, 674, 675, - 672, 793, 0, 733, 734, 0, 0, 597, 598, 402, - 0, 616, 410, 369, 503, 404, 581, 434, 0, 609, - 676, 610, 522, 523, 737, 742, 738, 739, 741, 762, - 493, 424, 430, 539, 444, 466, 525, 580, 501, 530, - 367, 570, 541, 472, 661, 689, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 325, 326, 0, + 322, 323, 324, 0, 0, 0, 0, 333, 771, 772, + 773, 774, 775, 0, 0, 334, 335, 336, 0, 0, + 299, 300, 327, 552, 328, 329, 330, 331, 0, 0, + 591, 592, 593, 616, 0, 594, 575, 641, 412, 342, + 556, 583, 782, 783, 0, 0, 0, 0, 0, 0, + 0, 692, 703, 739, 740, 0, 753, 754, 756, 758, + 757, 760, 549, 550, 768, 0, 0, 437, 358, 359, + 496, 497, 535, 0, 438, 439, 440, 444, 441, 442, + 443, 762, 763, 764, 761, 1236, 534, 557, 543, 781, + 483, 0, 789, 631, 632, 790, 749, 748, 343, 0, + 337, 338, 706, 707, 0, 0, 537, 482, 638, 502, + 0, 0, 647, 681, 670, 759, 634, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 395, 0, 0, + 465, 685, 666, 677, 667, 508, 652, 653, 654, 661, + 407, 655, 791, 657, 626, 658, 627, 659, 660, 0, + 684, 633, 545, 484, 0, 701, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 1816, 0, 0, 268, 0, 0, + 0, 0, 0, 0, 366, 269, 628, 755, 630, 629, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 369, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 546, 574, 0, 588, 0, 434, 435, 0, 0, 0, + 0, 0, 0, 0, 350, 553, 571, 367, 540, 586, + 372, 548, 564, 362, 501, 536, 0, 0, 352, 569, + 547, 479, 351, 0, 529, 392, 409, 389, 499, 0, + 0, 568, 599, 388, 589, 0, 579, 354, 0, 578, + 498, 471, 565, 570, 480, 473, 0, 353, 567, 478, + 472, 448, 399, 615, 449, 450, 451, 452, 453, 454, + 413, 514, 469, 515, 414, 486, 485, 487, 415, 416, + 417, 418, 419, 420, 421, 422, 423, 424, 0, 0, + 0, 0, 0, 610, 611, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 747, 0, 0, 752, 0, 582, 0, 0, 0, 0, + 0, 0, 551, 0, 0, 455, 0, 0, 0, 600, + 0, 532, 504, 793, 0, 0, 530, 467, 566, 516, + 572, 554, 580, 523, 517, 344, 555, 391, 481, 363, + 365, 780, 393, 396, 400, 401, 492, 493, 510, 539, + 558, 559, 560, 390, 377, 531, 378, 410, 379, 345, + 382, 381, 383, 541, 384, 347, 511, 563, 0, 406, + 527, 476, 348, 475, 512, 562, 561, 364, 590, 597, + 598, 689, 0, 603, 794, 795, 796, 612, 0, 518, + 360, 357, 0, 0, 0, 386, 513, 373, 375, 376, + 374, 507, 509, 617, 618, 619, 621, 0, 622, 623, + 0, 0, 0, 0, 624, 690, 708, 674, 643, 605, + 698, 640, 644, 645, 427, 428, 429, 430, 711, 0, + 0, 0, 596, 456, 457, 0, 398, 397, 477, 349, + 0, 0, 445, 426, 519, 355, 394, 447, 432, 458, + 459, 460, 461, 462, 0, 463, 464, 404, 339, 340, + 788, 387, 500, 713, 750, 751, 635, 0, 699, 636, + 646, 380, 671, 683, 682, 494, 595, 0, 694, 697, + 625, 787, 0, 691, 705, 792, 704, 784, 506, 0, + 538, 702, 649, 0, 695, 668, 669, 0, 696, 664, + 700, 356, 433, 524, 581, 0, 637, 0, 606, 609, + 639, 726, 727, 728, 346, 608, 730, 731, 732, 733, + 734, 735, 736, 729, 577, 672, 648, 675, 587, 651, + 650, 0, 0, 686, 604, 687, 688, 488, 489, 490, + 491, 408, 714, 371, 607, 521, 522, 0, 673, 0, + 0, 0, 0, 0, 0, 0, 0, 678, 679, 676, + 797, 0, 737, 738, 0, 0, 601, 602, 403, 0, + 620, 411, 370, 505, 405, 585, 436, 0, 613, 680, + 614, 525, 526, 741, 746, 742, 743, 745, 766, 495, + 425, 431, 542, 446, 468, 528, 584, 503, 533, 368, + 573, 544, 474, 665, 693, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 325, 326, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 718, 717, 716, 715, 714, 713, 712, - 711, 0, 0, 658, 384, 781, 775, 782, 763, 766, - 765, 740, 0, 341, 638, 464, 518, 401, 705, 706, - 0, 761, 284, 285, 286, 287, 288, 289, 290, 291, - 332, 292, 293, 294, 295, 296, 297, 298, 303, 304, - 305, 306, 307, 308, 309, 310, 708, 301, 302, 311, - 312, 313, 314, 315, 316, 317, 318, 319, 320, 321, - 322, 323, 324, 0, 0, 0, 0, 333, 767, 768, - 769, 770, 771, 0, 0, 334, 335, 336, 0, 0, - 299, 300, 327, 549, 328, 329, 330, 331, 0, 0, - 587, 588, 589, 612, 0, 590, 572, 637, 411, 342, - 553, 579, 778, 779, 0, 0, 0, 0, 0, 0, - 0, 688, 699, 735, 736, 0, 749, 750, 752, 754, - 753, 756, 546, 547, 764, 0, 0, 435, 357, 358, - 494, 495, 532, 0, 436, 437, 438, 442, 439, 440, - 441, 758, 759, 760, 757, 468, 531, 554, 540, 777, - 481, 0, 785, 627, 628, 786, 745, 744, 343, 0, - 337, 338, 702, 703, 0, 0, 534, 480, 634, 643, - 677, 666, 755, 630, 0, 0, 0, 0, 0, 2621, - 0, 0, 0, 0, 394, 0, 0, 463, 681, 662, - 673, 663, 506, 648, 649, 650, 657, 406, 651, 787, - 653, 622, 654, 623, 655, 656, 0, 680, 629, 542, - 482, 0, 697, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 268, 0, 0, 2622, 0, 0, - 0, 365, 269, 624, 751, 626, 625, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 368, 0, 0, 1565, - 0, 1566, 1564, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 543, 571, 0, - 584, 0, 432, 433, 0, 0, 0, 0, 0, 0, - 0, 350, 550, 568, 366, 537, 582, 371, 545, 561, - 361, 499, 533, 0, 0, 352, 566, 544, 477, 351, - 0, 526, 391, 408, 388, 497, 0, 0, 565, 595, - 387, 585, 0, 576, 354, 0, 575, 496, 469, 562, - 567, 478, 471, 0, 353, 564, 476, 470, 446, 398, - 611, 447, 448, 449, 450, 451, 452, 412, 512, 467, - 513, 413, 484, 483, 485, 414, 415, 416, 417, 418, - 419, 420, 421, 422, 423, 0, 0, 0, 0, 0, - 606, 607, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 743, 0, 0, - 748, 0, 578, 0, 0, 0, 0, 0, 0, 548, - 0, 0, 453, 0, 0, 0, 596, 0, 529, 502, - 789, 0, 0, 527, 465, 563, 514, 569, 551, 577, - 521, 515, 344, 552, 390, 479, 362, 364, 776, 392, - 395, 399, 400, 490, 491, 508, 536, 555, 556, 557, - 389, 376, 528, 377, 409, 378, 345, 381, 380, 382, - 538, 383, 347, 509, 560, 0, 405, 524, 474, 348, - 473, 510, 559, 558, 363, 586, 593, 594, 685, 0, - 599, 790, 791, 792, 608, 0, 516, 359, 356, 0, - 0, 0, 385, 511, 372, 374, 375, 373, 505, 507, - 613, 614, 615, 617, 0, 618, 619, 0, 0, 0, - 0, 620, 686, 704, 670, 639, 601, 694, 636, 640, - 641, 426, 427, 428, 429, 707, 0, 0, 0, 592, - 454, 455, 0, 397, 396, 475, 349, 0, 0, 443, - 425, 517, 355, 393, 445, 431, 456, 457, 458, 459, - 460, 0, 461, 462, 403, 339, 340, 784, 386, 498, - 709, 746, 747, 631, 0, 695, 632, 642, 379, 667, - 679, 678, 492, 591, 0, 690, 693, 621, 783, 0, - 687, 701, 788, 700, 780, 504, 0, 535, 698, 645, - 0, 691, 664, 665, 0, 692, 660, 696, 0, 633, - 0, 602, 605, 635, 722, 723, 724, 346, 604, 726, - 727, 728, 729, 730, 731, 732, 725, 574, 668, 644, - 671, 583, 647, 646, 0, 0, 682, 600, 683, 684, - 486, 487, 488, 489, 407, 710, 370, 603, 519, 520, - 0, 669, 0, 0, 0, 0, 0, 0, 0, 0, - 674, 675, 672, 793, 0, 733, 734, 0, 0, 597, - 598, 402, 0, 616, 410, 369, 503, 404, 581, 434, - 0, 609, 676, 610, 522, 523, 737, 742, 738, 739, - 741, 762, 493, 424, 430, 539, 444, 466, 525, 580, - 501, 530, 367, 570, 541, 472, 661, 689, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 325, - 326, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 718, 717, 716, 715, 714, - 713, 712, 711, 0, 0, 658, 384, 781, 775, 782, - 763, 766, 765, 740, 0, 341, 638, 464, 518, 401, - 705, 706, 0, 761, 284, 285, 286, 287, 288, 289, - 290, 291, 332, 292, 293, 294, 295, 296, 297, 298, - 303, 304, 305, 306, 307, 308, 309, 310, 708, 301, - 302, 311, 312, 313, 314, 315, 316, 317, 318, 319, - 320, 321, 322, 323, 324, 0, 0, 0, 0, 333, - 767, 768, 769, 770, 771, 0, 0, 334, 335, 336, - 0, 0, 299, 300, 327, 549, 328, 329, 330, 331, - 0, 0, 587, 588, 589, 612, 0, 590, 572, 637, - 411, 342, 553, 579, 778, 779, 0, 0, 0, 0, - 0, 0, 0, 688, 699, 735, 736, 0, 749, 750, - 752, 754, 753, 756, 546, 547, 764, 0, 0, 435, - 357, 358, 494, 495, 532, 0, 436, 437, 438, 442, - 439, 440, 441, 758, 759, 760, 757, 468, 531, 554, - 540, 777, 481, 0, 785, 627, 628, 786, 745, 0, - 343, 0, 337, 338, 702, 703, 0, 0, 534, 480, - 634, 500, 0, 0, 643, 677, 666, 755, 630, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 394, - 1868, 0, 463, 681, 662, 673, 663, 506, 648, 649, - 650, 657, 406, 651, 787, 653, 622, 654, 623, 655, - 656, 0, 680, 629, 542, 482, 0, 697, 0, 0, + 0, 0, 722, 721, 720, 719, 718, 717, 716, 715, + 0, 0, 662, 385, 785, 779, 786, 767, 770, 769, + 744, 0, 341, 642, 466, 520, 402, 709, 710, 0, + 765, 284, 285, 286, 287, 288, 289, 290, 291, 332, + 292, 293, 294, 295, 296, 297, 298, 303, 304, 305, + 306, 307, 308, 309, 310, 712, 301, 302, 311, 312, + 313, 314, 315, 316, 317, 318, 319, 320, 321, 322, + 323, 324, 0, 0, 0, 0, 333, 771, 772, 773, + 774, 775, 0, 0, 334, 335, 336, 0, 0, 299, + 300, 327, 552, 328, 329, 330, 331, 0, 0, 591, + 592, 593, 616, 0, 594, 575, 641, 412, 342, 556, + 583, 782, 783, 0, 0, 0, 0, 0, 0, 0, + 692, 703, 739, 740, 0, 753, 754, 756, 758, 757, + 760, 549, 550, 768, 0, 0, 437, 358, 359, 496, + 497, 535, 0, 438, 439, 440, 444, 441, 442, 443, + 762, 763, 764, 761, 470, 534, 557, 543, 781, 483, + 0, 789, 631, 632, 790, 749, 748, 343, 0, 337, + 338, 706, 707, 0, 0, 537, 482, 638, 502, 2459, + 0, 647, 681, 670, 759, 634, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 395, 0, 0, 465, + 685, 666, 677, 667, 508, 652, 653, 654, 661, 407, + 655, 791, 657, 626, 658, 627, 659, 660, 0, 684, + 633, 545, 484, 0, 701, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 268, 1250, 1251, 0, + 0, 0, 0, 366, 269, 628, 755, 630, 629, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 1254, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 546, + 574, 0, 588, 0, 434, 435, 0, 0, 0, 0, + 0, 0, 0, 350, 553, 571, 367, 540, 586, 372, + 548, 564, 362, 501, 536, 0, 0, 352, 569, 547, + 479, 351, 0, 529, 392, 409, 389, 499, 0, 0, + 568, 599, 388, 589, 901, 579, 354, 900, 578, 498, + 471, 565, 570, 480, 473, 0, 353, 567, 478, 472, + 448, 399, 615, 449, 450, 451, 452, 453, 454, 413, + 514, 469, 515, 414, 486, 485, 487, 415, 416, 417, + 418, 419, 420, 421, 422, 423, 424, 0, 0, 0, + 0, 0, 610, 611, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 747, + 0, 0, 752, 0, 582, 0, 0, 0, 0, 0, + 0, 551, 0, 0, 455, 0, 0, 0, 600, 0, + 532, 504, 793, 0, 0, 530, 467, 566, 516, 572, + 554, 580, 523, 517, 344, 555, 391, 481, 363, 365, + 780, 393, 396, 400, 401, 492, 493, 510, 539, 558, + 559, 560, 390, 377, 531, 378, 410, 379, 345, 382, + 381, 383, 541, 384, 347, 511, 563, 0, 406, 527, + 476, 348, 475, 512, 562, 561, 364, 590, 597, 598, + 689, 0, 603, 794, 795, 796, 612, 0, 518, 360, + 357, 0, 0, 0, 386, 513, 373, 375, 376, 374, + 507, 509, 617, 618, 619, 621, 0, 622, 623, 0, + 0, 0, 0, 624, 690, 708, 674, 643, 605, 698, + 640, 644, 645, 427, 428, 429, 430, 711, 0, 0, + 0, 596, 456, 457, 0, 398, 397, 477, 349, 0, + 0, 445, 426, 519, 355, 394, 447, 432, 458, 459, + 460, 461, 462, 0, 463, 464, 404, 339, 340, 788, + 387, 500, 713, 750, 751, 635, 0, 699, 636, 646, + 380, 671, 683, 682, 494, 595, 0, 694, 697, 625, + 787, 0, 691, 705, 792, 704, 784, 506, 0, 538, + 702, 649, 0, 695, 668, 669, 0, 696, 664, 700, + 356, 433, 524, 581, 0, 637, 0, 606, 609, 639, + 726, 727, 728, 346, 608, 730, 731, 732, 733, 734, + 735, 736, 729, 577, 672, 648, 675, 587, 651, 650, + 0, 0, 686, 604, 687, 688, 488, 489, 490, 491, + 408, 714, 371, 607, 521, 522, 0, 673, 0, 0, + 0, 0, 0, 0, 0, 0, 678, 679, 676, 797, + 0, 737, 738, 0, 0, 601, 602, 403, 0, 620, + 411, 370, 505, 405, 585, 436, 0, 613, 680, 614, + 525, 526, 741, 746, 742, 743, 745, 766, 1252, 2565, + 1248, 2566, 446, 468, 528, 584, 503, 533, 368, 573, + 544, 1249, 665, 693, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 325, 326, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 268, - 0, 0, 1866, 0, 0, 0, 365, 269, 624, 751, - 626, 625, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 368, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 543, 571, 0, 584, 0, 432, 433, 1864, - 0, 0, 0, 0, 0, 0, 350, 550, 568, 366, - 537, 582, 371, 545, 561, 361, 499, 533, 0, 0, - 352, 566, 544, 477, 351, 0, 526, 391, 408, 388, - 497, 0, 0, 565, 595, 387, 585, 0, 576, 354, - 0, 575, 496, 469, 562, 567, 478, 471, 0, 353, - 564, 476, 470, 446, 398, 611, 447, 448, 449, 450, - 451, 452, 412, 512, 467, 513, 413, 484, 483, 485, - 414, 415, 416, 417, 418, 419, 420, 421, 422, 423, - 0, 0, 0, 0, 0, 606, 607, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 743, 0, 0, 748, 0, 578, 0, 0, - 0, 0, 0, 0, 548, 0, 0, 453, 0, 0, - 0, 596, 0, 529, 502, 789, 0, 0, 527, 465, - 563, 514, 569, 551, 577, 521, 515, 344, 552, 390, - 479, 362, 364, 776, 392, 395, 399, 400, 490, 491, - 508, 536, 555, 556, 557, 389, 376, 528, 377, 409, - 378, 345, 381, 380, 382, 538, 383, 347, 509, 560, - 0, 405, 524, 474, 348, 473, 510, 559, 558, 363, - 586, 593, 594, 685, 0, 599, 790, 791, 792, 608, - 0, 516, 359, 356, 0, 0, 0, 385, 511, 372, - 374, 375, 373, 505, 507, 613, 614, 615, 617, 0, - 618, 619, 0, 0, 0, 0, 620, 686, 704, 670, - 639, 601, 694, 636, 640, 641, 426, 427, 428, 429, - 707, 0, 0, 0, 592, 454, 455, 0, 397, 396, - 475, 349, 0, 0, 443, 425, 517, 355, 393, 445, - 431, 456, 457, 458, 459, 460, 0, 461, 462, 403, - 339, 340, 784, 386, 498, 709, 746, 747, 631, 0, - 695, 632, 642, 379, 667, 679, 678, 492, 591, 0, - 690, 693, 621, 783, 0, 687, 701, 788, 700, 780, - 504, 0, 535, 698, 645, 0, 691, 664, 665, 0, - 692, 660, 696, 0, 633, 0, 602, 605, 635, 722, - 723, 724, 346, 604, 726, 727, 728, 729, 730, 731, - 732, 725, 574, 668, 644, 671, 583, 647, 646, 0, - 0, 682, 600, 683, 684, 486, 487, 488, 489, 407, - 710, 370, 603, 519, 520, 0, 669, 0, 0, 0, - 0, 0, 0, 0, 0, 674, 675, 672, 793, 0, - 733, 734, 0, 0, 597, 598, 402, 0, 616, 410, - 369, 503, 404, 581, 434, 0, 609, 676, 610, 522, - 523, 737, 742, 738, 739, 741, 762, 493, 424, 430, - 539, 444, 466, 525, 580, 501, 530, 367, 570, 541, - 472, 661, 689, 0, 0, 0, 0, 0, 0, 0, + 0, 722, 721, 720, 719, 718, 717, 716, 715, 0, + 0, 662, 385, 785, 779, 786, 767, 770, 769, 744, + 0, 341, 642, 466, 520, 402, 709, 710, 0, 765, + 284, 285, 286, 287, 288, 289, 290, 291, 332, 292, + 293, 294, 295, 296, 297, 298, 303, 304, 305, 306, + 307, 308, 309, 310, 712, 301, 302, 311, 312, 313, + 314, 315, 316, 317, 318, 319, 320, 321, 322, 323, + 324, 0, 0, 0, 0, 333, 771, 772, 773, 774, + 775, 0, 0, 334, 335, 336, 0, 0, 299, 300, + 327, 552, 328, 329, 330, 331, 0, 0, 591, 592, + 593, 616, 0, 594, 575, 641, 412, 342, 556, 583, + 782, 783, 0, 0, 0, 0, 0, 0, 0, 692, + 703, 739, 740, 0, 753, 754, 756, 758, 757, 760, + 549, 550, 768, 0, 0, 437, 358, 359, 496, 497, + 535, 0, 438, 439, 440, 444, 441, 442, 443, 762, + 763, 764, 761, 470, 534, 557, 543, 781, 483, 0, + 789, 631, 632, 790, 749, 748, 343, 0, 337, 338, + 706, 707, 0, 0, 537, 482, 638, 502, 0, 0, + 647, 681, 670, 759, 634, 0, 0, 3761, 0, 0, + 0, 0, 0, 0, 0, 395, 0, 0, 465, 685, + 666, 677, 667, 508, 652, 653, 654, 661, 407, 655, + 791, 657, 626, 658, 627, 659, 660, 0, 684, 633, + 545, 484, 0, 701, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 268, 0, 0, 0, 0, + 0, 0, 366, 269, 628, 755, 630, 629, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 369, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 546, 574, + 0, 588, 0, 434, 435, 0, 0, 0, 0, 0, + 0, 0, 350, 553, 571, 367, 540, 586, 372, 548, + 564, 362, 501, 536, 0, 0, 352, 569, 547, 479, + 351, 0, 529, 392, 409, 389, 499, 0, 0, 568, + 599, 388, 589, 0, 579, 354, 0, 578, 498, 471, + 565, 570, 480, 473, 0, 353, 567, 478, 472, 448, + 399, 615, 449, 450, 451, 452, 453, 454, 413, 514, + 469, 515, 414, 486, 485, 487, 415, 416, 417, 418, + 419, 420, 421, 422, 423, 424, 0, 0, 0, 0, + 0, 610, 611, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 3764, 0, 0, 0, 0, 3763, 747, 0, + 0, 752, 0, 582, 0, 0, 0, 0, 0, 0, + 551, 0, 0, 455, 0, 0, 0, 600, 0, 532, + 504, 793, 0, 0, 530, 467, 566, 516, 572, 554, + 580, 523, 517, 344, 555, 391, 481, 363, 365, 780, + 393, 396, 400, 401, 492, 493, 510, 539, 558, 559, + 560, 390, 377, 531, 378, 410, 379, 345, 382, 381, + 383, 541, 384, 347, 511, 563, 0, 406, 527, 476, + 348, 475, 512, 562, 561, 364, 590, 597, 598, 689, + 0, 603, 794, 795, 796, 612, 0, 518, 360, 357, + 0, 0, 0, 386, 513, 373, 375, 376, 374, 507, + 509, 617, 618, 619, 621, 0, 622, 623, 0, 0, + 0, 0, 624, 690, 708, 674, 643, 605, 698, 640, + 644, 645, 427, 428, 429, 430, 711, 0, 0, 0, + 596, 456, 457, 0, 398, 397, 477, 349, 0, 0, + 445, 426, 519, 355, 394, 447, 432, 458, 459, 460, + 461, 462, 0, 463, 464, 404, 339, 340, 788, 387, + 500, 713, 750, 751, 635, 0, 699, 636, 646, 380, + 671, 683, 682, 494, 595, 0, 694, 697, 625, 787, + 0, 691, 705, 792, 704, 784, 506, 0, 538, 702, + 649, 0, 695, 668, 669, 0, 696, 664, 700, 356, + 433, 524, 581, 0, 637, 0, 606, 609, 639, 726, + 727, 728, 346, 608, 730, 731, 732, 733, 734, 735, + 736, 729, 577, 672, 648, 675, 587, 651, 650, 0, + 0, 686, 604, 687, 688, 488, 489, 490, 491, 408, + 714, 371, 607, 521, 522, 0, 673, 0, 0, 0, + 0, 0, 0, 0, 0, 678, 679, 676, 797, 0, + 737, 738, 0, 0, 601, 602, 403, 0, 620, 411, + 370, 505, 405, 585, 436, 0, 613, 680, 614, 525, + 526, 741, 746, 742, 743, 745, 766, 495, 425, 431, + 542, 446, 468, 528, 584, 503, 533, 368, 573, 544, + 474, 665, 693, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 325, 326, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 718, 717, 716, 715, 714, 713, 712, 711, 0, 0, - 658, 384, 781, 775, 782, 763, 766, 765, 740, 0, - 341, 638, 464, 518, 401, 705, 706, 0, 761, 284, + 722, 721, 720, 719, 718, 717, 716, 715, 0, 0, + 662, 385, 785, 779, 786, 767, 770, 769, 744, 0, + 341, 642, 466, 520, 402, 709, 710, 0, 765, 284, 285, 286, 287, 288, 289, 290, 291, 332, 292, 293, 294, 295, 296, 297, 298, 303, 304, 305, 306, 307, - 308, 309, 310, 708, 301, 302, 311, 312, 313, 314, + 308, 309, 310, 712, 301, 302, 311, 312, 313, 314, 315, 316, 317, 318, 319, 320, 321, 322, 323, 324, - 0, 0, 0, 0, 333, 767, 768, 769, 770, 771, + 0, 0, 0, 0, 333, 771, 772, 773, 774, 775, 0, 0, 334, 335, 336, 0, 0, 299, 300, 327, - 549, 328, 329, 330, 331, 0, 0, 587, 588, 589, - 612, 0, 590, 572, 637, 411, 342, 553, 579, 778, - 779, 0, 0, 0, 0, 0, 0, 0, 688, 699, - 735, 736, 0, 749, 750, 752, 754, 753, 756, 546, - 547, 764, 0, 0, 435, 357, 358, 494, 495, 532, - 0, 436, 437, 438, 442, 439, 440, 441, 758, 759, - 760, 757, 468, 531, 554, 540, 777, 481, 0, 785, - 627, 628, 786, 745, 744, 343, 0, 337, 338, 702, - 703, 0, 0, 534, 480, 634, 500, 0, 0, 643, - 677, 666, 755, 630, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 394, 1862, 0, 463, 681, 662, - 673, 663, 506, 648, 649, 650, 657, 406, 651, 787, - 653, 622, 654, 623, 655, 656, 0, 680, 629, 542, - 482, 0, 697, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 268, 0, 0, 1866, 0, 0, - 0, 365, 269, 624, 751, 626, 625, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 368, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 543, 571, 0, - 584, 0, 432, 433, 1864, 0, 0, 0, 0, 0, - 0, 350, 550, 568, 366, 537, 582, 371, 545, 561, - 361, 499, 533, 0, 0, 352, 566, 544, 477, 351, - 0, 526, 391, 408, 388, 497, 0, 0, 565, 595, - 387, 585, 0, 576, 354, 0, 575, 496, 469, 562, - 567, 478, 471, 0, 353, 564, 476, 470, 446, 398, - 611, 447, 448, 449, 450, 451, 452, 412, 512, 467, - 513, 413, 484, 483, 485, 414, 415, 416, 417, 418, - 419, 420, 421, 422, 423, 0, 0, 0, 0, 0, - 606, 607, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 743, 0, 0, - 748, 0, 578, 0, 0, 0, 0, 0, 0, 548, - 0, 0, 453, 0, 0, 0, 596, 0, 529, 502, - 789, 0, 0, 527, 465, 563, 514, 569, 551, 577, - 521, 515, 344, 552, 390, 479, 362, 364, 776, 392, - 395, 399, 400, 490, 491, 508, 536, 555, 556, 557, - 389, 376, 528, 377, 409, 378, 345, 381, 380, 382, - 538, 383, 347, 509, 560, 0, 405, 524, 474, 348, - 473, 510, 559, 558, 363, 586, 593, 594, 685, 0, - 599, 790, 791, 792, 608, 0, 516, 359, 356, 0, - 0, 0, 385, 511, 372, 374, 375, 373, 505, 507, - 613, 614, 615, 617, 0, 618, 619, 0, 0, 0, - 0, 620, 686, 704, 670, 639, 601, 694, 636, 640, - 641, 426, 427, 428, 429, 707, 0, 0, 0, 592, - 454, 455, 0, 397, 396, 475, 349, 0, 0, 443, - 425, 517, 355, 393, 445, 431, 456, 457, 458, 459, - 460, 0, 461, 462, 403, 339, 340, 784, 386, 498, - 709, 746, 747, 631, 0, 695, 632, 642, 379, 667, - 679, 678, 492, 591, 0, 690, 693, 621, 783, 0, - 687, 701, 788, 700, 780, 504, 0, 535, 698, 645, - 0, 691, 664, 665, 0, 692, 660, 696, 0, 633, - 0, 602, 605, 635, 722, 723, 724, 346, 604, 726, - 727, 728, 729, 730, 731, 732, 725, 574, 668, 644, - 671, 583, 647, 646, 0, 0, 682, 600, 683, 684, - 486, 487, 488, 489, 407, 710, 370, 603, 519, 520, - 0, 669, 0, 0, 0, 0, 0, 0, 0, 0, - 674, 675, 672, 793, 0, 733, 734, 0, 0, 597, - 598, 402, 0, 616, 410, 369, 503, 404, 581, 434, - 0, 609, 676, 610, 522, 523, 737, 742, 738, 739, - 741, 762, 493, 424, 430, 539, 444, 466, 525, 580, - 501, 530, 367, 570, 541, 472, 661, 689, 0, 0, + 552, 328, 329, 330, 331, 0, 0, 591, 592, 593, + 616, 0, 594, 575, 641, 412, 342, 556, 583, 782, + 783, 0, 0, 0, 0, 0, 0, 0, 692, 703, + 739, 740, 0, 753, 754, 756, 758, 757, 760, 549, + 550, 768, 0, 0, 437, 358, 359, 496, 497, 535, + 0, 438, 439, 440, 444, 441, 442, 443, 762, 763, + 764, 761, 470, 534, 557, 543, 781, 483, 0, 789, + 631, 632, 790, 749, 748, 343, 0, 337, 338, 706, + 707, 0, 0, 537, 482, 638, 647, 681, 670, 759, + 634, 0, 0, 0, 0, 0, 2625, 0, 0, 0, + 0, 395, 0, 0, 465, 685, 666, 677, 667, 508, + 652, 653, 654, 661, 407, 655, 791, 657, 626, 658, + 627, 659, 660, 0, 684, 633, 545, 484, 0, 701, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 268, 0, 0, 2626, 0, 0, 0, 366, 269, + 628, 755, 630, 629, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 369, 0, 0, 1569, 0, 1570, 1568, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 546, 574, 0, 588, 0, 434, + 435, 0, 0, 0, 0, 0, 0, 0, 350, 553, + 571, 367, 540, 586, 372, 548, 564, 362, 501, 536, + 0, 0, 352, 569, 547, 479, 351, 0, 529, 392, + 409, 389, 499, 0, 0, 568, 599, 388, 589, 0, + 579, 354, 0, 578, 498, 471, 565, 570, 480, 473, + 0, 353, 567, 478, 472, 448, 399, 615, 449, 450, + 451, 452, 453, 454, 413, 514, 469, 515, 414, 486, + 485, 487, 415, 416, 417, 418, 419, 420, 421, 422, + 423, 424, 0, 0, 0, 0, 0, 610, 611, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 747, 0, 0, 752, 0, 582, + 0, 0, 0, 0, 0, 0, 551, 0, 0, 455, + 0, 0, 0, 600, 0, 532, 504, 793, 0, 0, + 530, 467, 566, 516, 572, 554, 580, 523, 517, 344, + 555, 391, 481, 363, 365, 780, 393, 396, 400, 401, + 492, 493, 510, 539, 558, 559, 560, 390, 377, 531, + 378, 410, 379, 345, 382, 381, 383, 541, 384, 347, + 511, 563, 0, 406, 527, 476, 348, 475, 512, 562, + 561, 364, 590, 597, 598, 689, 0, 603, 794, 795, + 796, 612, 0, 518, 360, 357, 0, 0, 0, 386, + 513, 373, 375, 376, 374, 507, 509, 617, 618, 619, + 621, 0, 622, 623, 0, 0, 0, 0, 624, 690, + 708, 674, 643, 605, 698, 640, 644, 645, 427, 428, + 429, 430, 711, 0, 0, 0, 596, 456, 457, 0, + 398, 397, 477, 349, 0, 0, 445, 426, 519, 355, + 394, 447, 432, 458, 459, 460, 461, 462, 0, 463, + 464, 404, 339, 340, 788, 387, 500, 713, 750, 751, + 635, 0, 699, 636, 646, 380, 671, 683, 682, 494, + 595, 0, 694, 697, 625, 787, 0, 691, 705, 792, + 704, 784, 506, 0, 538, 702, 649, 0, 695, 668, + 669, 0, 696, 664, 700, 356, 433, 524, 581, 0, + 637, 0, 606, 609, 639, 726, 727, 728, 346, 608, + 730, 731, 732, 733, 734, 735, 736, 729, 577, 672, + 648, 675, 587, 651, 650, 0, 0, 686, 604, 687, + 688, 488, 489, 490, 491, 408, 714, 371, 607, 521, + 522, 0, 673, 0, 0, 0, 0, 0, 0, 0, + 0, 678, 679, 676, 797, 0, 737, 738, 0, 0, + 601, 602, 403, 0, 620, 411, 370, 505, 405, 585, + 436, 0, 613, 680, 614, 525, 526, 741, 746, 742, + 743, 745, 766, 495, 425, 431, 542, 446, 468, 528, + 584, 503, 533, 368, 573, 544, 474, 665, 693, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 325, 326, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 722, 721, 720, 719, + 718, 717, 716, 715, 0, 0, 662, 385, 785, 779, + 786, 767, 770, 769, 744, 0, 341, 642, 466, 520, + 402, 709, 710, 0, 765, 284, 285, 286, 287, 288, + 289, 290, 291, 332, 292, 293, 294, 295, 296, 297, + 298, 303, 304, 305, 306, 307, 308, 309, 310, 712, + 301, 302, 311, 312, 313, 314, 315, 316, 317, 318, + 319, 320, 321, 322, 323, 324, 0, 0, 0, 0, + 333, 771, 772, 773, 774, 775, 0, 0, 334, 335, + 336, 0, 0, 299, 300, 327, 552, 328, 329, 330, + 331, 0, 0, 591, 592, 593, 616, 0, 594, 575, + 641, 412, 342, 556, 583, 782, 783, 0, 0, 0, + 0, 0, 0, 0, 692, 703, 739, 740, 0, 753, + 754, 756, 758, 757, 760, 549, 550, 768, 0, 0, + 437, 358, 359, 496, 497, 535, 0, 438, 439, 440, + 444, 441, 442, 443, 762, 763, 764, 761, 470, 534, + 557, 543, 781, 483, 0, 789, 631, 632, 790, 749, + 0, 343, 0, 337, 338, 706, 707, 0, 0, 537, + 482, 638, 502, 0, 0, 647, 681, 670, 759, 634, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 395, 1872, 0, 465, 685, 666, 677, 667, 508, 652, + 653, 654, 661, 407, 655, 791, 657, 626, 658, 627, + 659, 660, 0, 684, 633, 545, 484, 0, 701, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 268, 0, 0, 1870, 0, 0, 0, 366, 269, 628, + 755, 630, 629, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 369, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 546, 574, 0, 588, 0, 434, 435, + 1868, 0, 0, 0, 0, 0, 0, 350, 553, 571, + 367, 540, 586, 372, 548, 564, 362, 501, 536, 0, + 0, 352, 569, 547, 479, 351, 0, 529, 392, 409, + 389, 499, 0, 0, 568, 599, 388, 589, 0, 579, + 354, 0, 578, 498, 471, 565, 570, 480, 473, 0, + 353, 567, 478, 472, 448, 399, 615, 449, 450, 451, + 452, 453, 454, 413, 514, 469, 515, 414, 486, 485, + 487, 415, 416, 417, 418, 419, 420, 421, 422, 423, + 424, 0, 0, 0, 0, 0, 610, 611, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 747, 0, 0, 752, 0, 582, 0, + 0, 0, 0, 0, 0, 551, 0, 0, 455, 0, + 0, 0, 600, 0, 532, 504, 793, 0, 0, 530, + 467, 566, 516, 572, 554, 580, 523, 517, 344, 555, + 391, 481, 363, 365, 780, 393, 396, 400, 401, 492, + 493, 510, 539, 558, 559, 560, 390, 377, 531, 378, + 410, 379, 345, 382, 381, 383, 541, 384, 347, 511, + 563, 0, 406, 527, 476, 348, 475, 512, 562, 561, + 364, 590, 597, 598, 689, 0, 603, 794, 795, 796, + 612, 0, 518, 360, 357, 0, 0, 0, 386, 513, + 373, 375, 376, 374, 507, 509, 617, 618, 619, 621, + 0, 622, 623, 0, 0, 0, 0, 624, 690, 708, + 674, 643, 605, 698, 640, 644, 645, 427, 428, 429, + 430, 711, 0, 0, 0, 596, 456, 457, 0, 398, + 397, 477, 349, 0, 0, 445, 426, 519, 355, 394, + 447, 432, 458, 459, 460, 461, 462, 0, 463, 464, + 404, 339, 340, 788, 387, 500, 713, 750, 751, 635, + 0, 699, 636, 646, 380, 671, 683, 682, 494, 595, + 0, 694, 697, 625, 787, 0, 691, 705, 792, 704, + 784, 506, 0, 538, 702, 649, 0, 695, 668, 669, + 0, 696, 664, 700, 356, 433, 524, 581, 0, 637, + 0, 606, 609, 639, 726, 727, 728, 346, 608, 730, + 731, 732, 733, 734, 735, 736, 729, 577, 672, 648, + 675, 587, 651, 650, 0, 0, 686, 604, 687, 688, + 488, 489, 490, 491, 408, 714, 371, 607, 521, 522, + 0, 673, 0, 0, 0, 0, 0, 0, 0, 0, + 678, 679, 676, 797, 0, 737, 738, 0, 0, 601, + 602, 403, 0, 620, 411, 370, 505, 405, 585, 436, + 0, 613, 680, 614, 525, 526, 741, 746, 742, 743, + 745, 766, 495, 425, 431, 542, 446, 468, 528, 584, + 503, 533, 368, 573, 544, 474, 665, 693, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 325, 326, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 718, 717, 716, 715, 714, - 713, 712, 711, 0, 0, 658, 384, 781, 775, 782, - 763, 766, 765, 740, 0, 341, 638, 464, 518, 401, - 705, 706, 0, 761, 284, 285, 286, 287, 288, 289, + 0, 0, 0, 0, 0, 722, 721, 720, 719, 718, + 717, 716, 715, 0, 0, 662, 385, 785, 779, 786, + 767, 770, 769, 744, 0, 341, 642, 466, 520, 402, + 709, 710, 0, 765, 284, 285, 286, 287, 288, 289, 290, 291, 332, 292, 293, 294, 295, 296, 297, 298, - 303, 304, 305, 306, 307, 308, 309, 310, 708, 301, + 303, 304, 305, 306, 307, 308, 309, 310, 712, 301, 302, 311, 312, 313, 314, 315, 316, 317, 318, 319, 320, 321, 322, 323, 324, 0, 0, 0, 0, 333, - 767, 768, 769, 770, 771, 0, 0, 334, 335, 336, - 0, 0, 299, 300, 327, 549, 328, 329, 330, 331, - 0, 0, 587, 588, 589, 612, 0, 590, 572, 637, - 411, 342, 553, 579, 778, 779, 0, 0, 0, 0, - 0, 0, 0, 688, 699, 735, 736, 0, 749, 750, - 752, 754, 753, 756, 546, 547, 764, 0, 0, 435, - 357, 358, 494, 495, 532, 0, 436, 437, 438, 442, - 439, 440, 441, 758, 759, 760, 757, 468, 531, 554, - 540, 777, 481, 0, 785, 627, 628, 786, 745, 744, - 343, 0, 337, 338, 702, 703, 0, 0, 534, 480, - 634, 500, 0, 0, 643, 677, 666, 755, 630, 0, - 0, 1200, 0, 0, 0, 0, 0, 0, 1205, 394, - 0, 0, 463, 681, 1201, 673, 663, 1203, 648, 649, - 650, 657, 406, 651, 787, 653, 622, 654, 623, 655, - 656, 0, 680, 629, 542, 482, 0, 697, 0, 0, + 771, 772, 773, 774, 775, 0, 0, 334, 335, 336, + 0, 0, 299, 300, 327, 552, 328, 329, 330, 331, + 0, 0, 591, 592, 593, 616, 0, 594, 575, 641, + 412, 342, 556, 583, 782, 783, 0, 0, 0, 0, + 0, 0, 0, 692, 703, 739, 740, 0, 753, 754, + 756, 758, 757, 760, 549, 550, 768, 0, 0, 437, + 358, 359, 496, 497, 535, 0, 438, 439, 440, 444, + 441, 442, 443, 762, 763, 764, 761, 470, 534, 557, + 543, 781, 483, 0, 789, 631, 632, 790, 749, 748, + 343, 0, 337, 338, 706, 707, 0, 0, 537, 482, + 638, 502, 0, 0, 647, 681, 670, 759, 634, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 395, + 1866, 0, 465, 685, 666, 677, 667, 508, 652, 653, + 654, 661, 407, 655, 791, 657, 626, 658, 627, 659, + 660, 0, 684, 633, 545, 484, 0, 701, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 268, - 0, 0, 0, 0, 0, 0, 365, 269, 624, 751, - 626, 625, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 368, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 543, 571, 0, 584, 0, 432, 433, 0, - 0, 0, 0, 0, 0, 0, 350, 550, 568, 366, - 537, 582, 371, 545, 561, 361, 499, 533, 0, 0, - 352, 566, 544, 477, 351, 0, 526, 391, 408, 388, - 497, 0, 0, 565, 595, 387, 585, 0, 576, 354, - 0, 575, 496, 469, 562, 567, 478, 471, 0, 353, - 564, 476, 470, 446, 398, 611, 447, 448, 449, 450, - 451, 452, 412, 512, 467, 513, 413, 484, 483, 485, - 414, 415, 416, 417, 418, 419, 420, 421, 422, 423, - 0, 0, 0, 0, 0, 606, 607, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 743, 0, 0, 748, 0, 578, 0, 1204, - 0, 0, 0, 0, 548, 0, 0, 453, 0, 0, - 0, 596, 0, 529, 502, 789, 0, 0, 527, 465, - 563, 514, 569, 551, 577, 521, 515, 344, 552, 390, - 479, 362, 364, 776, 392, 395, 399, 400, 490, 491, - 508, 536, 555, 556, 557, 389, 376, 528, 377, 409, - 378, 345, 381, 380, 382, 538, 383, 347, 509, 560, - 0, 405, 524, 474, 348, 473, 510, 559, 558, 363, - 586, 593, 594, 685, 0, 599, 790, 791, 792, 608, - 0, 516, 359, 356, 0, 0, 0, 385, 511, 372, - 374, 375, 373, 505, 507, 613, 614, 615, 617, 0, - 618, 619, 0, 0, 0, 0, 620, 686, 704, 670, - 639, 601, 694, 636, 640, 641, 426, 427, 428, 429, - 707, 0, 0, 0, 592, 454, 455, 0, 397, 396, - 475, 349, 0, 0, 443, 425, 517, 355, 393, 445, - 431, 456, 457, 458, 459, 460, 0, 461, 462, 403, - 339, 340, 784, 386, 498, 709, 746, 747, 631, 0, - 695, 632, 642, 379, 667, 679, 678, 492, 591, 0, - 690, 693, 621, 783, 0, 687, 701, 788, 700, 780, - 504, 0, 535, 698, 645, 0, 691, 664, 665, 0, - 692, 660, 696, 0, 633, 0, 602, 605, 635, 722, - 723, 724, 346, 604, 726, 727, 728, 729, 730, 731, - 732, 725, 574, 668, 644, 671, 583, 647, 646, 0, - 0, 682, 600, 683, 684, 486, 487, 488, 489, 407, - 710, 370, 603, 519, 520, 0, 669, 0, 0, 0, - 0, 0, 0, 0, 0, 674, 675, 672, 793, 0, - 733, 734, 0, 0, 597, 598, 402, 0, 616, 410, - 369, 503, 404, 581, 434, 0, 609, 676, 610, 522, - 523, 737, 742, 738, 739, 741, 762, 493, 424, 430, - 539, 444, 466, 525, 580, 501, 530, 367, 570, 541, - 472, 661, 689, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 325, 326, 0, 0, 0, 0, + 0, 0, 1870, 0, 0, 0, 366, 269, 628, 755, + 630, 629, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 369, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 546, 574, 0, 588, 0, 434, 435, 1868, + 0, 0, 0, 0, 0, 0, 350, 553, 571, 367, + 540, 586, 372, 548, 564, 362, 501, 536, 0, 0, + 352, 569, 547, 479, 351, 0, 529, 392, 409, 389, + 499, 0, 0, 568, 599, 388, 589, 0, 579, 354, + 0, 578, 498, 471, 565, 570, 480, 473, 0, 353, + 567, 478, 472, 448, 399, 615, 449, 450, 451, 452, + 453, 454, 413, 514, 469, 515, 414, 486, 485, 487, + 415, 416, 417, 418, 419, 420, 421, 422, 423, 424, + 0, 0, 0, 0, 0, 610, 611, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 747, 0, 0, 752, 0, 582, 0, 0, + 0, 0, 0, 0, 551, 0, 0, 455, 0, 0, + 0, 600, 0, 532, 504, 793, 0, 0, 530, 467, + 566, 516, 572, 554, 580, 523, 517, 344, 555, 391, + 481, 363, 365, 780, 393, 396, 400, 401, 492, 493, + 510, 539, 558, 559, 560, 390, 377, 531, 378, 410, + 379, 345, 382, 381, 383, 541, 384, 347, 511, 563, + 0, 406, 527, 476, 348, 475, 512, 562, 561, 364, + 590, 597, 598, 689, 0, 603, 794, 795, 796, 612, + 0, 518, 360, 357, 0, 0, 0, 386, 513, 373, + 375, 376, 374, 507, 509, 617, 618, 619, 621, 0, + 622, 623, 0, 0, 0, 0, 624, 690, 708, 674, + 643, 605, 698, 640, 644, 645, 427, 428, 429, 430, + 711, 0, 0, 0, 596, 456, 457, 0, 398, 397, + 477, 349, 0, 0, 445, 426, 519, 355, 394, 447, + 432, 458, 459, 460, 461, 462, 0, 463, 464, 404, + 339, 340, 788, 387, 500, 713, 750, 751, 635, 0, + 699, 636, 646, 380, 671, 683, 682, 494, 595, 0, + 694, 697, 625, 787, 0, 691, 705, 792, 704, 784, + 506, 0, 538, 702, 649, 0, 695, 668, 669, 0, + 696, 664, 700, 356, 433, 524, 581, 0, 637, 0, + 606, 609, 639, 726, 727, 728, 346, 608, 730, 731, + 732, 733, 734, 735, 736, 729, 577, 672, 648, 675, + 587, 651, 650, 0, 0, 686, 604, 687, 688, 488, + 489, 490, 491, 408, 714, 371, 607, 521, 522, 0, + 673, 0, 0, 0, 0, 0, 0, 0, 0, 678, + 679, 676, 797, 0, 737, 738, 0, 0, 601, 602, + 403, 0, 620, 411, 370, 505, 405, 585, 436, 0, + 613, 680, 614, 525, 526, 741, 746, 742, 743, 745, + 766, 495, 425, 431, 542, 446, 468, 528, 584, 503, + 533, 368, 573, 544, 474, 665, 693, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 325, 326, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 718, 717, 716, 715, 714, 713, 712, 711, 0, 0, - 658, 384, 781, 775, 782, 763, 766, 765, 740, 0, - 341, 638, 464, 518, 401, 705, 706, 0, 761, 284, - 285, 286, 287, 288, 289, 290, 291, 332, 292, 293, - 294, 295, 296, 297, 298, 303, 304, 305, 306, 307, - 308, 309, 310, 708, 301, 302, 311, 312, 313, 314, - 315, 316, 317, 318, 319, 320, 321, 322, 323, 324, - 0, 0, 0, 0, 333, 767, 768, 769, 770, 771, - 0, 0, 334, 335, 336, 0, 0, 299, 300, 327, - 549, 328, 329, 330, 331, 0, 0, 587, 588, 589, - 612, 0, 590, 572, 637, 411, 342, 553, 579, 778, - 779, 0, 0, 0, 0, 0, 0, 0, 688, 699, - 735, 736, 0, 749, 750, 752, 754, 753, 756, 546, - 547, 764, 0, 0, 435, 357, 358, 494, 495, 532, - 0, 436, 437, 438, 442, 439, 440, 441, 758, 759, - 760, 757, 468, 531, 554, 540, 777, 481, 0, 785, - 627, 628, 786, 745, 744, 343, 0, 337, 338, 702, - 703, 0, 0, 534, 480, 634, 500, 0, 0, 643, - 677, 666, 755, 630, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 394, 0, 0, 463, 681, 662, - 673, 663, 506, 648, 649, 650, 657, 406, 651, 787, - 653, 622, 654, 623, 655, 656, 0, 680, 629, 542, - 482, 0, 697, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 5226, 0, 268, 1069, 0, 0, 0, 0, - 0, 365, 269, 624, 751, 626, 625, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 368, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 543, 571, 0, - 584, 0, 432, 433, 0, 0, 0, 0, 0, 0, - 0, 350, 550, 568, 366, 537, 582, 371, 545, 561, - 361, 499, 533, 0, 0, 352, 566, 544, 477, 351, - 0, 526, 391, 408, 388, 497, 0, 0, 565, 595, - 387, 585, 0, 576, 354, 0, 575, 496, 469, 562, - 567, 478, 471, 0, 353, 564, 476, 470, 446, 398, - 611, 447, 448, 449, 450, 451, 452, 412, 512, 467, - 513, 413, 484, 483, 485, 414, 415, 416, 417, 418, - 419, 420, 421, 422, 423, 0, 0, 0, 0, 0, - 606, 607, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 743, 0, 0, - 748, 0, 578, 0, 0, 0, 0, 0, 0, 548, - 0, 0, 453, 0, 0, 0, 596, 0, 529, 502, - 789, 0, 0, 527, 465, 563, 514, 569, 551, 577, - 521, 515, 344, 552, 390, 479, 362, 364, 776, 392, - 395, 399, 400, 490, 491, 508, 536, 555, 556, 557, - 389, 376, 528, 377, 409, 378, 345, 381, 380, 382, - 538, 383, 347, 509, 560, 0, 405, 524, 474, 348, - 473, 510, 559, 558, 363, 586, 593, 594, 685, 0, - 599, 790, 791, 792, 608, 0, 516, 359, 356, 0, - 0, 0, 385, 511, 372, 374, 375, 373, 505, 507, - 613, 614, 615, 617, 0, 618, 619, 0, 0, 0, - 0, 620, 686, 704, 670, 639, 601, 694, 636, 640, - 641, 426, 427, 428, 429, 707, 0, 0, 0, 592, - 454, 455, 0, 397, 396, 475, 349, 0, 0, 443, - 425, 517, 355, 393, 445, 431, 456, 457, 458, 459, - 460, 0, 461, 462, 403, 339, 340, 784, 386, 498, - 709, 746, 747, 631, 0, 695, 632, 642, 379, 667, - 679, 678, 492, 591, 0, 690, 693, 621, 783, 0, - 687, 701, 788, 700, 780, 504, 0, 535, 698, 645, - 0, 691, 664, 665, 0, 692, 660, 696, 0, 633, - 0, 602, 605, 635, 722, 723, 724, 346, 604, 726, - 727, 728, 729, 730, 731, 732, 725, 574, 668, 644, - 671, 583, 647, 646, 0, 0, 682, 600, 683, 684, - 486, 487, 488, 489, 407, 710, 370, 603, 519, 520, - 0, 669, 0, 0, 0, 0, 0, 0, 0, 0, - 674, 675, 672, 793, 0, 733, 734, 0, 0, 597, - 598, 402, 0, 616, 410, 369, 503, 404, 581, 434, - 0, 609, 676, 610, 522, 523, 737, 742, 738, 739, - 741, 762, 493, 424, 430, 539, 444, 466, 525, 580, - 501, 530, 367, 570, 541, 472, 661, 689, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 325, - 326, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 718, 717, 716, 715, 714, - 713, 712, 711, 0, 0, 658, 384, 781, 775, 782, - 763, 766, 765, 740, 0, 341, 638, 464, 518, 401, - 705, 706, 0, 761, 284, 285, 286, 287, 288, 289, - 290, 291, 332, 292, 293, 294, 295, 296, 297, 298, - 303, 304, 305, 306, 307, 308, 309, 310, 708, 301, - 302, 311, 312, 313, 314, 315, 316, 317, 318, 319, - 320, 321, 322, 323, 324, 0, 0, 0, 0, 333, - 767, 768, 769, 770, 771, 0, 0, 334, 335, 336, - 0, 0, 299, 300, 327, 549, 328, 329, 330, 331, - 0, 0, 587, 588, 589, 612, 0, 590, 572, 637, - 411, 342, 553, 579, 778, 779, 0, 0, 0, 0, - 0, 0, 0, 688, 699, 735, 736, 0, 749, 750, - 752, 754, 753, 756, 546, 547, 764, 0, 0, 435, - 357, 358, 494, 495, 532, 0, 436, 437, 438, 442, - 439, 440, 441, 758, 759, 760, 757, 468, 531, 554, - 540, 777, 481, 0, 785, 627, 628, 786, 745, 744, - 343, 0, 337, 338, 702, 703, 0, 0, 534, 480, - 634, 500, 0, 0, 643, 677, 666, 755, 630, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 394, - 0, 0, 463, 681, 662, 673, 663, 506, 648, 649, - 650, 657, 406, 651, 787, 653, 622, 654, 623, 655, - 656, 0, 680, 629, 542, 482, 0, 697, 0, 0, + 0, 0, 0, 0, 722, 721, 720, 719, 718, 717, + 716, 715, 0, 0, 662, 385, 785, 779, 786, 767, + 770, 769, 744, 0, 341, 642, 466, 520, 402, 709, + 710, 0, 765, 284, 285, 286, 287, 288, 289, 290, + 291, 332, 292, 293, 294, 295, 296, 297, 298, 303, + 304, 305, 306, 307, 308, 309, 310, 712, 301, 302, + 311, 312, 313, 314, 315, 316, 317, 318, 319, 320, + 321, 322, 323, 324, 0, 0, 0, 0, 333, 771, + 772, 773, 774, 775, 0, 0, 334, 335, 336, 0, + 0, 299, 300, 327, 552, 328, 329, 330, 331, 0, + 0, 591, 592, 593, 616, 0, 594, 575, 641, 412, + 342, 556, 583, 782, 783, 0, 0, 0, 0, 0, + 0, 0, 692, 703, 739, 740, 0, 753, 754, 756, + 758, 757, 760, 549, 550, 768, 0, 0, 437, 358, + 359, 496, 497, 535, 0, 438, 439, 440, 444, 441, + 442, 443, 762, 763, 764, 761, 470, 534, 557, 543, + 781, 483, 0, 789, 631, 632, 790, 749, 748, 343, + 0, 337, 338, 706, 707, 0, 0, 537, 482, 638, + 502, 0, 0, 647, 681, 670, 759, 634, 0, 0, + 1204, 0, 0, 0, 0, 0, 0, 1209, 395, 0, + 0, 465, 685, 1205, 677, 667, 1207, 652, 653, 654, + 661, 407, 655, 791, 657, 626, 658, 627, 659, 660, + 0, 684, 633, 545, 484, 0, 701, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 268, - 0, 0, 1866, 0, 0, 0, 365, 269, 624, 751, - 626, 625, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 368, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 543, 571, 0, 584, 0, 432, 433, 1864, - 0, 0, 0, 0, 0, 0, 350, 550, 568, 366, - 537, 582, 371, 545, 561, 361, 499, 533, 0, 0, - 352, 566, 544, 477, 351, 0, 526, 391, 408, 388, - 497, 0, 0, 565, 595, 387, 585, 0, 576, 354, - 0, 575, 496, 469, 562, 567, 478, 471, 0, 353, - 564, 476, 470, 446, 398, 611, 447, 448, 449, 450, - 451, 452, 412, 512, 467, 513, 413, 484, 483, 485, - 414, 415, 416, 417, 418, 419, 420, 421, 422, 423, - 0, 0, 0, 0, 0, 606, 607, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 743, 0, 0, 748, 0, 578, 0, 0, - 0, 0, 0, 0, 548, 0, 0, 453, 0, 0, - 0, 596, 0, 529, 502, 789, 0, 0, 527, 465, - 563, 514, 569, 551, 577, 521, 515, 344, 552, 390, - 479, 362, 364, 776, 392, 395, 399, 400, 490, 491, - 508, 536, 555, 556, 557, 389, 376, 528, 377, 409, - 378, 345, 381, 380, 382, 538, 383, 347, 509, 560, - 0, 405, 524, 474, 348, 473, 510, 559, 558, 363, - 586, 593, 594, 685, 0, 599, 790, 791, 792, 608, - 0, 516, 359, 356, 0, 0, 0, 385, 511, 372, - 374, 375, 373, 505, 507, 613, 614, 615, 617, 0, - 618, 619, 0, 0, 0, 0, 620, 686, 704, 670, - 639, 601, 694, 636, 640, 641, 426, 427, 428, 429, - 707, 0, 0, 0, 592, 454, 455, 0, 397, 396, - 475, 349, 0, 0, 443, 425, 517, 355, 393, 445, - 431, 456, 457, 458, 459, 460, 0, 461, 462, 403, - 339, 340, 784, 386, 498, 709, 746, 747, 631, 0, - 695, 632, 642, 379, 667, 679, 678, 492, 591, 0, - 690, 693, 621, 783, 0, 687, 701, 788, 700, 780, - 504, 0, 535, 698, 645, 0, 691, 664, 665, 0, - 692, 660, 696, 0, 633, 0, 602, 605, 635, 722, - 723, 724, 346, 604, 726, 727, 728, 729, 730, 731, - 732, 725, 574, 668, 644, 671, 583, 647, 646, 0, - 0, 682, 600, 683, 684, 486, 487, 488, 489, 407, - 710, 370, 603, 519, 520, 0, 669, 0, 0, 0, - 0, 0, 0, 0, 0, 674, 675, 672, 793, 0, - 733, 734, 0, 0, 597, 598, 402, 0, 616, 410, - 369, 503, 404, 581, 434, 0, 609, 676, 610, 522, - 523, 737, 742, 738, 739, 741, 762, 493, 424, 430, - 539, 444, 466, 525, 580, 501, 530, 367, 570, 541, - 472, 661, 689, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 325, 326, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 268, 0, + 0, 0, 0, 0, 0, 366, 269, 628, 755, 630, + 629, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 369, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 546, 574, 0, 588, 0, 434, 435, 0, 0, + 0, 0, 0, 0, 0, 350, 553, 571, 367, 540, + 586, 372, 548, 564, 362, 501, 536, 0, 0, 352, + 569, 547, 479, 351, 0, 529, 392, 409, 389, 499, + 0, 0, 568, 599, 388, 589, 0, 579, 354, 0, + 578, 498, 471, 565, 570, 480, 473, 0, 353, 567, + 478, 472, 448, 399, 615, 449, 450, 451, 452, 453, + 454, 413, 514, 469, 515, 414, 486, 485, 487, 415, + 416, 417, 418, 419, 420, 421, 422, 423, 424, 0, + 0, 0, 0, 0, 610, 611, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 747, 0, 0, 752, 0, 582, 0, 1208, 0, + 0, 0, 0, 551, 0, 0, 455, 0, 0, 0, + 600, 0, 532, 504, 793, 0, 0, 530, 467, 566, + 516, 572, 554, 580, 523, 517, 344, 555, 391, 481, + 363, 365, 780, 393, 396, 400, 401, 492, 493, 510, + 539, 558, 559, 560, 390, 377, 531, 378, 410, 379, + 345, 382, 381, 383, 541, 384, 347, 511, 563, 0, + 406, 527, 476, 348, 475, 512, 562, 561, 364, 590, + 597, 598, 689, 0, 603, 794, 795, 796, 612, 0, + 518, 360, 357, 0, 0, 0, 386, 513, 373, 375, + 376, 374, 507, 509, 617, 618, 619, 621, 0, 622, + 623, 0, 0, 0, 0, 624, 690, 708, 674, 643, + 605, 698, 640, 644, 645, 427, 428, 429, 430, 711, + 0, 0, 0, 596, 456, 457, 0, 398, 397, 477, + 349, 0, 0, 445, 426, 519, 355, 394, 447, 432, + 458, 459, 460, 461, 462, 0, 463, 464, 404, 339, + 340, 788, 387, 500, 713, 750, 751, 635, 0, 699, + 636, 646, 380, 671, 683, 682, 494, 595, 0, 694, + 697, 625, 787, 0, 691, 705, 792, 704, 784, 506, + 0, 538, 702, 649, 0, 695, 668, 669, 0, 696, + 664, 700, 356, 433, 524, 581, 0, 637, 0, 606, + 609, 639, 726, 727, 728, 346, 608, 730, 731, 732, + 733, 734, 735, 736, 729, 577, 672, 648, 675, 587, + 651, 650, 0, 0, 686, 604, 687, 688, 488, 489, + 490, 491, 408, 714, 371, 607, 521, 522, 0, 673, + 0, 0, 0, 0, 0, 0, 0, 0, 678, 679, + 676, 797, 0, 737, 738, 0, 0, 601, 602, 403, + 0, 620, 411, 370, 505, 405, 585, 436, 0, 613, + 680, 614, 525, 526, 741, 746, 742, 743, 745, 766, + 495, 425, 431, 542, 446, 468, 528, 584, 503, 533, + 368, 573, 544, 474, 665, 693, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 325, 326, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 718, 717, 716, 715, 714, 713, 712, 711, 0, 0, - 658, 384, 781, 775, 782, 763, 766, 765, 740, 0, - 341, 638, 464, 518, 401, 705, 706, 0, 761, 284, - 285, 286, 287, 288, 289, 290, 291, 332, 292, 293, - 294, 295, 296, 297, 298, 303, 304, 305, 306, 307, - 308, 309, 310, 708, 301, 302, 311, 312, 313, 314, - 315, 316, 317, 318, 319, 320, 321, 322, 323, 324, - 0, 0, 0, 0, 333, 767, 768, 769, 770, 771, - 0, 0, 334, 335, 336, 0, 0, 299, 300, 327, - 549, 328, 329, 330, 331, 0, 0, 587, 588, 589, - 612, 0, 590, 572, 637, 411, 342, 553, 579, 778, - 779, 0, 0, 0, 0, 0, 0, 0, 688, 699, - 735, 736, 0, 749, 750, 752, 754, 753, 756, 546, - 547, 764, 0, 0, 435, 357, 358, 494, 495, 532, - 0, 436, 437, 438, 442, 439, 440, 441, 758, 759, - 760, 757, 468, 531, 554, 540, 777, 481, 0, 785, - 627, 628, 786, 745, 744, 343, 0, 337, 338, 702, - 703, 0, 0, 534, 480, 634, 500, 0, 0, 643, - 677, 666, 755, 630, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 394, 0, 0, 463, 681, 662, - 673, 663, 506, 648, 649, 650, 657, 406, 651, 787, - 653, 622, 654, 623, 655, 656, 0, 680, 629, 542, - 482, 0, 697, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 268, 0, 0, 1866, 0, 0, - 0, 365, 269, 624, 751, 626, 625, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 368, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 543, 571, 0, - 584, 0, 432, 433, 2184, 0, 0, 0, 0, 0, - 0, 350, 550, 568, 366, 537, 582, 371, 545, 561, - 361, 499, 533, 0, 0, 352, 566, 544, 477, 351, - 0, 526, 391, 408, 388, 497, 0, 0, 565, 595, - 387, 585, 0, 576, 354, 0, 575, 496, 469, 562, - 567, 478, 471, 0, 353, 564, 476, 470, 446, 398, - 611, 447, 448, 449, 450, 451, 452, 412, 512, 467, - 513, 413, 484, 483, 485, 414, 415, 416, 417, 418, - 419, 420, 421, 422, 423, 0, 0, 0, 0, 0, - 606, 607, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 743, 0, 0, - 748, 0, 578, 0, 0, 0, 0, 0, 0, 548, - 0, 0, 453, 0, 0, 0, 596, 0, 529, 502, - 789, 0, 0, 527, 465, 563, 514, 569, 551, 577, - 521, 515, 344, 552, 390, 479, 362, 364, 776, 392, - 395, 399, 400, 490, 491, 508, 536, 555, 556, 557, - 389, 376, 528, 377, 409, 378, 345, 381, 380, 382, - 538, 383, 347, 509, 560, 0, 405, 524, 474, 348, - 473, 510, 559, 558, 363, 586, 593, 594, 685, 0, - 599, 790, 791, 792, 608, 0, 516, 359, 356, 0, - 0, 0, 385, 511, 372, 374, 375, 373, 505, 507, - 613, 614, 615, 617, 0, 618, 619, 0, 0, 0, - 0, 620, 686, 704, 670, 639, 601, 694, 636, 640, - 641, 426, 427, 428, 429, 707, 0, 0, 0, 592, - 454, 455, 0, 397, 396, 475, 349, 0, 0, 443, - 425, 517, 355, 393, 445, 431, 456, 457, 458, 459, - 460, 0, 461, 462, 403, 339, 340, 784, 386, 498, - 709, 746, 747, 631, 0, 695, 632, 642, 379, 667, - 679, 678, 492, 591, 0, 690, 693, 621, 783, 0, - 687, 701, 788, 700, 780, 504, 0, 535, 698, 645, - 0, 691, 664, 665, 0, 692, 660, 696, 0, 633, - 0, 602, 605, 635, 722, 723, 724, 346, 604, 726, - 727, 728, 729, 730, 731, 732, 725, 574, 668, 644, - 671, 583, 647, 646, 0, 0, 682, 600, 683, 684, - 486, 487, 488, 489, 407, 710, 370, 603, 519, 520, - 0, 669, 0, 0, 0, 0, 0, 0, 0, 0, - 674, 675, 672, 793, 0, 733, 734, 0, 0, 597, - 598, 402, 0, 616, 410, 369, 503, 404, 581, 434, - 0, 609, 676, 610, 522, 523, 737, 742, 738, 739, - 741, 762, 493, 424, 430, 539, 444, 466, 525, 580, - 501, 530, 367, 570, 541, 472, 661, 689, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 325, - 326, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 718, 717, 716, 715, 714, - 713, 712, 711, 0, 0, 658, 384, 781, 775, 782, - 763, 766, 765, 740, 0, 341, 638, 464, 518, 401, - 705, 706, 0, 761, 284, 285, 286, 287, 288, 289, - 290, 291, 332, 292, 293, 294, 295, 296, 297, 298, - 303, 304, 305, 306, 307, 308, 309, 310, 708, 301, - 302, 311, 312, 313, 314, 315, 316, 317, 318, 319, - 320, 321, 322, 323, 324, 0, 0, 0, 0, 333, - 767, 768, 769, 770, 771, 0, 0, 334, 335, 336, - 0, 0, 299, 300, 327, 549, 328, 329, 330, 331, - 0, 0, 587, 588, 589, 612, 0, 590, 572, 637, - 411, 342, 553, 579, 778, 779, 0, 0, 0, 0, - 0, 0, 0, 688, 699, 735, 736, 0, 749, 750, - 752, 754, 753, 756, 546, 547, 764, 0, 0, 435, - 357, 358, 494, 495, 532, 0, 436, 437, 438, 442, - 439, 440, 441, 758, 759, 760, 757, 468, 531, 554, - 540, 777, 481, 0, 785, 627, 628, 786, 745, 744, - 343, 0, 337, 338, 702, 703, 0, 0, 534, 480, - 634, 500, 0, 0, 643, 677, 666, 755, 630, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 394, - 0, 0, 463, 681, 662, 673, 663, 506, 648, 649, - 650, 657, 406, 651, 787, 653, 622, 654, 623, 655, - 656, 0, 680, 629, 542, 482, 0, 697, 0, 0, + 0, 0, 0, 722, 721, 720, 719, 718, 717, 716, + 715, 0, 0, 662, 385, 785, 779, 786, 767, 770, + 769, 744, 0, 341, 642, 466, 520, 402, 709, 710, + 0, 765, 284, 285, 286, 287, 288, 289, 290, 291, + 332, 292, 293, 294, 295, 296, 297, 298, 303, 304, + 305, 306, 307, 308, 309, 310, 712, 301, 302, 311, + 312, 313, 314, 315, 316, 317, 318, 319, 320, 321, + 322, 323, 324, 0, 0, 0, 0, 333, 771, 772, + 773, 774, 775, 0, 0, 334, 335, 336, 0, 0, + 299, 300, 327, 552, 328, 329, 330, 331, 0, 0, + 591, 592, 593, 616, 0, 594, 575, 641, 412, 342, + 556, 583, 782, 783, 0, 0, 0, 0, 0, 0, + 0, 692, 703, 739, 740, 0, 753, 754, 756, 758, + 757, 760, 549, 550, 768, 0, 0, 437, 358, 359, + 496, 497, 535, 0, 438, 439, 440, 444, 441, 442, + 443, 762, 763, 764, 761, 470, 534, 557, 543, 781, + 483, 0, 789, 631, 632, 790, 749, 748, 343, 0, + 337, 338, 706, 707, 0, 0, 537, 482, 638, 502, + 0, 0, 647, 681, 670, 759, 634, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 395, 0, 0, + 465, 685, 666, 677, 667, 508, 652, 653, 654, 661, + 407, 655, 791, 657, 626, 658, 627, 659, 660, 0, + 684, 633, 545, 484, 0, 701, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 5230, 0, 268, 1073, 0, + 0, 0, 0, 0, 366, 269, 628, 755, 630, 629, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 369, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 546, 574, 0, 588, 0, 434, 435, 0, 0, 0, + 0, 0, 0, 0, 350, 553, 571, 367, 540, 586, + 372, 548, 564, 362, 501, 536, 0, 0, 352, 569, + 547, 479, 351, 0, 529, 392, 409, 389, 499, 0, + 0, 568, 599, 388, 589, 0, 579, 354, 0, 578, + 498, 471, 565, 570, 480, 473, 0, 353, 567, 478, + 472, 448, 399, 615, 449, 450, 451, 452, 453, 454, + 413, 514, 469, 515, 414, 486, 485, 487, 415, 416, + 417, 418, 419, 420, 421, 422, 423, 424, 0, 0, + 0, 0, 0, 610, 611, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 747, 0, 0, 752, 0, 582, 0, 0, 0, 0, + 0, 0, 551, 0, 0, 455, 0, 0, 0, 600, + 0, 532, 504, 793, 0, 0, 530, 467, 566, 516, + 572, 554, 580, 523, 517, 344, 555, 391, 481, 363, + 365, 780, 393, 396, 400, 401, 492, 493, 510, 539, + 558, 559, 560, 390, 377, 531, 378, 410, 379, 345, + 382, 381, 383, 541, 384, 347, 511, 563, 0, 406, + 527, 476, 348, 475, 512, 562, 561, 364, 590, 597, + 598, 689, 0, 603, 794, 795, 796, 612, 0, 518, + 360, 357, 0, 0, 0, 386, 513, 373, 375, 376, + 374, 507, 509, 617, 618, 619, 621, 0, 622, 623, + 0, 0, 0, 0, 624, 690, 708, 674, 643, 605, + 698, 640, 644, 645, 427, 428, 429, 430, 711, 0, + 0, 0, 596, 456, 457, 0, 398, 397, 477, 349, + 0, 0, 445, 426, 519, 355, 394, 447, 432, 458, + 459, 460, 461, 462, 0, 463, 464, 404, 339, 340, + 788, 387, 500, 713, 750, 751, 635, 0, 699, 636, + 646, 380, 671, 683, 682, 494, 595, 0, 694, 697, + 625, 787, 0, 691, 705, 792, 704, 784, 506, 0, + 538, 702, 649, 0, 695, 668, 669, 0, 696, 664, + 700, 356, 433, 524, 581, 0, 637, 0, 606, 609, + 639, 726, 727, 728, 346, 608, 730, 731, 732, 733, + 734, 735, 736, 729, 577, 672, 648, 675, 587, 651, + 650, 0, 0, 686, 604, 687, 688, 488, 489, 490, + 491, 408, 714, 371, 607, 521, 522, 0, 673, 0, + 0, 0, 0, 0, 0, 0, 0, 678, 679, 676, + 797, 0, 737, 738, 0, 0, 601, 602, 403, 0, + 620, 411, 370, 505, 405, 585, 436, 0, 613, 680, + 614, 525, 526, 741, 746, 742, 743, 745, 766, 495, + 425, 431, 542, 446, 468, 528, 584, 503, 533, 368, + 573, 544, 474, 665, 693, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 325, 326, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 268, - 0, 0, 0, 0, 0, 0, 365, 269, 624, 751, - 626, 625, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 368, 0, 0, 0, 0, 0, 0, 4638, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 4639, 0, 0, - 0, 0, 543, 571, 0, 584, 0, 432, 433, 0, - 0, 0, 0, 0, 0, 0, 350, 550, 568, 366, - 537, 582, 371, 545, 561, 361, 499, 533, 0, 0, - 352, 566, 544, 477, 351, 0, 526, 391, 408, 388, - 497, 0, 0, 565, 595, 387, 585, 0, 576, 354, - 0, 575, 496, 469, 562, 567, 478, 471, 0, 353, - 564, 476, 470, 446, 398, 611, 447, 448, 449, 450, - 451, 452, 412, 512, 467, 513, 413, 484, 483, 485, - 414, 415, 416, 417, 418, 419, 420, 421, 422, 423, - 0, 0, 0, 0, 0, 606, 607, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 743, 0, 0, 748, 0, 578, 0, 0, - 0, 0, 0, 0, 548, 0, 0, 453, 0, 0, - 0, 596, 0, 529, 502, 789, 0, 0, 527, 465, - 563, 514, 569, 551, 577, 521, 515, 344, 552, 390, - 479, 362, 364, 776, 392, 395, 399, 400, 490, 491, - 508, 536, 555, 556, 557, 389, 376, 528, 377, 409, - 378, 345, 381, 380, 382, 538, 383, 347, 509, 560, - 0, 405, 524, 474, 348, 473, 510, 559, 558, 363, - 586, 593, 594, 685, 0, 599, 790, 791, 792, 608, - 0, 516, 359, 356, 0, 0, 0, 385, 511, 372, - 374, 375, 373, 505, 507, 613, 614, 615, 617, 0, - 618, 619, 0, 0, 0, 0, 620, 686, 704, 670, - 639, 601, 694, 636, 640, 641, 426, 427, 428, 429, - 707, 0, 0, 0, 592, 454, 455, 0, 397, 396, - 475, 349, 0, 0, 443, 425, 517, 355, 393, 445, - 431, 456, 457, 458, 459, 460, 0, 461, 462, 403, - 339, 340, 784, 386, 498, 709, 746, 747, 631, 0, - 695, 632, 642, 379, 667, 679, 678, 492, 591, 0, - 690, 693, 621, 783, 0, 687, 701, 788, 700, 780, - 504, 0, 535, 698, 645, 0, 691, 664, 665, 0, - 692, 660, 696, 0, 633, 0, 602, 605, 635, 722, - 723, 724, 346, 604, 726, 727, 728, 729, 730, 731, - 732, 725, 574, 668, 644, 671, 583, 647, 646, 0, - 0, 682, 600, 683, 684, 486, 487, 488, 489, 407, - 710, 370, 603, 519, 520, 0, 669, 0, 0, 0, - 0, 0, 0, 0, 0, 674, 675, 672, 793, 0, - 733, 734, 0, 0, 597, 598, 402, 0, 616, 410, - 369, 503, 404, 581, 434, 0, 609, 676, 610, 522, - 523, 737, 742, 738, 739, 741, 762, 493, 424, 430, - 539, 444, 466, 525, 580, 501, 530, 367, 570, 541, - 472, 661, 689, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 722, 721, 720, 719, 718, 717, 716, 715, + 0, 0, 662, 385, 785, 779, 786, 767, 770, 769, + 744, 0, 341, 642, 466, 520, 402, 709, 710, 0, + 765, 284, 285, 286, 287, 288, 289, 290, 291, 332, + 292, 293, 294, 295, 296, 297, 298, 303, 304, 305, + 306, 307, 308, 309, 310, 712, 301, 302, 311, 312, + 313, 314, 315, 316, 317, 318, 319, 320, 321, 322, + 323, 324, 0, 0, 0, 0, 333, 771, 772, 773, + 774, 775, 0, 0, 334, 335, 336, 0, 0, 299, + 300, 327, 552, 328, 329, 330, 331, 0, 0, 591, + 592, 593, 616, 0, 594, 575, 641, 412, 342, 556, + 583, 782, 783, 0, 0, 0, 0, 0, 0, 0, + 692, 703, 739, 740, 0, 753, 754, 756, 758, 757, + 760, 549, 550, 768, 0, 0, 437, 358, 359, 496, + 497, 535, 0, 438, 439, 440, 444, 441, 442, 443, + 762, 763, 764, 761, 470, 534, 557, 543, 781, 483, + 0, 789, 631, 632, 790, 749, 748, 343, 0, 337, + 338, 706, 707, 0, 0, 537, 482, 638, 502, 0, + 0, 647, 681, 670, 759, 634, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 395, 0, 0, 465, + 685, 666, 677, 667, 508, 652, 653, 654, 661, 407, + 655, 791, 657, 626, 658, 627, 659, 660, 0, 684, + 633, 545, 484, 0, 701, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 268, 0, 0, 1870, + 0, 0, 0, 366, 269, 628, 755, 630, 629, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 369, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 546, + 574, 0, 588, 0, 434, 435, 1868, 0, 0, 0, + 0, 0, 0, 350, 553, 571, 367, 540, 586, 372, + 548, 564, 362, 501, 536, 0, 0, 352, 569, 547, + 479, 351, 0, 529, 392, 409, 389, 499, 0, 0, + 568, 599, 388, 589, 0, 579, 354, 0, 578, 498, + 471, 565, 570, 480, 473, 0, 353, 567, 478, 472, + 448, 399, 615, 449, 450, 451, 452, 453, 454, 413, + 514, 469, 515, 414, 486, 485, 487, 415, 416, 417, + 418, 419, 420, 421, 422, 423, 424, 0, 0, 0, + 0, 0, 610, 611, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 747, + 0, 0, 752, 0, 582, 0, 0, 0, 0, 0, + 0, 551, 0, 0, 455, 0, 0, 0, 600, 0, + 532, 504, 793, 0, 0, 530, 467, 566, 516, 572, + 554, 580, 523, 517, 344, 555, 391, 481, 363, 365, + 780, 393, 396, 400, 401, 492, 493, 510, 539, 558, + 559, 560, 390, 377, 531, 378, 410, 379, 345, 382, + 381, 383, 541, 384, 347, 511, 563, 0, 406, 527, + 476, 348, 475, 512, 562, 561, 364, 590, 597, 598, + 689, 0, 603, 794, 795, 796, 612, 0, 518, 360, + 357, 0, 0, 0, 386, 513, 373, 375, 376, 374, + 507, 509, 617, 618, 619, 621, 0, 622, 623, 0, + 0, 0, 0, 624, 690, 708, 674, 643, 605, 698, + 640, 644, 645, 427, 428, 429, 430, 711, 0, 0, + 0, 596, 456, 457, 0, 398, 397, 477, 349, 0, + 0, 445, 426, 519, 355, 394, 447, 432, 458, 459, + 460, 461, 462, 0, 463, 464, 404, 339, 340, 788, + 387, 500, 713, 750, 751, 635, 0, 699, 636, 646, + 380, 671, 683, 682, 494, 595, 0, 694, 697, 625, + 787, 0, 691, 705, 792, 704, 784, 506, 0, 538, + 702, 649, 0, 695, 668, 669, 0, 696, 664, 700, + 356, 433, 524, 581, 0, 637, 0, 606, 609, 639, + 726, 727, 728, 346, 608, 730, 731, 732, 733, 734, + 735, 736, 729, 577, 672, 648, 675, 587, 651, 650, + 0, 0, 686, 604, 687, 688, 488, 489, 490, 491, + 408, 714, 371, 607, 521, 522, 0, 673, 0, 0, + 0, 0, 0, 0, 0, 0, 678, 679, 676, 797, + 0, 737, 738, 0, 0, 601, 602, 403, 0, 620, + 411, 370, 505, 405, 585, 436, 0, 613, 680, 614, + 525, 526, 741, 746, 742, 743, 745, 766, 495, 425, + 431, 542, 446, 468, 528, 584, 503, 533, 368, 573, + 544, 474, 665, 693, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 325, 326, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 722, 721, 720, 719, 718, 717, 716, 715, 0, + 0, 662, 385, 785, 779, 786, 767, 770, 769, 744, + 0, 341, 642, 466, 520, 402, 709, 710, 0, 765, + 284, 285, 286, 287, 288, 289, 290, 291, 332, 292, + 293, 294, 295, 296, 297, 298, 303, 304, 305, 306, + 307, 308, 309, 310, 712, 301, 302, 311, 312, 313, + 314, 315, 316, 317, 318, 319, 320, 321, 322, 323, + 324, 0, 0, 0, 0, 333, 771, 772, 773, 774, + 775, 0, 0, 334, 335, 336, 0, 0, 299, 300, + 327, 552, 328, 329, 330, 331, 0, 0, 591, 592, + 593, 616, 0, 594, 575, 641, 412, 342, 556, 583, + 782, 783, 0, 0, 0, 0, 0, 0, 0, 692, + 703, 739, 740, 0, 753, 754, 756, 758, 757, 760, + 549, 550, 768, 0, 0, 437, 358, 359, 496, 497, + 535, 0, 438, 439, 440, 444, 441, 442, 443, 762, + 763, 764, 761, 470, 534, 557, 543, 781, 483, 0, + 789, 631, 632, 790, 749, 748, 343, 0, 337, 338, + 706, 707, 0, 0, 537, 482, 638, 502, 0, 0, + 647, 681, 670, 759, 634, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 395, 0, 0, 465, 685, + 666, 677, 667, 508, 652, 653, 654, 661, 407, 655, + 791, 657, 626, 658, 627, 659, 660, 0, 684, 633, + 545, 484, 0, 701, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 268, 0, 0, 1870, 0, + 0, 0, 366, 269, 628, 755, 630, 629, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 369, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 546, 574, + 0, 588, 0, 434, 435, 2188, 0, 0, 0, 0, + 0, 0, 350, 553, 571, 367, 540, 586, 372, 548, + 564, 362, 501, 536, 0, 0, 352, 569, 547, 479, + 351, 0, 529, 392, 409, 389, 499, 0, 0, 568, + 599, 388, 589, 0, 579, 354, 0, 578, 498, 471, + 565, 570, 480, 473, 0, 353, 567, 478, 472, 448, + 399, 615, 449, 450, 451, 452, 453, 454, 413, 514, + 469, 515, 414, 486, 485, 487, 415, 416, 417, 418, + 419, 420, 421, 422, 423, 424, 0, 0, 0, 0, + 0, 610, 611, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 747, 0, + 0, 752, 0, 582, 0, 0, 0, 0, 0, 0, + 551, 0, 0, 455, 0, 0, 0, 600, 0, 532, + 504, 793, 0, 0, 530, 467, 566, 516, 572, 554, + 580, 523, 517, 344, 555, 391, 481, 363, 365, 780, + 393, 396, 400, 401, 492, 493, 510, 539, 558, 559, + 560, 390, 377, 531, 378, 410, 379, 345, 382, 381, + 383, 541, 384, 347, 511, 563, 0, 406, 527, 476, + 348, 475, 512, 562, 561, 364, 590, 597, 598, 689, + 0, 603, 794, 795, 796, 612, 0, 518, 360, 357, + 0, 0, 0, 386, 513, 373, 375, 376, 374, 507, + 509, 617, 618, 619, 621, 0, 622, 623, 0, 0, + 0, 0, 624, 690, 708, 674, 643, 605, 698, 640, + 644, 645, 427, 428, 429, 430, 711, 0, 0, 0, + 596, 456, 457, 0, 398, 397, 477, 349, 0, 0, + 445, 426, 519, 355, 394, 447, 432, 458, 459, 460, + 461, 462, 0, 463, 464, 404, 339, 340, 788, 387, + 500, 713, 750, 751, 635, 0, 699, 636, 646, 380, + 671, 683, 682, 494, 595, 0, 694, 697, 625, 787, + 0, 691, 705, 792, 704, 784, 506, 0, 538, 702, + 649, 0, 695, 668, 669, 0, 696, 664, 700, 356, + 433, 524, 581, 0, 637, 0, 606, 609, 639, 726, + 727, 728, 346, 608, 730, 731, 732, 733, 734, 735, + 736, 729, 577, 672, 648, 675, 587, 651, 650, 0, + 0, 686, 604, 687, 688, 488, 489, 490, 491, 408, + 714, 371, 607, 521, 522, 0, 673, 0, 0, 0, + 0, 0, 0, 0, 0, 678, 679, 676, 797, 0, + 737, 738, 0, 0, 601, 602, 403, 0, 620, 411, + 370, 505, 405, 585, 436, 0, 613, 680, 614, 525, + 526, 741, 746, 742, 743, 745, 766, 495, 425, 431, + 542, 446, 468, 528, 584, 503, 533, 368, 573, 544, + 474, 665, 693, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 325, 326, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 718, 717, 716, 715, 714, 713, 712, 711, 0, 0, - 658, 384, 781, 775, 782, 763, 766, 765, 740, 0, - 341, 638, 464, 518, 401, 705, 706, 0, 761, 284, + 722, 721, 720, 719, 718, 717, 716, 715, 0, 0, + 662, 385, 785, 779, 786, 767, 770, 769, 744, 0, + 341, 642, 466, 520, 402, 709, 710, 0, 765, 284, 285, 286, 287, 288, 289, 290, 291, 332, 292, 293, 294, 295, 296, 297, 298, 303, 304, 305, 306, 307, - 308, 309, 310, 708, 301, 302, 311, 312, 313, 314, + 308, 309, 310, 712, 301, 302, 311, 312, 313, 314, 315, 316, 317, 318, 319, 320, 321, 322, 323, 324, - 0, 0, 0, 0, 333, 767, 768, 769, 770, 771, + 0, 0, 0, 0, 333, 771, 772, 773, 774, 775, 0, 0, 334, 335, 336, 0, 0, 299, 300, 327, - 549, 328, 329, 330, 331, 0, 0, 587, 588, 589, - 612, 0, 590, 572, 637, 411, 342, 553, 579, 778, - 779, 0, 0, 0, 0, 0, 0, 0, 688, 699, - 735, 736, 0, 749, 750, 752, 754, 753, 756, 546, - 547, 764, 0, 0, 435, 357, 358, 494, 495, 532, - 0, 436, 437, 438, 442, 439, 440, 441, 758, 759, - 760, 757, 468, 531, 554, 540, 777, 481, 0, 785, - 627, 628, 786, 745, 744, 343, 0, 337, 338, 702, - 703, 0, 0, 534, 480, 634, 500, 0, 0, 643, - 677, 666, 755, 630, 0, 0, 0, 0, 0, 2621, - 0, 0, 0, 0, 394, 0, 0, 463, 681, 662, - 673, 663, 506, 648, 649, 650, 657, 406, 651, 787, - 653, 622, 654, 623, 655, 656, 0, 680, 629, 542, - 482, 0, 697, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 268, 0, 0, 2622, 0, 0, - 0, 365, 269, 624, 751, 626, 625, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 368, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 543, 571, 0, - 584, 0, 432, 433, 0, 0, 0, 0, 0, 0, - 0, 350, 550, 568, 366, 537, 582, 371, 545, 561, - 361, 499, 533, 0, 0, 352, 566, 544, 477, 351, - 0, 526, 391, 408, 388, 497, 0, 0, 565, 595, - 387, 585, 0, 576, 354, 0, 575, 496, 469, 562, - 567, 478, 471, 0, 353, 564, 476, 470, 446, 398, - 611, 447, 448, 449, 450, 451, 452, 412, 512, 467, - 513, 413, 484, 483, 485, 414, 415, 416, 417, 418, - 419, 420, 421, 422, 423, 0, 0, 0, 0, 0, - 606, 607, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 743, 0, 0, - 748, 0, 578, 0, 0, 0, 0, 0, 0, 548, - 0, 0, 453, 0, 0, 0, 596, 0, 529, 502, - 789, 0, 0, 527, 465, 563, 514, 569, 551, 577, - 521, 515, 344, 552, 390, 479, 362, 364, 776, 392, - 395, 399, 400, 490, 491, 508, 536, 555, 556, 557, - 389, 376, 528, 377, 409, 378, 345, 381, 380, 382, - 538, 383, 347, 509, 560, 0, 405, 524, 474, 348, - 473, 510, 559, 558, 363, 586, 593, 594, 685, 0, - 599, 790, 791, 792, 608, 0, 516, 359, 356, 0, - 0, 0, 385, 511, 372, 374, 375, 373, 505, 507, - 613, 614, 615, 617, 0, 618, 619, 0, 0, 0, - 0, 620, 686, 704, 670, 639, 601, 694, 636, 640, - 641, 426, 427, 428, 429, 707, 0, 0, 0, 592, - 454, 455, 0, 397, 396, 475, 349, 0, 0, 443, - 425, 517, 355, 393, 445, 431, 456, 457, 458, 459, - 460, 0, 461, 462, 403, 339, 340, 784, 386, 498, - 709, 746, 747, 631, 0, 695, 632, 642, 379, 667, - 679, 678, 492, 591, 0, 690, 693, 621, 783, 0, - 687, 701, 788, 700, 780, 504, 0, 535, 698, 645, - 0, 691, 664, 665, 0, 692, 660, 696, 0, 633, - 0, 602, 605, 635, 722, 723, 724, 346, 604, 726, - 727, 728, 729, 730, 731, 732, 725, 574, 668, 644, - 671, 583, 647, 646, 0, 0, 682, 600, 683, 684, - 486, 487, 488, 489, 407, 710, 370, 603, 519, 520, - 0, 669, 0, 0, 0, 0, 0, 0, 0, 0, - 674, 675, 672, 793, 0, 733, 734, 0, 0, 597, - 598, 402, 0, 616, 410, 369, 503, 404, 581, 434, - 0, 609, 676, 610, 522, 523, 737, 742, 738, 739, - 741, 762, 493, 424, 430, 539, 444, 466, 525, 580, - 501, 530, 367, 570, 541, 472, 661, 689, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 325, - 326, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 718, 717, 716, 715, 714, - 713, 712, 711, 0, 0, 658, 384, 781, 775, 782, - 763, 766, 765, 740, 0, 341, 638, 464, 518, 401, - 705, 706, 0, 761, 284, 285, 286, 287, 288, 289, - 290, 291, 332, 292, 293, 294, 295, 296, 297, 298, - 303, 304, 305, 306, 307, 308, 309, 310, 708, 301, - 302, 311, 312, 313, 314, 315, 316, 317, 318, 319, - 320, 321, 322, 323, 324, 0, 0, 0, 0, 333, - 767, 768, 769, 770, 771, 0, 0, 334, 335, 336, - 0, 0, 299, 300, 327, 549, 328, 329, 330, 331, - 0, 0, 587, 588, 589, 612, 0, 590, 572, 637, - 411, 342, 553, 579, 778, 779, 0, 0, 0, 0, - 0, 0, 0, 688, 699, 735, 736, 0, 749, 750, - 752, 754, 753, 756, 546, 547, 764, 0, 0, 435, - 357, 358, 494, 495, 532, 0, 436, 437, 438, 442, - 439, 440, 441, 758, 759, 760, 757, 468, 531, 554, - 540, 777, 481, 0, 785, 627, 628, 786, 745, 744, - 343, 0, 337, 338, 702, 703, 0, 0, 534, 480, - 634, 500, 0, 0, 643, 677, 666, 755, 630, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 394, - 0, 0, 463, 681, 662, 673, 663, 506, 648, 649, - 650, 657, 406, 651, 787, 653, 622, 654, 623, 655, - 656, 0, 680, 629, 542, 482, 0, 697, 0, 0, + 552, 328, 329, 330, 331, 0, 0, 591, 592, 593, + 616, 0, 594, 575, 641, 412, 342, 556, 583, 782, + 783, 0, 0, 0, 0, 0, 0, 0, 692, 703, + 739, 740, 0, 753, 754, 756, 758, 757, 760, 549, + 550, 768, 0, 0, 437, 358, 359, 496, 497, 535, + 0, 438, 439, 440, 444, 441, 442, 443, 762, 763, + 764, 761, 470, 534, 557, 543, 781, 483, 0, 789, + 631, 632, 790, 749, 748, 343, 0, 337, 338, 706, + 707, 0, 0, 537, 482, 638, 502, 0, 0, 647, + 681, 670, 759, 634, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 395, 0, 0, 465, 685, 666, + 677, 667, 508, 652, 653, 654, 661, 407, 655, 791, + 657, 626, 658, 627, 659, 660, 0, 684, 633, 545, + 484, 0, 701, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 268, - 0, 0, 4227, 4228, 0, 0, 365, 269, 624, 751, - 626, 625, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 368, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 543, 571, 0, 584, 0, 432, 433, 0, - 0, 0, 0, 0, 0, 0, 350, 550, 568, 366, - 537, 582, 371, 545, 561, 361, 499, 533, 0, 0, - 352, 566, 544, 477, 351, 0, 526, 391, 408, 388, - 497, 0, 0, 565, 595, 387, 585, 0, 576, 354, - 0, 575, 496, 469, 562, 567, 478, 471, 0, 353, - 564, 476, 470, 446, 398, 611, 447, 448, 449, 450, - 451, 452, 412, 512, 467, 513, 413, 484, 483, 485, - 414, 415, 416, 417, 418, 419, 420, 421, 422, 423, - 0, 0, 0, 0, 0, 606, 607, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 743, 0, 0, 748, 0, 578, 0, 0, - 0, 0, 0, 0, 548, 0, 0, 453, 0, 0, - 0, 596, 0, 529, 502, 789, 0, 0, 527, 465, - 563, 514, 569, 551, 577, 521, 515, 344, 552, 390, - 479, 362, 364, 776, 392, 395, 399, 400, 490, 491, - 508, 536, 555, 556, 557, 389, 376, 528, 377, 409, - 378, 345, 381, 380, 382, 538, 383, 347, 509, 560, - 0, 405, 524, 474, 348, 473, 510, 559, 558, 363, - 586, 593, 594, 685, 0, 599, 790, 791, 792, 608, - 0, 516, 359, 356, 0, 0, 0, 385, 511, 372, - 374, 375, 373, 505, 507, 613, 614, 615, 617, 0, - 618, 619, 0, 0, 0, 0, 620, 686, 704, 670, - 639, 601, 694, 636, 640, 641, 426, 427, 428, 429, - 707, 0, 0, 0, 592, 454, 455, 0, 397, 396, - 475, 349, 0, 0, 443, 425, 517, 355, 393, 445, - 431, 456, 457, 458, 459, 460, 0, 461, 462, 403, - 339, 340, 784, 386, 498, 709, 746, 747, 631, 0, - 695, 632, 642, 379, 667, 679, 678, 492, 591, 0, - 690, 693, 621, 783, 0, 687, 701, 788, 700, 780, - 504, 0, 535, 698, 645, 0, 691, 664, 665, 0, - 692, 660, 696, 0, 633, 0, 602, 605, 635, 722, - 723, 724, 346, 604, 726, 727, 728, 729, 730, 731, - 732, 725, 574, 668, 644, 671, 583, 647, 646, 0, - 0, 682, 600, 683, 684, 486, 487, 488, 489, 407, - 710, 370, 603, 519, 520, 0, 669, 0, 0, 0, - 0, 0, 0, 0, 0, 674, 675, 672, 793, 0, - 733, 734, 0, 0, 597, 598, 402, 0, 616, 410, - 369, 503, 404, 581, 434, 0, 609, 676, 610, 522, - 523, 737, 742, 738, 739, 741, 762, 493, 424, 430, - 539, 444, 466, 525, 580, 501, 530, 367, 570, 541, - 472, 661, 689, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 325, 326, 0, 0, 0, 0, + 0, 0, 0, 0, 268, 0, 0, 0, 0, 0, + 0, 366, 269, 628, 755, 630, 629, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 369, 0, 0, 0, + 0, 0, 0, 4642, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 4643, 0, 0, 0, 0, 546, 574, 0, + 588, 0, 434, 435, 0, 0, 0, 0, 0, 0, + 0, 350, 553, 571, 367, 540, 586, 372, 548, 564, + 362, 501, 536, 0, 0, 352, 569, 547, 479, 351, + 0, 529, 392, 409, 389, 499, 0, 0, 568, 599, + 388, 589, 0, 579, 354, 0, 578, 498, 471, 565, + 570, 480, 473, 0, 353, 567, 478, 472, 448, 399, + 615, 449, 450, 451, 452, 453, 454, 413, 514, 469, + 515, 414, 486, 485, 487, 415, 416, 417, 418, 419, + 420, 421, 422, 423, 424, 0, 0, 0, 0, 0, + 610, 611, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 747, 0, 0, + 752, 0, 582, 0, 0, 0, 0, 0, 0, 551, + 0, 0, 455, 0, 0, 0, 600, 0, 532, 504, + 793, 0, 0, 530, 467, 566, 516, 572, 554, 580, + 523, 517, 344, 555, 391, 481, 363, 365, 780, 393, + 396, 400, 401, 492, 493, 510, 539, 558, 559, 560, + 390, 377, 531, 378, 410, 379, 345, 382, 381, 383, + 541, 384, 347, 511, 563, 0, 406, 527, 476, 348, + 475, 512, 562, 561, 364, 590, 597, 598, 689, 0, + 603, 794, 795, 796, 612, 0, 518, 360, 357, 0, + 0, 0, 386, 513, 373, 375, 376, 374, 507, 509, + 617, 618, 619, 621, 0, 622, 623, 0, 0, 0, + 0, 624, 690, 708, 674, 643, 605, 698, 640, 644, + 645, 427, 428, 429, 430, 711, 0, 0, 0, 596, + 456, 457, 0, 398, 397, 477, 349, 0, 0, 445, + 426, 519, 355, 394, 447, 432, 458, 459, 460, 461, + 462, 0, 463, 464, 404, 339, 340, 788, 387, 500, + 713, 750, 751, 635, 0, 699, 636, 646, 380, 671, + 683, 682, 494, 595, 0, 694, 697, 625, 787, 0, + 691, 705, 792, 704, 784, 506, 0, 538, 702, 649, + 0, 695, 668, 669, 0, 696, 664, 700, 356, 433, + 524, 581, 0, 637, 0, 606, 609, 639, 726, 727, + 728, 346, 608, 730, 731, 732, 733, 734, 735, 736, + 729, 577, 672, 648, 675, 587, 651, 650, 0, 0, + 686, 604, 687, 688, 488, 489, 490, 491, 408, 714, + 371, 607, 521, 522, 0, 673, 0, 0, 0, 0, + 0, 0, 0, 0, 678, 679, 676, 797, 0, 737, + 738, 0, 0, 601, 602, 403, 0, 620, 411, 370, + 505, 405, 585, 436, 0, 613, 680, 614, 525, 526, + 741, 746, 742, 743, 745, 766, 495, 425, 431, 542, + 446, 468, 528, 584, 503, 533, 368, 573, 544, 474, + 665, 693, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 325, 326, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 722, + 721, 720, 719, 718, 717, 716, 715, 0, 0, 662, + 385, 785, 779, 786, 767, 770, 769, 744, 0, 341, + 642, 466, 520, 402, 709, 710, 0, 765, 284, 285, + 286, 287, 288, 289, 290, 291, 332, 292, 293, 294, + 295, 296, 297, 298, 303, 304, 305, 306, 307, 308, + 309, 310, 712, 301, 302, 311, 312, 313, 314, 315, + 316, 317, 318, 319, 320, 321, 322, 323, 324, 0, + 0, 0, 0, 333, 771, 772, 773, 774, 775, 0, + 0, 334, 335, 336, 0, 0, 299, 300, 327, 552, + 328, 329, 330, 331, 0, 0, 591, 592, 593, 616, + 0, 594, 575, 641, 412, 342, 556, 583, 782, 783, + 0, 0, 0, 0, 0, 0, 0, 692, 703, 739, + 740, 0, 753, 754, 756, 758, 757, 760, 549, 550, + 768, 0, 0, 437, 358, 359, 496, 497, 535, 0, + 438, 439, 440, 444, 441, 442, 443, 762, 763, 764, + 761, 470, 534, 557, 543, 781, 483, 0, 789, 631, + 632, 790, 749, 748, 343, 0, 337, 338, 706, 707, + 0, 0, 537, 482, 638, 502, 0, 0, 647, 681, + 670, 759, 634, 0, 0, 0, 0, 0, 2625, 0, + 0, 0, 0, 395, 0, 0, 465, 685, 666, 677, + 667, 508, 652, 653, 654, 661, 407, 655, 791, 657, + 626, 658, 627, 659, 660, 0, 684, 633, 545, 484, + 0, 701, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 268, 0, 0, 2626, 0, 0, 0, + 366, 269, 628, 755, 630, 629, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 369, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 546, 574, 0, 588, + 0, 434, 435, 0, 0, 0, 0, 0, 0, 0, + 350, 553, 571, 367, 540, 586, 372, 548, 564, 362, + 501, 536, 0, 0, 352, 569, 547, 479, 351, 0, + 529, 392, 409, 389, 499, 0, 0, 568, 599, 388, + 589, 0, 579, 354, 0, 578, 498, 471, 565, 570, + 480, 473, 0, 353, 567, 478, 472, 448, 399, 615, + 449, 450, 451, 452, 453, 454, 413, 514, 469, 515, + 414, 486, 485, 487, 415, 416, 417, 418, 419, 420, + 421, 422, 423, 424, 0, 0, 0, 0, 0, 610, + 611, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 747, 0, 0, 752, + 0, 582, 0, 0, 0, 0, 0, 0, 551, 0, + 0, 455, 0, 0, 0, 600, 0, 532, 504, 793, + 0, 0, 530, 467, 566, 516, 572, 554, 580, 523, + 517, 344, 555, 391, 481, 363, 365, 780, 393, 396, + 400, 401, 492, 493, 510, 539, 558, 559, 560, 390, + 377, 531, 378, 410, 379, 345, 382, 381, 383, 541, + 384, 347, 511, 563, 0, 406, 527, 476, 348, 475, + 512, 562, 561, 364, 590, 597, 598, 689, 0, 603, + 794, 795, 796, 612, 0, 518, 360, 357, 0, 0, + 0, 386, 513, 373, 375, 376, 374, 507, 509, 617, + 618, 619, 621, 0, 622, 623, 0, 0, 0, 0, + 624, 690, 708, 674, 643, 605, 698, 640, 644, 645, + 427, 428, 429, 430, 711, 0, 0, 0, 596, 456, + 457, 0, 398, 397, 477, 349, 0, 0, 445, 426, + 519, 355, 394, 447, 432, 458, 459, 460, 461, 462, + 0, 463, 464, 404, 339, 340, 788, 387, 500, 713, + 750, 751, 635, 0, 699, 636, 646, 380, 671, 683, + 682, 494, 595, 0, 694, 697, 625, 787, 0, 691, + 705, 792, 704, 784, 506, 0, 538, 702, 649, 0, + 695, 668, 669, 0, 696, 664, 700, 356, 433, 524, + 581, 0, 637, 0, 606, 609, 639, 726, 727, 728, + 346, 608, 730, 731, 732, 733, 734, 735, 736, 729, + 577, 672, 648, 675, 587, 651, 650, 0, 0, 686, + 604, 687, 688, 488, 489, 490, 491, 408, 714, 371, + 607, 521, 522, 0, 673, 0, 0, 0, 0, 0, + 0, 0, 0, 678, 679, 676, 797, 0, 737, 738, + 0, 0, 601, 602, 403, 0, 620, 411, 370, 505, + 405, 585, 436, 0, 613, 680, 614, 525, 526, 741, + 746, 742, 743, 745, 766, 495, 425, 431, 542, 446, + 468, 528, 584, 503, 533, 368, 573, 544, 474, 665, + 693, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 325, 326, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 722, 721, + 720, 719, 718, 717, 716, 715, 0, 0, 662, 385, + 785, 779, 786, 767, 770, 769, 744, 0, 341, 642, + 466, 520, 402, 709, 710, 0, 765, 284, 285, 286, + 287, 288, 289, 290, 291, 332, 292, 293, 294, 295, + 296, 297, 298, 303, 304, 305, 306, 307, 308, 309, + 310, 712, 301, 302, 311, 312, 313, 314, 315, 316, + 317, 318, 319, 320, 321, 322, 323, 324, 0, 0, + 0, 0, 333, 771, 772, 773, 774, 775, 0, 0, + 334, 335, 336, 0, 0, 299, 300, 327, 552, 328, + 329, 330, 331, 0, 0, 591, 592, 593, 616, 0, + 594, 575, 641, 412, 342, 556, 583, 782, 783, 0, + 0, 0, 0, 0, 0, 0, 692, 703, 739, 740, + 0, 753, 754, 756, 758, 757, 760, 549, 550, 768, + 0, 0, 437, 358, 359, 496, 497, 535, 0, 438, + 439, 440, 444, 441, 442, 443, 762, 763, 764, 761, + 470, 534, 557, 543, 781, 483, 0, 789, 631, 632, + 790, 749, 748, 343, 0, 337, 338, 706, 707, 0, + 0, 537, 482, 638, 502, 0, 0, 647, 681, 670, + 759, 634, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 395, 0, 0, 465, 685, 666, 677, 667, + 508, 652, 653, 654, 661, 407, 655, 791, 657, 626, + 658, 627, 659, 660, 0, 684, 633, 545, 484, 0, + 701, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 268, 0, 0, 4231, 4232, 0, 0, 366, + 269, 628, 755, 630, 629, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 369, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 546, 574, 0, 588, 0, + 434, 435, 0, 0, 0, 0, 0, 0, 0, 350, + 553, 571, 367, 540, 586, 372, 548, 564, 362, 501, + 536, 0, 0, 352, 569, 547, 479, 351, 0, 529, + 392, 409, 389, 499, 0, 0, 568, 599, 388, 589, + 0, 579, 354, 0, 578, 498, 471, 565, 570, 480, + 473, 0, 353, 567, 478, 472, 448, 399, 615, 449, + 450, 451, 452, 453, 454, 413, 514, 469, 515, 414, + 486, 485, 487, 415, 416, 417, 418, 419, 420, 421, + 422, 423, 424, 0, 0, 0, 0, 0, 610, 611, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 747, 0, 0, 752, 0, + 582, 0, 0, 0, 0, 0, 0, 551, 0, 0, + 455, 0, 0, 0, 600, 0, 532, 504, 793, 0, + 0, 530, 467, 566, 516, 572, 554, 580, 523, 517, + 344, 555, 391, 481, 363, 365, 780, 393, 396, 400, + 401, 492, 493, 510, 539, 558, 559, 560, 390, 377, + 531, 378, 410, 379, 345, 382, 381, 383, 541, 384, + 347, 511, 563, 0, 406, 527, 476, 348, 475, 512, + 562, 561, 364, 590, 597, 598, 689, 0, 603, 794, + 795, 796, 612, 0, 518, 360, 357, 0, 0, 0, + 386, 513, 373, 375, 376, 374, 507, 509, 617, 618, + 619, 621, 0, 622, 623, 0, 0, 0, 0, 624, + 690, 708, 674, 643, 605, 698, 640, 644, 645, 427, + 428, 429, 430, 711, 0, 0, 0, 596, 456, 457, + 0, 398, 397, 477, 349, 0, 0, 445, 426, 519, + 355, 394, 447, 432, 458, 459, 460, 461, 462, 0, + 463, 464, 404, 339, 340, 788, 387, 500, 713, 750, + 751, 635, 0, 699, 636, 646, 380, 671, 683, 682, + 494, 595, 0, 694, 697, 625, 787, 0, 691, 705, + 792, 704, 784, 506, 0, 538, 702, 649, 0, 695, + 668, 669, 0, 696, 664, 700, 356, 433, 524, 581, + 0, 637, 0, 606, 609, 639, 726, 727, 728, 346, + 608, 730, 731, 732, 733, 734, 735, 736, 729, 577, + 672, 648, 675, 587, 651, 650, 0, 0, 686, 604, + 687, 688, 488, 489, 490, 491, 408, 714, 371, 607, + 521, 522, 0, 673, 0, 0, 0, 0, 0, 0, + 0, 0, 678, 679, 676, 797, 0, 737, 738, 0, + 0, 601, 602, 403, 0, 620, 411, 370, 505, 405, + 585, 436, 0, 613, 680, 614, 525, 526, 741, 746, + 742, 743, 745, 766, 495, 425, 431, 542, 446, 468, + 528, 584, 503, 533, 368, 573, 544, 474, 665, 693, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 718, 717, 716, 715, 714, 713, 712, 711, 0, 0, - 658, 384, 781, 775, 782, 763, 766, 765, 740, 0, - 341, 638, 464, 518, 401, 705, 706, 0, 761, 284, - 285, 286, 287, 288, 289, 290, 291, 332, 292, 293, - 294, 295, 296, 297, 298, 303, 304, 305, 306, 307, - 308, 309, 310, 708, 301, 302, 311, 312, 313, 314, - 315, 316, 317, 318, 319, 320, 321, 322, 323, 324, - 0, 0, 0, 0, 333, 767, 768, 769, 770, 771, - 0, 0, 334, 335, 336, 0, 0, 299, 300, 327, - 549, 328, 329, 330, 331, 0, 0, 587, 588, 589, - 612, 0, 590, 572, 637, 411, 342, 553, 579, 778, - 779, 0, 0, 0, 0, 0, 0, 0, 688, 699, - 735, 736, 0, 749, 750, 752, 754, 753, 756, 546, - 547, 764, 0, 0, 435, 357, 358, 494, 495, 532, - 0, 436, 437, 438, 442, 439, 440, 441, 758, 759, - 760, 757, 468, 531, 554, 540, 777, 481, 0, 785, - 627, 628, 786, 745, 744, 343, 0, 337, 338, 702, - 703, 0, 0, 534, 480, 634, 500, 0, 0, 643, - 677, 666, 755, 630, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 394, 0, 0, 463, 681, 662, - 673, 663, 506, 648, 649, 650, 657, 406, 651, 787, - 653, 622, 654, 623, 655, 656, 0, 680, 629, 542, - 482, 0, 697, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 268, 0, 0, 3992, 3994, 0, - 0, 365, 269, 624, 751, 626, 625, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 368, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 543, 571, 0, - 584, 0, 432, 433, 0, 0, 0, 0, 0, 0, - 0, 350, 550, 568, 366, 537, 582, 371, 545, 561, - 361, 499, 533, 0, 0, 352, 566, 544, 477, 351, - 0, 526, 391, 408, 388, 497, 0, 0, 565, 595, - 387, 585, 0, 576, 354, 0, 575, 496, 469, 562, - 567, 478, 471, 0, 353, 564, 476, 470, 446, 398, - 611, 447, 448, 449, 450, 451, 452, 412, 512, 467, - 513, 413, 484, 483, 485, 414, 415, 416, 417, 418, - 419, 420, 421, 422, 423, 0, 0, 0, 0, 0, - 606, 607, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 743, 0, 0, - 748, 0, 578, 0, 0, 0, 0, 0, 0, 548, - 0, 0, 453, 0, 0, 0, 596, 0, 529, 502, - 789, 0, 0, 527, 465, 563, 514, 569, 551, 577, - 521, 515, 344, 552, 390, 479, 362, 364, 776, 392, - 395, 399, 400, 490, 491, 508, 536, 555, 556, 557, - 389, 376, 528, 377, 409, 378, 345, 381, 380, 382, - 538, 383, 347, 509, 560, 0, 405, 524, 474, 348, - 473, 510, 559, 558, 363, 586, 593, 594, 685, 0, - 599, 790, 791, 792, 608, 0, 516, 359, 356, 0, - 0, 0, 385, 511, 372, 374, 375, 373, 505, 507, - 613, 614, 615, 617, 0, 618, 619, 0, 0, 0, - 0, 620, 686, 704, 670, 639, 601, 694, 636, 640, - 641, 426, 427, 428, 429, 707, 0, 0, 0, 592, - 454, 455, 0, 397, 396, 475, 349, 0, 0, 443, - 425, 517, 355, 393, 445, 431, 456, 457, 458, 459, - 460, 0, 461, 462, 403, 339, 340, 784, 386, 498, - 709, 746, 747, 631, 0, 695, 632, 642, 379, 667, - 679, 678, 492, 591, 0, 690, 693, 621, 783, 0, - 687, 701, 788, 700, 780, 504, 0, 535, 698, 645, - 0, 691, 664, 665, 0, 692, 660, 696, 0, 633, - 0, 602, 605, 635, 722, 723, 724, 346, 604, 726, - 727, 728, 729, 730, 731, 732, 725, 574, 668, 644, - 671, 583, 647, 646, 0, 0, 682, 600, 683, 684, - 486, 487, 488, 489, 407, 710, 370, 603, 519, 520, - 0, 669, 0, 0, 0, 0, 0, 0, 0, 0, - 674, 675, 672, 793, 0, 733, 734, 0, 0, 597, - 598, 402, 0, 616, 410, 369, 503, 404, 581, 434, - 0, 609, 676, 610, 522, 523, 737, 742, 738, 739, - 741, 762, 493, 424, 430, 539, 444, 466, 525, 580, - 501, 530, 367, 570, 541, 472, 661, 689, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 325, - 326, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 718, 717, 716, 715, 714, - 713, 712, 711, 0, 0, 658, 384, 781, 775, 782, - 763, 766, 765, 740, 0, 341, 638, 464, 518, 401, - 705, 706, 0, 761, 284, 285, 286, 287, 288, 289, - 290, 291, 332, 292, 293, 294, 295, 296, 297, 298, - 303, 304, 305, 306, 307, 308, 309, 310, 708, 301, - 302, 311, 312, 313, 314, 315, 316, 317, 318, 319, - 320, 321, 322, 323, 324, 0, 0, 0, 0, 333, - 767, 768, 769, 770, 771, 0, 0, 334, 335, 336, - 0, 0, 299, 300, 327, 549, 328, 329, 330, 331, - 0, 0, 587, 588, 589, 612, 0, 590, 572, 637, - 411, 342, 553, 579, 778, 779, 0, 0, 0, 0, - 0, 0, 0, 688, 699, 735, 736, 0, 749, 750, - 752, 754, 753, 756, 546, 547, 764, 0, 0, 435, - 357, 358, 494, 495, 532, 0, 436, 437, 438, 442, - 439, 440, 441, 758, 759, 760, 757, 468, 531, 554, - 540, 777, 481, 0, 785, 627, 628, 786, 745, 744, - 343, 0, 337, 338, 702, 703, 0, 0, 534, 480, - 634, 500, 0, 0, 643, 677, 666, 755, 630, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 394, - 0, 0, 463, 681, 662, 673, 663, 506, 648, 649, - 650, 657, 406, 651, 787, 653, 622, 654, 623, 655, - 656, 0, 680, 629, 542, 482, 0, 697, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 3622, 0, 0, 268, - 0, 0, 3626, 0, 0, 0, 365, 269, 624, 751, - 626, 625, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 368, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 543, 571, 0, 584, 0, 432, 433, 0, - 0, 0, 0, 0, 0, 0, 350, 550, 568, 366, - 537, 582, 371, 545, 561, 361, 499, 533, 0, 0, - 352, 566, 544, 477, 351, 0, 526, 391, 408, 388, - 497, 0, 0, 565, 595, 387, 585, 0, 576, 354, - 0, 575, 496, 469, 562, 567, 478, 471, 0, 353, - 564, 476, 470, 446, 398, 611, 447, 448, 449, 450, - 451, 452, 412, 512, 467, 513, 413, 484, 483, 485, - 414, 415, 416, 417, 418, 419, 420, 421, 422, 423, - 0, 0, 0, 0, 0, 606, 607, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 743, 0, 0, 748, 0, 578, 0, 0, - 0, 0, 0, 0, 548, 0, 0, 453, 0, 0, - 0, 596, 0, 529, 502, 789, 0, 0, 527, 465, - 563, 514, 569, 551, 577, 521, 515, 344, 552, 390, - 479, 362, 364, 776, 392, 395, 399, 400, 490, 491, - 508, 536, 555, 556, 557, 389, 376, 528, 377, 409, - 378, 345, 381, 380, 382, 538, 383, 347, 509, 560, - 0, 405, 524, 474, 348, 473, 510, 559, 558, 363, - 586, 593, 594, 685, 0, 599, 790, 791, 792, 608, - 0, 516, 359, 356, 0, 0, 0, 385, 511, 372, - 374, 375, 373, 505, 507, 613, 614, 615, 617, 0, - 618, 619, 0, 0, 0, 0, 620, 686, 704, 670, - 639, 601, 694, 636, 640, 641, 426, 427, 428, 429, - 707, 0, 0, 0, 592, 454, 455, 0, 397, 396, - 475, 349, 0, 0, 443, 425, 517, 355, 393, 445, - 431, 456, 457, 458, 459, 460, 0, 461, 462, 403, - 339, 340, 784, 386, 498, 709, 746, 747, 631, 0, - 695, 632, 642, 379, 667, 679, 678, 492, 591, 0, - 690, 693, 621, 783, 0, 687, 701, 788, 700, 780, - 504, 0, 535, 698, 645, 0, 691, 664, 665, 0, - 692, 660, 696, 0, 633, 0, 602, 605, 635, 722, - 723, 724, 346, 604, 726, 727, 728, 729, 730, 731, - 732, 725, 574, 668, 644, 671, 583, 647, 646, 0, - 0, 682, 600, 683, 684, 486, 487, 488, 489, 407, - 710, 370, 603, 519, 520, 0, 669, 0, 0, 0, - 0, 0, 0, 0, 0, 674, 675, 672, 793, 0, - 733, 734, 0, 0, 597, 598, 402, 0, 616, 410, - 369, 503, 404, 581, 434, 0, 609, 676, 610, 522, - 523, 737, 742, 738, 739, 741, 762, 493, 424, 430, - 539, 444, 466, 525, 580, 501, 530, 367, 570, 541, - 472, 661, 689, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 325, 326, 0, 0, 0, 0, + 0, 325, 326, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 722, 721, 720, + 719, 718, 717, 716, 715, 0, 0, 662, 385, 785, + 779, 786, 767, 770, 769, 744, 0, 341, 642, 466, + 520, 402, 709, 710, 0, 765, 284, 285, 286, 287, + 288, 289, 290, 291, 332, 292, 293, 294, 295, 296, + 297, 298, 303, 304, 305, 306, 307, 308, 309, 310, + 712, 301, 302, 311, 312, 313, 314, 315, 316, 317, + 318, 319, 320, 321, 322, 323, 324, 0, 0, 0, + 0, 333, 771, 772, 773, 774, 775, 0, 0, 334, + 335, 336, 0, 0, 299, 300, 327, 552, 328, 329, + 330, 331, 0, 0, 591, 592, 593, 616, 0, 594, + 575, 641, 412, 342, 556, 583, 782, 783, 0, 0, + 0, 0, 0, 0, 0, 692, 703, 739, 740, 0, + 753, 754, 756, 758, 757, 760, 549, 550, 768, 0, + 0, 437, 358, 359, 496, 497, 535, 0, 438, 439, + 440, 444, 441, 442, 443, 762, 763, 764, 761, 470, + 534, 557, 543, 781, 483, 0, 789, 631, 632, 790, + 749, 748, 343, 0, 337, 338, 706, 707, 0, 0, + 537, 482, 638, 502, 0, 0, 647, 681, 670, 759, + 634, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 395, 0, 0, 465, 685, 666, 677, 667, 508, + 652, 653, 654, 661, 407, 655, 791, 657, 626, 658, + 627, 659, 660, 0, 684, 633, 545, 484, 0, 701, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 268, 0, 0, 3996, 3998, 0, 0, 366, 269, + 628, 755, 630, 629, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 369, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 546, 574, 0, 588, 0, 434, + 435, 0, 0, 0, 0, 0, 0, 0, 350, 553, + 571, 367, 540, 586, 372, 548, 564, 362, 501, 536, + 0, 0, 352, 569, 547, 479, 351, 0, 529, 392, + 409, 389, 499, 0, 0, 568, 599, 388, 589, 0, + 579, 354, 0, 578, 498, 471, 565, 570, 480, 473, + 0, 353, 567, 478, 472, 448, 399, 615, 449, 450, + 451, 452, 453, 454, 413, 514, 469, 515, 414, 486, + 485, 487, 415, 416, 417, 418, 419, 420, 421, 422, + 423, 424, 0, 0, 0, 0, 0, 610, 611, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 747, 0, 0, 752, 0, 582, + 0, 0, 0, 0, 0, 0, 551, 0, 0, 455, + 0, 0, 0, 600, 0, 532, 504, 793, 0, 0, + 530, 467, 566, 516, 572, 554, 580, 523, 517, 344, + 555, 391, 481, 363, 365, 780, 393, 396, 400, 401, + 492, 493, 510, 539, 558, 559, 560, 390, 377, 531, + 378, 410, 379, 345, 382, 381, 383, 541, 384, 347, + 511, 563, 0, 406, 527, 476, 348, 475, 512, 562, + 561, 364, 590, 597, 598, 689, 0, 603, 794, 795, + 796, 612, 0, 518, 360, 357, 0, 0, 0, 386, + 513, 373, 375, 376, 374, 507, 509, 617, 618, 619, + 621, 0, 622, 623, 0, 0, 0, 0, 624, 690, + 708, 674, 643, 605, 698, 640, 644, 645, 427, 428, + 429, 430, 711, 0, 0, 0, 596, 456, 457, 0, + 398, 397, 477, 349, 0, 0, 445, 426, 519, 355, + 394, 447, 432, 458, 459, 460, 461, 462, 0, 463, + 464, 404, 339, 340, 788, 387, 500, 713, 750, 751, + 635, 0, 699, 636, 646, 380, 671, 683, 682, 494, + 595, 0, 694, 697, 625, 787, 0, 691, 705, 792, + 704, 784, 506, 0, 538, 702, 649, 0, 695, 668, + 669, 0, 696, 664, 700, 356, 433, 524, 581, 0, + 637, 0, 606, 609, 639, 726, 727, 728, 346, 608, + 730, 731, 732, 733, 734, 735, 736, 729, 577, 672, + 648, 675, 587, 651, 650, 0, 0, 686, 604, 687, + 688, 488, 489, 490, 491, 408, 714, 371, 607, 521, + 522, 0, 673, 0, 0, 0, 0, 0, 0, 0, + 0, 678, 679, 676, 797, 0, 737, 738, 0, 0, + 601, 602, 403, 0, 620, 411, 370, 505, 405, 585, + 436, 0, 613, 680, 614, 525, 526, 741, 746, 742, + 743, 745, 766, 495, 425, 431, 542, 446, 468, 528, + 584, 503, 533, 368, 573, 544, 474, 665, 693, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 718, 717, 716, 715, 714, 713, 712, 711, 0, 0, - 658, 384, 781, 775, 782, 763, 766, 765, 740, 0, - 341, 638, 464, 518, 401, 705, 706, 0, 761, 284, - 285, 286, 287, 288, 289, 290, 291, 332, 292, 293, - 294, 295, 296, 297, 298, 303, 304, 305, 306, 307, - 308, 309, 310, 708, 301, 302, 311, 312, 313, 314, - 315, 316, 317, 318, 319, 320, 321, 322, 323, 324, - 0, 0, 0, 0, 333, 767, 768, 769, 770, 771, - 0, 0, 334, 335, 336, 0, 0, 299, 300, 327, - 549, 328, 329, 330, 331, 0, 0, 587, 588, 589, - 612, 0, 590, 572, 637, 411, 342, 553, 579, 778, - 779, 0, 0, 0, 0, 0, 0, 0, 688, 699, - 735, 736, 0, 749, 750, 752, 754, 753, 756, 546, - 547, 764, 0, 0, 435, 357, 358, 494, 495, 532, - 0, 436, 437, 438, 442, 439, 440, 441, 758, 759, - 760, 757, 468, 531, 554, 540, 777, 481, 0, 785, - 627, 628, 786, 745, 744, 343, 0, 337, 338, 702, - 703, 0, 0, 534, 480, 634, 500, 0, 0, 643, - 677, 666, 755, 630, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 394, 0, 0, 463, 681, 662, - 673, 663, 506, 648, 649, 650, 657, 406, 651, 787, - 653, 622, 654, 623, 655, 656, 0, 680, 629, 542, - 482, 0, 697, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 3616, 0, 0, 268, 0, 0, 3620, 0, 0, - 0, 365, 269, 624, 751, 626, 625, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 368, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 543, 571, 0, - 584, 0, 432, 433, 0, 0, 0, 0, 0, 0, - 0, 350, 550, 568, 366, 537, 582, 371, 545, 561, - 361, 499, 533, 0, 0, 352, 566, 544, 477, 351, - 0, 526, 391, 408, 388, 497, 0, 0, 565, 595, - 387, 585, 0, 576, 354, 0, 575, 496, 469, 562, - 567, 478, 471, 0, 353, 564, 476, 470, 446, 398, - 611, 447, 448, 449, 450, 451, 452, 412, 512, 467, - 513, 413, 484, 483, 485, 414, 415, 416, 417, 418, - 419, 420, 421, 422, 423, 0, 0, 0, 0, 0, - 606, 607, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 743, 0, 0, - 748, 0, 578, 0, 0, 0, 0, 0, 0, 548, - 0, 0, 453, 0, 0, 0, 596, 0, 529, 502, - 789, 0, 0, 527, 465, 563, 514, 569, 551, 577, - 521, 515, 344, 552, 390, 479, 362, 364, 776, 392, - 395, 399, 400, 490, 491, 508, 536, 555, 556, 557, - 389, 376, 528, 377, 409, 378, 345, 381, 380, 382, - 538, 383, 347, 509, 560, 0, 405, 524, 474, 348, - 473, 510, 559, 558, 363, 586, 593, 594, 685, 0, - 599, 790, 791, 792, 608, 0, 516, 359, 356, 0, - 0, 0, 385, 511, 372, 374, 375, 373, 505, 507, - 613, 614, 615, 617, 0, 618, 619, 0, 0, 0, - 0, 620, 686, 704, 670, 639, 601, 694, 636, 640, - 641, 426, 427, 428, 429, 707, 0, 0, 0, 592, - 454, 455, 0, 397, 396, 475, 349, 0, 0, 443, - 425, 517, 355, 393, 445, 431, 456, 457, 458, 459, - 460, 0, 461, 462, 403, 339, 340, 784, 386, 498, - 709, 746, 747, 631, 0, 695, 632, 642, 379, 667, - 679, 678, 492, 591, 0, 690, 693, 621, 783, 0, - 687, 701, 788, 700, 780, 504, 0, 535, 698, 645, - 0, 691, 664, 665, 0, 692, 660, 696, 0, 633, - 0, 602, 605, 635, 722, 723, 724, 346, 604, 726, - 727, 728, 729, 730, 731, 732, 725, 574, 668, 644, - 671, 583, 647, 646, 0, 0, 682, 600, 683, 684, - 486, 487, 488, 489, 407, 710, 370, 603, 519, 520, - 0, 669, 0, 0, 0, 0, 0, 0, 0, 0, - 674, 675, 672, 793, 0, 733, 734, 0, 0, 597, - 598, 402, 0, 616, 410, 369, 503, 404, 581, 434, - 0, 609, 676, 610, 522, 523, 737, 742, 738, 739, - 741, 762, 493, 424, 430, 539, 444, 466, 525, 580, - 501, 530, 367, 570, 541, 472, 661, 689, 0, 0, + 325, 326, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 722, 721, 720, 719, + 718, 717, 716, 715, 0, 0, 662, 385, 785, 779, + 786, 767, 770, 769, 744, 0, 341, 642, 466, 520, + 402, 709, 710, 0, 765, 284, 285, 286, 287, 288, + 289, 290, 291, 332, 292, 293, 294, 295, 296, 297, + 298, 303, 304, 305, 306, 307, 308, 309, 310, 712, + 301, 302, 311, 312, 313, 314, 315, 316, 317, 318, + 319, 320, 321, 322, 323, 324, 0, 0, 0, 0, + 333, 771, 772, 773, 774, 775, 0, 0, 334, 335, + 336, 0, 0, 299, 300, 327, 552, 328, 329, 330, + 331, 0, 0, 591, 592, 593, 616, 0, 594, 575, + 641, 412, 342, 556, 583, 782, 783, 0, 0, 0, + 0, 0, 0, 0, 692, 703, 739, 740, 0, 753, + 754, 756, 758, 757, 760, 549, 550, 768, 0, 0, + 437, 358, 359, 496, 497, 535, 0, 438, 439, 440, + 444, 441, 442, 443, 762, 763, 764, 761, 470, 534, + 557, 543, 781, 483, 0, 789, 631, 632, 790, 749, + 748, 343, 0, 337, 338, 706, 707, 0, 0, 537, + 482, 638, 502, 0, 0, 647, 681, 670, 759, 634, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 395, 0, 0, 465, 685, 666, 677, 667, 508, 652, + 653, 654, 661, 407, 655, 791, 657, 626, 658, 627, + 659, 660, 0, 684, 633, 545, 484, 0, 701, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 3626, 0, 0, + 268, 0, 0, 3630, 0, 0, 0, 366, 269, 628, + 755, 630, 629, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 369, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 546, 574, 0, 588, 0, 434, 435, + 0, 0, 0, 0, 0, 0, 0, 350, 553, 571, + 367, 540, 586, 372, 548, 564, 362, 501, 536, 0, + 0, 352, 569, 547, 479, 351, 0, 529, 392, 409, + 389, 499, 0, 0, 568, 599, 388, 589, 0, 579, + 354, 0, 578, 498, 471, 565, 570, 480, 473, 0, + 353, 567, 478, 472, 448, 399, 615, 449, 450, 451, + 452, 453, 454, 413, 514, 469, 515, 414, 486, 485, + 487, 415, 416, 417, 418, 419, 420, 421, 422, 423, + 424, 0, 0, 0, 0, 0, 610, 611, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 747, 0, 0, 752, 0, 582, 0, + 0, 0, 0, 0, 0, 551, 0, 0, 455, 0, + 0, 0, 600, 0, 532, 504, 793, 0, 0, 530, + 467, 566, 516, 572, 554, 580, 523, 517, 344, 555, + 391, 481, 363, 365, 780, 393, 396, 400, 401, 492, + 493, 510, 539, 558, 559, 560, 390, 377, 531, 378, + 410, 379, 345, 382, 381, 383, 541, 384, 347, 511, + 563, 0, 406, 527, 476, 348, 475, 512, 562, 561, + 364, 590, 597, 598, 689, 0, 603, 794, 795, 796, + 612, 0, 518, 360, 357, 0, 0, 0, 386, 513, + 373, 375, 376, 374, 507, 509, 617, 618, 619, 621, + 0, 622, 623, 0, 0, 0, 0, 624, 690, 708, + 674, 643, 605, 698, 640, 644, 645, 427, 428, 429, + 430, 711, 0, 0, 0, 596, 456, 457, 0, 398, + 397, 477, 349, 0, 0, 445, 426, 519, 355, 394, + 447, 432, 458, 459, 460, 461, 462, 0, 463, 464, + 404, 339, 340, 788, 387, 500, 713, 750, 751, 635, + 0, 699, 636, 646, 380, 671, 683, 682, 494, 595, + 0, 694, 697, 625, 787, 0, 691, 705, 792, 704, + 784, 506, 0, 538, 702, 649, 0, 695, 668, 669, + 0, 696, 664, 700, 356, 433, 524, 581, 0, 637, + 0, 606, 609, 639, 726, 727, 728, 346, 608, 730, + 731, 732, 733, 734, 735, 736, 729, 577, 672, 648, + 675, 587, 651, 650, 0, 0, 686, 604, 687, 688, + 488, 489, 490, 491, 408, 714, 371, 607, 521, 522, + 0, 673, 0, 0, 0, 0, 0, 0, 0, 0, + 678, 679, 676, 797, 0, 737, 738, 0, 0, 601, + 602, 403, 0, 620, 411, 370, 505, 405, 585, 436, + 0, 613, 680, 614, 525, 526, 741, 746, 742, 743, + 745, 766, 495, 425, 431, 542, 446, 468, 528, 584, + 503, 533, 368, 573, 544, 474, 665, 693, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 325, 326, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 718, 717, 716, 715, 714, - 713, 712, 711, 0, 0, 658, 384, 781, 775, 782, - 763, 766, 765, 740, 0, 341, 638, 464, 518, 401, - 705, 706, 0, 761, 284, 285, 286, 287, 288, 289, + 0, 0, 0, 0, 0, 722, 721, 720, 719, 718, + 717, 716, 715, 0, 0, 662, 385, 785, 779, 786, + 767, 770, 769, 744, 0, 341, 642, 466, 520, 402, + 709, 710, 0, 765, 284, 285, 286, 287, 288, 289, 290, 291, 332, 292, 293, 294, 295, 296, 297, 298, - 303, 304, 305, 306, 307, 308, 309, 310, 708, 301, + 303, 304, 305, 306, 307, 308, 309, 310, 712, 301, 302, 311, 312, 313, 314, 315, 316, 317, 318, 319, 320, 321, 322, 323, 324, 0, 0, 0, 0, 333, - 767, 768, 769, 770, 771, 0, 0, 334, 335, 336, - 0, 0, 299, 300, 327, 549, 328, 329, 330, 331, - 0, 0, 587, 588, 589, 612, 0, 590, 572, 637, - 411, 342, 553, 579, 778, 779, 0, 0, 0, 0, - 0, 0, 0, 688, 699, 735, 736, 0, 749, 750, - 752, 754, 753, 756, 546, 547, 764, 0, 0, 435, - 357, 358, 494, 495, 532, 0, 436, 437, 438, 442, - 439, 440, 441, 758, 759, 760, 757, 468, 531, 554, - 540, 777, 481, 0, 785, 627, 628, 786, 745, 744, - 343, 0, 337, 338, 702, 703, 0, 0, 534, 480, - 634, 500, 0, 0, 643, 677, 666, 755, 630, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 394, - 3150, 0, 463, 681, 662, 673, 663, 506, 648, 649, - 650, 657, 406, 651, 787, 653, 622, 654, 623, 655, - 656, 0, 680, 629, 542, 482, 0, 697, 0, 0, + 771, 772, 773, 774, 775, 0, 0, 334, 335, 336, + 0, 0, 299, 300, 327, 552, 328, 329, 330, 331, + 0, 0, 591, 592, 593, 616, 0, 594, 575, 641, + 412, 342, 556, 583, 782, 783, 0, 0, 0, 0, + 0, 0, 0, 692, 703, 739, 740, 0, 753, 754, + 756, 758, 757, 760, 549, 550, 768, 0, 0, 437, + 358, 359, 496, 497, 535, 0, 438, 439, 440, 444, + 441, 442, 443, 762, 763, 764, 761, 470, 534, 557, + 543, 781, 483, 0, 789, 631, 632, 790, 749, 748, + 343, 0, 337, 338, 706, 707, 0, 0, 537, 482, + 638, 502, 0, 0, 647, 681, 670, 759, 634, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 395, + 0, 0, 465, 685, 666, 677, 667, 508, 652, 653, + 654, 661, 407, 655, 791, 657, 626, 658, 627, 659, + 660, 0, 684, 633, 545, 484, 0, 701, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 3620, 0, 0, 268, + 0, 0, 3624, 0, 0, 0, 366, 269, 628, 755, + 630, 629, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 369, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 546, 574, 0, 588, 0, 434, 435, 0, + 0, 0, 0, 0, 0, 0, 350, 553, 571, 367, + 540, 586, 372, 548, 564, 362, 501, 536, 0, 0, + 352, 569, 547, 479, 351, 0, 529, 392, 409, 389, + 499, 0, 0, 568, 599, 388, 589, 0, 579, 354, + 0, 578, 498, 471, 565, 570, 480, 473, 0, 353, + 567, 478, 472, 448, 399, 615, 449, 450, 451, 452, + 453, 454, 413, 514, 469, 515, 414, 486, 485, 487, + 415, 416, 417, 418, 419, 420, 421, 422, 423, 424, + 0, 0, 0, 0, 0, 610, 611, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 747, 0, 0, 752, 0, 582, 0, 0, + 0, 0, 0, 0, 551, 0, 0, 455, 0, 0, + 0, 600, 0, 532, 504, 793, 0, 0, 530, 467, + 566, 516, 572, 554, 580, 523, 517, 344, 555, 391, + 481, 363, 365, 780, 393, 396, 400, 401, 492, 493, + 510, 539, 558, 559, 560, 390, 377, 531, 378, 410, + 379, 345, 382, 381, 383, 541, 384, 347, 511, 563, + 0, 406, 527, 476, 348, 475, 512, 562, 561, 364, + 590, 597, 598, 689, 0, 603, 794, 795, 796, 612, + 0, 518, 360, 357, 0, 0, 0, 386, 513, 373, + 375, 376, 374, 507, 509, 617, 618, 619, 621, 0, + 622, 623, 0, 0, 0, 0, 624, 690, 708, 674, + 643, 605, 698, 640, 644, 645, 427, 428, 429, 430, + 711, 0, 0, 0, 596, 456, 457, 0, 398, 397, + 477, 349, 0, 0, 445, 426, 519, 355, 394, 447, + 432, 458, 459, 460, 461, 462, 0, 463, 464, 404, + 339, 340, 788, 387, 500, 713, 750, 751, 635, 0, + 699, 636, 646, 380, 671, 683, 682, 494, 595, 0, + 694, 697, 625, 787, 0, 691, 705, 792, 704, 784, + 506, 0, 538, 702, 649, 0, 695, 668, 669, 0, + 696, 664, 700, 356, 433, 524, 581, 0, 637, 0, + 606, 609, 639, 726, 727, 728, 346, 608, 730, 731, + 732, 733, 734, 735, 736, 729, 577, 672, 648, 675, + 587, 651, 650, 0, 0, 686, 604, 687, 688, 488, + 489, 490, 491, 408, 714, 371, 607, 521, 522, 0, + 673, 0, 0, 0, 0, 0, 0, 0, 0, 678, + 679, 676, 797, 0, 737, 738, 0, 0, 601, 602, + 403, 0, 620, 411, 370, 505, 405, 585, 436, 0, + 613, 680, 614, 525, 526, 741, 746, 742, 743, 745, + 766, 495, 425, 431, 542, 446, 468, 528, 584, 503, + 533, 368, 573, 544, 474, 665, 693, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 325, 326, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 268, - 0, 0, 1866, 0, 0, 0, 365, 269, 624, 751, - 626, 625, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 368, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 543, 571, 0, 584, 0, 432, 433, 0, - 0, 0, 0, 0, 0, 0, 350, 550, 568, 366, - 537, 582, 371, 545, 561, 361, 499, 533, 0, 0, - 352, 566, 544, 477, 351, 0, 526, 391, 408, 388, - 497, 0, 0, 565, 595, 387, 585, 0, 576, 354, - 0, 575, 496, 469, 562, 567, 478, 471, 0, 353, - 564, 476, 470, 446, 398, 611, 447, 448, 449, 450, - 451, 452, 412, 512, 467, 513, 413, 484, 483, 485, - 414, 415, 416, 417, 418, 419, 420, 421, 422, 423, - 0, 0, 0, 0, 0, 606, 607, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 743, 0, 0, 748, 0, 578, 0, 0, - 0, 0, 0, 0, 548, 0, 0, 453, 0, 0, - 0, 596, 0, 529, 502, 789, 0, 0, 527, 465, - 563, 514, 569, 551, 577, 521, 515, 344, 552, 390, - 479, 362, 364, 776, 392, 395, 399, 400, 490, 491, - 508, 536, 555, 556, 557, 389, 376, 528, 377, 409, - 378, 345, 381, 380, 382, 538, 383, 347, 509, 560, - 0, 405, 524, 474, 348, 473, 510, 559, 558, 363, - 586, 593, 594, 685, 0, 599, 790, 791, 792, 608, - 0, 516, 359, 356, 0, 0, 0, 385, 511, 372, - 374, 375, 373, 505, 507, 613, 614, 615, 617, 0, - 618, 619, 0, 0, 0, 0, 620, 686, 704, 670, - 639, 601, 694, 636, 640, 641, 426, 427, 428, 429, - 707, 0, 0, 0, 592, 454, 455, 0, 397, 396, - 475, 349, 0, 0, 443, 425, 517, 355, 393, 445, - 431, 456, 457, 458, 459, 460, 0, 461, 462, 403, - 339, 340, 784, 386, 498, 709, 746, 747, 631, 0, - 695, 632, 642, 379, 667, 679, 678, 492, 591, 0, - 690, 693, 621, 783, 0, 687, 701, 788, 700, 780, - 504, 0, 535, 698, 645, 0, 691, 664, 665, 0, - 692, 660, 696, 0, 633, 0, 602, 605, 635, 722, - 723, 724, 346, 604, 726, 727, 728, 729, 730, 731, - 732, 725, 574, 668, 644, 671, 583, 647, 646, 0, - 0, 682, 600, 683, 684, 486, 487, 488, 489, 407, - 710, 370, 603, 519, 520, 0, 669, 0, 0, 0, - 0, 0, 0, 0, 0, 674, 675, 672, 793, 0, - 733, 734, 0, 0, 597, 598, 402, 0, 616, 410, - 369, 503, 404, 581, 434, 0, 609, 676, 610, 522, - 523, 737, 742, 738, 739, 741, 762, 493, 424, 430, - 539, 444, 466, 525, 580, 501, 530, 367, 570, 541, - 472, 661, 689, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 325, 326, 0, 0, 0, 0, + 0, 0, 0, 0, 722, 721, 720, 719, 718, 717, + 716, 715, 0, 0, 662, 385, 785, 779, 786, 767, + 770, 769, 744, 0, 341, 642, 466, 520, 402, 709, + 710, 0, 765, 284, 285, 286, 287, 288, 289, 290, + 291, 332, 292, 293, 294, 295, 296, 297, 298, 303, + 304, 305, 306, 307, 308, 309, 310, 712, 301, 302, + 311, 312, 313, 314, 315, 316, 317, 318, 319, 320, + 321, 322, 323, 324, 0, 0, 0, 0, 333, 771, + 772, 773, 774, 775, 0, 0, 334, 335, 336, 0, + 0, 299, 300, 327, 552, 328, 329, 330, 331, 0, + 0, 591, 592, 593, 616, 0, 594, 575, 641, 412, + 342, 556, 583, 782, 783, 0, 0, 0, 0, 0, + 0, 0, 692, 703, 739, 740, 0, 753, 754, 756, + 758, 757, 760, 549, 550, 768, 0, 0, 437, 358, + 359, 496, 497, 535, 0, 438, 439, 440, 444, 441, + 442, 443, 762, 763, 764, 761, 470, 534, 557, 543, + 781, 483, 0, 789, 631, 632, 790, 749, 748, 343, + 0, 337, 338, 706, 707, 0, 0, 537, 482, 638, + 502, 0, 0, 647, 681, 670, 759, 634, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 395, 3154, + 0, 465, 685, 666, 677, 667, 508, 652, 653, 654, + 661, 407, 655, 791, 657, 626, 658, 627, 659, 660, + 0, 684, 633, 545, 484, 0, 701, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 718, 717, 716, 715, 714, 713, 712, 711, 0, 0, - 658, 384, 781, 775, 782, 763, 766, 765, 740, 0, - 341, 638, 464, 518, 401, 705, 706, 0, 761, 284, - 285, 286, 287, 288, 289, 290, 291, 332, 292, 293, - 294, 295, 296, 297, 298, 303, 304, 305, 306, 307, - 308, 309, 310, 708, 301, 302, 311, 312, 313, 314, - 315, 316, 317, 318, 319, 320, 321, 322, 323, 324, - 0, 0, 0, 0, 333, 767, 768, 769, 770, 771, - 0, 0, 334, 335, 336, 0, 0, 299, 300, 327, - 549, 328, 329, 330, 331, 0, 0, 587, 588, 589, - 612, 0, 590, 572, 637, 411, 342, 553, 579, 778, - 779, 0, 0, 0, 0, 0, 0, 0, 688, 699, - 735, 736, 0, 749, 750, 752, 754, 753, 756, 546, - 547, 764, 0, 0, 435, 357, 358, 494, 495, 532, - 0, 436, 437, 438, 442, 439, 440, 441, 758, 759, - 760, 757, 468, 531, 554, 540, 777, 481, 0, 785, - 627, 628, 786, 745, 744, 343, 0, 337, 338, 702, - 703, 0, 0, 534, 480, 634, 500, 0, 0, 643, - 677, 666, 755, 630, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 394, 0, 0, 463, 681, 662, - 673, 663, 506, 648, 649, 650, 657, 406, 651, 787, - 653, 622, 654, 623, 655, 656, 0, 680, 629, 542, - 482, 0, 697, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 268, 0, 0, 5305, 0, 0, - 0, 365, 269, 624, 751, 626, 625, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 368, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 543, 571, 0, - 584, 0, 432, 433, 0, 0, 0, 0, 0, 0, - 0, 350, 550, 568, 366, 537, 582, 371, 545, 561, - 361, 499, 533, 0, 0, 352, 566, 544, 477, 351, - 0, 526, 391, 408, 388, 497, 0, 0, 565, 595, - 387, 585, 0, 576, 354, 0, 575, 496, 469, 562, - 567, 478, 471, 0, 353, 564, 476, 470, 446, 398, - 611, 447, 448, 449, 450, 451, 452, 412, 512, 467, - 513, 413, 484, 483, 485, 414, 415, 416, 417, 418, - 419, 420, 421, 422, 423, 0, 0, 0, 0, 0, - 606, 607, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 743, 0, 0, - 748, 0, 578, 0, 0, 0, 0, 0, 0, 548, - 0, 0, 453, 0, 0, 0, 596, 0, 529, 502, - 789, 0, 0, 527, 465, 563, 514, 569, 551, 577, - 521, 515, 344, 552, 390, 479, 362, 364, 776, 392, - 395, 399, 400, 490, 491, 508, 536, 555, 556, 557, - 389, 376, 528, 377, 409, 378, 345, 381, 380, 382, - 538, 383, 347, 509, 560, 0, 405, 524, 474, 348, - 473, 510, 559, 558, 363, 586, 593, 594, 685, 0, - 599, 790, 791, 792, 608, 0, 516, 359, 356, 0, - 0, 0, 385, 511, 372, 374, 375, 373, 505, 507, - 613, 614, 615, 617, 0, 618, 619, 0, 0, 0, - 0, 620, 686, 704, 670, 639, 601, 694, 636, 640, - 641, 426, 427, 428, 429, 707, 0, 0, 0, 592, - 454, 455, 0, 397, 396, 475, 349, 0, 0, 443, - 425, 517, 355, 393, 445, 431, 456, 457, 458, 459, - 460, 0, 461, 462, 403, 339, 340, 784, 386, 498, - 709, 746, 747, 631, 0, 695, 632, 642, 379, 667, - 679, 678, 492, 591, 0, 690, 693, 621, 783, 0, - 687, 701, 788, 700, 780, 504, 0, 535, 698, 645, - 0, 691, 664, 665, 0, 692, 660, 696, 0, 633, - 0, 602, 605, 635, 722, 723, 724, 346, 604, 726, - 727, 728, 729, 730, 731, 732, 725, 574, 668, 644, - 671, 583, 647, 646, 0, 0, 682, 600, 683, 684, - 486, 487, 488, 489, 407, 710, 370, 603, 519, 520, - 0, 669, 0, 0, 0, 0, 0, 0, 0, 0, - 674, 675, 672, 793, 0, 733, 734, 0, 0, 597, - 598, 402, 0, 616, 410, 369, 503, 404, 581, 434, - 0, 609, 676, 610, 522, 523, 737, 742, 738, 739, - 741, 762, 493, 424, 430, 539, 444, 466, 525, 580, - 501, 530, 367, 570, 541, 472, 661, 689, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 325, - 326, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 718, 717, 716, 715, 714, - 713, 712, 711, 0, 0, 658, 384, 781, 775, 782, - 763, 766, 765, 740, 0, 341, 638, 464, 518, 401, - 705, 706, 0, 761, 284, 285, 286, 287, 288, 289, - 290, 291, 332, 292, 293, 294, 295, 296, 297, 298, - 303, 304, 305, 306, 307, 308, 309, 310, 708, 301, - 302, 311, 312, 313, 314, 315, 316, 317, 318, 319, - 320, 321, 322, 323, 324, 0, 0, 0, 0, 333, - 767, 768, 769, 770, 771, 0, 0, 334, 335, 336, - 0, 0, 299, 300, 327, 549, 328, 329, 330, 331, - 0, 0, 587, 588, 589, 612, 0, 590, 572, 637, - 411, 342, 553, 579, 778, 779, 0, 0, 0, 0, - 0, 0, 0, 688, 699, 735, 736, 0, 749, 750, - 752, 754, 753, 756, 546, 547, 764, 0, 0, 435, - 357, 358, 494, 495, 532, 0, 436, 437, 438, 442, - 439, 440, 441, 758, 759, 760, 757, 468, 531, 554, - 540, 777, 481, 0, 785, 627, 628, 786, 745, 744, - 343, 0, 337, 338, 702, 703, 0, 0, 534, 480, - 634, 500, 0, 0, 643, 677, 666, 755, 630, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 394, - 0, 0, 463, 681, 662, 673, 663, 506, 648, 649, - 650, 657, 406, 651, 787, 653, 622, 654, 623, 655, - 656, 0, 680, 629, 542, 482, 0, 697, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 268, 0, + 0, 1870, 0, 0, 0, 366, 269, 628, 755, 630, + 629, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 369, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 546, 574, 0, 588, 0, 434, 435, 0, 0, + 0, 0, 0, 0, 0, 350, 553, 571, 367, 540, + 586, 372, 548, 564, 362, 501, 536, 0, 0, 352, + 569, 547, 479, 351, 0, 529, 392, 409, 389, 499, + 0, 0, 568, 599, 388, 589, 0, 579, 354, 0, + 578, 498, 471, 565, 570, 480, 473, 0, 353, 567, + 478, 472, 448, 399, 615, 449, 450, 451, 452, 453, + 454, 413, 514, 469, 515, 414, 486, 485, 487, 415, + 416, 417, 418, 419, 420, 421, 422, 423, 424, 0, + 0, 0, 0, 0, 610, 611, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 747, 0, 0, 752, 0, 582, 0, 0, 0, + 0, 0, 0, 551, 0, 0, 455, 0, 0, 0, + 600, 0, 532, 504, 793, 0, 0, 530, 467, 566, + 516, 572, 554, 580, 523, 517, 344, 555, 391, 481, + 363, 365, 780, 393, 396, 400, 401, 492, 493, 510, + 539, 558, 559, 560, 390, 377, 531, 378, 410, 379, + 345, 382, 381, 383, 541, 384, 347, 511, 563, 0, + 406, 527, 476, 348, 475, 512, 562, 561, 364, 590, + 597, 598, 689, 0, 603, 794, 795, 796, 612, 0, + 518, 360, 357, 0, 0, 0, 386, 513, 373, 375, + 376, 374, 507, 509, 617, 618, 619, 621, 0, 622, + 623, 0, 0, 0, 0, 624, 690, 708, 674, 643, + 605, 698, 640, 644, 645, 427, 428, 429, 430, 711, + 0, 0, 0, 596, 456, 457, 0, 398, 397, 477, + 349, 0, 0, 445, 426, 519, 355, 394, 447, 432, + 458, 459, 460, 461, 462, 0, 463, 464, 404, 339, + 340, 788, 387, 500, 713, 750, 751, 635, 0, 699, + 636, 646, 380, 671, 683, 682, 494, 595, 0, 694, + 697, 625, 787, 0, 691, 705, 792, 704, 784, 506, + 0, 538, 702, 649, 0, 695, 668, 669, 0, 696, + 664, 700, 356, 433, 524, 581, 0, 637, 0, 606, + 609, 639, 726, 727, 728, 346, 608, 730, 731, 732, + 733, 734, 735, 736, 729, 577, 672, 648, 675, 587, + 651, 650, 0, 0, 686, 604, 687, 688, 488, 489, + 490, 491, 408, 714, 371, 607, 521, 522, 0, 673, + 0, 0, 0, 0, 0, 0, 0, 0, 678, 679, + 676, 797, 0, 737, 738, 0, 0, 601, 602, 403, + 0, 620, 411, 370, 505, 405, 585, 436, 0, 613, + 680, 614, 525, 526, 741, 746, 742, 743, 745, 766, + 495, 425, 431, 542, 446, 468, 528, 584, 503, 533, + 368, 573, 544, 474, 665, 693, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 325, 326, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 268, - 0, 0, 5247, 0, 0, 0, 365, 269, 624, 751, - 626, 625, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 368, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 543, 571, 0, 584, 0, 432, 433, 0, - 0, 0, 0, 0, 0, 0, 350, 550, 568, 366, - 537, 582, 371, 545, 561, 361, 499, 533, 0, 0, - 352, 566, 544, 477, 351, 0, 526, 391, 408, 388, - 497, 0, 0, 565, 595, 387, 585, 0, 576, 354, - 0, 575, 496, 469, 562, 567, 478, 471, 0, 353, - 564, 476, 470, 446, 398, 611, 447, 448, 449, 450, - 451, 452, 412, 512, 467, 513, 413, 484, 483, 485, - 414, 415, 416, 417, 418, 419, 420, 421, 422, 423, - 0, 0, 0, 0, 0, 606, 607, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 743, 0, 0, 748, 0, 578, 0, 0, - 0, 0, 0, 0, 548, 0, 0, 453, 0, 0, - 0, 596, 0, 529, 502, 789, 0, 0, 527, 465, - 563, 514, 569, 551, 577, 521, 515, 344, 552, 390, - 479, 362, 364, 776, 392, 395, 399, 400, 490, 491, - 508, 536, 555, 556, 557, 389, 376, 528, 377, 409, - 378, 345, 381, 380, 382, 538, 383, 347, 509, 560, - 0, 405, 524, 474, 348, 473, 510, 559, 558, 363, - 586, 593, 594, 685, 0, 599, 790, 791, 792, 608, - 0, 516, 359, 356, 0, 0, 0, 385, 511, 372, - 374, 375, 373, 505, 507, 613, 614, 615, 617, 0, - 618, 619, 0, 0, 0, 0, 620, 686, 704, 670, - 639, 601, 694, 636, 640, 641, 426, 427, 428, 429, - 707, 0, 0, 0, 592, 454, 455, 0, 397, 396, - 475, 349, 0, 0, 443, 425, 517, 355, 393, 445, - 431, 456, 457, 458, 459, 460, 0, 461, 462, 403, - 339, 340, 784, 386, 498, 709, 746, 747, 631, 0, - 695, 632, 642, 379, 667, 679, 678, 492, 591, 0, - 690, 693, 621, 783, 0, 687, 701, 788, 700, 780, - 504, 0, 535, 698, 645, 0, 691, 664, 665, 0, - 692, 660, 696, 0, 633, 0, 602, 605, 635, 722, - 723, 724, 346, 604, 726, 727, 728, 729, 730, 731, - 732, 725, 574, 668, 644, 671, 583, 647, 646, 0, - 0, 682, 600, 683, 684, 486, 487, 488, 489, 407, - 710, 370, 603, 519, 520, 0, 669, 0, 0, 0, - 0, 0, 0, 0, 0, 674, 675, 672, 793, 0, - 733, 734, 0, 0, 597, 598, 402, 0, 616, 410, - 369, 503, 404, 581, 434, 0, 609, 676, 610, 522, - 523, 737, 742, 738, 739, 741, 762, 493, 424, 430, - 539, 444, 466, 525, 580, 501, 530, 367, 570, 541, - 472, 661, 689, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 325, 326, 0, 0, 0, 0, + 0, 0, 0, 722, 721, 720, 719, 718, 717, 716, + 715, 0, 0, 662, 385, 785, 779, 786, 767, 770, + 769, 744, 0, 341, 642, 466, 520, 402, 709, 710, + 0, 765, 284, 285, 286, 287, 288, 289, 290, 291, + 332, 292, 293, 294, 295, 296, 297, 298, 303, 304, + 305, 306, 307, 308, 309, 310, 712, 301, 302, 311, + 312, 313, 314, 315, 316, 317, 318, 319, 320, 321, + 322, 323, 324, 0, 0, 0, 0, 333, 771, 772, + 773, 774, 775, 0, 0, 334, 335, 336, 0, 0, + 299, 300, 327, 552, 328, 329, 330, 331, 0, 0, + 591, 592, 593, 616, 0, 594, 575, 641, 412, 342, + 556, 583, 782, 783, 0, 0, 0, 0, 0, 0, + 0, 692, 703, 739, 740, 0, 753, 754, 756, 758, + 757, 760, 549, 550, 768, 0, 0, 437, 358, 359, + 496, 497, 535, 0, 438, 439, 440, 444, 441, 442, + 443, 762, 763, 764, 761, 470, 534, 557, 543, 781, + 483, 0, 789, 631, 632, 790, 749, 748, 343, 0, + 337, 338, 706, 707, 0, 0, 537, 482, 638, 502, + 0, 0, 647, 681, 670, 759, 634, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 395, 0, 0, + 465, 685, 666, 677, 667, 508, 652, 653, 654, 661, + 407, 655, 791, 657, 626, 658, 627, 659, 660, 0, + 684, 633, 545, 484, 0, 701, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 268, 0, 0, + 5320, 0, 0, 0, 366, 269, 628, 755, 630, 629, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 369, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 546, 574, 0, 588, 0, 434, 435, 0, 0, 0, + 0, 0, 0, 0, 350, 553, 571, 367, 540, 586, + 372, 548, 564, 362, 501, 536, 0, 0, 352, 569, + 547, 479, 351, 0, 529, 392, 409, 389, 499, 0, + 0, 568, 599, 388, 589, 0, 579, 354, 0, 578, + 498, 471, 565, 570, 480, 473, 0, 353, 567, 478, + 472, 448, 399, 615, 449, 450, 451, 452, 453, 454, + 413, 514, 469, 515, 414, 486, 485, 487, 415, 416, + 417, 418, 419, 420, 421, 422, 423, 424, 0, 0, + 0, 0, 0, 610, 611, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 747, 0, 0, 752, 0, 582, 0, 0, 0, 0, + 0, 0, 551, 0, 0, 455, 0, 0, 0, 600, + 0, 532, 504, 793, 0, 0, 530, 467, 566, 516, + 572, 554, 580, 523, 517, 344, 555, 391, 481, 363, + 365, 780, 393, 396, 400, 401, 492, 493, 510, 539, + 558, 559, 560, 390, 377, 531, 378, 410, 379, 345, + 382, 381, 383, 541, 384, 347, 511, 563, 0, 406, + 527, 476, 348, 475, 512, 562, 561, 364, 590, 597, + 598, 689, 0, 603, 794, 795, 796, 612, 0, 518, + 360, 357, 0, 0, 0, 386, 513, 373, 375, 376, + 374, 507, 509, 617, 618, 619, 621, 0, 622, 623, + 0, 0, 0, 0, 624, 690, 708, 674, 643, 605, + 698, 640, 644, 645, 427, 428, 429, 430, 711, 0, + 0, 0, 596, 456, 457, 0, 398, 397, 477, 349, + 0, 0, 445, 426, 519, 355, 394, 447, 432, 458, + 459, 460, 461, 462, 0, 463, 464, 404, 339, 340, + 788, 387, 500, 713, 750, 751, 635, 0, 699, 636, + 646, 380, 671, 683, 682, 494, 595, 0, 694, 697, + 625, 787, 0, 691, 705, 792, 704, 784, 506, 0, + 538, 702, 649, 0, 695, 668, 669, 0, 696, 664, + 700, 356, 433, 524, 581, 0, 637, 0, 606, 609, + 639, 726, 727, 728, 346, 608, 730, 731, 732, 733, + 734, 735, 736, 729, 577, 672, 648, 675, 587, 651, + 650, 0, 0, 686, 604, 687, 688, 488, 489, 490, + 491, 408, 714, 371, 607, 521, 522, 0, 673, 0, + 0, 0, 0, 0, 0, 0, 0, 678, 679, 676, + 797, 0, 737, 738, 0, 0, 601, 602, 403, 0, + 620, 411, 370, 505, 405, 585, 436, 0, 613, 680, + 614, 525, 526, 741, 746, 742, 743, 745, 766, 495, + 425, 431, 542, 446, 468, 528, 584, 503, 533, 368, + 573, 544, 474, 665, 693, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 325, 326, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 718, 717, 716, 715, 714, 713, 712, 711, 0, 0, - 658, 384, 781, 775, 782, 763, 766, 765, 740, 0, - 341, 638, 464, 518, 401, 705, 706, 0, 761, 284, - 285, 286, 287, 288, 289, 290, 291, 332, 292, 293, - 294, 295, 296, 297, 298, 303, 304, 305, 306, 307, - 308, 309, 310, 708, 301, 302, 311, 312, 313, 314, - 315, 316, 317, 318, 319, 320, 321, 322, 323, 324, - 0, 0, 0, 0, 333, 767, 768, 769, 770, 771, - 0, 0, 334, 335, 336, 0, 0, 299, 300, 327, - 549, 328, 329, 330, 331, 0, 0, 587, 588, 589, - 612, 0, 590, 572, 637, 411, 342, 553, 579, 778, - 779, 0, 0, 0, 0, 0, 0, 0, 688, 699, - 735, 736, 0, 749, 750, 752, 754, 753, 756, 546, - 547, 764, 0, 0, 435, 357, 358, 494, 495, 532, - 0, 436, 437, 438, 442, 439, 440, 441, 758, 759, - 760, 757, 468, 531, 554, 540, 777, 481, 0, 785, - 627, 628, 786, 745, 744, 343, 0, 337, 338, 702, - 703, 0, 0, 534, 480, 634, 500, 0, 0, 643, - 677, 666, 755, 630, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 394, 0, 0, 463, 681, 662, - 673, 663, 506, 648, 649, 650, 657, 406, 651, 787, - 653, 622, 654, 623, 655, 656, 0, 680, 629, 542, - 482, 0, 697, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 268, 1069, 0, 0, 0, 0, - 0, 365, 269, 624, 751, 626, 625, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 368, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 543, 571, 0, - 584, 0, 432, 433, 0, 0, 0, 0, 0, 0, - 0, 350, 550, 568, 366, 537, 582, 371, 545, 561, - 361, 499, 533, 0, 0, 352, 566, 544, 477, 351, - 0, 526, 391, 408, 388, 497, 0, 0, 565, 595, - 387, 585, 0, 576, 354, 0, 575, 496, 469, 562, - 567, 478, 471, 0, 353, 564, 476, 470, 446, 398, - 611, 447, 448, 449, 450, 451, 452, 412, 512, 467, - 513, 413, 484, 483, 485, 414, 415, 416, 417, 418, - 419, 420, 421, 422, 423, 0, 0, 0, 0, 0, - 606, 607, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 743, 0, 0, - 748, 0, 578, 0, 0, 0, 0, 0, 0, 548, - 0, 0, 453, 0, 0, 0, 596, 0, 529, 502, - 789, 0, 0, 527, 465, 563, 514, 569, 551, 577, - 521, 515, 344, 552, 390, 479, 362, 364, 776, 392, - 395, 399, 400, 490, 491, 508, 536, 555, 556, 557, - 389, 376, 528, 377, 409, 378, 345, 381, 380, 382, - 538, 383, 347, 509, 560, 0, 405, 524, 474, 348, - 473, 510, 559, 558, 363, 586, 593, 594, 685, 0, - 599, 790, 791, 792, 608, 0, 516, 359, 356, 0, - 0, 0, 385, 511, 372, 374, 375, 373, 505, 507, - 613, 614, 615, 617, 0, 618, 619, 0, 0, 0, - 0, 620, 686, 704, 670, 639, 601, 694, 636, 640, - 641, 426, 427, 428, 429, 707, 0, 0, 0, 592, - 454, 455, 0, 397, 396, 475, 349, 0, 0, 443, - 425, 517, 355, 393, 445, 431, 456, 457, 458, 459, - 460, 0, 461, 462, 403, 339, 340, 784, 386, 498, - 709, 746, 747, 631, 0, 695, 632, 642, 379, 667, - 679, 678, 492, 591, 0, 690, 693, 621, 783, 0, - 687, 701, 788, 700, 780, 504, 0, 535, 698, 645, - 0, 691, 664, 665, 0, 692, 660, 696, 0, 633, - 0, 602, 605, 635, 722, 723, 724, 346, 604, 726, - 727, 728, 729, 730, 731, 732, 725, 574, 668, 644, - 671, 583, 647, 646, 0, 0, 682, 600, 683, 684, - 486, 487, 488, 489, 407, 710, 370, 603, 519, 520, - 0, 669, 0, 0, 0, 0, 0, 0, 0, 0, - 674, 675, 672, 793, 0, 733, 734, 0, 0, 597, - 598, 402, 0, 616, 410, 369, 503, 404, 581, 434, - 0, 609, 676, 610, 522, 523, 737, 742, 738, 739, - 741, 762, 493, 424, 430, 539, 444, 466, 525, 580, - 501, 530, 367, 570, 541, 472, 661, 689, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 325, - 326, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 718, 717, 716, 715, 714, - 713, 712, 711, 0, 0, 658, 384, 781, 775, 782, - 763, 766, 765, 740, 0, 341, 638, 464, 518, 401, - 705, 706, 0, 761, 284, 285, 286, 287, 288, 289, - 290, 291, 332, 292, 293, 294, 295, 296, 297, 298, - 303, 304, 305, 306, 307, 308, 309, 310, 708, 301, - 302, 311, 312, 313, 314, 315, 316, 317, 318, 319, - 320, 321, 322, 323, 324, 0, 0, 0, 0, 333, - 767, 768, 769, 770, 771, 0, 0, 334, 335, 336, - 0, 0, 299, 300, 327, 549, 328, 329, 330, 331, - 0, 0, 587, 588, 589, 612, 0, 590, 572, 637, - 411, 342, 553, 579, 778, 779, 0, 0, 0, 0, - 0, 0, 0, 688, 699, 735, 736, 0, 749, 750, - 752, 754, 753, 756, 546, 547, 764, 0, 0, 435, - 357, 358, 494, 495, 532, 0, 436, 437, 438, 442, - 439, 440, 441, 758, 759, 760, 757, 468, 531, 554, - 540, 777, 481, 0, 785, 627, 628, 786, 745, 744, - 343, 0, 337, 338, 702, 703, 0, 0, 534, 480, - 634, 500, 0, 0, 643, 677, 666, 755, 630, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 394, - 0, 0, 463, 681, 662, 673, 663, 506, 648, 649, - 650, 657, 406, 651, 787, 653, 622, 654, 623, 655, - 656, 0, 680, 629, 542, 482, 0, 697, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 5194, 0, 0, 268, - 0, 0, 0, 0, 0, 0, 365, 269, 624, 751, - 626, 625, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 368, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 543, 571, 0, 584, 0, 432, 433, 0, - 0, 0, 0, 0, 0, 0, 350, 550, 568, 366, - 537, 582, 371, 545, 561, 361, 499, 533, 0, 0, - 352, 566, 544, 477, 351, 0, 526, 391, 408, 388, - 497, 0, 0, 565, 595, 387, 585, 0, 576, 354, - 0, 575, 496, 469, 562, 567, 478, 471, 0, 353, - 564, 476, 470, 446, 398, 611, 447, 448, 449, 450, - 451, 452, 412, 512, 467, 513, 413, 484, 483, 485, - 414, 415, 416, 417, 418, 419, 420, 421, 422, 423, - 0, 0, 0, 0, 0, 606, 607, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 743, 0, 0, 748, 0, 578, 0, 0, - 0, 0, 0, 0, 548, 0, 0, 453, 0, 0, - 0, 596, 0, 529, 502, 789, 0, 0, 527, 465, - 563, 514, 569, 551, 577, 521, 515, 344, 552, 390, - 479, 362, 364, 776, 392, 395, 399, 400, 490, 491, - 508, 536, 555, 556, 557, 389, 376, 528, 377, 409, - 378, 345, 381, 380, 382, 538, 383, 347, 509, 560, - 0, 405, 524, 474, 348, 473, 510, 559, 558, 363, - 586, 593, 594, 685, 0, 599, 790, 791, 792, 608, - 0, 516, 359, 356, 0, 0, 0, 385, 511, 372, - 374, 375, 373, 505, 507, 613, 614, 615, 617, 0, - 618, 619, 0, 0, 0, 0, 620, 686, 704, 670, - 639, 601, 694, 636, 640, 641, 426, 427, 428, 429, - 707, 0, 0, 0, 592, 454, 455, 0, 397, 396, - 475, 349, 0, 0, 443, 425, 517, 355, 393, 445, - 431, 456, 457, 458, 459, 460, 0, 461, 462, 403, - 339, 340, 784, 386, 498, 709, 746, 747, 631, 0, - 695, 632, 642, 379, 667, 679, 678, 492, 591, 0, - 690, 693, 621, 783, 0, 687, 701, 788, 700, 780, - 504, 0, 535, 698, 645, 0, 691, 664, 665, 0, - 692, 660, 696, 0, 633, 0, 602, 605, 635, 722, - 723, 724, 346, 604, 726, 727, 728, 729, 730, 731, - 732, 725, 574, 668, 644, 671, 583, 647, 646, 0, - 0, 682, 600, 683, 684, 486, 487, 488, 489, 407, - 710, 370, 603, 519, 520, 0, 669, 0, 0, 0, - 0, 0, 0, 0, 0, 674, 675, 672, 793, 0, - 733, 734, 0, 0, 597, 598, 402, 0, 616, 410, - 369, 503, 404, 581, 434, 0, 609, 676, 610, 522, - 523, 737, 742, 738, 739, 741, 762, 493, 424, 430, - 539, 444, 466, 525, 580, 501, 530, 367, 570, 541, - 472, 661, 689, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 325, 326, 0, 0, 0, 0, + 0, 0, 722, 721, 720, 719, 718, 717, 716, 715, + 0, 0, 662, 385, 785, 779, 786, 767, 770, 769, + 744, 0, 341, 642, 466, 520, 402, 709, 710, 0, + 765, 284, 285, 286, 287, 288, 289, 290, 291, 332, + 292, 293, 294, 295, 296, 297, 298, 303, 304, 305, + 306, 307, 308, 309, 310, 712, 301, 302, 311, 312, + 313, 314, 315, 316, 317, 318, 319, 320, 321, 322, + 323, 324, 0, 0, 0, 0, 333, 771, 772, 773, + 774, 775, 0, 0, 334, 335, 336, 0, 0, 299, + 300, 327, 552, 328, 329, 330, 331, 0, 0, 591, + 592, 593, 616, 0, 594, 575, 641, 412, 342, 556, + 583, 782, 783, 0, 0, 0, 0, 0, 0, 0, + 692, 703, 739, 740, 0, 753, 754, 756, 758, 757, + 760, 549, 550, 768, 0, 0, 437, 358, 359, 496, + 497, 535, 0, 438, 439, 440, 444, 441, 442, 443, + 762, 763, 764, 761, 470, 534, 557, 543, 781, 483, + 0, 789, 631, 632, 790, 749, 748, 343, 0, 337, + 338, 706, 707, 0, 0, 537, 482, 638, 502, 0, + 0, 647, 681, 670, 759, 634, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 395, 0, 0, 465, + 685, 666, 677, 667, 508, 652, 653, 654, 661, 407, + 655, 791, 657, 626, 658, 627, 659, 660, 0, 684, + 633, 545, 484, 0, 701, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 268, 0, 0, 5251, + 0, 0, 0, 366, 269, 628, 755, 630, 629, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 369, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 546, + 574, 0, 588, 0, 434, 435, 0, 0, 0, 0, + 0, 0, 0, 350, 553, 571, 367, 540, 586, 372, + 548, 564, 362, 501, 536, 0, 0, 352, 569, 547, + 479, 351, 0, 529, 392, 409, 389, 499, 0, 0, + 568, 599, 388, 589, 0, 579, 354, 0, 578, 498, + 471, 565, 570, 480, 473, 0, 353, 567, 478, 472, + 448, 399, 615, 449, 450, 451, 452, 453, 454, 413, + 514, 469, 515, 414, 486, 485, 487, 415, 416, 417, + 418, 419, 420, 421, 422, 423, 424, 0, 0, 0, + 0, 0, 610, 611, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 747, + 0, 0, 752, 0, 582, 0, 0, 0, 0, 0, + 0, 551, 0, 0, 455, 0, 0, 0, 600, 0, + 532, 504, 793, 0, 0, 530, 467, 566, 516, 572, + 554, 580, 523, 517, 344, 555, 391, 481, 363, 365, + 780, 393, 396, 400, 401, 492, 493, 510, 539, 558, + 559, 560, 390, 377, 531, 378, 410, 379, 345, 382, + 381, 383, 541, 384, 347, 511, 563, 0, 406, 527, + 476, 348, 475, 512, 562, 561, 364, 590, 597, 598, + 689, 0, 603, 794, 795, 796, 612, 0, 518, 360, + 357, 0, 0, 0, 386, 513, 373, 375, 376, 374, + 507, 509, 617, 618, 619, 621, 0, 622, 623, 0, + 0, 0, 0, 624, 690, 708, 674, 643, 605, 698, + 640, 644, 645, 427, 428, 429, 430, 711, 0, 0, + 0, 596, 456, 457, 0, 398, 397, 477, 349, 0, + 0, 445, 426, 519, 355, 394, 447, 432, 458, 459, + 460, 461, 462, 0, 463, 464, 404, 339, 340, 788, + 387, 500, 713, 750, 751, 635, 0, 699, 636, 646, + 380, 671, 683, 682, 494, 595, 0, 694, 697, 625, + 787, 0, 691, 705, 792, 704, 784, 506, 0, 538, + 702, 649, 0, 695, 668, 669, 0, 696, 664, 700, + 356, 433, 524, 581, 0, 637, 0, 606, 609, 639, + 726, 727, 728, 346, 608, 730, 731, 732, 733, 734, + 735, 736, 729, 577, 672, 648, 675, 587, 651, 650, + 0, 0, 686, 604, 687, 688, 488, 489, 490, 491, + 408, 714, 371, 607, 521, 522, 0, 673, 0, 0, + 0, 0, 0, 0, 0, 0, 678, 679, 676, 797, + 0, 737, 738, 0, 0, 601, 602, 403, 0, 620, + 411, 370, 505, 405, 585, 436, 0, 613, 680, 614, + 525, 526, 741, 746, 742, 743, 745, 766, 495, 425, + 431, 542, 446, 468, 528, 584, 503, 533, 368, 573, + 544, 474, 665, 693, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 325, 326, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 718, 717, 716, 715, 714, 713, 712, 711, 0, 0, - 658, 384, 781, 775, 782, 763, 766, 765, 740, 0, - 341, 638, 464, 518, 401, 705, 706, 0, 761, 284, - 285, 286, 287, 288, 289, 290, 291, 332, 292, 293, - 294, 295, 296, 297, 298, 303, 304, 305, 306, 307, - 308, 309, 310, 708, 301, 302, 311, 312, 313, 314, - 315, 316, 317, 318, 319, 320, 321, 322, 323, 324, - 0, 0, 0, 0, 333, 767, 768, 769, 770, 771, - 0, 0, 334, 335, 336, 0, 0, 299, 300, 327, - 549, 328, 329, 330, 331, 0, 0, 587, 588, 589, - 612, 0, 590, 572, 637, 411, 342, 553, 579, 778, - 779, 0, 0, 0, 0, 0, 0, 0, 688, 699, - 735, 736, 0, 749, 750, 752, 754, 753, 756, 546, - 547, 764, 0, 0, 435, 357, 358, 494, 495, 532, - 0, 436, 437, 438, 442, 439, 440, 441, 758, 759, - 760, 757, 468, 531, 554, 540, 777, 481, 0, 785, - 627, 628, 786, 745, 744, 343, 0, 337, 338, 702, - 703, 0, 0, 534, 480, 634, 500, 0, 0, 643, - 677, 666, 755, 630, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 394, 0, 0, 463, 681, 662, - 673, 663, 506, 648, 649, 650, 657, 406, 651, 787, - 653, 622, 654, 623, 655, 656, 0, 680, 629, 542, - 482, 0, 697, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 2198, 0, 0, 268, 0, 0, 0, 0, 0, - 0, 365, 269, 624, 751, 626, 625, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 368, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 543, 571, 0, - 584, 0, 432, 433, 0, 0, 0, 0, 0, 0, - 0, 350, 550, 568, 366, 537, 582, 371, 545, 561, - 361, 499, 533, 0, 0, 352, 566, 544, 477, 351, - 0, 526, 391, 408, 388, 497, 0, 0, 565, 595, - 387, 585, 0, 576, 354, 0, 575, 496, 469, 562, - 567, 478, 471, 0, 353, 564, 476, 470, 446, 398, - 611, 447, 448, 449, 450, 451, 452, 412, 512, 467, - 513, 413, 484, 483, 485, 414, 415, 416, 417, 418, - 419, 420, 421, 422, 423, 0, 0, 0, 0, 0, - 606, 607, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 743, 0, 0, - 748, 0, 578, 0, 0, 0, 0, 0, 0, 548, - 0, 0, 453, 0, 0, 0, 596, 0, 529, 502, - 789, 0, 0, 527, 465, 563, 514, 569, 551, 577, - 521, 515, 344, 552, 390, 479, 362, 364, 776, 392, - 395, 399, 400, 490, 491, 508, 536, 555, 556, 557, - 389, 376, 528, 377, 409, 378, 345, 381, 380, 382, - 538, 383, 347, 509, 560, 0, 405, 524, 474, 348, - 473, 510, 559, 558, 363, 586, 593, 594, 685, 0, - 599, 790, 791, 792, 608, 0, 516, 359, 356, 0, - 0, 0, 385, 511, 372, 374, 375, 373, 505, 507, - 613, 614, 615, 617, 0, 618, 619, 0, 0, 0, - 0, 620, 686, 704, 670, 639, 601, 694, 636, 640, - 641, 426, 427, 428, 429, 707, 0, 0, 0, 592, - 454, 455, 0, 397, 396, 475, 349, 0, 0, 443, - 425, 517, 355, 393, 445, 431, 456, 457, 458, 459, - 460, 0, 461, 462, 403, 339, 340, 784, 386, 498, - 709, 746, 747, 631, 0, 695, 632, 642, 379, 667, - 679, 678, 492, 591, 0, 690, 693, 621, 783, 0, - 687, 701, 788, 700, 780, 504, 0, 535, 698, 645, - 0, 691, 664, 665, 0, 692, 660, 696, 0, 633, - 0, 602, 605, 635, 722, 723, 724, 346, 604, 726, - 727, 728, 729, 730, 731, 732, 725, 574, 668, 644, - 671, 583, 647, 646, 0, 0, 682, 600, 683, 684, - 486, 487, 488, 489, 407, 710, 370, 603, 519, 520, - 0, 669, 0, 0, 0, 0, 0, 0, 0, 0, - 674, 675, 672, 793, 0, 733, 734, 0, 0, 597, - 598, 402, 0, 616, 410, 369, 503, 404, 581, 434, - 0, 609, 676, 610, 522, 523, 737, 742, 738, 739, - 741, 762, 493, 424, 430, 539, 444, 466, 525, 580, - 501, 530, 367, 570, 541, 472, 661, 689, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 325, - 326, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 718, 717, 716, 715, 714, - 713, 712, 711, 0, 0, 658, 384, 781, 775, 782, - 763, 766, 765, 740, 0, 341, 638, 464, 518, 401, - 705, 706, 0, 761, 284, 285, 286, 287, 288, 289, - 290, 291, 332, 292, 293, 294, 295, 296, 297, 298, - 303, 304, 305, 306, 307, 308, 309, 310, 708, 301, - 302, 311, 312, 313, 314, 315, 316, 317, 318, 319, - 320, 321, 322, 323, 324, 0, 0, 0, 0, 333, - 767, 768, 769, 770, 771, 0, 0, 334, 335, 336, - 0, 0, 299, 300, 327, 549, 328, 329, 330, 331, - 0, 0, 587, 588, 589, 612, 0, 590, 572, 637, - 411, 342, 553, 579, 778, 779, 0, 0, 0, 0, - 0, 0, 0, 688, 699, 735, 736, 0, 749, 750, - 752, 754, 753, 756, 546, 547, 764, 0, 0, 435, - 357, 358, 494, 495, 532, 0, 436, 437, 438, 442, - 439, 440, 441, 758, 759, 760, 757, 468, 531, 554, - 540, 777, 481, 0, 785, 627, 628, 786, 745, 744, - 343, 0, 337, 338, 702, 703, 0, 0, 534, 480, - 634, 500, 0, 0, 643, 677, 666, 755, 630, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 394, - 0, 0, 463, 681, 662, 673, 663, 506, 648, 649, - 650, 657, 406, 651, 787, 653, 622, 654, 623, 655, - 656, 0, 680, 629, 542, 482, 0, 697, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 4867, 0, 268, - 0, 0, 0, 0, 0, 0, 365, 269, 624, 751, - 626, 625, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 368, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 543, 571, 0, 584, 0, 432, 433, 0, - 0, 0, 0, 0, 0, 0, 350, 550, 568, 366, - 537, 582, 371, 545, 561, 361, 499, 533, 0, 0, - 352, 566, 544, 477, 351, 0, 526, 391, 408, 388, - 497, 0, 0, 565, 595, 387, 585, 0, 576, 354, - 0, 575, 496, 469, 562, 567, 478, 471, 0, 353, - 564, 476, 470, 446, 398, 611, 447, 448, 449, 450, - 451, 452, 412, 512, 467, 513, 413, 484, 483, 485, - 414, 415, 416, 417, 418, 419, 420, 421, 422, 423, - 0, 0, 0, 0, 0, 606, 607, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 743, 0, 0, 748, 0, 578, 0, 0, - 0, 0, 0, 0, 548, 0, 0, 453, 0, 0, - 0, 596, 0, 529, 502, 789, 0, 0, 527, 465, - 563, 514, 569, 551, 577, 521, 515, 344, 552, 390, - 479, 362, 364, 776, 392, 395, 399, 400, 490, 491, - 508, 536, 555, 556, 557, 389, 376, 528, 377, 409, - 378, 345, 381, 380, 382, 538, 383, 347, 509, 560, - 0, 405, 524, 474, 348, 473, 510, 559, 558, 363, - 586, 593, 594, 685, 0, 599, 790, 791, 792, 608, - 0, 516, 359, 356, 0, 0, 0, 385, 511, 372, - 374, 375, 373, 505, 507, 613, 614, 615, 617, 0, - 618, 619, 0, 0, 0, 0, 620, 686, 704, 670, - 639, 601, 694, 636, 640, 641, 426, 427, 428, 429, - 707, 0, 0, 0, 592, 454, 455, 0, 397, 396, - 475, 349, 0, 0, 443, 425, 517, 355, 393, 445, - 431, 456, 457, 458, 459, 460, 0, 461, 462, 403, - 339, 340, 784, 386, 498, 709, 746, 747, 631, 0, - 695, 632, 642, 379, 667, 679, 678, 492, 591, 0, - 690, 693, 621, 783, 0, 687, 701, 788, 700, 780, - 504, 0, 535, 698, 645, 0, 691, 664, 665, 0, - 692, 660, 696, 0, 633, 0, 602, 605, 635, 722, - 723, 724, 346, 604, 726, 727, 728, 729, 730, 731, - 732, 725, 574, 668, 644, 671, 583, 647, 646, 0, - 0, 682, 600, 683, 684, 486, 487, 488, 489, 407, - 710, 370, 603, 519, 520, 0, 669, 0, 0, 0, - 0, 0, 0, 0, 0, 674, 675, 672, 793, 0, - 733, 734, 0, 0, 597, 598, 402, 0, 616, 410, - 369, 503, 404, 581, 434, 0, 609, 676, 610, 522, - 523, 737, 742, 738, 739, 741, 762, 493, 424, 430, - 539, 444, 466, 525, 580, 501, 530, 367, 570, 541, - 472, 661, 689, 0, 0, 0, 0, 0, 0, 0, + 0, 722, 721, 720, 719, 718, 717, 716, 715, 0, + 0, 662, 385, 785, 779, 786, 767, 770, 769, 744, + 0, 341, 642, 466, 520, 402, 709, 710, 0, 765, + 284, 285, 286, 287, 288, 289, 290, 291, 332, 292, + 293, 294, 295, 296, 297, 298, 303, 304, 305, 306, + 307, 308, 309, 310, 712, 301, 302, 311, 312, 313, + 314, 315, 316, 317, 318, 319, 320, 321, 322, 323, + 324, 0, 0, 0, 0, 333, 771, 772, 773, 774, + 775, 0, 0, 334, 335, 336, 0, 0, 299, 300, + 327, 552, 328, 329, 330, 331, 0, 0, 591, 592, + 593, 616, 0, 594, 575, 641, 412, 342, 556, 583, + 782, 783, 0, 0, 0, 0, 0, 0, 0, 692, + 703, 739, 740, 0, 753, 754, 756, 758, 757, 760, + 549, 550, 768, 0, 0, 437, 358, 359, 496, 497, + 535, 0, 438, 439, 440, 444, 441, 442, 443, 762, + 763, 764, 761, 470, 534, 557, 543, 781, 483, 0, + 789, 631, 632, 790, 749, 748, 343, 0, 337, 338, + 706, 707, 0, 0, 537, 482, 638, 502, 0, 0, + 647, 681, 670, 759, 634, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 395, 0, 0, 465, 685, + 666, 677, 667, 508, 652, 653, 654, 661, 407, 655, + 791, 657, 626, 658, 627, 659, 660, 0, 684, 633, + 545, 484, 0, 701, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 268, 1073, 0, 0, 0, + 0, 0, 366, 269, 628, 755, 630, 629, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 369, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 546, 574, + 0, 588, 0, 434, 435, 0, 0, 0, 0, 0, + 0, 0, 350, 553, 571, 367, 540, 586, 372, 548, + 564, 362, 501, 536, 0, 0, 352, 569, 547, 479, + 351, 0, 529, 392, 409, 389, 499, 0, 0, 568, + 599, 388, 589, 0, 579, 354, 0, 578, 498, 471, + 565, 570, 480, 473, 0, 353, 567, 478, 472, 448, + 399, 615, 449, 450, 451, 452, 453, 454, 413, 514, + 469, 515, 414, 486, 485, 487, 415, 416, 417, 418, + 419, 420, 421, 422, 423, 424, 0, 0, 0, 0, + 0, 610, 611, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 747, 0, + 0, 752, 0, 582, 0, 0, 0, 0, 0, 0, + 551, 0, 0, 455, 0, 0, 0, 600, 0, 532, + 504, 793, 0, 0, 530, 467, 566, 516, 572, 554, + 580, 523, 517, 344, 555, 391, 481, 363, 365, 780, + 393, 396, 400, 401, 492, 493, 510, 539, 558, 559, + 560, 390, 377, 531, 378, 410, 379, 345, 382, 381, + 383, 541, 384, 347, 511, 563, 0, 406, 527, 476, + 348, 475, 512, 562, 561, 364, 590, 597, 598, 689, + 0, 603, 794, 795, 796, 612, 0, 518, 360, 357, + 0, 0, 0, 386, 513, 373, 375, 376, 374, 507, + 509, 617, 618, 619, 621, 0, 622, 623, 0, 0, + 0, 0, 624, 690, 708, 674, 643, 605, 698, 640, + 644, 645, 427, 428, 429, 430, 711, 0, 0, 0, + 596, 456, 457, 0, 398, 397, 477, 349, 0, 0, + 445, 426, 519, 355, 394, 447, 432, 458, 459, 460, + 461, 462, 0, 463, 464, 404, 339, 340, 788, 387, + 500, 713, 750, 751, 635, 0, 699, 636, 646, 380, + 671, 683, 682, 494, 595, 0, 694, 697, 625, 787, + 0, 691, 705, 792, 704, 784, 506, 0, 538, 702, + 649, 0, 695, 668, 669, 0, 696, 664, 700, 356, + 433, 524, 581, 0, 637, 0, 606, 609, 639, 726, + 727, 728, 346, 608, 730, 731, 732, 733, 734, 735, + 736, 729, 577, 672, 648, 675, 587, 651, 650, 0, + 0, 686, 604, 687, 688, 488, 489, 490, 491, 408, + 714, 371, 607, 521, 522, 0, 673, 0, 0, 0, + 0, 0, 0, 0, 0, 678, 679, 676, 797, 0, + 737, 738, 0, 0, 601, 602, 403, 0, 620, 411, + 370, 505, 405, 585, 436, 0, 613, 680, 614, 525, + 526, 741, 746, 742, 743, 745, 766, 495, 425, 431, + 542, 446, 468, 528, 584, 503, 533, 368, 573, 544, + 474, 665, 693, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 325, 326, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 718, 717, 716, 715, 714, 713, 712, 711, 0, 0, - 658, 384, 781, 775, 782, 763, 766, 765, 740, 0, - 341, 638, 464, 518, 401, 705, 706, 0, 761, 284, + 722, 721, 720, 719, 718, 717, 716, 715, 0, 0, + 662, 385, 785, 779, 786, 767, 770, 769, 744, 0, + 341, 642, 466, 520, 402, 709, 710, 0, 765, 284, 285, 286, 287, 288, 289, 290, 291, 332, 292, 293, 294, 295, 296, 297, 298, 303, 304, 305, 306, 307, - 308, 309, 310, 708, 301, 302, 311, 312, 313, 314, + 308, 309, 310, 712, 301, 302, 311, 312, 313, 314, 315, 316, 317, 318, 319, 320, 321, 322, 323, 324, - 0, 0, 0, 0, 333, 767, 768, 769, 770, 771, + 0, 0, 0, 0, 333, 771, 772, 773, 774, 775, 0, 0, 334, 335, 336, 0, 0, 299, 300, 327, - 549, 328, 329, 330, 331, 0, 0, 587, 588, 589, - 612, 0, 590, 572, 637, 411, 342, 553, 579, 778, - 779, 0, 0, 0, 0, 0, 0, 0, 688, 699, - 735, 736, 0, 749, 750, 752, 754, 753, 756, 546, - 547, 764, 0, 0, 435, 357, 358, 494, 495, 532, - 0, 436, 437, 438, 442, 439, 440, 441, 758, 759, - 760, 757, 468, 531, 554, 540, 777, 481, 0, 785, - 627, 628, 786, 745, 744, 343, 0, 337, 338, 702, - 703, 0, 0, 534, 480, 634, 500, 0, 0, 643, - 677, 666, 755, 630, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 394, 0, 0, 463, 681, 662, - 673, 663, 506, 648, 649, 650, 657, 406, 651, 787, - 653, 622, 654, 623, 655, 656, 0, 680, 629, 542, - 482, 0, 697, 0, 0, 0, 0, 0, 0, 0, + 552, 328, 329, 330, 331, 0, 0, 591, 592, 593, + 616, 0, 594, 575, 641, 412, 342, 556, 583, 782, + 783, 0, 0, 0, 0, 0, 0, 0, 692, 703, + 739, 740, 0, 753, 754, 756, 758, 757, 760, 549, + 550, 768, 0, 0, 437, 358, 359, 496, 497, 535, + 0, 438, 439, 440, 444, 441, 442, 443, 762, 763, + 764, 761, 470, 534, 557, 543, 781, 483, 0, 789, + 631, 632, 790, 749, 748, 343, 0, 337, 338, 706, + 707, 0, 0, 537, 482, 638, 502, 0, 0, 647, + 681, 670, 759, 634, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 395, 0, 0, 465, 685, 666, + 677, 667, 508, 652, 653, 654, 661, 407, 655, 791, + 657, 626, 658, 627, 659, 660, 0, 684, 633, 545, + 484, 0, 701, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 5198, 0, 0, 268, 0, 0, 0, 0, 0, + 0, 366, 269, 628, 755, 630, 629, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 369, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 546, 574, 0, + 588, 0, 434, 435, 0, 0, 0, 0, 0, 0, + 0, 350, 553, 571, 367, 540, 586, 372, 548, 564, + 362, 501, 536, 0, 0, 352, 569, 547, 479, 351, + 0, 529, 392, 409, 389, 499, 0, 0, 568, 599, + 388, 589, 0, 579, 354, 0, 578, 498, 471, 565, + 570, 480, 473, 0, 353, 567, 478, 472, 448, 399, + 615, 449, 450, 451, 452, 453, 454, 413, 514, 469, + 515, 414, 486, 485, 487, 415, 416, 417, 418, 419, + 420, 421, 422, 423, 424, 0, 0, 0, 0, 0, + 610, 611, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 747, 0, 0, + 752, 0, 582, 0, 0, 0, 0, 0, 0, 551, + 0, 0, 455, 0, 0, 0, 600, 0, 532, 504, + 793, 0, 0, 530, 467, 566, 516, 572, 554, 580, + 523, 517, 344, 555, 391, 481, 363, 365, 780, 393, + 396, 400, 401, 492, 493, 510, 539, 558, 559, 560, + 390, 377, 531, 378, 410, 379, 345, 382, 381, 383, + 541, 384, 347, 511, 563, 0, 406, 527, 476, 348, + 475, 512, 562, 561, 364, 590, 597, 598, 689, 0, + 603, 794, 795, 796, 612, 0, 518, 360, 357, 0, + 0, 0, 386, 513, 373, 375, 376, 374, 507, 509, + 617, 618, 619, 621, 0, 622, 623, 0, 0, 0, + 0, 624, 690, 708, 674, 643, 605, 698, 640, 644, + 645, 427, 428, 429, 430, 711, 0, 0, 0, 596, + 456, 457, 0, 398, 397, 477, 349, 0, 0, 445, + 426, 519, 355, 394, 447, 432, 458, 459, 460, 461, + 462, 0, 463, 464, 404, 339, 340, 788, 387, 500, + 713, 750, 751, 635, 0, 699, 636, 646, 380, 671, + 683, 682, 494, 595, 0, 694, 697, 625, 787, 0, + 691, 705, 792, 704, 784, 506, 0, 538, 702, 649, + 0, 695, 668, 669, 0, 696, 664, 700, 356, 433, + 524, 581, 0, 637, 0, 606, 609, 639, 726, 727, + 728, 346, 608, 730, 731, 732, 733, 734, 735, 736, + 729, 577, 672, 648, 675, 587, 651, 650, 0, 0, + 686, 604, 687, 688, 488, 489, 490, 491, 408, 714, + 371, 607, 521, 522, 0, 673, 0, 0, 0, 0, + 0, 0, 0, 0, 678, 679, 676, 797, 0, 737, + 738, 0, 0, 601, 602, 403, 0, 620, 411, 370, + 505, 405, 585, 436, 0, 613, 680, 614, 525, 526, + 741, 746, 742, 743, 745, 766, 495, 425, 431, 542, + 446, 468, 528, 584, 503, 533, 368, 573, 544, 474, + 665, 693, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 325, 326, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 722, + 721, 720, 719, 718, 717, 716, 715, 0, 0, 662, + 385, 785, 779, 786, 767, 770, 769, 744, 0, 341, + 642, 466, 520, 402, 709, 710, 0, 765, 284, 285, + 286, 287, 288, 289, 290, 291, 332, 292, 293, 294, + 295, 296, 297, 298, 303, 304, 305, 306, 307, 308, + 309, 310, 712, 301, 302, 311, 312, 313, 314, 315, + 316, 317, 318, 319, 320, 321, 322, 323, 324, 0, + 0, 0, 0, 333, 771, 772, 773, 774, 775, 0, + 0, 334, 335, 336, 0, 0, 299, 300, 327, 552, + 328, 329, 330, 331, 0, 0, 591, 592, 593, 616, + 0, 594, 575, 641, 412, 342, 556, 583, 782, 783, + 0, 0, 0, 0, 0, 0, 0, 692, 703, 739, + 740, 0, 753, 754, 756, 758, 757, 760, 549, 550, + 768, 0, 0, 437, 358, 359, 496, 497, 535, 0, + 438, 439, 440, 444, 441, 442, 443, 762, 763, 764, + 761, 470, 534, 557, 543, 781, 483, 0, 789, 631, + 632, 790, 749, 748, 343, 0, 337, 338, 706, 707, + 0, 0, 537, 482, 638, 502, 0, 0, 647, 681, + 670, 759, 634, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 395, 0, 0, 465, 685, 666, 677, + 667, 508, 652, 653, 654, 661, 407, 655, 791, 657, + 626, 658, 627, 659, 660, 0, 684, 633, 545, 484, + 0, 701, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 2202, 0, 0, 268, 0, 0, 0, 0, 0, 0, + 366, 269, 628, 755, 630, 629, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 369, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 546, 574, 0, 588, + 0, 434, 435, 0, 0, 0, 0, 0, 0, 0, + 350, 553, 571, 367, 540, 586, 372, 548, 564, 362, + 501, 536, 0, 0, 352, 569, 547, 479, 351, 0, + 529, 392, 409, 389, 499, 0, 0, 568, 599, 388, + 589, 0, 579, 354, 0, 578, 498, 471, 565, 570, + 480, 473, 0, 353, 567, 478, 472, 448, 399, 615, + 449, 450, 451, 452, 453, 454, 413, 514, 469, 515, + 414, 486, 485, 487, 415, 416, 417, 418, 419, 420, + 421, 422, 423, 424, 0, 0, 0, 0, 0, 610, + 611, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 747, 0, 0, 752, + 0, 582, 0, 0, 0, 0, 0, 0, 551, 0, + 0, 455, 0, 0, 0, 600, 0, 532, 504, 793, + 0, 0, 530, 467, 566, 516, 572, 554, 580, 523, + 517, 344, 555, 391, 481, 363, 365, 780, 393, 396, + 400, 401, 492, 493, 510, 539, 558, 559, 560, 390, + 377, 531, 378, 410, 379, 345, 382, 381, 383, 541, + 384, 347, 511, 563, 0, 406, 527, 476, 348, 475, + 512, 562, 561, 364, 590, 597, 598, 689, 0, 603, + 794, 795, 796, 612, 0, 518, 360, 357, 0, 0, + 0, 386, 513, 373, 375, 376, 374, 507, 509, 617, + 618, 619, 621, 0, 622, 623, 0, 0, 0, 0, + 624, 690, 708, 674, 643, 605, 698, 640, 644, 645, + 427, 428, 429, 430, 711, 0, 0, 0, 596, 456, + 457, 0, 398, 397, 477, 349, 0, 0, 445, 426, + 519, 355, 394, 447, 432, 458, 459, 460, 461, 462, + 0, 463, 464, 404, 339, 340, 788, 387, 500, 713, + 750, 751, 635, 0, 699, 636, 646, 380, 671, 683, + 682, 494, 595, 0, 694, 697, 625, 787, 0, 691, + 705, 792, 704, 784, 506, 0, 538, 702, 649, 0, + 695, 668, 669, 0, 696, 664, 700, 356, 433, 524, + 581, 0, 637, 0, 606, 609, 639, 726, 727, 728, + 346, 608, 730, 731, 732, 733, 734, 735, 736, 729, + 577, 672, 648, 675, 587, 651, 650, 0, 0, 686, + 604, 687, 688, 488, 489, 490, 491, 408, 714, 371, + 607, 521, 522, 0, 673, 0, 0, 0, 0, 0, + 0, 0, 0, 678, 679, 676, 797, 0, 737, 738, + 0, 0, 601, 602, 403, 0, 620, 411, 370, 505, + 405, 585, 436, 0, 613, 680, 614, 525, 526, 741, + 746, 742, 743, 745, 766, 495, 425, 431, 542, 446, + 468, 528, 584, 503, 533, 368, 573, 544, 474, 665, + 693, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 325, 326, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 722, 721, + 720, 719, 718, 717, 716, 715, 0, 0, 662, 385, + 785, 779, 786, 767, 770, 769, 744, 0, 341, 642, + 466, 520, 402, 709, 710, 0, 765, 284, 285, 286, + 287, 288, 289, 290, 291, 332, 292, 293, 294, 295, + 296, 297, 298, 303, 304, 305, 306, 307, 308, 309, + 310, 712, 301, 302, 311, 312, 313, 314, 315, 316, + 317, 318, 319, 320, 321, 322, 323, 324, 0, 0, + 0, 0, 333, 771, 772, 773, 774, 775, 0, 0, + 334, 335, 336, 0, 0, 299, 300, 327, 552, 328, + 329, 330, 331, 0, 0, 591, 592, 593, 616, 0, + 594, 575, 641, 412, 342, 556, 583, 782, 783, 0, + 0, 0, 0, 0, 0, 0, 692, 703, 739, 740, + 0, 753, 754, 756, 758, 757, 760, 549, 550, 768, + 0, 0, 437, 358, 359, 496, 497, 535, 0, 438, + 439, 440, 444, 441, 442, 443, 762, 763, 764, 761, + 470, 534, 557, 543, 781, 483, 0, 789, 631, 632, + 790, 749, 748, 343, 0, 337, 338, 706, 707, 0, + 0, 537, 482, 638, 502, 0, 0, 647, 681, 670, + 759, 634, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 395, 0, 0, 465, 685, 666, 677, 667, + 508, 652, 653, 654, 661, 407, 655, 791, 657, 626, + 658, 627, 659, 660, 0, 684, 633, 545, 484, 0, + 701, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 4871, 0, 268, 0, 0, 0, 0, 0, 0, 366, + 269, 628, 755, 630, 629, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 369, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 546, 574, 0, 588, 0, + 434, 435, 0, 0, 0, 0, 0, 0, 0, 350, + 553, 571, 367, 540, 586, 372, 548, 564, 362, 501, + 536, 0, 0, 352, 569, 547, 479, 351, 0, 529, + 392, 409, 389, 499, 0, 0, 568, 599, 388, 589, + 0, 579, 354, 0, 578, 498, 471, 565, 570, 480, + 473, 0, 353, 567, 478, 472, 448, 399, 615, 449, + 450, 451, 452, 453, 454, 413, 514, 469, 515, 414, + 486, 485, 487, 415, 416, 417, 418, 419, 420, 421, + 422, 423, 424, 0, 0, 0, 0, 0, 610, 611, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 747, 0, 0, 752, 0, + 582, 0, 0, 0, 0, 0, 0, 551, 0, 0, + 455, 0, 0, 0, 600, 0, 532, 504, 793, 0, + 0, 530, 467, 566, 516, 572, 554, 580, 523, 517, + 344, 555, 391, 481, 363, 365, 780, 393, 396, 400, + 401, 492, 493, 510, 539, 558, 559, 560, 390, 377, + 531, 378, 410, 379, 345, 382, 381, 383, 541, 384, + 347, 511, 563, 0, 406, 527, 476, 348, 475, 512, + 562, 561, 364, 590, 597, 598, 689, 0, 603, 794, + 795, 796, 612, 0, 518, 360, 357, 0, 0, 0, + 386, 513, 373, 375, 376, 374, 507, 509, 617, 618, + 619, 621, 0, 622, 623, 0, 0, 0, 0, 624, + 690, 708, 674, 643, 605, 698, 640, 644, 645, 427, + 428, 429, 430, 711, 0, 0, 0, 596, 456, 457, + 0, 398, 397, 477, 349, 0, 0, 445, 426, 519, + 355, 394, 447, 432, 458, 459, 460, 461, 462, 0, + 463, 464, 404, 339, 340, 788, 387, 500, 713, 750, + 751, 635, 0, 699, 636, 646, 380, 671, 683, 682, + 494, 595, 0, 694, 697, 625, 787, 0, 691, 705, + 792, 704, 784, 506, 0, 538, 702, 649, 0, 695, + 668, 669, 0, 696, 664, 700, 356, 433, 524, 581, + 0, 637, 0, 606, 609, 639, 726, 727, 728, 346, + 608, 730, 731, 732, 733, 734, 735, 736, 729, 577, + 672, 648, 675, 587, 651, 650, 0, 0, 686, 604, + 687, 688, 488, 489, 490, 491, 408, 714, 371, 607, + 521, 522, 0, 673, 0, 0, 0, 0, 0, 0, + 0, 0, 678, 679, 676, 797, 0, 737, 738, 0, + 0, 601, 602, 403, 0, 620, 411, 370, 505, 405, + 585, 436, 0, 613, 680, 614, 525, 526, 741, 746, + 742, 743, 745, 766, 495, 425, 431, 542, 446, 468, + 528, 584, 503, 533, 368, 573, 544, 474, 665, 693, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 268, 0, 0, 0, 0, 0, - 0, 365, 269, 624, 751, 626, 625, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 368, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 543, 571, 0, - 584, 0, 432, 433, 0, 0, 0, 0, 0, 0, - 0, 350, 550, 568, 366, 537, 582, 371, 545, 561, - 361, 499, 533, 0, 0, 352, 566, 544, 477, 351, - 0, 526, 391, 408, 388, 497, 0, 0, 565, 595, - 387, 585, 0, 576, 354, 0, 575, 496, 469, 562, - 567, 478, 471, 0, 353, 564, 476, 470, 446, 398, - 611, 447, 448, 449, 450, 451, 452, 412, 512, 467, - 513, 413, 484, 483, 485, 414, 415, 416, 417, 418, - 419, 420, 421, 422, 423, 0, 0, 0, 0, 0, - 606, 607, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 743, 0, 0, - 748, 0, 578, 0, 0, 0, 4860, 0, 0, 548, - 0, 0, 453, 0, 0, 0, 596, 0, 529, 502, - 789, 0, 0, 527, 465, 563, 514, 569, 551, 577, - 521, 515, 344, 552, 390, 479, 362, 364, 776, 392, - 395, 399, 400, 490, 491, 508, 536, 555, 556, 557, - 389, 376, 528, 377, 409, 378, 345, 381, 380, 382, - 538, 383, 347, 509, 560, 0, 405, 524, 474, 348, - 473, 510, 559, 558, 363, 586, 593, 594, 685, 0, - 599, 790, 791, 792, 608, 0, 516, 359, 356, 0, - 0, 0, 385, 511, 372, 374, 375, 373, 505, 507, - 613, 614, 615, 617, 0, 618, 619, 0, 0, 0, - 0, 620, 686, 704, 670, 639, 601, 694, 636, 640, - 641, 426, 427, 428, 429, 707, 0, 0, 0, 592, - 454, 455, 0, 397, 396, 475, 349, 0, 0, 443, - 425, 517, 355, 393, 445, 431, 456, 457, 458, 459, - 460, 0, 461, 462, 403, 339, 340, 784, 386, 498, - 709, 746, 747, 631, 0, 695, 632, 642, 379, 667, - 679, 678, 492, 591, 0, 690, 693, 621, 783, 0, - 687, 701, 788, 700, 780, 504, 0, 535, 698, 645, - 0, 691, 664, 665, 0, 692, 660, 696, 0, 633, - 0, 602, 605, 635, 722, 723, 724, 346, 604, 726, - 727, 728, 729, 730, 731, 732, 725, 574, 668, 644, - 671, 583, 647, 646, 0, 0, 682, 600, 683, 684, - 486, 487, 488, 489, 407, 710, 370, 603, 519, 520, - 0, 669, 0, 0, 0, 0, 0, 0, 0, 0, - 674, 675, 672, 793, 0, 733, 734, 0, 0, 597, - 598, 402, 0, 616, 410, 369, 503, 404, 581, 434, - 0, 609, 676, 610, 522, 523, 737, 742, 738, 739, - 741, 762, 493, 424, 430, 539, 444, 466, 525, 580, - 501, 530, 367, 570, 541, 472, 661, 689, 0, 0, + 0, 325, 326, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 722, 721, 720, + 719, 718, 717, 716, 715, 0, 0, 662, 385, 785, + 779, 786, 767, 770, 769, 744, 0, 341, 642, 466, + 520, 402, 709, 710, 0, 765, 284, 285, 286, 287, + 288, 289, 290, 291, 332, 292, 293, 294, 295, 296, + 297, 298, 303, 304, 305, 306, 307, 308, 309, 310, + 712, 301, 302, 311, 312, 313, 314, 315, 316, 317, + 318, 319, 320, 321, 322, 323, 324, 0, 0, 0, + 0, 333, 771, 772, 773, 774, 775, 0, 0, 334, + 335, 336, 0, 0, 299, 300, 327, 552, 328, 329, + 330, 331, 0, 0, 591, 592, 593, 616, 0, 594, + 575, 641, 412, 342, 556, 583, 782, 783, 0, 0, + 0, 0, 0, 0, 0, 692, 703, 739, 740, 0, + 753, 754, 756, 758, 757, 760, 549, 550, 768, 0, + 0, 437, 358, 359, 496, 497, 535, 0, 438, 439, + 440, 444, 441, 442, 443, 762, 763, 764, 761, 470, + 534, 557, 543, 781, 483, 0, 789, 631, 632, 790, + 749, 748, 343, 0, 337, 338, 706, 707, 0, 0, + 537, 482, 638, 502, 0, 0, 647, 681, 670, 759, + 634, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 395, 0, 0, 465, 685, 666, 677, 667, 508, + 652, 653, 654, 661, 407, 655, 791, 657, 626, 658, + 627, 659, 660, 0, 684, 633, 545, 484, 0, 701, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 268, 0, 0, 0, 0, 0, 0, 366, 269, + 628, 755, 630, 629, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 369, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 546, 574, 0, 588, 0, 434, + 435, 0, 0, 0, 0, 0, 0, 0, 350, 553, + 571, 367, 540, 586, 372, 548, 564, 362, 501, 536, + 0, 0, 352, 569, 547, 479, 351, 0, 529, 392, + 409, 389, 499, 0, 0, 568, 599, 388, 589, 0, + 579, 354, 0, 578, 498, 471, 565, 570, 480, 473, + 0, 353, 567, 478, 472, 448, 399, 615, 449, 450, + 451, 452, 453, 454, 413, 514, 469, 515, 414, 486, + 485, 487, 415, 416, 417, 418, 419, 420, 421, 422, + 423, 424, 0, 0, 0, 0, 0, 610, 611, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 747, 0, 0, 752, 0, 582, + 0, 0, 0, 4864, 0, 0, 551, 0, 0, 455, + 0, 0, 0, 600, 0, 532, 504, 793, 0, 0, + 530, 467, 566, 516, 572, 554, 580, 523, 517, 344, + 555, 391, 481, 363, 365, 780, 393, 396, 400, 401, + 492, 493, 510, 539, 558, 559, 560, 390, 377, 531, + 378, 410, 379, 345, 382, 381, 383, 541, 384, 347, + 511, 563, 0, 406, 527, 476, 348, 475, 512, 562, + 561, 364, 590, 597, 598, 689, 0, 603, 794, 795, + 796, 612, 0, 518, 360, 357, 0, 0, 0, 386, + 513, 373, 375, 376, 374, 507, 509, 617, 618, 619, + 621, 0, 622, 623, 0, 0, 0, 0, 624, 690, + 708, 674, 643, 605, 698, 640, 644, 645, 427, 428, + 429, 430, 711, 0, 0, 0, 596, 456, 457, 0, + 398, 397, 477, 349, 0, 0, 445, 426, 519, 355, + 394, 447, 432, 458, 459, 460, 461, 462, 0, 463, + 464, 404, 339, 340, 788, 387, 500, 713, 750, 751, + 635, 0, 699, 636, 646, 380, 671, 683, 682, 494, + 595, 0, 694, 697, 625, 787, 0, 691, 705, 792, + 704, 784, 506, 0, 538, 702, 649, 0, 695, 668, + 669, 0, 696, 664, 700, 356, 433, 524, 581, 0, + 637, 0, 606, 609, 639, 726, 727, 728, 346, 608, + 730, 731, 732, 733, 734, 735, 736, 729, 577, 672, + 648, 675, 587, 651, 650, 0, 0, 686, 604, 687, + 688, 488, 489, 490, 491, 408, 714, 371, 607, 521, + 522, 0, 673, 0, 0, 0, 0, 0, 0, 0, + 0, 678, 679, 676, 797, 0, 737, 738, 0, 0, + 601, 602, 403, 0, 620, 411, 370, 505, 405, 585, + 436, 0, 613, 680, 614, 525, 526, 741, 746, 742, + 743, 745, 766, 495, 425, 431, 542, 446, 468, 528, + 584, 503, 533, 368, 573, 544, 474, 665, 693, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 325, 326, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 722, 721, 720, 719, + 718, 717, 716, 715, 0, 0, 662, 385, 785, 779, + 786, 767, 770, 769, 744, 0, 341, 642, 466, 520, + 402, 709, 710, 0, 765, 284, 285, 286, 287, 288, + 289, 290, 291, 332, 292, 293, 294, 295, 296, 297, + 298, 303, 304, 305, 306, 307, 308, 309, 310, 712, + 301, 302, 311, 312, 313, 314, 315, 316, 317, 318, + 319, 320, 321, 322, 323, 324, 0, 0, 0, 0, + 333, 771, 772, 773, 774, 775, 0, 0, 334, 335, + 336, 0, 0, 299, 300, 327, 552, 328, 329, 330, + 331, 0, 0, 591, 592, 593, 616, 0, 594, 575, + 641, 412, 342, 556, 583, 782, 783, 0, 0, 0, + 0, 0, 0, 0, 692, 703, 739, 740, 0, 753, + 754, 756, 758, 757, 760, 549, 550, 768, 0, 0, + 437, 358, 359, 496, 497, 535, 0, 438, 439, 440, + 444, 441, 442, 443, 762, 763, 764, 761, 470, 534, + 557, 543, 781, 483, 0, 789, 631, 632, 790, 749, + 748, 343, 0, 337, 338, 706, 707, 0, 0, 537, + 482, 638, 502, 0, 0, 647, 681, 670, 759, 634, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 395, 0, 0, 465, 685, 666, 677, 667, 508, 652, + 653, 654, 661, 407, 655, 791, 657, 626, 658, 627, + 659, 660, 0, 684, 633, 545, 484, 0, 701, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 268, 0, 0, 4557, 0, 0, 0, 366, 269, 628, + 755, 630, 629, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 369, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 546, 574, 0, 588, 0, 434, 435, + 0, 0, 0, 0, 0, 0, 0, 350, 553, 571, + 367, 540, 586, 372, 548, 564, 362, 501, 536, 0, + 0, 352, 569, 547, 479, 351, 0, 529, 392, 409, + 389, 499, 0, 0, 568, 599, 388, 589, 0, 579, + 354, 0, 578, 498, 471, 565, 570, 480, 473, 0, + 353, 567, 478, 472, 448, 399, 615, 449, 450, 451, + 452, 453, 454, 413, 514, 469, 515, 414, 486, 485, + 487, 415, 416, 417, 418, 419, 420, 421, 422, 423, + 424, 0, 0, 0, 0, 0, 610, 611, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 747, 0, 0, 752, 0, 582, 0, + 0, 0, 0, 0, 0, 551, 0, 0, 455, 0, + 0, 0, 600, 0, 532, 504, 793, 0, 0, 530, + 467, 566, 516, 572, 554, 580, 523, 517, 344, 555, + 391, 481, 363, 365, 780, 393, 396, 400, 401, 492, + 493, 510, 539, 558, 559, 560, 390, 377, 531, 378, + 410, 379, 345, 382, 381, 383, 541, 384, 347, 511, + 563, 0, 406, 527, 476, 348, 475, 512, 562, 561, + 364, 590, 597, 598, 689, 0, 603, 794, 795, 796, + 612, 0, 518, 360, 357, 0, 0, 0, 386, 513, + 373, 375, 376, 374, 507, 509, 617, 618, 619, 621, + 0, 622, 623, 0, 0, 0, 0, 624, 690, 708, + 674, 643, 605, 698, 640, 644, 645, 427, 428, 429, + 430, 711, 0, 0, 0, 596, 456, 457, 0, 398, + 397, 477, 349, 0, 0, 445, 426, 519, 355, 394, + 447, 432, 458, 459, 460, 461, 462, 0, 463, 464, + 404, 339, 340, 788, 387, 500, 713, 750, 751, 635, + 0, 699, 636, 646, 380, 671, 683, 682, 494, 595, + 0, 694, 697, 625, 787, 0, 691, 705, 792, 704, + 784, 506, 0, 538, 702, 649, 0, 695, 668, 669, + 0, 696, 664, 700, 356, 433, 524, 581, 0, 637, + 0, 606, 609, 639, 726, 727, 728, 346, 608, 730, + 731, 732, 733, 734, 735, 736, 729, 577, 672, 648, + 675, 587, 651, 650, 0, 0, 686, 604, 687, 688, + 488, 489, 490, 491, 408, 714, 371, 607, 521, 522, + 0, 673, 0, 0, 0, 0, 0, 0, 0, 0, + 678, 679, 676, 797, 0, 737, 738, 0, 0, 601, + 602, 403, 0, 620, 411, 370, 505, 405, 585, 436, + 0, 613, 680, 614, 525, 526, 741, 746, 742, 743, + 745, 766, 495, 425, 431, 542, 446, 468, 528, 584, + 503, 533, 368, 573, 544, 474, 665, 693, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 325, 326, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 718, 717, 716, 715, 714, - 713, 712, 711, 0, 0, 658, 384, 781, 775, 782, - 763, 766, 765, 740, 0, 341, 638, 464, 518, 401, - 705, 706, 0, 761, 284, 285, 286, 287, 288, 289, + 0, 0, 0, 0, 0, 722, 721, 720, 719, 718, + 717, 716, 715, 0, 0, 662, 385, 785, 779, 786, + 767, 770, 769, 744, 0, 341, 642, 466, 520, 402, + 709, 710, 0, 765, 284, 285, 286, 287, 288, 289, 290, 291, 332, 292, 293, 294, 295, 296, 297, 298, - 303, 304, 305, 306, 307, 308, 309, 310, 708, 301, + 303, 304, 305, 306, 307, 308, 309, 310, 712, 301, 302, 311, 312, 313, 314, 315, 316, 317, 318, 319, 320, 321, 322, 323, 324, 0, 0, 0, 0, 333, - 767, 768, 769, 770, 771, 0, 0, 334, 335, 336, - 0, 0, 299, 300, 327, 549, 328, 329, 330, 331, - 0, 0, 587, 588, 589, 612, 0, 590, 572, 637, - 411, 342, 553, 579, 778, 779, 0, 0, 0, 0, - 0, 0, 0, 688, 699, 735, 736, 0, 749, 750, - 752, 754, 753, 756, 546, 547, 764, 0, 0, 435, - 357, 358, 494, 495, 532, 0, 436, 437, 438, 442, - 439, 440, 441, 758, 759, 760, 757, 468, 531, 554, - 540, 777, 481, 0, 785, 627, 628, 786, 745, 744, - 343, 0, 337, 338, 702, 703, 0, 0, 534, 480, - 634, 500, 0, 0, 643, 677, 666, 755, 630, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 394, - 0, 0, 463, 681, 662, 673, 663, 506, 648, 649, - 650, 657, 406, 651, 787, 653, 622, 654, 623, 655, - 656, 0, 680, 629, 542, 482, 0, 697, 0, 0, + 771, 772, 773, 774, 775, 0, 0, 334, 335, 336, + 0, 0, 299, 300, 327, 552, 328, 329, 330, 331, + 0, 0, 591, 592, 593, 616, 0, 594, 575, 641, + 412, 342, 556, 583, 782, 783, 0, 0, 0, 0, + 0, 0, 0, 692, 703, 739, 740, 0, 753, 754, + 756, 758, 757, 760, 549, 550, 768, 0, 0, 437, + 358, 359, 496, 497, 535, 0, 438, 439, 440, 444, + 441, 442, 443, 762, 763, 764, 761, 470, 534, 557, + 543, 781, 483, 0, 789, 631, 632, 790, 749, 748, + 343, 0, 337, 338, 706, 707, 0, 0, 537, 482, + 638, 502, 0, 0, 647, 681, 670, 759, 634, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 395, + 0, 0, 465, 685, 666, 677, 667, 508, 652, 653, + 654, 661, 407, 655, 791, 657, 626, 658, 627, 659, + 660, 0, 684, 633, 545, 484, 0, 701, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 268, - 0, 0, 4553, 0, 0, 0, 365, 269, 624, 751, - 626, 625, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 368, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 543, 571, 0, 584, 0, 432, 433, 0, - 0, 0, 0, 0, 0, 0, 350, 550, 568, 366, - 537, 582, 371, 545, 561, 361, 499, 533, 0, 0, - 352, 566, 544, 477, 351, 0, 526, 391, 408, 388, - 497, 0, 0, 565, 595, 387, 585, 0, 576, 354, - 0, 575, 496, 469, 562, 567, 478, 471, 0, 353, - 564, 476, 470, 446, 398, 611, 447, 448, 449, 450, - 451, 452, 412, 512, 467, 513, 413, 484, 483, 485, - 414, 415, 416, 417, 418, 419, 420, 421, 422, 423, - 0, 0, 0, 0, 0, 606, 607, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 743, 0, 0, 748, 0, 578, 0, 0, - 0, 0, 0, 0, 548, 0, 0, 453, 0, 0, - 0, 596, 0, 529, 502, 789, 0, 0, 527, 465, - 563, 514, 569, 551, 577, 521, 515, 344, 552, 390, - 479, 362, 364, 776, 392, 395, 399, 400, 490, 491, - 508, 536, 555, 556, 557, 389, 376, 528, 377, 409, - 378, 345, 381, 380, 382, 538, 383, 347, 509, 560, - 0, 405, 524, 474, 348, 473, 510, 559, 558, 363, - 586, 593, 594, 685, 0, 599, 790, 791, 792, 608, - 0, 516, 359, 356, 0, 0, 0, 385, 511, 372, - 374, 375, 373, 505, 507, 613, 614, 615, 617, 0, - 618, 619, 0, 0, 0, 0, 620, 686, 704, 670, - 639, 601, 694, 636, 640, 641, 426, 427, 428, 429, - 707, 0, 0, 0, 592, 454, 455, 0, 397, 396, - 475, 349, 0, 0, 443, 425, 517, 355, 393, 445, - 431, 456, 457, 458, 459, 460, 0, 461, 462, 403, - 339, 340, 784, 386, 498, 709, 746, 747, 631, 0, - 695, 632, 642, 379, 667, 679, 678, 492, 591, 0, - 690, 693, 621, 783, 0, 687, 701, 788, 700, 780, - 504, 0, 535, 698, 645, 0, 691, 664, 665, 0, - 692, 660, 696, 0, 633, 0, 602, 605, 635, 722, - 723, 724, 346, 604, 726, 727, 728, 729, 730, 731, - 732, 725, 574, 668, 644, 671, 583, 647, 646, 0, - 0, 682, 600, 683, 684, 486, 487, 488, 489, 407, - 710, 370, 603, 519, 520, 0, 669, 0, 0, 0, - 0, 0, 0, 0, 0, 674, 675, 672, 793, 0, - 733, 734, 0, 0, 597, 598, 402, 0, 616, 410, - 369, 503, 404, 581, 434, 0, 609, 676, 610, 522, - 523, 737, 742, 738, 739, 741, 762, 493, 424, 430, - 539, 444, 466, 525, 580, 501, 530, 367, 570, 541, - 472, 661, 689, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 325, 326, 0, 0, 0, 0, + 0, 0, 4392, 0, 0, 0, 366, 269, 628, 755, + 630, 629, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 369, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 546, 574, 0, 588, 0, 434, 435, 0, + 0, 0, 0, 0, 0, 0, 350, 553, 571, 367, + 540, 586, 372, 548, 564, 362, 501, 536, 0, 0, + 352, 569, 547, 479, 351, 0, 529, 392, 409, 389, + 499, 0, 0, 568, 599, 388, 589, 0, 579, 354, + 0, 578, 498, 471, 565, 570, 480, 473, 0, 353, + 567, 478, 472, 448, 399, 615, 449, 450, 451, 452, + 453, 454, 413, 514, 469, 515, 414, 486, 485, 487, + 415, 416, 417, 418, 419, 420, 421, 422, 423, 424, + 0, 0, 0, 0, 0, 610, 611, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 747, 0, 0, 752, 0, 582, 0, 0, + 0, 0, 0, 0, 551, 0, 0, 455, 0, 0, + 0, 600, 0, 532, 504, 793, 0, 0, 530, 467, + 566, 516, 572, 554, 580, 523, 517, 344, 555, 391, + 481, 363, 365, 780, 393, 396, 400, 401, 492, 493, + 510, 539, 558, 559, 560, 390, 377, 531, 378, 410, + 379, 345, 382, 381, 383, 541, 384, 347, 511, 563, + 0, 406, 527, 476, 348, 475, 512, 562, 561, 364, + 590, 597, 598, 689, 0, 603, 794, 795, 796, 612, + 0, 518, 360, 357, 0, 0, 0, 386, 513, 373, + 375, 376, 374, 507, 509, 617, 618, 619, 621, 0, + 622, 623, 0, 0, 0, 0, 624, 690, 708, 674, + 643, 605, 698, 640, 644, 645, 427, 428, 429, 430, + 711, 0, 0, 0, 596, 456, 457, 0, 398, 397, + 477, 349, 0, 0, 445, 426, 519, 355, 394, 447, + 432, 458, 459, 460, 461, 462, 0, 463, 464, 404, + 339, 340, 788, 387, 500, 713, 750, 751, 635, 0, + 699, 636, 646, 380, 671, 683, 682, 494, 595, 0, + 694, 697, 625, 787, 0, 691, 705, 792, 704, 784, + 506, 0, 538, 702, 649, 0, 695, 668, 669, 0, + 696, 664, 700, 356, 433, 524, 581, 0, 637, 0, + 606, 609, 639, 726, 727, 728, 346, 608, 730, 731, + 732, 733, 734, 735, 736, 729, 577, 672, 648, 675, + 587, 651, 650, 0, 0, 686, 604, 687, 688, 488, + 489, 490, 491, 408, 714, 371, 607, 521, 522, 0, + 673, 0, 0, 0, 0, 0, 0, 0, 0, 678, + 679, 676, 797, 0, 737, 738, 0, 0, 601, 602, + 403, 0, 620, 411, 370, 505, 405, 585, 436, 0, + 613, 680, 614, 525, 526, 741, 746, 742, 743, 745, + 766, 495, 425, 431, 542, 446, 468, 528, 584, 503, + 533, 368, 573, 544, 474, 665, 693, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 325, 326, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 718, 717, 716, 715, 714, 713, 712, 711, 0, 0, - 658, 384, 781, 775, 782, 763, 766, 765, 740, 0, - 341, 638, 464, 518, 401, 705, 706, 0, 761, 284, - 285, 286, 287, 288, 289, 290, 291, 332, 292, 293, - 294, 295, 296, 297, 298, 303, 304, 305, 306, 307, - 308, 309, 310, 708, 301, 302, 311, 312, 313, 314, - 315, 316, 317, 318, 319, 320, 321, 322, 323, 324, - 0, 0, 0, 0, 333, 767, 768, 769, 770, 771, - 0, 0, 334, 335, 336, 0, 0, 299, 300, 327, - 549, 328, 329, 330, 331, 0, 0, 587, 588, 589, - 612, 0, 590, 572, 637, 411, 342, 553, 579, 778, - 779, 0, 0, 0, 0, 0, 0, 0, 688, 699, - 735, 736, 0, 749, 750, 752, 754, 753, 756, 546, - 547, 764, 0, 0, 435, 357, 358, 494, 495, 532, - 0, 436, 437, 438, 442, 439, 440, 441, 758, 759, - 760, 757, 468, 531, 554, 540, 777, 481, 0, 785, - 627, 628, 786, 745, 744, 343, 0, 337, 338, 702, - 703, 0, 0, 534, 480, 634, 500, 0, 0, 643, - 677, 666, 755, 630, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 394, 0, 0, 463, 681, 662, - 673, 663, 506, 648, 649, 650, 657, 406, 651, 787, - 653, 622, 654, 623, 655, 656, 0, 680, 629, 542, - 482, 0, 697, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 268, 0, 0, 4388, 0, 0, - 0, 365, 269, 624, 751, 626, 625, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 368, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 543, 571, 0, - 584, 0, 432, 433, 0, 0, 0, 0, 0, 0, - 0, 350, 550, 568, 366, 537, 582, 371, 545, 561, - 361, 499, 533, 0, 0, 352, 566, 544, 477, 351, - 0, 526, 391, 408, 388, 497, 0, 0, 565, 595, - 387, 585, 0, 576, 354, 0, 575, 496, 469, 562, - 567, 478, 471, 0, 353, 564, 476, 470, 446, 398, - 611, 447, 448, 449, 450, 451, 452, 412, 512, 467, - 513, 413, 484, 483, 485, 414, 415, 416, 417, 418, - 419, 420, 421, 422, 423, 0, 0, 0, 0, 0, - 606, 607, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 743, 0, 0, - 748, 0, 578, 0, 0, 0, 0, 0, 0, 548, - 0, 0, 453, 0, 0, 0, 596, 0, 529, 502, - 789, 0, 0, 527, 465, 563, 514, 569, 551, 577, - 521, 515, 344, 552, 390, 479, 362, 364, 776, 392, - 395, 399, 400, 490, 491, 508, 536, 555, 556, 557, - 389, 376, 528, 377, 409, 378, 345, 381, 380, 382, - 538, 383, 347, 509, 560, 0, 405, 524, 474, 348, - 473, 510, 559, 558, 363, 586, 593, 594, 685, 0, - 599, 790, 791, 792, 608, 0, 516, 359, 356, 0, - 0, 0, 385, 511, 372, 374, 375, 373, 505, 507, - 613, 614, 615, 617, 0, 618, 619, 0, 0, 0, - 0, 620, 686, 704, 670, 639, 601, 694, 636, 640, - 641, 426, 427, 428, 429, 707, 0, 0, 0, 592, - 454, 455, 0, 397, 396, 475, 349, 0, 0, 443, - 425, 517, 355, 393, 445, 431, 456, 457, 458, 459, - 460, 0, 461, 462, 403, 339, 340, 784, 386, 498, - 709, 746, 747, 631, 0, 695, 632, 642, 379, 667, - 679, 678, 492, 591, 0, 690, 693, 621, 783, 0, - 687, 701, 788, 700, 780, 504, 0, 535, 698, 645, - 0, 691, 664, 665, 0, 692, 660, 696, 0, 633, - 0, 602, 605, 635, 722, 723, 724, 346, 604, 726, - 727, 728, 729, 730, 731, 732, 725, 574, 668, 644, - 671, 583, 647, 646, 0, 0, 682, 600, 683, 684, - 486, 487, 488, 489, 407, 710, 370, 603, 519, 520, - 0, 669, 0, 0, 0, 0, 0, 0, 0, 0, - 674, 675, 672, 793, 0, 733, 734, 0, 0, 597, - 598, 402, 0, 616, 410, 369, 503, 404, 581, 434, - 0, 609, 676, 610, 522, 523, 737, 742, 738, 739, - 741, 762, 493, 424, 430, 539, 444, 466, 525, 580, - 501, 530, 367, 570, 541, 472, 661, 689, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 325, - 326, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 718, 717, 716, 715, 714, - 713, 712, 711, 0, 0, 658, 384, 781, 775, 782, - 763, 766, 765, 740, 0, 341, 638, 464, 518, 401, - 705, 706, 0, 761, 284, 285, 286, 287, 288, 289, - 290, 291, 332, 292, 293, 294, 295, 296, 297, 298, - 303, 304, 305, 306, 307, 308, 309, 310, 708, 301, - 302, 311, 312, 313, 314, 315, 316, 317, 318, 319, - 320, 321, 322, 323, 324, 0, 0, 0, 0, 333, - 767, 768, 769, 770, 771, 0, 0, 334, 335, 336, - 0, 0, 299, 300, 327, 549, 328, 329, 330, 331, - 0, 0, 587, 588, 589, 612, 0, 590, 572, 637, - 411, 342, 553, 579, 778, 779, 0, 0, 0, 0, - 0, 0, 0, 688, 699, 735, 736, 0, 749, 750, - 752, 754, 753, 756, 546, 547, 764, 0, 0, 435, - 357, 358, 494, 495, 532, 0, 436, 437, 438, 442, - 439, 440, 441, 758, 759, 760, 757, 468, 531, 554, - 540, 777, 481, 0, 785, 627, 628, 786, 745, 744, - 343, 0, 337, 338, 702, 703, 0, 0, 534, 480, - 634, 500, 0, 0, 643, 677, 666, 755, 630, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 394, - 0, 0, 463, 681, 662, 673, 663, 506, 648, 649, - 650, 657, 406, 651, 787, 653, 622, 654, 623, 655, - 656, 0, 680, 629, 542, 482, 0, 697, 0, 0, + 0, 0, 0, 0, 722, 721, 720, 719, 718, 717, + 716, 715, 0, 0, 662, 385, 785, 779, 786, 767, + 770, 769, 744, 0, 341, 642, 466, 520, 402, 709, + 710, 0, 765, 284, 285, 286, 287, 288, 289, 290, + 291, 332, 292, 293, 294, 295, 296, 297, 298, 303, + 304, 305, 306, 307, 308, 309, 310, 712, 301, 302, + 311, 312, 313, 314, 315, 316, 317, 318, 319, 320, + 321, 322, 323, 324, 0, 0, 0, 0, 333, 771, + 772, 773, 774, 775, 0, 0, 334, 335, 336, 0, + 0, 299, 300, 327, 552, 328, 329, 330, 331, 0, + 0, 591, 592, 593, 616, 0, 594, 575, 641, 412, + 342, 556, 583, 782, 783, 0, 0, 0, 0, 0, + 0, 0, 692, 703, 739, 740, 0, 753, 754, 756, + 758, 757, 760, 549, 550, 768, 0, 0, 437, 358, + 359, 496, 497, 535, 0, 438, 439, 440, 444, 441, + 442, 443, 762, 763, 764, 761, 470, 534, 557, 543, + 781, 483, 0, 789, 631, 632, 790, 749, 748, 343, + 0, 337, 338, 706, 707, 0, 0, 537, 482, 638, + 502, 0, 0, 647, 681, 670, 759, 634, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 395, 0, + 0, 465, 685, 666, 677, 667, 508, 652, 653, 654, + 661, 407, 655, 791, 657, 626, 658, 627, 659, 660, + 0, 684, 633, 545, 484, 0, 701, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 268, - 0, 0, 3626, 0, 0, 0, 365, 269, 624, 751, - 626, 625, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 368, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 543, 571, 0, 584, 0, 432, 433, 0, - 0, 0, 0, 0, 0, 0, 350, 550, 568, 366, - 537, 582, 371, 545, 561, 361, 499, 533, 0, 0, - 352, 566, 544, 477, 351, 0, 526, 391, 408, 388, - 497, 0, 0, 565, 595, 387, 585, 0, 576, 354, - 0, 575, 496, 469, 562, 567, 478, 471, 0, 353, - 564, 476, 470, 446, 398, 611, 447, 448, 449, 450, - 451, 452, 412, 512, 467, 513, 413, 484, 483, 485, - 414, 415, 416, 417, 418, 419, 420, 421, 422, 423, - 0, 0, 0, 0, 0, 606, 607, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 743, 0, 0, 748, 0, 578, 0, 0, - 0, 0, 0, 0, 548, 0, 0, 453, 0, 0, - 0, 596, 0, 529, 502, 789, 0, 0, 527, 465, - 563, 514, 569, 551, 577, 521, 515, 344, 552, 390, - 479, 362, 364, 776, 392, 395, 399, 400, 490, 491, - 508, 536, 555, 556, 557, 389, 376, 528, 377, 409, - 378, 345, 381, 380, 382, 538, 383, 347, 509, 560, - 0, 405, 524, 474, 348, 473, 510, 559, 558, 363, - 586, 593, 594, 685, 0, 599, 790, 791, 792, 608, - 0, 516, 359, 356, 0, 0, 0, 385, 511, 372, - 374, 375, 373, 505, 507, 613, 614, 615, 617, 0, - 618, 619, 0, 0, 0, 0, 620, 686, 704, 670, - 639, 601, 694, 636, 640, 641, 426, 427, 428, 429, - 707, 0, 0, 0, 592, 454, 455, 0, 397, 396, - 475, 349, 0, 0, 443, 425, 517, 355, 393, 445, - 431, 456, 457, 458, 459, 460, 0, 461, 462, 403, - 339, 340, 784, 386, 498, 709, 746, 747, 631, 0, - 695, 632, 642, 379, 667, 679, 678, 492, 591, 0, - 690, 693, 621, 783, 0, 687, 701, 788, 700, 780, - 504, 0, 535, 698, 645, 0, 691, 664, 665, 0, - 692, 660, 696, 0, 633, 0, 602, 605, 635, 722, - 723, 724, 346, 604, 726, 727, 728, 729, 730, 731, - 732, 725, 574, 668, 644, 671, 583, 647, 646, 0, - 0, 682, 600, 683, 684, 486, 487, 488, 489, 407, - 710, 370, 603, 519, 520, 0, 669, 0, 0, 0, - 0, 0, 0, 0, 0, 674, 675, 672, 793, 0, - 733, 734, 0, 0, 597, 598, 402, 0, 616, 410, - 369, 503, 404, 581, 434, 0, 609, 676, 610, 522, - 523, 737, 742, 738, 739, 741, 762, 493, 424, 430, - 539, 444, 466, 525, 580, 501, 530, 367, 570, 541, - 472, 661, 689, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 325, 326, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 268, 0, + 0, 3630, 0, 0, 0, 366, 269, 628, 755, 630, + 629, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 369, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 546, 574, 0, 588, 0, 434, 435, 0, 0, + 0, 0, 0, 0, 0, 350, 553, 571, 367, 540, + 586, 372, 548, 564, 362, 501, 536, 0, 0, 352, + 569, 547, 479, 351, 0, 529, 392, 409, 389, 499, + 0, 0, 568, 599, 388, 589, 0, 579, 354, 0, + 578, 498, 471, 565, 570, 480, 473, 0, 353, 567, + 478, 472, 448, 399, 615, 449, 450, 451, 452, 453, + 454, 413, 514, 469, 515, 414, 486, 485, 487, 415, + 416, 417, 418, 419, 420, 421, 422, 423, 424, 0, + 0, 0, 0, 0, 610, 611, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 747, 0, 0, 752, 0, 582, 0, 0, 0, + 0, 0, 0, 551, 0, 0, 455, 0, 0, 0, + 600, 0, 532, 504, 793, 0, 0, 530, 467, 566, + 516, 572, 554, 580, 523, 517, 344, 555, 391, 481, + 363, 365, 780, 393, 396, 400, 401, 492, 493, 510, + 539, 558, 559, 560, 390, 377, 531, 378, 410, 379, + 345, 382, 381, 383, 541, 384, 347, 511, 563, 0, + 406, 527, 476, 348, 475, 512, 562, 561, 364, 590, + 597, 598, 689, 0, 603, 794, 795, 796, 612, 0, + 518, 360, 357, 0, 0, 0, 386, 513, 373, 375, + 376, 374, 507, 509, 617, 618, 619, 621, 0, 622, + 623, 0, 0, 0, 0, 624, 690, 708, 674, 643, + 605, 698, 640, 644, 645, 427, 428, 429, 430, 711, + 0, 0, 0, 596, 456, 457, 0, 398, 397, 477, + 349, 0, 0, 445, 426, 519, 355, 394, 447, 432, + 458, 459, 460, 461, 462, 0, 463, 464, 404, 339, + 340, 788, 387, 500, 713, 750, 751, 635, 0, 699, + 636, 646, 380, 671, 683, 682, 494, 595, 0, 694, + 697, 625, 787, 0, 691, 705, 792, 704, 784, 506, + 0, 538, 702, 649, 0, 695, 668, 669, 0, 696, + 664, 700, 356, 433, 524, 581, 0, 637, 0, 606, + 609, 639, 726, 727, 728, 346, 608, 730, 731, 732, + 733, 734, 735, 736, 729, 577, 672, 648, 675, 587, + 651, 650, 0, 0, 686, 604, 687, 688, 488, 489, + 490, 491, 408, 714, 371, 607, 521, 522, 0, 673, + 0, 0, 0, 0, 0, 0, 0, 0, 678, 679, + 676, 797, 0, 737, 738, 0, 0, 601, 602, 403, + 0, 620, 411, 370, 505, 405, 585, 436, 0, 613, + 680, 614, 525, 526, 741, 746, 742, 743, 745, 766, + 495, 425, 431, 542, 446, 468, 528, 584, 503, 533, + 368, 573, 544, 474, 665, 693, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 325, 326, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 718, 717, 716, 715, 714, 713, 712, 711, 0, 0, - 658, 384, 781, 775, 782, 763, 766, 765, 740, 0, - 341, 638, 464, 518, 401, 705, 706, 0, 761, 284, - 285, 286, 287, 288, 289, 290, 291, 332, 292, 293, - 294, 295, 296, 297, 298, 303, 304, 305, 306, 307, - 308, 309, 310, 708, 301, 302, 311, 312, 313, 314, - 315, 316, 317, 318, 319, 320, 321, 322, 323, 324, - 0, 0, 0, 0, 333, 767, 768, 769, 770, 771, - 0, 0, 334, 335, 336, 0, 0, 299, 300, 327, - 549, 328, 329, 330, 331, 0, 0, 587, 588, 589, - 612, 0, 590, 572, 637, 411, 342, 553, 579, 778, - 779, 0, 0, 0, 0, 0, 0, 0, 688, 699, - 735, 736, 0, 749, 750, 752, 754, 753, 756, 546, - 547, 764, 0, 0, 435, 357, 358, 494, 495, 532, - 0, 436, 437, 438, 442, 439, 440, 441, 758, 759, - 760, 757, 468, 531, 554, 540, 777, 481, 0, 785, - 627, 628, 786, 745, 744, 343, 0, 337, 338, 702, - 703, 0, 0, 534, 480, 634, 500, 0, 0, 643, - 677, 666, 755, 630, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 394, 0, 0, 463, 681, 662, - 673, 663, 506, 648, 649, 650, 657, 406, 651, 787, - 653, 622, 654, 623, 655, 656, 0, 680, 629, 542, - 482, 0, 697, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 268, 0, 0, 3252, 0, 0, - 0, 365, 269, 624, 751, 626, 625, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 368, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 543, 571, 0, - 584, 0, 432, 433, 0, 0, 0, 0, 0, 0, - 0, 350, 550, 568, 366, 537, 582, 371, 545, 561, - 361, 499, 533, 0, 0, 352, 566, 544, 477, 351, - 0, 526, 391, 408, 388, 497, 0, 0, 565, 595, - 387, 585, 0, 576, 354, 0, 575, 496, 469, 562, - 567, 478, 471, 0, 353, 564, 476, 470, 446, 398, - 611, 447, 448, 449, 450, 451, 452, 412, 512, 467, - 513, 413, 484, 483, 485, 414, 415, 416, 417, 418, - 419, 420, 421, 422, 423, 0, 0, 0, 0, 0, - 606, 607, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 743, 0, 0, - 748, 0, 578, 0, 0, 0, 0, 0, 0, 548, - 0, 0, 453, 0, 0, 0, 596, 0, 529, 502, - 789, 0, 0, 527, 465, 563, 514, 569, 551, 577, - 521, 515, 344, 552, 390, 479, 362, 364, 776, 392, - 395, 399, 400, 490, 491, 508, 536, 555, 556, 557, - 389, 376, 528, 377, 409, 378, 345, 381, 380, 382, - 538, 383, 347, 509, 560, 0, 405, 524, 474, 348, - 473, 510, 559, 558, 363, 586, 593, 594, 685, 0, - 599, 790, 791, 792, 608, 0, 516, 359, 356, 0, - 0, 0, 385, 511, 372, 374, 375, 373, 505, 507, - 613, 614, 615, 617, 0, 618, 619, 0, 0, 0, - 0, 620, 686, 704, 670, 639, 601, 694, 636, 640, - 641, 426, 427, 428, 429, 707, 0, 0, 0, 592, - 454, 455, 0, 397, 396, 475, 349, 0, 0, 443, - 425, 517, 355, 393, 445, 431, 456, 457, 458, 459, - 460, 0, 461, 462, 403, 339, 340, 784, 386, 498, - 709, 746, 747, 631, 0, 695, 632, 642, 379, 667, - 679, 678, 492, 591, 0, 690, 693, 621, 783, 0, - 687, 701, 788, 700, 780, 504, 0, 535, 698, 645, - 0, 691, 664, 665, 0, 692, 660, 696, 0, 633, - 0, 602, 605, 635, 722, 723, 724, 346, 604, 726, - 727, 728, 729, 730, 731, 732, 725, 574, 668, 644, - 671, 583, 647, 646, 0, 0, 682, 600, 683, 684, - 486, 487, 488, 489, 407, 710, 370, 603, 519, 520, - 0, 669, 0, 0, 0, 0, 0, 0, 0, 0, - 674, 675, 672, 793, 0, 733, 734, 0, 0, 597, - 598, 402, 0, 616, 410, 369, 503, 404, 581, 434, - 0, 609, 676, 610, 522, 523, 737, 742, 738, 739, - 741, 762, 493, 424, 430, 539, 444, 466, 525, 580, - 501, 530, 367, 570, 541, 472, 661, 689, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 325, - 326, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 718, 717, 716, 715, 714, - 713, 712, 711, 0, 0, 658, 384, 781, 775, 782, - 763, 766, 765, 740, 0, 341, 638, 464, 518, 401, - 705, 706, 0, 761, 284, 285, 286, 287, 288, 289, - 290, 291, 332, 292, 293, 294, 295, 296, 297, 298, - 303, 304, 305, 306, 307, 308, 309, 310, 708, 301, - 302, 311, 312, 313, 314, 315, 316, 317, 318, 319, - 320, 321, 322, 323, 324, 0, 0, 0, 0, 333, - 767, 768, 769, 770, 771, 0, 0, 334, 335, 336, - 0, 0, 299, 300, 327, 549, 328, 329, 330, 331, - 0, 0, 587, 588, 589, 612, 0, 590, 572, 637, - 411, 342, 553, 579, 778, 779, 0, 0, 0, 0, - 0, 0, 0, 688, 699, 735, 736, 0, 749, 750, - 752, 754, 753, 756, 546, 547, 764, 0, 0, 435, - 357, 358, 494, 495, 532, 0, 436, 437, 438, 442, - 439, 440, 441, 758, 759, 760, 757, 468, 531, 554, - 540, 777, 481, 0, 785, 627, 628, 786, 745, 744, - 343, 0, 337, 338, 702, 703, 0, 0, 534, 480, - 634, 500, 0, 0, 643, 677, 666, 755, 630, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 394, - 0, 0, 463, 681, 662, 673, 663, 506, 648, 649, - 650, 657, 406, 651, 787, 653, 622, 654, 623, 655, - 656, 0, 680, 629, 542, 482, 0, 697, 0, 0, + 0, 0, 0, 722, 721, 720, 719, 718, 717, 716, + 715, 0, 0, 662, 385, 785, 779, 786, 767, 770, + 769, 744, 0, 341, 642, 466, 520, 402, 709, 710, + 0, 765, 284, 285, 286, 287, 288, 289, 290, 291, + 332, 292, 293, 294, 295, 296, 297, 298, 303, 304, + 305, 306, 307, 308, 309, 310, 712, 301, 302, 311, + 312, 313, 314, 315, 316, 317, 318, 319, 320, 321, + 322, 323, 324, 0, 0, 0, 0, 333, 771, 772, + 773, 774, 775, 0, 0, 334, 335, 336, 0, 0, + 299, 300, 327, 552, 328, 329, 330, 331, 0, 0, + 591, 592, 593, 616, 0, 594, 575, 641, 412, 342, + 556, 583, 782, 783, 0, 0, 0, 0, 0, 0, + 0, 692, 703, 739, 740, 0, 753, 754, 756, 758, + 757, 760, 549, 550, 768, 0, 0, 437, 358, 359, + 496, 497, 535, 0, 438, 439, 440, 444, 441, 442, + 443, 762, 763, 764, 761, 470, 534, 557, 543, 781, + 483, 0, 789, 631, 632, 790, 749, 748, 343, 0, + 337, 338, 706, 707, 0, 0, 537, 482, 638, 502, + 0, 0, 647, 681, 670, 759, 634, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 395, 0, 0, + 465, 685, 666, 677, 667, 508, 652, 653, 654, 661, + 407, 655, 791, 657, 626, 658, 627, 659, 660, 0, + 684, 633, 545, 484, 0, 701, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 268, 0, 0, + 3256, 0, 0, 0, 366, 269, 628, 755, 630, 629, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 369, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 546, 574, 0, 588, 0, 434, 435, 0, 0, 0, + 0, 0, 0, 0, 350, 553, 571, 367, 540, 586, + 372, 548, 564, 362, 501, 536, 0, 0, 352, 569, + 547, 479, 351, 0, 529, 392, 409, 389, 499, 0, + 0, 568, 599, 388, 589, 0, 579, 354, 0, 578, + 498, 471, 565, 570, 480, 473, 0, 353, 567, 478, + 472, 448, 399, 615, 449, 450, 451, 452, 453, 454, + 413, 514, 469, 515, 414, 486, 485, 487, 415, 416, + 417, 418, 419, 420, 421, 422, 423, 424, 0, 0, + 0, 0, 0, 610, 611, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 747, 0, 0, 752, 0, 582, 0, 0, 0, 0, + 0, 0, 551, 0, 0, 455, 0, 0, 0, 600, + 0, 532, 504, 793, 0, 0, 530, 467, 566, 516, + 572, 554, 580, 523, 517, 344, 555, 391, 481, 363, + 365, 780, 393, 396, 400, 401, 492, 493, 510, 539, + 558, 559, 560, 390, 377, 531, 378, 410, 379, 345, + 382, 381, 383, 541, 384, 347, 511, 563, 0, 406, + 527, 476, 348, 475, 512, 562, 561, 364, 590, 597, + 598, 689, 0, 603, 794, 795, 796, 612, 0, 518, + 360, 357, 0, 0, 0, 386, 513, 373, 375, 376, + 374, 507, 509, 617, 618, 619, 621, 0, 622, 623, + 0, 0, 0, 0, 624, 690, 708, 674, 643, 605, + 698, 640, 644, 645, 427, 428, 429, 430, 711, 0, + 0, 0, 596, 456, 457, 0, 398, 397, 477, 349, + 0, 0, 445, 426, 519, 355, 394, 447, 432, 458, + 459, 460, 461, 462, 0, 463, 464, 404, 339, 340, + 788, 387, 500, 713, 750, 751, 635, 0, 699, 636, + 646, 380, 671, 683, 682, 494, 595, 0, 694, 697, + 625, 787, 0, 691, 705, 792, 704, 784, 506, 0, + 538, 702, 649, 0, 695, 668, 669, 0, 696, 664, + 700, 356, 433, 524, 581, 0, 637, 0, 606, 609, + 639, 726, 727, 728, 346, 608, 730, 731, 732, 733, + 734, 735, 736, 729, 577, 672, 648, 675, 587, 651, + 650, 0, 0, 686, 604, 687, 688, 488, 489, 490, + 491, 408, 714, 371, 607, 521, 522, 0, 673, 0, + 0, 0, 0, 0, 0, 0, 0, 678, 679, 676, + 797, 0, 737, 738, 0, 0, 601, 602, 403, 0, + 620, 411, 370, 505, 405, 585, 436, 0, 613, 680, + 614, 525, 526, 741, 746, 742, 743, 745, 766, 495, + 425, 431, 542, 446, 468, 528, 584, 503, 533, 368, + 573, 544, 474, 665, 693, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 325, 326, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 268, - 0, 0, 3620, 0, 0, 0, 365, 269, 624, 751, - 626, 625, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 368, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 543, 571, 0, 584, 0, 432, 433, 0, - 0, 0, 0, 0, 0, 0, 350, 550, 568, 366, - 537, 582, 371, 545, 561, 361, 499, 533, 0, 0, - 352, 566, 544, 477, 351, 0, 526, 391, 408, 388, - 497, 0, 0, 565, 595, 387, 585, 0, 576, 354, - 0, 575, 496, 469, 562, 567, 478, 471, 0, 353, - 564, 476, 470, 446, 398, 611, 447, 448, 449, 450, - 451, 452, 412, 512, 467, 513, 413, 484, 483, 485, - 414, 415, 416, 417, 418, 419, 420, 421, 422, 423, - 0, 0, 0, 0, 0, 606, 607, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 743, 0, 0, 748, 0, 578, 0, 0, - 0, 0, 0, 0, 548, 0, 0, 453, 0, 0, - 0, 596, 0, 529, 502, 789, 0, 0, 527, 465, - 563, 514, 569, 551, 577, 521, 515, 344, 552, 390, - 479, 362, 364, 776, 392, 395, 399, 400, 490, 491, - 508, 536, 555, 556, 557, 389, 376, 528, 377, 409, - 378, 345, 381, 380, 382, 538, 383, 347, 509, 560, - 0, 405, 524, 474, 348, 473, 510, 559, 558, 363, - 586, 593, 594, 685, 0, 599, 790, 791, 792, 608, - 0, 516, 359, 356, 0, 0, 0, 385, 511, 372, - 374, 375, 373, 505, 507, 613, 614, 615, 617, 0, - 618, 619, 0, 0, 0, 0, 620, 686, 704, 670, - 639, 601, 694, 636, 640, 641, 426, 427, 428, 429, - 707, 0, 0, 0, 592, 454, 455, 0, 397, 396, - 475, 349, 0, 0, 443, 425, 517, 355, 393, 445, - 431, 456, 457, 458, 459, 460, 0, 461, 462, 403, - 339, 340, 784, 386, 498, 709, 746, 747, 631, 0, - 695, 632, 642, 379, 667, 679, 678, 492, 591, 0, - 690, 693, 621, 783, 0, 687, 701, 788, 700, 780, - 504, 0, 535, 698, 645, 0, 691, 664, 665, 0, - 692, 660, 696, 0, 633, 0, 602, 605, 635, 722, - 723, 724, 346, 604, 726, 727, 728, 729, 730, 731, - 732, 725, 574, 668, 644, 671, 583, 647, 646, 0, - 0, 682, 600, 683, 684, 486, 487, 488, 489, 407, - 710, 370, 603, 519, 520, 0, 669, 0, 0, 0, - 0, 0, 0, 0, 0, 674, 675, 672, 793, 0, - 733, 734, 0, 0, 597, 598, 402, 0, 616, 410, - 369, 503, 404, 581, 434, 0, 609, 676, 610, 522, - 523, 737, 742, 738, 739, 741, 762, 493, 424, 430, - 539, 444, 466, 525, 580, 501, 530, 367, 570, 541, - 472, 661, 689, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 722, 721, 720, 719, 718, 717, 716, 715, + 0, 0, 662, 385, 785, 779, 786, 767, 770, 769, + 744, 0, 341, 642, 466, 520, 402, 709, 710, 0, + 765, 284, 285, 286, 287, 288, 289, 290, 291, 332, + 292, 293, 294, 295, 296, 297, 298, 303, 304, 305, + 306, 307, 308, 309, 310, 712, 301, 302, 311, 312, + 313, 314, 315, 316, 317, 318, 319, 320, 321, 322, + 323, 324, 0, 0, 0, 0, 333, 771, 772, 773, + 774, 775, 0, 0, 334, 335, 336, 0, 0, 299, + 300, 327, 552, 328, 329, 330, 331, 0, 0, 591, + 592, 593, 616, 0, 594, 575, 641, 412, 342, 556, + 583, 782, 783, 0, 0, 0, 0, 0, 0, 0, + 692, 703, 739, 740, 0, 753, 754, 756, 758, 757, + 760, 549, 550, 768, 0, 0, 437, 358, 359, 496, + 497, 535, 0, 438, 439, 440, 444, 441, 442, 443, + 762, 763, 764, 761, 470, 534, 557, 543, 781, 483, + 0, 789, 631, 632, 790, 749, 748, 343, 0, 337, + 338, 706, 707, 0, 0, 537, 482, 638, 502, 0, + 0, 647, 681, 670, 759, 634, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 395, 0, 0, 465, + 685, 666, 677, 667, 508, 652, 653, 654, 661, 407, + 655, 791, 657, 626, 658, 627, 659, 660, 0, 684, + 633, 545, 484, 0, 701, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 268, 0, 0, 3624, + 0, 0, 0, 366, 269, 628, 755, 630, 629, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 369, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 546, + 574, 0, 588, 0, 434, 435, 0, 0, 0, 0, + 0, 0, 0, 350, 553, 571, 367, 540, 586, 372, + 548, 564, 362, 501, 536, 0, 0, 352, 569, 547, + 479, 351, 0, 529, 392, 409, 389, 499, 0, 0, + 568, 599, 388, 589, 0, 579, 354, 0, 578, 498, + 471, 565, 570, 480, 473, 0, 353, 567, 478, 472, + 448, 399, 615, 449, 450, 451, 452, 453, 454, 413, + 514, 469, 515, 414, 486, 485, 487, 415, 416, 417, + 418, 419, 420, 421, 422, 423, 424, 0, 0, 0, + 0, 0, 610, 611, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 747, + 0, 0, 752, 0, 582, 0, 0, 0, 0, 0, + 0, 551, 0, 0, 455, 0, 0, 0, 600, 0, + 532, 504, 793, 0, 0, 530, 467, 566, 516, 572, + 554, 580, 523, 517, 344, 555, 391, 481, 363, 365, + 780, 393, 396, 400, 401, 492, 493, 510, 539, 558, + 559, 560, 390, 377, 531, 378, 410, 379, 345, 382, + 381, 383, 541, 384, 347, 511, 563, 0, 406, 527, + 476, 348, 475, 512, 562, 561, 364, 590, 597, 598, + 689, 0, 603, 794, 795, 796, 612, 0, 518, 360, + 357, 0, 0, 0, 386, 513, 373, 375, 376, 374, + 507, 509, 617, 618, 619, 621, 0, 622, 623, 0, + 0, 0, 0, 624, 690, 708, 674, 643, 605, 698, + 640, 644, 645, 427, 428, 429, 430, 711, 0, 0, + 0, 596, 456, 457, 0, 398, 397, 477, 349, 0, + 0, 445, 426, 519, 355, 394, 447, 432, 458, 459, + 460, 461, 462, 0, 463, 464, 404, 339, 340, 788, + 387, 500, 713, 750, 751, 635, 0, 699, 636, 646, + 380, 671, 683, 682, 494, 595, 0, 694, 697, 625, + 787, 0, 691, 705, 792, 704, 784, 506, 0, 538, + 702, 649, 0, 695, 668, 669, 0, 696, 664, 700, + 356, 433, 524, 581, 0, 637, 0, 606, 609, 639, + 726, 727, 728, 346, 608, 730, 731, 732, 733, 734, + 735, 736, 729, 577, 672, 648, 675, 587, 651, 650, + 0, 0, 686, 604, 687, 688, 488, 489, 490, 491, + 408, 714, 371, 607, 521, 522, 0, 673, 0, 0, + 0, 0, 0, 0, 0, 0, 678, 679, 676, 797, + 0, 737, 738, 0, 0, 601, 602, 403, 0, 620, + 411, 370, 505, 405, 585, 436, 0, 613, 680, 614, + 525, 526, 741, 746, 742, 743, 745, 766, 495, 425, + 431, 542, 446, 468, 528, 584, 503, 533, 368, 573, + 544, 474, 665, 693, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 325, 326, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 722, 721, 720, 719, 718, 717, 716, 715, 0, + 0, 662, 385, 785, 779, 786, 767, 770, 769, 744, + 0, 341, 642, 466, 520, 402, 709, 710, 0, 765, + 284, 285, 286, 287, 288, 289, 290, 291, 332, 292, + 293, 294, 295, 296, 297, 298, 303, 304, 305, 306, + 307, 308, 309, 310, 712, 301, 302, 311, 312, 313, + 314, 315, 316, 317, 318, 319, 320, 321, 322, 323, + 324, 0, 0, 0, 0, 333, 771, 772, 773, 774, + 775, 0, 0, 334, 335, 336, 0, 0, 299, 300, + 327, 552, 328, 329, 330, 331, 0, 0, 591, 592, + 593, 616, 0, 594, 575, 641, 412, 342, 556, 583, + 782, 783, 0, 0, 0, 0, 0, 0, 0, 692, + 703, 739, 740, 0, 753, 754, 756, 758, 757, 760, + 549, 550, 768, 0, 0, 437, 358, 359, 496, 497, + 535, 0, 438, 439, 440, 444, 441, 442, 443, 762, + 763, 764, 761, 470, 534, 557, 543, 781, 483, 0, + 789, 631, 632, 790, 749, 748, 343, 0, 337, 338, + 706, 707, 0, 0, 537, 482, 638, 502, 0, 0, + 647, 681, 670, 759, 634, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 395, 0, 0, 465, 685, + 666, 677, 667, 508, 652, 653, 654, 661, 407, 655, + 791, 657, 626, 658, 627, 659, 660, 0, 684, 633, + 545, 484, 0, 701, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 268, 0, 0, 0, 0, + 0, 0, 366, 269, 628, 755, 630, 629, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 369, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 546, 574, + 0, 588, 0, 434, 435, 0, 0, 0, 0, 0, + 0, 0, 350, 553, 571, 367, 540, 586, 372, 548, + 564, 362, 501, 536, 0, 0, 352, 569, 547, 479, + 351, 0, 529, 392, 409, 389, 499, 0, 0, 568, + 599, 388, 589, 0, 579, 354, 0, 578, 498, 471, + 565, 570, 480, 473, 0, 353, 567, 478, 472, 448, + 399, 615, 449, 450, 451, 452, 453, 454, 413, 514, + 469, 515, 414, 486, 485, 487, 415, 416, 417, 418, + 419, 420, 421, 422, 423, 424, 0, 0, 0, 0, + 0, 610, 611, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 747, 0, + 0, 752, 0, 582, 0, 0, 0, 4382, 0, 0, + 551, 0, 0, 455, 0, 0, 0, 600, 0, 532, + 504, 793, 0, 0, 530, 467, 566, 516, 572, 554, + 580, 523, 517, 344, 555, 391, 481, 363, 365, 780, + 393, 396, 400, 401, 492, 493, 510, 539, 558, 559, + 560, 390, 377, 531, 378, 410, 379, 345, 382, 381, + 383, 541, 384, 347, 511, 563, 0, 406, 527, 476, + 348, 475, 512, 562, 561, 364, 590, 597, 598, 689, + 0, 603, 794, 795, 796, 612, 0, 518, 360, 357, + 0, 0, 0, 386, 513, 373, 375, 376, 374, 507, + 509, 617, 618, 619, 621, 0, 622, 623, 0, 0, + 0, 0, 624, 690, 708, 674, 643, 605, 698, 640, + 644, 645, 427, 428, 429, 430, 711, 0, 0, 0, + 596, 456, 457, 0, 398, 397, 477, 349, 0, 0, + 445, 426, 519, 355, 394, 447, 432, 458, 459, 460, + 461, 462, 0, 463, 464, 404, 339, 340, 788, 387, + 500, 713, 750, 751, 635, 0, 699, 636, 646, 380, + 671, 683, 682, 494, 595, 0, 694, 697, 625, 787, + 0, 691, 705, 792, 704, 784, 506, 0, 538, 702, + 649, 0, 695, 668, 669, 0, 696, 664, 700, 356, + 433, 524, 581, 0, 637, 0, 606, 609, 639, 726, + 727, 728, 346, 608, 730, 731, 732, 733, 734, 735, + 736, 729, 577, 672, 648, 675, 587, 651, 650, 0, + 0, 686, 604, 687, 688, 488, 489, 490, 491, 408, + 714, 371, 607, 521, 522, 0, 673, 0, 0, 0, + 0, 0, 0, 0, 0, 678, 679, 676, 797, 0, + 737, 738, 0, 0, 601, 602, 403, 0, 620, 411, + 370, 505, 405, 585, 436, 0, 613, 680, 614, 525, + 526, 741, 746, 742, 743, 745, 766, 495, 425, 431, + 542, 446, 468, 528, 584, 503, 533, 368, 573, 544, + 474, 665, 693, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 325, 326, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 718, 717, 716, 715, 714, 713, 712, 711, 0, 0, - 658, 384, 781, 775, 782, 763, 766, 765, 740, 0, - 341, 638, 464, 518, 401, 705, 706, 0, 761, 284, + 722, 721, 720, 719, 718, 717, 716, 715, 0, 0, + 662, 385, 785, 779, 786, 767, 770, 769, 744, 0, + 341, 642, 466, 520, 402, 709, 710, 0, 765, 284, 285, 286, 287, 288, 289, 290, 291, 332, 292, 293, 294, 295, 296, 297, 298, 303, 304, 305, 306, 307, - 308, 309, 310, 708, 301, 302, 311, 312, 313, 314, + 308, 309, 310, 712, 301, 302, 311, 312, 313, 314, 315, 316, 317, 318, 319, 320, 321, 322, 323, 324, - 0, 0, 0, 0, 333, 767, 768, 769, 770, 771, + 0, 0, 0, 0, 333, 771, 772, 773, 774, 775, 0, 0, 334, 335, 336, 0, 0, 299, 300, 327, - 549, 328, 329, 330, 331, 0, 0, 587, 588, 589, - 612, 0, 590, 572, 637, 411, 342, 553, 579, 778, - 779, 0, 0, 0, 0, 0, 0, 0, 688, 699, - 735, 736, 0, 749, 750, 752, 754, 753, 756, 546, - 547, 764, 0, 0, 435, 357, 358, 494, 495, 532, - 0, 436, 437, 438, 442, 439, 440, 441, 758, 759, - 760, 757, 468, 531, 554, 540, 777, 481, 0, 785, - 627, 628, 786, 745, 744, 343, 0, 337, 338, 702, - 703, 0, 0, 534, 480, 634, 500, 0, 0, 643, - 677, 666, 755, 630, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 394, 0, 0, 463, 681, 662, - 673, 663, 506, 648, 649, 650, 657, 406, 651, 787, - 653, 622, 654, 623, 655, 656, 0, 680, 629, 542, - 482, 0, 697, 0, 0, 0, 0, 0, 0, 0, + 552, 328, 329, 330, 331, 0, 0, 591, 592, 593, + 616, 0, 594, 575, 641, 412, 342, 556, 583, 782, + 783, 0, 0, 0, 0, 0, 0, 0, 692, 703, + 739, 740, 0, 753, 754, 756, 758, 757, 760, 549, + 550, 768, 0, 0, 437, 358, 359, 496, 497, 535, + 0, 438, 439, 440, 444, 441, 442, 443, 762, 763, + 764, 761, 470, 534, 557, 543, 781, 483, 0, 789, + 631, 632, 790, 749, 748, 343, 0, 337, 338, 706, + 707, 0, 0, 537, 482, 638, 502, 0, 0, 647, + 681, 670, 759, 634, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 395, 0, 0, 465, 685, 666, + 677, 667, 508, 652, 653, 654, 661, 407, 655, 791, + 657, 626, 658, 627, 659, 660, 0, 684, 633, 545, + 484, 0, 701, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 268, 0, 0, 0, 0, 0, - 0, 365, 269, 624, 751, 626, 625, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 368, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 543, 571, 0, - 584, 0, 432, 433, 0, 0, 0, 0, 0, 0, - 0, 350, 550, 568, 366, 537, 582, 371, 545, 561, - 361, 499, 533, 0, 0, 352, 566, 544, 477, 351, - 0, 526, 391, 408, 388, 497, 0, 0, 565, 595, - 387, 585, 0, 576, 354, 0, 575, 496, 469, 562, - 567, 478, 471, 0, 353, 564, 476, 470, 446, 398, - 611, 447, 448, 449, 450, 451, 452, 412, 512, 467, - 513, 413, 484, 483, 485, 414, 415, 416, 417, 418, - 419, 420, 421, 422, 423, 0, 0, 0, 0, 0, - 606, 607, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 743, 0, 0, - 748, 0, 578, 0, 0, 0, 4378, 0, 0, 548, - 0, 0, 453, 0, 0, 0, 596, 0, 529, 502, - 789, 0, 0, 527, 465, 563, 514, 569, 551, 577, - 521, 515, 344, 552, 390, 479, 362, 364, 776, 392, - 395, 399, 400, 490, 491, 508, 536, 555, 556, 557, - 389, 376, 528, 377, 409, 378, 345, 381, 380, 382, - 538, 383, 347, 509, 560, 0, 405, 524, 474, 348, - 473, 510, 559, 558, 363, 586, 593, 594, 685, 0, - 599, 790, 791, 792, 608, 0, 516, 359, 356, 0, - 0, 0, 385, 511, 372, 374, 375, 373, 505, 507, - 613, 614, 615, 617, 0, 618, 619, 0, 0, 0, - 0, 620, 686, 704, 670, 639, 601, 694, 636, 640, - 641, 426, 427, 428, 429, 707, 0, 0, 0, 592, - 454, 455, 0, 397, 396, 475, 349, 0, 0, 443, - 425, 517, 355, 393, 445, 431, 456, 457, 458, 459, - 460, 0, 461, 462, 403, 339, 340, 784, 386, 498, - 709, 746, 747, 631, 0, 695, 632, 642, 379, 667, - 679, 678, 492, 591, 0, 690, 693, 621, 783, 0, - 687, 701, 788, 700, 780, 504, 0, 535, 698, 645, - 0, 691, 664, 665, 0, 692, 660, 696, 0, 633, - 0, 602, 605, 635, 722, 723, 724, 346, 604, 726, - 727, 728, 729, 730, 731, 732, 725, 574, 668, 644, - 671, 583, 647, 646, 0, 0, 682, 600, 683, 684, - 486, 487, 488, 489, 407, 710, 370, 603, 519, 520, - 0, 669, 0, 0, 0, 0, 0, 0, 0, 0, - 674, 675, 672, 793, 0, 733, 734, 0, 0, 597, - 598, 402, 0, 616, 410, 369, 503, 404, 581, 434, - 0, 609, 676, 610, 522, 523, 737, 742, 738, 739, - 741, 762, 493, 424, 430, 539, 444, 466, 525, 580, - 501, 530, 367, 570, 541, 472, 661, 689, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 325, - 326, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 718, 717, 716, 715, 714, - 713, 712, 711, 0, 0, 658, 384, 781, 775, 782, - 763, 766, 765, 740, 0, 341, 638, 464, 518, 401, - 705, 706, 0, 761, 284, 285, 286, 287, 288, 289, - 290, 291, 332, 292, 293, 294, 295, 296, 297, 298, - 303, 304, 305, 306, 307, 308, 309, 310, 708, 301, - 302, 311, 312, 313, 314, 315, 316, 317, 318, 319, - 320, 321, 322, 323, 324, 0, 0, 0, 0, 333, - 767, 768, 769, 770, 771, 0, 0, 334, 335, 336, - 0, 0, 299, 300, 327, 549, 328, 329, 330, 331, - 0, 0, 587, 588, 589, 612, 0, 590, 572, 637, - 411, 342, 553, 579, 778, 779, 0, 0, 0, 0, - 0, 0, 0, 688, 699, 735, 736, 0, 749, 750, - 752, 754, 753, 756, 546, 547, 764, 0, 0, 435, - 357, 358, 494, 495, 532, 0, 436, 437, 438, 442, - 439, 440, 441, 758, 759, 760, 757, 468, 531, 554, - 540, 777, 481, 0, 785, 627, 628, 786, 745, 744, - 343, 0, 337, 338, 702, 703, 0, 0, 534, 480, - 634, 500, 0, 0, 643, 677, 666, 755, 630, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 394, - 0, 0, 463, 681, 662, 673, 663, 506, 648, 649, - 650, 657, 406, 651, 787, 653, 622, 654, 623, 655, - 656, 0, 680, 629, 542, 482, 0, 697, 0, 0, + 0, 366, 269, 628, 755, 630, 629, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 369, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 4073, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 546, 574, 0, + 588, 0, 434, 435, 0, 0, 0, 0, 0, 0, + 0, 350, 553, 571, 367, 540, 586, 372, 548, 564, + 362, 501, 536, 0, 0, 352, 569, 547, 479, 351, + 0, 529, 392, 409, 389, 499, 0, 0, 568, 599, + 388, 589, 0, 579, 354, 0, 578, 498, 471, 565, + 570, 480, 473, 0, 353, 567, 478, 472, 448, 399, + 615, 449, 450, 451, 452, 453, 454, 413, 514, 469, + 515, 414, 486, 485, 487, 415, 416, 417, 418, 419, + 420, 421, 422, 423, 424, 0, 0, 0, 0, 0, + 610, 611, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 747, 0, 0, + 752, 0, 582, 0, 0, 0, 0, 0, 0, 551, + 0, 0, 455, 0, 0, 0, 600, 0, 532, 504, + 793, 0, 0, 530, 467, 566, 516, 572, 554, 580, + 523, 517, 344, 555, 391, 481, 363, 365, 780, 393, + 396, 400, 401, 492, 493, 510, 539, 558, 559, 560, + 390, 377, 531, 378, 410, 379, 345, 382, 381, 383, + 541, 384, 347, 511, 563, 0, 406, 527, 476, 348, + 475, 512, 562, 561, 364, 590, 597, 598, 689, 0, + 603, 794, 795, 796, 612, 0, 518, 360, 357, 0, + 0, 0, 386, 513, 373, 375, 376, 374, 507, 509, + 617, 618, 619, 621, 0, 622, 623, 0, 0, 0, + 0, 624, 690, 708, 674, 643, 605, 698, 640, 644, + 645, 427, 428, 429, 430, 711, 0, 0, 0, 596, + 456, 457, 0, 398, 397, 477, 349, 0, 0, 445, + 426, 519, 355, 394, 447, 432, 458, 459, 460, 461, + 462, 0, 463, 464, 404, 339, 340, 788, 387, 500, + 713, 750, 751, 635, 0, 699, 636, 646, 380, 671, + 683, 682, 494, 595, 0, 694, 697, 625, 787, 0, + 691, 705, 792, 704, 784, 506, 0, 538, 702, 649, + 0, 695, 668, 669, 0, 696, 664, 700, 356, 433, + 524, 581, 0, 637, 0, 606, 609, 639, 726, 727, + 728, 346, 608, 730, 731, 732, 733, 734, 735, 736, + 729, 577, 672, 648, 675, 587, 651, 650, 0, 0, + 686, 604, 687, 688, 488, 489, 490, 491, 408, 714, + 371, 607, 521, 522, 0, 673, 0, 0, 0, 0, + 0, 0, 0, 0, 678, 679, 676, 797, 0, 737, + 738, 0, 0, 601, 602, 403, 0, 620, 411, 370, + 505, 405, 585, 436, 0, 613, 680, 614, 525, 526, + 741, 746, 742, 743, 745, 766, 495, 425, 431, 542, + 446, 468, 528, 584, 503, 533, 368, 573, 544, 474, + 665, 693, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 325, 326, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 722, + 721, 720, 719, 718, 717, 716, 715, 0, 0, 662, + 385, 785, 779, 786, 767, 770, 769, 744, 0, 341, + 642, 466, 520, 402, 709, 710, 0, 765, 284, 285, + 286, 287, 288, 289, 290, 291, 332, 292, 293, 294, + 295, 296, 297, 298, 303, 304, 305, 306, 307, 308, + 309, 310, 712, 301, 302, 311, 312, 313, 314, 315, + 316, 317, 318, 319, 320, 321, 322, 323, 324, 0, + 0, 0, 0, 333, 771, 772, 773, 774, 775, 0, + 0, 334, 335, 336, 0, 0, 299, 300, 327, 552, + 328, 329, 330, 331, 0, 0, 591, 592, 593, 616, + 0, 594, 575, 641, 412, 342, 556, 583, 782, 783, + 0, 0, 0, 0, 0, 0, 0, 692, 703, 739, + 740, 0, 753, 754, 756, 758, 757, 760, 549, 550, + 768, 0, 0, 437, 358, 359, 496, 497, 535, 0, + 438, 439, 440, 444, 441, 442, 443, 762, 763, 764, + 761, 470, 534, 557, 543, 781, 483, 0, 789, 631, + 632, 790, 749, 748, 343, 0, 337, 338, 706, 707, + 0, 0, 537, 482, 638, 502, 0, 0, 647, 681, + 670, 759, 634, 0, 0, 0, 0, 0, 2451, 0, + 0, 0, 0, 395, 0, 0, 465, 685, 666, 677, + 667, 508, 652, 653, 654, 661, 407, 655, 791, 657, + 626, 658, 627, 659, 660, 0, 684, 633, 545, 484, + 0, 701, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 268, 0, 0, 2453, 0, 0, 0, + 366, 269, 628, 755, 630, 629, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 369, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 546, 574, 0, 588, + 0, 434, 435, 0, 0, 0, 0, 0, 0, 0, + 350, 553, 571, 367, 540, 586, 372, 548, 564, 362, + 501, 536, 0, 0, 352, 569, 547, 479, 351, 0, + 529, 392, 409, 389, 499, 0, 0, 568, 599, 388, + 589, 0, 579, 354, 0, 578, 498, 471, 565, 570, + 480, 473, 0, 353, 567, 478, 472, 448, 399, 615, + 449, 450, 451, 452, 453, 454, 413, 514, 469, 515, + 414, 486, 485, 487, 415, 416, 417, 418, 419, 420, + 421, 422, 423, 424, 0, 0, 0, 0, 0, 610, + 611, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 747, 0, 0, 752, + 0, 582, 0, 0, 0, 0, 0, 0, 551, 0, + 0, 455, 0, 0, 0, 600, 0, 532, 504, 793, + 0, 0, 530, 467, 566, 516, 572, 554, 580, 523, + 517, 344, 555, 391, 481, 363, 365, 780, 393, 396, + 400, 401, 492, 493, 510, 539, 558, 559, 560, 390, + 377, 531, 378, 410, 379, 345, 382, 381, 383, 541, + 384, 347, 511, 563, 0, 406, 527, 476, 348, 475, + 512, 562, 561, 364, 590, 597, 598, 689, 0, 603, + 794, 795, 796, 612, 0, 518, 360, 357, 0, 0, + 0, 386, 513, 373, 375, 376, 374, 507, 509, 617, + 618, 619, 621, 0, 622, 623, 0, 0, 0, 0, + 624, 690, 708, 674, 643, 605, 698, 640, 644, 645, + 427, 428, 429, 430, 711, 0, 0, 0, 596, 456, + 457, 0, 398, 397, 477, 349, 0, 0, 445, 426, + 519, 355, 394, 447, 432, 458, 459, 460, 461, 462, + 0, 463, 464, 404, 339, 340, 788, 387, 500, 713, + 750, 751, 635, 0, 699, 636, 646, 380, 671, 683, + 682, 494, 595, 0, 694, 697, 625, 787, 0, 691, + 705, 792, 704, 784, 506, 0, 538, 702, 649, 0, + 695, 668, 669, 0, 696, 664, 700, 356, 433, 524, + 581, 0, 637, 0, 606, 609, 639, 726, 727, 728, + 346, 608, 730, 731, 732, 733, 734, 735, 736, 729, + 577, 672, 648, 675, 587, 651, 650, 0, 0, 686, + 604, 687, 688, 488, 489, 490, 491, 408, 714, 371, + 607, 521, 522, 0, 673, 0, 0, 0, 0, 0, + 0, 0, 0, 678, 679, 676, 797, 0, 737, 738, + 0, 0, 601, 602, 403, 0, 620, 411, 370, 505, + 405, 585, 436, 0, 613, 680, 614, 525, 526, 741, + 746, 742, 743, 745, 766, 495, 425, 431, 542, 446, + 468, 528, 584, 503, 533, 368, 573, 544, 474, 665, + 693, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 325, 326, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 722, 721, + 720, 719, 718, 717, 716, 715, 0, 0, 662, 385, + 785, 779, 786, 767, 770, 769, 744, 0, 341, 642, + 466, 520, 402, 709, 710, 0, 765, 284, 285, 286, + 287, 288, 289, 290, 291, 332, 292, 293, 294, 295, + 296, 297, 298, 303, 304, 305, 306, 307, 308, 309, + 310, 712, 301, 302, 311, 312, 313, 314, 315, 316, + 317, 318, 319, 320, 321, 322, 323, 324, 0, 0, + 0, 0, 333, 771, 772, 773, 774, 775, 0, 0, + 334, 335, 336, 0, 0, 299, 300, 327, 552, 328, + 329, 330, 331, 0, 0, 591, 592, 593, 616, 0, + 594, 575, 641, 412, 342, 556, 583, 782, 783, 0, + 0, 0, 0, 0, 0, 0, 692, 703, 739, 740, + 0, 753, 754, 756, 758, 757, 760, 549, 550, 768, + 0, 0, 437, 358, 359, 496, 497, 535, 0, 438, + 439, 440, 444, 441, 442, 443, 762, 763, 764, 761, + 470, 534, 557, 543, 781, 483, 0, 789, 631, 632, + 790, 749, 0, 343, 0, 337, 338, 706, 707, 0, + 0, 537, 482, 638, 502, 0, 0, 647, 681, 670, + 759, 634, 0, 0, 4329, 0, 0, 0, 0, 0, + 0, 0, 395, 0, 0, 465, 685, 666, 677, 667, + 508, 652, 653, 654, 661, 407, 655, 791, 657, 626, + 658, 627, 659, 660, 0, 684, 633, 545, 484, 0, + 701, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 268, 0, 0, 0, 0, 0, 0, 366, + 269, 628, 755, 630, 629, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 369, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 546, 574, 0, 588, 0, + 434, 435, 0, 0, 0, 0, 0, 0, 0, 350, + 553, 571, 367, 540, 586, 372, 548, 564, 362, 501, + 536, 0, 0, 352, 569, 547, 479, 351, 0, 529, + 392, 409, 389, 499, 0, 0, 568, 599, 388, 589, + 0, 579, 354, 0, 578, 498, 471, 565, 570, 480, + 473, 0, 353, 567, 478, 472, 448, 399, 615, 449, + 450, 451, 452, 453, 454, 413, 514, 469, 515, 414, + 486, 485, 487, 415, 416, 417, 418, 419, 420, 421, + 422, 423, 424, 0, 0, 0, 0, 0, 610, 611, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 747, 0, 0, 752, 0, + 582, 0, 0, 0, 0, 0, 0, 551, 0, 0, + 455, 0, 0, 0, 600, 0, 532, 504, 793, 0, + 0, 530, 467, 566, 516, 572, 554, 580, 523, 517, + 344, 555, 391, 481, 363, 365, 780, 393, 396, 400, + 401, 492, 493, 510, 539, 558, 559, 560, 390, 377, + 531, 378, 410, 379, 345, 382, 381, 383, 541, 384, + 347, 511, 563, 0, 406, 527, 476, 348, 475, 512, + 562, 561, 364, 590, 597, 598, 689, 0, 603, 794, + 795, 796, 612, 0, 518, 360, 357, 0, 0, 0, + 386, 513, 373, 375, 376, 374, 507, 509, 617, 618, + 619, 621, 0, 622, 623, 0, 0, 0, 0, 624, + 690, 708, 674, 643, 605, 698, 640, 644, 645, 427, + 428, 429, 430, 711, 0, 0, 0, 596, 456, 457, + 0, 398, 397, 477, 349, 0, 0, 445, 426, 519, + 355, 394, 447, 432, 458, 459, 460, 461, 462, 0, + 463, 464, 404, 339, 340, 788, 387, 500, 713, 750, + 751, 635, 0, 699, 636, 646, 380, 671, 683, 682, + 494, 595, 0, 694, 697, 625, 787, 0, 691, 705, + 792, 704, 784, 506, 0, 538, 702, 649, 0, 695, + 668, 669, 0, 696, 664, 700, 356, 433, 524, 581, + 0, 637, 0, 606, 609, 639, 726, 727, 728, 346, + 608, 730, 731, 732, 733, 734, 735, 736, 729, 577, + 672, 648, 675, 587, 651, 650, 0, 0, 686, 604, + 687, 688, 488, 489, 490, 491, 408, 714, 371, 607, + 521, 522, 0, 673, 0, 0, 0, 0, 0, 0, + 0, 0, 678, 679, 676, 797, 0, 737, 738, 0, + 0, 601, 602, 403, 0, 620, 411, 370, 505, 405, + 585, 436, 0, 613, 680, 614, 525, 526, 741, 746, + 742, 743, 745, 766, 495, 425, 431, 542, 446, 468, + 528, 584, 503, 533, 368, 573, 544, 474, 665, 693, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 268, - 0, 0, 0, 0, 0, 0, 365, 269, 624, 751, - 626, 625, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 368, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 4069, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 543, 571, 0, 584, 0, 432, 433, 0, - 0, 0, 0, 0, 0, 0, 350, 550, 568, 366, - 537, 582, 371, 545, 561, 361, 499, 533, 0, 0, - 352, 566, 544, 477, 351, 0, 526, 391, 408, 388, - 497, 0, 0, 565, 595, 387, 585, 0, 576, 354, - 0, 575, 496, 469, 562, 567, 478, 471, 0, 353, - 564, 476, 470, 446, 398, 611, 447, 448, 449, 450, - 451, 452, 412, 512, 467, 513, 413, 484, 483, 485, - 414, 415, 416, 417, 418, 419, 420, 421, 422, 423, - 0, 0, 0, 0, 0, 606, 607, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 743, 0, 0, 748, 0, 578, 0, 0, - 0, 0, 0, 0, 548, 0, 0, 453, 0, 0, - 0, 596, 0, 529, 502, 789, 0, 0, 527, 465, - 563, 514, 569, 551, 577, 521, 515, 344, 552, 390, - 479, 362, 364, 776, 392, 395, 399, 400, 490, 491, - 508, 536, 555, 556, 557, 389, 376, 528, 377, 409, - 378, 345, 381, 380, 382, 538, 383, 347, 509, 560, - 0, 405, 524, 474, 348, 473, 510, 559, 558, 363, - 586, 593, 594, 685, 0, 599, 790, 791, 792, 608, - 0, 516, 359, 356, 0, 0, 0, 385, 511, 372, - 374, 375, 373, 505, 507, 613, 614, 615, 617, 0, - 618, 619, 0, 0, 0, 0, 620, 686, 704, 670, - 639, 601, 694, 636, 640, 641, 426, 427, 428, 429, - 707, 0, 0, 0, 592, 454, 455, 0, 397, 396, - 475, 349, 0, 0, 443, 425, 517, 355, 393, 445, - 431, 456, 457, 458, 459, 460, 0, 461, 462, 403, - 339, 340, 784, 386, 498, 709, 746, 747, 631, 0, - 695, 632, 642, 379, 667, 679, 678, 492, 591, 0, - 690, 693, 621, 783, 0, 687, 701, 788, 700, 780, - 504, 0, 535, 698, 645, 0, 691, 664, 665, 0, - 692, 660, 696, 0, 633, 0, 602, 605, 635, 722, - 723, 724, 346, 604, 726, 727, 728, 729, 730, 731, - 732, 725, 574, 668, 644, 671, 583, 647, 646, 0, - 0, 682, 600, 683, 684, 486, 487, 488, 489, 407, - 710, 370, 603, 519, 520, 0, 669, 0, 0, 0, - 0, 0, 0, 0, 0, 674, 675, 672, 793, 0, - 733, 734, 0, 0, 597, 598, 402, 0, 616, 410, - 369, 503, 404, 581, 434, 0, 609, 676, 610, 522, - 523, 737, 742, 738, 739, 741, 762, 493, 424, 430, - 539, 444, 466, 525, 580, 501, 530, 367, 570, 541, - 472, 661, 689, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 325, 326, 0, 0, 0, 0, + 0, 325, 326, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 722, 721, 720, + 719, 718, 717, 716, 715, 0, 0, 662, 385, 785, + 779, 786, 767, 770, 769, 744, 0, 341, 642, 466, + 520, 402, 709, 710, 0, 765, 284, 285, 286, 287, + 288, 289, 290, 291, 332, 292, 293, 294, 295, 296, + 297, 298, 303, 304, 305, 306, 307, 308, 309, 310, + 712, 301, 302, 311, 312, 313, 314, 315, 316, 317, + 318, 319, 320, 321, 322, 323, 324, 0, 0, 0, + 0, 333, 771, 772, 773, 774, 775, 0, 0, 334, + 335, 336, 0, 0, 299, 300, 327, 552, 328, 329, + 330, 331, 0, 0, 591, 592, 593, 616, 0, 594, + 575, 641, 412, 342, 556, 583, 782, 783, 0, 0, + 0, 0, 0, 0, 0, 692, 703, 739, 740, 0, + 753, 754, 756, 758, 757, 760, 549, 550, 768, 0, + 0, 437, 358, 359, 496, 497, 535, 0, 438, 439, + 440, 444, 441, 442, 443, 762, 763, 764, 761, 470, + 534, 557, 543, 781, 483, 0, 789, 631, 632, 790, + 749, 748, 343, 0, 337, 338, 706, 707, 0, 0, + 537, 482, 638, 502, 0, 0, 647, 681, 670, 759, + 634, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 395, 0, 0, 465, 685, 666, 677, 667, 508, + 652, 653, 654, 661, 407, 655, 791, 657, 626, 658, + 627, 659, 660, 0, 684, 633, 545, 484, 0, 701, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 268, 0, 0, 0, 0, 0, 0, 366, 269, + 628, 755, 630, 629, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 369, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 4192, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 546, 574, 0, 588, 0, 434, + 435, 0, 0, 0, 0, 0, 0, 0, 350, 553, + 571, 367, 540, 586, 372, 548, 564, 362, 501, 536, + 0, 0, 352, 569, 547, 479, 351, 0, 529, 392, + 409, 389, 499, 0, 0, 568, 599, 388, 589, 0, + 579, 354, 0, 578, 498, 471, 565, 570, 480, 473, + 0, 353, 567, 478, 472, 448, 399, 615, 449, 450, + 451, 452, 453, 454, 413, 514, 469, 515, 414, 486, + 485, 487, 415, 416, 417, 418, 419, 420, 421, 422, + 423, 424, 0, 0, 0, 0, 0, 610, 611, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 747, 0, 0, 752, 0, 582, + 0, 0, 0, 0, 0, 0, 551, 0, 0, 455, + 0, 0, 0, 600, 0, 532, 504, 793, 0, 0, + 530, 467, 566, 516, 572, 554, 580, 523, 517, 344, + 555, 391, 481, 363, 365, 780, 393, 396, 400, 401, + 492, 493, 510, 539, 558, 559, 560, 390, 377, 531, + 378, 410, 379, 345, 382, 381, 383, 541, 384, 347, + 511, 563, 0, 406, 527, 476, 348, 475, 512, 562, + 561, 364, 590, 597, 598, 689, 0, 603, 794, 795, + 796, 612, 0, 518, 360, 357, 0, 0, 0, 386, + 513, 373, 375, 376, 374, 507, 509, 617, 618, 619, + 621, 0, 622, 623, 0, 0, 0, 0, 624, 690, + 708, 674, 643, 605, 698, 640, 644, 645, 427, 428, + 429, 430, 711, 0, 0, 0, 596, 456, 457, 0, + 398, 397, 477, 349, 0, 0, 445, 426, 519, 355, + 394, 447, 432, 458, 459, 460, 461, 462, 0, 463, + 464, 404, 339, 340, 788, 387, 500, 713, 750, 751, + 635, 0, 699, 636, 646, 380, 671, 683, 682, 494, + 595, 0, 694, 697, 625, 787, 0, 691, 705, 792, + 704, 784, 506, 0, 538, 702, 649, 0, 695, 668, + 669, 0, 696, 664, 700, 356, 433, 524, 581, 0, + 637, 0, 606, 609, 639, 726, 727, 728, 346, 608, + 730, 731, 732, 733, 734, 735, 736, 729, 577, 672, + 648, 675, 587, 651, 650, 0, 0, 686, 604, 687, + 688, 488, 489, 490, 491, 408, 714, 371, 607, 521, + 522, 0, 673, 0, 0, 0, 0, 0, 0, 0, + 0, 678, 679, 676, 797, 0, 737, 738, 0, 0, + 601, 602, 403, 0, 620, 411, 370, 505, 405, 585, + 436, 0, 613, 680, 614, 525, 526, 741, 746, 742, + 743, 745, 766, 495, 425, 431, 542, 446, 468, 528, + 584, 503, 533, 368, 573, 544, 474, 665, 693, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 718, 717, 716, 715, 714, 713, 712, 711, 0, 0, - 658, 384, 781, 775, 782, 763, 766, 765, 740, 0, - 341, 638, 464, 518, 401, 705, 706, 0, 761, 284, - 285, 286, 287, 288, 289, 290, 291, 332, 292, 293, - 294, 295, 296, 297, 298, 303, 304, 305, 306, 307, - 308, 309, 310, 708, 301, 302, 311, 312, 313, 314, - 315, 316, 317, 318, 319, 320, 321, 322, 323, 324, - 0, 0, 0, 0, 333, 767, 768, 769, 770, 771, - 0, 0, 334, 335, 336, 0, 0, 299, 300, 327, - 549, 328, 329, 330, 331, 0, 0, 587, 588, 589, - 612, 0, 590, 572, 637, 411, 342, 553, 579, 778, - 779, 0, 0, 0, 0, 0, 0, 0, 688, 699, - 735, 736, 0, 749, 750, 752, 754, 753, 756, 546, - 547, 764, 0, 0, 435, 357, 358, 494, 495, 532, - 0, 436, 437, 438, 442, 439, 440, 441, 758, 759, - 760, 757, 468, 531, 554, 540, 777, 481, 0, 785, - 627, 628, 786, 745, 744, 343, 0, 337, 338, 702, - 703, 0, 0, 534, 480, 634, 500, 0, 0, 643, - 677, 666, 755, 630, 0, 0, 0, 0, 0, 2447, - 0, 0, 0, 0, 394, 0, 0, 463, 681, 662, - 673, 663, 506, 648, 649, 650, 657, 406, 651, 787, - 653, 622, 654, 623, 655, 656, 0, 680, 629, 542, - 482, 0, 697, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 268, 0, 0, 2449, 0, 0, - 0, 365, 269, 624, 751, 626, 625, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 368, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 543, 571, 0, - 584, 0, 432, 433, 0, 0, 0, 0, 0, 0, - 0, 350, 550, 568, 366, 537, 582, 371, 545, 561, - 361, 499, 533, 0, 0, 352, 566, 544, 477, 351, - 0, 526, 391, 408, 388, 497, 0, 0, 565, 595, - 387, 585, 0, 576, 354, 0, 575, 496, 469, 562, - 567, 478, 471, 0, 353, 564, 476, 470, 446, 398, - 611, 447, 448, 449, 450, 451, 452, 412, 512, 467, - 513, 413, 484, 483, 485, 414, 415, 416, 417, 418, - 419, 420, 421, 422, 423, 0, 0, 0, 0, 0, - 606, 607, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 743, 0, 0, - 748, 0, 578, 0, 0, 0, 0, 0, 0, 548, - 0, 0, 453, 0, 0, 0, 596, 0, 529, 502, - 789, 0, 0, 527, 465, 563, 514, 569, 551, 577, - 521, 515, 344, 552, 390, 479, 362, 364, 776, 392, - 395, 399, 400, 490, 491, 508, 536, 555, 556, 557, - 389, 376, 528, 377, 409, 378, 345, 381, 380, 382, - 538, 383, 347, 509, 560, 0, 405, 524, 474, 348, - 473, 510, 559, 558, 363, 586, 593, 594, 685, 0, - 599, 790, 791, 792, 608, 0, 516, 359, 356, 0, - 0, 0, 385, 511, 372, 374, 375, 373, 505, 507, - 613, 614, 615, 617, 0, 618, 619, 0, 0, 0, - 0, 620, 686, 704, 670, 639, 601, 694, 636, 640, - 641, 426, 427, 428, 429, 707, 0, 0, 0, 592, - 454, 455, 0, 397, 396, 475, 349, 0, 0, 443, - 425, 517, 355, 393, 445, 431, 456, 457, 458, 459, - 460, 0, 461, 462, 403, 339, 340, 784, 386, 498, - 709, 746, 747, 631, 0, 695, 632, 642, 379, 667, - 679, 678, 492, 591, 0, 690, 693, 621, 783, 0, - 687, 701, 788, 700, 780, 504, 0, 535, 698, 645, - 0, 691, 664, 665, 0, 692, 660, 696, 0, 633, - 0, 602, 605, 635, 722, 723, 724, 346, 604, 726, - 727, 728, 729, 730, 731, 732, 725, 574, 668, 644, - 671, 583, 647, 646, 0, 0, 682, 600, 683, 684, - 486, 487, 488, 489, 407, 710, 370, 603, 519, 520, - 0, 669, 0, 0, 0, 0, 0, 0, 0, 0, - 674, 675, 672, 793, 0, 733, 734, 0, 0, 597, - 598, 402, 0, 616, 410, 369, 503, 404, 581, 434, - 0, 609, 676, 610, 522, 523, 737, 742, 738, 739, - 741, 762, 493, 424, 430, 539, 444, 466, 525, 580, - 501, 530, 367, 570, 541, 472, 661, 689, 0, 0, + 325, 326, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 722, 721, 720, 719, + 718, 717, 716, 715, 0, 0, 662, 385, 785, 779, + 786, 767, 770, 769, 744, 0, 341, 642, 466, 520, + 402, 709, 710, 0, 765, 284, 285, 286, 287, 288, + 289, 290, 291, 332, 292, 293, 294, 295, 296, 297, + 298, 303, 304, 305, 306, 307, 308, 309, 310, 712, + 301, 302, 311, 312, 313, 314, 315, 316, 317, 318, + 319, 320, 321, 322, 323, 324, 0, 0, 0, 0, + 333, 771, 772, 773, 774, 775, 0, 0, 334, 335, + 336, 0, 0, 299, 300, 327, 552, 328, 329, 330, + 331, 0, 0, 591, 592, 593, 616, 0, 594, 575, + 641, 412, 342, 556, 583, 782, 783, 0, 0, 0, + 0, 0, 0, 0, 692, 703, 739, 740, 0, 753, + 754, 756, 758, 757, 760, 549, 550, 768, 0, 0, + 437, 358, 359, 496, 497, 535, 0, 438, 439, 440, + 444, 441, 442, 443, 762, 763, 764, 761, 470, 534, + 557, 543, 781, 483, 0, 789, 631, 632, 790, 749, + 748, 343, 0, 337, 338, 706, 707, 0, 0, 537, + 482, 638, 502, 0, 0, 647, 681, 670, 759, 634, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 395, 0, 0, 465, 685, 666, 677, 667, 508, 652, + 653, 654, 661, 407, 655, 791, 657, 626, 658, 627, + 659, 660, 0, 684, 633, 545, 484, 0, 701, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 268, 0, 0, 4039, 0, 0, 0, 366, 269, 628, + 755, 630, 629, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 369, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 546, 574, 0, 588, 0, 434, 435, + 0, 0, 0, 0, 0, 0, 0, 350, 553, 571, + 367, 540, 586, 372, 548, 564, 362, 501, 536, 0, + 0, 352, 569, 547, 479, 351, 0, 529, 392, 409, + 389, 499, 0, 0, 568, 599, 388, 589, 0, 579, + 354, 0, 578, 498, 471, 565, 570, 480, 473, 0, + 353, 567, 478, 472, 448, 399, 615, 449, 450, 451, + 452, 453, 454, 413, 514, 469, 515, 414, 486, 485, + 487, 415, 416, 417, 418, 419, 420, 421, 422, 423, + 424, 0, 0, 0, 0, 0, 610, 611, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 747, 0, 0, 752, 0, 582, 0, + 0, 0, 0, 0, 0, 551, 0, 0, 455, 0, + 0, 0, 600, 0, 532, 504, 793, 0, 0, 530, + 467, 566, 516, 572, 554, 580, 523, 517, 344, 555, + 391, 481, 363, 365, 780, 393, 396, 400, 401, 492, + 493, 510, 539, 558, 559, 560, 390, 377, 531, 378, + 410, 379, 345, 382, 381, 383, 541, 384, 347, 511, + 563, 0, 406, 527, 476, 348, 475, 512, 562, 561, + 364, 590, 597, 598, 689, 0, 603, 794, 795, 796, + 612, 0, 518, 360, 357, 0, 0, 0, 386, 513, + 373, 375, 376, 374, 507, 509, 617, 618, 619, 621, + 0, 622, 623, 0, 0, 0, 0, 624, 690, 708, + 674, 643, 605, 698, 640, 644, 645, 427, 428, 429, + 430, 711, 0, 0, 0, 596, 456, 457, 0, 398, + 397, 477, 349, 0, 0, 445, 426, 519, 355, 394, + 447, 432, 458, 459, 460, 461, 462, 0, 463, 464, + 404, 339, 340, 788, 387, 500, 713, 750, 751, 635, + 0, 699, 636, 646, 380, 671, 683, 682, 494, 595, + 0, 694, 697, 625, 787, 0, 691, 705, 792, 704, + 784, 506, 0, 538, 702, 649, 0, 695, 668, 669, + 0, 696, 664, 700, 356, 433, 524, 581, 0, 637, + 0, 606, 609, 639, 726, 727, 728, 346, 608, 730, + 731, 732, 733, 734, 735, 736, 729, 577, 672, 648, + 675, 587, 651, 650, 0, 0, 686, 604, 687, 688, + 488, 489, 490, 491, 408, 714, 371, 607, 521, 522, + 0, 673, 0, 0, 0, 0, 0, 0, 0, 0, + 678, 679, 676, 797, 0, 737, 738, 0, 0, 601, + 602, 403, 0, 620, 411, 370, 505, 405, 585, 436, + 0, 613, 680, 614, 525, 526, 741, 746, 742, 743, + 745, 766, 495, 425, 431, 542, 446, 468, 528, 584, + 503, 533, 368, 573, 544, 474, 665, 693, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 325, 326, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 718, 717, 716, 715, 714, - 713, 712, 711, 0, 0, 658, 384, 781, 775, 782, - 763, 766, 765, 740, 0, 341, 638, 464, 518, 401, - 705, 706, 0, 761, 284, 285, 286, 287, 288, 289, + 0, 0, 0, 0, 0, 722, 721, 720, 719, 718, + 717, 716, 715, 0, 0, 662, 385, 785, 779, 786, + 767, 770, 769, 744, 0, 341, 642, 466, 520, 402, + 709, 710, 0, 765, 284, 285, 286, 287, 288, 289, 290, 291, 332, 292, 293, 294, 295, 296, 297, 298, - 303, 304, 305, 306, 307, 308, 309, 310, 708, 301, + 303, 304, 305, 306, 307, 308, 309, 310, 712, 301, 302, 311, 312, 313, 314, 315, 316, 317, 318, 319, 320, 321, 322, 323, 324, 0, 0, 0, 0, 333, - 767, 768, 769, 770, 771, 0, 0, 334, 335, 336, - 0, 0, 299, 300, 327, 549, 328, 329, 330, 331, - 0, 0, 587, 588, 589, 612, 0, 590, 572, 637, - 411, 342, 553, 579, 778, 779, 0, 0, 0, 0, - 0, 0, 0, 688, 699, 735, 736, 0, 749, 750, - 752, 754, 753, 756, 546, 547, 764, 0, 0, 435, - 357, 358, 494, 495, 532, 0, 436, 437, 438, 442, - 439, 440, 441, 758, 759, 760, 757, 468, 531, 554, - 540, 777, 481, 0, 785, 627, 628, 786, 745, 0, - 343, 0, 337, 338, 702, 703, 0, 0, 534, 480, - 634, 500, 0, 0, 643, 677, 666, 755, 630, 0, - 0, 4325, 0, 0, 0, 0, 0, 0, 0, 394, - 0, 0, 463, 681, 662, 673, 663, 506, 648, 649, - 650, 657, 406, 651, 787, 653, 622, 654, 623, 655, - 656, 0, 680, 629, 542, 482, 0, 697, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 268, - 0, 0, 0, 0, 0, 0, 365, 269, 624, 751, - 626, 625, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 368, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 543, 571, 0, 584, 0, 432, 433, 0, - 0, 0, 0, 0, 0, 0, 350, 550, 568, 366, - 537, 582, 371, 545, 561, 361, 499, 533, 0, 0, - 352, 566, 544, 477, 351, 0, 526, 391, 408, 388, - 497, 0, 0, 565, 595, 387, 585, 0, 576, 354, - 0, 575, 496, 469, 562, 567, 478, 471, 0, 353, - 564, 476, 470, 446, 398, 611, 447, 448, 449, 450, - 451, 452, 412, 512, 467, 513, 413, 484, 483, 485, - 414, 415, 416, 417, 418, 419, 420, 421, 422, 423, - 0, 0, 0, 0, 0, 606, 607, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 743, 0, 0, 748, 0, 578, 0, 0, - 0, 0, 0, 0, 548, 0, 0, 453, 0, 0, - 0, 596, 0, 529, 502, 789, 0, 0, 527, 465, - 563, 514, 569, 551, 577, 521, 515, 344, 552, 390, - 479, 362, 364, 776, 392, 395, 399, 400, 490, 491, - 508, 536, 555, 556, 557, 389, 376, 528, 377, 409, - 378, 345, 381, 380, 382, 538, 383, 347, 509, 560, - 0, 405, 524, 474, 348, 473, 510, 559, 558, 363, - 586, 593, 594, 685, 0, 599, 790, 791, 792, 608, - 0, 516, 359, 356, 0, 0, 0, 385, 511, 372, - 374, 375, 373, 505, 507, 613, 614, 615, 617, 0, - 618, 619, 0, 0, 0, 0, 620, 686, 704, 670, - 639, 601, 694, 636, 640, 641, 426, 427, 428, 429, - 707, 0, 0, 0, 592, 454, 455, 0, 397, 396, - 475, 349, 0, 0, 443, 425, 517, 355, 393, 445, - 431, 456, 457, 458, 459, 460, 0, 461, 462, 403, - 339, 340, 784, 386, 498, 709, 746, 747, 631, 0, - 695, 632, 642, 379, 667, 679, 678, 492, 591, 0, - 690, 693, 621, 783, 0, 687, 701, 788, 700, 780, - 504, 0, 535, 698, 645, 0, 691, 664, 665, 0, - 692, 660, 696, 0, 633, 0, 602, 605, 635, 722, - 723, 724, 346, 604, 726, 727, 728, 729, 730, 731, - 732, 725, 574, 668, 644, 671, 583, 647, 646, 0, - 0, 682, 600, 683, 684, 486, 487, 488, 489, 407, - 710, 370, 603, 519, 520, 0, 669, 0, 0, 0, - 0, 0, 0, 0, 0, 674, 675, 672, 793, 0, - 733, 734, 0, 0, 597, 598, 402, 0, 616, 410, - 369, 503, 404, 581, 434, 0, 609, 676, 610, 522, - 523, 737, 742, 738, 739, 741, 762, 493, 424, 430, - 539, 444, 466, 525, 580, 501, 530, 367, 570, 541, - 472, 661, 689, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 325, 326, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 718, 717, 716, 715, 714, 713, 712, 711, 0, 0, - 658, 384, 781, 775, 782, 763, 766, 765, 740, 0, - 341, 638, 464, 518, 401, 705, 706, 0, 761, 284, - 285, 286, 287, 288, 289, 290, 291, 332, 292, 293, - 294, 295, 296, 297, 298, 303, 304, 305, 306, 307, - 308, 309, 310, 708, 301, 302, 311, 312, 313, 314, - 315, 316, 317, 318, 319, 320, 321, 322, 323, 324, - 0, 0, 0, 0, 333, 767, 768, 769, 770, 771, - 0, 0, 334, 335, 336, 0, 0, 299, 300, 327, - 549, 328, 329, 330, 331, 0, 0, 587, 588, 589, - 612, 0, 590, 572, 637, 411, 342, 553, 579, 778, - 779, 0, 0, 0, 0, 0, 0, 0, 688, 699, - 735, 736, 0, 749, 750, 752, 754, 753, 756, 546, - 547, 764, 0, 0, 435, 357, 358, 494, 495, 532, - 0, 436, 437, 438, 442, 439, 440, 441, 758, 759, - 760, 757, 468, 531, 554, 540, 777, 481, 0, 785, - 627, 628, 786, 745, 744, 343, 0, 337, 338, 702, - 703, 0, 0, 534, 480, 634, 500, 0, 0, 643, - 677, 666, 755, 630, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 394, 0, 0, 463, 681, 662, - 673, 663, 506, 648, 649, 650, 657, 406, 651, 787, - 653, 622, 654, 623, 655, 656, 0, 680, 629, 542, - 482, 0, 697, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 268, 0, 0, 0, 0, 0, - 0, 365, 269, 624, 751, 626, 625, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 368, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 4188, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 543, 571, 0, - 584, 0, 432, 433, 0, 0, 0, 0, 0, 0, - 0, 350, 550, 568, 366, 537, 582, 371, 545, 561, - 361, 499, 533, 0, 0, 352, 566, 544, 477, 351, - 0, 526, 391, 408, 388, 497, 0, 0, 565, 595, - 387, 585, 0, 576, 354, 0, 575, 496, 469, 562, - 567, 478, 471, 0, 353, 564, 476, 470, 446, 398, - 611, 447, 448, 449, 450, 451, 452, 412, 512, 467, - 513, 413, 484, 483, 485, 414, 415, 416, 417, 418, - 419, 420, 421, 422, 423, 0, 0, 0, 0, 0, - 606, 607, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 743, 0, 0, - 748, 0, 578, 0, 0, 0, 0, 0, 0, 548, - 0, 0, 453, 0, 0, 0, 596, 0, 529, 502, - 789, 0, 0, 527, 465, 563, 514, 569, 551, 577, - 521, 515, 344, 552, 390, 479, 362, 364, 776, 392, - 395, 399, 400, 490, 491, 508, 536, 555, 556, 557, - 389, 376, 528, 377, 409, 378, 345, 381, 380, 382, - 538, 383, 347, 509, 560, 0, 405, 524, 474, 348, - 473, 510, 559, 558, 363, 586, 593, 594, 685, 0, - 599, 790, 791, 792, 608, 0, 516, 359, 356, 0, - 0, 0, 385, 511, 372, 374, 375, 373, 505, 507, - 613, 614, 615, 617, 0, 618, 619, 0, 0, 0, - 0, 620, 686, 704, 670, 639, 601, 694, 636, 640, - 641, 426, 427, 428, 429, 707, 0, 0, 0, 592, - 454, 455, 0, 397, 396, 475, 349, 0, 0, 443, - 425, 517, 355, 393, 445, 431, 456, 457, 458, 459, - 460, 0, 461, 462, 403, 339, 340, 784, 386, 498, - 709, 746, 747, 631, 0, 695, 632, 642, 379, 667, - 679, 678, 492, 591, 0, 690, 693, 621, 783, 0, - 687, 701, 788, 700, 780, 504, 0, 535, 698, 645, - 0, 691, 664, 665, 0, 692, 660, 696, 0, 633, - 0, 602, 605, 635, 722, 723, 724, 346, 604, 726, - 727, 728, 729, 730, 731, 732, 725, 574, 668, 644, - 671, 583, 647, 646, 0, 0, 682, 600, 683, 684, - 486, 487, 488, 489, 407, 710, 370, 603, 519, 520, - 0, 669, 0, 0, 0, 0, 0, 0, 0, 0, - 674, 675, 672, 793, 0, 733, 734, 0, 0, 597, - 598, 402, 0, 616, 410, 369, 503, 404, 581, 434, - 0, 609, 676, 610, 522, 523, 737, 742, 738, 739, - 741, 762, 493, 424, 430, 539, 444, 466, 525, 580, - 501, 530, 367, 570, 541, 472, 661, 689, 0, 0, + 771, 772, 773, 774, 775, 0, 0, 334, 335, 336, + 0, 0, 299, 300, 327, 552, 328, 329, 330, 331, + 0, 0, 591, 592, 593, 616, 0, 594, 575, 641, + 412, 342, 556, 583, 782, 783, 0, 0, 0, 0, + 0, 0, 0, 692, 703, 739, 740, 0, 753, 754, + 756, 758, 757, 760, 549, 550, 768, 0, 0, 437, + 358, 359, 496, 497, 535, 0, 438, 439, 440, 444, + 441, 442, 443, 762, 763, 764, 761, 470, 534, 557, + 543, 781, 483, 0, 789, 631, 632, 790, 749, 748, + 343, 0, 337, 338, 706, 707, 3968, 0, 537, 482, + 638, 0, 502, 0, 0, 647, 681, 670, 759, 634, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 395, 0, 0, 465, 685, 666, 677, 667, 508, 652, + 653, 654, 661, 407, 655, 791, 657, 626, 658, 627, + 659, 660, 0, 684, 633, 545, 484, 0, 701, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 268, 0, 0, 0, 0, 0, 0, 366, 269, 628, + 755, 630, 629, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 369, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 546, 574, 0, 588, 0, 434, 435, + 0, 0, 0, 0, 0, 0, 0, 350, 553, 571, + 367, 540, 586, 372, 548, 564, 362, 501, 536, 0, + 0, 352, 569, 547, 479, 351, 0, 529, 392, 409, + 389, 499, 0, 0, 568, 599, 388, 589, 0, 579, + 354, 0, 578, 498, 471, 565, 570, 480, 473, 0, + 353, 567, 478, 472, 448, 399, 615, 449, 450, 451, + 452, 453, 454, 413, 514, 469, 515, 414, 486, 485, + 487, 415, 416, 417, 418, 419, 420, 421, 422, 423, + 424, 0, 0, 0, 0, 0, 610, 611, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 747, 0, 0, 752, 0, 582, 0, + 0, 0, 0, 0, 0, 551, 0, 0, 455, 0, + 0, 0, 600, 0, 532, 504, 793, 0, 0, 530, + 467, 566, 516, 572, 554, 580, 523, 517, 344, 555, + 391, 481, 363, 365, 780, 393, 396, 400, 401, 492, + 493, 510, 539, 558, 559, 560, 390, 377, 531, 378, + 410, 379, 345, 382, 381, 383, 541, 384, 347, 511, + 563, 0, 406, 527, 476, 348, 475, 512, 562, 561, + 364, 590, 597, 598, 689, 0, 603, 794, 795, 796, + 612, 0, 518, 360, 357, 0, 0, 0, 386, 513, + 373, 375, 376, 374, 507, 509, 617, 618, 619, 621, + 0, 622, 623, 0, 0, 0, 0, 624, 690, 708, + 674, 643, 605, 698, 640, 644, 645, 427, 428, 429, + 430, 711, 0, 0, 0, 596, 456, 457, 0, 398, + 397, 477, 349, 0, 0, 445, 426, 519, 355, 394, + 447, 432, 458, 459, 460, 461, 462, 0, 463, 464, + 404, 339, 340, 788, 387, 500, 713, 750, 751, 635, + 0, 699, 636, 646, 380, 671, 683, 682, 494, 595, + 0, 694, 697, 625, 787, 0, 691, 705, 792, 704, + 784, 506, 0, 538, 702, 649, 0, 695, 668, 669, + 0, 696, 664, 700, 356, 433, 524, 581, 0, 637, + 0, 606, 609, 639, 726, 727, 728, 346, 608, 730, + 731, 732, 733, 734, 735, 736, 729, 577, 672, 648, + 675, 587, 651, 650, 0, 0, 686, 604, 687, 688, + 488, 489, 490, 491, 408, 714, 371, 607, 521, 522, + 0, 673, 0, 0, 0, 0, 0, 0, 0, 0, + 678, 679, 676, 797, 0, 737, 738, 0, 0, 601, + 602, 403, 0, 620, 411, 370, 505, 405, 585, 436, + 0, 613, 680, 614, 525, 526, 741, 746, 742, 743, + 745, 766, 495, 425, 431, 542, 446, 468, 528, 584, + 503, 533, 368, 573, 544, 474, 665, 693, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 325, 326, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 718, 717, 716, 715, 714, - 713, 712, 711, 0, 0, 658, 384, 781, 775, 782, - 763, 766, 765, 740, 0, 341, 638, 464, 518, 401, - 705, 706, 0, 761, 284, 285, 286, 287, 288, 289, + 0, 0, 0, 0, 0, 722, 721, 720, 719, 718, + 717, 716, 715, 0, 0, 662, 385, 785, 779, 786, + 767, 770, 769, 744, 0, 341, 642, 466, 520, 402, + 709, 710, 0, 765, 284, 285, 286, 287, 288, 289, 290, 291, 332, 292, 293, 294, 295, 296, 297, 298, - 303, 304, 305, 306, 307, 308, 309, 310, 708, 301, + 303, 304, 305, 306, 307, 308, 309, 310, 712, 301, 302, 311, 312, 313, 314, 315, 316, 317, 318, 319, 320, 321, 322, 323, 324, 0, 0, 0, 0, 333, - 767, 768, 769, 770, 771, 0, 0, 334, 335, 336, - 0, 0, 299, 300, 327, 549, 328, 329, 330, 331, - 0, 0, 587, 588, 589, 612, 0, 590, 572, 637, - 411, 342, 553, 579, 778, 779, 0, 0, 0, 0, - 0, 0, 0, 688, 699, 735, 736, 0, 749, 750, - 752, 754, 753, 756, 546, 547, 764, 0, 0, 435, - 357, 358, 494, 495, 532, 0, 436, 437, 438, 442, - 439, 440, 441, 758, 759, 760, 757, 468, 531, 554, - 540, 777, 481, 0, 785, 627, 628, 786, 745, 744, - 343, 0, 337, 338, 702, 703, 0, 0, 534, 480, - 634, 500, 0, 0, 643, 677, 666, 755, 630, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 394, - 0, 0, 463, 681, 662, 673, 663, 506, 648, 649, - 650, 657, 406, 651, 787, 653, 622, 654, 623, 655, - 656, 0, 680, 629, 542, 482, 0, 697, 0, 0, + 771, 772, 773, 774, 775, 0, 0, 334, 335, 336, + 0, 0, 299, 300, 327, 552, 328, 329, 330, 331, + 0, 0, 591, 592, 593, 616, 0, 594, 575, 641, + 412, 342, 556, 583, 782, 783, 0, 0, 0, 0, + 0, 0, 0, 692, 703, 739, 740, 0, 753, 754, + 756, 758, 757, 760, 549, 550, 768, 0, 0, 437, + 358, 359, 496, 497, 535, 0, 438, 439, 440, 444, + 441, 442, 443, 762, 763, 764, 761, 470, 534, 557, + 543, 781, 483, 0, 789, 631, 632, 790, 749, 748, + 343, 0, 337, 338, 706, 707, 0, 0, 537, 482, + 638, 502, 0, 0, 647, 681, 670, 759, 634, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 395, + 0, 0, 465, 685, 666, 677, 667, 508, 652, 653, + 654, 661, 407, 655, 791, 657, 626, 658, 627, 659, + 660, 0, 684, 633, 545, 484, 0, 701, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 268, - 0, 0, 4035, 0, 0, 0, 365, 269, 624, 751, - 626, 625, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 368, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 543, 571, 0, 584, 0, 432, 433, 0, - 0, 0, 0, 0, 0, 0, 350, 550, 568, 366, - 537, 582, 371, 545, 561, 361, 499, 533, 0, 0, - 352, 566, 544, 477, 351, 0, 526, 391, 408, 388, - 497, 0, 0, 565, 595, 387, 585, 0, 576, 354, - 0, 575, 496, 469, 562, 567, 478, 471, 0, 353, - 564, 476, 470, 446, 398, 611, 447, 448, 449, 450, - 451, 452, 412, 512, 467, 513, 413, 484, 483, 485, - 414, 415, 416, 417, 418, 419, 420, 421, 422, 423, - 0, 0, 0, 0, 0, 606, 607, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 743, 0, 0, 748, 0, 578, 0, 0, - 0, 0, 0, 0, 548, 0, 0, 453, 0, 0, - 0, 596, 0, 529, 502, 789, 0, 0, 527, 465, - 563, 514, 569, 551, 577, 521, 515, 344, 552, 390, - 479, 362, 364, 776, 392, 395, 399, 400, 490, 491, - 508, 536, 555, 556, 557, 389, 376, 528, 377, 409, - 378, 345, 381, 380, 382, 538, 383, 347, 509, 560, - 0, 405, 524, 474, 348, 473, 510, 559, 558, 363, - 586, 593, 594, 685, 0, 599, 790, 791, 792, 608, - 0, 516, 359, 356, 0, 0, 0, 385, 511, 372, - 374, 375, 373, 505, 507, 613, 614, 615, 617, 0, - 618, 619, 0, 0, 0, 0, 620, 686, 704, 670, - 639, 601, 694, 636, 640, 641, 426, 427, 428, 429, - 707, 0, 0, 0, 592, 454, 455, 0, 397, 396, - 475, 349, 0, 0, 443, 425, 517, 355, 393, 445, - 431, 456, 457, 458, 459, 460, 0, 461, 462, 403, - 339, 340, 784, 386, 498, 709, 746, 747, 631, 0, - 695, 632, 642, 379, 667, 679, 678, 492, 591, 0, - 690, 693, 621, 783, 0, 687, 701, 788, 700, 780, - 504, 0, 535, 698, 645, 0, 691, 664, 665, 0, - 692, 660, 696, 0, 633, 0, 602, 605, 635, 722, - 723, 724, 346, 604, 726, 727, 728, 729, 730, 731, - 732, 725, 574, 668, 644, 671, 583, 647, 646, 0, - 0, 682, 600, 683, 684, 486, 487, 488, 489, 407, - 710, 370, 603, 519, 520, 0, 669, 0, 0, 0, - 0, 0, 0, 0, 0, 674, 675, 672, 793, 0, - 733, 734, 0, 0, 597, 598, 402, 0, 616, 410, - 369, 503, 404, 581, 434, 0, 609, 676, 610, 522, - 523, 737, 742, 738, 739, 741, 762, 493, 424, 430, - 539, 444, 466, 525, 580, 501, 530, 367, 570, 541, - 472, 661, 689, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 325, 326, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 366, 269, 628, 755, + 630, 629, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 369, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 3839, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 546, 574, 0, 588, 0, 434, 435, 0, + 0, 0, 0, 0, 0, 0, 350, 553, 571, 367, + 540, 586, 372, 548, 564, 362, 501, 536, 0, 0, + 352, 569, 547, 479, 351, 0, 529, 392, 409, 389, + 499, 0, 0, 568, 599, 388, 589, 0, 579, 354, + 0, 578, 498, 471, 565, 570, 480, 473, 0, 353, + 567, 478, 472, 448, 399, 615, 449, 450, 451, 452, + 453, 454, 413, 514, 469, 515, 414, 486, 485, 487, + 415, 416, 417, 418, 419, 420, 421, 422, 423, 424, + 0, 0, 0, 0, 0, 610, 611, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 747, 0, 0, 752, 0, 582, 0, 0, + 0, 0, 0, 0, 551, 0, 0, 455, 0, 0, + 0, 600, 0, 532, 504, 793, 0, 0, 530, 467, + 566, 516, 572, 554, 580, 523, 517, 344, 555, 391, + 481, 363, 365, 780, 393, 396, 400, 401, 492, 493, + 510, 539, 558, 559, 560, 390, 377, 531, 378, 410, + 379, 345, 382, 381, 383, 541, 384, 347, 511, 563, + 0, 406, 527, 476, 348, 475, 512, 562, 561, 364, + 590, 597, 598, 689, 0, 603, 794, 795, 796, 612, + 0, 518, 360, 357, 0, 0, 0, 386, 513, 373, + 375, 376, 374, 507, 509, 617, 618, 619, 621, 0, + 622, 623, 0, 0, 0, 0, 624, 690, 708, 674, + 643, 605, 698, 640, 644, 645, 427, 428, 429, 430, + 711, 0, 0, 0, 596, 456, 457, 0, 398, 397, + 477, 349, 0, 0, 445, 426, 519, 355, 394, 447, + 432, 458, 459, 460, 461, 462, 0, 463, 464, 404, + 339, 340, 788, 387, 500, 713, 750, 751, 635, 0, + 699, 636, 646, 380, 671, 683, 682, 494, 595, 0, + 694, 697, 625, 787, 0, 691, 705, 792, 704, 784, + 506, 0, 538, 702, 649, 0, 695, 668, 669, 0, + 696, 664, 700, 356, 433, 524, 581, 0, 637, 0, + 606, 609, 639, 726, 727, 728, 346, 608, 730, 731, + 732, 733, 734, 735, 736, 729, 577, 672, 648, 675, + 587, 651, 650, 0, 0, 686, 604, 687, 688, 488, + 489, 490, 491, 408, 714, 371, 607, 521, 522, 0, + 673, 0, 0, 0, 0, 0, 0, 0, 0, 678, + 679, 676, 797, 0, 737, 738, 0, 0, 601, 602, + 403, 0, 620, 411, 370, 505, 405, 585, 436, 0, + 613, 680, 614, 525, 526, 741, 746, 742, 743, 745, + 766, 495, 425, 431, 542, 446, 468, 528, 584, 503, + 533, 368, 573, 544, 474, 665, 693, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 325, 326, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 718, 717, 716, 715, 714, 713, 712, 711, 0, 0, - 658, 384, 781, 775, 782, 763, 766, 765, 740, 0, - 341, 638, 464, 518, 401, 705, 706, 0, 761, 284, - 285, 286, 287, 288, 289, 290, 291, 332, 292, 293, - 294, 295, 296, 297, 298, 303, 304, 305, 306, 307, - 308, 309, 310, 708, 301, 302, 311, 312, 313, 314, - 315, 316, 317, 318, 319, 320, 321, 322, 323, 324, - 0, 0, 0, 0, 333, 767, 768, 769, 770, 771, - 0, 0, 334, 335, 336, 0, 0, 299, 300, 327, - 549, 328, 329, 330, 331, 0, 0, 587, 588, 589, - 612, 0, 590, 572, 637, 411, 342, 553, 579, 778, - 779, 0, 0, 0, 0, 0, 0, 0, 688, 699, - 735, 736, 0, 749, 750, 752, 754, 753, 756, 546, - 547, 764, 0, 0, 435, 357, 358, 494, 495, 532, - 0, 436, 437, 438, 442, 439, 440, 441, 758, 759, - 760, 757, 468, 531, 554, 540, 777, 481, 0, 785, - 627, 628, 786, 745, 744, 343, 0, 337, 338, 702, - 703, 3964, 0, 534, 480, 634, 0, 500, 0, 0, - 643, 677, 666, 755, 630, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 394, 0, 0, 463, 681, - 662, 673, 663, 506, 648, 649, 650, 657, 406, 651, - 787, 653, 622, 654, 623, 655, 656, 0, 680, 629, - 542, 482, 0, 697, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 722, 721, 720, 719, 718, 717, + 716, 715, 0, 0, 662, 385, 785, 779, 786, 767, + 770, 769, 744, 0, 341, 642, 466, 520, 402, 709, + 710, 0, 765, 284, 285, 286, 287, 288, 289, 290, + 291, 332, 292, 293, 294, 295, 296, 297, 298, 303, + 304, 305, 306, 307, 308, 309, 310, 712, 301, 302, + 311, 312, 313, 314, 315, 316, 317, 318, 319, 320, + 321, 322, 323, 324, 0, 0, 0, 0, 333, 771, + 772, 773, 774, 775, 0, 0, 334, 335, 336, 0, + 0, 299, 300, 327, 552, 328, 329, 330, 331, 0, + 0, 591, 592, 593, 616, 0, 594, 575, 641, 412, + 342, 556, 583, 782, 783, 0, 0, 0, 0, 0, + 0, 0, 692, 703, 739, 740, 0, 753, 754, 756, + 758, 757, 760, 549, 550, 768, 0, 0, 437, 358, + 359, 496, 497, 535, 0, 438, 439, 440, 444, 441, + 442, 443, 762, 763, 764, 761, 470, 534, 557, 543, + 781, 483, 0, 789, 631, 632, 790, 749, 748, 343, + 0, 337, 338, 706, 707, 0, 0, 537, 482, 638, + 502, 0, 0, 647, 681, 670, 759, 634, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 395, 0, + 0, 465, 685, 666, 677, 667, 508, 652, 653, 654, + 661, 407, 655, 791, 657, 626, 658, 627, 659, 660, + 0, 684, 633, 545, 484, 0, 701, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 268, 0, 0, 0, 0, - 0, 0, 365, 269, 624, 751, 626, 625, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 368, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 543, 571, - 0, 584, 0, 432, 433, 0, 0, 0, 0, 0, - 0, 0, 350, 550, 568, 366, 537, 582, 371, 545, - 561, 361, 499, 533, 0, 0, 352, 566, 544, 477, - 351, 0, 526, 391, 408, 388, 497, 0, 0, 565, - 595, 387, 585, 0, 576, 354, 0, 575, 496, 469, - 562, 567, 478, 471, 0, 353, 564, 476, 470, 446, - 398, 611, 447, 448, 449, 450, 451, 452, 412, 512, - 467, 513, 413, 484, 483, 485, 414, 415, 416, 417, - 418, 419, 420, 421, 422, 423, 0, 0, 0, 0, - 0, 606, 607, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 743, 0, - 0, 748, 0, 578, 0, 0, 0, 0, 0, 0, - 548, 0, 0, 453, 0, 0, 0, 596, 0, 529, - 502, 789, 0, 0, 527, 465, 563, 514, 569, 551, - 577, 521, 515, 344, 552, 390, 479, 362, 364, 776, - 392, 395, 399, 400, 490, 491, 508, 536, 555, 556, - 557, 389, 376, 528, 377, 409, 378, 345, 381, 380, - 382, 538, 383, 347, 509, 560, 0, 405, 524, 474, - 348, 473, 510, 559, 558, 363, 586, 593, 594, 685, - 0, 599, 790, 791, 792, 608, 0, 516, 359, 356, - 0, 0, 0, 385, 511, 372, 374, 375, 373, 505, - 507, 613, 614, 615, 617, 0, 618, 619, 0, 0, - 0, 0, 620, 686, 704, 670, 639, 601, 694, 636, - 640, 641, 426, 427, 428, 429, 707, 0, 0, 0, - 592, 454, 455, 0, 397, 396, 475, 349, 0, 0, - 443, 425, 517, 355, 393, 445, 431, 456, 457, 458, - 459, 460, 0, 461, 462, 403, 339, 340, 784, 386, - 498, 709, 746, 747, 631, 0, 695, 632, 642, 379, - 667, 679, 678, 492, 591, 0, 690, 693, 621, 783, - 0, 687, 701, 788, 700, 780, 504, 0, 535, 698, - 645, 0, 691, 664, 665, 0, 692, 660, 696, 0, - 633, 0, 602, 605, 635, 722, 723, 724, 346, 604, - 726, 727, 728, 729, 730, 731, 732, 725, 574, 668, - 644, 671, 583, 647, 646, 0, 0, 682, 600, 683, - 684, 486, 487, 488, 489, 407, 710, 370, 603, 519, - 520, 0, 669, 0, 0, 0, 0, 0, 0, 0, - 0, 674, 675, 672, 793, 0, 733, 734, 0, 0, - 597, 598, 402, 0, 616, 410, 369, 503, 404, 581, - 434, 0, 609, 676, 610, 522, 523, 737, 742, 738, - 739, 741, 762, 493, 424, 430, 539, 444, 466, 525, - 580, 501, 530, 367, 570, 541, 472, 661, 689, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 268, 0, + 0, 1870, 0, 0, 0, 366, 269, 628, 755, 630, + 629, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 369, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 546, 574, 0, 588, 0, 434, 435, 0, 0, + 0, 0, 0, 0, 0, 350, 553, 571, 367, 540, + 586, 372, 548, 564, 362, 501, 536, 0, 0, 352, + 569, 547, 479, 351, 0, 529, 392, 409, 389, 499, + 0, 0, 568, 599, 388, 589, 0, 579, 354, 0, + 578, 498, 471, 565, 570, 480, 473, 0, 353, 567, + 478, 472, 448, 399, 615, 449, 450, 451, 452, 453, + 454, 413, 514, 469, 515, 414, 486, 485, 487, 415, + 416, 417, 418, 419, 420, 421, 422, 423, 424, 0, + 0, 0, 0, 0, 610, 611, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 747, 0, 0, 752, 0, 582, 0, 0, 0, + 0, 0, 0, 551, 0, 0, 455, 0, 0, 0, + 600, 0, 532, 504, 793, 0, 0, 530, 467, 566, + 516, 572, 554, 580, 523, 517, 344, 555, 391, 481, + 363, 365, 780, 393, 396, 400, 401, 492, 493, 510, + 539, 558, 559, 560, 390, 377, 531, 378, 410, 379, + 345, 382, 381, 383, 541, 384, 347, 511, 563, 0, + 406, 527, 476, 348, 475, 512, 562, 561, 364, 590, + 597, 598, 689, 0, 603, 794, 795, 796, 612, 0, + 518, 360, 357, 0, 0, 0, 386, 513, 373, 375, + 376, 374, 507, 509, 617, 618, 619, 621, 0, 622, + 623, 0, 0, 0, 0, 624, 690, 708, 674, 643, + 605, 698, 640, 644, 645, 427, 428, 429, 430, 711, + 0, 0, 0, 596, 456, 457, 0, 398, 397, 477, + 349, 0, 0, 445, 426, 519, 355, 394, 447, 432, + 458, 459, 460, 461, 462, 0, 463, 464, 404, 339, + 340, 788, 387, 500, 713, 750, 751, 635, 0, 699, + 636, 646, 380, 671, 683, 682, 494, 595, 0, 694, + 697, 625, 787, 0, 691, 705, 792, 704, 784, 506, + 0, 538, 702, 649, 0, 695, 668, 669, 0, 696, + 664, 700, 356, 433, 524, 581, 0, 637, 0, 606, + 609, 639, 726, 727, 728, 346, 608, 730, 731, 732, + 733, 734, 735, 736, 729, 577, 672, 648, 675, 587, + 651, 650, 0, 0, 686, 604, 687, 688, 488, 489, + 490, 491, 408, 714, 371, 607, 521, 522, 0, 673, + 0, 0, 0, 0, 0, 0, 0, 0, 678, 679, + 676, 797, 0, 737, 738, 0, 0, 601, 602, 403, + 0, 620, 411, 370, 505, 405, 585, 436, 0, 613, + 680, 614, 525, 526, 741, 746, 742, 743, 745, 766, + 495, 425, 431, 542, 446, 468, 528, 584, 503, 533, + 368, 573, 544, 474, 665, 693, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 325, 326, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 325, 326, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 718, 717, 716, 715, - 714, 713, 712, 711, 0, 0, 658, 384, 781, 775, - 782, 763, 766, 765, 740, 0, 341, 638, 464, 518, - 401, 705, 706, 0, 761, 284, 285, 286, 287, 288, - 289, 290, 291, 332, 292, 293, 294, 295, 296, 297, - 298, 303, 304, 305, 306, 307, 308, 309, 310, 708, - 301, 302, 311, 312, 313, 314, 315, 316, 317, 318, - 319, 320, 321, 322, 323, 324, 0, 0, 0, 0, - 333, 767, 768, 769, 770, 771, 0, 0, 334, 335, - 336, 0, 0, 299, 300, 327, 549, 328, 329, 330, - 331, 0, 0, 587, 588, 589, 612, 0, 590, 572, - 637, 411, 342, 553, 579, 778, 779, 0, 0, 0, - 0, 0, 0, 0, 688, 699, 735, 736, 0, 749, - 750, 752, 754, 753, 756, 546, 547, 764, 0, 0, - 435, 357, 358, 494, 495, 532, 0, 436, 437, 438, - 442, 439, 440, 441, 758, 759, 760, 757, 468, 531, - 554, 540, 777, 481, 0, 785, 627, 628, 786, 745, - 744, 343, 0, 337, 338, 702, 703, 0, 0, 534, - 480, 634, 500, 0, 0, 643, 677, 666, 755, 630, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 394, 0, 0, 463, 681, 662, 673, 663, 506, 648, - 649, 650, 657, 406, 651, 787, 653, 622, 654, 623, - 655, 656, 0, 680, 629, 542, 482, 0, 697, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 268, 0, 0, 0, 0, 0, 0, 365, 269, 624, - 751, 626, 625, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 368, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 3835, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 543, 571, 0, 584, 0, 432, 433, - 0, 0, 0, 0, 0, 0, 0, 350, 550, 568, - 366, 537, 582, 371, 545, 561, 361, 499, 533, 0, - 0, 352, 566, 544, 477, 351, 0, 526, 391, 408, - 388, 497, 0, 0, 565, 595, 387, 585, 0, 576, - 354, 0, 575, 496, 469, 562, 567, 478, 471, 0, - 353, 564, 476, 470, 446, 398, 611, 447, 448, 449, - 450, 451, 452, 412, 512, 467, 513, 413, 484, 483, - 485, 414, 415, 416, 417, 418, 419, 420, 421, 422, - 423, 0, 0, 0, 0, 0, 606, 607, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 743, 0, 0, 748, 0, 578, 0, - 0, 0, 0, 0, 0, 548, 0, 0, 453, 0, - 0, 0, 596, 0, 529, 502, 789, 0, 0, 527, - 465, 563, 514, 569, 551, 577, 521, 515, 344, 552, - 390, 479, 362, 364, 776, 392, 395, 399, 400, 490, - 491, 508, 536, 555, 556, 557, 389, 376, 528, 377, - 409, 378, 345, 381, 380, 382, 538, 383, 347, 509, - 560, 0, 405, 524, 474, 348, 473, 510, 559, 558, - 363, 586, 593, 594, 685, 0, 599, 790, 791, 792, - 608, 0, 516, 359, 356, 0, 0, 0, 385, 511, - 372, 374, 375, 373, 505, 507, 613, 614, 615, 617, - 0, 618, 619, 0, 0, 0, 0, 620, 686, 704, - 670, 639, 601, 694, 636, 640, 641, 426, 427, 428, - 429, 707, 0, 0, 0, 592, 454, 455, 0, 397, - 396, 475, 349, 0, 0, 443, 425, 517, 355, 393, - 445, 431, 456, 457, 458, 459, 460, 0, 461, 462, - 403, 339, 340, 784, 386, 498, 709, 746, 747, 631, - 0, 695, 632, 642, 379, 667, 679, 678, 492, 591, - 0, 690, 693, 621, 783, 0, 687, 701, 788, 700, - 780, 504, 0, 535, 698, 645, 0, 691, 664, 665, - 0, 692, 660, 696, 0, 633, 0, 602, 605, 635, - 722, 723, 724, 346, 604, 726, 727, 728, 729, 730, - 731, 732, 725, 574, 668, 644, 671, 583, 647, 646, - 0, 0, 682, 600, 683, 684, 486, 487, 488, 489, - 407, 710, 370, 603, 519, 520, 0, 669, 0, 0, - 0, 0, 0, 0, 0, 0, 674, 675, 672, 793, - 0, 733, 734, 0, 0, 597, 598, 402, 0, 616, - 410, 369, 503, 404, 581, 434, 0, 609, 676, 610, - 522, 523, 737, 742, 738, 739, 741, 762, 493, 424, - 430, 539, 444, 466, 525, 580, 501, 530, 367, 570, - 541, 472, 661, 689, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 325, 326, 0, 0, 0, + 0, 0, 0, 722, 721, 720, 719, 718, 717, 716, + 715, 0, 0, 662, 385, 785, 779, 786, 767, 770, + 769, 744, 0, 341, 642, 466, 520, 402, 709, 710, + 0, 765, 284, 285, 286, 287, 288, 289, 290, 291, + 332, 292, 293, 294, 295, 296, 297, 298, 303, 304, + 305, 306, 307, 308, 309, 310, 712, 301, 302, 311, + 312, 313, 314, 315, 316, 317, 318, 319, 320, 321, + 322, 323, 324, 0, 0, 0, 0, 333, 771, 772, + 773, 774, 775, 0, 0, 334, 335, 336, 0, 0, + 299, 300, 327, 552, 328, 329, 330, 331, 0, 0, + 591, 592, 593, 616, 0, 594, 575, 641, 412, 342, + 556, 583, 782, 783, 0, 0, 0, 0, 0, 0, + 0, 692, 703, 739, 740, 0, 753, 754, 756, 758, + 757, 760, 549, 550, 768, 0, 0, 437, 358, 359, + 496, 497, 535, 0, 438, 439, 440, 444, 441, 442, + 443, 762, 763, 764, 761, 470, 534, 557, 543, 781, + 483, 0, 789, 631, 632, 790, 749, 748, 343, 0, + 337, 338, 706, 707, 0, 0, 537, 482, 638, 502, + 0, 0, 647, 681, 670, 759, 634, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 395, 0, 0, + 465, 685, 666, 677, 667, 508, 652, 653, 654, 661, + 407, 655, 791, 657, 626, 658, 627, 659, 660, 0, + 684, 633, 545, 484, 0, 701, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 268, 0, 0, + 2453, 0, 0, 0, 366, 269, 628, 755, 630, 629, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 369, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 546, 574, 0, 588, 0, 434, 435, 0, 0, 0, + 0, 0, 0, 0, 350, 553, 571, 367, 540, 586, + 372, 548, 564, 362, 501, 536, 0, 0, 352, 569, + 547, 479, 351, 0, 529, 392, 409, 389, 499, 0, + 0, 568, 599, 388, 589, 0, 579, 354, 0, 578, + 498, 471, 565, 570, 480, 473, 0, 353, 567, 478, + 472, 448, 399, 615, 449, 450, 451, 452, 453, 454, + 413, 514, 469, 515, 414, 486, 485, 487, 415, 416, + 417, 418, 419, 420, 421, 422, 423, 424, 0, 0, + 0, 0, 0, 610, 611, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 747, 0, 0, 752, 0, 582, 0, 0, 0, 0, + 0, 0, 551, 0, 0, 455, 0, 0, 0, 600, + 0, 532, 504, 793, 0, 0, 530, 467, 566, 516, + 572, 554, 580, 523, 517, 344, 555, 391, 481, 363, + 365, 780, 393, 396, 400, 401, 492, 493, 510, 539, + 558, 559, 560, 390, 377, 531, 378, 410, 379, 345, + 382, 381, 383, 541, 384, 347, 511, 563, 0, 406, + 527, 476, 348, 475, 512, 562, 561, 364, 590, 597, + 598, 689, 0, 603, 794, 795, 796, 612, 0, 518, + 360, 357, 0, 0, 0, 386, 513, 373, 375, 376, + 374, 507, 509, 617, 618, 619, 621, 0, 622, 623, + 0, 0, 0, 0, 624, 690, 708, 674, 643, 605, + 698, 640, 644, 645, 427, 428, 429, 430, 711, 0, + 0, 0, 596, 456, 457, 0, 398, 397, 477, 349, + 0, 0, 445, 426, 519, 355, 394, 447, 432, 458, + 459, 460, 461, 462, 0, 463, 464, 404, 339, 340, + 788, 387, 500, 713, 750, 751, 635, 0, 699, 636, + 646, 380, 671, 683, 682, 494, 595, 0, 694, 697, + 625, 787, 0, 691, 705, 792, 704, 784, 506, 0, + 538, 702, 649, 0, 695, 668, 669, 0, 696, 664, + 700, 356, 433, 524, 581, 0, 637, 0, 606, 609, + 639, 726, 727, 728, 346, 608, 730, 731, 732, 733, + 734, 735, 736, 729, 577, 672, 648, 675, 587, 651, + 650, 0, 0, 686, 604, 687, 688, 488, 489, 490, + 491, 408, 714, 371, 607, 521, 522, 0, 673, 0, + 0, 0, 0, 0, 0, 0, 0, 678, 679, 676, + 797, 0, 737, 738, 0, 0, 601, 602, 403, 0, + 620, 411, 370, 505, 405, 585, 436, 0, 613, 680, + 614, 525, 526, 741, 746, 742, 743, 745, 766, 495, + 425, 431, 542, 446, 468, 528, 584, 503, 533, 368, + 573, 544, 474, 665, 693, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 325, 326, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 718, 717, 716, 715, 714, 713, 712, 711, 0, - 0, 658, 384, 781, 775, 782, 763, 766, 765, 740, - 0, 341, 638, 464, 518, 401, 705, 706, 0, 761, - 284, 285, 286, 287, 288, 289, 290, 291, 332, 292, - 293, 294, 295, 296, 297, 298, 303, 304, 305, 306, - 307, 308, 309, 310, 708, 301, 302, 311, 312, 313, - 314, 315, 316, 317, 318, 319, 320, 321, 322, 323, - 324, 0, 0, 0, 0, 333, 767, 768, 769, 770, - 771, 0, 0, 334, 335, 336, 0, 0, 299, 300, - 327, 549, 328, 329, 330, 331, 0, 0, 587, 588, - 589, 612, 0, 590, 572, 637, 411, 342, 553, 579, - 778, 779, 0, 0, 0, 0, 0, 0, 0, 688, - 699, 735, 736, 0, 749, 750, 752, 754, 753, 756, - 546, 547, 764, 0, 0, 435, 357, 358, 494, 495, - 532, 0, 436, 437, 438, 442, 439, 440, 441, 758, - 759, 760, 757, 468, 531, 554, 540, 777, 481, 0, - 785, 627, 628, 786, 745, 744, 343, 0, 337, 338, - 702, 703, 0, 0, 534, 480, 634, 500, 0, 0, - 643, 677, 666, 755, 630, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 394, 0, 0, 463, 681, - 662, 673, 663, 506, 648, 649, 650, 657, 406, 651, - 787, 653, 622, 654, 623, 655, 656, 0, 680, 629, - 542, 482, 0, 697, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 268, 0, 0, 1866, 0, - 0, 0, 365, 269, 624, 751, 626, 625, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 368, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 543, 571, - 0, 584, 0, 432, 433, 0, 0, 0, 0, 0, - 0, 0, 350, 550, 568, 366, 537, 582, 371, 545, - 561, 361, 499, 533, 0, 0, 352, 566, 544, 477, - 351, 0, 526, 391, 408, 388, 497, 0, 0, 565, - 595, 387, 585, 0, 576, 354, 0, 575, 496, 469, - 562, 567, 478, 471, 0, 353, 564, 476, 470, 446, - 398, 611, 447, 448, 449, 450, 451, 452, 412, 512, - 467, 513, 413, 484, 483, 485, 414, 415, 416, 417, - 418, 419, 420, 421, 422, 423, 0, 0, 0, 0, - 0, 606, 607, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 743, 0, - 0, 748, 0, 578, 0, 0, 0, 0, 0, 0, - 548, 0, 0, 453, 0, 0, 0, 596, 0, 529, - 502, 789, 0, 0, 527, 465, 563, 514, 569, 551, - 577, 521, 515, 344, 552, 390, 479, 362, 364, 776, - 392, 395, 399, 400, 490, 491, 508, 536, 555, 556, - 557, 389, 376, 528, 377, 409, 378, 345, 381, 380, - 382, 538, 383, 347, 509, 560, 0, 405, 524, 474, - 348, 473, 510, 559, 558, 363, 586, 593, 594, 685, - 0, 599, 790, 791, 792, 608, 0, 516, 359, 356, - 0, 0, 0, 385, 511, 372, 374, 375, 373, 505, - 507, 613, 614, 615, 617, 0, 618, 619, 0, 0, - 0, 0, 620, 686, 704, 670, 639, 601, 694, 636, - 640, 641, 426, 427, 428, 429, 707, 0, 0, 0, - 592, 454, 455, 0, 397, 396, 475, 349, 0, 0, - 443, 425, 517, 355, 393, 445, 431, 456, 457, 458, - 459, 460, 0, 461, 462, 403, 339, 340, 784, 386, - 498, 709, 746, 747, 631, 0, 695, 632, 642, 379, - 667, 679, 678, 492, 591, 0, 690, 693, 621, 783, - 0, 687, 701, 788, 700, 780, 504, 0, 535, 698, - 645, 0, 691, 664, 665, 0, 692, 660, 696, 0, - 633, 0, 602, 605, 635, 722, 723, 724, 346, 604, - 726, 727, 728, 729, 730, 731, 732, 725, 574, 668, - 644, 671, 583, 647, 646, 0, 0, 682, 600, 683, - 684, 486, 487, 488, 489, 407, 710, 370, 603, 519, - 520, 0, 669, 0, 0, 0, 0, 0, 0, 0, - 0, 674, 675, 672, 793, 0, 733, 734, 0, 0, - 597, 598, 402, 0, 616, 410, 369, 503, 404, 581, - 434, 0, 609, 676, 610, 522, 523, 737, 742, 738, - 739, 741, 762, 493, 424, 430, 539, 444, 466, 525, - 580, 501, 530, 367, 570, 541, 472, 661, 689, 0, + 0, 0, 722, 721, 720, 719, 718, 717, 716, 715, + 0, 0, 662, 385, 785, 779, 786, 767, 770, 769, + 744, 0, 341, 642, 466, 520, 402, 709, 710, 0, + 765, 284, 285, 286, 287, 288, 289, 290, 291, 332, + 292, 293, 294, 295, 296, 297, 298, 303, 304, 305, + 306, 307, 308, 309, 310, 712, 301, 302, 311, 312, + 313, 314, 315, 316, 317, 318, 319, 320, 321, 322, + 323, 324, 0, 0, 0, 0, 333, 771, 772, 773, + 774, 775, 0, 0, 334, 335, 336, 0, 0, 299, + 300, 327, 552, 328, 329, 330, 331, 0, 0, 591, + 592, 593, 616, 0, 594, 575, 641, 412, 342, 556, + 583, 782, 783, 0, 0, 0, 0, 0, 0, 0, + 692, 703, 739, 740, 0, 753, 754, 756, 758, 757, + 760, 549, 550, 768, 0, 0, 437, 358, 359, 496, + 497, 535, 0, 438, 439, 440, 444, 441, 442, 443, + 762, 763, 764, 761, 470, 534, 557, 543, 781, 483, + 0, 789, 631, 632, 790, 749, 748, 343, 0, 337, + 338, 706, 707, 0, 0, 537, 482, 638, 502, 0, + 0, 647, 681, 670, 759, 634, 0, 0, 3671, 0, + 0, 0, 0, 0, 0, 0, 395, 0, 0, 465, + 685, 666, 677, 667, 508, 652, 653, 654, 661, 407, + 655, 791, 657, 626, 658, 627, 659, 660, 0, 684, + 633, 545, 484, 0, 701, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 325, 326, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 718, 717, 716, 715, - 714, 713, 712, 711, 0, 0, 658, 384, 781, 775, - 782, 763, 766, 765, 740, 0, 341, 638, 464, 518, - 401, 705, 706, 0, 761, 284, 285, 286, 287, 288, - 289, 290, 291, 332, 292, 293, 294, 295, 296, 297, - 298, 303, 304, 305, 306, 307, 308, 309, 310, 708, - 301, 302, 311, 312, 313, 314, 315, 316, 317, 318, - 319, 320, 321, 322, 323, 324, 0, 0, 0, 0, - 333, 767, 768, 769, 770, 771, 0, 0, 334, 335, - 336, 0, 0, 299, 300, 327, 549, 328, 329, 330, - 331, 0, 0, 587, 588, 589, 612, 0, 590, 572, - 637, 411, 342, 553, 579, 778, 779, 0, 0, 0, - 0, 0, 0, 0, 688, 699, 735, 736, 0, 749, - 750, 752, 754, 753, 756, 546, 547, 764, 0, 0, - 435, 357, 358, 494, 495, 532, 0, 436, 437, 438, - 442, 439, 440, 441, 758, 759, 760, 757, 468, 531, - 554, 540, 777, 481, 0, 785, 627, 628, 786, 745, - 744, 343, 0, 337, 338, 702, 703, 0, 0, 534, - 480, 634, 500, 0, 0, 643, 677, 666, 755, 630, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 394, 0, 0, 463, 681, 662, 673, 663, 506, 648, - 649, 650, 657, 406, 651, 787, 653, 622, 654, 623, - 655, 656, 0, 680, 629, 542, 482, 0, 697, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 268, 0, 0, 2449, 0, 0, 0, 365, 269, 624, - 751, 626, 625, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 368, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 543, 571, 0, 584, 0, 432, 433, - 0, 0, 0, 0, 0, 0, 0, 350, 550, 568, - 366, 537, 582, 371, 545, 561, 361, 499, 533, 0, - 0, 352, 566, 544, 477, 351, 0, 526, 391, 408, - 388, 497, 0, 0, 565, 595, 387, 585, 0, 576, - 354, 0, 575, 496, 469, 562, 567, 478, 471, 0, - 353, 564, 476, 470, 446, 398, 611, 447, 448, 449, - 450, 451, 452, 412, 512, 467, 513, 413, 484, 483, - 485, 414, 415, 416, 417, 418, 419, 420, 421, 422, - 423, 0, 0, 0, 0, 0, 606, 607, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 743, 0, 0, 748, 0, 578, 0, - 0, 0, 0, 0, 0, 548, 0, 0, 453, 0, - 0, 0, 596, 0, 529, 502, 789, 0, 0, 527, - 465, 563, 514, 569, 551, 577, 521, 515, 344, 552, - 390, 479, 362, 364, 776, 392, 395, 399, 400, 490, - 491, 508, 536, 555, 556, 557, 389, 376, 528, 377, - 409, 378, 345, 381, 380, 382, 538, 383, 347, 509, - 560, 0, 405, 524, 474, 348, 473, 510, 559, 558, - 363, 586, 593, 594, 685, 0, 599, 790, 791, 792, - 608, 0, 516, 359, 356, 0, 0, 0, 385, 511, - 372, 374, 375, 373, 505, 507, 613, 614, 615, 617, - 0, 618, 619, 0, 0, 0, 0, 620, 686, 704, - 670, 639, 601, 694, 636, 640, 641, 426, 427, 428, - 429, 707, 0, 0, 0, 592, 454, 455, 0, 397, - 396, 475, 349, 0, 0, 443, 425, 517, 355, 393, - 445, 431, 456, 457, 458, 459, 460, 0, 461, 462, - 403, 339, 340, 784, 386, 498, 709, 746, 747, 631, - 0, 695, 632, 642, 379, 667, 679, 678, 492, 591, - 0, 690, 693, 621, 783, 0, 687, 701, 788, 700, - 780, 504, 0, 535, 698, 645, 0, 691, 664, 665, - 0, 692, 660, 696, 0, 633, 0, 602, 605, 635, - 722, 723, 724, 346, 604, 726, 727, 728, 729, 730, - 731, 732, 725, 574, 668, 644, 671, 583, 647, 646, - 0, 0, 682, 600, 683, 684, 486, 487, 488, 489, - 407, 710, 370, 603, 519, 520, 0, 669, 0, 0, - 0, 0, 0, 0, 0, 0, 674, 675, 672, 793, - 0, 733, 734, 0, 0, 597, 598, 402, 0, 616, - 410, 369, 503, 404, 581, 434, 0, 609, 676, 610, - 522, 523, 737, 742, 738, 739, 741, 762, 493, 424, - 430, 539, 444, 466, 525, 580, 501, 530, 367, 570, - 541, 472, 661, 689, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 268, 0, 0, 0, + 0, 0, 0, 366, 269, 628, 755, 630, 629, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 369, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 546, + 574, 0, 588, 0, 434, 435, 0, 0, 0, 0, + 0, 0, 0, 350, 553, 571, 367, 540, 586, 372, + 548, 564, 362, 501, 536, 0, 0, 352, 569, 547, + 479, 351, 0, 529, 392, 409, 389, 499, 0, 0, + 568, 599, 388, 589, 0, 579, 354, 0, 578, 498, + 471, 565, 570, 480, 473, 0, 353, 567, 478, 472, + 448, 399, 615, 449, 450, 451, 452, 453, 454, 413, + 514, 469, 515, 414, 486, 485, 487, 415, 416, 417, + 418, 419, 420, 421, 422, 423, 424, 0, 0, 0, + 0, 0, 610, 611, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 747, + 0, 0, 752, 0, 582, 0, 0, 0, 0, 0, + 0, 551, 0, 0, 455, 0, 0, 0, 600, 0, + 532, 504, 793, 0, 0, 530, 467, 566, 516, 572, + 554, 580, 523, 517, 344, 555, 391, 481, 363, 365, + 780, 393, 396, 400, 401, 492, 493, 510, 539, 558, + 559, 560, 390, 377, 531, 378, 410, 379, 345, 382, + 381, 383, 541, 384, 347, 511, 563, 0, 406, 527, + 476, 348, 475, 512, 562, 561, 364, 590, 597, 598, + 689, 0, 603, 794, 795, 796, 612, 0, 518, 360, + 357, 0, 0, 0, 386, 513, 373, 375, 376, 374, + 507, 509, 617, 618, 619, 621, 0, 622, 623, 0, + 0, 0, 0, 624, 690, 708, 674, 643, 605, 698, + 640, 644, 645, 427, 428, 429, 430, 711, 0, 0, + 0, 596, 456, 457, 0, 398, 397, 477, 349, 0, + 0, 445, 426, 519, 355, 394, 447, 432, 458, 459, + 460, 461, 462, 0, 463, 464, 404, 339, 340, 788, + 387, 500, 713, 750, 751, 635, 0, 699, 636, 646, + 380, 671, 683, 682, 494, 595, 0, 694, 697, 625, + 787, 0, 691, 705, 792, 704, 784, 506, 0, 538, + 702, 649, 0, 695, 668, 669, 0, 696, 664, 700, + 356, 433, 524, 581, 0, 637, 0, 606, 609, 639, + 726, 727, 728, 346, 608, 730, 731, 732, 733, 734, + 735, 736, 729, 577, 672, 648, 675, 587, 651, 650, + 0, 0, 686, 604, 687, 688, 488, 489, 490, 491, + 408, 714, 371, 607, 521, 522, 0, 673, 0, 0, + 0, 0, 0, 0, 0, 0, 678, 679, 676, 797, + 0, 737, 738, 0, 0, 601, 602, 403, 0, 620, + 411, 370, 505, 405, 585, 436, 0, 613, 680, 614, + 525, 526, 741, 746, 742, 743, 745, 766, 495, 425, + 431, 542, 446, 468, 528, 584, 503, 533, 368, 573, + 544, 474, 665, 693, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 325, 326, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 718, 717, 716, 715, 714, 713, 712, 711, 0, - 0, 658, 384, 781, 775, 782, 763, 766, 765, 740, - 0, 341, 638, 464, 518, 401, 705, 706, 0, 761, + 0, 722, 721, 720, 719, 718, 717, 716, 715, 0, + 0, 662, 385, 785, 779, 786, 767, 770, 769, 744, + 0, 341, 642, 466, 520, 402, 709, 710, 0, 765, 284, 285, 286, 287, 288, 289, 290, 291, 332, 292, 293, 294, 295, 296, 297, 298, 303, 304, 305, 306, - 307, 308, 309, 310, 708, 301, 302, 311, 312, 313, + 307, 308, 309, 310, 712, 301, 302, 311, 312, 313, 314, 315, 316, 317, 318, 319, 320, 321, 322, 323, - 324, 0, 0, 0, 0, 333, 767, 768, 769, 770, - 771, 0, 0, 334, 335, 336, 0, 0, 299, 300, - 327, 549, 328, 329, 330, 331, 0, 0, 587, 588, - 589, 612, 0, 590, 572, 637, 411, 342, 553, 579, - 778, 779, 0, 0, 0, 0, 0, 0, 0, 688, - 699, 735, 736, 0, 749, 750, 752, 754, 753, 756, - 546, 547, 764, 0, 0, 435, 357, 358, 494, 495, - 532, 0, 436, 437, 438, 442, 439, 440, 441, 758, - 759, 760, 757, 468, 531, 554, 540, 777, 481, 0, - 785, 627, 628, 786, 745, 744, 343, 0, 337, 338, - 702, 703, 0, 0, 534, 480, 634, 500, 0, 0, - 643, 677, 666, 755, 630, 0, 0, 3667, 0, 0, - 0, 0, 0, 0, 0, 394, 0, 0, 463, 681, - 662, 673, 663, 506, 648, 649, 650, 657, 406, 651, - 787, 653, 622, 654, 623, 655, 656, 0, 680, 629, - 542, 482, 0, 697, 0, 0, 0, 0, 0, 0, + 324, 0, 0, 0, 0, 333, 771, 772, 773, 774, + 775, 0, 0, 334, 335, 336, 0, 0, 299, 300, + 327, 552, 328, 329, 330, 331, 0, 0, 591, 592, + 593, 616, 0, 594, 575, 641, 412, 342, 556, 583, + 782, 783, 0, 0, 0, 0, 0, 0, 0, 692, + 703, 739, 740, 0, 753, 754, 756, 758, 757, 760, + 549, 550, 768, 0, 0, 437, 358, 359, 496, 497, + 535, 0, 438, 439, 440, 444, 441, 442, 443, 762, + 763, 764, 761, 470, 534, 557, 543, 781, 483, 0, + 789, 631, 632, 790, 749, 748, 343, 0, 337, 338, + 706, 707, 0, 0, 537, 482, 638, 502, 0, 0, + 647, 681, 670, 759, 634, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 395, 0, 0, 465, 685, + 666, 677, 667, 508, 652, 653, 654, 661, 407, 655, + 791, 657, 626, 658, 627, 659, 660, 0, 684, 633, + 545, 484, 0, 701, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 1816, 0, 0, 268, 0, 0, 0, 0, + 0, 0, 366, 269, 628, 755, 630, 629, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 369, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 546, 574, + 0, 588, 0, 434, 435, 0, 0, 0, 0, 0, + 0, 0, 350, 553, 571, 367, 540, 586, 372, 548, + 564, 362, 501, 536, 0, 0, 352, 569, 547, 479, + 351, 0, 529, 392, 409, 389, 499, 0, 0, 568, + 599, 388, 589, 0, 579, 354, 0, 578, 498, 471, + 565, 570, 480, 473, 0, 353, 567, 478, 472, 448, + 399, 615, 449, 450, 451, 452, 453, 454, 413, 514, + 469, 515, 414, 486, 485, 487, 415, 416, 417, 418, + 419, 420, 421, 422, 423, 424, 0, 0, 0, 0, + 0, 610, 611, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 747, 0, + 0, 752, 0, 582, 0, 0, 0, 0, 0, 0, + 551, 0, 0, 455, 0, 0, 0, 600, 0, 532, + 504, 793, 0, 0, 530, 467, 566, 516, 572, 554, + 580, 523, 517, 344, 555, 391, 481, 363, 365, 780, + 393, 396, 400, 401, 492, 493, 510, 539, 558, 559, + 560, 390, 377, 531, 378, 410, 379, 345, 382, 381, + 383, 541, 384, 347, 511, 563, 0, 406, 527, 476, + 348, 475, 512, 562, 561, 364, 590, 597, 598, 689, + 0, 603, 794, 795, 796, 612, 0, 518, 360, 357, + 0, 0, 0, 386, 513, 373, 375, 376, 374, 507, + 509, 617, 618, 619, 621, 0, 622, 623, 0, 0, + 0, 0, 624, 690, 708, 674, 643, 605, 698, 640, + 644, 645, 427, 428, 429, 430, 711, 0, 0, 0, + 596, 456, 457, 0, 398, 397, 477, 349, 0, 0, + 445, 426, 519, 355, 394, 447, 432, 458, 459, 460, + 461, 462, 0, 463, 464, 404, 339, 340, 788, 387, + 500, 713, 750, 751, 635, 0, 699, 636, 646, 380, + 671, 683, 682, 494, 595, 0, 694, 697, 625, 787, + 0, 691, 705, 792, 704, 784, 506, 0, 538, 702, + 649, 0, 695, 668, 669, 0, 696, 664, 700, 356, + 433, 524, 581, 0, 637, 0, 606, 609, 639, 726, + 727, 728, 346, 608, 730, 731, 732, 733, 734, 735, + 736, 729, 577, 672, 648, 675, 587, 651, 650, 0, + 0, 686, 604, 687, 688, 488, 489, 490, 491, 408, + 714, 371, 607, 521, 522, 0, 673, 0, 0, 0, + 0, 0, 0, 0, 0, 678, 679, 676, 797, 0, + 737, 738, 0, 0, 601, 602, 403, 0, 620, 411, + 370, 505, 405, 585, 436, 0, 613, 680, 614, 525, + 526, 741, 746, 742, 743, 745, 766, 495, 425, 431, + 542, 446, 468, 528, 584, 503, 533, 368, 573, 544, + 474, 665, 693, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 325, 326, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 268, 0, 0, 0, 0, - 0, 0, 365, 269, 624, 751, 626, 625, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 368, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 543, 571, - 0, 584, 0, 432, 433, 0, 0, 0, 0, 0, - 0, 0, 350, 550, 568, 366, 537, 582, 371, 545, - 561, 361, 499, 533, 0, 0, 352, 566, 544, 477, - 351, 0, 526, 391, 408, 388, 497, 0, 0, 565, - 595, 387, 585, 0, 576, 354, 0, 575, 496, 469, - 562, 567, 478, 471, 0, 353, 564, 476, 470, 446, - 398, 611, 447, 448, 449, 450, 451, 452, 412, 512, - 467, 513, 413, 484, 483, 485, 414, 415, 416, 417, - 418, 419, 420, 421, 422, 423, 0, 0, 0, 0, - 0, 606, 607, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 743, 0, - 0, 748, 0, 578, 0, 0, 0, 0, 0, 0, - 548, 0, 0, 453, 0, 0, 0, 596, 0, 529, - 502, 789, 0, 0, 527, 465, 563, 514, 569, 551, - 577, 521, 515, 344, 552, 390, 479, 362, 364, 776, - 392, 395, 399, 400, 490, 491, 508, 536, 555, 556, - 557, 389, 376, 528, 377, 409, 378, 345, 381, 380, - 382, 538, 383, 347, 509, 560, 0, 405, 524, 474, - 348, 473, 510, 559, 558, 363, 586, 593, 594, 685, - 0, 599, 790, 791, 792, 608, 0, 516, 359, 356, - 0, 0, 0, 385, 511, 372, 374, 375, 373, 505, - 507, 613, 614, 615, 617, 0, 618, 619, 0, 0, - 0, 0, 620, 686, 704, 670, 639, 601, 694, 636, - 640, 641, 426, 427, 428, 429, 707, 0, 0, 0, - 592, 454, 455, 0, 397, 396, 475, 349, 0, 0, - 443, 425, 517, 355, 393, 445, 431, 456, 457, 458, - 459, 460, 0, 461, 462, 403, 339, 340, 784, 386, - 498, 709, 746, 747, 631, 0, 695, 632, 642, 379, - 667, 679, 678, 492, 591, 0, 690, 693, 621, 783, - 0, 687, 701, 788, 700, 780, 504, 0, 535, 698, - 645, 0, 691, 664, 665, 0, 692, 660, 696, 0, - 633, 0, 602, 605, 635, 722, 723, 724, 346, 604, - 726, 727, 728, 729, 730, 731, 732, 725, 574, 668, - 644, 671, 583, 647, 646, 0, 0, 682, 600, 683, - 684, 486, 487, 488, 489, 407, 710, 370, 603, 519, - 520, 0, 669, 0, 0, 0, 0, 0, 0, 0, - 0, 674, 675, 672, 793, 0, 733, 734, 0, 0, - 597, 598, 402, 0, 616, 410, 369, 503, 404, 581, - 434, 0, 609, 676, 610, 522, 523, 737, 742, 738, - 739, 741, 762, 493, 424, 430, 539, 444, 466, 525, - 580, 501, 530, 367, 570, 541, 472, 661, 689, 0, + 722, 721, 720, 719, 718, 717, 716, 715, 0, 0, + 662, 385, 785, 779, 786, 767, 770, 769, 744, 0, + 341, 642, 466, 520, 402, 709, 710, 0, 765, 284, + 285, 286, 287, 288, 289, 290, 291, 332, 292, 293, + 294, 295, 296, 297, 298, 303, 304, 305, 306, 307, + 308, 309, 310, 712, 301, 302, 311, 312, 313, 314, + 315, 316, 317, 318, 319, 320, 321, 322, 323, 324, + 0, 0, 0, 0, 333, 771, 772, 773, 774, 775, + 0, 0, 334, 335, 336, 0, 0, 299, 300, 327, + 552, 328, 329, 330, 331, 0, 0, 591, 592, 593, + 616, 0, 594, 575, 641, 412, 342, 556, 583, 782, + 783, 0, 0, 0, 0, 0, 0, 0, 692, 703, + 739, 740, 0, 753, 754, 756, 758, 757, 760, 549, + 550, 768, 0, 0, 437, 358, 359, 496, 497, 535, + 0, 438, 439, 440, 444, 441, 442, 443, 762, 763, + 764, 761, 470, 534, 557, 543, 781, 483, 0, 789, + 631, 632, 790, 749, 748, 343, 0, 337, 338, 706, + 707, 0, 0, 537, 482, 638, 502, 0, 0, 647, + 681, 670, 759, 634, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 395, 0, 0, 465, 685, 666, + 677, 667, 508, 652, 653, 654, 661, 407, 655, 791, + 657, 626, 658, 627, 659, 660, 0, 684, 633, 545, + 484, 0, 701, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 268, 0, 0, 3542, 0, 0, + 0, 366, 269, 628, 755, 630, 629, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 369, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 546, 574, 0, + 588, 0, 434, 435, 0, 0, 0, 0, 0, 0, + 0, 350, 553, 571, 367, 540, 586, 372, 548, 564, + 362, 501, 536, 0, 0, 352, 569, 547, 479, 351, + 0, 529, 392, 409, 389, 499, 0, 0, 568, 599, + 388, 589, 0, 579, 354, 0, 578, 498, 471, 565, + 570, 480, 473, 0, 353, 567, 478, 472, 448, 399, + 615, 449, 450, 451, 452, 453, 454, 413, 514, 469, + 515, 414, 486, 485, 487, 415, 416, 417, 418, 419, + 420, 421, 422, 423, 424, 0, 0, 0, 0, 0, + 610, 611, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 747, 0, 0, + 752, 0, 582, 0, 0, 0, 0, 0, 0, 551, + 0, 0, 455, 0, 0, 0, 600, 0, 532, 504, + 793, 0, 0, 530, 467, 566, 516, 572, 554, 580, + 523, 517, 344, 555, 391, 481, 363, 365, 780, 393, + 396, 400, 401, 492, 493, 510, 539, 558, 559, 560, + 390, 377, 531, 378, 410, 379, 345, 382, 381, 383, + 541, 384, 347, 511, 563, 0, 406, 527, 476, 348, + 475, 512, 562, 561, 364, 590, 597, 598, 689, 0, + 603, 794, 795, 796, 612, 0, 518, 360, 357, 0, + 0, 0, 386, 513, 373, 375, 376, 374, 507, 509, + 617, 618, 619, 621, 0, 622, 623, 0, 0, 0, + 0, 624, 690, 708, 674, 643, 605, 698, 640, 644, + 645, 427, 428, 429, 430, 711, 0, 0, 0, 596, + 456, 457, 0, 398, 397, 477, 349, 0, 0, 445, + 426, 519, 355, 394, 447, 432, 458, 459, 460, 461, + 462, 0, 463, 464, 404, 339, 340, 788, 387, 500, + 713, 750, 751, 635, 0, 699, 636, 646, 380, 671, + 683, 682, 494, 595, 0, 694, 697, 625, 787, 0, + 691, 705, 792, 704, 784, 506, 0, 538, 702, 649, + 0, 695, 668, 669, 0, 696, 664, 700, 356, 433, + 524, 581, 0, 637, 0, 606, 609, 639, 726, 727, + 728, 346, 608, 730, 731, 732, 733, 734, 735, 736, + 729, 577, 672, 648, 675, 587, 651, 650, 0, 0, + 686, 604, 687, 688, 488, 489, 490, 491, 408, 714, + 371, 607, 521, 522, 0, 673, 0, 0, 0, 0, + 0, 0, 0, 0, 678, 679, 676, 797, 0, 737, + 738, 0, 0, 601, 602, 403, 0, 620, 411, 370, + 505, 405, 585, 436, 0, 613, 680, 614, 525, 526, + 741, 746, 742, 743, 745, 766, 495, 425, 431, 542, + 446, 468, 528, 584, 503, 533, 368, 573, 544, 474, + 665, 693, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 325, 326, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 722, + 721, 720, 719, 718, 717, 716, 715, 0, 0, 662, + 385, 785, 779, 786, 767, 770, 769, 744, 0, 341, + 642, 466, 520, 402, 709, 710, 0, 765, 284, 285, + 286, 287, 288, 289, 290, 291, 332, 292, 293, 294, + 295, 296, 297, 298, 303, 304, 305, 306, 307, 308, + 309, 310, 712, 301, 302, 311, 312, 313, 314, 315, + 316, 317, 318, 319, 320, 321, 322, 323, 324, 0, + 0, 0, 0, 333, 771, 772, 773, 774, 775, 0, + 0, 334, 335, 336, 0, 0, 299, 300, 327, 552, + 328, 329, 330, 331, 0, 0, 591, 592, 593, 616, + 0, 594, 575, 641, 412, 342, 556, 583, 782, 783, + 0, 0, 0, 0, 0, 0, 0, 692, 703, 739, + 740, 0, 753, 754, 756, 758, 757, 760, 549, 550, + 768, 0, 0, 437, 358, 359, 496, 497, 535, 0, + 438, 439, 440, 444, 441, 442, 443, 762, 763, 764, + 761, 470, 534, 557, 543, 781, 483, 0, 789, 631, + 632, 790, 749, 748, 343, 0, 337, 338, 706, 707, + 0, 0, 537, 482, 638, 502, 0, 0, 647, 681, + 670, 759, 634, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 395, 0, 0, 465, 685, 666, 677, + 667, 508, 652, 653, 654, 661, 407, 655, 791, 657, + 626, 658, 627, 659, 660, 0, 684, 633, 545, 484, + 0, 701, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 325, 326, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 718, 717, 716, 715, - 714, 713, 712, 711, 0, 0, 658, 384, 781, 775, - 782, 763, 766, 765, 740, 0, 341, 638, 464, 518, - 401, 705, 706, 0, 761, 284, 285, 286, 287, 288, - 289, 290, 291, 332, 292, 293, 294, 295, 296, 297, - 298, 303, 304, 305, 306, 307, 308, 309, 310, 708, - 301, 302, 311, 312, 313, 314, 315, 316, 317, 318, - 319, 320, 321, 322, 323, 324, 0, 0, 0, 0, - 333, 767, 768, 769, 770, 771, 0, 0, 334, 335, - 336, 0, 0, 299, 300, 327, 549, 328, 329, 330, - 331, 0, 0, 587, 588, 589, 612, 0, 590, 572, - 637, 411, 342, 553, 579, 778, 779, 0, 0, 0, - 0, 0, 0, 0, 688, 699, 735, 736, 0, 749, - 750, 752, 754, 753, 756, 546, 547, 764, 0, 0, - 435, 357, 358, 494, 495, 532, 0, 436, 437, 438, - 442, 439, 440, 441, 758, 759, 760, 757, 468, 531, - 554, 540, 777, 481, 0, 785, 627, 628, 786, 745, - 744, 343, 0, 337, 338, 702, 703, 0, 0, 534, - 480, 634, 500, 0, 0, 643, 677, 666, 755, 630, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 394, 0, 0, 463, 681, 662, 673, 663, 506, 648, - 649, 650, 657, 406, 651, 787, 653, 622, 654, 623, - 655, 656, 0, 680, 629, 542, 482, 0, 697, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 1812, 0, 0, - 268, 0, 0, 0, 0, 0, 0, 365, 269, 624, - 751, 626, 625, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 368, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 543, 571, 0, 584, 0, 432, 433, - 0, 0, 0, 0, 0, 0, 0, 350, 550, 568, - 366, 537, 582, 371, 545, 561, 361, 499, 533, 0, - 0, 352, 566, 544, 477, 351, 0, 526, 391, 408, - 388, 497, 0, 0, 565, 595, 387, 585, 0, 576, - 354, 0, 575, 496, 469, 562, 567, 478, 471, 0, - 353, 564, 476, 470, 446, 398, 611, 447, 448, 449, - 450, 451, 452, 412, 512, 467, 513, 413, 484, 483, - 485, 414, 415, 416, 417, 418, 419, 420, 421, 422, - 423, 0, 0, 0, 0, 0, 606, 607, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 743, 0, 0, 748, 0, 578, 0, - 0, 0, 0, 0, 0, 548, 0, 0, 453, 0, - 0, 0, 596, 0, 529, 502, 789, 0, 0, 527, - 465, 563, 514, 569, 551, 577, 521, 515, 344, 552, - 390, 479, 362, 364, 776, 392, 395, 399, 400, 490, - 491, 508, 536, 555, 556, 557, 389, 376, 528, 377, - 409, 378, 345, 381, 380, 382, 538, 383, 347, 509, - 560, 0, 405, 524, 474, 348, 473, 510, 559, 558, - 363, 586, 593, 594, 685, 0, 599, 790, 791, 792, - 608, 0, 516, 359, 356, 0, 0, 0, 385, 511, - 372, 374, 375, 373, 505, 507, 613, 614, 615, 617, - 0, 618, 619, 0, 0, 0, 0, 620, 686, 704, - 670, 639, 601, 694, 636, 640, 641, 426, 427, 428, - 429, 707, 0, 0, 0, 592, 454, 455, 0, 397, - 396, 475, 349, 0, 0, 443, 425, 517, 355, 393, - 445, 431, 456, 457, 458, 459, 460, 0, 461, 462, - 403, 339, 340, 784, 386, 498, 709, 746, 747, 631, - 0, 695, 632, 642, 379, 667, 679, 678, 492, 591, - 0, 690, 693, 621, 783, 0, 687, 701, 788, 700, - 780, 504, 0, 535, 698, 645, 0, 691, 664, 665, - 0, 692, 660, 696, 0, 633, 0, 602, 605, 635, - 722, 723, 724, 346, 604, 726, 727, 728, 729, 730, - 731, 732, 725, 574, 668, 644, 671, 583, 647, 646, - 0, 0, 682, 600, 683, 684, 486, 487, 488, 489, - 407, 710, 370, 603, 519, 520, 0, 669, 0, 0, - 0, 0, 0, 0, 0, 0, 674, 675, 672, 793, - 0, 733, 734, 0, 0, 597, 598, 402, 0, 616, - 410, 369, 503, 404, 581, 434, 0, 609, 676, 610, - 522, 523, 737, 742, 738, 739, 741, 762, 493, 424, - 430, 539, 444, 466, 525, 580, 501, 530, 367, 570, - 541, 472, 661, 689, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 325, 326, 0, 0, 0, + 0, 0, 0, 268, 0, 0, 0, 0, 0, 0, + 366, 269, 628, 755, 630, 629, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 369, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 3523, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 546, 574, 0, 588, + 0, 434, 435, 0, 0, 0, 0, 0, 0, 0, + 350, 553, 571, 367, 540, 586, 372, 548, 564, 362, + 501, 536, 0, 0, 352, 569, 547, 479, 351, 0, + 529, 392, 409, 389, 499, 0, 0, 568, 599, 388, + 589, 0, 579, 354, 0, 578, 498, 471, 565, 570, + 480, 473, 0, 353, 567, 478, 472, 448, 399, 615, + 449, 450, 451, 452, 453, 454, 413, 514, 469, 515, + 414, 486, 485, 487, 415, 416, 417, 418, 419, 420, + 421, 422, 423, 424, 0, 0, 0, 0, 0, 610, + 611, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 747, 0, 0, 752, + 0, 582, 0, 0, 0, 0, 0, 0, 551, 0, + 0, 455, 0, 0, 0, 600, 0, 532, 504, 793, + 0, 0, 530, 467, 566, 516, 572, 554, 580, 523, + 517, 344, 555, 391, 481, 363, 365, 780, 393, 396, + 400, 401, 492, 493, 510, 539, 558, 559, 560, 390, + 377, 531, 378, 410, 379, 345, 382, 381, 383, 541, + 384, 347, 511, 563, 0, 406, 527, 476, 348, 475, + 512, 562, 561, 364, 590, 597, 598, 689, 0, 603, + 794, 795, 796, 612, 0, 518, 360, 357, 0, 0, + 0, 386, 513, 373, 375, 376, 374, 507, 509, 617, + 618, 619, 621, 0, 622, 623, 0, 0, 0, 0, + 624, 690, 708, 674, 643, 605, 698, 640, 644, 645, + 427, 428, 429, 430, 711, 0, 0, 0, 596, 456, + 457, 0, 398, 397, 477, 349, 0, 0, 445, 426, + 519, 355, 394, 447, 432, 458, 459, 460, 461, 462, + 0, 463, 464, 404, 339, 340, 788, 387, 500, 713, + 750, 751, 635, 0, 699, 636, 646, 380, 671, 683, + 682, 494, 595, 0, 694, 697, 625, 787, 0, 691, + 705, 792, 704, 784, 506, 0, 538, 702, 649, 0, + 695, 668, 669, 0, 696, 664, 700, 356, 433, 524, + 581, 0, 637, 0, 606, 609, 639, 726, 727, 728, + 346, 608, 730, 731, 732, 733, 734, 735, 736, 729, + 577, 672, 648, 675, 587, 651, 650, 0, 0, 686, + 604, 687, 688, 488, 489, 490, 491, 408, 714, 371, + 607, 521, 522, 0, 673, 0, 0, 0, 0, 0, + 0, 0, 0, 678, 679, 676, 797, 0, 737, 738, + 0, 0, 601, 602, 403, 0, 620, 411, 370, 505, + 405, 585, 436, 0, 613, 680, 614, 525, 526, 741, + 746, 742, 743, 745, 766, 495, 425, 431, 542, 446, + 468, 528, 584, 503, 533, 368, 573, 544, 474, 665, + 693, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 325, 326, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 722, 721, + 720, 719, 718, 717, 716, 715, 0, 0, 662, 385, + 785, 779, 786, 767, 770, 769, 744, 0, 341, 642, + 466, 520, 402, 709, 710, 0, 765, 284, 285, 286, + 287, 288, 289, 290, 291, 332, 292, 293, 294, 295, + 296, 297, 298, 303, 304, 305, 306, 307, 308, 309, + 310, 712, 301, 302, 311, 312, 313, 314, 315, 316, + 317, 318, 319, 320, 321, 322, 323, 324, 0, 0, + 0, 0, 333, 771, 772, 773, 774, 775, 0, 0, + 334, 335, 336, 0, 0, 299, 300, 327, 552, 328, + 329, 330, 331, 0, 0, 591, 592, 593, 616, 0, + 594, 575, 641, 412, 342, 556, 583, 782, 783, 0, + 0, 0, 0, 0, 0, 0, 692, 703, 739, 740, + 0, 753, 754, 756, 758, 757, 760, 549, 550, 768, + 0, 0, 437, 358, 359, 496, 497, 535, 0, 438, + 439, 440, 444, 441, 442, 443, 762, 763, 764, 761, + 470, 534, 557, 543, 781, 483, 0, 789, 631, 632, + 790, 749, 748, 343, 0, 337, 338, 706, 707, 0, + 0, 537, 482, 638, 502, 0, 0, 647, 681, 670, + 759, 634, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 395, 0, 0, 465, 685, 666, 677, 667, + 508, 652, 653, 654, 661, 407, 655, 791, 657, 626, + 658, 627, 659, 660, 0, 684, 633, 545, 484, 0, + 701, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 268, 0, 0, 3401, 0, 0, 0, 366, + 269, 628, 755, 630, 629, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 369, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 546, 574, 0, 588, 0, + 434, 435, 0, 0, 0, 0, 0, 0, 0, 350, + 553, 571, 367, 540, 586, 372, 548, 564, 362, 501, + 536, 0, 0, 352, 569, 547, 479, 351, 0, 529, + 392, 409, 389, 499, 0, 0, 568, 599, 388, 589, + 0, 579, 354, 0, 578, 498, 471, 565, 570, 480, + 473, 0, 353, 567, 478, 472, 448, 399, 615, 449, + 450, 451, 452, 453, 454, 413, 514, 469, 515, 414, + 486, 485, 487, 415, 416, 417, 418, 419, 420, 421, + 422, 423, 424, 0, 0, 0, 0, 0, 610, 611, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 747, 0, 0, 752, 0, + 582, 0, 0, 0, 0, 0, 0, 551, 0, 0, + 455, 0, 0, 0, 600, 0, 532, 504, 793, 0, + 0, 530, 467, 566, 516, 572, 554, 580, 523, 517, + 344, 555, 391, 481, 363, 365, 780, 393, 396, 400, + 401, 492, 493, 510, 539, 558, 559, 560, 390, 377, + 531, 378, 410, 379, 345, 382, 381, 383, 541, 384, + 347, 511, 563, 0, 406, 527, 476, 348, 475, 512, + 562, 561, 364, 590, 597, 598, 689, 0, 603, 794, + 795, 796, 612, 0, 518, 360, 357, 0, 0, 0, + 386, 513, 373, 375, 376, 374, 507, 509, 617, 618, + 619, 621, 0, 622, 623, 0, 0, 0, 0, 624, + 690, 708, 674, 643, 605, 698, 640, 644, 645, 427, + 428, 429, 430, 711, 0, 0, 0, 596, 456, 457, + 0, 398, 397, 477, 349, 0, 0, 445, 426, 519, + 355, 394, 447, 432, 458, 459, 460, 461, 462, 0, + 463, 464, 404, 339, 340, 788, 387, 500, 713, 750, + 751, 635, 0, 699, 636, 646, 380, 671, 683, 682, + 494, 595, 0, 694, 697, 625, 787, 0, 691, 705, + 792, 704, 784, 506, 0, 538, 702, 649, 0, 695, + 668, 669, 0, 696, 664, 700, 356, 433, 524, 581, + 0, 637, 0, 606, 609, 639, 726, 727, 728, 346, + 608, 730, 731, 732, 733, 734, 735, 736, 729, 577, + 672, 648, 675, 587, 651, 650, 0, 0, 686, 604, + 687, 688, 488, 489, 490, 491, 408, 714, 371, 607, + 521, 522, 0, 673, 0, 0, 0, 0, 0, 0, + 0, 0, 678, 679, 676, 797, 0, 737, 738, 0, + 0, 601, 602, 403, 0, 620, 411, 370, 505, 405, + 585, 436, 0, 613, 680, 614, 525, 526, 741, 746, + 742, 743, 745, 766, 495, 425, 431, 542, 446, 468, + 528, 584, 503, 533, 368, 573, 544, 474, 665, 693, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 718, 717, 716, 715, 714, 713, 712, 711, 0, - 0, 658, 384, 781, 775, 782, 763, 766, 765, 740, - 0, 341, 638, 464, 518, 401, 705, 706, 0, 761, - 284, 285, 286, 287, 288, 289, 290, 291, 332, 292, - 293, 294, 295, 296, 297, 298, 303, 304, 305, 306, - 307, 308, 309, 310, 708, 301, 302, 311, 312, 313, - 314, 315, 316, 317, 318, 319, 320, 321, 322, 323, - 324, 0, 0, 0, 0, 333, 767, 768, 769, 770, - 771, 0, 0, 334, 335, 336, 0, 0, 299, 300, - 327, 549, 328, 329, 330, 331, 0, 0, 587, 588, - 589, 612, 0, 590, 572, 637, 411, 342, 553, 579, - 778, 779, 0, 0, 0, 0, 0, 0, 0, 688, - 699, 735, 736, 0, 749, 750, 752, 754, 753, 756, - 546, 547, 764, 0, 0, 435, 357, 358, 494, 495, - 532, 0, 436, 437, 438, 442, 439, 440, 441, 758, - 759, 760, 757, 468, 531, 554, 540, 777, 481, 0, - 785, 627, 628, 786, 745, 744, 343, 0, 337, 338, - 702, 703, 0, 0, 534, 480, 634, 500, 0, 0, - 643, 677, 666, 755, 630, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 394, 0, 0, 463, 681, - 662, 673, 663, 506, 648, 649, 650, 657, 406, 651, - 787, 653, 622, 654, 623, 655, 656, 0, 680, 629, - 542, 482, 0, 697, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 268, 0, 0, 3538, 0, - 0, 0, 365, 269, 624, 751, 626, 625, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 368, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 543, 571, - 0, 584, 0, 432, 433, 0, 0, 0, 0, 0, - 0, 0, 350, 550, 568, 366, 537, 582, 371, 545, - 561, 361, 499, 533, 0, 0, 352, 566, 544, 477, - 351, 0, 526, 391, 408, 388, 497, 0, 0, 565, - 595, 387, 585, 0, 576, 354, 0, 575, 496, 469, - 562, 567, 478, 471, 0, 353, 564, 476, 470, 446, - 398, 611, 447, 448, 449, 450, 451, 452, 412, 512, - 467, 513, 413, 484, 483, 485, 414, 415, 416, 417, - 418, 419, 420, 421, 422, 423, 0, 0, 0, 0, - 0, 606, 607, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 743, 0, - 0, 748, 0, 578, 0, 0, 0, 0, 0, 0, - 548, 0, 0, 453, 0, 0, 0, 596, 0, 529, - 502, 789, 0, 0, 527, 465, 563, 514, 569, 551, - 577, 521, 515, 344, 552, 390, 479, 362, 364, 776, - 392, 395, 399, 400, 490, 491, 508, 536, 555, 556, - 557, 389, 376, 528, 377, 409, 378, 345, 381, 380, - 382, 538, 383, 347, 509, 560, 0, 405, 524, 474, - 348, 473, 510, 559, 558, 363, 586, 593, 594, 685, - 0, 599, 790, 791, 792, 608, 0, 516, 359, 356, - 0, 0, 0, 385, 511, 372, 374, 375, 373, 505, - 507, 613, 614, 615, 617, 0, 618, 619, 0, 0, - 0, 0, 620, 686, 704, 670, 639, 601, 694, 636, - 640, 641, 426, 427, 428, 429, 707, 0, 0, 0, - 592, 454, 455, 0, 397, 396, 475, 349, 0, 0, - 443, 425, 517, 355, 393, 445, 431, 456, 457, 458, - 459, 460, 0, 461, 462, 403, 339, 340, 784, 386, - 498, 709, 746, 747, 631, 0, 695, 632, 642, 379, - 667, 679, 678, 492, 591, 0, 690, 693, 621, 783, - 0, 687, 701, 788, 700, 780, 504, 0, 535, 698, - 645, 0, 691, 664, 665, 0, 692, 660, 696, 0, - 633, 0, 602, 605, 635, 722, 723, 724, 346, 604, - 726, 727, 728, 729, 730, 731, 732, 725, 574, 668, - 644, 671, 583, 647, 646, 0, 0, 682, 600, 683, - 684, 486, 487, 488, 489, 407, 710, 370, 603, 519, - 520, 0, 669, 0, 0, 0, 0, 0, 0, 0, - 0, 674, 675, 672, 793, 0, 733, 734, 0, 0, - 597, 598, 402, 0, 616, 410, 369, 503, 404, 581, - 434, 0, 609, 676, 610, 522, 523, 737, 742, 738, - 739, 741, 762, 493, 424, 430, 539, 444, 466, 525, - 580, 501, 530, 367, 570, 541, 472, 661, 689, 0, + 0, 325, 326, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 722, 721, 720, + 719, 718, 717, 716, 715, 0, 0, 662, 385, 785, + 779, 786, 767, 770, 769, 744, 0, 341, 642, 466, + 520, 402, 709, 710, 0, 765, 284, 285, 286, 287, + 288, 289, 290, 291, 332, 292, 293, 294, 295, 296, + 297, 298, 303, 304, 305, 306, 307, 308, 309, 310, + 712, 301, 302, 311, 312, 313, 314, 315, 316, 317, + 318, 319, 320, 321, 322, 323, 324, 0, 0, 0, + 0, 333, 771, 772, 773, 774, 775, 0, 0, 334, + 335, 336, 0, 0, 299, 300, 327, 552, 328, 329, + 330, 331, 0, 0, 591, 592, 593, 616, 0, 594, + 575, 641, 412, 342, 556, 583, 782, 783, 0, 0, + 0, 0, 0, 0, 0, 692, 703, 739, 740, 0, + 753, 754, 756, 758, 757, 760, 549, 550, 768, 0, + 0, 437, 358, 359, 496, 497, 535, 0, 438, 439, + 440, 444, 441, 442, 443, 762, 763, 764, 761, 470, + 534, 557, 543, 781, 483, 0, 789, 631, 632, 790, + 749, 748, 343, 0, 337, 338, 706, 707, 0, 0, + 537, 482, 638, 502, 0, 0, 647, 681, 670, 759, + 634, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 395, 0, 0, 465, 685, 666, 677, 667, 508, + 652, 653, 654, 661, 407, 655, 791, 657, 626, 658, + 627, 659, 660, 0, 684, 633, 545, 484, 0, 701, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 3367, 0, + 0, 268, 0, 0, 0, 0, 0, 0, 366, 269, + 628, 755, 630, 629, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 369, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 546, 574, 0, 588, 0, 434, + 435, 0, 0, 0, 0, 0, 0, 0, 350, 553, + 571, 367, 540, 586, 372, 548, 564, 362, 501, 536, + 0, 0, 352, 569, 547, 479, 351, 0, 529, 392, + 409, 389, 499, 0, 0, 568, 599, 388, 589, 0, + 579, 354, 0, 578, 498, 471, 565, 570, 480, 473, + 0, 353, 567, 478, 472, 448, 399, 615, 449, 450, + 451, 452, 453, 454, 413, 514, 469, 515, 414, 486, + 485, 487, 415, 416, 417, 418, 419, 420, 421, 422, + 423, 424, 0, 0, 0, 0, 0, 610, 611, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 747, 0, 0, 752, 0, 582, + 0, 0, 0, 0, 0, 0, 551, 0, 0, 455, + 0, 0, 0, 600, 0, 532, 504, 793, 0, 0, + 530, 467, 566, 516, 572, 554, 580, 523, 517, 344, + 555, 391, 481, 363, 365, 780, 393, 396, 400, 401, + 492, 493, 510, 539, 558, 559, 560, 390, 377, 531, + 378, 410, 379, 345, 382, 381, 383, 541, 384, 347, + 511, 563, 0, 406, 527, 476, 348, 475, 512, 562, + 561, 364, 590, 597, 598, 689, 0, 603, 794, 795, + 796, 612, 0, 518, 360, 357, 0, 0, 0, 386, + 513, 373, 375, 376, 374, 507, 509, 617, 618, 619, + 621, 0, 622, 623, 0, 0, 0, 0, 624, 690, + 708, 674, 643, 605, 698, 640, 644, 645, 427, 428, + 429, 430, 711, 0, 0, 0, 596, 456, 457, 0, + 398, 397, 477, 349, 0, 0, 445, 426, 519, 355, + 394, 447, 432, 458, 459, 460, 461, 462, 0, 463, + 464, 404, 339, 340, 788, 387, 500, 713, 750, 751, + 635, 0, 699, 636, 646, 380, 671, 683, 682, 494, + 595, 0, 694, 697, 625, 787, 0, 691, 705, 792, + 704, 784, 506, 0, 538, 702, 649, 0, 695, 668, + 669, 0, 696, 664, 700, 356, 433, 524, 581, 0, + 637, 0, 606, 609, 639, 726, 727, 728, 346, 608, + 730, 731, 732, 733, 734, 735, 736, 729, 577, 672, + 648, 675, 587, 651, 650, 0, 0, 686, 604, 687, + 688, 488, 489, 490, 491, 408, 714, 371, 607, 521, + 522, 0, 673, 0, 0, 0, 0, 0, 0, 0, + 0, 678, 679, 676, 797, 0, 737, 738, 0, 0, + 601, 602, 403, 0, 620, 411, 370, 505, 405, 585, + 436, 0, 613, 680, 614, 525, 526, 741, 746, 742, + 743, 745, 766, 495, 425, 431, 542, 446, 468, 528, + 584, 503, 533, 368, 573, 544, 474, 665, 693, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 325, 326, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 718, 717, 716, 715, - 714, 713, 712, 711, 0, 0, 658, 384, 781, 775, - 782, 763, 766, 765, 740, 0, 341, 638, 464, 518, - 401, 705, 706, 0, 761, 284, 285, 286, 287, 288, + 0, 0, 0, 0, 0, 0, 722, 721, 720, 719, + 718, 717, 716, 715, 0, 0, 662, 385, 785, 779, + 786, 767, 770, 769, 744, 0, 341, 642, 466, 520, + 402, 709, 710, 0, 765, 284, 285, 286, 287, 288, 289, 290, 291, 332, 292, 293, 294, 295, 296, 297, - 298, 303, 304, 305, 306, 307, 308, 309, 310, 708, + 298, 303, 304, 305, 306, 307, 308, 309, 310, 712, 301, 302, 311, 312, 313, 314, 315, 316, 317, 318, 319, 320, 321, 322, 323, 324, 0, 0, 0, 0, - 333, 767, 768, 769, 770, 771, 0, 0, 334, 335, - 336, 0, 0, 299, 300, 327, 549, 328, 329, 330, - 331, 0, 0, 587, 588, 589, 612, 0, 590, 572, - 637, 411, 342, 553, 579, 778, 779, 0, 0, 0, - 0, 0, 0, 0, 688, 699, 735, 736, 0, 749, - 750, 752, 754, 753, 756, 546, 547, 764, 0, 0, - 435, 357, 358, 494, 495, 532, 0, 436, 437, 438, - 442, 439, 440, 441, 758, 759, 760, 757, 468, 531, - 554, 540, 777, 481, 0, 785, 627, 628, 786, 745, - 744, 343, 0, 337, 338, 702, 703, 0, 0, 534, - 480, 634, 500, 0, 0, 643, 677, 666, 755, 630, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 394, 0, 0, 463, 681, 662, 673, 663, 506, 648, - 649, 650, 657, 406, 651, 787, 653, 622, 654, 623, - 655, 656, 0, 680, 629, 542, 482, 0, 697, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 268, 0, 0, 0, 0, 0, 0, 365, 269, 624, - 751, 626, 625, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 368, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 3519, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 543, 571, 0, 584, 0, 432, 433, - 0, 0, 0, 0, 0, 0, 0, 350, 550, 568, - 366, 537, 582, 371, 545, 561, 361, 499, 533, 0, - 0, 352, 566, 544, 477, 351, 0, 526, 391, 408, - 388, 497, 0, 0, 565, 595, 387, 585, 0, 576, - 354, 0, 575, 496, 469, 562, 567, 478, 471, 0, - 353, 564, 476, 470, 446, 398, 611, 447, 448, 449, - 450, 451, 452, 412, 512, 467, 513, 413, 484, 483, - 485, 414, 415, 416, 417, 418, 419, 420, 421, 422, - 423, 0, 0, 0, 0, 0, 606, 607, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 743, 0, 0, 748, 0, 578, 0, - 0, 0, 0, 0, 0, 548, 0, 0, 453, 0, - 0, 0, 596, 0, 529, 502, 789, 0, 0, 527, - 465, 563, 514, 569, 551, 577, 521, 515, 344, 552, - 390, 479, 362, 364, 776, 392, 395, 399, 400, 490, - 491, 508, 536, 555, 556, 557, 389, 376, 528, 377, - 409, 378, 345, 381, 380, 382, 538, 383, 347, 509, - 560, 0, 405, 524, 474, 348, 473, 510, 559, 558, - 363, 586, 593, 594, 685, 0, 599, 790, 791, 792, - 608, 0, 516, 359, 356, 0, 0, 0, 385, 511, - 372, 374, 375, 373, 505, 507, 613, 614, 615, 617, - 0, 618, 619, 0, 0, 0, 0, 620, 686, 704, - 670, 639, 601, 694, 636, 640, 641, 426, 427, 428, - 429, 707, 0, 0, 0, 592, 454, 455, 0, 397, - 396, 475, 349, 0, 0, 443, 425, 517, 355, 393, - 445, 431, 456, 457, 458, 459, 460, 0, 461, 462, - 403, 339, 340, 784, 386, 498, 709, 746, 747, 631, - 0, 695, 632, 642, 379, 667, 679, 678, 492, 591, - 0, 690, 693, 621, 783, 0, 687, 701, 788, 700, - 780, 504, 0, 535, 698, 645, 0, 691, 664, 665, - 0, 692, 660, 696, 0, 633, 0, 602, 605, 635, - 722, 723, 724, 346, 604, 726, 727, 728, 729, 730, - 731, 732, 725, 574, 668, 644, 671, 583, 647, 646, - 0, 0, 682, 600, 683, 684, 486, 487, 488, 489, - 407, 710, 370, 603, 519, 520, 0, 669, 0, 0, - 0, 0, 0, 0, 0, 0, 674, 675, 672, 793, - 0, 733, 734, 0, 0, 597, 598, 402, 0, 616, - 410, 369, 503, 404, 581, 434, 0, 609, 676, 610, - 522, 523, 737, 742, 738, 739, 741, 762, 493, 424, - 430, 539, 444, 466, 525, 580, 501, 530, 367, 570, - 541, 472, 661, 689, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 325, 326, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 718, 717, 716, 715, 714, 713, 712, 711, 0, - 0, 658, 384, 781, 775, 782, 763, 766, 765, 740, - 0, 341, 638, 464, 518, 401, 705, 706, 0, 761, - 284, 285, 286, 287, 288, 289, 290, 291, 332, 292, - 293, 294, 295, 296, 297, 298, 303, 304, 305, 306, - 307, 308, 309, 310, 708, 301, 302, 311, 312, 313, - 314, 315, 316, 317, 318, 319, 320, 321, 322, 323, - 324, 0, 0, 0, 0, 333, 767, 768, 769, 770, - 771, 0, 0, 334, 335, 336, 0, 0, 299, 300, - 327, 549, 328, 329, 330, 331, 0, 0, 587, 588, - 589, 612, 0, 590, 572, 637, 411, 342, 553, 579, - 778, 779, 0, 0, 0, 0, 0, 0, 0, 688, - 699, 735, 736, 0, 749, 750, 752, 754, 753, 756, - 546, 547, 764, 0, 0, 435, 357, 358, 494, 495, - 532, 0, 436, 437, 438, 442, 439, 440, 441, 758, - 759, 760, 757, 468, 531, 554, 540, 777, 481, 0, - 785, 627, 628, 786, 745, 744, 343, 0, 337, 338, - 702, 703, 0, 0, 534, 480, 634, 500, 0, 0, - 643, 677, 666, 755, 630, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 394, 0, 0, 463, 681, - 662, 673, 663, 506, 648, 649, 650, 657, 406, 651, - 787, 653, 622, 654, 623, 655, 656, 0, 680, 629, - 542, 482, 0, 697, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 268, 0, 0, 3397, 0, - 0, 0, 365, 269, 624, 751, 626, 625, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 368, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 543, 571, - 0, 584, 0, 432, 433, 0, 0, 0, 0, 0, - 0, 0, 350, 550, 568, 366, 537, 582, 371, 545, - 561, 361, 499, 533, 0, 0, 352, 566, 544, 477, - 351, 0, 526, 391, 408, 388, 497, 0, 0, 565, - 595, 387, 585, 0, 576, 354, 0, 575, 496, 469, - 562, 567, 478, 471, 0, 353, 564, 476, 470, 446, - 398, 611, 447, 448, 449, 450, 451, 452, 412, 512, - 467, 513, 413, 484, 483, 485, 414, 415, 416, 417, - 418, 419, 420, 421, 422, 423, 0, 0, 0, 0, - 0, 606, 607, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 743, 0, - 0, 748, 0, 578, 0, 0, 0, 0, 0, 0, - 548, 0, 0, 453, 0, 0, 0, 596, 0, 529, - 502, 789, 0, 0, 527, 465, 563, 514, 569, 551, - 577, 521, 515, 344, 552, 390, 479, 362, 364, 776, - 392, 395, 399, 400, 490, 491, 508, 536, 555, 556, - 557, 389, 376, 528, 377, 409, 378, 345, 381, 380, - 382, 538, 383, 347, 509, 560, 0, 405, 524, 474, - 348, 473, 510, 559, 558, 363, 586, 593, 594, 685, - 0, 599, 790, 791, 792, 608, 0, 516, 359, 356, - 0, 0, 0, 385, 511, 372, 374, 375, 373, 505, - 507, 613, 614, 615, 617, 0, 618, 619, 0, 0, - 0, 0, 620, 686, 704, 670, 639, 601, 694, 636, - 640, 641, 426, 427, 428, 429, 707, 0, 0, 0, - 592, 454, 455, 0, 397, 396, 475, 349, 0, 0, - 443, 425, 517, 355, 393, 445, 431, 456, 457, 458, - 459, 460, 0, 461, 462, 403, 339, 340, 784, 386, - 498, 709, 746, 747, 631, 0, 695, 632, 642, 379, - 667, 679, 678, 492, 591, 0, 690, 693, 621, 783, - 0, 687, 701, 788, 700, 780, 504, 0, 535, 698, - 645, 0, 691, 664, 665, 0, 692, 660, 696, 0, - 633, 0, 602, 605, 635, 722, 723, 724, 346, 604, - 726, 727, 728, 729, 730, 731, 732, 725, 574, 668, - 644, 671, 583, 647, 646, 0, 0, 682, 600, 683, - 684, 486, 487, 488, 489, 407, 710, 370, 603, 519, - 520, 0, 669, 0, 0, 0, 0, 0, 0, 0, - 0, 674, 675, 672, 793, 0, 733, 734, 0, 0, - 597, 598, 402, 0, 616, 410, 369, 503, 404, 581, - 434, 0, 609, 676, 610, 522, 523, 737, 742, 738, - 739, 741, 762, 493, 424, 430, 539, 444, 466, 525, - 580, 501, 530, 367, 570, 541, 472, 661, 689, 0, + 333, 771, 772, 773, 774, 775, 0, 0, 334, 335, + 336, 0, 0, 299, 300, 327, 552, 328, 329, 330, + 331, 0, 0, 591, 592, 593, 616, 0, 594, 575, + 641, 412, 342, 556, 583, 782, 783, 0, 0, 0, + 0, 0, 0, 0, 692, 703, 739, 740, 0, 753, + 754, 756, 758, 757, 760, 549, 550, 768, 0, 0, + 437, 358, 359, 496, 497, 535, 0, 438, 439, 440, + 444, 441, 442, 443, 762, 763, 764, 761, 470, 534, + 557, 543, 781, 483, 0, 789, 631, 632, 790, 749, + 748, 343, 0, 337, 338, 706, 707, 0, 0, 537, + 482, 638, 502, 0, 0, 647, 681, 670, 759, 634, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 395, 0, 0, 465, 685, 666, 677, 667, 508, 652, + 653, 654, 661, 407, 655, 791, 657, 626, 658, 627, + 659, 660, 0, 684, 633, 545, 484, 0, 701, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 268, 0, 0, 0, 0, 0, 0, 366, 269, 628, + 755, 630, 629, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 369, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 2735, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 546, 574, 0, 588, 0, 434, 435, + 0, 0, 0, 0, 0, 0, 0, 350, 553, 571, + 367, 540, 586, 372, 548, 564, 362, 501, 536, 0, + 0, 352, 569, 547, 479, 351, 0, 529, 392, 409, + 389, 499, 0, 0, 568, 599, 388, 589, 0, 579, + 354, 0, 578, 498, 471, 565, 570, 480, 473, 0, + 353, 567, 478, 472, 448, 399, 615, 449, 450, 451, + 452, 453, 454, 413, 514, 469, 515, 414, 486, 485, + 487, 415, 416, 417, 418, 419, 420, 421, 422, 423, + 424, 0, 0, 0, 0, 0, 610, 611, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 747, 0, 0, 752, 0, 582, 0, + 0, 0, 0, 0, 0, 551, 0, 0, 455, 0, + 0, 0, 600, 0, 532, 504, 793, 0, 0, 530, + 467, 566, 516, 572, 554, 580, 523, 517, 344, 555, + 391, 481, 363, 365, 780, 393, 396, 400, 401, 492, + 493, 510, 539, 558, 559, 560, 390, 377, 531, 378, + 410, 379, 345, 382, 381, 383, 541, 384, 347, 511, + 563, 0, 406, 527, 476, 348, 475, 512, 562, 561, + 364, 590, 597, 598, 689, 0, 603, 794, 795, 796, + 612, 0, 518, 360, 357, 0, 0, 0, 386, 513, + 373, 375, 376, 374, 507, 509, 617, 618, 619, 621, + 0, 622, 623, 0, 0, 0, 0, 624, 690, 708, + 674, 643, 605, 698, 640, 644, 645, 427, 428, 429, + 430, 711, 0, 0, 0, 596, 456, 457, 0, 398, + 397, 477, 349, 0, 0, 445, 426, 519, 355, 394, + 447, 432, 458, 459, 460, 461, 462, 0, 463, 464, + 404, 339, 340, 788, 387, 500, 713, 750, 751, 635, + 0, 699, 636, 646, 380, 671, 683, 682, 494, 595, + 0, 694, 697, 625, 787, 0, 691, 705, 792, 704, + 784, 506, 0, 538, 702, 649, 0, 695, 668, 669, + 0, 696, 664, 700, 356, 433, 524, 581, 0, 637, + 0, 606, 609, 639, 726, 727, 728, 346, 608, 730, + 731, 732, 733, 734, 735, 736, 729, 577, 672, 648, + 675, 587, 651, 650, 0, 0, 686, 604, 687, 688, + 488, 489, 490, 491, 408, 714, 371, 607, 521, 522, + 0, 673, 0, 0, 0, 0, 0, 0, 0, 0, + 678, 679, 676, 797, 0, 737, 738, 0, 0, 601, + 602, 403, 0, 620, 411, 370, 505, 405, 585, 436, + 0, 613, 680, 614, 525, 526, 741, 746, 742, 743, + 745, 766, 495, 425, 431, 542, 446, 468, 528, 584, + 503, 533, 368, 573, 544, 474, 665, 693, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 325, + 326, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 722, 721, 720, 719, 718, + 717, 716, 715, 0, 0, 662, 385, 785, 779, 786, + 767, 770, 769, 744, 0, 341, 642, 466, 520, 402, + 709, 710, 0, 765, 284, 285, 286, 287, 288, 289, + 290, 291, 332, 292, 293, 294, 295, 296, 297, 298, + 303, 304, 305, 306, 307, 308, 309, 310, 712, 301, + 302, 311, 312, 313, 314, 315, 316, 317, 318, 319, + 320, 321, 322, 323, 324, 0, 0, 0, 0, 333, + 771, 772, 773, 774, 775, 0, 0, 334, 335, 336, + 0, 0, 299, 300, 327, 552, 328, 329, 330, 331, + 0, 0, 591, 592, 593, 616, 0, 594, 575, 641, + 412, 342, 556, 583, 782, 783, 0, 0, 0, 0, + 0, 0, 0, 692, 703, 739, 740, 0, 753, 754, + 756, 758, 757, 760, 549, 550, 768, 0, 0, 437, + 358, 359, 496, 497, 535, 0, 438, 439, 440, 444, + 441, 442, 443, 762, 763, 764, 761, 470, 534, 557, + 543, 781, 483, 0, 789, 631, 632, 790, 749, 748, + 343, 0, 337, 338, 706, 707, 0, 0, 537, 482, + 638, 502, 0, 0, 647, 681, 670, 759, 634, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 395, + 0, 0, 465, 685, 666, 677, 667, 508, 652, 653, + 654, 661, 407, 655, 791, 657, 626, 658, 627, 659, + 660, 0, 684, 633, 545, 484, 0, 701, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 325, 326, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 718, 717, 716, 715, - 714, 713, 712, 711, 0, 0, 658, 384, 781, 775, - 782, 763, 766, 765, 740, 0, 341, 638, 464, 518, - 401, 705, 706, 0, 761, 284, 285, 286, 287, 288, - 289, 290, 291, 332, 292, 293, 294, 295, 296, 297, - 298, 303, 304, 305, 306, 307, 308, 309, 310, 708, - 301, 302, 311, 312, 313, 314, 315, 316, 317, 318, - 319, 320, 321, 322, 323, 324, 0, 0, 0, 0, - 333, 767, 768, 769, 770, 771, 0, 0, 334, 335, - 336, 0, 0, 299, 300, 327, 549, 328, 329, 330, - 331, 0, 0, 587, 588, 589, 612, 0, 590, 572, - 637, 411, 342, 553, 579, 778, 779, 0, 0, 0, - 0, 0, 0, 0, 688, 699, 735, 736, 0, 749, - 750, 752, 754, 753, 756, 546, 547, 764, 0, 0, - 435, 357, 358, 494, 495, 532, 0, 436, 437, 438, - 442, 439, 440, 441, 758, 759, 760, 757, 468, 531, - 554, 540, 777, 481, 0, 785, 627, 628, 786, 745, - 744, 343, 0, 337, 338, 702, 703, 0, 0, 534, - 480, 634, 500, 0, 0, 643, 677, 666, 755, 630, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 394, 0, 0, 463, 681, 662, 673, 663, 506, 648, - 649, 650, 657, 406, 651, 787, 653, 622, 654, 623, - 655, 656, 0, 680, 629, 542, 482, 0, 697, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 3363, 0, 0, - 268, 0, 0, 0, 0, 0, 0, 365, 269, 624, - 751, 626, 625, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 368, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 543, 571, 0, 584, 0, 432, 433, - 0, 0, 0, 0, 0, 0, 0, 350, 550, 568, - 366, 537, 582, 371, 545, 561, 361, 499, 533, 0, - 0, 352, 566, 544, 477, 351, 0, 526, 391, 408, - 388, 497, 0, 0, 565, 595, 387, 585, 0, 576, - 354, 0, 575, 496, 469, 562, 567, 478, 471, 0, - 353, 564, 476, 470, 446, 398, 611, 447, 448, 449, - 450, 451, 452, 412, 512, 467, 513, 413, 484, 483, - 485, 414, 415, 416, 417, 418, 419, 420, 421, 422, - 423, 0, 0, 0, 0, 0, 606, 607, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 743, 0, 0, 748, 0, 578, 0, - 0, 0, 0, 0, 0, 548, 0, 0, 453, 0, - 0, 0, 596, 0, 529, 502, 789, 0, 0, 527, - 465, 563, 514, 569, 551, 577, 521, 515, 344, 552, - 390, 479, 362, 364, 776, 392, 395, 399, 400, 490, - 491, 508, 536, 555, 556, 557, 389, 376, 528, 377, - 409, 378, 345, 381, 380, 382, 538, 383, 347, 509, - 560, 0, 405, 524, 474, 348, 473, 510, 559, 558, - 363, 586, 593, 594, 685, 0, 599, 790, 791, 792, - 608, 0, 516, 359, 356, 0, 0, 0, 385, 511, - 372, 374, 375, 373, 505, 507, 613, 614, 615, 617, - 0, 618, 619, 0, 0, 0, 0, 620, 686, 704, - 670, 639, 601, 694, 636, 640, 641, 426, 427, 428, - 429, 707, 0, 0, 0, 592, 454, 455, 0, 397, - 396, 475, 349, 0, 0, 443, 425, 517, 355, 393, - 445, 431, 456, 457, 458, 459, 460, 0, 461, 462, - 403, 339, 340, 784, 386, 498, 709, 746, 747, 631, - 0, 695, 632, 642, 379, 667, 679, 678, 492, 591, - 0, 690, 693, 621, 783, 0, 687, 701, 788, 700, - 780, 504, 0, 535, 698, 645, 0, 691, 664, 665, - 0, 692, 660, 696, 0, 633, 0, 602, 605, 635, - 722, 723, 724, 346, 604, 726, 727, 728, 729, 730, - 731, 732, 725, 574, 668, 644, 671, 583, 647, 646, - 0, 0, 682, 600, 683, 684, 486, 487, 488, 489, - 407, 710, 370, 603, 519, 520, 0, 669, 0, 0, - 0, 0, 0, 0, 0, 0, 674, 675, 672, 793, - 0, 733, 734, 0, 0, 597, 598, 402, 0, 616, - 410, 369, 503, 404, 581, 434, 0, 609, 676, 610, - 522, 523, 737, 742, 738, 739, 741, 762, 493, 424, - 430, 539, 444, 466, 525, 580, 501, 530, 367, 570, - 541, 472, 661, 689, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 325, 326, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 268, + 0, 0, 3239, 0, 0, 0, 366, 269, 628, 755, + 630, 629, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 369, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 546, 574, 0, 588, 0, 434, 435, 0, + 0, 0, 0, 0, 0, 0, 350, 553, 571, 367, + 540, 586, 372, 548, 564, 362, 501, 536, 0, 0, + 352, 569, 547, 479, 351, 0, 529, 392, 409, 389, + 499, 0, 0, 568, 599, 388, 589, 0, 579, 354, + 0, 578, 498, 471, 565, 570, 480, 473, 0, 353, + 567, 478, 472, 448, 399, 615, 449, 450, 451, 452, + 453, 454, 413, 514, 469, 515, 414, 486, 485, 487, + 415, 416, 417, 418, 419, 420, 421, 422, 423, 424, + 0, 0, 0, 0, 0, 610, 611, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 747, 0, 0, 752, 0, 582, 0, 0, + 0, 0, 0, 0, 551, 0, 0, 455, 0, 0, + 0, 600, 0, 532, 504, 793, 0, 0, 530, 467, + 566, 516, 572, 554, 580, 523, 517, 344, 555, 391, + 481, 363, 365, 780, 393, 396, 400, 401, 492, 493, + 510, 539, 558, 559, 560, 390, 377, 531, 378, 410, + 379, 345, 382, 381, 383, 541, 384, 347, 511, 563, + 0, 406, 527, 476, 348, 475, 512, 562, 561, 364, + 590, 597, 598, 689, 0, 603, 794, 795, 796, 612, + 0, 518, 360, 357, 0, 0, 0, 386, 513, 373, + 375, 376, 374, 507, 509, 617, 618, 619, 621, 0, + 622, 623, 0, 0, 0, 0, 624, 690, 708, 674, + 643, 605, 698, 640, 644, 645, 427, 428, 429, 430, + 711, 0, 0, 0, 596, 456, 457, 0, 398, 397, + 477, 349, 0, 0, 445, 426, 519, 355, 394, 447, + 432, 458, 459, 460, 461, 462, 0, 463, 464, 404, + 339, 340, 788, 387, 500, 713, 750, 751, 635, 0, + 699, 636, 646, 380, 671, 683, 682, 494, 595, 0, + 694, 697, 625, 787, 0, 691, 705, 792, 704, 784, + 506, 0, 538, 702, 649, 0, 695, 668, 669, 0, + 696, 664, 700, 356, 433, 524, 581, 0, 637, 0, + 606, 609, 639, 726, 727, 728, 346, 608, 730, 731, + 732, 733, 734, 735, 736, 729, 577, 672, 648, 675, + 587, 651, 650, 0, 0, 686, 604, 687, 688, 488, + 489, 490, 491, 408, 714, 371, 607, 521, 522, 0, + 673, 0, 0, 0, 0, 0, 0, 0, 0, 678, + 679, 676, 797, 0, 737, 738, 0, 0, 601, 602, + 403, 0, 620, 411, 370, 505, 405, 585, 436, 0, + 613, 680, 614, 525, 526, 741, 746, 742, 743, 745, + 766, 495, 425, 431, 542, 446, 468, 528, 584, 503, + 533, 368, 573, 544, 474, 665, 693, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 325, 326, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 718, 717, 716, 715, 714, 713, 712, 711, 0, - 0, 658, 384, 781, 775, 782, 763, 766, 765, 740, - 0, 341, 638, 464, 518, 401, 705, 706, 0, 761, - 284, 285, 286, 287, 288, 289, 290, 291, 332, 292, - 293, 294, 295, 296, 297, 298, 303, 304, 305, 306, - 307, 308, 309, 310, 708, 301, 302, 311, 312, 313, - 314, 315, 316, 317, 318, 319, 320, 321, 322, 323, - 324, 0, 0, 0, 0, 333, 767, 768, 769, 770, - 771, 0, 0, 334, 335, 336, 0, 0, 299, 300, - 327, 549, 328, 329, 330, 331, 0, 0, 587, 588, - 589, 612, 0, 590, 572, 637, 411, 342, 553, 579, - 778, 779, 0, 0, 0, 0, 0, 0, 0, 688, - 699, 735, 736, 0, 749, 750, 752, 754, 753, 756, - 546, 547, 764, 0, 0, 435, 357, 358, 494, 495, - 532, 0, 436, 437, 438, 442, 439, 440, 441, 758, - 759, 760, 757, 468, 531, 554, 540, 777, 481, 0, - 785, 627, 628, 786, 745, 744, 343, 0, 337, 338, - 702, 703, 0, 0, 534, 480, 634, 500, 0, 0, - 643, 677, 666, 755, 630, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 394, 0, 0, 463, 681, - 662, 673, 663, 506, 648, 649, 650, 657, 406, 651, - 787, 653, 622, 654, 623, 655, 656, 0, 680, 629, - 542, 482, 0, 697, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 722, 721, 720, 719, 718, 717, + 716, 715, 0, 0, 662, 385, 785, 779, 786, 767, + 770, 769, 744, 0, 341, 642, 466, 520, 402, 709, + 710, 0, 765, 284, 285, 286, 287, 288, 289, 290, + 291, 332, 292, 293, 294, 295, 296, 297, 298, 303, + 304, 305, 306, 307, 308, 309, 310, 712, 301, 302, + 311, 312, 313, 314, 315, 316, 317, 318, 319, 320, + 321, 322, 323, 324, 0, 0, 0, 0, 333, 771, + 772, 773, 774, 775, 0, 0, 334, 335, 336, 0, + 0, 299, 300, 327, 552, 328, 329, 330, 331, 0, + 0, 591, 592, 593, 616, 0, 594, 575, 641, 412, + 342, 556, 583, 782, 783, 0, 0, 0, 0, 0, + 0, 0, 692, 703, 739, 740, 0, 753, 754, 756, + 758, 757, 760, 549, 550, 768, 0, 0, 437, 358, + 359, 496, 497, 535, 0, 438, 439, 440, 444, 441, + 442, 443, 762, 763, 764, 761, 470, 534, 557, 543, + 781, 483, 0, 789, 631, 632, 790, 749, 748, 343, + 0, 337, 338, 706, 707, 0, 0, 537, 482, 638, + 502, 0, 0, 647, 681, 670, 759, 634, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 395, 0, + 0, 465, 685, 666, 677, 667, 508, 652, 653, 654, + 661, 407, 655, 791, 657, 626, 658, 627, 659, 660, + 0, 684, 633, 545, 484, 0, 701, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 268, 0, 0, 0, 0, - 0, 0, 365, 269, 624, 751, 626, 625, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 368, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 2731, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 543, 571, - 0, 584, 0, 432, 433, 0, 0, 0, 0, 0, - 0, 0, 350, 550, 568, 366, 537, 582, 371, 545, - 561, 361, 499, 533, 0, 0, 352, 566, 544, 477, - 351, 0, 526, 391, 408, 388, 497, 0, 0, 565, - 595, 387, 585, 0, 576, 354, 0, 575, 496, 469, - 562, 567, 478, 471, 0, 353, 564, 476, 470, 446, - 398, 611, 447, 448, 449, 450, 451, 452, 412, 512, - 467, 513, 413, 484, 483, 485, 414, 415, 416, 417, - 418, 419, 420, 421, 422, 423, 0, 0, 0, 0, - 0, 606, 607, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 743, 0, - 0, 748, 0, 578, 0, 0, 0, 0, 0, 0, - 548, 0, 0, 453, 0, 0, 0, 596, 0, 529, - 502, 789, 0, 0, 527, 465, 563, 514, 569, 551, - 577, 521, 515, 344, 552, 390, 479, 362, 364, 776, - 392, 395, 399, 400, 490, 491, 508, 536, 555, 556, - 557, 389, 376, 528, 377, 409, 378, 345, 381, 380, - 382, 538, 383, 347, 509, 560, 0, 405, 524, 474, - 348, 473, 510, 559, 558, 363, 586, 593, 594, 685, - 0, 599, 790, 791, 792, 608, 0, 516, 359, 356, - 0, 0, 0, 385, 511, 372, 374, 375, 373, 505, - 507, 613, 614, 615, 617, 0, 618, 619, 0, 0, - 0, 0, 620, 686, 704, 670, 639, 601, 694, 636, - 640, 641, 426, 427, 428, 429, 707, 0, 0, 0, - 592, 454, 455, 0, 397, 396, 475, 349, 0, 0, - 443, 425, 517, 355, 393, 445, 431, 456, 457, 458, - 459, 460, 0, 461, 462, 403, 339, 340, 784, 386, - 498, 709, 746, 747, 631, 0, 695, 632, 642, 379, - 667, 679, 678, 492, 591, 0, 690, 693, 621, 783, - 0, 687, 701, 788, 700, 780, 504, 0, 535, 698, - 645, 0, 691, 664, 665, 0, 692, 660, 696, 0, - 633, 0, 602, 605, 635, 722, 723, 724, 346, 604, - 726, 727, 728, 729, 730, 731, 732, 725, 574, 668, - 644, 671, 583, 647, 646, 0, 0, 682, 600, 683, - 684, 486, 487, 488, 489, 407, 710, 370, 603, 519, - 520, 0, 669, 0, 0, 0, 0, 0, 0, 0, - 0, 674, 675, 672, 793, 0, 733, 734, 0, 0, - 597, 598, 402, 0, 616, 410, 369, 503, 404, 581, - 434, 0, 609, 676, 610, 522, 523, 737, 742, 738, - 739, 741, 762, 493, 424, 430, 539, 444, 466, 525, - 580, 501, 530, 367, 570, 541, 472, 661, 689, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 268, 0, + 0, 0, 0, 0, 0, 366, 269, 628, 755, 630, + 629, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 369, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 3187, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 546, 574, 0, 588, 0, 434, 435, 0, 0, + 0, 0, 0, 0, 0, 350, 553, 571, 367, 540, + 586, 372, 548, 564, 362, 501, 536, 0, 0, 352, + 569, 547, 479, 351, 0, 529, 392, 409, 389, 499, + 0, 0, 568, 599, 388, 589, 0, 579, 354, 0, + 578, 498, 471, 565, 570, 480, 473, 0, 353, 567, + 478, 472, 448, 399, 615, 449, 450, 451, 452, 453, + 454, 413, 514, 469, 515, 414, 486, 485, 487, 415, + 416, 417, 418, 419, 420, 421, 422, 423, 424, 0, + 0, 0, 0, 0, 610, 611, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 747, 0, 0, 752, 0, 582, 0, 0, 0, + 0, 0, 0, 551, 0, 0, 455, 0, 0, 0, + 600, 0, 532, 504, 793, 0, 0, 530, 467, 566, + 516, 572, 554, 580, 523, 517, 344, 555, 391, 481, + 363, 365, 780, 393, 396, 400, 401, 492, 493, 510, + 539, 558, 559, 560, 390, 377, 531, 378, 410, 379, + 345, 382, 381, 383, 541, 384, 347, 511, 563, 0, + 406, 527, 476, 348, 475, 512, 562, 561, 364, 590, + 597, 598, 689, 0, 603, 794, 795, 796, 612, 0, + 518, 360, 357, 0, 0, 0, 386, 513, 373, 375, + 376, 374, 507, 509, 617, 618, 619, 621, 0, 622, + 623, 0, 0, 0, 0, 624, 690, 708, 674, 643, + 605, 698, 640, 644, 645, 427, 428, 429, 430, 711, + 0, 0, 0, 596, 456, 457, 0, 398, 397, 477, + 349, 0, 0, 445, 426, 519, 355, 394, 447, 432, + 458, 459, 460, 461, 462, 0, 463, 464, 404, 339, + 340, 788, 387, 500, 713, 750, 751, 635, 0, 699, + 636, 646, 380, 671, 683, 682, 494, 595, 0, 694, + 697, 625, 787, 0, 691, 705, 792, 704, 784, 506, + 0, 538, 702, 649, 0, 695, 668, 669, 0, 696, + 664, 700, 356, 433, 524, 581, 0, 637, 0, 606, + 609, 639, 726, 727, 728, 346, 608, 730, 731, 732, + 733, 734, 735, 736, 729, 577, 672, 648, 675, 587, + 651, 650, 0, 0, 686, 604, 687, 688, 488, 489, + 490, 491, 408, 714, 371, 607, 521, 522, 0, 673, + 0, 0, 0, 0, 0, 0, 0, 0, 678, 679, + 676, 797, 0, 737, 738, 0, 0, 601, 602, 403, + 0, 620, 411, 370, 505, 405, 585, 436, 0, 613, + 680, 614, 525, 526, 741, 746, 742, 743, 745, 766, + 495, 425, 431, 542, 446, 468, 528, 584, 503, 533, + 368, 573, 544, 474, 665, 693, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 325, 326, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 325, 326, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 718, 717, 716, 715, - 714, 713, 712, 711, 0, 0, 658, 384, 781, 775, - 782, 763, 766, 765, 740, 0, 341, 638, 464, 518, - 401, 705, 706, 0, 761, 284, 285, 286, 287, 288, - 289, 290, 291, 332, 292, 293, 294, 295, 296, 297, - 298, 303, 304, 305, 306, 307, 308, 309, 310, 708, - 301, 302, 311, 312, 313, 314, 315, 316, 317, 318, - 319, 320, 321, 322, 323, 324, 0, 0, 0, 0, - 333, 767, 768, 769, 770, 771, 0, 0, 334, 335, - 336, 0, 0, 299, 300, 327, 549, 328, 329, 330, - 331, 0, 0, 587, 588, 589, 612, 0, 590, 572, - 637, 411, 342, 553, 579, 778, 779, 0, 0, 0, - 0, 0, 0, 0, 688, 699, 735, 736, 0, 749, - 750, 752, 754, 753, 756, 546, 547, 764, 0, 0, - 435, 357, 358, 494, 495, 532, 0, 436, 437, 438, - 442, 439, 440, 441, 758, 759, 760, 757, 468, 531, - 554, 540, 777, 481, 0, 785, 627, 628, 786, 745, - 744, 343, 0, 337, 338, 702, 703, 0, 0, 534, - 480, 634, 500, 0, 0, 643, 677, 666, 755, 630, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 394, 0, 0, 463, 681, 662, 673, 663, 506, 648, - 649, 650, 657, 406, 651, 787, 653, 622, 654, 623, - 655, 656, 0, 680, 629, 542, 482, 0, 697, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 268, 0, 0, 3235, 0, 0, 0, 365, 269, 624, - 751, 626, 625, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 368, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 543, 571, 0, 584, 0, 432, 433, - 0, 0, 0, 0, 0, 0, 0, 350, 550, 568, - 366, 537, 582, 371, 545, 561, 361, 499, 533, 0, - 0, 352, 566, 544, 477, 351, 0, 526, 391, 408, - 388, 497, 0, 0, 565, 595, 387, 585, 0, 576, - 354, 0, 575, 496, 469, 562, 567, 478, 471, 0, - 353, 564, 476, 470, 446, 398, 611, 447, 448, 449, - 450, 451, 452, 412, 512, 467, 513, 413, 484, 483, - 485, 414, 415, 416, 417, 418, 419, 420, 421, 422, - 423, 0, 0, 0, 0, 0, 606, 607, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 743, 0, 0, 748, 0, 578, 0, - 0, 0, 0, 0, 0, 548, 0, 0, 453, 0, - 0, 0, 596, 0, 529, 502, 789, 0, 0, 527, - 465, 563, 514, 569, 551, 577, 521, 515, 344, 552, - 390, 479, 362, 364, 776, 392, 395, 399, 400, 490, - 491, 508, 536, 555, 556, 557, 389, 376, 528, 377, - 409, 378, 345, 381, 380, 382, 538, 383, 347, 509, - 560, 0, 405, 524, 474, 348, 473, 510, 559, 558, - 363, 586, 593, 594, 685, 0, 599, 790, 791, 792, - 608, 0, 516, 359, 356, 0, 0, 0, 385, 511, - 372, 374, 375, 373, 505, 507, 613, 614, 615, 617, - 0, 618, 619, 0, 0, 0, 0, 620, 686, 704, - 670, 639, 601, 694, 636, 640, 641, 426, 427, 428, - 429, 707, 0, 0, 0, 592, 454, 455, 0, 397, - 396, 475, 349, 0, 0, 443, 425, 517, 355, 393, - 445, 431, 456, 457, 458, 459, 460, 0, 461, 462, - 403, 339, 340, 784, 386, 498, 709, 746, 747, 631, - 0, 695, 632, 642, 379, 667, 679, 678, 492, 591, - 0, 690, 693, 621, 783, 0, 687, 701, 788, 700, - 780, 504, 0, 535, 698, 645, 0, 691, 664, 665, - 0, 692, 660, 696, 0, 633, 0, 602, 605, 635, - 722, 723, 724, 346, 604, 726, 727, 728, 729, 730, - 731, 732, 725, 574, 668, 644, 671, 583, 647, 646, - 0, 0, 682, 600, 683, 684, 486, 487, 488, 489, - 407, 710, 370, 603, 519, 520, 0, 669, 0, 0, - 0, 0, 0, 0, 0, 0, 674, 675, 672, 793, - 0, 733, 734, 0, 0, 597, 598, 402, 0, 616, - 410, 369, 503, 404, 581, 434, 0, 609, 676, 610, - 522, 523, 737, 742, 738, 739, 741, 762, 493, 424, - 430, 539, 444, 466, 525, 580, 501, 530, 367, 570, - 541, 472, 661, 689, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 325, 326, 0, 0, 0, + 0, 0, 0, 722, 721, 720, 719, 718, 717, 716, + 715, 0, 0, 662, 385, 785, 779, 786, 767, 770, + 769, 744, 0, 341, 642, 466, 520, 402, 709, 710, + 0, 765, 284, 285, 286, 287, 288, 289, 290, 291, + 332, 292, 293, 294, 295, 296, 297, 298, 303, 304, + 305, 306, 307, 308, 309, 310, 712, 301, 302, 311, + 312, 313, 314, 315, 316, 317, 318, 319, 320, 321, + 322, 323, 324, 0, 0, 0, 0, 333, 771, 772, + 773, 774, 775, 0, 0, 334, 335, 336, 0, 0, + 299, 300, 327, 552, 328, 329, 330, 331, 0, 0, + 591, 592, 593, 616, 0, 594, 575, 641, 412, 342, + 556, 583, 782, 783, 0, 0, 0, 0, 0, 0, + 0, 692, 703, 739, 740, 0, 753, 754, 756, 758, + 757, 760, 549, 550, 768, 0, 0, 437, 358, 359, + 496, 497, 535, 0, 438, 439, 440, 444, 441, 442, + 443, 762, 763, 764, 761, 470, 534, 557, 543, 781, + 483, 0, 789, 631, 632, 790, 749, 748, 343, 0, + 337, 338, 706, 707, 0, 0, 537, 482, 638, 502, + 0, 0, 647, 681, 670, 759, 634, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 395, 0, 0, + 465, 685, 666, 677, 667, 508, 652, 653, 654, 661, + 407, 655, 791, 657, 626, 658, 627, 659, 660, 0, + 684, 633, 545, 484, 0, 701, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 268, 0, 0, + 3185, 0, 0, 0, 366, 269, 628, 755, 630, 629, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 369, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 546, 574, 0, 588, 0, 434, 435, 0, 0, 0, + 0, 0, 0, 0, 350, 553, 571, 367, 540, 586, + 372, 548, 564, 362, 501, 536, 0, 0, 352, 569, + 547, 479, 351, 0, 529, 392, 409, 389, 499, 0, + 0, 568, 599, 388, 589, 0, 579, 354, 0, 578, + 498, 471, 565, 570, 480, 473, 0, 353, 567, 478, + 472, 448, 399, 615, 449, 450, 451, 452, 453, 454, + 413, 514, 469, 515, 414, 486, 485, 487, 415, 416, + 417, 418, 419, 420, 421, 422, 423, 424, 0, 0, + 0, 0, 0, 610, 611, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 747, 0, 0, 752, 0, 582, 0, 0, 0, 0, + 0, 0, 551, 0, 0, 455, 0, 0, 0, 600, + 0, 532, 504, 793, 0, 0, 530, 467, 566, 516, + 572, 554, 580, 523, 517, 344, 555, 391, 481, 363, + 365, 780, 393, 396, 400, 401, 492, 493, 510, 539, + 558, 559, 560, 390, 377, 531, 378, 410, 379, 345, + 382, 381, 383, 541, 384, 347, 511, 563, 0, 406, + 527, 476, 348, 475, 512, 562, 561, 364, 590, 597, + 598, 689, 0, 603, 794, 795, 796, 612, 0, 518, + 360, 357, 0, 0, 0, 386, 513, 373, 375, 376, + 374, 507, 509, 617, 618, 619, 621, 0, 622, 623, + 0, 0, 0, 0, 624, 690, 708, 674, 643, 605, + 698, 640, 644, 645, 427, 428, 429, 430, 711, 0, + 0, 0, 596, 456, 457, 0, 398, 397, 477, 349, + 0, 0, 445, 426, 519, 355, 394, 447, 432, 458, + 459, 460, 461, 462, 0, 463, 464, 404, 339, 340, + 788, 387, 500, 713, 750, 751, 635, 0, 699, 636, + 646, 380, 671, 683, 682, 494, 595, 0, 694, 697, + 625, 787, 0, 691, 705, 792, 704, 784, 506, 0, + 538, 702, 649, 0, 695, 668, 669, 0, 696, 664, + 700, 356, 433, 524, 581, 0, 637, 0, 606, 609, + 639, 726, 727, 728, 346, 608, 730, 731, 732, 733, + 734, 735, 736, 729, 577, 672, 648, 675, 587, 651, + 650, 0, 0, 686, 604, 687, 688, 488, 489, 490, + 491, 408, 714, 371, 607, 521, 522, 0, 673, 0, + 0, 0, 0, 0, 0, 0, 0, 678, 679, 676, + 797, 0, 737, 738, 0, 0, 601, 602, 403, 0, + 620, 411, 370, 505, 405, 585, 436, 0, 613, 680, + 614, 525, 526, 741, 746, 742, 743, 745, 766, 495, + 425, 431, 542, 446, 468, 528, 584, 503, 533, 368, + 573, 544, 474, 665, 693, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 325, 326, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 718, 717, 716, 715, 714, 713, 712, 711, 0, - 0, 658, 384, 781, 775, 782, 763, 766, 765, 740, - 0, 341, 638, 464, 518, 401, 705, 706, 0, 761, - 284, 285, 286, 287, 288, 289, 290, 291, 332, 292, - 293, 294, 295, 296, 297, 298, 303, 304, 305, 306, - 307, 308, 309, 310, 708, 301, 302, 311, 312, 313, - 314, 315, 316, 317, 318, 319, 320, 321, 322, 323, - 324, 0, 0, 0, 0, 333, 767, 768, 769, 770, - 771, 0, 0, 334, 335, 336, 0, 0, 299, 300, - 327, 549, 328, 329, 330, 331, 0, 0, 587, 588, - 589, 612, 0, 590, 572, 637, 411, 342, 553, 579, - 778, 779, 0, 0, 0, 0, 0, 0, 0, 688, - 699, 735, 736, 0, 749, 750, 752, 754, 753, 756, - 546, 547, 764, 0, 0, 435, 357, 358, 494, 495, - 532, 0, 436, 437, 438, 442, 439, 440, 441, 758, - 759, 760, 757, 468, 531, 554, 540, 777, 481, 0, - 785, 627, 628, 786, 745, 744, 343, 0, 337, 338, - 702, 703, 0, 0, 534, 480, 634, 500, 0, 0, - 643, 677, 666, 755, 630, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 394, 0, 0, 463, 681, - 662, 673, 663, 506, 648, 649, 650, 657, 406, 651, - 787, 653, 622, 654, 623, 655, 656, 0, 680, 629, - 542, 482, 0, 697, 0, 0, 0, 0, 0, 0, + 0, 0, 722, 721, 720, 719, 718, 717, 716, 715, + 0, 0, 662, 385, 785, 779, 786, 767, 770, 769, + 744, 0, 341, 642, 466, 520, 402, 709, 710, 0, + 765, 284, 285, 286, 287, 288, 289, 290, 291, 332, + 292, 293, 294, 295, 296, 297, 298, 303, 304, 305, + 306, 307, 308, 309, 310, 712, 301, 302, 311, 312, + 313, 314, 315, 316, 317, 318, 319, 320, 321, 322, + 323, 324, 0, 0, 0, 0, 333, 771, 772, 773, + 774, 775, 0, 0, 334, 335, 336, 0, 0, 299, + 300, 327, 552, 328, 329, 330, 331, 0, 0, 591, + 592, 593, 616, 0, 594, 575, 641, 412, 342, 556, + 583, 782, 783, 0, 0, 0, 0, 0, 0, 0, + 692, 703, 739, 740, 0, 753, 754, 756, 758, 757, + 760, 549, 550, 768, 0, 0, 437, 358, 359, 496, + 497, 535, 0, 438, 439, 440, 444, 441, 442, 443, + 762, 763, 764, 761, 470, 534, 557, 543, 781, 483, + 0, 789, 631, 632, 790, 749, 748, 343, 0, 337, + 338, 706, 707, 2982, 0, 537, 482, 638, 0, 502, + 0, 0, 647, 681, 670, 759, 634, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 395, 0, 0, + 465, 685, 666, 677, 667, 508, 652, 653, 654, 661, + 407, 655, 791, 657, 626, 658, 627, 659, 660, 0, + 684, 633, 545, 484, 0, 701, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 268, 0, 0, + 0, 0, 0, 0, 366, 269, 628, 755, 630, 629, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 369, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 546, 574, 0, 588, 0, 434, 435, 0, 0, 0, + 0, 0, 0, 0, 350, 553, 571, 367, 540, 586, + 372, 548, 564, 362, 501, 536, 0, 0, 352, 569, + 547, 479, 351, 0, 529, 392, 409, 389, 499, 0, + 0, 568, 599, 388, 589, 0, 579, 354, 0, 578, + 498, 471, 565, 570, 480, 473, 0, 353, 567, 478, + 472, 448, 399, 615, 449, 450, 451, 452, 453, 454, + 413, 514, 469, 515, 414, 486, 485, 487, 415, 416, + 417, 418, 419, 420, 421, 422, 423, 424, 0, 0, + 0, 0, 0, 610, 611, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 747, 0, 0, 752, 0, 582, 0, 0, 0, 0, + 0, 0, 551, 0, 0, 455, 0, 0, 0, 600, + 0, 532, 504, 793, 0, 0, 530, 467, 566, 516, + 572, 554, 580, 523, 517, 344, 555, 391, 481, 363, + 365, 780, 393, 396, 400, 401, 492, 493, 510, 539, + 558, 559, 560, 390, 377, 531, 378, 410, 379, 345, + 382, 381, 383, 541, 384, 347, 511, 563, 0, 406, + 527, 476, 348, 475, 512, 562, 561, 364, 590, 597, + 598, 689, 0, 603, 794, 795, 796, 612, 0, 518, + 360, 357, 0, 0, 0, 386, 513, 373, 375, 376, + 374, 507, 509, 617, 618, 619, 621, 0, 622, 623, + 0, 0, 0, 0, 624, 690, 708, 674, 643, 605, + 698, 640, 644, 645, 427, 428, 429, 430, 711, 0, + 0, 0, 596, 456, 457, 0, 398, 397, 477, 349, + 0, 0, 445, 426, 519, 355, 394, 447, 432, 458, + 459, 460, 461, 462, 0, 463, 464, 404, 339, 340, + 788, 387, 500, 713, 750, 751, 635, 0, 699, 636, + 646, 380, 671, 683, 682, 494, 595, 0, 694, 697, + 625, 787, 0, 691, 705, 792, 704, 784, 506, 0, + 538, 702, 649, 0, 695, 668, 669, 0, 696, 664, + 700, 356, 433, 524, 581, 0, 637, 0, 606, 609, + 639, 726, 727, 728, 346, 608, 730, 731, 732, 733, + 734, 735, 736, 729, 577, 672, 648, 675, 587, 651, + 650, 0, 0, 686, 604, 687, 688, 488, 489, 490, + 491, 408, 714, 371, 607, 521, 522, 0, 673, 0, + 0, 0, 0, 0, 0, 0, 0, 678, 679, 676, + 797, 0, 737, 738, 0, 0, 601, 602, 403, 0, + 620, 411, 370, 505, 405, 585, 436, 0, 613, 680, + 614, 525, 526, 741, 746, 742, 743, 745, 766, 495, + 425, 431, 542, 446, 468, 528, 584, 503, 533, 368, + 573, 544, 474, 665, 693, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 325, 326, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 268, 0, 0, 0, 0, - 0, 0, 365, 269, 624, 751, 626, 625, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 368, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 3183, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 543, 571, - 0, 584, 0, 432, 433, 0, 0, 0, 0, 0, - 0, 0, 350, 550, 568, 366, 537, 582, 371, 545, - 561, 361, 499, 533, 0, 0, 352, 566, 544, 477, - 351, 0, 526, 391, 408, 388, 497, 0, 0, 565, - 595, 387, 585, 0, 576, 354, 0, 575, 496, 469, - 562, 567, 478, 471, 0, 353, 564, 476, 470, 446, - 398, 611, 447, 448, 449, 450, 451, 452, 412, 512, - 467, 513, 413, 484, 483, 485, 414, 415, 416, 417, - 418, 419, 420, 421, 422, 423, 0, 0, 0, 0, - 0, 606, 607, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 743, 0, - 0, 748, 0, 578, 0, 0, 0, 0, 0, 0, - 548, 0, 0, 453, 0, 0, 0, 596, 0, 529, - 502, 789, 0, 0, 527, 465, 563, 514, 569, 551, - 577, 521, 515, 344, 552, 390, 479, 362, 364, 776, - 392, 395, 399, 400, 490, 491, 508, 536, 555, 556, - 557, 389, 376, 528, 377, 409, 378, 345, 381, 380, - 382, 538, 383, 347, 509, 560, 0, 405, 524, 474, - 348, 473, 510, 559, 558, 363, 586, 593, 594, 685, - 0, 599, 790, 791, 792, 608, 0, 516, 359, 356, - 0, 0, 0, 385, 511, 372, 374, 375, 373, 505, - 507, 613, 614, 615, 617, 0, 618, 619, 0, 0, - 0, 0, 620, 686, 704, 670, 639, 601, 694, 636, - 640, 641, 426, 427, 428, 429, 707, 0, 0, 0, - 592, 454, 455, 0, 397, 396, 475, 349, 0, 0, - 443, 425, 517, 355, 393, 445, 431, 456, 457, 458, - 459, 460, 0, 461, 462, 403, 339, 340, 784, 386, - 498, 709, 746, 747, 631, 0, 695, 632, 642, 379, - 667, 679, 678, 492, 591, 0, 690, 693, 621, 783, - 0, 687, 701, 788, 700, 780, 504, 0, 535, 698, - 645, 0, 691, 664, 665, 0, 692, 660, 696, 0, - 633, 0, 602, 605, 635, 722, 723, 724, 346, 604, - 726, 727, 728, 729, 730, 731, 732, 725, 574, 668, - 644, 671, 583, 647, 646, 0, 0, 682, 600, 683, - 684, 486, 487, 488, 489, 407, 710, 370, 603, 519, - 520, 0, 669, 0, 0, 0, 0, 0, 0, 0, - 0, 674, 675, 672, 793, 0, 733, 734, 0, 0, - 597, 598, 402, 0, 616, 410, 369, 503, 404, 581, - 434, 0, 609, 676, 610, 522, 523, 737, 742, 738, - 739, 741, 762, 493, 424, 430, 539, 444, 466, 525, - 580, 501, 530, 367, 570, 541, 472, 661, 689, 0, + 0, 0, 722, 721, 720, 719, 718, 717, 716, 715, + 0, 0, 662, 385, 785, 779, 786, 767, 770, 769, + 744, 0, 341, 642, 466, 520, 402, 709, 710, 0, + 765, 284, 285, 286, 287, 288, 289, 290, 291, 332, + 292, 293, 294, 295, 296, 297, 298, 303, 304, 305, + 306, 307, 308, 309, 310, 712, 301, 302, 311, 312, + 313, 314, 315, 316, 317, 318, 319, 320, 321, 322, + 323, 324, 0, 0, 0, 0, 333, 771, 772, 773, + 774, 775, 0, 0, 334, 335, 336, 0, 0, 299, + 300, 327, 552, 328, 329, 330, 331, 0, 0, 591, + 592, 593, 616, 0, 594, 575, 641, 412, 342, 556, + 583, 782, 783, 0, 0, 0, 0, 0, 0, 0, + 692, 703, 739, 740, 0, 753, 754, 756, 758, 757, + 760, 549, 550, 768, 0, 0, 437, 358, 359, 496, + 497, 535, 0, 438, 439, 440, 444, 441, 442, 443, + 762, 763, 764, 761, 470, 534, 557, 543, 781, 483, + 0, 789, 631, 632, 790, 749, 748, 343, 0, 337, + 338, 706, 707, 0, 0, 537, 482, 638, 502, 0, + 0, 647, 681, 670, 759, 634, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 395, 0, 0, 465, + 685, 666, 677, 667, 508, 652, 653, 654, 661, 407, + 655, 791, 657, 626, 658, 627, 659, 660, 0, 684, + 633, 545, 484, 0, 701, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 325, 326, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 718, 717, 716, 715, - 714, 713, 712, 711, 0, 0, 658, 384, 781, 775, - 782, 763, 766, 765, 740, 0, 341, 638, 464, 518, - 401, 705, 706, 0, 761, 284, 285, 286, 287, 288, - 289, 290, 291, 332, 292, 293, 294, 295, 296, 297, - 298, 303, 304, 305, 306, 307, 308, 309, 310, 708, - 301, 302, 311, 312, 313, 314, 315, 316, 317, 318, - 319, 320, 321, 322, 323, 324, 0, 0, 0, 0, - 333, 767, 768, 769, 770, 771, 0, 0, 334, 335, - 336, 0, 0, 299, 300, 327, 549, 328, 329, 330, - 331, 0, 0, 587, 588, 589, 612, 0, 590, 572, - 637, 411, 342, 553, 579, 778, 779, 0, 0, 0, - 0, 0, 0, 0, 688, 699, 735, 736, 0, 749, - 750, 752, 754, 753, 756, 546, 547, 764, 0, 0, - 435, 357, 358, 494, 495, 532, 0, 436, 437, 438, - 442, 439, 440, 441, 758, 759, 760, 757, 468, 531, - 554, 540, 777, 481, 0, 785, 627, 628, 786, 745, - 744, 343, 0, 337, 338, 702, 703, 0, 0, 534, - 480, 634, 500, 0, 0, 643, 677, 666, 755, 630, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 394, 0, 0, 463, 681, 662, 673, 663, 506, 648, - 649, 650, 657, 406, 651, 787, 653, 622, 654, 623, - 655, 656, 0, 680, 629, 542, 482, 0, 697, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 268, 0, 0, 3181, 0, 0, 0, 365, 269, 624, - 751, 626, 625, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 368, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 543, 571, 0, 584, 0, 432, 433, - 0, 0, 0, 0, 0, 0, 0, 350, 550, 568, - 366, 537, 582, 371, 545, 561, 361, 499, 533, 0, - 0, 352, 566, 544, 477, 351, 0, 526, 391, 408, - 388, 497, 0, 0, 565, 595, 387, 585, 0, 576, - 354, 0, 575, 496, 469, 562, 567, 478, 471, 0, - 353, 564, 476, 470, 446, 398, 611, 447, 448, 449, - 450, 451, 452, 412, 512, 467, 513, 413, 484, 483, - 485, 414, 415, 416, 417, 418, 419, 420, 421, 422, - 423, 0, 0, 0, 0, 0, 606, 607, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 743, 0, 0, 748, 0, 578, 0, - 0, 0, 0, 0, 0, 548, 0, 0, 453, 0, - 0, 0, 596, 0, 529, 502, 789, 0, 0, 527, - 465, 563, 514, 569, 551, 577, 521, 515, 344, 552, - 390, 479, 362, 364, 776, 392, 395, 399, 400, 490, - 491, 508, 536, 555, 556, 557, 389, 376, 528, 377, - 409, 378, 345, 381, 380, 382, 538, 383, 347, 509, - 560, 0, 405, 524, 474, 348, 473, 510, 559, 558, - 363, 586, 593, 594, 685, 0, 599, 790, 791, 792, - 608, 0, 516, 359, 356, 0, 0, 0, 385, 511, - 372, 374, 375, 373, 505, 507, 613, 614, 615, 617, - 0, 618, 619, 0, 0, 0, 0, 620, 686, 704, - 670, 639, 601, 694, 636, 640, 641, 426, 427, 428, - 429, 707, 0, 0, 0, 592, 454, 455, 0, 397, - 396, 475, 349, 0, 0, 443, 425, 517, 355, 393, - 445, 431, 456, 457, 458, 459, 460, 0, 461, 462, - 403, 339, 340, 784, 386, 498, 709, 746, 747, 631, - 0, 695, 632, 642, 379, 667, 679, 678, 492, 591, - 0, 690, 693, 621, 783, 0, 687, 701, 788, 700, - 780, 504, 0, 535, 698, 645, 0, 691, 664, 665, - 0, 692, 660, 696, 0, 633, 0, 602, 605, 635, - 722, 723, 724, 346, 604, 726, 727, 728, 729, 730, - 731, 732, 725, 574, 668, 644, 671, 583, 647, 646, - 0, 0, 682, 600, 683, 684, 486, 487, 488, 489, - 407, 710, 370, 603, 519, 520, 0, 669, 0, 0, - 0, 0, 0, 0, 0, 0, 674, 675, 672, 793, - 0, 733, 734, 0, 0, 597, 598, 402, 0, 616, - 410, 369, 503, 404, 581, 434, 0, 609, 676, 610, - 522, 523, 737, 742, 738, 739, 741, 762, 493, 424, - 430, 539, 444, 466, 525, 580, 501, 530, 367, 570, - 541, 472, 661, 689, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 268, 0, 0, 0, + 2386, 0, 0, 366, 269, 628, 755, 630, 629, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 369, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 546, + 574, 0, 588, 0, 434, 435, 0, 0, 0, 0, + 0, 0, 0, 350, 553, 571, 367, 540, 586, 372, + 548, 564, 362, 501, 536, 0, 0, 352, 569, 547, + 479, 351, 0, 529, 392, 409, 389, 499, 0, 0, + 568, 599, 388, 589, 0, 579, 354, 0, 578, 498, + 471, 565, 570, 480, 473, 0, 353, 567, 478, 472, + 448, 399, 615, 449, 450, 451, 452, 453, 454, 413, + 514, 469, 515, 414, 486, 485, 487, 415, 416, 417, + 418, 419, 420, 421, 422, 423, 424, 0, 0, 0, + 0, 0, 610, 611, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 747, + 0, 0, 752, 0, 582, 0, 0, 0, 0, 0, + 0, 551, 0, 0, 455, 0, 0, 0, 600, 0, + 532, 504, 793, 0, 0, 530, 467, 566, 516, 572, + 554, 580, 523, 517, 344, 555, 391, 481, 363, 365, + 780, 393, 396, 400, 401, 492, 493, 510, 539, 558, + 559, 560, 390, 377, 531, 378, 410, 379, 345, 382, + 381, 383, 541, 384, 347, 511, 563, 0, 406, 527, + 476, 348, 475, 512, 562, 561, 364, 590, 597, 598, + 689, 0, 603, 794, 795, 796, 612, 0, 518, 360, + 357, 0, 0, 0, 386, 513, 373, 375, 376, 374, + 507, 509, 617, 618, 619, 621, 0, 622, 623, 0, + 0, 0, 0, 624, 690, 708, 674, 643, 605, 698, + 640, 644, 645, 427, 428, 429, 430, 711, 0, 0, + 0, 596, 456, 457, 0, 398, 397, 477, 349, 0, + 0, 445, 426, 519, 355, 394, 447, 432, 458, 459, + 460, 461, 462, 0, 463, 464, 404, 339, 340, 788, + 387, 500, 713, 750, 751, 635, 0, 699, 636, 646, + 380, 671, 683, 682, 494, 595, 0, 694, 697, 625, + 787, 0, 691, 705, 792, 704, 784, 506, 0, 538, + 702, 649, 0, 695, 668, 669, 0, 696, 664, 700, + 356, 433, 524, 581, 0, 637, 0, 606, 609, 639, + 726, 727, 728, 346, 608, 730, 731, 732, 733, 734, + 735, 736, 729, 577, 672, 648, 675, 587, 651, 650, + 0, 0, 686, 604, 687, 688, 488, 489, 490, 491, + 408, 714, 371, 607, 521, 522, 0, 673, 0, 0, + 0, 0, 0, 0, 0, 0, 678, 679, 676, 797, + 0, 737, 738, 0, 0, 601, 602, 403, 0, 620, + 411, 370, 505, 405, 585, 436, 0, 613, 680, 614, + 525, 526, 741, 746, 742, 743, 745, 766, 495, 425, + 431, 542, 446, 468, 528, 584, 503, 533, 368, 573, + 544, 474, 665, 693, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 325, 326, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 718, 717, 716, 715, 714, 713, 712, 711, 0, - 0, 658, 384, 781, 775, 782, 763, 766, 765, 740, - 0, 341, 638, 464, 518, 401, 705, 706, 0, 761, + 0, 722, 721, 720, 719, 718, 717, 716, 715, 0, + 0, 662, 385, 785, 779, 786, 767, 770, 769, 744, + 0, 341, 642, 466, 520, 402, 709, 710, 0, 765, 284, 285, 286, 287, 288, 289, 290, 291, 332, 292, 293, 294, 295, 296, 297, 298, 303, 304, 305, 306, - 307, 308, 309, 310, 708, 301, 302, 311, 312, 313, + 307, 308, 309, 310, 712, 301, 302, 311, 312, 313, 314, 315, 316, 317, 318, 319, 320, 321, 322, 323, - 324, 0, 0, 0, 0, 333, 767, 768, 769, 770, - 771, 0, 0, 334, 335, 336, 0, 0, 299, 300, - 327, 549, 328, 329, 330, 331, 0, 0, 587, 588, - 589, 612, 0, 590, 572, 637, 411, 342, 553, 579, - 778, 779, 0, 0, 0, 0, 0, 0, 0, 688, - 699, 735, 736, 0, 749, 750, 752, 754, 753, 756, - 546, 547, 764, 0, 0, 435, 357, 358, 494, 495, - 532, 0, 436, 437, 438, 442, 439, 440, 441, 758, - 759, 760, 757, 468, 531, 554, 540, 777, 481, 0, - 785, 627, 628, 786, 745, 744, 343, 0, 337, 338, - 702, 703, 2978, 0, 534, 480, 634, 0, 500, 0, - 0, 643, 677, 666, 755, 630, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 394, 0, 0, 463, - 681, 662, 673, 663, 506, 648, 649, 650, 657, 406, - 651, 787, 653, 622, 654, 623, 655, 656, 0, 680, - 629, 542, 482, 0, 697, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 268, 0, 0, 0, - 0, 0, 0, 365, 269, 624, 751, 626, 625, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 368, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 543, - 571, 0, 584, 0, 432, 433, 0, 0, 0, 0, - 0, 0, 0, 350, 550, 568, 366, 537, 582, 371, - 545, 561, 361, 499, 533, 0, 0, 352, 566, 544, - 477, 351, 0, 526, 391, 408, 388, 497, 0, 0, - 565, 595, 387, 585, 0, 576, 354, 0, 575, 496, - 469, 562, 567, 478, 471, 0, 353, 564, 476, 470, - 446, 398, 611, 447, 448, 449, 450, 451, 452, 412, - 512, 467, 513, 413, 484, 483, 485, 414, 415, 416, - 417, 418, 419, 420, 421, 422, 423, 0, 0, 0, - 0, 0, 606, 607, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 743, - 0, 0, 748, 0, 578, 0, 0, 0, 0, 0, - 0, 548, 0, 0, 453, 0, 0, 0, 596, 0, - 529, 502, 789, 0, 0, 527, 465, 563, 514, 569, - 551, 577, 521, 515, 344, 552, 390, 479, 362, 364, - 776, 392, 395, 399, 400, 490, 491, 508, 536, 555, - 556, 557, 389, 376, 528, 377, 409, 378, 345, 381, - 380, 382, 538, 383, 347, 509, 560, 0, 405, 524, - 474, 348, 473, 510, 559, 558, 363, 586, 593, 594, - 685, 0, 599, 790, 791, 792, 608, 0, 516, 359, - 356, 0, 0, 0, 385, 511, 372, 374, 375, 373, - 505, 507, 613, 614, 615, 617, 0, 618, 619, 0, - 0, 0, 0, 620, 686, 704, 670, 639, 601, 694, - 636, 640, 641, 426, 427, 428, 429, 707, 0, 0, - 0, 592, 454, 455, 0, 397, 396, 475, 349, 0, - 0, 443, 425, 517, 355, 393, 445, 431, 456, 457, - 458, 459, 460, 0, 461, 462, 403, 339, 340, 784, - 386, 498, 709, 746, 747, 631, 0, 695, 632, 642, - 379, 667, 679, 678, 492, 591, 0, 690, 693, 621, - 783, 0, 687, 701, 788, 700, 780, 504, 0, 535, - 698, 645, 0, 691, 664, 665, 0, 692, 660, 696, - 0, 633, 0, 602, 605, 635, 722, 723, 724, 346, - 604, 726, 727, 728, 729, 730, 731, 732, 725, 574, - 668, 644, 671, 583, 647, 646, 0, 0, 682, 600, - 683, 684, 486, 487, 488, 489, 407, 710, 370, 603, - 519, 520, 0, 669, 0, 0, 0, 0, 0, 0, - 0, 0, 674, 675, 672, 793, 0, 733, 734, 0, - 0, 597, 598, 402, 0, 616, 410, 369, 503, 404, - 581, 434, 0, 609, 676, 610, 522, 523, 737, 742, - 738, 739, 741, 762, 493, 424, 430, 539, 444, 466, - 525, 580, 501, 530, 367, 570, 541, 472, 661, 689, + 324, 0, 0, 0, 0, 333, 771, 772, 773, 774, + 775, 0, 0, 334, 335, 336, 0, 0, 299, 300, + 327, 552, 328, 329, 330, 331, 0, 0, 591, 592, + 593, 616, 0, 594, 575, 641, 412, 342, 556, 583, + 782, 783, 0, 0, 0, 0, 0, 0, 0, 692, + 703, 739, 740, 0, 753, 754, 756, 758, 757, 760, + 549, 550, 768, 0, 0, 437, 358, 359, 496, 497, + 535, 0, 438, 439, 440, 444, 441, 442, 443, 762, + 763, 764, 761, 470, 534, 557, 543, 781, 483, 0, + 789, 631, 632, 790, 749, 748, 343, 0, 337, 338, + 706, 707, 0, 0, 537, 482, 638, 502, 0, 0, + 647, 681, 670, 759, 634, 0, 0, 1448, 0, 0, + 0, 0, 0, 0, 0, 395, 0, 0, 465, 685, + 666, 677, 667, 508, 652, 653, 654, 661, 407, 655, + 791, 657, 626, 658, 627, 659, 660, 0, 684, 633, + 545, 484, 0, 701, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 325, 326, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 718, 717, 716, - 715, 714, 713, 712, 711, 0, 0, 658, 384, 781, - 775, 782, 763, 766, 765, 740, 0, 341, 638, 464, - 518, 401, 705, 706, 0, 761, 284, 285, 286, 287, - 288, 289, 290, 291, 332, 292, 293, 294, 295, 296, - 297, 298, 303, 304, 305, 306, 307, 308, 309, 310, - 708, 301, 302, 311, 312, 313, 314, 315, 316, 317, - 318, 319, 320, 321, 322, 323, 324, 0, 0, 0, - 0, 333, 767, 768, 769, 770, 771, 0, 0, 334, - 335, 336, 0, 0, 299, 300, 327, 549, 328, 329, - 330, 331, 0, 0, 587, 588, 589, 612, 0, 590, - 572, 637, 411, 342, 553, 579, 778, 779, 0, 0, - 0, 0, 0, 0, 0, 688, 699, 735, 736, 0, - 749, 750, 752, 754, 753, 756, 546, 547, 764, 0, - 0, 435, 357, 358, 494, 495, 532, 0, 436, 437, - 438, 442, 439, 440, 441, 758, 759, 760, 757, 468, - 531, 554, 540, 777, 481, 0, 785, 627, 628, 786, - 745, 744, 343, 0, 337, 338, 702, 703, 0, 0, - 534, 480, 634, 500, 0, 0, 643, 677, 666, 755, - 630, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 394, 0, 0, 463, 681, 662, 673, 663, 506, - 648, 649, 650, 657, 406, 651, 787, 653, 622, 654, - 623, 655, 656, 0, 680, 629, 542, 482, 0, 697, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 268, 0, 0, 0, 2382, 0, 0, 365, 269, - 624, 751, 626, 625, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 368, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 543, 571, 0, 584, 0, 432, - 433, 0, 0, 0, 0, 0, 0, 0, 350, 550, - 568, 366, 537, 582, 371, 545, 561, 361, 499, 533, - 0, 0, 352, 566, 544, 477, 351, 0, 526, 391, - 408, 388, 497, 0, 0, 565, 595, 387, 585, 0, - 576, 354, 0, 575, 496, 469, 562, 567, 478, 471, - 0, 353, 564, 476, 470, 446, 398, 611, 447, 448, - 449, 450, 451, 452, 412, 512, 467, 513, 413, 484, - 483, 485, 414, 415, 416, 417, 418, 419, 420, 421, - 422, 423, 0, 0, 0, 0, 0, 606, 607, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 743, 0, 0, 748, 0, 578, - 0, 0, 0, 0, 0, 0, 548, 0, 0, 453, - 0, 0, 0, 596, 0, 529, 502, 789, 0, 0, - 527, 465, 563, 514, 569, 551, 577, 521, 515, 344, - 552, 390, 479, 362, 364, 776, 392, 395, 399, 400, - 490, 491, 508, 536, 555, 556, 557, 389, 376, 528, - 377, 409, 378, 345, 381, 380, 382, 538, 383, 347, - 509, 560, 0, 405, 524, 474, 348, 473, 510, 559, - 558, 363, 586, 593, 594, 685, 0, 599, 790, 791, - 792, 608, 0, 516, 359, 356, 0, 0, 0, 385, - 511, 372, 374, 375, 373, 505, 507, 613, 614, 615, - 617, 0, 618, 619, 0, 0, 0, 0, 620, 686, - 704, 670, 639, 601, 694, 636, 640, 641, 426, 427, - 428, 429, 707, 0, 0, 0, 592, 454, 455, 0, - 397, 396, 475, 349, 0, 0, 443, 425, 517, 355, - 393, 445, 431, 456, 457, 458, 459, 460, 0, 461, - 462, 403, 339, 340, 784, 386, 498, 709, 746, 747, - 631, 0, 695, 632, 642, 379, 667, 679, 678, 492, - 591, 0, 690, 693, 621, 783, 0, 687, 701, 788, - 700, 780, 504, 0, 535, 698, 645, 0, 691, 664, - 665, 0, 692, 660, 696, 0, 633, 0, 602, 605, - 635, 722, 723, 724, 346, 604, 726, 727, 728, 729, - 730, 731, 732, 725, 574, 668, 644, 671, 583, 647, - 646, 0, 0, 682, 600, 683, 684, 486, 487, 488, - 489, 407, 710, 370, 603, 519, 520, 0, 669, 0, - 0, 0, 0, 0, 0, 0, 0, 674, 675, 672, - 793, 0, 733, 734, 0, 0, 597, 598, 402, 0, - 616, 410, 369, 503, 404, 581, 434, 0, 609, 676, - 610, 522, 523, 737, 742, 738, 739, 741, 762, 493, - 424, 430, 539, 444, 466, 525, 580, 501, 530, 367, - 570, 541, 472, 661, 689, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 325, 326, 0, 0, + 0, 0, 0, 0, 0, 268, 0, 0, 0, 0, + 0, 0, 366, 269, 628, 755, 630, 629, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 369, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 546, 574, + 0, 588, 0, 434, 435, 0, 0, 0, 0, 0, + 0, 0, 350, 553, 571, 367, 540, 586, 372, 548, + 564, 362, 501, 536, 0, 0, 352, 569, 547, 479, + 351, 0, 529, 392, 409, 389, 499, 0, 0, 568, + 599, 388, 589, 0, 579, 354, 0, 578, 498, 471, + 565, 570, 480, 473, 0, 353, 567, 478, 472, 448, + 399, 615, 449, 450, 451, 452, 453, 454, 413, 514, + 469, 515, 414, 486, 485, 487, 415, 416, 417, 418, + 419, 420, 421, 422, 423, 424, 0, 0, 0, 0, + 0, 610, 611, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 747, 0, + 0, 752, 0, 582, 0, 0, 0, 0, 0, 0, + 551, 0, 0, 455, 0, 0, 0, 600, 0, 532, + 504, 793, 0, 0, 530, 467, 566, 516, 572, 554, + 580, 523, 517, 344, 555, 391, 481, 363, 365, 780, + 393, 396, 400, 401, 492, 493, 510, 539, 558, 559, + 560, 390, 377, 531, 378, 410, 379, 345, 382, 381, + 383, 541, 384, 347, 511, 563, 0, 406, 527, 476, + 348, 475, 512, 562, 561, 364, 590, 597, 598, 689, + 0, 603, 794, 795, 796, 612, 0, 518, 360, 357, + 0, 0, 0, 386, 513, 373, 375, 376, 374, 507, + 509, 617, 618, 619, 621, 0, 622, 623, 0, 0, + 0, 0, 624, 690, 708, 674, 643, 605, 698, 640, + 644, 645, 427, 428, 429, 430, 711, 0, 0, 0, + 596, 456, 457, 0, 398, 397, 477, 349, 0, 0, + 445, 426, 519, 355, 394, 447, 432, 458, 459, 460, + 461, 462, 0, 463, 464, 404, 339, 340, 788, 387, + 500, 713, 750, 751, 635, 0, 699, 636, 646, 380, + 671, 683, 682, 494, 595, 0, 694, 697, 625, 787, + 0, 691, 705, 792, 704, 784, 506, 0, 538, 702, + 649, 0, 695, 668, 669, 0, 696, 664, 700, 356, + 433, 524, 581, 0, 637, 0, 606, 609, 639, 726, + 727, 728, 346, 608, 730, 731, 732, 733, 734, 735, + 736, 729, 577, 672, 648, 675, 587, 651, 650, 0, + 0, 686, 604, 687, 688, 488, 489, 490, 491, 408, + 714, 371, 607, 521, 522, 0, 673, 0, 0, 0, + 0, 0, 0, 0, 0, 678, 679, 676, 797, 0, + 737, 738, 0, 0, 601, 602, 403, 0, 620, 411, + 370, 505, 405, 585, 436, 0, 613, 680, 614, 525, + 526, 741, 746, 742, 743, 745, 766, 495, 425, 431, + 542, 446, 468, 528, 584, 503, 533, 368, 573, 544, + 474, 665, 693, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 325, 326, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 718, 717, 716, 715, 714, 713, 712, 711, - 0, 0, 658, 384, 781, 775, 782, 763, 766, 765, - 740, 0, 341, 638, 464, 518, 401, 705, 706, 0, - 761, 284, 285, 286, 287, 288, 289, 290, 291, 332, - 292, 293, 294, 295, 296, 297, 298, 303, 304, 305, - 306, 307, 308, 309, 310, 708, 301, 302, 311, 312, - 313, 314, 315, 316, 317, 318, 319, 320, 321, 322, - 323, 324, 0, 0, 0, 0, 333, 767, 768, 769, - 770, 771, 0, 0, 334, 335, 336, 0, 0, 299, - 300, 327, 549, 328, 329, 330, 331, 0, 0, 587, - 588, 589, 612, 0, 590, 572, 637, 411, 342, 553, - 579, 778, 779, 0, 0, 0, 0, 0, 0, 0, - 688, 699, 735, 736, 0, 749, 750, 752, 754, 753, - 756, 546, 547, 764, 0, 0, 435, 357, 358, 494, - 495, 532, 0, 436, 437, 438, 442, 439, 440, 441, - 758, 759, 760, 757, 468, 531, 554, 540, 777, 481, - 0, 785, 627, 628, 786, 745, 744, 343, 0, 337, - 338, 702, 703, 0, 0, 534, 480, 634, 500, 0, - 0, 643, 677, 666, 755, 630, 0, 0, 1444, 0, - 0, 0, 0, 0, 0, 0, 394, 0, 0, 463, - 681, 662, 673, 663, 506, 648, 649, 650, 657, 406, - 651, 787, 653, 622, 654, 623, 655, 656, 0, 680, - 629, 542, 482, 0, 697, 0, 0, 0, 0, 0, + 722, 721, 720, 719, 718, 717, 716, 715, 0, 0, + 662, 385, 785, 779, 786, 767, 770, 769, 2601, 0, + 341, 642, 466, 520, 402, 709, 710, 0, 765, 284, + 285, 286, 287, 288, 289, 290, 291, 332, 292, 293, + 294, 295, 296, 297, 298, 303, 304, 305, 306, 307, + 308, 309, 310, 712, 301, 302, 311, 312, 313, 314, + 315, 316, 317, 318, 319, 320, 321, 322, 323, 324, + 0, 0, 0, 0, 333, 771, 772, 773, 774, 775, + 0, 0, 334, 335, 336, 0, 0, 299, 300, 327, + 552, 328, 329, 330, 331, 0, 0, 591, 592, 593, + 616, 0, 594, 575, 641, 412, 342, 556, 583, 782, + 783, 0, 0, 0, 0, 0, 0, 0, 692, 703, + 739, 740, 0, 753, 754, 756, 758, 757, 760, 549, + 550, 768, 0, 0, 437, 358, 359, 496, 497, 535, + 0, 438, 439, 440, 444, 441, 442, 443, 762, 763, + 764, 761, 470, 534, 557, 543, 781, 483, 0, 789, + 631, 632, 790, 749, 748, 343, 0, 337, 338, 706, + 707, 0, 0, 537, 482, 638, 502, 0, 0, 647, + 681, 670, 759, 634, 0, 2537, 0, 0, 0, 0, + 0, 0, 0, 0, 395, 0, 0, 465, 685, 666, + 677, 667, 508, 652, 653, 654, 661, 407, 655, 791, + 657, 626, 658, 627, 659, 660, 0, 684, 633, 545, + 484, 0, 701, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 268, 0, 0, 0, - 0, 0, 0, 365, 269, 624, 751, 626, 625, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 368, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 543, - 571, 0, 584, 0, 432, 433, 0, 0, 0, 0, - 0, 0, 0, 350, 550, 568, 366, 537, 582, 371, - 545, 561, 361, 499, 533, 0, 0, 352, 566, 544, - 477, 351, 0, 526, 391, 408, 388, 497, 0, 0, - 565, 595, 387, 585, 0, 576, 354, 0, 575, 496, - 469, 562, 567, 478, 471, 0, 353, 564, 476, 470, - 446, 398, 611, 447, 448, 449, 450, 451, 452, 412, - 512, 467, 513, 413, 484, 483, 485, 414, 415, 416, - 417, 418, 419, 420, 421, 422, 423, 0, 0, 0, - 0, 0, 606, 607, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 743, - 0, 0, 748, 0, 578, 0, 0, 0, 0, 0, - 0, 548, 0, 0, 453, 0, 0, 0, 596, 0, - 529, 502, 789, 0, 0, 527, 465, 563, 514, 569, - 551, 577, 521, 515, 344, 552, 390, 479, 362, 364, - 776, 392, 395, 399, 400, 490, 491, 508, 536, 555, - 556, 557, 389, 376, 528, 377, 409, 378, 345, 381, - 380, 382, 538, 383, 347, 509, 560, 0, 405, 524, - 474, 348, 473, 510, 559, 558, 363, 586, 593, 594, - 685, 0, 599, 790, 791, 792, 608, 0, 516, 359, - 356, 0, 0, 0, 385, 511, 372, 374, 375, 373, - 505, 507, 613, 614, 615, 617, 0, 618, 619, 0, - 0, 0, 0, 620, 686, 704, 670, 639, 601, 694, - 636, 640, 641, 426, 427, 428, 429, 707, 0, 0, - 0, 592, 454, 455, 0, 397, 396, 475, 349, 0, - 0, 443, 425, 517, 355, 393, 445, 431, 456, 457, - 458, 459, 460, 0, 461, 462, 403, 339, 340, 784, - 386, 498, 709, 746, 747, 631, 0, 695, 632, 642, - 379, 667, 679, 678, 492, 591, 0, 690, 693, 621, - 783, 0, 687, 701, 788, 700, 780, 504, 0, 535, - 698, 645, 0, 691, 664, 665, 0, 692, 660, 696, - 0, 633, 0, 602, 605, 635, 722, 723, 724, 346, - 604, 726, 727, 728, 729, 730, 731, 732, 725, 574, - 668, 644, 671, 583, 647, 646, 0, 0, 682, 600, - 683, 684, 486, 487, 488, 489, 407, 710, 370, 603, - 519, 520, 0, 669, 0, 0, 0, 0, 0, 0, - 0, 0, 674, 675, 672, 793, 0, 733, 734, 0, - 0, 597, 598, 402, 0, 616, 410, 369, 503, 404, - 581, 434, 0, 609, 676, 610, 522, 523, 737, 742, - 738, 739, 741, 762, 493, 424, 430, 539, 444, 466, - 525, 580, 501, 530, 367, 570, 541, 472, 661, 689, + 0, 0, 0, 0, 268, 0, 0, 0, 0, 0, + 0, 366, 269, 628, 755, 630, 629, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 369, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 546, 574, 0, + 588, 0, 434, 435, 0, 0, 0, 0, 0, 0, + 0, 350, 553, 571, 367, 540, 586, 372, 548, 564, + 362, 501, 536, 0, 0, 352, 569, 547, 479, 351, + 0, 529, 392, 409, 389, 499, 0, 0, 568, 599, + 388, 589, 0, 579, 354, 0, 578, 498, 471, 565, + 570, 480, 473, 0, 353, 567, 478, 472, 448, 399, + 615, 449, 450, 451, 452, 453, 454, 413, 514, 469, + 515, 414, 486, 485, 487, 415, 416, 417, 418, 419, + 420, 421, 422, 423, 424, 0, 0, 0, 0, 0, + 610, 611, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 747, 0, 0, + 752, 0, 582, 0, 0, 0, 0, 0, 0, 551, + 0, 0, 455, 0, 0, 0, 600, 0, 532, 504, + 793, 0, 0, 530, 467, 566, 516, 572, 554, 580, + 523, 517, 344, 555, 391, 481, 363, 365, 780, 393, + 396, 400, 401, 492, 493, 510, 539, 558, 559, 560, + 390, 377, 531, 378, 410, 379, 345, 382, 381, 383, + 541, 384, 347, 511, 563, 0, 406, 527, 476, 348, + 475, 512, 562, 561, 364, 590, 597, 598, 689, 0, + 603, 794, 795, 796, 612, 0, 518, 360, 357, 0, + 0, 0, 386, 513, 373, 375, 376, 374, 507, 509, + 617, 618, 619, 621, 0, 622, 623, 0, 0, 0, + 0, 624, 690, 708, 674, 643, 605, 698, 640, 644, + 645, 427, 428, 429, 430, 711, 0, 0, 0, 596, + 456, 457, 0, 398, 397, 477, 349, 0, 0, 445, + 426, 519, 355, 394, 447, 432, 458, 459, 460, 461, + 462, 0, 463, 464, 404, 339, 340, 788, 387, 500, + 713, 750, 751, 635, 0, 699, 636, 646, 380, 671, + 683, 682, 494, 595, 0, 694, 697, 625, 787, 0, + 691, 705, 792, 704, 784, 506, 0, 538, 702, 649, + 0, 695, 668, 669, 0, 696, 664, 700, 356, 433, + 524, 581, 0, 637, 0, 606, 609, 639, 726, 727, + 728, 346, 608, 730, 731, 732, 733, 734, 735, 736, + 729, 577, 672, 648, 675, 587, 651, 650, 0, 0, + 686, 604, 687, 688, 488, 489, 490, 491, 408, 714, + 371, 607, 521, 522, 0, 673, 0, 0, 0, 0, + 0, 0, 0, 0, 678, 679, 676, 797, 0, 737, + 738, 0, 0, 601, 602, 403, 0, 620, 411, 370, + 505, 405, 585, 436, 0, 613, 680, 614, 525, 526, + 741, 746, 742, 743, 745, 766, 495, 425, 431, 542, + 446, 468, 528, 584, 503, 533, 368, 573, 544, 474, + 665, 693, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 325, 326, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 722, + 721, 720, 719, 718, 717, 716, 715, 0, 0, 662, + 385, 785, 779, 786, 767, 770, 769, 744, 0, 341, + 642, 466, 520, 402, 709, 710, 0, 765, 284, 285, + 286, 287, 288, 289, 290, 291, 332, 292, 293, 294, + 295, 296, 297, 298, 303, 304, 305, 306, 307, 308, + 309, 310, 712, 301, 302, 311, 312, 313, 314, 315, + 316, 317, 318, 319, 320, 321, 322, 323, 324, 0, + 0, 0, 0, 333, 771, 772, 773, 774, 775, 0, + 0, 334, 335, 336, 0, 0, 299, 300, 327, 552, + 328, 329, 330, 331, 0, 0, 591, 592, 593, 616, + 0, 594, 575, 641, 412, 342, 556, 583, 782, 783, + 0, 0, 0, 0, 0, 0, 0, 692, 703, 739, + 740, 0, 753, 754, 756, 758, 757, 760, 549, 550, + 768, 0, 0, 437, 358, 359, 496, 497, 535, 0, + 438, 439, 440, 444, 441, 442, 443, 762, 763, 764, + 761, 470, 534, 557, 543, 781, 483, 0, 789, 631, + 632, 790, 749, 748, 343, 0, 337, 338, 706, 707, + 0, 0, 537, 482, 638, 502, 0, 0, 647, 681, + 670, 759, 634, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 395, 0, 0, 465, 685, 666, 677, + 667, 508, 652, 653, 654, 661, 407, 655, 791, 657, + 626, 658, 627, 659, 660, 0, 684, 633, 545, 484, + 0, 701, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 268, 0, 0, 1870, 0, 0, 0, + 366, 269, 628, 755, 630, 629, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 369, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 546, 574, 0, 588, + 0, 434, 435, 0, 0, 0, 0, 0, 0, 0, + 350, 553, 571, 367, 540, 586, 372, 548, 564, 362, + 501, 536, 0, 0, 352, 569, 547, 479, 351, 0, + 529, 392, 409, 389, 499, 0, 0, 568, 599, 388, + 589, 0, 579, 354, 0, 578, 498, 471, 565, 570, + 480, 473, 0, 353, 567, 478, 472, 448, 399, 615, + 449, 450, 451, 452, 453, 454, 413, 514, 469, 515, + 414, 486, 485, 487, 415, 416, 417, 418, 419, 420, + 421, 422, 423, 424, 0, 0, 0, 0, 0, 610, + 611, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 747, 0, 0, 752, + 0, 582, 0, 0, 0, 0, 0, 0, 551, 0, + 0, 455, 0, 0, 0, 600, 0, 532, 504, 793, + 0, 0, 530, 467, 566, 516, 572, 554, 580, 2071, + 517, 344, 555, 391, 481, 363, 365, 780, 393, 396, + 400, 401, 492, 493, 510, 539, 558, 559, 560, 390, + 377, 531, 378, 410, 379, 345, 382, 381, 383, 541, + 384, 347, 511, 563, 0, 406, 527, 476, 348, 475, + 512, 562, 561, 364, 590, 597, 598, 689, 0, 603, + 794, 795, 796, 612, 0, 518, 360, 357, 0, 0, + 0, 386, 513, 373, 375, 376, 374, 507, 509, 617, + 618, 619, 621, 0, 622, 623, 0, 0, 0, 0, + 624, 690, 708, 674, 643, 605, 698, 640, 644, 645, + 427, 428, 429, 430, 711, 0, 0, 0, 596, 456, + 457, 0, 398, 397, 477, 349, 0, 0, 445, 426, + 519, 355, 394, 447, 432, 458, 459, 460, 461, 462, + 0, 463, 464, 404, 339, 340, 788, 387, 500, 713, + 750, 751, 635, 0, 699, 636, 646, 380, 671, 683, + 682, 494, 595, 0, 694, 697, 625, 787, 0, 691, + 705, 792, 704, 784, 506, 0, 538, 702, 649, 0, + 695, 668, 669, 0, 696, 664, 700, 356, 433, 524, + 581, 0, 637, 0, 606, 609, 639, 726, 727, 728, + 346, 608, 730, 731, 732, 733, 734, 735, 736, 729, + 577, 672, 648, 675, 587, 651, 650, 0, 0, 686, + 604, 687, 688, 488, 489, 490, 491, 408, 714, 371, + 607, 521, 522, 0, 673, 0, 0, 0, 0, 0, + 0, 0, 0, 678, 679, 676, 797, 0, 737, 738, + 0, 0, 601, 602, 403, 0, 620, 411, 370, 505, + 405, 585, 436, 0, 613, 680, 614, 525, 526, 741, + 746, 742, 743, 745, 766, 495, 425, 431, 542, 446, + 468, 528, 584, 503, 533, 368, 573, 544, 474, 665, + 693, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 325, 326, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 722, 721, + 720, 719, 718, 717, 716, 715, 0, 0, 662, 385, + 785, 779, 786, 767, 770, 769, 744, 0, 341, 642, + 466, 520, 402, 709, 710, 0, 765, 284, 285, 286, + 287, 288, 289, 290, 291, 332, 292, 293, 294, 295, + 296, 297, 298, 303, 304, 305, 306, 307, 308, 309, + 310, 712, 301, 302, 311, 312, 313, 314, 315, 316, + 317, 318, 319, 320, 321, 322, 323, 324, 0, 0, + 0, 0, 333, 771, 772, 773, 774, 775, 0, 0, + 334, 335, 336, 0, 0, 299, 300, 327, 552, 328, + 329, 330, 331, 0, 0, 591, 592, 593, 616, 0, + 594, 575, 641, 412, 342, 556, 583, 782, 783, 0, + 0, 0, 0, 0, 0, 0, 692, 703, 739, 740, + 0, 753, 754, 756, 758, 757, 760, 549, 550, 768, + 0, 0, 437, 358, 359, 496, 497, 535, 0, 438, + 439, 440, 444, 441, 442, 443, 762, 763, 764, 761, + 470, 534, 557, 543, 781, 483, 0, 789, 631, 632, + 790, 749, 748, 343, 0, 337, 338, 706, 707, 0, + 0, 537, 482, 638, 502, 0, 0, 647, 681, 670, + 759, 634, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 395, 0, 0, 465, 685, 666, 677, 667, + 508, 652, 653, 654, 661, 407, 655, 791, 657, 626, + 658, 627, 659, 660, 0, 684, 633, 545, 484, 0, + 701, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 268, 0, 0, 0, 0, 0, 0, 366, + 269, 628, 755, 630, 629, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 369, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 546, 574, 0, 588, 0, + 434, 435, 0, 0, 0, 0, 0, 0, 0, 350, + 553, 571, 367, 540, 586, 372, 548, 564, 362, 501, + 536, 0, 0, 352, 569, 547, 479, 351, 0, 529, + 392, 409, 389, 499, 0, 0, 568, 599, 388, 589, + 0, 579, 354, 0, 578, 498, 471, 565, 570, 480, + 473, 0, 353, 567, 478, 472, 448, 399, 615, 449, + 450, 451, 452, 453, 454, 413, 514, 469, 515, 414, + 486, 485, 487, 415, 416, 417, 418, 419, 420, 421, + 422, 423, 424, 0, 0, 0, 0, 0, 610, 611, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 747, 0, 0, 752, 0, + 582, 0, 0, 1913, 0, 0, 0, 551, 0, 0, + 455, 0, 0, 0, 600, 0, 532, 504, 793, 0, + 0, 530, 467, 566, 516, 572, 554, 580, 523, 517, + 344, 555, 391, 481, 363, 365, 780, 393, 396, 400, + 401, 492, 493, 510, 539, 558, 559, 560, 390, 377, + 531, 378, 410, 379, 345, 382, 381, 383, 541, 384, + 347, 511, 563, 0, 406, 527, 476, 348, 475, 512, + 562, 561, 364, 590, 597, 598, 689, 0, 603, 794, + 795, 796, 612, 0, 518, 360, 357, 0, 0, 0, + 386, 513, 373, 375, 376, 374, 507, 509, 617, 618, + 619, 621, 0, 622, 623, 0, 0, 0, 0, 624, + 690, 708, 674, 643, 605, 698, 640, 644, 645, 427, + 428, 429, 430, 711, 0, 0, 0, 596, 456, 457, + 0, 398, 397, 477, 349, 0, 0, 445, 426, 519, + 355, 394, 447, 432, 458, 459, 460, 461, 462, 0, + 463, 464, 404, 339, 340, 788, 387, 500, 713, 750, + 751, 635, 0, 699, 636, 646, 380, 671, 683, 682, + 494, 595, 0, 694, 697, 625, 787, 0, 691, 705, + 792, 704, 784, 506, 0, 538, 702, 649, 0, 695, + 668, 669, 0, 696, 664, 700, 356, 433, 524, 581, + 0, 637, 0, 606, 609, 639, 726, 727, 728, 346, + 608, 730, 731, 732, 733, 734, 735, 736, 729, 577, + 672, 648, 675, 587, 651, 650, 0, 0, 686, 604, + 687, 688, 488, 489, 490, 491, 408, 714, 371, 607, + 521, 522, 0, 673, 0, 0, 0, 0, 0, 0, + 0, 0, 678, 679, 676, 797, 0, 737, 738, 0, + 0, 601, 602, 403, 0, 620, 411, 370, 505, 405, + 585, 436, 0, 613, 680, 614, 525, 526, 741, 746, + 742, 743, 745, 766, 495, 425, 431, 542, 446, 468, + 528, 584, 503, 533, 368, 573, 544, 474, 665, 693, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 325, 326, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 718, 717, 716, - 715, 714, 713, 712, 711, 0, 0, 658, 384, 781, - 775, 782, 763, 766, 765, 2597, 0, 341, 638, 464, - 518, 401, 705, 706, 0, 761, 284, 285, 286, 287, + 0, 0, 0, 0, 0, 0, 0, 722, 721, 720, + 719, 718, 717, 716, 715, 0, 0, 662, 385, 785, + 779, 786, 767, 770, 769, 744, 0, 341, 642, 466, + 520, 402, 709, 710, 0, 765, 284, 285, 286, 287, 288, 289, 290, 291, 332, 292, 293, 294, 295, 296, 297, 298, 303, 304, 305, 306, 307, 308, 309, 310, - 708, 301, 302, 311, 312, 313, 314, 315, 316, 317, + 712, 301, 302, 311, 312, 313, 314, 315, 316, 317, 318, 319, 320, 321, 322, 323, 324, 0, 0, 0, - 0, 333, 767, 768, 769, 770, 771, 0, 0, 334, - 335, 336, 0, 0, 299, 300, 327, 549, 328, 329, - 330, 331, 0, 0, 587, 588, 589, 612, 0, 590, - 572, 637, 411, 342, 553, 579, 778, 779, 0, 0, - 0, 0, 0, 0, 0, 688, 699, 735, 736, 0, - 749, 750, 752, 754, 753, 756, 546, 547, 764, 0, - 0, 435, 357, 358, 494, 495, 532, 0, 436, 437, - 438, 442, 439, 440, 441, 758, 759, 760, 757, 468, - 531, 554, 540, 777, 481, 0, 785, 627, 628, 786, - 745, 744, 343, 0, 337, 338, 702, 703, 0, 0, - 534, 480, 634, 500, 0, 0, 643, 677, 666, 755, - 630, 0, 2533, 0, 0, 0, 0, 0, 0, 0, - 0, 394, 0, 0, 463, 681, 662, 673, 663, 506, - 648, 649, 650, 657, 406, 651, 787, 653, 622, 654, - 623, 655, 656, 0, 680, 629, 542, 482, 0, 697, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 268, 0, 0, 0, 0, 0, 0, 365, 269, - 624, 751, 626, 625, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 368, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 543, 571, 0, 584, 0, 432, - 433, 0, 0, 0, 0, 0, 0, 0, 350, 550, - 568, 366, 537, 582, 371, 545, 561, 361, 499, 533, - 0, 0, 352, 566, 544, 477, 351, 0, 526, 391, - 408, 388, 497, 0, 0, 565, 595, 387, 585, 0, - 576, 354, 0, 575, 496, 469, 562, 567, 478, 471, - 0, 353, 564, 476, 470, 446, 398, 611, 447, 448, - 449, 450, 451, 452, 412, 512, 467, 513, 413, 484, - 483, 485, 414, 415, 416, 417, 418, 419, 420, 421, - 422, 423, 0, 0, 0, 0, 0, 606, 607, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 743, 0, 0, 748, 0, 578, - 0, 0, 0, 0, 0, 0, 548, 0, 0, 453, - 0, 0, 0, 596, 0, 529, 502, 789, 0, 0, - 527, 465, 563, 514, 569, 551, 577, 521, 515, 344, - 552, 390, 479, 362, 364, 776, 392, 395, 399, 400, - 490, 491, 508, 536, 555, 556, 557, 389, 376, 528, - 377, 409, 378, 345, 381, 380, 382, 538, 383, 347, - 509, 560, 0, 405, 524, 474, 348, 473, 510, 559, - 558, 363, 586, 593, 594, 685, 0, 599, 790, 791, - 792, 608, 0, 516, 359, 356, 0, 0, 0, 385, - 511, 372, 374, 375, 373, 505, 507, 613, 614, 615, - 617, 0, 618, 619, 0, 0, 0, 0, 620, 686, - 704, 670, 639, 601, 694, 636, 640, 641, 426, 427, - 428, 429, 707, 0, 0, 0, 592, 454, 455, 0, - 397, 396, 475, 349, 0, 0, 443, 425, 517, 355, - 393, 445, 431, 456, 457, 458, 459, 460, 0, 461, - 462, 403, 339, 340, 784, 386, 498, 709, 746, 747, - 631, 0, 695, 632, 642, 379, 667, 679, 678, 492, - 591, 0, 690, 693, 621, 783, 0, 687, 701, 788, - 700, 780, 504, 0, 535, 698, 645, 0, 691, 664, - 665, 0, 692, 660, 696, 0, 633, 0, 602, 605, - 635, 722, 723, 724, 346, 604, 726, 727, 728, 729, - 730, 731, 732, 725, 574, 668, 644, 671, 583, 647, - 646, 0, 0, 682, 600, 683, 684, 486, 487, 488, - 489, 407, 710, 370, 603, 519, 520, 0, 669, 0, - 0, 0, 0, 0, 0, 0, 0, 674, 675, 672, - 793, 0, 733, 734, 0, 0, 597, 598, 402, 0, - 616, 410, 369, 503, 404, 581, 434, 0, 609, 676, - 610, 522, 523, 737, 742, 738, 739, 741, 762, 493, - 424, 430, 539, 444, 466, 525, 580, 501, 530, 367, - 570, 541, 472, 661, 689, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 325, 326, 0, 0, + 0, 333, 771, 772, 773, 774, 775, 0, 0, 334, + 335, 336, 0, 0, 299, 300, 327, 552, 328, 329, + 330, 331, 0, 0, 591, 592, 593, 616, 0, 594, + 575, 641, 412, 342, 556, 583, 782, 783, 0, 0, + 0, 0, 0, 0, 0, 692, 703, 739, 740, 0, + 753, 754, 756, 758, 757, 760, 549, 550, 768, 0, + 0, 437, 358, 359, 496, 497, 535, 0, 438, 439, + 440, 444, 441, 442, 443, 762, 763, 764, 761, 470, + 534, 557, 543, 781, 483, 0, 789, 631, 632, 790, + 749, 748, 343, 0, 337, 338, 706, 707, 0, 0, + 537, 482, 638, 502, 0, 0, 647, 681, 670, 759, + 634, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 1209, 395, 0, 0, 465, 685, 666, 677, 667, 508, + 652, 653, 654, 661, 407, 655, 791, 657, 626, 658, + 627, 659, 660, 0, 684, 633, 545, 484, 0, 701, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 268, 0, 0, 0, 0, 0, 0, 366, 269, + 628, 755, 630, 629, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 369, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 546, 574, 0, 588, 0, 434, + 435, 0, 0, 0, 0, 0, 0, 0, 350, 553, + 571, 367, 540, 586, 372, 548, 564, 362, 501, 536, + 0, 0, 352, 569, 547, 479, 351, 0, 529, 392, + 409, 389, 499, 0, 0, 568, 599, 388, 589, 0, + 579, 354, 0, 578, 498, 471, 565, 570, 480, 473, + 0, 353, 567, 478, 472, 448, 399, 615, 449, 450, + 451, 452, 453, 454, 413, 514, 469, 515, 414, 486, + 485, 487, 415, 416, 417, 418, 419, 420, 421, 422, + 423, 424, 0, 0, 0, 0, 0, 610, 611, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 747, 0, 0, 752, 0, 582, + 0, 0, 0, 0, 0, 0, 551, 0, 0, 455, + 0, 0, 0, 600, 0, 532, 504, 793, 0, 0, + 530, 467, 566, 516, 572, 554, 580, 523, 517, 344, + 555, 391, 481, 363, 365, 780, 393, 396, 400, 401, + 492, 493, 510, 539, 558, 559, 560, 390, 377, 531, + 378, 410, 379, 345, 382, 381, 383, 541, 384, 347, + 511, 563, 0, 406, 527, 476, 348, 475, 512, 562, + 561, 364, 590, 597, 598, 689, 0, 603, 794, 795, + 796, 612, 0, 518, 360, 357, 0, 0, 0, 386, + 513, 373, 375, 376, 374, 507, 509, 617, 618, 619, + 621, 0, 622, 623, 0, 0, 0, 0, 624, 690, + 708, 674, 643, 605, 698, 640, 644, 645, 427, 428, + 429, 430, 711, 0, 0, 0, 596, 456, 457, 0, + 398, 397, 477, 349, 0, 0, 445, 426, 519, 355, + 394, 447, 432, 458, 459, 460, 461, 462, 0, 463, + 464, 404, 339, 340, 788, 387, 500, 713, 750, 751, + 635, 0, 699, 636, 646, 380, 671, 683, 682, 494, + 595, 0, 694, 697, 625, 787, 0, 691, 705, 792, + 704, 784, 506, 0, 538, 702, 649, 0, 695, 668, + 669, 0, 696, 664, 700, 356, 433, 524, 581, 0, + 637, 0, 606, 609, 639, 726, 727, 728, 346, 608, + 730, 731, 732, 733, 734, 735, 736, 729, 577, 672, + 648, 675, 587, 651, 650, 0, 0, 686, 604, 687, + 688, 488, 489, 490, 491, 408, 714, 371, 607, 521, + 522, 0, 673, 0, 0, 0, 0, 0, 0, 0, + 0, 678, 679, 676, 797, 0, 737, 738, 0, 0, + 601, 602, 403, 0, 620, 411, 370, 505, 405, 585, + 436, 0, 613, 680, 614, 525, 526, 741, 746, 742, + 743, 745, 766, 495, 425, 431, 542, 446, 468, 528, + 584, 503, 533, 368, 573, 544, 474, 665, 693, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 718, 717, 716, 715, 714, 713, 712, 711, - 0, 0, 658, 384, 781, 775, 782, 763, 766, 765, - 740, 0, 341, 638, 464, 518, 401, 705, 706, 0, - 761, 284, 285, 286, 287, 288, 289, 290, 291, 332, - 292, 293, 294, 295, 296, 297, 298, 303, 304, 305, - 306, 307, 308, 309, 310, 708, 301, 302, 311, 312, - 313, 314, 315, 316, 317, 318, 319, 320, 321, 322, - 323, 324, 0, 0, 0, 0, 333, 767, 768, 769, - 770, 771, 0, 0, 334, 335, 336, 0, 0, 299, - 300, 327, 549, 328, 329, 330, 331, 0, 0, 587, - 588, 589, 612, 0, 590, 572, 637, 411, 342, 553, - 579, 778, 779, 0, 0, 0, 0, 0, 0, 0, - 688, 699, 735, 736, 0, 749, 750, 752, 754, 753, - 756, 546, 547, 764, 0, 0, 435, 357, 358, 494, - 495, 532, 0, 436, 437, 438, 442, 439, 440, 441, - 758, 759, 760, 757, 468, 531, 554, 540, 777, 481, - 0, 785, 627, 628, 786, 745, 744, 343, 0, 337, - 338, 702, 703, 0, 0, 534, 480, 634, 500, 0, - 0, 643, 677, 666, 755, 630, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 394, 0, 0, 463, - 681, 662, 673, 663, 506, 648, 649, 650, 657, 406, - 651, 787, 653, 622, 654, 623, 655, 656, 0, 680, - 629, 542, 482, 0, 697, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 268, 0, 0, 1866, - 0, 0, 0, 365, 269, 624, 751, 626, 625, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 368, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 543, - 571, 0, 584, 0, 432, 433, 0, 0, 0, 0, - 0, 0, 0, 350, 550, 568, 366, 537, 582, 371, - 545, 561, 361, 499, 533, 0, 0, 352, 566, 544, - 477, 351, 0, 526, 391, 408, 388, 497, 0, 0, - 565, 595, 387, 585, 0, 576, 354, 0, 575, 496, - 469, 562, 567, 478, 471, 0, 353, 564, 476, 470, - 446, 398, 611, 447, 448, 449, 450, 451, 452, 412, - 512, 467, 513, 413, 484, 483, 485, 414, 415, 416, - 417, 418, 419, 420, 421, 422, 423, 0, 0, 0, - 0, 0, 606, 607, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 743, - 0, 0, 748, 0, 578, 0, 0, 0, 0, 0, - 0, 548, 0, 0, 453, 0, 0, 0, 596, 0, - 529, 502, 789, 0, 0, 527, 465, 563, 514, 569, - 551, 577, 2067, 515, 344, 552, 390, 479, 362, 364, - 776, 392, 395, 399, 400, 490, 491, 508, 536, 555, - 556, 557, 389, 376, 528, 377, 409, 378, 345, 381, - 380, 382, 538, 383, 347, 509, 560, 0, 405, 524, - 474, 348, 473, 510, 559, 558, 363, 586, 593, 594, - 685, 0, 599, 790, 791, 792, 608, 0, 516, 359, - 356, 0, 0, 0, 385, 511, 372, 374, 375, 373, - 505, 507, 613, 614, 615, 617, 0, 618, 619, 0, - 0, 0, 0, 620, 686, 704, 670, 639, 601, 694, - 636, 640, 641, 426, 427, 428, 429, 707, 0, 0, - 0, 592, 454, 455, 0, 397, 396, 475, 349, 0, - 0, 443, 425, 517, 355, 393, 445, 431, 456, 457, - 458, 459, 460, 0, 461, 462, 403, 339, 340, 784, - 386, 498, 709, 746, 747, 631, 0, 695, 632, 642, - 379, 667, 679, 678, 492, 591, 0, 690, 693, 621, - 783, 0, 687, 701, 788, 700, 780, 504, 0, 535, - 698, 645, 0, 691, 664, 665, 0, 692, 660, 696, - 0, 633, 0, 602, 605, 635, 722, 723, 724, 346, - 604, 726, 727, 728, 729, 730, 731, 732, 725, 574, - 668, 644, 671, 583, 647, 646, 0, 0, 682, 600, - 683, 684, 486, 487, 488, 489, 407, 710, 370, 603, - 519, 520, 0, 669, 0, 0, 0, 0, 0, 0, - 0, 0, 674, 675, 672, 793, 0, 733, 734, 0, - 0, 597, 598, 402, 0, 616, 410, 369, 503, 404, - 581, 434, 0, 609, 676, 610, 522, 523, 737, 742, - 738, 739, 741, 762, 493, 424, 430, 539, 444, 466, - 525, 580, 501, 530, 367, 570, 541, 472, 661, 689, + 325, 326, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 722, 721, 720, 719, + 718, 717, 716, 715, 0, 0, 662, 385, 785, 779, + 786, 767, 770, 769, 744, 0, 341, 642, 466, 520, + 402, 709, 710, 0, 765, 284, 285, 286, 287, 288, + 289, 290, 291, 332, 292, 293, 294, 295, 296, 297, + 298, 303, 304, 305, 306, 307, 308, 309, 310, 712, + 301, 302, 311, 312, 313, 314, 315, 316, 317, 318, + 319, 320, 321, 322, 323, 324, 0, 0, 0, 0, + 333, 771, 772, 773, 774, 775, 0, 0, 334, 335, + 336, 0, 0, 299, 300, 327, 552, 328, 329, 330, + 331, 0, 0, 591, 592, 593, 616, 0, 594, 575, + 641, 412, 342, 556, 583, 782, 783, 0, 0, 0, + 0, 0, 0, 0, 692, 703, 739, 740, 0, 753, + 754, 756, 758, 757, 760, 549, 550, 768, 0, 0, + 437, 358, 359, 496, 497, 535, 0, 438, 439, 440, + 444, 441, 442, 443, 762, 763, 764, 761, 470, 534, + 557, 543, 781, 483, 0, 789, 631, 632, 790, 749, + 748, 343, 0, 337, 338, 706, 707, 0, 0, 537, + 482, 638, 502, 0, 0, 647, 681, 670, 759, 634, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 395, 0, 0, 465, 685, 666, 677, 667, 508, 652, + 653, 654, 661, 407, 655, 791, 657, 626, 658, 627, + 659, 660, 0, 684, 633, 545, 484, 0, 701, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 268, 0, 0, 0, 0, 0, 0, 366, 269, 628, + 755, 630, 629, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 369, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 546, 574, 0, 588, 0, 434, 435, + 0, 0, 0, 0, 0, 0, 0, 350, 553, 571, + 367, 540, 586, 372, 548, 564, 362, 501, 536, 0, + 0, 352, 569, 547, 479, 351, 0, 529, 392, 409, + 389, 499, 0, 0, 568, 599, 388, 589, 0, 579, + 354, 0, 578, 498, 471, 565, 570, 480, 473, 0, + 353, 567, 478, 472, 448, 399, 615, 449, 450, 451, + 452, 453, 454, 413, 514, 469, 515, 414, 486, 485, + 487, 415, 416, 417, 418, 419, 420, 421, 422, 423, + 424, 0, 0, 0, 0, 0, 610, 611, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 747, 0, 816, 752, 0, 582, 0, + 0, 0, 0, 0, 0, 551, 0, 0, 455, 0, + 0, 0, 600, 0, 532, 504, 793, 0, 0, 530, + 467, 566, 516, 572, 554, 580, 523, 517, 344, 555, + 391, 481, 363, 365, 780, 393, 396, 400, 401, 492, + 493, 510, 539, 558, 559, 560, 390, 377, 531, 378, + 410, 379, 345, 382, 381, 383, 541, 384, 347, 511, + 563, 0, 406, 527, 476, 348, 475, 512, 562, 561, + 364, 590, 597, 598, 689, 0, 603, 794, 795, 796, + 612, 0, 518, 360, 357, 0, 0, 0, 386, 513, + 373, 375, 376, 374, 507, 509, 617, 618, 619, 621, + 0, 622, 623, 0, 0, 0, 0, 624, 690, 708, + 674, 643, 605, 698, 640, 644, 645, 427, 428, 429, + 430, 711, 0, 0, 0, 596, 456, 457, 0, 398, + 397, 477, 349, 0, 0, 445, 426, 519, 355, 394, + 447, 432, 458, 459, 460, 461, 462, 0, 463, 464, + 404, 339, 340, 788, 387, 500, 713, 750, 751, 635, + 0, 699, 636, 646, 380, 671, 683, 682, 494, 595, + 0, 694, 697, 625, 787, 0, 691, 705, 792, 704, + 784, 506, 0, 538, 702, 649, 0, 695, 668, 669, + 0, 696, 664, 700, 356, 433, 524, 581, 0, 637, + 0, 606, 609, 639, 726, 727, 728, 346, 608, 730, + 731, 732, 733, 734, 735, 736, 729, 577, 672, 648, + 675, 587, 651, 650, 0, 0, 686, 604, 687, 688, + 488, 489, 490, 491, 408, 714, 371, 607, 521, 522, + 0, 673, 0, 0, 0, 0, 0, 0, 0, 0, + 678, 679, 676, 797, 0, 737, 738, 0, 0, 601, + 602, 403, 0, 620, 411, 370, 505, 405, 585, 436, + 0, 613, 680, 614, 525, 526, 741, 746, 742, 743, + 745, 766, 495, 425, 431, 542, 446, 468, 528, 584, + 503, 533, 368, 573, 544, 474, 665, 693, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 325, + 326, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 722, 721, 720, 719, 718, + 717, 716, 715, 0, 0, 662, 385, 785, 779, 786, + 767, 770, 769, 744, 0, 341, 642, 466, 520, 402, + 709, 710, 0, 765, 284, 285, 286, 287, 288, 289, + 290, 291, 332, 292, 293, 294, 295, 296, 297, 298, + 303, 304, 305, 306, 307, 308, 309, 310, 712, 301, + 302, 311, 312, 313, 314, 315, 316, 317, 318, 319, + 320, 321, 322, 323, 324, 0, 0, 0, 0, 333, + 771, 772, 773, 774, 775, 0, 0, 334, 335, 336, + 0, 0, 299, 300, 327, 552, 328, 329, 330, 331, + 0, 0, 591, 592, 593, 616, 0, 594, 575, 641, + 412, 342, 556, 583, 782, 783, 0, 0, 0, 0, + 0, 0, 0, 692, 703, 739, 740, 0, 753, 754, + 756, 758, 757, 760, 549, 550, 768, 0, 0, 437, + 358, 359, 496, 497, 535, 0, 438, 439, 440, 444, + 441, 442, 443, 762, 763, 764, 761, 470, 534, 557, + 543, 781, 483, 0, 789, 631, 632, 790, 749, 748, + 343, 0, 337, 338, 706, 707, 0, 0, 537, 482, + 638, 502, 0, 0, 647, 681, 670, 759, 634, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 395, + 0, 0, 465, 685, 666, 677, 667, 508, 652, 653, + 654, 661, 407, 655, 791, 657, 626, 658, 627, 659, + 660, 0, 684, 633, 545, 484, 0, 701, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 325, 326, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 718, 717, 716, - 715, 714, 713, 712, 711, 0, 0, 658, 384, 781, - 775, 782, 763, 766, 765, 740, 0, 341, 638, 464, - 518, 401, 705, 706, 0, 761, 284, 285, 286, 287, - 288, 289, 290, 291, 332, 292, 293, 294, 295, 296, - 297, 298, 303, 304, 305, 306, 307, 308, 309, 310, - 708, 301, 302, 311, 312, 313, 314, 315, 316, 317, - 318, 319, 320, 321, 322, 323, 324, 0, 0, 0, - 0, 333, 767, 768, 769, 770, 771, 0, 0, 334, - 335, 336, 0, 0, 299, 300, 327, 549, 328, 329, - 330, 331, 0, 0, 587, 588, 589, 612, 0, 590, - 572, 637, 411, 342, 553, 579, 778, 779, 0, 0, - 0, 0, 0, 0, 0, 688, 699, 735, 736, 0, - 749, 750, 752, 754, 753, 756, 546, 547, 764, 0, - 0, 435, 357, 358, 494, 495, 532, 0, 436, 437, - 438, 442, 439, 440, 441, 758, 759, 760, 757, 468, - 531, 554, 540, 777, 481, 0, 785, 627, 628, 786, - 745, 744, 343, 0, 337, 338, 702, 703, 0, 0, - 534, 480, 634, 500, 0, 0, 643, 677, 666, 755, - 630, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 394, 0, 0, 463, 681, 662, 673, 663, 506, - 648, 649, 650, 657, 406, 651, 787, 653, 622, 654, - 623, 655, 656, 0, 680, 629, 542, 482, 0, 697, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 268, 0, 0, 0, 0, 0, 0, 365, 269, - 624, 751, 626, 625, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 368, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 543, 571, 0, 584, 0, 432, - 433, 0, 0, 0, 0, 0, 0, 0, 350, 550, - 568, 366, 537, 582, 371, 545, 561, 361, 499, 533, - 0, 0, 352, 566, 544, 477, 351, 0, 526, 391, - 408, 388, 497, 0, 0, 565, 595, 387, 585, 0, - 576, 354, 0, 575, 496, 469, 562, 567, 478, 471, - 0, 353, 564, 476, 470, 446, 398, 611, 447, 448, - 449, 450, 451, 452, 412, 512, 467, 513, 413, 484, - 483, 485, 414, 415, 416, 417, 418, 419, 420, 421, - 422, 423, 0, 0, 0, 0, 0, 606, 607, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 743, 0, 0, 748, 0, 578, - 0, 0, 1909, 0, 0, 0, 548, 0, 0, 453, - 0, 0, 0, 596, 0, 529, 502, 789, 0, 0, - 527, 465, 563, 514, 569, 551, 577, 521, 515, 344, - 552, 390, 479, 362, 364, 776, 392, 395, 399, 400, - 490, 491, 508, 536, 555, 556, 557, 389, 376, 528, - 377, 409, 378, 345, 381, 380, 382, 538, 383, 347, - 509, 560, 0, 405, 524, 474, 348, 473, 510, 559, - 558, 363, 586, 593, 594, 685, 0, 599, 790, 791, - 792, 608, 0, 516, 359, 356, 0, 0, 0, 385, - 511, 372, 374, 375, 373, 505, 507, 613, 614, 615, - 617, 0, 618, 619, 0, 0, 0, 0, 620, 686, - 704, 670, 639, 601, 694, 636, 640, 641, 426, 427, - 428, 429, 707, 0, 0, 0, 592, 454, 455, 0, - 397, 396, 475, 349, 0, 0, 443, 425, 517, 355, - 393, 445, 431, 456, 457, 458, 459, 460, 0, 461, - 462, 403, 339, 340, 784, 386, 498, 709, 746, 747, - 631, 0, 695, 632, 642, 379, 667, 679, 678, 492, - 591, 0, 690, 693, 621, 783, 0, 687, 701, 788, - 700, 780, 504, 0, 535, 698, 645, 0, 691, 664, - 665, 0, 692, 660, 696, 0, 633, 0, 602, 605, - 635, 722, 723, 724, 346, 604, 726, 727, 728, 729, - 730, 731, 732, 725, 574, 668, 644, 671, 583, 647, - 646, 0, 0, 682, 600, 683, 684, 486, 487, 488, - 489, 407, 710, 370, 603, 519, 520, 0, 669, 0, - 0, 0, 0, 0, 0, 0, 0, 674, 675, 672, - 793, 0, 733, 734, 0, 0, 597, 598, 402, 0, - 616, 410, 369, 503, 404, 581, 434, 0, 609, 676, - 610, 522, 523, 737, 742, 738, 739, 741, 762, 493, - 424, 430, 539, 444, 466, 525, 580, 501, 530, 367, - 570, 541, 472, 661, 689, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 325, 326, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 268, + 0, 0, 0, 0, 0, 0, 366, 269, 628, 755, + 630, 629, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 369, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 546, 574, 0, 588, 0, 434, 435, 0, + 0, 0, 0, 0, 0, 0, 350, 553, 571, 367, + 540, 586, 372, 548, 564, 362, 501, 536, 0, 0, + 352, 569, 547, 479, 351, 0, 529, 392, 409, 389, + 499, 0, 0, 568, 599, 388, 589, 0, 579, 354, + 0, 578, 498, 471, 565, 570, 480, 473, 0, 353, + 567, 478, 472, 448, 399, 615, 449, 450, 451, 452, + 453, 454, 413, 514, 469, 515, 414, 486, 485, 487, + 415, 416, 417, 418, 419, 420, 421, 422, 423, 424, + 0, 0, 0, 0, 0, 610, 611, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 747, 0, 0, 752, 0, 582, 0, 0, + 0, 0, 0, 0, 551, 0, 0, 455, 0, 0, + 0, 600, 0, 532, 504, 793, 0, 0, 530, 467, + 566, 516, 572, 554, 580, 523, 517, 344, 555, 391, + 481, 363, 365, 780, 393, 396, 400, 401, 492, 493, + 510, 539, 558, 559, 560, 390, 377, 531, 378, 410, + 379, 345, 382, 381, 383, 541, 384, 347, 511, 563, + 0, 406, 527, 476, 348, 475, 512, 562, 561, 364, + 590, 597, 598, 689, 0, 603, 794, 795, 796, 612, + 0, 518, 360, 357, 0, 0, 0, 386, 513, 373, + 375, 376, 374, 507, 509, 617, 618, 619, 621, 0, + 622, 623, 0, 0, 0, 0, 624, 690, 708, 674, + 643, 605, 698, 640, 644, 645, 427, 428, 429, 430, + 711, 0, 0, 0, 596, 456, 457, 0, 398, 397, + 477, 349, 0, 0, 445, 426, 519, 355, 394, 447, + 432, 458, 459, 460, 461, 462, 0, 463, 464, 404, + 339, 340, 788, 387, 500, 713, 750, 751, 635, 0, + 699, 636, 646, 380, 671, 683, 682, 494, 595, 0, + 694, 697, 625, 787, 0, 691, 705, 792, 704, 784, + 506, 0, 538, 702, 649, 0, 695, 668, 669, 0, + 696, 664, 700, 356, 433, 524, 581, 0, 637, 0, + 606, 609, 639, 726, 727, 728, 346, 608, 730, 731, + 732, 733, 734, 735, 736, 729, 577, 672, 648, 675, + 587, 651, 650, 0, 0, 686, 604, 687, 688, 488, + 489, 490, 491, 408, 714, 371, 607, 521, 522, 0, + 673, 0, 0, 0, 0, 0, 0, 0, 0, 678, + 679, 676, 797, 0, 737, 738, 0, 0, 601, 602, + 403, 0, 620, 411, 370, 505, 405, 585, 436, 0, + 613, 680, 614, 525, 526, 741, 746, 742, 743, 745, + 766, 495, 425, 431, 542, 446, 468, 528, 584, 503, + 533, 368, 573, 544, 474, 665, 693, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 325, 326, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 718, 717, 716, 715, 714, 713, 712, 711, - 0, 0, 658, 384, 781, 775, 782, 763, 766, 765, - 740, 0, 341, 638, 464, 518, 401, 705, 706, 0, - 761, 284, 285, 286, 287, 288, 289, 290, 291, 332, - 292, 293, 294, 295, 296, 297, 298, 303, 304, 305, - 306, 307, 308, 309, 310, 708, 301, 302, 311, 312, - 313, 314, 315, 316, 317, 318, 319, 320, 321, 322, - 323, 324, 0, 0, 0, 0, 333, 767, 768, 769, - 770, 771, 0, 0, 334, 335, 336, 0, 0, 299, - 300, 327, 549, 328, 329, 330, 331, 0, 0, 587, - 588, 589, 612, 0, 590, 572, 637, 411, 342, 553, - 579, 778, 779, 0, 0, 0, 0, 0, 0, 0, - 688, 699, 735, 736, 0, 749, 750, 752, 754, 753, - 756, 546, 547, 764, 0, 0, 435, 357, 358, 494, - 495, 532, 0, 436, 437, 438, 442, 439, 440, 441, - 758, 759, 760, 757, 468, 531, 554, 540, 777, 481, - 0, 785, 627, 628, 786, 745, 744, 343, 0, 337, - 338, 702, 703, 0, 0, 534, 480, 634, 500, 0, - 0, 643, 677, 666, 755, 630, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 1205, 394, 0, 0, 463, - 681, 662, 673, 663, 506, 648, 649, 650, 657, 406, - 651, 787, 653, 622, 654, 623, 655, 656, 0, 680, - 629, 542, 482, 0, 697, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 722, 721, 720, 719, 718, 717, + 716, 715, 1212, 0, 662, 385, 785, 779, 786, 767, + 770, 769, 744, 0, 341, 642, 466, 520, 402, 709, + 710, 0, 765, 284, 285, 286, 287, 288, 289, 290, + 291, 332, 292, 293, 294, 295, 296, 297, 298, 303, + 304, 305, 306, 307, 308, 309, 310, 712, 301, 302, + 311, 312, 313, 314, 315, 316, 317, 318, 319, 320, + 321, 322, 323, 324, 0, 0, 0, 0, 333, 771, + 772, 773, 774, 775, 0, 0, 334, 335, 336, 0, + 0, 299, 300, 327, 552, 328, 329, 330, 331, 0, + 0, 591, 592, 593, 616, 0, 594, 575, 641, 412, + 342, 556, 583, 782, 783, 0, 0, 0, 0, 0, + 0, 0, 692, 703, 739, 740, 0, 753, 754, 756, + 758, 757, 760, 549, 550, 768, 0, 0, 437, 358, + 359, 496, 497, 535, 0, 438, 439, 440, 444, 441, + 442, 443, 762, 763, 764, 761, 470, 534, 557, 543, + 781, 483, 0, 789, 631, 632, 790, 749, 748, 343, + 0, 337, 338, 706, 707, 0, 0, 537, 482, 638, + 502, 0, 0, 647, 681, 670, 759, 634, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 395, 0, + 0, 465, 685, 666, 677, 667, 508, 652, 653, 654, + 661, 407, 655, 791, 657, 626, 658, 627, 659, 660, + 0, 684, 633, 545, 484, 0, 701, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 268, 0, 0, 0, - 0, 0, 0, 365, 269, 624, 751, 626, 625, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 368, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 543, - 571, 0, 584, 0, 432, 433, 0, 0, 0, 0, - 0, 0, 0, 350, 550, 568, 366, 537, 582, 371, - 545, 561, 361, 499, 533, 0, 0, 352, 566, 544, - 477, 351, 0, 526, 391, 408, 388, 497, 0, 0, - 565, 595, 387, 585, 0, 576, 354, 0, 575, 496, - 469, 562, 567, 478, 471, 0, 353, 564, 476, 470, - 446, 398, 611, 447, 448, 449, 450, 451, 452, 412, - 512, 467, 513, 413, 484, 483, 485, 414, 415, 416, - 417, 418, 419, 420, 421, 422, 423, 0, 0, 0, - 0, 0, 606, 607, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 743, - 0, 0, 748, 0, 578, 0, 0, 0, 0, 0, - 0, 548, 0, 0, 453, 0, 0, 0, 596, 0, - 529, 502, 789, 0, 0, 527, 465, 563, 514, 569, - 551, 577, 521, 515, 344, 552, 390, 479, 362, 364, - 776, 392, 395, 399, 400, 490, 491, 508, 536, 555, - 556, 557, 389, 376, 528, 377, 409, 378, 345, 381, - 380, 382, 538, 383, 347, 509, 560, 0, 405, 524, - 474, 348, 473, 510, 559, 558, 363, 586, 593, 594, - 685, 0, 599, 790, 791, 792, 608, 0, 516, 359, - 356, 0, 0, 0, 385, 511, 372, 374, 375, 373, - 505, 507, 613, 614, 615, 617, 0, 618, 619, 0, - 0, 0, 0, 620, 686, 704, 670, 639, 601, 694, - 636, 640, 641, 426, 427, 428, 429, 707, 0, 0, - 0, 592, 454, 455, 0, 397, 396, 475, 349, 0, - 0, 443, 425, 517, 355, 393, 445, 431, 456, 457, - 458, 459, 460, 0, 461, 462, 403, 339, 340, 784, - 386, 498, 709, 746, 747, 631, 0, 695, 632, 642, - 379, 667, 679, 678, 492, 591, 0, 690, 693, 621, - 783, 0, 687, 701, 788, 700, 780, 504, 0, 535, - 698, 645, 0, 691, 664, 665, 0, 692, 660, 696, - 0, 633, 0, 602, 605, 635, 722, 723, 724, 346, - 604, 726, 727, 728, 729, 730, 731, 732, 725, 574, - 668, 644, 671, 583, 647, 646, 0, 0, 682, 600, - 683, 684, 486, 487, 488, 489, 407, 710, 370, 603, - 519, 520, 0, 669, 0, 0, 0, 0, 0, 0, - 0, 0, 674, 675, 672, 793, 0, 733, 734, 0, - 0, 597, 598, 402, 0, 616, 410, 369, 503, 404, - 581, 434, 0, 609, 676, 610, 522, 523, 737, 742, - 738, 739, 741, 762, 493, 424, 430, 539, 444, 466, - 525, 580, 501, 530, 367, 570, 541, 472, 661, 689, + 0, 0, 0, 0, 0, 0, 0, 0, 268, 0, + 0, 0, 0, 0, 0, 366, 269, 628, 755, 630, + 629, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 369, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 546, 574, 0, 588, 0, 434, 435, 0, 0, + 0, 0, 0, 0, 0, 350, 553, 571, 367, 540, + 586, 372, 548, 564, 362, 501, 536, 0, 0, 352, + 569, 547, 479, 351, 0, 529, 392, 409, 389, 499, + 0, 0, 568, 599, 388, 589, 0, 579, 354, 0, + 578, 498, 471, 565, 570, 480, 473, 0, 353, 567, + 478, 472, 448, 399, 615, 449, 450, 451, 452, 453, + 454, 413, 514, 469, 515, 414, 486, 485, 487, 415, + 416, 417, 418, 419, 420, 421, 422, 423, 424, 0, + 0, 0, 0, 0, 610, 611, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 747, 0, 952, 752, 0, 582, 0, 0, 0, + 0, 0, 0, 551, 0, 0, 455, 0, 0, 0, + 600, 0, 532, 504, 793, 0, 0, 530, 467, 566, + 516, 572, 554, 580, 523, 517, 344, 555, 391, 481, + 363, 365, 951, 393, 396, 400, 401, 492, 493, 510, + 539, 558, 559, 560, 390, 377, 531, 378, 410, 379, + 345, 382, 381, 383, 541, 384, 347, 511, 563, 0, + 406, 527, 476, 348, 475, 512, 562, 561, 364, 590, + 597, 598, 689, 0, 603, 794, 795, 796, 612, 0, + 518, 360, 357, 0, 0, 0, 386, 513, 373, 375, + 376, 374, 507, 509, 617, 618, 619, 621, 0, 622, + 623, 0, 0, 0, 0, 624, 690, 708, 674, 643, + 605, 698, 640, 644, 645, 427, 428, 429, 430, 711, + 0, 0, 0, 596, 456, 457, 0, 398, 397, 477, + 349, 0, 0, 445, 426, 519, 355, 394, 447, 432, + 458, 459, 460, 461, 462, 0, 463, 464, 404, 339, + 340, 788, 387, 500, 713, 750, 751, 635, 0, 699, + 636, 646, 380, 671, 683, 682, 494, 595, 0, 694, + 697, 625, 787, 0, 691, 705, 792, 704, 784, 506, + 0, 538, 702, 649, 0, 695, 668, 669, 0, 696, + 664, 700, 356, 433, 524, 581, 0, 637, 0, 606, + 609, 639, 726, 727, 728, 346, 608, 730, 731, 732, + 733, 734, 735, 736, 729, 577, 672, 648, 675, 587, + 651, 650, 0, 0, 686, 604, 687, 688, 488, 489, + 490, 491, 408, 714, 371, 607, 521, 522, 0, 673, + 0, 0, 0, 0, 0, 0, 0, 0, 678, 679, + 676, 797, 0, 737, 738, 0, 0, 601, 602, 403, + 0, 620, 411, 370, 505, 405, 585, 436, 0, 613, + 680, 614, 525, 526, 741, 746, 742, 743, 745, 766, + 495, 425, 431, 542, 446, 468, 528, 584, 503, 533, + 368, 573, 544, 474, 665, 693, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 325, 326, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 325, 326, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 718, 717, 716, - 715, 714, 713, 712, 711, 0, 0, 658, 384, 781, - 775, 782, 763, 766, 765, 740, 0, 341, 638, 464, - 518, 401, 705, 706, 0, 761, 284, 285, 286, 287, - 288, 289, 290, 291, 332, 292, 293, 294, 295, 296, - 297, 298, 303, 304, 305, 306, 307, 308, 309, 310, - 708, 301, 302, 311, 312, 313, 314, 315, 316, 317, - 318, 319, 320, 321, 322, 323, 324, 0, 0, 0, - 0, 333, 767, 768, 769, 770, 771, 0, 0, 334, - 335, 336, 0, 0, 299, 300, 327, 549, 328, 329, - 330, 331, 0, 0, 587, 588, 589, 612, 0, 590, - 572, 637, 411, 342, 553, 579, 778, 779, 0, 0, - 0, 0, 0, 0, 0, 688, 699, 735, 736, 0, - 749, 750, 752, 754, 753, 756, 546, 547, 764, 0, - 0, 435, 357, 358, 494, 495, 532, 0, 436, 437, - 438, 442, 439, 440, 441, 758, 759, 760, 757, 468, - 531, 554, 540, 777, 481, 0, 785, 627, 628, 786, - 745, 744, 343, 0, 337, 338, 702, 703, 0, 0, - 534, 480, 634, 500, 0, 0, 643, 677, 666, 755, - 630, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 394, 0, 0, 463, 681, 662, 673, 663, 506, - 648, 649, 650, 657, 406, 651, 787, 653, 622, 654, - 623, 655, 656, 0, 680, 629, 542, 482, 0, 697, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 268, 0, 0, 0, 0, 0, 0, 365, 269, - 624, 751, 626, 625, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 368, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 543, 571, 0, 584, 0, 432, - 433, 0, 0, 0, 0, 0, 0, 0, 350, 550, - 568, 366, 537, 582, 371, 545, 561, 361, 499, 533, - 0, 0, 352, 566, 544, 477, 351, 0, 526, 391, - 408, 388, 497, 0, 0, 565, 595, 387, 585, 0, - 576, 354, 0, 575, 496, 469, 562, 567, 478, 471, - 0, 353, 564, 476, 470, 446, 398, 611, 447, 448, - 449, 450, 451, 452, 412, 512, 467, 513, 413, 484, - 483, 485, 414, 415, 416, 417, 418, 419, 420, 421, - 422, 423, 0, 0, 0, 0, 0, 606, 607, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 743, 0, 812, 748, 0, 578, - 0, 0, 0, 0, 0, 0, 548, 0, 0, 453, - 0, 0, 0, 596, 0, 529, 502, 789, 0, 0, - 527, 465, 563, 514, 569, 551, 577, 521, 515, 344, - 552, 390, 479, 362, 364, 776, 392, 395, 399, 400, - 490, 491, 508, 536, 555, 556, 557, 389, 376, 528, - 377, 409, 378, 345, 381, 380, 382, 538, 383, 347, - 509, 560, 0, 405, 524, 474, 348, 473, 510, 559, - 558, 363, 586, 593, 594, 685, 0, 599, 790, 791, - 792, 608, 0, 516, 359, 356, 0, 0, 0, 385, - 511, 372, 374, 375, 373, 505, 507, 613, 614, 615, - 617, 0, 618, 619, 0, 0, 0, 0, 620, 686, - 704, 670, 639, 601, 694, 636, 640, 641, 426, 427, - 428, 429, 707, 0, 0, 0, 592, 454, 455, 0, - 397, 396, 475, 349, 0, 0, 443, 425, 517, 355, - 393, 445, 431, 456, 457, 458, 459, 460, 0, 461, - 462, 403, 339, 340, 784, 386, 498, 709, 746, 747, - 631, 0, 695, 632, 642, 379, 667, 679, 678, 492, - 591, 0, 690, 693, 621, 783, 0, 687, 701, 788, - 700, 780, 504, 0, 535, 698, 645, 0, 691, 664, - 665, 0, 692, 660, 696, 0, 633, 0, 602, 605, - 635, 722, 723, 724, 346, 604, 726, 727, 728, 729, - 730, 731, 732, 725, 574, 668, 644, 671, 583, 647, - 646, 0, 0, 682, 600, 683, 684, 486, 487, 488, - 489, 407, 710, 370, 603, 519, 520, 0, 669, 0, - 0, 0, 0, 0, 0, 0, 0, 674, 675, 672, - 793, 0, 733, 734, 0, 0, 597, 598, 402, 0, - 616, 410, 369, 503, 404, 581, 434, 0, 609, 676, - 610, 522, 523, 737, 742, 738, 739, 741, 762, 493, - 424, 430, 539, 444, 466, 525, 580, 501, 530, 367, - 570, 541, 472, 661, 689, 0, 0, 0, 0, 0, + 0, 0, 0, 722, 721, 720, 719, 718, 717, 716, + 715, 0, 0, 662, 385, 785, 779, 786, 767, 770, + 769, 744, 0, 341, 642, 466, 520, 402, 709, 710, + 0, 765, 284, 285, 286, 287, 288, 289, 290, 291, + 332, 292, 293, 294, 295, 296, 297, 298, 303, 304, + 305, 306, 307, 308, 309, 310, 712, 301, 302, 311, + 312, 313, 314, 315, 316, 317, 318, 319, 320, 321, + 322, 323, 324, 0, 0, 0, 0, 333, 771, 772, + 773, 774, 775, 0, 0, 334, 335, 336, 0, 0, + 299, 300, 327, 552, 328, 329, 330, 331, 0, 0, + 591, 592, 593, 616, 0, 594, 575, 641, 412, 342, + 556, 583, 782, 783, 0, 0, 0, 0, 0, 0, + 0, 692, 703, 739, 740, 0, 753, 754, 756, 758, + 757, 760, 549, 550, 768, 0, 0, 437, 358, 359, + 496, 497, 535, 0, 438, 439, 440, 444, 441, 442, + 443, 762, 763, 764, 761, 470, 534, 557, 543, 781, + 483, 0, 789, 631, 632, 790, 749, 748, 343, 0, + 337, 338, 706, 707, 0, 0, 537, 482, 638, 502, + 0, 0, 647, 681, 670, 759, 634, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 395, 0, 0, + 465, 685, 666, 677, 667, 508, 652, 653, 654, 661, + 407, 655, 791, 657, 626, 658, 627, 659, 660, 0, + 684, 633, 545, 484, 0, 701, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 268, 0, 0, + 0, 0, 0, 0, 366, 269, 628, 755, 630, 629, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 369, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 546, 574, 0, 588, 0, 434, 435, 0, 0, 0, + 0, 0, 0, 0, 350, 553, 571, 367, 540, 586, + 372, 548, 564, 362, 501, 536, 0, 0, 352, 569, + 547, 479, 351, 0, 529, 392, 409, 389, 499, 0, + 0, 568, 599, 388, 589, 0, 579, 354, 0, 578, + 498, 471, 565, 570, 480, 473, 0, 353, 567, 478, + 472, 448, 399, 615, 449, 450, 451, 452, 453, 454, + 413, 514, 469, 515, 414, 486, 485, 487, 415, 416, + 417, 418, 419, 420, 421, 422, 423, 424, 0, 0, + 0, 0, 0, 610, 611, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 747, 0, 0, 752, 0, 582, 0, 0, 0, 0, + 0, 0, 551, 0, 0, 455, 0, 0, 0, 600, + 0, 532, 504, 793, 0, 0, 530, 467, 566, 516, + 572, 554, 580, 523, 517, 344, 555, 391, 481, 363, + 365, 780, 393, 396, 400, 401, 492, 493, 510, 539, + 558, 559, 560, 390, 377, 531, 378, 410, 379, 345, + 382, 381, 383, 541, 384, 347, 511, 563, 0, 406, + 527, 476, 348, 475, 512, 562, 561, 364, 590, 597, + 598, 689, 0, 603, 794, 795, 796, 612, 0, 518, + 360, 357, 0, 0, 0, 386, 513, 373, 375, 376, + 374, 507, 509, 617, 618, 619, 621, 0, 622, 623, + 0, 0, 0, 0, 624, 690, 708, 674, 643, 605, + 698, 640, 644, 645, 427, 428, 429, 430, 711, 0, + 0, 0, 596, 456, 457, 0, 398, 397, 477, 349, + 0, 0, 445, 426, 519, 355, 394, 447, 432, 458, + 459, 460, 461, 462, 0, 463, 464, 404, 339, 340, + 788, 387, 500, 713, 750, 751, 635, 0, 699, 636, + 646, 380, 671, 683, 682, 494, 595, 0, 694, 697, + 625, 787, 0, 691, 705, 792, 704, 784, 506, 0, + 538, 702, 649, 0, 695, 668, 669, 0, 696, 664, + 700, 356, 433, 524, 581, 0, 637, 0, 606, 609, + 639, 726, 727, 728, 346, 608, 730, 731, 732, 733, + 734, 735, 736, 729, 577, 672, 648, 675, 587, 651, + 650, 0, 0, 686, 604, 687, 688, 488, 489, 490, + 491, 408, 714, 371, 607, 521, 522, 0, 673, 0, + 0, 0, 0, 0, 0, 0, 0, 678, 679, 676, + 797, 0, 737, 738, 0, 0, 601, 602, 403, 0, + 620, 411, 370, 505, 405, 585, 436, 0, 613, 680, + 614, 525, 526, 741, 746, 742, 743, 745, 766, 495, + 425, 431, 542, 446, 468, 528, 584, 503, 533, 368, + 573, 544, 474, 665, 693, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 325, 326, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 718, 717, 716, 715, 714, 713, 712, 711, - 0, 0, 658, 384, 781, 775, 782, 763, 766, 765, - 740, 0, 341, 638, 464, 518, 401, 705, 706, 0, - 761, 284, 285, 286, 287, 288, 289, 290, 291, 332, + 0, 0, 722, 721, 720, 719, 718, 717, 716, 715, + 0, 0, 662, 385, 785, 779, 786, 767, 770, 769, + 744, 0, 341, 642, 466, 520, 402, 709, 710, 0, + 765, 284, 285, 286, 287, 288, 289, 290, 291, 332, 292, 293, 294, 295, 296, 297, 298, 303, 304, 305, - 306, 307, 308, 309, 310, 708, 301, 302, 311, 312, + 306, 307, 308, 309, 310, 712, 301, 302, 311, 312, 313, 314, 315, 316, 317, 318, 319, 320, 321, 322, - 323, 324, 0, 0, 0, 0, 333, 767, 768, 769, - 770, 771, 0, 0, 334, 335, 336, 0, 0, 299, - 300, 327, 549, 328, 329, 330, 331, 0, 0, 587, - 588, 589, 612, 0, 590, 572, 637, 411, 342, 553, - 579, 778, 779, 0, 0, 0, 0, 0, 0, 0, - 688, 699, 735, 736, 0, 749, 750, 752, 754, 753, - 756, 546, 547, 764, 0, 0, 435, 357, 358, 494, - 495, 532, 0, 436, 437, 438, 442, 439, 440, 441, - 758, 759, 760, 757, 468, 531, 554, 540, 777, 481, - 0, 785, 627, 628, 786, 745, 744, 343, 0, 337, - 338, 702, 703, 0, 0, 534, 480, 634, 500, 0, - 0, 643, 677, 666, 755, 630, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 394, 0, 0, 463, - 681, 662, 673, 663, 506, 648, 649, 650, 657, 406, - 651, 787, 653, 622, 654, 623, 655, 656, 0, 680, - 629, 542, 482, 0, 697, 0, 0, 0, 0, 0, + 323, 324, 0, 0, 0, 0, 333, 771, 772, 773, + 774, 775, 0, 0, 334, 335, 336, 0, 0, 299, + 300, 327, 552, 328, 329, 330, 331, 0, 0, 591, + 592, 593, 616, 0, 594, 575, 641, 412, 342, 556, + 583, 782, 783, 0, 0, 0, 0, 0, 0, 0, + 692, 703, 739, 740, 0, 753, 754, 756, 758, 757, + 760, 549, 550, 768, 0, 0, 437, 358, 359, 496, + 497, 535, 0, 438, 439, 440, 444, 441, 442, 443, + 762, 763, 764, 761, 470, 534, 557, 543, 781, 483, + 0, 789, 631, 632, 790, 749, 748, 343, 0, 337, + 338, 706, 707, 0, 0, 537, 482, 638, 502, 0, + 0, 647, 681, 670, 759, 634, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 395, 0, 0, 465, + 685, 666, 677, 667, 508, 652, 653, 654, 661, 407, + 655, 791, 657, 626, 658, 627, 659, 660, 0, 684, + 633, 545, 484, 0, 701, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 268, 0, 0, 0, - 0, 0, 0, 365, 269, 624, 751, 626, 625, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 368, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 543, - 571, 0, 584, 0, 432, 433, 0, 0, 0, 0, - 0, 0, 0, 350, 550, 568, 366, 537, 582, 371, - 545, 561, 361, 499, 533, 0, 0, 352, 566, 544, - 477, 351, 0, 526, 391, 408, 388, 497, 0, 0, - 565, 595, 387, 585, 0, 576, 354, 0, 575, 496, - 469, 562, 567, 478, 471, 0, 353, 564, 476, 470, - 446, 398, 611, 447, 448, 449, 450, 451, 452, 412, - 512, 467, 513, 413, 484, 483, 485, 414, 415, 416, - 417, 418, 419, 420, 421, 422, 423, 0, 0, 0, - 0, 0, 606, 607, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 743, - 0, 0, 748, 0, 578, 0, 0, 0, 0, 0, - 0, 548, 0, 0, 453, 0, 0, 0, 596, 0, - 529, 502, 789, 0, 0, 527, 465, 563, 514, 569, - 551, 577, 521, 515, 344, 552, 390, 479, 362, 364, - 776, 392, 395, 399, 400, 490, 491, 508, 536, 555, - 556, 557, 389, 376, 528, 377, 409, 378, 345, 381, - 380, 382, 538, 383, 347, 509, 560, 0, 405, 524, - 474, 348, 473, 510, 559, 558, 363, 586, 593, 594, - 685, 0, 599, 790, 791, 792, 608, 0, 516, 359, - 356, 0, 0, 0, 385, 511, 372, 374, 375, 373, - 505, 507, 613, 614, 615, 617, 0, 618, 619, 0, - 0, 0, 0, 620, 686, 704, 670, 639, 601, 694, - 636, 640, 641, 426, 427, 428, 429, 707, 0, 0, - 0, 592, 454, 455, 0, 397, 396, 475, 349, 0, - 0, 443, 425, 517, 355, 393, 445, 431, 456, 457, - 458, 459, 460, 0, 461, 462, 403, 339, 340, 784, - 386, 498, 709, 746, 747, 631, 0, 695, 632, 642, - 379, 667, 679, 678, 492, 591, 0, 690, 693, 621, - 783, 0, 687, 701, 788, 700, 780, 504, 0, 535, - 698, 645, 0, 691, 664, 665, 0, 692, 660, 696, - 0, 633, 0, 602, 605, 635, 722, 723, 724, 346, - 604, 726, 727, 728, 729, 730, 731, 732, 725, 574, - 668, 644, 671, 583, 647, 646, 0, 0, 682, 600, - 683, 684, 486, 487, 488, 489, 407, 710, 370, 603, - 519, 520, 0, 669, 0, 0, 0, 0, 0, 0, - 0, 0, 674, 675, 672, 793, 0, 733, 734, 0, - 0, 597, 598, 402, 0, 616, 410, 369, 503, 404, - 581, 434, 0, 609, 676, 610, 522, 523, 737, 742, - 738, 739, 741, 762, 493, 424, 430, 539, 444, 466, - 525, 580, 501, 530, 367, 570, 541, 472, 661, 689, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 325, 326, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 718, 717, 716, - 715, 714, 713, 712, 711, 1208, 0, 658, 384, 781, - 775, 782, 763, 766, 765, 740, 0, 341, 638, 464, - 518, 401, 705, 706, 0, 761, 284, 285, 286, 287, - 288, 289, 290, 291, 332, 292, 293, 294, 295, 296, - 297, 298, 303, 304, 305, 306, 307, 308, 309, 310, - 708, 301, 302, 311, 312, 313, 314, 315, 316, 317, - 318, 319, 320, 321, 322, 323, 324, 0, 0, 0, - 0, 333, 767, 768, 769, 770, 771, 0, 0, 334, - 335, 336, 0, 0, 299, 300, 327, 549, 328, 329, - 330, 331, 0, 0, 587, 588, 589, 612, 0, 590, - 572, 637, 411, 342, 553, 579, 778, 779, 0, 0, - 0, 0, 0, 0, 0, 688, 699, 735, 736, 0, - 749, 750, 752, 754, 753, 756, 546, 547, 764, 0, - 0, 435, 357, 358, 494, 495, 532, 0, 436, 437, - 438, 442, 439, 440, 441, 758, 759, 760, 757, 468, - 531, 554, 540, 777, 481, 0, 785, 627, 628, 786, - 745, 744, 343, 0, 337, 338, 702, 703, 0, 0, - 534, 480, 634, 500, 0, 0, 643, 677, 666, 755, - 630, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 394, 0, 0, 463, 681, 662, 673, 663, 506, - 648, 649, 650, 657, 406, 651, 787, 653, 622, 654, - 623, 655, 656, 0, 680, 629, 542, 482, 0, 697, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 268, 0, 0, 0, 0, 0, 0, 365, 269, - 624, 751, 626, 625, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 368, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 543, 571, 0, 584, 0, 432, - 433, 0, 0, 0, 0, 0, 0, 0, 350, 550, - 568, 366, 537, 582, 371, 545, 561, 361, 499, 533, - 0, 0, 352, 566, 544, 477, 351, 0, 526, 391, - 408, 388, 497, 0, 0, 565, 595, 387, 585, 0, - 576, 354, 0, 575, 496, 469, 562, 567, 478, 471, - 0, 353, 564, 476, 470, 446, 398, 611, 447, 448, - 449, 450, 451, 452, 412, 512, 467, 513, 413, 484, - 483, 485, 414, 415, 416, 417, 418, 419, 420, 421, - 422, 423, 0, 0, 0, 0, 0, 606, 607, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 743, 0, 948, 748, 0, 578, - 0, 0, 0, 0, 0, 0, 548, 0, 0, 453, - 0, 0, 0, 596, 0, 529, 502, 789, 0, 0, - 527, 465, 563, 514, 569, 551, 577, 521, 515, 344, - 552, 390, 479, 362, 364, 947, 392, 395, 399, 400, - 490, 491, 508, 536, 555, 556, 557, 389, 376, 528, - 377, 409, 378, 345, 381, 380, 382, 538, 383, 347, - 509, 560, 0, 405, 524, 474, 348, 473, 510, 559, - 558, 363, 586, 593, 594, 685, 0, 599, 790, 791, - 792, 608, 0, 516, 359, 356, 0, 0, 0, 385, - 511, 372, 374, 375, 373, 505, 507, 613, 614, 615, - 617, 0, 618, 619, 0, 0, 0, 0, 620, 686, - 704, 670, 639, 601, 694, 636, 640, 641, 426, 427, - 428, 429, 707, 0, 0, 0, 592, 454, 455, 0, - 397, 396, 475, 349, 0, 0, 443, 425, 517, 355, - 393, 445, 431, 456, 457, 458, 459, 460, 0, 461, - 462, 403, 339, 340, 784, 386, 498, 709, 746, 747, - 631, 0, 695, 632, 642, 379, 667, 679, 678, 492, - 591, 0, 690, 693, 621, 783, 0, 687, 701, 788, - 700, 780, 504, 0, 535, 698, 645, 0, 691, 664, - 665, 0, 692, 660, 696, 0, 633, 0, 602, 605, - 635, 722, 723, 724, 346, 604, 726, 727, 728, 729, - 730, 731, 732, 725, 574, 668, 644, 671, 583, 647, - 646, 0, 0, 682, 600, 683, 684, 486, 487, 488, - 489, 407, 710, 370, 603, 519, 520, 0, 669, 0, - 0, 0, 0, 0, 0, 0, 0, 674, 675, 672, - 793, 0, 733, 734, 0, 0, 597, 598, 402, 0, - 616, 410, 369, 503, 404, 581, 434, 0, 609, 676, - 610, 522, 523, 737, 742, 738, 739, 741, 762, 493, - 424, 430, 539, 444, 466, 525, 580, 501, 530, 367, - 570, 541, 472, 661, 689, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 325, 326, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 718, 717, 716, 715, 714, 713, 712, 711, - 0, 0, 658, 384, 781, 775, 782, 763, 766, 765, - 740, 0, 341, 638, 464, 518, 401, 705, 706, 0, - 761, 284, 285, 286, 287, 288, 289, 290, 291, 332, - 292, 293, 294, 295, 296, 297, 298, 303, 304, 305, - 306, 307, 308, 309, 310, 708, 301, 302, 311, 312, - 313, 314, 315, 316, 317, 318, 319, 320, 321, 322, - 323, 324, 0, 0, 0, 0, 333, 767, 768, 769, - 770, 771, 0, 0, 334, 335, 336, 0, 0, 299, - 300, 327, 549, 328, 329, 330, 331, 0, 0, 587, - 588, 589, 612, 0, 590, 572, 637, 411, 342, 553, - 579, 778, 779, 0, 0, 0, 0, 0, 0, 0, - 688, 699, 735, 736, 0, 749, 750, 752, 754, 753, - 756, 546, 547, 764, 0, 0, 435, 357, 358, 494, - 495, 532, 0, 436, 437, 438, 442, 439, 440, 441, - 758, 759, 760, 757, 468, 531, 554, 540, 777, 481, - 0, 785, 627, 628, 786, 745, 744, 343, 0, 337, - 338, 702, 703, 0, 0, 534, 480, 634, 500, 0, - 0, 643, 677, 666, 755, 630, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 394, 0, 0, 463, - 681, 662, 673, 663, 506, 648, 649, 650, 657, 406, - 651, 787, 653, 622, 654, 623, 655, 656, 0, 680, - 629, 542, 482, 0, 697, 0, 0, 0, 0, 0, + 0, 0, 0, 366, 269, 628, 755, 630, 629, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 369, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 546, + 574, 0, 588, 0, 434, 435, 0, 0, 0, 0, + 0, 0, 0, 350, 553, 571, 367, 540, 586, 372, + 548, 564, 362, 501, 536, 0, 0, 352, 569, 547, + 479, 351, 0, 529, 392, 409, 389, 499, 0, 0, + 568, 599, 388, 589, 0, 579, 354, 0, 578, 498, + 471, 565, 570, 480, 473, 0, 353, 567, 478, 472, + 448, 399, 615, 449, 450, 451, 452, 453, 454, 413, + 514, 469, 515, 414, 486, 485, 487, 415, 416, 417, + 418, 419, 420, 421, 422, 423, 424, 0, 0, 0, + 0, 0, 610, 611, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 747, + 0, 0, 752, 0, 582, 0, 0, 0, 0, 0, + 0, 551, 0, 0, 455, 0, 0, 0, 600, 0, + 532, 504, 793, 0, 0, 530, 467, 566, 516, 572, + 554, 580, 523, 517, 344, 555, 391, 481, 363, 365, + 780, 393, 396, 400, 401, 492, 493, 510, 539, 558, + 559, 560, 390, 377, 531, 378, 410, 379, 345, 382, + 381, 383, 541, 384, 347, 511, 563, 0, 406, 3971, + 476, 348, 475, 512, 562, 561, 364, 590, 597, 598, + 689, 0, 603, 794, 795, 796, 612, 0, 518, 360, + 357, 0, 0, 0, 386, 513, 373, 375, 376, 374, + 507, 509, 617, 618, 619, 621, 0, 622, 623, 0, + 0, 0, 0, 624, 690, 708, 674, 643, 605, 698, + 640, 644, 645, 427, 428, 429, 430, 711, 0, 0, + 0, 596, 456, 457, 0, 398, 397, 477, 349, 0, + 0, 445, 426, 519, 355, 394, 447, 432, 458, 459, + 460, 461, 462, 0, 463, 464, 404, 339, 340, 788, + 387, 500, 713, 750, 751, 635, 0, 699, 636, 646, + 380, 671, 683, 682, 494, 595, 0, 694, 697, 625, + 787, 0, 691, 705, 792, 704, 784, 506, 0, 538, + 702, 649, 0, 695, 668, 669, 0, 696, 664, 700, + 356, 433, 524, 581, 0, 637, 0, 606, 609, 639, + 726, 727, 728, 346, 608, 730, 731, 732, 733, 734, + 735, 736, 729, 577, 672, 648, 675, 587, 651, 650, + 0, 0, 686, 604, 687, 688, 488, 489, 490, 491, + 408, 714, 371, 607, 521, 522, 0, 673, 0, 0, + 0, 0, 0, 0, 0, 0, 678, 679, 676, 797, + 0, 737, 738, 0, 0, 601, 602, 403, 0, 620, + 411, 370, 505, 405, 585, 436, 0, 613, 680, 614, + 525, 526, 741, 746, 742, 743, 745, 766, 495, 425, + 431, 542, 446, 468, 528, 584, 503, 533, 368, 573, + 544, 474, 665, 693, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 325, 326, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 268, 0, 0, 0, - 0, 0, 0, 365, 269, 624, 751, 626, 625, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 368, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 543, - 571, 0, 584, 0, 432, 433, 0, 0, 0, 0, - 0, 0, 0, 350, 550, 568, 366, 537, 582, 371, - 545, 561, 361, 499, 533, 0, 0, 352, 566, 544, - 477, 351, 0, 526, 391, 408, 388, 497, 0, 0, - 565, 595, 387, 585, 0, 576, 354, 0, 575, 496, - 469, 562, 567, 478, 471, 0, 353, 564, 476, 470, - 446, 398, 611, 447, 448, 449, 450, 451, 452, 412, - 512, 467, 513, 413, 484, 483, 485, 414, 415, 416, - 417, 418, 419, 420, 421, 422, 423, 0, 0, 0, - 0, 0, 606, 607, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 743, - 0, 0, 748, 0, 578, 0, 0, 0, 0, 0, - 0, 548, 0, 0, 453, 0, 0, 0, 596, 0, - 529, 502, 789, 0, 0, 527, 465, 563, 514, 569, - 551, 577, 521, 515, 344, 552, 390, 479, 362, 364, - 776, 392, 395, 399, 400, 490, 491, 508, 536, 555, - 556, 557, 389, 376, 528, 377, 409, 378, 345, 381, - 380, 382, 538, 383, 347, 509, 560, 0, 405, 524, - 474, 348, 473, 510, 559, 558, 363, 586, 593, 594, - 685, 0, 599, 790, 791, 792, 608, 0, 516, 359, - 356, 0, 0, 0, 385, 511, 372, 374, 375, 373, - 505, 507, 613, 614, 615, 617, 0, 618, 619, 0, - 0, 0, 0, 620, 686, 704, 670, 639, 601, 694, - 636, 640, 641, 426, 427, 428, 429, 707, 0, 0, - 0, 592, 454, 455, 0, 397, 396, 475, 349, 0, - 0, 443, 425, 517, 355, 393, 445, 431, 456, 457, - 458, 459, 460, 0, 461, 462, 403, 339, 340, 784, - 386, 498, 709, 746, 747, 631, 0, 695, 632, 642, - 379, 667, 679, 678, 492, 591, 0, 690, 693, 621, - 783, 0, 687, 701, 788, 700, 780, 504, 0, 535, - 698, 645, 0, 691, 664, 665, 0, 692, 660, 696, - 0, 633, 0, 602, 605, 635, 722, 723, 724, 346, - 604, 726, 727, 728, 729, 730, 731, 732, 725, 574, - 668, 644, 671, 583, 647, 646, 0, 0, 682, 600, - 683, 684, 486, 487, 488, 489, 407, 710, 370, 603, - 519, 520, 0, 669, 0, 0, 0, 0, 0, 0, - 0, 0, 674, 675, 672, 793, 0, 733, 734, 0, - 0, 597, 598, 402, 0, 616, 410, 369, 503, 404, - 581, 434, 0, 609, 676, 610, 522, 523, 737, 742, - 738, 739, 741, 762, 493, 424, 430, 539, 444, 466, - 525, 580, 501, 530, 367, 570, 541, 472, 661, 689, + 0, 722, 721, 720, 719, 718, 717, 716, 715, 0, + 0, 662, 385, 785, 779, 786, 767, 770, 769, 744, + 0, 341, 642, 466, 520, 402, 709, 710, 0, 765, + 284, 285, 286, 287, 288, 289, 290, 291, 332, 292, + 293, 294, 295, 296, 297, 298, 303, 304, 305, 306, + 307, 308, 309, 310, 712, 301, 302, 311, 312, 313, + 314, 315, 316, 317, 318, 319, 320, 321, 322, 323, + 324, 0, 0, 0, 0, 333, 771, 772, 773, 774, + 775, 0, 0, 334, 335, 336, 0, 0, 299, 300, + 327, 552, 328, 329, 330, 331, 0, 0, 591, 592, + 593, 616, 0, 594, 575, 641, 412, 342, 556, 583, + 782, 783, 0, 0, 0, 0, 0, 0, 0, 692, + 703, 739, 740, 0, 753, 754, 756, 758, 757, 760, + 549, 550, 768, 0, 0, 437, 358, 359, 496, 497, + 535, 0, 438, 439, 440, 444, 441, 442, 443, 762, + 763, 764, 761, 470, 534, 557, 543, 781, 483, 0, + 789, 631, 632, 790, 749, 748, 343, 0, 337, 338, + 706, 707, 0, 0, 537, 482, 638, 647, 681, 670, + 759, 634, 0, 0, 0, 0, 0, 2451, 0, 0, + 0, 0, 395, 0, 0, 465, 685, 666, 677, 667, + 508, 652, 653, 654, 661, 407, 655, 791, 657, 626, + 658, 627, 659, 660, 0, 684, 633, 545, 484, 0, + 701, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 268, 0, 0, 2453, 0, 0, 0, 366, + 269, 628, 755, 630, 629, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 369, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 546, 574, 0, 588, 0, + 434, 435, 0, 0, 0, 0, 0, 0, 0, 350, + 553, 571, 367, 540, 586, 372, 548, 564, 362, 501, + 536, 0, 0, 352, 569, 547, 479, 351, 0, 529, + 392, 409, 389, 499, 0, 0, 568, 599, 388, 589, + 0, 579, 354, 0, 578, 498, 471, 565, 570, 480, + 473, 0, 353, 567, 478, 472, 448, 399, 615, 449, + 450, 451, 452, 453, 454, 413, 514, 469, 515, 414, + 486, 485, 487, 415, 416, 417, 418, 419, 420, 421, + 422, 423, 424, 0, 0, 0, 0, 0, 610, 611, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 747, 0, 0, 752, 0, + 582, 0, 0, 0, 0, 0, 0, 551, 0, 0, + 455, 0, 0, 0, 600, 0, 532, 504, 793, 0, + 0, 530, 467, 566, 516, 572, 554, 580, 523, 517, + 344, 555, 391, 481, 363, 365, 780, 393, 396, 400, + 401, 492, 493, 510, 539, 558, 559, 560, 390, 377, + 531, 378, 410, 379, 345, 382, 381, 383, 541, 384, + 347, 511, 563, 0, 406, 527, 476, 348, 475, 512, + 562, 561, 364, 590, 597, 598, 689, 0, 603, 794, + 795, 796, 612, 0, 518, 360, 357, 0, 0, 0, + 386, 513, 373, 375, 376, 374, 507, 509, 617, 618, + 619, 621, 0, 622, 623, 0, 0, 0, 0, 624, + 690, 708, 674, 643, 605, 698, 640, 644, 645, 427, + 428, 429, 430, 711, 0, 0, 0, 596, 456, 457, + 0, 398, 397, 477, 349, 0, 0, 445, 426, 519, + 355, 394, 447, 432, 458, 459, 460, 461, 462, 0, + 463, 464, 404, 339, 340, 788, 387, 500, 713, 750, + 751, 635, 0, 699, 636, 646, 380, 671, 683, 682, + 494, 595, 0, 694, 697, 625, 787, 0, 691, 705, + 792, 704, 784, 506, 0, 538, 702, 649, 0, 695, + 668, 669, 0, 696, 664, 700, 356, 433, 524, 581, + 0, 637, 0, 606, 609, 639, 726, 727, 728, 346, + 608, 730, 731, 732, 733, 734, 735, 736, 729, 577, + 672, 648, 675, 587, 651, 650, 0, 0, 686, 604, + 687, 688, 488, 489, 490, 491, 408, 714, 371, 607, + 521, 522, 0, 673, 0, 0, 0, 0, 0, 0, + 0, 0, 678, 679, 676, 797, 0, 737, 738, 0, + 0, 601, 602, 403, 0, 620, 411, 370, 505, 405, + 585, 436, 0, 613, 680, 614, 525, 526, 741, 746, + 742, 743, 745, 766, 495, 425, 431, 542, 446, 468, + 528, 584, 503, 533, 368, 573, 544, 474, 665, 693, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 325, 326, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 718, 717, 716, - 715, 714, 713, 712, 711, 0, 0, 658, 384, 781, - 775, 782, 763, 766, 765, 740, 0, 341, 638, 464, - 518, 401, 705, 706, 0, 761, 284, 285, 286, 287, + 0, 0, 0, 0, 0, 0, 0, 722, 721, 720, + 719, 718, 717, 716, 715, 0, 0, 662, 385, 785, + 779, 786, 767, 770, 769, 744, 0, 341, 642, 466, + 520, 402, 709, 710, 0, 765, 284, 285, 286, 287, 288, 289, 290, 291, 332, 292, 293, 294, 295, 296, 297, 298, 303, 304, 305, 306, 307, 308, 309, 310, - 708, 301, 302, 311, 312, 313, 314, 315, 316, 317, + 712, 301, 302, 311, 312, 313, 314, 315, 316, 317, 318, 319, 320, 321, 322, 323, 324, 0, 0, 0, - 0, 333, 767, 768, 769, 770, 771, 0, 0, 334, - 335, 336, 0, 0, 299, 300, 327, 549, 328, 329, - 330, 331, 0, 0, 587, 588, 589, 612, 0, 590, - 572, 637, 411, 342, 553, 579, 778, 779, 0, 0, - 0, 0, 0, 0, 0, 688, 699, 735, 736, 0, - 749, 750, 752, 754, 753, 756, 546, 547, 764, 0, - 0, 435, 357, 358, 494, 495, 532, 0, 436, 437, - 438, 442, 439, 440, 441, 758, 759, 760, 757, 468, - 531, 554, 540, 777, 481, 0, 785, 627, 628, 786, - 745, 744, 343, 0, 337, 338, 702, 703, 0, 0, - 534, 480, 634, 500, 0, 0, 643, 677, 666, 755, - 630, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 394, 0, 0, 463, 681, 662, 673, 663, 506, - 648, 649, 650, 657, 406, 651, 787, 653, 622, 654, - 623, 655, 656, 0, 680, 629, 542, 482, 0, 697, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 268, 0, 0, 0, 0, 0, 0, 365, 269, - 624, 751, 626, 625, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 368, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 543, 571, 0, 584, 0, 432, - 433, 0, 0, 0, 0, 0, 0, 0, 350, 550, - 568, 366, 537, 582, 371, 545, 561, 361, 499, 533, - 0, 0, 352, 566, 544, 477, 351, 0, 526, 391, - 408, 388, 497, 0, 0, 565, 595, 387, 585, 0, - 576, 354, 0, 575, 496, 469, 562, 567, 478, 471, - 0, 353, 564, 476, 470, 446, 398, 611, 447, 448, - 449, 450, 451, 452, 412, 512, 467, 513, 413, 484, - 483, 485, 414, 415, 416, 417, 418, 419, 420, 421, - 422, 423, 0, 0, 0, 0, 0, 606, 607, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 743, 0, 0, 748, 0, 578, - 0, 0, 0, 0, 0, 0, 548, 0, 0, 453, - 0, 0, 0, 596, 0, 529, 502, 789, 0, 0, - 527, 465, 563, 514, 569, 551, 577, 521, 515, 344, - 552, 390, 479, 362, 364, 776, 392, 395, 399, 400, - 490, 491, 508, 536, 555, 556, 557, 389, 376, 528, - 377, 409, 378, 345, 381, 380, 382, 538, 383, 347, - 509, 560, 0, 405, 3967, 474, 348, 473, 510, 559, - 558, 363, 586, 593, 594, 685, 0, 599, 790, 791, - 792, 608, 0, 516, 359, 356, 0, 0, 0, 385, - 511, 372, 374, 375, 373, 505, 507, 613, 614, 615, - 617, 0, 618, 619, 0, 0, 0, 0, 620, 686, - 704, 670, 639, 601, 694, 636, 640, 641, 426, 427, - 428, 429, 707, 0, 0, 0, 592, 454, 455, 0, - 397, 396, 475, 349, 0, 0, 443, 425, 517, 355, - 393, 445, 431, 456, 457, 458, 459, 460, 0, 461, - 462, 403, 339, 340, 784, 386, 498, 709, 746, 747, - 631, 0, 695, 632, 642, 379, 667, 679, 678, 492, - 591, 0, 690, 693, 621, 783, 0, 687, 701, 788, - 700, 780, 504, 0, 535, 698, 645, 0, 691, 664, - 665, 0, 692, 660, 696, 0, 633, 0, 602, 605, - 635, 722, 723, 724, 346, 604, 726, 727, 728, 729, - 730, 731, 732, 725, 574, 668, 644, 671, 583, 647, - 646, 0, 0, 682, 600, 683, 684, 486, 487, 488, - 489, 407, 710, 370, 603, 519, 520, 0, 669, 0, - 0, 0, 0, 0, 0, 0, 0, 674, 675, 672, - 793, 0, 733, 734, 0, 0, 597, 598, 402, 0, - 616, 410, 369, 503, 404, 581, 434, 0, 609, 676, - 610, 522, 523, 737, 742, 738, 739, 741, 762, 493, - 424, 430, 539, 444, 466, 525, 580, 501, 530, 367, - 570, 541, 472, 661, 689, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 325, 326, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 718, 717, 716, 715, 714, 713, 712, 711, - 0, 0, 658, 384, 781, 775, 782, 763, 766, 765, - 740, 0, 341, 638, 464, 518, 401, 705, 706, 0, - 761, 284, 285, 286, 287, 288, 289, 290, 291, 332, - 292, 293, 294, 295, 296, 297, 298, 303, 304, 305, - 306, 307, 308, 309, 310, 708, 301, 302, 311, 312, - 313, 314, 315, 316, 317, 318, 319, 320, 321, 322, - 323, 324, 0, 0, 0, 0, 333, 767, 768, 769, - 770, 771, 0, 0, 334, 335, 336, 0, 0, 299, - 300, 327, 549, 328, 329, 330, 331, 0, 0, 587, - 588, 589, 612, 0, 590, 572, 637, 411, 342, 553, - 579, 778, 779, 0, 0, 0, 0, 0, 0, 0, - 688, 699, 735, 736, 0, 749, 750, 752, 754, 753, - 756, 546, 547, 764, 0, 0, 435, 357, 358, 494, - 495, 532, 0, 436, 437, 438, 442, 439, 440, 441, - 758, 759, 760, 757, 468, 531, 554, 540, 777, 481, - 0, 785, 627, 628, 786, 745, 744, 343, 0, 337, - 338, 702, 703, 0, 0, 534, 480, 634, 643, 677, - 666, 755, 630, 0, 0, 0, 0, 0, 2447, 0, - 0, 0, 0, 394, 0, 0, 463, 681, 662, 673, - 663, 506, 648, 649, 650, 657, 406, 651, 787, 653, - 622, 654, 623, 655, 656, 0, 680, 629, 542, 482, - 0, 697, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 268, 0, 0, 2449, 0, 0, 0, - 365, 269, 624, 751, 626, 625, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 368, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 543, 571, 0, 584, - 0, 432, 433, 0, 0, 0, 0, 0, 0, 0, - 350, 550, 568, 366, 537, 582, 371, 545, 561, 361, - 499, 533, 0, 0, 352, 566, 544, 477, 351, 0, - 526, 391, 408, 388, 497, 0, 0, 565, 595, 387, - 585, 0, 576, 354, 0, 575, 496, 469, 562, 567, - 478, 471, 0, 353, 564, 476, 470, 446, 398, 611, - 447, 448, 449, 450, 451, 452, 412, 512, 467, 513, - 413, 484, 483, 485, 414, 415, 416, 417, 418, 419, - 420, 421, 422, 423, 0, 0, 0, 0, 0, 606, - 607, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 743, 0, 0, 748, - 0, 578, 0, 0, 0, 0, 0, 0, 548, 0, - 0, 453, 0, 0, 0, 596, 0, 529, 502, 789, - 0, 0, 527, 465, 563, 514, 569, 551, 577, 521, - 515, 344, 552, 390, 479, 362, 364, 776, 392, 395, - 399, 400, 490, 491, 508, 536, 555, 556, 557, 389, - 376, 528, 377, 409, 378, 345, 381, 380, 382, 538, - 383, 347, 509, 560, 0, 405, 524, 474, 348, 473, - 510, 559, 558, 363, 586, 593, 594, 685, 0, 599, - 790, 791, 792, 608, 0, 516, 359, 356, 0, 0, - 0, 385, 511, 372, 374, 375, 373, 505, 507, 613, - 614, 615, 617, 0, 618, 619, 0, 0, 0, 0, - 620, 686, 704, 670, 639, 601, 694, 636, 640, 641, - 426, 427, 428, 429, 707, 0, 0, 0, 592, 454, - 455, 0, 397, 396, 475, 349, 0, 0, 443, 425, - 517, 355, 393, 445, 431, 456, 457, 458, 459, 460, - 0, 461, 462, 403, 339, 340, 784, 386, 498, 709, - 746, 747, 631, 0, 695, 632, 642, 379, 667, 679, - 678, 492, 591, 0, 690, 693, 621, 783, 0, 687, - 701, 788, 700, 780, 504, 0, 535, 698, 645, 0, - 691, 664, 665, 0, 692, 660, 696, 0, 633, 0, - 602, 605, 635, 722, 723, 724, 346, 604, 726, 727, - 728, 729, 730, 731, 732, 725, 574, 668, 644, 671, - 583, 647, 646, 0, 0, 682, 600, 683, 684, 486, - 487, 488, 489, 407, 710, 370, 603, 519, 520, 0, - 669, 0, 0, 0, 0, 0, 0, 0, 0, 674, - 675, 672, 793, 0, 733, 734, 0, 0, 597, 598, - 402, 0, 616, 410, 369, 503, 404, 581, 434, 0, - 609, 676, 610, 522, 523, 737, 742, 738, 739, 741, - 762, 493, 424, 430, 539, 444, 466, 525, 580, 501, - 530, 367, 570, 541, 472, 661, 689, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 325, 326, + 0, 333, 771, 772, 773, 774, 775, 0, 0, 334, + 335, 336, 0, 0, 299, 300, 327, 552, 328, 329, + 330, 331, 0, 0, 591, 592, 593, 616, 0, 594, + 575, 641, 412, 342, 556, 583, 782, 783, 0, 0, + 0, 0, 0, 0, 0, 692, 703, 739, 740, 0, + 753, 754, 756, 758, 757, 760, 549, 550, 768, 0, + 0, 437, 358, 359, 496, 497, 535, 0, 438, 439, + 440, 444, 441, 442, 443, 762, 763, 764, 761, 470, + 534, 557, 543, 781, 483, 0, 789, 631, 632, 790, + 749, 0, 343, 0, 337, 338, 706, 707, 0, 0, + 537, 482, 638, 502, 0, 0, 647, 681, 670, 759, + 634, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 395, 0, 0, 465, 685, 666, 677, 667, 508, + 652, 653, 654, 661, 407, 655, 791, 657, 626, 658, + 627, 659, 660, 0, 684, 633, 545, 484, 0, 701, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 268, 0, 0, 0, 0, 0, 0, 366, 269, + 628, 755, 630, 629, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 369, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 546, 574, 0, 588, 0, 434, + 435, 0, 0, 0, 0, 0, 0, 0, 350, 553, + 571, 367, 540, 586, 372, 548, 2368, 362, 501, 536, + 0, 0, 352, 569, 547, 479, 351, 0, 529, 392, + 409, 389, 499, 0, 0, 568, 599, 388, 589, 0, + 579, 354, 0, 578, 498, 471, 565, 570, 480, 473, + 0, 353, 567, 478, 472, 448, 399, 615, 449, 450, + 451, 452, 453, 454, 413, 514, 469, 515, 414, 486, + 485, 487, 415, 416, 417, 418, 419, 420, 421, 422, + 423, 424, 0, 0, 0, 0, 0, 610, 611, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 747, 0, 0, 752, 0, 582, + 0, 0, 0, 0, 0, 0, 551, 0, 0, 455, + 0, 0, 0, 600, 0, 532, 504, 793, 0, 0, + 530, 467, 566, 516, 572, 554, 580, 523, 517, 344, + 555, 391, 481, 363, 365, 780, 393, 396, 400, 401, + 492, 493, 510, 539, 558, 559, 560, 390, 377, 531, + 378, 410, 379, 345, 382, 381, 383, 541, 384, 347, + 511, 563, 0, 406, 527, 476, 348, 475, 512, 562, + 561, 364, 590, 597, 598, 689, 0, 603, 794, 795, + 796, 612, 0, 518, 360, 357, 0, 0, 0, 386, + 513, 373, 375, 376, 374, 507, 509, 617, 618, 619, + 621, 0, 622, 623, 0, 0, 0, 0, 624, 690, + 708, 674, 643, 605, 698, 640, 644, 645, 427, 428, + 429, 430, 711, 0, 0, 0, 596, 456, 457, 0, + 398, 397, 477, 349, 0, 0, 445, 426, 519, 355, + 394, 447, 432, 458, 459, 460, 461, 462, 0, 463, + 464, 404, 339, 340, 788, 387, 500, 713, 750, 751, + 635, 0, 699, 636, 646, 380, 671, 683, 682, 494, + 595, 0, 694, 697, 625, 787, 0, 691, 705, 792, + 704, 784, 506, 0, 538, 702, 649, 0, 695, 668, + 669, 0, 696, 664, 700, 356, 433, 524, 581, 0, + 637, 0, 606, 609, 639, 726, 727, 728, 346, 608, + 730, 731, 732, 733, 734, 735, 736, 729, 577, 672, + 648, 675, 587, 651, 650, 0, 0, 686, 604, 687, + 688, 488, 489, 490, 491, 408, 714, 371, 607, 521, + 522, 0, 673, 0, 0, 0, 0, 0, 0, 0, + 0, 678, 679, 676, 797, 0, 737, 738, 0, 0, + 601, 602, 403, 0, 620, 411, 370, 505, 405, 585, + 436, 0, 613, 680, 614, 525, 526, 741, 746, 742, + 743, 745, 766, 495, 425, 431, 542, 446, 468, 528, + 584, 503, 533, 368, 573, 544, 474, 665, 693, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 718, 717, 716, 715, 714, 713, - 712, 711, 0, 0, 658, 384, 781, 775, 782, 763, - 766, 765, 740, 0, 341, 638, 464, 518, 401, 705, - 706, 0, 761, 284, 285, 286, 287, 288, 289, 290, - 291, 332, 292, 293, 294, 295, 296, 297, 298, 303, - 304, 305, 306, 307, 308, 309, 310, 708, 301, 302, - 311, 312, 313, 314, 315, 316, 317, 318, 319, 320, - 321, 322, 323, 324, 0, 0, 0, 0, 333, 767, - 768, 769, 770, 771, 0, 0, 334, 335, 336, 0, - 0, 299, 300, 327, 549, 328, 329, 330, 331, 0, - 0, 587, 588, 589, 612, 0, 590, 572, 637, 411, - 342, 553, 579, 778, 779, 0, 0, 0, 0, 0, - 0, 0, 688, 699, 735, 736, 0, 749, 750, 752, - 754, 753, 756, 546, 547, 764, 0, 0, 435, 357, - 358, 494, 495, 532, 0, 436, 437, 438, 442, 439, - 440, 441, 758, 759, 760, 757, 468, 531, 554, 540, - 777, 481, 0, 785, 627, 628, 786, 745, 0, 343, - 0, 337, 338, 702, 703, 0, 0, 534, 480, 634, - 500, 0, 0, 643, 677, 666, 755, 630, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 394, 0, - 0, 463, 681, 662, 673, 663, 506, 648, 649, 650, - 657, 406, 651, 787, 653, 622, 654, 623, 655, 656, - 0, 680, 629, 542, 482, 0, 697, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 268, 0, - 0, 0, 0, 0, 0, 365, 269, 624, 751, 626, - 625, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 368, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 543, 571, 0, 584, 0, 432, 433, 0, 0, - 0, 0, 0, 0, 0, 350, 550, 568, 366, 537, - 582, 371, 545, 2364, 361, 499, 533, 0, 0, 352, - 566, 544, 477, 351, 0, 526, 391, 408, 388, 497, - 0, 0, 565, 595, 387, 585, 0, 576, 354, 0, - 575, 496, 469, 562, 567, 478, 471, 0, 353, 564, - 476, 470, 446, 398, 611, 447, 448, 449, 450, 451, - 452, 412, 512, 467, 513, 413, 484, 483, 485, 414, - 415, 416, 417, 418, 419, 420, 421, 422, 423, 0, - 0, 0, 0, 0, 606, 607, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 743, 0, 0, 748, 0, 578, 0, 0, 0, - 0, 0, 0, 548, 0, 0, 453, 0, 0, 0, - 596, 0, 529, 502, 789, 0, 0, 527, 465, 563, - 514, 569, 551, 577, 521, 515, 344, 552, 390, 479, - 362, 364, 776, 392, 395, 399, 400, 490, 491, 508, - 536, 555, 556, 557, 389, 376, 528, 377, 409, 378, - 345, 381, 380, 382, 538, 383, 347, 509, 560, 0, - 405, 524, 474, 348, 473, 510, 559, 558, 363, 586, - 593, 594, 685, 0, 599, 790, 791, 792, 608, 0, - 516, 359, 356, 0, 0, 0, 385, 511, 372, 374, - 375, 373, 505, 507, 613, 614, 615, 617, 0, 618, - 619, 0, 0, 0, 0, 620, 686, 704, 670, 639, - 601, 694, 636, 640, 641, 426, 427, 428, 429, 707, - 0, 0, 0, 592, 454, 455, 0, 397, 396, 475, - 349, 0, 0, 443, 425, 517, 355, 393, 445, 431, - 456, 457, 458, 459, 460, 0, 461, 462, 403, 339, - 340, 784, 386, 498, 709, 746, 747, 631, 0, 695, - 632, 642, 379, 667, 679, 678, 492, 591, 0, 690, - 693, 621, 783, 0, 687, 701, 788, 700, 780, 504, - 0, 535, 698, 645, 0, 691, 664, 665, 0, 692, - 660, 696, 0, 633, 0, 602, 605, 635, 722, 723, - 724, 346, 604, 726, 727, 728, 729, 730, 731, 732, - 725, 574, 668, 644, 671, 583, 647, 646, 0, 0, - 682, 600, 683, 684, 486, 487, 488, 489, 407, 710, - 370, 603, 519, 520, 0, 669, 0, 0, 0, 0, - 0, 0, 0, 0, 674, 675, 672, 793, 0, 733, - 734, 0, 0, 597, 598, 402, 0, 616, 410, 369, - 503, 404, 581, 434, 0, 609, 676, 610, 522, 523, - 737, 742, 738, 739, 741, 762, 493, 424, 430, 539, - 444, 466, 525, 580, 501, 530, 367, 570, 541, 472, - 661, 689, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 325, 326, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 718, - 717, 716, 715, 714, 713, 712, 711, 0, 0, 658, - 384, 781, 775, 782, 763, 766, 765, 740, 0, 341, - 638, 464, 518, 401, 705, 706, 0, 761, 284, 285, - 286, 287, 288, 289, 290, 291, 332, 292, 293, 294, - 295, 296, 297, 298, 303, 304, 305, 306, 307, 308, - 309, 310, 708, 301, 302, 311, 312, 313, 314, 315, - 316, 317, 318, 319, 320, 321, 322, 323, 324, 0, - 0, 0, 0, 333, 767, 768, 769, 770, 771, 0, - 0, 334, 335, 336, 0, 0, 299, 300, 327, 549, - 328, 329, 330, 331, 0, 0, 587, 588, 589, 612, - 0, 590, 572, 637, 411, 342, 553, 579, 778, 779, - 0, 0, 0, 0, 0, 0, 0, 688, 699, 735, - 736, 0, 749, 750, 752, 754, 753, 756, 546, 547, - 764, 0, 0, 435, 357, 358, 494, 495, 532, 0, - 436, 437, 438, 442, 439, 440, 441, 758, 759, 760, - 757, 468, 531, 554, 540, 777, 481, 0, 785, 627, - 628, 786, 745, 744, 343, 0, 337, 338, 702, 703, - 0, 0, 534, 480, 634, 500, 0, 0, 643, 677, - 666, 755, 630, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 394, 0, 0, 463, 681, 662, 673, - 663, 506, 648, 649, 650, 657, 406, 651, 787, 653, - 622, 654, 623, 655, 656, 0, 680, 629, 542, 482, - 0, 697, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 268, 0, 0, 0, 0, 0, 0, - 365, 269, 624, 751, 626, 625, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 368, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 543, 571, 0, 584, - 0, 432, 433, 0, 0, 0, 0, 0, 0, 0, - 350, 550, 1845, 366, 537, 582, 371, 545, 561, 361, - 499, 533, 0, 0, 352, 566, 544, 477, 351, 0, - 526, 391, 408, 388, 497, 0, 0, 565, 595, 387, - 585, 0, 576, 354, 0, 575, 496, 469, 562, 567, - 478, 471, 0, 353, 564, 476, 470, 446, 398, 611, - 447, 448, 449, 450, 451, 452, 412, 512, 467, 513, - 413, 484, 483, 485, 414, 415, 416, 417, 418, 419, - 420, 421, 422, 423, 0, 0, 0, 0, 0, 606, - 607, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 743, 0, 0, 748, - 0, 578, 0, 0, 0, 0, 0, 0, 548, 0, - 0, 453, 0, 0, 0, 596, 0, 529, 502, 789, - 0, 0, 527, 465, 563, 514, 569, 551, 577, 521, - 515, 344, 552, 390, 479, 362, 364, 776, 392, 395, - 399, 400, 490, 491, 508, 536, 555, 556, 557, 389, - 376, 528, 377, 409, 378, 345, 381, 380, 382, 538, - 383, 347, 509, 560, 0, 405, 524, 474, 348, 473, - 510, 559, 558, 363, 586, 593, 594, 685, 0, 599, - 790, 791, 792, 608, 0, 516, 359, 356, 0, 0, - 0, 385, 511, 372, 374, 375, 373, 505, 507, 613, - 614, 615, 617, 0, 618, 619, 0, 0, 0, 0, - 620, 686, 704, 670, 639, 601, 694, 636, 640, 641, - 426, 427, 428, 429, 707, 0, 0, 0, 592, 454, - 455, 0, 397, 396, 475, 349, 0, 0, 443, 425, - 517, 355, 393, 445, 431, 456, 457, 458, 459, 460, - 0, 461, 462, 403, 339, 340, 784, 386, 498, 709, - 746, 747, 631, 0, 695, 632, 642, 379, 667, 679, - 678, 492, 591, 0, 690, 693, 621, 783, 0, 687, - 701, 788, 700, 780, 504, 0, 535, 698, 645, 0, - 691, 664, 665, 0, 692, 660, 696, 0, 633, 0, - 602, 605, 635, 722, 723, 724, 346, 604, 726, 727, - 728, 729, 730, 731, 732, 725, 574, 668, 644, 671, - 583, 647, 646, 0, 0, 682, 600, 683, 684, 486, - 487, 488, 489, 407, 710, 370, 603, 519, 520, 0, - 669, 0, 0, 0, 0, 0, 0, 0, 0, 674, - 675, 672, 793, 0, 733, 734, 0, 0, 597, 598, - 402, 0, 616, 410, 369, 503, 404, 581, 434, 0, - 609, 676, 610, 522, 523, 737, 742, 738, 739, 741, - 762, 493, 424, 430, 539, 444, 466, 525, 580, 501, - 530, 367, 570, 541, 472, 661, 689, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 325, 326, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 718, 717, 716, 715, 714, 713, - 712, 711, 0, 0, 658, 384, 781, 775, 782, 763, - 766, 765, 740, 0, 341, 638, 464, 518, 401, 705, - 706, 0, 761, 284, 285, 286, 287, 288, 289, 290, - 291, 332, 292, 293, 294, 295, 296, 297, 298, 303, - 304, 305, 306, 307, 308, 309, 310, 708, 301, 302, - 311, 312, 313, 314, 315, 316, 317, 318, 319, 320, - 321, 322, 323, 324, 0, 0, 0, 0, 333, 767, - 768, 769, 770, 771, 0, 0, 334, 335, 336, 0, - 0, 299, 300, 327, 549, 328, 329, 330, 331, 0, - 0, 587, 588, 589, 612, 0, 590, 572, 637, 411, - 342, 553, 579, 778, 779, 0, 0, 0, 0, 0, - 0, 0, 688, 699, 735, 736, 0, 749, 750, 752, - 754, 753, 756, 546, 547, 764, 0, 0, 435, 357, - 358, 494, 495, 532, 0, 436, 437, 438, 442, 439, - 440, 441, 758, 759, 760, 757, 468, 531, 554, 540, - 777, 481, 0, 785, 627, 628, 786, 745, 744, 343, - 0, 337, 338, 702, 703, 0, 0, 534, 480, 634, - 500, 0, 0, 643, 677, 666, 755, 630, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 394, 0, - 0, 463, 681, 662, 673, 663, 506, 648, 649, 650, - 657, 406, 651, 787, 653, 622, 654, 623, 655, 656, - 0, 680, 629, 542, 482, 0, 697, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 268, 0, - 0, 0, 0, 0, 0, 365, 269, 624, 751, 626, - 625, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 368, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 543, 571, 0, 584, 0, 432, 433, 0, 0, - 0, 0, 0, 0, 0, 350, 550, 1843, 366, 537, - 582, 371, 545, 561, 361, 499, 533, 0, 0, 352, - 566, 544, 477, 351, 0, 526, 391, 408, 388, 497, - 0, 0, 565, 595, 387, 585, 0, 576, 354, 0, - 575, 496, 469, 562, 567, 478, 471, 0, 353, 564, - 476, 470, 446, 398, 611, 447, 448, 449, 450, 451, - 452, 412, 512, 467, 513, 413, 484, 483, 485, 414, - 415, 416, 417, 418, 419, 420, 421, 422, 423, 0, - 0, 0, 0, 0, 606, 607, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 743, 0, 0, 748, 0, 578, 0, 0, 0, - 0, 0, 0, 548, 0, 0, 453, 0, 0, 0, - 596, 0, 529, 502, 789, 0, 0, 527, 465, 563, - 514, 569, 551, 577, 521, 515, 344, 552, 390, 479, - 362, 364, 776, 392, 395, 399, 400, 490, 491, 508, - 536, 555, 556, 557, 389, 376, 528, 377, 409, 378, - 345, 381, 380, 382, 538, 383, 347, 509, 560, 0, - 405, 524, 474, 348, 473, 510, 559, 558, 363, 586, - 593, 594, 685, 0, 599, 790, 791, 792, 608, 0, - 516, 359, 356, 0, 0, 0, 385, 511, 372, 374, - 375, 373, 505, 507, 613, 614, 615, 617, 0, 618, - 619, 0, 0, 0, 0, 620, 686, 704, 670, 639, - 601, 694, 636, 640, 641, 426, 427, 428, 429, 707, - 0, 0, 0, 592, 454, 455, 0, 397, 396, 475, - 349, 0, 0, 443, 425, 517, 355, 393, 445, 431, - 456, 457, 458, 459, 460, 0, 461, 462, 403, 339, - 340, 784, 386, 498, 709, 746, 747, 631, 0, 695, - 632, 642, 379, 667, 679, 678, 492, 591, 0, 690, - 693, 621, 783, 0, 687, 701, 788, 700, 780, 504, - 0, 535, 698, 645, 0, 691, 664, 665, 0, 692, - 660, 696, 0, 633, 0, 602, 605, 635, 722, 723, - 724, 346, 604, 726, 727, 728, 729, 730, 731, 732, - 725, 574, 668, 644, 671, 583, 647, 646, 0, 0, - 682, 600, 683, 684, 486, 487, 488, 489, 407, 710, - 370, 603, 519, 520, 0, 669, 0, 0, 0, 0, - 0, 0, 0, 0, 674, 675, 672, 793, 0, 733, - 734, 0, 0, 597, 598, 402, 0, 616, 410, 369, - 503, 404, 581, 434, 0, 609, 676, 610, 522, 523, - 737, 742, 738, 739, 741, 762, 493, 424, 430, 539, - 444, 466, 525, 580, 501, 530, 367, 570, 541, 472, - 661, 689, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 325, 326, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 718, - 717, 716, 715, 714, 713, 712, 711, 0, 0, 658, - 384, 781, 775, 782, 763, 766, 765, 740, 0, 341, - 638, 464, 518, 401, 705, 706, 0, 761, 284, 285, - 286, 287, 288, 289, 290, 291, 332, 292, 293, 294, - 295, 296, 297, 298, 303, 304, 305, 306, 307, 308, - 309, 310, 708, 301, 302, 311, 312, 313, 314, 315, - 316, 317, 318, 319, 320, 321, 322, 323, 324, 0, - 0, 0, 0, 333, 767, 768, 769, 770, 771, 0, - 0, 334, 335, 336, 0, 0, 299, 300, 327, 549, - 328, 329, 330, 331, 0, 0, 587, 588, 589, 612, - 0, 590, 572, 637, 411, 342, 553, 579, 778, 779, - 0, 0, 0, 0, 0, 0, 0, 688, 699, 735, - 736, 0, 749, 750, 752, 754, 753, 756, 546, 547, - 764, 0, 0, 435, 357, 358, 494, 495, 532, 0, - 436, 437, 438, 442, 439, 440, 441, 758, 759, 760, - 757, 468, 531, 554, 540, 777, 481, 0, 785, 627, - 628, 786, 745, 744, 343, 0, 337, 338, 702, 703, - 0, 0, 534, 480, 634, 500, 0, 0, 643, 677, - 666, 755, 630, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 394, 0, 0, 463, 681, 662, 673, - 663, 506, 648, 649, 650, 657, 406, 651, 787, 653, - 622, 654, 623, 655, 656, 0, 680, 629, 542, 482, - 0, 697, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 268, 0, 0, 0, 0, 0, 0, - 365, 269, 624, 751, 626, 625, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 368, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 543, 571, 0, 584, - 0, 432, 433, 0, 0, 0, 0, 0, 0, 0, - 350, 550, 568, 366, 537, 582, 371, 545, 1754, 361, - 499, 533, 0, 0, 352, 566, 544, 477, 351, 0, - 526, 391, 408, 388, 497, 0, 0, 565, 595, 387, - 585, 0, 576, 354, 0, 575, 496, 469, 562, 567, - 478, 471, 0, 353, 564, 476, 470, 446, 398, 611, - 447, 448, 449, 450, 451, 452, 412, 512, 467, 513, - 413, 484, 483, 485, 414, 415, 416, 417, 418, 419, - 420, 421, 422, 423, 0, 0, 0, 0, 0, 606, - 607, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 743, 0, 0, 748, - 0, 578, 0, 0, 0, 0, 0, 0, 548, 0, - 0, 453, 0, 0, 0, 596, 0, 529, 502, 789, - 0, 0, 527, 465, 563, 514, 569, 551, 577, 521, - 515, 344, 552, 390, 479, 362, 364, 776, 392, 395, - 399, 400, 490, 491, 508, 536, 555, 556, 557, 389, - 376, 528, 377, 409, 378, 345, 381, 380, 382, 538, - 383, 347, 509, 560, 0, 405, 524, 474, 348, 473, - 510, 559, 558, 363, 586, 593, 594, 685, 0, 599, - 790, 791, 792, 608, 0, 516, 359, 356, 0, 0, - 0, 385, 511, 372, 374, 375, 373, 505, 507, 613, - 614, 615, 617, 0, 618, 619, 0, 0, 0, 0, - 620, 686, 704, 670, 639, 601, 694, 636, 640, 641, - 426, 427, 428, 429, 707, 0, 0, 0, 592, 454, - 455, 0, 397, 396, 475, 349, 0, 0, 443, 425, - 517, 355, 393, 445, 431, 456, 457, 458, 459, 460, - 0, 461, 462, 403, 339, 340, 784, 386, 498, 709, - 746, 747, 631, 0, 695, 632, 642, 379, 667, 679, - 678, 492, 591, 0, 690, 693, 621, 783, 0, 687, - 701, 788, 700, 780, 504, 0, 535, 698, 645, 0, - 691, 664, 665, 0, 692, 660, 696, 0, 633, 0, - 602, 605, 635, 722, 723, 724, 346, 604, 726, 727, - 728, 729, 730, 731, 732, 725, 574, 668, 644, 671, - 583, 647, 646, 0, 0, 682, 600, 683, 684, 486, - 487, 488, 489, 407, 710, 370, 603, 519, 520, 0, - 669, 0, 0, 0, 0, 0, 0, 0, 0, 674, - 675, 672, 793, 0, 733, 734, 0, 0, 597, 598, - 402, 0, 616, 410, 369, 503, 404, 581, 434, 0, - 609, 676, 610, 522, 523, 737, 742, 738, 739, 741, - 762, 493, 424, 430, 539, 444, 466, 525, 580, 501, - 530, 367, 570, 541, 472, 661, 689, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 325, 326, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 718, 717, 716, 715, 714, 713, - 712, 711, 0, 0, 658, 384, 781, 775, 782, 763, - 766, 765, 740, 0, 341, 638, 464, 518, 401, 705, - 706, 0, 761, 284, 285, 286, 287, 288, 289, 290, - 291, 332, 292, 293, 294, 295, 296, 297, 298, 303, - 304, 305, 306, 307, 308, 309, 310, 708, 301, 302, - 311, 312, 313, 314, 315, 316, 317, 318, 319, 320, - 321, 322, 323, 324, 0, 0, 0, 0, 333, 767, - 768, 769, 770, 771, 0, 0, 334, 335, 336, 0, - 0, 299, 300, 327, 549, 328, 329, 330, 331, 0, - 0, 587, 588, 589, 612, 0, 590, 572, 637, 411, - 342, 553, 579, 778, 779, 0, 0, 0, 0, 0, - 0, 0, 688, 699, 735, 736, 0, 749, 750, 752, - 754, 753, 756, 546, 547, 764, 0, 0, 435, 357, - 358, 494, 495, 532, 0, 436, 437, 438, 442, 439, - 440, 441, 758, 759, 760, 757, 468, 531, 554, 540, - 777, 481, 0, 785, 627, 628, 786, 745, 744, 343, - 0, 337, 338, 702, 703, 0, 0, 534, 480, 634, - 500, 0, 0, 643, 677, 666, 755, 630, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 394, 0, - 0, 463, 681, 662, 673, 663, 506, 648, 649, 650, - 657, 406, 651, 787, 653, 622, 654, 623, 655, 656, - 0, 680, 629, 542, 482, 0, 697, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 268, 0, - 0, 0, 0, 0, 0, 365, 269, 624, 751, 626, - 625, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 368, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 543, 571, 0, 584, 0, 432, 433, 0, 0, - 0, 0, 0, 0, 0, 350, 550, 568, 366, 537, - 582, 371, 545, 561, 361, 499, 533, 0, 0, 352, - 566, 544, 477, 351, 0, 526, 391, 408, 388, 497, - 0, 0, 565, 595, 387, 585, 0, 576, 354, 0, - 575, 496, 469, 562, 567, 478, 471, 0, 353, 564, - 476, 470, 446, 398, 611, 447, 448, 449, 450, 451, - 452, 412, 512, 467, 513, 413, 484, 483, 485, 414, - 415, 416, 417, 418, 419, 420, 421, 422, 423, 0, - 0, 0, 0, 0, 606, 607, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 743, 0, 0, 748, 0, 578, 0, 0, 0, - 0, 0, 0, 548, 0, 0, 453, 0, 0, 0, - 596, 0, 529, 502, 789, 0, 0, 527, 465, 563, - 514, 569, 551, 577, 899, 515, 344, 552, 390, 479, - 362, 364, 776, 392, 395, 399, 400, 490, 491, 508, - 536, 555, 556, 557, 389, 376, 528, 377, 409, 378, - 345, 381, 380, 382, 538, 383, 347, 509, 560, 0, - 405, 524, 474, 348, 473, 510, 559, 558, 363, 586, - 593, 594, 685, 0, 599, 790, 791, 792, 608, 0, - 516, 359, 356, 0, 0, 0, 385, 511, 372, 374, - 375, 373, 505, 507, 613, 614, 615, 617, 0, 618, - 619, 0, 0, 0, 0, 620, 686, 704, 670, 639, - 601, 694, 636, 640, 641, 426, 427, 428, 429, 707, - 0, 0, 0, 592, 454, 455, 0, 397, 396, 475, - 349, 0, 0, 443, 425, 517, 355, 393, 445, 431, - 456, 457, 458, 459, 460, 0, 461, 462, 403, 339, - 340, 784, 386, 498, 709, 746, 747, 631, 0, 695, - 632, 642, 379, 667, 679, 678, 492, 591, 0, 690, - 693, 621, 783, 0, 687, 701, 788, 700, 780, 504, - 0, 535, 698, 645, 0, 691, 664, 665, 0, 692, - 660, 696, 0, 633, 0, 602, 605, 635, 722, 723, - 724, 346, 604, 726, 727, 728, 729, 730, 731, 900, - 725, 574, 668, 644, 671, 583, 647, 646, 0, 0, - 682, 600, 683, 684, 486, 487, 488, 489, 407, 710, - 370, 603, 519, 520, 0, 669, 0, 0, 0, 0, - 0, 0, 0, 0, 674, 675, 672, 793, 0, 733, - 734, 0, 0, 597, 598, 402, 0, 616, 410, 369, - 503, 404, 581, 434, 0, 609, 676, 610, 522, 523, - 737, 742, 738, 739, 741, 762, 493, 424, 430, 539, - 444, 466, 525, 580, 501, 530, 367, 570, 541, 472, - 661, 689, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 325, 326, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 718, - 717, 716, 715, 714, 713, 712, 711, 0, 0, 658, - 384, 781, 775, 782, 763, 766, 765, 740, 0, 341, - 638, 464, 518, 401, 705, 706, 0, 761, 284, 285, - 286, 287, 288, 289, 290, 291, 332, 292, 293, 294, - 295, 296, 297, 298, 303, 304, 305, 306, 307, 308, - 309, 310, 708, 301, 302, 311, 312, 313, 314, 315, - 316, 317, 318, 319, 320, 321, 322, 323, 324, 0, - 0, 0, 0, 333, 767, 768, 769, 770, 771, 0, - 0, 334, 335, 336, 0, 0, 299, 300, 327, 549, - 328, 329, 330, 331, 0, 0, 587, 588, 589, 612, - 0, 590, 572, 637, 411, 342, 553, 579, 778, 779, - 0, 0, 0, 0, 0, 0, 0, 688, 699, 735, - 736, 0, 749, 750, 752, 754, 753, 756, 546, 547, - 764, 0, 0, 435, 357, 358, 494, 495, 532, 0, - 436, 437, 438, 442, 439, 440, 441, 758, 759, 760, - 757, 468, 531, 554, 540, 777, 481, 0, 785, 627, - 628, 786, 745, 744, 343, 0, 337, 338, 702, 703, - 0, 0, 534, 480, 634, 500, 0, 0, 643, 677, - 666, 755, 630, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 394, 0, 0, 463, 681, 662, 673, - 663, 506, 648, 649, 650, 657, 406, 651, 787, 653, - 622, 654, 623, 655, 656, 0, 680, 629, 542, 482, - 0, 697, 0, 0, 0, 0, 0, 0, 0, 0, + 325, 326, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 722, 721, 720, 719, + 718, 717, 716, 715, 0, 0, 662, 385, 785, 779, + 786, 767, 770, 769, 744, 0, 341, 642, 466, 520, + 402, 709, 710, 0, 765, 284, 285, 286, 287, 288, + 289, 290, 291, 332, 292, 293, 294, 295, 296, 297, + 298, 303, 304, 305, 306, 307, 308, 309, 310, 712, + 301, 302, 311, 312, 313, 314, 315, 316, 317, 318, + 319, 320, 321, 322, 323, 324, 0, 0, 0, 0, + 333, 771, 772, 773, 774, 775, 0, 0, 334, 335, + 336, 0, 0, 299, 300, 327, 552, 328, 329, 330, + 331, 0, 0, 591, 592, 593, 616, 0, 594, 575, + 641, 412, 342, 556, 583, 782, 783, 0, 0, 0, + 0, 0, 0, 0, 692, 703, 739, 740, 0, 753, + 754, 756, 758, 757, 760, 549, 550, 768, 0, 0, + 437, 358, 359, 496, 497, 535, 0, 438, 439, 440, + 444, 441, 442, 443, 762, 763, 764, 761, 470, 534, + 557, 543, 781, 483, 0, 789, 631, 632, 790, 749, + 748, 343, 0, 337, 338, 706, 707, 0, 0, 537, + 482, 638, 502, 0, 0, 647, 681, 670, 759, 634, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 395, 0, 0, 465, 685, 666, 677, 667, 508, 652, + 653, 654, 661, 407, 655, 791, 657, 626, 658, 627, + 659, 660, 0, 684, 633, 545, 484, 0, 701, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 268, 0, 0, 0, 0, 0, 0, 366, 269, 628, + 755, 630, 629, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 369, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 546, 574, 0, 588, 0, 434, 435, + 0, 0, 0, 0, 0, 0, 0, 350, 553, 1849, + 367, 540, 586, 372, 548, 564, 362, 501, 536, 0, + 0, 352, 569, 547, 479, 351, 0, 529, 392, 409, + 389, 499, 0, 0, 568, 599, 388, 589, 0, 579, + 354, 0, 578, 498, 471, 565, 570, 480, 473, 0, + 353, 567, 478, 472, 448, 399, 615, 449, 450, 451, + 452, 453, 454, 413, 514, 469, 515, 414, 486, 485, + 487, 415, 416, 417, 418, 419, 420, 421, 422, 423, + 424, 0, 0, 0, 0, 0, 610, 611, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 747, 0, 0, 752, 0, 582, 0, + 0, 0, 0, 0, 0, 551, 0, 0, 455, 0, + 0, 0, 600, 0, 532, 504, 793, 0, 0, 530, + 467, 566, 516, 572, 554, 580, 523, 517, 344, 555, + 391, 481, 363, 365, 780, 393, 396, 400, 401, 492, + 493, 510, 539, 558, 559, 560, 390, 377, 531, 378, + 410, 379, 345, 382, 381, 383, 541, 384, 347, 511, + 563, 0, 406, 527, 476, 348, 475, 512, 562, 561, + 364, 590, 597, 598, 689, 0, 603, 794, 795, 796, + 612, 0, 518, 360, 357, 0, 0, 0, 386, 513, + 373, 375, 376, 374, 507, 509, 617, 618, 619, 621, + 0, 622, 623, 0, 0, 0, 0, 624, 690, 708, + 674, 643, 605, 698, 640, 644, 645, 427, 428, 429, + 430, 711, 0, 0, 0, 596, 456, 457, 0, 398, + 397, 477, 349, 0, 0, 445, 426, 519, 355, 394, + 447, 432, 458, 459, 460, 461, 462, 0, 463, 464, + 404, 339, 340, 788, 387, 500, 713, 750, 751, 635, + 0, 699, 636, 646, 380, 671, 683, 682, 494, 595, + 0, 694, 697, 625, 787, 0, 691, 705, 792, 704, + 784, 506, 0, 538, 702, 649, 0, 695, 668, 669, + 0, 696, 664, 700, 356, 433, 524, 581, 0, 637, + 0, 606, 609, 639, 726, 727, 728, 346, 608, 730, + 731, 732, 733, 734, 735, 736, 729, 577, 672, 648, + 675, 587, 651, 650, 0, 0, 686, 604, 687, 688, + 488, 489, 490, 491, 408, 714, 371, 607, 521, 522, + 0, 673, 0, 0, 0, 0, 0, 0, 0, 0, + 678, 679, 676, 797, 0, 737, 738, 0, 0, 601, + 602, 403, 0, 620, 411, 370, 505, 405, 585, 436, + 0, 613, 680, 614, 525, 526, 741, 746, 742, 743, + 745, 766, 495, 425, 431, 542, 446, 468, 528, 584, + 503, 533, 368, 573, 544, 474, 665, 693, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 325, + 326, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 722, 721, 720, 719, 718, + 717, 716, 715, 0, 0, 662, 385, 785, 779, 786, + 767, 770, 769, 744, 0, 341, 642, 466, 520, 402, + 709, 710, 0, 765, 284, 285, 286, 287, 288, 289, + 290, 291, 332, 292, 293, 294, 295, 296, 297, 298, + 303, 304, 305, 306, 307, 308, 309, 310, 712, 301, + 302, 311, 312, 313, 314, 315, 316, 317, 318, 319, + 320, 321, 322, 323, 324, 0, 0, 0, 0, 333, + 771, 772, 773, 774, 775, 0, 0, 334, 335, 336, + 0, 0, 299, 300, 327, 552, 328, 329, 330, 331, + 0, 0, 591, 592, 593, 616, 0, 594, 575, 641, + 412, 342, 556, 583, 782, 783, 0, 0, 0, 0, + 0, 0, 0, 692, 703, 739, 740, 0, 753, 754, + 756, 758, 757, 760, 549, 550, 768, 0, 0, 437, + 358, 359, 496, 497, 535, 0, 438, 439, 440, 444, + 441, 442, 443, 762, 763, 764, 761, 470, 534, 557, + 543, 781, 483, 0, 789, 631, 632, 790, 749, 748, + 343, 0, 337, 338, 706, 707, 0, 0, 537, 482, + 638, 502, 0, 0, 647, 681, 670, 759, 634, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 395, + 0, 0, 465, 685, 666, 677, 667, 508, 652, 653, + 654, 661, 407, 655, 791, 657, 626, 658, 627, 659, + 660, 0, 684, 633, 545, 484, 0, 701, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 268, 0, 0, 0, 0, 0, 0, - 365, 269, 624, 751, 626, 625, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 368, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 543, 571, 0, 584, - 0, 432, 433, 0, 0, 0, 0, 0, 0, 0, - 350, 550, 568, 366, 537, 582, 371, 545, 561, 361, - 499, 533, 0, 0, 352, 566, 544, 477, 351, 0, - 526, 391, 408, 388, 497, 0, 0, 565, 595, 387, - 585, 0, 576, 354, 0, 575, 496, 469, 562, 567, - 478, 471, 0, 353, 564, 476, 470, 446, 398, 611, - 447, 448, 449, 450, 451, 452, 412, 512, 467, 513, - 413, 484, 483, 485, 414, 415, 416, 417, 418, 419, - 420, 421, 422, 423, 0, 0, 0, 0, 0, 606, - 607, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 743, 0, 0, 748, - 0, 578, 0, 0, 0, 0, 0, 0, 548, 0, - 0, 453, 0, 0, 0, 596, 0, 529, 502, 789, - 0, 0, 527, 465, 563, 514, 569, 551, 577, 521, - 515, 344, 552, 390, 479, 362, 364, 776, 392, 395, - 399, 400, 490, 491, 508, 536, 555, 556, 557, 389, - 376, 528, 377, 409, 378, 345, 381, 380, 382, 538, - 383, 347, 509, 560, 0, 405, 524, 474, 348, 473, - 510, 559, 558, 363, 586, 593, 594, 685, 0, 599, - 790, 791, 792, 608, 0, 516, 359, 356, 0, 0, - 0, 385, 511, 372, 374, 375, 373, 505, 507, 613, - 614, 615, 617, 0, 618, 619, 0, 0, 0, 0, - 620, 686, 704, 670, 639, 601, 694, 636, 640, 641, - 426, 427, 428, 429, 707, 0, 0, 0, 592, 454, - 455, 0, 397, 396, 475, 349, 0, 0, 443, 425, - 517, 355, 393, 445, 431, 456, 457, 458, 459, 460, - 0, 461, 462, 403, 339, 340, 784, 386, 498, 709, - 746, 747, 631, 0, 695, 632, 642, 379, 667, 679, - 678, 492, 591, 0, 690, 693, 621, 783, 0, 687, - 701, 788, 700, 780, 504, 0, 535, 698, 645, 0, - 691, 664, 665, 0, 692, 660, 696, 0, 633, 0, - 602, 605, 635, 722, 723, 724, 346, 604, 726, 727, - 728, 729, 730, 731, 732, 725, 574, 668, 644, 671, - 583, 647, 646, 0, 0, 682, 600, 683, 684, 486, - 487, 488, 489, 407, 710, 370, 603, 519, 520, 0, - 669, 0, 0, 0, 0, 0, 0, 0, 0, 674, - 675, 672, 793, 0, 733, 734, 0, 0, 597, 598, - 402, 0, 616, 410, 369, 503, 404, 581, 434, 0, - 609, 676, 610, 522, 523, 737, 742, 738, 739, 741, - 762, 493, 424, 430, 539, 444, 466, 525, 580, 501, - 530, 367, 570, 541, 472, 661, 689, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 268, + 0, 0, 0, 0, 0, 0, 366, 269, 628, 755, + 630, 629, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 369, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 546, 574, 0, 588, 0, 434, 435, 0, + 0, 0, 0, 0, 0, 0, 350, 553, 1847, 367, + 540, 586, 372, 548, 564, 362, 501, 536, 0, 0, + 352, 569, 547, 479, 351, 0, 529, 392, 409, 389, + 499, 0, 0, 568, 599, 388, 589, 0, 579, 354, + 0, 578, 498, 471, 565, 570, 480, 473, 0, 353, + 567, 478, 472, 448, 399, 615, 449, 450, 451, 452, + 453, 454, 413, 514, 469, 515, 414, 486, 485, 487, + 415, 416, 417, 418, 419, 420, 421, 422, 423, 424, + 0, 0, 0, 0, 0, 610, 611, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 747, 0, 0, 752, 0, 582, 0, 0, + 0, 0, 0, 0, 551, 0, 0, 455, 0, 0, + 0, 600, 0, 532, 504, 793, 0, 0, 530, 467, + 566, 516, 572, 554, 580, 523, 517, 344, 555, 391, + 481, 363, 365, 780, 393, 396, 400, 401, 492, 493, + 510, 539, 558, 559, 560, 390, 377, 531, 378, 410, + 379, 345, 382, 381, 383, 541, 384, 347, 511, 563, + 0, 406, 527, 476, 348, 475, 512, 562, 561, 364, + 590, 597, 598, 689, 0, 603, 794, 795, 796, 612, + 0, 518, 360, 357, 0, 0, 0, 386, 513, 373, + 375, 376, 374, 507, 509, 617, 618, 619, 621, 0, + 622, 623, 0, 0, 0, 0, 624, 690, 708, 674, + 643, 605, 698, 640, 644, 645, 427, 428, 429, 430, + 711, 0, 0, 0, 596, 456, 457, 0, 398, 397, + 477, 349, 0, 0, 445, 426, 519, 355, 394, 447, + 432, 458, 459, 460, 461, 462, 0, 463, 464, 404, + 339, 340, 788, 387, 500, 713, 750, 751, 635, 0, + 699, 636, 646, 380, 671, 683, 682, 494, 595, 0, + 694, 697, 625, 787, 0, 691, 705, 792, 704, 784, + 506, 0, 538, 702, 649, 0, 695, 668, 669, 0, + 696, 664, 700, 356, 433, 524, 581, 0, 637, 0, + 606, 609, 639, 726, 727, 728, 346, 608, 730, 731, + 732, 733, 734, 735, 736, 729, 577, 672, 648, 675, + 587, 651, 650, 0, 0, 686, 604, 687, 688, 488, + 489, 490, 491, 408, 714, 371, 607, 521, 522, 0, + 673, 0, 0, 0, 0, 0, 0, 0, 0, 678, + 679, 676, 797, 0, 737, 738, 0, 0, 601, 602, + 403, 0, 620, 411, 370, 505, 405, 585, 436, 0, + 613, 680, 614, 525, 526, 741, 746, 742, 743, 745, + 766, 495, 425, 431, 542, 446, 468, 528, 584, 503, + 533, 368, 573, 544, 474, 665, 693, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 325, 326, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 718, 717, 716, 715, 714, 713, - 712, 711, 0, 0, 658, 384, 781, 775, 782, 763, - 766, 765, 836, 0, 341, 638, 464, 518, 401, 705, - 706, 0, 761, 284, 285, 286, 287, 288, 289, 290, + 0, 0, 0, 0, 722, 721, 720, 719, 718, 717, + 716, 715, 0, 0, 662, 385, 785, 779, 786, 767, + 770, 769, 744, 0, 341, 642, 466, 520, 402, 709, + 710, 0, 765, 284, 285, 286, 287, 288, 289, 290, 291, 332, 292, 293, 294, 295, 296, 297, 298, 303, - 304, 305, 306, 307, 308, 309, 310, 708, 301, 302, + 304, 305, 306, 307, 308, 309, 310, 712, 301, 302, 311, 312, 313, 314, 315, 316, 317, 318, 319, 320, - 321, 322, 323, 324, 0, 0, 0, 0, 333, 767, - 768, 769, 770, 771, 0, 0, 334, 335, 336, 0, - 0, 299, 300, 327, 549, 328, 329, 330, 331, 0, - 0, 587, 588, 589, 612, 0, 590, 572, 637, 411, - 342, 553, 579, 778, 779, 0, 0, 0, 0, 0, - 0, 0, 688, 699, 735, 736, 0, 749, 750, 752, - 754, 753, 756, 546, 547, 764, 0, 0, 435, 357, - 358, 494, 495, 532, 0, 436, 437, 438, 442, 439, - 440, 441, 758, 759, 760, 757, 468, 531, 554, 540, - 777, 481, 0, 785, 627, 628, 786, 745, 744, 343, - 0, 337, 338, 702, 703, 0, 0, 534, 480, 634, - 500, 0, 0, 643, 677, 666, 755, 630, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 394, 0, - 0, 463, 681, 662, 673, 663, 506, 648, 649, 650, - 657, 406, 651, 787, 653, 622, 654, 623, 655, 656, - 0, 680, 629, 542, 482, 0, 697, 0, 0, 0, + 321, 322, 323, 324, 0, 0, 0, 0, 333, 771, + 772, 773, 774, 775, 0, 0, 334, 335, 336, 0, + 0, 299, 300, 327, 552, 328, 329, 330, 331, 0, + 0, 591, 592, 593, 616, 0, 594, 575, 641, 412, + 342, 556, 583, 782, 783, 0, 0, 0, 0, 0, + 0, 0, 692, 703, 739, 740, 0, 753, 754, 756, + 758, 757, 760, 549, 550, 768, 0, 0, 437, 358, + 359, 496, 497, 535, 0, 438, 439, 440, 444, 441, + 442, 443, 762, 763, 764, 761, 470, 534, 557, 543, + 781, 483, 0, 789, 631, 632, 790, 749, 748, 343, + 0, 337, 338, 706, 707, 0, 0, 537, 482, 638, + 502, 0, 0, 647, 681, 670, 759, 634, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 395, 0, + 0, 465, 685, 666, 677, 667, 508, 652, 653, 654, + 661, 407, 655, 791, 657, 626, 658, 627, 659, 660, + 0, 684, 633, 545, 484, 0, 701, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 268, 0, - 0, 0, 0, 0, 0, 365, 269, 624, 751, 626, - 625, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 368, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 543, 571, 0, 584, 0, 432, 433, 0, 0, - 0, 0, 0, 0, 0, 350, 550, 568, 366, 537, - 582, 371, 545, 561, 361, 499, 533, 0, 0, 352, - 566, 544, 477, 351, 0, 526, 391, 408, 388, 497, - 0, 0, 565, 595, 387, 585, 0, 576, 354, 0, - 575, 496, 469, 562, 567, 478, 471, 0, 353, 564, - 476, 470, 446, 398, 611, 447, 448, 449, 450, 451, - 452, 412, 512, 467, 513, 413, 484, 483, 485, 414, - 415, 416, 417, 418, 419, 420, 421, 422, 423, 0, - 0, 0, 0, 0, 606, 607, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 743, 0, 0, 748, 0, 578, 0, 0, 0, - 0, 0, 0, 548, 0, 0, 453, 0, 0, 0, - 596, 0, 529, 502, 789, 0, 0, 527, 465, 563, - 514, 569, 551, 577, 521, 515, 344, 552, 390, 479, - 362, 364, 776, 392, 395, 399, 400, 490, 491, 508, - 536, 555, 556, 557, 389, 376, 528, 377, 409, 378, - 345, 381, 380, 382, 538, 383, 347, 509, 560, 0, - 405, 0, 474, 348, 473, 510, 559, 558, 363, 586, - 593, 594, 685, 0, 599, 790, 791, 792, 608, 0, - 516, 359, 356, 0, 0, 0, 385, 511, 372, 374, - 375, 373, 505, 507, 613, 614, 615, 617, 0, 618, - 619, 0, 0, 0, 0, 620, 686, 704, 670, 639, - 601, 694, 636, 640, 641, 426, 427, 428, 429, 707, - 0, 0, 0, 592, 454, 455, 0, 397, 396, 475, - 349, 0, 0, 443, 425, 517, 355, 393, 445, 431, - 456, 457, 458, 459, 460, 0, 461, 462, 403, 339, - 340, 784, 386, 498, 709, 746, 747, 631, 0, 695, - 632, 642, 379, 667, 679, 678, 492, 591, 0, 690, - 693, 621, 783, 0, 687, 701, 788, 700, 780, 504, - 0, 535, 698, 645, 0, 691, 664, 665, 0, 692, - 660, 696, 0, 633, 0, 602, 605, 635, 722, 723, - 724, 346, 604, 726, 727, 728, 729, 730, 731, 732, - 725, 574, 668, 644, 671, 583, 647, 646, 0, 0, - 682, 600, 683, 684, 486, 487, 488, 489, 407, 710, - 370, 603, 519, 520, 0, 669, 0, 0, 0, 0, - 0, 0, 0, 0, 674, 675, 672, 793, 0, 733, - 734, 0, 0, 597, 598, 402, 0, 616, 410, 369, - 503, 404, 581, 434, 0, 609, 676, 610, 522, 523, - 737, 742, 738, 739, 741, 762, 493, 424, 430, 539, - 444, 466, 525, 580, 501, 530, 367, 570, 541, 472, - 661, 689, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 366, 269, 628, 755, 630, + 629, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 369, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 546, 574, 0, 588, 0, 434, 435, 0, 0, + 0, 0, 0, 0, 0, 350, 553, 571, 367, 540, + 586, 372, 548, 1758, 362, 501, 536, 0, 0, 352, + 569, 547, 479, 351, 0, 529, 392, 409, 389, 499, + 0, 0, 568, 599, 388, 589, 0, 579, 354, 0, + 578, 498, 471, 565, 570, 480, 473, 0, 353, 567, + 478, 472, 448, 399, 615, 449, 450, 451, 452, 453, + 454, 413, 514, 469, 515, 414, 486, 485, 487, 415, + 416, 417, 418, 419, 420, 421, 422, 423, 424, 0, + 0, 0, 0, 0, 610, 611, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 747, 0, 0, 752, 0, 582, 0, 0, 0, + 0, 0, 0, 551, 0, 0, 455, 0, 0, 0, + 600, 0, 532, 504, 793, 0, 0, 530, 467, 566, + 516, 572, 554, 580, 523, 517, 344, 555, 391, 481, + 363, 365, 780, 393, 396, 400, 401, 492, 493, 510, + 539, 558, 559, 560, 390, 377, 531, 378, 410, 379, + 345, 382, 381, 383, 541, 384, 347, 511, 563, 0, + 406, 527, 476, 348, 475, 512, 562, 561, 364, 590, + 597, 598, 689, 0, 603, 794, 795, 796, 612, 0, + 518, 360, 357, 0, 0, 0, 386, 513, 373, 375, + 376, 374, 507, 509, 617, 618, 619, 621, 0, 622, + 623, 0, 0, 0, 0, 624, 690, 708, 674, 643, + 605, 698, 640, 644, 645, 427, 428, 429, 430, 711, + 0, 0, 0, 596, 456, 457, 0, 398, 397, 477, + 349, 0, 0, 445, 426, 519, 355, 394, 447, 432, + 458, 459, 460, 461, 462, 0, 463, 464, 404, 339, + 340, 788, 387, 500, 713, 750, 751, 635, 0, 699, + 636, 646, 380, 671, 683, 682, 494, 595, 0, 694, + 697, 625, 787, 0, 691, 705, 792, 704, 784, 506, + 0, 538, 702, 649, 0, 695, 668, 669, 0, 696, + 664, 700, 356, 433, 524, 581, 0, 637, 0, 606, + 609, 639, 726, 727, 728, 346, 608, 730, 731, 732, + 733, 734, 735, 736, 729, 577, 672, 648, 675, 587, + 651, 650, 0, 0, 686, 604, 687, 688, 488, 489, + 490, 491, 408, 714, 371, 607, 521, 522, 0, 673, + 0, 0, 0, 0, 0, 0, 0, 0, 678, 679, + 676, 797, 0, 737, 738, 0, 0, 601, 602, 403, + 0, 620, 411, 370, 505, 405, 585, 436, 0, 613, + 680, 614, 525, 526, 741, 746, 742, 743, 745, 766, + 495, 425, 431, 542, 446, 468, 528, 584, 503, 533, + 368, 573, 544, 474, 665, 693, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 325, 326, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 722, 721, 720, 719, 718, 717, 716, + 715, 0, 0, 662, 385, 785, 779, 786, 767, 770, + 769, 744, 0, 341, 642, 466, 520, 402, 709, 710, + 0, 765, 284, 285, 286, 287, 288, 289, 290, 291, + 332, 292, 293, 294, 295, 296, 297, 298, 303, 304, + 305, 306, 307, 308, 309, 310, 712, 301, 302, 311, + 312, 313, 314, 315, 316, 317, 318, 319, 320, 321, + 322, 323, 324, 0, 0, 0, 0, 333, 771, 772, + 773, 774, 775, 0, 0, 334, 335, 336, 0, 0, + 299, 300, 327, 552, 328, 329, 330, 331, 0, 0, + 591, 592, 593, 616, 0, 594, 575, 641, 412, 342, + 556, 583, 782, 783, 0, 0, 0, 0, 0, 0, + 0, 692, 703, 739, 740, 0, 753, 754, 756, 758, + 757, 760, 549, 550, 768, 0, 0, 437, 358, 359, + 496, 497, 535, 0, 438, 439, 440, 444, 441, 442, + 443, 762, 763, 764, 761, 470, 534, 557, 543, 781, + 483, 0, 789, 631, 632, 790, 749, 748, 343, 0, + 337, 338, 706, 707, 0, 0, 537, 482, 638, 502, + 0, 0, 647, 681, 670, 759, 634, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 395, 0, 0, + 465, 685, 666, 677, 667, 508, 652, 653, 654, 661, + 407, 655, 791, 657, 626, 658, 627, 659, 660, 0, + 684, 633, 545, 484, 0, 701, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 268, 0, 0, + 0, 0, 0, 0, 366, 269, 628, 755, 630, 629, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 369, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 546, 574, 0, 588, 0, 434, 435, 0, 0, 0, + 0, 0, 0, 0, 350, 553, 571, 367, 540, 586, + 372, 548, 564, 362, 501, 536, 0, 0, 352, 569, + 547, 479, 351, 0, 529, 392, 409, 389, 499, 0, + 0, 568, 599, 388, 589, 0, 579, 354, 0, 578, + 498, 471, 565, 570, 480, 473, 0, 353, 567, 478, + 472, 448, 399, 615, 449, 450, 451, 452, 453, 454, + 413, 514, 469, 515, 414, 486, 485, 487, 415, 416, + 417, 418, 419, 420, 421, 422, 423, 424, 0, 0, + 0, 0, 0, 610, 611, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 747, 0, 0, 752, 0, 582, 0, 0, 0, 0, + 0, 0, 551, 0, 0, 455, 0, 0, 0, 600, + 0, 532, 504, 793, 0, 0, 530, 467, 566, 516, + 572, 554, 580, 903, 517, 344, 555, 391, 481, 363, + 365, 780, 393, 396, 400, 401, 492, 493, 510, 539, + 558, 559, 560, 390, 377, 531, 378, 410, 379, 345, + 382, 381, 383, 541, 384, 347, 511, 563, 0, 406, + 527, 476, 348, 475, 512, 562, 561, 364, 590, 597, + 598, 689, 0, 603, 794, 795, 796, 612, 0, 518, + 360, 357, 0, 0, 0, 386, 513, 373, 375, 376, + 374, 507, 509, 617, 618, 619, 621, 0, 622, 623, + 0, 0, 0, 0, 624, 690, 708, 674, 643, 605, + 698, 640, 644, 645, 427, 428, 429, 430, 711, 0, + 0, 0, 596, 456, 457, 0, 398, 397, 477, 349, + 0, 0, 445, 426, 519, 355, 394, 447, 432, 458, + 459, 460, 461, 462, 0, 463, 464, 404, 339, 340, + 788, 387, 500, 713, 750, 751, 635, 0, 699, 636, + 646, 380, 671, 683, 682, 494, 595, 0, 694, 697, + 625, 787, 0, 691, 705, 792, 704, 784, 506, 0, + 538, 702, 649, 0, 695, 668, 669, 0, 696, 664, + 700, 356, 433, 524, 581, 0, 637, 0, 606, 609, + 639, 726, 727, 728, 346, 608, 730, 731, 732, 733, + 734, 735, 904, 729, 577, 672, 648, 675, 587, 651, + 650, 0, 0, 686, 604, 687, 688, 488, 489, 490, + 491, 408, 714, 371, 607, 521, 522, 0, 673, 0, + 0, 0, 0, 0, 0, 0, 0, 678, 679, 676, + 797, 0, 737, 738, 0, 0, 601, 602, 403, 0, + 620, 411, 370, 505, 405, 585, 436, 0, 613, 680, + 614, 525, 526, 741, 746, 742, 743, 745, 766, 495, + 425, 431, 542, 446, 468, 528, 584, 503, 533, 368, + 573, 544, 474, 665, 693, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 325, 326, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 722, 721, 720, 719, 718, 717, 716, 715, + 0, 0, 662, 385, 785, 779, 786, 767, 770, 769, + 744, 0, 341, 642, 466, 520, 402, 709, 710, 0, + 765, 284, 285, 286, 287, 288, 289, 290, 291, 332, + 292, 293, 294, 295, 296, 297, 298, 303, 304, 305, + 306, 307, 308, 309, 310, 712, 301, 302, 311, 312, + 313, 314, 315, 316, 317, 318, 319, 320, 321, 322, + 323, 324, 0, 0, 0, 0, 333, 771, 772, 773, + 774, 775, 0, 0, 334, 335, 336, 0, 0, 299, + 300, 327, 552, 328, 329, 330, 331, 0, 0, 591, + 592, 593, 616, 0, 594, 575, 641, 412, 342, 556, + 583, 782, 783, 0, 0, 0, 0, 0, 0, 0, + 692, 703, 739, 740, 0, 753, 754, 756, 758, 757, + 760, 549, 550, 768, 0, 0, 437, 358, 359, 496, + 497, 535, 0, 438, 439, 440, 444, 441, 442, 443, + 762, 763, 764, 761, 470, 534, 557, 543, 781, 483, + 0, 789, 631, 632, 790, 749, 748, 343, 0, 337, + 338, 706, 707, 0, 0, 537, 482, 638, 502, 0, + 0, 647, 681, 670, 759, 634, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 395, 0, 0, 465, + 685, 666, 677, 667, 508, 652, 653, 654, 661, 407, + 655, 791, 657, 626, 658, 627, 659, 660, 0, 684, + 633, 545, 484, 0, 701, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 268, 0, 0, 0, + 0, 0, 0, 366, 269, 628, 755, 630, 629, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 369, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 546, + 574, 0, 588, 0, 434, 435, 0, 0, 0, 0, + 0, 0, 0, 350, 553, 571, 367, 540, 586, 372, + 548, 564, 362, 501, 536, 0, 0, 352, 569, 547, + 479, 351, 0, 529, 392, 409, 389, 499, 0, 0, + 568, 599, 388, 589, 0, 579, 354, 0, 578, 498, + 471, 565, 570, 480, 473, 0, 353, 567, 478, 472, + 448, 399, 615, 449, 450, 451, 452, 453, 454, 413, + 514, 469, 515, 414, 486, 485, 487, 415, 416, 417, + 418, 419, 420, 421, 422, 423, 424, 0, 0, 0, + 0, 0, 610, 611, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 747, + 0, 0, 752, 0, 582, 0, 0, 0, 0, 0, + 0, 551, 0, 0, 455, 0, 0, 0, 600, 0, + 532, 504, 793, 0, 0, 530, 467, 566, 516, 572, + 554, 580, 523, 517, 344, 555, 391, 481, 363, 365, + 780, 393, 396, 400, 401, 492, 493, 510, 539, 558, + 559, 560, 390, 377, 531, 378, 410, 379, 345, 382, + 381, 383, 541, 384, 347, 511, 563, 0, 406, 527, + 476, 348, 475, 512, 562, 561, 364, 590, 597, 598, + 689, 0, 603, 794, 795, 796, 612, 0, 518, 360, + 357, 0, 0, 0, 386, 513, 373, 375, 376, 374, + 507, 509, 617, 618, 619, 621, 0, 622, 623, 0, + 0, 0, 0, 624, 690, 708, 674, 643, 605, 698, + 640, 644, 645, 427, 428, 429, 430, 711, 0, 0, + 0, 596, 456, 457, 0, 398, 397, 477, 349, 0, + 0, 445, 426, 519, 355, 394, 447, 432, 458, 459, + 460, 461, 462, 0, 463, 464, 404, 339, 340, 788, + 387, 500, 713, 750, 751, 635, 0, 699, 636, 646, + 380, 671, 683, 682, 494, 595, 0, 694, 697, 625, + 787, 0, 691, 705, 792, 704, 784, 506, 0, 538, + 702, 649, 0, 695, 668, 669, 0, 696, 664, 700, + 356, 433, 524, 581, 0, 637, 0, 606, 609, 639, + 726, 727, 728, 346, 608, 730, 731, 732, 733, 734, + 735, 736, 729, 577, 672, 648, 675, 587, 651, 650, + 0, 0, 686, 604, 687, 688, 488, 489, 490, 491, + 408, 714, 371, 607, 521, 522, 0, 673, 0, 0, + 0, 0, 0, 0, 0, 0, 678, 679, 676, 797, + 0, 737, 738, 0, 0, 601, 602, 403, 0, 620, + 411, 370, 505, 405, 585, 436, 0, 613, 680, 614, + 525, 526, 741, 746, 742, 743, 745, 766, 495, 425, + 431, 542, 446, 468, 528, 584, 503, 533, 368, 573, + 544, 474, 665, 693, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 325, 326, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 722, 721, 720, 719, 718, 717, 716, 715, 0, + 0, 662, 385, 785, 779, 786, 767, 770, 769, 840, + 0, 341, 642, 466, 520, 402, 709, 710, 0, 765, + 284, 285, 286, 287, 288, 289, 290, 291, 332, 292, + 293, 294, 295, 296, 297, 298, 303, 304, 305, 306, + 307, 308, 309, 310, 712, 301, 302, 311, 312, 313, + 314, 315, 316, 317, 318, 319, 320, 321, 322, 323, + 324, 0, 0, 0, 0, 333, 771, 772, 773, 774, + 775, 0, 0, 334, 335, 336, 0, 0, 299, 300, + 327, 552, 328, 329, 330, 331, 0, 0, 591, 592, + 593, 616, 0, 594, 575, 641, 412, 342, 556, 583, + 782, 783, 0, 0, 0, 0, 0, 0, 0, 692, + 703, 739, 740, 0, 753, 754, 756, 758, 757, 760, + 549, 550, 768, 0, 0, 437, 358, 359, 496, 497, + 535, 0, 438, 439, 440, 444, 441, 442, 443, 762, + 763, 764, 761, 470, 534, 557, 543, 781, 483, 0, + 789, 631, 632, 790, 749, 748, 343, 0, 337, 338, + 706, 707, 0, 0, 537, 482, 638, 502, 0, 0, + 647, 681, 670, 759, 634, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 395, 0, 0, 465, 685, + 666, 677, 667, 508, 652, 653, 654, 661, 407, 655, + 791, 657, 626, 658, 627, 659, 660, 0, 684, 633, + 545, 484, 0, 701, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 268, 0, 0, 0, 0, + 0, 0, 366, 269, 628, 755, 630, 629, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 369, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 546, 574, + 0, 588, 0, 434, 435, 0, 0, 0, 0, 0, + 0, 0, 350, 553, 571, 367, 540, 586, 372, 548, + 564, 362, 501, 536, 0, 0, 352, 569, 547, 479, + 351, 0, 529, 392, 409, 389, 499, 0, 0, 568, + 599, 388, 589, 0, 579, 354, 0, 578, 498, 471, + 565, 570, 480, 473, 0, 353, 567, 478, 472, 448, + 399, 615, 449, 450, 451, 452, 453, 454, 413, 514, + 469, 515, 414, 486, 485, 487, 415, 416, 417, 418, + 419, 420, 421, 422, 423, 424, 0, 0, 0, 0, + 0, 610, 611, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 747, 0, + 0, 752, 0, 582, 0, 0, 0, 0, 0, 0, + 551, 0, 0, 455, 0, 0, 0, 600, 0, 532, + 504, 793, 0, 0, 530, 467, 566, 516, 572, 554, + 580, 523, 517, 344, 555, 391, 481, 363, 365, 780, + 393, 396, 400, 401, 492, 493, 510, 539, 558, 559, + 560, 390, 377, 531, 378, 410, 379, 345, 382, 381, + 383, 541, 384, 347, 511, 563, 0, 406, 0, 476, + 348, 475, 512, 562, 561, 364, 590, 597, 598, 689, + 0, 603, 794, 795, 796, 612, 0, 518, 360, 357, + 0, 0, 0, 386, 513, 373, 375, 376, 374, 507, + 509, 617, 618, 619, 621, 0, 622, 623, 0, 0, + 0, 0, 624, 690, 708, 674, 643, 605, 698, 640, + 644, 645, 427, 428, 429, 430, 711, 0, 0, 0, + 596, 456, 457, 0, 398, 397, 477, 349, 0, 0, + 445, 426, 519, 355, 394, 447, 432, 458, 459, 460, + 461, 462, 0, 463, 464, 404, 339, 340, 788, 387, + 500, 713, 750, 751, 635, 0, 699, 636, 646, 380, + 671, 683, 682, 494, 595, 0, 694, 697, 625, 787, + 0, 691, 705, 792, 704, 784, 506, 0, 538, 702, + 649, 0, 695, 668, 669, 0, 696, 664, 700, 356, + 433, 524, 581, 0, 637, 0, 606, 609, 639, 726, + 727, 728, 346, 608, 730, 731, 732, 733, 734, 735, + 736, 729, 577, 672, 648, 675, 587, 651, 650, 0, + 0, 686, 604, 687, 688, 488, 489, 490, 491, 408, + 714, 371, 607, 521, 522, 0, 673, 0, 0, 0, + 0, 0, 0, 0, 0, 678, 679, 676, 797, 0, + 737, 738, 0, 0, 601, 602, 403, 0, 620, 411, + 370, 505, 405, 585, 436, 0, 613, 680, 614, 525, + 526, 741, 746, 742, 743, 745, 766, 495, 425, 431, + 542, 446, 468, 528, 584, 503, 533, 368, 573, 544, + 474, 665, 693, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 325, 326, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 722, 721, 720, 719, 718, 717, 716, 715, 0, 0, + 662, 385, 785, 779, 786, 767, 770, 769, 744, 0, + 341, 642, 466, 520, 402, 709, 710, 0, 765, 284, + 285, 286, 287, 288, 289, 290, 291, 332, 292, 293, + 294, 295, 296, 297, 298, 303, 304, 305, 306, 307, + 308, 309, 310, 712, 301, 302, 311, 312, 313, 314, + 315, 316, 317, 318, 319, 320, 321, 322, 323, 324, + 0, 0, 0, 0, 333, 771, 772, 773, 774, 775, + 0, 0, 334, 335, 336, 0, 0, 299, 300, 327, + 552, 328, 329, 330, 331, 0, 0, 591, 592, 593, + 616, 0, 594, 575, 641, 412, 342, 556, 583, 782, + 783, 0, 0, 0, 0, 0, 0, 0, 692, 703, + 739, 740, 0, 753, 754, 756, 758, 757, 760, 549, + 550, 768, 0, 0, 437, 358, 359, 496, 497, 535, + 0, 438, 439, 440, 444, 441, 442, 443, 762, 763, + 764, 761, 470, 534, 557, 543, 781, 483, 0, 789, + 631, 632, 790, 749, 748, 343, 0, 337, 338, 706, + 707, 0, 0, 537, 482, 638, 502, 0, 0, 647, + 681, 670, 759, 634, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 395, 0, 0, 465, 685, 666, + 677, 667, 508, 652, 653, 654, 661, 407, 655, 791, + 657, 626, 658, 627, 659, 660, 0, 684, 633, 545, + 484, 0, 701, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 268, 0, 0, 0, 0, 0, + 0, 366, 269, 628, 755, 630, 629, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 369, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 546, 574, 0, + 588, 0, 434, 435, 0, 0, 0, 0, 0, 0, + 0, 350, 553, 571, 367, 540, 586, 372, 548, 564, + 362, 501, 536, 0, 0, 352, 569, 547, 479, 351, + 0, 529, 392, 409, 389, 499, 0, 0, 568, 599, + 388, 589, 0, 579, 354, 0, 578, 498, 471, 565, + 570, 480, 473, 0, 353, 567, 478, 472, 448, 399, + 615, 449, 450, 451, 452, 453, 454, 413, 514, 469, + 515, 414, 486, 485, 487, 415, 416, 417, 418, 419, + 420, 421, 422, 423, 424, 0, 0, 0, 0, 0, + 610, 611, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 747, 0, 0, + 752, 0, 582, 0, 0, 0, 0, 0, 0, 551, + 0, 0, 455, 0, 0, 0, 600, 0, 532, 504, + 793, 0, 0, 530, 467, 566, 516, 572, 554, 580, + 523, 517, 344, 555, 391, 481, 363, 365, 780, 393, + 396, 400, 401, 492, 493, 510, 539, 558, 559, 560, + 390, 377, 531, 378, 410, 379, 345, 382, 381, 383, + 541, 384, 347, 511, 563, 0, 406, 527, 476, 348, + 475, 512, 562, 561, 364, 590, 597, 598, 689, 0, + 603, 794, 795, 796, 612, 0, 518, 360, 357, 0, + 0, 0, 386, 513, 373, 375, 376, 374, 507, 509, + 617, 618, 619, 621, 0, 622, 623, 0, 0, 0, + 0, 624, 690, 708, 674, 643, 605, 698, 640, 644, + 645, 427, 428, 429, 430, 711, 0, 0, 0, 596, + 456, 457, 0, 398, 397, 477, 349, 0, 0, 445, + 426, 519, 355, 394, 447, 432, 458, 459, 460, 461, + 462, 0, 463, 464, 404, 339, 340, 788, 387, 500, + 713, 750, 751, 635, 0, 699, 636, 646, 380, 671, + 683, 682, 494, 595, 0, 694, 697, 625, 787, 0, + 691, 705, 792, 704, 784, 506, 0, 538, 702, 649, + 0, 695, 668, 669, 0, 696, 664, 700, 356, 433, + 524, 581, 0, 637, 0, 606, 609, 639, 726, 727, + 728, 346, 608, 730, 731, 732, 733, 734, 735, 736, + 729, 577, 672, 648, 675, 587, 651, 650, 0, 0, + 686, 604, 687, 688, 488, 489, 490, 491, 408, 714, + 371, 607, 521, 522, 0, 673, 0, 0, 0, 0, + 0, 0, 0, 0, 678, 679, 676, 797, 0, 737, + 738, 0, 0, 601, 602, 403, 0, 620, 411, 370, + 505, 405, 585, 436, 0, 613, 680, 614, 525, 526, + 741, 746, 742, 743, 745, 766, 495, 425, 431, 542, + 446, 468, 528, 584, 503, 533, 368, 573, 544, 474, + 665, 693, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 325, 326, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 718, - 717, 716, 715, 714, 713, 712, 711, 0, 0, 658, - 384, 781, 775, 782, 763, 766, 765, 740, 0, 341, - 638, 464, 518, 401, 705, 706, 0, 761, 284, 285, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 722, + 721, 720, 719, 718, 717, 716, 715, 0, 0, 662, + 385, 785, 779, 786, 767, 770, 769, 744, 0, 341, + 642, 466, 520, 402, 709, 710, 0, 765, 284, 285, 286, 287, 288, 289, 290, 291, 332, 292, 293, 294, 295, 296, 297, 298, 303, 304, 305, 306, 307, 308, - 309, 310, 708, 301, 302, 311, 312, 313, 314, 315, + 309, 310, 712, 301, 302, 311, 312, 313, 314, 315, 316, 317, 318, 319, 320, 321, 322, 323, 324, 0, - 0, 0, 0, 333, 767, 768, 769, 770, 771, 0, - 0, 334, 335, 336, 0, 0, 299, 300, 327, 549, - 328, 329, 330, 331, 0, 0, 587, 588, 589, 612, - 0, 590, 572, 637, 411, 342, 553, 579, 778, 779, - 0, 0, 0, 0, 0, 0, 0, 688, 699, 735, - 736, 0, 749, 750, 752, 754, 753, 756, 546, 547, - 764, 0, 0, 435, 357, 358, 494, 495, 532, 0, - 436, 437, 438, 442, 439, 440, 441, 758, 759, 760, - 757, 468, 531, 554, 540, 777, 481, 0, 785, 627, - 628, 786, 745, 744, 343, 0, 337, 338, 702, 703, - 0, 0, 534, 480, 634, 500, 0, 0, 643, 677, - 666, 755, 630, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 394, 0, 0, 463, 681, 662, 673, - 663, 506, 648, 649, 650, 657, 406, 651, 787, 653, - 622, 654, 623, 655, 656, 0, 680, 629, 542, 482, - 0, 697, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 268, 0, 0, 0, 0, 0, 0, - 365, 269, 624, 751, 626, 625, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 368, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 543, 571, 0, 584, - 0, 432, 433, 0, 0, 0, 0, 0, 0, 0, - 350, 550, 568, 366, 537, 582, 371, 545, 561, 361, - 499, 533, 0, 0, 352, 566, 544, 477, 351, 0, - 526, 391, 408, 388, 497, 0, 0, 565, 595, 387, - 585, 0, 576, 354, 0, 575, 496, 469, 562, 567, - 478, 471, 0, 353, 564, 476, 470, 446, 398, 611, - 447, 448, 449, 450, 451, 452, 412, 512, 467, 513, - 413, 484, 483, 485, 414, 415, 416, 417, 418, 419, - 420, 421, 422, 423, 0, 0, 0, 0, 0, 606, - 607, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 743, 0, 0, 748, - 0, 578, 0, 0, 0, 0, 0, 0, 548, 0, - 0, 453, 0, 0, 0, 596, 0, 529, 502, 789, - 0, 0, 527, 465, 563, 514, 569, 551, 577, 521, - 515, 344, 552, 390, 479, 362, 364, 776, 392, 395, - 399, 400, 490, 491, 508, 536, 555, 556, 557, 389, - 376, 528, 377, 409, 378, 345, 381, 380, 382, 538, - 383, 347, 509, 560, 0, 405, 524, 474, 348, 473, - 510, 559, 558, 363, 586, 593, 594, 685, 0, 599, - 790, 791, 792, 608, 0, 516, 359, 356, 0, 0, - 0, 385, 511, 372, 374, 375, 373, 505, 507, 613, - 614, 615, 617, 0, 618, 619, 0, 0, 0, 0, - 620, 686, 704, 670, 639, 601, 694, 636, 640, 641, - 426, 427, 428, 429, 707, 0, 0, 0, 592, 454, - 455, 0, 397, 396, 475, 349, 0, 0, 443, 425, - 517, 355, 393, 445, 431, 456, 457, 458, 459, 460, - 0, 461, 462, 403, 339, 340, 784, 386, 498, 709, - 746, 747, 631, 0, 695, 632, 642, 379, 667, 679, - 678, 492, 591, 0, 690, 693, 621, 783, 0, 687, - 701, 788, 700, 780, 504, 0, 535, 698, 645, 0, - 691, 664, 665, 0, 692, 660, 696, 0, 633, 0, - 602, 605, 635, 722, 723, 724, 346, 604, 726, 727, - 728, 729, 730, 731, 732, 725, 574, 668, 644, 671, - 583, 647, 646, 0, 0, 682, 600, 683, 684, 486, - 487, 488, 489, 407, 710, 370, 603, 519, 520, 0, - 669, 0, 0, 0, 0, 0, 0, 0, 0, 674, - 675, 672, 793, 0, 733, 734, 0, 0, 597, 598, - 402, 0, 616, 410, 369, 503, 404, 581, 434, 0, - 609, 676, 610, 522, 523, 737, 742, 738, 739, 741, - 762, 493, 424, 430, 539, 444, 466, 525, 580, 501, - 530, 367, 570, 541, 472, 661, 689, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 325, 326, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 718, 717, 716, 715, 714, 713, - 712, 711, 0, 0, 658, 384, 781, 775, 782, 763, - 766, 765, 740, 0, 341, 638, 464, 518, 401, 705, - 706, 0, 761, 284, 285, 286, 287, 288, 289, 290, - 291, 332, 292, 293, 294, 295, 296, 297, 298, 303, - 304, 305, 306, 307, 308, 309, 310, 708, 301, 302, - 311, 312, 313, 314, 315, 316, 317, 318, 319, 320, - 321, 322, 323, 324, 0, 0, 0, 0, 333, 767, - 768, 769, 770, 771, 0, 0, 334, 335, 336, 0, - 0, 299, 300, 327, 549, 328, 329, 330, 331, 0, - 0, 587, 588, 589, 612, 0, 590, 572, 637, 411, - 342, 553, 579, 778, 779, 0, 0, 0, 0, 0, - 0, 0, 688, 699, 735, 736, 0, 749, 750, 752, - 754, 753, 756, 546, 547, 764, 0, 0, 435, 357, - 358, 494, 495, 532, 0, 436, 437, 438, 442, 439, - 440, 441, 758, 759, 760, 757, 468, 531, 554, 540, - 777, 481, 0, 785, 627, 628, 786, 745, 0, 343, - 0, 337, 338, 702, 703, 0, 0, 534, 480, 634, + 0, 0, 0, 333, 771, 772, 773, 774, 775, 0, + 0, 334, 335, 336, 0, 0, 299, 300, 327, 552, + 328, 329, 330, 331, 0, 0, 591, 592, 593, 616, + 0, 594, 575, 641, 412, 342, 556, 583, 782, 783, + 0, 0, 0, 0, 0, 0, 0, 692, 703, 739, + 740, 0, 753, 754, 756, 758, 757, 760, 549, 550, + 768, 0, 0, 437, 358, 359, 496, 497, 535, 0, + 438, 439, 440, 444, 441, 442, 443, 762, 763, 764, + 761, 470, 534, 557, 543, 781, 483, 0, 789, 631, + 632, 790, 749, 0, 343, 0, 337, 338, 706, 707, + 0, 0, 537, 482, 638, } var yyPact = [...]int{ - 6178, -1000, -1000, -365, -1000, -1000, -1000, -1000, -1000, 20904, + 6208, -1000, -1000, -382, -1000, -1000, -1000, -1000, -1000, 20971, + -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, + -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, + -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, + -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, + -1000, -1000, -1000, -1000, -1000, 74894, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, + -1000, -1000, 823, 74894, 1559, -358, -1000, 4064, -1000, 1659, + -1000, -1000, -1000, -1000, 668, 72617, 22506, -1000, 74894, 990, + 978, 80963, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, - -1000, -1000, -1000, -1000, -1000, 74543, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, - -1000, -1000, 760, 74543, 1418, -326, -1000, 4043, -1000, 1541, - -1000, -1000, -1000, -1000, 576, 72278, 22431, -1000, 74543, 964, - 960, 80580, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, + 1569, 1568, 6935, -1000, 80204, -1000, -1000, -1000, -1000, -1000, + -1000, -1000, -1000, -1000, 1382, 7612, 74135, 16384, 4047, -167, + -1000, -1000, 2652, 49, 3951, 911, 80, 62, 970, 1823, + 1844, 2028, 1801, 74894, 1787, -1000, -1000, -1000, -1000, -1000, + -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, 5219, 34665, + 73376, 1676, 4619, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, + 2030, 1561, 1676, 29355, 412, 406, 2159, 4827, 2209, -1000, + -1000, 50, 5910, -1000, -1000, 2500, 74894, 6208, 271, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, + 16384, 16384, 20971, -434, 20971, 16384, 74894, 74894, -1000, -1000, + -1000, -1000, 1559, -358, 72617, 1382, 7612, 16384, 4047, 3951, + 911, 80, 62, 970, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, - 1445, 1437, 6632, -1000, 79825, -1000, -1000, -1000, -1000, -1000, - -1000, -1000, -1000, -1000, 1305, 6786, 73788, 16341, 4025, -117, - -1000, -1000, 2341, 84, 3933, 805, 89, 85, 947, 1698, - 1730, 2122, 1932, 74543, 1645, -1000, -1000, -1000, -1000, -1000, - -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, 7165, 34526, - 73033, 1714, 4600, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, - 509, 1420, 1714, 29244, 360, 354, 2531, 4806, 2318, -1000, - -1000, 77, 2266, -1000, -1000, 2491, 74543, 6178, 290, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, - 16341, 16341, 20904, -412, 20904, 16341, 74543, 74543, -1000, -1000, - -1000, -1000, 1418, -326, 72278, 1305, 6786, 16341, 4025, 3933, - 805, 89, 85, 947, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, + -1000, 50, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, - 77, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, @@ -10532,19 +10584,19 @@ var yyPact = [...]int{ -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, + -1000, -1000, -1000, -1000, -1000, -1000, 406, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, - -1000, -1000, -1000, 354, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, + -1000, -1000, -1000, -1000, -1000, -1000, 4619, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, - -1000, -1000, 4600, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, @@ -10552,549 +10604,547 @@ var yyPact = [...]int{ -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, + -1000, -1000, -1000, -1000, -1000, -1000, 494, -1000, 2579, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, + -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, 3633, 4391, + 2569, 74894, 3950, -1000, -1000, -1000, -1000, -1000, -1000, 2159, + 4727, 71858, -1000, -1000, 4804, -1000, 74894, 220, 74894, 534, + 2967, -1000, 2245, 1343, 1325, 2469, 727, 2564, -1000, -1000, + -1000, -1000, -1000, -1000, 4096, 1158, 4803, -1000, 74894, 74894, + 74894, 4409, 74894, 74894, 547, 4724, -1000, 256, -61, -325, + -340, 4440, 2658, 2658, 4770, 4770, 4722, 4721, 1194, 1190, + 1170, 2658, 1038, -1000, 2828, 2828, 2828, 2828, 2658, 782, + 1221, 4717, 4390, 4717, 374, 2828, 297, 2658, 2658, 297, + 2658, 2658, 887, -1000, 2937, 896, 533, -257, -1000, -1000, + -1000, 2828, 2828, -1000, -1000, -1000, -1000, -1000, -1000, -1000, + 4693, 4692, -1000, 734, 1191, -1000, 6310, 4633, 2250, 1528, + 4437, -1000, -1000, 4389, -1000, 731, 939, 523, 1011, 812, + -1000, -1000, -1000, -1000, -1000, -1000, -1000, 650, -1000, 4699, + -1000, -1000, 710, -1000, -1000, 735, -1000, -1000, -1000, 401, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, + -1000, 209, 74894, -1000, -1000, 1977, 3121, 16384, 3381, -1000, + 845, 2773, -1000, -1000, -1000, 8740, 20199, 20199, 20199, 20199, + 20199, 74894, -1000, -1000, 4231, 16384, 4388, 4387, 4386, 4385, + -1000, -1000, -1000, -1000, -1000, -1000, 4382, 2561, -1000, -1000, + -1000, -1000, -1000, -1000, -1000, -1000, -1000, 3257, -1000, -1000, + -1000, 19436, -1000, 4359, 4357, 4356, 4355, 4353, 4352, 4351, + 4350, 4345, 4342, 4341, 4338, 4337, 4336, 4335, 4147, 23265, + 4332, 3946, 3945, 4328, 4326, 4325, 3943, 4318, 4317, 4314, + 4147, 4147, 4310, 4308, 4307, 4306, 4304, 4303, 4302, 4300, + 4298, 4297, 4296, 4293, 4291, 4289, 4286, 4285, 4284, 4283, + 4282, 4281, 4280, 4276, 4275, 4274, 4272, 4271, 4270, 4269, + 4267, 4264, 4263, 4259, 4258, 4257, 4256, 4254, 4251, 4250, + 4246, 4243, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, - -1000, -1000, 525, -1000, 2493, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, - -1000, -1000, -1000, -1000, 3575, 4390, 2490, 74543, 3931, -1000, - -1000, -1000, -1000, -1000, -1000, 2531, 4711, 71523, -1000, -1000, - 4779, -1000, 74543, 207, 74543, 471, 2897, -1000, 813, 1590, - 982, 1717, 610, 2453, -1000, -1000, -1000, -1000, -1000, -1000, - 4052, 1128, 4778, -1000, 74543, 74543, 74543, 4406, 74543, 74543, - 580, 4706, -1000, 252, -59, -296, -311, 4432, 2742, 2742, - 4752, 4752, 4705, 4704, 1212, 1210, 1186, 2742, 1021, -1000, - 2757, 2757, 2757, 2757, 2742, 830, 1200, 4710, 4389, 4710, - 335, 2757, 303, 2742, 2742, 303, 2742, 2742, 811, -1000, - 2723, 897, 636, -205, -1000, -1000, -1000, 2757, 2757, -1000, - -1000, -1000, -1000, -1000, -1000, -1000, 4672, 4667, -1000, 774, - 1158, -1000, 7508, 4611, 2259, 1446, 4428, -1000, -1000, 4387, - -1000, 629, 632, 524, 744, 754, -1000, -1000, -1000, -1000, - -1000, -1000, -1000, 655, -1000, 4681, -1000, -1000, 605, -1000, - -1000, 608, -1000, -1000, -1000, 351, -1000, -1000, -1000, -1000, - -1000, -1000, -1000, -1000, -1000, -1000, -1000, 150, 74543, -1000, - -1000, 1877, 3162, 16341, 3248, -1000, 701, 2709, -1000, -1000, - -1000, 8737, 20136, 20136, 20136, 20136, 20136, 74543, -1000, -1000, - 4226, 16341, 4386, 4385, 4384, 4383, -1000, -1000, -1000, -1000, - -1000, -1000, 4382, 2452, -1000, -1000, -1000, -1000, -1000, -1000, - -1000, -1000, -1000, 3108, -1000, -1000, -1000, 19377, -1000, 4378, - 4374, 4351, 4350, 4349, 4348, 4346, 4345, 4344, 4338, 4337, - 4336, 4335, 4334, 4329, 4122, 23186, 4326, 3928, 3927, 4324, - 4320, 4314, 3924, 4311, 4306, 4302, 4122, 4122, 4301, 4300, - 4299, 4298, 4296, 4294, 4293, 4292, 4291, 4290, 4289, 4288, - 4287, 4282, 4281, 4280, 4279, 4278, 4277, 4274, 4272, 4270, - 4268, 4267, 4266, 4265, 4264, 4263, 4262, 4261, 4257, 4255, - 4253, 4252, 4250, 4247, 4242, 4240, 4239, 4238, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, + -1000, -1000, -1000, -1000, -1000, -1000, -1000, 2159, 4172, 4861, + 2140, -1000, 4242, 4830, 4756, 4726, 1848, 16384, -241, 4680, + 4676, 4674, 4668, 4241, 3542, -1000, -1000, 157, 74894, 74894, + 363, 74894, -270, 547, 904, -32, -42, 901, -45, 1376, + -1000, 795, -1000, -1000, 1794, -1000, 1762, 79445, 1471, -1000, + -1000, 74894, 1333, 1333, 1333, -302, 532, 1333, 74894, 946, + 1395, 1644, 1333, 1333, 1333, 1333, 1397, 1333, 1551, 1542, + 1541, 1333, 81, -1000, -1000, -1000, -1000, -1000, -1000, -1000, + -1000, 2965, 2962, 4539, 3094, 3003, 1311, 71858, 71858, 74894, + -1000, 2333, 74894, -1000, 4216, 1734, -1000, -1000, -1000, -1000, + 60472, -303, 530, 1382, 1382, 74894, 1382, 74894, 697, 481, + 74894, 1382, 1382, 1382, 74894, 1387, -346, 239, 78686, 77927, + 3717, 734, 1189, 1183, 2266, 2939, -1000, 2724, 74894, 74894, + 2724, 2724, 33906, 33147, -1000, 74894, -1000, 4830, 1848, -1000, + 4715, 1892, -1000, 4760, 4118, 2679, 4827, 4827, 4172, -1000, + -167, -46, 1382, 1382, -306, 529, 1382, 1382, 1382, 1382, + 1382, 1536, 1382, 1382, 1382, 74894, 1382, 71099, 1382, 900, + 1382, 1382, 1382, 13327, 2500, -1000, -1000, -1000, -1000, -1000, + -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, 20971, + 3206, 3337, 270, 64, -319, 403, -1000, -1000, 74894, 4598, + 2626, -1000, -1000, -1000, 4189, 4185, -1000, 4188, 4188, 4188, + 4188, 1752, -1000, 4185, -1000, -1000, -1000, 4185, -1000, -1000, + -1000, -1000, 4188, 4188, 4188, 4188, 4188, 4188, 4187, 4239, + -1000, -1000, 4186, 4186, 4186, 4185, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, + -1000, 1875, 4182, 4183, 4183, 4182, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, + -1000, -1000, -1000, -1000, 74894, 4833, -1000, -1000, 16384, 74894, + 1467, 4618, 4830, 4620, 4717, 4756, 1288, 3439, -1000, -317, + 74894, 626, -1000, 2523, 3536, 3942, -1000, 727, -1000, 2401, + 727, -1000, 1414, 1414, 2834, -1000, 1910, -1000, -1000, -1000, + -1000, -1000, -1000, -1000, 74894, 209, 7012, -1000, -1000, -1000, + 3887, 1091, -1000, 4238, 805, -1000, -1000, -1000, -1000, 74894, + 4717, -67, 4719, 2658, 3106, 3026, 2658, -1000, -1000, -1000, + 1177, 4770, 20199, 20199, -1000, -1000, 16384, -1000, 477, 70340, + 2828, 2658, 2658, -1000, -1000, 74894, -1000, -1000, -1000, 2828, + 74894, 2828, 2828, 4770, 2828, -1000, -1000, -1000, 2658, 2658, + -1000, -1000, 16384, 2629, -1000, -1000, 2828, 2828, -1000, -1000, + 4770, 74894, 395, 4770, 4770, 365, -1000, -1000, 74894, -1000, + 2658, 3941, -1000, 74894, 74894, 1333, 74894, -1000, 74894, 74894, + -1000, -1000, -1000, 1075, 2096, 2342, -1000, 804, 7633, 58195, + 734, 58195, 74894, -1000, -1000, -1000, -1000, -1000, -1000, 386, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, - -1000, -1000, -1000, 2531, 4179, 4828, 2004, -1000, 4237, 4818, - 4738, 4708, 2126, 16341, -191, 4669, 4654, 4652, 4644, 4235, - 3501, -1000, -1000, 184, 74543, 74543, 368, 74543, -216, 580, - 840, 14, 5, 838, -1, 1577, -1000, 802, -1000, -1000, - 1765, -1000, 1618, 79070, 1363, -1000, -1000, 74543, 1296, 1296, - 1296, -272, 467, 1296, 74543, 876, 1330, 1613, 1296, 1296, - 1296, 1296, 1367, 1296, 1417, 1416, 1411, 1296, 118, -1000, - -1000, -1000, -1000, -1000, -1000, -1000, -1000, 2896, 2894, 4518, - 3047, 2951, 1264, 71523, 71523, 74543, -1000, 2306, 74543, -1000, - 4212, 1598, -1000, -1000, -1000, -1000, 60197, -274, 459, 1305, - 1305, 74543, 1305, 74543, 617, 411, 74543, 1305, 1305, 1305, - 74543, 1308, -310, 244, 78315, 77560, 3570, 774, 1155, 1153, - 2224, 2830, -1000, 2762, 74543, 74543, 2762, 2762, 33771, 33016, - -1000, 74543, -1000, 4818, 2126, -1000, 4699, 2214, -1000, 4742, - 4104, 2443, 4806, 4806, 4179, -1000, -117, -9, 1305, 1305, - -276, 450, 1305, 1305, 1305, 1305, 1305, 1408, 1305, 1305, - 1305, 74543, 1305, 70768, 1305, 829, 1305, 1305, 1305, 13300, - 2491, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, - -1000, -1000, -1000, -1000, -1000, 20904, 3023, 3147, 288, 80, - -308, 457, -1000, -1000, 74543, 4579, 2560, -1000, -1000, -1000, - 4184, 4177, -1000, 4181, 4181, 4181, 4181, 1710, -1000, 4177, - -1000, -1000, -1000, 4177, -1000, -1000, -1000, -1000, 4181, 4181, - 4181, 4181, 4181, 4181, 4180, 4234, -1000, -1000, 4178, 4178, - 4178, 4177, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, - -1000, -1000, -1000, -1000, -1000, -1000, -1000, 1872, 4175, 4176, - 4176, 4175, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, + -1000, -1000, -1000, -1000, 712, -1000, 4802, 16384, 16384, 16384, + 16384, -1000, 1285, 18673, -1000, -1000, -1000, -1000, -1000, -1000, + -1000, 20199, 20199, 20199, 20199, 20199, 20199, 20199, 20199, 20199, + 20199, 20199, 20199, 20199, 20199, 4230, 3083, 3020, 4704, 20199, + 20199, 20199, 20199, 20199, 405, 36942, 2679, 3954, 2334, 443, + 2644, 2644, 2644, 2644, 2644, 16384, -1000, 3002, 3121, 16384, + 16384, 16384, 16384, 46050, 74894, -1000, -1000, 8740, 6091, 16384, + 16384, 7167, 20199, 16384, 4664, 16384, 16384, 16384, 4115, 9503, + 74894, 16384, -1000, 4114, 4113, -1000, -1000, 3264, 16384, -1000, + -1000, 16384, -1000, -1000, 16384, 20199, 16384, -1000, 16384, 16384, + 16384, -1000, -1000, 2112, 2112, 1439, 4664, 16384, 16384, 4664, + 4664, 2984, 16384, 16384, 4664, 4664, 4664, 2969, 4664, 4664, + 4664, 4664, 4664, 4664, 4664, 4013, 4664, 4664, 4664, 4664, + 4664, 4112, 4109, 4108, 4107, 16384, 4106, 16384, 16384, 16384, + 16384, 16384, 15621, 4830, 2159, 4715, -167, 14090, 4620, 4717, + 4604, -141, 4756, -1000, -1000, -1000, -1000, 3121, -247, 4236, + -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, 74894, 3937, + 3928, -387, -388, 1836, -388, 2515, -1000, -282, 1815, 337, + 74894, -1000, 3927, 3535, 74894, 3926, 3533, 501, 497, 74894, + 74894, 74894, 233, 1818, 1770, 1784, -1000, -1000, 74894, 77168, + -1000, 74894, 2608, 74894, 74894, 1333, 1333, 74894, 4653, -1000, + 74894, 74894, 1333, 1333, -1000, 68063, 3918, 58195, 74894, 74894, + 734, 74894, 74894, 1333, 1333, 1333, 74894, -1000, 4548, 58195, + 4543, 2037, 2991, -1000, -1000, 74894, 16384, 2991, 2027, 4235, + 1311, 1311, -1000, 74894, 2333, 4651, 74894, 1387, -1000, 600, + -1000, -1000, -1000, -1000, 76409, 76409, 25548, 76409, -1000, 254, + 74894, 74894, 74894, 74894, 5471, 74894, 576, 4632, 1621, 68063, + 69581, 4691, -1000, 58195, 74894, 74894, 2328, -1000, 1456, 30114, + -1000, 74894, 2282, -1000, 246, -1000, 227, 239, 2724, 239, + 2724, 1451, -1000, 1064, 1492, 30873, 968, 58195, 11038, -1000, + -1000, 2724, 2724, 11038, 11038, 2532, -1000, -1000, -1000, -1000, + -1000, -1000, -1000, 2325, -1000, 553, 4715, -1000, -1000, -1000, + -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -167, 16384, + -1000, -1000, 4715, 4715, 1848, 2277, 3530, 68822, 74894, 1382, + 1382, 74894, 68063, 58195, 734, 74894, 1382, 74894, 74894, 74894, + -1000, 74894, -1000, 4626, 74894, 1382, 74894, 74894, 74894, 2178, + -1000, -1000, 32388, 2504, -1000, -1000, 2979, -1000, 16384, 20971, + -222, 16384, 20971, 20971, 16384, 20971, -1000, 16384, 2463, -1000, + -1000, 652, -1000, -1000, 3520, -1000, 3519, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, - 74543, 4811, -1000, -1000, 16341, 74543, 1360, 4599, 4818, 4604, - 4710, 4738, 1241, 3400, -1000, -287, 74543, 500, -1000, 2444, - 3500, 3918, -1000, 610, -1000, 1348, 610, -1000, 1125, 1125, - 2719, -1000, 2014, -1000, -1000, -1000, -1000, -1000, -1000, -1000, - 74543, 150, 6128, -1000, -1000, -1000, 3867, 1026, -1000, 4232, - 812, -1000, -1000, -1000, -1000, 74543, 4710, -24, 4703, 2742, - 2976, 2958, 2742, -1000, -1000, -1000, 1150, 4752, 20136, 20136, - -1000, -1000, 16341, -1000, 437, 70013, 2757, 2742, 2742, -1000, - -1000, 74543, -1000, -1000, -1000, 2757, 74543, 2757, 2757, 4752, - 2757, -1000, -1000, -1000, 2742, 2742, -1000, -1000, 16341, 2533, - -1000, -1000, 2757, 2757, -1000, -1000, 4752, 74543, 350, 4752, - 4752, 342, -1000, -1000, 74543, -1000, 2742, 3914, -1000, 74543, - 74543, 1296, 74543, -1000, 74543, 74543, -1000, -1000, -1000, 994, - 2219, 2118, -1000, 585, 4968, 57932, 774, 57932, 74543, -1000, - -1000, -1000, -1000, -1000, -1000, 334, -1000, -1000, -1000, -1000, + 3917, 3917, -1000, 3518, -1000, -1000, -1000, -1000, 4182, 3513, + -1000, -1000, 3512, -1000, -1000, -1000, -1000, -81, 4105, 1977, + -1000, 3914, 3913, 4715, -1000, 4756, 437, 4756, 4604, 2242, + 4232, 1382, -1000, 266, -400, 2960, 2950, 2946, 4703, 74894, + -1000, 4718, -1000, -1000, 727, -1000, -1000, -1000, 1414, 818, + -1000, -1000, -1000, -1000, -1000, -1000, -1000, 2499, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, + 30873, -1000, -1000, -1000, -1000, 755, 6979, 2, 0, 2321, + -1000, -1000, 74894, -1000, 74894, -1000, 74894, -1000, -79, -1000, + -1000, 3509, 4732, 74894, 4770, 74894, -307, 4770, 4770, 74894, + -311, 4770, 734, -1000, 2773, 2773, 3121, 74894, 74894, 74894, + 4408, 74894, 247, 74894, 4770, 4770, -1000, -1000, -1000, -1000, + -1000, -1000, -1000, 2828, 4770, 4770, 2356, 4103, 2658, 2828, + -1000, -1000, 2828, -400, -1000, 2828, -1000, -1000, -1000, -400, + 2498, -400, 74894, -1000, -1000, -1000, -1000, -1000, 804, 58195, + 64267, -1000, -1000, -1000, -1000, -1000, -1000, -1000, 2376, -1000, + -1000, -1000, 1450, 586, 2831, -1000, -1000, 425, 285, 544, + 1610, 3911, 3121, -1000, 3037, 3054, -1000, 1377, 1336, -1000, + -1000, -1000, -1000, 4231, -1000, -1000, -1000, 3342, 3191, -1000, + 3044, 3044, 2551, 2551, 2551, 2551, 2551, 2933, 2933, 2773, + 2773, -1000, -1000, -1000, 8740, 4230, 20199, 20199, 20199, 20199, + 4230, 20199, 20199, 20199, 20199, 4229, -1000, 1572, 1572, 6047, + 5926, 2644, -1000, -1000, 2585, 2585, -1000, -1000, -1000, -1000, + 16384, 251, 2974, -1000, 16384, 3642, 2457, 3435, 2273, 2825, + -1000, 4185, 16384, 2470, 355, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, - 723, -1000, 4777, 16341, 16341, 16341, 16341, -1000, 1228, 18618, - -1000, -1000, -1000, -1000, -1000, -1000, -1000, 20136, 20136, 20136, - 20136, 20136, 20136, 20136, 20136, 20136, 20136, 20136, 20136, 20136, - 20136, 4225, 2969, 2774, 4687, 20136, 20136, 20136, 20136, 20136, - 422, 36791, 2443, 3925, 2307, 490, 2601, 2601, 2601, 2601, - 2601, 16341, -1000, 2950, 3162, 16341, 16341, 16341, 16341, 45851, - 74543, -1000, -1000, 8737, 7112, 16341, 16341, 6695, 20136, 16341, - 4638, 16341, 16341, 16341, 4101, 9496, 74543, 16341, -1000, 4099, - 4098, -1000, -1000, 3175, 16341, -1000, -1000, 16341, -1000, -1000, - 16341, 20136, 16341, -1000, 16341, 16341, 16341, -1000, -1000, 879, - 879, 1410, 4638, 16341, 16341, 4638, 4638, 2849, 16341, 16341, - 4638, 4638, 4638, 2804, 4638, 4638, 4638, 4638, 4638, 4638, - 4638, 4019, 4638, 4638, 4638, 4638, 4638, 4097, 4094, 4093, - 4092, 16341, 4090, 16341, 16341, 16341, 16341, 16341, 15582, 4818, - 2531, 4699, -117, 14059, 4604, 4710, 4585, -91, 4738, -1000, - -1000, -1000, -1000, 3162, -195, 4231, -1000, -1000, -1000, -1000, - -1000, -1000, -1000, -1000, 74543, 3913, 3910, -377, -387, 1639, - -387, 2437, -1000, -232, 1660, 349, 74543, -1000, 3900, 3498, - 74543, 3899, 3482, 436, 429, 74543, 74543, 74543, 235, 1655, - 1626, 1634, -1000, -1000, 74543, 76805, -1000, 74543, 2641, 74543, - 74543, 1296, 1296, 74543, 4634, -1000, 74543, 74543, 1296, 1296, - -1000, 67748, 3888, 57932, 74543, 74543, 774, 74543, 74543, 1296, - 1296, 1296, 74543, -1000, 4528, 57932, 4523, 1895, 2945, -1000, - -1000, 74543, 16341, 2945, 2136, 4230, 1264, 1264, -1000, 74543, - 2306, 4633, 74543, 1308, -1000, 489, -1000, -1000, -1000, -1000, - 76050, 76050, 25457, 76050, -1000, 274, 74543, 74543, 74543, 74543, - 5649, 74543, 659, 4609, 1693, 67748, 69258, 4665, -1000, 57932, - 74543, 74543, 2300, -1000, 1359, 29999, -1000, 74543, 2246, -1000, - 229, -1000, 231, 244, 2762, 244, 2762, 1358, -1000, 986, - 593, 30754, 914, 57932, 11023, -1000, -1000, 2762, 2762, 11023, - 11023, 2451, -1000, -1000, -1000, -1000, -1000, -1000, -1000, 2299, - -1000, 536, 4699, -1000, -1000, -1000, -1000, -1000, -1000, -1000, - -1000, -1000, -1000, -1000, -117, 16341, -1000, -1000, 4699, 4699, - 2126, 2243, 3479, 68503, 74543, 1305, 1305, 74543, 67748, 57932, - 774, 74543, 1305, 74543, 74543, 74543, -1000, 74543, -1000, 4608, - 74543, 1305, 74543, 74543, 74543, 2247, -1000, -1000, 32261, 2435, - -1000, -1000, 2939, -1000, 16341, 20904, -177, 16341, 20904, 20904, - 16341, 20904, -1000, 16341, 2422, -1000, -1000, 5648, -1000, -1000, - 3472, -1000, 3469, -1000, -1000, -1000, -1000, -1000, -1000, -1000, - -1000, -1000, -1000, -1000, -1000, -1000, 3887, 3887, -1000, 3467, - -1000, -1000, -1000, -1000, 4175, 3461, -1000, -1000, 3460, -1000, - -1000, -1000, -1000, -42, 4088, 1877, -1000, 3877, 3875, 4699, - -1000, 4738, 487, 4738, 4585, 2213, 4229, 1305, -1000, 268, - -393, 2893, 2891, 2888, 4686, 74543, -1000, 4701, -1000, -1000, - 610, -1000, -1000, -1000, 1125, 846, -1000, -1000, -1000, -1000, - -1000, -1000, -1000, 2434, -1000, -1000, -1000, -1000, -1000, -1000, - -1000, -1000, -1000, -1000, -1000, -1000, 30754, -1000, -1000, -1000, - -1000, 955, 6945, 76, 75, 2293, -1000, -1000, 74543, -1000, - 74543, -1000, 74543, -1000, -39, -1000, -1000, 3459, 4714, 74543, - 4752, 74543, -278, 4752, 4752, 74543, -281, 4752, 774, -1000, - 2709, 2709, 3162, 74543, 74543, 74543, 4405, 74543, 250, 74543, - 4752, 4752, -1000, -1000, -1000, -1000, -1000, -1000, -1000, 2757, - 4752, 4752, 2431, 4087, 2742, 2757, -1000, -1000, 2757, -393, - -1000, 2757, -1000, -1000, -1000, -393, 2418, -393, 74543, -1000, - -1000, -1000, -1000, -1000, 585, 57932, 63972, -1000, -1000, -1000, - -1000, -1000, -1000, -1000, 2389, -1000, -1000, -1000, 1352, 516, - 2718, -1000, -1000, 418, 286, 474, 1521, 3874, 3162, -1000, - 2994, 2986, -1000, 1485, 1436, -1000, -1000, -1000, -1000, 4226, - -1000, -1000, -1000, 3005, 3444, -1000, 2887, 2887, 2509, 2509, - 2509, 2509, 2509, 2929, 2929, 2709, 2709, -1000, -1000, -1000, - 8737, 4225, 20136, 20136, 20136, 20136, 4225, 20136, 20136, 20136, - 20136, 4224, -1000, 1475, 1475, 5417, 5387, 2601, -1000, -1000, - 2538, 2538, -1000, -1000, -1000, -1000, 16341, 249, 2920, -1000, - 16341, 3594, 2525, 3439, 2079, 2714, -1000, 4177, 16341, 2414, - 358, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, + -1000, -1000, -1000, -1000, -1000, 4102, 4101, 3409, 4800, 5874, + 4090, 16384, -1000, -1000, 2818, 2817, 2815, -1000, 3360, 14858, + -1000, -1000, -1000, 4086, 2446, 4085, -1000, -1000, -1000, 4083, + 2812, 2022, 4081, 2730, 4080, 4079, 4076, 4074, 2315, 2312, + 2311, -1000, -1000, -1000, -1000, 16384, 3939, 3935, 16384, 16384, + 16384, 4072, 2811, 2799, 16384, 16384, 16384, 16384, 4070, 16384, + 16384, 16384, 16384, 16384, 16384, 16384, 16384, -365, 16384, 16384, + 16384, 16384, 16384, 431, 431, 431, 431, 3907, 431, 2533, + 2430, 3900, 3879, 2388, 2303, 2301, 4715, 4830, -1000, -1000, + 2774, -1000, 3121, -1000, -1000, 4756, 4756, -1000, 4828, 4604, + -245, 4228, 3508, 2295, -1000, -1000, -355, 3830, 1449, 74894, + -283, 74894, 1449, 74894, 74894, 2944, 1449, 74894, -285, 3910, + -1000, -1000, 3898, -1000, -1000, 74894, 74894, 74894, 74894, -53, + 4617, 4616, -1000, -1000, 1806, 1753, 1788, -1000, 74894, -1000, + 3896, 4625, 4624, 4716, 1352, 74894, 74894, -12, 74894, 4227, + 4226, 2445, 74894, 74894, 639, -1000, -1000, 74894, 2189, -1000, + 586, 182, 996, 2040, 4407, 1313, 74894, 74894, 74894, 4650, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, - -1000, 4086, 4083, 3385, 4776, 5005, 4082, 16341, -1000, -1000, - 2713, 2712, 2706, -1000, 3252, 14823, -1000, -1000, -1000, 4081, - 2410, 4079, -1000, -1000, -1000, 4076, 2702, 2038, 4075, 2836, - 4074, 4072, 4071, 4069, 2290, 2289, 2284, -1000, -1000, -1000, - -1000, 16341, 3917, 3912, 16341, 16341, 16341, 4068, 2699, 2698, - 16341, 16341, 16341, 16341, 4066, 16341, 16341, 16341, 16341, 16341, - 16341, 16341, 16341, -333, 16341, 16341, 16341, 16341, 16341, 393, - 393, 393, 393, 3901, 393, 2404, 2322, 3897, 3873, 2467, - 2283, 2278, 4699, 4818, -1000, -1000, 2697, -1000, 3162, -1000, - -1000, 4738, 4738, -1000, 4810, 4585, -193, 4223, 3458, 2264, - -1000, -1000, -323, 3786, 1347, 74543, -240, 74543, 1347, 74543, - 74543, 2885, 1347, 74543, -249, 3872, -1000, -1000, 3863, -1000, - -1000, 74543, 74543, 74543, 74543, -14, 4596, 4595, -1000, -1000, - 1656, 1614, 1669, -1000, 74543, -1000, 3860, 4606, 4593, 4700, - 1341, 74543, 74543, 69, 74543, 4222, 4221, 2406, 74543, 74543, - 563, -1000, -1000, 74543, 2015, -1000, 516, 128, 978, 1935, - 4404, 1317, 74543, 74543, 74543, 4630, -1000, -1000, -1000, -1000, - -1000, -1000, -1000, -1000, -1000, -1000, 1871, -1000, 4591, 4078, - 3122, 1871, 4424, -120, -1000, 28489, 82090, 74543, 2136, 2136, - -1000, 4220, 2693, -1000, 66993, 60197, 60197, 60197, 60197, 60197, - 60197, 60197, 60197, -1000, 4489, 4447, 4219, -1000, 4473, 4455, - 4454, 847, 4487, 4481, 4439, 4212, -1000, 58687, -1000, -1000, - -1000, 2443, 2692, -1000, 413, 3551, 1512, 16341, 4659, 4557, - 4628, 4212, 2261, -1000, -1000, -1000, -1000, 4741, 1032, 2200, - 1283, 1283, 1712, 4739, 26212, -1000, 2734, 1910, 1346, 4559, - 625, -1000, 2734, -34, 1258, 2734, 2734, 2734, 2734, 2734, - 2734, 2734, 2734, 1116, 1104, 2734, 2734, 2734, 2734, 2734, - 2734, 2734, 2734, 2734, 2734, 2734, 1721, 2734, 2734, 2734, - 2734, 2734, -1000, 2734, 4218, -1000, -1000, -1000, -1000, -1000, - -1000, -1000, 1157, 1041, -1000, -1000, 465, 774, 1345, 266, - 257, 558, 4657, 671, -1000, 660, 2015, 983, 4651, 748, - 74543, 74543, 352, 2002, 2762, 244, 219, -1000, -1000, 2246, - 11023, 2246, 11023, 3457, -1000, -1000, 1342, -1000, -1000, 1935, - -1000, 74543, 74543, -1000, -1000, 4217, 2884, -1000, -1000, 23186, - -1000, 11023, 11023, -1000, -1000, 42076, 74543, -1000, 138, -1000, - 217, 4738, 2243, 2234, -1000, 3221, 4064, 4063, -1000, -1000, - -1000, -1000, -1000, -1000, -265, -1000, -1000, 74543, -1000, 74543, - 74543, 1923, -1000, 2229, 1935, 4423, 74543, 1335, -1000, -1000, - 4214, 74543, -1000, 3858, -1000, 4775, -265, 4774, 13300, -1000, - -1000, 66237, -1000, 65482, 2803, -1000, 20904, 3095, 281, -1000, - 424, -314, 264, 3019, 262, 3162, -1000, -1000, 4062, 4061, - 4058, 2691, -1000, 2687, 4056, -1000, 2684, 2683, 3456, -1000, - -316, 291, 4738, 4585, 3856, 2228, -1000, 4585, -1000, -328, - -120, -1000, 27728, 82090, 74543, 3854, 3141, 4626, 3518, -287, - 16341, 64727, 16341, 1582, 2397, 438, -1000, -1000, -1000, 74543, - 2224, 813, 3867, -1000, 2675, -1000, -1000, -1000, -1000, 3455, - -1000, -1000, 2742, 74543, -1000, -1000, 2195, 74543, 50381, -1000, - -1000, -1000, -1000, 74543, -1000, 774, -1000, 57932, -1000, 2742, - -1000, -1000, 4752, -1000, -1000, 16341, 16341, 314, 4752, 2742, - 2742, -1000, 2757, -1000, 74543, -1000, -393, 63972, 2103, -1000, - 1333, 2392, 2391, -1000, 57932, 618, 57932, -1000, -1000, 4723, - -1000, 74543, 4592, -1000, -1000, -1000, 3786, 2883, -391, 74543, + 1857, -1000, 4605, 4099, 3273, 1857, 4436, -168, -1000, 28596, + 82481, 74894, 2027, 2027, -1000, 4225, 2767, -1000, 67304, 60472, + 60472, 60472, 60472, 60472, 60472, 60472, 60472, -1000, 4500, 4483, + 4224, -1000, 4488, 4460, 4458, 861, 4499, 4485, 4444, 4216, + -1000, 58954, -1000, -1000, -1000, 2679, 2749, -1000, 684, 3665, + 1722, 16384, 4690, 4575, 4649, 4216, 2286, -1000, -1000, -1000, + -1000, 4759, 1031, 2214, 1300, 1300, 1688, 4758, 26307, -1000, + 2796, 2164, 1446, 4581, 720, -1000, 2796, -77, 1310, 2796, + 2796, 2796, 2796, 2796, 2796, 2796, 2796, 1150, 1148, 2796, + 2796, 2796, 2796, 2796, 2796, 2796, 2796, 2796, 2796, 2796, + 1833, 2796, 2796, 2796, 2796, 2796, -1000, 2796, 4223, -1000, + -1000, -1000, -1000, -1000, -1000, -1000, 1166, 1062, -1000, -1000, + 427, 734, 1441, 269, 265, 634, 4689, 759, -1000, 784, + 2189, 1151, 4688, 811, 74894, 74894, 513, 2166, 2724, 239, + 217, -1000, -1000, 2282, 11038, 2282, 11038, 3507, -1000, -1000, + 1433, -1000, -1000, 2040, -1000, 74894, 74894, -1000, -1000, 4222, + 2941, -1000, -1000, 23265, -1000, 11038, 11038, -1000, -1000, 42255, + 74894, -1000, 208, -1000, 235, 4756, 2277, 2272, -1000, 3277, + 4068, 4067, -1000, -1000, -1000, -1000, -1000, -1000, -296, -1000, + -1000, 74894, -1000, 74894, 74894, 2016, -1000, 2268, 2040, 4428, + 74894, 1322, -1000, -1000, 4218, 74894, -1000, 3891, -1000, 4796, + -296, 4795, 13327, -1000, -1000, 66544, -1000, 65785, 2966, -1000, + 20971, 3253, 263, -1000, 372, -335, 262, 3013, 261, 3121, + -1000, -1000, 4065, 4062, 4061, 2744, -1000, 2738, 4060, -1000, + 2733, 2712, 3504, -1000, -350, 286, 4756, 4604, 3890, 2267, + -1000, 4604, -1000, -360, -168, -1000, 27831, 82481, 74894, 3889, + 3138, 4647, 3563, -317, 16384, 65026, 16384, 1708, 2444, 521, + -1000, -1000, -1000, 74894, 2266, 2245, 3887, -1000, 2687, -1000, + -1000, -1000, -1000, 3502, -1000, -1000, 2658, 74894, -1000, -1000, + 1904, 74894, 50604, -1000, -1000, -1000, -1000, 74894, -1000, 734, + -1000, 58195, -1000, 2658, -1000, -1000, 4770, -1000, -1000, 16384, + 16384, 287, 4770, 2658, 2658, -1000, 2828, -1000, 74894, -1000, + -400, 64267, 2188, -1000, 1408, 2439, 2425, -1000, 58195, 713, + 58195, -1000, -1000, 4740, -1000, 74894, 4615, -1000, -1000, -1000, + 3830, 2930, -398, 74894, -1000, -1000, -1000, -1000, -1000, -1000, + -1000, -1000, -1000, -1000, 2685, -1000, 1572, 1572, 6047, 5794, + -1000, 1572, 1572, 6047, 5679, 20199, -1000, 20199, -1000, 20199, + -1000, -1000, -1000, -1000, 3875, -1000, 2743, -1000, 16384, 3240, + 405, 16384, 405, 2945, 36183, 46050, -54, 4612, 3847, 74894, + 16384, -1000, -1000, 16384, 16384, 20199, -1000, 3843, -1000, -1000, + -1000, -1000, 16384, 16384, 3332, -1000, 74894, -1000, -1000, -1000, + -1000, 36183, -1000, 20199, -1000, -1000, -1000, -1000, 16384, 16384, + 16384, 2151, -366, -366, 2151, 3838, 2683, 431, 431, -366, + 3827, 3822, 3803, 2682, 431, 3780, 3772, 3768, 3761, 3752, + 3736, 3718, 3714, 4767, 3701, 3695, 2680, 3686, 2375, -1000, + 63508, -1000, -1000, -1000, 431, -1000, -377, 16384, -377, 16384, + -377, -377, 16384, 3167, 17910, 4756, 4715, 14090, -1000, 4604, + 4604, 707, -1000, -1000, 4217, 1306, 2249, 3501, 3881, 197, + -1000, 2925, -1000, 807, -1000, 74894, 4794, -1000, 2424, 3878, + 62749, -1000, 1780, 74894, -1000, -1000, 4793, 4790, -1000, -1000, + 74894, 74894, 74894, -1000, -1000, -1000, 1738, -1000, 3877, -1000, + -1000, 662, 504, 3370, 4825, 4823, 2980, 3876, 676, 1888, + 24783, 74894, 4216, 4216, 369, 2796, 925, 1106, 58195, 1173, + -1000, 61990, 3451, 2924, 4427, 1870, 4594, 74894, 61231, 2306, + 1373, 4214, 949, 652, -1000, 4605, 4213, 74894, 4605, -1000, + -360, -1000, 4592, 1995, -1000, 4210, -1000, 2668, 4535, -1000, + 2423, 2246, -1000, 2918, 2667, 2662, 2917, -360, 325, 6208, + -1000, 74894, 74894, 2330, -1000, 2416, 466, 4399, 4399, 4399, + -1000, 4399, 4399, -1000, -1000, -1000, -1000, 4498, 3866, -1000, + 4497, -1000, 4484, -1000, 4481, -1000, -1000, -1000, -1000, 4457, + -1000, -1000, -1000, 30114, -1000, 60472, -1000, 1722, -1000, 4711, + 1674, 1674, 1674, 4059, 41496, 40737, -1000, -1000, 949, 652, + 4646, 5837, 1113, 74894, 74894, -1000, -1000, 74894, 74894, 74894, + 4130, -1000, 74894, -1000, -1000, 1355, -1000, 1674, 1674, 1333, + 74894, 3075, 1674, 3072, 4208, -1000, -1000, 74894, 74894, 74894, + 74894, -1000, -1000, 74894, 59713, -1000, 74894, 74894, -1000, 74894, + 3071, 74894, 3070, 4597, -1000, 2796, 2796, 1691, -1000, -1000, + 1088, -1000, 59713, 3498, 3497, 3496, 3492, 3490, 3864, 3858, + 3857, 2796, 2796, 3483, 3856, 58954, 3854, 1941, 3471, 3465, + 3461, 3417, 3853, 1666, -1000, 3850, 3357, 3339, 3319, 74894, + 4204, 3765, -1000, -1000, 3370, 3848, 4203, 3458, 3845, 1593, + 734, 3839, 4426, 369, 2796, 757, 74894, 2916, 2912, 1106, + 994, 994, 995, 128, 31632, -1000, -1000, -1000, 74894, 74894, + 11038, -1000, -1000, 244, 221, -1000, -1000, -1000, -1000, 58195, + 3836, 968, -1000, -1000, -1000, -1000, -1000, -1000, -1000, 4604, + 16384, 4579, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, + 74894, 1322, -1000, -1000, -1000, 24783, -1000, -1000, 74894, -1000, + 4770, -1000, 30114, -1000, -1000, -1000, 2414, -1000, 2745, -383, + 20971, 2684, 2660, -1000, 16384, 20971, 16384, -231, 742, -233, + -1000, -1000, -1000, -1000, 3835, -1000, -1000, -1000, 3457, -1000, + 3444, -1000, 222, 476, 510, 4604, -1000, 543, 3636, -1000, + -1000, 13327, 1995, 2653, 4532, 2246, 2643, -1000, -1000, -1000, + -1000, 4627, -1000, -1000, -1000, -1000, -1000, 245, -407, -408, + 241, 3834, 74894, 3442, -1000, -1000, -1000, -1000, 4770, 2658, + 4770, 50604, 2658, 2873, -1000, -1000, -1000, -1000, 2040, -1000, + 4770, -1000, 3121, 3121, -1000, -1000, -400, 4770, 4770, 2658, + -1000, -1000, 4787, 58195, 734, 2612, 57436, -1000, 705, 2339, + 1018, 3832, -1000, 1535, 191, 3831, 3830, -1000, -1000, -1000, + -1000, 20199, -1000, -1000, 20199, 2836, 2773, -1000, -1000, -1000, + 3121, 16384, 4057, 3193, 4056, 4055, -1000, -1000, 4188, 4188, + -1000, 4185, 4186, 4185, 2585, 2585, 4053, -1000, 4198, -1000, + 4612, -1000, 2642, 3441, 3662, 5576, 431, 3658, 3634, 16384, + 2411, 4052, 5457, 2169, 1917, 3603, 77, 431, 4766, 431, + -114, 431, 431, -1000, -1000, -1000, 431, 431, 431, 431, + 431, -1000, 431, 431, 431, 431, 431, 431, 431, 431, + 4196, 431, 431, 431, 431, 16384, -1000, 81722, -1000, 431, + 4576, 2350, 431, 2337, 431, 431, 3598, 19, -263, -41, + -264, 4604, 4756, -1000, -1000, -1000, 16384, 418, 4051, 7167, + 2236, 1002, 1002, 3830, 3829, 74894, 3826, -290, 74894, -1000, + -409, -410, -291, 74894, 3823, 74894, 74894, 281, 2940, 3097, + -1000, 3820, -1000, -1000, 56677, 74894, 74894, 75653, 1056, 74894, + 74894, 3819, -1000, -1000, 4193, -1000, 4192, -82, 4191, -26, + 3818, 4049, 2232, -1000, -1000, 74894, -1000, -1000, -1000, 4039, + 2230, -1000, -1000, -1000, 652, 4189, 4188, 4188, 1752, 4185, + -1000, -1000, -1000, 4185, -1000, -1000, -1000, -1000, 4188, 4188, + 4188, 4188, 4188, 4188, 4187, -1000, -1000, 4186, 4186, 4186, + 4185, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, + 1875, 4182, 4183, 4183, 4182, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, - 2670, -1000, 1475, 1475, 5417, 4909, -1000, 1475, 1475, 5417, - 4712, 20136, -1000, 20136, -1000, 20136, -1000, -1000, -1000, -1000, - 3869, -1000, 2717, -1000, 16341, 3064, 422, 16341, 422, 2450, - 36036, 45851, -15, 4590, 3837, 74543, 16341, -1000, -1000, 16341, - 16341, 20136, -1000, 3802, -1000, -1000, -1000, -1000, 16341, 16341, - 3201, -1000, 74543, -1000, -1000, -1000, -1000, 36036, -1000, 20136, - -1000, -1000, -1000, -1000, 16341, 16341, 16341, 2058, -335, -335, - 2058, 3795, 2660, 393, 393, -335, 3763, 3739, 3732, 2658, - 393, 3728, 3721, 3716, 3711, 3704, 3646, 3631, 3613, 4750, - 3589, 3585, 2656, 3579, 2415, -1000, 63217, -1000, -1000, -1000, - 393, -1000, -354, 16341, -354, 16341, -354, -354, 16341, 3104, - 17859, 4738, 4699, 14059, -1000, 4585, 4585, 598, -1000, -1000, - 4213, 1257, 2212, 3452, 3853, 180, -1000, 2877, -1000, 728, - -1000, 74543, 4771, -1000, 2384, 3852, 62462, -1000, 1650, 74543, - -1000, -1000, 4770, 4769, -1000, -1000, 74543, 74543, 74543, -1000, - -1000, -1000, 1611, -1000, 3850, -1000, -1000, 461, 431, 3313, - 4808, 4807, 2946, 3849, 579, 1976, 24696, 74543, 4212, 4212, - 389, 2734, 889, 1012, 57932, 1140, -1000, 61707, 3255, 2875, - 4419, 1686, 4575, 74543, 60952, 987, 1338, 4209, 904, 5648, - -1000, 4591, 4202, 74543, 4591, -1000, -328, -1000, 4583, 1886, - -1000, 4200, -1000, 2652, 4515, -1000, 2372, 2183, -1000, 2874, - 2649, 2647, 2873, -328, 353, 6178, -1000, 74543, 74543, 1926, - -1000, 2354, 323, 4396, 4396, 4396, -1000, 4396, 4396, -1000, - -1000, -1000, -1000, 4485, 3848, -1000, 4482, -1000, 4477, -1000, - 4461, -1000, -1000, -1000, -1000, 4451, -1000, -1000, -1000, 29999, - -1000, 60197, -1000, 1512, -1000, 4695, 1549, 1549, 1549, 4055, - 41321, 40566, -1000, -1000, 904, 5648, 4621, 5747, 1078, 74543, - 74543, -1000, -1000, 74543, 74543, 74543, 4107, -1000, 74543, -1000, - -1000, 1514, -1000, 1549, 1549, 1296, 74543, 3057, 1549, 3054, - 4199, -1000, -1000, 74543, 74543, 74543, 74543, -1000, -1000, 74543, - 59442, -1000, 74543, 74543, -1000, 74543, 3053, 74543, 3048, 4544, - -1000, 2734, 2734, 1510, -1000, -1000, 968, -1000, 59442, 3446, - 3445, 3443, 3440, 3438, 3847, 3830, 3828, 2734, 2734, 3436, - 3825, 58687, 3821, 1958, 3434, 3417, 3415, 3318, 3818, 1609, - -1000, 3815, 3300, 3291, 3287, 74543, 4198, 3717, -1000, -1000, - 3313, 3814, 4196, 3413, 3805, 1440, 774, 3801, 4416, 389, - 2734, 654, 74543, 2868, 2865, 1012, 935, 935, 966, 127, - 31509, -1000, -1000, -1000, 74543, 74543, 11023, -1000, -1000, 224, - 222, -1000, -1000, -1000, -1000, 57932, 3800, 914, -1000, -1000, - -1000, -1000, -1000, -1000, -1000, 4585, 16341, 4556, -1000, -1000, - -1000, -1000, -1000, -1000, -1000, -1000, 74543, 1335, -1000, -1000, - -1000, 24696, -1000, -1000, 74543, -1000, 4752, -1000, 29999, -1000, - -1000, -1000, 2343, -1000, 2953, -367, 20904, 2703, 2603, -1000, - 16341, 20904, 16341, -179, 640, -181, -1000, -1000, -1000, -1000, - 3796, -1000, -1000, -1000, 3409, -1000, 3407, -1000, 172, 420, - 442, 4585, -1000, 472, 3524, -1000, -1000, 13300, 1886, 2643, - 4512, 2183, 2640, -1000, -1000, -1000, -1000, 4685, -1000, -1000, - -1000, -1000, -1000, 245, -401, -403, 241, 3794, 74543, 3404, - -1000, -1000, -1000, -1000, 4752, 2742, 4752, 50381, 2742, 2701, - -1000, -1000, -1000, -1000, 1935, -1000, 4752, -1000, 3162, 3162, - -1000, -1000, -393, 4752, 4752, 2742, -1000, -1000, 4768, 57932, - 774, 2529, 57177, -1000, 596, 2288, 942, 3791, -1000, 1404, - 179, 3789, 3786, -1000, -1000, -1000, -1000, 20136, -1000, -1000, - 20136, 2739, 2709, -1000, -1000, -1000, 3162, 16341, 4054, 3157, - 4053, 4049, -1000, -1000, 4181, 4181, -1000, 4177, 4178, 4177, - 2538, 2538, 4048, -1000, 4192, -1000, 4590, -1000, 2638, 3106, - 3567, 5349, 393, 3548, 3529, 16341, 2339, 4047, 4102, 1995, - 1937, 3522, 112, 393, 4748, 393, -75, 393, 393, -1000, - -1000, -1000, 393, 393, 393, 393, 393, -1000, 393, 393, - 393, 393, 393, 393, 393, 393, 4191, 393, 393, 393, - 393, 16341, -1000, 81335, -1000, 393, 4555, 2301, 393, 2238, - 393, 393, 3517, 1, -210, 0, -212, 4585, 4738, -1000, - -1000, -1000, 16341, 458, 4037, 6695, 2180, 989, 989, 3786, - 3781, 74543, 3777, -252, 74543, -1000, -404, -408, -256, 74543, - 3776, 74543, 74543, 277, 2810, 3042, -1000, 3774, -1000, -1000, - 56422, 74543, 74543, 75298, 1040, 74543, 74543, 3773, -1000, -1000, - 4189, -1000, 4186, -47, 4185, 64, 3772, 4027, 2169, -1000, - -1000, 74543, -1000, -1000, -1000, 4023, 2149, -1000, -1000, -1000, - 5648, 4184, 4181, 4181, 1710, 4177, -1000, -1000, -1000, 4177, - -1000, -1000, -1000, -1000, 4181, 4181, 4181, 4181, 4181, 4181, - 4180, -1000, -1000, 4178, 4178, 4178, 4177, -1000, -1000, -1000, - -1000, -1000, -1000, -1000, -1000, -1000, 1872, 4175, 4176, 4176, - 4175, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, + -1000, -1000, -1000, -1000, 4644, 4643, 3553, -1000, -1000, -1000, + 39978, 74894, 994, -1000, -1000, -1000, 1188, 767, 3440, 997, + -1000, 74894, 956, 799, 4554, 2899, 3814, 74894, -1000, -1000, + -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, 4594, -1000, + 1643, -400, 74894, 924, 21736, -1000, 1797, 4642, 74894, -1000, + 74894, 55917, -1000, -167, -1000, -1000, 24024, -1000, 2796, 24024, + 912, 2923, -1000, 16384, 2620, 4605, -1000, 3067, -168, 14090, + 2073, 74894, -168, 74894, 74894, 14090, -1000, 74894, -1000, 74894, + 16384, -1000, -1000, -360, 4038, -1000, 1288, 2117, 219, 30114, + 74894, -1000, 52881, 16384, -1000, -1000, 16384, 4181, -1000, -1000, + -1000, -371, -1000, 4037, -1000, -1000, -1000, -1000, -1000, 4461, + -1000, 384, -1000, 4179, 4556, -1000, -1000, -1000, -1000, 2224, + 51363, -1000, 2898, -1000, -1000, 2197, 49845, -1000, 2896, -1000, + -1000, 912, -1000, 1857, -1000, 2308, 1187, 27066, 1022, 776, + 761, -1000, 1168, -1000, 903, 1819, 884, 1304, 1304, -1000, + 1244, 4566, 652, -1000, 74894, -1000, 74894, 74894, -1000, 74894, + 74894, 1333, 16384, 4566, 74894, 1406, -1000, 2195, -1000, 4612, + -1000, -1000, 2190, -1000, -1000, -1000, -1000, 74894, -1000, -1000, + -1000, -1000, -1000, -1000, -1000, 36183, 36183, 4684, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, - 4620, 4619, 3509, -1000, -1000, -1000, 39811, 74543, 935, -1000, - -1000, -1000, 1163, 823, 3395, 920, -1000, 74543, 883, 721, - 4533, 2848, 3770, 74543, -1000, -1000, -1000, -1000, -1000, -1000, - -1000, -1000, -1000, -1000, 4575, -1000, 1820, -393, 74543, 887, - 21665, -1000, 2225, 4618, 74543, -1000, 74543, 55666, -1000, -117, - -1000, -1000, 23941, -1000, 2734, 23941, 880, 2798, -1000, 16341, - 2631, 4591, -1000, 3030, -120, 14059, 2277, 74543, -120, 74543, - 74543, 14059, -1000, 74543, -1000, 74543, 16341, -1000, -1000, -328, - 4022, -1000, 1241, 2134, 200, 29999, 74543, -1000, 52646, 16341, - -1000, -1000, 16341, 4171, -1000, -1000, -1000, -341, -1000, 4021, - -1000, -1000, -1000, -1000, -1000, 4457, -1000, 276, -1000, 4155, - 4545, -1000, -1000, -1000, -1000, 2146, 51136, -1000, 2842, -1000, - -1000, 2145, 49626, -1000, 2839, -1000, -1000, 880, -1000, 1871, - -1000, 2101, 1299, 26967, 1007, 778, 742, -1000, 1099, -1000, - 903, 1654, 865, 1384, 1384, -1000, 1226, 4551, 5648, -1000, - 74543, -1000, 74543, 74543, -1000, 74543, 74543, 1296, 16341, 4551, - 74543, 1332, -1000, 2141, -1000, 4590, -1000, -1000, 2131, -1000, - -1000, -1000, -1000, 74543, -1000, -1000, -1000, -1000, -1000, -1000, - -1000, 36036, 36036, 4649, -1000, -1000, -1000, -1000, -1000, -1000, - -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, 3769, 3768, + -1000, -1000, 3813, 3807, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, - -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, 74543, - 2630, -1000, 2833, 3766, 64, 10264, -1000, -1000, 1328, -1000, - 4415, 1403, 3509, 39811, 2829, 2762, 3765, 3762, 935, -1000, - 3760, 3755, -1000, 3255, 2827, 1401, 74543, -1000, 1928, 74543, - 74543, -1000, 2123, -1000, 2819, 2147, -1000, -1000, -1000, -1000, - 2103, 4018, -1000, -1000, 4580, -1000, -1000, 4560, -1000, -1000, - 4017, 885, 4747, 2075, 54911, -1000, -367, 2607, -1000, 3006, - 260, 3001, 74543, -1000, -1000, -1000, 4016, 4015, -1000, -131, - 435, 4737, 4736, 4580, -163, 3749, -1000, 1869, 2272, -120, - -1000, 1688, -1000, -1000, -1000, -1000, -410, -1000, -1000, -1000, - 4752, -1000, 2742, 4752, 4752, -280, -1000, -1000, -1000, -1000, - 4752, 774, -1000, 2119, -1000, -1000, -1000, -1000, -1000, -1000, - 498, -1000, 74543, -1000, 2067, 169, -1000, -1000, -1000, 3162, - -1000, 422, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, - -1000, -1000, 39056, -1000, -1000, -1000, 16341, -1000, -1000, -1000, - -1000, -1000, 3463, 74543, -1000, -1000, 16341, 16341, -1000, 4013, - 3742, -1000, 4154, -1000, 4012, 3737, -1000, -1000, -1000, -1000, + -1000, -1000, -1000, 74894, 2614, -1000, 2895, 3801, -26, 10275, + -1000, -1000, 1403, -1000, 4420, 1495, 3553, 39978, 2890, 2724, + 3800, 3799, 994, -1000, 3795, 3791, -1000, 3451, 2889, 1477, + 74894, -1000, 2035, 74894, 74894, -1000, 2114, -1000, 2878, 2161, + -1000, -1000, -1000, -1000, 2188, 4032, -1000, -1000, 4591, -1000, + -1000, 4570, -1000, -1000, 4029, 920, 4765, 2156, 55158, -1000, + -383, 2686, -1000, 3201, 257, 2934, 74894, -1000, -1000, -1000, + 4028, 4027, -1000, -175, 506, 4755, 4753, 4591, -190, 3787, + -1000, 1971, 1886, -168, -1000, 1863, -1000, -1000, -1000, -1000, + -426, -1000, -1000, -1000, 4770, -1000, 2658, 4770, 4770, -308, + -1000, -1000, -1000, -1000, 4770, 734, -1000, 2050, -1000, -1000, + -1000, -1000, -1000, -1000, 570, -1000, 74894, -1000, 2118, 190, + -1000, -1000, -1000, 3121, -1000, 405, -1000, -1000, -1000, -1000, + -1000, -1000, -1000, -1000, -1000, -1000, 39219, -1000, -1000, -1000, + 16384, -1000, -1000, -1000, -1000, -1000, 3549, 74894, -1000, -1000, + 16384, 16384, -1000, 4026, 3781, -1000, 4177, -1000, 4025, 3779, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, - -1000, 4818, -1000, -1000, -1000, -1000, 3396, 4011, 1232, -1000, - -1000, -1000, -354, 16341, -1000, -354, 16341, -1000, -1000, 4767, - 2621, 4009, 4008, 2610, 4003, 4001, 4580, 4585, 1877, 3999, - 2000, -1000, -1000, -1000, -1000, -1000, -1000, 3996, 6695, 1537, - 3730, 1537, -1000, -1000, -1000, -1000, 74543, -1000, -1000, -1000, - 74543, 4765, 38301, 1321, -393, 918, 4153, -1000, 940, 2810, - 1648, 4152, 3724, -1000, 74543, 4763, 74543, 3313, 1031, 3313, - 1119, 74543, -265, 51136, 49626, 61, 3390, 10264, -1000, 3720, - -1000, -18, 1976, 5648, 1386, 24696, 4571, 2225, 2225, -1000, - 3719, 511, -1000, -1000, -1000, 814, -1000, 3389, -1000, -1000, - 3269, 2342, 543, -1000, -1000, -1000, -1000, -1000, -1000, 3397, - 74543, 54156, 3397, 3450, 2816, -396, -1000, 4151, -1000, 2734, - 2734, 2734, 1321, 884, 74543, 3993, 1980, -1000, -1000, -1000, - -1000, -1000, 2225, -1000, 4540, 74543, 2004, 3991, -269, 3990, - 1268, 2765, 2749, -1000, 3386, 2604, 4511, 1599, -1000, 3989, - 1943, -120, -1000, -1000, 1886, 2338, -1000, -1000, -1000, 2815, - 3162, -1000, -1000, 53401, 29999, 52646, 2018, -1000, 2326, -1000, - 2932, 3162, 74543, -1000, 2323, -1000, -1000, -1000, 51891, -1000, - 4735, 4734, 51136, 2602, 50381, 49626, 2600, 48871, 1268, -1000, - 1000, 3368, 20136, 4150, 20136, 4149, 1013, 4147, -1000, -1000, - -1000, -1000, -1000, 1181, 4570, -1000, 759, -1000, -1000, -1000, - -1000, 1054, 1048, -1000, 2596, -1000, 74543, -1000, -1000, 74543, - 6649, 4146, -1000, 4145, 4144, 4403, 988, 4143, 4142, 74543, - 3391, -1000, 4551, 74543, 74543, -1000, 74543, -1000, 2416, -1000, - 36036, -1000, -1000, 2594, -1000, 3717, 3715, -1000, -1000, 3985, - 3162, -1000, 2341, 774, 1390, 74543, -1000, 511, 3714, 11023, - -1000, -1000, -1000, -1000, -1000, 4533, 3713, 3397, 74543, -1000, - 74543, 1928, 1928, 4818, 29999, 74543, 14059, -328, -1000, -1000, - 4554, 2563, -1000, -1000, -1000, 74543, 4744, 4733, -1000, 1293, - 20904, -183, 637, -1000, -1000, -1000, -148, 3712, -1000, -1000, - 4732, 3710, 3532, -1000, 291, 3709, -1000, -120, -1000, 1886, - 3707, -1000, -1000, -1000, 4752, -1000, -1000, 74543, -1000, 1935, - -1000, -1000, 1808, 1122, -1000, 3984, 2800, -1000, -1000, -1000, - 3370, -1000, -1000, 3358, 3334, -335, -1000, 4818, 393, -1000, - 3983, 393, -1000, 4818, -1000, 4730, 393, 3330, 393, 3278, - -354, 4552, -1000, 3705, -1000, -1000, -1000, 3701, -1000, -1000, - -1000, 4580, -1000, 16341, -1000, 3980, -1000, 3696, -1000, -1000, - 3694, 3693, -262, -1000, -1000, 720, 1321, -1000, 562, 74543, - 1006, -1000, 48116, 10264, -397, 871, 74543, 4762, 3692, 3313, - 3691, 3313, 74543, 1028, -1000, 2583, 2582, 4617, 3672, -1000, - 3972, -1000, 3667, 3666, -1000, -1000, 5648, -1000, 2400, 4809, - 4809, -1000, -1000, 4721, -1000, 2279, 716, -1000, -1000, 3228, - 973, -1000, -1000, 3663, 961, -1000, 1928, -1000, -1000, 2814, - 3173, 3588, 45851, 36036, 36791, 3662, -1000, 74543, -1000, 6273, - 23941, -1000, 1321, 4677, 856, 4699, 758, 4808, 6379, 1753, - 2728, -1000, 3367, -1000, 3366, -1000, -1000, 4141, 74543, -1000, - -1000, 1886, 74543, 16341, 4752, 2018, 197, -328, -1000, 2518, - 312, -1000, 37546, 2571, -1000, 16341, 2546, -1000, -1000, -1000, - -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, 1753, - 4107, 4729, -1000, 3784, 74543, 3625, 74543, 4140, 2811, 20136, - 7406, 7406, 7406, 7406, 7406, 20136, -1000, -1000, -1000, 2809, - -1000, -1000, -1000, 1226, 4510, -1000, -1000, 6649, -1000, -1000, - 3072, 3065, 20136, -1000, -1000, 3659, 3658, 3651, 36791, 1389, - 2801, 2799, 1456, 4139, -1000, 1071, 4789, 3361, -1000, -1000, - -1000, 1500, -170, 4138, 3008, 3000, -1000, 74543, -1000, 45851, - 45851, 45851, 1809, 1809, 45851, 45851, 4136, 1384, -1000, -1000, - -1000, -1000, 2734, 2394, -1000, -1000, -1000, -1000, -1000, 74543, - 2312, -1000, -1000, -1000, 3450, -1000, -1000, 1923, -1000, 4710, - 2018, -1000, -1000, -1000, -1000, -1000, 4504, -1000, -343, 16341, - 17100, -1000, -367, 74543, 74543, -150, 3359, -1000, 3640, 432, - -1000, -1000, 420, -1000, 1886, -1000, -1000, 2742, -152, 342, - 36036, 2797, -1000, 3971, 526, -25, -1000, -1000, -1000, 393, - 3966, -1000, 393, -1000, 523, 16341, -1000, -354, -1000, -354, - 393, -1000, 3965, 3953, -1000, 3256, -1000, -1000, -1000, -1000, - 74543, 74543, -1000, 74543, 3639, 3356, -1000, -1000, 2534, -1000, - -1000, -1000, 2984, 2980, 2530, 3951, 3559, 74543, 852, 74543, - -265, 3632, -265, 3630, 1027, 3313, -1000, -1000, -200, -1000, - -1000, -1000, -1000, -19, -1000, -1000, -1000, 652, -1000, 977, - 3525, 3354, -1000, -1000, 715, -1000, -1000, -1000, 3397, 3629, - -1000, -1000, 168, -1000, 2792, 2515, -1000, -1000, -1000, 814, - 1224, -1000, 2225, 4616, 6003, -1000, 1910, -1000, -1000, -1000, - 74543, -1000, -1000, -1000, -1000, -1000, -1000, -1000, 1808, 2734, - -1000, 1224, 1117, 2824, -1000, 3350, -1000, -1000, 16341, 1831, - -1000, 3162, 4818, -328, -1000, 13300, -1000, 2974, 74543, 74543, - -1000, -1000, -1000, 47361, 1112, -1000, 1009, -1000, 2511, -1000, - 2506, 46606, 3347, 3380, -1000, 2734, 2734, 3624, 3621, 804, - -1000, -1000, 2734, 2734, 2734, 2734, 2734, 2734, 4130, 3615, - 3606, 2734, 2734, 2734, 2734, 2734, 2734, 2790, -1000, -1000, - -1000, 2789, 2734, 2734, 2734, 5417, 7406, 3133, -1000, 5130, - 1382, -1000, -1000, -1000, 5417, -1000, -1000, -1000, -1000, -1000, - -1000, -1000, -1000, 3605, 3602, -1000, -1000, -1000, -1000, -1000, - 3320, 4129, 325, -1000, 4647, 4615, 16341, -1000, -1000, 4127, - 2504, 2502, 2481, -1000, -1000, -1000, -1000, -1000, -1000, -1000, - -1000, -1000, -1000, -1000, -1000, -1000, 2463, 2438, 45851, -1000, - -1000, 36036, 2734, 2201, 74543, -1000, -328, -328, 4500, -1000, - 74543, 3162, 1988, 363, 4126, 4125, -1000, -1000, -1000, 4124, - -1000, -1000, 4727, -131, 4752, -157, 3599, 419, 470, -1000, - 3597, -1000, -20, 4499, 4498, -30, -1000, -1000, -1000, -1000, - -1000, 1745, -1000, -1000, -1000, 1877, 393, 393, -1000, -1000, - -1000, -1000, 3595, -1000, -1000, -1000, -1000, -1000, 74543, 3593, - -1000, -1000, 160, -1000, 2761, -1000, 74543, 848, -1000, -265, - -1000, -265, 3313, 3590, -1000, 74543, 1058, -1000, -1000, -1000, - -1000, 484, -1000, -1000, -1000, -1000, -1000, -1000, -1000, 3588, - 3571, -1000, -1000, 992, 4726, -1000, 2225, 6379, -1000, 2734, - 814, -1000, 434, 992, -1000, 913, -1000, 2578, -1000, 3309, - -1000, 2432, 4710, -1000, 1869, 2948, 16341, 2974, 2974, -1000, - -1000, -1000, 912, -1000, 997, -1000, -1000, -1000, 2427, -1000, - -1000, 3304, 3303, -1000, -1000, 74543, 3295, 3288, 3285, 3283, - 3282, 3253, 74543, -1000, -1000, 3249, 3234, 3227, 3224, 3219, - 3185, 3081, 3077, 3181, 3151, 3129, -1000, -1000, -1000, 5130, - 1003, -1000, -1000, 1218, 4123, -1000, -1000, 3943, 16341, 4122, - 2734, 4121, 3245, -11, 45851, 4400, -1000, 4398, 3876, 3634, - 2403, -1000, 36036, 74543, -1000, -1000, -1000, -1000, 1863, -1000, - 4605, -1000, -304, 4120, 16341, 16341, 74543, 3532, -148, -1000, - -1000, 3568, -1000, 1300, 433, 470, -1000, 4725, 414, 4724, - 4720, 1856, 4496, -1000, -1000, -1000, -1000, 1525, -125, 390, - 386, 373, 346, -1000, -1000, -1000, -1000, -1000, -1000, 3016, - 3016, -265, 3559, 3556, -1000, 74543, -1000, -1000, 3552, -265, - 894, -1000, 592, -1000, -1000, -1000, 7406, -1000, 4718, 1078, - -1000, -1000, 36036, -1000, 4807, 4805, 4804, 4797, -1000, -1000, - -1000, -1000, -1000, 3128, -1000, -1000, -328, 4761, 3162, 2924, - 2841, -1000, -1000, 3096, -1000, -1000, -1000, -1000, -1000, -1000, - -1000, -1000, -1000, -1000, 2388, -1000, -1000, -1000, -1000, -1000, + -1000, -1000, -1000, -1000, -1000, 4830, -1000, -1000, -1000, -1000, + 3531, 4024, 1273, -1000, -1000, -1000, -377, 16384, -1000, -377, + 16384, -1000, -1000, 4786, 2609, 4019, 4017, 2606, 4016, 4015, + 4591, 4604, 1977, 4014, 2101, -1000, -1000, -1000, -1000, -1000, + -1000, 4010, 7167, 1680, 3778, 1680, -1000, -1000, -1000, -1000, + 74894, -1000, -1000, -1000, 74894, 4785, 38460, 1393, -400, 952, + 4176, -1000, 957, 2940, 1798, 4174, 3770, -1000, 74894, 4779, + 74894, 3370, 1047, 3370, 1145, 74894, -296, 51363, 49845, -29, + 3438, 10275, -1000, 3769, -1000, -57, 1888, 652, 1574, 24783, + 4589, 1797, 1797, -1000, 3766, 580, -1000, -1000, -1000, 885, + -1000, 3434, -1000, -1000, 3308, 2407, 597, -1000, -1000, -1000, + -1000, -1000, -1000, 3447, 74894, 54399, 3447, 3550, 2877, -401, + -1000, 4173, -1000, 2796, 2796, 2796, 1393, 917, 74894, 4003, + 2077, -1000, -1000, -1000, -1000, -1000, 1797, -1000, 4558, 74894, + 2140, 3995, -300, 3994, 1222, 2860, 2841, -1000, 3427, 2603, + 4529, 1638, -1000, 3993, 2048, -168, -1000, -1000, 1995, 2410, + -1000, -1000, -1000, 2875, 3121, -1000, -1000, 53640, 30114, 52881, + 2141, -1000, 2409, -1000, 3150, 3121, 74894, -1000, 2379, -1000, + -1000, -1000, 52122, -1000, 4752, 4750, 51363, 2575, 50604, 49845, + 2573, 49086, 1222, -1000, 1013, 3425, 20199, 4171, 20199, 4170, + 1028, 4169, -1000, -1000, -1000, -1000, -1000, 1226, 4585, -1000, + 848, -1000, -1000, -1000, -1000, 1095, 1082, -1000, 2572, -1000, + 74894, -1000, -1000, 74894, 7115, 4168, -1000, 4167, 4165, 4406, + 999, 4164, 4163, 74894, 3526, -1000, 4566, 74894, 74894, -1000, + 74894, -1000, 2454, -1000, 36183, -1000, -1000, 2571, -1000, 3765, + 3764, -1000, -1000, 3987, 3121, -1000, 2652, 734, 1443, 74894, + -1000, 580, 3759, 11038, -1000, -1000, -1000, -1000, -1000, 4554, + 3754, 3447, 74894, -1000, 74894, 2035, 2035, 4830, 30114, 74894, + 14090, -360, -1000, -1000, 4569, 2646, -1000, -1000, -1000, 74894, + 4762, 4749, -1000, 1331, 20971, -234, 740, -1000, -1000, -1000, + -179, 3753, -1000, -1000, 4747, 3750, 3581, -1000, 286, 3749, + -1000, -168, -1000, 1995, 3746, -1000, -1000, -1000, 4770, -1000, + -1000, 74894, -1000, 2040, -1000, -1000, 1835, 1152, -1000, 3985, + 2849, -1000, -1000, -1000, 3514, -1000, -1000, 3510, 3491, -366, + -1000, 4830, 431, -1000, 3980, 431, -1000, 4830, -1000, 4746, + 431, 3479, 431, 3474, -377, 4573, -1000, 3745, -1000, -1000, + -1000, 3744, -1000, -1000, -1000, 4591, -1000, 16384, -1000, 3976, + -1000, 3741, -1000, -1000, 3734, 3733, -292, -1000, -1000, 770, + 1393, -1000, 545, 74894, 972, -1000, 48327, 10275, -402, 916, + 74894, 4772, 3732, 3370, 3724, 3370, 74894, 1046, -1000, 2559, + 2558, 4641, 3721, -1000, 3974, -1000, 3716, 3715, -1000, -1000, + 652, -1000, 2740, 4826, 4826, -1000, -1000, 4739, -1000, 2351, + 781, -1000, -1000, 3217, 1060, -1000, -1000, 3703, 1053, -1000, + 2035, -1000, -1000, 2871, 3153, 3621, 46050, 36183, 36942, 3700, + -1000, 74894, -1000, 6431, 24024, -1000, 1393, 4698, 914, 4715, + 736, 4825, 6632, 1846, 2791, -1000, 3419, -1000, 3415, -1000, + -1000, 4162, 74894, -1000, -1000, 1995, 74894, 16384, 4770, 2141, + 199, -360, -1000, 2598, 284, -1000, 37701, 2554, -1000, 16384, + 2519, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, + -1000, -1000, -1000, 1846, 4130, 4745, -1000, 5210, 74894, 4708, + 74894, 4161, 2869, 20199, 7535, 7535, 7535, 7535, 7535, 20199, + -1000, -1000, -1000, 2865, -1000, -1000, -1000, 1244, 4528, -1000, + -1000, 7115, -1000, -1000, 3119, 3100, 20199, -1000, -1000, 3698, + 3692, 3691, 36942, 1516, 2864, 2853, 1546, 4160, -1000, 1099, + 4808, 3400, -1000, -1000, -1000, 1679, -199, 4159, 3065, 3024, + -1000, 74894, -1000, 46050, 46050, 46050, 1234, 1234, 46050, 46050, + 4157, 1304, -1000, -1000, -1000, -1000, 2796, 2443, -1000, -1000, + -1000, -1000, -1000, 74894, 2403, -1000, -1000, -1000, 3550, -1000, + -1000, 2016, -1000, 4717, 2141, -1000, -1000, -1000, -1000, -1000, + 4521, -1000, -373, 16384, 17147, -1000, -383, 74894, 74894, -181, + 3399, -1000, 3685, 496, -1000, -1000, 476, -1000, 1995, -1000, + -1000, 2658, -183, 365, 36183, 2848, -1000, 3967, 514, -68, + -1000, -1000, -1000, 431, 3940, -1000, 431, -1000, 599, 16384, + -1000, -377, -1000, -377, 431, -1000, 3892, 3884, -1000, 3467, + -1000, -1000, -1000, -1000, 74894, 74894, -1000, 74894, 3679, 3395, + -1000, -1000, 2512, -1000, -1000, -1000, 3046, 3042, 2480, 3785, + 3597, 74894, 913, 74894, -296, 3678, -296, 3676, 1041, 3370, + -1000, -1000, -254, -1000, -1000, -1000, -1000, -60, -1000, -1000, + -1000, 719, -1000, 1126, 3578, 3380, -1000, -1000, 775, -1000, + -1000, -1000, 3447, 3675, -1000, -1000, 170, -1000, 2835, 2456, + -1000, -1000, -1000, 885, 1209, -1000, 1797, 4640, 6556, -1000, + 2164, -1000, -1000, -1000, 74894, -1000, -1000, -1000, -1000, -1000, + -1000, -1000, 1835, 2796, -1000, 1209, 1136, 2963, -1000, 3377, + -1000, -1000, 16384, 1916, -1000, 3121, 4830, -360, -1000, 13327, + -1000, 3032, 74894, 74894, -1000, -1000, -1000, 47568, 1132, -1000, + 1025, -1000, 2452, -1000, 2450, 46809, 3376, 3552, -1000, 2796, + 2796, 3673, 3672, 875, -1000, -1000, 2796, 2796, 2796, 2796, + 2796, 2796, 4156, 3671, 3661, 2796, 2796, 2796, 2796, 2796, + 2796, 2822, -1000, -1000, -1000, 2820, 2796, 2796, 2796, 6047, + 7535, 3141, -1000, 5584, 1424, -1000, -1000, -1000, 6047, -1000, + -1000, -1000, -1000, -1000, -1000, -1000, -1000, 3655, 3645, -1000, + -1000, -1000, -1000, -1000, 3371, 4155, 193, -1000, 4683, 4637, + 16384, -1000, -1000, 4154, 2448, 2441, 2437, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, - 2381, -1000, 74543, 1433, 12541, -1000, 3236, -1000, 36036, 16341, - -165, 4414, -1000, 554, 2316, 7406, 1809, 7406, 7406, 1809, - 7406, 1809, 7406, 1809, 589, -1000, -1000, 74543, 4119, -1000, - 4118, 2294, 2270, 2197, -1000, -1000, -150, -1000, 4117, 2991, - 435, 397, 4717, -1000, 3532, 4702, 3532, 3532, -1000, 356, - 4795, 2970, -1000, 396, 381, 377, 365, -1000, -1000, -1000, - -1000, -1000, -1000, 2810, -1000, 2810, -1000, -1000, -1000, -1000, - -265, -1000, 3537, -1000, -1000, -1000, 45096, 1007, -1000, -1000, - -1000, 4116, -1000, 4115, -1000, 4109, -1000, -1000, 4643, -1000, - 4792, 4764, -1000, -1000, -1000, 1003, 6379, -1000, 12541, 2193, - -1000, 3162, -1000, 1384, -1000, 3132, -1000, -1000, -1000, -1000, - 4411, 4410, 4757, -1000, -1000, -1000, -1000, -1000, -1000, -1000, - -1000, -1000, -1000, 81335, 2157, 16341, -1000, -1000, -1000, 74543, - -1000, 4641, 35281, 417, -1000, -1000, -1000, 3535, -1000, 3532, - -1000, -1000, 2679, -26, 1525, -1000, -1000, -1000, -1000, -1000, - -1000, -1000, -208, -1000, 74543, 1000, 43586, 43586, 43586, 74543, - 3987, 3982, -1000, 6379, 2154, -1000, 12541, -1000, -165, -1000, - 4785, -1000, 4782, 1516, 1516, 7406, 7406, 7406, 7406, 3940, - -1000, 4108, 3878, -1000, -1000, 74543, -1000, 2151, -1000, -1000, - -1000, -1000, -1000, -1000, -1000, 3530, -31, -1000, -1000, -1000, - 3519, 2133, 4107, 2080, -1000, 2674, -1000, -1000, 2069, -1000, - 2624, 2056, -1000, 2614, 1831, 74543, 4026, 74543, 3988, -1000, - -1000, -1000, -1000, -1000, -1000, 3177, 1076, -1000, -1000, 16341, - -1000, 1805, -1000, 2609, -1000, 44341, -1000, -1000, -1000, -1000, - -1000, -1000, 43586, 42831, -1000, 43586, 42831, -1000, 43586, 42831, - 2043, 1821, 16341, 1981, 16341, -1000, -1000, -1000, 3534, 74543, - 11782, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, - 4509, 74543, 74543, 1969, 4508, 1954, -1000, -1000, -1000, 3162, - 3790, 1810, -1000, -1000, 3591, -1000, 16341, 74543, 16341, 1913, - -1000, 1874, -1000, -1000, + 2431, 2406, 46050, -1000, -1000, 36183, 2796, 2396, 74894, -1000, + -360, -360, 4516, -1000, 74894, 3121, 2026, 336, 4152, 4150, + -1000, -1000, -1000, 4149, -1000, -1000, 4744, -175, 4770, -187, + 3639, 444, 549, -1000, 3635, -1000, -63, 4510, 4506, -71, + -1000, -1000, -1000, -1000, -1000, 2047, -1000, -1000, -1000, 1977, + 431, 431, -1000, -1000, -1000, -1000, 3631, -1000, -1000, -1000, + -1000, -1000, 74894, 3630, -1000, -1000, 166, -1000, 2809, -1000, + 74894, 909, -1000, -296, -1000, -296, 3370, 3624, -1000, 74894, + 1092, -1000, -1000, -1000, -1000, 556, -1000, -1000, -1000, -1000, + -1000, -1000, -1000, 3621, 3616, -1000, -1000, 1012, 4743, -1000, + 1797, 6632, -1000, 2796, 885, -1000, 452, 1012, -1000, 945, + -1000, 2669, -1000, 3365, -1000, 2399, 4717, -1000, 1971, 2999, + 16384, 3032, 3032, -1000, -1000, -1000, 941, -1000, 1016, -1000, + -1000, -1000, 2378, -1000, -1000, 3362, 3359, -1000, -1000, 74894, + 3356, 3354, 3323, 3313, 3304, 3302, 74894, -1000, -1000, 3299, + 3286, 3281, 3278, 3272, 3263, 3116, 3105, 3229, 3223, 3166, + -1000, -1000, -1000, 5584, 1020, -1000, -1000, 1192, 4148, -1000, + -1000, 3773, 16384, 4147, 2796, 4146, 3448, -48, 46050, 4403, + -1000, 4401, 4400, 4396, 2365, -1000, 36183, 74894, -1000, -1000, + -1000, -1000, 1959, -1000, 4636, -1000, -334, 4145, 16384, 16384, + 74894, 3581, -179, -1000, -1000, 3615, -1000, 1337, 500, 549, + -1000, 4742, 491, 4741, 4738, 1927, 4505, -1000, -1000, -1000, + -1000, 1308, -169, 428, 426, 422, 376, -1000, -1000, -1000, + -1000, -1000, -1000, 3055, 3055, -296, 3597, 3592, -1000, 74894, + -1000, -1000, 3589, -296, 974, -1000, 695, -1000, -1000, -1000, + 7535, -1000, 4737, 1113, -1000, -1000, 36183, -1000, 4823, 4817, + 4816, 4815, -1000, -1000, -1000, -1000, -1000, 3152, -1000, -1000, + -360, 4783, 3121, 2997, 2986, -1000, -1000, 3149, -1000, -1000, + -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, 2304, -1000, + -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, + -1000, -1000, -1000, -1000, 2298, -1000, 74894, 1418, 12564, -1000, + 3416, -1000, 36183, 16384, -197, 4417, -1000, 608, 2296, 7535, + 1234, 7535, 7535, 1234, 7535, 1234, 7535, 1234, 689, -1000, + -1000, 74894, 4144, -1000, 4143, 2271, 2263, 2233, -1000, -1000, + -181, -1000, 4142, 3143, 506, 433, 4736, -1000, 3581, 4734, + 3581, 3581, -1000, 445, 4788, 3030, -1000, 479, 440, 435, + 432, -1000, -1000, -1000, -1000, -1000, -1000, 2940, -1000, 2940, + -1000, -1000, -1000, -1000, -296, -1000, 3588, -1000, -1000, -1000, + 45291, 1022, -1000, -1000, -1000, 4136, -1000, 4135, -1000, 4133, + -1000, -1000, 4671, -1000, 4812, 4811, -1000, -1000, -1000, 1020, + 6632, -1000, 12564, 2225, -1000, 3121, -1000, 1304, -1000, 3160, + -1000, -1000, -1000, -1000, 4416, 4415, 4776, -1000, -1000, -1000, + -1000, -1000, -1000, -1000, -1000, -1000, -1000, 81722, 2223, 16384, + -1000, -1000, -1000, 74894, -1000, 4659, 35424, 490, -1000, -1000, + -1000, 3585, -1000, 3581, -1000, -1000, 2768, -69, 1308, -1000, + -1000, -1000, -1000, -1000, -1000, -1000, -261, -1000, 74894, 1013, + 43773, 43773, 43773, 74894, 4008, 4001, -1000, 6632, 2205, -1000, + 12564, -1000, -197, -1000, 4807, -1000, 4777, 1512, 1512, 7535, + 7535, 7535, 7535, 3757, -1000, 4131, 3723, -1000, -1000, 74894, + -1000, 2193, -1000, -1000, -1000, -1000, -1000, -1000, -1000, 3566, + -74, -1000, 1115, -1000, 3558, 2191, 4130, 2186, -1000, 2741, + -1000, -1000, 2160, -1000, 2706, 2123, -1000, 2693, 1916, 74894, + 4129, 74894, 4120, -1000, -1000, -1000, -1000, -1000, -1000, 3246, + 1111, -1000, -1000, 16384, -1000, 1876, -1000, 2677, -1000, 44532, + -1000, -1000, -1000, 655, 4504, 4411, 3098, -1000, -1000, -1000, + -1000, 43773, 43014, -1000, 43773, 43014, -1000, 43773, 43014, 2116, + 2393, 16384, 2111, 16384, -1000, -1000, -1000, 3647, 74894, 11801, + -1000, 854, -1000, -1000, 655, 3084, 4069, -1000, -1000, -1000, + -1000, -1000, -1000, -1000, -1000, 4526, 74894, 74894, 2038, 4525, + 2033, -1000, -1000, -1000, 3121, -1000, -1000, 664, 3082, 4009, + 2029, -1000, -1000, 3849, -1000, -1000, 658, 16384, 74894, 16384, + -1000, 2031, -1000, 1964, -1000, -1000, } var yyPgo = [...]int{ - 0, 227, 93, 319, 235, 5649, 145, 329, 551, 343, - 527, 5648, 326, 324, 5647, 5641, 5640, 342, 340, 5637, - 5635, 5634, 5633, 5632, 5631, 5629, 5628, 5626, 5625, 5624, - 5623, 5622, 5621, 5620, 5619, 5618, 5617, 5616, 5615, 5614, - 5613, 5611, 5610, 5609, 5607, 5606, 5605, 5604, 5603, 5602, - 5601, 5600, 5598, 5597, 5596, 321, 5595, 5594, 5593, 5592, - 5591, 5589, 5588, 5587, 5586, 5585, 5584, 5583, 5582, 5581, - 5580, 5579, 5578, 5577, 5576, 5560, 5557, 5556, 5555, 5553, - 5552, 5551, 5550, 5548, 5547, 5531, 5530, 5529, 5528, 5527, - 5526, 5525, 5524, 5523, 5522, 468, 5521, 338, 5519, 5518, - 5517, 5516, 5514, 5511, 4543, 5510, 96, 53, 70, 5509, - 5493, 5492, 5488, 5487, 5486, 5485, 5484, 318, 244, 5477, - 5476, 5475, 5465, 5464, 5460, 5459, 5448, 5436, 5435, 5432, - 5428, 5427, 288, 5425, 5424, 316, 5423, 5422, 5420, 5419, - 5418, 276, 5417, 377, 5416, 311, 225, 5413, 5412, 5411, - 5409, 5408, 5407, 5406, 141, 156, 5405, 5404, 5401, 5397, - 5389, 5382, 5379, 5378, 5377, 5375, 5374, 5373, 177, 5372, - 298, 252, 116, 5370, 77, 5369, 301, 260, 5368, 279, - 5367, 191, 5366, 189, 5365, 5364, 5363, 5362, 5360, 5359, - 5358, 5357, 5356, 5355, 5354, 5353, 5352, 5350, 5348, 5347, - 5346, 5345, 5344, 5343, 5342, 5338, 5336, 5335, 5333, 5330, - 5329, 5326, 5325, 5324, 78, 5323, 332, 5322, 112, 5321, - 104, 103, 199, 5320, 136, 5319, 231, 5318, 107, 5314, - 310, 128, 5310, 38, 47, 5309, 62, 60, 153, 5308, - 5307, 325, 4421, 302, 5306, 215, 5305, 283, 5304, 5303, - 5300, 307, 72, 44, 243, 5299, 5298, 322, 221, 5285, - 5282, 5281, 339, 5279, 287, 213, 222, 122, 169, 5278, - 196, 5277, 111, 121, 130, 55, 67, 99, 132, 198, - 172, 5275, 5273, 123, 251, 170, 246, 138, 5272, 5271, - 5270, 7, 224, 295, 5268, 219, 220, 114, 5266, 5264, - 270, 5263, 5262, 5261, 281, 195, 5259, 143, 5257, 5255, - 5254, 32, 5253, 5250, 266, 253, 5249, 5247, 140, 5246, - 5245, 98, 242, 176, 5244, 101, 168, 209, 166, 5239, - 4563, 163, 125, 5235, 160, 150, 5234, 218, 5233, 5232, - 5231, 5229, 237, 5228, 5225, 182, 5224, 85, 5219, 5218, - 5217, 100, 5214, 110, 105, 148, 142, 5211, 42, 146, - 137, 5210, 5209, 5207, 5202, 5201, 14, 5, 6, 5196, - 5194, 5193, 5192, 3, 5191, 5189, 9, 5188, 41, 5187, - 83, 5186, 5181, 5180, 5179, 5176, 5175, 5166, 5165, 5148, - 5146, 5145, 5139, 49, 5127, 5126, 5122, 5121, 11, 19, - 27, 5119, 40, 5115, 210, 5111, 5109, 207, 5107, 5106, - 245, 5103, 5102, 131, 127, 5101, 134, 5100, 205, 5098, - 71, 5094, 5093, 81, 5091, 5089, 5087, 312, 5086, 5085, - 5084, 254, 5083, 5082, 5081, 5079, 587, 5078, 5077, 5076, - 5075, 5074, 5073, 444, 299, 1, 278, 37, 5072, 179, - 187, 5071, 57, 43, 5070, 75, 152, 273, 183, 147, - 5069, 5053, 5050, 806, 162, 139, 59, 0, 144, 280, - 208, 306, 5048, 5047, 330, 5046, 317, 259, 314, 102, - 335, 241, 5045, 5044, 80, 5042, 197, 108, 66, 193, - 292, 23, 685, 5040, 2842, 16, 269, 5039, 272, 5038, - 46, 29, 22, 181, 5036, 5032, 51, 328, 5031, 5028, - 5027, 178, 5026, 5025, 230, 106, 5024, 5023, 5022, 5021, - 5020, 65, 5019, 232, 24, 5015, 175, 5014, 313, 119, - 261, 188, 238, 229, 203, 290, 289, 115, 15, 223, - 5011, 1918, 202, 165, 28, 5010, 13, 286, 274, 206, - 158, 5007, 117, 5006, 309, 334, 264, 5005, 239, 18, - 73, 54, 45, 69, 20, 84, 97, 26, 36, 5002, - 5001, 4998, 4992, 4991, 21, 63, 4990, 82, 4989, 31, - 4987, 4979, 58, 61, 4978, 90, 8, 4977, 4975, 30, - 17, 33, 4974, 52, 297, 216, 171, 135, 91, 4973, - 4972, 212, 522, 4971, 180, 194, 185, 4970, 68, 4969, - 4968, 4967, 4966, 4111, 1766, 4965, 192, 4962, 4960, 4959, - 4956, 4955, 4954, 256, 336, 186, 233, 304, 308, 333, - 4953, 109, 64, 4951, 4950, 4949, 4948, 124, 173, 4947, - 4946, 4945, 4939, 48, 113, 4938, 25, 4936, 39, 34, - 50, 4935, 79, 4934, 4933, 4932, 4, 240, 4931, 4930, - 4929, 12, 4928, 4927, 2, 4926, 4925, 161, 4922, 133, - 35, 214, 211, 4921, 4919, 126, 249, 174, 4918, 4916, - 157, 315, 4914, 257, 4913, 293, 296, 327, 4910, 277, - 4907, 4906, 4905, 4904, 4903, 1736, 4900, 4896, 291, 95, - 129, 4895, 285, 159, 4894, 4893, 120, 204, 164, 151, - 76, 118, 4892, 154, 258, 4891, 248, 4890, 282, 4889, - 4888, 4886, 4882, 217, 4881, 4880, 4879, 4878, 250, 4877, - 4875, 255, 284, 4874, 4873, 323, 320, 4872, 4855, 4854, - 4853, 4852, 4851, 4850, 4849, 4847, 4843, 331, 271, 4840, - 4831, 88, 4830, 4820, -} - -//line mysql_sql.y:16591 + 0, 225, 100, 313, 232, 5577, 129, 355, 293, 351, + 283, 5575, 352, 349, 5574, 5573, 5570, 350, 346, 5569, + 5568, 5564, 5559, 5557, 5556, 5555, 5554, 5553, 5551, 5550, + 5549, 5547, 5545, 5544, 5543, 5542, 5541, 5540, 5538, 5537, + 5536, 5535, 5534, 5532, 5531, 5530, 5529, 5525, 5523, 5522, + 5521, 5520, 5518, 5514, 5513, 337, 5512, 5511, 5510, 5509, + 5506, 5505, 5504, 5503, 5502, 5501, 5500, 5499, 5497, 5495, + 5494, 5493, 5492, 5491, 5490, 5486, 5484, 5483, 5478, 5476, + 5475, 5474, 5467, 5466, 5458, 5457, 5456, 5455, 5454, 5451, + 5450, 5449, 5448, 5447, 5446, 277, 5445, 342, 5444, 5443, + 5439, 5437, 5435, 5431, 4547, 5425, 93, 54, 70, 5424, + 5421, 5420, 5417, 5415, 5414, 5413, 5412, 333, 288, 5411, + 5410, 5409, 5408, 5407, 5405, 5404, 5403, 5402, 5401, 5400, + 5396, 5394, 244, 5393, 5392, 329, 5391, 5390, 5389, 5388, + 5387, 301, 5386, 249, 5385, 326, 230, 5384, 5380, 5378, + 5377, 5375, 5371, 5369, 144, 160, 5354, 5353, 5352, 5351, + 5350, 5348, 5347, 5346, 5345, 5344, 5343, 5342, 188, 5341, + 303, 156, 110, 5340, 72, 5339, 299, 266, 5338, 279, + 5336, 196, 5334, 197, 5333, 5332, 5330, 5329, 5325, 5323, + 5322, 5320, 5319, 5318, 5317, 5315, 5311, 5310, 5309, 5308, + 5294, 5290, 5289, 5288, 5286, 5285, 5284, 5283, 5282, 5278, + 5277, 5276, 5275, 5273, 76, 5272, 324, 5271, 108, 5270, + 127, 125, 202, 5269, 139, 5268, 236, 5267, 109, 5266, + 320, 104, 5265, 38, 48, 5263, 219, 59, 118, 5258, + 5257, 325, 4429, 321, 5256, 199, 5251, 316, 5250, 5249, + 5248, 285, 81, 45, 251, 5247, 5246, 315, 223, 5245, + 5244, 5239, 317, 5238, 282, 217, 218, 132, 163, 5237, + 203, 5236, 113, 121, 128, 52, 67, 94, 135, 198, + 175, 5235, 5234, 111, 240, 169, 248, 141, 5231, 5230, + 5227, 12, 227, 305, 5225, 221, 226, 112, 5224, 5222, + 269, 5220, 5218, 5217, 280, 189, 5216, 143, 5215, 5212, + 5211, 34, 5209, 5206, 262, 2, 1, 5205, 253, 5200, + 5198, 148, 5197, 5196, 95, 245, 179, 5194, 103, 166, + 210, 165, 5192, 3737, 168, 117, 5191, 161, 147, 5190, + 178, 5189, 5188, 5187, 5186, 238, 5182, 5181, 194, 5180, + 83, 5179, 5176, 5171, 97, 5170, 102, 105, 149, 142, + 5169, 41, 146, 134, 5167, 5166, 5165, 5164, 5163, 15, + 5, 6, 5161, 5160, 5159, 5158, 8, 5154, 5153, 9, + 5150, 46, 5147, 85, 5143, 5140, 5131, 5130, 5126, 5120, + 5119, 5116, 5114, 5113, 5111, 5109, 49, 5107, 5104, 5102, + 5101, 13, 29, 28, 5098, 36, 5096, 215, 5093, 5091, + 208, 5090, 5089, 254, 5088, 5087, 131, 126, 5086, 133, + 5085, 229, 5083, 61, 5082, 5081, 1327, 5080, 5079, 5078, + 328, 5077, 5076, 5075, 255, 5074, 5073, 5071, 5070, 559, + 5069, 5068, 5067, 5066, 5065, 5064, 444, 311, 3, 270, + 44, 5063, 182, 183, 5062, 58, 43, 5061, 75, 172, + 278, 180, 151, 5059, 5058, 5057, 792, 162, 140, 98, + 0, 145, 281, 213, 319, 5055, 5054, 327, 5052, 314, + 296, 318, 80, 339, 257, 5051, 5050, 82, 5049, 206, + 107, 66, 193, 292, 24, 345, 5048, 2842, 17, 274, + 5046, 276, 5044, 23, 32, 22, 177, 5042, 5039, 50, + 338, 5038, 5036, 5035, 176, 5034, 5032, 373, 106, 5031, + 5030, 5029, 5028, 5027, 65, 5026, 237, 78, 5024, 181, + 5021, 307, 138, 239, 192, 242, 241, 209, 284, 289, + 115, 19, 224, 5019, 2013, 204, 150, 30, 5017, 16, + 286, 273, 332, 158, 5016, 116, 5015, 312, 335, 264, + 5014, 243, 20, 71, 57, 47, 69, 21, 84, 99, + 27, 40, 5011, 5009, 5008, 5007, 5006, 25, 60, 5005, + 73, 4996, 35, 4995, 4994, 62, 63, 4991, 91, 14, + 4990, 4989, 31, 18, 33, 4988, 51, 294, 216, 171, + 137, 90, 4987, 4985, 212, 754, 4983, 222, 191, 187, + 4982, 64, 4981, 4979, 4978, 4977, 4149, 1873, 4976, 205, + 4975, 4974, 4971, 4970, 4969, 4967, 259, 340, 174, 235, + 310, 298, 334, 4966, 122, 68, 4965, 4960, 4959, 4958, + 120, 173, 4957, 4956, 4954, 4953, 53, 114, 4952, 26, + 4951, 39, 37, 55, 4950, 77, 4949, 4948, 4944, 7, + 246, 4943, 4942, 4940, 11, 4939, 4938, 4, 4937, 4936, + 164, 4935, 136, 42, 211, 214, 4934, 4933, 124, 250, + 170, 4932, 4931, 152, 304, 4930, 258, 4929, 185, 291, + 330, 4926, 271, 4925, 4923, 4921, 4920, 4918, 1897, 4915, + 4913, 297, 96, 130, 4912, 295, 154, 4911, 4910, 123, + 207, 157, 159, 79, 119, 4906, 153, 261, 4904, 260, + 4902, 287, 4901, 4899, 4898, 4897, 220, 4896, 4895, 4894, + 4893, 252, 4889, 4888, 256, 302, 4886, 4885, 323, 309, + 4884, 4883, 4882, 4881, 4878, 4877, 4876, 4875, 4874, 4873, + 322, 290, 4872, 4871, 88, 4851, 4849, +} + +//line mysql_sql.y:16643 type yySymType struct { union interface{} id int @@ -11859,6 +11909,11 @@ func (st *yySymType) propertyUnion() tree.Property { return v } +func (st *yySymType) pythonFunctionOptionsUnion() tree.PythonFunctionOptions { + v, _ := st.union.(tree.PythonFunctionOptions) + return v +} + func (st *yySymType) rankOptionUnion() *tree.RankOption { v, _ := st.union.(*tree.RankOption) return v @@ -12315,70 +12370,70 @@ func (st *yySymType) zeroFillOptUnion() bool { } var yyR1 = [...]int{ - 0, 742, 745, 5, 5, 2, 6, 6, 3, 3, + 0, 745, 748, 5, 5, 2, 6, 6, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 1, 1, 1, 1, 1, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 160, - 160, 458, 458, 459, 459, 162, 454, 454, 453, 453, - 163, 164, 165, 719, 719, 720, 720, 718, 718, 166, - 167, 205, 717, 717, 717, 717, 717, 717, 717, 717, - 207, 207, 207, 207, 207, 207, 207, 582, 161, 161, + 160, 461, 461, 462, 462, 162, 457, 457, 456, 456, + 163, 164, 165, 722, 722, 723, 723, 721, 721, 166, + 167, 205, 720, 720, 720, 720, 720, 720, 720, 720, + 207, 207, 207, 207, 207, 207, 207, 585, 161, 161, 161, 161, 288, 288, 289, 289, 176, 176, 177, 177, - 211, 211, 211, 211, 211, 211, 211, 151, 726, 726, - 726, 727, 727, 148, 188, 187, 190, 190, 189, 189, + 211, 211, 211, 211, 211, 211, 211, 151, 729, 729, + 729, 730, 730, 148, 188, 187, 190, 190, 189, 189, 186, 186, 182, 185, 185, 184, 184, 183, 178, 180, 180, 179, 181, 181, 149, 132, 134, 134, 133, 150, - 666, 666, 665, 665, 664, 664, 609, 609, 610, 610, - 445, 445, 445, 663, 663, 663, 662, 662, 661, 661, - 659, 659, 660, 660, 657, 657, 658, 656, 655, 655, - 655, 651, 651, 651, 647, 647, 649, 648, 648, 650, - 642, 642, 645, 645, 643, 643, 643, 643, 646, 641, - 641, 641, 640, 640, 131, 131, 131, 543, 543, 130, - 130, 558, 558, 558, 558, 558, 556, 556, 556, 556, - 556, 556, 555, 555, 554, 554, 559, 559, 557, 557, - 557, 557, 557, 557, 557, 557, 557, 557, 557, 557, - 557, 557, 557, 557, 557, 557, 557, 557, 557, 557, - 557, 557, 557, 557, 557, 557, 557, 557, 557, 557, - 557, 557, 557, 557, 557, 557, 557, 557, 557, 557, - 557, 557, 557, 557, 557, 557, 557, 557, 557, 119, + 669, 669, 668, 668, 667, 667, 612, 612, 613, 613, + 448, 448, 448, 666, 666, 666, 665, 665, 664, 664, + 662, 662, 663, 663, 660, 660, 661, 659, 658, 658, + 658, 654, 654, 654, 650, 650, 652, 651, 651, 653, + 645, 645, 648, 648, 646, 646, 646, 646, 649, 644, + 644, 644, 643, 643, 131, 131, 131, 546, 546, 130, + 130, 561, 561, 561, 561, 561, 559, 559, 559, 559, + 559, 559, 558, 558, 557, 557, 562, 562, 560, 560, + 560, 560, 560, 560, 560, 560, 560, 560, 560, 560, + 560, 560, 560, 560, 560, 560, 560, 560, 560, 560, + 560, 560, 560, 560, 560, 560, 560, 560, 560, 560, + 560, 560, 560, 560, 560, 560, 560, 560, 560, 560, + 560, 560, 560, 560, 560, 560, 560, 560, 560, 119, 119, 119, 119, 119, 119, 119, 126, 124, 124, 124, - 125, 732, 732, 731, 731, 733, 733, 733, 733, 734, - 734, 122, 122, 122, 123, 553, 553, 553, 120, 121, - 121, 542, 542, 548, 548, 547, 547, 547, 547, 547, - 547, 547, 547, 547, 547, 547, 547, 547, 552, 552, - 552, 550, 550, 549, 549, 551, 551, 109, 109, 109, + 125, 735, 735, 734, 734, 736, 736, 736, 736, 737, + 737, 122, 122, 122, 123, 556, 556, 556, 120, 121, + 121, 545, 545, 551, 551, 550, 550, 550, 550, 550, + 550, 550, 550, 550, 550, 550, 550, 550, 555, 555, + 555, 553, 553, 552, 552, 554, 554, 109, 109, 109, 109, 109, 109, 113, 114, 115, 115, 115, 115, 112, - 111, 541, 541, 541, 541, 541, 541, 541, 541, 541, + 111, 544, 544, 544, 544, 544, 544, 544, 544, 544, 110, 110, 110, 110, 110, 110, 96, 96, 96, 96, - 96, 95, 95, 97, 97, 97, 538, 538, 537, 127, - 127, 128, 729, 729, 728, 730, 730, 730, 730, 129, + 96, 95, 95, 97, 97, 97, 541, 541, 540, 127, + 127, 128, 732, 732, 731, 733, 733, 733, 733, 129, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 138, 138, 138, 141, 141, 141, 140, 142, 118, 118, 118, 118, 118, 118, 116, 116, 116, 116, 116, 117, 117, 117, 117, 117, 117, 117, - 117, 117, 117, 117, 117, 692, 692, 692, 692, 692, - 693, 693, 472, 473, 746, 475, 471, 471, 471, 688, - 688, 689, 690, 691, 691, 691, 691, 135, 301, 301, + 117, 117, 117, 117, 117, 695, 695, 695, 695, 695, + 696, 696, 475, 476, 749, 478, 474, 474, 474, 691, + 691, 692, 693, 694, 694, 694, 694, 135, 301, 301, 299, 299, 300, 300, 136, 302, 302, 302, 137, 303, - 303, 16, 298, 298, 581, 581, 13, 13, 13, 13, + 303, 16, 298, 298, 584, 584, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 102, 102, 103, 103, 103, 103, 15, 94, 99, 99, - 99, 101, 409, 409, 410, 408, 408, 408, 408, 408, - 403, 403, 411, 411, 210, 52, 52, 52, 52, 52, - 52, 100, 100, 100, 100, 412, 412, 412, 418, 418, - 419, 419, 404, 404, 404, 404, 404, 404, 404, 404, - 404, 404, 404, 404, 404, 404, 404, 404, 404, 404, - 404, 404, 404, 404, 388, 388, 388, 383, 383, 383, - 383, 384, 384, 385, 385, 386, 386, 386, 386, 387, - 387, 464, 464, 413, 413, 413, 415, 415, 414, 407, - 405, 405, 405, 405, 405, 405, 405, 406, 406, 406, - 406, 416, 416, 417, 417, 92, 98, 98, 98, 98, - 705, 705, 93, 93, 93, 716, 716, 613, 613, 486, - 486, 485, 485, 485, 485, 485, 485, 485, 485, 485, - 485, 485, 485, 485, 485, 485, 485, 618, 619, 482, + 99, 101, 412, 412, 413, 411, 411, 411, 411, 411, + 406, 406, 414, 414, 210, 52, 52, 52, 52, 52, + 52, 100, 100, 100, 100, 415, 415, 415, 421, 421, + 422, 422, 407, 407, 407, 407, 407, 407, 407, 407, + 407, 407, 407, 407, 407, 407, 407, 407, 407, 407, + 407, 407, 407, 407, 391, 391, 391, 386, 386, 386, + 386, 387, 387, 388, 388, 389, 389, 389, 389, 390, + 390, 467, 467, 416, 416, 416, 418, 418, 417, 410, + 408, 408, 408, 408, 408, 408, 408, 409, 409, 409, + 409, 419, 419, 420, 420, 92, 98, 98, 98, 98, + 708, 708, 93, 93, 93, 719, 719, 616, 616, 489, + 489, 488, 488, 488, 488, 488, 488, 488, 488, 488, + 488, 488, 488, 488, 488, 488, 488, 621, 622, 485, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, @@ -12386,222 +12441,223 @@ var yyR1 = [...]int{ 55, 55, 55, 55, 53, 54, 157, 157, 159, 159, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 70, 222, 89, 90, 91, 68, 62, 65, 66, 209, - 212, 212, 212, 212, 61, 61, 61, 527, 527, 60, - 747, 747, 457, 457, 78, 77, 64, 79, 80, 81, + 212, 212, 212, 212, 61, 61, 61, 530, 530, 60, + 750, 750, 460, 460, 78, 77, 64, 79, 80, 81, 82, 83, 84, 59, 76, 76, 76, 76, 76, 76, - 76, 76, 87, 636, 636, 749, 749, 749, 85, 86, - 612, 612, 612, 75, 75, 74, 74, 73, 72, 71, + 76, 76, 87, 639, 639, 752, 752, 752, 85, 86, + 615, 615, 615, 75, 75, 74, 74, 73, 72, 71, 71, 58, 58, 57, 57, 63, 194, 196, 67, 195, - 195, 197, 197, 479, 479, 479, 481, 481, 477, 748, - 748, 577, 577, 480, 480, 56, 56, 56, 56, 88, - 478, 478, 456, 476, 476, 476, 14, 14, 12, 19, + 195, 197, 197, 482, 482, 482, 484, 484, 480, 751, + 751, 580, 580, 483, 483, 56, 56, 56, 56, 88, + 481, 481, 459, 479, 479, 479, 14, 14, 12, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 51, - 28, 29, 27, 30, 32, 535, 535, 532, 31, 22, + 28, 29, 27, 30, 32, 538, 538, 535, 31, 22, 21, 24, 20, 20, 23, 25, 26, 26, 10, 10, - 10, 10, 17, 17, 18, 258, 258, 331, 331, 699, - 699, 695, 695, 696, 696, 696, 697, 697, 698, 698, - 750, 750, 750, 143, 630, 630, 630, 630, 630, 630, - 8, 8, 8, 8, 624, 624, 624, 629, 629, 628, - 627, 627, 626, 626, 625, 625, 625, 625, 9, 9, + 10, 10, 17, 17, 18, 258, 258, 334, 334, 702, + 702, 698, 698, 699, 699, 699, 700, 700, 701, 701, + 753, 753, 753, 143, 633, 633, 633, 633, 633, 633, + 8, 8, 8, 8, 627, 627, 627, 632, 632, 631, + 630, 630, 629, 629, 628, 628, 628, 628, 9, 9, 9, 9, 253, 253, 11, 11, 104, 104, 105, 105, 106, 106, 106, 106, 106, 106, 108, 107, 107, 287, - 287, 623, 623, 623, 623, 623, 623, 540, 540, 539, - 539, 676, 676, 676, 677, 292, 292, 280, 280, 280, - 291, 291, 291, 291, 631, 631, 503, 678, 678, 639, - 639, 638, 638, 637, 637, 285, 285, 293, 293, 293, - 294, 294, 286, 286, 262, 262, 171, 171, 171, 653, - 653, 654, 654, 644, 644, 644, 644, 652, 652, 608, - 608, 393, 393, 447, 447, 448, 448, 240, 236, 236, + 287, 626, 626, 626, 626, 626, 626, 543, 543, 542, + 542, 679, 679, 679, 680, 292, 292, 280, 280, 280, + 291, 291, 291, 291, 634, 634, 506, 681, 681, 642, + 642, 641, 641, 640, 640, 285, 285, 293, 293, 293, + 294, 294, 286, 286, 262, 262, 171, 171, 171, 656, + 656, 657, 657, 647, 647, 647, 647, 655, 655, 611, + 611, 396, 396, 450, 450, 451, 451, 240, 236, 236, 236, 237, 237, 238, 238, 238, 238, 238, 238, 238, - 238, 238, 238, 238, 735, 735, 736, 737, 738, 738, - 739, 739, 740, 740, 740, 741, 741, 741, 741, 741, - 685, 685, 687, 687, 686, 284, 284, 275, 275, 277, + 238, 238, 238, 238, 738, 738, 739, 740, 741, 741, + 742, 742, 743, 743, 743, 744, 744, 744, 744, 744, + 688, 688, 690, 690, 689, 284, 284, 275, 275, 277, 277, 277, 276, 276, 276, 278, 279, 279, 273, 273, 274, 274, 272, 271, 271, 270, 268, 268, 268, 269, 269, 269, 297, 297, 297, 297, 242, 242, 242, 242, 241, 241, 241, 241, 241, 241, 241, 241, 241, 244, 245, 245, 247, 246, 246, 246, 246, 248, 249, 249, - 251, 250, 250, 250, 250, 427, 427, 427, 427, 427, - 427, 427, 427, 427, 427, 427, 427, 243, 255, 255, + 251, 250, 250, 250, 250, 430, 430, 430, 430, 430, + 430, 430, 430, 430, 430, 430, 430, 243, 255, 255, 256, 256, 257, 257, 257, 257, 257, 257, 257, 257, - 257, 257, 424, 424, 425, 425, 425, 425, 425, 169, - 169, 617, 617, 423, 423, 252, 252, 254, 254, 254, - 254, 422, 422, 421, 267, 267, 266, 266, 265, 265, - 265, 259, 259, 259, 259, 259, 259, 261, 433, 433, - 432, 432, 431, 431, 431, 431, 431, 431, 434, 435, - 435, 435, 260, 260, 147, 168, 168, 170, 282, 281, - 430, 429, 429, 429, 429, 296, 296, 295, 295, 283, - 283, 264, 264, 264, 264, 428, 263, 426, 725, 725, - 724, 724, 723, 721, 721, 721, 722, 722, 722, 722, - 668, 668, 668, 668, 668, 465, 465, 465, 470, 470, - 468, 468, 468, 468, 468, 474, 7, 7, 7, 7, + 257, 257, 427, 427, 428, 428, 428, 428, 428, 169, + 169, 620, 620, 426, 426, 252, 252, 254, 254, 254, + 254, 425, 425, 424, 267, 267, 266, 266, 265, 265, + 265, 259, 259, 259, 259, 259, 259, 261, 436, 436, + 435, 435, 434, 434, 434, 434, 434, 434, 437, 438, + 438, 438, 260, 260, 147, 168, 168, 170, 282, 281, + 433, 432, 432, 432, 432, 296, 296, 295, 295, 283, + 283, 264, 264, 264, 264, 431, 263, 429, 728, 728, + 727, 727, 726, 724, 724, 724, 725, 725, 725, 725, + 671, 671, 671, 671, 671, 468, 468, 468, 473, 473, + 471, 471, 471, 471, 471, 477, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 45, 46, 50, 152, 152, 155, 155, 153, 153, 154, 154, - 158, 158, 156, 156, 42, 313, 314, 43, 315, 315, - 316, 316, 317, 317, 318, 319, 320, 320, 320, 320, - 519, 519, 41, 304, 304, 305, 305, 306, 306, 307, - 308, 308, 308, 312, 309, 310, 310, 744, 744, 743, - 40, 40, 33, 33, 215, 215, 216, 216, 216, 218, - 218, 389, 389, 389, 217, 217, 219, 219, 219, 700, - 702, 702, 704, 703, 703, 703, 706, 706, 706, 706, - 706, 707, 707, 707, 707, 708, 708, 34, 191, 191, - 191, 228, 228, 201, 711, 711, 711, 710, 710, 583, - 583, 712, 712, 713, 713, 451, 451, 452, 452, 213, - 214, 214, 203, 193, 227, 227, 227, 227, 227, 229, - 229, 333, 333, 192, 198, 199, 200, 202, 204, 206, - 206, 208, 701, 709, 709, 709, 536, 536, 533, 534, - 534, 531, 530, 530, 530, 715, 715, 714, 714, 714, - 466, 466, 35, 526, 526, 528, 529, 529, 529, 529, - 529, 529, 529, 529, 520, 520, 520, 520, 520, 39, - 524, 524, 525, 525, 525, 525, 525, 525, 525, 525, - 525, 525, 525, 525, 525, 525, 525, 525, 525, 525, - 525, 525, 525, 525, 525, 525, 525, 525, 525, 525, - 525, 525, 525, 525, 521, 521, 523, 523, 518, 518, - 518, 518, 518, 518, 518, 518, 518, 518, 38, 38, - 38, 226, 226, 517, 517, 514, 514, 514, 311, 311, - 512, 512, 513, 513, 511, 511, 511, 515, 515, 47, - 48, 49, 516, 516, 230, 230, 230, 230, 230, 230, - 230, 230, 230, 230, 230, 290, 290, 235, 235, 235, - 235, 235, 235, 233, 233, 233, 233, 234, 234, 231, - 231, 232, 232, 220, 220, 220, 221, 221, 37, 37, + 158, 158, 156, 156, 42, 313, 314, 43, 318, 318, + 319, 319, 320, 320, 321, 322, 323, 323, 323, 323, + 522, 522, 41, 304, 304, 305, 305, 306, 306, 307, + 308, 308, 308, 312, 309, 310, 310, 747, 747, 746, + 317, 317, 317, 317, 317, 316, 316, 315, 315, 40, + 40, 33, 33, 215, 215, 216, 216, 216, 218, 218, + 392, 392, 392, 217, 217, 219, 219, 219, 703, 705, + 705, 707, 706, 706, 706, 709, 709, 709, 709, 709, + 710, 710, 710, 710, 711, 711, 34, 191, 191, 191, + 228, 228, 201, 714, 714, 714, 713, 713, 586, 586, + 715, 715, 716, 716, 454, 454, 455, 455, 213, 214, + 214, 203, 193, 227, 227, 227, 227, 227, 229, 229, + 336, 336, 192, 198, 199, 200, 202, 204, 206, 206, + 208, 704, 712, 712, 712, 539, 539, 536, 537, 537, + 534, 533, 533, 533, 718, 718, 717, 717, 717, 469, + 469, 35, 529, 529, 531, 532, 532, 532, 532, 532, + 532, 532, 532, 523, 523, 523, 523, 523, 39, 527, + 527, 528, 528, 528, 528, 528, 528, 528, 528, 528, + 528, 528, 528, 528, 528, 528, 528, 528, 528, 528, + 528, 528, 528, 528, 528, 528, 528, 528, 528, 528, + 528, 528, 528, 524, 524, 526, 526, 521, 521, 521, + 521, 521, 521, 521, 521, 521, 521, 38, 38, 38, + 226, 226, 520, 520, 517, 517, 517, 311, 311, 515, + 515, 516, 516, 514, 514, 514, 518, 518, 47, 48, + 49, 519, 519, 230, 230, 230, 230, 230, 230, 230, + 230, 230, 230, 230, 290, 290, 235, 235, 235, 235, + 235, 235, 233, 233, 233, 233, 234, 234, 231, 231, + 232, 232, 220, 220, 220, 221, 221, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, - 37, 37, 37, 37, 37, 37, 37, 173, 172, 172, - 172, 172, 172, 175, 175, 450, 450, 449, 449, 174, - 390, 390, 44, 344, 344, 600, 600, 595, 595, 595, - 595, 595, 615, 615, 615, 596, 596, 596, 597, 597, - 597, 599, 599, 599, 598, 598, 598, 598, 598, 614, - 614, 616, 616, 616, 560, 560, 561, 561, 561, 564, - 564, 586, 586, 587, 587, 585, 585, 593, 593, 592, - 592, 591, 591, 589, 589, 590, 588, 588, 588, 588, - 580, 580, 579, 579, 562, 562, 562, 562, 562, 563, - 563, 563, 578, 578, 584, 584, 353, 354, 354, 355, - 355, 356, 223, 223, 224, 224, 357, 358, 358, 359, - 359, 360, 361, 361, 362, 362, 363, 364, 364, 365, - 365, 366, 369, 370, 371, 371, 372, 372, 373, 377, - 377, 374, 374, 375, 375, 376, 367, 367, 368, 368, - 351, 351, 352, 352, 352, 420, 420, 420, 420, 420, - 420, 420, 420, 420, 420, 420, 420, 420, 420, 420, - 420, 420, 420, 420, 420, 420, 420, 420, 420, 420, - 420, 420, 420, 420, 420, 420, 420, 420, 420, 420, - 420, 674, 674, 675, 379, 379, 391, 391, 391, 391, - 391, 391, 378, 378, 380, 380, 332, 332, 330, 330, - 322, 322, 322, 322, 322, 322, 225, 225, 225, 321, - 321, 321, 321, 321, 321, 323, 323, 324, 324, 325, - 325, 325, 329, 329, 328, 328, 328, 328, 328, 326, - 326, 327, 327, 327, 327, 327, 327, 545, 545, 671, - 671, 672, 672, 667, 667, 667, 670, 670, 670, 670, - 670, 670, 670, 670, 670, 670, 670, 673, 673, 673, - 669, 669, 334, 444, 444, 444, 467, 467, 467, 467, - 469, 443, 443, 443, 350, 350, 349, 349, 347, 347, - 347, 347, 347, 347, 347, 347, 347, 347, 347, 347, - 347, 347, 347, 347, 347, 347, 347, 347, 347, 347, - 347, 347, 347, 347, 546, 546, 546, 544, 544, 544, - 483, 483, 484, 484, 402, 401, 401, 401, 401, 401, - 399, 400, 398, 398, 398, 398, 398, 395, 395, 394, - 394, 394, 396, 396, 396, 396, 396, 396, 522, 522, - 392, 392, 382, 382, 382, 381, 381, 381, 594, 490, - 490, 490, 490, 490, 490, 490, 490, 490, 490, 490, - 490, 490, 490, 490, 492, 492, 492, 492, 492, 492, - 492, 492, 492, 492, 492, 492, 492, 492, 492, 492, - 492, 492, 492, 492, 492, 492, 492, 492, 492, 492, - 492, 492, 492, 492, 397, 397, 397, 441, 441, 441, - 441, 441, 441, 441, 441, 441, 441, 441, 441, 441, - 441, 441, 442, 442, 442, 442, 442, 442, 442, 442, - 493, 493, 499, 499, 684, 684, 683, 335, 335, 335, - 336, 336, 336, 336, 336, 336, 336, 336, 336, 336, - 345, 345, 345, 574, 574, 574, 574, 574, 575, 575, - 575, 575, 575, 576, 576, 576, 572, 572, 573, 573, - 504, 505, 505, 621, 621, 622, 622, 565, 565, 566, - 566, 567, 571, 571, 569, 569, 570, 570, 568, 440, - 440, 440, 440, 440, 440, 440, 440, 440, 440, 440, + 37, 37, 37, 37, 37, 37, 173, 172, 172, 172, + 172, 172, 175, 175, 453, 453, 452, 452, 174, 393, + 393, 44, 347, 347, 603, 603, 598, 598, 598, 598, + 598, 618, 618, 618, 599, 599, 599, 600, 600, 600, + 602, 602, 602, 601, 601, 601, 601, 601, 617, 617, + 619, 619, 619, 563, 563, 564, 564, 564, 567, 567, + 589, 589, 590, 590, 588, 588, 596, 596, 595, 595, + 594, 594, 592, 592, 593, 591, 591, 591, 591, 583, + 583, 582, 582, 565, 565, 565, 565, 565, 566, 566, + 566, 581, 581, 587, 587, 356, 357, 357, 358, 358, + 359, 223, 223, 224, 224, 360, 361, 361, 362, 362, + 363, 364, 364, 365, 365, 366, 367, 367, 368, 368, + 369, 372, 373, 374, 374, 375, 375, 376, 380, 380, + 377, 377, 378, 378, 379, 370, 370, 371, 371, 354, + 354, 355, 355, 355, 423, 423, 423, 423, 423, 423, + 423, 423, 423, 423, 423, 423, 423, 423, 423, 423, + 423, 423, 423, 423, 423, 423, 423, 423, 423, 423, + 423, 423, 423, 423, 423, 423, 423, 423, 423, 423, + 677, 677, 678, 382, 382, 394, 394, 394, 394, 394, + 394, 381, 381, 383, 383, 335, 335, 333, 333, 325, + 325, 325, 325, 325, 325, 225, 225, 225, 324, 324, + 324, 324, 324, 324, 326, 326, 327, 327, 328, 328, + 328, 332, 332, 331, 331, 331, 331, 331, 329, 329, + 330, 330, 330, 330, 330, 330, 548, 548, 674, 674, + 675, 675, 670, 670, 670, 673, 673, 673, 673, 673, + 673, 673, 673, 673, 673, 673, 676, 676, 676, 672, + 672, 337, 447, 447, 447, 470, 470, 470, 470, 472, + 446, 446, 446, 353, 353, 352, 352, 350, 350, 350, + 350, 350, 350, 350, 350, 350, 350, 350, 350, 350, + 350, 350, 350, 350, 350, 350, 350, 350, 350, 350, + 350, 350, 350, 549, 549, 549, 547, 547, 547, 486, + 486, 487, 487, 405, 404, 404, 404, 404, 404, 402, + 403, 401, 401, 401, 401, 401, 398, 398, 397, 397, + 397, 399, 399, 399, 399, 399, 399, 525, 525, 395, + 395, 385, 385, 385, 384, 384, 384, 597, 493, 493, + 493, 493, 493, 493, 493, 493, 493, 493, 493, 493, + 493, 493, 493, 495, 495, 495, 495, 495, 495, 495, + 495, 495, 495, 495, 495, 495, 495, 495, 495, 495, + 495, 495, 495, 495, 495, 495, 495, 495, 495, 495, + 495, 495, 495, 400, 400, 400, 444, 444, 444, 444, + 444, 444, 444, 444, 444, 444, 444, 444, 444, 444, + 444, 445, 445, 445, 445, 445, 445, 445, 445, 496, + 496, 502, 502, 687, 687, 686, 338, 338, 338, 339, + 339, 339, 339, 339, 339, 339, 339, 339, 339, 348, + 348, 348, 577, 577, 577, 577, 577, 578, 578, 578, + 578, 578, 579, 579, 579, 575, 575, 576, 576, 507, + 508, 508, 624, 624, 625, 625, 568, 568, 569, 569, + 570, 574, 574, 572, 572, 573, 573, 571, 443, 443, + 443, 443, 443, 443, 443, 443, 443, 443, 443, 443, + 443, 443, 443, 443, 443, 443, 443, 443, 443, 443, + 443, 443, 443, 443, 443, 443, 638, 638, 638, 440, 440, 440, 440, 440, 440, 440, 440, 440, 440, 440, - 440, 440, 440, 440, 440, 440, 440, 635, 635, 635, - 437, 437, 437, 437, 437, 437, 437, 437, 437, 437, - 437, 437, 437, 437, 437, 437, 437, 437, 694, 694, - 694, 679, 679, 679, 680, 680, 680, 680, 680, 680, - 680, 680, 680, 680, 680, 680, 681, 681, 681, 681, - 681, 681, 681, 681, 681, 681, 681, 681, 681, 681, - 681, 681, 681, 682, 682, 682, 682, 439, 439, 439, - 439, 439, 438, 438, 438, 438, 438, 438, 438, 438, - 438, 438, 438, 438, 438, 438, 438, 438, 438, 438, - 506, 506, 507, 507, 632, 632, 632, 632, 632, 632, - 633, 633, 634, 634, 634, 634, 620, 620, 620, 620, - 620, 620, 620, 620, 620, 620, 620, 620, 620, 620, - 620, 620, 620, 620, 620, 620, 620, 620, 620, 620, - 620, 620, 620, 620, 620, 620, 491, 436, 436, 436, - 508, 500, 500, 501, 501, 502, 502, 494, 494, 494, - 494, 494, 495, 495, 497, 497, 497, 497, 497, 497, - 497, 497, 497, 497, 497, 497, 497, 497, 497, 489, - 489, 489, 489, 489, 489, 489, 489, 489, 489, 489, - 489, 489, 489, 489, 489, 489, 496, 496, 753, 753, - 498, 498, 510, 510, 510, 509, 509, 509, 509, 509, - 509, 509, 348, 348, 348, 348, 488, 488, 488, 487, - 487, 487, 487, 487, 487, 487, 487, 487, 487, 487, - 487, 337, 337, 337, 337, 337, 341, 341, 343, 343, - 343, 343, 343, 343, 343, 343, 343, 343, 343, 343, - 343, 343, 342, 342, 342, 342, 342, 342, 340, 340, - 340, 340, 340, 338, 338, 338, 338, 338, 338, 338, - 338, 338, 338, 338, 338, 338, 338, 338, 338, 338, - 338, 338, 338, 338, 338, 338, 338, 338, 144, 145, - 239, 239, 146, 146, 339, 346, 346, 346, 346, 346, + 440, 440, 440, 440, 440, 440, 440, 697, 697, 697, + 682, 682, 682, 683, 683, 683, 683, 683, 683, 683, + 683, 683, 683, 683, 683, 684, 684, 684, 684, 684, + 684, 684, 684, 684, 684, 684, 684, 684, 684, 684, + 684, 684, 685, 685, 685, 685, 442, 442, 442, 442, + 442, 441, 441, 441, 441, 441, 441, 441, 441, 441, + 441, 441, 441, 441, 441, 441, 441, 441, 441, 509, + 509, 510, 510, 635, 635, 635, 635, 635, 635, 636, + 636, 637, 637, 637, 637, 623, 623, 623, 623, 623, + 623, 623, 623, 623, 623, 623, 623, 623, 623, 623, + 623, 623, 623, 623, 623, 623, 623, 623, 623, 623, + 623, 623, 623, 623, 623, 494, 439, 439, 439, 511, + 503, 503, 504, 504, 505, 505, 497, 497, 497, 497, + 497, 498, 498, 500, 500, 500, 500, 500, 500, 500, + 500, 500, 500, 500, 500, 500, 500, 500, 492, 492, + 492, 492, 492, 492, 492, 492, 492, 492, 492, 492, + 492, 492, 492, 492, 492, 499, 499, 756, 756, 501, + 501, 513, 513, 513, 512, 512, 512, 512, 512, 512, + 512, 351, 351, 351, 351, 491, 491, 491, 490, 490, + 490, 490, 490, 490, 490, 490, 490, 490, 490, 490, + 340, 340, 340, 340, 340, 344, 344, 346, 346, 346, 346, 346, 346, 346, 346, 346, 346, 346, 346, 346, - 346, 346, 346, 446, 446, 601, 601, 604, 604, 602, - 602, 603, 605, 605, 605, 606, 606, 606, 607, 607, - 607, 611, 611, 455, 455, 455, 463, 463, 462, 462, - 462, 462, 462, 462, 462, 462, 462, 462, 462, 462, - 462, 462, 462, 462, 462, 462, 462, 462, 462, 462, - 462, 462, 462, 462, 462, 462, 462, 462, 462, 462, - 462, 462, 462, 462, 462, 462, 462, 462, 462, 462, - 462, 462, 462, 462, 462, 462, 462, 462, 462, 462, - 462, 462, 462, 462, 462, 462, 462, 462, 462, 462, - 462, 462, 462, 462, 462, 462, 462, 462, 462, 462, - 462, 462, 462, 462, 462, 462, 462, 462, 462, 462, - 462, 462, 462, 462, 462, 462, 462, 462, 462, 462, - 462, 462, 462, 462, 462, 462, 462, 462, 462, 462, - 462, 462, 462, 462, 462, 462, 462, 462, 462, 462, - 462, 462, 462, 462, 462, 462, 462, 462, 462, 462, - 462, 462, 462, 462, 462, 462, 462, 462, 462, 462, - 462, 462, 462, 462, 462, 462, 462, 462, 462, 462, - 462, 462, 462, 462, 462, 462, 462, 462, 462, 462, - 462, 462, 462, 462, 462, 462, 462, 462, 462, 462, - 462, 462, 462, 462, 462, 462, 462, 462, 462, 462, - 462, 462, 462, 462, 462, 462, 462, 462, 462, 462, - 462, 462, 462, 462, 462, 462, 462, 462, 462, 462, - 462, 462, 462, 462, 462, 462, 462, 462, 462, 462, - 462, 462, 462, 462, 462, 462, 462, 462, 462, 462, - 462, 462, 462, 462, 462, 462, 462, 462, 462, 462, - 462, 462, 462, 462, 462, 462, 462, 462, 462, 462, - 462, 462, 462, 462, 462, 462, 462, 462, 462, 462, - 462, 462, 462, 462, 462, 462, 462, 462, 462, 462, - 462, 462, 462, 462, 462, 462, 462, 462, 462, 462, - 462, 462, 462, 462, 462, 462, 462, 462, 462, 462, - 462, 462, 462, 462, 462, 462, 462, 462, 462, 462, - 462, 462, 462, 462, 462, 462, 462, 462, 462, 462, - 462, 462, 462, 462, 462, 462, 462, 462, 462, 462, - 462, 462, 462, 462, 462, 462, 462, 462, 462, 462, - 462, 462, 462, 462, 462, 462, 462, 462, 462, 462, - 462, 462, 462, 462, 462, 462, 462, 462, 462, 462, - 462, 462, 462, 462, 462, 462, 462, 462, 462, 462, - 462, 462, 462, 462, 462, 462, 462, 462, 462, 462, - 462, 462, 462, 462, 462, 462, 462, 462, 462, 462, - 462, 462, 462, 462, 462, 462, 462, 462, 462, 462, - 462, 462, 462, 462, 462, 462, 462, 462, 462, 462, - 462, 462, 462, 462, 462, 462, 462, 462, 462, 462, - 462, 462, 462, 462, 462, 462, 462, 462, 462, 462, - 462, 462, 462, 462, 462, 462, 462, 462, 462, 462, - 462, 462, 462, 462, 462, 462, 462, 462, 462, 462, - 462, 462, 462, 462, 462, 462, 462, 462, 462, 462, - 462, 462, 462, 462, 462, 462, 462, 462, 462, 462, - 462, 462, 462, 461, 461, 461, 461, 461, 461, 461, - 461, 461, 460, 460, 460, 460, 460, 460, 460, 460, - 460, 460, 460, 460, 460, 460, 460, 460, 460, 460, - 460, 460, 460, 460, 460, 460, 460, 460, 460, 460, - 460, 460, 460, 460, 460, 460, 460, 460, 460, 460, - 460, 460, 460, 460, 460, 460, 460, 460, 460, 460, - 460, 460, 460, 460, 460, 460, 460, 751, 751, 752, - 752, + 346, 345, 345, 345, 345, 345, 345, 343, 343, 343, + 343, 343, 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 144, 145, 239, + 239, 146, 146, 342, 349, 349, 349, 349, 349, 349, + 349, 349, 349, 349, 349, 349, 349, 349, 349, 349, + 349, 349, 449, 449, 604, 604, 607, 607, 605, 605, + 606, 608, 608, 608, 609, 609, 609, 610, 610, 610, + 614, 614, 458, 458, 458, 466, 466, 465, 465, 465, + 465, 465, 465, 465, 465, 465, 465, 465, 465, 465, + 465, 465, 465, 465, 465, 465, 465, 465, 465, 465, + 465, 465, 465, 465, 465, 465, 465, 465, 465, 465, + 465, 465, 465, 465, 465, 465, 465, 465, 465, 465, + 465, 465, 465, 465, 465, 465, 465, 465, 465, 465, + 465, 465, 465, 465, 465, 465, 465, 465, 465, 465, + 465, 465, 465, 465, 465, 465, 465, 465, 465, 465, + 465, 465, 465, 465, 465, 465, 465, 465, 465, 465, + 465, 465, 465, 465, 465, 465, 465, 465, 465, 465, + 465, 465, 465, 465, 465, 465, 465, 465, 465, 465, + 465, 465, 465, 465, 465, 465, 465, 465, 465, 465, + 465, 465, 465, 465, 465, 465, 465, 465, 465, 465, + 465, 465, 465, 465, 465, 465, 465, 465, 465, 465, + 465, 465, 465, 465, 465, 465, 465, 465, 465, 465, + 465, 465, 465, 465, 465, 465, 465, 465, 465, 465, + 465, 465, 465, 465, 465, 465, 465, 465, 465, 465, + 465, 465, 465, 465, 465, 465, 465, 465, 465, 465, + 465, 465, 465, 465, 465, 465, 465, 465, 465, 465, + 465, 465, 465, 465, 465, 465, 465, 465, 465, 465, + 465, 465, 465, 465, 465, 465, 465, 465, 465, 465, + 465, 465, 465, 465, 465, 465, 465, 465, 465, 465, + 465, 465, 465, 465, 465, 465, 465, 465, 465, 465, + 465, 465, 465, 465, 465, 465, 465, 465, 465, 465, + 465, 465, 465, 465, 465, 465, 465, 465, 465, 465, + 465, 465, 465, 465, 465, 465, 465, 465, 465, 465, + 465, 465, 465, 465, 465, 465, 465, 465, 465, 465, + 465, 465, 465, 465, 465, 465, 465, 465, 465, 465, + 465, 465, 465, 465, 465, 465, 465, 465, 465, 465, + 465, 465, 465, 465, 465, 465, 465, 465, 465, 465, + 465, 465, 465, 465, 465, 465, 465, 465, 465, 465, + 465, 465, 465, 465, 465, 465, 465, 465, 465, 465, + 465, 465, 465, 465, 465, 465, 465, 465, 465, 465, + 465, 465, 465, 465, 465, 465, 465, 465, 465, 465, + 465, 465, 465, 465, 465, 465, 465, 465, 465, 465, + 465, 465, 465, 465, 465, 465, 465, 465, 465, 465, + 465, 465, 465, 465, 465, 465, 465, 465, 465, 465, + 465, 465, 465, 465, 465, 465, 465, 465, 465, 465, + 465, 465, 465, 465, 465, 465, 465, 465, 465, 465, + 465, 465, 465, 465, 465, 465, 465, 465, 465, 465, + 465, 465, 465, 465, 465, 465, 465, 465, 465, 465, + 465, 465, 465, 465, 465, 465, 465, 465, 465, 465, + 465, 465, 465, 465, 465, 465, 465, 465, 465, 465, + 465, 465, 465, 465, 465, 465, 465, 465, 465, 465, + 465, 465, 465, 465, 465, 465, 465, 465, 465, 465, + 465, 465, 465, 465, 465, 465, 464, 464, 464, 464, + 464, 464, 464, 464, 464, 463, 463, 463, 463, 463, + 463, 463, 463, 463, 463, 463, 463, 463, 463, 463, + 463, 463, 463, 463, 463, 463, 463, 463, 463, 463, + 463, 463, 463, 463, 463, 463, 463, 463, 463, 463, + 463, 463, 463, 463, 463, 463, 463, 463, 463, 463, + 463, 463, 463, 463, 463, 463, 463, 463, 463, 463, + 754, 754, 755, 755, } var yyR2 = [...]int{ @@ -12731,115 +12787,117 @@ var yyR2 = [...]int{ 6, 10, 0, 3, 0, 2, 0, 4, 1, 1, 0, 2, 0, 2, 7, 1, 1, 9, 1, 3, 0, 1, 1, 3, 1, 3, 0, 1, 1, 1, - 0, 2, 14, 1, 3, 0, 1, 1, 3, 1, + 0, 2, 15, 1, 3, 0, 1, 1, 3, 1, 1, 2, 4, 1, 1, 1, 1, 0, 1, 2, - 9, 9, 7, 8, 1, 2, 3, 3, 3, 0, - 4, 1, 1, 1, 1, 1, 0, 1, 1, 1, - 1, 1, 4, 1, 1, 1, 3, 3, 4, 3, - 3, 0, 1, 1, 1, 0, 2, 7, 8, 10, - 8, 2, 2, 8, 0, 3, 3, 0, 3, 0, - 3, 0, 3, 0, 5, 1, 3, 0, 3, 3, - 0, 2, 9, 8, 0, 2, 2, 3, 3, 0, - 2, 0, 2, 4, 5, 4, 4, 4, 6, 4, - 8, 5, 1, 0, 2, 2, 1, 3, 2, 1, - 3, 2, 1, 3, 2, 0, 1, 3, 4, 3, - 1, 1, 4, 1, 3, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 0, 1, 1, 1, 1, 11, - 0, 2, 3, 3, 2, 2, 3, 1, 1, 3, - 3, 3, 3, 3, 3, 4, 2, 2, 3, 3, - 3, 3, 3, 3, 3, 3, 1, 1, 1, 3, - 3, 3, 3, 3, 1, 3, 3, 4, 0, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 6, 8, - 10, 0, 4, 1, 1, 0, 3, 3, 0, 1, - 0, 1, 1, 2, 4, 4, 4, 0, 1, 4, - 4, 4, 0, 2, 8, 9, 5, 5, 8, 7, - 8, 12, 12, 13, 13, 0, 4, 0, 3, 3, - 3, 2, 2, 0, 3, 3, 3, 4, 4, 0, - 3, 0, 2, 0, 1, 1, 1, 1, 11, 9, - 9, 9, 9, 9, 9, 6, 11, 6, 9, 7, - 10, 8, 11, 7, 6, 9, 11, 2, 2, 9, - 4, 5, 3, 0, 4, 1, 3, 0, 3, 6, - 0, 2, 10, 0, 2, 0, 2, 0, 3, 2, - 4, 3, 0, 2, 1, 0, 2, 3, 0, 2, - 3, 0, 2, 1, 0, 3, 2, 4, 3, 0, - 1, 0, 1, 1, 0, 6, 0, 3, 5, 0, - 4, 0, 3, 1, 3, 4, 5, 0, 3, 1, - 3, 2, 3, 1, 2, 1, 0, 4, 6, 5, - 0, 2, 0, 2, 4, 5, 4, 5, 1, 5, - 6, 5, 0, 3, 0, 1, 4, 0, 4, 1, - 3, 3, 1, 1, 1, 1, 4, 0, 4, 1, - 3, 3, 1, 1, 1, 1, 4, 0, 4, 1, - 3, 3, 4, 4, 0, 4, 1, 3, 3, 1, - 1, 0, 4, 1, 3, 3, 1, 1, 1, 1, - 0, 1, 1, 3, 2, 3, 3, 3, 3, 4, - 4, 3, 3, 3, 3, 4, 4, 3, 3, 3, - 3, 3, 3, 3, 3, 3, 3, 3, 3, 2, - 3, 3, 3, 3, 3, 3, 3, 3, 1, 5, - 4, 1, 3, 3, 2, 2, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 3, 3, 5, - 0, 3, 3, 6, 5, 3, 1, 1, 1, 0, - 5, 5, 5, 5, 6, 0, 1, 1, 3, 1, - 1, 1, 1, 1, 7, 9, 7, 7, 9, 2, - 1, 7, 9, 7, 9, 8, 5, 0, 1, 0, - 1, 1, 1, 1, 3, 3, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 0, 1, 3, 1, 3, 5, 1, 1, 1, 1, - 1, 1, 3, 5, 0, 1, 1, 2, 1, 2, - 2, 2, 1, 1, 2, 2, 2, 2, 2, 3, - 3, 2, 2, 1, 5, 6, 4, 2, 1, 1, - 1, 5, 7, 5, 0, 1, 1, 1, 2, 2, - 0, 1, 1, 2, 5, 0, 1, 1, 2, 2, - 3, 3, 1, 1, 2, 2, 2, 0, 1, 2, - 2, 2, 0, 4, 7, 3, 3, 3, 0, 3, - 0, 3, 1, 1, 1, 1, 1, 1, 1, 3, + 0, 2, 1, 3, 3, 1, 1, 4, 5, 9, + 9, 7, 8, 1, 2, 3, 3, 3, 0, 4, + 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, + 1, 4, 1, 1, 1, 3, 3, 4, 3, 3, + 0, 1, 1, 1, 0, 2, 7, 8, 10, 8, + 2, 2, 8, 0, 3, 3, 0, 3, 0, 3, + 0, 3, 0, 5, 1, 3, 0, 3, 3, 0, + 2, 9, 8, 0, 2, 2, 3, 3, 0, 2, + 0, 2, 4, 5, 4, 4, 4, 6, 4, 8, + 5, 1, 0, 2, 2, 1, 3, 2, 1, 3, + 2, 1, 3, 2, 0, 1, 3, 4, 3, 1, + 1, 4, 1, 3, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 0, 1, 1, 1, 1, 11, 0, + 2, 3, 3, 2, 2, 3, 1, 1, 3, 3, + 3, 3, 3, 3, 4, 2, 2, 3, 3, 3, + 3, 3, 3, 3, 3, 1, 1, 1, 3, 3, + 3, 3, 3, 1, 3, 3, 4, 0, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 6, 8, 10, + 0, 4, 1, 1, 0, 3, 3, 0, 1, 0, + 1, 1, 2, 4, 4, 4, 0, 1, 4, 4, + 4, 0, 2, 8, 9, 5, 5, 8, 7, 8, + 12, 12, 13, 13, 0, 4, 0, 3, 3, 3, + 2, 2, 0, 3, 3, 3, 4, 4, 0, 3, + 0, 2, 0, 1, 1, 1, 1, 11, 9, 9, + 9, 9, 9, 9, 6, 11, 6, 9, 7, 10, + 8, 11, 7, 6, 9, 11, 2, 2, 9, 4, + 5, 3, 0, 4, 1, 3, 0, 3, 6, 0, + 2, 10, 0, 2, 0, 2, 0, 3, 2, 4, + 3, 0, 2, 1, 0, 2, 3, 0, 2, 3, + 0, 2, 1, 0, 3, 2, 4, 3, 0, 1, + 0, 1, 1, 0, 6, 0, 3, 5, 0, 4, + 0, 3, 1, 3, 4, 5, 0, 3, 1, 3, + 2, 3, 1, 2, 1, 0, 4, 6, 5, 0, + 2, 0, 2, 4, 5, 4, 5, 1, 5, 6, + 5, 0, 3, 0, 1, 4, 0, 4, 1, 3, + 3, 1, 1, 1, 1, 4, 0, 4, 1, 3, + 3, 1, 1, 1, 1, 4, 0, 4, 1, 3, + 3, 4, 4, 0, 4, 1, 3, 3, 1, 1, + 0, 4, 1, 3, 3, 1, 1, 1, 1, 0, + 1, 1, 3, 2, 3, 3, 3, 3, 4, 4, + 3, 3, 3, 3, 4, 4, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 2, 3, + 3, 3, 3, 3, 3, 3, 3, 1, 5, 4, + 1, 3, 3, 2, 2, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 3, 3, 5, 0, + 3, 3, 6, 5, 3, 1, 1, 1, 0, 5, + 5, 5, 5, 6, 0, 1, 1, 3, 1, 1, + 1, 1, 1, 7, 9, 7, 7, 9, 2, 1, + 7, 9, 7, 9, 8, 5, 0, 1, 0, 1, + 1, 1, 1, 3, 3, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, + 1, 3, 1, 3, 5, 1, 1, 1, 1, 1, + 1, 3, 5, 0, 1, 1, 2, 1, 2, 2, + 2, 1, 1, 2, 2, 2, 2, 2, 3, 3, + 2, 2, 1, 5, 6, 4, 2, 1, 1, 1, + 5, 7, 5, 0, 1, 1, 1, 2, 2, 0, + 1, 1, 2, 5, 0, 1, 1, 2, 2, 3, + 3, 1, 1, 2, 2, 2, 0, 1, 2, 2, + 2, 0, 4, 7, 3, 3, 3, 0, 3, 0, + 3, 1, 1, 1, 1, 1, 1, 1, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, - 3, 3, 3, 1, 1, 1, 1, 3, 3, 5, - 2, 2, 2, 2, 2, 4, 1, 1, 2, 5, - 6, 8, 6, 3, 6, 6, 1, 1, 1, 1, - 1, 1, 3, 9, 1, 1, 1, 4, 4, 4, - 4, 5, 4, 6, 8, 10, 6, 8, 10, 6, - 6, 9, 7, 9, 7, 7, 7, 9, 7, 7, - 0, 2, 0, 1, 1, 2, 4, 1, 2, 2, - 1, 1, 2, 2, 1, 2, 2, 2, 2, 2, - 0, 1, 1, 1, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 1, 1, 1, 2, 5, 0, 1, - 3, 0, 1, 0, 2, 0, 2, 0, 1, 2, - 4, 4, 0, 1, 0, 2, 1, 3, 5, 9, - 6, 6, 8, 6, 6, 5, 5, 6, 6, 6, - 6, 6, 5, 6, 6, 6, 6, 6, 6, 6, - 6, 9, 6, 6, 6, 6, 8, 1, 1, 1, - 4, 6, 4, 6, 8, 6, 6, 4, 6, 4, - 4, 4, 3, 4, 6, 6, 7, 4, 1, 1, + 3, 3, 1, 1, 1, 1, 3, 3, 5, 2, + 2, 2, 2, 2, 4, 1, 1, 2, 5, 6, + 8, 6, 3, 6, 6, 1, 1, 1, 1, 1, + 1, 3, 9, 1, 1, 1, 4, 4, 4, 4, + 5, 4, 6, 8, 10, 6, 8, 10, 6, 6, + 9, 7, 9, 7, 7, 7, 9, 7, 7, 0, + 2, 0, 1, 1, 2, 4, 1, 2, 2, 1, + 1, 2, 2, 1, 2, 2, 2, 2, 2, 0, + 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 1, 1, 1, 2, 5, 0, 1, 3, + 0, 1, 0, 2, 0, 2, 0, 1, 2, 4, + 4, 0, 1, 0, 2, 1, 3, 5, 9, 6, + 6, 8, 6, 6, 5, 5, 6, 6, 6, 6, + 6, 5, 6, 6, 6, 6, 6, 6, 6, 6, + 9, 6, 6, 6, 6, 8, 1, 1, 1, 4, + 6, 4, 6, 8, 6, 6, 4, 6, 4, 4, + 4, 3, 4, 6, 6, 7, 4, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 2, 2, 8, 8, + 6, 4, 2, 3, 2, 4, 2, 2, 4, 6, + 2, 2, 4, 6, 4, 2, 4, 4, 4, 0, + 1, 2, 3, 1, 1, 1, 1, 1, 1, 0, + 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 2, 2, 8, - 8, 6, 4, 2, 3, 2, 4, 2, 2, 4, - 6, 2, 2, 4, 6, 4, 2, 4, 4, 4, - 0, 1, 2, 3, 1, 1, 1, 1, 1, 1, - 0, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 3, 0, 1, 1, 3, + 0, 1, 1, 3, 1, 3, 3, 3, 3, 2, + 1, 1, 1, 3, 4, 4, 3, 4, 4, 3, + 4, 4, 3, 4, 4, 3, 4, 1, 3, 4, + 4, 6, 4, 5, 5, 4, 5, 5, 3, 4, + 4, 5, 6, 6, 1, 0, 2, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 3, 0, 1, 1, - 3, 0, 1, 1, 3, 1, 3, 3, 3, 3, - 2, 1, 1, 1, 3, 4, 4, 3, 4, 4, - 3, 4, 4, 3, 4, 4, 3, 4, 1, 3, - 4, 4, 6, 4, 5, 5, 4, 5, 5, 3, - 4, 4, 5, 6, 6, 1, 0, 2, 0, 1, + 1, 2, 2, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 2, 1, 1, 1, 2, + 3, 1, 1, 1, 4, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 2, 2, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, - 2, 3, 1, 1, 1, 4, 2, 1, 1, 1, + 1, 2, 3, 2, 2, 2, 2, 1, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 1, 2, + 1, 1, 1, 2, 1, 1, 1, 1, 2, 2, + 2, 2, 2, 2, 4, 4, 1, 2, 2, 7, + 8, 3, 5, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 2, 3, 2, 2, 2, 2, 1, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, - 2, 1, 1, 1, 2, 1, 1, 1, 1, 2, - 2, 2, 2, 2, 2, 4, 4, 1, 2, 2, - 7, 8, 3, 5, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 3, 0, 1, 0, 3, 0, 3, + 3, 0, 3, 5, 0, 3, 5, 0, 1, 1, + 0, 1, 1, 2, 2, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 3, 0, 1, 0, 3, 0, - 3, 3, 0, 3, 5, 0, 3, 5, 0, 1, - 1, 0, 1, 1, 2, 2, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, @@ -12890,545 +12948,546 @@ var yyR2 = [...]int{ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 0, 2, 0, - 2, + 0, 2, 0, 2, } var yyChk = [...]int{ - -1000, -742, -745, -5, -1, -2, -4, -146, -109, 726, + -1000, -745, -748, -5, -1, -2, -4, -146, -109, 730, -7, -16, -148, -134, -149, -8, -143, -10, -11, -12, -204, -14, -116, -138, -140, -142, -141, -55, -13, -135, -136, -137, -95, -96, -119, -127, -130, -131, -132, -133, -150, -144, -145, -147, -236, -151, -160, -161, -211, -164, - -166, -167, -199, -200, -230, 715, -110, -111, -112, -113, + -166, -167, -199, -200, -230, 719, -110, -111, -112, -113, -114, -115, -36, -35, -34, -33, -191, -201, -205, -207, - -162, -50, 631, 721, 760, 532, -9, -685, -624, 585, - -17, -18, -104, -19, 272, 299, -471, -117, -472, -473, - -475, -746, -56, -57, -58, -71, -72, -73, -74, -75, + -162, -50, 635, 725, 764, 536, -9, -688, -627, 589, + -17, -18, -104, -19, 272, 299, -474, -117, -475, -476, + -478, -749, -56, -57, -58, -71, -72, -73, -74, -75, -85, -86, -87, -59, -60, -61, -64, -62, -78, -77, -79, -80, -81, -82, -83, -84, -63, -67, -194, -195, -196, -197, -88, -65, -66, -209, -212, -163, -89, -90, -91, -68, -53, -54, -69, -70, -93, -92, -98, -94, -99, -193, -203, -15, -210, -100, -52, -102, -103, -101, 273, 344, 292, -97, 80, -120, -121, -122, -123, -124, - -125, -126, -128, -129, 457, 463, 519, 713, 714, 65, - -238, -240, -242, 762, 763, 766, 616, 618, 317, 180, + -125, -126, -128, -129, 457, 463, 523, 717, 718, 65, + -238, -240, -242, 766, 767, 770, 620, 622, 317, 180, 181, 183, 184, 188, 191, -37, -38, -39, -40, -41, -42, -44, -43, -45, -46, -47, -48, -49, 268, 16, 14, 18, 58, -20, -23, -21, -24, -22, -31, -32, -30, -25, -27, -192, -198, -26, -202, -206, -208, -165, -51, -28, -29, 294, 293, 41, 360, 361, 362, 461, 269, 271, 17, 34, 45, 436, -241, -245, 90, -248, - -244, 617, 270, -243, -247, 15, 278, 780, -6, -3, + -244, 621, 270, -243, -247, 15, 278, 784, -6, -3, -2, -178, -182, -186, -189, -190, -187, -188, -4, -146, - 125, 284, 716, -467, 453, 717, 719, 718, 93, 101, - -460, -462, 760, 532, 299, 457, 463, 713, 714, 763, - 766, 616, 618, 317, 633, 634, 635, 636, 637, 638, - 639, 640, 642, 643, 644, 645, 646, 647, 648, 691, - 692, 658, 659, 649, 650, 651, 652, 653, 654, 655, - 656, 660, 661, 662, 663, 664, 665, 666, 667, 668, - 669, 670, 671, 672, 673, 588, 589, 693, 695, 696, - 697, 698, 641, 678, 686, 687, 688, 771, 772, 434, - 435, 624, 710, 769, 311, 335, 486, 341, 348, 415, - 180, 198, 194, 223, 213, 421, 367, 739, 740, 366, - 617, 189, 315, 353, 316, 100, 183, 571, 115, 544, - 515, 186, 373, 376, 374, 375, 330, 332, 334, 447, - 337, 336, 338, 340, 615, 371, 437, 209, 203, 329, - 313, 201, 318, 422, 43, 319, 413, 412, 228, 320, - 321, 628, 540, 433, 546, 345, 56, 513, 202, 333, - 543, 709, 236, 240, 244, 245, 246, 247, 248, 249, - 250, 251, 252, 253, 562, 419, 400, 401, 402, 403, - 563, 424, 171, 172, 548, 738, 745, 746, 747, 749, - 750, 751, 748, 418, 565, 423, 227, 230, 231, 232, - 233, 234, 235, 291, 409, 410, 425, 426, 427, 428, - 429, 431, 432, 46, 626, 303, 566, 238, 756, 217, - 226, 221, 574, 349, 347, 414, 225, 197, 220, 314, - 778, 761, 69, 242, 241, 243, 509, 510, 511, 512, - 322, 323, 451, 561, 741, 742, 216, 204, 438, 190, - 25, 569, 298, 545, 464, 377, 51, 378, 324, 342, - 350, 372, 237, 239, 305, 310, 365, 420, 627, 517, - 518, 309, 553, 554, 346, 567, 200, 302, 331, 297, - 570, 757, 743, 191, 777, 466, 325, 184, 339, 564, - 759, 573, 68, 166, 196, 187, 733, 734, 288, 694, - 181, 307, 312, 711, 758, 326, 327, 328, 352, 351, - 343, 188, 218, 304, 224, 207, 195, 219, 182, 306, - 572, 167, 707, 436, 496, 215, 212, 308, 281, 712, - 568, 547, 185, 500, 169, 210, 354, 701, 702, 703, - 706, 452, 408, 355, 356, 208, 295, 538, 539, 359, - 506, 395, 480, 516, 487, 481, 259, 260, 363, 550, - 552, 229, 704, 379, 380, 381, 542, 382, 384, 385, - 390, 456, 60, 62, 102, 105, 104, 764, 765, 67, - 32, 442, 445, 478, 779, 482, 397, 708, 625, 394, - 398, 399, 446, 28, 498, 468, 502, 501, 52, 53, - 54, 57, 58, 59, 61, 63, 64, 55, 614, 461, - 475, 575, 48, 50, 471, 472, 30, 448, 497, 520, - 393, 499, 531, 49, 529, 530, 551, 29, 450, 449, - 66, 47, 505, 507, 508, 357, 391, 459, 722, 576, - 454, 470, 474, 455, 396, 444, 476, 71, 467, 723, - 462, 460, 773, 774, 392, 629, 630, 404, 657, 439, - 514, 611, 610, 609, 608, 607, 606, 605, 604, 360, - 361, 362, 483, 484, 485, 495, 488, 489, 490, 491, - 492, 493, 494, 534, 535, 724, 725, 555, 557, 558, - 622, 559, 556, 276, 768, 767, 440, 441, 279, 727, - 728, 103, 729, 731, 730, 31, 732, 755, 752, 753, - 754, 632, 560, 619, 735, 621, 620, 679, 680, 681, - 682, 683, -551, -549, -467, 617, 317, 760, 713, 714, - 463, 616, 618, 457, 436, 763, 766, 58, 461, 299, - 360, 361, 362, 532, 434, -315, -467, 278, 767, -97, - -18, -17, -9, -624, -104, -241, -242, -750, 265, 267, - 472, -330, 278, -467, -476, 26, 513, -118, 514, 273, - 274, 90, 81, -467, -10, -132, -8, -143, -95, -236, - -685, 519, -474, -467, 360, 360, 622, -474, 278, 278, - 737, 518, 559, 622, 738, 745, 756, 554, 447, 104, - 105, 733, 734, 30, 278, 458, 305, 552, 550, 551, - 555, 556, 557, 558, -76, -636, -612, 547, 638, 546, - -480, 539, 545, 537, 549, 540, 435, 385, 382, 631, - 384, 389, 268, 727, 613, -455, 480, 516, 575, 576, - 459, 517, 562, 564, 541, 115, 214, 211, -469, 309, - 494, -467, -613, 284, -555, -526, 310, -554, -528, -557, - -529, 35, 268, 270, 269, 631, 306, 18, 461, 280, - 16, 15, 462, 292, 28, 29, 31, 17, 463, 465, - 32, 466, 469, 470, 471, 45, 475, 476, 299, 93, - 101, 96, 679, 680, 681, 682, 683, 317, 278, -314, - -467, -502, -494, 122, -497, -489, -490, -492, -445, -657, - -487, 90, 151, 152, 159, 123, 161, 781, -491, -594, - 39, 125, 637, 641, 678, 586, -437, -438, -439, -440, - -441, -442, 623, -467, -658, -656, 96, 106, 108, 112, - 113, 111, 109, 174, 205, 110, 97, 175, -242, 93, - -679, 647, 653, -461, 670, 695, 696, 697, 698, 669, - 65, -620, -634, 277, -632, 173, 211, 295, 207, 16, - 157, 506, 208, 686, 687, 688, 644, 666, 588, 589, - 693, 648, 771, 772, 658, 673, 639, 640, 642, 634, - 635, 636, 638, 649, 651, 665, -635, 661, 671, 672, - 657, 689, 690, 752, 691, 692, 674, 675, 676, 685, - 684, 677, 679, 680, 681, 682, 683, 731, 95, 94, - 664, 663, 650, 645, 646, 652, 633, 643, 654, 662, - 667, 668, 445, 115, 446, 447, 578, 437, 84, 448, - 284, 513, 74, 449, 450, 451, 452, 453, 585, 454, - 777, 75, 455, 444, 299, 496, 456, 210, 229, 591, - 590, 592, 582, 579, 577, 580, 581, 583, 584, 655, - 656, 660, -239, -241, -685, -242, -168, -170, 699, -735, - -278, -736, -427, 25, -737, 6, 7, 8, 9, 175, - -726, 515, 627, 96, 578, 278, 353, 434, 19, 737, - 389, 615, 737, 389, 615, 367, 185, 182, -541, 185, - 121, 191, 190, 282, 185, -541, -467, 188, 737, 187, - 733, 738, 745, 622, 363, -516, -215, 434, 496, 382, - 102, 309, -520, -517, -614, 357, 353, 279, 118, -216, - 289, 430, 288, 116, 578, 277, 473, 348, 60, 62, - 35, 48, -262, 51, 283, 42, -330, -687, 612, -686, - -467, -695, -696, 265, 266, 267, 42, 738, 745, 279, - 281, 278, 733, 622, 309, 434, 578, 496, 102, 382, - 278, -695, 756, 182, 562, 564, 515, 309, 494, 44, - -548, 506, -547, -549, 563, 574, 94, 95, 561, -455, - 115, 538, 538, -735, -427, -736, -273, -427, -272, 11, - -238, -242, -245, -249, -685, -251, 65, 615, 737, 622, - 738, 745, 279, 434, 496, 309, 280, -614, 281, 578, - 277, 360, 458, 305, 382, 389, 102, 187, 733, -255, - -256, -257, 261, 262, 263, 73, 266, 264, 70, 35, - 36, 37, -330, -1, 129, 780, -494, -494, -6, 783, - -6, -494, -467, -467, 177, -337, -341, -338, -340, -339, - 769, -343, -342, 211, 212, 173, 215, 217, 222, 218, - 219, 220, 221, 223, 224, 225, 226, 227, 230, 231, - 232, 233, 234, 235, 228, 34, 229, 295, 207, 208, - 209, 210, -346, 194, 213, 625, 254, 195, 255, 196, - 256, 197, 257, 171, 172, 258, 198, 201, 202, 203, - 204, 200, 236, 237, 238, 239, 240, 241, 242, 243, - 245, 244, 246, 247, 248, 249, 250, 251, 252, 253, - 176, -298, 96, 35, 90, 176, -330, 96, -735, -272, - -273, -278, -262, 19, -330, -321, 176, 781, -443, -467, - 515, 132, -118, 81, -118, 514, 81, -118, 514, 273, - -688, -689, -690, -692, 273, 514, 513, 274, 344, -141, - 176, 317, 19, -474, -474, -467, 87, -299, -300, -330, - -332, -330, -719, -718, 35, 622, -303, 26, 560, 740, - 742, 538, 748, 757, 758, 759, 86, -479, 140, 144, - -479, -423, 20, -423, 26, 26, 307, 307, 307, -479, - 347, -747, -748, 19, 142, -477, -748, -477, -477, -479, - -749, 280, 548, 46, 308, 307, -275, -276, 24, 90, - -275, 542, 538, -577, 543, 544, -481, -748, -480, -479, - -479, -480, -479, -479, 388, -479, 35, 383, 384, 278, - 281, 578, 382, 728, -747, -747, 34, 34, -528, 309, - -555, -526, 39, 86, 177, 282, 177, 86, 90, 459, - 434, 496, 460, 578, 278, 473, 281, 309, 474, 434, - 496, 278, 281, 578, 309, 434, 278, 281, 496, 309, - 474, 434, 537, 538, 281, 30, 464, 467, 468, 538, - -640, 574, -330, 177, 121, 118, 120, -494, 139, -509, - 132, 133, 134, 135, 136, 137, 138, 146, 145, 158, - 151, 152, 153, 154, 155, 156, 157, 147, 148, 149, - 150, 142, 122, 161, 778, 140, 144, 141, 124, 119, - 164, 163, -242, -494, -502, 65, -492, -492, -492, -492, - -492, -467, -594, -499, -494, 90, 90, 90, 90, 90, - 176, 109, 96, 90, -494, 90, 90, 90, 90, 90, - 90, 90, 90, 90, 90, 90, 90, 90, -633, 90, - 90, -506, -507, 90, 90, -487, -443, 90, 96, 96, - 90, 90, 90, 96, 90, 90, 90, -507, -507, 90, + 125, 284, 720, -470, 453, 721, 723, 722, 93, 101, + -463, -465, 764, 536, 299, 457, 463, 717, 718, 767, + 770, 620, 622, 317, 637, 638, 639, 640, 641, 642, + 643, 644, 646, 647, 648, 649, 650, 651, 652, 695, + 696, 662, 663, 653, 654, 655, 656, 657, 658, 659, + 660, 664, 665, 666, 667, 668, 669, 670, 671, 672, + 673, 674, 675, 676, 677, 592, 593, 697, 699, 700, + 701, 702, 645, 682, 690, 691, 692, 775, 776, 434, + 435, 628, 714, 773, 311, 335, 490, 341, 348, 415, + 180, 198, 194, 223, 213, 421, 477, 367, 743, 744, + 366, 621, 189, 315, 353, 316, 100, 183, 575, 115, + 548, 519, 186, 373, 376, 374, 375, 330, 332, 334, + 447, 337, 336, 338, 340, 619, 371, 437, 209, 203, + 329, 313, 201, 318, 422, 43, 319, 413, 412, 228, + 320, 321, 632, 544, 433, 550, 345, 56, 517, 202, + 333, 547, 713, 236, 240, 244, 245, 246, 247, 248, + 249, 250, 251, 252, 253, 566, 419, 400, 401, 402, + 403, 567, 424, 478, 171, 172, 552, 742, 749, 750, + 751, 753, 754, 755, 752, 418, 569, 423, 227, 230, + 231, 232, 233, 234, 235, 291, 409, 410, 425, 426, + 427, 428, 429, 431, 432, 46, 630, 303, 570, 238, + 760, 217, 226, 221, 578, 349, 347, 414, 225, 197, + 220, 314, 782, 765, 69, 242, 241, 243, 513, 514, + 515, 516, 322, 323, 451, 565, 745, 746, 216, 204, + 438, 190, 25, 573, 298, 549, 464, 377, 51, 378, + 324, 342, 350, 372, 237, 239, 305, 310, 365, 420, + 631, 521, 522, 309, 479, 557, 558, 346, 571, 200, + 302, 331, 297, 574, 761, 747, 191, 781, 466, 325, + 184, 339, 568, 763, 577, 68, 166, 196, 187, 737, + 738, 288, 698, 181, 307, 312, 715, 762, 326, 327, + 328, 352, 351, 343, 188, 218, 304, 224, 207, 195, + 219, 182, 306, 576, 167, 711, 436, 500, 215, 212, + 308, 480, 281, 716, 572, 551, 185, 504, 169, 210, + 354, 705, 706, 707, 710, 452, 408, 355, 356, 208, + 295, 542, 543, 359, 510, 395, 484, 520, 491, 485, + 259, 260, 363, 554, 556, 229, 708, 379, 380, 381, + 546, 382, 384, 385, 390, 456, 60, 62, 102, 105, + 104, 768, 769, 67, 32, 442, 445, 482, 783, 486, + 397, 712, 629, 394, 398, 399, 446, 28, 502, 468, + 506, 505, 52, 53, 54, 57, 58, 59, 61, 63, + 64, 55, 618, 461, 475, 579, 48, 50, 471, 472, + 30, 448, 501, 524, 393, 503, 535, 49, 533, 534, + 555, 29, 450, 449, 66, 47, 509, 511, 512, 357, + 391, 459, 726, 580, 454, 470, 474, 455, 396, 444, + 476, 71, 467, 727, 462, 460, 777, 778, 392, 633, + 634, 404, 661, 439, 518, 615, 614, 613, 612, 611, + 610, 609, 608, 360, 361, 362, 487, 488, 489, 499, + 492, 493, 494, 495, 496, 497, 498, 538, 539, 728, + 729, 559, 561, 562, 626, 563, 560, 276, 772, 771, + 440, 441, 279, 731, 732, 103, 733, 735, 734, 31, + 736, 759, 756, 757, 758, 636, 564, 623, 739, 625, + 624, 683, 684, 685, 686, 687, -554, -552, -470, 621, + 317, 764, 717, 718, 463, 620, 622, 457, 436, 767, + 770, 58, 461, 299, 360, 361, 362, 536, 434, -318, + -470, 278, 771, -97, -18, -17, -9, -627, -104, -241, + -242, -753, 265, 267, 472, -333, 278, -470, -479, 26, + 517, -118, 518, 273, 274, 90, 81, -470, -10, -132, + -8, -143, -95, -236, -688, 523, -477, -470, 360, 360, + 626, -477, 278, 278, 741, 522, 563, 626, 742, 749, + 760, 558, 447, 104, 105, 737, 738, 30, 278, 458, + 305, 556, 554, 555, 559, 560, 561, 562, -76, -639, + -615, 551, 642, 550, -483, 543, 549, 541, 553, 544, + 435, 385, 382, 635, 384, 389, 268, 731, 617, -458, + 484, 520, 579, 580, 459, 521, 566, 568, 545, 115, + 214, 211, -472, 309, 498, -470, -616, 284, -558, -529, + 310, -557, -531, -560, -532, 35, 268, 270, 269, 635, + 306, 18, 461, 280, 16, 15, 462, 292, 28, 29, + 31, 17, 463, 465, 32, 466, 469, 470, 471, 45, + 475, 476, 299, 93, 101, 96, 683, 684, 685, 686, + 687, 317, 278, -314, -470, -505, -497, 122, -500, -492, + -493, -495, -448, -660, -490, 90, 151, 152, 159, 123, + 161, 785, -494, -597, 39, 125, 641, 645, 682, 590, + -440, -441, -442, -443, -444, -445, 627, -470, -661, -659, + 96, 106, 108, 112, 113, 111, 109, 174, 205, 110, + 97, 175, -242, 93, -682, 651, 657, -464, 674, 699, + 700, 701, 702, 673, 65, -623, -637, 277, -635, 173, + 211, 295, 207, 16, 157, 510, 208, 690, 691, 692, + 648, 670, 592, 593, 697, 652, 775, 776, 662, 677, + 643, 644, 646, 638, 639, 640, 642, 653, 655, 669, + -638, 665, 675, 676, 661, 693, 694, 756, 695, 696, + 678, 679, 680, 689, 688, 681, 683, 684, 685, 686, + 687, 735, 95, 94, 668, 667, 654, 649, 650, 656, + 637, 647, 658, 666, 671, 672, 445, 115, 446, 447, + 582, 437, 84, 448, 284, 517, 74, 449, 450, 451, + 452, 453, 589, 454, 781, 75, 455, 444, 299, 500, + 456, 210, 229, 595, 594, 596, 586, 583, 581, 584, + 585, 587, 588, 659, 660, 664, -239, -241, -688, -242, + -168, -170, 703, -738, -278, -739, -430, 25, -740, 6, + 7, 8, 9, 175, -729, 519, 631, 96, 582, 278, + 353, 434, 19, 741, 389, 619, 741, 389, 619, 367, + 185, 182, -544, 185, 121, 191, 190, 282, 185, -544, + -470, 188, 741, 187, 737, 742, 749, 626, 363, -519, + -215, 434, 500, 382, 102, 309, -523, -520, -617, 357, + 353, 279, 118, -216, 289, 430, 288, 116, 582, 277, + 473, 348, 60, 62, 35, 48, -262, 51, 283, 42, + -333, -690, 616, -689, -470, -698, -699, 265, 266, 267, + 42, 742, 749, 279, 281, 278, 737, 626, 309, 434, + 582, 500, 102, 382, 278, -698, 760, 182, 566, 568, + 519, 309, 498, 44, -551, 510, -550, -552, 567, 578, + 94, 95, 565, -458, 115, 542, 542, -738, -430, -739, + -273, -430, -272, 11, -238, -242, -245, -249, -688, -251, + 65, 619, 741, 626, 742, 749, 279, 434, 500, 309, + 280, -617, 281, 582, 277, 360, 458, 305, 382, 389, + 102, 187, 737, -255, -256, -257, 261, 262, 263, 73, + 266, 264, 70, 35, 36, 37, -333, -1, 129, 784, + -497, -497, -6, 787, -6, -497, -470, -470, 177, -340, + -344, -341, -343, -342, 773, -346, -345, 211, 212, 173, + 215, 217, 222, 218, 219, 220, 221, 223, 224, 225, + 226, 227, 230, 231, 232, 233, 234, 235, 228, 34, + 229, 295, 207, 208, 209, 210, -349, 194, 213, 629, + 254, 195, 255, 196, 256, 197, 257, 171, 172, 258, + 198, 201, 202, 203, 204, 200, 236, 237, 238, 239, + 240, 241, 242, 243, 245, 244, 246, 247, 248, 249, + 250, 251, 252, 253, 176, -298, 96, 35, 90, 176, + -333, 96, -738, -272, -273, -278, -262, 19, -333, -324, + 176, 785, -446, -470, 519, 132, -118, 81, -118, 518, + 81, -118, 518, 273, -691, -692, -693, -695, 273, 518, + 517, 274, 344, -141, 176, 317, 19, -477, -477, -470, + 87, -299, -300, -333, -335, -333, -722, -721, 35, 626, + -303, 26, 564, 744, 746, 542, 752, 761, 762, 763, + 86, -482, 140, 144, -482, -426, 20, -426, 26, 26, + 307, 307, 307, -482, 347, -750, -751, 19, 142, -480, + -751, -480, -480, -482, -752, 280, 552, 46, 308, 307, + -275, -276, 24, 90, -275, 546, 542, -580, 547, 548, + -484, -751, -483, -482, -482, -483, -482, -482, 388, -482, + 35, 383, 384, 278, 281, 582, 382, 732, -750, -750, + 34, 34, -531, 309, -558, -529, 39, 86, 177, 282, + 177, 86, 90, 459, 434, 500, 460, 582, 278, 473, + 281, 309, 474, 434, 500, 278, 281, 582, 309, 434, + 278, 281, 500, 309, 474, 434, 541, 542, 281, 30, + 464, 467, 468, 542, -643, 578, -333, 177, 121, 118, + 120, -497, 139, -512, 132, 133, 134, 135, 136, 137, + 138, 146, 145, 158, 151, 152, 153, 154, 155, 156, + 157, 147, 148, 149, 150, 142, 122, 161, 782, 140, + 144, 141, 124, 119, 164, 163, -242, -497, -505, 65, + -495, -495, -495, -495, -495, -470, -597, -502, -497, 90, + 90, 90, 90, 90, 176, 109, 96, 90, -497, 90, + 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, + 90, 90, -636, 90, 90, -509, -510, 90, 90, -490, + -446, 90, 96, 96, 90, 90, 90, 96, 90, 90, + 90, -510, -510, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, - 90, 90, 90, 90, 90, 90, 90, 90, 90, -735, - -241, -273, 177, 90, -272, -273, -279, 23, -278, -243, - -242, -247, -251, -494, -738, 723, 35, 36, 35, 36, - 35, 36, 35, 36, 90, 106, 764, 259, -288, -467, - -289, -467, -176, 19, 781, -467, 733, -718, 383, 622, - 622, 383, 622, 268, 18, 371, 58, 372, 567, 14, - 189, 190, 191, -467, 188, 282, -467, -514, 284, -514, - -514, 739, 515, -514, -313, -467, 305, 458, 281, 281, - -216, -514, 19, -514, -514, -514, -514, 280, -514, 278, - 278, 278, -514, 585, 132, 132, 63, -626, -629, -625, - -628, 42, 126, -629, -293, 301, -262, -262, -330, 177, - -687, -284, 90, -697, 193, -265, -264, -259, -261, -426, - -428, -263, 90, -330, -242, -467, 739, 515, -613, -613, - -330, -613, -467, 284, -529, -613, 613, -456, -467, -613, - -613, -613, -409, -410, -330, -698, 283, 759, -732, -731, - 565, -734, 567, 182, -549, 182, -549, 93, -528, 309, - 309, 177, 132, 26, -550, 132, 143, -549, -549, -550, - -550, -378, 44, -466, 173, -467, 96, -378, 44, -729, - -728, -330, -273, -243, -242, -247, -251, -277, -276, -278, - -246, -243, -247, -242, 65, 23, 91, 91, -273, -273, - -427, -168, 622, -613, -613, 739, 515, -613, -613, -613, - -613, -613, 278, -613, -613, -613, -474, -613, -315, 284, - -613, 383, -613, -613, -613, -252, -254, 153, -494, -467, - -257, -3, -180, -179, 126, 127, 129, 717, 453, 716, - 720, 713, -549, 44, -607, 167, 166, 90, -601, -603, - 90, -602, 90, -602, -602, -602, 212, 215, -601, -601, - -602, -602, -602, -602, -602, -602, 90, 90, -604, 90, - -604, -604, -601, -605, 206, 90, -605, -606, 90, -606, - -605, -467, -581, 14, -500, -502, -467, 282, 42, -273, - -171, -278, 42, -276, -279, -285, 301, 102, -322, 744, - -467, 208, 187, 732, 38, 176, 106, 96, -141, -118, - 81, -141, -118, -118, 91, 177, -691, 112, 113, -693, - 96, 227, 218, -467, -139, 96, -656, -7, -13, -117, - -8, -143, -10, -12, -55, -95, 34, -145, -135, -230, - -236, -471, 292, 616, 618, -659, -657, -301, 177, 432, - 90, -302, 177, 540, 26, -467, -275, 627, -157, 26, - -479, 19, 142, -479, -479, 19, 142, -479, 309, -423, - -492, -492, -494, 434, 578, 278, -530, 309, -466, -747, - -479, -479, -457, -456, -481, -476, -481, -481, -423, -477, - -479, -479, -494, 153, -481, -477, -423, -467, 538, -423, - -423, -577, -456, -479, 96, -478, -467, -478, -514, -456, - -457, -457, 35, 505, 86, 19, -556, 278, 578, 458, - 305, 281, 434, -554, -535, -532, -530, -528, -531, -530, - -559, -443, 538, -172, 521, 520, 359, 19, -494, -494, - -494, -494, 111, 122, 161, 408, 112, 113, -489, -510, - 35, 355, 356, -490, -490, -490, -490, -490, -490, -490, - -490, -490, -490, -490, -490, -492, -492, -498, -508, -594, - 90, 142, 140, 144, 141, 124, 142, 140, 144, 141, - 124, -753, 27, -492, -492, -490, -490, -492, -335, -337, - 166, 167, -380, -466, 173, 91, 177, -494, -684, -683, - 126, -494, -494, -494, -494, -521, -523, -443, 90, -467, - -490, -680, -681, 593, 594, 595, 596, 597, 598, 599, - 600, 601, 602, 603, 449, 444, 450, 448, 437, 456, - 451, 452, 210, 610, 611, 604, 605, 606, 607, 608, - 609, -500, -500, -494, -680, -490, -500, -436, 36, 35, - -502, -502, -502, 91, -494, -694, 406, 405, 407, -444, - -467, -500, 91, 91, 91, 106, -502, -502, -500, -490, - -500, -500, -500, -500, -681, -681, -682, 295, 207, 209, - 208, -436, -494, -494, -436, -436, -436, 153, -502, -502, - -436, -436, -436, -436, 153, -436, -436, -436, -436, -436, - -436, -436, -436, 91, -436, -436, -436, -436, -436, 91, - 91, 91, 91, -494, 91, -494, -494, -494, -494, -494, - 153, -502, -273, -735, -277, -170, -638, -637, -494, 44, - -171, -278, -276, -171, 674, -279, -739, 725, 90, -443, - -727, 96, 96, 781, -176, 176, 19, 278, -176, 176, - 733, 187, -176, 578, 19, -467, 96, 106, -467, 96, - 106, 278, 578, 278, 578, -330, -330, -330, 568, 569, - 186, 190, 189, -467, 188, -467, -467, 122, 161, -467, - -467, -514, -514, -467, 38, -315, -304, -467, -514, -514, - -702, -467, 97, 96, -536, -533, -530, -467, -467, -526, - -467, -456, -514, -514, -514, -330, -389, 57, 58, 59, - -530, -217, 60, 61, -236, -625, -627, -628, 128, -330, - -494, -627, -623, 65, -236, 90, 34, 90, -293, -293, - -686, 38, -283, -467, -698, 87, -429, -430, -431, -432, - -434, -435, -433, 72, 76, 78, 82, 73, 74, 75, - 541, 79, 84, 773, 77, -465, -470, 38, -467, 96, - -465, -242, -267, -266, -265, 781, -465, 90, -467, -467, - -330, -330, -403, -411, -408, -404, -412, 301, 269, 275, - 442, 443, 271, 11, 270, -420, 348, -515, 586, -384, - -385, 81, 45, -387, 299, 482, 478, 779, 311, 315, - 100, 316, 515, 317, 280, 319, 320, 321, 336, 338, - 291, 322, 323, 324, 506, 325, 181, 337, 326, 327, - 328, 460, -379, 6, 390, 44, 55, 56, 530, 529, - 629, 14, 312, -467, 485, 618, 34, 39, 271, 275, - 270, -702, -700, 34, -467, 34, -536, -530, -467, -467, - 177, 282, -265, -267, -549, 177, 566, 568, 569, -732, - -550, -732, -550, 282, 35, 505, -553, 505, 35, -526, - -547, 562, 564, -542, 96, 506, -531, -552, 86, 173, - -637, -550, -550, -552, -552, 163, 177, -730, 567, 568, - 265, -277, -168, -271, -270, -494, -277, -277, -250, -243, - -251, -247, -242, 106, -720, -718, -467, 622, -467, -613, - -613, -332, -702, -535, -526, -467, -613, -332, -469, -469, - -304, 39, -467, -613, -467, -467, -467, -171, 177, -468, - -467, 38, 96, 176, -181, -179, 128, -494, -6, 716, - -494, -6, -6, -494, -6, -494, -611, 169, -337, 106, - 106, -446, 96, -446, 106, -605, 106, 106, 632, 91, - 96, 96, -277, -279, 700, -660, -656, -279, -171, -630, - 65, -236, 90, 34, 90, -613, 187, 208, 743, -321, - 132, 132, 132, 27, -467, 26, -141, -118, -689, 176, - -548, 514, 177, -300, -283, -330, 631, 106, -159, 24, - -467, -423, -467, 739, -423, -423, -467, 739, 741, -423, - -529, -467, -467, -467, -527, 87, -467, 784, 94, -457, - -423, -423, -481, -423, -423, 177, 25, 91, -479, -481, - -481, -321, -477, -321, 176, -321, -456, -556, -534, -531, - -558, 153, -467, -543, 177, 14, 282, -715, -714, 497, - 91, 177, -641, 283, 585, 96, 781, 513, 259, 260, - 96, 111, 408, 112, 113, 111, 408, 112, 113, -594, - -502, -498, -492, -492, -490, -490, -498, -492, -492, -490, - -490, 90, -496, 296, -496, 121, -345, 172, 171, -345, - -494, 782, -493, -683, 128, -494, 38, 177, 38, 177, - 87, 177, 91, -601, -494, 176, 177, 91, 91, 19, - 19, 142, 91, -494, 91, 91, 91, 91, 19, 19, - -494, 91, 176, 91, 91, 91, 91, 87, 91, 177, - 91, 91, 91, 91, 177, 177, 177, -502, 91, 91, - -502, -494, -502, 91, 91, 91, -494, -494, -494, -502, - 91, -494, -494, -494, -494, -494, -494, -494, -494, 770, - -494, -494, -502, -494, -494, -566, 533, -566, -566, -566, - 91, -566, 91, 177, 91, 177, 91, 91, 177, 177, - 177, -277, -273, 177, 91, -279, -279, 14, -171, -740, - 724, 90, 106, 177, 765, -450, -449, 96, -177, 282, - -467, 733, -467, -177, -467, -467, 132, -177, -467, 733, - 96, 96, -330, -456, -330, -456, 624, 42, 42, 187, - 191, 191, 190, -467, 96, 39, 39, 26, 26, 346, - -467, -467, -152, 619, -314, 90, 90, 176, -330, -330, - -704, 483, -467, -716, 177, 44, -714, 578, -213, 359, - -518, 87, -226, 366, 19, -330, -330, -330, -344, 38, - -236, -626, 65, 90, 127, -236, -540, -539, 86, -631, - -503, -678, 699, -292, 91, -280, -467, -676, -677, -280, - -286, -294, -467, -623, -623, 90, 91, 177, 19, -258, - -331, -467, -265, -264, -264, -264, -264, -264, -264, -264, - 72, 83, 72, -281, 90, 72, 77, 72, 77, 72, - 77, 72, -431, 72, 72, 74, 83, -284, -470, 177, - 91, 93, -725, -724, -723, -721, 80, 283, 81, -500, - 34, 34, 56, 55, -600, 38, -284, 177, 23, 411, - -464, -406, 286, 280, 344, 287, 301, -329, -464, -326, - -328, -484, -327, 289, 430, -672, 287, 285, 116, 290, - 344, 117, 280, -464, -464, 286, -388, 282, 38, 23, - 301, -405, 280, 117, -467, 286, 290, 287, 285, -463, - 132, -455, 163, 282, 46, 460, -463, 630, 301, -463, - -463, -463, -463, -463, -463, -463, -463, 318, 318, -463, - -463, -463, -463, -463, -463, -463, -463, -463, -463, -463, - 182, -463, -463, -463, -463, -463, -463, 90, 313, 314, - 346, 619, 126, 632, 621, -529, 282, 553, 553, -705, - 483, 34, 440, 440, 441, -716, 436, 45, 34, -227, - 434, -410, -407, -478, 34, 34, -550, -731, -733, 570, - 567, 573, -552, -552, 106, 282, 90, 132, -552, -552, - 44, -466, -728, 574, 568, -279, 177, -268, 40, 41, - 91, 91, -311, 735, -467, -467, 86, -332, -616, 339, - 340, 90, -315, 96, 19, -311, -422, -421, 19, -254, - -467, 96, -467, 153, 129, -6, 127, -185, -184, -183, - 130, 713, 720, 129, 129, 129, 91, 91, 91, 91, - 177, 91, 91, 91, 177, 91, 177, 106, 761, -644, - 543, -279, -171, 96, 177, -171, -253, 768, -631, -292, - 91, -676, -286, -467, 96, 106, 96, 38, -225, 93, - 101, 96, -322, -494, -467, 96, -494, 208, 176, 515, - -467, -657, 91, 106, -479, -467, -479, 176, -467, -222, - -224, -467, 96, -467, -526, -466, -479, -423, -494, -494, - 547, 546, -423, -479, -479, -481, -467, -321, -558, 177, - 282, 176, 176, -532, 463, -534, 23, 14, -443, 42, - -450, 132, 781, -467, 91, -496, -496, 121, -496, -496, - 121, -492, -492, -489, 91, 129, -494, 127, -335, -494, - -335, -336, -342, 227, 173, 211, 295, 210, 209, 207, - 166, 167, -378, -523, 624, -268, 91, -467, -502, -494, - -494, -490, 91, -494, -494, 19, -467, -378, -490, -494, - -494, -494, -273, -274, 770, -274, -273, 91, 91, -565, - -566, -565, -274, 91, 91, 91, 91, -565, 91, 91, - 91, 91, 91, 91, 91, 91, 21, 91, 91, 91, - 91, 177, -467, 90, -566, -751, 777, -494, -751, -494, - -751, -751, -494, 106, 108, 106, 108, -279, -277, -637, - -171, -171, 464, 90, 301, 177, 106, 96, 782, 177, - 132, 434, -467, 19, 176, 96, -467, 96, 19, 278, - -467, 19, 19, -330, -330, -330, 191, 96, -717, 353, - 434, 578, 278, 434, 353, 578, 278, -582, 106, -354, - 14, -358, 14, -153, 126, 96, 471, -316, -317, -318, - -319, -320, 142, 178, 179, -305, -306, -307, -308, -337, - -443, 769, 212, 215, 217, 218, 219, 220, 221, 223, - 224, 225, 226, 227, 230, 231, 232, 233, 234, 235, - 228, 229, 295, 207, 208, 209, 210, 194, 213, 625, - 195, 196, 197, 171, 172, 198, 201, 202, 203, 204, - 200, 236, 237, 238, 239, 240, 241, 242, 243, 245, - 244, 246, 247, 248, 249, 250, 251, 252, 253, -467, - -284, -284, -707, 545, 485, 495, -463, 382, -486, -485, - 436, 45, -618, 506, 491, 492, -533, 309, -456, 153, - -713, 103, 132, 86, 394, 398, 400, 402, 401, 399, - 395, 396, 397, -512, -513, -511, -515, -456, 96, -700, - 90, -237, -221, -220, 140, -226, 366, 19, -236, 65, - 283, 585, 90, -353, 321, 90, -595, 379, -337, 90, - -292, -626, -253, 43, 177, 90, 91, 177, 65, 176, - 177, 132, 91, 177, 91, 177, 132, -253, -253, -539, - -1, -467, -330, -258, -467, 19, 177, -699, 176, 86, - -296, -295, 86, 87, -296, -296, -295, -295, 72, -282, - 96, 72, 72, 72, 72, 77, -266, -265, -723, -722, - 26, -672, -672, -672, 91, -355, 90, -356, -223, -467, - 96, -359, 90, -360, -361, -467, 96, -595, -337, 38, - -404, 275, 269, 270, -562, 346, 347, -563, -584, 349, - -467, -443, -467, -467, -467, -586, 90, -334, -443, -327, - -671, -672, -671, -514, -467, 117, -671, 117, 90, -334, - -443, -443, -407, -415, -414, -443, -418, 35, -419, -467, - -467, -467, -467, 117, -467, 117, -383, 44, 52, 53, - 54, -463, -463, 214, -386, 44, 505, 507, 508, -418, - 106, 106, 106, 106, 106, 96, 96, 96, -463, -463, - 106, 96, -470, 96, -673, 190, 48, 49, 106, 106, - 106, 106, 44, 96, -391, 44, 329, 333, 330, 331, - 332, 96, 106, 44, 106, 44, 106, 44, -467, 90, - -674, -675, 96, -582, 96, 90, 106, 96, 271, -529, - 96, 86, -707, -463, 440, -549, 132, 132, -486, -709, - 100, 486, -709, -712, 359, -229, 578, 35, -287, 275, - 270, -700, -538, -537, -443, -538, -552, 567, 571, 572, - -534, -482, 96, -542, -171, -270, -269, 47, -330, -616, - -305, -467, -423, -267, 176, 125, -6, -181, -183, -494, - -6, -494, 716, 453, 717, 96, 106, 106, 569, -652, - 527, 522, 524, -171, -653, 513, -656, -252, 91, 65, - 91, 27, 782, 782, 782, 782, 96, -467, 106, -423, - -479, -423, -222, -479, -479, 142, -423, -321, -423, -423, - -479, 19, -531, -526, 153, 153, -467, 464, -543, 96, - 484, 96, 278, 782, 96, -450, -489, -489, 91, -494, - 91, 38, 91, 91, -602, -602, -601, -604, -601, -345, - -345, 91, 90, -268, 91, 91, 26, 91, 91, -565, - 91, 91, -494, 176, 91, 91, 177, 177, 91, -621, - 587, -565, 21, -565, -622, 659, -565, -565, -565, -565, - -565, -565, -565, -565, -565, -565, -565, -565, -565, -565, - -565, 90, -565, -565, -565, -565, -494, -567, -571, -467, - -566, 47, 91, 177, -566, 91, 177, -566, -566, 91, - 528, 730, 730, 528, 730, 730, -171, -279, -502, -741, - 67, 722, 66, 505, 111, 349, 91, -680, 177, -458, - 354, -458, -449, 96, -467, 96, 733, -467, 782, 782, - 733, -467, 96, -330, -456, -290, 544, -233, 126, -234, - 124, 46, 96, -467, 19, -467, -467, 346, -467, 346, - -467, -467, 96, 90, 90, -158, 632, 90, -155, 620, - 96, 91, 177, -443, 91, 177, -337, 38, 38, -708, - 100, -703, 96, -467, 97, -467, -709, 175, 438, 44, - 487, 488, 503, 433, 106, 106, 493, -701, -467, -228, - 278, 434, -228, -711, 56, 132, 96, -330, -511, -455, - 163, 320, -321, -467, 382, -323, -324, -325, -334, -326, - -328, -237, 38, -330, -330, 96, -168, -323, -463, -323, - -596, 381, 23, 106, 152, -502, 91, 117, -503, -639, - -638, 65, -236, -280, -631, -467, -677, -637, -467, -467, - -494, -253, 91, -285, 87, 176, -267, -331, -467, 153, - -494, -494, 90, -260, 774, 91, 72, 782, 90, 72, - 11, 21, 177, -355, 132, 177, -359, 132, -596, -236, - -579, 350, 90, -577, 90, -577, 117, 395, 398, 400, - 402, 401, 430, 34, 270, -416, 398, 392, 393, -544, - 345, 122, 161, -544, -587, -585, 301, -413, 48, 50, - -337, -669, -467, -669, -667, -669, -467, -667, -667, -514, - -494, -413, -334, 282, 177, -268, 177, -467, -378, -378, - 34, 96, 96, -332, 91, 177, 132, 96, -155, -154, - -494, -238, -242, 282, 86, 278, -708, -703, 132, -550, - 96, 96, -709, 96, 96, -713, 132, -333, 278, -456, - 177, -287, -287, -423, 19, 177, 132, -423, 91, -297, - 26, 45, 48, 49, 91, 382, -425, 21, 153, 129, - 127, 129, 129, -467, 91, 91, -608, 701, -648, -650, - 522, 23, 23, -297, -654, 706, 96, 65, -236, -631, - 208, 96, 782, -423, -479, -423, -423, 739, -423, -526, - -543, 506, -330, 177, 782, -335, -397, 96, 97, -467, - -494, 91, -467, -494, -494, 91, 96, 90, 91, 96, - -272, 91, 91, -505, -504, 301, -751, -494, -751, -494, - -752, 19, 91, 177, 91, 91, 91, 177, 91, 91, - -297, -171, 91, 177, 91, -680, -459, 208, 96, -459, - -467, -467, 19, -468, -231, 282, -321, -235, 377, 90, - 373, -233, 187, 90, 96, -467, 19, -467, -582, 346, - -582, 346, 278, -467, -311, -355, -359, -156, 621, 106, - -154, 96, -519, 626, -318, -337, 276, -307, 44, -237, - -237, 96, -706, 497, -583, 387, 106, 44, 106, 175, - 489, -619, -214, 100, -332, 35, -287, -214, -710, 100, - 132, 781, 90, -463, -463, -463, -231, 382, -467, 91, - 177, -237, -232, 54, -467, -273, 91, 738, 91, -597, - 300, 106, 152, 106, 152, 106, 91, 65, 17, 283, - 91, -631, 176, 132, -465, -267, -467, -423, -699, 176, - -105, -106, 126, -283, -491, 175, -668, -467, 285, 23, - 23, -356, 91, -224, -360, 91, -362, -467, 96, -597, - -564, 351, 106, -490, 90, -490, 90, -578, 348, 90, - -524, -524, -524, -524, -524, 44, -416, 44, -417, 417, - 345, 345, 91, 177, -467, -443, -350, -349, -347, 111, - 122, 161, 44, 478, -348, 100, 746, 747, 163, 334, - 337, 336, 312, 335, -402, -483, 86, 694, 481, 392, - 393, -515, 709, 38, 285, 116, 117, 465, -484, 90, - 90, 90, 87, 354, 90, 90, -669, 91, -413, -443, - -414, -467, 163, -378, 91, -675, 96, 91, -529, 278, - -467, -706, 96, -552, -711, 96, -214, -332, -700, -273, - -267, -537, -637, -253, 17, 68, 142, -467, -424, 22, - 23, 284, -6, 717, 453, -393, 702, 96, 23, 96, - -646, 96, -644, 96, -631, 96, -423, -467, -174, -390, - -455, 317, 91, -396, 142, 14, 91, 91, 91, -274, - -272, -565, 91, -565, -273, 23, -566, 91, -566, 91, - -751, 48, 96, 96, -297, -494, 91, 96, 96, 96, - 733, 434, -231, 38, 471, 24, 638, 378, -283, 374, - 375, 376, -467, 96, -502, -238, 781, 382, -467, 19, - 96, -582, 96, -582, -467, 346, 91, 91, 38, 96, - 91, 96, 96, -309, -337, -487, -218, 14, -218, 23, - 14, 175, 437, 44, 106, 44, 490, 96, -228, 132, - 112, 113, -451, -452, 96, -521, -378, -380, 96, -467, - -351, -237, -221, -220, -352, -420, -515, -325, -231, 30, - 382, -277, -173, -353, -357, -363, -369, -370, -172, 321, - -354, -351, -598, 181, 106, 152, 106, 106, 90, -538, - -467, -494, -423, -423, -253, 768, -106, -108, 122, 161, - -467, 91, 91, 177, -598, -586, 23, 91, -559, 91, - -559, 90, 132, -490, -525, 291, 409, 410, 100, 14, - 392, 393, 414, 413, 412, 418, 419, 423, 424, 420, - 422, 421, 425, 429, 426, 427, 428, 431, 415, 416, - 58, 417, 437, 448, 444, -490, -524, 132, -585, -588, - 65, -347, 111, 111, -490, 96, 96, 96, -380, -381, - 44, 333, 329, 132, 132, -382, 44, 313, 314, -392, - 90, 344, 17, 106, 214, 710, 90, 117, 117, -330, - -521, -521, -521, -670, 394, 395, 396, 404, 398, 399, - 397, 400, 401, 402, 403, -670, -521, -521, 90, -545, - -544, -463, 163, -467, 176, -710, -275, -423, 68, -569, - 775, -494, -502, 752, 754, 755, -467, -467, -447, 703, - 106, 96, 524, -652, -479, -609, 704, -642, -577, -378, - 132, 91, 79, 625, 403, 627, -565, 91, -565, -573, - -572, -576, 528, 346, 536, -502, -751, -751, -566, 91, - 91, 91, -467, -467, -330, 96, 106, 91, 121, 121, - 91, 91, -454, -453, 96, -467, 382, -467, -311, 96, - -311, 96, 346, -582, -2, 626, -219, 64, 574, 96, - 97, 484, 96, 97, 106, 437, -214, 96, 782, 177, - 132, 91, -583, -560, 301, -237, 38, 177, -420, -455, - -467, -174, -463, -560, -615, 190, 380, 14, 106, 152, - 106, -502, -273, -253, -252, -107, 121, -108, -108, -467, - 285, -599, 190, 380, -563, 91, 91, 91, -559, 106, - 91, -463, -463, 96, 96, 391, -463, -463, -463, -463, - -463, -463, 90, 96, 96, -463, -463, -463, -463, -463, - -463, 132, 132, -463, -463, -463, 112, 113, -593, -589, - 90, -590, -420, 303, 142, 96, 96, 106, 90, -632, - 34, 38, -494, -522, 90, 91, 91, 91, 91, 91, - -521, -378, -463, 176, -467, -253, -253, 69, -570, -568, - -467, -617, 14, 753, 90, 90, 90, 23, -608, -423, - -610, 705, 96, -651, 527, -645, -643, 522, 523, 524, - 525, 96, 626, 69, 69, 628, -575, 124, 489, 493, - -488, 97, -491, 106, 108, 205, -566, -566, 96, -467, - 96, 782, 177, 132, -467, 382, -311, -311, -582, 96, - -312, -467, 344, 506, -452, 96, -524, -561, 353, 23, - -237, -420, -463, -583, 745, 749, -377, 751, 750, 62, - -561, 380, 106, 152, 106, 91, -275, 127, -494, -107, - -107, 380, -580, 352, 91, 106, 106, -467, 106, 106, - 106, 106, 106, 106, -559, 106, 106, 106, 106, 106, - 106, 112, 113, 112, 113, 106, 106, 106, -593, -590, - -592, -591, 351, 304, 90, 91, -494, -506, -463, 90, - 91, -395, -394, 623, -521, -524, 87, -524, -524, 87, - -524, 87, -524, 87, 91, -378, -467, 177, 38, 755, - 90, -502, -502, -448, -467, -646, -393, 96, -655, 283, - -649, -650, 526, -643, 23, 524, 23, 23, -175, 177, - 69, -574, -575, 493, -488, 97, -491, 699, 534, 534, - 534, 534, -233, -234, -233, -234, -311, -453, 96, -467, - 96, -311, -310, 38, 529, 464, 23, -562, -378, -358, - -364, 14, -374, 14, -371, 14, 106, -253, 17, 18, - 127, 127, 106, 91, 91, 177, -467, 300, 90, -501, - -495, -494, 300, 91, -378, -494, -546, 712, 711, -401, - -399, -400, 86, 541, 342, 343, 91, -670, -670, -670, - -670, -402, -568, 90, -169, 90, 91, 91, 91, 177, - -447, -663, 90, 106, -648, -647, -649, 23, -646, 23, - -646, -646, 531, 14, 121, 535, 535, 535, 535, -233, - -233, -311, 96, -443, 90, -579, 90, 90, 90, 34, - 16, 16, -591, -589, -501, 91, 177, -544, 91, -400, - 86, -399, 86, 18, 17, -524, -524, -524, -524, -567, - 91, 177, -500, -467, -666, 34, 91, -662, -661, -444, - -656, 527, 528, 96, -646, 132, 627, -574, -744, -743, - 729, -559, -564, -365, -366, -367, -467, 96, -375, -376, - -367, -372, -373, -367, -538, 90, 65, 90, 65, 91, - -495, -546, -398, 339, 340, 34, 190, -398, 91, 90, - 91, -665, -664, -445, 91, 177, 96, 628, 96, 91, - -586, 91, 177, 132, 91, 177, 132, 91, 177, 132, - -291, -467, 90, -291, 90, 111, 44, 341, -500, 177, - 132, -661, -366, -368, -467, 96, -376, -368, -373, -368, - 91, 177, 176, -502, 91, -502, 91, -664, 44, -494, - 65, -467, -467, 91, 65, 91, 90, 176, 90, -502, - -467, -502, 91, 91, + 90, 90, 90, -738, -241, -273, 177, 90, -272, -273, + -279, 23, -278, -243, -242, -247, -251, -497, -741, 727, + 35, 36, 35, 36, 35, 36, 35, 36, 90, 106, + 768, 259, -288, -470, -289, -470, -176, 19, 785, -470, + 737, -721, 383, 626, 626, 383, 626, 268, 18, 371, + 58, 372, 571, 14, 189, 190, 191, -470, 188, 282, + -470, -517, 284, -517, -517, 743, 519, -517, -313, -470, + 305, 458, 281, 281, -216, -517, 19, -517, -517, -517, + -517, 280, -517, 278, 278, 278, -517, 589, 132, 132, + 63, -629, -632, -628, -631, 42, 126, -632, -293, 301, + -262, -262, -333, 177, -690, -284, 90, -700, 193, -265, + -264, -259, -261, -429, -431, -263, 90, -333, -242, -470, + 743, 519, -616, -616, -333, -616, -470, 284, -532, -616, + 617, -459, -470, -616, -616, -616, -412, -413, -333, -701, + 283, 763, -735, -734, 569, -737, 571, 182, -552, 182, + -552, 93, -531, 309, 309, 177, 132, 26, -553, 132, + 143, -552, -552, -553, -553, -381, 44, -469, 173, -470, + 96, -381, 44, -732, -731, -333, -273, -243, -242, -247, + -251, -277, -276, -278, -246, -243, -247, -242, 65, 23, + 91, 91, -273, -273, -430, -168, 626, -616, -616, 743, + 519, -616, -616, -616, -616, -616, 278, -616, -616, -616, + -477, -616, -318, 284, -616, 383, -616, -616, -616, -252, + -254, 153, -497, -470, -257, -3, -180, -179, 126, 127, + 129, 721, 453, 720, 724, 717, -552, 44, -610, 167, + 166, 90, -604, -606, 90, -605, 90, -605, -605, -605, + 212, 215, -604, -604, -605, -605, -605, -605, -605, -605, + 90, 90, -607, 90, -607, -607, -604, -608, 206, 90, + -608, -609, 90, -609, -608, -470, -584, 14, -503, -505, + -470, 282, 42, -273, -171, -278, 42, -276, -279, -285, + 301, 102, -325, 748, -470, 208, 187, 736, 38, 176, + 106, 96, -141, -118, 81, -141, -118, -118, 91, 177, + -694, 112, 113, -696, 96, 227, 218, -470, -139, 96, + -659, -7, -13, -117, -8, -143, -10, -12, -55, -95, + 34, -145, -135, -230, -236, -474, 292, 620, 622, -662, + -660, -301, 177, 432, 90, -302, 177, 544, 26, -470, + -275, 631, -157, 26, -482, 19, 142, -482, -482, 19, + 142, -482, 309, -426, -495, -495, -497, 434, 582, 278, + -533, 309, -469, -750, -482, -482, -460, -459, -484, -479, + -484, -484, -426, -480, -482, -482, -497, 153, -484, -480, + -426, -470, 542, -426, -426, -580, -459, -482, 96, -481, + -470, -481, -517, -459, -460, -460, 35, 509, 86, 19, + -559, 278, 582, 458, 305, 281, 434, -557, -538, -535, + -533, -531, -534, -533, -562, -446, 542, -172, 525, 524, + 359, 19, -497, -497, -497, -497, 111, 122, 161, 408, + 112, 113, -492, -513, 35, 355, 356, -493, -493, -493, + -493, -493, -493, -493, -493, -493, -493, -493, -493, -495, + -495, -501, -511, -597, 90, 142, 140, 144, 141, 124, + 142, 140, 144, 141, 124, -756, 27, -495, -495, -493, + -493, -495, -338, -340, 166, 167, -383, -469, 173, 91, + 177, -497, -687, -686, 126, -497, -497, -497, -497, -524, + -526, -446, 90, -470, -493, -683, -684, 597, 598, 599, + 600, 601, 602, 603, 604, 605, 606, 607, 449, 444, + 450, 448, 437, 456, 451, 452, 210, 614, 615, 608, + 609, 610, 611, 612, 613, -503, -503, -497, -683, -493, + -503, -439, 36, 35, -505, -505, -505, 91, -497, -697, + 406, 405, 407, -447, -470, -503, 91, 91, 91, 106, + -505, -505, -503, -493, -503, -503, -503, -503, -684, -684, + -685, 295, 207, 209, 208, -439, -497, -497, -439, -439, + -439, 153, -505, -505, -439, -439, -439, -439, 153, -439, + -439, -439, -439, -439, -439, -439, -439, 91, -439, -439, + -439, -439, -439, 91, 91, 91, 91, -497, 91, -497, + -497, -497, -497, -497, 153, -505, -273, -738, -277, -170, + -641, -640, -497, 44, -171, -278, -276, -171, 678, -279, + -742, 729, 90, -446, -730, 96, 96, 785, -176, 176, + 19, 278, -176, 176, 737, 187, -176, 582, 19, -470, + 96, 106, -470, 96, 106, 278, 582, 278, 582, -333, + -333, -333, 572, 573, 186, 190, 189, -470, 188, -470, + -470, 122, 161, -470, -470, -517, -517, -470, 38, -318, + -304, -470, -517, -517, -705, -470, 97, 96, -539, -536, + -533, -470, -470, -529, -470, -459, -517, -517, -517, -333, + -392, 57, 58, 59, -533, -217, 60, 61, -236, -628, + -630, -631, 128, -333, -497, -630, -626, 65, -236, 90, + 34, 90, -293, -293, -689, 38, -283, -470, -701, 87, + -432, -433, -434, -435, -437, -438, -436, 72, 76, 78, + 82, 73, 74, 75, 545, 79, 84, 777, 77, -468, + -473, 38, -470, 96, -468, -242, -267, -266, -265, 785, + -468, 90, -470, -470, -333, -333, -406, -414, -411, -407, + -415, 301, 269, 275, 442, 443, 271, 11, 270, -423, + 348, -518, 590, -387, -388, 81, 45, -390, 299, 486, + 482, 783, 311, 315, 100, 316, 519, 317, 280, 319, + 320, 321, 336, 338, 291, 322, 323, 324, 510, 325, + 181, 337, 326, 327, 328, 460, -382, 6, 390, 44, + 55, 56, 534, 533, 633, 14, 312, -470, 489, 622, + 34, 39, 271, 275, 270, -705, -703, 34, -470, 34, + -539, -533, -470, -470, 177, 282, -265, -267, -552, 177, + 570, 572, 573, -735, -553, -735, -553, 282, 35, 509, + -556, 509, 35, -529, -550, 566, 568, -545, 96, 510, + -534, -555, 86, 173, -640, -553, -553, -555, -555, 163, + 177, -733, 571, 572, 265, -277, -168, -271, -270, -497, + -277, -277, -250, -243, -251, -247, -242, 106, -723, -721, + -470, 626, -470, -616, -616, -335, -705, -538, -529, -470, + -616, -335, -472, -472, -304, 39, -470, -616, -470, -470, + -470, -171, 177, -471, -470, 38, 96, 176, -181, -179, + 128, -497, -6, 720, -497, -6, -6, -497, -6, -497, + -614, 169, -340, 106, 106, -449, 96, -449, 106, -608, + 106, 106, 636, 91, 96, 96, -277, -279, 704, -663, + -659, -279, -171, -633, 65, -236, 90, 34, 90, -616, + 187, 208, 747, -324, 132, 132, 132, 27, -470, 26, + -141, -118, -692, 176, -551, 518, 177, -300, -283, -333, + 635, 106, -159, 24, -470, -426, -470, 743, -426, -426, + -470, 743, 745, -426, -532, -470, -470, -470, -530, 87, + -470, 788, 94, -460, -426, -426, -484, -426, -426, 177, + 25, 91, -482, -484, -484, -324, -480, -324, 176, -324, + -459, -559, -537, -534, -561, 153, -470, -546, 177, 14, + 282, -718, -717, 501, 91, 177, -644, 283, 589, 96, + 785, 517, 259, 260, 96, 111, 408, 112, 113, 111, + 408, 112, 113, -597, -505, -501, -495, -495, -493, -493, + -501, -495, -495, -493, -493, 90, -499, 296, -499, 121, + -348, 172, 171, -348, -497, 786, -496, -686, 128, -497, + 38, 177, 38, 177, 87, 177, 91, -604, -497, 176, + 177, 91, 91, 19, 19, 142, 91, -497, 91, 91, + 91, 91, 19, 19, -497, 91, 176, 91, 91, 91, + 91, 87, 91, 177, 91, 91, 91, 91, 177, 177, + 177, -505, 91, 91, -505, -497, -505, 91, 91, 91, + -497, -497, -497, -505, 91, -497, -497, -497, -497, -497, + -497, -497, -497, 774, -497, -497, -505, -497, -497, -569, + 537, -569, -569, -569, 91, -569, 91, 177, 91, 177, + 91, 91, 177, 177, 177, -277, -273, 177, 91, -279, + -279, 14, -171, -743, 728, 90, 106, 177, 769, -453, + -452, 96, -177, 282, -470, 737, -470, -177, -470, -470, + 132, -177, -470, 737, 96, 96, -333, -459, -333, -459, + 628, 42, 42, 187, 191, 191, 190, -470, 96, 39, + 39, 26, 26, 346, -470, -470, -152, 623, -314, 90, + 90, 176, -333, -333, -707, 487, -470, -719, 177, 44, + -717, 582, -213, 359, -521, 87, -226, 366, 19, -333, + -333, -333, -347, 38, -236, -629, 65, 90, 127, -236, + -543, -542, 86, -634, -506, -681, 703, -292, 91, -280, + -470, -679, -680, -280, -286, -294, -470, -626, -626, 90, + 91, 177, 19, -258, -334, -470, -265, -264, -264, -264, + -264, -264, -264, -264, 72, 83, 72, -281, 90, 72, + 77, 72, 77, 72, 77, 72, -434, 72, 72, 74, + 83, -284, -473, 177, 91, 93, -728, -727, -726, -724, + 80, 283, 81, -503, 34, 34, 56, 55, -603, 38, + -284, 177, 23, 411, -467, -409, 286, 280, 344, 287, + 301, -332, -467, -329, -331, -487, -330, 289, 430, -675, + 287, 285, 116, 290, 344, 117, 280, -467, -467, 286, + -391, 282, 38, 23, 301, -408, 280, 117, -470, 286, + 290, 287, 285, -466, 132, -458, 163, 282, 46, 460, + -466, 634, 301, -466, -466, -466, -466, -466, -466, -466, + -466, 318, 318, -466, -466, -466, -466, -466, -466, -466, + -466, -466, -466, -466, 182, -466, -466, -466, -466, -466, + -466, 90, 313, 314, 346, 623, 126, 636, 625, -532, + 282, 557, 557, -708, 487, 34, 440, 440, 441, -719, + 436, 45, 34, -227, 434, -413, -410, -481, 34, 34, + -553, -734, -736, 574, 571, 577, -555, -555, 106, 282, + 90, 132, -555, -555, 44, -469, -731, 578, 572, -279, + 177, -268, 40, 41, 91, 91, -311, 739, -470, -470, + 86, -335, -619, 339, 340, 90, -318, 96, 19, -311, + -425, -424, 19, -254, -470, 96, -470, 153, 129, -6, + 127, -185, -184, -183, 130, 717, 724, 129, 129, 129, + 91, 91, 91, 91, 177, 91, 91, 91, 177, 91, + 177, 106, 765, -647, 547, -279, -171, 96, 177, -171, + -253, 772, -634, -292, 91, -679, -286, -470, 96, 106, + 96, 38, -225, 93, 101, 96, -325, -497, -470, 96, + -497, 208, 176, 519, -470, -660, 91, 106, -482, -470, + -482, 176, -470, -222, -224, -470, 96, -470, -529, -469, + -482, -426, -497, -497, 551, 550, -426, -482, -482, -484, + -470, -324, -561, 177, 282, 176, 176, -535, 463, -537, + 23, 14, -446, 42, -453, 132, 785, -470, 91, -499, + -499, 121, -499, -499, 121, -495, -495, -492, 91, 129, + -497, 127, -338, -497, -338, -339, -345, 227, 173, 211, + 295, 210, 209, 207, 166, 167, -381, -526, 628, -268, + 91, -470, -505, -497, -497, -493, 91, -497, -497, 19, + -470, -381, -493, -497, -497, -497, -273, -274, 774, -274, + -273, 91, 91, -568, -569, -568, -274, 91, 91, 91, + 91, -568, 91, 91, 91, 91, 91, 91, 91, 91, + 21, 91, 91, 91, 91, 177, -470, 90, -569, -754, + 781, -497, -754, -497, -754, -754, -497, 106, 108, 106, + 108, -279, -277, -640, -171, -171, 464, 90, 301, 177, + 106, 96, 786, 177, 132, 434, -470, 19, 176, 96, + -470, 96, 19, 278, -470, 19, 19, -333, -333, -333, + 191, 96, -720, 353, 434, 582, 278, 434, 353, 582, + 278, -585, 106, -357, 14, -361, 14, -153, 126, 96, + 471, -319, -320, -321, -322, -323, 142, 178, 179, -305, + -306, -307, -308, -340, -446, 773, 212, 215, 217, 218, + 219, 220, 221, 223, 224, 225, 226, 227, 230, 231, + 232, 233, 234, 235, 228, 229, 295, 207, 208, 209, + 210, 194, 213, 629, 195, 196, 197, 171, 172, 198, + 201, 202, 203, 204, 200, 236, 237, 238, 239, 240, + 241, 242, 243, 245, 244, 246, 247, 248, 249, 250, + 251, 252, 253, -470, -284, -284, -710, 549, 489, 499, + -466, 382, -489, -488, 436, 45, -621, 510, 495, 496, + -536, 309, -459, 153, -716, 103, 132, 86, 394, 398, + 400, 402, 401, 399, 395, 396, 397, -515, -516, -514, + -518, -459, 96, -703, 90, -237, -221, -220, 140, -226, + 366, 19, -236, 65, 283, 589, 90, -356, 321, 90, + -598, 379, -340, 90, -292, -629, -253, 43, 177, 90, + 91, 177, 65, 176, 177, 132, 91, 177, 91, 177, + 132, -253, -253, -542, -1, -470, -333, -258, -470, 19, + 177, -702, 176, 86, -296, -295, 86, 87, -296, -296, + -295, -295, 72, -282, 96, 72, 72, 72, 72, 77, + -266, -265, -726, -725, 26, -675, -675, -675, 91, -358, + 90, -359, -223, -470, 96, -362, 90, -363, -364, -470, + 96, -598, -340, 38, -407, 275, 269, 270, -565, 346, + 347, -566, -587, 349, -470, -446, -470, -470, -470, -589, + 90, -337, -446, -330, -674, -675, -674, -517, -470, 117, + -674, 117, 90, -337, -446, -446, -410, -418, -417, -446, + -421, 35, -422, -470, -470, -470, -470, 117, -470, 117, + -386, 44, 52, 53, 54, -466, -466, 214, -389, 44, + 509, 511, 512, -421, 106, 106, 106, 106, 106, 96, + 96, 96, -466, -466, 106, 96, -473, 96, -676, 190, + 48, 49, 106, 106, 106, 106, 44, 96, -394, 44, + 329, 333, 330, 331, 332, 96, 106, 44, 106, 44, + 106, 44, -470, 90, -677, -678, 96, -585, 96, 90, + 106, 96, 271, -532, 96, 86, -710, -466, 440, -552, + 132, 132, -489, -712, 100, 490, -712, -715, 359, -229, + 582, 35, -287, 275, 270, -703, -541, -540, -446, -541, + -555, 571, 575, 576, -537, -485, 96, -545, -171, -270, + -269, 47, -333, -619, -305, -470, -426, -267, 176, 125, + -6, -181, -183, -497, -6, -497, 720, 453, 721, 96, + 106, 106, 573, -655, 531, 526, 528, -171, -656, 517, + -659, -252, 91, 65, 91, 27, 786, 786, 786, 786, + 96, -470, 106, -426, -482, -426, -222, -482, -482, 142, + -426, -324, -426, -426, -482, 19, -534, -529, 153, 153, + -470, 464, -546, 96, 488, 96, 278, 786, 96, -453, + -492, -492, 91, -497, 91, 38, 91, 91, -605, -605, + -604, -607, -604, -348, -348, 91, 90, -268, 91, 91, + 26, 91, 91, -568, 91, 91, -497, 176, 91, 91, + 177, 177, 91, -624, 591, -568, 21, -568, -625, 663, + -568, -568, -568, -568, -568, -568, -568, -568, -568, -568, + -568, -568, -568, -568, -568, 90, -568, -568, -568, -568, + -497, -570, -574, -470, -569, 47, 91, 177, -569, 91, + 177, -569, -569, 91, 532, 734, 734, 532, 734, 734, + -171, -279, -505, -744, 67, 726, 66, 509, 111, 349, + 91, -683, 177, -461, 354, -461, -452, 96, -470, 96, + 737, -470, 786, 786, 737, -470, 96, -333, -459, -290, + 548, -233, 126, -234, 124, 46, 96, -470, 19, -470, + -470, 346, -470, 346, -470, -470, 96, 90, 90, -158, + 636, 90, -155, 624, 96, 91, 177, -446, 91, 177, + -340, 38, 38, -711, 100, -706, 96, -470, 97, -470, + -712, 175, 438, 44, 491, 492, 507, 433, 106, 106, + 497, -704, -470, -228, 278, 434, -228, -714, 56, 132, + 96, -333, -514, -458, 163, 320, -324, -470, 382, -326, + -327, -328, -337, -329, -331, -237, 38, -333, -333, 96, + -168, -326, -466, -326, -599, 381, 23, 106, 152, -505, + 91, 117, -506, -642, -641, 65, -236, -280, -634, -470, + -680, -640, -470, -470, -497, -253, 91, -285, 87, 176, + -267, -334, -470, 153, -497, -497, 90, -260, 778, 91, + 72, 786, 90, 72, 11, 21, 177, -358, 132, 177, + -362, 132, -599, -236, -582, 350, 90, -580, 90, -580, + 117, 395, 398, 400, 402, 401, 430, 34, 270, -419, + 398, 392, 393, -547, 345, 122, 161, -547, -590, -588, + 301, -416, 48, 50, -340, -672, -470, -672, -670, -672, + -470, -670, -670, -517, -497, -416, -337, 282, 177, -268, + 177, -470, -381, -381, 34, 96, 96, -335, 91, 177, + 132, 96, -155, -154, -497, -238, -242, 282, 86, 278, + -711, -706, 132, -553, 96, 96, -712, 96, 96, -716, + 132, -336, 278, -459, 177, -287, -287, -426, 19, 177, + 132, -426, 91, -297, 26, 45, 48, 49, 91, 382, + -428, 21, 153, 129, 127, 129, 129, -470, 91, 91, + -611, 705, -651, -653, 526, 23, 23, -297, -657, 710, + 96, 65, -236, -634, 208, 96, 786, -426, -482, -426, + -426, 743, -426, -529, -546, 510, -333, 177, 786, -338, + -400, 96, 97, -470, -497, 91, -470, -497, -497, 91, + 96, 90, 91, 96, -272, 91, 91, -508, -507, 301, + -754, -497, -754, -497, -755, 19, 91, 177, 91, 91, + 91, 177, 91, 91, -297, -171, 91, 177, 91, -683, + -462, 208, 96, -462, -470, -470, 19, -471, -231, 282, + -324, -235, 377, 90, 373, -233, 187, 90, 96, -470, + 19, -470, -585, 346, -585, 346, 278, -470, -311, -358, + -362, -156, 625, 106, -154, 96, -522, 630, -321, -340, + 276, -307, 44, -237, -237, 96, -709, 501, -586, 387, + 106, 44, 106, 175, 493, -622, -214, 100, -335, 35, + -287, -214, -713, 100, 132, 785, 90, -466, -466, -466, + -231, 382, -470, 91, 177, -237, -232, 54, -470, -273, + 91, 742, 91, -600, 300, 106, 152, 106, 152, 106, + 91, 65, 17, 283, 91, -634, 176, 132, -468, -267, + -470, -426, -702, 176, -105, -106, 126, -283, -494, 175, + -671, -470, 285, 23, 23, -359, 91, -224, -363, 91, + -365, -470, 96, -600, -567, 351, 106, -493, 90, -493, + 90, -581, 348, 90, -527, -527, -527, -527, -527, 44, + -419, 44, -420, 417, 345, 345, 91, 177, -470, -446, + -353, -352, -350, 111, 122, 161, 44, 482, -351, 100, + 750, 751, 163, 334, 337, 336, 312, 335, -405, -486, + 86, 698, 485, 392, 393, -518, 713, 38, 285, 116, + 117, 465, -487, 90, 90, 90, 87, 354, 90, 90, + -672, 91, -416, -446, -417, -470, 163, -381, 91, -678, + 96, 91, -532, 278, -470, -709, 96, -555, -714, 96, + -214, -335, -703, -273, -267, -540, -640, -253, 17, 68, + 142, -470, -427, 22, 23, 284, -6, 721, 453, -396, + 706, 96, 23, 96, -649, 96, -647, 96, -634, 96, + -426, -470, -174, -393, -458, 317, 91, -399, 142, 14, + 91, 91, 91, -274, -272, -568, 91, -568, -273, 23, + -569, 91, -569, 91, -754, 48, 96, 96, -297, -497, + 91, 96, 96, 96, 737, 434, -231, 38, 471, 24, + 642, 378, -283, 374, 375, 376, -470, 96, -505, -238, + 785, 382, -470, 19, 96, -585, 96, -585, -470, 346, + 91, 91, 38, 96, 91, 96, 96, -309, -340, -490, + -218, 14, -218, 23, 14, 175, 437, 44, 106, 44, + 494, 96, -228, 132, 112, 113, -454, -455, 96, -524, + -381, -383, 96, -470, -354, -237, -221, -220, -355, -423, + -518, -328, -231, 30, 382, -277, -173, -356, -360, -366, + -372, -373, -172, 321, -357, -354, -601, 181, 106, 152, + 106, 106, 90, -541, -470, -497, -426, -426, -253, 772, + -106, -108, 122, 161, -470, 91, 91, 177, -601, -589, + 23, 91, -562, 91, -562, 90, 132, -493, -528, 291, + 409, 410, 100, 14, 392, 393, 414, 413, 412, 418, + 419, 423, 424, 420, 422, 421, 425, 429, 426, 427, + 428, 431, 415, 416, 58, 417, 437, 448, 444, -493, + -527, 132, -588, -591, 65, -350, 111, 111, -493, 96, + 96, 96, -383, -384, 44, 333, 329, 132, 132, -385, + 44, 313, 314, -395, 90, 344, 17, 106, 214, 714, + 90, 117, 117, -333, -524, -524, -524, -673, 394, 395, + 396, 404, 398, 399, 397, 400, 401, 402, 403, -673, + -524, -524, 90, -548, -547, -466, 163, -470, 176, -713, + -275, -426, 68, -572, 779, -497, -505, 756, 758, 759, + -470, -470, -450, 707, 106, 96, 528, -655, -482, -612, + 708, -645, -580, -381, 132, 91, 79, 629, 403, 631, + -568, 91, -568, -576, -575, -579, 532, 346, 540, -505, + -754, -754, -569, 91, 91, 91, -470, -470, -333, 96, + 106, 91, 121, 121, 91, 91, -457, -456, 96, -470, + 382, -470, -311, 96, -311, 96, 346, -585, -2, 630, + -219, 64, 578, 96, 97, 488, 96, 97, 106, 437, + -214, 96, 786, 177, 132, 91, -586, -563, 301, -237, + 38, 177, -423, -458, -470, -174, -466, -563, -618, 190, + 380, 14, 106, 152, 106, -505, -273, -253, -252, -107, + 121, -108, -108, -470, 285, -602, 190, 380, -566, 91, + 91, 91, -562, 106, 91, -466, -466, 96, 96, 391, + -466, -466, -466, -466, -466, -466, 90, 96, 96, -466, + -466, -466, -466, -466, -466, 132, 132, -466, -466, -466, + 112, 113, -596, -592, 90, -593, -423, 303, 142, 96, + 96, 106, 90, -635, 34, 38, -497, -525, 90, 91, + 91, 91, 91, 91, -524, -381, -466, 176, -470, -253, + -253, 69, -573, -571, -470, -620, 14, 757, 90, 90, + 90, 23, -611, -426, -613, 709, 96, -654, 531, -648, + -646, 526, 527, 528, 529, 96, 630, 69, 69, 632, + -578, 124, 493, 497, -491, 97, -494, 106, 108, 205, + -569, -569, 96, -470, 96, 786, 177, 132, -470, 382, + -311, -311, -585, 96, -312, -470, 344, 510, -455, 96, + -527, -564, 353, 23, -237, -423, -466, -586, 749, 753, + -380, 755, 754, 62, -564, 380, 106, 152, 106, 91, + -275, 127, -497, -107, -107, 380, -583, 352, 91, 106, + 106, -470, 106, 106, 106, 106, 106, 106, -562, 106, + 106, 106, 106, 106, 106, 112, 113, 112, 113, 106, + 106, 106, -596, -593, -595, -594, 351, 304, 90, 91, + -497, -509, -466, 90, 91, -398, -397, 627, -524, -527, + 87, -527, -527, 87, -527, 87, -527, 87, 91, -381, + -470, 177, 38, 759, 90, -505, -505, -451, -470, -649, + -396, 96, -658, 283, -652, -653, 530, -646, 23, 528, + 23, 23, -175, 177, 69, -577, -578, 497, -491, 97, + -494, 703, 538, 538, 538, 538, -233, -234, -233, -234, + -311, -456, 96, -470, 96, -311, -310, 38, 533, 464, + 23, -565, -381, -361, -367, 14, -377, 14, -374, 14, + 106, -253, 17, 18, 127, 127, 106, 91, 91, 177, + -470, 300, 90, -504, -498, -497, 300, 91, -381, -497, + -549, 716, 715, -404, -402, -403, 86, 545, 342, 343, + 91, -673, -673, -673, -673, -405, -571, 90, -169, 90, + 91, 91, 91, 177, -450, -666, 90, 106, -651, -650, + -652, 23, -649, 23, -649, -649, 535, 14, 121, 539, + 539, 539, 539, -233, -233, -311, 96, -446, 90, -582, + 90, 90, 90, 34, 16, 16, -594, -592, -504, 91, + 177, -547, 91, -403, 86, -402, 86, 18, 17, -527, + -527, -527, -527, -570, 91, 177, -503, -470, -669, 34, + 91, -665, -664, -447, -659, 531, 532, 96, -649, 132, + 631, -577, -747, -746, 733, -562, -567, -368, -369, -370, + -470, 96, -378, -379, -370, -375, -376, -370, -541, 90, + 65, 90, 65, 91, -498, -549, -401, 339, 340, 34, + 190, -401, 91, 90, 91, -668, -667, -448, 91, 177, + 96, 632, -317, 69, -315, 477, 276, 96, 91, -589, + 91, 177, 132, 91, 177, 132, 91, 177, 132, -291, + -470, 90, -291, 90, 111, 44, 341, -503, 177, 132, + -664, -316, 479, 480, 69, 86, 111, -369, -371, -470, + 96, -379, -371, -376, -371, 91, 177, 176, -505, 91, + -505, 91, -667, 44, -497, -315, -316, 111, 86, 65, + -470, -470, 91, 65, 91, 478, 111, 90, 176, 90, + 478, -505, -470, -505, 91, 91, } var yyDef = [...]int{ @@ -13456,516 +13515,518 @@ var yyDef = [...]int{ 806, 807, 808, 809, 810, 811, 812, 813, 814, 815, 816, 817, 818, 456, 457, 458, 452, 453, 455, 454, 0, 0, 851, 0, 0, 0, 994, 1028, 0, 1048, - 1049, 0, 1569, 1050, 1060, 1088, 0, 23, 0, 6, + 1049, 0, 1578, 1050, 1060, 1088, 0, 23, 0, 6, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, - 0, 0, 18, 0, 18, 0, 0, 0, 1786, 1787, - 1788, 1789, 2401, 2753, 2721, -2, 2444, 2402, 2403, 2644, - 2645, 2533, 2549, 2394, 2802, 2803, 2804, 2805, 2806, 2807, - 2808, 2809, 2810, 2811, 2812, 2813, 2814, 2815, 2816, 2817, - 2818, 2819, 2820, 2821, 2822, 2823, 2824, 2825, 2826, 2827, - 2828, 2829, 2830, 2831, 2832, 2833, 2834, 2835, 2836, 2837, - 2838, 2839, 2840, 2841, 2842, 2843, 2844, 2845, 2846, 2847, - 2848, 2849, 2850, 2851, 2852, 2853, 2854, 2855, 2856, 2348, - 2349, 2350, 2351, 2352, 2353, 2354, 2355, 2356, 2357, 2358, - 2359, 2360, 2361, 2362, 2363, 2364, 2365, 2366, 2367, 2368, - 2369, 2370, 2371, 2372, 2373, 2374, 2375, 2376, 2377, 2378, - 2379, 2380, 2381, 2382, 2383, 2384, 2385, 2386, 2387, 2388, - 2389, 2390, 2391, 2392, 2393, 2395, 2396, 2397, 2398, 2399, - 2400, 2404, 2405, 2406, 2407, 2408, 2409, 2410, 2411, 2412, - 2413, 2414, 2415, 2416, 2417, 2418, 2419, 2420, 2421, 2422, - 2423, 2424, 2425, 2426, 2427, 2428, 2429, 2430, 2431, 2432, - 2433, 2434, 2435, 2436, 2437, 2438, 2439, 2440, 2441, 2442, - 2443, 2445, 2446, 2447, 2448, 2449, 2450, 2451, 2452, 2453, - 2454, 2455, 2456, 2457, 2458, 2459, 2460, 2461, 2462, 2463, - 2464, 2465, 2466, 2467, 2468, 2469, 2470, 2471, 2472, 2473, - 2474, 2475, 2476, 2477, 2478, 2479, 2480, 2481, 2482, 2483, - 2484, 2485, 2486, 2487, 2488, 2489, 2490, 2491, 2492, 2493, - 2494, 2495, 2496, 2497, 2498, 2499, 2500, 2501, 2502, 2503, - 2504, 2505, 2506, 2507, 2508, 2509, 2510, 2511, 2512, 2513, - 2514, 2515, 2516, 2517, 2518, 2519, 2520, 2521, 2522, 2523, - 2524, 2525, 2526, 2527, 2528, 2529, 2530, 2531, 2532, 2534, - 2535, 2536, 2537, 2538, 2539, 2540, 2541, 2542, 2543, 2544, - 2545, 2546, 2547, 2548, 2550, 2552, 2553, 2554, 2555, 2556, - 2557, 2558, 2559, 2560, 2561, 2562, 2563, 2564, 2565, 2566, - 2567, 2568, 2569, 2570, 2571, 2572, 2573, 2574, 2575, 2576, - 2577, 2578, 2579, 2580, 2581, 2582, 2583, 2584, 2585, 2586, - 2587, 2588, 2589, 2590, 2591, 2592, 2593, 2594, 2595, 2596, - 2597, 2598, 2599, 2600, 2601, 2602, 2603, 2604, 2605, 2606, - 2607, 2608, 2609, 2610, 2611, 2612, 2613, 2614, 2615, 2616, - 2617, 2618, 2619, 2620, 2621, 2622, 2623, 2624, 2625, 2626, - 2627, 2628, 2629, 2630, 2631, 2632, 2633, 2634, 2635, 2636, - 2637, 2638, 2639, 2640, 2641, 2642, 2643, 2646, 2647, 2648, - 2649, 2650, 2651, 2652, 2653, 2654, 2655, 2656, 2657, 2658, - 2659, 2660, 2661, 2662, 2663, 2664, 2665, 2666, 2667, 2668, - 2669, 2670, 2671, 2672, 2673, 2674, 2675, 2676, 2677, -2, - 2679, 2680, 2681, 2682, 2683, 2684, 2685, 2686, 2687, 2688, - 2689, 2690, 2691, 2692, 2693, 2694, 2695, 2696, 2697, 2698, - 2699, 2700, 2701, 2702, 2703, 2704, 2705, 2706, 2707, 2708, - 2709, 2710, 2711, 2712, 2713, 2714, 2715, 2716, 2717, 2718, - 2719, 2720, 2722, 2723, 2724, 2725, 2726, 2727, 2728, 2729, - 2730, 2731, 2732, 2733, 2734, 2735, 2736, 2737, 2738, -2, - -2, -2, 2742, 2743, 2744, 2745, 2746, 2747, 2748, 2749, - 2750, 2751, 2752, 2754, 2755, 2756, 2757, 2758, 2759, 2760, - 2761, 2762, 2763, 2764, 2765, 2766, 2767, 2768, 2769, 2770, - 2771, 2772, 2773, 2774, 2775, 2776, 2777, 2778, 2779, 2780, - 2781, 2782, 2783, 2784, 2785, 2786, 2787, 2788, 2789, 2790, - 2791, 2792, 0, 345, 343, 2369, 2394, 2401, 2402, 2403, - 2444, 2533, 2549, 2551, 2590, 2644, 2645, 2671, 2678, 2721, - 2739, 2740, 2741, 2753, 0, 0, 1248, 0, 0, 382, - 840, 841, 871, 873, 895, 994, 1028, 0, 861, 862, - 0, 796, 0, 1729, 428, 0, 2420, 433, 2730, 0, - 0, 0, 0, 793, 422, 423, 424, 425, 426, 427, - 0, 0, 0, 1205, 0, 0, 2761, 418, 0, 0, - 83, 479, 684, 0, 0, 0, 0, 0, 773, 773, - 1113, 1113, 0, 714, 0, 0, 0, 773, 0, 728, - 720, 0, 0, 0, 773, 0, 0, 1017, 0, 1017, - 0, 776, 783, 773, 773, -2, 773, 773, 0, 768, - 773, 0, 0, 0, 734, 735, 736, 720, 720, 739, - 740, 741, 751, 752, 784, 2343, 0, 0, 376, 2536, - 2752, 1790, 0, 0, 0, 0, 0, 232, 1383, 234, - 1385, 238, 246, 0, 0, 0, 251, 252, 255, 256, - 257, 258, 259, 0, 263, 0, 265, 268, 0, 270, - 271, 0, 274, 275, 276, 0, 286, 287, 288, 1386, - 1387, 1388, 1389, 1390, 1391, 1392, 1393, -2, 0, 159, - 1246, 2298, 2165, 0, 2171, 2188, 2205, 1883, 1884, 1885, - 1886, 0, 0, 0, 0, 0, 0, 0, 1896, 1897, - 0, 1942, 2806, 2850, 2851, 0, 1906, 1907, 1908, 1909, - 1910, 1911, 0, 170, 184, 185, 2229, 2230, 2231, 2232, - 2233, 2234, 2235, 0, 2237, 2238, 2239, 0, 1868, 1786, - 0, 2815, 2825, 0, 2839, 2846, 2847, 2848, 2849, 2838, - 0, 0, 2120, 0, 2110, 0, 0, -2, -2, 0, - 0, 2617, -2, 2852, 2853, 2854, 2812, 2835, 2843, 2844, - 2845, 2816, 2855, 2856, 2819, 2842, 2808, 2809, 2810, 2803, - 2804, 2805, 2807, 2821, 2823, 2834, 0, 2830, 2840, 2841, - 2728, 0, 0, 2779, 2817, 2818, 0, 0, 0, 0, - 0, 0, 2788, 2789, 2790, 2791, 2792, 2774, 186, 187, + 0, 0, 18, 0, 18, 0, 0, 0, 1795, 1796, + 1797, 1798, 2411, 2766, 2734, -2, 2454, 2412, 2413, 2657, + 2658, 2544, 2561, 2404, 2815, 2816, 2817, 2818, 2819, 2820, + 2821, 2822, 2823, 2824, 2825, 2826, 2827, 2828, 2829, 2830, + 2831, 2832, 2833, 2834, 2835, 2836, 2837, 2838, 2839, 2840, + 2841, 2842, 2843, 2844, 2845, 2846, 2847, 2848, 2849, 2850, + 2851, 2852, 2853, 2854, 2855, 2856, 2857, 2858, 2859, 2860, + 2861, 2862, 2863, 2864, 2865, 2866, 2867, 2868, 2869, 2357, + 2358, 2359, 2360, 2361, 2362, 2363, 2364, 2365, 2366, 2367, + 2368, 2369, 2370, 2371, 2372, 2373, 2374, 2375, 2376, 2377, + 2378, 2379, 2380, 2381, 2382, 2383, 2384, 2385, 2386, 2387, + 2388, 2389, 2390, 2391, 2392, 2393, 2394, 2395, 2396, 2397, + 2398, 2399, 2400, 2401, 2402, 2403, 2405, 2406, 2407, 2408, + 2409, 2410, 2414, 2415, 2416, 2417, 2418, 2419, 2420, 2421, + 2422, 2423, 2424, 2425, 2426, 2427, 2428, 2429, 2430, 2431, + 2432, 2433, 2434, 2435, 2436, 2437, 2438, 2439, 2440, 2441, + 2442, 2443, 2444, 2445, 2446, 2447, 2448, 2449, 2450, 2451, + 2452, 2453, 2455, 2456, 2457, 2458, 2459, 2460, 2461, 2462, + 2463, 2464, 2465, 2466, 2467, 2468, 2469, 2470, 2471, 2472, + 2473, 2474, 2475, 2476, 2477, 2478, 2479, 2480, 2481, 2482, + 2483, 2484, 2485, 2486, 2487, 2488, 2489, 2490, 2491, 2492, + 2493, 2494, 2495, 2496, 2497, 2498, 2499, 2500, 2501, 2502, + 2503, 2504, 2505, 2506, 2507, 2508, 2509, 2510, 2511, 2512, + 2513, 2514, 2515, 2516, 2517, 2518, 2519, 2520, 2521, 2522, + 2523, 2524, 2525, 2526, 2527, 2528, 2529, 2530, 2531, 2532, + 2533, 2534, 2535, 2536, 2537, 2538, 2539, 2540, 2541, 2542, + 2543, 2545, 2546, 2547, 2548, 2549, 2550, 2551, 2552, 2553, + 2554, 2555, 2556, 2557, 2558, 2559, 2560, 2562, 2564, 2565, + 2566, 2567, 2568, 2569, 2570, 2571, 2572, 2573, 2574, 2575, + 2576, 2577, 2578, 2579, 2580, 2581, 2582, 2583, 2584, 2585, + 2586, 2587, 2588, 2589, 2590, 2591, 2592, 2593, 2594, 2595, + 2596, 2597, 2598, 2599, 2600, 2601, 2602, 2603, 2604, 2605, + 2606, 2607, 2608, 2609, 2610, 2611, 2612, 2613, 2614, 2615, + 2616, 2617, 2618, 2619, 2620, 2621, 2622, 2623, 2624, 2625, + 2626, 2627, 2628, 2629, 2630, 2631, 2632, 2633, 2634, 2635, + 2636, 2637, 2638, 2639, 2640, 2641, 2642, 2643, 2644, 2645, + 2646, 2647, 2648, 2649, 2650, 2651, 2652, 2653, 2654, 2655, + 2656, 2659, 2660, 2661, 2662, 2663, 2664, 2665, 2666, 2667, + 2668, 2669, 2670, 2671, 2672, 2673, 2674, 2675, 2676, 2677, + 2678, 2679, 2680, 2681, 2682, 2683, 2684, 2685, 2686, 2687, + 2688, 2689, 2690, -2, 2692, 2693, 2694, 2695, 2696, 2697, + 2698, 2699, 2700, 2701, 2702, 2703, 2704, 2705, 2706, 2707, + 2708, 2709, 2710, 2711, 2712, 2713, 2714, 2715, 2716, 2717, + 2718, 2719, 2720, 2721, 2722, 2723, 2724, 2725, 2726, 2727, + 2728, 2729, 2730, 2731, 2732, 2733, 2735, 2736, 2737, 2738, + 2739, 2740, 2741, 2742, 2743, 2744, 2745, 2746, 2747, 2748, + 2749, 2750, 2751, -2, -2, -2, 2755, 2756, 2757, 2758, + 2759, 2760, 2761, 2762, 2763, 2764, 2765, 2767, 2768, 2769, + 2770, 2771, 2772, 2773, 2774, 2775, 2776, 2777, 2778, 2779, + 2780, 2781, 2782, 2783, 2784, 2785, 2786, 2787, 2788, 2789, + 2790, 2791, 2792, 2793, 2794, 2795, 2796, 2797, 2798, 2799, + 2800, 2801, 2802, 2803, 2804, 2805, 0, 345, 343, 2379, + 2404, 2411, 2412, 2413, 2454, 2544, 2561, 2563, 2602, 2657, + 2658, 2684, 2691, 2734, 2752, 2753, 2754, 2766, 0, 0, + 1248, 0, 0, 382, 840, 841, 871, 873, 895, 994, + 1028, 0, 861, 862, 0, 796, 0, 1738, 428, 0, + 2430, 433, 2743, 0, 0, 0, 0, 793, 422, 423, + 424, 425, 426, 427, 0, 0, 0, 1205, 0, 0, + 2774, 418, 0, 0, 83, 479, 684, 0, 0, 0, + 0, 0, 773, 773, 1113, 1113, 0, 714, 0, 0, + 0, 773, 0, 728, 720, 0, 0, 0, 773, 0, + 0, 1017, 0, 1017, 0, 776, 783, 773, 773, -2, + 773, 773, 0, 768, 773, 0, 0, 0, 734, 735, + 736, 720, 720, 739, 740, 741, 751, 752, 784, 2352, + 0, 0, 376, 2547, 2765, 1799, 0, 0, 0, 0, + 0, 232, 1392, 234, 1394, 238, 246, 0, 0, 0, + 251, 252, 255, 256, 257, 258, 259, 0, 263, 0, + 265, 268, 0, 270, 271, 0, 274, 275, 276, 0, + 286, 287, 288, 1395, 1396, 1397, 1398, 1399, 1400, 1401, + 1402, -2, 0, 159, 1246, 2307, 2174, 0, 2180, 2197, + 2214, 1892, 1893, 1894, 1895, 0, 0, 0, 0, 0, + 0, 0, 1905, 1906, 0, 1951, 2819, 2863, 2864, 0, + 1915, 1916, 1917, 1918, 1919, 1920, 0, 170, 184, 185, + 2238, 2239, 2240, 2241, 2242, 2243, 2244, 0, 2246, 2247, + 2248, 0, 1877, 1795, 0, 2828, 2838, 0, 2852, 2859, + 2860, 2861, 2862, 2851, 0, 0, 2129, 0, 2119, 0, + 0, -2, -2, 0, 0, 2630, -2, 2865, 2866, 2867, + 2825, 2848, 2856, 2857, 2858, 2829, 2868, 2869, 2832, 2855, + 2821, 2822, 2823, 2816, 2817, 2818, 2820, 2834, 2836, 2847, + 0, 2843, 2853, 2854, 2741, 0, 0, 2792, 2830, 2831, + 0, 0, 0, 0, 0, 0, 2801, 2802, 2803, 2804, + 2805, 2787, 186, 187, -2, -2, -2, -2, -2, -2, + -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, + 2140, -2, 2142, -2, 2144, -2, 2146, -2, -2, -2, + -2, 2151, 2152, -2, -2, 2155, -2, -2, -2, -2, + -2, -2, -2, 2131, 2132, 2133, 2134, 2123, 2124, 2125, + 2126, 2127, 2128, -2, -2, -2, 2308, 994, 0, 0, + 1028, 1155, 0, 1028, 1026, 995, 0, 0, 998, 1075, + 1078, 1081, 1084, 0, 0, 129, 130, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 371, 372, 360, 362, 0, 366, 0, 0, 362, 359, + 353, 0, 1464, 1464, 1464, 0, 0, 1464, 0, 0, + 0, 1464, 1464, 1464, 1464, 1464, 0, 1464, 0, 0, + 0, 1464, 0, 1293, 1404, 1405, 1406, 1407, 1462, 1463, + 1579, 0, 0, 0, 0, 2694, 947, 2531, 0, 0, + 955, 1010, 0, 1012, 1015, 856, 852, 853, 854, 855, + 0, 0, 0, 617, 617, 0, 617, 0, 0, 617, + 0, 617, 617, 617, 0, 858, 2493, 2597, 2447, 2568, + 2389, 2547, 2765, 0, 318, 2630, 323, 0, 2453, 2497, + 0, 0, 2518, 0, -2, 0, 399, 1028, 0, 995, + 1019, 0, 1029, 0, 0, 994, 1028, 1028, 0, 1068, + 0, 0, 617, 617, 0, 0, 617, 617, 617, 617, + 617, 0, 617, 617, 617, 0, 617, 0, 617, 0, + 617, 617, 617, 0, 1089, 1090, 1092, 1093, 1094, 1095, + 1096, 1097, 1098, 1099, 1100, 1101, 1062, 4, 5, 18, + 0, 0, 0, 0, 0, 0, 135, 134, 0, 2311, + 2347, 2251, 2252, 2253, 0, 2334, 2256, 2338, 2338, 2338, + 2338, 0, 2288, 2334, 2290, 2291, 2292, 2334, 2294, 2295, + 2296, 2297, 2338, 2338, 2338, 2338, 2338, 2338, 0, 0, + 2306, 2277, 2336, 2336, 2336, 2334, 2313, 2257, 2258, 2259, + 2260, 2261, 2262, 2263, 2264, 2265, 2266, 2267, 2268, 2269, + 2270, 2341, 2341, 2344, 2344, 2341, 2314, 2315, 2316, 2317, + 2318, 2319, 2320, 2321, 2322, 2323, 2324, 2325, 2326, 2327, + 2328, 2329, 2330, 2331, 0, 484, 482, 483, 2170, 0, + 0, 0, 1028, -2, 0, 1026, 945, 0, 797, 1729, + 0, 0, 429, 1800, 0, 0, 434, 0, 435, 0, + 0, 437, 0, 0, 0, 459, 0, 462, 445, 446, + 447, 448, 449, 441, 0, 212, 0, 420, 421, 417, + 0, 468, 470, 472, 475, 1725, 80, 84, 87, 0, + 1017, 0, 686, 773, 773, 773, 773, 702, 703, 704, + 0, 1113, 0, 0, 707, 708, 0, 709, 0, 0, + 720, 773, 773, 726, 727, 722, 721, 779, 780, 776, + 0, 776, 776, 1113, 0, 745, 746, 747, 773, 773, + 753, 1018, 0, 0, 755, 757, 776, 0, 781, 782, + 1113, 0, 0, 1113, 1113, 0, 765, 766, 0, 769, + 773, 0, 772, 0, 0, 1464, 0, 789, 722, 722, + 2353, 2354, 378, 0, 0, 0, 618, 0, 0, 0, + 0, 0, 0, 243, 239, 247, 250, 260, 267, 0, + 279, 281, 284, 240, 248, 253, 254, 261, 282, 241, + 244, 245, 249, 283, 285, 242, 262, 266, 280, 264, + 269, 272, 273, 278, 0, 213, 0, 0, 0, 0, + 0, 2179, 0, 0, 2224, 2225, 2226, 2227, 2228, 2229, + 2230, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 2217, 0, + 0, 0, 0, 0, 0, 0, -2, 2174, 0, 0, + 1899, 1900, 1901, 1902, 1903, 0, 1907, 0, 1952, 0, + 0, 0, 0, 0, 0, 2245, 2249, 0, 0, 2170, + 2170, 0, 0, 2170, 2166, 0, 0, 0, 0, 0, + 0, 2170, 2102, 0, 0, 2104, 2120, 0, 0, 2106, + 2107, 0, 2110, 2111, 2170, 0, 2170, 2115, 2170, 2170, + 2170, 2096, 2097, 0, 0, 0, 2166, 0, 0, 2166, + 2166, 2166, 0, 0, 2166, 2166, 2166, 2166, 2166, 2166, + 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, + 2166, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 1028, 994, 1019, 0, 0, -2, 0, + 956, 0, 1026, 1052, 1054, 1056, 1058, 1025, 1000, 0, + 1076, 1077, 1079, 1080, 1082, 1083, 1085, 1086, 0, 131, + 0, 0, 0, 112, 0, 0, 110, 0, 0, 0, + 0, 81, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 364, 0, 369, 355, 2590, 0, + 354, 0, 0, 0, 0, 1464, 1464, 0, 0, 1245, + 0, 0, 1464, 1464, 1294, 0, 0, 0, 0, 0, + 0, 0, 0, 1464, 1464, 1464, 0, 1482, 0, 0, + 0, 0, 880, 882, 877, 0, 0, 880, 0, 0, + 947, 947, 954, 0, 1011, 0, 0, 858, 857, 0, + 1128, 1129, 1130, 1171, 1195, 1195, 0, 1195, 1175, 1738, + 0, 0, 0, 0, 1476, 0, 0, 0, 0, 0, + 0, 0, 792, 0, 0, 0, 511, 512, 0, 0, + 859, 0, 297, 301, 0, 304, 0, 2597, 0, 2597, + 0, 0, 311, 0, 0, 0, 0, 0, 0, 341, + 342, 0, 0, 0, 0, 332, 335, 1721, 1722, 1389, + 1390, 336, 337, 391, 392, 0, 1019, 1051, 1053, 1055, + 1057, 977, 1020, 1021, 1061, 1063, 1064, 1065, 0, 0, + 1046, 1047, 1019, 1019, 0, 1070, 0, 0, 0, 617, + 617, 0, 0, 0, 0, 0, 617, 0, 0, 0, + 834, 0, 836, 0, 0, 617, 0, 0, 0, 956, + 1115, 1117, 1200, 170, 1091, 7, 152, 149, 0, 18, + 0, 0, 18, 18, 0, 18, 346, 0, 2350, 2348, + 2349, 0, 2255, 2335, 0, 2282, 0, 2283, 2284, 2285, + 2286, 2287, 2289, 2293, 2298, 2299, 2300, 2301, 2302, 2303, + 0, 0, 2278, 0, 2279, 2280, 2281, 2271, 2341, 0, + 2273, 2274, 0, 2275, 2276, 344, 481, 0, 0, 2171, + 1249, 0, 0, 1019, 990, 1026, 0, 1026, 956, 0, + 0, 617, 1727, 0, 1738, 0, 0, 0, 0, 0, + 430, 0, 442, 436, 0, 443, 438, 439, 0, 0, + 461, 463, 464, 465, 466, 450, 451, 794, 414, 415, + 416, 400, 401, 402, 403, 404, 405, 406, 407, 408, + 0, 410, 411, 412, 413, 0, -2, 0, 0, 419, + 180, 467, 0, 469, 0, 474, 0, 476, 0, 88, + 478, 0, 688, 0, 1113, 0, 0, 1113, 1113, 0, + 0, 1113, 0, 706, 774, 775, 1114, 710, 0, 0, + 717, 2547, 1381, 722, 1113, 1113, 729, 723, 730, 778, + 731, 732, 733, 776, 1113, 1113, 1022, 0, 773, 776, + 758, 777, 776, 1738, 762, 0, 767, 770, 771, 1738, + 790, 1738, 0, 788, 737, 738, 379, 380, 0, 0, + 0, 226, 227, 228, 229, 230, 231, 233, 217, 825, + 827, 1393, 0, 1384, 0, 236, 277, 209, 0, 0, + 0, 0, 2175, 2176, 2177, 2178, 2183, 0, 0, 2186, + 2189, 2192, 2195, 0, 2221, -2, -2, 1878, 1879, 1880, + 1881, 1882, 1883, 1884, 1885, 1886, 1887, 1888, 1889, 1890, + 1891, 2198, 2219, 2220, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 2218, 2215, 2215, 2208, + 0, 1896, 1912, 1956, 1969, 1969, 1921, 1723, 1724, 1897, + 0, 0, 1949, 1953, 0, 0, 0, 0, 0, 0, + 1443, 2334, 0, 171, 2214, 2165, 2063, 2064, 2065, 2066, + 2067, 2068, 2069, 2070, 2071, 2072, 2073, 2074, 2075, 2076, + 2077, 2078, 2079, 2080, 2081, 2082, 2083, 2084, 2085, 2086, + 2087, 2088, 2089, 2090, 2091, 0, 0, 2174, 0, 0, + 0, 0, 2167, 2168, 0, 0, 0, 2051, 0, 0, + 2057, 2058, 2059, 0, 1792, 0, 2130, 2103, 2121, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 2092, 2093, 2094, 2095, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 1019, 1028, 1154, 1156, + 0, 941, 943, 944, 984, 1026, 1026, 986, 0, 956, + 1002, 0, 0, 0, 127, 132, 0, 1546, 118, 0, + 0, 0, 118, 0, 0, 0, 118, 0, 0, 0, + 1365, 1478, 89, 1364, 1480, 0, 0, 0, 0, 0, + 0, 0, 373, 374, 0, 0, 368, 356, 2590, 358, + 0, 0, 0, 0, 0, 0, 0, 1232, 0, 0, + 0, 1263, 0, 0, 0, 1309, 1310, 0, 615, 1375, + 0, 0, 0, 1391, 1447, 1460, 0, 0, 0, 1552, + 1295, 1300, 1301, 1302, 1296, 1297, 1303, 1304, 874, 883, + 0, 878, 0, 884, 0, 0, 917, 937, 912, 0, + 921, 0, 0, 0, 1013, 0, 0, 1169, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 1161, 0, 0, + 0, 1160, 0, 0, 0, 0, 0, 0, 0, 1015, + 1196, 0, 1198, 1199, 1173, -2, 0, 1124, 1126, 0, + 1178, 2170, 0, 0, 1554, 1015, 508, 509, 510, 520, + 522, 0, 581, 581, 581, 581, 564, 0, 0, 552, + 2355, 0, 0, 0, 0, 561, 2355, 0, 0, 2355, + 2355, 2355, 2355, 2355, 2355, 2355, 2355, 0, 0, 2355, + 2355, 2355, 2355, 2355, 2355, 2355, 2355, 2355, 2355, 2355, + 0, 2355, 2355, 2355, 2355, 2355, 1707, 2355, 0, 1477, + 571, 572, 573, 574, 579, 580, 0, 0, 525, 526, + 0, 0, 0, 0, 0, 610, 0, 0, 1308, 0, + 615, 0, 0, 1353, 0, 0, 1126, 0, 0, 0, + 0, 309, 310, 298, 0, 299, 0, 0, 312, 313, + 0, 315, 316, 317, 324, 2447, 2568, 319, 321, 0, + 0, 325, 338, 339, 340, 0, 0, 330, 331, 0, + 0, 394, 395, 397, 0, 1026, 1066, 1032, 1033, 1036, + 0, 0, 1069, 1071, 1072, 1073, 1074, 1479, 1467, 85, + 86, 2774, 819, 0, 0, 822, 823, 824, 828, 0, + 0, 1580, 832, 833, 0, 0, 1362, 0, 1366, 1368, + 1467, 1121, 0, 1118, 1201, 0, 1203, 0, 0, 150, + 18, 0, 143, 140, 0, 0, 0, 0, 0, 2312, + 2250, 2351, 0, 0, 0, 0, 2332, 0, 0, 2272, + 0, 0, 0, 133, 156, 963, 1026, 956, 0, 958, + 182, 956, 992, 892, 937, 865, 0, 921, 0, 0, + 0, 0, 0, 1729, 0, 0, 0, 0, 1801, 0, + 444, 440, 460, 0, 409, 0, 0, 471, 0, 1726, + 477, 480, 685, 0, 687, 690, 773, 0, 693, 694, + 773, 0, 0, 700, 705, 713, 711, 0, 715, 0, + 716, 0, 1383, 773, 724, 725, 1113, 748, 749, 0, + 0, 0, 1113, 773, 773, 761, 776, 785, 0, 786, + 1738, 0, 220, 1378, 0, 221, 225, 215, 0, 0, + 0, 1380, 1385, 0, 235, 0, 0, 210, 211, 1537, + 1546, 0, 0, 0, 158, 2184, 2187, 2190, 2193, 2185, + 2188, 2191, 2194, 2196, 0, 2199, 2215, 2215, 2209, 0, + 2200, 2215, 2215, 2210, 0, 0, 2202, 0, 2205, 0, + 1957, 1970, 1971, 1958, 2175, 1904, 0, 1954, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 1036, 0, 0, + 0, 2039, 2041, 0, 0, 0, 2046, 0, 2048, 2049, + 2050, 2052, 0, 0, 0, 2056, 0, 2101, 2122, 2105, + 2108, 0, 2112, 0, 2114, 2116, 2117, 2118, 0, 0, + 0, 1028, 1030, 1030, 1028, 0, 0, 1996, 1996, 1030, + 0, 0, 0, 0, 1996, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 1926, + 0, 1927, 1928, 1929, 0, 1931, 2870, 0, 2870, 0, + 2870, 2870, 0, 0, 0, 1026, 1019, 0, 1157, 956, + 956, 0, 987, 996, 0, 0, 0, 0, 0, 0, + 1544, 0, 108, 0, 113, 0, 0, 109, 114, 0, + 0, 111, 0, 0, 120, 90, 0, 0, 1485, 1486, + 0, 0, 0, 375, 363, 365, 0, 357, 0, 1465, + 1466, 0, 0, 0, 1626, 1636, 1236, 0, 0, -2, + 1265, 0, 1015, 1015, 1320, 2355, 0, 619, 0, 0, + 1377, 0, 1342, 0, 0, 0, -2, 0, 0, -2, + 0, 0, 1556, 0, 875, 881, 0, 0, 0, 876, + 892, 918, 0, 911, 934, 0, 938, 0, 0, 925, + 927, 916, 922, 0, 0, 0, 952, 892, 892, 23, + 1016, 0, 0, 0, 845, 849, 0, 1165, 1165, 1165, + 1134, 0, 0, 1137, 1162, 1138, 1163, 0, 0, 1142, + 0, 1144, 0, 1146, 0, 1140, 1141, 1148, 1149, 0, + 1139, 1172, 1197, 0, 1174, 0, 1177, 1179, 1180, 1186, + 0, 0, 0, 0, 0, 0, 504, 505, 1556, 0, + 0, 1476, 1623, 0, 0, 551, 582, 0, 0, 0, + 1590, 542, 0, 1751, 1752, 0, 1759, 1768, 1768, 1464, + 1841, 0, 1768, 0, 0, 1770, 1771, 0, 0, 0, + 0, 565, 566, 0, 0, 550, 0, 0, 592, 0, + 0, 0, 0, 0, 2356, 2355, 2355, 0, 559, 560, + 0, 563, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 2355, 2355, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 1698, 0, 0, 0, 0, 0, + 0, 0, 1713, 1714, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 1320, 2355, 0, 0, 0, 0, 619, + 1372, 1372, 1340, 1358, 0, 513, 514, 589, 0, 0, + 0, 302, 303, 0, 0, 308, 326, 328, 300, 0, + 0, 0, 327, 329, 333, 334, 393, 396, 398, 956, + 0, 1039, 1037, 1038, 1059, 1067, 82, 1468, 820, 821, + 0, 1580, 831, 1581, 1582, 1265, 837, 1363, 0, 1370, + 1113, 1122, 0, 1116, 1202, 1204, 171, 1119, 0, 153, + 18, 152, 144, 145, 0, 18, 0, 0, 0, 0, + 2254, 2340, 2339, 2304, 0, 2305, 2337, 2342, 0, 2345, + 0, 485, 0, 967, 0, 956, 993, 959, 0, 991, + 863, 0, 864, 0, 0, 869, 0, 952, 1367, 1730, + 1731, 0, 1734, 1735, 1736, 1737, 1728, 0, 0, 0, + 0, 0, 0, 0, 795, 181, 473, 689, 1113, 773, + 1113, 0, 773, 773, 701, 1633, 1634, 712, 718, 1382, + 1113, 742, 1023, 1024, 754, 756, 1738, 1113, 1113, 773, + 791, 787, 0, 0, 0, 0, 0, 826, 0, 217, + 0, 0, 237, 0, 0, 0, 1546, 1541, 2169, 2203, + 2206, 0, 2204, 2207, 0, 0, 2216, 2211, 1898, 1908, + 1950, 0, 0, 0, 0, 0, 1959, 1960, 2338, 2338, + 1963, 2334, 2336, 2334, 1969, 1969, 0, 1444, 0, 1445, + 1036, 172, 0, 0, 0, 0, 1996, 0, 0, 0, + 1793, 0, 0, 0, 0, 0, 1992, 1996, 0, 1996, + 1994, 1996, 1996, 2014, 1997, 2015, 1996, 1996, 1996, 1996, + 1996, 2021, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, + 0, 1996, 1996, 1996, 1996, 0, 1998, 2001, 1930, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 2239, + 2240, 956, 1026, 942, 988, 985, 0, 0, 0, 0, + 0, 71, 71, 1546, 0, 0, 0, 0, 0, 126, + 0, 0, 0, 0, 0, 0, 0, 1494, 1502, 0, + 367, 0, 91, 92, 94, 0, 0, 0, 0, 0, + 0, 0, 107, 1229, 0, 1230, 0, 1240, 0, 1234, + 0, 0, 1251, 1252, 1254, 0, 1257, 1258, 1259, 0, + 1266, 1267, 1269, 1270, 0, 2361, -2, -2, 2494, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, - -2, -2, -2, -2, -2, -2, 2131, -2, 2133, -2, - 2135, -2, 2137, -2, -2, -2, -2, 2142, 2143, -2, - -2, 2146, -2, -2, -2, -2, -2, -2, -2, 2122, - 2123, 2124, 2125, 2114, 2115, 2116, 2117, 2118, 2119, -2, - -2, -2, 2299, 994, 0, 0, 1028, 1155, 0, 1028, - 1026, 995, 0, 0, 998, 1075, 1078, 1081, 1084, 0, - 0, 129, 130, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 371, 372, 360, 362, - 0, 366, 0, 0, 362, 359, 353, 0, 1455, 1455, - 1455, 0, 0, 1455, 0, 0, 0, 1455, 1455, 1455, - 1455, 1455, 0, 1455, 0, 0, 0, 1455, 0, 1284, - 1395, 1396, 1397, 1398, 1453, 1454, 1570, 0, 0, 0, - 0, 2681, 947, 2520, 0, 0, 955, 1010, 0, 1012, - 1015, 856, 852, 853, 854, 855, 0, 0, 0, 617, - 617, 0, 617, 0, 0, 617, 0, 617, 617, 617, - 0, 858, 2482, 2585, 2437, 2556, 2379, 2536, 2752, 0, - 318, 2617, 323, 0, 2443, 2486, 0, 0, 2507, 0, - -2, 0, 399, 1028, 0, 995, 1019, 0, 1029, 0, - 0, 994, 1028, 1028, 0, 1068, 0, 0, 617, 617, - 0, 0, 617, 617, 617, 617, 617, 0, 617, 617, - 617, 0, 617, 0, 617, 0, 617, 617, 617, 0, - 1089, 1090, 1092, 1093, 1094, 1095, 1096, 1097, 1098, 1099, - 1100, 1101, 1062, 4, 5, 18, 0, 0, 0, 0, - 0, 0, 135, 134, 0, 2302, 2338, 2242, 2243, 2244, - 0, 2325, 2247, 2329, 2329, 2329, 2329, 0, 2279, 2325, - 2281, 2282, 2283, 2325, 2285, 2286, 2287, 2288, 2329, 2329, - 2329, 2329, 2329, 2329, 0, 0, 2297, 2268, 2327, 2327, - 2327, 2325, 2304, 2248, 2249, 2250, 2251, 2252, 2253, 2254, - 2255, 2256, 2257, 2258, 2259, 2260, 2261, 2332, 2332, 2335, - 2335, 2332, 2305, 2306, 2307, 2308, 2309, 2310, 2311, 2312, - 2313, 2314, 2315, 2316, 2317, 2318, 2319, 2320, 2321, 2322, - 0, 484, 482, 483, 2161, 0, 0, 0, 1028, -2, - 0, 1026, 945, 0, 797, 1720, 0, 0, 429, 1791, - 0, 0, 434, 0, 435, 0, 0, 437, 0, 0, - 0, 459, 0, 462, 445, 446, 447, 448, 449, 441, - 0, 212, 0, 420, 421, 417, 0, 468, 470, 472, - 475, 1716, 80, 84, 87, 0, 1017, 0, 686, 773, - 773, 773, 773, 702, 703, 704, 0, 1113, 0, 0, - 707, 708, 0, 709, 0, 0, 720, 773, 773, 726, - 727, 722, 721, 779, 780, 776, 0, 776, 776, 1113, - 0, 745, 746, 747, 773, 773, 753, 1018, 0, 0, - 755, 757, 776, 0, 781, 782, 1113, 0, 0, 1113, - 1113, 0, 765, 766, 0, 769, 773, 0, 772, 0, - 0, 1455, 0, 789, 722, 722, 2344, 2345, 378, 0, - 0, 0, 618, 0, 0, 0, 0, 0, 0, 243, - 239, 247, 250, 260, 267, 0, 279, 281, 284, 240, - 248, 253, 254, 261, 282, 241, 244, 245, 249, 283, - 285, 242, 262, 266, 280, 264, 269, 272, 273, 278, - 0, 213, 0, 0, 0, 0, 0, 2170, 0, 0, - 2215, 2216, 2217, 2218, 2219, 2220, 2221, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 2208, 0, 0, 0, 0, 0, - 0, 0, -2, 2165, 0, 0, 1890, 1891, 1892, 1893, - 1894, 0, 1898, 0, 1943, 0, 0, 0, 0, 0, - 0, 2236, 2240, 0, 0, 2161, 2161, 0, 0, 2161, - 2157, 0, 0, 0, 0, 0, 0, 2161, 2093, 0, - 0, 2095, 2111, 0, 0, 2097, 2098, 0, 2101, 2102, - 2161, 0, 2161, 2106, 2161, 2161, 2161, 2087, 2088, 0, - 0, 0, 2157, 0, 0, 2157, 2157, 2157, 0, 0, - 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, - 2157, 2157, 2157, 2157, 2157, 2157, 2157, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 1028, - 994, 1019, 0, 0, -2, 0, 956, 0, 1026, 1052, - 1054, 1056, 1058, 1025, 1000, 0, 1076, 1077, 1079, 1080, - 1082, 1083, 1085, 1086, 0, 131, 0, 0, 0, 112, - 0, 0, 110, 0, 0, 0, 0, 81, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 364, 0, 369, 355, 2578, 0, 354, 0, 0, 0, - 0, 1455, 1455, 0, 0, 1245, 0, 0, 1455, 1455, - 1285, 0, 0, 0, 0, 0, 0, 0, 0, 1455, - 1455, 1455, 0, 1473, 0, 0, 0, 0, 880, 882, - 877, 0, 0, 880, 0, 0, 947, 947, 954, 0, - 1011, 0, 0, 858, 857, 0, 1128, 1129, 1130, 1171, - 1195, 1195, 0, 1195, 1175, 1729, 0, 0, 0, 0, - 1467, 0, 0, 0, 0, 0, 0, 0, 792, 0, - 0, 0, 511, 512, 0, 0, 859, 0, 297, 301, - 0, 304, 0, 2585, 0, 2585, 0, 0, 311, 0, - 0, 0, 0, 0, 0, 341, 342, 0, 0, 0, - 0, 332, 335, 1712, 1713, 1380, 1381, 336, 337, 391, - 392, 0, 1019, 1051, 1053, 1055, 1057, 977, 1020, 1021, - 1061, 1063, 1064, 1065, 0, 0, 1046, 1047, 1019, 1019, - 0, 1070, 0, 0, 0, 617, 617, 0, 0, 0, - 0, 0, 617, 0, 0, 0, 834, 0, 836, 0, - 0, 617, 0, 0, 0, 956, 1115, 1117, 1200, 170, - 1091, 7, 152, 149, 0, 18, 0, 0, 18, 18, - 0, 18, 346, 0, 2341, 2339, 2340, 0, 2246, 2326, - 0, 2273, 0, 2274, 2275, 2276, 2277, 2278, 2280, 2284, - 2289, 2290, 2291, 2292, 2293, 2294, 0, 0, 2269, 0, - 2270, 2271, 2272, 2262, 2332, 0, 2264, 2265, 0, 2266, - 2267, 344, 481, 0, 0, 2162, 1249, 0, 0, 1019, - 990, 1026, 0, 1026, 956, 0, 0, 617, 1718, 0, - 1729, 0, 0, 0, 0, 0, 430, 0, 442, 436, - 0, 443, 438, 439, 0, 0, 461, 463, 464, 465, - 466, 450, 451, 794, 414, 415, 416, 400, 401, 402, - 403, 404, 405, 406, 407, 408, 0, 410, 411, 412, - 413, 0, -2, 0, 0, 419, 180, 467, 0, 469, - 0, 474, 0, 476, 0, 88, 478, 0, 688, 0, - 1113, 0, 0, 1113, 1113, 0, 0, 1113, 0, 706, - 774, 775, 1114, 710, 0, 0, 717, 2536, 1372, 722, - 1113, 1113, 729, 723, 730, 778, 731, 732, 733, 776, - 1113, 1113, 1022, 0, 773, 776, 758, 777, 776, 1729, - 762, 0, 767, 770, 771, 1729, 790, 1729, 0, 788, - 737, 738, 379, 380, 0, 0, 0, 226, 227, 228, - 229, 230, 231, 233, 217, 825, 827, 1384, 0, 1375, - 0, 236, 277, 209, 0, 0, 0, 0, 2166, 2167, - 2168, 2169, 2174, 0, 0, 2177, 2180, 2183, 2186, 0, - 2212, -2, -2, 1869, 1870, 1871, 1872, 1873, 1874, 1875, - 1876, 1877, 1878, 1879, 1880, 1881, 1882, 2189, 2210, 2211, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 2209, 2206, 2206, 2199, 0, 1887, 1903, 1947, - 1960, 1960, 1912, 1714, 1715, 1888, 0, 0, 1940, 1944, - 0, 0, 0, 0, 0, 0, 1434, 2325, 0, 171, - 2205, 2156, 2054, 2055, 2056, 2057, 2058, 2059, 2060, 2061, - 2062, 2063, 2064, 2065, 2066, 2067, 2068, 2069, 2070, 2071, - 2072, 2073, 2074, 2075, 2076, 2077, 2078, 2079, 2080, 2081, - 2082, 0, 0, 2165, 0, 0, 0, 0, 2158, 2159, - 0, 0, 0, 2042, 0, 0, 2048, 2049, 2050, 0, - 1783, 0, 2121, 2094, 2112, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 2083, 2084, 2085, - 2086, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 1019, 1028, 1154, 1156, 0, 941, 943, 944, - 984, 1026, 1026, 986, 0, 956, 1002, 0, 0, 0, - 127, 132, 0, 1537, 118, 0, 0, 0, 118, 0, - 0, 0, 118, 0, 0, 0, 1356, 1469, 89, 1355, - 1471, 0, 0, 0, 0, 0, 0, 0, 373, 374, - 0, 0, 368, 356, 2578, 358, 0, 0, 0, 0, - 0, 0, 0, 1232, 0, 0, 0, 1263, 0, 0, - 0, 1300, 1301, 0, 615, 1366, 0, 0, 0, 1382, - 1438, 1451, 0, 0, 0, 1543, 1286, 1291, 1292, 1293, - 1287, 1288, 1294, 1295, 874, 883, 0, 878, 0, 884, - 0, 0, 917, 937, 912, 0, 921, 0, 0, 0, - 1013, 0, 0, 1169, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 1161, 0, 0, 0, 1160, 0, 0, - 0, 0, 0, 0, 0, 1015, 1196, 0, 1198, 1199, - 1173, -2, 0, 1124, 1126, 0, 1178, 2161, 0, 0, - 1545, 1015, 508, 509, 510, 520, 522, 0, 581, 581, - 581, 581, 564, 0, 0, 552, 2346, 0, 0, 0, - 0, 561, 2346, 0, 0, 2346, 2346, 2346, 2346, 2346, - 2346, 2346, 2346, 0, 0, 2346, 2346, 2346, 2346, 2346, - 2346, 2346, 2346, 2346, 2346, 2346, 0, 2346, 2346, 2346, - 2346, 2346, 1698, 2346, 0, 1468, 571, 572, 573, 574, - 579, 580, 0, 0, 525, 526, 0, 0, 0, 0, - 0, 610, 0, 0, 1299, 0, 615, 0, 0, 1344, - 0, 0, 1126, 0, 0, 0, 0, 309, 310, 298, - 0, 299, 0, 0, 312, 313, 0, 315, 316, 317, - 324, 2437, 2556, 319, 321, 0, 0, 325, 338, 339, - 340, 0, 0, 330, 331, 0, 0, 394, 395, 397, - 0, 1026, 1066, 1032, 1033, 1036, 0, 0, 1069, 1071, - 1072, 1073, 1074, 1470, 1458, 85, 86, 2761, 819, 0, - 0, 822, 823, 824, 828, 0, 0, 1571, 832, 833, - 0, 0, 1353, 0, 1357, 1359, 1458, 1121, 0, 1118, - 1201, 0, 1203, 0, 0, 150, 18, 0, 143, 140, - 0, 0, 0, 0, 0, 2303, 2241, 2342, 0, 0, - 0, 0, 2323, 0, 0, 2263, 0, 0, 0, 133, - 156, 963, 1026, 956, 0, 958, 182, 956, 992, 892, - 937, 865, 0, 921, 0, 0, 0, 0, 0, 1720, - 0, 0, 0, 0, 1792, 0, 444, 440, 460, 0, - 409, 0, 0, 471, 0, 1717, 477, 480, 685, 0, - 687, 690, 773, 0, 693, 694, 773, 0, 0, 700, - 705, 713, 711, 0, 715, 0, 716, 0, 1374, 773, - 724, 725, 1113, 748, 749, 0, 0, 0, 1113, 773, - 773, 761, 776, 785, 0, 786, 1729, 0, 220, 1369, - 0, 221, 225, 215, 0, 0, 0, 1371, 1376, 0, - 235, 0, 0, 210, 211, 1528, 1537, 0, 0, 0, - 158, 2175, 2178, 2181, 2184, 2176, 2179, 2182, 2185, 2187, - 0, 2190, 2206, 2206, 2200, 0, 2191, 2206, 2206, 2201, - 0, 0, 2193, 0, 2196, 0, 1948, 1961, 1962, 1949, - 2166, 1895, 0, 1945, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 1036, 0, 0, 0, 2030, 2032, 0, - 0, 0, 2037, 0, 2039, 2040, 2041, 2043, 0, 0, - 0, 2047, 0, 2092, 2113, 2096, 2099, 0, 2103, 0, - 2105, 2107, 2108, 2109, 0, 0, 0, 1028, 1030, 1030, - 1028, 0, 0, 1987, 1987, 1030, 0, 0, 0, 0, - 1987, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 1917, 0, 1918, 1919, 1920, - 0, 1922, 2857, 0, 2857, 0, 2857, 2857, 0, 0, - 0, 1026, 1019, 0, 1157, 956, 956, 0, 987, 996, - 0, 0, 0, 0, 0, 0, 1535, 0, 108, 0, - 113, 0, 0, 109, 114, 0, 0, 111, 0, 0, - 120, 90, 0, 0, 1476, 1477, 0, 0, 0, 375, - 363, 365, 0, 357, 0, 1456, 1457, 0, 0, 0, - 1617, 1627, 1236, 0, 0, -2, 1265, 0, 1015, 1015, - 1311, 2346, 0, 619, 0, 0, 1368, 0, 1333, 0, - 0, 0, -2, 0, 0, -2, 0, 0, 1547, 0, - 875, 881, 0, 0, 0, 876, 892, 918, 0, 911, - 934, 0, 938, 0, 0, 925, 927, 916, 922, 0, - 0, 0, 952, 892, 892, 23, 1016, 0, 0, 0, - 845, 849, 0, 1165, 1165, 1165, 1134, 0, 0, 1137, - 1162, 1138, 1163, 0, 0, 1142, 0, 1144, 0, 1146, - 0, 1140, 1141, 1148, 1149, 0, 1139, 1172, 1197, 0, - 1174, 0, 1177, 1179, 1180, 1186, 0, 0, 0, 0, - 0, 0, 504, 505, 1547, 0, 0, 1467, 1614, 0, - 0, 551, 582, 0, 0, 0, 1581, 542, 0, 1742, - 1743, 0, 1750, 1759, 1759, 1455, 1832, 0, 1759, 0, - 0, 1761, 1762, 0, 0, 0, 0, 565, 566, 0, - 0, 550, 0, 0, 592, 0, 0, 0, 0, 0, - 2347, 2346, 2346, 0, 559, 560, 0, 563, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 2346, 2346, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 1689, 0, 0, 0, 0, 0, 0, 0, 1704, 1705, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 1311, - 2346, 0, 0, 0, 0, 619, 1363, 1363, 1331, 1349, - 0, 513, 514, 589, 0, 0, 0, 302, 303, 0, - 0, 308, 326, 328, 300, 0, 0, 0, 327, 329, - 333, 334, 393, 396, 398, 956, 0, 1039, 1037, 1038, - 1059, 1067, 82, 1459, 820, 821, 0, 1571, 831, 1572, - 1573, 1265, 837, 1354, 0, 1361, 1113, 1122, 0, 1116, - 1202, 1204, 171, 1119, 0, 153, 18, 152, 144, 145, - 0, 18, 0, 0, 0, 0, 2245, 2331, 2330, 2295, - 0, 2296, 2328, 2333, 0, 2336, 0, 485, 0, 967, - 0, 956, 993, 959, 0, 991, 863, 0, 864, 0, - 0, 869, 0, 952, 1358, 1721, 1722, 0, 1725, 1726, - 1727, 1728, 1719, 0, 0, 0, 0, 0, 0, 0, - 795, 181, 473, 689, 1113, 773, 1113, 0, 773, 773, - 701, 1624, 1625, 712, 718, 1373, 1113, 742, 1023, 1024, - 754, 756, 1729, 1113, 1113, 773, 791, 787, 0, 0, - 0, 0, 0, 826, 0, 217, 0, 0, 237, 0, - 0, 0, 1537, 1532, 2160, 2194, 2197, 0, 2195, 2198, - 0, 0, 2207, 2202, 1889, 1899, 1941, 0, 0, 0, - 0, 0, 1950, 1951, 2329, 2329, 1954, 2325, 2327, 2325, - 1960, 1960, 0, 1435, 0, 1436, 1036, 172, 0, 0, - 0, 0, 1987, 0, 0, 0, 1784, 0, 0, 0, - 0, 0, 1983, 1987, 0, 1987, 1985, 1987, 1987, 2005, - 1988, 2006, 1987, 1987, 1987, 1987, 1987, 2012, 1987, 1987, - 1987, 1987, 1987, 1987, 1987, 1987, 0, 1987, 1987, 1987, - 1987, 0, 1989, 1992, 1921, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 2230, 2231, 956, 1026, 942, - 988, 985, 0, 0, 0, 0, 0, 71, 71, 1537, - 0, 0, 0, 0, 0, 126, 0, 0, 0, 0, - 0, 0, 0, 1485, 1493, 0, 367, 0, 91, 92, - 94, 0, 0, 0, 0, 0, 0, 0, 107, 1229, - 0, 1230, 0, 1240, 0, 1234, 0, 0, 1251, 1252, - 1254, 0, 1257, 1258, 1259, 0, 1266, 1267, 1269, 1270, - 0, 2352, -2, -2, 2483, -2, -2, -2, -2, -2, + -2, -2, -2, -2, 2421, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, - 2411, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, - -2, -2, -2, -2, -2, -2, -2, -2, -2, 1264, - 0, 0, 1315, 1312, 1313, 1314, 0, 0, 1363, 620, - 621, 622, 623, 0, 0, 0, 1367, 0, 0, 0, - 1324, 0, 0, 0, 1439, 1440, 1441, 1442, 1443, 1444, - 1445, 1446, 1447, 1448, -2, 1462, 0, 1729, 0, 0, - 1735, 1517, 0, 0, 0, 1524, 0, 0, 981, 0, - -2, -2, 1735, 1515, 2346, 1735, 1555, 0, 1544, 0, - 0, 879, 888, 0, 937, 939, 0, 0, 937, 0, - 0, 0, 948, 0, 949, 0, 0, 889, 890, 892, - 0, 1170, 945, 0, -2, 0, 0, 847, 0, 0, - 1131, 1166, 0, 0, 1132, 1133, 1135, 1152, 1164, 0, - 1158, 1143, 1145, 1147, 1150, 0, 1125, 0, 1181, 0, - 0, 1183, 1184, 1185, 1176, 500, 0, 1619, 0, 1622, - 1623, 502, 0, 1629, 0, 1632, 1633, 1555, 1546, 0, - 521, 581, 581, 0, 1602, 0, 0, 1608, 0, 1615, - 0, 0, 0, 0, 0, 535, 0, 583, 0, 1749, - 1780, 1760, 1780, 1780, 1833, 1780, 1780, 1455, 0, 583, - 0, 0, 553, 549, 586, 1036, 536, 538, 539, 540, - 590, 591, 593, 0, 595, 596, 555, 567, 568, 569, - 570, 0, 0, 0, 562, 575, 576, 577, 578, 537, - 1665, 1666, 1667, 1668, 1671, 1672, 1673, 1674, 0, 0, - 1677, 1678, 1679, 1680, 1681, 1777, 1778, 1779, 1682, 1683, - 1684, 1685, 1686, 1687, 1688, 1706, 1707, 1708, 1709, 1710, - 1711, 1690, 1691, 1692, 1693, 1694, 1695, 1696, 1697, 0, - 0, 1701, 0, 0, 1234, 0, 529, 530, 0, 532, - 0, 0, 1315, 0, 0, 0, 0, 0, 1363, 613, - 0, 0, 614, 1333, 0, 1351, 0, 1345, 1346, 0, - 0, 909, 1113, 386, 0, 1113, 296, 305, 306, 307, - 314, 0, 639, 320, 1042, 1034, 1035, 0, 829, 830, - 0, 0, 1104, 1123, 0, 148, 151, 0, 146, 0, - 0, 0, 0, 138, 136, 2324, 0, 0, 157, 969, - 197, 0, 0, 1042, 961, 0, 183, 893, 0, 937, - 946, 0, 1730, 1731, 1732, 1733, 0, 1793, 431, 691, - 1113, 695, 773, 1113, 1113, 0, 719, 759, 763, 760, - 1113, 0, 1370, 217, 222, 223, 224, 218, 216, 1377, - 0, 1379, 0, 1530, 0, 0, 2203, 2204, 2192, 1946, - 1900, 0, 1902, 1904, 1952, 1953, 1955, 1956, 1957, 1958, - 1959, 1905, 0, 1437, 2031, 2033, 0, 2035, 2036, 2038, - 2044, 2045, 0, 0, 2100, 2104, 0, 0, 2091, 0, - 0, 2000, 0, 2001, 0, 0, 2003, 2004, 2007, 2008, - 2009, 2010, 2011, 2013, 2014, 2015, 2016, 2017, 2018, 2019, - 2020, 0, 2022, 2023, 2024, 2025, 0, 0, 1981, 1993, - 1923, 2858, 2857, 0, 1926, 2857, 0, 1929, 1930, 2859, - 0, 0, 0, 0, 0, 0, 1042, 956, 1027, 0, - 0, 1005, 1006, 1007, 1008, 1009, 1001, 0, 0, 73, - 0, 73, 1536, 1538, 119, 121, 0, 115, 116, 117, - 0, 0, 1200, 1499, 1729, 1487, 0, 1479, 0, 1493, - 0, 0, 0, 93, 0, 95, 0, 2539, 0, 0, - 0, 0, 1458, 0, 0, 1242, 0, 0, 1233, 0, - 1244, 1260, 1256, 0, 0, 0, 1271, 0, 0, 1282, - 0, 0, 1303, 1304, 1305, 1329, 1317, 0, 625, 626, - 0, 0, 0, 638, 634, 635, 636, 616, 1362, 1340, - 0, 0, 1340, 1327, 0, 0, 1339, 0, 1463, 2346, - 2346, 2346, 1499, 0, 0, 0, 1736, 1737, 1739, 1740, - 1741, 1519, 0, 1523, 1501, 0, 1028, 0, 0, 0, - 1558, 0, 0, 1549, 0, 0, 886, 0, 935, 0, - 940, 937, 915, 926, 914, 928, 923, 924, 953, 0, - 950, 891, 1014, 1195, 0, 0, 1113, 846, 849, 850, - 0, 1167, 0, 1136, 0, 1159, 1151, 1127, 1190, 1187, - 0, 0, 0, 0, 0, 0, 0, 0, 1558, 507, - 1579, 0, 0, 0, 0, 0, 1612, 0, 1400, 1400, - 1400, 1400, 1400, 0, 0, 597, 0, 601, 602, 599, - 1827, 0, 0, 600, 0, 1583, 0, 554, 584, 0, - -2, 0, 1781, 0, 0, 1763, 1781, 0, 0, 1780, - 0, 543, 583, 0, 0, 588, 0, 594, 1669, 1670, - 0, 1675, 1676, 0, 1700, 0, 0, 524, 527, 0, - 1238, 1239, -2, 0, 0, 0, 605, 0, 0, 0, - 606, 607, 612, 1364, 1365, 1324, 0, 1340, 0, 1350, - 0, 1347, 1348, 1028, 0, 0, 0, 892, 322, 983, - 0, 0, 1040, 1041, 835, 0, 1102, 0, 1120, 0, - 18, 0, 0, 141, 2334, 2337, 971, 0, 968, 198, - 0, 0, 0, 989, 963, 0, 960, 937, 868, 867, - 0, 1724, 1734, 692, 1113, 697, 698, 0, 764, 219, - 214, 1378, 1540, 0, 1531, 0, 1852, 1914, 1915, 1916, - 0, 2046, 1785, 0, 0, 1030, 1984, 0, 1987, 1986, - 0, 1987, 1990, 1028, 1982, 0, 0, 0, 0, 0, - 2857, 0, 1932, 0, 1934, 1935, 1936, 0, 1938, 1939, - 2300, 1042, 1003, 0, 999, 0, 69, 0, 72, 70, - 0, 0, 0, 125, 1474, 0, 1499, 1478, 0, 0, - 0, 1480, 0, 0, 0, 0, 0, 96, 0, 0, - 0, 0, 0, 0, 105, 0, 0, 0, 0, 1241, - 0, 1235, 0, 0, 1253, 1255, 0, 1268, 0, 1289, - 1289, 1316, 1302, 0, 1283, 0, 0, 627, 628, 0, - 631, 637, 1318, 0, 0, 1321, 1322, 1320, 1323, 0, - 0, 1337, 0, 0, 0, 0, 1449, 0, 1452, -2, - 0, 1521, 1499, 0, 0, 1019, 0, 1617, -2, 1564, - 0, 1556, 0, 1548, 0, 1551, 885, 0, 0, 920, - 936, 913, 0, 0, 1113, 1113, -2, 892, 848, 0, - 896, 898, 0, 0, 1153, 0, 0, 1191, 1193, 1188, - 1189, 1620, 501, 1621, 1630, 503, 1631, 1634, 1635, 1564, - 1581, 0, 1603, 0, 0, 0, 0, 0, 0, 0, - 515, 516, 517, 518, 519, 0, 547, 548, 1400, 0, - 1828, 1829, 1582, 0, 1596, 585, 1782, -2, 1796, 1798, - 0, 0, 1468, 1802, 1803, 0, 0, 0, 0, 0, - 0, 0, 0, 1860, 1813, 0, 0, 0, 1818, 1819, - 1820, 0, 0, 0, 0, 2224, 2225, 0, 1831, 0, - 0, 0, 0, 0, 0, 0, 0, 1757, 544, 545, - 587, 541, 2346, 557, 1699, 1702, 1703, 528, 531, 0, - 0, 611, 608, 609, 1327, 1332, 1343, 1352, 910, 1017, - 1113, 387, 388, 384, 1043, 1044, 0, 1360, 1994, 0, - 0, 142, 147, 0, 0, 973, 0, 970, 0, 964, - 966, 208, 967, 962, 866, 1723, 696, 773, 166, 200, - 0, 0, 1901, 0, 0, 0, 2034, 2089, 2090, 1987, - 0, 2002, 1987, 2026, 1978, 0, 1924, 2857, 1927, 2857, - 0, 2860, 0, 0, 2301, 0, 997, 74, 123, 122, - 0, 0, 1475, 0, 0, 0, 1491, 1492, 0, 1494, - 1495, 1496, 0, 0, 0, 0, 78, 0, 0, 0, - 1458, 0, 1458, 0, 0, 0, 1618, 1628, 0, 1243, - 1237, 1247, 1261, 0, 1274, 1272, 1281, 1296, 1280, 0, - 0, 0, 624, 629, 0, 632, 633, 1341, 1340, 0, - 1325, 1326, 0, 1335, 0, 0, 1464, 1465, 1466, 1329, - 1574, 1518, 0, 0, -2, 1662, 0, 1738, 1525, 1502, - 0, 982, 1509, 1510, 1511, 1512, 1513, 1514, 1540, 2346, - 1616, 1574, 1552, 0, 1559, 0, 1557, 1550, 0, 919, - 929, 951, 1028, 892, 843, 0, 899, 907, 0, 0, - 906, 1168, 1182, 0, 1561, 523, 1614, 1604, 0, 1606, - 0, 0, 0, 0, 1401, 2346, 2346, 0, 0, 0, - 1407, 1408, 2346, 2346, 2346, 2346, 2346, 2346, 0, 0, - 0, 2346, 2346, 2346, 2346, 2346, 2346, 0, 1426, 1427, - 1428, 0, 2346, 2346, 2346, 546, 598, 0, 1584, -2, - 0, 1797, 1799, 1800, 1801, 1804, 1805, 1806, 1807, 1808, - 1865, 1866, 1867, 0, 0, 1811, 1862, 1863, 1864, 1812, - 0, 0, 0, 1817, 0, 0, 0, 2222, 2223, 1858, - 0, 0, 0, 1764, 1766, 1767, 1768, 1769, 1770, 1771, - 1772, 1773, 1774, 1775, 1776, 1765, 0, 0, 0, 1756, - 1758, 0, 2346, 0, 0, 1342, 892, 892, 0, 1087, - 0, 1103, 1111, 2779, 2781, 2778, 137, 139, 154, 0, - 972, 199, 0, 969, 1113, 168, 0, 191, 0, 1541, - 0, 1913, 0, 0, 0, 0, 1999, 1031, 2021, 1991, - 1979, 0, 1973, 1974, 1975, 1980, 0, 0, 1931, 1933, - 1937, 1004, 0, 1500, 1488, 1489, 1490, 1486, 0, 0, - 1497, 1498, 0, 76, 0, 99, 0, 0, 100, 1458, - 101, 1458, 0, 0, 1231, 0, 0, 1297, 1298, 1306, - 1307, 0, 1309, 1310, 1330, 630, 1319, 1328, 1334, 1337, - 0, 1400, 1450, 1576, 0, 1520, 0, 1467, 1664, 2346, - 1329, 1527, 0, 1576, 1542, 0, 1554, 0, 1566, 0, - 1560, 0, 1017, 844, 897, 0, 0, 907, 907, 1192, - 1194, 506, 0, 1563, 1600, 1605, 1607, 1609, 0, 1613, - 1611, 0, 0, 1404, 1405, 0, 0, 0, 0, 0, - 0, 0, 0, 1416, 1417, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 603, 604, 1585, -2, - 0, 1593, 1595, 0, 0, 1809, 1810, 0, 0, 2110, - 2346, 0, 0, 1847, 0, 1400, 1400, 1400, 1400, 1400, - 0, 556, 0, 0, 534, 383, 385, 1045, 1995, 1996, - 0, 1105, 0, 0, 0, 0, 0, 0, 971, 699, - 155, 0, 167, 188, 0, 201, 202, 0, 0, 0, - 0, 1533, 0, 1855, 1857, 1856, 1976, 0, 0, 0, - 0, 0, 0, 2226, 2227, 2228, 1925, 1928, 124, 1493, - 1493, 1458, 78, 0, 98, 0, 102, 103, 0, 1458, - 0, 1273, 0, 1308, 1336, 1338, 1399, 1508, 0, 1614, - 1522, 1663, 0, 1526, 1627, 1637, 1651, 1644, 1649, 1650, - 1516, 1553, 1565, 0, 1568, 887, 892, 0, 908, 0, - 0, 1562, 1580, 0, 1610, 1402, 1403, 1406, 1409, 1410, - 1411, 1412, 1413, 1414, 0, 1418, 1419, 1420, 1421, 1422, - 1423, 1424, 1425, 1429, 1430, 1431, 1432, 1433, 1586, 1594, - 0, 1589, 0, 0, 0, 1861, 0, 1816, 0, 0, - 1824, 1835, 1848, 0, 0, 1744, 0, 1746, 1747, 0, - 1751, 0, 1753, 0, 0, 558, 533, 0, 0, 1112, - 0, 0, 0, 0, 975, 965, 973, 169, 173, 0, - 197, 194, 0, 203, 0, 0, 0, 0, 1529, 0, - 1853, 0, 1963, 0, 0, 0, 0, 1968, 1969, 1970, - 1971, 1972, 1481, 1493, 1482, 1493, 75, 77, 79, 97, - 1458, 104, 0, 1275, 1276, 1290, 0, 1602, 1669, 1626, - 1636, 0, 1642, 0, 1643, 0, 1567, 842, 0, 901, - 0, 0, 1601, 1415, 1588, 0, -2, 1597, 0, 0, - 2163, 2172, 2173, 1814, 1821, 0, 1823, 1825, 1826, 1834, - 1836, 1837, 0, 1849, 1850, 1851, 1859, 1400, 1400, 1400, - 1400, 1755, 1997, 1992, 0, 2161, 1107, 1108, 974, 0, - 957, 160, 0, 0, 192, 193, 195, 0, 204, 0, - 206, 207, 0, 0, 0, 1964, 1965, 1966, 1967, 1483, - 1484, 106, 1277, 1577, 0, 1579, 0, 0, 0, 0, - 0, 0, 1590, -2, 0, 1599, 0, 1815, 1824, 1838, - 0, 1839, 0, 0, 0, 1745, 1748, 1752, 1754, 0, - 1106, 0, 0, 976, 1539, 0, 174, 0, 176, 178, - 179, 189, 190, 196, 205, 0, 0, 1977, 1262, 1278, - 0, 0, 1581, 0, 1639, 0, 1656, 1657, 0, 1653, - 0, 0, 1646, 0, 900, 0, 0, 0, 0, 1598, - 2164, 1822, 1840, 1842, 1843, 0, 0, 1841, 1998, 2161, - 1109, 161, 162, 0, 175, 0, 1534, 1854, 1279, 1578, - 1575, 1638, 0, 0, 1652, 0, 0, 1645, 0, 0, - 0, 930, 0, 0, 0, 1844, 1846, 1845, 0, 0, - 0, 177, 1640, 1641, 1658, 1659, 1654, 1655, 1647, 1648, - 0, 0, 0, 0, 0, 0, 1110, 163, 164, 165, - 0, 932, 931, 904, 0, 905, 0, 0, 0, 0, - 933, 0, 902, 903, + -2, -2, -2, 1264, 0, 0, 1324, 1321, 1322, 1323, + 0, 0, 1372, 620, 621, 622, 623, 0, 0, 0, + 1376, 0, 0, 0, 1333, 0, 0, 0, 1448, 1449, + 1450, 1451, 1452, 1453, 1454, 1455, 1456, 1457, -2, 1471, + 0, 1738, 0, 0, 1744, 1526, 0, 0, 0, 1533, + 0, 0, 981, 0, -2, -2, 1744, 1524, 2355, 1744, + 1564, 0, 1553, 0, 0, 879, 888, 0, 937, 939, + 0, 0, 937, 0, 0, 0, 948, 0, 949, 0, + 0, 889, 890, 892, 0, 1170, 945, 0, -2, 0, + 0, 847, 0, 0, 1131, 1166, 0, 0, 1132, 1133, + 1135, 1152, 1164, 0, 1158, 1143, 1145, 1147, 1150, 0, + 1125, 0, 1181, 0, 0, 1183, 1184, 1185, 1176, 500, + 0, 1628, 0, 1631, 1632, 502, 0, 1638, 0, 1641, + 1642, 1564, 1555, 0, 521, 581, 581, 0, 1611, 0, + 0, 1617, 0, 1624, 0, 0, 0, 0, 0, 535, + 0, 583, 0, 1758, 1789, 1769, 1789, 1789, 1842, 1789, + 1789, 1464, 0, 583, 0, 0, 553, 549, 586, 1036, + 536, 538, 539, 540, 590, 591, 593, 0, 595, 596, + 555, 567, 568, 569, 570, 0, 0, 0, 562, 575, + 576, 577, 578, 537, 1674, 1675, 1676, 1677, 1680, 1681, + 1682, 1683, 0, 0, 1686, 1687, 1688, 1689, 1690, 1786, + 1787, 1788, 1691, 1692, 1693, 1694, 1695, 1696, 1697, 1715, + 1716, 1717, 1718, 1719, 1720, 1699, 1700, 1701, 1702, 1703, + 1704, 1705, 1706, 0, 0, 1710, 0, 0, 1234, 0, + 529, 530, 0, 532, 0, 0, 1324, 0, 0, 0, + 0, 0, 1372, 613, 0, 0, 614, 1342, 0, 1360, + 0, 1354, 1355, 0, 0, 909, 1113, 386, 0, 1113, + 296, 305, 306, 307, 314, 0, 639, 320, 1042, 1034, + 1035, 0, 829, 830, 0, 0, 1104, 1123, 0, 148, + 151, 0, 146, 0, 0, 0, 0, 138, 136, 2333, + 0, 0, 157, 969, 197, 0, 0, 1042, 961, 0, + 183, 893, 0, 937, 946, 0, 1739, 1740, 1741, 1742, + 0, 1802, 431, 691, 1113, 695, 773, 1113, 1113, 0, + 719, 759, 763, 760, 1113, 0, 1379, 217, 222, 223, + 224, 218, 216, 1386, 0, 1388, 0, 1539, 0, 0, + 2212, 2213, 2201, 1955, 1909, 0, 1911, 1913, 1961, 1962, + 1964, 1965, 1966, 1967, 1968, 1914, 0, 1446, 2040, 2042, + 0, 2044, 2045, 2047, 2053, 2054, 0, 0, 2109, 2113, + 0, 0, 2100, 0, 0, 2009, 0, 2010, 0, 0, + 2012, 2013, 2016, 2017, 2018, 2019, 2020, 2022, 2023, 2024, + 2025, 2026, 2027, 2028, 2029, 0, 2031, 2032, 2033, 2034, + 0, 0, 1990, 2002, 1932, 2871, 2870, 0, 1935, 2870, + 0, 1938, 1939, 2872, 0, 0, 0, 0, 0, 0, + 1042, 956, 1027, 0, 0, 1005, 1006, 1007, 1008, 1009, + 1001, 0, 0, 73, 0, 73, 1545, 1547, 119, 121, + 0, 115, 116, 117, 0, 0, 1200, 1508, 1738, 1496, + 0, 1488, 0, 1502, 0, 0, 0, 93, 0, 95, + 0, 2551, 0, 0, 0, 0, 1467, 0, 0, 1242, + 0, 0, 1233, 0, 1244, 1260, 1256, 0, 0, 0, + 1271, 0, 0, 1291, 0, 0, 1312, 1313, 1314, 1338, + 1326, 0, 625, 626, 0, 0, 0, 638, 634, 635, + 636, 616, 1371, 1349, 0, 0, 1349, 1336, 0, 0, + 1348, 0, 1472, 2355, 2355, 2355, 1508, 0, 0, 0, + 1745, 1746, 1748, 1749, 1750, 1528, 0, 1532, 1510, 0, + 1028, 0, 0, 0, 1567, 0, 0, 1558, 0, 0, + 886, 0, 935, 0, 940, 937, 915, 926, 914, 928, + 923, 924, 953, 0, 950, 891, 1014, 1195, 0, 0, + 1113, 846, 849, 850, 0, 1167, 0, 1136, 0, 1159, + 1151, 1127, 1190, 1187, 0, 0, 0, 0, 0, 0, + 0, 0, 1567, 507, 1588, 0, 0, 0, 0, 0, + 1621, 0, 1409, 1409, 1409, 1409, 1409, 0, 0, 597, + 0, 601, 602, 599, 1836, 0, 0, 600, 0, 1592, + 0, 554, 584, 0, -2, 0, 1790, 0, 0, 1772, + 1790, 0, 0, 1789, 0, 543, 583, 0, 0, 588, + 0, 594, 1678, 1679, 0, 1684, 1685, 0, 1709, 0, + 0, 524, 527, 0, 1238, 1239, -2, 0, 0, 0, + 605, 0, 0, 0, 606, 607, 612, 1373, 1374, 1333, + 0, 1349, 0, 1359, 0, 1356, 1357, 1028, 0, 0, + 0, 892, 322, 983, 0, 0, 1040, 1041, 835, 0, + 1102, 0, 1120, 0, 18, 0, 0, 141, 2343, 2346, + 971, 0, 968, 198, 0, 0, 0, 989, 963, 0, + 960, 937, 868, 867, 0, 1733, 1743, 692, 1113, 697, + 698, 0, 764, 219, 214, 1387, 1549, 0, 1540, 0, + 1861, 1923, 1924, 1925, 0, 2055, 1794, 0, 0, 1030, + 1993, 0, 1996, 1995, 0, 1996, 1999, 1028, 1991, 0, + 0, 0, 0, 0, 2870, 0, 1941, 0, 1943, 1944, + 1945, 0, 1947, 1948, 2309, 1042, 1003, 0, 999, 0, + 69, 0, 72, 70, 0, 0, 0, 125, 1483, 0, + 1508, 1487, 0, 0, 0, 1489, 0, 0, 0, 0, + 0, 96, 0, 0, 0, 0, 0, 0, 105, 0, + 0, 0, 0, 1241, 0, 1235, 0, 0, 1253, 1255, + 0, 1268, 0, 1298, 1298, 1325, 1311, 0, 1292, 0, + 0, 627, 628, 0, 631, 637, 1327, 0, 0, 1330, + 1331, 1329, 1332, 0, 0, 1346, 0, 0, 0, 0, + 1458, 0, 1461, -2, 0, 1530, 1508, 0, 0, 1019, + 0, 1626, -2, 1573, 0, 1565, 0, 1557, 0, 1560, + 885, 0, 0, 920, 936, 913, 0, 0, 1113, 1113, + -2, 892, 848, 0, 896, 898, 0, 0, 1153, 0, + 0, 1191, 1193, 1188, 1189, 1629, 501, 1630, 1639, 503, + 1640, 1643, 1644, 1573, 1590, 0, 1612, 0, 0, 0, + 0, 0, 0, 0, 515, 516, 517, 518, 519, 0, + 547, 548, 1409, 0, 1837, 1838, 1591, 0, 1605, 585, + 1791, -2, 1805, 1807, 0, 0, 1477, 1811, 1812, 0, + 0, 0, 0, 0, 0, 0, 0, 1869, 1822, 0, + 0, 0, 1827, 1828, 1829, 0, 0, 0, 0, 2233, + 2234, 0, 1840, 0, 0, 0, 0, 0, 0, 0, + 0, 1766, 544, 545, 587, 541, 2355, 557, 1708, 1711, + 1712, 528, 531, 0, 0, 611, 608, 609, 1336, 1341, + 1352, 1361, 910, 1017, 1113, 387, 388, 384, 1043, 1044, + 0, 1369, 2003, 0, 0, 142, 147, 0, 0, 973, + 0, 970, 0, 964, 966, 208, 967, 962, 866, 1732, + 696, 773, 166, 200, 0, 0, 1910, 0, 0, 0, + 2043, 2098, 2099, 1996, 0, 2011, 1996, 2035, 1987, 0, + 1933, 2870, 1936, 2870, 0, 2873, 0, 0, 2310, 0, + 997, 74, 123, 122, 0, 0, 1484, 0, 0, 0, + 1500, 1501, 0, 1503, 1504, 1505, 0, 0, 0, 0, + 78, 0, 0, 0, 1467, 0, 1467, 0, 0, 0, + 1627, 1637, 0, 1243, 1237, 1247, 1261, 0, 1274, 1272, + 1290, 1305, 1289, 0, 0, 0, 624, 629, 0, 632, + 633, 1350, 1349, 0, 1334, 1335, 0, 1344, 0, 0, + 1473, 1474, 1475, 1338, 1583, 1527, 0, 0, -2, 1671, + 0, 1747, 1534, 1511, 0, 982, 1518, 1519, 1520, 1521, + 1522, 1523, 1549, 2355, 1625, 1583, 1561, 0, 1568, 0, + 1566, 1559, 0, 919, 929, 951, 1028, 892, 843, 0, + 899, 907, 0, 0, 906, 1168, 1182, 0, 1570, 523, + 1623, 1613, 0, 1615, 0, 0, 0, 0, 1410, 2355, + 2355, 0, 0, 0, 1416, 1417, 2355, 2355, 2355, 2355, + 2355, 2355, 0, 0, 0, 2355, 2355, 2355, 2355, 2355, + 2355, 0, 1435, 1436, 1437, 0, 2355, 2355, 2355, 546, + 598, 0, 1593, -2, 0, 1806, 1808, 1809, 1810, 1813, + 1814, 1815, 1816, 1817, 1874, 1875, 1876, 0, 0, 1820, + 1871, 1872, 1873, 1821, 0, 0, 0, 1826, 0, 0, + 0, 2231, 2232, 1867, 0, 0, 0, 1773, 1775, 1776, + 1777, 1778, 1779, 1780, 1781, 1782, 1783, 1784, 1785, 1774, + 0, 0, 0, 1765, 1767, 0, 2355, 0, 0, 1351, + 892, 892, 0, 1087, 0, 1103, 1111, 2792, 2794, 2791, + 137, 139, 154, 0, 972, 199, 0, 969, 1113, 168, + 0, 191, 0, 1550, 0, 1922, 0, 0, 0, 0, + 2008, 1031, 2030, 2000, 1988, 0, 1982, 1983, 1984, 1989, + 0, 0, 1940, 1942, 1946, 1004, 0, 1509, 1497, 1498, + 1499, 1495, 0, 0, 1506, 1507, 0, 76, 0, 99, + 0, 0, 100, 1467, 101, 1467, 0, 0, 1231, 0, + 0, 1306, 1307, 1315, 1316, 0, 1318, 1319, 1339, 630, + 1328, 1337, 1343, 1346, 0, 1409, 1459, 1585, 0, 1529, + 0, 1476, 1673, 2355, 1338, 1536, 0, 1585, 1551, 0, + 1563, 0, 1575, 0, 1569, 0, 1017, 844, 897, 0, + 0, 907, 907, 1192, 1194, 506, 0, 1572, 1609, 1614, + 1616, 1618, 0, 1622, 1620, 0, 0, 1413, 1414, 0, + 0, 0, 0, 0, 0, 0, 0, 1425, 1426, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 603, 604, 1594, -2, 0, 1602, 1604, 0, 0, 1818, + 1819, 0, 0, 2119, 2355, 0, 0, 1856, 0, 1409, + 1409, 1409, 1409, 1409, 0, 556, 0, 0, 534, 383, + 385, 1045, 2004, 2005, 0, 1105, 0, 0, 0, 0, + 0, 0, 971, 699, 155, 0, 167, 188, 0, 201, + 202, 0, 0, 0, 0, 1542, 0, 1864, 1866, 1865, + 1985, 0, 0, 0, 0, 0, 0, 2235, 2236, 2237, + 1934, 1937, 124, 1502, 1502, 1467, 78, 0, 98, 0, + 102, 103, 0, 1467, 0, 1273, 0, 1317, 1345, 1347, + 1408, 1517, 0, 1623, 1531, 1672, 0, 1535, 1636, 1646, + 1660, 1653, 1658, 1659, 1525, 1562, 1574, 0, 1577, 887, + 892, 0, 908, 0, 0, 1571, 1589, 0, 1619, 1411, + 1412, 1415, 1418, 1419, 1420, 1421, 1422, 1423, 0, 1427, + 1428, 1429, 1430, 1431, 1432, 1433, 1434, 1438, 1439, 1440, + 1441, 1442, 1595, 1603, 0, 1598, 0, 0, 0, 1870, + 0, 1825, 0, 0, 1833, 1844, 1857, 0, 0, 1753, + 0, 1755, 1756, 0, 1760, 0, 1762, 0, 0, 558, + 533, 0, 0, 1112, 0, 0, 0, 0, 975, 965, + 973, 169, 173, 0, 197, 194, 0, 203, 0, 0, + 0, 0, 1538, 0, 1862, 0, 1972, 0, 0, 0, + 0, 1977, 1978, 1979, 1980, 1981, 1490, 1502, 1491, 1502, + 75, 77, 79, 97, 1467, 104, 0, 1275, 1276, 1299, + 0, 1611, 1678, 1635, 1645, 0, 1651, 0, 1652, 0, + 1576, 842, 0, 901, 0, 0, 1610, 1424, 1597, 0, + -2, 1606, 0, 0, 2172, 2181, 2182, 1823, 1830, 0, + 1832, 1834, 1835, 1843, 1845, 1846, 0, 1858, 1859, 1860, + 1868, 1409, 1409, 1409, 1409, 1764, 2006, 2001, 0, 2170, + 1107, 1108, 974, 0, 957, 160, 0, 0, 192, 193, + 195, 0, 204, 0, 206, 207, 0, 0, 0, 1973, + 1974, 1975, 1976, 1492, 1493, 106, 1277, 1586, 0, 1588, + 0, 0, 0, 0, 0, 0, 1599, -2, 0, 1608, + 0, 1824, 1833, 1847, 0, 1848, 0, 0, 0, 1754, + 1757, 1761, 1763, 0, 1106, 0, 0, 976, 1548, 0, + 174, 0, 176, 178, 179, 189, 190, 196, 205, 0, + 0, 1986, 1280, 1278, 0, 0, 1590, 0, 1648, 0, + 1665, 1666, 0, 1662, 0, 0, 1655, 0, 900, 0, + 0, 0, 0, 1607, 2173, 1831, 1849, 1851, 1852, 0, + 0, 1850, 2007, 2170, 1109, 161, 162, 0, 175, 0, + 1543, 1863, 1262, 0, 1282, 0, 0, 1279, 1587, 1584, + 1647, 0, 0, 1661, 0, 0, 1654, 0, 0, 0, + 930, 0, 0, 0, 1853, 1855, 1854, 0, 0, 0, + 177, 1281, 1285, 1286, 0, 0, 0, 1649, 1650, 1667, + 1668, 1663, 1664, 1656, 1657, 0, 0, 0, 0, 0, + 0, 1110, 163, 164, 165, 1283, 1284, 0, 0, 0, + 932, 931, 904, 0, 905, 1287, 0, 0, 0, 0, + 1288, 0, 933, 0, 902, 903, } var yyTok1 = [...]int{ @@ -13974,14 +14035,14 @@ var yyTok1 = [...]int{ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 123, 3, 3, 3, 156, 146, 3, 90, 91, 153, 151, 177, 152, 176, 154, 3, 3, - 3, 3, 3, 3, 3, 3, 3, 3, 783, 780, - 133, 132, 134, 3, 784, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 787, 784, + 133, 132, 134, 3, 788, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 158, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, - 3, 3, 3, 781, 145, 782, 159, + 3, 3, 3, 785, 145, 786, 159, } var yyTok2 = [...]int{ @@ -14115,7 +14176,7 @@ var yyTok3 = [...]int{ 58090, 765, 58091, 766, 58092, 767, 58093, 768, 58094, 769, 58095, 770, 58096, 771, 58097, 772, 58098, 773, 58099, 774, 58100, 775, 58101, 776, 58102, 777, 58103, 778, 58104, 779, - 0, + 58105, 780, 58106, 781, 58107, 782, 58108, 783, 0, } var yyErrorMessages = [...]struct { @@ -14465,7 +14526,7 @@ yydefault: case 3: yyDollar = yyS[yypt-1 : yypt+1] -//line mysql_sql.y:1074 +//line mysql_sql.y:1077 { if yyDollar[1].statementUnion() != nil { yylex.(*Lexer).AppendStmt(yyDollar[1].statementUnion()) @@ -14473,7 +14534,7 @@ yydefault: } case 4: yyDollar = yyS[yypt-3 : yypt+1] -//line mysql_sql.y:1080 +//line mysql_sql.y:1083 { yylex.(*Lexer).AppendTopLevelSemicolon(yyDollar[2].pos) if yyDollar[3].statementUnion() != nil { @@ -14483,7 +14544,7 @@ yydefault: case 5: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:1089 +//line mysql_sql.y:1092 { yyLOCAL = tree.NewCompoundStmt(yyDollar[2].statementsUnion()) } @@ -14491,7 +14552,7 @@ yydefault: case 6: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL []tree.Statement -//line mysql_sql.y:1095 +//line mysql_sql.y:1098 { yyLOCAL = []tree.Statement{yyDollar[1].statementUnion()} } @@ -14499,7 +14560,7 @@ yydefault: case 7: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL []tree.Statement -//line mysql_sql.y:1099 +//line mysql_sql.y:1102 { yyLOCAL = append(yyDollar[1].statementsUnion(), yyDollar[3].statementUnion()) } @@ -14507,7 +14568,7 @@ yydefault: case 17: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:1114 +//line mysql_sql.y:1117 { yyLOCAL = yyDollar[1].statementUnion() } @@ -14515,7 +14576,7 @@ yydefault: case 18: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:1118 +//line mysql_sql.y:1121 { yyLOCAL = tree.Statement(nil) } @@ -14523,7 +14584,7 @@ yydefault: case 19: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:1124 +//line mysql_sql.y:1127 { yyLOCAL = yyDollar[1].statementUnion() } @@ -14531,7 +14592,7 @@ yydefault: case 20: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:1128 +//line mysql_sql.y:1131 { yyLOCAL = yyDollar[1].statementUnion() } @@ -14539,7 +14600,7 @@ yydefault: case 22: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:1133 +//line mysql_sql.y:1136 { yyLOCAL = yyDollar[1].statementUnion() } @@ -14547,7 +14608,7 @@ yydefault: case 23: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:1137 +//line mysql_sql.y:1140 { yyLOCAL = tree.Statement(nil) } @@ -14555,7 +14616,7 @@ yydefault: case 58: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:1177 +//line mysql_sql.y:1180 { yyLOCAL = yyDollar[1].selectUnion() } @@ -14563,7 +14624,7 @@ yydefault: case 69: yyDollar = yyS[yypt-8 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:1193 +//line mysql_sql.y:1196 { var timestamp = yyDollar[2].str var isS3 = false @@ -14578,7 +14639,7 @@ yydefault: case 70: yyDollar = yyS[yypt-8 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:1204 +//line mysql_sql.y:1207 { var timestamp = yyDollar[2].str var isS3 = true @@ -14592,32 +14653,32 @@ yydefault: yyVAL.union = yyLOCAL case 71: yyDollar = yyS[yypt-0 : yypt+1] -//line mysql_sql.y:1216 +//line mysql_sql.y:1219 { yyVAL.str = "" } case 72: yyDollar = yyS[yypt-2 : yypt+1] -//line mysql_sql.y:1220 +//line mysql_sql.y:1223 { yyVAL.str = yyDollar[2].str } case 73: yyDollar = yyS[yypt-0 : yypt+1] -//line mysql_sql.y:1225 +//line mysql_sql.y:1228 { yyVAL.str = "" } case 74: yyDollar = yyS[yypt-2 : yypt+1] -//line mysql_sql.y:1229 +//line mysql_sql.y:1232 { yyVAL.str = yyDollar[2].str } case 75: yyDollar = yyS[yypt-12 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:1235 +//line mysql_sql.y:1238 { yyLOCAL = &tree.CreateCDC{ IfNotExists: yyDollar[3].ifNotExistsUnion(), @@ -14634,7 +14695,7 @@ yydefault: case 76: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL []string -//line mysql_sql.y:1250 +//line mysql_sql.y:1253 { yyLOCAL = yyDollar[1].strsUnion() } @@ -14642,7 +14703,7 @@ yydefault: case 77: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL []string -//line mysql_sql.y:1254 +//line mysql_sql.y:1257 { yyLOCAL = append(yyDollar[1].strsUnion(), yyDollar[3].strsUnion()...) } @@ -14650,7 +14711,7 @@ yydefault: case 78: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL []string -//line mysql_sql.y:1258 +//line mysql_sql.y:1261 { yyLOCAL = []string{} } @@ -14658,7 +14719,7 @@ yydefault: case 79: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL []string -//line mysql_sql.y:1262 +//line mysql_sql.y:1265 { yyLOCAL = append(yyLOCAL, yyDollar[1].str) yyLOCAL = append(yyLOCAL, yyDollar[3].str) @@ -14667,7 +14728,7 @@ yydefault: case 80: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:1269 +//line mysql_sql.y:1272 { yyLOCAL = &tree.ShowCDC{ Option: yyDollar[3].allCDCOptionUnion(), @@ -14677,7 +14738,7 @@ yydefault: case 81: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:1277 +//line mysql_sql.y:1280 { yyLOCAL = &tree.PauseCDC{ Option: yyDollar[3].allCDCOptionUnion(), @@ -14687,7 +14748,7 @@ yydefault: case 82: yyDollar = yyS[yypt-5 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:1285 +//line mysql_sql.y:1288 { yyLOCAL = tree.NewDropCDC(yyDollar[4].allCDCOptionUnion(), yyDollar[3].boolValUnion(), yyDollar[5].boolValUnion()) } @@ -14695,7 +14756,7 @@ yydefault: case 83: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL *tree.AllOrNotCDC -//line mysql_sql.y:1290 +//line mysql_sql.y:1293 { yyLOCAL = &tree.AllOrNotCDC{ All: true, @@ -14706,7 +14767,7 @@ yydefault: case 84: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL *tree.AllOrNotCDC -//line mysql_sql.y:1297 +//line mysql_sql.y:1300 { yyLOCAL = yyDollar[1].allCDCOptionUnion() } @@ -14714,7 +14775,7 @@ yydefault: case 85: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL *tree.AllOrNotCDC -//line mysql_sql.y:1303 +//line mysql_sql.y:1306 { yyLOCAL = yyDollar[1].allCDCOptionUnion() } @@ -14722,7 +14783,7 @@ yydefault: case 86: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL *tree.AllOrNotCDC -//line mysql_sql.y:1308 +//line mysql_sql.y:1311 { yyLOCAL = &tree.AllOrNotCDC{ All: false, @@ -14733,7 +14794,7 @@ yydefault: case 87: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL *tree.AllOrNotCDC -//line mysql_sql.y:1317 +//line mysql_sql.y:1320 { yyLOCAL = &tree.AllOrNotCDC{ All: true, @@ -14744,7 +14805,7 @@ yydefault: case 88: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL *tree.AllOrNotCDC -//line mysql_sql.y:1324 +//line mysql_sql.y:1327 { yyLOCAL = &tree.AllOrNotCDC{ All: false, @@ -14755,7 +14816,7 @@ yydefault: case 89: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:1333 +//line mysql_sql.y:1336 { yyLOCAL = &tree.ResumeCDC{ TaskName: tree.Identifier(yyDollar[4].cstrUnion().Compare()), @@ -14765,7 +14826,7 @@ yydefault: case 90: yyDollar = yyS[yypt-5 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:1341 +//line mysql_sql.y:1344 { yyLOCAL = &tree.RestartCDC{ TaskName: tree.Identifier(yyDollar[4].cstrUnion().Compare()), @@ -14775,7 +14836,7 @@ yydefault: case 91: yyDollar = yyS[yypt-6 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:1349 +//line mysql_sql.y:1352 { yyLOCAL = &tree.CreateSnapShot{ IfNotExists: yyDollar[3].ifNotExistsUnion(), @@ -14787,7 +14848,7 @@ yydefault: case 92: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.ObjectInfo -//line mysql_sql.y:1359 +//line mysql_sql.y:1362 { spLevel := tree.SnapshotLevelType{ Level: tree.SNAPSHOTLEVELCLUSTER, @@ -14801,7 +14862,7 @@ yydefault: case 93: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL tree.ObjectInfo -//line mysql_sql.y:1369 +//line mysql_sql.y:1372 { spLevel := tree.SnapshotLevelType{ Level: tree.SNAPSHOTLEVELACCOUNT, @@ -14815,7 +14876,7 @@ yydefault: case 94: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.ObjectInfo -//line mysql_sql.y:1379 +//line mysql_sql.y:1382 { spLevel := tree.SnapshotLevelType{ Level: tree.SNAPSHOTLEVELACCOUNT, @@ -14829,7 +14890,7 @@ yydefault: case 95: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL tree.ObjectInfo -//line mysql_sql.y:1389 +//line mysql_sql.y:1392 { spLevel := tree.SnapshotLevelType{ Level: tree.SNAPSHOTLEVELDATABASE, @@ -14843,7 +14904,7 @@ yydefault: case 96: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL tree.ObjectInfo -//line mysql_sql.y:1399 +//line mysql_sql.y:1402 { spLevel := tree.SnapshotLevelType{ Level: tree.SNAPSHOTLEVELTABLE, @@ -14857,7 +14918,7 @@ yydefault: case 97: yyDollar = yyS[yypt-7 : yypt+1] var yyLOCAL tree.ObjectInfo -//line mysql_sql.y:1409 +//line mysql_sql.y:1412 { spLevel := tree.SnapshotLevelType{ Level: tree.SNAPSHOTLEVELTABLE, @@ -14873,7 +14934,7 @@ yydefault: case 98: yyDollar = yyS[yypt-6 : yypt+1] var yyLOCAL tree.ObjectInfo -//line mysql_sql.y:1421 +//line mysql_sql.y:1424 { spLevel := tree.SnapshotLevelType{ Level: tree.SNAPSHOTLEVELDATABASE, @@ -14889,7 +14950,7 @@ yydefault: case 99: yyDollar = yyS[yypt-5 : yypt+1] var yyLOCAL tree.ObjectInfo -//line mysql_sql.y:1433 +//line mysql_sql.y:1436 { spLevel := tree.SnapshotLevelType{ Level: tree.SNAPSHOTLEVELACCOUNT, @@ -14904,7 +14965,7 @@ yydefault: case 100: yyDollar = yyS[yypt-10 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:1446 +//line mysql_sql.y:1449 { yyLOCAL = &tree.CreatePitr{ IfNotExists: yyDollar[3].ifNotExistsUnion(), @@ -14919,7 +14980,7 @@ yydefault: case 101: yyDollar = yyS[yypt-10 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:1457 +//line mysql_sql.y:1460 { yyLOCAL = &tree.CreatePitr{ IfNotExists: yyDollar[3].ifNotExistsUnion(), @@ -14934,7 +14995,7 @@ yydefault: case 102: yyDollar = yyS[yypt-11 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:1468 +//line mysql_sql.y:1471 { yyLOCAL = &tree.CreatePitr{ IfNotExists: yyDollar[3].ifNotExistsUnion(), @@ -14950,7 +15011,7 @@ yydefault: case 103: yyDollar = yyS[yypt-11 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:1480 +//line mysql_sql.y:1483 { yyLOCAL = &tree.CreatePitr{ IfNotExists: yyDollar[3].ifNotExistsUnion(), @@ -14966,7 +15027,7 @@ yydefault: case 104: yyDollar = yyS[yypt-12 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:1492 +//line mysql_sql.y:1495 { yyLOCAL = &tree.CreatePitr{ IfNotExists: yyDollar[3].ifNotExistsUnion(), @@ -14983,7 +15044,7 @@ yydefault: case 105: yyDollar = yyS[yypt-8 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:1505 +//line mysql_sql.y:1508 { yyLOCAL = &tree.CreatePitr{ IfNotExists: yyDollar[3].ifNotExistsUnion(), @@ -14998,7 +15059,7 @@ yydefault: case 106: yyDollar = yyS[yypt-13 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:1516 +//line mysql_sql.y:1519 { yyLOCAL = &tree.CreatePitr{ IfNotExists: yyDollar[3].ifNotExistsUnion(), @@ -15015,7 +15076,7 @@ yydefault: case 107: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL int64 -//line mysql_sql.y:1531 +//line mysql_sql.y:1534 { yyLOCAL = yyDollar[1].item.(int64) } @@ -15023,7 +15084,7 @@ yydefault: case 108: yyDollar = yyS[yypt-5 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:1538 +//line mysql_sql.y:1541 { var account tree.Identifier var database tree.Identifier @@ -15059,7 +15120,7 @@ yydefault: case 109: yyDollar = yyS[yypt-5 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:1570 +//line mysql_sql.y:1573 { var account tree.Identifier var database tree.Identifier @@ -15100,7 +15161,7 @@ yydefault: case 110: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:1607 +//line mysql_sql.y:1610 { yyLOCAL = &tree.RestoreSnapShot{ Level: tree.RESTORELEVELCLUSTER, @@ -15111,7 +15172,7 @@ yydefault: case 111: yyDollar = yyS[yypt-5 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:1614 +//line mysql_sql.y:1617 { result := &tree.RestoreSnapShot{ Level: tree.RESTORELEVELACCOUNT, @@ -15129,7 +15190,7 @@ yydefault: case 112: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.IdentifierList -//line mysql_sql.y:1630 +//line mysql_sql.y:1633 { yyLOCAL = tree.IdentifierList{tree.Identifier(yyDollar[1].cstrUnion().Compare())} } @@ -15137,7 +15198,7 @@ yydefault: case 113: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL tree.IdentifierList -//line mysql_sql.y:1634 +//line mysql_sql.y:1637 { yyLOCAL = tree.IdentifierList{ tree.Identifier(yyDollar[1].cstrUnion().Compare()), @@ -15148,7 +15209,7 @@ yydefault: case 114: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL tree.IdentifierList -//line mysql_sql.y:1643 +//line mysql_sql.y:1646 { yyLOCAL = tree.IdentifierList{ tree.Identifier(yyDollar[1].cstrUnion().Compare()), @@ -15159,7 +15220,7 @@ yydefault: case 115: yyDollar = yyS[yypt-5 : yypt+1] var yyLOCAL tree.IdentifierList -//line mysql_sql.y:1650 +//line mysql_sql.y:1653 { yyLOCAL = tree.IdentifierList{ tree.Identifier(yyDollar[1].cstrUnion().Compare()), @@ -15170,32 +15231,32 @@ yydefault: yyVAL.union = yyLOCAL case 116: yyDollar = yyS[yypt-5 : yypt+1] -//line mysql_sql.y:1660 +//line mysql_sql.y:1663 { yyVAL.str = yyDollar[4].cstrUnion().Compare() } case 117: yyDollar = yyS[yypt-5 : yypt+1] -//line mysql_sql.y:1664 +//line mysql_sql.y:1667 { yyVAL.str = strings.ToLower(yyDollar[4].str) } case 118: yyDollar = yyS[yypt-0 : yypt+1] -//line mysql_sql.y:1669 +//line mysql_sql.y:1672 { yyVAL.str = "" } case 119: yyDollar = yyS[yypt-3 : yypt+1] -//line mysql_sql.y:1673 +//line mysql_sql.y:1676 { yyVAL.str = yyDollar[3].cstrUnion().Compare() } case 120: yyDollar = yyS[yypt-5 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:1679 +//line mysql_sql.y:1682 { yyLOCAL = &tree.RestorePitr{ Level: tree.RESTORELEVELACCOUNT, @@ -15207,7 +15268,7 @@ yydefault: case 121: yyDollar = yyS[yypt-7 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:1687 +//line mysql_sql.y:1690 { yyLOCAL = &tree.RestorePitr{ Level: tree.RESTORELEVELDATABASE, @@ -15220,7 +15281,7 @@ yydefault: case 122: yyDollar = yyS[yypt-9 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:1696 +//line mysql_sql.y:1699 { yyLOCAL = &tree.RestorePitr{ Level: tree.RESTORELEVELDATABASE, @@ -15234,7 +15295,7 @@ yydefault: case 123: yyDollar = yyS[yypt-9 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:1706 +//line mysql_sql.y:1709 { yyLOCAL = &tree.RestorePitr{ Level: tree.RESTORELEVELTABLE, @@ -15248,7 +15309,7 @@ yydefault: case 124: yyDollar = yyS[yypt-11 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:1716 +//line mysql_sql.y:1719 { yyLOCAL = &tree.RestorePitr{ Level: tree.RESTORELEVELTABLE, @@ -15263,7 +15324,7 @@ yydefault: case 125: yyDollar = yyS[yypt-8 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:1727 +//line mysql_sql.y:1730 { yyLOCAL = &tree.RestorePitr{ Level: tree.RESTORELEVELACCOUNT, @@ -15277,7 +15338,7 @@ yydefault: case 126: yyDollar = yyS[yypt-6 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:1737 +//line mysql_sql.y:1740 { yyLOCAL = &tree.RestorePitr{ Level: tree.RESTORELEVELCLUSTER, @@ -15289,7 +15350,7 @@ yydefault: case 127: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:1747 +//line mysql_sql.y:1750 { var connectionId uint64 switch v := yyDollar[3].item.(type) { @@ -15312,7 +15373,7 @@ yydefault: case 128: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL tree.KillOption -//line mysql_sql.y:1767 +//line mysql_sql.y:1770 { yyLOCAL = tree.KillOption{ Exist: false, @@ -15322,7 +15383,7 @@ yydefault: case 129: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.KillOption -//line mysql_sql.y:1773 +//line mysql_sql.y:1776 { yyLOCAL = tree.KillOption{ Exist: true, @@ -15333,7 +15394,7 @@ yydefault: case 130: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.KillOption -//line mysql_sql.y:1780 +//line mysql_sql.y:1783 { yyLOCAL = tree.KillOption{ Exist: true, @@ -15344,7 +15405,7 @@ yydefault: case 131: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL tree.StatementOption -//line mysql_sql.y:1788 +//line mysql_sql.y:1791 { yyLOCAL = tree.StatementOption{ Exist: false, @@ -15354,7 +15415,7 @@ yydefault: case 132: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.StatementOption -//line mysql_sql.y:1794 +//line mysql_sql.y:1797 { yyLOCAL = tree.StatementOption{ Exist: true, @@ -15365,7 +15426,7 @@ yydefault: case 133: yyDollar = yyS[yypt-5 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:1803 +//line mysql_sql.y:1806 { yyLOCAL = &tree.CallStmt{ Name: yyDollar[2].procNameUnion(), @@ -15376,7 +15437,7 @@ yydefault: case 134: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:1812 +//line mysql_sql.y:1815 { yyLOCAL = &tree.LeaveStmt{ Name: tree.Identifier(yyDollar[2].cstrUnion().Compare()), @@ -15386,7 +15447,7 @@ yydefault: case 135: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:1820 +//line mysql_sql.y:1823 { yyLOCAL = &tree.IterateStmt{ Name: tree.Identifier(yyDollar[2].cstrUnion().Compare()), @@ -15396,7 +15457,7 @@ yydefault: case 136: yyDollar = yyS[yypt-6 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:1828 +//line mysql_sql.y:1831 { yyLOCAL = &tree.WhileStmt{ Name: "", @@ -15408,7 +15469,7 @@ yydefault: case 137: yyDollar = yyS[yypt-9 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:1836 +//line mysql_sql.y:1839 { yyLOCAL = &tree.WhileStmt{ Name: tree.Identifier(yyDollar[1].cstrUnion().Compare()), @@ -15420,7 +15481,7 @@ yydefault: case 138: yyDollar = yyS[yypt-6 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:1846 +//line mysql_sql.y:1849 { yyLOCAL = &tree.RepeatStmt{ Name: "", @@ -15432,7 +15493,7 @@ yydefault: case 139: yyDollar = yyS[yypt-9 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:1854 +//line mysql_sql.y:1857 { yyLOCAL = &tree.RepeatStmt{ Name: tree.Identifier(yyDollar[1].cstrUnion().Compare()), @@ -15444,7 +15505,7 @@ yydefault: case 140: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:1864 +//line mysql_sql.y:1867 { yyLOCAL = &tree.LoopStmt{ Name: "", @@ -15455,7 +15516,7 @@ yydefault: case 141: yyDollar = yyS[yypt-7 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:1871 +//line mysql_sql.y:1874 { yyLOCAL = &tree.LoopStmt{ Name: tree.Identifier(yyDollar[1].cstrUnion().Compare()), @@ -15466,7 +15527,7 @@ yydefault: case 142: yyDollar = yyS[yypt-8 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:1880 +//line mysql_sql.y:1883 { yyLOCAL = &tree.IfStmt{ Cond: yyDollar[2].exprUnion(), @@ -15479,7 +15540,7 @@ yydefault: case 143: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL []*tree.ElseIfStmt -//line mysql_sql.y:1890 +//line mysql_sql.y:1893 { yyLOCAL = nil } @@ -15487,7 +15548,7 @@ yydefault: case 144: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL []*tree.ElseIfStmt -//line mysql_sql.y:1894 +//line mysql_sql.y:1897 { yyLOCAL = yyDollar[1].elseIfClauseListUnion() } @@ -15495,7 +15556,7 @@ yydefault: case 145: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL []*tree.ElseIfStmt -//line mysql_sql.y:1900 +//line mysql_sql.y:1903 { yyLOCAL = []*tree.ElseIfStmt{yyDollar[1].elseIfClauseUnion()} } @@ -15503,7 +15564,7 @@ yydefault: case 146: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL []*tree.ElseIfStmt -//line mysql_sql.y:1904 +//line mysql_sql.y:1907 { yyLOCAL = append(yyDollar[1].elseIfClauseListUnion(), yyDollar[2].elseIfClauseUnion()) } @@ -15511,7 +15572,7 @@ yydefault: case 147: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL *tree.ElseIfStmt -//line mysql_sql.y:1910 +//line mysql_sql.y:1913 { yyLOCAL = &tree.ElseIfStmt{ Cond: yyDollar[2].exprUnion(), @@ -15522,7 +15583,7 @@ yydefault: case 148: yyDollar = yyS[yypt-6 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:1919 +//line mysql_sql.y:1922 { yyLOCAL = &tree.CaseStmt{ Expr: yyDollar[2].exprUnion(), @@ -15534,7 +15595,7 @@ yydefault: case 149: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL []*tree.WhenStmt -//line mysql_sql.y:1929 +//line mysql_sql.y:1932 { yyLOCAL = []*tree.WhenStmt{yyDollar[1].whenClause2Union()} } @@ -15542,7 +15603,7 @@ yydefault: case 150: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL []*tree.WhenStmt -//line mysql_sql.y:1933 +//line mysql_sql.y:1936 { yyLOCAL = append(yyDollar[1].whenClauseList2Union(), yyDollar[2].whenClause2Union()) } @@ -15550,7 +15611,7 @@ yydefault: case 151: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL *tree.WhenStmt -//line mysql_sql.y:1939 +//line mysql_sql.y:1942 { yyLOCAL = &tree.WhenStmt{ Cond: yyDollar[2].exprUnion(), @@ -15561,7 +15622,7 @@ yydefault: case 152: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL []tree.Statement -//line mysql_sql.y:1948 +//line mysql_sql.y:1951 { yyLOCAL = nil } @@ -15569,7 +15630,7 @@ yydefault: case 153: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL []tree.Statement -//line mysql_sql.y:1952 +//line mysql_sql.y:1955 { yyLOCAL = yyDollar[2].statementsUnion() } @@ -15577,7 +15638,7 @@ yydefault: case 154: yyDollar = yyS[yypt-10 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:1958 +//line mysql_sql.y:1961 { ep := &tree.ExportParam{ Outfile: true, @@ -15597,7 +15658,7 @@ yydefault: case 155: yyDollar = yyS[yypt-11 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:1976 +//line mysql_sql.y:1979 { yyLOCAL = &tree.Load{ Local: yyDollar[3].boolValUnion(), @@ -15613,7 +15674,7 @@ yydefault: case 156: yyDollar = yyS[yypt-5 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:1990 +//line mysql_sql.y:1993 { yyLOCAL = &tree.DumpTable{Table: yyDollar[3].tableNameUnion(), Path: yyDollar[5].str} } @@ -15621,7 +15682,7 @@ yydefault: case 157: yyDollar = yyS[yypt-7 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:1994 +//line mysql_sql.y:1997 { yyLOCAL = &tree.DumpTable{Table: yyDollar[3].tableNameUnion(), Path: yyDollar[5].str, MetadataOnly: true} } @@ -15629,7 +15690,7 @@ yydefault: case 158: yyDollar = yyS[yypt-5 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:2000 +//line mysql_sql.y:2003 { yyLOCAL = &tree.LoadTable{Table: yyDollar[3].tableNameUnion(), Path: yyDollar[5].str} } @@ -15637,7 +15698,7 @@ yydefault: case 159: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:2006 +//line mysql_sql.y:2009 { yyLOCAL = &tree.LoadExtension{ Name: tree.Identifier(yyDollar[2].str), @@ -15647,7 +15708,7 @@ yydefault: case 160: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL tree.UpdateExprs -//line mysql_sql.y:2013 +//line mysql_sql.y:2016 { yyLOCAL = nil } @@ -15655,7 +15716,7 @@ yydefault: case 161: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL tree.UpdateExprs -//line mysql_sql.y:2017 +//line mysql_sql.y:2020 { yyLOCAL = yyDollar[2].updateExprsUnion() } @@ -15663,7 +15724,7 @@ yydefault: case 162: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.UpdateExprs -//line mysql_sql.y:2023 +//line mysql_sql.y:2026 { yyLOCAL = tree.UpdateExprs{yyDollar[1].updateExprUnion()} } @@ -15671,7 +15732,7 @@ yydefault: case 163: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL tree.UpdateExprs -//line mysql_sql.y:2027 +//line mysql_sql.y:2030 { yyLOCAL = append(yyDollar[1].updateExprsUnion(), yyDollar[3].updateExprUnion()) } @@ -15679,7 +15740,7 @@ yydefault: case 164: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL *tree.UpdateExpr -//line mysql_sql.y:2033 +//line mysql_sql.y:2036 { yyLOCAL = &tree.UpdateExpr{ Names: []*tree.UnresolvedName{yyDollar[1].unresolvedNameUnion()}, @@ -15690,7 +15751,7 @@ yydefault: case 165: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL *tree.UpdateExpr -//line mysql_sql.y:2040 +//line mysql_sql.y:2043 { yyLOCAL = &tree.UpdateExpr{ Names: []*tree.UnresolvedName{yyDollar[1].unresolvedNameUnion()}, @@ -15701,7 +15762,7 @@ yydefault: case 166: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL int64 -//line mysql_sql.y:2048 +//line mysql_sql.y:2051 { yyLOCAL = -1 } @@ -15709,7 +15770,7 @@ yydefault: case 167: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL int64 -//line mysql_sql.y:2052 +//line mysql_sql.y:2055 { str := strings.ToLower(yyDollar[2].str) if str == "true" { @@ -15725,7 +15786,7 @@ yydefault: case 168: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL bool -//line mysql_sql.y:2064 +//line mysql_sql.y:2067 { yyLOCAL = true } @@ -15733,7 +15794,7 @@ yydefault: case 169: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL bool -//line mysql_sql.y:2068 +//line mysql_sql.y:2071 { str := strings.ToLower(yyDollar[2].str) if str == "true" { @@ -15749,7 +15810,7 @@ yydefault: case 170: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL *tree.UnresolvedName -//line mysql_sql.y:2082 +//line mysql_sql.y:2085 { yyLOCAL = tree.NewUnresolvedName(yyDollar[1].cstrUnion()) } @@ -15757,7 +15818,7 @@ yydefault: case 171: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL *tree.UnresolvedName -//line mysql_sql.y:2086 +//line mysql_sql.y:2089 { tblNameCStr := yylex.(*Lexer).GetDbOrTblNameCStr(yyDollar[1].cstrUnion().Origin()) yyLOCAL = tree.NewUnresolvedName(tblNameCStr, yyDollar[3].cstrUnion()) @@ -15766,7 +15827,7 @@ yydefault: case 172: yyDollar = yyS[yypt-5 : yypt+1] var yyLOCAL *tree.UnresolvedName -//line mysql_sql.y:2091 +//line mysql_sql.y:2094 { dbNameCStr := yylex.(*Lexer).GetDbOrTblNameCStr(yyDollar[1].cstrUnion().Origin()) tblNameCStr := yylex.(*Lexer).GetDbOrTblNameCStr(yyDollar[3].cstrUnion().Origin()) @@ -15776,7 +15837,7 @@ yydefault: case 173: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL []tree.LoadColumn -//line mysql_sql.y:2098 +//line mysql_sql.y:2101 { yyLOCAL = nil } @@ -15784,7 +15845,7 @@ yydefault: case 174: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL []tree.LoadColumn -//line mysql_sql.y:2102 +//line mysql_sql.y:2105 { yyLOCAL = nil } @@ -15792,7 +15853,7 @@ yydefault: case 175: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL []tree.LoadColumn -//line mysql_sql.y:2106 +//line mysql_sql.y:2109 { yyLOCAL = yyDollar[2].loadColumnsUnion() } @@ -15800,7 +15861,7 @@ yydefault: case 176: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL []tree.LoadColumn -//line mysql_sql.y:2112 +//line mysql_sql.y:2115 { switch yyDollar[1].loadColumnUnion().(type) { case *tree.UnresolvedName: @@ -15813,7 +15874,7 @@ yydefault: case 177: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL []tree.LoadColumn -//line mysql_sql.y:2121 +//line mysql_sql.y:2124 { switch yyDollar[3].loadColumnUnion().(type) { case *tree.UnresolvedName: @@ -15826,7 +15887,7 @@ yydefault: case 178: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.LoadColumn -//line mysql_sql.y:2132 +//line mysql_sql.y:2135 { yyLOCAL = yyDollar[1].unresolvedNameUnion() } @@ -15834,7 +15895,7 @@ yydefault: case 179: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.LoadColumn -//line mysql_sql.y:2136 +//line mysql_sql.y:2139 { yyLOCAL = yyDollar[1].varExprUnion() } @@ -15842,7 +15903,7 @@ yydefault: case 180: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL []*tree.VarExpr -//line mysql_sql.y:2142 +//line mysql_sql.y:2145 { yyLOCAL = []*tree.VarExpr{yyDollar[1].varExprUnion()} } @@ -15850,7 +15911,7 @@ yydefault: case 181: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL []*tree.VarExpr -//line mysql_sql.y:2146 +//line mysql_sql.y:2149 { yyLOCAL = append(yyDollar[1].varExprsUnion(), yyDollar[3].varExprUnion()) } @@ -15858,7 +15919,7 @@ yydefault: case 182: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL []*tree.VarExpr -//line mysql_sql.y:2152 +//line mysql_sql.y:2155 { yyLOCAL = []*tree.VarExpr{yyDollar[1].varExprUnion()} } @@ -15866,7 +15927,7 @@ yydefault: case 183: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL []*tree.VarExpr -//line mysql_sql.y:2156 +//line mysql_sql.y:2159 { yyLOCAL = append(yyDollar[1].varExprsUnion(), yyDollar[3].varExprUnion()) } @@ -15874,7 +15935,7 @@ yydefault: case 184: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL *tree.VarExpr -//line mysql_sql.y:2162 +//line mysql_sql.y:2165 { yyLOCAL = yyDollar[1].varExprUnion() } @@ -15882,7 +15943,7 @@ yydefault: case 185: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL *tree.VarExpr -//line mysql_sql.y:2166 +//line mysql_sql.y:2169 { yyLOCAL = yyDollar[1].varExprUnion() } @@ -15890,7 +15951,7 @@ yydefault: case 186: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL *tree.VarExpr -//line mysql_sql.y:2172 +//line mysql_sql.y:2175 { v := strings.ToLower(yyDollar[1].str) var isGlobal bool @@ -15912,7 +15973,7 @@ yydefault: case 187: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL *tree.VarExpr -//line mysql_sql.y:2192 +//line mysql_sql.y:2195 { // vs := strings.Split($1, ".") // var r string @@ -15934,7 +15995,7 @@ yydefault: case 188: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL int64 -//line mysql_sql.y:2211 +//line mysql_sql.y:2214 { yyLOCAL = 0 } @@ -15942,7 +16003,7 @@ yydefault: case 189: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL int64 -//line mysql_sql.y:2215 +//line mysql_sql.y:2218 { yyLOCAL = yyDollar[2].item.(int64) } @@ -15950,7 +16011,7 @@ yydefault: case 190: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL int64 -//line mysql_sql.y:2219 +//line mysql_sql.y:2222 { yyLOCAL = yyDollar[2].item.(int64) } @@ -15958,7 +16019,7 @@ yydefault: case 191: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL *tree.Lines -//line mysql_sql.y:2224 +//line mysql_sql.y:2227 { yyLOCAL = nil } @@ -15966,7 +16027,7 @@ yydefault: case 192: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL *tree.Lines -//line mysql_sql.y:2228 +//line mysql_sql.y:2231 { yyLOCAL = &tree.Lines{ StartingBy: yyDollar[2].str, @@ -15979,7 +16040,7 @@ yydefault: case 193: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL *tree.Lines -//line mysql_sql.y:2237 +//line mysql_sql.y:2240 { yyLOCAL = &tree.Lines{ StartingBy: yyDollar[3].str, @@ -15991,32 +16052,32 @@ yydefault: yyVAL.union = yyLOCAL case 194: yyDollar = yyS[yypt-0 : yypt+1] -//line mysql_sql.y:2247 +//line mysql_sql.y:2250 { yyVAL.str = "" } case 196: yyDollar = yyS[yypt-3 : yypt+1] -//line mysql_sql.y:2254 +//line mysql_sql.y:2257 { yyVAL.str = yyDollar[3].str } case 197: yyDollar = yyS[yypt-0 : yypt+1] -//line mysql_sql.y:2259 +//line mysql_sql.y:2262 { yyVAL.str = "\n" } case 199: yyDollar = yyS[yypt-3 : yypt+1] -//line mysql_sql.y:2266 +//line mysql_sql.y:2269 { yyVAL.str = yyDollar[3].str } case 200: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL *tree.Fields -//line mysql_sql.y:2271 +//line mysql_sql.y:2274 { yyLOCAL = nil } @@ -16024,7 +16085,7 @@ yydefault: case 201: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL *tree.Fields -//line mysql_sql.y:2275 +//line mysql_sql.y:2278 { res := &tree.Fields{ Terminated: &tree.Terminated{ @@ -16054,7 +16115,7 @@ yydefault: case 202: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL []*tree.Fields -//line mysql_sql.y:2303 +//line mysql_sql.y:2306 { yyLOCAL = []*tree.Fields{yyDollar[1].fieldsUnion()} } @@ -16062,7 +16123,7 @@ yydefault: case 203: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL []*tree.Fields -//line mysql_sql.y:2307 +//line mysql_sql.y:2310 { yyLOCAL = append(yyDollar[1].fieldsListUnion(), yyDollar[2].fieldsUnion()) } @@ -16070,7 +16131,7 @@ yydefault: case 204: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL *tree.Fields -//line mysql_sql.y:2313 +//line mysql_sql.y:2316 { yyLOCAL = &tree.Fields{ Terminated: &tree.Terminated{ @@ -16082,7 +16143,7 @@ yydefault: case 205: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL *tree.Fields -//line mysql_sql.y:2321 +//line mysql_sql.y:2324 { str := yyDollar[4].str if str != "\\" && len(str) > 1 { @@ -16106,7 +16167,7 @@ yydefault: case 206: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL *tree.Fields -//line mysql_sql.y:2341 +//line mysql_sql.y:2344 { str := yyDollar[3].str if str != "\\" && len(str) > 1 { @@ -16129,7 +16190,7 @@ yydefault: case 207: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL *tree.Fields -//line mysql_sql.y:2360 +//line mysql_sql.y:2363 { str := yyDollar[3].str if str != "\\" && len(str) > 1 { @@ -16152,7 +16213,7 @@ yydefault: case 209: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL tree.DuplicateKey -//line mysql_sql.y:2385 +//line mysql_sql.y:2388 { yyLOCAL = &tree.DuplicateKeyError{} } @@ -16160,7 +16221,7 @@ yydefault: case 210: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.DuplicateKey -//line mysql_sql.y:2389 +//line mysql_sql.y:2392 { yyLOCAL = &tree.DuplicateKeyIgnore{} } @@ -16168,7 +16229,7 @@ yydefault: case 211: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.DuplicateKey -//line mysql_sql.y:2393 +//line mysql_sql.y:2396 { yyLOCAL = &tree.DuplicateKeyReplace{} } @@ -16176,7 +16237,7 @@ yydefault: case 212: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL bool -//line mysql_sql.y:2398 +//line mysql_sql.y:2401 { yyLOCAL = false } @@ -16184,7 +16245,7 @@ yydefault: case 213: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL bool -//line mysql_sql.y:2402 +//line mysql_sql.y:2405 { yyLOCAL = true } @@ -16192,7 +16253,7 @@ yydefault: case 214: yyDollar = yyS[yypt-8 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:2408 +//line mysql_sql.y:2411 { yyLOCAL = &tree.Grant{ Typ: tree.GrantTypePrivilege, @@ -16209,7 +16270,7 @@ yydefault: case 215: yyDollar = yyS[yypt-5 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:2421 +//line mysql_sql.y:2424 { yyLOCAL = &tree.Grant{ Typ: tree.GrantTypeRole, @@ -16224,7 +16285,7 @@ yydefault: case 216: yyDollar = yyS[yypt-7 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:2432 +//line mysql_sql.y:2435 { yyLOCAL = &tree.Grant{ Typ: tree.GrantTypeProxy, @@ -16240,7 +16301,7 @@ yydefault: case 217: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL bool -//line mysql_sql.y:2445 +//line mysql_sql.y:2448 { yyLOCAL = false } @@ -16248,7 +16309,7 @@ yydefault: case 218: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL bool -//line mysql_sql.y:2449 +//line mysql_sql.y:2452 { yyLOCAL = true } @@ -16256,7 +16317,7 @@ yydefault: case 219: yyDollar = yyS[yypt-8 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:2459 +//line mysql_sql.y:2462 { yyLOCAL = &tree.Revoke{ Typ: tree.RevokeTypePrivilege, @@ -16273,7 +16334,7 @@ yydefault: case 220: yyDollar = yyS[yypt-5 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:2472 +//line mysql_sql.y:2475 { yyLOCAL = &tree.Revoke{ Typ: tree.RevokeTypeRole, @@ -16288,7 +16349,7 @@ yydefault: case 221: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL *tree.PrivilegeLevel -//line mysql_sql.y:2485 +//line mysql_sql.y:2488 { yyLOCAL = &tree.PrivilegeLevel{ Level: tree.PRIVILEGE_LEVEL_TYPE_STAR, @@ -16298,7 +16359,7 @@ yydefault: case 222: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL *tree.PrivilegeLevel -//line mysql_sql.y:2491 +//line mysql_sql.y:2494 { yyLOCAL = &tree.PrivilegeLevel{ Level: tree.PRIVILEGE_LEVEL_TYPE_STAR_STAR, @@ -16308,7 +16369,7 @@ yydefault: case 223: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL *tree.PrivilegeLevel -//line mysql_sql.y:2497 +//line mysql_sql.y:2500 { tblName := yylex.(*Lexer).GetDbOrTblName(yyDollar[1].cstrUnion().Origin()) yyLOCAL = &tree.PrivilegeLevel{ @@ -16320,7 +16381,7 @@ yydefault: case 224: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL *tree.PrivilegeLevel -//line mysql_sql.y:2505 +//line mysql_sql.y:2508 { dbName := yylex.(*Lexer).GetDbOrTblName(yyDollar[1].cstrUnion().Origin()) tblName := yylex.(*Lexer).GetDbOrTblName(yyDollar[3].cstrUnion().Origin()) @@ -16334,7 +16395,7 @@ yydefault: case 225: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL *tree.PrivilegeLevel -//line mysql_sql.y:2515 +//line mysql_sql.y:2518 { tblName := yylex.(*Lexer).GetDbOrTblName(yyDollar[1].cstrUnion().Origin()) yyLOCAL = &tree.PrivilegeLevel{ @@ -16346,7 +16407,7 @@ yydefault: case 226: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.ObjectType -//line mysql_sql.y:2525 +//line mysql_sql.y:2528 { yyLOCAL = tree.OBJECT_TYPE_TABLE } @@ -16354,7 +16415,7 @@ yydefault: case 227: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.ObjectType -//line mysql_sql.y:2529 +//line mysql_sql.y:2532 { yyLOCAL = tree.OBJECT_TYPE_DATABASE } @@ -16362,7 +16423,7 @@ yydefault: case 228: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.ObjectType -//line mysql_sql.y:2533 +//line mysql_sql.y:2536 { yyLOCAL = tree.OBJECT_TYPE_FUNCTION } @@ -16370,7 +16431,7 @@ yydefault: case 229: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.ObjectType -//line mysql_sql.y:2537 +//line mysql_sql.y:2540 { yyLOCAL = tree.OBJECT_TYPE_PROCEDURE } @@ -16378,7 +16439,7 @@ yydefault: case 230: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.ObjectType -//line mysql_sql.y:2541 +//line mysql_sql.y:2544 { yyLOCAL = tree.OBJECT_TYPE_VIEW } @@ -16386,7 +16447,7 @@ yydefault: case 231: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.ObjectType -//line mysql_sql.y:2545 +//line mysql_sql.y:2548 { yyLOCAL = tree.OBJECT_TYPE_ACCOUNT } @@ -16394,7 +16455,7 @@ yydefault: case 232: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL []*tree.Privilege -//line mysql_sql.y:2551 +//line mysql_sql.y:2554 { yyLOCAL = []*tree.Privilege{yyDollar[1].privilegeUnion()} } @@ -16402,7 +16463,7 @@ yydefault: case 233: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL []*tree.Privilege -//line mysql_sql.y:2555 +//line mysql_sql.y:2558 { yyLOCAL = append(yyDollar[1].privilegesUnion(), yyDollar[3].privilegeUnion()) } @@ -16410,7 +16471,7 @@ yydefault: case 234: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL *tree.Privilege -//line mysql_sql.y:2561 +//line mysql_sql.y:2564 { yyLOCAL = &tree.Privilege{ Type: yyDollar[1].privilegeTypeUnion(), @@ -16421,7 +16482,7 @@ yydefault: case 235: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL *tree.Privilege -//line mysql_sql.y:2568 +//line mysql_sql.y:2571 { yyLOCAL = &tree.Privilege{ Type: yyDollar[1].privilegeTypeUnion(), @@ -16432,7 +16493,7 @@ yydefault: case 236: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL []*tree.UnresolvedName -//line mysql_sql.y:2577 +//line mysql_sql.y:2580 { yyLOCAL = []*tree.UnresolvedName{yyDollar[1].unresolvedNameUnion()} } @@ -16440,7 +16501,7 @@ yydefault: case 237: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL []*tree.UnresolvedName -//line mysql_sql.y:2581 +//line mysql_sql.y:2584 { yyLOCAL = append(yyDollar[1].unresolveNamesUnion(), yyDollar[3].unresolvedNameUnion()) } @@ -16448,7 +16509,7 @@ yydefault: case 238: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.PrivilegeType -//line mysql_sql.y:2587 +//line mysql_sql.y:2590 { yyLOCAL = tree.PRIVILEGE_TYPE_STATIC_ALL } @@ -16456,7 +16517,7 @@ yydefault: case 239: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL tree.PrivilegeType -//line mysql_sql.y:2591 +//line mysql_sql.y:2594 { yyLOCAL = tree.PRIVILEGE_TYPE_STATIC_CREATE_ACCOUNT } @@ -16464,7 +16525,7 @@ yydefault: case 240: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL tree.PrivilegeType -//line mysql_sql.y:2595 +//line mysql_sql.y:2598 { yyLOCAL = tree.PRIVILEGE_TYPE_STATIC_DROP_ACCOUNT } @@ -16472,7 +16533,7 @@ yydefault: case 241: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL tree.PrivilegeType -//line mysql_sql.y:2599 +//line mysql_sql.y:2602 { yyLOCAL = tree.PRIVILEGE_TYPE_STATIC_ALTER_ACCOUNT } @@ -16480,7 +16541,7 @@ yydefault: case 242: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL tree.PrivilegeType -//line mysql_sql.y:2603 +//line mysql_sql.y:2606 { yyLOCAL = tree.PRIVILEGE_TYPE_STATIC_UPGRADE_ACCOUNT } @@ -16488,7 +16549,7 @@ yydefault: case 243: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL tree.PrivilegeType -//line mysql_sql.y:2607 +//line mysql_sql.y:2610 { yyLOCAL = tree.PRIVILEGE_TYPE_STATIC_ALL } @@ -16496,7 +16557,7 @@ yydefault: case 244: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL tree.PrivilegeType -//line mysql_sql.y:2611 +//line mysql_sql.y:2614 { yyLOCAL = tree.PRIVILEGE_TYPE_STATIC_ALTER_TABLE } @@ -16504,7 +16565,7 @@ yydefault: case 245: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL tree.PrivilegeType -//line mysql_sql.y:2615 +//line mysql_sql.y:2618 { yyLOCAL = tree.PRIVILEGE_TYPE_STATIC_ALTER_VIEW } @@ -16512,7 +16573,7 @@ yydefault: case 246: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.PrivilegeType -//line mysql_sql.y:2619 +//line mysql_sql.y:2622 { yyLOCAL = tree.PRIVILEGE_TYPE_STATIC_CREATE } @@ -16520,7 +16581,7 @@ yydefault: case 247: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL tree.PrivilegeType -//line mysql_sql.y:2623 +//line mysql_sql.y:2626 { yyLOCAL = tree.PRIVILEGE_TYPE_STATIC_CREATE_USER } @@ -16528,7 +16589,7 @@ yydefault: case 248: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL tree.PrivilegeType -//line mysql_sql.y:2627 +//line mysql_sql.y:2630 { yyLOCAL = tree.PRIVILEGE_TYPE_STATIC_DROP_USER } @@ -16536,7 +16597,7 @@ yydefault: case 249: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL tree.PrivilegeType -//line mysql_sql.y:2631 +//line mysql_sql.y:2634 { yyLOCAL = tree.PRIVILEGE_TYPE_STATIC_ALTER_USER } @@ -16544,7 +16605,7 @@ yydefault: case 250: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL tree.PrivilegeType -//line mysql_sql.y:2635 +//line mysql_sql.y:2638 { yyLOCAL = tree.PRIVILEGE_TYPE_STATIC_CREATE_TABLESPACE } @@ -16552,7 +16613,7 @@ yydefault: case 251: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.PrivilegeType -//line mysql_sql.y:2639 +//line mysql_sql.y:2642 { yyLOCAL = tree.PRIVILEGE_TYPE_STATIC_TRIGGER } @@ -16560,7 +16621,7 @@ yydefault: case 252: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.PrivilegeType -//line mysql_sql.y:2643 +//line mysql_sql.y:2646 { yyLOCAL = tree.PRIVILEGE_TYPE_STATIC_DELETE } @@ -16568,7 +16629,7 @@ yydefault: case 253: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL tree.PrivilegeType -//line mysql_sql.y:2647 +//line mysql_sql.y:2650 { yyLOCAL = tree.PRIVILEGE_TYPE_STATIC_DROP_TABLE } @@ -16576,7 +16637,7 @@ yydefault: case 254: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL tree.PrivilegeType -//line mysql_sql.y:2651 +//line mysql_sql.y:2654 { yyLOCAL = tree.PRIVILEGE_TYPE_STATIC_DROP_VIEW } @@ -16584,7 +16645,7 @@ yydefault: case 255: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.PrivilegeType -//line mysql_sql.y:2655 +//line mysql_sql.y:2658 { yyLOCAL = tree.PRIVILEGE_TYPE_STATIC_EXECUTE } @@ -16592,7 +16653,7 @@ yydefault: case 256: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.PrivilegeType -//line mysql_sql.y:2659 +//line mysql_sql.y:2662 { yyLOCAL = tree.PRIVILEGE_TYPE_STATIC_INDEX } @@ -16600,7 +16661,7 @@ yydefault: case 257: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.PrivilegeType -//line mysql_sql.y:2663 +//line mysql_sql.y:2666 { yyLOCAL = tree.PRIVILEGE_TYPE_STATIC_INSERT } @@ -16608,7 +16669,7 @@ yydefault: case 258: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.PrivilegeType -//line mysql_sql.y:2667 +//line mysql_sql.y:2670 { yyLOCAL = tree.PRIVILEGE_TYPE_STATIC_SELECT } @@ -16616,7 +16677,7 @@ yydefault: case 259: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.PrivilegeType -//line mysql_sql.y:2671 +//line mysql_sql.y:2674 { yyLOCAL = tree.PRIVILEGE_TYPE_STATIC_SUPER } @@ -16624,7 +16685,7 @@ yydefault: case 260: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL tree.PrivilegeType -//line mysql_sql.y:2675 +//line mysql_sql.y:2678 { yyLOCAL = tree.PRIVILEGE_TYPE_STATIC_CREATE_DATABASE } @@ -16632,7 +16693,7 @@ yydefault: case 261: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL tree.PrivilegeType -//line mysql_sql.y:2679 +//line mysql_sql.y:2682 { yyLOCAL = tree.PRIVILEGE_TYPE_STATIC_DROP_DATABASE } @@ -16640,7 +16701,7 @@ yydefault: case 262: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL tree.PrivilegeType -//line mysql_sql.y:2683 +//line mysql_sql.y:2686 { yyLOCAL = tree.PRIVILEGE_TYPE_STATIC_SHOW_DATABASES } @@ -16648,7 +16709,7 @@ yydefault: case 263: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.PrivilegeType -//line mysql_sql.y:2687 +//line mysql_sql.y:2690 { yyLOCAL = tree.PRIVILEGE_TYPE_STATIC_CONNECT } @@ -16656,7 +16717,7 @@ yydefault: case 264: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL tree.PrivilegeType -//line mysql_sql.y:2691 +//line mysql_sql.y:2694 { yyLOCAL = tree.PRIVILEGE_TYPE_STATIC_MANAGE_GRANTS } @@ -16664,7 +16725,7 @@ yydefault: case 265: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.PrivilegeType -//line mysql_sql.y:2695 +//line mysql_sql.y:2698 { yyLOCAL = tree.PRIVILEGE_TYPE_STATIC_OWNERSHIP } @@ -16672,7 +16733,7 @@ yydefault: case 266: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL tree.PrivilegeType -//line mysql_sql.y:2699 +//line mysql_sql.y:2702 { yyLOCAL = tree.PRIVILEGE_TYPE_STATIC_SHOW_TABLES } @@ -16680,7 +16741,7 @@ yydefault: case 267: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL tree.PrivilegeType -//line mysql_sql.y:2703 +//line mysql_sql.y:2706 { yyLOCAL = tree.PRIVILEGE_TYPE_STATIC_CREATE_TABLE } @@ -16688,7 +16749,7 @@ yydefault: case 268: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.PrivilegeType -//line mysql_sql.y:2707 +//line mysql_sql.y:2710 { yyLOCAL = tree.PRIVILEGE_TYPE_STATIC_UPDATE } @@ -16696,7 +16757,7 @@ yydefault: case 269: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL tree.PrivilegeType -//line mysql_sql.y:2711 +//line mysql_sql.y:2714 { yyLOCAL = tree.PRIVILEGE_TYPE_STATIC_GRANT_OPTION } @@ -16704,7 +16765,7 @@ yydefault: case 270: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.PrivilegeType -//line mysql_sql.y:2715 +//line mysql_sql.y:2718 { yyLOCAL = tree.PRIVILEGE_TYPE_STATIC_REFERENCES } @@ -16712,7 +16773,7 @@ yydefault: case 271: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.PrivilegeType -//line mysql_sql.y:2719 +//line mysql_sql.y:2722 { yyLOCAL = tree.PRIVILEGE_TYPE_STATIC_REFERENCE } @@ -16720,7 +16781,7 @@ yydefault: case 272: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL tree.PrivilegeType -//line mysql_sql.y:2723 +//line mysql_sql.y:2726 { yyLOCAL = tree.PRIVILEGE_TYPE_STATIC_REPLICATION_SLAVE } @@ -16728,7 +16789,7 @@ yydefault: case 273: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL tree.PrivilegeType -//line mysql_sql.y:2727 +//line mysql_sql.y:2730 { yyLOCAL = tree.PRIVILEGE_TYPE_STATIC_REPLICATION_CLIENT } @@ -16736,7 +16797,7 @@ yydefault: case 274: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.PrivilegeType -//line mysql_sql.y:2731 +//line mysql_sql.y:2734 { yyLOCAL = tree.PRIVILEGE_TYPE_STATIC_USAGE } @@ -16744,7 +16805,7 @@ yydefault: case 275: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.PrivilegeType -//line mysql_sql.y:2735 +//line mysql_sql.y:2738 { yyLOCAL = tree.PRIVILEGE_TYPE_STATIC_RELOAD } @@ -16752,7 +16813,7 @@ yydefault: case 276: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.PrivilegeType -//line mysql_sql.y:2739 +//line mysql_sql.y:2742 { yyLOCAL = tree.PRIVILEGE_TYPE_STATIC_FILE } @@ -16760,7 +16821,7 @@ yydefault: case 277: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL tree.PrivilegeType -//line mysql_sql.y:2743 +//line mysql_sql.y:2746 { yyLOCAL = tree.PRIVILEGE_TYPE_STATIC_CREATE_TEMPORARY_TABLES } @@ -16768,7 +16829,7 @@ yydefault: case 278: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL tree.PrivilegeType -//line mysql_sql.y:2747 +//line mysql_sql.y:2750 { yyLOCAL = tree.PRIVILEGE_TYPE_STATIC_LOCK_TABLES } @@ -16776,7 +16837,7 @@ yydefault: case 279: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL tree.PrivilegeType -//line mysql_sql.y:2751 +//line mysql_sql.y:2754 { yyLOCAL = tree.PRIVILEGE_TYPE_STATIC_CREATE_VIEW } @@ -16784,7 +16845,7 @@ yydefault: case 280: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL tree.PrivilegeType -//line mysql_sql.y:2755 +//line mysql_sql.y:2758 { yyLOCAL = tree.PRIVILEGE_TYPE_STATIC_SHOW_VIEW } @@ -16792,7 +16853,7 @@ yydefault: case 281: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL tree.PrivilegeType -//line mysql_sql.y:2759 +//line mysql_sql.y:2762 { yyLOCAL = tree.PRIVILEGE_TYPE_STATIC_CREATE_ROLE } @@ -16800,7 +16861,7 @@ yydefault: case 282: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL tree.PrivilegeType -//line mysql_sql.y:2763 +//line mysql_sql.y:2766 { yyLOCAL = tree.PRIVILEGE_TYPE_STATIC_DROP_ROLE } @@ -16808,7 +16869,7 @@ yydefault: case 283: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL tree.PrivilegeType -//line mysql_sql.y:2767 +//line mysql_sql.y:2770 { yyLOCAL = tree.PRIVILEGE_TYPE_STATIC_ALTER_ROLE } @@ -16816,7 +16877,7 @@ yydefault: case 284: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL tree.PrivilegeType -//line mysql_sql.y:2771 +//line mysql_sql.y:2774 { yyLOCAL = tree.PRIVILEGE_TYPE_STATIC_CREATE_ROUTINE } @@ -16824,7 +16885,7 @@ yydefault: case 285: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL tree.PrivilegeType -//line mysql_sql.y:2775 +//line mysql_sql.y:2778 { yyLOCAL = tree.PRIVILEGE_TYPE_STATIC_ALTER_ROUTINE } @@ -16832,7 +16893,7 @@ yydefault: case 286: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.PrivilegeType -//line mysql_sql.y:2779 +//line mysql_sql.y:2782 { yyLOCAL = tree.PRIVILEGE_TYPE_STATIC_EVENT } @@ -16840,7 +16901,7 @@ yydefault: case 287: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.PrivilegeType -//line mysql_sql.y:2783 +//line mysql_sql.y:2786 { yyLOCAL = tree.PRIVILEGE_TYPE_STATIC_SHUTDOWN } @@ -16848,7 +16909,7 @@ yydefault: case 288: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.PrivilegeType -//line mysql_sql.y:2787 +//line mysql_sql.y:2790 { yyLOCAL = tree.PRIVILEGE_TYPE_STATIC_TRUNCATE } @@ -16856,7 +16917,7 @@ yydefault: case 296: yyDollar = yyS[yypt-6 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:2802 +//line mysql_sql.y:2805 { yyLOCAL = &tree.SetLogserviceSettings{ Name: yyDollar[4].str, @@ -16867,7 +16928,7 @@ yydefault: case 297: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:2811 +//line mysql_sql.y:2814 { yyLOCAL = &tree.SetTransaction{ Scope: tree.TransactionScopeNext, @@ -16878,7 +16939,7 @@ yydefault: case 298: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:2818 +//line mysql_sql.y:2821 { yyLOCAL = &tree.SetTransaction{ Scope: tree.TransactionScopeGlobal, @@ -16889,7 +16950,7 @@ yydefault: case 299: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:2825 +//line mysql_sql.y:2828 { yyLOCAL = &tree.SetTransaction{ Scope: tree.TransactionScopeSession, @@ -16900,7 +16961,7 @@ yydefault: case 300: yyDollar = yyS[yypt-5 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:2834 +//line mysql_sql.y:2837 { var connID uint32 switch v := yyDollar[5].item.(type) { @@ -16920,7 +16981,7 @@ yydefault: case 301: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL []*tree.TransactionCharacteristic -//line mysql_sql.y:2852 +//line mysql_sql.y:2855 { yyLOCAL = []*tree.TransactionCharacteristic{yyDollar[1].transactionCharacteristicUnion()} } @@ -16928,7 +16989,7 @@ yydefault: case 302: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL []*tree.TransactionCharacteristic -//line mysql_sql.y:2856 +//line mysql_sql.y:2859 { yyLOCAL = append(yyDollar[1].transactionCharacteristicListUnion(), yyDollar[3].transactionCharacteristicUnion()) } @@ -16936,7 +16997,7 @@ yydefault: case 303: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL *tree.TransactionCharacteristic -//line mysql_sql.y:2862 +//line mysql_sql.y:2865 { yyLOCAL = &tree.TransactionCharacteristic{ IsLevel: true, @@ -16947,7 +17008,7 @@ yydefault: case 304: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL *tree.TransactionCharacteristic -//line mysql_sql.y:2869 +//line mysql_sql.y:2872 { yyLOCAL = &tree.TransactionCharacteristic{ Access: yyDollar[1].accessModeUnion(), @@ -16957,7 +17018,7 @@ yydefault: case 305: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL tree.IsolationLevelType -//line mysql_sql.y:2877 +//line mysql_sql.y:2880 { yyLOCAL = tree.ISOLATION_LEVEL_REPEATABLE_READ } @@ -16965,7 +17026,7 @@ yydefault: case 306: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL tree.IsolationLevelType -//line mysql_sql.y:2881 +//line mysql_sql.y:2884 { yyLOCAL = tree.ISOLATION_LEVEL_READ_COMMITTED } @@ -16973,7 +17034,7 @@ yydefault: case 307: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL tree.IsolationLevelType -//line mysql_sql.y:2885 +//line mysql_sql.y:2888 { yyLOCAL = tree.ISOLATION_LEVEL_READ_UNCOMMITTED } @@ -16981,7 +17042,7 @@ yydefault: case 308: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.IsolationLevelType -//line mysql_sql.y:2889 +//line mysql_sql.y:2892 { yyLOCAL = tree.ISOLATION_LEVEL_SERIALIZABLE } @@ -16989,7 +17050,7 @@ yydefault: case 309: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL tree.AccessModeType -//line mysql_sql.y:2895 +//line mysql_sql.y:2898 { yyLOCAL = tree.ACCESS_MODE_READ_WRITE } @@ -16997,7 +17058,7 @@ yydefault: case 310: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL tree.AccessModeType -//line mysql_sql.y:2899 +//line mysql_sql.y:2902 { yyLOCAL = tree.ACCESS_MODE_READ_ONLY } @@ -17005,7 +17066,7 @@ yydefault: case 311: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:2905 +//line mysql_sql.y:2908 { yyLOCAL = &tree.SetRole{ SecondaryRole: false, @@ -17016,7 +17077,7 @@ yydefault: case 312: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:2912 +//line mysql_sql.y:2915 { yyLOCAL = &tree.SetRole{ SecondaryRole: true, @@ -17027,7 +17088,7 @@ yydefault: case 313: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:2919 +//line mysql_sql.y:2922 { yyLOCAL = &tree.SetRole{ SecondaryRole: true, @@ -17038,7 +17099,7 @@ yydefault: case 314: yyDollar = yyS[yypt-6 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:2928 +//line mysql_sql.y:2931 { dr := yyDollar[4].setDefaultRoleUnion() dr.Users = yyDollar[6].usersUnion() @@ -17048,7 +17109,7 @@ yydefault: case 315: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL *tree.SetDefaultRole -//line mysql_sql.y:2958 +//line mysql_sql.y:2961 { yyLOCAL = &tree.SetDefaultRole{Type: tree.SET_DEFAULT_ROLE_TYPE_NONE, Roles: nil} } @@ -17056,7 +17117,7 @@ yydefault: case 316: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL *tree.SetDefaultRole -//line mysql_sql.y:2962 +//line mysql_sql.y:2965 { yyLOCAL = &tree.SetDefaultRole{Type: tree.SET_DEFAULT_ROLE_TYPE_ALL, Roles: nil} } @@ -17064,7 +17125,7 @@ yydefault: case 317: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL *tree.SetDefaultRole -//line mysql_sql.y:2966 +//line mysql_sql.y:2969 { yyLOCAL = &tree.SetDefaultRole{Type: tree.SET_DEFAULT_ROLE_TYPE_NORMAL, Roles: yyDollar[1].rolesUnion()} } @@ -17072,7 +17133,7 @@ yydefault: case 318: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:2972 +//line mysql_sql.y:2975 { yyLOCAL = &tree.SetVar{Assignments: yyDollar[2].varAssignmentExprsUnion()} } @@ -17080,7 +17141,7 @@ yydefault: case 319: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:2978 +//line mysql_sql.y:2981 { yyLOCAL = &tree.SetPassword{Password: yyDollar[4].str} } @@ -17088,21 +17149,21 @@ yydefault: case 320: yyDollar = yyS[yypt-6 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:2982 +//line mysql_sql.y:2985 { yyLOCAL = &tree.SetPassword{User: yyDollar[4].userUnion(), Password: yyDollar[6].str} } yyVAL.union = yyLOCAL case 322: yyDollar = yyS[yypt-4 : yypt+1] -//line mysql_sql.y:2989 +//line mysql_sql.y:2992 { yyVAL.str = yyDollar[3].str } case 323: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL []*tree.VarAssignmentExpr -//line mysql_sql.y:2995 +//line mysql_sql.y:2998 { yyLOCAL = []*tree.VarAssignmentExpr{yyDollar[1].varAssignmentExprUnion()} } @@ -17110,7 +17171,7 @@ yydefault: case 324: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL []*tree.VarAssignmentExpr -//line mysql_sql.y:2999 +//line mysql_sql.y:3002 { yyLOCAL = append(yyDollar[1].varAssignmentExprsUnion(), yyDollar[3].varAssignmentExprUnion()) } @@ -17118,7 +17179,7 @@ yydefault: case 325: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL *tree.VarAssignmentExpr -//line mysql_sql.y:3005 +//line mysql_sql.y:3008 { yyLOCAL = &tree.VarAssignmentExpr{ System: true, @@ -17131,7 +17192,7 @@ yydefault: case 326: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL *tree.VarAssignmentExpr -//line mysql_sql.y:3014 +//line mysql_sql.y:3017 { yyLOCAL = &tree.VarAssignmentExpr{ System: true, @@ -17145,7 +17206,7 @@ yydefault: case 327: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL *tree.VarAssignmentExpr -//line mysql_sql.y:3024 +//line mysql_sql.y:3027 { yyLOCAL = &tree.VarAssignmentExpr{ System: true, @@ -17159,7 +17220,7 @@ yydefault: case 328: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL *tree.VarAssignmentExpr -//line mysql_sql.y:3034 +//line mysql_sql.y:3037 { yyLOCAL = &tree.VarAssignmentExpr{ System: true, @@ -17172,7 +17233,7 @@ yydefault: case 329: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL *tree.VarAssignmentExpr -//line mysql_sql.y:3043 +//line mysql_sql.y:3046 { yyLOCAL = &tree.VarAssignmentExpr{ System: true, @@ -17185,7 +17246,7 @@ yydefault: case 330: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL *tree.VarAssignmentExpr -//line mysql_sql.y:3052 +//line mysql_sql.y:3055 { vs := strings.Split(yyDollar[1].str, ".") var isGlobal bool @@ -17212,7 +17273,7 @@ yydefault: case 331: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL *tree.VarAssignmentExpr -//line mysql_sql.y:3075 +//line mysql_sql.y:3078 { v := strings.ToLower(yyDollar[1].str) var isGlobal bool @@ -17240,7 +17301,7 @@ yydefault: case 332: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL *tree.VarAssignmentExpr -//line mysql_sql.y:3099 +//line mysql_sql.y:3102 { yyLOCAL = &tree.VarAssignmentExpr{ SetNames: true, @@ -17252,7 +17313,7 @@ yydefault: case 333: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL *tree.VarAssignmentExpr -//line mysql_sql.y:3107 +//line mysql_sql.y:3110 { yyLOCAL = &tree.VarAssignmentExpr{ SetNames: true, @@ -17264,7 +17325,7 @@ yydefault: case 334: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL *tree.VarAssignmentExpr -//line mysql_sql.y:3115 +//line mysql_sql.y:3118 { yyLOCAL = &tree.VarAssignmentExpr{ SetNames: true, @@ -17277,7 +17338,7 @@ yydefault: case 335: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL *tree.VarAssignmentExpr -//line mysql_sql.y:3124 +//line mysql_sql.y:3127 { yyLOCAL = &tree.VarAssignmentExpr{ SetNames: true, @@ -17289,7 +17350,7 @@ yydefault: case 336: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL *tree.VarAssignmentExpr -//line mysql_sql.y:3132 +//line mysql_sql.y:3135 { yyLOCAL = &tree.VarAssignmentExpr{ Name: strings.ToLower(yyDollar[1].str), @@ -17300,7 +17361,7 @@ yydefault: case 337: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL *tree.VarAssignmentExpr -//line mysql_sql.y:3139 +//line mysql_sql.y:3142 { yyLOCAL = &tree.VarAssignmentExpr{ Name: strings.ToLower(yyDollar[1].str), @@ -17311,7 +17372,7 @@ yydefault: case 338: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.Expr -//line mysql_sql.y:3148 +//line mysql_sql.y:3151 { yyLOCAL = tree.NewNumVal(yyDollar[1].str, yyDollar[1].str, false, tree.P_char) } @@ -17319,7 +17380,7 @@ yydefault: case 339: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.Expr -//line mysql_sql.y:3152 +//line mysql_sql.y:3155 { yyLOCAL = tree.NewNumVal(yyDollar[1].str, yyDollar[1].str, false, tree.P_char) } @@ -17327,39 +17388,39 @@ yydefault: case 340: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.Expr -//line mysql_sql.y:3156 +//line mysql_sql.y:3159 { yyLOCAL = yyDollar[1].exprUnion() } yyVAL.union = yyLOCAL case 341: yyDollar = yyS[yypt-1 : yypt+1] -//line mysql_sql.y:3162 +//line mysql_sql.y:3165 { yyVAL.str = string(yyDollar[1].str) } case 342: yyDollar = yyS[yypt-1 : yypt+1] -//line mysql_sql.y:3166 +//line mysql_sql.y:3169 { yyVAL.str = yyDollar[1].str } case 343: yyDollar = yyS[yypt-1 : yypt+1] -//line mysql_sql.y:3172 +//line mysql_sql.y:3175 { yyVAL.str = yyDollar[1].cstrUnion().Compare() } case 344: yyDollar = yyS[yypt-3 : yypt+1] -//line mysql_sql.y:3176 +//line mysql_sql.y:3179 { yyVAL.str = yyDollar[1].cstrUnion().Compare() + "." + yyDollar[3].cstrUnion().Compare() } case 345: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL []string -//line mysql_sql.y:3182 +//line mysql_sql.y:3185 { yyLOCAL = []string{yyDollar[1].str} } @@ -17367,7 +17428,7 @@ yydefault: case 346: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL []string -//line mysql_sql.y:3186 +//line mysql_sql.y:3189 { yyLOCAL = append(yyDollar[1].strsUnion(), yyDollar[3].str) } @@ -17375,7 +17436,7 @@ yydefault: case 353: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:3200 +//line mysql_sql.y:3203 { yyLOCAL = &tree.SavePoint{Name: tree.Identifier(yyDollar[2].cstrUnion().Compare())} } @@ -17383,7 +17444,7 @@ yydefault: case 354: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:3206 +//line mysql_sql.y:3209 { yyLOCAL = &tree.ReleaseSavePoint{Name: tree.Identifier(yyDollar[3].cstrUnion().Compare())} } @@ -17391,7 +17452,7 @@ yydefault: case 355: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:3212 +//line mysql_sql.y:3215 { yyLOCAL = &tree.RollbackToSavePoint{Name: tree.Identifier(yyDollar[3].cstrUnion().Compare())} } @@ -17399,7 +17460,7 @@ yydefault: case 356: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:3217 +//line mysql_sql.y:3220 { yyLOCAL = &tree.RollbackToSavePoint{Name: tree.Identifier(yyDollar[4].cstrUnion().Compare())} } @@ -17407,7 +17468,7 @@ yydefault: case 357: yyDollar = yyS[yypt-5 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:3222 +//line mysql_sql.y:3225 { yyLOCAL = &tree.RollbackToSavePoint{Name: tree.Identifier(yyDollar[5].cstrUnion().Compare())} } @@ -17415,7 +17476,7 @@ yydefault: case 358: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:3227 +//line mysql_sql.y:3230 { yyLOCAL = &tree.RollbackToSavePoint{Name: tree.Identifier(yyDollar[4].cstrUnion().Compare())} } @@ -17423,7 +17484,7 @@ yydefault: case 359: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:3233 +//line mysql_sql.y:3236 { yyLOCAL = &tree.RollbackTransaction{Type: yyDollar[2].completionTypeUnion()} } @@ -17431,7 +17492,7 @@ yydefault: case 360: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:3239 +//line mysql_sql.y:3242 { yyLOCAL = &tree.CommitTransaction{Type: yyDollar[2].completionTypeUnion()} } @@ -17439,7 +17500,7 @@ yydefault: case 361: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL tree.CompletionType -//line mysql_sql.y:3244 +//line mysql_sql.y:3247 { yyLOCAL = tree.COMPLETION_TYPE_NO_CHAIN } @@ -17447,7 +17508,7 @@ yydefault: case 362: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.CompletionType -//line mysql_sql.y:3248 +//line mysql_sql.y:3251 { yyLOCAL = tree.COMPLETION_TYPE_NO_CHAIN } @@ -17455,7 +17516,7 @@ yydefault: case 363: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL tree.CompletionType -//line mysql_sql.y:3252 +//line mysql_sql.y:3255 { yyLOCAL = tree.COMPLETION_TYPE_CHAIN } @@ -17463,7 +17524,7 @@ yydefault: case 364: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL tree.CompletionType -//line mysql_sql.y:3256 +//line mysql_sql.y:3259 { yyLOCAL = tree.COMPLETION_TYPE_CHAIN } @@ -17471,7 +17532,7 @@ yydefault: case 365: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL tree.CompletionType -//line mysql_sql.y:3260 +//line mysql_sql.y:3263 { yyLOCAL = tree.COMPLETION_TYPE_RELEASE } @@ -17479,7 +17540,7 @@ yydefault: case 366: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.CompletionType -//line mysql_sql.y:3264 +//line mysql_sql.y:3267 { yyLOCAL = tree.COMPLETION_TYPE_RELEASE } @@ -17487,7 +17548,7 @@ yydefault: case 367: yyDollar = yyS[yypt-5 : yypt+1] var yyLOCAL tree.CompletionType -//line mysql_sql.y:3268 +//line mysql_sql.y:3271 { yyLOCAL = tree.COMPLETION_TYPE_NO_CHAIN } @@ -17495,7 +17556,7 @@ yydefault: case 368: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL tree.CompletionType -//line mysql_sql.y:3272 +//line mysql_sql.y:3275 { yyLOCAL = tree.COMPLETION_TYPE_NO_CHAIN } @@ -17503,7 +17564,7 @@ yydefault: case 369: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL tree.CompletionType -//line mysql_sql.y:3276 +//line mysql_sql.y:3279 { yyLOCAL = tree.COMPLETION_TYPE_NO_CHAIN } @@ -17511,7 +17572,7 @@ yydefault: case 370: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:3282 +//line mysql_sql.y:3285 { yyLOCAL = &tree.BeginTransaction{} } @@ -17519,7 +17580,7 @@ yydefault: case 371: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:3286 +//line mysql_sql.y:3289 { yyLOCAL = &tree.BeginTransaction{} } @@ -17527,7 +17588,7 @@ yydefault: case 372: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:3290 +//line mysql_sql.y:3293 { yyLOCAL = &tree.BeginTransaction{} } @@ -17535,7 +17596,7 @@ yydefault: case 373: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:3294 +//line mysql_sql.y:3297 { m := tree.MakeTransactionModes(tree.READ_WRITE_MODE_READ_WRITE) yyLOCAL = &tree.BeginTransaction{Modes: m} @@ -17544,7 +17605,7 @@ yydefault: case 374: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:3299 +//line mysql_sql.y:3302 { m := tree.MakeTransactionModes(tree.READ_WRITE_MODE_READ_ONLY) yyLOCAL = &tree.BeginTransaction{Modes: m} @@ -17553,7 +17614,7 @@ yydefault: case 375: yyDollar = yyS[yypt-5 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:3304 +//line mysql_sql.y:3307 { yyLOCAL = &tree.BeginTransaction{} } @@ -17561,7 +17622,7 @@ yydefault: case 376: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:3310 +//line mysql_sql.y:3313 { name := yyDollar[2].cstrUnion() secondaryRole := false @@ -17578,7 +17639,7 @@ yydefault: case 377: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:3323 +//line mysql_sql.y:3326 { name := yylex.(*Lexer).GetDbOrTblNameCStr("") secondaryRole := false @@ -17595,7 +17656,7 @@ yydefault: case 378: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:3336 +//line mysql_sql.y:3339 { name := yylex.(*Lexer).GetDbOrTblNameCStr("") secondaryRole := false @@ -17612,7 +17673,7 @@ yydefault: case 379: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:3349 +//line mysql_sql.y:3352 { name := yylex.(*Lexer).GetDbOrTblNameCStr("") secondaryRole := true @@ -17629,7 +17690,7 @@ yydefault: case 380: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:3362 +//line mysql_sql.y:3365 { name := yylex.(*Lexer).GetDbOrTblNameCStr("") secondaryRole := true @@ -17646,7 +17707,7 @@ yydefault: case 381: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:3377 +//line mysql_sql.y:3380 { if intoErr := tree.ValidateSelectIntoEnclosingStatement(yyDollar[1].statementUnion()); intoErr != "" { yylex.Error(intoErr) @@ -17658,7 +17719,7 @@ yydefault: case 382: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:3385 +//line mysql_sql.y:3388 { yyDollar[2].statementUnion().(*tree.Update).With = yyDollar[1].withClauseUnion() if intoErr := tree.ValidateSelectIntoEnclosingStatement(yyDollar[2].statementUnion()); intoErr != "" { @@ -17671,7 +17732,7 @@ yydefault: case 383: yyDollar = yyS[yypt-10 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:3396 +//line mysql_sql.y:3399 { // Single-table syntax yyLOCAL = &tree.Update{ @@ -17689,7 +17750,7 @@ yydefault: case 384: yyDollar = yyS[yypt-8 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:3410 +//line mysql_sql.y:3413 { // Multiple-table syntax yyLOCAL = &tree.Update{ @@ -17706,7 +17767,7 @@ yydefault: case 385: yyDollar = yyS[yypt-10 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:3423 +//line mysql_sql.y:3426 { // PostgreSQL-style UPDATE target SET ... FROM source_tables WHERE ... // The target table is kept in Tables; FROM-clause sources are stored @@ -17726,7 +17787,7 @@ yydefault: case 386: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.UpdateExprs -//line mysql_sql.y:3441 +//line mysql_sql.y:3444 { yyLOCAL = tree.UpdateExprs{yyDollar[1].updateExprUnion()} } @@ -17734,7 +17795,7 @@ yydefault: case 387: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL tree.UpdateExprs -//line mysql_sql.y:3445 +//line mysql_sql.y:3448 { yyLOCAL = append(yyDollar[1].updateExprsUnion(), yyDollar[3].updateExprUnion()) } @@ -17742,7 +17803,7 @@ yydefault: case 388: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL *tree.UpdateExpr -//line mysql_sql.y:3451 +//line mysql_sql.y:3454 { yyLOCAL = &tree.UpdateExpr{Names: []*tree.UnresolvedName{yyDollar[1].unresolvedNameUnion()}, Expr: yyDollar[3].exprUnion()} } @@ -17750,7 +17811,7 @@ yydefault: case 391: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:3461 +//line mysql_sql.y:3464 { yyLOCAL = &tree.LockTableStmt{TableLocks: yyDollar[3].tableLocksUnion()} } @@ -17758,7 +17819,7 @@ yydefault: case 392: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL []tree.TableLock -//line mysql_sql.y:3467 +//line mysql_sql.y:3470 { yyLOCAL = []tree.TableLock{yyDollar[1].tableLockUnion()} } @@ -17766,7 +17827,7 @@ yydefault: case 393: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL []tree.TableLock -//line mysql_sql.y:3471 +//line mysql_sql.y:3474 { yyLOCAL = append(yyDollar[1].tableLocksUnion(), yyDollar[3].tableLockUnion()) } @@ -17774,7 +17835,7 @@ yydefault: case 394: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL tree.TableLock -//line mysql_sql.y:3477 +//line mysql_sql.y:3480 { yyLOCAL = tree.TableLock{Table: *yyDollar[1].tableNameUnion(), LockType: yyDollar[2].tableLockTypeUnion()} } @@ -17782,7 +17843,7 @@ yydefault: case 395: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.TableLockType -//line mysql_sql.y:3483 +//line mysql_sql.y:3486 { yyLOCAL = tree.TableLockRead } @@ -17790,7 +17851,7 @@ yydefault: case 396: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL tree.TableLockType -//line mysql_sql.y:3487 +//line mysql_sql.y:3490 { yyLOCAL = tree.TableLockReadLocal } @@ -17798,7 +17859,7 @@ yydefault: case 397: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.TableLockType -//line mysql_sql.y:3491 +//line mysql_sql.y:3494 { yyLOCAL = tree.TableLockWrite } @@ -17806,7 +17867,7 @@ yydefault: case 398: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL tree.TableLockType -//line mysql_sql.y:3495 +//line mysql_sql.y:3498 { yyLOCAL = tree.TableLockLowPriorityWrite } @@ -17814,7 +17875,7 @@ yydefault: case 399: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:3501 +//line mysql_sql.y:3504 { yyLOCAL = &tree.UnLockTableStmt{} } @@ -17822,7 +17883,7 @@ yydefault: case 409: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:3516 +//line mysql_sql.y:3519 { yyLOCAL = &tree.SetVar{Assignments: yyDollar[2].varAssignmentExprsUnion()} } @@ -17830,7 +17891,7 @@ yydefault: case 413: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:3523 +//line mysql_sql.y:3526 { yyLOCAL = yyDollar[1].selectUnion() } @@ -17838,7 +17899,7 @@ yydefault: case 414: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:3529 +//line mysql_sql.y:3532 { yyLOCAL = tree.NewPrepareStmt(tree.Identifier(yyDollar[2].str), yyDollar[4].statementUnion()) } @@ -17846,7 +17907,7 @@ yydefault: case 415: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:3533 +//line mysql_sql.y:3536 { yyLOCAL = tree.NewPrepareString(tree.Identifier(yyDollar[2].str), yyDollar[4].str) } @@ -17854,7 +17915,7 @@ yydefault: case 416: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:3537 +//line mysql_sql.y:3540 { yyLOCAL = tree.NewPrepareVar(tree.Identifier(yyDollar[2].str), yyDollar[4].varExprUnion()) } @@ -17862,7 +17923,7 @@ yydefault: case 417: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:3543 +//line mysql_sql.y:3546 { yyLOCAL = &tree.ExecuteSQLTask{ Name: tree.Identifier(yyDollar[3].cstrUnion().Compare()), @@ -17872,7 +17933,7 @@ yydefault: case 418: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:3549 +//line mysql_sql.y:3552 { yyLOCAL = tree.NewExecute(tree.Identifier(yyDollar[2].str)) } @@ -17880,7 +17941,7 @@ yydefault: case 419: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:3553 +//line mysql_sql.y:3556 { yyLOCAL = tree.NewExecuteWithVariables(tree.Identifier(yyDollar[2].str), yyDollar[4].varExprsUnion()) } @@ -17888,7 +17949,7 @@ yydefault: case 420: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:3559 +//line mysql_sql.y:3562 { yyLOCAL = tree.NewDeallocate(tree.Identifier(yyDollar[3].str), false) } @@ -17896,7 +17957,7 @@ yydefault: case 421: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:3565 +//line mysql_sql.y:3568 { yyLOCAL = tree.NewReset(tree.Identifier(yyDollar[3].str)) } @@ -17904,7 +17965,7 @@ yydefault: case 427: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:3576 +//line mysql_sql.y:3579 { yyLOCAL = yyDollar[1].selectUnion() } @@ -17912,7 +17973,7 @@ yydefault: case 428: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:3582 +//line mysql_sql.y:3585 { yyLOCAL = &tree.ShowColumns{Table: yyDollar[2].unresolvedObjectNameUnion()} } @@ -17920,7 +17981,7 @@ yydefault: case 429: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:3586 +//line mysql_sql.y:3589 { yyLOCAL = &tree.ShowColumns{Table: yyDollar[2].unresolvedObjectNameUnion(), ColName: yyDollar[3].unresolvedNameUnion()} } @@ -17928,7 +17989,7 @@ yydefault: case 430: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:3590 +//line mysql_sql.y:3593 { yyLOCAL = tree.NewExplainFor("", uint64(yyDollar[4].item.(int64))) } @@ -17936,7 +17997,7 @@ yydefault: case 431: yyDollar = yyS[yypt-7 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:3594 +//line mysql_sql.y:3597 { yyLOCAL = tree.NewExplainFor(yyDollar[4].str, uint64(yyDollar[7].item.(int64))) } @@ -17944,7 +18005,7 @@ yydefault: case 432: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:3598 +//line mysql_sql.y:3601 { yyLOCAL = yyDollar[1].statementUnion() } @@ -17952,7 +18013,7 @@ yydefault: case 433: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:3604 +//line mysql_sql.y:3607 { if intoErr := tree.ValidateSelectIntoEnclosingStatement(yyDollar[2].statementUnion()); intoErr != "" { yylex.Error(intoErr) @@ -17964,7 +18025,7 @@ yydefault: case 434: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:3612 +//line mysql_sql.y:3615 { if intoErr := tree.ValidateSelectIntoEnclosingStatement(yyDollar[3].statementUnion()); intoErr != "" { yylex.Error(intoErr) @@ -17979,7 +18040,7 @@ yydefault: case 435: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:3623 +//line mysql_sql.y:3626 { if intoErr := tree.ValidateSelectIntoEnclosingStatement(yyDollar[3].statementUnion()); intoErr != "" { yylex.Error(intoErr) @@ -17994,7 +18055,7 @@ yydefault: case 436: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:3634 +//line mysql_sql.y:3637 { if intoErr := tree.ValidateSelectIntoEnclosingStatement(yyDollar[4].statementUnion()); intoErr != "" { yylex.Error(intoErr) @@ -18010,7 +18071,7 @@ yydefault: case 437: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:3646 +//line mysql_sql.y:3649 { if intoErr := tree.ValidateSelectIntoEnclosingStatement(yyDollar[3].statementUnion()); intoErr != "" { yylex.Error(intoErr) @@ -18025,7 +18086,7 @@ yydefault: case 438: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:3657 +//line mysql_sql.y:3660 { if intoErr := tree.ValidateSelectIntoEnclosingStatement(yyDollar[4].statementUnion()); intoErr != "" { yylex.Error(intoErr) @@ -18041,7 +18102,7 @@ yydefault: case 439: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:3669 +//line mysql_sql.y:3672 { if intoErr := tree.ValidateSelectIntoEnclosingStatement(yyDollar[4].statementUnion()); intoErr != "" { yylex.Error(intoErr) @@ -18057,7 +18118,7 @@ yydefault: case 440: yyDollar = yyS[yypt-5 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:3681 +//line mysql_sql.y:3684 { if intoErr := tree.ValidateSelectIntoEnclosingStatement(yyDollar[5].statementUnion()); intoErr != "" { yylex.Error(intoErr) @@ -18069,7 +18130,7 @@ yydefault: case 441: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:3689 +//line mysql_sql.y:3692 { yyLOCAL = tree.MakeExplainStmt(yyDollar[3].statementUnion(), nil) } @@ -18077,7 +18138,7 @@ yydefault: case 442: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:3693 +//line mysql_sql.y:3696 { options := []tree.OptionElem{ tree.MakeOptionElem(tree.VerboseOption, "NULL"), @@ -18088,7 +18149,7 @@ yydefault: case 443: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:3700 +//line mysql_sql.y:3703 { options := []tree.OptionElem{ tree.MakeOptionElem(tree.AnalyzeOption, "NULL"), @@ -18099,7 +18160,7 @@ yydefault: case 444: yyDollar = yyS[yypt-5 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:3707 +//line mysql_sql.y:3710 { options := []tree.OptionElem{ tree.MakeOptionElem(tree.AnalyzeOption, "NULL"), @@ -18111,7 +18172,7 @@ yydefault: case 459: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL []tree.OptionElem -//line mysql_sql.y:3745 +//line mysql_sql.y:3748 { yyLOCAL = []tree.OptionElem{yyDollar[1].explainOptionUnion()} } @@ -18119,7 +18180,7 @@ yydefault: case 460: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL []tree.OptionElem -//line mysql_sql.y:3749 +//line mysql_sql.y:3752 { yyLOCAL = append(yyDollar[1].explainOptionsUnion(), yyDollar[3].explainOptionUnion()) } @@ -18127,45 +18188,45 @@ yydefault: case 461: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL tree.OptionElem -//line mysql_sql.y:3755 +//line mysql_sql.y:3758 { yyLOCAL = tree.MakeOptionElem(yyDollar[1].str, yyDollar[2].str) } yyVAL.union = yyLOCAL case 462: yyDollar = yyS[yypt-1 : yypt+1] -//line mysql_sql.y:3761 +//line mysql_sql.y:3764 { yyVAL.str = yyDollar[1].str } case 463: yyDollar = yyS[yypt-1 : yypt+1] -//line mysql_sql.y:3766 +//line mysql_sql.y:3769 { yyVAL.str = "true" } case 464: yyDollar = yyS[yypt-1 : yypt+1] -//line mysql_sql.y:3767 +//line mysql_sql.y:3770 { yyVAL.str = "false" } case 465: yyDollar = yyS[yypt-1 : yypt+1] -//line mysql_sql.y:3768 +//line mysql_sql.y:3771 { yyVAL.str = yyDollar[1].str } case 466: yyDollar = yyS[yypt-1 : yypt+1] -//line mysql_sql.y:3769 +//line mysql_sql.y:3772 { yyVAL.str = yyDollar[1].str } case 467: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:3773 +//line mysql_sql.y:3776 { stmt := tree.NewAnalyzeStmt(yyDollar[3].analyzeTableEntriesUnion()) stmt.FullScan = yyDollar[4].boolValUnion() @@ -18175,7 +18236,7 @@ yydefault: case 468: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL bool -//line mysql_sql.y:3781 +//line mysql_sql.y:3784 { yyLOCAL = false } @@ -18183,7 +18244,7 @@ yydefault: case 469: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL bool -//line mysql_sql.y:3785 +//line mysql_sql.y:3788 { yyLOCAL = true } @@ -18191,7 +18252,7 @@ yydefault: case 470: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL []*tree.AnalyzeTableEntry -//line mysql_sql.y:3791 +//line mysql_sql.y:3794 { yyLOCAL = []*tree.AnalyzeTableEntry{yyDollar[1].analyzeTableEntryUnion()} } @@ -18199,7 +18260,7 @@ yydefault: case 471: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL []*tree.AnalyzeTableEntry -//line mysql_sql.y:3795 +//line mysql_sql.y:3798 { yyLOCAL = append(yyDollar[1].analyzeTableEntriesUnion(), yyDollar[3].analyzeTableEntryUnion()) } @@ -18207,7 +18268,7 @@ yydefault: case 472: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL *tree.AnalyzeTableEntry -//line mysql_sql.y:3801 +//line mysql_sql.y:3804 { yyLOCAL = &tree.AnalyzeTableEntry{Table: yyDollar[1].tableNameUnion()} } @@ -18215,7 +18276,7 @@ yydefault: case 473: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL *tree.AnalyzeTableEntry -//line mysql_sql.y:3805 +//line mysql_sql.y:3808 { yyLOCAL = &tree.AnalyzeTableEntry{Table: yyDollar[1].tableNameUnion(), Cols: yyDollar[3].identifierListUnion()} } @@ -18223,7 +18284,7 @@ yydefault: case 474: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:3811 +//line mysql_sql.y:3814 { yyLOCAL = tree.NewCheckTableStmt(yyDollar[3].tableNamesUnion(), yyDollar[4].checkTableOptionUnion()) } @@ -18231,7 +18292,7 @@ yydefault: case 475: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL tree.CheckTableOption -//line mysql_sql.y:3817 +//line mysql_sql.y:3820 { yyLOCAL = tree.CheckTableOptionNone } @@ -18239,7 +18300,7 @@ yydefault: case 476: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.CheckTableOption -//line mysql_sql.y:3821 +//line mysql_sql.y:3824 { yyLOCAL = tree.CheckTableOptionExtended } @@ -18247,7 +18308,7 @@ yydefault: case 477: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL tree.CheckTableOption -//line mysql_sql.y:3825 +//line mysql_sql.y:3828 { yyLOCAL = tree.CheckTableOptionForUpgrade } @@ -18255,7 +18316,7 @@ yydefault: case 478: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:3831 +//line mysql_sql.y:3834 { yyLOCAL = tree.NewShowProfileStmt(yyDollar[3].int64ValUnion(), yyDollar[4].limitUnion()) } @@ -18263,7 +18324,7 @@ yydefault: case 479: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL int64 -//line mysql_sql.y:3837 +//line mysql_sql.y:3840 { yyLOCAL = 0 } @@ -18271,7 +18332,7 @@ yydefault: case 480: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL int64 -//line mysql_sql.y:3841 +//line mysql_sql.y:3844 { yyLOCAL = yyDollar[3].item.(int64) } @@ -18279,7 +18340,7 @@ yydefault: case 481: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:3847 +//line mysql_sql.y:3850 { yyLOCAL = &tree.UpgradeStatement{ Target: yyDollar[3].upgrade_targetUnion(), @@ -18290,7 +18351,7 @@ yydefault: case 482: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL *tree.Target -//line mysql_sql.y:3856 +//line mysql_sql.y:3859 { yyLOCAL = &tree.Target{ AccountName: yyDollar[1].str, @@ -18301,7 +18362,7 @@ yydefault: case 483: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL *tree.Target -//line mysql_sql.y:3863 +//line mysql_sql.y:3866 { yyLOCAL = &tree.Target{ AccountName: "", @@ -18312,7 +18373,7 @@ yydefault: case 484: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL int64 -//line mysql_sql.y:3871 +//line mysql_sql.y:3874 { yyLOCAL = -1 } @@ -18320,7 +18381,7 @@ yydefault: case 485: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL int64 -//line mysql_sql.y:3875 +//line mysql_sql.y:3878 { res := yyDollar[3].item.(int64) if res <= 0 { @@ -18333,7 +18394,7 @@ yydefault: case 500: yyDollar = yyS[yypt-6 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:3903 +//line mysql_sql.y:3906 { yyLOCAL = &tree.AlterIcebergCatalog{ Name: tree.Identifier(yyDollar[4].cstrUnion().Compare()), @@ -18344,7 +18405,7 @@ yydefault: case 501: yyDollar = yyS[yypt-8 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:3910 +//line mysql_sql.y:3913 { yyLOCAL = &tree.AlterIcebergCatalog{ Name: tree.Identifier(yyDollar[4].cstrUnion().Compare()), @@ -18355,7 +18416,7 @@ yydefault: case 502: yyDollar = yyS[yypt-6 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:3919 +//line mysql_sql.y:3922 { yyLOCAL = &tree.AlterMongoDBConnection{ Name: tree.Identifier(yyDollar[4].cstrUnion().Compare()), @@ -18367,7 +18428,7 @@ yydefault: case 503: yyDollar = yyS[yypt-8 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:3927 +//line mysql_sql.y:3930 { yyLOCAL = &tree.AlterMongoDBConnection{ Name: tree.Identifier(yyDollar[4].cstrUnion().Compare()), @@ -18379,7 +18440,7 @@ yydefault: case 504: yyDollar = yyS[yypt-5 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:3935 +//line mysql_sql.y:3938 { yyLOCAL = &tree.AlterMongoDBConnection{ Name: tree.Identifier(yyDollar[4].cstrUnion().Compare()), @@ -18390,7 +18451,7 @@ yydefault: case 505: yyDollar = yyS[yypt-5 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:3942 +//line mysql_sql.y:3945 { yyLOCAL = &tree.AlterMongoDBConnection{ Name: tree.Identifier(yyDollar[4].cstrUnion().Compare()), @@ -18401,7 +18462,7 @@ yydefault: case 506: yyDollar = yyS[yypt-10 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:3951 +//line mysql_sql.y:3954 { var ifExists = yyDollar[3].boolValUnion() var name = yyDollar[4].tableNameUnion() @@ -18426,7 +18487,7 @@ yydefault: case 507: yyDollar = yyS[yypt-7 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:3974 +//line mysql_sql.y:3977 { var ifExists = yyDollar[3].boolValUnion() var name = yyDollar[4].tableNameUnion() @@ -18438,7 +18499,7 @@ yydefault: case 508: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:3984 +//line mysql_sql.y:3987 { var table = yyDollar[3].tableNameUnion() alterTable := tree.NewAlterTable(table) @@ -18449,7 +18510,7 @@ yydefault: case 509: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:3991 +//line mysql_sql.y:3994 { var table = yyDollar[3].tableNameUnion() alterTable := tree.NewAlterTable(table) @@ -18460,7 +18521,7 @@ yydefault: case 510: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:3998 +//line mysql_sql.y:4001 { var table = yyDollar[3].tableNameUnion() alterTable := tree.NewAlterTable(table) @@ -18471,7 +18532,7 @@ yydefault: case 511: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:4007 +//line mysql_sql.y:4010 { alterTables := yyDollar[3].renameTableOptionsUnion() renameTables := tree.NewRenameTable(alterTables) @@ -18481,7 +18542,7 @@ yydefault: case 512: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL []*tree.AlterTable -//line mysql_sql.y:4015 +//line mysql_sql.y:4018 { yyLOCAL = []*tree.AlterTable{yyDollar[1].renameTableOptionUnion()} } @@ -18489,7 +18550,7 @@ yydefault: case 513: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL []*tree.AlterTable -//line mysql_sql.y:4019 +//line mysql_sql.y:4022 { yyLOCAL = append(yyDollar[1].renameTableOptionsUnion(), yyDollar[3].renameTableOptionUnion()) } @@ -18497,7 +18558,7 @@ yydefault: case 514: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL *tree.AlterTable -//line mysql_sql.y:4025 +//line mysql_sql.y:4028 { var table = yyDollar[1].tableNameUnion() alterTable := tree.NewAlterTable(table) @@ -18509,7 +18570,7 @@ yydefault: case 515: yyDollar = yyS[yypt-5 : yypt+1] var yyLOCAL tree.AlterTableOption -//line mysql_sql.y:4035 +//line mysql_sql.y:4038 { var io *tree.IndexOption = nil if yyDollar[5].indexOptionUnion() == nil { @@ -18526,7 +18587,7 @@ yydefault: case 516: yyDollar = yyS[yypt-5 : yypt+1] var yyLOCAL tree.AlterTableOption -//line mysql_sql.y:4048 +//line mysql_sql.y:4051 { var io *tree.IndexOption = nil if yyDollar[5].indexOptionUnion() == nil { @@ -18543,7 +18604,7 @@ yydefault: case 517: yyDollar = yyS[yypt-5 : yypt+1] var yyLOCAL tree.AlterTableOption -//line mysql_sql.y:4061 +//line mysql_sql.y:4064 { var io *tree.IndexOption = nil if yyDollar[5].indexOptionUnion() == nil { @@ -18560,7 +18621,7 @@ yydefault: case 518: yyDollar = yyS[yypt-5 : yypt+1] var yyLOCAL tree.AlterTableOption -//line mysql_sql.y:4074 +//line mysql_sql.y:4077 { var io *tree.IndexOption = nil if yyDollar[5].indexOptionUnion() == nil { @@ -18577,7 +18638,7 @@ yydefault: case 519: yyDollar = yyS[yypt-5 : yypt+1] var yyLOCAL tree.AlterTableOption -//line mysql_sql.y:4087 +//line mysql_sql.y:4090 { var io *tree.IndexOption = nil if yyDollar[5].indexOptionUnion() == nil { @@ -18594,7 +18655,7 @@ yydefault: case 520: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.AlterTableOptions -//line mysql_sql.y:4102 +//line mysql_sql.y:4105 { yyLOCAL = []tree.AlterTableOption{yyDollar[1].alterTableOptionUnion()} } @@ -18602,7 +18663,7 @@ yydefault: case 521: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL tree.AlterTableOptions -//line mysql_sql.y:4106 +//line mysql_sql.y:4109 { yyLOCAL = append(yyDollar[1].alterTableOptionsUnion(), yyDollar[3].alterTableOptionUnion()) } @@ -18610,7 +18671,7 @@ yydefault: case 522: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.AlterPartitionOption -//line mysql_sql.y:4112 +//line mysql_sql.y:4115 { yyLOCAL = yyDollar[1].alterPartitionOptionUnion() } @@ -18618,7 +18679,7 @@ yydefault: case 523: yyDollar = yyS[yypt-6 : yypt+1] var yyLOCAL tree.AlterPartitionOption -//line mysql_sql.y:4116 +//line mysql_sql.y:4119 { yyDollar[3].partitionByUnion().Num = uint64(yyDollar[4].int64ValUnion()) var PartBy = yyDollar[3].partitionByUnion() @@ -18644,7 +18705,7 @@ yydefault: case 524: yyDollar = yyS[yypt-7 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:4140 +//line mysql_sql.y:4143 { var ifExists = yyDollar[3].boolValUnion() var name = tree.Identifier(yyDollar[4].cstrUnion().Compare()) @@ -18656,7 +18717,7 @@ yydefault: case 525: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:4150 +//line mysql_sql.y:4153 { yyLOCAL = &tree.AlterSQLTask{ Name: tree.Identifier(yyDollar[3].cstrUnion().Compare()), @@ -18667,7 +18728,7 @@ yydefault: case 526: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:4157 +//line mysql_sql.y:4160 { yyLOCAL = &tree.AlterSQLTask{ Name: tree.Identifier(yyDollar[3].cstrUnion().Compare()), @@ -18678,7 +18739,7 @@ yydefault: case 527: yyDollar = yyS[yypt-7 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:4164 +//line mysql_sql.y:4167 { yyLOCAL = &tree.AlterSQLTask{ Name: tree.Identifier(yyDollar[3].cstrUnion().Compare()), @@ -18691,7 +18752,7 @@ yydefault: case 528: yyDollar = yyS[yypt-8 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:4173 +//line mysql_sql.y:4176 { yyLOCAL = &tree.AlterSQLTask{ Name: tree.Identifier(yyDollar[3].cstrUnion().Compare()), @@ -18703,7 +18764,7 @@ yydefault: case 529: yyDollar = yyS[yypt-6 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:4181 +//line mysql_sql.y:4184 { yyLOCAL = &tree.AlterSQLTask{ Name: tree.Identifier(yyDollar[3].cstrUnion().Compare()), @@ -18715,7 +18776,7 @@ yydefault: case 530: yyDollar = yyS[yypt-6 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:4189 +//line mysql_sql.y:4192 { yyLOCAL = &tree.AlterSQLTask{ Name: tree.Identifier(yyDollar[3].cstrUnion().Compare()), @@ -18727,7 +18788,7 @@ yydefault: case 531: yyDollar = yyS[yypt-8 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:4199 +//line mysql_sql.y:4202 { var oldName = yyDollar[5].cstrUnion().Compare() var newName = yyDollar[8].cstrUnion().Compare() @@ -18737,7 +18798,7 @@ yydefault: case 532: yyDollar = yyS[yypt-6 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:4205 +//line mysql_sql.y:4208 { var oldName = yyDollar[3].cstrUnion().Compare() var newName = yyDollar[6].cstrUnion().Compare() @@ -18747,7 +18808,7 @@ yydefault: case 533: yyDollar = yyS[yypt-11 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:4211 +//line mysql_sql.y:4214 { var roleName = yyDollar[3].cstrUnion().Compare() var ruleSQL = yyDollar[6].str @@ -18760,7 +18821,7 @@ yydefault: case 534: yyDollar = yyS[yypt-10 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:4220 +//line mysql_sql.y:4223 { var roleName = yyDollar[3].cstrUnion().Compare() var dbName = yyDollar[8].cstrUnion().Compare() @@ -18771,7 +18832,7 @@ yydefault: case 535: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL tree.AlterPartitionOption -//line mysql_sql.y:4229 +//line mysql_sql.y:4232 { var typ = tree.AlterPartitionAddPartition var partitions = yyDollar[3].partitionsUnion() @@ -18785,7 +18846,7 @@ yydefault: case 536: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL tree.AlterPartitionOption -//line mysql_sql.y:4239 +//line mysql_sql.y:4242 { var typ = tree.AlterPartitionDropPartition var partitionNames = yyDollar[3].PartitionNamesUnion() @@ -18805,7 +18866,7 @@ yydefault: case 537: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL tree.AlterPartitionOption -//line mysql_sql.y:4255 +//line mysql_sql.y:4258 { var typ = tree.AlterPartitionTruncatePartition var partitionNames = yyDollar[3].PartitionNamesUnion() @@ -18825,7 +18886,7 @@ yydefault: case 538: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.IdentifierList -//line mysql_sql.y:4273 +//line mysql_sql.y:4276 { yyLOCAL = nil } @@ -18833,7 +18894,7 @@ yydefault: case 539: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.IdentifierList -//line mysql_sql.y:4277 +//line mysql_sql.y:4280 { yyLOCAL = yyDollar[1].PartitionNamesUnion() } @@ -18841,7 +18902,7 @@ yydefault: case 540: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.IdentifierList -//line mysql_sql.y:4283 +//line mysql_sql.y:4286 { yyLOCAL = tree.IdentifierList{tree.Identifier(yyDollar[1].cstrUnion().Compare())} } @@ -18849,7 +18910,7 @@ yydefault: case 541: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL tree.IdentifierList -//line mysql_sql.y:4287 +//line mysql_sql.y:4290 { yyLOCAL = append(yyDollar[1].PartitionNamesUnion(), tree.Identifier(yyDollar[3].cstrUnion().Compare())) } @@ -18857,7 +18918,7 @@ yydefault: case 542: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL tree.AlterTableOption -//line mysql_sql.y:4293 +//line mysql_sql.y:4296 { var def = yyDollar[2].tableDefUnion() opt := tree.NewAlterOptionAdd(def) @@ -18867,7 +18928,7 @@ yydefault: case 543: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL tree.AlterTableOption -//line mysql_sql.y:4299 +//line mysql_sql.y:4302 { var typ = tree.AlterTableModifyColumn var newColumn = yyDollar[3].columnTableDefUnion() @@ -18879,7 +18940,7 @@ yydefault: case 544: yyDollar = yyS[yypt-5 : yypt+1] var yyLOCAL tree.AlterTableOption -//line mysql_sql.y:4307 +//line mysql_sql.y:4310 { // Type OldColumnName NewColumn Position var typ = tree.AlterTableChangeColumn @@ -18893,7 +18954,7 @@ yydefault: case 545: yyDollar = yyS[yypt-5 : yypt+1] var yyLOCAL tree.AlterTableOption -//line mysql_sql.y:4317 +//line mysql_sql.y:4320 { var typ = tree.AlterTableRenameColumn var oldColumnName = yyDollar[3].unresolvedNameUnion() @@ -18905,7 +18966,7 @@ yydefault: case 546: yyDollar = yyS[yypt-6 : yypt+1] var yyLOCAL tree.AlterTableOption -//line mysql_sql.y:4325 +//line mysql_sql.y:4328 { var typ = tree.AlterTableAlterColumn var columnName = yyDollar[3].unresolvedNameUnion() @@ -18919,7 +18980,7 @@ yydefault: case 547: yyDollar = yyS[yypt-5 : yypt+1] var yyLOCAL tree.AlterTableOption -//line mysql_sql.y:4335 +//line mysql_sql.y:4338 { var typ = tree.AlterTableAlterColumn var columnName = yyDollar[3].unresolvedNameUnion() @@ -18933,7 +18994,7 @@ yydefault: case 548: yyDollar = yyS[yypt-5 : yypt+1] var yyLOCAL tree.AlterTableOption -//line mysql_sql.y:4345 +//line mysql_sql.y:4348 { var typ = tree.AlterTableAlterColumn var columnName = yyDollar[3].unresolvedNameUnion() @@ -18947,7 +19008,7 @@ yydefault: case 549: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL tree.AlterTableOption -//line mysql_sql.y:4355 +//line mysql_sql.y:4358 { var orderByClauseType = tree.AlterTableOrderByColumn var orderByColumnList = yyDollar[3].alterColumnOrderByUnion() @@ -18958,7 +19019,7 @@ yydefault: case 550: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL tree.AlterTableOption -//line mysql_sql.y:4362 +//line mysql_sql.y:4365 { yyLOCAL = tree.AlterTableOption(yyDollar[2].alterTableOptionUnion()) } @@ -18966,7 +19027,7 @@ yydefault: case 551: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL tree.AlterTableOption -//line mysql_sql.y:4366 +//line mysql_sql.y:4369 { yyLOCAL = tree.AlterTableOption(yyDollar[2].alterTableOptionUnion()) } @@ -18974,7 +19035,7 @@ yydefault: case 552: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.AlterTableOption -//line mysql_sql.y:4370 +//line mysql_sql.y:4373 { yyLOCAL = tree.AlterTableOption(yyDollar[1].tableOptionUnion()) } @@ -18982,7 +19043,7 @@ yydefault: case 553: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL tree.AlterTableOption -//line mysql_sql.y:4374 +//line mysql_sql.y:4377 { yyLOCAL = tree.AlterTableOption(yyDollar[3].alterTableOptionUnion()) } @@ -18990,7 +19051,7 @@ yydefault: case 554: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL tree.AlterTableOption -//line mysql_sql.y:4378 +//line mysql_sql.y:4381 { var column = yyDollar[3].columnTableDefUnion() var position = yyDollar[4].alterColPositionUnion() @@ -19001,7 +19062,7 @@ yydefault: case 555: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL tree.AlterTableOption -//line mysql_sql.y:4385 +//line mysql_sql.y:4388 { yyLOCAL = tree.NewAlterOptionAlgorithm(yyDollar[3].str) } @@ -19009,7 +19070,7 @@ yydefault: case 556: yyDollar = yyS[yypt-7 : yypt+1] var yyLOCAL tree.AlterTableOption -//line mysql_sql.y:4389 +//line mysql_sql.y:4392 { yyLOCAL = tree.NewTableOptionCharset(yyDollar[4].str) } @@ -19017,7 +19078,7 @@ yydefault: case 557: yyDollar = yyS[yypt-5 : yypt+1] var yyLOCAL tree.AlterTableOption -//line mysql_sql.y:4393 +//line mysql_sql.y:4396 { yyLOCAL = tree.NewTableOptionCharset(yyDollar[5].str) } @@ -19025,7 +19086,7 @@ yydefault: case 558: yyDollar = yyS[yypt-8 : yypt+1] var yyLOCAL tree.AlterTableOption -//line mysql_sql.y:4397 +//line mysql_sql.y:4400 { yyLOCAL = tree.NewTableOptionCharset(yyDollar[5].str) } @@ -19033,7 +19094,7 @@ yydefault: case 559: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL tree.AlterTableOption -//line mysql_sql.y:4401 +//line mysql_sql.y:4404 { yyLOCAL = tree.NewTableOptionCharset(yyDollar[1].str) } @@ -19041,7 +19102,7 @@ yydefault: case 560: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL tree.AlterTableOption -//line mysql_sql.y:4405 +//line mysql_sql.y:4408 { yyLOCAL = tree.NewTableOptionCharset(yyDollar[1].str) } @@ -19049,7 +19110,7 @@ yydefault: case 561: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.AlterTableOption -//line mysql_sql.y:4409 +//line mysql_sql.y:4412 { yyLOCAL = tree.NewTableOptionCharset(yyDollar[1].str) } @@ -19057,7 +19118,7 @@ yydefault: case 562: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL tree.AlterTableOption -//line mysql_sql.y:4413 +//line mysql_sql.y:4416 { yyLOCAL = tree.NewAlterOptionLock(yyDollar[3].str) } @@ -19065,33 +19126,33 @@ yydefault: case 563: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL tree.AlterTableOption -//line mysql_sql.y:4417 +//line mysql_sql.y:4420 { yyLOCAL = tree.NewTableOptionCharset(yyDollar[1].str) } yyVAL.union = yyLOCAL case 564: yyDollar = yyS[yypt-0 : yypt+1] -//line mysql_sql.y:4422 +//line mysql_sql.y:4425 { yyVAL.str = "" } case 581: yyDollar = yyS[yypt-0 : yypt+1] -//line mysql_sql.y:4453 +//line mysql_sql.y:4456 { yyVAL.str = "" } case 582: yyDollar = yyS[yypt-1 : yypt+1] -//line mysql_sql.y:4457 +//line mysql_sql.y:4460 { yyVAL.str = string("COLUMN") } case 583: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL *tree.ColumnPosition -//line mysql_sql.y:4462 +//line mysql_sql.y:4465 { var typ = tree.ColumnPositionNone var relativeColumn *tree.UnresolvedName @@ -19101,7 +19162,7 @@ yydefault: case 584: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL *tree.ColumnPosition -//line mysql_sql.y:4468 +//line mysql_sql.y:4471 { var typ = tree.ColumnPositionFirst var relativeColumn *tree.UnresolvedName @@ -19111,7 +19172,7 @@ yydefault: case 585: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL *tree.ColumnPosition -//line mysql_sql.y:4474 +//line mysql_sql.y:4477 { var typ = tree.ColumnPositionAfter var relativeColumn = yyDollar[2].unresolvedNameUnion() @@ -19121,7 +19182,7 @@ yydefault: case 586: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL []*tree.AlterColumnOrder -//line mysql_sql.y:4482 +//line mysql_sql.y:4485 { yyLOCAL = []*tree.AlterColumnOrder{yyDollar[1].alterColumnOrderUnion()} } @@ -19129,7 +19190,7 @@ yydefault: case 587: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL []*tree.AlterColumnOrder -//line mysql_sql.y:4486 +//line mysql_sql.y:4489 { yyLOCAL = append(yyDollar[1].alterColumnOrderByUnion(), yyDollar[3].alterColumnOrderUnion()) } @@ -19137,7 +19198,7 @@ yydefault: case 588: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL *tree.AlterColumnOrder -//line mysql_sql.y:4492 +//line mysql_sql.y:4495 { var column = yyDollar[1].unresolvedNameUnion() var direction = yyDollar[2].directionUnion() @@ -19147,7 +19208,7 @@ yydefault: case 589: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.AlterTableOption -//line mysql_sql.y:4500 +//line mysql_sql.y:4503 { var name = yyDollar[1].unresolvedObjectNameUnion() yyLOCAL = tree.NewAlterOptionTableName(name) @@ -19156,7 +19217,7 @@ yydefault: case 590: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL tree.AlterTableOption -//line mysql_sql.y:4507 +//line mysql_sql.y:4510 { var dropType = tree.AlterTableDropIndex var name = tree.Identifier(yyDollar[2].cstrUnion().Compare()) @@ -19166,7 +19227,7 @@ yydefault: case 591: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL tree.AlterTableOption -//line mysql_sql.y:4513 +//line mysql_sql.y:4516 { var dropType = tree.AlterTableDropKey var name = tree.Identifier(yyDollar[2].cstrUnion().Compare()) @@ -19176,7 +19237,7 @@ yydefault: case 592: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.AlterTableOption -//line mysql_sql.y:4519 +//line mysql_sql.y:4522 { var dropType = tree.AlterTableDropColumn var name = tree.Identifier(yyDollar[1].cstrUnion().Compare()) @@ -19186,7 +19247,7 @@ yydefault: case 593: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL tree.AlterTableOption -//line mysql_sql.y:4525 +//line mysql_sql.y:4528 { var dropType = tree.AlterTableDropColumn var name = tree.Identifier(yyDollar[2].cstrUnion().Compare()) @@ -19196,7 +19257,7 @@ yydefault: case 594: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL tree.AlterTableOption -//line mysql_sql.y:4531 +//line mysql_sql.y:4534 { var dropType = tree.AlterTableDropForeignKey var name = tree.Identifier(yyDollar[3].cstrUnion().Compare()) @@ -19207,7 +19268,7 @@ yydefault: case 595: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL tree.AlterTableOption -//line mysql_sql.y:4538 +//line mysql_sql.y:4541 { yyLOCAL = &tree.AlterOptionDrop{ Typ: tree.AlterTableDropForeignKey, @@ -19218,7 +19279,7 @@ yydefault: case 596: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL tree.AlterTableOption -//line mysql_sql.y:4545 +//line mysql_sql.y:4548 { var dropType = tree.AlterTableDropPrimaryKey var name = tree.Identifier("") @@ -19228,7 +19289,7 @@ yydefault: case 597: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL tree.AlterTableOption -//line mysql_sql.y:4553 +//line mysql_sql.y:4556 { var indexName = tree.Identifier(yyDollar[2].cstrUnion().Compare()) var visibility = yyDollar[3].indexVisibilityUnion() @@ -19238,7 +19299,7 @@ yydefault: case 598: yyDollar = yyS[yypt-5 : yypt+1] var yyLOCAL tree.AlterTableOption -//line mysql_sql.y:4559 +//line mysql_sql.y:4562 { var io *tree.IndexOption = nil if yyDollar[5].indexOptionUnion() == nil { @@ -19257,7 +19318,7 @@ yydefault: case 599: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL tree.AlterTableOption -//line mysql_sql.y:4574 +//line mysql_sql.y:4577 { var checkType = yyDollar[1].str var enforce = yyDollar[3].boolValUnion() @@ -19267,7 +19328,7 @@ yydefault: case 600: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL tree.AlterTableOption -//line mysql_sql.y:4580 +//line mysql_sql.y:4583 { var checkType = yyDollar[1].str var enforce = yyDollar[3].boolValUnion() @@ -19277,7 +19338,7 @@ yydefault: case 601: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.VisibleType -//line mysql_sql.y:4588 +//line mysql_sql.y:4591 { yyLOCAL = tree.VISIBLE_TYPE_VISIBLE } @@ -19285,7 +19346,7 @@ yydefault: case 602: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.VisibleType -//line mysql_sql.y:4592 +//line mysql_sql.y:4595 { yyLOCAL = tree.VISIBLE_TYPE_INVISIBLE } @@ -19293,7 +19354,7 @@ yydefault: case 603: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL bool -//line mysql_sql.y:4598 +//line mysql_sql.y:4601 { yyLOCAL = true } @@ -19301,7 +19362,7 @@ yydefault: case 604: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL bool -//line mysql_sql.y:4602 +//line mysql_sql.y:4605 { yyLOCAL = false } @@ -19309,7 +19370,7 @@ yydefault: case 605: yyDollar = yyS[yypt-7 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:4608 +//line mysql_sql.y:4611 { var ifExists = yyDollar[3].boolValUnion() var name = yyDollar[4].exprUnion() @@ -19329,7 +19390,7 @@ yydefault: case 606: yyDollar = yyS[yypt-7 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:4626 +//line mysql_sql.y:4629 { var accountName = "" var dbName = yyDollar[3].str @@ -19348,7 +19409,7 @@ yydefault: case 607: yyDollar = yyS[yypt-7 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:4641 +//line mysql_sql.y:4644 { var accountName = "" var dbName = yyDollar[3].str @@ -19367,7 +19428,7 @@ yydefault: case 608: yyDollar = yyS[yypt-8 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:4656 +//line mysql_sql.y:4659 { var accountName = yyDollar[4].str var dbName = "" @@ -19386,7 +19447,7 @@ yydefault: case 609: yyDollar = yyS[yypt-8 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:4671 +//line mysql_sql.y:4674 { assignments := []*tree.VarAssignmentExpr{ { @@ -19402,7 +19463,7 @@ yydefault: case 610: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL tree.AlterAccountAuthOption -//line mysql_sql.y:4684 +//line mysql_sql.y:4687 { yyLOCAL = tree.AlterAccountAuthOption{ Exist: false, @@ -19412,7 +19473,7 @@ yydefault: case 611: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL tree.AlterAccountAuthOption -//line mysql_sql.y:4690 +//line mysql_sql.y:4693 { yyLOCAL = tree.AlterAccountAuthOption{ Exist: true, @@ -19425,7 +19486,7 @@ yydefault: case 612: yyDollar = yyS[yypt-7 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:4701 +//line mysql_sql.y:4704 { // Create temporary variables with meaningful names ifExists := yyDollar[3].boolValUnion() @@ -19441,7 +19502,7 @@ yydefault: case 613: yyDollar = yyS[yypt-6 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:4713 +//line mysql_sql.y:4716 { ifExists := yyDollar[3].boolValUnion() var Username = yyDollar[4].usernameRecordUnion().Username @@ -19456,7 +19517,7 @@ yydefault: case 614: yyDollar = yyS[yypt-6 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:4724 +//line mysql_sql.y:4727 { ifExists := yyDollar[3].boolValUnion() var Username = yyDollar[4].usernameRecordUnion().Username @@ -19471,7 +19532,7 @@ yydefault: case 615: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL *tree.Role -//line mysql_sql.y:4736 +//line mysql_sql.y:4739 { yyLOCAL = nil } @@ -19479,7 +19540,7 @@ yydefault: case 616: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL *tree.Role -//line mysql_sql.y:4740 +//line mysql_sql.y:4743 { var UserName = yyDollar[3].str yyLOCAL = tree.NewRole( @@ -19490,7 +19551,7 @@ yydefault: case 617: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL bool -//line mysql_sql.y:4748 +//line mysql_sql.y:4751 { yyLOCAL = false } @@ -19498,7 +19559,7 @@ yydefault: case 618: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL bool -//line mysql_sql.y:4752 +//line mysql_sql.y:4755 { yyLOCAL = true } @@ -19506,7 +19567,7 @@ yydefault: case 619: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL tree.UserMiscOption -//line mysql_sql.y:4757 +//line mysql_sql.y:4760 { yyLOCAL = nil } @@ -19514,7 +19575,7 @@ yydefault: case 620: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.UserMiscOption -//line mysql_sql.y:4761 +//line mysql_sql.y:4764 { yyLOCAL = yyDollar[1].userMiscOptionUnion() } @@ -19522,7 +19583,7 @@ yydefault: case 621: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.UserMiscOption -//line mysql_sql.y:4777 +//line mysql_sql.y:4780 { yyLOCAL = tree.NewUserMiscOptionAccountUnlock() } @@ -19530,7 +19591,7 @@ yydefault: case 622: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.UserMiscOption -//line mysql_sql.y:4781 +//line mysql_sql.y:4784 { yyLOCAL = tree.NewUserMiscOptionAccountLock() } @@ -19538,7 +19599,7 @@ yydefault: case 623: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.UserMiscOption -//line mysql_sql.y:4785 +//line mysql_sql.y:4788 { yyLOCAL = tree.NewUserMiscOptionPasswordExpireNone() } @@ -19546,7 +19607,7 @@ yydefault: case 624: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL tree.UserMiscOption -//line mysql_sql.y:4789 +//line mysql_sql.y:4792 { var Value = yyDollar[3].item.(int64) yyLOCAL = tree.NewUserMiscOptionPasswordExpireInterval( @@ -19557,7 +19618,7 @@ yydefault: case 625: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL tree.UserMiscOption -//line mysql_sql.y:4796 +//line mysql_sql.y:4799 { yyLOCAL = tree.NewUserMiscOptionPasswordExpireNever() } @@ -19565,7 +19626,7 @@ yydefault: case 626: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL tree.UserMiscOption -//line mysql_sql.y:4800 +//line mysql_sql.y:4803 { yyLOCAL = tree.NewUserMiscOptionPasswordExpireDefault() } @@ -19573,7 +19634,7 @@ yydefault: case 627: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL tree.UserMiscOption -//line mysql_sql.y:4804 +//line mysql_sql.y:4807 { yyLOCAL = tree.NewUserMiscOptionPasswordHistoryDefault() } @@ -19581,7 +19642,7 @@ yydefault: case 628: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL tree.UserMiscOption -//line mysql_sql.y:4808 +//line mysql_sql.y:4811 { var Value = yyDollar[3].item.(int64) yyLOCAL = tree.NewUserMiscOptionPasswordHistoryCount( @@ -19592,7 +19653,7 @@ yydefault: case 629: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL tree.UserMiscOption -//line mysql_sql.y:4815 +//line mysql_sql.y:4818 { yyLOCAL = tree.NewUserMiscOptionPasswordReuseIntervalDefault() } @@ -19600,7 +19661,7 @@ yydefault: case 630: yyDollar = yyS[yypt-5 : yypt+1] var yyLOCAL tree.UserMiscOption -//line mysql_sql.y:4819 +//line mysql_sql.y:4822 { var Value = yyDollar[4].item.(int64) yyLOCAL = tree.NewUserMiscOptionPasswordReuseIntervalCount( @@ -19611,7 +19672,7 @@ yydefault: case 631: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL tree.UserMiscOption -//line mysql_sql.y:4826 +//line mysql_sql.y:4829 { yyLOCAL = tree.NewUserMiscOptionPasswordRequireCurrentNone() } @@ -19619,7 +19680,7 @@ yydefault: case 632: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL tree.UserMiscOption -//line mysql_sql.y:4830 +//line mysql_sql.y:4833 { yyLOCAL = tree.NewUserMiscOptionPasswordRequireCurrentDefault() } @@ -19627,7 +19688,7 @@ yydefault: case 633: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL tree.UserMiscOption -//line mysql_sql.y:4834 +//line mysql_sql.y:4837 { yyLOCAL = tree.NewUserMiscOptionPasswordRequireCurrentOptional() } @@ -19635,7 +19696,7 @@ yydefault: case 634: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL tree.UserMiscOption -//line mysql_sql.y:4838 +//line mysql_sql.y:4841 { var Value = yyDollar[2].item.(int64) yyLOCAL = tree.NewUserMiscOptionFailedLoginAttempts( @@ -19646,7 +19707,7 @@ yydefault: case 635: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL tree.UserMiscOption -//line mysql_sql.y:4845 +//line mysql_sql.y:4848 { var Value = yyDollar[2].item.(int64) yyLOCAL = tree.NewUserMiscOptionPasswordLockTimeCount( @@ -19657,27 +19718,27 @@ yydefault: case 636: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL tree.UserMiscOption -//line mysql_sql.y:4852 +//line mysql_sql.y:4855 { yyLOCAL = tree.NewUserMiscOptionPasswordLockTimeUnbounded() } yyVAL.union = yyLOCAL case 637: yyDollar = yyS[yypt-3 : yypt+1] -//line mysql_sql.y:4858 +//line mysql_sql.y:4861 { yyVAL.item = nil } case 638: yyDollar = yyS[yypt-0 : yypt+1] -//line mysql_sql.y:4863 +//line mysql_sql.y:4866 { yyVAL.item = nil } case 684: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:4918 +//line mysql_sql.y:4921 { yyLOCAL = &tree.ShowSQLTasks{} } @@ -19685,7 +19746,7 @@ yydefault: case 685: yyDollar = yyS[yypt-5 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:4924 +//line mysql_sql.y:4927 { stmt := &tree.ShowSQLTaskRuns{} if yyDollar[4].str != "" { @@ -19701,20 +19762,20 @@ yydefault: yyVAL.union = yyLOCAL case 686: yyDollar = yyS[yypt-0 : yypt+1] -//line mysql_sql.y:4938 +//line mysql_sql.y:4941 { yyVAL.str = "" } case 687: yyDollar = yyS[yypt-2 : yypt+1] -//line mysql_sql.y:4942 +//line mysql_sql.y:4945 { yyVAL.str = yyDollar[2].cstrUnion().Compare() } case 688: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL int64 -//line mysql_sql.y:4947 +//line mysql_sql.y:4950 { yyLOCAL = -1 } @@ -19722,7 +19783,7 @@ yydefault: case 689: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL int64 -//line mysql_sql.y:4951 +//line mysql_sql.y:4954 { yyLOCAL = sqlTaskInt64(yyDollar[2].item) } @@ -19730,7 +19791,7 @@ yydefault: case 690: yyDollar = yyS[yypt-5 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:4957 +//line mysql_sql.y:4960 { yyLOCAL = &tree.ShowIcebergCatalogs{ Like: yyDollar[4].comparisionExprUnion(), @@ -19741,7 +19802,7 @@ yydefault: case 691: yyDollar = yyS[yypt-7 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:4965 +//line mysql_sql.y:4968 { yyLOCAL = &tree.ShowIcebergNamespaces{ Catalog: tree.Identifier(yyDollar[5].cstrUnion().Compare()), @@ -19753,7 +19814,7 @@ yydefault: case 692: yyDollar = yyS[yypt-8 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:4973 +//line mysql_sql.y:4976 { yyLOCAL = &tree.ShowIcebergNamespaces{ Catalog: tree.Identifier(yyDollar[6].cstrUnion().Compare()), @@ -19765,7 +19826,7 @@ yydefault: case 693: yyDollar = yyS[yypt-5 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:4981 +//line mysql_sql.y:4984 { yyLOCAL = &tree.ShowIcebergNamespaces{ Like: yyDollar[4].comparisionExprUnion(), @@ -19776,7 +19837,7 @@ yydefault: case 694: yyDollar = yyS[yypt-5 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:4988 +//line mysql_sql.y:4991 { yyLOCAL = &tree.ShowIcebergTables{ Like: yyDollar[4].comparisionExprUnion(), @@ -19787,7 +19848,7 @@ yydefault: case 695: yyDollar = yyS[yypt-7 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:4995 +//line mysql_sql.y:4998 { yyLOCAL = &tree.ShowIcebergTables{ Catalog: tree.Identifier(yyDollar[5].cstrUnion().Compare()), @@ -19799,7 +19860,7 @@ yydefault: case 696: yyDollar = yyS[yypt-9 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:5003 +//line mysql_sql.y:5006 { yyLOCAL = &tree.ShowIcebergTables{ Catalog: tree.Identifier(yyDollar[5].cstrUnion().Compare()), @@ -19812,7 +19873,7 @@ yydefault: case 697: yyDollar = yyS[yypt-8 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:5012 +//line mysql_sql.y:5015 { yyLOCAL = &tree.ShowIcebergTables{ Catalog: tree.Identifier(yyDollar[6].cstrUnion().Compare()), @@ -19824,7 +19885,7 @@ yydefault: case 698: yyDollar = yyS[yypt-8 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:5020 +//line mysql_sql.y:5023 { yyLOCAL = &tree.ShowIcebergTables{ Namespace: yyDollar[6].str, @@ -19836,7 +19897,7 @@ yydefault: case 699: yyDollar = yyS[yypt-11 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:5028 +//line mysql_sql.y:5031 { yyLOCAL = &tree.ShowIcebergTables{ Namespace: yyDollar[6].str, @@ -19849,7 +19910,7 @@ yydefault: case 700: yyDollar = yyS[yypt-5 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:5039 +//line mysql_sql.y:5042 { yyLOCAL = &tree.ShowMongoDBConnections{ Like: yyDollar[4].comparisionExprUnion(), @@ -19859,14 +19920,14 @@ yydefault: yyVAL.union = yyLOCAL case 701: yyDollar = yyS[yypt-1 : yypt+1] -//line mysql_sql.y:5048 +//line mysql_sql.y:5051 { yyVAL.str = yyDollar[1].str } case 702: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:5054 +//line mysql_sql.y:5057 { yyLOCAL = &tree.ShowLogserviceReplicas{} } @@ -19874,7 +19935,7 @@ yydefault: case 703: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:5060 +//line mysql_sql.y:5063 { yyLOCAL = &tree.ShowLogserviceStores{} } @@ -19882,7 +19943,7 @@ yydefault: case 704: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:5066 +//line mysql_sql.y:5069 { yyLOCAL = &tree.ShowLogserviceSettings{} } @@ -19890,7 +19951,7 @@ yydefault: case 705: yyDollar = yyS[yypt-5 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:5072 +//line mysql_sql.y:5075 { yyLOCAL = &tree.ShowRules{ RoleName: yyDollar[5].cstrUnion().Compare(), @@ -19900,7 +19961,7 @@ yydefault: case 706: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:5080 +//line mysql_sql.y:5083 { yyLOCAL = &tree.ShowCollation{ Like: yyDollar[3].comparisionExprUnion(), @@ -19911,7 +19972,7 @@ yydefault: case 707: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:5089 +//line mysql_sql.y:5092 { yyLOCAL = &tree.ShowStages{ Like: yyDollar[3].comparisionExprUnion(), @@ -19921,7 +19982,7 @@ yydefault: case 708: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:5097 +//line mysql_sql.y:5100 { yyLOCAL = &tree.ShowSnapShots{ Where: yyDollar[3].whereUnion(), @@ -19931,7 +19992,7 @@ yydefault: case 709: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:5105 +//line mysql_sql.y:5108 { yyLOCAL = &tree.ShowPitr{ Where: yyDollar[3].whereUnion(), @@ -19941,7 +20002,7 @@ yydefault: case 710: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:5113 +//line mysql_sql.y:5116 { yyLOCAL = &tree.ShowRecoveryWindow{ Level: tree.RECOVERYWINDOWLEVELACCOUNT, @@ -19951,7 +20012,7 @@ yydefault: case 711: yyDollar = yyS[yypt-5 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:5119 +//line mysql_sql.y:5122 { yyLOCAL = &tree.ShowRecoveryWindow{ Level: tree.RECOVERYWINDOWLEVELDATABASE, @@ -19962,7 +20023,7 @@ yydefault: case 712: yyDollar = yyS[yypt-6 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:5126 +//line mysql_sql.y:5129 { yyLOCAL = &tree.ShowRecoveryWindow{ Level: tree.RECOVERYWINDOWLEVELTABLE, @@ -19974,7 +20035,7 @@ yydefault: case 713: yyDollar = yyS[yypt-5 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:5134 +//line mysql_sql.y:5137 { yyLOCAL = &tree.ShowRecoveryWindow{ Level: tree.RECOVERYWINDOWLEVELACCOUNT, @@ -19985,7 +20046,7 @@ yydefault: case 714: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:5143 +//line mysql_sql.y:5146 { yyLOCAL = &tree.ShowGrants{ShowGrantType: tree.GrantForUser} } @@ -19993,7 +20054,7 @@ yydefault: case 715: yyDollar = yyS[yypt-5 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:5147 +//line mysql_sql.y:5150 { yyLOCAL = &tree.ShowGrants{Username: yyDollar[4].usernameRecordUnion().Username, Hostname: yyDollar[4].usernameRecordUnion().Hostname, Roles: yyDollar[5].rolesUnion(), ShowGrantType: tree.GrantForUser} } @@ -20001,7 +20062,7 @@ yydefault: case 716: yyDollar = yyS[yypt-5 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:5151 +//line mysql_sql.y:5154 { s := &tree.ShowGrants{} roles := []*tree.Role{ @@ -20015,7 +20076,7 @@ yydefault: case 717: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL []*tree.Role -//line mysql_sql.y:5162 +//line mysql_sql.y:5165 { yyLOCAL = nil } @@ -20023,7 +20084,7 @@ yydefault: case 718: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL []*tree.Role -//line mysql_sql.y:5166 +//line mysql_sql.y:5169 { yyLOCAL = yyDollar[2].rolesUnion() } @@ -20031,25 +20092,25 @@ yydefault: case 719: yyDollar = yyS[yypt-7 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:5172 +//line mysql_sql.y:5175 { yyLOCAL = &tree.ShowTableStatus{DbName: yyDollar[5].str, Like: yyDollar[6].comparisionExprUnion(), Where: yyDollar[7].whereUnion()} } yyVAL.union = yyLOCAL case 720: yyDollar = yyS[yypt-0 : yypt+1] -//line mysql_sql.y:5177 +//line mysql_sql.y:5180 { } case 722: yyDollar = yyS[yypt-0 : yypt+1] -//line mysql_sql.y:5181 +//line mysql_sql.y:5184 { } case 724: yyDollar = yyS[yypt-5 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:5186 +//line mysql_sql.y:5189 { yyLOCAL = &tree.ShowFunctionOrProcedureStatus{ Like: yyDollar[4].comparisionExprUnion(), @@ -20061,7 +20122,7 @@ yydefault: case 725: yyDollar = yyS[yypt-5 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:5196 +//line mysql_sql.y:5199 { yyLOCAL = &tree.ShowFunctionOrProcedureStatus{ Like: yyDollar[4].comparisionExprUnion(), @@ -20073,7 +20134,7 @@ yydefault: case 726: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:5206 +//line mysql_sql.y:5209 { yyLOCAL = &tree.ShowRolesStmt{ Like: yyDollar[3].comparisionExprUnion(), @@ -20083,7 +20144,7 @@ yydefault: case 727: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:5214 +//line mysql_sql.y:5217 { yyLOCAL = &tree.ShowNodeList{} } @@ -20091,7 +20152,7 @@ yydefault: case 728: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:5220 +//line mysql_sql.y:5223 { yyLOCAL = &tree.ShowLocks{} } @@ -20099,7 +20160,7 @@ yydefault: case 729: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:5226 +//line mysql_sql.y:5229 { yyLOCAL = &tree.ShowTableNumber{DbName: yyDollar[4].str} } @@ -20107,7 +20168,7 @@ yydefault: case 730: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:5232 +//line mysql_sql.y:5235 { yyLOCAL = &tree.ShowColumnNumber{Table: yyDollar[3].unresolvedObjectNameUnion(), DbName: yyDollar[4].str} } @@ -20115,7 +20176,7 @@ yydefault: case 731: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:5238 +//line mysql_sql.y:5241 { yyLOCAL = &tree.ShowTableValues{Table: yyDollar[3].unresolvedObjectNameUnion(), DbName: yyDollar[4].str} } @@ -20123,7 +20184,7 @@ yydefault: case 732: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:5244 +//line mysql_sql.y:5247 { yyLOCAL = &tree.ShowTableSize{Table: yyDollar[3].unresolvedObjectNameUnion(), DbName: yyDollar[4].str} } @@ -20131,7 +20192,7 @@ yydefault: case 733: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:5250 +//line mysql_sql.y:5253 { s := yyDollar[2].statementUnion().(*tree.ShowTarget) s.Like = yyDollar[3].comparisionExprUnion() @@ -20142,7 +20203,7 @@ yydefault: case 734: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:5259 +//line mysql_sql.y:5262 { yyLOCAL = &tree.ShowTarget{Type: tree.ShowConfig} } @@ -20150,7 +20211,7 @@ yydefault: case 735: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:5263 +//line mysql_sql.y:5266 { yyLOCAL = &tree.ShowTarget{Type: tree.ShowCharset} } @@ -20158,7 +20219,7 @@ yydefault: case 736: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:5267 +//line mysql_sql.y:5270 { yyLOCAL = &tree.ShowTarget{Type: tree.ShowEngines} } @@ -20166,7 +20227,7 @@ yydefault: case 737: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:5271 +//line mysql_sql.y:5274 { yyLOCAL = &tree.ShowTarget{DbName: yyDollar[3].str, Type: tree.ShowTriggers} } @@ -20174,7 +20235,7 @@ yydefault: case 738: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:5275 +//line mysql_sql.y:5278 { yyLOCAL = &tree.ShowTarget{DbName: yyDollar[3].str, Type: tree.ShowEvents} } @@ -20182,7 +20243,7 @@ yydefault: case 739: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:5279 +//line mysql_sql.y:5282 { yyLOCAL = &tree.ShowTarget{Type: tree.ShowPlugins} } @@ -20190,7 +20251,7 @@ yydefault: case 740: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:5283 +//line mysql_sql.y:5286 { yyLOCAL = &tree.ShowTarget{Type: tree.ShowPrivileges} } @@ -20198,7 +20259,7 @@ yydefault: case 741: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:5287 +//line mysql_sql.y:5290 { yyLOCAL = &tree.ShowTarget{Type: tree.ShowProfiles} } @@ -20206,7 +20267,7 @@ yydefault: case 742: yyDollar = yyS[yypt-6 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:5293 +//line mysql_sql.y:5296 { yyLOCAL = &tree.ShowIndex{ TableName: yyDollar[4].unresolvedObjectNameUnion(), @@ -20217,18 +20278,18 @@ yydefault: yyVAL.union = yyLOCAL case 743: yyDollar = yyS[yypt-0 : yypt+1] -//line mysql_sql.y:5302 +//line mysql_sql.y:5305 { } case 744: yyDollar = yyS[yypt-1 : yypt+1] -//line mysql_sql.y:5304 +//line mysql_sql.y:5307 { } case 748: yyDollar = yyS[yypt-5 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:5313 +//line mysql_sql.y:5316 { yyLOCAL = &tree.ShowVariables{ Global: yyDollar[2].boolValUnion(), @@ -20240,7 +20301,7 @@ yydefault: case 749: yyDollar = yyS[yypt-5 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:5323 +//line mysql_sql.y:5326 { yyLOCAL = &tree.ShowStatus{ Global: yyDollar[2].boolValUnion(), @@ -20252,7 +20313,7 @@ yydefault: case 750: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL bool -//line mysql_sql.y:5332 +//line mysql_sql.y:5335 { yyLOCAL = false } @@ -20260,7 +20321,7 @@ yydefault: case 751: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL bool -//line mysql_sql.y:5336 +//line mysql_sql.y:5339 { yyLOCAL = true } @@ -20268,7 +20329,7 @@ yydefault: case 752: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL bool -//line mysql_sql.y:5340 +//line mysql_sql.y:5343 { yyLOCAL = false } @@ -20276,7 +20337,7 @@ yydefault: case 753: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:5346 +//line mysql_sql.y:5349 { yyLOCAL = &tree.ShowWarnings{Limit: yyDollar[3].limitUnion()} } @@ -20284,7 +20345,7 @@ yydefault: case 754: yyDollar = yyS[yypt-6 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:5350 +//line mysql_sql.y:5353 { yyLOCAL = &tree.ShowWarnings{Count: true} } @@ -20292,7 +20353,7 @@ yydefault: case 755: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:5356 +//line mysql_sql.y:5359 { yyLOCAL = &tree.ShowErrors{Limit: yyDollar[3].limitUnion()} } @@ -20300,7 +20361,7 @@ yydefault: case 756: yyDollar = yyS[yypt-6 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:5360 +//line mysql_sql.y:5363 { yyLOCAL = &tree.ShowErrors{Count: true} } @@ -20308,7 +20369,7 @@ yydefault: case 757: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:5366 +//line mysql_sql.y:5369 { yyLOCAL = &tree.ShowProcessList{Full: yyDollar[2].fullOptUnion()} } @@ -20316,7 +20377,7 @@ yydefault: case 758: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:5372 +//line mysql_sql.y:5375 { yyLOCAL = &tree.ShowSequences{ DBName: yyDollar[3].str, @@ -20327,7 +20388,7 @@ yydefault: case 759: yyDollar = yyS[yypt-7 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:5381 +//line mysql_sql.y:5384 { yyLOCAL = &tree.ShowTables{ Open: false, @@ -20342,7 +20403,7 @@ yydefault: case 760: yyDollar = yyS[yypt-7 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:5392 +//line mysql_sql.y:5395 { yyLOCAL = &tree.ShowTables{ Open: true, @@ -20356,7 +20417,7 @@ yydefault: case 761: yyDollar = yyS[yypt-5 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:5404 +//line mysql_sql.y:5407 { yyLOCAL = &tree.ShowDatabases{ Like: yyDollar[3].comparisionExprUnion(), @@ -20368,7 +20429,7 @@ yydefault: case 762: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:5412 +//line mysql_sql.y:5415 { yyLOCAL = &tree.ShowDatabases{Like: yyDollar[3].comparisionExprUnion(), Where: yyDollar[4].whereUnion()} } @@ -20376,7 +20437,7 @@ yydefault: case 763: yyDollar = yyS[yypt-7 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:5418 +//line mysql_sql.y:5421 { yyLOCAL = &tree.ShowColumns{ Ext: false, @@ -20392,7 +20453,7 @@ yydefault: case 764: yyDollar = yyS[yypt-8 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:5430 +//line mysql_sql.y:5433 { yyLOCAL = &tree.ShowColumns{ Ext: true, @@ -20408,7 +20469,7 @@ yydefault: case 765: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:5444 +//line mysql_sql.y:5447 { yyLOCAL = &tree.ShowAccounts{Like: yyDollar[3].comparisionExprUnion()} } @@ -20416,7 +20477,7 @@ yydefault: case 766: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:5450 +//line mysql_sql.y:5453 { yyLOCAL = &tree.ShowPublications{Like: yyDollar[3].comparisionExprUnion()} } @@ -20424,7 +20485,7 @@ yydefault: case 767: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:5456 +//line mysql_sql.y:5459 { yyLOCAL = &tree.ShowPublicationCoverage{Name: yyDollar[4].str} } @@ -20432,7 +20493,7 @@ yydefault: case 768: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:5462 +//line mysql_sql.y:5465 { yyLOCAL = &tree.ShowAccountUpgrade{} } @@ -20440,7 +20501,7 @@ yydefault: case 769: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:5468 +//line mysql_sql.y:5471 { yyLOCAL = &tree.ShowSubscriptions{Like: yyDollar[3].comparisionExprUnion()} } @@ -20448,7 +20509,7 @@ yydefault: case 770: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:5472 +//line mysql_sql.y:5475 { yyLOCAL = &tree.ShowSubscriptions{All: true, Like: yyDollar[4].comparisionExprUnion()} } @@ -20456,7 +20517,7 @@ yydefault: case 771: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:5478 +//line mysql_sql.y:5481 { yyLOCAL = &tree.ShowCcprSubscriptions{TaskId: yyDollar[4].str} } @@ -20464,7 +20525,7 @@ yydefault: case 772: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:5482 +//line mysql_sql.y:5485 { yyLOCAL = &tree.ShowCcprSubscriptions{} } @@ -20472,7 +20533,7 @@ yydefault: case 773: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL *tree.ComparisonExpr -//line mysql_sql.y:5487 +//line mysql_sql.y:5490 { yyLOCAL = nil } @@ -20480,7 +20541,7 @@ yydefault: case 774: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL *tree.ComparisonExpr -//line mysql_sql.y:5491 +//line mysql_sql.y:5494 { yyLOCAL = tree.NewComparisonExpr(tree.LIKE, nil, yyDollar[2].exprUnion()) } @@ -20488,27 +20549,27 @@ yydefault: case 775: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL *tree.ComparisonExpr -//line mysql_sql.y:5495 +//line mysql_sql.y:5498 { yyLOCAL = tree.NewComparisonExpr(tree.ILIKE, nil, yyDollar[2].exprUnion()) } yyVAL.union = yyLOCAL case 776: yyDollar = yyS[yypt-0 : yypt+1] -//line mysql_sql.y:5500 +//line mysql_sql.y:5503 { yyVAL.str = "" } case 777: yyDollar = yyS[yypt-2 : yypt+1] -//line mysql_sql.y:5504 +//line mysql_sql.y:5507 { yyVAL.str = yyDollar[2].cstrUnion().Compare() } case 778: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL *tree.UnresolvedObjectName -//line mysql_sql.y:5510 +//line mysql_sql.y:5513 { yyLOCAL = yyDollar[2].unresolvedObjectNameUnion() } @@ -20516,7 +20577,7 @@ yydefault: case 783: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL bool -//line mysql_sql.y:5523 +//line mysql_sql.y:5526 { yyLOCAL = false } @@ -20524,7 +20585,7 @@ yydefault: case 784: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL bool -//line mysql_sql.y:5527 +//line mysql_sql.y:5530 { yyLOCAL = true } @@ -20532,7 +20593,7 @@ yydefault: case 785: yyDollar = yyS[yypt-5 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:5533 +//line mysql_sql.y:5536 { yyLOCAL = &tree.ShowCreateTable{ Name: yyDollar[4].unresolvedObjectNameUnion(), @@ -20543,7 +20604,7 @@ yydefault: case 786: yyDollar = yyS[yypt-5 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:5541 +//line mysql_sql.y:5544 { yyLOCAL = &tree.ShowCreateView{ Name: yyDollar[4].unresolvedObjectNameUnion(), @@ -20554,7 +20615,7 @@ yydefault: case 787: yyDollar = yyS[yypt-6 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:5548 +//line mysql_sql.y:5551 { yyLOCAL = &tree.ShowCreateDatabase{ IfNotExists: yyDollar[4].ifNotExistsUnion(), @@ -20566,7 +20627,7 @@ yydefault: case 788: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:5556 +//line mysql_sql.y:5559 { yyLOCAL = &tree.ShowCreatePublications{Name: yyDollar[4].str} } @@ -20574,7 +20635,7 @@ yydefault: case 789: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:5562 +//line mysql_sql.y:5565 { yyLOCAL = &tree.ShowBackendServers{} } @@ -20582,7 +20643,7 @@ yydefault: case 790: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL *tree.UnresolvedObjectName -//line mysql_sql.y:5568 +//line mysql_sql.y:5571 { tblName := yylex.(*Lexer).GetDbOrTblName(yyDollar[1].cstrUnion().Origin()) yyLOCAL = tree.NewUnresolvedObjectName(tblName) @@ -20591,7 +20652,7 @@ yydefault: case 791: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL *tree.UnresolvedObjectName -//line mysql_sql.y:5573 +//line mysql_sql.y:5576 { dbName := yylex.(*Lexer).GetDbOrTblName(yyDollar[1].cstrUnion().Origin()) tblName := yylex.(*Lexer).GetDbOrTblName(yyDollar[3].cstrUnion().Origin()) @@ -20600,14 +20661,14 @@ yydefault: yyVAL.union = yyLOCAL case 792: yyDollar = yyS[yypt-1 : yypt+1] -//line mysql_sql.y:5581 +//line mysql_sql.y:5584 { yyVAL.str = yylex.(*Lexer).GetDbOrTblName(yyDollar[1].cstrUnion().Origin()) } case 793: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL *tree.UnresolvedObjectName -//line mysql_sql.y:5587 +//line mysql_sql.y:5590 { tblName := yylex.(*Lexer).GetDbOrTblName(yyDollar[1].cstrUnion().Origin()) yyLOCAL = tree.NewUnresolvedObjectName(tblName) @@ -20616,7 +20677,7 @@ yydefault: case 794: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL *tree.UnresolvedObjectName -//line mysql_sql.y:5592 +//line mysql_sql.y:5595 { dbName := yylex.(*Lexer).GetDbOrTblName(yyDollar[1].cstrUnion().Origin()) tblName := yylex.(*Lexer).GetDbOrTblName(yyDollar[3].cstrUnion().Origin()) @@ -20626,7 +20687,7 @@ yydefault: case 795: yyDollar = yyS[yypt-5 : yypt+1] var yyLOCAL *tree.UnresolvedObjectName -//line mysql_sql.y:5598 +//line mysql_sql.y:5601 { yyLOCAL = tree.NewUnresolvedObjectName(yyDollar[1].cstrUnion().Compare(), yyDollar[3].cstrUnion().Compare(), yyDollar[5].cstrUnion().Compare()) } @@ -20634,7 +20695,7 @@ yydefault: case 796: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:5604 +//line mysql_sql.y:5607 { yyLOCAL = tree.NewTruncateTable(yyDollar[2].tableNameUnion()) } @@ -20642,7 +20703,7 @@ yydefault: case 797: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:5608 +//line mysql_sql.y:5611 { yyLOCAL = tree.NewTruncateTable(yyDollar[3].tableNameUnion()) } @@ -20650,7 +20711,7 @@ yydefault: case 819: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:5639 +//line mysql_sql.y:5642 { yyLOCAL = &tree.DropSQLTask{ IfExists: yyDollar[3].boolValUnion(), @@ -20661,7 +20722,7 @@ yydefault: case 820: yyDollar = yyS[yypt-5 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:5648 +//line mysql_sql.y:5651 { yyLOCAL = &tree.DropIcebergCatalog{ IfExists: yyDollar[4].boolValUnion(), @@ -20672,7 +20733,7 @@ yydefault: case 821: yyDollar = yyS[yypt-5 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:5657 +//line mysql_sql.y:5660 { yyLOCAL = &tree.DropMongoDBConnection{ IfExists: yyDollar[4].boolValUnion(), @@ -20683,7 +20744,7 @@ yydefault: case 822: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:5666 +//line mysql_sql.y:5669 { var ifExists = yyDollar[3].boolValUnion() var name = yyDollar[4].tableNamesUnion() @@ -20693,7 +20754,7 @@ yydefault: case 823: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:5674 +//line mysql_sql.y:5677 { var ifExists = yyDollar[3].boolValUnion() var name = yyDollar[4].exprUnion() @@ -20703,7 +20764,7 @@ yydefault: case 824: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:5682 +//line mysql_sql.y:5685 { var ifExists = yyDollar[3].boolValUnion() var users = yyDollar[4].usersUnion() @@ -20713,7 +20774,7 @@ yydefault: case 825: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL []*tree.User -//line mysql_sql.y:5690 +//line mysql_sql.y:5693 { yyLOCAL = []*tree.User{yyDollar[1].userUnion()} } @@ -20721,7 +20782,7 @@ yydefault: case 826: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL []*tree.User -//line mysql_sql.y:5694 +//line mysql_sql.y:5697 { yyLOCAL = append(yyDollar[1].usersUnion(), yyDollar[3].userUnion()) } @@ -20729,7 +20790,7 @@ yydefault: case 827: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL *tree.User -//line mysql_sql.y:5700 +//line mysql_sql.y:5703 { var Username = yyDollar[1].usernameRecordUnion().Username var Hostname = yyDollar[1].usernameRecordUnion().Hostname @@ -20744,7 +20805,7 @@ yydefault: case 828: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:5713 +//line mysql_sql.y:5716 { var ifExists = yyDollar[3].boolValUnion() var roles = yyDollar[4].rolesUnion() @@ -20754,7 +20815,7 @@ yydefault: case 829: yyDollar = yyS[yypt-6 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:5721 +//line mysql_sql.y:5724 { var name = tree.Identifier(yyDollar[4].cstrUnion().Compare()) var tableName = yyDollar[6].tableNameUnion() @@ -20765,7 +20826,7 @@ yydefault: case 830: yyDollar = yyS[yypt-6 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:5730 +//line mysql_sql.y:5733 { var ifExists = yyDollar[4].boolValUnion() var names = yyDollar[5].tableNamesUnion() @@ -20777,7 +20838,7 @@ yydefault: case 831: yyDollar = yyS[yypt-5 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:5740 +//line mysql_sql.y:5743 { var ifExists = yyDollar[3].boolValUnion() var names = yyDollar[4].tableNamesUnion() @@ -20787,7 +20848,7 @@ yydefault: case 832: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:5748 +//line mysql_sql.y:5751 { var name = tree.Identifier(yyDollar[4].cstrUnion().Compare()) var ifExists = yyDollar[3].boolValUnion() @@ -20797,7 +20858,7 @@ yydefault: case 833: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:5754 +//line mysql_sql.y:5757 { var name = tree.Identifier(yyDollar[4].cstrUnion().Compare()) var ifExists = yyDollar[3].boolValUnion() @@ -20807,7 +20868,7 @@ yydefault: case 834: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:5762 +//line mysql_sql.y:5765 { yyLOCAL = tree.NewDeallocate(tree.Identifier(yyDollar[3].str), true) } @@ -20815,7 +20876,7 @@ yydefault: case 835: yyDollar = yyS[yypt-7 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:5768 +//line mysql_sql.y:5771 { var ifExists = yyDollar[3].boolValUnion() var name = yyDollar[4].functionNameUnion() @@ -20828,7 +20889,7 @@ yydefault: case 836: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:5779 +//line mysql_sql.y:5782 { var name = yyDollar[3].procNameUnion() var ifExists = false @@ -20838,7 +20899,7 @@ yydefault: case 837: yyDollar = yyS[yypt-5 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:5785 +//line mysql_sql.y:5788 { var name = yyDollar[5].procNameUnion() var ifExists = true @@ -20848,7 +20909,7 @@ yydefault: case 838: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:5793 +//line mysql_sql.y:5796 { if intoErr := tree.ValidateSelectIntoEnclosingStatement(yyDollar[1].statementUnion()); intoErr != "" { yylex.Error(intoErr) @@ -20860,7 +20921,7 @@ yydefault: case 839: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:5801 +//line mysql_sql.y:5804 { if intoErr := tree.ValidateSelectIntoEnclosingStatement(yyDollar[1].statementUnion()); intoErr != "" { yylex.Error(intoErr) @@ -20872,7 +20933,7 @@ yydefault: case 840: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:5809 +//line mysql_sql.y:5812 { yyDollar[2].statementUnion().(*tree.Delete).With = yyDollar[1].withClauseUnion() if intoErr := tree.ValidateSelectIntoEnclosingStatement(yyDollar[2].statementUnion()); intoErr != "" { @@ -20885,7 +20946,7 @@ yydefault: case 841: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:5818 +//line mysql_sql.y:5821 { yyDollar[2].statementUnion().(*tree.Delete).With = yyDollar[1].withClauseUnion() if intoErr := tree.ValidateSelectIntoEnclosingStatement(yyDollar[2].statementUnion()); intoErr != "" { @@ -20898,7 +20959,7 @@ yydefault: case 842: yyDollar = yyS[yypt-12 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:5829 +//line mysql_sql.y:5832 { // Single-Table Syntax t := &tree.AliasedTableExpr{ @@ -20923,7 +20984,7 @@ yydefault: case 843: yyDollar = yyS[yypt-9 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:5850 +//line mysql_sql.y:5853 { // Multiple-Table Syntax yyLOCAL = &tree.Delete{ @@ -20940,7 +21001,7 @@ yydefault: case 844: yyDollar = yyS[yypt-10 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:5865 +//line mysql_sql.y:5868 { // Multiple-Table Syntax yyLOCAL = &tree.Delete{ @@ -20957,7 +21018,7 @@ yydefault: case 845: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.TableExprs -//line mysql_sql.y:5880 +//line mysql_sql.y:5883 { yyLOCAL = tree.TableExprs{yyDollar[1].tableNameUnion()} } @@ -20965,7 +21026,7 @@ yydefault: case 846: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL tree.TableExprs -//line mysql_sql.y:5884 +//line mysql_sql.y:5887 { yyLOCAL = append(yyDollar[1].tableExprsUnion(), yyDollar[3].tableNameUnion()) } @@ -20973,7 +21034,7 @@ yydefault: case 847: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL *tree.TableName -//line mysql_sql.y:5890 +//line mysql_sql.y:5893 { tblName := yylex.(*Lexer).GetDbOrTblName(yyDollar[1].cstrUnion().Origin()) prefix := tree.ObjectNamePrefix{ExplicitSchema: false} @@ -20983,7 +21044,7 @@ yydefault: case 848: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL *tree.TableName -//line mysql_sql.y:5896 +//line mysql_sql.y:5899 { dbName := yylex.(*Lexer).GetDbOrTblName(yyDollar[1].cstrUnion().Origin()) tblName := yylex.(*Lexer).GetDbOrTblName(yyDollar[3].cstrUnion().Origin()) @@ -20993,47 +21054,47 @@ yydefault: yyVAL.union = yyLOCAL case 849: yyDollar = yyS[yypt-0 : yypt+1] -//line mysql_sql.y:5905 +//line mysql_sql.y:5908 { } case 850: yyDollar = yyS[yypt-2 : yypt+1] -//line mysql_sql.y:5907 +//line mysql_sql.y:5910 { } case 851: yyDollar = yyS[yypt-0 : yypt+1] -//line mysql_sql.y:5910 +//line mysql_sql.y:5913 { yyVAL.str = "" } case 856: yyDollar = yyS[yypt-0 : yypt+1] -//line mysql_sql.y:5919 +//line mysql_sql.y:5922 { yyVAL.str = "" } case 858: yyDollar = yyS[yypt-0 : yypt+1] -//line mysql_sql.y:5923 +//line mysql_sql.y:5926 { yyVAL.str = "" } case 859: yyDollar = yyS[yypt-1 : yypt+1] -//line mysql_sql.y:5927 +//line mysql_sql.y:5930 { yyVAL.str = "ignore" } case 860: yyDollar = yyS[yypt-0 : yypt+1] -//line mysql_sql.y:5935 +//line mysql_sql.y:5938 { } case 863: yyDollar = yyS[yypt-6 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:5941 +//line mysql_sql.y:5944 { rep := yyDollar[5].replaceUnion() if intoErr := tree.ValidateSelectIntoNotAllowed(rep.Rows); intoErr != "" { @@ -21052,7 +21113,7 @@ yydefault: case 864: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL *tree.Replace -//line mysql_sql.y:5958 +//line mysql_sql.y:5961 { vc := tree.NewValuesClause(yyDollar[2].rowsExprsUnion()) yyLOCAL = &tree.Replace{ @@ -21063,7 +21124,7 @@ yydefault: case 865: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL *tree.Replace -//line mysql_sql.y:5965 +//line mysql_sql.y:5968 { yyLOCAL = &tree.Replace{ Rows: yyDollar[1].selectUnion(), @@ -21073,7 +21134,7 @@ yydefault: case 866: yyDollar = yyS[yypt-5 : yypt+1] var yyLOCAL *tree.Replace -//line mysql_sql.y:5971 +//line mysql_sql.y:5974 { vc := tree.NewValuesClause(yyDollar[5].rowsExprsUnion()) yyLOCAL = &tree.Replace{ @@ -21086,7 +21147,7 @@ yydefault: case 867: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL *tree.Replace -//line mysql_sql.y:5980 +//line mysql_sql.y:5983 { vc := tree.NewValuesClause(yyDollar[4].rowsExprsUnion()) yyLOCAL = &tree.Replace{ @@ -21097,7 +21158,7 @@ yydefault: case 868: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL *tree.Replace -//line mysql_sql.y:5987 +//line mysql_sql.y:5990 { yyLOCAL = &tree.Replace{ Columns: yyDollar[2].insertColumnsUnion().Identifiers, @@ -21109,7 +21170,7 @@ yydefault: case 869: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL *tree.Replace -//line mysql_sql.y:5995 +//line mysql_sql.y:5998 { if yyDollar[2].assignmentsUnion() == nil { yylex.Error("the set list of replace can not be empty") @@ -21135,7 +21196,7 @@ yydefault: case 871: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:6020 +//line mysql_sql.y:6023 { yyDollar[2].statementUnion().(*tree.Insert).With = yyDollar[1].withClauseUnion() if intoErr := tree.ValidateSelectIntoEnclosingStatement(yyDollar[2].statementUnion()); intoErr != "" { @@ -21148,7 +21209,7 @@ yydefault: case 873: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:6030 +//line mysql_sql.y:6033 { yyDollar[2].statementUnion().(*tree.MultiInsert).With = yyDollar[1].withClauseUnion() yyLOCAL = yyDollar[2].statementUnion() @@ -21157,7 +21218,7 @@ yydefault: case 874: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:6040 +//line mysql_sql.y:6043 { if intoErr := tree.ValidateSelectIntoNotAllowed(yyDollar[4].selectUnion()); intoErr != "" { yylex.Error(intoErr) @@ -21169,7 +21230,7 @@ yydefault: case 875: yyDollar = yyS[yypt-5 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:6048 +//line mysql_sql.y:6051 { if intoErr := tree.ValidateSelectIntoNotAllowed(yyDollar[5].selectUnion()); intoErr != "" { yylex.Error(intoErr) @@ -21181,7 +21242,7 @@ yydefault: case 876: yyDollar = yyS[yypt-5 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:6056 +//line mysql_sql.y:6059 { if intoErr := tree.ValidateSelectIntoNotAllowed(yyDollar[5].selectUnion()); intoErr != "" { yylex.Error(intoErr) @@ -21193,7 +21254,7 @@ yydefault: case 877: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL []*tree.MultiInsertWhen -//line mysql_sql.y:6066 +//line mysql_sql.y:6069 { yyLOCAL = []*tree.MultiInsertWhen{yyDollar[1].multiInsertWhenUnion()} } @@ -21201,7 +21262,7 @@ yydefault: case 878: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL []*tree.MultiInsertWhen -//line mysql_sql.y:6070 +//line mysql_sql.y:6073 { yyLOCAL = append(yyDollar[1].multiInsertWhensUnion(), yyDollar[2].multiInsertWhenUnion()) } @@ -21209,7 +21270,7 @@ yydefault: case 879: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL *tree.MultiInsertWhen -//line mysql_sql.y:6076 +//line mysql_sql.y:6079 { yyLOCAL = &tree.MultiInsertWhen{Cond: yyDollar[2].exprUnion(), Targets: yyDollar[4].multiInsertTargetsUnion()} } @@ -21217,7 +21278,7 @@ yydefault: case 880: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL []*tree.MultiInsertTarget -//line mysql_sql.y:6081 +//line mysql_sql.y:6084 { yyLOCAL = nil } @@ -21225,7 +21286,7 @@ yydefault: case 881: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL []*tree.MultiInsertTarget -//line mysql_sql.y:6085 +//line mysql_sql.y:6088 { yyLOCAL = yyDollar[2].multiInsertTargetsUnion() } @@ -21233,7 +21294,7 @@ yydefault: case 882: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL []*tree.MultiInsertTarget -//line mysql_sql.y:6091 +//line mysql_sql.y:6094 { yyLOCAL = []*tree.MultiInsertTarget{yyDollar[1].multiInsertTargetUnion()} } @@ -21241,7 +21302,7 @@ yydefault: case 883: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL []*tree.MultiInsertTarget -//line mysql_sql.y:6095 +//line mysql_sql.y:6098 { yyLOCAL = append(yyDollar[1].multiInsertTargetsUnion(), yyDollar[2].multiInsertTargetUnion()) } @@ -21249,7 +21310,7 @@ yydefault: case 884: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL *tree.MultiInsertTarget -//line mysql_sql.y:6101 +//line mysql_sql.y:6104 { yyLOCAL = &tree.MultiInsertTarget{Table: yyDollar[2].tableNameUnion()} } @@ -21257,7 +21318,7 @@ yydefault: case 885: yyDollar = yyS[yypt-6 : yypt+1] var yyLOCAL *tree.MultiInsertTarget -//line mysql_sql.y:6105 +//line mysql_sql.y:6108 { yyLOCAL = &tree.MultiInsertTarget{Table: yyDollar[2].tableNameUnion(), Values: yyDollar[5].exprsUnion()} } @@ -21265,7 +21326,7 @@ yydefault: case 886: yyDollar = yyS[yypt-5 : yypt+1] var yyLOCAL *tree.MultiInsertTarget -//line mysql_sql.y:6109 +//line mysql_sql.y:6112 { yyLOCAL = &tree.MultiInsertTarget{Table: yyDollar[2].tableNameUnion(), Columns: yyDollar[4].insertColumnsUnion().Identifiers, ColumnNames: yyDollar[4].insertColumnsUnion().Names} } @@ -21273,7 +21334,7 @@ yydefault: case 887: yyDollar = yyS[yypt-9 : yypt+1] var yyLOCAL *tree.MultiInsertTarget -//line mysql_sql.y:6113 +//line mysql_sql.y:6116 { yyLOCAL = &tree.MultiInsertTarget{Table: yyDollar[2].tableNameUnion(), Columns: yyDollar[4].insertColumnsUnion().Identifiers, ColumnNames: yyDollar[4].insertColumnsUnion().Names, Values: yyDollar[8].exprsUnion()} } @@ -21281,7 +21342,7 @@ yydefault: case 888: yyDollar = yyS[yypt-6 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:6119 +//line mysql_sql.y:6122 { ins := yyDollar[4].insertUnion() if intoErr := tree.ValidateSelectIntoNotAllowed(ins.Rows); intoErr != "" { @@ -21308,7 +21369,7 @@ yydefault: case 889: yyDollar = yyS[yypt-6 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:6142 +//line mysql_sql.y:6145 { ins := yyDollar[5].insertUnion() if intoErr := tree.ValidateSelectIntoNotAllowed(ins.Rows); intoErr != "" { @@ -21331,7 +21392,7 @@ yydefault: case 890: yyDollar = yyS[yypt-6 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:6161 +//line mysql_sql.y:6164 { ins := yyDollar[5].insertUnion() if intoErr := tree.ValidateSelectIntoNotAllowed(ins.Rows); intoErr != "" { @@ -21355,7 +21416,7 @@ yydefault: case 891: yyDollar = yyS[yypt-7 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:6181 +//line mysql_sql.y:6184 { ins := yyDollar[5].insertUnion() if intoErr := tree.ValidateSelectIntoNotAllowed(ins.Rows); intoErr != "" { @@ -21382,7 +21443,7 @@ yydefault: case 892: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL tree.SelectExprs -//line mysql_sql.y:6205 +//line mysql_sql.y:6208 { yyLOCAL = nil } @@ -21390,7 +21451,7 @@ yydefault: case 893: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL tree.SelectExprs -//line mysql_sql.y:6209 +//line mysql_sql.y:6212 { yyLOCAL = yyDollar[2].selectExprsUnion() } @@ -21398,7 +21459,7 @@ yydefault: case 894: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:6215 +//line mysql_sql.y:6218 { yyLOCAL = yyDollar[1].mergeUnion() } @@ -21406,7 +21467,7 @@ yydefault: case 895: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:6219 +//line mysql_sql.y:6222 { yyDollar[2].mergeUnion().With = yyDollar[1].withClauseUnion() yyLOCAL = yyDollar[2].mergeUnion() @@ -21415,7 +21476,7 @@ yydefault: case 896: yyDollar = yyS[yypt-8 : yypt+1] var yyLOCAL *tree.Merge -//line mysql_sql.y:6226 +//line mysql_sql.y:6229 { yyLOCAL = &tree.Merge{ Target: yyDollar[3].tableExprUnion(), @@ -21428,7 +21489,7 @@ yydefault: case 897: yyDollar = yyS[yypt-10 : yypt+1] var yyLOCAL *tree.Merge -//line mysql_sql.y:6235 +//line mysql_sql.y:6238 { yyLOCAL = &tree.Merge{ Target: yyDollar[3].tableExprUnion(), @@ -21442,7 +21503,7 @@ yydefault: case 898: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.MergeClauses -//line mysql_sql.y:6247 +//line mysql_sql.y:6250 { yyLOCAL = tree.MergeClauses{yyDollar[1].mergeClauseUnion()} } @@ -21450,7 +21511,7 @@ yydefault: case 899: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL tree.MergeClauses -//line mysql_sql.y:6251 +//line mysql_sql.y:6254 { yyLOCAL = append(yyDollar[1].mergeClausesUnion(), yyDollar[2].mergeClauseUnion()) } @@ -21458,7 +21519,7 @@ yydefault: case 900: yyDollar = yyS[yypt-7 : yypt+1] var yyLOCAL *tree.MergeClause -//line mysql_sql.y:6257 +//line mysql_sql.y:6260 { yyLOCAL = &tree.MergeClause{ Matched: true, @@ -21471,7 +21532,7 @@ yydefault: case 901: yyDollar = yyS[yypt-5 : yypt+1] var yyLOCAL *tree.MergeClause -//line mysql_sql.y:6266 +//line mysql_sql.y:6269 { yyLOCAL = &tree.MergeClause{ Matched: true, @@ -21483,7 +21544,7 @@ yydefault: case 902: yyDollar = yyS[yypt-13 : yypt+1] var yyLOCAL *tree.MergeClause -//line mysql_sql.y:6274 +//line mysql_sql.y:6277 { yyLOCAL = &tree.MergeClause{ Matched: false, @@ -21497,7 +21558,7 @@ yydefault: case 903: yyDollar = yyS[yypt-13 : yypt+1] var yyLOCAL *tree.MergeClause -//line mysql_sql.y:6284 +//line mysql_sql.y:6287 { yyLOCAL = &tree.MergeClause{ Matched: false, @@ -21511,7 +21572,7 @@ yydefault: case 904: yyDollar = yyS[yypt-10 : yypt+1] var yyLOCAL *tree.MergeClause -//line mysql_sql.y:6294 +//line mysql_sql.y:6297 { yyLOCAL = &tree.MergeClause{ Matched: false, @@ -21524,7 +21585,7 @@ yydefault: case 905: yyDollar = yyS[yypt-10 : yypt+1] var yyLOCAL *tree.MergeClause -//line mysql_sql.y:6303 +//line mysql_sql.y:6306 { yyLOCAL = &tree.MergeClause{ Matched: false, @@ -21536,7 +21597,7 @@ yydefault: yyVAL.union = yyLOCAL case 906: yyDollar = yyS[yypt-1 : yypt+1] -//line mysql_sql.y:6314 +//line mysql_sql.y:6317 { if !strings.EqualFold(yyDollar[1].cstrUnion().Origin(), "matched") { yylex.Error("expected MATCHED") @@ -21547,7 +21608,7 @@ yydefault: case 907: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL tree.Expr -//line mysql_sql.y:6323 +//line mysql_sql.y:6326 { yyLOCAL = nil } @@ -21555,7 +21616,7 @@ yydefault: case 908: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL tree.Expr -//line mysql_sql.y:6327 +//line mysql_sql.y:6330 { yyLOCAL = yyDollar[2].exprUnion() } @@ -21563,7 +21624,7 @@ yydefault: case 909: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.IdentifierList -//line mysql_sql.y:6333 +//line mysql_sql.y:6336 { yyLOCAL = tree.IdentifierList{tree.Identifier(yyDollar[1].str)} } @@ -21571,7 +21632,7 @@ yydefault: case 910: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL tree.IdentifierList -//line mysql_sql.y:6337 +//line mysql_sql.y:6340 { yyLOCAL = append(yyDollar[1].identifierListUnion(), tree.Identifier(yyDollar[3].str)) } @@ -21579,7 +21640,7 @@ yydefault: case 911: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL *tree.Insert -//line mysql_sql.y:6343 +//line mysql_sql.y:6346 { vc := tree.NewValuesClause(yyDollar[2].rowsExprsUnion()) yyLOCAL = &tree.Insert{ @@ -21590,7 +21651,7 @@ yydefault: case 912: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL *tree.Insert -//line mysql_sql.y:6350 +//line mysql_sql.y:6353 { yyLOCAL = &tree.Insert{ Rows: yyDollar[1].selectUnion(), @@ -21600,7 +21661,7 @@ yydefault: case 913: yyDollar = yyS[yypt-5 : yypt+1] var yyLOCAL *tree.Insert -//line mysql_sql.y:6356 +//line mysql_sql.y:6359 { vc := tree.NewValuesClause(yyDollar[5].rowsExprsUnion()) yyLOCAL = &tree.Insert{ @@ -21613,7 +21674,7 @@ yydefault: case 914: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL *tree.Insert -//line mysql_sql.y:6365 +//line mysql_sql.y:6368 { vc := tree.NewValuesClause(yyDollar[4].rowsExprsUnion()) yyLOCAL = &tree.Insert{ @@ -21624,7 +21685,7 @@ yydefault: case 915: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL *tree.Insert -//line mysql_sql.y:6372 +//line mysql_sql.y:6375 { yyLOCAL = &tree.Insert{ Columns: yyDollar[2].insertColumnsUnion().Identifiers, @@ -21636,7 +21697,7 @@ yydefault: case 916: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL *tree.Insert -//line mysql_sql.y:6380 +//line mysql_sql.y:6383 { if yyDollar[2].assignmentsUnion() == nil { yylex.Error("the set list of insert can not be empty") @@ -21661,7 +21722,7 @@ yydefault: case 917: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL tree.UpdateExprs -//line mysql_sql.y:6402 +//line mysql_sql.y:6405 { yyLOCAL = []*tree.UpdateExpr{} } @@ -21669,7 +21730,7 @@ yydefault: case 918: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.UpdateExprs -//line mysql_sql.y:6406 +//line mysql_sql.y:6409 { yyLOCAL = yyDollar[1].updateExprsUnion() } @@ -21677,7 +21738,7 @@ yydefault: case 919: yyDollar = yyS[yypt-5 : yypt+1] var yyLOCAL tree.UpdateExprs -//line mysql_sql.y:6412 +//line mysql_sql.y:6415 { yyLOCAL = yyDollar[5].updateExprsUnion() } @@ -21685,7 +21746,7 @@ yydefault: case 920: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL tree.UpdateExprs -//line mysql_sql.y:6416 +//line mysql_sql.y:6419 { yyLOCAL = []*tree.UpdateExpr{nil} } @@ -21693,7 +21754,7 @@ yydefault: case 921: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL []*tree.Assignment -//line mysql_sql.y:6422 +//line mysql_sql.y:6425 { yyLOCAL = nil } @@ -21701,7 +21762,7 @@ yydefault: case 922: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL []*tree.Assignment -//line mysql_sql.y:6426 +//line mysql_sql.y:6429 { yyLOCAL = []*tree.Assignment{yyDollar[1].assignmentUnion()} } @@ -21709,7 +21770,7 @@ yydefault: case 923: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL []*tree.Assignment -//line mysql_sql.y:6430 +//line mysql_sql.y:6433 { yyLOCAL = append(yyDollar[1].assignmentsUnion(), yyDollar[3].assignmentUnion()) } @@ -21717,7 +21778,7 @@ yydefault: case 924: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL *tree.Assignment -//line mysql_sql.y:6436 +//line mysql_sql.y:6439 { yyLOCAL = &tree.Assignment{ Column: tree.Identifier(yyDollar[1].unresolvedNameUnion().ColName()), @@ -21729,7 +21790,7 @@ yydefault: case 925: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.InsertColumns -//line mysql_sql.y:6446 +//line mysql_sql.y:6449 { yyLOCAL = tree.InsertColumns{ Identifiers: tree.IdentifierList{tree.Identifier(yyDollar[1].unresolvedNameUnion().ColName())}, @@ -21740,7 +21801,7 @@ yydefault: case 926: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL tree.InsertColumns -//line mysql_sql.y:6453 +//line mysql_sql.y:6456 { yyLOCAL = yyDollar[1].insertColumnsUnion() yyLOCAL.Identifiers = append(yyDollar[1].insertColumnsUnion().Identifiers, tree.Identifier(yyDollar[3].unresolvedNameUnion().ColName())) @@ -21750,7 +21811,7 @@ yydefault: case 927: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL *tree.UnresolvedName -//line mysql_sql.y:6461 +//line mysql_sql.y:6464 { yyLOCAL = tree.NewUnresolvedName(yylex.(*Lexer).GetDbOrTblNameCStr(yyDollar[1].cstrUnion().Origin())) } @@ -21758,7 +21819,7 @@ yydefault: case 928: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL *tree.UnresolvedName -//line mysql_sql.y:6465 +//line mysql_sql.y:6468 { tblName := yylex.(*Lexer).GetDbOrTblNameCStr(yyDollar[1].cstrUnion().Origin()) yyLOCAL = tree.NewUnresolvedName(tblName, yyDollar[3].cstrUnion()) @@ -21767,7 +21828,7 @@ yydefault: case 929: yyDollar = yyS[yypt-5 : yypt+1] var yyLOCAL *tree.UnresolvedName -//line mysql_sql.y:6470 +//line mysql_sql.y:6473 { dbName := yylex.(*Lexer).GetDbOrTblNameCStr(yyDollar[1].cstrUnion().Origin()) tblName := yylex.(*Lexer).GetDbOrTblNameCStr(yyDollar[3].cstrUnion().Origin()) @@ -21777,7 +21838,7 @@ yydefault: case 930: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.IdentifierList -//line mysql_sql.y:6478 +//line mysql_sql.y:6481 { yyLOCAL = tree.IdentifierList{tree.Identifier(yyDollar[1].cstrUnion().Compare())} } @@ -21785,7 +21846,7 @@ yydefault: case 931: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL tree.IdentifierList -//line mysql_sql.y:6482 +//line mysql_sql.y:6485 { yyLOCAL = tree.IdentifierList{tree.Identifier(yyDollar[3].cstrUnion().Compare())} } @@ -21793,7 +21854,7 @@ yydefault: case 932: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL tree.IdentifierList -//line mysql_sql.y:6486 +//line mysql_sql.y:6489 { yyLOCAL = append(yyDollar[1].identifierListUnion(), tree.Identifier(yyDollar[3].cstrUnion().Compare())) } @@ -21801,7 +21862,7 @@ yydefault: case 933: yyDollar = yyS[yypt-5 : yypt+1] var yyLOCAL tree.IdentifierList -//line mysql_sql.y:6490 +//line mysql_sql.y:6493 { yyLOCAL = append(yyDollar[1].identifierListUnion(), tree.Identifier(yyDollar[5].cstrUnion().Compare())) } @@ -21809,7 +21870,7 @@ yydefault: case 934: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL []tree.Exprs -//line mysql_sql.y:6496 +//line mysql_sql.y:6499 { yyLOCAL = []tree.Exprs{yyDollar[1].exprsUnion()} } @@ -21817,7 +21878,7 @@ yydefault: case 935: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL []tree.Exprs -//line mysql_sql.y:6500 +//line mysql_sql.y:6503 { yyLOCAL = append(yyDollar[1].rowsExprsUnion(), yyDollar[3].exprsUnion()) } @@ -21825,20 +21886,20 @@ yydefault: case 936: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL tree.Exprs -//line mysql_sql.y:6506 +//line mysql_sql.y:6509 { yyLOCAL = yyDollar[3].exprsUnion() } yyVAL.union = yyLOCAL case 937: yyDollar = yyS[yypt-0 : yypt+1] -//line mysql_sql.y:6511 +//line mysql_sql.y:6514 { } case 939: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL tree.Exprs -//line mysql_sql.y:6515 +//line mysql_sql.y:6518 { yyLOCAL = nil } @@ -21846,7 +21907,7 @@ yydefault: case 941: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.Exprs -//line mysql_sql.y:6522 +//line mysql_sql.y:6525 { yyLOCAL = tree.Exprs{yyDollar[1].exprUnion()} } @@ -21854,7 +21915,7 @@ yydefault: case 942: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL tree.Exprs -//line mysql_sql.y:6526 +//line mysql_sql.y:6529 { yyLOCAL = append(yyDollar[1].exprsUnion(), yyDollar[3].exprUnion()) } @@ -21862,7 +21923,7 @@ yydefault: case 944: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.Expr -//line mysql_sql.y:6533 +//line mysql_sql.y:6536 { yyLOCAL = &tree.DefaultVal{} } @@ -21870,7 +21931,7 @@ yydefault: case 945: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL tree.IdentifierList -//line mysql_sql.y:6538 +//line mysql_sql.y:6541 { yyLOCAL = nil } @@ -21878,7 +21939,7 @@ yydefault: case 946: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL tree.IdentifierList -//line mysql_sql.y:6542 +//line mysql_sql.y:6545 { yyLOCAL = yyDollar[3].identifierListUnion() } @@ -21886,7 +21947,7 @@ yydefault: case 947: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL *tree.InsertPartitionClause -//line mysql_sql.y:6547 +//line mysql_sql.y:6550 { yyLOCAL = nil } @@ -21894,7 +21955,7 @@ yydefault: case 948: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL *tree.InsertPartitionClause -//line mysql_sql.y:6551 +//line mysql_sql.y:6554 { yyLOCAL = &tree.InsertPartitionClause{Names: yyDollar[3].identifierListUnion()} } @@ -21902,7 +21963,7 @@ yydefault: case 949: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL *tree.InsertPartitionClause -//line mysql_sql.y:6555 +//line mysql_sql.y:6558 { yyLOCAL = &tree.InsertPartitionClause{Values: yyDollar[3].partitionValuesUnion()} } @@ -21910,7 +21971,7 @@ yydefault: case 950: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL tree.PartitionValues -//line mysql_sql.y:6561 +//line mysql_sql.y:6564 { yyLOCAL = tree.PartitionValues{{Name: tree.Identifier(yyDollar[1].cstrUnion().Compare()), Expr: yyDollar[3].exprUnion()}} } @@ -21918,7 +21979,7 @@ yydefault: case 951: yyDollar = yyS[yypt-5 : yypt+1] var yyLOCAL tree.PartitionValues -//line mysql_sql.y:6565 +//line mysql_sql.y:6568 { yyLOCAL = append(yyDollar[1].partitionValuesUnion(), tree.PartitionValue{Name: tree.Identifier(yyDollar[3].cstrUnion().Compare()), Expr: yyDollar[5].exprUnion()}) } @@ -21926,7 +21987,7 @@ yydefault: case 952: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.IdentifierList -//line mysql_sql.y:6571 +//line mysql_sql.y:6574 { yyLOCAL = tree.IdentifierList{tree.Identifier(yyDollar[1].cstrUnion().Compare())} } @@ -21934,7 +21995,7 @@ yydefault: case 953: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL tree.IdentifierList -//line mysql_sql.y:6575 +//line mysql_sql.y:6578 { yyLOCAL = append(yyDollar[1].identifierListUnion(), tree.Identifier(yyDollar[3].cstrUnion().Compare())) } @@ -21942,7 +22003,7 @@ yydefault: case 954: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL tree.TableExpr -//line mysql_sql.y:6581 +//line mysql_sql.y:6584 { yyLOCAL = yyDollar[2].tableNameUnion() } @@ -21950,7 +22011,7 @@ yydefault: case 955: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.TableExpr -//line mysql_sql.y:6585 +//line mysql_sql.y:6588 { yyLOCAL = yyDollar[1].tableNameUnion() } @@ -21958,7 +22019,7 @@ yydefault: case 956: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL *tree.SelectInto -//line mysql_sql.y:6591 +//line mysql_sql.y:6594 { yyLOCAL = &tree.SelectInto{} } @@ -21966,7 +22027,7 @@ yydefault: case 957: yyDollar = yyS[yypt-10 : yypt+1] var yyLOCAL *tree.SelectInto -//line mysql_sql.y:6595 +//line mysql_sql.y:6598 { yyLOCAL = &tree.SelectInto{Export: &tree.ExportParam{ Outfile: true, @@ -21984,20 +22045,20 @@ yydefault: case 958: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL *tree.SelectInto -//line mysql_sql.y:6609 +//line mysql_sql.y:6612 { yyLOCAL = &tree.SelectInto{UserVars: yyDollar[2].varExprsUnion()} } yyVAL.union = yyLOCAL case 959: yyDollar = yyS[yypt-0 : yypt+1] -//line mysql_sql.y:6614 +//line mysql_sql.y:6617 { yyVAL.str = "" } case 960: yyDollar = yyS[yypt-2 : yypt+1] -//line mysql_sql.y:6618 +//line mysql_sql.y:6621 { str := strings.ToLower(yyDollar[2].str) if str != "csv" && str != "jsonline" && str != "parquet" { @@ -22009,7 +22070,7 @@ yydefault: case 961: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL uint64 -//line mysql_sql.y:6628 +//line mysql_sql.y:6631 { yyLOCAL = uint64(0) } @@ -22017,7 +22078,7 @@ yydefault: case 962: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL uint64 -//line mysql_sql.y:6632 +//line mysql_sql.y:6635 { size, err := util.ParseDataSize(yyDollar[2].str) if err != nil { @@ -22030,7 +22091,7 @@ yydefault: case 963: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL *tree.Fields -//line mysql_sql.y:6642 +//line mysql_sql.y:6645 { yyLOCAL = &tree.Fields{ Terminated: &tree.Terminated{ @@ -22045,7 +22106,7 @@ yydefault: case 964: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL *tree.Fields -//line mysql_sql.y:6653 +//line mysql_sql.y:6656 { yyLOCAL = &tree.Fields{ Terminated: &tree.Terminated{ @@ -22060,7 +22121,7 @@ yydefault: case 965: yyDollar = yyS[yypt-7 : yypt+1] var yyLOCAL *tree.Fields -//line mysql_sql.y:6664 +//line mysql_sql.y:6667 { str := yyDollar[7].str if str != "\\" && len(str) > 1 { @@ -22086,7 +22147,7 @@ yydefault: case 966: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL *tree.Fields -//line mysql_sql.y:6686 +//line mysql_sql.y:6689 { str := yyDollar[4].str if str != "\\" && len(str) > 1 { @@ -22112,7 +22173,7 @@ yydefault: case 967: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL *tree.Lines -//line mysql_sql.y:6709 +//line mysql_sql.y:6712 { yyLOCAL = &tree.Lines{ TerminatedBy: &tree.Terminated{ @@ -22124,7 +22185,7 @@ yydefault: case 968: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL *tree.Lines -//line mysql_sql.y:6717 +//line mysql_sql.y:6720 { yyLOCAL = &tree.Lines{ TerminatedBy: &tree.Terminated{ @@ -22136,7 +22197,7 @@ yydefault: case 969: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL bool -//line mysql_sql.y:6726 +//line mysql_sql.y:6729 { yyLOCAL = true } @@ -22144,7 +22205,7 @@ yydefault: case 970: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL bool -//line mysql_sql.y:6730 +//line mysql_sql.y:6733 { str := strings.ToLower(yyDollar[2].str) if str == "true" { @@ -22160,7 +22221,7 @@ yydefault: case 971: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL int64 -//line mysql_sql.y:6743 +//line mysql_sql.y:6746 { yyLOCAL = 0 } @@ -22168,7 +22229,7 @@ yydefault: case 972: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL int64 -//line mysql_sql.y:6747 +//line mysql_sql.y:6750 { yyLOCAL = yyDollar[2].item.(int64) } @@ -22176,7 +22237,7 @@ yydefault: case 973: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL []string -//line mysql_sql.y:6752 +//line mysql_sql.y:6755 { yyLOCAL = []string{} } @@ -22184,7 +22245,7 @@ yydefault: case 974: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL []string -//line mysql_sql.y:6756 +//line mysql_sql.y:6759 { yyLOCAL = yyDollar[3].strsUnion() } @@ -22192,7 +22253,7 @@ yydefault: case 975: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL []string -//line mysql_sql.y:6762 +//line mysql_sql.y:6765 { yyLOCAL = make([]string, 0, 4) yyLOCAL = append(yyLOCAL, yyDollar[1].cstrUnion().Compare()) @@ -22201,7 +22262,7 @@ yydefault: case 976: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL []string -//line mysql_sql.y:6767 +//line mysql_sql.y:6770 { yyLOCAL = append(yyDollar[1].strsUnion(), yyDollar[3].cstrUnion().Compare()) } @@ -22209,7 +22270,7 @@ yydefault: case 977: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL *tree.Select -//line mysql_sql.y:6776 +//line mysql_sql.y:6779 { intoVars, deprecatedInto, intoErr := tree.SelectIntoVariablesForTopLevel(yyDollar[1].selectStatementUnion()) if intoErr != "" { @@ -22226,7 +22287,7 @@ yydefault: case 979: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL *tree.Select -//line mysql_sql.y:6792 +//line mysql_sql.y:6795 { yyLOCAL = yyDollar[1].selectUnion() } @@ -22234,7 +22295,7 @@ yydefault: case 980: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL *tree.Select -//line mysql_sql.y:6796 +//line mysql_sql.y:6799 { intoVars, deprecatedInto, intoErr := tree.SelectIntoVariablesForTopLevel(yyDollar[1].selectStatementUnion()) if intoErr != "" { @@ -22251,7 +22312,7 @@ yydefault: case 981: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL *tree.Select -//line mysql_sql.y:6816 +//line mysql_sql.y:6819 { yyLOCAL = yyDollar[1].selectUnion() } @@ -22259,7 +22320,7 @@ yydefault: case 982: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL *tree.Select -//line mysql_sql.y:6820 +//line mysql_sql.y:6823 { yyLOCAL = tree.NewSelect(&tree.ValuesClause{Rows: yyDollar[2].rowsExprsUnion(), RowWord: true}, yyDollar[3].orderByUnion(), yyDollar[4].limitUnion()) } @@ -22267,7 +22328,7 @@ yydefault: case 983: yyDollar = yyS[yypt-7 : yypt+1] var yyLOCAL *tree.Select -//line mysql_sql.y:6826 +//line mysql_sql.y:6829 { intoVars, deprecatedInto, intoErr := tree.SelectIntoVariablesForTopLevel(yyDollar[1].selectStatementUnion()) if intoErr != "" { @@ -22288,7 +22349,7 @@ yydefault: case 984: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL *tree.Select -//line mysql_sql.y:6843 +//line mysql_sql.y:6846 { intoVars, deprecatedInto, intoErr := tree.SelectIntoVariablesForTopLevel(yyDollar[1].selectStatementUnion()) if intoErr != "" { @@ -22309,7 +22370,7 @@ yydefault: case 985: yyDollar = yyS[yypt-6 : yypt+1] var yyLOCAL *tree.Select -//line mysql_sql.y:6860 +//line mysql_sql.y:6863 { intoVars, deprecatedInto, intoErr := tree.SelectIntoVariablesForTopLevel(yyDollar[1].selectStatementUnion()) if intoErr != "" { @@ -22330,7 +22391,7 @@ yydefault: case 986: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL *tree.Select -//line mysql_sql.y:6877 +//line mysql_sql.y:6880 { intoVars, deprecatedInto, intoErr := tree.SelectIntoVariablesForTopLevel(yyDollar[1].selectStatementUnion()) if intoErr != "" { @@ -22351,7 +22412,7 @@ yydefault: case 987: yyDollar = yyS[yypt-5 : yypt+1] var yyLOCAL *tree.Select -//line mysql_sql.y:6894 +//line mysql_sql.y:6897 { intoVars, deprecatedInto, intoErr := tree.SelectIntoVariablesForTopLevel(yyDollar[1].selectStatementUnion()) if intoErr != "" { @@ -22372,7 +22433,7 @@ yydefault: case 988: yyDollar = yyS[yypt-6 : yypt+1] var yyLOCAL *tree.Select -//line mysql_sql.y:6911 +//line mysql_sql.y:6914 { intoVars, deprecatedInto, intoErr := tree.SelectIntoVariablesForTopLevel(yyDollar[1].selectStatementUnion()) if intoErr != "" { @@ -22393,7 +22454,7 @@ yydefault: case 989: yyDollar = yyS[yypt-8 : yypt+1] var yyLOCAL *tree.Select -//line mysql_sql.y:6928 +//line mysql_sql.y:6931 { intoVars, deprecatedInto, intoErr := tree.SelectIntoVariablesForTopLevel(yyDollar[2].selectStatementUnion()) if intoErr != "" { @@ -22414,7 +22475,7 @@ yydefault: case 990: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL *tree.Select -//line mysql_sql.y:6945 +//line mysql_sql.y:6948 { intoVars, deprecatedInto, intoErr := tree.SelectIntoVariablesForTopLevel(yyDollar[2].selectStatementUnion()) if intoErr != "" { @@ -22435,7 +22496,7 @@ yydefault: case 991: yyDollar = yyS[yypt-6 : yypt+1] var yyLOCAL *tree.Select -//line mysql_sql.y:6962 +//line mysql_sql.y:6965 { intoVars, deprecatedInto, intoErr := tree.SelectIntoVariablesForTopLevel(yyDollar[2].selectStatementUnion()) if intoErr != "" { @@ -22456,7 +22517,7 @@ yydefault: case 992: yyDollar = yyS[yypt-5 : yypt+1] var yyLOCAL *tree.Select -//line mysql_sql.y:6979 +//line mysql_sql.y:6982 { intoVars, deprecatedInto, intoErr := tree.SelectIntoVariablesForTopLevel(yyDollar[2].selectStatementUnion()) if intoErr != "" { @@ -22477,7 +22538,7 @@ yydefault: case 993: yyDollar = yyS[yypt-6 : yypt+1] var yyLOCAL *tree.Select -//line mysql_sql.y:6996 +//line mysql_sql.y:6999 { intoVars, deprecatedInto, intoErr := tree.SelectIntoVariablesForTopLevel(yyDollar[2].selectStatementUnion()) if intoErr != "" { @@ -22498,7 +22559,7 @@ yydefault: case 994: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL *tree.TimeWindow -//line mysql_sql.y:7014 +//line mysql_sql.y:7017 { yyLOCAL = nil } @@ -22506,7 +22567,7 @@ yydefault: case 995: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL *tree.TimeWindow -//line mysql_sql.y:7018 +//line mysql_sql.y:7021 { yyLOCAL = yyDollar[1].timeWindowUnion() } @@ -22514,7 +22575,7 @@ yydefault: case 996: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL *tree.TimeWindow -//line mysql_sql.y:7024 +//line mysql_sql.y:7027 { yyLOCAL = &tree.TimeWindow{ Interval: yyDollar[1].timeIntervalUnion(), @@ -22527,7 +22588,7 @@ yydefault: case 997: yyDollar = yyS[yypt-8 : yypt+1] var yyLOCAL *tree.Interval -//line mysql_sql.y:7035 +//line mysql_sql.y:7038 { str := fmt.Sprintf("%v", yyDollar[5].item) v, errStr := util.GetInt64(yyDollar[5].item) @@ -22545,7 +22606,7 @@ yydefault: case 998: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL *tree.Sliding -//line mysql_sql.y:7050 +//line mysql_sql.y:7053 { yyLOCAL = nil } @@ -22553,7 +22614,7 @@ yydefault: case 999: yyDollar = yyS[yypt-6 : yypt+1] var yyLOCAL *tree.Sliding -//line mysql_sql.y:7054 +//line mysql_sql.y:7057 { str := fmt.Sprintf("%v", yyDollar[3].item) v, errStr := util.GetInt64(yyDollar[3].item) @@ -22570,7 +22631,7 @@ yydefault: case 1000: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL bool -//line mysql_sql.y:7068 +//line mysql_sql.y:7071 { yyLOCAL = false } @@ -22578,7 +22639,7 @@ yydefault: case 1001: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL bool -//line mysql_sql.y:7072 +//line mysql_sql.y:7075 { yyLOCAL = true } @@ -22586,7 +22647,7 @@ yydefault: case 1002: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL *tree.Fill -//line mysql_sql.y:7077 +//line mysql_sql.y:7080 { yyLOCAL = nil } @@ -22594,7 +22655,7 @@ yydefault: case 1003: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL *tree.Fill -//line mysql_sql.y:7081 +//line mysql_sql.y:7084 { yyLOCAL = &tree.Fill{ Mode: yyDollar[3].fillModeUnion(), @@ -22604,7 +22665,7 @@ yydefault: case 1004: yyDollar = yyS[yypt-6 : yypt+1] var yyLOCAL *tree.Fill -//line mysql_sql.y:7087 +//line mysql_sql.y:7090 { yyLOCAL = &tree.Fill{ Mode: tree.FillValue, @@ -22615,7 +22676,7 @@ yydefault: case 1005: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.FillMode -//line mysql_sql.y:7096 +//line mysql_sql.y:7099 { yyLOCAL = tree.FillPrev } @@ -22623,7 +22684,7 @@ yydefault: case 1006: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.FillMode -//line mysql_sql.y:7100 +//line mysql_sql.y:7103 { yyLOCAL = tree.FillNext } @@ -22631,7 +22692,7 @@ yydefault: case 1007: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.FillMode -//line mysql_sql.y:7104 +//line mysql_sql.y:7107 { yyLOCAL = tree.FillNone } @@ -22639,7 +22700,7 @@ yydefault: case 1008: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.FillMode -//line mysql_sql.y:7108 +//line mysql_sql.y:7111 { yyLOCAL = tree.FillNull } @@ -22647,7 +22708,7 @@ yydefault: case 1009: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.FillMode -//line mysql_sql.y:7112 +//line mysql_sql.y:7115 { yyLOCAL = tree.FillLinear } @@ -22655,7 +22716,7 @@ yydefault: case 1010: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL *tree.With -//line mysql_sql.y:7118 +//line mysql_sql.y:7121 { yyLOCAL = &tree.With{ IsRecursive: false, @@ -22666,7 +22727,7 @@ yydefault: case 1011: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL *tree.With -//line mysql_sql.y:7125 +//line mysql_sql.y:7128 { yyLOCAL = &tree.With{ IsRecursive: true, @@ -22677,7 +22738,7 @@ yydefault: case 1012: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL []*tree.CTE -//line mysql_sql.y:7134 +//line mysql_sql.y:7137 { yyLOCAL = []*tree.CTE{yyDollar[1].cteUnion()} } @@ -22685,7 +22746,7 @@ yydefault: case 1013: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL []*tree.CTE -//line mysql_sql.y:7138 +//line mysql_sql.y:7141 { yyLOCAL = append(yyDollar[1].cteListUnion(), yyDollar[3].cteUnion()) } @@ -22693,7 +22754,7 @@ yydefault: case 1014: yyDollar = yyS[yypt-6 : yypt+1] var yyLOCAL *tree.CTE -//line mysql_sql.y:7144 +//line mysql_sql.y:7147 { yyLOCAL = &tree.CTE{ Name: &tree.AliasClause{Alias: tree.Identifier(yyDollar[1].cstrUnion().Compare()), Cols: yyDollar[2].identifierListUnion()}, @@ -22704,7 +22765,7 @@ yydefault: case 1015: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL tree.IdentifierList -//line mysql_sql.y:7152 +//line mysql_sql.y:7155 { yyLOCAL = nil } @@ -22712,7 +22773,7 @@ yydefault: case 1016: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL tree.IdentifierList -//line mysql_sql.y:7156 +//line mysql_sql.y:7159 { yyLOCAL = yyDollar[2].identifierListUnion() } @@ -22720,7 +22781,7 @@ yydefault: case 1017: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL *tree.Limit -//line mysql_sql.y:7161 +//line mysql_sql.y:7164 { yyLOCAL = nil } @@ -22728,7 +22789,7 @@ yydefault: case 1018: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL *tree.Limit -//line mysql_sql.y:7165 +//line mysql_sql.y:7168 { yyLOCAL = yyDollar[1].limitUnion() } @@ -22736,7 +22797,7 @@ yydefault: case 1019: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL *tree.Limit -//line mysql_sql.y:7170 +//line mysql_sql.y:7173 { yyLOCAL = nil } @@ -22744,7 +22805,7 @@ yydefault: case 1020: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL *tree.Limit -//line mysql_sql.y:7174 +//line mysql_sql.y:7177 { yyLOCAL = yyDollar[1].limitUnion() } @@ -22752,7 +22813,7 @@ yydefault: case 1021: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL *tree.Limit -//line mysql_sql.y:7178 +//line mysql_sql.y:7181 { yyLOCAL = yyDollar[1].limitUnion() } @@ -22760,7 +22821,7 @@ yydefault: case 1022: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL *tree.Limit -//line mysql_sql.y:7184 +//line mysql_sql.y:7187 { yyLOCAL = &tree.Limit{Count: yyDollar[2].exprUnion()} } @@ -22768,7 +22829,7 @@ yydefault: case 1023: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL *tree.Limit -//line mysql_sql.y:7188 +//line mysql_sql.y:7191 { yyLOCAL = &tree.Limit{Offset: yyDollar[2].exprUnion(), Count: yyDollar[4].exprUnion()} } @@ -22776,7 +22837,7 @@ yydefault: case 1024: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL *tree.Limit -//line mysql_sql.y:7192 +//line mysql_sql.y:7195 { yyLOCAL = &tree.Limit{Offset: yyDollar[4].exprUnion(), Count: yyDollar[2].exprUnion()} } @@ -22784,7 +22845,7 @@ yydefault: case 1025: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL *tree.Limit -//line mysql_sql.y:7198 +//line mysql_sql.y:7201 { yyLOCAL = &tree.Limit{Offset: yyDollar[2].exprUnion()} } @@ -22792,7 +22853,7 @@ yydefault: case 1026: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL *tree.RankOption -//line mysql_sql.y:7203 +//line mysql_sql.y:7206 { yyLOCAL = nil } @@ -22800,7 +22861,7 @@ yydefault: case 1027: yyDollar = yyS[yypt-5 : yypt+1] var yyLOCAL *tree.RankOption -//line mysql_sql.y:7207 +//line mysql_sql.y:7210 { // Parse option strings to extract key=value pairs into a map optionMap := make(map[string]string) @@ -22838,7 +22899,7 @@ yydefault: case 1028: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL tree.OrderBy -//line mysql_sql.y:7242 +//line mysql_sql.y:7245 { yyLOCAL = nil } @@ -22846,7 +22907,7 @@ yydefault: case 1029: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.OrderBy -//line mysql_sql.y:7246 +//line mysql_sql.y:7249 { yyLOCAL = yyDollar[1].orderByUnion() } @@ -22854,7 +22915,7 @@ yydefault: case 1030: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL tree.OrderBy -//line mysql_sql.y:7251 +//line mysql_sql.y:7254 { yyLOCAL = nil } @@ -22862,7 +22923,7 @@ yydefault: case 1031: yyDollar = yyS[yypt-5 : yypt+1] var yyLOCAL tree.OrderBy -//line mysql_sql.y:7255 +//line mysql_sql.y:7258 { yyLOCAL = yyDollar[4].orderByUnion() } @@ -22870,7 +22931,7 @@ yydefault: case 1032: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL tree.OrderBy -//line mysql_sql.y:7261 +//line mysql_sql.y:7264 { yyLOCAL = yyDollar[3].orderByUnion() } @@ -22878,7 +22939,7 @@ yydefault: case 1033: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.OrderBy -//line mysql_sql.y:7267 +//line mysql_sql.y:7270 { yyLOCAL = tree.OrderBy{yyDollar[1].orderUnion()} } @@ -22886,7 +22947,7 @@ yydefault: case 1034: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL tree.OrderBy -//line mysql_sql.y:7271 +//line mysql_sql.y:7274 { yyLOCAL = append(yyDollar[1].orderByUnion(), yyDollar[3].orderUnion()) } @@ -22894,7 +22955,7 @@ yydefault: case 1035: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL *tree.Order -//line mysql_sql.y:7277 +//line mysql_sql.y:7280 { yyLOCAL = &tree.Order{Expr: yyDollar[1].exprUnion(), Direction: yyDollar[2].directionUnion(), NullsPosition: yyDollar[3].nullsPositionUnion()} } @@ -22902,7 +22963,7 @@ yydefault: case 1036: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL tree.Direction -//line mysql_sql.y:7282 +//line mysql_sql.y:7285 { yyLOCAL = tree.DefaultDirection } @@ -22910,7 +22971,7 @@ yydefault: case 1037: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.Direction -//line mysql_sql.y:7286 +//line mysql_sql.y:7289 { yyLOCAL = tree.Ascending } @@ -22918,7 +22979,7 @@ yydefault: case 1038: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.Direction -//line mysql_sql.y:7290 +//line mysql_sql.y:7293 { yyLOCAL = tree.Descending } @@ -22926,21 +22987,21 @@ yydefault: case 1039: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL tree.NullsPosition -//line mysql_sql.y:7295 +//line mysql_sql.y:7298 { yyLOCAL = tree.DefaultNullsPosition } yyVAL.union = yyLOCAL case 1040: yyDollar = yyS[yypt-2 : yypt+1] -//line mysql_sql.y:7299 +//line mysql_sql.y:7302 { yylex.Error("NULLS FIRST is not supported in MySQL syntax") return 1 } case 1041: yyDollar = yyS[yypt-2 : yypt+1] -//line mysql_sql.y:7303 +//line mysql_sql.y:7306 { yylex.Error("NULLS LAST is not supported in MySQL syntax") return 1 @@ -22948,7 +23009,7 @@ yydefault: case 1042: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL *tree.SelectLockInfo -//line mysql_sql.y:7308 +//line mysql_sql.y:7311 { yyLOCAL = nil } @@ -22956,7 +23017,7 @@ yydefault: case 1043: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL *tree.SelectLockInfo -//line mysql_sql.y:7312 +//line mysql_sql.y:7315 { yyLOCAL = &tree.SelectLockInfo{ LockType: tree.SelectLockForUpdate, @@ -22966,7 +23027,7 @@ yydefault: case 1044: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL *tree.SelectLockInfo -//line mysql_sql.y:7318 +//line mysql_sql.y:7321 { yyLOCAL = &tree.SelectLockInfo{ LockType: tree.SelectLockForShare, @@ -22976,7 +23037,7 @@ yydefault: case 1045: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL *tree.SelectLockInfo -//line mysql_sql.y:7324 +//line mysql_sql.y:7327 { yyLOCAL = &tree.SelectLockInfo{ LockType: tree.SelectLockForShare, @@ -22986,7 +23047,7 @@ yydefault: case 1046: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL tree.SelectStatement -//line mysql_sql.y:7332 +//line mysql_sql.y:7335 { yyLOCAL = &tree.ParenSelect{Select: yyDollar[2].selectUnion()} } @@ -22994,7 +23055,7 @@ yydefault: case 1047: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL tree.SelectStatement -//line mysql_sql.y:7336 +//line mysql_sql.y:7339 { yyLOCAL = &tree.ParenSelect{Select: &tree.Select{Select: yyDollar[2].selectStatementUnion()}} } @@ -23002,7 +23063,7 @@ yydefault: case 1048: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.SelectStatement -//line mysql_sql.y:7340 +//line mysql_sql.y:7343 { yyLOCAL = yyDollar[1].selectStatementUnion() } @@ -23010,7 +23071,7 @@ yydefault: case 1049: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.SelectStatement -//line mysql_sql.y:7344 +//line mysql_sql.y:7347 { yyLOCAL = yyDollar[1].selectStatementUnion() } @@ -23018,7 +23079,7 @@ yydefault: case 1050: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.SelectStatement -//line mysql_sql.y:7349 +//line mysql_sql.y:7352 { yyLOCAL = yyDollar[1].selectStatementUnion() } @@ -23026,7 +23087,7 @@ yydefault: case 1051: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL tree.SelectStatement -//line mysql_sql.y:7353 +//line mysql_sql.y:7356 { yyLOCAL = &tree.UnionClause{ Type: yyDollar[2].unionTypeRecordUnion().Type, @@ -23040,7 +23101,7 @@ yydefault: case 1052: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL tree.SelectStatement -//line mysql_sql.y:7363 +//line mysql_sql.y:7366 { yyLOCAL = &tree.UnionClause{ Type: yyDollar[2].unionTypeRecordUnion().Type, @@ -23054,7 +23115,7 @@ yydefault: case 1053: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL tree.SelectStatement -//line mysql_sql.y:7373 +//line mysql_sql.y:7376 { yyLOCAL = &tree.UnionClause{ Type: yyDollar[2].unionTypeRecordUnion().Type, @@ -23068,7 +23129,7 @@ yydefault: case 1054: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL tree.SelectStatement -//line mysql_sql.y:7383 +//line mysql_sql.y:7386 { yyLOCAL = &tree.UnionClause{ Type: yyDollar[2].unionTypeRecordUnion().Type, @@ -23082,7 +23143,7 @@ yydefault: case 1055: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL tree.SelectStatement -//line mysql_sql.y:7393 +//line mysql_sql.y:7396 { yyLOCAL = &tree.UnionClause{Type: yyDollar[2].unionTypeRecordUnion().Type, Left: yyDollar[1].selectStatementUnion(), Right: yyDollar[3].selectStatementUnion(), All: yyDollar[2].unionTypeRecordUnion().All, Distinct: yyDollar[2].unionTypeRecordUnion().Distinct} } @@ -23090,7 +23151,7 @@ yydefault: case 1056: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL tree.SelectStatement -//line mysql_sql.y:7397 +//line mysql_sql.y:7400 { yyLOCAL = &tree.UnionClause{Type: yyDollar[2].unionTypeRecordUnion().Type, Left: yyDollar[1].selectStatementUnion(), Right: yyDollar[3].selectStatementUnion(), All: yyDollar[2].unionTypeRecordUnion().All, Distinct: yyDollar[2].unionTypeRecordUnion().Distinct} } @@ -23098,7 +23159,7 @@ yydefault: case 1057: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL tree.SelectStatement -//line mysql_sql.y:7401 +//line mysql_sql.y:7404 { yyLOCAL = &tree.UnionClause{Type: yyDollar[2].unionTypeRecordUnion().Type, Left: yyDollar[1].selectStatementUnion(), Right: yyDollar[3].selectStatementUnion(), All: yyDollar[2].unionTypeRecordUnion().All, Distinct: yyDollar[2].unionTypeRecordUnion().Distinct} } @@ -23106,7 +23167,7 @@ yydefault: case 1058: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL tree.SelectStatement -//line mysql_sql.y:7405 +//line mysql_sql.y:7408 { yyLOCAL = &tree.UnionClause{Type: yyDollar[2].unionTypeRecordUnion().Type, Left: yyDollar[1].selectStatementUnion(), Right: yyDollar[3].selectStatementUnion(), All: yyDollar[2].unionTypeRecordUnion().All, Distinct: yyDollar[2].unionTypeRecordUnion().Distinct} } @@ -23114,7 +23175,7 @@ yydefault: case 1059: yyDollar = yyS[yypt-5 : yypt+1] var yyLOCAL tree.SelectStatement -//line mysql_sql.y:7413 +//line mysql_sql.y:7416 { yyLOCAL = &tree.ParenSelect{Select: tree.NewSelect(yyDollar[2].selectStatementUnion(), yyDollar[3].orderByUnion(), yyDollar[4].limitUnion())} } @@ -23122,7 +23183,7 @@ yydefault: case 1060: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.SelectStatement -//line mysql_sql.y:7419 +//line mysql_sql.y:7422 { yyLOCAL = yyDollar[1].selectStatementUnion() } @@ -23130,7 +23191,7 @@ yydefault: case 1061: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL tree.SelectStatement -//line mysql_sql.y:7423 +//line mysql_sql.y:7426 { yyLOCAL = &tree.UnionClause{Type: yyDollar[2].unionTypeRecordUnion().Type, Left: yyDollar[1].selectStatementUnion(), Right: yyDollar[3].selectStatementUnion(), All: yyDollar[2].unionTypeRecordUnion().All, Distinct: yyDollar[2].unionTypeRecordUnion().Distinct} } @@ -23138,7 +23199,7 @@ yydefault: case 1062: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL tree.SelectStatement -//line mysql_sql.y:7429 +//line mysql_sql.y:7432 { yyLOCAL = makeSelectStarFromTable(yyDollar[2].tableNameUnion()) } @@ -23146,7 +23207,7 @@ yydefault: case 1063: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.SelectStatement -//line mysql_sql.y:7434 +//line mysql_sql.y:7437 { yyLOCAL = yyDollar[1].selectStatementUnion() } @@ -23154,7 +23215,7 @@ yydefault: case 1064: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.SelectStatement -//line mysql_sql.y:7438 +//line mysql_sql.y:7441 { yyLOCAL = yyDollar[1].selectStatementUnion() } @@ -23162,7 +23223,7 @@ yydefault: case 1065: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.SelectStatement -//line mysql_sql.y:7442 +//line mysql_sql.y:7445 { yyLOCAL = yyDollar[1].selectStatementUnion() } @@ -23170,7 +23231,7 @@ yydefault: case 1066: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL tree.SelectStatement -//line mysql_sql.y:7446 +//line mysql_sql.y:7449 { yyLOCAL = &tree.ValuesClause{Rows: yyDollar[2].rowsExprsUnion(), RowWord: true} } @@ -23178,7 +23239,7 @@ yydefault: case 1067: yyDollar = yyS[yypt-5 : yypt+1] var yyLOCAL tree.SelectStatement -//line mysql_sql.y:7452 +//line mysql_sql.y:7455 { yyLOCAL = &tree.ParenSelect{Select: tree.NewSelect(yyDollar[2].selectStatementUnion(), yyDollar[3].orderByUnion(), yyDollar[4].limitUnion())} } @@ -23186,7 +23247,7 @@ yydefault: case 1068: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.SelectStatement -//line mysql_sql.y:7458 +//line mysql_sql.y:7461 { yyLOCAL = yyDollar[1].selectStatementUnion() } @@ -23194,7 +23255,7 @@ yydefault: case 1069: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL tree.SelectStatement -//line mysql_sql.y:7462 +//line mysql_sql.y:7465 { yyLOCAL = &tree.UnionClause{Type: yyDollar[2].unionTypeRecordUnion().Type, Left: yyDollar[1].selectStatementUnion(), Right: yyDollar[3].selectStatementUnion(), All: yyDollar[2].unionTypeRecordUnion().All, Distinct: yyDollar[2].unionTypeRecordUnion().Distinct} } @@ -23202,7 +23263,7 @@ yydefault: case 1070: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL tree.SelectStatement -//line mysql_sql.y:7468 +//line mysql_sql.y:7471 { yyLOCAL = &tree.ValuesClause{Rows: yyDollar[2].rowsExprsUnion(), RowWord: true} } @@ -23210,7 +23271,7 @@ yydefault: case 1071: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.SelectStatement -//line mysql_sql.y:7474 +//line mysql_sql.y:7477 { yyLOCAL = yyDollar[1].selectStatementUnion() } @@ -23218,7 +23279,7 @@ yydefault: case 1072: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.SelectStatement -//line mysql_sql.y:7478 +//line mysql_sql.y:7481 { yyLOCAL = yyDollar[1].selectStatementUnion() } @@ -23226,7 +23287,7 @@ yydefault: case 1073: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.SelectStatement -//line mysql_sql.y:7482 +//line mysql_sql.y:7485 { yyLOCAL = yyDollar[1].selectStatementUnion() } @@ -23234,7 +23295,7 @@ yydefault: case 1074: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.SelectStatement -//line mysql_sql.y:7486 +//line mysql_sql.y:7489 { yyLOCAL = yyDollar[1].selectStatementUnion() } @@ -23242,7 +23303,7 @@ yydefault: case 1075: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL *tree.UnionTypeRecord -//line mysql_sql.y:7492 +//line mysql_sql.y:7495 { yyLOCAL = &tree.UnionTypeRecord{ Type: tree.UNION, @@ -23254,7 +23315,7 @@ yydefault: case 1076: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL *tree.UnionTypeRecord -//line mysql_sql.y:7500 +//line mysql_sql.y:7503 { yyLOCAL = &tree.UnionTypeRecord{ Type: tree.UNION, @@ -23266,7 +23327,7 @@ yydefault: case 1077: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL *tree.UnionTypeRecord -//line mysql_sql.y:7508 +//line mysql_sql.y:7511 { yyLOCAL = &tree.UnionTypeRecord{ Type: tree.UNION, @@ -23278,7 +23339,7 @@ yydefault: case 1078: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL *tree.UnionTypeRecord -//line mysql_sql.y:7517 +//line mysql_sql.y:7520 { yyLOCAL = &tree.UnionTypeRecord{ Type: tree.EXCEPT, @@ -23290,7 +23351,7 @@ yydefault: case 1079: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL *tree.UnionTypeRecord -//line mysql_sql.y:7525 +//line mysql_sql.y:7528 { yyLOCAL = &tree.UnionTypeRecord{ Type: tree.EXCEPT, @@ -23302,7 +23363,7 @@ yydefault: case 1080: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL *tree.UnionTypeRecord -//line mysql_sql.y:7533 +//line mysql_sql.y:7536 { yyLOCAL = &tree.UnionTypeRecord{ Type: tree.EXCEPT, @@ -23314,7 +23375,7 @@ yydefault: case 1081: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL *tree.UnionTypeRecord -//line mysql_sql.y:7541 +//line mysql_sql.y:7544 { yyLOCAL = &tree.UnionTypeRecord{ Type: tree.INTERSECT, @@ -23326,7 +23387,7 @@ yydefault: case 1082: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL *tree.UnionTypeRecord -//line mysql_sql.y:7549 +//line mysql_sql.y:7552 { yyLOCAL = &tree.UnionTypeRecord{ Type: tree.INTERSECT, @@ -23338,7 +23399,7 @@ yydefault: case 1083: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL *tree.UnionTypeRecord -//line mysql_sql.y:7557 +//line mysql_sql.y:7560 { yyLOCAL = &tree.UnionTypeRecord{ Type: tree.INTERSECT, @@ -23350,7 +23411,7 @@ yydefault: case 1084: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL *tree.UnionTypeRecord -//line mysql_sql.y:7565 +//line mysql_sql.y:7568 { yyLOCAL = &tree.UnionTypeRecord{ Type: tree.UT_MINUS, @@ -23362,7 +23423,7 @@ yydefault: case 1085: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL *tree.UnionTypeRecord -//line mysql_sql.y:7573 +//line mysql_sql.y:7576 { yyLOCAL = &tree.UnionTypeRecord{ Type: tree.UT_MINUS, @@ -23374,7 +23435,7 @@ yydefault: case 1086: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL *tree.UnionTypeRecord -//line mysql_sql.y:7581 +//line mysql_sql.y:7584 { yyLOCAL = &tree.UnionTypeRecord{ Type: tree.UT_MINUS, @@ -23386,7 +23447,7 @@ yydefault: case 1087: yyDollar = yyS[yypt-9 : yypt+1] var yyLOCAL tree.SelectStatement -//line mysql_sql.y:7591 +//line mysql_sql.y:7594 { yyLOCAL = &tree.SelectClause{ Distinct: tree.QuerySpecOptionDistinct&yyDollar[2].selectOptionsUnion() != 0, @@ -23405,7 +23466,7 @@ yydefault: case 1088: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL uint64 -//line mysql_sql.y:7608 +//line mysql_sql.y:7611 { yyLOCAL = tree.QuerySpecOptionNone } @@ -23413,7 +23474,7 @@ yydefault: case 1089: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL uint64 -//line mysql_sql.y:7612 +//line mysql_sql.y:7615 { yyLOCAL = yyDollar[1].selectOptionsUnion() } @@ -23421,7 +23482,7 @@ yydefault: case 1090: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL uint64 -//line mysql_sql.y:7618 +//line mysql_sql.y:7621 { yyLOCAL = yyDollar[1].selectOptionUnion() } @@ -23429,7 +23490,7 @@ yydefault: case 1091: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL uint64 -//line mysql_sql.y:7622 +//line mysql_sql.y:7625 { yyLOCAL = yyDollar[1].selectOptionsUnion() | yyDollar[2].selectOptionUnion() } @@ -23437,7 +23498,7 @@ yydefault: case 1092: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL uint64 -//line mysql_sql.y:7628 +//line mysql_sql.y:7631 { yyLOCAL = tree.QuerySpecOptionSqlSmallResult } @@ -23445,7 +23506,7 @@ yydefault: case 1093: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL uint64 -//line mysql_sql.y:7632 +//line mysql_sql.y:7635 { yyLOCAL = tree.QuerySpecOptionSqlBigResult } @@ -23453,7 +23514,7 @@ yydefault: case 1094: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL uint64 -//line mysql_sql.y:7636 +//line mysql_sql.y:7639 { yyLOCAL = tree.QuerySpecOptionSqlBufferResult } @@ -23461,7 +23522,7 @@ yydefault: case 1095: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL uint64 -//line mysql_sql.y:7640 +//line mysql_sql.y:7643 { yyLOCAL = tree.QuerySpecOptionStraightJoin } @@ -23469,7 +23530,7 @@ yydefault: case 1096: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL uint64 -//line mysql_sql.y:7644 +//line mysql_sql.y:7647 { yyLOCAL = tree.QuerySpecOptionHighPriority } @@ -23477,7 +23538,7 @@ yydefault: case 1097: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL uint64 -//line mysql_sql.y:7648 +//line mysql_sql.y:7651 { yyLOCAL = tree.QuerySpecOptionSqlCalcFoundRows } @@ -23485,7 +23546,7 @@ yydefault: case 1098: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL uint64 -//line mysql_sql.y:7652 +//line mysql_sql.y:7655 { yyLOCAL = tree.QuerySpecOptionSqlNoCache } @@ -23493,7 +23554,7 @@ yydefault: case 1099: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL uint64 -//line mysql_sql.y:7656 +//line mysql_sql.y:7659 { yyLOCAL = tree.QuerySpecOptionAll } @@ -23501,7 +23562,7 @@ yydefault: case 1100: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL uint64 -//line mysql_sql.y:7660 +//line mysql_sql.y:7663 { yyLOCAL = tree.QuerySpecOptionDistinct } @@ -23509,7 +23570,7 @@ yydefault: case 1101: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL uint64 -//line mysql_sql.y:7664 +//line mysql_sql.y:7667 { yyLOCAL = tree.QuerySpecOptionDistinctRow } @@ -23517,7 +23578,7 @@ yydefault: case 1102: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL *tree.Where -//line mysql_sql.y:7686 +//line mysql_sql.y:7689 { yyLOCAL = nil } @@ -23525,7 +23586,7 @@ yydefault: case 1103: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL *tree.Where -//line mysql_sql.y:7690 +//line mysql_sql.y:7693 { yyLOCAL = &tree.Where{Type: tree.AstHaving, Expr: yyDollar[2].exprUnion()} } @@ -23533,7 +23594,7 @@ yydefault: case 1104: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL *tree.GroupByClause -//line mysql_sql.y:7695 +//line mysql_sql.y:7698 { yyLOCAL = nil } @@ -23541,7 +23602,7 @@ yydefault: case 1105: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL *tree.GroupByClause -//line mysql_sql.y:7699 +//line mysql_sql.y:7702 { exprsList := []tree.Exprs{yyDollar[3].exprsUnion()} yyLOCAL = &tree.GroupByClause{ @@ -23555,7 +23616,7 @@ yydefault: case 1106: yyDollar = yyS[yypt-7 : yypt+1] var yyLOCAL *tree.GroupByClause -//line mysql_sql.y:7709 +//line mysql_sql.y:7712 { yyLOCAL = &tree.GroupByClause{ GroupByExprsList: yyDollar[6].rowsExprsUnion(), @@ -23569,7 +23630,7 @@ yydefault: case 1107: yyDollar = yyS[yypt-6 : yypt+1] var yyLOCAL *tree.GroupByClause -//line mysql_sql.y:7719 +//line mysql_sql.y:7722 { yyLOCAL = &tree.GroupByClause{ GroupByExprsList: []tree.Exprs{yyDollar[5].exprsUnion()}, @@ -23582,7 +23643,7 @@ yydefault: case 1108: yyDollar = yyS[yypt-6 : yypt+1] var yyLOCAL *tree.GroupByClause -//line mysql_sql.y:7728 +//line mysql_sql.y:7731 { yyLOCAL = &tree.GroupByClause{ GroupByExprsList: []tree.Exprs{yyDollar[5].exprsUnion()}, @@ -23595,7 +23656,7 @@ yydefault: case 1109: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL []tree.Exprs -//line mysql_sql.y:7739 +//line mysql_sql.y:7742 { yyLOCAL = []tree.Exprs{yyDollar[2].exprsUnion()} } @@ -23603,7 +23664,7 @@ yydefault: case 1110: yyDollar = yyS[yypt-5 : yypt+1] var yyLOCAL []tree.Exprs -//line mysql_sql.y:7743 +//line mysql_sql.y:7746 { yyLOCAL = append(yyDollar[1].rowsExprsUnion(), yyDollar[4].exprsUnion()) } @@ -23611,7 +23672,7 @@ yydefault: case 1111: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL bool -//line mysql_sql.y:7749 +//line mysql_sql.y:7752 { yyLOCAL = false } @@ -23619,7 +23680,7 @@ yydefault: case 1112: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL bool -//line mysql_sql.y:7753 +//line mysql_sql.y:7756 { yyLOCAL = true } @@ -23627,7 +23688,7 @@ yydefault: case 1113: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL *tree.Where -//line mysql_sql.y:7758 +//line mysql_sql.y:7761 { yyLOCAL = nil } @@ -23635,7 +23696,7 @@ yydefault: case 1114: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL *tree.Where -//line mysql_sql.y:7762 +//line mysql_sql.y:7765 { yyLOCAL = &tree.Where{Type: tree.AstWhere, Expr: yyDollar[2].exprUnion()} } @@ -23643,7 +23704,7 @@ yydefault: case 1115: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.SelectExprs -//line mysql_sql.y:7768 +//line mysql_sql.y:7771 { yyLOCAL = tree.SelectExprs{yyDollar[1].selectExprUnion()} } @@ -23651,7 +23712,7 @@ yydefault: case 1116: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL tree.SelectExprs -//line mysql_sql.y:7772 +//line mysql_sql.y:7775 { yyLOCAL = append(yyDollar[1].selectExprsUnion(), yyDollar[3].selectExprUnion()) } @@ -23659,7 +23720,7 @@ yydefault: case 1117: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.SelectExpr -//line mysql_sql.y:7778 +//line mysql_sql.y:7781 { yyLOCAL = tree.SelectExpr{Expr: tree.StarExpr()} } @@ -23667,7 +23728,7 @@ yydefault: case 1118: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL tree.SelectExpr -//line mysql_sql.y:7782 +//line mysql_sql.y:7785 { yyLOCAL = tree.SelectExpr{Expr: yyDollar[1].exprUnion(), As: yyDollar[2].cstrUnion()} } @@ -23675,7 +23736,7 @@ yydefault: case 1119: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL tree.SelectExpr -//line mysql_sql.y:7786 +//line mysql_sql.y:7789 { yyLOCAL = tree.SelectExpr{Expr: tree.NewUnresolvedNameWithStar(yyDollar[1].cstrUnion())} } @@ -23683,7 +23744,7 @@ yydefault: case 1120: yyDollar = yyS[yypt-5 : yypt+1] var yyLOCAL tree.SelectExpr -//line mysql_sql.y:7790 +//line mysql_sql.y:7793 { yyLOCAL = tree.SelectExpr{Expr: tree.NewUnresolvedNameWithStar(yyDollar[1].cstrUnion(), yyDollar[3].cstrUnion())} } @@ -23691,7 +23752,7 @@ yydefault: case 1121: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL *tree.From -//line mysql_sql.y:7795 +//line mysql_sql.y:7798 { prefix := tree.ObjectNamePrefix{ExplicitSchema: false} tn := tree.NewTableName(tree.Identifier(""), prefix, nil) @@ -23703,7 +23764,7 @@ yydefault: case 1122: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL *tree.From -//line mysql_sql.y:7803 +//line mysql_sql.y:7806 { yyLOCAL = yyDollar[1].fromUnion() } @@ -23711,7 +23772,7 @@ yydefault: case 1123: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL *tree.From -//line mysql_sql.y:7809 +//line mysql_sql.y:7812 { yyLOCAL = &tree.From{ Tables: tree.TableExprs{yyDollar[2].tableExprUnion()}, @@ -23721,7 +23782,7 @@ yydefault: case 1124: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.TableExpr -//line mysql_sql.y:7817 +//line mysql_sql.y:7820 { if t, ok := yyDollar[1].tableExprUnion().(*tree.JoinTableExpr); ok { yyLOCAL = t @@ -23735,7 +23796,7 @@ yydefault: case 1125: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL tree.TableExpr -//line mysql_sql.y:7827 +//line mysql_sql.y:7830 { yyLOCAL = &tree.JoinTableExpr{Left: yyDollar[1].tableExprUnion(), Right: yyDollar[3].tableExprUnion(), JoinType: tree.JOIN_TYPE_CROSS} } @@ -23743,7 +23804,7 @@ yydefault: case 1127: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL tree.TableExpr -//line mysql_sql.y:7834 +//line mysql_sql.y:7837 { if !strings.EqualFold(yyDollar[2].str, "OJ") { yylex.Error("expected OJ in table reference escape") @@ -23755,7 +23816,7 @@ yydefault: case 1129: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.TableExpr -//line mysql_sql.y:7845 +//line mysql_sql.y:7848 { yyLOCAL = yyDollar[1].joinTableExprUnion() } @@ -23763,7 +23824,7 @@ yydefault: case 1130: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.TableExpr -//line mysql_sql.y:7849 +//line mysql_sql.y:7852 { yyLOCAL = yyDollar[1].applyTableExprUnion() } @@ -23771,7 +23832,7 @@ yydefault: case 1131: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL *tree.JoinTableExpr -//line mysql_sql.y:7855 +//line mysql_sql.y:7858 { if strings.Contains(yyDollar[2].str, ":") { ss := strings.SplitN(yyDollar[2].str, ":", 2) @@ -23795,7 +23856,7 @@ yydefault: case 1132: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL *tree.JoinTableExpr -//line mysql_sql.y:7875 +//line mysql_sql.y:7878 { yyLOCAL = &tree.JoinTableExpr{ Left: yyDollar[1].tableExprUnion(), @@ -23808,7 +23869,7 @@ yydefault: case 1133: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL *tree.JoinTableExpr -//line mysql_sql.y:7884 +//line mysql_sql.y:7887 { if yyDollar[4].joinCondUnion() == nil { yylex.Error("outer join requires ON/USING clause") @@ -23825,7 +23886,7 @@ yydefault: case 1134: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL *tree.JoinTableExpr -//line mysql_sql.y:7897 +//line mysql_sql.y:7900 { yyLOCAL = &tree.JoinTableExpr{ Left: yyDollar[1].tableExprUnion(), @@ -23837,7 +23898,7 @@ yydefault: case 1135: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL *tree.JoinTableExpr -//line mysql_sql.y:7905 +//line mysql_sql.y:7908 { yyLOCAL = &tree.JoinTableExpr{ Left: yyDollar[1].tableExprUnion(), @@ -23850,7 +23911,7 @@ yydefault: case 1136: yyDollar = yyS[yypt-5 : yypt+1] var yyLOCAL *tree.JoinTableExpr -//line mysql_sql.y:7914 +//line mysql_sql.y:7917 { if !hasAsofLeftFactorAlias(yyDollar[1].tableExprUnion()) { yylex.Error("native ASOF JOIN requires an aliased left table factor") @@ -23868,7 +23929,7 @@ yydefault: case 1137: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL *tree.ApplyTableExpr -//line mysql_sql.y:7930 +//line mysql_sql.y:7933 { yyLOCAL = &tree.ApplyTableExpr{ Left: yyDollar[1].tableExprUnion(), @@ -23879,25 +23940,25 @@ yydefault: yyVAL.union = yyLOCAL case 1138: yyDollar = yyS[yypt-2 : yypt+1] -//line mysql_sql.y:7940 +//line mysql_sql.y:7943 { yyVAL.str = tree.APPLY_TYPE_CROSS } case 1139: yyDollar = yyS[yypt-2 : yypt+1] -//line mysql_sql.y:7944 +//line mysql_sql.y:7947 { yyVAL.str = tree.APPLY_TYPE_OUTER } case 1140: yyDollar = yyS[yypt-2 : yypt+1] -//line mysql_sql.y:7950 +//line mysql_sql.y:7953 { yyVAL.str = tree.JOIN_TYPE_NATURAL } case 1141: yyDollar = yyS[yypt-2 : yypt+1] -//line mysql_sql.y:7954 +//line mysql_sql.y:7957 { switch yyDollar[2].str { case tree.JOIN_TYPE_LEFT: @@ -23910,68 +23971,68 @@ yydefault: } case 1142: yyDollar = yyS[yypt-2 : yypt+1] -//line mysql_sql.y:7967 +//line mysql_sql.y:7970 { yyVAL.str = tree.JOIN_TYPE_LEFT } case 1143: yyDollar = yyS[yypt-3 : yypt+1] -//line mysql_sql.y:7971 +//line mysql_sql.y:7974 { yyVAL.str = tree.JOIN_TYPE_LEFT } case 1144: yyDollar = yyS[yypt-2 : yypt+1] -//line mysql_sql.y:7975 +//line mysql_sql.y:7978 { yyVAL.str = tree.JOIN_TYPE_RIGHT } case 1145: yyDollar = yyS[yypt-3 : yypt+1] -//line mysql_sql.y:7979 +//line mysql_sql.y:7982 { yyVAL.str = tree.JOIN_TYPE_RIGHT } case 1146: yyDollar = yyS[yypt-2 : yypt+1] -//line mysql_sql.y:7983 +//line mysql_sql.y:7986 { yyVAL.str = tree.JOIN_TYPE_FULL } case 1147: yyDollar = yyS[yypt-3 : yypt+1] -//line mysql_sql.y:7987 +//line mysql_sql.y:7990 { yyVAL.str = tree.JOIN_TYPE_FULL } case 1148: yyDollar = yyS[yypt-2 : yypt+1] -//line mysql_sql.y:7993 +//line mysql_sql.y:7996 { yyVAL.str = tree.JOIN_TYPE_DEDUP } case 1149: yyDollar = yyS[yypt-2 : yypt+1] -//line mysql_sql.y:7999 +//line mysql_sql.y:8002 { yyVAL.str = tree.JOIN_TYPE_ASOF } case 1150: yyDollar = yyS[yypt-3 : yypt+1] -//line mysql_sql.y:8003 +//line mysql_sql.y:8006 { yyVAL.str = tree.JOIN_TYPE_ASOF_LEFT } case 1151: yyDollar = yyS[yypt-4 : yypt+1] -//line mysql_sql.y:8007 +//line mysql_sql.y:8010 { yyVAL.str = tree.JOIN_TYPE_ASOF_LEFT } case 1152: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL tree.Expr -//line mysql_sql.y:8012 +//line mysql_sql.y:8015 { yyLOCAL = nil } @@ -23979,7 +24040,7 @@ yydefault: case 1153: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL tree.Expr -//line mysql_sql.y:8016 +//line mysql_sql.y:8019 { yyLOCAL = yyDollar[2].exprUnion() } @@ -23987,7 +24048,7 @@ yydefault: case 1154: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:8022 +//line mysql_sql.y:8025 { stmt := &tree.ValuesStatement{ Rows: yyDollar[2].rowsExprsUnion(), @@ -24004,7 +24065,7 @@ yydefault: case 1155: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL []tree.Exprs -//line mysql_sql.y:8037 +//line mysql_sql.y:8040 { yyLOCAL = []tree.Exprs{yyDollar[1].exprsUnion()} } @@ -24012,7 +24073,7 @@ yydefault: case 1156: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL []tree.Exprs -//line mysql_sql.y:8041 +//line mysql_sql.y:8044 { yyLOCAL = append(yyDollar[1].rowsExprsUnion(), yyDollar[3].exprsUnion()) } @@ -24020,57 +24081,57 @@ yydefault: case 1157: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL tree.Exprs -//line mysql_sql.y:8047 +//line mysql_sql.y:8050 { yyLOCAL = yyDollar[3].exprsUnion() } yyVAL.union = yyLOCAL case 1158: yyDollar = yyS[yypt-1 : yypt+1] -//line mysql_sql.y:8053 +//line mysql_sql.y:8056 { yyVAL.str = yyDollar[1].str } case 1159: yyDollar = yyS[yypt-3 : yypt+1] -//line mysql_sql.y:8059 +//line mysql_sql.y:8062 { yyVAL.str = yyDollar[2].str } case 1160: yyDollar = yyS[yypt-1 : yypt+1] -//line mysql_sql.y:8065 +//line mysql_sql.y:8068 { yyVAL.str = tree.JOIN_TYPE_STRAIGHT } case 1161: yyDollar = yyS[yypt-1 : yypt+1] -//line mysql_sql.y:8071 +//line mysql_sql.y:8074 { yyVAL.str = tree.JOIN_TYPE_INNER } case 1162: yyDollar = yyS[yypt-2 : yypt+1] -//line mysql_sql.y:8075 +//line mysql_sql.y:8078 { yyVAL.str = tree.JOIN_TYPE_INNER } case 1163: yyDollar = yyS[yypt-2 : yypt+1] -//line mysql_sql.y:8079 +//line mysql_sql.y:8082 { yyVAL.str = tree.JOIN_TYPE_CROSS } case 1164: yyDollar = yyS[yypt-3 : yypt+1] -//line mysql_sql.y:8083 +//line mysql_sql.y:8086 { yyVAL.str = tree.JOIN_TYPE_CENTROIDX + ":" + yyDollar[2].str } case 1165: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL tree.JoinCond -//line mysql_sql.y:8089 +//line mysql_sql.y:8092 { yyLOCAL = nil } @@ -24078,7 +24139,7 @@ yydefault: case 1166: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.JoinCond -//line mysql_sql.y:8093 +//line mysql_sql.y:8096 { yyLOCAL = yyDollar[1].joinCondUnion() } @@ -24086,7 +24147,7 @@ yydefault: case 1167: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL tree.JoinCond -//line mysql_sql.y:8099 +//line mysql_sql.y:8102 { yyLOCAL = &tree.OnJoinCond{Expr: yyDollar[2].exprUnion()} } @@ -24094,7 +24155,7 @@ yydefault: case 1168: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL tree.JoinCond -//line mysql_sql.y:8103 +//line mysql_sql.y:8106 { yyLOCAL = &tree.UsingJoinCond{Cols: yyDollar[3].identifierListUnion()} } @@ -24102,7 +24163,7 @@ yydefault: case 1169: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.IdentifierList -//line mysql_sql.y:8109 +//line mysql_sql.y:8112 { yyLOCAL = tree.IdentifierList{tree.Identifier(yyDollar[1].cstrUnion().Compare())} } @@ -24110,7 +24171,7 @@ yydefault: case 1170: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL tree.IdentifierList -//line mysql_sql.y:8113 +//line mysql_sql.y:8116 { yyLOCAL = append(yyDollar[1].identifierListUnion(), tree.Identifier(yyDollar[3].cstrUnion().Compare())) } @@ -24118,7 +24179,7 @@ yydefault: case 1171: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.TableExpr -//line mysql_sql.y:8119 +//line mysql_sql.y:8122 { yyLOCAL = yyDollar[1].aliasedTableExprUnion() } @@ -24126,7 +24187,7 @@ yydefault: case 1172: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL tree.TableExpr -//line mysql_sql.y:8123 +//line mysql_sql.y:8126 { yyLOCAL = &tree.AliasedTableExpr{ Expr: yyDollar[1].parenTableExprUnion(), @@ -24140,7 +24201,7 @@ yydefault: case 1173: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL tree.TableExpr -//line mysql_sql.y:8133 +//line mysql_sql.y:8136 { if yyDollar[2].str != "" { yyLOCAL = &tree.AliasedTableExpr{ @@ -24157,7 +24218,7 @@ yydefault: case 1174: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL tree.TableExpr -//line mysql_sql.y:8146 +//line mysql_sql.y:8149 { yyLOCAL = yyDollar[2].tableExprUnion() } @@ -24165,7 +24226,7 @@ yydefault: case 1175: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL *tree.ParenTableExpr -//line mysql_sql.y:8154 +//line mysql_sql.y:8157 { yyLOCAL = &tree.ParenTableExpr{Expr: yyDollar[1].selectStatementUnion().(*tree.ParenSelect).Select} } @@ -24173,7 +24234,7 @@ yydefault: case 1176: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL tree.TableExpr -//line mysql_sql.y:8160 +//line mysql_sql.y:8163 { name := tree.NewUnresolvedName(yyDollar[1].cstrUnion()) yyLOCAL = &tree.TableFunction{ @@ -24189,7 +24250,7 @@ yydefault: case 1177: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL *tree.AliasedTableExpr -//line mysql_sql.y:8174 +//line mysql_sql.y:8177 { yyLOCAL = &tree.AliasedTableExpr{ Expr: yyDollar[1].tableNameUnion(), @@ -24203,7 +24264,7 @@ yydefault: case 1178: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL []*tree.IndexHint -//line mysql_sql.y:8185 +//line mysql_sql.y:8188 { yyLOCAL = nil } @@ -24211,7 +24272,7 @@ yydefault: case 1180: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL []*tree.IndexHint -//line mysql_sql.y:8192 +//line mysql_sql.y:8195 { yyLOCAL = []*tree.IndexHint{yyDollar[1].indexHintUnion()} } @@ -24219,7 +24280,7 @@ yydefault: case 1181: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL []*tree.IndexHint -//line mysql_sql.y:8196 +//line mysql_sql.y:8199 { yyLOCAL = append(yyDollar[1].indexHintListUnion(), yyDollar[2].indexHintUnion()) } @@ -24227,7 +24288,7 @@ yydefault: case 1182: yyDollar = yyS[yypt-5 : yypt+1] var yyLOCAL *tree.IndexHint -//line mysql_sql.y:8202 +//line mysql_sql.y:8205 { yyLOCAL = &tree.IndexHint{ IndexNames: yyDollar[4].strsUnion(), @@ -24239,7 +24300,7 @@ yydefault: case 1183: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL tree.IndexHintType -//line mysql_sql.y:8212 +//line mysql_sql.y:8215 { yyLOCAL = tree.HintUse } @@ -24247,7 +24308,7 @@ yydefault: case 1184: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL tree.IndexHintType -//line mysql_sql.y:8216 +//line mysql_sql.y:8219 { yyLOCAL = tree.HintIgnore } @@ -24255,7 +24316,7 @@ yydefault: case 1185: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL tree.IndexHintType -//line mysql_sql.y:8220 +//line mysql_sql.y:8223 { yyLOCAL = tree.HintForce } @@ -24263,7 +24324,7 @@ yydefault: case 1186: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL tree.IndexHintScope -//line mysql_sql.y:8225 +//line mysql_sql.y:8228 { yyLOCAL = tree.HintForScan } @@ -24271,7 +24332,7 @@ yydefault: case 1187: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL tree.IndexHintScope -//line mysql_sql.y:8229 +//line mysql_sql.y:8232 { yyLOCAL = tree.HintForJoin } @@ -24279,7 +24340,7 @@ yydefault: case 1188: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL tree.IndexHintScope -//line mysql_sql.y:8233 +//line mysql_sql.y:8236 { yyLOCAL = tree.HintForOrderBy } @@ -24287,7 +24348,7 @@ yydefault: case 1189: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL tree.IndexHintScope -//line mysql_sql.y:8237 +//line mysql_sql.y:8240 { yyLOCAL = tree.HintForGroupBy } @@ -24295,7 +24356,7 @@ yydefault: case 1190: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL []string -//line mysql_sql.y:8242 +//line mysql_sql.y:8245 { yyLOCAL = nil } @@ -24303,7 +24364,7 @@ yydefault: case 1191: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL []string -//line mysql_sql.y:8246 +//line mysql_sql.y:8249 { yyLOCAL = []string{yyDollar[1].cstrUnion().Compare()} } @@ -24311,7 +24372,7 @@ yydefault: case 1192: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL []string -//line mysql_sql.y:8250 +//line mysql_sql.y:8253 { yyLOCAL = append(yyDollar[1].strsUnion(), yyDollar[3].cstrUnion().Compare()) } @@ -24319,7 +24380,7 @@ yydefault: case 1193: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL []string -//line mysql_sql.y:8254 +//line mysql_sql.y:8257 { yyLOCAL = []string{yyDollar[1].str} } @@ -24327,45 +24388,45 @@ yydefault: case 1194: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL []string -//line mysql_sql.y:8258 +//line mysql_sql.y:8261 { yyLOCAL = append(yyDollar[1].strsUnion(), yyDollar[3].str) } yyVAL.union = yyLOCAL case 1195: yyDollar = yyS[yypt-0 : yypt+1] -//line mysql_sql.y:8264 +//line mysql_sql.y:8267 { yyVAL.str = "" } case 1196: yyDollar = yyS[yypt-1 : yypt+1] -//line mysql_sql.y:8268 +//line mysql_sql.y:8271 { yyVAL.str = yyDollar[1].str } case 1197: yyDollar = yyS[yypt-2 : yypt+1] -//line mysql_sql.y:8272 +//line mysql_sql.y:8275 { yyVAL.str = yyDollar[2].str } case 1198: yyDollar = yyS[yypt-1 : yypt+1] -//line mysql_sql.y:8278 +//line mysql_sql.y:8281 { yyVAL.str = yylex.(*Lexer).GetDbOrTblName(yyDollar[1].cstrUnion().Origin()) } case 1199: yyDollar = yyS[yypt-1 : yypt+1] -//line mysql_sql.y:8282 +//line mysql_sql.y:8285 { yyVAL.str = yylex.(*Lexer).GetDbOrTblName(yyDollar[1].str) } case 1200: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL *tree.CStr -//line mysql_sql.y:8288 +//line mysql_sql.y:8291 { yyLOCAL = tree.NewCStr("", 1) } @@ -24373,7 +24434,7 @@ yydefault: case 1201: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL *tree.CStr -//line mysql_sql.y:8292 +//line mysql_sql.y:8295 { yyLOCAL = yyDollar[1].cstrUnion() } @@ -24381,7 +24442,7 @@ yydefault: case 1202: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL *tree.CStr -//line mysql_sql.y:8296 +//line mysql_sql.y:8299 { yyLOCAL = yyDollar[2].cstrUnion() } @@ -24389,7 +24450,7 @@ yydefault: case 1203: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL *tree.CStr -//line mysql_sql.y:8300 +//line mysql_sql.y:8303 { yyLOCAL = tree.NewCStr(yyDollar[1].str, 1) } @@ -24397,21 +24458,21 @@ yydefault: case 1204: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL *tree.CStr -//line mysql_sql.y:8304 +//line mysql_sql.y:8307 { yyLOCAL = tree.NewCStr(yyDollar[2].str, 1) } yyVAL.union = yyLOCAL case 1205: yyDollar = yyS[yypt-1 : yypt+1] -//line mysql_sql.y:8310 +//line mysql_sql.y:8313 { yyVAL.str = yyDollar[1].cstrUnion().Compare() } case 1229: yyDollar = yyS[yypt-6 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:8353 +//line mysql_sql.y:8356 { yyLOCAL = &tree.CreateIcebergCatalog{ IfNotExists: yyDollar[4].ifNotExistsUnion(), @@ -24423,7 +24484,7 @@ yydefault: case 1230: yyDollar = yyS[yypt-6 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:8363 +//line mysql_sql.y:8366 { yyLOCAL = &tree.CreateMongoDBConnection{ IfNotExists: yyDollar[4].ifNotExistsUnion(), @@ -24435,7 +24496,7 @@ yydefault: case 1231: yyDollar = yyS[yypt-10 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:8373 +//line mysql_sql.y:8376 { cronExpr := "" timezone := "" @@ -24458,7 +24519,7 @@ yydefault: case 1232: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL *tree.SQLTaskSchedule -//line mysql_sql.y:8393 +//line mysql_sql.y:8396 { yyLOCAL = nil } @@ -24466,7 +24527,7 @@ yydefault: case 1233: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL *tree.SQLTaskSchedule -//line mysql_sql.y:8397 +//line mysql_sql.y:8400 { yyLOCAL = &tree.SQLTaskSchedule{ CronExpr: yyDollar[2].str, @@ -24476,20 +24537,20 @@ yydefault: yyVAL.union = yyLOCAL case 1234: yyDollar = yyS[yypt-0 : yypt+1] -//line mysql_sql.y:8405 +//line mysql_sql.y:8408 { yyVAL.str = "" } case 1235: yyDollar = yyS[yypt-2 : yypt+1] -//line mysql_sql.y:8409 +//line mysql_sql.y:8412 { yyVAL.str = yyDollar[2].str } case 1236: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL tree.Expr -//line mysql_sql.y:8414 +//line mysql_sql.y:8417 { yyLOCAL = tree.Expr(nil) } @@ -24497,7 +24558,7 @@ yydefault: case 1237: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL tree.Expr -//line mysql_sql.y:8418 +//line mysql_sql.y:8421 { yyLOCAL = yyDollar[3].exprUnion() } @@ -24505,7 +24566,7 @@ yydefault: case 1238: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.Expr -//line mysql_sql.y:8424 +//line mysql_sql.y:8427 { yyLOCAL = yyDollar[1].exprUnion() } @@ -24513,7 +24574,7 @@ yydefault: case 1239: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.Expr -//line mysql_sql.y:8428 +//line mysql_sql.y:8431 { yyLOCAL = tree.NewSubquery(yyDollar[1].selectUnion(), false) } @@ -24521,7 +24582,7 @@ yydefault: case 1240: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL int64 -//line mysql_sql.y:8433 +//line mysql_sql.y:8436 { yyLOCAL = 0 } @@ -24529,27 +24590,27 @@ yydefault: case 1241: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL int64 -//line mysql_sql.y:8437 +//line mysql_sql.y:8440 { yyLOCAL = sqlTaskInt64(yyDollar[2].item) } yyVAL.union = yyLOCAL case 1242: yyDollar = yyS[yypt-0 : yypt+1] -//line mysql_sql.y:8442 +//line mysql_sql.y:8445 { yyVAL.str = "" } case 1243: yyDollar = yyS[yypt-2 : yypt+1] -//line mysql_sql.y:8446 +//line mysql_sql.y:8449 { yyVAL.str = yyDollar[2].str } case 1244: yyDollar = yyS[yypt-7 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:8452 +//line mysql_sql.y:8455 { var Language = yyDollar[3].str var Name = tree.Identifier(yyDollar[5].str) @@ -24563,20 +24624,20 @@ yydefault: yyVAL.union = yyLOCAL case 1245: yyDollar = yyS[yypt-1 : yypt+1] -//line mysql_sql.y:8465 +//line mysql_sql.y:8468 { yyVAL.str = yyDollar[1].cstrUnion().Compare() } case 1246: yyDollar = yyS[yypt-1 : yypt+1] -//line mysql_sql.y:8471 +//line mysql_sql.y:8474 { yyVAL.str = yyDollar[1].cstrUnion().Compare() } case 1247: yyDollar = yyS[yypt-9 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:8477 +//line mysql_sql.y:8480 { yyLOCAL = tree.NewCreateProcedure( yyDollar[2].sourceOptionalUnion(), yyDollar[4].procNameUnion(), yyDollar[6].procArgsUnion(), yyDollar[8].str, yyDollar[9].str, @@ -24586,7 +24647,7 @@ yydefault: case 1248: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL *tree.ProcedureName -//line mysql_sql.y:8485 +//line mysql_sql.y:8488 { prefix := tree.ObjectNamePrefix{ExplicitSchema: false} yyLOCAL = tree.NewProcedureName(tree.Identifier(yyDollar[1].cstrUnion().Compare()), prefix) @@ -24595,7 +24656,7 @@ yydefault: case 1249: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL *tree.ProcedureName -//line mysql_sql.y:8490 +//line mysql_sql.y:8493 { dbName := yylex.(*Lexer).GetDbOrTblName(yyDollar[1].cstrUnion().Origin()) prefix := tree.ObjectNamePrefix{SchemaName: tree.Identifier(dbName), ExplicitSchema: true} @@ -24605,7 +24666,7 @@ yydefault: case 1250: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL tree.ProcedureArgs -//line mysql_sql.y:8497 +//line mysql_sql.y:8500 { yyLOCAL = tree.ProcedureArgs(nil) } @@ -24613,7 +24674,7 @@ yydefault: case 1252: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.ProcedureArgs -//line mysql_sql.y:8504 +//line mysql_sql.y:8507 { yyLOCAL = tree.ProcedureArgs{yyDollar[1].procArgUnion()} } @@ -24621,7 +24682,7 @@ yydefault: case 1253: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL tree.ProcedureArgs -//line mysql_sql.y:8508 +//line mysql_sql.y:8511 { yyLOCAL = append(yyDollar[1].procArgsUnion(), yyDollar[3].procArgUnion()) } @@ -24629,7 +24690,7 @@ yydefault: case 1254: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.ProcedureArg -//line mysql_sql.y:8514 +//line mysql_sql.y:8517 { yyLOCAL = tree.ProcedureArg(yyDollar[1].procArgDeclUnion()) } @@ -24637,7 +24698,7 @@ yydefault: case 1255: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL *tree.ProcedureArgDecl -//line mysql_sql.y:8520 +//line mysql_sql.y:8523 { yyLOCAL = tree.NewProcedureArgDecl(yyDollar[1].procArgTypeUnion(), yyDollar[2].unresolvedNameUnion(), yyDollar[3].columnTypeUnion()) } @@ -24645,7 +24706,7 @@ yydefault: case 1256: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL tree.InOutArgType -//line mysql_sql.y:8525 +//line mysql_sql.y:8528 { yyLOCAL = tree.TYPE_IN } @@ -24653,7 +24714,7 @@ yydefault: case 1257: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.InOutArgType -//line mysql_sql.y:8529 +//line mysql_sql.y:8532 { yyLOCAL = tree.TYPE_IN } @@ -24661,7 +24722,7 @@ yydefault: case 1258: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.InOutArgType -//line mysql_sql.y:8533 +//line mysql_sql.y:8536 { yyLOCAL = tree.TYPE_OUT } @@ -24669,27 +24730,27 @@ yydefault: case 1259: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.InOutArgType -//line mysql_sql.y:8537 +//line mysql_sql.y:8540 { yyLOCAL = tree.TYPE_INOUT } yyVAL.union = yyLOCAL case 1260: yyDollar = yyS[yypt-0 : yypt+1] -//line mysql_sql.y:8542 +//line mysql_sql.y:8545 { yyVAL.str = "sql" } case 1261: yyDollar = yyS[yypt-2 : yypt+1] -//line mysql_sql.y:8546 +//line mysql_sql.y:8549 { yyVAL.str = yyDollar[2].str } case 1262: - yyDollar = yyS[yypt-14 : yypt+1] + yyDollar = yyS[yypt-15 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:8552 +//line mysql_sql.y:8555 { if yyDollar[13].str == "" { yylex.Error("no function body error") @@ -24708,8 +24769,9 @@ yydefault: var Import = yyDollar[12].boolValUnion() var Body = yyDollar[13].str var Handler = yyDollar[14].str + var Options = yyDollar[15].pythonFunctionOptionsUnion() - yyLOCAL = tree.NewCreateFunction( + node := tree.NewCreateFunction( Replace, Name, Args, @@ -24719,12 +24781,15 @@ yydefault: Body, Handler, ) + node.Mode = Options.Mode + node.NullPolicy = Options.NullPolicy + yyLOCAL = node } yyVAL.union = yyLOCAL case 1263: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL *tree.FunctionName -//line mysql_sql.y:8585 +//line mysql_sql.y:8592 { prefix := tree.ObjectNamePrefix{ExplicitSchema: false} yyLOCAL = tree.NewFuncName(tree.Identifier(yyDollar[1].cstrUnion().Compare()), prefix) @@ -24733,7 +24798,7 @@ yydefault: case 1264: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL *tree.FunctionName -//line mysql_sql.y:8590 +//line mysql_sql.y:8597 { dbName := yylex.(*Lexer).GetDbOrTblName(yyDollar[1].cstrUnion().Origin()) prefix := tree.ObjectNamePrefix{SchemaName: tree.Identifier(dbName), ExplicitSchema: true} @@ -24743,7 +24808,7 @@ yydefault: case 1265: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL tree.FunctionArgs -//line mysql_sql.y:8597 +//line mysql_sql.y:8604 { yyLOCAL = tree.FunctionArgs(nil) } @@ -24751,7 +24816,7 @@ yydefault: case 1267: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.FunctionArgs -//line mysql_sql.y:8604 +//line mysql_sql.y:8611 { yyLOCAL = tree.FunctionArgs{yyDollar[1].funcArgUnion()} } @@ -24759,7 +24824,7 @@ yydefault: case 1268: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL tree.FunctionArgs -//line mysql_sql.y:8608 +//line mysql_sql.y:8615 { yyLOCAL = append(yyDollar[1].funcArgsUnion(), yyDollar[3].funcArgUnion()) } @@ -24767,7 +24832,7 @@ yydefault: case 1269: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.FunctionArg -//line mysql_sql.y:8614 +//line mysql_sql.y:8621 { yyLOCAL = tree.FunctionArg(yyDollar[1].funcArgDeclUnion()) } @@ -24775,7 +24840,7 @@ yydefault: case 1270: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL *tree.FunctionArgDecl -//line mysql_sql.y:8620 +//line mysql_sql.y:8627 { yyLOCAL = tree.NewFunctionArgDecl(nil, yyDollar[1].columnTypeUnion(), nil) } @@ -24783,7 +24848,7 @@ yydefault: case 1271: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL *tree.FunctionArgDecl -//line mysql_sql.y:8624 +//line mysql_sql.y:8631 { yyLOCAL = tree.NewFunctionArgDecl(yyDollar[1].unresolvedNameUnion(), yyDollar[2].columnTypeUnion(), nil) } @@ -24791,21 +24856,21 @@ yydefault: case 1272: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL *tree.FunctionArgDecl -//line mysql_sql.y:8628 +//line mysql_sql.y:8635 { yyLOCAL = tree.NewFunctionArgDecl(yyDollar[1].unresolvedNameUnion(), yyDollar[2].columnTypeUnion(), yyDollar[4].exprUnion()) } yyVAL.union = yyLOCAL case 1273: yyDollar = yyS[yypt-1 : yypt+1] -//line mysql_sql.y:8634 +//line mysql_sql.y:8641 { yyVAL.str = yyDollar[1].cstrUnion().Compare() } case 1274: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL *tree.ReturnType -//line mysql_sql.y:8640 +//line mysql_sql.y:8647 { yyLOCAL = tree.NewReturnType(yyDollar[1].columnTypeUnion()) } @@ -24813,7 +24878,7 @@ yydefault: case 1275: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL bool -//line mysql_sql.y:8646 +//line mysql_sql.y:8653 { yyLOCAL = false } @@ -24821,27 +24886,91 @@ yydefault: case 1276: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL bool -//line mysql_sql.y:8650 +//line mysql_sql.y:8657 { yyLOCAL = true } yyVAL.union = yyLOCAL case 1277: yyDollar = yyS[yypt-0 : yypt+1] -//line mysql_sql.y:8655 +//line mysql_sql.y:8662 { yyVAL.str = "" } case 1279: yyDollar = yyS[yypt-2 : yypt+1] -//line mysql_sql.y:8662 +//line mysql_sql.y:8669 { yyVAL.str = yyDollar[2].str } case 1280: + yyDollar = yyS[yypt-0 : yypt+1] + var yyLOCAL tree.PythonFunctionOptions +//line mysql_sql.y:8674 + { + yyLOCAL = tree.PythonFunctionOptions{} + } + yyVAL.union = yyLOCAL + case 1281: + yyDollar = yyS[yypt-2 : yypt+1] + var yyLOCAL tree.PythonFunctionOptions +//line mysql_sql.y:8678 + { + yyLOCAL = tree.PythonFunctionOptions{Mode: yyDollar[2].str} + } + yyVAL.union = yyLOCAL + case 1282: + yyDollar = yyS[yypt-1 : yypt+1] + var yyLOCAL tree.PythonFunctionOptions +//line mysql_sql.y:8682 + { + yyLOCAL = tree.PythonFunctionOptions{NullPolicy: yyDollar[1].str} + } + yyVAL.union = yyLOCAL + case 1283: + yyDollar = yyS[yypt-3 : yypt+1] + var yyLOCAL tree.PythonFunctionOptions +//line mysql_sql.y:8686 + { + yyLOCAL = tree.PythonFunctionOptions{Mode: yyDollar[2].str, NullPolicy: yyDollar[3].str} + } + yyVAL.union = yyLOCAL + case 1284: + yyDollar = yyS[yypt-3 : yypt+1] + var yyLOCAL tree.PythonFunctionOptions +//line mysql_sql.y:8690 + { + yyLOCAL = tree.PythonFunctionOptions{Mode: yyDollar[3].str, NullPolicy: yyDollar[1].str} + } + yyVAL.union = yyLOCAL + case 1285: + yyDollar = yyS[yypt-1 : yypt+1] +//line mysql_sql.y:8696 + { + yyVAL.str = "SCALAR" + } + case 1286: + yyDollar = yyS[yypt-1 : yypt+1] +//line mysql_sql.y:8700 + { + yyVAL.str = "VECTOR" + } + case 1287: + yyDollar = yyS[yypt-4 : yypt+1] +//line mysql_sql.y:8706 + { + yyVAL.str = "CALLED_ON_NULL_INPUT" + } + case 1288: + yyDollar = yyS[yypt-5 : yypt+1] +//line mysql_sql.y:8710 + { + yyVAL.str = "RETURNS_NULL_ON_NULL_INPUT" + } + case 1289: yyDollar = yyS[yypt-9 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:8668 +//line mysql_sql.y:8716 { var Replace bool var Name = yyDollar[5].tableNameUnion() @@ -24861,10 +24990,10 @@ yydefault: ) } yyVAL.union = yyLOCAL - case 1281: + case 1290: yyDollar = yyS[yypt-9 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:8687 +//line mysql_sql.y:8735 { var Replace = yyDollar[2].sourceOptionalUnion() var Name = yyDollar[5].tableNameUnion() @@ -24884,10 +25013,10 @@ yydefault: ) } yyVAL.union = yyLOCAL - case 1282: + case 1291: yyDollar = yyS[yypt-7 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:8708 +//line mysql_sql.y:8756 { var IfNotExists = yyDollar[3].ifNotExistsUnion() var Name = yyDollar[4].exprUnion() @@ -24903,10 +25032,10 @@ yydefault: ) } yyVAL.union = yyLOCAL - case 1283: + case 1292: yyDollar = yyS[yypt-8 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:8723 +//line mysql_sql.y:8771 { var FromUri = yyDollar[4].str var SubscriptionAccountName = yyDollar[5].cstrUnion().Compare() @@ -24924,81 +25053,81 @@ yydefault: yyLOCAL = cs } yyVAL.union = yyLOCAL - case 1284: + case 1293: yyDollar = yyS[yypt-1 : yypt+1] -//line mysql_sql.y:8742 +//line mysql_sql.y:8790 { yyVAL.str = yyDollar[1].str } - case 1285: + case 1294: yyDollar = yyS[yypt-2 : yypt+1] -//line mysql_sql.y:8746 +//line mysql_sql.y:8794 { yyVAL.str = yyVAL.str + yyDollar[2].str } - case 1286: + case 1295: yyDollar = yyS[yypt-3 : yypt+1] -//line mysql_sql.y:8752 +//line mysql_sql.y:8800 { yyVAL.str = "ALGORITHM = " + yyDollar[3].str } - case 1287: + case 1296: yyDollar = yyS[yypt-3 : yypt+1] -//line mysql_sql.y:8756 +//line mysql_sql.y:8804 { yyVAL.str = "DEFINER = " } - case 1288: + case 1297: yyDollar = yyS[yypt-3 : yypt+1] -//line mysql_sql.y:8760 +//line mysql_sql.y:8808 { yyVAL.str = "SQL SECURITY " + yyDollar[3].str } - case 1289: + case 1298: yyDollar = yyS[yypt-0 : yypt+1] -//line mysql_sql.y:8765 +//line mysql_sql.y:8813 { yyVAL.str = "" } - case 1290: + case 1299: yyDollar = yyS[yypt-4 : yypt+1] -//line mysql_sql.y:8769 +//line mysql_sql.y:8817 { yyVAL.str = "WITH " + yyDollar[2].str + " CHECK OPTION" } - case 1296: + case 1305: yyDollar = yyS[yypt-0 : yypt+1] -//line mysql_sql.y:8783 +//line mysql_sql.y:8831 { yyVAL.str = "" } - case 1299: + case 1308: yyDollar = yyS[yypt-1 : yypt+1] -//line mysql_sql.y:8791 +//line mysql_sql.y:8839 { yyVAL.str = yyDollar[1].cstrUnion().Compare() } - case 1300: + case 1309: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.Expr -//line mysql_sql.y:8797 +//line mysql_sql.y:8845 { var str = yyDollar[1].cstrUnion().Compare() yyLOCAL = tree.NewNumVal(str, str, false, tree.P_char) } yyVAL.union = yyLOCAL - case 1301: + case 1310: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.Expr -//line mysql_sql.y:8802 +//line mysql_sql.y:8850 { yyLOCAL = tree.NewParamExpr(yylex.(*Lexer).GetParamIndex()) } yyVAL.union = yyLOCAL - case 1302: + case 1311: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL tree.AccountAuthOption -//line mysql_sql.y:8808 +//line mysql_sql.y:8856 { var Equal = yyDollar[2].str var AdminName = yyDollar[3].exprUnion() @@ -25010,36 +25139,36 @@ yydefault: ) } yyVAL.union = yyLOCAL - case 1303: + case 1312: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.Expr -//line mysql_sql.y:8821 +//line mysql_sql.y:8869 { var str = yyDollar[1].str yyLOCAL = tree.NewNumVal(str, str, false, tree.P_char) } yyVAL.union = yyLOCAL - case 1304: + case 1313: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.Expr -//line mysql_sql.y:8826 +//line mysql_sql.y:8874 { var str = yyDollar[1].cstrUnion().Compare() yyLOCAL = tree.NewNumVal(str, str, false, tree.P_char) } yyVAL.union = yyLOCAL - case 1305: + case 1314: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.Expr -//line mysql_sql.y:8831 +//line mysql_sql.y:8879 { yyLOCAL = tree.NewParamExpr(yylex.(*Lexer).GetParamIndex()) } yyVAL.union = yyLOCAL - case 1306: + case 1315: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL tree.AccountIdentified -//line mysql_sql.y:8837 +//line mysql_sql.y:8885 { yyLOCAL = *tree.NewAccountIdentified( tree.AccountIdentifiedByPassword, @@ -25047,10 +25176,10 @@ yydefault: ) } yyVAL.union = yyLOCAL - case 1307: + case 1316: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL tree.AccountIdentified -//line mysql_sql.y:8844 +//line mysql_sql.y:8892 { yyLOCAL = *tree.NewAccountIdentified( tree.AccountIdentifiedByPassword, @@ -25058,10 +25187,10 @@ yydefault: ) } yyVAL.union = yyLOCAL - case 1308: + case 1317: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL tree.AccountIdentified -//line mysql_sql.y:8851 +//line mysql_sql.y:8899 { yyLOCAL = *tree.NewAccountIdentified( tree.AccountIdentifiedByRandomPassword, @@ -25069,10 +25198,10 @@ yydefault: ) } yyVAL.union = yyLOCAL - case 1309: + case 1318: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL tree.AccountIdentified -//line mysql_sql.y:8858 +//line mysql_sql.y:8906 { yyLOCAL = *tree.NewAccountIdentified( tree.AccountIdentifiedWithSSL, @@ -25080,10 +25209,10 @@ yydefault: ) } yyVAL.union = yyLOCAL - case 1310: + case 1319: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL tree.AccountIdentified -//line mysql_sql.y:8865 +//line mysql_sql.y:8913 { yyLOCAL = *tree.NewAccountIdentified( tree.AccountIdentifiedWithSSL, @@ -25091,20 +25220,20 @@ yydefault: ) } yyVAL.union = yyLOCAL - case 1311: + case 1320: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL tree.AccountStatus -//line mysql_sql.y:8873 +//line mysql_sql.y:8921 { as := tree.NewAccountStatus() as.Exist = false yyLOCAL = *as } yyVAL.union = yyLOCAL - case 1312: + case 1321: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.AccountStatus -//line mysql_sql.y:8879 +//line mysql_sql.y:8927 { as := tree.NewAccountStatus() as.Exist = true @@ -25112,10 +25241,10 @@ yydefault: yyLOCAL = *as } yyVAL.union = yyLOCAL - case 1313: + case 1322: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.AccountStatus -//line mysql_sql.y:8886 +//line mysql_sql.y:8934 { as := tree.NewAccountStatus() as.Exist = true @@ -25123,10 +25252,10 @@ yydefault: yyLOCAL = *as } yyVAL.union = yyLOCAL - case 1314: + case 1323: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.AccountStatus -//line mysql_sql.y:8893 +//line mysql_sql.y:8941 { as := tree.NewAccountStatus() as.Exist = true @@ -25134,20 +25263,20 @@ yydefault: yyLOCAL = *as } yyVAL.union = yyLOCAL - case 1315: + case 1324: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL tree.AccountComment -//line mysql_sql.y:8901 +//line mysql_sql.y:8949 { ac := tree.NewAccountComment() ac.Exist = false yyLOCAL = *ac } yyVAL.union = yyLOCAL - case 1316: + case 1325: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL tree.AccountComment -//line mysql_sql.y:8907 +//line mysql_sql.y:8955 { ac := tree.NewAccountComment() ac.Exist = true @@ -25155,10 +25284,10 @@ yydefault: yyLOCAL = *ac } yyVAL.union = yyLOCAL - case 1317: + case 1326: yyDollar = yyS[yypt-7 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:8916 +//line mysql_sql.y:8964 { var IfNotExists = yyDollar[3].ifNotExistsUnion() var Users = yyDollar[4].usersUnion() @@ -25174,10 +25303,10 @@ yydefault: ) } yyVAL.union = yyLOCAL - case 1318: + case 1327: yyDollar = yyS[yypt-8 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:8933 +//line mysql_sql.y:8981 { var IfNotExists = yyDollar[3].ifNotExistsUnion() var Name = tree.Identifier(yyDollar[4].cstrUnion().Compare()) @@ -25194,10 +25323,10 @@ yydefault: ) } yyVAL.union = yyLOCAL - case 1319: + case 1328: yyDollar = yyS[yypt-10 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:8949 +//line mysql_sql.y:8997 { var IfNotExists = yyDollar[3].ifNotExistsUnion() var Name = tree.Identifier(yyDollar[4].cstrUnion().Compare()) @@ -25215,10 +25344,10 @@ yydefault: ) } yyVAL.union = yyLOCAL - case 1320: + case 1329: yyDollar = yyS[yypt-8 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:8966 +//line mysql_sql.y:9014 { var IfNotExists = yyDollar[3].ifNotExistsUnion() var Name = tree.Identifier(yyDollar[4].cstrUnion().Compare()) @@ -25235,30 +25364,30 @@ yydefault: ) } yyVAL.union = yyLOCAL - case 1321: + case 1330: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL *tree.AccountsSetOption -//line mysql_sql.y:8984 +//line mysql_sql.y:9032 { yyLOCAL = &tree.AccountsSetOption{ All: true, } } yyVAL.union = yyLOCAL - case 1322: + case 1331: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL *tree.AccountsSetOption -//line mysql_sql.y:8990 +//line mysql_sql.y:9038 { yyLOCAL = &tree.AccountsSetOption{ SetAccounts: yyDollar[2].identifierListUnion(), } } yyVAL.union = yyLOCAL - case 1323: + case 1332: yyDollar = yyS[yypt-8 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:8998 +//line mysql_sql.y:9046 { var IfNotExists = yyDollar[3].ifNotExistsUnion() var Name = tree.Identifier(yyDollar[4].cstrUnion().Compare()) @@ -25276,20 +25405,20 @@ yydefault: ) } yyVAL.union = yyLOCAL - case 1324: + case 1333: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL tree.StageStatus -//line mysql_sql.y:9016 +//line mysql_sql.y:9064 { yyLOCAL = tree.StageStatus{ Exist: false, } } yyVAL.union = yyLOCAL - case 1325: + case 1334: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL tree.StageStatus -//line mysql_sql.y:9022 +//line mysql_sql.y:9070 { yyLOCAL = tree.StageStatus{ Exist: true, @@ -25297,10 +25426,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 1326: + case 1335: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL tree.StageStatus -//line mysql_sql.y:9029 +//line mysql_sql.y:9077 { yyLOCAL = tree.StageStatus{ Exist: true, @@ -25308,20 +25437,20 @@ yydefault: } } yyVAL.union = yyLOCAL - case 1327: + case 1336: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL tree.StageComment -//line mysql_sql.y:9037 +//line mysql_sql.y:9085 { yyLOCAL = tree.StageComment{ Exist: false, } } yyVAL.union = yyLOCAL - case 1328: + case 1337: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL tree.StageComment -//line mysql_sql.y:9043 +//line mysql_sql.y:9091 { yyLOCAL = tree.StageComment{ Exist: true, @@ -25329,18 +25458,18 @@ yydefault: } } yyVAL.union = yyLOCAL - case 1329: + case 1338: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL int64 -//line mysql_sql.y:9052 +//line mysql_sql.y:9100 { yyLOCAL = int64(0) } yyVAL.union = yyLOCAL - case 1330: + case 1339: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL int64 -//line mysql_sql.y:9056 +//line mysql_sql.y:9104 { switch v := yyDollar[3].item.(type) { case int64: @@ -25352,20 +25481,20 @@ yydefault: } } yyVAL.union = yyLOCAL - case 1331: + case 1340: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL tree.StageUrl -//line mysql_sql.y:9068 +//line mysql_sql.y:9116 { yyLOCAL = tree.StageUrl{ Exist: false, } } yyVAL.union = yyLOCAL - case 1332: + case 1341: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL tree.StageUrl -//line mysql_sql.y:9074 +//line mysql_sql.y:9122 { yyLOCAL = tree.StageUrl{ Exist: true, @@ -25373,20 +25502,20 @@ yydefault: } } yyVAL.union = yyLOCAL - case 1333: + case 1342: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL tree.StageCredentials -//line mysql_sql.y:9082 +//line mysql_sql.y:9130 { yyLOCAL = tree.StageCredentials{ Exist: false, } } yyVAL.union = yyLOCAL - case 1334: + case 1343: yyDollar = yyS[yypt-5 : yypt+1] var yyLOCAL tree.StageCredentials -//line mysql_sql.y:9088 +//line mysql_sql.y:9136 { yyLOCAL = tree.StageCredentials{ Exist: true, @@ -25394,61 +25523,61 @@ yydefault: } } yyVAL.union = yyLOCAL - case 1335: + case 1344: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL []string -//line mysql_sql.y:9097 +//line mysql_sql.y:9145 { yyLOCAL = yyDollar[1].strsUnion() } yyVAL.union = yyLOCAL - case 1336: + case 1345: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL []string -//line mysql_sql.y:9101 +//line mysql_sql.y:9149 { yyLOCAL = append(yyDollar[1].strsUnion(), yyDollar[3].strsUnion()...) } yyVAL.union = yyLOCAL - case 1337: + case 1346: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL []string -//line mysql_sql.y:9106 +//line mysql_sql.y:9154 { yyLOCAL = []string{} } yyVAL.union = yyLOCAL - case 1338: + case 1347: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL []string -//line mysql_sql.y:9110 +//line mysql_sql.y:9158 { yyLOCAL = append(yyLOCAL, yyDollar[1].str) yyLOCAL = append(yyLOCAL, yyDollar[3].str) } yyVAL.union = yyLOCAL - case 1339: + case 1348: yyDollar = yyS[yypt-3 : yypt+1] -//line mysql_sql.y:9117 +//line mysql_sql.y:9165 { yyVAL.str = yyDollar[3].str } - case 1340: + case 1349: yyDollar = yyS[yypt-0 : yypt+1] -//line mysql_sql.y:9122 +//line mysql_sql.y:9170 { yyVAL.str = "" } - case 1341: + case 1350: yyDollar = yyS[yypt-2 : yypt+1] -//line mysql_sql.y:9126 +//line mysql_sql.y:9174 { yyVAL.str = yyDollar[2].str } - case 1342: + case 1351: yyDollar = yyS[yypt-9 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:9132 +//line mysql_sql.y:9180 { var ifNotExists = yyDollar[3].boolValUnion() var name = tree.Identifier(yyDollar[4].cstrUnion().Compare()) @@ -25459,10 +25588,10 @@ yydefault: yyLOCAL = tree.NewAlterStage(ifNotExists, name, urlOption, credentialsOption, statusOption, comment) } yyVAL.union = yyLOCAL - case 1343: + case 1352: yyDollar = yyS[yypt-8 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:9144 +//line mysql_sql.y:9192 { var ifExists = yyDollar[3].boolValUnion() var name = tree.Identifier(yyDollar[4].cstrUnion().Compare()) @@ -25473,154 +25602,154 @@ yydefault: yyLOCAL = tree.NewAlterPublication(ifExists, name, accountsSet, dbName, table, comment) } yyVAL.union = yyLOCAL - case 1344: + case 1353: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL *tree.AccountsSetOption -//line mysql_sql.y:9155 +//line mysql_sql.y:9203 { yyLOCAL = nil } yyVAL.union = yyLOCAL - case 1345: + case 1354: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL *tree.AccountsSetOption -//line mysql_sql.y:9159 +//line mysql_sql.y:9207 { yyLOCAL = &tree.AccountsSetOption{ All: true, } } yyVAL.union = yyLOCAL - case 1346: + case 1355: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL *tree.AccountsSetOption -//line mysql_sql.y:9165 +//line mysql_sql.y:9213 { yyLOCAL = &tree.AccountsSetOption{ SetAccounts: yyDollar[2].identifierListUnion(), } } yyVAL.union = yyLOCAL - case 1347: + case 1356: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL *tree.AccountsSetOption -//line mysql_sql.y:9171 +//line mysql_sql.y:9219 { yyLOCAL = &tree.AccountsSetOption{ AddAccounts: yyDollar[3].identifierListUnion(), } } yyVAL.union = yyLOCAL - case 1348: + case 1357: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL *tree.AccountsSetOption -//line mysql_sql.y:9177 +//line mysql_sql.y:9225 { yyLOCAL = &tree.AccountsSetOption{ DropAccounts: yyDollar[3].identifierListUnion(), } } yyVAL.union = yyLOCAL - case 1349: + case 1358: yyDollar = yyS[yypt-0 : yypt+1] -//line mysql_sql.y:9184 +//line mysql_sql.y:9232 { yyVAL.str = "" } - case 1350: + case 1359: yyDollar = yyS[yypt-2 : yypt+1] -//line mysql_sql.y:9188 +//line mysql_sql.y:9236 { yyVAL.str = yyDollar[2].str } - case 1351: + case 1360: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL tree.TableNames -//line mysql_sql.y:9193 +//line mysql_sql.y:9241 { yyLOCAL = nil } yyVAL.union = yyLOCAL - case 1352: + case 1361: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL tree.TableNames -//line mysql_sql.y:9197 +//line mysql_sql.y:9245 { yyLOCAL = yyDollar[2].tableNamesUnion() } yyVAL.union = yyLOCAL - case 1353: + case 1362: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:9203 +//line mysql_sql.y:9251 { var ifExists = yyDollar[3].boolValUnion() var name = tree.Identifier(yyDollar[4].cstrUnion().Compare()) yyLOCAL = tree.NewDropPublication(ifExists, name) } yyVAL.union = yyLOCAL - case 1354: + case 1363: yyDollar = yyS[yypt-5 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:9211 +//line mysql_sql.y:9259 { var ifExists = yyDollar[4].boolValUnion() var taskID = yyDollar[5].str yyLOCAL = tree.NewDropCcprSubscription(ifExists, taskID) } yyVAL.union = yyLOCAL - case 1355: + case 1364: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:9219 +//line mysql_sql.y:9267 { var taskID = yyDollar[4].str yyLOCAL = tree.NewResumeCcprSubscription(taskID) } yyVAL.union = yyLOCAL - case 1356: + case 1365: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:9226 +//line mysql_sql.y:9274 { var taskID = yyDollar[4].str yyLOCAL = tree.NewPauseCcprSubscription(taskID) } yyVAL.union = yyLOCAL - case 1357: + case 1366: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:9233 +//line mysql_sql.y:9281 { var ifNotExists = yyDollar[3].boolValUnion() var name = tree.Identifier(yyDollar[4].cstrUnion().Compare()) yyLOCAL = tree.NewDropStage(ifNotExists, name) } yyVAL.union = yyLOCAL - case 1358: + case 1367: yyDollar = yyS[yypt-6 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:9241 +//line mysql_sql.y:9289 { var ifExists = yyDollar[5].boolValUnion() var path = yyDollar[6].str yyLOCAL = tree.NewRemoveStageFiles(ifExists, path) } yyVAL.union = yyLOCAL - case 1359: + case 1368: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:9249 +//line mysql_sql.y:9297 { var ifExists = yyDollar[3].boolValUnion() var name = tree.Identifier(yyDollar[4].cstrUnion().Compare()) yyLOCAL = tree.NewDropSnapShot(ifExists, name, "", "") } yyVAL.union = yyLOCAL - case 1360: + case 1369: yyDollar = yyS[yypt-8 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:9255 +//line mysql_sql.y:9303 { var ifExists = yyDollar[3].boolValUnion() var name = tree.Identifier(yyDollar[4].cstrUnion().Compare()) @@ -25629,10 +25758,10 @@ yydefault: yyLOCAL = tree.NewDropSnapShot(ifExists, name, accountName, pubName) } yyVAL.union = yyLOCAL - case 1361: + case 1370: yyDollar = yyS[yypt-5 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:9265 +//line mysql_sql.y:9313 { var ifExists = yyDollar[3].boolValUnion() var name = tree.Identifier(yyDollar[4].cstrUnion().Compare()) @@ -25644,16 +25773,16 @@ yydefault: } yyVAL.union = yyLOCAL - case 1362: + case 1371: yyDollar = yyS[yypt-1 : yypt+1] -//line mysql_sql.y:9278 +//line mysql_sql.y:9326 { yyVAL.str = yyDollar[1].cstrUnion().Compare() } - case 1363: + case 1372: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL tree.AccountCommentOrAttribute -//line mysql_sql.y:9283 +//line mysql_sql.y:9331 { var Exist = false var IsComment bool @@ -25666,10 +25795,10 @@ yydefault: } yyVAL.union = yyLOCAL - case 1364: + case 1373: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL tree.AccountCommentOrAttribute -//line mysql_sql.y:9295 +//line mysql_sql.y:9343 { var Exist = true var IsComment = true @@ -25681,10 +25810,10 @@ yydefault: ) } yyVAL.union = yyLOCAL - case 1365: + case 1374: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL tree.AccountCommentOrAttribute -//line mysql_sql.y:9306 +//line mysql_sql.y:9354 { var Exist = true var IsComment = false @@ -25696,26 +25825,26 @@ yydefault: ) } yyVAL.union = yyLOCAL - case 1366: + case 1375: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL []*tree.User -//line mysql_sql.y:9414 +//line mysql_sql.y:9462 { yyLOCAL = []*tree.User{yyDollar[1].userUnion()} } yyVAL.union = yyLOCAL - case 1367: + case 1376: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL []*tree.User -//line mysql_sql.y:9418 +//line mysql_sql.y:9466 { yyLOCAL = append(yyDollar[1].usersUnion(), yyDollar[3].userUnion()) } yyVAL.union = yyLOCAL - case 1368: + case 1377: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL *tree.User -//line mysql_sql.y:9424 +//line mysql_sql.y:9472 { var Username = yyDollar[1].usernameRecordUnion().Username var Hostname = yyDollar[1].usernameRecordUnion().Hostname @@ -25727,26 +25856,26 @@ yydefault: ) } yyVAL.union = yyLOCAL - case 1369: + case 1378: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL []*tree.User -//line mysql_sql.y:9437 +//line mysql_sql.y:9485 { yyLOCAL = []*tree.User{yyDollar[1].userUnion()} } yyVAL.union = yyLOCAL - case 1370: + case 1379: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL []*tree.User -//line mysql_sql.y:9441 +//line mysql_sql.y:9489 { yyLOCAL = append(yyDollar[1].usersUnion(), yyDollar[3].userUnion()) } yyVAL.union = yyLOCAL - case 1371: + case 1380: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL *tree.User -//line mysql_sql.y:9447 +//line mysql_sql.y:9495 { var Username = yyDollar[1].usernameRecordUnion().Username var Hostname = yyDollar[1].usernameRecordUnion().Hostname @@ -25758,50 +25887,50 @@ yydefault: ) } yyVAL.union = yyLOCAL - case 1372: + case 1381: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL *tree.UsernameRecord -//line mysql_sql.y:9460 +//line mysql_sql.y:9508 { yyLOCAL = &tree.UsernameRecord{Username: yyDollar[1].str, Hostname: "%"} } yyVAL.union = yyLOCAL - case 1373: + case 1382: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL *tree.UsernameRecord -//line mysql_sql.y:9464 +//line mysql_sql.y:9512 { yyLOCAL = &tree.UsernameRecord{Username: yyDollar[1].str, Hostname: yyDollar[3].str} } yyVAL.union = yyLOCAL - case 1374: + case 1383: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL *tree.UsernameRecord -//line mysql_sql.y:9468 +//line mysql_sql.y:9516 { yyLOCAL = &tree.UsernameRecord{Username: yyDollar[1].str, Hostname: yyDollar[2].str} } yyVAL.union = yyLOCAL - case 1375: + case 1384: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL *tree.AccountIdentified -//line mysql_sql.y:9473 +//line mysql_sql.y:9521 { yyLOCAL = nil } yyVAL.union = yyLOCAL - case 1376: + case 1385: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL *tree.AccountIdentified -//line mysql_sql.y:9477 +//line mysql_sql.y:9525 { yyLOCAL = yyDollar[1].userIdentifiedUnion() } yyVAL.union = yyLOCAL - case 1377: + case 1386: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL *tree.AccountIdentified -//line mysql_sql.y:9483 +//line mysql_sql.y:9531 { yyLOCAL = &tree.AccountIdentified{ Typ: tree.AccountIdentifiedByPassword, @@ -25809,20 +25938,20 @@ yydefault: } } yyVAL.union = yyLOCAL - case 1378: + case 1387: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL *tree.AccountIdentified -//line mysql_sql.y:9490 +//line mysql_sql.y:9538 { yyLOCAL = &tree.AccountIdentified{ Typ: tree.AccountIdentifiedByRandomPassword, } } yyVAL.union = yyLOCAL - case 1379: + case 1388: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL *tree.AccountIdentified -//line mysql_sql.y:9496 +//line mysql_sql.y:9544 { yyLOCAL = &tree.AccountIdentified{ Typ: tree.AccountIdentifiedWithSSL, @@ -25830,16 +25959,16 @@ yydefault: } } yyVAL.union = yyLOCAL - case 1380: + case 1389: yyDollar = yyS[yypt-1 : yypt+1] -//line mysql_sql.y:9505 +//line mysql_sql.y:9553 { yyVAL.str = yyDollar[1].cstrUnion().Compare() } - case 1382: + case 1391: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:9512 +//line mysql_sql.y:9560 { var IfNotExists = yyDollar[3].ifNotExistsUnion() var Roles = yyDollar[4].rolesUnion() @@ -25849,26 +25978,26 @@ yydefault: ) } yyVAL.union = yyLOCAL - case 1383: + case 1392: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL []*tree.Role -//line mysql_sql.y:9523 +//line mysql_sql.y:9571 { yyLOCAL = []*tree.Role{yyDollar[1].roleUnion()} } yyVAL.union = yyLOCAL - case 1384: + case 1393: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL []*tree.Role -//line mysql_sql.y:9527 +//line mysql_sql.y:9575 { yyLOCAL = append(yyDollar[1].rolesUnion(), yyDollar[3].roleUnion()) } yyVAL.union = yyLOCAL - case 1385: + case 1394: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL *tree.Role -//line mysql_sql.y:9533 +//line mysql_sql.y:9581 { var UserName = yyDollar[1].cstrUnion().Compare() yyLOCAL = tree.NewRole( @@ -25876,114 +26005,114 @@ yydefault: ) } yyVAL.union = yyLOCAL - case 1386: + case 1395: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL *tree.CStr -//line mysql_sql.y:9542 +//line mysql_sql.y:9590 { yyLOCAL = tree.NewCStr(yyDollar[1].str, 1) } yyVAL.union = yyLOCAL - case 1387: + case 1396: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL *tree.CStr -//line mysql_sql.y:9546 +//line mysql_sql.y:9594 { yyLOCAL = tree.NewCStr(yyDollar[1].str, 1) } yyVAL.union = yyLOCAL - case 1388: + case 1397: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL *tree.CStr -//line mysql_sql.y:9550 +//line mysql_sql.y:9598 { yyLOCAL = tree.NewCStr(yyDollar[1].str, 1) } yyVAL.union = yyLOCAL - case 1389: + case 1398: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL *tree.CStr -//line mysql_sql.y:9554 +//line mysql_sql.y:9602 { yyLOCAL = tree.NewCStr("lag", 1) } yyVAL.union = yyLOCAL - case 1390: + case 1399: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL *tree.CStr -//line mysql_sql.y:9558 +//line mysql_sql.y:9606 { yyLOCAL = tree.NewCStr("lead", 1) } yyVAL.union = yyLOCAL - case 1391: + case 1400: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL *tree.CStr -//line mysql_sql.y:9562 +//line mysql_sql.y:9610 { yyLOCAL = tree.NewCStr("first_value", 1) } yyVAL.union = yyLOCAL - case 1392: + case 1401: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL *tree.CStr -//line mysql_sql.y:9566 +//line mysql_sql.y:9614 { yyLOCAL = tree.NewCStr("last_value", 1) } yyVAL.union = yyLOCAL - case 1393: + case 1402: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL *tree.CStr -//line mysql_sql.y:9570 +//line mysql_sql.y:9618 { yyLOCAL = tree.NewCStr("nth_value", 1) } yyVAL.union = yyLOCAL - case 1394: + case 1403: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL tree.IndexCategory -//line mysql_sql.y:9575 +//line mysql_sql.y:9623 { yyLOCAL = tree.INDEX_CATEGORY_NONE } yyVAL.union = yyLOCAL - case 1395: + case 1404: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.IndexCategory -//line mysql_sql.y:9579 +//line mysql_sql.y:9627 { yyLOCAL = tree.INDEX_CATEGORY_FULLTEXT } yyVAL.union = yyLOCAL - case 1396: + case 1405: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.IndexCategory -//line mysql_sql.y:9583 +//line mysql_sql.y:9631 { yyLOCAL = tree.INDEX_CATEGORY_FULLTEXT2 } yyVAL.union = yyLOCAL - case 1397: + case 1406: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.IndexCategory -//line mysql_sql.y:9587 +//line mysql_sql.y:9635 { yyLOCAL = tree.INDEX_CATEGORY_SPATIAL } yyVAL.union = yyLOCAL - case 1398: + case 1407: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.IndexCategory -//line mysql_sql.y:9591 +//line mysql_sql.y:9639 { yyLOCAL = tree.INDEX_CATEGORY_UNIQUE } yyVAL.union = yyLOCAL - case 1399: + case 1408: yyDollar = yyS[yypt-11 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:9597 +//line mysql_sql.y:9645 { var io *tree.IndexOption = nil if yyDollar[11].indexOptionUnion() == nil && yyDollar[5].indexTypeUnion() != tree.INDEX_TYPE_INVALID { @@ -26014,18 +26143,18 @@ yydefault: ) } yyVAL.union = yyLOCAL - case 1400: + case 1409: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL *tree.IndexOption -//line mysql_sql.y:9628 +//line mysql_sql.y:9676 { yyLOCAL = nil } yyVAL.union = yyLOCAL - case 1401: + case 1410: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL *tree.IndexOption -//line mysql_sql.y:9632 +//line mysql_sql.y:9680 { // Merge the options if yyDollar[1].indexOptionUnion() == nil { @@ -26099,20 +26228,20 @@ yydefault: } } yyVAL.union = yyLOCAL - case 1402: + case 1411: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL *tree.IndexOption -//line mysql_sql.y:9707 +//line mysql_sql.y:9755 { io := tree.NewIndexOption() io.KeyBlockSize = uint64(yyDollar[3].item.(int64)) yyLOCAL = io } yyVAL.union = yyLOCAL - case 1403: + case 1412: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL *tree.IndexOption -//line mysql_sql.y:9713 +//line mysql_sql.y:9761 { val := int64(yyDollar[3].item.(int64)) if val <= 0 { @@ -26125,60 +26254,60 @@ yydefault: yyLOCAL = io } yyVAL.union = yyLOCAL - case 1404: + case 1413: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL *tree.IndexOption -//line mysql_sql.y:9725 +//line mysql_sql.y:9773 { io := tree.NewIndexOption() io.AlgoParamVectorOpType = yyDollar[2].str yyLOCAL = io } yyVAL.union = yyLOCAL - case 1405: + case 1414: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL *tree.IndexOption -//line mysql_sql.y:9731 +//line mysql_sql.y:9779 { io := tree.NewIndexOption() io.Comment = yyDollar[2].str yyLOCAL = io } yyVAL.union = yyLOCAL - case 1406: + case 1415: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL *tree.IndexOption -//line mysql_sql.y:9737 +//line mysql_sql.y:9785 { io := tree.NewIndexOption() io.ParserName = yyDollar[3].cstrUnion().Compare() yyLOCAL = io } yyVAL.union = yyLOCAL - case 1407: + case 1416: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL *tree.IndexOption -//line mysql_sql.y:9743 +//line mysql_sql.y:9791 { io := tree.NewIndexOption() io.Visible = tree.VISIBLE_TYPE_VISIBLE yyLOCAL = io } yyVAL.union = yyLOCAL - case 1408: + case 1417: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL *tree.IndexOption -//line mysql_sql.y:9749 +//line mysql_sql.y:9797 { io := tree.NewIndexOption() io.Visible = tree.VISIBLE_TYPE_INVISIBLE yyLOCAL = io } yyVAL.union = yyLOCAL - case 1409: + case 1418: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL *tree.IndexOption -//line mysql_sql.y:9755 +//line mysql_sql.y:9803 { val := int64(yyDollar[3].item.(int64)) if val <= 0 { @@ -26190,10 +26319,10 @@ yydefault: yyLOCAL = io } yyVAL.union = yyLOCAL - case 1410: + case 1419: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL *tree.IndexOption -//line mysql_sql.y:9766 +//line mysql_sql.y:9814 { val := int64(yyDollar[3].item.(int64)) if val <= 0 { @@ -26205,10 +26334,10 @@ yydefault: yyLOCAL = io } yyVAL.union = yyLOCAL - case 1411: + case 1420: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL *tree.IndexOption -//line mysql_sql.y:9777 +//line mysql_sql.y:9825 { val := int64(yyDollar[3].item.(int64)) if val <= 0 { @@ -26220,10 +26349,10 @@ yydefault: yyLOCAL = io } yyVAL.union = yyLOCAL - case 1412: + case 1421: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL *tree.IndexOption -//line mysql_sql.y:9788 +//line mysql_sql.y:9836 { val := int64(yyDollar[3].item.(int64)) if val <= 0 { @@ -26235,10 +26364,10 @@ yydefault: yyLOCAL = io } yyVAL.union = yyLOCAL - case 1413: + case 1422: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL *tree.IndexOption -//line mysql_sql.y:9799 +//line mysql_sql.y:9847 { val := int64(yyDollar[3].item.(int64)) if val <= 0 { @@ -26250,10 +26379,10 @@ yydefault: yyLOCAL = io } yyVAL.union = yyLOCAL - case 1414: + case 1423: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL *tree.IndexOption -//line mysql_sql.y:9810 +//line mysql_sql.y:9858 { val := int64(yyDollar[3].item.(int64)) if val <= 0 { @@ -26265,40 +26394,40 @@ yydefault: yyLOCAL = io } yyVAL.union = yyLOCAL - case 1415: + case 1424: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL *tree.IndexOption -//line mysql_sql.y:9821 +//line mysql_sql.y:9869 { io := tree.NewIndexOption() io.IncludeColumns = yyDollar[3].unresolveNamesUnion() yyLOCAL = io } yyVAL.union = yyLOCAL - case 1416: + case 1425: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL *tree.IndexOption -//line mysql_sql.y:9827 +//line mysql_sql.y:9875 { io := tree.NewIndexOption() io.Quantization = yyDollar[2].str yyLOCAL = io } yyVAL.union = yyLOCAL - case 1417: + case 1426: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL *tree.IndexOption -//line mysql_sql.y:9833 +//line mysql_sql.y:9881 { io := tree.NewIndexOption() io.DistributionMode = yyDollar[2].str yyLOCAL = io } yyVAL.union = yyLOCAL - case 1418: + case 1427: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL *tree.IndexOption -//line mysql_sql.y:9839 +//line mysql_sql.y:9887 { val := int64(yyDollar[3].item.(int64)) if val <= 0 { @@ -26310,10 +26439,10 @@ yydefault: yyLOCAL = io } yyVAL.union = yyLOCAL - case 1419: + case 1428: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL *tree.IndexOption -//line mysql_sql.y:9850 +//line mysql_sql.y:9898 { val := int64(yyDollar[3].item.(int64)) if val <= 0 { @@ -26325,10 +26454,10 @@ yydefault: yyLOCAL = io } yyVAL.union = yyLOCAL - case 1420: + case 1429: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL *tree.IndexOption -//line mysql_sql.y:9861 +//line mysql_sql.y:9909 { val := int64(yyDollar[3].item.(int64)) if val <= 0 { @@ -26340,10 +26469,10 @@ yydefault: yyLOCAL = io } yyVAL.union = yyLOCAL - case 1421: + case 1430: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL *tree.IndexOption -//line mysql_sql.y:9872 +//line mysql_sql.y:9920 { val := int64(yyDollar[3].item.(int64)) if val <= 0 { @@ -26355,10 +26484,10 @@ yydefault: yyLOCAL = io } yyVAL.union = yyLOCAL - case 1422: + case 1431: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL *tree.IndexOption -//line mysql_sql.y:9883 +//line mysql_sql.y:9931 { val := int64(yyDollar[3].item.(int64)) if val <= 0 { @@ -26370,10 +26499,10 @@ yydefault: yyLOCAL = io } yyVAL.union = yyLOCAL - case 1423: + case 1432: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL *tree.IndexOption -//line mysql_sql.y:9894 +//line mysql_sql.y:9942 { val := int64(yyDollar[3].item.(int64)) if val <= 0 { @@ -26385,10 +26514,10 @@ yydefault: yyLOCAL = io } yyVAL.union = yyLOCAL - case 1424: + case 1433: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL *tree.IndexOption -//line mysql_sql.y:9905 +//line mysql_sql.y:9953 { io := tree.NewIndexOption() io.PositionFree = true @@ -26396,10 +26525,10 @@ yydefault: yyLOCAL = io } yyVAL.union = yyLOCAL - case 1425: + case 1434: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL *tree.IndexOption -//line mysql_sql.y:9912 +//line mysql_sql.y:9960 { io := tree.NewIndexOption() io.PositionFree = false @@ -26407,60 +26536,60 @@ yydefault: yyLOCAL = io } yyVAL.union = yyLOCAL - case 1426: + case 1435: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL *tree.IndexOption -//line mysql_sql.y:9919 +//line mysql_sql.y:9967 { io := tree.NewIndexOption() io.Async = true yyLOCAL = io } yyVAL.union = yyLOCAL - case 1427: + case 1436: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL *tree.IndexOption -//line mysql_sql.y:9925 +//line mysql_sql.y:9973 { io := tree.NewIndexOption() io.ForceSync = true yyLOCAL = io } yyVAL.union = yyLOCAL - case 1428: + case 1437: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL *tree.IndexOption -//line mysql_sql.y:9931 +//line mysql_sql.y:9979 { io := tree.NewIndexOption() io.Merge = true yyLOCAL = io } yyVAL.union = yyLOCAL - case 1429: + case 1438: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL *tree.IndexOption -//line mysql_sql.y:9937 +//line mysql_sql.y:9985 { io := tree.NewIndexOption() io.AutoUpdate = true yyLOCAL = io } yyVAL.union = yyLOCAL - case 1430: + case 1439: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL *tree.IndexOption -//line mysql_sql.y:9943 +//line mysql_sql.y:9991 { io := tree.NewIndexOption() io.AutoUpdate = false yyLOCAL = io } yyVAL.union = yyLOCAL - case 1431: + case 1440: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL *tree.IndexOption -//line mysql_sql.y:9949 +//line mysql_sql.y:9997 { val := int64(yyDollar[3].item.(int64)) if val < 0 { @@ -26472,10 +26601,10 @@ yydefault: yyLOCAL = io } yyVAL.union = yyLOCAL - case 1432: + case 1441: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL *tree.IndexOption -//line mysql_sql.y:9960 +//line mysql_sql.y:10008 { val := int64(yyDollar[3].item.(int64)) if val < 0 || val > 23 { @@ -26487,10 +26616,10 @@ yydefault: yyLOCAL = io } yyVAL.union = yyLOCAL - case 1433: + case 1442: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL *tree.IndexOption -//line mysql_sql.y:9971 +//line mysql_sql.y:10019 { val := int64(yyDollar[3].item.(int64)) if val < 0 { @@ -26502,26 +26631,26 @@ yydefault: yyLOCAL = io } yyVAL.union = yyLOCAL - case 1434: + case 1443: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL []*tree.KeyPart -//line mysql_sql.y:9985 +//line mysql_sql.y:10033 { yyLOCAL = []*tree.KeyPart{yyDollar[1].keyPartUnion()} } yyVAL.union = yyLOCAL - case 1435: + case 1444: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL []*tree.KeyPart -//line mysql_sql.y:9989 +//line mysql_sql.y:10037 { yyLOCAL = append(yyDollar[1].keyPartsUnion(), yyDollar[3].keyPartUnion()) } yyVAL.union = yyLOCAL - case 1436: + case 1445: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL *tree.KeyPart -//line mysql_sql.y:9995 +//line mysql_sql.y:10043 { // Order is parsed but just ignored as MySQL dtree. var ColName = yyDollar[1].unresolvedNameUnion() @@ -26536,10 +26665,10 @@ yydefault: ) } yyVAL.union = yyLOCAL - case 1437: + case 1446: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL *tree.KeyPart -//line mysql_sql.y:10009 +//line mysql_sql.y:10057 { var ColName *tree.UnresolvedName var Length int @@ -26553,90 +26682,90 @@ yydefault: ) } yyVAL.union = yyLOCAL - case 1438: + case 1447: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL tree.IndexType -//line mysql_sql.y:10023 +//line mysql_sql.y:10071 { yyLOCAL = tree.INDEX_TYPE_INVALID } yyVAL.union = yyLOCAL - case 1439: + case 1448: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL tree.IndexType -//line mysql_sql.y:10027 +//line mysql_sql.y:10075 { yyLOCAL = tree.INDEX_TYPE_BTREE } yyVAL.union = yyLOCAL - case 1440: + case 1449: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL tree.IndexType -//line mysql_sql.y:10031 +//line mysql_sql.y:10079 { yyLOCAL = tree.INDEX_TYPE_IVFFLAT } yyVAL.union = yyLOCAL - case 1441: + case 1450: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL tree.IndexType -//line mysql_sql.y:10035 +//line mysql_sql.y:10083 { yyLOCAL = tree.INDEX_TYPE_HNSW } yyVAL.union = yyLOCAL - case 1442: + case 1451: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL tree.IndexType -//line mysql_sql.y:10039 +//line mysql_sql.y:10087 { yyLOCAL = tree.INDEX_TYPE_IVFPQ } yyVAL.union = yyLOCAL - case 1443: + case 1452: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL tree.IndexType -//line mysql_sql.y:10043 +//line mysql_sql.y:10091 { yyLOCAL = tree.INDEX_TYPE_CAGRA } yyVAL.union = yyLOCAL - case 1444: + case 1453: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL tree.IndexType -//line mysql_sql.y:10047 +//line mysql_sql.y:10095 { yyLOCAL = tree.INDEX_TYPE_MASTER } yyVAL.union = yyLOCAL - case 1445: + case 1454: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL tree.IndexType -//line mysql_sql.y:10051 +//line mysql_sql.y:10099 { yyLOCAL = tree.INDEX_TYPE_HASH } yyVAL.union = yyLOCAL - case 1446: + case 1455: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL tree.IndexType -//line mysql_sql.y:10055 +//line mysql_sql.y:10103 { yyLOCAL = tree.INDEX_TYPE_RTREE } yyVAL.union = yyLOCAL - case 1447: + case 1456: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL tree.IndexType -//line mysql_sql.y:10059 +//line mysql_sql.y:10107 { yyLOCAL = tree.INDEX_TYPE_BSI } yyVAL.union = yyLOCAL - case 1448: + case 1457: yyDollar = yyS[yypt-6 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:10065 +//line mysql_sql.y:10113 { var IfNotExists = yyDollar[3].ifNotExistsUnion() var Name = tree.Identifier(yyDollar[4].str) @@ -26650,10 +26779,10 @@ yydefault: ) } yyVAL.union = yyLOCAL - case 1449: + case 1458: yyDollar = yyS[yypt-8 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:10079 +//line mysql_sql.y:10127 { var t = tree.NewCloneDatabase() t.IfNotExists = yyDollar[3].ifNotExistsUnion() @@ -26664,10 +26793,10 @@ yydefault: yyLOCAL = t } yyVAL.union = yyLOCAL - case 1450: + case 1459: yyDollar = yyS[yypt-10 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:10089 +//line mysql_sql.y:10137 { var DbName = tree.Identifier(yyDollar[4].str) var FromUri = yyDollar[6].str @@ -26685,100 +26814,100 @@ yydefault: ) } yyVAL.union = yyLOCAL - case 1451: + case 1460: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL *tree.SubscriptionOption -//line mysql_sql.y:10107 +//line mysql_sql.y:10155 { yyLOCAL = nil } yyVAL.union = yyLOCAL - case 1452: + case 1461: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL *tree.SubscriptionOption -//line mysql_sql.y:10111 +//line mysql_sql.y:10159 { var From = tree.Identifier(yyDollar[2].str) var Publication = tree.Identifier(yyDollar[4].cstrUnion().Compare()) yyLOCAL = tree.NewSubscriptionOption(From, Publication) } yyVAL.union = yyLOCAL - case 1455: + case 1464: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL bool -//line mysql_sql.y:10122 +//line mysql_sql.y:10170 { yyLOCAL = false } yyVAL.union = yyLOCAL - case 1456: + case 1465: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL bool -//line mysql_sql.y:10126 +//line mysql_sql.y:10174 { yyLOCAL = true } yyVAL.union = yyLOCAL - case 1457: + case 1466: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL bool -//line mysql_sql.y:10130 +//line mysql_sql.y:10178 { yyLOCAL = true } yyVAL.union = yyLOCAL - case 1458: + case 1467: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL bool -//line mysql_sql.y:10135 +//line mysql_sql.y:10183 { yyLOCAL = false } yyVAL.union = yyLOCAL - case 1459: + case 1468: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL bool -//line mysql_sql.y:10139 +//line mysql_sql.y:10187 { yyLOCAL = true } yyVAL.union = yyLOCAL - case 1460: + case 1469: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL []tree.CreateOption -//line mysql_sql.y:10144 +//line mysql_sql.y:10192 { yyLOCAL = nil } yyVAL.union = yyLOCAL - case 1461: + case 1470: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL []tree.CreateOption -//line mysql_sql.y:10148 +//line mysql_sql.y:10196 { yyLOCAL = yyDollar[1].createOptionsUnion() } yyVAL.union = yyLOCAL - case 1462: + case 1471: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL []tree.CreateOption -//line mysql_sql.y:10154 +//line mysql_sql.y:10202 { yyLOCAL = []tree.CreateOption{yyDollar[1].createOptionUnion()} } yyVAL.union = yyLOCAL - case 1463: + case 1472: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL []tree.CreateOption -//line mysql_sql.y:10158 +//line mysql_sql.y:10206 { yyLOCAL = append(yyDollar[1].createOptionsUnion(), yyDollar[2].createOptionUnion()) } yyVAL.union = yyLOCAL - case 1464: + case 1473: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL tree.CreateOption -//line mysql_sql.y:10164 +//line mysql_sql.y:10212 { var IsDefault = yyDollar[1].defaultOptionalUnion() var Charset = yyDollar[4].str @@ -26788,10 +26917,10 @@ yydefault: ) } yyVAL.union = yyLOCAL - case 1465: + case 1474: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL tree.CreateOption -//line mysql_sql.y:10173 +//line mysql_sql.y:10221 { var IsDefault = yyDollar[1].defaultOptionalUnion() var Collate = yyDollar[4].str @@ -26801,35 +26930,35 @@ yydefault: ) } yyVAL.union = yyLOCAL - case 1466: + case 1475: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL tree.CreateOption -//line mysql_sql.y:10182 +//line mysql_sql.y:10230 { var Encrypt = yyDollar[4].str yyLOCAL = tree.NewCreateOptionEncryption(Encrypt) } yyVAL.union = yyLOCAL - case 1467: + case 1476: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL bool -//line mysql_sql.y:10188 +//line mysql_sql.y:10236 { yyLOCAL = false } yyVAL.union = yyLOCAL - case 1468: + case 1477: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL bool -//line mysql_sql.y:10192 +//line mysql_sql.y:10240 { yyLOCAL = true } yyVAL.union = yyLOCAL - case 1469: + case 1478: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:10198 +//line mysql_sql.y:10246 { var taskID uint64 switch v := yyDollar[4].item.(type) { @@ -26846,10 +26975,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 1470: + case 1479: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:10216 +//line mysql_sql.y:10264 { var taskID uint64 switch v := yyDollar[4].item.(type) { @@ -26866,10 +26995,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 1471: + case 1480: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:10234 +//line mysql_sql.y:10282 { var taskID uint64 switch v := yyDollar[4].item.(type) { @@ -26886,26 +27015,26 @@ yydefault: } } yyVAL.union = yyLOCAL - case 1472: + case 1481: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL bool -//line mysql_sql.y:10251 +//line mysql_sql.y:10299 { yyLOCAL = false } yyVAL.union = yyLOCAL - case 1473: + case 1482: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL bool -//line mysql_sql.y:10255 +//line mysql_sql.y:10303 { yyLOCAL = true } yyVAL.union = yyLOCAL - case 1474: + case 1483: yyDollar = yyS[yypt-8 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:10261 +//line mysql_sql.y:10309 { t := tree.NewDataBranchCreateTable() t.CreateTable.Table = *yyDollar[5].tableNameUnion() @@ -26916,10 +27045,10 @@ yydefault: yyLOCAL = t } yyVAL.union = yyLOCAL - case 1475: + case 1484: yyDollar = yyS[yypt-9 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:10271 +//line mysql_sql.y:10319 { t := tree.NewDataBranchCreateDatabase() t.DstDatabase = tree.Identifier(yyDollar[5].str) @@ -26929,30 +27058,30 @@ yydefault: yyLOCAL = t } yyVAL.union = yyLOCAL - case 1476: + case 1485: yyDollar = yyS[yypt-5 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:10280 +//line mysql_sql.y:10328 { t := tree.NewDataBranchDeleteTable() t.TableName = *yyDollar[5].tableNameUnion() yyLOCAL = t } yyVAL.union = yyLOCAL - case 1477: + case 1486: yyDollar = yyS[yypt-5 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:10286 +//line mysql_sql.y:10334 { t := tree.NewDataBranchDeleteDatabase() t.DatabaseName = tree.Identifier(yyDollar[5].str) yyLOCAL = t } yyVAL.union = yyLOCAL - case 1478: + case 1487: yyDollar = yyS[yypt-8 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:10292 +//line mysql_sql.y:10340 { t := tree.NewDataBranchDiff() t.TargetTable = *yyDollar[4].tableNameUnion() @@ -26962,10 +27091,10 @@ yydefault: yyLOCAL = t } yyVAL.union = yyLOCAL - case 1479: + case 1488: yyDollar = yyS[yypt-7 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:10301 +//line mysql_sql.y:10349 { t := tree.NewDataBranchMerge() t.SrcTable = *yyDollar[4].tableNameUnion() @@ -26974,10 +27103,10 @@ yydefault: yyLOCAL = t } yyVAL.union = yyLOCAL - case 1480: + case 1489: yyDollar = yyS[yypt-8 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:10309 +//line mysql_sql.y:10357 { t := tree.NewDataBranchPick() t.SrcTable = *yyDollar[4].tableNameUnion() @@ -26987,10 +27116,10 @@ yydefault: yyLOCAL = t } yyVAL.union = yyLOCAL - case 1481: + case 1490: yyDollar = yyS[yypt-12 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:10318 +//line mysql_sql.y:10366 { t := tree.NewDataBranchPick() t.SrcTable = *yyDollar[4].tableNameUnion() @@ -27001,10 +27130,10 @@ yydefault: yyLOCAL = t } yyVAL.union = yyLOCAL - case 1482: + case 1491: yyDollar = yyS[yypt-12 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:10328 +//line mysql_sql.y:10376 { t := tree.NewDataBranchPick() t.SrcTable = *yyDollar[4].tableNameUnion() @@ -27015,10 +27144,10 @@ yydefault: yyLOCAL = t } yyVAL.union = yyLOCAL - case 1483: + case 1492: yyDollar = yyS[yypt-13 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:10338 +//line mysql_sql.y:10386 { t := tree.NewDataBranchPick() t.SrcTable = *yyDollar[4].tableNameUnion() @@ -27030,10 +27159,10 @@ yydefault: yyLOCAL = t } yyVAL.union = yyLOCAL - case 1484: + case 1493: yyDollar = yyS[yypt-13 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:10349 +//line mysql_sql.y:10397 { t := tree.NewDataBranchPick() t.SrcTable = *yyDollar[4].tableNameUnion() @@ -27045,54 +27174,54 @@ yydefault: yyLOCAL = t } yyVAL.union = yyLOCAL - case 1485: + case 1494: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL tree.IdentifierList -//line mysql_sql.y:10361 +//line mysql_sql.y:10409 { yyLOCAL = nil } yyVAL.union = yyLOCAL - case 1486: + case 1495: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL tree.IdentifierList -//line mysql_sql.y:10365 +//line mysql_sql.y:10413 { yyLOCAL = yyDollar[3].identifierListUnion() } yyVAL.union = yyLOCAL - case 1487: + case 1496: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL *tree.DiffOutputOpt -//line mysql_sql.y:10370 +//line mysql_sql.y:10418 { yyLOCAL = nil } yyVAL.union = yyLOCAL - case 1488: + case 1497: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL *tree.DiffOutputOpt -//line mysql_sql.y:10374 +//line mysql_sql.y:10422 { yyLOCAL = &tree.DiffOutputOpt{ As: *yyDollar[3].tableNameUnion(), } } yyVAL.union = yyLOCAL - case 1489: + case 1498: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL *tree.DiffOutputOpt -//line mysql_sql.y:10380 +//line mysql_sql.y:10428 { yyLOCAL = &tree.DiffOutputOpt{ DirPath: yyDollar[3].str, } } yyVAL.union = yyLOCAL - case 1490: + case 1499: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL *tree.DiffOutputOpt -//line mysql_sql.y:10386 +//line mysql_sql.y:10434 { x, errStr := util.GetInt64(yyDollar[3].item) if errStr != "" { @@ -27104,68 +27233,68 @@ yydefault: } } yyVAL.union = yyLOCAL - case 1491: + case 1500: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL *tree.DiffOutputOpt -//line mysql_sql.y:10393 +//line mysql_sql.y:10441 { yyLOCAL = &tree.DiffOutputOpt{ Count: true, } } yyVAL.union = yyLOCAL - case 1492: + case 1501: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL *tree.DiffOutputOpt -//line mysql_sql.y:10399 +//line mysql_sql.y:10447 { yyLOCAL = &tree.DiffOutputOpt{ Summary: true, } } yyVAL.union = yyLOCAL - case 1493: + case 1502: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL *tree.ConflictOpt -//line mysql_sql.y:10407 +//line mysql_sql.y:10455 { yyLOCAL = nil } yyVAL.union = yyLOCAL - case 1494: + case 1503: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL *tree.ConflictOpt -//line mysql_sql.y:10411 +//line mysql_sql.y:10459 { yyLOCAL = &tree.ConflictOpt{ Opt: tree.CONFLICT_FAIL, } } yyVAL.union = yyLOCAL - case 1495: + case 1504: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL *tree.ConflictOpt -//line mysql_sql.y:10417 +//line mysql_sql.y:10465 { yyLOCAL = &tree.ConflictOpt{ Opt: tree.CONFLICT_SKIP, } } yyVAL.union = yyLOCAL - case 1496: + case 1505: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL *tree.ConflictOpt -//line mysql_sql.y:10423 +//line mysql_sql.y:10471 { yyLOCAL = &tree.ConflictOpt{ Opt: tree.CONFLICT_ACCEPT, } } yyVAL.union = yyLOCAL - case 1497: + case 1506: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL *tree.PickKeys -//line mysql_sql.y:10431 +//line mysql_sql.y:10479 { yyLOCAL = &tree.PickKeys{ Type: tree.PickKeysValues, @@ -27173,10 +27302,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 1498: + case 1507: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL *tree.PickKeys -//line mysql_sql.y:10438 +//line mysql_sql.y:10486 { yyLOCAL = &tree.PickKeys{ Type: tree.PickKeysSubquery, @@ -27184,74 +27313,74 @@ yydefault: } } yyVAL.union = yyLOCAL - case 1499: + case 1508: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL *tree.ToAccountOpt -//line mysql_sql.y:10447 +//line mysql_sql.y:10495 { yyLOCAL = nil } yyVAL.union = yyLOCAL - case 1500: + case 1509: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL *tree.ToAccountOpt -//line mysql_sql.y:10451 +//line mysql_sql.y:10499 { yyLOCAL = &tree.ToAccountOpt{ AccountName: tree.Identifier(yyDollar[3].cstrUnion().Compare()), } } yyVAL.union = yyLOCAL - case 1501: + case 1510: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL bool -//line mysql_sql.y:10459 +//line mysql_sql.y:10507 { yyLOCAL = false } yyVAL.union = yyLOCAL - case 1502: + case 1511: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL bool -//line mysql_sql.y:10463 +//line mysql_sql.y:10511 { yyLOCAL = true } yyVAL.union = yyLOCAL - case 1503: + case 1512: yyDollar = yyS[yypt-0 : yypt+1] -//line mysql_sql.y:10468 +//line mysql_sql.y:10516 { yyVAL.str = "" } - case 1504: + case 1513: yyDollar = yyS[yypt-1 : yypt+1] -//line mysql_sql.y:10469 +//line mysql_sql.y:10517 { yyVAL.str = "ignore" } - case 1505: + case 1514: yyDollar = yyS[yypt-1 : yypt+1] -//line mysql_sql.y:10470 +//line mysql_sql.y:10518 { yyVAL.str = "replace" } - case 1506: + case 1515: yyDollar = yyS[yypt-1 : yypt+1] -//line mysql_sql.y:10473 +//line mysql_sql.y:10521 { yyVAL.str = "ignore" } - case 1507: + case 1516: yyDollar = yyS[yypt-1 : yypt+1] -//line mysql_sql.y:10474 +//line mysql_sql.y:10522 { yyVAL.str = "replace" } - case 1508: + case 1517: yyDollar = yyS[yypt-11 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:10478 +//line mysql_sql.y:10526 { t := tree.NewCreateTable() t.Temporary = yyDollar[2].boolValUnion() @@ -27264,10 +27393,10 @@ yydefault: yyLOCAL = t } yyVAL.union = yyLOCAL - case 1509: + case 1518: yyDollar = yyS[yypt-9 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:10490 +//line mysql_sql.y:10538 { t := tree.NewCreateTable() t.IfNotExists = yyDollar[4].ifNotExistsUnion() @@ -27277,10 +27406,10 @@ yydefault: yyLOCAL = t } yyVAL.union = yyLOCAL - case 1510: + case 1519: yyDollar = yyS[yypt-9 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:10499 +//line mysql_sql.y:10547 { t := tree.NewCreateTable() t.IfNotExists = yyDollar[4].ifNotExistsUnion() @@ -27290,10 +27419,10 @@ yydefault: yyLOCAL = t } yyVAL.union = yyLOCAL - case 1511: + case 1520: yyDollar = yyS[yypt-9 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:10508 +//line mysql_sql.y:10556 { t := tree.NewCreateTable() t.IfNotExists = yyDollar[4].ifNotExistsUnion() @@ -27303,10 +27432,10 @@ yydefault: yyLOCAL = t } yyVAL.union = yyLOCAL - case 1512: + case 1521: yyDollar = yyS[yypt-9 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:10517 +//line mysql_sql.y:10565 { t := tree.NewCreateTable() t.IfNotExists = yyDollar[4].ifNotExistsUnion() @@ -27316,10 +27445,10 @@ yydefault: yyLOCAL = t } yyVAL.union = yyLOCAL - case 1513: + case 1522: yyDollar = yyS[yypt-9 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:10526 +//line mysql_sql.y:10574 { t := tree.NewCreateTable() t.IfNotExists = yyDollar[4].ifNotExistsUnion() @@ -27329,10 +27458,10 @@ yydefault: yyLOCAL = t } yyVAL.union = yyLOCAL - case 1514: + case 1523: yyDollar = yyS[yypt-9 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:10535 +//line mysql_sql.y:10583 { t := tree.NewCreateTable() t.IfNotExists = yyDollar[4].ifNotExistsUnion() @@ -27342,10 +27471,10 @@ yydefault: yyLOCAL = t } yyVAL.union = yyLOCAL - case 1515: + case 1524: yyDollar = yyS[yypt-6 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:10544 +//line mysql_sql.y:10592 { t := tree.NewCreateTable() t.IfNotExists = yyDollar[4].ifNotExistsUnion() @@ -27354,10 +27483,10 @@ yydefault: yyLOCAL = t } yyVAL.union = yyLOCAL - case 1516: + case 1525: yyDollar = yyS[yypt-11 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:10552 +//line mysql_sql.y:10600 { t := tree.NewCreateTable() t.IsClusterTable = true @@ -27370,10 +27499,10 @@ yydefault: yyLOCAL = t } yyVAL.union = yyLOCAL - case 1517: + case 1526: yyDollar = yyS[yypt-6 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:10564 +//line mysql_sql.y:10612 { if intoErr := tree.ValidateSelectIntoNotAllowed(yyDollar[6].selectUnion()); intoErr != "" { yylex.Error(intoErr) @@ -27388,10 +27517,10 @@ yydefault: yyLOCAL = t } yyVAL.union = yyLOCAL - case 1518: + case 1527: yyDollar = yyS[yypt-9 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:10578 +//line mysql_sql.y:10626 { if intoErr := tree.ValidateSelectIntoNotAllowed(yyDollar[9].selectUnion()); intoErr != "" { yylex.Error(intoErr) @@ -27407,10 +27536,10 @@ yydefault: yyLOCAL = t } yyVAL.union = yyLOCAL - case 1519: + case 1528: yyDollar = yyS[yypt-7 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:10593 +//line mysql_sql.y:10641 { if intoErr := tree.ValidateSelectIntoNotAllowed(yyDollar[7].selectUnion()); intoErr != "" { yylex.Error(intoErr) @@ -27426,10 +27555,10 @@ yydefault: yyLOCAL = t } yyVAL.union = yyLOCAL - case 1520: + case 1529: yyDollar = yyS[yypt-10 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:10598 +//line mysql_sql.y:10646 { if intoErr := tree.ValidateSelectIntoNotAllowed(yyDollar[10].selectUnion()); intoErr != "" { yylex.Error(intoErr) @@ -27446,10 +27575,10 @@ yydefault: yyLOCAL = t } yyVAL.union = yyLOCAL - case 1521: + case 1530: yyDollar = yyS[yypt-8 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:10603 +//line mysql_sql.y:10651 { if intoErr := tree.ValidateSelectIntoNotAllowed(yyDollar[8].selectUnion()); intoErr != "" { yylex.Error(intoErr) @@ -27465,10 +27594,10 @@ yydefault: yyLOCAL = t } yyVAL.union = yyLOCAL - case 1522: + case 1531: yyDollar = yyS[yypt-11 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:10608 +//line mysql_sql.y:10656 { if intoErr := tree.ValidateSelectIntoNotAllowed(yyDollar[11].selectUnion()); intoErr != "" { yylex.Error(intoErr) @@ -27485,10 +27614,10 @@ yydefault: yyLOCAL = t } yyVAL.union = yyLOCAL - case 1523: + case 1532: yyDollar = yyS[yypt-7 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:10613 +//line mysql_sql.y:10661 { t := tree.NewCreateTable() t.IsAsLike = true @@ -27499,10 +27628,10 @@ yydefault: yyLOCAL = t } yyVAL.union = yyLOCAL - case 1524: + case 1533: yyDollar = yyS[yypt-6 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:10623 +//line mysql_sql.y:10671 { t := tree.NewCreateTable() t.Temporary = yyDollar[2].boolValUnion() @@ -27512,10 +27641,10 @@ yydefault: yyLOCAL = t } yyVAL.union = yyLOCAL - case 1525: + case 1534: yyDollar = yyS[yypt-9 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:10632 +//line mysql_sql.y:10680 { t := tree.NewCloneTable() t.CreateTable.Temporary = yyDollar[2].boolValUnion() @@ -27529,10 +27658,10 @@ yydefault: yyLOCAL = t } yyVAL.union = yyLOCAL - case 1526: + case 1535: yyDollar = yyS[yypt-11 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:10645 +//line mysql_sql.y:10693 { var TableName = yyDollar[5].tableNameUnion() var FromUri = yyDollar[7].str @@ -27556,19 +27685,19 @@ yydefault: ) } yyVAL.union = yyLOCAL - case 1527: + case 1536: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL *tree.ExternParam -//line mysql_sql.y:10670 +//line mysql_sql.y:10718 { yyLOCAL = yyDollar[1].loadParamUnion() yyLOCAL.Tail = yyDollar[2].tailParamUnion() } yyVAL.union = yyLOCAL - case 1528: + case 1537: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL *tree.ExternParam -//line mysql_sql.y:10677 +//line mysql_sql.y:10725 { yyLOCAL = &tree.ExternParam{ ExParamConst: tree.ExParamConst{ @@ -27579,10 +27708,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 1529: + case 1538: yyDollar = yyS[yypt-9 : yypt+1] var yyLOCAL *tree.ExternParam -//line mysql_sql.y:10687 +//line mysql_sql.y:10735 { yyLOCAL = &tree.ExternParam{ ExParamConst: tree.ExParamConst{ @@ -27596,10 +27725,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 1530: + case 1539: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL *tree.ExternParam -//line mysql_sql.y:10700 +//line mysql_sql.y:10748 { yyLOCAL = &tree.ExternParam{ ExParamConst: tree.ExParamConst{ @@ -27608,10 +27737,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 1531: + case 1540: yyDollar = yyS[yypt-5 : yypt+1] var yyLOCAL *tree.ExternParam -//line mysql_sql.y:10708 +//line mysql_sql.y:10756 { yyLOCAL = &tree.ExternParam{ ExParamConst: tree.ExParamConst{ @@ -27621,10 +27750,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 1532: + case 1541: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL *tree.ExternParam -//line mysql_sql.y:10717 +//line mysql_sql.y:10765 { yyLOCAL = &tree.ExternParam{ ExParamConst: tree.ExParamConst{ @@ -27633,55 +27762,55 @@ yydefault: } } yyVAL.union = yyLOCAL - case 1533: + case 1542: yyDollar = yyS[yypt-0 : yypt+1] -//line mysql_sql.y:10726 +//line mysql_sql.y:10774 { yyVAL.str = "" } - case 1534: + case 1543: yyDollar = yyS[yypt-4 : yypt+1] -//line mysql_sql.y:10730 +//line mysql_sql.y:10778 { yyVAL.str = yyDollar[4].str } - case 1535: + case 1544: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL []string -//line mysql_sql.y:10736 +//line mysql_sql.y:10784 { yyLOCAL = yyDollar[1].strsUnion() } yyVAL.union = yyLOCAL - case 1536: + case 1545: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL []string -//line mysql_sql.y:10740 +//line mysql_sql.y:10788 { yyLOCAL = append(yyDollar[1].strsUnion(), yyDollar[3].strsUnion()...) } yyVAL.union = yyLOCAL - case 1537: + case 1546: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL []string -//line mysql_sql.y:10745 +//line mysql_sql.y:10793 { yyLOCAL = []string{} } yyVAL.union = yyLOCAL - case 1538: + case 1547: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL []string -//line mysql_sql.y:10749 +//line mysql_sql.y:10797 { yyLOCAL = append(yyLOCAL, yyDollar[1].str) yyLOCAL = append(yyLOCAL, yyDollar[3].str) } yyVAL.union = yyLOCAL - case 1539: + case 1548: yyDollar = yyS[yypt-6 : yypt+1] var yyLOCAL *tree.TailParameter -//line mysql_sql.y:10756 +//line mysql_sql.y:10804 { yyLOCAL = &tree.TailParameter{ Charset: yyDollar[1].str, @@ -27693,22 +27822,22 @@ yydefault: } } yyVAL.union = yyLOCAL - case 1540: + case 1549: yyDollar = yyS[yypt-0 : yypt+1] -//line mysql_sql.y:10768 +//line mysql_sql.y:10816 { yyVAL.str = "" } - case 1541: + case 1550: yyDollar = yyS[yypt-2 : yypt+1] -//line mysql_sql.y:10772 +//line mysql_sql.y:10820 { yyVAL.str = yyDollar[2].str } - case 1542: + case 1551: yyDollar = yyS[yypt-10 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:10778 +//line mysql_sql.y:10826 { var Name = yyDollar[4].tableNameUnion() var Type = yyDollar[5].columnTypeUnion() @@ -27730,10 +27859,10 @@ yydefault: ) } yyVAL.union = yyLOCAL - case 1543: + case 1552: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL *tree.T -//line mysql_sql.y:10799 +//line mysql_sql.y:10847 { locale := "" fstr := "bigint" @@ -27748,44 +27877,44 @@ yydefault: } } yyVAL.union = yyLOCAL - case 1544: + case 1553: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL *tree.T -//line mysql_sql.y:10813 +//line mysql_sql.y:10861 { yyLOCAL = yyDollar[2].columnTypeUnion() } yyVAL.union = yyLOCAL - case 1545: + case 1554: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL *tree.TypeOption -//line mysql_sql.y:10817 +//line mysql_sql.y:10865 { yyLOCAL = nil } yyVAL.union = yyLOCAL - case 1546: + case 1555: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL *tree.TypeOption -//line mysql_sql.y:10821 +//line mysql_sql.y:10869 { yyLOCAL = &tree.TypeOption{ Type: yyDollar[2].columnTypeUnion(), } } yyVAL.union = yyLOCAL - case 1547: + case 1556: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL *tree.IncrementByOption -//line mysql_sql.y:10827 +//line mysql_sql.y:10875 { yyLOCAL = nil } yyVAL.union = yyLOCAL - case 1548: + case 1557: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL *tree.IncrementByOption -//line mysql_sql.y:10831 +//line mysql_sql.y:10879 { yyLOCAL = &tree.IncrementByOption{ Minus: false, @@ -27793,10 +27922,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 1549: + case 1558: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL *tree.IncrementByOption -//line mysql_sql.y:10838 +//line mysql_sql.y:10886 { yyLOCAL = &tree.IncrementByOption{ Minus: false, @@ -27804,10 +27933,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 1550: + case 1559: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL *tree.IncrementByOption -//line mysql_sql.y:10845 +//line mysql_sql.y:10893 { yyLOCAL = &tree.IncrementByOption{ Minus: true, @@ -27815,10 +27944,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 1551: + case 1560: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL *tree.IncrementByOption -//line mysql_sql.y:10852 +//line mysql_sql.y:10900 { yyLOCAL = &tree.IncrementByOption{ Minus: true, @@ -27826,42 +27955,42 @@ yydefault: } } yyVAL.union = yyLOCAL - case 1552: + case 1561: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL bool -//line mysql_sql.y:10859 +//line mysql_sql.y:10907 { yyLOCAL = false } yyVAL.union = yyLOCAL - case 1553: + case 1562: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL bool -//line mysql_sql.y:10863 +//line mysql_sql.y:10911 { yyLOCAL = false } yyVAL.union = yyLOCAL - case 1554: + case 1563: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL bool -//line mysql_sql.y:10867 +//line mysql_sql.y:10915 { yyLOCAL = true } yyVAL.union = yyLOCAL - case 1555: + case 1564: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL *tree.MinValueOption -//line mysql_sql.y:10871 +//line mysql_sql.y:10919 { yyLOCAL = nil } yyVAL.union = yyLOCAL - case 1556: + case 1565: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL *tree.MinValueOption -//line mysql_sql.y:10875 +//line mysql_sql.y:10923 { yyLOCAL = &tree.MinValueOption{ Minus: false, @@ -27869,10 +27998,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 1557: + case 1566: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL *tree.MinValueOption -//line mysql_sql.y:10882 +//line mysql_sql.y:10930 { yyLOCAL = &tree.MinValueOption{ Minus: true, @@ -27880,18 +28009,18 @@ yydefault: } } yyVAL.union = yyLOCAL - case 1558: + case 1567: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL *tree.MaxValueOption -//line mysql_sql.y:10889 +//line mysql_sql.y:10937 { yyLOCAL = nil } yyVAL.union = yyLOCAL - case 1559: + case 1568: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL *tree.MaxValueOption -//line mysql_sql.y:10893 +//line mysql_sql.y:10941 { yyLOCAL = &tree.MaxValueOption{ Minus: false, @@ -27899,10 +28028,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 1560: + case 1569: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL *tree.MaxValueOption -//line mysql_sql.y:10900 +//line mysql_sql.y:10948 { yyLOCAL = &tree.MaxValueOption{ Minus: true, @@ -27910,46 +28039,46 @@ yydefault: } } yyVAL.union = yyLOCAL - case 1561: + case 1570: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL *tree.CycleOption -//line mysql_sql.y:10907 +//line mysql_sql.y:10955 { yyLOCAL = nil } yyVAL.union = yyLOCAL - case 1562: + case 1571: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL *tree.CycleOption -//line mysql_sql.y:10911 +//line mysql_sql.y:10959 { yyLOCAL = &tree.CycleOption{ Cycle: false, } } yyVAL.union = yyLOCAL - case 1563: + case 1572: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL *tree.CycleOption -//line mysql_sql.y:10917 +//line mysql_sql.y:10965 { yyLOCAL = &tree.CycleOption{ Cycle: true, } } yyVAL.union = yyLOCAL - case 1564: + case 1573: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL *tree.StartWithOption -//line mysql_sql.y:10923 +//line mysql_sql.y:10971 { yyLOCAL = nil } yyVAL.union = yyLOCAL - case 1565: + case 1574: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL *tree.StartWithOption -//line mysql_sql.y:10927 +//line mysql_sql.y:10975 { yyLOCAL = &tree.StartWithOption{ Minus: false, @@ -27957,10 +28086,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 1566: + case 1575: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL *tree.StartWithOption -//line mysql_sql.y:10934 +//line mysql_sql.y:10982 { yyLOCAL = &tree.StartWithOption{ Minus: false, @@ -27968,10 +28097,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 1567: + case 1576: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL *tree.StartWithOption -//line mysql_sql.y:10941 +//line mysql_sql.y:10989 { yyLOCAL = &tree.StartWithOption{ Minus: true, @@ -27979,10 +28108,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 1568: + case 1577: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL *tree.StartWithOption -//line mysql_sql.y:10948 +//line mysql_sql.y:10996 { yyLOCAL = &tree.StartWithOption{ Minus: true, @@ -27990,58 +28119,58 @@ yydefault: } } yyVAL.union = yyLOCAL - case 1569: + case 1578: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL bool -//line mysql_sql.y:10955 +//line mysql_sql.y:11003 { yyLOCAL = false } yyVAL.union = yyLOCAL - case 1570: + case 1579: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL bool -//line mysql_sql.y:10959 +//line mysql_sql.y:11007 { yyLOCAL = true } yyVAL.union = yyLOCAL - case 1571: + case 1580: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL bool -//line mysql_sql.y:10964 +//line mysql_sql.y:11012 { yyLOCAL = true } yyVAL.union = yyLOCAL - case 1572: + case 1581: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL bool -//line mysql_sql.y:10968 +//line mysql_sql.y:11016 { yyLOCAL = true } yyVAL.union = yyLOCAL - case 1573: + case 1582: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL bool -//line mysql_sql.y:10972 +//line mysql_sql.y:11020 { yyLOCAL = true } yyVAL.union = yyLOCAL - case 1574: + case 1583: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL *tree.PartitionOption -//line mysql_sql.y:10977 +//line mysql_sql.y:11025 { yyLOCAL = nil } yyVAL.union = yyLOCAL - case 1575: + case 1584: yyDollar = yyS[yypt-6 : yypt+1] var yyLOCAL *tree.PartitionOption -//line mysql_sql.y:10981 +//line mysql_sql.y:11029 { yyDollar[3].partitionByUnion().Num = uint64(yyDollar[4].int64ValUnion()) var PartBy = yyDollar[3].partitionByUnion() @@ -28054,18 +28183,18 @@ yydefault: ) } yyVAL.union = yyLOCAL - case 1576: + case 1585: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL *tree.ClusterByOption -//line mysql_sql.y:10994 +//line mysql_sql.y:11042 { yyLOCAL = nil } yyVAL.union = yyLOCAL - case 1577: + case 1586: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL *tree.ClusterByOption -//line mysql_sql.y:10998 +//line mysql_sql.y:11046 { var ColumnList = []*tree.UnresolvedName{yyDollar[3].unresolvedNameUnion()} yyLOCAL = tree.NewClusterByOption( @@ -28074,10 +28203,10 @@ yydefault: } yyVAL.union = yyLOCAL - case 1578: + case 1587: yyDollar = yyS[yypt-5 : yypt+1] var yyLOCAL *tree.ClusterByOption -//line mysql_sql.y:11006 +//line mysql_sql.y:11054 { var ColumnList = yyDollar[4].unresolveNamesUnion() yyLOCAL = tree.NewClusterByOption( @@ -28085,18 +28214,18 @@ yydefault: ) } yyVAL.union = yyLOCAL - case 1579: + case 1588: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL *tree.PartitionBy -//line mysql_sql.y:11014 +//line mysql_sql.y:11062 { yyLOCAL = nil } yyVAL.union = yyLOCAL - case 1580: + case 1589: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL *tree.PartitionBy -//line mysql_sql.y:11018 +//line mysql_sql.y:11066 { var IsSubPartition = true var PType = yyDollar[3].partitionByUnion().PType @@ -28110,42 +28239,42 @@ yydefault: ) } yyVAL.union = yyLOCAL - case 1581: + case 1590: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL []*tree.Partition -//line mysql_sql.y:11032 +//line mysql_sql.y:11080 { yyLOCAL = nil } yyVAL.union = yyLOCAL - case 1582: + case 1591: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL []*tree.Partition -//line mysql_sql.y:11036 +//line mysql_sql.y:11084 { yyLOCAL = yyDollar[2].partitionsUnion() } yyVAL.union = yyLOCAL - case 1583: + case 1592: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL []*tree.Partition -//line mysql_sql.y:11042 +//line mysql_sql.y:11090 { yyLOCAL = []*tree.Partition{yyDollar[1].partitionUnion()} } yyVAL.union = yyLOCAL - case 1584: + case 1593: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL []*tree.Partition -//line mysql_sql.y:11046 +//line mysql_sql.y:11094 { yyLOCAL = append(yyDollar[1].partitionsUnion(), yyDollar[3].partitionUnion()) } yyVAL.union = yyLOCAL - case 1585: + case 1594: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL *tree.Partition -//line mysql_sql.y:11052 +//line mysql_sql.y:11100 { var Name = tree.Identifier(yyDollar[2].cstrUnion().Compare()) var Values = yyDollar[3].valuesUnion() @@ -28159,10 +28288,10 @@ yydefault: ) } yyVAL.union = yyLOCAL - case 1586: + case 1595: yyDollar = yyS[yypt-5 : yypt+1] var yyLOCAL *tree.Partition -//line mysql_sql.y:11065 +//line mysql_sql.y:11113 { var Name = tree.Identifier(yyDollar[2].cstrUnion().Compare()) var Values = yyDollar[3].valuesUnion() @@ -28176,42 +28305,42 @@ yydefault: ) } yyVAL.union = yyLOCAL - case 1587: + case 1596: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL []*tree.SubPartition -//line mysql_sql.y:11079 +//line mysql_sql.y:11127 { yyLOCAL = nil } yyVAL.union = yyLOCAL - case 1588: + case 1597: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL []*tree.SubPartition -//line mysql_sql.y:11083 +//line mysql_sql.y:11131 { yyLOCAL = yyDollar[2].subPartitionsUnion() } yyVAL.union = yyLOCAL - case 1589: + case 1598: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL []*tree.SubPartition -//line mysql_sql.y:11089 +//line mysql_sql.y:11137 { yyLOCAL = []*tree.SubPartition{yyDollar[1].subPartitionUnion()} } yyVAL.union = yyLOCAL - case 1590: + case 1599: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL []*tree.SubPartition -//line mysql_sql.y:11093 +//line mysql_sql.y:11141 { yyLOCAL = append(yyDollar[1].subPartitionsUnion(), yyDollar[3].subPartitionUnion()) } yyVAL.union = yyLOCAL - case 1591: + case 1600: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL *tree.SubPartition -//line mysql_sql.y:11099 +//line mysql_sql.y:11147 { var Name = tree.Identifier(yyDollar[2].cstrUnion().Compare()) var Options []tree.TableOption @@ -28221,10 +28350,10 @@ yydefault: ) } yyVAL.union = yyLOCAL - case 1592: + case 1601: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL *tree.SubPartition -//line mysql_sql.y:11108 +//line mysql_sql.y:11156 { var Name = tree.Identifier(yyDollar[2].cstrUnion().Compare()) var Options = yyDollar[3].tableOptionsUnion() @@ -28234,26 +28363,26 @@ yydefault: ) } yyVAL.union = yyLOCAL - case 1593: + case 1602: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL []tree.TableOption -//line mysql_sql.y:11119 +//line mysql_sql.y:11167 { yyLOCAL = []tree.TableOption{yyDollar[1].tableOptionUnion()} } yyVAL.union = yyLOCAL - case 1594: + case 1603: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL []tree.TableOption -//line mysql_sql.y:11123 +//line mysql_sql.y:11171 { yyLOCAL = append(yyDollar[1].tableOptionsUnion(), yyDollar[2].tableOptionUnion()) } yyVAL.union = yyLOCAL - case 1595: + case 1604: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.TableOption -//line mysql_sql.y:11129 +//line mysql_sql.y:11177 { if option, ok := yyDollar[1].tableOptionUnion().(*tree.TableOptionAutoIDCache); ok { option.Free() @@ -28263,37 +28392,37 @@ yydefault: yyLOCAL = yyDollar[1].tableOptionUnion() } yyVAL.union = yyLOCAL - case 1596: + case 1605: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL tree.Values -//line mysql_sql.y:11139 +//line mysql_sql.y:11187 { yyLOCAL = nil } yyVAL.union = yyLOCAL - case 1597: + case 1606: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL tree.Values -//line mysql_sql.y:11143 +//line mysql_sql.y:11191 { expr := tree.NewMaxValue() var valueList = tree.Exprs{expr} yyLOCAL = tree.NewValuesLessThan(valueList) } yyVAL.union = yyLOCAL - case 1598: + case 1607: yyDollar = yyS[yypt-6 : yypt+1] var yyLOCAL tree.Values -//line mysql_sql.y:11149 +//line mysql_sql.y:11197 { var valueList = yyDollar[5].exprsUnion() yyLOCAL = tree.NewValuesLessThan(valueList) } yyVAL.union = yyLOCAL - case 1599: + case 1608: yyDollar = yyS[yypt-5 : yypt+1] var yyLOCAL tree.Values -//line mysql_sql.y:11154 +//line mysql_sql.y:11202 { var valueList = yyDollar[4].exprsUnion() yyLOCAL = tree.NewValuesIn( @@ -28301,18 +28430,18 @@ yydefault: ) } yyVAL.union = yyLOCAL - case 1600: + case 1609: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL int64 -//line mysql_sql.y:11162 +//line mysql_sql.y:11210 { yyLOCAL = 0 } yyVAL.union = yyLOCAL - case 1601: + case 1610: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL int64 -//line mysql_sql.y:11166 +//line mysql_sql.y:11214 { res := yyDollar[2].item.(int64) if res == 0 { @@ -28322,18 +28451,18 @@ yydefault: yyLOCAL = res } yyVAL.union = yyLOCAL - case 1602: + case 1611: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL int64 -//line mysql_sql.y:11176 +//line mysql_sql.y:11224 { yyLOCAL = 0 } yyVAL.union = yyLOCAL - case 1603: + case 1612: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL int64 -//line mysql_sql.y:11180 +//line mysql_sql.y:11228 { res := yyDollar[2].item.(int64) if res == 0 { @@ -28343,10 +28472,10 @@ yydefault: yyLOCAL = res } yyVAL.union = yyLOCAL - case 1604: + case 1613: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL *tree.PartitionBy -//line mysql_sql.y:11191 +//line mysql_sql.y:11239 { rangeTyp := tree.NewRangeType() rangeTyp.Expr = yyDollar[3].exprUnion() @@ -28355,10 +28484,10 @@ yydefault: ) } yyVAL.union = yyLOCAL - case 1605: + case 1614: yyDollar = yyS[yypt-5 : yypt+1] var yyLOCAL *tree.PartitionBy -//line mysql_sql.y:11199 +//line mysql_sql.y:11247 { rangeTyp := tree.NewRangeType() rangeTyp.ColumnList = yyDollar[4].unresolveNamesUnion() @@ -28367,10 +28496,10 @@ yydefault: ) } yyVAL.union = yyLOCAL - case 1606: + case 1615: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL *tree.PartitionBy -//line mysql_sql.y:11207 +//line mysql_sql.y:11255 { listTyp := tree.NewListType() listTyp.Expr = yyDollar[3].exprUnion() @@ -28379,10 +28508,10 @@ yydefault: ) } yyVAL.union = yyLOCAL - case 1607: + case 1616: yyDollar = yyS[yypt-5 : yypt+1] var yyLOCAL *tree.PartitionBy -//line mysql_sql.y:11215 +//line mysql_sql.y:11263 { listTyp := tree.NewListType() listTyp.ColumnList = yyDollar[4].unresolveNamesUnion() @@ -28391,10 +28520,10 @@ yydefault: ) } yyVAL.union = yyLOCAL - case 1609: + case 1618: yyDollar = yyS[yypt-5 : yypt+1] var yyLOCAL *tree.PartitionBy -//line mysql_sql.y:11226 +//line mysql_sql.y:11274 { keyTyp := tree.NewKeyType() keyTyp.Linear = yyDollar[1].boolValUnion() @@ -28404,10 +28533,10 @@ yydefault: ) } yyVAL.union = yyLOCAL - case 1610: + case 1619: yyDollar = yyS[yypt-6 : yypt+1] var yyLOCAL *tree.PartitionBy -//line mysql_sql.y:11235 +//line mysql_sql.y:11283 { keyTyp := tree.NewKeyType() keyTyp.Linear = yyDollar[1].boolValUnion() @@ -28418,10 +28547,10 @@ yydefault: ) } yyVAL.union = yyLOCAL - case 1611: + case 1620: yyDollar = yyS[yypt-5 : yypt+1] var yyLOCAL *tree.PartitionBy -//line mysql_sql.y:11245 +//line mysql_sql.y:11293 { Linear := yyDollar[1].boolValUnion() Expr := yyDollar[4].exprUnion() @@ -28431,728 +28560,728 @@ yydefault: ) } yyVAL.union = yyLOCAL - case 1612: + case 1621: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL int64 -//line mysql_sql.y:11255 +//line mysql_sql.y:11303 { yyLOCAL = 2 } yyVAL.union = yyLOCAL - case 1613: + case 1622: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL int64 -//line mysql_sql.y:11259 +//line mysql_sql.y:11307 { yyLOCAL = yyDollar[3].item.(int64) } yyVAL.union = yyLOCAL - case 1614: + case 1623: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL bool -//line mysql_sql.y:11264 +//line mysql_sql.y:11312 { yyLOCAL = false } yyVAL.union = yyLOCAL - case 1615: + case 1624: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL bool -//line mysql_sql.y:11268 +//line mysql_sql.y:11316 { yyLOCAL = true } yyVAL.union = yyLOCAL - case 1616: + case 1625: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL *tree.IcebergTableParam -//line mysql_sql.y:11274 +//line mysql_sql.y:11322 { yyLOCAL = tree.NewIcebergTableParam(yyDollar[4].icebergOptionsUnion()) } yyVAL.union = yyLOCAL - case 1617: + case 1626: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL tree.IcebergOptions -//line mysql_sql.y:11279 +//line mysql_sql.y:11327 { yyLOCAL = nil } yyVAL.union = yyLOCAL - case 1618: + case 1627: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL tree.IcebergOptions -//line mysql_sql.y:11283 +//line mysql_sql.y:11331 { yyLOCAL = yyDollar[3].icebergOptionsUnion() } yyVAL.union = yyLOCAL - case 1619: + case 1628: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.IcebergOptions -//line mysql_sql.y:11289 +//line mysql_sql.y:11337 { yyLOCAL = tree.IcebergOptions{yyDollar[1].icebergOptionUnion()} } yyVAL.union = yyLOCAL - case 1620: + case 1629: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL tree.IcebergOptions -//line mysql_sql.y:11293 +//line mysql_sql.y:11341 { yyLOCAL = append(yyDollar[1].icebergOptionsUnion(), yyDollar[3].icebergOptionUnion()) } yyVAL.union = yyLOCAL - case 1621: + case 1630: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL *tree.IcebergOption -//line mysql_sql.y:11299 +//line mysql_sql.y:11347 { yyLOCAL = tree.NewIcebergOption(tree.Identifier(yyDollar[1].str), yyDollar[3].str) } yyVAL.union = yyLOCAL - case 1622: + case 1631: yyDollar = yyS[yypt-1 : yypt+1] -//line mysql_sql.y:11305 +//line mysql_sql.y:11353 { yyVAL.str = yyDollar[1].cstrUnion().Compare() } - case 1623: + case 1632: yyDollar = yyS[yypt-1 : yypt+1] -//line mysql_sql.y:11309 +//line mysql_sql.y:11357 { yyVAL.str = yyDollar[1].str } - case 1624: + case 1633: yyDollar = yyS[yypt-1 : yypt+1] -//line mysql_sql.y:11315 +//line mysql_sql.y:11363 { yyVAL.str = yyDollar[1].cstrUnion().Compare() } - case 1625: + case 1634: yyDollar = yyS[yypt-1 : yypt+1] -//line mysql_sql.y:11319 +//line mysql_sql.y:11367 { yyVAL.str = yyDollar[1].str } - case 1626: + case 1635: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL *tree.MongoDBTableParam -//line mysql_sql.y:11325 +//line mysql_sql.y:11373 { yyLOCAL = tree.NewMongoDBTableParam(yyDollar[4].mongodbOptionsUnion()) } yyVAL.union = yyLOCAL - case 1627: + case 1636: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL tree.MongoDBOptions -//line mysql_sql.y:11330 +//line mysql_sql.y:11378 { yyLOCAL = nil } yyVAL.union = yyLOCAL - case 1628: + case 1637: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL tree.MongoDBOptions -//line mysql_sql.y:11334 +//line mysql_sql.y:11382 { yyLOCAL = yyDollar[3].mongodbOptionsUnion() } yyVAL.union = yyLOCAL - case 1629: + case 1638: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.MongoDBOptions -//line mysql_sql.y:11340 +//line mysql_sql.y:11388 { yyLOCAL = tree.MongoDBOptions{yyDollar[1].mongodbOptionUnion()} } yyVAL.union = yyLOCAL - case 1630: + case 1639: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL tree.MongoDBOptions -//line mysql_sql.y:11344 +//line mysql_sql.y:11392 { yyLOCAL = append(yyDollar[1].mongodbOptionsUnion(), yyDollar[3].mongodbOptionUnion()) } yyVAL.union = yyLOCAL - case 1631: + case 1640: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL *tree.MongoDBOption -//line mysql_sql.y:11350 +//line mysql_sql.y:11398 { yyLOCAL = tree.NewMongoDBOption(tree.Identifier(yyDollar[1].str), yyDollar[3].str) } yyVAL.union = yyLOCAL - case 1632: + case 1641: yyDollar = yyS[yypt-1 : yypt+1] -//line mysql_sql.y:11356 +//line mysql_sql.y:11404 { yyVAL.str = yyDollar[1].cstrUnion().Compare() } - case 1633: + case 1642: yyDollar = yyS[yypt-1 : yypt+1] -//line mysql_sql.y:11360 +//line mysql_sql.y:11408 { yyVAL.str = yyDollar[1].str } - case 1634: + case 1643: yyDollar = yyS[yypt-1 : yypt+1] -//line mysql_sql.y:11366 +//line mysql_sql.y:11414 { yyVAL.str = yyDollar[1].cstrUnion().Compare() } - case 1635: + case 1644: yyDollar = yyS[yypt-1 : yypt+1] -//line mysql_sql.y:11370 +//line mysql_sql.y:11418 { yyVAL.str = yyDollar[1].str } - case 1636: + case 1645: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL *tree.DataStreamTableParam -//line mysql_sql.y:11376 +//line mysql_sql.y:11424 { yyLOCAL = tree.NewDataStreamTableParam(yyDollar[4].datastreamOptionsUnion()) } yyVAL.union = yyLOCAL - case 1637: + case 1646: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL tree.DataStreamOptions -//line mysql_sql.y:11381 +//line mysql_sql.y:11429 { yyLOCAL = nil } yyVAL.union = yyLOCAL - case 1638: + case 1647: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL tree.DataStreamOptions -//line mysql_sql.y:11385 +//line mysql_sql.y:11433 { yyLOCAL = yyDollar[3].datastreamOptionsUnion() } yyVAL.union = yyLOCAL - case 1639: + case 1648: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.DataStreamOptions -//line mysql_sql.y:11391 +//line mysql_sql.y:11439 { yyLOCAL = tree.DataStreamOptions{yyDollar[1].datastreamOptionUnion()} } yyVAL.union = yyLOCAL - case 1640: + case 1649: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL tree.DataStreamOptions -//line mysql_sql.y:11395 +//line mysql_sql.y:11443 { yyLOCAL = append(yyDollar[1].datastreamOptionsUnion(), yyDollar[3].datastreamOptionUnion()) } yyVAL.union = yyLOCAL - case 1641: + case 1650: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL *tree.DataStreamOption -//line mysql_sql.y:11401 +//line mysql_sql.y:11449 { yyLOCAL = tree.NewDataStreamOption(tree.Identifier(yyDollar[1].str), yyDollar[3].str) } yyVAL.union = yyLOCAL - case 1642: + case 1651: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL *tree.ForeignTableParam -//line mysql_sql.y:11407 +//line mysql_sql.y:11455 { yyLOCAL = tree.NewForeignTableParam(yyDollar[3].str, yyDollar[4].foreignTableOptionsUnion()) } yyVAL.union = yyLOCAL - case 1643: + case 1652: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL *tree.KafkaTableParam -//line mysql_sql.y:11413 +//line mysql_sql.y:11461 { yyLOCAL = tree.NewKafkaTableParam(yyDollar[4].kafkaTableOptionsUnion()) } yyVAL.union = yyLOCAL - case 1644: + case 1653: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL tree.KafkaTableOptions -//line mysql_sql.y:11418 +//line mysql_sql.y:11466 { yyLOCAL = nil } yyVAL.union = yyLOCAL - case 1645: + case 1654: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL tree.KafkaTableOptions -//line mysql_sql.y:11422 +//line mysql_sql.y:11470 { yyLOCAL = yyDollar[3].kafkaTableOptionsUnion() } yyVAL.union = yyLOCAL - case 1646: + case 1655: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.KafkaTableOptions -//line mysql_sql.y:11428 +//line mysql_sql.y:11476 { yyLOCAL = tree.KafkaTableOptions{yyDollar[1].kafkaTableOptionUnion()} } yyVAL.union = yyLOCAL - case 1647: + case 1656: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL tree.KafkaTableOptions -//line mysql_sql.y:11432 +//line mysql_sql.y:11480 { yyLOCAL = append(yyDollar[1].kafkaTableOptionsUnion(), yyDollar[3].kafkaTableOptionUnion()) } yyVAL.union = yyLOCAL - case 1648: + case 1657: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL *tree.KafkaTableOption -//line mysql_sql.y:11438 +//line mysql_sql.y:11486 { yyLOCAL = tree.NewKafkaTableOption(tree.Identifier(yyDollar[1].str), yyDollar[3].str) } yyVAL.union = yyLOCAL - case 1649: + case 1658: yyDollar = yyS[yypt-1 : yypt+1] -//line mysql_sql.y:11444 +//line mysql_sql.y:11492 { yyVAL.str = "esql" } - case 1650: + case 1659: yyDollar = yyS[yypt-1 : yypt+1] -//line mysql_sql.y:11448 +//line mysql_sql.y:11496 { yyVAL.str = "sql" } - case 1651: + case 1660: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL tree.ForeignTableOptions -//line mysql_sql.y:11453 +//line mysql_sql.y:11501 { yyLOCAL = nil } yyVAL.union = yyLOCAL - case 1652: + case 1661: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL tree.ForeignTableOptions -//line mysql_sql.y:11457 +//line mysql_sql.y:11505 { yyLOCAL = yyDollar[3].foreignTableOptionsUnion() } yyVAL.union = yyLOCAL - case 1653: + case 1662: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.ForeignTableOptions -//line mysql_sql.y:11463 +//line mysql_sql.y:11511 { yyLOCAL = tree.ForeignTableOptions{yyDollar[1].foreignTableOptionUnion()} } yyVAL.union = yyLOCAL - case 1654: + case 1663: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL tree.ForeignTableOptions -//line mysql_sql.y:11467 +//line mysql_sql.y:11515 { yyLOCAL = append(yyDollar[1].foreignTableOptionsUnion(), yyDollar[3].foreignTableOptionUnion()) } yyVAL.union = yyLOCAL - case 1655: + case 1664: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL tree.ForeignTableOption -//line mysql_sql.y:11473 +//line mysql_sql.y:11521 { yyLOCAL = tree.NewForeignTableOption(tree.Identifier(yyDollar[1].str), yyDollar[3].str) } yyVAL.union = yyLOCAL - case 1656: + case 1665: yyDollar = yyS[yypt-1 : yypt+1] -//line mysql_sql.y:11479 +//line mysql_sql.y:11527 { yyVAL.str = yyDollar[1].cstrUnion().Compare() } - case 1657: + case 1666: yyDollar = yyS[yypt-1 : yypt+1] -//line mysql_sql.y:11483 +//line mysql_sql.y:11531 { yyVAL.str = yyDollar[1].str } - case 1658: + case 1667: yyDollar = yyS[yypt-1 : yypt+1] -//line mysql_sql.y:11489 +//line mysql_sql.y:11537 { yyVAL.str = yyDollar[1].cstrUnion().Compare() } - case 1659: + case 1668: yyDollar = yyS[yypt-1 : yypt+1] -//line mysql_sql.y:11493 +//line mysql_sql.y:11541 { yyVAL.str = yyDollar[1].str } - case 1660: + case 1669: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL []tree.TableOption -//line mysql_sql.y:11498 +//line mysql_sql.y:11546 { yyLOCAL = nil } yyVAL.union = yyLOCAL - case 1661: + case 1670: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL []tree.TableOption -//line mysql_sql.y:11502 +//line mysql_sql.y:11550 { yyLOCAL = yyDollar[1].tableOptionsUnion() } yyVAL.union = yyLOCAL - case 1662: + case 1671: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL []tree.TableOption -//line mysql_sql.y:11508 +//line mysql_sql.y:11556 { yyLOCAL = []tree.TableOption{yyDollar[1].tableOptionUnion()} } yyVAL.union = yyLOCAL - case 1663: + case 1672: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL []tree.TableOption -//line mysql_sql.y:11512 +//line mysql_sql.y:11560 { yyLOCAL = append(yyDollar[1].tableOptionsUnion(), yyDollar[3].tableOptionUnion()) } yyVAL.union = yyLOCAL - case 1664: + case 1673: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL []tree.TableOption -//line mysql_sql.y:11516 +//line mysql_sql.y:11564 { yyLOCAL = append(yyDollar[1].tableOptionsUnion(), yyDollar[2].tableOptionUnion()) } yyVAL.union = yyLOCAL - case 1665: + case 1674: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL tree.TableOption -//line mysql_sql.y:11522 +//line mysql_sql.y:11570 { yyLOCAL = tree.NewTableOptionAUTOEXTEND_SIZE(uint64(yyDollar[3].item.(int64))) } yyVAL.union = yyLOCAL - case 1666: + case 1675: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL tree.TableOption -//line mysql_sql.y:11526 +//line mysql_sql.y:11574 { yyLOCAL = tree.NewTableOptionAutoIncrement(integralToUint64(yyDollar[3].item)) } yyVAL.union = yyLOCAL - case 1667: + case 1676: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL tree.TableOption -//line mysql_sql.y:11530 +//line mysql_sql.y:11578 { yyLOCAL = tree.NewTableOptionAutoIDCache(integralToUint64(yyDollar[3].item)) } yyVAL.union = yyLOCAL - case 1668: + case 1677: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL tree.TableOption -//line mysql_sql.y:11534 +//line mysql_sql.y:11582 { yyLOCAL = tree.NewTableOptionAvgRowLength(uint64(yyDollar[3].item.(int64))) } yyVAL.union = yyLOCAL - case 1669: + case 1678: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL tree.TableOption -//line mysql_sql.y:11538 +//line mysql_sql.y:11586 { yyLOCAL = tree.NewTableOptionCharset(yyDollar[4].str) } yyVAL.union = yyLOCAL - case 1670: + case 1679: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL tree.TableOption -//line mysql_sql.y:11542 +//line mysql_sql.y:11590 { yyLOCAL = tree.NewTableOptionCollate(yyDollar[4].str) } yyVAL.union = yyLOCAL - case 1671: + case 1680: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL tree.TableOption -//line mysql_sql.y:11546 +//line mysql_sql.y:11594 { yyLOCAL = tree.NewTableOptionChecksum(uint64(yyDollar[3].item.(int64))) } yyVAL.union = yyLOCAL - case 1672: + case 1681: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL tree.TableOption -//line mysql_sql.y:11550 +//line mysql_sql.y:11598 { str := util.DealCommentString(yyDollar[3].str) yyLOCAL = tree.NewTableOptionComment(str) } yyVAL.union = yyLOCAL - case 1673: + case 1682: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL tree.TableOption -//line mysql_sql.y:11555 +//line mysql_sql.y:11603 { yyLOCAL = tree.NewTableOptionCompression(yyDollar[3].str) } yyVAL.union = yyLOCAL - case 1674: + case 1683: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL tree.TableOption -//line mysql_sql.y:11559 +//line mysql_sql.y:11607 { yyLOCAL = tree.NewTableOptionConnection(yyDollar[3].str) } yyVAL.union = yyLOCAL - case 1675: + case 1684: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL tree.TableOption -//line mysql_sql.y:11563 +//line mysql_sql.y:11611 { yyLOCAL = tree.NewTableOptionDataDirectory(yyDollar[4].str) } yyVAL.union = yyLOCAL - case 1676: + case 1685: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL tree.TableOption -//line mysql_sql.y:11567 +//line mysql_sql.y:11615 { yyLOCAL = tree.NewTableOptionIndexDirectory(yyDollar[4].str) } yyVAL.union = yyLOCAL - case 1677: + case 1686: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL tree.TableOption -//line mysql_sql.y:11571 +//line mysql_sql.y:11619 { yyLOCAL = tree.NewTableOptionDelayKeyWrite(uint64(yyDollar[3].item.(int64))) } yyVAL.union = yyLOCAL - case 1678: + case 1687: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL tree.TableOption -//line mysql_sql.y:11575 +//line mysql_sql.y:11623 { yyLOCAL = tree.NewTableOptionEncryption(yyDollar[3].str) } yyVAL.union = yyLOCAL - case 1679: + case 1688: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL tree.TableOption -//line mysql_sql.y:11579 +//line mysql_sql.y:11627 { yyLOCAL = tree.NewTableOptionEngine(yyDollar[3].str) } yyVAL.union = yyLOCAL - case 1680: + case 1689: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL tree.TableOption -//line mysql_sql.y:11583 +//line mysql_sql.y:11631 { yyLOCAL = tree.NewTableOptionEngineAttr(yyDollar[3].str) } yyVAL.union = yyLOCAL - case 1681: + case 1690: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL tree.TableOption -//line mysql_sql.y:11587 +//line mysql_sql.y:11635 { yyLOCAL = tree.NewTableOptionInsertMethod(yyDollar[3].str) } yyVAL.union = yyLOCAL - case 1682: + case 1691: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL tree.TableOption -//line mysql_sql.y:11591 +//line mysql_sql.y:11639 { yyLOCAL = tree.NewTableOptionKeyBlockSize(uint64(yyDollar[3].item.(int64))) } yyVAL.union = yyLOCAL - case 1683: + case 1692: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL tree.TableOption -//line mysql_sql.y:11595 +//line mysql_sql.y:11643 { yyLOCAL = tree.NewTableOptionMaxRows(uint64(yyDollar[3].item.(int64))) } yyVAL.union = yyLOCAL - case 1684: + case 1693: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL tree.TableOption -//line mysql_sql.y:11599 +//line mysql_sql.y:11647 { yyLOCAL = tree.NewTableOptionMinRows(uint64(yyDollar[3].item.(int64))) } yyVAL.union = yyLOCAL - case 1685: + case 1694: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL tree.TableOption -//line mysql_sql.y:11603 +//line mysql_sql.y:11651 { t := tree.NewTableOptionPackKeys() t.Value = yyDollar[3].item.(int64) yyLOCAL = t } yyVAL.union = yyLOCAL - case 1686: + case 1695: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL tree.TableOption -//line mysql_sql.y:11609 +//line mysql_sql.y:11657 { t := tree.NewTableOptionPackKeys() t.Default = true yyLOCAL = t } yyVAL.union = yyLOCAL - case 1687: + case 1696: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL tree.TableOption -//line mysql_sql.y:11615 +//line mysql_sql.y:11663 { yyLOCAL = tree.NewTableOptionPassword(yyDollar[3].str) } yyVAL.union = yyLOCAL - case 1688: + case 1697: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL tree.TableOption -//line mysql_sql.y:11619 +//line mysql_sql.y:11667 { yyLOCAL = tree.NewTableOptionRowFormat(yyDollar[3].rowFormatTypeUnion()) } yyVAL.union = yyLOCAL - case 1689: + case 1698: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL tree.TableOption -//line mysql_sql.y:11623 +//line mysql_sql.y:11671 { yyLOCAL = tree.NewTTableOptionStartTrans(true) } yyVAL.union = yyLOCAL - case 1690: + case 1699: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL tree.TableOption -//line mysql_sql.y:11627 +//line mysql_sql.y:11675 { yyLOCAL = tree.NewTTableOptionSecondaryEngineAttr(yyDollar[3].str) } yyVAL.union = yyLOCAL - case 1691: + case 1700: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL tree.TableOption -//line mysql_sql.y:11631 +//line mysql_sql.y:11679 { t := tree.NewTableOptionStatsAutoRecalc() t.Value = uint64(yyDollar[3].item.(int64)) yyLOCAL = t } yyVAL.union = yyLOCAL - case 1692: + case 1701: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL tree.TableOption -//line mysql_sql.y:11637 +//line mysql_sql.y:11685 { t := tree.NewTableOptionStatsAutoRecalc() t.Default = true yyLOCAL = t } yyVAL.union = yyLOCAL - case 1693: + case 1702: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL tree.TableOption -//line mysql_sql.y:11643 +//line mysql_sql.y:11691 { t := tree.NewTableOptionStatsPersistent() t.Value = uint64(yyDollar[3].item.(int64)) yyLOCAL = t } yyVAL.union = yyLOCAL - case 1694: + case 1703: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL tree.TableOption -//line mysql_sql.y:11649 +//line mysql_sql.y:11697 { t := tree.NewTableOptionStatsPersistent() t.Default = true yyLOCAL = t } yyVAL.union = yyLOCAL - case 1695: + case 1704: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL tree.TableOption -//line mysql_sql.y:11655 +//line mysql_sql.y:11703 { t := tree.NewTableOptionStatsSamplePages() t.Value = uint64(yyDollar[3].item.(int64)) yyLOCAL = t } yyVAL.union = yyLOCAL - case 1696: + case 1705: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL tree.TableOption -//line mysql_sql.y:11661 +//line mysql_sql.y:11709 { t := tree.NewTableOptionStatsSamplePages() t.Default = true yyLOCAL = t } yyVAL.union = yyLOCAL - case 1697: + case 1706: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL tree.TableOption -//line mysql_sql.y:11667 +//line mysql_sql.y:11715 { yyLOCAL = tree.NewTableOptionTablespace(yyDollar[3].cstrUnion().Compare(), "") } yyVAL.union = yyLOCAL - case 1698: + case 1707: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.TableOption -//line mysql_sql.y:11671 +//line mysql_sql.y:11719 { yyLOCAL = tree.NewTableOptionTablespace("", yyDollar[1].str) } yyVAL.union = yyLOCAL - case 1699: + case 1708: yyDollar = yyS[yypt-5 : yypt+1] var yyLOCAL tree.TableOption -//line mysql_sql.y:11675 +//line mysql_sql.y:11723 { yyLOCAL = tree.NewTableOptionUnion(yyDollar[4].tableNamesUnion()) } yyVAL.union = yyLOCAL - case 1700: + case 1709: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL tree.TableOption -//line mysql_sql.y:11679 +//line mysql_sql.y:11727 { var Preperties = yyDollar[3].propertiesUnion() yyLOCAL = tree.NewTableOptionProperties(Preperties) } yyVAL.union = yyLOCAL - case 1701: + case 1710: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL []tree.Property -//line mysql_sql.y:11686 +//line mysql_sql.y:11734 { yyLOCAL = []tree.Property{yyDollar[1].propertyUnion()} } yyVAL.union = yyLOCAL - case 1702: + case 1711: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL []tree.Property -//line mysql_sql.y:11690 +//line mysql_sql.y:11738 { yyLOCAL = append(yyDollar[1].propertiesUnion(), yyDollar[3].propertyUnion()) } yyVAL.union = yyLOCAL - case 1703: + case 1712: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL tree.Property -//line mysql_sql.y:11696 +//line mysql_sql.y:11744 { var Key = yyDollar[1].str var Value = yyDollar[3].str @@ -29162,86 +29291,86 @@ yydefault: ) } yyVAL.union = yyLOCAL - case 1704: + case 1713: yyDollar = yyS[yypt-2 : yypt+1] -//line mysql_sql.y:11707 +//line mysql_sql.y:11755 { yyVAL.str = " " + yyDollar[1].str + " " + yyDollar[2].str } - case 1705: + case 1714: yyDollar = yyS[yypt-2 : yypt+1] -//line mysql_sql.y:11711 +//line mysql_sql.y:11759 { yyVAL.str = " " + yyDollar[1].str + " " + yyDollar[2].str } - case 1706: + case 1715: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.RowFormatType -//line mysql_sql.y:11717 +//line mysql_sql.y:11765 { yyLOCAL = tree.ROW_FORMAT_DEFAULT } yyVAL.union = yyLOCAL - case 1707: + case 1716: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.RowFormatType -//line mysql_sql.y:11721 +//line mysql_sql.y:11769 { yyLOCAL = tree.ROW_FORMAT_DYNAMIC } yyVAL.union = yyLOCAL - case 1708: + case 1717: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.RowFormatType -//line mysql_sql.y:11725 +//line mysql_sql.y:11773 { yyLOCAL = tree.ROW_FORMAT_FIXED } yyVAL.union = yyLOCAL - case 1709: + case 1718: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.RowFormatType -//line mysql_sql.y:11729 +//line mysql_sql.y:11777 { yyLOCAL = tree.ROW_FORMAT_COMPRESSED } yyVAL.union = yyLOCAL - case 1710: + case 1719: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.RowFormatType -//line mysql_sql.y:11733 +//line mysql_sql.y:11781 { yyLOCAL = tree.ROW_FORMAT_REDUNDANT } yyVAL.union = yyLOCAL - case 1711: + case 1720: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.RowFormatType -//line mysql_sql.y:11737 +//line mysql_sql.y:11785 { yyLOCAL = tree.ROW_FORMAT_COMPACT } yyVAL.union = yyLOCAL - case 1716: + case 1725: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.TableNames -//line mysql_sql.y:11751 +//line mysql_sql.y:11799 { yyLOCAL = tree.TableNames{yyDollar[1].tableNameUnion()} } yyVAL.union = yyLOCAL - case 1717: + case 1726: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL tree.TableNames -//line mysql_sql.y:11755 +//line mysql_sql.y:11803 { yyLOCAL = append(yyDollar[1].tableNamesUnion(), yyDollar[3].tableNameUnion()) } yyVAL.union = yyLOCAL - case 1718: + case 1727: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL *tree.TableName -//line mysql_sql.y:11764 +//line mysql_sql.y:11812 { tblName := yylex.(*Lexer).GetDbOrTblName(yyDollar[1].cstrUnion().Origin()) prefix := tree.ObjectNamePrefix{ExplicitSchema: false} @@ -29249,10 +29378,10 @@ yydefault: yyLOCAL.IcebergRef = yyDollar[3].icebergRefSpecUnion() } yyVAL.union = yyLOCAL - case 1719: + case 1728: yyDollar = yyS[yypt-5 : yypt+1] var yyLOCAL *tree.TableName -//line mysql_sql.y:11771 +//line mysql_sql.y:11819 { dbName := yylex.(*Lexer).GetDbOrTblName(yyDollar[1].cstrUnion().Origin()) tblName := yylex.(*Lexer).GetDbOrTblName(yyDollar[3].cstrUnion().Origin()) @@ -29261,85 +29390,85 @@ yydefault: yyLOCAL.IcebergRef = yyDollar[5].icebergRefSpecUnion() } yyVAL.union = yyLOCAL - case 1720: + case 1729: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL *tree.IcebergRefSpec -//line mysql_sql.y:11780 +//line mysql_sql.y:11828 { yyLOCAL = nil } yyVAL.union = yyLOCAL - case 1721: + case 1730: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL *tree.IcebergRefSpec -//line mysql_sql.y:11784 +//line mysql_sql.y:11832 { raw := fmt.Sprintf("%v", yyDollar[3].item) yyLOCAL = tree.NewIcebergSnapshotRef(tree.NewNumVal(raw, raw, false, tree.P_int64)) } yyVAL.union = yyLOCAL - case 1722: + case 1731: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL *tree.IcebergRefSpec -//line mysql_sql.y:11789 +//line mysql_sql.y:11837 { yyLOCAL = tree.NewIcebergSnapshotRef(tree.NewNumVal(yyDollar[3].str, yyDollar[3].str, false, tree.P_char)) } yyVAL.union = yyLOCAL - case 1723: + case 1732: yyDollar = yyS[yypt-6 : yypt+1] var yyLOCAL *tree.IcebergRefSpec -//line mysql_sql.y:11793 +//line mysql_sql.y:11841 { yyLOCAL = tree.NewIcebergTimestampRef(tree.NewNumVal(yyDollar[6].str, yyDollar[6].str, false, tree.P_char)) } yyVAL.union = yyLOCAL - case 1724: + case 1733: yyDollar = yyS[yypt-5 : yypt+1] var yyLOCAL *tree.IcebergRefSpec -//line mysql_sql.y:11797 +//line mysql_sql.y:11845 { yyLOCAL = tree.NewIcebergTimestampRef(tree.NewNumVal(yyDollar[5].str, yyDollar[5].str, false, tree.P_char)) } yyVAL.union = yyLOCAL - case 1725: + case 1734: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL *tree.IcebergRefSpec -//line mysql_sql.y:11801 +//line mysql_sql.y:11849 { yyLOCAL = tree.NewIcebergNamedRef(tree.Identifier(yyDollar[3].str)) } yyVAL.union = yyLOCAL - case 1726: + case 1735: yyDollar = yyS[yypt-1 : yypt+1] -//line mysql_sql.y:11807 +//line mysql_sql.y:11855 { yyVAL.str = yyDollar[1].str } - case 1727: + case 1736: yyDollar = yyS[yypt-1 : yypt+1] -//line mysql_sql.y:11811 +//line mysql_sql.y:11859 { yyVAL.str = yyDollar[1].str } - case 1728: + case 1737: yyDollar = yyS[yypt-1 : yypt+1] -//line mysql_sql.y:11815 +//line mysql_sql.y:11863 { yyVAL.str = yyDollar[1].str } - case 1729: + case 1738: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL *tree.AtTimeStamp -//line mysql_sql.y:11820 +//line mysql_sql.y:11868 { yyLOCAL = nil } yyVAL.union = yyLOCAL - case 1730: + case 1739: yyDollar = yyS[yypt-5 : yypt+1] var yyLOCAL *tree.AtTimeStamp -//line mysql_sql.y:11824 +//line mysql_sql.y:11872 { yyLOCAL = &tree.AtTimeStamp{ Type: tree.ATTIMESTAMPTIME, @@ -29347,10 +29476,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 1731: + case 1740: yyDollar = yyS[yypt-5 : yypt+1] var yyLOCAL *tree.AtTimeStamp -//line mysql_sql.y:11831 +//line mysql_sql.y:11879 { var str = yyDollar[4].cstrUnion().Compare() yyLOCAL = &tree.AtTimeStamp{ @@ -29360,10 +29489,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 1732: + case 1741: yyDollar = yyS[yypt-5 : yypt+1] var yyLOCAL *tree.AtTimeStamp -//line mysql_sql.y:11840 +//line mysql_sql.y:11888 { yyLOCAL = &tree.AtTimeStamp{ Type: tree.ATTIMESTAMPSNAPSHOT, @@ -29372,10 +29501,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 1733: + case 1742: yyDollar = yyS[yypt-5 : yypt+1] var yyLOCAL *tree.AtTimeStamp -//line mysql_sql.y:11848 +//line mysql_sql.y:11896 { yyLOCAL = &tree.AtTimeStamp{ Type: tree.ATMOTIMESTAMP, @@ -29383,10 +29512,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 1734: + case 1743: yyDollar = yyS[yypt-6 : yypt+1] var yyLOCAL *tree.AtTimeStamp -//line mysql_sql.y:11855 +//line mysql_sql.y:11903 { yyLOCAL = &tree.AtTimeStamp{ Type: tree.ASOFTIMESTAMP, @@ -29394,74 +29523,74 @@ yydefault: } } yyVAL.union = yyLOCAL - case 1735: + case 1744: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL tree.TableDefs -//line mysql_sql.y:11863 +//line mysql_sql.y:11911 { yyLOCAL = tree.TableDefs(nil) } yyVAL.union = yyLOCAL - case 1737: + case 1746: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.TableDefs -//line mysql_sql.y:11870 +//line mysql_sql.y:11918 { yyLOCAL = tree.TableDefs{yyDollar[1].tableDefUnion()} } yyVAL.union = yyLOCAL - case 1738: + case 1747: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL tree.TableDefs -//line mysql_sql.y:11874 +//line mysql_sql.y:11922 { yyLOCAL = append(yyDollar[1].tableDefsUnion(), yyDollar[3].tableDefUnion()) } yyVAL.union = yyLOCAL - case 1739: + case 1748: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.TableDef -//line mysql_sql.y:11880 +//line mysql_sql.y:11928 { yyLOCAL = tree.TableDef(yyDollar[1].columnTableDefUnion()) } yyVAL.union = yyLOCAL - case 1740: + case 1749: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.TableDef -//line mysql_sql.y:11884 +//line mysql_sql.y:11932 { yyLOCAL = yyDollar[1].tableDefUnion() } yyVAL.union = yyLOCAL - case 1741: + case 1750: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.TableDef -//line mysql_sql.y:11888 +//line mysql_sql.y:11936 { yyLOCAL = yyDollar[1].tableDefUnion() } yyVAL.union = yyLOCAL - case 1742: + case 1751: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.TableDef -//line mysql_sql.y:11894 +//line mysql_sql.y:11942 { yyLOCAL = yyDollar[1].tableDefUnion() } yyVAL.union = yyLOCAL - case 1743: + case 1752: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.TableDef -//line mysql_sql.y:11898 +//line mysql_sql.y:11946 { yyLOCAL = yyDollar[1].tableDefUnion() } yyVAL.union = yyLOCAL - case 1744: + case 1753: yyDollar = yyS[yypt-7 : yypt+1] var yyLOCAL tree.TableDef -//line mysql_sql.y:11904 +//line mysql_sql.y:11952 { var KeyParts = yyDollar[5].keyPartsUnion() var Name = yyDollar[3].str @@ -29475,10 +29604,10 @@ yydefault: ) } yyVAL.union = yyLOCAL - case 1745: + case 1754: yyDollar = yyS[yypt-9 : yypt+1] var yyLOCAL tree.TableDef -//line mysql_sql.y:11917 +//line mysql_sql.y:11965 { var KeyParts = yyDollar[5].keyPartsUnion() var Name = yyDollar[3].str @@ -29492,10 +29621,10 @@ yydefault: ) } yyVAL.union = yyLOCAL - case 1746: + case 1755: yyDollar = yyS[yypt-7 : yypt+1] var yyLOCAL tree.TableDef -//line mysql_sql.y:11930 +//line mysql_sql.y:11978 { var KeyParts = yyDollar[5].keyPartsUnion() var Name = yyDollar[3].str @@ -29511,10 +29640,10 @@ yydefault: yyLOCAL = fti } yyVAL.union = yyLOCAL - case 1747: + case 1756: yyDollar = yyS[yypt-7 : yypt+1] var yyLOCAL tree.TableDef -//line mysql_sql.y:11945 +//line mysql_sql.y:11993 { keyTyp := tree.INDEX_TYPE_INVALID if yyDollar[3].strsUnion()[1] != "" { @@ -29560,10 +29689,10 @@ yydefault: ) } yyVAL.union = yyLOCAL - case 1748: + case 1757: yyDollar = yyS[yypt-9 : yypt+1] var yyLOCAL tree.TableDef -//line mysql_sql.y:11990 +//line mysql_sql.y:12038 { keyTyp := tree.INDEX_TYPE_INVALID if yyDollar[3].strsUnion()[1] != "" { @@ -29608,10 +29737,10 @@ yydefault: ) } yyVAL.union = yyLOCAL - case 1749: + case 1758: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL tree.TableDef -//line mysql_sql.y:12035 +//line mysql_sql.y:12083 { if yyDollar[1].str != "" { switch v := yyDollar[2].tableDefUnion().(type) { @@ -29628,18 +29757,18 @@ yydefault: yyLOCAL = yyDollar[2].tableDefUnion() } yyVAL.union = yyLOCAL - case 1750: + case 1759: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.TableDef -//line mysql_sql.y:12050 +//line mysql_sql.y:12098 { yyLOCAL = yyDollar[1].tableDefUnion() } yyVAL.union = yyLOCAL - case 1751: + case 1760: yyDollar = yyS[yypt-7 : yypt+1] var yyLOCAL tree.TableDef -//line mysql_sql.y:12056 +//line mysql_sql.y:12104 { var KeyParts = yyDollar[5].keyPartsUnion() var Name = yyDollar[3].strsUnion()[0] @@ -29653,10 +29782,10 @@ yydefault: ) } yyVAL.union = yyLOCAL - case 1752: + case 1761: yyDollar = yyS[yypt-9 : yypt+1] var yyLOCAL tree.TableDef -//line mysql_sql.y:12069 +//line mysql_sql.y:12117 { var KeyParts = yyDollar[5].keyPartsUnion() var Name = yyDollar[3].strsUnion()[0] @@ -29670,10 +29799,10 @@ yydefault: ) } yyVAL.union = yyLOCAL - case 1753: + case 1762: yyDollar = yyS[yypt-7 : yypt+1] var yyLOCAL tree.TableDef -//line mysql_sql.y:12082 +//line mysql_sql.y:12130 { var KeyParts = yyDollar[5].keyPartsUnion() var Name = yyDollar[3].strsUnion()[0] @@ -29687,10 +29816,10 @@ yydefault: ) } yyVAL.union = yyLOCAL - case 1754: + case 1763: yyDollar = yyS[yypt-9 : yypt+1] var yyLOCAL tree.TableDef -//line mysql_sql.y:12095 +//line mysql_sql.y:12143 { var KeyParts = yyDollar[5].keyPartsUnion() var Name = yyDollar[3].strsUnion()[0] @@ -29704,10 +29833,10 @@ yydefault: ) } yyVAL.union = yyLOCAL - case 1755: + case 1764: yyDollar = yyS[yypt-8 : yypt+1] var yyLOCAL tree.TableDef -//line mysql_sql.y:12108 +//line mysql_sql.y:12156 { var IfNotExists = yyDollar[3].ifNotExistsUnion() var KeyParts = yyDollar[6].keyPartsUnion() @@ -29723,10 +29852,10 @@ yydefault: ) } yyVAL.union = yyLOCAL - case 1756: + case 1765: yyDollar = yyS[yypt-5 : yypt+1] var yyLOCAL tree.TableDef -//line mysql_sql.y:12123 +//line mysql_sql.y:12171 { var Expr = yyDollar[3].exprUnion() var Enforced = yyDollar[5].boolValUnion() @@ -29736,107 +29865,107 @@ yydefault: ) } yyVAL.union = yyLOCAL - case 1757: + case 1766: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL bool -//line mysql_sql.y:12133 +//line mysql_sql.y:12181 { yyLOCAL = true } yyVAL.union = yyLOCAL - case 1759: + case 1768: yyDollar = yyS[yypt-0 : yypt+1] -//line mysql_sql.y:12139 +//line mysql_sql.y:12187 { yyVAL.str = "" } - case 1760: + case 1769: yyDollar = yyS[yypt-1 : yypt+1] -//line mysql_sql.y:12143 +//line mysql_sql.y:12191 { yyVAL.str = yyDollar[1].str } - case 1763: + case 1772: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL []string -//line mysql_sql.y:12153 +//line mysql_sql.y:12201 { yyLOCAL = make([]string, 2) yyLOCAL[0] = yyDollar[1].str yyLOCAL[1] = "" } yyVAL.union = yyLOCAL - case 1764: + case 1773: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL []string -//line mysql_sql.y:12159 +//line mysql_sql.y:12207 { yyLOCAL = make([]string, 2) yyLOCAL[0] = yyDollar[1].str yyLOCAL[1] = yyDollar[3].str } yyVAL.union = yyLOCAL - case 1765: + case 1774: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL []string -//line mysql_sql.y:12165 +//line mysql_sql.y:12213 { yyLOCAL = make([]string, 2) yyLOCAL[0] = yyDollar[1].cstrUnion().Origin() yyLOCAL[1] = yyDollar[3].str } yyVAL.union = yyLOCAL - case 1780: + case 1789: yyDollar = yyS[yypt-0 : yypt+1] -//line mysql_sql.y:12190 +//line mysql_sql.y:12238 { yyVAL.str = "" } - case 1781: + case 1790: yyDollar = yyS[yypt-1 : yypt+1] -//line mysql_sql.y:12194 +//line mysql_sql.y:12242 { yyVAL.str = yyDollar[1].cstrUnion().Origin() } - case 1782: + case 1791: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL *tree.ColumnTableDef -//line mysql_sql.y:12200 +//line mysql_sql.y:12248 { yyLOCAL = tree.NewColumnTableDef(yyDollar[1].unresolvedNameUnion(), yyDollar[2].columnTypeUnion(), yyDollar[3].columnAttributesUnion()) } yyVAL.union = yyLOCAL - case 1783: + case 1792: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL *tree.UnresolvedName -//line mysql_sql.y:12206 +//line mysql_sql.y:12254 { yyLOCAL = tree.NewUnresolvedName(yyDollar[1].cstrUnion()) } yyVAL.union = yyLOCAL - case 1784: + case 1793: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL *tree.UnresolvedName -//line mysql_sql.y:12210 +//line mysql_sql.y:12258 { tblNameCStr := yylex.(*Lexer).GetDbOrTblNameCStr(yyDollar[1].cstrUnion().Origin()) yyLOCAL = tree.NewUnresolvedName(tblNameCStr, yyDollar[3].cstrUnion()) } yyVAL.union = yyLOCAL - case 1785: + case 1794: yyDollar = yyS[yypt-5 : yypt+1] var yyLOCAL *tree.UnresolvedName -//line mysql_sql.y:12215 +//line mysql_sql.y:12263 { dbNameCStr := yylex.(*Lexer).GetDbOrTblNameCStr(yyDollar[1].cstrUnion().Origin()) tblNameCStr := yylex.(*Lexer).GetDbOrTblNameCStr(yyDollar[3].cstrUnion().Origin()) yyLOCAL = tree.NewUnresolvedName(dbNameCStr, tblNameCStr, yyDollar[5].cstrUnion()) } yyVAL.union = yyLOCAL - case 1786: + case 1795: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL *tree.CStr -//line mysql_sql.y:12223 +//line mysql_sql.y:12271 { if rejectSQLModeReservedFunctionName(yylex, yyDollar[1].str) { goto ret1 @@ -29844,18 +29973,18 @@ yydefault: yyLOCAL = tree.NewCStr(yyDollar[1].str, 1) } yyVAL.union = yyLOCAL - case 1787: + case 1796: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL *tree.CStr -//line mysql_sql.y:12227 +//line mysql_sql.y:12275 { yyLOCAL = tree.NewCStr(yyDollar[1].str, 1) } yyVAL.union = yyLOCAL - case 1788: + case 1797: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL *tree.CStr -//line mysql_sql.y:12231 +//line mysql_sql.y:12279 { if rejectSQLModeReservedFunctionName(yylex, yyDollar[1].str) { goto ret1 @@ -29863,10 +29992,10 @@ yydefault: yyLOCAL = tree.NewCStr(yyDollar[1].str, 1) } yyVAL.union = yyLOCAL - case 1789: + case 1798: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL *tree.CStr -//line mysql_sql.y:12235 +//line mysql_sql.y:12283 { if rejectSQLModeReservedFunctionName(yylex, yyDollar[1].str) { goto ret1 @@ -29874,222 +30003,222 @@ yydefault: yyLOCAL = tree.NewCStr(yyDollar[1].str, 1) } yyVAL.union = yyLOCAL - case 1790: + case 1799: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL *tree.CStr -//line mysql_sql.y:12241 +//line mysql_sql.y:12289 { yyLOCAL = yylex.(*Lexer).GetDbOrTblNameCStr(yyDollar[1].cstrUnion().Origin()) } yyVAL.union = yyLOCAL - case 1791: + case 1800: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL *tree.UnresolvedName -//line mysql_sql.y:12247 +//line mysql_sql.y:12295 { yyLOCAL = tree.NewUnresolvedName(yyDollar[1].cstrUnion()) } yyVAL.union = yyLOCAL - case 1792: + case 1801: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL *tree.UnresolvedName -//line mysql_sql.y:12251 +//line mysql_sql.y:12299 { tblNameCStr := yylex.(*Lexer).GetDbOrTblNameCStr(yyDollar[1].cstrUnion().Origin()) yyLOCAL = tree.NewUnresolvedName(tblNameCStr, yyDollar[3].cstrUnion()) } yyVAL.union = yyLOCAL - case 1793: + case 1802: yyDollar = yyS[yypt-5 : yypt+1] var yyLOCAL *tree.UnresolvedName -//line mysql_sql.y:12256 +//line mysql_sql.y:12304 { dbNameCStr := yylex.(*Lexer).GetDbOrTblNameCStr(yyDollar[1].cstrUnion().Origin()) tblNameCStr := yylex.(*Lexer).GetDbOrTblNameCStr(yyDollar[3].cstrUnion().Origin()) yyLOCAL = tree.NewUnresolvedName(dbNameCStr, tblNameCStr, yyDollar[5].cstrUnion()) } yyVAL.union = yyLOCAL - case 1794: + case 1803: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL []tree.ColumnAttribute -//line mysql_sql.y:12263 +//line mysql_sql.y:12311 { yyLOCAL = nil } yyVAL.union = yyLOCAL - case 1795: + case 1804: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL []tree.ColumnAttribute -//line mysql_sql.y:12267 +//line mysql_sql.y:12315 { yyLOCAL = yyDollar[1].columnAttributesUnion() } yyVAL.union = yyLOCAL - case 1796: + case 1805: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL []tree.ColumnAttribute -//line mysql_sql.y:12273 +//line mysql_sql.y:12321 { yyLOCAL = []tree.ColumnAttribute{yyDollar[1].columnAttributeUnion()} } yyVAL.union = yyLOCAL - case 1797: + case 1806: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL []tree.ColumnAttribute -//line mysql_sql.y:12277 +//line mysql_sql.y:12325 { yyLOCAL = append(yyDollar[1].columnAttributesUnion(), yyDollar[2].columnAttributeUnion()) } yyVAL.union = yyLOCAL - case 1798: + case 1807: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.ColumnAttribute -//line mysql_sql.y:12283 +//line mysql_sql.y:12331 { yyLOCAL = tree.NewAttributeNull(true) } yyVAL.union = yyLOCAL - case 1799: + case 1808: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL tree.ColumnAttribute -//line mysql_sql.y:12287 +//line mysql_sql.y:12335 { yyLOCAL = tree.NewAttributeNull(false) } yyVAL.union = yyLOCAL - case 1800: + case 1809: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL tree.ColumnAttribute -//line mysql_sql.y:12291 +//line mysql_sql.y:12339 { yyLOCAL = tree.NewAttributeNull(false) } yyVAL.union = yyLOCAL - case 1801: + case 1810: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL tree.ColumnAttribute -//line mysql_sql.y:12295 +//line mysql_sql.y:12343 { yyLOCAL = tree.NewAttributeDefault(yyDollar[2].exprUnion()) } yyVAL.union = yyLOCAL - case 1802: + case 1811: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.ColumnAttribute -//line mysql_sql.y:12299 +//line mysql_sql.y:12347 { yyLOCAL = tree.NewAttributeAutoIncrement() } yyVAL.union = yyLOCAL - case 1803: + case 1812: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.ColumnAttribute -//line mysql_sql.y:12303 +//line mysql_sql.y:12351 { yyLOCAL = yyDollar[1].columnAttributeUnion() } yyVAL.union = yyLOCAL - case 1804: + case 1813: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL tree.ColumnAttribute -//line mysql_sql.y:12307 +//line mysql_sql.y:12355 { str := util.DealCommentString(yyDollar[2].str) yyLOCAL = tree.NewAttributeComment(tree.NewNumVal(str, str, false, tree.P_char)) } yyVAL.union = yyLOCAL - case 1805: + case 1814: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL tree.ColumnAttribute -//line mysql_sql.y:12312 +//line mysql_sql.y:12360 { yyLOCAL = tree.NewAttributeMongoDBPath(yyDollar[2].str) } yyVAL.union = yyLOCAL - case 1806: + case 1815: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL tree.ColumnAttribute -//line mysql_sql.y:12316 +//line mysql_sql.y:12364 { yyLOCAL = tree.NewAttributeMongoDBConvert(yyDollar[2].str) } yyVAL.union = yyLOCAL - case 1807: + case 1816: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL tree.ColumnAttribute -//line mysql_sql.y:12320 +//line mysql_sql.y:12368 { yyLOCAL = tree.NewAttributeCollate(yyDollar[2].str) } yyVAL.union = yyLOCAL - case 1808: + case 1817: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL tree.ColumnAttribute -//line mysql_sql.y:12324 +//line mysql_sql.y:12372 { yyLOCAL = tree.NewAttributeColumnFormat(yyDollar[2].str) } yyVAL.union = yyLOCAL - case 1809: + case 1818: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL tree.ColumnAttribute -//line mysql_sql.y:12328 +//line mysql_sql.y:12376 { yyLOCAL = nil } yyVAL.union = yyLOCAL - case 1810: + case 1819: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL tree.ColumnAttribute -//line mysql_sql.y:12332 +//line mysql_sql.y:12380 { yyLOCAL = nil } yyVAL.union = yyLOCAL - case 1811: + case 1820: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL tree.ColumnAttribute -//line mysql_sql.y:12336 +//line mysql_sql.y:12384 { yyLOCAL = tree.NewAttributeStorage(yyDollar[2].str) } yyVAL.union = yyLOCAL - case 1812: + case 1821: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL tree.ColumnAttribute -//line mysql_sql.y:12340 +//line mysql_sql.y:12388 { yyLOCAL = tree.NewAttributeAutoRandom(int(yyDollar[2].int64ValUnion())) } yyVAL.union = yyLOCAL - case 1813: + case 1822: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.ColumnAttribute -//line mysql_sql.y:12344 +//line mysql_sql.y:12392 { yyLOCAL = yyDollar[1].attributeReferenceUnion() } yyVAL.union = yyLOCAL - case 1814: + case 1823: yyDollar = yyS[yypt-5 : yypt+1] var yyLOCAL tree.ColumnAttribute -//line mysql_sql.y:12348 +//line mysql_sql.y:12396 { yyLOCAL = tree.NewAttributeCheckConstraint(yyDollar[4].exprUnion(), true, yyDollar[1].str) } yyVAL.union = yyLOCAL - case 1815: + case 1824: yyDollar = yyS[yypt-6 : yypt+1] var yyLOCAL tree.ColumnAttribute -//line mysql_sql.y:12352 +//line mysql_sql.y:12400 { yyLOCAL = tree.NewAttributeCheckConstraint(yyDollar[4].exprUnion(), yyDollar[6].boolValUnion(), yyDollar[1].str) } yyVAL.union = yyLOCAL - case 1816: + case 1825: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL tree.ColumnAttribute -//line mysql_sql.y:12356 +//line mysql_sql.y:12404 { name := tree.NewUnresolvedColName(yyDollar[3].str) var es tree.Exprs = nil @@ -30104,10 +30233,10 @@ yydefault: yyLOCAL = tree.NewAttributeOnUpdate(expr) } yyVAL.union = yyLOCAL - case 1817: + case 1826: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL tree.ColumnAttribute -//line mysql_sql.y:12370 +//line mysql_sql.y:12418 { v, errStr := util.GetInt64(yyDollar[2].item) if errStr != "" { @@ -30121,130 +30250,130 @@ yydefault: yyLOCAL = tree.NewAttributeSRID(uint32(v)) } yyVAL.union = yyLOCAL - case 1818: + case 1827: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.ColumnAttribute -//line mysql_sql.y:12383 +//line mysql_sql.y:12431 { yyLOCAL = tree.NewAttributeLowCardinality() } yyVAL.union = yyLOCAL - case 1819: + case 1828: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.ColumnAttribute -//line mysql_sql.y:12387 +//line mysql_sql.y:12435 { yyLOCAL = tree.NewAttributeVisable(true) } yyVAL.union = yyLOCAL - case 1820: + case 1829: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.ColumnAttribute -//line mysql_sql.y:12391 +//line mysql_sql.y:12439 { yyLOCAL = tree.NewAttributeVisable(false) } yyVAL.union = yyLOCAL - case 1821: + case 1830: yyDollar = yyS[yypt-5 : yypt+1] var yyLOCAL tree.ColumnAttribute -//line mysql_sql.y:12395 +//line mysql_sql.y:12443 { yyLOCAL = tree.NewAttributeCharset(yyDollar[5].str) } yyVAL.union = yyLOCAL - case 1822: + case 1831: yyDollar = yyS[yypt-7 : yypt+1] var yyLOCAL tree.ColumnAttribute -//line mysql_sql.y:12399 +//line mysql_sql.y:12447 { yyLOCAL = tree.NewAttributeGeneratedAlways(yyDollar[5].exprUnion(), yyDollar[7].boolValUnion()) } yyVAL.union = yyLOCAL - case 1823: + case 1832: yyDollar = yyS[yypt-5 : yypt+1] var yyLOCAL tree.ColumnAttribute -//line mysql_sql.y:12403 +//line mysql_sql.y:12451 { yyLOCAL = tree.NewAttributeGeneratedAlways(yyDollar[3].exprUnion(), yyDollar[5].boolValUnion()) } yyVAL.union = yyLOCAL - case 1824: + case 1833: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL bool -//line mysql_sql.y:12408 +//line mysql_sql.y:12456 { yyLOCAL = false } yyVAL.union = yyLOCAL - case 1825: + case 1834: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL bool -//line mysql_sql.y:12412 +//line mysql_sql.y:12460 { yyLOCAL = false } yyVAL.union = yyLOCAL - case 1826: + case 1835: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL bool -//line mysql_sql.y:12416 +//line mysql_sql.y:12464 { yyLOCAL = true } yyVAL.union = yyLOCAL - case 1827: + case 1836: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL bool -//line mysql_sql.y:12422 +//line mysql_sql.y:12470 { yyLOCAL = true } yyVAL.union = yyLOCAL - case 1828: + case 1837: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL bool -//line mysql_sql.y:12426 +//line mysql_sql.y:12474 { yyLOCAL = false } yyVAL.union = yyLOCAL - case 1829: + case 1838: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL bool -//line mysql_sql.y:12430 +//line mysql_sql.y:12478 { yyLOCAL = false } yyVAL.union = yyLOCAL - case 1830: + case 1839: yyDollar = yyS[yypt-0 : yypt+1] -//line mysql_sql.y:12435 +//line mysql_sql.y:12483 { yyVAL.str = "" } - case 1831: + case 1840: yyDollar = yyS[yypt-1 : yypt+1] -//line mysql_sql.y:12439 +//line mysql_sql.y:12487 { yyVAL.str = yyDollar[1].str } - case 1832: + case 1841: yyDollar = yyS[yypt-1 : yypt+1] -//line mysql_sql.y:12445 +//line mysql_sql.y:12493 { yyVAL.str = "" } - case 1833: + case 1842: yyDollar = yyS[yypt-2 : yypt+1] -//line mysql_sql.y:12449 +//line mysql_sql.y:12497 { yyVAL.str = yyDollar[2].cstrUnion().Compare() } - case 1834: + case 1843: yyDollar = yyS[yypt-5 : yypt+1] var yyLOCAL *tree.AttributeReference -//line mysql_sql.y:12455 +//line mysql_sql.y:12503 { var TableName = yyDollar[2].tableNameUnion() var KeyParts = yyDollar[3].keyPartsUnion() @@ -30260,10 +30389,10 @@ yydefault: ) } yyVAL.union = yyLOCAL - case 1835: + case 1844: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL *tree.ReferenceOnRecord -//line mysql_sql.y:12472 +//line mysql_sql.y:12520 { yyLOCAL = &tree.ReferenceOnRecord{ OnDelete: tree.REFERENCE_OPTION_INVALID, @@ -30271,10 +30400,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 1836: + case 1845: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL *tree.ReferenceOnRecord -//line mysql_sql.y:12479 +//line mysql_sql.y:12527 { yyLOCAL = &tree.ReferenceOnRecord{ OnDelete: yyDollar[1].referenceOptionTypeUnion(), @@ -30282,10 +30411,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 1837: + case 1846: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL *tree.ReferenceOnRecord -//line mysql_sql.y:12486 +//line mysql_sql.y:12534 { yyLOCAL = &tree.ReferenceOnRecord{ OnDelete: tree.REFERENCE_OPTION_INVALID, @@ -30293,10 +30422,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 1838: + case 1847: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL *tree.ReferenceOnRecord -//line mysql_sql.y:12493 +//line mysql_sql.y:12541 { yyLOCAL = &tree.ReferenceOnRecord{ OnDelete: yyDollar[1].referenceOptionTypeUnion(), @@ -30304,10 +30433,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 1839: + case 1848: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL *tree.ReferenceOnRecord -//line mysql_sql.y:12500 +//line mysql_sql.y:12548 { yyLOCAL = &tree.ReferenceOnRecord{ OnDelete: yyDollar[2].referenceOptionTypeUnion(), @@ -30315,282 +30444,282 @@ yydefault: } } yyVAL.union = yyLOCAL - case 1840: + case 1849: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL tree.ReferenceOptionType -//line mysql_sql.y:12509 +//line mysql_sql.y:12557 { yyLOCAL = yyDollar[3].referenceOptionTypeUnion() } yyVAL.union = yyLOCAL - case 1841: + case 1850: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL tree.ReferenceOptionType -//line mysql_sql.y:12515 +//line mysql_sql.y:12563 { yyLOCAL = yyDollar[3].referenceOptionTypeUnion() } yyVAL.union = yyLOCAL - case 1842: + case 1851: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.ReferenceOptionType -//line mysql_sql.y:12521 +//line mysql_sql.y:12569 { yyLOCAL = tree.REFERENCE_OPTION_RESTRICT } yyVAL.union = yyLOCAL - case 1843: + case 1852: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.ReferenceOptionType -//line mysql_sql.y:12525 +//line mysql_sql.y:12573 { yyLOCAL = tree.REFERENCE_OPTION_CASCADE } yyVAL.union = yyLOCAL - case 1844: + case 1853: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL tree.ReferenceOptionType -//line mysql_sql.y:12529 +//line mysql_sql.y:12577 { yyLOCAL = tree.REFERENCE_OPTION_SET_NULL } yyVAL.union = yyLOCAL - case 1845: + case 1854: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL tree.ReferenceOptionType -//line mysql_sql.y:12533 +//line mysql_sql.y:12581 { yyLOCAL = tree.REFERENCE_OPTION_NO_ACTION } yyVAL.union = yyLOCAL - case 1846: + case 1855: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL tree.ReferenceOptionType -//line mysql_sql.y:12537 +//line mysql_sql.y:12585 { yyLOCAL = tree.REFERENCE_OPTION_SET_DEFAULT } yyVAL.union = yyLOCAL - case 1847: + case 1856: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL tree.MatchType -//line mysql_sql.y:12542 +//line mysql_sql.y:12590 { yyLOCAL = tree.MATCH_INVALID } yyVAL.union = yyLOCAL - case 1849: + case 1858: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL tree.MatchType -//line mysql_sql.y:12549 +//line mysql_sql.y:12597 { yyLOCAL = tree.MATCH_FULL } yyVAL.union = yyLOCAL - case 1850: + case 1859: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL tree.MatchType -//line mysql_sql.y:12553 +//line mysql_sql.y:12601 { yyLOCAL = tree.MATCH_PARTIAL } yyVAL.union = yyLOCAL - case 1851: + case 1860: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL tree.MatchType -//line mysql_sql.y:12557 +//line mysql_sql.y:12605 { yyLOCAL = tree.MATCH_SIMPLE } yyVAL.union = yyLOCAL - case 1852: + case 1861: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL tree.FullTextSearchType -//line mysql_sql.y:12562 +//line mysql_sql.y:12610 { yyLOCAL = tree.FULLTEXT_DEFAULT } yyVAL.union = yyLOCAL - case 1853: + case 1862: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL tree.FullTextSearchType -//line mysql_sql.y:12566 +//line mysql_sql.y:12614 { yyLOCAL = tree.FULLTEXT_NL } yyVAL.union = yyLOCAL - case 1854: + case 1863: yyDollar = yyS[yypt-7 : yypt+1] var yyLOCAL tree.FullTextSearchType -//line mysql_sql.y:12570 +//line mysql_sql.y:12618 { yyLOCAL = tree.FULLTEXT_NL_QUERY_EXPANSION } yyVAL.union = yyLOCAL - case 1855: + case 1864: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL tree.FullTextSearchType -//line mysql_sql.y:12574 +//line mysql_sql.y:12622 { yyLOCAL = tree.FULLTEXT_BOOLEAN } yyVAL.union = yyLOCAL - case 1856: + case 1865: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL tree.FullTextSearchType -//line mysql_sql.y:12578 +//line mysql_sql.y:12626 { yyLOCAL = tree.FULLTEXT_QUERY_EXPANSION } yyVAL.union = yyLOCAL - case 1857: + case 1866: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL tree.FullTextSearchType -//line mysql_sql.y:12582 +//line mysql_sql.y:12630 { yyLOCAL = tree.FULLTEXT_BM25 } yyVAL.union = yyLOCAL - case 1858: + case 1867: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL []*tree.KeyPart -//line mysql_sql.y:12587 +//line mysql_sql.y:12635 { yyLOCAL = nil } yyVAL.union = yyLOCAL - case 1859: + case 1868: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL []*tree.KeyPart -//line mysql_sql.y:12591 +//line mysql_sql.y:12639 { yyLOCAL = yyDollar[2].keyPartsUnion() } yyVAL.union = yyLOCAL - case 1860: + case 1869: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL int64 -//line mysql_sql.y:12596 +//line mysql_sql.y:12644 { yyLOCAL = -1 } yyVAL.union = yyLOCAL - case 1861: + case 1870: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL int64 -//line mysql_sql.y:12600 +//line mysql_sql.y:12648 { yyLOCAL = yyDollar[2].item.(int64) } yyVAL.union = yyLOCAL - case 1868: + case 1877: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL *tree.Subquery -//line mysql_sql.y:12616 +//line mysql_sql.y:12664 { yyLOCAL = &tree.Subquery{Select: yyDollar[1].selectStatementUnion(), Exists: false} } yyVAL.union = yyLOCAL - case 1869: + case 1878: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL tree.Expr -//line mysql_sql.y:12622 +//line mysql_sql.y:12670 { yyLOCAL = tree.NewBinaryExpr(tree.BIT_AND, yyDollar[1].exprUnion(), yyDollar[3].exprUnion()) } yyVAL.union = yyLOCAL - case 1870: + case 1879: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL tree.Expr -//line mysql_sql.y:12626 +//line mysql_sql.y:12674 { yyLOCAL = tree.NewBinaryExpr(tree.BIT_OR, yyDollar[1].exprUnion(), yyDollar[3].exprUnion()) } yyVAL.union = yyLOCAL - case 1871: + case 1880: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL tree.Expr -//line mysql_sql.y:12630 +//line mysql_sql.y:12678 { yyLOCAL = tree.NewBinaryExpr(tree.BIT_XOR, yyDollar[1].exprUnion(), yyDollar[3].exprUnion()) } yyVAL.union = yyLOCAL - case 1872: + case 1881: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL tree.Expr -//line mysql_sql.y:12634 +//line mysql_sql.y:12682 { yyLOCAL = tree.NewBinaryExpr(tree.PLUS, yyDollar[1].exprUnion(), yyDollar[3].exprUnion()) } yyVAL.union = yyLOCAL - case 1873: + case 1882: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL tree.Expr -//line mysql_sql.y:12638 +//line mysql_sql.y:12686 { yyLOCAL = tree.NewBinaryExpr(tree.MINUS, yyDollar[1].exprUnion(), yyDollar[3].exprUnion()) } yyVAL.union = yyLOCAL - case 1874: + case 1883: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL tree.Expr -//line mysql_sql.y:12642 +//line mysql_sql.y:12690 { yyLOCAL = tree.NewBinaryExpr(tree.MULTI, yyDollar[1].exprUnion(), yyDollar[3].exprUnion()) } yyVAL.union = yyLOCAL - case 1875: + case 1884: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL tree.Expr -//line mysql_sql.y:12646 +//line mysql_sql.y:12694 { yyLOCAL = tree.NewBinaryExpr(tree.DIV, yyDollar[1].exprUnion(), yyDollar[3].exprUnion()) } yyVAL.union = yyLOCAL - case 1876: + case 1885: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL tree.Expr -//line mysql_sql.y:12650 +//line mysql_sql.y:12698 { yyLOCAL = tree.NewBinaryExpr(tree.INTEGER_DIV, yyDollar[1].exprUnion(), yyDollar[3].exprUnion()) } yyVAL.union = yyLOCAL - case 1877: + case 1886: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL tree.Expr -//line mysql_sql.y:12654 +//line mysql_sql.y:12702 { yyLOCAL = tree.NewBinaryExpr(tree.MOD, yyDollar[1].exprUnion(), yyDollar[3].exprUnion()) } yyVAL.union = yyLOCAL - case 1878: + case 1887: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL tree.Expr -//line mysql_sql.y:12658 +//line mysql_sql.y:12706 { yyLOCAL = tree.NewBinaryExpr(tree.MOD, yyDollar[1].exprUnion(), yyDollar[3].exprUnion()) } yyVAL.union = yyLOCAL - case 1879: + case 1888: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL tree.Expr -//line mysql_sql.y:12662 +//line mysql_sql.y:12710 { yyLOCAL = tree.NewBinaryExpr(tree.LEFT_SHIFT, yyDollar[1].exprUnion(), yyDollar[3].exprUnion()) } yyVAL.union = yyLOCAL - case 1880: + case 1889: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL tree.Expr -//line mysql_sql.y:12666 +//line mysql_sql.y:12714 { yyLOCAL = tree.NewBinaryExpr(tree.RIGHT_SHIFT, yyDollar[1].exprUnion(), yyDollar[3].exprUnion()) } yyVAL.union = yyLOCAL - case 1881: + case 1890: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL tree.Expr -//line mysql_sql.y:12670 +//line mysql_sql.y:12718 { name := tree.NewUnresolvedColName("json_extract") yyLOCAL = &tree.FuncExpr{ @@ -30600,10 +30729,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 1882: + case 1891: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL tree.Expr -//line mysql_sql.y:12679 +//line mysql_sql.y:12727 { extractName := tree.NewUnresolvedColName("json_extract") inner := &tree.FuncExpr{ @@ -30619,42 +30748,42 @@ yydefault: } } yyVAL.union = yyLOCAL - case 1883: + case 1892: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.Expr -//line mysql_sql.y:12694 +//line mysql_sql.y:12742 { yyLOCAL = yyDollar[1].exprUnion() } yyVAL.union = yyLOCAL - case 1884: + case 1893: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.Expr -//line mysql_sql.y:12700 +//line mysql_sql.y:12748 { yyLOCAL = yyDollar[1].unresolvedNameUnion() } yyVAL.union = yyLOCAL - case 1885: + case 1894: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.Expr -//line mysql_sql.y:12704 +//line mysql_sql.y:12752 { yyLOCAL = yyDollar[1].varExprUnion() } yyVAL.union = yyLOCAL - case 1886: + case 1895: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.Expr -//line mysql_sql.y:12708 +//line mysql_sql.y:12756 { yyLOCAL = yyDollar[1].exprUnion() } yyVAL.union = yyLOCAL - case 1887: + case 1896: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL tree.Expr -//line mysql_sql.y:12712 +//line mysql_sql.y:12760 { name := tree.NewUnresolvedColName("concat") yyLOCAL = &tree.FuncExpr{ @@ -30664,66 +30793,66 @@ yydefault: } } yyVAL.union = yyLOCAL - case 1888: + case 1897: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL tree.Expr -//line mysql_sql.y:12721 +//line mysql_sql.y:12769 { yyLOCAL = tree.NewParentExpr(yyDollar[2].exprUnion()) } yyVAL.union = yyLOCAL - case 1889: + case 1898: yyDollar = yyS[yypt-5 : yypt+1] var yyLOCAL tree.Expr -//line mysql_sql.y:12725 +//line mysql_sql.y:12773 { yyLOCAL = tree.NewTuple(append(yyDollar[2].exprsUnion(), yyDollar[4].exprUnion())) } yyVAL.union = yyLOCAL - case 1890: + case 1899: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL tree.Expr -//line mysql_sql.y:12729 +//line mysql_sql.y:12777 { yyLOCAL = tree.NewUnaryExpr(tree.UNARY_PLUS, yyDollar[2].exprUnion()) } yyVAL.union = yyLOCAL - case 1891: + case 1900: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL tree.Expr -//line mysql_sql.y:12733 +//line mysql_sql.y:12781 { yyLOCAL = tree.NewUnaryExpr(tree.UNARY_MINUS, yyDollar[2].exprUnion()) } yyVAL.union = yyLOCAL - case 1892: + case 1901: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL tree.Expr -//line mysql_sql.y:12737 +//line mysql_sql.y:12785 { yyLOCAL = tree.NewUnaryExpr(tree.UNARY_TILDE, yyDollar[2].exprUnion()) } yyVAL.union = yyLOCAL - case 1893: + case 1902: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL tree.Expr -//line mysql_sql.y:12741 +//line mysql_sql.y:12789 { yyLOCAL = tree.NewUnaryExpr(tree.UNARY_MARK, yyDollar[2].exprUnion()) } yyVAL.union = yyLOCAL - case 1894: + case 1903: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL tree.Expr -//line mysql_sql.y:12745 +//line mysql_sql.y:12793 { yyLOCAL = tree.NewNotExpr(yyDollar[2].exprUnion()) } yyVAL.union = yyLOCAL - case 1895: + case 1904: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL tree.Expr -//line mysql_sql.y:12749 +//line mysql_sql.y:12797 { hint := strings.ToLower(yyDollar[2].cstrUnion().Compare()) switch hint { @@ -30766,35 +30895,35 @@ yydefault: } } yyVAL.union = yyLOCAL - case 1896: + case 1905: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.Expr -//line mysql_sql.y:12791 +//line mysql_sql.y:12839 { yyLOCAL = yyDollar[1].exprUnion() } yyVAL.union = yyLOCAL - case 1897: + case 1906: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.Expr -//line mysql_sql.y:12795 +//line mysql_sql.y:12843 { yyLOCAL = yyDollar[1].subqueryUnion() } yyVAL.union = yyLOCAL - case 1898: + case 1907: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL tree.Expr -//line mysql_sql.y:12799 +//line mysql_sql.y:12847 { yyDollar[2].subqueryUnion().Exists = true yyLOCAL = yyDollar[2].subqueryUnion() } yyVAL.union = yyLOCAL - case 1899: + case 1908: yyDollar = yyS[yypt-5 : yypt+1] var yyLOCAL tree.Expr -//line mysql_sql.y:12804 +//line mysql_sql.y:12852 { yyLOCAL = &tree.CaseExpr{ Expr: yyDollar[2].exprUnion(), @@ -30803,50 +30932,50 @@ yydefault: } } yyVAL.union = yyLOCAL - case 1900: + case 1909: yyDollar = yyS[yypt-6 : yypt+1] var yyLOCAL tree.Expr -//line mysql_sql.y:12812 +//line mysql_sql.y:12860 { yyLOCAL = tree.NewCastExpr(yyDollar[3].exprUnion(), yyDollar[5].columnTypeUnion()) } yyVAL.union = yyLOCAL - case 1901: + case 1910: yyDollar = yyS[yypt-8 : yypt+1] var yyLOCAL tree.Expr -//line mysql_sql.y:12816 +//line mysql_sql.y:12864 { yyLOCAL = tree.NewSerialExtractExpr(yyDollar[3].exprUnion(), yyDollar[5].exprUnion(), yyDollar[7].columnTypeUnion()) } yyVAL.union = yyLOCAL - case 1902: + case 1911: yyDollar = yyS[yypt-6 : yypt+1] var yyLOCAL tree.Expr -//line mysql_sql.y:12820 +//line mysql_sql.y:12868 { yyLOCAL = tree.NewBitCastExpr(yyDollar[3].exprUnion(), yyDollar[5].columnTypeUnion()) } yyVAL.union = yyLOCAL - case 1903: + case 1912: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL tree.Expr -//line mysql_sql.y:12824 +//line mysql_sql.y:12872 { yyLOCAL = tree.NewCastExpr(yyDollar[1].exprUnion(), yyDollar[3].columnTypeUnion()) } yyVAL.union = yyLOCAL - case 1904: + case 1913: yyDollar = yyS[yypt-6 : yypt+1] var yyLOCAL tree.Expr -//line mysql_sql.y:12828 +//line mysql_sql.y:12876 { yyLOCAL = tree.NewCastExpr(yyDollar[3].exprUnion(), yyDollar[5].columnTypeUnion()) } yyVAL.union = yyLOCAL - case 1905: + case 1914: yyDollar = yyS[yypt-6 : yypt+1] var yyLOCAL tree.Expr -//line mysql_sql.y:12832 +//line mysql_sql.y:12880 { name := tree.NewUnresolvedColName(yyDollar[1].str) es := tree.NewNumVal(yyDollar[5].str, yyDollar[5].str, false, tree.P_char) @@ -30857,66 +30986,66 @@ yydefault: } } yyVAL.union = yyLOCAL - case 1906: + case 1915: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.Expr -//line mysql_sql.y:12842 +//line mysql_sql.y:12890 { yyLOCAL = yyDollar[1].funcExprUnion() } yyVAL.union = yyLOCAL - case 1907: + case 1916: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.Expr -//line mysql_sql.y:12846 +//line mysql_sql.y:12894 { yyLOCAL = yyDollar[1].funcExprUnion() } yyVAL.union = yyLOCAL - case 1908: + case 1917: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.Expr -//line mysql_sql.y:12850 +//line mysql_sql.y:12898 { yyLOCAL = yyDollar[1].funcExprUnion() } yyVAL.union = yyLOCAL - case 1909: + case 1918: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.Expr -//line mysql_sql.y:12854 +//line mysql_sql.y:12902 { yyLOCAL = yyDollar[1].funcExprUnion() } yyVAL.union = yyLOCAL - case 1910: + case 1919: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.Expr -//line mysql_sql.y:12858 +//line mysql_sql.y:12906 { yyLOCAL = yyDollar[1].funcExprUnion() } yyVAL.union = yyLOCAL - case 1911: + case 1920: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.Expr -//line mysql_sql.y:12862 +//line mysql_sql.y:12910 { yyLOCAL = yyDollar[1].exprUnion() } yyVAL.union = yyLOCAL - case 1912: + case 1921: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL tree.Expr -//line mysql_sql.y:12866 +//line mysql_sql.y:12914 { yyLOCAL = yyDollar[1].exprUnion() } yyVAL.union = yyLOCAL - case 1913: + case 1922: yyDollar = yyS[yypt-9 : yypt+1] var yyLOCAL tree.Expr -//line mysql_sql.y:12870 +//line mysql_sql.y:12918 { val, err := tree.NewFullTextMatchFuncExpression(yyDollar[3].keyPartsUnion(), yyDollar[7].exprUnion(), yyDollar[8].fullTextSearchTypeUnion()) if err != nil { @@ -30926,34 +31055,34 @@ yydefault: yyLOCAL = val } yyVAL.union = yyLOCAL - case 1914: + case 1923: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.Expr -//line mysql_sql.y:12880 +//line mysql_sql.y:12928 { yyLOCAL = tree.NewNumVal(yyDollar[1].str, yyDollar[1].str, false, tree.P_char) } yyVAL.union = yyLOCAL - case 1915: + case 1924: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.Expr -//line mysql_sql.y:12884 +//line mysql_sql.y:12932 { yyLOCAL = tree.NewParamExpr(yylex.(*Lexer).GetParamIndex()) } yyVAL.union = yyLOCAL - case 1916: + case 1925: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.Expr -//line mysql_sql.y:12888 +//line mysql_sql.y:12936 { yyLOCAL = tree.NewUnresolvedName(yyDollar[1].cstrUnion()) } yyVAL.union = yyLOCAL - case 1917: + case 1926: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL *tree.FuncExpr -//line mysql_sql.y:12894 +//line mysql_sql.y:12942 { name := tree.NewUnresolvedColName(yyDollar[1].str) yyLOCAL = &tree.FuncExpr{ @@ -30963,10 +31092,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 1918: + case 1927: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL *tree.FuncExpr -//line mysql_sql.y:12903 +//line mysql_sql.y:12951 { name := tree.NewUnresolvedColName(yyDollar[1].str) yyLOCAL = &tree.FuncExpr{ @@ -30976,10 +31105,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 1919: + case 1928: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL *tree.FuncExpr -//line mysql_sql.y:12912 +//line mysql_sql.y:12960 { name := tree.NewUnresolvedColName(yyDollar[1].str) yyLOCAL = &tree.FuncExpr{ @@ -30989,10 +31118,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 1920: + case 1929: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL *tree.FuncExpr -//line mysql_sql.y:12921 +//line mysql_sql.y:12969 { name := tree.NewUnresolvedColName(yyDollar[1].str) yyLOCAL = &tree.FuncExpr{ @@ -31002,10 +31131,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 1921: + case 1930: yyDollar = yyS[yypt-5 : yypt+1] var yyLOCAL *tree.FuncExpr -//line mysql_sql.y:12930 +//line mysql_sql.y:12978 { name := tree.NewUnresolvedColName(yyDollar[1].str) yyLOCAL = &tree.FuncExpr{ @@ -31016,10 +31145,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 1922: + case 1931: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL *tree.FuncExpr -//line mysql_sql.y:12940 +//line mysql_sql.y:12988 { name := tree.NewUnresolvedColName(yyDollar[1].str) yyLOCAL = &tree.FuncExpr{ @@ -31029,10 +31158,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 1923: + case 1932: yyDollar = yyS[yypt-6 : yypt+1] var yyLOCAL *tree.FuncExpr -//line mysql_sql.y:12949 +//line mysql_sql.y:12997 { name := tree.NewUnresolvedColName(yyDollar[1].str) yyLOCAL = &tree.FuncExpr{ @@ -31043,10 +31172,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 1924: + case 1933: yyDollar = yyS[yypt-8 : yypt+1] var yyLOCAL *tree.FuncExpr -//line mysql_sql.y:12959 +//line mysql_sql.y:13007 { name := tree.NewUnresolvedColName(yyDollar[1].str) yyLOCAL = &tree.FuncExpr{ @@ -31057,10 +31186,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 1925: + case 1934: yyDollar = yyS[yypt-10 : yypt+1] var yyLOCAL *tree.FuncExpr -//line mysql_sql.y:12969 +//line mysql_sql.y:13017 { name := tree.NewUnresolvedColName(yyDollar[1].str) yyLOCAL = &tree.FuncExpr{ @@ -31071,10 +31200,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 1926: + case 1935: yyDollar = yyS[yypt-6 : yypt+1] var yyLOCAL *tree.FuncExpr -//line mysql_sql.y:12979 +//line mysql_sql.y:13027 { name := tree.NewUnresolvedColName(yyDollar[1].str) yyLOCAL = &tree.FuncExpr{ @@ -31085,10 +31214,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 1927: + case 1936: yyDollar = yyS[yypt-8 : yypt+1] var yyLOCAL *tree.FuncExpr -//line mysql_sql.y:12989 +//line mysql_sql.y:13037 { name := tree.NewUnresolvedColName(yyDollar[1].str) yyLOCAL = &tree.FuncExpr{ @@ -31099,10 +31228,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 1928: + case 1937: yyDollar = yyS[yypt-10 : yypt+1] var yyLOCAL *tree.FuncExpr -//line mysql_sql.y:12999 +//line mysql_sql.y:13047 { name := tree.NewUnresolvedColName(yyDollar[1].str) yyLOCAL = &tree.FuncExpr{ @@ -31113,10 +31242,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 1929: + case 1938: yyDollar = yyS[yypt-6 : yypt+1] var yyLOCAL *tree.FuncExpr -//line mysql_sql.y:13009 +//line mysql_sql.y:13057 { name := tree.NewUnresolvedColName(yyDollar[1].str) yyLOCAL = &tree.FuncExpr{ @@ -31127,10 +31256,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 1930: + case 1939: yyDollar = yyS[yypt-6 : yypt+1] var yyLOCAL *tree.FuncExpr -//line mysql_sql.y:13019 +//line mysql_sql.y:13067 { name := tree.NewUnresolvedColName(yyDollar[1].str) yyLOCAL = &tree.FuncExpr{ @@ -31141,10 +31270,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 1931: + case 1940: yyDollar = yyS[yypt-9 : yypt+1] var yyLOCAL *tree.FuncExpr -//line mysql_sql.y:13029 +//line mysql_sql.y:13077 { name := tree.NewUnresolvedColName(yyDollar[1].str) yyLOCAL = &tree.FuncExpr{ @@ -31155,10 +31284,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 1932: + case 1941: yyDollar = yyS[yypt-7 : yypt+1] var yyLOCAL tree.Expr -//line mysql_sql.y:13041 +//line mysql_sql.y:13089 { v := int(yyDollar[5].item.(int64)) val, err := tree.NewSampleRowsFuncExpression(v, true, nil, "block") @@ -31169,10 +31298,10 @@ yydefault: yyLOCAL = val } yyVAL.union = yyLOCAL - case 1933: + case 1942: yyDollar = yyS[yypt-9 : yypt+1] var yyLOCAL tree.Expr -//line mysql_sql.y:13051 +//line mysql_sql.y:13099 { v := int(yyDollar[5].item.(int64)) val, err := tree.NewSampleRowsFuncExpression(v, true, nil, yyDollar[8].str) @@ -31183,10 +31312,10 @@ yydefault: yyLOCAL = val } yyVAL.union = yyLOCAL - case 1934: + case 1943: yyDollar = yyS[yypt-7 : yypt+1] var yyLOCAL tree.Expr -//line mysql_sql.y:13061 +//line mysql_sql.y:13109 { val, err := tree.NewSamplePercentFuncExpression1(yyDollar[5].item.(int64), true, nil) if err != nil { @@ -31196,10 +31325,10 @@ yydefault: yyLOCAL = val } yyVAL.union = yyLOCAL - case 1935: + case 1944: yyDollar = yyS[yypt-7 : yypt+1] var yyLOCAL tree.Expr -//line mysql_sql.y:13070 +//line mysql_sql.y:13118 { val, err := tree.NewSamplePercentFuncExpression2(yyDollar[5].item.(float64), true, nil) if err != nil { @@ -31209,10 +31338,10 @@ yydefault: yyLOCAL = val } yyVAL.union = yyLOCAL - case 1936: + case 1945: yyDollar = yyS[yypt-7 : yypt+1] var yyLOCAL tree.Expr -//line mysql_sql.y:13080 +//line mysql_sql.y:13128 { v := int(yyDollar[5].item.(int64)) val, err := tree.NewSampleRowsFuncExpression(v, false, yyDollar[3].exprsUnion(), "block") @@ -31223,10 +31352,10 @@ yydefault: yyLOCAL = val } yyVAL.union = yyLOCAL - case 1937: + case 1946: yyDollar = yyS[yypt-9 : yypt+1] var yyLOCAL tree.Expr -//line mysql_sql.y:13090 +//line mysql_sql.y:13138 { v := int(yyDollar[5].item.(int64)) val, err := tree.NewSampleRowsFuncExpression(v, false, yyDollar[3].exprsUnion(), yyDollar[8].str) @@ -31237,10 +31366,10 @@ yydefault: yyLOCAL = val } yyVAL.union = yyLOCAL - case 1938: + case 1947: yyDollar = yyS[yypt-7 : yypt+1] var yyLOCAL tree.Expr -//line mysql_sql.y:13100 +//line mysql_sql.y:13148 { val, err := tree.NewSamplePercentFuncExpression1(yyDollar[5].item.(int64), false, yyDollar[3].exprsUnion()) if err != nil { @@ -31250,10 +31379,10 @@ yydefault: yyLOCAL = val } yyVAL.union = yyLOCAL - case 1939: + case 1948: yyDollar = yyS[yypt-7 : yypt+1] var yyLOCAL tree.Expr -//line mysql_sql.y:13109 +//line mysql_sql.y:13157 { val, err := tree.NewSamplePercentFuncExpression2(yyDollar[5].item.(float64), false, yyDollar[3].exprsUnion()) if err != nil { @@ -31263,58 +31392,58 @@ yydefault: yyLOCAL = val } yyVAL.union = yyLOCAL - case 1940: + case 1949: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL tree.Expr -//line mysql_sql.y:13119 +//line mysql_sql.y:13167 { yyLOCAL = nil } yyVAL.union = yyLOCAL - case 1941: + case 1950: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL tree.Expr -//line mysql_sql.y:13123 +//line mysql_sql.y:13171 { yyLOCAL = yyDollar[2].exprUnion() } yyVAL.union = yyLOCAL - case 1942: + case 1951: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL tree.Expr -//line mysql_sql.y:13128 +//line mysql_sql.y:13176 { yyLOCAL = nil } yyVAL.union = yyLOCAL - case 1943: + case 1952: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.Expr -//line mysql_sql.y:13132 +//line mysql_sql.y:13180 { yyLOCAL = yyDollar[1].exprUnion() } yyVAL.union = yyLOCAL - case 1944: + case 1953: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL []*tree.When -//line mysql_sql.y:13138 +//line mysql_sql.y:13186 { yyLOCAL = []*tree.When{yyDollar[1].whenClauseUnion()} } yyVAL.union = yyLOCAL - case 1945: + case 1954: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL []*tree.When -//line mysql_sql.y:13142 +//line mysql_sql.y:13190 { yyLOCAL = append(yyDollar[1].whenClauseListUnion(), yyDollar[2].whenClauseUnion()) } yyVAL.union = yyLOCAL - case 1946: + case 1955: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL *tree.When -//line mysql_sql.y:13148 +//line mysql_sql.y:13196 { yyLOCAL = &tree.When{ Cond: yyDollar[2].exprUnion(), @@ -31322,9 +31451,9 @@ yydefault: } } yyVAL.union = yyLOCAL - case 1947: + case 1956: yyDollar = yyS[yypt-1 : yypt+1] -//line mysql_sql.y:13157 +//line mysql_sql.y:13205 { t := yyVAL.columnTypeUnion() str := strings.ToLower(t.InternalType.FamilyString) @@ -31337,10 +31466,10 @@ yydefault: } } } - case 1948: + case 1957: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL *tree.T -//line mysql_sql.y:13169 +//line mysql_sql.y:13217 { name := yyDollar[1].str if yyDollar[2].str != "" { @@ -31358,10 +31487,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 1949: + case 1958: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL *tree.T -//line mysql_sql.y:13186 +//line mysql_sql.y:13234 { locale := "" yyLOCAL = &tree.T{ @@ -31376,10 +31505,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 1951: + case 1960: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL *tree.T -//line mysql_sql.y:13203 +//line mysql_sql.y:13251 { locale := "" yyLOCAL = &tree.T{ @@ -31392,10 +31521,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 1952: + case 1961: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL *tree.T -//line mysql_sql.y:13215 +//line mysql_sql.y:13263 { locale := "" yyLOCAL = &tree.T{ @@ -31410,10 +31539,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 1953: + case 1962: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL *tree.T -//line mysql_sql.y:13229 +//line mysql_sql.y:13277 { locale := "" oid := uint32(defines.MYSQL_TYPE_STRING) @@ -31433,10 +31562,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 1954: + case 1963: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL *tree.T -//line mysql_sql.y:13248 +//line mysql_sql.y:13296 { locale := "" yyLOCAL = &tree.T{ @@ -31449,10 +31578,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 1955: + case 1964: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL *tree.T -//line mysql_sql.y:13260 +//line mysql_sql.y:13308 { locale := "" yyLOCAL = &tree.T{ @@ -31467,10 +31596,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 1956: + case 1965: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL *tree.T -//line mysql_sql.y:13274 +//line mysql_sql.y:13322 { locale := "" yyLOCAL = &tree.T{ @@ -31486,10 +31615,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 1957: + case 1966: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL *tree.T -//line mysql_sql.y:13289 +//line mysql_sql.y:13337 { locale := "" yyLOCAL = &tree.T{ @@ -31505,10 +31634,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 1958: + case 1967: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL *tree.T -//line mysql_sql.y:13304 +//line mysql_sql.y:13352 { name := yyDollar[1].str if yyDollar[2].str != "" { @@ -31526,10 +31655,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 1959: + case 1968: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL *tree.T -//line mysql_sql.y:13321 +//line mysql_sql.y:13369 { locale := "" yyLOCAL = &tree.T{ @@ -31544,112 +31673,112 @@ yydefault: } } yyVAL.union = yyLOCAL - case 1960: + case 1969: yyDollar = yyS[yypt-0 : yypt+1] -//line mysql_sql.y:13337 +//line mysql_sql.y:13385 { yyVAL.str = "" } - case 1964: + case 1973: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL *tree.FrameBound -//line mysql_sql.y:13346 +//line mysql_sql.y:13394 { yyLOCAL = &tree.FrameBound{Type: tree.Following, UnBounded: true} } yyVAL.union = yyLOCAL - case 1965: + case 1974: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL *tree.FrameBound -//line mysql_sql.y:13350 +//line mysql_sql.y:13398 { yyLOCAL = &tree.FrameBound{Type: tree.Following, Expr: yyDollar[1].exprUnion()} } yyVAL.union = yyLOCAL - case 1966: + case 1975: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL *tree.FrameBound -//line mysql_sql.y:13354 +//line mysql_sql.y:13402 { yyLOCAL = &tree.FrameBound{Type: tree.Following, Expr: tree.NewParamExpr(yylex.(*Lexer).GetParamIndex())} } yyVAL.union = yyLOCAL - case 1967: + case 1976: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL *tree.FrameBound -//line mysql_sql.y:13358 +//line mysql_sql.y:13406 { yyLOCAL = &tree.FrameBound{Type: tree.Following, Expr: yyDollar[1].exprUnion()} } yyVAL.union = yyLOCAL - case 1968: + case 1977: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL *tree.FrameBound -//line mysql_sql.y:13364 +//line mysql_sql.y:13412 { yyLOCAL = &tree.FrameBound{Type: tree.CurrentRow} } yyVAL.union = yyLOCAL - case 1969: + case 1978: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL *tree.FrameBound -//line mysql_sql.y:13368 +//line mysql_sql.y:13416 { yyLOCAL = &tree.FrameBound{Type: tree.Preceding, UnBounded: true} } yyVAL.union = yyLOCAL - case 1970: + case 1979: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL *tree.FrameBound -//line mysql_sql.y:13372 +//line mysql_sql.y:13420 { yyLOCAL = &tree.FrameBound{Type: tree.Preceding, Expr: yyDollar[1].exprUnion()} } yyVAL.union = yyLOCAL - case 1971: + case 1980: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL *tree.FrameBound -//line mysql_sql.y:13376 +//line mysql_sql.y:13424 { yyLOCAL = &tree.FrameBound{Type: tree.Preceding, Expr: tree.NewParamExpr(yylex.(*Lexer).GetParamIndex())} } yyVAL.union = yyLOCAL - case 1972: + case 1981: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL *tree.FrameBound -//line mysql_sql.y:13380 +//line mysql_sql.y:13428 { yyLOCAL = &tree.FrameBound{Type: tree.Preceding, Expr: yyDollar[1].exprUnion()} } yyVAL.union = yyLOCAL - case 1973: + case 1982: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.FrameType -//line mysql_sql.y:13386 +//line mysql_sql.y:13434 { yyLOCAL = tree.Rows } yyVAL.union = yyLOCAL - case 1974: + case 1983: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.FrameType -//line mysql_sql.y:13390 +//line mysql_sql.y:13438 { yyLOCAL = tree.Range } yyVAL.union = yyLOCAL - case 1975: + case 1984: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.FrameType -//line mysql_sql.y:13394 +//line mysql_sql.y:13442 { yyLOCAL = tree.Groups } yyVAL.union = yyLOCAL - case 1976: + case 1985: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL *tree.FrameClause -//line mysql_sql.y:13400 +//line mysql_sql.y:13448 { yyLOCAL = &tree.FrameClause{ Type: yyDollar[1].frameTypeUnion(), @@ -31658,10 +31787,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 1977: + case 1986: yyDollar = yyS[yypt-5 : yypt+1] var yyLOCAL *tree.FrameClause -//line mysql_sql.y:13408 +//line mysql_sql.y:13456 { yyLOCAL = &tree.FrameClause{ Type: yyDollar[1].frameTypeUnion(), @@ -31671,163 +31800,163 @@ yydefault: } } yyVAL.union = yyLOCAL - case 1978: + case 1987: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL *tree.FrameClause -//line mysql_sql.y:13418 +//line mysql_sql.y:13466 { yyLOCAL = nil } yyVAL.union = yyLOCAL - case 1979: + case 1988: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL *tree.FrameClause -//line mysql_sql.y:13422 +//line mysql_sql.y:13470 { yyLOCAL = yyDollar[1].frameClauseUnion() } yyVAL.union = yyLOCAL - case 1980: + case 1989: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL tree.Exprs -//line mysql_sql.y:13428 +//line mysql_sql.y:13476 { yyLOCAL = yyDollar[3].exprsUnion() } yyVAL.union = yyLOCAL - case 1981: + case 1990: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL tree.Exprs -//line mysql_sql.y:13433 +//line mysql_sql.y:13481 { yyLOCAL = nil } yyVAL.union = yyLOCAL - case 1982: + case 1991: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.Exprs -//line mysql_sql.y:13437 +//line mysql_sql.y:13485 { yyLOCAL = yyDollar[1].exprsUnion() } yyVAL.union = yyLOCAL - case 1983: + case 1992: yyDollar = yyS[yypt-0 : yypt+1] -//line mysql_sql.y:13442 +//line mysql_sql.y:13490 { yyVAL.str = "," } - case 1984: + case 1993: yyDollar = yyS[yypt-2 : yypt+1] -//line mysql_sql.y:13446 +//line mysql_sql.y:13494 { yyVAL.str = yyDollar[2].str } - case 1985: + case 1994: yyDollar = yyS[yypt-0 : yypt+1] -//line mysql_sql.y:13451 +//line mysql_sql.y:13499 { yyVAL.str = "1,vector_l2_ops,random,false" } - case 1986: + case 1995: yyDollar = yyS[yypt-2 : yypt+1] -//line mysql_sql.y:13455 +//line mysql_sql.y:13503 { yyVAL.str = yyDollar[2].str } - case 1987: + case 1996: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL *tree.WindowSpec -//line mysql_sql.y:13460 +//line mysql_sql.y:13508 { yyLOCAL = nil } yyVAL.union = yyLOCAL - case 1989: + case 1998: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL *tree.WindowSpec -//line mysql_sql.y:13467 +//line mysql_sql.y:13515 { yyLOCAL = makeWindowSpec(yyDollar[2].cstrUnion(), nil, nil, nil) yyLOCAL.ReferencedOnly = true } yyVAL.union = yyLOCAL - case 1990: + case 1999: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL *tree.WindowSpec -//line mysql_sql.y:13472 +//line mysql_sql.y:13520 { yyLOCAL = yyDollar[3].windowSpecUnion() } yyVAL.union = yyLOCAL - case 1991: + case 2000: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL *tree.WindowSpec -//line mysql_sql.y:13478 +//line mysql_sql.y:13526 { yyLOCAL = makeWindowSpec(yyDollar[1].cstrUnion(), yyDollar[2].exprsUnion(), yyDollar[3].orderByUnion(), yyDollar[4].frameClauseUnion()) } yyVAL.union = yyLOCAL - case 1992: + case 2001: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL *tree.CStr -//line mysql_sql.y:13487 +//line mysql_sql.y:13535 { yyLOCAL = nil } yyVAL.union = yyLOCAL - case 1993: + case 2002: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL *tree.CStr -//line mysql_sql.y:13491 +//line mysql_sql.y:13539 { yyLOCAL = yyDollar[1].cstrUnion() } yyVAL.union = yyLOCAL - case 1994: + case 2003: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL tree.WindowDefinitions -//line mysql_sql.y:13496 +//line mysql_sql.y:13544 { yyLOCAL = nil } yyVAL.union = yyLOCAL - case 1995: + case 2004: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL tree.WindowDefinitions -//line mysql_sql.y:13500 +//line mysql_sql.y:13548 { yyLOCAL = yyDollar[2].windowDefinitionsUnion() } yyVAL.union = yyLOCAL - case 1996: + case 2005: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.WindowDefinitions -//line mysql_sql.y:13506 +//line mysql_sql.y:13554 { yyLOCAL = tree.WindowDefinitions{yyDollar[1].windowDefinitionUnion()} } yyVAL.union = yyLOCAL - case 1997: + case 2006: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL tree.WindowDefinitions -//line mysql_sql.y:13510 +//line mysql_sql.y:13558 { yyLOCAL = append(yyDollar[1].windowDefinitionsUnion(), yyDollar[3].windowDefinitionUnion()) } yyVAL.union = yyLOCAL - case 1998: + case 2007: yyDollar = yyS[yypt-5 : yypt+1] var yyLOCAL *tree.WindowDefinition -//line mysql_sql.y:13516 +//line mysql_sql.y:13564 { yyLOCAL = &tree.WindowDefinition{Name: yyDollar[1].cstrUnion(), Spec: yyDollar[4].windowSpecUnion()} } yyVAL.union = yyLOCAL - case 1999: + case 2008: yyDollar = yyS[yypt-9 : yypt+1] var yyLOCAL *tree.FuncExpr -//line mysql_sql.y:13522 +//line mysql_sql.y:13570 { functionName := yyDollar[1].str arguments := yyDollar[4].exprsUnion() @@ -31870,10 +31999,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 2000: + case 2009: yyDollar = yyS[yypt-6 : yypt+1] var yyLOCAL *tree.FuncExpr -//line mysql_sql.y:13564 +//line mysql_sql.y:13612 { name := tree.NewUnresolvedColName(yyDollar[1].str) yyLOCAL = &tree.FuncExpr{ @@ -31886,10 +32015,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 2001: + case 2010: yyDollar = yyS[yypt-6 : yypt+1] var yyLOCAL *tree.FuncExpr -//line mysql_sql.y:13576 +//line mysql_sql.y:13624 { name := tree.NewUnresolvedColName(yyDollar[1].str) yyLOCAL = &tree.FuncExpr{ @@ -31902,10 +32031,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 2002: + case 2011: yyDollar = yyS[yypt-8 : yypt+1] var yyLOCAL *tree.FuncExpr -//line mysql_sql.y:13588 +//line mysql_sql.y:13636 { name := tree.NewUnresolvedColName(yyDollar[1].str) yyLOCAL = &tree.FuncExpr{ @@ -31918,10 +32047,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 2003: + case 2012: yyDollar = yyS[yypt-6 : yypt+1] var yyLOCAL *tree.FuncExpr -//line mysql_sql.y:13600 +//line mysql_sql.y:13648 { name := tree.NewUnresolvedColName(yyDollar[1].str) yyLOCAL = &tree.FuncExpr{ @@ -31933,10 +32062,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 2004: + case 2013: yyDollar = yyS[yypt-6 : yypt+1] var yyLOCAL *tree.FuncExpr -//line mysql_sql.y:13611 +//line mysql_sql.y:13659 { name := tree.NewUnresolvedColName(yyDollar[1].str) yyLOCAL = &tree.FuncExpr{ @@ -31948,10 +32077,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 2005: + case 2014: yyDollar = yyS[yypt-5 : yypt+1] var yyLOCAL *tree.FuncExpr -//line mysql_sql.y:13622 +//line mysql_sql.y:13670 { name := tree.NewUnresolvedColName(yyDollar[1].str) es := tree.NewNumVal("*", "*", false, tree.P_star) @@ -31963,10 +32092,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 2006: + case 2015: yyDollar = yyS[yypt-5 : yypt+1] var yyLOCAL *tree.FuncExpr -//line mysql_sql.y:13633 +//line mysql_sql.y:13681 { name := tree.NewUnresolvedColName(yyDollar[1].str) yyLOCAL = &tree.FuncExpr{ @@ -31977,10 +32106,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 2007: + case 2016: yyDollar = yyS[yypt-6 : yypt+1] var yyLOCAL *tree.FuncExpr -//line mysql_sql.y:13643 +//line mysql_sql.y:13691 { name := tree.NewUnresolvedColName(yyDollar[1].str) yyLOCAL = &tree.FuncExpr{ @@ -31993,10 +32122,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 2008: + case 2017: yyDollar = yyS[yypt-6 : yypt+1] var yyLOCAL *tree.FuncExpr -//line mysql_sql.y:13655 +//line mysql_sql.y:13703 { name := tree.NewUnresolvedColName(yyDollar[1].str) yyLOCAL = &tree.FuncExpr{ @@ -32008,10 +32137,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 2009: + case 2018: yyDollar = yyS[yypt-6 : yypt+1] var yyLOCAL *tree.FuncExpr -//line mysql_sql.y:13666 +//line mysql_sql.y:13714 { name := tree.NewUnresolvedColName(yyDollar[1].str) yyLOCAL = &tree.FuncExpr{ @@ -32023,10 +32152,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 2010: + case 2019: yyDollar = yyS[yypt-6 : yypt+1] var yyLOCAL *tree.FuncExpr -//line mysql_sql.y:13677 +//line mysql_sql.y:13725 { name := tree.NewUnresolvedColName(yyDollar[1].str) yyLOCAL = &tree.FuncExpr{ @@ -32038,10 +32167,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 2011: + case 2020: yyDollar = yyS[yypt-6 : yypt+1] var yyLOCAL *tree.FuncExpr -//line mysql_sql.y:13688 +//line mysql_sql.y:13736 { name := tree.NewUnresolvedColName(yyDollar[1].str) yyLOCAL = &tree.FuncExpr{ @@ -32053,10 +32182,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 2012: + case 2021: yyDollar = yyS[yypt-5 : yypt+1] var yyLOCAL *tree.FuncExpr -//line mysql_sql.y:13699 +//line mysql_sql.y:13747 { name := tree.NewUnresolvedColName(yyDollar[1].str) es := tree.NewNumVal("*", "*", false, tree.P_star) @@ -32068,10 +32197,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 2013: + case 2022: yyDollar = yyS[yypt-6 : yypt+1] var yyLOCAL *tree.FuncExpr -//line mysql_sql.y:13710 +//line mysql_sql.y:13758 { name := tree.NewUnresolvedColName(yyDollar[1].str) yyLOCAL = &tree.FuncExpr{ @@ -32083,10 +32212,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 2014: + case 2023: yyDollar = yyS[yypt-6 : yypt+1] var yyLOCAL *tree.FuncExpr -//line mysql_sql.y:13721 +//line mysql_sql.y:13769 { name := tree.NewUnresolvedColName(yyDollar[1].str) yyLOCAL = &tree.FuncExpr{ @@ -32098,10 +32227,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 2015: + case 2024: yyDollar = yyS[yypt-6 : yypt+1] var yyLOCAL *tree.FuncExpr -//line mysql_sql.y:13732 +//line mysql_sql.y:13780 { name := tree.NewUnresolvedColName(yyDollar[1].str) yyLOCAL = &tree.FuncExpr{ @@ -32113,10 +32242,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 2016: + case 2025: yyDollar = yyS[yypt-6 : yypt+1] var yyLOCAL *tree.FuncExpr -//line mysql_sql.y:13743 +//line mysql_sql.y:13791 { name := tree.NewUnresolvedColName(yyDollar[1].str) yyLOCAL = &tree.FuncExpr{ @@ -32128,10 +32257,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 2017: + case 2026: yyDollar = yyS[yypt-6 : yypt+1] var yyLOCAL *tree.FuncExpr -//line mysql_sql.y:13754 +//line mysql_sql.y:13802 { name := tree.NewUnresolvedColName(yyDollar[1].str) yyLOCAL = &tree.FuncExpr{ @@ -32143,10 +32272,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 2018: + case 2027: yyDollar = yyS[yypt-6 : yypt+1] var yyLOCAL *tree.FuncExpr -//line mysql_sql.y:13765 +//line mysql_sql.y:13813 { name := tree.NewUnresolvedColName(yyDollar[1].str) yyLOCAL = &tree.FuncExpr{ @@ -32158,10 +32287,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 2019: + case 2028: yyDollar = yyS[yypt-6 : yypt+1] var yyLOCAL *tree.FuncExpr -//line mysql_sql.y:13776 +//line mysql_sql.y:13824 { name := tree.NewUnresolvedColName(yyDollar[1].str) yyLOCAL = &tree.FuncExpr{ @@ -32173,10 +32302,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 2020: + case 2029: yyDollar = yyS[yypt-6 : yypt+1] var yyLOCAL *tree.FuncExpr -//line mysql_sql.y:13787 +//line mysql_sql.y:13835 { name := tree.NewUnresolvedColName(yyDollar[1].str) yyLOCAL = &tree.FuncExpr{ @@ -32188,10 +32317,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 2021: + case 2030: yyDollar = yyS[yypt-9 : yypt+1] var yyLOCAL *tree.FuncExpr -//line mysql_sql.y:13798 +//line mysql_sql.y:13846 { name := tree.NewUnresolvedColName(yyDollar[1].str) yyLOCAL = &tree.FuncExpr{ @@ -32203,10 +32332,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 2022: + case 2031: yyDollar = yyS[yypt-6 : yypt+1] var yyLOCAL *tree.FuncExpr -//line mysql_sql.y:13809 +//line mysql_sql.y:13857 { name := tree.NewUnresolvedColName(yyDollar[1].str) yyLOCAL = &tree.FuncExpr{ @@ -32218,10 +32347,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 2023: + case 2032: yyDollar = yyS[yypt-6 : yypt+1] var yyLOCAL *tree.FuncExpr -//line mysql_sql.y:13820 +//line mysql_sql.y:13868 { name := tree.NewUnresolvedColName(yyDollar[1].str) yyLOCAL = &tree.FuncExpr{ @@ -32233,10 +32362,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 2024: + case 2033: yyDollar = yyS[yypt-6 : yypt+1] var yyLOCAL *tree.FuncExpr -//line mysql_sql.y:13831 +//line mysql_sql.y:13879 { name := tree.NewUnresolvedColName(yyDollar[1].str) yyLOCAL = &tree.FuncExpr{ @@ -32248,10 +32377,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 2025: + case 2034: yyDollar = yyS[yypt-6 : yypt+1] var yyLOCAL *tree.FuncExpr -//line mysql_sql.y:13842 +//line mysql_sql.y:13890 { functionName := yyDollar[1].str if strings.EqualFold(functionName, "array_agg") { @@ -32269,10 +32398,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 2026: + case 2035: yyDollar = yyS[yypt-8 : yypt+1] var yyLOCAL *tree.FuncExpr -//line mysql_sql.y:13859 +//line mysql_sql.y:13907 { name := tree.NewUnresolvedColName(yyDollar[1].str) yyLOCAL = &tree.FuncExpr{ @@ -32284,10 +32413,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 2030: + case 2039: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL *tree.FuncExpr -//line mysql_sql.y:13877 +//line mysql_sql.y:13925 { name := tree.NewUnresolvedColName(yyDollar[1].str) yyLOCAL = &tree.FuncExpr{ @@ -32298,10 +32427,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 2031: + case 2040: yyDollar = yyS[yypt-6 : yypt+1] var yyLOCAL *tree.FuncExpr -//line mysql_sql.y:13887 +//line mysql_sql.y:13935 { name := tree.NewUnresolvedColName(yyDollar[1].str) exprs := tree.Exprs{yyDollar[3].exprUnion()} @@ -32313,10 +32442,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 2032: + case 2041: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL *tree.FuncExpr -//line mysql_sql.y:13898 +//line mysql_sql.y:13946 { name := tree.NewUnresolvedColName(yyDollar[1].str) yyLOCAL = &tree.FuncExpr{ @@ -32326,10 +32455,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 2033: + case 2042: yyDollar = yyS[yypt-6 : yypt+1] var yyLOCAL *tree.FuncExpr -//line mysql_sql.y:13907 +//line mysql_sql.y:13955 { name := tree.NewUnresolvedColName(yyDollar[1].str) yyLOCAL = &tree.FuncExpr{ @@ -32339,10 +32468,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 2034: + case 2043: yyDollar = yyS[yypt-8 : yypt+1] var yyLOCAL *tree.FuncExpr -//line mysql_sql.y:13916 +//line mysql_sql.y:13964 { name := tree.NewUnresolvedColName(yyDollar[1].str) yyLOCAL = &tree.FuncExpr{ @@ -32352,10 +32481,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 2035: + case 2044: yyDollar = yyS[yypt-6 : yypt+1] var yyLOCAL *tree.FuncExpr -//line mysql_sql.y:13925 +//line mysql_sql.y:13973 { name := tree.NewUnresolvedColName(yyDollar[1].str) str := strings.ToLower(yyDollar[3].str) @@ -32367,10 +32496,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 2036: + case 2045: yyDollar = yyS[yypt-6 : yypt+1] var yyLOCAL *tree.FuncExpr -//line mysql_sql.y:13936 +//line mysql_sql.y:13984 { name := tree.NewUnresolvedColName(yyDollar[1].str) yyLOCAL = &tree.FuncExpr{ @@ -32380,10 +32509,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 2037: + case 2046: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL *tree.FuncExpr -//line mysql_sql.y:13945 +//line mysql_sql.y:13993 { name := tree.NewUnresolvedColName(yyDollar[1].str) yyLOCAL = &tree.FuncExpr{ @@ -32393,10 +32522,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 2038: + case 2047: yyDollar = yyS[yypt-6 : yypt+1] var yyLOCAL *tree.FuncExpr -//line mysql_sql.y:13954 +//line mysql_sql.y:14002 { name := tree.NewUnresolvedColName(yyDollar[1].str) yyLOCAL = &tree.FuncExpr{ @@ -32408,10 +32537,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 2039: + case 2048: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL *tree.FuncExpr -//line mysql_sql.y:13965 +//line mysql_sql.y:14013 { name := tree.NewUnresolvedColName(yyDollar[1].str) yyLOCAL = &tree.FuncExpr{ @@ -32421,10 +32550,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 2040: + case 2049: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL *tree.FuncExpr -//line mysql_sql.y:13974 +//line mysql_sql.y:14022 { name := tree.NewUnresolvedColName(yyDollar[1].str) yyLOCAL = &tree.FuncExpr{ @@ -32434,10 +32563,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 2041: + case 2050: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL *tree.FuncExpr -//line mysql_sql.y:13983 +//line mysql_sql.y:14031 { name := tree.NewUnresolvedColName(yyDollar[1].str) yyLOCAL = &tree.FuncExpr{ @@ -32447,10 +32576,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 2042: + case 2051: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL *tree.FuncExpr -//line mysql_sql.y:13992 +//line mysql_sql.y:14040 { name := tree.NewUnresolvedColName(yyDollar[1].str) yyLOCAL = &tree.FuncExpr{ @@ -32460,10 +32589,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 2043: + case 2052: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL *tree.FuncExpr -//line mysql_sql.y:14001 +//line mysql_sql.y:14049 { name := tree.NewUnresolvedColName(yyDollar[1].str) arg0 := tree.NewNumVal(int64(0), "0", false, tree.P_int64) @@ -32476,10 +32605,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 2044: + case 2053: yyDollar = yyS[yypt-6 : yypt+1] var yyLOCAL *tree.FuncExpr -//line mysql_sql.y:14013 +//line mysql_sql.y:14061 { name := tree.NewUnresolvedColName(yyDollar[1].str) arg0 := tree.NewNumVal(int64(1), "1", false, tree.P_int64) @@ -32491,10 +32620,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 2045: + case 2054: yyDollar = yyS[yypt-6 : yypt+1] var yyLOCAL *tree.FuncExpr -//line mysql_sql.y:14024 +//line mysql_sql.y:14072 { name := tree.NewUnresolvedColName(yyDollar[1].str) arg0 := tree.NewNumVal(int64(2), "2", false, tree.P_int64) @@ -32508,10 +32637,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 2046: + case 2055: yyDollar = yyS[yypt-7 : yypt+1] var yyLOCAL *tree.FuncExpr -//line mysql_sql.y:14037 +//line mysql_sql.y:14085 { name := tree.NewUnresolvedColName(yyDollar[1].str) arg0 := tree.NewNumVal(int64(3), "3", false, tree.P_int64) @@ -32524,10 +32653,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 2047: + case 2056: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL *tree.FuncExpr -//line mysql_sql.y:14049 +//line mysql_sql.y:14097 { name := tree.NewUnresolvedColName(yyDollar[1].str) yyLOCAL = &tree.FuncExpr{ @@ -32537,16 +32666,16 @@ yydefault: } } yyVAL.union = yyLOCAL - case 2054: + case 2063: yyDollar = yyS[yypt-1 : yypt+1] -//line mysql_sql.y:14070 +//line mysql_sql.y:14118 { yyVAL.str = yyDollar[1].str } - case 2087: + case 2096: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL *tree.FuncExpr -//line mysql_sql.y:14112 +//line mysql_sql.y:14160 { name := tree.NewUnresolvedColName(yyDollar[1].str) var es tree.Exprs = nil @@ -32560,10 +32689,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 2088: + case 2097: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL *tree.FuncExpr -//line mysql_sql.y:14125 +//line mysql_sql.y:14173 { name := tree.NewUnresolvedColName(yyDollar[1].str) var es tree.Exprs = nil @@ -32577,10 +32706,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 2089: + case 2098: yyDollar = yyS[yypt-8 : yypt+1] var yyLOCAL *tree.FuncExpr -//line mysql_sql.y:14138 +//line mysql_sql.y:14186 { name := tree.NewUnresolvedColName(yyDollar[1].str) str := strings.ToLower(yyDollar[3].str) @@ -32592,10 +32721,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 2090: + case 2099: yyDollar = yyS[yypt-8 : yypt+1] var yyLOCAL *tree.FuncExpr -//line mysql_sql.y:14149 +//line mysql_sql.y:14197 { name := tree.NewUnresolvedColName(yyDollar[1].str) str := strings.ToLower(yyDollar[3].str) @@ -32607,10 +32736,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 2091: + case 2100: yyDollar = yyS[yypt-6 : yypt+1] var yyLOCAL *tree.FuncExpr -//line mysql_sql.y:14160 +//line mysql_sql.y:14208 { name := tree.NewUnresolvedColName(yyDollar[1].str) str := strings.ToUpper(yyDollar[3].str) @@ -32622,10 +32751,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 2092: + case 2101: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL *tree.FuncExpr -//line mysql_sql.y:14172 +//line mysql_sql.y:14220 { name := tree.NewUnresolvedColName(yyDollar[1].str) yyLOCAL = &tree.FuncExpr{ @@ -32635,10 +32764,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 2093: + case 2102: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL *tree.FuncExpr -//line mysql_sql.y:14181 +//line mysql_sql.y:14229 { name := tree.NewUnresolvedColName(yyDollar[1].str) yyLOCAL = &tree.FuncExpr{ @@ -32647,10 +32776,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 2094: + case 2103: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL *tree.FuncExpr -//line mysql_sql.y:14189 +//line mysql_sql.y:14237 { name := tree.NewUnresolvedColName(yyDollar[1].str) yyLOCAL = &tree.FuncExpr{ @@ -32659,10 +32788,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 2095: + case 2104: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL *tree.FuncExpr -//line mysql_sql.y:14197 +//line mysql_sql.y:14245 { name := tree.NewUnresolvedColName(yyDollar[1].str) var es tree.Exprs = nil @@ -32676,10 +32805,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 2096: + case 2105: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL *tree.FuncExpr -//line mysql_sql.y:14210 +//line mysql_sql.y:14258 { name := tree.NewUnresolvedColName(yyDollar[1].str) yyLOCAL = &tree.FuncExpr{ @@ -32689,10 +32818,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 2097: + case 2106: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL *tree.FuncExpr -//line mysql_sql.y:14219 +//line mysql_sql.y:14267 { name := tree.NewUnresolvedColName(yyDollar[1].str) exprs := make([]tree.Expr, 1) @@ -32704,10 +32833,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 2098: + case 2107: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL *tree.FuncExpr -//line mysql_sql.y:14230 +//line mysql_sql.y:14278 { name := tree.NewUnresolvedColName(yyDollar[1].str) exprs := make([]tree.Expr, 1) @@ -32719,10 +32848,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 2099: + case 2108: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL *tree.FuncExpr -//line mysql_sql.y:14241 +//line mysql_sql.y:14289 { name := tree.NewUnresolvedColName(yyDollar[1].str) yyLOCAL = &tree.FuncExpr{ @@ -32732,10 +32861,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 2100: + case 2109: yyDollar = yyS[yypt-6 : yypt+1] var yyLOCAL *tree.FuncExpr -//line mysql_sql.y:14250 +//line mysql_sql.y:14298 { charName := tree.NewUnresolvedColName(yyDollar[1].str) charExpr := &tree.FuncExpr{ @@ -32752,10 +32881,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 2101: + case 2110: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL *tree.FuncExpr -//line mysql_sql.y:14266 +//line mysql_sql.y:14314 { val := tree.NewNumVal(yyDollar[2].str, yyDollar[2].str, false, tree.P_char) name := tree.NewUnresolvedColName(yyDollar[1].str) @@ -32766,10 +32895,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 2102: + case 2111: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL *tree.FuncExpr -//line mysql_sql.y:14276 +//line mysql_sql.y:14324 { val := tree.NewNumVal(yyDollar[2].str, yyDollar[2].str, false, tree.P_char) name := tree.NewUnresolvedColName(yyDollar[1].str) @@ -32780,10 +32909,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 2103: + case 2112: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL *tree.FuncExpr -//line mysql_sql.y:14286 +//line mysql_sql.y:14334 { name := tree.NewUnresolvedColName(yyDollar[1].str) yyLOCAL = &tree.FuncExpr{ @@ -32793,10 +32922,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 2104: + case 2113: yyDollar = yyS[yypt-6 : yypt+1] var yyLOCAL *tree.FuncExpr -//line mysql_sql.y:14295 +//line mysql_sql.y:14343 { es := tree.Exprs{yyDollar[3].exprUnion()} es = append(es, yyDollar[5].exprUnion()) @@ -32808,10 +32937,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 2105: + case 2114: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL *tree.FuncExpr -//line mysql_sql.y:14306 +//line mysql_sql.y:14354 { name := tree.NewUnresolvedColName(yyDollar[1].str) yyLOCAL = &tree.FuncExpr{ @@ -32821,10 +32950,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 2106: + case 2115: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL *tree.FuncExpr -//line mysql_sql.y:14315 +//line mysql_sql.y:14363 { val := tree.NewNumVal(yyDollar[2].str, yyDollar[2].str, false, tree.P_char) name := tree.NewUnresolvedColName(yyDollar[1].str) @@ -32835,10 +32964,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 2107: + case 2116: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL *tree.FuncExpr -//line mysql_sql.y:14325 +//line mysql_sql.y:14373 { name := tree.NewUnresolvedColName(yyDollar[1].str) yyLOCAL = &tree.FuncExpr{ @@ -32848,10 +32977,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 2108: + case 2117: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL *tree.FuncExpr -//line mysql_sql.y:14334 +//line mysql_sql.y:14382 { name := tree.NewUnresolvedColName(yyDollar[1].str) yyLOCAL = &tree.FuncExpr{ @@ -32861,10 +32990,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 2109: + case 2118: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL *tree.FuncExpr -//line mysql_sql.y:14343 +//line mysql_sql.y:14391 { name := tree.NewUnresolvedColName(yyDollar[1].str) yyLOCAL = &tree.FuncExpr{ @@ -32874,34 +33003,34 @@ yydefault: } } yyVAL.union = yyLOCAL - case 2110: + case 2119: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL tree.Expr -//line mysql_sql.y:14353 +//line mysql_sql.y:14401 { yyLOCAL = nil } yyVAL.union = yyLOCAL - case 2111: + case 2120: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.Expr -//line mysql_sql.y:14357 +//line mysql_sql.y:14405 { yyLOCAL = yyDollar[1].exprUnion() } yyVAL.union = yyLOCAL - case 2112: + case 2121: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL tree.Expr -//line mysql_sql.y:14363 +//line mysql_sql.y:14411 { yyLOCAL = nil } yyVAL.union = yyLOCAL - case 2113: + case 2122: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL tree.Expr -//line mysql_sql.y:14367 +//line mysql_sql.y:14415 { ival, errStr := util.GetInt64(yyDollar[2].item) if errStr != "" { @@ -32912,20 +33041,20 @@ yydefault: yyLOCAL = tree.NewNumVal(ival, str, false, tree.P_int64) } yyVAL.union = yyLOCAL - case 2120: + case 2129: yyDollar = yyS[yypt-0 : yypt+1] -//line mysql_sql.y:14386 +//line mysql_sql.y:14434 { } - case 2121: + case 2130: yyDollar = yyS[yypt-2 : yypt+1] -//line mysql_sql.y:14388 +//line mysql_sql.y:14436 { } - case 2156: + case 2165: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL tree.Expr -//line mysql_sql.y:14430 +//line mysql_sql.y:14478 { name := tree.NewUnresolvedColName(yyDollar[1].str) str := strings.ToLower(yyDollar[3].str) @@ -32937,531 +33066,531 @@ yydefault: } } yyVAL.union = yyLOCAL - case 2157: + case 2166: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL tree.FuncType -//line mysql_sql.y:14442 +//line mysql_sql.y:14490 { yyLOCAL = tree.FUNC_TYPE_DEFAULT } yyVAL.union = yyLOCAL - case 2158: + case 2167: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.FuncType -//line mysql_sql.y:14446 +//line mysql_sql.y:14494 { yyLOCAL = tree.FUNC_TYPE_DISTINCT } yyVAL.union = yyLOCAL - case 2159: + case 2168: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.FuncType -//line mysql_sql.y:14450 +//line mysql_sql.y:14498 { yyLOCAL = tree.FUNC_TYPE_ALL } yyVAL.union = yyLOCAL - case 2160: + case 2169: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL *tree.Tuple -//line mysql_sql.y:14456 +//line mysql_sql.y:14504 { yyLOCAL = tree.NewTuple(yyDollar[2].exprsUnion()) } yyVAL.union = yyLOCAL - case 2161: + case 2170: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL tree.Exprs -//line mysql_sql.y:14461 +//line mysql_sql.y:14509 { yyLOCAL = nil } yyVAL.union = yyLOCAL - case 2162: + case 2171: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.Exprs -//line mysql_sql.y:14465 +//line mysql_sql.y:14513 { yyLOCAL = yyDollar[1].exprsUnion() } yyVAL.union = yyLOCAL - case 2163: + case 2172: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.Exprs -//line mysql_sql.y:14471 +//line mysql_sql.y:14519 { yyLOCAL = tree.Exprs{yyDollar[1].exprUnion()} } yyVAL.union = yyLOCAL - case 2164: + case 2173: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL tree.Exprs -//line mysql_sql.y:14475 +//line mysql_sql.y:14523 { yyLOCAL = append(yyDollar[1].exprsUnion(), yyDollar[3].exprUnion()) } yyVAL.union = yyLOCAL - case 2165: + case 2174: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.Exprs -//line mysql_sql.y:14481 +//line mysql_sql.y:14529 { yyLOCAL = tree.Exprs{yyDollar[1].exprUnion()} } yyVAL.union = yyLOCAL - case 2166: + case 2175: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL tree.Exprs -//line mysql_sql.y:14485 +//line mysql_sql.y:14533 { yyLOCAL = append(yyDollar[1].exprsUnion(), yyDollar[3].exprUnion()) } yyVAL.union = yyLOCAL - case 2167: + case 2176: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL tree.Expr -//line mysql_sql.y:14492 +//line mysql_sql.y:14540 { yyLOCAL = tree.NewAndExpr(yyDollar[1].exprUnion(), yyDollar[3].exprUnion()) } yyVAL.union = yyLOCAL - case 2168: + case 2177: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL tree.Expr -//line mysql_sql.y:14496 +//line mysql_sql.y:14544 { yyLOCAL = tree.NewOrExpr(yyDollar[1].exprUnion(), yyDollar[3].exprUnion()) } yyVAL.union = yyLOCAL - case 2169: + case 2178: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL tree.Expr -//line mysql_sql.y:14500 +//line mysql_sql.y:14548 { yyLOCAL = tree.NewXorExpr(yyDollar[1].exprUnion(), yyDollar[3].exprUnion()) } yyVAL.union = yyLOCAL - case 2170: + case 2179: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL tree.Expr -//line mysql_sql.y:14504 +//line mysql_sql.y:14552 { yyLOCAL = tree.NewNotExpr(yyDollar[2].exprUnion()) } yyVAL.union = yyLOCAL - case 2171: + case 2180: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.Expr -//line mysql_sql.y:14508 +//line mysql_sql.y:14556 { yyLOCAL = yyDollar[1].exprUnion() } yyVAL.union = yyLOCAL - case 2172: + case 2181: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.Expr -//line mysql_sql.y:14513 +//line mysql_sql.y:14561 { yyLOCAL = yyDollar[1].exprUnion() } yyVAL.union = yyLOCAL - case 2173: + case 2182: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.Expr -//line mysql_sql.y:14517 +//line mysql_sql.y:14565 { yyLOCAL = tree.NewMaxValue() } yyVAL.union = yyLOCAL - case 2174: + case 2183: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL tree.Expr -//line mysql_sql.y:14523 +//line mysql_sql.y:14571 { yyLOCAL = tree.NewIsNullExpr(yyDollar[1].exprUnion()) } yyVAL.union = yyLOCAL - case 2175: + case 2184: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL tree.Expr -//line mysql_sql.y:14527 +//line mysql_sql.y:14575 { yyLOCAL = tree.NewIsNotNullExpr(yyDollar[1].exprUnion()) } yyVAL.union = yyLOCAL - case 2176: + case 2185: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL tree.Expr -//line mysql_sql.y:14531 +//line mysql_sql.y:14579 { yyLOCAL = tree.NewIsNotNullExpr(yyDollar[1].exprUnion()) } yyVAL.union = yyLOCAL - case 2177: + case 2186: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL tree.Expr -//line mysql_sql.y:14535 +//line mysql_sql.y:14583 { yyLOCAL = tree.NewIsUnknownExpr(yyDollar[1].exprUnion()) } yyVAL.union = yyLOCAL - case 2178: + case 2187: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL tree.Expr -//line mysql_sql.y:14539 +//line mysql_sql.y:14587 { yyLOCAL = tree.NewIsNotUnknownExpr(yyDollar[1].exprUnion()) } yyVAL.union = yyLOCAL - case 2179: + case 2188: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL tree.Expr -//line mysql_sql.y:14543 +//line mysql_sql.y:14591 { yyLOCAL = tree.NewIsNotUnknownExpr(yyDollar[1].exprUnion()) } yyVAL.union = yyLOCAL - case 2180: + case 2189: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL tree.Expr -//line mysql_sql.y:14547 +//line mysql_sql.y:14595 { yyLOCAL = tree.NewIsTrueExpr(yyDollar[1].exprUnion()) } yyVAL.union = yyLOCAL - case 2181: + case 2190: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL tree.Expr -//line mysql_sql.y:14551 +//line mysql_sql.y:14599 { yyLOCAL = tree.NewIsNotTrueExpr(yyDollar[1].exprUnion()) } yyVAL.union = yyLOCAL - case 2182: + case 2191: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL tree.Expr -//line mysql_sql.y:14555 +//line mysql_sql.y:14603 { yyLOCAL = tree.NewIsNotTrueExpr(yyDollar[1].exprUnion()) } yyVAL.union = yyLOCAL - case 2183: + case 2192: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL tree.Expr -//line mysql_sql.y:14559 +//line mysql_sql.y:14607 { yyLOCAL = tree.NewIsFalseExpr(yyDollar[1].exprUnion()) } yyVAL.union = yyLOCAL - case 2184: + case 2193: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL tree.Expr -//line mysql_sql.y:14563 +//line mysql_sql.y:14611 { yyLOCAL = tree.NewIsNotFalseExpr(yyDollar[1].exprUnion()) } yyVAL.union = yyLOCAL - case 2185: + case 2194: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL tree.Expr -//line mysql_sql.y:14567 +//line mysql_sql.y:14615 { yyLOCAL = tree.NewIsNotFalseExpr(yyDollar[1].exprUnion()) } yyVAL.union = yyLOCAL - case 2186: + case 2195: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL tree.Expr -//line mysql_sql.y:14571 +//line mysql_sql.y:14619 { yyLOCAL = tree.NewComparisonExpr(yyDollar[2].comparisonOpUnion(), yyDollar[1].exprUnion(), yyDollar[3].exprUnion()) } yyVAL.union = yyLOCAL - case 2187: + case 2196: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL tree.Expr -//line mysql_sql.y:14575 +//line mysql_sql.y:14623 { yyLOCAL = tree.NewSubqueryComparisonExpr(yyDollar[2].comparisonOpUnion(), yyDollar[3].comparisonOpUnion(), yyDollar[1].exprUnion(), yyDollar[4].subqueryUnion()) yyLOCAL = tree.NewSubqueryComparisonExpr(yyDollar[2].comparisonOpUnion(), yyDollar[3].comparisonOpUnion(), yyDollar[1].exprUnion(), yyDollar[4].subqueryUnion()) } yyVAL.union = yyLOCAL - case 2189: + case 2198: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL tree.Expr -//line mysql_sql.y:14583 +//line mysql_sql.y:14631 { yyLOCAL = tree.NewComparisonExpr(tree.IN, yyDollar[1].exprUnion(), yyDollar[3].exprUnion()) } yyVAL.union = yyLOCAL - case 2190: + case 2199: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL tree.Expr -//line mysql_sql.y:14587 +//line mysql_sql.y:14635 { yyLOCAL = tree.NewComparisonExpr(tree.NOT_IN, yyDollar[1].exprUnion(), yyDollar[4].exprUnion()) } yyVAL.union = yyLOCAL - case 2191: + case 2200: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL tree.Expr -//line mysql_sql.y:14591 +//line mysql_sql.y:14639 { yyLOCAL = tree.NewComparisonExpr(tree.NOT_IN, yyDollar[1].exprUnion(), yyDollar[4].exprUnion()) } yyVAL.union = yyLOCAL - case 2192: + case 2201: yyDollar = yyS[yypt-6 : yypt+1] var yyLOCAL tree.Expr -//line mysql_sql.y:14595 +//line mysql_sql.y:14643 { yyLOCAL = tree.NewComparisonExpr(tree.MEMBER_OF, yyDollar[1].exprUnion(), yyDollar[5].exprUnion()) } yyVAL.union = yyLOCAL - case 2193: + case 2202: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL tree.Expr -//line mysql_sql.y:14599 +//line mysql_sql.y:14647 { yyLOCAL = tree.NewComparisonExprWithEscape(tree.LIKE, yyDollar[1].exprUnion(), yyDollar[3].exprUnion(), yyDollar[4].exprUnion()) } yyVAL.union = yyLOCAL - case 2194: + case 2203: yyDollar = yyS[yypt-5 : yypt+1] var yyLOCAL tree.Expr -//line mysql_sql.y:14603 +//line mysql_sql.y:14651 { yyLOCAL = tree.NewComparisonExprWithEscape(tree.NOT_LIKE, yyDollar[1].exprUnion(), yyDollar[4].exprUnion(), yyDollar[5].exprUnion()) } yyVAL.union = yyLOCAL - case 2195: + case 2204: yyDollar = yyS[yypt-5 : yypt+1] var yyLOCAL tree.Expr -//line mysql_sql.y:14607 +//line mysql_sql.y:14655 { yyLOCAL = tree.NewComparisonExprWithEscape(tree.NOT_LIKE, yyDollar[1].exprUnion(), yyDollar[4].exprUnion(), yyDollar[5].exprUnion()) } yyVAL.union = yyLOCAL - case 2196: + case 2205: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL tree.Expr -//line mysql_sql.y:14611 +//line mysql_sql.y:14659 { yyLOCAL = tree.NewComparisonExprWithEscape(tree.ILIKE, yyDollar[1].exprUnion(), yyDollar[3].exprUnion(), yyDollar[4].exprUnion()) } yyVAL.union = yyLOCAL - case 2197: + case 2206: yyDollar = yyS[yypt-5 : yypt+1] var yyLOCAL tree.Expr -//line mysql_sql.y:14615 +//line mysql_sql.y:14663 { yyLOCAL = tree.NewComparisonExprWithEscape(tree.NOT_ILIKE, yyDollar[1].exprUnion(), yyDollar[4].exprUnion(), yyDollar[5].exprUnion()) } yyVAL.union = yyLOCAL - case 2198: + case 2207: yyDollar = yyS[yypt-5 : yypt+1] var yyLOCAL tree.Expr -//line mysql_sql.y:14619 +//line mysql_sql.y:14667 { yyLOCAL = tree.NewComparisonExprWithEscape(tree.NOT_ILIKE, yyDollar[1].exprUnion(), yyDollar[4].exprUnion(), yyDollar[5].exprUnion()) } yyVAL.union = yyLOCAL - case 2199: + case 2208: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL tree.Expr -//line mysql_sql.y:14623 +//line mysql_sql.y:14671 { yyLOCAL = tree.NewComparisonExpr(tree.REG_MATCH, yyDollar[1].exprUnion(), yyDollar[3].exprUnion()) } yyVAL.union = yyLOCAL - case 2200: + case 2209: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL tree.Expr -//line mysql_sql.y:14627 +//line mysql_sql.y:14675 { yyLOCAL = tree.NewComparisonExpr(tree.NOT_REG_MATCH, yyDollar[1].exprUnion(), yyDollar[4].exprUnion()) } yyVAL.union = yyLOCAL - case 2201: + case 2210: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL tree.Expr -//line mysql_sql.y:14631 +//line mysql_sql.y:14679 { yyLOCAL = tree.NewComparisonExpr(tree.NOT_REG_MATCH, yyDollar[1].exprUnion(), yyDollar[4].exprUnion()) } yyVAL.union = yyLOCAL - case 2202: + case 2211: yyDollar = yyS[yypt-5 : yypt+1] var yyLOCAL tree.Expr -//line mysql_sql.y:14635 +//line mysql_sql.y:14683 { yyLOCAL = tree.NewRangeCond(false, yyDollar[1].exprUnion(), yyDollar[3].exprUnion(), yyDollar[5].exprUnion()) } yyVAL.union = yyLOCAL - case 2203: + case 2212: yyDollar = yyS[yypt-6 : yypt+1] var yyLOCAL tree.Expr -//line mysql_sql.y:14639 +//line mysql_sql.y:14687 { yyLOCAL = tree.NewRangeCond(true, yyDollar[1].exprUnion(), yyDollar[4].exprUnion(), yyDollar[6].exprUnion()) } yyVAL.union = yyLOCAL - case 2204: + case 2213: yyDollar = yyS[yypt-6 : yypt+1] var yyLOCAL tree.Expr -//line mysql_sql.y:14643 +//line mysql_sql.y:14691 { yyLOCAL = tree.NewRangeCond(true, yyDollar[1].exprUnion(), yyDollar[4].exprUnion(), yyDollar[6].exprUnion()) } yyVAL.union = yyLOCAL - case 2206: + case 2215: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL tree.Expr -//line mysql_sql.y:14649 +//line mysql_sql.y:14697 { yyLOCAL = nil } yyVAL.union = yyLOCAL - case 2207: + case 2216: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL tree.Expr -//line mysql_sql.y:14653 +//line mysql_sql.y:14701 { yyLOCAL = yyDollar[2].exprUnion() } yyVAL.union = yyLOCAL - case 2210: + case 2219: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.Expr -//line mysql_sql.y:14663 +//line mysql_sql.y:14711 { yyLOCAL = yyDollar[1].tupleUnion() } yyVAL.union = yyLOCAL - case 2211: + case 2220: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.Expr -//line mysql_sql.y:14667 +//line mysql_sql.y:14715 { yyLOCAL = yyDollar[1].subqueryUnion() } yyVAL.union = yyLOCAL - case 2212: + case 2221: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.ComparisonOp -//line mysql_sql.y:14674 +//line mysql_sql.y:14722 { yyLOCAL = tree.ALL } yyVAL.union = yyLOCAL - case 2213: + case 2222: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.ComparisonOp -//line mysql_sql.y:14678 +//line mysql_sql.y:14726 { yyLOCAL = tree.ANY } yyVAL.union = yyLOCAL - case 2214: + case 2223: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.ComparisonOp -//line mysql_sql.y:14682 +//line mysql_sql.y:14730 { yyLOCAL = tree.SOME } yyVAL.union = yyLOCAL - case 2215: + case 2224: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.ComparisonOp -//line mysql_sql.y:14688 +//line mysql_sql.y:14736 { yyLOCAL = tree.EQUAL } yyVAL.union = yyLOCAL - case 2216: + case 2225: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.ComparisonOp -//line mysql_sql.y:14692 +//line mysql_sql.y:14740 { yyLOCAL = tree.LESS_THAN } yyVAL.union = yyLOCAL - case 2217: + case 2226: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.ComparisonOp -//line mysql_sql.y:14696 +//line mysql_sql.y:14744 { yyLOCAL = tree.GREAT_THAN } yyVAL.union = yyLOCAL - case 2218: + case 2227: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.ComparisonOp -//line mysql_sql.y:14700 +//line mysql_sql.y:14748 { yyLOCAL = tree.LESS_THAN_EQUAL } yyVAL.union = yyLOCAL - case 2219: + case 2228: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.ComparisonOp -//line mysql_sql.y:14704 +//line mysql_sql.y:14752 { yyLOCAL = tree.GREAT_THAN_EQUAL } yyVAL.union = yyLOCAL - case 2220: + case 2229: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.ComparisonOp -//line mysql_sql.y:14708 +//line mysql_sql.y:14756 { yyLOCAL = tree.NOT_EQUAL } yyVAL.union = yyLOCAL - case 2221: + case 2230: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.ComparisonOp -//line mysql_sql.y:14712 +//line mysql_sql.y:14760 { yyLOCAL = tree.NULL_SAFE_EQUAL } yyVAL.union = yyLOCAL - case 2222: + case 2231: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL tree.ColumnAttribute -//line mysql_sql.y:14718 +//line mysql_sql.y:14766 { yyLOCAL = tree.NewAttributePrimaryKey() } yyVAL.union = yyLOCAL - case 2223: + case 2232: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL tree.ColumnAttribute -//line mysql_sql.y:14722 +//line mysql_sql.y:14770 { yyLOCAL = tree.NewAttributeUniqueKey() } yyVAL.union = yyLOCAL - case 2224: + case 2233: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.ColumnAttribute -//line mysql_sql.y:14726 +//line mysql_sql.y:14774 { yyLOCAL = tree.NewAttributeUnique() } yyVAL.union = yyLOCAL - case 2225: + case 2234: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.ColumnAttribute -//line mysql_sql.y:14730 +//line mysql_sql.y:14778 { yyLOCAL = tree.NewAttributeKey() } yyVAL.union = yyLOCAL - case 2226: + case 2235: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.Expr -//line mysql_sql.y:14736 +//line mysql_sql.y:14784 { str := fmt.Sprintf("%v", yyDollar[1].item) switch v := yyDollar[1].item.(type) { @@ -33475,35 +33604,35 @@ yydefault: } } yyVAL.union = yyLOCAL - case 2227: + case 2236: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.Expr -//line mysql_sql.y:14749 +//line mysql_sql.y:14797 { fval := yyDollar[1].item.(float64) yyLOCAL = tree.NewNumVal(fval, yylex.(*Lexer).scanner.LastToken, false, tree.P_float64) } yyVAL.union = yyLOCAL - case 2228: + case 2237: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.Expr -//line mysql_sql.y:14754 +//line mysql_sql.y:14802 { yyLOCAL = tree.NewNumVal(yyDollar[1].str, yyDollar[1].str, false, tree.P_decimal) } yyVAL.union = yyLOCAL - case 2229: + case 2238: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.Expr -//line mysql_sql.y:14760 +//line mysql_sql.y:14808 { yyLOCAL = tree.NewNumVal(yyDollar[1].str, yyDollar[1].str, false, tree.P_char) } yyVAL.union = yyLOCAL - case 2230: + case 2239: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.Expr -//line mysql_sql.y:14764 +//line mysql_sql.y:14812 { str := fmt.Sprintf("%v", yyDollar[1].item) switch v := yyDollar[1].item.(type) { @@ -33517,101 +33646,101 @@ yydefault: } } yyVAL.union = yyLOCAL - case 2231: + case 2240: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.Expr -//line mysql_sql.y:14777 +//line mysql_sql.y:14825 { fval := yyDollar[1].item.(float64) yyLOCAL = tree.NewNumVal(fval, yylex.(*Lexer).scanner.LastToken, false, tree.P_float64) } yyVAL.union = yyLOCAL - case 2232: + case 2241: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.Expr -//line mysql_sql.y:14782 +//line mysql_sql.y:14830 { yyLOCAL = tree.NewNumVal(true, "true", false, tree.P_bool) } yyVAL.union = yyLOCAL - case 2233: + case 2242: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.Expr -//line mysql_sql.y:14786 +//line mysql_sql.y:14834 { yyLOCAL = tree.NewNumVal(false, "false", false, tree.P_bool) } yyVAL.union = yyLOCAL - case 2234: + case 2243: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.Expr -//line mysql_sql.y:14790 +//line mysql_sql.y:14838 { yyLOCAL = tree.NewNumVal("null", "null", false, tree.P_null) } yyVAL.union = yyLOCAL - case 2235: + case 2244: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.Expr -//line mysql_sql.y:14794 +//line mysql_sql.y:14842 { yyLOCAL = tree.NewNumVal(yyDollar[1].str, yyDollar[1].str, false, tree.P_hexnum) } yyVAL.union = yyLOCAL - case 2236: + case 2245: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL tree.Expr -//line mysql_sql.y:14798 +//line mysql_sql.y:14846 { yyLOCAL = tree.NewNumVal(yyDollar[2].str, yyDollar[2].str, false, tree.P_ScoreBinaryHexnum) } yyVAL.union = yyLOCAL - case 2237: + case 2246: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.Expr -//line mysql_sql.y:14802 +//line mysql_sql.y:14850 { yyLOCAL = tree.NewNumVal(yyDollar[1].str, yyDollar[1].str, false, tree.P_decimal) } yyVAL.union = yyLOCAL - case 2238: + case 2247: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.Expr -//line mysql_sql.y:14806 +//line mysql_sql.y:14854 { yyLOCAL = tree.NewNumVal(yyDollar[1].str, yyDollar[1].str, false, tree.P_bit) } yyVAL.union = yyLOCAL - case 2239: + case 2248: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.Expr -//line mysql_sql.y:14810 +//line mysql_sql.y:14858 { yyLOCAL = tree.NewParamExpr(yylex.(*Lexer).GetParamIndex()) } yyVAL.union = yyLOCAL - case 2240: + case 2249: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL tree.Expr -//line mysql_sql.y:14814 +//line mysql_sql.y:14862 { yyLOCAL = tree.NewNumVal(yyDollar[2].str, yyDollar[2].str, false, tree.P_ScoreBinary) } yyVAL.union = yyLOCAL - case 2241: + case 2250: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL *tree.T -//line mysql_sql.y:14820 +//line mysql_sql.y:14868 { yyLOCAL = yyDollar[1].columnTypeUnion() yyLOCAL.InternalType.Unsigned = yyDollar[2].unsignedOptUnion() yyLOCAL.InternalType.Zerofill = yyDollar[3].zeroFillOptUnion() } yyVAL.union = yyLOCAL - case 2245: + case 2254: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL *tree.T -//line mysql_sql.y:14829 +//line mysql_sql.y:14877 { locale := "" yyLOCAL = &tree.T{ @@ -33625,27 +33754,27 @@ yydefault: } } yyVAL.union = yyLOCAL - case 2246: + case 2255: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL *tree.T -//line mysql_sql.y:14844 +//line mysql_sql.y:14892 { yyLOCAL = yyDollar[1].columnTypeUnion() yyLOCAL.InternalType.DisplayWith = yyDollar[2].lengthOptUnion() } yyVAL.union = yyLOCAL - case 2247: + case 2256: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL *tree.T -//line mysql_sql.y:14849 +//line mysql_sql.y:14897 { yyLOCAL = yyDollar[1].columnTypeUnion() } yyVAL.union = yyLOCAL - case 2248: + case 2257: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL *tree.T -//line mysql_sql.y:14855 +//line mysql_sql.y:14903 { locale := "" yyLOCAL = &tree.T{ @@ -33658,10 +33787,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 2249: + case 2258: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL *tree.T -//line mysql_sql.y:14867 +//line mysql_sql.y:14915 { locale := "" yyLOCAL = &tree.T{ @@ -33674,10 +33803,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 2250: + case 2259: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL *tree.T -//line mysql_sql.y:14879 +//line mysql_sql.y:14927 { locale := "" yyLOCAL = &tree.T{ @@ -33690,10 +33819,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 2251: + case 2260: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL *tree.T -//line mysql_sql.y:14891 +//line mysql_sql.y:14939 { locale := "" yyLOCAL = &tree.T{ @@ -33707,10 +33836,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 2252: + case 2261: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL *tree.T -//line mysql_sql.y:14904 +//line mysql_sql.y:14952 { locale := "" yyLOCAL = &tree.T{ @@ -33724,10 +33853,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 2253: + case 2262: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL *tree.T -//line mysql_sql.y:14917 +//line mysql_sql.y:14965 { locale := "" yyLOCAL = &tree.T{ @@ -33741,10 +33870,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 2254: + case 2263: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL *tree.T -//line mysql_sql.y:14930 +//line mysql_sql.y:14978 { locale := "" yyLOCAL = &tree.T{ @@ -33758,10 +33887,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 2255: + case 2264: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL *tree.T -//line mysql_sql.y:14943 +//line mysql_sql.y:14991 { locale := "" yyLOCAL = &tree.T{ @@ -33775,10 +33904,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 2256: + case 2265: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL *tree.T -//line mysql_sql.y:14956 +//line mysql_sql.y:15004 { locale := "" yyLOCAL = &tree.T{ @@ -33792,10 +33921,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 2257: + case 2266: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL *tree.T -//line mysql_sql.y:14969 +//line mysql_sql.y:15017 { locale := "" yyLOCAL = &tree.T{ @@ -33809,10 +33938,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 2258: + case 2267: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL *tree.T -//line mysql_sql.y:14982 +//line mysql_sql.y:15030 { locale := "" yyLOCAL = &tree.T{ @@ -33826,10 +33955,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 2259: + case 2268: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL *tree.T -//line mysql_sql.y:14995 +//line mysql_sql.y:15043 { locale := "" yyLOCAL = &tree.T{ @@ -33843,10 +33972,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 2260: + case 2269: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL *tree.T -//line mysql_sql.y:15008 +//line mysql_sql.y:15056 { locale := "" yyLOCAL = &tree.T{ @@ -33860,10 +33989,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 2261: + case 2270: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL *tree.T -//line mysql_sql.y:15021 +//line mysql_sql.y:15069 { locale := "" yyLOCAL = &tree.T{ @@ -33877,10 +34006,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 2262: + case 2271: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL *tree.T -//line mysql_sql.y:15036 +//line mysql_sql.y:15084 { locale := "" if yyDollar[2].lengthScaleOptUnion().DisplayWith > 255 { @@ -33908,10 +34037,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 2263: + case 2272: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL *tree.T -//line mysql_sql.y:15063 +//line mysql_sql.y:15111 { // DOUBLE PRECISION is the SQL-standard synonym for DOUBLE (float64). locale := "" @@ -33940,10 +34069,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 2264: + case 2273: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL *tree.T -//line mysql_sql.y:15091 +//line mysql_sql.y:15139 { locale := "" if yyDollar[2].lengthScaleOptUnion().DisplayWith > 255 { @@ -33985,10 +34114,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 2265: + case 2274: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL *tree.T -//line mysql_sql.y:15133 +//line mysql_sql.y:15181 { locale := "" if yyDollar[2].lengthScaleOptUnion().Scale != tree.NotDefineDec && yyDollar[2].lengthScaleOptUnion().Scale > yyDollar[2].lengthScaleOptUnion().DisplayWith { @@ -34037,10 +34166,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 2266: + case 2275: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL *tree.T -//line mysql_sql.y:15181 +//line mysql_sql.y:15229 { locale := "" if yyDollar[2].lengthScaleOptUnion().Scale != tree.NotDefineDec && yyDollar[2].lengthScaleOptUnion().Scale > yyDollar[2].lengthScaleOptUnion().DisplayWith { @@ -34089,10 +34218,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 2267: + case 2276: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL *tree.T -//line mysql_sql.y:15229 +//line mysql_sql.y:15277 { locale := "" width := int32(64) @@ -34114,10 +34243,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 2268: + case 2277: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL *tree.T -//line mysql_sql.y:15252 +//line mysql_sql.y:15300 { locale := "" yyLOCAL = &tree.T{ @@ -34130,10 +34259,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 2269: + case 2278: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL *tree.T -//line mysql_sql.y:15264 +//line mysql_sql.y:15312 { locale := "" if yyDollar[2].lengthOptUnion() < 0 || yyDollar[2].lengthOptUnion() > 6 { @@ -34154,10 +34283,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 2270: + case 2279: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL *tree.T -//line mysql_sql.y:15284 +//line mysql_sql.y:15332 { locale := "" if yyDollar[2].lengthOptUnion() < 0 || yyDollar[2].lengthOptUnion() > 6 { @@ -34178,10 +34307,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 2271: + case 2280: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL *tree.T -//line mysql_sql.y:15304 +//line mysql_sql.y:15352 { locale := "" if yyDollar[2].lengthOptUnion() < 0 || yyDollar[2].lengthOptUnion() > 6 { @@ -34202,10 +34331,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 2272: + case 2281: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL *tree.T -//line mysql_sql.y:15324 +//line mysql_sql.y:15372 { locale := "" yyLOCAL = &tree.T{ @@ -34220,10 +34349,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 2273: + case 2282: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL *tree.T -//line mysql_sql.y:15340 +//line mysql_sql.y:15388 { locale := "" yyLOCAL = &tree.T{ @@ -34237,10 +34366,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 2274: + case 2283: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL *tree.T -//line mysql_sql.y:15353 +//line mysql_sql.y:15401 { locale := "" yyLOCAL = &tree.T{ @@ -34254,10 +34383,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 2275: + case 2284: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL *tree.T -//line mysql_sql.y:15366 +//line mysql_sql.y:15414 { locale := "" yyLOCAL = &tree.T{ @@ -34271,10 +34400,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 2276: + case 2285: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL *tree.T -//line mysql_sql.y:15379 +//line mysql_sql.y:15427 { locale := "" yyLOCAL = &tree.T{ @@ -34288,10 +34417,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 2277: + case 2286: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL *tree.T -//line mysql_sql.y:15392 +//line mysql_sql.y:15440 { locale := "" yyLOCAL = &tree.T{ @@ -34304,10 +34433,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 2278: + case 2287: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL *tree.T -//line mysql_sql.y:15404 +//line mysql_sql.y:15452 { locale := "" yyLOCAL = &tree.T{ @@ -34320,10 +34449,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 2279: + case 2288: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL *tree.T -//line mysql_sql.y:15416 +//line mysql_sql.y:15464 { locale := "" yyLOCAL = &tree.T{ @@ -34336,10 +34465,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 2280: + case 2289: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL *tree.T -//line mysql_sql.y:15428 +//line mysql_sql.y:15476 { locale := "" yyLOCAL = &tree.T{ @@ -34353,10 +34482,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 2281: + case 2290: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL *tree.T -//line mysql_sql.y:15441 +//line mysql_sql.y:15489 { locale := "" yyLOCAL = &tree.T{ @@ -34369,10 +34498,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 2282: + case 2291: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL *tree.T -//line mysql_sql.y:15453 +//line mysql_sql.y:15501 { locale := "" yyLOCAL = &tree.T{ @@ -34385,10 +34514,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 2283: + case 2292: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL *tree.T -//line mysql_sql.y:15465 +//line mysql_sql.y:15513 { locale := "" yyLOCAL = &tree.T{ @@ -34401,10 +34530,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 2284: + case 2293: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL *tree.T -//line mysql_sql.y:15477 +//line mysql_sql.y:15525 { locale := "" yyLOCAL = &tree.T{ @@ -34418,10 +34547,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 2285: + case 2294: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL *tree.T -//line mysql_sql.y:15490 +//line mysql_sql.y:15538 { locale := "" yyLOCAL = &tree.T{ @@ -34434,10 +34563,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 2286: + case 2295: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL *tree.T -//line mysql_sql.y:15502 +//line mysql_sql.y:15550 { locale := "" yyLOCAL = &tree.T{ @@ -34450,10 +34579,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 2287: + case 2296: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL *tree.T -//line mysql_sql.y:15514 +//line mysql_sql.y:15562 { locale := "" yyLOCAL = &tree.T{ @@ -34466,10 +34595,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 2288: + case 2297: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL *tree.T -//line mysql_sql.y:15526 +//line mysql_sql.y:15574 { locale := "" yyLOCAL = &tree.T{ @@ -34482,10 +34611,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 2289: + case 2298: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL *tree.T -//line mysql_sql.y:15538 +//line mysql_sql.y:15586 { locale := "" yyLOCAL = &tree.T{ @@ -34499,10 +34628,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 2290: + case 2299: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL *tree.T -//line mysql_sql.y:15551 +//line mysql_sql.y:15599 { locale := "" yyLOCAL = &tree.T{ @@ -34516,10 +34645,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 2291: + case 2300: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL *tree.T -//line mysql_sql.y:15564 +//line mysql_sql.y:15612 { locale := "" yyLOCAL = &tree.T{ @@ -34533,10 +34662,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 2292: + case 2301: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL *tree.T -//line mysql_sql.y:15577 +//line mysql_sql.y:15625 { locale := "" yyLOCAL = &tree.T{ @@ -34550,10 +34679,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 2293: + case 2302: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL *tree.T -//line mysql_sql.y:15590 +//line mysql_sql.y:15638 { locale := "" yyLOCAL = &tree.T{ @@ -34567,10 +34696,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 2294: + case 2303: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL *tree.T -//line mysql_sql.y:15603 +//line mysql_sql.y:15651 { locale := "" yyLOCAL = &tree.T{ @@ -34584,10 +34713,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 2295: + case 2304: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL *tree.T -//line mysql_sql.y:15616 +//line mysql_sql.y:15664 { locale := "" yyLOCAL = &tree.T{ @@ -34601,10 +34730,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 2296: + case 2305: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL *tree.T -//line mysql_sql.y:15629 +//line mysql_sql.y:15677 { locale := "" yyLOCAL = &tree.T{ @@ -34618,10 +34747,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 2297: + case 2306: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL *tree.T -//line mysql_sql.y:15642 +//line mysql_sql.y:15690 { locale := "" yyLOCAL = &tree.T{ @@ -34635,20 +34764,20 @@ yydefault: } } yyVAL.union = yyLOCAL - case 2298: + case 2307: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:15657 +//line mysql_sql.y:15705 { yyLOCAL = &tree.Do{ Exprs: yyDollar[2].exprsUnion(), } } yyVAL.union = yyLOCAL - case 2299: + case 2308: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:15665 +//line mysql_sql.y:15713 { yyDollar[2].selectUnion().IsPerform = true if intoErr := tree.ValidatePerformSelectIntoPlacement(yyDollar[2].selectUnion()); intoErr != "" { @@ -34658,10 +34787,10 @@ yydefault: yyLOCAL = yyDollar[2].selectUnion() } yyVAL.union = yyLOCAL - case 2300: + case 2309: yyDollar = yyS[yypt-7 : yypt+1] var yyLOCAL *tree.Select -//line mysql_sql.y:15676 +//line mysql_sql.y:15724 { yyLOCAL = &tree.Select{Select: yyDollar[1].selectStatementUnion(), TimeWindow: yyDollar[2].timeWindowUnion(), OrderBy: yyDollar[3].orderByUnion(), Limit: yyDollar[4].limitUnion(), RankOption: yyDollar[5].rankOptionUnion(), Ep: tree.SelectIntoExportOr(yyDollar[1].selectStatementUnion(), yyDollar[6].selectIntoUnion().Export), IntoVars: yyDollar[6].selectIntoUnion().UserVars, SelectLockInfo: yyDollar[7].selectLockInfoUnion()} if intoErr := tree.ValidatePerformSelectIntoPlacement(yyLOCAL); intoErr != "" { @@ -34670,10 +34799,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 2301: + case 2310: yyDollar = yyS[yypt-8 : yypt+1] var yyLOCAL *tree.Select -//line mysql_sql.y:15684 +//line mysql_sql.y:15732 { yyLOCAL = &tree.Select{Select: yyDollar[2].selectStatementUnion(), TimeWindow: yyDollar[3].timeWindowUnion(), OrderBy: yyDollar[4].orderByUnion(), Limit: yyDollar[5].limitUnion(), RankOption: yyDollar[6].rankOptionUnion(), Ep: tree.SelectIntoExportOr(yyDollar[2].selectStatementUnion(), yyDollar[7].selectIntoUnion().Export), IntoVars: yyDollar[7].selectIntoUnion().UserVars, SelectLockInfo: yyDollar[8].selectLockInfoUnion(), With: yyDollar[1].withClauseUnion()} if intoErr := tree.ValidatePerformSelectIntoPlacement(yyLOCAL); intoErr != "" { @@ -34682,10 +34811,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 2302: + case 2311: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:15694 +//line mysql_sql.y:15742 { yyLOCAL = &tree.Declare{ Variables: yyDollar[2].strsUnion(), @@ -34694,10 +34823,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 2303: + case 2312: yyDollar = yyS[yypt-5 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:15703 +//line mysql_sql.y:15751 { yyLOCAL = &tree.Declare{ Variables: yyDollar[2].strsUnion(), @@ -34706,83 +34835,83 @@ yydefault: } } yyVAL.union = yyLOCAL - case 2304: + case 2313: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL *tree.T -//line mysql_sql.y:15713 +//line mysql_sql.y:15761 { yyLOCAL = tree.NewSpatialType(yyDollar[1].str) } yyVAL.union = yyLOCAL - case 2323: + case 2332: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL []string -//line mysql_sql.y:15741 +//line mysql_sql.y:15789 { yyLOCAL = make([]string, 0, 4) yyLOCAL = append(yyLOCAL, yyDollar[1].str) } yyVAL.union = yyLOCAL - case 2324: + case 2333: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL []string -//line mysql_sql.y:15746 +//line mysql_sql.y:15794 { yyLOCAL = append(yyDollar[1].strsUnion(), yyDollar[3].str) } yyVAL.union = yyLOCAL - case 2325: + case 2334: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL int32 -//line mysql_sql.y:15752 +//line mysql_sql.y:15800 { yyLOCAL = 0 } yyVAL.union = yyLOCAL - case 2327: + case 2336: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL int32 -//line mysql_sql.y:15759 +//line mysql_sql.y:15807 { yyLOCAL = 0 } yyVAL.union = yyLOCAL - case 2328: + case 2337: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL int32 -//line mysql_sql.y:15763 +//line mysql_sql.y:15811 { yyLOCAL = int32(yyDollar[2].item.(int64)) } yyVAL.union = yyLOCAL - case 2329: + case 2338: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL int32 -//line mysql_sql.y:15768 +//line mysql_sql.y:15816 { yyLOCAL = int32(-1) } yyVAL.union = yyLOCAL - case 2330: + case 2339: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL int32 -//line mysql_sql.y:15772 +//line mysql_sql.y:15820 { yyLOCAL = int32(yyDollar[2].item.(int64)) } yyVAL.union = yyLOCAL - case 2331: + case 2340: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL int32 -//line mysql_sql.y:15778 +//line mysql_sql.y:15826 { yyLOCAL = tree.GetDisplayWith(int32(yyDollar[2].item.(int64))) } yyVAL.union = yyLOCAL - case 2332: + case 2341: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL tree.LengthScaleOpt -//line mysql_sql.y:15784 +//line mysql_sql.y:15832 { yyLOCAL = tree.LengthScaleOpt{ DisplayWith: tree.NotDefineDisplayWidth, @@ -34790,10 +34919,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 2333: + case 2342: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL tree.LengthScaleOpt -//line mysql_sql.y:15791 +//line mysql_sql.y:15839 { yyLOCAL = tree.LengthScaleOpt{ DisplayWith: tree.GetDisplayWith(int32(yyDollar[2].item.(int64))), @@ -34801,10 +34930,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 2334: + case 2343: yyDollar = yyS[yypt-5 : yypt+1] var yyLOCAL tree.LengthScaleOpt -//line mysql_sql.y:15798 +//line mysql_sql.y:15846 { yyLOCAL = tree.LengthScaleOpt{ DisplayWith: tree.GetDisplayWith(int32(yyDollar[2].item.(int64))), @@ -34812,10 +34941,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 2335: + case 2344: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL tree.LengthScaleOpt -//line mysql_sql.y:15807 +//line mysql_sql.y:15855 { yyLOCAL = tree.LengthScaleOpt{ DisplayWith: 10, // MySQL default precision for DECIMAL/NUMERIC @@ -34823,10 +34952,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 2336: + case 2345: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL tree.LengthScaleOpt -//line mysql_sql.y:15814 +//line mysql_sql.y:15862 { yyLOCAL = tree.LengthScaleOpt{ DisplayWith: tree.GetDisplayWith(int32(yyDollar[2].item.(int64))), @@ -34834,10 +34963,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 2337: + case 2346: yyDollar = yyS[yypt-5 : yypt+1] var yyLOCAL tree.LengthScaleOpt -//line mysql_sql.y:15821 +//line mysql_sql.y:15869 { yyLOCAL = tree.LengthScaleOpt{ DisplayWith: tree.GetDisplayWith(int32(yyDollar[2].item.(int64))), @@ -34845,73 +34974,73 @@ yydefault: } } yyVAL.union = yyLOCAL - case 2338: + case 2347: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL bool -//line mysql_sql.y:15830 +//line mysql_sql.y:15878 { yyLOCAL = false } yyVAL.union = yyLOCAL - case 2339: + case 2348: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL bool -//line mysql_sql.y:15834 +//line mysql_sql.y:15882 { yyLOCAL = true } yyVAL.union = yyLOCAL - case 2340: + case 2349: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL bool -//line mysql_sql.y:15838 +//line mysql_sql.y:15886 { yyLOCAL = false } yyVAL.union = yyLOCAL - case 2341: + case 2350: yyDollar = yyS[yypt-0 : yypt+1] -//line mysql_sql.y:15844 +//line mysql_sql.y:15892 { } - case 2342: + case 2351: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL bool -//line mysql_sql.y:15846 +//line mysql_sql.y:15894 { yyLOCAL = true } yyVAL.union = yyLOCAL - case 2346: + case 2355: yyDollar = yyS[yypt-0 : yypt+1] -//line mysql_sql.y:15856 +//line mysql_sql.y:15904 { yyVAL.str = "" } - case 2347: + case 2356: yyDollar = yyS[yypt-1 : yypt+1] -//line mysql_sql.y:15860 +//line mysql_sql.y:15908 { yyVAL.str = string(yyDollar[1].str) } - case 2857: + case 2870: yyDollar = yyS[yypt-0 : yypt+1] -//line mysql_sql.y:16573 +//line mysql_sql.y:16625 { } - case 2858: + case 2871: yyDollar = yyS[yypt-2 : yypt+1] -//line mysql_sql.y:16576 +//line mysql_sql.y:16628 { } - case 2859: + case 2872: yyDollar = yyS[yypt-0 : yypt+1] -//line mysql_sql.y:16580 +//line mysql_sql.y:16632 { } - case 2860: + case 2873: yyDollar = yyS[yypt-2 : yypt+1] -//line mysql_sql.y:16583 +//line mysql_sql.y:16635 { } } diff --git a/pkg/sql/parsers/dialect/mysql/mysql_sql.y b/pkg/sql/parsers/dialect/mysql/mysql_sql.y index 54c21c13d959c..db8dfc565dca6 100644 --- a/pkg/sql/parsers/dialect/mysql/mysql_sql.y +++ b/pkg/sql/parsers/dialect/mysql/mysql_sql.y @@ -152,6 +152,7 @@ func makeWindowSpec(refName *tree.CStr, partitionBy tree.Exprs, orderBy tree.Ord funcArgs tree.FunctionArgs funcArgDecl *tree.FunctionArgDecl funcReturn *tree.ReturnType + pythonFunctionOptions tree.PythonFunctionOptions procName *tree.ProcedureName procArg tree.ProcedureArg @@ -505,6 +506,7 @@ func makeWindowSpec(refName *tree.CStr, partitionBy tree.Exprs, orderBy tree.Ord // Revoke %token REVOKE FUNCTION PRIVILEGES TABLESPACE EXECUTE SUPER GRANT OPTION REFERENCES REPLICATION %token SLAVE CLIENT USAGE RELOAD FILE FILES TEMPORARY ROUTINE EVENT SHUTDOWN +%token CALLED INPUT SCALAR VECTOR // Type Modifiers %token NULLX AUTO_INCREMENT APPROXNUM SIGNED UNSIGNED ZEROFILL ENGINES LOW_CARDINALITY AUTOEXTEND_SIZE @@ -762,7 +764,8 @@ func makeWindowSpec(refName *tree.CStr, partitionBy tree.Exprs, orderBy tree.Ord %type func_arg_decl %type func_return %type func_body_import internal_opt -%type func_lang extension_lang extension_name +%type func_lang extension_lang extension_name func_null_policy func_mode +%type func_python_options_opt %type proc_name %type proc_args_list_opt proc_args_list @@ -8548,7 +8551,7 @@ opt_lang: } create_function_stmt: - CREATE replace_opt FUNCTION func_name '(' func_args_list_opt ')' RETURNS func_return LANGUAGE func_lang func_body_import STRING func_handler_opt + CREATE replace_opt FUNCTION func_name '(' func_args_list_opt ')' RETURNS func_return LANGUAGE func_lang func_body_import STRING func_handler_opt func_python_options_opt { if $13 == "" { yylex.Error("no function body error") @@ -8567,8 +8570,9 @@ create_function_stmt: var Import = $12 var Body = $13 var Handler = $14 + var Options = $15 - $$ = tree.NewCreateFunction( + node := tree.NewCreateFunction( Replace, Name, Args, @@ -8578,6 +8582,9 @@ create_function_stmt: Body, Handler, ) + node.Mode = Options.Mode + node.NullPolicy = Options.NullPolicy + $$ = node } func_name: @@ -8663,6 +8670,47 @@ func_handler: $$ = $2 } +func_python_options_opt: + { + $$ = tree.PythonFunctionOptions{} + } +| MODE func_mode + { + $$ = tree.PythonFunctionOptions{Mode: $2} + } +| func_null_policy + { + $$ = tree.PythonFunctionOptions{NullPolicy: $1} + } +| MODE func_mode func_null_policy + { + $$ = tree.PythonFunctionOptions{Mode: $2, NullPolicy: $3} + } +| func_null_policy MODE func_mode + { + $$ = tree.PythonFunctionOptions{Mode: $3, NullPolicy: $1} + } + +func_mode: + SCALAR + { + $$ = "SCALAR" + } +| VECTOR + { + $$ = "VECTOR" + } + +func_null_policy: + CALLED ON NULL INPUT + { + $$ = "CALLED_ON_NULL_INPUT" + } +| RETURNS NULL ON NULL INPUT + { + $$ = "RETURNS_NULL_ON_NULL_INPUT" + } + create_view_stmt: CREATE view_list_opt VIEW not_exists_opt table_name column_list_opt AS ctas_select_stmt view_tail { @@ -16072,6 +16120,7 @@ non_reserved_keyword: | BLOB | BOOL | BITS_PER_CODE +| CALLED | BRANCH | CATALOG | CATALOGS @@ -16153,6 +16202,7 @@ non_reserved_keyword: | PERSIST | GRANT | INCLUDE +| INPUT | INT | INTEGER | INDEXES @@ -16244,6 +16294,7 @@ non_reserved_keyword: | PROFILES | PROFILE | ROLE +| SCALAR | RULE | RULES | RANGE @@ -16302,6 +16353,7 @@ non_reserved_keyword: | VARBINARY | VARCHAR | VARIABLES +| VECTOR | VIEW | VIRTUAL | WRITE diff --git a/pkg/sql/parsers/dialect/mysql/mysql_sql_test.go b/pkg/sql/parsers/dialect/mysql/mysql_sql_test.go index b213f10e239f3..49564fac3facb 100644 --- a/pkg/sql/parsers/dialect/mysql/mysql_sql_test.go +++ b/pkg/sql/parsers/dialect/mysql/mysql_sql_test.go @@ -104,6 +104,44 @@ func TestDiagnosticCountAndLimitSyntax(t *testing.T) { } } +func TestPythonFunctionOptions(t *testing.T) { + stmt, err := ParseOne(context.Background(), + "create function py (x int) returns int language python as 'def py(ctx, x): return x' handler 'py' mode vector returns null on null input", 1) + require.NoError(t, err) + defer stmt.Free() + + create, ok := stmt.(*tree.CreateFunction) + require.True(t, ok) + require.Equal(t, "VECTOR", create.Mode) + require.Equal(t, "RETURNS_NULL_ON_NULL_INPUT", create.NullPolicy) + require.NoError(t, create.Valid()) + require.Equal(t, + "create function py (x int) returns int language python as 'def py(ctx, x): return x' handler 'py' mode vector returns null on null input", + tree.String(stmt, dialect.MYSQL)) + + reversed, err := ParseOne(context.Background(), + "create function py (x int) returns int language python as 'def py(ctx, x): return x' handler 'py' returns null on null input mode scalar", 1) + require.NoError(t, err) + defer reversed.Free() + reversedCreate := reversed.(*tree.CreateFunction) + require.Equal(t, "SCALAR", reversedCreate.Mode) + require.Equal(t, "RETURNS_NULL_ON_NULL_INPUT", reversedCreate.NullPolicy) + + called, err := ParseOne(context.Background(), + "create function py (x int) returns int language python as 'def py(ctx, x): return x' handler 'py' mode vector called on null input", 1) + require.NoError(t, err) + defer called.Free() + calledCreate := called.(*tree.CreateFunction) + require.Equal(t, "VECTOR", calledCreate.Mode) + require.Equal(t, "CALLED_ON_NULL_INPUT", calledCreate.NullPolicy) + + sqlOptions, err := ParseOne(context.Background(), + "create function sql_fn (x int) returns int language sql as 'select $1' mode vector", 1) + require.NoError(t, err) + defer sqlOptions.Free() + require.ErrorContains(t, sqlOptions.(*tree.CreateFunction).Valid(), "require LANGUAGE PYTHON") +} + func TestCreateTablePreservesIndexIdentifierCase(t *testing.T) { stmt, err := ParseOne(context.Background(), "create table t (id int, v varchar(20), key MixedCaseIdx(v), unique key `UniQue_Mix`(id))", 1) diff --git a/pkg/sql/parsers/tree/function.go b/pkg/sql/parsers/tree/function.go index 267509c212720..f5d6d16476bcc 100644 --- a/pkg/sql/parsers/tree/function.go +++ b/pkg/sql/parsers/tree/function.go @@ -179,6 +179,16 @@ type CreateFunction struct { Import bool Body string Handler string + // Mode and NullPolicy are Python routine options. Empty values mean that + // the language default applies; the frontend persists the resolved values + // in the routine body so execution never has to infer them later. + Mode string + NullPolicy string +} + +type PythonFunctionOptions struct { + Mode string + NullPolicy string } func NewCreateFunction(replace bool, name *FunctionName, args FunctionArgs, returnType *ReturnType, lang string, import_ bool, body string, handler string) *CreateFunction { @@ -201,8 +211,23 @@ func (node *CreateFunction) Valid() error { if node.Import { return moerr.NewInvalidInputNoCtx("import") } + if node.Mode != "" || node.NullPolicy != "" { + return moerr.NewInvalidInputNoCtx("Python function options require LANGUAGE PYTHON") + } return nil case string(PYTHON): + if node.Mode != "" { + node.Mode = strings.ToUpper(node.Mode) + if node.Mode != "SCALAR" && node.Mode != "VECTOR" { + return moerr.NewInvalidArgNoCtx("Python function mode", node.Mode) + } + } + if node.NullPolicy != "" { + node.NullPolicy = strings.ToUpper(node.NullPolicy) + if node.NullPolicy != "CALLED_ON_NULL_INPUT" && node.NullPolicy != "RETURNS_NULL_ON_NULL_INPUT" { + return moerr.NewInvalidArgNoCtx("Python function NULL policy", node.NullPolicy) + } + } return nil default: return moerr.NewInvalidArgNoCtx("function language", node.Language) @@ -251,6 +276,14 @@ func (node *CreateFunction) Format(ctx *FmtCtx) { ctx.WriteString(node.Handler) ctx.WriteString("'") } + if node.Mode != "" { + ctx.WriteString(" mode ") + ctx.WriteString(strings.ToLower(node.Mode)) + } + if node.NullPolicy != "" { + ctx.WriteByte(' ') + ctx.WriteString(strings.ToLower(strings.ReplaceAll(node.NullPolicy, "_", " "))) + } } func (node *CreateFunction) GetStatementType() string { return "CreateFunction" } diff --git a/pkg/udf/protocol/protocol.go b/pkg/udf/protocol/protocol.go index 3b3722d908514..13d0df81d9b21 100644 --- a/pkg/udf/protocol/protocol.go +++ b/pkg/udf/protocol/protocol.go @@ -182,7 +182,7 @@ func (s *Sequence) AcceptResult(sequence uint64) error { func (s *Sequence) AcknowledgeResults(sequence uint64) error { s.mu.Lock() defer s.mu.Unlock() - if sequence < s.ackedResult || sequence > s.lastResult { + if sequence == 0 || sequence < s.ackedResult || sequence > s.lastResult { return fmt.Errorf("%w: result ACK %d outside [%d,%d]", ErrSequence, sequence, s.ackedResult, s.lastResult) } s.ackedResult = sequence diff --git a/pkg/udf/protocol/protocol_test.go b/pkg/udf/protocol/protocol_test.go index 7ae2198ecb2aa..0ab88d5fc3148 100644 --- a/pkg/udf/protocol/protocol_test.go +++ b/pkg/udf/protocol/protocol_test.go @@ -70,6 +70,7 @@ func TestSequenceKeepsHalfCloseIndependentFromResults(t *testing.T) { require.ErrorIs(t, sequence.AcceptInput(3), ErrSequence) require.ErrorIs(t, sequence.AcknowledgeResults(3), ErrSequence) + require.ErrorIs(t, sequence.AcknowledgeResults(0), ErrSequence) require.NoError(t, sequence.EndInput(2)) } diff --git a/pkg/udf/python/arrow.go b/pkg/udf/python/arrow.go index 484d16aff3b09..b85fa657ce943 100644 --- a/pkg/udf/python/arrow.go +++ b/pkg/udf/python/arrow.go @@ -600,18 +600,18 @@ func AppendArrowResult(descriptor TypeDescriptor, input arrow.Array, result vect if result == nil { return fmt.Errorf("python udf: missing result wrapper") } - if input.Len() == 0 { - return nil - } - if mp == nil { - return fmt.Errorf("python udf: missing memory pool for Arrow result") - } if err := validateArrayType(descriptor, input); err != nil { return err } if err := validateArrowValueDomain(descriptor, input); err != nil { return err } + if input.Len() == 0 { + return nil + } + if mp == nil { + return fmt.Errorf("python udf: missing memory pool for Arrow result") + } result.GetResultVector().SetTypeScale(descriptor.Scale) typ := types.T(descriptor.TypeID) for i := 0; i < input.Len(); i++ { diff --git a/pkg/udf/python/arrow_test.go b/pkg/udf/python/arrow_test.go index 716290debabf8..acc8ca40f451b 100644 --- a/pkg/udf/python/arrow_test.go +++ b/pkg/udf/python/arrow_test.go @@ -12,7 +12,9 @@ import ( "github.com/apache/arrow-go/v18/arrow/array" "github.com/apache/arrow-go/v18/arrow/decimal128" "github.com/apache/arrow-go/v18/arrow/memory" + "github.com/matrixorigin/matrixone/pkg/common/mpool" "github.com/matrixorigin/matrixone/pkg/container/types" + "github.com/matrixorigin/matrixone/pkg/container/vector" "github.com/stretchr/testify/require" ) @@ -94,6 +96,24 @@ func TestAppendArrowResultRejectsMissingConsumer(t *testing.T) { require.ErrorContains(t, AppendArrowResult(descriptor, values, nil, nil), "missing result wrapper") } +func TestAppendArrowResultValidatesEmptyArrayType(t *testing.T) { + allocator := memory.NewGoAllocator() + stringBuilder := array.NewStringBuilder(allocator) + values := stringBuilder.NewStringArray() + defer values.Release() + + mp := mpool.MustNewZeroNoFixed() + result := vector.NewFunctionResultWrapper(types.T_int64.ToType(), mp) + defer func() { + result.Free() + mpool.DeleteMPool(mp) + }() + descriptor, err := NewTypeDescriptor(types.T_int64.ToType()) + require.NoError(t, err) + + require.ErrorContains(t, AppendArrowResult(descriptor, values, result, mp), "Arrow type") +} + func TestZeroArgumentRecordPreservesRows(t *testing.T) { record := array.NewRecordBatch(arrow.NewSchema(nil, nil), nil, 3) defer record.Release() diff --git a/pkg/udf/python/gateway.go b/pkg/udf/python/gateway.go index 8cba52c105cee..d99c397691699 100644 --- a/pkg/udf/python/gateway.go +++ b/pkg/udf/python/gateway.go @@ -39,12 +39,11 @@ const ( var errGatewayClosed = errors.New("python udf gateway is closed") type Gateway struct { - cfg ClientConfig - conn *grpc.ClientConn - flight flight.FlightServiceClient - mu sync.Mutex - lifecycle sync.RWMutex - closed bool + cfg ClientConfig + conn *grpc.ClientConn + flight flight.FlightServiceClient + mu sync.Mutex + closed bool } func NewGateway(cfg ClientConfig) (*Gateway, error) { @@ -66,13 +65,18 @@ func NewGateway(cfg ClientConfig) (*Gateway, error) { func (g *Gateway) Language() string { return udf.LanguagePython } func (g *Gateway) connect() error { + _, err := g.flightClient() + return err +} + +func (g *Gateway) flightClient() (flight.FlightServiceClient, error) { g.mu.Lock() defer g.mu.Unlock() if g.closed { - return errGatewayClosed + return nil, errGatewayClosed } if g.flight != nil { - return nil + return g.flight, nil } conn, err := grpc.NewClient(g.cfg.ServerAddress, grpc.WithTransportCredentials(insecure.NewCredentials()), @@ -82,29 +86,31 @@ func (g *Gateway) connect() error { ), ) if err != nil { - return err + return nil, err } g.conn = conn g.flight = flight.NewFlightServiceClient(conn) - return nil + return g.flight, nil } func (g *Gateway) Close() error { - g.lifecycle.Lock() - defer g.lifecycle.Unlock() g.mu.Lock() - defer g.mu.Unlock() if g.closed { + g.mu.Unlock() return nil } g.closed = true - if g.conn == nil { + conn := g.conn + g.conn = nil + g.mu.Unlock() + if conn == nil { return nil } - err := g.conn.Close() - g.conn = nil - g.flight = nil - return err + // Closing the connection is deliberately outside the mutex. gRPC uses + // this operation to interrupt in-flight exchanges; holding a lifecycle + // read lock until Execute returns would make shutdown wait for the very RPC + // it needs to cancel. + return conn.Close() } type openPayload struct { @@ -124,8 +130,6 @@ type openPayload struct { } func (g *Gateway) Execute(ctx context.Context, invocation *udf.Invocation, result vector.FunctionResultWrapper, mp *mpool.MPool) error { - g.lifecycle.RLock() - defer g.lifecycle.RUnlock() g.mu.Lock() closed := g.closed g.mu.Unlock() @@ -158,7 +162,8 @@ func (g *Gateway) Execute(ctx context.Context, invocation *udf.Invocation, resul if invocation.Length == 0 { return result.PreExtendAndReset(0) } - if err := g.connect(); err != nil { + client, err := g.flightClient() + if err != nil { return err } openBody, err := json.Marshal(openPayload{Handler: invocation.Handler, Source: invocation.Source, Mode: invocation.Mode, NullPolicy: invocation.NullPolicy, ABIContract: invocation.ABIContract, AdapterVersion: invocation.AdapterVersion, SDKVersion: invocation.SDKVersion, Context: cloneMap(invocation.Context), Args: args, Return: returnDescriptor, MaxBatchBytes: g.cfg.MaxBatchBytes, MaxBatchRows: g.cfg.MaxBatchRows, HandlerTimeoutSeconds: g.cfg.RequestTimeout.Seconds()}) @@ -170,7 +175,7 @@ func (g *Gateway) Execute(ctx context.Context, invocation *udf.Invocation, resul return err } - stream, err := g.flight.DoExchange(streamCtx) + stream, err := client.DoExchange(streamCtx) if err != nil { return fmt.Errorf("python udf: open Arrow Flight exchange: %w", err) } @@ -187,6 +192,7 @@ func (g *Gateway) Execute(ctx context.Context, invocation *udf.Invocation, resul var sequence protocol.Sequence var schemaFrame *ArrowFrame + schemaReady := false var inputSchema []byte rows := 0 var start int64 @@ -241,7 +247,7 @@ func (g *Gateway) Execute(ctx context.Context, invocation *udf.Invocation, resul } inputEnded = true } - batchRows, err := g.receiveResultBatch(streamCtx, stream, invocation.Tuple, sequenceNumber, batch.Rows, &schemaFrame, returnDescriptor, result, mp, &sequence) + batchRows, err := g.receiveResultBatch(streamCtx, client, stream, invocation.Tuple, sequenceNumber, batch.Rows, &schemaFrame, &schemaReady, returnDescriptor, result, mp, &sequence) if err != nil { return err } @@ -255,7 +261,7 @@ func (g *Gateway) Execute(ctx context.Context, invocation *udf.Invocation, resul if !inputEnded || lastSequence != batchIndex { return fmt.Errorf("python udf: input stream did not reach EndInput") } - return g.receiveFinish(streamCtx, stream, invocation.Tuple, invocation.Length, rows, &sequence) + return g.receiveFinish(streamCtx, client, stream, invocation.Tuple, invocation.Length, rows, &sequence) } func encodeInputBatch(inputs []*vector.Vector, args []types.Type, start, remaining, maxBytes, maxRows int64) (encodedRecordBatch, error) { @@ -322,11 +328,13 @@ func encodeInputBatch(inputs []*vector.Vector, args []types.Type, start, remaini func (g *Gateway) receiveResultBatch( ctx context.Context, + client flight.FlightServiceClient, stream flight.FlightService_DoExchangeClient, tuple protocol.FencingTuple, sequenceNumber uint64, expectedRows int64, schemaFrame **ArrowFrame, + schemaReady *bool, returnDescriptor TypeDescriptor, result vector.FunctionResultWrapper, mp *mpool.MPool, @@ -348,16 +356,18 @@ func (g *Gateway) receiveResultBatch( if len(data.DataBody) != 0 { return 0, fmt.Errorf("python udf: result schema contains a body") } - if len(data.AppMetadata) == 0 { - return 0, fmt.Errorf("python udf: result schema is missing metadata") - } - control, err := protocol.UnmarshalControl(data.AppMetadata) - if err != nil || control.Tuple != tuple || control.Kind != "ResultSchema" { - return 0, fmt.Errorf("python udf: invalid result schema metadata") - } *schemaFrame = &ArrowFrame{Header: append([]byte(nil), data.DataHeader...)} + if len(data.AppMetadata) != 0 { + if err := validateResultSchemaMetadata(data.AppMetadata, tuple); err != nil { + return 0, err + } + *schemaReady = true + } continue } + if !*schemaReady { + return 0, fmt.Errorf("python udf: result schema metadata was not received") + } control, err := protocol.UnmarshalControl(data.AppMetadata) if err != nil { return 0, err @@ -397,7 +407,7 @@ func (g *Gateway) receiveResultBatch( return 0, err } decoded.Release() - if err := g.action(ctx, "AcknowledgeResults", protocol.Control{Kind: "AcknowledgeResults", Tuple: tuple, AckSequence: control.Sequence}); err != nil { + if err := g.action(ctx, client, "AcknowledgeResults", protocol.Control{Kind: "AcknowledgeResults", Tuple: tuple, AckSequence: control.Sequence}); err != nil { return 0, err } if err := sequence.AcknowledgeResults(control.Sequence); err != nil { @@ -414,6 +424,10 @@ func (g *Gateway) receiveResultBatch( } switch control.Kind { case "ResultSchema": + if *schemaFrame == nil || *schemaReady { + return 0, fmt.Errorf("python udf: invalid result schema metadata") + } + *schemaReady = true continue case "InputConsumed": if control.Sequence != sequenceNumber { @@ -430,8 +444,17 @@ func (g *Gateway) receiveResultBatch( } } +func validateResultSchemaMetadata(metadata []byte, tuple protocol.FencingTuple) error { + control, err := protocol.UnmarshalControl(metadata) + if err != nil || control.Tuple != tuple || control.Kind != "ResultSchema" { + return fmt.Errorf("python udf: invalid result schema metadata") + } + return nil +} + func (g *Gateway) receiveFinish( ctx context.Context, + client flight.FlightServiceClient, stream flight.FlightService_DoExchangeClient, tuple protocol.FencingTuple, expectedRows, rows int, @@ -466,7 +489,7 @@ func (g *Gateway) receiveFinish( !inputEnded || control.LastSequence != lastInput || lastResult != lastInput || acked != lastInput || !sequence.ReadyToFinish() { return fmt.Errorf("python udf: invalid Finish status=%q rows=%d", control.Status, rows) } - return g.action(ctx, "AcknowledgeFinish", protocol.Control{Kind: "AcknowledgeFinish", Tuple: tuple, FinishID: control.FinishID}) + return g.action(ctx, client, "AcknowledgeFinish", protocol.Control{Kind: "AcknowledgeFinish", Tuple: tuple, FinishID: control.FinishID}) default: return fmt.Errorf("python udf: unexpected control %q", control.Kind) } @@ -498,14 +521,14 @@ func validateInvocation(invocation *udf.Invocation) error { return invocation.Tuple.Validate() } -func (g *Gateway) action(ctx context.Context, kind string, control protocol.Control) error { +func (g *Gateway) action(ctx context.Context, client flight.FlightServiceClient, kind string, control protocol.Control) error { body, err := protocol.MarshalControl(control) if err != nil { return err } actionCtx, cancel := context.WithTimeout(ctx, g.cfg.RequestTimeout) defer cancel() - stream, err := g.flight.DoAction(actionCtx, &flight.Action{Type: kind, Body: body}) + stream, err := client.DoAction(actionCtx, &flight.Action{Type: kind, Body: body}) if err != nil { return fmt.Errorf("python udf: %s: %w", kind, err) } diff --git a/pkg/udf/python/supervisor.go b/pkg/udf/python/supervisor.go index 30d85fc2233f4..87df045f68ac8 100644 --- a/pkg/udf/python/supervisor.go +++ b/pkg/udf/python/supervisor.go @@ -9,26 +9,24 @@ package python import ( "errors" "fmt" - "io" "os" "os/exec" "path/filepath" "sync" - "sync/atomic" "github.com/matrixorigin/matrixone/pkg/logutil" ) +var errSupervisorClosing = errors.New("python udf worker is shutting down") + type Supervisor struct { - cfg Config - mu sync.Mutex - cmd *exec.Cmd - log io.WriteCloser - done chan struct{} + cfg Config + mu sync.Mutex + cmd *exec.Cmd + done chan struct{} + closing bool } -var supervisorNumber atomic.Int32 - func NewSupervisor(cfg Config) (*Supervisor, error) { if err := cfg.Validate(); err != nil { return nil, err @@ -39,6 +37,9 @@ func NewSupervisor(cfg Config) (*Supervisor, error) { func (s *Supervisor) Start() error { s.mu.Lock() defer s.mu.Unlock() + if s.closing { + return errSupervisorClosing + } if s.cmd != nil { return nil } @@ -58,35 +59,28 @@ func (s *Supervisor) Start() error { if executable == "" { executable = "python" } - exePath, err := os.Executable() - if err != nil { - return err - } - logPath := filepath.Join(filepath.Dir(exePath), fmt.Sprintf("python-udf-worker-%d.log", supervisorNumber.Add(1))) - logFile, err := os.OpenFile(logPath, os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0644) - if err != nil { - return err - } cmd := exec.Command(executable, "-u", workerPath, "--address="+s.cfg.Address) prepareSupervisorCommand(cmd) - cmd.Stdout, cmd.Stderr = logFile, logFile + // The MatrixOne service owns process logging. Inheriting stderr keeps + // worker diagnostics in the service log and avoids an unbounded, per-start + // file beside the binary. + cmd.Stdout, cmd.Stderr = os.Stderr, os.Stderr if err := cmd.Start(); err != nil { - _ = logFile.Close() return err } done := make(chan struct{}) - s.cmd, s.log, s.done = cmd, logFile, done - go s.wait(cmd, logFile, done) + s.cmd, s.done = cmd, done + go s.wait(cmd, done) logutil.Infof("started Python UDF worker: %s", cmd.String()) return nil } -func (s *Supervisor) wait(cmd *exec.Cmd, log io.WriteCloser, done chan struct{}) { +func (s *Supervisor) wait(cmd *exec.Cmd, done chan struct{}) { err := cmd.Wait() - _ = log.Close() s.mu.Lock() if s.cmd == cmd { - s.cmd, s.log, s.done = nil, nil, nil + s.cmd, s.done = nil, nil + s.closing = false } s.mu.Unlock() close(done) @@ -97,12 +91,12 @@ func (s *Supervisor) wait(cmd *exec.Cmd, log io.WriteCloser, done chan struct{}) func (s *Supervisor) Close() error { s.mu.Lock() - cmd, log, done := s.cmd, s.log, s.done + cmd, done := s.cmd, s.done + if cmd != nil { + s.closing = true + } s.mu.Unlock() if cmd == nil { - if log != nil { - return log.Close() - } return nil } var closeErr error diff --git a/pkg/udf/python/worker/test_worker.py b/pkg/udf/python/worker/test_worker.py index a49d25373dec6..6772d68b87392 100644 --- a/pkg/udf/python/worker/test_worker.py +++ b/pkg/udf/python/worker/test_worker.py @@ -516,6 +516,12 @@ def test_control_numeric_fields_are_strict(self): with self.assertRaisesRegex(ValueError, "invalid control field sequence"): worker._required_uint64({"sequence": "1"}, "sequence") + def test_result_ack_rejects_zero_sequence(self): + state = worker._InvocationState({}, 1) + state.last_result = 1 + with self.assertRaisesRegex(ValueError, "result ACK"): + state.ack_result(0) + if __name__ == "__main__": unittest.main() diff --git a/pkg/udf/python/worker/worker.py b/pkg/udf/python/worker/worker.py index 7d581e47b9d45..8cc22bac35503 100644 --- a/pkg/udf/python/worker/worker.py +++ b/pkg/udf/python/worker/worker.py @@ -968,7 +968,7 @@ def __init__(self, tuple_value: Dict[str, Any], terminal_bytes: int): def ack_result(self, sequence: int) -> None: with self.condition: - if sequence < self.acked_result or sequence > self.last_result: + if sequence <= 0 or sequence < self.acked_result or sequence > self.last_result: raise ValueError("PROTOCOL: result ACK is outside the received range") self.acked_result = sequence self.condition.notify_all() @@ -1098,7 +1098,6 @@ def do_action(self, context, action): } ) return - raise ValueError("PROTOCOL: unknown invocation") if action.type == "AcknowledgeResults": state.ack_result(_required_uint64(control, "ack_sequence")) elif action.type == "AcknowledgeFinish": diff --git a/test/distributed/cases/udf_python/vector_mode.result b/test/distributed/cases/udf_python/vector_mode.result new file mode 100644 index 0000000000000..3c7c9bd29c6e5 --- /dev/null +++ b/test/distributed/cases/udf_python/vector_mode.result @@ -0,0 +1,31 @@ +drop database if exists udf_python_vector_bvt; +create database udf_python_vector_bvt; +use udf_python_vector_bvt; +create function python_bvt_vector (x int) returns int language python as 'python_bvt_vector = lambda ctx, values: __import__("pyarrow").array([-99 if value.as_py() is None else value.as_py() + 10 for value in values], type=__import__("pyarrow").int32())' handler 'python_bvt_vector' mode vector; +create function python_bvt_vector_strict (x int) returns int language python as 'python_bvt_vector_strict = lambda ctx, values: __import__("pyarrow").array([value.as_py() + 10 for value in values], type=__import__("pyarrow").int32())' handler 'python_bvt_vector_strict' mode vector returns null on null input; +create function python_bvt_zero_vector () returns int language python as 'python_bvt_zero_vector = lambda ctx: __import__("pyarrow").array([7] * ctx.num_rows, type=__import__("pyarrow").int32())' handler 'python_bvt_zero_vector' mode vector; +create table vector_values (id int, value int); +insert into vector_values values (1, 1), (2, null), (3, 2); +select id, python_bvt_vector(value) as called_result +from vector_values order by id; +➤ id[4,32,0] ¦ called_result[4,32,0] 𝄀 +1 ¦ 11 𝄀 +2 ¦ -99 𝄀 +3 ¦ 12 +select id, python_bvt_vector_strict(value) as strict_result +from vector_values order by id; +➤ id[4,32,0] ¦ strict_result[4,32,0] 𝄀 +1 ¦ 11 𝄀 +2 ¦ null 𝄀 +3 ¦ 12 +select id, python_bvt_zero_vector() as zero_result +from vector_values order by id; +➤ id[4,32,0] ¦ zero_result[4,32,0] 𝄀 +1 ¦ 7 𝄀 +2 ¦ 7 𝄀 +3 ¦ 7 +drop function python_bvt_vector(int); +drop function python_bvt_vector_strict(int); +drop function python_bvt_zero_vector(); +drop table vector_values; +drop database udf_python_vector_bvt; diff --git a/test/distributed/cases/udf_python/vector_mode.sql b/test/distributed/cases/udf_python/vector_mode.sql new file mode 100644 index 0000000000000..6051e26aa69ab --- /dev/null +++ b/test/distributed/cases/udf_python/vector_mode.sql @@ -0,0 +1,31 @@ +-- Vector handlers receive one Arrow array per argument and return one Arrow +-- array with the same number of rows. Both NULL policies are exercised here. +drop database if exists udf_python_vector_bvt; +create database udf_python_vector_bvt; +use udf_python_vector_bvt; + +create function python_bvt_vector (x int) returns int language python as 'python_bvt_vector = lambda ctx, values: __import__("pyarrow").array([-99 if value.as_py() is None else value.as_py() + 10 for value in values], type=__import__("pyarrow").int32())' handler 'python_bvt_vector' mode vector; +create function python_bvt_vector_strict (x int) returns int language python as 'python_bvt_vector_strict = lambda ctx, values: __import__("pyarrow").array([value.as_py() + 10 for value in values], type=__import__("pyarrow").int32())' handler 'python_bvt_vector_strict' mode vector returns null on null input; +create function python_bvt_zero_vector () returns int language python as 'python_bvt_zero_vector = lambda ctx: __import__("pyarrow").array([7] * ctx.num_rows, type=__import__("pyarrow").int32())' handler 'python_bvt_zero_vector' mode vector; + +create table vector_values (id int, value int); +insert into vector_values values (1, 1), (2, null), (3, 2); + +-- CALLED ON NULL INPUT reaches the vector handler for the NULL row. +select id, python_bvt_vector(value) as called_result +from vector_values order by id; + +-- RETURNS NULL ON NULL INPUT compacts the selected rows and scatters NULL +-- back to the original row positions. +select id, python_bvt_vector_strict(value) as strict_result +from vector_values order by id; + +-- A zero-argument VECTOR routine gets its result shape from ctx.num_rows. +select id, python_bvt_zero_vector() as zero_result +from vector_values order by id; + +drop function python_bvt_vector(int); +drop function python_bvt_vector_strict(int); +drop function python_bvt_zero_vector(); +drop table vector_values; +drop database udf_python_vector_bvt; From f56e2c712ca67c7245d3f69cbc8883642104bae2 Mon Sep 17 00:00:00 2001 From: iamlinjunhong <1030420200@qq.com> Date: Thu, 10 Sep 2026 12:03:36 +0800 Subject: [PATCH 022/202] fix(udf): require complete Python type contracts --- pkg/frontend/compiler_context.go | 5 ++- pkg/sql/plan/function/python_udf.go | 2 +- pkg/sql/plan/function/udf.go | 69 ++++++++++++++++++++++++----- pkg/sql/plan/function/udf_test.go | 31 +++++++++++-- 4 files changed, 92 insertions(+), 15 deletions(-) diff --git a/pkg/frontend/compiler_context.go b/pkg/frontend/compiler_context.go index fc3065f2cd232..a211a61c4aa18 100644 --- a/pkg/frontend/compiler_context.go +++ b/pkg/frontend/compiler_context.go @@ -910,6 +910,10 @@ func (tcc *TxnCompilerContext) ResolveUdf(name string, args []*plan.Expr) (udf * if len(argList) != len(args) { // mismatch continue } + udf.Args = argList + if err = udf.ValidatePythonTypeContract(); err != nil { + return nil, err + } toList := make([]types.T, len(args)) for j := range argList { @@ -925,7 +929,6 @@ func (tcc *TxnCompilerContext) ResolveUdf(name string, args []*plan.Expr) (udf * continue } - udf.Args = argList matchedList = append(matchedList, &MatchUdf{ Udf: udf, Cost: cost, diff --git a/pkg/sql/plan/function/python_udf.go b/pkg/sql/plan/function/python_udf.go index 3b27d6f3e5ddb..5ef466e73d678 100644 --- a/pkg/sql/plan/function/python_udf.go +++ b/pkg/sql/plan/function/python_udf.go @@ -266,7 +266,7 @@ func hasNullInput(inputs []*vector.Vector, row int) bool { } func routineArgumentTypes(routine *UdfWithContext) ([]types.Type, error) { - if len(routine.PythonArgTypes) != 0 { + if routine.Language == "python" { args := make([]types.Type, len(routine.PythonArgTypes)) for i, descriptor := range routine.PythonArgTypes { args[i] = descriptor.Type() diff --git a/pkg/sql/plan/function/udf.go b/pkg/sql/plan/function/udf.go index 47d0fb22800b4..4e2af1402c2b2 100644 --- a/pkg/sql/plan/function/udf.go +++ b/pkg/sql/plan/function/udf.go @@ -10,6 +10,8 @@ package function import ( "encoding/json" + "fmt" + "reflect" "github.com/google/uuid" "github.com/matrixorigin/matrixone/pkg/container/types" @@ -86,7 +88,7 @@ func (u *Udf) GetArgsPlanType() []*plan.Type { } func (u *Udf) GetRetPlanType() *plan.Type { return type2PlanType(u.GetRetType()) } func (u *Udf) GetArgsType() []types.Type { - if len(u.PythonArgTypes) != 0 { + if u.Language == "python" { args := make([]types.Type, len(u.PythonArgTypes)) for i, descriptor := range u.PythonArgTypes { args[i] = descriptor.Type() @@ -96,29 +98,76 @@ func (u *Udf) GetArgsType() []types.Type { return u.ArgsType } func (u *Udf) GetRetType() types.Type { - if u.PythonReturnType != nil { + if u.Language == "python" { + if u.PythonReturnType == nil { + return types.Type{} + } return u.PythonReturnType.Type() } return types.Types[u.RetType].ToType() } -// LoadPythonTypeContract copies exact type metadata persisted in the Python -// routine body into the plan object. Older catalog rows have no such fields -// and intentionally retain the legacy logical-type fallback. +// LoadPythonTypeContract loads the exact type metadata persisted in the Python +// routine body. Python execution has one ABI, so an incomplete body is +// rejected instead of being reinterpreted through the legacy logical-type +// columns. func (u *Udf) LoadPythonTypeContract() error { if u.Language != "python" { return nil } + u.PythonArgTypes = nil + u.PythonReturnType = nil body := PythonRoutineBody{} if err := json.Unmarshal([]byte(u.Body), &body); err != nil { return err } - if len(body.ArgTypes) != 0 { - u.PythonArgTypes = append([]PythonTypeDescriptor(nil), body.ArgTypes...) + if body.ReturnType == nil { + return fmt.Errorf("python routine is missing return descriptor") + } + for i, descriptor := range body.ArgTypes { + if err := validatePythonTypeDescriptor(descriptor); err != nil { + return fmt.Errorf("python routine argument descriptor %d: %w", i, err) + } + } + if err := validatePythonTypeDescriptor(*body.ReturnType); err != nil { + return fmt.Errorf("python routine return descriptor: %w", err) + } + u.PythonArgTypes = append([]PythonTypeDescriptor(nil), body.ArgTypes...) + returnType := *body.ReturnType + u.PythonReturnType = &returnType + return nil +} + +// ValidatePythonTypeContract checks the descriptor count after catalog +// argument names have been decoded. LoadPythonTypeContract deliberately runs +// before that decode during lookup, so this second check closes the only point +// where an argument list and its exact ABI could otherwise diverge. +func (u *Udf) ValidatePythonTypeContract() error { + if u.Language != "python" { + return nil + } + if u.PythonReturnType == nil { + return fmt.Errorf("python routine is missing return descriptor") + } + if len(u.PythonArgTypes) != len(u.Args) { + return fmt.Errorf("python routine has %d descriptors for %d arguments", len(u.PythonArgTypes), len(u.Args)) + } + for i, descriptor := range u.PythonArgTypes { + if err := validatePythonTypeDescriptor(descriptor); err != nil { + return fmt.Errorf("python routine argument descriptor %d: %w", i, err) + } + } + return validatePythonTypeDescriptor(*u.PythonReturnType) +} + +func validatePythonTypeDescriptor(descriptor PythonTypeDescriptor) error { + typ := descriptor.Type() + canonical, err := NewPythonTypeDescriptor(typ) + if err != nil { + return err } - if body.ReturnType != nil { - returnType := *body.ReturnType - u.PythonReturnType = &returnType + if !reflect.DeepEqual(canonical, descriptor) { + return fmt.Errorf("descriptor is not canonical for %s", typ.String()) } return nil } diff --git a/pkg/sql/plan/function/udf_test.go b/pkg/sql/plan/function/udf_test.go index 2a6ee085556c6..a844109c5e3f3 100644 --- a/pkg/sql/plan/function/udf_test.go +++ b/pkg/sql/plan/function/udf_test.go @@ -38,22 +38,47 @@ func TestPythonTypeContractPreservesDeclaredShape(t *testing.T) { routine := &Udf{Language: "python", Body: string(body), ArgsType: []types.Type{types.T_decimal64.ToType()}} require.NoError(t, routine.LoadPythonTypeContract()) + routine.Args = []*Arg{{Type: "decimal"}} + require.NoError(t, routine.ValidatePythonTypeContract()) require.Equal(t, argument, routine.GetArgsType()[0]) require.Equal(t, returnType, routine.GetRetType()) require.Equal(t, int32(6), routine.GetArgsPlanType()[0].Scale) require.Equal(t, int32(64), routine.GetRetPlanType().Width) } -func TestPythonTypeContractKeepsLegacyFallback(t *testing.T) { +func TestPythonTypeContractRejectsMissingReturnDescriptor(t *testing.T) { routine := &Udf{ Language: "python", Body: `{"handler":"legacy","source":"def legacy(ctx, x): return x"}`, ArgsType: []types.Type{types.T_int32.ToType()}, RetType: "int", } + require.ErrorContains(t, routine.LoadPythonTypeContract(), "return descriptor") +} + +func TestPythonTypeContractRejectsArgumentCountMismatch(t *testing.T) { + argument, err := NewPythonTypeDescriptor(types.T_int32.ToType()) + require.NoError(t, err) + returnType, err := NewPythonTypeDescriptor(types.T_int32.ToType()) + require.NoError(t, err) + body, err := json.Marshal(PythonRoutineBody{ArgTypes: []PythonTypeDescriptor{argument}, ReturnType: &returnType}) + require.NoError(t, err) + routine := &Udf{Language: "python", Body: string(body)} + require.NoError(t, routine.LoadPythonTypeContract()) + require.ErrorContains(t, routine.ValidatePythonTypeContract(), "descriptors for 0 arguments") +} + +func TestPythonTypeContractReloadReplacesDescriptors(t *testing.T) { + argument, err := NewPythonTypeDescriptor(types.T_int32.ToType()) + require.NoError(t, err) + returnType, err := NewPythonTypeDescriptor(types.T_int32.ToType()) + require.NoError(t, err) + body, err := json.Marshal(PythonRoutineBody{ArgTypes: []PythonTypeDescriptor{argument}, ReturnType: &returnType}) + require.NoError(t, err) + routine := &Udf{Language: "python", Body: string(body)} + require.NoError(t, routine.LoadPythonTypeContract()) require.NoError(t, routine.LoadPythonTypeContract()) - require.Equal(t, types.T_int32.ToType(), routine.GetArgsType()[0]) - require.Equal(t, types.T_int32.ToType(), routine.GetRetType()) + require.Len(t, routine.PythonArgTypes, 1) } func TestPythonBindingNormalizesDecimalMetadata(t *testing.T) { From 158d19b7e1a34a4eb117414c44b0224f66d6cdd4 Mon Sep 17 00:00:00 2001 From: iamlinjunhong <1030420200@qq.com> Date: Thu, 10 Sep 2026 12:04:51 +0800 Subject: [PATCH 023/202] fix(udf): enforce one-shot group state transitions --- pkg/udf/protocol/protocol.go | 89 +++++++++++++++++++------------ pkg/udf/protocol/protocol_test.go | 18 +++++-- 2 files changed, 69 insertions(+), 38 deletions(-) diff --git a/pkg/udf/protocol/protocol.go b/pkg/udf/protocol/protocol.go index 13d0df81d9b21..9fc962d572aa7 100644 --- a/pkg/udf/protocol/protocol.go +++ b/pkg/udf/protocol/protocol.go @@ -373,53 +373,70 @@ func (g *ExecutionGroup) BeginOpen(memberID string) (*OpenToken, error) { type OpenToken struct { group *ExecutionGroup memberID string - once sync.Once - result error + mu sync.Mutex + state openTokenState } +type openTokenState uint8 + +const ( + openTokenPending openTokenState = iota + openTokenCommitted + openTokenAborted +) + func (t *OpenToken) Commit() error { if t == nil || t.group == nil { return ErrUnknownIdentity } - t.once.Do(func() { - g := t.group - g.mu.Lock() - delete(g.pending, t.memberID) - g.inFlight-- - if g.closing || g.released { - t.result = ErrGroupClosed - release := g.canReleaseLocked() - g.mu.Unlock() - if release { - if err := t.group.releaseIfReady(); err != nil { - t.result = errors.Join(t.result, err) - } - } - return - } - g.members[t.memberID] = true - g.registered++ + if err := t.begin(openTokenCommitted); err != nil { + return err + } + g := t.group + g.mu.Lock() + delete(g.pending, t.memberID) + g.inFlight-- + if g.closing || g.released { + release := g.canReleaseLocked() g.mu.Unlock() - }) - return t.result + if release { + return errors.Join(ErrGroupClosed, g.releaseIfReady()) + } + return ErrGroupClosed + } + g.members[t.memberID] = true + g.registered++ + g.mu.Unlock() + return nil } func (t *OpenToken) Abort() error { if t == nil || t.group == nil { return ErrUnknownIdentity } - t.once.Do(func() { - g := t.group - g.mu.Lock() - delete(g.pending, t.memberID) - g.inFlight-- - release := g.canReleaseLocked() - g.mu.Unlock() - if release { - t.result = t.group.releaseIfReady() - } - }) - return t.result + if err := t.begin(openTokenAborted); err != nil { + return err + } + g := t.group + g.mu.Lock() + delete(g.pending, t.memberID) + g.inFlight-- + release := g.canReleaseLocked() + g.mu.Unlock() + if release { + return g.releaseIfReady() + } + return nil +} + +func (t *OpenToken) begin(state openTokenState) error { + t.mu.Lock() + defer t.mu.Unlock() + if t.state != openTokenPending { + return fmt.Errorf("%w: open token was already finalized", ErrProtocol) + } + t.state = state + return nil } func (g *ExecutionGroup) Close(reason CloseReason) error { @@ -427,6 +444,10 @@ func (g *ExecutionGroup) Close(reason CloseReason) error { return fmt.Errorf("%w: close reason is empty", ErrProtocol) } g.mu.Lock() + if g.closing && g.reason != reason { + g.mu.Unlock() + return fmt.Errorf("%w: close reason changed from %q to %q", ErrProtocol, g.reason, reason) + } if g.released { g.mu.Unlock() return nil diff --git a/pkg/udf/protocol/protocol_test.go b/pkg/udf/protocol/protocol_test.go index 0ab88d5fc3148..c640e2483e519 100644 --- a/pkg/udf/protocol/protocol_test.go +++ b/pkg/udf/protocol/protocol_test.go @@ -123,7 +123,8 @@ func TestExecutionGroupReleasesOnlyAfterCloseAndAllMembersTerminal(t *testing.T) _, err = group.BeginOpen("late") require.ErrorIs(t, err, ErrGroupClosed) - require.NoError(t, group.Close(ReasonCancel)) + require.ErrorIs(t, group.Close(ReasonCancel), ErrProtocol) + require.NoError(t, group.Close(ReasonInputEOF)) require.Equal(t, int32(1), releases.Load()) } @@ -158,7 +159,7 @@ func TestExecutionGroupZeroMemberAndAbortedOpenRelease(t *testing.T) { require.NoError(t, err) require.NoError(t, group.Close(ReasonCancel)) require.ErrorIs(t, open.Commit(), ErrGroupClosed) - require.ErrorIs(t, open.Commit(), ErrGroupClosed) + require.ErrorIs(t, open.Commit(), ErrProtocol) } func TestExecutionGroupReservesPendingMemberIdentity(t *testing.T) { @@ -185,6 +186,15 @@ func TestExecutionGroupReservesPendingMemberIdentity(t *testing.T) { require.Zero(t, inFlight) } +func TestExecutionGroupCloseReasonIsImmutable(t *testing.T) { + group, err := NewExecutionGroup("g", 1, 1, func() error { return nil }) + require.NoError(t, err) + require.NoError(t, group.Close(ReasonIdle)) + require.NoError(t, group.Close(ReasonIdle)) + require.ErrorIs(t, group.Close(ReasonCancel), ErrProtocol) + require.Equal(t, ReasonIdle, group.Reason()) +} + func TestTerminalLedgerRetainsTombstonesUntilExpiry(t *testing.T) { ledger, err := NewTerminalLedger(2, 200) require.NoError(t, err) @@ -269,8 +279,8 @@ func TestExecutionGroupCommitReportsReleaseFailure(t *testing.T) { err = open.Commit() require.ErrorIs(t, err, ErrGroupClosed) require.ErrorContains(t, err, "temporary release failure") - require.ErrorIs(t, open.Commit(), ErrGroupClosed) - require.ErrorContains(t, open.Commit(), "temporary release failure") + require.ErrorIs(t, open.Commit(), ErrProtocol) + require.ErrorIs(t, open.Abort(), ErrProtocol) require.Equal(t, GroupDraining, group.State()) require.NoError(t, group.Close(ReasonCancel)) require.Equal(t, GroupReleased, group.State()) From 34332a3c197280675a9585f88ab73c07c81da237 Mon Sep 17 00:00:00 2001 From: iamlinjunhong <1030420200@qq.com> Date: Thu, 10 Sep 2026 12:06:21 +0800 Subject: [PATCH 024/202] fix(udf): reject late Flight completion controls --- pkg/udf/python/gateway.go | 10 +++++- pkg/udf/python/gateway_test.go | 56 ++++++++++++++++++++++++++++++++++ 2 files changed, 65 insertions(+), 1 deletion(-) diff --git a/pkg/udf/python/gateway.go b/pkg/udf/python/gateway.go index d99c397691699..2c6753851c1dd 100644 --- a/pkg/udf/python/gateway.go +++ b/pkg/udf/python/gateway.go @@ -340,6 +340,7 @@ func (g *Gateway) receiveResultBatch( mp *mpool.MPool, sequence *protocol.Sequence, ) (int, error) { + inputConsumed := false for { data, err := stream.Recv() if err != nil { @@ -375,6 +376,9 @@ func (g *Gateway) receiveResultBatch( if control.Tuple != tuple || control.Kind != "ResultBatch" || control.Sequence != sequenceNumber { return 0, fmt.Errorf("python udf: unexpected result sequence %d", control.Sequence) } + if !inputConsumed { + return 0, fmt.Errorf("python udf: result sequence %d arrived before InputConsumed", sequenceNumber) + } if err := sequence.AcceptResult(control.Sequence); err != nil { return 0, err } @@ -433,6 +437,10 @@ func (g *Gateway) receiveResultBatch( if control.Sequence != sequenceNumber { return 0, fmt.Errorf("python udf: input sequence %d was consumed while waiting for result %d", control.Sequence, sequenceNumber) } + if inputConsumed { + return 0, fmt.Errorf("python udf: input sequence %d was consumed more than once", sequenceNumber) + } + inputConsumed = true continue case "Error": return 0, fmt.Errorf("python udf: worker error: %s", control.Reason) @@ -480,7 +488,7 @@ func (g *Gateway) receiveFinish( } switch control.Kind { case "InputConsumed", "ResultSchema": - continue + return fmt.Errorf("python udf: %s arrived after result stream was drained", control.Kind) case "Error": return fmt.Errorf("python udf: worker error: %s", control.Reason) case "Finish": diff --git a/pkg/udf/python/gateway_test.go b/pkg/udf/python/gateway_test.go index f83abcca7e31b..793c5070374a0 100644 --- a/pkg/udf/python/gateway_test.go +++ b/pkg/udf/python/gateway_test.go @@ -6,15 +6,50 @@ package python import ( + "context" + "io" "testing" + "time" + "github.com/apache/arrow-go/v18/arrow/flight/gen/flight" "github.com/matrixorigin/matrixone/pkg/container/types" "github.com/matrixorigin/matrixone/pkg/container/vector" "github.com/matrixorigin/matrixone/pkg/udf" "github.com/matrixorigin/matrixone/pkg/udf/protocol" "github.com/stretchr/testify/require" + "google.golang.org/grpc" ) +type gatewayResultStream struct { + grpc.ClientStream + results []*flight.FlightData + index int +} + +func (s *gatewayResultStream) Recv() (*flight.FlightData, error) { + if s.index == len(s.results) { + return nil, io.EOF + } + result := s.results[s.index] + s.index++ + return result, nil +} + +func (s *gatewayResultStream) Send(*flight.FlightData) error { + return nil +} + +func gatewayControl(t *testing.T, kind string, tuple protocol.FencingTuple, fields ...func(*protocol.Control)) *flight.FlightData { + t.Helper() + control := protocol.Control{Kind: kind, Tuple: tuple} + for _, field := range fields { + field(&control) + } + encoded, err := protocol.MarshalControl(control) + require.NoError(t, err) + return &flight.FlightData{AppMetadata: encoded} +} + func validInvocation() *udf.Invocation { return &udf.Invocation{ Language: udf.LanguagePython, @@ -84,3 +119,24 @@ func TestGatewayCloseIsTerminal(t *testing.T) { require.ErrorIs(t, gateway.connect(), errGatewayClosed) require.NoError(t, gateway.Close()) } + +func TestGatewayFinishRejectsLateHalfStreamControls(t *testing.T) { + tuple := validInvocation().Tuple + for _, kind := range []string{"InputConsumed", "ResultSchema"} { + t.Run(kind, func(t *testing.T) { + sequence := protocol.Sequence{} + require.NoError(t, sequence.AcceptInput(1)) + require.NoError(t, sequence.EndInput(1)) + require.NoError(t, sequence.AcceptResult(1)) + require.NoError(t, sequence.AcknowledgeResults(1)) + stream := &gatewayResultStream{results: []*flight.FlightData{ + gatewayControl(t, kind, tuple, func(control *protocol.Control) { + control.Sequence = 1 + }), + }} + gateway := &Gateway{cfg: ClientConfig{RequestTimeout: time.Second}} + err := gateway.receiveFinish(context.Background(), nil, stream, tuple, 1, 1, &sequence) + require.ErrorContains(t, err, "arrived after result stream was drained") + }) + } +} From 1dcb5fa6a501dc80abbc203c26b305fa0aaf5581 Mon Sep 17 00:00:00 2001 From: iamlinjunhong <1030420200@qq.com> Date: Thu, 10 Sep 2026 12:08:45 +0800 Subject: [PATCH 025/202] fix(udf): align Python JSON validation with SQL --- pkg/udf/python/worker/test_worker.py | 8 ++++++++ pkg/udf/python/worker/worker.py | 17 ++++++++++++++++- 2 files changed, 24 insertions(+), 1 deletion(-) diff --git a/pkg/udf/python/worker/test_worker.py b/pkg/udf/python/worker/test_worker.py index 6772d68b87392..28e658bbad38a 100644 --- a/pkg/udf/python/worker/test_worker.py +++ b/pkg/udf/python/worker/test_worker.py @@ -70,6 +70,14 @@ def test_json_input_is_canonicalized_before_handler(self): worker._scalar_input(array, 0, descriptor), ) + def test_json_rejects_nonstandard_numbers(self): + descriptor = {"type_id": worker.JSON, "offset_width": 32, "json_encoding": "canonical_text"} + for value in ("NaN", "Infinity", "-Infinity", "1e9999"): + with self.assertRaisesRegex(ValueError, "invalid JSON"): + worker._canonical_json_text(value) + with self.assertRaisesRegex(ValueError, "JSON result"): + worker._check_scalar(value, descriptor) + def test_uuid_scalar_round_trip_keeps_uuid_object_until_array_encoding(self): descriptor = {"type_id": worker.UUID, "offset_width": 0} value = uuid.UUID("123e4567-e89b-12d3-a456-426614174000") diff --git a/pkg/udf/python/worker/worker.py b/pkg/udf/python/worker/worker.py index 8cc22bac35503..f96705f3163db 100644 --- a/pkg/udf/python/worker/worker.py +++ b/pkg/udf/python/worker/worker.py @@ -21,6 +21,7 @@ import inspect import json import logging +import math import os import pickle import selectors @@ -497,8 +498,22 @@ def _scalar_input(array: pa.Array, row: int, descriptor: Dict[str, Any]): def _canonical_json_text(value: str) -> str: if not isinstance(value, str): raise ValueError("TYPE_CONTRACT: JSON value must be text") + + def reject_nonstandard_number(_constant: str): + raise ValueError("non-standard JSON number") + + def reject_nonfinite_float(number: str): + parsed = float(number) + if not math.isfinite(parsed): + raise ValueError("non-finite JSON number") + return parsed + try: - parsed = json.loads(value) + parsed = json.loads( + value, + parse_constant=reject_nonstandard_number, + parse_float=reject_nonfinite_float, + ) except Exception as exc: raise ValueError("TYPE_CONTRACT: invalid JSON input") from exc return json.dumps(parsed, ensure_ascii=False, separators=(",", ":")) From 4dbeb65cb0fcfc0dc5ca427da4b7d11601c52311 Mon Sep 17 00:00:00 2001 From: iamlinjunhong <1030420200@qq.com> Date: Thu, 10 Sep 2026 12:10:05 +0800 Subject: [PATCH 026/202] fix(udf): isolate Python handler environment --- pkg/udf/python/worker/test_worker.py | 23 +++++++++++++++++++++++ pkg/udf/python/worker/worker.py | 11 ++++++++++- 2 files changed, 33 insertions(+), 1 deletion(-) diff --git a/pkg/udf/python/worker/test_worker.py b/pkg/udf/python/worker/test_worker.py index 28e658bbad38a..de513484a2161 100644 --- a/pkg/udf/python/worker/test_worker.py +++ b/pkg/udf/python/worker/test_worker.py @@ -183,6 +183,29 @@ def test_handler_stdout_cannot_inject_parent_protocol(self): ) self.assertEqual([1], result.column(0).to_pylist()) + def test_handler_does_not_inherit_parent_environment(self): + descriptor = {"type_id": worker.BOOL, "offset_width": 32} + batch = pa.RecordBatch.from_arrays( + [pa.array([True], type=pa.bool_())], ["arg_0"] + ) + request = { + "source": "import os;\ndef f(ctx, x): return 'MATRIXONE_TEST_SECRET' in os.environ", + "handler": "f", + "mode": worker.MODE_SCALAR, + "null_policy": worker.NULL_CALL, + "sdk_version": worker.SDK_VERSION, + "context": None, + "args": [descriptor], + "return": descriptor, + "max_batch_bytes": 1 << 20, + "input": worker._serialize_record_batch(batch), + } + with mock.patch.dict(os.environ, {"MATRIXONE_TEST_SECRET": "must-not-leak"}, clear=False): + result = worker._deserialize_record_batch( + worker._run_handler_process(None, request, 3) + ) + self.assertEqual([False], result.column(0).to_pylist()) + def test_handler_cannot_forge_completion_on_stdout(self): descriptor = {"type_id": worker.INT64, "offset_width": 32} batch = pa.RecordBatch.from_arrays([pa.array([77], type=pa.int64())], ["arg_0"]) diff --git a/pkg/udf/python/worker/worker.py b/pkg/udf/python/worker/worker.py index f96705f3163db..679fc1edcfb8f 100644 --- a/pkg/udf/python/worker/worker.py +++ b/pkg/udf/python/worker/worker.py @@ -52,6 +52,7 @@ _HANDLER_RESPONSE_ERROR = 0 _HANDLER_RESPONSE_OK = 1 _HANDLER_RESPONSE_FD_ENV = "MATRIXONE_HANDLER_RESPONSE_FD" +_HANDLER_ENV_ALLOWLIST = frozenset({"PATH"}) _MICROS_PER_SECOND = 1_000_000 _MAX_TIME_MICROS = (838 * 60 * 60 + 59 * 60 + 59) * _MICROS_PER_SECOND _MIN_TIMESTAMP = _datetime.datetime(1970, 1, 1, 0, 0, 1, tzinfo=_datetime.timezone.utc) @@ -873,7 +874,15 @@ def _run_handler_process(context, request: Dict[str, Any], timeout_seconds: floa if os.name == "posix": popen_kwargs["start_new_session"] = True popen_kwargs["pass_fds"] = (response_write_fd,) - child_env = os.environ.copy() + # User code runs under a separate execution identity. In particular, do + # not inherit CN, object-store, database, or tenant credentials from the + # worker process. Routine dependencies are supplied by the selected Python + # environment and do not need ambient environment variables. + child_env = { + name: value + for name, value in os.environ.items() + if name in _HANDLER_ENV_ALLOWLIST + } child_env[_HANDLER_RESPONSE_FD_ENV] = str(response_write_fd) # The extra descriptor is a protocol-channel separation mechanism, not a # sandbox boundary. An unisolated handler with arbitrary OS access can From e5751d94becc4887c3391cf92307c8a9edfde551 Mon Sep 17 00:00:00 2001 From: iamlinjunhong <1030420200@qq.com> Date: Thu, 10 Sep 2026 12:17:17 +0800 Subject: [PATCH 027/202] fix(udf): enforce complete Python routine bodies --- .../parsers/dialect/mysql/mysql_sql_test.go | 12 +++ pkg/sql/parsers/tree/function.go | 3 + pkg/sql/plan/function/python_udf.go | 19 +---- pkg/sql/plan/function/udf.go | 75 +++++++++++++++---- pkg/sql/plan/function/udf_test.go | 63 ++++++++++++++-- 5 files changed, 137 insertions(+), 35 deletions(-) diff --git a/pkg/sql/parsers/dialect/mysql/mysql_sql_test.go b/pkg/sql/parsers/dialect/mysql/mysql_sql_test.go index 49564fac3facb..2fdd99fe24272 100644 --- a/pkg/sql/parsers/dialect/mysql/mysql_sql_test.go +++ b/pkg/sql/parsers/dialect/mysql/mysql_sql_test.go @@ -142,6 +142,18 @@ func TestPythonFunctionOptions(t *testing.T) { require.ErrorContains(t, sqlOptions.(*tree.CreateFunction).Valid(), "require LANGUAGE PYTHON") } +func TestPythonFunctionImportIsRejectedBeforeExecution(t *testing.T) { + stmt, err := ParseOne(context.Background(), + "create function py (x int) returns int language python import 'udf.py' handler 'py'", 1) + require.NoError(t, err) + defer stmt.Free() + + create, ok := stmt.(*tree.CreateFunction) + require.True(t, ok) + require.True(t, create.Import) + require.ErrorContains(t, create.Valid(), "immutable artifact catalog") +} + func TestCreateTablePreservesIndexIdentifierCase(t *testing.T) { stmt, err := ParseOne(context.Background(), "create table t (id int, v varchar(20), key MixedCaseIdx(v), unique key `UniQue_Mix`(id))", 1) diff --git a/pkg/sql/parsers/tree/function.go b/pkg/sql/parsers/tree/function.go index f5d6d16476bcc..a1e225939ee92 100644 --- a/pkg/sql/parsers/tree/function.go +++ b/pkg/sql/parsers/tree/function.go @@ -216,6 +216,9 @@ func (node *CreateFunction) Valid() error { } return nil case string(PYTHON): + if node.Import { + return moerr.NewNotSupportedNoCtx("Python artifact import requires the immutable artifact catalog") + } if node.Mode != "" { node.Mode = strings.ToUpper(node.Mode) if node.Mode != "SCALAR" && node.Mode != "VECTOR" { diff --git a/pkg/sql/plan/function/python_udf.go b/pkg/sql/plan/function/python_udf.go index 5ef466e73d678..57496bb53d63b 100644 --- a/pkg/sql/plan/function/python_udf.go +++ b/pkg/sql/plan/function/python_udf.go @@ -112,25 +112,10 @@ func runPythonUdf(parameters []*vector.Vector, result vector.FunctionResultWrapp return fmt.Errorf("python udf: decode Python type contract: %w", err) } - body := PythonRoutineBody{} - if err := json.Unmarshal([]byte(routine.Body), &body); err != nil { + body, err := DecodePythonRoutineBody(routine.Body) + if err != nil { return fmt.Errorf("python udf: decode Python routine body: %w", err) } - if body.Handler == "" || body.Source == "" { - return fmt.Errorf("python udf: routine handler and source are required") - } - if body.Mode != "SCALAR" && body.Mode != "VECTOR" { - return fmt.Errorf("python udf: unsupported mode %q", body.Mode) - } - if body.NullPolicy != udf.NullCallHandler && body.NullPolicy != udf.NullReturnNull { - return fmt.Errorf("python udf: unsupported NULL policy %q", body.NullPolicy) - } - if body.ABIContract != udf.PythonABIContract || body.AdapterVersion != udf.PythonAdapterVersion { - return fmt.Errorf("python udf: unsupported Python ABI contract %q/%q", body.ABIContract, body.AdapterVersion) - } - if body.SDKVersion != udf.PythonSDKVersion { - return fmt.Errorf("python udf: unsupported Python SDK %q", body.SDKVersion) - } argTypes, err := routineArgumentTypes(routine) if err != nil { return err diff --git a/pkg/sql/plan/function/udf.go b/pkg/sql/plan/function/udf.go index 4e2af1402c2b2..c3beb559981df 100644 --- a/pkg/sql/plan/function/udf.go +++ b/pkg/sql/plan/function/udf.go @@ -11,11 +11,14 @@ package function import ( "encoding/json" "fmt" + "io" "reflect" + "strings" "github.com/google/uuid" "github.com/matrixorigin/matrixone/pkg/container/types" "github.com/matrixorigin/matrixone/pkg/pb/plan" + "github.com/matrixorigin/matrixone/pkg/udf" pythonudf "github.com/matrixorigin/matrixone/pkg/udf/python" ) @@ -58,6 +61,63 @@ type PythonRoutineBody struct { ReturnType *PythonTypeDescriptor `json:"return_type,omitempty"` } +// DecodePythonRoutineBody decodes the one catalog representation accepted by +// the Python execution path. Catalog data is an execution authority, so an +// unknown field or a second JSON value must be rejected instead of being +// silently ignored by encoding/json. +func DecodePythonRoutineBody(raw string) (PythonRoutineBody, error) { + decoder := json.NewDecoder(strings.NewReader(raw)) + decoder.DisallowUnknownFields() + var body PythonRoutineBody + if err := decoder.Decode(&body); err != nil { + return PythonRoutineBody{}, err + } + var extra json.RawMessage + if err := decoder.Decode(&extra); err != io.EOF { + if err == nil { + return PythonRoutineBody{}, fmt.Errorf("python routine body contains multiple JSON values") + } + return PythonRoutineBody{}, err + } + if err := body.Validate(); err != nil { + return PythonRoutineBody{}, err + } + return body, nil +} + +// Validate checks fields that are independent of the catalog argument list. +// The argument count is checked by Udf.ValidatePythonTypeContract after the +// legacy catalog args column has been decoded. +func (body PythonRoutineBody) Validate() error { + if body.Handler == "" || body.Source == "" { + return fmt.Errorf("python routine handler and source are required") + } + if body.Mode != "SCALAR" && body.Mode != "VECTOR" { + return fmt.Errorf("python routine has unsupported mode %q", body.Mode) + } + if body.NullPolicy != udf.NullCallHandler && body.NullPolicy != udf.NullReturnNull { + return fmt.Errorf("python routine has unsupported NULL policy %q", body.NullPolicy) + } + if body.ABIContract != udf.PythonABIContract || body.AdapterVersion != udf.PythonAdapterVersion { + return fmt.Errorf("python routine has unsupported ABI contract %q/%q", body.ABIContract, body.AdapterVersion) + } + if body.SDKVersion != udf.PythonSDKVersion { + return fmt.Errorf("python routine has unsupported SDK %q", body.SDKVersion) + } + if body.ReturnType == nil { + return fmt.Errorf("python routine is missing return descriptor") + } + for i, descriptor := range body.ArgTypes { + if err := validatePythonTypeDescriptor(descriptor); err != nil { + return fmt.Errorf("python routine argument descriptor %d: %w", i, err) + } + } + if err := validatePythonTypeDescriptor(*body.ReturnType); err != nil { + return fmt.Errorf("python routine return descriptor: %w", err) + } + return nil +} + // PythonTypeDescriptor is the single Arrow ABI descriptor used by both plan // resolution and the Python adapter. The UDF package keeps it as an alias so // the catalog body and the Arrow metadata cannot drift independently. @@ -117,21 +177,10 @@ func (u *Udf) LoadPythonTypeContract() error { } u.PythonArgTypes = nil u.PythonReturnType = nil - body := PythonRoutineBody{} - if err := json.Unmarshal([]byte(u.Body), &body); err != nil { + body, err := DecodePythonRoutineBody(u.Body) + if err != nil { return err } - if body.ReturnType == nil { - return fmt.Errorf("python routine is missing return descriptor") - } - for i, descriptor := range body.ArgTypes { - if err := validatePythonTypeDescriptor(descriptor); err != nil { - return fmt.Errorf("python routine argument descriptor %d: %w", i, err) - } - } - if err := validatePythonTypeDescriptor(*body.ReturnType); err != nil { - return fmt.Errorf("python routine return descriptor: %w", err) - } u.PythonArgTypes = append([]PythonTypeDescriptor(nil), body.ArgTypes...) returnType := *body.ReturnType u.PythonReturnType = &returnType diff --git a/pkg/sql/plan/function/udf_test.go b/pkg/sql/plan/function/udf_test.go index a844109c5e3f3..fc43f1f0388a0 100644 --- a/pkg/sql/plan/function/udf_test.go +++ b/pkg/sql/plan/function/udf_test.go @@ -20,6 +20,7 @@ import ( "testing" "github.com/matrixorigin/matrixone/pkg/container/types" + "github.com/matrixorigin/matrixone/pkg/udf" "github.com/stretchr/testify/require" ) @@ -31,8 +32,15 @@ func TestPythonTypeContractPreservesDeclaredShape(t *testing.T) { returnDescriptor, err := NewPythonTypeDescriptor(returnType) require.NoError(t, err) body, err := json.Marshal(PythonRoutineBody{ - ArgTypes: []PythonTypeDescriptor{argumentDescriptor}, - ReturnType: &returnDescriptor, + Handler: "identity", + Source: "def identity(ctx, x): return x", + Mode: "SCALAR", + NullPolicy: udf.NullCallHandler, + ABIContract: udf.PythonABIContract, + AdapterVersion: udf.PythonAdapterVersion, + SDKVersion: udf.PythonSDKVersion, + ArgTypes: []PythonTypeDescriptor{argumentDescriptor}, + ReturnType: &returnDescriptor, }) require.NoError(t, err) @@ -49,7 +57,10 @@ func TestPythonTypeContractPreservesDeclaredShape(t *testing.T) { func TestPythonTypeContractRejectsMissingReturnDescriptor(t *testing.T) { routine := &Udf{ Language: "python", - Body: `{"handler":"legacy","source":"def legacy(ctx, x): return x"}`, + Body: `{"handler":"legacy","source":"def legacy(ctx, x): return x",` + + `"mode":"SCALAR","null_policy":"CALLED_ON_NULL_INPUT",` + + `"abi_contract":"PYTHON_ARROW","adapter_version":"2026-09",` + + `"sdk_version":"1.0"}`, ArgsType: []types.Type{types.T_int32.ToType()}, RetType: "int", } @@ -61,7 +72,17 @@ func TestPythonTypeContractRejectsArgumentCountMismatch(t *testing.T) { require.NoError(t, err) returnType, err := NewPythonTypeDescriptor(types.T_int32.ToType()) require.NoError(t, err) - body, err := json.Marshal(PythonRoutineBody{ArgTypes: []PythonTypeDescriptor{argument}, ReturnType: &returnType}) + body, err := json.Marshal(PythonRoutineBody{ + Handler: "identity", + Source: "def identity(ctx, x): return x", + Mode: "SCALAR", + NullPolicy: udf.NullCallHandler, + ABIContract: udf.PythonABIContract, + AdapterVersion: udf.PythonAdapterVersion, + SDKVersion: udf.PythonSDKVersion, + ArgTypes: []PythonTypeDescriptor{argument}, + ReturnType: &returnType, + }) require.NoError(t, err) routine := &Udf{Language: "python", Body: string(body)} require.NoError(t, routine.LoadPythonTypeContract()) @@ -73,7 +94,17 @@ func TestPythonTypeContractReloadReplacesDescriptors(t *testing.T) { require.NoError(t, err) returnType, err := NewPythonTypeDescriptor(types.T_int32.ToType()) require.NoError(t, err) - body, err := json.Marshal(PythonRoutineBody{ArgTypes: []PythonTypeDescriptor{argument}, ReturnType: &returnType}) + body, err := json.Marshal(PythonRoutineBody{ + Handler: "identity", + Source: "def identity(ctx, x): return x", + Mode: "SCALAR", + NullPolicy: udf.NullCallHandler, + ABIContract: udf.PythonABIContract, + AdapterVersion: udf.PythonAdapterVersion, + SDKVersion: udf.PythonSDKVersion, + ArgTypes: []PythonTypeDescriptor{argument}, + ReturnType: &returnType, + }) require.NoError(t, err) routine := &Udf{Language: "python", Body: string(body)} require.NoError(t, routine.LoadPythonTypeContract()) @@ -81,6 +112,28 @@ func TestPythonTypeContractReloadReplacesDescriptors(t *testing.T) { require.Len(t, routine.PythonArgTypes, 1) } +func TestPythonRoutineBodyRejectsUnknownAndTrailingData(t *testing.T) { + valid, err := json.Marshal(PythonRoutineBody{ + Handler: "identity", + Source: "def identity(ctx): return 1", + Mode: "SCALAR", + NullPolicy: udf.NullCallHandler, + ABIContract: udf.PythonABIContract, + AdapterVersion: udf.PythonAdapterVersion, + SDKVersion: udf.PythonSDKVersion, + ReturnType: func() *PythonTypeDescriptor { d, _ := NewPythonTypeDescriptor(types.T_int32.ToType()); return &d }(), + }) + require.NoError(t, err) + + var withUnknown map[string]any + require.NoError(t, json.Unmarshal(valid, &withUnknown)) + withUnknown["future_field"] = true + unknown, err := json.Marshal(withUnknown) + require.NoError(t, err) + require.ErrorContains(t, func() error { _, err := DecodePythonRoutineBody(string(unknown)); return err }(), "unknown field") + require.ErrorContains(t, func() error { _, err := DecodePythonRoutineBody(string(valid) + string(valid)); return err }(), "multiple JSON values") +} + func TestPythonBindingNormalizesDecimalMetadata(t *testing.T) { received := types.New(types.T_decimal64, 18, 2) required := types.New(types.T_decimal64, 18, 6) From 0a689e3898001f3ff4175c81f30d7d52fbaf01a2 Mon Sep 17 00:00:00 2001 From: iamlinjunhong <1030420200@qq.com> Date: Thu, 10 Sep 2026 12:19:34 +0800 Subject: [PATCH 028/202] fix(udf): reject non-finite handler deadlines --- pkg/udf/python/worker/test_worker.py | 2 ++ pkg/udf/python/worker/worker.py | 8 +++++++- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/pkg/udf/python/worker/test_worker.py b/pkg/udf/python/worker/test_worker.py index de513484a2161..c031ef786cd94 100644 --- a/pkg/udf/python/worker/test_worker.py +++ b/pkg/udf/python/worker/test_worker.py @@ -546,6 +546,8 @@ def test_control_numeric_fields_are_strict(self): worker._decode_control(json.dumps({"version": True, "kind": "InputBatch", "tuple": tuple_value}).encode()) with self.assertRaisesRegex(ValueError, "invalid control field sequence"): worker._required_uint64({"sequence": "1"}, "sequence") + with self.assertRaisesRegex(ValueError, "invalid invocation field timeout"): + worker._required_positive_float({"timeout": float("nan")}, "timeout", 60.0) def test_result_ack_rejects_zero_sequence(self): state = worker._InvocationState({}, 1) diff --git a/pkg/udf/python/worker/worker.py b/pkg/udf/python/worker/worker.py index 679fc1edcfb8f..bb03e7638bd3e 100644 --- a/pkg/udf/python/worker/worker.py +++ b/pkg/udf/python/worker/worker.py @@ -320,7 +320,13 @@ def _required_positive_int(value: Dict[str, Any], key: str, maximum: int) -> int def _required_positive_float(value: Dict[str, Any], key: str, maximum: float) -> float: item = value.get(key) - if isinstance(item, bool) or not isinstance(item, (int, float)) or item <= 0 or item > maximum: + if ( + isinstance(item, bool) + or not isinstance(item, (int, float)) + or not math.isfinite(item) + or item <= 0 + or item > maximum + ): raise ValueError(f"PROTOCOL: invalid invocation field {key}") return float(item) From 6b2b2bc603d894d30ec3a99e3bdf46d960083f7c Mon Sep 17 00:00:00 2001 From: iamlinjunhong <1030420200@qq.com> Date: Thu, 10 Sep 2026 12:24:17 +0800 Subject: [PATCH 029/202] fix(udf): validate Python execution input shapes --- pkg/sql/plan/function/python_udf.go | 44 ++++++++++++++++++++++++++--- pkg/sql/plan/function/udf_test.go | 18 ++++++++++++ 2 files changed, 58 insertions(+), 4 deletions(-) diff --git a/pkg/sql/plan/function/python_udf.go b/pkg/sql/plan/function/python_udf.go index 57496bb53d63b..0b25207216d47 100644 --- a/pkg/sql/plan/function/python_udf.go +++ b/pkg/sql/plan/function/python_udf.go @@ -93,6 +93,9 @@ func runPythonUdf(parameters []*vector.Vector, result vector.FunctionResultWrapp if len(parameters) == 0 || parameters[0] == nil { return fmt.Errorf("python udf: missing routine descriptor") } + if err := validatePythonRoutineDescriptor(parameters[0]); err != nil { + return err + } if length < 0 { return fmt.Errorf("python udf: negative input length %d", length) } @@ -127,10 +130,8 @@ func runPythonUdf(parameters []*vector.Vector, result vector.FunctionResultWrapp if length == 0 { return result.PreExtendAndReset(0) } - for index, input := range parameters[1:] { - if input == nil || (!input.IsConst() && input.Length() < length) { - return fmt.Errorf("python udf: input vector %d is shorter than invocation length", index) - } + if err := validatePythonInputVectors(parameters[1:], argTypes, length); err != nil { + return err } // FunctionExpressionExecutor passes an empty, non-nil select list when the // caller selected every row. In that state AnyNull is false and the @@ -241,6 +242,41 @@ func runPythonUdf(parameters []*vector.Vector, result vector.FunctionResultWrapp return nil } +func validatePythonRoutineDescriptor(descriptor *vector.Vector) error { + if descriptor == nil { + return fmt.Errorf("python udf: missing routine descriptor") + } + if !descriptor.IsConst() { + return fmt.Errorf("python udf: routine descriptor must be constant") + } + if descriptor.Length() == 0 { + return fmt.Errorf("python udf: routine descriptor is empty") + } + return nil +} + +func validatePythonInputVectors(inputs []*vector.Vector, args []types.Type, length int) error { + if len(inputs) != len(args) { + return fmt.Errorf("python udf: input column count %d does not match routine argument count %d", len(inputs), len(args)) + } + for index, input := range inputs { + if input == nil { + return fmt.Errorf("python udf: input vector %d is nil", index) + } + if !input.IsConst() && input.Length() < length { + return fmt.Errorf("python udf: input vector %d is shorter than invocation length", index) + } + if input.GetType() == nil || !pythonTypesEqual(*input.GetType(), args[index]) { + actual := "" + if input.GetType() != nil { + actual = input.GetType().String() + } + return fmt.Errorf("python udf: input vector %d type %s does not match %s", index, actual, args[index].String()) + } + } + return nil +} + func hasNullInput(inputs []*vector.Vector, row int) bool { for _, input := range inputs { if input == nil || input.IsNull(uint64(row)) { diff --git a/pkg/sql/plan/function/udf_test.go b/pkg/sql/plan/function/udf_test.go index fc43f1f0388a0..2018e7a00073a 100644 --- a/pkg/sql/plan/function/udf_test.go +++ b/pkg/sql/plan/function/udf_test.go @@ -20,6 +20,7 @@ import ( "testing" "github.com/matrixorigin/matrixone/pkg/container/types" + "github.com/matrixorigin/matrixone/pkg/container/vector" "github.com/matrixorigin/matrixone/pkg/udf" "github.com/stretchr/testify/require" ) @@ -148,6 +149,23 @@ func TestPythonBindingNormalizesDecimalMetadata(t *testing.T) { require.Equal(t, required, result.finalType[1]) } +func TestPythonExecutionValidatesDescriptorAndInputTypes(t *testing.T) { + empty, err := vector.NewConstBytes(types.T_text.ToType(), nil, 0, nil) + require.NoError(t, err) + require.ErrorContains(t, validatePythonRoutineDescriptor(empty), "descriptor is empty") + + nonConst := vector.NewOffHeapVecWithTypeAndData( + types.T_text.ToType(), []byte("routine"), 1, 1, + ) + require.ErrorContains(t, validatePythonRoutineDescriptor(nonConst), "must be constant") + + input := vector.NewOffHeapVecWithTypeAndData(types.T_int32.ToType(), make([]byte, 4), 1, 1) + require.ErrorContains(t, + validatePythonInputVectors([]*vector.Vector{input}, []types.Type{types.T_int64.ToType()}, 1), + "does not match", + ) +} + func TestPythonRoutineIsVolatile(t *testing.T) { resolved, err := GetFunctionByName(context.Background(), "python_user_defined_function", []types.Type{ types.T_text.ToType(), From 4cf272d1f7fc3d47667cd7592ecdd34b13891f9e Mon Sep 17 00:00:00 2001 From: iamlinjunhong <1030420200@qq.com> Date: Thu, 10 Sep 2026 12:29:43 +0800 Subject: [PATCH 030/202] fix(udf): validate Arrow physical descriptor limits --- pkg/sql/plan/function/udf.go | 3 +++ pkg/sql/plan/function/udf_test.go | 21 +++++++++++++++++++++ 2 files changed, 24 insertions(+) diff --git a/pkg/sql/plan/function/udf.go b/pkg/sql/plan/function/udf.go index c3beb559981df..980b8d4cb715c 100644 --- a/pkg/sql/plan/function/udf.go +++ b/pkg/sql/plan/function/udf.go @@ -218,6 +218,9 @@ func validatePythonTypeDescriptor(descriptor PythonTypeDescriptor) error { if !reflect.DeepEqual(canonical, descriptor) { return fmt.Errorf("descriptor is not canonical for %s", typ.String()) } + if _, err := descriptor.Fingerprint(); err != nil { + return fmt.Errorf("descriptor has no valid Arrow physical type: %w", err) + } return nil } func type2PlanType(typ types.Type) *plan.Type { diff --git a/pkg/sql/plan/function/udf_test.go b/pkg/sql/plan/function/udf_test.go index 2018e7a00073a..e145a47324f0b 100644 --- a/pkg/sql/plan/function/udf_test.go +++ b/pkg/sql/plan/function/udf_test.go @@ -135,6 +135,27 @@ func TestPythonRoutineBodyRejectsUnknownAndTrailingData(t *testing.T) { require.ErrorContains(t, func() error { _, err := DecodePythonRoutineBody(string(valid) + string(valid)); return err }(), "multiple JSON values") } +func TestPythonTypeContractRejectsUnrepresentableArrowPrecision(t *testing.T) { + returnDescriptor := PythonTypeDescriptor{ + TypeID: int32(types.T_decimal128), + Width: 39, + OffsetWidth: 32, + } + body, err := json.Marshal(PythonRoutineBody{ + Handler: "identity", + Source: "def identity(ctx): return 1", + Mode: "SCALAR", + NullPolicy: udf.NullCallHandler, + ABIContract: udf.PythonABIContract, + AdapterVersion: udf.PythonAdapterVersion, + SDKVersion: udf.PythonSDKVersion, + ReturnType: &returnDescriptor, + }) + require.NoError(t, err) + routine := &Udf{Language: "python", Body: string(body)} + require.ErrorContains(t, routine.LoadPythonTypeContract(), "precision") +} + func TestPythonBindingNormalizesDecimalMetadata(t *testing.T) { received := types.New(types.T_decimal64, 18, 2) required := types.New(types.T_decimal64, 18, 6) From 3eaea974329473097e2f879309aacf77cbd3be63 Mon Sep 17 00:00:00 2001 From: iamlinjunhong <1030420200@qq.com> Date: Thu, 10 Sep 2026 12:31:42 +0800 Subject: [PATCH 031/202] fix(udf): ignore null decimal payloads --- pkg/udf/python/arrow.go | 24 ++++++++++++++++-------- pkg/udf/python/arrow_test.go | 26 ++++++++++++++++++++++++++ 2 files changed, 42 insertions(+), 8 deletions(-) diff --git a/pkg/udf/python/arrow.go b/pkg/udf/python/arrow.go index b85fa657ce943..650db728280fb 100644 --- a/pkg/udf/python/arrow.go +++ b/pkg/udf/python/arrow.go @@ -662,19 +662,27 @@ func AppendArrowResult(descriptor TypeDescriptor, input arrow.Array, result vect return err } case types.T_decimal64: - value, err := decimalFromArray(input.(*array.Decimal128).Value(i), true) - if err != nil { - return err + var value types.Decimal64 + if !null { + converted, err := decimalFromArray(input.(*array.Decimal128).Value(i), true) + if err != nil { + return err + } + value = converted.(types.Decimal64) } - if err = vector.MustFunctionResult[types.Decimal64](result).Append(value.(types.Decimal64), null); err != nil { + if err := vector.MustFunctionResult[types.Decimal64](result).Append(value, null); err != nil { return err } case types.T_decimal128: - value, err := decimalFromArray(input.(*array.Decimal128).Value(i), false) - if err != nil { - return err + var value types.Decimal128 + if !null { + converted, err := decimalFromArray(input.(*array.Decimal128).Value(i), false) + if err != nil { + return err + } + value = converted.(types.Decimal128) } - if err = vector.MustFunctionResult[types.Decimal128](result).Append(value.(types.Decimal128), null); err != nil { + if err := vector.MustFunctionResult[types.Decimal128](result).Append(value, null); err != nil { return err } case types.T_char, types.T_varchar, types.T_text: diff --git a/pkg/udf/python/arrow_test.go b/pkg/udf/python/arrow_test.go index acc8ca40f451b..35adcc63d9a3b 100644 --- a/pkg/udf/python/arrow_test.go +++ b/pkg/udf/python/arrow_test.go @@ -6,6 +6,7 @@ package python import ( + "encoding/binary" "testing" "github.com/apache/arrow-go/v18/arrow" @@ -189,3 +190,28 @@ func TestArrowStringWidthCountsUnicodeCharacters(t *testing.T) { descriptor := TypeDescriptor{TypeID: int32(types.T_varchar), Width: 1, OffsetWidth: 32} require.NoError(t, validateArrowValueDomain(descriptor, values)) } + +func TestAppendArrowResultIgnoresNullDecimalPayload(t *testing.T) { + decimalType := &arrow.Decimal128Type{Precision: 18, Scale: 0} + validity := memory.NewBufferBytes([]byte{0}) + values := memory.NewBufferBytes(make([]byte, 16)) + // The physical coefficient of a NULL slot is unspecified. Make it a + // value that would not fit Decimal64 to ensure conversion never observes + // the payload after the validity check. + binary.LittleEndian.PutUint64(values.Bytes()[8:], 1) + data := array.NewData(decimalType, 1, []*memory.Buffer{validity, values}, nil, 1, 0) + decimals := array.NewDecimal128Data(data) + data.Release() + defer decimals.Release() + defer validity.Release() + defer values.Release() + + mp := mpool.MustNewZeroNoFixed() + result := vector.NewFunctionResultWrapper(types.T_decimal64.ToType(), mp) + defer result.Free() + require.NoError(t, result.PreExtendAndReset(1)) + + descriptor := TypeDescriptor{TypeID: int32(types.T_decimal64), Width: 18, OffsetWidth: 32} + require.NoError(t, AppendArrowResult(descriptor, decimals, result, mp)) + require.True(t, result.GetResultVector().IsNull(0)) +} From c1d3ef083cf802ea478cd2480f48ecbd57cd461f Mon Sep 17 00:00:00 2001 From: iamlinjunhong <1030420200@qq.com> Date: Thu, 10 Sep 2026 12:41:01 +0800 Subject: [PATCH 032/202] fix(udf): honor SQL integer type equivalence --- pkg/sql/plan/function/python_udf.go | 10 ++++++---- pkg/sql/plan/function/udf_test.go | 6 ++++++ 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/pkg/sql/plan/function/python_udf.go b/pkg/sql/plan/function/python_udf.go index 0b25207216d47..c47cb27cf2818 100644 --- a/pkg/sql/plan/function/python_udf.go +++ b/pkg/sql/plan/function/python_udf.go @@ -70,10 +70,12 @@ func checkPythonUdf(overloads []overload, inputs []types.Type) checkResult { } func pythonTypesEqual(left, right types.Type) bool { - return left.Oid == right.Oid && - left.Width == right.Width && - left.Scale == right.Scale && - left.Charset == right.Charset + // Type.Eq applies SQL value semantics. In particular, integer display + // width and the legacy scale marker are not part of the value contract; + // CASE and table expressions are allowed to materialize those fields + // differently while retaining the same fixed-width Arrow type. Decimal, + // string, temporal, and vector metadata remain part of the comparison. + return left.Eq(right) } // param parameters is same with param inputs in function checkPythonUdf diff --git a/pkg/sql/plan/function/udf_test.go b/pkg/sql/plan/function/udf_test.go index e145a47324f0b..a5844164906ff 100644 --- a/pkg/sql/plan/function/udf_test.go +++ b/pkg/sql/plan/function/udf_test.go @@ -170,6 +170,12 @@ func TestPythonBindingNormalizesDecimalMetadata(t *testing.T) { require.Equal(t, required, result.finalType[1]) } +func TestPythonTypeEqualityUsesSQLIntegerSemantics(t *testing.T) { + declared := types.New(types.T_int32, 0, -1) + caseResult := types.New(types.T_int32, 32, -1) + require.True(t, pythonTypesEqual(declared, caseResult)) +} + func TestPythonExecutionValidatesDescriptorAndInputTypes(t *testing.T) { empty, err := vector.NewConstBytes(types.T_text.ToType(), nil, 0, nil) require.NoError(t, err) From ffa97bf55d594e91deea2007ba130c87fa13d378 Mon Sep 17 00:00:00 2001 From: iamlinjunhong <1030420200@qq.com> Date: Thu, 10 Sep 2026 14:05:26 +0800 Subject: [PATCH 033/202] fix(udf): reject duplicate invocation opens --- pkg/udf/python/worker/test_worker.py | 63 ++++++++++++++++++++++++++++ pkg/udf/python/worker/worker.py | 4 +- 2 files changed, 66 insertions(+), 1 deletion(-) diff --git a/pkg/udf/python/worker/test_worker.py b/pkg/udf/python/worker/test_worker.py index c031ef786cd94..755e5847c5d48 100644 --- a/pkg/udf/python/worker/test_worker.py +++ b/pkg/udf/python/worker/test_worker.py @@ -309,6 +309,69 @@ def write_with_metadata(self, record, data): server.shutdown() self.assertEqual(0, handler.call_count) + def test_exchange_rejects_duplicate_open_control(self): + server = worker.RoutineFlightServer("grpc://127.0.0.1:0") + fence = { + "account_id": 1, + "statement_id": "review", + "group_id": "group", + "group_epoch": 1, + "invocation_id": "duplicate-open", + "lease_epoch": 1, + } + descriptor = {"type_id": worker.INT64, "offset_width": 32} + payload = { + "mode": worker.MODE_SCALAR, + "null_policy": worker.NULL_CALL, + "abi_contract": worker.ABI_CONTRACT, + "adapter_version": worker.ADAPTER_VERSION, + "sdk_version": worker.SDK_VERSION, + "args": [descriptor], + "return": descriptor, + "source": "def f(ctx, x): return x", + "handler": "f", + "max_batch_bytes": 1 << 20, + "max_batch_rows": 1024, + "handler_timeout_seconds": 2, + } + + def control(kind, **fields): + return worker._encode_control(dict(kind=kind, tuple=fence, **fields)) + + duplicate = control("OpenInvocation", payload=payload) + chunks = iter( + [ + types.SimpleNamespace(data=None, app_metadata=duplicate), + types.SimpleNamespace( + data=None, app_metadata=control("EndInput", last_sequence=1) + ), + ] + ) + reader = types.SimpleNamespace( + schema=None, read_chunk=lambda: next(chunks) + ) + + class Writer: + def begin(self, schema): + pass + + def write_metadata(self, data): + pass + + def write_with_metadata(self, record, data): + pass + + try: + with self.assertRaisesRegex(ValueError, "duplicate OpenInvocation"): + server.do_exchange( + types.SimpleNamespace(is_cancelled=lambda: False), + types.SimpleNamespace(command=duplicate), + reader, + Writer(), + ) + finally: + server.shutdown() + @unittest.skipUnless(os.name == "posix", "process-group test") def test_descendant_is_killed_after_handler_leader_exits(self): with tempfile.TemporaryDirectory(prefix="mo-udf-owned-child-") as artifact: diff --git a/pkg/udf/python/worker/worker.py b/pkg/udf/python/worker/worker.py index bb03e7638bd3e..8f3bcf14e5a70 100644 --- a/pkg/udf/python/worker/worker.py +++ b/pkg/udf/python/worker/worker.py @@ -1247,7 +1247,9 @@ def do_exchange(self, context, descriptor, reader, writer): if last_sequence != state.last_input: raise ValueError("PROTOCOL: invalid EndInput") ended = True - elif control["kind"] != "OpenInvocation": + elif control["kind"] == "OpenInvocation": + raise ValueError("PROTOCOL: duplicate OpenInvocation") + else: raise ValueError("PROTOCOL: unexpected control without Arrow data") if not ended: raise ValueError("PROTOCOL: input stream ended before EndInput") if state.acked_result != state.last_result: raise ValueError("PROTOCOL: result is not acknowledged") From fcbafe1935d5cf1214b1ddfd95fbb1b1ddb570f4 Mon Sep 17 00:00:00 2001 From: iamlinjunhong <1030420200@qq.com> Date: Thu, 10 Sep 2026 14:07:58 +0800 Subject: [PATCH 034/202] fix(udf): reject unknown control envelope fields --- pkg/udf/protocol/protocol.go | 13 ++++++++++++- pkg/udf/protocol/protocol_test.go | 14 ++++++++++++++ pkg/udf/python/worker/test_worker.py | 21 +++++++++++++++++++++ pkg/udf/python/worker/worker.py | 18 ++++++++++++++++++ 4 files changed, 65 insertions(+), 1 deletion(-) diff --git a/pkg/udf/protocol/protocol.go b/pkg/udf/protocol/protocol.go index 9fc962d572aa7..239dbd3209762 100644 --- a/pkg/udf/protocol/protocol.go +++ b/pkg/udf/protocol/protocol.go @@ -18,11 +18,13 @@ package protocol import ( + "bytes" "crypto/sha256" "encoding/hex" "encoding/json" "errors" "fmt" + "io" "sync" "time" ) @@ -113,9 +115,18 @@ func UnmarshalControl(data []byte) (Control, error) { return Control{}, fmt.Errorf("%w: control size %d is outside the allowed range", ErrProtocol, len(data)) } var control Control - if err := json.Unmarshal(data, &control); err != nil { + decoder := json.NewDecoder(bytes.NewReader(data)) + decoder.DisallowUnknownFields() + if err := decoder.Decode(&control); err != nil { return Control{}, fmt.Errorf("%w: decode control: %v", ErrProtocol, err) } + var trailing any + if err := decoder.Decode(&trailing); err != io.EOF { + if err == nil { + return Control{}, fmt.Errorf("%w: control contains trailing JSON", ErrProtocol) + } + return Control{}, fmt.Errorf("%w: decode trailing control data: %v", ErrProtocol, err) + } if control.Version != Version || control.Kind == "" { return Control{}, fmt.Errorf("%w: unsupported control version %d", ErrProtocol, control.Version) } diff --git a/pkg/udf/protocol/protocol_test.go b/pkg/udf/protocol/protocol_test.go index c640e2483e519..d129668d2edea 100644 --- a/pkg/udf/protocol/protocol_test.go +++ b/pkg/udf/protocol/protocol_test.go @@ -16,6 +16,7 @@ package protocol import ( "bytes" + "encoding/json" "errors" "sync/atomic" "testing" @@ -48,6 +49,19 @@ func TestControlRoundTripIsBoundedAndVersioned(t *testing.T) { require.ErrorIs(t, err, ErrProtocol) } +func TestControlRejectsUnknownEnvelopeFields(t *testing.T) { + wire, err := MarshalControl(Control{Kind: "InputBatch", Tuple: testTuple(), Sequence: 1}) + require.NoError(t, err) + var object map[string]any + require.NoError(t, json.Unmarshal(wire, &object)) + object["future_field"] = "must not be ignored" + wire, err = json.Marshal(object) + require.NoError(t, err) + _, err = UnmarshalControl(wire) + require.ErrorIs(t, err, ErrProtocol) + require.ErrorContains(t, err, "unknown field") +} + func TestSystemAccountIsValidFencingIdentity(t *testing.T) { tuple := testTuple() tuple.AccountID = 0 diff --git a/pkg/udf/python/worker/test_worker.py b/pkg/udf/python/worker/test_worker.py index 755e5847c5d48..41dd1cc3f8923 100644 --- a/pkg/udf/python/worker/test_worker.py +++ b/pkg/udf/python/worker/test_worker.py @@ -612,6 +612,27 @@ def test_control_numeric_fields_are_strict(self): with self.assertRaisesRegex(ValueError, "invalid invocation field timeout"): worker._required_positive_float({"timeout": float("nan")}, "timeout", 60.0) + def test_control_rejects_unknown_envelope_fields(self): + tuple_value = { + "account_id": 0, + "statement_id": "statement", + "group_id": "group", + "group_epoch": 1, + "invocation_id": "invocation", + "lease_epoch": 1, + } + value = { + "version": 1, + "kind": "InputBatch", + "tuple": tuple_value, + "sequence": 1, + "future_field": "must not be ignored", + } + with self.assertRaisesRegex(ValueError, "unsupported control field"): + worker._decode_control(json.dumps(value).encode()) + with self.assertRaisesRegex(ValueError, "unsupported control field"): + worker._encode_control(value) + def test_result_ack_rejects_zero_sequence(self): state = worker._InvocationState({}, 1) state.last_result = 1 diff --git a/pkg/udf/python/worker/worker.py b/pkg/udf/python/worker/worker.py index 8f3bcf14e5a70..16823e0dc5c88 100644 --- a/pkg/udf/python/worker/worker.py +++ b/pkg/udf/python/worker/worker.py @@ -49,6 +49,20 @@ TERMINAL_TTL_SECONDS = 300.0 ACK_TIMEOUT_SECONDS = 60.0 MAX_EXECUTION_FRAME_BYTES = 1 << 30 +_CONTROL_KEYS = frozenset( + { + "version", + "kind", + "tuple", + "sequence", + "last_sequence", + "ack_sequence", + "finish_id", + "status", + "reason", + "payload", + } +) _HANDLER_RESPONSE_ERROR = 0 _HANDLER_RESPONSE_OK = 1 _HANDLER_RESPONSE_FD_ENV = "MATRIXONE_HANDLER_RESPONSE_FD" @@ -295,6 +309,8 @@ def _decode_control(data: bytes) -> Dict[str, Any]: or not value["kind"] ): raise ValueError("PROTOCOL: unsupported control") + if set(value) - _CONTROL_KEYS: + raise ValueError("PROTOCOL: unsupported control field") _tuple_key(value.get("tuple") or {}) return value @@ -341,6 +357,8 @@ def _required_uint64(value: Dict[str, Any], key: str, allow_zero: bool = False) def _encode_control(value: Dict[str, Any]) -> bytes: value = dict(value) + if set(value) - _CONTROL_KEYS: + raise ValueError("PROTOCOL: unsupported control field") value.setdefault("version", PROTOCOL_VERSION) _tuple_key(value["tuple"]) data = json.dumps(value, separators=(",", ":"), ensure_ascii=True).encode("utf-8") From 41670cbab913ccda76c83be38d38d44bc51342ef Mon Sep 17 00:00:00 2001 From: iamlinjunhong <1030420200@qq.com> Date: Thu, 10 Sep 2026 14:08:55 +0800 Subject: [PATCH 035/202] fix(udf): reject duplicate control JSON fields --- pkg/udf/protocol/protocol.go | 62 ++++++++++++++++++++++++++++ pkg/udf/protocol/protocol_test.go | 7 ++++ pkg/udf/python/worker/test_worker.py | 10 +++++ pkg/udf/python/worker/worker.py | 19 ++++++++- 4 files changed, 97 insertions(+), 1 deletion(-) diff --git a/pkg/udf/protocol/protocol.go b/pkg/udf/protocol/protocol.go index 239dbd3209762..42274f57956c4 100644 --- a/pkg/udf/protocol/protocol.go +++ b/pkg/udf/protocol/protocol.go @@ -114,6 +114,9 @@ func UnmarshalControl(data []byte) (Control, error) { if len(data) == 0 || len(data) > MaxControlBytes { return Control{}, fmt.Errorf("%w: control size %d is outside the allowed range", ErrProtocol, len(data)) } + if err := rejectDuplicateJSONKeys(data); err != nil { + return Control{}, fmt.Errorf("%w: %v", ErrProtocol, err) + } var control Control decoder := json.NewDecoder(bytes.NewReader(data)) decoder.DisallowUnknownFields() @@ -136,6 +139,65 @@ func UnmarshalControl(data []byte) (Control, error) { return control, nil } +func rejectDuplicateJSONKeys(data []byte) error { + decoder := json.NewDecoder(bytes.NewReader(data)) + if err := walkJSONValue(decoder); err != nil { + return err + } + var trailing any + if err := decoder.Decode(&trailing); err != io.EOF { + if err == nil { + return errors.New("control contains trailing JSON") + } + return fmt.Errorf("decode trailing control data: %v", err) + } + return nil +} + +func walkJSONValue(decoder *json.Decoder) error { + token, err := decoder.Token() + if err != nil { + return fmt.Errorf("decode control JSON: %v", err) + } + delim, ok := token.(json.Delim) + if !ok { + return nil + } + switch delim { + case '{': + seen := make(map[string]struct{}) + for decoder.More() { + keyToken, err := decoder.Token() + if err != nil { + return fmt.Errorf("decode control object key: %v", err) + } + key, ok := keyToken.(string) + if !ok { + return errors.New("control object key is not a string") + } + if _, exists := seen[key]; exists { + return fmt.Errorf("duplicate control JSON field %q", key) + } + seen[key] = struct{}{} + if err := walkJSONValue(decoder); err != nil { + return err + } + } + _, err = decoder.Token() + return err + case '[': + for decoder.More() { + if err := walkJSONValue(decoder); err != nil { + return err + } + } + _, err = decoder.Token() + return err + default: + return fmt.Errorf("unexpected control JSON delimiter %q", delim) + } +} + // Sequence validates the independent input and result directions of one // invocation. EndInput is a normal half-close; it does not make results // terminal until all results have been acknowledged. diff --git a/pkg/udf/protocol/protocol_test.go b/pkg/udf/protocol/protocol_test.go index d129668d2edea..206e46e6a8e5b 100644 --- a/pkg/udf/protocol/protocol_test.go +++ b/pkg/udf/protocol/protocol_test.go @@ -62,6 +62,13 @@ func TestControlRejectsUnknownEnvelopeFields(t *testing.T) { require.ErrorContains(t, err, "unknown field") } +func TestControlRejectsDuplicateJSONFields(t *testing.T) { + wire := []byte(`{"version":1,"kind":"InputBatch","tuple":{"account_id":1,"statement_id":"statement","group_id":"group","group_epoch":2,"invocation_id":"invocation","lease_epoch":3},"sequence":1,"sequence":2}`) + _, err := UnmarshalControl(wire) + require.ErrorIs(t, err, ErrProtocol) + require.ErrorContains(t, err, "duplicate control JSON field") +} + func TestSystemAccountIsValidFencingIdentity(t *testing.T) { tuple := testTuple() tuple.AccountID = 0 diff --git a/pkg/udf/python/worker/test_worker.py b/pkg/udf/python/worker/test_worker.py index 41dd1cc3f8923..8cd5be5b5fd3e 100644 --- a/pkg/udf/python/worker/test_worker.py +++ b/pkg/udf/python/worker/test_worker.py @@ -633,6 +633,16 @@ def test_control_rejects_unknown_envelope_fields(self): with self.assertRaisesRegex(ValueError, "unsupported control field"): worker._encode_control(value) + def test_control_rejects_duplicate_json_fields(self): + wire = ( + b'{"version":1,"kind":"InputBatch","tuple":{"account_id":1,' + b'"statement_id":"statement","group_id":"group","group_epoch":1,' + b'"invocation_id":"invocation","lease_epoch":1},' + b'"sequence":1,"sequence":2}' + ) + with self.assertRaisesRegex(ValueError, "duplicate control JSON field"): + worker._decode_control(wire) + def test_result_ack_rejects_zero_sequence(self): state = worker._InvocationState({}, 1) state.last_result = 1 diff --git a/pkg/udf/python/worker/worker.py b/pkg/udf/python/worker/worker.py index 16823e0dc5c88..a8d0f3e394781 100644 --- a/pkg/udf/python/worker/worker.py +++ b/pkg/udf/python/worker/worker.py @@ -297,10 +297,27 @@ def _tuple_key(value: Dict[str, Any]) -> tuple: ) +def _reject_duplicate_json_pairs(pairs): + result = {} + for key, value in pairs: + if key in result: + raise ValueError(f"PROTOCOL: duplicate control JSON field {key!r}") + result[key] = value + return result + + +def _reject_nonstandard_json_constant(value): + raise ValueError(f"PROTOCOL: invalid JSON constant {value}") + + def _decode_control(data: bytes) -> Dict[str, Any]: if not data or len(data) > MAX_CONTROL_BYTES: raise ValueError("PROTOCOL: invalid control size") - value = json.loads(bytes(data).decode("utf-8")) + value = json.loads( + bytes(data).decode("utf-8"), + object_pairs_hook=_reject_duplicate_json_pairs, + parse_constant=_reject_nonstandard_json_constant, + ) if ( not isinstance(value, dict) or type(value.get("version")) is not int From ed014fa49ca53fd1af2ba9d91e6e94c2a1b8b6b2 Mon Sep 17 00:00:00 2001 From: iamlinjunhong <1030420200@qq.com> Date: Thu, 10 Sep 2026 14:09:47 +0800 Subject: [PATCH 036/202] fix(udf): validate opens before ledger admission --- pkg/udf/python/worker/test_worker.py | 47 ++++++++++++++++++++++++++++ pkg/udf/python/worker/worker.py | 11 +++++-- 2 files changed, 55 insertions(+), 3 deletions(-) diff --git a/pkg/udf/python/worker/test_worker.py b/pkg/udf/python/worker/test_worker.py index 8cd5be5b5fd3e..2494d83e13835 100644 --- a/pkg/udf/python/worker/test_worker.py +++ b/pkg/udf/python/worker/test_worker.py @@ -372,6 +372,53 @@ def write_with_metadata(self, record, data): finally: server.shutdown() + def test_invalid_open_does_not_consume_terminal_ledger(self): + server = worker.RoutineFlightServer("grpc://127.0.0.1:0") + fence = { + "account_id": 1, + "statement_id": "review", + "group_id": "group", + "group_epoch": 1, + "invocation_id": "invalid-open", + "lease_epoch": 1, + } + payload = { + "mode": worker.MODE_SCALAR, + "null_policy": worker.NULL_CALL, + "abi_contract": worker.ABI_CONTRACT, + "adapter_version": worker.ADAPTER_VERSION, + "sdk_version": worker.SDK_VERSION, + "args": [{"type_id": worker.INT64, "offset_width": 32}], + "return": {"type_id": 999, "offset_width": 32}, + "source": "def f(ctx, x): return x", + "handler": "f", + "max_batch_bytes": 1 << 20, + "max_batch_rows": 1024, + "handler_timeout_seconds": 2, + } + command = worker._encode_control( + {"kind": "OpenInvocation", "tuple": fence, "payload": payload} + ) + reader = types.SimpleNamespace(schema=None, read_chunk=lambda: (_ for _ in ()).throw(StopIteration)) + writer = types.SimpleNamespace( + begin=lambda schema: None, + write_metadata=lambda data: None, + write_with_metadata=lambda record, data: None, + ) + key = worker._tuple_key(fence) + try: + with self.assertRaisesRegex(ValueError, "unsupported type id"): + server.do_exchange( + types.SimpleNamespace(is_cancelled=lambda: False), + types.SimpleNamespace(command=command), + reader, + writer, + ) + self.assertNotIn(key, server._active) + self.assertNotIn(key, server._terminal) + finally: + server.shutdown() + @unittest.skipUnless(os.name == "posix", "process-group test") def test_descendant_is_killed_after_handler_leader_exits(self): with tempfile.TemporaryDirectory(prefix="mo-udf-owned-child-") as artifact: diff --git a/pkg/udf/python/worker/worker.py b/pkg/udf/python/worker/worker.py index a8d0f3e394781..d428157c40cbe 100644 --- a/pkg/udf/python/worker/worker.py +++ b/pkg/udf/python/worker/worker.py @@ -1211,12 +1211,17 @@ def do_exchange(self, context, descriptor, reader, writer): if mode not in (MODE_SCALAR, MODE_VECTOR) or null_policy not in (NULL_CALL, NULL_RETURN): raise ValueError("PROTOCOL: unsupported call mode or NULL policy") if abi_contract != ABI_CONTRACT or adapter_version != ADAPTER_VERSION: raise ValueError("PROTOCOL: unsupported Python ABI contract") if sdk_version != SDK_VERSION: raise ValueError("PROTOCOL: unsupported Python SDK") - state = self._admit(key) - state.tuple = open_control["tuple"] source = _required_string(payload, "source") handler_name = _required_string(payload, "handler") - schema = None + # Validate the complete frozen type contract before reserving a + # ledger entry. A malformed Open is rejected before it can leave + # a terminal tombstone behind. + for index, descriptor in enumerate(args): + _field(f"arg_{index}", descriptor) result_field = _field("result", result_descriptor) + state = self._admit(key) + state.tuple = open_control["tuple"] + schema = None result_schema = pa.schema([result_field]) writer.begin(result_schema) writer_started = True From a1045eb3adf5b879c65b6bd5d5f0be632e315641 Mon Sep 17 00:00:00 2001 From: iamlinjunhong <1030420200@qq.com> Date: Thu, 10 Sep 2026 14:11:17 +0800 Subject: [PATCH 037/202] fix(udf): enforce Python descriptor shape --- pkg/udf/python/worker/test_worker.py | 11 +++++++++++ pkg/udf/python/worker/worker.py | 28 ++++++++++++++++++++++++++++ 2 files changed, 39 insertions(+) diff --git a/pkg/udf/python/worker/test_worker.py b/pkg/udf/python/worker/test_worker.py index 2494d83e13835..4537ae2d4a827 100644 --- a/pkg/udf/python/worker/test_worker.py +++ b/pkg/udf/python/worker/test_worker.py @@ -52,6 +52,17 @@ def test_metadata_is_owned_by_adapter(self): with self.assertRaisesRegex(ValueError, "field name"): worker._validate_field(pa.field("other", field.type, metadata=field.metadata), "result", descriptor) + def test_type_descriptor_shape_is_strict(self): + with self.assertRaisesRegex(ValueError, "unsupported descriptor field"): + worker._field( + "result", + {"type_id": worker.INT64, "offset_width": 32, "future": 1}, + ) + with self.assertRaisesRegex(ValueError, "type_id must be an integer"): + worker._field("result", {"type_id": "23", "offset_width": 32}) + with self.assertRaisesRegex(ValueError, "offset_width must be an integer"): + worker._field("result", {"type_id": worker.INT64, "offset_width": "32"}) + def test_zero_temporal_is_distinct_from_null(self): descriptor = {"type_id": worker.DATE, "offset_width": 32, "temporal_encoding": "sql_zero_struct"} zero = worker.SqlDate(True, None) diff --git a/pkg/udf/python/worker/worker.py b/pkg/udf/python/worker/worker.py index d428157c40cbe..7f0ef04a50eca 100644 --- a/pkg/udf/python/worker/worker.py +++ b/pkg/udf/python/worker/worker.py @@ -63,6 +63,21 @@ "payload", } ) +_DESCRIPTOR_KEYS = frozenset( + { + "type_id", + "width", + "scale", + "charset", + "offset_width", + "json_encoding", + "temporal_encoding", + } +) +_DESCRIPTOR_INT_KEYS = frozenset( + {"type_id", "width", "scale", "charset", "offset_width"} +) +_DESCRIPTOR_TEXT_KEYS = frozenset({"json_encoding", "temporal_encoding"}) _HANDLER_RESPONSE_ERROR = 0 _HANDLER_RESPONSE_OK = 1 _HANDLER_RESPONSE_FD_ENV = "MATRIXONE_HANDLER_RESPONSE_FD" @@ -385,6 +400,19 @@ def _encode_control(value: Dict[str, Any]) -> bytes: def _canonical_descriptor(descriptor: Dict[str, Any]) -> bytes: + if not isinstance(descriptor, dict): + raise ValueError("TYPE_CONTRACT: descriptor must be an object") + unknown = set(descriptor) - _DESCRIPTOR_KEYS + if unknown: + raise ValueError("TYPE_CONTRACT: unsupported descriptor field") + if type(descriptor.get("type_id")) is not int: + raise ValueError("TYPE_CONTRACT: descriptor type_id must be an integer") + for key in _DESCRIPTOR_INT_KEYS - {"type_id"}: + if key in descriptor and type(descriptor[key]) is not int: + raise ValueError(f"TYPE_CONTRACT: descriptor {key} must be an integer") + for key in _DESCRIPTOR_TEXT_KEYS: + if key in descriptor and type(descriptor[key]) is not str: + raise ValueError(f"TYPE_CONTRACT: descriptor {key} must be text") order = ("type_id", "width", "scale", "charset", "offset_width", "json_encoding", "temporal_encoding") result = {} for key in order: From e8781f422211ce170f897c45ed26e030373cca9a Mon Sep 17 00:00:00 2001 From: iamlinjunhong <1030420200@qq.com> Date: Thu, 10 Sep 2026 14:12:07 +0800 Subject: [PATCH 038/202] fix(udf): reject Arrow bodies on control frames --- pkg/udf/python/gateway.go | 5 ++++- pkg/udf/python/gateway_test.go | 22 ++++++++++++++++++++++ 2 files changed, 26 insertions(+), 1 deletion(-) diff --git a/pkg/udf/python/gateway.go b/pkg/udf/python/gateway.go index 2c6753851c1dd..0329541172bad 100644 --- a/pkg/udf/python/gateway.go +++ b/pkg/udf/python/gateway.go @@ -419,6 +419,9 @@ func (g *Gateway) receiveResultBatch( } return int(expectedRows), nil } + if len(data.DataBody) != 0 { + return 0, fmt.Errorf("python udf: control frame contains an Arrow body") + } control, err := protocol.UnmarshalControl(data.AppMetadata) if err != nil { return 0, err @@ -476,7 +479,7 @@ func (g *Gateway) receiveFinish( } return fmt.Errorf("python udf: receive Finish: %w", err) } - if data == nil || len(data.DataHeader) > 0 { + if data == nil || len(data.DataHeader) > 0 || len(data.DataBody) > 0 { return fmt.Errorf("python udf: unexpected Arrow data while finishing") } control, err := protocol.UnmarshalControl(data.AppMetadata) diff --git a/pkg/udf/python/gateway_test.go b/pkg/udf/python/gateway_test.go index 793c5070374a0..f103923374c6c 100644 --- a/pkg/udf/python/gateway_test.go +++ b/pkg/udf/python/gateway_test.go @@ -140,3 +140,25 @@ func TestGatewayFinishRejectsLateHalfStreamControls(t *testing.T) { }) } } + +func TestGatewayRejectsArrowBodyOnControlFrame(t *testing.T) { + tuple := validInvocation().Tuple + sequence := protocol.Sequence{} + require.NoError(t, sequence.AcceptInput(1)) + require.NoError(t, sequence.EndInput(1)) + require.NoError(t, sequence.AcceptResult(1)) + require.NoError(t, sequence.AcknowledgeResults(1)) + control := gatewayControl(t, "Finish", tuple, func(value *protocol.Control) { + value.Status = statusOK + value.FinishID = "finish-1" + value.LastSequence = 1 + }) + control.DataBody = []byte("hidden Arrow payload") + gateway := &Gateway{cfg: ClientConfig{RequestTimeout: time.Second}} + err := gateway.receiveFinish( + context.Background(), nil, + &gatewayResultStream{results: []*flight.FlightData{control}}, + tuple, 1, 1, &sequence, + ) + require.ErrorContains(t, err, "unexpected Arrow data") +} From 2ac59e33fd99daf9f82c79813d97cbab803bb727 Mon Sep 17 00:00:00 2001 From: iamlinjunhong <1030420200@qq.com> Date: Thu, 10 Sep 2026 14:15:36 +0800 Subject: [PATCH 039/202] fix(udf): align Python fencing tuple validation --- pkg/udf/python/worker/test_worker.py | 16 ++++++++++++++++ pkg/udf/python/worker/worker.py | 12 ++++++++++++ 2 files changed, 28 insertions(+) diff --git a/pkg/udf/python/worker/test_worker.py b/pkg/udf/python/worker/test_worker.py index 4537ae2d4a827..2c1441a1649b2 100644 --- a/pkg/udf/python/worker/test_worker.py +++ b/pkg/udf/python/worker/test_worker.py @@ -691,6 +691,22 @@ def test_control_rejects_unknown_envelope_fields(self): with self.assertRaisesRegex(ValueError, "unsupported control field"): worker._encode_control(value) + def test_control_rejects_unknown_fencing_tuple_fields(self): + tuple_value = { + "account_id": 0, + "statement_id": "statement", + "group_id": "group", + "group_epoch": 1, + "invocation_id": "invocation", + "lease_epoch": 1, + "future_epoch": 2, + } + with self.assertRaisesRegex(ValueError, "unsupported fencing tuple field"): + worker._tuple_key(tuple_value) + value = {"version": 1, "kind": "InputBatch", "tuple": tuple_value, "sequence": 1} + with self.assertRaisesRegex(ValueError, "unsupported fencing tuple field"): + worker._decode_control(json.dumps(value).encode()) + def test_control_rejects_duplicate_json_fields(self): wire = ( b'{"version":1,"kind":"InputBatch","tuple":{"account_id":1,' diff --git a/pkg/udf/python/worker/worker.py b/pkg/udf/python/worker/worker.py index 7f0ef04a50eca..ad9d6f6c3379a 100644 --- a/pkg/udf/python/worker/worker.py +++ b/pkg/udf/python/worker/worker.py @@ -63,6 +63,16 @@ "payload", } ) +_TUPLE_KEYS = frozenset( + { + "account_id", + "statement_id", + "group_id", + "group_epoch", + "invocation_id", + "lease_epoch", + } +) _DESCRIPTOR_KEYS = frozenset( { "type_id", @@ -283,6 +293,8 @@ def _statement_context(raw: Any) -> Optional[StatementContext]: def _tuple_key(value: Dict[str, Any]) -> tuple: + if not isinstance(value, dict) or set(value) - _TUPLE_KEYS: + raise ValueError("PROTOCOL: unsupported fencing tuple field") string_fields = ("statement_id", "group_id", "invocation_id") if not isinstance(value, dict) or any(not isinstance(value.get(k), str) or not value[k] for k in string_fields): raise ValueError("PROTOCOL: incomplete fencing tuple") From ce47f4ead7575a616bf01af560e4082aca93e8ab Mon Sep 17 00:00:00 2001 From: iamlinjunhong <1030420200@qq.com> Date: Thu, 10 Sep 2026 14:16:30 +0800 Subject: [PATCH 040/202] fix(udf): reject empty Flight input frames --- pkg/udf/python/worker/test_worker.py | 56 ++++++++++++++++++++++++++++ pkg/udf/python/worker/worker.py | 2 + 2 files changed, 58 insertions(+) diff --git a/pkg/udf/python/worker/test_worker.py b/pkg/udf/python/worker/test_worker.py index 2c1441a1649b2..11c576c3b2781 100644 --- a/pkg/udf/python/worker/test_worker.py +++ b/pkg/udf/python/worker/test_worker.py @@ -430,6 +430,62 @@ def test_invalid_open_does_not_consume_terminal_ledger(self): finally: server.shutdown() + def test_exchange_rejects_empty_input_frame(self): + server = worker.RoutineFlightServer("grpc://127.0.0.1:0") + fence = { + "account_id": 1, + "statement_id": "review", + "group_id": "group", + "group_epoch": 1, + "invocation_id": "empty-frame", + "lease_epoch": 1, + } + descriptor = {"type_id": worker.INT64, "offset_width": 32} + payload = { + "mode": worker.MODE_SCALAR, + "null_policy": worker.NULL_CALL, + "abi_contract": worker.ABI_CONTRACT, + "adapter_version": worker.ADAPTER_VERSION, + "sdk_version": worker.SDK_VERSION, + "args": [descriptor], + "return": descriptor, + "source": "def f(ctx, x): return x", + "handler": "f", + "max_batch_bytes": 1 << 20, + "max_batch_rows": 1024, + "handler_timeout_seconds": 2, + } + + def control(kind, **fields): + return worker._encode_control(dict(kind=kind, tuple=fence, **fields)) + + chunks = iter( + [ + types.SimpleNamespace(data=None, app_metadata=b""), + types.SimpleNamespace( + data=None, app_metadata=control("EndInput", last_sequence=0) + ), + ] + ) + reader = types.SimpleNamespace(schema=None, read_chunk=lambda: next(chunks)) + writer = types.SimpleNamespace( + begin=lambda schema: None, + write_metadata=lambda data: None, + write_with_metadata=lambda record, data: None, + ) + command = control("OpenInvocation", payload=payload) + try: + with mock.patch.object(worker._InvocationState, "wait_finish_ack"): + with self.assertRaisesRegex(ValueError, "empty input frame"): + server.do_exchange( + types.SimpleNamespace(is_cancelled=lambda: False), + types.SimpleNamespace(command=command), + reader, + writer, + ) + finally: + server.shutdown() + @unittest.skipUnless(os.name == "posix", "process-group test") def test_descendant_is_killed_after_handler_leader_exits(self): with tempfile.TemporaryDirectory(prefix="mo-udf-owned-child-") as artifact: diff --git a/pkg/udf/python/worker/worker.py b/pkg/udf/python/worker/worker.py index ad9d6f6c3379a..3bac6dcb30c29 100644 --- a/pkg/udf/python/worker/worker.py +++ b/pkg/udf/python/worker/worker.py @@ -1272,6 +1272,8 @@ def do_exchange(self, context, descriptor, reader, writer): chunk = reader.read_chunk() except StopIteration: break + if chunk is None or (chunk.data is None and not chunk.app_metadata): + raise ValueError("PROTOCOL: empty input frame") if chunk.data is not None: if ended: raise ValueError("PROTOCOL: input arrived after EndInput") From 67ac30b4ef1ca32755b44d0ab8678a71d33f8672 Mon Sep 17 00:00:00 2001 From: iamlinjunhong <1030420200@qq.com> Date: Thu, 10 Sep 2026 14:20:39 +0800 Subject: [PATCH 041/202] fix(udf): encode zero sequence control fields --- pkg/udf/protocol/protocol.go | 31 ++++++++++++++++++++++++++++ pkg/udf/protocol/protocol_test.go | 23 +++++++++++++++++++++ pkg/udf/python/worker/test_worker.py | 16 ++++++++++++++ 3 files changed, 70 insertions(+) diff --git a/pkg/udf/protocol/protocol.go b/pkg/udf/protocol/protocol.go index 42274f57956c4..3e2d14298b612 100644 --- a/pkg/udf/protocol/protocol.go +++ b/pkg/udf/protocol/protocol.go @@ -87,6 +87,37 @@ type Control struct { Payload json.RawMessage `json:"payload,omitempty"` } +// MarshalJSON keeps closing controls self-describing when their final +// sequence is zero. The zero value is meaningful for an empty input stream; +// omitting it would make EndInput(0) indistinguishable from a malformed +// control at the Python boundary. Other controls retain the compact +// omission used for fields they do not own. +func (c Control) MarshalJSON() ([]byte, error) { + type wireControl struct { + Version int `json:"version"` + Kind string `json:"kind"` + Tuple FencingTuple `json:"tuple"` + Sequence uint64 `json:"sequence,omitempty"` + LastSequence *uint64 `json:"last_sequence,omitempty"` + AckSequence uint64 `json:"ack_sequence,omitempty"` + FinishID string `json:"finish_id,omitempty"` + Status string `json:"status,omitempty"` + Reason string `json:"reason,omitempty"` + Payload json.RawMessage `json:"payload,omitempty"` + } + wire := wireControl{ + Version: c.Version, Kind: c.Kind, Tuple: c.Tuple, + Sequence: c.Sequence, AckSequence: c.AckSequence, + FinishID: c.FinishID, Status: c.Status, Reason: c.Reason, + Payload: c.Payload, + } + if c.Kind == "EndInput" || c.Kind == "Finish" || c.LastSequence != 0 { + lastSequence := c.LastSequence + wire.LastSequence = &lastSequence + } + return json.Marshal(wire) +} + // MarshalControl produces the byte representation used in the Flight // application metadata field. Struct-field order is intentional: the same // bytes are used when a descriptor fingerprint is calculated. diff --git a/pkg/udf/protocol/protocol_test.go b/pkg/udf/protocol/protocol_test.go index 206e46e6a8e5b..97b5e08af9d3e 100644 --- a/pkg/udf/protocol/protocol_test.go +++ b/pkg/udf/protocol/protocol_test.go @@ -69,6 +69,29 @@ func TestControlRejectsDuplicateJSONFields(t *testing.T) { require.ErrorContains(t, err, "duplicate control JSON field") } +func TestClosingControlCarriesZeroLastSequence(t *testing.T) { + for _, kind := range []string{"EndInput", "Finish"} { + wire, err := MarshalControl(Control{Kind: kind, Tuple: testTuple()}) + require.NoError(t, err) + var object map[string]any + require.NoError(t, json.Unmarshal(wire, &object)) + lastSequence, ok := object["last_sequence"] + require.True(t, ok, "%s must carry an explicit last_sequence", kind) + require.Equal(t, float64(0), lastSequence) + + decoded, err := UnmarshalControl(wire) + require.NoError(t, err) + require.Zero(t, decoded.LastSequence) + } + + wire, err := MarshalControl(Control{Kind: "InputBatch", Tuple: testTuple(), Sequence: 1}) + require.NoError(t, err) + var object map[string]any + require.NoError(t, json.Unmarshal(wire, &object)) + _, ok := object["last_sequence"] + require.False(t, ok, "non-closing controls should not grow a closing-only field") +} + func TestSystemAccountIsValidFencingIdentity(t *testing.T) { tuple := testTuple() tuple.AccountID = 0 diff --git a/pkg/udf/python/worker/test_worker.py b/pkg/udf/python/worker/test_worker.py index 11c576c3b2781..d036b9d93c5bd 100644 --- a/pkg/udf/python/worker/test_worker.py +++ b/pkg/udf/python/worker/test_worker.py @@ -63,6 +63,22 @@ def test_type_descriptor_shape_is_strict(self): with self.assertRaisesRegex(ValueError, "offset_width must be an integer"): worker._field("result", {"type_id": worker.INT64, "offset_width": "32"}) + def test_closing_control_preserves_zero_last_sequence(self): + fence = { + "account_id": 1, + "statement_id": "review", + "group_id": "group", + "group_epoch": 1, + "invocation_id": "zero-sequence", + "lease_epoch": 1, + } + for kind in ("EndInput", "Finish"): + wire = worker._encode_control( + {"kind": kind, "tuple": fence, "last_sequence": 0} + ) + self.assertEqual(0, worker._decode_control(wire)["last_sequence"]) + self.assertIn(b'"last_sequence":0', wire) + def test_zero_temporal_is_distinct_from_null(self): descriptor = {"type_id": worker.DATE, "offset_width": 32, "temporal_encoding": "sql_zero_struct"} zero = worker.SqlDate(True, None) From c7319ebacf06402c2fc383094b5d79af0927a4ee Mon Sep 17 00:00:00 2001 From: iamlinjunhong <1030420200@qq.com> Date: Thu, 10 Sep 2026 14:27:50 +0800 Subject: [PATCH 042/202] fix(udf): enforce control field ownership --- pkg/udf/protocol/protocol.go | 121 +++++++++++++++++++++++++++ pkg/udf/protocol/protocol_test.go | 30 ++++++- pkg/udf/python/gateway_test.go | 4 +- pkg/udf/python/worker/test_worker.py | 40 ++++++++- pkg/udf/python/worker/worker.py | 93 ++++++++++++++++---- 5 files changed, 265 insertions(+), 23 deletions(-) diff --git a/pkg/udf/protocol/protocol.go b/pkg/udf/protocol/protocol.go index 3e2d14298b612..ce044aed64efa 100644 --- a/pkg/udf/protocol/protocol.go +++ b/pkg/udf/protocol/protocol.go @@ -87,6 +87,117 @@ type Control struct { Payload json.RawMessage `json:"payload,omitempty"` } +type controlFieldRule struct { + allowed map[string]struct{} + required map[string]struct{} +} + +var controlFieldRules = map[string]controlFieldRule{ + "OpenInvocation": {allowed: map[string]struct{}{"payload": {}}, required: map[string]struct{}{"payload": {}}}, + "InputBatch": {allowed: map[string]struct{}{"sequence": {}}, required: map[string]struct{}{"sequence": {}}}, + "EndInput": {allowed: map[string]struct{}{"last_sequence": {}}, required: map[string]struct{}{"last_sequence": {}}}, + "ResultSchema": {allowed: map[string]struct{}{}}, + "InputConsumed": {allowed: map[string]struct{}{"sequence": {}}, required: map[string]struct{}{"sequence": {}}}, + "ResultBatch": {allowed: map[string]struct{}{"sequence": {}}, required: map[string]struct{}{"sequence": {}}}, + "Finish": {allowed: map[string]struct{}{"last_sequence": {}, "finish_id": {}, "status": {}}, required: map[string]struct{}{"last_sequence": {}, "finish_id": {}, "status": {}}}, + "AcknowledgeResults": {allowed: map[string]struct{}{"ack_sequence": {}}, required: map[string]struct{}{"ack_sequence": {}}}, + "AcknowledgeFinish": {allowed: map[string]struct{}{"finish_id": {}}, required: map[string]struct{}{"finish_id": {}}}, + "Ack": {allowed: map[string]struct{}{"ack_sequence": {}, "finish_id": {}, "status": {}}, required: map[string]struct{}{"status": {}}}, + "Error": {allowed: map[string]struct{}{"status": {}, "reason": {}}, required: map[string]struct{}{"status": {}, "reason": {}}}, +} + +var controlBaseFields = map[string]struct{}{ + "version": {}, "kind": {}, "tuple": {}, +} + +func validateControlFields(control Control, wire map[string]json.RawMessage) error { + rule, ok := controlFieldRules[control.Kind] + if !ok { + return fmt.Errorf("%w: unsupported control kind %q", ErrProtocol, control.Kind) + } + if wire != nil { + for field := range wire { + if _, base := controlBaseFields[field]; base { + continue + } + if _, allowed := rule.allowed[field]; !allowed { + return fmt.Errorf("%w: field %q is not valid for control kind %q", ErrProtocol, field, control.Kind) + } + } + for field := range rule.required { + if _, present := wire[field]; !present { + return fmt.Errorf("%w: control kind %q is missing field %q", ErrProtocol, control.Kind, field) + } + } + } + if control.Sequence != 0 { + if _, allowed := rule.allowed["sequence"]; !allowed { + return fmt.Errorf("%w: field %q is not valid for control kind %q", ErrProtocol, "sequence", control.Kind) + } + } + if control.LastSequence != 0 { + if _, allowed := rule.allowed["last_sequence"]; !allowed { + return fmt.Errorf("%w: field %q is not valid for control kind %q", ErrProtocol, "last_sequence", control.Kind) + } + } + if control.AckSequence != 0 { + if _, allowed := rule.allowed["ack_sequence"]; !allowed { + return fmt.Errorf("%w: field %q is not valid for control kind %q", ErrProtocol, "ack_sequence", control.Kind) + } + } + if control.FinishID != "" { + if _, allowed := rule.allowed["finish_id"]; !allowed { + return fmt.Errorf("%w: field %q is not valid for control kind %q", ErrProtocol, "finish_id", control.Kind) + } + } + if control.Status != "" { + if _, allowed := rule.allowed["status"]; !allowed { + return fmt.Errorf("%w: field %q is not valid for control kind %q", ErrProtocol, "status", control.Kind) + } + } + if control.Reason != "" { + if _, allowed := rule.allowed["reason"]; !allowed { + return fmt.Errorf("%w: field %q is not valid for control kind %q", ErrProtocol, "reason", control.Kind) + } + } + if len(control.Payload) != 0 { + if _, allowed := rule.allowed["payload"]; !allowed { + return fmt.Errorf("%w: field %q is not valid for control kind %q", ErrProtocol, "payload", control.Kind) + } + } + switch control.Kind { + case "OpenInvocation": + if len(control.Payload) == 0 && wire == nil { + return fmt.Errorf("%w: control kind %q is missing field %q", ErrProtocol, control.Kind, "payload") + } + case "InputBatch", "InputConsumed", "ResultBatch": + if control.Sequence == 0 { + return fmt.Errorf("%w: control kind %q requires a positive sequence", ErrProtocol, control.Kind) + } + case "AcknowledgeResults": + if control.AckSequence == 0 { + return fmt.Errorf("%w: control kind %q requires a positive ack_sequence", ErrProtocol, control.Kind) + } + case "Finish": + if control.FinishID == "" || control.Status == "" { + return fmt.Errorf("%w: control kind %q requires finish_id and status", ErrProtocol, control.Kind) + } + case "AcknowledgeFinish": + if control.FinishID == "" { + return fmt.Errorf("%w: control kind %q requires finish_id", ErrProtocol, control.Kind) + } + case "Ack": + if control.Status == "" || ((control.AckSequence == 0) == (control.FinishID == "")) { + return fmt.Errorf("%w: Ack requires status and exactly one acknowledgement identity", ErrProtocol) + } + case "Error": + if control.Status == "" || control.Reason == "" { + return fmt.Errorf("%w: control kind %q requires status and reason", ErrProtocol, control.Kind) + } + } + return nil +} + // MarshalJSON keeps closing controls self-describing when their final // sequence is zero. The zero value is meaningful for an empty input stream; // omitting it would make EndInput(0) indistinguishable from a malformed @@ -131,6 +242,9 @@ func MarshalControl(control Control) ([]byte, error) { if err := control.Tuple.Validate(); err != nil { return nil, err } + if err := validateControlFields(control, nil); err != nil { + return nil, err + } encoded, err := json.Marshal(control) if err != nil { return nil, fmt.Errorf("%w: encode control: %v", ErrProtocol, err) @@ -167,6 +281,13 @@ func UnmarshalControl(data []byte) (Control, error) { if err := control.Tuple.Validate(); err != nil { return Control{}, err } + var fields map[string]json.RawMessage + if err := json.Unmarshal(data, &fields); err != nil { + return Control{}, fmt.Errorf("%w: control must be an object: %v", ErrProtocol, err) + } + if err := validateControlFields(control, fields); err != nil { + return Control{}, err + } return control, nil } diff --git a/pkg/udf/protocol/protocol_test.go b/pkg/udf/protocol/protocol_test.go index 97b5e08af9d3e..be08e92c386f7 100644 --- a/pkg/udf/protocol/protocol_test.go +++ b/pkg/udf/protocol/protocol_test.go @@ -69,9 +69,35 @@ func TestControlRejectsDuplicateJSONFields(t *testing.T) { require.ErrorContains(t, err, "duplicate control JSON field") } +func TestControlFieldsBelongToTheirKind(t *testing.T) { + wire, err := MarshalControl(Control{Kind: "InputBatch", Tuple: testTuple(), Sequence: 1}) + require.NoError(t, err) + var object map[string]any + require.NoError(t, json.Unmarshal(wire, &object)) + object["last_sequence"] = 1 + wire, err = json.Marshal(object) + require.NoError(t, err) + _, err = UnmarshalControl(wire) + require.ErrorIs(t, err, ErrProtocol) + require.ErrorContains(t, err, "not valid for control kind") + + _, err = MarshalControl(Control{Kind: "InputBatch", Tuple: testTuple(), Sequence: 1, Status: "ERROR"}) + require.ErrorIs(t, err, ErrProtocol) + require.ErrorContains(t, err, "not valid for control kind") + + _, err = UnmarshalControl([]byte(`{"version":1,"kind":"Unknown","tuple":{"account_id":1,"statement_id":"statement","group_id":"group","group_epoch":2,"invocation_id":"invocation","lease_epoch":3}}`)) + require.ErrorIs(t, err, ErrProtocol) + require.ErrorContains(t, err, "unsupported control kind") +} + func TestClosingControlCarriesZeroLastSequence(t *testing.T) { for _, kind := range []string{"EndInput", "Finish"} { - wire, err := MarshalControl(Control{Kind: kind, Tuple: testTuple()}) + control := Control{Kind: kind, Tuple: testTuple()} + if kind == "Finish" { + control.Status = "OK" + control.FinishID = "finish" + } + wire, err := MarshalControl(control) require.NoError(t, err) var object map[string]any require.NoError(t, json.Unmarshal(wire, &object)) @@ -96,7 +122,7 @@ func TestSystemAccountIsValidFencingIdentity(t *testing.T) { tuple := testTuple() tuple.AccountID = 0 require.NoError(t, tuple.Validate()) - _, err := MarshalControl(Control{Kind: "OpenInvocation", Tuple: tuple}) + _, err := MarshalControl(Control{Kind: "OpenInvocation", Tuple: tuple, Payload: []byte(`{}`)}) require.NoError(t, err) } diff --git a/pkg/udf/python/gateway_test.go b/pkg/udf/python/gateway_test.go index f103923374c6c..f4def0e6051a1 100644 --- a/pkg/udf/python/gateway_test.go +++ b/pkg/udf/python/gateway_test.go @@ -131,7 +131,9 @@ func TestGatewayFinishRejectsLateHalfStreamControls(t *testing.T) { require.NoError(t, sequence.AcknowledgeResults(1)) stream := &gatewayResultStream{results: []*flight.FlightData{ gatewayControl(t, kind, tuple, func(control *protocol.Control) { - control.Sequence = 1 + if kind == "InputConsumed" { + control.Sequence = 1 + } }), }} gateway := &Gateway{cfg: ClientConfig{RequestTimeout: time.Second}} diff --git a/pkg/udf/python/worker/test_worker.py b/pkg/udf/python/worker/test_worker.py index d036b9d93c5bd..cb7799d45be4b 100644 --- a/pkg/udf/python/worker/test_worker.py +++ b/pkg/udf/python/worker/test_worker.py @@ -72,9 +72,13 @@ def test_closing_control_preserves_zero_last_sequence(self): "invocation_id": "zero-sequence", "lease_epoch": 1, } - for kind in ("EndInput", "Finish"): + controls = { + "EndInput": {"last_sequence": 0}, + "Finish": {"last_sequence": 0, "finish_id": "finish", "status": "OK"}, + } + for kind, fields in controls.items(): wire = worker._encode_control( - {"kind": kind, "tuple": fence, "last_sequence": 0} + {"kind": kind, "tuple": fence, **fields} ) self.assertEqual(0, worker._decode_control(wire)["last_sequence"]) self.assertIn(b'"last_sequence":0', wire) @@ -763,6 +767,38 @@ def test_control_rejects_unknown_envelope_fields(self): with self.assertRaisesRegex(ValueError, "unsupported control field"): worker._encode_control(value) + def test_control_fields_belong_to_their_kind(self): + tuple_value = { + "account_id": 1, + "statement_id": "statement", + "group_id": "group", + "group_epoch": 1, + "invocation_id": "invocation", + "lease_epoch": 1, + } + input_batch = { + "version": worker.PROTOCOL_VERSION, + "kind": "InputBatch", + "tuple": tuple_value, + "sequence": 1, + } + with self.assertRaisesRegex(ValueError, "not valid for control kind"): + worker._encode_control({**input_batch, "last_sequence": 1}) + with self.assertRaisesRegex(ValueError, "not valid for control kind"): + worker._decode_control( + json.dumps({**input_batch, "last_sequence": 1}).encode() + ) + with self.assertRaisesRegex(ValueError, "unsupported control kind"): + worker._decode_control( + json.dumps( + { + "version": worker.PROTOCOL_VERSION, + "kind": "Unknown", + "tuple": tuple_value, + } + ).encode() + ) + def test_control_rejects_unknown_fencing_tuple_fields(self): tuple_value = { "account_id": 0, diff --git a/pkg/udf/python/worker/worker.py b/pkg/udf/python/worker/worker.py index 3bac6dcb30c29..8f0587be4eb66 100644 --- a/pkg/udf/python/worker/worker.py +++ b/pkg/udf/python/worker/worker.py @@ -63,6 +63,29 @@ "payload", } ) +_CONTROL_BASE_KEYS = frozenset({"version", "kind", "tuple"}) +_CONTROL_FIELD_RULES = { + "OpenInvocation": (frozenset({"payload"}), frozenset({"payload"})), + "InputBatch": (frozenset({"sequence"}), frozenset({"sequence"})), + "EndInput": (frozenset({"last_sequence"}), frozenset({"last_sequence"})), + "ResultSchema": (frozenset(), frozenset()), + "InputConsumed": (frozenset({"sequence"}), frozenset({"sequence"})), + "ResultBatch": (frozenset({"sequence"}), frozenset({"sequence"})), + "Finish": ( + frozenset({"last_sequence", "finish_id", "status"}), + frozenset({"last_sequence", "finish_id", "status"}), + ), + "AcknowledgeResults": ( + frozenset({"ack_sequence"}), + frozenset({"ack_sequence"}), + ), + "AcknowledgeFinish": (frozenset({"finish_id"}), frozenset({"finish_id"})), + "Ack": ( + frozenset({"ack_sequence", "finish_id", "status"}), + frozenset({"status"}), + ), + "Error": (frozenset({"status", "reason"}), frozenset({"status", "reason"})), +} _TUPLE_KEYS = frozenset( { "account_id", @@ -356,6 +379,7 @@ def _decode_control(data: bytes) -> Dict[str, Any]: if set(value) - _CONTROL_KEYS: raise ValueError("PROTOCOL: unsupported control field") _tuple_key(value.get("tuple") or {}) + _validate_control_fields(value, wire=True) return value @@ -399,12 +423,52 @@ def _required_uint64(value: Dict[str, Any], key: str, allow_zero: bool = False) return item +def _validate_control_fields(value: Dict[str, Any], wire: bool) -> None: + kind = value["kind"] + rule = _CONTROL_FIELD_RULES.get(kind) + if rule is None: + raise ValueError(f"PROTOCOL: unsupported control kind {kind!r}") + allowed, required = rule + fields = set(value) - _CONTROL_BASE_KEYS + invalid = fields - allowed + if invalid: + raise ValueError( + f"PROTOCOL: field {next(iter(invalid))!r} is not valid for control kind {kind!r}" + ) + if wire: + missing = required - set(value) + if missing: + raise ValueError( + f"PROTOCOL: control kind {kind!r} is missing field {next(iter(missing))!r}" + ) + if "sequence" in value: + _required_uint64(value, "sequence") + if "last_sequence" in value: + _required_uint64(value, "last_sequence", allow_zero=True) + if "ack_sequence" in value: + _required_uint64(value, "ack_sequence") + if "finish_id" in value: + _required_string(value, "finish_id") + if "status" in value: + _required_string(value, "status") + if "reason" in value: + _required_string(value, "reason") + if kind == "Ack": + if ("ack_sequence" in value) == ("finish_id" in value): + raise ValueError("PROTOCOL: Ack requires exactly one acknowledgement identity") + elif kind == "OpenInvocation" and ( + "payload" not in value or (wire and value["payload"] is None) + ): + raise ValueError("PROTOCOL: control kind 'OpenInvocation' is missing field 'payload'") + + def _encode_control(value: Dict[str, Any]) -> bytes: value = dict(value) if set(value) - _CONTROL_KEYS: raise ValueError("PROTOCOL: unsupported control field") value.setdefault("version", PROTOCOL_VERSION) _tuple_key(value["tuple"]) + _validate_control_fields(value, wire=False) data = json.dumps(value, separators=(",", ":"), ensure_ascii=True).encode("utf-8") if len(data) > MAX_CONTROL_BYTES: raise ValueError("PROTOCOL: control is too large") @@ -1193,29 +1257,22 @@ def do_action(self, context, action): finish_id = _required_string(control, "finish_id") if finish_id != terminal.finish_id: raise ValueError("PROTOCOL: terminal Finish ACK does not match the completed Finish") - yield _encode_control( - { - "kind": "Ack", - "tuple": control["tuple"], - "status": "OK", - "ack_sequence": control.get("ack_sequence", 0), - "finish_id": control.get("finish_id", ""), - } - ) + yield _encode_control(self._ack_control(control)) return if action.type == "AcknowledgeResults": state.ack_result(_required_uint64(control, "ack_sequence")) elif action.type == "AcknowledgeFinish": state.ack_finish(_required_string(control, "finish_id")) - yield _encode_control( - { - "kind": "Ack", - "tuple": control["tuple"], - "status": "OK", - "ack_sequence": control.get("ack_sequence", 0), - "finish_id": control.get("finish_id", ""), - } - ) + yield _encode_control(self._ack_control(control)) + + @staticmethod + def _ack_control(control: Dict[str, Any]) -> Dict[str, Any]: + value = {"kind": "Ack", "tuple": control["tuple"], "status": "OK"} + if control["kind"] == "AcknowledgeResults": + value["ack_sequence"] = control["ack_sequence"] + else: + value["finish_id"] = control["finish_id"] + return value def do_exchange(self, context, descriptor, reader, writer): state = None From d0d45c9b451c85820ff76a8993f4b635a5ef0604 Mon Sep 17 00:00:00 2001 From: iamlinjunhong <1030420200@qq.com> Date: Thu, 10 Sep 2026 14:31:50 +0800 Subject: [PATCH 043/202] fix(udf): preserve decimal64 signed range --- pkg/udf/python/arrow.go | 11 ++++++++++- pkg/udf/python/arrow_test.go | 16 ++++++++++++++++ 2 files changed, 26 insertions(+), 1 deletion(-) diff --git a/pkg/udf/python/arrow.go b/pkg/udf/python/arrow.go index 650db728280fb..a3f6fc4b4a4b8 100644 --- a/pkg/udf/python/arrow.go +++ b/pkg/udf/python/arrow.go @@ -904,7 +904,7 @@ func hasExactMicrosecondScale(value int64, scale int32) bool { } func decimalFromArray(value decimal128.Num, narrow bool) (any, error) { high, low := uint64(value.HighBits()), value.LowBits() - if narrow && high != 0 && high != math.MaxUint64 { + if narrow && !decimal128FitsDecimal64(high, low) { return nil, fmt.Errorf("TYPE_CONTRACT: Decimal128 result does not fit Decimal64") } if narrow { @@ -912,6 +912,15 @@ func decimalFromArray(value decimal128.Num, narrow bool) (any, error) { } return types.Decimal128{B0_63: low, B64_127: high}, nil } + +func decimal128FitsDecimal64(high, low uint64) bool { + // A signed 128-bit value fits in signed 64 bits only when the high word is + // a proper sign extension of the low word. Checking the high word alone + // would turn +2^63 into the negative Decimal64 minimum. + return (high == 0 && low <= math.MaxInt64) || + (high == math.MaxUint64 && low >= uint64(1)<<63) +} + func appendTemporalResult(input *array.Struct, typ types.T, index int, result vector.FunctionResultWrapper) error { if input.IsNull(index) { return appendTemporalNull(typ, result) diff --git a/pkg/udf/python/arrow_test.go b/pkg/udf/python/arrow_test.go index 35adcc63d9a3b..3d6ce2b6fc0ab 100644 --- a/pkg/udf/python/arrow_test.go +++ b/pkg/udf/python/arrow_test.go @@ -7,6 +7,7 @@ package python import ( "encoding/binary" + "math" "testing" "github.com/apache/arrow-go/v18/arrow" @@ -215,3 +216,18 @@ func TestAppendArrowResultIgnoresNullDecimalPayload(t *testing.T) { require.NoError(t, AppendArrowResult(descriptor, decimals, result, mp)) require.True(t, result.GetResultVector().IsNull(0)) } + +func TestDecimal128ToDecimal64ChecksSignedRange(t *testing.T) { + max, err := decimalFromArray(decimal128.New(0, math.MaxInt64), true) + require.NoError(t, err) + require.Equal(t, types.Decimal64(math.MaxInt64), max) + + min, err := decimalFromArray(decimal128.New(-1, uint64(1)<<63), true) + require.NoError(t, err) + require.Equal(t, types.Decimal64(uint64(1)<<63), min) + + _, err = decimalFromArray(decimal128.New(0, uint64(1)<<63), true) + require.ErrorContains(t, err, "does not fit Decimal64") + _, err = decimalFromArray(decimal128.New(-1, uint64(1)<<63-1), true) + require.ErrorContains(t, err, "does not fit Decimal64") +} From 4fed46ea2fd43bb825052b8ff35cd61be665fc3c Mon Sep 17 00:00:00 2001 From: iamlinjunhong <1030420200@qq.com> Date: Thu, 10 Sep 2026 14:32:20 +0800 Subject: [PATCH 044/202] fix(udf): reject nonstandard control JSON --- pkg/udf/python/worker/test_worker.py | 19 +++++++++++++++++++ pkg/udf/python/worker/worker.py | 10 +++++++++- 2 files changed, 28 insertions(+), 1 deletion(-) diff --git a/pkg/udf/python/worker/test_worker.py b/pkg/udf/python/worker/test_worker.py index cb7799d45be4b..a800f3ebe554e 100644 --- a/pkg/udf/python/worker/test_worker.py +++ b/pkg/udf/python/worker/test_worker.py @@ -799,6 +799,25 @@ def test_control_fields_belong_to_their_kind(self): ).encode() ) + def test_control_encoder_rejects_nonstandard_json_numbers(self): + tuple_value = { + "account_id": 1, + "statement_id": "statement", + "group_id": "group", + "group_epoch": 1, + "invocation_id": "invocation", + "lease_epoch": 1, + } + for number in (float("nan"), float("inf"), float("-inf")): + with self.assertRaisesRegex(ValueError, "invalid control JSON"): + worker._encode_control( + { + "kind": "OpenInvocation", + "tuple": tuple_value, + "payload": {"number": number}, + } + ) + def test_control_rejects_unknown_fencing_tuple_fields(self): tuple_value = { "account_id": 0, diff --git a/pkg/udf/python/worker/worker.py b/pkg/udf/python/worker/worker.py index 8f0587be4eb66..42b708184325c 100644 --- a/pkg/udf/python/worker/worker.py +++ b/pkg/udf/python/worker/worker.py @@ -469,7 +469,15 @@ def _encode_control(value: Dict[str, Any]) -> bytes: value.setdefault("version", PROTOCOL_VERSION) _tuple_key(value["tuple"]) _validate_control_fields(value, wire=False) - data = json.dumps(value, separators=(",", ":"), ensure_ascii=True).encode("utf-8") + try: + data = json.dumps( + value, + separators=(",", ":"), + ensure_ascii=True, + allow_nan=False, + ).encode("utf-8") + except (TypeError, ValueError, UnicodeError) as exc: + raise ValueError("PROTOCOL: invalid control JSON") from exc if len(data) > MAX_CONTROL_BYTES: raise ValueError("PROTOCOL: control is too large") return data From 7c06e1861e1a7c9b195f56d88ba008fb078b1bba Mon Sep 17 00:00:00 2001 From: iamlinjunhong <1030420200@qq.com> Date: Thu, 10 Sep 2026 14:32:56 +0800 Subject: [PATCH 045/202] fix(udf): preserve fencing tuple encoding --- pkg/udf/protocol/protocol.go | 4 ++++ pkg/udf/protocol/protocol_test.go | 8 ++++++++ pkg/udf/python/worker/test_worker.py | 12 ++++++++++++ pkg/udf/python/worker/worker.py | 5 +++++ 4 files changed, 29 insertions(+) diff --git a/pkg/udf/protocol/protocol.go b/pkg/udf/protocol/protocol.go index ce044aed64efa..3f81620765bcc 100644 --- a/pkg/udf/protocol/protocol.go +++ b/pkg/udf/protocol/protocol.go @@ -27,6 +27,7 @@ import ( "io" "sync" "time" + "unicode/utf8" ) const ( @@ -68,6 +69,9 @@ func (t FencingTuple) Validate() error { t.GroupEpoch == 0 || t.InvocationID == "" || t.LeaseEpoch == 0 { return fmt.Errorf("%w: incomplete fencing tuple", ErrProtocol) } + if !utf8.ValidString(t.StatementID) || !utf8.ValidString(t.GroupID) || !utf8.ValidString(t.InvocationID) { + return fmt.Errorf("%w: fencing tuple contains invalid UTF-8", ErrProtocol) + } return nil } diff --git a/pkg/udf/protocol/protocol_test.go b/pkg/udf/protocol/protocol_test.go index be08e92c386f7..4052a9fbf91a9 100644 --- a/pkg/udf/protocol/protocol_test.go +++ b/pkg/udf/protocol/protocol_test.go @@ -126,6 +126,14 @@ func TestSystemAccountIsValidFencingIdentity(t *testing.T) { require.NoError(t, err) } +func TestFencingTupleRejectsInvalidUTF8(t *testing.T) { + tuple := testTuple() + tuple.StatementID = string([]byte{0xff}) + require.ErrorContains(t, tuple.Validate(), "invalid UTF-8") + _, err := MarshalControl(Control{Kind: "OpenInvocation", Tuple: tuple, Payload: []byte(`{}`)}) + require.ErrorContains(t, err, "invalid UTF-8") +} + func TestSequenceKeepsHalfCloseIndependentFromResults(t *testing.T) { var sequence Sequence require.NoError(t, sequence.AcceptInput(1)) diff --git a/pkg/udf/python/worker/test_worker.py b/pkg/udf/python/worker/test_worker.py index a800f3ebe554e..260540c523f89 100644 --- a/pkg/udf/python/worker/test_worker.py +++ b/pkg/udf/python/worker/test_worker.py @@ -834,6 +834,18 @@ def test_control_rejects_unknown_fencing_tuple_fields(self): with self.assertRaisesRegex(ValueError, "unsupported fencing tuple field"): worker._decode_control(json.dumps(value).encode()) + def test_fencing_tuple_rejects_invalid_utf8(self): + tuple_value = { + "account_id": 1, + "statement_id": "\ud800", + "group_id": "group", + "group_epoch": 1, + "invocation_id": "invocation", + "lease_epoch": 1, + } + with self.assertRaisesRegex(ValueError, "invalid UTF-8"): + worker._tuple_key(tuple_value) + def test_control_rejects_duplicate_json_fields(self): wire = ( b'{"version":1,"kind":"InputBatch","tuple":{"account_id":1,' diff --git a/pkg/udf/python/worker/worker.py b/pkg/udf/python/worker/worker.py index 42b708184325c..7d573ef0cfcf1 100644 --- a/pkg/udf/python/worker/worker.py +++ b/pkg/udf/python/worker/worker.py @@ -321,6 +321,11 @@ def _tuple_key(value: Dict[str, Any]) -> tuple: string_fields = ("statement_id", "group_id", "invocation_id") if not isinstance(value, dict) or any(not isinstance(value.get(k), str) or not value[k] for k in string_fields): raise ValueError("PROTOCOL: incomplete fencing tuple") + try: + for key in string_fields: + value[key].encode("utf-8") + except UnicodeEncodeError as exc: + raise ValueError("PROTOCOL: fencing tuple contains invalid UTF-8") from exc account_id = value.get("account_id") if ( isinstance(account_id, bool) From c83fc6b583923833209c6e8f08580292e9491f2a Mon Sep 17 00:00:00 2001 From: iamlinjunhong <1030420200@qq.com> Date: Thu, 10 Sep 2026 14:33:44 +0800 Subject: [PATCH 046/202] fix(udf): validate IANA timezone identities --- pkg/udf/python/worker/test_worker.py | 27 +++++++++++++++++++++++++++ pkg/udf/python/worker/worker.py | 5 +++++ 2 files changed, 32 insertions(+) diff --git a/pkg/udf/python/worker/test_worker.py b/pkg/udf/python/worker/test_worker.py index 260540c523f89..4eec30c723577 100644 --- a/pkg/udf/python/worker/test_worker.py +++ b/pkg/udf/python/worker/test_worker.py @@ -645,6 +645,33 @@ def test_statement_context_is_whitelisted_and_frozen(self): } ) + def test_statement_context_rejects_unknown_iana_timezone(self): + with self.assertRaisesRegex(ValueError, "not present in the local tzdb"): + worker._statement_context( + { + "statement_timestamp_utc": "1704067200000000", + "session_timezone_kind": "IANA", + "session_timezone_name": "NoSuch/Zone", + "session_timezone_tzdb_version": "2025a", + "sql_mode": "[]", + "current_user": "alice", + "connection_collation": "utf8mb4_bin", + } + ) + + context = worker._statement_context( + { + "statement_timestamp_utc": "1704067200000000", + "session_timezone_kind": "IANA", + "session_timezone_name": "UTC", + "session_timezone_tzdb_version": "2025a", + "sql_mode": "[]", + "current_user": "alice", + "connection_collation": "utf8mb4_bin", + } + ) + self.assertEqual("UTC", context.session_timezone.name) + def test_terminal_admission_does_not_evict_live_tombstones(self): old_records = worker.MAX_LEDGER_ENTRIES old_bytes = worker.MAX_LEDGER_BYTES diff --git a/pkg/udf/python/worker/worker.py b/pkg/udf/python/worker/worker.py index 7d573ef0cfcf1..879a7f289c927 100644 --- a/pkg/udf/python/worker/worker.py +++ b/pkg/udf/python/worker/worker.py @@ -35,6 +35,7 @@ from collections import OrderedDict from dataclasses import dataclass from typing import Any, Dict, Iterable, Optional +from zoneinfo import ZoneInfo, ZoneInfoNotFoundError import pyarrow as pa import pyarrow.flight as flight @@ -276,6 +277,10 @@ def _statement_context(raw: Any) -> Optional[StatementContext]: tzdb_version = _required_context_value(raw, "session_timezone_tzdb_version") if "session_timezone_offset_minutes" in raw: raise ValueError("PROTOCOL: IANA timezone cannot carry a fixed offset") + try: + ZoneInfo(timezone_name) + except (ZoneInfoNotFoundError, ValueError) as exc: + raise ValueError("PROTOCOL: IANA timezone is not present in the local tzdb") from exc timezone = TimezoneContext("IANA", name=timezone_name, tzdb_version=tzdb_version) elif timezone_kind == "FIXED_OFFSET": offset_text = _required_context_value(raw, "session_timezone_offset_minutes") From 62fd4ba04bb0ecc858df8d3cceda5a7e675ba044 Mon Sep 17 00:00:00 2001 From: iamlinjunhong <1030420200@qq.com> Date: Thu, 10 Sep 2026 14:34:44 +0800 Subject: [PATCH 047/202] fix(udf): validate input Arrow values --- pkg/udf/python/worker/test_worker.py | 64 ++++++++++++++++++++++++++++ pkg/udf/python/worker/worker.py | 2 + 2 files changed, 66 insertions(+) diff --git a/pkg/udf/python/worker/test_worker.py b/pkg/udf/python/worker/test_worker.py index 4eec30c723577..6149b206bf6a6 100644 --- a/pkg/udf/python/worker/test_worker.py +++ b/pkg/udf/python/worker/test_worker.py @@ -506,6 +506,70 @@ def control(kind, **fields): finally: server.shutdown() + def test_exchange_rejects_invalid_input_values_before_handler(self): + server = worker.RoutineFlightServer("grpc://127.0.0.1:0") + fence = { + "account_id": 1, + "statement_id": "review", + "group_id": "group", + "group_epoch": 1, + "invocation_id": "invalid-input-values", + "lease_epoch": 1, + } + descriptor = {"type_id": worker.VECF32, "width": 2, "offset_width": 0} + child = pa.array([1.0, None], type=pa.float32()) + invalid_vector = pa.FixedSizeListArray.from_arrays(child, 2) + batch = pa.RecordBatch.from_arrays( + [invalid_vector], + schema=pa.schema([worker._field("arg_0", descriptor)]), + ) + payload = { + "mode": worker.MODE_VECTOR, + "null_policy": worker.NULL_CALL, + "abi_contract": worker.ABI_CONTRACT, + "adapter_version": worker.ADAPTER_VERSION, + "sdk_version": worker.SDK_VERSION, + "args": [descriptor], + "return": descriptor, + "source": "def f(ctx, x): return x", + "handler": "f", + "max_batch_bytes": 1 << 20, + "max_batch_rows": 1024, + "handler_timeout_seconds": 2, + } + + def control(kind, **fields): + return worker._encode_control(dict(kind=kind, tuple=fence, **fields)) + + chunks = iter( + [ + types.SimpleNamespace( + data=batch, + app_metadata=control("InputBatch", sequence=1), + ), + ] + ) + reader = types.SimpleNamespace(schema=batch.schema, read_chunk=lambda: next(chunks)) + writer = types.SimpleNamespace( + begin=lambda schema: None, + write_metadata=lambda data: None, + write_with_metadata=lambda record, data: None, + ) + command = control("OpenInvocation", payload=payload) + try: + handler = mock.Mock() + with mock.patch.object(worker, "_run_handler_process", handler): + with self.assertRaisesRegex(ValueError, "child validity"): + server.do_exchange( + types.SimpleNamespace(is_cancelled=lambda: False), + types.SimpleNamespace(command=command), + reader, + writer, + ) + handler.assert_not_called() + finally: + server.shutdown() + @unittest.skipUnless(os.name == "posix", "process-group test") def test_descendant_is_killed_after_handler_leader_exits(self): with tempfile.TemporaryDirectory(prefix="mo-udf-owned-child-") as artifact: diff --git a/pkg/udf/python/worker/worker.py b/pkg/udf/python/worker/worker.py index 879a7f289c927..cf8f5d7e77cb2 100644 --- a/pkg/udf/python/worker/worker.py +++ b/pkg/udf/python/worker/worker.py @@ -1364,6 +1364,8 @@ def do_exchange(self, context, descriptor, reader, writer): if batch.num_rows <= 0: raise ValueError("PROTOCOL: empty input batch") if batch.num_rows > max_batch_rows: raise ValueError("RESOURCE_EXHAUSTED: input batch has too many rows") if batch.nbytes > max_batch_bytes: raise ValueError("RESOURCE_EXHAUSTED: input batch exceeds byte limit") + for index, descriptor in enumerate(args): + _validate_array_values(batch.column(index), descriptor) execution_request = { "source": source, "handler": handler_name, From 5dcd1027949e8fa784f401345f31c839e2154ad0 Mon Sep 17 00:00:00 2001 From: iamlinjunhong <1030420200@qq.com> Date: Thu, 10 Sep 2026 14:36:49 +0800 Subject: [PATCH 048/202] fix(udf): reject empty constant inputs --- pkg/sql/plan/function/python_udf.go | 3 +++ pkg/udf/python/arrow.go | 2 +- pkg/udf/python/arrow_test.go | 14 ++++++++++++++ 3 files changed, 18 insertions(+), 1 deletion(-) diff --git a/pkg/sql/plan/function/python_udf.go b/pkg/sql/plan/function/python_udf.go index c47cb27cf2818..90b29bdf5cc44 100644 --- a/pkg/sql/plan/function/python_udf.go +++ b/pkg/sql/plan/function/python_udf.go @@ -265,6 +265,9 @@ func validatePythonInputVectors(inputs []*vector.Vector, args []types.Type, leng if input == nil { return fmt.Errorf("python udf: input vector %d is nil", index) } + if input.Length() == 0 { + return fmt.Errorf("python udf: input vector %d is empty", index) + } if !input.IsConst() && input.Length() < length { return fmt.Errorf("python udf: input vector %d is shorter than invocation length", index) } diff --git a/pkg/udf/python/arrow.go b/pkg/udf/python/arrow.go index a3f6fc4b4a4b8..66b47475ffddb 100644 --- a/pkg/udf/python/arrow.go +++ b/pkg/udf/python/arrow.go @@ -304,7 +304,7 @@ func BuildInputRecordRange(inputs []*vector.Vector, args []types.Type, start, le if err != nil { return nil, nil, err } - if inputs[i] == nil || (!inputs[i].IsConst() && inputs[i].Length() < start+length) { + if inputs[i] == nil || inputs[i].Length() == 0 || (!inputs[i].IsConst() && inputs[i].Length() < start+length) { return nil, nil, fmt.Errorf("input column %d is shorter than batch range", i) } } diff --git a/pkg/udf/python/arrow_test.go b/pkg/udf/python/arrow_test.go index 3d6ce2b6fc0ab..6b6b9797dedb1 100644 --- a/pkg/udf/python/arrow_test.go +++ b/pkg/udf/python/arrow_test.go @@ -129,6 +129,20 @@ func TestZeroArgumentRecordPreservesRows(t *testing.T) { require.Equal(t, int64(0), decoded.NumCols()) } +func TestBuildInputRecordRejectsEmptyConstantVector(t *testing.T) { + mp := mpool.MustNewZeroNoFixed() + input := vector.NewConstNull(types.T_int64.ToType(), 0, mp) + defer input.Free(mp) + + _, _, err := BuildInputRecordRange( + []*vector.Vector{input}, + []types.Type{types.T_int64.ToType()}, + 0, + 1, + ) + require.ErrorContains(t, err, "shorter than batch range") +} + func TestArrowValueDomainRejectsWidthAndTimeOverflow(t *testing.T) { allocator := memory.NewGoAllocator() stringBuilder := array.NewStringBuilder(allocator) From 339dbc9a753fd6fc303c9129d0c338ee106615f0 Mon Sep 17 00:00:00 2001 From: iamlinjunhong <1030420200@qq.com> Date: Thu, 10 Sep 2026 14:40:18 +0800 Subject: [PATCH 049/202] fix(udf): classify malformed worker frames --- pkg/udf/python/worker/test_worker.py | 14 ++++++++++++++ pkg/udf/python/worker/worker.py | 24 ++++++++++++++++++------ 2 files changed, 32 insertions(+), 6 deletions(-) diff --git a/pkg/udf/python/worker/test_worker.py b/pkg/udf/python/worker/test_worker.py index 6149b206bf6a6..26e1880f74a73 100644 --- a/pkg/udf/python/worker/test_worker.py +++ b/pkg/udf/python/worker/test_worker.py @@ -63,6 +63,20 @@ def test_type_descriptor_shape_is_strict(self): with self.assertRaisesRegex(ValueError, "offset_width must be an integer"): worker._field("result", {"type_id": worker.INT64, "offset_width": "32"}) + def test_malformed_control_is_reported_as_protocol_error(self): + with self.assertRaisesRegex(ValueError, "PROTOCOL: invalid control UTF-8"): + worker._decode_control(b"\xff") + with self.assertRaisesRegex(ValueError, "PROTOCOL: invalid control JSON"): + worker._decode_control(b"{") + self.assertEqual( + "PROTOCOL: invalid control JSON", + worker._safe_error(ValueError("PROTOCOL: invalid control JSON")), + ) + + def test_malformed_arrow_payload_is_reported_as_protocol_error(self): + with self.assertRaisesRegex(ValueError, "PROTOCOL: execution payload is not a valid Arrow stream"): + worker._deserialize_record_batch(b"not-an-arrow-stream") + def test_closing_control_preserves_zero_last_sequence(self): fence = { "account_id": 1, diff --git a/pkg/udf/python/worker/worker.py b/pkg/udf/python/worker/worker.py index cf8f5d7e77cb2..d2e3f08771c81 100644 --- a/pkg/udf/python/worker/worker.py +++ b/pkg/udf/python/worker/worker.py @@ -373,11 +373,20 @@ def _reject_nonstandard_json_constant(value): def _decode_control(data: bytes) -> Dict[str, Any]: if not data or len(data) > MAX_CONTROL_BYTES: raise ValueError("PROTOCOL: invalid control size") - value = json.loads( - bytes(data).decode("utf-8"), - object_pairs_hook=_reject_duplicate_json_pairs, - parse_constant=_reject_nonstandard_json_constant, - ) + try: + text = bytes(data).decode("utf-8") + except UnicodeDecodeError as exc: + raise ValueError("PROTOCOL: invalid control UTF-8") from exc + try: + value = json.loads( + text, + object_pairs_hook=_reject_duplicate_json_pairs, + parse_constant=_reject_nonstandard_json_constant, + ) + except json.JSONDecodeError as exc: + raise ValueError("PROTOCOL: invalid control JSON") from exc + except RecursionError as exc: + raise ValueError("PROTOCOL: control JSON is too deeply nested") from exc if ( not isinstance(value, dict) or type(value.get("version")) is not int @@ -881,7 +890,10 @@ def _serialize_record_batch(record: pa.RecordBatch) -> bytes: def _deserialize_record_batch(data: bytes) -> pa.RecordBatch: if not isinstance(data, (bytes, bytearray)) or not data: raise ValueError("PROTOCOL: execution payload is missing an Arrow batch") - reader = pa.ipc.open_stream(pa.py_buffer(data)) + try: + reader = pa.ipc.open_stream(pa.py_buffer(data)) + except pa.ArrowException as exc: + raise ValueError("PROTOCOL: execution payload is not a valid Arrow stream") from exc try: try: record = reader.read_next_batch() From 24b5e26fa24b044880f744ad5be9709a94f61bda Mon Sep 17 00:00:00 2001 From: iamlinjunhong <1030420200@qq.com> Date: Thu, 10 Sep 2026 14:44:03 +0800 Subject: [PATCH 050/202] fix(udf): enforce descriptor value domains --- pkg/udf/python/arrow.go | 80 ++++++++++++++++++++++++++++ pkg/udf/python/arrow_test.go | 13 +++++ pkg/udf/python/worker/test_worker.py | 11 ++++ pkg/udf/python/worker/worker.py | 57 ++++++++++++++++++++ 4 files changed, 161 insertions(+) diff --git a/pkg/udf/python/arrow.go b/pkg/udf/python/arrow.go index 66b47475ffddb..7d4b14dc8a08a 100644 --- a/pkg/udf/python/arrow.go +++ b/pkg/udf/python/arrow.go @@ -91,6 +91,9 @@ func (d TypeDescriptor) Type() types.Type { } func (d TypeDescriptor) canonical() ([]byte, error) { return json.Marshal(d) } func (d TypeDescriptor) Fingerprint() (string, error) { + if err := d.Validate(); err != nil { + return "", err + } canonical, err := d.canonical() if err != nil { return "", err @@ -114,6 +117,83 @@ func (d TypeDescriptor) Fingerprint() (string, error) { return hex.EncodeToString(h.Sum(nil)), nil } +// Validate checks the semantic fields that Arrow's physical type does not +// carry. Keeping this at the descriptor boundary makes manually constructed +// catalog or wire descriptors obey the same contract as descriptors produced +// from a SQL type. +func (d TypeDescriptor) Validate() error { + oid := types.T(d.TypeID) + if d.Width < 0 { + return fmt.Errorf("TYPE_CONTRACT: descriptor width must be non-negative") + } + if d.Scale < 0 { + return fmt.Errorf("TYPE_CONTRACT: descriptor scale must be non-negative") + } + expectedOffsetWidth := int32(32) + if oid == types.T_uuid || oid == types.T_array_float32 || oid == types.T_array_float64 { + expectedOffsetWidth = 0 + } + if d.OffsetWidth != expectedOffsetWidth { + return fmt.Errorf("TYPE_CONTRACT: descriptor offset_width must be %d for %s", expectedOffsetWidth, oid.String()) + } + if d.JSONEncoding != "" && (oid != types.T_json || d.JSONEncoding != "canonical_text") { + return fmt.Errorf("TYPE_CONTRACT: unsupported JSON encoding") + } + if d.TemporalEncoding != "" && (oid != types.T_date && oid != types.T_datetime && oid != types.T_timestamp || d.TemporalEncoding != "sql_zero_struct") { + return fmt.Errorf("TYPE_CONTRACT: unsupported temporal encoding") + } + + switch oid { + case types.T_bool, types.T_int8, types.T_int16, types.T_int32, types.T_int64, + types.T_uint8, types.T_uint16, types.T_uint32, types.T_uint64, + types.T_float32, types.T_float64, types.T_json: + if d.Width != 0 || d.Scale != 0 || d.Charset != types.CharsetLegacy { + return fmt.Errorf("TYPE_CONTRACT: descriptor carries unused fields for %s", oid.String()) + } + case types.T_decimal64, types.T_decimal128: + maxPrecision := int32(38) + if oid == types.T_decimal64 { + maxPrecision = 18 + } + if d.Width < 1 || d.Width > maxPrecision { + return fmt.Errorf("TYPE_CONTRACT: decimal precision %d is outside %s range", d.Width, oid.String()) + } + if d.Scale > d.Width { + return fmt.Errorf("TYPE_CONTRACT: decimal scale %d exceeds precision %d", d.Scale, d.Width) + } + if d.Charset != types.CharsetLegacy { + return fmt.Errorf("TYPE_CONTRACT: decimal descriptor carries a charset") + } + case types.T_date: + if d.Scale != 0 || d.Charset != types.CharsetLegacy { + return fmt.Errorf("TYPE_CONTRACT: DATE descriptor has invalid scale or charset") + } + case types.T_time, types.T_datetime, types.T_timestamp: + if d.Scale > 6 || d.Charset != types.CharsetLegacy { + return fmt.Errorf("TYPE_CONTRACT: temporal scale or charset is outside the supported range") + } + case types.T_char, types.T_varchar, types.T_text: + if d.Charset > types.CharsetUTF8 { + return fmt.Errorf("TYPE_CONTRACT: unsupported text charset %d", d.Charset) + } + case types.T_binary, types.T_varbinary, types.T_blob: + if d.Charset != types.CharsetBinary { + return fmt.Errorf("TYPE_CONTRACT: binary descriptor must use the binary charset") + } + case types.T_uuid: + if d.Width != 0 || d.Scale != 0 || d.Charset != types.CharsetLegacy { + return fmt.Errorf("TYPE_CONTRACT: UUID descriptor carries unused fields") + } + case types.T_array_float32, types.T_array_float64: + if d.Width < 1 || d.Width > types.MaxArrayDimension || d.Scale != 0 || d.Charset != types.CharsetLegacy { + return fmt.Errorf("TYPE_CONTRACT: vector dimension or metadata is outside the supported range") + } + default: + return fmt.Errorf("TYPE_CONTRACT: unsupported MatrixOne type id %d", d.TypeID) + } + return nil +} + func (d TypeDescriptor) arrowType() (arrow.DataType, error) { switch types.T(d.TypeID) { case types.T_bool: diff --git a/pkg/udf/python/arrow_test.go b/pkg/udf/python/arrow_test.go index 6b6b9797dedb1..c4b9c5b01570d 100644 --- a/pkg/udf/python/arrow_test.go +++ b/pkg/udf/python/arrow_test.go @@ -178,6 +178,19 @@ func TestArrowTypeValidationIncludesFixedBinaryWidth(t *testing.T) { require.ErrorContains(t, descriptor.ValidateField(field), "does not match") } +func TestTypeDescriptorRejectsNonCanonicalDomain(t *testing.T) { + invalid := []TypeDescriptor{ + {TypeID: int32(types.T_varchar), Width: -1, OffsetWidth: 32}, + {TypeID: int32(types.T_int64), OffsetWidth: 64}, + {TypeID: int32(types.T_decimal64), Width: 3, Scale: 4, OffsetWidth: 32}, + {TypeID: int32(types.T_array_float32), Width: types.MaxArrayDimension + 1}, + } + for _, descriptor := range invalid { + _, err := descriptor.Field("value") + require.Error(t, err, "%+v", descriptor) + } +} + func TestArrowValueDomainRejectsDecimalOverflowAndNullableVectorChild(t *testing.T) { decimalType := &arrow.Decimal128Type{Precision: 3, Scale: 0} decimalBuilder := array.NewDecimal128Builder(memory.NewGoAllocator(), decimalType) diff --git a/pkg/udf/python/worker/test_worker.py b/pkg/udf/python/worker/test_worker.py index 26e1880f74a73..8598ee6e5497e 100644 --- a/pkg/udf/python/worker/test_worker.py +++ b/pkg/udf/python/worker/test_worker.py @@ -77,6 +77,17 @@ def test_malformed_arrow_payload_is_reported_as_protocol_error(self): with self.assertRaisesRegex(ValueError, "PROTOCOL: execution payload is not a valid Arrow stream"): worker._deserialize_record_batch(b"not-an-arrow-stream") + def test_descriptor_domain_is_strict(self): + invalid = [ + {"type_id": worker.VARCHAR, "width": -1, "offset_width": 32}, + {"type_id": worker.INT64, "offset_width": 64}, + {"type_id": worker.DECIMAL64, "width": 3, "scale": 4, "offset_width": 32}, + {"type_id": worker.VECF32, "width": 65536, "offset_width": 0}, + ] + for descriptor in invalid: + with self.assertRaisesRegex(ValueError, "TYPE_CONTRACT"): + worker._field("value", descriptor) + def test_closing_control_preserves_zero_last_sequence(self): fence = { "account_id": 1, diff --git a/pkg/udf/python/worker/worker.py b/pkg/udf/python/worker/worker.py index d2e3f08771c81..13643c85bc8e1 100644 --- a/pkg/udf/python/worker/worker.py +++ b/pkg/udf/python/worker/worker.py @@ -516,6 +516,7 @@ def _canonical_descriptor(descriptor: Dict[str, Any]) -> bytes: for key in _DESCRIPTOR_TEXT_KEYS: if key in descriptor and type(descriptor[key]) is not str: raise ValueError(f"TYPE_CONTRACT: descriptor {key} must be text") + _validate_descriptor_domain(descriptor) order = ("type_id", "width", "scale", "charset", "offset_width", "json_encoding", "temporal_encoding") result = {} for key in order: @@ -525,6 +526,62 @@ def _canonical_descriptor(descriptor: Dict[str, Any]) -> bytes: return json.dumps(result, separators=(",", ":"), ensure_ascii=True).encode("utf-8") +def _validate_descriptor_domain(descriptor: Dict[str, Any]) -> None: + type_id = int(descriptor["type_id"]) + width = int(descriptor.get("width") or 0) + scale = int(descriptor.get("scale") or 0) + charset = int(descriptor.get("charset") or 0) + expected_offset_width = 0 if type_id in (UUID, VECF32, VECF64) else 32 + if width < 0: + raise ValueError("TYPE_CONTRACT: descriptor width must be non-negative") + if scale < 0: + raise ValueError("TYPE_CONTRACT: descriptor scale must be non-negative") + if int(descriptor.get("offset_width") or expected_offset_width) != expected_offset_width: + raise ValueError( + f"TYPE_CONTRACT: descriptor offset_width must be {expected_offset_width}" + ) + json_encoding = descriptor.get("json_encoding") + if json_encoding not in (None, "") and (type_id != JSON or json_encoding != "canonical_text"): + raise ValueError("TYPE_CONTRACT: unsupported JSON encoding") + temporal_encoding = descriptor.get("temporal_encoding") + if temporal_encoding not in (None, "") and ( + type_id not in (DATE, DATETIME, TIMESTAMP) or temporal_encoding != "sql_zero_struct" + ): + raise ValueError("TYPE_CONTRACT: unsupported temporal encoding") + + if type_id in (BOOL, INT8, INT16, INT32, INT64, UINT8, UINT16, UINT32, UINT64, JSON): + if width or scale or charset: + raise ValueError("TYPE_CONTRACT: descriptor carries unused fields") + elif type_id in (DECIMAL64, DECIMAL128): + maximum = 18 if type_id == DECIMAL64 else 38 + if width < 1 or width > maximum: + raise ValueError("TYPE_CONTRACT: decimal precision is outside the supported range") + if scale > width: + raise ValueError("TYPE_CONTRACT: decimal scale exceeds precision") + if charset: + raise ValueError("TYPE_CONTRACT: decimal descriptor carries a charset") + elif type_id == DATE: + if scale or charset: + raise ValueError("TYPE_CONTRACT: DATE descriptor has invalid scale or charset") + elif type_id in (TIME, DATETIME, TIMESTAMP): + if scale > 6 or charset: + raise ValueError("TYPE_CONTRACT: temporal scale or charset is outside the supported range") + elif type_id in (CHAR, VARCHAR, TEXT): + if charset > 3: + raise ValueError("TYPE_CONTRACT: unsupported text charset") + elif type_id in (BINARY, VARBINARY, BLOB): + if charset != 1: + raise ValueError("TYPE_CONTRACT: binary descriptor must use the binary charset") + elif type_id == UUID: + if width or scale or charset: + raise ValueError("TYPE_CONTRACT: UUID descriptor carries unused fields") + elif type_id in (VECF32, VECF64): + if width < 1 or width > 65535 or scale or charset: + raise ValueError("TYPE_CONTRACT: vector dimension or metadata is outside the supported range") + else: + raise ValueError(f"TYPE_CONTRACT: unsupported type id {type_id}") + + def _physical_fingerprint(descriptor: Dict[str, Any]) -> str: type_id = int(descriptor["type_id"]) primitive = { From 6089a816129cae2b5728953dcc814b582240fe86 Mon Sep 17 00:00:00 2001 From: iamlinjunhong <1030420200@qq.com> Date: Thu, 10 Sep 2026 14:45:07 +0800 Subject: [PATCH 051/202] fix(udf): reject invalid control UTF-8 --- pkg/udf/protocol/protocol.go | 7 +++++++ pkg/udf/protocol/protocol_test.go | 9 +++++++++ 2 files changed, 16 insertions(+) diff --git a/pkg/udf/protocol/protocol.go b/pkg/udf/protocol/protocol.go index 3f81620765bcc..61bce709bbee7 100644 --- a/pkg/udf/protocol/protocol.go +++ b/pkg/udf/protocol/protocol.go @@ -263,6 +263,13 @@ func UnmarshalControl(data []byte) (Control, error) { if len(data) == 0 || len(data) > MaxControlBytes { return Control{}, fmt.Errorf("%w: control size %d is outside the allowed range", ErrProtocol, len(data)) } + // encoding/json replaces invalid UTF-8 in JSON strings with U+FFFD. A + // fencing tuple is an identity, so accepting that replacement would make a + // malformed wire tuple refer to a different invocation than the sender + // intended. Reject the bytes before any JSON decoder can normalize them. + if !utf8.Valid(data) { + return Control{}, fmt.Errorf("%w: control contains invalid UTF-8", ErrProtocol) + } if err := rejectDuplicateJSONKeys(data); err != nil { return Control{}, fmt.Errorf("%w: %v", ErrProtocol, err) } diff --git a/pkg/udf/protocol/protocol_test.go b/pkg/udf/protocol/protocol_test.go index 4052a9fbf91a9..f22aaa2d3073e 100644 --- a/pkg/udf/protocol/protocol_test.go +++ b/pkg/udf/protocol/protocol_test.go @@ -134,6 +134,15 @@ func TestFencingTupleRejectsInvalidUTF8(t *testing.T) { require.ErrorContains(t, err, "invalid UTF-8") } +func TestUnmarshalControlRejectsInvalidWireUTF8(t *testing.T) { + wire := []byte(`{"version":1,"kind":"InputBatch","tuple":{"account_id":1,"statement_id":"`) + wire = append(wire, 0xff) + wire = append(wire, []byte(`","group_id":"group","group_epoch":2,"invocation_id":"invocation","lease_epoch":3},"sequence":1}`)...) + _, err := UnmarshalControl(wire) + require.ErrorIs(t, err, ErrProtocol) + require.ErrorContains(t, err, "invalid UTF-8") +} + func TestSequenceKeepsHalfCloseIndependentFromResults(t *testing.T) { var sequence Sequence require.NoError(t, sequence.AcceptInput(1)) From 765853b43ad85c911b3d9bbeacc4e65acd978f99 Mon Sep 17 00:00:00 2001 From: iamlinjunhong <1030420200@qq.com> Date: Thu, 10 Sep 2026 14:49:23 +0800 Subject: [PATCH 052/202] fix(udf): validate every Python input batch --- pkg/udf/python/worker/test_worker.py | 12 ++++++++++++ pkg/udf/python/worker/worker.py | 14 +++++++++++++- 2 files changed, 25 insertions(+), 1 deletion(-) diff --git a/pkg/udf/python/worker/test_worker.py b/pkg/udf/python/worker/test_worker.py index 8598ee6e5497e..91a2001b97284 100644 --- a/pkg/udf/python/worker/test_worker.py +++ b/pkg/udf/python/worker/test_worker.py @@ -88,6 +88,18 @@ def test_descriptor_domain_is_strict(self): with self.assertRaisesRegex(ValueError, "TYPE_CONTRACT"): worker._field("value", descriptor) + def test_every_input_batch_keeps_the_frozen_schema(self): + descriptor = {"type_id": worker.INT64, "offset_width": 32} + valid_schema = pa.schema([worker._field("arg_0", descriptor)]) + invalid_batch = pa.RecordBatch.from_arrays( + [pa.array([1], type=pa.int32())], + schema=pa.schema( + [pa.field("arg_0", pa.int32(), nullable=True, metadata=valid_schema.field(0).metadata)] + ), + ) + with self.assertRaisesRegex(ValueError, "Arrow type"): + worker._validate_input_batch_schema(invalid_batch, valid_schema, [descriptor]) + def test_closing_control_preserves_zero_last_sequence(self): fence = { "account_id": 1, diff --git a/pkg/udf/python/worker/worker.py b/pkg/udf/python/worker/worker.py index 13643c85bc8e1..1d526313ef063 100644 --- a/pkg/udf/python/worker/worker.py +++ b/pkg/udf/python/worker/worker.py @@ -683,6 +683,17 @@ def _validate_schema(schema: pa.Schema, descriptors: Iterable[Dict[str, Any]]) - _validate_field(schema.field(index), f"arg_{index}", descriptor) +def _validate_input_batch_schema( + batch: pa.RecordBatch, expected_schema: pa.Schema, descriptors: Iterable[Dict[str, Any]] +) -> None: + # Flight carries one stream schema, but each received RecordBatch still + # owns a schema object. Validate every batch so a later frame cannot + # replace a fixed-width Arrow type after the first frame was admitted. + _validate_schema(batch.schema, descriptors) + if batch.schema != expected_schema: + raise ValueError("TYPE_CONTRACT: input batch schema changed") + + def _load_handler(source: str, handler: str): namespace: Dict[str, Any] = {"__name__": "__matrixone_routine__"} exec(compile(source, "", "exec"), namespace, namespace) @@ -1424,12 +1435,13 @@ def do_exchange(self, context, descriptor, reader, writer): if schema is None: schema = reader.schema _validate_schema(schema, args) + batch = chunk.data + _validate_input_batch_schema(batch, schema, args) control = _decode_control(chunk.app_metadata) _require_tuple(control["tuple"], open_control["tuple"]) if control["kind"] != "InputBatch": raise ValueError("PROTOCOL: data batch is missing InputBatch") sequence = _required_uint64(control, "sequence") if sequence != state.last_input + 1: raise ValueError("PROTOCOL: input sequence is not contiguous") - batch = chunk.data if batch.num_rows <= 0: raise ValueError("PROTOCOL: empty input batch") if batch.num_rows > max_batch_rows: raise ValueError("RESOURCE_EXHAUSTED: input batch has too many rows") if batch.nbytes > max_batch_bytes: raise ValueError("RESOURCE_EXHAUSTED: input batch exceeds byte limit") From 374a77996362d04a2e0499df751280b37fdd1faa Mon Sep 17 00:00:00 2001 From: iamlinjunhong <1030420200@qq.com> Date: Fri, 11 Sep 2026 17:58:24 +0800 Subject: [PATCH 053/202] feat(udf): implement long-term Python UDF execution --- cmd/mo-service/main.go | 25 +- etc/launch-tae-compose/config/cn-0.toml | 2 + etc/launch-tae-compose/config/cn-1.toml | 2 + etc/launch-with-python-udf-worker/cn.toml | 2 + optools/run_bvt.sh | 39 +- .../versions/v4_0_7/tenant_upgrade_list.go | 140 +- pkg/bootstrap/versions/v4_0_7/upgrade_test.go | 105 + pkg/cnservice/server.go | 6 +- pkg/cnservice/types.go | 3 + pkg/cnservice/types_test.go | 26 + pkg/frontend/authenticate.go | 886 ++- pkg/frontend/authenticate_test.go | 24 + pkg/frontend/clone.go | 17 + pkg/frontend/clone_database_source.go | 44 +- pkg/frontend/clone_database_source_test.go | 45 +- pkg/frontend/clone_test.go | 26 +- pkg/frontend/compiler_context.go | 490 +- pkg/frontend/computation_wrapper.go | 5 + pkg/frontend/mysql_cmd_executor.go | 13 +- pkg/frontend/pitr.go | 15 + pkg/frontend/predefined.go | 34 +- pkg/frontend/python_udf_catalog_test.go | 945 +++ pkg/frontend/routine_plan_validation.go | 509 ++ pkg/frontend/routine_plan_validation_test.go | 304 + pkg/frontend/snapshot.go | 16 +- pkg/frontend/snapshot_catalog_restore.go | 405 +- pkg/frontend/snapshot_restore_with_ts.go | 11 + pkg/frontend/snapshot_test.go | 121 +- pkg/pb/plan/plan.pb.go | 5948 +++++++++++++---- pkg/sql/colexec/evalExpression.go | 59 +- pkg/sql/colexec/external_routine_eval.go | 704 ++ pkg/sql/colexec/external_routine_eval_test.go | 368 + pkg/sql/plan/base_binder.go | 90 +- pkg/sql/plan/deepcopy.go | 11 + pkg/sql/plan/deepcopy_test.go | 19 + pkg/sql/plan/expr_hash.go | 29 + pkg/sql/plan/expr_hash_test.go | 36 + pkg/sql/plan/function/list_builtIn.go | 2 +- pkg/sql/plan/function/python_udf.go | 273 +- pkg/sql/plan/function/udf.go | 379 +- pkg/sql/plan/function/udf_test.go | 320 +- pkg/sql/plan/returning.go | 3 + pkg/sql/plan/routine_dependency.go | 105 + pkg/sql/plan/routine_dependency_test.go | 120 + pkg/sql/plan/rule/constant_fold.go | 9 + pkg/sql/plan/rule/constant_fold_test.go | 23 + pkg/sql/plan/types.go | 20 +- pkg/sql/plan/utils.go | 5 + pkg/udf/protocol/protocol.go | 185 +- pkg/udf/protocol/protocol_test.go | 83 +- pkg/udf/python/arrow.go | 55 +- pkg/udf/python/arrow_test.go | 273 + pkg/udf/python/artifact.go | 190 + pkg/udf/python/artifact_test.go | 97 + pkg/udf/python/config.go | 50 +- pkg/udf/python/config_test.go | 25 + pkg/udf/python/definition.go | 78 + pkg/udf/python/definition_test.go | 51 + pkg/udf/python/gateway.go | 968 ++- pkg/udf/python/gateway_admission_test.go | 200 + pkg/udf/python/gateway_benchmark_test.go | 118 + pkg/udf/python/gateway_integration_test.go | 631 ++ pkg/udf/python/gateway_test.go | 304 +- pkg/udf/python/supervisor.go | 41 +- pkg/udf/python/supervisor_test.go | 60 +- pkg/udf/python/testdata/type_descriptors.json | 29 + pkg/udf/python/worker/test_worker.py | 2156 +++++- pkg/udf/python/worker/worker.py | 1897 +++++- pkg/udf/python_contract.go | 86 + pkg/udf/python_contract_test.go | 28 + pkg/udf/runtime.go | 224 +- pkg/udf/runtime_test.go | 70 + pkg/udf/timezone.go | 47 + proto/plan.proto | 90 + test/distributed/cases/udf_python/README.md | 8 + .../cases/udf_python/dml_error_paths.result | 30 + .../cases/udf_python/dml_error_paths.sql | 31 + .../cases/udf_python/dml_positions.result | 17 + .../cases/udf_python/dml_positions.sql | 25 + .../udf_python/relational_positions.result | 59 + .../cases/udf_python/relational_positions.sql | 54 + .../udf_python/revision_lifecycle.result | 35 + .../cases/udf_python/revision_lifecycle.sql | 20 + 83 files changed, 18870 insertions(+), 2228 deletions(-) create mode 100644 pkg/bootstrap/versions/v4_0_7/upgrade_test.go create mode 100644 pkg/frontend/python_udf_catalog_test.go create mode 100644 pkg/frontend/routine_plan_validation.go create mode 100644 pkg/frontend/routine_plan_validation_test.go create mode 100644 pkg/sql/colexec/external_routine_eval.go create mode 100644 pkg/sql/colexec/external_routine_eval_test.go create mode 100644 pkg/sql/plan/routine_dependency.go create mode 100644 pkg/sql/plan/routine_dependency_test.go create mode 100644 pkg/udf/python/artifact.go create mode 100644 pkg/udf/python/artifact_test.go create mode 100644 pkg/udf/python/definition.go create mode 100644 pkg/udf/python/definition_test.go create mode 100644 pkg/udf/python/gateway_admission_test.go create mode 100644 pkg/udf/python/gateway_benchmark_test.go create mode 100644 pkg/udf/python/gateway_integration_test.go create mode 100644 pkg/udf/python/testdata/type_descriptors.json create mode 100644 pkg/udf/python_contract.go create mode 100644 pkg/udf/python_contract_test.go create mode 100644 pkg/udf/timezone.go create mode 100644 test/distributed/cases/udf_python/dml_error_paths.result create mode 100644 test/distributed/cases/udf_python/dml_error_paths.sql create mode 100644 test/distributed/cases/udf_python/dml_positions.result create mode 100644 test/distributed/cases/udf_python/dml_positions.sql create mode 100644 test/distributed/cases/udf_python/relational_positions.result create mode 100644 test/distributed/cases/udf_python/relational_positions.sql create mode 100644 test/distributed/cases/udf_python/revision_lifecycle.result create mode 100644 test/distributed/cases/udf_python/revision_lifecycle.sql diff --git a/cmd/mo-service/main.go b/cmd/mo-service/main.go index 15c4fcb0e68ca..e62b681634136 100644 --- a/cmd/mo-service/main.go +++ b/cmd/mo-service/main.go @@ -764,10 +764,27 @@ func startPythonUdfWorker(cfg *Config, stopper *stopper.Stopper) error { if err := s.Start(); err != nil { panic(err) } - <-roleCtx.Done() - if err := s.Close(); err != nil { - closeErr = err - logutil.GetGlobalLogger().Error("failed to close Python UDF worker", zap.Error(err)) + workerDone := s.Done() + select { + case <-roleCtx.Done(): + if err := s.Close(); err != nil { + closeErr = err + logutil.GetGlobalLogger().Error("failed to close Python UDF worker", zap.Error(err)) + } + case <-workerDone: + // A worker exit while the role is still running leaves CN with a + // configured but unusable Python runtime. Surface it as a fatal + // service event so the existing lifecycle supervisor drains CN + // before the worker dependency and does not accept partial service + // availability. A zero exit is still unexpected here. + if roleCtx.Err() == nil { + workerErr := s.Err() + if workerErr == nil { + workerErr = errors.New("process exited without an error") + } + closeErr = fmt.Errorf("Python UDF worker exited unexpectedly: %w", workerErr) + serviceLifecycle.notifyFatal(closeErr) + } } }) if err != nil { diff --git a/etc/launch-tae-compose/config/cn-0.toml b/etc/launch-tae-compose/config/cn-0.toml index d75de7326e2f2..0ea0b00c0449d 100644 --- a/etc/launch-tae-compose/config/cn-0.toml +++ b/etc/launch-tae-compose/config/cn-0.toml @@ -71,3 +71,5 @@ enable-maintenance = true enabled = true allow-unisolated = true server-address = "python-udf-worker-cn0:50051" +max-invocation-rows = 1048576 +max-invocation-result-bytes = 268435456 diff --git a/etc/launch-tae-compose/config/cn-1.toml b/etc/launch-tae-compose/config/cn-1.toml index 479e8784e4c1d..372d105e7869b 100644 --- a/etc/launch-tae-compose/config/cn-1.toml +++ b/etc/launch-tae-compose/config/cn-1.toml @@ -71,3 +71,5 @@ enable-maintenance = true enabled = true allow-unisolated = true server-address = "python-udf-worker-cn1:50051" +max-invocation-rows = 1048576 +max-invocation-result-bytes = 268435456 diff --git a/etc/launch-with-python-udf-worker/cn.toml b/etc/launch-with-python-udf-worker/cn.toml index cf5b576cbc864..76ae8790bf7b5 100644 --- a/etc/launch-with-python-udf-worker/cn.toml +++ b/etc/launch-with-python-udf-worker/cn.toml @@ -12,3 +12,5 @@ port-base = 18000 enabled = true allow-unisolated = true server-address = "127.0.0.1:50051" +max-invocation-rows = 1048576 +max-invocation-result-bytes = 268435456 diff --git a/optools/run_bvt.sh b/optools/run_bvt.sh index 0929047397241..ed1d3f19f8cf5 100755 --- a/optools/run_bvt.sh +++ b/optools/run_bvt.sh @@ -19,17 +19,20 @@ set -o nounset MO_WORKSPACE=$1 LAUNCH=$2 PROXY=${3:-} +LAUNCH_CONFIG=$LAUNCH +MO_PID="" function launch_mo() { cd $MO_WORKSPACE # Ordinary launch BVT remains the single CI entry point, but its Python # cases opt into the worker-enabled manifest explicitly. The generic # etc/launch manifest stays safe for users who start mo-service directly. - local launch_config=$LAUNCH + LAUNCH_CONFIG=$LAUNCH if [[ "$LAUNCH" == "launch" ]]; then - launch_config=launch-with-python-udf-worker + LAUNCH_CONFIG=launch-with-python-udf-worker fi - ./mo-service -debug-http=:12345 -launch ./etc/$launch_config/launch.toml $PROXY &>mo-service.log & + ./mo-service -debug-http=:12345 -launch ./etc/$LAUNCH_CONFIG/launch.toml $PROXY &>mo-service.log & + MO_PID=$! } # this will wait mo all system init completed @@ -45,6 +48,32 @@ function wait_system_init() { return 1 } +# MySQL readiness only proves that the CN frontend accepted connections. The +# worker is a separately started service, so Python UDF DDL/execution can still +# race its Flight listener. Keep this in the existing BVT launcher instead of +# adding a second CI job or making every SQL case retry its first call. +function wait_python_udf_worker() { + if [[ "$LAUNCH_CONFIG" != "launch-with-python-udf-worker" ]]; then + return 0 + fi + for num in {1..120} + do + if [[ -n "$MO_PID" ]] && ! kill -0 "$MO_PID" 2>/dev/null; then + echo "MatrixOne exited before Python UDF worker became ready" >&2 + tail -n 160 mo-service.log >&2 || true + return 1 + fi + if bash -c 'exec 3<>/dev/tcp/127.0.0.1/50051' 2>/dev/null; then + echo "Python UDF worker is ready, cost $num seconds" + return 0 + fi + sleep 1 + done + echo "Python UDF worker did not become ready on 127.0.0.1:50051" >&2 + tail -n 160 mo-service.log >&2 || true + return 1 +} + # Start the jstfu datastream server (xtool/jstfu) that # test/distributed/cases/datastream talks to on 127.0.0.1:4444. In this # launch deployment MO runs on the host, so a host jstfu is reachable from @@ -85,4 +114,8 @@ else launch_jstfu || exit $? fi wait_system_init +if [[ "$?" -ne 0 ]]; then + exit 1 +fi +wait_python_udf_worker exit $? diff --git a/pkg/bootstrap/versions/v4_0_7/tenant_upgrade_list.go b/pkg/bootstrap/versions/v4_0_7/tenant_upgrade_list.go index 6ead4552b581c..921df76c80311 100644 --- a/pkg/bootstrap/versions/v4_0_7/tenant_upgrade_list.go +++ b/pkg/bootstrap/versions/v4_0_7/tenant_upgrade_list.go @@ -14,10 +14,146 @@ package v4_0_7 -import "github.com/matrixorigin/matrixone/pkg/bootstrap/versions" +import ( + "fmt" + + "github.com/matrixorigin/matrixone/pkg/bootstrap/versions" + "github.com/matrixorigin/matrixone/pkg/catalog" + "github.com/matrixorigin/matrixone/pkg/common/moerr" + "github.com/matrixorigin/matrixone/pkg/container/types" + "github.com/matrixorigin/matrixone/pkg/defines" + "github.com/matrixorigin/matrixone/pkg/util/executor" +) // The index metadata provenance migration is NOT an entry: the set of tables it alters is // only known at runtime (one metadata table per index, per account), which a fixed UpgSql // cannot express. It runs from HandleTenantUpgrade, the same escape hatch v4_0_6 uses for // upgradeLegacyForeignKeyMetadata. -var tenantUpgEntries = []versions.UpgradeEntry{} +var tenantUpgEntries = []versions.UpgradeEntry{ + addPythonRevisionHeadColumn("active_revision", "bigint unsigned not null default 0"), + addPythonRevisionHeadColumn("namespace_version", "bigint unsigned not null default 0"), + addPythonSignatureColumn("canonical_input_descriptor", fmt.Sprintf("varchar(%d) not null default ''", types.MaxStringSize), "arg_types"), + addPythonSignatureColumn("return_descriptor", fmt.Sprintf("varchar(%d) not null default ''", types.MaxStringSize), "canonical_input_descriptor"), + addPythonSignatureColumn("signature_key_schema_version", "int not null default 0", "return_descriptor"), + addPythonSignatureColumn("signature_fingerprint", "varchar(128) not null default ''", "signature_key_schema_version"), + addPythonSignatureIndex(), + dropLegacyPythonSignatureIndex(), + { + Schema: catalog.MO_CATALOG, + TableName: "mo_function_revisions", + UpgType: versions.CREATE_NEW_TABLE, + UpgSql: fmt.Sprintf(`create table mo_catalog.mo_function_revisions ( + function_id bigint unsigned not null, + revision bigint unsigned not null, + namespace_version bigint unsigned not null, + name varchar(100) not null, + args json not null, + arg_types varchar(%d) not null default '', + rettype varchar(20) not null, + body text not null, + language varchar(20) not null, + definition_schema_version int not null default 0, + abi_contract varchar(64) not null default '', + adapter_version varchar(64) not null default '', + artifact_digest varchar(128) not null default '', + environment_digest varchar(128) not null default '', + sdk_version varchar(64) not null default '', + null_policy varchar(64) not null default '', + volatility varchar(20) not null default 'VOLATILE', + definition_fingerprint varchar(128) not null default '', + created_time timestamp, + security_type varchar(10) not null default 'DEFINER', + primary key(function_id, revision) + )`, types.MaxStringSize), + RequiredProtocolVersion: defines.MORPCVersion48, + CheckFunc: func(txn executor.TxnExecutor, accountID uint32) (bool, error) { + return versions.CheckTableDefinition(txn, accountID, catalog.MO_CATALOG, "mo_function_revisions") + }, + }, +} + +// addPythonSignatureIndex makes the shared function namespace distinguish the +// exact current Python descriptor while preserving the historical logical +// arg_types column for SQL UDFs and catalog readers. +func addPythonSignatureIndex() versions.UpgradeEntry { + return versions.UpgradeEntry{ + Schema: catalog.MO_CATALOG, + TableName: "mo_user_defined_function", + UpgType: versions.ADD_INDEX, + UpgSql: "create unique index name_db_arg_types_descriptor on mo_catalog.mo_user_defined_function(name, db, arg_types, canonical_input_descriptor)", + RequiredProtocolVersion: defines.MORPCVersion48, + CheckFunc: func(txn executor.TxnExecutor, accountID uint32) (bool, error) { + return versions.CheckIndexDefinition( + txn, accountID, catalog.MO_CATALOG, "mo_user_defined_function", "name_db_arg_types_descriptor", + ) + }, + } +} + +func dropLegacyPythonSignatureIndex() versions.UpgradeEntry { + return versions.UpgradeEntry{ + Schema: catalog.MO_CATALOG, + TableName: "mo_user_defined_function", + UpgType: versions.DROP_INDEX, + UpgSql: "alter table mo_catalog.mo_user_defined_function drop index name_db_arg_types", + RequiredProtocolVersion: defines.MORPCVersion48, + CheckFunc: func(txn executor.TxnExecutor, accountID uint32) (bool, error) { + exists, err := versions.CheckIndexDefinition( + txn, accountID, catalog.MO_CATALOG, "mo_user_defined_function", "name_db_arg_types", + ) + return !exists, err + }, + } +} + +func addPythonRevisionHeadColumn(name, definition string) versions.UpgradeEntry { + return versions.UpgradeEntry{ + Schema: catalog.MO_CATALOG, + TableName: "mo_user_defined_function", + UpgType: versions.ADD_COLUMN, + UpgSql: fmt.Sprintf( + "alter table mo_catalog.mo_user_defined_function add column %s %s after function_id", + name, definition, + ), + RequiredProtocolVersion: defines.MORPCVersion48, + CheckFunc: func(txn executor.TxnExecutor, accountID uint32) (bool, error) { + column, err := versions.CheckTableColumn(txn, accountID, catalog.MO_CATALOG, "mo_user_defined_function", name) + if err != nil { + return false, err + } + if !column.IsExits { + return false, nil + } + if column.ColType == "" { + return false, moerr.NewInternalErrorNoCtxf("catalog column %s has no type", name) + } + return true, nil + }, + } +} + +func addPythonSignatureColumn(name, definition, after string) versions.UpgradeEntry { + return versions.UpgradeEntry{ + Schema: catalog.MO_CATALOG, + TableName: "mo_user_defined_function", + UpgType: versions.ADD_COLUMN, + UpgSql: fmt.Sprintf( + "alter table mo_catalog.mo_user_defined_function add column %s %s after %s", + name, definition, after, + ), + RequiredProtocolVersion: defines.MORPCVersion48, + CheckFunc: func(txn executor.TxnExecutor, accountID uint32) (bool, error) { + column, err := versions.CheckTableColumn(txn, accountID, catalog.MO_CATALOG, "mo_user_defined_function", name) + if err != nil { + return false, err + } + if !column.IsExits { + return false, nil + } + if column.ColType == "" { + return false, moerr.NewInternalErrorNoCtxf("catalog column %s has no type", name) + } + return true, nil + }, + } +} diff --git a/pkg/bootstrap/versions/v4_0_7/upgrade_test.go b/pkg/bootstrap/versions/v4_0_7/upgrade_test.go new file mode 100644 index 0000000000000..cb15460b65457 --- /dev/null +++ b/pkg/bootstrap/versions/v4_0_7/upgrade_test.go @@ -0,0 +1,105 @@ +// Copyright 2026 Matrix Origin +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package v4_0_7 + +import ( + "strings" + "testing" + + "github.com/matrixorigin/matrixone/pkg/bootstrap/versions" + "github.com/matrixorigin/matrixone/pkg/catalog" + "github.com/matrixorigin/matrixone/pkg/defines" +) + +func TestPythonFunctionRevisionUpgradeContract(t *testing.T) { + metadata := Handler.Metadata() + if metadata.Version != "4.0.7" || metadata.MinUpgradeVersion != "4.0.6" { + t.Fatalf("unexpected version metadata: %+v", metadata) + } + if metadata.UpgradeCluster != versions.No || metadata.UpgradeTenant != versions.Yes { + t.Fatalf("python catalog upgrade must be tenant scoped: %+v", metadata) + } + if metadata.VersionOffset != uint32(len(tenantUpgEntries)) { + t.Fatalf("version offset %d does not match tenant entries %d", metadata.VersionOffset, len(tenantUpgEntries)) + } + if metadata.RequiredProtocolVersion != defines.MORPCVersion48 { + t.Fatalf("unexpected protocol gate: %v", metadata.RequiredProtocolVersion) + } + + if len(tenantUpgEntries) != 9 { + t.Fatalf("expected six UDF identity columns, index replacement, and one revision table, got %d entries", len(tenantUpgEntries)) + } + for _, name := range []string{ + "active_revision", "namespace_version", "canonical_input_descriptor", + "return_descriptor", "signature_key_schema_version", "signature_fingerprint", + } { + var found bool + for _, entry := range tenantUpgEntries { + if entry.TableName == "mo_user_defined_function" && strings.Contains(strings.ToLower(entry.UpgSql), "add column "+name) { + found = true + if entry.UpgType != versions.ADD_COLUMN || entry.Schema != catalog.MO_CATALOG { + t.Fatalf("unexpected upgrade entry for %s: %+v", name, entry) + } + break + } + } + if !found { + t.Fatalf("missing mo_user_defined_function.%s upgrade", name) + } + } + + var addIndex, dropIndex, revision *versions.UpgradeEntry + for i := range tenantUpgEntries { + entry := &tenantUpgEntries[i] + lower := strings.ToLower(entry.UpgSql) + switch { + case strings.Contains(lower, "name_db_arg_types_descriptor"): + addIndex = entry + case strings.Contains(lower, "drop index name_db_arg_types"): + dropIndex = entry + case entry.TableName == "mo_function_revisions": + revision = entry + } + } + if addIndex == nil || addIndex.UpgType != versions.ADD_INDEX || addIndex.RequiredProtocolVersion != defines.MORPCVersion48 { + t.Fatalf("missing current Python signature index upgrade: %+v", addIndex) + } + if !strings.Contains(strings.ToLower(addIndex.UpgSql), "canonical_input_descriptor") { + t.Fatalf("current Python signature index omits exact descriptor: %s", addIndex.UpgSql) + } + if dropIndex == nil || dropIndex.UpgType != versions.DROP_INDEX || dropIndex.RequiredProtocolVersion != defines.MORPCVersion48 { + t.Fatalf("missing legacy signature index removal: %+v", dropIndex) + } + if revision == nil { + t.Fatal("missing Python revision table upgrade") + } + if revision.Schema != catalog.MO_CATALOG || revision.TableName != "mo_function_revisions" { + t.Fatalf("unexpected revision catalog identity: %+v", revision) + } + if revision.UpgType != versions.CREATE_NEW_TABLE || revision.RequiredProtocolVersion != defines.MORPCVersion48 { + t.Fatalf("unexpected revision catalog upgrade: %+v", revision) + } + lowerDDL := strings.ToLower(revision.UpgSql) + for _, column := range []string{ + "function_id", "revision", "namespace_version", "arg_types", + "definition_schema_version", "abi_contract", "adapter_version", + "artifact_digest", "environment_digest", "sdk_version", "null_policy", + "volatility", "definition_fingerprint", "primary key(function_id, revision)", + } { + if !strings.Contains(lowerDDL, column) { + t.Fatalf("revision catalog DDL missing %q: %s", column, revision.UpgSql) + } + } +} diff --git a/pkg/cnservice/server.go b/pkg/cnservice/server.go index fd04c9f2a77ce..1304c6fea1bcc 100644 --- a/pkg/cnservice/server.go +++ b/pkg/cnservice/server.go @@ -243,8 +243,12 @@ func NewService( var udfServices []udf.Runtime // add python client to handle python udf if srv.cfg.PythonUdfClient.Enabled { + artifactStore, storeErr := python.NewFileArtifactStore(srv.fileService, python.DefaultMaxArtifactBytes) + if storeErr != nil { + panic(storeErr) + } var pc *python.Gateway - pc, err = python.NewGateway(srv.cfg.PythonUdfClient) + pc, err = python.NewGatewayWithArtifactStore(srv.cfg.PythonUdfClient, artifactStore) if err != nil { panic(err) } diff --git a/pkg/cnservice/types.go b/pkg/cnservice/types.go index b4b92ee3a66bc..a57f63e1cd1b1 100644 --- a/pkg/cnservice/types.go +++ b/pkg/cnservice/types.go @@ -503,6 +503,9 @@ func (c *Config) Validate() error { if err := c.Sirius.validate(); err != nil { return err } + if err := c.PythonUdfClient.Validate(); err != nil { + return err + } if !metadata.ValidStateString(c.InitWorkState) { c.InitWorkState = metadata.WorkState_Working.String() diff --git a/pkg/cnservice/types_test.go b/pkg/cnservice/types_test.go index f4c118a732e27..bc89f062d7aac 100644 --- a/pkg/cnservice/types_test.go +++ b/pkg/cnservice/types_test.go @@ -18,7 +18,9 @@ import ( "testing" "time" + moruntime "github.com/matrixorigin/matrixone/pkg/common/runtime" "github.com/matrixorigin/matrixone/pkg/logservice" + "github.com/matrixorigin/matrixone/pkg/pb/metadata" "github.com/stretchr/testify/require" ) @@ -47,3 +49,27 @@ func TestValidateHeartbeatDurations(t *testing.T) { cfg.HAKeeper.HeatbeatTimeout.Duration = -time.Nanosecond require.ErrorContains(t, cfg.Validate(), "hakeeper heartbeat timeout") } + +func TestValidatePythonUdfClientContract(t *testing.T) { + service := "python-udf-config-" + t.Name() + rt := moruntime.NewRuntime(metadata.ServiceType_CN, service, nil) + moruntime.SetupServiceBasedRuntime(service, rt) + + disabled := Config{UUID: service} + require.NoError(t, disabled.Validate()) + + requiresOptIn := Config{UUID: service} + requiresOptIn.PythonUdfClient.Enabled = true + require.ErrorContains(t, requiresOptIn.Validate(), "allow-unisolated") + + requiresAddress := Config{UUID: service} + requiresAddress.PythonUdfClient.Enabled = true + requiresAddress.PythonUdfClient.AllowUnisolated = true + require.ErrorContains(t, requiresAddress.Validate(), "missing python udf address") + + valid := Config{UUID: service} + valid.PythonUdfClient.Enabled = true + valid.PythonUdfClient.AllowUnisolated = true + valid.PythonUdfClient.ServerAddress = "127.0.0.1:50051" + require.NoError(t, valid.Validate()) +} diff --git a/pkg/frontend/authenticate.go b/pkg/frontend/authenticate.go index 95e37d4a0ba86..5b354b10fa90a 100644 --- a/pkg/frontend/authenticate.go +++ b/pkg/frontend/authenticate.go @@ -67,6 +67,7 @@ import ( "github.com/matrixorigin/matrixone/pkg/stage/stageutil" "github.com/matrixorigin/matrixone/pkg/taskservice" "github.com/matrixorigin/matrixone/pkg/udf" + pythonudf "github.com/matrixorigin/matrixone/pkg/udf/python" "github.com/matrixorigin/matrixone/pkg/util/metric/mometric" v2 "github.com/matrixorigin/matrixone/pkg/util/metric/v2" "github.com/matrixorigin/matrixone/pkg/util/sysview" @@ -934,6 +935,7 @@ var ( "mo_role_grant": 0, "mo_role_privs": 0, "mo_user_defined_function": 0, + "mo_function_revisions": 0, "mo_stored_procedure": 0, "mo_mysql_compatibility_mode": 0, "mo_stages": 0, @@ -975,6 +977,7 @@ var ( "mo_role_grant": 0, "mo_role_privs": 0, "mo_user_defined_function": 0, + "mo_function_revisions": 0, "mo_stored_procedure": 0, "mo_mysql_compatibility_mode": 0, catalog.MOAutoIncrTable: 0, @@ -1036,6 +1039,7 @@ var ( MoCatalogMoRoleGrantDDL, MoCatalogMoRolePrivsDDL, MoCatalogMoUserDefinedFunctionDDL, + MoCatalogMoFunctionRevisionDDL, MoCatalogMoMysqlCompatibilityModeDDL, MoCatalogMoSnapshotsDDL, MoCatalogMoPubsDDL, @@ -1088,6 +1092,7 @@ var ( `drop table if exists mo_catalog.mo_role_grant;`, `drop table if exists mo_catalog.mo_role_privs;`, `drop table if exists mo_catalog.mo_user_defined_function;`, + `drop table if exists mo_catalog.mo_function_revisions;`, `drop table if exists mo_catalog.mo_stored_procedure;`, `drop table if exists mo_catalog.mo_stages;`, `drop view if exists mo_catalog.mo_sessions;`, @@ -1137,6 +1142,34 @@ var ( comment) values ('%s','%s', '%s', '%s','%s', '%s');` initMoUserDefinedFunctionFormat = `insert into mo_catalog.mo_user_defined_function( + name, + owner, + args, + arg_types, + canonical_input_descriptor, + return_descriptor, + signature_key_schema_version, + signature_fingerprint, + retType, + body, + language, + db, + definer, + modified_time, + created_time, + type, + security_type, + comment, + character_set_client, + collation_connection, + database_collation, + sql_mode) values ("%s",%d,'%s','%s',"%s","%s",%d,"%s","%s","%s","%s","%s","%s","%s","%s","%s","%s","%s","%s","%s","%s","%s");` + + // SQL UDF creation keeps the pre-Python catalog write shape. During a + // rolling upgrade an ordinary SQL writer may still target a catalog that + // has not received the Python identity columns. Python creation is gated on + // the current catalog contract and uses initMoUserDefinedFunctionFormat. + initMoUserDefinedFunctionSQLFormat = `insert into mo_catalog.mo_user_defined_function( name, owner, args, @@ -1554,7 +1587,22 @@ const ( checkUdfWithDb = `select function_id,body from mo_catalog.mo_user_defined_function where db = "%s" order by function_id;` - checkUdfExistence = `select function_id from mo_catalog.mo_user_defined_function where name = "%s" and db = "%s" and arg_types = %s order by function_id;` + // SQL keeps this query on columns that existed before the Python identity + // fields. That lets ordinary SQL UDF creation continue during a rolling + // catalog upgrade while the Python path uses the current exact-descriptor + // query below after its readiness gate succeeds. + checkUdfExistence = `select function_id, args, language, arg_types from mo_catalog.mo_user_defined_function where name = "%s" and db = "%s" order by function_id;` + checkPythonUdfExistence = `select function_id, args, language, arg_types, canonical_input_descriptor from mo_catalog.mo_user_defined_function where name = "%s" and db = "%s" order by function_id;` + + // These schema probes are the write gate for the current Python catalog + // contract. They deliberately reference every column used by the Python + // writer. COUNT keeps one result row even when the catalog is empty; a + // literal LIMIT 0 is optimized away by BackgroundExec and does not expose a + // result-set schema. A frontend in a rolling upgrade must reject Python + // publication before it creates or changes a routine when the tenant has + // not reached this schema. + pythonUdfCatalogIdentitySchemaCheck = `select count(active_revision), count(namespace_version), count(canonical_input_descriptor), count(return_descriptor), count(signature_key_schema_version), count(signature_fingerprint) from mo_catalog.mo_user_defined_function;` + functionRevisionCatalogSchemaCheck = `select count(function_id), count(revision), count(namespace_version), count(name), count(args), count(arg_types), count(rettype), count(body), count(language), count(definition_schema_version), count(abi_contract), count(adapter_version), count(artifact_digest), count(environment_digest), count(sdk_version), count(null_policy), count(volatility), count(definition_fingerprint), count(created_time), count(security_type) from mo_catalog.mo_function_revisions;` checkStoredProcedureArgs = `select proc_id, args from mo_catalog.mo_stored_procedure where name = "%s" and db = "%s" order by proc_id;` @@ -1598,7 +1646,8 @@ const ( deleteUserFromMoUserGrantFormat = `delete from mo_catalog.mo_user_grant where user_id = %d;` // delete user defined function from mo_user_defined_function - deleteUserDefinedFunctionFormat = `delete from mo_catalog.mo_user_defined_function where function_id = %d;` + deleteUserDefinedFunctionFormat = `delete from mo_catalog.mo_user_defined_function where function_id = %d;` + deleteUserDefinedFunctionRevisionsFormat = `delete from mo_catalog.mo_function_revisions where function_id = %d;` // delete stored procedure from mo_user_defined_function deleteStoredProcedureFormat = `delete from mo_catalog.mo_stored_procedure where proc_id = %d;` @@ -1677,6 +1726,24 @@ var ( } ) +// deleteUserDefinedFunctionRevisionsIfPresent removes the immutable Python +// revisions belonging to a function identity. The revision table was added by +// the current catalog upgrade and is not part of the legacy SQL UDF contract. +// A mixed-version tenant must therefore still be able to drop an ordinary SQL +// UDF while that table is absent. Once the table exists, every error other than +// the explicit missing-table condition is returned so a failed revision +// cleanup cannot be hidden by deleting the identity row. +func deleteUserDefinedFunctionRevisionsIfPresent(ctx context.Context, bh BackgroundExec, functionID int64) error { + err := bh.Exec(ctx, fmt.Sprintf(deleteUserDefinedFunctionRevisionsFormat, functionID)) + if err == nil { + return nil + } + if moerr.IsMoErrCode(err, moerr.ErrNoSuchTable) || strings.Contains(strings.ToLower(err.Error()), "no such table") { + return nil + } + return err +} + func init() { tables := make([]string, 0) for tbl := range predefinedTables { @@ -5002,6 +5069,16 @@ func doDropFunction(ctx context.Context, ses *Session, df *tree.DropFunction, rm if !match { continue } + // The historical args column intentionally stores only logical + // type names. A current Python body carries the exact Arrow + // descriptors, so use those descriptors when selecting an + // overload for DROP. Otherwise two DECIMAL or bounded string + // overloads would be indistinguishable and DROP could remove the + // wrong immutable identity. A non-current body is left on the + // logical path so an old demo can still be explicitly removed. + if !pythonDropSignatureMatches(df.Args, argstr) { + continue + } handleArgMatch := func() (rtnErr error) { sql = fmt.Sprintf(deleteUserDefinedFunctionFormat, funcId) @@ -5009,6 +5086,10 @@ func doDropFunction(ctx context.Context, ses *Session, df *tree.DropFunction, rm if rtnErr != nil { return rtnErr } + rtnErr = deleteUserDefinedFunctionRevisionsIfPresent(ctx, bh, funcId) + if rtnErr != nil { + return rtnErr + } return rtnErr } err = handleArgMatch() @@ -5029,6 +5110,36 @@ func doDropFunction(ctx context.Context, ses *Session, df *tree.DropFunction, rm return moerr.NewNoUDFNoCtx(string(df.Name.Name.ObjectName)) } +// pythonDropSignatureMatches recognizes the current Python catalog body and +// compares its exact Arrow input descriptors with a DROP declaration. A +// body that is not the current Python contract returns true so the caller can +// retain the historical logical matching needed to explicitly remove an old +// demo definition; such a body is never admitted for execution. +func pythonDropSignatureMatches(args tree.FunctionArgs, rawBody string) bool { + body, err := function.DecodePythonRoutineBody(rawBody) + if err != nil { + return true + } + if len(body.ArgTypes) != len(args) { + return false + } + for index, arg := range args { + argDecl, ok := arg.(*tree.FunctionArgDecl) + if !ok || argDecl == nil { + return false + } + typ, err := plan2.GetFunctionTypeFromAst(argDecl.Type) + if err != nil { + return false + } + descriptor, err := function.NewPythonTypeDescriptor(typ) + if err != nil || descriptor != body.ArgTypes[index] { + return false + } + } + return true +} + func doDropFunctionWithDB(ctx context.Context, ses *Session, stmt tree.Statement, rm rmPkg) (err error) { var sql string var funcId int64 @@ -5078,6 +5189,10 @@ func doDropFunctionWithDB(ctx context.Context, ses *Session, stmt tree.Statement if rtnErr != nil { return rtnErr } + rtnErr = deleteUserDefinedFunctionRevisionsIfPresent(ctx, bh, funcId) + if rtnErr != nil { + return rtnErr + } return rtnErr } @@ -11484,19 +11599,127 @@ func Upload(ses FeSession, execCtx *ExecCtx, localPath string, storageDir string return ioVector.FilePath, nil } +func ensurePythonUdfCatalogReady(ctx context.Context, bh BackgroundExec) error { + probes := []struct { + query string + expectedCols uint64 + }{ + {query: pythonUdfCatalogIdentitySchemaCheck, expectedCols: 6}, + {query: functionRevisionCatalogSchemaCheck, expectedCols: 20}, + } + for _, probe := range probes { + query := probe.query + bh.ClearExecResultSet() + if err := bh.Exec(ctx, query); err != nil { + return moerr.NewNotSupportedNoCtxf( + "Python UDF catalog contract is not ready; upgrade the tenant before creating or replacing Python UDFs: %v", + err, + ) + } + results, err := getResultSet(ctx, bh) + if err != nil { + return moerr.NewNotSupportedNoCtxf( + "Python UDF catalog contract is not ready; catalog probe returned an invalid result: %v", + err, + ) + } + result, resultErr := exactlyOneCatalogRow(ctx, results, "Python UDF catalog schema probe") + if resultErr != nil { + return moerr.NewNotSupportedNoCtxf( + "Python UDF catalog contract is not ready; catalog probe returned an invalid shape: %v", + resultErr, + ) + } + if result.GetColumnCount() != probe.expectedCols { + return moerr.NewNotSupportedNoCtxf( + "Python UDF catalog contract is not ready; catalog probe returned %d columns, expected %d", + result.GetColumnCount(), probe.expectedCols, + ) + } + } + return nil +} + +// functionRevisionCatalogAvailable reports whether the current shared +// revision table can be used by the SQL UDF writer. Ordinary SQL UDFs remain +// writable during a rolling upgrade before v4_0_7 reaches every tenant; once +// the table is present, revision publication is part of the same caller-owned +// transaction as the legacy compatibility row. +func functionRevisionCatalogAvailable(ctx context.Context, bh BackgroundExec) (bool, error) { + bh.ClearExecResultSet() + if err := bh.Exec(ctx, functionRevisionCatalogSchemaCheck); err != nil { + if isMissingCatalogObjectError(err) { + return false, nil + } + return false, err + } + // The aggregate probe must return one row. Anything else indicates a broken + // executor/result contract and must reach the caller; only an explicitly + // missing catalog object is a supported rolling-upgrade path. + results, err := getResultSet(ctx, bh) + if err != nil { + return false, err + } + if _, err := exactlyOneCatalogRow(ctx, results, "function revision catalog schema probe"); err != nil { + return false, err + } + return true, nil +} + +func isMissingCatalogObjectError(err error) bool { + if err == nil { + return false + } + message := strings.ToLower(err.Error()) + return moerr.IsMoErrCode(err, moerr.ErrNoSuchTable) || + strings.Contains(message, "no such table") || + strings.Contains(message, "table does not exist") || + strings.Contains(message, "unknown column") +} + +func ensurePythonUdfRuntimeReady(ctx context.Context, ses *Session) error { + pu := getPuIfPresent(ses.GetService()) + if pu == nil || pu.UdfService == nil { + return moerr.NewNotSupportedNoCtx("Python UDF feature is disabled") + } + readiness, ok := pu.UdfService.(udf.RuntimeReadiness) + if !ok { + return moerr.NewNotSupportedNoCtx("Python UDF runtime does not expose the current contract") + } + return readiness.CheckLanguageReady(ctx, udf.LanguagePython) +} + +// canonicalPythonInputDescriptor is the shared overload key used by the +// catalog lookup and by the immutable revision metadata. Keeping this +// conversion at the create boundary prevents the logical SQL OID list from +// accidentally being used to match width/scale-sensitive Python signatures. +func canonicalPythonInputDescriptor( + args []function.PythonTypeDescriptor, + returnType *function.PythonTypeDescriptor, +) (string, error) { + inputDescriptor, _, _, err := function.PythonSignatureMetadata(args, returnType) + return inputDescriptor, err +} + func InitFunction(ses *Session, execCtx *ExecCtx, tenant *TenantInfo, cf *tree.CreateFunction) (err error) { var retTypeStr string var dbName string var dbExists bool var checkExistence string var argsJson []byte - var argsCondition string + var logicalArgTypes string + var exactArgTypes string var fmtctx *tree.FmtCtx var argList []*function.Arg var typeList []string var erArray []ExecResult var pythonArgTypes []function.PythonTypeDescriptor var pythonReturnType *function.PythonTypeDescriptor + if strings.EqualFold(cf.Language, string(tree.PYTHON)) { + if err := ensurePythonUdfRuntimeReady(execCtx.reqCtx, ses); err != nil { + return err + } + } // a database must be selected or specified as qualifier when create a function if cf.Name.HasNoNameQualifier() { @@ -11523,6 +11746,11 @@ func InitFunction(ses *Session, execCtx *ExecCtx, tenant *TenantInfo, cf *tree.C defer func() { err = finishTxn(execCtx.reqCtx, bh, err) }() + if cf.Language == string(tree.PYTHON) { + if err = ensurePythonUdfCatalogReady(execCtx.reqCtx, bh); err != nil { + return err + } + } // Lock and authenticate the target database before the exact-signature // read. The lock stays held through persistence and commit. @@ -11587,11 +11815,21 @@ func InitFunction(ses *Session, execCtx *ExecCtx, tenant *TenantInfo, cf *tree.C if err != nil { return err } - argsCondition = fmt.Sprintf("'%s'", argTypes) + logicalArgTypes = argTypes + if cf.Language == string(tree.PYTHON) { + exactArgTypes, err = canonicalPythonInputDescriptor(pythonArgTypes, pythonReturnType) + if err != nil { + return err + } + } // validate duplicate function declaration bh.ClearExecResultSet() - checkExistence = fmt.Sprintf(checkUdfExistence, string(cf.Name.Name.ObjectName), dbName, argsCondition) + if cf.Language == string(tree.PYTHON) { + checkExistence = fmt.Sprintf(checkPythonUdfExistence, string(cf.Name.Name.ObjectName), dbName) + } else { + checkExistence = fmt.Sprintf(checkUdfExistence, string(cf.Name.Name.ObjectName), dbName) + } err = bh.Exec(execCtx.reqCtx, checkExistence) if err != nil { return err @@ -11602,7 +11840,14 @@ func InitFunction(ses *Session, execCtx *ExecCtx, tenant *TenantInfo, cf *tree.C return err } - if execResultArrayHasData(erArray) && !cf.Replace { + matchedIDs, err := matchUserDefinedFunctionCandidates(execCtx.reqCtx, erArray, string(cf.Language), logicalArgTypes, exactArgTypes) + if err != nil { + return err + } + if len(matchedIDs) > 1 { + return moerr.NewInvalidInputNoCtxf("function %s has multiple catalog identities for the same signature", string(cf.Name.Name.ObjectName)) + } + if len(matchedIDs) == 1 && !cf.Replace { return moerr.NewUDFAlreadyExistsNoCtx(string(cf.Name.Name.ObjectName)) } var body string @@ -11620,17 +11865,24 @@ func InitFunction(ses *Session, execCtx *ExecCtx, tenant *TenantInfo, cf *tree.C if nullPolicy == "" { nullPolicy = udf.NullCallHandler } + environmentDigest, digestErr := udf.PythonEnvironmentDigest() + if digestErr != nil { + return moerr.NewInvalidInputNoCtxf("Python UDF environment is not available: %v", digestErr) + } nb := function.PythonRoutineBody{ - Handler: cf.Handler, - Source: cf.Body, - Mode: mode, - NullPolicy: nullPolicy, - ABIContract: udf.PythonABIContract, - AdapterVersion: udf.PythonAdapterVersion, - SDKVersion: udf.PythonSDKVersion, - ArgTypes: pythonArgTypes, - ReturnType: pythonReturnType, + DefinitionSchemaVersion: udf.PythonDefinitionSchemaVersion, + Handler: cf.Handler, + Source: cf.Body, + Mode: mode, + NullPolicy: nullPolicy, + ABIContract: udf.PythonABIContract, + AdapterVersion: udf.PythonAdapterVersion, + ArtifactDigest: udf.PythonInlineArtifactDigest(cf.Handler, cf.Body), + EnvironmentDigest: environmentDigest, + SDKVersion: udf.PythonSDKVersion, + ArgTypes: pythonArgTypes, + ReturnType: pythonReturnType, } var byt []byte byt, err = json.Marshal(nb) @@ -11652,11 +11904,29 @@ func InitFunction(ses *Session, execCtx *ExecCtx, tenant *TenantInfo, cf *tree.C sqlMode: sessionSQLModeForParser(ses), dbName: dbName, } - if execResultArrayHasData(erArray) { // replace - id, err := erArray[0].GetInt64(execCtx.reqCtx, 0, 0) - if err != nil { + if strings.EqualFold(cf.Language, string(tree.PYTHON)) { + // The catalog revision stores the source for restore/inspection, while + // the executable plan carries only its immutable digest. Publish the + // account-scoped object before the catalog row becomes visible; a failed + // transaction can leave only an unreferenced object for later GC. + artifactStore, storeErr := pythonudf.NewFileArtifactStore( + getPu(ses.GetService()).FileService, + pythonudf.DefaultMaxArtifactBytes, + ) + if storeErr != nil { + return storeErr + } + if _, err = artifactStore.Publish( + execCtx.reqCtx, + uint64(tenant.GetTenantID()), + cf.Handler, + cf.Body, + ); err != nil { return err } + } + if len(matchedIDs) == 1 { // replace + id := matchedIDs[0] return persistUserDefinedFunction( execCtx.reqCtx, bh, tenant, ses.GetTenantInfo().GetDefaultRoleID(), definition, &id, ) @@ -11698,20 +11968,26 @@ func escapeSQLStringForDoubleQuotes(s string) string { // userDefinedFunctionDefinition is the function metadata kept outside // mo_tables. Callers control the transaction that persists it. type userDefinedFunctionDefinition struct { - name string - args string - argTypes string - retType string - body string - lang string - sqlMode string - dbName string -} - -// userDefinedFunctionArgumentTypes is the canonical identity of a function -// overload. Argument names do not participate in function resolution, while -// their ordered types do. Keep the zero-argument identity empty to match the -// legacy json_extract(args, '$[*].type') IS NULL representation. + name string + args string + argTypes string + canonicalInputDescriptor string + returnDescriptor string + signatureKeySchemaVersion int + signatureFingerprint string + retType string + body string + lang string + sqlMode string + dbName string +} + +// userDefinedFunctionArgumentTypes is the historical logical signature key +// shared by ordinary SQL routines and by the compatibility columns of the +// current Python catalog. Python's exact width/scale identity is stored in +// canonical_input_descriptor; this helper deliberately remains limited to +// ordered logical type names. Keep the zero-argument value empty to match the +// historical json_extract(args, '$[*].type') IS NULL representation. func userDefinedFunctionArgumentTypes(argumentTypes []string) (string, error) { if len(argumentTypes) == 0 { return "", nil @@ -11752,6 +12028,132 @@ func userDefinedFunctionArgumentTypesFromJSON(args string) (string, error) { return userDefinedFunctionArgumentTypes(types) } +// matchUserDefinedFunctionCandidates compares the current declaration with +// every same-name routine in the namespace. SQL retains its historical +// logical arg_types key; current Python rows additionally carry the exact +// canonical descriptor. Looking at the full candidate set is necessary now +// that Python width/scale overloads may share one logical OID sequence. +func matchUserDefinedFunctionCandidates( + ctx context.Context, + results []ExecResult, + language, logicalArgTypes, exactArgTypes string, +) ([]int64, error) { + result, err := candidateCatalogResultSet(results, "function catalog candidate lookup") + if err != nil { + return nil, err + } + if result == nil { + return nil, nil + } + if result.GetRowCount() == 0 { + return nil, nil + } + columnCount := result.GetColumnCount() + if columnCount < 4 { + return nil, moerr.NewInvalidInputNoCtx("UNSUPPORTED_ROUTINE_VERSION: malformed function catalog candidate") + } + matched := make([]int64, 0, 1) + for row := uint64(0); row < result.GetRowCount(); row++ { + functionID, err := result.GetInt64(ctx, row, 0) + if err != nil { + return nil, err + } + args, err := result.GetString(ctx, row, 1) + if err != nil { + return nil, err + } + existingLanguage, err := result.GetString(ctx, row, 2) + if err != nil { + return nil, err + } + storedArgTypes, err := result.GetString(ctx, row, 3) + if err != nil { + return nil, err + } + existingLogical := storedArgTypes + if parsed, parseErr := userDefinedFunctionArgumentTypesFromJSON(args); parseErr == nil { + existingLogical = parsed + } else if existingLogical == "" { + return nil, moerr.NewInvalidInputNoCtxf( + "UNSUPPORTED_ROUTINE_VERSION: function %d has malformed argument metadata: %v", functionID, parseErr, + ) + } + + isMatch := false + if strings.EqualFold(language, string(tree.PYTHON)) { + existingCanonical := "" + if columnCount >= 5 { + existingCanonical, err = result.GetString(ctx, row, 4) + if err != nil { + return nil, err + } + } + if strings.EqualFold(existingLanguage, string(tree.PYTHON)) && existingCanonical != "" { + isMatch = existingCanonical == exactArgTypes + } else { + // A legacy/partially upgraded Python row is rejected as the + // same logical identity. It cannot be silently replaced with a + // different exact descriptor. + isMatch = existingLogical == logicalArgTypes + } + } else { + // SQL and any unknown language still use the shared logical + // namespace until their own canonical descriptor contract is + // published. + isMatch = existingLogical == logicalArgTypes + } + if isMatch { + matched = append(matched, functionID) + } + } + return matched, nil +} + +func normalizePythonFunctionDefinition(definition userDefinedFunctionDefinition) (userDefinedFunctionDefinition, error) { + body, err := function.DecodePythonRoutineBody(definition.body) + if err != nil { + return userDefinedFunctionDefinition{}, moerr.NewInvalidInputNoCtxf( + "UNSUPPORTED_ROUTINE_VERSION: Python definition rejected before publication: %v", err, + ) + } + canonicalInput, returnDescriptor, signatureFingerprint, err := function.PythonSignatureMetadata(body.ArgTypes, body.ReturnType) + if err != nil { + return userDefinedFunctionDefinition{}, moerr.NewInvalidInputNoCtxf( + "UNSUPPORTED_ROUTINE_VERSION: Python signature rejected before publication: %v", err, + ) + } + if definition.canonicalInputDescriptor != "" && definition.canonicalInputDescriptor != canonicalInput { + return userDefinedFunctionDefinition{}, moerr.NewInvalidInputNoCtx("UNSUPPORTED_ROUTINE_VERSION: Python input descriptor does not match its definition") + } + if definition.returnDescriptor != "" && definition.returnDescriptor != returnDescriptor { + return userDefinedFunctionDefinition{}, moerr.NewInvalidInputNoCtx("UNSUPPORTED_ROUTINE_VERSION: Python return descriptor does not match its definition") + } + if definition.signatureKeySchemaVersion != 0 && definition.signatureKeySchemaVersion != udf.PythonSignatureKeySchemaVersion { + return userDefinedFunctionDefinition{}, moerr.NewInvalidInputNoCtxf( + "UNSUPPORTED_ROUTINE_VERSION: Python signature key schema %d is not supported", definition.signatureKeySchemaVersion, + ) + } + if definition.signatureFingerprint != "" && definition.signatureFingerprint != signatureFingerprint { + return userDefinedFunctionDefinition{}, moerr.NewInvalidInputNoCtx("UNSUPPORTED_ROUTINE_VERSION: Python signature fingerprint does not match its definition") + } + definition.canonicalInputDescriptor = canonicalInput + definition.returnDescriptor = returnDescriptor + definition.signatureKeySchemaVersion = udf.PythonSignatureKeySchemaVersion + definition.signatureFingerprint = signatureFingerprint + // Keep arg_types as the shared logical signature key. The exact Python + // descriptor belongs in canonical_input_descriptor and in the immutable + // revision row; mixing the two encodings makes shared SQL readers and + // upgrade paths depend on a language-specific workaround. + logicalArgTypes, err := userDefinedFunctionArgumentTypesFromJSON(definition.args) + if err != nil { + return userDefinedFunctionDefinition{}, moerr.NewInvalidInputNoCtxf( + "UNSUPPORTED_ROUTINE_VERSION: Python logical argument metadata is invalid: %v", err, + ) + } + definition.argTypes = logicalArgTypes + return definition, nil +} + // persistUserDefinedFunction writes function metadata into the caller-owned // transaction. A non-nil functionID replaces that existing definition. func persistUserDefinedFunction( @@ -11762,22 +12164,420 @@ func persistUserDefinedFunction( definition userDefinedFunctionDefinition, functionID *int64, ) error { - body := escapeSQLStringForDoubleQuotes(definition.body) sqlMode := plan2.EscapeFormat(definition.sqlMode) + securityType := "DEFINER" + if strings.EqualFold(definition.lang, string(tree.PYTHON)) { + // Python handlers run with the caller's execution frame in the current + // contract. They cannot declare DEFINER semantics; keeping this value + // in the shared catalog makes the security decision visible to readers + // and prevents a Python row from inheriting the SQL UDF default. + securityType = "INVOKER" + } + var revisionCatalogReady bool + if !strings.EqualFold(definition.lang, string(tree.PYTHON)) { + var err error + revisionCatalogReady, err = functionRevisionCatalogAvailable(ctx, bh) + if err != nil { + return err + } + } if functionID != nil { - return bh.Exec(ctx, fmt.Sprintf(updateMoUserDefinedFunctionFormat, + previousLanguage, err := readUserDefinedFunctionLanguage(ctx, bh, *functionID) + if err != nil { + return err + } + if previousLanguage == "" || !strings.EqualFold(previousLanguage, definition.lang) { + return moerr.NewInvalidInputNoCtxf( + "UNSUPPORTED_ROUTINE_VERSION: function identity %d cannot change language from %q to %q; use DROP and CREATE", + *functionID, previousLanguage, definition.lang, + ) + } + var previousRevision, previousNamespace uint64 + if strings.EqualFold(definition.lang, string(tree.PYTHON)) { + // Validate the current head and definition before touching the legacy + // compatibility row. A historical Python demo has no immutable + // revision and must be rejected without partially rewriting its body. + var readErr error + previousRevision, previousNamespace, readErr = readPythonRevisionHead(ctx, bh, *functionID) + if readErr != nil { + return readErr + } + if previousRevision == 0 || previousNamespace == 0 { + return moerr.NewInvalidInputNoCtx("UNSUPPORTED_ROUTINE_VERSION: legacy Python definition requires DROP and CREATE") + } + definition, err = normalizePythonFunctionDefinition(definition) + if err != nil { + return err + } + candidate, err := function.DecodePythonRoutineBody(definition.body) + if err != nil { + return moerr.NewInvalidInputNoCtxf("UNSUPPORTED_ROUTINE_VERSION: Python definition rejected before publication: %v", err) + } + previousBody, err := readPythonRevisionBody(ctx, bh, *functionID, previousRevision, previousNamespace) + if err != nil { + return err + } + previous, err := function.DecodePythonRoutineBody(previousBody) + if err != nil { + return moerr.NewInvalidInputNoCtxf("UNSUPPORTED_ROUTINE_VERSION: current Python revision %d is invalid: %v", previousRevision, err) + } + if !samePythonRoutineSignature(previous, candidate) { + return moerr.NewInvalidInputNoCtxf( + "UNSUPPORTED_ROUTINE_VERSION: Python REPLACE cannot change input or return descriptor; use DROP and CREATE", + ) + } + } else if revisionCatalogReady { + var readErr error + previousRevision, previousNamespace, readErr = readFunctionRevisionHead(ctx, bh, *functionID) + if readErr != nil { + return readErr + } + previousReturnType, returnErr := readUserDefinedFunctionReturnType(ctx, bh, *functionID) + if returnErr != nil { + return returnErr + } + if !strings.EqualFold(previousReturnType, definition.retType) { + return moerr.NewInvalidInputNoCtxf( + "SQL REPLACE cannot change return type from %q to %q; use DROP and CREATE", + previousReturnType, definition.retType, + ) + } + } + body := escapeSQLStringForDoubleQuotes(definition.body) + if err := bh.Exec(ctx, fmt.Sprintf(updateMoUserDefinedFunctionFormat, ownerRoleID, definition.args, definition.argTypes, definition.retType, body, definition.lang, - tenant.GetUser(), types.CurrentTimestamp().String2(time.UTC, 0), "FUNCTION", "DEFINER", "", "utf8mb4", "utf8mb4_0900_ai_ci", "utf8mb4_0900_ai_ci", sqlMode, - int32(*functionID))) - } - return bh.Exec(ctx, fmt.Sprintf(initMoUserDefinedFunctionFormat, - definition.name, - ownerRoleID, - definition.args, - definition.argTypes, definition.retType, body, definition.lang, definition.dbName, - tenant.GetUser(), types.CurrentTimestamp().String2(time.UTC, 0), types.CurrentTimestamp().String2(time.UTC, 0), "FUNCTION", "DEFINER", "", "utf8mb4", "utf8mb4_0900_ai_ci", "utf8mb4_0900_ai_ci", sqlMode)) + tenant.GetUser(), types.CurrentTimestamp().String2(time.UTC, 0), "FUNCTION", securityType, "", "utf8mb4", "utf8mb4_0900_ai_ci", "utf8mb4_0900_ai_ci", sqlMode, + int32(*functionID))); err != nil { + return err + } + if strings.EqualFold(definition.lang, string(tree.PYTHON)) { + nextRevision := previousRevision + 1 + nextNamespace := previousNamespace + 1 + if err := insertPythonRevision(ctx, bh, definition, *functionID, nextRevision, nextNamespace); err != nil { + return err + } + return activateFunctionRevision(ctx, bh, *functionID, nextRevision, nextNamespace) + } + if !revisionCatalogReady { + return nil + } + nextRevision := previousRevision + 1 + nextNamespace := previousNamespace + 1 + if previousRevision == 0 || previousNamespace == 0 { + nextRevision = 1 + nextNamespace = 1 + } + if err := insertSQLRevision(ctx, bh, definition, *functionID, nextRevision, nextNamespace); err != nil { + return err + } + return activateFunctionRevision(ctx, bh, *functionID, nextRevision, nextNamespace) + } + if strings.EqualFold(definition.lang, string(tree.PYTHON)) { + // A current Python definition must be valid before the base catalog row + // is created. The caller owns the transaction, but rejecting before the + // first write also keeps direct/background writers from leaving a + // partially published identity when validation fails. + var err error + definition, err = normalizePythonFunctionDefinition(definition) + if err != nil { + return err + } + } + body := escapeSQLStringForDoubleQuotes(definition.body) + created := types.CurrentTimestamp().String2(time.UTC, 0) + var insertSQL string + if strings.EqualFold(definition.lang, string(tree.PYTHON)) { + canonicalInputDescriptor := escapeSQLStringForDoubleQuotes(definition.canonicalInputDescriptor) + returnDescriptor := escapeSQLStringForDoubleQuotes(definition.returnDescriptor) + insertSQL = fmt.Sprintf(initMoUserDefinedFunctionFormat, + definition.name, + ownerRoleID, + definition.args, + definition.argTypes, canonicalInputDescriptor, returnDescriptor, definition.signatureKeySchemaVersion, + definition.signatureFingerprint, definition.retType, body, definition.lang, definition.dbName, + tenant.GetUser(), created, created, "FUNCTION", securityType, "", "utf8mb4", "utf8mb4_0900_ai_ci", "utf8mb4_0900_ai_ci", sqlMode) + } else { + // Keep ordinary SQL UDF creation independent of the Python catalog + // columns until the rolling upgrade has reached every writer. + insertSQL = fmt.Sprintf(initMoUserDefinedFunctionSQLFormat, + definition.name, + ownerRoleID, + definition.args, + definition.argTypes, definition.retType, body, definition.lang, definition.dbName, + tenant.GetUser(), created, created, "FUNCTION", "DEFINER", "", "utf8mb4", "utf8mb4_0900_ai_ci", "utf8mb4_0900_ai_ci", sqlMode) + } + if err := bh.Exec(ctx, insertSQL); err != nil { + return err + } + if strings.EqualFold(definition.lang, string(tree.PYTHON)) { + functionIDValue, err := findPersistedFunctionID(ctx, bh, definition) + if err != nil { + return err + } + if err := insertPythonRevision(ctx, bh, definition, functionIDValue, 1, 1); err != nil { + return err + } + return activateFunctionRevision(ctx, bh, functionIDValue, 1, 1) + } + if !revisionCatalogReady { + return nil + } + functionIDValue, err := findPersistedSQLFunctionID(ctx, bh, definition) + if err != nil { + return err + } + if err := insertSQLRevision(ctx, bh, definition, functionIDValue, 1, 1); err != nil { + return err + } + return activateFunctionRevision(ctx, bh, functionIDValue, 1, 1) +} + +func readUserDefinedFunctionLanguage(ctx context.Context, bh BackgroundExec, functionID int64) (string, error) { + bh.ClearExecResultSet() + if err := bh.Exec(ctx, fmt.Sprintf( + "select language from mo_catalog.mo_user_defined_function where function_id = %d;", + functionID, + )); err != nil { + return "", err + } + rows, err := getResultSet(ctx, bh) + if err != nil { + return "", err + } + row, err := exactlyOneCatalogRow(ctx, rows, fmt.Sprintf("function identity %d while replacing", functionID)) + if err != nil { + return "", err + } + language, err := row.GetString(ctx, 0, 0) + if err != nil { + return "", err + } + return language, nil +} + +func readUserDefinedFunctionReturnType(ctx context.Context, bh BackgroundExec, functionID int64) (string, error) { + bh.ClearExecResultSet() + if err := bh.Exec(ctx, fmt.Sprintf( + "select rettype from mo_catalog.mo_user_defined_function where function_id = %d;", + functionID, + )); err != nil { + return "", err + } + rows, err := getResultSet(ctx, bh) + if err != nil { + return "", err + } + row, err := exactlyOneCatalogRow(ctx, rows, fmt.Sprintf("function identity %d while checking its return type", functionID)) + if err != nil { + return "", err + } + return row.GetString(ctx, 0, 0) +} + +func readPythonRevisionBody(ctx context.Context, bh BackgroundExec, functionID int64, revision, namespace uint64) (string, error) { + bh.ClearExecResultSet() + if err := bh.Exec(ctx, fmt.Sprintf( + "select body from mo_catalog.mo_function_revisions where function_id = %d and revision = %d and namespace_version = %d;", + functionID, revision, namespace, + )); err != nil { + return "", fmt.Errorf("UNSUPPORTED_ROUTINE_VERSION: Python revision %d is unavailable: %w", revision, err) + } + rows, err := getResultSet(ctx, bh) + if err != nil { + return "", err + } + row, err := exactlyOneCatalogRow(ctx, rows, fmt.Sprintf("UNSUPPORTED_ROUTINE_VERSION: Python revision %d", revision)) + if err != nil { + return "", err + } + body, err := row.GetString(ctx, 0, 0) + if err != nil { + return "", err + } + return body, nil +} + +func samePythonRoutineSignature(left, right function.PythonRoutineBody) bool { + if len(left.ArgTypes) != len(right.ArgTypes) { + return false + } + for index := range left.ArgTypes { + if left.ArgTypes[index] != right.ArgTypes[index] { + return false + } + } + return left.ReturnType != nil && right.ReturnType != nil && *left.ReturnType == *right.ReturnType +} + +func readPythonRevisionHead(ctx context.Context, bh BackgroundExec, functionID int64) (uint64, uint64, error) { + bh.ClearExecResultSet() + if err := bh.Exec(ctx, fmt.Sprintf( + "select active_revision, namespace_version from mo_catalog.mo_user_defined_function where function_id = %d;", + functionID, + )); err != nil { + return 0, 0, err + } + rows, err := getResultSet(ctx, bh) + if err != nil { + return 0, 0, err + } + row, err := exactlyOneCatalogRow(ctx, rows, fmt.Sprintf("function identity %d while publishing Python revision", functionID)) + if err != nil { + return 0, 0, err + } + revision, err := row.GetInt64(ctx, 0, 0) + if err != nil { + return 0, 0, err + } + namespace, err := row.GetInt64(ctx, 0, 1) + if err != nil { + return 0, 0, err + } + if revision < 0 || namespace < 0 { + return 0, 0, moerr.NewInvalidInputNoCtxf("function identity %d has invalid Python revision head", functionID) + } + return uint64(revision), uint64(namespace), nil +} + +func findPersistedFunctionID(ctx context.Context, bh BackgroundExec, definition userDefinedFunctionDefinition) (int64, error) { + bh.ClearExecResultSet() + query := fmt.Sprintf( + `select function_id from mo_catalog.mo_user_defined_function where name = "%s" and db = "%s" and canonical_input_descriptor = "%s" and language = '%s' order by function_id desc limit 1;`, + escapeSQLStringForDoubleQuotes(definition.name), + escapeSQLStringForDoubleQuotes(definition.dbName), + escapeSQLStringForDoubleQuotes(definition.canonicalInputDescriptor), + escapeSQLStringForDoubleQuotes(definition.lang), + ) + if err := bh.Exec(ctx, query); err != nil { + return 0, err + } + rows, err := getResultSet(ctx, bh) + if err != nil { + return 0, err + } + row, err := exactlyOneCatalogRow(ctx, rows, "Python function identity after creation") + if err != nil { + return 0, err + } + return row.GetInt64(ctx, 0, 0) +} + +func findPersistedSQLFunctionID(ctx context.Context, bh BackgroundExec, definition userDefinedFunctionDefinition) (int64, error) { + bh.ClearExecResultSet() + query := fmt.Sprintf( + `select function_id from mo_catalog.mo_user_defined_function where name = "%s" and db = "%s" and arg_types = %s and language = '%s' order by function_id desc limit 1;`, + escapeSQLStringForDoubleQuotes(definition.name), + escapeSQLStringForDoubleQuotes(definition.dbName), + quoteSQLStringLiteral(definition.argTypes), + escapeSQLStringForDoubleQuotes(definition.lang), + ) + if err := bh.Exec(ctx, query); err != nil { + return 0, err + } + rows, err := getResultSet(ctx, bh) + if err != nil { + return 0, err + } + row, err := exactlyOneCatalogRow(ctx, rows, "SQL function identity after creation") + if err != nil { + return 0, err + } + return row.GetInt64(ctx, 0, 0) +} + +func insertPythonRevision(ctx context.Context, bh BackgroundExec, definition userDefinedFunctionDefinition, functionID int64, revision, namespace uint64) error { + body, err := function.DecodePythonRoutineBody(definition.body) + if err != nil { + return moerr.NewInvalidInputNoCtxf("UNSUPPORTED_ROUTINE_VERSION: Python definition rejected before publication: %v", err) + } + fingerprint, err := function.PythonRoutineFingerprint(definition.body) + if err != nil { + return err + } + canonicalInput, _, _, err := function.PythonSignatureMetadata(body.ArgTypes, body.ReturnType) + if err != nil { + return moerr.NewInvalidInputNoCtxf("UNSUPPORTED_ROUTINE_VERSION: Python signature rejected before publication: %v", err) + } + if definition.canonicalInputDescriptor != "" && definition.canonicalInputDescriptor != canonicalInput { + return moerr.NewInvalidInputNoCtx("UNSUPPORTED_ROUTINE_VERSION: Python input descriptor does not match its definition") + } + quote := func(value string) string { return escapeSQLStringForDoubleQuotes(value) } + created := types.CurrentTimestamp().String2(time.UTC, 0) + sql := fmt.Sprintf(`insert into mo_catalog.mo_function_revisions( + function_id, revision, namespace_version, name, args, arg_types, rettype, body, + language, definition_schema_version, abi_contract, adapter_version, + artifact_digest, environment_digest, sdk_version, null_policy, volatility, + definition_fingerprint, created_time, security_type) values ( + %d, %d, %d, "%s", "%s", "%s", "%s", "%s", "%s", %d, "%s", "%s", "%s", "%s", "%s", "%s", "VOLATILE", "%s", "%s", "INVOKER");`, + functionID, revision, namespace, quote(definition.name), quote(definition.args), quote(canonicalInput), + quote(definition.retType), quote(definition.body), quote(definition.lang), body.DefinitionSchemaVersion, + quote(body.ABIContract), quote(body.AdapterVersion), quote(body.ArtifactDigest), quote(body.EnvironmentDigest), + quote(body.SDKVersion), quote(body.NullPolicy), quote(fingerprint), created, + ) + return bh.Exec(ctx, sql) +} + +func insertSQLRevision(ctx context.Context, bh BackgroundExec, definition userDefinedFunctionDefinition, functionID int64, revision, namespace uint64) error { + fingerprint, err := function.SQLRoutineFingerprint(definition.body, definition.argTypes, definition.retType) + if err != nil { + return moerr.NewInvalidInputNoCtxf("SQL definition rejected before publication: %v", err) + } + quote := func(value string) string { return escapeSQLStringForDoubleQuotes(value) } + created := types.CurrentTimestamp().String2(time.UTC, 0) + sql := fmt.Sprintf(`insert into mo_catalog.mo_function_revisions( + function_id, revision, namespace_version, name, args, arg_types, rettype, body, + language, definition_schema_version, abi_contract, adapter_version, + artifact_digest, environment_digest, sdk_version, null_policy, volatility, + definition_fingerprint, created_time, security_type) values ( + %d, %d, %d, "%s", "%s", "%s", "%s", "%s", "%s", %d, "", "", "", "", "", "%s", "VOLATILE", "%s", "%s", "DEFINER");`, + functionID, revision, namespace, quote(definition.name), quote(definition.args), + quote(definition.argTypes), quote(definition.retType), quote(definition.body), quote(definition.lang), + udf.SQLDefinitionSchemaVersion, quote(udf.NullCallHandler), quote(fingerprint), created, + ) + return bh.Exec(ctx, sql) +} + +func readFunctionRevisionHead(ctx context.Context, bh BackgroundExec, functionID int64) (uint64, uint64, error) { + bh.ClearExecResultSet() + if err := bh.Exec(ctx, fmt.Sprintf( + "select active_revision, namespace_version from mo_catalog.mo_user_defined_function where function_id = %d;", + functionID, + )); err != nil { + return 0, 0, err + } + rows, err := getResultSet(ctx, bh) + if err != nil { + return 0, 0, err + } + row, err := exactlyOneCatalogRow(ctx, rows, fmt.Sprintf("function identity %d while publishing a revision", functionID)) + if err != nil { + return 0, 0, err + } + revision, err := row.GetInt64(ctx, 0, 0) + if err != nil { + return 0, 0, err + } + namespace, err := row.GetInt64(ctx, 0, 1) + if err != nil { + return 0, 0, err + } + if revision < 0 || namespace < 0 { + return 0, 0, moerr.NewInvalidInputNoCtxf("function identity %d has an invalid revision head", functionID) + } + return uint64(revision), uint64(namespace), nil +} + +func activateFunctionRevision(ctx context.Context, bh BackgroundExec, functionID int64, revision, namespace uint64) error { + return bh.Exec(ctx, fmt.Sprintf( + "update mo_catalog.mo_user_defined_function set active_revision = %d, namespace_version = %d where function_id = %d;", + revision, namespace, functionID, + )) +} + +func activatePythonRevision(ctx context.Context, bh BackgroundExec, functionID int64, revision, namespace uint64) error { + return activateFunctionRevision(ctx, bh, functionID, revision, namespace) } // storedProcedureDefinition is the procedure metadata kept outside mo_tables. diff --git a/pkg/frontend/authenticate_test.go b/pkg/frontend/authenticate_test.go index 7a8ac420b18ea..43916620a8b0e 100644 --- a/pkg/frontend/authenticate_test.go +++ b/pkg/frontend/authenticate_test.go @@ -9981,6 +9981,30 @@ func Test_doDropFunctionIfExistsWithDifferentOverload(t *testing.T) { } } +func TestDeleteUserDefinedFunctionRevisionsIfPresent(t *testing.T) { + ctx := context.Background() + functionID := int64(91) + deleteSQL := fmt.Sprintf(deleteUserDefinedFunctionRevisionsFormat, functionID) + + t.Run("missing revision table is compatible with legacy SQL UDF drop", func(t *testing.T) { + bh := &backgroundExecTest{} + bh.init() + bh.sql2err[deleteSQL] = moerr.NewNoSuchTableNoCtx("mo_catalog", "mo_function_revisions") + + require.NoError(t, deleteUserDefinedFunctionRevisionsIfPresent(ctx, bh, functionID)) + require.Equal(t, []string{deleteSQL}, bh.executedSQLs) + }) + + t.Run("unexpected cleanup failure is not hidden", func(t *testing.T) { + bh := &backgroundExecTest{} + bh.init() + wantErr := errors.New("revision cleanup failed") + bh.sql2err[deleteSQL] = wantErr + + require.ErrorIs(t, deleteUserDefinedFunctionRevisionsIfPresent(ctx, bh, functionID), wantErr) + }) +} + func Test_doDropRole(t *testing.T) { convey.Convey("drop role succ", t, func() { ctrl := gomock.NewController(t) diff --git a/pkg/frontend/clone.go b/pkg/frontend/clone.go index 617ae66c23da8..0b3d11f271c5f 100644 --- a/pkg/frontend/clone.go +++ b/pkg/frontend/clone.go @@ -1147,6 +1147,22 @@ func handleCloneDatabaseWithSource( if err = validateCloneUserDefinedFunctions(source.userDefinedFuncs); err != nil { return } + for _, definition := range source.userDefinedFuncs { + if !strings.EqualFold(definition.lang, string(tree.PYTHON)) { + continue + } + // Database clone is a new Python publication in the target account. + // Apply the same runtime and catalog gates before creating the target + // database, so a disabled or partially upgraded node cannot leave a + // clone that contains an unrunnable routine. + if err = ensurePythonUdfRuntimeReady(reqCtx, ses); err != nil { + return + } + if err = ensurePythonUdfCatalogReady(reqCtx, bh); err != nil { + return + } + break + } fromAccountID := source.opAccountId if source.snapshot != nil && source.snapshot.Tenant != nil { fromAccountID = source.snapshot.Tenant.TenantID @@ -1387,6 +1403,7 @@ func handleCloneDatabaseWithSource( } if err = restoreCloneDatabaseUserDefinedFunctions( ctx1, bh, routineTenant, source.userDefinedFuncs, stmt.DstDatabase.String(), + getPu(ses.GetService()).FileService, ); err != nil { return } diff --git a/pkg/frontend/clone_database_source.go b/pkg/frontend/clone_database_source.go index cdbd9cdd6af61..9ed047eb11f93 100644 --- a/pkg/frontend/clone_database_source.go +++ b/pkg/frontend/clone_database_source.go @@ -25,13 +25,14 @@ import ( "github.com/matrixorigin/matrixone/pkg/catalog" "github.com/matrixorigin/matrixone/pkg/common/moerr" "github.com/matrixorigin/matrixone/pkg/defines" + "github.com/matrixorigin/matrixone/pkg/fileservice" "github.com/matrixorigin/matrixone/pkg/pb/plan" "github.com/matrixorigin/matrixone/pkg/sql/parsers" "github.com/matrixorigin/matrixone/pkg/sql/parsers/dialect" "github.com/matrixorigin/matrixone/pkg/sql/parsers/dialect/mysql" "github.com/matrixorigin/matrixone/pkg/sql/parsers/tree" "github.com/matrixorigin/matrixone/pkg/sql/plan/function" - "github.com/matrixorigin/matrixone/pkg/udf" + pythonudf "github.com/matrixorigin/matrixone/pkg/udf/python" ) type cloneDatabaseSource struct { @@ -680,17 +681,12 @@ func validateCloneUserDefinedFunctions(functions []userDefinedFunctionDefinition ) } if strings.EqualFold(definition.lang, string(tree.PYTHON)) { - var body function.PythonRoutineBody - if err := json.Unmarshal([]byte(definition.body), &body); err != nil || - body.Handler == "" || body.Source == "" || - (body.Mode != "SCALAR" && body.Mode != "VECTOR") || - (body.NullPolicy != udf.NullCallHandler && body.NullPolicy != udf.NullReturnNull) || - body.ABIContract != udf.PythonABIContract || body.AdapterVersion != udf.PythonAdapterVersion || - body.SDKVersion != udf.PythonSDKVersion { + if _, err := function.DecodePythonRoutineBody(definition.body); err != nil { return moerr.NewNotSupportedNoCtxf( - "CREATE DATABASE CLONE with incomplete %s function %s is not supported", + "CREATE DATABASE CLONE with unsupported %s function %s is not supported: %v", definition.lang, definition.name, + err, ) } } @@ -793,11 +789,35 @@ func restoreCloneDatabaseUserDefinedFunctions( tenant *TenantInfo, functions []userDefinedFunctionDefinition, dbName string, + artifactFileServices ...fileservice.FileService, ) error { - for _, function := range functions { - function.dbName = dbName + var artifactStore *pythonudf.FileArtifactStore + if len(artifactFileServices) > 0 && artifactFileServices[0] != nil { + var err error + artifactStore, err = pythonudf.NewFileArtifactStore( + artifactFileServices[0], + pythonudf.DefaultMaxArtifactBytes, + ) + if err != nil { + return err + } + } + for _, definition := range functions { + definition.dbName = dbName + if strings.EqualFold(definition.lang, string(tree.PYTHON)) { + if artifactStore == nil { + return moerr.NewNotSupportedNoCtx("Python artifact store is required to clone a current Python routine") + } + body, err := function.DecodePythonRoutineBody(definition.body) + if err != nil { + return moerr.NewInvalidInputNoCtxf("UNSUPPORTED_ROUTINE_VERSION: Python clone source is invalid: %v", err) + } + if _, err := artifactStore.Publish(ctx, uint64(tenant.GetTenantID()), body.Handler, body.Source); err != nil { + return err + } + } if err := persistUserDefinedFunction( - ctx, bh, tenant, tenant.GetDefaultRoleID(), function, nil, + ctx, bh, tenant, tenant.GetDefaultRoleID(), definition, nil, ); err != nil { return err } diff --git a/pkg/frontend/clone_database_source_test.go b/pkg/frontend/clone_database_source_test.go index 55d78004f6bbc..01a0e16ece6c3 100644 --- a/pkg/frontend/clone_database_source_test.go +++ b/pkg/frontend/clone_database_source_test.go @@ -48,6 +48,14 @@ type erroringBackgroundExec struct { err error } +func configureSQLFunctionIdentityLookup(bh *backgroundExecTest, functionID int64) { + bh.beforeExec = func(sql string) { + if strings.HasPrefix(sql, "select function_id from mo_catalog.mo_user_defined_function where name = ") { + bh.sql2result[sql] = newMrsForPasswordOfUser([][]interface{}{{functionID, nil, nil}}) + } + } +} + func (bt *erroringBackgroundExec) Exec(ctx context.Context, sql string) error { if err := bt.backgroundExecTest.Exec(ctx, sql); err != nil { return err @@ -172,7 +180,7 @@ func TestUdfCatalogLookupUsesSnapshot(t *testing.T) { require.NoError(t, err) require.Equal(t, uint32(7), accountID) require.Equal(t, - `select args, body, language, rettype, db, modified_time, sql_mode from mo_catalog.mo_user_defined_function {MO_TS = 42} where name = "f_snapshot" and db = "source_db";`, + `select function_id, args, body, language, rettype, db, modified_time, sql_mode from mo_catalog.mo_user_defined_function {MO_TS = 42} where name = "f_snapshot" and db = "source_db";`, sql, ) @@ -180,11 +188,25 @@ func TestUdfCatalogLookupUsesSnapshot(t *testing.T) { _, err = defines.GetAccountId(queryCtx) require.Error(t, err) require.Equal(t, - `select args, body, language, rettype, db, modified_time, sql_mode from mo_catalog.mo_user_defined_function where name = "f_live" and db = "live_db";`, + `select function_id, args, body, language, rettype, db, modified_time, sql_mode from mo_catalog.mo_user_defined_function where name = "f_live" and db = "live_db";`, sql, ) } +func TestPythonRevisionCatalogLookupUsesSameSnapshotForBothTables(t *testing.T) { + snapshot := &plan.Snapshot{TS: ×tamp.Timestamp{PhysicalTime: 99}} + query := pythonRevisionCatalogSQL(snapshot, 41) + require.Contains(t, query, "from mo_catalog.mo_user_defined_function {MO_TS = 99} f") + require.Contains(t, query, "join mo_catalog.mo_function_revisions {MO_TS = 99} r") + require.Contains(t, query, "r.namespace_version = f.namespace_version") + require.Contains(t, query, "where f.function_id = 41;") + + liveQuery := pythonRevisionCatalogSQL(nil, 41) + require.Contains(t, liveQuery, "from mo_catalog.mo_user_defined_function f") + require.Contains(t, liveQuery, "join mo_catalog.mo_function_revisions r") + require.Contains(t, liveQuery, "r.namespace_version = f.namespace_version") +} + func TestGetCloneDatabaseRoutineInfosRespectsSubscriptionBoundary(t *testing.T) { t.Run("database source collects functions and procedures", func(t *testing.T) { const dbName = "source_db" @@ -295,19 +317,26 @@ func TestRestoreCloneDatabaseUserDefinedFunctions(t *testing.T) { bh := &backgroundExecTest{} bh.init() + bh.sql2result[functionRevisionCatalogSchemaCheck] = emptyCatalogProbeResult(20) + configureSQLFunctionIdentityLookup(bh, 1) require.NoError(t, restoreCloneDatabaseUserDefinedFunctions(ctx, bh, tenant, []userDefinedFunctionDefinition{function}, "target_db")) - require.Len(t, bh.executedSQLs, 1) - require.Contains(t, bh.executedSQLs[0], "insert into mo_catalog.mo_user_defined_function") - require.Contains(t, bh.executedSQLs[0], "\"f_answer\",2") - require.Contains(t, bh.executedSQLs[0], "\"target_db\"") - require.Contains(t, bh.executedSQLs[0], "\"root1\"") + require.Len(t, bh.executedSQLs, 5) + require.Equal(t, functionRevisionCatalogSchemaCheck, bh.executedSQLs[0]) + require.Contains(t, bh.executedSQLs[1], "insert into mo_catalog.mo_user_defined_function") + require.Contains(t, bh.executedSQLs[1], "\"f_answer\",2") + require.Contains(t, bh.executedSQLs[1], "\"target_db\"") + require.Contains(t, bh.executedSQLs[1], "\"root1\"") + require.Contains(t, bh.executedSQLs[2], "select function_id from mo_catalog.mo_user_defined_function") + require.Contains(t, bh.executedSQLs[3], "insert into mo_catalog.mo_function_revisions") + require.Contains(t, bh.executedSQLs[4], "update mo_catalog.mo_user_defined_function set active_revision") // SQL literal quoting is an implementation detail of EscapeFormat; preserve // the behavior under test rather than coupling this regression to its style. - require.Contains(t, bh.executedSQLs[0], "PIPES_AS_CONCAT") + require.Contains(t, bh.executedSQLs[1], "PIPES_AS_CONCAT") require.NotContains(t, bh.executedSQLs, "begin;") failingBase := &backgroundExecTest{} failingBase.init() + failingBase.sql2result[functionRevisionCatalogSchemaCheck] = emptyCatalogProbeResult(20) wantErr := errors.New("function persistence failed") failing := &erroringBackgroundExec{backgroundExecTest: failingBase, err: wantErr} require.ErrorIs(t, restoreCloneDatabaseUserDefinedFunctions(ctx, failing, tenant, []userDefinedFunctionDefinition{function}, "target_db"), wantErr) diff --git a/pkg/frontend/clone_test.go b/pkg/frontend/clone_test.go index 64878230ac9fb..939816f7813f9 100644 --- a/pkg/frontend/clone_test.go +++ b/pkg/frontend/clone_test.go @@ -957,6 +957,8 @@ func TestHandleCloneDatabaseWithSourceRestoresRoutines(t *testing.T) { t.Cleanup(ses.Close) bh := &backgroundExecTest{} bh.init() + bh.sql2result[functionRevisionCatalogSchemaCheck] = emptyCatalogProbeResult(20) + configureSQLFunctionIdentityLookup(bh, 1) checkSQL := getSqlForCheckProcedureExistence("p_answer", "destination") bh.sql2result[checkSQL] = newMrsForPasswordOfUser(nil) @@ -965,11 +967,15 @@ func TestHandleCloneDatabaseWithSourceRestoresRoutines(t *testing.T) { ses, bh, newStatement(), newSource(), ) require.NoError(t, err) - require.Len(t, bh.executedSQLs, 4) + require.Len(t, bh.executedSQLs, 8) require.Equal(t, "create database `destination`", bh.executedSQLs[0]) - require.Contains(t, bh.executedSQLs[1], "insert into mo_catalog.mo_user_defined_function") - require.Equal(t, checkSQL, bh.executedSQLs[2]) - require.Contains(t, bh.executedSQLs[3], "insert into mo_catalog.mo_stored_procedure") + require.Equal(t, functionRevisionCatalogSchemaCheck, bh.executedSQLs[1]) + require.Contains(t, bh.executedSQLs[2], "insert into mo_catalog.mo_user_defined_function") + require.Contains(t, bh.executedSQLs[3], "select function_id from mo_catalog.mo_user_defined_function") + require.Contains(t, bh.executedSQLs[4], "insert into mo_catalog.mo_function_revisions") + require.Contains(t, bh.executedSQLs[5], "update mo_catalog.mo_user_defined_function set active_revision") + require.Equal(t, checkSQL, bh.executedSQLs[6]) + require.Contains(t, bh.executedSQLs[7], "insert into mo_catalog.mo_stored_procedure") }) t.Run("propagates procedure restoration failures", func(t *testing.T) { @@ -978,6 +984,8 @@ func TestHandleCloneDatabaseWithSourceRestoresRoutines(t *testing.T) { t.Cleanup(ses.Close) bh := &backgroundExecTest{} bh.init() + bh.sql2result[functionRevisionCatalogSchemaCheck] = emptyCatalogProbeResult(20) + configureSQLFunctionIdentityLookup(bh, 1) checkSQL := getSqlForCheckProcedureExistence("p_answer", "destination") wantErr := errors.New("procedure lookup failed") bh.sql2err[checkSQL] = wantErr @@ -987,10 +995,14 @@ func TestHandleCloneDatabaseWithSourceRestoresRoutines(t *testing.T) { ses, bh, newStatement(), newSource(), ) require.ErrorIs(t, err, wantErr) - require.Len(t, bh.executedSQLs, 3) + require.Len(t, bh.executedSQLs, 7) require.Equal(t, "create database `destination`", bh.executedSQLs[0]) - require.Contains(t, bh.executedSQLs[1], "insert into mo_catalog.mo_user_defined_function") - require.Equal(t, checkSQL, bh.executedSQLs[2]) + require.Equal(t, functionRevisionCatalogSchemaCheck, bh.executedSQLs[1]) + require.Contains(t, bh.executedSQLs[2], "insert into mo_catalog.mo_user_defined_function") + require.Contains(t, bh.executedSQLs[3], "select function_id from mo_catalog.mo_user_defined_function") + require.Contains(t, bh.executedSQLs[4], "insert into mo_catalog.mo_function_revisions") + require.Contains(t, bh.executedSQLs[5], "update mo_catalog.mo_user_defined_function set active_revision") + require.Equal(t, checkSQL, bh.executedSQLs[6]) }) t.Run("rejects imported functions before target database creation", func(t *testing.T) { diff --git a/pkg/frontend/compiler_context.go b/pkg/frontend/compiler_context.go index a211a61c4aa18..9e42516d8a60d 100644 --- a/pkg/frontend/compiler_context.go +++ b/pkg/frontend/compiler_context.go @@ -43,6 +43,7 @@ import ( plan2 "github.com/matrixorigin/matrixone/pkg/sql/plan" "github.com/matrixorigin/matrixone/pkg/sql/plan/function" "github.com/matrixorigin/matrixone/pkg/sql/util" + "github.com/matrixorigin/matrixone/pkg/udf" v2 "github.com/matrixorigin/matrixone/pkg/util/metric/v2" "github.com/matrixorigin/matrixone/pkg/util/trace/impl/motrace/statistic" "github.com/matrixorigin/matrixone/pkg/vm/engine" @@ -841,14 +842,18 @@ func (tcc *TxnCompilerContext) ResolveUdf(name string, args []*plan.Expr) (udf * if err != nil { return nil, err } + if len(erArray) > 1 || (len(erArray) == 1 && erArray[0] == nil) { + return nil, fmt.Errorf("UNSUPPORTED_ROUTINE_VERSION: UDF %s lookup returned %d result sets, expected one or an empty candidate result", name, len(erArray)) + } if execResultArrayHasData(erArray) { fromList := make([]types.Type, len(args)) for i, arg := range args { fromList[i] = types.Type{ - Oid: types.T(arg.Typ.Id), - Width: arg.Typ.Width, - Scale: arg.Typ.Scale, + Oid: types.T(arg.Typ.Id), + Charset: uint8(arg.Typ.Charset), + Width: arg.Typ.Width, + Scale: arg.Typ.Scale, } argTypeStr += strings.ToLower(fromList[i].String()) @@ -866,38 +871,72 @@ func (tcc *TxnCompilerContext) ResolveUdf(name string, args []*plan.Expr) (udf * matchedList := make([]*MatchUdf, 0) for i := uint64(0); i < erArray[0].GetRowCount(); i++ { - argstr, err = erArray[0].GetString(ctx, i, 0) + functionID, getErr := erArray[0].GetInt64(ctx, i, 0) + if getErr != nil { + return nil, getErr + } + argstr, err = erArray[0].GetString(ctx, i, 1) if err != nil { return nil, err } udf = &function.Udf{} - udf.Body, err = erArray[0].GetString(ctx, i, 1) + udf.Body, err = erArray[0].GetString(ctx, i, 2) if err != nil { return nil, err } - udf.Language, err = erArray[0].GetString(ctx, i, 2) + udf.Language, err = erArray[0].GetString(ctx, i, 3) if err != nil { return nil, err } - udf.RetType, err = erArray[0].GetString(ctx, i, 3) + udf.RetType, err = erArray[0].GetString(ctx, i, 4) if err != nil { return nil, err } - udf.Db, err = erArray[0].GetString(ctx, i, 4) + udf.Db, err = erArray[0].GetString(ctx, i, 5) if err != nil { return nil, err } - udf.ModifiedTime, err = erArray[0].GetString(ctx, i, 5) + udf.ModifiedTime, err = erArray[0].GetString(ctx, i, 6) if err != nil { return nil, err } udf.ModifiedTime = strings.ReplaceAll(udf.ModifiedTime, " ", "_") udf.ModifiedTime = strings.ReplaceAll(udf.ModifiedTime, ":", "-") - mode, getErr := erArray[0].GetString(ctx, i, 6) + mode, getErr := erArray[0].GetString(ctx, i, 7) if getErr != nil { return nil, getErr } udf.SQLMode = &mode + udf.FunctionID = functionID + if strings.EqualFold(udf.Language, string(tree.PYTHON)) { + revision, revisionErr := readPythonRevision(queryCtx, bh, functionID, tcc.GetSnapshot()) + if revisionErr != nil { + return nil, revisionErr + } + argstr = revision.Args + udf.Body = revision.Body + udf.Language = revision.Language + udf.RetType = revision.RetType + udf.Revision = revision.Revision + udf.NamespaceVersion = revision.NamespaceVersion + } else if strings.EqualFold(udf.Language, string(tree.SQL)) { + revision, found, revisionErr := readSQLRevision(queryCtx, bh, functionID, tcc.GetSnapshot()) + if revisionErr != nil { + return nil, revisionErr + } + if found { + argstr = revision.Args + udf.Body = revision.Body + udf.Language = revision.Language + udf.RetType = revision.RetType + udf.Revision = revision.Revision + udf.NamespaceVersion = revision.NamespaceVersion + udf.DefinitionFingerprint = revision.DefinitionFingerprint + udf.SemanticDefinitionSchemaVersion = int(revision.DefinitionSchema) + udf.Volatility = revision.Volatility + udf.NullPolicy = revision.NullPolicy + } + } if err = udf.LoadPythonTypeContract(); err != nil { return nil, err } @@ -914,10 +953,19 @@ func (tcc *TxnCompilerContext) ResolveUdf(name string, args []*plan.Expr) (udf * if err = udf.ValidatePythonTypeContract(); err != nil { return nil, err } + if err = udf.ValidatePythonCatalogSignature(); err != nil { + return nil, err + } toList := make([]types.T, len(args)) + pythonTargetTypes := make([]types.Type, 0) + if strings.EqualFold(udf.Language, string(tree.PYTHON)) { + pythonTargetTypes = udf.GetArgsType() + } for j := range argList { - if fromList[j].IsDecimal() && argList[j].Type == "decimal" { + if len(pythonTargetTypes) != 0 { + toList[j] = pythonTargetTypes[j].Oid + } else if fromList[j].IsDecimal() && argList[j].Type == "decimal" { toList[j] = fromList[j].Oid } else { toList[j] = types.Types[argList[j].Type] @@ -925,6 +973,9 @@ func (tcc *TxnCompilerContext) ResolveUdf(name string, args []*plan.Expr) (udf * } canCast, cost := function.UdfArgTypeMatch(fromList, toList) + if len(pythonTargetTypes) != 0 { + canCast, cost = function.PythonUdfArgTypeMatch(fromList, pythonTargetTypes) + } if !canCast { // mismatch continue } @@ -952,7 +1003,26 @@ func (tcc *TxnCompilerContext) ResolveUdf(name string, args []*plan.Expr) (udf * } if matchNum == 1 { - matchedList[0].Udf.ArgsType = function.UdfArgTypeCast(fromList, matchedList[0].TypeList) + if strings.EqualFold(matchedList[0].Udf.Language, string(tree.PYTHON)) { + matchedList[0].Udf.ArgsType = function.PythonUdfArgTypeCast(fromList, matchedList[0].Udf.GetArgsType()) + if len(matchedList[0].Udf.ArgsType) != len(fromList) { + return nil, fmt.Errorf("python udf: argument descriptor count changed during resolution") + } + } else { + matchedList[0].Udf.ArgsType = function.UdfArgTypeCast(fromList, matchedList[0].TypeList) + } + if matchedList[0].Udf.Revision != 0 { + accountID, accountErr := tcc.GetAccountId() + if accountErr != nil { + return nil, accountErr + } + databaseID, databaseErr := tcc.GetDatabaseId(matchedList[0].Udf.Db, tcc.GetSnapshot()) + if databaseErr != nil { + return nil, fmt.Errorf("python routine database identity: %w", databaseErr) + } + matchedList[0].Udf.AccountID = uint64(accountID) + matchedList[0].Udf.DatabaseID = databaseID + } return matchedList[0].Udf, err } @@ -982,13 +1052,407 @@ func udfCatalogLookup( } } return ctx, fmt.Sprintf( - `select args, body, language, rettype, db, modified_time, sql_mode from %s where name = "%s" and db = "%s";`, + `select function_id, args, body, language, rettype, db, modified_time, sql_mode from %s where name = "%s" and db = "%s";`, catalogTable, name, database, ) } +// exactlyOneCatalogResultSet is for catalog reads that must be interpreted +// atomically. A BackgroundExec result array is a transport shape, not a +// guarantee that a single SELECT produced one result set. Treating the first +// result set as authoritative would let a split or malformed reader hide +// catalog corruption from the planner/cache validator. +func exactlyOneCatalogResultSet(ctx context.Context, rows []ExecResult, label string) (ExecResult, error) { + if len(rows) != 1 || rows[0] == nil { + return nil, fmt.Errorf("UNSUPPORTED_ROUTINE_VERSION: %s returned %d result sets, expected exactly one", label, len(rows)) + } + return rows[0], nil +} + +// candidateCatalogResultSet is used only for a name/signature lookup. The +// execution engine represents a SELECT with no matching rows as an empty +// result-set slice, and that is the normal state before the first CREATE. +// Multiple result sets or a nil result remain malformed and fail closed. +func candidateCatalogResultSet(rows []ExecResult, label string) (ExecResult, error) { + if len(rows) == 0 { + return nil, nil + } + return exactlyOneCatalogResultSet(context.Background(), rows, label) +} + +// exactlyOneCatalogRow is for identity and revision point reads. A catalog +// query with a function_id/revision predicate is expected to return one +// logical row in one result set. Treating the first row as authoritative +// would turn duplicate or split result sets caused by corruption, restore, or +// a malformed reader into an arbitrary executable contract. +func exactlyOneCatalogRow(ctx context.Context, rows []ExecResult, label string) (ExecResult, error) { + result, err := exactlyOneCatalogResultSet(ctx, rows, label) + if err != nil { + return nil, err + } + rowCount := result.GetRowCount() + if rowCount != 1 { + return nil, fmt.Errorf("UNSUPPORTED_ROUTINE_VERSION: %s returned %d rows, expected exactly one", label, rowCount) + } + return result, nil +} + +type pythonRevisionCatalogRow struct { + Args string + ArgTypes string + Body string + Language string + RetType string + Revision uint64 + NamespaceVersion uint64 + CanonicalInputDescriptor string + ReturnDescriptor string + SignatureKeySchemaVersion int64 + SignatureFingerprint string + DefinitionSchema int64 + ABIContract string + AdapterVersion string + SDKVersion string + NullPolicy string + Volatility string + DefinitionFingerprint string + ArtifactDigest string + EnvironmentDigest string + SecurityType string +} + +type sqlRevisionCatalogRow struct { + Args string + ArgTypes string + Body string + Language string + RetType string + Revision uint64 + NamespaceVersion uint64 + DefinitionSchema int64 + DefinitionFingerprint string + Volatility string + NullPolicy string + SecurityType string +} + +// readSQLRevision resolves the current shared revision head without making a +// legacy SQL row look current. A missing v4_0_7 table/column is reported as +// found=false so ordinary SQL remains available during a rolling upgrade; +// once a head is published, every missing or malformed revision is a hard +// catalog error. +func readSQLRevision( + ctx context.Context, + bh BackgroundExec, + functionID int64, + snapshot *plan2.Snapshot, +) (sqlRevisionCatalogRow, bool, error) { + baseTable := "mo_catalog.mo_user_defined_function" + revisionTable := "mo_catalog.mo_function_revisions" + if snapshot != nil && snapshot.TS != nil { + suffix := fmt.Sprintf(" {MO_TS = %d}", snapshot.TS.PhysicalTime) + baseTable += suffix + revisionTable += suffix + } + + bh.ClearExecResultSet() + if err := bh.Exec(ctx, fmt.Sprintf( + "select active_revision, namespace_version, security_type from %s where function_id = %d;", + baseTable, functionID, + )); err != nil { + if isMissingCatalogObjectError(err) { + return sqlRevisionCatalogRow{}, false, nil + } + return sqlRevisionCatalogRow{}, false, fmt.Errorf("UNSUPPORTED_ROUTINE_VERSION: SQL function %d revision head is unavailable: %w", functionID, err) + } + rows, err := getResultSet(ctx, bh) + if err != nil { + return sqlRevisionCatalogRow{}, false, err + } + row, err := exactlyOneCatalogRow(ctx, rows, fmt.Sprintf("SQL function %d revision head", functionID)) + if err != nil { + return sqlRevisionCatalogRow{}, false, err + } + activeRevision, err := row.GetInt64(ctx, 0, 0) + if err != nil { + return sqlRevisionCatalogRow{}, false, err + } + namespaceVersion, err := row.GetInt64(ctx, 0, 1) + if err != nil { + return sqlRevisionCatalogRow{}, false, err + } + baseSecurityType, err := row.GetString(ctx, 0, 2) + if err != nil { + return sqlRevisionCatalogRow{}, false, err + } + if activeRevision == 0 && namespaceVersion == 0 { + return sqlRevisionCatalogRow{}, false, nil + } + if activeRevision <= 0 || namespaceVersion <= 0 { + return sqlRevisionCatalogRow{}, false, fmt.Errorf("UNSUPPORTED_ROUTINE_VERSION: SQL function %d has an invalid revision head", functionID) + } + + bh.ClearExecResultSet() + if err := bh.Exec(ctx, fmt.Sprintf( + "select revision, args, arg_types, body, rettype, language, "+ + "definition_schema_version, definition_fingerprint, volatility, null_policy, security_type "+ + "from %s where function_id = %d and revision = %d and namespace_version = %d;", + revisionTable, functionID, activeRevision, namespaceVersion, + )); err != nil { + if isMissingCatalogObjectError(err) { + return sqlRevisionCatalogRow{}, false, fmt.Errorf("UNSUPPORTED_ROUTINE_VERSION: SQL function %d revision table is unavailable: %w", functionID, err) + } + return sqlRevisionCatalogRow{}, false, fmt.Errorf("UNSUPPORTED_ROUTINE_VERSION: SQL function %d revision is unavailable: %w", functionID, err) + } + rows, err = getResultSet(ctx, bh) + if err != nil { + return sqlRevisionCatalogRow{}, false, err + } + row, err = exactlyOneCatalogRow(ctx, rows, fmt.Sprintf("SQL function %d active revision", functionID)) + if err != nil { + return sqlRevisionCatalogRow{}, false, err + } + getString := func(column uint64) (string, error) { return row.GetString(ctx, 0, column) } + revisionValue, err := row.GetInt64(ctx, 0, 0) + if err != nil { + return sqlRevisionCatalogRow{}, false, err + } + args, err := getString(1) + if err != nil { + return sqlRevisionCatalogRow{}, false, err + } + argTypes, err := getString(2) + if err != nil { + return sqlRevisionCatalogRow{}, false, err + } + body, err := getString(3) + if err != nil { + return sqlRevisionCatalogRow{}, false, err + } + retType, err := getString(4) + if err != nil { + return sqlRevisionCatalogRow{}, false, err + } + language, err := getString(5) + if err != nil { + return sqlRevisionCatalogRow{}, false, err + } + definitionSchema, err := row.GetInt64(ctx, 0, 6) + if err != nil { + return sqlRevisionCatalogRow{}, false, err + } + fingerprint, err := getString(7) + if err != nil { + return sqlRevisionCatalogRow{}, false, err + } + volatility, err := getString(8) + if err != nil { + return sqlRevisionCatalogRow{}, false, err + } + nullPolicy, err := getString(9) + if err != nil { + return sqlRevisionCatalogRow{}, false, err + } + securityType, err := getString(10) + if err != nil { + return sqlRevisionCatalogRow{}, false, err + } + if revisionValue <= 0 || uint64(revisionValue) != uint64(activeRevision) || + !strings.EqualFold(language, string(tree.SQL)) || + definitionSchema != udf.SQLDefinitionSchemaVersion || + volatility != "VOLATILE" || nullPolicy != udf.NullCallHandler || + !strings.EqualFold(securityType, "DEFINER") || + !strings.EqualFold(baseSecurityType, securityType) { + return sqlRevisionCatalogRow{}, false, fmt.Errorf("UNSUPPORTED_ROUTINE_VERSION: SQL function %d revision contract is not supported", functionID) + } + logicalArgTypes, err := userDefinedFunctionArgumentTypesFromJSON(args) + if err != nil || logicalArgTypes != argTypes { + return sqlRevisionCatalogRow{}, false, fmt.Errorf("UNSUPPORTED_ROUTINE_VERSION: SQL function %d revision argument metadata is invalid", functionID) + } + expectedFingerprint, err := function.SQLRoutineFingerprint(body, argTypes, retType) + if err != nil || fingerprint == "" || fingerprint != expectedFingerprint { + return sqlRevisionCatalogRow{}, false, fmt.Errorf("UNSUPPORTED_ROUTINE_VERSION: SQL function %d revision fingerprint mismatch", functionID) + } + return sqlRevisionCatalogRow{ + Args: args, ArgTypes: argTypes, Body: body, Language: language, RetType: retType, + Revision: uint64(revisionValue), NamespaceVersion: uint64(namespaceVersion), + DefinitionSchema: definitionSchema, DefinitionFingerprint: fingerprint, + Volatility: volatility, NullPolicy: nullPolicy, SecurityType: securityType, + }, true, nil +} + +func readPythonRevision(ctx context.Context, bh BackgroundExec, functionID int64, snapshot *plan2.Snapshot) (pythonRevisionCatalogRow, error) { + bh.ClearExecResultSet() + query := pythonRevisionCatalogSQL(snapshot, functionID) + if err := bh.Exec(ctx, query); err != nil { + return pythonRevisionCatalogRow{}, fmt.Errorf("UNSUPPORTED_ROUTINE_VERSION: Python catalog revision is unavailable: %w", err) + } + rows, err := getResultSet(ctx, bh) + if err != nil { + return pythonRevisionCatalogRow{}, err + } + row, err := exactlyOneCatalogRow(ctx, rows, fmt.Sprintf("Python function %d active revision", functionID)) + if err != nil { + return pythonRevisionCatalogRow{}, err + } + activeRevision, err := row.GetInt64(ctx, 0, 0) + if err != nil { + return pythonRevisionCatalogRow{}, err + } + namespaceVersion, err := row.GetInt64(ctx, 0, 1) + if err != nil { + return pythonRevisionCatalogRow{}, err + } + revision, err := row.GetInt64(ctx, 0, 2) + if err != nil { + return pythonRevisionCatalogRow{}, err + } + if activeRevision <= 0 || namespaceVersion <= 0 || revision != activeRevision { + return pythonRevisionCatalogRow{}, fmt.Errorf("UNSUPPORTED_ROUTINE_VERSION: Python function %d has an invalid revision head", functionID) + } + getString := func(column uint64) (string, error) { return row.GetString(ctx, 0, column) } + canonicalInput, err := getString(3) + if err != nil { + return pythonRevisionCatalogRow{}, err + } + returnDescriptor, err := getString(4) + if err != nil { + return pythonRevisionCatalogRow{}, err + } + signatureKeySchemaVersion, err := row.GetInt64(ctx, 0, 5) + if err != nil { + return pythonRevisionCatalogRow{}, err + } + signatureFingerprint, err := getString(6) + if err != nil { + return pythonRevisionCatalogRow{}, err + } + args, err := getString(7) + if err != nil { + return pythonRevisionCatalogRow{}, err + } + argTypes, err := getString(8) + if err != nil { + return pythonRevisionCatalogRow{}, err + } + body, err := getString(9) + if err != nil { + return pythonRevisionCatalogRow{}, err + } + language, err := getString(10) + if err != nil { + return pythonRevisionCatalogRow{}, err + } + retType, err := getString(11) + if err != nil { + return pythonRevisionCatalogRow{}, err + } + definitionSchema, err := row.GetInt64(ctx, 0, 12) + if err != nil { + return pythonRevisionCatalogRow{}, err + } + abi, err := getString(13) + if err != nil { + return pythonRevisionCatalogRow{}, err + } + adapter, err := getString(14) + if err != nil { + return pythonRevisionCatalogRow{}, err + } + sdk, err := getString(15) + if err != nil { + return pythonRevisionCatalogRow{}, err + } + nullPolicy, err := getString(16) + if err != nil { + return pythonRevisionCatalogRow{}, err + } + volatility, err := getString(17) + if err != nil { + return pythonRevisionCatalogRow{}, err + } + fingerprint, err := getString(18) + if err != nil { + return pythonRevisionCatalogRow{}, err + } + artifact, err := getString(19) + if err != nil { + return pythonRevisionCatalogRow{}, err + } + environment, err := getString(20) + if err != nil { + return pythonRevisionCatalogRow{}, err + } + securityType, err := getString(21) + if err != nil { + return pythonRevisionCatalogRow{}, err + } + baseSecurityType, err := getString(22) + if err != nil { + return pythonRevisionCatalogRow{}, err + } + if !strings.EqualFold(language, string(tree.PYTHON)) || definitionSchema != udf.PythonDefinitionSchemaVersion || abi != udf.PythonABIContract || adapter != udf.PythonAdapterVersion || sdk != udf.PythonSDKVersion || volatility != "VOLATILE" || !strings.EqualFold(securityType, "INVOKER") || !strings.EqualFold(baseSecurityType, securityType) { + return pythonRevisionCatalogRow{}, fmt.Errorf("UNSUPPORTED_ROUTINE_VERSION: Python function %d revision contract is not supported", functionID) + } + bodyDefinition, err := function.DecodePythonRoutineBody(body) + if err != nil { + return pythonRevisionCatalogRow{}, fmt.Errorf("UNSUPPORTED_ROUTINE_VERSION: Python function %d revision body is invalid: %w", functionID, err) + } + if bodyDefinition.ABIContract != abi || bodyDefinition.AdapterVersion != adapter || bodyDefinition.SDKVersion != sdk || bodyDefinition.NullPolicy != nullPolicy || bodyDefinition.ArtifactDigest != artifact || bodyDefinition.EnvironmentDigest != environment { + return pythonRevisionCatalogRow{}, fmt.Errorf("UNSUPPORTED_ROUTINE_VERSION: Python function %d revision metadata does not match its definition", functionID) + } + actualFingerprint, err := function.PythonRoutineFingerprint(body) + if err != nil { + return pythonRevisionCatalogRow{}, fmt.Errorf("UNSUPPORTED_ROUTINE_VERSION: Python function %d revision fingerprint cannot be computed: %w", functionID, err) + } + if fingerprint == "" || fingerprint != actualFingerprint { + return pythonRevisionCatalogRow{}, fmt.Errorf("UNSUPPORTED_ROUTINE_VERSION: Python function %d revision fingerprint mismatch", functionID) + } + expectedInput, expectedReturn, expectedSignature, err := function.PythonSignatureMetadata(bodyDefinition.ArgTypes, bodyDefinition.ReturnType) + if err != nil { + return pythonRevisionCatalogRow{}, fmt.Errorf("UNSUPPORTED_ROUTINE_VERSION: Python function %d signature cannot be computed: %w", functionID, err) + } + if signatureKeySchemaVersion != udf.PythonSignatureKeySchemaVersion || + canonicalInput != expectedInput || returnDescriptor != expectedReturn || signatureFingerprint != expectedSignature { + return pythonRevisionCatalogRow{}, fmt.Errorf("UNSUPPORTED_ROUTINE_VERSION: Python function %d identity descriptor metadata mismatch", functionID) + } + if argTypes != expectedInput { + return pythonRevisionCatalogRow{}, fmt.Errorf("UNSUPPORTED_ROUTINE_VERSION: Python function %d revision argument descriptor mismatch", functionID) + } + return pythonRevisionCatalogRow{ + Args: args, ArgTypes: argTypes, Body: body, Language: language, RetType: retType, + Revision: uint64(revision), NamespaceVersion: uint64(namespaceVersion), + CanonicalInputDescriptor: canonicalInput, ReturnDescriptor: returnDescriptor, + SignatureKeySchemaVersion: signatureKeySchemaVersion, SignatureFingerprint: signatureFingerprint, + DefinitionSchema: definitionSchema, ABIContract: abi, AdapterVersion: adapter, + SDKVersion: sdk, NullPolicy: nullPolicy, Volatility: volatility, DefinitionFingerprint: fingerprint, + ArtifactDigest: artifact, EnvironmentDigest: environment, SecurityType: securityType, + }, nil +} + +func pythonRevisionCatalogSQL(snapshot *plan2.Snapshot, functionID int64) string { + userFunctionTable := "mo_catalog.mo_user_defined_function" + revisionTable := "mo_catalog.mo_function_revisions" + if snapshot != nil && snapshot.TS != nil { + userFunctionTable += fmt.Sprintf(" {MO_TS = %d}", snapshot.TS.PhysicalTime) + revisionTable += fmt.Sprintf(" {MO_TS = %d}", snapshot.TS.PhysicalTime) + } + return fmt.Sprintf(`select f.active_revision, f.namespace_version, r.revision, + f.canonical_input_descriptor, f.return_descriptor, + f.signature_key_schema_version, f.signature_fingerprint, + r.args, r.arg_types, r.body, r.language, r.rettype, r.definition_schema_version, + r.abi_contract, r.adapter_version, r.sdk_version, r.null_policy, r.volatility, + r.definition_fingerprint, r.artifact_digest, r.environment_digest, + r.security_type, f.security_type + from %s f + join %s r + on r.function_id = f.function_id and r.revision = f.active_revision + and r.namespace_version = f.namespace_version + where f.function_id = %d;`, userFunctionTable, revisionTable, functionID) +} + func (tcc *TxnCompilerContext) ResolveVariable(varName string, isSystemVar, isGlobalVar bool) (varValue interface{}, err error) { stats := statistic.StatsInfoFromContext(tcc.execCtx.reqCtx) start := time.Now() diff --git a/pkg/frontend/computation_wrapper.go b/pkg/frontend/computation_wrapper.go index cc41c18f98e5e..07402b4f69064 100644 --- a/pkg/frontend/computation_wrapper.go +++ b/pkg/frontend/computation_wrapper.go @@ -1430,6 +1430,11 @@ func initExecuteStmtParamWithResolverInSession( currentDDLVersion := owner.getDDLVersion() change := prepareStmt.tempTableVersion != currentTempTableVersion || prepareStmt.ddlVersion != currentDDLVersion + routineChanged, routineErr := validateRoutinePlanDependencies(reqCtx, executionSes, executionPlan) + if routineErr != nil { + return nil, nil, nil, "", false, routineErr + } + change = change || routineChanged var preparedMetadataTS timestamp.Timestamp if catalogCache != nil { preparedMetadataTS = catalogCache.GetPreparedMetadataTS() diff --git a/pkg/frontend/mysql_cmd_executor.go b/pkg/frontend/mysql_cmd_executor.go index 7dc741979fd10..80ca1b65b6495 100644 --- a/pkg/frontend/mysql_cmd_executor.go +++ b/pkg/frontend/mysql_cmd_executor.go @@ -4146,6 +4146,13 @@ func cachedPlanForInput(ses *Session, input *UserInput) *cachedPlan { return nil } cached := ses.getCachedPlan(input.getHash()) + if cached != nil && !cachedRoutinePlanDependenciesCurrent(ses, cached) { + // A cached plan carries exact routine revisions. Do not let an entry + // survive a replace/drop or an integrity mismatch merely because the + // ordinary table/schema generation is unchanged. + ses.removeCachedPlan(input.getHash()) + return nil + } // SELECT ... INTO @var changes the type of a session variable as part of // execution. A cached SELECT-INTO plan can therefore never be reused: it // may have been bound against the variable's pre-assignment type, and the @@ -5399,7 +5406,11 @@ func dispatchStmt(ses FeSession, if err != nil { return err } - if flag { + routineChanged, err := validateRoutinePlanDependencies(execCtx.reqCtx, ses, execCtx.cw.Plan()) + if err != nil { + return err + } + if flag || routineChanged { if err = rebuildStaleCachedStatements(ses, execCtx); err != nil { return err } diff --git a/pkg/frontend/pitr.go b/pkg/frontend/pitr.go index 414273f674b29..ccb89eb6210f5 100644 --- a/pkg/frontend/pitr.go +++ b/pkg/frontend/pitr.go @@ -1597,6 +1597,21 @@ func reCreateTableWithPitr( fmt.Sprintf(" {MO_TS = %d}", ts), accountID, accountID, + tblInfo.createSql, + ) + } + if isCurrentFunctionRevisionCatalog(tblInfo) { + accountID, accountErr := defines.GetAccountId(ctx) + if accountErr != nil { + return accountErr + } + return restoreFunctionRevisionCatalogWithCurrentSchema( + ctx, + bh, + fmt.Sprintf(" {MO_TS = %d}", ts), + accountID, + accountID, + tblInfo.createSql, ) } if isSequence(tblInfo) { diff --git a/pkg/frontend/predefined.go b/pkg/frontend/predefined.go index c9ccfc4fc13d9..c988702a3503b 100644 --- a/pkg/frontend/predefined.go +++ b/pkg/frontend/predefined.go @@ -99,10 +99,16 @@ var ( MoCatalogMoUserDefinedFunctionDDL = fmt.Sprintf(`create table mo_catalog.mo_user_defined_function ( function_id int auto_increment, + active_revision bigint unsigned not null default 0, + namespace_version bigint unsigned not null default 0, name varchar(100), owner int unsigned, args json, arg_types varchar(%d) not null default '', + canonical_input_descriptor varchar(%d) not null default '', + return_descriptor varchar(%d) not null default '', + signature_key_schema_version int not null default 0, + signature_fingerprint varchar(128) not null default '', retType varchar(20), body text, language varchar(20), @@ -111,14 +117,38 @@ var ( modified_time timestamp, created_time timestamp, type varchar(10), - security_type varchar(10), + security_type varchar(10) not null default 'DEFINER', comment varchar(5000), character_set_client varchar(64), collation_connection varchar(64), database_collation varchar(64), sql_mode varchar(1024) not null default 'PIPES_AS_CONCAT', primary key(function_id), - unique key name_db_arg_types(name, db, arg_types) + unique key name_db_arg_types_descriptor(name, db, arg_types, canonical_input_descriptor) + )`, types.MaxStringSize, types.MaxStringSize, types.MaxStringSize) + + MoCatalogMoFunctionRevisionDDL = fmt.Sprintf(`create table mo_catalog.mo_function_revisions ( + function_id bigint unsigned not null, + revision bigint unsigned not null, + namespace_version bigint unsigned not null, + name varchar(100) not null, + args json not null, + arg_types varchar(%d) not null default '', + rettype varchar(20) not null, + body text not null, + language varchar(20) not null, + definition_schema_version int not null default 0, + abi_contract varchar(64) not null default '', + adapter_version varchar(64) not null default '', + artifact_digest varchar(128) not null default '', + environment_digest varchar(128) not null default '', + sdk_version varchar(64) not null default '', + null_policy varchar(64) not null default '', + volatility varchar(20) not null default 'VOLATILE', + definition_fingerprint varchar(128) not null default '', + created_time timestamp, + security_type varchar(10) not null default 'DEFINER', + primary key(function_id, revision) )`, types.MaxStringSize) MoCatalogMoMysqlCompatibilityModeDDL = `create table mo_catalog.mo_mysql_compatibility_mode ( diff --git a/pkg/frontend/python_udf_catalog_test.go b/pkg/frontend/python_udf_catalog_test.go new file mode 100644 index 0000000000000..6d7c9fa549789 --- /dev/null +++ b/pkg/frontend/python_udf_catalog_test.go @@ -0,0 +1,945 @@ +// Copyright 2026 Matrix Origin +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. + +package frontend + +import ( + "context" + "encoding/json" + "fmt" + "strconv" + "strings" + "testing" + + "github.com/matrixorigin/matrixone/pkg/common/moerr" + "github.com/matrixorigin/matrixone/pkg/config" + "github.com/matrixorigin/matrixone/pkg/container/types" + "github.com/matrixorigin/matrixone/pkg/defines" + "github.com/matrixorigin/matrixone/pkg/fileservice" + "github.com/matrixorigin/matrixone/pkg/sql/parsers" + "github.com/matrixorigin/matrixone/pkg/sql/parsers/dialect" + "github.com/matrixorigin/matrixone/pkg/sql/parsers/tree" + "github.com/matrixorigin/matrixone/pkg/sql/plan/function" + "github.com/matrixorigin/matrixone/pkg/udf" + pythonudf "github.com/matrixorigin/matrixone/pkg/udf/python" + "github.com/stretchr/testify/require" +) + +func TestPersistPythonReplaceRejectsLegacyHeadBeforeWriting(t *testing.T) { + ctx := context.Background() + functionID := int64(41) + languageSQL := fmt.Sprintf( + "select language from mo_catalog.mo_user_defined_function where function_id = %d;", + functionID, + ) + headSQL := fmt.Sprintf( + "select active_revision, namespace_version from mo_catalog.mo_user_defined_function where function_id = %d;", + functionID, + ) + bh := &backgroundExecTestWithHistory{} + bh.init() + language := &MysqlResultSet{} + languageColumn := &MysqlColumn{} + languageColumn.SetName("language") + languageColumn.SetColumnType(defines.MYSQL_TYPE_VAR_STRING) + language.AddColumn(languageColumn) + language.AddRow([]interface{}{"python"}) + bh.sql2result[languageSQL] = language + head := &MysqlResultSet{} + for _, name := range []string{"active_revision", "namespace_version"} { + column := &MysqlColumn{} + column.SetName(name) + column.SetColumnType(defines.MYSQL_TYPE_LONGLONG) + head.AddColumn(column) + } + head.AddRow([]interface{}{int64(0), int64(0)}) + bh.sql2result[headSQL] = head + + err := persistUserDefinedFunction(ctx, bh, nil, 7, userDefinedFunctionDefinition{ + name: "legacy_python", + args: "[]", + argTypes: "[]", + retType: "int", + body: "legacy demo body", + lang: "python", + dbName: "db1", + }, &functionID) + + require.ErrorContains(t, err, "UNSUPPORTED_ROUTINE_VERSION") + require.ErrorContains(t, err, "DROP and CREATE") + require.Equal(t, []string{languageSQL, headSQL}, bh.executedSqls) +} + +func TestPersistFunctionReplaceRejectsLanguageChangeBeforeWriting(t *testing.T) { + ctx := context.Background() + functionID := int64(42) + languageSQL := fmt.Sprintf( + "select language from mo_catalog.mo_user_defined_function where function_id = %d;", + functionID, + ) + b := &backgroundExecTestWithHistory{} + b.init() + language := &MysqlResultSet{} + column := &MysqlColumn{} + column.SetName("language") + column.SetColumnType(defines.MYSQL_TYPE_VAR_STRING) + language.AddColumn(column) + language.AddRow([]interface{}{"SQL"}) + b.sql2result[languageSQL] = language + + err := persistUserDefinedFunction(ctx, b, nil, 7, userDefinedFunctionDefinition{ + name: "cross_language", + lang: "python", + body: "not published", + }, &functionID) + + require.ErrorContains(t, err, "cannot change language") + require.ErrorContains(t, err, "DROP and CREATE") + require.Equal(t, []string{languageSQL}, b.executedSqls) +} + +func TestPersistPythonReplaceRejectsSignatureChangeBeforeWriting(t *testing.T) { + ctx := context.Background() + functionID := int64(43) + languageSQL := fmt.Sprintf( + "select language from mo_catalog.mo_user_defined_function where function_id = %d;", + functionID, + ) + headSQL := fmt.Sprintf( + "select active_revision, namespace_version from mo_catalog.mo_user_defined_function where function_id = %d;", + functionID, + ) + bodySQL := fmt.Sprintf( + "select body from mo_catalog.mo_function_revisions where function_id = %d and revision = %d and namespace_version = %d;", + functionID, 1, 1, + ) + b := &backgroundExecTestWithHistory{} + b.init() + b.sql2result[languageSQL] = singleStringResult("language", "python") + head := &MysqlResultSet{} + for _, name := range []string{"active_revision", "namespace_version"} { + column := &MysqlColumn{} + column.SetName(name) + column.SetColumnType(defines.MYSQL_TYPE_LONGLONG) + head.AddColumn(column) + } + head.AddRow([]interface{}{int64(1), int64(1)}) + b.sql2result[headSQL] = head + previous := pythonCatalogTestBody(t, types.T_int64) + b.sql2result[bodySQL] = singleStringResult("body", previous) + candidate := pythonCatalogTestBody(t, types.T_int32) + + err := persistUserDefinedFunction(ctx, b, nil, 7, userDefinedFunctionDefinition{ + name: "current_python", + args: "[]", + argTypes: "[]", + retType: "int", + body: candidate, + lang: "python", + dbName: "db1", + }, &functionID) + + require.ErrorContains(t, err, "cannot change input or return descriptor") + require.Equal(t, []string{languageSQL, headSQL, bodySQL}, b.executedSqls) +} + +func singleStringResult(name, value string) *MysqlResultSet { + result := &MysqlResultSet{} + column := &MysqlColumn{} + column.SetName(name) + column.SetColumnType(defines.MYSQL_TYPE_VAR_STRING) + result.AddColumn(column) + result.AddRow([]interface{}{value}) + return result +} + +func singleInt64Result(name string, value int64) *MysqlResultSet { + result := &MysqlResultSet{} + column := &MysqlColumn{} + column.SetName(name) + column.SetColumnType(defines.MYSQL_TYPE_LONGLONG) + result.AddColumn(column) + result.AddRow([]interface{}{value}) + return result +} + +func pythonCatalogTestBody(t *testing.T, oid types.T) string { + t.Helper() + descriptor, err := function.NewPythonTypeDescriptor(oid.ToType()) + require.NoError(t, err) + source := "def f(ctx, value): return value" + environment, err := udf.PythonEnvironmentDigest() + require.NoError(t, err) + body, err := json.Marshal(function.PythonRoutineBody{ + DefinitionSchemaVersion: udf.PythonDefinitionSchemaVersion, + Handler: "f", + Source: source, + Mode: "SCALAR", + NullPolicy: udf.NullCallHandler, + ABIContract: udf.PythonABIContract, + AdapterVersion: udf.PythonAdapterVersion, + ArtifactDigest: udf.PythonInlineArtifactDigest("f", source), + EnvironmentDigest: environment, + SDKVersion: udf.PythonSDKVersion, + ArgTypes: []function.PythonTypeDescriptor{descriptor}, + ReturnType: &descriptor, + }) + require.NoError(t, err) + return string(body) +} + +func TestPersistPythonCreateRejectsMalformedDefinitionBeforeWriting(t *testing.T) { + bh := &backgroundExecTestWithHistory{} + bh.init() + err := persistUserDefinedFunction(context.Background(), bh, &TenantInfo{User: "owner"}, 7, userDefinedFunctionDefinition{ + name: "broken_python", + args: "[]", + argTypes: "[]", + retType: "int", + body: "not current python routine json", + lang: "python", + dbName: "db1", + }, nil) + + require.ErrorContains(t, err, "UNSUPPORTED_ROUTINE_VERSION") + require.Empty(t, bh.executedSqls) +} + +func TestPersistPythonCreateIdentityLookupIncludesLanguage(t *testing.T) { + definition := userDefinedFunctionDefinition{ + name: "python_identity", + dbName: "db1", + canonicalInputDescriptor: "{\"types\":[]}", + lang: "python", + } + query := fmt.Sprintf( + `select function_id from mo_catalog.mo_user_defined_function where name = "%s" and db = "%s" and canonical_input_descriptor = "%s" and language = '%s' order by function_id desc limit 1;`, + escapeSQLStringForDoubleQuotes(definition.name), + escapeSQLStringForDoubleQuotes(definition.dbName), + escapeSQLStringForDoubleQuotes(definition.canonicalInputDescriptor), + escapeSQLStringForDoubleQuotes(definition.lang), + ) + b := &backgroundExecTest{} + b.init() + b.sql2result[query] = singleInt64Result("function_id", 101) + + id, err := findPersistedFunctionID(context.Background(), b, definition) + require.NoError(t, err) + require.Equal(t, int64(101), id) + require.Equal(t, []string{query}, b.executedSQLs) + require.Contains(t, b.executedSQLs[0], "language = 'python'") +} + +func TestPersistSQLCreateKeepsRollingCatalogCompatibility(t *testing.T) { + bh := &backgroundExecTestWithHistory{} + bh.init() + bh.sql2err[functionRevisionCatalogSchemaCheck] = moerr.NewNoSuchTableNoCtx( + "mo_catalog", "mo_function_revisions") + + err := persistUserDefinedFunction(context.Background(), bh, &TenantInfo{User: "owner"}, 7, userDefinedFunctionDefinition{ + name: "sql_function", + args: `[{"name":"value","type":"bigint"}]`, + argTypes: `["bigint"]`, + retType: "bigint", + body: "select value + 1", + lang: string(tree.SQL), + dbName: "db1", + }, nil) + require.NoError(t, err) + require.Len(t, bh.executedSqls, 2) + require.Equal(t, functionRevisionCatalogSchemaCheck, bh.executedSqls[0]) + require.NotContains(t, bh.executedSqls[1], "canonical_input_descriptor") + require.NotContains(t, bh.executedSqls[1], "mo_function_revisions") +} + +func TestFunctionRevisionCatalogAvailablePropagatesMalformedProbeResult(t *testing.T) { + bh := &backgroundExecTest{} + bh.init() + + available, err := functionRevisionCatalogAvailable(context.Background(), bh) + + require.ErrorContains(t, err, "it is not the type of result set") + require.False(t, available) +} + +func TestFunctionRevisionCatalogAvailableRejectsSplitOrEmptyProbe(t *testing.T) { + for _, tc := range []struct { + name string + results []interface{} + }{ + {name: "empty", results: []interface{}{}}, + {name: "split", results: []interface{}{emptyCatalogProbeResult(20), emptyCatalogProbeResult(20)}}, + } { + t.Run(tc.name, func(t *testing.T) { + bh := &backgroundExecTestWithResults{results: tc.results} + bh.init() + available, err := functionRevisionCatalogAvailable(context.Background(), bh) + require.ErrorContains(t, err, "expected exactly one") + require.False(t, available) + }) + } +} + +type backgroundExecTestWithResults struct { + backgroundExecTest + results []interface{} +} + +func (bt *backgroundExecTestWithResults) GetExecResultSet() []interface{} { + return bt.results +} + +func TestExactlyOneCatalogRowRejectsDuplicateOrSplitPointReads(t *testing.T) { + one := singleStringResult("value", "current") + row, err := exactlyOneCatalogRow(context.Background(), []ExecResult{one}, "routine point read") + require.NoError(t, err) + require.Same(t, one, row) + + one.AddRow([]interface{}{"duplicate"}) + _, err = exactlyOneCatalogRow(context.Background(), []ExecResult{one}, "routine point read") + require.ErrorContains(t, err, "expected exactly one") + + _, err = exactlyOneCatalogRow(context.Background(), []ExecResult{singleStringResult("value", "one"), singleStringResult("value", "two")}, "routine point read") + require.ErrorContains(t, err, "result sets") +} + +func TestPersistSQLCreatePublishesSharedRevisionWhenCatalogIsReady(t *testing.T) { + bh := &backgroundExecTestWithHistory{} + bh.init() + bh.sql2result[functionRevisionCatalogSchemaCheck] = emptyCatalogProbeResult(20) + definition := userDefinedFunctionDefinition{ + name: "sql_revision", + args: `[{"name":"value","type":"bigint"}]`, + argTypes: `["bigint"]`, + retType: "bigint", + body: "select value + 1", + lang: string(tree.SQL), + dbName: "db1", + } + findSQL := `select function_id from mo_catalog.mo_user_defined_function where name = "sql_revision" and db = "db1" and arg_types = '["bigint"]' and language = 'sql' order by function_id desc limit 1;` + bh.sql2result[findSQL] = singleInt64Result("function_id", 91) + + err := persistUserDefinedFunction(context.Background(), bh, &TenantInfo{User: "owner"}, 7, definition, nil) + require.NoError(t, err) + require.Len(t, bh.executedSqls, 5) + require.Equal(t, functionRevisionCatalogSchemaCheck, bh.executedSqls[0]) + require.Contains(t, bh.executedSqls[1], "insert into mo_catalog.mo_user_defined_function") + require.Equal(t, findSQL, bh.executedSqls[2]) + require.Contains(t, bh.executedSqls[3], "insert into mo_catalog.mo_function_revisions") + require.Contains(t, bh.executedSqls[3], `"sql_revision"`) + require.Contains(t, bh.executedSqls[3], `"select value + 1"`) + require.Contains(t, bh.executedSqls[4], "active_revision = 1, namespace_version = 1") +} + +func TestPersistPythonCreatePublishesInvokerSecurityType(t *testing.T) { + bh := &backgroundExecTestWithHistory{} + bh.init() + body := pythonCatalogTestBody(t, types.T_int64) + decoded, err := function.DecodePythonRoutineBody(body) + require.NoError(t, err) + canonicalInput, _, _, err := function.PythonSignatureMetadata(decoded.ArgTypes, decoded.ReturnType) + require.NoError(t, err) + definition := userDefinedFunctionDefinition{ + name: "python_invoker", + args: `[{"name":"value","type":"bigint"}]`, + argTypes: `["bigint"]`, + retType: "bigint", + body: body, + lang: string(tree.PYTHON), + dbName: "db1", + canonicalInputDescriptor: canonicalInput, + } + findSQL := fmt.Sprintf( + `select function_id from mo_catalog.mo_user_defined_function where name = "%s" and db = "%s" and canonical_input_descriptor = "%s" and language = '%s' order by function_id desc limit 1;`, + escapeSQLStringForDoubleQuotes(definition.name), + escapeSQLStringForDoubleQuotes(definition.dbName), + escapeSQLStringForDoubleQuotes(canonicalInput), + escapeSQLStringForDoubleQuotes(definition.lang), + ) + bh.sql2result[findSQL] = singleInt64Result("function_id", 94) + + err = persistUserDefinedFunction(context.Background(), bh, &TenantInfo{User: "owner"}, 7, definition, nil) + require.NoError(t, err) + require.GreaterOrEqual(t, len(bh.executedSqls), 1) + require.Contains(t, bh.executedSqls[0], `"FUNCTION","INVOKER"`) + for _, sql := range bh.executedSqls { + if strings.Contains(sql, "mo_function_revisions") { + require.Contains(t, sql, `"python_invoker"`) + } + } +} + +func TestPublishRestoredPythonArtifactsRepublishesExactAccountArtifact(t *testing.T) { + const ( + sid = "python-artifact-restore-test" + targetAccount = uint32(27) + ) + InitServerLevelVars(sid) + oldPU := getPuIfPresent(sid) + t.Cleanup(func() { setPu(sid, oldPU) }) + + fs, err := fileservice.NewMemoryFS(defines.SharedFileServiceName, fileservice.DisabledCacheConfig, nil) + require.NoError(t, err) + setPu(sid, &config.ParameterUnit{FileService: fs}) + + body := pythonCatalogTestBody(t, types.T_int64) + decoded, err := function.DecodePythonRoutineBody(body) + require.NoError(t, err) + const query = "select language, artifact_digest, body from mo_catalog.mo_function_revisions order by function_id, revision;" + bh := &backgroundExecTest{} + bh.init() + bh.sql2result[query] = newMrsForRestoreStringRows( + []string{"language", "artifact_digest", "body"}, + [][]interface{}{{"python", decoded.ArtifactDigest, body}}, + ) + + err = publishRestoredPythonArtifacts( + t.Context(), sid, + bh, + defines.AttachAccountId(t.Context(), targetAccount), targetAccount, + ) + require.NoError(t, err) + + store, err := pythonudf.NewFileArtifactStore(fs, pythonudf.DefaultMaxArtifactBytes) + require.NoError(t, err) + resolved, err := store.Resolve(t.Context(), uint64(targetAccount), decoded.Handler, decoded.ArtifactDigest) + require.NoError(t, err) + require.Equal(t, decoded.Source, resolved) + _, err = store.Resolve(t.Context(), uint64(targetAccount-1), decoded.Handler, decoded.ArtifactDigest) + require.ErrorContains(t, err, "unavailable") +} + +func TestPublishRestoredPythonArtifactsRejectsMissingArtifactStore(t *testing.T) { + const sid = "python-artifact-restore-missing-store-test" + InitServerLevelVars(sid) + oldPU := getPuIfPresent(sid) + t.Cleanup(func() { setPu(sid, oldPU) }) + setPu(sid, &config.ParameterUnit{}) + + body := pythonCatalogTestBody(t, types.T_int64) + decoded, err := function.DecodePythonRoutineBody(body) + require.NoError(t, err) + const query = "select language, artifact_digest, body from mo_catalog.mo_function_revisions order by function_id, revision;" + bh := &backgroundExecTest{} + bh.init() + bh.sql2result[query] = newMrsForRestoreStringRows( + []string{"language", "artifact_digest", "body"}, + [][]interface{}{{"python", decoded.ArtifactDigest, body}}, + ) + + err = publishRestoredPythonArtifacts( + t.Context(), sid, bh, + defines.AttachAccountId(t.Context(), 27), 27, + ) + require.ErrorContains(t, err, "artifact store is required") +} + +func TestPersistSQLReplacePublishesNextImmutableRevision(t *testing.T) { + bh := &backgroundExecTestWithHistory{} + bh.init() + bh.sql2result[functionRevisionCatalogSchemaCheck] = emptyCatalogProbeResult(20) + functionID := int64(92) + bh.sql2result[fmt.Sprintf( + "select language from mo_catalog.mo_user_defined_function where function_id = %d;", functionID, + )] = singleStringResult("language", string(tree.SQL)) + bh.sql2result[fmt.Sprintf( + "select active_revision, namespace_version from mo_catalog.mo_user_defined_function where function_id = %d;", functionID, + )] = func() *MysqlResultSet { + result := &MysqlResultSet{} + for _, name := range []string{"active_revision", "namespace_version"} { + column := &MysqlColumn{} + column.SetName(name) + column.SetColumnType(defines.MYSQL_TYPE_LONGLONG) + result.AddColumn(column) + } + result.AddRow([]interface{}{int64(2), int64(5)}) + return result + }() + bh.sql2result[fmt.Sprintf( + "select rettype from mo_catalog.mo_user_defined_function where function_id = %d;", functionID, + )] = singleStringResult("rettype", "bigint") + + err := persistUserDefinedFunction(context.Background(), bh, &TenantInfo{User: "owner"}, 7, userDefinedFunctionDefinition{ + name: "sql_revision", + args: `[{"name":"value","type":"bigint"}]`, + argTypes: `["bigint"]`, + retType: "bigint", + body: "select value + 2", + lang: string(tree.SQL), + dbName: "db1", + }, &functionID) + require.NoError(t, err) + require.Len(t, bh.executedSqls, 7) + require.Equal(t, functionRevisionCatalogSchemaCheck, bh.executedSqls[0]) + require.Contains(t, bh.executedSqls[4], "body = \"select value + 2\"") + require.Contains(t, bh.executedSqls[5], "function_id, revision, namespace_version") + require.Contains(t, bh.executedSqls[5], ", 3, 6,") + require.Contains(t, bh.executedSqls[6], "active_revision = 3, namespace_version = 6") +} + +func TestReadSQLRevisionBindsActiveHeadAndRejectsTampering(t *testing.T) { + const functionID = int64(93) + const args = `[{"name":"value","type":"bigint"}]` + const argTypes = `["bigint"]` + const body = "select value + 1" + const retType = "bigint" + fingerprint, err := function.SQLRoutineFingerprint(body, argTypes, retType) + require.NoError(t, err) + baseSQL := fmt.Sprintf("select active_revision, namespace_version, security_type from mo_catalog.mo_user_defined_function where function_id = %d;", functionID) + revisionSQL := fmt.Sprintf("select revision, args, arg_types, body, rettype, language, definition_schema_version, definition_fingerprint, volatility, null_policy, security_type from mo_catalog.mo_function_revisions where function_id = %d and revision = 1 and namespace_version = 1;", functionID) + newBase := func() *MysqlResultSet { + result := &MysqlResultSet{} + for _, name := range []string{"active_revision", "namespace_version", "security_type"} { + column := &MysqlColumn{} + column.SetName(name) + if name == "security_type" { + column.SetColumnType(defines.MYSQL_TYPE_VAR_STRING) + } else { + column.SetColumnType(defines.MYSQL_TYPE_LONGLONG) + } + result.AddColumn(column) + } + result.AddRow([]interface{}{int64(1), int64(1), "DEFINER"}) + return result + } + newRevision := func(candidateBody, candidateFingerprint string) *MysqlResultSet { + result := &MysqlResultSet{} + for _, name := range []string{"revision", "args", "arg_types", "body", "rettype", "language", "definition_schema_version", "definition_fingerprint", "volatility", "null_policy", "security_type"} { + column := &MysqlColumn{} + column.SetName(name) + if name == "revision" || name == "definition_schema_version" { + column.SetColumnType(defines.MYSQL_TYPE_LONGLONG) + } else { + column.SetColumnType(defines.MYSQL_TYPE_VAR_STRING) + } + result.AddColumn(column) + } + result.AddRow([]interface{}{int64(1), args, argTypes, candidateBody, retType, string(tree.SQL), int64(udf.SQLDefinitionSchemaVersion), candidateFingerprint, "VOLATILE", udf.NullCallHandler, "DEFINER"}) + return result + } + + bh := &backgroundExecTest{} + bh.init() + bh.sql2result[baseSQL] = newBase() + bh.sql2result[revisionSQL] = newRevision(body, fingerprint) + got, found, err := readSQLRevision(context.Background(), bh, functionID, nil) + require.NoError(t, err) + require.True(t, found) + require.Equal(t, uint64(1), got.Revision) + require.Equal(t, body, got.Body) + require.Equal(t, fingerprint, got.DefinitionFingerprint) + + bh.init() + bh.sql2result[baseSQL] = newBase() + bh.sql2result[revisionSQL] = newRevision(body, strings.Repeat("a", 64)) + _, found, err = readSQLRevision(context.Background(), bh, functionID, nil) + require.ErrorContains(t, err, "fingerprint mismatch") + require.False(t, found) +} + +func TestEnsurePythonUdfCatalogReadyRequiresCurrentSchema(t *testing.T) { + bh := &backgroundExecTestWithHistory{} + bh.init() + bh.sql2result[pythonUdfCatalogIdentitySchemaCheck] = emptyCatalogProbeResult(6) + bh.sql2result[functionRevisionCatalogSchemaCheck] = emptyCatalogProbeResult(20) + require.NoError(t, ensurePythonUdfCatalogReady(context.Background(), bh)) + require.Equal(t, []string{ + pythonUdfCatalogIdentitySchemaCheck, + functionRevisionCatalogSchemaCheck, + }, bh.executedSqls) + + bh.init() + bh.sql2result[pythonUdfCatalogIdentitySchemaCheck] = emptyCatalogProbeResult(6) + wantErr := fmt.Errorf("missing current revision catalog") + bh.sql2err[functionRevisionCatalogSchemaCheck] = wantErr + err := ensurePythonUdfCatalogReady(context.Background(), bh) + require.ErrorContains(t, err, "Python UDF catalog contract is not ready") + require.ErrorContains(t, err, wantErr.Error()) + require.Equal(t, []string{ + pythonUdfCatalogIdentitySchemaCheck, + functionRevisionCatalogSchemaCheck, + }, bh.executedSqls) + + bh.init() + bh.sql2result[pythonUdfCatalogIdentitySchemaCheck] = emptyCatalogProbeResult(1) + bh.sql2result[functionRevisionCatalogSchemaCheck] = emptyCatalogProbeResult(20) + err = ensurePythonUdfCatalogReady(context.Background(), bh) + require.ErrorContains(t, err, "catalog probe returned 1 columns, expected 6") +} + +func emptyCatalogProbeResult(columnCount int) *MysqlResultSet { + result := &MysqlResultSet{} + for index := 0; index < columnCount; index++ { + column := &MysqlColumn{} + column.SetName(fmt.Sprintf("column_%d", index)) + column.SetColumnType(defines.MYSQL_TYPE_VAR_STRING) + result.AddColumn(column) + } + values := make([]interface{}, columnCount) + for index := range values { + values[index] = int64(0) + } + result.AddRow(values) + return result +} + +func TestValidateRestoredPythonRevisionCatalogChecksCurrentContract(t *testing.T) { + body := pythonCatalogTestBody(t, types.T_int64) + fingerprint, err := function.PythonRoutineFingerprint(body) + require.NoError(t, err) + decodedBody, err := function.DecodePythonRoutineBody(body) + require.NoError(t, err) + inputDescriptor, _, _, err := function.PythonSignatureMetadata(decodedBody.ArgTypes, decodedBody.ReturnType) + require.NoError(t, err) + query := fmt.Sprintf( + "select %s from %s order by function_id, revision;", + functionRevisionCatalogColumns, + qualifiedTableName(moCatalog, "mo_function_revisions"), + ) + result := &MysqlResultSet{} + for index := 0; index < 20; index++ { + column := &MysqlColumn{} + column.SetName(fmt.Sprintf("column_%d", index)) + column.SetColumnType(defines.MYSQL_TYPE_VAR_STRING) + result.AddColumn(column) + } + result.AddRow([]interface{}{ + "44", "1", "1", "f", `[ {"name":"value","type":"bigint"} ]`, inputDescriptor, "bigint", body, + "python", "1", udf.PythonABIContract, udf.PythonAdapterVersion, + udf.PythonInlineArtifactDigest("f", "def f(ctx, value): return value"), + func() string { + value, digestErr := udf.PythonEnvironmentDigest() + require.NoError(t, digestErr) + return value + }(), + udf.PythonSDKVersion, udf.NullCallHandler, "VOLATILE", fingerprint, "2026-09-11 00:00:00", "INVOKER", + }) + + bh := &backgroundExecTest{} + bh.init() + bh.sql2result[query] = result + require.NoError(t, validateRestoredFunctionRevisionCatalog(context.Background(), bh, context.Background())) + + result.Data[0][17] = "stale-fingerprint" + bh.init() + bh.sql2result[query] = result + require.ErrorContains(t, + validateRestoredFunctionRevisionCatalog(context.Background(), bh, context.Background()), + "fingerprint mismatch", + ) +} + +func TestValidateRestoredSharedRevisionCatalogAcceptsSQLAndRejectsTampering(t *testing.T) { + args := `[ {"name":"value","type":"bigint"} ]` + argTypes, err := userDefinedFunctionArgumentTypesFromJSON(args) + require.NoError(t, err) + body := "select value + 1" + retType := "bigint" + fingerprint, err := function.SQLRoutineFingerprint(body, argTypes, retType) + require.NoError(t, err) + query := fmt.Sprintf( + "select %s from %s order by function_id, revision;", + functionRevisionCatalogColumns, + qualifiedTableName(moCatalog, "mo_function_revisions"), + ) + newResult := func(body, digest string) *MysqlResultSet { + result := &MysqlResultSet{} + for index := 0; index < 20; index++ { + column := &MysqlColumn{} + column.SetName(fmt.Sprintf("column_%d", index)) + column.SetColumnType(defines.MYSQL_TYPE_VAR_STRING) + result.AddColumn(column) + } + result.AddRow([]interface{}{ + "45", "1", "1", "f", args, argTypes, retType, body, + string(tree.SQL), strconv.Itoa(udf.SQLDefinitionSchemaVersion), "", "", "", "", "", + udf.NullCallHandler, "VOLATILE", digest, "2026-09-11 00:00:00", "DEFINER", + }) + return result + } + + bh := &backgroundExecTest{} + bh.init() + bh.sql2result[query] = newResult(body, fingerprint) + require.NoError(t, validateRestoredFunctionRevisionCatalog(context.Background(), bh, context.Background())) + + bh.init() + bh.sql2result[query] = newResult("select value + 2", fingerprint) + require.ErrorContains(t, + validateRestoredFunctionRevisionCatalog(context.Background(), bh, context.Background()), + "fingerprint mismatch", + ) +} + +func TestValidateRestoredPythonCatalogHeadsBindsIdentityToActiveRevision(t *testing.T) { + body := pythonCatalogTestBody(t, types.T_int64) + decoded, err := function.DecodePythonRoutineBody(body) + require.NoError(t, err) + input, output, signature, err := function.PythonSignatureMetadata(decoded.ArgTypes, decoded.ReturnType) + require.NoError(t, err) + fingerprint, err := function.PythonRoutineFingerprint(body) + require.NoError(t, err) + query := `select cast(f.function_id as char), cast(f.active_revision as char), cast(f.namespace_version as char), + cast(coalesce(r.revision, 0) as char), cast(coalesce(r.namespace_version, 0) as char), + f.canonical_input_descriptor, f.return_descriptor, + cast(f.signature_key_schema_version as char), f.signature_fingerprint, + coalesce(r.arg_types, ''), coalesce(r.rettype, ''), coalesce(r.body, ''), + lower(f.language), coalesce(r.definition_fingerprint, ''), + coalesce(r.security_type, ''), coalesce(f.security_type, '') + from mo_catalog.mo_user_defined_function f + left join mo_catalog.mo_function_revisions r + on r.function_id = f.function_id and r.revision = f.active_revision + and r.namespace_version = f.namespace_version + where lower(f.language) in ("python", "sql") + order by f.function_id;` + newResult := func() *MysqlResultSet { + result := &MysqlResultSet{} + for index := 0; index < 16; index++ { + column := &MysqlColumn{} + column.SetName(fmt.Sprintf("column_%d", index)) + column.SetColumnType(defines.MYSQL_TYPE_VAR_STRING) + result.AddColumn(column) + } + result.AddRow([]interface{}{ + "44", "2", "3", "2", "3", input, output, + fmt.Sprintf("%d", udf.PythonSignatureKeySchemaVersion), signature, + input, "bigint", body, string(tree.PYTHON), fingerprint, + "INVOKER", "INVOKER", + }) + return result + } + + b := &backgroundExecTest{} + b.init() + b.sql2result[query] = newResult() + require.NoError(t, validateRestoredFunctionCatalogHeads(context.Background(), b, context.Background())) + + mutated := newResult() + mutated.Data[0][1] = "1" + b.init() + b.sql2result[query] = mutated + require.ErrorContains(t, + validateRestoredFunctionCatalogHeads(context.Background(), b, context.Background()), + "active revision does not exist", + ) + + mutated = newResult() + mutated.Data[0][8] = "stale-signature" + b.init() + b.sql2result[query] = mutated + require.ErrorContains(t, + validateRestoredFunctionCatalogHeads(context.Background(), b, context.Background()), + "head identity does not match", + ) +} + +func TestValidateRestoredSharedCatalogHeadsChecksSQLRevision(t *testing.T) { + args := `[ {"name":"value","type":"bigint"} ]` + argTypes, err := userDefinedFunctionArgumentTypesFromJSON(args) + require.NoError(t, err) + body := "select value + 1" + retType := "bigint" + fingerprint, err := function.SQLRoutineFingerprint(body, argTypes, retType) + require.NoError(t, err) + query := `select cast(f.function_id as char), cast(f.active_revision as char), cast(f.namespace_version as char), + cast(coalesce(r.revision, 0) as char), cast(coalesce(r.namespace_version, 0) as char), + f.canonical_input_descriptor, f.return_descriptor, + cast(f.signature_key_schema_version as char), f.signature_fingerprint, + coalesce(r.arg_types, ''), coalesce(r.rettype, ''), coalesce(r.body, ''), + lower(f.language), coalesce(r.definition_fingerprint, ''), + coalesce(r.security_type, ''), coalesce(f.security_type, '') + from mo_catalog.mo_user_defined_function f + left join mo_catalog.mo_function_revisions r + on r.function_id = f.function_id and r.revision = f.active_revision + and r.namespace_version = f.namespace_version + where lower(f.language) in ("python", "sql") + order by f.function_id;` + newResult := func(body, digest string) *MysqlResultSet { + result := &MysqlResultSet{} + for index := 0; index < 16; index++ { + column := &MysqlColumn{} + column.SetName(fmt.Sprintf("column_%d", index)) + column.SetColumnType(defines.MYSQL_TYPE_VAR_STRING) + result.AddColumn(column) + } + result.AddRow([]interface{}{ + "45", "1", "1", "1", "1", "", "", "0", "", + argTypes, retType, body, string(tree.SQL), digest, + "DEFINER", "DEFINER", + }) + return result + } + + b := &backgroundExecTest{} + b.init() + b.sql2result[query] = newResult(body, fingerprint) + require.NoError(t, validateRestoredFunctionCatalogHeads(context.Background(), b, context.Background())) + + b.init() + b.sql2result[query] = newResult("select value + 2", fingerprint) + require.ErrorContains(t, + validateRestoredFunctionCatalogHeads(context.Background(), b, context.Background()), + "head fingerprint does not match", + ) +} + +func TestReadPythonRevisionRequiresPersistedIdentitySignature(t *testing.T) { + body := pythonCatalogTestBody(t, types.T_int64) + inputDescriptor, returnDescriptor, signatureFingerprint, err := func() (string, string, string, error) { + decoded, decodeErr := function.DecodePythonRoutineBody(body) + if decodeErr != nil { + return "", "", "", decodeErr + } + return function.PythonSignatureMetadata(decoded.ArgTypes, decoded.ReturnType) + }() + require.NoError(t, err) + fingerprint, err := function.PythonRoutineFingerprint(body) + require.NoError(t, err) + environment, err := udf.PythonEnvironmentDigest() + require.NoError(t, err) + query := pythonRevisionCatalogSQL(nil, 44) + result := &MysqlResultSet{} + for index := 0; index < 23; index++ { + column := &MysqlColumn{} + column.SetName(fmt.Sprintf("column_%d", index)) + if index == 0 || index == 1 || index == 2 || index == 5 || index == 12 { + column.SetColumnType(defines.MYSQL_TYPE_LONGLONG) + } else { + column.SetColumnType(defines.MYSQL_TYPE_VAR_STRING) + } + result.AddColumn(column) + } + result.AddRow([]interface{}{ + int64(1), int64(1), int64(1), inputDescriptor, returnDescriptor, int64(udf.PythonSignatureKeySchemaVersion), signatureFingerprint, + `[{"name":"value","type":"bigint"}]`, inputDescriptor, body, "python", "bigint", int64(udf.PythonDefinitionSchemaVersion), + udf.PythonABIContract, udf.PythonAdapterVersion, udf.PythonSDKVersion, udf.NullCallHandler, "VOLATILE", fingerprint, + udf.PythonInlineArtifactDigest("f", "def f(ctx, value): return value"), environment, + "INVOKER", "INVOKER", + }) + bh := &backgroundExecTest{} + bh.init() + bh.sql2result[query] = result + revision, err := readPythonRevision(context.Background(), bh, 44, nil) + require.NoError(t, err) + require.Equal(t, uint64(1), revision.Revision) + require.Equal(t, inputDescriptor, revision.CanonicalInputDescriptor) + + result.Data[0][3] = "[]" + bh.init() + bh.sql2result[query] = result + _, err = readPythonRevision(context.Background(), bh, 44, nil) + require.ErrorContains(t, err, "identity descriptor metadata mismatch") + + result.Data[0][3] = inputDescriptor + result.Data[0][8] = "[]" + bh.init() + bh.sql2result[query] = result + _, err = readPythonRevision(context.Background(), bh, 44, nil) + require.ErrorContains(t, err, "revision argument descriptor mismatch") + + result.Data[0][8] = inputDescriptor + result.Data[0][21] = "DEFINER" + bh.init() + bh.sql2result[query] = result + _, err = readPythonRevision(context.Background(), bh, 44, nil) + require.ErrorContains(t, err, "revision contract is not supported") +} + +func TestMatchUserDefinedFunctionCandidatesUsesExactPythonDescriptor(t *testing.T) { + decimal2, err := function.NewPythonTypeDescriptor(types.New(types.T_decimal64, 18, 2)) + require.NoError(t, err) + decimal6, err := function.NewPythonTypeDescriptor(types.New(types.T_decimal64, 18, 6)) + require.NoError(t, err) + returnType, err := function.NewPythonTypeDescriptor(types.T_decimal64.ToType()) + require.NoError(t, err) + exact2, err := canonicalPythonInputDescriptor([]function.PythonTypeDescriptor{decimal2}, &returnType) + require.NoError(t, err) + exact6, err := canonicalPythonInputDescriptor([]function.PythonTypeDescriptor{decimal6}, &returnType) + require.NoError(t, err) + + result := &MysqlResultSet{} + for _, name := range []string{"function_id", "args", "language", "arg_types", "canonical_input_descriptor"} { + column := &MysqlColumn{} + column.SetName(name) + if name == "function_id" { + column.SetColumnType(defines.MYSQL_TYPE_LONGLONG) + } else { + column.SetColumnType(defines.MYSQL_TYPE_VAR_STRING) + } + result.AddColumn(column) + } + result.AddRow([]interface{}{int64(51), `[{"name":"value","type":"decimal"}]`, "python", exact2, exact2}) + result.AddRow([]interface{}{int64(52), `[{"name":"value","type":"decimal"}]`, "python", exact6, exact6}) + result.AddRow([]interface{}{int64(53), `[{"name":"value","type":"decimal"}]`, "sql", `["decimal"]`, ""}) + + ids, err := matchUserDefinedFunctionCandidates(context.Background(), []ExecResult{result}, "python", `["decimal"]`, exact2) + require.NoError(t, err) + // A SQL routine with the same logical signature still occupies the shared + // namespace. The exact descriptor distinguishes the two Python overloads; + // it does not permit an ambiguous SQL/Python pair. + require.Equal(t, []int64{51, 53}, ids) + + ids, err = matchUserDefinedFunctionCandidates(context.Background(), []ExecResult{result}, "sql", `["decimal"]`, `["decimal"]`) + require.NoError(t, err) + require.Equal(t, []int64{51, 52, 53}, ids) +} + +func TestMatchUserDefinedFunctionCandidatesRejectsSplitResultSets(t *testing.T) { + result := emptyCatalogProbeResult(5) + _, err := matchUserDefinedFunctionCandidates( + context.Background(), + []ExecResult{result, result}, + string(tree.PYTHON), `[]`, `[]`, + ) + require.ErrorContains(t, err, "function catalog candidate lookup returned 2 result sets") +} + +func TestMatchUserDefinedFunctionCandidatesAcceptsEmptyLookup(t *testing.T) { + ids, err := matchUserDefinedFunctionCandidates( + context.Background(), nil, + string(tree.PYTHON), `[]`, `[]`, + ) + require.NoError(t, err) + require.Empty(t, ids) +} + +func TestPythonDropSignatureUsesExactDescriptor(t *testing.T) { + statement, err := parsers.ParseOne(context.Background(), dialect.MYSQL, "drop function db.f(decimal(18,2))", 1) + require.NoError(t, err) + defer statement.Free() + drop, ok := statement.(*tree.DropFunction) + require.True(t, ok) + + decimal2, err := function.NewPythonTypeDescriptor(types.New(types.T_decimal128, 18, 2)) + require.NoError(t, err) + decimal6, err := function.NewPythonTypeDescriptor(types.New(types.T_decimal128, 18, 6)) + require.NoError(t, err) + returnType, err := function.NewPythonTypeDescriptor(types.T_decimal64.ToType()) + require.NoError(t, err) + makeBody := func(argument function.PythonTypeDescriptor) string { + source := "def f(ctx, value): return value" + environment, environmentErr := udf.PythonEnvironmentDigest() + require.NoError(t, environmentErr) + body, bodyErr := json.Marshal(function.PythonRoutineBody{ + DefinitionSchemaVersion: udf.PythonDefinitionSchemaVersion, + Handler: "f", + Source: source, + Mode: "SCALAR", + NullPolicy: udf.NullCallHandler, + ABIContract: udf.PythonABIContract, + AdapterVersion: udf.PythonAdapterVersion, + ArtifactDigest: udf.PythonInlineArtifactDigest("f", source), + EnvironmentDigest: environment, + SDKVersion: udf.PythonSDKVersion, + ArgTypes: []function.PythonTypeDescriptor{argument}, + ReturnType: &returnType, + }) + require.NoError(t, bodyErr) + return string(body) + } + + require.True(t, pythonDropSignatureMatches(drop.Args, makeBody(decimal2))) + require.False(t, pythonDropSignatureMatches(drop.Args, makeBody(decimal6))) +} diff --git a/pkg/frontend/routine_plan_validation.go b/pkg/frontend/routine_plan_validation.go new file mode 100644 index 0000000000000..0c411455bc538 --- /dev/null +++ b/pkg/frontend/routine_plan_validation.go @@ -0,0 +1,509 @@ +// Copyright 2026 Matrix Origin +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. + +package frontend + +import ( + "context" + "fmt" + "sort" + "strconv" + "strings" + + "github.com/matrixorigin/matrixone/pkg/container/types" + "github.com/matrixorigin/matrixone/pkg/defines" + planpb "github.com/matrixorigin/matrixone/pkg/pb/plan" + "github.com/matrixorigin/matrixone/pkg/sql/plan/function" + "github.com/matrixorigin/matrixone/pkg/udf" +) + +const ( + // Keep dependency validation bounded before allocating or opening a catalog + // transaction. The entry limit protects the result/accounting side; the + // depth limit protects the plan traversal side when a malformed plan nests + // background queries without contributing many routine entries. + maxRoutinePlanDependencies = 1024 + maxRoutinePlanDependencyBytes = 1 << 20 + maxRoutinePlanQueryDepth = 64 +) + +type routinePlanCatalogState struct { + activeRevision uint64 + namespaceVersion uint64 + databaseID uint64 + revision uint64 + language string + volatility string + nullPolicy string + fingerprint string + artifactDigest string + environmentDigest string + securityType string + identityChecked bool + identityValid bool +} + +func cachedRoutinePlanDependenciesCurrent(ses *Session, cached *cachedPlan) bool { + if cached == nil { + return true + } + if ses == nil { + // A cached plan that carries a routine dependency must never be reused + // without the tenant-aware catalog reader that can validate its exact + // revision. Plans without such metadata remain ordinary cache entries. + for _, p := range cached.plans { + dependencies, err := routinePlanDependencies(p) + if err != nil || len(dependencies) != 0 { + return false + } + } + return true + } + ctx := context.Background() + if tcc := ses.GetTxnCompileCtx(); tcc != nil && tcc.execCtx != nil { + ctx = tcc.GetContext() + } + for _, p := range cached.plans { + changed, err := validateRoutinePlanDependencies(ctx, ses, p) + if err != nil || changed { + return false + } + } + return true +} + +func routinePlanDependencies(p *planpb.Plan) ([]*planpb.RoutinePlanDependency, error) { + if p == nil { + return nil, nil + } + query := p.GetQuery() + if query == nil { + return nil, nil + } + + type queryFrame struct { + query *planpb.Query + depth int + next int + } + initialCapacity := len(query.GetRoutineDependencies()) + if initialCapacity > maxRoutinePlanDependencies { + initialCapacity = maxRoutinePlanDependencies + } + dependencies := make([]*planpb.RoutinePlanDependency, 0, initialCapacity) + dependencyBytes := 0 + stack := []queryFrame{{query: query}} + active := map[*planpb.Query]struct{}{query: {}} + for len(stack) != 0 { + frameIndex := len(stack) - 1 + frame := &stack[frameIndex] + if frame.next == 0 { + frameDependencies := frame.query.GetRoutineDependencies() + if len(dependencies)+len(frameDependencies) > maxRoutinePlanDependencies { + return nil, fmt.Errorf("PROGRAM_LIMIT_EXCEEDED: routine dependency count exceeds %d", maxRoutinePlanDependencies) + } + for _, dependency := range frameDependencies { + dependencySize := 1 + if dependency != nil { + dependencySize = dependency.ProtoSize() + } + if dependencySize > maxRoutinePlanDependencyBytes-dependencyBytes { + return nil, fmt.Errorf("PROGRAM_LIMIT_EXCEEDED: routine dependency bytes exceed %d", maxRoutinePlanDependencyBytes) + } + dependencyBytes += dependencySize + } + dependencies = append(dependencies, frameDependencies...) + } + backgroundQueries := frame.query.GetBackgroundQueries() + if frame.next >= len(backgroundQueries) { + delete(active, frame.query) + stack = stack[:frameIndex] + continue + } + background := backgroundQueries[frame.next] + frame.next++ + if background == nil { + continue + } + if frame.depth+1 > maxRoutinePlanQueryDepth { + return nil, fmt.Errorf("PROGRAM_LIMIT_EXCEEDED: routine dependency query nesting exceeds %d", maxRoutinePlanQueryDepth) + } + if _, exists := active[background]; exists { + return nil, fmt.Errorf("UNSUPPORTED_ROUTINE_VERSION: routine dependency query graph contains a cycle") + } + active[background] = struct{}{} + stack = append(stack, queryFrame{query: background, depth: frame.depth + 1}) + } + return dependencies, nil +} + +// validateRoutinePlanDependencies checks a plan against the current catalog +// publication. It is deliberately identity based: execution never resolves a +// routine by name or latest revision, while cache/prepared reuse is rejected +// when the active namespace has moved since the plan was bound. +func validateRoutinePlanDependencies(ctx context.Context, ses FeSession, p *planpb.Plan) (changed bool, err error) { + dependencies, err := routinePlanDependencies(p) + if err != nil { + return false, err + } + if len(dependencies) == 0 { + return false, nil + } + if len(dependencies) > maxRoutinePlanDependencies { + return false, fmt.Errorf("PROGRAM_LIMIT_EXCEEDED: routine dependency count exceeds %d", maxRoutinePlanDependencies) + } + if ses == nil { + return false, fmt.Errorf("UNSUPPORTED_ROUTINE_VERSION: routine dependency validation has no session") + } + accountID := ses.GetAccountId() + ids := make([]uint64, 0, len(dependencies)) + seen := make(map[uint64]struct{}, len(dependencies)) + for _, dependency := range dependencies { + if err := validateRoutinePlanDependencyShape(dependency, accountID); err != nil { + return false, err + } + id := dependency.FunctionRef.FunctionId + if _, ok := seen[id]; !ok { + seen[id] = struct{}{} + ids = append(ids, id) + } + } + sort.Slice(ids, func(i, j int) bool { return ids[i] < ids[j] }) + + if ctx == nil { + ctx = context.Background() + } + ctx = defines.AttachAccountId(ctx, accountID) + // Background execution is still part of the real SQL call path (for + // procedures, derived statements, restore and other frontend-owned work). + // Validate through the execution session so the catalog read carries the + // same tenant and transaction context as the plan. GetBackgroundExec is + // implemented for both client and background sessions; rejecting the latter + // here would make a valid typed routine fail only when a statement is + // executed through a nested frontend pipeline. + bh := ses.GetBackgroundExec(ctx) + defer bh.Close() + if err = bh.Exec(ctx, "begin;"); err != nil { + return false, fmt.Errorf("UNSUPPORTED_ROUTINE_VERSION: routine dependency validation could not start: %w", err) + } + defer func() { err = finishTxn(ctx, bh, err) }() + + bh.ClearExecResultSet() + query := routinePlanCatalogSQL(ids, accountID) + if err = bh.Exec(ctx, query); err != nil { + return false, fmt.Errorf("UNSUPPORTED_ROUTINE_VERSION: routine dependency validation failed: %w", err) + } + rows, err := getResultSet(ctx, bh) + if err != nil { + return false, err + } + catalogResult, err := exactlyOneCatalogResultSet(ctx, rows, "routine dependency catalog") + if err != nil { + return false, err + } + states := make(map[uint64]routinePlanCatalogState, len(rows)) + if catalogResult.GetRowCount() != 0 { + for row := uint64(0); row < catalogResult.GetRowCount(); row++ { + id, getErr := catalogResult.GetUint64(ctx, row, 0) + if getErr != nil { + return false, getErr + } + activeRevision, getErr := catalogResult.GetUint64(ctx, row, 1) + if getErr != nil { + return false, getErr + } + namespaceVersion, getErr := catalogResult.GetUint64(ctx, row, 2) + if getErr != nil { + return false, getErr + } + databaseID, getErr := catalogResult.GetUint64(ctx, row, 3) + if getErr != nil { + return false, getErr + } + revision, getErr := catalogResult.GetUint64(ctx, row, 4) + if getErr != nil { + return false, getErr + } + language, getErr := catalogResult.GetString(ctx, row, 5) + if getErr != nil { + return false, getErr + } + volatility, getErr := catalogResult.GetString(ctx, row, 6) + if getErr != nil { + return false, getErr + } + fingerprint, getErr := catalogResult.GetString(ctx, row, 7) + if getErr != nil { + return false, getErr + } + artifactDigest, getErr := catalogResult.GetString(ctx, row, 8) + if getErr != nil { + return false, getErr + } + environmentDigest, getErr := catalogResult.GetString(ctx, row, 9) + if getErr != nil { + return false, getErr + } + canonicalInput, getErr := catalogResult.GetString(ctx, row, 10) + if getErr != nil { + return false, getErr + } + returnDescriptor, getErr := catalogResult.GetString(ctx, row, 11) + if getErr != nil { + return false, getErr + } + signatureKeySchemaVersion, getErr := catalogResult.GetInt64(ctx, row, 12) + if getErr != nil { + return false, getErr + } + signatureFingerprint, getErr := catalogResult.GetString(ctx, row, 13) + if getErr != nil { + return false, getErr + } + body, getErr := catalogResult.GetString(ctx, row, 14) + if getErr != nil { + return false, getErr + } + revisionArgTypes, getErr := catalogResult.GetString(ctx, row, 15) + if getErr != nil { + return false, getErr + } + revisionRetType, getErr := catalogResult.GetString(ctx, row, 16) + if getErr != nil { + return false, getErr + } + definitionSchema, getErr := catalogResult.GetInt64(ctx, row, 17) + if getErr != nil { + return false, getErr + } + revisionABI, getErr := catalogResult.GetString(ctx, row, 18) + if getErr != nil { + return false, getErr + } + revisionAdapter, getErr := catalogResult.GetString(ctx, row, 19) + if getErr != nil { + return false, getErr + } + revisionSDK, getErr := catalogResult.GetString(ctx, row, 20) + if getErr != nil { + return false, getErr + } + revisionNullPolicy, getErr := catalogResult.GetString(ctx, row, 21) + if getErr != nil { + return false, getErr + } + revisionSecurityType, getErr := catalogResult.GetString(ctx, row, 22) + if getErr != nil { + return false, getErr + } + baseSecurityType, getErr := catalogResult.GetString(ctx, row, 23) + if getErr != nil { + return false, getErr + } + if !strings.EqualFold(revisionSecurityType, baseSecurityType) { + return false, fmt.Errorf("UNSUPPORTED_ROUTINE_VERSION: routine %d has inconsistent revision and identity security contracts", id) + } + if strings.EqualFold(language, udf.LanguagePython) && !strings.EqualFold(revisionSecurityType, "INVOKER") { + return false, fmt.Errorf("UNSUPPORTED_ROUTINE_VERSION: Python routine catalog security type is not INVOKER") + } + if strings.EqualFold(language, udf.LanguageSQL) && !strings.EqualFold(revisionSecurityType, "DEFINER") { + return false, fmt.Errorf("UNSUPPORTED_ROUTINE_VERSION: SQL routine catalog security type is not DEFINER") + } + identityValid := routinePlanCatalogIdentityValidForLanguage( + language, + body, fingerprint, signatureKeySchemaVersion, canonicalInput, + returnDescriptor, signatureFingerprint, revisionArgTypes, + revisionRetType, definitionSchema, revisionABI, revisionAdapter, + revisionSDK, revisionNullPolicy, + ) + if _, duplicate := states[id]; duplicate { + return false, fmt.Errorf("UNSUPPORTED_ROUTINE_VERSION: routine catalog returned duplicate state for function %d", id) + } + states[id] = routinePlanCatalogState{ + activeRevision: activeRevision, namespaceVersion: namespaceVersion, + databaseID: databaseID, + revision: revision, language: language, volatility: volatility, nullPolicy: revisionNullPolicy, fingerprint: fingerprint, + artifactDigest: artifactDigest, environmentDigest: environmentDigest, + securityType: revisionSecurityType, + identityChecked: true, identityValid: identityValid, + } + } + } + return routinePlanDependenciesChanged(dependencies, states), nil +} + +// routinePlanCatalogIdentityValid verifies the complete immutable revision +// contract before a cached plan can be reused. Keeping this check separate +// from the SQL row decoder makes the important tamper cases directly +// testable: changing source, a descriptor, an ABI field, or a legacy logical +// return type while retaining the old fingerprint must invalidate the plan. +func routinePlanCatalogIdentityValid( + rawBody, fingerprint string, + signatureKeySchemaVersion int64, + canonicalInput, returnDescriptor, signatureFingerprint, revisionArgTypes, + revisionRetType string, + definitionSchema int64, + revisionABI, revisionAdapter, revisionSDK, revisionNullPolicy string, +) bool { + return routinePlanCatalogIdentityValidForLanguage( + udf.LanguagePython, + rawBody, fingerprint, signatureKeySchemaVersion, canonicalInput, + returnDescriptor, signatureFingerprint, revisionArgTypes, revisionRetType, + definitionSchema, revisionABI, revisionAdapter, revisionSDK, revisionNullPolicy, + ) +} + +func routinePlanCatalogIdentityValidForLanguage( + language string, + rawBody, fingerprint string, + signatureKeySchemaVersion int64, + canonicalInput, returnDescriptor, signatureFingerprint, revisionArgTypes, + revisionRetType string, + definitionSchema int64, + revisionABI, revisionAdapter, revisionSDK, revisionNullPolicy string, +) bool { + if strings.EqualFold(language, udf.LanguageSQL) { + expectedFingerprint, err := function.SQLRoutineFingerprint(rawBody, revisionArgTypes, revisionRetType) + return err == nil && + fingerprint == expectedFingerprint && + definitionSchema == int64(udf.SQLDefinitionSchemaVersion) && + revisionABI == "" && revisionAdapter == "" && revisionSDK == "" && + revisionNullPolicy == udf.NullCallHandler + } + if !strings.EqualFold(language, udf.LanguagePython) { + return false + } + decoded, err := function.DecodePythonRoutineBody(rawBody) + if err != nil { + return false + } + expectedInput, expectedReturn, expectedFingerprint, err := function.PythonSignatureMetadata(decoded.ArgTypes, decoded.ReturnType) + if err != nil { + return false + } + bodyFingerprint, err := function.PythonRoutineBodyFingerprint(decoded) + if err != nil { + return false + } + return bodyFingerprint == fingerprint && + signatureKeySchemaVersion == udf.PythonSignatureKeySchemaVersion && + canonicalInput == expectedInput && returnDescriptor == expectedReturn && signatureFingerprint == expectedFingerprint && + revisionArgTypes == expectedInput && + routineRevisionMetadataMatches(decoded, definitionSchema, revisionABI, revisionAdapter, revisionSDK, revisionNullPolicy) && + routineRevisionReturnTypeMatches(revisionRetType, decoded.ReturnType.Type().Oid) +} + +func routinePlanCatalogSQL(ids []uint64, accountID uint32) string { + values := make([]string, len(ids)) + for i, id := range ids { + values[i] = strconv.FormatUint(id, 10) + } + return fmt.Sprintf(`select f.function_id, f.active_revision, f.namespace_version, d.dat_id, + r.revision, r.language, r.volatility, r.definition_fingerprint, r.artifact_digest, + r.environment_digest, f.canonical_input_descriptor, f.return_descriptor, + f.signature_key_schema_version, f.signature_fingerprint, r.body, r.arg_types, + r.rettype, r.definition_schema_version, r.abi_contract, r.adapter_version, + r.sdk_version, r.null_policy, r.security_type, f.security_type + from mo_catalog.mo_user_defined_function f + join mo_catalog.mo_database d on d.datname = f.db and d.account_id = %d + join mo_catalog.mo_function_revisions r + on r.function_id = f.function_id and r.revision = f.active_revision + and r.namespace_version = f.namespace_version + where f.function_id in (%s);`, accountID, strings.Join(values, ",")) +} + +func routineRevisionMetadataMatches( + body function.PythonRoutineBody, + definitionSchema int64, + abi, adapter, sdk, nullPolicy string, +) bool { + return definitionSchema == int64(udf.PythonDefinitionSchemaVersion) && + abi == udf.PythonABIContract && adapter == udf.PythonAdapterVersion && + sdk == udf.PythonSDKVersion && nullPolicy == body.NullPolicy && + body.ABIContract == abi && body.AdapterVersion == adapter && body.SDKVersion == sdk +} + +func routineRevisionReturnTypeMatches(rettype string, oid types.T) bool { + if strings.EqualFold(rettype, oid.String()) { + return true + } + return strings.EqualFold(rettype, "decimal") && (oid == types.T_decimal64 || oid == types.T_decimal128) +} + +func validateRoutinePlanDependencyShape(dependency *planpb.RoutinePlanDependency, accountID uint32) error { + if dependency == nil || dependency.FunctionRef == nil { + return fmt.Errorf("UNSUPPORTED_ROUTINE_VERSION: routine plan dependency has no FunctionRef") + } + ref := dependency.FunctionRef + if ref.FunctionId == 0 || ref.Revision == 0 || ref.NamespaceVersion == 0 || + ref.AccountId != uint64(accountID) || ref.DatabaseId == 0 { + return fmt.Errorf("UNSUPPORTED_ROUTINE_VERSION: routine plan dependency has an invalid catalog identity") + } + if dependency.ContractVersion != udf.RoutinePlanContractVersion || + !udf.IsSHA256Digest(dependency.DefinitionFingerprint) { + return fmt.Errorf("UNSUPPORTED_ROUTINE_VERSION: routine plan dependency contract is incomplete") + } + if dependency.Volatility == "" || dependency.NullPolicy == "" || !dependency.MayError || dependency.Leakproof { + return fmt.Errorf("UNSUPPORTED_ROUTINE_VERSION: routine plan dependency semantic contract is incomplete") + } + switch strings.ToLower(dependency.Language) { + case udf.LanguagePython: + if dependency.SecurityMode != "INVOKER" { + return fmt.Errorf("UNSUPPORTED_ROUTINE_VERSION: Python routine plan dependency must use INVOKER security") + } + if !udf.IsSHA256Digest(dependency.ArtifactDigest) || !udf.IsSHA256Digest(dependency.EnvironmentDigest) { + return fmt.Errorf("UNSUPPORTED_ROUTINE_VERSION: Python routine plan dependency integrity is incomplete") + } + case udf.LanguageSQL: + if dependency.SecurityMode == "" { + return fmt.Errorf("UNSUPPORTED_ROUTINE_VERSION: SQL routine plan dependency has no security mode") + } + if dependency.ArtifactDigest != "" || dependency.EnvironmentDigest != "" { + return fmt.Errorf("UNSUPPORTED_ROUTINE_VERSION: SQL routine plan dependency has external integrity fields") + } + default: + return fmt.Errorf("UNSUPPORTED_ROUTINE_VERSION: routine plan dependency language %q is unsupported", dependency.Language) + } + return nil +} + +func routinePlanDependenciesChanged( + dependencies []*planpb.RoutinePlanDependency, + states map[uint64]routinePlanCatalogState, +) bool { + for _, dependency := range dependencies { + if dependency == nil || dependency.FunctionRef == nil { + return true + } + ref := dependency.FunctionRef + state, ok := states[ref.FunctionId] + if !ok || state.activeRevision == 0 || state.revision == 0 { + return true + } + if state.identityChecked && !state.identityValid { + return true + } + expectedSecurityMode := dependency.SecurityMode + if expectedSecurityMode == "" { + return true + } + if state.activeRevision != ref.Revision || state.revision != ref.Revision || + state.databaseID != ref.DatabaseId || + state.namespaceVersion != ref.NamespaceVersion || + state.volatility != dependency.Volatility || + state.nullPolicy != dependency.NullPolicy || + !strings.EqualFold(state.securityType, dependency.SecurityMode) || + (dependency.Language == udf.LanguagePython && expectedSecurityMode != "INVOKER") || + (dependency.Language == udf.LanguageSQL && expectedSecurityMode == "") || + dependency.MayError != true || dependency.Leakproof || + !strings.EqualFold(state.language, dependency.Language) || + state.fingerprint != dependency.DefinitionFingerprint || + state.artifactDigest != dependency.ArtifactDigest || + state.environmentDigest != dependency.EnvironmentDigest { + return true + } + } + return false +} diff --git a/pkg/frontend/routine_plan_validation_test.go b/pkg/frontend/routine_plan_validation_test.go new file mode 100644 index 0000000000000..bcabf01f4dd7d --- /dev/null +++ b/pkg/frontend/routine_plan_validation_test.go @@ -0,0 +1,304 @@ +// Copyright 2026 Matrix Origin +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package frontend + +import ( + "context" + "encoding/json" + "strings" + "testing" + + "github.com/matrixorigin/matrixone/pkg/container/types" + planpb "github.com/matrixorigin/matrixone/pkg/pb/plan" + sqlplan "github.com/matrixorigin/matrixone/pkg/sql/plan" + "github.com/matrixorigin/matrixone/pkg/sql/plan/function" + "github.com/matrixorigin/matrixone/pkg/udf" + "github.com/stretchr/testify/require" +) + +func testRoutinePlanDependency() *planpb.RoutinePlanDependency { + return &planpb.RoutinePlanDependency{ + FunctionRef: &planpb.FunctionRef{ + FunctionId: 41, + Revision: 7, + NamespaceVersion: 12, + AccountId: 9, + DatabaseId: 8, + }, + Language: udf.LanguagePython, + ContractVersion: udf.PythonPlanContractVersion, + DefinitionFingerprint: strings.Repeat("a", 64), + ArtifactDigest: strings.Repeat("b", 64), + EnvironmentDigest: strings.Repeat("c", 64), + Volatility: "VOLATILE", + NullPolicy: udf.NullCallHandler, + MayError: true, + SecurityMode: "INVOKER", + Leakproof: false, + } +} + +func testRoutinePlanState() routinePlanCatalogState { + return routinePlanCatalogState{ + activeRevision: 7, + namespaceVersion: 12, + databaseID: 8, + revision: 7, + language: udf.LanguagePython, + volatility: "VOLATILE", + nullPolicy: udf.NullCallHandler, + fingerprint: strings.Repeat("a", 64), + artifactDigest: strings.Repeat("b", 64), + environmentDigest: strings.Repeat("c", 64), + securityType: "INVOKER", + } +} + +func TestRoutinePlanDependenciesChangedRequiresExactPublication(t *testing.T) { + dependency := testRoutinePlanDependency() + state := testRoutinePlanState() + + require.False(t, routinePlanDependenciesChanged( + []*planpb.RoutinePlanDependency{dependency}, + map[uint64]routinePlanCatalogState{41: state}, + )) + + cases := []struct { + name string + mutate func(*routinePlanCatalogState) + }{ + {name: "active revision", mutate: func(s *routinePlanCatalogState) { + s.activeRevision++ + }}, + {name: "namespace", mutate: func(s *routinePlanCatalogState) { + s.namespaceVersion++ + }}, + {name: "database", mutate: func(s *routinePlanCatalogState) { + s.databaseID++ + }}, + {name: "revision", mutate: func(s *routinePlanCatalogState) { + s.revision++ + }}, + {name: "fingerprint", mutate: func(s *routinePlanCatalogState) { + s.fingerprint = strings.Repeat("d", 64) + }}, + {name: "artifact", mutate: func(s *routinePlanCatalogState) { + s.artifactDigest = strings.Repeat("d", 64) + }}, + {name: "environment", mutate: func(s *routinePlanCatalogState) { + s.environmentDigest = strings.Repeat("d", 64) + }}, + {name: "security", mutate: func(s *routinePlanCatalogState) { + s.securityType = "DEFINER" + }}, + {name: "language", mutate: func(s *routinePlanCatalogState) { + s.language = "sql" + }}, + {name: "volatility", mutate: func(s *routinePlanCatalogState) { + s.volatility = "IMMUTABLE" + }}, + {name: "missing", mutate: func(s *routinePlanCatalogState) { + s.activeRevision = 0 + }}, + } + for _, tc := range cases { + t.Run(tc.name, func(t *testing.T) { + changedState := state + tc.mutate(&changedState) + states := map[uint64]routinePlanCatalogState{41: changedState} + if tc.name == "missing" { + states = map[uint64]routinePlanCatalogState{} + } + require.True(t, routinePlanDependenciesChanged( + []*planpb.RoutinePlanDependency{dependency}, states)) + }) + } +} + +func TestValidateRoutinePlanDependencyShapeRejectsCrossAccountAndUnknownContracts(t *testing.T) { + require.NoError(t, validateRoutinePlanDependencyShape(testRoutinePlanDependency(), 9)) + + cases := []struct { + name string + mutate func(*planpb.RoutinePlanDependency) + }{ + {name: "cross account", mutate: func(d *planpb.RoutinePlanDependency) { + d.FunctionRef.AccountId = 10 + }}, + {name: "missing database", mutate: func(d *planpb.RoutinePlanDependency) { + d.FunctionRef.DatabaseId = 0 + }}, + {name: "unknown contract", mutate: func(d *planpb.RoutinePlanDependency) { + d.ContractVersion++ + }}, + {name: "missing integrity", mutate: func(d *planpb.RoutinePlanDependency) { + d.ArtifactDigest = "" + }}, + {name: "malformed digest", mutate: func(d *planpb.RoutinePlanDependency) { + d.DefinitionFingerprint = "definition-fingerprint" + }}, + {name: "missing identity", mutate: func(d *planpb.RoutinePlanDependency) { + d.FunctionRef = nil + }}, + } + for _, tc := range cases { + t.Run(tc.name, func(t *testing.T) { + dependency := testRoutinePlanDependency() + tc.mutate(dependency) + require.ErrorContains(t, + validateRoutinePlanDependencyShape(dependency, 9), + "UNSUPPORTED_ROUTINE_VERSION") + }) + } +} + +func TestRoutinePlanDependenciesIncludesBackgroundQueries(t *testing.T) { + dependency := testRoutinePlanDependency() + plan := &planpb.Plan{ + Plan: &planpb.Plan_Query{Query: &planpb.Query{ + RoutineDependencies: []*planpb.RoutinePlanDependency{dependency}, + BackgroundQueries: []*planpb.Query{{ + RoutineDependencies: []*planpb.RoutinePlanDependency{testRoutinePlanDependency()}, + }}, + }}, + } + dependencies, err := routinePlanDependencies(plan) + require.NoError(t, err) + require.Len(t, dependencies, 2) + require.Same(t, dependency, dependencies[0]) + require.Equal(t, dependency, dependencies[0]) +} + +func TestRoutinePlanDependenciesRejectsUnboundedQueryGraph(t *testing.T) { + root := &planpb.Query{} + current := root + for depth := 0; depth < maxRoutinePlanQueryDepth+1; depth++ { + next := &planpb.Query{} + current.BackgroundQueries = []*planpb.Query{next} + current = next + } + + _, err := routinePlanDependencies(&planpb.Plan{ + Plan: &planpb.Plan_Query{Query: root}, + }) + require.ErrorContains(t, err, "query nesting exceeds") +} + +func TestRoutinePlanDependenciesRejectsOversizedClosureAndCycles(t *testing.T) { + root := &planpb.Query{} + root.RoutineDependencies = make([]*planpb.RoutinePlanDependency, maxRoutinePlanDependencies+1) + _, err := routinePlanDependencies(&planpb.Plan{ + Plan: &planpb.Plan_Query{Query: root}, + }) + require.ErrorContains(t, err, "dependency count exceeds") + + oversized := testRoutinePlanDependency() + oversized.DefinitionFingerprint = strings.Repeat("a", maxRoutinePlanDependencyBytes) + _, err = routinePlanDependencies(&planpb.Plan{ + Plan: &planpb.Plan_Query{Query: &planpb.Query{ + RoutineDependencies: []*planpb.RoutinePlanDependency{oversized}, + }}, + }) + require.ErrorContains(t, err, "dependency bytes exceed") + + cycle := &planpb.Query{} + cycle.BackgroundQueries = []*planpb.Query{cycle} + _, err = routinePlanDependencies(&planpb.Plan{ + Plan: &planpb.Plan_Query{Query: cycle}, + }) + require.ErrorContains(t, err, "graph contains a cycle") +} + +func TestCachedRoutinePlanWithDependencyRequiresCatalogSession(t *testing.T) { + plan := &planpb.Plan{Plan: &planpb.Plan_Query{Query: &planpb.Query{ + RoutineDependencies: []*planpb.RoutinePlanDependency{testRoutinePlanDependency()}, + }}} + plain := &cachedPlan{plans: []*sqlplan.Plan{{Plan: &planpb.Plan_Query{Query: &planpb.Query{}}}}} + withRoutine := &cachedPlan{plans: []*sqlplan.Plan{plan}} + require.True(t, cachedRoutinePlanDependenciesCurrent(nil, plain)) + require.False(t, cachedRoutinePlanDependenciesCurrent(nil, withRoutine)) +} + +func TestExactlyOneCatalogResultSetRequiresOneCompleteResultSet(t *testing.T) { + ctx := context.Background() + _, err := exactlyOneCatalogResultSet(ctx, nil, "routine dependency catalog") + require.ErrorContains(t, err, "expected exactly one") + + _, err = exactlyOneCatalogResultSet(ctx, []ExecResult{nil, nil}, "routine dependency catalog") + require.ErrorContains(t, err, "returned 2 result sets") + + result := singleInt64Result("function_id", 41) + got, err := exactlyOneCatalogResultSet(ctx, []ExecResult{result}, "routine dependency catalog") + require.NoError(t, err) + require.Same(t, result, got) +} + +func TestRoutinePlanCatalogIdentityRejectsTamperedRevisionBody(t *testing.T) { + raw := pythonCatalogTestBody(t, types.T_int64) + body, err := function.DecodePythonRoutineBody(raw) + require.NoError(t, err) + input, output, signature, err := function.PythonSignatureMetadata(body.ArgTypes, body.ReturnType) + require.NoError(t, err) + fingerprint, err := function.PythonRoutineBodyFingerprint(body) + require.NoError(t, err) + + valid := func(candidate string) bool { + return routinePlanCatalogIdentityValid( + candidate, fingerprint, udf.PythonSignatureKeySchemaVersion, + input, output, signature, input, "bigint", + udf.PythonDefinitionSchemaVersion, udf.PythonABIContract, + udf.PythonAdapterVersion, udf.PythonSDKVersion, udf.NullCallHandler, + ) + } + require.True(t, valid(raw)) + + body.Source = "def f(ctx, value): return value + 1" + tampered, err := json.Marshal(body) + require.NoError(t, err) + require.False(t, valid(string(tampered)), "retaining an old revision fingerprint must invalidate the cached plan") + + require.False(t, routinePlanCatalogIdentityValid( + raw, fingerprint, udf.PythonSignatureKeySchemaVersion, + input, output, signature, input+" ", "bigint", + udf.PythonDefinitionSchemaVersion, udf.PythonABIContract, + udf.PythonAdapterVersion, udf.PythonSDKVersion, udf.NullCallHandler, + )) +} + +func TestRoutinePlanCatalogIdentityAcceptsAndRejectsSQLRevision(t *testing.T) { + const ( + body = "select value + 1" + argTypes = "[\"bigint\"]" + retType = "bigint" + ) + fingerprint, err := function.SQLRoutineFingerprint(body, argTypes, retType) + require.NoError(t, err) + valid := func(candidateBody, candidateArgs, candidateReturn, candidateFingerprint string) bool { + return routinePlanCatalogIdentityValidForLanguage( + udf.LanguageSQL, + candidateBody, candidateFingerprint, 0, "", "", "", + candidateArgs, candidateReturn, udf.SQLDefinitionSchemaVersion, + "", "", "", udf.NullCallHandler, + ) + } + require.True(t, valid(body, argTypes, retType, fingerprint)) + require.False(t, valid("select value + 2", argTypes, retType, fingerprint)) + require.False(t, valid(body, argTypes, "int", fingerprint)) + require.False(t, valid(body, argTypes, retType, strings.Repeat("a", 64))) + require.False(t, routinePlanCatalogIdentityValidForLanguage( + "lua", body, fingerprint, 0, "", "", "", argTypes, retType, + udf.SQLDefinitionSchemaVersion, "", "", "", udf.NullCallHandler, + )) +} diff --git a/pkg/frontend/snapshot.go b/pkg/frontend/snapshot.go index fc073bd5f1966..d47266181f485 100644 --- a/pkg/frontend/snapshot.go +++ b/pkg/frontend/snapshot.go @@ -127,6 +127,7 @@ var ( "mo_role_privs": systemCatalogRestoreRebuild, "mo_role_rule": systemCatalogRestoreCopy, "mo_user_defined_function": systemCatalogRestoreCopy, + "mo_function_revisions": systemCatalogRestoreCopy, "mo_stored_procedure": systemCatalogRestoreCopy, "mo_mysql_compatibility_mode": systemCatalogRestoreSkip, "mo_stages": systemCatalogRestoreCopy, @@ -1980,7 +1981,20 @@ func recreateTable( sourceSnapshot = fmt.Sprintf(" {SNAPSHOT = %s}", escapeSQLString(snapshotName)) } return restoreUserDefinedFunctionCatalogWithCurrentSchema( - ctx, bh, sourceSnapshot, curAccountID, toAccountId, + ctx, bh, sourceSnapshot, curAccountID, toAccountId, tblInfo.createSql, + ) + } + if isCurrentFunctionRevisionCatalog(tblInfo) { + curAccountID, accountErr := defines.GetAccountId(ctx) + if accountErr != nil { + return accountErr + } + sourceSnapshot := fmt.Sprintf(" {MO_TS = %d}", snapshotTs) + if curAccountID != toAccountId { + sourceSnapshot = fmt.Sprintf(" {SNAPSHOT = %s}", escapeSQLString(snapshotName)) + } + return restoreFunctionRevisionCatalogWithCurrentSchema( + ctx, bh, sourceSnapshot, curAccountID, toAccountId, tblInfo.createSql, ) } if isSequence(tblInfo) { diff --git a/pkg/frontend/snapshot_catalog_restore.go b/pkg/frontend/snapshot_catalog_restore.go index fb1e736aae297..c05103646c332 100644 --- a/pkg/frontend/snapshot_catalog_restore.go +++ b/pkg/frontend/snapshot_catalog_restore.go @@ -16,6 +16,7 @@ package frontend import ( "context" + "encoding/json" "fmt" "strconv" "strings" @@ -24,6 +25,9 @@ import ( "github.com/matrixorigin/matrixone/pkg/common/moerr" "github.com/matrixorigin/matrixone/pkg/common/pubsub" "github.com/matrixorigin/matrixone/pkg/defines" + "github.com/matrixorigin/matrixone/pkg/sql/plan/function" + "github.com/matrixorigin/matrixone/pkg/udf" + pythonudf "github.com/matrixorigin/matrixone/pkg/udf/python" ) type systemCatalogRestorePolicy uint8 @@ -79,10 +83,27 @@ var systemCatalogPostRestoreHandlers = []systemCatalogPostRestoreHandler{ const rolePrivilegeRestoreInsertBatchSize = 256 -const userDefinedFunctionCatalogColumns = `function_id, name, owner, args, arg_types, retType, body, language, db, definer, modified_time, created_time, type, security_type, comment, character_set_client, collation_connection, database_collation, sql_mode` +const userDefinedFunctionCatalogColumns = `function_id, active_revision, namespace_version, name, owner, args, arg_types, canonical_input_descriptor, return_descriptor, signature_key_schema_version, signature_fingerprint, retType, body, language, db, definer, modified_time, created_time, type, security_type, comment, character_set_client, collation_connection, database_collation, sql_mode` -const userDefinedFunctionCatalogSourceColumns = `function_id, name, owner, args, ` + - catalog.UserDefinedFunctionArgumentTypesSQL + `, retType, body, language, db, definer, modified_time, created_time, type, security_type, comment, character_set_client, collation_connection, database_collation, sql_mode` +const userDefinedFunctionCatalogSourceColumns = `function_id, active_revision, namespace_version, name, owner, args, ` + + catalog.UserDefinedFunctionArgumentTypesSQL + `, canonical_input_descriptor, return_descriptor, signature_key_schema_version, signature_fingerprint, retType, body, language, db, definer, modified_time, created_time, type, security_type, comment, character_set_client, collation_connection, database_collation, sql_mode` + +// userDefinedFunctionCatalogRevisionSourceColumns preserves immutable heads +// from a snapshot that has the revision columns but predates the exact +// descriptor identity fields. Python rows from such a snapshot remain +// rejected by the current resolver because their identity metadata is absent; +// ordinary SQL rows retain their historical catalog behavior. +const userDefinedFunctionCatalogRevisionSourceColumns = `function_id, active_revision, namespace_version, name, owner, args, ` + + catalog.UserDefinedFunctionArgumentTypesSQL + `, '', '', 0, '', retType, body, language, db, definer, modified_time, created_time, type, security_type, comment, character_set_client, collation_connection, database_collation, sql_mode` + +// userDefinedFunctionCatalogLegacySourceColumns is used for snapshots whose +// historical table predates immutable revision heads. Such rows remain +// usable for existing SQL catalog behavior, while the new Python resolver +// rejects the zero revision and requires an explicit CREATE/replace. +const userDefinedFunctionCatalogLegacySourceColumns = `function_id, 0, 0, name, owner, args, ` + + catalog.UserDefinedFunctionArgumentTypesSQL + `, '', '', 0, '', retType, body, language, db, definer, modified_time, created_time, type, security_type, comment, character_set_client, collation_connection, database_collation, sql_mode` + +const functionRevisionCatalogColumns = `function_id, revision, namespace_version, name, args, arg_types, rettype, body, language, definition_schema_version, abi_contract, adapter_version, artifact_digest, environment_digest, sdk_version, null_policy, volatility, definition_fingerprint, created_time, security_type` // isCurrentSchemaUserDefinedFunctionCatalog identifies the catalog whose // schema is owned by the running binary. Restoring a historical CREATE TABLE @@ -92,6 +113,14 @@ func isCurrentSchemaUserDefinedFunctionCatalog(tblInfo *tableInfo) bool { return tblInfo != nil && tblInfo.dbName == moCatalog && tblInfo.tblName == "mo_user_defined_function" } +// isCurrentFunctionRevisionCatalog identifies the immutable shared revision +// catalog whose schema is owned by the running binary. A historical CREATE +// TABLE must never replace it: SQL and Python resolvers need every contract +// column to reject stale or partially restored definitions before execution. +func isCurrentFunctionRevisionCatalog(tblInfo *tableInfo) bool { + return tblInfo != nil && tblInfo.dbName == moCatalog && tblInfo.tblName == "mo_function_revisions" +} + // restoreUserDefinedFunctionCatalogWithCurrentSchema restores historical UDF // rows into the current catalog shape. The source may predate arg_types, so // the copy deliberately derives it from args through the same ByteJson SQL @@ -103,6 +132,7 @@ func restoreUserDefinedFunctionCatalogWithCurrentSchema( sourceSnapshot string, sourceAccount uint32, targetAccount uint32, + sourceCreateSQL string, ) error { targetCtx := defines.AttachAccountId(ctx, targetAccount) tableName := qualifiedTableName(moCatalog, "mo_user_defined_function") @@ -113,11 +143,22 @@ func restoreUserDefinedFunctionCatalogWithCurrentSchema( return err } + sourceColumns := userDefinedFunctionCatalogLegacySourceColumns + lowerCreateSQL := strings.ToLower(sourceCreateSQL) + if strings.Contains(lowerCreateSQL, "active_revision") && strings.Contains(lowerCreateSQL, "namespace_version") { + sourceColumns = userDefinedFunctionCatalogRevisionSourceColumns + if strings.Contains(lowerCreateSQL, "canonical_input_descriptor") && + strings.Contains(lowerCreateSQL, "return_descriptor") && + strings.Contains(lowerCreateSQL, "signature_key_schema_version") && + strings.Contains(lowerCreateSQL, "signature_fingerprint") { + sourceColumns = userDefinedFunctionCatalogSourceColumns + } + } copySQL := fmt.Sprintf( "insert into %s (%s) select %s from %s%s", tableName, userDefinedFunctionCatalogColumns, - userDefinedFunctionCatalogSourceColumns, + sourceColumns, tableName, sourceSnapshot, ) @@ -127,6 +168,285 @@ func restoreUserDefinedFunctionCatalogWithCurrentSchema( return bh.ExecRestore(targetCtx, copySQL, sourceAccount, targetAccount) } +// validateRestoredFunctionRevisionCatalog validates the shared immutable +// revision table. Each language owns a distinct typed implementation contract; +// accepting a SQL row as Python (or vice versa) would make restore fail for a +// valid mixed catalog or publish a row that the resolver cannot execute. +func validateRestoredFunctionRevisionCatalog( + ctx context.Context, + bh BackgroundExec, + targetCtx context.Context, +) error { + tableName := qualifiedTableName(moCatalog, "mo_function_revisions") + query := fmt.Sprintf( + "select %s from %s order by function_id, revision;", + functionRevisionCatalogColumns, + tableName, + ) + rows, err := getStringColsList(targetCtx, bh, query, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19) + if err != nil { + return fmt.Errorf("UNSUPPORTED_ROUTINE_VERSION: restored Python revision catalog cannot be validated: %w", err) + } + for rowIndex, row := range rows { + if len(row) != 20 { + return fmt.Errorf("UNSUPPORTED_ROUTINE_VERSION: restored Python revision row %d has %d columns", rowIndex, len(row)) + } + functionID, err := strconv.ParseUint(row[0], 10, 64) + if err != nil || functionID == 0 { + return fmt.Errorf("UNSUPPORTED_ROUTINE_VERSION: restored Python revision row %d has an invalid function identity", rowIndex) + } + revision, err := strconv.ParseUint(row[1], 10, 64) + if err != nil || revision == 0 { + return fmt.Errorf("UNSUPPORTED_ROUTINE_VERSION: restored Python function %d has an invalid revision", functionID) + } + namespace, err := strconv.ParseUint(row[2], 10, 64) + if err != nil || namespace == 0 { + return fmt.Errorf("UNSUPPORTED_ROUTINE_VERSION: restored Python function %d has an invalid namespace version", functionID) + } + definitionSchema, err := strconv.Atoi(row[9]) + if err != nil { + return fmt.Errorf("UNSUPPORTED_ROUTINE_VERSION: restored function %d revision %d has an invalid definition schema", functionID, revision) + } + if strings.EqualFold(row[8], udf.LanguageSQL) { + if err := validateRestoredSQLFunctionRevision(functionID, revision, row, definitionSchema); err != nil { + return err + } + continue + } + if !strings.EqualFold(row[8], udf.LanguagePython) || + definitionSchema != udf.PythonDefinitionSchemaVersion || + row[10] != udf.PythonABIContract || + row[11] != udf.PythonAdapterVersion || + row[14] != udf.PythonSDKVersion || + !strings.EqualFold(row[16], "VOLATILE") || + !strings.EqualFold(row[19], "INVOKER") { + return fmt.Errorf("UNSUPPORTED_ROUTINE_VERSION: restored function %d revision %d has an unsupported execution contract", functionID, revision) + } + + body, err := function.DecodePythonRoutineBody(row[7]) + if err != nil { + return fmt.Errorf("UNSUPPORTED_ROUTINE_VERSION: restored Python function %d revision %d body is invalid: %w", functionID, revision, err) + } + if body.DefinitionSchemaVersion != definitionSchema || + body.ABIContract != row[10] || + body.AdapterVersion != row[11] || + body.ArtifactDigest != row[12] || + body.EnvironmentDigest != row[13] || + body.SDKVersion != row[14] || + body.NullPolicy != row[15] { + return fmt.Errorf("UNSUPPORTED_ROUTINE_VERSION: restored Python function %d revision %d metadata does not match its definition", functionID, revision) + } + fingerprint, err := function.PythonRoutineFingerprint(row[7]) + if err != nil || row[17] == "" || row[17] != fingerprint { + return fmt.Errorf("UNSUPPORTED_ROUTINE_VERSION: restored Python function %d revision %d fingerprint mismatch", functionID, revision) + } + // `args` retains the shared logical names while `arg_types` in the + // current Python revision is the exact descriptor identity used for + // overload selection. Validate both representations independently; + // comparing arg_types with the logical OID list would reject every + // current Python revision after restore. + if _, err := userDefinedFunctionArgumentTypesFromJSON(row[4]); err != nil { + return fmt.Errorf("UNSUPPORTED_ROUTINE_VERSION: restored Python function %d revision %d arguments are invalid: %w", functionID, revision, err) + } + var args []*function.Arg + if err := json.Unmarshal([]byte(row[4]), &args); err != nil { + return fmt.Errorf("UNSUPPORTED_ROUTINE_VERSION: restored Python function %d revision %d arguments are invalid: %w", functionID, revision, err) + } + routine := &function.Udf{ + Language: udf.LanguagePython, + Args: args, + RetType: row[6], + PythonArgTypes: append([]function.PythonTypeDescriptor(nil), body.ArgTypes...), + PythonReturnType: body.ReturnType, + } + if err := routine.ValidatePythonTypeContract(); err != nil { + return fmt.Errorf("UNSUPPORTED_ROUTINE_VERSION: restored Python function %d revision %d type contract mismatch: %w", functionID, revision, err) + } + if err := routine.ValidatePythonCatalogSignature(); err != nil { + return fmt.Errorf("UNSUPPORTED_ROUTINE_VERSION: restored Python function %d revision %d catalog signature mismatch: %w", functionID, revision, err) + } + expectedInput, _, _, err := function.PythonSignatureMetadata(body.ArgTypes, body.ReturnType) + if err != nil || row[5] != expectedInput { + return fmt.Errorf("UNSUPPORTED_ROUTINE_VERSION: restored Python function %d revision %d argument descriptor mismatch", functionID, revision) + } + } + return nil +} + +func validateRestoredSQLFunctionRevision(functionID, revision uint64, row []string, definitionSchema int) error { + if definitionSchema != udf.SQLDefinitionSchemaVersion || + row[10] != "" || row[11] != "" || row[12] != "" || row[13] != "" || row[14] != "" || + row[15] != udf.NullCallHandler || !strings.EqualFold(row[16], "VOLATILE") || + !strings.EqualFold(row[19], "DEFINER") { + return fmt.Errorf("UNSUPPORTED_ROUTINE_VERSION: restored SQL function %d revision %d has an unsupported execution contract", functionID, revision) + } + logicalArgTypes, err := userDefinedFunctionArgumentTypesFromJSON(row[4]) + if err != nil || logicalArgTypes != row[5] { + return fmt.Errorf("UNSUPPORTED_ROUTINE_VERSION: restored SQL function %d revision %d argument metadata is invalid", functionID, revision) + } + fingerprint, err := function.SQLRoutineFingerprint(row[7], row[5], row[6]) + if err != nil || row[17] == "" || row[17] != fingerprint { + return fmt.Errorf("UNSUPPORTED_ROUTINE_VERSION: restored SQL function %d revision %d fingerprint mismatch", functionID, revision) + } + return nil +} + +// validateRestoredFunctionCatalogHeads checks the other half of the shared +// immutable publication. The revision validator proves each copied row is +// self-consistent; this check proves that the active head points at that exact +// row after restore. +func validateRestoredFunctionCatalogHeads( + ctx context.Context, + bh BackgroundExec, + targetCtx context.Context, +) error { + query := `select cast(f.function_id as char), cast(f.active_revision as char), cast(f.namespace_version as char), + cast(coalesce(r.revision, 0) as char), cast(coalesce(r.namespace_version, 0) as char), + f.canonical_input_descriptor, f.return_descriptor, + cast(f.signature_key_schema_version as char), f.signature_fingerprint, + coalesce(r.arg_types, ''), coalesce(r.rettype, ''), coalesce(r.body, ''), + lower(f.language), coalesce(r.definition_fingerprint, ''), + coalesce(r.security_type, ''), coalesce(f.security_type, '') + from mo_catalog.mo_user_defined_function f + left join mo_catalog.mo_function_revisions r + on r.function_id = f.function_id and r.revision = f.active_revision + and r.namespace_version = f.namespace_version + where lower(f.language) in ("python", "sql") + order by f.function_id;` + rows, err := getStringColsList(targetCtx, bh, query, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15) + if err != nil { + return fmt.Errorf("UNSUPPORTED_ROUTINE_VERSION: restored routine catalog heads cannot be validated: %w", err) + } + for rowIndex, row := range rows { + if len(row) != 16 { + return fmt.Errorf("UNSUPPORTED_ROUTINE_VERSION: restored routine catalog head row %d has %d columns", rowIndex, len(row)) + } + functionID, err := strconv.ParseUint(row[0], 10, 64) + if err != nil || functionID == 0 { + return fmt.Errorf("UNSUPPORTED_ROUTINE_VERSION: restored routine catalog head row %d has an invalid function identity", rowIndex) + } + activeRevision, err := strconv.ParseUint(row[1], 10, 64) + if err != nil { + return fmt.Errorf("UNSUPPORTED_ROUTINE_VERSION: restored routine function %d has an invalid active revision: %v", functionID, err) + } + namespaceVersion, err := strconv.ParseUint(row[2], 10, 64) + if err != nil { + return fmt.Errorf("UNSUPPORTED_ROUTINE_VERSION: restored routine function %d has an invalid namespace version: %v", functionID, err) + } + language := strings.ToLower(row[12]) + if language == udf.LanguageSQL && activeRevision == 0 && namespaceVersion == 0 { + // Legacy SQL rows predate the shared revision table and retain their + // old execution path. A current SQL head is validated below. + continue + } + if activeRevision == 0 { + return fmt.Errorf("UNSUPPORTED_ROUTINE_VERSION: restored %s function %d has no active immutable revision", language, functionID) + } + if namespaceVersion == 0 { + return fmt.Errorf("UNSUPPORTED_ROUTINE_VERSION: restored %s function %d has an invalid namespace version", language, functionID) + } + revision, err := strconv.ParseUint(row[3], 10, 64) + if err != nil || revision != activeRevision { + return fmt.Errorf("UNSUPPORTED_ROUTINE_VERSION: restored %s function %d active revision does not exist", language, functionID) + } + revisionNamespace, err := strconv.ParseUint(row[4], 10, 64) + if err != nil || revisionNamespace != namespaceVersion { + return fmt.Errorf("UNSUPPORTED_ROUTINE_VERSION: restored %s function %d head namespace does not match its revision", language, functionID) + } + if language == udf.LanguageSQL { + if row[10] == "" || row[11] == "" { + return fmt.Errorf("UNSUPPORTED_ROUTINE_VERSION: restored SQL function %d has incomplete active revision", functionID) + } + if !strings.EqualFold(row[14], "DEFINER") || !strings.EqualFold(row[15], "DEFINER") { + return fmt.Errorf("UNSUPPORTED_ROUTINE_VERSION: restored SQL function %d has inconsistent security contract", functionID) + } + expectedFingerprint, fingerprintErr := function.SQLRoutineFingerprint(row[11], row[9], row[10]) + if fingerprintErr != nil || row[13] == "" || row[13] != expectedFingerprint { + return fmt.Errorf("UNSUPPORTED_ROUTINE_VERSION: restored SQL function %d head fingerprint does not match its active revision", functionID) + } + continue + } + if language != udf.LanguagePython { + return fmt.Errorf("UNSUPPORTED_ROUTINE_VERSION: restored function %d has unsupported language %q", functionID, row[12]) + } + if row[5] == "" || row[6] == "" || row[7] == "" || row[8] == "" || row[9] == "" || row[10] == "" || row[11] == "" || row[13] == "" || + !strings.EqualFold(row[14], "INVOKER") || !strings.EqualFold(row[15], "INVOKER") || + !strings.EqualFold(row[14], row[15]) { + return fmt.Errorf("UNSUPPORTED_ROUTINE_VERSION: restored Python function %d has incomplete head identity", functionID) + } + body, err := function.DecodePythonRoutineBody(row[11]) + if err != nil { + return fmt.Errorf("UNSUPPORTED_ROUTINE_VERSION: restored Python function %d head body is invalid: %w", functionID, err) + } + input, output, signature, err := function.PythonSignatureMetadata(body.ArgTypes, body.ReturnType) + if err != nil { + return fmt.Errorf("UNSUPPORTED_ROUTINE_VERSION: restored Python function %d head signature is invalid: %w", functionID, err) + } + bodyFingerprint, fingerprintErr := function.PythonRoutineFingerprint(row[11]) + if fingerprintErr != nil || row[13] != bodyFingerprint || + row[5] != input || row[6] != output || row[7] != strconv.Itoa(udf.PythonSignatureKeySchemaVersion) || row[8] != signature || row[9] != input { + return fmt.Errorf("UNSUPPORTED_ROUTINE_VERSION: restored Python function %d head identity does not match its active revision", functionID) + } + } + return nil +} + +// restoreFunctionRevisionCatalogWithCurrentSchema restores only a revision +// table that already carries the current immutable shared contract. Older/demo +// revision tables are intentionally left empty after the current table is +// created. Their rows cannot be losslessly interpreted by the new resolver, and +// the corresponding active head will be rejected until the function is +// explicitly recreated with the current DDL. +func restoreFunctionRevisionCatalogWithCurrentSchema( + ctx context.Context, + bh BackgroundExec, + sourceSnapshot string, + sourceAccount uint32, + targetAccount uint32, + sourceCreateSQL string, +) error { + targetCtx := defines.AttachAccountId(ctx, targetAccount) + tableName := qualifiedTableName(moCatalog, "mo_function_revisions") + if err := bh.Exec(targetCtx, dropTableIfExistsSQL(moCatalog, "mo_function_revisions")); err != nil { + return err + } + if err := bh.Exec(targetCtx, MoCatalogMoFunctionRevisionDDL); err != nil { + return err + } + + // Requiring every current column makes the refusal stable for all older + // layouts. Checking the CREATE text also avoids querying a historical table + // with columns that only exist in the target schema. + lowerCreateSQL := strings.ToLower(sourceCreateSQL) + for _, column := range strings.Split(functionRevisionCatalogColumns, ", ") { + if !strings.Contains(lowerCreateSQL, strings.ToLower(column)) { + return nil + } + } + + copySQL := fmt.Sprintf( + "insert into %s (%s) select %s from %s%s", + tableName, + functionRevisionCatalogColumns, + functionRevisionCatalogColumns, + tableName, + sourceSnapshot, + ) + if sourceAccount == targetAccount { + if err := bh.Exec(targetCtx, copySQL); err != nil { + return err + } + } else if err := bh.ExecRestore(targetCtx, copySQL, sourceAccount, targetAccount); err != nil { + return err + } + // A current revision table is executable metadata. Validate the copied + // rows after the transport has completed so a backup/restore or cross + // account path cannot publish a row whose body, logical catalog columns, + // digest, or current ABI disagree. The resolver repeats this check at bind + // time; restore must fail early as well, before a bad head can be reused. + return validateRestoredFunctionRevisionCatalog(ctx, bh, targetCtx) +} + func restoreSystemCatalogsAfterObjects( ctx context.Context, sid string, @@ -151,6 +471,83 @@ func restoreSystemCatalogsAfterObjects( return err } } + if bh == nil { + return nil + } + if err := validateRestoredFunctionCatalogHeads( + ctx, + bh, + defines.AttachAccountId(ctx, targetAccount), + ); err != nil { + return err + } + return publishRestoredPythonArtifacts( + ctx, + sid, + bh, + defines.AttachAccountId(ctx, targetAccount), + targetAccount, + ) +} + +// publishRestoredPythonArtifacts rebuilds the account-scoped immutable +// artifact objects after a catalog restore. Catalog rows and FileService +// objects are separate persistence domains; copying only the revision table +// would leave a syntactically valid head that cannot execute after restore. +// This runs after all catalog identity/head checks, uses the exact restored +// revision body, and publishes write-once objects before restore returns. +func publishRestoredPythonArtifacts( + ctx context.Context, + sid string, + bh BackgroundExec, + targetCtx context.Context, + targetAccount uint32, +) error { + rows, err := getStringColsList( + targetCtx, + bh, + "select language, artifact_digest, body from mo_catalog.mo_function_revisions order by function_id, revision;", + 0, 1, 2, + ) + if err != nil { + return fmt.Errorf("UNSUPPORTED_ROUTINE_VERSION: restored Python artifacts cannot be inspected: %w", err) + } + needStore := false + for rowIndex, row := range rows { + if len(row) != 3 { + return fmt.Errorf("UNSUPPORTED_ROUTINE_VERSION: restored revision row %d has %d artifact columns", rowIndex, len(row)) + } + if strings.EqualFold(row[0], udf.LanguagePython) { + needStore = true + break + } + } + if !needStore { + return nil + } + pu := getPuIfPresent(sid) + if pu == nil || pu.FileService == nil { + return moerr.NewNotSupportedNoCtx("Python artifact store is required to restore a current Python revision") + } + store, err := pythonudf.NewFileArtifactStore(pu.FileService, pythonudf.DefaultMaxArtifactBytes) + if err != nil { + return err + } + for rowIndex, row := range rows { + if !strings.EqualFold(row[0], udf.LanguagePython) { + continue + } + body, decodeErr := function.DecodePythonRoutineBody(row[2]) + if decodeErr != nil { + return fmt.Errorf("UNSUPPORTED_ROUTINE_VERSION: restored Python artifact row %d is invalid: %w", rowIndex, decodeErr) + } + if body.ArtifactDigest != row[1] { + return fmt.Errorf("UNSUPPORTED_ROUTINE_VERSION: restored Python artifact row %d digest does not match its revision", rowIndex) + } + if _, publishErr := store.Publish(ctx, uint64(targetAccount), body.Handler, body.Source); publishErr != nil { + return publishErr + } + } return nil } diff --git a/pkg/frontend/snapshot_restore_with_ts.go b/pkg/frontend/snapshot_restore_with_ts.go index a30be51a15902..5b6d50b475ba3 100644 --- a/pkg/frontend/snapshot_restore_with_ts.go +++ b/pkg/frontend/snapshot_restore_with_ts.go @@ -576,6 +576,17 @@ func recreateTableFromTS( fmt.Sprintf(" {MO_TS = %d}", snapshotTs), restoreAccount, toAccountId, + tblInfo.createSql, + ) + } + if isCurrentFunctionRevisionCatalog(tblInfo) { + return restoreFunctionRevisionCatalogWithCurrentSchema( + ctx, + bh, + fmt.Sprintf(" {MO_TS = %d}", snapshotTs), + restoreAccount, + toAccountId, + tblInfo.createSql, ) } if isSequence(tblInfo) { diff --git a/pkg/frontend/snapshot_test.go b/pkg/frontend/snapshot_test.go index 6ae7a25e19cc5..bf0bc46131d3b 100644 --- a/pkg/frontend/snapshot_test.go +++ b/pkg/frontend/snapshot_test.go @@ -1804,6 +1804,20 @@ func TestRecreateUserDefinedFunctionCatalogPreservesCurrentSchema(t *testing.T) tblName: "mo_user_defined_function", createSql: legacyCreateSQL, } + currentUDFTable := &tableInfo{ + dbName: moCatalog, + tblName: "mo_user_defined_function", + createSql: MoCatalogMoUserDefinedFunctionDDL, + } + + t.Run("current schema preserves revision head", func(t *testing.T) { + bh := &backgroundExecTest{} + bh.init() + ctx := defines.AttachAccountId(t.Context(), sourceAccount) + require.NoError(t, recreateTable(ctx, "", bh, snapshotName, currentUDFTable, sourceAccount, snapshotTS)) + require.Contains(t, bh.executedSQLs[len(bh.executedSQLs)-1], + " select "+userDefinedFunctionCatalogSourceColumns+" from ") + }) t.Run("same account timestamp snapshot", func(t *testing.T) { bh := &backgroundExecTest{} @@ -1814,7 +1828,7 @@ func TestRecreateUserDefinedFunctionCatalogPreservesCurrentSchema(t *testing.T) dropTableIfExistsSQL(moCatalog, udfTable.tblName), MoCatalogMoUserDefinedFunctionDDL, "insert into `mo_catalog`.`mo_user_defined_function` (" + userDefinedFunctionCatalogColumns + - ") select " + userDefinedFunctionCatalogSourceColumns + + ") select " + userDefinedFunctionCatalogLegacySourceColumns + " from `mo_catalog`.`mo_user_defined_function` {MO_TS = 123}", }, bh.executedSQLs) require.NotContains(t, strings.Join(bh.executedSQLs, "\n"), legacyCreateSQL) @@ -1827,7 +1841,7 @@ func TestRecreateUserDefinedFunctionCatalogPreservesCurrentSchema(t *testing.T) require.NoError(t, recreateTable(ctx, "", bh, snapshotName, udfTable, targetAccount, snapshotTS, false)) require.Equal(t, "insert into `mo_catalog`.`mo_user_defined_function` ("+userDefinedFunctionCatalogColumns+ - ") select "+userDefinedFunctionCatalogSourceColumns+ + ") select "+userDefinedFunctionCatalogLegacySourceColumns+ " from `mo_catalog`.`mo_user_defined_function` {SNAPSHOT = 'udf_snapshot'}", bh.executedSQLs[len(bh.executedSQLs)-1], ) @@ -1842,7 +1856,7 @@ func TestRecreateUserDefinedFunctionCatalogPreservesCurrentSchema(t *testing.T) )) require.Equal(t, "insert into `mo_catalog`.`mo_user_defined_function` ("+userDefinedFunctionCatalogColumns+ - ") select "+userDefinedFunctionCatalogSourceColumns+ + ") select "+userDefinedFunctionCatalogLegacySourceColumns+ " from `mo_catalog`.`mo_user_defined_function` {MO_TS = 123}", bh.executedSQLs[len(bh.executedSQLs)-1], ) @@ -1856,7 +1870,7 @@ func TestRecreateUserDefinedFunctionCatalogPreservesCurrentSchema(t *testing.T) require.NoError(t, reCreateTableWithPitr(ctx, "", bh, "udf_pitr", snapshotTS, udfTable)) require.Equal(t, "insert into `mo_catalog`.`mo_user_defined_function` ("+userDefinedFunctionCatalogColumns+ - ") select "+userDefinedFunctionCatalogSourceColumns+ + ") select "+userDefinedFunctionCatalogLegacySourceColumns+ " from `mo_catalog`.`mo_user_defined_function` {MO_TS = 123}", bh.executedSQLs[len(bh.executedSQLs)-1], ) @@ -1864,6 +1878,105 @@ func TestRecreateUserDefinedFunctionCatalogPreservesCurrentSchema(t *testing.T) }) } +func TestRecreatePythonFunctionRevisionCatalogPreservesCurrentSchema(t *testing.T) { + const ( + sourceAccount = uint32(10) + targetAccount = uint32(20) + snapshotName = "python_revision_snapshot" + snapshotTS = int64(123) + ) + legacyCreateSQL := "create table mo_catalog.mo_function_revisions (function_id bigint, revision bigint, name varchar(100), body text)" + legacyTable := &tableInfo{ + dbName: moCatalog, + tblName: "mo_function_revisions", + createSql: legacyCreateSQL, + } + currentTable := &tableInfo{ + dbName: moCatalog, + tblName: "mo_function_revisions", + createSql: MoCatalogMoFunctionRevisionDDL, + } + copySQL := func(sourceSnapshot string) string { + return "insert into `mo_catalog`.`mo_function_revisions` (" + functionRevisionCatalogColumns + + ") select " + functionRevisionCatalogColumns + + " from `mo_catalog`.`mo_function_revisions`" + sourceSnapshot + } + validationSQL := fmt.Sprintf( + "select %s from %s order by function_id, revision;", + functionRevisionCatalogColumns, + qualifiedTableName(moCatalog, "mo_function_revisions"), + ) + + t.Run("same account current revision schema", func(t *testing.T) { + bh := &backgroundExecTest{} + bh.init() + bh.sql2result[validationSQL] = &MysqlResultSet{} + ctx := defines.AttachAccountId(t.Context(), sourceAccount) + require.NoError(t, recreateTable(ctx, "", bh, snapshotName, currentTable, sourceAccount, snapshotTS)) + require.Equal(t, []string{ + dropTableIfExistsSQL(moCatalog, currentTable.tblName), + MoCatalogMoFunctionRevisionDDL, + copySQL(" {MO_TS = 123}"), + validationSQL, + }, bh.executedSQLs) + }) + + t.Run("legacy revision rows are not copied", func(t *testing.T) { + bh := &backgroundExecTest{} + bh.init() + ctx := defines.AttachAccountId(t.Context(), sourceAccount) + require.NoError(t, recreateTable(ctx, "", bh, snapshotName, legacyTable, sourceAccount, snapshotTS)) + require.Equal(t, []string{ + dropTableIfExistsSQL(moCatalog, legacyTable.tblName), + MoCatalogMoFunctionRevisionDDL, + }, bh.executedSQLs) + require.NotContains(t, strings.Join(bh.executedSQLs, "\n"), legacyCreateSQL) + }) + + t.Run("cross account named snapshot", func(t *testing.T) { + bh := &backgroundExecTest{} + bh.init() + bh.sql2result[validationSQL] = &MysqlResultSet{} + ctx := defines.AttachAccountId(t.Context(), sourceAccount) + require.NoError(t, recreateTable(ctx, "", bh, snapshotName, currentTable, targetAccount, snapshotTS)) + require.Equal(t, []string{ + dropTableIfExistsSQL(moCatalog, currentTable.tblName), + MoCatalogMoFunctionRevisionDDL, + copySQL(" {SNAPSHOT = 'python_revision_snapshot'}"), + validationSQL, + }, bh.executedSQLs) + }) + + t.Run("cross account timestamp restore", func(t *testing.T) { + bh := &backgroundExecTest{} + bh.init() + bh.sql2result[validationSQL] = &MysqlResultSet{} + require.NoError(t, recreateTableFromTS( + t.Context(), "", bh, currentTable, snapshotTS, sourceAccount, targetAccount, + )) + require.Equal(t, []string{ + dropTableIfExistsSQL(moCatalog, currentTable.tblName), + MoCatalogMoFunctionRevisionDDL, + copySQL(" {MO_TS = 123}"), + validationSQL, + }, bh.executedSQLs) + }) + + t.Run("point in time recovery", func(t *testing.T) { + bh := &backgroundExecTest{} + bh.init() + bh.sql2result[validationSQL] = &MysqlResultSet{} + ctx := defines.AttachAccountId(t.Context(), sourceAccount) + require.NoError(t, reCreateTableWithPitr(ctx, "", bh, "python_revision_pitr", snapshotTS, currentTable)) + require.Equal(t, []string{ + dropTableIfExistsSQL(moCatalog, currentTable.tblName), + MoCatalogMoFunctionRevisionDDL, + copySQL(" {MO_TS = 123}"), + validationSQL, + }, bh.executedSQLs) + }) +} + func Test_fkTablesTopoSortWithTS(t *testing.T) { convey.Convey("fkTablesTopoSortWithTS ", t, func() { ctrl := gomock.NewController(t) diff --git a/pkg/pb/plan/plan.pb.go b/pkg/pb/plan/plan.pb.go index dff90cbf4d436..bced50c135912 100644 --- a/pkg/pb/plan/plan.pb.go +++ b/pkg/pb/plan/plan.pb.go @@ -3062,10 +3062,12 @@ type Function struct { // Planner provenance only. Explicit CAST syntax can share the legacy CAST // overload while optimizers still distinguish it from an implicit cast. // Older CNs safely ignore this unknown field and execute the legacy overload. - SyntaxExplicitCast bool `protobuf:"varint,5,opt,name=syntax_explicit_cast,json=syntaxExplicitCast,proto3" json:"syntax_explicit_cast,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` + SyntaxExplicitCast bool `protobuf:"varint,5,opt,name=syntax_explicit_cast,json=syntaxExplicitCast,proto3" json:"syntax_explicit_cast,omitempty"` + // Typed routine metadata. Ordinary builtin Function values leave this nil. + RoutineCall *RoutineCall `protobuf:"bytes,6,opt,name=routine_call,json=routineCall,proto3" json:"routine_call,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` } func (m *Function) Reset() { *m = Function{} } @@ -3136,6 +3138,13 @@ func (m *Function) GetSyntaxExplicitCast() bool { return false } +func (m *Function) GetRoutineCall() *RoutineCall { + if m != nil { + return m.RoutineCall + } + return nil +} + // PreparedNumericMetadata is sparse planner provenance for a numeric overload // or runtime string domain that cannot be represented by Expr.Type alone. // It is kept behind one optional pointer on Expr because the metadata is rare; @@ -10846,9 +10855,14 @@ type Query struct { // execution so a committed concurrent CREATE INDEX can rebuild the plan; // without a definition change, it returns the original key-not-found error. UnresolvedIndexHints []*UnresolvedIndexHint `protobuf:"bytes,16,rep,name=unresolved_index_hints,json=unresolvedIndexHints,proto3" json:"unresolved_index_hints,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` + // Exact routine revisions captured while binding this statement. The + // expression tree carries the call-site implementation, while this bounded + // envelope lets prepared and ordinary plan-cache reuse validate the shared + // catalog namespace without walking or reinterpreting user source. + RoutineDependencies []*RoutinePlanDependency `protobuf:"bytes,17,rep,name=routine_dependencies,json=routineDependencies,proto3" json:"routine_dependencies,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` } func (m *Query) Reset() { *m = Query{} } @@ -10996,6 +11010,13 @@ func (m *Query) GetUnresolvedIndexHints() []*UnresolvedIndexHint { return nil } +func (m *Query) GetRoutineDependencies() []*RoutinePlanDependency { + if m != nil { + return m.RoutineDependencies + } + return nil +} + type TransationControl struct { // TransationControl type TclType TransationControl_TclType `protobuf:"varint,1,opt,name=tcl_type,json=tclType,proto3,enum=plan.TransationControl_TclType" json:"tcl_type,omitempty"` @@ -16340,6 +16361,603 @@ func (m *UnresolvedIndexHint) GetIndexName() string { return "" } +// FunctionRef is the exact catalog identity carried by an executable plan. +// A zero revision is invalid for external routines; the planner validates the +// complete call before a fragment is dispatched. +type FunctionRef struct { + FunctionId uint64 `protobuf:"varint,1,opt,name=function_id,json=functionId,proto3" json:"function_id,omitempty"` + Revision uint64 `protobuf:"varint,2,opt,name=revision,proto3" json:"revision,omitempty"` + NamespaceVersion uint64 `protobuf:"varint,3,opt,name=namespace_version,json=namespaceVersion,proto3" json:"namespace_version,omitempty"` + // Function identity is tenant/database scoped. These fields prevent a + // plan fragment from being interpreted in a different catalog namespace. + AccountId uint64 `protobuf:"varint,4,opt,name=account_id,json=accountId,proto3" json:"account_id,omitempty"` + DatabaseId uint64 `protobuf:"varint,5,opt,name=database_id,json=databaseId,proto3" json:"database_id,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *FunctionRef) Reset() { *m = FunctionRef{} } +func (m *FunctionRef) String() string { return proto.CompactTextString(m) } +func (*FunctionRef) ProtoMessage() {} +func (*FunctionRef) Descriptor() ([]byte, []int) { + return fileDescriptor_2d655ab2f7683c23, []int{153} +} +func (m *FunctionRef) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *FunctionRef) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_FunctionRef.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *FunctionRef) XXX_Merge(src proto.Message) { + xxx_messageInfo_FunctionRef.Merge(m, src) +} +func (m *FunctionRef) XXX_Size() int { + return m.ProtoSize() +} +func (m *FunctionRef) XXX_DiscardUnknown() { + xxx_messageInfo_FunctionRef.DiscardUnknown(m) +} + +var xxx_messageInfo_FunctionRef proto.InternalMessageInfo + +func (m *FunctionRef) GetFunctionId() uint64 { + if m != nil { + return m.FunctionId + } + return 0 +} + +func (m *FunctionRef) GetRevision() uint64 { + if m != nil { + return m.Revision + } + return 0 +} + +func (m *FunctionRef) GetNamespaceVersion() uint64 { + if m != nil { + return m.NamespaceVersion + } + return 0 +} + +func (m *FunctionRef) GetAccountId() uint64 { + if m != nil { + return m.AccountId + } + return 0 +} + +func (m *FunctionRef) GetDatabaseId() uint64 { + if m != nil { + return m.DatabaseId + } + return 0 +} + +type SqlRoutineImplementation struct { + DefinitionFingerprint []byte `protobuf:"bytes,1,opt,name=definition_fingerprint,json=definitionFingerprint,proto3" json:"definition_fingerprint,omitempty"` + SemanticDefinitionSchemaVersion int32 `protobuf:"varint,2,opt,name=semantic_definition_schema_version,json=semanticDefinitionSchemaVersion,proto3" json:"semantic_definition_schema_version,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *SqlRoutineImplementation) Reset() { *m = SqlRoutineImplementation{} } +func (m *SqlRoutineImplementation) String() string { return proto.CompactTextString(m) } +func (*SqlRoutineImplementation) ProtoMessage() {} +func (*SqlRoutineImplementation) Descriptor() ([]byte, []int) { + return fileDescriptor_2d655ab2f7683c23, []int{154} +} +func (m *SqlRoutineImplementation) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *SqlRoutineImplementation) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_SqlRoutineImplementation.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *SqlRoutineImplementation) XXX_Merge(src proto.Message) { + xxx_messageInfo_SqlRoutineImplementation.Merge(m, src) +} +func (m *SqlRoutineImplementation) XXX_Size() int { + return m.ProtoSize() +} +func (m *SqlRoutineImplementation) XXX_DiscardUnknown() { + xxx_messageInfo_SqlRoutineImplementation.DiscardUnknown(m) +} + +var xxx_messageInfo_SqlRoutineImplementation proto.InternalMessageInfo + +func (m *SqlRoutineImplementation) GetDefinitionFingerprint() []byte { + if m != nil { + return m.DefinitionFingerprint + } + return nil +} + +func (m *SqlRoutineImplementation) GetSemanticDefinitionSchemaVersion() int32 { + if m != nil { + return m.SemanticDefinitionSchemaVersion + } + return 0 +} + +type PythonRoutineImplementation struct { + Handler string `protobuf:"bytes,1,opt,name=handler,proto3" json:"handler,omitempty"` + // Source is retained only so an old serialized demo plan can be identified + // and rejected. A current executable plan must carry the immutable artifact + // digest and resolve the artifact through the runtime artifact store. + Source string `protobuf:"bytes,2,opt,name=source,proto3" json:"source,omitempty"` // Deprecated: Do not use. + AbiContract string `protobuf:"bytes,3,opt,name=abi_contract,json=abiContract,proto3" json:"abi_contract,omitempty"` + AdapterVersion string `protobuf:"bytes,4,opt,name=adapter_version,json=adapterVersion,proto3" json:"adapter_version,omitempty"` + ArtifactDigest string `protobuf:"bytes,5,opt,name=artifact_digest,json=artifactDigest,proto3" json:"artifact_digest,omitempty"` + EnvironmentDigest string `protobuf:"bytes,6,opt,name=environment_digest,json=environmentDigest,proto3" json:"environment_digest,omitempty"` + SdkVersion string `protobuf:"bytes,7,opt,name=sdk_version,json=sdkVersion,proto3" json:"sdk_version,omitempty"` + DefinitionSchemaVersion int32 `protobuf:"varint,8,opt,name=definition_schema_version,json=definitionSchemaVersion,proto3" json:"definition_schema_version,omitempty"` + Mode string `protobuf:"bytes,9,opt,name=mode,proto3" json:"mode,omitempty"` + NullPolicy string `protobuf:"bytes,10,opt,name=null_policy,json=nullPolicy,proto3" json:"null_policy,omitempty"` + DefinitionFingerprint string `protobuf:"bytes,11,opt,name=definition_fingerprint,json=definitionFingerprint,proto3" json:"definition_fingerprint,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *PythonRoutineImplementation) Reset() { *m = PythonRoutineImplementation{} } +func (m *PythonRoutineImplementation) String() string { return proto.CompactTextString(m) } +func (*PythonRoutineImplementation) ProtoMessage() {} +func (*PythonRoutineImplementation) Descriptor() ([]byte, []int) { + return fileDescriptor_2d655ab2f7683c23, []int{155} +} +func (m *PythonRoutineImplementation) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *PythonRoutineImplementation) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_PythonRoutineImplementation.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *PythonRoutineImplementation) XXX_Merge(src proto.Message) { + xxx_messageInfo_PythonRoutineImplementation.Merge(m, src) +} +func (m *PythonRoutineImplementation) XXX_Size() int { + return m.ProtoSize() +} +func (m *PythonRoutineImplementation) XXX_DiscardUnknown() { + xxx_messageInfo_PythonRoutineImplementation.DiscardUnknown(m) +} + +var xxx_messageInfo_PythonRoutineImplementation proto.InternalMessageInfo + +func (m *PythonRoutineImplementation) GetHandler() string { + if m != nil { + return m.Handler + } + return "" +} + +// Deprecated: Do not use. +func (m *PythonRoutineImplementation) GetSource() string { + if m != nil { + return m.Source + } + return "" +} + +func (m *PythonRoutineImplementation) GetAbiContract() string { + if m != nil { + return m.AbiContract + } + return "" +} + +func (m *PythonRoutineImplementation) GetAdapterVersion() string { + if m != nil { + return m.AdapterVersion + } + return "" +} + +func (m *PythonRoutineImplementation) GetArtifactDigest() string { + if m != nil { + return m.ArtifactDigest + } + return "" +} + +func (m *PythonRoutineImplementation) GetEnvironmentDigest() string { + if m != nil { + return m.EnvironmentDigest + } + return "" +} + +func (m *PythonRoutineImplementation) GetSdkVersion() string { + if m != nil { + return m.SdkVersion + } + return "" +} + +func (m *PythonRoutineImplementation) GetDefinitionSchemaVersion() int32 { + if m != nil { + return m.DefinitionSchemaVersion + } + return 0 +} + +func (m *PythonRoutineImplementation) GetMode() string { + if m != nil { + return m.Mode + } + return "" +} + +func (m *PythonRoutineImplementation) GetNullPolicy() string { + if m != nil { + return m.NullPolicy + } + return "" +} + +func (m *PythonRoutineImplementation) GetDefinitionFingerprint() string { + if m != nil { + return m.DefinitionFingerprint + } + return "" +} + +// RoutineCall is the common typed call envelope for SQL and external +// language routines. Function.Args remains the normal expression argument +// list so existing expression traversal and CASE/selection ownership stay +// intact; this field carries only immutable routine identity and semantics. +type RoutineCall struct { + ContractVersion int32 `protobuf:"varint,1,opt,name=contract_version,json=contractVersion,proto3" json:"contract_version,omitempty"` + FunctionRef *FunctionRef `protobuf:"bytes,2,opt,name=function_ref,json=functionRef,proto3" json:"function_ref,omitempty"` + Language string `protobuf:"bytes,3,opt,name=language,proto3" json:"language,omitempty"` + ArgumentTypes []*Type `protobuf:"bytes,4,rep,name=argument_types,json=argumentTypes,proto3" json:"argument_types,omitempty"` + ReturnType Type `protobuf:"bytes,5,opt,name=return_type,json=returnType,proto3" json:"return_type"` + Volatility string `protobuf:"bytes,6,opt,name=volatility,proto3" json:"volatility,omitempty"` + NullPolicy string `protobuf:"bytes,7,opt,name=null_policy,json=nullPolicy,proto3" json:"null_policy,omitempty"` + // Types that are valid to be assigned to Implementation: + // + // *RoutineCall_Sql + // *RoutineCall_Python + Implementation isRoutineCall_Implementation `protobuf_oneof:"implementation"` + // Reserved from the current executable contract. Statement identity, + // session values, and group fences are created at execution time; keeping + // them in a prepared plan would allow stale state to cross executions. + Context map[string]string `protobuf:"bytes,10,rep,name=context,proto3" json:"context,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` // Deprecated: Do not use. + // These fields are the common semantic contract. They must survive SQL + // lowering and expression/plan cloning; the optimizer may only move a + // routine call when the attached contract permits it. + CallsiteId string `protobuf:"bytes,11,opt,name=callsite_id,json=callsiteId,proto3" json:"callsite_id,omitempty"` + MayError bool `protobuf:"varint,12,opt,name=may_error,json=mayError,proto3" json:"may_error,omitempty"` + SecurityMode string `protobuf:"bytes,13,opt,name=security_mode,json=securityMode,proto3" json:"security_mode,omitempty"` + Leakproof bool `protobuf:"varint,14,opt,name=leakproof,proto3" json:"leakproof,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *RoutineCall) Reset() { *m = RoutineCall{} } +func (m *RoutineCall) String() string { return proto.CompactTextString(m) } +func (*RoutineCall) ProtoMessage() {} +func (*RoutineCall) Descriptor() ([]byte, []int) { + return fileDescriptor_2d655ab2f7683c23, []int{156} +} +func (m *RoutineCall) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *RoutineCall) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_RoutineCall.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *RoutineCall) XXX_Merge(src proto.Message) { + xxx_messageInfo_RoutineCall.Merge(m, src) +} +func (m *RoutineCall) XXX_Size() int { + return m.ProtoSize() +} +func (m *RoutineCall) XXX_DiscardUnknown() { + xxx_messageInfo_RoutineCall.DiscardUnknown(m) +} + +var xxx_messageInfo_RoutineCall proto.InternalMessageInfo + +type isRoutineCall_Implementation interface { + isRoutineCall_Implementation() + MarshalTo([]byte) (int, error) + ProtoSize() int +} + +type RoutineCall_Sql struct { + Sql *SqlRoutineImplementation `protobuf:"bytes,8,opt,name=sql,proto3,oneof" json:"sql,omitempty"` +} +type RoutineCall_Python struct { + Python *PythonRoutineImplementation `protobuf:"bytes,9,opt,name=python,proto3,oneof" json:"python,omitempty"` +} + +func (*RoutineCall_Sql) isRoutineCall_Implementation() {} +func (*RoutineCall_Python) isRoutineCall_Implementation() {} + +func (m *RoutineCall) GetImplementation() isRoutineCall_Implementation { + if m != nil { + return m.Implementation + } + return nil +} + +func (m *RoutineCall) GetContractVersion() int32 { + if m != nil { + return m.ContractVersion + } + return 0 +} + +func (m *RoutineCall) GetFunctionRef() *FunctionRef { + if m != nil { + return m.FunctionRef + } + return nil +} + +func (m *RoutineCall) GetLanguage() string { + if m != nil { + return m.Language + } + return "" +} + +func (m *RoutineCall) GetArgumentTypes() []*Type { + if m != nil { + return m.ArgumentTypes + } + return nil +} + +func (m *RoutineCall) GetReturnType() Type { + if m != nil { + return m.ReturnType + } + return Type{} +} + +func (m *RoutineCall) GetVolatility() string { + if m != nil { + return m.Volatility + } + return "" +} + +func (m *RoutineCall) GetNullPolicy() string { + if m != nil { + return m.NullPolicy + } + return "" +} + +func (m *RoutineCall) GetSql() *SqlRoutineImplementation { + if x, ok := m.GetImplementation().(*RoutineCall_Sql); ok { + return x.Sql + } + return nil +} + +func (m *RoutineCall) GetPython() *PythonRoutineImplementation { + if x, ok := m.GetImplementation().(*RoutineCall_Python); ok { + return x.Python + } + return nil +} + +// Deprecated: Do not use. +func (m *RoutineCall) GetContext() map[string]string { + if m != nil { + return m.Context + } + return nil +} + +func (m *RoutineCall) GetCallsiteId() string { + if m != nil { + return m.CallsiteId + } + return "" +} + +func (m *RoutineCall) GetMayError() bool { + if m != nil { + return m.MayError + } + return false +} + +func (m *RoutineCall) GetSecurityMode() string { + if m != nil { + return m.SecurityMode + } + return "" +} + +func (m *RoutineCall) GetLeakproof() bool { + if m != nil { + return m.Leakproof + } + return false +} + +// XXX_OneofWrappers is for the internal use of the proto package. +func (*RoutineCall) XXX_OneofWrappers() []interface{} { + return []interface{}{ + (*RoutineCall_Sql)(nil), + (*RoutineCall_Python)(nil), + } +} + +// RoutinePlanDependency is the shared cache/prepared-plan validation envelope +// for a resolved routine closure. FunctionRef is the executable identity; +// the integrity fields prove that the implementation attached to the plan is +// the same immutable revision that the resolver admitted. +type RoutinePlanDependency struct { + FunctionRef *FunctionRef `protobuf:"bytes,1,opt,name=function_ref,json=functionRef,proto3" json:"function_ref,omitempty"` + Language string `protobuf:"bytes,2,opt,name=language,proto3" json:"language,omitempty"` + ContractVersion int32 `protobuf:"varint,3,opt,name=contract_version,json=contractVersion,proto3" json:"contract_version,omitempty"` + DefinitionFingerprint string `protobuf:"bytes,4,opt,name=definition_fingerprint,json=definitionFingerprint,proto3" json:"definition_fingerprint,omitempty"` + ArtifactDigest string `protobuf:"bytes,5,opt,name=artifact_digest,json=artifactDigest,proto3" json:"artifact_digest,omitempty"` + EnvironmentDigest string `protobuf:"bytes,6,opt,name=environment_digest,json=environmentDigest,proto3" json:"environment_digest,omitempty"` + Volatility string `protobuf:"bytes,7,opt,name=volatility,proto3" json:"volatility,omitempty"` + NullPolicy string `protobuf:"bytes,8,opt,name=null_policy,json=nullPolicy,proto3" json:"null_policy,omitempty"` + MayError bool `protobuf:"varint,9,opt,name=may_error,json=mayError,proto3" json:"may_error,omitempty"` + SecurityMode string `protobuf:"bytes,10,opt,name=security_mode,json=securityMode,proto3" json:"security_mode,omitempty"` + Leakproof bool `protobuf:"varint,11,opt,name=leakproof,proto3" json:"leakproof,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *RoutinePlanDependency) Reset() { *m = RoutinePlanDependency{} } +func (m *RoutinePlanDependency) String() string { return proto.CompactTextString(m) } +func (*RoutinePlanDependency) ProtoMessage() {} +func (*RoutinePlanDependency) Descriptor() ([]byte, []int) { + return fileDescriptor_2d655ab2f7683c23, []int{157} +} +func (m *RoutinePlanDependency) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *RoutinePlanDependency) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_RoutinePlanDependency.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *RoutinePlanDependency) XXX_Merge(src proto.Message) { + xxx_messageInfo_RoutinePlanDependency.Merge(m, src) +} +func (m *RoutinePlanDependency) XXX_Size() int { + return m.ProtoSize() +} +func (m *RoutinePlanDependency) XXX_DiscardUnknown() { + xxx_messageInfo_RoutinePlanDependency.DiscardUnknown(m) +} + +var xxx_messageInfo_RoutinePlanDependency proto.InternalMessageInfo + +func (m *RoutinePlanDependency) GetFunctionRef() *FunctionRef { + if m != nil { + return m.FunctionRef + } + return nil +} + +func (m *RoutinePlanDependency) GetLanguage() string { + if m != nil { + return m.Language + } + return "" +} + +func (m *RoutinePlanDependency) GetContractVersion() int32 { + if m != nil { + return m.ContractVersion + } + return 0 +} + +func (m *RoutinePlanDependency) GetDefinitionFingerprint() string { + if m != nil { + return m.DefinitionFingerprint + } + return "" +} + +func (m *RoutinePlanDependency) GetArtifactDigest() string { + if m != nil { + return m.ArtifactDigest + } + return "" +} + +func (m *RoutinePlanDependency) GetEnvironmentDigest() string { + if m != nil { + return m.EnvironmentDigest + } + return "" +} + +func (m *RoutinePlanDependency) GetVolatility() string { + if m != nil { + return m.Volatility + } + return "" +} + +func (m *RoutinePlanDependency) GetNullPolicy() string { + if m != nil { + return m.NullPolicy + } + return "" +} + +func (m *RoutinePlanDependency) GetMayError() bool { + if m != nil { + return m.MayError + } + return false +} + +func (m *RoutinePlanDependency) GetSecurityMode() string { + if m != nil { + return m.SecurityMode + } + return "" +} + +func (m *RoutinePlanDependency) GetLeakproof() bool { + if m != nil { + return m.Leakproof + } + return false +} + func init() { proto.RegisterEnum("plan.StringLiteralForm", StringLiteralForm_name, StringLiteralForm_value) proto.RegisterEnum("plan.AggregateConfigType", AggregateConfigType_name, AggregateConfigType_value) @@ -16541,1105 +17159,1152 @@ func init() { proto.RegisterType((*ODKUForeignKeyCheck)(nil), "plan.ODKUForeignKeyCheck") proto.RegisterType((*VectorIndexScanWork)(nil), "plan.VectorIndexScanWork") proto.RegisterType((*UnresolvedIndexHint)(nil), "plan.UnresolvedIndexHint") + proto.RegisterType((*FunctionRef)(nil), "plan.FunctionRef") + proto.RegisterType((*SqlRoutineImplementation)(nil), "plan.SqlRoutineImplementation") + proto.RegisterType((*PythonRoutineImplementation)(nil), "plan.PythonRoutineImplementation") + proto.RegisterType((*RoutineCall)(nil), "plan.RoutineCall") + proto.RegisterMapType((map[string]string)(nil), "plan.RoutineCall.ContextEntry") + proto.RegisterType((*RoutinePlanDependency)(nil), "plan.RoutinePlanDependency") } func init() { proto.RegisterFile("plan.proto", fileDescriptor_2d655ab2f7683c23) } var fileDescriptor_2d655ab2f7683c23 = []byte{ - // 17475 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xcc, 0xbd, 0x5b, 0x6c, 0x23, 0xd9, - 0x92, 0x20, 0x56, 0x14, 0x29, 0x89, 0x0c, 0x52, 0x54, 0xea, 0x94, 0xaa, 0x8a, 0x55, 0x5d, 0x5d, - 0xa5, 0xce, 0xae, 0xee, 0xae, 0xae, 0xee, 0xae, 0xee, 0xae, 0xea, 0x47, 0xf5, 0x9d, 0x3b, 0x7b, - 0x2f, 0x45, 0x51, 0x25, 0x76, 0x51, 0xa4, 0x6e, 0x92, 0xaa, 0xea, 0xbe, 0xb3, 0x83, 0x44, 0x8a, - 0x99, 0x92, 0xb2, 0x95, 0xcc, 0x64, 0x67, 0x26, 0x4b, 0x52, 0x03, 0x86, 0x2f, 0xfc, 0xe1, 0xf1, - 0x18, 0xfe, 0x58, 0x63, 0xe0, 0xf5, 0x87, 0x01, 0x63, 0x76, 0x81, 0x85, 0x81, 0xb5, 0xfd, 0x61, - 0x63, 0xe1, 0xf5, 0x03, 0x6b, 0x63, 0xbd, 0x06, 0xbc, 0x80, 0x0d, 0x63, 0xe0, 0x85, 0x3d, 0xb6, - 0x17, 0xd8, 0x35, 0x66, 0xfe, 0x6d, 0x03, 0xf6, 0x8f, 0xfd, 0x63, 0x23, 0x22, 0xce, 0xc9, 0x07, - 0xc9, 0xea, 0xc7, 0x9d, 0x19, 0xc0, 0x3f, 0x12, 0x4f, 0x44, 0x9c, 0x93, 0xe7, 0x19, 0x27, 0x22, - 0x4e, 0x9c, 0x38, 0x00, 0x13, 0xcf, 0xf2, 0x1f, 0x4e, 0xc2, 0x20, 0x0e, 0x44, 0x09, 0x7f, 0xdf, - 0xfa, 0xe0, 0xc4, 0x8d, 0x4f, 0xa7, 0x47, 0x0f, 0x47, 0xc1, 0xf8, 0xc3, 0x93, 0xe0, 0x24, 0xf8, - 0x90, 0x90, 0x47, 0xd3, 0x63, 0x4a, 0x51, 0x82, 0x7e, 0x71, 0xa6, 0x5b, 0xe0, 0x05, 0xa3, 0x33, - 0xf9, 0x7b, 0x3d, 0x76, 0xc7, 0x4e, 0x14, 0x5b, 0xe3, 0x09, 0x03, 0xf4, 0xff, 0xad, 0x00, 0xa5, - 0xe1, 0xe5, 0xc4, 0x11, 0x75, 0x58, 0x72, 0xed, 0x46, 0x61, 0xab, 0x70, 0x7f, 0xd9, 0x58, 0x72, - 0x6d, 0xb1, 0x05, 0x55, 0x3f, 0x88, 0x7b, 0x53, 0xcf, 0xb3, 0x8e, 0x3c, 0xa7, 0xb1, 0xb4, 0x55, - 0xb8, 0x5f, 0x36, 0xb2, 0x20, 0xf1, 0x1a, 0x54, 0xac, 0x69, 0x1c, 0x98, 0xae, 0x3f, 0x0a, 0x1b, - 0x45, 0xc2, 0x97, 0x11, 0xd0, 0xf1, 0x47, 0xa1, 0xd8, 0x84, 0xe5, 0x73, 0xd7, 0x8e, 0x4f, 0x1b, - 0x25, 0x2a, 0x91, 0x13, 0x08, 0x8d, 0x46, 0x96, 0xe7, 0x34, 0x96, 0x19, 0x4a, 0x09, 0x84, 0xc6, - 0xf4, 0x91, 0x95, 0xad, 0xc2, 0xfd, 0x8a, 0xc1, 0x09, 0x71, 0x07, 0xc0, 0xf1, 0xa7, 0xe3, 0x97, - 0x96, 0x37, 0x75, 0xa2, 0xc6, 0x2a, 0xa1, 0x32, 0x10, 0xd1, 0x80, 0xd5, 0xd1, 0xa9, 0x15, 0x46, - 0x4e, 0xdc, 0x28, 0x6f, 0x15, 0xee, 0xaf, 0x19, 0x2a, 0x89, 0x15, 0x9b, 0x58, 0xb6, 0x19, 0x4d, - 0xac, 0x91, 0xd3, 0xa8, 0x70, 0xc5, 0x26, 0x96, 0x3d, 0xc0, 0xb4, 0xfe, 0x0b, 0xa8, 0x8c, 0xa3, - 0x93, 0x3d, 0xc7, 0xb2, 0x9d, 0x50, 0xdc, 0x80, 0xd5, 0x71, 0x74, 0x62, 0xc6, 0xd6, 0x89, 0x6c, - 0xf9, 0xca, 0x38, 0x3a, 0x19, 0x5a, 0x27, 0xe2, 0x26, 0x94, 0x09, 0x71, 0x39, 0xe1, 0xa6, 0x2f, - 0x1b, 0x48, 0x88, 0x1d, 0xa5, 0xff, 0xc9, 0x2a, 0xac, 0x76, 0xdd, 0xd8, 0x09, 0x2d, 0x4f, 0x5c, - 0x87, 0x15, 0x37, 0xf2, 0xa7, 0x9e, 0x47, 0xd9, 0xcb, 0x86, 0x4c, 0x89, 0xeb, 0xb0, 0xec, 0x3e, - 0x79, 0x69, 0x79, 0x9c, 0x77, 0xef, 0x8a, 0xc1, 0x49, 0xd1, 0x80, 0x15, 0xf7, 0xe3, 0xcf, 0x10, - 0x51, 0x94, 0x08, 0x99, 0x26, 0xcc, 0xe3, 0x47, 0x88, 0x29, 0x25, 0x18, 0x4a, 0x13, 0xe6, 0xb3, - 0x4f, 0x10, 0x83, 0x9d, 0x56, 0x24, 0x0c, 0xa5, 0xf1, 0x2b, 0x53, 0xfa, 0x0a, 0xf6, 0xdb, 0x1a, - 0x7e, 0x65, 0xaa, 0xbe, 0x32, 0xe5, 0xaf, 0xac, 0x4a, 0x84, 0x4c, 0x13, 0x86, 0xbf, 0x52, 0x4e, - 0x30, 0xc9, 0x57, 0xa6, 0xfc, 0x15, 0xec, 0xb0, 0x12, 0x61, 0xf8, 0x2b, 0x9b, 0x50, 0xb2, 0x11, - 0x0e, 0x5b, 0x85, 0xfb, 0x85, 0xbd, 0x2b, 0x06, 0xa5, 0x10, 0x1a, 0x21, 0xb4, 0x8a, 0xe3, 0x82, - 0xd0, 0x48, 0x42, 0x8f, 0x10, 0x5a, 0xc3, 0xde, 0x40, 0xe8, 0x91, 0x84, 0x1e, 0x23, 0x74, 0x6d, - 0xab, 0x70, 0x7f, 0x09, 0xa1, 0x98, 0x12, 0xb7, 0x60, 0xd5, 0xb6, 0x62, 0x07, 0x11, 0x75, 0xd9, - 0x64, 0x05, 0x40, 0x1c, 0x4e, 0x54, 0xc4, 0xad, 0xcb, 0x46, 0x2b, 0x80, 0xd0, 0xa1, 0x8a, 0x64, - 0x0a, 0xaf, 0x49, 0x7c, 0x16, 0x28, 0x3e, 0x85, 0x9a, 0xed, 0x8c, 0xdc, 0xb1, 0xe5, 0x71, 0x9b, - 0x36, 0xb6, 0x0a, 0xf7, 0xab, 0x8f, 0xd6, 0x1f, 0xd2, 0x52, 0x4a, 0x30, 0x7b, 0x57, 0x8c, 0x1c, - 0x99, 0x78, 0x02, 0x6b, 0x32, 0xfd, 0xf1, 0x23, 0xea, 0x58, 0x41, 0xf9, 0xb4, 0x5c, 0xbe, 0x8f, - 0x1f, 0x3d, 0xd9, 0xbb, 0x62, 0xe4, 0x09, 0xc5, 0x3d, 0xa8, 0x25, 0x2b, 0x0b, 0x33, 0x5e, 0x95, - 0xb5, 0xca, 0x41, 0xb1, 0x59, 0xdf, 0x44, 0x81, 0x8f, 0x04, 0x9b, 0xb2, 0xdf, 0x14, 0x40, 0x6c, - 0x01, 0xd8, 0xce, 0xb1, 0x35, 0xf5, 0x62, 0x44, 0x5f, 0x93, 0x1d, 0x98, 0x81, 0x89, 0x3b, 0x50, - 0x99, 0x4e, 0xb0, 0x95, 0xcf, 0x2d, 0xaf, 0x71, 0x5d, 0x12, 0xa4, 0x20, 0x2c, 0x1d, 0x97, 0x07, - 0x62, 0x6f, 0xc8, 0xd1, 0x55, 0x00, 0x1c, 0xde, 0x97, 0xce, 0x08, 0x51, 0x0d, 0xf9, 0x61, 0x99, - 0xc6, 0xc5, 0xe7, 0x46, 0xdb, 0xae, 0xdf, 0xb8, 0x49, 0x33, 0x98, 0x13, 0xe2, 0x36, 0x14, 0xa3, - 0x70, 0xd4, 0xb8, 0x45, 0xed, 0x07, 0x6e, 0x7f, 0xfb, 0x62, 0x12, 0x1a, 0x08, 0x16, 0x6f, 0xc2, - 0x9a, 0x1b, 0x99, 0x91, 0x13, 0xba, 0x96, 0xe7, 0x7e, 0xe7, 0xd8, 0x8d, 0xd7, 0x28, 0x6f, 0xcd, - 0x8d, 0x06, 0x09, 0x4c, 0xfc, 0x0c, 0x6a, 0x1e, 0x2f, 0x13, 0xf3, 0x38, 0x08, 0xc7, 0x8d, 0xdb, - 0x5b, 0x85, 0xfb, 0xf5, 0x47, 0x37, 0xb8, 0xac, 0x41, 0x1c, 0xba, 0xfe, 0x89, 0x5c, 0x46, 0xbb, - 0x41, 0x38, 0x36, 0xaa, 0x5e, 0x9a, 0xc0, 0x0f, 0x44, 0x44, 0x61, 0x46, 0xc1, 0x34, 0x1c, 0x39, - 0x8d, 0xd7, 0x69, 0x85, 0xd7, 0x18, 0x38, 0x20, 0x98, 0xf8, 0x05, 0xdc, 0x96, 0x83, 0x60, 0xaa, - 0x0f, 0x85, 0xce, 0xb7, 0x53, 0x37, 0x74, 0x22, 0xf3, 0xe5, 0x93, 0x47, 0x8d, 0x3b, 0x54, 0xa9, - 0x9b, 0x92, 0x46, 0x7e, 0xcb, 0x90, 0x14, 0xcf, 0x9f, 0x3c, 0xda, 0x5e, 0x85, 0x65, 0xe2, 0x25, - 0xfa, 0x6d, 0x28, 0x1f, 0x58, 0xa1, 0x35, 0x36, 0x9c, 0x63, 0xa1, 0x41, 0x71, 0x12, 0x44, 0x92, - 0x1d, 0xe0, 0x4f, 0xbd, 0x0b, 0x2b, 0xcf, 0xad, 0x10, 0x71, 0x02, 0x4a, 0xbe, 0x35, 0x76, 0x08, - 0x59, 0x31, 0xe8, 0x37, 0xb2, 0x80, 0xe8, 0x32, 0x8a, 0x9d, 0xb1, 0x64, 0x91, 0x32, 0x85, 0xf0, - 0x13, 0x2f, 0x38, 0x92, 0x4b, 0xbd, 0x6c, 0xc8, 0x94, 0xfe, 0x2f, 0x15, 0x60, 0xa5, 0x15, 0x78, - 0x58, 0xdc, 0x0d, 0x58, 0x0d, 0x1d, 0xcf, 0x4c, 0x3f, 0xb7, 0x12, 0x3a, 0xde, 0x41, 0x10, 0x21, - 0x62, 0x14, 0x30, 0x82, 0x99, 0xcf, 0xca, 0x28, 0x20, 0x84, 0xaa, 0x40, 0x31, 0x53, 0x81, 0x9b, - 0x50, 0x8e, 0x8f, 0x3c, 0x93, 0xe0, 0x25, 0x82, 0xaf, 0xc6, 0x47, 0x5e, 0x0f, 0x51, 0x37, 0x60, - 0xd5, 0x3e, 0x62, 0xcc, 0x32, 0x61, 0x56, 0xec, 0x23, 0x44, 0xe8, 0x5f, 0x40, 0xc5, 0xb0, 0xce, - 0x65, 0x35, 0xae, 0xc1, 0x0a, 0x16, 0x20, 0xb9, 0x7f, 0xc9, 0x58, 0x8e, 0x8f, 0xbc, 0x8e, 0x8d, - 0x60, 0xac, 0x84, 0x6b, 0x53, 0x1d, 0x4a, 0xc6, 0xf2, 0x28, 0xf0, 0x3a, 0xb6, 0x3e, 0x04, 0x68, - 0x05, 0x61, 0xf8, 0x5b, 0x37, 0x61, 0x13, 0x96, 0x6d, 0x67, 0x12, 0x9f, 0x32, 0x07, 0x34, 0x38, - 0xa1, 0x3f, 0x80, 0x32, 0x4e, 0xaf, 0xae, 0x1b, 0xc5, 0xe2, 0x0e, 0x94, 0x3c, 0x37, 0x8a, 0x1b, - 0x85, 0xad, 0xe2, 0xcc, 0xe4, 0x23, 0xb8, 0xbe, 0x05, 0xe5, 0x7d, 0xeb, 0xe2, 0x39, 0x8e, 0x1c, - 0x96, 0x46, 0x43, 0x28, 0x87, 0x44, 0x8e, 0x67, 0x0d, 0x60, 0x68, 0x85, 0x27, 0x4e, 0x4c, 0x0c, - 0xfb, 0xff, 0x2c, 0x40, 0x75, 0x30, 0x3d, 0xfa, 0x76, 0xea, 0x84, 0x97, 0x58, 0xe7, 0xfb, 0x50, - 0x8c, 0x2f, 0x27, 0x94, 0xa3, 0xfe, 0xe8, 0xba, 0x9c, 0x8f, 0x29, 0xfe, 0x21, 0x66, 0x32, 0x90, - 0x04, 0x1b, 0xe1, 0x07, 0xb6, 0xa3, 0xfa, 0x60, 0xd9, 0x58, 0xc1, 0x64, 0xc7, 0xc6, 0xcd, 0x32, - 0x98, 0xc8, 0x51, 0x58, 0x0a, 0x26, 0x62, 0x0b, 0x96, 0x47, 0xa7, 0xae, 0x67, 0xd3, 0x00, 0xe4, - 0xeb, 0xcc, 0x08, 0x1c, 0xa5, 0x30, 0x38, 0x37, 0x23, 0xf7, 0x3b, 0xb5, 0xf9, 0xad, 0x86, 0xc1, - 0xf9, 0xc0, 0xfd, 0xce, 0xd1, 0x87, 0x72, 0x07, 0x06, 0x58, 0x19, 0xb4, 0x9a, 0xdd, 0xa6, 0xa1, - 0x5d, 0xc1, 0xdf, 0xed, 0xaf, 0x3a, 0x83, 0xe1, 0x40, 0x2b, 0x88, 0x3a, 0x40, 0xaf, 0x3f, 0x34, - 0x65, 0x7a, 0x49, 0xac, 0xc0, 0x52, 0xa7, 0xa7, 0x15, 0x91, 0x06, 0xe1, 0x9d, 0x9e, 0x56, 0x12, - 0xab, 0x50, 0x6c, 0xf6, 0xbe, 0xd6, 0x96, 0xe9, 0x47, 0xb7, 0xab, 0xad, 0xe8, 0xff, 0xcf, 0x12, - 0x54, 0xfa, 0x47, 0xdf, 0x38, 0xa3, 0x18, 0xdb, 0x8c, 0xb3, 0xd4, 0x09, 0x5f, 0x3a, 0x21, 0x35, - 0xbb, 0x68, 0xc8, 0x14, 0x36, 0xc4, 0x3e, 0xa2, 0xc6, 0x15, 0x8d, 0x25, 0xfb, 0x88, 0xe8, 0x46, - 0xa7, 0xce, 0xd8, 0xa2, 0xc6, 0x21, 0x1d, 0xa5, 0x70, 0x55, 0x04, 0x47, 0xdf, 0x50, 0xf3, 0x8a, - 0x06, 0xfe, 0x14, 0x77, 0xa1, 0xca, 0x65, 0x64, 0xe7, 0x17, 0x30, 0x68, 0x76, 0xf2, 0xad, 0x64, - 0x27, 0x1f, 0xe5, 0xa4, 0x52, 0x19, 0x29, 0x77, 0x76, 0x06, 0xf5, 0xe4, 0x8c, 0x0e, 0x8e, 0xbe, - 0x61, 0x6c, 0x99, 0x67, 0x74, 0x70, 0xf4, 0x0d, 0xa1, 0xde, 0x83, 0x8d, 0x68, 0x7a, 0x14, 0x8d, - 0x42, 0x77, 0x12, 0xbb, 0x81, 0xcf, 0x34, 0x15, 0xa2, 0xd1, 0xb2, 0x08, 0x22, 0xbe, 0x0f, 0xe5, - 0xc9, 0xf4, 0xc8, 0x74, 0xfd, 0xe3, 0x80, 0x76, 0xaf, 0xea, 0xa3, 0x35, 0x1e, 0x98, 0x83, 0xe9, - 0x51, 0xc7, 0x3f, 0x0e, 0x8c, 0xd5, 0x09, 0xff, 0x10, 0x3a, 0xac, 0xf9, 0x41, 0x6c, 0xa2, 0xa0, - 0x64, 0x8e, 0x9d, 0xd8, 0xa2, 0x6d, 0x8d, 0xc5, 0x9d, 0x6e, 0x30, 0x3a, 0xdb, 0x77, 0x62, 0x4b, - 0x3c, 0x80, 0x72, 0xe4, 0x5b, 0x93, 0xe8, 0x34, 0x88, 0x69, 0x7f, 0xab, 0x3e, 0xaa, 0xcb, 0xb9, - 0x23, 0xa1, 0x46, 0x82, 0xd7, 0xdf, 0x86, 0x55, 0xf9, 0x0d, 0x14, 0x46, 0x62, 0xc7, 0xb7, 0xfc, - 0xd8, 0x4c, 0xc4, 0xab, 0x32, 0x03, 0x3a, 0xb6, 0xfe, 0xf7, 0x0a, 0xa0, 0x0d, 0x32, 0xd5, 0xa6, - 0x0f, 0x2d, 0xe2, 0x32, 0xaf, 0x03, 0x58, 0xa3, 0x51, 0x30, 0xe5, 0x62, 0x78, 0x32, 0x56, 0x24, - 0xa4, 0x63, 0x67, 0xfb, 0xba, 0x98, 0xeb, 0xeb, 0x37, 0xa0, 0xa6, 0xf2, 0x65, 0x18, 0x44, 0x55, - 0xc2, 0x54, 0x6f, 0x47, 0xd3, 0x1c, 0x97, 0x58, 0x8d, 0xa6, 0x9c, 0xfb, 0x3a, 0xac, 0x90, 0x2c, - 0x16, 0xa9, 0x11, 0xe4, 0x94, 0xfe, 0xa7, 0x05, 0x58, 0xeb, 0xf8, 0xb6, 0x73, 0x31, 0x18, 0x59, - 0xbe, 0xea, 0x40, 0x37, 0x32, 0x5d, 0x84, 0x99, 0xd1, 0xc8, 0xf2, 0xa5, 0x3c, 0x54, 0x75, 0xa3, - 0x84, 0x0e, 0xdb, 0xc0, 0x04, 0xf4, 0xa9, 0x25, 0x2a, 0xb1, 0x42, 0x10, 0xfa, 0xd8, 0xdb, 0xb0, - 0x7e, 0xe4, 0x78, 0x81, 0x7f, 0x62, 0xc6, 0x81, 0xc9, 0xf2, 0x20, 0xb7, 0x65, 0x8d, 0xc1, 0xc3, - 0x60, 0x48, 0x72, 0xe1, 0x26, 0x2c, 0x4f, 0xac, 0x30, 0x8e, 0x1a, 0xa5, 0xad, 0x22, 0x2e, 0x79, - 0x4a, 0x60, 0x37, 0xbb, 0x91, 0x39, 0xf5, 0xdd, 0x6f, 0xa7, 0xdc, 0x8c, 0xb2, 0x51, 0x76, 0xa3, - 0x43, 0x4a, 0x8b, 0xfb, 0xa0, 0xf1, 0x97, 0xa9, 0xd8, 0xec, 0x9c, 0xac, 0x13, 0x9c, 0x0a, 0x26, - 0xc6, 0xf8, 0x6f, 0x17, 0xa1, 0xbc, 0x3b, 0xf5, 0x47, 0x38, 0x18, 0xe2, 0x4d, 0x28, 0x1d, 0x4f, - 0xfd, 0x11, 0xb5, 0x25, 0x91, 0x1e, 0x92, 0x35, 0x65, 0x10, 0x12, 0xb9, 0x95, 0x15, 0x9e, 0x20, - 0x97, 0x9b, 0xe3, 0x56, 0x08, 0xa7, 0x91, 0x3b, 0x39, 0x31, 0x47, 0x81, 0x7f, 0xec, 0x9e, 0x50, - 0x8b, 0x6a, 0x46, 0xc5, 0x3a, 0x39, 0x69, 0x11, 0x40, 0x34, 0x61, 0x3d, 0x45, 0xb3, 0xbc, 0x59, - 0x22, 0xc6, 0x74, 0x93, 0x4b, 0x6a, 0x9e, 0x9c, 0x84, 0xce, 0x89, 0x15, 0x3b, 0x4c, 0x4f, 0xbc, - 0x69, 0x2d, 0xc9, 0x4e, 0x7c, 0xe3, 0x23, 0xd8, 0x8c, 0x2e, 0xfd, 0xd8, 0xba, 0x30, 0x9d, 0x8b, - 0x89, 0xe7, 0x8e, 0xdc, 0xd8, 0x1c, 0x59, 0x51, 0x2c, 0x7b, 0x41, 0x30, 0xae, 0x2d, 0x51, 0x2d, - 0x2b, 0x8a, 0xf5, 0xff, 0xb8, 0xc0, 0xad, 0xdc, 0xf5, 0xac, 0x13, 0x51, 0x86, 0x52, 0xaf, 0xdf, - 0x6b, 0x6b, 0x57, 0x44, 0x0d, 0xca, 0x9d, 0xde, 0xb0, 0x6d, 0xf4, 0x9a, 0x5d, 0xad, 0x40, 0xec, - 0x68, 0xd8, 0xdc, 0xee, 0xb6, 0xb5, 0x25, 0xc4, 0x3c, 0xef, 0x77, 0x9b, 0xc3, 0x4e, 0xb7, 0xad, - 0x95, 0x18, 0x63, 0x74, 0x5a, 0x43, 0xad, 0x2c, 0x34, 0xa8, 0x1d, 0x18, 0xfd, 0x9d, 0xc3, 0x56, - 0xdb, 0xec, 0x1d, 0x76, 0xbb, 0x9a, 0x26, 0xae, 0xc2, 0x7a, 0x02, 0xe9, 0x33, 0x70, 0x0b, 0xb3, - 0x3c, 0x6f, 0x1a, 0x4d, 0xe3, 0xa9, 0xf6, 0x4b, 0x51, 0x86, 0x62, 0xf3, 0xe9, 0x53, 0xed, 0x37, - 0xc8, 0xd9, 0x2a, 0x2f, 0x3a, 0x3d, 0xf3, 0x79, 0xb3, 0x7b, 0xd8, 0xd6, 0x7e, 0xb3, 0xa4, 0xd2, - 0x7d, 0x63, 0xa7, 0x6d, 0x68, 0xbf, 0x29, 0x89, 0x0d, 0xa8, 0xfd, 0xba, 0xdf, 0x6b, 0xef, 0x37, - 0x0f, 0x0e, 0xa8, 0x22, 0xbf, 0x29, 0xeb, 0x7f, 0x5e, 0x82, 0x1b, 0x07, 0xa1, 0x33, 0xb1, 0x42, - 0xc7, 0xee, 0x4d, 0xc7, 0x4e, 0xe8, 0x8e, 0x70, 0xd1, 0xd8, 0x56, 0x6c, 0x89, 0x5b, 0x50, 0x3e, - 0xb6, 0x3c, 0xef, 0xc8, 0x1a, 0x9d, 0xc9, 0xf9, 0x97, 0xa4, 0x59, 0x27, 0x08, 0xad, 0x71, 0x66, - 0x47, 0x2a, 0x13, 0x00, 0xf7, 0xa4, 0x77, 0x60, 0x5d, 0x11, 0x2a, 0x81, 0x83, 0x37, 0xed, 0xba, - 0x02, 0x4b, 0x91, 0xe3, 0x53, 0xb8, 0x31, 0x43, 0x68, 0xaa, 0x0d, 0x82, 0xf5, 0x9c, 0xcd, 0x7c, - 0x86, 0x1e, 0x6f, 0x17, 0x0b, 0xb2, 0xa9, 0xcd, 0x71, 0x79, 0x51, 0xb6, 0x16, 0x6f, 0x95, 0x9f, - 0xc1, 0x8d, 0x49, 0x18, 0xbc, 0x74, 0x23, 0x37, 0xf0, 0x49, 0xb8, 0x89, 0xa6, 0x9e, 0x1c, 0xdb, - 0x15, 0xaa, 0xde, 0xb5, 0x0c, 0xda, 0x20, 0x2c, 0x0e, 0xef, 0x2b, 0xf2, 0x4d, 0x1c, 0x27, 0x24, - 0x66, 0xbb, 0x28, 0xdf, 0x81, 0xe3, 0x84, 0x62, 0x07, 0xee, 0xbe, 0x22, 0x1f, 0x4d, 0x4c, 0x6c, - 0x65, 0x99, 0xaa, 0xfb, 0xda, 0xc2, 0xfc, 0x38, 0x19, 0x3b, 0xb6, 0x68, 0xc2, 0xeb, 0xaf, 0x2a, - 0x85, 0x35, 0xc2, 0x0a, 0x95, 0x71, 0x6b, 0x61, 0x19, 0x2f, 0x48, 0x4d, 0xfc, 0x9e, 0x22, 0x58, - 0x7d, 0x84, 0xef, 0x29, 0x62, 0x40, 0x3a, 0xe5, 0xcf, 0x61, 0x53, 0x4a, 0x90, 0x76, 0x30, 0xb6, - 0x5c, 0x5f, 0x8d, 0x6b, 0x75, 0x6e, 0x83, 0x16, 0x4c, 0xb7, 0x43, 0x64, 0xdc, 0xff, 0xfa, 0x3f, - 0x58, 0x86, 0x12, 0x22, 0x85, 0x9e, 0xca, 0x0a, 0x49, 0x2e, 0x6c, 0xe7, 0x76, 0xe9, 0x1f, 0xff, - 0xb3, 0xbb, 0x57, 0x58, 0x4a, 0x78, 0x03, 0x8a, 0x9e, 0x1b, 0xd3, 0xa4, 0x4a, 0x76, 0x18, 0x29, - 0x6d, 0xee, 0x5d, 0x31, 0x10, 0x27, 0xee, 0x40, 0x81, 0xc5, 0x85, 0x64, 0xd3, 0x50, 0xf2, 0xe6, - 0xde, 0x15, 0xa3, 0x30, 0x11, 0xb7, 0xa1, 0xf0, 0x52, 0xca, 0x0e, 0x35, 0xc6, 0xb3, 0xc4, 0x89, - 0xd8, 0x97, 0x62, 0x0b, 0x8a, 0xa3, 0x80, 0xd5, 0xbf, 0x04, 0xcf, 0xf2, 0x17, 0x96, 0x3f, 0x0a, - 0x3c, 0xf1, 0x26, 0x14, 0x43, 0xeb, 0x9c, 0x66, 0x45, 0xc2, 0xa8, 0x12, 0x01, 0x0f, 0x89, 0x42, - 0xeb, 0x1c, 0x2b, 0x71, 0x4c, 0x13, 0x20, 0xa9, 0x84, 0xe2, 0x74, 0xf8, 0x99, 0x63, 0xb1, 0x05, - 0x85, 0x73, 0x1a, 0xe0, 0x44, 0xe3, 0x79, 0xe1, 0xfa, 0x76, 0x70, 0x3e, 0x98, 0x38, 0x23, 0xa4, - 0x38, 0x17, 0x6f, 0x41, 0x31, 0x9a, 0x1e, 0xd1, 0x00, 0x56, 0x1f, 0x6d, 0xcc, 0x49, 0x4e, 0xf8, - 0xa1, 0x68, 0x7a, 0x24, 0xde, 0x86, 0xd2, 0x28, 0x08, 0x43, 0xb9, 0xe7, 0x6a, 0xaa, 0xc2, 0x4a, - 0x68, 0x44, 0x0d, 0x10, 0xf1, 0xf8, 0xc1, 0x58, 0x0e, 0x88, 0x24, 0x4a, 0xa5, 0x36, 0xfc, 0x60, - 0x2c, 0xee, 0x49, 0x51, 0x30, 0xb7, 0xdf, 0x2a, 0x41, 0x11, 0xcb, 0x41, 0x2c, 0x0e, 0xd2, 0xd8, - 0xba, 0x20, 0xf5, 0x32, 0x21, 0x52, 0x12, 0x22, 0xd6, 0x69, 0x6c, 0x5d, 0x88, 0x7b, 0x50, 0x7c, - 0xe9, 0x8c, 0x48, 0xd3, 0x4c, 0xbe, 0x26, 0x07, 0xe9, 0x39, 0x35, 0x0f, 0xd1, 0xc4, 0xf1, 0x03, - 0xcf, 0x26, 0xa5, 0x33, 0x19, 0xcb, 0xdd, 0xc0, 0xb3, 0x9f, 0xd3, 0x58, 0x12, 0x12, 0x05, 0x63, - 0x6b, 0x7a, 0x81, 0xab, 0x41, 0x63, 0x11, 0xd6, 0x9a, 0x5e, 0x74, 0x6c, 0x14, 0x91, 0x7c, 0xfb, - 0x25, 0xa9, 0x9a, 0x05, 0x03, 0x7f, 0x8a, 0x2d, 0x14, 0x91, 0x3c, 0x67, 0x14, 0xbb, 0x2f, 0xdd, - 0xf8, 0x92, 0x94, 0xc9, 0x82, 0x91, 0x05, 0x89, 0x3d, 0xd0, 0x26, 0x92, 0x99, 0x99, 0x3e, 0x73, - 0x33, 0x52, 0x1d, 0xab, 0x8f, 0x5e, 0x97, 0xd3, 0x64, 0x31, 0xab, 0x33, 0xd6, 0x27, 0x79, 0xc4, - 0xf6, 0x0a, 0x94, 0x9c, 0x8b, 0x49, 0xf8, 0x65, 0xa9, 0xbc, 0xa9, 0x35, 0xf4, 0x3d, 0x58, 0x95, - 0xb5, 0x9e, 0xb3, 0xeb, 0xdc, 0x84, 0xb2, 0x1b, 0xe1, 0x7e, 0x13, 0xc5, 0x52, 0x63, 0x59, 0x75, - 0xa3, 0x16, 0x26, 0x51, 0xf0, 0xc0, 0xc2, 0xe5, 0x26, 0x45, 0xbf, 0xf5, 0x7f, 0x50, 0x00, 0x48, - 0xfb, 0x09, 0x1b, 0xe9, 0x39, 0xbe, 0xd2, 0x8e, 0x3c, 0xc7, 0x4f, 0x32, 0x2d, 0xa5, 0x99, 0xe6, - 0xf5, 0xc3, 0xe2, 0x02, 0xfd, 0x70, 0x4e, 0xc7, 0x2b, 0xfd, 0x16, 0x3a, 0xde, 0xf2, 0x0f, 0xe8, - 0x78, 0xfa, 0x4d, 0xa8, 0x24, 0x2a, 0xbe, 0xa8, 0x41, 0xc1, 0x92, 0x02, 0x70, 0xc1, 0xd2, 0xef, - 0xa3, 0xc6, 0xad, 0x94, 0xf8, 0x3c, 0x0e, 0x53, 0x4a, 0x2c, 0x2e, 0x1c, 0xe9, 0x3f, 0x87, 0x9a, - 0x64, 0xaf, 0x81, 0xb7, 0xe3, 0x1c, 0x8b, 0xf7, 0x01, 0x92, 0x74, 0x24, 0xf5, 0x94, 0x74, 0x5d, - 0xee, 0x38, 0xc7, 0x46, 0x06, 0xaf, 0xff, 0xad, 0x65, 0xd2, 0xf8, 0x76, 0x58, 0xd5, 0x92, 0x3a, - 0x55, 0x21, 0xa3, 0x53, 0x25, 0x12, 0xdf, 0x52, 0x5e, 0xaf, 0x3c, 0x75, 0x6d, 0xdb, 0xf1, 0x95, - 0xfe, 0xc8, 0x29, 0x9c, 0xc8, 0x96, 0x77, 0x22, 0x85, 0x04, 0xa1, 0x3e, 0x3a, 0x9e, 0x84, 0x4e, - 0x14, 0xb1, 0xe6, 0x62, 0x79, 0x27, 0x8a, 0x6f, 0x2d, 0x7f, 0x1f, 0xdf, 0xba, 0x09, 0x65, 0x14, - 0x7a, 0xc9, 0x7c, 0xc5, 0xfb, 0xc9, 0xaa, 0x34, 0xef, 0x89, 0x77, 0x60, 0x55, 0x1a, 0x1e, 0x24, - 0xc3, 0x90, 0x4b, 0x61, 0x87, 0x81, 0x86, 0xc2, 0x92, 0x11, 0x2e, 0x18, 0x8f, 0x1d, 0x3f, 0x56, - 0x92, 0xba, 0x4c, 0x8a, 0xf7, 0xa0, 0x12, 0xf8, 0x26, 0x5b, 0x27, 0x24, 0xc7, 0x90, 0x4b, 0xb3, - 0xef, 0x1f, 0x12, 0xd4, 0x28, 0x07, 0xf2, 0x17, 0x56, 0xc5, 0x0b, 0xce, 0xcd, 0x91, 0x15, 0xda, - 0xc4, 0x35, 0xca, 0xc6, 0xaa, 0x17, 0x9c, 0xb7, 0xac, 0xd0, 0x66, 0xcd, 0xe5, 0x5b, 0x7f, 0x3a, - 0x26, 0x4e, 0xb1, 0x66, 0xc8, 0x94, 0xb8, 0x0d, 0x95, 0x91, 0x37, 0x8d, 0x62, 0x27, 0xdc, 0xbe, - 0x64, 0x7b, 0x93, 0x91, 0x02, 0xb0, 0x5e, 0x93, 0xd0, 0x1d, 0x5b, 0xe1, 0x25, 0xb1, 0x85, 0xb2, - 0xa1, 0x92, 0x24, 0x3e, 0x9e, 0xb9, 0xf6, 0x05, 0x1b, 0x9d, 0x0c, 0x4e, 0x20, 0xfd, 0x29, 0x99, - 0x04, 0x23, 0x5a, 0xfb, 0x65, 0x43, 0x25, 0x69, 0x1c, 0xe8, 0x27, 0xad, 0xf6, 0x8a, 0x21, 0x53, - 0x39, 0xb5, 0x7b, 0xe3, 0x95, 0x6a, 0xb7, 0x98, 0xd5, 0x7c, 0x82, 0xd0, 0x3d, 0x71, 0xa5, 0xde, - 0x72, 0x95, 0x35, 0x1f, 0x06, 0x11, 0xc1, 0xe7, 0xb0, 0x76, 0xe2, 0xf8, 0x4e, 0x68, 0xc5, 0x8e, - 0x8d, 0x22, 0x02, 0x99, 0x89, 0xaa, 0x6a, 0x98, 0x9f, 0x2a, 0x14, 0xf2, 0xd1, 0xda, 0x49, 0x26, - 0x85, 0xb5, 0x94, 0xb2, 0xef, 0x35, 0x9e, 0x2d, 0x9c, 0x42, 0xa1, 0x5a, 0x7e, 0x31, 0xa9, 0xec, - 0x75, 0x16, 0xaa, 0x19, 0x3c, 0xe4, 0x2a, 0xeb, 0xdf, 0xc2, 0xaa, 0x1c, 0x5b, 0x14, 0x68, 0x91, - 0x93, 0xe4, 0xf7, 0x3c, 0x16, 0x68, 0x11, 0x8e, 0xeb, 0x56, 0x16, 0xc9, 0x2b, 0x55, 0xce, 0xda, - 0x1a, 0x03, 0xd9, 0xb0, 0x83, 0x7a, 0x07, 0xce, 0x2b, 0xd3, 0x3a, 0x72, 0x3d, 0xe4, 0x7d, 0x45, - 0xa9, 0x4f, 0x4d, 0x3d, 0xaf, 0xc9, 0x20, 0xbd, 0x0f, 0x65, 0x35, 0x13, 0xfe, 0x52, 0xbe, 0xa9, - 0x4f, 0xa0, 0x96, 0xed, 0xa1, 0xbf, 0x9c, 0x86, 0xb0, 0x5e, 0x11, 0xc5, 0x41, 0x98, 0xb0, 0xb1, - 0xb2, 0x1b, 0x0d, 0x28, 0xad, 0xff, 0xf1, 0x12, 0x54, 0x49, 0xbf, 0xe9, 0x93, 0xf6, 0x26, 0xde, - 0x07, 0x31, 0x0a, 0x1d, 0x2b, 0x76, 0x4c, 0xe7, 0x22, 0x0e, 0x2d, 0xa9, 0xc5, 0xb0, 0x28, 0xaa, - 0x31, 0xa6, 0x8d, 0x08, 0x56, 0x64, 0xee, 0x42, 0x75, 0x62, 0x85, 0x91, 0xd2, 0xa0, 0xf9, 0xeb, - 0xc0, 0x20, 0xa9, 0xbf, 0x6a, 0xfe, 0x09, 0xca, 0xac, 0x71, 0x70, 0xe6, 0xf8, 0x6c, 0x3b, 0x60, - 0xab, 0x49, 0x9d, 0xe0, 0x43, 0x04, 0x0f, 0xdc, 0xef, 0x50, 0xda, 0x01, 0x14, 0x84, 0x64, 0x67, - 0x33, 0x6f, 0xb8, 0xcd, 0x0d, 0xce, 0xd4, 0xef, 0xe1, 0xf3, 0x84, 0xc6, 0xc8, 0xd0, 0xeb, 0xbf, - 0x06, 0x48, 0x31, 0xe2, 0x16, 0x5c, 0x7f, 0xde, 0x19, 0x74, 0xb6, 0x3b, 0xdd, 0xce, 0xf0, 0x6b, - 0xf3, 0xb0, 0x37, 0x38, 0x68, 0xb7, 0x3a, 0xbb, 0x9d, 0xf6, 0x8e, 0x76, 0x45, 0x5c, 0x07, 0x91, - 0xc1, 0xd1, 0xcf, 0x6e, 0x5b, 0x2b, 0x88, 0x06, 0x6c, 0x66, 0xe0, 0x9d, 0x9e, 0xc2, 0x2c, 0xe9, - 0xff, 0x4b, 0x01, 0xd6, 0x0e, 0x78, 0xe9, 0x3d, 0x73, 0x2e, 0x77, 0xd8, 0x88, 0x36, 0x52, 0x6c, - 0xb3, 0x64, 0xd0, 0x6f, 0x71, 0x07, 0xaa, 0x93, 0x33, 0xe7, 0xd2, 0xcc, 0x19, 0x9c, 0x2a, 0x08, - 0x6a, 0x11, 0x83, 0x7c, 0x17, 0x56, 0x02, 0x6a, 0x82, 0x14, 0xa2, 0x36, 0xe6, 0xda, 0x66, 0x48, - 0x02, 0xd4, 0x44, 0x93, 0xa2, 0xb2, 0x2a, 0xaf, 0x2c, 0x8c, 0x3a, 0x76, 0x13, 0x96, 0x11, 0x85, - 0xd2, 0x37, 0xa9, 0x90, 0x94, 0x10, 0x1f, 0xc1, 0xda, 0x28, 0x18, 0x4f, 0x4c, 0x95, 0x5d, 0x8a, - 0x53, 0x79, 0xc6, 0x5e, 0x45, 0x92, 0x03, 0x2e, 0x4b, 0xff, 0xd3, 0x22, 0x94, 0xa9, 0x0e, 0x92, - 0xb7, 0xbb, 0xf6, 0x85, 0xe2, 0xed, 0x15, 0x63, 0xd9, 0xb5, 0x51, 0x2c, 0xf8, 0x01, 0xad, 0x37, - 0xd1, 0x66, 0x8b, 0x59, 0x6d, 0x36, 0x5d, 0xce, 0xa5, 0xdc, 0x72, 0x5e, 0xa4, 0xc8, 0x2e, 0x2f, - 0x52, 0x64, 0x71, 0x72, 0x31, 0x8d, 0x73, 0xe1, 0x26, 0xfa, 0x02, 0x10, 0xa8, 0x8d, 0x90, 0x2c, - 0xe7, 0x5e, 0xcd, 0x73, 0xee, 0x06, 0xac, 0xd2, 0xe4, 0xf0, 0xd8, 0xfa, 0x52, 0x36, 0x54, 0x32, - 0x33, 0x0c, 0x95, 0x1f, 0x1a, 0x86, 0xa4, 0xd9, 0x96, 0x77, 0xc2, 0xd6, 0x17, 0xd5, 0xec, 0xa6, - 0x77, 0x12, 0x88, 0x8f, 0xe1, 0x5a, 0x8a, 0x96, 0xad, 0x21, 0xe5, 0x97, 0xce, 0x13, 0x0c, 0x91, - 0x50, 0x52, 0x8b, 0x48, 0xcd, 0x7d, 0x00, 0x1b, 0x99, 0x2c, 0xa4, 0xbb, 0x45, 0xc4, 0xf8, 0x2b, - 0xc6, 0x7a, 0x42, 0x4e, 0x62, 0x75, 0x24, 0xde, 0xc5, 0x7e, 0x1a, 0x79, 0x53, 0x9b, 0xd9, 0xe8, - 0x74, 0xec, 0x47, 0x8d, 0x35, 0xea, 0xe0, 0x75, 0x05, 0x6f, 0x31, 0xf8, 0xcb, 0x52, 0xb9, 0xae, - 0xad, 0x1b, 0xf5, 0x74, 0x39, 0x98, 0x91, 0x13, 0xeb, 0x7f, 0xb8, 0x0c, 0x6b, 0xbb, 0x41, 0xe8, - 0xb8, 0x27, 0x7e, 0x3a, 0x6d, 0xe7, 0xac, 0x32, 0x6a, 0x2a, 0x2f, 0x65, 0xa6, 0xf2, 0x5d, 0xa8, - 0x1e, 0x73, 0x46, 0x64, 0xb9, 0x34, 0x5f, 0x4b, 0x06, 0x48, 0xd0, 0xf0, 0xc8, 0x43, 0xd6, 0xa8, - 0x08, 0x28, 0x73, 0x89, 0x32, 0xab, 0x4c, 0x28, 0x31, 0x88, 0x9f, 0xd1, 0xde, 0x69, 0x3b, 0x9e, - 0x13, 0xf3, 0xf8, 0xd6, 0x95, 0x3c, 0x98, 0xab, 0xd3, 0x43, 0xc3, 0x39, 0x6e, 0x92, 0x00, 0x8f, - 0x5b, 0xe9, 0x0e, 0x91, 0xcb, 0xbc, 0x72, 0xdf, 0x5d, 0xf9, 0x91, 0x79, 0x25, 0x1b, 0x7e, 0x04, - 0xd7, 0x42, 0xe7, 0xd8, 0x09, 0x1d, 0x7f, 0xe4, 0xd8, 0x66, 0x66, 0xda, 0xf2, 0x0c, 0xb9, 0x9a, - 0x22, 0x3b, 0xc9, 0x04, 0xde, 0x07, 0x2d, 0xa9, 0xab, 0xc9, 0xac, 0x93, 0xa6, 0x4d, 0xfd, 0xd1, - 0x9b, 0xdf, 0xfb, 0xd9, 0x3e, 0x91, 0x1a, 0x75, 0x55, 0x71, 0x4e, 0xcb, 0xe2, 0xb8, 0xfa, 0xaa, - 0xb8, 0xca, 0x4f, 0x2a, 0x8e, 0xdb, 0xc2, 0x69, 0x7d, 0x08, 0x95, 0x84, 0x44, 0xd4, 0xa0, 0x6c, - 0xb4, 0xa5, 0xe5, 0xe2, 0x8a, 0xa8, 0xc2, 0x6a, 0xab, 0x39, 0x68, 0x35, 0x77, 0x90, 0x81, 0xd5, - 0xa0, 0x3c, 0x68, 0x0f, 0xd9, 0x5a, 0xb1, 0x24, 0xd6, 0xa1, 0x8a, 0xa9, 0x9d, 0xf6, 0x6e, 0xf3, - 0xb0, 0x3b, 0xd4, 0x8a, 0x62, 0x0d, 0x2a, 0xbd, 0xbe, 0xd9, 0x6c, 0x0d, 0x3b, 0xfd, 0x9e, 0x56, - 0xd2, 0x3d, 0x58, 0x9f, 0xf9, 0x30, 0x72, 0x4d, 0x46, 0x9b, 0x7d, 0xa3, 0xf3, 0xb4, 0xd3, 0x33, - 0xdb, 0x5f, 0x1d, 0x74, 0x3b, 0xad, 0x0e, 0x7e, 0xe9, 0x26, 0x5c, 0xcb, 0xe3, 0x54, 0xc1, 0x05, - 0xa1, 0xc3, 0x9d, 0x3c, 0xaa, 0xdb, 0x7e, 0xda, 0x6c, 0x7d, 0x6d, 0x36, 0xf7, 0xb7, 0x3b, 0x4f, - 0x0f, 0xfb, 0x87, 0x03, 0x6d, 0x49, 0x37, 0xa1, 0xdc, 0x3a, 0x75, 0x46, 0x67, 0xaf, 0x9a, 0x85, - 0x64, 0x7c, 0x76, 0x46, 0x67, 0x52, 0x03, 0x9d, 0x31, 0x3e, 0x3b, 0xa3, 0x33, 0x5c, 0x8c, 0x6a, - 0xa7, 0xfb, 0xd6, 0x93, 0x56, 0xb5, 0x8a, 0xdc, 0xe6, 0xbe, 0xf5, 0xf4, 0x36, 0x54, 0x0e, 0xac, - 0x30, 0x76, 0xa9, 0x93, 0x9e, 0xc0, 0x5a, 0x92, 0xd8, 0x71, 0x8e, 0x95, 0x78, 0x2b, 0x12, 0xb5, - 0x35, 0x41, 0x19, 0x79, 0x42, 0xfd, 0x7d, 0xa8, 0x65, 0x01, 0xe2, 0x36, 0x14, 0x6d, 0xe7, 0x78, - 0xc1, 0xf6, 0x8b, 0x60, 0xfd, 0x39, 0xd4, 0x5a, 0x4a, 0x5c, 0x7b, 0x55, 0xcb, 0x1e, 0x41, 0x9d, - 0x38, 0xf2, 0xe8, 0x48, 0xb1, 0xe4, 0xa5, 0x05, 0x2c, 0xb9, 0x86, 0x34, 0xad, 0x23, 0xc9, 0x93, - 0x3f, 0x85, 0xea, 0x41, 0x18, 0x4c, 0x9c, 0x30, 0xa6, 0x62, 0x35, 0x28, 0x9e, 0x39, 0x97, 0xb2, - 0x54, 0xfc, 0x99, 0x1e, 0x19, 0x2c, 0x65, 0x8f, 0x0c, 0x1e, 0x41, 0x59, 0x65, 0xfb, 0xd1, 0x79, - 0x7e, 0x81, 0x5b, 0x1b, 0xe5, 0x71, 0x9d, 0x08, 0x3f, 0xf6, 0x10, 0x60, 0x92, 0x00, 0x64, 0xc7, - 0x29, 0x7d, 0x5f, 0x16, 0x6e, 0x64, 0x28, 0xf4, 0xd7, 0x61, 0xf5, 0xb9, 0xeb, 0x9c, 0xcb, 0xe6, - 0xbf, 0x74, 0x9d, 0x73, 0xd5, 0x7c, 0xfc, 0xad, 0xff, 0x57, 0x00, 0x65, 0xe2, 0x7f, 0x3b, 0xaf, - 0x3e, 0xa5, 0xf9, 0x29, 0xaa, 0xc3, 0x96, 0x64, 0x57, 0xa5, 0x05, 0x0a, 0x0b, 0x33, 0xaf, 0xd7, - 0x01, 0x32, 0xbc, 0x98, 0x77, 0x96, 0x4a, 0x9c, 0xb0, 0x60, 0x94, 0xb9, 0x49, 0x8a, 0xc1, 0x69, - 0xc4, 0x06, 0xd4, 0x14, 0x20, 0x1e, 0xb2, 0x44, 0x4c, 0x26, 0x53, 0xd6, 0x1a, 0xae, 0x2a, 0xad, - 0xfe, 0xc8, 0x73, 0x94, 0xad, 0x81, 0xc4, 0x64, 0x4c, 0xd0, 0x3e, 0xe3, 0x84, 0x11, 0x6e, 0x27, - 0xf2, 0x00, 0x5f, 0x26, 0xc5, 0x3b, 0x50, 0xc2, 0x4d, 0x58, 0xda, 0x06, 0xae, 0xaa, 0x1e, 0xcc, - 0x48, 0x11, 0x06, 0x11, 0x88, 0xfb, 0xb0, 0x4a, 0x5c, 0xca, 0xc1, 0x9d, 0x20, 0xd3, 0xdb, 0x6a, - 0x53, 0x36, 0x14, 0x5a, 0xbc, 0x0b, 0xcb, 0xc7, 0x67, 0xce, 0x25, 0x6f, 0x03, 0x49, 0x99, 0x39, - 0x66, 0x62, 0x30, 0x85, 0xb8, 0x07, 0xf5, 0xd0, 0x39, 0x36, 0xe9, 0xdc, 0x06, 0x79, 0x78, 0xd4, - 0xa8, 0x13, 0x8b, 0xae, 0x85, 0xce, 0x71, 0x0b, 0x81, 0xc3, 0x23, 0x2f, 0x12, 0x6f, 0xc3, 0x0a, - 0x2d, 0x2e, 0x54, 0x18, 0x32, 0x5f, 0x56, 0x2b, 0xd5, 0x90, 0x58, 0xf1, 0x31, 0x80, 0x54, 0x4b, - 0xcc, 0xa3, 0x4b, 0x12, 0xbe, 0x93, 0xc5, 0x94, 0x9d, 0xff, 0x59, 0xe5, 0xe5, 0x1d, 0x58, 0xc6, - 0x49, 0x12, 0x35, 0x6e, 0x50, 0xc9, 0x1b, 0xf9, 0x19, 0x44, 0x35, 0x25, 0xbc, 0xb8, 0x0f, 0x65, - 0x9c, 0x28, 0xb4, 0xaa, 0x1b, 0x59, 0x3d, 0x4d, 0xce, 0x2a, 0xdc, 0xb9, 0x9d, 0xf3, 0xc1, 0xb7, - 0x9e, 0x78, 0x00, 0x25, 0x1b, 0x17, 0xf3, 0x4d, 0x2a, 0xf1, 0x7a, 0x66, 0x5c, 0x90, 0x8b, 0xee, - 0x38, 0xc7, 0xa4, 0x3a, 0x12, 0x8d, 0xd8, 0x83, 0x3a, 0x4e, 0xa3, 0x47, 0x24, 0x8c, 0x61, 0xf7, - 0x35, 0x6e, 0x51, 0xae, 0x37, 0x66, 0x72, 0xf5, 0x24, 0x11, 0x75, 0x76, 0xdb, 0x8f, 0xc3, 0x4b, - 0x63, 0xcd, 0xcf, 0xc2, 0xc4, 0x2d, 0x28, 0xbb, 0x51, 0x37, 0x18, 0x9d, 0x25, 0x47, 0xc4, 0x49, - 0x5a, 0x7c, 0x01, 0x6b, 0x34, 0xb1, 0x30, 0x89, 0x1f, 0x97, 0xe7, 0xc3, 0xd9, 0x29, 0xa3, 0x50, - 0x46, 0x9e, 0x12, 0x77, 0x50, 0x37, 0x32, 0x63, 0x67, 0x3c, 0x09, 0x42, 0xd4, 0xf0, 0x5e, 0x57, - 0x67, 0x0d, 0x43, 0x05, 0x42, 0x41, 0x29, 0xf1, 0x4d, 0x31, 0x83, 0xe3, 0xe3, 0xc8, 0x89, 0xe9, - 0x3c, 0xb8, 0x64, 0xd4, 0x95, 0x8b, 0x4a, 0x9f, 0xa0, 0x24, 0xa8, 0x44, 0xa6, 0x7d, 0xe9, 0x5b, - 0x63, 0x77, 0xd4, 0xb8, 0xcb, 0x8a, 0xa4, 0x1b, 0xed, 0x30, 0x20, 0xab, 0xcb, 0x6d, 0xe5, 0x74, - 0xb9, 0xab, 0xb0, 0x6c, 0x1f, 0xe1, 0x72, 0x7c, 0x83, 0x8a, 0x2d, 0xd9, 0x47, 0x1d, 0x5b, 0x7c, - 0x40, 0x56, 0x66, 0x66, 0x81, 0x0d, 0x3d, 0x6b, 0x8d, 0x4b, 0x38, 0xa3, 0x91, 0x52, 0x88, 0x2d, - 0xa8, 0xee, 0x3a, 0x56, 0x3c, 0x0d, 0x9d, 0x5d, 0xcf, 0x3a, 0x69, 0xbc, 0x49, 0x25, 0x65, 0x41, - 0x58, 0x3b, 0x2f, 0x38, 0x71, 0x47, 0x16, 0xad, 0xfc, 0x7b, 0x2c, 0x17, 0x4b, 0x48, 0xc7, 0x4e, - 0x55, 0x18, 0x4b, 0x0a, 0xbb, 0x6f, 0x65, 0x55, 0x18, 0xcb, 0x53, 0x07, 0x2b, 0x69, 0x5f, 0x38, - 0x93, 0x60, 0x74, 0xda, 0x78, 0x9b, 0xd6, 0xdb, 0x9a, 0xea, 0x8a, 0x36, 0x02, 0xc5, 0x3b, 0xb0, - 0x2e, 0xd5, 0x7a, 0x53, 0x39, 0xd6, 0xbc, 0x43, 0x74, 0x75, 0x09, 0x6e, 0x49, 0xff, 0x1a, 0x1d, - 0xd6, 0xb8, 0x40, 0xdb, 0x1c, 0x59, 0xa3, 0x53, 0xa7, 0x71, 0x9f, 0x2b, 0x4e, 0xc5, 0xd9, 0x2d, - 0x04, 0xdd, 0x7a, 0x4a, 0x0a, 0x25, 0x0d, 0xd7, 0xa7, 0x33, 0x5c, 0x31, 0xb7, 0xa6, 0x33, 0xec, - 0x73, 0xef, 0x4a, 0x96, 0x39, 0x6e, 0x2f, 0xd3, 0xf6, 0x71, 0xeb, 0x97, 0x20, 0xe6, 0x27, 0xda, - 0x0f, 0xb1, 0xe8, 0x65, 0xc9, 0xa2, 0x7f, 0xb6, 0xf4, 0xa4, 0xa0, 0xff, 0x8f, 0x05, 0x58, 0xcb, - 0xb1, 0xa0, 0x85, 0x7b, 0x0d, 0x0b, 0xe2, 0xd6, 0x58, 0x1a, 0xb2, 0x38, 0xa1, 0xd4, 0x3f, 0xd7, - 0x3f, 0xf1, 0x9c, 0x8c, 0xfa, 0x47, 0x69, 0xf1, 0x0b, 0xb8, 0x7a, 0x3c, 0xf5, 0xbc, 0xd8, 0xb9, - 0x88, 0x95, 0x59, 0x3f, 0x74, 0x8e, 0xa5, 0x1d, 0x77, 0xee, 0xb8, 0x68, 0x43, 0xd1, 0xb2, 0x69, - 0xcb, 0x70, 0x8e, 0xc5, 0xef, 0x82, 0x48, 0x0a, 0x60, 0x69, 0x0b, 0xf3, 0x2f, 0x2f, 0xce, 0xaf, - 0x29, 0x52, 0xea, 0x06, 0xc3, 0x39, 0xd6, 0xff, 0xbb, 0x22, 0xd4, 0xf6, 0xac, 0xe8, 0x74, 0xdf, - 0x9a, 0x0c, 0x62, 0x2b, 0x8e, 0x70, 0x61, 0x9c, 0x5a, 0xd1, 0xe9, 0xd8, 0x9a, 0xb0, 0xb2, 0x58, - 0x60, 0x8b, 0xa3, 0x84, 0x91, 0xa6, 0x78, 0x0b, 0xca, 0x98, 0xec, 0xfb, 0x07, 0xcf, 0xa4, 0xf9, - 0x2f, 0x49, 0x23, 0x43, 0x8e, 0x4e, 0xa7, 0xc7, 0xc7, 0x49, 0x53, 0x55, 0x52, 0xdc, 0x83, 0x35, - 0xf9, 0x93, 0xf4, 0xb1, 0x0b, 0x79, 0xda, 0x91, 0x07, 0x8a, 0xc7, 0x50, 0x95, 0x80, 0xa1, 0xda, - 0x3e, 0xea, 0x89, 0x99, 0x38, 0x45, 0x18, 0x59, 0x2a, 0xf1, 0x2b, 0xb8, 0x96, 0x49, 0xee, 0x06, - 0xe1, 0xfe, 0xd4, 0x8b, 0xdd, 0x56, 0x4f, 0xca, 0xae, 0xaf, 0xcd, 0x65, 0x4f, 0x49, 0x8c, 0xc5, - 0x39, 0xf3, 0xb5, 0xdd, 0x77, 0x7d, 0xda, 0x8d, 0x8a, 0x46, 0x1e, 0x38, 0x43, 0x65, 0x5d, 0xd0, - 0x26, 0x94, 0xa7, 0xb2, 0x2e, 0x90, 0x4d, 0x49, 0xc0, 0xbe, 0x13, 0x9f, 0x06, 0xb6, 0x14, 0x46, - 0xaf, 0xe6, 0xaa, 0xc5, 0x28, 0x23, 0x4f, 0x89, 0xdd, 0xe9, 0x4f, 0x3d, 0x6f, 0xe4, 0xc7, 0xa4, - 0xff, 0x14, 0x0d, 0x95, 0xc4, 0x0d, 0x3a, 0xb4, 0xfc, 0x13, 0x27, 0x6a, 0x54, 0xb7, 0x8a, 0xf7, - 0x0b, 0x86, 0x4c, 0xe9, 0x7f, 0x67, 0x09, 0x96, 0x79, 0x24, 0x5f, 0x83, 0xca, 0x11, 0x9d, 0x46, - 0xfb, 0xd3, 0xb1, 0x3a, 0x35, 0x26, 0x40, 0x6f, 0x3a, 0x66, 0xb5, 0x43, 0x9a, 0x6f, 0x0b, 0x06, - 0xfd, 0xc6, 0x22, 0x83, 0x69, 0x8c, 0xdf, 0x2a, 0x12, 0x54, 0xa6, 0xb0, 0x12, 0x61, 0x70, 0x4e, - 0xb3, 0xa1, 0x44, 0x08, 0x95, 0xa4, 0x83, 0x69, 0xda, 0xeb, 0x31, 0xd3, 0x32, 0xe1, 0xca, 0x04, - 0x68, 0xf9, 0xf1, 0xac, 0xe9, 0x7a, 0x65, 0xde, 0x74, 0x7d, 0x07, 0x50, 0xa9, 0x19, 0x39, 0x7d, - 0xdf, 0x69, 0xf5, 0xe4, 0xb9, 0x52, 0x06, 0x82, 0x2b, 0xd4, 0x0e, 0x26, 0xf2, 0xc0, 0x08, 0x7f, - 0x8a, 0xcf, 0x92, 0xd9, 0x49, 0x6d, 0x94, 0x3a, 0xa4, 0xdc, 0x0b, 0xb3, 0xf3, 0xd8, 0xc8, 0xd1, - 0x61, 0x49, 0xb8, 0xc1, 0xb1, 0x0e, 0x89, 0x3f, 0xf5, 0x36, 0x80, 0x11, 0x9c, 0x47, 0x4e, 0x4c, - 0x67, 0x34, 0x37, 0xa8, 0x89, 0x39, 0x1f, 0x94, 0xe0, 0xfc, 0x20, 0x88, 0x12, 0x03, 0xd0, 0xd2, - 0x62, 0x03, 0x90, 0xfe, 0x21, 0xac, 0xa2, 0xf4, 0x63, 0xc5, 0x96, 0xb8, 0x27, 0xad, 0xd8, 0x2c, - 0xb3, 0xc9, 0xf3, 0x81, 0xf4, 0x1b, 0xd2, 0x18, 0xde, 0x55, 0xdf, 0xa5, 0x3c, 0x6f, 0x64, 0x0c, - 0x19, 0xc9, 0xce, 0x2b, 0x0b, 0x94, 0xf2, 0xd4, 0x6b, 0x50, 0xc1, 0xaa, 0xd1, 0x61, 0xbb, 0x3a, - 0x75, 0x0c, 0x83, 0xf3, 0x16, 0xa6, 0xf5, 0x7f, 0x5a, 0x80, 0x6a, 0x3f, 0xb4, 0x71, 0xcb, 0x1f, - 0x4c, 0x9c, 0xd1, 0x0f, 0xda, 0xab, 0x50, 0xfa, 0x0a, 0x3c, 0xcf, 0xa2, 0xcd, 0x45, 0x1a, 0x12, - 0x12, 0x80, 0xf8, 0x18, 0x4a, 0xc7, 0xb8, 0x89, 0x14, 0xb3, 0x2a, 0x5f, 0xa6, 0x78, 0xf5, 0x1b, - 0xb7, 0x15, 0x83, 0x48, 0xf5, 0xdf, 0x4b, 0xbe, 0x4f, 0x7b, 0x4d, 0xf6, 0xf8, 0xf7, 0x0a, 0x39, - 0x92, 0x0c, 0x5a, 0x5a, 0x41, 0x94, 0xa1, 0xb4, 0xd3, 0x1e, 0xb4, 0x58, 0x2d, 0x42, 0x05, 0x69, - 0x60, 0xee, 0x76, 0x8c, 0xc1, 0x50, 0x2b, 0x91, 0x67, 0x0a, 0x01, 0xba, 0xcd, 0xc1, 0x50, 0x2b, - 0x0b, 0x80, 0x95, 0xc3, 0x5e, 0xe7, 0x57, 0x87, 0x6d, 0x4d, 0xd3, 0xff, 0x49, 0x01, 0x20, 0x3d, - 0x3f, 0x12, 0xef, 0x41, 0xf5, 0x9c, 0x52, 0x66, 0xe6, 0x48, 0x3d, 0xdb, 0x46, 0x60, 0x34, 0x49, - 0x86, 0x1f, 0x40, 0x2d, 0xd9, 0x24, 0x51, 0x6a, 0x9a, 0x3f, 0x5b, 0xaf, 0x26, 0xf8, 0xed, 0x4b, - 0xf1, 0x3e, 0x94, 0x03, 0x6c, 0x07, 0x92, 0x16, 0xb3, 0x22, 0x53, 0xa6, 0xf9, 0xc6, 0x6a, 0xc0, - 0x09, 0x94, 0xae, 0x8e, 0x43, 0x65, 0x18, 0x4a, 0x48, 0x77, 0x11, 0xd4, 0xf2, 0xac, 0x69, 0xe4, - 0x18, 0x8c, 0x4f, 0x76, 0x89, 0xe5, 0x74, 0x97, 0xd0, 0x7f, 0x0d, 0xf5, 0x81, 0x35, 0x9e, 0xf0, - 0x5e, 0x42, 0x0d, 0x13, 0x50, 0xc2, 0x39, 0x21, 0xa7, 0x1e, 0xfd, 0xc6, 0x45, 0x77, 0xe0, 0x84, - 0x23, 0xc7, 0x57, 0x6b, 0x54, 0x25, 0x91, 0xfd, 0x1e, 0xe2, 0x6e, 0x62, 0x04, 0xe7, 0x6a, 0x3b, - 0x51, 0x69, 0xfd, 0xdf, 0x2b, 0x40, 0x35, 0x53, 0x0d, 0xf1, 0x21, 0x94, 0x48, 0x0c, 0x2f, 0x64, - 0x19, 0x61, 0x86, 0x80, 0x7f, 0xb3, 0xe0, 0x86, 0x84, 0xe2, 0x6d, 0x58, 0x8e, 0x62, 0x2b, 0x54, - 0x47, 0x91, 0x5a, 0x26, 0xc7, 0x76, 0x30, 0xf5, 0x6d, 0x83, 0xd1, 0x42, 0x87, 0xa2, 0xe3, 0xdb, - 0xd2, 0x94, 0x36, 0x4f, 0x85, 0x48, 0x7d, 0x0b, 0x2a, 0x49, 0xf1, 0x38, 0x05, 0x8c, 0xfe, 0x8b, - 0x81, 0x76, 0x45, 0x54, 0x60, 0xd9, 0x68, 0xf6, 0x9e, 0xb6, 0xb5, 0x82, 0xfe, 0xf7, 0x0a, 0x00, - 0x69, 0x2e, 0xf1, 0x30, 0x57, 0xdb, 0x5b, 0xb3, 0xa5, 0x3e, 0xa4, 0xbf, 0x99, 0xca, 0xde, 0x86, - 0xca, 0xd4, 0x27, 0xa0, 0x63, 0xcb, 0x9d, 0x28, 0x05, 0xa0, 0xee, 0xa8, 0xbc, 0x64, 0x67, 0x74, - 0xc7, 0x97, 0x96, 0xa7, 0xff, 0x0c, 0x2a, 0x49, 0x71, 0xa8, 0x9b, 0xef, 0xf6, 0xbb, 0xdd, 0xfe, - 0x8b, 0x4e, 0xef, 0xa9, 0x76, 0x05, 0x93, 0x07, 0x46, 0xbb, 0xd5, 0xde, 0xc1, 0x64, 0x01, 0xe7, - 0x6c, 0xeb, 0xd0, 0x30, 0xda, 0xbd, 0xa1, 0x69, 0xf4, 0x5f, 0x68, 0x4b, 0xfa, 0x1f, 0x14, 0x60, - 0xad, 0xef, 0xd9, 0xad, 0xc0, 0x6b, 0x59, 0x13, 0x94, 0xb3, 0xc4, 0x2f, 0x60, 0xe3, 0x68, 0x8a, - 0xb2, 0xfe, 0xc4, 0xb3, 0x46, 0xce, 0x69, 0xe0, 0xd9, 0x8e, 0x5a, 0x84, 0xb9, 0xc3, 0x56, 0x79, - 0x76, 0xa2, 0x11, 0xf1, 0x41, 0x4a, 0x2b, 0x3e, 0x85, 0xda, 0x24, 0x0c, 0x8e, 0x1c, 0x75, 0x90, - 0xb5, 0xf4, 0xca, 0xbc, 0x55, 0xa2, 0x93, 0x87, 0xcc, 0xff, 0xf5, 0x32, 0xd4, 0x76, 0x1c, 0x7b, - 0x3a, 0xf9, 0x32, 0x70, 0xfd, 0x56, 0x7c, 0x21, 0x3e, 0x81, 0x5a, 0xe0, 0x91, 0xc1, 0xca, 0xcc, - 0x38, 0xc0, 0x2d, 0x2a, 0x07, 0x02, 0x6a, 0x01, 0xb9, 0xcb, 0x7d, 0x00, 0x57, 0xa5, 0xb9, 0x84, - 0xad, 0xa7, 0x17, 0x9c, 0x19, 0xd7, 0xcc, 0xb2, 0xa1, 0x31, 0x8a, 0x37, 0x68, 0x22, 0xff, 0x1d, - 0xd8, 0xcc, 0x90, 0x23, 0x63, 0x61, 0xfa, 0xe2, 0xdc, 0x1a, 0xdb, 0x48, 0xf2, 0x26, 0xae, 0x79, - 0x5f, 0xc2, 0xa6, 0xaa, 0xe1, 0x88, 0x7b, 0x8f, 0x33, 0x97, 0xb2, 0x4a, 0x55, 0xae, 0x77, 0x65, - 0x85, 0x37, 0x82, 0x2c, 0x90, 0xca, 0xfa, 0x18, 0xae, 0xd9, 0xd8, 0x7a, 0x93, 0x3b, 0xff, 0xcc, - 0x71, 0x26, 0xa6, 0x97, 0xf1, 0x5b, 0x21, 0xe4, 0x36, 0xe2, 0x9e, 0x39, 0xce, 0xa4, 0x6b, 0x45, - 0xb1, 0x78, 0x02, 0x1b, 0xd6, 0xf1, 0xb1, 0x33, 0x8a, 0x1d, 0xdb, 0xc4, 0x6d, 0x6c, 0xa1, 0x95, - 0x16, 0x65, 0xa5, 0x75, 0x45, 0x86, 0xcb, 0xb1, 0x15, 0x78, 0xe2, 0x29, 0xdc, 0x9c, 0x9c, 0x5e, - 0x46, 0x24, 0x48, 0x8f, 0x4e, 0x71, 0xb7, 0xcd, 0x94, 0xb0, 0xba, 0xa0, 0x84, 0xeb, 0x8a, 0xbc, - 0xc5, 0xd4, 0xaa, 0xa0, 0x27, 0x70, 0x53, 0x75, 0x1f, 0xea, 0x77, 0xf9, 0x3e, 0x2f, 0x53, 0x9f, - 0x5f, 0x93, 0xfd, 0x86, 0xf8, 0x4c, 0xc7, 0xdf, 0x07, 0x8d, 0x1d, 0xb1, 0x8e, 0x71, 0xea, 0xd2, - 0xd7, 0xa5, 0x73, 0x7a, 0x9d, 0xe0, 0xbb, 0xb4, 0xf8, 0x90, 0x7d, 0xdc, 0x07, 0xcd, 0x19, 0xbb, - 0xb1, 0x69, 0x91, 0xb4, 0xca, 0x94, 0x7c, 0x2a, 0x56, 0x47, 0xb8, 0x34, 0xdb, 0x21, 0xe5, 0x67, - 0xa0, 0x49, 0xa2, 0x63, 0xd2, 0x01, 0xb0, 0x35, 0xd5, 0x05, 0xad, 0xa9, 0x33, 0xd5, 0x2e, 0x12, - 0x61, 0x2b, 0xf6, 0x41, 0x28, 0x1b, 0x24, 0x59, 0x56, 0x58, 0x91, 0x65, 0x15, 0x5a, 0x3a, 0x1e, - 0xf5, 0x77, 0x9e, 0x1d, 0xa6, 0xea, 0x31, 0x35, 0x46, 0x2d, 0x80, 0xe3, 0x3c, 0x38, 0xd2, 0xff, - 0xcb, 0x15, 0xa8, 0xb0, 0xd9, 0x0d, 0xa7, 0xf1, 0x7d, 0x58, 0x0d, 0x8e, 0xbe, 0x21, 0x51, 0xf6, - 0x15, 0x9e, 0x53, 0x2b, 0xc1, 0xd1, 0x37, 0x28, 0xff, 0xbe, 0xa7, 0x44, 0x10, 0xdb, 0x39, 0x96, - 0xab, 0xa6, 0x9e, 0x57, 0x32, 0xa5, 0x48, 0xb2, 0xe3, 0x1c, 0xa3, 0x74, 0xe9, 0xfa, 0x91, 0x13, - 0xc6, 0x6c, 0x36, 0x5d, 0x7d, 0xf5, 0xe2, 0x60, 0x32, 0xb2, 0xa4, 0x3e, 0x86, 0xaa, 0x34, 0x4d, - 0x52, 0xa6, 0xf2, 0xab, 0x33, 0x31, 0x19, 0x65, 0xfa, 0x02, 0xea, 0xe9, 0xf6, 0x43, 0xf9, 0x2a, - 0xaf, 0xcc, 0xb7, 0x96, 0x50, 0x52, 0xd6, 0x47, 0x70, 0x3d, 0x3a, 0x73, 0x27, 0xa6, 0xac, 0x69, - 0xe0, 0xd3, 0xf1, 0xaa, 0x39, 0x39, 0x93, 0x03, 0x28, 0x10, 0xdb, 0x21, 0x64, 0xdf, 0xef, 0x4d, - 0x3d, 0xef, 0xe0, 0x4c, 0xbc, 0x0b, 0x1b, 0x92, 0x7c, 0x92, 0xcc, 0x27, 0x1a, 0xc5, 0x65, 0xa3, - 0xce, 0x88, 0x03, 0x39, 0x8f, 0xc4, 0xe7, 0xd0, 0xe0, 0x39, 0x24, 0x1b, 0xc5, 0xd3, 0x9c, 0x67, - 0x08, 0x9f, 0x81, 0x5e, 0x23, 0x3c, 0x9b, 0x54, 0x9b, 0x84, 0xa5, 0x89, 0xf2, 0x11, 0x6c, 0xba, - 0x27, 0x7e, 0x10, 0xaa, 0x2c, 0x72, 0xfa, 0xcb, 0xc3, 0x51, 0xc1, 0xb8, 0x66, 0x66, 0xd1, 0x60, - 0x4b, 0xe4, 0xf2, 0xbc, 0x34, 0x63, 0x72, 0xcb, 0xc0, 0x1c, 0xa8, 0x85, 0xd6, 0xb3, 0xeb, 0xf3, - 0x92, 0x5d, 0x36, 0x8c, 0xe0, 0xbc, 0x63, 0xe3, 0x92, 0x96, 0xa4, 0x6a, 0x8d, 0xc4, 0x17, 0xd4, - 0x9a, 0x75, 0x6a, 0x8d, 0x60, 0x64, 0x32, 0x53, 0xb0, 0x45, 0xbf, 0x04, 0x31, 0xb7, 0xa4, 0xa3, - 0x86, 0xf6, 0xca, 0xfe, 0xd6, 0x66, 0x56, 0x36, 0xad, 0x81, 0xb9, 0x15, 0xbd, 0xb1, 0x68, 0x0d, - 0x8c, 0xf2, 0x2b, 0xb9, 0x0d, 0x8d, 0xfc, 0x97, 0xcf, 0x1d, 0xf7, 0xe4, 0x94, 0x66, 0x97, 0xf4, - 0xfb, 0xcf, 0xe7, 0xbf, 0x96, 0xfd, 0xf2, 0x0b, 0xa2, 0xfd, 0x41, 0xce, 0x72, 0xf5, 0xc7, 0x73, - 0x16, 0xfd, 0x3f, 0x28, 0x40, 0x85, 0x67, 0x06, 0x2e, 0xa2, 0x37, 0xa0, 0xf8, 0x3d, 0x0b, 0x08, - 0x71, 0xe2, 0x01, 0x6c, 0x58, 0xb6, 0x3d, 0x33, 0xa0, 0xbc, 0x93, 0xae, 0x5b, 0xb6, 0x9d, 0x1b, - 0xcd, 0xfb, 0xa0, 0xb9, 0x91, 0xa9, 0x0c, 0x51, 0xa9, 0x77, 0x65, 0xd9, 0xa8, 0xbb, 0x91, 0xb4, - 0x43, 0xf1, 0xa9, 0x64, 0x6e, 0x4d, 0x96, 0xbe, 0x7f, 0x4d, 0xea, 0x02, 0xc0, 0x70, 0x68, 0xdb, - 0x6c, 0xc5, 0x17, 0x5f, 0x96, 0xca, 0x05, 0x0d, 0xf4, 0xff, 0xa6, 0x0a, 0xd5, 0xa6, 0x6f, 0x79, - 0x97, 0xdf, 0x39, 0xe4, 0x1a, 0x4a, 0x27, 0x41, 0x93, 0xa9, 0x9c, 0xa5, 0xec, 0x61, 0x51, 0x21, - 0x08, 0xd5, 0xec, 0x2e, 0x54, 0x83, 0x69, 0x9c, 0xe0, 0xd9, 0xe7, 0x02, 0x18, 0x44, 0x04, 0x49, - 0xfe, 0xe4, 0xfc, 0x53, 0xe5, 0x27, 0x85, 0x36, 0xcd, 0x9f, 0x28, 0x39, 0x49, 0x7e, 0x22, 0x78, - 0x13, 0xd6, 0x62, 0x77, 0xec, 0x90, 0xcb, 0xcb, 0x74, 0xec, 0xd8, 0x7c, 0x8d, 0x86, 0x6f, 0x66, - 0xb4, 0x24, 0x0c, 0x4b, 0x19, 0x3b, 0xe3, 0x20, 0xbc, 0xe4, 0x52, 0x56, 0xb8, 0x14, 0x06, 0x51, - 0x29, 0xef, 0x83, 0x38, 0xb7, 0xdc, 0xd8, 0xcc, 0x17, 0xc5, 0x8a, 0xa5, 0x86, 0x98, 0x61, 0xb6, - 0xb8, 0xeb, 0xb0, 0x62, 0xbb, 0xd1, 0x59, 0xa7, 0x2f, 0x95, 0x4a, 0x99, 0xc2, 0xb6, 0x44, 0x23, - 0x0b, 0x65, 0xda, 0xd8, 0x61, 0xee, 0x5f, 0x34, 0x2a, 0x08, 0xd9, 0x46, 0x00, 0xca, 0x44, 0xbe, - 0x13, 0x9f, 0x07, 0x21, 0xe6, 0x64, 0x9d, 0x31, 0x05, 0xa0, 0xec, 0x88, 0xa4, 0xf8, 0x21, 0x62, - 0x0f, 0x45, 0x23, 0x49, 0xa3, 0x36, 0xc6, 0xac, 0x82, 0xb0, 0x35, 0xae, 0x7e, 0x0a, 0x11, 0xf7, - 0xa0, 0x4e, 0xd5, 0x27, 0x9d, 0x12, 0xdb, 0x40, 0x2b, 0xbf, 0x68, 0xd4, 0x10, 0x4a, 0x86, 0x35, - 0xa4, 0xfa, 0x02, 0x6e, 0xe6, 0xda, 0x67, 0x5a, 0x61, 0x68, 0x5d, 0x9a, 0x63, 0xeb, 0x9b, 0x20, - 0x24, 0x23, 0x68, 0xd1, 0xb8, 0x9e, 0xed, 0xb6, 0x26, 0xa2, 0xf7, 0x11, 0xfb, 0xca, 0xac, 0xae, - 0x1f, 0x84, 0x64, 0x21, 0x5d, 0x98, 0x15, 0xb1, 0x64, 0xce, 0xa3, 0x01, 0x26, 0x05, 0x37, 0xe2, - 0x1b, 0x3d, 0x46, 0x95, 0x60, 0xdb, 0x04, 0x42, 0x15, 0x2f, 0x7a, 0xcc, 0x7b, 0xec, 0x86, 0xf4, - 0x4b, 0x7f, 0x4c, 0x9b, 0x2a, 0x23, 0x4e, 0x1d, 0xcb, 0xa6, 0x35, 0x4b, 0x88, 0x3d, 0xc7, 0x22, - 0x27, 0xad, 0xe8, 0xb1, 0x39, 0x99, 0xc6, 0x7c, 0x15, 0xc7, 0x58, 0x8e, 0x1e, 0x1f, 0x4c, 0x63, - 0x09, 0x3e, 0x71, 0x62, 0xf2, 0xac, 0x20, 0xf0, 0x53, 0xbe, 0x31, 0x16, 0x3d, 0x56, 0x07, 0x6e, - 0xd7, 0x64, 0xdf, 0x3e, 0x96, 0x27, 0x6a, 0x3a, 0xac, 0x25, 0x48, 0x73, 0x3c, 0xe5, 0xbb, 0x37, - 0x45, 0xa3, 0xaa, 0x08, 0xf6, 0xa7, 0x1e, 0x0e, 0x2c, 0x99, 0xc2, 0xcc, 0x10, 0xab, 0x72, 0x83, - 0x87, 0x8e, 0x20, 0x06, 0xd6, 0xe6, 0x35, 0xe0, 0x84, 0x79, 0xea, 0xc6, 0x64, 0xa9, 0x2d, 0x1a, - 0x65, 0x02, 0xec, 0xb9, 0x31, 0xae, 0x63, 0x46, 0xca, 0x19, 0x48, 0x45, 0xdc, 0x24, 0xa2, 0x75, - 0x42, 0xec, 0x13, 0x9c, 0x0a, 0x42, 0x21, 0x22, 0x4b, 0x8b, 0xe5, 0xdd, 0x22, 0xd2, 0x7a, 0x86, - 0x14, 0x4b, 0x7d, 0x1b, 0x38, 0xb3, 0x89, 0x53, 0x8f, 0xcb, 0x7c, 0x8d, 0x0d, 0x1c, 0x04, 0xde, - 0x71, 0xa3, 0x33, 0x2a, 0xf1, 0x1e, 0xd4, 0x33, 0x74, 0x58, 0xde, 0x6d, 0x9e, 0x19, 0x09, 0x59, - 0xae, 0x8e, 0xa1, 0x33, 0x0e, 0x62, 0xd9, 0xcc, 0xd7, 0x33, 0x75, 0x34, 0x08, 0x9e, 0xaf, 0xa3, - 0xa4, 0xc5, 0x32, 0xef, 0x64, 0xea, 0xc8, 0xa4, 0x58, 0xea, 0x1b, 0x50, 0x3b, 0x0f, 0xdd, 0x38, - 0x76, 0xa4, 0x90, 0x73, 0x97, 0x3b, 0x56, 0xc2, 0x68, 0xf5, 0xbf, 0x01, 0x35, 0xee, 0x79, 0xc9, - 0xdf, 0xb6, 0x98, 0x44, 0xc2, 0x14, 0x83, 0x90, 0xbd, 0x31, 0x76, 0x7d, 0x32, 0xc7, 0x16, 0x8d, - 0x0a, 0x43, 0xf6, 0x5d, 0x3f, 0x8b, 0xb6, 0x2e, 0xc8, 0x28, 0x9b, 0xa2, 0xad, 0x0b, 0x5a, 0x92, - 0x13, 0xd7, 0xf3, 0x78, 0xe1, 0xbf, 0x29, 0x97, 0x24, 0x42, 0x68, 0xdd, 0x27, 0x68, 0xfa, 0xfa, - 0xbd, 0x0c, 0x9a, 0xbe, 0x8d, 0x13, 0x87, 0xd0, 0xf8, 0xe9, 0xb7, 0xe4, 0xc4, 0x41, 0x00, 0x7e, - 0x39, 0x45, 0x5a, 0x17, 0x64, 0x72, 0x4d, 0x90, 0xd6, 0x85, 0xe4, 0x5b, 0xb4, 0x53, 0x60, 0xde, - 0x77, 0x12, 0xbe, 0x85, 0x20, 0xcc, 0x9d, 0x25, 0xb0, 0x2e, 0xc8, 0xc6, 0x9a, 0x21, 0xb0, 0x2e, - 0xc8, 0xb8, 0xe0, 0x58, 0x36, 0x57, 0xfc, 0x5d, 0x2e, 0x1e, 0x01, 0x54, 0xef, 0x2d, 0xa8, 0x45, - 0x8f, 0xcd, 0x14, 0xff, 0x80, 0xb3, 0x47, 0x8f, 0x0d, 0x45, 0x71, 0x0f, 0xea, 0xc9, 0xd4, 0x60, - 0x9a, 0xf7, 0x78, 0xe0, 0x6d, 0x39, 0x35, 0xe8, 0x72, 0xca, 0x6f, 0x0a, 0x70, 0x8b, 0x8f, 0x38, - 0x89, 0xfd, 0xef, 0x3b, 0x51, 0x64, 0x9d, 0x38, 0xbb, 0x41, 0xb8, 0x3b, 0xfd, 0xee, 0xbb, 0x4b, - 0x71, 0x1f, 0xd6, 0x0f, 0xac, 0xd0, 0xf1, 0xe3, 0xc4, 0xf3, 0x40, 0x1a, 0x55, 0x67, 0xc1, 0xe2, - 0x09, 0x68, 0x0c, 0x62, 0x9f, 0xfc, 0x96, 0x3a, 0x37, 0x9f, 0x3d, 0x88, 0x9a, 0xa3, 0xd2, 0xff, - 0x69, 0x01, 0x2a, 0x3b, 0x6e, 0x14, 0x1b, 0xb8, 0x5d, 0x8a, 0x2f, 0x40, 0xf3, 0x82, 0x73, 0x54, - 0xf6, 0x49, 0x8c, 0xce, 0xe8, 0x9c, 0x72, 0x97, 0x4c, 0x15, 0xcd, 0x3a, 0x11, 0xa6, 0x9a, 0xe2, - 0x17, 0xa0, 0x4d, 0x27, 0x93, 0x7c, 0xd6, 0xa5, 0x57, 0x64, 0x25, 0xc2, 0x34, 0xeb, 0x7b, 0x50, - 0xcd, 0x7c, 0x75, 0x81, 0x5e, 0x0a, 0xe9, 0xb7, 0x90, 0x38, 0xf3, 0x9d, 0x05, 0x77, 0x82, 0x20, - 0x2d, 0x5d, 0xff, 0x8f, 0x96, 0x40, 0x93, 0x16, 0x5d, 0xcb, 0x76, 0x42, 0x72, 0x60, 0xc8, 0x59, - 0x34, 0x0a, 0x3f, 0x68, 0xd1, 0xd8, 0x82, 0x65, 0xcf, 0x1d, 0x27, 0x2e, 0xc8, 0xb9, 0x03, 0x60, - 0x42, 0xe0, 0x58, 0x07, 0xa1, 0x7b, 0x42, 0xb6, 0x97, 0xec, 0x35, 0x11, 0x3a, 0x28, 0xd8, 0x9d, - 0xfa, 0x23, 0x1a, 0xa2, 0x87, 0x00, 0xb6, 0x1b, 0xc5, 0x26, 0x59, 0x23, 0xf3, 0x66, 0xec, 0xa4, - 0xff, 0x8d, 0x8a, 0x9d, 0x0c, 0xc5, 0x7d, 0xd0, 0x32, 0x72, 0xb2, 0x9f, 0x18, 0x12, 0x97, 0x8d, - 0x54, 0x7e, 0x6e, 0xf9, 0x2d, 0x3f, 0x9e, 0xa3, 0x44, 0x99, 0x70, 0x65, 0x8e, 0x12, 0xe5, 0xc1, - 0xfb, 0xa0, 0x05, 0x2f, 0x9d, 0xd0, 0x3c, 0x76, 0xe2, 0xd1, 0xa9, 0xc9, 0xcd, 0x5a, 0xe5, 0x83, - 0x1b, 0x84, 0xef, 0x22, 0xb8, 0x8b, 0x50, 0xfd, 0x02, 0xae, 0x3e, 0x77, 0x46, 0x71, 0x10, 0x76, - 0x12, 0xcf, 0x17, 0x79, 0x47, 0x2f, 0x0c, 0xbc, 0xc4, 0xb6, 0x8f, 0xbf, 0xc5, 0x3b, 0x80, 0x1a, - 0x87, 0x33, 0x52, 0x3d, 0xb4, 0x50, 0x21, 0x71, 0x46, 0xd8, 0x4f, 0xcb, 0xa9, 0x6c, 0x34, 0x2f, - 0xf8, 0x30, 0x52, 0xff, 0xdb, 0xab, 0xb0, 0x9e, 0xf9, 0x34, 0x5d, 0x6e, 0x79, 0x04, 0x35, 0x79, - 0x04, 0x90, 0x3a, 0x7d, 0x2d, 0xf8, 0x50, 0x95, 0x89, 0x58, 0xd4, 0x7a, 0x02, 0x5a, 0x36, 0xcf, - 0xf7, 0x68, 0x41, 0xf5, 0x4c, 0x36, 0xd4, 0x85, 0xee, 0xc1, 0x32, 0x9f, 0xcc, 0xe5, 0xea, 0x99, - 0x9c, 0x7a, 0x32, 0x52, 0xfc, 0x35, 0x58, 0xe3, 0x93, 0x5f, 0x53, 0xde, 0xe2, 0x29, 0x65, 0x15, - 0xbc, 0x05, 0x9d, 0x67, 0xd4, 0x98, 0x9e, 0xd2, 0x91, 0xf8, 0x00, 0x6a, 0xe4, 0xda, 0x6d, 0xbe, - 0x24, 0xd2, 0xbc, 0x37, 0x29, 0x9b, 0xe1, 0x08, 0xcf, 0x25, 0x89, 0xf7, 0x60, 0x03, 0xe7, 0x86, - 0xe5, 0x8f, 0x1c, 0x9a, 0x68, 0x64, 0xa7, 0xe4, 0x53, 0x62, 0x4d, 0x21, 0x92, 0x23, 0x98, 0xdf, - 0x81, 0x8a, 0xed, 0x86, 0x0e, 0x13, 0xad, 0xfe, 0x18, 0x9b, 0x65, 0x4a, 0x2f, 0x1e, 0xe3, 0xde, - 0xe6, 0xdb, 0x2e, 0xe9, 0x18, 0x3c, 0x47, 0xca, 0x73, 0x75, 0xab, 0x27, 0x24, 0x34, 0x5f, 0xc4, - 0x67, 0xc4, 0xef, 0xb8, 0x7a, 0x3c, 0xc3, 0x2b, 0x8b, 0x67, 0xf8, 0x9a, 0x22, 0xe3, 0x59, 0xfe, - 0x1e, 0x54, 0x27, 0xa1, 0x63, 0x1e, 0xbb, 0x5e, 0xec, 0x84, 0xa8, 0x88, 0xcf, 0xda, 0x49, 0x60, - 0x12, 0x3a, 0xbb, 0x8c, 0x5d, 0xe8, 0x78, 0x54, 0x5d, 0xe8, 0x78, 0x24, 0x1e, 0xc2, 0x55, 0xd7, - 0x77, 0x63, 0xd7, 0xf2, 0x4c, 0xb6, 0x1e, 0xb1, 0x95, 0xb8, 0x46, 0x47, 0x6e, 0x1b, 0x12, 0x75, - 0x80, 0x18, 0x32, 0x17, 0x8b, 0xcf, 0x60, 0xe3, 0xd8, 0x0d, 0x71, 0x79, 0x12, 0xf7, 0xe2, 0x66, - 0xaf, 0xcd, 0x35, 0x7b, 0x9d, 0x88, 0x0c, 0xa4, 0xe1, 0x76, 0xbf, 0x0f, 0xe2, 0x68, 0x3a, 0x3a, - 0x73, 0x62, 0xd3, 0xb9, 0x98, 0x58, 0xbe, 0x6d, 0x46, 0xb1, 0x33, 0x21, 0x25, 0x6e, 0xcd, 0xd0, - 0x18, 0xd3, 0x26, 0xc4, 0x20, 0x76, 0x26, 0xe2, 0x2d, 0xa8, 0xc7, 0xa7, 0xb8, 0x25, 0x44, 0x66, - 0xe4, 0x58, 0xe1, 0xe8, 0x94, 0x2f, 0x60, 0x1b, 0x6b, 0x12, 0x3a, 0x20, 0xa0, 0x78, 0x0c, 0x6b, - 0x24, 0xc8, 0x26, 0x37, 0xe2, 0xb4, 0x85, 0x37, 0xe2, 0x6a, 0x48, 0xa4, 0x52, 0xe2, 0x31, 0x5c, - 0x9f, 0x04, 0x51, 0x2c, 0xbb, 0xd2, 0x4c, 0xd7, 0x39, 0x09, 0x75, 0x65, 0xe3, 0x2a, 0x62, 0xb9, - 0x27, 0xfb, 0x6a, 0xad, 0x8b, 0xcf, 0x80, 0x04, 0x64, 0x13, 0x85, 0x60, 0xa9, 0x97, 0xcd, 0x4f, - 0x60, 0x5c, 0x82, 0x2f, 0x82, 0xf0, 0x8c, 0x25, 0x62, 0xfc, 0xa5, 0xff, 0x9d, 0xf7, 0xa1, 0xd4, - 0x0b, 0x6c, 0x47, 0x7c, 0x04, 0x15, 0xba, 0xa3, 0x93, 0xd9, 0x29, 0xa4, 0xa1, 0x0a, 0xd1, 0xf4, - 0x87, 0x58, 0x7e, 0xd9, 0x97, 0xbf, 0x5e, 0x7d, 0xed, 0xf3, 0x0d, 0x32, 0xb0, 0x92, 0xb3, 0x1e, - 0xd6, 0xa3, 0xaa, 0xee, 0x32, 0x5b, 0x71, 0x64, 0x30, 0x06, 0x85, 0x74, 0x72, 0x1c, 0x08, 0x1d, - 0x9f, 0x96, 0xdb, 0xb2, 0x91, 0xa4, 0xc9, 0xac, 0x1d, 0x06, 0xc8, 0x0a, 0x58, 0x94, 0x5d, 0x5e, - 0x60, 0xd6, 0x66, 0x3c, 0xc9, 0xb6, 0x1f, 0x41, 0xe5, 0x9b, 0xc0, 0xf5, 0xb9, 0xe2, 0x2b, 0x73, - 0x15, 0xff, 0x32, 0x70, 0x7d, 0xae, 0xf8, 0x37, 0xf2, 0x97, 0x78, 0x13, 0x56, 0x03, 0x9f, 0xcb, - 0x5e, 0x9d, 0x2b, 0x7b, 0x25, 0xf0, 0xbb, 0x7c, 0x5b, 0x62, 0xcd, 0x8d, 0xcc, 0x90, 0x74, 0x5d, - 0xcc, 0x29, 0x9d, 0xfc, 0xaa, 0x6e, 0x64, 0x20, 0x0c, 0x8b, 0xc5, 0x39, 0x2f, 0x07, 0x89, 0x0a, - 0xab, 0xcc, 0xcf, 0x79, 0x46, 0x53, 0x81, 0x6f, 0x41, 0xf9, 0x24, 0x0c, 0xc8, 0x52, 0xb0, 0x60, - 0x75, 0xac, 0x12, 0x6e, 0xfb, 0x12, 0xf5, 0x30, 0xfa, 0xe9, 0xfa, 0x27, 0x26, 0x9d, 0x54, 0xe0, - 0xba, 0x28, 0x1b, 0x35, 0x05, 0xa4, 0x33, 0x88, 0xb7, 0xa0, 0x6c, 0x9d, 0x9c, 0x98, 0xf2, 0xd2, - 0xc7, 0x5c, 0x59, 0xd6, 0xc9, 0x09, 0x7d, 0xf2, 0x21, 0xac, 0x9d, 0xbb, 0xbe, 0x19, 0x4d, 0x9c, - 0x11, 0xd3, 0xae, 0xcd, 0x77, 0xe5, 0xb9, 0xeb, 0x23, 0x0f, 0x21, 0xfa, 0xec, 0x7e, 0x5a, 0xff, - 0xf1, 0xfb, 0xe9, 0xfa, 0xab, 0xf6, 0x53, 0x1d, 0x56, 0xa4, 0x53, 0x81, 0x36, 0x47, 0x22, 0x31, - 0xe2, 0x63, 0xa8, 0x86, 0x96, 0x7f, 0x66, 0x4a, 0x8f, 0xc9, 0xaf, 0xb3, 0xd6, 0x76, 0xc3, 0xf2, - 0xcf, 0xa4, 0xc3, 0x24, 0x84, 0xc9, 0xef, 0xbc, 0xee, 0xbd, 0xf1, 0x03, 0xf6, 0xb0, 0x8c, 0x99, - 0x4d, 0x7c, 0xbf, 0x99, 0xed, 0x53, 0xb2, 0x67, 0x39, 0x7e, 0x6c, 0xaa, 0x0c, 0x57, 0x17, 0x67, - 0xa8, 0x31, 0x59, 0x9f, 0xb3, 0x61, 0x03, 0xe8, 0xb4, 0xcb, 0xa4, 0xa3, 0xb1, 0xcd, 0x5c, 0x03, - 0x92, 0x63, 0x30, 0x03, 0xc2, 0xf4, 0x48, 0xac, 0x09, 0xeb, 0xe9, 0x1d, 0x50, 0xbe, 0x78, 0x7b, - 0x2d, 0x7b, 0xde, 0x9f, 0xbb, 0x34, 0xaa, 0x2c, 0x68, 0x6e, 0xee, 0x26, 0xe9, 0x9b, 0xb0, 0xc6, - 0xce, 0xd3, 0xdc, 0x6f, 0x91, 0xf4, 0x57, 0xaf, 0x11, 0x90, 0xfb, 0x29, 0x22, 0x49, 0x45, 0x9a, - 0xf5, 0xe2, 0x0b, 0x52, 0xb7, 0x52, 0x3e, 0xce, 0x76, 0xbc, 0xf8, 0xc2, 0xa8, 0xd8, 0xea, 0x27, - 0x6a, 0x11, 0x47, 0xae, 0x6f, 0xe3, 0xd4, 0x8b, 0xad, 0x93, 0xa8, 0xd1, 0xa0, 0x95, 0x59, 0x95, - 0xb0, 0xa1, 0x75, 0x12, 0x89, 0x4f, 0xa0, 0x66, 0xb1, 0xd5, 0x82, 0xeb, 0x7d, 0x33, 0x7b, 0x3a, - 0x94, 0xb1, 0x67, 0x18, 0x55, 0x2b, 0x63, 0xdc, 0xf8, 0x1c, 0x84, 0xf2, 0x79, 0xca, 0xd8, 0xd2, - 0x6f, 0xcd, 0xcd, 0xc6, 0x75, 0xe9, 0xf4, 0x94, 0x58, 0xd2, 0xef, 0x82, 0x14, 0x05, 0x98, 0x1d, - 0xbf, 0x46, 0x15, 0x02, 0x06, 0x11, 0x23, 0xfe, 0x1c, 0xd6, 0xf2, 0xe6, 0x9a, 0xdb, 0x0b, 0x5c, - 0x87, 0x78, 0xe3, 0xae, 0x8d, 0xb2, 0x06, 0x9c, 0x37, 0xf9, 0x2e, 0x33, 0xa9, 0x5a, 0x94, 0x91, - 0xdd, 0x63, 0x6a, 0x7e, 0x10, 0xb7, 0x14, 0x0c, 0x3b, 0x50, 0x19, 0x53, 0xe3, 0x0b, 0xd2, 0xce, - 0x92, 0x0e, 0x4c, 0x6c, 0x50, 0x46, 0xc5, 0x4d, 0xcc, 0x51, 0x3b, 0x20, 0x94, 0x83, 0x02, 0x4a, - 0xa9, 0xec, 0x7e, 0x4b, 0xca, 0x58, 0xe2, 0x4d, 0x34, 0x2b, 0xc4, 0x1a, 0xec, 0xaa, 0x9c, 0x15, - 0x6b, 0x9f, 0x40, 0x1d, 0xb7, 0xd2, 0xcc, 0x97, 0xf5, 0x6c, 0xa3, 0x0e, 0x42, 0x27, 0xfd, 0x78, - 0x6d, 0x92, 0x49, 0x89, 0x5f, 0xc0, 0x46, 0x26, 0xe7, 0xf4, 0x8c, 0x32, 0xbf, 0x49, 0x99, 0x37, - 0x67, 0x32, 0x1f, 0x9e, 0x61, 0xf6, 0xfa, 0x24, 0x97, 0x16, 0x9b, 0xb0, 0xdc, 0x89, 0xda, 0xbe, - 0x4d, 0x4a, 0x5a, 0xd9, 0xe0, 0x84, 0x78, 0x0c, 0x35, 0xb6, 0x88, 0x90, 0x61, 0x32, 0x6a, 0xbc, - 0x9d, 0x3d, 0xfe, 0x25, 0xb3, 0x08, 0x5b, 0x37, 0xab, 0x5e, 0xf2, 0x3b, 0xc2, 0x8d, 0x98, 0xcf, - 0xe6, 0xb3, 0x2c, 0xf2, 0x9d, 0xf9, 0x21, 0x27, 0xa2, 0xdd, 0x94, 0x4f, 0x1a, 0x70, 0x33, 0x9c, - 0xfa, 0x64, 0x25, 0x91, 0x39, 0x79, 0xdf, 0xa7, 0xfc, 0xf7, 0x29, 0xbf, 0x8c, 0x8e, 0x61, 0x30, - 0x19, 0xe7, 0x25, 0xde, 0x74, 0x3d, 0xcc, 0x82, 0x48, 0x2a, 0x78, 0x45, 0x99, 0x7c, 0x9a, 0x42, - 0x65, 0xbe, 0xfb, 0x53, 0xca, 0xa4, 0x93, 0x16, 0x2a, 0x53, 0x40, 0x69, 0x3a, 0x75, 0x6d, 0x52, - 0x19, 0x6b, 0x06, 0xfd, 0x46, 0xb1, 0x20, 0x74, 0x46, 0xd3, 0x30, 0x72, 0x5f, 0x3a, 0x66, 0xe4, - 0xfa, 0x67, 0xa4, 0x2c, 0x96, 0x8d, 0xb5, 0x04, 0x3a, 0x70, 0xfd, 0x33, 0x64, 0x19, 0xce, 0x45, - 0xec, 0x84, 0x3e, 0x5f, 0x02, 0x7f, 0x3f, 0xcb, 0x32, 0xda, 0x84, 0xc0, 0x75, 0x6e, 0x80, 0x93, - 0xfc, 0x9e, 0x19, 0xd9, 0x88, 0x47, 0xf6, 0xe1, 0x8f, 0x1a, 0xd9, 0x01, 0x8d, 0xec, 0xdb, 0x50, - 0x76, 0xfd, 0xd8, 0x09, 0x5f, 0x5a, 0x5e, 0xe3, 0xc3, 0x39, 0x6e, 0x9c, 0xe0, 0xc4, 0x3d, 0x58, - 0x8d, 0x3c, 0x17, 0xd7, 0x7b, 0xe3, 0xa3, 0x39, 0x32, 0x85, 0x12, 0xf7, 0xa1, 0x92, 0x84, 0x6c, - 0x69, 0x7c, 0x3c, 0x47, 0x97, 0x22, 0xc5, 0x1d, 0x28, 0x9d, 0xe3, 0x84, 0x7a, 0x34, 0x7f, 0x5c, - 0x8f, 0x70, 0xdc, 0xbe, 0x8f, 0x51, 0xf9, 0xa7, 0xed, 0xfb, 0xf1, 0xdc, 0xf6, 0xbd, 0xeb, 0x7a, - 0x1e, 0x6f, 0xdf, 0xc7, 0xf2, 0x17, 0x6e, 0x7e, 0x94, 0x03, 0x5b, 0xf2, 0xc9, 0xfc, 0xe6, 0x87, - 0xb8, 0xe7, 0x14, 0xdc, 0xa6, 0x1a, 0xd1, 0x19, 0x34, 0x1f, 0xa5, 0x7f, 0x9a, 0xed, 0xab, 0xfc, - 0xe1, 0xb4, 0x01, 0x51, 0x92, 0x16, 0xaf, 0x83, 0x3c, 0x62, 0x27, 0xed, 0xeb, 0x33, 0x0e, 0x21, - 0xc0, 0x10, 0x54, 0xbc, 0x3e, 0x82, 0x35, 0xe5, 0x3b, 0x8d, 0x9f, 0x8b, 0x1a, 0x9f, 0xcf, 0xd5, - 0x20, 0x4f, 0x20, 0x76, 0xa0, 0x76, 0x3c, 0xfd, 0xee, 0xbb, 0xcb, 0x31, 0xdb, 0x04, 0x1a, 0x4f, - 0xa8, 0x22, 0x5b, 0x6a, 0x63, 0x7d, 0x95, 0xcd, 0xc0, 0xc8, 0xe5, 0x12, 0x0f, 0x41, 0xb8, 0xc7, - 0x3c, 0x9e, 0xbb, 0x61, 0x30, 0x66, 0xbd, 0xbf, 0xf1, 0x85, 0x3c, 0x97, 0x98, 0xc3, 0x90, 0x43, - 0x8e, 0xe3, 0xdb, 0xe6, 0x38, 0x92, 0x62, 0xc2, 0xcf, 0xa8, 0x9e, 0x92, 0x79, 0x25, 0xa1, 0x9d, - 0xd4, 0x81, 0x2b, 0xd2, 0xee, 0x47, 0x2c, 0x35, 0x7c, 0x01, 0x38, 0x5d, 0x5f, 0xa6, 0x59, 0x7f, - 0xe7, 0x7b, 0xb3, 0x22, 0xad, 0xca, 0xfa, 0x04, 0xea, 0x7c, 0x1a, 0x42, 0x12, 0x19, 0x4e, 0xd1, - 0x9f, 0x67, 0x39, 0x57, 0xf6, 0x18, 0xd7, 0xa8, 0xd9, 0xd9, 0x43, 0xdd, 0xcf, 0x61, 0x3d, 0x73, - 0x18, 0x42, 0x9f, 0xfd, 0xdd, 0xec, 0x67, 0x93, 0xd3, 0x10, 0x63, 0x6d, 0xaa, 0x7e, 0xd2, 0x27, - 0xe7, 0x44, 0xec, 0xbf, 0xf6, 0x23, 0x44, 0xec, 0x37, 0x21, 0x5d, 0x91, 0xe6, 0x28, 0x76, 0x1a, - 0xbf, 0x60, 0xe6, 0x9f, 0x00, 0x5b, 0xb1, 0x23, 0x1e, 0x03, 0x58, 0x93, 0x89, 0x77, 0xc9, 0x53, - 0xf3, 0x97, 0x34, 0x35, 0x37, 0x33, 0x53, 0xb3, 0x89, 0x48, 0x9a, 0x9b, 0x15, 0x4b, 0xfd, 0x44, - 0x05, 0x97, 0x84, 0x77, 0x7b, 0xec, 0x51, 0xfb, 0x9b, 0xd9, 0xb5, 0x7d, 0x10, 0x44, 0xf1, 0xce, - 0xd8, 0xc3, 0x56, 0xc0, 0x24, 0xf9, 0x2d, 0xba, 0x70, 0x35, 0xf0, 0x4d, 0x7b, 0x3a, 0xf1, 0xdc, - 0x11, 0xf6, 0x00, 0x9f, 0x42, 0x36, 0xb6, 0xb3, 0xf7, 0x93, 0xe8, 0x8b, 0x7d, 0x7f, 0x47, 0x11, - 0xc9, 0xd3, 0xcd, 0x8d, 0x60, 0x16, 0x44, 0x06, 0x2b, 0x1a, 0x83, 0xe4, 0x6a, 0x4f, 0x8b, 0x45, - 0x03, 0x82, 0xaa, 0xbb, 0x3d, 0x4f, 0x60, 0x3d, 0xa5, 0xc2, 0x06, 0x46, 0x8d, 0x9d, 0xec, 0x4c, - 0xce, 0xdc, 0x82, 0x5c, 0x53, 0x19, 0x11, 0x16, 0x51, 0xdf, 0x05, 0x9e, 0x37, 0x9d, 0x48, 0x56, - 0xda, 0x68, 0xcb, 0xbe, 0x23, 0x20, 0x73, 0xc9, 0x8c, 0x4d, 0xcf, 0x19, 0x37, 0x76, 0xb3, 0x36, - 0x3d, 0x67, 0x8c, 0x62, 0x86, 0xf4, 0xb3, 0x7f, 0xe9, 0x3a, 0xe7, 0x51, 0xe3, 0x29, 0x69, 0x7e, - 0xf2, 0xce, 0xdf, 0x73, 0x04, 0xe1, 0xbe, 0xcf, 0x7a, 0x2c, 0x91, 0x34, 0xf6, 0xf8, 0xd2, 0x17, - 0x83, 0x90, 0x42, 0xb4, 0xa0, 0x41, 0xec, 0x5c, 0xae, 0xd5, 0x9c, 0x1f, 0x4c, 0x67, 0x6e, 0x49, - 0x5e, 0x43, 0x5a, 0x76, 0xb3, 0x39, 0xc8, 0x78, 0xc4, 0xfc, 0x12, 0x5e, 0x5f, 0x5c, 0x88, 0x0a, - 0x3b, 0xf0, 0x25, 0xc9, 0x1b, 0x37, 0x17, 0xe4, 0x96, 0xb1, 0x07, 0x7e, 0x06, 0x6b, 0x27, 0x16, - 0xf5, 0x84, 0x67, 0x8e, 0x03, 0xdb, 0x69, 0x3c, 0xcb, 0x86, 0xcb, 0xa1, 0x41, 0x7b, 0x6a, 0x61, - 0xa7, 0x78, 0xfb, 0x81, 0xed, 0x18, 0xd5, 0x93, 0x34, 0x21, 0x9e, 0x40, 0x23, 0x9d, 0x84, 0x53, - 0x1f, 0xbf, 0x8b, 0x2a, 0xb5, 0xeb, 0x8f, 0xe2, 0x46, 0x97, 0xfa, 0xf4, 0x7a, 0x82, 0x3f, 0x44, - 0xf4, 0x8e, 0xc4, 0x8a, 0x16, 0x68, 0xc4, 0x1c, 0xe4, 0x2e, 0x16, 0xb9, 0xb6, 0xd3, 0xd8, 0xcf, - 0x86, 0xc3, 0x60, 0xd6, 0x89, 0x24, 0xb4, 0x5f, 0x0d, 0x5c, 0xdb, 0x31, 0xea, 0xc7, 0xb9, 0xb4, - 0x78, 0x17, 0x36, 0x94, 0x3e, 0x62, 0x9e, 0x5a, 0xd1, 0xa9, 0x79, 0xe6, 0x5c, 0x36, 0x7a, 0xd4, - 0xe0, 0xba, 0x54, 0x46, 0xf6, 0xac, 0xe8, 0xf4, 0x99, 0x73, 0x29, 0x1e, 0xa0, 0x4e, 0x4d, 0xdb, - 0xa6, 0x1b, 0x99, 0x47, 0x56, 0x18, 0xba, 0x4e, 0xd8, 0xe8, 0xf3, 0x11, 0x1a, 0x23, 0x3a, 0xd1, - 0x36, 0x83, 0x51, 0x8f, 0xce, 0x0e, 0x86, 0x54, 0xd7, 0x0f, 0x88, 0x8f, 0x6a, 0x19, 0x77, 0x24, - 0xd6, 0xd6, 0x3f, 0x82, 0x7a, 0xd2, 0x7f, 0xec, 0x94, 0xf3, 0xab, 0xb9, 0x7d, 0xa2, 0x26, 0x3b, - 0x6d, 0x40, 0x5e, 0x39, 0xef, 0x43, 0x2d, 0xc9, 0xe1, 0xf8, 0x76, 0xc3, 0x98, 0x37, 0x1b, 0x4a, - 0x7a, 0xdc, 0x5d, 0x3e, 0x85, 0x1b, 0x3c, 0xcd, 0xf9, 0xe8, 0xe4, 0xcc, 0xb9, 0x4c, 0xa2, 0x9f, - 0x0c, 0xa8, 0xfe, 0x9b, 0x84, 0xee, 0x20, 0xf6, 0x99, 0x73, 0x19, 0x25, 0xdc, 0xf3, 0x26, 0xb9, - 0x16, 0x8c, 0xe4, 0x8d, 0x61, 0x36, 0xab, 0xcb, 0xb6, 0x0c, 0x79, 0x6c, 0x90, 0xa0, 0x95, 0xe0, - 0x0d, 0xe9, 0xae, 0x26, 0x9a, 0xb0, 0xc1, 0x76, 0xa0, 0x6c, 0x98, 0x97, 0x43, 0xaa, 0xe4, 0xb5, - 0x85, 0x0a, 0xb9, 0xb1, 0xfe, 0x72, 0xc6, 0x48, 0xf6, 0x14, 0x6e, 0x25, 0x67, 0xa5, 0xce, 0xb7, - 0x53, 0x8b, 0xae, 0x5b, 0x9d, 0x39, 0x97, 0xcc, 0x16, 0x5f, 0xcc, 0xcd, 0xee, 0x1b, 0x8a, 0xba, - 0x2d, 0x89, 0x9f, 0x39, 0x97, 0xc4, 0x1b, 0xef, 0x41, 0xdd, 0x8a, 0x82, 0x63, 0xa9, 0xc5, 0x8e, - 0x02, 0xaf, 0xf1, 0x9c, 0xc6, 0xa1, 0x86, 0x50, 0x43, 0x1d, 0xcd, 0xf6, 0xe0, 0x6a, 0x3a, 0x62, - 0x96, 0x77, 0x12, 0x84, 0x6e, 0x7c, 0x3a, 0x6e, 0xfc, 0x3a, 0x6b, 0x6d, 0xa2, 0x09, 0x95, 0x4c, - 0xfe, 0xa6, 0x22, 0x32, 0xd2, 0xb1, 0x4e, 0x60, 0xe2, 0x09, 0xdc, 0x4c, 0xcb, 0x8b, 0x83, 0x89, - 0xe9, 0x9b, 0xe7, 0x6e, 0x7c, 0x6a, 0x92, 0x5f, 0xf3, 0xef, 0xc9, 0xc8, 0x1a, 0x8a, 0x60, 0x18, - 0x4c, 0x7a, 0x2f, 0xdc, 0xf8, 0x74, 0xe8, 0x3a, 0x91, 0xd8, 0x86, 0x3b, 0x96, 0x6d, 0x4d, 0x62, - 0x5c, 0x10, 0x98, 0x31, 0x89, 0x06, 0x12, 0xf8, 0xa6, 0x33, 0x9e, 0xc4, 0x97, 0x8d, 0xbf, 0x4e, - 0xd9, 0x6f, 0x29, 0xaa, 0x61, 0x30, 0xd9, 0x95, 0x34, 0x7d, 0xbf, 0x8d, 0x14, 0xfa, 0x3f, 0x5f, - 0x86, 0xb2, 0x32, 0x56, 0x88, 0x2a, 0xac, 0x1e, 0xf6, 0x9e, 0xf5, 0xfa, 0x2f, 0x7a, 0x1c, 0x2c, - 0xaa, 0x39, 0x18, 0xb4, 0x8d, 0xa1, 0x66, 0x8b, 0x3a, 0x00, 0x85, 0x53, 0x31, 0x07, 0xad, 0x66, - 0x8f, 0x83, 0x47, 0x51, 0x10, 0x17, 0x4e, 0x2f, 0x89, 0x0d, 0x58, 0xdb, 0x3d, 0xec, 0xf1, 0x95, - 0x23, 0x02, 0x15, 0x11, 0xd4, 0xfe, 0x8a, 0xfd, 0xfe, 0x18, 0x54, 0x42, 0xd0, 0x7e, 0x73, 0xd8, - 0x36, 0x3a, 0x0a, 0xb4, 0x2c, 0xae, 0xc1, 0x46, 0x42, 0xa5, 0x4a, 0xd0, 0x56, 0x11, 0xfc, 0xbc, - 0xdd, 0x1a, 0xf6, 0x0d, 0xb3, 0xd3, 0xdb, 0x69, 0x7f, 0xc5, 0xd4, 0x65, 0xac, 0xdf, 0x81, 0xd1, - 0xff, 0xb2, 0xdd, 0x1a, 0x6a, 0x40, 0xae, 0x84, 0x4f, 0x9f, 0x6a, 0x55, 0x51, 0x83, 0xf2, 0x4e, - 0x67, 0x30, 0xec, 0xf4, 0x5a, 0x43, 0xad, 0x86, 0xd5, 0xde, 0xed, 0x74, 0x87, 0x6d, 0x43, 0x5b, - 0x13, 0x65, 0x28, 0x7d, 0xd9, 0xef, 0xf4, 0xb4, 0x3a, 0x05, 0x97, 0x69, 0xee, 0x1f, 0x74, 0xdb, - 0xda, 0x3a, 0x42, 0x07, 0x7d, 0x63, 0xa8, 0x69, 0x08, 0x7d, 0xd1, 0xe9, 0xed, 0xf4, 0x5f, 0x68, - 0x1b, 0xa2, 0x02, 0xcb, 0x87, 0x3d, 0xfc, 0xba, 0x10, 0x6b, 0x50, 0xa1, 0x9f, 0x66, 0xb3, 0xdb, - 0xd5, 0xae, 0x66, 0xfc, 0x0f, 0x37, 0x11, 0x45, 0xde, 0x8c, 0x03, 0xac, 0xc3, 0x35, 0x6c, 0x51, - 0x92, 0x24, 0xea, 0xeb, 0x58, 0xce, 0x7e, 0xa7, 0x77, 0x38, 0xd0, 0x6e, 0x20, 0x31, 0xfd, 0x24, - 0x4c, 0x03, 0xcb, 0xe9, 0xf4, 0xa8, 0x43, 0xef, 0xe0, 0xef, 0x9d, 0x76, 0xb7, 0x3d, 0x6c, 0x6b, - 0x77, 0xb1, 0x55, 0xdd, 0x7e, 0xeb, 0x99, 0xd9, 0x3f, 0xd0, 0xde, 0xc0, 0x9e, 0x3d, 0x30, 0xda, - 0xa6, 0x24, 0x7c, 0x53, 0x68, 0x50, 0xdb, 0x3d, 0xfc, 0xf5, 0xaf, 0xbf, 0x36, 0x65, 0xa3, 0xde, - 0xc2, 0x6f, 0xa6, 0x14, 0xe6, 0xe1, 0x33, 0xed, 0xed, 0x19, 0xd0, 0xe0, 0x99, 0xf6, 0x0e, 0x76, - 0x8a, 0xea, 0x6b, 0xed, 0x3e, 0x12, 0x18, 0xed, 0xd6, 0xa1, 0x31, 0xe8, 0x3c, 0x6f, 0x9b, 0xad, - 0x61, 0x5b, 0x7b, 0x97, 0x7a, 0xa1, 0xd3, 0x7b, 0xa6, 0x3d, 0xc0, 0x6a, 0xe2, 0x2f, 0xee, 0xe4, - 0xf7, 0x84, 0x80, 0x7a, 0x4a, 0x4b, 0xb0, 0xf7, 0x91, 0x64, 0xdb, 0xe8, 0x37, 0x77, 0x5a, 0xcd, - 0xc1, 0x50, 0xfb, 0x00, 0xdb, 0x38, 0x38, 0xe8, 0x76, 0x86, 0xda, 0x43, 0x6c, 0xc8, 0xd3, 0xe6, - 0x70, 0xaf, 0x6d, 0x68, 0x1f, 0x8a, 0x75, 0xa8, 0x0e, 0x3b, 0xfb, 0x6d, 0x53, 0xf6, 0xe9, 0x23, - 0xfc, 0xc6, 0x6e, 0xa7, 0xdb, 0xd5, 0x1e, 0x93, 0xff, 0x5c, 0xd3, 0x18, 0x76, 0x68, 0x7c, 0x3f, - 0xc1, 0x02, 0x9a, 0x07, 0x07, 0xdd, 0xaf, 0xb5, 0x4f, 0xb1, 0x81, 0xfb, 0x87, 0xdd, 0x61, 0xc7, - 0x3c, 0x3c, 0xd8, 0x69, 0x0e, 0xdb, 0xda, 0x67, 0x34, 0xca, 0xfd, 0xc1, 0x70, 0x67, 0xbf, 0xab, - 0x7d, 0x4e, 0x65, 0xd2, 0x4c, 0x6b, 0x75, 0xfb, 0xbd, 0xb6, 0xf6, 0x04, 0xe9, 0x9b, 0x3b, 0xcd, - 0x83, 0x21, 0xd6, 0x6e, 0xd8, 0x3f, 0xd0, 0xbe, 0xd0, 0x4b, 0xe5, 0x15, 0x6d, 0x45, 0x2f, 0x95, - 0xb7, 0xb4, 0x2d, 0xbd, 0x54, 0xd6, 0x35, 0xfd, 0x41, 0x75, 0xd0, 0x3f, 0x34, 0x5a, 0x5c, 0x7b, - 0xfd, 0x6f, 0x16, 0xa0, 0xac, 0xac, 0x5a, 0xf8, 0xe1, 0x4e, 0xaf, 0xd7, 0x36, 0xb4, 0x2b, 0x58, - 0xb9, 0x6e, 0x7b, 0x77, 0xa8, 0x15, 0xc8, 0xfd, 0xb0, 0xf3, 0x74, 0x6f, 0xa8, 0x2d, 0xe1, 0xcf, - 0xfe, 0x21, 0xf6, 0x73, 0x91, 0x3a, 0xa8, 0xbd, 0xdf, 0xd1, 0x4a, 0xf8, 0xab, 0xd9, 0x1b, 0x76, - 0xb4, 0x65, 0x9a, 0x46, 0x9d, 0xde, 0xd3, 0x6e, 0x5b, 0x5b, 0x41, 0xe8, 0x7e, 0xd3, 0x78, 0xa6, - 0xad, 0x72, 0xa1, 0x3b, 0xed, 0xaf, 0xb4, 0xb2, 0x58, 0x81, 0xa5, 0xee, 0x23, 0xad, 0x82, 0xa0, - 0x9d, 0xf6, 0xce, 0xe1, 0x81, 0x06, 0x94, 0x7b, 0xd0, 0xdf, 0xd5, 0xaa, 0xd8, 0x09, 0xf8, 0xcb, - 0xa4, 0xcf, 0xd6, 0xf4, 0xfb, 0xb0, 0xda, 0x3c, 0x39, 0xa1, 0xbd, 0x0d, 0x3b, 0xea, 0xb0, 0xdb, - 0xe5, 0x55, 0xb7, 0xdd, 0x1f, 0x0e, 0xfb, 0xfb, 0x5a, 0x01, 0x67, 0x38, 0xb6, 0x70, 0x49, 0xef, - 0x40, 0x59, 0x09, 0xf6, 0x99, 0xc0, 0x4a, 0x65, 0x28, 0x1d, 0x18, 0xed, 0xe7, 0xec, 0x4c, 0xdb, - 0x6b, 0x7f, 0x85, 0xf5, 0xc7, 0x5f, 0x58, 0x50, 0x11, 0x6b, 0xc0, 0x11, 0x90, 0x28, 0xb2, 0x52, - 0xb7, 0xd3, 0x6b, 0x37, 0x0d, 0x6d, 0x59, 0x7f, 0x02, 0xd5, 0xcc, 0x0e, 0x8b, 0x13, 0xe3, 0x69, - 0xf3, 0x00, 0x27, 0x57, 0xd7, 0x94, 0xc5, 0x5e, 0x07, 0x91, 0x80, 0xd2, 0x31, 0x2b, 0xe8, 0x9f, - 0xc2, 0xc6, 0x9c, 0x40, 0x45, 0x15, 0x6f, 0x76, 0x64, 0xc5, 0x3b, 0x4f, 0x7b, 0x7d, 0xa3, 0xcd, - 0x41, 0x9e, 0xe4, 0x68, 0x2e, 0xe9, 0xef, 0x41, 0x25, 0x91, 0xfc, 0x70, 0x76, 0xb7, 0x8c, 0xfe, - 0x60, 0xc0, 0x83, 0x7f, 0x05, 0xd3, 0xd4, 0xdd, 0x9c, 0x2e, 0xe8, 0x2e, 0xd4, 0xf3, 0xdb, 0xb0, - 0xd8, 0x82, 0xdb, 0x3c, 0xff, 0xb7, 0x0f, 0x3b, 0xdd, 0x1d, 0x73, 0xd0, 0xd9, 0x69, 0xcf, 0xdc, - 0x1e, 0xbf, 0x05, 0xd7, 0xe7, 0x28, 0x38, 0xc2, 0x54, 0x41, 0xdc, 0x84, 0x6b, 0x73, 0x38, 0x9a, - 0xf5, 0x4b, 0xfa, 0x01, 0x88, 0x79, 0x06, 0x2d, 0x6e, 0x43, 0x23, 0x69, 0xb3, 0xd9, 0xec, 0x3e, - 0xed, 0x1b, 0x9d, 0xe1, 0xde, 0xbe, 0x49, 0xfc, 0xe2, 0xca, 0xab, 0xb0, 0x7b, 0xcd, 0xc1, 0x9e, - 0x56, 0xf8, 0xb2, 0x54, 0xbe, 0xab, 0x6d, 0x7d, 0x59, 0x2a, 0xdf, 0xd3, 0xde, 0xd2, 0x07, 0xb0, - 0xa1, 0xa4, 0x67, 0xba, 0x9b, 0x4f, 0x66, 0x9d, 0x4d, 0x58, 0xee, 0x3a, 0x2f, 0x1d, 0x4f, 0x5d, - 0xe5, 0xa6, 0x04, 0x42, 0xfb, 0x47, 0xdf, 0x74, 0x92, 0x80, 0x88, 0x94, 0x40, 0x75, 0xb9, 0x97, - 0x89, 0xc9, 0x48, 0xe1, 0x14, 0xfe, 0x8d, 0x02, 0x94, 0x13, 0x99, 0xfc, 0x1e, 0x2c, 0x0d, 0x07, - 0xf2, 0x40, 0x68, 0xf3, 0x61, 0x1a, 0x80, 0x77, 0xa8, 0x7e, 0x19, 0x4b, 0xc3, 0x81, 0x78, 0x1f, - 0x56, 0x38, 0x2c, 0x9c, 0x3c, 0x02, 0xda, 0xcc, 0xcb, 0xf9, 0x43, 0xc2, 0x19, 0x92, 0x46, 0x7c, - 0x0a, 0x95, 0xa4, 0xb6, 0xd2, 0x1a, 0x7d, 0x23, 0x9f, 0x21, 0x41, 0x1b, 0x29, 0xa5, 0xde, 0x85, - 0x7a, 0xbe, 0x40, 0x71, 0x07, 0x80, 0x8b, 0xcc, 0x9c, 0xdd, 0x66, 0x20, 0xe2, 0x16, 0xa8, 0x68, - 0x75, 0x3b, 0x54, 0xb1, 0xb5, 0x24, 0x7a, 0xdd, 0x8e, 0xfe, 0xef, 0x2e, 0x03, 0xa4, 0x5a, 0x3d, - 0x76, 0x44, 0x62, 0x63, 0x5f, 0x96, 0x5e, 0xbe, 0xaf, 0x41, 0xc5, 0x0b, 0x2c, 0x3b, 0x1b, 0x48, - 0xb7, 0x8c, 0x00, 0x9a, 0x63, 0xd9, 0x78, 0x33, 0x15, 0x19, 0x3a, 0xe6, 0x3a, 0xac, 0x1c, 0x07, - 0xe1, 0xd8, 0x8a, 0xe5, 0xbd, 0x7d, 0x99, 0xa2, 0x90, 0x32, 0xe4, 0xa4, 0x66, 0x9b, 0x9e, 0xeb, - 0x3b, 0x1c, 0x38, 0xab, 0x64, 0xd4, 0x24, 0xb0, 0x8b, 0x30, 0x14, 0xae, 0x1d, 0x7f, 0xe4, 0x05, - 0x28, 0xd6, 0x1c, 0xf1, 0xad, 0x85, 0x9a, 0x01, 0x0a, 0xb4, 0x7d, 0xc9, 0xad, 0x0d, 0xc7, 0xae, - 0x6f, 0xc5, 0xd2, 0x7b, 0x87, 0x5a, 0xab, 0x20, 0x58, 0xdd, 0x6f, 0xa2, 0x40, 0x9a, 0xdc, 0x39, - 0xa0, 0x49, 0x19, 0x01, 0x54, 0xdd, 0xd7, 0x01, 0x9c, 0x68, 0x64, 0x4d, 0xb8, 0xf0, 0x0a, 0x47, - 0x72, 0x93, 0x90, 0xed, 0x4b, 0xd1, 0x85, 0xfa, 0xf0, 0x08, 0x95, 0x89, 0x60, 0xc7, 0x8a, 0xad, - 0x56, 0xe0, 0x49, 0x63, 0xf8, 0xbd, 0x59, 0xf3, 0xc7, 0xc3, 0x3c, 0x19, 0xdf, 0x9c, 0x9b, 0xc9, - 0x2b, 0x3e, 0x81, 0x9a, 0x3b, 0x72, 0x8e, 0x9c, 0xf0, 0x84, 0x05, 0xad, 0x6a, 0xee, 0xc2, 0x3d, - 0x63, 0x48, 0xc8, 0xaa, 0xba, 0x69, 0x02, 0x95, 0xb4, 0x71, 0xe0, 0x9f, 0x04, 0xf6, 0x11, 0xe7, - 0xaa, 0x65, 0x0d, 0x7b, 0xfb, 0x88, 0xe1, 0x3c, 0x92, 0x88, 0xf2, 0xfc, 0x2e, 0xac, 0x63, 0xcf, - 0x47, 0x71, 0xe8, 0x58, 0x63, 0xce, 0xb6, 0x96, 0x9d, 0x81, 0x58, 0xa3, 0x01, 0x21, 0x29, 0x6f, - 0x3d, 0x25, 0xa6, 0xec, 0x9f, 0xa4, 0xd7, 0xd9, 0x29, 0x6f, 0x3d, 0xe7, 0x55, 0xcf, 0x18, 0xfe, - 0xe8, 0x71, 0x9a, 0x10, 0x0f, 0x01, 0xce, 0xac, 0xe3, 0x33, 0x8b, 0xf3, 0xac, 0x67, 0xab, 0xf9, - 0x0c, 0xe1, 0x94, 0xa3, 0x72, 0xa6, 0x7e, 0xde, 0x6a, 0xc2, 0xd5, 0x05, 0xbd, 0xf6, 0x93, 0xae, - 0x81, 0xfd, 0xdd, 0x02, 0xd4, 0xf3, 0x6d, 0x99, 0x89, 0x8a, 0x59, 0x49, 0xa2, 0x62, 0x0a, 0x28, - 0x4d, 0x82, 0x50, 0xdd, 0xd8, 0xa0, 0xdf, 0x69, 0x90, 0xea, 0x62, 0x36, 0x48, 0x75, 0x03, 0x56, - 0x43, 0x87, 0x6f, 0x5f, 0x73, 0xa4, 0x06, 0x95, 0xc4, 0x09, 0x3b, 0x99, 0x46, 0xa7, 0x8e, 0xad, - 0xb4, 0x49, 0xbe, 0x46, 0x50, 0x63, 0xa0, 0xd4, 0x26, 0x6f, 0xc0, 0xaa, 0x35, 0x71, 0x49, 0x41, - 0x91, 0x11, 0x16, 0xad, 0x89, 0xfb, 0xcc, 0xb9, 0xd4, 0xbf, 0x83, 0x6a, 0xa6, 0xef, 0xb0, 0x42, - 0x67, 0xae, 0xaf, 0x22, 0x4b, 0xd0, 0x6f, 0xac, 0xbc, 0x0c, 0x2a, 0xc8, 0x77, 0x41, 0x64, 0x0a, - 0x3f, 0xac, 0xae, 0xf1, 0xd1, 0xe9, 0xac, 0x3a, 0xea, 0x97, 0xc0, 0x5f, 0x21, 0x0c, 0x97, 0x35, - 0x56, 0xc4, 0x0e, 0xce, 0x7d, 0x59, 0xf1, 0x24, 0xad, 0xff, 0x17, 0x4b, 0x50, 0x49, 0x06, 0x01, - 0x5b, 0x78, 0x14, 0x06, 0x67, 0x4e, 0x18, 0xc9, 0xaf, 0xab, 0x24, 0xf5, 0x48, 0x30, 0x71, 0x47, - 0xea, 0x52, 0x34, 0x25, 0xc4, 0xed, 0xec, 0x15, 0x48, 0x8e, 0x56, 0x92, 0xb9, 0xf1, 0x78, 0x07, - 0xc0, 0x9a, 0xc6, 0xc1, 0x28, 0x18, 0x8f, 0xdd, 0x58, 0x7e, 0x39, 0x03, 0xc1, 0x32, 0x49, 0x45, - 0x93, 0xbd, 0xc5, 0x89, 0x0c, 0x53, 0x58, 0xc9, 0x31, 0x85, 0xdb, 0x50, 0x89, 0x9c, 0x89, 0x15, - 0x5a, 0x71, 0x10, 0xca, 0xd5, 0x9c, 0x02, 0x28, 0xb0, 0x25, 0x6a, 0x56, 0x2a, 0x6e, 0x5d, 0xd1, - 0x58, 0xa5, 0x74, 0xc7, 0x16, 0x5b, 0x74, 0x15, 0xc9, 0x4c, 0xd0, 0xec, 0x87, 0x0d, 0xa7, 0x56, - 0x34, 0x90, 0x14, 0x02, 0x4a, 0xe4, 0x13, 0xc3, 0x5e, 0x78, 0xf4, 0x5b, 0xbc, 0x03, 0x14, 0x3e, - 0x3d, 0x98, 0xc6, 0x66, 0xe4, 0x8c, 0x02, 0xdf, 0x8e, 0xa4, 0x1f, 0x5e, 0x5d, 0x82, 0x07, 0x0c, - 0xd5, 0xff, 0xdb, 0x25, 0xa8, 0x66, 0xd6, 0x28, 0xb9, 0x20, 0x59, 0x13, 0x3a, 0xf9, 0x4a, 0xee, - 0x6f, 0x57, 0x24, 0x84, 0x43, 0x84, 0x8c, 0xac, 0xd8, 0xf2, 0x82, 0x93, 0x4c, 0xf0, 0x13, 0x09, - 0xe9, 0xd8, 0xe4, 0x15, 0x68, 0x8d, 0x1d, 0x0e, 0x67, 0x2e, 0x2f, 0xef, 0x27, 0x80, 0x74, 0x5e, - 0x96, 0xb2, 0xf3, 0x52, 0x63, 0xf7, 0x51, 0xee, 0x45, 0xf2, 0x16, 0xbd, 0x0b, 0x55, 0x65, 0x49, - 0xc2, 0xaf, 0x48, 0x0f, 0x47, 0x05, 0xea, 0xd8, 0xe2, 0x6d, 0x48, 0x83, 0xc3, 0x9b, 0x56, 0x64, - 0x06, 0xc7, 0xea, 0xde, 0x5c, 0x02, 0x6e, 0x46, 0xfd, 0xe3, 0xc4, 0xed, 0x88, 0xac, 0x02, 0x92, - 0x47, 0x22, 0x80, 0xa4, 0x94, 0x87, 0x70, 0x55, 0x1e, 0x61, 0xd2, 0xc4, 0x77, 0x3c, 0xdb, 0x74, - 0x6d, 0xf6, 0x9f, 0x5e, 0x36, 0x36, 0x12, 0xd4, 0x2e, 0x62, 0x3a, 0x36, 0xd9, 0x5c, 0xa4, 0x02, - 0x6e, 0xbb, 0x27, 0x4e, 0x14, 0xcb, 0x5b, 0x5e, 0x35, 0x06, 0xee, 0x10, 0x4c, 0xff, 0xbf, 0x57, - 0xa0, 0x92, 0x30, 0xaf, 0x1f, 0xea, 0xcc, 0x37, 0x61, 0x6d, 0x14, 0xf8, 0x3e, 0x7b, 0x0a, 0xa4, - 0xfd, 0x59, 0x4b, 0x81, 0x74, 0x4f, 0x57, 0x64, 0x88, 0xd4, 0x2d, 0x74, 0x8e, 0xd5, 0xb1, 0x91, - 0x62, 0x9e, 0xcb, 0xfb, 0xe8, 0xb7, 0xa0, 0x8c, 0x5c, 0xef, 0xc8, 0x8a, 0x54, 0x37, 0x27, 0x69, - 0x9c, 0xd1, 0xa3, 0xc0, 0xf3, 0xa4, 0x27, 0x83, 0x0c, 0x83, 0x9b, 0x42, 0xc4, 0x23, 0x0a, 0x84, - 0x4c, 0x8e, 0x00, 0x2b, 0xb4, 0x1f, 0x34, 0x32, 0xdc, 0x98, 0x7d, 0x01, 0xf6, 0xb9, 0xf2, 0x86, - 0x22, 0x14, 0xbf, 0x00, 0x4d, 0xf2, 0x8e, 0x49, 0xe8, 0xd8, 0x24, 0xc5, 0xc9, 0x4b, 0x0a, 0x9b, - 0x99, 0xcc, 0x07, 0x0a, 0x67, 0xac, 0x33, 0x75, 0x02, 0xc0, 0x99, 0x9a, 0x0e, 0xc3, 0xc4, 0x8a, - 0x4f, 0xd9, 0xf5, 0xbd, 0x62, 0xd4, 0x13, 0xf0, 0x01, 0x42, 0xc5, 0x27, 0x70, 0x3d, 0x74, 0x22, - 0xd7, 0x9e, 0x5a, 0x9e, 0x99, 0x1f, 0x08, 0x0e, 0xaa, 0xbb, 0xa9, 0xb0, 0xbb, 0x99, 0x01, 0xc1, - 0xe2, 0xc7, 0xd6, 0x05, 0x1d, 0x02, 0x79, 0x9e, 0xe3, 0xb9, 0xd1, 0x58, 0x46, 0x64, 0xac, 0x8f, - 0xad, 0x8b, 0x83, 0x14, 0x4a, 0x37, 0x92, 0x26, 0x9e, 0x3b, 0x13, 0xe5, 0x8f, 0xc7, 0x12, 0xe1, - 0x06, 0xa3, 0x85, 0x0e, 0x6b, 0x58, 0x20, 0xd9, 0x3b, 0x13, 0x67, 0xf6, 0xa2, 0x51, 0x1d, 0x5b, - 0x6c, 0x49, 0x08, 0xce, 0xe9, 0x5a, 0x7e, 0x42, 0xc3, 0xfe, 0xb3, 0xd2, 0x85, 0x55, 0x12, 0xb1, - 0x0b, 0x2d, 0x55, 0x8d, 0x67, 0x87, 0x1a, 0xd6, 0x3a, 0xbb, 0x0f, 0x49, 0xb0, 0x1a, 0xd3, 0x9b, - 0xc0, 0x47, 0xa9, 0x38, 0x45, 0xd6, 0x89, 0x62, 0x95, 0xd2, 0xbc, 0x12, 0xa6, 0x91, 0x13, 0x32, - 0xfb, 0x34, 0x89, 0xf1, 0x72, 0xa4, 0xbf, 0x35, 0x04, 0x13, 0x03, 0x7d, 0x86, 0x1c, 0xf8, 0x3e, - 0x68, 0x44, 0xa7, 0x4e, 0x5e, 0xa2, 0xc0, 0xa7, 0x53, 0xdb, 0x9a, 0x51, 0x47, 0xb8, 0x3c, 0x57, - 0x89, 0x02, 0x5f, 0x7c, 0x0e, 0x0d, 0xa2, 0x9c, 0xb8, 0x13, 0x07, 0xc5, 0x17, 0xe4, 0x3c, 0x27, - 0x0e, 0xe7, 0x10, 0x5b, 0xc5, 0xfb, 0x35, 0xe3, 0x1a, 0xe2, 0x0f, 0x24, 0x7a, 0x80, 0x58, 0xca, - 0xf8, 0x00, 0x36, 0x32, 0x55, 0x91, 0x43, 0xc3, 0x71, 0xc3, 0xd6, 0x93, 0xca, 0xc8, 0x51, 0xf9, - 0x08, 0x36, 0xa5, 0x8f, 0x89, 0x24, 0xe7, 0xe9, 0x44, 0x07, 0xb7, 0x65, 0x43, 0x48, 0x1c, 0xe5, - 0xe0, 0x79, 0x27, 0xde, 0x80, 0x1a, 0x59, 0x1f, 0x64, 0x84, 0x4d, 0x19, 0x3b, 0xac, 0x4a, 0x30, - 0x0e, 0x60, 0xa7, 0xff, 0x8d, 0x02, 0x88, 0xf9, 0xa9, 0xfa, 0xaa, 0x68, 0x38, 0x38, 0xd5, 0x54, - 0x28, 0x0a, 0xfc, 0x2d, 0xde, 0x85, 0xd5, 0x71, 0xc0, 0xe2, 0x54, 0xf1, 0x15, 0xb1, 0xe8, 0x56, - 0xc6, 0x01, 0x89, 0x57, 0xef, 0xc0, 0xfa, 0x28, 0xf0, 0xe5, 0xa0, 0x31, 0x77, 0xe1, 0xb5, 0x56, - 0x4f, 0xc1, 0xc8, 0x63, 0xf4, 0xff, 0xbc, 0x00, 0xf5, 0xfc, 0x02, 0x10, 0x6f, 0x53, 0x3c, 0xee, - 0x5c, 0x44, 0xef, 0x3c, 0x45, 0x7f, 0x42, 0x71, 0xba, 0x17, 0x55, 0xf1, 0x75, 0x00, 0x12, 0x12, - 0x78, 0x34, 0x64, 0x80, 0x5e, 0x82, 0xd0, 0x08, 0xdc, 0x85, 0x2a, 0x3f, 0x38, 0xc1, 0xf8, 0x12, - 0x8d, 0x16, 0xe7, 0x88, 0x88, 0xe0, 0xa3, 0x8c, 0xc7, 0x07, 0x7b, 0x74, 0x2c, 0x5e, 0xa4, 0x09, - 0x95, 0xfe, 0xf7, 0x0b, 0x00, 0xe9, 0x1a, 0xc0, 0x99, 0x78, 0xe6, 0x5c, 0xd2, 0x32, 0x55, 0x5b, - 0xec, 0x99, 0x73, 0x79, 0x20, 0xeb, 0x26, 0xdd, 0x0e, 0x23, 0x79, 0xe7, 0xb3, 0x66, 0x54, 0xd8, - 0xd3, 0x30, 0xe2, 0x20, 0x4b, 0xd2, 0xd1, 0x30, 0x53, 0x75, 0xf6, 0x2d, 0x8c, 0x28, 0x8c, 0xc6, - 0x3a, 0xe7, 0xa6, 0xa1, 0x8f, 0xdc, 0x97, 0x2a, 0x9c, 0x14, 0x3b, 0x46, 0x76, 0x14, 0x14, 0x09, - 0xb9, 0x9c, 0x94, 0x90, 0x2f, 0x61, 0xb1, 0x1b, 0x64, 0x42, 0xa8, 0x7b, 0x4a, 0xe0, 0x6f, 0xc6, - 0x31, 0x6d, 0xb0, 0x89, 0x29, 0xbe, 0xa0, 0x62, 0x48, 0xb1, 0x15, 0xfe, 0x35, 0xba, 0xab, 0x2a, - 0xc3, 0x47, 0x48, 0xb9, 0x3f, 0x09, 0x0b, 0xf1, 0x36, 0x8e, 0xb4, 0xa7, 0xb6, 0x87, 0xc4, 0x8f, - 0x6d, 0xd9, 0x58, 0x1b, 0x05, 0x1e, 0x6f, 0x0d, 0x08, 0xd4, 0xff, 0xcd, 0x65, 0x80, 0xf4, 0x10, - 0x36, 0xb7, 0x62, 0x0b, 0xf9, 0x15, 0xfb, 0x08, 0xae, 0xcb, 0xf8, 0x7e, 0xc9, 0x85, 0x2c, 0xd7, - 0x37, 0x8f, 0x2c, 0x25, 0xc2, 0x09, 0x89, 0xe5, 0x6b, 0x34, 0x1d, 0x7f, 0xdb, 0x8a, 0xc5, 0x13, - 0xe4, 0x91, 0x69, 0x9e, 0xf8, 0x72, 0xf2, 0xca, 0x29, 0xba, 0x96, 0x66, 0x1f, 0x5e, 0x4e, 0xc4, - 0x47, 0x14, 0x26, 0x29, 0x74, 0xa2, 0x53, 0x33, 0x8e, 0xb2, 0x1f, 0xe3, 0x5b, 0xf1, 0x1b, 0x12, - 0x39, 0x8c, 0x92, 0x6f, 0x7d, 0x04, 0xd7, 0x24, 0x93, 0x98, 0xa9, 0x1e, 0xbb, 0x4b, 0x4a, 0x23, - 0x74, 0xb6, 0x76, 0x34, 0xf2, 0x74, 0x32, 0xad, 0x1e, 0x46, 0x29, 0xe3, 0xc8, 0x63, 0x67, 0xc8, - 0x20, 0xd8, 0x74, 0xbc, 0x2c, 0x25, 0x17, 0x4e, 0x08, 0x1d, 0x4a, 0xb8, 0x42, 0x88, 0x19, 0xd7, - 0x1f, 0xd5, 0x1f, 0xd2, 0xc3, 0x2f, 0x14, 0xc0, 0x3c, 0xb0, 0x1d, 0x83, 0x70, 0xe2, 0x03, 0xdc, - 0xa1, 0xd3, 0x66, 0xab, 0xe8, 0xfd, 0x55, 0x69, 0xc7, 0x4e, 0x1a, 0x6a, 0x70, 0x1c, 0xff, 0xf7, - 0x40, 0x64, 0x6a, 0xae, 0xa8, 0x6b, 0x44, 0xbd, 0x9e, 0x54, 0x5b, 0x12, 0xbf, 0x03, 0x54, 0xc5, - 0xf4, 0x6a, 0xd1, 0xcc, 0x59, 0x2c, 0x22, 0xe5, 0x75, 0xa4, 0x6b, 0x69, 0xeb, 0x4c, 0x2b, 0x36, - 0xe3, 0x53, 0x87, 0x8c, 0xde, 0x7c, 0xb7, 0x68, 0x23, 0x69, 0x68, 0x33, 0x1e, 0x9e, 0x3a, 0x6d, - 0xe2, 0xb5, 0x89, 0xb7, 0xcb, 0xfa, 0xf7, 0x7b, 0xbb, 0x7c, 0x06, 0x8d, 0xfc, 0x79, 0x47, 0xa6, - 0xbb, 0x99, 0x8d, 0x6f, 0x66, 0xef, 0x6c, 0x25, 0x3d, 0xfe, 0x00, 0x36, 0x50, 0x26, 0xcc, 0xe5, - 0x95, 0x8e, 0x69, 0xeb, 0xa7, 0x56, 0x94, 0x3d, 0x20, 0xd1, 0xff, 0xd1, 0x0a, 0xd4, 0xf3, 0xc7, - 0xd2, 0x1c, 0x4f, 0x8d, 0xf7, 0xf9, 0x02, 0x49, 0x3b, 0xc9, 0x6e, 0xfe, 0x1a, 0x54, 0xf8, 0x62, - 0x17, 0x32, 0x63, 0x15, 0x7a, 0xfa, 0x4c, 0xb2, 0xe0, 0x77, 0x61, 0x75, 0x72, 0xf6, 0x03, 0x0c, - 0x72, 0xc2, 0x61, 0x4f, 0xde, 0x85, 0xd5, 0xe9, 0x59, 0x1a, 0x22, 0x7c, 0x21, 0xe9, 0xf4, 0x4c, - 0x7a, 0x3a, 0xdf, 0x94, 0x07, 0xea, 0xf2, 0xd6, 0xd7, 0x78, 0xea, 0xc5, 0xae, 0x49, 0x47, 0x02, - 0x72, 0x69, 0x5f, 0x67, 0x82, 0x0e, 0xe1, 0x29, 0x72, 0x02, 0x1d, 0x62, 0x22, 0xbf, 0x93, 0xc1, - 0x96, 0x12, 0x99, 0x65, 0xd9, 0x00, 0x0e, 0xae, 0x44, 0xcd, 0x79, 0x17, 0x34, 0xd4, 0x34, 0x3c, - 0x77, 0x14, 0x27, 0x54, 0xab, 0x44, 0xb5, 0xae, 0xe0, 0x8a, 0xf4, 0x2d, 0xa8, 0xcb, 0xbb, 0x39, - 0x8a, 0x90, 0xc3, 0x01, 0xac, 0x31, 0x54, 0x91, 0x7d, 0x06, 0x37, 0x02, 0xfb, 0x6c, 0xaa, 0xae, - 0x99, 0x59, 0xe1, 0x91, 0x1b, 0x87, 0x56, 0x12, 0x67, 0xae, 0x6c, 0x5c, 0x43, 0x34, 0x73, 0x81, - 0x66, 0x8a, 0x24, 0x5f, 0x45, 0xce, 0xa2, 0x8a, 0x07, 0xaa, 0xc7, 0x1a, 0x43, 0x55, 0xf1, 0x9f, - 0xc0, 0xf5, 0x24, 0xfe, 0x89, 0x33, 0x76, 0xfc, 0xd8, 0x0c, 0x1d, 0x72, 0xf5, 0x92, 0x51, 0xfe, - 0x37, 0x55, 0x18, 0x14, 0x42, 0x1a, 0x8c, 0x13, 0x8f, 0xe0, 0xda, 0x4c, 0x2e, 0x39, 0x82, 0x3c, - 0xf7, 0xaf, 0xe6, 0x32, 0xc9, 0xc1, 0x6c, 0xc3, 0xdd, 0x99, 0x3c, 0xb9, 0x68, 0x9e, 0x98, 0x7b, - 0x8d, 0x72, 0xdf, 0xce, 0xe5, 0xce, 0x46, 0xad, 0xc4, 0x62, 0x3e, 0x87, 0xc6, 0x4c, 0x31, 0x38, - 0x22, 0xcc, 0x28, 0x39, 0x6a, 0xe9, 0xb5, 0x5c, 0xfe, 0x67, 0xce, 0x25, 0x33, 0xd6, 0x5f, 0xc0, - 0xed, 0x99, 0x8c, 0xb9, 0xee, 0x97, 0xd7, 0xf0, 0x6e, 0xe6, 0x32, 0xf7, 0x33, 0x43, 0x81, 0x53, - 0x15, 0x3f, 0xc5, 0xc1, 0x11, 0x35, 0x92, 0x18, 0x71, 0x6f, 0xea, 0x51, 0x7c, 0xc4, 0x77, 0x18, - 0xc9, 0x67, 0xaa, 0x1b, 0xb3, 0x67, 0xaa, 0x44, 0xc8, 0x07, 0xa9, 0x6f, 0xc3, 0xba, 0x22, 0xe4, - 0x93, 0xa5, 0x88, 0x84, 0x9c, 0x65, 0x63, 0x4d, 0x92, 0xd0, 0x81, 0x52, 0xa4, 0xff, 0xd1, 0x32, - 0xd4, 0xb2, 0x3e, 0x3f, 0x3f, 0xe6, 0xd2, 0xdb, 0x4f, 0xba, 0x32, 0x2a, 0xd5, 0x3c, 0xea, 0x13, - 0x5c, 0xcd, 0xc5, 0x44, 0xcd, 0x6b, 0x4e, 0x63, 0x94, 0x68, 0x48, 0xf5, 0x0a, 0x3c, 0x15, 0x21, - 0x88, 0xf9, 0x37, 0xee, 0x5c, 0x32, 0x38, 0xd0, 0x47, 0x32, 0x00, 0x19, 0x85, 0x84, 0xa4, 0xe0, - 0x0c, 0xf3, 0x3e, 0xd0, 0x35, 0x15, 0x11, 0x92, 0x82, 0x51, 0x3c, 0x82, 0xf5, 0x34, 0xdc, 0x13, - 0x67, 0x59, 0x99, 0xcb, 0xb2, 0x96, 0xc4, 0x7a, 0x92, 0x41, 0xc6, 0xd7, 0xdc, 0xc8, 0x0c, 0x3c, - 0x5b, 0x85, 0xed, 0x5b, 0x55, 0x1e, 0x99, 0x7d, 0xcf, 0x96, 0xa1, 0xf9, 0x98, 0xc6, 0x77, 0xce, - 0x15, 0x4d, 0xe2, 0xb5, 0xd9, 0x73, 0xce, 0x25, 0xcd, 0x43, 0xb8, 0x8a, 0xcd, 0x95, 0x6b, 0x82, - 0x83, 0x75, 0x04, 0xa1, 0x5c, 0x43, 0xc8, 0xdc, 0x78, 0x09, 0x0d, 0x24, 0x22, 0x73, 0x5d, 0x33, - 0x0c, 0xce, 0x4d, 0x7f, 0x3a, 0x3e, 0x62, 0x36, 0x2f, 0x85, 0x79, 0x79, 0x5d, 0xd3, 0x08, 0xce, - 0x7b, 0x84, 0xc2, 0xfe, 0x7a, 0x00, 0x1b, 0x6a, 0x95, 0x8e, 0xe8, 0x34, 0x4b, 0xdd, 0x38, 0x5e, - 0x36, 0xd6, 0x19, 0xd1, 0x24, 0x38, 0xd2, 0xbe, 0x9b, 0xd0, 0xf2, 0xc5, 0x51, 0xa2, 0xe5, 0xd5, - 0x53, 0x8f, 0xd3, 0x5b, 0xa3, 0x48, 0xda, 0x82, 0x3b, 0x71, 0x68, 0x8d, 0xce, 0xcc, 0x57, 0x2d, - 0x1f, 0x79, 0x51, 0xf5, 0x35, 0xa2, 0x6a, 0x2e, 0x5c, 0x3c, 0x3f, 0x66, 0xf5, 0xd5, 0x7f, 0x78, - 0xf5, 0xe9, 0x7f, 0x58, 0x82, 0x8d, 0x39, 0x67, 0x29, 0x54, 0xa8, 0xd3, 0x57, 0xc2, 0xf0, 0x27, - 0x0a, 0xcf, 0x63, 0x2b, 0x1e, 0x9d, 0xa2, 0x8e, 0x76, 0xec, 0x5e, 0xa8, 0x17, 0xd2, 0x08, 0x76, - 0x40, 0x20, 0xe4, 0xa5, 0x2c, 0x57, 0xb1, 0x3b, 0x29, 0x0b, 0x39, 0x2c, 0xb2, 0xb1, 0x6f, 0xb6, - 0x0a, 0xf9, 0x51, 0x7a, 0x45, 0xc8, 0x8f, 0x5b, 0x50, 0xf1, 0x03, 0xba, 0x46, 0x3c, 0x39, 0x93, - 0x7c, 0x7b, 0xd5, 0x0f, 0xe2, 0xbe, 0x7f, 0x70, 0x86, 0x0c, 0x6a, 0x1a, 0xd1, 0x45, 0xb0, 0x23, - 0x27, 0x8c, 0x4e, 0xdd, 0xc4, 0x6d, 0x81, 0x85, 0x85, 0xab, 0xd3, 0xc8, 0xd9, 0x4f, 0x70, 0xd2, - 0xde, 0xd4, 0x84, 0x1a, 0xcd, 0x64, 0x7f, 0x14, 0x90, 0x23, 0x14, 0x3b, 0xde, 0xdf, 0x59, 0xe0, - 0x21, 0xf6, 0xcc, 0xb9, 0x6c, 0x4b, 0x2a, 0x03, 0x37, 0x04, 0x95, 0x10, 0xef, 0x52, 0x34, 0xa7, - 0x23, 0x27, 0xb5, 0x91, 0xe6, 0xd9, 0x40, 0x85, 0xb0, 0xb4, 0x0b, 0x3d, 0x83, 0x9b, 0xbc, 0x95, - 0x8c, 0x27, 0x81, 0x8f, 0xe3, 0x91, 0x66, 0x8c, 0xf2, 0x3e, 0xc5, 0x99, 0x2d, 0xec, 0x3a, 0x6d, - 0x35, 0x32, 0xc7, 0x81, 0x2a, 0x0b, 0xb7, 0x1d, 0x60, 0xa7, 0x00, 0xea, 0x30, 0x98, 0xf7, 0xcc, - 0x22, 0x2c, 0xad, 0xa7, 0x77, 0x41, 0x8b, 0x46, 0x96, 0x67, 0x85, 0x19, 0xf5, 0x99, 0x59, 0xfd, - 0x3a, 0xc3, 0x53, 0xc5, 0xe1, 0x75, 0x80, 0xf1, 0x34, 0x8a, 0x4d, 0xf2, 0x73, 0x51, 0x61, 0xa4, - 0x11, 0x42, 0x27, 0x21, 0xfa, 0x6d, 0x58, 0xe9, 0x24, 0x4e, 0x71, 0x49, 0x4c, 0x86, 0xa2, 0x7c, - 0x88, 0x28, 0x80, 0x0a, 0x7b, 0x4b, 0xec, 0x5b, 0x13, 0xf1, 0x00, 0x8a, 0x63, 0x6b, 0x22, 0x6f, - 0xeb, 0x34, 0x92, 0x2b, 0x4d, 0x8c, 0x7d, 0xb8, 0x6f, 0x4d, 0xd8, 0xce, 0x8b, 0x44, 0xb7, 0x3e, - 0x83, 0xb2, 0x02, 0xfc, 0xb4, 0x48, 0x56, 0x4b, 0x50, 0xd9, 0xc9, 0x3a, 0xb5, 0xa2, 0x2e, 0x1c, - 0x87, 0x53, 0x9f, 0x9a, 0x28, 0x9f, 0x5c, 0x19, 0x59, 0xfe, 0x50, 0x82, 0x14, 0x43, 0x5d, 0xfa, - 0x1e, 0x86, 0x7a, 0x1b, 0x40, 0x2e, 0x4f, 0xd7, 0x56, 0x72, 0x78, 0x39, 0xc4, 0x85, 0xd9, 0xb1, - 0x2f, 0x16, 0xdf, 0x31, 0x2e, 0xfd, 0xf8, 0x3b, 0xc6, 0xcb, 0x0b, 0xef, 0x18, 0xbf, 0x9d, 0x8a, - 0xde, 0xb4, 0xb3, 0xd9, 0x17, 0xf2, 0x51, 0x08, 0x25, 0x68, 0xe3, 0x8e, 0x66, 0x5f, 0x88, 0x9f, - 0x41, 0x5d, 0xb5, 0x4e, 0x96, 0x07, 0xb9, 0xb8, 0x82, 0x12, 0xc7, 0x5e, 0xb0, 0x6b, 0x71, 0x36, - 0x99, 0xdf, 0x28, 0xaa, 0x3f, 0x70, 0x8f, 0xf9, 0x6f, 0x15, 0x40, 0x48, 0x1f, 0xa6, 0xdd, 0xa9, - 0xe7, 0x0d, 0x9d, 0x0b, 0xda, 0x8f, 0x1e, 0xc0, 0x46, 0xee, 0x82, 0x4e, 0x46, 0xd3, 0x59, 0xcf, - 0xdc, 0xc8, 0x51, 0xc1, 0x9a, 0xe7, 0x42, 0xf3, 0x2e, 0x2d, 0x0c, 0xcd, 0xbb, 0x38, 0xe4, 0xef, - 0x5d, 0xa8, 0x66, 0x03, 0xdb, 0xb2, 0xea, 0x0b, 0x56, 0x12, 0xd3, 0x56, 0xff, 0x9f, 0x97, 0x00, - 0x52, 0x3f, 0xab, 0xbf, 0xec, 0x0b, 0xe2, 0x0b, 0x86, 0xa4, 0xb8, 0x68, 0x48, 0xee, 0x83, 0x96, - 0xa5, 0xcb, 0x44, 0x58, 0xae, 0xa7, 0x84, 0x4a, 0x05, 0x74, 0xa3, 0x6c, 0x10, 0x5b, 0x0a, 0x9d, - 0x26, 0xef, 0xd4, 0x32, 0x92, 0x45, 0x50, 0xc9, 0xb0, 0xca, 0x6e, 0xc4, 0x92, 0x00, 0x49, 0xaf, - 0x2a, 0x27, 0x79, 0x58, 0x04, 0xd3, 0x38, 0xb9, 0x7f, 0xb3, 0x2a, 0xa5, 0x57, 0x59, 0xd2, 0x0b, - 0x46, 0xab, 0xfb, 0x37, 0x9f, 0x42, 0xe5, 0x78, 0xea, 0x79, 0x66, 0xec, 0x5c, 0xa8, 0x3b, 0x41, - 0x8d, 0x9c, 0x8b, 0x5a, 0x66, 0x78, 0x8d, 0xf2, 0xb1, 0x4c, 0xe8, 0xff, 0xfa, 0x0a, 0x2c, 0xb3, - 0x61, 0xfc, 0x33, 0xa8, 0x44, 0xf1, 0x38, 0xce, 0xde, 0x16, 0x91, 0xae, 0x47, 0x84, 0xa7, 0xcb, - 0x1e, 0xb4, 0x6d, 0xb0, 0xcc, 0x83, 0xb4, 0xc4, 0xeb, 0x36, 0x61, 0x39, 0x8a, 0x9d, 0x49, 0x24, - 0xe3, 0xae, 0x70, 0x42, 0x6c, 0xc1, 0xb2, 0x1f, 0xd8, 0x4e, 0x94, 0x8f, 0xae, 0xd2, 0x43, 0x6d, - 0x8c, 0x11, 0x42, 0x87, 0x95, 0x64, 0xc4, 0xe7, 0x6e, 0x6c, 0x30, 0x86, 0x62, 0xb6, 0x39, 0x16, - 0x72, 0x5f, 0x15, 0xb1, 0x3a, 0x49, 0xa3, 0xda, 0x41, 0xe7, 0x69, 0xd6, 0x89, 0x8a, 0xe1, 0x2f, - 0x93, 0x62, 0x0b, 0xaa, 0xf8, 0xf3, 0x45, 0xe8, 0xc6, 0xce, 0xe0, 0xb1, 0x92, 0x29, 0x32, 0x20, - 0x71, 0x07, 0xc0, 0x76, 0x62, 0x67, 0x14, 0x0f, 0xbe, 0xf5, 0x94, 0x81, 0x30, 0x03, 0x11, 0x3f, - 0x03, 0x71, 0x64, 0x8d, 0xce, 0x4e, 0xf8, 0xc2, 0xd1, 0xb7, 0x53, 0x27, 0x74, 0x13, 0xc6, 0x5d, - 0xcd, 0x74, 0x8a, 0xb1, 0x91, 0x92, 0xfd, 0x8a, 0xa9, 0xc4, 0x75, 0x58, 0x19, 0x5b, 0x17, 0x3b, - 0xc1, 0x44, 0x5a, 0xd0, 0x65, 0x4a, 0x3c, 0x86, 0xeb, 0x28, 0xa3, 0x64, 0xa3, 0x8f, 0x48, 0xaf, - 0x40, 0xe6, 0xd0, 0x28, 0xc1, 0xa4, 0x61, 0x47, 0xe4, 0xd9, 0xf5, 0x36, 0x6c, 0x2a, 0x03, 0xb9, - 0xed, 0x4c, 0x1c, 0xdf, 0x76, 0xfc, 0x91, 0x9b, 0xc4, 0xfc, 0x9c, 0x9b, 0xfc, 0x57, 0x25, 0xf1, - 0x4e, 0x86, 0x56, 0xbc, 0x09, 0x6b, 0xf8, 0xe1, 0xd0, 0x89, 0xa7, 0xa1, 0x8f, 0x7b, 0x1f, 0x4b, - 0x14, 0x28, 0x20, 0x1a, 0x0a, 0xc6, 0x6e, 0xce, 0x32, 0x91, 0xde, 0x93, 0x5a, 0x36, 0xd6, 0x12, - 0x28, 0xf9, 0xe6, 0x3f, 0x86, 0xeb, 0xec, 0x40, 0x19, 0x7d, 0xeb, 0x49, 0x39, 0xcb, 0x4c, 0x6f, - 0x8c, 0x94, 0x8d, 0xab, 0x84, 0x1d, 0x7c, 0xeb, 0xb1, 0xa8, 0xc5, 0x7b, 0x7d, 0x1f, 0xae, 0x4f, - 0xfd, 0xd0, 0x89, 0x02, 0xef, 0x65, 0x12, 0x5c, 0xf9, 0xd4, 0x45, 0xe1, 0x58, 0xcb, 0x5e, 0xcb, - 0x3b, 0x4c, 0x68, 0x48, 0xa6, 0xdf, 0x73, 0xfd, 0xd8, 0xd8, 0x9c, 0xce, 0x03, 0x23, 0xfd, 0xf7, - 0x61, 0x2d, 0x37, 0x1b, 0xe7, 0x9c, 0x82, 0x06, 0xed, 0x6e, 0xbb, 0x35, 0xe4, 0x53, 0x7e, 0xe9, - 0xa6, 0xb2, 0x94, 0xf1, 0x67, 0x29, 0x65, 0x4e, 0xff, 0x97, 0xc9, 0x1b, 0xa6, 0x6d, 0x3c, 0x6d, - 0x93, 0x83, 0x46, 0x51, 0x2b, 0xea, 0x7f, 0x7b, 0x09, 0x36, 0x86, 0xa1, 0xe5, 0x47, 0x16, 0x6b, - 0xbd, 0x7e, 0x1c, 0x06, 0x9e, 0xf8, 0x19, 0x94, 0xe3, 0x91, 0x97, 0x5d, 0x1e, 0x77, 0x15, 0x33, - 0x9e, 0x21, 0x7d, 0x38, 0x1c, 0xb1, 0x83, 0xf3, 0x6a, 0xcc, 0x3f, 0xc4, 0x07, 0xb0, 0x7c, 0xe4, - 0x9c, 0xb8, 0xbe, 0xdc, 0x8f, 0xae, 0xcd, 0x66, 0xdc, 0x46, 0xe4, 0xde, 0x15, 0x83, 0xa9, 0xc4, - 0x47, 0xb0, 0x22, 0xcf, 0x89, 0x8a, 0xd9, 0x7b, 0x06, 0xd9, 0x0f, 0x21, 0x76, 0xef, 0x8a, 0x21, - 0xe9, 0xc4, 0x67, 0x50, 0x0e, 0x03, 0xf9, 0x00, 0x54, 0x29, 0xbb, 0xf8, 0xd3, 0x3c, 0x86, 0xc4, - 0xef, 0x5d, 0x31, 0x12, 0x5a, 0xfd, 0x21, 0xac, 0xca, 0xca, 0x62, 0x37, 0x6c, 0xb7, 0x9f, 0x76, - 0x64, 0x0f, 0xb6, 0xfa, 0xfb, 0xfb, 0x9d, 0x21, 0xc7, 0x87, 0x36, 0xfa, 0xdd, 0xee, 0x76, 0xb3, - 0xf5, 0x4c, 0x5b, 0xda, 0x2e, 0xc3, 0x0a, 0x4f, 0x5a, 0xfd, 0x5f, 0x2e, 0xc0, 0xfa, 0x4c, 0x03, - 0xc4, 0x13, 0x28, 0x91, 0xf5, 0x92, 0xbb, 0xe7, 0xde, 0xc2, 0x56, 0x66, 0xd2, 0x6c, 0x9b, 0xc1, - 0x1c, 0xfa, 0x17, 0x50, 0xcf, 0xc3, 0x33, 0x3e, 0x24, 0x6b, 0x50, 0x31, 0xda, 0xcd, 0x1d, 0xb3, - 0xdf, 0xeb, 0x7e, 0xcd, 0x7e, 0x5d, 0x94, 0x7c, 0x61, 0x74, 0xc8, 0x79, 0xe3, 0xf7, 0x40, 0x9b, - 0xed, 0x18, 0xf1, 0x14, 0xd6, 0x51, 0xf6, 0xf2, 0x1c, 0x76, 0x58, 0x4b, 0x87, 0xec, 0xce, 0x82, - 0x9e, 0x94, 0x64, 0x7c, 0xfb, 0x79, 0x94, 0x4b, 0xeb, 0xbf, 0x0f, 0x62, 0xbe, 0x07, 0xff, 0xf2, - 0x8a, 0xff, 0xbf, 0x0a, 0x50, 0x3a, 0xf0, 0x2c, 0x5f, 0xbc, 0x09, 0xcb, 0x7c, 0x64, 0x59, 0xc8, - 0xde, 0xaf, 0x23, 0x1e, 0x83, 0xd3, 0x82, 0x70, 0xe2, 0x3d, 0x28, 0xc6, 0x23, 0x15, 0x09, 0xfa, - 0xc6, 0x2b, 0x26, 0xdf, 0xde, 0x15, 0x03, 0xa9, 0xc4, 0x7d, 0x28, 0xda, 0xb6, 0x8a, 0x23, 0x96, - 0x39, 0xd0, 0xde, 0x71, 0x8e, 0xe9, 0x86, 0x25, 0xbd, 0x7d, 0x84, 0x24, 0xe2, 0x2d, 0x28, 0xda, - 0x23, 0x2f, 0x1f, 0x14, 0x8e, 0xcf, 0x9a, 0x93, 0x02, 0xed, 0x91, 0x87, 0x7a, 0x58, 0x1c, 0x5e, - 0x9a, 0xe1, 0xd4, 0xa7, 0x30, 0x1a, 0xea, 0x01, 0xaf, 0x2a, 0xca, 0x76, 0x53, 0x8a, 0xc5, 0x11, - 0xc9, 0x90, 0xb2, 0xf2, 0x85, 0x1e, 0x65, 0xbb, 0x73, 0x23, 0xf9, 0x96, 0xcf, 0xf6, 0x0a, 0xd0, - 0x33, 0xce, 0xfa, 0xfb, 0xf4, 0x18, 0x0c, 0xaa, 0xdd, 0xba, 0xfa, 0xb5, 0xe0, 0xcd, 0x4b, 0x89, - 0xd1, 0xff, 0x59, 0x11, 0xaa, 0x99, 0xfa, 0x88, 0x4f, 0xa0, 0x6c, 0xe7, 0x17, 0xe2, 0xcd, 0xb9, - 0x4a, 0x3f, 0xdc, 0x51, 0x4b, 0xd0, 0x96, 0xd3, 0x9b, 0xbc, 0xe7, 0x63, 0xf3, 0xa5, 0x15, 0xba, - 0x7c, 0x25, 0x78, 0x29, 0xeb, 0xc6, 0x3e, 0x70, 0xe2, 0xe7, 0x0a, 0xb3, 0x77, 0xc5, 0xa8, 0x45, - 0x99, 0x34, 0x59, 0xaa, 0x64, 0x93, 0x8a, 0xb9, 0xc7, 0x14, 0x19, 0xb8, 0x77, 0xc5, 0x50, 0x78, - 0x24, 0x75, 0x2e, 0x9c, 0xd1, 0x34, 0x56, 0x96, 0xaa, 0x35, 0xd5, 0x20, 0x02, 0xd2, 0xc3, 0xb4, - 0xfc, 0x53, 0x3c, 0xc2, 0x3d, 0xc8, 0xf2, 0xbc, 0x80, 0x44, 0xd8, 0xe5, 0xec, 0x31, 0xd1, 0x4e, - 0x02, 0xe7, 0x87, 0x70, 0x55, 0x4a, 0xbc, 0x0d, 0xcb, 0x41, 0x7c, 0xea, 0x28, 0xcd, 0x5a, 0x3d, - 0x2b, 0x83, 0xa0, 0x9d, 0x56, 0x17, 0x67, 0x0a, 0xa1, 0xf5, 0x3f, 0x2e, 0xc0, 0xaa, 0xec, 0x01, - 0xb1, 0x01, 0x6b, 0x83, 0xf6, 0xd0, 0x7c, 0xde, 0x34, 0x3a, 0xcd, 0xed, 0x6e, 0x5b, 0xc6, 0xb2, - 0x7b, 0x6a, 0x34, 0x7b, 0x92, 0x41, 0x1a, 0xed, 0xe7, 0xfd, 0x67, 0x6d, 0x76, 0xcc, 0xda, 0x69, - 0xf7, 0xbe, 0xd6, 0x8a, 0xec, 0xc4, 0xd8, 0x3e, 0x68, 0x1a, 0xc8, 0x2b, 0xab, 0xb0, 0xda, 0xfe, - 0xaa, 0xdd, 0x3a, 0x24, 0x66, 0x59, 0x07, 0xd8, 0x69, 0x37, 0xbb, 0xdd, 0x7e, 0x0b, 0x99, 0xe7, - 0x8a, 0x10, 0x50, 0x6f, 0x19, 0xed, 0xe6, 0xb0, 0x6d, 0x36, 0x5b, 0xad, 0xfe, 0x61, 0x6f, 0xa8, - 0xad, 0xe2, 0x17, 0x9b, 0xdd, 0x61, 0xdb, 0x48, 0x40, 0xf4, 0x58, 0xde, 0x8e, 0xd1, 0x3f, 0x48, - 0x20, 0x95, 0xed, 0x0a, 0xac, 0x8e, 0x78, 0xac, 0xf4, 0xff, 0x6c, 0x83, 0xfd, 0x13, 0xd2, 0xa9, - 0x29, 0x3e, 0x87, 0xb2, 0x6d, 0xe7, 0xc6, 0xf8, 0xf6, 0xa2, 0x29, 0xfc, 0x70, 0xc7, 0x56, 0xc3, - 0xcc, 0x3f, 0xc4, 0x1b, 0x6a, 0x21, 0x2d, 0xcd, 0x2d, 0x24, 0xb5, 0x8c, 0x7e, 0x01, 0xeb, 0xf2, - 0xad, 0x92, 0xe4, 0x68, 0x33, 0xb7, 0x4a, 0x5a, 0x84, 0xdc, 0x91, 0xb8, 0xbd, 0x2b, 0x46, 0x7d, - 0x94, 0x83, 0x88, 0x9f, 0x43, 0xdd, 0xe2, 0x03, 0xc3, 0xec, 0xd1, 0x68, 0x22, 0x9c, 0x37, 0xe9, - 0xbc, 0x30, 0xcd, 0xbe, 0x66, 0x65, 0x01, 0x38, 0x11, 0xed, 0x30, 0x98, 0xa4, 0x99, 0x97, 0x73, - 0xf7, 0x29, 0xc2, 0x60, 0x92, 0xc9, 0x5b, 0xb3, 0x33, 0x69, 0xf1, 0x19, 0xd4, 0x64, 0xcd, 0x53, - 0xe3, 0x78, 0xb2, 0x64, 0xb9, 0xda, 0x24, 0x6c, 0xef, 0x5d, 0x31, 0xaa, 0xa3, 0x34, 0x29, 0x1e, - 0xa3, 0x84, 0x9d, 0xaa, 0x26, 0xab, 0xd9, 0xb9, 0x46, 0xb5, 0x55, 0xb9, 0xc0, 0x4a, 0x52, 0xe2, - 0x23, 0x00, 0xaa, 0x27, 0xe7, 0x29, 0xe7, 0x6e, 0x1a, 0x86, 0xc1, 0x44, 0x65, 0xa9, 0xd8, 0x2a, - 0x91, 0xa9, 0x1e, 0x5b, 0xec, 0x2a, 0xf3, 0xd5, 0xe3, 0x4d, 0x3c, 0xa9, 0x1e, 0x1b, 0xef, 0x92, - 0xea, 0x71, 0x36, 0x98, 0xab, 0x9e, 0xca, 0xc5, 0xd5, 0xe3, 0x4c, 0xaa, 0x7a, 0x9c, 0xa7, 0x3a, - 0x5b, 0x3d, 0x95, 0x85, 0xaa, 0xc7, 0x39, 0x7e, 0x3e, 0xa7, 0x53, 0xd5, 0x5e, 0xa9, 0x53, 0xe1, - 0xb0, 0xe5, 0xb5, 0xaa, 0x9f, 0x43, 0x3d, 0x3a, 0x0d, 0xce, 0x33, 0x0c, 0x64, 0x2d, 0x9b, 0x7b, - 0x70, 0x1a, 0x9c, 0x67, 0x39, 0xc8, 0x5a, 0x94, 0x05, 0x60, 0x6d, 0xb9, 0x89, 0x74, 0xf7, 0xa1, - 0x9e, 0xad, 0x2d, 0xb5, 0xf0, 0xb9, 0xeb, 0x9c, 0x63, 0x6d, 0x2d, 0x95, 0xc0, 0x4e, 0x49, 0x0f, - 0x0a, 0x22, 0x69, 0xfa, 0xcf, 0xdd, 0xcf, 0x93, 0x5f, 0x82, 0xe4, 0xc8, 0x20, 0xc2, 0xb9, 0x35, - 0xf5, 0xb3, 0xd9, 0xb4, 0xec, 0xdc, 0x3a, 0xf4, 0x73, 0x19, 0x6b, 0x4c, 0x2a, 0xb3, 0xa6, 0xab, - 0x22, 0x72, 0xbe, 0x9d, 0x3a, 0xfe, 0xc8, 0x91, 0xf7, 0x70, 0x73, 0xab, 0x62, 0x20, 0x71, 0xe9, - 0xaa, 0x50, 0x90, 0x64, 0x5e, 0x27, 0xd9, 0xc5, 0xec, 0xbc, 0xce, 0x64, 0xa6, 0x79, 0x9d, 0x64, - 0x4d, 0x16, 0x54, 0x92, 0xf7, 0xea, 0xdc, 0x82, 0xca, 0x64, 0xe6, 0x05, 0x95, 0xe4, 0x7e, 0x0c, - 0x72, 0x36, 0x71, 0xe7, 0xe6, 0x6e, 0xeb, 0x72, 0xad, 0x65, 0xef, 0xc2, 0x28, 0x49, 0xe1, 0x5c, - 0x0d, 0x1d, 0xd4, 0xe1, 0xe4, 0x54, 0xb8, 0x96, 0x9d, 0xab, 0x06, 0x61, 0x92, 0xa5, 0x14, 0xa6, - 0xc9, 0xcc, 0xc7, 0x26, 0x6e, 0x1c, 0x36, 0xec, 0xf9, 0x8f, 0x1d, 0xb8, 0x71, 0x98, 0x7e, 0x0c, - 0x53, 0xe2, 0x03, 0xa0, 0x69, 0xc8, 0x59, 0x9c, 0x2c, 0xeb, 0xc6, 0x6e, 0x91, 0x19, 0xca, 0xb6, - 0xfc, 0x8d, 0x93, 0x45, 0x7e, 0x63, 0x64, 0x8f, 0x1a, 0xc7, 0xd9, 0xc9, 0xc2, 0x9f, 0x68, 0xed, - 0xb4, 0x70, 0xb2, 0x30, 0x51, 0xcb, 0x1e, 0x89, 0x07, 0x40, 0xb9, 0x89, 0xfe, 0x24, 0xf7, 0x6c, - 0x59, 0x18, 0x4c, 0x98, 0x7a, 0x15, 0x09, 0x90, 0x16, 0x5b, 0xe0, 0x05, 0xbe, 0x6a, 0xf8, 0x69, - 0xae, 0x05, 0x88, 0x48, 0x98, 0xc1, 0x28, 0x49, 0xe9, 0xff, 0xea, 0x0a, 0xac, 0x4a, 0x5e, 0x2b, - 0xae, 0xc2, 0xba, 0x64, 0xf9, 0x3b, 0xcd, 0x61, 0x73, 0xbb, 0x39, 0x40, 0x21, 0x4d, 0x40, 0x9d, - 0x79, 0x7e, 0x02, 0x2b, 0xe0, 0x3e, 0x40, 0x4c, 0x3f, 0x01, 0x2d, 0xe1, 0x3e, 0x20, 0xf3, 0xb2, - 0xfb, 0x6b, 0x51, 0xac, 0x43, 0x95, 0x33, 0x32, 0x80, 0x42, 0xeb, 0x52, 0x2e, 0x4e, 0x2f, 0x67, - 0xb2, 0xb0, 0x03, 0xf3, 0x4a, 0x9a, 0x85, 0x01, 0xab, 0x49, 0x16, 0xe5, 0xe1, 0x2c, 0xa0, 0x3e, - 0x34, 0x0e, 0x7b, 0xad, 0xf4, 0x3b, 0x15, 0x0a, 0x87, 0xca, 0xc5, 0x3c, 0xef, 0xb4, 0x5f, 0x68, - 0x80, 0x99, 0xb8, 0x14, 0x4a, 0x93, 0xe7, 0x33, 0x15, 0x42, 0xc9, 0x9a, 0xb8, 0x01, 0x57, 0x07, - 0x7b, 0xfd, 0x17, 0x26, 0x67, 0x4a, 0x9a, 0xb0, 0x26, 0x36, 0x41, 0xcb, 0x20, 0xb8, 0xf8, 0x3a, - 0x7e, 0x92, 0xa0, 0x8a, 0x70, 0xa0, 0xad, 0xd3, 0x63, 0x2a, 0x08, 0x1b, 0xf2, 0xbe, 0xab, 0x61, - 0x53, 0x38, 0x6b, 0xbf, 0x7b, 0xb8, 0xdf, 0x1b, 0x68, 0x1b, 0x58, 0x09, 0x82, 0x70, 0xcd, 0x45, - 0x52, 0x4c, 0xba, 0x5b, 0x5f, 0xa5, 0x0d, 0x1c, 0x61, 0x2f, 0x9a, 0x46, 0xaf, 0xd3, 0x7b, 0x3a, - 0xd0, 0x36, 0x93, 0x92, 0xdb, 0x86, 0xd1, 0x37, 0x06, 0xda, 0xb5, 0x04, 0x30, 0x18, 0x36, 0x87, - 0x87, 0x03, 0xed, 0x7a, 0x52, 0xcb, 0x03, 0xa3, 0xdf, 0x6a, 0x0f, 0x06, 0xdd, 0xce, 0x60, 0xa8, - 0xdd, 0x10, 0xd7, 0x60, 0x23, 0xad, 0x91, 0x22, 0x6e, 0x64, 0x2a, 0x6a, 0x3c, 0x6d, 0x0f, 0xb5, - 0x9b, 0x49, 0x35, 0x5a, 0xfd, 0x6e, 0xb7, 0x49, 0xbe, 0xd5, 0xb7, 0x90, 0x88, 0xae, 0x00, 0xc8, - 0xd6, 0xbc, 0x86, 0xf5, 0x3a, 0xec, 0x65, 0x41, 0xb7, 0x33, 0x53, 0x63, 0xd0, 0xfe, 0xd5, 0x61, - 0xbb, 0xd7, 0x6a, 0x6b, 0xaf, 0xa7, 0x53, 0x23, 0x81, 0xdd, 0x49, 0xa6, 0x46, 0x02, 0xba, 0x9b, - 0x7c, 0x53, 0x81, 0x06, 0xda, 0x56, 0xd2, 0x61, 0x87, 0x07, 0x4f, 0x8d, 0xe6, 0x4e, 0x5b, 0xd3, - 0x11, 0x62, 0xb4, 0x7b, 0xcd, 0x7d, 0x35, 0xb0, 0x6f, 0x66, 0x06, 0xf6, 0xa0, 0x33, 0x34, 0xb4, - 0x7b, 0xc9, 0x40, 0x52, 0xf2, 0x2d, 0xf1, 0x1a, 0xdc, 0xc8, 0x4e, 0x39, 0xf3, 0x45, 0x67, 0xb8, - 0x27, 0x3d, 0xf5, 0xdf, 0x66, 0x67, 0x6f, 0x42, 0xb6, 0x76, 0x5a, 0x7c, 0x25, 0x81, 0xf2, 0x62, - 0xea, 0xbe, 0x5e, 0x2a, 0xbf, 0xa1, 0xbd, 0xf1, 0x60, 0x5d, 0x76, 0x45, 0xaf, 0x47, 0xd7, 0x3d, - 0x06, 0xdb, 0x35, 0x94, 0xdc, 0x94, 0x08, 0xa2, 0x7f, 0x09, 0x22, 0xfb, 0x90, 0xb5, 0x0c, 0x68, - 0x20, 0xa0, 0x74, 0x1c, 0x06, 0x63, 0xe5, 0x26, 0x82, 0xbf, 0xc5, 0x16, 0x54, 0x27, 0xd3, 0x23, - 0xf2, 0x4d, 0x4f, 0x03, 0x59, 0x67, 0x41, 0xfa, 0xbf, 0x5f, 0x80, 0x7a, 0x5e, 0xfc, 0xa0, 0xe3, - 0x8f, 0x63, 0xd3, 0x0f, 0x62, 0x7e, 0xcf, 0x2a, 0x4a, 0xde, 0x97, 0x3e, 0xee, 0x05, 0x31, 0x3d, - 0x68, 0x15, 0xe5, 0xbc, 0xb4, 0x96, 0x66, 0xbc, 0xb4, 0x3a, 0x70, 0x35, 0xf7, 0x6e, 0x78, 0xee, - 0x35, 0xb1, 0x46, 0xf2, 0x92, 0xe9, 0x4c, 0xfd, 0x0d, 0x11, 0xcd, 0xb7, 0x49, 0x86, 0x23, 0x2f, - 0xa5, 0xe1, 0xc8, 0xf7, 0x60, 0x2d, 0x27, 0xed, 0x90, 0x75, 0xeb, 0x38, 0x5f, 0xd3, 0xb2, 0x7b, - 0xfc, 0xc3, 0xd5, 0xd4, 0xff, 0x6e, 0x01, 0x6a, 0x59, 0xd9, 0xe7, 0xb7, 0x2e, 0x89, 0xee, 0x19, - 0xca, 0xdf, 0xa6, 0x6b, 0xab, 0x67, 0xa8, 0x14, 0x88, 0x5c, 0x20, 0xab, 0x7c, 0x92, 0xb4, 0x7b, - 0x36, 0x48, 0x9a, 0x93, 0x05, 0x91, 0x67, 0xdb, 0xa9, 0x33, 0x3a, 0xdb, 0x7d, 0x86, 0x04, 0xca, - 0xb3, 0x2d, 0x81, 0xe8, 0x77, 0xa1, 0xb2, 0x7b, 0xa6, 0xae, 0x5d, 0x66, 0x5f, 0x75, 0xab, 0x70, - 0xf4, 0x73, 0xfd, 0xef, 0x17, 0xa0, 0x9e, 0xda, 0x72, 0xc8, 0xb1, 0x9e, 0xdf, 0x9b, 0xe7, 0xe9, - 0xb0, 0x64, 0x1f, 0xa5, 0xde, 0x8b, 0x4b, 0x59, 0xef, 0xc5, 0x37, 0x65, 0x61, 0xc5, 0x2c, 0xd3, - 0x4f, 0xbe, 0x25, 0x63, 0xab, 0x3f, 0x86, 0x1a, 0xfe, 0x37, 0x9c, 0x63, 0x27, 0x0c, 0x1d, 0x3b, - 0x1f, 0xaf, 0x2a, 0x25, 0xce, 0x11, 0x91, 0x96, 0x97, 0x3c, 0xb1, 0xb0, 0xf0, 0x11, 0x18, 0x7a, - 0x9c, 0xe8, 0xff, 0x28, 0x42, 0x35, 0x23, 0x49, 0xfe, 0xa8, 0xe9, 0x77, 0x1b, 0x2a, 0xe9, 0x93, - 0x24, 0x32, 0xa0, 0x75, 0x02, 0xc8, 0x8d, 0x55, 0x71, 0x66, 0xac, 0xc8, 0x89, 0x98, 0xa2, 0x68, - 0xa6, 0x4e, 0xc4, 0x94, 0xcc, 0x1b, 0xb1, 0x97, 0x7f, 0xe0, 0xb4, 0xf3, 0x63, 0xa8, 0x65, 0x2c, - 0xd0, 0xca, 0xdd, 0x70, 0x96, 0xbe, 0x9a, 0x5a, 0xa3, 0x23, 0x71, 0x0d, 0x56, 0x8e, 0xcf, 0x4c, - 0xfb, 0x88, 0x4f, 0xf0, 0x2b, 0xc6, 0xf2, 0xf1, 0xd9, 0xce, 0x11, 0x79, 0x2c, 0x1c, 0x27, 0xc2, - 0x13, 0xdb, 0x05, 0xcb, 0xc7, 0x4a, 0x44, 0xba, 0x0f, 0xab, 0xc7, 0x67, 0xd9, 0xb0, 0xb8, 0x73, - 0x5d, 0xbe, 0x72, 0x7c, 0x46, 0x91, 0x59, 0x3f, 0x84, 0x4d, 0xb9, 0x83, 0x5b, 0x91, 0x32, 0x93, - 0xe1, 0xd2, 0xe1, 0x37, 0xde, 0x36, 0x18, 0xd7, 0x8c, 0xd8, 0x48, 0x86, 0x33, 0x4e, 0x87, 0x5a, - 0x66, 0x02, 0xb2, 0x7d, 0xaf, 0x62, 0xe4, 0x60, 0xe2, 0x09, 0xd4, 0x8e, 0xcf, 0x78, 0x40, 0x87, - 0xc1, 0xbe, 0x23, 0x23, 0xbf, 0x6c, 0xce, 0x0e, 0x25, 0xdd, 0x7c, 0xc8, 0x51, 0x8a, 0xeb, 0xb0, - 0x62, 0x58, 0xe7, 0x83, 0x5f, 0x75, 0x49, 0x8c, 0xac, 0x18, 0x32, 0xc5, 0x2f, 0xc0, 0xe9, 0xff, - 0xa8, 0x00, 0xf5, 0x54, 0x0b, 0xc0, 0x45, 0x28, 0x1e, 0xa4, 0x8f, 0x3f, 0xd7, 0x15, 0x93, 0xc8, - 0x93, 0x3c, 0x1c, 0x5e, 0x4e, 0xf8, 0x31, 0xd5, 0x45, 0xef, 0x30, 0x2d, 0x3a, 0x12, 0x28, 0x2e, - 0x7c, 0x76, 0xfe, 0x29, 0x14, 0x87, 0x97, 0x13, 0xb6, 0x38, 0xe1, 0xa6, 0xc8, 0xda, 0x29, 0x6f, - 0x87, 0x74, 0xbb, 0xe8, 0x59, 0xfb, 0x6b, 0x0e, 0xc0, 0x7f, 0x60, 0x74, 0xf6, 0x9b, 0xc6, 0xd7, - 0x26, 0x02, 0x48, 0x6c, 0xd8, 0xed, 0x1b, 0xed, 0xce, 0xd3, 0x1e, 0x01, 0x4a, 0x64, 0x8f, 0x4a, - 0xab, 0xd8, 0xb4, 0xed, 0xdd, 0xb3, 0xec, 0x13, 0x36, 0x85, 0xdc, 0x13, 0x36, 0xc9, 0x6b, 0x4f, - 0xd9, 0xa7, 0x09, 0xe3, 0xe4, 0x9c, 0x42, 0x24, 0xab, 0x30, 0x59, 0xd2, 0xe2, 0x1d, 0x28, 0x1d, - 0x9f, 0x39, 0x97, 0x79, 0x55, 0x2f, 0xbf, 0x80, 0x88, 0x40, 0xff, 0xb3, 0x02, 0x88, 0x5c, 0x45, - 0x58, 0xfb, 0xf8, 0x6d, 0xeb, 0xf2, 0x39, 0x34, 0xd4, 0x3b, 0xa6, 0x44, 0x95, 0x39, 0x83, 0x90, - 0x5d, 0x7a, 0x2d, 0x48, 0x23, 0x0e, 0xa4, 0x4f, 0x45, 0x89, 0x0f, 0x81, 0x5f, 0x1d, 0xa4, 0x8b, - 0x32, 0xa5, 0x57, 0x68, 0x8a, 0x46, 0x4a, 0x93, 0x3e, 0x33, 0x98, 0x7d, 0x3e, 0x91, 0x8f, 0x2f, - 0xd6, 0xd3, 0x51, 0xa3, 0x35, 0xaf, 0xff, 0xcd, 0x02, 0x5c, 0xcd, 0x4f, 0x88, 0xbf, 0x58, 0x2b, - 0xf3, 0x6f, 0x45, 0x16, 0x67, 0xdf, 0x8a, 0x5c, 0x34, 0x9f, 0x4a, 0x0b, 0xe7, 0xd3, 0x1f, 0x14, - 0x60, 0x33, 0xd3, 0xfb, 0xa9, 0xbe, 0xf8, 0x57, 0x54, 0xb3, 0xcc, 0x93, 0x91, 0xa5, 0xdc, 0x93, - 0x91, 0xfa, 0x9f, 0x16, 0xe0, 0xfa, 0x4c, 0x4d, 0x0c, 0xe7, 0xaf, 0xb4, 0x2e, 0xf9, 0xa7, 0x25, - 0xf9, 0xd1, 0x7f, 0x19, 0xa4, 0xbe, 0x70, 0xbf, 0x98, 0x79, 0x5a, 0x92, 0xce, 0xd5, 0xe8, 0x70, - 0xf7, 0x75, 0xf9, 0x96, 0x89, 0x19, 0x5d, 0xfa, 0x23, 0x39, 0xd8, 0x15, 0x82, 0x0c, 0x2e, 0xfd, - 0x11, 0x6e, 0x52, 0x63, 0x27, 0x3c, 0x51, 0xa6, 0x3c, 0x4e, 0xe8, 0xff, 0x49, 0x01, 0x6e, 0xce, - 0xb4, 0xac, 0x39, 0x8d, 0x03, 0xe9, 0x8b, 0xf1, 0x57, 0xd4, 0xb8, 0xbb, 0x40, 0xaf, 0x2c, 0x29, - 0x27, 0x90, 0xcc, 0x05, 0x0a, 0xf9, 0x5d, 0x0d, 0x8a, 0xb6, 0x75, 0x29, 0x63, 0x1c, 0xe3, 0x4f, - 0x5c, 0xc6, 0xa7, 0xc1, 0x34, 0x94, 0x1e, 0xff, 0xf4, 0x5b, 0xff, 0x04, 0x36, 0xd2, 0xaa, 0xb7, - 0xe4, 0xbb, 0x9f, 0x77, 0xa1, 0xea, 0x3b, 0xe7, 0xa6, 0x7a, 0x15, 0x54, 0xde, 0x05, 0xf3, 0x9d, - 0x73, 0x49, 0xa0, 0xef, 0x66, 0x39, 0x24, 0x55, 0xe7, 0x26, 0x94, 0x03, 0xcf, 0xce, 0x79, 0x80, - 0x06, 0x9e, 0xad, 0x50, 0x58, 0x5a, 0xa6, 0x95, 0xab, 0xbe, 0x73, 0x4e, 0xb3, 0xf3, 0x5c, 0x96, - 0xd3, 0xb4, 0x95, 0xc7, 0xd3, 0x22, 0x77, 0xe2, 0x9b, 0x50, 0x9e, 0x84, 0xb9, 0x6e, 0x5a, 0x9d, - 0x84, 0xfc, 0xd9, 0x7b, 0xf2, 0xa6, 0xd9, 0xab, 0x3c, 0xe6, 0xf8, 0xee, 0x99, 0x06, 0xc5, 0x49, - 0x10, 0x49, 0xa7, 0x1e, 0xfc, 0xa9, 0x7f, 0x2a, 0x99, 0x23, 0xe9, 0x83, 0xfc, 0x65, 0x0d, 0x8a, - 0xae, 0x7d, 0x41, 0x1f, 0x5e, 0x33, 0xf0, 0x27, 0xc9, 0x77, 0xce, 0xb7, 0xf2, 0xb2, 0x1b, 0xfe, - 0xd4, 0xb7, 0xa1, 0x6a, 0xe4, 0x94, 0xdf, 0x5a, 0xc6, 0x8e, 0x14, 0xe5, 0xdf, 0x8c, 0x49, 0x3b, - 0xc8, 0xa8, 0xa6, 0x66, 0xa4, 0x48, 0x8f, 0x24, 0x3b, 0x7c, 0x6e, 0x85, 0xa3, 0x53, 0x2b, 0xec, - 0x3a, 0xfe, 0x49, 0x7c, 0x8a, 0x5d, 0xce, 0xe6, 0xdd, 0x6c, 0x17, 0x02, 0x83, 0xd4, 0x74, 0xc0, - 0x5e, 0xf4, 0x88, 0x5c, 0x3a, 0x04, 0x54, 0x7c, 0xe7, 0x5c, 0xe6, 0x7f, 0x1d, 0x00, 0xfb, 0x5f, - 0xa2, 0xe5, 0x6d, 0x9b, 0xc0, 0xb3, 0x19, 0xad, 0x6f, 0xc8, 0xf6, 0xca, 0x18, 0xdd, 0x3b, 0xce, - 0xb1, 0xee, 0xc9, 0x91, 0xe7, 0x06, 0xc9, 0x4e, 0xf8, 0xad, 0x86, 0x51, 0xbc, 0x01, 0x35, 0x65, - 0xa9, 0xa0, 0x67, 0x8a, 0xf8, 0xf3, 0x55, 0x05, 0xeb, 0x4d, 0xc7, 0xfa, 0x7f, 0x5a, 0x82, 0x5a, - 0x93, 0x7d, 0x44, 0x27, 0x97, 0xfd, 0x49, 0x2c, 0x7e, 0x1f, 0xae, 0x51, 0x7c, 0x7c, 0x0e, 0x55, - 0x40, 0xae, 0x99, 0xec, 0x94, 0xc8, 0x9d, 0xf8, 0x20, 0xd3, 0x89, 0x32, 0xcb, 0xc3, 0xc1, 0x99, - 0x3b, 0xe1, 0xe8, 0x05, 0x1d, 0xfb, 0x82, 0x7c, 0x14, 0xd9, 0x93, 0x82, 0x42, 0xe9, 0xe7, 0x11, - 0x14, 0xaa, 0x19, 0x8b, 0x9f, 0x9c, 0xc9, 0x62, 0xa5, 0x53, 0x0e, 0x02, 0x0f, 0xce, 0x98, 0x26, - 0x75, 0x61, 0x9a, 0xdb, 0x96, 0xa5, 0x0b, 0x53, 0x3a, 0xbf, 0x07, 0xb0, 0x21, 0xc3, 0xf9, 0xd3, - 0x2b, 0x82, 0xe6, 0x28, 0x98, 0x5c, 0xca, 0x83, 0xda, 0x77, 0x5e, 0x51, 0xd5, 0x0e, 0x93, 0x22, - 0x88, 0xeb, 0xb9, 0x1e, 0xe5, 0xa1, 0xe2, 0x39, 0x08, 0xec, 0xd2, 0x89, 0x37, 0xc5, 0xfd, 0x4c, - 0xbd, 0x5f, 0xc8, 0xae, 0xe3, 0xf7, 0x17, 0x94, 0xda, 0x73, 0xce, 0x0f, 0x88, 0x56, 0x16, 0xc2, - 0xc5, 0x6a, 0xfe, 0x0c, 0xf8, 0x56, 0x1b, 0x6e, 0xbc, 0xa2, 0xaf, 0x7e, 0xc8, 0xc9, 0xa4, 0x9c, - 0x71, 0x32, 0xb9, 0xb5, 0x0d, 0x9b, 0x8b, 0xda, 0xf1, 0x93, 0xca, 0x68, 0xc1, 0xb5, 0x85, 0xb5, - 0xfe, 0x29, 0x85, 0xe8, 0x7f, 0x50, 0x07, 0x48, 0x97, 0x53, 0x4e, 0x82, 0x2e, 0xcc, 0x48, 0xd0, - 0x3f, 0xc9, 0x2b, 0xf0, 0x13, 0xa8, 0xe3, 0x38, 0x66, 0x82, 0xae, 0x2e, 0x8e, 0xf6, 0x5a, 0x43, - 0xaa, 0x61, 0x1a, 0x6e, 0x6f, 0xde, 0xbb, 0xa5, 0xb4, 0xd0, 0xbb, 0xe5, 0x63, 0x58, 0xe5, 0xd3, - 0x41, 0x35, 0xa8, 0x37, 0x66, 0x59, 0xc3, 0x43, 0x19, 0xe4, 0x46, 0xd1, 0x89, 0x36, 0xd4, 0x93, - 0xf8, 0x14, 0xd9, 0x78, 0x8f, 0x77, 0xe6, 0x73, 0x2a, 0x32, 0x7e, 0x18, 0xd1, 0xca, 0x26, 0x33, - 0x02, 0x77, 0x3c, 0x96, 0x26, 0x6b, 0x12, 0xb8, 0x57, 0xb3, 0x02, 0xf7, 0x70, 0xcc, 0x86, 0x6a, - 0x14, 0xb8, 0x3f, 0x80, 0xab, 0xd2, 0x4f, 0x18, 0x33, 0x60, 0x77, 0x12, 0x3d, 0xdf, 0xea, 0xd2, - 0x64, 0xfc, 0xf8, 0x31, 0xa9, 0xa3, 0x48, 0xfe, 0x15, 0x6c, 0xf2, 0x13, 0x07, 0xf4, 0x9a, 0xfc, - 0x28, 0xf0, 0x3a, 0xb6, 0x39, 0xb6, 0x26, 0x52, 0x0f, 0x78, 0x67, 0xae, 0xb2, 0xfc, 0xc2, 0xc1, - 0xf0, 0xc8, 0x23, 0x3f, 0xeb, 0xc4, 0x07, 0x6a, 0x63, 0x34, 0x0b, 0x9f, 0x71, 0x45, 0x80, 0x39, - 0x57, 0x84, 0x59, 0xcd, 0xa0, 0xba, 0x40, 0x33, 0x48, 0xe5, 0xfb, 0x5a, 0x56, 0xbe, 0x17, 0xef, - 0xc3, 0xaa, 0x8c, 0x1b, 0x28, 0x8d, 0xd5, 0x62, 0x7e, 0x91, 0x19, 0x8a, 0x04, 0xbf, 0xa4, 0x1c, - 0x63, 0x28, 0x46, 0x75, 0x9d, 0xbf, 0x94, 0x85, 0x89, 0x6d, 0x69, 0xa9, 0x4d, 0x7c, 0xc2, 0xa5, - 0x61, 0xfa, 0x56, 0xa6, 0xe0, 0x04, 0x27, 0x4d, 0x09, 0x33, 0x39, 0x6e, 0xfd, 0xd1, 0x2a, 0xac, - 0x48, 0xf7, 0x86, 0x07, 0x50, 0xb2, 0x43, 0x79, 0x7f, 0x25, 0x51, 0x65, 0xf2, 0x72, 0xe5, 0xde, - 0x15, 0x83, 0x68, 0xc4, 0x43, 0x58, 0xb1, 0x6c, 0xdb, 0x3c, 0x3e, 0xcb, 0x1f, 0xa2, 0xcf, 0xc8, - 0xfc, 0x7b, 0x57, 0x8c, 0x65, 0x8b, 0x84, 0xff, 0xcf, 0xa1, 0x82, 0xf4, 0xd9, 0x68, 0xc1, 0x8d, - 0x05, 0x59, 0xd4, 0x41, 0x41, 0xd9, 0x52, 0x92, 0xfa, 0xef, 0xe6, 0x8f, 0x23, 0x4a, 0x73, 0x0d, - 0x9c, 0x11, 0x2d, 0x67, 0x0e, 0x26, 0xfe, 0x1a, 0xb0, 0x7d, 0x3a, 0x11, 0x27, 0x96, 0xb3, 0xe7, - 0xb5, 0x73, 0xc2, 0xc7, 0xde, 0x15, 0x83, 0x37, 0x55, 0x25, 0x8c, 0x7c, 0xaa, 0x8e, 0x0a, 0x88, - 0x3d, 0xaf, 0x2c, 0xee, 0x19, 0xe4, 0xd1, 0xc9, 0x79, 0x01, 0x31, 0x6c, 0xcc, 0x66, 0x27, 0xee, - 0x9e, 0xab, 0x73, 0xd9, 0x12, 0x91, 0x83, 0xb2, 0x25, 0xf2, 0xc7, 0x13, 0xa8, 0xb2, 0xe5, 0x98, - 0xf3, 0x95, 0xe7, 0xba, 0x36, 0x95, 0x18, 0xe8, 0x2c, 0x32, 0x95, 0x1f, 0x5a, 0xaa, 0x9d, 0xa1, - 0x93, 0x3d, 0xee, 0xb9, 0xbd, 0xb0, 0xa3, 0x8c, 0xe4, 0xe4, 0x87, 0x1b, 0x6b, 0x70, 0x1e, 0xd1, - 0x85, 0x4d, 0x79, 0x2e, 0xc2, 0xd2, 0x81, 0xda, 0xd0, 0x61, 0x6e, 0xbc, 0x72, 0xe2, 0xc3, 0xde, - 0x15, 0x43, 0x58, 0xf3, 0x42, 0x45, 0x0b, 0x36, 0x54, 0x95, 0x68, 0xdb, 0xcf, 0x78, 0xc0, 0x65, - 0x9b, 0x94, 0x0a, 0x05, 0x7b, 0x57, 0x8c, 0x75, 0x2b, 0x0f, 0x12, 0x1d, 0xb8, 0xaa, 0x0a, 0xa1, - 0xf3, 0x81, 0x8c, 0xf3, 0x7b, 0x7e, 0x14, 0xb3, 0x82, 0xc4, 0xde, 0x15, 0x63, 0xc3, 0x9a, 0x93, - 0x2e, 0xf6, 0x55, 0x7d, 0xb2, 0x92, 0x2b, 0xaf, 0xc4, 0xbb, 0x0b, 0xbb, 0x29, 0x15, 0xa3, 0x93, - 0x9a, 0x65, 0x24, 0xeb, 0x5f, 0xa9, 0xce, 0xca, 0x3b, 0xfb, 0xca, 0xe3, 0xa4, 0xd7, 0xe7, 0x4a, - 0xcc, 0xfa, 0xfa, 0x26, 0x3d, 0x96, 0x83, 0xa6, 0xfe, 0x1c, 0xb7, 0x0c, 0xb8, 0xbe, 0x98, 0x69, - 0x65, 0x77, 0xae, 0x12, 0xef, 0x5c, 0x7a, 0x76, 0xe7, 0x9a, 0x0d, 0x63, 0x9f, 0xd9, 0xc7, 0x7e, - 0x09, 0x6b, 0x39, 0xae, 0x2d, 0xaa, 0xb0, 0xaa, 0x5e, 0x00, 0xa7, 0xd8, 0x20, 0xad, 0xfe, 0xc1, - 0xd7, 0x5a, 0x01, 0xc1, 0x9d, 0xde, 0x60, 0xd8, 0xec, 0x0d, 0xb5, 0x25, 0x4e, 0x1c, 0x74, 0x9b, - 0xad, 0xb6, 0x56, 0xd4, 0xff, 0x61, 0x11, 0x2a, 0xc9, 0x69, 0xe3, 0x6f, 0x6f, 0x13, 0x5c, 0x7c, - 0x85, 0x7d, 0x46, 0xe1, 0x65, 0xdf, 0xff, 0x92, 0x8a, 0x59, 0x9d, 0x55, 0x2b, 0xa3, 0xf9, 0xa0, - 0xa4, 0xcb, 0x3f, 0x32, 0x28, 0x69, 0xf6, 0xee, 0xd6, 0x4a, 0xfe, 0xee, 0xd6, 0xcc, 0x83, 0xf9, - 0xab, 0xf4, 0xd6, 0x72, 0xf6, 0xc1, 0xfc, 0xeb, 0xb0, 0xe2, 0x46, 0xcf, 0x5d, 0xe7, 0x5c, 0x7a, - 0xb2, 0xcb, 0x54, 0x7e, 0xd3, 0x87, 0x1f, 0xd8, 0xf4, 0x7f, 0xcc, 0x06, 0xf2, 0x08, 0x36, 0x8f, - 0xcf, 0x92, 0x17, 0x9e, 0x53, 0x13, 0x53, 0x8d, 0xaa, 0xb4, 0x10, 0x27, 0xde, 0x81, 0x95, 0x58, - 0x1d, 0x84, 0x16, 0x17, 0x9c, 0x0d, 0x1b, 0x12, 0xad, 0xff, 0x51, 0x01, 0x20, 0x3d, 0x87, 0xfb, - 0x0b, 0x1b, 0xb4, 0x33, 0x36, 0xc3, 0xe2, 0xf7, 0xd8, 0x0c, 0x7f, 0xe8, 0x01, 0x9f, 0x6f, 0xa1, - 0x92, 0x9c, 0xbc, 0xfe, 0xf6, 0x13, 0xeb, 0x27, 0x7d, 0xf2, 0x5f, 0x50, 0xc6, 0xfd, 0xe4, 0xe8, - 0xf2, 0x2f, 0xda, 0x17, 0xb9, 0xcf, 0x17, 0x7f, 0xe0, 0xf3, 0x17, 0x6c, 0x61, 0x4f, 0x3e, 0xfe, - 0x97, 0xbc, 0x9a, 0xb2, 0x13, 0xbd, 0x94, 0x9b, 0xe8, 0xfa, 0x54, 0x1e, 0x13, 0xfc, 0xc5, 0x3f, - 0xfd, 0x93, 0x1a, 0xfc, 0xbf, 0x17, 0x94, 0x2d, 0x3b, 0x79, 0x94, 0xfb, 0x95, 0x72, 0xf4, 0x62, - 0x73, 0xfc, 0x4f, 0xf9, 0xdc, 0xf7, 0x5a, 0xea, 0x4a, 0xdf, 0x67, 0xa9, 0x7b, 0x47, 0xbd, 0x69, - 0xb0, 0xfc, 0x2a, 0x2b, 0x9d, 0x7c, 0xd6, 0xe0, 0x2e, 0x54, 0xb3, 0xb6, 0x39, 0x36, 0xca, 0xb0, - 0x51, 0x85, 0xcd, 0x72, 0xba, 0xd4, 0x1b, 0xb8, 0xbd, 0x9b, 0xaa, 0x5c, 0x19, 0xcb, 0x87, 0x12, - 0xfa, 0x5f, 0x67, 0x8e, 0xfa, 0xdb, 0x76, 0xc9, 0xf7, 0x9b, 0x69, 0xf4, 0xff, 0xb7, 0x00, 0x6b, - 0x39, 0x4f, 0x8a, 0xdf, 0xe2, 0x13, 0x0b, 0xf9, 0x72, 0xf1, 0xff, 0x47, 0x7c, 0x39, 0xe7, 0xe0, - 0x5d, 0xce, 0x3b, 0x78, 0x23, 0xbb, 0xab, 0xe5, 0xb4, 0xa2, 0x45, 0xfa, 0x53, 0x61, 0xa1, 0xfe, - 0x74, 0x07, 0xc0, 0x1a, 0xd1, 0xbd, 0xb1, 0xce, 0x0e, 0xfb, 0x53, 0xaf, 0x19, 0x19, 0x88, 0xf8, - 0x02, 0x6e, 0x4a, 0xa3, 0x09, 0xf7, 0x4f, 0x70, 0x6c, 0x2a, 0xac, 0x2d, 0x8d, 0x10, 0xd7, 0x99, - 0x80, 0x46, 0xb7, 0x7f, 0xdc, 0x54, 0x58, 0xbd, 0x03, 0x6b, 0x39, 0x17, 0x15, 0xdc, 0x5b, 0x4e, - 0xbc, 0xe0, 0xc8, 0xf2, 0x64, 0x5d, 0x64, 0x4a, 0x6c, 0xc1, 0xf2, 0xf9, 0xa9, 0x13, 0x3a, 0x0b, - 0x9e, 0x9e, 0x65, 0x84, 0xfe, 0x73, 0xa8, 0x65, 0xdd, 0xe5, 0xc4, 0xfb, 0xb0, 0xec, 0xc6, 0xce, - 0x58, 0x99, 0x83, 0xae, 0xcf, 0x7b, 0xd4, 0x75, 0x62, 0x67, 0x6c, 0x30, 0x91, 0xfe, 0xc7, 0x05, - 0xd0, 0x66, 0x71, 0x14, 0x8e, 0xe6, 0x32, 0x8a, 0x9d, 0xb1, 0xaa, 0x0c, 0xa7, 0x32, 0x95, 0x5c, - 0xca, 0x55, 0x52, 0x59, 0xcd, 0x8a, 0x19, 0xab, 0xd9, 0x96, 0x12, 0x4a, 0xe6, 0xaf, 0x14, 0x31, - 0x42, 0xbc, 0x0d, 0xe5, 0xd0, 0xa1, 0x40, 0x37, 0xf6, 0x82, 0xdb, 0x6c, 0x09, 0x4e, 0xff, 0xc3, - 0x02, 0xac, 0x4a, 0xdf, 0xbe, 0x85, 0xf6, 0xb9, 0x77, 0x61, 0x35, 0x1a, 0x9d, 0x3a, 0x63, 0x4b, - 0xbd, 0xe3, 0x33, 0xe7, 0x87, 0xad, 0xf0, 0xe2, 0x0e, 0x7b, 0x3c, 0xe6, 0xed, 0x75, 0x07, 0x9e, - 0xe5, 0x1b, 0x04, 0xc7, 0xa9, 0xc6, 0x76, 0x5a, 0xbe, 0x1a, 0xc4, 0xef, 0x26, 0x00, 0x81, 0xe8, - 0xf2, 0x8f, 0xfe, 0xbb, 0xb0, 0x2a, 0x7d, 0x07, 0x17, 0x56, 0xe5, 0x0e, 0x94, 0xac, 0xf0, 0x24, - 0x5a, 0x30, 0x58, 0x04, 0xd7, 0xb7, 0x00, 0x52, 0x67, 0xc2, 0x45, 0x25, 0xe8, 0x0f, 0xa0, 0xac, - 0xfc, 0x07, 0x71, 0xfe, 0xa5, 0x9f, 0x96, 0x97, 0x79, 0xb3, 0x95, 0xf1, 0xe4, 0x73, 0xf2, 0xdd, - 0x60, 0x74, 0x46, 0x47, 0x06, 0x1f, 0x02, 0xdd, 0x6c, 0x1e, 0xce, 0x3d, 0x30, 0x91, 0x90, 0xf1, - 0x65, 0x01, 0x45, 0x24, 0x1e, 0x40, 0xc2, 0x2f, 0x7f, 0xc8, 0x5a, 0xa1, 0x37, 0xd5, 0x1d, 0x78, - 0x9a, 0x65, 0x8f, 0xa5, 0xf5, 0xb8, 0x4b, 0x2f, 0xa5, 0x15, 0xb2, 0xcf, 0xae, 0xe6, 0xea, 0x64, - 0x64, 0xc8, 0xf4, 0x3a, 0xd4, 0xb2, 0x4e, 0x4f, 0x7a, 0x13, 0x36, 0xf6, 0x9d, 0xd8, 0x42, 0xfe, - 0xa3, 0xc2, 0xee, 0xf3, 0xfc, 0xc5, 0x1f, 0xf9, 0xf9, 0x3b, 0x4b, 0x67, 0x30, 0x91, 0xfe, 0x0f, - 0x4b, 0xa0, 0xcd, 0xe2, 0xbe, 0x2f, 0x1e, 0xc0, 0x5d, 0xa8, 0xf2, 0x13, 0x3b, 0x59, 0x4b, 0x22, - 0x30, 0x28, 0x73, 0x5b, 0x84, 0x9f, 0x9f, 0x49, 0x1f, 0xda, 0xdf, 0xa3, 0xb4, 0xb8, 0xc1, 0x97, - 0xbf, 0xbd, 0x60, 0x44, 0xd3, 0xba, 0x46, 0x77, 0xbd, 0xbb, 0xc1, 0x88, 0xc2, 0x0c, 0x48, 0x83, - 0x07, 0x7b, 0xe2, 0xd6, 0x8c, 0xb2, 0xb4, 0x72, 0xd0, 0x29, 0xa6, 0xbc, 0x43, 0x12, 0x47, 0x32, - 0x16, 0x58, 0x99, 0x01, 0xc3, 0x48, 0x3d, 0x1a, 0x3e, 0xf2, 0x63, 0x19, 0xe5, 0x66, 0x25, 0x0c, - 0xce, 0x5b, 0x3e, 0x45, 0x19, 0xa0, 0x27, 0x3b, 0x11, 0x53, 0x4e, 0x9f, 0x6d, 0x47, 0x14, 0x85, - 0x96, 0xe1, 0xb8, 0xa8, 0xfc, 0x60, 0x56, 0x45, 0xbe, 0x94, 0x26, 0x81, 0xc9, 0x7b, 0x84, 0xbc, - 0xff, 0x65, 0xe2, 0x07, 0x01, 0x83, 0x88, 0xe0, 0x26, 0x94, 0xbf, 0x0b, 0x7c, 0x87, 0x0c, 0x27, - 0x55, 0xaa, 0xd5, 0x2a, 0xa6, 0xf7, 0xad, 0x09, 0x6e, 0x04, 0x1e, 0x85, 0x9d, 0xe3, 0x5b, 0x92, - 0x9c, 0xd0, 0xff, 0x87, 0x02, 0x6c, 0xce, 0xf6, 0x35, 0x4d, 0xa3, 0x1a, 0x94, 0x5b, 0xfd, 0xae, - 0xd9, 0x6b, 0xee, 0xb7, 0xb5, 0x2b, 0x62, 0x1d, 0xaa, 0xfd, 0xed, 0x2f, 0xdb, 0xad, 0x21, 0x03, - 0x0a, 0x14, 0x01, 0x73, 0x60, 0xee, 0x75, 0x76, 0x76, 0xda, 0x3d, 0xd6, 0x28, 0xfa, 0xdb, 0x5f, - 0x9a, 0xdd, 0x7e, 0x4b, 0x2b, 0x22, 0x4e, 0xb9, 0x83, 0x0c, 0xb4, 0x12, 0x39, 0x8b, 0xd0, 0xa5, - 0x00, 0x4c, 0x2e, 0xb3, 0xb7, 0xfb, 0x8b, 0x81, 0xd9, 0xea, 0x0d, 0xb5, 0x15, 0x4c, 0xf5, 0x0e, - 0xbb, 0x5d, 0x4a, 0x91, 0x5b, 0x6b, 0xab, 0xbf, 0x7f, 0x60, 0xb4, 0x07, 0x03, 0x73, 0xd0, 0xf9, - 0x75, 0x5b, 0x2b, 0xd3, 0x97, 0x8d, 0xce, 0xd3, 0x4e, 0x8f, 0x01, 0x15, 0xb1, 0x0a, 0xc5, 0xfd, - 0x4e, 0x8f, 0x23, 0x7f, 0xee, 0x37, 0xbf, 0xd2, 0xaa, 0xf8, 0x63, 0x70, 0xb8, 0xaf, 0xd5, 0x44, - 0x05, 0x96, 0xbb, 0xed, 0xe7, 0xed, 0xae, 0xb6, 0xa6, 0xff, 0xf3, 0xa2, 0x92, 0x88, 0xc9, 0xdf, - 0xeb, 0xc7, 0x48, 0x81, 0x8b, 0x4e, 0x59, 0x93, 0x4e, 0x2b, 0x66, 0x3a, 0x4d, 0xbc, 0x01, 0x35, - 0xb9, 0x29, 0x64, 0xcf, 0xc9, 0xaa, 0x12, 0x46, 0x53, 0xee, 0x4d, 0x58, 0x4b, 0x5c, 0x24, 0x32, - 0x0f, 0x7d, 0xd7, 0x14, 0x70, 0xc1, 0x71, 0xcd, 0xca, 0x82, 0xe3, 0x9a, 0x89, 0x1b, 0xa3, 0xe2, - 0x8e, 0x3c, 0x97, 0x67, 0x52, 0x05, 0x21, 0xcf, 0x89, 0xd7, 0xbe, 0x06, 0x94, 0x30, 0xa7, 0xbe, - 0x7c, 0xa2, 0xa8, 0x62, 0x94, 0x11, 0x70, 0xe8, 0xbb, 0xf1, 0xac, 0x8b, 0x46, 0x65, 0xce, 0x45, - 0x23, 0xbb, 0x39, 0x43, 0x7e, 0x73, 0x7e, 0x3d, 0xd9, 0x23, 0x11, 0x59, 0xa5, 0xb3, 0x8a, 0x4a, - 0xb2, 0x0b, 0x8a, 0xf7, 0x41, 0x8c, 0xa6, 0x21, 0x3d, 0xf9, 0x91, 0x21, 0xe3, 0xa7, 0x85, 0x34, - 0x89, 0x49, 0x76, 0x45, 0x8a, 0xbc, 0x92, 0xa7, 0x26, 0xf5, 0xbc, 0x62, 0xd4, 0xf3, 0xa4, 0xe2, - 0x21, 0x5c, 0x95, 0x73, 0x3b, 0xd7, 0xb7, 0x32, 0x68, 0x03, 0xa3, 0x9a, 0x69, 0x0f, 0xeb, 0xbf, - 0x03, 0x65, 0xe5, 0xda, 0xf7, 0xfd, 0xc2, 0xee, 0x82, 0x71, 0xd5, 0xff, 0xc3, 0x25, 0xa8, 0x24, - 0x8e, 0x7e, 0x3f, 0x6a, 0x76, 0xbc, 0x86, 0x72, 0x6a, 0x74, 0x96, 0x65, 0x31, 0x65, 0x04, 0xa8, - 0x91, 0x92, 0x77, 0xf9, 0xa6, 0xa1, 0xab, 0x24, 0x36, 0x86, 0x1c, 0x86, 0x2e, 0xc5, 0xf5, 0x76, - 0xfd, 0x4c, 0x78, 0x85, 0x8a, 0x51, 0x46, 0x00, 0x2d, 0xb4, 0x9b, 0x40, 0xbf, 0x29, 0x27, 0x4f, - 0x92, 0x55, 0x4c, 0x63, 0xbe, 0xeb, 0x89, 0xfe, 0x27, 0x43, 0x93, 0xc5, 0x89, 0x60, 0x21, 0x7d, - 0x8c, 0xd8, 0xb3, 0x42, 0xa6, 0xf0, 0x3b, 0x14, 0xd0, 0x87, 0xea, 0x28, 0x67, 0x04, 0x02, 0x54, - 0x1d, 0x33, 0xc3, 0x55, 0x99, 0x1d, 0xd5, 0xd9, 0x39, 0x0d, 0x73, 0x73, 0x5a, 0x8f, 0x61, 0x55, - 0x3a, 0x3b, 0x7e, 0x7f, 0x87, 0x7f, 0x6f, 0x57, 0x69, 0x50, 0xb4, 0x3c, 0x75, 0x5b, 0x1e, 0x7f, - 0xce, 0x54, 0xac, 0x34, 0x53, 0x31, 0xfd, 0xdf, 0x59, 0x02, 0x48, 0x9d, 0x26, 0xc5, 0x07, 0x33, - 0x0e, 0xda, 0x85, 0xb9, 0x6d, 0x7f, 0xc6, 0x2f, 0x7b, 0x26, 0xd0, 0xfd, 0xd2, 0x8f, 0x08, 0x74, - 0xff, 0x08, 0xd6, 0xa2, 0x70, 0xf4, 0x83, 0x36, 0xfc, 0x6a, 0x14, 0x8e, 0x12, 0x13, 0xfe, 0x87, - 0x80, 0x49, 0x7a, 0x04, 0x27, 0x55, 0x54, 0xe7, 0xa4, 0x96, 0x4a, 0x14, 0x8e, 0xfa, 0x47, 0xdf, - 0xec, 0xf0, 0x05, 0x4a, 0x3b, 0x8a, 0xcd, 0x45, 0x5c, 0x62, 0xdd, 0x8e, 0xe2, 0x9d, 0x2c, 0xa3, - 0xb8, 0x07, 0x75, 0xa4, 0x9d, 0x63, 0x16, 0x35, 0x3b, 0x4a, 0x0f, 0x94, 0xf4, 0x7f, 0x45, 0x1d, - 0xcc, 0xcf, 0x98, 0x87, 0xc5, 0x67, 0x52, 0x11, 0xcf, 0x08, 0x11, 0x8d, 0x45, 0xd6, 0x64, 0xbe, - 0x63, 0x9d, 0x90, 0x8a, 0x27, 0xb0, 0x96, 0xe0, 0x76, 0x9c, 0x63, 0x25, 0x03, 0xa9, 0x17, 0x55, - 0x32, 0x28, 0x23, 0x4f, 0x88, 0x42, 0x51, 0xfa, 0x0c, 0x11, 0xae, 0xc0, 0xe4, 0xd2, 0x52, 0x45, - 0x5e, 0x47, 0x7a, 0x02, 0x37, 0x5e, 0x61, 0x47, 0x53, 0xe7, 0x92, 0x32, 0x6e, 0x82, 0x0c, 0xc2, - 0xe6, 0x3b, 0xe7, 0x1c, 0x37, 0x41, 0xf7, 0xe1, 0xea, 0x82, 0xf7, 0xc3, 0xc5, 0x16, 0xb9, 0x6c, - 0xa5, 0xef, 0xa5, 0x4b, 0xd9, 0x6a, 0x94, 0x3e, 0x92, 0xfe, 0x29, 0xac, 0x3b, 0x9e, 0x7b, 0xe2, - 0x1e, 0xb9, 0x14, 0x1b, 0x7c, 0x14, 0x78, 0x8b, 0x5e, 0xd3, 0x37, 0xea, 0x19, 0xa2, 0x56, 0xe0, - 0xe9, 0xff, 0x5a, 0x21, 0xf7, 0x18, 0xa0, 0x7a, 0x0e, 0x8c, 0x1f, 0x03, 0x94, 0xaf, 0x0e, 0x17, - 0x0c, 0xfa, 0x8d, 0x2b, 0x53, 0x3e, 0x34, 0x2b, 0x1f, 0xf7, 0xe2, 0x94, 0xf8, 0x10, 0x36, 0x65, - 0x7c, 0x73, 0x8a, 0x2e, 0x66, 0x4e, 0x9c, 0xd0, 0x0c, 0x83, 0x73, 0x9a, 0x58, 0x05, 0x43, 0xc6, - 0x3e, 0xa7, 0x20, 0x63, 0x07, 0x4e, 0x68, 0x04, 0xe7, 0xa2, 0x41, 0x52, 0x89, 0x33, 0x8a, 0xd5, - 0x19, 0xb3, 0x4a, 0xea, 0xbf, 0x07, 0x57, 0x17, 0x5c, 0xe3, 0x13, 0x6f, 0x29, 0x3d, 0xef, 0x15, - 0x97, 0x76, 0x17, 0xea, 0x96, 0x4b, 0x33, 0xba, 0xe5, 0x83, 0x7f, 0xab, 0x00, 0x1b, 0x83, 0x38, - 0x74, 0xfd, 0x93, 0xae, 0x1b, 0x3b, 0xa1, 0xe5, 0xed, 0x06, 0xe1, 0x98, 0x1c, 0x75, 0x87, 0x46, - 0xa7, 0xf7, 0xd4, 0xec, 0x76, 0x86, 0x6d, 0xa3, 0x99, 0x04, 0x09, 0x9e, 0x47, 0x0c, 0xdb, 0x5f, - 0x0d, 0xb5, 0x82, 0xb8, 0x07, 0x5b, 0x33, 0x88, 0xed, 0x4e, 0xaf, 0x69, 0x7c, 0x6d, 0x76, 0x7a, - 0x43, 0xa3, 0xbf, 0x73, 0xd8, 0x6a, 0x1b, 0xda, 0x92, 0xb8, 0x0e, 0x62, 0x86, 0x6a, 0xaf, 0xfd, - 0x95, 0x56, 0x5c, 0x00, 0xdf, 0xee, 0x0c, 0xb5, 0xd2, 0x83, 0x7d, 0xb8, 0xda, 0x3c, 0x39, 0x09, - 0x9d, 0x13, 0xe4, 0xdb, 0x14, 0x90, 0x52, 0x79, 0x4b, 0x37, 0x9f, 0x3e, 0x35, 0x5b, 0xfd, 0xde, - 0x6e, 0xe7, 0xa9, 0xaa, 0xda, 0x1b, 0xf0, 0x7a, 0x06, 0xf8, 0xd4, 0xe8, 0x1f, 0x1e, 0x60, 0xa2, - 0xd5, 0x1c, 0x9a, 0x7d, 0x63, 0xa7, 0x6d, 0x68, 0x85, 0x07, 0x6f, 0x40, 0x4d, 0x85, 0xa2, 0x4f, - 0x62, 0x2a, 0x07, 0xbe, 0xa3, 0x5d, 0x41, 0x89, 0xa2, 0xfb, 0xdd, 0x27, 0x5a, 0xe1, 0x81, 0x0e, - 0xd5, 0xc1, 0xe9, 0xf4, 0xf8, 0xd8, 0x73, 0x14, 0xc5, 0x9e, 0x15, 0x9d, 0xb2, 0x07, 0x15, 0xbd, - 0xe0, 0xa7, 0x15, 0x1e, 0xbc, 0x8d, 0x7a, 0x1f, 0xd1, 0xec, 0x3b, 0xf1, 0x69, 0x60, 0x0b, 0x80, - 0x95, 0x5e, 0x10, 0x8e, 0x2d, 0x4f, 0xd2, 0x39, 0xd3, 0x08, 0xe9, 0x3e, 0x84, 0x6b, 0x99, 0xb2, - 0x76, 0x83, 0x90, 0x02, 0xe3, 0xb4, 0x7a, 0x74, 0x9b, 0xd2, 0x1d, 0x4f, 0x3c, 0x87, 0xef, 0x05, - 0xee, 0x5d, 0x1e, 0x85, 0xae, 0xad, 0x15, 0x1e, 0x3c, 0x81, 0x4a, 0xfa, 0x48, 0x28, 0x45, 0x23, - 0xdf, 0xee, 0x1f, 0xf6, 0x76, 0x28, 0xdc, 0x31, 0x45, 0x20, 0x6f, 0x75, 0x0f, 0x07, 0x9d, 0xe7, - 0x52, 0x1c, 0x6b, 0x7f, 0xa5, 0x92, 0x4b, 0x0f, 0xfe, 0xb0, 0xa0, 0x62, 0x69, 0xa9, 0x6a, 0x77, - 0xfb, 0xcd, 0x1d, 0x96, 0xe3, 0x92, 0x40, 0xeb, 0xc3, 0x6d, 0xad, 0x80, 0x00, 0xa3, 0x3d, 0x38, - 0xec, 0x0e, 0x55, 0x0c, 0xf7, 0x3a, 0x40, 0xa7, 0xd5, 0xde, 0x6e, 0x1b, 0x4f, 0x91, 0xa0, 0x88, - 0xe9, 0xfd, 0x7e, 0xef, 0x69, 0x7f, 0x67, 0x1b, 0xd3, 0x14, 0xbd, 0x7d, 0xa7, 0x39, 0x6c, 0x0e, - 0x86, 0x46, 0xbb, 0xb9, 0x8f, 0x20, 0xba, 0xb0, 0xa4, 0x1c, 0xc5, 0x86, 0xdb, 0x2c, 0xd3, 0x3d, - 0x6b, 0xee, 0x3e, 0x6b, 0x62, 0x6a, 0xf5, 0xc1, 0x7f, 0xbf, 0x04, 0xda, 0x6c, 0xbc, 0x34, 0xf1, - 0x1a, 0xdc, 0xa0, 0x52, 0xcd, 0x03, 0xa3, 0xbd, 0xd3, 0x69, 0xb1, 0xeb, 0xf9, 0xf3, 0x66, 0xb7, - 0xb3, 0xc3, 0xb3, 0x6a, 0x16, 0xd9, 0xec, 0xed, 0x70, 0x1c, 0xe7, 0x59, 0x44, 0xfb, 0x57, 0x87, - 0xcd, 0xae, 0xb6, 0x24, 0x5e, 0x87, 0x9b, 0xb3, 0xa8, 0x5e, 0x7f, 0x28, 0xd1, 0x45, 0xd1, 0x80, - 0xcd, 0x59, 0x74, 0xb7, 0x3d, 0x40, 0xe9, 0xf4, 0x0e, 0xdc, 0x5a, 0x84, 0x91, 0x39, 0x97, 0x17, - 0xd5, 0xf4, 0x29, 0x09, 0xb7, 0x86, 0xb6, 0x82, 0x93, 0xec, 0x15, 0x48, 0x99, 0x7f, 0x15, 0xe7, - 0xf2, 0x7c, 0x4b, 0xb5, 0xf2, 0xc2, 0x1e, 0x18, 0x98, 0x14, 0xb5, 0xbb, 0x22, 0xee, 0xc2, 0x6b, - 0x8b, 0x90, 0xfd, 0x21, 0x13, 0xc0, 0x83, 0x7f, 0x52, 0x80, 0xc6, 0xab, 0x42, 0x6a, 0x08, 0x1d, - 0xee, 0x18, 0x87, 0x3d, 0x8a, 0xc1, 0xce, 0xe1, 0xe2, 0xcd, 0x67, 0xed, 0xaf, 0x67, 0xc2, 0x65, - 0xbf, 0x0e, 0x37, 0x17, 0xd0, 0x18, 0xcd, 0x17, 0xe6, 0xf3, 0x8f, 0xb5, 0x82, 0x78, 0x0f, 0xde, - 0x59, 0x80, 0xde, 0xed, 0xf6, 0x9b, 0x43, 0xf3, 0xd7, 0x6d, 0xa3, 0x6f, 0xb6, 0xba, 0xfd, 0x41, - 0x7b, 0x07, 0x89, 0x97, 0xc4, 0x16, 0xdc, 0x5e, 0x40, 0x3c, 0xe0, 0x08, 0xff, 0xcf, 0x3f, 0xd6, - 0x8a, 0xe2, 0x2d, 0x78, 0xe3, 0xd5, 0x14, 0xbb, 0x28, 0xff, 0x3f, 0xff, 0x58, 0x2b, 0x3d, 0xf8, - 0x25, 0x34, 0x5e, 0x75, 0xaf, 0x13, 0x17, 0x52, 0x6b, 0xaf, 0x49, 0x77, 0x67, 0x51, 0x67, 0xe8, - 0x9b, 0x9c, 0xa2, 0x93, 0x0d, 0xa3, 0xdd, 0x6d, 0xd3, 0xe5, 0x87, 0x07, 0xbf, 0x29, 0x64, 0xf4, - 0x67, 0x75, 0x37, 0x2f, 0x01, 0xc8, 0xd5, 0x9d, 0x05, 0x19, 0x8e, 0x65, 0x6b, 0x05, 0x1c, 0xa5, - 0x1c, 0xa8, 0x1b, 0x8c, 0x2c, 0x4f, 0x5b, 0xa2, 0x6b, 0x0e, 0x0a, 0x4e, 0x37, 0xdb, 0xb5, 0x22, - 0x76, 0x5d, 0x02, 0xeb, 0x06, 0xe7, 0x07, 0xa1, 0x1b, 0x84, 0x6e, 0x7c, 0xc9, 0xe8, 0xd2, 0x83, - 0x7f, 0x51, 0x3a, 0xe3, 0xe4, 0x76, 0x55, 0xfc, 0x40, 0xd3, 0xb6, 0x53, 0x18, 0x09, 0x72, 0x3c, - 0xd7, 0x49, 0x8a, 0x9d, 0x41, 0x14, 0x70, 0x7e, 0x28, 0x23, 0xdf, 0x2c, 0x72, 0x09, 0x91, 0x86, - 0x43, 0x0e, 0xf2, 0x73, 0xc8, 0xe2, 0xf6, 0xb3, 0x7f, 0xfc, 0x67, 0x77, 0x0a, 0x7f, 0xf2, 0x67, - 0x77, 0x0a, 0xff, 0xeb, 0x9f, 0xdd, 0xb9, 0xf2, 0x37, 0xfe, 0xfc, 0x4e, 0xe1, 0x8f, 0xff, 0xfc, - 0x4e, 0xe1, 0x4f, 0xfe, 0xfc, 0x4e, 0xe1, 0x7f, 0xfa, 0xf3, 0x3b, 0x85, 0x5f, 0x7f, 0x70, 0xe2, - 0xc6, 0xa7, 0xd3, 0xa3, 0x87, 0xa3, 0x60, 0xfc, 0xe1, 0xd8, 0x8a, 0x43, 0xf7, 0x82, 0x45, 0x6b, - 0x95, 0xf0, 0x9d, 0x0f, 0x27, 0x67, 0x27, 0x1f, 0x4e, 0x8e, 0x3e, 0xc4, 0x0d, 0xe4, 0x68, 0x65, - 0x12, 0x06, 0x71, 0xf0, 0xf8, 0xff, 0x0b, 0x00, 0x00, 0xff, 0xff, 0x4b, 0xcf, 0xdb, 0xf6, 0x29, - 0xb4, 0x00, 0x00, + // 18144 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xcc, 0xbd, 0x5d, 0x8c, 0x1b, 0x57, + 0x97, 0x20, 0x26, 0x36, 0xc9, 0x6e, 0xf2, 0x90, 0xcd, 0xae, 0xbe, 0x6a, 0x49, 0x2d, 0x59, 0x96, + 0xe4, 0xb2, 0x6c, 0xcb, 0xb2, 0x2d, 0xdb, 0x92, 0x7f, 0x64, 0xcf, 0x37, 0xf3, 0x7d, 0x6c, 0x36, + 0x5b, 0x4d, 0x8b, 0x4d, 0xf6, 0x57, 0x64, 0x4b, 0xf6, 0x37, 0x3b, 0x28, 0x54, 0xb3, 0xaa, 0xbb, + 0xcb, 0x5d, 0xac, 0xa2, 0xab, 0x8a, 0xea, 0x6e, 0x03, 0x41, 0x3e, 0xe4, 0x21, 0x93, 0x09, 0xf2, + 0xb0, 0xc0, 0x20, 0x9b, 0x87, 0x00, 0xc1, 0xec, 0x02, 0x8b, 0x00, 0x9b, 0x04, 0x41, 0x82, 0x45, + 0x26, 0x3f, 0xd8, 0x24, 0x9b, 0x5d, 0x20, 0x0b, 0x24, 0x08, 0x06, 0xd9, 0x24, 0x93, 0x64, 0x81, + 0xdd, 0x64, 0xe6, 0x3d, 0x09, 0x90, 0xbc, 0x24, 0x2f, 0x09, 0xce, 0x39, 0xf7, 0xd6, 0x0f, 0x49, + 0x59, 0xf6, 0x37, 0x33, 0x40, 0x5e, 0xba, 0x79, 0xcf, 0x39, 0xf7, 0xd6, 0xfd, 0x3d, 0xf7, 0x9c, + 0x73, 0xcf, 0x3d, 0x17, 0x60, 0xe2, 0x59, 0xfe, 0x83, 0x49, 0x18, 0xc4, 0x81, 0x28, 0xe1, 0xef, + 0x1b, 0x1f, 0x1c, 0xbb, 0xf1, 0xc9, 0xf4, 0xf0, 0xc1, 0x28, 0x18, 0x7f, 0x78, 0x1c, 0x1c, 0x07, + 0x1f, 0x12, 0xf2, 0x70, 0x7a, 0x44, 0x29, 0x4a, 0xd0, 0x2f, 0xce, 0x74, 0x03, 0xbc, 0x60, 0x74, + 0x2a, 0x7f, 0xaf, 0xc5, 0xee, 0xd8, 0x89, 0x62, 0x6b, 0x3c, 0x61, 0x80, 0xfe, 0xbf, 0x15, 0xa0, + 0x34, 0xbc, 0x98, 0x38, 0xa2, 0x01, 0x4b, 0xae, 0xbd, 0x59, 0xb8, 0x53, 0xb8, 0x57, 0x36, 0x96, + 0x5c, 0x5b, 0xdc, 0x81, 0x9a, 0x1f, 0xc4, 0xbd, 0xa9, 0xe7, 0x59, 0x87, 0x9e, 0xb3, 0xb9, 0x74, + 0xa7, 0x70, 0xaf, 0x62, 0x64, 0x41, 0xe2, 0x35, 0xa8, 0x5a, 0xd3, 0x38, 0x30, 0x5d, 0x7f, 0x14, + 0x6e, 0x16, 0x09, 0x5f, 0x41, 0x40, 0xc7, 0x1f, 0x85, 0x62, 0x03, 0xca, 0x67, 0xae, 0x1d, 0x9f, + 0x6c, 0x96, 0xa8, 0x44, 0x4e, 0x20, 0x34, 0x1a, 0x59, 0x9e, 0xb3, 0x59, 0x66, 0x28, 0x25, 0x10, + 0x1a, 0xd3, 0x47, 0x96, 0xef, 0x14, 0xee, 0x55, 0x0d, 0x4e, 0x88, 0x5b, 0x00, 0x8e, 0x3f, 0x1d, + 0xbf, 0xb0, 0xbc, 0xa9, 0x13, 0x6d, 0xae, 0x10, 0x2a, 0x03, 0x11, 0x9b, 0xb0, 0x32, 0x3a, 0xb1, + 0xc2, 0xc8, 0x89, 0x37, 0x2b, 0x77, 0x0a, 0xf7, 0x56, 0x0d, 0x95, 0xc4, 0x8a, 0x4d, 0x2c, 0xdb, + 0x8c, 0x26, 0xd6, 0xc8, 0xd9, 0xac, 0x72, 0xc5, 0x26, 0x96, 0x3d, 0xc0, 0xb4, 0xfe, 0x73, 0xa8, + 0x8e, 0xa3, 0xe3, 0x5d, 0xc7, 0xb2, 0x9d, 0x50, 0x5c, 0x83, 0x95, 0x71, 0x74, 0x6c, 0xc6, 0xd6, + 0xb1, 0x6c, 0xf9, 0xf2, 0x38, 0x3a, 0x1e, 0x5a, 0xc7, 0xe2, 0x3a, 0x54, 0x08, 0x71, 0x31, 0xe1, + 0xa6, 0x97, 0x0d, 0x24, 0xc4, 0x8e, 0xd2, 0xff, 0x64, 0x05, 0x56, 0xba, 0x6e, 0xec, 0x84, 0x96, + 0x27, 0xae, 0xc2, 0xb2, 0x1b, 0xf9, 0x53, 0xcf, 0xa3, 0xec, 0x15, 0x43, 0xa6, 0xc4, 0x55, 0x28, + 0xbb, 0x8f, 0x5f, 0x58, 0x1e, 0xe7, 0xdd, 0xbd, 0x64, 0x70, 0x52, 0x6c, 0xc2, 0xb2, 0xfb, 0xf1, + 0x67, 0x88, 0x28, 0x4a, 0x84, 0x4c, 0x13, 0xe6, 0xd1, 0x43, 0xc4, 0x94, 0x12, 0x0c, 0xa5, 0x09, + 0xf3, 0xd9, 0x27, 0x88, 0xc1, 0x4e, 0x2b, 0x12, 0x86, 0xd2, 0xf8, 0x95, 0x29, 0x7d, 0x05, 0xfb, + 0x6d, 0x15, 0xbf, 0x32, 0x55, 0x5f, 0x99, 0xf2, 0x57, 0x56, 0x24, 0x42, 0xa6, 0x09, 0xc3, 0x5f, + 0xa9, 0x24, 0x98, 0xe4, 0x2b, 0x53, 0xfe, 0x0a, 0x76, 0x58, 0x89, 0x30, 0xfc, 0x95, 0x0d, 0x28, + 0xd9, 0x08, 0x87, 0x3b, 0x85, 0x7b, 0x85, 0xdd, 0x4b, 0x06, 0xa5, 0x10, 0x1a, 0x21, 0xb4, 0x86, + 0xe3, 0x82, 0xd0, 0x48, 0x42, 0x0f, 0x11, 0x5a, 0xc7, 0xde, 0x40, 0xe8, 0xa1, 0x84, 0x1e, 0x21, + 0x74, 0xf5, 0x4e, 0xe1, 0xde, 0x12, 0x42, 0x31, 0x25, 0x6e, 0xc0, 0x8a, 0x6d, 0xc5, 0x0e, 0x22, + 0x1a, 0xb2, 0xc9, 0x0a, 0x80, 0x38, 0x9c, 0xa8, 0x88, 0x5b, 0x93, 0x8d, 0x56, 0x00, 0xa1, 0x43, + 0x0d, 0xc9, 0x14, 0x5e, 0x93, 0xf8, 0x2c, 0x50, 0x7c, 0x0a, 0x75, 0xdb, 0x19, 0xb9, 0x63, 0xcb, + 0xe3, 0x36, 0xad, 0xdf, 0x29, 0xdc, 0xab, 0x3d, 0x5c, 0x7b, 0x40, 0x4b, 0x29, 0xc1, 0xec, 0x5e, + 0x32, 0x72, 0x64, 0xe2, 0x31, 0xac, 0xca, 0xf4, 0xc7, 0x0f, 0xa9, 0x63, 0x05, 0xe5, 0xd3, 0x72, + 0xf9, 0x3e, 0x7e, 0xf8, 0x78, 0xf7, 0x92, 0x91, 0x27, 0x14, 0x77, 0xa1, 0x9e, 0xac, 0x2c, 0xcc, + 0x78, 0x59, 0xd6, 0x2a, 0x07, 0xc5, 0x66, 0x7d, 0x1b, 0x05, 0x3e, 0x12, 0x6c, 0xc8, 0x7e, 0x53, + 0x00, 0x71, 0x07, 0xc0, 0x76, 0x8e, 0xac, 0xa9, 0x17, 0x23, 0xfa, 0x8a, 0xec, 0xc0, 0x0c, 0x4c, + 0xdc, 0x82, 0xea, 0x74, 0x82, 0xad, 0x7c, 0x66, 0x79, 0x9b, 0x57, 0x25, 0x41, 0x0a, 0xc2, 0xd2, + 0x71, 0x79, 0x20, 0xf6, 0x9a, 0x1c, 0x5d, 0x05, 0xc0, 0xe1, 0x7d, 0xe1, 0x8c, 0x10, 0xb5, 0x29, + 0x3f, 0x2c, 0xd3, 0xb8, 0xf8, 0xdc, 0x68, 0xcb, 0xf5, 0x37, 0xaf, 0xd3, 0x0c, 0xe6, 0x84, 0xb8, + 0x09, 0xc5, 0x28, 0x1c, 0x6d, 0xde, 0xa0, 0xf6, 0x03, 0xb7, 0xbf, 0x7d, 0x3e, 0x09, 0x0d, 0x04, + 0x8b, 0x37, 0x61, 0xd5, 0x8d, 0xcc, 0xc8, 0x09, 0x5d, 0xcb, 0x73, 0xbf, 0x77, 0xec, 0xcd, 0xd7, + 0x28, 0x6f, 0xdd, 0x8d, 0x06, 0x09, 0x4c, 0x7c, 0x09, 0x75, 0x8f, 0x97, 0x89, 0x79, 0x14, 0x84, + 0xe3, 0xcd, 0x9b, 0x77, 0x0a, 0xf7, 0x1a, 0x0f, 0xaf, 0x71, 0x59, 0x83, 0x38, 0x74, 0xfd, 0x63, + 0xb9, 0x8c, 0x76, 0x82, 0x70, 0x6c, 0xd4, 0xbc, 0x34, 0x81, 0x1f, 0x88, 0x88, 0xc2, 0x8c, 0x82, + 0x69, 0x38, 0x72, 0x36, 0x5f, 0xa7, 0x15, 0x5e, 0x67, 0xe0, 0x80, 0x60, 0xe2, 0xe7, 0x70, 0x53, + 0x0e, 0x82, 0xa9, 0x3e, 0x14, 0x3a, 0xdf, 0x4d, 0xdd, 0xd0, 0x89, 0xcc, 0x17, 0x8f, 0x1f, 0x6e, + 0xde, 0xa2, 0x4a, 0x5d, 0x97, 0x34, 0xf2, 0x5b, 0x86, 0xa4, 0x78, 0xf6, 0xf8, 0xe1, 0xd6, 0x0a, + 0x94, 0x89, 0x97, 0xe8, 0x37, 0xa1, 0xb2, 0x6f, 0x85, 0xd6, 0xd8, 0x70, 0x8e, 0x84, 0x06, 0xc5, + 0x49, 0x10, 0x49, 0x76, 0x80, 0x3f, 0xf5, 0x2e, 0x2c, 0x3f, 0xb3, 0x42, 0xc4, 0x09, 0x28, 0xf9, + 0xd6, 0xd8, 0x21, 0x64, 0xd5, 0xa0, 0xdf, 0xc8, 0x02, 0xa2, 0x8b, 0x28, 0x76, 0xc6, 0x92, 0x45, + 0xca, 0x14, 0xc2, 0x8f, 0xbd, 0xe0, 0x50, 0x2e, 0xf5, 0x8a, 0x21, 0x53, 0xfa, 0xbf, 0x50, 0x80, + 0xe5, 0x56, 0xe0, 0x61, 0x71, 0xd7, 0x60, 0x25, 0x74, 0x3c, 0x33, 0xfd, 0xdc, 0x72, 0xe8, 0x78, + 0xfb, 0x41, 0x84, 0x88, 0x51, 0xc0, 0x08, 0x66, 0x3e, 0xcb, 0xa3, 0x80, 0x10, 0xaa, 0x02, 0xc5, + 0x4c, 0x05, 0xae, 0x43, 0x25, 0x3e, 0xf4, 0x4c, 0x82, 0x97, 0x08, 0xbe, 0x12, 0x1f, 0x7a, 0x3d, + 0x44, 0x5d, 0x83, 0x15, 0xfb, 0x90, 0x31, 0x65, 0xc2, 0x2c, 0xdb, 0x87, 0x88, 0xd0, 0xbf, 0x80, + 0xaa, 0x61, 0x9d, 0xc9, 0x6a, 0x5c, 0x81, 0x65, 0x2c, 0x40, 0x72, 0xff, 0x92, 0x51, 0x8e, 0x0f, + 0xbd, 0x8e, 0x8d, 0x60, 0xac, 0x84, 0x6b, 0x53, 0x1d, 0x4a, 0x46, 0x79, 0x14, 0x78, 0x1d, 0x5b, + 0x1f, 0x02, 0xb4, 0x82, 0x30, 0xfc, 0x8d, 0x9b, 0xb0, 0x01, 0x65, 0xdb, 0x99, 0xc4, 0x27, 0xcc, + 0x01, 0x0d, 0x4e, 0xe8, 0xf7, 0xa1, 0x82, 0xd3, 0xab, 0xeb, 0x46, 0xb1, 0xb8, 0x05, 0x25, 0xcf, + 0x8d, 0xe2, 0xcd, 0xc2, 0x9d, 0xe2, 0xcc, 0xe4, 0x23, 0xb8, 0x7e, 0x07, 0x2a, 0x7b, 0xd6, 0xf9, + 0x33, 0x1c, 0x39, 0x2c, 0x8d, 0x86, 0x50, 0x0e, 0x89, 0x1c, 0xcf, 0x3a, 0xc0, 0xd0, 0x0a, 0x8f, + 0x9d, 0x98, 0x18, 0xf6, 0xff, 0x59, 0x80, 0xda, 0x60, 0x7a, 0xf8, 0xdd, 0xd4, 0x09, 0x2f, 0xb0, + 0xce, 0xf7, 0xa0, 0x18, 0x5f, 0x4c, 0x28, 0x47, 0xe3, 0xe1, 0x55, 0x39, 0x1f, 0x53, 0xfc, 0x03, + 0xcc, 0x64, 0x20, 0x09, 0x36, 0xc2, 0x0f, 0x6c, 0x47, 0xf5, 0x41, 0xd9, 0x58, 0xc6, 0x64, 0xc7, + 0xc6, 0xcd, 0x32, 0x98, 0xc8, 0x51, 0x58, 0x0a, 0x26, 0xe2, 0x0e, 0x94, 0x47, 0x27, 0xae, 0x67, + 0xd3, 0x00, 0xe4, 0xeb, 0xcc, 0x08, 0x1c, 0xa5, 0x30, 0x38, 0x33, 0x23, 0xf7, 0x7b, 0xb5, 0xf9, + 0xad, 0x84, 0xc1, 0xd9, 0xc0, 0xfd, 0xde, 0xd1, 0x87, 0x72, 0x07, 0x06, 0x58, 0x1e, 0xb4, 0x9a, + 0xdd, 0xa6, 0xa1, 0x5d, 0xc2, 0xdf, 0xed, 0xaf, 0x3b, 0x83, 0xe1, 0x40, 0x2b, 0x88, 0x06, 0x40, + 0xaf, 0x3f, 0x34, 0x65, 0x7a, 0x49, 0x2c, 0xc3, 0x52, 0xa7, 0xa7, 0x15, 0x91, 0x06, 0xe1, 0x9d, + 0x9e, 0x56, 0x12, 0x2b, 0x50, 0x6c, 0xf6, 0xbe, 0xd1, 0xca, 0xf4, 0xa3, 0xdb, 0xd5, 0x96, 0xf5, + 0xff, 0x67, 0x09, 0xaa, 0xfd, 0xc3, 0x6f, 0x9d, 0x51, 0x8c, 0x6d, 0xc6, 0x59, 0xea, 0x84, 0x2f, + 0x9c, 0x90, 0x9a, 0x5d, 0x34, 0x64, 0x0a, 0x1b, 0x62, 0x1f, 0x52, 0xe3, 0x8a, 0xc6, 0x92, 0x7d, + 0x48, 0x74, 0xa3, 0x13, 0x67, 0x6c, 0x51, 0xe3, 0x90, 0x8e, 0x52, 0xb8, 0x2a, 0x82, 0xc3, 0x6f, + 0xa9, 0x79, 0x45, 0x03, 0x7f, 0x8a, 0xdb, 0x50, 0xe3, 0x32, 0xb2, 0xf3, 0x0b, 0x18, 0x34, 0x3b, + 0xf9, 0x96, 0xb3, 0x93, 0x8f, 0x72, 0x52, 0xa9, 0x8c, 0x94, 0x3b, 0x3b, 0x83, 0x7a, 0x72, 0x46, + 0x07, 0x87, 0xdf, 0x32, 0xb6, 0xc2, 0x33, 0x3a, 0x38, 0xfc, 0x96, 0x50, 0xef, 0xc1, 0x7a, 0x34, + 0x3d, 0x8c, 0x46, 0xa1, 0x3b, 0x89, 0xdd, 0xc0, 0x67, 0x9a, 0x2a, 0xd1, 0x68, 0x59, 0x04, 0x11, + 0xdf, 0x83, 0xca, 0x64, 0x7a, 0x68, 0xba, 0xfe, 0x51, 0x40, 0xbb, 0x57, 0xed, 0xe1, 0x2a, 0x0f, + 0xcc, 0xfe, 0xf4, 0xb0, 0xe3, 0x1f, 0x05, 0xc6, 0xca, 0x84, 0x7f, 0x08, 0x1d, 0x56, 0xfd, 0x20, + 0x36, 0x51, 0x50, 0x32, 0xc7, 0x4e, 0x6c, 0xd1, 0xb6, 0xc6, 0xe2, 0x4e, 0x37, 0x18, 0x9d, 0xee, + 0x39, 0xb1, 0x25, 0xee, 0x43, 0x25, 0xf2, 0xad, 0x49, 0x74, 0x12, 0xc4, 0xb4, 0xbf, 0xd5, 0x1e, + 0x36, 0xe4, 0xdc, 0x91, 0x50, 0x23, 0xc1, 0xeb, 0x6f, 0xc3, 0x8a, 0xfc, 0x06, 0x0a, 0x23, 0xb1, + 0xe3, 0x5b, 0x7e, 0x6c, 0x26, 0xe2, 0x55, 0x85, 0x01, 0x1d, 0x5b, 0xff, 0xbb, 0x05, 0xd0, 0x06, + 0x99, 0x6a, 0xd3, 0x87, 0x16, 0x71, 0x99, 0xd7, 0x01, 0xac, 0xd1, 0x28, 0x98, 0x72, 0x31, 0x3c, + 0x19, 0xab, 0x12, 0xd2, 0xb1, 0xb3, 0x7d, 0x5d, 0xcc, 0xf5, 0xf5, 0x1b, 0x50, 0x57, 0xf9, 0x32, + 0x0c, 0xa2, 0x26, 0x61, 0xaa, 0xb7, 0xa3, 0x69, 0x8e, 0x4b, 0xac, 0x44, 0x53, 0xce, 0x7d, 0x15, + 0x96, 0x49, 0x16, 0x8b, 0xd4, 0x08, 0x72, 0x4a, 0xff, 0xd3, 0x02, 0xac, 0x76, 0x7c, 0xdb, 0x39, + 0x1f, 0x8c, 0x2c, 0x5f, 0x75, 0xa0, 0x1b, 0x99, 0x2e, 0xc2, 0xcc, 0x68, 0x64, 0xf9, 0x52, 0x1e, + 0xaa, 0xb9, 0x51, 0x42, 0x87, 0x6d, 0x60, 0x02, 0xfa, 0xd4, 0x12, 0x95, 0x58, 0x25, 0x08, 0x7d, + 0xec, 0x6d, 0x58, 0x3b, 0x74, 0xbc, 0xc0, 0x3f, 0x36, 0xe3, 0xc0, 0x64, 0x79, 0x90, 0xdb, 0xb2, + 0xca, 0xe0, 0x61, 0x30, 0x24, 0xb9, 0x70, 0x03, 0xca, 0x13, 0x2b, 0x8c, 0xa3, 0xcd, 0xd2, 0x9d, + 0x22, 0x2e, 0x79, 0x4a, 0x60, 0x37, 0xbb, 0x91, 0x39, 0xf5, 0xdd, 0xef, 0xa6, 0xdc, 0x8c, 0x8a, + 0x51, 0x71, 0xa3, 0x03, 0x4a, 0x8b, 0x7b, 0xa0, 0xf1, 0x97, 0xa9, 0xd8, 0xec, 0x9c, 0x6c, 0x10, + 0x9c, 0x0a, 0x26, 0xc6, 0xf8, 0xdf, 0x15, 0xa1, 0xb2, 0x33, 0xf5, 0x47, 0x38, 0x18, 0xe2, 0x4d, + 0x28, 0x1d, 0x4d, 0xfd, 0x11, 0xb5, 0x25, 0x91, 0x1e, 0x92, 0x35, 0x65, 0x10, 0x12, 0xb9, 0x95, + 0x15, 0x1e, 0x23, 0x97, 0x9b, 0xe3, 0x56, 0x08, 0xa7, 0x91, 0x3b, 0x3e, 0x36, 0x47, 0x81, 0x7f, + 0xe4, 0x1e, 0x53, 0x8b, 0xea, 0x46, 0xd5, 0x3a, 0x3e, 0x6e, 0x11, 0x40, 0x34, 0x61, 0x2d, 0x45, + 0xb3, 0xbc, 0x59, 0x22, 0xc6, 0x74, 0x9d, 0x4b, 0x6a, 0x1e, 0x1f, 0x87, 0xce, 0xb1, 0x15, 0x3b, + 0x4c, 0x4f, 0xbc, 0x69, 0x35, 0xc9, 0x4e, 0x7c, 0xe3, 0x23, 0xd8, 0x88, 0x2e, 0xfc, 0xd8, 0x3a, + 0x37, 0x9d, 0xf3, 0x89, 0xe7, 0x8e, 0xdc, 0xd8, 0x1c, 0x59, 0x51, 0x2c, 0x7b, 0x41, 0x30, 0xae, + 0x2d, 0x51, 0x2d, 0x2b, 0x8a, 0xc5, 0x27, 0x50, 0x0f, 0x83, 0x69, 0xec, 0xfa, 0x8e, 0x39, 0xb2, + 0x3c, 0x96, 0x1f, 0x6b, 0x0f, 0xd7, 0xf9, 0x8b, 0x06, 0x63, 0x5a, 0x96, 0xe7, 0x19, 0xb5, 0x30, + 0x4d, 0xe8, 0xff, 0x61, 0x81, 0xfb, 0x66, 0xc7, 0xb3, 0x8e, 0x45, 0x05, 0x4a, 0xbd, 0x7e, 0xaf, + 0xad, 0x5d, 0x12, 0x75, 0xa8, 0x74, 0x7a, 0xc3, 0xb6, 0xd1, 0x6b, 0x76, 0xb5, 0x02, 0x31, 0xb1, + 0x61, 0x73, 0xab, 0xdb, 0xd6, 0x96, 0x10, 0xf3, 0xac, 0xdf, 0x6d, 0x0e, 0x3b, 0xdd, 0xb6, 0x56, + 0x62, 0x8c, 0xd1, 0x69, 0x0d, 0xb5, 0x8a, 0xd0, 0xa0, 0xbe, 0x6f, 0xf4, 0xb7, 0x0f, 0x5a, 0x6d, + 0xb3, 0x77, 0xd0, 0xed, 0x6a, 0x9a, 0xb8, 0x0c, 0x6b, 0x09, 0xa4, 0xcf, 0xc0, 0x3b, 0x98, 0xe5, + 0x59, 0xd3, 0x68, 0x1a, 0x4f, 0xb4, 0x5f, 0x88, 0x0a, 0x14, 0x9b, 0x4f, 0x9e, 0x68, 0xbf, 0x46, + 0x7e, 0x58, 0x7d, 0xde, 0xe9, 0x99, 0xcf, 0x9a, 0xdd, 0x83, 0xb6, 0xf6, 0xeb, 0x25, 0x95, 0xee, + 0x1b, 0xdb, 0x6d, 0x43, 0xfb, 0x75, 0x49, 0xac, 0x43, 0xfd, 0x57, 0xfd, 0x5e, 0x7b, 0xaf, 0xb9, + 0xbf, 0x4f, 0x15, 0xf9, 0x75, 0x45, 0xff, 0xf3, 0x12, 0x5c, 0xdb, 0x0f, 0x9d, 0x89, 0x15, 0x3a, + 0x76, 0x6f, 0x3a, 0x76, 0x42, 0x77, 0x84, 0x4b, 0xcd, 0xb6, 0x62, 0x4b, 0xdc, 0x80, 0xca, 0x91, + 0xe5, 0x79, 0x87, 0xd6, 0xe8, 0x54, 0xce, 0xda, 0x24, 0xcd, 0x9a, 0x44, 0x68, 0x8d, 0x33, 0xfb, + 0x58, 0x85, 0x00, 0xb8, 0x93, 0xbd, 0x03, 0x6b, 0x8a, 0x50, 0x89, 0x29, 0xbc, 0xd5, 0x37, 0x14, + 0x58, 0x0a, 0x2a, 0x9f, 0xc2, 0xb5, 0x19, 0x42, 0x53, 0x6d, 0x2b, 0xac, 0x1d, 0x6d, 0xe4, 0x33, + 0xf4, 0x78, 0x93, 0x59, 0x90, 0x4d, 0x6d, 0xa9, 0xe5, 0x45, 0xd9, 0x5a, 0xbc, 0xc1, 0x7e, 0x06, + 0xd7, 0x26, 0x61, 0xf0, 0xc2, 0x8d, 0xdc, 0xc0, 0x27, 0x91, 0x28, 0x9a, 0x7a, 0x72, 0x46, 0x2c, + 0x53, 0xf5, 0xae, 0x64, 0xd0, 0x06, 0x61, 0x69, 0x52, 0x2c, 0xce, 0x37, 0x71, 0x9c, 0x90, 0x58, + 0xf4, 0xa2, 0x7c, 0xfb, 0x8e, 0x13, 0x8a, 0x6d, 0xb8, 0xfd, 0x92, 0x7c, 0x34, 0x9d, 0xb1, 0x95, + 0x15, 0xaa, 0xee, 0x6b, 0x0b, 0xf3, 0xe3, 0x14, 0xee, 0xd8, 0xa2, 0x09, 0xaf, 0xbf, 0xac, 0x14, + 0xd6, 0x23, 0xab, 0x54, 0xc6, 0x8d, 0x85, 0x65, 0x3c, 0x27, 0xe5, 0xf2, 0x07, 0x8a, 0x60, 0xa5, + 0x13, 0x7e, 0xa0, 0x88, 0x01, 0x69, 0xa2, 0x3f, 0x83, 0x0d, 0x29, 0x77, 0xda, 0xc1, 0xd8, 0x72, + 0x7d, 0x35, 0xae, 0xb5, 0xb9, 0x6d, 0x5d, 0x30, 0xdd, 0x36, 0x91, 0x71, 0xff, 0xeb, 0x7f, 0xaf, + 0x0c, 0x25, 0x44, 0x0a, 0x3d, 0x95, 0x30, 0x92, 0x5c, 0xd8, 0xce, 0xad, 0xd2, 0x3f, 0xfa, 0xa7, + 0xb7, 0x2f, 0xb1, 0x6c, 0xf1, 0x06, 0x14, 0x3d, 0x37, 0xa6, 0x49, 0x95, 0xec, 0x4b, 0x52, 0x46, + 0xdd, 0xbd, 0x64, 0x20, 0x4e, 0xdc, 0x82, 0x02, 0x0b, 0x19, 0xc9, 0x56, 0xa3, 0xa4, 0xd4, 0xdd, + 0x4b, 0x46, 0x61, 0x22, 0x6e, 0x42, 0xe1, 0x85, 0x94, 0x38, 0xea, 0x8c, 0x67, 0x39, 0x15, 0xb1, + 0x2f, 0xc4, 0x1d, 0x28, 0x8e, 0x02, 0x56, 0x1a, 0x13, 0x3c, 0x4b, 0x6d, 0x58, 0xfe, 0x28, 0xf0, + 0xc4, 0x9b, 0x50, 0x0c, 0xad, 0x33, 0xb9, 0xfa, 0x25, 0x7b, 0x4b, 0xc4, 0x42, 0x24, 0x0a, 0xad, + 0x33, 0xac, 0xc4, 0x11, 0x4d, 0x80, 0xa4, 0x12, 0x8a, 0x3f, 0xe2, 0x67, 0x8e, 0xc4, 0x1d, 0x28, + 0x9c, 0xd1, 0x00, 0x27, 0x7a, 0xd2, 0x73, 0xd7, 0xb7, 0x83, 0xb3, 0xc1, 0xc4, 0x19, 0x21, 0xc5, + 0x99, 0x78, 0x0b, 0x8a, 0xd1, 0xf4, 0x90, 0x06, 0x30, 0x61, 0x32, 0x19, 0x79, 0x0b, 0x3f, 0x14, + 0x4d, 0x0f, 0xc5, 0xdb, 0x50, 0x1a, 0x05, 0x61, 0x28, 0x77, 0x6a, 0x4d, 0x55, 0x58, 0x89, 0x9a, + 0xa8, 0x37, 0x22, 0x1e, 0x3f, 0x18, 0xcb, 0x01, 0x91, 0x44, 0xa9, 0xac, 0x87, 0x1f, 0x8c, 0xc5, + 0x5d, 0x29, 0x40, 0xe6, 0x76, 0x69, 0x25, 0x5e, 0x62, 0x39, 0x88, 0xc5, 0x41, 0x1a, 0x5b, 0xe7, + 0xa4, 0x94, 0x26, 0x44, 0x4a, 0xae, 0xc4, 0x3a, 0x8d, 0xad, 0x73, 0x71, 0x17, 0x8a, 0x2f, 0x9c, + 0x11, 0xe9, 0xa7, 0xc9, 0xd7, 0xe4, 0x20, 0x3d, 0xa3, 0xe6, 0x21, 0x9a, 0xf6, 0x89, 0xc0, 0xb3, + 0x49, 0x55, 0x4d, 0xc6, 0x72, 0x27, 0xf0, 0xec, 0x67, 0x34, 0x96, 0x84, 0x44, 0x71, 0xda, 0x9a, + 0x9e, 0xe3, 0x6a, 0xd0, 0x58, 0xf0, 0xb5, 0xa6, 0xe7, 0x1d, 0x1b, 0x05, 0x2b, 0xdf, 0x7e, 0x41, + 0x0a, 0x6a, 0xc1, 0xc0, 0x9f, 0xe2, 0x0e, 0x0a, 0x56, 0x9e, 0x33, 0x8a, 0xdd, 0x17, 0x6e, 0x7c, + 0x41, 0x2a, 0x68, 0xc1, 0xc8, 0x82, 0xc4, 0x2e, 0x68, 0x13, 0xc9, 0xcc, 0x4c, 0x9f, 0xb9, 0x19, + 0x29, 0x9c, 0xb5, 0x87, 0xaf, 0xcb, 0x69, 0xb2, 0x98, 0xd5, 0x19, 0x6b, 0x93, 0x3c, 0x62, 0x6b, + 0x19, 0x4a, 0xce, 0xf9, 0x24, 0xfc, 0xaa, 0x54, 0xd9, 0xd0, 0x36, 0xf5, 0x5d, 0x58, 0x91, 0xb5, + 0x9e, 0xb3, 0x06, 0x5d, 0x87, 0x8a, 0x1b, 0xe1, 0x2e, 0x15, 0xc5, 0x52, 0xcf, 0x59, 0x71, 0xa3, + 0x16, 0x26, 0x51, 0x5c, 0xc1, 0xc2, 0xe5, 0xd6, 0x46, 0xbf, 0xf5, 0xbf, 0x57, 0x00, 0x48, 0xfb, + 0x09, 0x1b, 0xe9, 0x39, 0xbe, 0xd2, 0xa9, 0x3c, 0xc7, 0x4f, 0x32, 0x2d, 0xa5, 0x99, 0xe6, 0xb5, + 0xca, 0xe2, 0x02, 0xad, 0x72, 0x4e, 0x33, 0x2c, 0xfd, 0x06, 0x9a, 0x61, 0xf9, 0x15, 0x9a, 0xa1, + 0x7e, 0x1d, 0xaa, 0x89, 0x61, 0x40, 0xd4, 0xa1, 0x60, 0x49, 0xb1, 0xb9, 0x60, 0xe9, 0xf7, 0x50, + 0x4f, 0x57, 0xaa, 0x7f, 0x1e, 0x87, 0x29, 0x25, 0x4c, 0x17, 0x0e, 0xf5, 0x9f, 0x41, 0x5d, 0xb2, + 0xd7, 0xc0, 0xdb, 0x76, 0x8e, 0xc4, 0xfb, 0x00, 0x49, 0x3a, 0x92, 0xda, 0x4d, 0xba, 0x2e, 0xb7, + 0x9d, 0x23, 0x23, 0x83, 0xd7, 0xff, 0x66, 0x99, 0xf4, 0xc4, 0x6d, 0x56, 0xd0, 0xa4, 0x26, 0x56, + 0xc8, 0x68, 0x62, 0x89, 0x9c, 0xb8, 0x94, 0xd7, 0x46, 0x4f, 0x5c, 0xdb, 0x76, 0x7c, 0xa5, 0x75, + 0x72, 0x0a, 0x27, 0xb2, 0xe5, 0x1d, 0x4b, 0xd1, 0x42, 0xa8, 0x8f, 0x8e, 0x27, 0xa1, 0x13, 0x45, + 0xac, 0xef, 0x58, 0xde, 0xb1, 0xe2, 0x5b, 0xe5, 0x1f, 0xe2, 0x5b, 0xd7, 0xa1, 0x82, 0xa2, 0x32, + 0x19, 0xbd, 0x78, 0x3f, 0x59, 0x91, 0x46, 0x41, 0xf1, 0x0e, 0xac, 0x48, 0x73, 0x85, 0x64, 0x18, + 0x72, 0x29, 0x6c, 0x33, 0xd0, 0x50, 0x58, 0x32, 0xdd, 0x05, 0xe3, 0xb1, 0xe3, 0xc7, 0x4a, 0xbe, + 0x97, 0x49, 0xf1, 0x1e, 0x54, 0x03, 0xdf, 0x64, 0x9b, 0x86, 0xe4, 0x18, 0x72, 0x69, 0xf6, 0xfd, + 0x03, 0x82, 0x1a, 0x95, 0x40, 0xfe, 0xc2, 0xaa, 0x78, 0xc1, 0x99, 0x39, 0xb2, 0x42, 0x9b, 0xb8, + 0x46, 0xc5, 0x58, 0xf1, 0x82, 0xb3, 0x96, 0x15, 0xda, 0xac, 0xef, 0x7c, 0xe7, 0x4f, 0xc7, 0xc4, + 0x29, 0x56, 0x0d, 0x99, 0x12, 0x37, 0xa1, 0x3a, 0xf2, 0xa6, 0x51, 0xec, 0x84, 0x5b, 0x17, 0x6c, + 0xa5, 0x32, 0x52, 0x00, 0xd6, 0x6b, 0x12, 0xba, 0x63, 0x2b, 0xbc, 0x20, 0xb6, 0x50, 0x31, 0x54, + 0x92, 0x84, 0xce, 0x53, 0xd7, 0x3e, 0x67, 0x53, 0x95, 0xc1, 0x09, 0xa4, 0x3f, 0x21, 0x43, 0x62, + 0x44, 0x6b, 0xbf, 0x62, 0xa8, 0x24, 0x8d, 0x03, 0xfd, 0xa4, 0xd5, 0x5e, 0x35, 0x64, 0x2a, 0xa7, + 0xac, 0xaf, 0xbf, 0x54, 0x59, 0x17, 0xb3, 0xfa, 0x52, 0x10, 0xba, 0xc7, 0xae, 0xd4, 0x76, 0x2e, + 0xb3, 0xbe, 0xc4, 0x20, 0x22, 0xf8, 0x1c, 0x56, 0x8f, 0x1d, 0xdf, 0x09, 0xad, 0xd8, 0xb1, 0x51, + 0x44, 0x20, 0xe3, 0x52, 0x4d, 0x0d, 0xf3, 0x13, 0x85, 0x42, 0x3e, 0x5a, 0x3f, 0xce, 0xa4, 0xb0, + 0x96, 0x52, 0x62, 0xbe, 0xc2, 0xb3, 0x85, 0x53, 0x28, 0x8a, 0xcb, 0x2f, 0x26, 0x95, 0xbd, 0xca, + 0xa2, 0x38, 0x83, 0x87, 0x5c, 0x65, 0xfd, 0x3b, 0x58, 0x91, 0x63, 0x8b, 0x62, 0x30, 0x72, 0x92, + 0xfc, 0x9e, 0xc7, 0x62, 0x30, 0xc2, 0x71, 0xdd, 0xca, 0x22, 0x79, 0xa5, 0xca, 0x59, 0x5b, 0x67, + 0x20, 0x9b, 0x83, 0x50, 0x5b, 0xc1, 0x79, 0x65, 0x5a, 0x87, 0xae, 0x87, 0xbc, 0xaf, 0x28, 0xb5, + 0xb0, 0xa9, 0xe7, 0x35, 0x19, 0xa4, 0xf7, 0xa1, 0xa2, 0x66, 0xc2, 0x5f, 0xca, 0x37, 0xf5, 0x09, + 0xd4, 0xb3, 0x3d, 0xf4, 0x97, 0xd3, 0x10, 0xd6, 0x46, 0xa2, 0x38, 0x08, 0x13, 0x36, 0x56, 0x71, + 0xa3, 0x01, 0xa5, 0xf5, 0x3f, 0x5a, 0x82, 0x1a, 0x69, 0x45, 0x7d, 0xd2, 0xf9, 0xc4, 0xfb, 0x20, + 0x46, 0xa1, 0x63, 0xc5, 0x8e, 0xe9, 0x9c, 0xc7, 0xa1, 0x25, 0x75, 0x1f, 0x16, 0x45, 0x35, 0xc6, + 0xb4, 0x11, 0xc1, 0xea, 0xcf, 0x6d, 0xa8, 0x4d, 0xac, 0x30, 0x52, 0x7a, 0x37, 0x7f, 0x1d, 0x18, + 0x24, 0xb5, 0x5e, 0xcd, 0x3f, 0x46, 0x99, 0x35, 0x0e, 0x4e, 0x1d, 0x9f, 0x2d, 0x0e, 0x6c, 0x6b, + 0x69, 0x10, 0x7c, 0x88, 0xe0, 0x81, 0xfb, 0x3d, 0x4a, 0x3b, 0x80, 0x82, 0x90, 0xec, 0x6c, 0xe6, + 0x0d, 0x37, 0xb9, 0xc1, 0x99, 0xfa, 0x3d, 0x78, 0x96, 0xd0, 0x18, 0x19, 0x7a, 0xfd, 0x57, 0x00, + 0x29, 0x46, 0xdc, 0x80, 0xab, 0xcf, 0x3a, 0x83, 0xce, 0x56, 0xa7, 0xdb, 0x19, 0x7e, 0x63, 0x1e, + 0xf4, 0x06, 0xfb, 0xed, 0x56, 0x67, 0xa7, 0xd3, 0xde, 0xd6, 0x2e, 0x89, 0xab, 0x20, 0x32, 0x38, + 0xfa, 0xd9, 0x6d, 0x6b, 0x05, 0xb1, 0x09, 0x1b, 0x19, 0x78, 0xa7, 0xa7, 0x30, 0x4b, 0xfa, 0xff, + 0x5c, 0x80, 0xd5, 0x7d, 0x5e, 0x7a, 0x4f, 0x9d, 0x8b, 0x6d, 0x36, 0xbd, 0x8d, 0x14, 0xdb, 0x2c, + 0x19, 0xf4, 0x5b, 0xdc, 0x82, 0xda, 0xe4, 0xd4, 0xb9, 0x30, 0x73, 0x66, 0xaa, 0x2a, 0x82, 0x5a, + 0xc4, 0x20, 0xdf, 0x85, 0xe5, 0x80, 0x9a, 0x20, 0x85, 0xa8, 0xf5, 0xb9, 0xb6, 0x19, 0x92, 0x00, + 0xf5, 0xd7, 0xa4, 0xa8, 0xac, 0xa2, 0x2c, 0x0b, 0xa3, 0x8e, 0xdd, 0x80, 0x32, 0xa2, 0x50, 0xfa, + 0x26, 0xc5, 0x93, 0x12, 0xe2, 0x23, 0x58, 0x1d, 0x05, 0xe3, 0x89, 0xa9, 0xb2, 0x4b, 0x71, 0x2a, + 0xcf, 0xd8, 0x6b, 0x48, 0xb2, 0xcf, 0x65, 0xe9, 0x7f, 0x5a, 0x84, 0x0a, 0xd5, 0x41, 0xf2, 0x76, + 0xd7, 0x3e, 0x57, 0xbc, 0xbd, 0x6a, 0x94, 0x5d, 0x1b, 0xc5, 0x82, 0x57, 0xe8, 0xca, 0x89, 0x0e, + 0x5c, 0xcc, 0xea, 0xc0, 0xe9, 0x72, 0x2e, 0xe5, 0x96, 0xf3, 0x22, 0xf5, 0xb7, 0xbc, 0x48, 0xfd, + 0xc5, 0xc9, 0xc5, 0x34, 0xce, 0xb9, 0x9b, 0xe8, 0x0b, 0x40, 0xa0, 0x36, 0x42, 0xb2, 0x9c, 0x7b, + 0x25, 0xcf, 0xb9, 0x37, 0x61, 0x85, 0x26, 0x87, 0xc7, 0x36, 0x9b, 0x8a, 0xa1, 0x92, 0x99, 0x61, + 0xa8, 0xbe, 0x6a, 0x18, 0x92, 0x66, 0x5b, 0xde, 0x31, 0xdb, 0x6c, 0x54, 0xb3, 0x9b, 0xde, 0x71, + 0x20, 0x3e, 0x86, 0x2b, 0x29, 0x5a, 0xb6, 0x86, 0x54, 0x66, 0x3a, 0x85, 0x30, 0x44, 0x42, 0x49, + 0x2d, 0x22, 0xe5, 0xf8, 0x3e, 0xac, 0x67, 0xb2, 0x90, 0xee, 0x16, 0x11, 0xe3, 0xaf, 0x1a, 0x6b, + 0x09, 0x39, 0x89, 0xd5, 0x91, 0x78, 0x17, 0xfb, 0x69, 0xe4, 0x4d, 0x6d, 0x66, 0xa3, 0xd3, 0xb1, + 0x1f, 0x6d, 0xae, 0x52, 0x07, 0xaf, 0x29, 0x78, 0x8b, 0xc1, 0x5f, 0x95, 0x2a, 0x0d, 0x6d, 0xcd, + 0x68, 0xa4, 0xcb, 0xc1, 0x8c, 0x9c, 0x58, 0xff, 0x83, 0x32, 0xac, 0xee, 0x04, 0xa1, 0xe3, 0x1e, + 0xfb, 0xe9, 0xb4, 0x9d, 0xb3, 0xe5, 0xa8, 0xa9, 0xbc, 0x94, 0x99, 0xca, 0xb7, 0xa1, 0x76, 0xc4, + 0x19, 0x91, 0xe5, 0xd2, 0x7c, 0x2d, 0x19, 0x20, 0x41, 0xc3, 0x43, 0x0f, 0x59, 0xa3, 0x22, 0xa0, + 0xcc, 0x25, 0xca, 0xac, 0x32, 0xa1, 0xc4, 0x20, 0xbe, 0xa4, 0xbd, 0xd3, 0x76, 0x3c, 0x27, 0xe6, + 0xf1, 0x6d, 0x28, 0x79, 0x30, 0x57, 0xa7, 0x07, 0x86, 0x73, 0xd4, 0x24, 0x01, 0x1e, 0xb7, 0xd2, + 0x6d, 0x22, 0x97, 0x79, 0xe5, 0xbe, 0xbb, 0xfc, 0x23, 0xf3, 0x4a, 0x36, 0xfc, 0x10, 0xae, 0x84, + 0xce, 0x91, 0x13, 0x3a, 0xfe, 0xc8, 0xb1, 0xcd, 0xcc, 0xb4, 0xe5, 0x19, 0x72, 0x39, 0x45, 0x76, + 0x92, 0x09, 0xbc, 0x07, 0x5a, 0x52, 0x57, 0x93, 0x59, 0x27, 0x4d, 0x9b, 0xc6, 0xc3, 0x37, 0x7f, + 0xf0, 0xb3, 0x7d, 0x22, 0x35, 0x1a, 0xaa, 0xe2, 0x9c, 0x96, 0xc5, 0x71, 0xf5, 0x55, 0x71, 0xd5, + 0x9f, 0x54, 0x1c, 0xb7, 0x85, 0xd3, 0xfa, 0x10, 0xaa, 0x09, 0x89, 0xa8, 0x43, 0xc5, 0x68, 0x4b, + 0xcb, 0xc5, 0x25, 0x51, 0x83, 0x95, 0x56, 0x73, 0xd0, 0x6a, 0x6e, 0x23, 0x03, 0xab, 0x43, 0x65, + 0xd0, 0x1e, 0xb2, 0xb5, 0x62, 0x49, 0xac, 0x41, 0x0d, 0x53, 0xdb, 0xed, 0x9d, 0xe6, 0x41, 0x77, + 0xa8, 0x15, 0xc5, 0x2a, 0x54, 0x7b, 0x7d, 0xb3, 0xd9, 0x1a, 0x76, 0xfa, 0x3d, 0xad, 0xa4, 0x7b, + 0xb0, 0x36, 0xf3, 0x61, 0xe4, 0x9a, 0x8c, 0x36, 0xfb, 0x46, 0xe7, 0x49, 0xa7, 0x67, 0xb6, 0xbf, + 0xde, 0xef, 0x76, 0x5a, 0x1d, 0xfc, 0xd2, 0x75, 0xb8, 0x92, 0xc7, 0xa9, 0x82, 0x0b, 0x42, 0x87, + 0x5b, 0x79, 0x54, 0xb7, 0xfd, 0xa4, 0xd9, 0xfa, 0xc6, 0x6c, 0xee, 0x6d, 0x75, 0x9e, 0x1c, 0xf4, + 0x0f, 0x06, 0xda, 0x92, 0x6e, 0x42, 0xa5, 0x75, 0xe2, 0x8c, 0x4e, 0x5f, 0x36, 0x0b, 0xc9, 0x64, + 0xed, 0x8c, 0x4e, 0xa5, 0x06, 0x3a, 0x63, 0xb2, 0x76, 0x46, 0xa7, 0xb8, 0x18, 0xd5, 0x4e, 0xf7, + 0x9d, 0x27, 0x6d, 0x71, 0x55, 0xb9, 0xcd, 0x7d, 0xe7, 0xe9, 0x6d, 0xa8, 0xee, 0x5b, 0x61, 0xec, + 0x52, 0x27, 0x3d, 0x86, 0xd5, 0x24, 0xb1, 0xed, 0x1c, 0x29, 0xf1, 0x56, 0x24, 0x6a, 0x6b, 0x82, + 0x32, 0xf2, 0x84, 0xfa, 0xfb, 0x50, 0xcf, 0x02, 0xc4, 0x4d, 0x28, 0xda, 0xce, 0xd1, 0x82, 0xed, + 0x17, 0xc1, 0xfa, 0x33, 0xa8, 0xb7, 0x94, 0xb8, 0xf6, 0xb2, 0x96, 0x3d, 0x84, 0x06, 0x71, 0xe4, + 0xd1, 0xa1, 0x62, 0xc9, 0x4b, 0x0b, 0x58, 0x72, 0x1d, 0x69, 0x5a, 0x87, 0x92, 0x27, 0x7f, 0x0a, + 0xb5, 0xfd, 0x30, 0x98, 0x38, 0x61, 0x4c, 0xc5, 0x6a, 0x50, 0x3c, 0x75, 0x2e, 0x64, 0xa9, 0xf8, + 0x33, 0x3d, 0x68, 0x58, 0xca, 0x1e, 0x34, 0x3c, 0x84, 0x8a, 0xca, 0xf6, 0xa3, 0xf3, 0xfc, 0x1c, + 0xb7, 0x36, 0xca, 0xe3, 0x3a, 0x11, 0x7e, 0xec, 0x01, 0xc0, 0x24, 0x01, 0xc8, 0x8e, 0x53, 0xfa, + 0xbe, 0x2c, 0xdc, 0xc8, 0x50, 0xe8, 0xaf, 0xc3, 0xca, 0x33, 0xd7, 0x39, 0x93, 0xcd, 0x7f, 0xe1, + 0x3a, 0x67, 0xaa, 0xf9, 0xf8, 0x5b, 0xff, 0x87, 0x00, 0x15, 0xe2, 0x7f, 0xdb, 0x2f, 0x3f, 0xdb, + 0xf9, 0x29, 0xaa, 0xc3, 0x1d, 0xc9, 0xae, 0x4a, 0x0b, 0x14, 0x16, 0x66, 0x5e, 0xaf, 0x03, 0x64, + 0x78, 0x31, 0xef, 0x2c, 0xd5, 0x38, 0x61, 0xc1, 0x28, 0x73, 0x93, 0x14, 0x83, 0xd3, 0x88, 0xcd, + 0xae, 0x29, 0x40, 0x3c, 0x60, 0x89, 0x98, 0x0c, 0xad, 0xac, 0x35, 0x5c, 0x56, 0x5a, 0xfd, 0xa1, + 0xe7, 0x28, 0x5b, 0x03, 0x89, 0xc9, 0x98, 0xa0, 0x7d, 0xc6, 0x09, 0x23, 0xdc, 0x4e, 0xe4, 0xb1, + 0xbf, 0x4c, 0x8a, 0x77, 0xa0, 0x84, 0x9b, 0xb0, 0xb4, 0x0d, 0x5c, 0x56, 0x3d, 0x98, 0x91, 0x22, + 0x0c, 0x22, 0x10, 0xf7, 0x60, 0x85, 0xb8, 0x94, 0x83, 0x3b, 0x41, 0xa6, 0xb7, 0xd5, 0xa6, 0x6c, + 0x28, 0xb4, 0x78, 0x17, 0xca, 0x47, 0xa7, 0xce, 0x05, 0x6f, 0x03, 0x49, 0x99, 0x39, 0x66, 0x62, + 0x30, 0x85, 0xb8, 0x0b, 0x8d, 0xd0, 0x39, 0x32, 0xe9, 0xb4, 0x07, 0x79, 0x78, 0xb4, 0xd9, 0x20, + 0x16, 0x5d, 0x0f, 0x9d, 0xa3, 0x16, 0x02, 0x87, 0x87, 0x5e, 0x24, 0xde, 0x86, 0x65, 0x5a, 0x5c, + 0xa8, 0x30, 0x64, 0xbe, 0xac, 0x56, 0xaa, 0x21, 0xb1, 0xe2, 0x63, 0x00, 0xa9, 0x96, 0x98, 0x87, + 0x17, 0x24, 0x7c, 0x27, 0x8b, 0x29, 0x3b, 0xff, 0xb3, 0xca, 0xcb, 0x3b, 0x50, 0xc6, 0x49, 0x12, + 0x6d, 0x5e, 0xa3, 0x92, 0xd7, 0xf3, 0x33, 0x88, 0x6a, 0x4a, 0x78, 0x71, 0x0f, 0x2a, 0x38, 0x51, + 0x68, 0x55, 0x6f, 0x66, 0xf5, 0x34, 0x39, 0xab, 0x70, 0xe7, 0x76, 0xce, 0x06, 0xdf, 0x79, 0xe2, + 0x3e, 0x94, 0x6c, 0x5c, 0xcc, 0xd7, 0xa9, 0xc4, 0xab, 0x99, 0x71, 0x41, 0x2e, 0xba, 0xed, 0x1c, + 0x91, 0xea, 0x48, 0x34, 0x62, 0x17, 0x1a, 0x38, 0x8d, 0x1e, 0x92, 0x30, 0x86, 0xdd, 0xb7, 0x79, + 0x83, 0x72, 0xbd, 0x31, 0x93, 0xab, 0x27, 0x89, 0xa8, 0xb3, 0xdb, 0x7e, 0x1c, 0x5e, 0x18, 0xab, + 0x7e, 0x16, 0x26, 0x6e, 0x40, 0xc5, 0x8d, 0xba, 0xc1, 0xe8, 0x34, 0x39, 0x58, 0x4e, 0xd2, 0xe2, + 0x0b, 0x58, 0xa5, 0x89, 0x85, 0x49, 0xfc, 0xb8, 0x3c, 0x55, 0xce, 0x4e, 0x19, 0x85, 0x32, 0xf2, + 0x94, 0xb8, 0x83, 0xba, 0x91, 0x19, 0x3b, 0xe3, 0x49, 0x10, 0xa2, 0x86, 0xf7, 0xba, 0x3a, 0xa1, + 0x18, 0x2a, 0x10, 0x0a, 0x4a, 0x89, 0x47, 0x8b, 0x19, 0x1c, 0x1d, 0x45, 0x4e, 0x4c, 0xa7, 0xc8, + 0x25, 0xa3, 0xa1, 0x1c, 0x5b, 0xfa, 0x04, 0x25, 0x41, 0x25, 0x32, 0xed, 0x0b, 0xdf, 0x1a, 0xbb, + 0xa3, 0xcd, 0xdb, 0xac, 0x48, 0xba, 0xd1, 0x36, 0x03, 0xb2, 0xba, 0xdc, 0x9d, 0x9c, 0x2e, 0x77, + 0x19, 0xca, 0xf6, 0x21, 0x2e, 0xc7, 0x37, 0xa8, 0xd8, 0x92, 0x7d, 0xd8, 0xb1, 0xc5, 0x07, 0x64, + 0x65, 0x66, 0x16, 0xb8, 0xa9, 0x67, 0xad, 0x71, 0x09, 0x67, 0x34, 0x52, 0x0a, 0x71, 0x07, 0x6a, + 0x3b, 0x8e, 0x15, 0x4f, 0x43, 0x67, 0xc7, 0xb3, 0x8e, 0x37, 0xdf, 0xa4, 0x92, 0xb2, 0x20, 0xac, + 0x9d, 0x17, 0x1c, 0xbb, 0x23, 0x8b, 0x56, 0xfe, 0x5d, 0x96, 0x8b, 0x25, 0xa4, 0x63, 0xa7, 0x2a, + 0x8c, 0x25, 0x85, 0xdd, 0xb7, 0xb2, 0x2a, 0x8c, 0xe5, 0xa9, 0xe3, 0x98, 0xb4, 0x2f, 0x9c, 0x49, + 0x30, 0x3a, 0xd9, 0x7c, 0x9b, 0xd6, 0xdb, 0xaa, 0xea, 0x8a, 0x36, 0x02, 0xc5, 0x3b, 0xb0, 0x26, + 0xd5, 0x7a, 0x53, 0xb9, 0xe3, 0xbc, 0x43, 0x74, 0x0d, 0x09, 0x6e, 0x49, 0xaf, 0x1c, 0x1d, 0x56, + 0xb9, 0x40, 0xdb, 0x1c, 0x59, 0xa3, 0x13, 0x67, 0xf3, 0x1e, 0x57, 0x9c, 0x8a, 0xb3, 0x5b, 0x08, + 0xba, 0xf1, 0x84, 0x14, 0x4a, 0x1a, 0xae, 0x4f, 0x67, 0xb8, 0x62, 0x6e, 0x4d, 0x67, 0xd8, 0xe7, + 0xee, 0xa5, 0x2c, 0x73, 0xdc, 0x2a, 0xd3, 0xf6, 0x71, 0xe3, 0x17, 0x20, 0xe6, 0x27, 0xda, 0xab, + 0x58, 0x74, 0x59, 0xb2, 0xe8, 0x2f, 0x97, 0x1e, 0x17, 0xf4, 0xff, 0xa1, 0x00, 0xab, 0x39, 0x16, + 0xb4, 0x70, 0xaf, 0x61, 0x41, 0xdc, 0x1a, 0x4b, 0x43, 0x16, 0x27, 0x94, 0xfa, 0xe7, 0xfa, 0xc7, + 0x9e, 0x93, 0x51, 0xff, 0x28, 0x2d, 0x7e, 0x0e, 0x97, 0x8f, 0xa6, 0x9e, 0x17, 0x3b, 0xe7, 0xb1, + 0x32, 0xeb, 0x87, 0xce, 0x91, 0xb4, 0xe3, 0xce, 0x1d, 0x32, 0xad, 0x2b, 0x5a, 0x36, 0x6d, 0x19, + 0xce, 0x91, 0xf8, 0x6d, 0x10, 0x49, 0x01, 0x2c, 0x6d, 0x61, 0xfe, 0xf2, 0xe2, 0xfc, 0x9a, 0x22, + 0xa5, 0x6e, 0x30, 0x9c, 0x23, 0xfd, 0xbf, 0x29, 0x42, 0x7d, 0xd7, 0x8a, 0x4e, 0xf6, 0xac, 0xc9, + 0x20, 0xb6, 0xe2, 0x08, 0x17, 0xc6, 0x89, 0x15, 0x9d, 0x8c, 0xad, 0x09, 0x2b, 0x8b, 0x05, 0xb6, + 0x38, 0x4a, 0x18, 0x69, 0x8a, 0x37, 0xa0, 0x82, 0xc9, 0xbe, 0xbf, 0xff, 0x54, 0x9a, 0xff, 0x92, + 0x34, 0x32, 0xe4, 0xe8, 0x64, 0x7a, 0x74, 0x94, 0x34, 0x55, 0x25, 0xc5, 0x5d, 0x58, 0x95, 0x3f, + 0x49, 0x1f, 0x3b, 0x97, 0xa7, 0x1d, 0x79, 0xa0, 0x78, 0x04, 0x35, 0x09, 0x18, 0xaa, 0xed, 0xa3, + 0x91, 0x98, 0x89, 0x53, 0x84, 0x91, 0xa5, 0x12, 0xbf, 0x84, 0x2b, 0x99, 0xe4, 0x4e, 0x10, 0xee, + 0x4d, 0xbd, 0xd8, 0x6d, 0xf5, 0xa4, 0xec, 0xfa, 0xda, 0x5c, 0xf6, 0x94, 0xc4, 0x58, 0x9c, 0x33, + 0x5f, 0xdb, 0x3d, 0xd7, 0xa7, 0xdd, 0xa8, 0x68, 0xe4, 0x81, 0x33, 0x54, 0xd6, 0x39, 0x6d, 0x42, + 0x79, 0x2a, 0xeb, 0x1c, 0xd9, 0x94, 0x04, 0xec, 0x39, 0xf1, 0x49, 0x60, 0x4b, 0x61, 0xf4, 0x72, + 0xae, 0x5a, 0x8c, 0x32, 0xf2, 0x94, 0xd8, 0x9d, 0xfe, 0xd4, 0xf3, 0x46, 0x7e, 0x4c, 0xfa, 0x4f, + 0xd1, 0x50, 0x49, 0xdc, 0xa0, 0x43, 0xcb, 0x3f, 0x76, 0xa2, 0xcd, 0xda, 0x9d, 0xe2, 0xbd, 0x82, + 0x21, 0x53, 0xfa, 0xdf, 0x5e, 0x82, 0x32, 0x8f, 0xe4, 0x6b, 0x50, 0x3d, 0xa4, 0x33, 0x6c, 0x7f, + 0x3a, 0x56, 0x67, 0xcd, 0x04, 0xe8, 0x4d, 0xc7, 0xac, 0x76, 0x48, 0xf3, 0x6d, 0xc1, 0xa0, 0xdf, + 0x58, 0x64, 0x30, 0x8d, 0xf1, 0x5b, 0x45, 0x82, 0xca, 0x14, 0x56, 0x22, 0x0c, 0xce, 0x68, 0x36, + 0x94, 0x08, 0xa1, 0x92, 0x74, 0x9c, 0x4d, 0x7b, 0x3d, 0x66, 0x2a, 0x13, 0xae, 0x42, 0x80, 0x96, + 0x1f, 0xcf, 0x9a, 0xae, 0x97, 0xe7, 0x4d, 0xd7, 0xb7, 0x00, 0x95, 0x9a, 0x91, 0xd3, 0xf7, 0x9d, + 0x56, 0x4f, 0x9e, 0x2b, 0x65, 0x20, 0xb8, 0x42, 0xed, 0x60, 0x22, 0x0f, 0x8c, 0xf0, 0xa7, 0xf8, + 0x2c, 0x99, 0x9d, 0xd4, 0x46, 0xa9, 0x43, 0xca, 0xbd, 0x30, 0x3b, 0x8f, 0x8d, 0x1c, 0x1d, 0x96, + 0x84, 0x1b, 0x1c, 0xeb, 0x90, 0xf8, 0x53, 0x6f, 0x03, 0x18, 0xc1, 0x59, 0xe4, 0xc4, 0x74, 0x46, + 0x73, 0x8d, 0x9a, 0x98, 0xf3, 0x5c, 0x09, 0xce, 0xf6, 0x83, 0x28, 0x31, 0x00, 0xcd, 0xcb, 0xc5, + 0x04, 0xd7, 0x3f, 0x84, 0x15, 0x94, 0x7e, 0xac, 0xd8, 0x12, 0x77, 0xa5, 0x15, 0x9b, 0x65, 0x36, + 0x4d, 0x9d, 0x9f, 0xaa, 0x6f, 0x48, 0x63, 0x78, 0x57, 0x7d, 0x97, 0xf2, 0xbc, 0x91, 0x31, 0x64, + 0x24, 0x3b, 0xaf, 0x2c, 0x50, 0xca, 0x53, 0xaf, 0x41, 0x15, 0xab, 0x46, 0x47, 0xf4, 0xea, 0xd4, + 0x31, 0x0c, 0xce, 0x5a, 0x98, 0xd6, 0xff, 0x49, 0x01, 0x6a, 0xfd, 0xd0, 0xc6, 0x2d, 0x7f, 0x30, + 0x71, 0x46, 0xaf, 0xb4, 0x57, 0xa1, 0xf4, 0x15, 0x78, 0x9e, 0x45, 0x9b, 0x8b, 0x34, 0x24, 0x24, + 0x00, 0xf1, 0x31, 0x94, 0x8e, 0x70, 0x13, 0x29, 0x66, 0x55, 0xbe, 0x4c, 0xf1, 0xea, 0x37, 0x6e, + 0x2b, 0x06, 0x91, 0xea, 0xbf, 0x9b, 0x7c, 0x9f, 0xf6, 0x9a, 0xec, 0xf1, 0xef, 0x25, 0x72, 0x3f, + 0x19, 0xb4, 0xb4, 0x82, 0xa8, 0x40, 0x69, 0xbb, 0x3d, 0x68, 0xb1, 0x5a, 0x84, 0x0a, 0xd2, 0xc0, + 0xdc, 0xe9, 0x18, 0x83, 0xa1, 0x56, 0x22, 0x7f, 0x16, 0x02, 0x74, 0x9b, 0x83, 0xa1, 0x56, 0x11, + 0x00, 0xcb, 0x07, 0xbd, 0xce, 0x2f, 0x0f, 0xda, 0x9a, 0xa6, 0xff, 0xe3, 0x02, 0x40, 0x7a, 0x7e, + 0x24, 0xde, 0x83, 0xda, 0x19, 0xa5, 0xcc, 0xcc, 0x41, 0x7c, 0xb6, 0x8d, 0xc0, 0x68, 0x92, 0x0c, + 0x3f, 0x80, 0x7a, 0xb2, 0x49, 0xa2, 0xd4, 0x34, 0x7f, 0x22, 0x5f, 0x4b, 0xf0, 0x5b, 0x17, 0xe2, + 0x7d, 0xa8, 0x04, 0xd8, 0x0e, 0x24, 0x2d, 0x66, 0x45, 0xa6, 0x4c, 0xf3, 0x8d, 0x95, 0x80, 0x13, + 0x28, 0x5d, 0x1d, 0x85, 0xca, 0x30, 0x94, 0x90, 0xee, 0x20, 0xa8, 0xe5, 0x59, 0xd3, 0xc8, 0x31, + 0x18, 0x9f, 0xec, 0x12, 0xe5, 0x74, 0x97, 0xd0, 0x7f, 0x05, 0x8d, 0x81, 0x35, 0x9e, 0xf0, 0x5e, + 0x42, 0x0d, 0x13, 0x50, 0xc2, 0x39, 0x21, 0xa7, 0x1e, 0xfd, 0xc6, 0x45, 0xb7, 0xef, 0x84, 0x23, + 0xc7, 0x57, 0x6b, 0x54, 0x25, 0x91, 0xfd, 0x1e, 0xe0, 0x6e, 0x62, 0x04, 0x67, 0x6a, 0x3b, 0x51, + 0x69, 0xfd, 0xdf, 0x2e, 0x40, 0x2d, 0x53, 0x0d, 0xf1, 0x21, 0x94, 0x48, 0x0c, 0x2f, 0x64, 0x19, + 0x61, 0x86, 0x80, 0x7f, 0xb3, 0xe0, 0x86, 0x84, 0xe2, 0x6d, 0x28, 0x47, 0xb1, 0x15, 0xaa, 0xa3, + 0x48, 0x2d, 0x93, 0x63, 0x2b, 0x98, 0xfa, 0xb6, 0xc1, 0x68, 0xa1, 0x43, 0xd1, 0xf1, 0x6d, 0x69, + 0x4a, 0x9b, 0xa7, 0x42, 0xa4, 0x7e, 0x07, 0xaa, 0x49, 0xf1, 0x38, 0x05, 0x8c, 0xfe, 0xf3, 0x81, + 0x76, 0x49, 0x54, 0xa1, 0x6c, 0x34, 0x7b, 0x4f, 0xda, 0x5a, 0x41, 0xff, 0xbb, 0x05, 0x80, 0x34, + 0x97, 0x78, 0x90, 0xab, 0xed, 0x8d, 0xd9, 0x52, 0x1f, 0xd0, 0xdf, 0x4c, 0x65, 0x6f, 0x42, 0x75, + 0xea, 0x13, 0xd0, 0xb1, 0xe5, 0x4e, 0x94, 0x02, 0x50, 0x77, 0x54, 0xbe, 0xb5, 0x33, 0xba, 0xe3, + 0x0b, 0xcb, 0xd3, 0xbf, 0x84, 0x6a, 0x52, 0x1c, 0xea, 0xe6, 0x3b, 0xfd, 0x6e, 0xb7, 0xff, 0xbc, + 0xd3, 0x7b, 0xa2, 0x5d, 0xc2, 0xe4, 0xbe, 0xd1, 0x6e, 0xb5, 0xb7, 0x31, 0x59, 0xc0, 0x39, 0xdb, + 0x3a, 0x30, 0x8c, 0x76, 0x6f, 0x68, 0x1a, 0xfd, 0xe7, 0xda, 0x92, 0xfe, 0xfb, 0x05, 0x58, 0xed, + 0x7b, 0x76, 0x2b, 0xf0, 0x5a, 0xd6, 0x04, 0xe5, 0x2c, 0xf1, 0x73, 0x58, 0x3f, 0x9c, 0xa2, 0xac, + 0x3f, 0xf1, 0xac, 0x91, 0x73, 0x12, 0x78, 0xb6, 0xa3, 0x16, 0x61, 0xee, 0xb0, 0x55, 0x9e, 0x9d, + 0x68, 0x44, 0xbc, 0x9f, 0xd2, 0x8a, 0x4f, 0xa1, 0x3e, 0x09, 0x83, 0x43, 0x47, 0x1d, 0x64, 0xcd, + 0x2a, 0xa9, 0x69, 0xde, 0x1a, 0xd1, 0xc9, 0x43, 0xe6, 0xff, 0xb2, 0x0c, 0xf5, 0x6d, 0xc7, 0x9e, + 0x4e, 0xbe, 0x0a, 0x5c, 0xbf, 0x15, 0x9f, 0x8b, 0x4f, 0xa0, 0x1e, 0x78, 0x64, 0xb0, 0x32, 0x33, + 0x6e, 0x73, 0x8b, 0xca, 0x81, 0x80, 0x5a, 0x40, 0x4e, 0x76, 0x1f, 0xc0, 0x65, 0x69, 0x2e, 0x61, + 0xeb, 0xe9, 0x39, 0x67, 0xc6, 0x35, 0x53, 0x36, 0x34, 0x46, 0xf1, 0x06, 0x4d, 0xe4, 0xbf, 0x05, + 0x1b, 0x19, 0x72, 0x64, 0x2c, 0x4c, 0x5f, 0x9c, 0x5b, 0x63, 0xeb, 0x49, 0xde, 0xc4, 0xa1, 0xef, + 0x2b, 0xd8, 0x50, 0x35, 0x1c, 0x71, 0xef, 0x71, 0xe6, 0x52, 0x56, 0xa9, 0xca, 0xf5, 0xae, 0xac, + 0xf0, 0x7a, 0x90, 0x05, 0x52, 0x59, 0x1f, 0xc3, 0x15, 0x1b, 0x5b, 0x6f, 0x72, 0xe7, 0x9f, 0x3a, + 0xce, 0xc4, 0xf4, 0x32, 0xde, 0x2e, 0x84, 0xdc, 0x42, 0xdc, 0x53, 0xc7, 0x99, 0x74, 0xad, 0x28, + 0x16, 0x8f, 0x61, 0xdd, 0x3a, 0x3a, 0x72, 0x46, 0xb1, 0x63, 0x9b, 0xb8, 0x8d, 0x2d, 0xb4, 0xd2, + 0xa2, 0xac, 0xb4, 0xa6, 0xc8, 0x70, 0x39, 0xb6, 0x02, 0x4f, 0x3c, 0x81, 0xeb, 0x93, 0x93, 0x8b, + 0x88, 0x04, 0xe9, 0xd1, 0x09, 0xee, 0xb6, 0x99, 0x12, 0x56, 0x16, 0x94, 0x70, 0x55, 0x91, 0xb7, + 0x98, 0x5a, 0x15, 0xf4, 0x18, 0xae, 0xab, 0xee, 0x43, 0xfd, 0x2e, 0xdf, 0xe7, 0x15, 0xea, 0xf3, + 0x2b, 0xb2, 0xdf, 0x10, 0x9f, 0xe9, 0xf8, 0x7b, 0xa0, 0xb1, 0xfb, 0xd6, 0x11, 0x4e, 0x5d, 0xfa, + 0xba, 0x74, 0x69, 0x6f, 0x10, 0x7c, 0x87, 0x16, 0x1f, 0xb2, 0x8f, 0x7b, 0xa0, 0x39, 0x63, 0x37, + 0x36, 0x2d, 0x92, 0x56, 0x99, 0x92, 0x4f, 0xc5, 0x1a, 0x08, 0x97, 0x66, 0x3b, 0xa4, 0xfc, 0x0c, + 0x34, 0x49, 0x74, 0x44, 0x3a, 0x00, 0xb6, 0xa6, 0xb6, 0xa0, 0x35, 0x0d, 0xa6, 0xda, 0x41, 0x22, + 0x6c, 0xc5, 0x1e, 0x08, 0x65, 0x83, 0x24, 0xcb, 0x0a, 0x2b, 0xb2, 0xac, 0x42, 0x4b, 0x77, 0xa5, + 0xfe, 0xf6, 0xd3, 0x83, 0x54, 0x3d, 0xa6, 0xc6, 0xa8, 0x05, 0x70, 0x94, 0x07, 0x47, 0xfa, 0x7f, + 0xb1, 0x0c, 0x55, 0x36, 0xbb, 0xe1, 0x34, 0xbe, 0x07, 0x2b, 0xc1, 0xe1, 0xb7, 0x24, 0xca, 0xbe, + 0xc4, 0xdf, 0x6a, 0x39, 0x38, 0xfc, 0x16, 0xe5, 0xdf, 0xf7, 0x94, 0x08, 0x62, 0x3b, 0x47, 0x72, + 0xd5, 0x34, 0xf2, 0x4a, 0xa6, 0x14, 0x49, 0xb6, 0x9d, 0x23, 0x94, 0x2e, 0x5d, 0x3f, 0x72, 0xc2, + 0x98, 0xcd, 0xa6, 0x2b, 0x2f, 0x5f, 0x1c, 0x4c, 0x46, 0x96, 0xd4, 0x47, 0x50, 0x93, 0xa6, 0x49, + 0xca, 0x54, 0x79, 0x79, 0x26, 0x26, 0xa3, 0x4c, 0x5f, 0x40, 0x23, 0xdd, 0x7e, 0x28, 0x5f, 0xf5, + 0xa5, 0xf9, 0x56, 0x13, 0x4a, 0xca, 0xfa, 0x10, 0xae, 0x46, 0xa7, 0xee, 0xc4, 0x94, 0x35, 0x0d, + 0x7c, 0x3a, 0x5e, 0x35, 0x27, 0xa7, 0x72, 0x00, 0x05, 0x62, 0x3b, 0x84, 0xec, 0xfb, 0xbd, 0xa9, + 0xe7, 0xed, 0x9f, 0x8a, 0x77, 0x61, 0x5d, 0x92, 0x4f, 0x92, 0xf9, 0x44, 0xa3, 0x58, 0x36, 0x1a, + 0x8c, 0xd8, 0x97, 0xf3, 0x48, 0x7c, 0x0e, 0x9b, 0x3c, 0x87, 0x64, 0xa3, 0x78, 0x9a, 0xf3, 0x0c, + 0xe1, 0x33, 0xd0, 0x2b, 0x84, 0x67, 0x93, 0x6a, 0x93, 0xb0, 0x34, 0x51, 0x3e, 0x82, 0x0d, 0xf7, + 0xd8, 0x0f, 0x42, 0x95, 0x45, 0x4e, 0x7f, 0x79, 0x38, 0x2a, 0x18, 0xd7, 0xcc, 0x2c, 0x1a, 0x6c, + 0x89, 0x5c, 0x9e, 0x17, 0x66, 0x4c, 0x6e, 0x19, 0x98, 0x03, 0xb5, 0xd0, 0x46, 0x76, 0x7d, 0x5e, + 0xb0, 0xcb, 0x86, 0x11, 0x9c, 0x75, 0x6c, 0x5c, 0xd2, 0x92, 0x54, 0xad, 0x91, 0xf8, 0x9c, 0x5a, + 0xb3, 0x46, 0xad, 0x11, 0x8c, 0x4c, 0x66, 0x0a, 0xb6, 0xe8, 0x17, 0x20, 0xe6, 0x96, 0x74, 0xb4, + 0xa9, 0xbd, 0xb4, 0xbf, 0xb5, 0x99, 0x95, 0x4d, 0x6b, 0x60, 0x6e, 0x45, 0xaf, 0x2f, 0x5a, 0x03, + 0xa3, 0xfc, 0x4a, 0x6e, 0xc3, 0x66, 0xfe, 0xcb, 0x67, 0x8e, 0x7b, 0x7c, 0x42, 0xb3, 0x4b, 0xde, + 0x16, 0xc8, 0xe7, 0xbf, 0x92, 0xfd, 0xf2, 0x73, 0xa2, 0x7d, 0x25, 0x67, 0xb9, 0xfc, 0xe3, 0x39, + 0x8b, 0xfe, 0xef, 0x15, 0xa0, 0xca, 0x33, 0x03, 0x17, 0xd1, 0x1b, 0x50, 0xfc, 0x81, 0x05, 0x84, + 0x38, 0x71, 0x1f, 0xd6, 0x2d, 0xdb, 0x9e, 0x19, 0x50, 0xde, 0x49, 0xd7, 0x2c, 0xdb, 0xce, 0x8d, + 0xe6, 0x3d, 0xd0, 0xdc, 0xc8, 0x54, 0x86, 0xa8, 0xd4, 0x27, 0xb3, 0x62, 0x34, 0xdc, 0x48, 0xda, + 0xa1, 0xf8, 0x54, 0x32, 0xb7, 0x26, 0x4b, 0x3f, 0xbc, 0x26, 0x75, 0x01, 0x60, 0x38, 0xb4, 0x6d, + 0xb6, 0xe2, 0xf3, 0xaf, 0x4a, 0x95, 0x82, 0x06, 0xfa, 0x7f, 0x55, 0x83, 0x5a, 0xd3, 0xb7, 0xbc, + 0x8b, 0xef, 0x1d, 0x72, 0x28, 0xa5, 0x93, 0xa0, 0xc9, 0x54, 0xce, 0x52, 0xf6, 0xb0, 0xa8, 0x12, + 0x84, 0x6a, 0x76, 0x1b, 0x6a, 0xc1, 0x34, 0x4e, 0xf0, 0xec, 0x73, 0x01, 0x0c, 0x22, 0x82, 0x24, + 0x7f, 0x72, 0xfe, 0xa9, 0xf2, 0x93, 0x42, 0x9b, 0xe6, 0x4f, 0x94, 0x9c, 0x24, 0x3f, 0x11, 0xbc, + 0x09, 0xab, 0xb1, 0x3b, 0x76, 0xc8, 0xe5, 0x65, 0x3a, 0x76, 0x6c, 0xbe, 0x7c, 0xc3, 0xf7, 0x39, + 0x5a, 0x12, 0x86, 0xa5, 0x8c, 0x9d, 0x71, 0x10, 0x5e, 0x70, 0x29, 0xcb, 0x5c, 0x0a, 0x83, 0xa8, + 0x94, 0xf7, 0x41, 0x9c, 0x59, 0x6e, 0x6c, 0xe6, 0x8b, 0x62, 0xc5, 0x52, 0x43, 0xcc, 0x30, 0x5b, + 0xdc, 0x55, 0x58, 0xb6, 0xdd, 0xe8, 0xb4, 0xd3, 0x97, 0x4a, 0xa5, 0x4c, 0x61, 0x5b, 0xa2, 0x91, + 0x85, 0x32, 0x6d, 0xec, 0x30, 0xf7, 0x2f, 0x1a, 0x55, 0x84, 0x6c, 0x21, 0x00, 0x65, 0x22, 0xdf, + 0x89, 0xcf, 0x82, 0x10, 0x73, 0xb2, 0xce, 0x98, 0x02, 0x50, 0x76, 0x44, 0x52, 0xfc, 0x10, 0xb1, + 0x87, 0xa2, 0x91, 0xa4, 0x51, 0x1b, 0x63, 0x56, 0x41, 0xd8, 0x3a, 0x57, 0x3f, 0x85, 0x88, 0xbb, + 0xd0, 0xa0, 0xea, 0x93, 0x4e, 0x89, 0x6d, 0xa0, 0x95, 0x5f, 0x34, 0xea, 0x08, 0x25, 0xc3, 0x1a, + 0x52, 0x7d, 0x01, 0xd7, 0x73, 0xed, 0x33, 0xad, 0x30, 0xb4, 0x2e, 0xcc, 0xb1, 0xf5, 0x6d, 0x10, + 0x92, 0x11, 0xb4, 0x68, 0x5c, 0xcd, 0x76, 0x5b, 0x13, 0xd1, 0x7b, 0x88, 0x7d, 0x69, 0x56, 0xd7, + 0x0f, 0x42, 0xb2, 0x90, 0x2e, 0xcc, 0x8a, 0x58, 0x32, 0xe7, 0xd1, 0x00, 0x93, 0x82, 0x1b, 0xf1, + 0x3d, 0x20, 0xa3, 0x46, 0xb0, 0x2d, 0x02, 0xa1, 0x8a, 0x17, 0x3d, 0xe2, 0x3d, 0x76, 0x5d, 0x7a, + 0xb3, 0x3f, 0xa2, 0x4d, 0x95, 0x11, 0x27, 0x8e, 0x65, 0xd3, 0x9a, 0x25, 0xc4, 0xae, 0x63, 0x91, + 0x93, 0x56, 0xf4, 0xc8, 0x9c, 0x4c, 0x63, 0xbe, 0xc0, 0x63, 0x94, 0xa3, 0x47, 0xfb, 0xd3, 0x58, + 0x82, 0x8f, 0x9d, 0x98, 0x3c, 0x2b, 0x08, 0xfc, 0x84, 0xef, 0x99, 0x45, 0x8f, 0xd4, 0x81, 0xdb, + 0x15, 0xd9, 0xb7, 0x8f, 0xe4, 0x89, 0x9a, 0x0e, 0xab, 0x09, 0xd2, 0x1c, 0x4f, 0xf9, 0xc6, 0x4e, + 0xd1, 0xa8, 0x29, 0x82, 0xbd, 0xa9, 0x87, 0x03, 0x4b, 0xa6, 0x30, 0x33, 0xc4, 0xaa, 0x5c, 0xe3, + 0xa1, 0x23, 0x88, 0x81, 0xb5, 0x79, 0x0d, 0x38, 0x61, 0x9e, 0xb8, 0x31, 0x59, 0x6a, 0x8b, 0x46, + 0x85, 0x00, 0xbb, 0x6e, 0x8c, 0xeb, 0x98, 0x91, 0x72, 0x06, 0x52, 0x11, 0xd7, 0x89, 0x68, 0x8d, + 0x10, 0x7b, 0x04, 0xa7, 0x82, 0x50, 0x88, 0xc8, 0xd2, 0x62, 0x79, 0x37, 0x88, 0xb4, 0x91, 0x21, + 0xc5, 0x52, 0xdf, 0x06, 0xce, 0x6c, 0xe2, 0xd4, 0xe3, 0x32, 0x5f, 0x63, 0x03, 0x07, 0x81, 0xb7, + 0xdd, 0xe8, 0x94, 0x4a, 0xbc, 0x0b, 0x8d, 0x0c, 0x1d, 0x96, 0x77, 0x93, 0x67, 0x46, 0x42, 0x96, + 0xab, 0x63, 0xe8, 0x8c, 0x83, 0x58, 0x36, 0xf3, 0xf5, 0x4c, 0x1d, 0x0d, 0x82, 0xe7, 0xeb, 0x28, + 0x69, 0xb1, 0xcc, 0x5b, 0x99, 0x3a, 0x32, 0x29, 0x96, 0xfa, 0x06, 0xd4, 0xcf, 0x42, 0x37, 0x8e, + 0x1d, 0x29, 0xe4, 0xdc, 0xe6, 0x8e, 0x95, 0x30, 0x5a, 0xfd, 0x6f, 0x40, 0x9d, 0x7b, 0x5e, 0xf2, + 0xb7, 0x3b, 0x4c, 0x22, 0x61, 0x8a, 0x41, 0xc8, 0xde, 0x18, 0xbb, 0x3e, 0x99, 0x63, 0x8b, 0x46, + 0x95, 0x21, 0x7b, 0xae, 0x9f, 0x45, 0x5b, 0xe7, 0x64, 0x94, 0x4d, 0xd1, 0xd6, 0x39, 0x2d, 0xc9, + 0x89, 0xeb, 0x79, 0xbc, 0xf0, 0xdf, 0x94, 0x4b, 0x12, 0x21, 0xb4, 0xee, 0x13, 0x34, 0x7d, 0xfd, + 0x6e, 0x06, 0x4d, 0xdf, 0xc6, 0x89, 0x43, 0x68, 0xfc, 0xf4, 0x5b, 0x72, 0xe2, 0x20, 0x00, 0xbf, + 0x9c, 0x22, 0xad, 0x73, 0x32, 0xb9, 0x26, 0x48, 0xeb, 0x5c, 0xf2, 0x2d, 0xda, 0x29, 0x30, 0xef, + 0x3b, 0x09, 0xdf, 0x42, 0x10, 0xe6, 0xce, 0x12, 0x58, 0xe7, 0x64, 0x63, 0xcd, 0x10, 0x58, 0xe7, + 0x64, 0x5c, 0x70, 0x2c, 0x9b, 0x2b, 0xfe, 0x2e, 0x17, 0x8f, 0x00, 0xaa, 0xf7, 0x1d, 0xa8, 0x47, + 0x8f, 0xcc, 0x14, 0x7f, 0x9f, 0xb3, 0x47, 0x8f, 0x0c, 0x45, 0x71, 0x17, 0x1a, 0xc9, 0xd4, 0x60, + 0x9a, 0xf7, 0x78, 0xe0, 0x6d, 0x39, 0x35, 0xe8, 0x4a, 0xcb, 0xaf, 0x0b, 0x70, 0x83, 0x8f, 0x38, + 0x89, 0xfd, 0xef, 0x39, 0x51, 0x64, 0x1d, 0x3b, 0x3b, 0x41, 0xb8, 0x33, 0xfd, 0xfe, 0xfb, 0x0b, + 0x71, 0x0f, 0xd6, 0xf6, 0xad, 0xd0, 0xf1, 0xe3, 0xc4, 0xf3, 0x40, 0x1a, 0x55, 0x67, 0xc1, 0xe2, + 0x31, 0x68, 0x0c, 0x62, 0x4f, 0xfe, 0x96, 0x3a, 0x37, 0x9f, 0x3d, 0x88, 0x9a, 0xa3, 0xd2, 0xff, + 0x49, 0x01, 0xaa, 0xdb, 0x6e, 0x14, 0x1b, 0xb8, 0x5d, 0x8a, 0x2f, 0x40, 0xf3, 0x82, 0x33, 0x54, + 0xf6, 0x49, 0x8c, 0xce, 0xe8, 0x9c, 0x72, 0x97, 0x4c, 0x15, 0xcd, 0x06, 0x11, 0xa6, 0x9a, 0xe2, + 0x17, 0xa0, 0x4d, 0x27, 0x93, 0x7c, 0xd6, 0xa5, 0x97, 0x64, 0x25, 0xc2, 0x34, 0xeb, 0x7b, 0x50, + 0xcb, 0x7c, 0x75, 0x81, 0x5e, 0x0a, 0xe9, 0xb7, 0x90, 0x38, 0xf3, 0x9d, 0x05, 0x37, 0x89, 0x20, + 0x2d, 0x5d, 0xff, 0x0f, 0x96, 0x40, 0x93, 0x16, 0x5d, 0xcb, 0x76, 0x42, 0x72, 0x60, 0xc8, 0x59, + 0x34, 0x0a, 0xaf, 0xb4, 0x68, 0xdc, 0x81, 0xb2, 0xe7, 0x8e, 0x13, 0x17, 0xe4, 0xdc, 0x01, 0x30, + 0x21, 0x70, 0xac, 0x83, 0xd0, 0x3d, 0x26, 0xdb, 0x4b, 0xf6, 0x72, 0x09, 0x1d, 0x14, 0xec, 0x4c, + 0xfd, 0x11, 0x0d, 0xd1, 0x03, 0x00, 0xdb, 0x8d, 0x62, 0x93, 0xac, 0x91, 0x79, 0x33, 0x76, 0xd2, + 0xff, 0x46, 0xd5, 0x4e, 0x86, 0xe2, 0x1e, 0x68, 0x19, 0x39, 0xd9, 0x4f, 0x0c, 0x89, 0x65, 0x23, + 0x95, 0x9f, 0x5b, 0x7e, 0xcb, 0x8f, 0xe7, 0x28, 0x51, 0x26, 0x5c, 0x9e, 0xa3, 0x44, 0x79, 0xf0, + 0x1e, 0x68, 0xc1, 0x0b, 0x27, 0x34, 0x8f, 0x9c, 0x78, 0x74, 0x62, 0x72, 0xb3, 0x56, 0xf8, 0xe0, + 0x06, 0xe1, 0x3b, 0x08, 0xee, 0x22, 0x54, 0x3f, 0x87, 0xcb, 0xcf, 0x9c, 0x51, 0x1c, 0x84, 0x9d, + 0xc4, 0xf3, 0x45, 0xde, 0xec, 0x0b, 0x03, 0x2f, 0xb1, 0xed, 0xe3, 0x6f, 0xf1, 0x0e, 0xa0, 0xc6, + 0xe1, 0x8c, 0x54, 0x0f, 0x2d, 0x54, 0x48, 0x9c, 0x11, 0xf6, 0x53, 0x39, 0x95, 0x8d, 0xe6, 0x05, + 0x1f, 0x46, 0xea, 0x7f, 0x6b, 0x05, 0xd6, 0x32, 0x9f, 0xa6, 0x2b, 0x31, 0x0f, 0xa1, 0x2e, 0x8f, + 0x00, 0x52, 0xa7, 0xaf, 0x05, 0x1f, 0xaa, 0x31, 0x11, 0x8b, 0x5a, 0x8f, 0x41, 0xcb, 0xe6, 0xf9, + 0x01, 0x2d, 0xa8, 0x91, 0xc9, 0x86, 0xba, 0xd0, 0x5d, 0x28, 0xf3, 0xc9, 0x5c, 0xae, 0x9e, 0xc9, + 0xa9, 0x27, 0x23, 0xc5, 0xef, 0xc0, 0x2a, 0x9f, 0xfc, 0x9a, 0xf2, 0xee, 0x4f, 0x29, 0xab, 0xe0, + 0x2d, 0xe8, 0x3c, 0xa3, 0xce, 0xf4, 0x94, 0x8e, 0xc4, 0x07, 0x50, 0x27, 0xd7, 0x6e, 0xf3, 0x05, + 0x91, 0xe6, 0xbd, 0x49, 0xd9, 0x0c, 0x47, 0x78, 0x2e, 0x49, 0xbc, 0x07, 0xeb, 0x38, 0x37, 0x2c, + 0x7f, 0xe4, 0xd0, 0x44, 0x23, 0x3b, 0x25, 0x9f, 0x12, 0x6b, 0x0a, 0x91, 0x1c, 0xc1, 0xfc, 0x16, + 0x54, 0x6d, 0x37, 0x74, 0x98, 0x68, 0xe5, 0xc7, 0xd8, 0x2c, 0x53, 0x7a, 0xf1, 0x08, 0xf7, 0x36, + 0xdf, 0x76, 0x49, 0xc7, 0xe0, 0x39, 0x52, 0x99, 0xab, 0x5b, 0x23, 0x21, 0xa1, 0xf9, 0x22, 0x3e, + 0x23, 0x7e, 0xc7, 0xd5, 0xe3, 0x19, 0x5e, 0x5d, 0x3c, 0xc3, 0x57, 0x15, 0x19, 0xcf, 0xf2, 0xf7, + 0xa0, 0x36, 0x09, 0x1d, 0xf3, 0xc8, 0xf5, 0x62, 0x27, 0x44, 0x45, 0x7c, 0xd6, 0x4e, 0x02, 0x93, + 0xd0, 0xd9, 0x61, 0xec, 0x42, 0xc7, 0xa3, 0xda, 0x42, 0xc7, 0x23, 0xf1, 0x00, 0x2e, 0xbb, 0xbe, + 0x1b, 0xbb, 0x96, 0x67, 0xb2, 0xf5, 0x88, 0xad, 0xc4, 0x75, 0x3a, 0x72, 0x5b, 0x97, 0xa8, 0x7d, + 0xc4, 0x90, 0xb9, 0x58, 0x7c, 0x06, 0xeb, 0x47, 0x6e, 0x88, 0xcb, 0x93, 0xb8, 0x17, 0x37, 0x7b, + 0x75, 0xae, 0xd9, 0x6b, 0x44, 0x64, 0x20, 0x0d, 0xb7, 0xfb, 0x7d, 0x10, 0x87, 0xd3, 0xd1, 0xa9, + 0x13, 0x9b, 0xce, 0xf9, 0xc4, 0xf2, 0x6d, 0x33, 0x8a, 0x9d, 0x09, 0x29, 0x71, 0xab, 0x86, 0xc6, + 0x98, 0x36, 0x21, 0x06, 0xb1, 0x33, 0x11, 0x6f, 0x41, 0x23, 0x3e, 0xc1, 0x2d, 0x21, 0x32, 0x23, + 0xc7, 0x0a, 0x47, 0x27, 0x7c, 0x6d, 0xdb, 0x58, 0x95, 0xd0, 0x01, 0x01, 0xc5, 0x23, 0x58, 0x25, + 0x41, 0x36, 0xb9, 0x47, 0xa7, 0x2d, 0xbc, 0x47, 0x57, 0x47, 0x22, 0x95, 0x12, 0x8f, 0xe0, 0xea, + 0x24, 0x88, 0x62, 0xd9, 0x95, 0x66, 0xba, 0xce, 0x49, 0xa8, 0xab, 0x18, 0x97, 0x11, 0xcb, 0x3d, + 0xd9, 0x57, 0x6b, 0x5d, 0x7c, 0x06, 0x24, 0x20, 0x9b, 0x28, 0x04, 0x4b, 0xbd, 0x6c, 0x7e, 0x02, + 0xe3, 0x12, 0x7c, 0x1e, 0x84, 0xa7, 0x2c, 0x11, 0xe3, 0x2f, 0xfd, 0x6f, 0xbf, 0x0f, 0xa5, 0x5e, + 0x60, 0x3b, 0xe2, 0x23, 0xa8, 0xd2, 0x1d, 0x9d, 0xcc, 0x4e, 0x21, 0x0d, 0x55, 0x88, 0xa6, 0x3f, + 0xc4, 0xf2, 0x2b, 0xbe, 0xfc, 0xf5, 0xf2, 0xcb, 0xa2, 0x6f, 0x90, 0x81, 0x95, 0x9c, 0xf5, 0xb0, + 0x1e, 0x35, 0x75, 0x03, 0xda, 0x8a, 0x23, 0x83, 0x31, 0x28, 0xa4, 0x93, 0xe3, 0x40, 0xe8, 0xf8, + 0xb4, 0xdc, 0xca, 0x46, 0x92, 0x26, 0xb3, 0x76, 0x18, 0x20, 0x2b, 0x60, 0x51, 0xb6, 0xbc, 0xc0, + 0xac, 0xcd, 0x78, 0x92, 0x6d, 0x3f, 0x82, 0xea, 0xb7, 0x81, 0xeb, 0x73, 0xc5, 0x97, 0xe7, 0x2a, + 0xfe, 0x55, 0xe0, 0xfa, 0x5c, 0xf1, 0x6f, 0xe5, 0x2f, 0xf1, 0x26, 0xac, 0x04, 0x3e, 0x97, 0xbd, + 0x32, 0x57, 0xf6, 0x72, 0xe0, 0x77, 0xf9, 0xb6, 0xc4, 0xaa, 0x1b, 0x99, 0x21, 0xe9, 0xba, 0x98, + 0x53, 0x3a, 0xf9, 0xd5, 0xdc, 0xc8, 0x40, 0x18, 0x16, 0x8b, 0x73, 0x5e, 0x0e, 0x12, 0x15, 0x56, + 0x9d, 0x9f, 0xf3, 0x8c, 0xa6, 0x02, 0xdf, 0x82, 0xca, 0x71, 0x18, 0x90, 0xa5, 0x60, 0xc1, 0xea, + 0x58, 0x21, 0xdc, 0xd6, 0x05, 0xea, 0x61, 0xf4, 0xd3, 0xf5, 0x8f, 0x4d, 0x3a, 0xa9, 0xc0, 0x75, + 0x51, 0x31, 0xea, 0x0a, 0x48, 0x67, 0x10, 0x6f, 0x41, 0xc5, 0x3a, 0x3e, 0x36, 0xe5, 0xa5, 0x8f, + 0xb9, 0xb2, 0xac, 0xe3, 0x63, 0xfa, 0xe4, 0x03, 0x58, 0x3d, 0x73, 0x7d, 0x33, 0x9a, 0x38, 0x23, + 0xa6, 0x5d, 0x9d, 0xef, 0xca, 0x33, 0xd7, 0x47, 0x1e, 0x42, 0xf4, 0xd9, 0xfd, 0xb4, 0xf1, 0xe3, + 0xf7, 0xd3, 0xb5, 0x97, 0xed, 0xa7, 0x3a, 0x2c, 0x4b, 0xa7, 0x02, 0x6d, 0x8e, 0x44, 0x62, 0xc4, + 0xc7, 0x50, 0x0b, 0x2d, 0xff, 0xd4, 0x94, 0x1e, 0x93, 0xdf, 0x64, 0xad, 0xed, 0x86, 0xe5, 0x9f, + 0x4a, 0x87, 0x49, 0x08, 0x93, 0xdf, 0x79, 0xdd, 0x7b, 0xfd, 0x15, 0xf6, 0xb0, 0x8c, 0x99, 0x4d, + 0xfc, 0xb0, 0x99, 0xed, 0x53, 0xb2, 0x67, 0x39, 0x7e, 0x6c, 0xaa, 0x0c, 0x97, 0x17, 0x67, 0xa8, + 0x33, 0x59, 0x9f, 0xb3, 0x61, 0x03, 0xe8, 0xb4, 0xcb, 0xa4, 0xa3, 0xb1, 0x8d, 0x5c, 0x03, 0x92, + 0x63, 0x30, 0x03, 0xc2, 0xf4, 0x48, 0xac, 0x09, 0x6b, 0xe9, 0xcd, 0x51, 0xbe, 0xae, 0x7b, 0x25, + 0x7b, 0xde, 0x9f, 0xbb, 0x6a, 0xaa, 0x2c, 0x68, 0x6e, 0xee, 0xfe, 0xe9, 0x9b, 0xb0, 0xca, 0xce, + 0xd3, 0xdc, 0x6f, 0x91, 0xf4, 0x57, 0xaf, 0x13, 0x90, 0xfb, 0x29, 0x22, 0x49, 0x45, 0x9a, 0xf5, + 0xe2, 0x73, 0x52, 0xb7, 0x52, 0x3e, 0xce, 0x76, 0xbc, 0xf8, 0xdc, 0xa8, 0xda, 0xea, 0x27, 0x6a, + 0x11, 0x87, 0xae, 0x6f, 0xe3, 0xd4, 0x8b, 0xad, 0xe3, 0x68, 0x73, 0x93, 0x56, 0x66, 0x4d, 0xc2, + 0x86, 0xd6, 0x71, 0x24, 0x3e, 0x81, 0xba, 0xc5, 0x56, 0x0b, 0xae, 0xf7, 0xf5, 0xec, 0xe9, 0x50, + 0xc6, 0x9e, 0x61, 0xd4, 0xac, 0x8c, 0x71, 0xe3, 0x73, 0x10, 0xca, 0xe7, 0x29, 0x63, 0x4b, 0xbf, + 0x31, 0x37, 0x1b, 0xd7, 0xa4, 0xd3, 0x53, 0x62, 0x49, 0xbf, 0x0d, 0x52, 0x14, 0x60, 0x76, 0xfc, + 0x1a, 0x55, 0x08, 0x18, 0x44, 0x8c, 0xf8, 0x73, 0x58, 0xcd, 0x9b, 0x6b, 0x6e, 0x2e, 0x70, 0x1d, + 0xe2, 0x8d, 0xbb, 0x3e, 0xca, 0x1a, 0x70, 0xde, 0xe4, 0x1b, 0xd0, 0xa4, 0x6a, 0x51, 0x46, 0x76, + 0x8f, 0xa9, 0xfb, 0x41, 0xdc, 0x52, 0x30, 0xec, 0x40, 0x65, 0x4c, 0x8d, 0xcf, 0x49, 0x3b, 0x4b, + 0x3a, 0x30, 0xb1, 0x41, 0x19, 0x55, 0x37, 0x31, 0x47, 0x6d, 0x83, 0x50, 0x0e, 0x0a, 0x28, 0xa5, + 0xb2, 0xfb, 0x2d, 0x29, 0x63, 0x89, 0x37, 0xd1, 0xac, 0x10, 0x6b, 0xb0, 0xab, 0x72, 0x56, 0xac, + 0x7d, 0x0c, 0x0d, 0xdc, 0x4a, 0x33, 0x5f, 0xd6, 0xb3, 0x8d, 0xda, 0x0f, 0x9d, 0xf4, 0xe3, 0xf5, + 0x49, 0x26, 0x25, 0x7e, 0x0e, 0xeb, 0x99, 0x9c, 0xd3, 0x53, 0xca, 0xfc, 0x26, 0x65, 0xde, 0x98, + 0xc9, 0x7c, 0x70, 0x8a, 0xd9, 0x1b, 0x93, 0x5c, 0x5a, 0x6c, 0x40, 0xb9, 0x13, 0xb5, 0x7d, 0x9b, + 0x94, 0xb4, 0x8a, 0xc1, 0x09, 0xf1, 0x08, 0xea, 0x6c, 0x11, 0x21, 0xc3, 0x64, 0xb4, 0xf9, 0x76, + 0xf6, 0xf8, 0x97, 0xcc, 0x22, 0x6c, 0xdd, 0xac, 0x79, 0xc9, 0xef, 0x08, 0x37, 0x62, 0x3e, 0x9b, + 0xcf, 0xb2, 0xc8, 0x77, 0xe6, 0x87, 0x9c, 0x88, 0x76, 0x52, 0x3e, 0x69, 0xc0, 0xf5, 0x70, 0xea, + 0x93, 0x95, 0x44, 0xe6, 0xe4, 0x7d, 0x9f, 0xf2, 0xdf, 0xa3, 0xfc, 0x32, 0xa6, 0x86, 0xc1, 0x64, + 0x9c, 0x97, 0x78, 0xd3, 0xd5, 0x30, 0x0b, 0x22, 0xa9, 0xe0, 0x25, 0x65, 0xf2, 0x69, 0x0a, 0x95, + 0xf9, 0xee, 0x4f, 0x29, 0x93, 0x4e, 0x5a, 0xa8, 0x4c, 0x01, 0xa5, 0xe9, 0xd4, 0xb5, 0x49, 0x65, + 0xac, 0x1b, 0xf4, 0x1b, 0xc5, 0x82, 0xd0, 0x19, 0x4d, 0xc3, 0xc8, 0x7d, 0xe1, 0x98, 0x91, 0xeb, + 0x9f, 0x92, 0xb2, 0x58, 0x31, 0x56, 0x13, 0xe8, 0xc0, 0xf5, 0x4f, 0x91, 0x65, 0x38, 0xe7, 0xb1, + 0x13, 0xfa, 0x7c, 0x75, 0xfc, 0xfd, 0x2c, 0xcb, 0x68, 0x13, 0x02, 0xd7, 0xb9, 0x01, 0x4e, 0xf2, + 0x7b, 0x66, 0x64, 0x23, 0x1e, 0xd9, 0x07, 0x3f, 0x6a, 0x64, 0x07, 0x34, 0xb2, 0x6f, 0x43, 0xc5, + 0xf5, 0x63, 0x27, 0x7c, 0x61, 0x79, 0x9b, 0x1f, 0xce, 0x71, 0xe3, 0x04, 0x27, 0xee, 0xc2, 0x4a, + 0xe4, 0xb9, 0xb8, 0xde, 0x37, 0x3f, 0x9a, 0x23, 0x53, 0x28, 0x71, 0x0f, 0xaa, 0x49, 0xa0, 0x97, + 0xcd, 0x8f, 0xe7, 0xe8, 0x52, 0xa4, 0xb8, 0x05, 0xa5, 0x33, 0x9c, 0x50, 0x0f, 0xe7, 0x8f, 0xeb, + 0x11, 0x8e, 0xdb, 0xf7, 0x11, 0x2a, 0xff, 0xb4, 0x7d, 0x3f, 0x9a, 0xdb, 0xbe, 0x77, 0x5c, 0xcf, + 0xe3, 0xed, 0xfb, 0x48, 0xfe, 0xc2, 0xcd, 0x8f, 0x72, 0x60, 0x4b, 0x3e, 0x99, 0xdf, 0xfc, 0x10, + 0xf7, 0x8c, 0x42, 0xe2, 0xd4, 0x22, 0x3a, 0x83, 0xe6, 0xa3, 0xf4, 0x4f, 0xb3, 0x7d, 0x95, 0x3f, + 0x9c, 0x36, 0x20, 0x4a, 0xd2, 0xe2, 0x75, 0x90, 0x47, 0xec, 0xa4, 0x7d, 0x7d, 0xc6, 0x81, 0x07, + 0x18, 0x82, 0x8a, 0xd7, 0x47, 0xb0, 0xaa, 0x7c, 0xa7, 0xf1, 0x73, 0xd1, 0xe6, 0xe7, 0x73, 0x35, + 0xc8, 0x13, 0x88, 0x6d, 0xa8, 0x1f, 0x4d, 0xbf, 0xff, 0xfe, 0x62, 0xcc, 0x36, 0x81, 0xcd, 0xc7, + 0x54, 0x91, 0x3b, 0x6a, 0x63, 0x7d, 0x99, 0xcd, 0xc0, 0xc8, 0xe5, 0x12, 0x0f, 0x40, 0xb8, 0x47, + 0x3c, 0x9e, 0x3b, 0x61, 0x30, 0x66, 0xbd, 0x7f, 0xf3, 0x0b, 0x79, 0x2e, 0x31, 0x87, 0x21, 0x87, + 0x1c, 0xc7, 0xb7, 0xcd, 0x71, 0x24, 0xc5, 0x84, 0x2f, 0xa9, 0x9e, 0x92, 0x79, 0x25, 0x01, 0xa1, + 0xd4, 0x81, 0x2b, 0xd2, 0xee, 0x45, 0x2c, 0x35, 0x7c, 0x01, 0x38, 0x5d, 0x5f, 0xa4, 0x59, 0x7f, + 0xeb, 0x07, 0xb3, 0x22, 0xad, 0xca, 0xfa, 0x18, 0x1a, 0x7c, 0x1a, 0x42, 0x12, 0x19, 0x4e, 0xd1, + 0x9f, 0x65, 0x39, 0x57, 0xf6, 0x18, 0xd7, 0xa8, 0xdb, 0xd9, 0x43, 0xdd, 0xcf, 0x61, 0x2d, 0x73, + 0x18, 0x42, 0x9f, 0xfd, 0xed, 0xec, 0x67, 0x93, 0xd3, 0x10, 0x63, 0x75, 0xaa, 0x7e, 0xd2, 0x27, + 0xe7, 0x44, 0xec, 0xdf, 0xf9, 0x11, 0x22, 0xf6, 0x9b, 0x90, 0xae, 0x48, 0x73, 0x14, 0x3b, 0x9b, + 0x3f, 0x67, 0xe6, 0x9f, 0x00, 0x5b, 0xb1, 0x23, 0x1e, 0x01, 0x58, 0x93, 0x89, 0x77, 0xc1, 0x53, + 0xf3, 0x17, 0x34, 0x35, 0x37, 0x32, 0x53, 0xb3, 0x89, 0x48, 0x9a, 0x9b, 0x55, 0x4b, 0xfd, 0x44, + 0x05, 0x97, 0x84, 0x77, 0x7b, 0xec, 0x51, 0xfb, 0x9b, 0xd9, 0xb5, 0xbd, 0x1f, 0x44, 0xf1, 0xf6, + 0xd8, 0xc3, 0x56, 0xc0, 0x24, 0xf9, 0x2d, 0xba, 0x70, 0x39, 0xf0, 0x4d, 0x7b, 0x3a, 0xf1, 0xdc, + 0x11, 0xf6, 0x00, 0x9f, 0x42, 0x6e, 0x6e, 0x65, 0xef, 0x27, 0xd1, 0x17, 0xfb, 0xfe, 0xb6, 0x22, + 0x92, 0xa7, 0x9b, 0xeb, 0xc1, 0x2c, 0x88, 0x0c, 0x56, 0x34, 0x06, 0xc9, 0xd5, 0x9e, 0x16, 0x8b, + 0x06, 0x04, 0x55, 0x77, 0x7b, 0x1e, 0xc3, 0x5a, 0x4a, 0x85, 0x0d, 0x8c, 0x36, 0xb7, 0xb3, 0x33, + 0x39, 0x73, 0x0b, 0x72, 0x55, 0x65, 0x44, 0x58, 0x44, 0x7d, 0x17, 0x78, 0xde, 0x74, 0x22, 0x59, + 0xe9, 0x66, 0x5b, 0xf6, 0x1d, 0x01, 0x99, 0x4b, 0x66, 0x6c, 0x7a, 0xce, 0x78, 0x73, 0x27, 0x6b, + 0xd3, 0x73, 0xc6, 0x28, 0x66, 0x48, 0x3f, 0xfb, 0x17, 0xae, 0x73, 0x16, 0x6d, 0x3e, 0x21, 0xcd, + 0x4f, 0xde, 0xf9, 0x7b, 0x86, 0x20, 0xdc, 0xf7, 0x59, 0x8f, 0x25, 0x92, 0xcd, 0x5d, 0xbe, 0xf4, + 0xc5, 0x20, 0xa4, 0x10, 0x2d, 0xd8, 0x24, 0x76, 0x2e, 0xd7, 0x6a, 0xce, 0x0f, 0xa6, 0x33, 0xb7, + 0x24, 0xaf, 0x20, 0x2d, 0xbb, 0xd9, 0xec, 0x67, 0x3c, 0x62, 0x7e, 0x01, 0xaf, 0x2f, 0x2e, 0x44, + 0x85, 0x1d, 0xf8, 0x8a, 0xe4, 0x8d, 0xeb, 0x0b, 0x72, 0xcb, 0xd8, 0x03, 0x5f, 0xc2, 0xea, 0xb1, + 0x45, 0x3d, 0xe1, 0x99, 0xe3, 0xc0, 0x76, 0x36, 0x9f, 0x66, 0x83, 0xec, 0xd0, 0xa0, 0x3d, 0xb1, + 0xb0, 0x53, 0xbc, 0xbd, 0xc0, 0x76, 0x8c, 0xda, 0x71, 0x9a, 0x10, 0x8f, 0x61, 0x33, 0x9d, 0x84, + 0x53, 0x1f, 0xbf, 0x8b, 0x2a, 0xb5, 0xeb, 0x8f, 0xe2, 0xcd, 0x2e, 0xf5, 0xe9, 0xd5, 0x04, 0x7f, + 0x80, 0xe8, 0x6d, 0x89, 0x15, 0x2d, 0xd0, 0x88, 0x39, 0xc8, 0x5d, 0x2c, 0x72, 0x6d, 0x67, 0x73, + 0x2f, 0x1b, 0x44, 0x83, 0x59, 0x27, 0x92, 0xd0, 0x7e, 0x35, 0x70, 0x6d, 0xc7, 0x68, 0x1c, 0xe5, + 0xd2, 0xe2, 0x5d, 0x58, 0x57, 0xfa, 0x88, 0x79, 0x62, 0x45, 0x27, 0xe6, 0xa9, 0x73, 0xb1, 0xd9, + 0xa3, 0x06, 0x37, 0xa4, 0x32, 0xb2, 0x6b, 0x45, 0x27, 0x4f, 0x9d, 0x0b, 0x71, 0x1f, 0x75, 0x6a, + 0xda, 0x36, 0xdd, 0xc8, 0x3c, 0xb4, 0xc2, 0xd0, 0x75, 0xc2, 0xcd, 0x3e, 0x1f, 0xa1, 0x31, 0xa2, + 0x13, 0x6d, 0x31, 0x18, 0xf5, 0xe8, 0xec, 0x60, 0x48, 0x75, 0x7d, 0x9f, 0xf8, 0xa8, 0x96, 0x71, + 0x47, 0x62, 0x6d, 0xfd, 0x23, 0x68, 0x24, 0xfd, 0xc7, 0x4e, 0x39, 0xbf, 0x9c, 0xdb, 0x27, 0xea, + 0xb2, 0xd3, 0x06, 0xe4, 0x95, 0xf3, 0x3e, 0xd4, 0x93, 0x1c, 0x8e, 0x6f, 0x6f, 0x1a, 0xf3, 0x66, + 0x43, 0x49, 0x8f, 0xbb, 0xcb, 0xa7, 0x70, 0x8d, 0xa7, 0x39, 0x1f, 0x9d, 0x9c, 0x3a, 0x17, 0x49, + 0xcc, 0x94, 0x01, 0xd5, 0x7f, 0x83, 0xd0, 0x1d, 0xc4, 0x3e, 0x75, 0x2e, 0xa2, 0x84, 0x7b, 0x5e, + 0x27, 0xd7, 0x82, 0x91, 0xbc, 0x31, 0xcc, 0x66, 0x75, 0xd9, 0x96, 0x21, 0x8f, 0x0d, 0x12, 0xb4, + 0x12, 0xbc, 0x21, 0xdd, 0xd5, 0x44, 0x13, 0xd6, 0xd9, 0x0e, 0x94, 0x0d, 0x0e, 0x73, 0x40, 0x95, + 0xbc, 0xb2, 0x50, 0x21, 0x37, 0xd6, 0x5e, 0xcc, 0x18, 0xc9, 0x9e, 0xc0, 0x8d, 0xe4, 0xac, 0xd4, + 0xf9, 0x6e, 0x6a, 0xd1, 0x75, 0xab, 0x53, 0xe7, 0x82, 0xd9, 0xe2, 0xf3, 0xb9, 0xd9, 0x7d, 0x4d, + 0x51, 0xb7, 0x25, 0xf1, 0x53, 0xe7, 0x82, 0x78, 0xe3, 0x5d, 0x68, 0x58, 0x51, 0x70, 0x24, 0xb5, + 0xd8, 0x51, 0xe0, 0x6d, 0x3e, 0xa3, 0x71, 0xa8, 0x23, 0xd4, 0x50, 0x47, 0xb3, 0x3d, 0xb8, 0x9c, + 0x8e, 0x98, 0xe5, 0x1d, 0x07, 0xa1, 0x1b, 0x9f, 0x8c, 0x37, 0x7f, 0x95, 0xb5, 0x36, 0xd1, 0x84, + 0x4a, 0x26, 0x7f, 0x53, 0x11, 0x19, 0xe9, 0x58, 0x27, 0x30, 0xf1, 0x18, 0xae, 0xa7, 0xe5, 0xc5, + 0xc1, 0xc4, 0xf4, 0xcd, 0x33, 0x37, 0x3e, 0x31, 0xc9, 0xaf, 0xf9, 0x77, 0x65, 0x64, 0x0d, 0x45, + 0x30, 0x0c, 0x26, 0xbd, 0xe7, 0x6e, 0x7c, 0x32, 0x74, 0x9d, 0x48, 0x6c, 0xc1, 0x2d, 0xcb, 0xb6, + 0x26, 0x31, 0x2e, 0x08, 0xcc, 0x98, 0x44, 0x03, 0x09, 0x7c, 0xd3, 0x19, 0x4f, 0xe2, 0x8b, 0xcd, + 0xbf, 0x46, 0xd9, 0x6f, 0x28, 0xaa, 0x61, 0x30, 0xd9, 0x91, 0x34, 0x7d, 0xbf, 0x8d, 0x14, 0xfa, + 0x3f, 0x2b, 0x43, 0x45, 0x19, 0x2b, 0x44, 0x0d, 0x56, 0x0e, 0x7a, 0x4f, 0x7b, 0xfd, 0xe7, 0x3d, + 0x0e, 0x31, 0xd5, 0x1c, 0x0c, 0xda, 0xc6, 0x50, 0xb3, 0x45, 0x03, 0x80, 0xc2, 0xa9, 0x98, 0x83, + 0x56, 0xb3, 0xc7, 0x21, 0xa7, 0x28, 0x88, 0x0b, 0xa7, 0x97, 0xc4, 0x3a, 0xac, 0xee, 0x1c, 0xf4, + 0xf8, 0xca, 0x11, 0x81, 0x8a, 0x08, 0x6a, 0x7f, 0xcd, 0x7e, 0x7f, 0x0c, 0x2a, 0x21, 0x68, 0xaf, + 0x39, 0x6c, 0x1b, 0x1d, 0x05, 0x2a, 0x8b, 0x2b, 0xb0, 0x9e, 0x50, 0xa9, 0x12, 0xb4, 0x15, 0x04, + 0x3f, 0x6b, 0xb7, 0x86, 0x7d, 0xc3, 0xec, 0xf4, 0xb6, 0xdb, 0x5f, 0x33, 0x75, 0x05, 0xeb, 0xb7, + 0x6f, 0xf4, 0xbf, 0x6a, 0xb7, 0x86, 0x1a, 0x90, 0x2b, 0xe1, 0x93, 0x27, 0x5a, 0x4d, 0xd4, 0xa1, + 0xb2, 0xdd, 0x19, 0x0c, 0x3b, 0xbd, 0xd6, 0x50, 0xab, 0x63, 0xb5, 0x77, 0x3a, 0xdd, 0x61, 0xdb, + 0xd0, 0x56, 0x45, 0x05, 0x4a, 0x5f, 0xf5, 0x3b, 0x3d, 0xad, 0x41, 0xc1, 0x65, 0x9a, 0x7b, 0xfb, + 0xdd, 0xb6, 0xb6, 0x86, 0xd0, 0x41, 0xdf, 0x18, 0x6a, 0x1a, 0x42, 0x9f, 0x77, 0x7a, 0xdb, 0xfd, + 0xe7, 0xda, 0xba, 0xa8, 0x42, 0xf9, 0xa0, 0x87, 0x5f, 0x17, 0x62, 0x15, 0xaa, 0xf4, 0xd3, 0x6c, + 0x76, 0xbb, 0xda, 0xe5, 0x8c, 0xff, 0xe1, 0x06, 0xa2, 0xc8, 0x9b, 0x71, 0x80, 0x75, 0xb8, 0x82, + 0x2d, 0x4a, 0x92, 0x44, 0x7d, 0x15, 0xcb, 0xd9, 0xeb, 0xf4, 0x0e, 0x06, 0xda, 0x35, 0x24, 0xa6, + 0x9f, 0x84, 0xd9, 0xc4, 0x72, 0x3a, 0x3d, 0xea, 0xd0, 0x5b, 0xf8, 0x7b, 0xbb, 0xdd, 0x6d, 0x0f, + 0xdb, 0xda, 0x6d, 0x6c, 0x55, 0xb7, 0xdf, 0x7a, 0x6a, 0xf6, 0xf7, 0xb5, 0x37, 0xb0, 0x67, 0xf7, + 0x8d, 0xb6, 0x29, 0x09, 0xdf, 0x14, 0x1a, 0xd4, 0x77, 0x0e, 0x7e, 0xf5, 0xab, 0x6f, 0x4c, 0xd9, + 0xa8, 0xb7, 0xf0, 0x9b, 0x29, 0x85, 0x79, 0xf0, 0x54, 0x7b, 0x7b, 0x06, 0x34, 0x78, 0xaa, 0xbd, + 0x83, 0x9d, 0xa2, 0xfa, 0x5a, 0xbb, 0x87, 0x04, 0x46, 0xbb, 0x75, 0x60, 0x0c, 0x3a, 0xcf, 0xda, + 0x66, 0x6b, 0xd8, 0xd6, 0xde, 0xa5, 0x5e, 0xe8, 0xf4, 0x9e, 0x6a, 0xf7, 0xb1, 0x9a, 0xf8, 0x8b, + 0x3b, 0xf9, 0x3d, 0x21, 0xa0, 0x91, 0xd2, 0x12, 0xec, 0x7d, 0x24, 0xd9, 0x32, 0xfa, 0xcd, 0xed, + 0x56, 0x73, 0x30, 0xd4, 0x3e, 0xc0, 0x36, 0x0e, 0xf6, 0xbb, 0x9d, 0xa1, 0xf6, 0x00, 0x1b, 0xf2, + 0xa4, 0x39, 0xdc, 0x6d, 0x1b, 0xda, 0x87, 0x62, 0x0d, 0x6a, 0xc3, 0xce, 0x5e, 0xdb, 0x94, 0x7d, + 0xfa, 0x10, 0xbf, 0xb1, 0xd3, 0xe9, 0x76, 0xb5, 0x47, 0xe4, 0x3f, 0xd7, 0x34, 0x86, 0x1d, 0x1a, + 0xdf, 0x4f, 0xb0, 0x80, 0xe6, 0xfe, 0x7e, 0xf7, 0x1b, 0xed, 0x53, 0x6c, 0xe0, 0xde, 0x41, 0x77, + 0xd8, 0x31, 0x0f, 0xf6, 0xb7, 0x9b, 0xc3, 0xb6, 0xf6, 0x19, 0x8d, 0x72, 0x7f, 0x30, 0xdc, 0xde, + 0xeb, 0x6a, 0x9f, 0x53, 0x99, 0x34, 0xd3, 0x5a, 0xdd, 0x7e, 0xaf, 0xad, 0x3d, 0x46, 0xfa, 0xe6, + 0x76, 0x73, 0x7f, 0x88, 0xb5, 0x1b, 0xf6, 0xf7, 0xb5, 0x2f, 0xf4, 0x52, 0x65, 0x59, 0x5b, 0xd6, + 0x4b, 0x95, 0x3b, 0xda, 0x1d, 0xbd, 0x54, 0xd1, 0x35, 0xfd, 0x7e, 0x6d, 0xd0, 0x3f, 0x30, 0x5a, + 0x5c, 0x7b, 0xfd, 0x6f, 0x14, 0xa0, 0xa2, 0xac, 0x5a, 0xf8, 0xe1, 0x4e, 0xaf, 0xd7, 0x36, 0xb4, + 0x4b, 0x58, 0xb9, 0x6e, 0x7b, 0x67, 0xa8, 0x15, 0xc8, 0xfd, 0xb0, 0xf3, 0x64, 0x77, 0xa8, 0x2d, + 0xe1, 0xcf, 0xfe, 0x01, 0xf6, 0x73, 0x91, 0x3a, 0xa8, 0xbd, 0xd7, 0xd1, 0x4a, 0xf8, 0xab, 0xd9, + 0x1b, 0x76, 0xb4, 0x32, 0x4d, 0xa3, 0x4e, 0xef, 0x49, 0xb7, 0xad, 0x2d, 0x23, 0x74, 0xaf, 0x69, + 0x3c, 0xd5, 0x56, 0xb8, 0xd0, 0xed, 0xf6, 0xd7, 0x5a, 0x45, 0x2c, 0xc3, 0x52, 0xf7, 0xa1, 0x56, + 0x45, 0xd0, 0x76, 0x7b, 0xfb, 0x60, 0x5f, 0x03, 0xca, 0x3d, 0xe8, 0xef, 0x68, 0x35, 0xec, 0x04, + 0xfc, 0x65, 0xd2, 0x67, 0xeb, 0xfa, 0x3d, 0x58, 0x69, 0x1e, 0x1f, 0xd3, 0xde, 0x86, 0x1d, 0x75, + 0xd0, 0xed, 0xf2, 0xaa, 0xdb, 0xea, 0x0f, 0x87, 0xfd, 0x3d, 0xad, 0x80, 0x33, 0x1c, 0x5b, 0xb8, + 0xa4, 0x77, 0xa0, 0xa2, 0x04, 0xfb, 0x4c, 0x60, 0xa5, 0x0a, 0x94, 0xf6, 0x8d, 0xf6, 0x33, 0x76, + 0xa6, 0xed, 0xb5, 0xbf, 0xc6, 0xfa, 0xe3, 0x2f, 0x2c, 0xa8, 0x88, 0x35, 0xe0, 0x08, 0x48, 0x14, + 0x59, 0xa9, 0xdb, 0xe9, 0xb5, 0x9b, 0x86, 0x56, 0xd6, 0x1f, 0x43, 0x2d, 0xb3, 0xc3, 0xe2, 0xc4, + 0x78, 0xd2, 0xdc, 0xc7, 0xc9, 0xd5, 0x35, 0x65, 0xb1, 0x57, 0x41, 0x24, 0xa0, 0x74, 0xcc, 0x0a, + 0xfa, 0xa7, 0xb0, 0x3e, 0x27, 0x50, 0x51, 0xc5, 0x9b, 0x1d, 0x59, 0xf1, 0xce, 0x93, 0x5e, 0xdf, + 0x68, 0x73, 0x90, 0x27, 0x39, 0x9a, 0x4b, 0xfa, 0x7b, 0x50, 0x4d, 0x24, 0x3f, 0x9c, 0xdd, 0x2d, + 0xa3, 0x3f, 0x18, 0xf0, 0xe0, 0x5f, 0xc2, 0x34, 0x75, 0x37, 0xa7, 0x0b, 0xba, 0x0b, 0x8d, 0xfc, + 0x36, 0x2c, 0xee, 0xc0, 0x4d, 0x9e, 0xff, 0x5b, 0x07, 0x9d, 0xee, 0xb6, 0x39, 0xe8, 0x6c, 0xb7, + 0x67, 0x6e, 0x8f, 0xdf, 0x80, 0xab, 0x73, 0x14, 0x1c, 0x61, 0xaa, 0x20, 0xae, 0xc3, 0x95, 0x39, + 0x1c, 0xcd, 0xfa, 0x25, 0x7d, 0x1f, 0xc4, 0x3c, 0x83, 0x16, 0x37, 0x61, 0x33, 0x69, 0xb3, 0xd9, + 0xec, 0x3e, 0xe9, 0x1b, 0x9d, 0xe1, 0xee, 0x9e, 0x49, 0xfc, 0xe2, 0xd2, 0xcb, 0xb0, 0xbb, 0xcd, + 0xc1, 0xae, 0x56, 0xf8, 0xaa, 0x54, 0xb9, 0xad, 0xdd, 0xf9, 0xaa, 0x54, 0xb9, 0xab, 0xbd, 0xa5, + 0x0f, 0x60, 0x5d, 0x49, 0xcf, 0x74, 0x37, 0x9f, 0xcc, 0x3a, 0x1b, 0x50, 0xee, 0x3a, 0x2f, 0x1c, + 0x4f, 0x5d, 0xe5, 0xa6, 0x04, 0x42, 0xfb, 0x87, 0xdf, 0x76, 0x92, 0x30, 0x8a, 0x94, 0x40, 0x75, + 0xb9, 0x97, 0x89, 0xe4, 0x48, 0xe1, 0x14, 0xfe, 0xd5, 0x02, 0x54, 0x12, 0x99, 0xfc, 0x2e, 0x2c, + 0x0d, 0x07, 0xf2, 0x40, 0x68, 0xe3, 0x41, 0x1a, 0xb6, 0x77, 0xa8, 0x7e, 0x19, 0x4b, 0xc3, 0x81, + 0x78, 0x1f, 0x96, 0x39, 0x98, 0x9c, 0x3c, 0x02, 0xda, 0xc8, 0xcb, 0xf9, 0x43, 0xc2, 0x19, 0x92, + 0x46, 0x7c, 0x0a, 0xd5, 0xa4, 0xb6, 0xd2, 0x1a, 0x7d, 0x2d, 0x9f, 0x21, 0x41, 0x1b, 0x29, 0xa5, + 0xde, 0x85, 0x46, 0xbe, 0x40, 0x71, 0x0b, 0x80, 0x8b, 0xcc, 0x9c, 0xdd, 0x66, 0x20, 0xe2, 0x06, + 0xa8, 0x18, 0x77, 0xdb, 0x54, 0xb1, 0xd5, 0x24, 0xe6, 0xdd, 0xb6, 0xfe, 0x6f, 0x95, 0x01, 0x52, + 0xad, 0x1e, 0x3b, 0x22, 0xb1, 0xb1, 0x97, 0xa5, 0x97, 0xef, 0x6b, 0x50, 0xf5, 0x02, 0xcb, 0xce, + 0x86, 0xdf, 0xad, 0x20, 0x80, 0xe6, 0x58, 0x36, 0xde, 0x4c, 0x55, 0x86, 0x8e, 0xb9, 0x0a, 0xcb, + 0x47, 0x41, 0x38, 0xb6, 0x62, 0x79, 0x6f, 0x5f, 0xa6, 0x28, 0xa4, 0x0c, 0x39, 0xa9, 0xd9, 0xa6, + 0xe7, 0xfa, 0x0e, 0x07, 0xce, 0x2a, 0x19, 0x75, 0x09, 0xec, 0x22, 0x0c, 0x85, 0x6b, 0xc7, 0x1f, + 0x79, 0x01, 0x8a, 0x35, 0x87, 0x7c, 0x6b, 0xa1, 0x6e, 0x80, 0x02, 0x6d, 0x5d, 0x70, 0x6b, 0xc3, + 0xb1, 0xeb, 0x5b, 0xb1, 0xf4, 0xde, 0xa1, 0xd6, 0x2a, 0x08, 0x56, 0xf7, 0xdb, 0x28, 0x90, 0x26, + 0x77, 0x0e, 0x68, 0x52, 0x41, 0x00, 0x55, 0xf7, 0x75, 0x00, 0x27, 0x1a, 0x59, 0x13, 0x2e, 0xbc, + 0xca, 0xf1, 0xdf, 0x24, 0x64, 0xeb, 0x42, 0x74, 0xa1, 0x31, 0x3c, 0x44, 0x65, 0x22, 0xd8, 0xb6, + 0x62, 0xab, 0x15, 0x78, 0xd2, 0x18, 0x7e, 0x77, 0xd6, 0xfc, 0xf1, 0x20, 0x4f, 0xc6, 0x37, 0xe7, + 0x66, 0xf2, 0x8a, 0x4f, 0xa0, 0xee, 0x8e, 0x9c, 0x43, 0x27, 0x3c, 0x66, 0x41, 0xab, 0x96, 0xbb, + 0x70, 0xcf, 0x18, 0x12, 0xb2, 0x6a, 0x6e, 0x9a, 0x40, 0x25, 0x6d, 0x1c, 0xf8, 0xc7, 0x81, 0x7d, + 0xc8, 0xb9, 0xea, 0x59, 0xc3, 0xde, 0x1e, 0x62, 0x38, 0x8f, 0x24, 0xa2, 0x3c, 0xbf, 0x0d, 0x6b, + 0xd8, 0xf3, 0x51, 0x1c, 0x3a, 0xd6, 0x98, 0xb3, 0xad, 0x66, 0x67, 0x20, 0xd6, 0x68, 0x40, 0x48, + 0xca, 0xdb, 0x48, 0x89, 0x29, 0xfb, 0x27, 0xe9, 0x75, 0x76, 0xca, 0xdb, 0xc8, 0x79, 0xd5, 0x33, + 0x86, 0x3f, 0x7a, 0x94, 0x26, 0xc4, 0x03, 0x80, 0x53, 0xeb, 0xe8, 0xd4, 0xe2, 0x3c, 0x6b, 0xd9, + 0x6a, 0x3e, 0x45, 0x38, 0xe5, 0xa8, 0x9e, 0xaa, 0x9f, 0x37, 0x9a, 0x70, 0x79, 0x41, 0xaf, 0xfd, + 0xa4, 0x6b, 0x60, 0x7f, 0xa7, 0x00, 0x8d, 0x7c, 0x5b, 0x66, 0x62, 0x69, 0x56, 0x93, 0x58, 0x9a, + 0x02, 0x4a, 0x93, 0x20, 0x54, 0x37, 0x36, 0xe8, 0x77, 0x1a, 0xda, 0xba, 0x98, 0x0d, 0x6d, 0xbd, + 0x09, 0x2b, 0xa1, 0xc3, 0xb7, 0xaf, 0x39, 0x52, 0x83, 0x4a, 0xe2, 0x84, 0x9d, 0x4c, 0xa3, 0x13, + 0xc7, 0x56, 0xda, 0x24, 0x5f, 0x23, 0xa8, 0x33, 0x50, 0x6a, 0x93, 0xd7, 0x60, 0xc5, 0x9a, 0xb8, + 0xa4, 0xa0, 0xc8, 0xb8, 0x8c, 0xd6, 0xc4, 0x7d, 0xea, 0x5c, 0xe8, 0xdf, 0x43, 0x2d, 0xd3, 0x77, + 0x58, 0xa1, 0x53, 0xd7, 0x57, 0x91, 0x25, 0xe8, 0x37, 0x56, 0x5e, 0x86, 0x22, 0xe4, 0xbb, 0x20, + 0x32, 0x85, 0x1f, 0x56, 0xd7, 0xf8, 0xe8, 0x74, 0x56, 0x1d, 0xf5, 0x4b, 0xe0, 0x2f, 0x11, 0x86, + 0xcb, 0x1a, 0x2b, 0x62, 0x07, 0x67, 0xbe, 0xac, 0x78, 0x92, 0xd6, 0xff, 0xb3, 0x25, 0xa8, 0x26, + 0x83, 0x80, 0x2d, 0x3c, 0x0c, 0x83, 0x53, 0x27, 0x8c, 0xe4, 0xd7, 0x55, 0x92, 0x7a, 0x24, 0x98, + 0xb8, 0x23, 0x75, 0x29, 0x9a, 0x12, 0xe2, 0x66, 0xf6, 0x0a, 0x24, 0x47, 0x2b, 0xc9, 0xdc, 0x78, + 0xbc, 0x05, 0x60, 0x4d, 0xe3, 0x60, 0x14, 0x8c, 0xc7, 0x6e, 0x2c, 0xbf, 0x9c, 0x81, 0x60, 0x99, + 0xa4, 0xa2, 0xc9, 0xde, 0xe2, 0x44, 0x86, 0x29, 0x2c, 0xe7, 0x98, 0xc2, 0x4d, 0xa8, 0x46, 0xce, + 0xc4, 0x0a, 0xad, 0x38, 0x08, 0xe5, 0x6a, 0x4e, 0x01, 0x14, 0x0e, 0x13, 0x35, 0x2b, 0x15, 0xb7, + 0xae, 0x68, 0xac, 0x50, 0xba, 0x63, 0x8b, 0x3b, 0x74, 0x15, 0xc9, 0x4c, 0xd0, 0xec, 0x87, 0x0d, + 0x27, 0x56, 0x34, 0x90, 0x14, 0x02, 0x4a, 0xe4, 0x13, 0xc3, 0x5e, 0x78, 0xf4, 0x5b, 0xbc, 0x03, + 0x14, 0x74, 0x3d, 0x98, 0xc6, 0x66, 0xe4, 0x8c, 0x02, 0xdf, 0x8e, 0xa4, 0x1f, 0x5e, 0x43, 0x82, + 0x07, 0x0c, 0xd5, 0xff, 0xeb, 0x25, 0xa8, 0x65, 0xd6, 0x28, 0xb9, 0x20, 0x59, 0x13, 0x3a, 0xf9, + 0x4a, 0xee, 0x6f, 0x57, 0x25, 0x84, 0x43, 0x84, 0x8c, 0xac, 0xd8, 0xf2, 0x82, 0xe3, 0x4c, 0xf0, + 0x13, 0x09, 0xe9, 0xd8, 0xe4, 0x15, 0x68, 0x8d, 0x1d, 0x0e, 0x82, 0x2e, 0x2f, 0xef, 0x27, 0x80, + 0x74, 0x5e, 0x96, 0xb2, 0xf3, 0x52, 0x63, 0xf7, 0x51, 0xee, 0x45, 0xf2, 0x16, 0xbd, 0x0d, 0x35, + 0x65, 0x49, 0xc2, 0xaf, 0x48, 0x0f, 0x47, 0x05, 0xea, 0xd8, 0xe2, 0x6d, 0x48, 0x43, 0xca, 0x9b, + 0x56, 0x64, 0x06, 0x47, 0xea, 0xde, 0x5c, 0x02, 0x6e, 0x46, 0xfd, 0xa3, 0xc4, 0xed, 0x88, 0xac, + 0x02, 0x92, 0x47, 0x22, 0x80, 0xa4, 0x94, 0x07, 0x70, 0x59, 0x1e, 0x61, 0xd2, 0xc4, 0x77, 0x3c, + 0xdb, 0x74, 0x6d, 0xf6, 0x9f, 0x2e, 0x1b, 0xeb, 0x09, 0x6a, 0x07, 0x31, 0x1d, 0x9b, 0x6c, 0x2e, + 0x52, 0x01, 0xb7, 0xdd, 0x63, 0x27, 0x8a, 0xe5, 0x2d, 0xaf, 0x3a, 0x03, 0xb7, 0x09, 0xa6, 0xff, + 0xdf, 0xcb, 0x50, 0x4d, 0x98, 0xd7, 0xab, 0x3a, 0xf3, 0x4d, 0x58, 0x1d, 0x05, 0xbe, 0xcf, 0x9e, + 0x02, 0x69, 0x7f, 0xd6, 0x53, 0x20, 0xdd, 0xd3, 0x15, 0x19, 0x22, 0x75, 0x0b, 0x9d, 0x63, 0x75, + 0xac, 0xa7, 0x98, 0x67, 0xf2, 0x3e, 0xfa, 0x0d, 0xa8, 0x20, 0xd7, 0x3b, 0xb4, 0x22, 0xd5, 0xcd, + 0x49, 0x1a, 0x67, 0xf4, 0x28, 0xf0, 0x3c, 0xe9, 0xc9, 0x20, 0x83, 0xe7, 0xa6, 0x10, 0xf1, 0x90, + 0xc2, 0x27, 0x93, 0x23, 0xc0, 0x32, 0xed, 0x07, 0x9b, 0x19, 0x6e, 0xcc, 0xbe, 0x00, 0x7b, 0x5c, + 0x79, 0x43, 0x11, 0x8a, 0x9f, 0x83, 0x26, 0x79, 0xc7, 0x24, 0x74, 0x6c, 0x92, 0xe2, 0xe4, 0x25, + 0x85, 0x8d, 0x4c, 0xe6, 0x7d, 0x85, 0x33, 0xd6, 0x98, 0x3a, 0x01, 0xe0, 0x4c, 0x4d, 0x87, 0x61, + 0x62, 0xc5, 0x27, 0xec, 0xfa, 0x5e, 0x35, 0x1a, 0x09, 0x78, 0x1f, 0xa1, 0xe2, 0x13, 0xb8, 0x1a, + 0x3a, 0x91, 0x6b, 0x4f, 0x2d, 0xcf, 0xcc, 0x0f, 0x04, 0x87, 0xe2, 0xdd, 0x50, 0xd8, 0x9d, 0xcc, + 0x80, 0x60, 0xf1, 0x63, 0xeb, 0x9c, 0x0e, 0x81, 0x3c, 0xcf, 0xf1, 0xdc, 0x68, 0x2c, 0x23, 0x32, + 0x36, 0xc6, 0xd6, 0xf9, 0x7e, 0x0a, 0xa5, 0x1b, 0x49, 0x13, 0xcf, 0x9d, 0x89, 0xf2, 0xc7, 0x63, + 0x89, 0x70, 0x83, 0xd1, 0x42, 0x87, 0x55, 0x2c, 0x90, 0xec, 0x9d, 0x89, 0x33, 0x7b, 0xd1, 0xa8, + 0x8d, 0x2d, 0xb6, 0x24, 0x04, 0x67, 0x74, 0x2d, 0x3f, 0xa1, 0x61, 0xff, 0x59, 0xe9, 0xc2, 0x2a, + 0x89, 0xd8, 0x85, 0x96, 0xaa, 0xc6, 0xb3, 0x43, 0x0d, 0x6b, 0x83, 0xdd, 0x87, 0x24, 0x58, 0x8d, + 0xe9, 0x75, 0xe0, 0xa3, 0x54, 0x9c, 0x22, 0x6b, 0x44, 0xb1, 0x42, 0x69, 0x5e, 0x09, 0xd3, 0xc8, + 0x09, 0x99, 0x7d, 0x9a, 0xc4, 0x78, 0x39, 0xd2, 0xdf, 0x2a, 0x82, 0x89, 0x81, 0x3e, 0x45, 0x0e, + 0x7c, 0x0f, 0x34, 0xa2, 0x53, 0x27, 0x2f, 0x51, 0xe0, 0xd3, 0xa9, 0x6d, 0xdd, 0x68, 0x20, 0x5c, + 0x9e, 0xab, 0x44, 0x81, 0x2f, 0x3e, 0x87, 0x4d, 0xa2, 0x9c, 0xb8, 0x13, 0x07, 0xc5, 0x17, 0xe4, + 0x3c, 0xc7, 0x0e, 0xe7, 0x10, 0x77, 0x8a, 0xf7, 0xea, 0xc6, 0x15, 0xc4, 0xef, 0x4b, 0xf4, 0x00, + 0xb1, 0x94, 0xf1, 0x3e, 0xac, 0x67, 0xaa, 0x22, 0x87, 0x86, 0xe3, 0x86, 0xad, 0x25, 0x95, 0x91, + 0xa3, 0xf2, 0x11, 0x6c, 0x48, 0x1f, 0x13, 0x49, 0xce, 0xd3, 0x89, 0x0e, 0x6e, 0x2b, 0x86, 0x90, + 0x38, 0xca, 0xc1, 0xf3, 0x4e, 0xbc, 0x01, 0x75, 0xb2, 0x3e, 0xc8, 0x08, 0x9b, 0x32, 0x76, 0x58, + 0x8d, 0x60, 0x1c, 0xc0, 0x4e, 0xff, 0xeb, 0x05, 0x10, 0xf3, 0x53, 0xf5, 0x65, 0xd1, 0x70, 0x70, + 0xaa, 0xa9, 0x50, 0x14, 0xf8, 0x5b, 0xbc, 0x0b, 0x2b, 0xe3, 0x80, 0xc5, 0xa9, 0xe2, 0x4b, 0x62, + 0xd1, 0x2d, 0x8f, 0x03, 0x12, 0xaf, 0xde, 0x81, 0xb5, 0x51, 0xe0, 0xcb, 0x41, 0x63, 0xee, 0xc2, + 0x6b, 0xad, 0x91, 0x82, 0x91, 0xc7, 0xe8, 0xff, 0x69, 0x01, 0x1a, 0xf9, 0x05, 0x20, 0xde, 0xa6, + 0x28, 0xde, 0xb9, 0x38, 0xe0, 0x79, 0x8a, 0xfe, 0x84, 0xa2, 0x7b, 0x2f, 0xaa, 0xe2, 0xeb, 0x00, + 0x24, 0x24, 0xf0, 0x68, 0xc8, 0xb0, 0xbe, 0x04, 0xa1, 0x11, 0xb8, 0x0d, 0x35, 0x7e, 0xa6, 0x82, + 0xf1, 0x25, 0x1a, 0x2d, 0xce, 0x11, 0x11, 0xc1, 0x47, 0x19, 0x8f, 0x0f, 0xf6, 0xe8, 0x58, 0xbc, + 0x48, 0x13, 0x2a, 0xfd, 0x8f, 0x0b, 0x00, 0xe9, 0x1a, 0xc0, 0x99, 0x78, 0xea, 0x5c, 0xd0, 0x32, + 0x55, 0x5b, 0xec, 0xa9, 0x73, 0xb1, 0x2f, 0xeb, 0x26, 0xdd, 0x0e, 0x23, 0x79, 0xe7, 0xb3, 0x6e, + 0x54, 0xd9, 0xd3, 0x30, 0xe2, 0x20, 0x4b, 0xd2, 0xd1, 0x30, 0x53, 0x75, 0xf6, 0x2d, 0x8c, 0x28, + 0x8c, 0xc6, 0x1a, 0xe7, 0xa6, 0xa1, 0x8f, 0xdc, 0x17, 0x2a, 0x9c, 0x14, 0x3b, 0x46, 0x76, 0x14, + 0x14, 0x09, 0xb9, 0x9c, 0x94, 0x90, 0x2f, 0x61, 0xb1, 0x1b, 0x64, 0x42, 0xa8, 0x7b, 0x4a, 0xe0, + 0x6f, 0xc6, 0x31, 0x6d, 0xb0, 0x89, 0x29, 0xbe, 0xa0, 0x62, 0x48, 0xb1, 0x15, 0xfe, 0x35, 0xba, + 0xab, 0x2a, 0xc3, 0x47, 0x48, 0xb9, 0x3f, 0x09, 0x0b, 0xf1, 0x36, 0x8e, 0xb4, 0xa7, 0xb6, 0x87, + 0xc4, 0x8f, 0xad, 0x6c, 0xac, 0x8e, 0x02, 0x8f, 0xb7, 0x06, 0x04, 0xea, 0xff, 0x5a, 0x19, 0x20, + 0x3d, 0x84, 0xcd, 0xad, 0xd8, 0x42, 0x7e, 0xc5, 0x3e, 0x84, 0xab, 0x32, 0xbe, 0x5f, 0x72, 0x21, + 0xcb, 0xf5, 0xcd, 0x43, 0x4b, 0x89, 0x70, 0x42, 0x62, 0xf9, 0x1a, 0x4d, 0xc7, 0xdf, 0xb2, 0x62, + 0xf1, 0x18, 0x79, 0x64, 0x9a, 0x27, 0xbe, 0x98, 0xbc, 0x74, 0x8a, 0xae, 0xa6, 0xd9, 0x87, 0x17, + 0x13, 0xf1, 0x11, 0x85, 0x49, 0x0a, 0x9d, 0xe8, 0xc4, 0x8c, 0xa3, 0xec, 0xc7, 0xf8, 0x56, 0xfc, + 0xba, 0x44, 0x0e, 0xa3, 0xe4, 0x5b, 0x1f, 0xc1, 0x15, 0xc9, 0x24, 0x66, 0xaa, 0xc7, 0xee, 0x92, + 0xd2, 0x08, 0x9d, 0xad, 0x1d, 0x8d, 0x3c, 0x9d, 0x4c, 0xab, 0xe7, 0x54, 0x2a, 0x38, 0xf2, 0xd8, + 0x19, 0x32, 0x74, 0x36, 0x1d, 0x2f, 0x4b, 0xc9, 0x85, 0x13, 0x42, 0x87, 0x12, 0xae, 0x10, 0x62, + 0xc6, 0x8d, 0x87, 0x8d, 0x07, 0xf4, 0x5c, 0x0c, 0x85, 0x3d, 0x0f, 0x6c, 0xc7, 0x20, 0x9c, 0xf8, + 0x00, 0x77, 0xe8, 0xb4, 0xd9, 0x2a, 0xe6, 0x7f, 0x4d, 0xda, 0xb1, 0x93, 0x86, 0x1a, 0x1c, 0xfd, + 0xff, 0x3d, 0x10, 0x99, 0x9a, 0x2b, 0xea, 0x3a, 0x51, 0xaf, 0x25, 0xd5, 0x96, 0xc4, 0xef, 0x00, + 0x55, 0x31, 0xbd, 0x5a, 0x34, 0x73, 0x16, 0x8b, 0x48, 0x79, 0x1d, 0xe9, 0x4a, 0xda, 0x3a, 0xd3, + 0x8a, 0xcd, 0xf8, 0xc4, 0x21, 0xa3, 0x37, 0xdf, 0x2d, 0x5a, 0x4f, 0x1a, 0xda, 0x8c, 0x87, 0x27, + 0x4e, 0x9b, 0x78, 0x6d, 0xe2, 0xed, 0xb2, 0xf6, 0xc3, 0xde, 0x2e, 0x9f, 0xc1, 0x66, 0xfe, 0xbc, + 0x23, 0xd3, 0xdd, 0xcc, 0xc6, 0x37, 0xb2, 0x77, 0xb6, 0x92, 0x1e, 0xbf, 0x0f, 0xeb, 0x28, 0x13, + 0xe6, 0xf2, 0x4a, 0xc7, 0xb4, 0xb5, 0x13, 0x2b, 0xca, 0x1e, 0x90, 0xe8, 0xff, 0x60, 0x19, 0x1a, + 0xf9, 0x63, 0x69, 0x8e, 0xa7, 0xc6, 0xfb, 0x7c, 0x81, 0xa4, 0x9d, 0x64, 0x37, 0x7f, 0x0d, 0xaa, + 0x7c, 0xb1, 0x0b, 0x99, 0xb1, 0x0a, 0x3d, 0x7d, 0x2a, 0x59, 0xf0, 0xbb, 0xb0, 0x32, 0x39, 0x7d, + 0x05, 0x83, 0x9c, 0x70, 0xd8, 0x93, 0x77, 0x61, 0x65, 0x7a, 0x9a, 0x06, 0x16, 0x5f, 0x48, 0x3a, + 0x3d, 0x95, 0x9e, 0xce, 0xd7, 0xe5, 0x81, 0xba, 0xbc, 0xf5, 0x35, 0x9e, 0x7a, 0xb1, 0x6b, 0xd2, + 0x91, 0x80, 0x5c, 0xda, 0x57, 0x99, 0xa0, 0x43, 0x78, 0x8a, 0x9c, 0x40, 0x87, 0x98, 0xc8, 0xef, + 0x64, 0xb0, 0xa5, 0x44, 0x66, 0x29, 0x1b, 0xc0, 0xc1, 0x95, 0xa8, 0x39, 0xef, 0x82, 0x86, 0x9a, + 0x86, 0xe7, 0x8e, 0xe2, 0x84, 0x6a, 0x85, 0xa8, 0xd6, 0x14, 0x5c, 0x91, 0xbe, 0x05, 0x0d, 0x79, + 0x37, 0x47, 0x11, 0x72, 0x38, 0x80, 0x55, 0x86, 0x2a, 0xb2, 0xcf, 0xe0, 0x5a, 0x60, 0x9f, 0x4e, + 0xd5, 0x35, 0x33, 0x2b, 0x3c, 0x74, 0xe3, 0xd0, 0x4a, 0xe2, 0xcc, 0x55, 0x8c, 0x2b, 0x88, 0x66, + 0x2e, 0xd0, 0x4c, 0x91, 0xe4, 0xab, 0xc8, 0x59, 0x54, 0xf1, 0x40, 0xf5, 0x58, 0x65, 0xa8, 0x2a, + 0xfe, 0x13, 0xb8, 0x9a, 0xc4, 0x3f, 0x71, 0xc6, 0x8e, 0x1f, 0x9b, 0xa1, 0x43, 0xae, 0x5e, 0xf2, + 0x6d, 0x80, 0x0d, 0x15, 0x06, 0x85, 0x90, 0x06, 0xe3, 0xc4, 0x43, 0xb8, 0x32, 0x93, 0x4b, 0x8e, + 0x20, 0xcf, 0xfd, 0xcb, 0xb9, 0x4c, 0x72, 0x30, 0xdb, 0x70, 0x7b, 0x26, 0x4f, 0x2e, 0x9a, 0x27, + 0xe6, 0x5e, 0xa5, 0xdc, 0x37, 0x73, 0xb9, 0xb3, 0x51, 0x2b, 0xb1, 0x98, 0xcf, 0x61, 0x73, 0xa6, + 0x18, 0x1c, 0x11, 0x66, 0x94, 0x1c, 0xb5, 0xf4, 0x4a, 0x2e, 0xff, 0x53, 0xe7, 0x82, 0x19, 0xeb, + 0xcf, 0xe1, 0xe6, 0x4c, 0xc6, 0x5c, 0xf7, 0xcb, 0x6b, 0x78, 0xd7, 0x73, 0x99, 0xfb, 0x99, 0xa1, + 0xc0, 0xa9, 0x8a, 0x9f, 0xe2, 0xe0, 0x88, 0x1a, 0x49, 0x8c, 0xb8, 0x37, 0xf5, 0x28, 0x3e, 0xe2, + 0x3b, 0x8c, 0xe4, 0x33, 0xd5, 0xf5, 0xd9, 0x33, 0x55, 0x22, 0xe4, 0x83, 0xd4, 0xb7, 0x61, 0x4d, + 0x11, 0xf2, 0xc9, 0x52, 0x44, 0x42, 0x4e, 0xd9, 0x58, 0x95, 0x24, 0x74, 0xa0, 0x14, 0xe9, 0x7f, + 0x58, 0x86, 0x7a, 0xd6, 0xe7, 0xe7, 0xc7, 0x5c, 0x7a, 0xfb, 0x49, 0x57, 0x46, 0xa5, 0x9a, 0x47, + 0x7d, 0x82, 0xab, 0xb9, 0x98, 0xa8, 0x79, 0xcd, 0x69, 0x8c, 0x12, 0x0d, 0xa9, 0x5e, 0x81, 0xa7, + 0x22, 0x04, 0x31, 0xff, 0xc6, 0x9d, 0x4b, 0x06, 0x07, 0xfa, 0x48, 0x06, 0x20, 0xa3, 0x90, 0x90, + 0x14, 0x9c, 0x61, 0xde, 0x07, 0xba, 0xae, 0x22, 0x42, 0x52, 0x30, 0x8a, 0x87, 0xb0, 0x96, 0x86, + 0x7b, 0xe2, 0x2c, 0xcb, 0x73, 0x59, 0x56, 0x93, 0x58, 0x4f, 0x32, 0xc8, 0xf8, 0xaa, 0x1b, 0x99, + 0x81, 0x67, 0xab, 0xb0, 0x7d, 0x2b, 0xca, 0x23, 0xb3, 0xef, 0xd9, 0x32, 0x34, 0x1f, 0xd3, 0xf8, + 0xce, 0x99, 0xa2, 0x49, 0xbc, 0x36, 0x7b, 0xce, 0x99, 0xa4, 0x79, 0x00, 0x97, 0xb1, 0xb9, 0x72, + 0x4d, 0x70, 0xb0, 0x8e, 0x20, 0x94, 0x6b, 0x08, 0x99, 0x1b, 0x2f, 0xa1, 0x81, 0x44, 0x64, 0xae, + 0x6b, 0x86, 0xc1, 0x99, 0xe9, 0x4f, 0xc7, 0x87, 0xcc, 0xe6, 0xa5, 0x30, 0x2f, 0xaf, 0x6b, 0x1a, + 0xc1, 0x59, 0x8f, 0x50, 0xd8, 0x5f, 0xf7, 0x61, 0x5d, 0xad, 0xd2, 0x11, 0x9d, 0x66, 0xa9, 0x1b, + 0xc7, 0x65, 0x63, 0x8d, 0x11, 0x4d, 0x82, 0x23, 0xed, 0xbb, 0x09, 0x2d, 0x5f, 0x1c, 0x25, 0x5a, + 0x5e, 0x3d, 0x8d, 0x38, 0xbd, 0x35, 0x8a, 0xa4, 0x2d, 0xb8, 0x15, 0x87, 0xd6, 0xe8, 0xd4, 0x7c, + 0xd9, 0xf2, 0x91, 0x17, 0x55, 0x5f, 0x23, 0xaa, 0xe6, 0xc2, 0xc5, 0xf3, 0x63, 0x56, 0x5f, 0xe3, + 0xd5, 0xab, 0x4f, 0xff, 0x83, 0x12, 0xac, 0xcf, 0x39, 0x4b, 0xa1, 0x42, 0x9d, 0xbe, 0x2d, 0x86, + 0x3f, 0x51, 0x78, 0x1e, 0x5b, 0xf1, 0xe8, 0x04, 0x75, 0xb4, 0x23, 0xf7, 0x5c, 0xbd, 0xab, 0x46, + 0xb0, 0x7d, 0x02, 0x21, 0x2f, 0x65, 0xb9, 0x8a, 0xdd, 0x49, 0x59, 0xc8, 0x61, 0x91, 0x8d, 0x7d, + 0xb3, 0x55, 0xc8, 0x8f, 0xf9, 0x2b, 0x22, 0x1c, 0xf2, 0xe3, 0x06, 0x54, 0xfd, 0x80, 0xae, 0x11, + 0x4f, 0x4e, 0x25, 0xdf, 0x5e, 0xf1, 0x83, 0xb8, 0xef, 0xef, 0x9f, 0x22, 0x83, 0x9a, 0x46, 0x74, + 0x11, 0xec, 0xd0, 0x09, 0xa3, 0x13, 0x37, 0x71, 0x5b, 0x60, 0x61, 0xe1, 0xf2, 0x34, 0x72, 0xf6, + 0x12, 0x9c, 0xb4, 0x37, 0x35, 0xa1, 0x4e, 0x33, 0xd9, 0x1f, 0x05, 0xe4, 0x08, 0xc5, 0x8e, 0xf7, + 0xb7, 0x16, 0x78, 0x88, 0x3d, 0x75, 0x2e, 0xda, 0x92, 0xca, 0xc0, 0x0d, 0x41, 0x25, 0xc4, 0xbb, + 0x14, 0xcd, 0xe9, 0xd0, 0x49, 0x6d, 0xa4, 0x79, 0x36, 0x50, 0x25, 0x2c, 0xed, 0x42, 0x4f, 0xe1, + 0x3a, 0x6f, 0x25, 0xe3, 0x49, 0xe0, 0xe3, 0x78, 0xa4, 0x19, 0xa3, 0xbc, 0x4f, 0x71, 0x66, 0x0b, + 0xbb, 0x4a, 0x5b, 0x8d, 0xcc, 0xb1, 0xaf, 0xca, 0xc2, 0x6d, 0x07, 0xd8, 0x29, 0x80, 0x3a, 0x0c, + 0xe6, 0x3d, 0xb3, 0x08, 0x4b, 0xeb, 0xe9, 0x5d, 0xd0, 0xa2, 0x91, 0xe5, 0x59, 0x61, 0x46, 0x7d, + 0x66, 0x56, 0xbf, 0xc6, 0xf0, 0x54, 0x71, 0x78, 0x1d, 0x60, 0x3c, 0x8d, 0x62, 0x93, 0xfc, 0x5c, + 0x54, 0x18, 0x69, 0x84, 0xd0, 0x49, 0x88, 0x7e, 0x13, 0x96, 0x3b, 0x89, 0x53, 0x5c, 0x12, 0x93, + 0xa1, 0x28, 0x9f, 0x2f, 0x0a, 0xa0, 0xca, 0xde, 0x12, 0x7b, 0xd6, 0x44, 0xdc, 0x87, 0xe2, 0xd8, + 0x9a, 0xc8, 0xdb, 0x3a, 0x9b, 0xc9, 0x95, 0x26, 0xc6, 0x3e, 0xd8, 0xb3, 0x26, 0x6c, 0xe7, 0x45, + 0xa2, 0x1b, 0x9f, 0x41, 0x45, 0x01, 0x7e, 0x5a, 0x24, 0xab, 0x25, 0xa8, 0x6e, 0x67, 0x9d, 0x5a, + 0x51, 0x17, 0x8e, 0xc3, 0xa9, 0x4f, 0x4d, 0x94, 0x0f, 0xb5, 0x8c, 0x2c, 0x7f, 0x28, 0x41, 0x8a, + 0xa1, 0xbe, 0xe4, 0xd6, 0x0b, 0x31, 0xd4, 0x9b, 0x00, 0x72, 0x79, 0xba, 0xb6, 0x92, 0xc3, 0x2b, + 0x21, 0x2e, 0xcc, 0x8e, 0x7d, 0xbe, 0xf8, 0x8e, 0x71, 0xe9, 0xc7, 0xdf, 0x31, 0x2e, 0x2f, 0xbc, + 0x63, 0xfc, 0x76, 0x2a, 0x7a, 0xd3, 0xce, 0x66, 0x9f, 0xcb, 0x47, 0x21, 0x94, 0xa0, 0x8d, 0x3b, + 0x9a, 0x7d, 0x2e, 0xbe, 0x84, 0x86, 0x6a, 0x9d, 0x2c, 0x0f, 0x72, 0x71, 0x05, 0x25, 0x8e, 0xbd, + 0x60, 0x57, 0xe3, 0x6c, 0x32, 0xbf, 0x51, 0xd4, 0x5e, 0x71, 0x8f, 0xf9, 0x6f, 0x16, 0x40, 0x48, + 0x1f, 0xa6, 0x9d, 0xa9, 0xe7, 0x0d, 0x9d, 0x73, 0xda, 0x8f, 0xee, 0xc3, 0x7a, 0xee, 0x82, 0x4e, + 0x46, 0xd3, 0x59, 0xcb, 0xdc, 0xc8, 0x51, 0xc1, 0x9a, 0xe7, 0x42, 0xf3, 0x2e, 0x2d, 0x0c, 0xcd, + 0xbb, 0x38, 0xe4, 0xef, 0x6d, 0xa8, 0x65, 0x03, 0xdb, 0xb2, 0xea, 0x0b, 0x56, 0x12, 0xd3, 0x56, + 0xff, 0x9f, 0x96, 0x00, 0x52, 0x3f, 0xab, 0xbf, 0xec, 0x0b, 0xe2, 0x0b, 0x86, 0xa4, 0xb8, 0x68, + 0x48, 0xee, 0x81, 0x96, 0xa5, 0xcb, 0x44, 0x58, 0x6e, 0xa4, 0x84, 0x4a, 0x05, 0x74, 0xa3, 0x6c, + 0x10, 0x5b, 0x0a, 0x9d, 0x26, 0xef, 0xd4, 0x32, 0x92, 0x45, 0x50, 0xc9, 0xb0, 0x2a, 0x6e, 0xc4, + 0x92, 0x00, 0x49, 0xaf, 0x2a, 0x27, 0x79, 0x58, 0x04, 0xd3, 0x38, 0xb9, 0x7f, 0xb3, 0x22, 0xa5, + 0x57, 0x59, 0xd2, 0x73, 0x46, 0xab, 0xfb, 0x37, 0x9f, 0x42, 0xf5, 0x68, 0xea, 0x79, 0x66, 0xec, + 0x9c, 0xab, 0x3b, 0x41, 0x9b, 0x39, 0x17, 0xb5, 0xcc, 0xf0, 0x1a, 0x95, 0x23, 0x99, 0xd0, 0xff, + 0xd7, 0x65, 0x28, 0xb3, 0x61, 0xfc, 0x33, 0xa8, 0x46, 0xf1, 0x38, 0xce, 0xde, 0x16, 0x91, 0xae, + 0x47, 0x84, 0xa7, 0xcb, 0x1e, 0xb4, 0x6d, 0xb0, 0xcc, 0x83, 0xb4, 0xc4, 0xeb, 0x36, 0xa0, 0x1c, + 0xc5, 0xce, 0x24, 0x92, 0x71, 0x57, 0x38, 0x21, 0xee, 0x40, 0xd9, 0x0f, 0x6c, 0x27, 0xca, 0x47, + 0x57, 0xe9, 0xa1, 0x36, 0xc6, 0x08, 0xa1, 0xc3, 0x72, 0x32, 0xe2, 0x73, 0x37, 0x36, 0x18, 0x43, + 0x31, 0xdb, 0x1c, 0x0b, 0xb9, 0xaf, 0x8a, 0x58, 0x9d, 0xa4, 0x51, 0xed, 0xa0, 0xf3, 0x34, 0xeb, + 0x58, 0xc5, 0xf0, 0x97, 0x49, 0x71, 0x07, 0x6a, 0xf8, 0xf3, 0x79, 0xe8, 0xc6, 0xce, 0xe0, 0x91, + 0x92, 0x29, 0x32, 0x20, 0x71, 0x0b, 0xc0, 0x76, 0x62, 0x67, 0x14, 0x0f, 0xbe, 0xf3, 0x94, 0x81, + 0x30, 0x03, 0x11, 0x5f, 0x82, 0x38, 0xb4, 0x46, 0xa7, 0xc7, 0x7c, 0xe1, 0xe8, 0xbb, 0xa9, 0x13, + 0xba, 0x09, 0xe3, 0xae, 0x65, 0x3a, 0xc5, 0x58, 0x4f, 0xc9, 0x7e, 0xc9, 0x54, 0xe2, 0x2a, 0x2c, + 0x8f, 0xad, 0xf3, 0xed, 0x60, 0x22, 0x2d, 0xe8, 0x32, 0x25, 0x1e, 0xc1, 0x55, 0x94, 0x51, 0xb2, + 0xd1, 0x47, 0xa4, 0x57, 0x20, 0x73, 0x68, 0x94, 0x60, 0xd2, 0xb0, 0x23, 0xf2, 0xec, 0x7a, 0x0b, + 0x36, 0x94, 0x81, 0xdc, 0x76, 0x26, 0x8e, 0x6f, 0x3b, 0xfe, 0xc8, 0x4d, 0x62, 0x7e, 0xce, 0x4d, + 0xfe, 0xcb, 0x92, 0x78, 0x3b, 0x43, 0x2b, 0xde, 0x84, 0x55, 0xfc, 0x70, 0xe8, 0xc4, 0xd3, 0xd0, + 0xc7, 0xbd, 0x8f, 0x25, 0x0a, 0x14, 0x10, 0x0d, 0x05, 0x63, 0x37, 0x67, 0x99, 0x48, 0xef, 0x49, + 0x95, 0x8d, 0xd5, 0x04, 0x4a, 0xbe, 0xf9, 0x8f, 0xe0, 0x2a, 0x3b, 0x50, 0x46, 0xdf, 0x79, 0x52, + 0xce, 0x32, 0xd3, 0x1b, 0x23, 0x15, 0xe3, 0x32, 0x61, 0x07, 0xdf, 0x79, 0x2c, 0x6a, 0xf1, 0x5e, + 0xdf, 0x87, 0xab, 0x53, 0x3f, 0x74, 0xa2, 0xc0, 0x7b, 0x91, 0x04, 0x57, 0x3e, 0x71, 0x51, 0x38, + 0xd6, 0xb2, 0xd7, 0xf2, 0x0e, 0x12, 0x1a, 0x92, 0xe9, 0x77, 0x5d, 0x3f, 0x36, 0x36, 0xa6, 0xf3, + 0xc0, 0x48, 0xf4, 0x60, 0x43, 0xbd, 0xfd, 0x94, 0xeb, 0x15, 0x16, 0xcd, 0x5f, 0x53, 0x17, 0x35, + 0x88, 0x62, 0xdf, 0xb3, 0xfc, 0xa4, 0x3b, 0x2e, 0x8c, 0xcb, 0x32, 0x63, 0xb6, 0x87, 0xf4, 0xdf, + 0x83, 0xd5, 0xdc, 0xec, 0x9e, 0x73, 0x32, 0x1a, 0xb4, 0xbb, 0xed, 0xd6, 0x90, 0xbd, 0x06, 0xa4, + 0xdb, 0xcb, 0x52, 0xc6, 0x3f, 0xa6, 0x94, 0xf1, 0x26, 0x28, 0x93, 0x77, 0x4d, 0xdb, 0x78, 0xd2, + 0x26, 0x87, 0x8f, 0xa2, 0x56, 0xd4, 0xff, 0xd6, 0x12, 0xac, 0x0f, 0x43, 0xcb, 0x8f, 0x2c, 0xd6, + 0xa2, 0xfd, 0x38, 0x0c, 0x3c, 0xf1, 0x25, 0x54, 0xe2, 0x91, 0x97, 0x5d, 0x6e, 0xb7, 0x15, 0x73, + 0x9f, 0x21, 0x7d, 0x30, 0x1c, 0xb1, 0xc3, 0xf4, 0x4a, 0xcc, 0x3f, 0xc4, 0x07, 0x50, 0x3e, 0x74, + 0x8e, 0x5d, 0x5f, 0xee, 0x6f, 0x57, 0x66, 0x33, 0x6e, 0x21, 0x72, 0xf7, 0x92, 0xc1, 0x54, 0xe2, + 0x23, 0x58, 0x96, 0xe7, 0x4e, 0xc5, 0xec, 0xbd, 0x85, 0xec, 0x87, 0x10, 0xbb, 0x7b, 0xc9, 0x90, + 0x74, 0xe2, 0x33, 0xa8, 0x84, 0x81, 0x7c, 0x50, 0xaa, 0x94, 0x65, 0x26, 0x69, 0x1e, 0x43, 0xe2, + 0x77, 0x2f, 0x19, 0x09, 0xad, 0xfe, 0x00, 0x56, 0x64, 0x65, 0xb1, 0x1b, 0xb6, 0xda, 0x4f, 0x3a, + 0xb2, 0x07, 0x5b, 0xfd, 0xbd, 0xbd, 0xce, 0x90, 0xe3, 0x4d, 0x1b, 0xfd, 0x6e, 0x77, 0xab, 0xd9, + 0x7a, 0xaa, 0x2d, 0x6d, 0x55, 0x60, 0x99, 0x17, 0x81, 0xfe, 0x2f, 0x16, 0x60, 0x6d, 0xa6, 0x01, + 0xe2, 0x31, 0x94, 0xc8, 0x1a, 0xca, 0xdd, 0x73, 0x77, 0x61, 0x2b, 0x33, 0x69, 0xb6, 0xf5, 0x60, + 0x0e, 0xfd, 0x0b, 0x68, 0xe4, 0xe1, 0x19, 0x9f, 0x94, 0x55, 0xa8, 0x1a, 0xed, 0xe6, 0xb6, 0xd9, + 0xef, 0x75, 0xbf, 0x61, 0x3f, 0x31, 0x4a, 0x3e, 0x37, 0x3a, 0xe4, 0x0c, 0xf2, 0xbb, 0xa0, 0xcd, + 0x76, 0x8c, 0x78, 0x02, 0x6b, 0x28, 0xcb, 0x79, 0x0e, 0x3b, 0xc0, 0xa5, 0x43, 0x76, 0x6b, 0x41, + 0x4f, 0x4a, 0x32, 0xbe, 0x4d, 0x3d, 0xca, 0xa5, 0xf5, 0xdf, 0x03, 0x31, 0xdf, 0x83, 0x7f, 0x79, + 0xc5, 0xff, 0x5f, 0x05, 0x28, 0xe1, 0x84, 0x17, 0x6f, 0x42, 0x99, 0x8f, 0x40, 0x0b, 0xd9, 0xfb, + 0x7a, 0xc4, 0xb3, 0x70, 0x5a, 0x10, 0x4e, 0xbc, 0x07, 0xc5, 0x78, 0xa4, 0x22, 0x4b, 0x5f, 0x7b, + 0xc9, 0xe4, 0xdb, 0xbd, 0x64, 0x20, 0x95, 0xb8, 0x07, 0x45, 0xdb, 0x56, 0x71, 0xc9, 0x32, 0x07, + 0xe4, 0xdb, 0xce, 0x11, 0xdd, 0xd8, 0xa4, 0xb7, 0x94, 0x90, 0x44, 0xbc, 0x05, 0x45, 0x7b, 0xe4, + 0xe5, 0x83, 0xcc, 0xf1, 0xd9, 0x75, 0x52, 0xa0, 0x3d, 0xf2, 0x50, 0xaf, 0x8b, 0xc3, 0x0b, 0x33, + 0x9c, 0xfa, 0x14, 0x96, 0x43, 0x3d, 0x08, 0x56, 0x43, 0x59, 0x71, 0x4a, 0xb1, 0x3d, 0x22, 0x19, + 0xa2, 0x56, 0xbe, 0xf8, 0xa3, 0x6c, 0x81, 0x6e, 0x24, 0xdf, 0x06, 0xda, 0x5a, 0x06, 0x7a, 0x4c, + 0x5a, 0x7f, 0x9f, 0x1e, 0x97, 0x41, 0x35, 0x5e, 0x57, 0xbf, 0x16, 0xbc, 0xbc, 0x29, 0x31, 0xfa, + 0x3f, 0x2d, 0x42, 0x2d, 0x53, 0x1f, 0xf1, 0x09, 0x54, 0xec, 0xfc, 0x42, 0xbc, 0x3e, 0x57, 0xe9, + 0x07, 0xdb, 0x6a, 0x09, 0xda, 0x72, 0x7a, 0x93, 0x37, 0x7e, 0x6c, 0xbe, 0xb0, 0x42, 0x97, 0xaf, + 0x18, 0x2f, 0x65, 0xdd, 0xe2, 0x07, 0x4e, 0xfc, 0x4c, 0x61, 0x76, 0x2f, 0x19, 0xf5, 0x28, 0x93, + 0x26, 0xcb, 0x97, 0x6c, 0x52, 0x31, 0x1b, 0xbb, 0x58, 0x36, 0x6b, 0xf7, 0x92, 0xa1, 0xf0, 0x48, + 0xea, 0x9c, 0x3b, 0xa3, 0x69, 0xac, 0x2c, 0x5f, 0xab, 0xaa, 0x41, 0x04, 0xa4, 0xe7, 0x71, 0xf9, + 0xa7, 0x78, 0x88, 0x7b, 0x9a, 0xe5, 0x79, 0x01, 0x89, 0xc4, 0xe5, 0xec, 0xb1, 0xd3, 0x76, 0x02, + 0xe7, 0xe7, 0x78, 0x55, 0x4a, 0xbc, 0x0d, 0xe5, 0x20, 0x3e, 0x71, 0x94, 0xa6, 0xae, 0x9e, 0xa9, + 0x41, 0xd0, 0x76, 0xab, 0x8b, 0x33, 0x85, 0xd0, 0xfa, 0x1f, 0x15, 0x60, 0x45, 0xf6, 0x80, 0x58, + 0x87, 0xd5, 0x41, 0x7b, 0x68, 0x3e, 0x6b, 0x1a, 0x9d, 0xe6, 0x56, 0xb7, 0x2d, 0x63, 0xe3, 0x3d, + 0x31, 0x9a, 0x3d, 0xc9, 0x20, 0x8d, 0xf6, 0xb3, 0xfe, 0xd3, 0x36, 0x3b, 0x7a, 0x6d, 0xb7, 0x7b, + 0xdf, 0x68, 0x45, 0x76, 0x8a, 0x6c, 0xef, 0x37, 0x0d, 0xe4, 0x95, 0x35, 0x58, 0x69, 0x7f, 0xdd, + 0x6e, 0x1d, 0x10, 0xb3, 0x6c, 0x00, 0x6c, 0xb7, 0x9b, 0xdd, 0x6e, 0xbf, 0x85, 0xcc, 0x73, 0x59, + 0x08, 0x68, 0xb4, 0x8c, 0x76, 0x73, 0xd8, 0x36, 0x9b, 0xad, 0x56, 0xff, 0xa0, 0x37, 0xd4, 0x56, + 0xf0, 0x8b, 0xcd, 0xee, 0xb0, 0x6d, 0x24, 0x20, 0x7a, 0x7c, 0x6f, 0xdb, 0xe8, 0xef, 0x27, 0x90, + 0xea, 0x56, 0x15, 0x56, 0x46, 0x3c, 0x56, 0xfa, 0x7f, 0xb2, 0xce, 0xfe, 0x0e, 0xe9, 0xd4, 0x14, + 0x9f, 0x43, 0xc5, 0xb6, 0x73, 0x63, 0x7c, 0x73, 0xd1, 0x14, 0x7e, 0xb0, 0x6d, 0xab, 0x61, 0xe6, + 0x1f, 0xe2, 0x0d, 0xb5, 0x90, 0x96, 0xe6, 0x16, 0x92, 0x5a, 0x46, 0x3f, 0x87, 0x35, 0xf9, 0xf6, + 0x49, 0x72, 0x54, 0x9a, 0x5b, 0x25, 0x2d, 0x42, 0x6e, 0x4b, 0xdc, 0xee, 0x25, 0xa3, 0x31, 0xca, + 0x41, 0xc4, 0xcf, 0xa0, 0x61, 0xf1, 0x01, 0x64, 0xf6, 0xa8, 0x35, 0x11, 0xf6, 0x9b, 0x74, 0xfe, + 0x98, 0x66, 0x5f, 0xb5, 0xb2, 0x00, 0x9c, 0x88, 0x76, 0x18, 0x4c, 0xd2, 0xcc, 0xe5, 0xdc, 0xfd, + 0x8c, 0x30, 0x98, 0x64, 0xf2, 0xd6, 0xed, 0x4c, 0x5a, 0x7c, 0x06, 0x75, 0x59, 0xf3, 0xd4, 0xd8, + 0x9e, 0x2c, 0x59, 0xae, 0x36, 0x09, 0xef, 0xbb, 0x97, 0x8c, 0xda, 0x28, 0x4d, 0x8a, 0x47, 0x28, + 0xb1, 0xa7, 0xaa, 0xce, 0x4a, 0x76, 0xae, 0x51, 0x6d, 0x55, 0x2e, 0xb0, 0x92, 0x94, 0xf8, 0x08, + 0x80, 0xea, 0xc9, 0x79, 0x2a, 0xb9, 0x9b, 0x8b, 0x61, 0x30, 0x51, 0x59, 0xaa, 0xb6, 0x4a, 0x64, + 0xaa, 0xc7, 0x16, 0xc0, 0xea, 0x7c, 0xf5, 0x58, 0x28, 0x48, 0xaa, 0xc7, 0xc6, 0xc0, 0xa4, 0x7a, + 0x9c, 0x0d, 0xe6, 0xaa, 0xa7, 0x72, 0x71, 0xf5, 0x38, 0x93, 0xaa, 0x1e, 0xe7, 0xa9, 0xcd, 0x56, + 0x4f, 0x65, 0xa1, 0xea, 0x71, 0x8e, 0x9f, 0xcd, 0xe9, 0x68, 0xf5, 0x97, 0xea, 0x68, 0x38, 0x6c, + 0x79, 0x2d, 0xed, 0x67, 0xd0, 0x88, 0x4e, 0x82, 0xb3, 0x0c, 0x03, 0x59, 0xcd, 0xe6, 0x1e, 0x9c, + 0x04, 0x67, 0x59, 0x0e, 0xb2, 0x1a, 0x65, 0x01, 0x58, 0x5b, 0x6e, 0x22, 0xdd, 0xa5, 0x68, 0x64, + 0x6b, 0x4b, 0x2d, 0x7c, 0xe6, 0x3a, 0x67, 0x58, 0x5b, 0x4b, 0x25, 0xb0, 0x53, 0xd2, 0x83, 0x87, + 0x48, 0x1e, 0x25, 0xe4, 0xee, 0xfb, 0xc9, 0x2f, 0x41, 0x72, 0x04, 0x11, 0xe1, 0xdc, 0x9a, 0xfa, + 0xd9, 0x6c, 0x5a, 0x76, 0x6e, 0x1d, 0xf8, 0xb9, 0x8c, 0x75, 0x26, 0x95, 0x59, 0xd3, 0x55, 0x11, + 0x39, 0xdf, 0x4d, 0x1d, 0x7f, 0xe4, 0xc8, 0x7b, 0xbd, 0xb9, 0x55, 0x31, 0x90, 0xb8, 0x74, 0x55, + 0x28, 0x48, 0x32, 0xaf, 0x93, 0xec, 0x62, 0x76, 0x5e, 0x67, 0x32, 0xd3, 0xbc, 0x4e, 0xb2, 0x26, + 0x0b, 0x2a, 0xc9, 0x7b, 0x79, 0x6e, 0x41, 0x65, 0x32, 0xf3, 0x82, 0x4a, 0x72, 0x3f, 0x02, 0x39, + 0x9b, 0xb8, 0x73, 0x73, 0xb7, 0x7f, 0xb9, 0xd6, 0xb2, 0x77, 0x61, 0x94, 0xa4, 0x70, 0xae, 0x86, + 0x0e, 0xea, 0x84, 0x72, 0x2a, 0x5c, 0xc9, 0xce, 0x55, 0x83, 0x30, 0xc9, 0x52, 0x0a, 0xd3, 0x64, + 0xe6, 0x63, 0x13, 0x37, 0x0e, 0x37, 0xed, 0xf9, 0x8f, 0xed, 0xbb, 0x71, 0x98, 0x7e, 0x0c, 0x53, + 0xe2, 0x03, 0xa0, 0x69, 0xc8, 0x59, 0x9c, 0x2c, 0xeb, 0xc6, 0x6e, 0x91, 0x19, 0x2a, 0xb6, 0xfc, + 0x8d, 0x93, 0x45, 0x7e, 0x63, 0x64, 0x8f, 0x36, 0x8f, 0xb2, 0x93, 0x85, 0x3f, 0xd1, 0xda, 0x6e, + 0xe1, 0x64, 0x61, 0xa2, 0x96, 0x3d, 0x12, 0xf7, 0x81, 0x72, 0x13, 0xfd, 0x71, 0x76, 0xdf, 0xc1, + 0xf2, 0x99, 0x7a, 0x05, 0x09, 0x90, 0x16, 0x5b, 0xe0, 0x05, 0xbe, 0x6a, 0xf8, 0x49, 0xae, 0x05, + 0x88, 0x48, 0x98, 0xc1, 0x28, 0x49, 0xe9, 0xff, 0xf2, 0x32, 0xac, 0x48, 0x5e, 0x2b, 0x2e, 0xc3, + 0x9a, 0x64, 0xf9, 0xdb, 0xcd, 0x61, 0x73, 0xab, 0x39, 0x40, 0x21, 0x4d, 0x40, 0x83, 0x79, 0x7e, + 0x02, 0x2b, 0xe0, 0x3e, 0x40, 0x4c, 0x3f, 0x01, 0x2d, 0xe1, 0x3e, 0x20, 0xf3, 0xb2, 0x3b, 0x6d, + 0x51, 0xac, 0x41, 0x8d, 0x33, 0x32, 0x80, 0x42, 0xf5, 0x52, 0x2e, 0x4e, 0x97, 0x33, 0x59, 0xd8, + 0x21, 0x7a, 0x39, 0xcd, 0xc2, 0x80, 0x95, 0x24, 0x8b, 0xf2, 0x98, 0x16, 0xd0, 0x18, 0x1a, 0x07, + 0xbd, 0x56, 0xfa, 0x9d, 0x2a, 0x85, 0x57, 0xe5, 0x62, 0x9e, 0x75, 0xda, 0xcf, 0x35, 0xc0, 0x4c, + 0x5c, 0x0a, 0xa5, 0xc9, 0x93, 0x9a, 0x0a, 0xa1, 0x64, 0x5d, 0x5c, 0x83, 0xcb, 0x83, 0xdd, 0xfe, + 0x73, 0x93, 0x33, 0x25, 0x4d, 0x58, 0x15, 0x1b, 0xa0, 0x65, 0x10, 0x5c, 0x7c, 0x03, 0x3f, 0x49, + 0x50, 0x45, 0x38, 0xd0, 0xd6, 0xe8, 0x71, 0x16, 0x84, 0x0d, 0x79, 0xdf, 0xd5, 0xb0, 0x29, 0x9c, + 0xb5, 0xdf, 0x3d, 0xd8, 0xeb, 0x0d, 0xb4, 0x75, 0xac, 0x04, 0x41, 0xb8, 0xe6, 0x22, 0x29, 0x26, + 0xdd, 0xad, 0x2f, 0xd3, 0x06, 0x8e, 0xb0, 0xe7, 0x4d, 0xa3, 0xd7, 0xe9, 0x3d, 0x19, 0x68, 0x1b, + 0x49, 0xc9, 0x6d, 0xc3, 0xe8, 0x1b, 0x03, 0xed, 0x4a, 0x02, 0x18, 0x0c, 0x9b, 0xc3, 0x83, 0x81, + 0x76, 0x35, 0xa9, 0xe5, 0xbe, 0xd1, 0x6f, 0xb5, 0x07, 0x83, 0x6e, 0x67, 0x30, 0xd4, 0xae, 0x89, + 0x2b, 0xb0, 0x9e, 0xd6, 0x48, 0x11, 0x6f, 0x66, 0x2a, 0x6a, 0x3c, 0x69, 0x0f, 0xb5, 0xeb, 0x49, + 0x35, 0x5a, 0xfd, 0x6e, 0xb7, 0x49, 0xbe, 0xda, 0x37, 0x90, 0x88, 0xae, 0x14, 0xc8, 0xd6, 0xbc, + 0x86, 0xf5, 0x3a, 0xe8, 0x65, 0x41, 0x37, 0x33, 0x53, 0x63, 0xd0, 0xfe, 0xe5, 0x41, 0xbb, 0xd7, + 0x6a, 0x6b, 0xaf, 0xa7, 0x53, 0x23, 0x81, 0xdd, 0x4a, 0xa6, 0x46, 0x02, 0xba, 0x9d, 0x7c, 0x53, + 0x81, 0x06, 0xda, 0x9d, 0xa4, 0xc3, 0x0e, 0xf6, 0x9f, 0x18, 0xcd, 0xed, 0xb6, 0xa6, 0x23, 0xc4, + 0x68, 0xf7, 0x9a, 0x7b, 0x6a, 0x60, 0xdf, 0xcc, 0x0c, 0xec, 0x7e, 0x67, 0x68, 0x68, 0x77, 0x93, + 0x81, 0xa4, 0xe4, 0x5b, 0xe2, 0x35, 0xb8, 0x96, 0x9d, 0x72, 0xe6, 0xf3, 0xce, 0x70, 0x57, 0x7a, + 0xfe, 0xbf, 0xcd, 0xce, 0xe3, 0x84, 0x6c, 0x6d, 0xb7, 0xf8, 0x8a, 0x03, 0xe5, 0xc5, 0xd4, 0x3d, + 0xbd, 0x54, 0x79, 0x43, 0x7b, 0xe3, 0xfe, 0x9a, 0xec, 0x8a, 0x5e, 0x8f, 0xae, 0x8f, 0x0c, 0xb6, + 0xea, 0x28, 0xb9, 0x29, 0x11, 0x44, 0xff, 0x0a, 0x44, 0xf6, 0x39, 0x6d, 0x19, 0x20, 0x41, 0x40, + 0xe9, 0x28, 0x0c, 0xc6, 0xca, 0xed, 0x04, 0x7f, 0x8b, 0x3b, 0x50, 0x9b, 0x4c, 0x0f, 0xc9, 0xd7, + 0x3d, 0x0d, 0x8c, 0x9d, 0x05, 0xe9, 0xff, 0x4e, 0x01, 0x1a, 0x79, 0xf1, 0x83, 0x8e, 0x53, 0x8e, + 0x4c, 0x3f, 0x88, 0xf9, 0x7d, 0xac, 0x28, 0x79, 0xe5, 0xfa, 0xa8, 0x17, 0xc4, 0xf4, 0x40, 0x56, + 0x94, 0xf3, 0xfa, 0x5a, 0x9a, 0xf1, 0xfa, 0xea, 0xc0, 0xe5, 0xdc, 0xeb, 0xe5, 0xb9, 0xd7, 0xc9, + 0xa4, 0x92, 0x38, 0x5f, 0x7f, 0x43, 0x44, 0xf3, 0x6d, 0x92, 0xe1, 0xcd, 0x4b, 0x69, 0x78, 0xf3, + 0x5d, 0x58, 0xcd, 0x49, 0x3b, 0x64, 0x2d, 0x3b, 0xca, 0xd7, 0xb4, 0xe2, 0x1e, 0xbd, 0xba, 0x9a, + 0xfa, 0xdf, 0x29, 0x40, 0x3d, 0x2b, 0xfb, 0xfc, 0xc6, 0x25, 0xd1, 0xbd, 0x45, 0xf9, 0xdb, 0x74, + 0x6d, 0xf5, 0xac, 0x95, 0x02, 0x91, 0x4b, 0x65, 0x8d, 0x4f, 0xa6, 0x76, 0x4e, 0x07, 0x49, 0x73, + 0xb2, 0x20, 0xf2, 0x94, 0x3b, 0x71, 0x46, 0xa7, 0x3b, 0x4f, 0x91, 0x40, 0x79, 0xca, 0x25, 0x10, + 0xfd, 0x36, 0x54, 0x77, 0x4e, 0xd5, 0x35, 0xce, 0xec, 0x2b, 0x71, 0x55, 0x8e, 0xa6, 0xae, 0xff, + 0x71, 0x01, 0x1a, 0xa9, 0x6d, 0x88, 0x1c, 0xf5, 0xf9, 0xd5, 0x7b, 0x9e, 0x0e, 0x4b, 0xf6, 0x61, + 0xea, 0x0d, 0xb9, 0x94, 0xf5, 0x86, 0x7c, 0x53, 0x16, 0x56, 0xcc, 0x32, 0xfd, 0xe4, 0x5b, 0x32, + 0x56, 0xfb, 0x23, 0xa8, 0xe3, 0x7f, 0xc3, 0x39, 0x72, 0xc2, 0xd0, 0xb1, 0xf3, 0xf1, 0xaf, 0x52, + 0xe2, 0x1c, 0x11, 0x69, 0x79, 0xc9, 0x93, 0x0d, 0x0b, 0x1f, 0x95, 0xa1, 0xc7, 0x8e, 0xfe, 0x8f, + 0x22, 0xd4, 0x32, 0x92, 0xe4, 0x8f, 0x9a, 0x7e, 0x37, 0xa1, 0x9a, 0x3e, 0x71, 0x22, 0x03, 0x64, + 0x27, 0x80, 0xdc, 0x58, 0x15, 0x67, 0xc6, 0x8a, 0x9c, 0x92, 0x29, 0x2a, 0x67, 0xea, 0x94, 0x4c, + 0xc9, 0xbc, 0x51, 0xbc, 0xfc, 0x8a, 0xd3, 0xd3, 0x8f, 0xa1, 0x9e, 0xb1, 0x68, 0x2b, 0xf7, 0xc5, + 0x59, 0xfa, 0x5a, 0x6a, 0xdd, 0x8e, 0xc4, 0x15, 0x58, 0x3e, 0x3a, 0x35, 0xed, 0x43, 0xf6, 0x08, + 0xa8, 0x1a, 0xe5, 0xa3, 0xd3, 0xed, 0x43, 0xf2, 0x80, 0x38, 0x4a, 0x84, 0x27, 0xb6, 0x33, 0x56, + 0x8e, 0x94, 0x88, 0x74, 0x0f, 0x56, 0x8e, 0x4e, 0xb3, 0x61, 0x76, 0xe7, 0xba, 0x7c, 0xf9, 0xe8, + 0x94, 0x22, 0xbd, 0x7e, 0x08, 0x1b, 0x72, 0x07, 0xb7, 0x22, 0x65, 0x76, 0xc3, 0xa5, 0xc3, 0x6f, + 0xc6, 0xad, 0x33, 0xae, 0x19, 0xb1, 0xd1, 0x0d, 0x67, 0x9c, 0x0e, 0xf5, 0xcc, 0x04, 0x64, 0x7b, + 0x61, 0xd5, 0xc8, 0xc1, 0xc4, 0x63, 0xa8, 0x1f, 0x9d, 0xf2, 0x80, 0x0e, 0x83, 0x3d, 0x47, 0x46, + 0x92, 0xd9, 0x98, 0x1d, 0x4a, 0xba, 0x49, 0x91, 0xa3, 0x14, 0x57, 0x61, 0xd9, 0xb0, 0xce, 0x06, + 0xbf, 0xec, 0x92, 0x18, 0x59, 0x35, 0x64, 0x8a, 0x5f, 0x94, 0xd3, 0xff, 0x41, 0x01, 0x1a, 0xa9, + 0x16, 0x80, 0x8b, 0x50, 0xdc, 0x4f, 0x1f, 0x93, 0x6e, 0x28, 0x26, 0x91, 0x27, 0x79, 0x30, 0xbc, + 0x98, 0xf0, 0xe3, 0xac, 0x8b, 0xde, 0x75, 0x5a, 0x74, 0xc4, 0x50, 0x5c, 0xf8, 0xf8, 0xfd, 0x13, + 0x28, 0x0e, 0x2f, 0x26, 0x6c, 0x71, 0xc2, 0x4d, 0x91, 0xb5, 0x53, 0xde, 0x0e, 0xe9, 0xb6, 0xd2, + 0xd3, 0xf6, 0x37, 0x1c, 0xd0, 0x7f, 0xdf, 0xe8, 0xec, 0x35, 0x8d, 0x6f, 0x4c, 0x04, 0x90, 0xd8, + 0xb0, 0xd3, 0x37, 0xda, 0x9d, 0x27, 0x3d, 0x02, 0x94, 0xc8, 0x1e, 0x95, 0x56, 0xb1, 0x69, 0xdb, + 0x3b, 0xa7, 0xd9, 0x27, 0x71, 0x0a, 0xb9, 0x27, 0x71, 0x92, 0xd7, 0xa3, 0xb2, 0x4f, 0x1d, 0xc6, + 0xc9, 0xb9, 0x87, 0x48, 0x56, 0x61, 0xb2, 0xa4, 0xc5, 0x3b, 0x50, 0x3a, 0x3a, 0x75, 0x2e, 0xf2, + 0xaa, 0x5e, 0x7e, 0x01, 0x11, 0x81, 0xfe, 0x67, 0x05, 0x10, 0xb9, 0x8a, 0xb0, 0xf6, 0xf1, 0x9b, + 0xd6, 0xe5, 0x73, 0xd8, 0x54, 0xef, 0xa2, 0x12, 0x55, 0xe6, 0x4c, 0x43, 0x76, 0xe9, 0x95, 0x20, + 0x8d, 0x60, 0x90, 0x3e, 0x3d, 0x25, 0x3e, 0x04, 0x7e, 0xc5, 0x90, 0x2e, 0xde, 0x94, 0x5e, 0xa2, + 0x29, 0x1a, 0x29, 0x4d, 0xfa, 0x6c, 0x61, 0xf6, 0x39, 0x46, 0x3e, 0x0e, 0x59, 0x4b, 0x47, 0x8d, + 0xd6, 0xbc, 0xfe, 0x37, 0x0a, 0x70, 0x39, 0x3f, 0x21, 0xfe, 0x62, 0xad, 0xcc, 0xbf, 0x3d, 0x59, + 0x9c, 0x7d, 0x7b, 0x72, 0xd1, 0x7c, 0x2a, 0x2d, 0x9c, 0x4f, 0xbf, 0x5f, 0x80, 0x8d, 0x4c, 0xef, + 0xa7, 0xfa, 0xe2, 0x5f, 0x51, 0xcd, 0x32, 0x4f, 0x50, 0x96, 0x72, 0x4f, 0x50, 0xea, 0x7f, 0x5a, + 0x80, 0xab, 0x33, 0x35, 0x31, 0x9c, 0xbf, 0xd2, 0xba, 0xe4, 0x9f, 0xaa, 0xa4, 0x23, 0x19, 0x15, + 0xf4, 0xbe, 0x70, 0xaf, 0x98, 0x79, 0xaa, 0x92, 0xce, 0xe9, 0xe8, 0xb0, 0xf8, 0x75, 0xf9, 0x36, + 0x8a, 0x19, 0x5d, 0xf8, 0x23, 0x39, 0xd8, 0x55, 0x82, 0x0c, 0x2e, 0xfc, 0x11, 0x6e, 0x52, 0x63, + 0x27, 0x3c, 0x56, 0xa6, 0x3c, 0x4e, 0xe8, 0xff, 0x51, 0x01, 0xae, 0xcf, 0xb4, 0xac, 0x39, 0x8d, + 0x03, 0xe9, 0xdb, 0xf1, 0x57, 0xd4, 0xb8, 0xdb, 0x40, 0xaf, 0x36, 0x29, 0xa7, 0x92, 0xcc, 0x85, + 0x0c, 0xf9, 0x5d, 0x0d, 0x8a, 0xb6, 0x75, 0x21, 0x63, 0x26, 0xe3, 0x4f, 0x5c, 0xc6, 0x27, 0xc1, + 0x34, 0x94, 0x37, 0x08, 0xe8, 0xb7, 0xfe, 0x09, 0xac, 0xa7, 0x55, 0x6f, 0xc9, 0x77, 0x44, 0x6f, + 0x43, 0xcd, 0x77, 0xce, 0x4c, 0xf5, 0xca, 0xa8, 0xbc, 0x5b, 0xe6, 0x3b, 0x67, 0x92, 0x40, 0xdf, + 0xc9, 0x72, 0x48, 0xaa, 0xce, 0x75, 0xa8, 0x04, 0x9e, 0x9d, 0xf3, 0x28, 0x0d, 0x3c, 0x5b, 0xa1, + 0xb0, 0xb4, 0x4c, 0x2b, 0x57, 0x7c, 0xe7, 0x8c, 0x66, 0xe7, 0x99, 0x2c, 0xa7, 0x69, 0x2b, 0x0f, + 0xaa, 0x45, 0xee, 0xc9, 0xd7, 0xa1, 0x32, 0x09, 0x73, 0xdd, 0xb4, 0x32, 0x09, 0xf9, 0xb3, 0x77, + 0xe5, 0xcd, 0xb5, 0x97, 0x79, 0xe0, 0xf1, 0x5d, 0x36, 0x0d, 0x8a, 0x93, 0x20, 0x92, 0x4e, 0x42, + 0xf8, 0x53, 0xff, 0x54, 0x32, 0x47, 0xd2, 0x07, 0xf9, 0xcb, 0x1a, 0x14, 0x5d, 0xfb, 0x9c, 0x3e, + 0xbc, 0x6a, 0xe0, 0x4f, 0x92, 0xef, 0x9c, 0xef, 0xe4, 0xe5, 0x39, 0xfc, 0xa9, 0x6f, 0x41, 0xcd, + 0xc8, 0x29, 0xbf, 0xf5, 0x8c, 0x1d, 0x29, 0xca, 0xbf, 0x41, 0x93, 0x76, 0x90, 0x51, 0x4b, 0xcd, + 0x48, 0x91, 0x1e, 0x49, 0x76, 0xf8, 0xcc, 0x0a, 0x47, 0x27, 0x56, 0xd8, 0x75, 0xfc, 0xe3, 0xf8, + 0x04, 0xbb, 0x9c, 0xcd, 0xbb, 0xd9, 0x2e, 0x04, 0x06, 0xa9, 0xe9, 0x80, 0xbd, 0xe8, 0x11, 0xb9, + 0x74, 0x30, 0xa8, 0xfa, 0xce, 0x99, 0xcc, 0xff, 0x3a, 0x00, 0xf6, 0xbf, 0x44, 0xcb, 0xdb, 0x3b, + 0x81, 0x67, 0x33, 0x5a, 0x5f, 0x97, 0xed, 0x95, 0x31, 0xbf, 0xb7, 0x9d, 0x23, 0xdd, 0x93, 0x23, + 0xcf, 0x0d, 0x92, 0x9d, 0xf0, 0x1b, 0x0d, 0xa3, 0x78, 0x03, 0xea, 0xca, 0x52, 0x41, 0xcf, 0x1e, + 0xf1, 0xe7, 0x6b, 0x0a, 0xd6, 0x9b, 0x8e, 0xf5, 0xff, 0xb8, 0x04, 0xf5, 0x26, 0xfb, 0x9c, 0x4e, + 0x2e, 0xfa, 0x93, 0x58, 0xfc, 0x1e, 0x5c, 0xa1, 0x78, 0xfb, 0x1c, 0xfa, 0x80, 0x5c, 0x3d, 0xd9, + 0xc9, 0x91, 0x3b, 0xf1, 0x7e, 0xa6, 0x13, 0x65, 0x96, 0x07, 0x83, 0x53, 0x77, 0xc2, 0xd1, 0x10, + 0x3a, 0xf6, 0x39, 0xf9, 0x3c, 0xb2, 0x67, 0x06, 0x85, 0xe6, 0xcf, 0x23, 0x28, 0xf4, 0x33, 0x16, + 0x3f, 0x39, 0x95, 0xc5, 0x4a, 0x27, 0x1f, 0x04, 0xee, 0x9f, 0x32, 0x4d, 0xea, 0x12, 0x35, 0xb7, + 0x2d, 0x4b, 0x97, 0xa8, 0x74, 0x7e, 0x0f, 0x60, 0x5d, 0x3e, 0x0f, 0x40, 0xaf, 0x12, 0x9a, 0xa3, + 0x60, 0x72, 0x21, 0x0f, 0x7e, 0xdf, 0x79, 0x49, 0x55, 0x3b, 0x4c, 0x8a, 0x20, 0xae, 0xe7, 0x5a, + 0x94, 0x87, 0x8a, 0x67, 0x20, 0xb0, 0x4b, 0x27, 0xde, 0x14, 0xf7, 0x33, 0xf5, 0x1e, 0x22, 0xbb, + 0xa2, 0xdf, 0x5b, 0x50, 0x6a, 0xcf, 0x39, 0xdb, 0x27, 0x5a, 0x59, 0x08, 0x17, 0xab, 0xf9, 0x33, + 0xe0, 0x1b, 0x6d, 0xb8, 0xf6, 0x92, 0xbe, 0x7a, 0x95, 0xd3, 0x4a, 0x25, 0xe3, 0xb4, 0x72, 0x63, + 0x0b, 0x36, 0x16, 0xb5, 0xe3, 0x27, 0x95, 0xd1, 0x82, 0x2b, 0x0b, 0x6b, 0xfd, 0x53, 0x0a, 0xd1, + 0x7f, 0xbf, 0x01, 0x90, 0x2e, 0xa7, 0x9c, 0x04, 0x5d, 0x98, 0x91, 0xa0, 0x7f, 0x92, 0x97, 0xe1, + 0x27, 0xd0, 0xc0, 0x71, 0xcc, 0x04, 0x71, 0x5d, 0x1c, 0x3d, 0xb6, 0x8e, 0x54, 0xc3, 0x34, 0x7c, + 0xdf, 0xbc, 0xb7, 0x4c, 0x69, 0xa1, 0xb7, 0xcc, 0xc7, 0xb0, 0xc2, 0xa7, 0x83, 0x6a, 0x50, 0xaf, + 0xcd, 0xb2, 0x86, 0x07, 0x32, 0x68, 0x8e, 0xa2, 0x13, 0x6d, 0x68, 0x24, 0xf1, 0x2e, 0xb2, 0xf1, + 0x23, 0x6f, 0xcd, 0xe7, 0x54, 0x64, 0xfc, 0xd0, 0xa2, 0x95, 0x4d, 0x66, 0x04, 0xee, 0x78, 0x2c, + 0x4d, 0xd6, 0x24, 0x70, 0xaf, 0x64, 0x05, 0xee, 0xe1, 0x98, 0x0d, 0xd5, 0x28, 0x70, 0x7f, 0x00, + 0x97, 0xa5, 0xdf, 0x31, 0x66, 0xc0, 0xee, 0x24, 0x7a, 0xbe, 0x25, 0xa6, 0xc9, 0x78, 0xf4, 0x63, + 0x52, 0x47, 0x91, 0xfc, 0x6b, 0xd8, 0xe0, 0x27, 0x13, 0xe8, 0x75, 0xfa, 0x51, 0xe0, 0x75, 0x6c, + 0x73, 0x6c, 0x4d, 0xa4, 0x1e, 0xf0, 0xce, 0x5c, 0x65, 0xf9, 0xc5, 0x84, 0xe1, 0xa1, 0x47, 0x7e, + 0xdb, 0x89, 0x4f, 0xd5, 0xfa, 0x68, 0x16, 0x3e, 0xe3, 0xda, 0x00, 0x73, 0xae, 0x0d, 0xb3, 0x9a, + 0x41, 0x6d, 0x81, 0x66, 0x90, 0xca, 0xf7, 0xf5, 0xac, 0x7c, 0x2f, 0xde, 0x87, 0x15, 0x19, 0x87, + 0x50, 0x1a, 0xab, 0xc5, 0xfc, 0x22, 0x33, 0x14, 0x09, 0x7e, 0x49, 0x39, 0xda, 0x50, 0xcc, 0xeb, + 0x06, 0x7f, 0x29, 0x0b, 0x13, 0x5b, 0xd2, 0x52, 0x9b, 0xf8, 0x98, 0x4b, 0xc3, 0xf4, 0x8d, 0x4c, + 0xc1, 0x09, 0x4e, 0x9a, 0x12, 0x66, 0x72, 0xdc, 0xf8, 0xc3, 0x15, 0x58, 0x96, 0xee, 0x12, 0xf7, + 0xa1, 0x64, 0x87, 0xf2, 0x3e, 0x4c, 0xa2, 0xca, 0xe4, 0xe5, 0xca, 0xdd, 0x4b, 0x06, 0xd1, 0x88, + 0x07, 0xb0, 0x6c, 0xd9, 0xb6, 0x79, 0x74, 0x9a, 0x3f, 0x44, 0x9f, 0x91, 0xf9, 0x77, 0x2f, 0x19, + 0x65, 0x8b, 0x84, 0xff, 0xcf, 0xa1, 0x8a, 0xf4, 0xd9, 0xe8, 0xc3, 0x73, 0x9a, 0x8c, 0x92, 0xce, + 0x77, 0x2f, 0x19, 0x15, 0x4b, 0x49, 0xea, 0xbf, 0x9d, 0x3f, 0x8e, 0x28, 0xcd, 0x35, 0x70, 0x46, + 0xb4, 0x9c, 0x39, 0x98, 0xf8, 0x1d, 0x60, 0xfb, 0x74, 0x22, 0x4e, 0x94, 0xb3, 0xe7, 0xb5, 0x73, + 0xc2, 0xc7, 0xee, 0x25, 0x83, 0x37, 0x55, 0x25, 0x8c, 0x7c, 0xaa, 0x8e, 0x0a, 0x88, 0x3d, 0x2f, + 0x2f, 0xee, 0x19, 0xe4, 0xd1, 0xc9, 0x79, 0x01, 0x31, 0x6c, 0xcc, 0x66, 0x27, 0xee, 0xa3, 0x2b, + 0x73, 0xd9, 0x12, 0x91, 0x83, 0xb2, 0x25, 0xf2, 0xc7, 0x63, 0xa8, 0xb1, 0xe5, 0x98, 0xf3, 0x55, + 0xe6, 0xba, 0x36, 0x95, 0x18, 0xe8, 0x2c, 0x32, 0x95, 0x1f, 0x5a, 0xaa, 0x9d, 0xa1, 0x93, 0x3d, + 0xee, 0xb9, 0xb9, 0xb0, 0xa3, 0x8c, 0xe4, 0xe4, 0x87, 0x1b, 0x6b, 0x70, 0x1e, 0xd1, 0x85, 0x0d, + 0x79, 0x2e, 0xc2, 0xd2, 0x81, 0xda, 0xd0, 0x61, 0x6e, 0xbc, 0x72, 0xe2, 0xc3, 0xee, 0x25, 0x43, + 0x58, 0xf3, 0x42, 0x45, 0x0b, 0xd6, 0x55, 0x95, 0x68, 0xdb, 0xcf, 0x78, 0xd4, 0x65, 0x9b, 0x94, + 0x0a, 0x05, 0xbb, 0x97, 0x8c, 0x35, 0x2b, 0x0f, 0x12, 0x1d, 0xb8, 0xac, 0x0a, 0xa1, 0xf3, 0x81, + 0x8c, 0x33, 0x7d, 0x7e, 0x14, 0xb3, 0x82, 0xc4, 0xee, 0x25, 0x63, 0xdd, 0x9a, 0x93, 0x2e, 0xf6, + 0x54, 0x7d, 0xb2, 0x92, 0x2b, 0xaf, 0xc4, 0xdb, 0x0b, 0xbb, 0x29, 0x15, 0xa3, 0x93, 0x9a, 0x65, + 0x24, 0xeb, 0x5f, 0xaa, 0xce, 0xca, 0x3b, 0x0f, 0xcb, 0xe3, 0xa4, 0xd7, 0xe7, 0x4a, 0xcc, 0xfa, + 0x0e, 0x27, 0x3d, 0x96, 0x83, 0xa6, 0xfe, 0x1c, 0x37, 0x0c, 0xb8, 0xba, 0x98, 0x69, 0x65, 0x77, + 0xae, 0x12, 0xef, 0x5c, 0x7a, 0x76, 0xe7, 0x9a, 0x0d, 0x8b, 0x9f, 0xd9, 0xc7, 0x7e, 0x01, 0xab, + 0x39, 0xae, 0x2d, 0x6a, 0xb0, 0xa2, 0x5e, 0x14, 0xa7, 0x58, 0x23, 0xad, 0xfe, 0xfe, 0x37, 0x5a, + 0x01, 0xc1, 0x9d, 0xde, 0x60, 0xd8, 0xec, 0x0d, 0xb5, 0x25, 0x4e, 0xec, 0x77, 0x9b, 0xad, 0xb6, + 0x56, 0xd4, 0xff, 0x7e, 0x11, 0xaa, 0xc9, 0x69, 0xe3, 0x6f, 0x6e, 0x13, 0x5c, 0x7c, 0x25, 0x7e, + 0x46, 0xe1, 0xe5, 0xbb, 0x04, 0x25, 0x15, 0x03, 0x3b, 0xab, 0x56, 0x46, 0xf3, 0x41, 0x4e, 0xcb, + 0x3f, 0x32, 0xc8, 0x69, 0xf6, 0x2e, 0xd8, 0x72, 0xfe, 0x2e, 0xd8, 0xcc, 0x03, 0xfc, 0x2b, 0xf4, + 0x76, 0x73, 0xf6, 0x01, 0xfe, 0xab, 0xb0, 0xec, 0x46, 0xcf, 0x5c, 0xe7, 0x4c, 0x7a, 0xc6, 0xcb, + 0x54, 0x7e, 0xd3, 0x87, 0x57, 0x6c, 0xfa, 0x3f, 0x66, 0x03, 0x79, 0x08, 0x1b, 0x47, 0xa7, 0xc9, + 0x8b, 0xd1, 0xa9, 0x89, 0xa9, 0x4e, 0x55, 0x5a, 0x88, 0x13, 0xef, 0xc0, 0x72, 0xac, 0x0e, 0x42, + 0x8b, 0x0b, 0xce, 0x86, 0x0d, 0x89, 0xd6, 0xff, 0xb0, 0x00, 0x90, 0x9e, 0xc3, 0xfd, 0x85, 0x0d, + 0xda, 0x19, 0x9b, 0x61, 0xf1, 0x07, 0x6c, 0x86, 0xaf, 0x7a, 0x10, 0xe8, 0x3b, 0xa8, 0x26, 0x27, + 0xaf, 0xbf, 0xf9, 0xc4, 0xfa, 0x49, 0x9f, 0xfc, 0xe7, 0x94, 0x71, 0x3f, 0x39, 0xba, 0xfc, 0x8b, + 0xf6, 0x45, 0xee, 0xf3, 0x8b, 0x65, 0xb9, 0xf4, 0xf3, 0xe7, 0x6c, 0x61, 0x4f, 0x3e, 0xfe, 0x97, + 0xbc, 0x9a, 0xb2, 0x13, 0xbd, 0x94, 0x9b, 0xe8, 0xfa, 0x54, 0x1e, 0x13, 0xfc, 0xc5, 0x3f, 0xfd, + 0x93, 0x1a, 0xfc, 0xbf, 0x17, 0x94, 0x2d, 0x3b, 0x79, 0xe4, 0xfb, 0xa5, 0x72, 0xf4, 0x62, 0x73, + 0xfc, 0x4f, 0xf9, 0xdc, 0x0f, 0x5a, 0xea, 0x4a, 0x3f, 0x64, 0xa9, 0x7b, 0x47, 0xbd, 0x91, 0x50, + 0x7e, 0x99, 0x95, 0x4e, 0x3e, 0x93, 0x70, 0x1b, 0x6a, 0x59, 0xdb, 0x1c, 0x1b, 0x65, 0xd8, 0xa8, + 0xc2, 0x66, 0x39, 0x5d, 0xea, 0x0d, 0xdc, 0xde, 0x0d, 0x55, 0xae, 0x8c, 0x0d, 0x44, 0x09, 0xfd, + 0xaf, 0x31, 0x47, 0xfd, 0x4d, 0xbb, 0xe4, 0x87, 0xcd, 0x34, 0xfa, 0xff, 0x5b, 0x80, 0xd5, 0x9c, + 0x27, 0xc5, 0x6f, 0xf0, 0x89, 0x85, 0x7c, 0xb9, 0xf8, 0xff, 0x23, 0xbe, 0x9c, 0x73, 0x18, 0xaf, + 0xe4, 0x1d, 0xc6, 0x91, 0xdd, 0xd5, 0x73, 0x5a, 0xd1, 0x22, 0xfd, 0xa9, 0xb0, 0x50, 0x7f, 0xba, + 0x05, 0x60, 0x8d, 0xe8, 0x1e, 0x5a, 0x67, 0x9b, 0xfd, 0xb3, 0x57, 0x8d, 0x0c, 0x44, 0x7c, 0x01, + 0xd7, 0xa5, 0xd1, 0x84, 0xfb, 0x27, 0x38, 0x32, 0x15, 0xd6, 0x96, 0x46, 0x88, 0xab, 0x4c, 0x40, + 0xa3, 0xdb, 0x3f, 0x6a, 0x2a, 0xac, 0xde, 0x81, 0xd5, 0x9c, 0x8b, 0x0a, 0xee, 0x2d, 0xc7, 0x5e, + 0x70, 0x68, 0x79, 0xb2, 0x2e, 0x32, 0x25, 0xee, 0x40, 0xf9, 0xec, 0xc4, 0x09, 0x9d, 0x05, 0x4f, + 0xd9, 0x32, 0x42, 0xff, 0x19, 0xd4, 0xb3, 0xee, 0x72, 0xe2, 0x7d, 0x28, 0xbb, 0xb1, 0x33, 0x56, + 0xe6, 0xa0, 0xab, 0xf3, 0x1e, 0x75, 0x9d, 0xd8, 0x19, 0x1b, 0x4c, 0xa4, 0xff, 0x51, 0x01, 0xb4, + 0x59, 0x1c, 0x85, 0xb7, 0xb9, 0x88, 0x62, 0x67, 0xac, 0x2a, 0xc3, 0xa9, 0x4c, 0x25, 0x97, 0x72, + 0x95, 0x54, 0x56, 0xb3, 0x62, 0xc6, 0x6a, 0x76, 0x47, 0x09, 0x25, 0xf3, 0x57, 0x94, 0x18, 0x21, + 0xde, 0x86, 0x4a, 0xe8, 0x50, 0xe0, 0x1c, 0x7b, 0xc1, 0xed, 0xb8, 0x04, 0xa7, 0xff, 0x41, 0x01, + 0x56, 0xa4, 0x6f, 0xdf, 0x42, 0xfb, 0xdc, 0xbb, 0xb0, 0x12, 0x8d, 0x4e, 0x9c, 0xb1, 0xa5, 0xde, + 0x05, 0x9a, 0xf3, 0xeb, 0x56, 0x78, 0x71, 0x8b, 0x3d, 0x1e, 0xf3, 0xf6, 0xba, 0x7d, 0xcf, 0xf2, + 0x0d, 0x82, 0xe3, 0x54, 0x63, 0x3b, 0x2d, 0x5f, 0x35, 0xe2, 0x77, 0x18, 0x80, 0x40, 0x74, 0x99, + 0x48, 0xff, 0x6d, 0x58, 0x91, 0xbe, 0x83, 0x0b, 0xab, 0x72, 0x0b, 0x4a, 0x56, 0x78, 0x1c, 0x2d, + 0x18, 0x2c, 0x82, 0xeb, 0x77, 0x00, 0x52, 0x67, 0xc2, 0x45, 0x25, 0xe8, 0xf7, 0xa1, 0xa2, 0xfc, + 0x07, 0x71, 0xfe, 0xa5, 0x9f, 0x96, 0x97, 0x83, 0xb3, 0x95, 0xf1, 0xe4, 0xf3, 0xf4, 0xdd, 0x60, + 0x74, 0x4a, 0x47, 0x06, 0x1f, 0x02, 0xdd, 0x94, 0x1e, 0xce, 0x3d, 0x58, 0x91, 0x90, 0xf1, 0xe5, + 0x03, 0x45, 0x24, 0xee, 0x43, 0xc2, 0x2f, 0x5f, 0x65, 0xad, 0xd0, 0x9b, 0xea, 0x4e, 0x3d, 0xcd, + 0xb2, 0x47, 0xd2, 0x7a, 0xdc, 0xa5, 0x97, 0xd7, 0x0a, 0xd9, 0x67, 0x5c, 0x73, 0x75, 0x32, 0x32, + 0x64, 0x7a, 0x03, 0xea, 0x59, 0xa7, 0x27, 0xbd, 0x09, 0xeb, 0x7b, 0x4e, 0x6c, 0x21, 0xff, 0x51, + 0x61, 0xfc, 0x79, 0xfe, 0xe2, 0x8f, 0xfc, 0xfc, 0x9d, 0xa5, 0x33, 0x98, 0x48, 0xff, 0xfb, 0x25, + 0xd0, 0x66, 0x71, 0x3f, 0x14, 0x5f, 0xe0, 0x36, 0xd4, 0xf8, 0xc9, 0x9e, 0xac, 0x25, 0x11, 0x18, + 0x94, 0xb9, 0x7d, 0xc2, 0xcf, 0xd9, 0xa4, 0x0f, 0xf7, 0xef, 0x52, 0x5a, 0x5c, 0xe3, 0xcb, 0xe4, + 0x5e, 0x30, 0xa2, 0x69, 0x5d, 0xa7, 0xbb, 0xe3, 0xdd, 0x60, 0x44, 0x61, 0x0b, 0xa4, 0xc1, 0x83, + 0x3d, 0x71, 0xeb, 0x46, 0x45, 0x5a, 0x39, 0xe8, 0x14, 0x53, 0xde, 0x49, 0x89, 0x23, 0x19, 0x5b, + 0xac, 0xc2, 0x80, 0x61, 0xa4, 0x1e, 0x21, 0x1f, 0xf9, 0xb1, 0x8c, 0x9a, 0xb3, 0x1c, 0x06, 0x67, + 0x2d, 0x9f, 0xa2, 0x16, 0xd0, 0x13, 0xa0, 0x88, 0xa9, 0xa4, 0xcf, 0xc0, 0x23, 0x8a, 0x42, 0xd5, + 0x70, 0x9c, 0x55, 0x7e, 0x80, 0xab, 0x2a, 0x5f, 0x5e, 0x93, 0xc0, 0xe4, 0x7d, 0x43, 0xde, 0xff, + 0x32, 0xf1, 0x88, 0x80, 0x41, 0x44, 0x70, 0x1d, 0x2a, 0xdf, 0x07, 0xbe, 0x43, 0x86, 0x93, 0x1a, + 0xd5, 0x6a, 0x05, 0xd3, 0x7b, 0xd6, 0x04, 0x37, 0x02, 0x8f, 0xc2, 0xd8, 0xf1, 0xad, 0x4b, 0x4e, + 0xe8, 0xff, 0x7d, 0x01, 0x36, 0x66, 0xfb, 0x9a, 0xa6, 0x51, 0x1d, 0x2a, 0xad, 0x7e, 0xd7, 0xec, + 0x35, 0xf7, 0xda, 0xda, 0x25, 0xb1, 0x06, 0xb5, 0xfe, 0xd6, 0x57, 0xed, 0xd6, 0x90, 0x01, 0x05, + 0x8a, 0xa8, 0x39, 0x30, 0x77, 0x3b, 0xdb, 0xdb, 0xed, 0x1e, 0x6b, 0x14, 0xfd, 0xad, 0xaf, 0xcc, + 0x6e, 0xbf, 0xa5, 0x15, 0x11, 0xa7, 0xdc, 0x41, 0x06, 0x5a, 0x89, 0x9c, 0x45, 0xe8, 0x52, 0x00, + 0x26, 0xcb, 0xec, 0xed, 0xfe, 0x7c, 0x60, 0xb6, 0x7a, 0x43, 0x6d, 0x19, 0x53, 0xbd, 0x83, 0x6e, + 0x97, 0x52, 0xe4, 0xd6, 0xda, 0xea, 0xef, 0xed, 0x1b, 0xed, 0xc1, 0xc0, 0x1c, 0x74, 0x7e, 0xd5, + 0xd6, 0x2a, 0xf4, 0x65, 0xa3, 0xf3, 0xa4, 0xd3, 0x63, 0x40, 0x55, 0xac, 0x40, 0x71, 0xaf, 0xd3, + 0xe3, 0x48, 0xa2, 0x7b, 0xcd, 0xaf, 0xb5, 0x1a, 0xfe, 0x18, 0x1c, 0xec, 0x69, 0x75, 0x51, 0x85, + 0x72, 0xb7, 0xfd, 0xac, 0xdd, 0xd5, 0x56, 0xf5, 0x7f, 0x56, 0x54, 0x12, 0x31, 0xf9, 0x7b, 0xfd, + 0x18, 0x29, 0x70, 0xd1, 0x29, 0x6b, 0xd2, 0x69, 0xc5, 0x4c, 0xa7, 0x89, 0x37, 0xa0, 0x2e, 0x37, + 0x85, 0xec, 0x39, 0x59, 0x4d, 0xc2, 0x68, 0xca, 0xbd, 0x09, 0xab, 0x89, 0x8b, 0x44, 0xe6, 0xe1, + 0xf0, 0xba, 0x02, 0x2e, 0x38, 0xae, 0x59, 0x5e, 0x70, 0x5c, 0x33, 0x71, 0x63, 0x54, 0xdc, 0x91, + 0xe7, 0xf2, 0x4c, 0xaa, 0x22, 0xe4, 0x19, 0xf1, 0xda, 0xd7, 0x80, 0x12, 0xe6, 0xd4, 0x97, 0x4f, + 0x1e, 0x55, 0x8d, 0x0a, 0x02, 0x0e, 0x7c, 0x37, 0x9e, 0x75, 0xd1, 0xa8, 0xce, 0xb9, 0x68, 0x64, + 0x37, 0x67, 0xc8, 0x6f, 0xce, 0xaf, 0x27, 0x7b, 0x24, 0x22, 0x6b, 0x74, 0x56, 0x51, 0x4d, 0x76, + 0x41, 0xf1, 0x3e, 0x88, 0xd1, 0x34, 0xa4, 0x27, 0x44, 0x32, 0x64, 0xfc, 0x54, 0x91, 0x26, 0x31, + 0xc9, 0xae, 0x48, 0x91, 0x5c, 0xf2, 0xd4, 0xa4, 0x9e, 0x57, 0x8d, 0x46, 0x9e, 0x54, 0x3c, 0x80, + 0xcb, 0x72, 0x6e, 0xe7, 0xfa, 0x56, 0x06, 0x81, 0x60, 0x54, 0x33, 0xed, 0x61, 0xfd, 0xb7, 0xa0, + 0xa2, 0x5c, 0xfb, 0x7e, 0x58, 0xd8, 0x5d, 0x30, 0xae, 0xfa, 0xbf, 0xbf, 0x04, 0xd5, 0xc4, 0xd1, + 0xef, 0x47, 0xcd, 0x8e, 0xd7, 0x50, 0x4e, 0x8d, 0x4e, 0xb3, 0x2c, 0xa6, 0x82, 0x00, 0x35, 0x52, + 0xf2, 0x6e, 0xe0, 0x34, 0x74, 0x95, 0xc4, 0xc6, 0x90, 0x83, 0xd0, 0xa5, 0x38, 0xe1, 0xae, 0x9f, + 0x09, 0xd7, 0x50, 0x35, 0x2a, 0x08, 0xa0, 0x85, 0x76, 0x1d, 0xe8, 0x37, 0xe5, 0xe4, 0x49, 0xb2, + 0x82, 0x69, 0xcc, 0x77, 0x35, 0xd1, 0xff, 0x64, 0xa8, 0xb3, 0x38, 0x11, 0x2c, 0xa4, 0x8f, 0x11, + 0x7b, 0x56, 0xc8, 0x14, 0x7e, 0x87, 0x02, 0x04, 0x51, 0x1d, 0xe5, 0x8c, 0x40, 0x80, 0xaa, 0x63, + 0x66, 0xb8, 0xaa, 0xb3, 0xa3, 0x3a, 0x3b, 0xa7, 0x61, 0x6e, 0x4e, 0xeb, 0x31, 0xac, 0x48, 0x67, + 0xc7, 0x1f, 0xee, 0xf0, 0x1f, 0xec, 0x2a, 0x0d, 0x8a, 0x96, 0xa7, 0x6e, 0xdf, 0xe3, 0xcf, 0x99, + 0x8a, 0x95, 0x66, 0x2a, 0xa6, 0xff, 0x9b, 0x4b, 0x00, 0xa9, 0xd3, 0xa4, 0xf8, 0x60, 0xc6, 0x41, + 0xbb, 0x30, 0xb7, 0xed, 0xcf, 0xf8, 0x65, 0xcf, 0x04, 0xce, 0x5f, 0xfa, 0x11, 0x81, 0xf3, 0x1f, + 0xc2, 0x6a, 0x14, 0x8e, 0x5e, 0x69, 0xc3, 0xaf, 0x45, 0xe1, 0x28, 0x31, 0xe1, 0x7f, 0x08, 0x98, + 0xa4, 0x47, 0x75, 0x52, 0x45, 0x75, 0x4e, 0x6a, 0xa9, 0x46, 0xe1, 0xa8, 0x7f, 0xf8, 0xed, 0x36, + 0x5f, 0xc8, 0xb4, 0xa3, 0xd8, 0x5c, 0xc4, 0x25, 0xd6, 0xec, 0x28, 0xde, 0xce, 0x32, 0x8a, 0xbb, + 0xd0, 0x40, 0xda, 0x39, 0x66, 0x51, 0xb7, 0xa3, 0xf4, 0x40, 0x49, 0xff, 0x97, 0xd4, 0xc1, 0xfc, + 0x8c, 0x79, 0x58, 0x7c, 0x26, 0x15, 0xf1, 0x8c, 0x10, 0xb1, 0xb9, 0xc8, 0x9a, 0xcc, 0x77, 0xb6, + 0x13, 0x52, 0xf1, 0x18, 0x56, 0x13, 0xdc, 0xb6, 0x73, 0xa4, 0x64, 0x20, 0xf5, 0x42, 0x4b, 0x06, + 0x65, 0xe4, 0x09, 0x51, 0x28, 0x4a, 0x9f, 0x35, 0xc2, 0x15, 0x98, 0x5c, 0x5a, 0xaa, 0xca, 0xeb, + 0x48, 0x8f, 0xe1, 0xda, 0x4b, 0xec, 0x68, 0xea, 0x5c, 0x52, 0xc6, 0x61, 0x90, 0x41, 0xdd, 0x7c, + 0xe7, 0x8c, 0xe3, 0x30, 0xe8, 0x3e, 0x5c, 0x5e, 0xf0, 0x1e, 0xb9, 0xb8, 0x43, 0x2e, 0x5b, 0xe9, + 0xfb, 0xeb, 0x52, 0xb6, 0x1a, 0xa5, 0x8f, 0xae, 0x7f, 0x0a, 0x6b, 0x8e, 0xe7, 0x1e, 0xbb, 0x87, + 0x2e, 0xc5, 0x1a, 0x1f, 0x05, 0xde, 0xa2, 0xd7, 0xf9, 0x8d, 0x46, 0x86, 0xa8, 0x15, 0x78, 0xfa, + 0xbf, 0x52, 0xc8, 0x3d, 0x2e, 0xa8, 0x9e, 0x17, 0xe3, 0xc7, 0x05, 0xe5, 0x2b, 0xc6, 0x05, 0x83, + 0x7e, 0xe3, 0xca, 0x94, 0x0f, 0xd7, 0xca, 0xc7, 0xc2, 0x38, 0x25, 0x3e, 0x84, 0x0d, 0x19, 0x2f, + 0x9d, 0xa2, 0x95, 0x99, 0x13, 0x27, 0x34, 0xc3, 0xe0, 0x8c, 0x26, 0x56, 0xc1, 0x90, 0xb1, 0xd4, + 0x29, 0x68, 0xd9, 0xbe, 0x13, 0x1a, 0xc1, 0x99, 0xd8, 0x24, 0xa9, 0xc4, 0x19, 0xc5, 0xea, 0x8c, + 0x59, 0x25, 0xf5, 0xdf, 0x85, 0xcb, 0x0b, 0xae, 0x05, 0x8a, 0xb7, 0x94, 0x9e, 0xf7, 0x92, 0x4b, + 0xc0, 0x0b, 0x75, 0xcb, 0xa5, 0x59, 0xdd, 0xf2, 0x8f, 0x0b, 0x50, 0x53, 0xcf, 0xf2, 0x19, 0x1c, + 0x28, 0x50, 0xbd, 0xde, 0x97, 0x46, 0x5a, 0x02, 0x05, 0xea, 0xd8, 0xa8, 0x7a, 0x86, 0xce, 0x0b, + 0x37, 0x52, 0xde, 0x96, 0x25, 0x23, 0x49, 0x8b, 0xf7, 0x60, 0x3d, 0x09, 0x4d, 0x38, 0x13, 0x57, + 0x4f, 0x4b, 0x10, 0x2a, 0x04, 0xdb, 0x3c, 0x17, 0x28, 0x65, 0xd9, 0xd3, 0xcc, 0x7e, 0x56, 0x9e, + 0xdd, 0xcf, 0xf4, 0x7f, 0xa3, 0x00, 0x9b, 0x83, 0xef, 0x3c, 0x79, 0xc5, 0xb1, 0x33, 0x9e, 0x78, + 0x34, 0x99, 0x38, 0x38, 0xcc, 0xa7, 0x70, 0x35, 0x75, 0x27, 0x35, 0x8f, 0x5c, 0xff, 0xd8, 0x09, + 0x27, 0xa1, 0x2b, 0x1d, 0x11, 0xea, 0xc6, 0x95, 0x14, 0xbb, 0x93, 0x22, 0xc5, 0x53, 0xd0, 0x23, + 0x67, 0x6c, 0xf9, 0xb1, 0x3b, 0x32, 0x33, 0xf9, 0x59, 0xf3, 0x48, 0x5a, 0xc4, 0x23, 0x7d, 0x5b, + 0x51, 0xa6, 0x57, 0x67, 0x06, 0x44, 0x27, 0x1b, 0xa8, 0xff, 0xe7, 0x45, 0x78, 0x6d, 0xff, 0x22, + 0x3e, 0x09, 0xfc, 0xc5, 0x75, 0xdc, 0x84, 0x95, 0x13, 0xcb, 0xb7, 0xbd, 0x24, 0x00, 0xa9, 0x4a, + 0x8a, 0x1b, 0xb0, 0xcc, 0x7b, 0x09, 0x8f, 0xd7, 0xd6, 0xd2, 0x66, 0xc1, 0x90, 0x10, 0x62, 0xdb, + 0x87, 0xae, 0x49, 0xf7, 0x7c, 0xac, 0x51, 0x2c, 0xf7, 0x9e, 0x9a, 0x75, 0xe8, 0xb6, 0x24, 0x08, + 0x77, 0x60, 0x8a, 0x24, 0xef, 0x84, 0x49, 0x95, 0xa5, 0x63, 0x8f, 0x04, 0xab, 0x21, 0x40, 0xc2, + 0x30, 0x76, 0x8f, 0xac, 0x51, 0xac, 0xa2, 0xcb, 0x95, 0x25, 0xa1, 0x04, 0xcb, 0xe0, 0x72, 0x1f, + 0x80, 0x70, 0xfc, 0x17, 0x6e, 0x18, 0xf8, 0x14, 0x59, 0x43, 0xd2, 0x32, 0x4b, 0x5a, 0xcf, 0x60, + 0x24, 0xf9, 0x6d, 0xa8, 0x45, 0xf6, 0x69, 0xf2, 0x71, 0x19, 0x69, 0x37, 0xb2, 0x4f, 0xd5, 0x87, + 0xbf, 0x84, 0xeb, 0x2f, 0xef, 0x5e, 0x8e, 0x12, 0x74, 0xcd, 0x5e, 0xdc, 0xad, 0x09, 0x6f, 0xa9, + 0xa6, 0xbc, 0x85, 0x9c, 0x4d, 0xe8, 0xa5, 0xfd, 0xc0, 0x73, 0x47, 0x17, 0x72, 0x2b, 0x03, 0x04, + 0xed, 0x13, 0xe4, 0x07, 0xe6, 0x03, 0xbb, 0x0f, 0x2e, 0x9e, 0x0f, 0xfa, 0xbf, 0x5b, 0x86, 0x9a, + 0x1c, 0xbc, 0x16, 0xee, 0x5c, 0x1c, 0xfd, 0x88, 0x7a, 0x39, 0xa9, 0x2e, 0x07, 0x05, 0x59, 0x53, + 0x70, 0x55, 0xcd, 0x4f, 0xa0, 0x9e, 0x2c, 0xa4, 0x34, 0x0a, 0x83, 0x8a, 0x8c, 0x9b, 0xae, 0x38, + 0x23, 0x59, 0x6f, 0xb8, 0xfc, 0x6e, 0x40, 0xc5, 0xb3, 0xfc, 0xe3, 0xa9, 0x75, 0x9c, 0x38, 0x76, + 0xaa, 0xb4, 0xf8, 0x18, 0x1a, 0x56, 0x78, 0x3c, 0xa5, 0x11, 0x48, 0x55, 0xdb, 0x7c, 0xfc, 0x8d, + 0x55, 0x45, 0xc1, 0x61, 0x33, 0x3e, 0x86, 0x1a, 0xdf, 0x5d, 0x66, 0x49, 0xa4, 0xfc, 0x12, 0x0f, + 0x17, 0x60, 0x22, 0xda, 0x02, 0x6e, 0x01, 0xbc, 0x08, 0x3c, 0x2b, 0x26, 0x6e, 0x28, 0x87, 0x38, + 0x03, 0x99, 0xed, 0xea, 0x95, 0xb9, 0xae, 0x7e, 0xc8, 0x1e, 0xcd, 0x7c, 0xea, 0x25, 0x8f, 0x9c, + 0x5f, 0xb6, 0x4e, 0x77, 0x2f, 0x91, 0xcf, 0xb3, 0xf8, 0x2d, 0x58, 0x9e, 0xd0, 0x4a, 0x91, 0x07, + 0x5e, 0x6f, 0xc8, 0x0d, 0xe7, 0xe5, 0xab, 0x67, 0xf7, 0x92, 0x21, 0xb3, 0x88, 0x9f, 0xf1, 0xad, + 0x37, 0xe7, 0x3c, 0x96, 0x31, 0x97, 0x6f, 0xe5, 0x2e, 0x3f, 0xe3, 0xc0, 0x3d, 0x68, 0x31, 0x01, + 0x1d, 0xbe, 0xd0, 0x72, 0x52, 0x59, 0xc8, 0x69, 0xc6, 0xf2, 0xbc, 0xc8, 0x8d, 0x1d, 0x25, 0xfc, + 0x56, 0x0d, 0x50, 0xa0, 0x0e, 0x45, 0x85, 0x1e, 0x5b, 0x17, 0xa6, 0x13, 0x86, 0x41, 0x28, 0x63, + 0x84, 0x54, 0xc6, 0xd6, 0x45, 0x1b, 0xd3, 0x28, 0xf5, 0x47, 0xce, 0x68, 0x1a, 0xe2, 0xf6, 0x42, + 0xb3, 0x92, 0x45, 0xdd, 0xba, 0x02, 0xd2, 0xb5, 0xdb, 0x9b, 0x50, 0xf5, 0x1c, 0xeb, 0x74, 0x12, + 0x06, 0xc1, 0x91, 0x14, 0x6f, 0x53, 0xc0, 0x8d, 0x2f, 0xa1, 0x9e, 0xad, 0xdd, 0xab, 0x9c, 0x1a, + 0xaa, 0x99, 0xc3, 0xa0, 0x2d, 0x0d, 0x1a, 0x6e, 0xae, 0x5b, 0xf4, 0x7f, 0x58, 0x84, 0x2b, 0x0b, + 0x6f, 0x7d, 0xcf, 0x4d, 0xc8, 0xc2, 0x4f, 0x9e, 0x90, 0x4b, 0x33, 0x13, 0x72, 0xd1, 0x6a, 0x28, + 0x2e, 0x5e, 0x0d, 0x2f, 0x5f, 0x7f, 0xa5, 0x1f, 0x58, 0x7f, 0x7f, 0x65, 0x0c, 0x2a, 0x3f, 0xc9, + 0x57, 0x5e, 0x35, 0xc9, 0x2b, 0x73, 0x93, 0x3c, 0x37, 0x29, 0xaa, 0xaf, 0x9a, 0x14, 0xf0, 0xaa, + 0x49, 0x51, 0x9b, 0x99, 0x14, 0xf7, 0xff, 0xf5, 0x02, 0xac, 0x0f, 0xe2, 0xd0, 0xf5, 0x8f, 0xbb, + 0x6e, 0xec, 0x84, 0x96, 0xb7, 0x13, 0x84, 0x63, 0xba, 0x3f, 0x33, 0x34, 0x3a, 0xbd, 0x27, 0x66, + 0xb7, 0x33, 0x6c, 0x1b, 0xcd, 0xe4, 0x2d, 0x80, 0x79, 0xc4, 0xb0, 0xfd, 0xf5, 0x50, 0x2b, 0x88, + 0xbb, 0x70, 0x67, 0x06, 0xb1, 0xd5, 0xe9, 0x35, 0x8d, 0x6f, 0xcc, 0x4e, 0x6f, 0x68, 0xf4, 0xb7, + 0x0f, 0x5a, 0x6d, 0x43, 0x5b, 0x12, 0x57, 0x41, 0xcc, 0x50, 0xed, 0xb6, 0xbf, 0xd6, 0x8a, 0x0b, + 0xe0, 0x5b, 0x9d, 0xa1, 0x56, 0xba, 0xbf, 0x07, 0x97, 0x9b, 0xc7, 0xc7, 0xa1, 0x73, 0x8c, 0xea, + 0x14, 0xc5, 0x9d, 0x56, 0x97, 0x98, 0x9a, 0x4f, 0x9e, 0x98, 0xad, 0x7e, 0x6f, 0xa7, 0xf3, 0x44, + 0x55, 0xed, 0x0d, 0x78, 0x3d, 0x03, 0x7c, 0x62, 0xf4, 0x0f, 0xf6, 0x31, 0xd1, 0x6a, 0x0e, 0xcd, + 0xbe, 0xb1, 0xdd, 0x36, 0xb4, 0xc2, 0xfd, 0x37, 0x70, 0x05, 0xb0, 0xd1, 0x23, 0x79, 0x3a, 0x21, + 0xf0, 0x1d, 0xed, 0x12, 0x2a, 0xfa, 0xdd, 0xef, 0x3f, 0xd1, 0x0a, 0xf7, 0x75, 0xa8, 0x0d, 0x4e, + 0xa6, 0x47, 0x47, 0x9e, 0xa3, 0x28, 0x76, 0xad, 0xe8, 0x84, 0x1d, 0x9b, 0xe9, 0xa1, 0x5e, 0xad, + 0x70, 0xff, 0x6d, 0x58, 0x95, 0x34, 0x7b, 0x4e, 0x7c, 0x12, 0xd8, 0x02, 0x60, 0xb9, 0x17, 0x84, + 0x63, 0xcb, 0x93, 0x74, 0xce, 0x34, 0x42, 0xba, 0x0f, 0xe1, 0x4a, 0xa6, 0xac, 0x9d, 0x20, 0xa4, + 0xf8, 0x77, 0xad, 0x1e, 0x05, 0x39, 0xa0, 0xe5, 0xc4, 0xd7, 0xf5, 0x77, 0x2f, 0x0e, 0x43, 0xd7, + 0xd6, 0x0a, 0xf7, 0x1f, 0x43, 0x35, 0x7d, 0x0b, 0x9c, 0x1e, 0x1d, 0xd9, 0xea, 0x1f, 0xf4, 0xb6, + 0xe9, 0x55, 0x03, 0x7a, 0x68, 0xa4, 0xd5, 0x3d, 0x18, 0x74, 0x9e, 0x49, 0x2b, 0x49, 0xfb, 0x6b, + 0x95, 0x5c, 0xba, 0xff, 0x07, 0x05, 0x15, 0x32, 0x53, 0x55, 0xbb, 0xdb, 0x6f, 0x6e, 0xb3, 0x79, + 0x25, 0x79, 0x4f, 0x65, 0xb8, 0xa5, 0x15, 0x10, 0x60, 0xb4, 0x07, 0x07, 0xdd, 0xa1, 0x7a, 0xaa, + 0xa5, 0x01, 0xd0, 0x69, 0xb5, 0xb7, 0xda, 0xc6, 0x13, 0x24, 0x28, 0x62, 0x7a, 0xaf, 0xdf, 0x7b, + 0xd2, 0xdf, 0xde, 0xc2, 0x34, 0x3d, 0xd2, 0xb2, 0xdd, 0x1c, 0x36, 0x07, 0x43, 0xa3, 0xdd, 0xdc, + 0x43, 0x10, 0xdd, 0x23, 0x56, 0xfe, 0xdb, 0xc3, 0x2d, 0x36, 0xb5, 0x3c, 0x6d, 0xee, 0x3c, 0x6d, + 0x62, 0x6a, 0xe5, 0xfe, 0x7f, 0xbb, 0x04, 0xda, 0x6c, 0x58, 0x54, 0xf1, 0x1a, 0x5c, 0xa3, 0x52, + 0xcd, 0x7d, 0xa3, 0xbd, 0xdd, 0x69, 0xf1, 0x8d, 0xb0, 0x67, 0xcd, 0x6e, 0x67, 0x9b, 0x67, 0xd5, + 0x2c, 0xb2, 0xd9, 0xdb, 0xe6, 0xe7, 0x1a, 0x66, 0x11, 0xed, 0x5f, 0x1e, 0x34, 0xbb, 0xda, 0x92, + 0x78, 0x1d, 0xae, 0xcf, 0xa2, 0x7a, 0xfd, 0xa1, 0x44, 0x17, 0xc5, 0x26, 0x6c, 0xcc, 0xa2, 0xbb, + 0xed, 0xc1, 0x40, 0x2b, 0x89, 0x5b, 0x70, 0x63, 0x11, 0x46, 0xe6, 0x2c, 0x2f, 0xaa, 0xe9, 0x13, + 0xb2, 0x39, 0x19, 0xda, 0x32, 0x4e, 0xb2, 0x97, 0x20, 0x65, 0xfe, 0x15, 0x9c, 0xcb, 0xf3, 0x2d, + 0xd5, 0x2a, 0x0b, 0x7b, 0x60, 0x60, 0xd2, 0xe3, 0x1c, 0x55, 0x71, 0x1b, 0x5e, 0x5b, 0x84, 0xec, + 0x0f, 0x99, 0x00, 0xee, 0xff, 0xe3, 0x02, 0x6c, 0xbe, 0x2c, 0x72, 0x96, 0xd0, 0xe1, 0x96, 0x71, + 0xd0, 0xa3, 0xa7, 0x56, 0xf8, 0x55, 0x18, 0xf3, 0x69, 0xfb, 0x9b, 0x99, 0x57, 0x31, 0x5e, 0x87, + 0xeb, 0x0b, 0x68, 0x8c, 0xe6, 0x73, 0xf3, 0xd9, 0xc7, 0x5a, 0x41, 0xbc, 0x07, 0xef, 0x2c, 0x40, + 0xef, 0x74, 0xfb, 0xcd, 0xa1, 0xf9, 0xab, 0xb6, 0xd1, 0x37, 0x5b, 0xdd, 0xfe, 0xa0, 0xbd, 0x8d, + 0xc4, 0x4b, 0xe2, 0x0e, 0xdc, 0x5c, 0x40, 0x3c, 0xe0, 0x87, 0x7c, 0x9e, 0x7d, 0xac, 0x15, 0xc5, + 0x5b, 0xf0, 0xc6, 0xcb, 0x29, 0x76, 0x0e, 0xba, 0x44, 0x56, 0xba, 0xff, 0x0b, 0xd8, 0x7c, 0x59, + 0xb8, 0x05, 0x5c, 0x48, 0xad, 0xdd, 0x26, 0x85, 0xb4, 0xa8, 0x43, 0xa5, 0xd7, 0x37, 0x39, 0x45, + 0x0e, 0x07, 0x46, 0xbb, 0xdb, 0xa6, 0x3b, 0x89, 0xf7, 0x7f, 0x5d, 0xc8, 0x98, 0xb5, 0xd5, 0x95, + 0xf9, 0x04, 0x20, 0x57, 0x77, 0x16, 0x64, 0x38, 0x96, 0xad, 0x15, 0x70, 0x94, 0x72, 0xa0, 0x6e, + 0x30, 0xb2, 0x3c, 0x6d, 0x89, 0x6e, 0x1f, 0x2a, 0x38, 0x05, 0xb0, 0xd1, 0x8a, 0xd8, 0x75, 0x09, + 0xac, 0x1b, 0x9c, 0xed, 0x87, 0x6e, 0x80, 0xdc, 0x95, 0xd1, 0xa5, 0xfb, 0xff, 0xbc, 0xf4, 0x91, + 0xcd, 0x29, 0xbb, 0xf8, 0x81, 0xa6, 0x6d, 0xa7, 0x30, 0xb2, 0xaf, 0xf0, 0x5c, 0x27, 0xe3, 0xd2, + 0x0c, 0xa2, 0x80, 0xf3, 0x43, 0x9d, 0xbd, 0xcd, 0x22, 0x97, 0x10, 0x69, 0x38, 0xb4, 0x75, 0xcd, + 0x21, 0x8b, 0x5b, 0x4f, 0xff, 0xd1, 0x9f, 0xdd, 0x2a, 0xfc, 0xc9, 0x9f, 0xdd, 0x2a, 0xfc, 0x2f, + 0x7f, 0x76, 0xeb, 0xd2, 0x5f, 0xff, 0xf3, 0x5b, 0x85, 0x3f, 0xfa, 0xf3, 0x5b, 0x85, 0x3f, 0xf9, + 0xf3, 0x5b, 0x85, 0xff, 0xf1, 0xcf, 0x6f, 0x15, 0x7e, 0xf5, 0xc1, 0xb1, 0x1b, 0x9f, 0x4c, 0x0f, + 0x1f, 0x8c, 0x82, 0xf1, 0x87, 0x63, 0x2b, 0x0e, 0xdd, 0x73, 0xb6, 0x78, 0xa9, 0x84, 0xef, 0x7c, + 0x38, 0x39, 0x3d, 0xfe, 0x70, 0x72, 0xf8, 0x21, 0x6e, 0xd0, 0x87, 0xcb, 0x93, 0x30, 0x88, 0x83, + 0x47, 0xff, 0x5f, 0x00, 0x00, 0x00, 0xff, 0xff, 0x90, 0x13, 0x34, 0xd6, 0x46, 0xbc, 0x00, 0x00, } func (m *Type) Marshal() (dAtA []byte, err error) { @@ -18917,6 +19582,18 @@ func (m *Function) MarshalToSizedBuffer(dAtA []byte) (int, error) { i -= len(m.XXX_unrecognized) copy(dAtA[i:], m.XXX_unrecognized) } + if m.RoutineCall != nil { + { + size, err := m.RoutineCall.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintPlan(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x32 + } if m.SyntaxExplicitCast { i-- if m.SyntaxExplicitCast { @@ -20185,20 +20862,20 @@ func (m *PrimaryKeyDef) MarshalToSizedBuffer(dAtA []byte) (int, error) { dAtA[i] = 0x10 } if len(m.Cols) > 0 { - dAtA35 := make([]byte, len(m.Cols)*10) - var j34 int + dAtA36 := make([]byte, len(m.Cols)*10) + var j35 int for _, num := range m.Cols { for num >= 1<<7 { - dAtA35[j34] = uint8(uint64(num)&0x7f | 0x80) + dAtA36[j35] = uint8(uint64(num)&0x7f | 0x80) num >>= 7 - j34++ + j35++ } - dAtA35[j34] = uint8(num) - j34++ + dAtA36[j35] = uint8(num) + j35++ } - i -= j34 - copy(dAtA[i:], dAtA35[:j34]) - i = encodeVarintPlan(dAtA, i, uint64(j34)) + i -= j35 + copy(dAtA[i:], dAtA36[:j35]) + i = encodeVarintPlan(dAtA, i, uint64(j35)) i-- dAtA[i] = 0xa } @@ -20393,20 +21070,20 @@ func (m *ForeignKeyDef) MarshalToSizedBuffer(dAtA []byte) (int, error) { dAtA[i] = 0x28 } if len(m.ForeignCols) > 0 { - dAtA38 := make([]byte, len(m.ForeignCols)*10) - var j37 int + dAtA39 := make([]byte, len(m.ForeignCols)*10) + var j38 int for _, num := range m.ForeignCols { for num >= 1<<7 { - dAtA38[j37] = uint8(uint64(num)&0x7f | 0x80) + dAtA39[j38] = uint8(uint64(num)&0x7f | 0x80) num >>= 7 - j37++ + j38++ } - dAtA38[j37] = uint8(num) - j37++ + dAtA39[j38] = uint8(num) + j38++ } - i -= j37 - copy(dAtA[i:], dAtA38[:j37]) - i = encodeVarintPlan(dAtA, i, uint64(j37)) + i -= j38 + copy(dAtA[i:], dAtA39[:j38]) + i = encodeVarintPlan(dAtA, i, uint64(j38)) i-- dAtA[i] = 0x22 } @@ -20416,20 +21093,20 @@ func (m *ForeignKeyDef) MarshalToSizedBuffer(dAtA []byte) (int, error) { dAtA[i] = 0x18 } if len(m.Cols) > 0 { - dAtA40 := make([]byte, len(m.Cols)*10) - var j39 int + dAtA41 := make([]byte, len(m.Cols)*10) + var j40 int for _, num := range m.Cols { for num >= 1<<7 { - dAtA40[j39] = uint8(uint64(num)&0x7f | 0x80) + dAtA41[j40] = uint8(uint64(num)&0x7f | 0x80) num >>= 7 - j39++ + j40++ } - dAtA40[j39] = uint8(num) - j39++ + dAtA41[j40] = uint8(num) + j40++ } - i -= j39 - copy(dAtA[i:], dAtA40[:j39]) - i = encodeVarintPlan(dAtA, i, uint64(j39)) + i -= j40 + copy(dAtA[i:], dAtA41[:j40]) + i = encodeVarintPlan(dAtA, i, uint64(j40)) i-- dAtA[i] = 0x12 } @@ -21021,20 +21698,20 @@ func (m *TableDef) MarshalToSizedBuffer(dAtA []byte) (int, error) { } } if len(m.RefChildTbls) > 0 { - dAtA48 := make([]byte, len(m.RefChildTbls)*10) - var j47 int + dAtA49 := make([]byte, len(m.RefChildTbls)*10) + var j48 int for _, num := range m.RefChildTbls { for num >= 1<<7 { - dAtA48[j47] = uint8(uint64(num)&0x7f | 0x80) + dAtA49[j48] = uint8(uint64(num)&0x7f | 0x80) num >>= 7 - j47++ + j48++ } - dAtA48[j47] = uint8(num) - j47++ + dAtA49[j48] = uint8(num) + j48++ } - i -= j47 - copy(dAtA[i:], dAtA48[:j47]) - i = encodeVarintPlan(dAtA, i, uint64(j47)) + i -= j48 + copy(dAtA[i:], dAtA49[:j48]) + i = encodeVarintPlan(dAtA, i, uint64(j48)) i-- dAtA[i] = 0x72 } @@ -21306,9 +21983,9 @@ func (m *HashMapStats) MarshalToSizedBuffer(dAtA []byte) (int, error) { } if len(m.Ranges) > 0 { for iNdEx := len(m.Ranges) - 1; iNdEx >= 0; iNdEx-- { - f54 := math.Float64bits(float64(m.Ranges[iNdEx])) + f55 := math.Float64bits(float64(m.Ranges[iNdEx])) i -= 8 - encoding_binary.LittleEndian.PutUint64(dAtA[i:], uint64(f54)) + encoding_binary.LittleEndian.PutUint64(dAtA[i:], uint64(f55)) } i = encodeVarintPlan(dAtA, i, uint64(len(m.Ranges)*8)) i-- @@ -22018,21 +22695,21 @@ func (m *DedupJoinCtx) MarshalToSizedBuffer(dAtA []byte) (int, error) { dAtA[i] = 0x48 } if len(m.UpdateCheckColIdxList) > 0 { - dAtA67 := make([]byte, len(m.UpdateCheckColIdxList)*10) - var j66 int + dAtA68 := make([]byte, len(m.UpdateCheckColIdxList)*10) + var j67 int for _, num1 := range m.UpdateCheckColIdxList { num := uint64(num1) for num >= 1<<7 { - dAtA67[j66] = uint8(uint64(num)&0x7f | 0x80) + dAtA68[j67] = uint8(uint64(num)&0x7f | 0x80) num >>= 7 - j66++ + j67++ } - dAtA67[j66] = uint8(num) - j66++ + dAtA68[j67] = uint8(num) + j67++ } - i -= j66 - copy(dAtA[i:], dAtA67[:j66]) - i = encodeVarintPlan(dAtA, i, uint64(j66)) + i -= j67 + copy(dAtA[i:], dAtA68[:j67]) + i = encodeVarintPlan(dAtA, i, uint64(j67)) i-- dAtA[i] = 0x42 } @@ -22099,21 +22776,21 @@ func (m *DedupJoinCtx) MarshalToSizedBuffer(dAtA []byte) (int, error) { } } if len(m.UpdateColIdxList) > 0 { - dAtA71 := make([]byte, len(m.UpdateColIdxList)*10) - var j70 int + dAtA72 := make([]byte, len(m.UpdateColIdxList)*10) + var j71 int for _, num1 := range m.UpdateColIdxList { num := uint64(num1) for num >= 1<<7 { - dAtA71[j70] = uint8(uint64(num)&0x7f | 0x80) + dAtA72[j71] = uint8(uint64(num)&0x7f | 0x80) num >>= 7 - j70++ + j71++ } - dAtA71[j70] = uint8(num) - j70++ + dAtA72[j71] = uint8(num) + j71++ } - i -= j70 - copy(dAtA[i:], dAtA71[:j70]) - i = encodeVarintPlan(dAtA, i, uint64(j70)) + i -= j71 + copy(dAtA[i:], dAtA72[:j71]) + i = encodeVarintPlan(dAtA, i, uint64(j71)) i-- dAtA[i] = 0x12 } @@ -22654,40 +23331,40 @@ func (m *AnalyzeInfo) MarshalToSizedBuffer(dAtA []byte) (int, error) { dAtA[i] = 0x80 } if len(m.TimeConsumedArrayMinor) > 0 { - dAtA80 := make([]byte, len(m.TimeConsumedArrayMinor)*10) - var j79 int + dAtA81 := make([]byte, len(m.TimeConsumedArrayMinor)*10) + var j80 int for _, num1 := range m.TimeConsumedArrayMinor { num := uint64(num1) for num >= 1<<7 { - dAtA80[j79] = uint8(uint64(num)&0x7f | 0x80) + dAtA81[j80] = uint8(uint64(num)&0x7f | 0x80) num >>= 7 - j79++ + j80++ } - dAtA80[j79] = uint8(num) - j79++ + dAtA81[j80] = uint8(num) + j80++ } - i -= j79 - copy(dAtA[i:], dAtA80[:j79]) - i = encodeVarintPlan(dAtA, i, uint64(j79)) + i -= j80 + copy(dAtA[i:], dAtA81[:j80]) + i = encodeVarintPlan(dAtA, i, uint64(j80)) i-- dAtA[i] = 0x7a } if len(m.TimeConsumedArrayMajor) > 0 { - dAtA82 := make([]byte, len(m.TimeConsumedArrayMajor)*10) - var j81 int + dAtA83 := make([]byte, len(m.TimeConsumedArrayMajor)*10) + var j82 int for _, num1 := range m.TimeConsumedArrayMajor { num := uint64(num1) for num >= 1<<7 { - dAtA82[j81] = uint8(uint64(num)&0x7f | 0x80) + dAtA83[j82] = uint8(uint64(num)&0x7f | 0x80) num >>= 7 - j81++ + j82++ } - dAtA82[j81] = uint8(num) - j81++ + dAtA83[j82] = uint8(num) + j82++ } - i -= j81 - copy(dAtA[i:], dAtA82[:j81]) - i = encodeVarintPlan(dAtA, i, uint64(j81)) + i -= j82 + copy(dAtA[i:], dAtA83[:j82]) + i = encodeVarintPlan(dAtA, i, uint64(j82)) i-- dAtA[i] = 0x72 } @@ -23406,21 +24083,21 @@ func (m *Node) MarshalToSizedBuffer(dAtA []byte) (int, error) { dAtA[i] = 0xf8 } if len(m.GroupByHashKey) > 0 { - dAtA103 := make([]byte, len(m.GroupByHashKey)*10) - var j102 int + dAtA104 := make([]byte, len(m.GroupByHashKey)*10) + var j103 int for _, num1 := range m.GroupByHashKey { num := uint64(num1) for num >= 1<<7 { - dAtA103[j102] = uint8(uint64(num)&0x7f | 0x80) + dAtA104[j103] = uint8(uint64(num)&0x7f | 0x80) num >>= 7 - j102++ + j103++ } - dAtA103[j102] = uint8(num) - j102++ + dAtA104[j103] = uint8(num) + j103++ } - i -= j102 - copy(dAtA[i:], dAtA103[:j102]) - i = encodeVarintPlan(dAtA, i, uint64(j102)) + i -= j103 + copy(dAtA[i:], dAtA104[:j103]) + i = encodeVarintPlan(dAtA, i, uint64(j103)) i-- dAtA[i] = 0x4 i-- @@ -23453,21 +24130,21 @@ func (m *Node) MarshalToSizedBuffer(dAtA []byte) (int, error) { dAtA[i] = 0xd8 } if len(m.TimeWindowPartitionColPos) > 0 { - dAtA105 := make([]byte, len(m.TimeWindowPartitionColPos)*10) - var j104 int + dAtA106 := make([]byte, len(m.TimeWindowPartitionColPos)*10) + var j105 int for _, num1 := range m.TimeWindowPartitionColPos { num := uint64(num1) for num >= 1<<7 { - dAtA105[j104] = uint8(uint64(num)&0x7f | 0x80) + dAtA106[j105] = uint8(uint64(num)&0x7f | 0x80) num >>= 7 - j104++ + j105++ } - dAtA105[j104] = uint8(num) - j104++ + dAtA106[j105] = uint8(num) + j105++ } - i -= j104 - copy(dAtA[i:], dAtA105[:j104]) - i = encodeVarintPlan(dAtA, i, uint64(j104)) + i -= j105 + copy(dAtA[i:], dAtA106[:j105]) + i = encodeVarintPlan(dAtA, i, uint64(j105)) i-- dAtA[i] = 0x4 i-- @@ -24019,21 +24696,21 @@ func (m *Node) MarshalToSizedBuffer(dAtA []byte) (int, error) { dAtA[i] = 0xe2 } if len(m.SourceStep) > 0 { - dAtA123 := make([]byte, len(m.SourceStep)*10) - var j122 int + dAtA124 := make([]byte, len(m.SourceStep)*10) + var j123 int for _, num1 := range m.SourceStep { num := uint64(num1) for num >= 1<<7 { - dAtA123[j122] = uint8(uint64(num)&0x7f | 0x80) + dAtA124[j123] = uint8(uint64(num)&0x7f | 0x80) num >>= 7 - j122++ + j123++ } - dAtA123[j122] = uint8(num) - j122++ + dAtA124[j123] = uint8(num) + j123++ } - i -= j122 - copy(dAtA[i:], dAtA123[:j122]) - i = encodeVarintPlan(dAtA, i, uint64(j122)) + i -= j123 + copy(dAtA[i:], dAtA124[:j123]) + i = encodeVarintPlan(dAtA, i, uint64(j123)) i-- dAtA[i] = 0x1 i-- @@ -24070,21 +24747,21 @@ func (m *Node) MarshalToSizedBuffer(dAtA []byte) (int, error) { dAtA[i] = 0xca } if len(m.BindingTags) > 0 { - dAtA126 := make([]byte, len(m.BindingTags)*10) - var j125 int + dAtA127 := make([]byte, len(m.BindingTags)*10) + var j126 int for _, num1 := range m.BindingTags { num := uint64(num1) for num >= 1<<7 { - dAtA126[j125] = uint8(uint64(num)&0x7f | 0x80) + dAtA127[j126] = uint8(uint64(num)&0x7f | 0x80) num >>= 7 - j125++ + j126++ } - dAtA126[j125] = uint8(num) - j125++ + dAtA127[j126] = uint8(num) + j126++ } - i -= j125 - copy(dAtA[i:], dAtA126[:j125]) - i = encodeVarintPlan(dAtA, i, uint64(j125)) + i -= j126 + copy(dAtA[i:], dAtA127[:j126]) + i = encodeVarintPlan(dAtA, i, uint64(j126)) i-- dAtA[i] = 0x1 i-- @@ -24334,21 +25011,21 @@ func (m *Node) MarshalToSizedBuffer(dAtA []byte) (int, error) { } } if len(m.Children) > 0 { - dAtA136 := make([]byte, len(m.Children)*10) - var j135 int + dAtA137 := make([]byte, len(m.Children)*10) + var j136 int for _, num1 := range m.Children { num := uint64(num1) for num >= 1<<7 { - dAtA136[j135] = uint8(uint64(num)&0x7f | 0x80) + dAtA137[j136] = uint8(uint64(num)&0x7f | 0x80) num >>= 7 - j135++ + j136++ } - dAtA136[j135] = uint8(num) - j135++ + dAtA137[j136] = uint8(num) + j136++ } - i -= j135 - copy(dAtA[i:], dAtA136[:j135]) - i = encodeVarintPlan(dAtA, i, uint64(j135)) + i -= j136 + copy(dAtA[i:], dAtA137[:j136]) + i = encodeVarintPlan(dAtA, i, uint64(j136)) i-- dAtA[i] = 0x22 } @@ -24948,21 +25625,21 @@ func (m *IcebergScan) MarshalToSizedBuffer(dAtA []byte) (int, error) { dAtA[i] = 0x52 } if len(m.ProjectedFieldIds) > 0 { - dAtA147 := make([]byte, len(m.ProjectedFieldIds)*10) - var j146 int + dAtA148 := make([]byte, len(m.ProjectedFieldIds)*10) + var j147 int for _, num1 := range m.ProjectedFieldIds { num := uint64(num1) for num >= 1<<7 { - dAtA147[j146] = uint8(uint64(num)&0x7f | 0x80) + dAtA148[j147] = uint8(uint64(num)&0x7f | 0x80) num >>= 7 - j146++ + j147++ } - dAtA147[j146] = uint8(num) - j146++ + dAtA148[j147] = uint8(num) + j147++ } - i -= j146 - copy(dAtA[i:], dAtA147[:j146]) - i = encodeVarintPlan(dAtA, i, uint64(j146)) + i -= j147 + copy(dAtA[i:], dAtA148[:j147]) + i = encodeVarintPlan(dAtA, i, uint64(j147)) i-- dAtA[i] = 0x4a } @@ -25621,21 +26298,21 @@ func (m *PreInsertUkCtx) MarshalToSizedBuffer(dAtA []byte) (int, error) { copy(dAtA[i:], m.XXX_unrecognized) } if len(m.KeyTypeCounts) > 0 { - dAtA155 := make([]byte, len(m.KeyTypeCounts)*10) - var j154 int + dAtA156 := make([]byte, len(m.KeyTypeCounts)*10) + var j155 int for _, num1 := range m.KeyTypeCounts { num := uint64(num1) for num >= 1<<7 { - dAtA155[j154] = uint8(uint64(num)&0x7f | 0x80) + dAtA156[j155] = uint8(uint64(num)&0x7f | 0x80) num >>= 7 - j154++ + j155++ } - dAtA155[j154] = uint8(num) - j154++ + dAtA156[j155] = uint8(num) + j155++ } - i -= j154 - copy(dAtA[i:], dAtA155[:j154]) - i = encodeVarintPlan(dAtA, i, uint64(j154)) + i -= j155 + copy(dAtA[i:], dAtA156[:j155]) + i = encodeVarintPlan(dAtA, i, uint64(j155)) i-- dAtA[i] = 0x1 i-- @@ -25699,21 +26376,21 @@ func (m *PreInsertUkCtx) MarshalToSizedBuffer(dAtA []byte) (int, error) { dAtA[i] = 0x58 } if len(m.TargetColumns) > 0 { - dAtA157 := make([]byte, len(m.TargetColumns)*10) - var j156 int + dAtA158 := make([]byte, len(m.TargetColumns)*10) + var j157 int for _, num1 := range m.TargetColumns { num := uint64(num1) for num >= 1<<7 { - dAtA157[j156] = uint8(uint64(num)&0x7f | 0x80) + dAtA158[j157] = uint8(uint64(num)&0x7f | 0x80) num >>= 7 - j156++ + j157++ } - dAtA157[j156] = uint8(num) - j156++ + dAtA158[j157] = uint8(num) + j157++ } - i -= j156 - copy(dAtA[i:], dAtA157[:j156]) - i = encodeVarintPlan(dAtA, i, uint64(j156)) + i -= j157 + copy(dAtA[i:], dAtA158[:j157]) + i = encodeVarintPlan(dAtA, i, uint64(j157)) i-- dAtA[i] = 0x52 } @@ -25733,40 +26410,40 @@ func (m *PreInsertUkCtx) MarshalToSizedBuffer(dAtA []byte) (int, error) { dAtA[i] = 0x40 } if len(m.ConflictColumns) > 0 { - dAtA159 := make([]byte, len(m.ConflictColumns)*10) - var j158 int + dAtA160 := make([]byte, len(m.ConflictColumns)*10) + var j159 int for _, num1 := range m.ConflictColumns { num := uint64(num1) for num >= 1<<7 { - dAtA159[j158] = uint8(uint64(num)&0x7f | 0x80) + dAtA160[j159] = uint8(uint64(num)&0x7f | 0x80) num >>= 7 - j158++ + j159++ } - dAtA159[j158] = uint8(num) - j158++ + dAtA160[j159] = uint8(num) + j159++ } - i -= j158 - copy(dAtA[i:], dAtA159[:j158]) - i = encodeVarintPlan(dAtA, i, uint64(j158)) + i -= j159 + copy(dAtA[i:], dAtA160[:j159]) + i = encodeVarintPlan(dAtA, i, uint64(j159)) i-- dAtA[i] = 0x3a } if len(m.KeyColumns) > 0 { - dAtA161 := make([]byte, len(m.KeyColumns)*10) - var j160 int + dAtA162 := make([]byte, len(m.KeyColumns)*10) + var j161 int for _, num1 := range m.KeyColumns { num := uint64(num1) for num >= 1<<7 { - dAtA161[j160] = uint8(uint64(num)&0x7f | 0x80) + dAtA162[j161] = uint8(uint64(num)&0x7f | 0x80) num >>= 7 - j160++ + j161++ } - dAtA161[j160] = uint8(num) - j160++ + dAtA162[j161] = uint8(num) + j161++ } - i -= j160 - copy(dAtA[i:], dAtA161[:j160]) - i = encodeVarintPlan(dAtA, i, uint64(j160)) + i -= j161 + copy(dAtA[i:], dAtA162[:j161]) + i = encodeVarintPlan(dAtA, i, uint64(j161)) i-- dAtA[i] = 0x32 } @@ -25806,21 +26483,21 @@ func (m *PreInsertUkCtx) MarshalToSizedBuffer(dAtA []byte) (int, error) { dAtA[i] = 0x10 } if len(m.Columns) > 0 { - dAtA165 := make([]byte, len(m.Columns)*10) - var j164 int + dAtA166 := make([]byte, len(m.Columns)*10) + var j165 int for _, num1 := range m.Columns { num := uint64(num1) for num >= 1<<7 { - dAtA165[j164] = uint8(uint64(num)&0x7f | 0x80) + dAtA166[j165] = uint8(uint64(num)&0x7f | 0x80) num >>= 7 - j164++ + j165++ } - dAtA165[j164] = uint8(num) - j164++ + dAtA166[j165] = uint8(num) + j165++ } - i -= j164 - copy(dAtA[i:], dAtA165[:j164]) - i = encodeVarintPlan(dAtA, i, uint64(j164)) + i -= j165 + copy(dAtA[i:], dAtA166[:j165]) + i = encodeVarintPlan(dAtA, i, uint64(j165)) i-- dAtA[i] = 0xa } @@ -26144,21 +26821,21 @@ func (m *IdList) MarshalToSizedBuffer(dAtA []byte) (int, error) { copy(dAtA[i:], m.XXX_unrecognized) } if len(m.List) > 0 { - dAtA174 := make([]byte, len(m.List)*10) - var j173 int + dAtA175 := make([]byte, len(m.List)*10) + var j174 int for _, num1 := range m.List { num := uint64(num1) for num >= 1<<7 { - dAtA174[j173] = uint8(uint64(num)&0x7f | 0x80) + dAtA175[j174] = uint8(uint64(num)&0x7f | 0x80) num >>= 7 - j173++ + j174++ } - dAtA174[j173] = uint8(num) - j173++ + dAtA175[j174] = uint8(num) + j174++ } - i -= j173 - copy(dAtA[i:], dAtA174[:j173]) - i = encodeVarintPlan(dAtA, i, uint64(j173)) + i -= j174 + copy(dAtA[i:], dAtA175[:j174]) + i = encodeVarintPlan(dAtA, i, uint64(j174)) i-- dAtA[i] = 0xa } @@ -26496,6 +27173,22 @@ func (m *Query) MarshalToSizedBuffer(dAtA []byte) (int, error) { i -= len(m.XXX_unrecognized) copy(dAtA[i:], m.XXX_unrecognized) } + if len(m.RoutineDependencies) > 0 { + for iNdEx := len(m.RoutineDependencies) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.RoutineDependencies[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintPlan(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x1 + i-- + dAtA[i] = 0x8a + } + } if len(m.UnresolvedIndexHints) > 0 { for iNdEx := len(m.UnresolvedIndexHints) - 1; iNdEx >= 0; iNdEx-- { { @@ -26647,21 +27340,21 @@ func (m *Query) MarshalToSizedBuffer(dAtA []byte) (int, error) { } } if len(m.Steps) > 0 { - dAtA181 := make([]byte, len(m.Steps)*10) - var j180 int + dAtA182 := make([]byte, len(m.Steps)*10) + var j181 int for _, num1 := range m.Steps { num := uint64(num1) for num >= 1<<7 { - dAtA181[j180] = uint8(uint64(num)&0x7f | 0x80) + dAtA182[j181] = uint8(uint64(num)&0x7f | 0x80) num >>= 7 - j180++ + j181++ } - dAtA181[j180] = uint8(num) - j180++ + dAtA182[j181] = uint8(num) + j181++ } - i -= j180 - copy(dAtA[i:], dAtA181[:j180]) - i = encodeVarintPlan(dAtA, i, uint64(j180)) + i -= j181 + copy(dAtA[i:], dAtA182[:j181]) + i = encodeVarintPlan(dAtA, i, uint64(j181)) i-- dAtA[i] = 0x12 } @@ -29677,20 +30370,20 @@ func (m *DropTable) MarshalToSizedBuffer(dAtA []byte) (int, error) { } } if len(m.FkChildTblsReferToMe) > 0 { - dAtA247 := make([]byte, len(m.FkChildTblsReferToMe)*10) - var j246 int + dAtA248 := make([]byte, len(m.FkChildTblsReferToMe)*10) + var j247 int for _, num := range m.FkChildTblsReferToMe { for num >= 1<<7 { - dAtA247[j246] = uint8(uint64(num)&0x7f | 0x80) + dAtA248[j247] = uint8(uint64(num)&0x7f | 0x80) num >>= 7 - j246++ + j247++ } - dAtA247[j246] = uint8(num) - j246++ + dAtA248[j247] = uint8(num) + j247++ } - i -= j246 - copy(dAtA[i:], dAtA247[:j246]) - i = encodeVarintPlan(dAtA, i, uint64(j246)) + i -= j247 + copy(dAtA[i:], dAtA248[:j247]) + i = encodeVarintPlan(dAtA, i, uint64(j247)) i-- dAtA[i] = 0x62 } @@ -29726,20 +30419,20 @@ func (m *DropTable) MarshalToSizedBuffer(dAtA []byte) (int, error) { dAtA[i] = 0x48 } if len(m.ForeignTbl) > 0 { - dAtA250 := make([]byte, len(m.ForeignTbl)*10) - var j249 int + dAtA251 := make([]byte, len(m.ForeignTbl)*10) + var j250 int for _, num := range m.ForeignTbl { for num >= 1<<7 { - dAtA250[j249] = uint8(uint64(num)&0x7f | 0x80) + dAtA251[j250] = uint8(uint64(num)&0x7f | 0x80) num >>= 7 - j249++ + j250++ } - dAtA250[j249] = uint8(num) - j249++ + dAtA251[j250] = uint8(num) + j250++ } - i -= j249 - copy(dAtA[i:], dAtA250[:j249]) - i = encodeVarintPlan(dAtA, i, uint64(j249)) + i -= j250 + copy(dAtA[i:], dAtA251[:j250]) + i = encodeVarintPlan(dAtA, i, uint64(j250)) i-- dAtA[i] = 0x3a } @@ -30285,20 +30978,20 @@ func (m *TruncateTable) MarshalToSizedBuffer(dAtA []byte) (int, error) { dAtA[i] = 0x40 } if len(m.ForeignTbl) > 0 { - dAtA259 := make([]byte, len(m.ForeignTbl)*10) - var j258 int + dAtA260 := make([]byte, len(m.ForeignTbl)*10) + var j259 int for _, num := range m.ForeignTbl { for num >= 1<<7 { - dAtA259[j258] = uint8(uint64(num)&0x7f | 0x80) + dAtA260[j259] = uint8(uint64(num)&0x7f | 0x80) num >>= 7 - j258++ + j259++ } - dAtA259[j258] = uint8(num) - j258++ + dAtA260[j259] = uint8(num) + j259++ } - i -= j258 - copy(dAtA[i:], dAtA259[:j258]) - i = encodeVarintPlan(dAtA, i, uint64(j258)) + i -= j259 + copy(dAtA[i:], dAtA260[:j259]) + i = encodeVarintPlan(dAtA, i, uint64(j259)) i-- dAtA[i] = 0x3a } @@ -30375,20 +31068,20 @@ func (m *ClusterTable) MarshalToSizedBuffer(dAtA []byte) (int, error) { dAtA[i] = 0x18 } if len(m.AccountIDs) > 0 { - dAtA262 := make([]byte, len(m.AccountIDs)*10) - var j261 int + dAtA263 := make([]byte, len(m.AccountIDs)*10) + var j262 int for _, num := range m.AccountIDs { for num >= 1<<7 { - dAtA262[j261] = uint8(uint64(num)&0x7f | 0x80) + dAtA263[j262] = uint8(uint64(num)&0x7f | 0x80) num >>= 7 - j261++ + j262++ } - dAtA262[j261] = uint8(num) - j261++ + dAtA263[j262] = uint8(num) + j262++ } - i -= j261 - copy(dAtA[i:], dAtA262[:j261]) - i = encodeVarintPlan(dAtA, i, uint64(j261)) + i -= j262 + copy(dAtA[i:], dAtA263[:j262]) + i = encodeVarintPlan(dAtA, i, uint64(j262)) i-- dAtA[i] = 0x12 } @@ -30600,21 +31293,21 @@ func (m *Prepare) MarshalToSizedBuffer(dAtA []byte) (int, error) { copy(dAtA[i:], m.XXX_unrecognized) } if len(m.ParamTypes) > 0 { - dAtA266 := make([]byte, len(m.ParamTypes)*10) - var j265 int + dAtA267 := make([]byte, len(m.ParamTypes)*10) + var j266 int for _, num1 := range m.ParamTypes { num := uint64(num1) for num >= 1<<7 { - dAtA266[j265] = uint8(uint64(num)&0x7f | 0x80) + dAtA267[j266] = uint8(uint64(num)&0x7f | 0x80) num >>= 7 - j265++ + j266++ } - dAtA266[j265] = uint8(num) - j265++ + dAtA267[j266] = uint8(num) + j266++ } - i -= j265 - copy(dAtA[i:], dAtA266[:j265]) - i = encodeVarintPlan(dAtA, i, uint64(j265)) + i -= j266 + copy(dAtA[i:], dAtA267[:j266]) + i = encodeVarintPlan(dAtA, i, uint64(j266)) i-- dAtA[i] = 0x22 } @@ -30761,21 +31454,21 @@ func (m *OtherDCL) MarshalToSizedBuffer(dAtA []byte) (int, error) { copy(dAtA[i:], m.XXX_unrecognized) } if len(m.ParamTypes) > 0 { - dAtA269 := make([]byte, len(m.ParamTypes)*10) - var j268 int + dAtA270 := make([]byte, len(m.ParamTypes)*10) + var j269 int for _, num1 := range m.ParamTypes { num := uint64(num1) for num >= 1<<7 { - dAtA269[j268] = uint8(uint64(num)&0x7f | 0x80) + dAtA270[j269] = uint8(uint64(num)&0x7f | 0x80) num >>= 7 - j268++ + j269++ } - dAtA269[j268] = uint8(num) - j268++ + dAtA270[j269] = uint8(num) + j269++ } - i -= j268 - copy(dAtA[i:], dAtA269[:j268]) - i = encodeVarintPlan(dAtA, i, uint64(j268)) + i -= j269 + copy(dAtA[i:], dAtA270[:j269]) + i = encodeVarintPlan(dAtA, i, uint64(j269)) i-- dAtA[i] = 0xa } @@ -31599,21 +32292,21 @@ func (m *ODKUForeignKeyCheck) MarshalToSizedBuffer(dAtA []byte) (int, error) { dAtA[i] = 0x12 } if len(m.ColIdxList) > 0 { - dAtA277 := make([]byte, len(m.ColIdxList)*10) - var j276 int + dAtA278 := make([]byte, len(m.ColIdxList)*10) + var j277 int for _, num1 := range m.ColIdxList { num := uint64(num1) for num >= 1<<7 { - dAtA277[j276] = uint8(uint64(num)&0x7f | 0x80) + dAtA278[j277] = uint8(uint64(num)&0x7f | 0x80) num >>= 7 - j276++ + j277++ } - dAtA277[j276] = uint8(num) - j276++ + dAtA278[j277] = uint8(num) + j277++ } - i -= j276 - copy(dAtA[i:], dAtA277[:j276]) - i = encodeVarintPlan(dAtA, i, uint64(j276)) + i -= j277 + copy(dAtA[i:], dAtA278[:j277]) + i = encodeVarintPlan(dAtA, i, uint64(j277)) i-- dAtA[i] = 0xa } @@ -31715,6 +32408,505 @@ func (m *UnresolvedIndexHint) MarshalToSizedBuffer(dAtA []byte) (int, error) { return len(dAtA) - i, nil } +func (m *FunctionRef) Marshal() (dAtA []byte, err error) { + size := m.ProtoSize() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *FunctionRef) MarshalTo(dAtA []byte) (int, error) { + size := m.ProtoSize() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *FunctionRef) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.XXX_unrecognized != nil { + i -= len(m.XXX_unrecognized) + copy(dAtA[i:], m.XXX_unrecognized) + } + if m.DatabaseId != 0 { + i = encodeVarintPlan(dAtA, i, uint64(m.DatabaseId)) + i-- + dAtA[i] = 0x28 + } + if m.AccountId != 0 { + i = encodeVarintPlan(dAtA, i, uint64(m.AccountId)) + i-- + dAtA[i] = 0x20 + } + if m.NamespaceVersion != 0 { + i = encodeVarintPlan(dAtA, i, uint64(m.NamespaceVersion)) + i-- + dAtA[i] = 0x18 + } + if m.Revision != 0 { + i = encodeVarintPlan(dAtA, i, uint64(m.Revision)) + i-- + dAtA[i] = 0x10 + } + if m.FunctionId != 0 { + i = encodeVarintPlan(dAtA, i, uint64(m.FunctionId)) + i-- + dAtA[i] = 0x8 + } + return len(dAtA) - i, nil +} + +func (m *SqlRoutineImplementation) Marshal() (dAtA []byte, err error) { + size := m.ProtoSize() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *SqlRoutineImplementation) MarshalTo(dAtA []byte) (int, error) { + size := m.ProtoSize() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *SqlRoutineImplementation) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.XXX_unrecognized != nil { + i -= len(m.XXX_unrecognized) + copy(dAtA[i:], m.XXX_unrecognized) + } + if m.SemanticDefinitionSchemaVersion != 0 { + i = encodeVarintPlan(dAtA, i, uint64(m.SemanticDefinitionSchemaVersion)) + i-- + dAtA[i] = 0x10 + } + if len(m.DefinitionFingerprint) > 0 { + i -= len(m.DefinitionFingerprint) + copy(dAtA[i:], m.DefinitionFingerprint) + i = encodeVarintPlan(dAtA, i, uint64(len(m.DefinitionFingerprint))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *PythonRoutineImplementation) Marshal() (dAtA []byte, err error) { + size := m.ProtoSize() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *PythonRoutineImplementation) MarshalTo(dAtA []byte) (int, error) { + size := m.ProtoSize() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *PythonRoutineImplementation) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.XXX_unrecognized != nil { + i -= len(m.XXX_unrecognized) + copy(dAtA[i:], m.XXX_unrecognized) + } + if len(m.DefinitionFingerprint) > 0 { + i -= len(m.DefinitionFingerprint) + copy(dAtA[i:], m.DefinitionFingerprint) + i = encodeVarintPlan(dAtA, i, uint64(len(m.DefinitionFingerprint))) + i-- + dAtA[i] = 0x5a + } + if len(m.NullPolicy) > 0 { + i -= len(m.NullPolicy) + copy(dAtA[i:], m.NullPolicy) + i = encodeVarintPlan(dAtA, i, uint64(len(m.NullPolicy))) + i-- + dAtA[i] = 0x52 + } + if len(m.Mode) > 0 { + i -= len(m.Mode) + copy(dAtA[i:], m.Mode) + i = encodeVarintPlan(dAtA, i, uint64(len(m.Mode))) + i-- + dAtA[i] = 0x4a + } + if m.DefinitionSchemaVersion != 0 { + i = encodeVarintPlan(dAtA, i, uint64(m.DefinitionSchemaVersion)) + i-- + dAtA[i] = 0x40 + } + if len(m.SdkVersion) > 0 { + i -= len(m.SdkVersion) + copy(dAtA[i:], m.SdkVersion) + i = encodeVarintPlan(dAtA, i, uint64(len(m.SdkVersion))) + i-- + dAtA[i] = 0x3a + } + if len(m.EnvironmentDigest) > 0 { + i -= len(m.EnvironmentDigest) + copy(dAtA[i:], m.EnvironmentDigest) + i = encodeVarintPlan(dAtA, i, uint64(len(m.EnvironmentDigest))) + i-- + dAtA[i] = 0x32 + } + if len(m.ArtifactDigest) > 0 { + i -= len(m.ArtifactDigest) + copy(dAtA[i:], m.ArtifactDigest) + i = encodeVarintPlan(dAtA, i, uint64(len(m.ArtifactDigest))) + i-- + dAtA[i] = 0x2a + } + if len(m.AdapterVersion) > 0 { + i -= len(m.AdapterVersion) + copy(dAtA[i:], m.AdapterVersion) + i = encodeVarintPlan(dAtA, i, uint64(len(m.AdapterVersion))) + i-- + dAtA[i] = 0x22 + } + if len(m.AbiContract) > 0 { + i -= len(m.AbiContract) + copy(dAtA[i:], m.AbiContract) + i = encodeVarintPlan(dAtA, i, uint64(len(m.AbiContract))) + i-- + dAtA[i] = 0x1a + } + if len(m.Source) > 0 { + i -= len(m.Source) + copy(dAtA[i:], m.Source) + i = encodeVarintPlan(dAtA, i, uint64(len(m.Source))) + i-- + dAtA[i] = 0x12 + } + if len(m.Handler) > 0 { + i -= len(m.Handler) + copy(dAtA[i:], m.Handler) + i = encodeVarintPlan(dAtA, i, uint64(len(m.Handler))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *RoutineCall) Marshal() (dAtA []byte, err error) { + size := m.ProtoSize() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *RoutineCall) MarshalTo(dAtA []byte) (int, error) { + size := m.ProtoSize() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *RoutineCall) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.XXX_unrecognized != nil { + i -= len(m.XXX_unrecognized) + copy(dAtA[i:], m.XXX_unrecognized) + } + if m.Leakproof { + i-- + if m.Leakproof { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i-- + dAtA[i] = 0x70 + } + if len(m.SecurityMode) > 0 { + i -= len(m.SecurityMode) + copy(dAtA[i:], m.SecurityMode) + i = encodeVarintPlan(dAtA, i, uint64(len(m.SecurityMode))) + i-- + dAtA[i] = 0x6a + } + if m.MayError { + i-- + if m.MayError { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i-- + dAtA[i] = 0x60 + } + if len(m.CallsiteId) > 0 { + i -= len(m.CallsiteId) + copy(dAtA[i:], m.CallsiteId) + i = encodeVarintPlan(dAtA, i, uint64(len(m.CallsiteId))) + i-- + dAtA[i] = 0x5a + } + if len(m.Context) > 0 { + for k := range m.Context { + v := m.Context[k] + baseI := i + i -= len(v) + copy(dAtA[i:], v) + i = encodeVarintPlan(dAtA, i, uint64(len(v))) + i-- + dAtA[i] = 0x12 + i -= len(k) + copy(dAtA[i:], k) + i = encodeVarintPlan(dAtA, i, uint64(len(k))) + i-- + dAtA[i] = 0xa + i = encodeVarintPlan(dAtA, i, uint64(baseI-i)) + i-- + dAtA[i] = 0x52 + } + } + if m.Implementation != nil { + { + size := m.Implementation.ProtoSize() + i -= size + if _, err := m.Implementation.MarshalTo(dAtA[i:]); err != nil { + return 0, err + } + } + } + if len(m.NullPolicy) > 0 { + i -= len(m.NullPolicy) + copy(dAtA[i:], m.NullPolicy) + i = encodeVarintPlan(dAtA, i, uint64(len(m.NullPolicy))) + i-- + dAtA[i] = 0x3a + } + if len(m.Volatility) > 0 { + i -= len(m.Volatility) + copy(dAtA[i:], m.Volatility) + i = encodeVarintPlan(dAtA, i, uint64(len(m.Volatility))) + i-- + dAtA[i] = 0x32 + } + { + size, err := m.ReturnType.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintPlan(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x2a + if len(m.ArgumentTypes) > 0 { + for iNdEx := len(m.ArgumentTypes) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.ArgumentTypes[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintPlan(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x22 + } + } + if len(m.Language) > 0 { + i -= len(m.Language) + copy(dAtA[i:], m.Language) + i = encodeVarintPlan(dAtA, i, uint64(len(m.Language))) + i-- + dAtA[i] = 0x1a + } + if m.FunctionRef != nil { + { + size, err := m.FunctionRef.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintPlan(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + } + if m.ContractVersion != 0 { + i = encodeVarintPlan(dAtA, i, uint64(m.ContractVersion)) + i-- + dAtA[i] = 0x8 + } + return len(dAtA) - i, nil +} + +func (m *RoutineCall_Sql) MarshalTo(dAtA []byte) (int, error) { + size := m.ProtoSize() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *RoutineCall_Sql) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + if m.Sql != nil { + { + size, err := m.Sql.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintPlan(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x42 + } + return len(dAtA) - i, nil +} +func (m *RoutineCall_Python) MarshalTo(dAtA []byte) (int, error) { + size := m.ProtoSize() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *RoutineCall_Python) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + if m.Python != nil { + { + size, err := m.Python.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintPlan(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x4a + } + return len(dAtA) - i, nil +} +func (m *RoutinePlanDependency) Marshal() (dAtA []byte, err error) { + size := m.ProtoSize() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *RoutinePlanDependency) MarshalTo(dAtA []byte) (int, error) { + size := m.ProtoSize() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *RoutinePlanDependency) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.XXX_unrecognized != nil { + i -= len(m.XXX_unrecognized) + copy(dAtA[i:], m.XXX_unrecognized) + } + if m.Leakproof { + i-- + if m.Leakproof { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i-- + dAtA[i] = 0x58 + } + if len(m.SecurityMode) > 0 { + i -= len(m.SecurityMode) + copy(dAtA[i:], m.SecurityMode) + i = encodeVarintPlan(dAtA, i, uint64(len(m.SecurityMode))) + i-- + dAtA[i] = 0x52 + } + if m.MayError { + i-- + if m.MayError { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i-- + dAtA[i] = 0x48 + } + if len(m.NullPolicy) > 0 { + i -= len(m.NullPolicy) + copy(dAtA[i:], m.NullPolicy) + i = encodeVarintPlan(dAtA, i, uint64(len(m.NullPolicy))) + i-- + dAtA[i] = 0x42 + } + if len(m.Volatility) > 0 { + i -= len(m.Volatility) + copy(dAtA[i:], m.Volatility) + i = encodeVarintPlan(dAtA, i, uint64(len(m.Volatility))) + i-- + dAtA[i] = 0x3a + } + if len(m.EnvironmentDigest) > 0 { + i -= len(m.EnvironmentDigest) + copy(dAtA[i:], m.EnvironmentDigest) + i = encodeVarintPlan(dAtA, i, uint64(len(m.EnvironmentDigest))) + i-- + dAtA[i] = 0x32 + } + if len(m.ArtifactDigest) > 0 { + i -= len(m.ArtifactDigest) + copy(dAtA[i:], m.ArtifactDigest) + i = encodeVarintPlan(dAtA, i, uint64(len(m.ArtifactDigest))) + i-- + dAtA[i] = 0x2a + } + if len(m.DefinitionFingerprint) > 0 { + i -= len(m.DefinitionFingerprint) + copy(dAtA[i:], m.DefinitionFingerprint) + i = encodeVarintPlan(dAtA, i, uint64(len(m.DefinitionFingerprint))) + i-- + dAtA[i] = 0x22 + } + if m.ContractVersion != 0 { + i = encodeVarintPlan(dAtA, i, uint64(m.ContractVersion)) + i-- + dAtA[i] = 0x18 + } + if len(m.Language) > 0 { + i -= len(m.Language) + copy(dAtA[i:], m.Language) + i = encodeVarintPlan(dAtA, i, uint64(len(m.Language))) + i-- + dAtA[i] = 0x12 + } + if m.FunctionRef != nil { + { + size, err := m.FunctionRef.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintPlan(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + func encodeVarintPlan(dAtA []byte, offset int, v uint64) int { offset -= sovPlan(v) base := offset @@ -32386,6 +33578,10 @@ func (m *Function) ProtoSize() (n int) { if m.SyntaxExplicitCast { n += 2 } + if m.RoutineCall != nil { + l = m.RoutineCall.ProtoSize() + n += 1 + l + sovPlan(uint64(l)) + } if m.XXX_unrecognized != nil { n += len(m.XXX_unrecognized) } @@ -35619,6 +36815,12 @@ func (m *Query) ProtoSize() (n int) { n += 2 + l + sovPlan(uint64(l)) } } + if len(m.RoutineDependencies) > 0 { + for _, e := range m.RoutineDependencies { + l = e.ProtoSize() + n += 2 + l + sovPlan(uint64(l)) + } + } if m.XXX_unrecognized != nil { n += len(m.XXX_unrecognized) } @@ -38014,6 +39216,248 @@ func (m *UnresolvedIndexHint) ProtoSize() (n int) { return n } +func (m *FunctionRef) ProtoSize() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.FunctionId != 0 { + n += 1 + sovPlan(uint64(m.FunctionId)) + } + if m.Revision != 0 { + n += 1 + sovPlan(uint64(m.Revision)) + } + if m.NamespaceVersion != 0 { + n += 1 + sovPlan(uint64(m.NamespaceVersion)) + } + if m.AccountId != 0 { + n += 1 + sovPlan(uint64(m.AccountId)) + } + if m.DatabaseId != 0 { + n += 1 + sovPlan(uint64(m.DatabaseId)) + } + if m.XXX_unrecognized != nil { + n += len(m.XXX_unrecognized) + } + return n +} + +func (m *SqlRoutineImplementation) ProtoSize() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.DefinitionFingerprint) + if l > 0 { + n += 1 + l + sovPlan(uint64(l)) + } + if m.SemanticDefinitionSchemaVersion != 0 { + n += 1 + sovPlan(uint64(m.SemanticDefinitionSchemaVersion)) + } + if m.XXX_unrecognized != nil { + n += len(m.XXX_unrecognized) + } + return n +} + +func (m *PythonRoutineImplementation) ProtoSize() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Handler) + if l > 0 { + n += 1 + l + sovPlan(uint64(l)) + } + l = len(m.Source) + if l > 0 { + n += 1 + l + sovPlan(uint64(l)) + } + l = len(m.AbiContract) + if l > 0 { + n += 1 + l + sovPlan(uint64(l)) + } + l = len(m.AdapterVersion) + if l > 0 { + n += 1 + l + sovPlan(uint64(l)) + } + l = len(m.ArtifactDigest) + if l > 0 { + n += 1 + l + sovPlan(uint64(l)) + } + l = len(m.EnvironmentDigest) + if l > 0 { + n += 1 + l + sovPlan(uint64(l)) + } + l = len(m.SdkVersion) + if l > 0 { + n += 1 + l + sovPlan(uint64(l)) + } + if m.DefinitionSchemaVersion != 0 { + n += 1 + sovPlan(uint64(m.DefinitionSchemaVersion)) + } + l = len(m.Mode) + if l > 0 { + n += 1 + l + sovPlan(uint64(l)) + } + l = len(m.NullPolicy) + if l > 0 { + n += 1 + l + sovPlan(uint64(l)) + } + l = len(m.DefinitionFingerprint) + if l > 0 { + n += 1 + l + sovPlan(uint64(l)) + } + if m.XXX_unrecognized != nil { + n += len(m.XXX_unrecognized) + } + return n +} + +func (m *RoutineCall) ProtoSize() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.ContractVersion != 0 { + n += 1 + sovPlan(uint64(m.ContractVersion)) + } + if m.FunctionRef != nil { + l = m.FunctionRef.ProtoSize() + n += 1 + l + sovPlan(uint64(l)) + } + l = len(m.Language) + if l > 0 { + n += 1 + l + sovPlan(uint64(l)) + } + if len(m.ArgumentTypes) > 0 { + for _, e := range m.ArgumentTypes { + l = e.ProtoSize() + n += 1 + l + sovPlan(uint64(l)) + } + } + l = m.ReturnType.ProtoSize() + n += 1 + l + sovPlan(uint64(l)) + l = len(m.Volatility) + if l > 0 { + n += 1 + l + sovPlan(uint64(l)) + } + l = len(m.NullPolicy) + if l > 0 { + n += 1 + l + sovPlan(uint64(l)) + } + if m.Implementation != nil { + n += m.Implementation.ProtoSize() + } + if len(m.Context) > 0 { + for k, v := range m.Context { + _ = k + _ = v + mapEntrySize := 1 + len(k) + sovPlan(uint64(len(k))) + 1 + len(v) + sovPlan(uint64(len(v))) + n += mapEntrySize + 1 + sovPlan(uint64(mapEntrySize)) + } + } + l = len(m.CallsiteId) + if l > 0 { + n += 1 + l + sovPlan(uint64(l)) + } + if m.MayError { + n += 2 + } + l = len(m.SecurityMode) + if l > 0 { + n += 1 + l + sovPlan(uint64(l)) + } + if m.Leakproof { + n += 2 + } + if m.XXX_unrecognized != nil { + n += len(m.XXX_unrecognized) + } + return n +} + +func (m *RoutineCall_Sql) ProtoSize() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.Sql != nil { + l = m.Sql.ProtoSize() + n += 1 + l + sovPlan(uint64(l)) + } + return n +} +func (m *RoutineCall_Python) ProtoSize() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.Python != nil { + l = m.Python.ProtoSize() + n += 1 + l + sovPlan(uint64(l)) + } + return n +} +func (m *RoutinePlanDependency) ProtoSize() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.FunctionRef != nil { + l = m.FunctionRef.ProtoSize() + n += 1 + l + sovPlan(uint64(l)) + } + l = len(m.Language) + if l > 0 { + n += 1 + l + sovPlan(uint64(l)) + } + if m.ContractVersion != 0 { + n += 1 + sovPlan(uint64(m.ContractVersion)) + } + l = len(m.DefinitionFingerprint) + if l > 0 { + n += 1 + l + sovPlan(uint64(l)) + } + l = len(m.ArtifactDigest) + if l > 0 { + n += 1 + l + sovPlan(uint64(l)) + } + l = len(m.EnvironmentDigest) + if l > 0 { + n += 1 + l + sovPlan(uint64(l)) + } + l = len(m.Volatility) + if l > 0 { + n += 1 + l + sovPlan(uint64(l)) + } + l = len(m.NullPolicy) + if l > 0 { + n += 1 + l + sovPlan(uint64(l)) + } + if m.MayError { + n += 2 + } + l = len(m.SecurityMode) + if l > 0 { + n += 1 + l + sovPlan(uint64(l)) + } + if m.Leakproof { + n += 2 + } + if m.XXX_unrecognized != nil { + n += len(m.XXX_unrecognized) + } + return n +} + func sovPlan(x uint64) (n int) { return (math_bits.Len64(x|1) + 6) / 7 } @@ -41116,6 +42560,42 @@ func (m *Function) Unmarshal(dAtA []byte) error { } } m.SyntaxExplicitCast = bool(v != 0) + case 6: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field RoutineCall", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowPlan + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthPlan + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthPlan + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.RoutineCall == nil { + m.RoutineCall = &RoutineCall{} + } + if err := m.RoutineCall.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipPlan(dAtA[iNdEx:]) @@ -61796,79 +63276,113 @@ func (m *Query) Unmarshal(dAtA []byte) error { return err } iNdEx = postIndex - default: - iNdEx = preIndex - skippy, err := skipPlan(dAtA[iNdEx:]) - if err != nil { - return err - } - if (skippy < 0) || (iNdEx+skippy) < 0 { - return ErrInvalidLengthPlan - } - if (iNdEx + skippy) > l { - return io.ErrUnexpectedEOF - } - m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) - iNdEx += skippy - } - } - - if iNdEx > l { - return io.ErrUnexpectedEOF - } - return nil -} -func (m *TransationControl) Unmarshal(dAtA []byte) error { - l := len(dAtA) - iNdEx := 0 - for iNdEx < l { - preIndex := iNdEx - var wire uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowPlan - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - wire |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - fieldNum := int32(wire >> 3) - wireType := int(wire & 0x7) - if wireType == 4 { - return fmt.Errorf("proto: TransationControl: wiretype end group for non-group") - } - if fieldNum <= 0 { - return fmt.Errorf("proto: TransationControl: illegal tag %d (wire type %d)", fieldNum, wire) - } - switch fieldNum { - case 1: - if wireType != 0 { - return fmt.Errorf("proto: wrong wireType = %d for field TclType", wireType) - } - m.TclType = 0 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowPlan - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - m.TclType |= TransationControl_TclType(b&0x7F) << shift - if b < 0x80 { - break - } - } - case 2: + case 17: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Begin", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field RoutineDependencies", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowPlan + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthPlan + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthPlan + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.RoutineDependencies = append(m.RoutineDependencies, &RoutinePlanDependency{}) + if err := m.RoutineDependencies[len(m.RoutineDependencies)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipPlan(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthPlan + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *TransationControl) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowPlan + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: TransationControl: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: TransationControl: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field TclType", wireType) + } + m.TclType = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowPlan + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.TclType |= TransationControl_TclType(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Begin", wireType) } var msglen int for shift := uint(0); ; shift += 7 { @@ -74439,6 +75953,1586 @@ func (m *UnresolvedIndexHint) Unmarshal(dAtA []byte) error { } return nil } +func (m *FunctionRef) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowPlan + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: FunctionRef: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: FunctionRef: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field FunctionId", wireType) + } + m.FunctionId = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowPlan + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.FunctionId |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 2: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Revision", wireType) + } + m.Revision = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowPlan + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Revision |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 3: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field NamespaceVersion", wireType) + } + m.NamespaceVersion = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowPlan + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.NamespaceVersion |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 4: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field AccountId", wireType) + } + m.AccountId = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowPlan + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.AccountId |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 5: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field DatabaseId", wireType) + } + m.DatabaseId = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowPlan + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.DatabaseId |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + default: + iNdEx = preIndex + skippy, err := skipPlan(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthPlan + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *SqlRoutineImplementation) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowPlan + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: SqlRoutineImplementation: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: SqlRoutineImplementation: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field DefinitionFingerprint", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowPlan + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthPlan + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthPlan + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.DefinitionFingerprint = append(m.DefinitionFingerprint[:0], dAtA[iNdEx:postIndex]...) + if m.DefinitionFingerprint == nil { + m.DefinitionFingerprint = []byte{} + } + iNdEx = postIndex + case 2: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field SemanticDefinitionSchemaVersion", wireType) + } + m.SemanticDefinitionSchemaVersion = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowPlan + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.SemanticDefinitionSchemaVersion |= int32(b&0x7F) << shift + if b < 0x80 { + break + } + } + default: + iNdEx = preIndex + skippy, err := skipPlan(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthPlan + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *PythonRoutineImplementation) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowPlan + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: PythonRoutineImplementation: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: PythonRoutineImplementation: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Handler", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowPlan + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthPlan + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthPlan + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Handler = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Source", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowPlan + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthPlan + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthPlan + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Source = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field AbiContract", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowPlan + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthPlan + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthPlan + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.AbiContract = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field AdapterVersion", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowPlan + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthPlan + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthPlan + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.AdapterVersion = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 5: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ArtifactDigest", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowPlan + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthPlan + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthPlan + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.ArtifactDigest = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 6: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field EnvironmentDigest", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowPlan + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthPlan + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthPlan + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.EnvironmentDigest = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 7: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field SdkVersion", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowPlan + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthPlan + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthPlan + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.SdkVersion = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 8: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field DefinitionSchemaVersion", wireType) + } + m.DefinitionSchemaVersion = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowPlan + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.DefinitionSchemaVersion |= int32(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 9: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Mode", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowPlan + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthPlan + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthPlan + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Mode = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 10: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field NullPolicy", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowPlan + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthPlan + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthPlan + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.NullPolicy = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 11: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field DefinitionFingerprint", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowPlan + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthPlan + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthPlan + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.DefinitionFingerprint = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipPlan(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthPlan + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *RoutineCall) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowPlan + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: RoutineCall: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: RoutineCall: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field ContractVersion", wireType) + } + m.ContractVersion = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowPlan + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.ContractVersion |= int32(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field FunctionRef", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowPlan + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthPlan + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthPlan + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.FunctionRef == nil { + m.FunctionRef = &FunctionRef{} + } + if err := m.FunctionRef.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Language", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowPlan + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthPlan + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthPlan + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Language = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ArgumentTypes", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowPlan + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthPlan + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthPlan + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.ArgumentTypes = append(m.ArgumentTypes, &Type{}) + if err := m.ArgumentTypes[len(m.ArgumentTypes)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 5: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ReturnType", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowPlan + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthPlan + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthPlan + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.ReturnType.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 6: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Volatility", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowPlan + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthPlan + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthPlan + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Volatility = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 7: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field NullPolicy", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowPlan + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthPlan + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthPlan + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.NullPolicy = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 8: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Sql", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowPlan + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthPlan + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthPlan + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + v := &SqlRoutineImplementation{} + if err := v.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + m.Implementation = &RoutineCall_Sql{v} + iNdEx = postIndex + case 9: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Python", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowPlan + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthPlan + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthPlan + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + v := &PythonRoutineImplementation{} + if err := v.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + m.Implementation = &RoutineCall_Python{v} + iNdEx = postIndex + case 10: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Context", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowPlan + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthPlan + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthPlan + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Context == nil { + m.Context = make(map[string]string) + } + var mapkey string + var mapvalue string + for iNdEx < postIndex { + entryPreIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowPlan + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + if fieldNum == 1 { + var stringLenmapkey uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowPlan + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLenmapkey |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLenmapkey := int(stringLenmapkey) + if intStringLenmapkey < 0 { + return ErrInvalidLengthPlan + } + postStringIndexmapkey := iNdEx + intStringLenmapkey + if postStringIndexmapkey < 0 { + return ErrInvalidLengthPlan + } + if postStringIndexmapkey > l { + return io.ErrUnexpectedEOF + } + mapkey = string(dAtA[iNdEx:postStringIndexmapkey]) + iNdEx = postStringIndexmapkey + } else if fieldNum == 2 { + var stringLenmapvalue uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowPlan + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLenmapvalue |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLenmapvalue := int(stringLenmapvalue) + if intStringLenmapvalue < 0 { + return ErrInvalidLengthPlan + } + postStringIndexmapvalue := iNdEx + intStringLenmapvalue + if postStringIndexmapvalue < 0 { + return ErrInvalidLengthPlan + } + if postStringIndexmapvalue > l { + return io.ErrUnexpectedEOF + } + mapvalue = string(dAtA[iNdEx:postStringIndexmapvalue]) + iNdEx = postStringIndexmapvalue + } else { + iNdEx = entryPreIndex + skippy, err := skipPlan(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthPlan + } + if (iNdEx + skippy) > postIndex { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + m.Context[mapkey] = mapvalue + iNdEx = postIndex + case 11: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field CallsiteId", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowPlan + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthPlan + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthPlan + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.CallsiteId = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 12: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field MayError", wireType) + } + var v int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowPlan + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + m.MayError = bool(v != 0) + case 13: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field SecurityMode", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowPlan + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthPlan + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthPlan + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.SecurityMode = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 14: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Leakproof", wireType) + } + var v int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowPlan + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + m.Leakproof = bool(v != 0) + default: + iNdEx = preIndex + skippy, err := skipPlan(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthPlan + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *RoutinePlanDependency) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowPlan + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: RoutinePlanDependency: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: RoutinePlanDependency: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field FunctionRef", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowPlan + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthPlan + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthPlan + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.FunctionRef == nil { + m.FunctionRef = &FunctionRef{} + } + if err := m.FunctionRef.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Language", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowPlan + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthPlan + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthPlan + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Language = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 3: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field ContractVersion", wireType) + } + m.ContractVersion = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowPlan + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.ContractVersion |= int32(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field DefinitionFingerprint", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowPlan + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthPlan + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthPlan + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.DefinitionFingerprint = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 5: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ArtifactDigest", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowPlan + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthPlan + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthPlan + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.ArtifactDigest = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 6: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field EnvironmentDigest", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowPlan + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthPlan + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthPlan + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.EnvironmentDigest = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 7: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Volatility", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowPlan + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthPlan + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthPlan + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Volatility = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 8: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field NullPolicy", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowPlan + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthPlan + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthPlan + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.NullPolicy = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 9: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field MayError", wireType) + } + var v int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowPlan + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + m.MayError = bool(v != 0) + case 10: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field SecurityMode", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowPlan + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthPlan + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthPlan + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.SecurityMode = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 11: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Leakproof", wireType) + } + var v int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowPlan + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + m.Leakproof = bool(v != 0) + default: + iNdEx = preIndex + skippy, err := skipPlan(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthPlan + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} func skipPlan(dAtA []byte) (n int, err error) { l := len(dAtA) iNdEx := 0 diff --git a/pkg/sql/colexec/evalExpression.go b/pkg/sql/colexec/evalExpression.go index d55a77bce14ae..3f5ed40c5a95b 100644 --- a/pkg/sql/colexec/evalExpression.go +++ b/pkg/sql/colexec/evalExpression.go @@ -350,6 +350,29 @@ func newExpressionExecutorWithAllocation( return executor, nil case *plan.Expr_F: + if routineCall := t.F.GetRoutineCall(); routineCall != nil { + parameters := make([]ExpressionExecutor, len(t.F.Args)) + for i := range parameters { + child, childErr := newExpressionExecutorWithAllocation(proc, t.F.Args[i], selection, buildCtx) + if childErr != nil { + for j := 0; j < i; j++ { + parameters[j].Free() + } + return nil, childErr + } + parameters[i] = child + } + external, externalErr := newExternalRoutineEval(proc, routineCall, parameters, selection) + if externalErr != nil { + for _, parameter := range parameters { + if parameter != nil { + parameter.Free() + } + } + return nil, externalErr + } + return external, nil + } overloadID := t.F.GetFunc().GetObj() overload, err := function.GetFunctionById(proc.Ctx, overloadID) if err != nil { @@ -1417,15 +1440,10 @@ func (expr *FunctionExpressionExecutor) evalSelectedRows( } for i, parameter := range expr.parameterResults { // Constants, folded vectors, and list/vector literals are not row-aligned. - // They must be passed through unchanged; only column and non-folded - // function results map one-to-one to the input batch rows. - rowAligned := false - switch executor := expr.parameterExecutor[i].(type) { - case *ColumnExpressionExecutor: - rowAligned = true - case *FunctionExpressionExecutor: - rowAligned = !executor.folded.canFold - } + // They must be passed through unchanged. ExternalRoutineEval is included + // in the row-aligned set because it owns selection compaction and scatters + // its result back to the caller's row domain before returning. + rowAligned := expressionExecutorIsRowAligned(expr.parameterExecutor[i]) if rowAligned && !parameter.IsConst() { selected := expr.selectedParameterVectors[i] if selected == nil { @@ -1535,6 +1553,29 @@ func (expr *FunctionExpressionExecutor) evalSelectedRows( return result, nil } +// expressionExecutorIsRowAligned reports whether Eval returns one physical +// value for every input row, even when it was called with a partial selection. +// ExternalRoutineEval owns selection itself and scatters its result back to +// the original row domain. Treating it as a constant here would pass the +// un-compacted vector to a surrounding expression and make a masked nested +// call read values from the wrong rows. +func expressionExecutorIsRowAligned(executor ExpressionExecutor) bool { + switch executor := executor.(type) { + case *ColumnExpressionExecutor: + return true + case *ExternalRoutineEval: + return true + case *FunctionExpressionExecutor: + return !executor.folded.canFold + case *memoExpressionExecutor: + return expressionExecutorIsRowAligned(executor.state.executor) + case *memoRootExpressionExecutor: + return expressionExecutorIsRowAligned(executor.executor) + default: + return false + } +} + func (expr *FunctionExpressionExecutor) Eval(proc *process.Process, batches []*batch.Batch, selectList []bool) (*vector.Vector, error) { if len(batches) == 0 { batches = []*batch.Batch{batch.EmptyForConstFoldBatch} diff --git a/pkg/sql/colexec/external_routine_eval.go b/pkg/sql/colexec/external_routine_eval.go new file mode 100644 index 0000000000000..b14f4c444f907 --- /dev/null +++ b/pkg/sql/colexec/external_routine_eval.go @@ -0,0 +1,704 @@ +// Copyright 2026 Matrix Origin +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. + +package colexec + +import ( + "encoding/json" + "fmt" + "os" + "sort" + "strconv" + "strings" + "sync" + "time" + + "github.com/google/uuid" + "github.com/matrixorigin/matrixone/pkg/common/mpool" + "github.com/matrixorigin/matrixone/pkg/container/batch" + "github.com/matrixorigin/matrixone/pkg/container/types" + "github.com/matrixorigin/matrixone/pkg/container/vector" + "github.com/matrixorigin/matrixone/pkg/defines" + planpb "github.com/matrixorigin/matrixone/pkg/pb/plan" + "github.com/matrixorigin/matrixone/pkg/sql/plan/function" + "github.com/matrixorigin/matrixone/pkg/udf" + "github.com/matrixorigin/matrixone/pkg/vm/process" +) + +// ExternalRoutineEval is the physical boundary for a typed RoutineCall. It +// owns argument evaluation, selection compaction/scatter, strict NULL +// guarding, and the result lifetime. The Flight gateway only sees the +// already-admitted selected rows and never owns SQL CASE semantics. +type ExternalRoutineEval struct { + mp *mpool.MPool + allocation *vector.AllocationAccountSelection + call *planpb.RoutineCall + resultType types.Type + + parameterExecutor []ExpressionExecutor + parameterResults []*vector.Vector + result vector.FunctionResultWrapper + + selectedRows []int64 + selectedParameters []*vector.Vector + selectedOwned []*vector.Vector + selectedResult vector.FunctionResultWrapper + selectedNull *vector.Vector + generation uint64 + executionMu sync.Mutex + freed bool + groupMu sync.Mutex + groupStatementID string + groupID string + groupEpoch uint64 +} + +func newExternalRoutineEval( + proc *process.Process, + call *planpb.RoutineCall, + parameters []ExpressionExecutor, + allocation *vector.AllocationAccountSelection, +) (*ExternalRoutineEval, error) { + if proc == nil || proc.Base == nil { + return nil, fmt.Errorf("python udf: evaluator has no process") + } + if err := validateRoutineCall(call); err != nil { + return nil, err + } + if len(call.ArgumentTypes) != len(parameters) { + return nil, fmt.Errorf("python udf: typed routine has %d argument descriptors but %d bound arguments", len(call.ArgumentTypes), len(parameters)) + } + resultType := planTypeToSQL(call.ReturnType) + result, err := vector.NewFunctionResultWrapperWithAllocation(resultType, proc.Mp(), allocation) + if err != nil { + return nil, err + } + return &ExternalRoutineEval{ + mp: proc.Mp(), + allocation: allocation, + call: call, + resultType: resultType, + parameterExecutor: parameters, + parameterResults: make([]*vector.Vector, len(parameters)), + result: result, + }, nil +} + +func validateRoutineCall(call *planpb.RoutineCall) error { + if call == nil || call.FunctionRef == nil || call.FunctionRef.FunctionId == 0 || + call.FunctionRef.DatabaseId == 0 || call.FunctionRef.Revision == 0 || call.FunctionRef.NamespaceVersion == 0 { + return fmt.Errorf("UNSUPPORTED_ROUTINE_VERSION: typed routine call has no exact FunctionRef") + } + if call.ReturnType.Id == int32(types.T_any) { + return fmt.Errorf("UNSUPPORTED_ROUTINE_VERSION: typed routine call has no return descriptor") + } + if call.ContractVersion != udf.PythonPlanContractVersion || !strings.EqualFold(call.Language, udf.LanguagePython) { + return fmt.Errorf("UNSUPPORTED_ROUTINE_VERSION: unsupported typed routine call contract") + } + if call.Volatility != "VOLATILE" { + return fmt.Errorf("UNSUPPORTED_ROUTINE_VERSION: Python routine must be VOLATILE") + } + if call.MayError != true || call.SecurityMode != "INVOKER" || call.Leakproof { + return fmt.Errorf("UNSUPPORTED_ROUTINE_VERSION: Python routine semantic contract is not supported") + } + if call.CallsiteId == "" || len(call.CallsiteId) > 256 || strings.ContainsAny(call.CallsiteId, "\r\n") { + return fmt.Errorf("UNSUPPORTED_ROUTINE_VERSION: typed routine call has no valid callsite id") + } + if len(call.Context) != 0 { + return fmt.Errorf("UNSUPPORTED_ROUTINE_VERSION: typed routine call contains execution context") + } + python := call.GetPython() + if python == nil { + return fmt.Errorf("UNSUPPORTED_ROUTINE_VERSION: typed routine call has no Python implementation") + } + if python.Source != "" { + return fmt.Errorf("UNSUPPORTED_ROUTINE_VERSION: typed Python plan contains source; rebind from the current artifact contract") + } + if python.Handler == "" || + python.AbiContract != udf.PythonABIContract || + python.AdapterVersion != udf.PythonAdapterVersion || + python.SdkVersion != udf.PythonSDKVersion || + python.DefinitionSchemaVersion != udf.PythonDefinitionSchemaVersion { + return fmt.Errorf("UNSUPPORTED_ROUTINE_VERSION: typed Python implementation contract is not supported") + } + if python.Mode != "SCALAR" && python.Mode != "VECTOR" { + return fmt.Errorf("python udf: unsupported typed call mode %q", python.Mode) + } + if python.NullPolicy != udf.NullCallHandler && python.NullPolicy != udf.NullReturnNull { + return fmt.Errorf("python udf: unsupported typed NULL policy %q", python.NullPolicy) + } + if call.NullPolicy != python.NullPolicy { + return fmt.Errorf("UNSUPPORTED_ROUTINE_VERSION: typed call NULL policy does not match its implementation") + } + if !udf.IsSHA256Digest(python.ArtifactDigest) || !udf.IsSHA256Digest(python.EnvironmentDigest) { + return fmt.Errorf("UNSUPPORTED_ROUTINE_VERSION: typed Python implementation has invalid artifact/environment digest") + } + if !udf.IsSHA256Digest(python.DefinitionFingerprint) { + return fmt.Errorf("UNSUPPORTED_ROUTINE_VERSION: typed Python implementation has invalid definition fingerprint") + } + if len(call.ArgumentTypes) > 0 { + for i, argumentType := range call.ArgumentTypes { + if argumentType == nil { + return fmt.Errorf("UNSUPPORTED_ROUTINE_VERSION: typed Python argument %d is nil", i) + } + } + } + return nil +} + +func (e *ExternalRoutineEval) Eval(proc *process.Process, batches []*batch.Batch, selectList []bool) (*vector.Vector, error) { + if e == nil || proc == nil || proc.Base == nil { + return nil, fmt.Errorf("python udf: evaluator has no process") + } + e.executionMu.Lock() + defer e.executionMu.Unlock() + if e.freed { + return nil, fmt.Errorf("python udf: evaluator is freed") + } + if err := validateRoutineCall(e.call); err != nil { + return nil, err + } + accountID, err := defines.GetAccountId(proc.Ctx) + if err != nil { + return nil, err + } + if uint64(accountID) != e.call.FunctionRef.AccountId { + return nil, fmt.Errorf("python udf: FunctionRef account does not match the execution account") + } + python := e.call.GetPython() + rowCount, err := externalRoutineRowCount(python.Mode, batches) + if err != nil { + return nil, err + } + if rowCount == 0 { + if err := e.result.PreExtendAndReset(0); err != nil { + return nil, err + } + return e.result.GetResultVector(), nil + } + if selectList != nil && len(selectList) < rowCount { + return nil, fmt.Errorf("external routine selection is shorter than the input batch") + } + + for i, parameter := range e.parameterExecutor { + var err error + e.parameterResults[i], err = parameter.Eval(proc, batches, selectList) + if err != nil { + return nil, err + } + } + if err := validateExternalRoutineInputs(e.parameterResults, e.call, rowCount); err != nil { + return nil, err + } + e.selectedRows = e.selectedRows[:0] + for row := 0; row < rowCount; row++ { + selected := selectList == nil || selectList[row] + if selected && python.NullPolicy == udf.NullReturnNull && routineHasNullAt(e.parameterResults, row) { + selected = false + } + if selected { + e.selectedRows = append(e.selectedRows, int64(row)) + } + } + if len(e.selectedRows) == 0 { + return e.nullResult(rowCount) + } + + inputs, err := e.compactParameters(proc, rowCount) + if err != nil { + // compactParameters may have already materialized earlier selected + // arguments before a later column fails. There is no execution defer + // until compaction succeeds, so release that partial ownership here. + e.releaseOwnedParameters() + return nil, err + } + defer e.releaseOwnedParameters() + + selectedResult := e.result + if len(e.selectedRows) != rowCount { + if e.selectedResult == nil { + e.selectedResult, err = vector.NewFunctionResultWrapperWithAllocation(e.resultType, e.mp, e.allocation) + if err != nil { + return nil, err + } + } + selectedResult = e.selectedResult + } + if err := selectedResult.PreExtendAndReset(len(e.selectedRows)); err != nil { + return nil, err + } + context, err := buildRoutineContext(proc) + if err != nil { + return nil, err + } + statementContext, err := udf.StatementContextFromMap(context) + if err != nil { + return nil, err + } + if err := e.attachInvocationGroup(context, proc.QueryId()); err != nil { + return nil, err + } + tuple, err := function.NewInvocationTuple(context, proc.QueryId(), accountID) + if err != nil { + return nil, err + } + args := make([]types.Type, len(e.call.ArgumentTypes)) + for i, typ := range e.call.ArgumentTypes { + args[i] = planTypeToSQL(*typ) + } + invocation := &udf.Invocation{ + FunctionRef: udf.FunctionRef{ + AccountID: pythonFunctionAccountID(e.call), + DatabaseID: e.call.FunctionRef.DatabaseId, + FunctionID: e.call.FunctionRef.FunctionId, + Revision: e.call.FunctionRef.Revision, + NamespaceVersion: e.call.FunctionRef.NamespaceVersion, + }, + Language: udf.LanguagePython, + Handler: python.Handler, + Args: args, + ReturnType: e.resultType, + Inputs: inputs, + Length: len(e.selectedRows), + Mode: python.Mode, + NullPolicy: python.NullPolicy, + ABIContract: python.AbiContract, + AdapterVersion: python.AdapterVersion, + SDKVersion: python.SdkVersion, + DefinitionSchemaVersion: int(python.DefinitionSchemaVersion), + ArtifactDigest: python.ArtifactDigest, + EnvironmentDigest: python.EnvironmentDigest, + DefinitionFingerprint: python.DefinitionFingerprint, + Context: context, + CallsiteID: e.call.CallsiteId, + MayError: e.call.MayError, + SecurityMode: e.call.SecurityMode, + Leakproof: e.call.Leakproof, + StatementContext: statementContext, + SecurityFrame: &udf.SecurityFrame{ + ContractVersion: udf.SecurityFrameContractVersion, + Mode: e.call.SecurityMode, + InvokerUserID: defines.GetUserId(proc.Ctx), + InvokerRoleID: defines.GetRoleId(proc.Ctx), + EffectiveUserID: defines.GetUserId(proc.Ctx), + EffectiveRoleID: defines.GetRoleId(proc.Ctx), + }, + Tuple: tuple, + } + if proc.Base.UdfService == nil { + return nil, fmt.Errorf("RESOURCE_UNAVAILABLE: Python UDF runtime is not enabled") + } + if err := proc.Base.UdfService.Execute(proc.Ctx, invocation, selectedResult, proc.Mp()); err != nil { + return nil, err + } + if len(e.selectedRows) == rowCount { + return e.result.GetResultVector(), nil + } + return e.scatter(proc, selectedResult.GetResultVector(), rowCount) +} + +// attachInvocationGroup gives one physical evaluator ownership of a reusable +// execution group for one statement. Each invocation still has its own +// InvocationID and monotonically increasing GroupEpoch, so a completed epoch +// cannot be reopened and a later batch cannot consume the previous member's +// resources. ResetForNextQuery discards the group identity; an evaluator that +// has no statement query id uses a fresh one-shot group because it cannot prove +// that two calls belong to the same statement. +func (e *ExternalRoutineEval) attachInvocationGroup(context map[string]string, queryID string) error { + if context == nil { + return fmt.Errorf("python udf: invocation context is unavailable") + } + e.groupMu.Lock() + defer e.groupMu.Unlock() + if queryID == "" { + group, err := uuid.NewV7() + if err != nil { + return fmt.Errorf("python udf: create execution group fence: %w", err) + } + context["group_id"] = "python/" + group.String() + context["group_epoch"] = "1" + return nil + } + if e.groupID == "" || e.groupStatementID != queryID { + group, err := uuid.NewV7() + if err != nil { + return fmt.Errorf("python udf: create execution group fence: %w", err) + } + e.groupStatementID = queryID + e.groupID = queryID + "/python/" + group.String() + e.groupEpoch = 0 + } + if e.groupEpoch == ^uint64(0) { + return fmt.Errorf("RESOURCE_EXHAUSTED: Python UDF execution group epoch overflow") + } + e.groupEpoch++ + context["group_id"] = e.groupID + context["group_epoch"] = strconv.FormatUint(e.groupEpoch, 10) + return nil +} + +func pythonFunctionAccountID(call *planpb.RoutineCall) uint64 { + if call == nil || call.FunctionRef == nil { + return 0 + } + return call.FunctionRef.AccountId +} + +func (e *ExternalRoutineEval) compactParameters(proc *process.Process, rowCount int) ([]*vector.Vector, error) { + if len(e.selectedRows) == 0 { + return nil, nil + } + e.selectedParameters = e.selectedParameters[:0] + for i, input := range e.parameterResults { + if input == nil { + return nil, fmt.Errorf("external routine parameter %d is nil", i) + } + if input.IsConst() || len(e.selectedRows) == rowCount { + e.selectedParameters = append(e.selectedParameters, input) + continue + } + selected, err := vector.NewOffHeapVecWithTypeAndAllocation(*input.GetType(), e.allocation) + if err != nil { + return nil, err + } + selected.SetIsBin(input.GetIsBin()) + if err := selected.Union(input, e.selectedRows, proc.Mp()); err != nil { + selected.Free(proc.Mp()) + return nil, err + } + e.selectedOwned = append(e.selectedOwned, selected) + e.selectedParameters = append(e.selectedParameters, selected) + } + return e.selectedParameters, nil +} + +func validateExternalRoutineInputs(inputs []*vector.Vector, call *planpb.RoutineCall, rowCount int) error { + if len(inputs) != len(call.ArgumentTypes) { + return fmt.Errorf("python udf: evaluated input count %d does not match routine argument count %d", len(inputs), len(call.ArgumentTypes)) + } + for index, input := range inputs { + if input == nil || input.GetType() == nil { + return fmt.Errorf("python udf: evaluated input vector %d has no type", index) + } + if input.Length() == 0 || (!input.IsConst() && input.Length() < rowCount) { + return fmt.Errorf("python udf: evaluated input vector %d has %d rows, expected at least %d", index, input.Length(), rowCount) + } + expected, err := function.NewPythonTypeDescriptor(planTypeToSQL(*call.ArgumentTypes[index])) + if err != nil { + return fmt.Errorf("python udf: invalid argument descriptor %d: %w", index, err) + } + actual, err := function.NewPythonTypeDescriptor(*input.GetType()) + if err != nil { + return fmt.Errorf("python udf: evaluated input vector %d has unsupported type: %w", index, err) + } + expectedFingerprint, err := expected.Fingerprint() + if err != nil { + return err + } + actualFingerprint, err := actual.Fingerprint() + if err != nil { + return err + } + if expectedFingerprint != actualFingerprint { + return fmt.Errorf("python udf: evaluated input vector %d type %s does not match the frozen descriptor", index, input.GetType().DescString()) + } + } + return nil +} + +func (e *ExternalRoutineEval) releaseOwnedParameters() { + for _, selected := range e.selectedOwned { + if selected != nil { + selected.Free(e.mp) + } + } + e.selectedOwned = e.selectedOwned[:0] +} + +func (e *ExternalRoutineEval) nullResult(rowCount int) (*vector.Vector, error) { + if err := e.result.PreExtendAndReset(rowCount); err != nil { + return nil, err + } + result := e.result.GetResultVector() + result.SetAllNulls(rowCount) + result.SetLength(rowCount) + return result, nil +} + +func (e *ExternalRoutineEval) scatter(proc *process.Process, selected *vector.Vector, rowCount int) (*vector.Vector, error) { + if err := e.result.PreExtendAndReset(rowCount); err != nil { + return nil, err + } + result := e.result.GetResultVector() + result.ResetWithSameType() + if e.selectedNull == nil || *e.selectedNull.GetType() != *selected.GetType() { + if e.selectedNull != nil { + e.selectedNull.Free(e.mp) + } + e.selectedNull = vector.NewConstNull(*selected.GetType(), 1, proc.Mp()) + } + selectedRow := 0 + for row := 0; row < rowCount; row++ { + if selectedRow < len(e.selectedRows) && e.selectedRows[selectedRow] == int64(row) { + if err := result.UnionOne(selected, int64(selectedRow), proc.Mp()); err != nil { + return nil, err + } + selectedRow++ + } else if err := result.UnionOne(e.selectedNull, 0, proc.Mp()); err != nil { + return nil, err + } + } + return result, nil +} + +func (e *ExternalRoutineEval) EvalWithoutResultReusing(proc *process.Process, batches []*batch.Batch, selectList []bool) (*vector.Vector, error) { + return e.Eval(proc, batches, selectList) +} + +func (e *ExternalRoutineEval) ResetForNextQuery() { + if e == nil { + return + } + e.executionMu.Lock() + defer e.executionMu.Unlock() + if e.freed { + return + } + e.generation++ + e.groupMu.Lock() + e.groupStatementID = "" + e.groupID = "" + e.groupEpoch = 0 + e.groupMu.Unlock() + for _, parameter := range e.parameterExecutor { + parameter.ResetForNextQuery() + } + e.parameterResults = make([]*vector.Vector, len(e.parameterExecutor)) +} + +func (e *ExternalRoutineEval) Free() { + if e == nil { + return + } + e.executionMu.Lock() + defer e.executionMu.Unlock() + if e.freed { + return + } + e.freed = true + for _, parameter := range e.parameterExecutor { + if parameter != nil { + parameter.Free() + } + } + if e.result != nil { + e.result.Free() + } + if e.selectedResult != nil { + e.selectedResult.Free() + } + if e.selectedNull != nil { + e.selectedNull.Free(e.mp) + } + e.releaseOwnedParameters() +} + +func (e *ExternalRoutineEval) IsColumnExpr() bool { return false } +func (e *ExternalRoutineEval) TypeName() string { return "external routine eval" } + +func routineHasNullAt(inputs []*vector.Vector, row int) bool { + for _, input := range inputs { + if input == nil || input.IsNull(uint64(row)) { + return true + } + } + return false +} + +func planTypeToSQL(typ planpb.Type) types.Type { + return types.NewWithCharset(types.T(typ.Id), typ.Width, typ.Scale, uint8(typ.Charset)) +} + +func externalRoutineRowCount(mode string, batches []*batch.Batch) (int, error) { + if len(batches) > 0 { + // ExpressionExecutor uses the first batch as the logical row domain and + // uses relation positions to read the other batches. Join operators pass + // several one-to-one materialized relation batches, so rejecting every + // multi-batch call would make a Python routine unusable in JOIN ON. A + // different cardinality is still a contract violation: silently selecting + // batches[0] would let an argument from another relation lose rows. + rowCount := -1 + for index, input := range batches { + if input == nil { + return 0, fmt.Errorf("external routine input batch %d is nil", index) + } + if rowCount < 0 { + rowCount = input.RowCount() + continue + } + if input.RowCount() != rowCount { + return 0, fmt.Errorf("python udf: external routine input batch %d has %d rows, expected %d for the logical row domain", index, input.RowCount(), rowCount) + } + } + return rowCount, nil + } + if mode == "VECTOR" { + return 0, fmt.Errorf("python udf: zero-argument VECTOR requires an input batch with num_rows") + } + return 1, nil +} + +// buildRoutineContext freezes the statement-visible values at the physical +// execution boundary. The plan carries only immutable routine metadata; it +// must not carry a statement identity or mutable session values because a +// prepared plan can outlive both. +func buildRoutineContext(proc *process.Process) (map[string]string, error) { + if proc == nil || proc.Base == nil { + return nil, fmt.Errorf("python udf: process is unavailable for statement context") + } + context := make(map[string]string) + // The current query id is stable for the statement and changes for every + // prepared execution; NewInvocationTuple generates one only for internal + // tests that have no query id. It is control metadata, never plan data. + if queryID := proc.QueryId(); queryID != "" { + context["statement_id"] = queryID + } else { + delete(context, "statement_id") + } + queryStart := proc.GetStmtProfile().GetQueryStart() + if queryStart.IsZero() { + // Internal/background processes do not always install a statement + // profile. UnixTime is process-scoped and therefore deterministic for + // the lifetime of that process; a real frontend statement always uses + // its query start above. + if proc.Base.UnixTime == 0 { + return nil, fmt.Errorf("python udf: statement timestamp is unavailable") + } + queryStart = time.Unix(0, proc.Base.UnixTime) + } + context["statement_timestamp_utc"] = strconv.FormatInt(queryStart.UTC().UnixMicro(), 10) + + location := proc.GetSessionInfo().TimeZone + if location == nil { + return nil, fmt.Errorf("python udf: session timezone is unavailable") + } + zoneName := location.String() + var timezoneErr error + if zoneName == "" || zoneName == "SYSTEM" { + return nil, fmt.Errorf("python udf: session timezone is not a stable IANA identity") + } + if zoneName == "Local" { + zoneName, location, timezoneErr = resolveSystemTimezone() + if timezoneErr != nil { + return nil, timezoneErr + } + } + if zoneName == "FixedZone" { + _, offset := queryStart.In(location).Zone() + context["session_timezone_kind"] = "FIXED_OFFSET" + context["session_timezone_offset_minutes"] = formatTimezoneOffsetMinutes(offset / 60) + delete(context, "session_timezone_name") + delete(context, "session_timezone_tzdb_version") + } else { + context["session_timezone_kind"] = "IANA" + context["session_timezone_name"] = zoneName + tzdbVersion, err := udf.TimezoneDatabaseVersion() + if err != nil { + return nil, err + } + context["session_timezone_tzdb_version"] = tzdbVersion + delete(context, "session_timezone_offset_minutes") + } + + mode := sessionSQLMode(proc) + encodedMode, err := json.Marshal(mode) + if err != nil { + return nil, fmt.Errorf("python udf: encode sql_mode context: %w", err) + } + context["sql_mode"] = string(encodedMode) + info := proc.GetSessionInfo() + context["current_user"] = info.User + context["connection_collation"] = info.GetCollation() + if info.Database != "" { + context["current_database"] = info.Database + } else { + delete(context, "current_database") + } + if info.Role != "" { + context["current_role"] = info.Role + } else { + delete(context, "current_role") + } + return context, nil +} + +// resolveSystemTimezone turns the frontend's SYSTEM location into the IANA +// identity that is part of the Python call contract. time.Local intentionally +// hides that identity behind the name "Local"; accepting that name would make +// two workers interpret a DST boundary from different host configuration. +// Prefer an explicit TZ setting, then use the conventional /etc/localtime +// symlink. A regular localtime file has no portable IANA identity and fails +// closed until the deployment supplies one. +func resolveSystemTimezone() (string, *time.Location, error) { + candidates := []string{strings.TrimSpace(os.Getenv("TZ"))} + if link, err := os.Readlink("/etc/localtime"); err == nil { + const marker = "/zoneinfo/" + if index := strings.Index(link, marker); index >= 0 { + candidates = append(candidates, link[index+len(marker):]) + } + } + for _, candidate := range candidates { + candidate = strings.TrimPrefix(candidate, ":") + candidate = strings.TrimPrefix(candidate, "posix/") + candidate = strings.TrimPrefix(candidate, "right/") + if candidate == "" || candidate == "Local" || candidate == "SYSTEM" || strings.HasPrefix(candidate, "/") || strings.Contains(candidate, "..") { + continue + } + location, err := time.LoadLocation(candidate) + if err == nil { + return candidate, location, nil + } + } + return "", nil, fmt.Errorf("python udf: SYSTEM timezone has no stable IANA identity") +} + +func formatTimezoneOffsetMinutes(offset int) string { + if offset >= 0 { + return "+" + strconv.Itoa(offset) + } + return strconv.Itoa(offset) +} + +func sessionSQLMode(proc *process.Process) []string { + value := "" + if proc != nil && proc.GetSessionInfo() != nil { + value = proc.GetSessionInfo().SqlMode + } + if proc != nil { + if resolve := proc.GetResolveVariableFunc(); resolve != nil { + if resolved, err := resolve("sql_mode", true, false); err == nil { + if text, ok := resolved.(string); ok { + value = text + } + } + } + } + if value == process.EmptySqlModeSentinel { + value = "" + } + seen := make(map[string]struct{}) + for _, item := range strings.FieldsFunc(value, func(r rune) bool { return r == ',' || r == ' ' || r == '\t' || r == '\n' || r == '\r' }) { + item = strings.ToUpper(strings.TrimSpace(item)) + if item != "" { + seen[item] = struct{}{} + } + } + result := make([]string, 0, len(seen)) + for item := range seen { + result = append(result, item) + } + sort.Strings(result) + return result +} diff --git a/pkg/sql/colexec/external_routine_eval_test.go b/pkg/sql/colexec/external_routine_eval_test.go new file mode 100644 index 0000000000000..63a54ee27fc5e --- /dev/null +++ b/pkg/sql/colexec/external_routine_eval_test.go @@ -0,0 +1,368 @@ +// Copyright 2026 Matrix Origin +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. + +package colexec + +import ( + "context" + "encoding/json" + "testing" + "time" + + "github.com/matrixorigin/matrixone/pkg/common/mpool" + "github.com/matrixorigin/matrixone/pkg/container/batch" + "github.com/matrixorigin/matrixone/pkg/container/types" + "github.com/matrixorigin/matrixone/pkg/container/vector" + planpb "github.com/matrixorigin/matrixone/pkg/pb/plan" + "github.com/matrixorigin/matrixone/pkg/sql/plan/function" + "github.com/matrixorigin/matrixone/pkg/testutil" + "github.com/matrixorigin/matrixone/pkg/udf" + "github.com/matrixorigin/matrixone/pkg/udf/python" + "github.com/matrixorigin/matrixone/pkg/vm/process" + "github.com/stretchr/testify/require" +) + +type externalRoutineTestExecutor struct { + vector *vector.Vector + evalSeen *int +} + +func (e *externalRoutineTestExecutor) Eval(_ *process.Process, _ []*batch.Batch, _ []bool) (*vector.Vector, error) { + if e.evalSeen != nil { + *e.evalSeen++ + } + return e.vector, nil +} + +func (e *externalRoutineTestExecutor) EvalWithoutResultReusing(proc *process.Process, batches []*batch.Batch, selectList []bool) (*vector.Vector, error) { + return e.Eval(proc, batches, selectList) +} + +func (e *externalRoutineTestExecutor) ResetForNextQuery() {} +func (e *externalRoutineTestExecutor) Free() {} +func (e *externalRoutineTestExecutor) IsColumnExpr() bool { return true } +func (e *externalRoutineTestExecutor) TypeName() string { return "test input" } + +type externalRoutineCaptureRuntime struct { + invocation *udf.Invocation + invocations []udf.Invocation +} + +func (r *externalRoutineCaptureRuntime) Language() string { return udf.LanguagePython } + +func (r *externalRoutineCaptureRuntime) Execute(_ context.Context, invocation *udf.Invocation, result vector.FunctionResultWrapper, mp *mpool.MPool) error { + r.invocation = invocation + r.invocations = append(r.invocations, *invocation) + output := result.(*vector.FunctionResult[int64]) + input := invocation.Inputs[0] + values := vector.MustFixedColNoTypeCheck[int64](input) + for row, value := range values[:invocation.Length] { + if err := output.Append(value+1, input.IsNull(uint64(row))); err != nil { + return err + } + } + return nil +} + +func TestExternalRoutineEvalReusesGroupWithNewEpoch(t *testing.T) { + proc := testutil.NewProcess(t) + defer proc.Free() + proc.SetQueryId("reentry-runtime-query") + proc.GetSessionInfo().TimeZone = time.FixedZone("FixedZone", 8*60*60) + proc.GetSessionInfo().User = "root" + proc.Base.StmtProfile = &process.StmtProfile{} + proc.GetStmtProfile().SetQueryStart(time.Date(2026, 9, 10, 1, 2, 3, 4000, time.UTC)) + + input := vector.NewVec(types.T_int64.ToType()) + defer input.Free(proc.Mp()) + require.NoError(t, vector.AppendFixed(input, int64(1), false, proc.Mp())) + inputBatch := batch.NewWithSize(0) + inputBatch.SetRowCount(1) + + runtime := &externalRoutineCaptureRuntime{} + proc.Base.UdfService = runtime + evaluator, err := newExternalRoutineEval(proc, testExternalRoutineCall(t, "SCALAR", udf.NullCallHandler), []ExpressionExecutor{ + &externalRoutineTestExecutor{vector: input}, + }, nil) + require.NoError(t, err) + defer evaluator.Free() + + _, err = evaluator.Eval(proc, []*batch.Batch{inputBatch}, []bool{true}) + require.NoError(t, err) + _, err = evaluator.Eval(proc, []*batch.Batch{inputBatch}, []bool{true}) + require.NoError(t, err) + require.Len(t, runtime.invocations, 2) + require.Equal(t, runtime.invocations[0].Tuple.GroupID, runtime.invocations[1].Tuple.GroupID) + require.Equal(t, uint64(1), runtime.invocations[0].Tuple.GroupEpoch) + require.Equal(t, uint64(2), runtime.invocations[1].Tuple.GroupEpoch) + require.NotEqual(t, runtime.invocations[0].Tuple.InvocationID, runtime.invocations[1].Tuple.InvocationID) + + evaluator.ResetForNextQuery() + _, err = evaluator.Eval(proc, []*batch.Batch{inputBatch}, []bool{true}) + require.NoError(t, err) + require.Len(t, runtime.invocations, 3) + require.NotEqual(t, runtime.invocations[1].Tuple.GroupID, runtime.invocations[2].Tuple.GroupID) + require.Equal(t, uint64(1), runtime.invocations[2].Tuple.GroupEpoch) +} + +func TestExternalRoutineEvalOwnsSelectionAndStrictNullGuard(t *testing.T) { + proc := testutil.NewProcess(t) + proc.SetQueryId("runtime-query") + proc.GetSessionInfo().TimeZone = time.FixedZone("FixedZone", 8*60*60) + proc.GetSessionInfo().User = "alice" + proc.GetSessionInfo().Database = "app" + proc.GetSessionInfo().Role = "writer" + proc.GetSessionInfo().SqlMode = "STRICT_TRANS_TABLES,ANSI" + proc.Base.StmtProfile = &process.StmtProfile{} + proc.GetStmtProfile().SetQueryStart(time.Date(2026, 9, 10, 1, 2, 3, 4000, time.FixedZone("input", 8*60*60))) + + mp := proc.Mp() + input := vector.NewVec(types.T_int64.ToType()) + defer input.Free(mp) + require.NoError(t, vector.AppendFixed(input, int64(1), false, mp)) + require.NoError(t, vector.AppendFixed(input, int64(0), true, mp)) + require.NoError(t, vector.AppendFixed(input, int64(3), false, mp)) + + runtime := &externalRoutineCaptureRuntime{} + proc.Base.UdfService = runtime + call := testExternalRoutineCall(t, "SCALAR", udf.NullReturnNull) + evaluator, err := newExternalRoutineEval(proc, call, []ExpressionExecutor{ + &externalRoutineTestExecutor{vector: input}, + }, nil) + require.NoError(t, err) + defer evaluator.Free() + + inputBatch := batch.NewWithSize(0) + inputBatch.SetRowCount(3) + result, err := evaluator.Eval(proc, []*batch.Batch{inputBatch}, []bool{true, true, true}) + require.NoError(t, err) + require.Equal(t, 2, runtime.invocation.Length, "the parent evaluator removes strict NULL rows before dispatch") + require.Equal(t, []int64{2, 0, 4}, vector.MustFixedColNoTypeCheck[int64](result)) + require.True(t, result.IsNull(1), "the skipped row is scattered back as NULL") + require.Equal(t, "runtime-query", runtime.invocation.Context["statement_id"]) + require.Equal(t, "[\"ANSI\",\"STRICT_TRANS_TABLES\"]", runtime.invocation.Context["sql_mode"]) + require.Equal(t, "+480", runtime.invocation.Context["session_timezone_offset_minutes"]) + require.Equal(t, "alice", runtime.invocation.Context["current_user"]) +} + +func TestNewExpressionExecutorDispatchesTypedRoutineCall(t *testing.T) { + proc := testutil.NewProcess(t) + defer proc.Free() + proc.SetQueryId("physical-external-routine-query") + proc.GetSessionInfo().TimeZone = time.FixedZone("FixedZone", 8*60*60) + proc.GetSessionInfo().User = "root" + proc.Base.StmtProfile = &process.StmtProfile{} + proc.GetStmtProfile().SetQueryStart(time.Date(2026, 9, 10, 1, 2, 3, 4000, time.UTC)) + + input := vector.NewVec(types.T_int64.ToType()) + for _, value := range []int64{1, 2, 3} { + require.NoError(t, vector.AppendFixed(input, value, false, proc.Mp())) + } + inputBatch := batch.NewWithSize(1) + inputBatch.Vecs[0] = input + inputBatch.SetRowCount(3) + defer inputBatch.Clean(proc.Mp()) + + runtime := &externalRoutineCaptureRuntime{} + proc.Base.UdfService = runtime + call := testExternalRoutineCall(t, "SCALAR", udf.NullCallHandler) + expression := &planpb.Expr{ + Typ: call.ReturnType, + Expr: &planpb.Expr_F{F: &planpb.Function{ + Args: []*planpb.Expr{{ + Typ: *call.ArgumentTypes[0], + Expr: &planpb.Expr_Col{Col: &planpb.ColRef{RelPos: 0, ColPos: 0}}, + }}, + RoutineCall: call, + }}, + } + + executor, err := NewExpressionExecutor(proc, expression) + require.NoError(t, err) + defer executor.Free() + result, err := executor.Eval(proc, []*batch.Batch{inputBatch}, []bool{true, false, true}) + require.NoError(t, err) + require.Len(t, runtime.invocations, 1) + require.Equal(t, 2, runtime.invocation.Length) + require.Equal(t, []int64{2, 0, 4}, vector.MustFixedColNoTypeCheck[int64](result)) + require.True(t, result.IsNull(1)) +} + +func TestExternalRoutineEvalRequiresRowDomainForZeroArgumentVector(t *testing.T) { + rows, err := externalRoutineRowCount("VECTOR", nil) + require.Zero(t, rows) + require.ErrorContains(t, err, "requires an input batch with num_rows") + + rows, err = externalRoutineRowCount("VECTOR", []*batch.Batch{nil}) + require.Zero(t, rows) + require.ErrorContains(t, err, "input batch 0 is nil") +} + +func TestExternalRoutineEvalAcceptsAlignedJoinBatches(t *testing.T) { + first := batch.NewWithSize(0) + first.SetRowCount(1) + second := batch.NewWithSize(0) + second.SetRowCount(1) + + rows, err := externalRoutineRowCount("SCALAR", []*batch.Batch{first, second}) + require.NoError(t, err) + require.Equal(t, 1, rows) + + second.SetRowCount(2) + rows, err = externalRoutineRowCount("SCALAR", []*batch.Batch{first, second}) + require.Zero(t, rows) + require.ErrorContains(t, err, "expected 1 for the logical row domain") +} + +func TestExternalRoutineEvalRejectsMissingReturnDescriptor(t *testing.T) { + call := testExternalRoutineCall(t, "SCALAR", udf.NullCallHandler) + call.ReturnType = planpb.Type{} + + require.NotPanics(t, func() { + err := validateRoutineCall(call) + require.ErrorContains(t, err, "no return descriptor") + }) +} + +func TestExternalRoutineEvalRejectsExecutionContextInPlan(t *testing.T) { + call := testExternalRoutineCall(t, "SCALAR", udf.NullCallHandler) + call.Context = map[string]string{"current_user": "attacker"} + + err := validateRoutineCall(call) + require.ErrorContains(t, err, "contains execution context") +} + +func TestExternalRoutineEvalRejectsAccountBeforeEvaluatingParameters(t *testing.T) { + proc := testutil.NewProcess(t) + defer proc.Free() + proc.SetQueryId("account-boundary-query") + proc.GetSessionInfo().TimeZone = time.FixedZone("FixedZone", 8*60*60) + proc.Base.StmtProfile = &process.StmtProfile{} + proc.GetStmtProfile().SetQueryStart(time.Date(2026, 9, 10, 1, 2, 3, 4000, time.UTC)) + + input := vector.NewVec(types.T_int64.ToType()) + defer input.Free(proc.Mp()) + require.NoError(t, vector.AppendFixed(input, int64(1), false, proc.Mp())) + seen := 0 + call := testExternalRoutineCall(t, "SCALAR", udf.NullCallHandler) + call.FunctionRef.AccountId = 99 + evaluator, err := newExternalRoutineEval(proc, call, []ExpressionExecutor{ + &externalRoutineTestExecutor{vector: input, evalSeen: &seen}, + }, nil) + require.NoError(t, err) + defer evaluator.Free() + + inputBatch := batch.NewWithSize(0) + inputBatch.SetRowCount(1) + _, err = evaluator.Eval(proc, []*batch.Batch{inputBatch}, []bool{true}) + require.ErrorContains(t, err, "FunctionRef account does not match") + require.Zero(t, seen) +} + +func TestMaskedNestedExternalRoutineIsCompactedBeforeOuterEval(t *testing.T) { + proc := testutil.NewProcess(t) + defer proc.Free() + proc.SetQueryId("nested-runtime-query") + proc.GetSessionInfo().TimeZone = time.FixedZone("FixedZone", 8*60*60) + proc.GetSessionInfo().User = "alice" + proc.GetSessionInfo().Database = "app" + proc.GetSessionInfo().SqlMode = "STRICT_TRANS_TABLES" + proc.Base.StmtProfile = &process.StmtProfile{} + proc.GetStmtProfile().SetQueryStart(time.Date(2026, 9, 10, 1, 2, 3, 4000, time.FixedZone("input", 8*60*60))) + + input := vector.NewVec(types.T_int64.ToType()) + for _, value := range []int64{1, 2, 3} { + require.NoError(t, vector.AppendFixed(input, value, false, proc.Mp())) + } + inputBatch := batch.NewWithSize(1) + inputBatch.Vecs[0] = input + inputBatch.SetRowCount(3) + defer inputBatch.Clean(proc.Mp()) + + runtime := &externalRoutineCaptureRuntime{} + proc.Base.UdfService = runtime + external, err := newExternalRoutineEval(proc, testExternalRoutineCall(t, "SCALAR", udf.NullCallHandler), []ExpressionExecutor{ + &externalRoutineTestExecutor{vector: input}, + }, nil) + require.NoError(t, err) + + outer := NewFunctionExpressionExecutor() + require.NoError(t, outer.Init(proc, 1, types.T_int64.ToType())) + defer outer.Free() + outer.evalFn = func(parameters []*vector.Vector, result vector.FunctionResultWrapper, _ *process.Process, length int, _ *function.FunctionSelectList) error { + values := vector.GenerateFunctionFixedTypeParameter[int64](parameters[0]) + output := vector.MustFunctionResult[int64](result) + for row := 0; row < length; row++ { + value, isNull := values.GetValue(uint64(row)) + if err := output.Append(value, isNull); err != nil { + return err + } + } + return nil + } + outer.SetParameter(0, external) + + result, err := outer.Eval(proc, []*batch.Batch{inputBatch}, []bool{false, true, true}) + require.NoError(t, err) + require.Equal(t, []int64{0, 3, 4}, vector.MustFixedColNoTypeCheck[int64](result)) + require.True(t, result.IsNull(0)) +} + +func TestBuildRoutineContextRejectsUnstableTimezone(t *testing.T) { + proc := testutil.NewProcess(t) + proc.GetSessionInfo().TimeZone = time.Local + _, err := buildRoutineContext(proc) + if time.Local.String() == "Local" { + if _, _, resolveErr := resolveSystemTimezone(); resolveErr != nil { + require.ErrorContains(t, err, "stable IANA identity") + } else { + require.NoError(t, err) + } + } else { + require.NoError(t, err) + } +} + +func testExternalRoutineCall(t *testing.T, mode, nullPolicy string) *planpb.RoutineCall { + t.Helper() + descriptor, err := function.NewPythonTypeDescriptor(types.T_int64.ToType()) + require.NoError(t, err) + body, err := json.Marshal(function.PythonRoutineBody{ + DefinitionSchemaVersion: udf.PythonDefinitionSchemaVersion, + Handler: "add", + Source: "def add(ctx, value): return value + 1", + Mode: mode, + NullPolicy: nullPolicy, + ABIContract: udf.PythonABIContract, + AdapterVersion: udf.PythonAdapterVersion, + ArtifactDigest: udf.PythonInlineArtifactDigest("add", "def add(ctx, value): return value + 1"), + EnvironmentDigest: func() string { digest, _ := udf.PythonEnvironmentDigest(); return digest }(), + SDKVersion: udf.PythonSDKVersion, + ArgTypes: []function.PythonTypeDescriptor{descriptor}, + ReturnType: &descriptor, + }) + require.NoError(t, err) + routine := &function.Udf{ + FunctionID: 100, + AccountID: 0, + DatabaseID: 2, + Revision: 7, + NamespaceVersion: 9, + Language: udf.LanguagePython, + Body: string(body), + PythonArgTypes: []function.PythonTypeDescriptor{descriptor}, + PythonReturnType: &descriptor, + } + call, err := routine.GetRoutineCall() + require.NoError(t, err) + call.CallsiteId = "python/test" + return call +} + +func TestExternalRoutineEvalRejectsSourceInExecutablePlan(t *testing.T) { + call := testExternalRoutineCall(t, python.ModeScalar, udf.NullCallHandler) + call.GetPython().Source = "def add(ctx, value): return value + 1" + require.ErrorContains(t, validateRoutineCall(call), "plan contains source") +} diff --git a/pkg/sql/plan/base_binder.go b/pkg/sql/plan/base_binder.go index dfa53a9257bbe..6149df76e5ffc 100644 --- a/pkg/sql/plan/base_binder.go +++ b/pkg/sql/plan/base_binder.go @@ -4291,8 +4291,24 @@ func bindFuncExprImplUdf( return nil, moerr.NewNotSupportedf(b.GetContext(), "function '%s'", name) } - switch udf.Language { - case string(tree.SQL): + switch strings.ToLower(udf.Language) { + case strings.ToLower(string(tree.SQL)): + // A current SQL revision enters the common dependency envelope before + // its compatibility lowering expands the body. Legacy rows have no + // revision and retain the old path until an explicit replacement + // publishes a current shared revision. + if b.builder != nil && udf.Revision != 0 { + routineCall, err := udf.GetRoutineCall() + if err != nil { + return nil, moerr.NewInvalidInputf(b.GetContext(), "SQL routine is not executable: %v", err) + } + if err := b.builder.assignRoutineCallsite(routineCall); err != nil { + return nil, moerr.NewInvalidInputf(b.GetContext(), "%v", err) + } + if err := b.builder.recordRoutinePlanDependency(routineCall); err != nil { + return nil, moerr.NewInvalidInputf(b.GetContext(), "%v", err) + } + } parserSQLMode := "PIPES_AS_CONCAT" if udf.SQLMode != nil { parserSQLMode = *udf.SQLMode @@ -4337,7 +4353,7 @@ func bindFuncExprImplUdf( } } return expr, nil - case string(tree.PYTHON): + case strings.ToLower(string(tree.PYTHON)): expr, err := b.bindPythonUdf(udf, astArgs, depth) if err != nil { return nil, err @@ -4519,31 +4535,63 @@ func correlateSQLUdfArgument(arg *plan.Expr, depth int32) (*plan.Expr, bool) { } func (b *baseBinder) bindPythonUdf(udf *function.Udf, astArgs []tree.Expr, depth int32) (*plan.Expr, error) { - args := make([]*Expr, 2*len(astArgs)+2) - - // python udf self info and query context - args[0] = udf.GetPlanExpr() - - // bind ast function's args + if len(astArgs) != len(udf.GetArgsType()) { + return nil, moerr.NewInvalidInputf(b.GetContext(), "Python routine %s expects %d arguments, got %d", udf.Db, len(udf.GetArgsType()), len(astArgs)) + } + args := make([]*Expr, len(astArgs)) for idx, arg := range astArgs { expr, err := b.impl.BindExpr(arg, depth, false) if err != nil { return nil, err } - args[idx+1] = expr + required := udf.GetArgsType()[idx] + actual := makeTypeByPlan2Expr(expr) + if !actual.Eq(required) { + canCast, _ := function.UdfArgTypeMatch([]types.Type{actual}, []types.T{required.Oid}) + if actual.Oid == required.Oid { + canCast = true + } + if !canCast { + return nil, moerr.NewInvalidInputf(b.GetContext(), "Python routine argument %d has type %s, expected %s", idx+1, actual.String(), required.String()) + } + expr, err = appendCastBeforeExpr(b.GetContext(), expr, makePlan2Type(&required)) + if err != nil { + return nil, err + } + } + args[idx] = expr } - - // function args - fArgTypes := udf.GetArgsPlanType() - for i, t := range fArgTypes { - args[len(astArgs)+i+1] = &Expr{Typ: *t} + // Execution identity is intentionally absent from the persisted plan. A + // prepared plan can be executed many times; the physical evaluator creates + // a fresh statement fence from the current process/query identity. + routineCall, err := udf.GetRoutineCall() + if err != nil { + // RETURNING has its own semantic prohibition for external routines. + // Preserve that deterministic SQL diagnostic even when a test or a + // partially upgraded catalog supplies a Python descriptor without the + // executable identity that the normal path requires. + if b.builder != nil && b.builder.bindingReturningProjection && + strings.Contains(err.Error(), "stable catalog identity") { + return nil, returningNotSupported(b.builder, "external UDF in RETURNING expression") + } + return nil, moerr.NewInvalidInputf(b.GetContext(), "Python routine is not executable: %v", err) } - - // function ret - fRetType := udf.GetRetPlanType() - args[2*len(astArgs)+1] = &Expr{Typ: *fRetType} - - return BindFuncExprImplByPlanExpr(b.GetContext(), "python_user_defined_function", args) + if b.builder != nil { + if err := b.builder.assignRoutineCallsite(routineCall); err != nil { + return nil, moerr.NewInvalidInputf(b.GetContext(), "%v", err) + } + if err := b.builder.recordRoutinePlanDependency(routineCall); err != nil { + return nil, moerr.NewInvalidInputf(b.GetContext(), "%v", err) + } + } + return &plan.Expr{ + Typ: *udf.GetRetPlanType(), + Expr: &plan.Expr_F{F: &plan.Function{ + Func: &plan.ObjectRef{Obj: function.EncodeOverloadID(function.PYTHON_UDF, 0), ObjName: "python_user_defined_function"}, + Args: args, + RoutineCall: routineCall, + }}, + }, nil } func isFindInSetName(name string) bool { diff --git a/pkg/sql/plan/deepcopy.go b/pkg/sql/plan/deepcopy.go index 30000f0b55066..ea6a7c9dfb887 100644 --- a/pkg/sql/plan/deepcopy.go +++ b/pkg/sql/plan/deepcopy.go @@ -858,6 +858,7 @@ func DeepCopyQuery(qry *plan.Query) *plan.Query { ApplySqlSelectLimit: qry.ApplySqlSelectLimit, DetectSqls: slices.Clone(qry.DetectSqls), CatalogDependencies: make([]*plan.ObjectRef, len(qry.CatalogDependencies)), + RoutineDependencies: make([]*plan.RoutinePlanDependency, len(qry.RoutineDependencies)), } for idx, node := range qry.Nodes { newQry.Nodes[idx] = DeepCopyNode(node) @@ -875,6 +876,11 @@ func DeepCopyQuery(qry *plan.Query) *plan.Query { } } } + for idx, dependency := range qry.RoutineDependencies { + if dependency != nil { + newQry.RoutineDependencies[idx] = proto.Clone(dependency).(*plan.RoutinePlanDependency) + } + } return newQry } @@ -1262,6 +1268,10 @@ func DeepCopyExpr(expr *Expr) *Expr { for idx, arg := range item.F.Args { newArgs[idx] = DeepCopyExpr(arg) } + var routineCall *plan.RoutineCall + if item.F.RoutineCall != nil { + routineCall = proto.Clone(item.F.RoutineCall).(*plan.RoutineCall) + } newExpr.Expr = &plan.Expr_F{ F: &plan.Function{ Func: DeepCopyObjectRef(item.F.Func), @@ -1269,6 +1279,7 @@ func DeepCopyExpr(expr *Expr) *Expr { AggConfig: bytes.Clone(item.F.AggConfig), AggConfigType: item.F.AggConfigType, SyntaxExplicitCast: item.F.SyntaxExplicitCast, + RoutineCall: routineCall, }, } diff --git a/pkg/sql/plan/deepcopy_test.go b/pkg/sql/plan/deepcopy_test.go index 23e01990daaca..da683be38de7c 100644 --- a/pkg/sql/plan/deepcopy_test.go +++ b/pkg/sql/plan/deepcopy_test.go @@ -381,6 +381,20 @@ func TestDeepCopyQueryPreservesExecutionMetadata(t *testing.T) { StmtType: planpb.Query_SELECT, Headings: []string{"background"}, }}, + RoutineDependencies: []*planpb.RoutinePlanDependency{{ + FunctionRef: &planpb.FunctionRef{ + FunctionId: 41, + Revision: 7, + NamespaceVersion: 12, + AccountId: 9, + DatabaseId: 8, + }, + Language: "python", + ContractVersion: 1, + DefinitionFingerprint: "fingerprint", + ArtifactDigest: "artifact", + EnvironmentDigest: "environment", + }}, } cloned := DeepCopyQuery(source) @@ -398,13 +412,18 @@ func TestDeepCopyQueryPreservesExecutionMetadata(t *testing.T) { require.Equal(t, int64(8), cloned.MaxDop) require.Len(t, cloned.BackgroundQueries, 1) require.NotSame(t, source.BackgroundQueries[0], cloned.BackgroundQueries[0]) + require.Equal(t, source.RoutineDependencies, cloned.RoutineDependencies) + require.NotSame(t, source.RoutineDependencies[0], cloned.RoutineDependencies[0]) + require.NotSame(t, source.RoutineDependencies[0].FunctionRef, cloned.RoutineDependencies[0].FunctionRef) cloned.Steps[0] = 99 cloned.Headings[0] = "changed" cloned.BackgroundQueries[0].Headings[0] = "changed" + cloned.RoutineDependencies[0].FunctionRef.Revision = 99 require.Equal(t, int32(3), source.Steps[0]) require.Equal(t, "id", source.Headings[0]) require.Equal(t, "background", source.BackgroundQueries[0].Headings[0]) + require.Equal(t, uint64(7), source.RoutineDependencies[0].FunctionRef.Revision) } func TestDeepCopyDataDefinitionCreateTablePreservesExecutionFields(t *testing.T) { diff --git a/pkg/sql/plan/expr_hash.go b/pkg/sql/plan/expr_hash.go index 64b47b6556aed..6c2b716381732 100644 --- a/pkg/sql/plan/expr_hash.go +++ b/pkg/sql/plan/expr_hash.go @@ -127,6 +127,17 @@ func hashExprInto(h writeByter, expr *plan.Expr) { } else { writeUint32(h, 0) } + if v.F.RoutineCall == nil { + writeByte(h, 0) + } else { + writeByte(h, 1) + if b, err := v.F.RoutineCall.Marshal(); err == nil { + writeUint32(h, uint32(len(b))) + _, _ = h.Write(b) + } else { + writeUint32(h, 0) + } + } for _, a := range v.F.Args { hashExprInto(h, a) } @@ -337,6 +348,9 @@ func exprStructuralEqual(a, b *plan.Expr) bool { if !objectRefEqual(av.F.Func, bv.F.Func) { return false } + if !routineCallEqual(av.F.RoutineCall, bv.F.RoutineCall) { + return false + } if len(av.F.Args) != len(bv.F.Args) { return false } @@ -396,6 +410,21 @@ func exprStructuralEqual(a, b *plan.Expr) bool { } } +func routineCallEqual(a, b *plan.RoutineCall) bool { + if a == b { + return true + } + if a == nil || b == nil { + return false + } + ab, aerr := a.Marshal() + bb, berr := b.Marshal() + if aerr != nil || berr != nil { + return false + } + return bytes.Equal(ab, bb) +} + func literalEqual(typ plan.Type, a, b *plan.Literal) bool { if a == b { return true diff --git a/pkg/sql/plan/expr_hash_test.go b/pkg/sql/plan/expr_hash_test.go index 56f67ab0972be..70aa4e372116c 100644 --- a/pkg/sql/plan/expr_hash_test.go +++ b/pkg/sql/plan/expr_hash_test.go @@ -67,6 +67,42 @@ func TestExprStructuralHashDistinguishesObjectRef(t *testing.T) { require.True(t, exprStructuralEqual(a, c)) } +func TestExprStructuralHashIncludesRoutineIdentity(t *testing.T) { + call := func(revision uint64) *planpb.RoutineCall { + return &planpb.RoutineCall{ + ContractVersion: 1, + FunctionRef: &planpb.FunctionRef{ + FunctionId: 17, + Revision: revision, + NamespaceVersion: 1, + AccountId: 9, + DatabaseId: 11, + }, + Language: "python", + Volatility: "VOLATILE", + NullPolicy: "CALLED_ON_NULL_INPUT", + ReturnType: planpb.Type{Id: int32(types.T_int64)}, + } + } + mk := func(revision uint64) *planpb.Expr { + return &planpb.Expr{ + Typ: planpb.Type{Id: int32(types.T_int64)}, + Expr: &planpb.Expr_F{F: &planpb.Function{ + Func: &planpb.ObjectRef{ObjName: "python_user_defined_function", Obj: 1}, + RoutineCall: call(revision), + }}, + } + } + + a := mk(1) + b := mk(2) + c := mk(1) + require.NotEqual(t, exprStructuralHash(a), exprStructuralHash(b)) + require.False(t, exprStructuralEqual(a, b)) + require.Equal(t, exprStructuralHash(a), exprStructuralHash(c)) + require.True(t, exprStructuralEqual(a, c)) +} + func TestExprStructuralIdentityIncludesNegativeAuxID(t *testing.T) { a := int64Lit(1) a.AuxId = -1 diff --git a/pkg/sql/plan/function/list_builtIn.go b/pkg/sql/plan/function/list_builtIn.go index e16d5d3b417c5..d2471af43700e 100644 --- a/pkg/sql/plan/function/list_builtIn.go +++ b/pkg/sql/plan/function/list_builtIn.go @@ -16241,7 +16241,7 @@ var supportedOthersBuiltIns = []FuncNew{ // execute it once and broadcast the result to all rows. volatile: true, newOp: func() executeLogicOfOverload { - return runPythonUdf + return rejectPythonJSONPlan }, }, }, diff --git a/pkg/sql/plan/function/python_udf.go b/pkg/sql/plan/function/python_udf.go index 90b29bdf5cc44..9f4b54554e78e 100644 --- a/pkg/sql/plan/function/python_udf.go +++ b/pkg/sql/plan/function/python_udf.go @@ -9,16 +9,12 @@ package function import ( - "encoding/json" "fmt" "strconv" "github.com/google/uuid" - "github.com/matrixorigin/matrixone/pkg/common/mpool" "github.com/matrixorigin/matrixone/pkg/container/types" "github.com/matrixorigin/matrixone/pkg/container/vector" - "github.com/matrixorigin/matrixone/pkg/defines" - "github.com/matrixorigin/matrixone/pkg/udf" "github.com/matrixorigin/matrixone/pkg/udf/protocol" "github.com/matrixorigin/matrixone/pkg/vm/process" ) @@ -78,170 +74,72 @@ func pythonTypesEqual(left, right types.Type) bool { return left.Eq(right) } -// param parameters is same with param inputs in function checkPythonUdf -func pythonUdfRetType(parameters []types.Type) types.Type { - return parameters[len(parameters)-1] -} - -// param parameters has two parts: -// 1. parameters[0]: const vector udf -// 2. parameters[1:]: data vectors -// -// The SQL executor normally applies CASE/selection compaction before this -// function is called. The NULL policy is still enforced here because it is -// part of the persisted Python routine contract and must not be inferred from -// the generic builtin STRICT bit. -func runPythonUdf(parameters []*vector.Vector, result vector.FunctionResultWrapper, proc *process.Process, length int, selectList *FunctionSelectList) error { - if len(parameters) == 0 || parameters[0] == nil { - return fmt.Errorf("python udf: missing routine descriptor") - } - if err := validatePythonRoutineDescriptor(parameters[0]); err != nil { - return err - } - if length < 0 { - return fmt.Errorf("python udf: negative input length %d", length) - } - - routine := &UdfWithContext{} - encoded, isNull := vector.GenerateFunctionStrParameter(parameters[0]).GetStrValue(0) - if isNull { - return fmt.Errorf("python udf: routine descriptor is NULL") - } - if err := json.Unmarshal(encoded, routine); err != nil { - return fmt.Errorf("python udf: decode routine descriptor: %w", err) - } - if routine.Udf == nil { - return fmt.Errorf("python udf: routine descriptor has no function") - } - if err := routine.LoadPythonTypeContract(); err != nil { - return fmt.Errorf("python udf: decode Python type contract: %w", err) - } - - body, err := DecodePythonRoutineBody(routine.Body) - if err != nil { - return fmt.Errorf("python udf: decode Python routine body: %w", err) - } - argTypes, err := routineArgumentTypes(routine) - if err != nil { - return err - } - if len(parameters)-1 != len(argTypes) { - return fmt.Errorf("python udf: routine has %d arguments, received %d", len(argTypes), len(parameters)-1) - } - - if length == 0 { - return result.PreExtendAndReset(0) - } - if err := validatePythonInputVectors(parameters[1:], argTypes, length); err != nil { - return err - } - // FunctionExpressionExecutor passes an empty, non-nil select list when the - // caller selected every row. In that state AnyNull is false and the - // missing bitmap means "all rows", not a truncated selection. A partial - // selection must still carry one entry per invocation row so that a row - // cannot accidentally be evaluated after CASE/short-circuit filtering. - if selectList != nil && !selectList.ShouldEvalAllRow() && len(selectList.SelectList) < length { - return fmt.Errorf("python udf: selection list is shorter than invocation length") - } - - selected := make([]int64, 0, length) - for row := 0; row < length; row++ { - if selectList != nil && len(selectList.SelectList) > row && !selectList.SelectList[row] { +// PythonUdfArgTypeMatch applies the ordinary implicit cast graph while also +// treating the complete frozen descriptor as the exact-match key. The shared +// UDF resolver historically compared only OIDs, which made two Python +// overloads such as DECIMAL(18,2) and DECIMAL(18,6) indistinguishable and +// allowed a non-exact candidate to win with the same cost as an exact one. +func PythonUdfArgTypeMatch(from, to []types.Type) (bool, int) { + if len(from) != len(to) { + return false, -1 + } + length := len(from) + cost := 0 + for index := range from { + if pythonTypesEqual(from[index], to[index]) { continue } - if body.NullPolicy == udf.NullReturnNull && hasNullInput(parameters[1:], row) { + if from[index].Oid == to[index].Oid { + // A same-OID descriptor change is a SQL cast to the declared + // width/scale. It is valid, but less specific than an exact match. + cost++ continue } - selected = append(selected, int64(row)) - } - if len(selected) == 0 { - if err := result.PreExtendAndReset(length); err != nil { - return err + canCast, castCost := fixedImplicitTypeCast(from[index], to[index].Oid) + if !canCast { + return false, -1 } - result.GetResultVector().SetAllNulls(length) - result.GetResultVector().SetLength(length) - return nil - } - - inputs := parameters[1:] - var compacted []*vector.Vector - if len(selected) != length { - compacted = make([]*vector.Vector, len(inputs)) - for i, input := range inputs { - if input == nil { - return fmt.Errorf("python udf: input vector %d is nil", i) - } - compacted[i] = vector.NewOffHeapVecWithType(*input.GetType()) - compacted[i].SetIsBin(input.GetIsBin()) - if err := compacted[i].Union(input, selected, proc.Mp()); err != nil { - freeVectors(compacted, proc.Mp()) - return fmt.Errorf("python udf: compact input %d: %w", i, err) - } + if castCost == 1 { + cost += castCost + } else { + cost += castCost * length } - inputs = compacted - defer freeVectors(compacted, proc.Mp()) - } - - callResult := result - var temporary vector.FunctionResultWrapper - if len(selected) != length { - temporary = vector.NewFunctionResultWrapper(routine.GetRetType(), proc.Mp()) - callResult = temporary - defer temporary.Free() - } - - accountID, err := defines.GetAccountId(proc.Ctx) - if err != nil { - return fmt.Errorf("python udf: resolve account: %w", err) - } - tuple, err := invocationTuple(routine.Context, proc.QueryId(), accountID) - if err != nil { - return err - } - invocation := &udf.Invocation{ - Language: udf.LanguagePython, - Handler: body.Handler, - Source: body.Source, - Args: argTypes, - ReturnType: routine.GetRetType(), - Inputs: inputs, - Length: len(selected), - Mode: body.Mode, - NullPolicy: body.NullPolicy, - ABIContract: body.ABIContract, - AdapterVersion: body.AdapterVersion, - SDKVersion: body.SDKVersion, - Context: cloneContext(routine.Context), - Tuple: tuple, - } - if err := proc.Base.UdfService.Execute(proc.Ctx, invocation, callResult, proc.Mp()); err != nil { - return err } + return true, cost +} - if len(selected) == length { +// PythonUdfArgTypeCast returns the exact descriptor that the Python handler +// declared for every argument. The generic UDF cast helper only receives an +// OID list, so it intentionally preserves a same-OID source type. That is +// unsafe for Python's frozen contract: DECIMAL(18,2) and DECIMAL(18,6) have +// the same OID but different Arrow interpretation. The SQL cast expression +// must therefore materialize the target descriptor even when only metadata +// differs. +func PythonUdfArgTypeCast(from, to []types.Type) []types.Type { + if len(from) != len(to) { return nil } - if err := result.PreExtendAndReset(length); err != nil { - return err - } - full := result.GetResultVector() - full.ResetWithSameType() - nullResult := vector.NewConstNull(routine.GetRetType(), 1, proc.Mp()) - defer nullResult.Free(proc.Mp()) - selectedRow := int64(0) - for row := 0; row < length; row++ { - if selectedRow < int64(len(selected)) && selected[selectedRow] == int64(row) { - if err := full.UnionOne(callResult.GetResultVector(), selectedRow, proc.Mp()); err != nil { - return err - } - selectedRow++ + castTypes := make([]types.Type, len(from)) + for index := range from { + if pythonTypesEqual(from[index], to[index]) { + castTypes[index] = from[index] continue } - if err := full.UnionOne(nullResult, 0, proc.Mp()); err != nil { - return err - } + castTypes[index] = to[index] } - return nil + return castTypes +} + +// param parameters is same with param inputs in function checkPythonUdf +func pythonUdfRetType(parameters []types.Type) types.Type { + return parameters[len(parameters)-1] +} + +// rejectPythonJSONPlan is kept as the explicit stale-plan boundary for the +// historical overload id. Long-term execution enters through the typed +// RoutineCall field and ExternalRoutineEval. +func rejectPythonJSONPlan(_ []*vector.Vector, _ vector.FunctionResultWrapper, _ *process.Process, _ int, _ *FunctionSelectList) error { + return fmt.Errorf("UNSUPPORTED_ROUTINE_VERSION: Python JSON plan execution is not supported; reprepare the statement") } func validatePythonRoutineDescriptor(descriptor *vector.Vector) error { @@ -291,50 +189,6 @@ func hasNullInput(inputs []*vector.Vector, row int) bool { return false } -func routineArgumentTypes(routine *UdfWithContext) ([]types.Type, error) { - if routine.Language == "python" { - args := make([]types.Type, len(routine.PythonArgTypes)) - for i, descriptor := range routine.PythonArgTypes { - args[i] = descriptor.Type() - } - return args, nil - } - if len(routine.ArgsType) != 0 || len(routine.Args) == 0 { - return append([]types.Type(nil), routine.ArgsType...), nil - } - args := make([]types.Type, len(routine.Args)) - for i, arg := range routine.Args { - if arg == nil { - return nil, fmt.Errorf("python udf: routine argument %d is nil", i) - } - typ, ok := types.Types[arg.Type] - if !ok { - return nil, fmt.Errorf("python udf: unknown routine argument type %q", arg.Type) - } - args[i] = typ.ToType() - } - return args, nil -} - -func cloneContext(input map[string]string) map[string]string { - if len(input) == 0 { - return nil - } - output := make(map[string]string, len(input)) - for key, value := range input { - output[key] = value - } - return output -} - -func freeVectors(vectors []*vector.Vector, mp *mpool.MPool) { - for _, vector := range vectors { - if vector != nil { - vector.Free(mp) - } - } -} - func invocationTuple(context map[string]string, queryID string, accountID uint32) (protocol.FencingTuple, error) { statementID := context["statement_id"] if statementID == "" { @@ -347,14 +201,20 @@ func invocationTuple(context map[string]string, queryID string, accountID uint32 } statementID = id.String() } - groupID := context["group_id"] - if groupID == "" { - groupID = statementID + "/python" - } invocationID, err := uuid.NewV7() if err != nil { return protocol.FencingTuple{}, fmt.Errorf("python udf: create invocation fence: %w", err) } + groupID := context["group_id"] + if groupID == "" { + // The current physical adapter owns one group per invocation. A stable + // statement-only group would reject the second batch or second routine + // in the same statement; the scheduler may supply a shared group and + // explicit epoch when it is introduced. + groupID = statementID + "/python/" + invocationID.String() + } else if context["group_epoch"] == "" { + return protocol.FencingTuple{}, fmt.Errorf("python udf: explicit group_id requires group_epoch") + } groupEpoch, err := positiveContextUint(context, "group_epoch", 1) if err != nil { return protocol.FencingTuple{}, err @@ -373,6 +233,13 @@ func invocationTuple(context map[string]string, queryID string, accountID uint32 }, nil } +// NewInvocationTuple is shared by the physical external evaluator and protocol +// tests. A prepared plan supplies no execution identity; the caller provides +// the current query id and this helper creates a fresh invocation fence. +func NewInvocationTuple(context map[string]string, queryID string, accountID uint32) (protocol.FencingTuple, error) { + return invocationTuple(context, queryID, accountID) +} + func positiveContextUint(context map[string]string, key string, fallback uint64) (uint64, error) { value := context[key] if value == "" { diff --git a/pkg/sql/plan/function/udf.go b/pkg/sql/plan/function/udf.go index 980b8d4cb715c..f8702f89245a6 100644 --- a/pkg/sql/plan/function/udf.go +++ b/pkg/sql/plan/function/udf.go @@ -9,13 +9,15 @@ package function import ( + "crypto/sha256" + "encoding/hex" "encoding/json" "fmt" "io" "reflect" "strings" + "unicode/utf8" - "github.com/google/uuid" "github.com/matrixorigin/matrixone/pkg/container/types" "github.com/matrixorigin/matrixone/pkg/pb/plan" "github.com/matrixorigin/matrixone/pkg/udf" @@ -23,14 +25,29 @@ import ( ) type Udf struct { - Body string `json:"body"` - Language string `json:"language"` - RetType string `json:"rettype"` - Args []*Arg `json:"args"` - Db string `json:"db"` - ModifiedTime string `json:"modified_time"` - SQLMode *string `json:"-"` - ArgsType []types.Type `json:"-"` + Body string `json:"body"` + Language string `json:"language"` + RetType string `json:"rettype"` + Args []*Arg `json:"args"` + Db string `json:"db"` + ModifiedTime string `json:"modified_time"` + // FunctionID and Revision are resolved from the shared catalog. They are + // part of the executable identity; the planner must never re-resolve a + // Python routine by name or latest revision at execution time. + FunctionID int64 `json:"function_id,omitempty"` + AccountID uint64 `json:"account_id,omitempty"` + DatabaseID uint64 `json:"database_id,omitempty"` + Revision uint64 `json:"revision,omitempty"` + NamespaceVersion uint64 `json:"namespace_version,omitempty"` + // SQL revisions use the same immutable identity envelope as Python. These + // fields are empty for legacy SQL rows, which intentionally remain on the + // historical text-expansion path until they are explicitly replaced. + DefinitionFingerprint string `json:"definition_fingerprint,omitempty"` + SemanticDefinitionSchemaVersion int `json:"semantic_definition_schema_version,omitempty"` + Volatility string `json:"volatility,omitempty"` + NullPolicy string `json:"null_policy,omitempty"` + SQLMode *string `json:"-"` + ArgsType []types.Type `json:"-"` // PythonArgTypes and PythonReturnType preserve the exact SQL declaration // for Python's Arrow contract. The legacy catalog's args/rettype columns // intentionally keep only logical type names, which is insufficient for @@ -42,23 +59,80 @@ type Udf struct { type UdfWithContext struct { *Udf `json:"udf"` Context map[string]string `json:"context"` + // PlanContractVersion is retained only for the guarded bridge while the + // typed RoutineCall plan node is rolled out. A missing or unknown value is + // rejected before the handler is reached. + PlanContractVersion int `json:"plan_contract_version"` } // PythonRoutineBody is the single supported Python routine definition. The // catalog must persist mode and NULL policy with the source so execution does // not infer either from a generic SQL function class bit. type PythonRoutineBody struct { - Handler string `json:"handler"` - Source string `json:"source,omitempty"` - Mode string `json:"mode"` - NullPolicy string `json:"null_policy"` - ABIContract string `json:"abi_contract"` - AdapterVersion string `json:"adapter_version"` - ArtifactDigest string `json:"artifact_digest,omitempty"` - EnvironmentDigest string `json:"environment_digest,omitempty"` - SDKVersion string `json:"sdk_version,omitempty"` - ArgTypes []PythonTypeDescriptor `json:"arg_types,omitempty"` - ReturnType *PythonTypeDescriptor `json:"return_type,omitempty"` + DefinitionSchemaVersion int `json:"definition_schema_version"` + Handler string `json:"handler"` + Source string `json:"source,omitempty"` + Mode string `json:"mode"` + NullPolicy string `json:"null_policy"` + ABIContract string `json:"abi_contract"` + AdapterVersion string `json:"adapter_version"` + ArtifactDigest string `json:"artifact_digest"` + EnvironmentDigest string `json:"environment_digest"` + SDKVersion string `json:"sdk_version"` + ArgTypes []PythonTypeDescriptor `json:"arg_types,omitempty"` + ReturnType *PythonTypeDescriptor `json:"return_type,omitempty"` +} + +// PythonSignatureMetadata is the canonical identity metadata for a Python +// routine. The input descriptor is the overload key; the return descriptor is +// persisted alongside it because it is immutable for the lifetime of the +// Function identity even though it does not participate in overload choice. +// Both JSON values are canonical encodings of the exact Arrow descriptors. +func PythonSignatureMetadata( + args []PythonTypeDescriptor, + returnType *PythonTypeDescriptor, +) (canonicalInput, canonicalReturn, fingerprint string, err error) { + if returnType == nil { + return "", "", "", fmt.Errorf("python routine is missing return descriptor") + } + if args == nil { + args = []PythonTypeDescriptor{} + } + for index, descriptor := range args { + if err := validatePythonTypeDescriptor(descriptor); err != nil { + return "", "", "", fmt.Errorf("python routine argument descriptor %d: %w", index, err) + } + } + if err := validatePythonTypeDescriptor(*returnType); err != nil { + return "", "", "", fmt.Errorf("python routine return descriptor: %w", err) + } + inputBytes, err := json.Marshal(args) + if err != nil { + return "", "", "", fmt.Errorf("marshal Python input descriptor: %w", err) + } + returnBytes, err := json.Marshal(returnType) + if err != nil { + return "", "", "", fmt.Errorf("marshal Python return descriptor: %w", err) + } + if len(inputBytes) > types.MaxStringSize || len(returnBytes) > types.MaxStringSize { + return "", "", "", fmt.Errorf("python signature descriptor exceeds the %d-byte catalog limit", types.MaxStringSize) + } + keyBytes, err := json.Marshal(struct { + SchemaVersion int `json:"schema_version"` + Input []PythonTypeDescriptor `json:"input_descriptor"` + Return PythonTypeDescriptor `json:"return_descriptor"` + }{ + SchemaVersion: udf.PythonSignatureKeySchemaVersion, + Input: args, + Return: *returnType, + }) + if err != nil { + return "", "", "", fmt.Errorf("marshal Python signature key: %w", err) + } + digest := sha256.New() + digest.Write([]byte("matrixone-python-udf-signature\x00")) + digest.Write(keyBytes) + return string(inputBytes), string(returnBytes), hex.EncodeToString(digest.Sum(nil)), nil } // DecodePythonRoutineBody decodes the one catalog representation accepted by @@ -85,13 +159,89 @@ func DecodePythonRoutineBody(raw string) (PythonRoutineBody, error) { return body, nil } +// PythonRoutineFingerprint returns the digest of the canonical current +// definition. The digest is persisted with an immutable revision and is +// checked before dispatch; source text alone is not an executable identity. +func PythonRoutineFingerprint(raw string) (string, error) { + body, err := DecodePythonRoutineBody(raw) + if err != nil { + return "", err + } + return PythonRoutineBodyFingerprint(body) +} + +func PythonRoutineBodyFingerprint(body PythonRoutineBody) (string, error) { + if err := body.Validate(); err != nil { + return "", err + } + return pythonudf.DefinitionFingerprint( + body.DefinitionSchemaVersion, + body.Handler, body.Mode, body.NullPolicy, + body.ABIContract, body.AdapterVersion, + body.ArtifactDigest, body.EnvironmentDigest, body.SDKVersion, + body.ArgTypes, *body.ReturnType, + ) +} + +// SQLRoutineFingerprint is the integrity key for the current shared SQL +// revision row. The full typed SQL semantic IR is a later catalog field; +// until that field is published, the revision's canonical stored argument +// signature, return type, and SQL body form the bounded semantic definition +// accepted by the SQL binder. Keeping this encoding here makes the writer, +// resolver, and plan-cache validator use one implementation instead of +// independently hashing source text. +func SQLRoutineFingerprint(body, argTypes, returnType string) (string, error) { + if body == "" || returnType == "" { + return "", fmt.Errorf("SQL routine definition is incomplete") + } + encoded, err := json.Marshal(struct { + SchemaVersion int `json:"schema_version"` + Body string `json:"body"` + ArgTypes string `json:"arg_types"` + ReturnType string `json:"return_type"` + }{ + SchemaVersion: udf.SQLDefinitionSchemaVersion, + Body: body, + ArgTypes: argTypes, + ReturnType: returnType, + }) + if err != nil { + return "", fmt.Errorf("marshal SQL routine definition: %w", err) + } + digest := sha256.New() + digest.Write([]byte("matrixone-sql-udf-definition\x00")) + digest.Write(encoded) + return hex.EncodeToString(digest.Sum(nil)), nil +} + +// DecodeUdfWithContext is retained only as a diagnostic boundary for old +// persisted/demo plans. JSON is not an executable representation of a new +// Python routine, so callers must rebind the statement from the current typed +// catalog contract instead of decoding and dispatching this payload. +func DecodeUdfWithContext(raw []byte) (UdfWithContext, error) { + _ = raw + return UdfWithContext{}, fmt.Errorf("UNSUPPORTED_ROUTINE_VERSION: Python JSON plan is not executable; rebind the statement from the current typed catalog contract") +} + // Validate checks fields that are independent of the catalog argument list. // The argument count is checked by Udf.ValidatePythonTypeContract after the // legacy catalog args column has been decoded. func (body PythonRoutineBody) Validate() error { + if body.DefinitionSchemaVersion != udf.PythonDefinitionSchemaVersion { + return fmt.Errorf("UNSUPPORTED_ROUTINE_VERSION: python definition schema %d is not supported", body.DefinitionSchemaVersion) + } if body.Handler == "" || body.Source == "" { return fmt.Errorf("python routine handler and source are required") } + if !utf8.ValidString(body.Source) { + return fmt.Errorf("UNSUPPORTED_ROUTINE_VERSION: Python source is not valid UTF-8") + } + if int64(len([]byte(body.Source))) > pythonudf.DefaultMaxArtifactBytes { + return fmt.Errorf("RESOURCE_EXHAUSTED: Python source exceeds %d bytes", pythonudf.DefaultMaxArtifactBytes) + } + if strings.Contains(body.Handler, ":") { + return fmt.Errorf("UNSUPPORTED_ROUTINE_VERSION: Python external handler import requires an immutable artifact catalog") + } if body.Mode != "SCALAR" && body.Mode != "VECTOR" { return fmt.Errorf("python routine has unsupported mode %q", body.Mode) } @@ -104,6 +254,19 @@ func (body PythonRoutineBody) Validate() error { if body.SDKVersion != udf.PythonSDKVersion { return fmt.Errorf("python routine has unsupported SDK %q", body.SDKVersion) } + if !udf.IsSHA256Digest(body.ArtifactDigest) || body.EnvironmentDigest == "" { + return fmt.Errorf("UNSUPPORTED_ROUTINE_VERSION: Python definition has no immutable artifact/environment digest; use DROP and CREATE") + } + if body.ArtifactDigest != udf.PythonInlineArtifactDigest(body.Handler, body.Source) { + return fmt.Errorf("python routine artifact digest does not match its source") + } + environmentDigest, err := udf.PythonEnvironmentDigest() + if err != nil { + return fmt.Errorf("python routine environment digest is unavailable: %w", err) + } + if body.EnvironmentDigest != environmentDigest { + return fmt.Errorf("python routine environment digest does not match the current contract") + } if body.ReturnType == nil { return fmt.Errorf("python routine is missing return descriptor") } @@ -132,10 +295,124 @@ type Arg struct { Type string `json:"type"` } -func (u *Udf) GetPlanExpr() *plan.Expr { - uid, _ := uuid.NewV7() - data, _ := json.Marshal(&UdfWithContext{Udf: u, Context: map[string]string{"statement_id": uid.String()}}) - return &plan.Expr{Typ: *type2PlanType(types.T_text.ToType()), Expr: &plan.Expr_Lit{Lit: &plan.Literal{Isnull: false, Value: &plan.Literal_Sval{Sval: string(data)}}}} +// GetRoutineCall materializes the immutable typed call metadata used by the +// physical external evaluator. Statement identity and session values are +// deliberately absent: a prepared plan may outlive the statement that bound +// it, so the physical evaluator must construct those values from the current +// process at execution time. +func (u *Udf) GetRoutineCall() (*plan.RoutineCall, error) { + if u == nil { + return nil, fmt.Errorf("routine call requires a non-nil routine") + } + if strings.EqualFold(u.Language, udf.LanguageSQL) { + return u.getSQLRoutineCall() + } + if !strings.EqualFold(u.Language, udf.LanguagePython) { + return nil, fmt.Errorf("routine call has unsupported language %q", u.Language) + } + if u.FunctionID <= 0 || u.DatabaseID == 0 || u.Revision == 0 || u.NamespaceVersion == 0 { + return nil, fmt.Errorf("python routine has no stable catalog identity or revision") + } + body, err := DecodePythonRoutineBody(u.Body) + if err != nil { + return nil, err + } + fingerprint, err := PythonRoutineFingerprint(u.Body) + if err != nil { + return nil, err + } + // The decoded immutable body is the source of truth for the executable + // Arrow signature. Do not rebuild it from the mutable legacy Udf fields; + // doing so could produce a plan whose fingerprint describes one signature + // while its argument types describe another damaged catalog row. + argTypes := make([]types.Type, len(body.ArgTypes)) + for i, descriptor := range body.ArgTypes { + argTypes[i] = descriptor.Type() + } + arguments := make([]*plan.Type, len(argTypes)) + for i, typ := range argTypes { + arguments[i] = type2PlanType(typ) + } + returnType := type2PlanType(body.ReturnType.Type()) + return &plan.RoutineCall{ + ContractVersion: udf.PythonPlanContractVersion, + FunctionRef: &plan.FunctionRef{ + FunctionId: uint64(u.FunctionID), + Revision: u.Revision, + NamespaceVersion: u.NamespaceVersion, + AccountId: u.AccountID, + DatabaseId: u.DatabaseID, + }, + Language: strings.ToLower(u.Language), + ArgumentTypes: arguments, + ReturnType: *returnType, + Volatility: "VOLATILE", + NullPolicy: body.NullPolicy, + MayError: true, + SecurityMode: "INVOKER", + Leakproof: false, + Implementation: &plan.RoutineCall_Python{Python: &plan.PythonRoutineImplementation{ + Handler: body.Handler, + AbiContract: body.ABIContract, + AdapterVersion: body.AdapterVersion, + ArtifactDigest: body.ArtifactDigest, + EnvironmentDigest: body.EnvironmentDigest, + SdkVersion: body.SDKVersion, + DefinitionSchemaVersion: int32(body.DefinitionSchemaVersion), + Mode: body.Mode, + NullPolicy: body.NullPolicy, + DefinitionFingerprint: fingerprint, + }}, + }, nil +} + +// getSQLRoutineCall builds the common catalog envelope for a current SQL +// revision. SQL's existing expression lowering still expands the body at +// bind time, but the exact revision and integrity key are retained in the +// plan dependency so replace/drop cannot silently reuse the old definition. +func (u *Udf) getSQLRoutineCall() (*plan.RoutineCall, error) { + if u.FunctionID <= 0 || u.DatabaseID == 0 || u.Revision == 0 || u.NamespaceVersion == 0 { + return nil, fmt.Errorf("SQL routine has no stable catalog identity or revision") + } + if u.SemanticDefinitionSchemaVersion != udf.SQLDefinitionSchemaVersion || !udf.IsSHA256Digest(u.DefinitionFingerprint) { + return nil, fmt.Errorf("UNSUPPORTED_ROUTINE_VERSION: SQL routine has no supported semantic definition") + } + argTypes := u.GetArgsType() + arguments := make([]*plan.Type, len(argTypes)) + for index, typ := range argTypes { + arguments[index] = type2PlanType(typ) + } + returnType := type2PlanType(u.GetRetType()) + volatility := u.Volatility + if volatility == "" { + volatility = "VOLATILE" + } + nullPolicy := u.NullPolicy + if nullPolicy == "" { + nullPolicy = udf.NullCallHandler + } + return &plan.RoutineCall{ + ContractVersion: udf.RoutinePlanContractVersion, + FunctionRef: &plan.FunctionRef{ + FunctionId: uint64(u.FunctionID), + Revision: u.Revision, + NamespaceVersion: u.NamespaceVersion, + AccountId: u.AccountID, + DatabaseId: u.DatabaseID, + }, + Language: udf.LanguageSQL, + ArgumentTypes: arguments, + ReturnType: *returnType, + Volatility: volatility, + NullPolicy: nullPolicy, + MayError: true, + SecurityMode: "DEFINER", + Leakproof: false, + Implementation: &plan.RoutineCall_Sql{Sql: &plan.SqlRoutineImplementation{ + DefinitionFingerprint: []byte(u.DefinitionFingerprint), + SemanticDefinitionSchemaVersion: int32(u.SemanticDefinitionSchemaVersion), + }}, + }, nil } func (u *Udf) GetArgsPlanType() []*plan.Type { @@ -148,7 +425,15 @@ func (u *Udf) GetArgsPlanType() []*plan.Type { } func (u *Udf) GetRetPlanType() *plan.Type { return type2PlanType(u.GetRetType()) } func (u *Udf) GetArgsType() []types.Type { - if u.Language == "python" { + if strings.EqualFold(u.Language, "python") { + // Resolver-created routines always set PythonReturnType when the + // canonical descriptor has been loaded. Keeping the fallback makes + // the generic Udf value usable by planner tests and by the SQL-side + // preflight checks without allowing a real Python call to skip body + // validation: GetRoutineCall decodes and validates Body again. + if u.PythonReturnType == nil && u.PythonArgTypes == nil { + return u.ArgsType + } args := make([]types.Type, len(u.PythonArgTypes)) for i, descriptor := range u.PythonArgTypes { args[i] = descriptor.Type() @@ -158,9 +443,9 @@ func (u *Udf) GetArgsType() []types.Type { return u.ArgsType } func (u *Udf) GetRetType() types.Type { - if u.Language == "python" { + if strings.EqualFold(u.Language, "python") { if u.PythonReturnType == nil { - return types.Type{} + return types.Types[u.RetType].ToType() } return u.PythonReturnType.Type() } @@ -172,7 +457,7 @@ func (u *Udf) GetRetType() types.Type { // rejected instead of being reinterpreted through the legacy logical-type // columns. func (u *Udf) LoadPythonTypeContract() error { - if u.Language != "python" { + if !strings.EqualFold(u.Language, "python") { return nil } u.PythonArgTypes = nil @@ -192,7 +477,7 @@ func (u *Udf) LoadPythonTypeContract() error { // before that decode during lookup, so this second check closes the only point // where an argument list and its exact ABI could otherwise diverge. func (u *Udf) ValidatePythonTypeContract() error { - if u.Language != "python" { + if !strings.EqualFold(u.Language, "python") { return nil } if u.PythonReturnType == nil { @@ -209,6 +494,42 @@ func (u *Udf) ValidatePythonTypeContract() error { return validatePythonTypeDescriptor(*u.PythonReturnType) } +// ValidatePythonCatalogSignature verifies the logical type columns that are +// retained by mo_user_defined_function/mo_function_revisions against the +// exact Arrow descriptors in the Python implementation. The legacy columns +// intentionally omit width and scale, so they cannot replace the descriptor; +// they still must agree on the logical OID or a damaged catalog row could be +// selected by overload resolution and dispatched with a different ABI. +func (u *Udf) ValidatePythonCatalogSignature() error { + if u == nil || !strings.EqualFold(u.Language, "python") { + return nil + } + if len(u.Args) != len(u.PythonArgTypes) || u.PythonReturnType == nil { + return fmt.Errorf("UNSUPPORTED_ROUTINE_VERSION: Python catalog signature is incomplete") + } + for index, argument := range u.Args { + if argument == nil || !pythonCatalogTypeNameMatches(argument.Type, u.PythonArgTypes[index].Type().Oid) { + name := "" + if argument != nil { + name = argument.Type + } + return fmt.Errorf("UNSUPPORTED_ROUTINE_VERSION: Python catalog argument %d type %q does not match its descriptor", index+1, name) + } + } + if !pythonCatalogTypeNameMatches(u.RetType, u.PythonReturnType.Type().Oid) { + return fmt.Errorf("UNSUPPORTED_ROUTINE_VERSION: Python catalog return type %q does not match its descriptor", u.RetType) + } + return nil +} + +func pythonCatalogTypeNameMatches(name string, oid types.T) bool { + name = strings.ToLower(strings.TrimSpace(name)) + if name == "decimal" { + return oid == types.T_decimal64 || oid == types.T_decimal128 + } + return name == strings.ToLower(oid.String()) +} + func validatePythonTypeDescriptor(descriptor PythonTypeDescriptor) error { typ := descriptor.Type() canonical, err := NewPythonTypeDescriptor(typ) diff --git a/pkg/sql/plan/function/udf_test.go b/pkg/sql/plan/function/udf_test.go index a5844164906ff..36d90a1b3057c 100644 --- a/pkg/sql/plan/function/udf_test.go +++ b/pkg/sql/plan/function/udf_test.go @@ -21,10 +21,17 @@ import ( "github.com/matrixorigin/matrixone/pkg/container/types" "github.com/matrixorigin/matrixone/pkg/container/vector" + planpb "github.com/matrixorigin/matrixone/pkg/pb/plan" "github.com/matrixorigin/matrixone/pkg/udf" "github.com/stretchr/testify/require" ) +func pythonTestBody(body PythonRoutineBody) PythonRoutineBody { + body.ArtifactDigest = udf.PythonInlineArtifactDigest(body.Handler, body.Source) + body.EnvironmentDigest, _ = udf.PythonEnvironmentDigest() + return body +} + func TestPythonTypeContractPreservesDeclaredShape(t *testing.T) { argument := types.New(types.T_decimal64, 18, 6) returnType := types.New(types.T_varchar, 64, 0) @@ -32,36 +39,115 @@ func TestPythonTypeContractPreservesDeclaredShape(t *testing.T) { require.NoError(t, err) returnDescriptor, err := NewPythonTypeDescriptor(returnType) require.NoError(t, err) - body, err := json.Marshal(PythonRoutineBody{ - Handler: "identity", - Source: "def identity(ctx, x): return x", - Mode: "SCALAR", - NullPolicy: udf.NullCallHandler, - ABIContract: udf.PythonABIContract, - AdapterVersion: udf.PythonAdapterVersion, - SDKVersion: udf.PythonSDKVersion, - ArgTypes: []PythonTypeDescriptor{argumentDescriptor}, - ReturnType: &returnDescriptor, - }) + body, err := json.Marshal(pythonTestBody(PythonRoutineBody{ + DefinitionSchemaVersion: udf.PythonDefinitionSchemaVersion, + Handler: "identity", + Source: "def identity(ctx, x): return x", + Mode: "SCALAR", + NullPolicy: udf.NullCallHandler, + ABIContract: udf.PythonABIContract, + AdapterVersion: udf.PythonAdapterVersion, + SDKVersion: udf.PythonSDKVersion, + ArgTypes: []PythonTypeDescriptor{argumentDescriptor}, + ReturnType: &returnDescriptor, + })) require.NoError(t, err) - routine := &Udf{Language: "python", Body: string(body), ArgsType: []types.Type{types.T_decimal64.ToType()}} + routine := &Udf{Language: "python", Body: string(body), RetType: "varchar", ArgsType: []types.Type{types.T_decimal64.ToType()}} require.NoError(t, routine.LoadPythonTypeContract()) routine.Args = []*Arg{{Type: "decimal"}} require.NoError(t, routine.ValidatePythonTypeContract()) + require.NoError(t, routine.ValidatePythonCatalogSignature()) require.Equal(t, argument, routine.GetArgsType()[0]) require.Equal(t, returnType, routine.GetRetType()) require.Equal(t, int32(6), routine.GetArgsPlanType()[0].Scale) require.Equal(t, int32(64), routine.GetRetPlanType().Width) } +func TestPythonSignatureMetadataIsCanonicalAndSensitiveToShape(t *testing.T) { + argument, err := NewPythonTypeDescriptor(types.New(types.T_decimal64, 18, 2)) + require.NoError(t, err) + returnType, err := NewPythonTypeDescriptor(types.T_varchar.ToType()) + require.NoError(t, err) + input, output, fingerprint, err := PythonSignatureMetadata([]PythonTypeDescriptor{argument}, &returnType) + require.NoError(t, err) + require.Equal(t, `[{"type_id":32,"width":18,"scale":2,"offset_width":32}]`, input) + encodedReturn, err := json.Marshal(returnType) + require.NoError(t, err) + require.Equal(t, string(encodedReturn), output) + require.Len(t, fingerprint, 64) + + otherArgument, err := NewPythonTypeDescriptor(types.New(types.T_decimal64, 18, 6)) + require.NoError(t, err) + _, _, otherFingerprint, err := PythonSignatureMetadata([]PythonTypeDescriptor{otherArgument}, &returnType) + require.NoError(t, err) + require.NotEqual(t, fingerprint, otherFingerprint) +} + +func TestPythonUdfArgTypeMatchPrefersExactDescriptor(t *testing.T) { + source := types.New(types.T_decimal64, 18, 2) + exact := types.New(types.T_decimal64, 18, 2) + coerce := types.New(types.T_decimal64, 18, 6) + exactOK, exactCost := PythonUdfArgTypeMatch([]types.Type{source}, []types.Type{exact}) + coerceOK, coerceCost := PythonUdfArgTypeMatch([]types.Type{source}, []types.Type{coerce}) + require.True(t, exactOK) + require.True(t, coerceOK) + require.Less(t, exactCost, coerceCost) +} + +func TestPythonUdfArgTypeCastKeepsDeclaredDescriptorForSameOID(t *testing.T) { + source := types.New(types.T_decimal64, 18, 2) + target := types.New(types.T_decimal64, 18, 6) + + castTypes := PythonUdfArgTypeCast([]types.Type{source}, []types.Type{target}) + require.Equal(t, []types.Type{target}, castTypes) + require.Nil(t, PythonUdfArgTypeCast([]types.Type{source}, nil)) +} + +func TestPythonCatalogSignatureRejectsLogicalDescriptorDrift(t *testing.T) { + argument, err := NewPythonTypeDescriptor(types.T_int64.ToType()) + require.NoError(t, err) + returnType, err := NewPythonTypeDescriptor(types.T_int64.ToType()) + require.NoError(t, err) + raw, err := json.Marshal(pythonTestBody(PythonRoutineBody{ + DefinitionSchemaVersion: udf.PythonDefinitionSchemaVersion, + Handler: "identity", + Source: "def identity(ctx, value): return value", + Mode: "SCALAR", + NullPolicy: udf.NullCallHandler, + ABIContract: udf.PythonABIContract, + AdapterVersion: udf.PythonAdapterVersion, + SDKVersion: udf.PythonSDKVersion, + ArgTypes: []PythonTypeDescriptor{argument}, + ReturnType: &returnType, + })) + require.NoError(t, err) + routine := &Udf{Language: "python", Body: string(raw), RetType: "bigint"} + require.NoError(t, routine.LoadPythonTypeContract()) + routine.Args = []*Arg{{Type: "varchar"}} + require.ErrorContains(t, routine.ValidatePythonCatalogSignature(), "argument 1") + + routine.Args[0].Type = "bigint" + routine.RetType = "varchar" + require.ErrorContains(t, routine.ValidatePythonCatalogSignature(), "return type") +} + func TestPythonTypeContractRejectsMissingReturnDescriptor(t *testing.T) { + body := pythonTestBody(PythonRoutineBody{ + DefinitionSchemaVersion: udf.PythonDefinitionSchemaVersion, + Handler: "legacy", + Source: "def legacy(ctx, x): return x", + Mode: "SCALAR", + NullPolicy: udf.NullCallHandler, + ABIContract: udf.PythonABIContract, + AdapterVersion: udf.PythonAdapterVersion, + SDKVersion: udf.PythonSDKVersion, + }) + rawBody, err := json.Marshal(body) + require.NoError(t, err) routine := &Udf{ Language: "python", - Body: `{"handler":"legacy","source":"def legacy(ctx, x): return x",` + - `"mode":"SCALAR","null_policy":"CALLED_ON_NULL_INPUT",` + - `"abi_contract":"PYTHON_ARROW","adapter_version":"2026-09",` + - `"sdk_version":"1.0"}`, + Body: string(rawBody), ArgsType: []types.Type{types.T_int32.ToType()}, RetType: "int", } @@ -73,17 +159,18 @@ func TestPythonTypeContractRejectsArgumentCountMismatch(t *testing.T) { require.NoError(t, err) returnType, err := NewPythonTypeDescriptor(types.T_int32.ToType()) require.NoError(t, err) - body, err := json.Marshal(PythonRoutineBody{ - Handler: "identity", - Source: "def identity(ctx, x): return x", - Mode: "SCALAR", - NullPolicy: udf.NullCallHandler, - ABIContract: udf.PythonABIContract, - AdapterVersion: udf.PythonAdapterVersion, - SDKVersion: udf.PythonSDKVersion, - ArgTypes: []PythonTypeDescriptor{argument}, - ReturnType: &returnType, - }) + body, err := json.Marshal(pythonTestBody(PythonRoutineBody{ + DefinitionSchemaVersion: udf.PythonDefinitionSchemaVersion, + Handler: "identity", + Source: "def identity(ctx, x): return x", + Mode: "SCALAR", + NullPolicy: udf.NullCallHandler, + ABIContract: udf.PythonABIContract, + AdapterVersion: udf.PythonAdapterVersion, + SDKVersion: udf.PythonSDKVersion, + ArgTypes: []PythonTypeDescriptor{argument}, + ReturnType: &returnType, + })) require.NoError(t, err) routine := &Udf{Language: "python", Body: string(body)} require.NoError(t, routine.LoadPythonTypeContract()) @@ -95,17 +182,18 @@ func TestPythonTypeContractReloadReplacesDescriptors(t *testing.T) { require.NoError(t, err) returnType, err := NewPythonTypeDescriptor(types.T_int32.ToType()) require.NoError(t, err) - body, err := json.Marshal(PythonRoutineBody{ - Handler: "identity", - Source: "def identity(ctx, x): return x", - Mode: "SCALAR", - NullPolicy: udf.NullCallHandler, - ABIContract: udf.PythonABIContract, - AdapterVersion: udf.PythonAdapterVersion, - SDKVersion: udf.PythonSDKVersion, - ArgTypes: []PythonTypeDescriptor{argument}, - ReturnType: &returnType, - }) + body, err := json.Marshal(pythonTestBody(PythonRoutineBody{ + DefinitionSchemaVersion: udf.PythonDefinitionSchemaVersion, + Handler: "identity", + Source: "def identity(ctx, x): return x", + Mode: "SCALAR", + NullPolicy: udf.NullCallHandler, + ABIContract: udf.PythonABIContract, + AdapterVersion: udf.PythonAdapterVersion, + SDKVersion: udf.PythonSDKVersion, + ArgTypes: []PythonTypeDescriptor{argument}, + ReturnType: &returnType, + })) require.NoError(t, err) routine := &Udf{Language: "python", Body: string(body)} require.NoError(t, routine.LoadPythonTypeContract()) @@ -114,16 +202,17 @@ func TestPythonTypeContractReloadReplacesDescriptors(t *testing.T) { } func TestPythonRoutineBodyRejectsUnknownAndTrailingData(t *testing.T) { - valid, err := json.Marshal(PythonRoutineBody{ - Handler: "identity", - Source: "def identity(ctx): return 1", - Mode: "SCALAR", - NullPolicy: udf.NullCallHandler, - ABIContract: udf.PythonABIContract, - AdapterVersion: udf.PythonAdapterVersion, - SDKVersion: udf.PythonSDKVersion, - ReturnType: func() *PythonTypeDescriptor { d, _ := NewPythonTypeDescriptor(types.T_int32.ToType()); return &d }(), - }) + valid, err := json.Marshal(pythonTestBody(PythonRoutineBody{ + DefinitionSchemaVersion: udf.PythonDefinitionSchemaVersion, + Handler: "identity", + Source: "def identity(ctx): return 1", + Mode: "SCALAR", + NullPolicy: udf.NullCallHandler, + ABIContract: udf.PythonABIContract, + AdapterVersion: udf.PythonAdapterVersion, + SDKVersion: udf.PythonSDKVersion, + ReturnType: func() *PythonTypeDescriptor { d, _ := NewPythonTypeDescriptor(types.T_int32.ToType()); return &d }(), + })) require.NoError(t, err) var withUnknown map[string]any @@ -141,16 +230,17 @@ func TestPythonTypeContractRejectsUnrepresentableArrowPrecision(t *testing.T) { Width: 39, OffsetWidth: 32, } - body, err := json.Marshal(PythonRoutineBody{ - Handler: "identity", - Source: "def identity(ctx): return 1", - Mode: "SCALAR", - NullPolicy: udf.NullCallHandler, - ABIContract: udf.PythonABIContract, - AdapterVersion: udf.PythonAdapterVersion, - SDKVersion: udf.PythonSDKVersion, - ReturnType: &returnDescriptor, - }) + body, err := json.Marshal(pythonTestBody(PythonRoutineBody{ + DefinitionSchemaVersion: udf.PythonDefinitionSchemaVersion, + Handler: "identity", + Source: "def identity(ctx): return 1", + Mode: "SCALAR", + NullPolicy: udf.NullCallHandler, + ABIContract: udf.PythonABIContract, + AdapterVersion: udf.PythonAdapterVersion, + SDKVersion: udf.PythonSDKVersion, + ReturnType: &returnDescriptor, + })) require.NoError(t, err) routine := &Udf{Language: "python", Body: string(body)} require.ErrorContains(t, routine.LoadPythonTypeContract(), "precision") @@ -202,3 +292,117 @@ func TestPythonRoutineIsVolatile(t *testing.T) { _, overloadID := DecodeOverloadID(resolved.GetEncodedOverloadID()) require.True(t, allSupportedFunctions[PYTHON_UDF].Overloads[overloadID].CannotFold()) } + +func TestPythonRoutineCallCarriesExactCatalogContract(t *testing.T) { + descriptor, err := NewPythonTypeDescriptor(types.T_int64.ToType()) + require.NoError(t, err) + body, err := json.Marshal(pythonTestBody(PythonRoutineBody{ + DefinitionSchemaVersion: udf.PythonDefinitionSchemaVersion, + Handler: "identity", + Source: "def identity(ctx, value): return value", + Mode: "SCALAR", + NullPolicy: udf.NullCallHandler, + ABIContract: udf.PythonABIContract, + AdapterVersion: udf.PythonAdapterVersion, + SDKVersion: udf.PythonSDKVersion, + ArgTypes: []PythonTypeDescriptor{descriptor}, + ReturnType: &descriptor, + })) + require.NoError(t, err) + + routine := &Udf{ + FunctionID: 41, + AccountID: 7, + DatabaseID: 8, + Revision: 7, + NamespaceVersion: 12, + Language: "python", + Body: string(body), + Args: []*Arg{{Name: "value", Type: "bigint"}}, + } + require.NoError(t, routine.LoadPythonTypeContract()) + require.NoError(t, routine.ValidatePythonTypeContract()) + + call, err := routine.GetRoutineCall() + require.NoError(t, err) + require.Equal(t, int32(udf.PythonPlanContractVersion), call.ContractVersion) + require.Equal(t, &planpb.FunctionRef{FunctionId: 41, Revision: 7, NamespaceVersion: 12, AccountId: 7, DatabaseId: 8}, call.FunctionRef) + require.Equal(t, "python", call.Language) + require.Equal(t, "VOLATILE", call.Volatility) + require.Equal(t, udf.NullCallHandler, call.NullPolicy) + require.True(t, call.MayError) + require.Equal(t, "INVOKER", call.SecurityMode) + require.False(t, call.Leakproof) + require.Empty(t, call.Context) + require.Empty(t, call.GetPython().Source, "source is catalog payload, never executable plan state") + require.NotEmpty(t, call.GetPython().DefinitionFingerprint) +} + +func TestSQLRoutineFingerprintAndCallUseSharedRevisionContract(t *testing.T) { + fingerprint, err := SQLRoutineFingerprint("select value + 1", "[\"bigint\"]", "bigint") + require.NoError(t, err) + require.Len(t, fingerprint, 64) + changed, err := SQLRoutineFingerprint("select value + 2", "[\"bigint\"]", "bigint") + require.NoError(t, err) + require.NotEqual(t, fingerprint, changed) + + routine := &Udf{ + FunctionID: 41, + AccountID: 7, + DatabaseID: 8, + Revision: 3, + NamespaceVersion: 4, + Language: udf.LanguageSQL, + RetType: "bigint", + ArgsType: []types.Type{types.T_int64.ToType()}, + DefinitionFingerprint: fingerprint, + SemanticDefinitionSchemaVersion: udf.SQLDefinitionSchemaVersion, + Volatility: "VOLATILE", + NullPolicy: udf.NullCallHandler, + } + call, err := routine.GetRoutineCall() + require.NoError(t, err) + require.Equal(t, udf.LanguageSQL, call.Language) + require.Equal(t, &planpb.FunctionRef{ + FunctionId: 41, Revision: 3, NamespaceVersion: 4, AccountId: 7, DatabaseId: 8, + }, call.FunctionRef) + require.Equal(t, fingerprint, string(call.GetSql().DefinitionFingerprint)) + require.Equal(t, int32(udf.SQLDefinitionSchemaVersion), call.GetSql().SemanticDefinitionSchemaVersion) + require.True(t, call.MayError) + require.Equal(t, "DEFINER", call.SecurityMode) + require.False(t, call.Leakproof) +} + +func TestPythonJSONPlanRequiresExactIdentity(t *testing.T) { + raw, err := json.Marshal(UdfWithContext{ + Udf: &Udf{ + Language: "python", + Body: `{"legacy":true}`, + }, + PlanContractVersion: udf.PythonPlanContractVersion, + }) + require.NoError(t, err) + _, err = DecodeUdfWithContext(raw) + require.ErrorContains(t, err, "JSON plan is not executable") +} + +func TestPythonInvocationTupleUsesOneShotGroupsByDefault(t *testing.T) { + first, err := NewInvocationTuple(nil, "query-1", 7) + require.NoError(t, err) + second, err := NewInvocationTuple(nil, "query-1", 7) + require.NoError(t, err) + require.NotEqual(t, first.InvocationID, second.InvocationID) + require.NotEqual(t, first.GroupID, second.GroupID) + require.Equal(t, first.GroupEpoch, uint64(1)) + require.Equal(t, second.GroupEpoch, uint64(1)) + + _, err = NewInvocationTuple(map[string]string{"group_id": "shared-group"}, "query-1", 7) + require.ErrorContains(t, err, "explicit group_id requires group_epoch") + shared, err := NewInvocationTuple(map[string]string{ + "group_id": "shared-group", + "group_epoch": "3", + }, "query-1", 7) + require.NoError(t, err) + require.Equal(t, "shared-group", shared.GroupID) + require.Equal(t, uint64(3), shared.GroupEpoch) +} diff --git a/pkg/sql/plan/returning.go b/pkg/sql/plan/returning.go index 9f9df4d17f32c..6408ab2f54bf4 100644 --- a/pkg/sql/plan/returning.go +++ b/pkg/sql/plan/returning.go @@ -329,6 +329,9 @@ func (builder *QueryBuilder) appendReturningProjection(exprs tree.SelectExprs, b if len(exprs) == 0 { return nil } + previousReturningBinding := builder.bindingReturningProjection + builder.bindingReturningProjection = true + defer func() { builder.bindingReturningProjection = previousReturningBinding }() if builder.returningSourceStep < 0 || builder.returningTableDef == nil { return moerr.NewInternalError(builder.GetContext(), "DML RETURNING row image was not materialized") } diff --git a/pkg/sql/plan/routine_dependency.go b/pkg/sql/plan/routine_dependency.go new file mode 100644 index 0000000000000..3245686641d67 --- /dev/null +++ b/pkg/sql/plan/routine_dependency.go @@ -0,0 +1,105 @@ +// Copyright 2026 Matrix Origin +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. + +package plan + +import ( + "fmt" + "strings" + + planpb "github.com/matrixorigin/matrixone/pkg/pb/plan" +) + +// maxRoutinePlanDependencies bounds the amount of catalog state retained by +// one executable plan. A plan with more distinct routine revisions is rejected +// during binding instead of creating an unbounded cache invalidation payload. +const maxRoutinePlanDependencies = 1024 + +// assignRoutineCallsite gives a bound routine expression a stable identity +// within its plan. It is not a function identity and is never used for +// catalog lookup; it lets the physical stage, diagnostics and future +// scheduler account for two references to the same revision independently. +func (builder *QueryBuilder) assignRoutineCallsite(call *planpb.RoutineCall) error { + if builder == nil || call == nil { + return nil + } + if call.CallsiteId != "" { + if len(call.CallsiteId) > 256 || strings.ContainsAny(call.CallsiteId, "\r\n") { + return fmt.Errorf("PROGRAM_LIMIT_EXCEEDED: routine callsite id is too large or invalid") + } + return nil + } + if builder.nextRoutineCallID == ^uint64(0) { + return fmt.Errorf("PROGRAM_LIMIT_EXCEEDED: routine callsite id exhausted") + } + builder.nextRoutineCallID++ + call.CallsiteId = fmt.Sprintf("%s/%d", strings.ToLower(call.Language), builder.nextRoutineCallID) + return nil +} + +func (builder *QueryBuilder) recordRoutinePlanDependency(call *planpb.RoutineCall) error { + if builder == nil || builder.qry == nil || call == nil || call.FunctionRef == nil { + return nil + } + ref := call.FunctionRef + dependency := &planpb.RoutinePlanDependency{ + FunctionRef: &planpb.FunctionRef{ + FunctionId: ref.FunctionId, + Revision: ref.Revision, + NamespaceVersion: ref.NamespaceVersion, + AccountId: ref.AccountId, + DatabaseId: ref.DatabaseId, + }, + Language: strings.ToLower(call.Language), + ContractVersion: call.ContractVersion, + DefinitionFingerprint: "", + Volatility: call.Volatility, + NullPolicy: call.NullPolicy, + MayError: call.MayError, + SecurityMode: call.SecurityMode, + Leakproof: call.Leakproof, + } + if python := call.GetPython(); python != nil { + dependency.DefinitionFingerprint = python.DefinitionFingerprint + dependency.ArtifactDigest = python.ArtifactDigest + dependency.EnvironmentDigest = python.EnvironmentDigest + } else if sql := call.GetSql(); sql != nil { + // SQL uses the same dependency envelope. The implementation digest is + // stored as the canonical hexadecimal SHA-256 string so cache + // validation has the same representation across languages. + dependency.DefinitionFingerprint = string(sql.DefinitionFingerprint) + } + for _, existing := range builder.qry.RoutineDependencies { + if routinePlanDependenciesEqual(existing, dependency) { + return nil + } + } + if len(builder.qry.RoutineDependencies) >= maxRoutinePlanDependencies { + return fmt.Errorf("PROGRAM_LIMIT_EXCEEDED: routine dependency count exceeds %d", maxRoutinePlanDependencies) + } + builder.qry.RoutineDependencies = append(builder.qry.RoutineDependencies, dependency) + return nil +} + +func routinePlanDependenciesEqual(left, right *planpb.RoutinePlanDependency) bool { + if left == nil || right == nil || left.FunctionRef == nil || right.FunctionRef == nil { + return left == right + } + return left.Language == right.Language && + left.ContractVersion == right.ContractVersion && + left.DefinitionFingerprint == right.DefinitionFingerprint && + left.ArtifactDigest == right.ArtifactDigest && + left.EnvironmentDigest == right.EnvironmentDigest && + left.Volatility == right.Volatility && + left.NullPolicy == right.NullPolicy && + left.MayError == right.MayError && + left.SecurityMode == right.SecurityMode && + left.Leakproof == right.Leakproof && + left.FunctionRef.FunctionId == right.FunctionRef.FunctionId && + left.FunctionRef.Revision == right.FunctionRef.Revision && + left.FunctionRef.NamespaceVersion == right.FunctionRef.NamespaceVersion && + left.FunctionRef.AccountId == right.FunctionRef.AccountId && + left.FunctionRef.DatabaseId == right.FunctionRef.DatabaseId +} diff --git a/pkg/sql/plan/routine_dependency_test.go b/pkg/sql/plan/routine_dependency_test.go new file mode 100644 index 0000000000000..4f49ada054540 --- /dev/null +++ b/pkg/sql/plan/routine_dependency_test.go @@ -0,0 +1,120 @@ +// Copyright 2026 Matrix Origin +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package plan + +import ( + "testing" + + planpb "github.com/matrixorigin/matrixone/pkg/pb/plan" + "github.com/matrixorigin/matrixone/pkg/udf" + "github.com/stretchr/testify/require" +) + +func testRoutineCallForDependency() *planpb.RoutineCall { + return &planpb.RoutineCall{ + ContractVersion: udf.PythonPlanContractVersion, + FunctionRef: &planpb.FunctionRef{ + FunctionId: 11, + Revision: 3, + NamespaceVersion: 4, + AccountId: 5, + DatabaseId: 6, + }, + Language: udf.LanguagePython, + Volatility: "VOLATILE", + NullPolicy: udf.NullCallHandler, + MayError: true, + SecurityMode: "INVOKER", + Leakproof: false, + Implementation: &planpb.RoutineCall_Python{Python: &planpb.PythonRoutineImplementation{ + DefinitionFingerprint: "fingerprint", + ArtifactDigest: "artifact", + EnvironmentDigest: "environment", + }}, + } +} + +func TestRecordRoutinePlanDependencyDeduplicatesAndCopiesIdentity(t *testing.T) { + builder := &QueryBuilder{qry: &planpb.Query{}} + call := testRoutineCallForDependency() + + require.NoError(t, builder.recordRoutinePlanDependency(call)) + require.NoError(t, builder.recordRoutinePlanDependency(call)) + require.Len(t, builder.qry.RoutineDependencies, 1) + + dependency := builder.qry.RoutineDependencies[0] + require.Equal(t, uint64(11), dependency.FunctionRef.FunctionId) + require.Equal(t, uint64(3), dependency.FunctionRef.Revision) + require.Equal(t, "fingerprint", dependency.DefinitionFingerprint) + require.Equal(t, "artifact", dependency.ArtifactDigest) + require.Equal(t, "environment", dependency.EnvironmentDigest) + require.Equal(t, "VOLATILE", dependency.Volatility) + require.Equal(t, udf.NullCallHandler, dependency.NullPolicy) + require.True(t, dependency.MayError) + require.Equal(t, "INVOKER", dependency.SecurityMode) + require.False(t, dependency.Leakproof) + + // The envelope is a plan-owned snapshot of the call identity. Mutating the + // source FunctionRef after binding must not mutate cache validation state. + call.FunctionRef.Revision = 99 + require.Equal(t, uint64(3), dependency.FunctionRef.Revision) +} + +func TestAssignRoutineCallsiteIsPlanLocalAndIdempotent(t *testing.T) { + builder := &QueryBuilder{qry: &planpb.Query{}} + call := testRoutineCallForDependency() + require.NoError(t, builder.assignRoutineCallsite(call)) + require.Equal(t, "python/1", call.CallsiteId) + require.NoError(t, builder.assignRoutineCallsite(call)) + require.Equal(t, "python/1", call.CallsiteId) + + second := testRoutineCallForDependency() + require.NoError(t, builder.assignRoutineCallsite(second)) + require.Equal(t, "python/2", second.CallsiteId) +} + +func TestRecordRoutinePlanDependencyBoundsPlanClosure(t *testing.T) { + builder := &QueryBuilder{qry: &planpb.Query{ + RoutineDependencies: make([]*planpb.RoutinePlanDependency, maxRoutinePlanDependencies), + }} + + err := builder.recordRoutinePlanDependency(testRoutineCallForDependency()) + require.ErrorContains(t, err, "PROGRAM_LIMIT_EXCEEDED") +} + +func TestRecordSQLRoutinePlanDependencyUsesSharedEnvelope(t *testing.T) { + builder := &QueryBuilder{qry: &planpb.Query{}} + call := &planpb.RoutineCall{ + ContractVersion: udf.RoutinePlanContractVersion, + FunctionRef: &planpb.FunctionRef{ + FunctionId: 11, Revision: 3, NamespaceVersion: 4, AccountId: 5, DatabaseId: 6, + }, + Language: udf.LanguageSQL, + Volatility: "VOLATILE", + NullPolicy: udf.NullCallHandler, + MayError: true, + SecurityMode: "DEFINER", + Leakproof: false, + Implementation: &planpb.RoutineCall_Sql{Sql: &planpb.SqlRoutineImplementation{ + DefinitionFingerprint: []byte("0123456789abcdef"), + }}, + } + require.NoError(t, builder.recordRoutinePlanDependency(call)) + require.Len(t, builder.qry.RoutineDependencies, 1) + require.Equal(t, udf.LanguageSQL, builder.qry.RoutineDependencies[0].Language) + require.Equal(t, "0123456789abcdef", builder.qry.RoutineDependencies[0].DefinitionFingerprint) + require.Empty(t, builder.qry.RoutineDependencies[0].ArtifactDigest) + require.Empty(t, builder.qry.RoutineDependencies[0].EnvironmentDigest) +} diff --git a/pkg/sql/plan/rule/constant_fold.go b/pkg/sql/plan/rule/constant_fold.go index a7ca93f35abce..949ce8f193865 100644 --- a/pkg/sql/plan/rule/constant_fold.go +++ b/pkg/sql/plan/rule/constant_fold.go @@ -215,6 +215,12 @@ func (r *ConstantFold) constantFold(expr *plan.Expr, proc *process.Process) *pla return expr } + // A typed RoutineCall is an execute-time boundary. Even a zero-argument + // call with only literal metadata must reach ExternalRoutineEval so that + // volatility, row cardinality, and the captured revision remain intact. + if fn.RoutineCall != nil { + return expr + } overloadID := fn.Func.GetObj() if r.isPrepared && IsNullIntegerArgumentCast(expr) { return expr @@ -948,6 +954,9 @@ func IsConstant(e *plan.Expr, varAndParamIsConst bool) bool { case *plan.Expr_Lit, *plan.Expr_T, *plan.Expr_Vec: return true case *plan.Expr_F: + if ef.F == nil || ef.F.RoutineCall != nil { + return false + } // CASE expressions should always be evaluated at runtime to preserve // branch semantics; treat them as non-constant. if fid, _ := function.DecodeOverloadID(ef.F.Func.GetObj()); fid == function.CASE { diff --git a/pkg/sql/plan/rule/constant_fold_test.go b/pkg/sql/plan/rule/constant_fold_test.go index 7b8e72accc2d0..4f5455fe96328 100644 --- a/pkg/sql/plan/rule/constant_fold_test.go +++ b/pkg/sql/plan/rule/constant_fold_test.go @@ -71,6 +71,29 @@ func TestConstantFoldRuleRetainsStandaloneInterval(t *testing.T) { require.Same(t, expr, NewConstantFold(false).constantFold(expr, testutil.NewProcess(t))) } +func TestConstantFoldRuleNeverExecutesTypedRoutineCall(t *testing.T) { + expr := &plan.Expr{ + Typ: plan.Type{Id: int32(types.T_int64)}, + Expr: &plan.Expr_F{F: &plan.Function{ + Func: &plan.ObjectRef{ + Obj: function.EncodeOverloadID(function.PYTHON_UDF, 0), + ObjName: "python_user_defined_function", + }, + Args: []*plan.Expr{{ + Typ: plan.Type{Id: int32(types.T_int64)}, + Expr: &plan.Expr_Lit{Lit: &plan.Literal{Value: &plan.Literal_I64Val{I64Val: 1}}}, + }}, + RoutineCall: &plan.RoutineCall{ + FunctionRef: &plan.FunctionRef{FunctionId: 1, DatabaseId: 1, Revision: 1, NamespaceVersion: 1}, + Language: "PYTHON", + }, + }}, + } + + require.False(t, IsConstant(expr, false)) + require.Same(t, expr, NewConstantFold(false).constantFold(expr, testutil.NewProcess(t))) +} + func TestGetConstantValue2PreservesAndValidatesLiteralStringSource(t *testing.T) { proc := testutil.NewProcess(t) vec := vector.NewVec(types.T_varchar.ToType()) diff --git a/pkg/sql/plan/types.go b/pkg/sql/plan/types.go index 051c9245d907b..ae15d03b39472 100644 --- a/pkg/sql/plan/types.go +++ b/pkg/sql/plan/types.go @@ -475,6 +475,11 @@ type QueryBuilder struct { nextBindTag int32 nextMsgTag int32 nextSQLUdfCallID uint64 + // nextRoutineCallID gives every typed routine expression a plan-local + // callsite identity. It is deliberately separate from the SQL body + // substitution counter: lowering must not make a callsite depend on the + // number of generated parameter markers. + nextRoutineCallID uint64 isPrepareStatement bool mysqlCompatible bool @@ -587,13 +592,14 @@ type QueryBuilder struct { // returningFilterPos identifies an optional semantic eligibility selector in // the materialized row image. It filters only the RETURNING reader; mutation // readers continue to consume implicit FK action rows. - returningFilterPos int32 - returningRequested bool - returningTableDef *plan.TableDef - returningObjRef *plan.ObjectRef - returningTableName string - returningAlias string - returningColPos map[string]int32 + returningFilterPos int32 + returningRequested bool + bindingReturningProjection bool + returningTableDef *plan.TableDef + returningObjRef *plan.ObjectRef + returningTableName string + returningAlias string + returningColPos map[string]int32 // updateParentActionStack bounds recursive ON UPDATE actions by the active // physical-table path. Acyclic multi-layer cascades recurse normally; a // cycle is rejected before any mutation step is appended. diff --git a/pkg/sql/plan/utils.go b/pkg/sql/plan/utils.go index 73bef9ec035ec..7490ec00b2af5 100644 --- a/pkg/sql/plan/utils.go +++ b/pkg/sql/plan/utils.go @@ -2083,6 +2083,11 @@ func constantFoldWithPreparedExactSource( if fn == nil || proc == nil { return expr, nil } + // Typed external routines are execute-time calls. Do not let statistics or + // binder-side folding execute or replace a captured FunctionRef/revision. + if fn.RoutineCall != nil { + return expr, nil + } overloadID := fn.Func.GetObj() functionID, _ := function.DecodeOverloadID(overloadID) diff --git a/pkg/udf/protocol/protocol.go b/pkg/udf/protocol/protocol.go index 61bce709bbee7..f06920c8461d8 100644 --- a/pkg/udf/protocol/protocol.go +++ b/pkg/udf/protocol/protocol.go @@ -38,6 +38,12 @@ const ( // MaxControlBytes is a trust-boundary ceiling. A runtime may negotiate a // smaller limit, but a caller must never silently raise it. MaxControlBytes = 1 << 20 + + // MaxFenceComponentBytes bounds the identity retained by active and + // terminal ledgers. The wire envelope is larger than a single component, + // but allowing an unbounded statement/group/invocation string would make + // every bounded entry claim an unbounded memory claim. + MaxFenceComponentBytes = 256 ) var ( @@ -72,6 +78,9 @@ func (t FencingTuple) Validate() error { if !utf8.ValidString(t.StatementID) || !utf8.ValidString(t.GroupID) || !utf8.ValidString(t.InvocationID) { return fmt.Errorf("%w: fencing tuple contains invalid UTF-8", ErrProtocol) } + if len(t.StatementID) > MaxFenceComponentBytes || len(t.GroupID) > MaxFenceComponentBytes || len(t.InvocationID) > MaxFenceComponentBytes { + return fmt.Errorf("%w: fencing tuple component is too large", ErrProtocol) + } return nil } @@ -79,16 +88,19 @@ func (t FencingTuple) Validate() error { // app_metadata. Payload is a versioned descriptor or error body owned by the // message kind; it is bounded before JSON decoding. type Control struct { - Version int `json:"version"` - Kind string `json:"kind"` - Tuple FencingTuple `json:"tuple"` - Sequence uint64 `json:"sequence,omitempty"` - LastSequence uint64 `json:"last_sequence,omitempty"` - AckSequence uint64 `json:"ack_sequence,omitempty"` - FinishID string `json:"finish_id,omitempty"` - Status string `json:"status,omitempty"` - Reason string `json:"reason,omitempty"` - Payload json.RawMessage `json:"payload,omitempty"` + Version int `json:"version"` + Kind string `json:"kind"` + Tuple FencingTuple `json:"tuple"` + Sequence uint64 `json:"sequence,omitempty"` + LastSequence uint64 `json:"last_sequence,omitempty"` + AckSequence uint64 `json:"ack_sequence,omitempty"` + ReleasedBytes int64 `json:"released_bytes,omitempty"` + ReleasedBatches uint64 `json:"released_batches,omitempty"` + LastResultSequence uint64 `json:"last_result_sequence,omitempty"` + FinishID string `json:"finish_id,omitempty"` + Status string `json:"status,omitempty"` + Reason string `json:"reason,omitempty"` + Payload json.RawMessage `json:"payload,omitempty"` } type controlFieldRule struct { @@ -101,9 +113,9 @@ var controlFieldRules = map[string]controlFieldRule{ "InputBatch": {allowed: map[string]struct{}{"sequence": {}}, required: map[string]struct{}{"sequence": {}}}, "EndInput": {allowed: map[string]struct{}{"last_sequence": {}}, required: map[string]struct{}{"last_sequence": {}}}, "ResultSchema": {allowed: map[string]struct{}{}}, - "InputConsumed": {allowed: map[string]struct{}{"sequence": {}}, required: map[string]struct{}{"sequence": {}}}, + "InputConsumed": {allowed: map[string]struct{}{"sequence": {}, "released_bytes": {}, "released_batches": {}}, required: map[string]struct{}{"sequence": {}, "released_bytes": {}, "released_batches": {}}}, "ResultBatch": {allowed: map[string]struct{}{"sequence": {}}, required: map[string]struct{}{"sequence": {}}}, - "Finish": {allowed: map[string]struct{}{"last_sequence": {}, "finish_id": {}, "status": {}}, required: map[string]struct{}{"last_sequence": {}, "finish_id": {}, "status": {}}}, + "Finish": {allowed: map[string]struct{}{"last_sequence": {}, "last_result_sequence": {}, "finish_id": {}, "status": {}}, required: map[string]struct{}{"last_sequence": {}, "last_result_sequence": {}, "finish_id": {}, "status": {}}}, "AcknowledgeResults": {allowed: map[string]struct{}{"ack_sequence": {}}, required: map[string]struct{}{"ack_sequence": {}}}, "AcknowledgeFinish": {allowed: map[string]struct{}{"finish_id": {}}, required: map[string]struct{}{"finish_id": {}}}, "Ack": {allowed: map[string]struct{}{"ack_sequence": {}, "finish_id": {}, "status": {}}, required: map[string]struct{}{"status": {}}}, @@ -149,6 +161,21 @@ func validateControlFields(control Control, wire map[string]json.RawMessage) err return fmt.Errorf("%w: field %q is not valid for control kind %q", ErrProtocol, "ack_sequence", control.Kind) } } + if control.ReleasedBytes != 0 { + if _, allowed := rule.allowed["released_bytes"]; !allowed { + return fmt.Errorf("%w: field %q is not valid for control kind %q", ErrProtocol, "released_bytes", control.Kind) + } + } + if control.ReleasedBatches != 0 { + if _, allowed := rule.allowed["released_batches"]; !allowed { + return fmt.Errorf("%w: field %q is not valid for control kind %q", ErrProtocol, "released_batches", control.Kind) + } + } + if control.LastResultSequence != 0 { + if _, allowed := rule.allowed["last_result_sequence"]; !allowed { + return fmt.Errorf("%w: field %q is not valid for control kind %q", ErrProtocol, "last_result_sequence", control.Kind) + } + } if control.FinishID != "" { if _, allowed := rule.allowed["finish_id"]; !allowed { return fmt.Errorf("%w: field %q is not valid for control kind %q", ErrProtocol, "finish_id", control.Kind) @@ -174,10 +201,14 @@ func validateControlFields(control Control, wire map[string]json.RawMessage) err if len(control.Payload) == 0 && wire == nil { return fmt.Errorf("%w: control kind %q is missing field %q", ErrProtocol, control.Kind, "payload") } - case "InputBatch", "InputConsumed", "ResultBatch": + case "InputBatch", "ResultBatch": if control.Sequence == 0 { return fmt.Errorf("%w: control kind %q requires a positive sequence", ErrProtocol, control.Kind) } + case "InputConsumed": + if control.Sequence == 0 || control.ReleasedBytes <= 0 || control.ReleasedBatches == 0 { + return fmt.Errorf("%w: control kind %q requires sequence and positive released bytes/batches", ErrProtocol, control.Kind) + } case "AcknowledgeResults": if control.AckSequence == 0 { return fmt.Errorf("%w: control kind %q requires a positive ack_sequence", ErrProtocol, control.Kind) @@ -209,16 +240,19 @@ func validateControlFields(control Control, wire map[string]json.RawMessage) err // omission used for fields they do not own. func (c Control) MarshalJSON() ([]byte, error) { type wireControl struct { - Version int `json:"version"` - Kind string `json:"kind"` - Tuple FencingTuple `json:"tuple"` - Sequence uint64 `json:"sequence,omitempty"` - LastSequence *uint64 `json:"last_sequence,omitempty"` - AckSequence uint64 `json:"ack_sequence,omitempty"` - FinishID string `json:"finish_id,omitempty"` - Status string `json:"status,omitempty"` - Reason string `json:"reason,omitempty"` - Payload json.RawMessage `json:"payload,omitempty"` + Version int `json:"version"` + Kind string `json:"kind"` + Tuple FencingTuple `json:"tuple"` + Sequence uint64 `json:"sequence,omitempty"` + LastSequence *uint64 `json:"last_sequence,omitempty"` + AckSequence uint64 `json:"ack_sequence,omitempty"` + ReleasedBytes *int64 `json:"released_bytes,omitempty"` + ReleasedBatches *uint64 `json:"released_batches,omitempty"` + LastResultSequence *uint64 `json:"last_result_sequence,omitempty"` + FinishID string `json:"finish_id,omitempty"` + Status string `json:"status,omitempty"` + Reason string `json:"reason,omitempty"` + Payload json.RawMessage `json:"payload,omitempty"` } wire := wireControl{ Version: c.Version, Kind: c.Kind, Tuple: c.Tuple, @@ -230,6 +264,16 @@ func (c Control) MarshalJSON() ([]byte, error) { lastSequence := c.LastSequence wire.LastSequence = &lastSequence } + if c.Kind == "Finish" { + lastResultSequence := c.LastResultSequence + wire.LastResultSequence = &lastResultSequence + } + if c.Kind == "InputConsumed" { + releasedBytes := c.ReleasedBytes + releasedBatches := c.ReleasedBatches + wire.ReleasedBytes = &releasedBytes + wire.ReleasedBatches = &releasedBatches + } return json.Marshal(wire) } @@ -546,6 +590,7 @@ const ( ReasonAllNull CloseReason = "ALL_NULL" ReasonNoSelectedRows CloseReason = "NO_SELECTED_ROWS" ReasonPartialOpenError CloseReason = "PARTIAL_OPEN_FAILURE" + ReasonFailure CloseReason = "FAILURE" ReasonCancel CloseReason = "CANCEL" ReasonIdle CloseReason = "IDLE" ) @@ -565,6 +610,8 @@ type ExecutionGroup struct { terminal map[string]bool closing bool releaseBusy bool + releaseDone chan struct{} + releaseErr error released bool release func() error reason CloseReason @@ -633,7 +680,7 @@ func (t *OpenToken) Commit() error { delete(g.pending, t.memberID) g.inFlight-- if g.closing || g.released { - release := g.canReleaseLocked() + release := g.shouldReleaseLocked() g.mu.Unlock() if release { return errors.Join(ErrGroupClosed, g.releaseIfReady()) @@ -657,7 +704,7 @@ func (t *OpenToken) Abort() error { g.mu.Lock() delete(g.pending, t.memberID) g.inFlight-- - release := g.canReleaseLocked() + release := g.shouldReleaseLocked() g.mu.Unlock() if release { return g.releaseIfReady() @@ -692,7 +739,7 @@ func (g *ExecutionGroup) Close(reason CloseReason) error { g.closing = true g.reason = reason } - release := g.canReleaseLocked() + release := g.shouldReleaseLocked() g.mu.Unlock() if release { return g.releaseIfReady() @@ -712,7 +759,7 @@ func (g *ExecutionGroup) MemberTerminal(memberID string) error { } delete(g.members, memberID) g.terminal[memberID] = true - release := g.canReleaseLocked() + release := g.shouldReleaseLocked() g.mu.Unlock() if release { return g.releaseIfReady() @@ -721,23 +768,51 @@ func (g *ExecutionGroup) MemberTerminal(memberID string) error { } func (g *ExecutionGroup) canReleaseLocked() bool { - return g.closing && len(g.members) == 0 && g.inFlight == 0 && !g.released && !g.releaseBusy + return g.shouldReleaseLocked() && !g.releaseBusy +} + +func (g *ExecutionGroup) shouldReleaseLocked() bool { + return g.closing && len(g.members) == 0 && g.inFlight == 0 && !g.released } func (g *ExecutionGroup) releaseIfReady() error { g.mu.Lock() - if !g.canReleaseLocked() { + if !g.shouldReleaseLocked() { g.mu.Unlock() return nil } + if g.releaseBusy { + done := g.releaseDone + g.mu.Unlock() + <-done + g.mu.Lock() + err := g.releaseErr + released := g.released + g.mu.Unlock() + if released { + return nil + } + return err + } g.releaseBusy = true + g.releaseDone = make(chan struct{}) + done := g.releaseDone g.mu.Unlock() - err := g.release() + err := func() (err error) { + defer func() { + if recovered := recover(); recovered != nil { + err = fmt.Errorf("%w: group release panicked: %v", ErrProtocol, recovered) + } + }() + return g.release() + }() g.mu.Lock() g.releaseBusy = false + g.releaseErr = err if err == nil { g.released = true } + close(done) g.mu.Unlock() return err } @@ -769,11 +844,33 @@ func (g *ExecutionGroup) Counts() (registered, active, inFlight int) { return g.registered, len(g.members), g.inFlight } +// ID and Epoch identify the ownership fence used by the group owner. They are +// immutable for the lifetime of an ExecutionGroup. +func (g *ExecutionGroup) ID() string { + if g == nil { + return "" + } + g.mu.Lock() + defer g.mu.Unlock() + return g.id +} + +func (g *ExecutionGroup) Epoch() uint64 { + if g == nil { + return 0 + } + g.mu.Lock() + defer g.mu.Unlock() + return g.epoch +} + type ledgerEntry struct { - key string - bytes int64 - expiresAt time.Time - tombstone bool + key string + groupID string + groupEpoch uint64 + bytes int64 + expiresAt time.Time + tombstone bool } // TerminalLedger is an admission-reserved bounded deduplication ledger. A @@ -791,6 +888,7 @@ type TerminalLedger struct { type LedgerCredit struct { ledger *TerminalLedger groupID string + groupEpoch uint64 remainingN int remainingB int64 closed bool @@ -803,8 +901,8 @@ func NewTerminalLedger(maxEntries int, maxBytes int64) (*TerminalLedger, error) return &TerminalLedger{maxEntries: maxEntries, maxBytes: maxBytes, entries: make(map[string]ledgerEntry)}, nil } -func (l *TerminalLedger) Reserve(groupID string, entries int, bytes int64) (*LedgerCredit, error) { - if groupID == "" || entries <= 0 || bytes <= 0 { +func (l *TerminalLedger) Reserve(groupID string, groupEpoch uint64, entries int, bytes int64) (*LedgerCredit, error) { + if groupID == "" || groupEpoch == 0 || entries <= 0 || bytes <= 0 { return nil, fmt.Errorf("%w: invalid terminal ledger reservation", ErrProtocol) } l.mu.Lock() @@ -814,7 +912,7 @@ func (l *TerminalLedger) Reserve(groupID string, entries int, bytes int64) (*Led } l.reservedN += entries l.reservedB += bytes - return &LedgerCredit{ledger: l, groupID: groupID, remainingN: entries, remainingB: bytes}, nil + return &LedgerCredit{ledger: l, groupID: groupID, groupEpoch: groupEpoch, remainingN: entries, remainingB: bytes}, nil } func (c *LedgerCredit) Add(key string, bytes int64, expiry time.Time) error { @@ -829,7 +927,7 @@ func (c *LedgerCredit) Add(key string, bytes int64, expiry time.Time) error { if _, exists := c.ledger.entries[key]; exists { return ErrDuplicate } - c.ledger.entries[key] = ledgerEntry{key: key, bytes: bytes, expiresAt: expiry} + c.ledger.entries[key] = ledgerEntry{key: key, groupID: c.groupID, groupEpoch: c.groupEpoch, bytes: bytes, expiresAt: expiry} c.remainingN-- c.remainingB -= bytes return nil @@ -866,7 +964,14 @@ func (l *TerminalLedger) Complete(key string) error { return nil } -func (l *TerminalLedger) Expire(now time.Time) int { +// EpochFence is supplied by the Scheduler/group owner. It must return true +// only after the owner has durably or monotonically closed the corresponding +// group epoch so that an old grant cannot be issued again. A wall clock TTL is +// deliberately insufficient proof and a nil fence therefore reclaims nothing. +// The callback must not call back into the ledger. +type EpochFence func(groupID string, groupEpoch uint64) bool + +func (l *TerminalLedger) Expire(now time.Time, fence EpochFence) int { l.mu.Lock() defer l.mu.Unlock() removed := 0 @@ -878,7 +983,7 @@ func (l *TerminalLedger) Expire(now time.Time) int { if !entry.tombstone { continue } - if !now.Before(entry.expiresAt) { + if !now.Before(entry.expiresAt) && fence != nil && fence(entry.groupID, entry.groupEpoch) { delete(l.entries, key) l.reservedN-- l.reservedB -= entry.bytes diff --git a/pkg/udf/protocol/protocol_test.go b/pkg/udf/protocol/protocol_test.go index f22aaa2d3073e..1eeee14913a0f 100644 --- a/pkg/udf/protocol/protocol_test.go +++ b/pkg/udf/protocol/protocol_test.go @@ -18,6 +18,7 @@ import ( "bytes" "encoding/json" "errors" + "sync" "sync/atomic" "testing" "time" @@ -96,6 +97,7 @@ func TestClosingControlCarriesZeroLastSequence(t *testing.T) { if kind == "Finish" { control.Status = "OK" control.FinishID = "finish" + control.LastResultSequence = 0 } wire, err := MarshalControl(control) require.NoError(t, err) @@ -118,6 +120,32 @@ func TestClosingControlCarriesZeroLastSequence(t *testing.T) { require.False(t, ok, "non-closing controls should not grow a closing-only field") } +func TestInputConsumedCarriesReleaseCredit(t *testing.T) { + control := Control{ + Kind: "InputConsumed", + Tuple: testTuple(), + Sequence: 2, + ReleasedBytes: 128, + ReleasedBatches: 1, + } + wire, err := MarshalControl(control) + require.NoError(t, err) + decoded, err := UnmarshalControl(wire) + require.NoError(t, err) + require.Equal(t, int64(128), decoded.ReleasedBytes) + require.Equal(t, uint64(1), decoded.ReleasedBatches) + + var object map[string]any + require.NoError(t, json.Unmarshal(wire, &object)) + require.Equal(t, float64(128), object["released_bytes"]) + require.Equal(t, float64(1), object["released_batches"]) + delete(object, "released_batches") + invalid, err := json.Marshal(object) + require.NoError(t, err) + _, err = UnmarshalControl(invalid) + require.ErrorIs(t, err, ErrProtocol) +} + func TestSystemAccountIsValidFencingIdentity(t *testing.T) { tuple := testTuple() tuple.AccountID = 0 @@ -134,6 +162,14 @@ func TestFencingTupleRejectsInvalidUTF8(t *testing.T) { require.ErrorContains(t, err, "invalid UTF-8") } +func TestFencingTupleRejectsUnboundedIdentityComponents(t *testing.T) { + tuple := testTuple() + tuple.GroupID = string(bytes.Repeat([]byte{'g'}, MaxFenceComponentBytes+1)) + require.ErrorContains(t, tuple.Validate(), "component is too large") + _, err := MarshalControl(Control{Kind: "OpenInvocation", Tuple: tuple, Payload: []byte(`{}`)}) + require.ErrorContains(t, err, "component is too large") +} + func TestUnmarshalControlRejectsInvalidWireUTF8(t *testing.T) { wire := []byte(`{"version":1,"kind":"InputBatch","tuple":{"account_id":1,"statement_id":"`) wire = append(wire, 0xff) @@ -286,25 +322,28 @@ func TestTerminalLedgerRetainsTombstonesUntilExpiry(t *testing.T) { ledger, err := NewTerminalLedger(2, 200) require.NoError(t, err) now := time.Unix(100, 0) - credit, err := ledger.Reserve("g1", 1, 100) + credit, err := ledger.Reserve("g1", 1, 1, 100) require.NoError(t, err) require.NoError(t, credit.Add("i1", 100, now.Add(time.Hour))) require.NoError(t, ledger.Complete("i1")) credit.ReleaseUnused() - second, err := ledger.Reserve("g2", 1, 100) + second, err := ledger.Reserve("g2", 1, 1, 100) require.NoError(t, err) require.NoError(t, second.Add("i2", 100, now.Add(time.Hour))) require.NoError(t, ledger.Complete("i2")) second.ReleaseUnused() - _, err = ledger.Reserve("g3", 1, 1) + _, err = ledger.Reserve("g3", 1, 1, 1) require.ErrorIs(t, err, ErrLedgerFull) require.Equal(t, 2, func() int { n, _ := ledger.Counts(); return n }()) - require.Equal(t, 0, ledger.Expire(now.Add(time.Minute))) + require.Equal(t, 0, ledger.Expire(now.Add(time.Minute), func(string, uint64) bool { return true })) require.Equal(t, 2, func() int { n, _ := ledger.Counts(); return n }()) - require.Equal(t, 2, ledger.Expire(now.Add(2*time.Hour))) - third, err := ledger.Reserve("g3", 1, 1) + require.Equal(t, 0, ledger.Expire(now.Add(2*time.Hour), nil), "TTL must not advance the ownership epoch") + require.Equal(t, 2, ledger.Expire(now.Add(2*time.Hour), func(groupID string, groupEpoch uint64) bool { + return groupID == "g1" && groupEpoch == 1 || groupID == "g2" && groupEpoch == 1 + })) + third, err := ledger.Reserve("g3", 1, 1, 1) require.NoError(t, err) require.NoError(t, third.Add("i3", 1, now.Add(3*time.Hour))) @@ -317,15 +356,15 @@ func TestTerminalLedgerDoesNotExpireActiveEntries(t *testing.T) { ledger, err := NewTerminalLedger(1, 100) require.NoError(t, err) now := time.Unix(100, 0) - credit, err := ledger.Reserve("g", 1, 100) + credit, err := ledger.Reserve("g", 1, 1, 100) require.NoError(t, err) require.NoError(t, credit.Add("active", 100, now.Add(time.Second))) credit.ReleaseUnused() - require.Equal(t, 0, ledger.Expire(now.Add(time.Hour))) + require.Equal(t, 0, ledger.Expire(now.Add(time.Hour), func(string, uint64) bool { return true })) entries, bytes := ledger.Counts() require.Equal(t, 1, entries) require.Equal(t, int64(100), bytes) - _, err = ledger.Reserve("other", 1, 1) + _, err = ledger.Reserve("other", 1, 1, 1) require.ErrorIs(t, err, ErrLedgerFull) require.NoError(t, ledger.Abandon("active")) entries, bytes = ledger.Counts() @@ -350,6 +389,32 @@ func TestExecutionGroupReleaseErrorCanBeRetried(t *testing.T) { require.Equal(t, int32(2), attempts.Load()) } +func TestExecutionGroupConcurrentReleaseWaitersObserveTheSameFailure(t *testing.T) { + releaseStarted := make(chan struct{}) + releaseContinue := make(chan struct{}) + var releaseStartOnce sync.Once + group, err := NewExecutionGroup("concurrent", 1, 1, func() error { + releaseStartOnce.Do(func() { close(releaseStarted) }) + <-releaseContinue + return errors.New("temporary concurrent release failure") + }) + require.NoError(t, err) + member, err := group.BeginOpen("member") + require.NoError(t, err) + require.NoError(t, member.Commit()) + require.NoError(t, group.Close(ReasonCancel)) + + firstDone := make(chan error, 1) + go func() { firstDone <- group.MemberTerminal("member") }() + <-releaseStarted + secondDone := make(chan error, 1) + go func() { secondDone <- group.Close(ReasonCancel) }() + close(releaseContinue) + require.ErrorContains(t, <-firstDone, "temporary concurrent release failure") + require.ErrorContains(t, <-secondDone, "temporary concurrent release failure") + require.Equal(t, GroupDraining, group.State()) +} + func TestExecutionGroupCommitReportsReleaseFailure(t *testing.T) { var attempts atomic.Int32 group, err := NewExecutionGroup("g", 1, 1, func() error { diff --git a/pkg/udf/python/arrow.go b/pkg/udf/python/arrow.go index 7d4b14dc8a08a..38e48df652361 100644 --- a/pkg/udf/python/arrow.go +++ b/pkg/udf/python/arrow.go @@ -59,7 +59,33 @@ type TypeDescriptor struct { } func NewTypeDescriptor(typ types.Type) (TypeDescriptor, error) { - d := TypeDescriptor{TypeID: int32(typ.Oid), Width: typ.Width, Scale: typ.Scale, Charset: typ.Charset, OffsetWidth: 32} + // Planner types use scale=-1 as the sentinel for types whose SQL + // definition has no scale (for example INT and BINARY). That sentinel is + // an internal planner representation and cannot cross the Python ABI: + // descriptors are canonical and use zero for every non-applicable field. + // Likewise, display widths on numeric types are not part of their value + // contract. Normalize those fields here so SQL planner metadata and the + // persisted/Arrow descriptor have one stable representation. + width := typ.Width + scale := typ.Scale + switch typ.Oid { + case types.T_decimal64, types.T_decimal128: + if scale < 0 { + scale = 0 + } + case types.T_time, types.T_datetime, types.T_timestamp: + width = 0 + if scale < 0 { + scale = 0 + } + case types.T_char, types.T_varchar, types.T_binary, types.T_varbinary, + types.T_array_float32, types.T_array_float64: + scale = 0 + default: + width = 0 + scale = 0 + } + d := TypeDescriptor{TypeID: int32(typ.Oid), Width: width, Scale: scale, Charset: typ.Charset, OffsetWidth: 32} switch typ.Oid { case types.T_json: d.JSONEncoding = "canonical_text" @@ -139,9 +165,15 @@ func (d TypeDescriptor) Validate() error { if d.JSONEncoding != "" && (oid != types.T_json || d.JSONEncoding != "canonical_text") { return fmt.Errorf("TYPE_CONTRACT: unsupported JSON encoding") } + if oid == types.T_json && d.JSONEncoding != "canonical_text" { + return fmt.Errorf("TYPE_CONTRACT: JSON descriptor must use canonical_text encoding") + } if d.TemporalEncoding != "" && (oid != types.T_date && oid != types.T_datetime && oid != types.T_timestamp || d.TemporalEncoding != "sql_zero_struct") { return fmt.Errorf("TYPE_CONTRACT: unsupported temporal encoding") } + if (oid == types.T_date || oid == types.T_datetime || oid == types.T_timestamp) && d.TemporalEncoding != "sql_zero_struct" { + return fmt.Errorf("TYPE_CONTRACT: temporal descriptor must use sql_zero_struct encoding") + } switch oid { case types.T_bool, types.T_int8, types.T_int16, types.T_int32, types.T_int64, @@ -525,7 +557,11 @@ func appendInputValue(builder array.Builder, v *vector.Vector, typ types.Type, s p := vector.GenerateFunctionStrParameter(v) value, _ := p.GetStrValue(uint64(index)) if typ.Oid == types.T_json { - value = []byte(types.DecodeJson(value).String()) + canonical, err := canonicalJSONInput(value) + if err != nil { + return err + } + value = canonical } b.Append(string(value)) } @@ -567,6 +603,21 @@ func appendInputValue(builder array.Builder, v *vector.Vector, typ types.Type, s } return nil } + +// canonicalJSONInput converts MatrixOne's binary JSON rendering into the +// compact UTF-8 text required by the Python ABI. ByteJson.String is intended +// for SQL display and includes object/array whitespace; passing it directly +// would make a valid object fail the worker's canonical-text check even +// though scalar JSON values happen to work. +func canonicalJSONInput(value []byte) ([]byte, error) { + text := []byte(types.DecodeJson(value).String()) + var compact bytes.Buffer + if err := json.Compact(&compact, text); err != nil { + return nil, fmt.Errorf("TYPE_CONTRACT: invalid JSON input: %w", err) + } + return compact.Bytes(), nil +} + func appendTemporalInput(builder *array.StructBuilder, v *vector.Vector, typ types.Type, index int, null bool) error { zero := false var raw int64 diff --git a/pkg/udf/python/arrow_test.go b/pkg/udf/python/arrow_test.go index c4b9c5b01570d..0caea06d05b05 100644 --- a/pkg/udf/python/arrow_test.go +++ b/pkg/udf/python/arrow_test.go @@ -7,7 +7,9 @@ package python import ( "encoding/binary" + "encoding/json" "math" + "os" "testing" "github.com/apache/arrow-go/v18/arrow" @@ -182,7 +184,10 @@ func TestTypeDescriptorRejectsNonCanonicalDomain(t *testing.T) { invalid := []TypeDescriptor{ {TypeID: int32(types.T_varchar), Width: -1, OffsetWidth: 32}, {TypeID: int32(types.T_int64), OffsetWidth: 64}, + {TypeID: int32(types.T_int64)}, {TypeID: int32(types.T_decimal64), Width: 3, Scale: 4, OffsetWidth: 32}, + {TypeID: int32(types.T_json), OffsetWidth: 32}, + {TypeID: int32(types.T_date), OffsetWidth: 32}, {TypeID: int32(types.T_array_float32), Width: types.MaxArrayDimension + 1}, } for _, descriptor := range invalid { @@ -191,6 +196,274 @@ func TestTypeDescriptorRejectsNonCanonicalDomain(t *testing.T) { } } +func TestFloatDescriptorsRoundTripThroughArrowFieldContract(t *testing.T) { + for _, typ := range []types.T{types.T_float32, types.T_float64} { + descriptor, err := NewTypeDescriptor(typ.ToType()) + require.NoError(t, err) + field, err := descriptor.Field("value") + require.NoError(t, err) + require.NoError(t, descriptor.ValidateField(field)) + require.Equal(t, typ, types.T(descriptor.TypeID)) + } +} + +func TestNewTypeDescriptorNormalizesPlannerOnlySentinels(t *testing.T) { + integer := types.T_int32.ToTypeWithScale(-1) + integer.Width = 32 + descriptor, err := NewTypeDescriptor(integer) + require.NoError(t, err) + require.Equal(t, int32(0), descriptor.Width) + require.Equal(t, int32(0), descriptor.Scale) + + binaryType := types.T_binary.ToTypeWithScale(-1) + binaryType.Width = 8 + descriptor, err = NewTypeDescriptor(binaryType) + require.NoError(t, err) + require.Equal(t, int32(8), descriptor.Width) + require.Equal(t, int32(0), descriptor.Scale) + + temporal := types.T_datetime.ToTypeWithScale(-1) + descriptor, err = NewTypeDescriptor(temporal) + require.NoError(t, err) + require.Equal(t, int32(0), descriptor.Width) + require.Equal(t, int32(0), descriptor.Scale) +} + +func TestBuildInputRecordCanonicalizesJSONText(t *testing.T) { + mp := mpool.MustNewZeroNoFixed() + input := vector.NewVec(types.T_json.ToType()) + defer input.Free(mp) + bj, err := types.ParseStringToByteJson(`{"a":1,"b":[true,null,"中"]}`) + require.NoError(t, err) + raw, err := bj.Marshal() + require.NoError(t, err) + require.NoError(t, vector.AppendBytes(input, raw, false, mp)) + + record, _, err := BuildInputRecord([]*vector.Vector{input}, []types.Type{types.T_json.ToType()}, 1) + require.NoError(t, err) + defer record.Release() + values := record.Column(0).(*array.String) + require.Equal(t, `{"a":1,"b":[true,null,"中"]}`, values.Value(0)) +} + +func TestGoPythonTypeDescriptorFixtures(t *testing.T) { + data, err := os.ReadFile("testdata/type_descriptors.json") + require.NoError(t, err) + var fixtures []struct { + Name string `json:"name"` + Descriptor TypeDescriptor `json:"descriptor"` + Fingerprint string `json:"fingerprint"` + } + require.NoError(t, json.Unmarshal(data, &fixtures)) + require.NotEmpty(t, fixtures) + for _, fixture := range fixtures { + require.NoError(t, fixture.Descriptor.Validate(), fixture.Name) + fingerprint, err := fixture.Descriptor.Fingerprint() + require.NoError(t, err, fixture.Name) + require.Equal(t, fixture.Fingerprint, fingerprint, fixture.Name) + field, err := fixture.Descriptor.Field("value") + require.NoError(t, err, fixture.Name) + require.NoError(t, fixture.Descriptor.ValidateField(field), fixture.Name) + } +} + +func TestBuildInputRecordCoversEverySupportedType(t *testing.T) { + mp := mpool.MustNewZeroNoFixed() + defer mpool.DeleteMPool(mp) + + jsonValue, err := types.ParseStringToByteJson(`{"a":1,"b":[true,null]}`) + require.NoError(t, err) + jsonBytes, err := jsonValue.Marshal() + require.NoError(t, err) + + cases := []struct { + name string + typ types.Type + append func(*vector.Vector) error + }{ + {"bool", types.T_bool.ToType(), func(v *vector.Vector) error { + if err := vector.AppendFixed(v, true, false, mp); err != nil { + return err + } + return vector.AppendFixed(v, false, true, mp) + }}, + {"int8", types.T_int8.ToType(), func(v *vector.Vector) error { + if err := vector.AppendFixed(v, int8(-1), false, mp); err != nil { + return err + } + return vector.AppendFixed(v, int8(0), true, mp) + }}, + {"int16", types.T_int16.ToType(), func(v *vector.Vector) error { + if err := vector.AppendFixed(v, int16(-1), false, mp); err != nil { + return err + } + return vector.AppendFixed(v, int16(0), true, mp) + }}, + {"int32", types.T_int32.ToType(), func(v *vector.Vector) error { + if err := vector.AppendFixed(v, int32(-1), false, mp); err != nil { + return err + } + return vector.AppendFixed(v, int32(0), true, mp) + }}, + {"int64", types.T_int64.ToType(), func(v *vector.Vector) error { + if err := vector.AppendFixed(v, int64(-1), false, mp); err != nil { + return err + } + return vector.AppendFixed(v, int64(0), true, mp) + }}, + {"uint8", types.T_uint8.ToType(), func(v *vector.Vector) error { + if err := vector.AppendFixed(v, uint8(1), false, mp); err != nil { + return err + } + return vector.AppendFixed(v, uint8(0), true, mp) + }}, + {"uint16", types.T_uint16.ToType(), func(v *vector.Vector) error { + if err := vector.AppendFixed(v, uint16(1), false, mp); err != nil { + return err + } + return vector.AppendFixed(v, uint16(0), true, mp) + }}, + {"uint32", types.T_uint32.ToType(), func(v *vector.Vector) error { + if err := vector.AppendFixed(v, uint32(1), false, mp); err != nil { + return err + } + return vector.AppendFixed(v, uint32(0), true, mp) + }}, + {"uint64", types.T_uint64.ToType(), func(v *vector.Vector) error { + if err := vector.AppendFixed(v, uint64(1), false, mp); err != nil { + return err + } + return vector.AppendFixed(v, uint64(0), true, mp) + }}, + {"float32", types.T_float32.ToType(), func(v *vector.Vector) error { + if err := vector.AppendFixed(v, float32(1.25), false, mp); err != nil { + return err + } + return vector.AppendFixed(v, float32(0), true, mp) + }}, + {"float64", types.T_float64.ToType(), func(v *vector.Vector) error { + if err := vector.AppendFixed(v, float64(1.25), false, mp); err != nil { + return err + } + return vector.AppendFixed(v, float64(0), true, mp) + }}, + {"decimal64", types.New(types.T_decimal64, 18, 6), func(v *vector.Vector) error { + if err := vector.AppendFixed(v, types.Decimal64(123456), false, mp); err != nil { + return err + } + return vector.AppendFixed(v, types.Decimal64(0), true, mp) + }}, + {"decimal128", types.New(types.T_decimal128, 38, 10), func(v *vector.Vector) error { + if err := vector.AppendFixed(v, types.Decimal128{B0_63: 123456}, false, mp); err != nil { + return err + } + return vector.AppendFixed(v, types.Decimal128{}, true, mp) + }}, + {"date", types.T_date.ToType(), func(v *vector.Vector) error { + if err := vector.AppendFixed(v, types.Date(1), false, mp); err != nil { + return err + } + return vector.AppendFixed(v, types.Date(0), true, mp) + }}, + {"time", types.New(types.T_time, 0, 6), func(v *vector.Vector) error { + if err := vector.AppendFixed(v, types.Time(123456), false, mp); err != nil { + return err + } + return vector.AppendFixed(v, types.Time(0), true, mp) + }}, + {"datetime", types.New(types.T_datetime, 0, 6), func(v *vector.Vector) error { + if err := vector.AppendFixed(v, types.Datetime(types.GetUnixEpochSecs()+1), false, mp); err != nil { + return err + } + return vector.AppendFixed(v, types.Datetime(0), true, mp) + }}, + {"timestamp", types.New(types.T_timestamp, 0, 6), func(v *vector.Vector) error { + if err := vector.AppendFixed(v, types.Timestamp(types.GetUnixEpochSecs()+1), false, mp); err != nil { + return err + } + return vector.AppendFixed(v, types.Timestamp(0), true, mp) + }}, + {"char", types.New(types.T_char, 16, 0), func(v *vector.Vector) error { + if err := vector.AppendBytes(v, []byte("中"), false, mp); err != nil { + return err + } + return vector.AppendBytes(v, nil, true, mp) + }}, + {"varchar", types.New(types.T_varchar, 64, 0), func(v *vector.Vector) error { + if err := vector.AppendBytes(v, []byte("matrixone"), false, mp); err != nil { + return err + } + return vector.AppendBytes(v, nil, true, mp) + }}, + {"text", types.T_text.ToType(), func(v *vector.Vector) error { + if err := vector.AppendBytes(v, []byte("matrixone"), false, mp); err != nil { + return err + } + return vector.AppendBytes(v, nil, true, mp) + }}, + {"json", types.T_json.ToType(), func(v *vector.Vector) error { + if err := vector.AppendBytes(v, jsonBytes, false, mp); err != nil { + return err + } + return vector.AppendBytes(v, nil, true, mp) + }}, + {"binary", types.New(types.T_binary, 8, 0), func(v *vector.Vector) error { + if err := vector.AppendBytes(v, []byte{1, 2}, false, mp); err != nil { + return err + } + return vector.AppendBytes(v, nil, true, mp) + }}, + {"varbinary", types.New(types.T_varbinary, 8, 0), func(v *vector.Vector) error { + if err := vector.AppendBytes(v, []byte{1, 2}, false, mp); err != nil { + return err + } + return vector.AppendBytes(v, nil, true, mp) + }}, + {"blob", types.T_blob.ToType(), func(v *vector.Vector) error { + if err := vector.AppendBytes(v, []byte{1, 2}, false, mp); err != nil { + return err + } + return vector.AppendBytes(v, nil, true, mp) + }}, + {"uuid", types.T_uuid.ToType(), func(v *vector.Vector) error { + if err := vector.AppendFixed(v, types.Uuid{1, 2, 3}, false, mp); err != nil { + return err + } + return vector.AppendFixed(v, types.Uuid{}, true, mp) + }}, + {"vecf32", types.New(types.T_array_float32, 3, 0), func(v *vector.Vector) error { + if err := vector.AppendArray(v, []float32{1, 2, 3}, false, mp); err != nil { + return err + } + return vector.AppendArray[float32](v, nil, true, mp) + }}, + {"vecf64", types.New(types.T_array_float64, 3, 0), func(v *vector.Vector) error { + if err := vector.AppendArray(v, []float64{1, 2, 3}, false, mp); err != nil { + return err + } + return vector.AppendArray[float64](v, nil, true, mp) + }}, + } + + for _, tc := range cases { + t.Run(tc.name, func(t *testing.T) { + input := vector.NewVec(tc.typ) + require.NoError(t, tc.append(input)) + record, _, err := BuildInputRecord([]*vector.Vector{input}, []types.Type{tc.typ}, 2) + require.NoError(t, err) + defer record.Release() + input.Free(mp) + + descriptor, err := NewTypeDescriptor(tc.typ) + require.NoError(t, err) + require.NoError(t, descriptor.ValidateField(record.Schema().Field(0))) + require.Equal(t, int64(2), record.NumRows()) + require.False(t, record.Column(0).IsNull(0)) + require.True(t, record.Column(0).IsNull(1)) + }) + } +} + func TestArrowValueDomainRejectsDecimalOverflowAndNullableVectorChild(t *testing.T) { decimalType := &arrow.Decimal128Type{Precision: 3, Scale: 0} decimalBuilder := array.NewDecimal128Builder(memory.NewGoAllocator(), decimalType) diff --git a/pkg/udf/python/artifact.go b/pkg/udf/python/artifact.go new file mode 100644 index 0000000000000..957eff9deccaf --- /dev/null +++ b/pkg/udf/python/artifact.go @@ -0,0 +1,190 @@ +// Copyright 2026 Matrix Origin +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. + +package python + +import ( + "context" + "fmt" + "strings" + "unicode/utf8" + + "github.com/matrixorigin/matrixone/pkg/common/moerr" + "github.com/matrixorigin/matrixone/pkg/defines" + "github.com/matrixorigin/matrixone/pkg/fileservice" + "github.com/matrixorigin/matrixone/pkg/udf" +) + +// DefaultMaxArtifactBytes bounds the inline source bundle used by the current +// Python contract. The bound is applied before a write and before a read, so a +// corrupt or malicious object cannot turn an artifact lookup into an +// unbounded allocation. +const DefaultMaxArtifactBytes int64 = 1 << 20 + +// ArtifactResolver supplies the immutable source bytes for an exact catalog +// artifact reference. The resolver is deliberately keyed by account and +// digest: a digest alone must not become a cross-account capability. +type ArtifactResolver interface { + Resolve(ctx context.Context, accountID uint64, handler, digest string) (string, error) +} + +// ArtifactPublisher publishes an immutable artifact before its catalog +// revision becomes visible. Implementations must make a digest collision or a +// corrupt pre-existing object an error; replacing an object at the same path +// would break old plans that still reference that digest. +type ArtifactPublisher interface { + Publish(ctx context.Context, accountID uint64, handler, source string) (string, error) +} + +// ArtifactStore is the current FileService-backed artifact implementation. +// Resolver and publisher are separate interfaces so execution only needs read +// capability and cannot accidentally publish from the hot path. +type ArtifactStore interface { + ArtifactResolver + ArtifactPublisher +} + +// FileArtifactStore stores content addressed inline source under the shared +// FileService. The account component is part of the object path even though +// the content digest is global, preventing one account from using another +// account's artifact as an execution capability. +type FileArtifactStore struct { + fs fileservice.FileService + maxSize int64 +} + +var _ ArtifactStore = (*FileArtifactStore)(nil) + +func NewFileArtifactStore(fs fileservice.FileService, maxSize int64) (*FileArtifactStore, error) { + if fs == nil { + return nil, fmt.Errorf("RESOURCE_UNAVAILABLE: Python artifact store has no FileService") + } + shared, err := fileservice.Get[fileservice.FileService](fs, defines.SharedFileServiceName) + if err != nil { + return nil, fmt.Errorf("RESOURCE_UNAVAILABLE: Python artifact store has no shared FileService: %w", err) + } + if maxSize <= 0 { + maxSize = DefaultMaxArtifactBytes + } + return &FileArtifactStore{fs: shared, maxSize: maxSize}, nil +} + +func (s *FileArtifactStore) Publish(ctx context.Context, accountID uint64, handler, source string) (string, error) { + if err := s.validate(); err != nil { + return "", err + } + if ctx == nil { + ctx = context.Background() + } + digest, err := validateArtifactInput(accountID, handler, source, s.maxSize) + if err != nil { + return "", err + } + path := artifactPath(accountID, digest) + data := []byte(source) + // FileService implementations may retain the caller's IOEntry until the + // write returns. Give the store its own immutable backing so later caller + // mutation cannot alter the bytes whose digest is being published. + owned := append([]byte(nil), data...) + err = s.fs.Write(ctx, fileservice.IOVector{ + FilePath: path, + Entries: []fileservice.IOEntry{{Offset: 0, Size: int64(len(owned)), Data: owned}}, + }) + if err == nil { + return digest, nil + } + if !moerr.IsMoErrCode(err, moerr.ErrFileAlreadyExists) { + return "", fmt.Errorf("python artifact publish %s: %w", digest, err) + } + // A concurrent or retrying publisher is successful only after reading and + // validating the existing object. This closes the write-once collision + // boundary instead of treating path existence as proof of content identity. + existing, resolveErr := s.Resolve(ctx, accountID, handler, digest) + if resolveErr != nil { + return "", fmt.Errorf("python artifact publish %s found an invalid existing object: %w", digest, resolveErr) + } + if existing != source { + return "", fmt.Errorf("python artifact publish %s encountered a digest collision", digest) + } + return digest, nil +} + +func (s *FileArtifactStore) Resolve(ctx context.Context, accountID uint64, handler, digest string) (string, error) { + if err := s.validate(); err != nil { + return "", err + } + if ctx == nil { + ctx = context.Background() + } + if strings.TrimSpace(handler) == "" { + return "", fmt.Errorf("UNSUPPORTED_ROUTINE_VERSION: Python artifact has no handler") + } + if !udf.IsSHA256Digest(digest) { + return "", fmt.Errorf("UNSUPPORTED_ROUTINE_VERSION: Python artifact digest is invalid") + } + path := artifactPath(accountID, digest) + entry, err := s.fs.StatFile(ctx, path) + if err != nil { + return "", fmt.Errorf("RESOURCE_UNAVAILABLE: Python artifact %s is unavailable: %w", digest, err) + } + if entry == nil || entry.IsDir || entry.Size <= 0 || entry.Size > s.maxSize { + return "", fmt.Errorf("UNSUPPORTED_ROUTINE_VERSION: Python artifact %s has invalid size", digest) + } + data := make([]byte, entry.Size) + readVector := &fileservice.IOVector{ + FilePath: path, + Entries: []fileservice.IOEntry{{Offset: 0, Size: entry.Size, Data: data}}, + } + if err := s.fs.Read(ctx, readVector); err != nil { + return "", fmt.Errorf("RESOURCE_UNAVAILABLE: Python artifact %s cannot be read: %w", digest, err) + } + if len(readVector.Entries) != 1 || int64(len(readVector.Entries[0].Data)) != entry.Size { + return "", fmt.Errorf("UNSUPPORTED_ROUTINE_VERSION: Python artifact %s changed size while reading", digest) + } + source := string(readVector.Entries[0].Data) + if !utf8.ValidString(source) { + return "", fmt.Errorf("UNSUPPORTED_ROUTINE_VERSION: Python artifact %s is not valid UTF-8", digest) + } + if udf.PythonInlineArtifactDigest(handler, source) != digest { + return "", fmt.Errorf("UNSUPPORTED_ROUTINE_VERSION: Python artifact %s content digest mismatch", digest) + } + return source, nil +} + +func validateArtifactInput(accountID uint64, handler, source string, maxSize int64) (string, error) { + if maxSize <= 0 { + return "", fmt.Errorf("RESOURCE_UNAVAILABLE: Python artifact store has an invalid size limit") + } + if strings.TrimSpace(handler) == "" { + return "", fmt.Errorf("UNSUPPORTED_ROUTINE_VERSION: Python artifact has no handler") + } + if source == "" { + return "", fmt.Errorf("UNSUPPORTED_ROUTINE_VERSION: Python artifact source is empty") + } + if !utf8.ValidString(source) { + return "", fmt.Errorf("UNSUPPORTED_ROUTINE_VERSION: Python artifact source is not valid UTF-8") + } + if int64(len([]byte(source))) > maxSize { + return "", fmt.Errorf("RESOURCE_EXHAUSTED: Python artifact exceeds %d bytes", maxSize) + } + return udf.PythonInlineArtifactDigest(handler, source), nil +} + +func (s *FileArtifactStore) validate() error { + if s == nil || s.fs == nil { + return fmt.Errorf("RESOURCE_UNAVAILABLE: Python artifact store has no FileService") + } + if s.maxSize <= 0 { + return fmt.Errorf("RESOURCE_UNAVAILABLE: Python artifact store has an invalid size limit") + } + return nil +} + +func artifactPath(accountID uint64, digest string) string { + return fileservice.JoinPath( + defines.SharedFileServiceName, + fmt.Sprintf("udf-artifacts/%d/%s", accountID, digest), + ) +} diff --git a/pkg/udf/python/artifact_test.go b/pkg/udf/python/artifact_test.go new file mode 100644 index 0000000000000..cc617f68f52ec --- /dev/null +++ b/pkg/udf/python/artifact_test.go @@ -0,0 +1,97 @@ +// Copyright 2026 Matrix Origin +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. + +package python + +import ( + "context" + "strings" + "testing" + + "github.com/matrixorigin/matrixone/pkg/defines" + "github.com/matrixorigin/matrixone/pkg/fileservice" + "github.com/matrixorigin/matrixone/pkg/udf" + "github.com/stretchr/testify/require" +) + +func newTestArtifactStore(t *testing.T, maxSize int64) *FileArtifactStore { + t.Helper() + fs, err := fileservice.NewMemoryFS(defines.SharedFileServiceName, fileservice.DisabledCacheConfig, nil) + require.NoError(t, err) + store, err := NewFileArtifactStore(fs, maxSize) + require.NoError(t, err) + return store +} + +func TestFileArtifactStorePublishesAndResolvesByAccount(t *testing.T) { + store := newTestArtifactStore(t, DefaultMaxArtifactBytes) + ctx := context.Background() + source := "def add(ctx, value):\n return value + 1\n" + digest := udf.PythonInlineArtifactDigest("add", source) + + published, err := store.Publish(ctx, 7, "add", source) + require.NoError(t, err) + require.Equal(t, digest, published) + resolved, err := store.Resolve(ctx, 7, "add", digest) + require.NoError(t, err) + require.Equal(t, source, resolved) + + _, err = store.Resolve(ctx, 8, "add", digest) + require.ErrorContains(t, err, "unavailable") + second, err := store.Publish(ctx, 7, "add", source) + require.NoError(t, err) + require.Equal(t, digest, second, "republication of the exact immutable object is idempotent") +} + +func TestFileArtifactStoreRejectsCorruptionAndDigestCollision(t *testing.T) { + store := newTestArtifactStore(t, DefaultMaxArtifactBytes) + ctx := context.Background() + source := "def add(ctx, value): return value + 1" + digest := udf.PythonInlineArtifactDigest("add", source) + + path := artifactPath(7, digest) + err := store.fs.Write(ctx, fileservice.IOVector{ + FilePath: path, + Entries: []fileservice.IOEntry{{Offset: 0, Size: int64(len("corrupt")), Data: []byte("corrupt")}}, + }) + require.NoError(t, err) + _, err = store.Resolve(ctx, 7, "add", digest) + require.ErrorContains(t, err, "content digest mismatch") + _, err = store.Publish(ctx, 7, "add", source) + require.ErrorContains(t, err, "invalid existing object") +} + +func TestFileArtifactStoreRejectsOversizedSourceBeforeWrite(t *testing.T) { + store := newTestArtifactStore(t, 4) + _, err := store.Publish(context.Background(), 7, "add", "12345") + require.ErrorContains(t, err, "exceeds 4 bytes") + _, statErr := store.fs.StatFile(context.Background(), artifactPath(7, udf.PythonInlineArtifactDigest("add", "12345"))) + require.Error(t, statErr) +} + +func TestNewFileArtifactStoreRequiresSharedFileService(t *testing.T) { + local, err := fileservice.NewMemoryFS("LOCAL", fileservice.DisabledCacheConfig, nil) + require.NoError(t, err) + _, err = NewFileArtifactStore(local, 0) + require.ErrorContains(t, err, "shared FileService") +} + +func TestFileArtifactStoreRejectsInvalidDirectConstruction(t *testing.T) { + var nilStore *FileArtifactStore + _, err := nilStore.Publish(context.Background(), 7, "add", "source") + require.ErrorContains(t, err, "no FileService") + _, err = nilStore.Resolve(context.Background(), 7, "add", strings.Repeat("a", 64)) + require.ErrorContains(t, err, "no FileService") + + store := &FileArtifactStore{} + _, err = store.Publish(context.Background(), 7, "add", "source") + require.ErrorContains(t, err, "no FileService") +} + +func TestFileArtifactStoreRejectsInvalidUTF8(t *testing.T) { + store := newTestArtifactStore(t, DefaultMaxArtifactBytes) + _, err := store.Publish(context.Background(), 7, "add", string([]byte{'\xff'})) + require.ErrorContains(t, err, "not valid UTF-8") +} diff --git a/pkg/udf/python/config.go b/pkg/udf/python/config.go index 13ccc64588969..a5de9d35dcefa 100644 --- a/pkg/udf/python/config.go +++ b/pkg/udf/python/config.go @@ -36,12 +36,32 @@ type ClientConfig struct { // Enabled is deliberately false by default. The Python worker is an // unisolated development adapter until the production sandbox contract is // deployed, so a generic CN launch must never attach it implicitly. - Enabled bool `toml:"enabled"` - AllowUnisolated bool `toml:"allow-unisolated"` - ServerAddress string `toml:"server-address"` - MaxBatchBytes int64 `toml:"max-batch-bytes"` - MaxBatchRows int64 `toml:"max-batch-rows"` - RequestTimeout time.Duration `toml:"request-timeout"` + Enabled bool `toml:"enabled"` + AllowUnisolated bool `toml:"allow-unisolated"` + ServerAddress string `toml:"server-address"` + MaxBatchBytes int64 `toml:"max-batch-bytes"` + MaxBatchRows int64 `toml:"max-batch-rows"` + // MaxInvocationRows bounds the rows retained by one physical invocation. + // It is checked before OpenInvocation so a large request cannot reserve a + // worker or run user code before the CN has accepted its result shape. + MaxInvocationRows int64 `toml:"max-invocation-rows"` + // MaxInvocationResultBytes bounds the retained MO result vector for one + // invocation. Arrow frames remain subject to MaxBatchBytes; this separate + // limit prevents many individually valid batches from accumulating an + // unbounded result in the Gateway. + MaxInvocationResultBytes int64 `toml:"max-invocation-result-bytes"` + // MaxActiveInvocations is K: a CN refuses a new physical invocation when + // all slots are in use. It deliberately does not queue, because queued + // execution would retain the caller's input vectors and their allocation + // accounts while waiting for a worker slot. + MaxActiveInvocations int `toml:"max-active-invocations"` + RequestTimeout time.Duration `toml:"request-timeout"` + // Terminal limits cover both active and retained completion tombstones. + // They are reserved before an invocation is sent, so a long stream of + // short calls cannot grow the deduplication ledger without bound. + MaxTerminalEntries int `toml:"max-terminal-entries"` + MaxTerminalBytes int64 `toml:"max-terminal-bytes"` + TerminalRecordTTL time.Duration `toml:"terminal-record-ttl"` } func (c *ClientConfig) Validate() error { @@ -60,8 +80,26 @@ func (c *ClientConfig) Validate() error { if c.MaxBatchRows < 0 || c.MaxBatchRows > 1<<30 { return moerr.NewInternalError(context.Background(), "invalid python udf max batch rows") } + if c.MaxInvocationRows < 0 || c.MaxInvocationRows > 1<<32 { + return moerr.NewInternalError(context.Background(), "invalid python udf max invocation rows") + } + if c.MaxInvocationResultBytes < 0 || c.MaxInvocationResultBytes > 1<<40 { + return moerr.NewInternalError(context.Background(), "invalid python udf max invocation result bytes") + } + if c.MaxActiveInvocations < 0 || c.MaxActiveInvocations > 1<<20 { + return moerr.NewInternalError(context.Background(), "invalid python udf max active invocations") + } if c.RequestTimeout < 0 { return moerr.NewInternalError(context.Background(), "invalid python udf request timeout") } + if c.MaxTerminalEntries < 0 || c.MaxTerminalEntries > 1<<30 { + return moerr.NewInternalError(context.Background(), "invalid python udf max terminal entries") + } + if c.MaxTerminalBytes < 0 || c.MaxTerminalBytes > 1<<40 { + return moerr.NewInternalError(context.Background(), "invalid python udf max terminal bytes") + } + if c.TerminalRecordTTL < 0 { + return moerr.NewInternalError(context.Background(), "invalid python udf terminal record ttl") + } return nil } diff --git a/pkg/udf/python/config_test.go b/pkg/udf/python/config_test.go index 090963b6709ea..29034df2000e8 100644 --- a/pkg/udf/python/config_test.go +++ b/pkg/udf/python/config_test.go @@ -23,3 +23,28 @@ func TestClientConfigRequiresExplicitUnisolatedOptIn(t *testing.T) { config.RequestTimeout = time.Second require.NoError(t, config.Validate()) } + +func TestClientConfigRejectsUnboundedInvocationAdmission(t *testing.T) { + config := &ClientConfig{ + Enabled: true, + AllowUnisolated: true, + ServerAddress: "127.0.0.1:50051", + MaxActiveInvocations: -1, + } + require.ErrorContains(t, config.Validate(), "max active invocations") + config.MaxActiveInvocations = 1 << 20 + require.NoError(t, config.Validate()) + config.MaxActiveInvocations++ + require.ErrorContains(t, config.Validate(), "max active invocations") +} + +func TestClientConfigRejectsInvalidInvocationBudgets(t *testing.T) { + config := &ClientConfig{ + Enabled: true, AllowUnisolated: true, ServerAddress: "127.0.0.1:50051", + MaxInvocationRows: -1, + } + require.ErrorContains(t, config.Validate(), "max invocation rows") + config.MaxInvocationRows = 1 + config.MaxInvocationResultBytes = -1 + require.ErrorContains(t, config.Validate(), "max invocation result bytes") +} diff --git a/pkg/udf/python/definition.go b/pkg/udf/python/definition.go new file mode 100644 index 0000000000000..4b118b3b83d9c --- /dev/null +++ b/pkg/udf/python/definition.go @@ -0,0 +1,78 @@ +// Copyright 2026 Matrix Origin +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. + +package python + +import ( + "crypto/sha256" + "encoding/hex" + "encoding/json" + "fmt" +) + +// definitionFingerprintBody is the canonical executable contract. Source is +// deliberately absent: original source is a catalog/audit payload, while the +// immutable artifact digest identifies the exact bytes that may be executed. +// Changing a type or execution policy must invalidate the definition even +// when the artifact bytes stay unchanged. +type definitionFingerprintBody struct { + DefinitionSchemaVersion int `json:"definition_schema_version"` + Handler string `json:"handler"` + Mode string `json:"mode"` + NullPolicy string `json:"null_policy"` + ABIContract string `json:"abi_contract"` + AdapterVersion string `json:"adapter_version"` + ArtifactDigest string `json:"artifact_digest"` + EnvironmentDigest string `json:"environment_digest"` + SDKVersion string `json:"sdk_version"` + ArgTypes []TypeDescriptor `json:"arg_types,omitempty"` + ReturnType *TypeDescriptor `json:"return_type,omitempty"` +} + +// DefinitionFingerprint computes the canonical current Python definition +// fingerprint. Callers must pass descriptors produced by NewTypeDescriptor; +// validating here makes the worker and Gateway reject an identity whose type +// metadata is not part of the supported Arrow contract before user code runs. +func DefinitionFingerprint( + definitionSchemaVersion int, + handler, mode, nullPolicy, abiContract, adapterVersion string, + artifactDigest, environmentDigest, sdkVersion string, + args []TypeDescriptor, + returnType TypeDescriptor, +) (string, error) { + if definitionSchemaVersion <= 0 || handler == "" || + mode == "" || nullPolicy == "" || abiContract == "" || + adapterVersion == "" || sdkVersion == "" || artifactDigest == "" || + environmentDigest == "" { + return "", fmt.Errorf("python definition fingerprint has incomplete metadata") + } + for index, descriptor := range args { + if err := descriptor.Validate(); err != nil { + return "", fmt.Errorf("argument descriptor %d: %w", index, err) + } + } + if err := returnType.Validate(); err != nil { + return "", fmt.Errorf("return descriptor: %w", err) + } + body := definitionFingerprintBody{ + DefinitionSchemaVersion: definitionSchemaVersion, + Handler: handler, + Mode: mode, + NullPolicy: nullPolicy, + ABIContract: abiContract, + AdapterVersion: adapterVersion, + ArtifactDigest: artifactDigest, + EnvironmentDigest: environmentDigest, + SDKVersion: sdkVersion, + ArgTypes: args, + ReturnType: &returnType, + } + canonical, err := json.Marshal(body) + if err != nil { + return "", fmt.Errorf("marshal definition fingerprint: %w", err) + } + digest := sha256.Sum256(canonical) + return hex.EncodeToString(digest[:]), nil +} diff --git a/pkg/udf/python/definition_test.go b/pkg/udf/python/definition_test.go new file mode 100644 index 0000000000000..9a0209935afe0 --- /dev/null +++ b/pkg/udf/python/definition_test.go @@ -0,0 +1,51 @@ +// Copyright 2026 Matrix Origin +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. + +package python + +import ( + "encoding/json" + "strings" + "testing" + + "github.com/matrixorigin/matrixone/pkg/container/types" + "github.com/matrixorigin/matrixone/pkg/udf" + "github.com/stretchr/testify/require" +) + +func TestDefinitionFingerprintCanonicalContractExcludesSource(t *testing.T) { + argument, err := NewTypeDescriptor(types.T_int64.ToType()) + require.NoError(t, err) + returnType, err := NewTypeDescriptor(types.T_int64.ToType()) + require.NoError(t, err) + artifactDigest := udf.PythonInlineArtifactDigest("add", "def add(ctx, value): return value") + first, err := DefinitionFingerprint( + udf.PythonDefinitionSchemaVersion, + "add", ModeScalar, NullCallHandler, + udf.PythonABIContract, udf.PythonAdapterVersion, + artifactDigest, strings.Repeat("e", 64), udf.PythonSDKVersion, + []TypeDescriptor{argument}, returnType, + ) + require.NoError(t, err) + require.Equal(t, "d0348f99deac2a1518fdf0f32d37a4e7924acbce450c7328d1e83d67d5f7e635", first) + require.True(t, udf.IsSHA256Digest(first)) + + canonical, err := json.Marshal(definitionFingerprintBody{ + DefinitionSchemaVersion: udf.PythonDefinitionSchemaVersion, + Handler: "add", + Mode: ModeScalar, + NullPolicy: NullCallHandler, + ABIContract: udf.PythonABIContract, + AdapterVersion: udf.PythonAdapterVersion, + ArtifactDigest: artifactDigest, + EnvironmentDigest: strings.Repeat("e", 64), + SDKVersion: udf.PythonSDKVersion, + ArgTypes: []TypeDescriptor{argument}, + ReturnType: &returnType, + }) + require.NoError(t, err) + require.NotContains(t, string(canonical), `"source"`) + require.Contains(t, string(canonical), `"artifact_digest"`) +} diff --git a/pkg/udf/python/gateway.go b/pkg/udf/python/gateway.go index 0329541172bad..b4de40772d9ff 100644 --- a/pkg/udf/python/gateway.go +++ b/pkg/udf/python/gateway.go @@ -11,10 +11,13 @@ package python import ( "bytes" "context" + "crypto/sha256" + "encoding/hex" "encoding/json" "errors" "fmt" "io" + "strings" "sync" "time" @@ -29,24 +32,75 @@ import ( ) const ( - ModeScalar = "SCALAR" - ModeVector = "VECTOR" - NullCallHandler = "CALLED_ON_NULL_INPUT" - NullReturnNull = "RETURNS_NULL_ON_NULL_INPUT" - statusOK = "OK" + ModeScalar = "SCALAR" + ModeVector = "VECTOR" + NullCallHandler = "CALLED_ON_NULL_INPUT" + NullReturnNull = "RETURNS_NULL_ON_NULL_INPUT" + DefaultMaxActiveInvocations = 8 + DefaultMaxInvocationRows = 1 << 20 + DefaultMaxInvocationResultBytes = 256 << 20 + DefaultMaxTerminalEntries = 8192 + DefaultMaxTerminalBytes = 8 << 20 + DefaultTerminalRecordTTL = 10 * time.Minute + // These limits are part of the current worker capability contract. H is + // the worker-wide child budget; account and owner limits are narrower + // fairness fences enforced before a handler child is started. + DefaultWorkerMaxHandlerProcesses = 8 + DefaultWorkerMaxAccountHandlers = 8 + DefaultWorkerMaxOwnerHandlers = 4 + statusOK = "OK" ) var errGatewayClosed = errors.New("python udf gateway is closed") type Gateway struct { - cfg ClientConfig - conn *grpc.ClientConn - flight flight.FlightServiceClient - mu sync.Mutex - closed bool + cfg ClientConfig + artifactResolver ArtifactResolver + allowInlineSource bool + conn *grpc.ClientConn + flight flight.FlightServiceClient + mu sync.Mutex + capabilityMu sync.Mutex + admissionMu sync.Mutex + capabilityReady bool + workerLeaseEpoch uint64 + closed bool + active chan struct{} + ledger *protocol.TerminalLedger + ledgerTTL time.Duration + // admittedGroups and closedGroups are the Gateway's ownership fence for + // the current one-member adapter. A group epoch may be admitted only once; + // a terminal tombstone is reclaimable only after its exact group epoch has + // been closed by this owner. closedGroups stores a monotonic high-water + // epoch per group so a late lower-epoch retry cannot become executable + // after its ledger tombstone is collected. + admittedGroups map[string]uint64 + closedGroups map[string]uint64 + closedGroupBytes int64 + reservedGroupBytes int64 + closedGroupLimit int + closedGroupByteLimit int64 } func NewGateway(cfg ClientConfig) (*Gateway, error) { + // NewGateway is kept for direct adapter tests and local development. CN + // production construction uses NewGatewayWithArtifactStore so an + // executable plan can never supply source bytes itself. + return newGateway(cfg, nil, true) +} + +// NewGatewayWithArtifactStore constructs the CN data-plane client for the +// current typed contract. Source is resolved from the exact account/digest +// reference before Open is sent to the worker; a plan carrying source is +// rejected at this boundary. +func NewGatewayWithArtifactStore(cfg ClientConfig, resolver ArtifactResolver) (*Gateway, error) { + if resolver == nil { + return nil, fmt.Errorf("RESOURCE_UNAVAILABLE: Python artifact resolver is not configured") + } + return newGateway(cfg, resolver, false) +} + +func newGateway(cfg ClientConfig, resolver ArtifactResolver, allowInlineSource bool) (*Gateway, error) { if err := cfg.Validate(); err != nil { return nil, err } @@ -56,14 +110,67 @@ func NewGateway(cfg ClientConfig) (*Gateway, error) { if cfg.MaxBatchRows == 0 { cfg.MaxBatchRows = DefaultMaxBatchRows } + if cfg.MaxInvocationRows == 0 { + cfg.MaxInvocationRows = DefaultMaxInvocationRows + } + if cfg.MaxInvocationResultBytes == 0 { + cfg.MaxInvocationResultBytes = DefaultMaxInvocationResultBytes + } if cfg.RequestTimeout == 0 { cfg.RequestTimeout = 30 * time.Second } - return &Gateway{cfg: cfg}, nil + if cfg.MaxActiveInvocations == 0 { + cfg.MaxActiveInvocations = DefaultMaxActiveInvocations + } + if cfg.MaxTerminalEntries == 0 { + cfg.MaxTerminalEntries = DefaultMaxTerminalEntries + } + if cfg.MaxTerminalBytes == 0 { + cfg.MaxTerminalBytes = DefaultMaxTerminalBytes + } + if cfg.TerminalRecordTTL == 0 { + cfg.TerminalRecordTTL = DefaultTerminalRecordTTL + } + ledger, err := protocol.NewTerminalLedger(cfg.MaxTerminalEntries, cfg.MaxTerminalBytes) + if err != nil { + return nil, err + } + return &Gateway{ + cfg: cfg, + artifactResolver: resolver, + allowInlineSource: allowInlineSource, + active: make(chan struct{}, cfg.MaxActiveInvocations), + ledger: ledger, + ledgerTTL: cfg.TerminalRecordTTL, + admittedGroups: make(map[string]uint64), + closedGroups: make(map[string]uint64), + closedGroupLimit: cfg.MaxTerminalEntries, + closedGroupByteLimit: cfg.MaxTerminalBytes, + }, nil } func (g *Gateway) Language() string { return udf.LanguagePython } +func (g *Gateway) CheckLanguageReady(ctx context.Context, language string) error { + if ctx == nil { + ctx = context.Background() + } + if language != udf.LanguagePython { + return fmt.Errorf("python udf: unsupported readiness language %q", language) + } + if !g.cfg.Enabled { + return fmt.Errorf("RESOURCE_UNAVAILABLE: Python UDF runtime is disabled") + } + if !g.cfg.AllowUnisolated { + return fmt.Errorf("RESOURCE_UNAVAILABLE: Python UDF runtime requires explicit unisolated opt-in") + } + client, err := g.flightClient() + if err != nil { + return err + } + return g.ensureCapabilities(ctx, client) +} + func (g *Gateway) connect() error { _, err := g.flightClient() return err @@ -114,28 +221,47 @@ func (g *Gateway) Close() error { } type openPayload struct { - Handler string `json:"handler"` - Source string `json:"source"` - Mode string `json:"mode"` - NullPolicy string `json:"null_policy"` - ABIContract string `json:"abi_contract"` - AdapterVersion string `json:"adapter_version"` - SDKVersion string `json:"sdk_version"` - Context map[string]string `json:"context,omitempty"` - Args []TypeDescriptor `json:"args"` - Return TypeDescriptor `json:"return"` - MaxBatchBytes int64 `json:"max_batch_bytes"` - MaxBatchRows int64 `json:"max_batch_rows"` - HandlerTimeoutSeconds float64 `json:"handler_timeout_seconds"` -} - -func (g *Gateway) Execute(ctx context.Context, invocation *udf.Invocation, result vector.FunctionResultWrapper, mp *mpool.MPool) error { + FunctionRef udf.FunctionRef `json:"function_ref"` + Handler string `json:"handler"` + Source string `json:"source"` + Mode string `json:"mode"` + NullPolicy string `json:"null_policy"` + ABIContract string `json:"abi_contract"` + AdapterVersion string `json:"adapter_version"` + SDKVersion string `json:"sdk_version"` + DefinitionSchemaVersion int `json:"definition_schema_version"` + ArtifactDigest string `json:"artifact_digest"` + EnvironmentDigest string `json:"environment_digest"` + DefinitionFingerprint string `json:"definition_fingerprint"` + Context map[string]string `json:"context,omitempty"` + CallsiteID string `json:"callsite_id"` + MayError bool `json:"may_error"` + SecurityMode string `json:"security_mode"` + Leakproof bool `json:"leakproof"` + StatementContext *udf.StatementContext `json:"statement_context,omitempty"` + SecurityFrame *udf.SecurityFrame `json:"security_frame,omitempty"` + Args []TypeDescriptor `json:"args"` + Return TypeDescriptor `json:"return"` + MaxBatchBytes int64 `json:"max_batch_bytes"` + MaxBatchRows int64 `json:"max_batch_rows"` + MaxInvocationRows int64 `json:"max_invocation_rows"` + MaxInvocationResultBytes int64 `json:"max_invocation_result_bytes"` + HandlerTimeoutSeconds float64 `json:"handler_timeout_seconds"` +} + +func (g *Gateway) Execute(ctx context.Context, invocation *udf.Invocation, result vector.FunctionResultWrapper, mp *mpool.MPool) (err error) { g.mu.Lock() closed := g.closed g.mu.Unlock() if closed { return errGatewayClosed } + if !g.cfg.Enabled { + return fmt.Errorf("RESOURCE_UNAVAILABLE: Python UDF runtime is disabled") + } + if !g.cfg.AllowUnisolated { + return fmt.Errorf("RESOURCE_UNAVAILABLE: Python UDF runtime requires explicit unisolated opt-in") + } if ctx == nil { ctx = context.Background() } @@ -144,12 +270,36 @@ func (g *Gateway) Execute(ctx context.Context, invocation *udf.Invocation, resul if invocation == nil || result == nil || mp == nil { return fmt.Errorf("python udf: nil invocation, result, or memory pool") } - if err := validateInvocation(invocation); err != nil { + execution := *invocation + if g.artifactResolver != nil { + if execution.Source != "" { + return fmt.Errorf("UNSUPPORTED_ROUTINE_VERSION: typed Python invocation contains source; use its immutable artifact digest") + } + // Validate the identity, semantic contract, descriptors, and resource + // shape before resolving an account-scoped artifact. Resolution is a + // read with tenant-sensitive error/latency behavior; it must not become + // the first operation performed on an untrusted or stale plan. The + // source-dependent digest check is repeated after the resolver returns. + if err := validateInvocationHeader(&execution); err != nil { + return err + } + execution.Source, err = g.artifactResolver.Resolve( + streamCtx, + execution.FunctionRef.AccountID, + execution.Handler, + execution.ArtifactDigest, + ) + if err != nil { + return err + } + } else if !g.allowInlineSource && execution.Source == "" { + return fmt.Errorf("RESOURCE_UNAVAILABLE: Python artifact resolver is not configured") + } + if err := validateInvocation(&execution); err != nil { return err } - args := make([]TypeDescriptor, len(invocation.Args)) - for i, typ := range invocation.Args { - var err error + args := make([]TypeDescriptor, len(execution.Args)) + for i, typ := range execution.Args { args[i], err = NewTypeDescriptor(typ) if err != nil { return err @@ -159,21 +309,77 @@ func (g *Gateway) Execute(ctx context.Context, invocation *udf.Invocation, resul if err != nil { return err } - if invocation.Length == 0 { + if err := validateInvocationBudget(executionBudget{ + rows: int64(execution.Length), + maxRows: g.cfg.MaxInvocationRows, + returnType: execution.ReturnType, + maxResultBytes: g.cfg.MaxInvocationResultBytes, + }); err != nil { + return err + } + if execution.Length == 0 { return result.PreExtendAndReset(0) } client, err := g.flightClient() if err != nil { return err } - openBody, err := json.Marshal(openPayload{Handler: invocation.Handler, Source: invocation.Source, Mode: invocation.Mode, NullPolicy: invocation.NullPolicy, ABIContract: invocation.ABIContract, AdapterVersion: invocation.AdapterVersion, SDKVersion: invocation.SDKVersion, Context: cloneMap(invocation.Context), Args: args, Return: returnDescriptor, MaxBatchBytes: g.cfg.MaxBatchBytes, MaxBatchRows: g.cfg.MaxBatchRows, HandlerTimeoutSeconds: g.cfg.RequestTimeout.Seconds()}) + if err := g.ensureCapabilities(streamCtx, client); err != nil { + return err + } + // The worker instance owns the lease epoch. Copy the invocation before + // attaching it so a caller cannot observe or reuse an identity issued for + // another worker process. If the worker was restarted after the cached + // handshake, the exchange fails with STALE_LEASE_EPOCH and the deferred + // invalidation below forces a fresh handshake on the next call. + g.mu.Lock() + workerLeaseEpoch := g.workerLeaseEpoch + g.mu.Unlock() + if workerLeaseEpoch == 0 { + return fmt.Errorf("python udf: capability handshake did not return a worker lease epoch") + } + execution.Tuple.LeaseEpoch = workerLeaseEpoch + if err := validateInvocation(&execution); err != nil { + return err + } + defer func() { + if err != nil { + g.invalidateCapabilities() + } + }() + openBody, err := json.Marshal(openPayload{ + FunctionRef: execution.FunctionRef, Handler: execution.Handler, Source: execution.Source, + Mode: execution.Mode, NullPolicy: execution.NullPolicy, ABIContract: execution.ABIContract, + AdapterVersion: execution.AdapterVersion, SDKVersion: execution.SDKVersion, + DefinitionSchemaVersion: execution.DefinitionSchemaVersion, + ArtifactDigest: execution.ArtifactDigest, EnvironmentDigest: execution.EnvironmentDigest, + DefinitionFingerprint: execution.DefinitionFingerprint, + Context: cloneMap(execution.Context), CallsiteID: execution.CallsiteID, + MayError: execution.MayError, SecurityMode: execution.SecurityMode, + Leakproof: execution.Leakproof, StatementContext: execution.StatementContext, + SecurityFrame: execution.SecurityFrame, Args: args, Return: returnDescriptor, + MaxBatchBytes: g.cfg.MaxBatchBytes, MaxBatchRows: g.cfg.MaxBatchRows, + MaxInvocationRows: g.cfg.MaxInvocationRows, MaxInvocationResultBytes: g.cfg.MaxInvocationResultBytes, + HandlerTimeoutSeconds: g.cfg.RequestTimeout.Seconds(), + }) if err != nil { return fmt.Errorf("python udf: encode open payload: %w", err) } - open, err := protocol.MarshalControl(protocol.Control{Kind: "OpenInvocation", Tuple: invocation.Tuple, Payload: openBody}) + open, err := protocol.MarshalControl(protocol.Control{Kind: "OpenInvocation", Tuple: execution.Tuple, Payload: openBody}) if err != nil { return err } + admission, err := g.admitInvocation(&execution) + if err != nil { + return err + } + openAttempted := false + defer func() { + reason := admissionReason(streamCtx, openAttempted, err) + if cleanupErr := admission.finish(openAttempted, reason); err == nil && cleanupErr != nil { + err = cleanupErr + } + }() stream, err := client.DoExchange(streamCtx) if err != nil { @@ -183,10 +389,14 @@ func (g *Gateway) Execute(ctx context.Context, invocation *udf.Invocation, resul // is delivered to the server separately from the Arrow IPC reader; putting // the JSON command in AppMetadata on this message makes PyArrow try to // decode it as an IPC message. + // Once Send is attempted the remote side may have accepted the command. The + // admission is therefore terminalized conservatively even when Send returns + // a transport error; transparent replay would violate VOLATILE semantics. + openAttempted = true if err := stream.Send(&flight.FlightData{FlightDescriptor: &flight.FlightDescriptor{Type: flight.FlightDescriptor_CMD, Cmd: open}}); err != nil { return err } - if err := result.PreExtendAndReset(invocation.Length); err != nil { + if err := result.PreExtendAndReset(execution.Length); err != nil { return err } @@ -199,8 +409,8 @@ func (g *Gateway) Execute(ctx context.Context, invocation *udf.Invocation, resul var batchIndex uint64 var lastSequence uint64 var inputEnded bool - for start < int64(invocation.Length) { - batch, err := encodeInputBatch(invocation.Inputs, invocation.Args, start, int64(invocation.Length)-start, g.cfg.MaxBatchBytes, g.cfg.MaxBatchRows) + for start < int64(execution.Length) { + batch, err := encodeInputBatch(execution.Inputs, execution.Args, start, int64(execution.Length)-start, g.cfg.MaxBatchBytes, g.cfg.MaxBatchRows) if err != nil { return err } @@ -219,7 +429,7 @@ func (g *Gateway) Execute(ctx context.Context, invocation *udf.Invocation, resul if err := sequence.AcceptInput(sequenceNumber); err != nil { return err } - inputControl, err := protocol.MarshalControl(protocol.Control{Kind: "InputBatch", Tuple: invocation.Tuple, Sequence: sequenceNumber}) + inputControl, err := protocol.MarshalControl(protocol.Control{Kind: "InputBatch", Tuple: execution.Tuple, Sequence: sequenceNumber}) if err != nil { return err } @@ -227,12 +437,12 @@ func (g *Gateway) Execute(ctx context.Context, invocation *udf.Invocation, resul return err } lastSequence = sequenceNumber - isLastBatch := start+batch.Rows == int64(invocation.Length) + isLastBatch := start+batch.Rows == int64(execution.Length) if isLastBatch { // EndInput is an application-level half-close. Send it before // waiting for the final result so the runtime must keep its result // direction alive when that result is delayed on the wire. - endInput, err := protocol.MarshalControl(protocol.Control{Kind: "EndInput", Tuple: invocation.Tuple, LastSequence: lastSequence}) + endInput, err := protocol.MarshalControl(protocol.Control{Kind: "EndInput", Tuple: execution.Tuple, LastSequence: lastSequence}) if err != nil { return err } @@ -247,7 +457,7 @@ func (g *Gateway) Execute(ctx context.Context, invocation *udf.Invocation, resul } inputEnded = true } - batchRows, err := g.receiveResultBatch(streamCtx, client, stream, invocation.Tuple, sequenceNumber, batch.Rows, &schemaFrame, &schemaReady, returnDescriptor, result, mp, &sequence) + batchRows, err := g.receiveResultBatch(streamCtx, client, stream, execution.Tuple, sequenceNumber, batch.Rows, &schemaFrame, &schemaReady, returnDescriptor, result, mp, &sequence) if err != nil { return err } @@ -261,7 +471,451 @@ func (g *Gateway) Execute(ctx context.Context, invocation *udf.Invocation, resul if !inputEnded || lastSequence != batchIndex { return fmt.Errorf("python udf: input stream did not reach EndInput") } - return g.receiveFinish(streamCtx, client, stream, invocation.Tuple, invocation.Length, rows, &sequence) + return g.receiveFinish(streamCtx, client, stream, execution.Tuple, execution.Length, rows, &sequence) +} + +// invocationAdmission is the real Gateway owner for the CN-side execution +// resources. K is the active slot, the group owns its release, and the ledger +// retains the fencing key after completion so a late retry cannot create a new +// execution. The current W=1 exchange has one member per physical invocation; +// the group API remains able to grow to a multi-member scheduler later. +type invocationAdmission struct { + gateway *Gateway + group *protocol.ExecutionGroup + credit *protocol.LedgerCredit + key string + invocationID string + + mu sync.Mutex + finished bool + memberOpen bool +} + +func (g *Gateway) admitInvocation(invocation *udf.Invocation) (*invocationAdmission, error) { + if invocation == nil { + return nil, fmt.Errorf("python udf: cannot admit a nil invocation") + } + if err := invocation.Tuple.Validate(); err != nil { + return nil, err + } + if err := g.ensureAdmissionState(); err != nil { + return nil, err + } + + key, err := invocationLedgerKey(invocation.Tuple) + if err != nil { + return nil, err + } + g.admissionMu.Lock() + ledger := g.ledger + ttl := g.ledgerTTL + active := g.active + if err := g.claimGroupLocked(invocation.Tuple.GroupID, invocation.Tuple.GroupEpoch); err != nil { + g.admissionMu.Unlock() + return nil, err + } + g.admissionMu.Unlock() + if ledger == nil || active == nil { + g.releaseGroupClaim(invocation.Tuple.GroupID, invocation.Tuple.GroupEpoch) + return nil, fmt.Errorf("python udf: admission state is not initialized") + } + // Expire is intentionally limited to tombstones by TerminalLedger. An + // active invocation can leave this path only through finish/abandon. + // TTL alone cannot prove that a stale tuple can no longer receive a grant. + // The group owner supplies that proof; a closed epoch is the only fence + // that permits an expired tombstone to release its reserved capacity. + ledger.Expire(time.Now(), g.epochClosed) + entryBytes := int64(len(key)) + credit, err := ledger.Reserve(invocation.Tuple.GroupID, invocation.Tuple.GroupEpoch, 1, entryBytes) + if err != nil { + g.releaseGroupClaim(invocation.Tuple.GroupID, invocation.Tuple.GroupEpoch) + return nil, fmt.Errorf("RESOURCE_EXHAUSTED: Python UDF terminal ledger: %w", err) + } + if err := credit.Add(key, entryBytes, time.Now().Add(ttl)); err != nil { + credit.ReleaseUnused() + g.releaseGroupClaim(invocation.Tuple.GroupID, invocation.Tuple.GroupEpoch) + return nil, err + } + select { + case active <- struct{}{}: + // The group release callback returns this slot after the member is + // terminal. No queue is created, so caller vectors are never held while + // waiting for K. + default: + _ = ledger.Abandon(key) + credit.ReleaseUnused() + g.releaseGroupClaim(invocation.Tuple.GroupID, invocation.Tuple.GroupEpoch) + return nil, fmt.Errorf("RESOURCE_EXHAUSTED: Python UDF active invocation slots are full") + } + + var releaseOnce sync.Once + release := func() error { + releaseOnce.Do(func() { + <-active + }) + credit.ReleaseUnused() + return nil + } + group, err := protocol.NewExecutionGroup(invocation.Tuple.GroupID, invocation.Tuple.GroupEpoch, 1, release) + if err != nil { + <-active + _ = ledger.Abandon(key) + credit.ReleaseUnused() + g.releaseGroupClaim(invocation.Tuple.GroupID, invocation.Tuple.GroupEpoch) + return nil, err + } + token, err := group.BeginOpen(invocation.Tuple.InvocationID) + if err != nil { + _ = group.Close(protocol.ReasonPartialOpenError) + _ = ledger.Abandon(key) + g.releaseGroupClaim(invocation.Tuple.GroupID, invocation.Tuple.GroupEpoch) + return nil, err + } + if err := token.Commit(); err != nil { + _ = group.Close(protocol.ReasonPartialOpenError) + _ = ledger.Abandon(key) + g.releaseGroupClaim(invocation.Tuple.GroupID, invocation.Tuple.GroupEpoch) + return nil, err + } + return &invocationAdmission{ + gateway: g, + group: group, + credit: credit, + key: key, + invocationID: invocation.Tuple.InvocationID, + memberOpen: true, + }, nil +} + +func (g *Gateway) ensureAdmissionState() error { + g.admissionMu.Lock() + defer g.admissionMu.Unlock() + if g.active == nil { + maxActive := g.cfg.MaxActiveInvocations + if maxActive <= 0 { + maxActive = DefaultMaxActiveInvocations + } + g.active = make(chan struct{}, maxActive) + } + if g.ledger == nil { + maxEntries := g.cfg.MaxTerminalEntries + if maxEntries <= 0 { + maxEntries = DefaultMaxTerminalEntries + } + maxBytes := g.cfg.MaxTerminalBytes + if maxBytes <= 0 { + maxBytes = DefaultMaxTerminalBytes + } + ledger, err := protocol.NewTerminalLedger(maxEntries, maxBytes) + if err != nil { + return err + } + g.ledger = ledger + } + if g.ledgerTTL <= 0 { + g.ledgerTTL = g.cfg.TerminalRecordTTL + if g.ledgerTTL <= 0 { + g.ledgerTTL = DefaultTerminalRecordTTL + } + } + if g.admittedGroups == nil { + g.admittedGroups = make(map[string]uint64) + } + if g.closedGroups == nil { + g.closedGroups = make(map[string]uint64) + } + if g.closedGroupLimit <= 0 { + g.closedGroupLimit = g.cfg.MaxTerminalEntries + if g.closedGroupLimit <= 0 { + g.closedGroupLimit = DefaultMaxTerminalEntries + } + } + if g.closedGroupByteLimit <= 0 { + g.closedGroupByteLimit = g.cfg.MaxTerminalBytes + if g.closedGroupByteLimit <= 0 { + g.closedGroupByteLimit = DefaultMaxTerminalBytes + } + } + return nil +} + +func (g *Gateway) claimGroupLocked(groupID string, groupEpoch uint64) error { + if closedEpoch, ok := g.closedGroups[groupID]; ok && groupEpoch <= closedEpoch { + return fmt.Errorf("%w: execution group %q epoch %d is already closed", protocol.ErrDuplicate, groupID, groupEpoch) + } + if admittedEpoch, ok := g.admittedGroups[groupID]; ok { + return fmt.Errorf("%w: execution group %q epoch %d is already admitted", protocol.ErrDuplicate, groupID, admittedEpoch) + } + if _, exists := g.closedGroups[groupID]; !exists { + // Reserve the future tombstone while the group is active. Checking only + // closedGroups admits an unbounded number of concurrent groups between + // two completions and can overflow the bounded fence ledger when they + // drain together. + if len(g.closedGroups)+len(g.admittedGroups) >= g.closedGroupLimit || + g.closedGroupBytes+g.reservedGroupBytes+int64(len(groupID)+8) > g.closedGroupByteLimit { + return fmt.Errorf("%w: Python UDF closed-group fence is full", protocol.ErrLedgerFull) + } + g.reservedGroupBytes += int64(len(groupID) + 8) + } + g.admittedGroups[groupID] = groupEpoch + return nil +} + +func (g *Gateway) releaseGroupClaim(groupID string, groupEpoch uint64) { + g.admissionMu.Lock() + if admittedEpoch, ok := g.admittedGroups[groupID]; ok && admittedEpoch == groupEpoch { + delete(g.admittedGroups, groupID) + if _, closed := g.closedGroups[groupID]; !closed { + g.reservedGroupBytes -= int64(len(groupID) + 8) + } + } + g.admissionMu.Unlock() +} + +func (g *Gateway) closeGroupEpoch(groupID string, groupEpoch uint64) { + g.admissionMu.Lock() + if admittedEpoch, ok := g.admittedGroups[groupID]; ok && admittedEpoch == groupEpoch { + delete(g.admittedGroups, groupID) + if _, closed := g.closedGroups[groupID]; !closed { + g.reservedGroupBytes -= int64(len(groupID) + 8) + } + } + if closedEpoch := g.closedGroups[groupID]; groupEpoch > closedEpoch { + g.closedGroups[groupID] = groupEpoch + if closedEpoch == 0 { + g.closedGroupBytes += int64(len(groupID) + 8) + } + } + g.admissionMu.Unlock() +} + +func (g *Gateway) epochClosed(groupID string, groupEpoch uint64) bool { + g.admissionMu.Lock() + defer g.admissionMu.Unlock() + closedEpoch, ok := g.closedGroups[groupID] + return ok && groupEpoch <= closedEpoch +} + +func invocationLedgerKey(tuple protocol.FencingTuple) (string, error) { + wire, err := json.Marshal(tuple) + if err != nil { + return "", fmt.Errorf("python udf: encode invocation identity: %w", err) + } + digest := sha256.Sum256(wire) + return hex.EncodeToString(digest[:]), nil +} + +func admissionReason(ctx context.Context, attempted bool, err error) protocol.CloseReason { + if err == nil { + return protocol.ReasonInputEOF + } + if errors.Is(err, context.Canceled) || errors.Is(err, context.DeadlineExceeded) || + (ctx != nil && ctx.Err() != nil) { + return protocol.ReasonCancel + } + if !attempted { + return protocol.ReasonPartialOpenError + } + return protocol.ReasonFailure +} + +func (a *invocationAdmission) finish(attempted bool, reason protocol.CloseReason) error { + if a == nil { + return nil + } + a.mu.Lock() + defer a.mu.Unlock() + if a.finished { + return nil + } + + // The admission owner is normally reached from one defer. A release + // callback can still fail after the terminal protocol state is frozen, so + // perform one bounded retry here before returning. The group and ledger + // operations are idempotent; only the group release callback has meaningful + // retry state. If both attempts fail, leave `finished` false so a higher + // level shutdown/recovery owner can call finish again without losing the + // resources still owned by this admission. + for attempt := 0; attempt < 2; attempt++ { + var errs []error + if attempted { + if err := a.gateway.ledger.Complete(a.key); err != nil && !errors.Is(err, protocol.ErrUnknownIdentity) { + errs = append(errs, err) + } + } else if err := a.gateway.ledger.Abandon(a.key); err != nil && !errors.Is(err, protocol.ErrUnknownIdentity) { + errs = append(errs, err) + } + if a.memberOpen { + if err := a.group.MemberTerminal(a.invocationID); err != nil && !errors.Is(err, protocol.ErrUnknownIdentity) { + errs = append(errs, err) + } + } + if err := a.group.Close(reason); err != nil { + errs = append(errs, err) + } + if a.group.State() == protocol.GroupReleased { + a.gateway.closeGroupEpoch(a.group.ID(), a.group.Epoch()) + } + if err := errors.Join(errs...); err == nil { + a.finished = true + return nil + } else if attempt == 1 { + return err + } + } + return nil +} + +type capabilityRequest struct { + ProtocolVersion int `json:"protocol_version"` +} + +type capabilityResponse struct { + ProtocolVersion int `json:"protocol_version"` + ABIContract string `json:"abi_contract"` + AdapterVersion string `json:"adapter_version"` + SDKVersion string `json:"sdk_version"` + DefinitionSchemaVersion int `json:"definition_schema_version"` + PlanContractVersion int `json:"plan_contract_version"` + TypeDescriptorContract string `json:"type_descriptor_contract"` + TimezoneDatabaseVersion string `json:"timezone_database_version"` + Modes []string `json:"modes"` + NullPolicies []string `json:"null_policies"` + WindowBatches int `json:"window_batches"` + CumulativeAck bool `json:"cumulative_ack"` + MaxExecutionFrameBytes int64 `json:"max_execution_frame_bytes"` + MaxHandlerProcesses int `json:"max_handler_processes"` + MaxAccountHandlerProcesses int `json:"max_account_handler_processes"` + MaxOwnerHandlerProcesses int `json:"max_owner_handler_processes"` + LeaseEpoch uint64 `json:"lease_epoch"` +} + +// ensureCapabilities is the feature-level readiness gate. It runs once per +// Gateway connection before OpenInvocation, and it caches only a successful +// match. A worker with an unknown or different contract therefore cannot run +// user code, while ordinary SQL remains independent of this client. +func (g *Gateway) ensureCapabilities(ctx context.Context, client flight.FlightServiceClient) error { + g.capabilityMu.Lock() + defer g.capabilityMu.Unlock() + g.mu.Lock() + ready := g.capabilityReady + closed := g.closed + g.mu.Unlock() + if closed { + return errGatewayClosed + } + if ready { + g.mu.Lock() + leaseEpoch := g.workerLeaseEpoch + g.mu.Unlock() + if leaseEpoch != 0 { + return nil + } + // A partially initialized Gateway must not treat readiness as a valid + // worker contract. Fall through and establish the complete capability + // record again. + } + request, err := json.Marshal(capabilityRequest{ProtocolVersion: protocol.Version}) + if err != nil { + return err + } + actionCtx, cancel := context.WithTimeout(ctx, g.cfg.RequestTimeout) + defer cancel() + stream, err := client.DoAction(actionCtx, &flight.Action{Type: "GetPythonCapabilities", Body: request}) + if err != nil { + return fmt.Errorf("python udf: capability handshake: %w", err) + } + result, err := stream.Recv() + if err != nil { + return fmt.Errorf("python udf: capability handshake response: %w", err) + } + if result == nil { + return fmt.Errorf("python udf: empty capability handshake response") + } + var response capabilityResponse + decoder := json.NewDecoder(bytes.NewReader(result.Body)) + decoder.DisallowUnknownFields() + if err := decoder.Decode(&response); err != nil { + return fmt.Errorf("python udf: decode capability handshake: %w", err) + } + var trailing any + if err := decoder.Decode(&trailing); err != io.EOF { + return fmt.Errorf("python udf: capability handshake has trailing JSON") + } + if err := validateCapabilities(response); err != nil { + return err + } + if _, err := stream.Recv(); err != io.EOF { + if err == nil { + return fmt.Errorf("python udf: capability handshake returned multiple results") + } + return fmt.Errorf("python udf: capability handshake stream: %w", err) + } + g.mu.Lock() + if !g.closed { + g.capabilityReady = true + g.workerLeaseEpoch = response.LeaseEpoch + } + g.mu.Unlock() + return nil +} + +// invalidateCapabilities is called after a post-handshake execution error. +// In particular, a restarted worker keeps the same endpoint but advertises a +// new instance lease epoch; clearing the cached record makes the next +// invocation perform the handshake again. This never retries the failed +// invocation or changes its terminal outcome. +func (g *Gateway) invalidateCapabilities() { + g.capabilityMu.Lock() + defer g.capabilityMu.Unlock() + g.mu.Lock() + g.capabilityReady = false + g.workerLeaseEpoch = 0 + g.mu.Unlock() +} + +func validateCapabilities(response capabilityResponse) error { + if response.ProtocolVersion != protocol.Version || + response.ABIContract != udf.PythonABIContract || + response.AdapterVersion != udf.PythonAdapterVersion || + response.SDKVersion != udf.PythonSDKVersion || + response.DefinitionSchemaVersion != udf.PythonDefinitionSchemaVersion || + response.PlanContractVersion != udf.PythonPlanContractVersion || + response.TypeDescriptorContract != udf.PythonTypeDescriptorContract || + response.TimezoneDatabaseVersion != currentTimezoneDatabaseVersion() || + response.WindowBatches != 1 || response.CumulativeAck || + response.MaxExecutionFrameBytes != 1<<30 || + response.MaxHandlerProcesses != DefaultWorkerMaxHandlerProcesses || + response.MaxAccountHandlerProcesses != DefaultWorkerMaxAccountHandlers || + response.MaxOwnerHandlerProcesses != DefaultWorkerMaxOwnerHandlers || + response.LeaseEpoch == 0 { + return fmt.Errorf("python udf: worker capability contract does not match the current contract") + } + if !sameStrings(response.Modes, []string{ModeScalar, ModeVector}) || + !sameStrings(response.NullPolicies, []string{NullCallHandler, NullReturnNull}) { + return fmt.Errorf("python udf: worker capability set does not match the current contract") + } + return nil +} + +func currentTimezoneDatabaseVersion() string { + version, err := udf.TimezoneDatabaseVersion() + if err != nil { + return "" + } + return version +} + +func sameStrings(actual, expected []string) bool { + if len(actual) != len(expected) { + return false + } + for i := range expected { + if actual[i] != expected[i] { + return false + } + } + return true } func encodeInputBatch(inputs []*vector.Vector, args []types.Type, start, remaining, maxBytes, maxRows int64) (encodedRecordBatch, error) { @@ -274,6 +928,24 @@ func encodeInputBatch(inputs []*vector.Vector, args []types.Type, start, remaini if remaining > maxRows { remaining = maxRows } + // Fixed-width input records can be materialized once at the bounded row + // limit and sliced for each size probe. This removes repeated Arrow builder + // work without assuming anything about variable-length SQL values. The + // slice keeps the same backing buffers, and every candidate is still encoded + // and checked against maxBytes before publication. + if fixedWidthInputTypes(args) { + record, _, err := BuildInputRecordRange(inputs, args, int(start), int(remaining)) + if err != nil { + return encodedRecordBatch{}, err + } + defer record.Release() + tryEncode := func(rows int64) ([]ArrowFrame, error) { + candidate := record.NewSlice(0, rows) + defer candidate.Release() + return EncodeRecordBatch(candidate, maxBytes) + } + return chooseInputBatch(remaining, maxBytes, tryEncode) + } tryEncode := func(rows int64) ([]ArrowFrame, error) { record, _, err := BuildInputRecordRange(inputs, args, int(start), int(rows)) if err != nil { @@ -283,6 +955,19 @@ func encodeInputBatch(inputs []*vector.Vector, args []types.Type, start, remaini record.Release() return frames, encodeErr } + return chooseInputBatch(remaining, maxBytes, tryEncode) +} + +func fixedWidthInputTypes(args []types.Type) bool { + for _, typ := range args { + if typ.IsVarlen() { + return false + } + } + return true +} + +func chooseInputBatch(remaining, maxBytes int64, tryEncode func(int64) ([]ArrowFrame, error)) (encodedRecordBatch, error) { best, probe := int64(0), int64(1) var bestFrames []ArrowFrame failedAt := int64(0) @@ -410,6 +1095,10 @@ func (g *Gateway) receiveResultBatch( decoded.Release() return 0, err } + if limit := g.cfg.MaxInvocationResultBytes; limit > 0 && result.GetResultVector() != nil && int64(result.GetResultVector().Size()) > limit { + decoded.Release() + return 0, fmt.Errorf("RESOURCE_EXHAUSTED: Python UDF invocation result exceeds %d bytes", limit) + } decoded.Release() if err := g.action(ctx, client, "AcknowledgeResults", protocol.Control{Kind: "AcknowledgeResults", Tuple: tuple, AckSequence: control.Sequence}); err != nil { return 0, err @@ -440,6 +1129,9 @@ func (g *Gateway) receiveResultBatch( if control.Sequence != sequenceNumber { return 0, fmt.Errorf("python udf: input sequence %d was consumed while waiting for result %d", control.Sequence, sequenceNumber) } + if control.ReleasedBatches != 1 || control.ReleasedBytes <= 0 || control.ReleasedBytes > g.cfg.MaxBatchBytes { + return 0, fmt.Errorf("python udf: input sequence %d released invalid bytes/batches (%d/%d)", sequenceNumber, control.ReleasedBytes, control.ReleasedBatches) + } if inputConsumed { return 0, fmt.Errorf("python udf: input sequence %d was consumed more than once", sequenceNumber) } @@ -455,6 +1147,38 @@ func (g *Gateway) receiveResultBatch( } } +type executionBudget struct { + rows int64 + maxRows int64 + returnType types.Type + maxResultBytes int64 +} + +func validateInvocationBudget(budget executionBudget) error { + if budget.rows < 0 { + return fmt.Errorf("python udf: invocation row count is negative") + } + if budget.maxRows > 0 && budget.rows > budget.maxRows { + return fmt.Errorf("RESOURCE_EXHAUSTED: Python UDF invocation has %d rows, limit is %d", budget.rows, budget.maxRows) + } + if budget.maxResultBytes <= 0 || budget.rows == 0 { + return nil + } + // PreExtendAndReset allocates the fixed-width payload and the validity + // domain before the first result arrives. Check that unavoidable portion + // before OpenInvocation; variable-width payloads are checked again after + // each validated append because their area size depends on user values. + validityBytes := (budget.rows-1)/8 + 1 + if validityBytes >= budget.maxResultBytes { + return fmt.Errorf("RESOURCE_EXHAUSTED: Python UDF invocation result reservation exceeds %d bytes", budget.maxResultBytes) + } + perRow := int64(budget.returnType.TypeSize()) + if perRow > 0 && budget.rows > (budget.maxResultBytes-validityBytes)/perRow { + return fmt.Errorf("RESOURCE_EXHAUSTED: Python UDF invocation result reservation exceeds %d bytes", budget.maxResultBytes) + } + return nil +} + func validateResultSchemaMetadata(metadata []byte, tuple protocol.FencingTuple) error { control, err := protocol.UnmarshalControl(metadata) if err != nil || control.Tuple != tuple || control.Kind != "ResultSchema" { @@ -500,19 +1224,68 @@ func (g *Gateway) receiveFinish( !inputEnded || control.LastSequence != lastInput || lastResult != lastInput || acked != lastInput || !sequence.ReadyToFinish() { return fmt.Errorf("python udf: invalid Finish status=%q rows=%d", control.Status, rows) } - return g.action(ctx, client, "AcknowledgeFinish", protocol.Control{Kind: "AcknowledgeFinish", Tuple: tuple, FinishID: control.FinishID}) + if control.LastResultSequence != lastResult { + return fmt.Errorf("python udf: invalid Finish result sequence %d, expected %d", control.LastResultSequence, lastResult) + } + return g.acknowledgeFinish(ctx, client, "AcknowledgeFinish", protocol.Control{Kind: "AcknowledgeFinish", Tuple: tuple, FinishID: control.FinishID}) default: return fmt.Errorf("python udf: unexpected control %q", control.Kind) } } } +// acknowledgeFinish retries only the idempotent terminal ACK. A lost ACK +// request or response must not replay any STARTED input or handler work. +// Once the request budget is exhausted the caller preserves the distinction +// between an accepted terminal result and a locally unconfirmed Finish. +func (g *Gateway) acknowledgeFinish(ctx context.Context, client flight.FlightServiceClient, kind string, finish protocol.Control) error { + if client == nil { + return fmt.Errorf("FINISH_UNCONFIRMED: finish ACK client is unavailable") + } + firstErr := g.action(ctx, client, kind, finish) + if firstErr == nil { + return nil + } + if ctx != nil && ctx.Err() != nil { + return fmt.Errorf("FINISH_UNCONFIRMED: finish ACK budget expired: %w", firstErr) + } + if retryErr := g.action(ctx, client, kind, finish); retryErr == nil { + return nil + } else { + return fmt.Errorf("FINISH_UNCONFIRMED: finish ACK was not confirmed after idempotent retry: %v; retry: %w", firstErr, retryErr) + } +} + func validateInvocation(invocation *udf.Invocation) error { + if err := validateInvocationHeader(invocation); err != nil { + return err + } + if invocation.Source == "" { + return fmt.Errorf("python udf: handler and source are required") + } + if invocation.ArtifactDigest != udf.PythonInlineArtifactDigest(invocation.Handler, invocation.Source) { + return fmt.Errorf("UNSUPPORTED_ROUTINE_VERSION: Python artifact digest does not match the immutable source") + } + return nil +} + +// validateInvocationHeader checks every field that is independent of the +// artifact bytes. The CN must run this before an account-scoped artifact +// lookup; otherwise a malformed plan could make the resolver inspect an +// arbitrary tenant path before its FunctionRef and fencing tuple are known to +// be valid. +func validateInvocationHeader(invocation *udf.Invocation) error { + if invocation == nil { + return fmt.Errorf("python udf: nil invocation") + } if invocation.Language != "" && invocation.Language != udf.LanguagePython { return fmt.Errorf("python udf: unsupported language %q", invocation.Language) } - if invocation.Handler == "" || invocation.Source == "" { - return fmt.Errorf("python udf: handler and source are required") + if invocation.Handler == "" { + return fmt.Errorf("python udf: handler is required") + } + if strings.Contains(invocation.Handler, ":") { + return fmt.Errorf("UNSUPPORTED_ROUTINE_VERSION: Python external handler import requires an immutable artifact catalog") } if invocation.Length < 0 || len(invocation.Args) != len(invocation.Inputs) { return fmt.Errorf("python udf: invalid input shape") @@ -520,6 +1293,27 @@ func validateInvocation(invocation *udf.Invocation) error { if invocation.Mode != ModeScalar && invocation.Mode != ModeVector { return fmt.Errorf("python udf: unsupported mode %q", invocation.Mode) } + if invocation.CallsiteID == "" || len(invocation.CallsiteID) > 256 || strings.ContainsAny(invocation.CallsiteID, "\r\n") { + return fmt.Errorf("UNSUPPORTED_ROUTINE_VERSION: Python invocation has no valid callsite id") + } + if !invocation.MayError || invocation.SecurityMode != "INVOKER" || invocation.Leakproof { + return fmt.Errorf("UNSUPPORTED_ROUTINE_VERSION: Python invocation semantic contract is not supported") + } + if invocation.StatementContext == nil { + return fmt.Errorf("UNSUPPORTED_ROUTINE_VERSION: Python invocation has no statement context") + } + if err := invocation.StatementContext.Validate(); err != nil { + return err + } + if invocation.SecurityFrame == nil { + return fmt.Errorf("UNSUPPORTED_ROUTINE_VERSION: Python invocation has no security frame") + } + if err := invocation.SecurityFrame.Validate(); err != nil { + return err + } + if invocation.SecurityFrame.Mode != invocation.SecurityMode { + return fmt.Errorf("UNSUPPORTED_ROUTINE_VERSION: Python invocation security mode does not match its frame") + } if invocation.NullPolicy != NullCallHandler && invocation.NullPolicy != NullReturnNull { return fmt.Errorf("python udf: unsupported NULL policy %q", invocation.NullPolicy) } @@ -529,9 +1323,93 @@ func validateInvocation(invocation *udf.Invocation) error { if invocation.SDKVersion != udf.PythonSDKVersion { return fmt.Errorf("python udf: unsupported Python SDK %q", invocation.SDKVersion) } + if invocation.DefinitionSchemaVersion != udf.PythonDefinitionSchemaVersion { + return fmt.Errorf("UNSUPPORTED_ROUTINE_VERSION: unsupported Python definition schema %d", invocation.DefinitionSchemaVersion) + } + if !udf.IsSHA256Digest(invocation.ArtifactDigest) { + return fmt.Errorf("UNSUPPORTED_ROUTINE_VERSION: Python artifact digest does not match the immutable source") + } + environmentDigest, err := udf.PythonEnvironmentDigest() + if err != nil { + return fmt.Errorf("UNSUPPORTED_ROUTINE_VERSION: Python environment digest is unavailable: %w", err) + } + if invocation.EnvironmentDigest != environmentDigest { + return fmt.Errorf("UNSUPPORTED_ROUTINE_VERSION: Python environment digest does not match the current worker contract") + } + if !udf.IsSHA256Digest(invocation.DefinitionFingerprint) { + return fmt.Errorf("UNSUPPORTED_ROUTINE_VERSION: Python definition fingerprint is invalid") + } + args := make([]TypeDescriptor, len(invocation.Args)) + for index, typ := range invocation.Args { + descriptor, err := NewTypeDescriptor(typ) + if err != nil { + return fmt.Errorf("UNSUPPORTED_ROUTINE_VERSION: Python argument descriptor %d: %w", index, err) + } + args[index] = descriptor + } + returnDescriptor, err := NewTypeDescriptor(invocation.ReturnType) + if err != nil { + return fmt.Errorf("UNSUPPORTED_ROUTINE_VERSION: Python return descriptor: %w", err) + } + if err := validateInvocationInputs(invocation, args); err != nil { + return err + } + expectedFingerprint, err := DefinitionFingerprint( + invocation.DefinitionSchemaVersion, + invocation.Handler, invocation.Mode, invocation.NullPolicy, + invocation.ABIContract, invocation.AdapterVersion, + invocation.ArtifactDigest, invocation.EnvironmentDigest, invocation.SDKVersion, + args, returnDescriptor, + ) + if err != nil { + return fmt.Errorf("UNSUPPORTED_ROUTINE_VERSION: Python definition fingerprint cannot be computed: %w", err) + } + if invocation.DefinitionFingerprint != expectedFingerprint { + return fmt.Errorf("UNSUPPORTED_ROUTINE_VERSION: Python definition fingerprint does not match the typed definition") + } + if err := invocation.FunctionRef.Validate(); err != nil { + return fmt.Errorf("UNSUPPORTED_ROUTINE_VERSION: %w", err) + } + if invocation.FunctionRef.AccountID != invocation.Tuple.AccountID { + return fmt.Errorf("python udf: FunctionRef account does not match the fencing tuple") + } return invocation.Tuple.Validate() } +func validateInvocationInputs(invocation *udf.Invocation, args []TypeDescriptor) error { + if invocation == nil || len(invocation.Inputs) != len(args) { + return fmt.Errorf("python udf: invalid input shape") + } + // An empty physical selection has no input backing to inspect. The + // descriptor and arity have already been validated above; keeping this + // case explicit allows zero-row operators to avoid manufacturing vectors. + if invocation.Length == 0 { + return nil + } + for index, input := range invocation.Inputs { + if input == nil || input.GetType() == nil || input.Length() == 0 { + return fmt.Errorf("python udf: input column %d has no backing vector", index) + } + if !input.GetType().Eq(invocation.Args[index]) { + return fmt.Errorf( + "python udf: input column %d type %s does not match the frozen argument type %s", + index, + input.GetType().DescString(), + invocation.Args[index].DescString(), + ) + } + if !input.IsConst() && input.Length() < invocation.Length { + return fmt.Errorf( + "python udf: input column %d has %d rows, expected at least %d", + index, + input.Length(), + invocation.Length, + ) + } + } + return nil +} + func (g *Gateway) action(ctx context.Context, client flight.FlightServiceClient, kind string, control protocol.Control) error { body, err := protocol.MarshalControl(control) if err != nil { diff --git a/pkg/udf/python/gateway_admission_test.go b/pkg/udf/python/gateway_admission_test.go new file mode 100644 index 0000000000000..457f0d7a9308a --- /dev/null +++ b/pkg/udf/python/gateway_admission_test.go @@ -0,0 +1,200 @@ +// Copyright 2026 Matrix Origin +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. + +package python + +import ( + "errors" + "testing" + "time" + + "github.com/matrixorigin/matrixone/pkg/udf/protocol" + "github.com/stretchr/testify/require" +) + +func newAdmissionTestGateway(t *testing.T, maxActive int) *Gateway { + t.Helper() + gateway, err := NewGateway(ClientConfig{ + Enabled: true, + AllowUnisolated: true, + ServerAddress: "127.0.0.1:1", + MaxActiveInvocations: maxActive, + MaxTerminalEntries: 8, + MaxTerminalBytes: 1 << 20, + TerminalRecordTTL: time.Minute, + }) + require.NoError(t, err) + return gateway +} + +func TestGatewayAdmissionFencesDuplicateAndReleasesOnce(t *testing.T) { + gateway := newAdmissionTestGateway(t, 1) + in := validInvocation() + admission, err := gateway.admitInvocation(in) + require.NoError(t, err) + registered, active, inFlight := admission.group.Counts() + require.Equal(t, 1, registered) + require.Equal(t, 1, active) + require.Equal(t, 0, inFlight) + + _, err = gateway.admitInvocation(in) + require.ErrorIs(t, err, protocol.ErrDuplicate) + entries, _ := gateway.ledger.Counts() + require.Equal(t, 1, entries) + require.Len(t, gateway.active, 1) + + require.NoError(t, admission.finish(true, protocol.ReasonInputEOF)) + require.NoError(t, admission.finish(true, protocol.ReasonInputEOF)) + entries, _ = gateway.ledger.Counts() + require.Equal(t, 1, entries, "accepted execution keeps its tombstone") + require.Len(t, gateway.active, 0, "group owner releases K exactly once") + require.Equal(t, protocol.GroupReleased, admission.group.State()) + _, err = gateway.admitInvocation(in) + require.ErrorIs(t, err, protocol.ErrDuplicate) +} + +func TestGatewayAdmissionRejectsAtKWithoutLeavingLedgerEntry(t *testing.T) { + gateway := newAdmissionTestGateway(t, 1) + first := validInvocation() + firstAdmission, err := gateway.admitInvocation(first) + require.NoError(t, err) + + second := validInvocation() + second.Tuple.GroupID = "second-group" + second.Tuple.InvocationID = "second-invocation" + _, err = gateway.admitInvocation(second) + require.ErrorContains(t, err, "active invocation slots are full") + entries, _ := gateway.ledger.Counts() + require.Equal(t, 1, entries, "K rejection abandons the speculative ledger entry") + require.Len(t, gateway.active, 1) + + require.NoError(t, firstAdmission.finish(true, protocol.ReasonFailure)) + secondAdmission, err := gateway.admitInvocation(second) + require.NoError(t, err) + require.NoError(t, secondAdmission.finish(false, protocol.ReasonPartialOpenError)) + entries, _ = gateway.ledger.Counts() + require.Equal(t, 1, entries, "a pre-send failure abandons instead of tombstoning") + require.Len(t, gateway.active, 0) +} + +func TestGatewayAdmissionReclaimsOnlyClosedEpochTombstones(t *testing.T) { + gateway := newAdmissionTestGateway(t, 1) + gateway.cfg.MaxTerminalEntries = 1 + gateway.ledgerTTL = time.Nanosecond + first := validInvocation() + first.Tuple.GroupID = "reclaim-first" + first.Tuple.InvocationID = "reclaim-first-invocation" + firstAdmission, err := gateway.admitInvocation(first) + require.NoError(t, err) + require.NoError(t, firstAdmission.finish(true, protocol.ReasonInputEOF)) + entries, _ := gateway.ledger.Counts() + require.Equal(t, 1, entries) + gateway.ledgerTTL = time.Nanosecond + + replay := validInvocation() + replay.Tuple.GroupID = first.Tuple.GroupID + replay.Tuple.InvocationID = "reclaim-replay" + _, err = gateway.admitInvocation(replay) + require.ErrorIs(t, err, protocol.ErrDuplicate, "a closed group epoch remains fenced after its TTL") + + second := validInvocation() + second.Tuple.GroupID = "reclaim-second" + second.Tuple.InvocationID = "reclaim-second-invocation" + secondAdmission, err := gateway.admitInvocation(second) + require.NoError(t, err, "an owner-closed tombstone can release bounded ledger capacity") + require.NoError(t, secondAdmission.finish(false, protocol.ReasonPartialOpenError)) + entries, _ = gateway.ledger.Counts() + require.Zero(t, entries) +} + +func TestGatewayAdmissionAcceptedSendIsTerminalOnTransportFailure(t *testing.T) { + gateway := newAdmissionTestGateway(t, 1) + in := validInvocation() + admission, err := gateway.admitInvocation(in) + require.NoError(t, err) + require.NoError(t, admission.finish(true, protocol.ReasonFailure)) + _, err = gateway.admitInvocation(in) + require.Error(t, err) + require.True(t, errors.Is(err, protocol.ErrDuplicate)) + require.Len(t, gateway.active, 0) +} + +func TestGatewayAdmissionRetriesOwnerCleanupAfterGroupReleaseFailure(t *testing.T) { + gateway := newAdmissionTestGateway(t, 1) + require.NoError(t, gateway.ensureAdmissionState()) + + var releaseAttempts int + group, err := protocol.NewExecutionGroup( + "release-retry", 1, 1, + func() error { + releaseAttempts++ + if releaseAttempts <= 2 { + return errors.New("injected group release failure") + } + return nil + }, + ) + require.NoError(t, err) + token, err := group.BeginOpen("member") + require.NoError(t, err) + require.NoError(t, token.Commit()) + credit, err := gateway.ledger.Reserve("release-retry", 1, 1, 16) + require.NoError(t, err) + require.NoError(t, credit.Add("release-retry-key", 16, time.Now().Add(time.Minute))) + + admission := &invocationAdmission{ + gateway: gateway, group: group, credit: credit, + key: "release-retry-key", invocationID: "member", memberOpen: true, + } + require.ErrorContains(t, admission.finish(true, protocol.ReasonFailure), "injected group release failure") + require.Equal(t, protocol.GroupDraining, group.State()) + require.NoError(t, admission.finish(true, protocol.ReasonFailure)) + require.Equal(t, 3, releaseAttempts) + require.Equal(t, protocol.GroupReleased, group.State()) + require.NoError(t, admission.finish(true, protocol.ReasonFailure)) +} + +func TestGatewayAdmissionReservesClosedGroupCapacityWhileActive(t *testing.T) { + gateway := newAdmissionTestGateway(t, 2) + gateway.closedGroupLimit = 1 + gateway.closedGroupByteLimit = 1 << 20 + + first := validInvocation() + first.Tuple.GroupID = "reserved-first" + first.Tuple.InvocationID = "first" + firstAdmission, err := gateway.admitInvocation(first) + require.NoError(t, err) + + second := validInvocation() + second.Tuple.GroupID = "reserved-second" + second.Tuple.InvocationID = "second" + _, err = gateway.admitInvocation(second) + require.ErrorIs(t, err, protocol.ErrLedgerFull) + gateway.admissionMu.Lock() + require.Len(t, gateway.admittedGroups, 1) + require.Positive(t, gateway.reservedGroupBytes) + gateway.admissionMu.Unlock() + + require.NoError(t, firstAdmission.finish(true, protocol.ReasonInputEOF)) + gateway.admissionMu.Lock() + require.Empty(t, gateway.admittedGroups) + require.Zero(t, gateway.reservedGroupBytes) + require.Equal(t, uint64(1), gateway.closedGroups[first.Tuple.GroupID]) + gateway.admissionMu.Unlock() + + // A new epoch of the same group reuses its already-reserved fence. A + // different group remains rejected until the closed fence is reclaimed by a + // future owner protocol. + reentry := validInvocation() + reentry.Tuple.GroupID = first.Tuple.GroupID + reentry.Tuple.GroupEpoch = 2 + reentry.Tuple.InvocationID = "reentry" + reentryAdmission, err := gateway.admitInvocation(reentry) + require.NoError(t, err) + require.NoError(t, reentryAdmission.finish(false, protocol.ReasonPartialOpenError)) + + _, err = gateway.admitInvocation(second) + require.ErrorIs(t, err, protocol.ErrLedgerFull) +} diff --git a/pkg/udf/python/gateway_benchmark_test.go b/pkg/udf/python/gateway_benchmark_test.go new file mode 100644 index 0000000000000..2ab7f7d49dc40 --- /dev/null +++ b/pkg/udf/python/gateway_benchmark_test.go @@ -0,0 +1,118 @@ +// Copyright 2026 Matrix Origin +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. + +package python + +import ( + "testing" + + "github.com/matrixorigin/matrixone/pkg/common/mpool" + "github.com/matrixorigin/matrixone/pkg/container/types" + "github.com/matrixorigin/matrixone/pkg/container/vector" +) + +func benchmarkInputVector(b *testing.B, typ types.Type, rows int) (*vector.Vector, *mpool.MPool) { + b.Helper() + mp := mpool.MustNewZeroNoFixed() + input := vector.NewVec(typ) + for row := 0; row < rows; row++ { + var err error + switch typ.Oid { + case types.T_int64: + err = vector.AppendFixed(input, int64(row), false, mp) + case types.T_varchar: + err = vector.AppendBytes(input, []byte("matrixone-python-udf"), false, mp) + default: + b.Fatalf("unsupported benchmark type %s", typ) + } + if err != nil { + input.Free(mp) + mpool.DeleteMPool(mp) + b.Fatal(err) + } + } + return input, mp +} + +func BenchmarkEncodeInputBatchFixedWidth(b *testing.B) { + benchmarkEncodeInputBatch(b, types.T_int64.ToType()) +} + +func BenchmarkEncodeInputBatchFixedWidthRebuildBaseline(b *testing.B) { + const rows = 8192 + input, mp := benchmarkInputVector(b, types.T_int64.ToType(), rows) + defer func() { + input.Free(mp) + mpool.DeleteMPool(mp) + }() + + b.ReportAllocs() + b.SetBytes(int64(rows)) + b.ResetTimer() + for i := 0; i < b.N; i++ { + batch, err := encodeInputBatchRebuildBaseline( + []*vector.Vector{input}, []types.Type{types.T_int64.ToType()}, + 0, rows, DefaultMaxBatchBytes, rows, + ) + if err != nil { + b.Fatal(err) + } + if batch.Rows != rows || len(batch.Frames) != 2 { + b.Fatalf("unexpected encoded batch rows=%d frames=%d", batch.Rows, len(batch.Frames)) + } + } +} + +func BenchmarkEncodeInputBatchVariableWidth(b *testing.B) { + benchmarkEncodeInputBatch(b, types.New(types.T_varchar, 64, 0)) +} + +func benchmarkEncodeInputBatch(b *testing.B, typ types.Type) { + const rows = 8192 + input, mp := benchmarkInputVector(b, typ, rows) + defer func() { + input.Free(mp) + mpool.DeleteMPool(mp) + }() + + b.ReportAllocs() + b.SetBytes(int64(rows)) + b.ResetTimer() + for i := 0; i < b.N; i++ { + batch, err := encodeInputBatch( + []*vector.Vector{input}, []types.Type{typ}, 0, rows, + DefaultMaxBatchBytes, rows, + ) + if err != nil { + b.Fatal(err) + } + if batch.Rows != rows || len(batch.Frames) != 2 { + b.Fatalf("unexpected encoded batch rows=%d frames=%d", batch.Rows, len(batch.Frames)) + } + } +} + +// encodeInputBatchRebuildBaseline is a benchmark-only reference for the +// pre-optimization path. It intentionally rebuilds the Arrow record for every +// size probe, while production encodeInputBatch slices one fixed-width record. +func encodeInputBatchRebuildBaseline( + inputs []*vector.Vector, + args []types.Type, + start, remaining, maxBytes, maxRows int64, +) (encodedRecordBatch, error) { + if remaining > maxRows { + remaining = maxRows + } + tryEncode := func(rows int64) ([]ArrowFrame, error) { + record, _, err := BuildInputRecordRange(inputs, args, int(start), int(rows)) + if err != nil { + return nil, err + } + frames, encodeErr := EncodeRecordBatch(record, maxBytes) + record.Release() + return frames, encodeErr + } + return chooseInputBatch(remaining, maxBytes, tryEncode) +} diff --git a/pkg/udf/python/gateway_integration_test.go b/pkg/udf/python/gateway_integration_test.go new file mode 100644 index 0000000000000..5e78aaf52da88 --- /dev/null +++ b/pkg/udf/python/gateway_integration_test.go @@ -0,0 +1,631 @@ +// Copyright 2026 Matrix Origin +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. + +package python + +import ( + "context" + "fmt" + "net" + "os" + "os/exec" + "path/filepath" + "runtime" + "strconv" + "strings" + "sync/atomic" + "syscall" + "testing" + "time" + + "github.com/matrixorigin/matrixone/pkg/common/mpool" + "github.com/matrixorigin/matrixone/pkg/container/types" + "github.com/matrixorigin/matrixone/pkg/container/vector" + "github.com/matrixorigin/matrixone/pkg/defines" + "github.com/matrixorigin/matrixone/pkg/fileservice" + "github.com/matrixorigin/matrixone/pkg/udf" + "github.com/matrixorigin/matrixone/pkg/udf/protocol" + "github.com/stretchr/testify/require" +) + +var realWorkerBenchmarkRun atomic.Uint64 + +func TestGatewayExecutesAgainstRealPythonWorker(t *testing.T) { + python, err := exec.LookPath("python3") + require.NoError(t, err, "python3 is required for the real worker contract test") + workerPath := realWorkerPath(t) + port := freeTCPPort(t) + + cmd := startRealWorker(t, python, workerPath, port) + artifactFS, err := fileservice.NewMemoryFS(defines.SharedFileServiceName, fileservice.DisabledCacheConfig, nil) + require.NoError(t, err) + artifactStore, err := NewFileArtifactStore(artifactFS, DefaultMaxArtifactBytes) + require.NoError(t, err) + + cfg := ClientConfig{ + Enabled: true, + AllowUnisolated: true, + ServerAddress: "127.0.0.1:" + strconv.Itoa(port), + MaxBatchBytes: 1 << 20, + // Keep the integration path deliberately smaller than the input so a + // single Gateway.Execute exercises more than one W=1 batch and the + // bounded handler burst reuse contract. + MaxBatchRows: 2, + MaxActiveInvocations: 2, + RequestTimeout: 5 * time.Second, + MaxTerminalEntries: 32, + MaxTerminalBytes: 1 << 20, + TerminalRecordTTL: time.Minute, + } + gateway, err := NewGatewayWithArtifactStore(cfg, artifactStore) + require.NoError(t, err) + defer func() { + _ = gateway.Close() + stopRealWorker(cmd) + }() + + readyContext, cancel := context.WithTimeout(context.Background(), 5*time.Second) + require.NoError(t, gateway.CheckLanguageReady(readyContext, udf.LanguagePython)) + cancel() + + t.Run("scalar", func(t *testing.T) { + input, mp := integrationInput(t, []int64{1, 2, 3}) + defer func() { + input.Free(mp) + mpool.DeleteMPool(mp) + }() + result := vector.NewFunctionResultWrapper(types.T_int64.ToType(), mp) + defer result.Free() + invocation := integrationInvocation(ModeScalar, "def add(ctx, value): return value + 10", input, protocol.FencingTuple{ + AccountID: 1, StatementID: "real-flight-scalar", GroupID: "real-flight-scalar-group", + GroupEpoch: 1, InvocationID: "real-flight-scalar-invocation", LeaseEpoch: 1, + }) + publishIntegrationArtifact(t, artifactStore, invocation) + require.NoError(t, gateway.Execute(context.Background(), invocation, result, mp)) + require.Equal(t, uint64(1), invocation.Tuple.LeaseEpoch, "Gateway must not mutate the caller's tuple when attaching the worker lease") + require.Equal(t, []int64{11, 12, 13}, vector.MustFixedColNoTypeCheck[int64](result.GetResultVector())) + }) + + t.Run("zero-argument scalar", func(t *testing.T) { + mp := mpool.MustNewZeroNoFixed() + defer mpool.DeleteMPool(mp) + result := vector.NewFunctionResultWrapper(types.T_int64.ToType(), mp) + defer result.Free() + invocation := integrationInvocationForArgs( + ModeScalar, + "add", + "def add(ctx): return 42", + nil, + nil, + 1, + protocol.FencingTuple{ + AccountID: 1, StatementID: "real-flight-zero-arg-scalar", GroupID: "real-flight-zero-arg-scalar-group", + GroupEpoch: 1, InvocationID: "real-flight-zero-arg-scalar-invocation", LeaseEpoch: 1, + }, + ) + publishIntegrationArtifact(t, artifactStore, invocation) + require.NoError(t, gateway.Execute(context.Background(), invocation, result, mp)) + require.Equal(t, []int64{42}, vector.MustFixedColNoTypeCheck[int64](result.GetResultVector())) + }) + + t.Run("vector", func(t *testing.T) { + input, mp := integrationInput(t, []int64{4, 5, 6}) + defer func() { + input.Free(mp) + mpool.DeleteMPool(mp) + }() + result := vector.NewFunctionResultWrapper(types.T_int64.ToType(), mp) + defer result.Free() + invocation := integrationInvocation( + ModeVector, + "import pyarrow as pa\ndef add(ctx, values): return pa.array([value.as_py() + 20 for value in values], type=pa.int64())", + input, + protocol.FencingTuple{ + AccountID: 1, StatementID: "real-flight-vector", GroupID: "real-flight-vector-group", + GroupEpoch: 1, InvocationID: "real-flight-vector-invocation", LeaseEpoch: 1, + }, + ) + publishIntegrationArtifact(t, artifactStore, invocation) + require.NoError(t, gateway.Execute(context.Background(), invocation, result, mp)) + require.Equal(t, []int64{24, 25, 26}, vector.MustFixedColNoTypeCheck[int64](result.GetResultVector())) + }) + + t.Run("zero-argument vector", func(t *testing.T) { + const rows = 4 + resultMP := mpool.MustNewZeroNoFixed() + defer mpool.DeleteMPool(resultMP) + result := vector.NewFunctionResultWrapper(types.T_int64.ToType(), resultMP) + defer result.Free() + source := "import pyarrow as pa\ndef add(ctx): return pa.array([ctx.num_rows] * ctx.num_rows, type=pa.int64())" + invocation := integrationZeroArgVectorInvocation(source, rows, protocol.FencingTuple{ + AccountID: 1, StatementID: "real-flight-zero-arg", GroupID: "real-flight-zero-arg-group", + GroupEpoch: 1, InvocationID: "real-flight-zero-arg-invocation", LeaseEpoch: 1, + }) + publishIntegrationArtifact(t, artifactStore, invocation) + require.NoError(t, gateway.Execute(context.Background(), invocation, result, resultMP)) + // MaxBatchRows is two, so each vector invocation receives the exact + // physical batch shape rather than the total statement length. + require.Equal(t, []int64{2, 2, 2, 2}, vector.MustFixedColNoTypeCheck[int64](result.GetResultVector())) + }) +} + +func TestSupervisorExecutesAgainstRealPythonWorker(t *testing.T) { + python, err := exec.LookPath("python3") + require.NoError(t, err, "python3 is required for the Supervisor integration test") + workerPath := realWorkerPath(t) + port := freeTCPPort(t) + supervisor, err := NewSupervisor(Config{ + Address: "127.0.0.1:" + strconv.Itoa(port), + Path: filepath.Dir(workerPath), + Python: python, + }) + require.NoError(t, err) + require.NoError(t, supervisor.Start()) + workerDone := supervisor.Done() + require.NotNil(t, workerDone) + t.Cleanup(func() { + _ = supervisor.Close() + }) + waitForWorkerPort(t, port) + + artifactFS, err := fileservice.NewMemoryFS(defines.SharedFileServiceName, fileservice.DisabledCacheConfig, nil) + require.NoError(t, err) + artifactStore, err := NewFileArtifactStore(artifactFS, DefaultMaxArtifactBytes) + require.NoError(t, err) + gateway, err := NewGatewayWithArtifactStore(ClientConfig{ + Enabled: true, + AllowUnisolated: true, + ServerAddress: "127.0.0.1:" + strconv.Itoa(port), + MaxBatchBytes: 1 << 20, + MaxBatchRows: 2, + MaxInvocationRows: 32, + MaxInvocationResultBytes: 1 << 20, + MaxActiveInvocations: 1, + RequestTimeout: 5 * time.Second, + MaxTerminalEntries: 16, + MaxTerminalBytes: 1 << 20, + TerminalRecordTTL: time.Minute, + }, artifactStore) + require.NoError(t, err) + defer gateway.Close() + readyContext, cancel := context.WithTimeout(context.Background(), 5*time.Second) + require.NoError(t, gateway.CheckLanguageReady(readyContext, udf.LanguagePython)) + cancel() + + input, mp := integrationInput(t, []int64{5, 6, 7}) + defer func() { + input.Free(mp) + mpool.DeleteMPool(mp) + }() + result := vector.NewFunctionResultWrapper(types.T_int64.ToType(), mp) + defer result.Free() + invocation := integrationInvocation(ModeScalar, "def add(ctx, value): return value + 30", input, protocol.FencingTuple{ + AccountID: 1, StatementID: "supervisor-flight", GroupID: "supervisor-flight-group", + GroupEpoch: 1, InvocationID: "supervisor-flight-invocation", LeaseEpoch: 1, + }) + publishIntegrationArtifact(t, artifactStore, invocation) + require.NoError(t, gateway.Execute(context.Background(), invocation, result, mp)) + require.Equal(t, []int64{35, 36, 37}, vector.MustFixedColNoTypeCheck[int64](result.GetResultVector())) + select { + case <-workerDone: + t.Fatal("Python worker exited during a successful Supervisor integration") + default: + } +} + +func publishIntegrationArtifact(t testing.TB, store *FileArtifactStore, invocation *udf.Invocation) { + t.Helper() + source := invocation.Source + require.NotEmpty(t, source) + _, err := store.Publish(context.Background(), invocation.FunctionRef.AccountID, invocation.Handler, source) + require.NoError(t, err) + invocation.Source = "" +} + +func TestGatewayRejectsThePreviousWorkerLeaseAfterRestart(t *testing.T) { + python, err := exec.LookPath("python3") + require.NoError(t, err, "python3 is required for the real worker contract test") + workerPath := realWorkerPath(t) + port := freeTCPPort(t) + first := startRealWorker(t, python, workerPath, port) + + cfg := ClientConfig{ + Enabled: true, + AllowUnisolated: true, + ServerAddress: "127.0.0.1:" + strconv.Itoa(port), + MaxBatchBytes: 1 << 20, + MaxBatchRows: 1024, + MaxActiveInvocations: 1, + RequestTimeout: 2 * time.Second, + MaxTerminalEntries: 32, + MaxTerminalBytes: 1 << 20, + TerminalRecordTTL: time.Minute, + } + gateway, err := NewGateway(cfg) + require.NoError(t, err) + defer gateway.Close() + readyContext, cancel := context.WithTimeout(context.Background(), 5*time.Second) + require.NoError(t, gateway.CheckLanguageReady(readyContext, udf.LanguagePython)) + cancel() + + stopRealWorker(first) + second := startRealWorker(t, python, workerPath, port) + defer stopRealWorker(second) + + input, mp := integrationInput(t, []int64{9}) + defer func() { + input.Free(mp) + mpool.DeleteMPool(mp) + }() + firstResult := vector.NewFunctionResultWrapper(types.T_int64.ToType(), mp) + defer firstResult.Free() + firstInvocation := integrationInvocation(ModeScalar, "def add(ctx, value): return value + 1", input, protocol.FencingTuple{ + AccountID: 1, StatementID: "restart-statement", GroupID: "restart-group", GroupEpoch: 1, + InvocationID: "restart-invocation", LeaseEpoch: 1, + }) + // The Gateway has a cached lease from the first worker. The second worker + // must reject it before handler admission; the failed invocation is never + // transparently replayed. + require.Error(t, gateway.Execute(context.Background(), firstInvocation, firstResult, mp)) + require.Equal(t, uint64(1), firstInvocation.Tuple.LeaseEpoch) + + secondResult := vector.NewFunctionResultWrapper(types.T_int64.ToType(), mp) + defer secondResult.Free() + secondInvocation := integrationInvocation(ModeScalar, "def add(ctx, value): return value + 1", input, protocol.FencingTuple{ + AccountID: 1, StatementID: "restart-statement", GroupID: "restart-group-2", GroupEpoch: 1, + InvocationID: "restart-invocation-2", LeaseEpoch: 1, + }) + require.NoError(t, gateway.Execute(context.Background(), secondInvocation, secondResult, mp)) + require.Equal(t, []int64{10}, vector.MustFixedColNoTypeCheck[int64](secondResult.GetResultVector())) +} + +func TestGatewayCloseCancelsAnInFlightExchange(t *testing.T) { + python, err := exec.LookPath("python3") + require.NoError(t, err, "python3 is required for the real worker contract test") + workerPath := realWorkerPath(t) + port := freeTCPPort(t) + worker := startRealWorker(t, python, workerPath, port) + + marker := filepath.Join(t.TempDir(), "handler-started") + // The handler has a deterministic start barrier and then never returns on + // its own. Gateway.Close must cancel the Flight exchange and cause the + // worker's independent cleanup path to terminate this process group. + source := fmt.Sprintf(`import pathlib +import time + +def add(ctx, value): + pathlib.Path(%q).write_text(str(__import__("os").getpid())) + while True: + time.sleep(1) +`, marker) + + gateway, err := NewGateway(ClientConfig{ + Enabled: true, + AllowUnisolated: true, + ServerAddress: "127.0.0.1:" + strconv.Itoa(port), + MaxBatchBytes: 1 << 20, + MaxBatchRows: 1, + MaxActiveInvocations: 1, + RequestTimeout: 30 * time.Second, + MaxTerminalEntries: 32, + MaxTerminalBytes: 1 << 20, + TerminalRecordTTL: time.Minute, + }) + require.NoError(t, err) + defer func() { + _ = gateway.Close() + stopRealWorker(worker) + }() + + readyContext, cancel := context.WithTimeout(context.Background(), 5*time.Second) + require.NoError(t, gateway.CheckLanguageReady(readyContext, udf.LanguagePython)) + cancel() + + input, mp := integrationInput(t, []int64{1}) + result := vector.NewFunctionResultWrapper(types.T_int64.ToType(), mp) + invocation := integrationInvocation(ModeScalar, source, input, protocol.FencingTuple{ + AccountID: 1, StatementID: "gateway-close", GroupID: "gateway-close-group", + GroupEpoch: 1, InvocationID: "gateway-close-invocation", LeaseEpoch: 1, + }) + done := make(chan error, 1) + go func() { + done <- gateway.Execute(context.Background(), invocation, result, mp) + }() + + require.Eventually(t, func() bool { + _, statErr := os.Stat(marker) + return statErr == nil + }, 5*time.Second, 10*time.Millisecond, "handler did not reach the cancellation barrier") + require.NoError(t, gateway.Close()) + select { + case err := <-done: + require.Error(t, err, "closing the Gateway must not report a successful in-flight invocation") + case <-time.After(5 * time.Second): + t.Fatal("Gateway.Close did not cancel the in-flight exchange") + } + pidBytes, err := os.ReadFile(marker) + require.NoError(t, err) + handlerPID, err := strconv.Atoi(string(pidBytes)) + require.NoError(t, err) + if runtime.GOOS != "windows" { + require.Eventually(t, func() bool { + return syscall.Kill(handlerPID, 0) != nil + }, 5*time.Second, 10*time.Millisecond, "handler process survived Gateway.Close") + } + // The transport error is only half of the shutdown contract. The owner + // cleanup must also have removed the active admission, released K, and + // retained exactly one terminal fence for the accepted Open. Inspect these + // after Execute has returned so the assertion is about the real Flight path, + // rather than a helper's cleanup callback. + gateway.admissionMu.Lock() + require.Empty(t, gateway.admittedGroups) + require.Empty(t, gateway.active) + closedEpoch := gateway.closedGroups[invocation.Tuple.GroupID] + gateway.admissionMu.Unlock() + require.Equal(t, uint64(1), closedEpoch) + entries, _ := gateway.ledger.Counts() + require.Equal(t, 1, entries, "accepted Open retains one terminal tombstone") + + input.Free(mp) + result.Free() + mpool.DeleteMPool(mp) +} + +// BenchmarkGatewayRealPythonWorkerBurst measures the real Flight path with a +// fixed two-row batch. The first sub-benchmark performs eight independent +// one-batch invocations; the second performs one invocation containing eight +// batches. The handler records module loads in a test-owned file, which makes +// the child/import count observable instead of inferring reuse from elapsed +// time. This is a W=1 comparison: it measures bounded burst reuse and does not +// claim a W>1 pipeline benefit. +func BenchmarkGatewayRealPythonWorkerBurst(b *testing.B) { + python, err := exec.LookPath("python3") + if err != nil { + b.Skip("python3 is required for the real worker benchmark") + } + workerPath := realWorkerPath(b) + port := freeTCPPort(b) + worker := startRealWorker(b, python, workerPath, port) + defer stopRealWorker(worker) + + gateway, err := NewGateway(ClientConfig{ + Enabled: true, + AllowUnisolated: true, + ServerAddress: "127.0.0.1:" + strconv.Itoa(port), + MaxBatchBytes: 1 << 20, + MaxBatchRows: 2, + MaxActiveInvocations: 2, + MaxInvocationRows: 1024, + MaxInvocationResultBytes: 1 << 20, + RequestTimeout: 30 * time.Second, + MaxTerminalEntries: 1024, + MaxTerminalBytes: 1 << 20, + TerminalRecordTTL: time.Minute, + }) + if err != nil { + b.Fatal(err) + } + defer gateway.Close() + readyContext, cancel := context.WithTimeout(context.Background(), 5*time.Second) + if err := gateway.CheckLanguageReady(readyContext, udf.LanguagePython); err != nil { + cancel() + b.Fatal(err) + } + cancel() + + benchmark := func(b *testing.B, rows, calls int, label string) { + marker := filepath.Join(b.TempDir(), label+"-module-loads") + runID := realWorkerBenchmarkRun.Add(1) + source := fmt.Sprintf(`from pathlib import Path +with Path(%q).open("a", encoding="utf-8") as marker: + marker.write("loaded\n") + +def add(ctx, value): + return value + 1 +`, marker) + input, mp := integrationInput(b, make([]int64, rows)) + for index := range make([]int64, rows) { + vector.MustFixedColNoTypeCheck[int64](input)[index] = int64(index) + } + result := vector.NewFunctionResultWrapper(types.T_int64.ToType(), mp) + defer func() { + input.Free(mp) + result.Free() + mpool.DeleteMPool(mp) + }() + + b.ReportAllocs() + b.ResetTimer() + for iteration := 0; iteration < b.N; iteration++ { + for call := 0; call < calls; call++ { + invocation := integrationInvocation( + ModeScalar, source, input, + protocol.FencingTuple{ + AccountID: 1, + StatementID: fmt.Sprintf("benchmark-%s-%d-%d-%d", label, runID, iteration, call), + GroupID: fmt.Sprintf("benchmark-%s-group-%d-%d-%d", label, runID, iteration, call), + GroupEpoch: 1, + InvocationID: fmt.Sprintf("benchmark-%s-invocation-%d-%d-%d", label, runID, iteration, call), + LeaseEpoch: 1, + }, + ) + if err := gateway.Execute(context.Background(), invocation, result, mp); err != nil { + b.Fatal(err) + } + } + } + b.StopTimer() + loads, err := os.ReadFile(marker) + if err != nil { + b.Fatal(err) + } + actualLoads := strings.Count(string(loads), "loaded\n") + if actualLoads == 0 { + b.Fatal("the handler did not record a module load") + } + b.ReportMetric(float64(rows*calls*b.N)/float64(b.Elapsed().Nanoseconds())*1e9, "rows/s") + b.Logf("batches/invocation=%d module_loads=%d", rows/2, actualLoads) + } + + b.Run("eight_independent_one_batch_invocations", func(b *testing.B) { + benchmark(b, 2, 8, "independent") + }) + b.Run("one_eight_batch_invocation", func(b *testing.B) { + benchmark(b, 16, 1, "burst") + }) +} + +func realWorkerPath(t testing.TB) string { + t.Helper() + _, sourceFile, _, ok := runtime.Caller(0) + require.True(t, ok) + path := filepath.Join(filepath.Dir(sourceFile), "worker", "worker.py") + _, err := os.Stat(path) + require.NoError(t, err) + return path +} + +func freeTCPPort(t testing.TB) int { + t.Helper() + listener, err := net.Listen("tcp", "127.0.0.1:0") + require.NoError(t, err) + defer listener.Close() + return listener.Addr().(*net.TCPAddr).Port +} + +func waitForWorkerPort(t testing.TB, port int) { + t.Helper() + deadline := time.Now().Add(5 * time.Second) + ticker := time.NewTicker(10 * time.Millisecond) + defer ticker.Stop() + for { + connection, err := net.DialTimeout("tcp", "127.0.0.1:"+strconv.Itoa(port), 100*time.Millisecond) + if err == nil { + _ = connection.Close() + return + } + if time.Now().After(deadline) { + require.NoError(t, err, "Python worker did not listen before the startup deadline") + } + <-ticker.C + } +} + +func startRealWorker(t testing.TB, python, workerPath string, port int) *exec.Cmd { + t.Helper() + cmd := exec.Command(python, workerPath, "--address=grpc+tcp://127.0.0.1:"+strconv.Itoa(port)) + cmd.Stdout = os.Stdout + cmd.Stderr = os.Stderr + cmd.Env = append(os.Environ(), "PYTHONUNBUFFERED=1") + if runtime.GOOS != "windows" { + cmd.SysProcAttr = &syscall.SysProcAttr{Setpgid: true} + } + require.NoError(t, cmd.Start()) + waitForWorkerPort(t, port) + return cmd +} + +func stopRealWorker(cmd *exec.Cmd) { + if cmd == nil || cmd.Process == nil || cmd.ProcessState != nil { + return + } + if runtime.GOOS != "windows" { + _ = syscall.Kill(-cmd.Process.Pid, syscall.SIGKILL) + } else { + _ = cmd.Process.Kill() + } + _, _ = cmd.Process.Wait() +} + +func integrationInput(t testing.TB, values []int64) (*vector.Vector, *mpool.MPool) { + t.Helper() + mp := mpool.MustNewZeroNoFixed() + input := vector.NewVec(types.T_int64.ToType()) + for _, value := range values { + require.NoError(t, vector.AppendFixed(input, value, false, mp)) + } + return input, mp +} + +func integrationInvocation(mode, source string, input *vector.Vector, tuple protocol.FencingTuple) *udf.Invocation { + return integrationInvocationForArgs( + mode, "add", source, + []types.Type{types.T_int64.ToType()}, []*vector.Vector{input}, input.Length(), tuple, + ) +} + +func integrationZeroArgVectorInvocation(source string, length int, tuple protocol.FencingTuple) *udf.Invocation { + return integrationInvocationForArgs(ModeVector, "add", source, nil, nil, length, tuple) +} + +func integrationInvocationForArgs( + mode, handler, source string, + args []types.Type, + inputs []*vector.Vector, + length int, + tuple protocol.FencingTuple, +) *udf.Invocation { + environmentDigest := func() string { + digest, _ := udf.PythonEnvironmentDigest() + return digest + }() + argumentDescriptors := make([]TypeDescriptor, len(args)) + for index, typ := range args { + argumentDescriptors[index], _ = NewTypeDescriptor(typ) + } + returnDescriptor, _ := NewTypeDescriptor(types.T_int64.ToType()) + statementValues := map[string]string{ + "statement_id": tuple.StatementID, + "statement_timestamp_utc": "1704067200123456", + "session_timezone_kind": "FIXED_OFFSET", + "session_timezone_offset_minutes": "+480", + "sql_mode": "[\"ANSI\",\"STRICT_TRANS_TABLES\"]", + "current_database": "udf_integration", + "current_user": "root", + "current_role": "writer", + "connection_collation": "utf8mb4_bin", + } + statementContext, _ := udf.StatementContextFromMap(statementValues) + return &udf.Invocation{ + FunctionRef: udf.FunctionRef{ + AccountID: tuple.AccountID, DatabaseID: 2, FunctionID: 3, Revision: 1, NamespaceVersion: 1, + }, + Language: udf.LanguagePython, + Handler: handler, + Source: source, + Args: args, + ReturnType: types.T_int64.ToType(), + Inputs: inputs, + Length: length, + Mode: mode, + NullPolicy: NullCallHandler, + ABIContract: udf.PythonABIContract, + AdapterVersion: udf.PythonAdapterVersion, + SDKVersion: udf.PythonSDKVersion, + DefinitionSchemaVersion: udf.PythonDefinitionSchemaVersion, + ArtifactDigest: udf.PythonInlineArtifactDigest(handler, source), + EnvironmentDigest: environmentDigest, + DefinitionFingerprint: func() string { + fingerprint, _ := DefinitionFingerprint( + udf.PythonDefinitionSchemaVersion, + handler, mode, NullCallHandler, + udf.PythonABIContract, udf.PythonAdapterVersion, + udf.PythonInlineArtifactDigest(handler, source), environmentDigest, + udf.PythonSDKVersion, argumentDescriptors, returnDescriptor, + ) + return fingerprint + }(), + CallsiteID: "python/integration", + MayError: true, + SecurityMode: "INVOKER", + StatementContext: statementContext, + SecurityFrame: &udf.SecurityFrame{ + ContractVersion: udf.SecurityFrameContractVersion, + Mode: "INVOKER", + }, + Context: statementValues, + Tuple: tuple, + } +} diff --git a/pkg/udf/python/gateway_test.go b/pkg/udf/python/gateway_test.go index f4def0e6051a1..c313e81eed2a7 100644 --- a/pkg/udf/python/gateway_test.go +++ b/pkg/udf/python/gateway_test.go @@ -7,11 +7,14 @@ package python import ( "context" + "errors" "io" + "strings" "testing" "time" "github.com/apache/arrow-go/v18/arrow/flight/gen/flight" + "github.com/matrixorigin/matrixone/pkg/common/mpool" "github.com/matrixorigin/matrixone/pkg/container/types" "github.com/matrixorigin/matrixone/pkg/container/vector" "github.com/matrixorigin/matrixone/pkg/udf" @@ -26,6 +29,66 @@ type gatewayResultStream struct { index int } +type finishAckActionStream struct { + grpc.ClientStream + result *flight.Result + err error + read bool +} + +func (s *finishAckActionStream) Recv() (*flight.Result, error) { + if !s.read { + s.read = true + if s.err != nil { + return nil, s.err + } + return s.result, nil + } + return nil, io.EOF +} + +type finishAckFlightClient struct { + flight.FlightServiceClient + calls int + actions []*flight.Action +} + +type recordingArtifactResolver struct { + source string + calls int + account uint64 + handler string + digest string +} + +func (r *recordingArtifactResolver) Resolve(_ context.Context, accountID uint64, handler, digest string) (string, error) { + r.calls++ + r.account = accountID + r.handler = handler + r.digest = digest + return r.source, nil +} + +func (c *finishAckFlightClient) DoAction( + ctx context.Context, + action *flight.Action, + opts ...grpc.CallOption, +) (flight.FlightService_DoActionClient, error) { + c.calls++ + c.actions = append(c.actions, action) + if c.calls == 1 { + return &finishAckActionStream{err: errors.New("injected ACK response loss")}, nil + } + tuple := validInvocation().Tuple + ack, err := protocol.MarshalControl(protocol.Control{ + Kind: "Ack", Tuple: tuple, Status: statusOK, FinishID: "finish-1", + }) + if err != nil { + return nil, err + } + return &finishAckActionStream{result: &flight.Result{Body: ack}}, nil +} + func (s *gatewayResultStream) Recv() (*flight.FlightData, error) { if s.index == len(s.results) { return nil, io.EOF @@ -52,18 +115,54 @@ func gatewayControl(t *testing.T, kind string, tuple protocol.FencingTuple, fiel func validInvocation() *udf.Invocation { return &udf.Invocation{ - Language: udf.LanguagePython, - Handler: "add", - Source: "def add(ctx, value): return value", - Args: []types.Type{types.T_int64.ToType()}, - ReturnType: types.T_int64.ToType(), - Length: 0, - Inputs: []*vector.Vector{nil}, - Mode: ModeScalar, - NullPolicy: NullCallHandler, - ABIContract: udf.PythonABIContract, - AdapterVersion: udf.PythonAdapterVersion, - SDKVersion: udf.PythonSDKVersion, + FunctionRef: udf.FunctionRef{ + AccountID: 1, DatabaseID: 2, FunctionID: 3, Revision: 1, NamespaceVersion: 1, + }, + Language: udf.LanguagePython, + Handler: "add", + Source: "def add(ctx, value): return value", + Args: []types.Type{types.T_int64.ToType()}, + ReturnType: types.T_int64.ToType(), + Length: 0, + Inputs: []*vector.Vector{nil}, + Mode: ModeScalar, + NullPolicy: NullCallHandler, + ABIContract: udf.PythonABIContract, + AdapterVersion: udf.PythonAdapterVersion, + SDKVersion: udf.PythonSDKVersion, + DefinitionSchemaVersion: udf.PythonDefinitionSchemaVersion, + ArtifactDigest: udf.PythonInlineArtifactDigest("add", "def add(ctx, value): return value"), + EnvironmentDigest: func() string { digest, _ := udf.PythonEnvironmentDigest(); return digest }(), + DefinitionFingerprint: func() string { + argument, _ := NewTypeDescriptor(types.T_int64.ToType()) + fingerprint, _ := DefinitionFingerprint( + udf.PythonDefinitionSchemaVersion, + "add", ModeScalar, NullCallHandler, + udf.PythonABIContract, udf.PythonAdapterVersion, + udf.PythonInlineArtifactDigest("add", "def add(ctx, value): return value"), + func() string { digest, _ := udf.PythonEnvironmentDigest(); return digest }(), + udf.PythonSDKVersion, []TypeDescriptor{argument}, argument, + ) + return fingerprint + }(), + CallsiteID: "python/test", + MayError: true, + SecurityMode: "INVOKER", + StatementContext: &udf.StatementContext{ + ContractVersion: udf.StatementContextContractVersion, + StatementTimestampUTC: 1704067200123456, + TimezoneKind: "FIXED_OFFSET", + TimezoneOffsetMinutes: 480, + SQLMode: []string{"ANSI", "STRICT_TRANS_TABLES"}, + CurrentDatabase: "udf", + CurrentUser: "root", + CurrentRole: "writer", + ConnectionCollation: "utf8mb4_bin", + }, + SecurityFrame: &udf.SecurityFrame{ + ContractVersion: udf.SecurityFrameContractVersion, + Mode: "INVOKER", + }, Tuple: protocol.FencingTuple{ AccountID: 1, StatementID: "statement", GroupID: "group", GroupEpoch: 1, InvocationID: "invocation", LeaseEpoch: 1, @@ -80,10 +179,18 @@ func TestValidateInvocationRequiresFrozenContract(t *testing.T) { mutate func(*udf.Invocation) }{ {name: "mode", mutate: func(invocation *udf.Invocation) { invocation.Mode = "" }}, + {name: "external handler", mutate: func(invocation *udf.Invocation) { invocation.Handler = "module:add" }}, {name: "null policy", mutate: func(invocation *udf.Invocation) { invocation.NullPolicy = "" }}, {name: "ABI", mutate: func(invocation *udf.Invocation) { invocation.ABIContract = "" }}, {name: "adapter", mutate: func(invocation *udf.Invocation) { invocation.AdapterVersion = "" }}, {name: "SDK", mutate: func(invocation *udf.Invocation) { invocation.SDKVersion = "" }}, + {name: "definition schema", mutate: func(invocation *udf.Invocation) { invocation.DefinitionSchemaVersion = 0 }}, + {name: "artifact digest", mutate: func(invocation *udf.Invocation) { invocation.ArtifactDigest = "" }}, + {name: "environment digest", mutate: func(invocation *udf.Invocation) { invocation.EnvironmentDigest = "" }}, + {name: "definition fingerprint", mutate: func(invocation *udf.Invocation) { invocation.DefinitionFingerprint = "" }}, + {name: "definition fingerprint contents", mutate: func(invocation *udf.Invocation) { + invocation.DefinitionFingerprint = strings.Repeat("b", 64) + }}, } for _, test := range cases { t.Run(test.name, func(t *testing.T) { @@ -94,6 +201,160 @@ func TestValidateInvocationRequiresFrozenContract(t *testing.T) { } } +func TestGatewayValidatesIdentityBeforeResolvingArtifact(t *testing.T) { + resolver := &recordingArtifactResolver{source: validInvocation().Source} + gateway, err := NewGatewayWithArtifactStore(ClientConfig{ + Enabled: true, + AllowUnisolated: true, + ServerAddress: "127.0.0.1:50051", + }, resolver) + require.NoError(t, err) + + invocation := validInvocation() + invocation.Source = "" + invocation.FunctionRef.FunctionID = 0 + resultMP := mpool.MustNewZeroNoFixed() + defer mpool.DeleteMPool(resultMP) + result := vector.NewFunctionResultWrapper(types.T_int64.ToType(), resultMP) + defer result.Free() + + err = gateway.Execute(context.Background(), invocation, result, resultMP) + require.ErrorContains(t, err, "incomplete UDF FunctionRef") + require.Zero(t, resolver.calls, "a malformed typed plan must not probe an account-scoped artifact") +} + +func TestGatewayResolvesArtifactOnlyAfterHeaderValidation(t *testing.T) { + base := validInvocation() + resolver := &recordingArtifactResolver{source: base.Source} + gateway, err := NewGatewayWithArtifactStore(ClientConfig{ + Enabled: true, + AllowUnisolated: true, + ServerAddress: "127.0.0.1:50051", + }, resolver) + require.NoError(t, err) + + invocation := *base + invocation.Source = "" + resultMP := mpool.MustNewZeroNoFixed() + defer mpool.DeleteMPool(resultMP) + result := vector.NewFunctionResultWrapper(types.T_int64.ToType(), resultMP) + defer result.Free() + + require.NoError(t, gateway.Execute(context.Background(), &invocation, result, resultMP)) + require.Equal(t, 1, resolver.calls) + require.Equal(t, uint64(1), resolver.account) + require.Equal(t, "add", resolver.handler) + require.Equal(t, base.ArtifactDigest, resolver.digest) +} + +func TestValidateInvocationRejectsMismatchedInputBacking(t *testing.T) { + cases := []struct { + name string + make func(*mpool.MPool) *vector.Vector + want string + }{ + { + name: "nil backing", + make: func(*mpool.MPool) *vector.Vector { return nil }, + want: "has no backing vector", + }, + { + name: "wrong type", + make: func(mp *mpool.MPool) *vector.Vector { + input := vector.NewVec(types.T_float64.ToType()) + require.NoError(t, vector.AppendFixed(input, float64(1), false, mp)) + return input + }, + want: "does not match", + }, + { + name: "short flat vector", + make: func(mp *mpool.MPool) *vector.Vector { + input := vector.NewVec(types.T_int64.ToType()) + require.NoError(t, vector.AppendFixed(input, int64(1), false, mp)) + return input + }, + want: "expected at least 2", + }, + } + for _, test := range cases { + t.Run(test.name, func(t *testing.T) { + mp := mpool.MustNewZeroNoFixed() + t.Cleanup(func() { mpool.DeleteMPool(mp) }) + input := test.make(mp) + if input != nil { + t.Cleanup(func() { input.Free(mp) }) + } + invocation := validInvocation() + invocation.Length = 2 + invocation.Inputs = []*vector.Vector{input} + require.ErrorContains(t, validateInvocation(invocation), test.want) + }) + } +} + +func TestValidateInvocationAcceptsConstInputWithFrozenType(t *testing.T) { + mp := mpool.MustNewZeroNoFixed() + t.Cleanup(func() { mpool.DeleteMPool(mp) }) + input, err := vector.NewConstFixed(types.T_int64.ToType(), int64(7), 2, mp) + require.NoError(t, err) + t.Cleanup(func() { input.Free(mp) }) + + invocation := validInvocation() + invocation.Length = 2 + invocation.Inputs = []*vector.Vector{input} + require.NoError(t, validateInvocation(invocation)) +} + +func TestValidateInvocationBudgetRejectsBeforeOpen(t *testing.T) { + require.NoError(t, validateInvocationBudget(executionBudget{ + rows: 2, maxRows: 2, returnType: types.T_int64.ToType(), maxResultBytes: 32, + })) + require.ErrorContains(t, validateInvocationBudget(executionBudget{ + rows: 3, maxRows: 2, returnType: types.T_int64.ToType(), maxResultBytes: 32, + }), "invocation has 3 rows") + // Two int64 values plus the validity bitmap need more than sixteen bytes; + // this is rejected before OpenInvocation rather than after user code runs. + require.ErrorContains(t, validateInvocationBudget(executionBudget{ + rows: 2, maxRows: 2, returnType: types.T_int64.ToType(), maxResultBytes: 16, + }), "result reservation") +} + +func TestGatewayFeatureGateRejectsDisabledRuntime(t *testing.T) { + gateway, err := NewGateway(ClientConfig{}) + require.NoError(t, err) + + ctx, cancel := context.WithTimeout(context.Background(), time.Second) + defer cancel() + require.ErrorContains(t, gateway.CheckLanguageReady(ctx, udf.LanguagePython), "runtime is disabled") + require.ErrorContains(t, gateway.Execute(ctx, validInvocation(), nil, nil), "runtime is disabled") +} + +func TestValidateCapabilitiesRequiresWorkerInstanceLease(t *testing.T) { + response := capabilityResponse{ + ProtocolVersion: protocol.Version, + ABIContract: udf.PythonABIContract, + AdapterVersion: udf.PythonAdapterVersion, + SDKVersion: udf.PythonSDKVersion, + DefinitionSchemaVersion: udf.PythonDefinitionSchemaVersion, + PlanContractVersion: udf.PythonPlanContractVersion, + TypeDescriptorContract: udf.PythonTypeDescriptorContract, + TimezoneDatabaseVersion: currentTimezoneDatabaseVersion(), + Modes: []string{ModeScalar, ModeVector}, + NullPolicies: []string{NullCallHandler, NullReturnNull}, + WindowBatches: 1, + MaxExecutionFrameBytes: 1 << 30, + MaxHandlerProcesses: DefaultWorkerMaxHandlerProcesses, + MaxAccountHandlerProcesses: DefaultWorkerMaxAccountHandlers, + MaxOwnerHandlerProcesses: DefaultWorkerMaxOwnerHandlers, + } + require.ErrorContains(t, validateCapabilities(response), "contract does not match") + response.LeaseEpoch = 17 + require.NoError(t, validateCapabilities(response)) + response.MaxOwnerHandlerProcesses++ + require.ErrorContains(t, validateCapabilities(response), "contract does not match") +} + func TestValidateActionAckCorrelatesTheRequestedFence(t *testing.T) { request := protocol.Control{ Kind: "AcknowledgeResults", @@ -112,6 +373,22 @@ func TestValidateActionAckCorrelatesTheRequestedFence(t *testing.T) { require.ErrorContains(t, validateActionAck(finish.Kind, finish, finishAck), "finish ID") } +func TestAcknowledgeFinishRetriesTheSameIdempotentRequest(t *testing.T) { + request := protocol.Control{ + Kind: "AcknowledgeFinish", + Tuple: validInvocation().Tuple, + FinishID: "finish-1", + } + client := &finishAckFlightClient{} + gateway := &Gateway{cfg: ClientConfig{RequestTimeout: time.Second}} + + require.NoError(t, gateway.acknowledgeFinish(context.Background(), client, request.Kind, request)) + require.Equal(t, 2, client.calls) + require.Len(t, client.actions, 2) + require.Equal(t, client.actions[0].Type, client.actions[1].Type) + require.Equal(t, client.actions[0].Body, client.actions[1].Body) +} + func TestGatewayCloseIsTerminal(t *testing.T) { gateway, err := NewGateway(ClientConfig{}) require.NoError(t, err) @@ -133,6 +410,8 @@ func TestGatewayFinishRejectsLateHalfStreamControls(t *testing.T) { gatewayControl(t, kind, tuple, func(control *protocol.Control) { if kind == "InputConsumed" { control.Sequence = 1 + control.ReleasedBytes = 1 + control.ReleasedBatches = 1 } }), }} @@ -154,6 +433,7 @@ func TestGatewayRejectsArrowBodyOnControlFrame(t *testing.T) { value.Status = statusOK value.FinishID = "finish-1" value.LastSequence = 1 + value.LastResultSequence = 1 }) control.DataBody = []byte("hidden Arrow payload") gateway := &Gateway{cfg: ClientConfig{RequestTimeout: time.Second}} diff --git a/pkg/udf/python/supervisor.go b/pkg/udf/python/supervisor.go index 87df045f68ac8..4927d3dde7109 100644 --- a/pkg/udf/python/supervisor.go +++ b/pkg/udf/python/supervisor.go @@ -24,6 +24,7 @@ type Supervisor struct { mu sync.Mutex cmd *exec.Cmd done chan struct{} + lastErr error closing bool } @@ -69,7 +70,13 @@ func (s *Supervisor) Start() error { return err } done := make(chan struct{}) + // Keep the notification channel after Wait clears cmd. A very short-lived + // worker can exit between cmd.Start and the caller's Done call; dropping the + // channel in that window would make the service role miss an unexpected + // worker exit entirely. The next successful Start replaces it with a fresh + // channel. s.cmd, s.done = cmd, done + s.lastErr = nil go s.wait(cmd, done) logutil.Infof("started Python UDF worker: %s", cmd.String()) return nil @@ -79,7 +86,10 @@ func (s *Supervisor) wait(cmd *exec.Cmd, done chan struct{}) { err := cmd.Wait() s.mu.Lock() if s.cmd == cmd { - s.cmd, s.done = nil, nil + // cmd is cleared so a later Start can launch a new worker, while done is + // retained as the completed notification that Start's caller captured. + s.cmd = nil + s.lastErr = err s.closing = false } s.mu.Unlock() @@ -89,6 +99,26 @@ func (s *Supervisor) wait(cmd *exec.Cmd, done chan struct{}) { } } +// Done returns the exit notification for the currently running worker. The +// channel is captured after Start and is closed exactly once by wait. A caller +// that owns the service role can therefore observe an unexpected worker exit +// independently of the normal shutdown context. +func (s *Supervisor) Done() <-chan struct{} { + s.mu.Lock() + defer s.mu.Unlock() + return s.done +} + +// Err returns the exit error for the most recently completed worker. It must +// be read after the channel returned by Done has closed. A nil error means the +// process exited with status zero; the service layer still treats that as an +// unexpected exit while the role is running. +func (s *Supervisor) Err() error { + s.mu.Lock() + defer s.mu.Unlock() + return s.lastErr +} + func (s *Supervisor) Close() error { s.mu.Lock() cmd, done := s.cmd, s.done @@ -96,12 +126,11 @@ func (s *Supervisor) Close() error { s.closing = true } s.mu.Unlock() - if cmd == nil { - return nil - } var closeErr error - if err := killSupervisorProcess(cmd); err != nil && !errors.Is(err, os.ErrProcessDone) { - closeErr = fmt.Errorf("stop Python UDF worker: %w", err) + if cmd != nil { + if err := killSupervisorProcess(cmd); err != nil && !errors.Is(err, os.ErrProcessDone) { + closeErr = fmt.Errorf("stop Python UDF worker: %w", err) + } } if done != nil { <-done diff --git a/pkg/udf/python/supervisor_test.go b/pkg/udf/python/supervisor_test.go index b5f17f2cfc6cb..9105ffb7adb6e 100644 --- a/pkg/udf/python/supervisor_test.go +++ b/pkg/udf/python/supervisor_test.go @@ -35,11 +35,69 @@ func TestSupervisorCloseStopsTheCurrentWorkerBeforeReturning(t *testing.T) { require.Eventually(t, func() bool { supervisor.mu.Lock() defer supervisor.mu.Unlock() - return supervisor.cmd == nil && supervisor.done == nil + return supervisor.cmd == nil && supervisor.done != nil }, time.Second, time.Millisecond) + select { + case <-supervisor.Done(): + default: + t.Fatal("closed worker did not publish a completed notification") + } // A second start is only safe after the first wait has completed. This // catches a Close implementation that clears cmd and returns before Wait. require.NoError(t, supervisor.Start()) require.NoError(t, supervisor.Close()) } + +func TestSupervisorPublishesUnexpectedWorkerExit(t *testing.T) { + if runtime.GOOS == "windows" { + t.Skip("worker supervisor test uses a POSIX executable script") + } + dir := t.TempDir() + workerPath := filepath.Join(dir, "worker.py") + require.NoError(t, os.WriteFile(workerPath, []byte("#!/bin/sh\nexit 17\n"), 0o755)) + + supervisor, err := NewSupervisor(Config{Address: "127.0.0.1:0", Path: dir, Python: "/bin/sh"}) + require.NoError(t, err) + require.NoError(t, supervisor.Start()) + done := supervisor.Done() + require.NotNil(t, done) + select { + case <-done: + case <-time.After(time.Second): + t.Fatal("worker exit was not published") + } + require.EqualError(t, supervisor.Err(), "exit status 17") + require.NoError(t, supervisor.Close()) +} + +func TestSupervisorKeepsImmediateExitObservable(t *testing.T) { + if runtime.GOOS == "windows" { + t.Skip("worker supervisor test uses a POSIX executable script") + } + dir := t.TempDir() + workerDir := filepath.Join(dir, "worker") + require.NoError(t, os.Mkdir(workerDir, 0o755)) + require.NoError(t, os.WriteFile(filepath.Join(workerDir, "worker.py"), []byte(""), 0o644)) + + script := filepath.Join(dir, "python") + require.NoError(t, os.WriteFile(script, []byte("#!/bin/sh\nexit 17\n"), 0o755)) + s, err := NewSupervisor(Config{ + Path: workerDir, + Python: script, + Address: "127.0.0.1:0", + }) + require.NoError(t, err) + require.NoError(t, s.Start()) + done := s.Done() + require.NotNil(t, done, "Start must publish an observable exit channel even for an immediate failure") + // Close must wait even when Wait has already cleared cmd but has not yet + // published the terminal notification. + require.NoError(t, s.Close()) + select { + case <-done: + case <-time.After(time.Second): + t.Fatal("immediate worker exit was not published") + } + require.Error(t, s.Err(), "a close that races an immediate worker exit must still publish the terminal process result") +} diff --git a/pkg/udf/python/testdata/type_descriptors.json b/pkg/udf/python/testdata/type_descriptors.json new file mode 100644 index 0000000000000..fcc281fc6ea61 --- /dev/null +++ b/pkg/udf/python/testdata/type_descriptors.json @@ -0,0 +1,29 @@ +[ + {"name":"bool","descriptor":{"type_id":10,"offset_width":32},"fingerprint":"7843e5e6f50016ce5cded0cf2c3c9d72134b84e33ef06680dfa1a249bb4c6595"}, + {"name":"int8","descriptor":{"type_id":20,"offset_width":32},"fingerprint":"43302f179a9dc14fc9ad45f5c2c785d7defb402c5e6da00d7f4735b4761b9cea"}, + {"name":"int16","descriptor":{"type_id":21,"offset_width":32},"fingerprint":"f346acef35ead4474900db739a1083b75d76439f4ac1e5ced6d4f2dd1bdcb748"}, + {"name":"int32","descriptor":{"type_id":22,"offset_width":32},"fingerprint":"069fd1e0859fa6d348313e71c9052087b867a486dae7b205906c85fb9b731a00"}, + {"name":"int64","descriptor":{"type_id":23,"offset_width":32},"fingerprint":"482d3c55b8a9d662c2b41326b3ca55cbae65153e662c47c4919ba020d006262b"}, + {"name":"uint8","descriptor":{"type_id":25,"offset_width":32},"fingerprint":"bfb8e804f99c17fda55421b073fecde0b8bb90fc857f21a43abe67de5a6b4e25"}, + {"name":"uint16","descriptor":{"type_id":26,"offset_width":32},"fingerprint":"373eace0b3ab82f3a5380113a75fecb56d4db68775e2e6aa70e0dc9779b918f7"}, + {"name":"uint32","descriptor":{"type_id":27,"offset_width":32},"fingerprint":"be2c02da94fda7b5ab40012b8d8b2f5a76312e1bdf7b93db26b067bd406f3dfc"}, + {"name":"uint64","descriptor":{"type_id":28,"offset_width":32},"fingerprint":"09088571e78bcb6fe65965e09a45886a9f15b58f6000a26de2214842ee9abaf8"}, + {"name":"float32","descriptor":{"type_id":30,"offset_width":32},"fingerprint":"8ade0ed866ef188cbe1dbfeadac487b54edf3e933e811637138db817222a3150"}, + {"name":"float64","descriptor":{"type_id":31,"offset_width":32},"fingerprint":"0121acc964077e1ada1525d4abca8011c297586bd8bb0c20614a8f8e21288b67"}, + {"name":"decimal64","descriptor":{"type_id":32,"width":18,"scale":6,"offset_width":32},"fingerprint":"0ee43811e24e92d89b801a19dbc4e2b4256e0688a08040b0995fde6f18ee6a20"}, + {"name":"decimal128","descriptor":{"type_id":33,"width":38,"scale":10,"offset_width":32},"fingerprint":"3690a03c5e39c40b2d53156c79ee2b9ccb4bea2d7ccb611a5ee8004bada46178"}, + {"name":"date","descriptor":{"type_id":50,"temporal_encoding":"sql_zero_struct","offset_width":32},"fingerprint":"8c401bde992eb173ca8aedca97dbd9f47453b7a860fae199ae0439307e858b4b"}, + {"name":"time","descriptor":{"type_id":51,"scale":6,"offset_width":32},"fingerprint":"8da0f51443fd6822c406d856627054978cef335020a4ba908d0ba41f5438dc93"}, + {"name":"datetime","descriptor":{"type_id":52,"scale":6,"temporal_encoding":"sql_zero_struct","offset_width":32},"fingerprint":"bc5e43e10dbd23c35fe3dea194d8f9a2c294f5ed3eb882e4f9fabd1768a6a618"}, + {"name":"timestamp","descriptor":{"type_id":53,"scale":6,"temporal_encoding":"sql_zero_struct","offset_width":32},"fingerprint":"c1e750fc23cb99b128f673299fa74580a1b8cf896ae64f2a8f7c73c01843f89b"}, + {"name":"char","descriptor":{"type_id":60,"width":16,"charset":3,"offset_width":32},"fingerprint":"1d158d5de44d8579a38f1267d8323ac5f787e83a9ce8a162d97ee744a44e5d60"}, + {"name":"varchar","descriptor":{"type_id":61,"width":64,"charset":3,"offset_width":32},"fingerprint":"eac25e693910d4a54f43e574304d8963c7ea0984c47a2ee1687f0fc4a6fac61f"}, + {"name":"text","descriptor":{"type_id":71,"offset_width":32},"fingerprint":"4d9f162421867c37eb20b22da43637a04e00c18d18a2d73d8018f7b3c2b56169"}, + {"name":"json","descriptor":{"type_id":62,"json_encoding":"canonical_text","offset_width":32},"fingerprint":"b156af5241a070ff80a287da19c46d1593dabee4fb5429f964452e711cd275eb"}, + {"name":"binary","descriptor":{"type_id":64,"width":8,"charset":1,"offset_width":32},"fingerprint":"18b158eee03eb5ea1e481c77fbb31a0201de6c68a27524cd208b0cb4ba09bbf3"}, + {"name":"varbinary","descriptor":{"type_id":65,"width":8,"charset":1,"offset_width":32},"fingerprint":"64406870de1c10859088bb512897cce6e80db525844780113bd9ffb5c3878f1e"}, + {"name":"blob","descriptor":{"type_id":70,"charset":1,"offset_width":32},"fingerprint":"d93ae4faac1d4c092a3fa35e7b8faf596eef890351aef5a7e9020c8fe0d2dd5d"}, + {"name":"uuid","descriptor":{"type_id":63,"offset_width":0},"fingerprint":"370e2939c178acee55aaae27a7bcf92937a203e2788e9e0140d84884be042780"}, + {"name":"vecf32","descriptor":{"type_id":224,"width":3,"offset_width":0},"fingerprint":"9134065bb458a203dae86dc150ec07d814e640ed3a39b04b474f7c9396e64ee9"}, + {"name":"vecf64","descriptor":{"type_id":225,"width":3,"offset_width":0},"fingerprint":"746241d9a35175c5d9fe14a61a9d951e1a97c371dba535c0fc1012f32412ec3c"} +] diff --git a/pkg/udf/python/worker/test_worker.py b/pkg/udf/python/worker/test_worker.py index 91a2001b97284..c0eb7f0421183 100644 --- a/pkg/udf/python/worker/test_worker.py +++ b/pkg/udf/python/worker/test_worker.py @@ -1,6 +1,7 @@ #!/usr/bin/env python3 import datetime +import decimal import importlib.util import json import os @@ -18,6 +19,7 @@ from unittest import mock import pyarrow as pa +import pyarrow.flight as flight WORKER_PATH = pathlib.Path(__file__).with_name("worker.py") @@ -27,7 +29,373 @@ spec.loader.exec_module(worker) +def complete_open_payload(payload): + """Build a current-contract Open payload for direct Flight tests.""" + payload = dict(payload) + payload.update( + { + "definition_schema_version": worker.DEFINITION_SCHEMA_VERSION, + "artifact_digest": worker._inline_artifact_digest( + payload["handler"], payload["source"] + ), + "environment_digest": worker._environment_digest(), + "max_invocation_rows": 1 << 20, + "max_invocation_result_bytes": 256 << 20, + } + ) + payload.setdefault("callsite_id", "python/test") + payload.setdefault("may_error", True) + payload.setdefault("security_mode", "INVOKER") + payload.setdefault("leakproof", False) + payload.setdefault( + "statement_context", + { + "contract_version": 1, + "statement_timestamp_utc": 1704067200123456, + "timezone_kind": "FIXED_OFFSET", + "timezone_offset_minutes": 480, + "sql_mode": ["ANSI", "STRICT_TRANS_TABLES"], + "current_database": "test", + "current_user": "root", + "current_role": "writer", + "connection_collation": "utf8mb4_bin", + }, + ) + payload.setdefault( + "security_frame", + { + "contract_version": 1, + "mode": "INVOKER", + "invoker_user_id": 0, + "invoker_role_id": 0, + "effective_user_id": 0, + "effective_role_id": 0, + }, + ) + try: + payload["definition_fingerprint"] = worker._definition_fingerprint(payload) + except ValueError: + # Some tests intentionally carry an invalid Arrow descriptor and must + # reach that validation branch before fingerprint computation. + payload["definition_fingerprint"] = "a" * 64 + return payload + + class WorkerContractTest(unittest.TestCase): + def test_capability_advertises_worker_instance_lease(self): + encoded = worker._encode_capabilities( + {"protocol_version": worker.PROTOCOL_VERSION}, lease_epoch=17 + ) + value = json.loads(encoded) + self.assertEqual(17, value["lease_epoch"]) + self.assertEqual(worker.MAX_HANDLER_PROCESSES, value["max_handler_processes"]) + self.assertEqual( + worker.MAX_ACCOUNT_HANDLER_PROCESSES, + value["max_account_handler_processes"], + ) + self.assertEqual( + worker.MAX_OWNER_HANDLER_PROCESSES, + value["max_owner_handler_processes"], + ) + with self.assertRaisesRegex(ValueError, "invalid worker lease epoch"): + worker._encode_capabilities( + {"protocol_version": worker.PROTOCOL_VERSION}, lease_epoch=0 + ) + + def test_typed_statement_context_matches_canonical_handler_context(self): + typed = { + "contract_version": 1, + "statement_timestamp_utc": 1704067200123456, + "timezone_kind": "FIXED_OFFSET", + "timezone_offset_minutes": 510, + "sql_mode": ["ANSI", "STRICT_TRANS_TABLES"], + "current_database": "app", + "current_user": "alice", + "current_role": "writer", + "connection_collation": "utf8mb4_bin", + } + values = worker._typed_statement_context(typed) + statement = worker._statement_context(values) + self.assertEqual("alice", statement.current_user) + self.assertEqual(510, statement.session_timezone.offset_minutes) + self.assertEqual(("ANSI", "STRICT_TRANS_TABLES"), statement.sql_mode) + + def test_typed_statement_context_accepts_unix_epoch(self): + typed = { + "contract_version": 1, + "statement_timestamp_utc": 0, + "timezone_kind": "FIXED_OFFSET", + "timezone_offset_minutes": 0, + "sql_mode": [], + "current_user": "alice", + "connection_collation": "utf8mb4_bin", + } + values = worker._typed_statement_context(typed) + statement = worker._statement_context(values) + self.assertEqual( + datetime.datetime(1970, 1, 1, tzinfo=datetime.timezone.utc), + statement.statement_timestamp_utc, + ) + + def test_typed_routine_semantics_reject_partial_or_definer_contract(self): + payload = { + "callsite_id": "python/1", + "may_error": True, + "security_mode": "INVOKER", + "leakproof": False, + "security_frame": { + "contract_version": 1, + "mode": "INVOKER", + "invoker_user_id": 7, + "invoker_role_id": 8, + "effective_user_id": 7, + "effective_role_id": 8, + }, + } + worker._validate_typed_call_contract(payload) + payload["security_mode"] = "DEFINER" + with self.assertRaisesRegex(ValueError, "semantic contract"): + worker._validate_typed_call_contract(payload) + payload.pop("security_mode") + with self.assertRaisesRegex(ValueError, "incomplete typed routine semantic contract"): + worker._validate_typed_call_contract(payload) + + def test_handler_quota_uses_typed_effective_principal(self): + payload = complete_open_payload( + { + "function_ref": { + "account_id": 1, + "database_id": 2, + "function_id": 3, + "revision": 1, + "namespace_version": 1, + }, + "source": "def f(ctx, value): return value", + "handler": "f", + "mode": worker.MODE_SCALAR, + "null_policy": worker.NULL_CALL, + "abi_contract": worker.ABI_CONTRACT, + "adapter_version": worker.ADAPTER_VERSION, + "sdk_version": worker.SDK_VERSION, + "args": [{"type_id": worker.INT64, "offset_width": 32}], + "return": {"type_id": worker.INT64, "offset_width": 32}, + "max_batch_bytes": 1 << 20, + "max_batch_rows": 1, + "handler_timeout_seconds": 1.0, + } + ) + payload["security_frame"].update( + { + "invoker_user_id": 23, + "invoker_role_id": 7, + "effective_user_id": 23, + "effective_role_id": 7, + } + ) + self.assertEqual("user:23/role:7", worker._handler_quota_owner(payload)) + payload["statement_context"]["current_user"] = "same-name-different-principal" + self.assertEqual("user:23/role:7", worker._handler_quota_owner(payload)) + + def test_exchange_rejects_old_demo_open_before_user_code(self): + server = worker.RoutineFlightServer("grpc://127.0.0.1:0") + fence = { + "account_id": 1, + "statement_id": "legacy-demo", + "group_id": "legacy-group", + "group_epoch": 1, + "invocation_id": "legacy-invocation", + "lease_epoch": 1, + } + descriptor = {"type_id": worker.INT64, "offset_width": 32} + payload = complete_open_payload( + { + "function_ref": { + "account_id": 1, + "database_id": 2, + "function_id": 3, + "revision": 1, + "namespace_version": 1, + }, + "source": "def f(ctx, x): return x", + "handler": "f", + "mode": worker.MODE_SCALAR, + "null_policy": worker.NULL_CALL, + "abi_contract": worker.ABI_CONTRACT, + "adapter_version": worker.ADAPTER_VERSION, + "sdk_version": worker.SDK_VERSION, + "args": [descriptor], + "return": descriptor, + "max_batch_bytes": 1 << 20, + "max_batch_rows": 8, + "handler_timeout_seconds": 2, + } + ) + payload.pop("statement_context") + command = worker._encode_control( + {"kind": "OpenInvocation", "tuple": fence, "payload": payload} + ) + reader = types.SimpleNamespace(schema=None, read_chunk=lambda: None) + writer = types.SimpleNamespace( + begin=lambda schema: None, + write_metadata=lambda data: None, + write_with_metadata=lambda record, data: None, + ) + try: + with self.assertRaisesRegex(ValueError, "invocation payload is missing a required field"): + server.do_exchange( + types.SimpleNamespace(is_cancelled=lambda: False), + types.SimpleNamespace(command=command), + reader, + writer, + ) + self.assertFalse(server._active) + self.assertFalse(server._terminal) + finally: + server.shutdown() + + def test_exchange_rejects_missing_typed_statement_context_before_user_code(self): + server = worker.RoutineFlightServer("grpc://127.0.0.1:0") + fence = { + "account_id": 1, + "statement_id": "missing-context", + "group_id": "missing-context-group", + "group_epoch": 1, + "invocation_id": "missing-context-invocation", + "lease_epoch": 1, + } + descriptor = {"type_id": worker.INT64, "offset_width": 32} + payload = complete_open_payload( + { + "function_ref": { + "account_id": 1, + "database_id": 2, + "function_id": 3, + "revision": 1, + "namespace_version": 1, + }, + "source": "def f(ctx, x): return x", + "handler": "f", + "mode": worker.MODE_SCALAR, + "null_policy": worker.NULL_CALL, + "abi_contract": worker.ABI_CONTRACT, + "adapter_version": worker.ADAPTER_VERSION, + "sdk_version": worker.SDK_VERSION, + "args": [descriptor], + "return": descriptor, + "max_batch_bytes": 1 << 20, + "max_batch_rows": 8, + "handler_timeout_seconds": 2, + } + ) + payload["statement_context"] = None + command = worker._encode_control( + {"kind": "OpenInvocation", "tuple": fence, "payload": payload} + ) + reader = types.SimpleNamespace(schema=None, read_chunk=lambda: None) + writer = types.SimpleNamespace( + begin=lambda schema: None, + write_metadata=lambda data: None, + write_with_metadata=lambda record, data: None, + ) + try: + with self.assertRaisesRegex(ValueError, "missing typed statement context"): + server.do_exchange( + types.SimpleNamespace(is_cancelled=lambda: False), + types.SimpleNamespace(command=command), + reader, + writer, + ) + self.assertFalse(server._active) + self.assertFalse(server._terminal) + finally: + server.shutdown() + + def test_worker_rejects_a_tuple_from_another_instance(self): + server = worker.RoutineFlightServer("grpc://127.0.0.1:0", lease_epoch=17) + with self.assertRaisesRegex(ValueError, "STALE_LEASE_EPOCH"): + server._require_current_lease((1, "statement", "group", 1, "invocation", 16)) + with self.assertRaisesRegex(ValueError, "invalid worker lease epoch"): + worker.RoutineFlightServer("grpc://127.0.0.1:0", lease_epoch=0) + + def test_inline_contract_rejects_external_handler_import(self): + with self.assertRaisesRegex(ValueError, "immutable artifact catalog"): + worker._load_handler("def add(ctx, value): return value", "module:add") + + def test_go_python_type_descriptor_fixtures(self): + fixture_path = WORKER_PATH.parent.parent / "testdata" / "type_descriptors.json" + fixtures = json.loads(fixture_path.read_text()) + self.assertGreaterEqual(len(fixtures), 20) + for fixture in fixtures: + descriptor = fixture["descriptor"] + field = worker._field("value", descriptor) + self.assertEqual( + fixture["fingerprint"], + worker._fingerprint(descriptor), + fixture["name"], + ) + self.assertEqual( + fixture["fingerprint"], + field.metadata[worker.TypeFingerprintKey].decode("ascii"), + fixture["name"], + ) + + def test_all_type_fixtures_round_trip_scalar_values_and_nulls(self): + fixture_path = WORKER_PATH.parent.parent / "testdata" / "type_descriptors.json" + fixtures = json.loads(fixture_path.read_text()) + values = { + "bool": False, + "int8": 1, + "int16": 1, + "int32": 1, + "int64": 1, + "uint8": 1, + "uint16": 1, + "uint32": 1, + "uint64": 1, + "float32": 1.25, + "float64": 1.25, + "decimal64": decimal.Decimal("0.123456"), + "decimal128": decimal.Decimal("0.0000000001"), + "date": worker.SqlDate(False, datetime.date(2024, 1, 2)), + "time": datetime.timedelta(microseconds=123456), + "datetime": worker.SqlDatetime( + False, datetime.datetime(2024, 1, 2, 3, 4, 5, 123456) + ), + "timestamp": worker.SqlTimestamp( + False, + datetime.datetime( + 2024, 1, 2, 3, 4, 5, 123456, tzinfo=datetime.timezone.utc + ), + ), + "char": "中", + "varchar": "中", + "text": "matrixone", + "json": '{"a":1,"b":[true,null]}', + "binary": b"\\x01\\x02", + "varbinary": b"\\x01\\x02", + "blob": b"\\x01\\x02", + "uuid": uuid.UUID("123e4567-e89b-12d3-a456-426614174000"), + "vecf32": memoryview(struct.pack(" str: + for root in TZPATH: + try: + with open( + os.path.join(root, "tzdata.zi"), "r", encoding="utf-8" + ) as source: + line = source.readline().strip() + except (OSError, UnicodeError): + continue + prefix = "# version " + if line.startswith(prefix) and line[len(prefix) :].strip(): + return line[len(prefix) :].strip() + return "" + + +TIMEZONE_DATABASE_VERSION = _timezone_database_version() + + +def _contract_digest(domain: str, *parts: str) -> str: + digest = hashlib.sha256() + for part in (domain, *parts): + encoded = part.encode("utf-8") + digest.update(struct.pack(">Q", len(encoded))) + digest.update(encoded) + return digest.hexdigest() + + +def _inline_artifact_digest(handler: str, source: str) -> str: + return _contract_digest("matrixone-python-inline-artifact", handler, source) + + +def _environment_digest() -> str: + if not TIMEZONE_DATABASE_VERSION: + return "" + return _contract_digest( + "matrixone-python-environment", + str(PROTOCOL_VERSION), + ABI_CONTRACT, + ADAPTER_VERSION, + SDK_VERSION, + str(DEFINITION_SCHEMA_VERSION), + str(PLAN_CONTRACT_VERSION), + TYPE_DESCRIPTOR_CONTRACT, + TIMEZONE_DATABASE_VERSION, + ) + + +def _canonical_definition_json(value: Any) -> bytes: + """Match Go encoding/json's canonical UTF-8 output for this contract.""" + encoded = json.dumps( + value, separators=(",", ":"), ensure_ascii=False, allow_nan=False + ) + # encoding/json escapes these characters even when the payload is UTF-8. + # U+2028/U+2029 are also escaped by Go for safe embedding in JavaScript. + return ( + encoded.replace("&", "\\u0026") + .replace("<", "\\u003c") + .replace(">", "\\u003e") + .replace("\u2028", "\\u2028") + .replace("\u2029", "\\u2029") + .encode("utf-8") + ) + + +def _definition_fingerprint(payload: Dict[str, Any]) -> str: + """Hash the immutable executable contract in an Open. + + The original source is a catalog/audit payload. The artifact digest + identifies the exact bytes resolved by the trusted Gateway, so source is + intentionally absent from this canonical definition body. + """ + body = { + "definition_schema_version": payload["definition_schema_version"], + "handler": payload["handler"], + "mode": payload["mode"], + "null_policy": payload["null_policy"], + "abi_contract": payload["abi_contract"], + "adapter_version": payload["adapter_version"], + "artifact_digest": payload["artifact_digest"], + "environment_digest": payload["environment_digest"], + "sdk_version": payload["sdk_version"], + # Go's canonical definition body uses `omitempty` for ArgTypes. An + # empty VECTOR signature must therefore omit the field rather than + # encode it as an empty array, otherwise zero-argument calls would + # have different fingerprints on the two sides of the ABI. + "arg_types": [ + json.loads(_canonical_descriptor(descriptor).decode("utf-8")) + for descriptor in payload["args"] + ], + "return_type": json.loads( + _canonical_descriptor(payload["return"]).decode("utf-8") + ), + } + if not body["arg_types"]: + del body["arg_types"] + return hashlib.sha256(_canonical_definition_json(body)).hexdigest() + +_CAPABILITY_REQUEST_KEYS = frozenset({"protocol_version"}) +_CAPABILITY_RESPONSE_KEYS = frozenset( + { + "protocol_version", + "abi_contract", + "adapter_version", + "sdk_version", + "definition_schema_version", + "plan_contract_version", + "type_descriptor_contract", + "timezone_database_version", + "modes", + "null_policies", + "window_batches", + "cumulative_ack", + "max_execution_frame_bytes", + "max_handler_processes", + "max_account_handler_processes", + "max_owner_handler_processes", + "lease_epoch", + } +) log = logging.getLogger("matrixone.python.udf.worker") @@ -214,6 +399,13 @@ class _TerminalRecord: bytes: int last_result: int finish_id: Optional[str] + outcome: str + + +_TERMINAL_SUCCESS = "SUCCESS" +_TERMINAL_FINISH_UNCONFIRMED = "FINISH_UNCONFIRMED" +_TERMINAL_CANCELLED = "CANCELLED" +_TERMINAL_FAILED = "FAILED" _FENCING_CONTEXT_KEYS = frozenset( @@ -233,6 +425,31 @@ class _TerminalRecord: "connection_collation", } ) +_TYPED_STATEMENT_CONTEXT_KEYS = frozenset( + { + "contract_version", + "statement_timestamp_utc", + "timezone_kind", + "timezone_name", + "timezone_offset_minutes", + "timezone_database_version", + "sql_mode", + "current_database", + "current_user", + "current_role", + "connection_collation", + } +) +_SECURITY_FRAME_KEYS = frozenset( + { + "contract_version", + "mode", + "invoker_user_id", + "invoker_role_id", + "effective_user_id", + "effective_role_id", + } +) def _required_context_value(context: Dict[str, Any], key: str) -> str: @@ -251,6 +468,108 @@ def _optional_context_value(context: Dict[str, Any], key: str) -> Optional[str]: return value or None +def _typed_statement_context(raw: Any) -> Optional[Dict[str, Any]]: + """Validate the typed Go execution snapshot and return canonical map data.""" + if raw is None: + return None + if not isinstance(raw, dict) or set(raw) - _TYPED_STATEMENT_CONTEXT_KEYS: + raise ValueError("PROTOCOL: unsupported typed statement context field") + if raw.get("contract_version") != 1: + raise ValueError("UNSUPPORTED_ROUTINE_VERSION: unsupported statement context contract") + timestamp = raw.get("statement_timestamp_utc") + if isinstance(timestamp, bool) or not isinstance(timestamp, int): + raise ValueError("PROTOCOL: invalid typed statement timestamp") + timezone_kind = raw.get("timezone_kind") + if not isinstance(timezone_kind, str): + raise ValueError("PROTOCOL: typed statement timezone is missing") + timezone_kind = timezone_kind.upper() + timezone_name = raw.get("timezone_name", "") + tzdb_version = raw.get("timezone_database_version", "") + offset = raw.get("timezone_offset_minutes", 0) + if isinstance(offset, bool) or not isinstance(offset, int): + raise ValueError("PROTOCOL: invalid typed timezone offset") + if timezone_kind == "IANA": + if not isinstance(timezone_name, str) or not timezone_name or not isinstance(tzdb_version, str) or not tzdb_version or offset != 0: + raise ValueError("PROTOCOL: incomplete typed IANA timezone") + elif timezone_kind == "FIXED_OFFSET": + if timezone_name != "" or tzdb_version != "" or offset < -839 or offset > 840: + raise ValueError("PROTOCOL: invalid typed fixed timezone") + else: + raise ValueError("PROTOCOL: unsupported typed statement timezone") + sql_mode = raw.get("sql_mode") + if not isinstance(sql_mode, list) or any(not isinstance(item, str) or not item for item in sql_mode): + raise ValueError("PROTOCOL: typed statement sql_mode must be an array of strings") + if sql_mode != sorted(set(sql_mode)): + raise ValueError("PROTOCOL: typed statement sql_mode is not canonical") + current_user = raw.get("current_user") + collation = raw.get("connection_collation") + if not isinstance(current_user, str) or not current_user or not isinstance(collation, str) or not collation: + raise ValueError("PROTOCOL: typed statement principal or collation is missing") + optional_text = ("current_database", "current_role") + for key in optional_text: + if key in raw and not isinstance(raw[key], str): + raise ValueError(f"PROTOCOL: typed statement field {key} must be text") + value = { + "statement_timestamp_utc": str(timestamp), + "session_timezone_kind": timezone_kind, + "sql_mode": json.dumps(sql_mode, separators=(",", ":"), ensure_ascii=True), + "current_user": current_user, + "connection_collation": collation, + } + if timezone_kind == "IANA": + value["session_timezone_name"] = timezone_name + value["session_timezone_tzdb_version"] = tzdb_version + else: + value["session_timezone_offset_minutes"] = str(offset) + for key in optional_text: + if raw.get(key, ""): + value[key] = raw[key] + return value + + +def _validate_typed_call_contract(payload: Dict[str, Any]) -> None: + fields = {"callsite_id", "may_error", "security_mode", "leakproof"} + present = fields & set(payload) + if not present: + return + if present != fields: + raise ValueError("UNSUPPORTED_ROUTINE_VERSION: incomplete typed routine semantic contract") + callsite_id = payload.get("callsite_id") + if not isinstance(callsite_id, str) or not callsite_id or len(callsite_id) > 256 or "\n" in callsite_id or "\r" in callsite_id: + raise ValueError("UNSUPPORTED_ROUTINE_VERSION: invalid typed routine callsite id") + if payload.get("may_error") is not True or payload.get("security_mode") != "INVOKER" or payload.get("leakproof") is not False: + raise ValueError("UNSUPPORTED_ROUTINE_VERSION: unsupported typed routine semantic contract") + frame = payload.get("security_frame") + if not isinstance(frame, dict) or set(frame) != _SECURITY_FRAME_KEYS: + raise ValueError("UNSUPPORTED_ROUTINE_VERSION: incomplete Python security frame") + if frame.get("contract_version") != 1 or frame.get("mode") != "INVOKER": + raise ValueError("UNSUPPORTED_ROUTINE_VERSION: unsupported Python security frame") + integer_fields = ("invoker_user_id", "invoker_role_id", "effective_user_id", "effective_role_id") + if any(type(frame.get(key)) is not int or frame[key] < 0 for key in integer_fields): + raise ValueError("UNSUPPORTED_ROUTINE_VERSION: invalid Python security frame") + if frame["invoker_user_id"] != frame["effective_user_id"] or frame["invoker_role_id"] != frame["effective_role_id"]: + raise ValueError("UNSUPPORTED_ROUTINE_VERSION: Python security frame changed effective principal") + + +def _handler_quota_owner(payload: Dict[str, Any]) -> str: + """Return the stable effective principal key used by the H budget. + + A session display username is mutable text and is not an identity boundary: + two principals can share it across roles or accounts. The typed security + frame has already been validated before this helper is called, so the + numeric effective IDs are the only values used for account/owner quota + accounting. + """ + frame = payload.get("security_frame") if isinstance(payload, dict) else None + if not isinstance(frame, dict): + raise ValueError("UNSUPPORTED_ROUTINE_VERSION: missing Python security frame") + user_id = frame.get("effective_user_id") + role_id = frame.get("effective_role_id") + if type(user_id) is not int or user_id < 0 or type(role_id) is not int or role_id < 0: + raise ValueError("UNSUPPORTED_ROUTINE_VERSION: invalid Python effective principal") + return f"user:{user_id}/role:{role_id}" + + def _statement_context(raw: Any) -> Optional[StatementContext]: if raw is None: return None @@ -277,6 +596,8 @@ def _statement_context(raw: Any) -> Optional[StatementContext]: tzdb_version = _required_context_value(raw, "session_timezone_tzdb_version") if "session_timezone_offset_minutes" in raw: raise ValueError("PROTOCOL: IANA timezone cannot carry a fixed offset") + if not TIMEZONE_DATABASE_VERSION or tzdb_version != TIMEZONE_DATABASE_VERSION: + raise ValueError("PROTOCOL: IANA timezone database version does not match the worker contract") try: ZoneInfo(timezone_name) except (ZoneInfoNotFoundError, ValueError) as exc: @@ -357,6 +678,26 @@ def _tuple_key(value: Dict[str, Any]) -> tuple: ) +def _function_ref(value: Any, tuple_value: Dict[str, Any]) -> tuple: + if not isinstance(value, dict) or set(value) != _FUNCTION_REF_KEYS: + raise ValueError("PROTOCOL: incomplete FunctionRef") + fields = {} + for key in _FUNCTION_REF_KEYS: + item = value.get(key) + minimum = 0 if key == "account_id" else 1 + if ( + isinstance(item, bool) + or not isinstance(item, int) + or item < minimum + or item > (1 << 64) - 1 + ): + raise ValueError(f"PROTOCOL: invalid FunctionRef field {key}") + fields[key] = item + if fields["account_id"] != tuple_value["account_id"]: + raise ValueError("PROTOCOL: FunctionRef account does not match the fencing tuple") + return tuple(fields[key] for key in ("account_id", "database_id", "function_id", "revision", "namespace_version")) + + def _reject_duplicate_json_pairs(pairs): result = {} for key, value in pairs: @@ -402,6 +743,63 @@ def _decode_control(data: bytes) -> Dict[str, Any]: return value +def _decode_capability_request(data: bytes) -> Dict[str, Any]: + if not data or len(data) > MAX_CONTROL_BYTES: + raise ValueError("PROTOCOL: invalid capability request size") + try: + value = json.loads( + bytes(data).decode("utf-8"), + object_pairs_hook=_reject_duplicate_json_pairs, + parse_constant=_reject_nonstandard_json_constant, + ) + except (UnicodeDecodeError, json.JSONDecodeError, RecursionError) as exc: + raise ValueError("PROTOCOL: invalid capability request JSON") from exc + if not isinstance(value, dict) or set(value) != _CAPABILITY_REQUEST_KEYS: + raise ValueError("PROTOCOL: invalid capability request fields") + if type(value.get("protocol_version")) is not int or value["protocol_version"] != PROTOCOL_VERSION: + raise ValueError("PROTOCOL: unsupported capability protocol version") + return value + + +def _encode_capabilities(request: Dict[str, Any], lease_epoch: int = 1) -> bytes: + _decode_capability_request(json.dumps(request, separators=(",", ":")).encode("utf-8")) + if isinstance(lease_epoch, bool) or not isinstance(lease_epoch, int) or lease_epoch <= 0 or lease_epoch > (1 << 64) - 1: + raise ValueError("PROTOCOL: invalid worker lease epoch") + value = { + "protocol_version": PROTOCOL_VERSION, + "abi_contract": ABI_CONTRACT, + "adapter_version": ADAPTER_VERSION, + "sdk_version": SDK_VERSION, + "definition_schema_version": DEFINITION_SCHEMA_VERSION, + "plan_contract_version": PLAN_CONTRACT_VERSION, + "type_descriptor_contract": TYPE_DESCRIPTOR_CONTRACT, + "timezone_database_version": TIMEZONE_DATABASE_VERSION, + "modes": [MODE_SCALAR, MODE_VECTOR], + "null_policies": [NULL_CALL, NULL_RETURN], + # The current implementation intentionally exposes one in-flight + # batch per invocation. This is a capability, not an implied promise + # that a future worker accepts cumulative ACKs. + "window_batches": 1, + "cumulative_ack": False, + "max_execution_frame_bytes": MAX_EXECUTION_FRAME_BYTES, + "max_handler_processes": MAX_HANDLER_PROCESSES, + "max_account_handler_processes": MAX_ACCOUNT_HANDLER_PROCESSES, + "max_owner_handler_processes": MAX_OWNER_HANDLER_PROCESSES, + # This is an instance lease, not a liveness timer. A new worker + # process gets a new value, so a Gateway cannot accidentally reuse an + # invocation tuple that was admitted by a previous worker instance. + "lease_epoch": lease_epoch, + } + if set(value) != _CAPABILITY_RESPONSE_KEYS: + raise ValueError("PROTOCOL: invalid capability response") + encoded = json.dumps( + value, separators=(",", ":"), sort_keys=True, allow_nan=False + ).encode("utf-8") + if len(encoded) > MAX_CONTROL_BYTES: + raise ValueError("RESOURCE_EXHAUSTED: capability response is too large") + return encoded + + def _require_tuple(value: Dict[str, Any], expected: Dict[str, Any]) -> None: if _tuple_key(value) != _tuple_key(expected): raise ValueError("PROTOCOL: fencing tuple changed") @@ -414,6 +812,19 @@ def _required_string(value: Dict[str, Any], key: str) -> str: return item +def _required_digest(value: Dict[str, Any], key: str) -> str: + item = _required_string(value, key) + if len(item) != hashlib.sha256().digest_size * 2: + raise ValueError(f"PROTOCOL: invalid invocation field {key}") + try: + decoded = bytes.fromhex(item) + except ValueError as exc: + raise ValueError(f"PROTOCOL: invalid invocation field {key}") from exc + if item.lower() != item or len(decoded) != hashlib.sha256().digest_size: + raise ValueError(f"PROTOCOL: invalid invocation field {key}") + return item + + def _required_positive_int(value: Dict[str, Any], key: str, maximum: int) -> int: item = value.get(key) if isinstance(item, bool) or not isinstance(item, int) or item <= 0 or item > maximum: @@ -464,8 +875,14 @@ def _validate_control_fields(value: Dict[str, Any], wire: bool) -> None: _required_uint64(value, "sequence") if "last_sequence" in value: _required_uint64(value, "last_sequence", allow_zero=True) + if "last_result_sequence" in value: + _required_uint64(value, "last_result_sequence", allow_zero=True) if "ack_sequence" in value: _required_uint64(value, "ack_sequence") + if "released_bytes" in value: + _required_positive_int(value, "released_bytes", MAX_EXECUTION_FRAME_BYTES) + if "released_batches" in value: + _required_uint64(value, "released_batches") if "finish_id" in value: _required_string(value, "finish_id") if "status" in value: @@ -536,20 +953,35 @@ def _validate_descriptor_domain(descriptor: Dict[str, Any]) -> None: raise ValueError("TYPE_CONTRACT: descriptor width must be non-negative") if scale < 0: raise ValueError("TYPE_CONTRACT: descriptor scale must be non-negative") - if int(descriptor.get("offset_width") or expected_offset_width) != expected_offset_width: + # A 32-bit offset is part of the canonical descriptor for ordinary values + # and is serialized by the Go descriptor. Only fixed-width UUID/vector + # descriptors omit the zero-valued field. Do not turn a missing or + # explicitly zero ordinary offset into an implicit default: that would + # accept a damaged Catalog row and produce a different ABI on the two + # sides. + if expected_offset_width == 32 and "offset_width" not in descriptor: + raise ValueError("TYPE_CONTRACT: descriptor offset_width is required") + if int(descriptor.get("offset_width", expected_offset_width)) != expected_offset_width: raise ValueError( f"TYPE_CONTRACT: descriptor offset_width must be {expected_offset_width}" ) json_encoding = descriptor.get("json_encoding") if json_encoding not in (None, "") and (type_id != JSON or json_encoding != "canonical_text"): raise ValueError("TYPE_CONTRACT: unsupported JSON encoding") + if type_id == JSON and json_encoding != "canonical_text": + raise ValueError("TYPE_CONTRACT: JSON descriptor must use canonical_text encoding") temporal_encoding = descriptor.get("temporal_encoding") if temporal_encoding not in (None, "") and ( type_id not in (DATE, DATETIME, TIMESTAMP) or temporal_encoding != "sql_zero_struct" ): raise ValueError("TYPE_CONTRACT: unsupported temporal encoding") + if type_id in (DATE, DATETIME, TIMESTAMP) and temporal_encoding != "sql_zero_struct": + raise ValueError("TYPE_CONTRACT: temporal descriptor must use sql_zero_struct encoding") - if type_id in (BOOL, INT8, INT16, INT32, INT64, UINT8, UINT16, UINT32, UINT64, JSON): + if type_id in ( + BOOL, INT8, INT16, INT32, INT64, UINT8, UINT16, UINT32, UINT64, + FLOAT32, FLOAT64, JSON, + ): if width or scale or charset: raise ValueError("TYPE_CONTRACT: descriptor carries unused fields") elif type_id in (DECIMAL64, DECIMAL128): @@ -695,15 +1127,13 @@ def _validate_input_batch_schema( def _load_handler(source: str, handler: str): + if ":" in handler: + raise ValueError( + "UNSUPPORTED_ROUTINE_VERSION: Python external handler import requires an immutable artifact catalog" + ) namespace: Dict[str, Any] = {"__name__": "__matrixone_routine__"} exec(compile(source, "", "exec"), namespace, namespace) - if ":" in handler: - module_name, function_name = handler.split(":", 1) - function = namespace.get(function_name) - if function is None and module_name: - function = getattr(importlib.import_module(module_name), function_name, None) - else: - function = namespace.get(handler) + function = namespace.get(handler) if not callable(function) or inspect.iscoroutinefunction(function): raise ValueError("USER_CODE: handler must be a synchronous callable") return function @@ -976,19 +1406,26 @@ def _deserialize_record_batch(data: bytes) -> pa.RecordBatch: reader.close() -def _execute_handler_batch(request: Dict[str, Any]) -> bytes: +def _execute_handler_batch( + request: Dict[str, Any], + *, + handler=None, + statement_context: Optional[StatementContext] = None, +) -> bytes: batch = _deserialize_record_batch(request["input"]) args = request["args"] result_descriptor = request["return"] mode = request["mode"] null_policy = request["null_policy"] sdk_version = request["sdk_version"] - statement_context = _statement_context(request.get("context")) + if statement_context is None: + statement_context = _statement_context(request.get("context")) # User code has no stdout/stderr channel in the Flight protocol. Discard # it so a print() cannot corrupt the length-prefixed response frame. with contextlib.redirect_stdout(_DiscardText()), contextlib.redirect_stderr(_DiscardText()): - handler = _load_handler(request["source"], request["handler"]) + if handler is None: + handler = _load_handler(request["source"], request["handler"]) if mode == MODE_VECTOR: call_context = VectorContext(sdk_version, _CallLogger(), statement_context, batch.num_rows) output = handler(call_context, *[batch.column(index) for index in range(batch.num_columns)]) @@ -1032,27 +1469,122 @@ def _write_execution_frame(stream, payload: bytes) -> None: stream.flush() +def _watch_parent_liveness(read_fd: int) -> None: + """Terminate the whole handler group when the worker disappears. + + The worker cannot run its normal cleanup after SIGKILL/OOM. A pipe whose + write end is owned by the worker gives the handler a kernel-owned death + notification, including the race where the worker exits before the + handler has finished starting. This is a reliability owner for ordinary + handler processes; it is deliberately not described as a security + sandbox, because unisolated user code can inspect or interfere with its + own process. + """ + try: + while True: + if os.read(read_fd, 1) == b"": + if os.name == "posix": + try: + os.killpg(os.getpgrp(), signal.SIGKILL) + except ProcessLookupError: + pass + os._exit(137) + except (OSError, ValueError): + os._exit(137) + + +def _watch_handler_group(read_fd: int, process_group_id: int) -> None: + """Reap a handler process group even after its leader has exited. + + This watchdog is a worker-owned reliability process, not a security + boundary. It has no user-code imports and only has the group id plus a + read end of a worker-owned pipe. If the worker disappears, the pipe gets + EOF and the watchdog kills the whole handler process group. Keeping this + responsibility outside the handler leader closes the case where the + leader exits first while a descendant keeps running. + """ + try: + while True: + if os.read(read_fd, 1) == b"": + if os.name == "posix": + try: + os.killpg(process_group_id, signal.SIGKILL) + except ProcessLookupError: + pass + return + except (OSError, ValueError): + return + finally: + try: + os.close(read_fd) + except OSError: + pass + + def _execute_handler_subprocess() -> None: response_fd_text = os.environ.pop(_HANDLER_RESPONSE_FD_ENV, None) - if response_fd_text is None: + parent_watch_fd_text = os.environ.pop(_HANDLER_PARENT_WATCH_FD_ENV, None) + if response_fd_text is None or parent_watch_fd_text is None: raise ValueError("PROTOCOL: handler response channel is missing") try: response_fd = int(response_fd_text) + parent_watch_fd = int(parent_watch_fd_text) response_stream = os.fdopen(response_fd, "wb", buffering=0, closefd=True) + threading.Thread( + target=_watch_parent_liveness, + args=(parent_watch_fd,), + name="matrixone-udf-parent-watch", + daemon=True, + ).start() except (OSError, TypeError, ValueError) as exc: raise ValueError("PROTOCOL: handler response channel is invalid") from exc try: - size = struct.unpack(">Q", _read_exact(sys.stdin.buffer, 8))[0] - if size > MAX_EXECUTION_FRAME_BYTES: - raise ValueError("RESOURCE_EXHAUSTED: execution request is too large") - request = pickle.loads(_read_exact(sys.stdin.buffer, size)) - response = bytes([_HANDLER_RESPONSE_OK]) + _execute_handler_batch(request) - except Exception as exc: - response = bytes([_HANDLER_RESPONSE_ERROR]) + _safe_error(exc).encode("utf-8") - try: - # This descriptor is created by the adapter and is distinct from the - # process stdout/stderr descriptors available to handler code. - _write_execution_frame(response_stream, response) + # A child is reused only within one bounded invocation burst. The + # source is compiled once and every later request must carry the same + # frozen contract; input Arrow batches remain independently validated + # by the parent and by _execute_handler_batch. + frozen = None + handler = None + statement_context = None + while True: + header = sys.stdin.buffer.read(8) + if not header: + break + if len(header) != 8: + raise EOFError("execution request header ended unexpectedly") + size = struct.unpack(">Q", header)[0] + if size > MAX_EXECUTION_FRAME_BYTES: + raise ValueError("RESOURCE_EXHAUSTED: execution request is too large") + try: + request = pickle.loads(_read_exact(sys.stdin.buffer, size)) + if not isinstance(request, dict): + raise ValueError("PROTOCOL: execution request is not an object") + contract = { + key: request.get(key) + for key in ( + "source", "handler", "mode", "null_policy", "abi_contract", + "adapter_version", "sdk_version", "definition_schema_version", + "artifact_digest", "environment_digest", "definition_fingerprint", + "context", "args", "return", "max_batch_bytes", "max_batch_rows", + "max_invocation_rows", "max_invocation_result_bytes", + ) + } + if frozen is None: + frozen = contract + statement_context = _statement_context(request.get("context")) + handler = _load_handler(request["source"], request["handler"]) + elif contract != frozen: + raise ValueError("PROTOCOL: handler burst contract changed") + response = bytes([_HANDLER_RESPONSE_OK]) + _execute_handler_batch( + request, handler=handler, statement_context=statement_context + ) + except Exception as exc: + response = bytes([_HANDLER_RESPONSE_ERROR]) + _safe_error(exc).encode("utf-8") + # This descriptor is created by the adapter and is distinct from + # process stdout/stderr descriptors available to handler code. + _write_execution_frame(response_stream, response) + if response[0] == _HANDLER_RESPONSE_ERROR: + break finally: response_stream.close() @@ -1066,22 +1598,121 @@ def _context_is_cancelled(context) -> bool: return False +class _ExchangeInputReader: + """Make a blocking Flight input read observable to cancellation. + + ``FlightStreamReader.read_chunk`` is a blocking native call. Polling the + Flight context around that call is insufficient: a cancelled RPC can + remain stuck in the native reader and therefore keep the invocation, its + handler, and its ledger entry alive. Keep at most one chunk in flight and + interrupt the native reader through its cancellation API when the server + context is cancelled. The reader thread is bounded by the admitted + exchange, and is joined before the exchange returns. + """ + + _POLL_SECONDS = 0.05 + + def __init__(self, reader): + self._reader = reader + self._stop = threading.Event() + self._items = queue.Queue(maxsize=1) + self._thread = threading.Thread( + target=self._read_loop, + name="matrixone-udf-flight-reader", + daemon=True, + ) + self._thread.start() + + def _publish(self, kind, value=None): + item = (kind, value) + while not self._stop.is_set(): + try: + self._items.put(item, timeout=self._POLL_SECONDS) + return True + except queue.Full: + continue + return False + + def _read_loop(self): + try: + while not self._stop.is_set(): + try: + chunk = self._reader.read_chunk() + except StopIteration: + self._publish("eof") + return + self._publish("chunk", chunk) + except Exception as exc: + self._publish("error", exc) + + def _cancel_native_reader(self): + cancel = getattr(self._reader, "cancel", None) + if not callable(cancel): + cancel = getattr(self._reader, "close", None) + if callable(cancel): + try: + cancel() + except Exception: + # The RPC is already being torn down. The join below still + # verifies whether the native read actually returned. + pass + + def next(self, context): + while True: + if _context_is_cancelled(context): + self.cancel() + raise TimeoutError("DEADLINE_EXCEEDED: input stream cancelled") + try: + kind, value = self._items.get(timeout=self._POLL_SECONDS) + except queue.Empty: + continue + if kind == "chunk": + return value + if kind == "eof": + raise StopIteration + raise value + + def cancel(self): + self._stop.set() + self._cancel_native_reader() + + def close(self): + self.cancel() + self._thread.join(timeout=1.0) + if self._thread.is_alive(): + raise ValueError( + "RESOURCE_EXHAUSTED: Flight input reader did not stop after cancellation" + ) def _kill_execution_process(process: subprocess.Popen) -> None: # The leader can already have exited while one of its descendants keeps - # the execution alive. Always address the process group first; checking - # poll() before killpg() leaves that descendant outside the cleanup path. + # the execution alive. The session closes the worker-owned liveness pipe + # before calling this helper, so the watchdog owns that leader-exited + # case. Do not call killpg with a reaped PID: macOS may have already + # reused the process-group id and returns EPERM (or could address an + # unrelated group). An un-reaped leader is still killed as a group. if os.name == "posix": - try: - os.killpg(process.pid, signal.SIGKILL) - except ProcessLookupError: - pass + if process.poll() is None: + try: + os.killpg(process.pid, signal.SIGKILL) + except ProcessLookupError: + pass elif process.poll() is None: process.kill() try: process.wait(timeout=1.0) except subprocess.TimeoutExpired: process.kill() - process.wait() + try: + process.wait(timeout=1.0) + except subprocess.TimeoutExpired as exc: + # The caller retains the handler session, H slot, and invocation + # ledger in the bounded pending-cleanup owner. Do not let an + # unbounded reap wait turn a cancellation path into a worker-wide + # hang; the process group has already received SIGKILL and the + # failure is surfaced to the owner. + raise TimeoutError( + "DEADLINE_EXCEEDED: handler process did not exit after SIGKILL" + ) from exc def _execution_group_alive(process: subprocess.Popen) -> bool: @@ -1096,129 +1727,455 @@ def _execution_group_alive(process: subprocess.Popen) -> bool: return True -def _run_handler_process(context, request: Dict[str, Any], timeout_seconds: float) -> bytes: - deadline = time.monotonic() + timeout_seconds - request_wire = pickle.dumps(request, protocol=5) - if len(request_wire) > MAX_EXECUTION_FRAME_BYTES: - raise ValueError("RESOURCE_EXHAUSTED: execution request is too large") - request_frame = struct.pack(">Q", len(request_wire)) + request_wire - response_read_fd, response_write_fd = os.pipe() - process = None - selector = None - popen_kwargs = { - "stdin": subprocess.PIPE, - # Handler output is diagnostic-only. It must never share the - # adapter response channel, even when user code writes directly to - # file descriptor 1. - "stdout": subprocess.DEVNULL, - "stderr": subprocess.DEVNULL, - "close_fds": True, - } - if os.name == "posix": - popen_kwargs["start_new_session"] = True - popen_kwargs["pass_fds"] = (response_write_fd,) - # User code runs under a separate execution identity. In particular, do - # not inherit CN, object-store, database, or tenant credentials from the - # worker process. Routine dependencies are supplied by the selected Python - # environment and do not need ambient environment variables. - child_env = { - name: value - for name, value in os.environ.items() - if name in _HANDLER_ENV_ALLOWLIST - } - child_env[_HANDLER_RESPONSE_FD_ENV] = str(response_write_fd) - # The extra descriptor is a protocol-channel separation mechanism, not a - # sandbox boundary. An unisolated handler with arbitrary OS access can - # still inspect inherited descriptors; production isolation must enforce - # the stronger policy that handler code cannot write this channel. - popen_kwargs["env"] = child_env - response = bytearray() - expected = None - response_eof = False - request_offset = 0 - try: - process = subprocess.Popen( - [sys.executable, os.path.abspath(__file__), "--execute-handler"], - **popen_kwargs, +DEFAULT_BURST_BATCHES = 64 +DEFAULT_BURST_BYTES = 64 << 20 +DEFAULT_BURST_SECONDS = 30.0 +# A pending cleanup retains an admitted invocation, so it is bounded by the +# same ledger entry budget as active invocations. H is only the bound for +# child processes; empty/ALL-NULL invocations can still own a Flight reader +# without owning a handler child. +MAX_PENDING_CLEANUPS = MAX_LEDGER_ENTRIES +PENDING_HANDLER_CLEANUP_INITIAL_DELAY = 0.05 +PENDING_HANDLER_CLEANUP_MAX_DELAY = 1.0 + + +class _HandlerQuotaLease: + """Idempotent ownership token for one live handler child.""" + + def __init__(self, quota, account_id: int, owner_id: str): + self._quota = quota + self._account_id = account_id + self._owner_id = owner_id + self._released = False + self._lock = threading.Lock() + + def release(self) -> None: + with self._lock: + if self._released: + return + self._released = True + self._quota._release(self._account_id, self._owner_id) + + +class _HandlerQuota: + """Non-blocking worker, account, and principal handler budgets. + + The owner key is scoped by account. Two tenants using the same username + therefore do not contend for one another's principal budget. Acquisition + is deliberately all-or-nothing and never waits while a caller owns an + input buffer or a process slot. + """ + + def __init__( + self, + max_account_handlers: int = MAX_ACCOUNT_HANDLER_PROCESSES, + max_owner_handlers: int = MAX_OWNER_HANDLER_PROCESSES, + ): + if ( + type(max_account_handlers) is not int + or max_account_handlers <= 0 + or max_account_handlers > MAX_HANDLER_PROCESSES + ): + raise ValueError("PROTOCOL: invalid account handler budget") + if ( + type(max_owner_handlers) is not int + or max_owner_handlers <= 0 + or max_owner_handlers > max_account_handlers + ): + raise ValueError("PROTOCOL: invalid owner handler budget") + self._max_account_handlers = max_account_handlers + self._max_owner_handlers = max_owner_handlers + self._lock = threading.Lock() + self._account_counts: Dict[int, int] = {} + self._owner_counts: Dict[tuple[int, str], int] = {} + + def acquire(self, account_id: int, owner_id: str) -> _HandlerQuotaLease: + if type(account_id) is not int or account_id < 0: + raise ValueError("PROTOCOL: invalid handler budget account") + if not isinstance(owner_id, str) or not owner_id: + raise ValueError("PROTOCOL: invalid handler budget owner") + owner_key = (account_id, owner_id) + with self._lock: + account_count = self._account_counts.get(account_id, 0) + if account_count >= self._max_account_handlers: + raise ValueError("RESOURCE_EXHAUSTED: account handler budget is full") + owner_count = self._owner_counts.get(owner_key, 0) + if owner_count >= self._max_owner_handlers: + raise ValueError("RESOURCE_EXHAUSTED: owner handler budget is full") + self._account_counts[account_id] = account_count + 1 + self._owner_counts[owner_key] = owner_count + 1 + return _HandlerQuotaLease(self, account_id, owner_id) + + def _release(self, account_id: int, owner_id: str) -> None: + owner_key = (account_id, owner_id) + with self._lock: + account_count = self._account_counts.get(account_id, 0) + owner_count = self._owner_counts.get(owner_key, 0) + if account_count <= 0 or owner_count <= 0: + raise RuntimeError("handler quota release without ownership") + if account_count == 1: + del self._account_counts[account_id] + else: + self._account_counts[account_id] = account_count - 1 + if owner_count == 1: + del self._owner_counts[owner_key] + else: + self._owner_counts[owner_key] = owner_count - 1 + + def counts(self) -> tuple[Dict[int, int], Dict[tuple[int, str], int]]: + """Return copies for deterministic tests and bounded diagnostics.""" + with self._lock: + return dict(self._account_counts), dict(self._owner_counts) + + +class _HandlerProcessSession: + """One handler child reused only inside a bounded invocation burst.""" + + def __init__( + self, + execution_slots: Optional[threading.BoundedSemaphore] = None, + *, + handler_quota: Optional[_HandlerQuota] = None, + account_id: int = 0, + owner_id: str = "__anonymous__", + ): + self._slots = execution_slots or _DEFAULT_HANDLER_SLOTS + self._slot_acquired = False + self._quota_lease = None + self._process = None + self._watchdog_process = None + self._selector = None + self._response_read_fd = -1 + self._parent_watch_write_fd = -1 + self._response_buffer = bytearray() + self._closed = False + self._close_lock = threading.Lock() + self._burst_started = time.monotonic() + self._burst_batches = 0 + self._burst_bytes = 0 + try: + if handler_quota is not None: + self._quota_lease = handler_quota.acquire(account_id, owner_id) + if not self._slots.acquire(blocking=False): + raise ValueError("RESOURCE_EXHAUSTED: handler execution slots are full") + self._slot_acquired = True + response_read_fd, response_write_fd = os.pipe() + parent_watch_read_fd, parent_watch_write_fd = os.pipe() + watchdog_read_fd = os.dup(parent_watch_read_fd) + self._response_read_fd = response_read_fd + self._parent_watch_write_fd = parent_watch_write_fd + popen_kwargs = { + "stdin": subprocess.PIPE, + # Handler output is diagnostic-only. It must never share the + # adapter response channel, even when user code writes directly + # to file descriptor 1. + "stdout": subprocess.DEVNULL, + "stderr": subprocess.DEVNULL, + "close_fds": True, + } + if os.name == "posix": + popen_kwargs["start_new_session"] = True + popen_kwargs["pass_fds"] = (response_write_fd, parent_watch_read_fd) + # User code runs under a separate execution identity. In + # particular, do not inherit CN, object-store, database, or tenant + # credentials from the worker process. This is process hygiene, + # not a sandbox boundary. + child_env = { + name: value + for name, value in os.environ.items() + if name in _HANDLER_ENV_ALLOWLIST + } + child_env[_HANDLER_RESPONSE_FD_ENV] = str(response_write_fd) + child_env[_HANDLER_PARENT_WATCH_FD_ENV] = str(parent_watch_read_fd) + popen_kwargs["env"] = child_env + self._process = subprocess.Popen( + [sys.executable, os.path.abspath(__file__), "--execute-handler"], + **popen_kwargs, + ) + os.close(response_write_fd) + response_write_fd = -1 + os.close(parent_watch_read_fd) + parent_watch_read_fd = -1 + if os.name == "posix": + watchdog_env = { + name: value + for name, value in os.environ.items() + if name in _HANDLER_ENV_ALLOWLIST + } + watchdog_env[_HANDLER_WATCHDOG_FD_ENV] = str(watchdog_read_fd) + self._watchdog_process = subprocess.Popen( + [ + sys.executable, + os.path.abspath(__file__), + "--watch-handler-group", + str(self._process.pid), + ], + stdin=subprocess.DEVNULL, + stdout=subprocess.DEVNULL, + stderr=subprocess.DEVNULL, + close_fds=True, + env=watchdog_env, + start_new_session=True, + pass_fds=(watchdog_read_fd,), + ) + os.close(watchdog_read_fd) + watchdog_read_fd = -1 + self._selector = selectors.DefaultSelector() + stdin_fd = self._process.stdin.fileno() + os.set_blocking(stdin_fd, False) + os.set_blocking(response_read_fd, False) + self._selector.register(response_read_fd, selectors.EVENT_READ, "response") + except Exception: + for fd in ( + locals().get("response_write_fd", -1), + locals().get("parent_watch_read_fd", -1), + locals().get("watchdog_read_fd", -1), + ): + if fd >= 0: + try: + os.close(fd) + except OSError: + pass + # Preserve the construction failure. Cleanup is best effort and + # must not hide the contract error that caused session creation + # to fail. + try: + self.close() + except Exception: + pass + raise + + @property + def should_rollover(self) -> bool: + return ( + self._burst_batches >= DEFAULT_BURST_BATCHES + or self._burst_bytes >= DEFAULT_BURST_BYTES + or time.monotonic() - self._burst_started >= DEFAULT_BURST_SECONDS ) - os.close(response_write_fd) - response_write_fd = -1 - selector = selectors.DefaultSelector() - stdin_fd = process.stdin.fileno() - os.set_blocking(stdin_fd, False) - os.set_blocking(response_read_fd, False) - selector.register(stdin_fd, selectors.EVENT_WRITE, "request") - selector.register(response_read_fd, selectors.EVENT_READ, "response") - while True: - if _context_is_cancelled(context): - raise TimeoutError("DEADLINE_EXCEEDED: handler execution cancelled") - remaining = deadline - time.monotonic() - if remaining <= 0: - raise TimeoutError("DEADLINE_EXCEEDED: handler execution timeout") - events = selector.select(min(remaining, 0.1)) - for event, _ in events: - if event.data == "request": + + def _take_response(self) -> Optional[bytes]: + if len(self._response_buffer) < 8: + return None + expected = struct.unpack(">Q", self._response_buffer[:8])[0] + if expected > MAX_EXECUTION_FRAME_BYTES: + raise ValueError("RESOURCE_EXHAUSTED: execution response is too large") + if len(self._response_buffer) < expected + 8: + return None + payload = bytes(self._response_buffer[8 : expected + 8]) + del self._response_buffer[: expected + 8] + return payload + + def run(self, context, request: Dict[str, Any], timeout_seconds: float) -> bytes: + if self._closed or self._process is None or self._selector is None: + raise ValueError("PROTOCOL: handler session is closed") + # The handler budget is an absolute deadline for the whole request, + # including serialization and the non-blocking write. Starting the + # clock after pickle.dumps would let a large request consume an + # unbounded part of the caller's budget before the deadline was even + # installed. + deadline = time.monotonic() + timeout_seconds + request_wire = pickle.dumps(request, protocol=5) + if time.monotonic() >= deadline: + raise TimeoutError("DEADLINE_EXCEEDED: handler execution timeout") + if len(request_wire) > MAX_EXECUTION_FRAME_BYTES: + raise ValueError("RESOURCE_EXHAUSTED: execution request is too large") + request_frame = struct.pack(">Q", len(request_wire)) + request_wire + request_offset = 0 + stdin_fd = self._process.stdin.fileno() + self._selector.register(stdin_fd, selectors.EVENT_WRITE, "request") + try: + while True: + if _context_is_cancelled(context): + raise TimeoutError("DEADLINE_EXCEEDED: handler execution cancelled") + remaining = deadline - time.monotonic() + if remaining <= 0: + raise TimeoutError("DEADLINE_EXCEEDED: handler execution timeout") + response = self._take_response() + if response is not None: + break + events = self._selector.select(min(remaining, 0.1)) + for event, _ in events: + if event.data == "request": + try: + written = os.write(stdin_fd, request_frame[request_offset:]) + except BlockingIOError: + continue + except BrokenPipeError as exc: + raise ValueError("USER_CODE: handler process closed its request channel") from exc + request_offset += written + if request_offset == len(request_frame): + self._selector.unregister(stdin_fd) + else: + try: + chunk = os.read(self._response_read_fd, 65536) + except BlockingIOError: + continue + if not chunk: + raise ValueError("USER_CODE: handler process exited without a response") + self._response_buffer.extend(chunk) + if self._process.poll() is not None: + if self._process.returncode != 0: + raise ValueError("USER_CODE: handler process exited abnormally") + if _execution_group_alive(self._process): + raise ValueError("USER_CODE: handler process left descendant processes") + raise ValueError("USER_CODE: handler process exited before the burst completed") + if not response: + raise ValueError("PROTOCOL: handler process returned an empty response") + if response[0] == _HANDLER_RESPONSE_ERROR: + try: + message = response[1:].decode("utf-8") + except UnicodeDecodeError as exc: + raise ValueError("PROTOCOL: handler process returned an invalid error") from exc + raise ValueError(message or "USER_CODE: handler process failed") + if response[0] != _HANDLER_RESPONSE_OK: + raise ValueError("PROTOCOL: handler process returned an unknown status") + output = response[1:] + self._burst_batches += 1 + self._burst_bytes += len(request_wire) + len(response) + return output + finally: + # A failed write/read must not leave a stale registration that a + # later close or diagnostic path mistakes for active work. + try: + self._selector.unregister(stdin_fd) + except (KeyError, ValueError): + pass + + def close(self) -> None: + # Mark the session closed before taking the lock so no new batch can + # enter while another terminal path is retrying cleanup. Ownership + # fields are cleared only after their operation succeeds; otherwise a + # transient kill/close error would make a later cleanup call unable to + # find the resource it still owns. + self._closed = True + with self._close_lock: + close_error = None + + def attempt(operation, commit) -> None: + nonlocal close_error + operation_error = None + # Process reaping can race a child exiting and descriptor + # close can transiently fail during RPC teardown. A bounded + # second attempt makes cleanup retryable without turning + # cancellation into an unbounded wait. + for _ in range(2): try: - written = os.write(stdin_fd, request_frame[request_offset:]) - except BlockingIOError: - continue - except BrokenPipeError as exc: - raise ValueError("USER_CODE: handler process closed its request channel") from exc - request_offset += written - if request_offset == len(request_frame): - selector.unregister(stdin_fd) + operation() + commit() + return + except Exception as exc: + operation_error = exc + if close_error is None: + close_error = operation_error + + if self._selector is not None: + selector = self._selector + attempt(selector.close, lambda: setattr(self, "_selector", None)) + if self._parent_watch_write_fd >= 0: + parent_watch_write_fd = self._parent_watch_write_fd + + def close_parent_watch() -> None: + os.close(parent_watch_write_fd) + + attempt( + close_parent_watch, + lambda: setattr(self, "_parent_watch_write_fd", -1), + ) + if self._process is not None: + process = self._process + + def close_process() -> None: + _kill_execution_process(process) + if process.stdin is not None: process.stdin.close() - else: + + attempt(close_process, lambda: setattr(self, "_process", None)) + if self._watchdog_process is not None: + watchdog_process = self._watchdog_process + + def wait_for_watchdog() -> None: try: - chunk = os.read(response_read_fd, 65536) - except BlockingIOError: - continue - if not chunk: - response_eof = True - selector.unregister(response_read_fd) - if expected is None or len(response) < expected + 8: - raise ValueError("USER_CODE: handler process exited without a response") - else: - response.extend(chunk) - if expected is None and len(response) >= 8: - expected = struct.unpack(">Q", response[:8])[0] - if expected > MAX_EXECUTION_FRAME_BYTES: - raise ValueError("RESOURCE_EXHAUSTED: execution response is too large") - if expected is not None and len(response) > expected + 8: - raise ValueError("PROTOCOL: handler process returned trailing response data") - - # A complete frame is not enough. The adapter wrapper must have - # returned normally, and all of its process-group descendants - # must be gone before the adapter accepts the result. - if process.poll() is not None: - if process.returncode != 0: - raise ValueError("USER_CODE: handler process exited abnormally") - if _execution_group_alive(process): - raise ValueError("USER_CODE: handler process left descendant processes") - if response_eof and expected is not None and len(response) == expected + 8: - break - payload = bytes(response[8 : expected + 8]) - if not payload: - raise ValueError("PROTOCOL: handler process returned an empty response") - if payload[0] == _HANDLER_RESPONSE_ERROR: - try: - message = payload[1:].decode("utf-8") - except UnicodeDecodeError as exc: - raise ValueError("PROTOCOL: handler process returned an invalid error") from exc - raise ValueError(message or "USER_CODE: handler process failed") - if payload[0] != _HANDLER_RESPONSE_OK: - raise ValueError("PROTOCOL: handler process returned an unknown status") - return payload[1:] - finally: - if selector is not None: - selector.close() - if process is not None: - _kill_execution_process(process) - if process.stdin is not None: - process.stdin.close() - if response_write_fd >= 0: - os.close(response_write_fd) - os.close(response_read_fd) + watchdog_process.wait(timeout=0.1) + except subprocess.TimeoutExpired: + try: + watchdog_process.kill() + except ProcessLookupError: + pass + watchdog_process.wait(timeout=1.0) + + attempt( + wait_for_watchdog, + lambda: setattr(self, "_watchdog_process", None), + ) + if self._response_read_fd >= 0: + response_read_fd = self._response_read_fd + + def close_response_read() -> None: + os.close(response_read_fd) + + attempt( + close_response_read, + lambda: setattr(self, "_response_read_fd", -1), + ) + # H and the account/owner quota describe a live handler child. A + # failed kill must therefore keep both leases held while this + # session is owned by the pending-cleanup reaper. Releasing them + # merely because the close attempt returned an error would let a + # later invocation exceed the real process budget. The fields are + # cleared only after every process/IPC owner has been cleared. + resources_closed = ( + self._selector is None + and self._process is None + and self._watchdog_process is None + and self._response_read_fd < 0 + and self._parent_watch_write_fd < 0 + ) + if resources_closed and self._slot_acquired: + attempt(self._slots.release, lambda: setattr(self, "_slot_acquired", False)) + quota_lease = getattr(self, "_quota_lease", None) + if resources_closed and quota_lease is not None: + attempt(quota_lease.release, lambda: setattr(self, "_quota_lease", None)) + if close_error is not None: + raise close_error + + +# Keep stable class identities for cleanup ownership checks. Contract tests +# replace the module constructors to inject failures; using the mutable module +# attributes in isinstance() would then turn a valid cleanup path into a +# TypeError and could mask the original exchange outcome. +_EXCHANGE_INPUT_READER_TYPE = _ExchangeInputReader +_HANDLER_PROCESS_SESSION_TYPE = _HandlerProcessSession + + +@dataclass +class _PendingInvocationCleanup: + input_reader: Optional[_ExchangeInputReader] + handler_session: Optional[_HandlerProcessSession] + key: tuple + state: _InvocationState + next_attempt: float + failures: int = 0 + + +def _run_handler_process( + context, request: Dict[str, Any], timeout_seconds: float, + execution_slots: Optional[threading.BoundedSemaphore] = None, +) -> bytes: + session = _HandlerProcessSession(execution_slots) + try: + result = session.run(context, request, timeout_seconds) + except BaseException: + # Preserve the handler/transport failure as the primary diagnosis. + # Cleanup is still mandatory, but a close failure must not replace a + # deadline, user-code, or protocol error and hide the actual cause. + try: + session.close() + except Exception: + logging.exception("Python UDF handler cleanup failed after an execution error") + raise + else: + session.close() + return result @@ -1232,9 +2189,108 @@ def __init__(self, tuple_value: Dict[str, Any], terminal_bytes: int): self.acked_result = 0 self.finish_id: Optional[str] = None self.finish_acked = False + self.finish_sent = False + self.cancelled = False + self.terminal_outcome: Optional[str] = None + + def input_sequence(self) -> int: + with self.condition: + return self.last_input + + def record_input(self, sequence: int) -> None: + with self.condition: + if self.terminal_outcome is not None or self.cancelled: + raise ValueError("PROTOCOL: invocation is already terminal") + if sequence != self.last_input + 1: + raise ValueError("PROTOCOL: input sequence is not contiguous") + self.last_input = sequence + self.condition.notify_all() + + def sequences(self) -> tuple: + with self.condition: + return self.last_input, self.last_result, self.acked_result + + def record_result(self, sequence: int) -> None: + with self.condition: + if self.terminal_outcome is not None or self.cancelled: + raise ValueError("PROTOCOL: invocation is already terminal") + if sequence != self.last_result + 1 or sequence > self.last_input: + raise ValueError("PROTOCOL: result sequence is not contiguous") + self.last_result = sequence + self.condition.notify_all() + + def terminal_snapshot(self) -> tuple: + with self.condition: + outcome = self.terminal_outcome + if outcome is None: + if self.finish_acked: + outcome = _TERMINAL_SUCCESS + elif self.finish_sent: + outcome = _TERMINAL_FINISH_UNCONFIRMED + elif self.cancelled: + outcome = _TERMINAL_CANCELLED + else: + outcome = _TERMINAL_FAILED + return self.last_result, self.finish_id, outcome + + def mark_finish_sent(self, finish_id: str) -> str: + with self.condition: + if self.terminal_outcome is not None or self.cancelled: + raise ValueError("PROTOCOL: invocation is already terminal") + if self.finish_sent or not finish_id: + raise ValueError("PROTOCOL: Finish was already sent") + self.finish_id = finish_id + self.finish_sent = True + return finish_id + + def freeze_terminal(self) -> str: + """Linearize the outcome before the active entry is removed. + + A Finish identifier only proves that the worker emitted Finish. It + does not prove that the peer accepted AcknowledgeFinish. The latter + is the condition for SUCCESS; otherwise a sent Finish is frozen as + FINISH_UNCONFIRMED and cannot be promoted by a late action. + """ + with self.condition: + if self.terminal_outcome is None: + if self.finish_acked: + self.terminal_outcome = _TERMINAL_SUCCESS + elif self.finish_sent: + self.terminal_outcome = _TERMINAL_FINISH_UNCONFIRMED + elif self.cancelled: + self.terminal_outcome = _TERMINAL_CANCELLED + else: + self.terminal_outcome = _TERMINAL_FAILED + return self.terminal_outcome + + def mark_cancelled(self) -> None: + """Freeze cancellation intent before the active lease is removed. + + Cancellation is recorded on the invocation state rather than inferred + from cleanup timing. This lets a concurrent ACK observe the same + terminal gate: an already accepted Finish ACK still wins, while a + result or Finish ACK arriving after cancellation cannot advance the + invocation. + """ + with self.condition: + if self.terminal_outcome is None: + # An accepted Finish ACK is the terminal proof. Cancellation + # may race the transport teardown after that ACK, but it must + # not overwrite SUCCESS or make a later cleanup publish a + # contradictory CANCELLED tombstone. + if self.finish_acked: + return + self.cancelled = True + self.condition.notify_all() def ack_result(self, sequence: int) -> None: with self.condition: + if self.cancelled: + raise ValueError("PROTOCOL: invocation terminal outcome is CANCELLED") + if self.terminal_outcome is not None and self.terminal_outcome != _TERMINAL_SUCCESS: + raise ValueError( + f"PROTOCOL: invocation terminal outcome is {self.terminal_outcome}" + ) if sequence <= 0 or sequence < self.acked_result or sequence > self.last_result: raise ValueError("PROTOCOL: result ACK is outside the received range") self.acked_result = sequence @@ -1252,6 +2308,20 @@ def wait_result_ack(self, sequence: int, context=None) -> None: def ack_finish(self, finish_id: str) -> None: with self.condition: + if self.terminal_outcome is not None: + if self.terminal_outcome == _TERMINAL_SUCCESS and finish_id == self.finish_id: + return + raise ValueError( + f"PROTOCOL: invocation terminal outcome is {self.terminal_outcome}" + ) + # Cancellation may race the response after the peer has already + # accepted this exact Finish ACK. The accepted acknowledgement + # is the terminal proof and remains idempotent; a cancellation + # that won the race before the ACK is still rejected below. + if self.finish_acked and finish_id == self.finish_id: + return + if self.cancelled: + raise ValueError("PROTOCOL: invocation terminal outcome is CANCELLED") if not self.finish_id or finish_id != self.finish_id: raise ValueError("PROTOCOL: invalid finish id") self.finish_acked = True self.condition.notify_all() @@ -1268,13 +2338,44 @@ def wait_finish_ack(self, context=None) -> None: class RoutineFlightServer(flight.FlightServerBase): - def __init__(self, location: str): + def __init__( + self, + location: str, + *, + clock=time.monotonic, + terminal_ttl_seconds=TERMINAL_TTL_SECONDS, + lease_epoch=1, + ): + if isinstance(lease_epoch, bool) or not isinstance(lease_epoch, int) or lease_epoch <= 0 or lease_epoch > (1 << 64) - 1: + raise ValueError("PROTOCOL: invalid worker lease epoch") super().__init__(location) self._lock = threading.RLock() self._active: Dict[tuple, _InvocationState] = {} self._terminal: OrderedDict[tuple, _TerminalRecord] = OrderedDict() self._terminal_bytes = 0 self._active_bytes = 0 + self._active_groups: Dict[str, int] = {} + self._closed_groups: Dict[str, int] = {} + self._closed_group_bytes = 0 + self._reserved_groups = set() + self._reserved_group_bytes = 0 + self._clock = clock + self._terminal_ttl_seconds = terminal_ttl_seconds + self._lease_epoch = lease_epoch + # H is independent from the number of admitted Flight invocations K. + # A rejected acquire fails before the handler process is created and + # therefore cannot accumulate a hidden Python task queue. + self._handler_slots = threading.BoundedSemaphore(MAX_HANDLER_PROCESSES) + self._handler_quota = _HandlerQuota() + # A Flight reader or handler session remains owned by the Flight + # server until every resource close has succeeded. This queue is + # bounded by the active ledger budget, so a transient cleanup failure + # cannot create an unbounded reaper queue or silently release an + # invocation while a native reader or child is still live. + self._pending_cleanup_condition = threading.Condition() + self._pending_cleanups: Dict[tuple, _PendingInvocationCleanup] = {} + self._pending_cleanup_thread: Optional[threading.Thread] = None + self._pending_cleanup_stopping = False @staticmethod def _entry_bytes(key: tuple) -> int: @@ -1285,68 +2386,367 @@ def _entry_bytes(key: tuple) -> int: return len(encoded) + 128 def _purge_terminal_locked(self, now: float) -> None: - expired = [key for key, record in self._terminal.items() if record.expires_at <= now] + expired = [ + key + for key, record in self._terminal.items() + if record.expires_at <= now + and self._closed_groups.get(key[2], 0) >= key[3] + ] for key in expired: record = self._terminal.pop(key) self._terminal_bytes -= record.bytes def _admit(self, key: tuple) -> _InvocationState: size = self._entry_bytes(key) + group_id, group_epoch = key[2], key[3] with self._lock: - self._purge_terminal_locked(time.monotonic()) + if self._closed_groups.get(group_id, 0) >= group_epoch: + raise ValueError("PROTOCOL: execution group epoch is already closed") + self._purge_terminal_locked(self._clock()) if key in self._active: raise ValueError("PROTOCOL: invocation fence is active") if key in self._terminal: raise ValueError("PROTOCOL: invocation fence is terminal") + if group_id in self._active_groups: + raise ValueError("PROTOCOL: execution group epoch is already active") if len(self._active) + len(self._terminal) >= MAX_LEDGER_ENTRIES: raise ValueError("RESOURCE_EXHAUSTED: terminal ledger entries are full") if self._active_bytes + self._terminal_bytes + size > MAX_LEDGER_BYTES: raise ValueError("RESOURCE_EXHAUSTED: terminal ledger bytes are full") + if group_id not in self._closed_groups and group_id not in self._reserved_groups: + fence_bytes = len(group_id.encode("utf-8")) + 8 + if ( + len(self._closed_groups) + len(self._reserved_groups) + >= MAX_CLOSED_GROUP_ENTRIES + or self._closed_group_bytes + self._reserved_group_bytes + fence_bytes + > MAX_CLOSED_GROUP_BYTES + ): + raise ValueError("RESOURCE_EXHAUSTED: closed-group fence is full") + # Reserve the future tombstone before creating the handler. + # Terminal cleanup converts this reservation into the + # closed-generation fence. + self._reserved_groups.add(group_id) + self._reserved_group_bytes += fence_bytes state = _InvocationState({}, size) self._active[key] = state self._active_bytes += size + self._active_groups[group_id] = group_epoch return state + def _require_current_lease(self, key: tuple) -> None: + if key[5] != self._lease_epoch: + raise ValueError( + "STALE_LEASE_EPOCH: invocation belongs to a different worker instance" + ) + def _remember_terminal_locked(self, key: tuple, state: _InvocationState) -> None: - deadline = time.monotonic() + TERMINAL_TTL_SECONDS + deadline = self._clock() + self._terminal_ttl_seconds + last_result, finish_id, outcome = state.terminal_snapshot() self._terminal[key] = _TerminalRecord( - deadline, state.terminal_bytes, state.last_result, state.finish_id + deadline, state.terminal_bytes, last_result, finish_id, outcome ) self._terminal.move_to_end(key) self._terminal_bytes += state.terminal_bytes + def _close_group_epoch_locked(self, key: tuple) -> None: + group_id, group_epoch = key[2], key[3] + current = self._closed_groups.get(group_id, 0) + if group_epoch <= current: + return + if group_id not in self._closed_groups: + fence_bytes = len(group_id.encode("utf-8")) + 8 + if group_id in self._reserved_groups: + self._reserved_groups.remove(group_id) + self._reserved_group_bytes -= fence_bytes + elif ( + len(self._closed_groups) >= MAX_CLOSED_GROUP_ENTRIES + or self._closed_group_bytes + fence_bytes > MAX_CLOSED_GROUP_BYTES + ): + # This is only reachable for state restored from an adapter + # predating group-fence reservations. Keep the terminal record + # and refuse collection until the fence is reconstructed. + return + self._closed_group_bytes += fence_bytes + self._closed_groups[group_id] = group_epoch + def _finish_invocation(self, key: tuple, state: Optional[_InvocationState]) -> None: # A rejected duplicate Open has no ownership of the existing state. # Cleanup must therefore be identity based, not key based. if key is None or state is None: return + state.freeze_terminal() with self._lock: current = self._active.get(key) if current is not state: return self._active.pop(key, None) self._active_bytes -= state.terminal_bytes + group_id, group_epoch = key[2], key[3] + if self._active_groups.get(group_id) == group_epoch: + self._active_groups.pop(group_id, None) # A started invocation is terminal even when the worker reports an # error. Retaining the fence prevents a late retry from running # user code a second time. self._remember_terminal_locked(key, state) + # The current Gateway contract uses one member per group. The + # worker records that owner closure before allowing TTL collection; + # a future multi-member scheduler must send an explicit group + # closure before reusing this hook. + self._close_group_epoch_locked(key) + + def _ensure_pending_cleanup_thread_locked(self) -> None: + thread = self._pending_cleanup_thread + if thread is not None and thread.is_alive(): + return + thread = threading.Thread( + target=self._pending_cleanup_loop, + name="matrixone-python-udf-cleanup", + daemon=True, + ) + self._pending_cleanup_thread = thread + thread.start() + + def _retain_pending_cleanup( + self, + input_reader: Optional[_ExchangeInputReader], + handler_session: Optional[_HandlerProcessSession], + key: Optional[tuple], + state: Optional[_InvocationState], + ) -> bool: + """Retain exchange resources until every cleanup operation succeeds.""" + if key is None or state is None or (input_reader is None and handler_session is None): + return False + with self._pending_cleanup_condition: + existing = self._pending_cleanups.get(key) + if existing is not None: + return True + if len(self._pending_cleanups) >= MAX_PENDING_CLEANUPS: + # An admitted invocation must not lose its cleanup owner. A + # full queue is an adapter invariant violation; keeping the + # invocation active is safer than pretending ownership was + # released. + logging.critical( + "Python UDF pending cleanup capacity is full for %r", + key, + ) + return False + self._pending_cleanups[key] = _PendingInvocationCleanup( + input_reader=input_reader, + handler_session=handler_session, + key=key, + state=state, + next_attempt=time.monotonic(), + ) + self._ensure_pending_cleanup_thread_locked() + self._pending_cleanup_condition.notify_all() + return True + + def _pending_cleanup_loop(self) -> None: + while True: + with self._pending_cleanup_condition: + while True: + if self._pending_cleanup_stopping and not self._pending_cleanups: + return + if not self._pending_cleanups: + self._pending_cleanup_condition.wait() + continue + now = time.monotonic() + ready = [ + item for item in self._pending_cleanups.values() + if item.next_attempt <= now + ] + if ready: + break + wait_for = min( + item.next_attempt + for item in self._pending_cleanups.values() + ) - now + self._pending_cleanup_condition.wait(max(0.01, wait_for)) + + for item in ready: + errors = [] + if item.input_reader is not None: + try: + item.input_reader.close() + except Exception as exc: + errors.append(exc) + else: + with self._pending_cleanup_condition: + current = self._pending_cleanups.get(item.key) + if current is item: + item.input_reader = None + if item.handler_session is not None: + try: + item.handler_session.close() + except Exception as exc: + errors.append(exc) + else: + with self._pending_cleanup_condition: + current = self._pending_cleanups.get(item.key) + if current is item: + item.handler_session = None + if errors: + exc = errors[0] + item.failures += 1 + delay = min( + PENDING_HANDLER_CLEANUP_MAX_DELAY, + PENDING_HANDLER_CLEANUP_INITIAL_DELAY + * (2 ** min(item.failures, 5)), + ) + with self._pending_cleanup_condition: + current = self._pending_cleanups.get(item.key) + if current is item: + item.next_attempt = time.monotonic() + delay + self._pending_cleanup_condition.notify_all() + logging.error( + "Python UDF exchange cleanup retry failed for %r: %s", + item.key, + exc, + ) + continue + + # The session is now fully closed. Only then can the + # invocation owner release K, the ledger entry, and the group + # fence. _finish_invocation is idempotent with respect to any + # late ACK that arrived while the cleanup was pending. + finish_error = None + try: + self._finish_invocation(item.key, item.state) + except Exception as exc: + finish_error = exc + with self._pending_cleanup_condition: + current = self._pending_cleanups.get(item.key) + if current is item and finish_error is None: + del self._pending_cleanups[item.key] + elif current is item: + item.failures += 1 + item.next_attempt = time.monotonic() + PENDING_HANDLER_CLEANUP_MAX_DELAY + self._pending_cleanup_condition.notify_all() + if finish_error is not None: + logging.error( + "Python UDF invocation cleanup retry failed for %r: %s", + item.key, + finish_error, + ) + + def shutdown(self): + result = super().shutdown() + with self._pending_cleanup_condition: + self._pending_cleanup_stopping = True + self._pending_cleanup_condition.notify_all() + thread = self._pending_cleanup_thread + if thread is not None: + # Normal shutdown has no pending sessions and joins immediately. + # A persistent OS/resource failure remains owned by the daemon + # reaper; the handler's parent-liveness pipe still guarantees + # child-group cleanup if this worker process exits. + thread.join(timeout=2.0) + with self._pending_cleanup_condition: + pending = len(self._pending_cleanups) + if pending: + logging.error( + "Python UDF worker shutdown left %d handler cleanups pending", + pending, + ) + return result + + def _cleanup_exchange( + self, + key: Optional[tuple], + state: Optional[_InvocationState], + input_reader, + handler_session, + ) -> Optional[Exception]: + """Run every exchange cleanup step and return its first error. + + Closing a native Flight reader or a handler process is allowed to + fail while an RPC is already being cancelled. Those failures must + not skip the invocation fence cleanup: otherwise K, handler slots, + group reservations, and terminal de-duplication can disagree about + whether the invocation still owns resources. The caller decides + whether a cleanup error should replace an existing exchange error. + """ + cleanup_error: Optional[Exception] = None + + def close_with_retry(resource) -> tuple[bool, Optional[Exception]]: + last_error = None + for _ in range(2): + try: + resource.close() + return True, None + except Exception as exc: + last_error = exc + return False, last_error + + input_cleanup_succeeded = input_reader is None + if input_reader is not None: + input_cleanup_succeeded, cleanup_error = close_with_retry(input_reader) + + handler_cleanup_succeeded = handler_session is None + handler_error = None + if handler_session is not None: + handler_cleanup_succeeded, handler_error = close_with_retry(handler_session) + if cleanup_error is None: + cleanup_error = handler_error + + # Real native readers and process sessions retain ownership when close + # fails. Test doubles deliberately keep the old direct-cleanup + # behaviour so the helper tests do not create a false production + # resource owner. + pending_reader = ( + input_reader + if isinstance(input_reader, _EXCHANGE_INPUT_READER_TYPE) and not input_cleanup_succeeded + else None + ) + pending_session = ( + handler_session + if isinstance(handler_session, _HANDLER_PROCESS_SESSION_TYPE) and not handler_cleanup_succeeded + else None + ) + if pending_reader is None and pending_session is None: + try: + self._finish_invocation(key, state) + except Exception as exc: + if cleanup_error is None: + cleanup_error = exc + elif not self._retain_pending_cleanup(pending_reader, pending_session, key, state): + # Keep the invocation active if no cleanup owner could be + # registered. Returning the error makes the failure visible and + # prevents a false terminal record from authorizing a retry. + if cleanup_error is None: + cleanup_error = RuntimeError( + "Python UDF exchange cleanup owner could not be retained" + ) + return cleanup_error def do_action(self, context, action): + if action.type == "GetPythonCapabilities": + request = _decode_capability_request(action.body) + # pyarrow exposes Result as a one positional-buffer value across + # supported releases; using a keyword here breaks the real Flight + # path before any routine is opened. + yield flight.Result(_encode_capabilities(request, self._lease_epoch)) + return control = _decode_control(action.body) key = _tuple_key(control["tuple"]) + self._require_current_lease(key) if action.type not in ("AcknowledgeResults", "AcknowledgeFinish"): raise ValueError("PROTOCOL: unknown action") if control["kind"] != action.type: raise ValueError("PROTOCOL: action kind does not match action type") with self._lock: - self._purge_terminal_locked(time.monotonic()) + self._purge_terminal_locked(self._clock()) state = self._active.get(key) terminal = self._terminal.get(key) if state is None: if terminal is None: raise ValueError("PROTOCOL: unknown invocation") + if terminal.outcome != _TERMINAL_SUCCESS: + raise ValueError( + f"PROTOCOL: invocation terminal outcome is {terminal.outcome}" + ) if not terminal.finish_id: - raise ValueError("PROTOCOL: invocation did not complete successfully") + raise ValueError("PROTOCOL: successful invocation has no Finish") if action.type == "AcknowledgeResults": sequence = _required_uint64(control, "ack_sequence") if sequence != terminal.last_result: @@ -1375,6 +2775,8 @@ def _ack_control(control: Dict[str, Any]) -> Dict[str, Any]: def do_exchange(self, context, descriptor, reader, writer): state = None key = None + handler_session = None + input_reader = None writer_started = False try: command = getattr(descriptor, "command", None) @@ -1385,21 +2787,56 @@ def do_exchange(self, context, descriptor, reader, writer): if not isinstance(open_control.get("payload"), dict): raise ValueError("PROTOCOL: invocation payload must be an object") key = _tuple_key(open_control["tuple"]) + self._require_current_lease(key) payload = open_control["payload"] + unknown_payload = set(payload) - _OPEN_PAYLOAD_KEYS + missing_payload = _OPEN_PAYLOAD_REQUIRED_KEYS - set(payload) + if unknown_payload: + raise ValueError("PROTOCOL: unsupported invocation payload field") + if missing_payload: + raise ValueError("PROTOCOL: invocation payload is missing a required field") + _validate_typed_call_contract(payload) + _function_ref(payload.get("function_ref"), open_control["tuple"]) args = payload.get("args") result_descriptor = payload.get("return") if not isinstance(args, list) or any(not isinstance(item, dict) for item in args): raise ValueError("PROTOCOL: invocation args must be an array of descriptors") if not isinstance(result_descriptor, dict): raise ValueError("PROTOCOL: invocation return must be a descriptor") - statement_context = _statement_context(payload.get("context")) + context_values = payload.get("context") + statement_context = _statement_context(context_values) + typed_context_values = _typed_statement_context(payload.get("statement_context")) + if typed_context_values is None: + raise ValueError("UNSUPPORTED_ROUTINE_VERSION: missing typed statement context") + typed_statement_context = _statement_context(typed_context_values) + if statement_context is not None and statement_context != typed_statement_context: + raise ValueError("PROTOCOL: typed statement context does not match context") + statement_context = typed_statement_context + effective_context = dict(context_values or {}) + # The typed form is the canonical wire representation. The + # trusted CN map predates it and may use equivalent textual + # encodings such as "+480" for a fixed offset. The parsed + # StatementContext comparison above rejects semantic drift; + # overwrite the handler-facing map with canonical values so + # equivalent encodings do not become a false protocol error. + effective_context.update(typed_context_values) mode = _required_string(payload, "mode") null_policy = _required_string(payload, "null_policy") abi_contract = _required_string(payload, "abi_contract") adapter_version = _required_string(payload, "adapter_version") sdk_version = _required_string(payload, "sdk_version") + definition_schema_version = payload.get("definition_schema_version") + if type(definition_schema_version) is not int or definition_schema_version != DEFINITION_SCHEMA_VERSION: + raise ValueError("UNSUPPORTED_ROUTINE_VERSION: unsupported Python definition schema") + artifact_digest = _required_digest(payload, "artifact_digest") + environment_digest = _required_digest(payload, "environment_digest") + definition_fingerprint = _required_digest(payload, "definition_fingerprint") max_batch_bytes = _required_positive_int(payload, "max_batch_bytes", 1 << 30) max_batch_rows = _required_positive_int(payload, "max_batch_rows", 1 << 30) + max_invocation_rows = _required_positive_int(payload, "max_invocation_rows", 1 << 32) + max_invocation_result_bytes = _required_positive_int( + payload, "max_invocation_result_bytes", 1 << 40 + ) handler_timeout_seconds = _required_positive_float( payload, "handler_timeout_seconds", 3600.0 ) @@ -1408,12 +2845,21 @@ def do_exchange(self, context, descriptor, reader, writer): if sdk_version != SDK_VERSION: raise ValueError("PROTOCOL: unsupported Python SDK") source = _required_string(payload, "source") handler_name = _required_string(payload, "handler") + if artifact_digest != _inline_artifact_digest(handler_name, source): + raise ValueError("UNSUPPORTED_ROUTINE_VERSION: Python artifact digest does not match the source") + if environment_digest != _environment_digest(): + raise ValueError("UNSUPPORTED_ROUTINE_VERSION: Python environment digest does not match the worker contract") # Validate the complete frozen type contract before reserving a # ledger entry. A malformed Open is rejected before it can leave # a terminal tombstone behind. for index, descriptor in enumerate(args): _field(f"arg_{index}", descriptor) result_field = _field("result", result_descriptor) + expected_fingerprint = _definition_fingerprint(payload) + if definition_fingerprint != expected_fingerprint: + raise ValueError( + "UNSUPPORTED_ROUTINE_VERSION: Python definition fingerprint does not match the typed definition" + ) state = self._admit(key) state.tuple = open_control["tuple"] schema = None @@ -1422,11 +2868,20 @@ def do_exchange(self, context, descriptor, reader, writer): writer_started = True writer.write_metadata(_encode_control({"kind": "ResultSchema", "tuple": open_control["tuple"]})) ended = False + invocation_rows = 0 + invocation_result_bytes = 0 + input_reader = _ExchangeInputReader(reader) while True: try: - chunk = reader.read_chunk() + chunk = input_reader.next(context) except StopIteration: break + # The reader can publish a chunk at the same instant that the + # RPC is cancelled. Re-check after the blocking handoff so a + # chunk already dequeued after cancellation cannot start user + # code or consume another handler slot. + if _context_is_cancelled(context): + raise TimeoutError("DEADLINE_EXCEEDED: input stream cancelled") if chunk is None or (chunk.data is None and not chunk.app_metadata): raise ValueError("PROTOCOL: empty input frame") if chunk.data is not None: @@ -1441,25 +2896,45 @@ def do_exchange(self, context, descriptor, reader, writer): _require_tuple(control["tuple"], open_control["tuple"]) if control["kind"] != "InputBatch": raise ValueError("PROTOCOL: data batch is missing InputBatch") sequence = _required_uint64(control, "sequence") - if sequence != state.last_input + 1: raise ValueError("PROTOCOL: input sequence is not contiguous") + if sequence != state.input_sequence() + 1: raise ValueError("PROTOCOL: input sequence is not contiguous") if batch.num_rows <= 0: raise ValueError("PROTOCOL: empty input batch") if batch.num_rows > max_batch_rows: raise ValueError("RESOURCE_EXHAUSTED: input batch has too many rows") + if invocation_rows > max_invocation_rows - batch.num_rows: + raise ValueError("RESOURCE_EXHAUSTED: invocation has too many rows") if batch.nbytes > max_batch_bytes: raise ValueError("RESOURCE_EXHAUSTED: input batch exceeds byte limit") for index, descriptor in enumerate(args): _validate_array_values(batch.column(index), descriptor) + state.record_input(sequence) execution_request = { "source": source, "handler": handler_name, "mode": mode, "null_policy": null_policy, + "abi_contract": abi_contract, + "adapter_version": adapter_version, "sdk_version": sdk_version, - "context": payload.get("context"), + "definition_schema_version": definition_schema_version, + "artifact_digest": artifact_digest, + "environment_digest": environment_digest, + "definition_fingerprint": definition_fingerprint, + "context": effective_context, "args": args, "return": result_descriptor, "max_batch_bytes": max_batch_bytes, + "max_batch_rows": max_batch_rows, + "max_invocation_rows": max_invocation_rows, + "max_invocation_result_bytes": max_invocation_result_bytes, "input": _serialize_record_batch(batch), } - output_wire = _run_handler_process( + if handler_session is None: + owner_id = _handler_quota_owner(payload) + handler_session = _HandlerProcessSession( + self._handler_slots, + handler_quota=self._handler_quota, + account_id=key[0], + owner_id=owner_id, + ) + output_wire = handler_session.run( context, execution_request, handler_timeout_seconds ) output_batch = _deserialize_record_batch(output_wire) @@ -1470,21 +2945,40 @@ def do_exchange(self, context, descriptor, reader, writer): _validate_array_values(output_array, result_descriptor) if pa.ipc.get_record_batch_size(output_batch) > max_batch_bytes: raise ValueError("RESOURCE_EXHAUSTED: output batch exceeds byte limit") - state.last_input = sequence - state.last_result = sequence - writer.write_metadata(_encode_control({"kind": "InputConsumed", "tuple": open_control["tuple"], "sequence": sequence})) + invocation_result_bytes += pa.ipc.get_record_batch_size(output_batch) + if invocation_result_bytes > max_invocation_result_bytes: + raise ValueError("RESOURCE_EXHAUSTED: invocation result exceeds byte limit") + invocation_rows += batch.num_rows + state.record_result(sequence) + writer.write_metadata(_encode_control({ + "kind": "InputConsumed", + "tuple": open_control["tuple"], + "sequence": sequence, + # This is the decoded Arrow backing released by the + # worker after the handler has returned. It is bounded + # by max_batch_bytes and is distinct from the CN's + # transport framing bytes. A zero-column batch has + # no value buffers, but still consumes one logical + # accounting unit so the positive-credit protocol + # remains valid for zero-argument VECTOR calls. + "released_bytes": max(int(batch.nbytes), 1), + "released_batches": 1, + })) writer.write_with_metadata(output_batch, _encode_control({"kind": "ResultBatch", "tuple": open_control["tuple"], "sequence": sequence})) state.wait_result_ack(sequence, context) + if handler_session.should_rollover: + handler_session.close() + handler_session = None elif chunk.app_metadata: control = _decode_control(chunk.app_metadata) _require_tuple(control["tuple"], open_control["tuple"]) if control["kind"] == "EndInput": last_sequence = _required_uint64(control, "last_sequence", allow_zero=True) if ended: - if last_sequence != state.last_input: + if last_sequence != state.input_sequence(): raise ValueError("PROTOCOL: EndInput changed after input was closed") continue - if last_sequence != state.last_input: + if last_sequence != state.input_sequence(): raise ValueError("PROTOCOL: invalid EndInput") ended = True elif control["kind"] == "OpenInvocation": @@ -1492,11 +2986,21 @@ def do_exchange(self, context, descriptor, reader, writer): else: raise ValueError("PROTOCOL: unexpected control without Arrow data") if not ended: raise ValueError("PROTOCOL: input stream ended before EndInput") - if state.acked_result != state.last_result: raise ValueError("PROTOCOL: result is not acknowledged") - state.finish_id = _uuid.uuid4().hex - writer.write_metadata(_encode_control({"kind": "Finish", "tuple": open_control["tuple"], "status": "OK", "finish_id": state.finish_id, "last_sequence": state.last_input})) + last_input, last_result, acked_result = state.sequences() + if acked_result != last_result: raise ValueError("PROTOCOL: result is not acknowledged") + finish_id = state.mark_finish_sent(_uuid.uuid4().hex) + writer.write_metadata(_encode_control({ + "kind": "Finish", + "tuple": open_control["tuple"], + "status": "OK", + "finish_id": finish_id, + "last_sequence": last_input, + "last_result_sequence": last_result, + })) state.wait_finish_ack(context) except Exception as exc: + if state is not None and _is_cancellation_error(exc, context): + state.mark_cancelled() if writer_started and key is not None: try: writer.write_metadata(_encode_control({"kind": "Error", "tuple": open_control["tuple"], "status": "ERROR", "reason": _safe_error(exc)})) @@ -1504,7 +3008,26 @@ def do_exchange(self, context, descriptor, reader, writer): pass raise finally: - self._finish_invocation(key, state) + exchange_error = sys.exc_info()[1] + cleanup_error = self._cleanup_exchange( + key, state, input_reader, handler_session + ) + if cleanup_error is not None and exchange_error is None: + raise cleanup_error + if cleanup_error is not None: + logging.error( + "Python UDF exchange cleanup failed after an exchange error: %s", + cleanup_error, + ) + + +def _is_cancellation_error(exc: Exception, context) -> bool: + if _context_is_cancelled(context): + return True + # Handler, result-ACK, and Finish-ACK deadlines use this stable protocol + # category. Once observed, they are a cancellation terminal outcome even + # if the transport context has not yet reported cancellation locally. + return isinstance(exc, TimeoutError) and str(exc).startswith("DEADLINE_EXCEEDED:") def _safe_error(exc: Exception) -> str: @@ -1518,16 +3041,32 @@ def _safe_error(exc: Exception) -> str: def main() -> None: parser = argparse.ArgumentParser() parser.add_argument("--execute-handler", action="store_true") + parser.add_argument("--watch-handler-group", type=int) parser.add_argument("--address") args = parser.parse_args() if args.execute_handler: _execute_handler_subprocess() return + if args.watch_handler_group is not None: + watchdog_fd_text = os.environ.pop(_HANDLER_WATCHDOG_FD_ENV, None) + if watchdog_fd_text is None: + parser.error("--watch-handler-group requires a watchdog pipe") + try: + watchdog_fd = int(watchdog_fd_text) + except ValueError: + parser.error("invalid watchdog pipe") + _watch_handler_group(watchdog_fd, args.watch_handler_group) + return if not args.address: parser.error("--address is required for the Flight server") logging.basicConfig(level=logging.INFO, format="[%(asctime)s] %(levelname)s %(message)s") address = args.address if "://" in args.address else "grpc://" + args.address - server = RoutineFlightServer(address) + # Every worker process is a distinct execution owner. A random positive + # epoch prevents a restarted worker at the same endpoint from accepting + # control messages issued to the previous process. Direct unit-test + # servers keep the deterministic default of one. + lease_epoch = secrets.randbits(63) or 1 + server = RoutineFlightServer(address, lease_epoch=lease_epoch) server.serve() diff --git a/pkg/udf/python_contract.go b/pkg/udf/python_contract.go new file mode 100644 index 0000000000000..c9a12dd2eaa61 --- /dev/null +++ b/pkg/udf/python_contract.go @@ -0,0 +1,86 @@ +// Copyright 2026 Matrix Origin +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. + +package udf + +import ( + "crypto/sha256" + "encoding/binary" + "encoding/hex" + "fmt" + + "github.com/matrixorigin/matrixone/pkg/udf/protocol" +) + +const ( + pythonInlineArtifactDigestDomain = "matrixone-python-inline-artifact" + pythonEnvironmentDigestDomain = "matrixone-python-environment" +) + +// PythonInlineArtifactDigest identifies the exact source payload sent to the +// worker. Length-prefixing each UTF-8 string prevents delimiter ambiguity and +// gives the Python adapter an unambiguous, language-independent algorithm. +func PythonInlineArtifactDigest(handler, source string) string { + return pythonContractDigest(pythonInlineArtifactDigestDomain, handler, source) +} + +// PythonEnvironmentDigest identifies the worker-side contract that interprets +// an inline source definition. The current environment plane is deliberately +// small: the executable artifact is inline, while the ABI, plan, type and +// timezone contracts are fixed by the negotiated worker capability. A future +// artifact store may add its immutable environment digest without changing +// this current digest algorithm. +func PythonEnvironmentDigest() (string, error) { + tzVersion, err := TimezoneDatabaseVersion() + if err != nil { + return "", err + } + return pythonContractDigest( + pythonEnvironmentDigestDomain, + fmt.Sprintf("%d", protocol.Version), + PythonABIContract, + PythonAdapterVersion, + PythonSDKVersion, + fmt.Sprintf("%d", PythonDefinitionSchemaVersion), + fmt.Sprintf("%d", PythonPlanContractVersion), + PythonTypeDescriptorContract, + tzVersion, + ), nil +} + +// IsSHA256Digest is used at execution boundaries for persisted digests and +// fingerprints. It accepts the canonical lower-case hexadecimal spelling +// only, so malformed or alternate encodings cannot create ambiguous identity. +func IsSHA256Digest(value string) bool { + if len(value) != sha256.Size*2 { + return false + } + decoded, err := hex.DecodeString(value) + if err != nil || len(decoded) != sha256.Size { + return false + } + for _, ch := range value { + if ch >= 'A' && ch <= 'F' { + return false + } + } + return true +} + +func pythonContractDigest(domain string, parts ...string) string { + hash := sha256.New() + writePythonDigestPart(hash, domain) + for _, part := range parts { + writePythonDigestPart(hash, part) + } + return hex.EncodeToString(hash.Sum(nil)) +} + +func writePythonDigestPart(hash interface{ Write([]byte) (int, error) }, value string) { + var length [8]byte + binary.BigEndian.PutUint64(length[:], uint64(len(value))) + _, _ = hash.Write(length[:]) + _, _ = hash.Write([]byte(value)) +} diff --git a/pkg/udf/python_contract_test.go b/pkg/udf/python_contract_test.go new file mode 100644 index 0000000000000..1f9edd7070603 --- /dev/null +++ b/pkg/udf/python_contract_test.go @@ -0,0 +1,28 @@ +// Copyright 2026 Matrix Origin +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. + +package udf + +import ( + "testing" + + "github.com/stretchr/testify/require" +) + +func TestPythonInlineArtifactDigestMatchesCrossLanguageVector(t *testing.T) { + const want = "d3b06769662f0e30fca60e6dedc479c5aacef6c488c6489366fdb540ee374f26" + require.Equal(t, want, PythonInlineArtifactDigest("add", "def add(ctx, value): return value")) + require.True(t, IsSHA256Digest(want)) + require.False(t, IsSHA256Digest("D3B06769662F0E30FCA60E6DED C479C5AACEF6C488C6489366FDB540EE374F26")) +} + +func TestPythonEnvironmentDigestIsCurrentAndStable(t *testing.T) { + first, err := PythonEnvironmentDigest() + require.NoError(t, err) + second, err := PythonEnvironmentDigest() + require.NoError(t, err) + require.Equal(t, first, second) + require.True(t, IsSHA256Digest(first)) +} diff --git a/pkg/udf/runtime.go b/pkg/udf/runtime.go index 5df11f7a374de..3c92e352a560d 100644 --- a/pkg/udf/runtime.go +++ b/pkg/udf/runtime.go @@ -20,8 +20,12 @@ package udf import ( "context" + "encoding/json" "errors" + "fmt" "sort" + "strconv" + "strings" "sync" "github.com/matrixorigin/matrixone/pkg/common/moerr" @@ -32,18 +36,178 @@ import ( ) const ( + LanguageSQL = "sql" LanguagePython = "python" PythonABIContract = "PYTHON_ARROW" PythonAdapterVersion = "2026-09" PythonSDKVersion = "1.0" - NullCallHandler = "CALLED_ON_NULL_INPUT" - NullReturnNull = "RETURNS_NULL_ON_NULL_INPUT" + // These numeric contracts are persisted in catalog and plan records. They + // are intentionally separate from the human-facing adapter/SDK names: + // changing a wire or persistent shape must fail closed before user code is + // entered, even when the runtime happens to use the same language ABI name. + SQLDefinitionSchemaVersion = 1 + RoutinePlanContractVersion = 1 + PythonDefinitionSchemaVersion = 1 + // Kept as a source-compatible alias for the Python adapter while the + // plan field is shared by SQL and Python routine calls. + PythonPlanContractVersion = RoutinePlanContractVersion + // PythonSignatureKeySchemaVersion versions the canonical descriptor + // encoding stored on the shared Function identity. It is independent of + // the implementation body schema: changing the overload key is an + // identity/catalog migration, while changing the body is a revision. + PythonSignatureKeySchemaVersion = 1 + PythonTypeDescriptorContract = "ARROW_DESCRIPTOR" + NullCallHandler = "CALLED_ON_NULL_INPUT" + NullReturnNull = "RETURNS_NULL_ON_NULL_INPUT" + StatementContextContractVersion = 1 + SecurityFrameContractVersion = 1 ) +// StatementContext is the typed execution-time snapshot visible to a Python +// handler. It is intentionally separate from plan metadata: a prepared plan +// may be reused by many statements, while these values are frozen once per +// execution. The JSON form is also the cross-process contract used by the +// Gateway and worker. +type StatementContext struct { + ContractVersion int `json:"contract_version"` + StatementTimestampUTC int64 `json:"statement_timestamp_utc"` + TimezoneKind string `json:"timezone_kind"` + TimezoneName string `json:"timezone_name,omitempty"` + TimezoneOffsetMinutes int32 `json:"timezone_offset_minutes"` + TimezoneDatabaseVersion string `json:"timezone_database_version,omitempty"` + SQLMode []string `json:"sql_mode"` + CurrentDatabase string `json:"current_database,omitempty"` + CurrentUser string `json:"current_user"` + CurrentRole string `json:"current_role,omitempty"` + ConnectionCollation string `json:"connection_collation"` +} + +func (c StatementContext) Validate() error { + if c.ContractVersion != StatementContextContractVersion { + return fmt.Errorf("UNSUPPORTED_ROUTINE_VERSION: unsupported statement context contract %d", c.ContractVersion) + } + if c.CurrentUser == "" || c.ConnectionCollation == "" { + return fmt.Errorf("python udf: statement context has no authenticated user or collation") + } + if c.TimezoneKind == "IANA" { + if c.TimezoneName == "" || c.TimezoneDatabaseVersion == "" || c.TimezoneOffsetMinutes != 0 { + return fmt.Errorf("python udf: IANA statement timezone is incomplete") + } + } else if c.TimezoneKind == "FIXED_OFFSET" { + if c.TimezoneName != "" || c.TimezoneDatabaseVersion != "" || c.TimezoneOffsetMinutes < -839 || c.TimezoneOffsetMinutes > 840 { + return fmt.Errorf("python udf: fixed statement timezone is invalid") + } + } else { + return fmt.Errorf("python udf: unsupported statement timezone kind %q", c.TimezoneKind) + } + mode := append([]string(nil), c.SQLMode...) + sort.Strings(mode) + for i := range mode { + if mode[i] == "" || (i > 0 && mode[i] == mode[i-1]) { + return fmt.Errorf("python udf: statement sql_mode is not canonical") + } + } + if len(mode) != len(c.SQLMode) { + return fmt.Errorf("python udf: statement sql_mode is not canonical") + } + for i := range mode { + if mode[i] != c.SQLMode[i] { + return fmt.Errorf("python udf: statement sql_mode is not sorted") + } + } + return nil +} + +// StatementContextFromMap is the only adapter from the legacy internal map +// assembled by the SQL session to the typed runtime contract. It accepts the +// map solely at the trusted CN boundary and returns nil when no statement +// fields are present, which keeps transport/protocol unit tests able to test +// zero-row admission without manufacturing session state. +func StatementContextFromMap(values map[string]string) (*StatementContext, error) { + if len(values) == 0 { + return nil, nil + } + statementKeys := []string{ + "statement_timestamp_utc", "session_timezone_kind", "session_timezone_name", + "session_timezone_offset_minutes", "session_timezone_tzdb_version", "sql_mode", + "current_database", "current_user", "current_role", "connection_collation", + } + present := false + for _, key := range statementKeys { + if _, ok := values[key]; ok { + present = true + break + } + } + if !present { + return nil, nil + } + timestamp, err := strconv.ParseInt(values["statement_timestamp_utc"], 10, 64) + if err != nil { + return nil, fmt.Errorf("python udf: invalid statement timestamp: %w", err) + } + mode := make([]string, 0) + if raw := values["sql_mode"]; raw != "" { + if err := json.Unmarshal([]byte(raw), &mode); err != nil { + return nil, fmt.Errorf("python udf: invalid statement sql_mode: %w", err) + } + } + context := &StatementContext{ + ContractVersion: StatementContextContractVersion, + StatementTimestampUTC: timestamp, + TimezoneKind: strings.ToUpper(values["session_timezone_kind"]), + TimezoneName: values["session_timezone_name"], + SQLMode: mode, + CurrentDatabase: values["current_database"], + CurrentUser: values["current_user"], + CurrentRole: values["current_role"], + ConnectionCollation: values["connection_collation"], + TimezoneDatabaseVersion: values["session_timezone_tzdb_version"], + } + if raw := values["session_timezone_offset_minutes"]; raw != "" { + offset, parseErr := strconv.ParseInt(raw, 10, 32) + if parseErr != nil { + return nil, fmt.Errorf("python udf: invalid fixed timezone offset: %w", parseErr) + } + context.TimezoneOffsetMinutes = int32(offset) + } + if err := context.Validate(); err != nil { + return nil, err + } + return context, nil +} + +// SecurityFrame records the principal entering this routine layer. Python +// currently supports INVOKER only, so the effective IDs must equal the +// invoker IDs. The explicit frame prevents a future definer implementation +// from silently reusing the outer session's principal. +type SecurityFrame struct { + ContractVersion int `json:"contract_version"` + Mode string `json:"mode"` + InvokerUserID uint32 `json:"invoker_user_id"` + InvokerRoleID uint32 `json:"invoker_role_id"` + EffectiveUserID uint32 `json:"effective_user_id"` + EffectiveRoleID uint32 `json:"effective_role_id"` +} + +func (f SecurityFrame) Validate() error { + if f.ContractVersion != SecurityFrameContractVersion || f.Mode != "INVOKER" { + return fmt.Errorf("UNSUPPORTED_ROUTINE_VERSION: unsupported Python security frame") + } + if f.InvokerUserID != f.EffectiveUserID || f.InvokerRoleID != f.EffectiveRoleID { + return fmt.Errorf("UNSUPPORTED_ROUTINE_VERSION: Python invoker security frame changed effective principal") + } + return nil +} + // Invocation is the language-neutral routine call handed to an admitted // runtime. The runtime owns transport, worker lifecycle, and Arrow // conversion; CN owns the SQL vectors, result wrapper, and fencing identity. type Invocation struct { + // Function identity and revision are copied from the typed plan. They are + // part of the execution grant sent to the worker; the runtime never + // resolves a function name or latest revision. + FunctionRef FunctionRef Language string Handler string Source string @@ -56,8 +220,40 @@ type Invocation struct { ABIContract string AdapterVersion string SDKVersion string - Context map[string]string - Tuple protocol.FencingTuple + // These values are copied from the immutable typed implementation. The + // gateway and worker validate them before opening user code; they are not + // inferred from a mutable source path or a function name. + DefinitionSchemaVersion int + ArtifactDigest string + EnvironmentDigest string + DefinitionFingerprint string + Context map[string]string + // Common routine semantics are copied from the typed plan. The Python + // gateway refuses an invocation with absent or altered values. + CallsiteID string + MayError bool + SecurityMode string + Leakproof bool + StatementContext *StatementContext + SecurityFrame *SecurityFrame + Tuple protocol.FencingTuple +} + +// FunctionRef is the language-neutral form of the shared Catalog identity +// carried through the runtime boundary. +type FunctionRef struct { + AccountID uint64 `json:"account_id"` + DatabaseID uint64 `json:"database_id"` + FunctionID uint64 `json:"function_id"` + Revision uint64 `json:"revision"` + NamespaceVersion uint64 `json:"namespace_version"` +} + +func (r FunctionRef) Validate() error { + if r.DatabaseID == 0 || r.FunctionID == 0 || r.Revision == 0 || r.NamespaceVersion == 0 { + return moerr.NewInvalidInputNoCtx("incomplete UDF FunctionRef") + } + return nil } // Runtime executes one invocation. Implementations must not retain the @@ -75,6 +271,14 @@ type RuntimeCloser interface { Close() error } +// RuntimeReadiness is an optional feature-level gate. A caller uses it before +// publishing a routine so a node never persists a new external definition +// that its current runtime cannot execute. It does not affect ordinary SQL +// routines or ordinary SQL when the Python runtime is disabled. +type RuntimeReadiness interface { + CheckLanguageReady(context.Context, string) error +} + // Registry dispatches to the one runtime selected by a routine's language. // A registry is immutable after construction, so plan execution cannot race // service registration or observe a partially initialized adapter. @@ -116,6 +320,18 @@ func (r *runtimeRegistry) Execute( return runtime.Execute(ctx, invocation, result, mp) } +func (r *runtimeRegistry) CheckLanguageReady(ctx context.Context, language string) error { + runtime := r.byLanguage[language] + if runtime == nil { + return moerr.NewNotSupportedf(ctx, "%s UDF runtime is not enabled", language) + } + readiness, ok := runtime.(RuntimeReadiness) + if !ok { + return moerr.NewNotSupportedf(ctx, "%s UDF runtime does not expose the current contract", language) + } + return readiness.CheckLanguageReady(ctx, language) +} + func (r *runtimeRegistry) Close() error { r.closeOnce.Do(func() { languages := make([]string, 0, len(r.byLanguage)) diff --git a/pkg/udf/runtime_test.go b/pkg/udf/runtime_test.go index 908d7764f225b..9c937606a8f15 100644 --- a/pkg/udf/runtime_test.go +++ b/pkg/udf/runtime_test.go @@ -46,3 +46,73 @@ func TestRuntimeRegistryClosesOwnedRuntimesOnce(t *testing.T) { require.NoError(t, closer.Close()) require.Equal(t, int32(1), python.closed.Load()) } + +func TestStatementContextFromMapProducesCanonicalTypedSnapshot(t *testing.T) { + snapshot, err := StatementContextFromMap(map[string]string{ + "statement_timestamp_utc": "1704067200123456", + "session_timezone_kind": "FIXED_OFFSET", + "session_timezone_offset_minutes": "+510", + "sql_mode": "[\"ANSI\",\"STRICT_TRANS_TABLES\"]", + "current_database": "app", + "current_user": "alice", + "current_role": "writer", + "connection_collation": "utf8mb4_bin", + }) + require.NoError(t, err) + require.Equal(t, int32(510), snapshot.TimezoneOffsetMinutes) + require.Equal(t, []string{"ANSI", "STRICT_TRANS_TABLES"}, snapshot.SQLMode) + require.NoError(t, snapshot.Validate()) + + snapshot.SQLMode = []string{"STRICT_TRANS_TABLES", "ANSI"} + require.ErrorContains(t, snapshot.Validate(), "not sorted") +} + +func TestStatementContextFromMapRejectsAmbiguousTimezoneAndMalformedMode(t *testing.T) { + base := map[string]string{ + "statement_timestamp_utc": "1704067200123456", + "session_timezone_kind": "FIXED_OFFSET", + "session_timezone_offset_minutes": "+480", + "sql_mode": "[]", + "current_user": "alice", + "connection_collation": "utf8mb4_bin", + } + ambiguous := make(map[string]string, len(base)+1) + for key, value := range base { + ambiguous[key] = value + } + ambiguous["session_timezone_name"] = "Asia/Shanghai" + _, err := StatementContextFromMap(ambiguous) + require.ErrorContains(t, err, "fixed statement timezone") + + malformed := make(map[string]string, len(base)) + for key, value := range base { + malformed[key] = value + } + malformed["sql_mode"] = "[\"ANSI\",\"ANSI\"]" + _, err = StatementContextFromMap(malformed) + require.ErrorContains(t, err, "not canonical") +} + +func TestStatementContextAcceptsUnixEpoch(t *testing.T) { + snapshot, err := StatementContextFromMap(map[string]string{ + "statement_timestamp_utc": "0", + "session_timezone_kind": "FIXED_OFFSET", + "session_timezone_offset_minutes": "0", + "sql_mode": "[]", + "current_user": "alice", + "connection_collation": "utf8mb4_bin", + }) + require.NoError(t, err) + require.Zero(t, snapshot.StatementTimestampUTC) + require.NoError(t, snapshot.Validate()) +} + +func TestSecurityFrameRejectsEffectivePrincipalChange(t *testing.T) { + frame := SecurityFrame{ + ContractVersion: SecurityFrameContractVersion, + Mode: "INVOKER", + InvokerUserID: 7, + EffectiveUserID: 8, + } + require.ErrorContains(t, frame.Validate(), "changed effective principal") +} diff --git a/pkg/udf/timezone.go b/pkg/udf/timezone.go new file mode 100644 index 0000000000000..f4816995f043d --- /dev/null +++ b/pkg/udf/timezone.go @@ -0,0 +1,47 @@ +// Copyright 2026 Matrix Origin +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. + +package udf + +import ( + "bufio" + "fmt" + "os" + "strings" +) + +// TimezoneDatabaseVersion returns the version of the system IANA tz database +// used by the current process. An IANA timezone is part of the Python UDF +// statement contract, so silently returning a made-up version would allow two +// hosts to interpret a DST boundary differently. +func TimezoneDatabaseVersion() (string, error) { + paths := []string{ + "/usr/share/zoneinfo/tzdata.zi", + "/usr/lib/zoneinfo/tzdata.zi", + "/usr/share/lib/zoneinfo/tzdata.zi", + "/etc/zoneinfo/tzdata.zi", + } + for _, path := range paths { + file, err := os.Open(path) + if err != nil { + continue + } + scanner := bufio.NewScanner(file) + lineOK := scanner.Scan() + line := scanner.Text() + _ = file.Close() + if !lineOK { + continue + } + const prefix = "# version " + if strings.HasPrefix(line, prefix) { + version := strings.TrimSpace(strings.TrimPrefix(line, prefix)) + if version != "" { + return version, nil + } + } + } + return "", fmt.Errorf("python udf: IANA timezone database version is unavailable") +} diff --git a/proto/plan.proto b/proto/plan.proto index c3dc24ed624fc..69f6461c2d89a 100644 --- a/proto/plan.proto +++ b/proto/plan.proto @@ -253,6 +253,8 @@ message Function { // overload while optimizers still distinguish it from an implicit cast. // Older CNs safely ignore this unknown field and execute the legacy overload. bool syntax_explicit_cast = 5; + // Typed routine metadata. Ordinary builtin Function values leave this nil. + RoutineCall routine_call = 6; } // PreparedNumericMetadata is sparse planner provenance for a numeric overload @@ -1709,6 +1711,12 @@ message Query { // execution so a committed concurrent CREATE INDEX can rebuild the plan; // without a definition change, it returns the original key-not-found error. repeated UnresolvedIndexHint unresolved_index_hints = 16; + + // Exact routine revisions captured while binding this statement. The + // expression tree carries the call-site implementation, while this bounded + // envelope lets prepared and ordinary plan-cache reuse validate the shared + // catalog namespace without walking or reinterpreting user source. + repeated RoutinePlanDependency routine_dependencies = 17; } message TransationControl { @@ -2379,3 +2387,85 @@ message UnresolvedIndexHint { ObjectRef table = 1; string index_name = 2; } + +// FunctionRef is the exact catalog identity carried by an executable plan. +// A zero revision is invalid for external routines; the planner validates the +// complete call before a fragment is dispatched. +message FunctionRef { + uint64 function_id = 1; + uint64 revision = 2; + uint64 namespace_version = 3; + // Function identity is tenant/database scoped. These fields prevent a + // plan fragment from being interpreted in a different catalog namespace. + uint64 account_id = 4; + uint64 database_id = 5; +} + +message SqlRoutineImplementation { + bytes definition_fingerprint = 1; + int32 semantic_definition_schema_version = 2; +} + +message PythonRoutineImplementation { + string handler = 1; + // Source is retained only so an old serialized demo plan can be identified + // and rejected. A current executable plan must carry the immutable artifact + // digest and resolve the artifact through the runtime artifact store. + string source = 2 [deprecated = true]; + string abi_contract = 3; + string adapter_version = 4; + string artifact_digest = 5; + string environment_digest = 6; + string sdk_version = 7; + int32 definition_schema_version = 8; + string mode = 9; + string null_policy = 10; + string definition_fingerprint = 11; +} + +// RoutineCall is the common typed call envelope for SQL and external +// language routines. Function.Args remains the normal expression argument +// list so existing expression traversal and CASE/selection ownership stay +// intact; this field carries only immutable routine identity and semantics. +message RoutineCall { + int32 contract_version = 1; + FunctionRef function_ref = 2; + string language = 3; + repeated Type argument_types = 4; + Type return_type = 5 [(gogoproto.nullable) = false]; + string volatility = 6; + string null_policy = 7; + oneof implementation { + SqlRoutineImplementation sql = 8; + PythonRoutineImplementation python = 9; + } + // Reserved from the current executable contract. Statement identity, + // session values, and group fences are created at execution time; keeping + // them in a prepared plan would allow stale state to cross executions. + map context = 10 [deprecated = true]; + // These fields are the common semantic contract. They must survive SQL + // lowering and expression/plan cloning; the optimizer may only move a + // routine call when the attached contract permits it. + string callsite_id = 11; + bool may_error = 12; + string security_mode = 13; + bool leakproof = 14; +} + +// RoutinePlanDependency is the shared cache/prepared-plan validation envelope +// for a resolved routine closure. FunctionRef is the executable identity; +// the integrity fields prove that the implementation attached to the plan is +// the same immutable revision that the resolver admitted. +message RoutinePlanDependency { + FunctionRef function_ref = 1; + string language = 2; + int32 contract_version = 3; + string definition_fingerprint = 4; + string artifact_digest = 5; + string environment_digest = 6; + string volatility = 7; + string null_policy = 8; + bool may_error = 9; + string security_mode = 10; + bool leakproof = 11; +} diff --git a/test/distributed/cases/udf_python/README.md b/test/distributed/cases/udf_python/README.md index 2e34d22d10070..4b253a3642c6f 100644 --- a/test/distributed/cases/udf_python/README.md +++ b/test/distributed/cases/udf_python/README.md @@ -39,6 +39,14 @@ The cases keep the data sets small and target contract boundaries: UUID round trips, and NULL. - `temporal_boundaries.sql` checks leap days, microseconds, TIME limits, SQL zero-date values, and NULL. +- `relational_positions.sql` checks UDF evaluation in WHERE, JOIN ON and outer + join predicates, aggregate arguments, window ordering, and nested calls. +- `dml_positions.sql` checks UDF evaluation in INSERT values, UPDATE + assignments, and DELETE predicates while preserving SQL NULL filtering. +- `dml_error_paths.sql` checks that handler failures abort UPDATE, INSERT + SELECT, and DELETE atomically without publishing partial changes. +- `revision_lifecycle.sql` checks create, immutable-revision replacement, + prepared-plan invalidation, drop, and recreate on the same SQL signature. The shape follows established correctness coverage from [DuckDB's scalar UDF tests](https://github.com/duckdb/duckdb-go/blob/main/scalar_udf_test.go) diff --git a/test/distributed/cases/udf_python/dml_error_paths.result b/test/distributed/cases/udf_python/dml_error_paths.result new file mode 100644 index 0000000000000..f1eaa4c487777 --- /dev/null +++ b/test/distributed/cases/udf_python/dml_error_paths.result @@ -0,0 +1,30 @@ +drop database if exists udf_python_dml_errors_bvt; +create database udf_python_dml_errors_bvt; +use udf_python_dml_errors_bvt; +create function python_bvt_dml_fail (x int) returns int language python as 'def python_bvt_dml_fail(ctx, x): return x if x != 2 else 1 / 0' handler 'python_bvt_dml_fail'; +create table source_values (id int primary key, value int); +insert into source_values values (1, 1), (2, 2), (3, 3); +update source_values set value = python_bvt_dml_fail(value); +python udf: worker error: USER_CODE: handler or runtime failed +select id, value from source_values order by id; +➤ id[4,32,0] ¦ value[4,32,0] 𝄀 +1 ¦ 1 𝄀 +2 ¦ 2 𝄀 +3 ¦ 3 +create table inserted_values (id int primary key, value int); +insert into inserted_values select id, python_bvt_dml_fail(value) from source_values; +python udf: worker error: USER_CODE: handler or runtime failed +select count(*) as inserted_rows from inserted_values; +➤ inserted_rows[-5,64,0] 𝄀 +0 +delete from source_values where python_bvt_dml_fail(value) > 0; +python udf: worker error: USER_CODE: handler or runtime failed +select id, value from source_values order by id; +➤ id[4,32,0] ¦ value[4,32,0] 𝄀 +1 ¦ 1 𝄀 +2 ¦ 2 𝄀 +3 ¦ 3 +drop function python_bvt_dml_fail(int); +drop table inserted_values; +drop table source_values; +drop database udf_python_dml_errors_bvt; diff --git a/test/distributed/cases/udf_python/dml_error_paths.sql b/test/distributed/cases/udf_python/dml_error_paths.sql new file mode 100644 index 0000000000000..5e7b4d562b889 --- /dev/null +++ b/test/distributed/cases/udf_python/dml_error_paths.sql @@ -0,0 +1,31 @@ +-- A Python failure is a statement failure. The DML operator must not publish +-- rows that were evaluated before the handler raised an error. +drop database if exists udf_python_dml_errors_bvt; +create database udf_python_dml_errors_bvt; +use udf_python_dml_errors_bvt; + +create function python_bvt_dml_fail (x int) returns int language python as 'def python_bvt_dml_fail(ctx, x): return x if x != 2 else 1 / 0' handler 'python_bvt_dml_fail'; +create table source_values (id int primary key, value int); +insert into source_values values (1, 1), (2, 2), (3, 3); + +-- UPDATE evaluates row 1 before row 2 raises. The original table must remain +-- unchanged after the failed statement. +--error +update source_values set value = python_bvt_dml_fail(value); +select id, value from source_values order by id; + +create table inserted_values (id int primary key, value int); +-- INSERT ... SELECT must publish no row after the same handler failure. +--error +insert into inserted_values select id, python_bvt_dml_fail(value) from source_values; +select count(*) as inserted_rows from inserted_values; + +-- DELETE must also leave all source rows intact when its predicate fails. +--error +delete from source_values where python_bvt_dml_fail(value) > 0; +select id, value from source_values order by id; + +drop function python_bvt_dml_fail(int); +drop table inserted_values; +drop table source_values; +drop database udf_python_dml_errors_bvt; diff --git a/test/distributed/cases/udf_python/dml_positions.result b/test/distributed/cases/udf_python/dml_positions.result new file mode 100644 index 0000000000000..8b284aaf5211e --- /dev/null +++ b/test/distributed/cases/udf_python/dml_positions.result @@ -0,0 +1,17 @@ +drop database if exists udf_python_dml_bvt; +create database udf_python_dml_bvt; +use udf_python_dml_bvt; +create function python_bvt_dml_add (x int) returns int language python as 'def python_bvt_dml_add(ctx, x): return None if x is None else x + 1' handler 'python_bvt_dml_add'; +create table dml_values (id int primary key, value int); +insert into dml_values values (1, 1), (2, 2), (3, null); +insert into dml_values values (4, python_bvt_dml_add(3)); +update dml_values set value = python_bvt_dml_add(value) where id = 1; +delete from dml_values where python_bvt_dml_add(value) >= 4; +select id, value from dml_values order by id; +➤ id[4,32,0] ¦ value[4,32,0] 𝄀 +1 ¦ 2 𝄀 +2 ¦ 2 𝄀 +3 ¦ null +drop function python_bvt_dml_add(int); +drop table dml_values; +drop database udf_python_dml_bvt; diff --git a/test/distributed/cases/udf_python/dml_positions.sql b/test/distributed/cases/udf_python/dml_positions.sql new file mode 100644 index 0000000000000..f956d284924af --- /dev/null +++ b/test/distributed/cases/udf_python/dml_positions.sql @@ -0,0 +1,25 @@ +-- Exercise Python UDFs in INSERT values, UPDATE assignments, and DELETE +-- predicates. Every mutation is observed by the final SELECT. +drop database if exists udf_python_dml_bvt; +create database udf_python_dml_bvt; +use udf_python_dml_bvt; + +create function python_bvt_dml_add (x int) returns int language python as 'def python_bvt_dml_add(ctx, x): return None if x is None else x + 1' handler 'python_bvt_dml_add'; +create table dml_values (id int primary key, value int); +insert into dml_values values (1, 1), (2, 2), (3, null); + +-- INSERT must evaluate the routine for the incoming row. +insert into dml_values values (4, python_bvt_dml_add(3)); + +-- UPDATE must evaluate the routine against the target row image. +update dml_values set value = python_bvt_dml_add(value) where id = 1; + +-- DELETE must keep the routine in its target predicate. NULL remains unknown +-- and therefore must not be deleted. +delete from dml_values where python_bvt_dml_add(value) >= 4; + +select id, value from dml_values order by id; + +drop function python_bvt_dml_add(int); +drop table dml_values; +drop database udf_python_dml_bvt; diff --git a/test/distributed/cases/udf_python/relational_positions.result b/test/distributed/cases/udf_python/relational_positions.result new file mode 100644 index 0000000000000..3ccca0c83215c --- /dev/null +++ b/test/distributed/cases/udf_python/relational_positions.result @@ -0,0 +1,59 @@ +drop database if exists udf_python_positions_bvt; +create database udf_python_positions_bvt; +use udf_python_positions_bvt; +create function python_bvt_position_add (x int) returns int language python as 'def python_bvt_position_add(ctx, x): return None if x is None else x + 1' handler 'python_bvt_position_add'; +create function python_bvt_position_twice (x int) returns int language python as 'def python_bvt_position_twice(ctx, x): return None if x is None else x * 2' handler 'python_bvt_position_twice'; +create table position_left (id int, join_key int, value int); +create table position_right (join_key int, tag varchar(8)); +insert into position_left values (1, 10, 1), (2, 20, 2), (3, 30, null); +insert into position_right values (11, 'a'), (21, 'b'), (40, 'd'); +select id, python_bvt_position_add(value) as plus +from position_left +where python_bvt_position_add(value) > 1 +order by id; +➤ id[4,32,0] ¦ plus[4,32,0] 𝄀 +1 ¦ 2 𝄀 +2 ¦ 3 +select l.id, r.tag +from position_left l join position_right r +on python_bvt_position_add(l.join_key) = r.join_key +order by l.id; +➤ id[4,32,0] ¦ tag[12,8,0] 𝄀 +1 ¦ a 𝄀 +2 ¦ b +select l.id, r.tag +from position_left l left join position_right r +on python_bvt_position_add(l.join_key) = r.join_key +order by l.id; +➤ id[4,32,0] ¦ tag[12,8,0] 𝄀 +1 ¦ a 𝄀 +2 ¦ b 𝄀 +3 ¦ null +select join_key, sum(python_bvt_position_add(value)) as total +from position_left +group by join_key +order by join_key; +➤ join_key[4,32,0] ¦ total[-5,64,0] 𝄀 +10 ¦ 2 𝄀 +20 ¦ 3 𝄀 +30 ¦ null +select id, python_bvt_position_add(value) as plus, +row_number() over (order by python_bvt_position_add(value)) as rn +from position_left +order by id; +➤ id[4,32,0] ¦ plus[4,32,0] ¦ rn[-5,64,0] 𝄀 +1 ¦ 2 ¦ 2 𝄀 +2 ¦ 3 ¦ 3 𝄀 +3 ¦ null ¦ 1 +select id, python_bvt_position_twice(python_bvt_position_add(value)) as nested +from position_left +order by id; +➤ id[4,32,0] ¦ nested[4,32,0] 𝄀 +1 ¦ 4 𝄀 +2 ¦ 6 𝄀 +3 ¦ null +drop function python_bvt_position_add(int); +drop function python_bvt_position_twice(int); +drop table position_left; +drop table position_right; +drop database udf_python_positions_bvt; diff --git a/test/distributed/cases/udf_python/relational_positions.sql b/test/distributed/cases/udf_python/relational_positions.sql new file mode 100644 index 0000000000000..8ec2e716f4470 --- /dev/null +++ b/test/distributed/cases/udf_python/relational_positions.sql @@ -0,0 +1,54 @@ +-- Exercise the current SQL execution positions that must preserve the +-- Python call's row mapping: WHERE, JOIN ON, outer join, aggregate, window, +-- and a dependent call g(f(x)). +drop database if exists udf_python_positions_bvt; +create database udf_python_positions_bvt; +use udf_python_positions_bvt; + +create function python_bvt_position_add (x int) returns int language python as 'def python_bvt_position_add(ctx, x): return None if x is None else x + 1' handler 'python_bvt_position_add'; +create function python_bvt_position_twice (x int) returns int language python as 'def python_bvt_position_twice(ctx, x): return None if x is None else x * 2' handler 'python_bvt_position_twice'; + +create table position_left (id int, join_key int, value int); +create table position_right (join_key int, tag varchar(8)); +insert into position_left values (1, 10, 1), (2, 20, 2), (3, 30, null); +insert into position_right values (11, 'a'), (21, 'b'), (40, 'd'); + +-- The predicate and projection both use Python, with NULL filtered by SQL. +select id, python_bvt_position_add(value) as plus +from position_left +where python_bvt_position_add(value) > 1 +order by id; + +-- The UDF remains part of JOIN ON and must not be rewritten as a post-join +-- filter, so the LEFT JOIN keeps the unmatched outer row. +select l.id, r.tag +from position_left l join position_right r + on python_bvt_position_add(l.join_key) = r.join_key +order by l.id; + +select l.id, r.tag +from position_left l left join position_right r + on python_bvt_position_add(l.join_key) = r.join_key +order by l.id; + +-- Aggregate input and window ordering both consume a Python expression. +select join_key, sum(python_bvt_position_add(value)) as total +from position_left +group by join_key +order by join_key; + +select id, python_bvt_position_add(value) as plus, + row_number() over (order by python_bvt_position_add(value)) as rn +from position_left +order by id; + +-- The outer call consumes the inner call's result and must retain NULL. +select id, python_bvt_position_twice(python_bvt_position_add(value)) as nested +from position_left +order by id; + +drop function python_bvt_position_add(int); +drop function python_bvt_position_twice(int); +drop table position_left; +drop table position_right; +drop database udf_python_positions_bvt; diff --git a/test/distributed/cases/udf_python/revision_lifecycle.result b/test/distributed/cases/udf_python/revision_lifecycle.result new file mode 100644 index 0000000000000..9cc809811a16c --- /dev/null +++ b/test/distributed/cases/udf_python/revision_lifecycle.result @@ -0,0 +1,35 @@ +drop database if exists udf_python_revision_bvt; +create database udf_python_revision_bvt; +use udf_python_revision_bvt; +create function python_bvt_revision (x int) returns int language python as 'def python_bvt_revision(ctx, x): return None if x is None else x + 1' handler 'python_bvt_revision'; +create table revision_values (id int, value int); +insert into revision_values values (1, 1), (2, null); +select id, python_bvt_revision(value) as result from revision_values order by id; +➤ id[4,32,0] ¦ result[4,32,0] 𝄀 +1 ¦ 2 𝄀 +2 ¦ null +create or replace function python_bvt_revision (x int) returns int language python as 'def python_bvt_revision(ctx, x): return None if x is None else x + 2' handler 'python_bvt_revision'; +select id, python_bvt_revision(value) as result from revision_values order by id; +➤ id[4,32,0] ¦ result[4,32,0] 𝄀 +1 ¦ 3 𝄀 +2 ¦ null +prepare python_revision_plan from 'select id, python_bvt_revision(value) as result from revision_values order by id'; +execute python_revision_plan; +➤ id[4,32,0] ¦ result[4,32,0] 𝄀 +1 ¦ 3 𝄀 +2 ¦ null +create or replace function python_bvt_revision (x int) returns int language python as 'def python_bvt_revision(ctx, x): return None if x is None else x + 4' handler 'python_bvt_revision'; +execute python_revision_plan; +➤ id[4,32,0] ¦ result[4,32,0] 𝄀 +1 ¦ 5 𝄀 +2 ¦ null +deallocate prepare python_revision_plan; +drop function python_bvt_revision(int); +create function python_bvt_revision (x int) returns int language python as 'def python_bvt_revision(ctx, x): return None if x is None else x + 3' handler 'python_bvt_revision'; +select id, python_bvt_revision(value) as result from revision_values order by id; +➤ id[4,32,0] ¦ result[4,32,0] 𝄀 +1 ¦ 4 𝄀 +2 ¦ null +drop function python_bvt_revision(int); +drop table revision_values; +drop database udf_python_revision_bvt; diff --git a/test/distributed/cases/udf_python/revision_lifecycle.sql b/test/distributed/cases/udf_python/revision_lifecycle.sql new file mode 100644 index 0000000000000..dbb28f1abd098 --- /dev/null +++ b/test/distributed/cases/udf_python/revision_lifecycle.sql @@ -0,0 +1,20 @@ +drop database if exists udf_python_revision_bvt; +create database udf_python_revision_bvt; +use udf_python_revision_bvt; +create function python_bvt_revision (x int) returns int language python as 'def python_bvt_revision(ctx, x): return None if x is None else x + 1' handler 'python_bvt_revision'; +create table revision_values (id int, value int); +insert into revision_values values (1, 1), (2, null); +select id, python_bvt_revision(value) as result from revision_values order by id; +create or replace function python_bvt_revision (x int) returns int language python as 'def python_bvt_revision(ctx, x): return None if x is None else x + 2' handler 'python_bvt_revision'; +select id, python_bvt_revision(value) as result from revision_values order by id; +prepare python_revision_plan from 'select id, python_bvt_revision(value) as result from revision_values order by id'; +execute python_revision_plan; +create or replace function python_bvt_revision (x int) returns int language python as 'def python_bvt_revision(ctx, x): return None if x is None else x + 4' handler 'python_bvt_revision'; +execute python_revision_plan; +deallocate prepare python_revision_plan; +drop function python_bvt_revision(int); +create function python_bvt_revision (x int) returns int language python as 'def python_bvt_revision(ctx, x): return None if x is None else x + 3' handler 'python_bvt_revision'; +select id, python_bvt_revision(value) as result from revision_values order by id; +drop function python_bvt_revision(int); +drop table revision_values; +drop database udf_python_revision_bvt; From a7623cf2f2a9203d9cb9b0d36bd837a1824b834a Mon Sep 17 00:00:00 2001 From: iamlinjunhong <1030420200@qq.com> Date: Fri, 11 Sep 2026 22:16:05 +0800 Subject: [PATCH 054/202] feat(udf): validate Python definitions before publication --- pkg/frontend/authenticate.go | 222 +++++++---- pkg/frontend/python_udf_catalog_test.go | 62 +++ pkg/udf/python/arrow.go | 240 +++++++++++- pkg/udf/python/arrow_test.go | 95 +++++ pkg/udf/python/gateway.go | 294 +++++++++++++- pkg/udf/python/gateway_benchmark_test.go | 58 ++- pkg/udf/python/gateway_integration_test.go | 49 +++ pkg/udf/python/worker/test_worker.py | 136 ++++++- pkg/udf/python/worker/worker.py | 362 ++++++++++++++++-- pkg/udf/runtime.go | 51 +++ .../cases/udf_python/udf_python.sql | 13 + 11 files changed, 1431 insertions(+), 151 deletions(-) diff --git a/pkg/frontend/authenticate.go b/pkg/frontend/authenticate.go index 5b354b10fa90a..49838c9dab995 100644 --- a/pkg/frontend/authenticate.go +++ b/pkg/frontend/authenticate.go @@ -11686,9 +11686,63 @@ func ensurePythonUdfRuntimeReady(ctx context.Context, ses *Session) error { if !ok { return moerr.NewNotSupportedNoCtx("Python UDF runtime does not expose the current contract") } + if _, ok := pu.UdfService.(udf.RuntimeDefinitionValidator); !ok { + return moerr.NewNotSupportedNoCtx("Python UDF runtime does not expose definition validation") + } return readiness.CheckLanguageReady(ctx, udf.LanguagePython) } +// validatePythonUdfDefinitionAtCreate runs after the immutable artifact has +// been published and before the Catalog transaction writes the new identity +// or revision. The source is transient input to the validator; the Gateway +// resolves the same account-scoped digest before sending it to the worker. +func validatePythonUdfDefinitionAtCreate( + ctx context.Context, + ses *Session, + tenant *TenantInfo, + body function.PythonRoutineBody, +) error { + pu := getPuIfPresent(ses.GetService()) + if pu == nil || pu.UdfService == nil { + return moerr.NewNotSupportedNoCtx("Python UDF feature is disabled") + } + validator, ok := pu.UdfService.(udf.RuntimeDefinitionValidator) + if !ok { + return moerr.NewNotSupportedNoCtx("Python UDF runtime does not expose definition validation") + } + if tenant == nil { + return moerr.NewInvalidInputNoCtx("Python UDF definition validation requires an account") + } + if err := body.Validate(); err != nil { + return moerr.NewInvalidInputNoCtxf("Python UDF definition rejected before publication: %v", err) + } + definitionFingerprint, err := function.PythonRoutineBodyFingerprint(body) + if err != nil { + return moerr.NewInvalidInputNoCtxf("Python UDF definition fingerprint cannot be computed: %v", err) + } + args := make([]types.Type, len(body.ArgTypes)) + for index, descriptor := range body.ArgTypes { + args[index] = descriptor.Type() + } + return validator.ValidateDefinition(ctx, &udf.RoutineDefinition{ + Language: udf.LanguagePython, + AccountID: uint64(tenant.GetTenantID()), + Handler: body.Handler, + Source: body.Source, + Args: args, + ReturnType: body.ReturnType.Type(), + Mode: body.Mode, + NullPolicy: body.NullPolicy, + ABIContract: body.ABIContract, + AdapterVersion: body.AdapterVersion, + SDKVersion: body.SDKVersion, + DefinitionSchemaVersion: body.DefinitionSchemaVersion, + ArtifactDigest: body.ArtifactDigest, + EnvironmentDigest: body.EnvironmentDigest, + DefinitionFingerprint: definitionFingerprint, + }) +} + // canonicalPythonInputDescriptor is the shared overload key used by the // catalog lookup and by the immutable revision metadata. Keeping this // conversion at the create boundary prevents the logical SQL OID list from @@ -11702,6 +11756,7 @@ func canonicalPythonInputDescriptor( } func InitFunction(ses *Session, execCtx *ExecCtx, tenant *TenantInfo, cf *tree.CreateFunction) (err error) { + isPython := strings.EqualFold(cf.Language, string(tree.PYTHON)) var retTypeStr string var dbName string var dbExists bool @@ -11715,7 +11770,7 @@ func InitFunction(ses *Session, execCtx *ExecCtx, tenant *TenantInfo, cf *tree.C var erArray []ExecResult var pythonArgTypes []function.PythonTypeDescriptor var pythonReturnType *function.PythonTypeDescriptor - if strings.EqualFold(cf.Language, string(tree.PYTHON)) { + if isPython { if err := ensurePythonUdfRuntimeReady(execCtx.reqCtx, ses); err != nil { return err } @@ -11731,44 +11786,17 @@ func InitFunction(ses *Session, execCtx *ExecCtx, tenant *TenantInfo, cf *tree.C dbName = string(cf.Name.Name.SchemaName) } - // Exact function signatures need an exclusion boundary in every deployment - // mode. The catalog unique index documents the identity, but optimistic/SI - // transactions do not acquire FOR UPDATE locks. This private transaction is - // therefore explicitly pessimistic RC, locks the target database row, then - // rechecks and writes the signature before releasing that lock. - bh := ses.GetBackgroundExec(execCtx.reqCtx, &BackgroundExecOption{forcePessimisticRC: true}) - defer bh.Close() - - err = bh.Exec(execCtx.reqCtx, "begin;") - if err != nil { - return err - } - defer func() { - err = finishTxn(execCtx.reqCtx, bh, err) - }() - if cf.Language == string(tree.PYTHON) { - if err = ensurePythonUdfCatalogReady(execCtx.reqCtx, bh); err != nil { - return err - } - } - - // Lock and authenticate the target database before the exact-signature - // read. The lock stays held through persistence and commit. - dbExists, err = lockDatabaseForUDFCreation(execCtx.reqCtx, bh, dbName) - if err != nil { - return err - } - if !dbExists { - return moerr.NewBadDB(execCtx.reqCtx, dbName) - } - - // format return type + // Parse the complete typed definition before opening the Catalog + // transaction. Python validation performs a network round trip to the + // worker; holding the database lock while waiting would make a slow or + // unavailable worker block unrelated DDL. The later transaction still + // locks and rechecks the exact identity before publication. fmtctx = tree.NewFmtCtx(dialect.MYSQL, tree.WithQuoteString(true)) retTypeStr, err = plan2.GetFunctionTypeStrFromAst(cf.ReturnType.Type) if err != nil { return err } - if cf.Language == string(tree.PYTHON) { + if isPython { returnType, typeErr := plan2.GetFunctionTypeFromAst(cf.ReturnType.Type) if typeErr != nil { return typeErr @@ -11794,7 +11822,7 @@ func InitFunction(ses *Session, execCtx *ExecCtx, tenant *TenantInfo, cf *tree.C } argList[i].Type = typ typeList[i] = typ - if cf.Language == string(tree.PYTHON) { + if isPython { argType, typeErr := plan2.GetFunctionTypeFromAst(cf.Args[i].(*tree.FunctionArgDecl).Type) if typeErr != nil { return typeErr @@ -11816,42 +11844,15 @@ func InitFunction(ses *Session, execCtx *ExecCtx, tenant *TenantInfo, cf *tree.C return err } logicalArgTypes = argTypes - if cf.Language == string(tree.PYTHON) { + if isPython { exactArgTypes, err = canonicalPythonInputDescriptor(pythonArgTypes, pythonReturnType) if err != nil { return err } } - // validate duplicate function declaration - bh.ClearExecResultSet() - if cf.Language == string(tree.PYTHON) { - checkExistence = fmt.Sprintf(checkPythonUdfExistence, string(cf.Name.Name.ObjectName), dbName) - } else { - checkExistence = fmt.Sprintf(checkUdfExistence, string(cf.Name.Name.ObjectName), dbName) - } - err = bh.Exec(execCtx.reqCtx, checkExistence) - if err != nil { - return err - } - - erArray, err = getResultSet(execCtx.reqCtx, bh) - if err != nil { - return err - } - - matchedIDs, err := matchUserDefinedFunctionCandidates(execCtx.reqCtx, erArray, string(cf.Language), logicalArgTypes, exactArgTypes) - if err != nil { - return err - } - if len(matchedIDs) > 1 { - return moerr.NewInvalidInputNoCtxf("function %s has multiple catalog identities for the same signature", string(cf.Name.Name.ObjectName)) - } - if len(matchedIDs) == 1 && !cf.Replace { - return moerr.NewUDFAlreadyExistsNoCtx(string(cf.Name.Name.ObjectName)) - } var body string - if cf.Language == string(tree.SQL) { + if !isPython { body = cf.Body } else { if cf.Import { @@ -11893,22 +11894,11 @@ func InitFunction(ses *Session, execCtx *ExecCtx, tenant *TenantInfo, cf *tree.C // Using strconv.Quote here would preserve backslashes before JSON quotes // in storage and break json.Unmarshal when invoking python UDFs. body = string(byt) - } - definition := userDefinedFunctionDefinition{ - name: string(cf.Name.Name.ObjectName), - args: string(argsJson), - argTypes: argTypes, - retType: retTypeStr, - body: body, - lang: cf.Language, - sqlMode: sessionSQLModeForParser(ses), - dbName: dbName, - } - if strings.EqualFold(cf.Language, string(tree.PYTHON)) { - // The catalog revision stores the source for restore/inspection, while - // the executable plan carries only its immutable digest. Publish the - // account-scoped object before the catalog row becomes visible; a failed - // transaction can leave only an unreferenced object for later GC. + + // Publish and validate the exact immutable artifact before opening the + // Catalog transaction. A failed CREATE/REPLACE therefore cannot expose + // a revision whose source the current worker has not accepted, while a + // failed replacement leaves its previous active revision untouched. artifactStore, storeErr := pythonudf.NewFileArtifactStore( getPu(ses.GetService()).FileService, pythonudf.DefaultMaxArtifactBytes, @@ -11924,6 +11914,78 @@ func InitFunction(ses *Session, execCtx *ExecCtx, tenant *TenantInfo, cf *tree.C ); err != nil { return err } + if err = validatePythonUdfDefinitionAtCreate(execCtx.reqCtx, ses, tenant, nb); err != nil { + return err + } + } + definition := userDefinedFunctionDefinition{ + name: string(cf.Name.Name.ObjectName), + args: string(argsJson), + argTypes: argTypes, + retType: retTypeStr, + body: body, + lang: cf.Language, + sqlMode: sessionSQLModeForParser(ses), + dbName: dbName, + } + + // Exact function signatures need an exclusion boundary in every deployment + // mode. The catalog unique index documents the identity, but optimistic/SI + // transactions do not acquire FOR UPDATE locks. This private transaction is + // therefore explicitly pessimistic RC, locks the target database row, then + // rechecks and writes the signature before releasing that lock. + bh := ses.GetBackgroundExec(execCtx.reqCtx, &BackgroundExecOption{forcePessimisticRC: true}) + defer bh.Close() + + err = bh.Exec(execCtx.reqCtx, "begin;") + if err != nil { + return err + } + defer func() { + err = finishTxn(execCtx.reqCtx, bh, err) + }() + if isPython { + if err = ensurePythonUdfCatalogReady(execCtx.reqCtx, bh); err != nil { + return err + } + } + + // Lock and authenticate the target database before the exact-signature + // read. The lock stays held through persistence and commit. + dbExists, err = lockDatabaseForUDFCreation(execCtx.reqCtx, bh, dbName) + if err != nil { + return err + } + if !dbExists { + return moerr.NewBadDB(execCtx.reqCtx, dbName) + } + + // Validate duplicate function declaration under the same lock used for + // persistence. Python source validation above is intentionally outside this + // transaction, but identity/revision publication remains linearized here. + bh.ClearExecResultSet() + if isPython { + checkExistence = fmt.Sprintf(checkPythonUdfExistence, string(cf.Name.Name.ObjectName), dbName) + } else { + checkExistence = fmt.Sprintf(checkUdfExistence, string(cf.Name.Name.ObjectName), dbName) + } + err = bh.Exec(execCtx.reqCtx, checkExistence) + if err != nil { + return err + } + erArray, err = getResultSet(execCtx.reqCtx, bh) + if err != nil { + return err + } + matchedIDs, err := matchUserDefinedFunctionCandidates(execCtx.reqCtx, erArray, string(cf.Language), logicalArgTypes, exactArgTypes) + if err != nil { + return err + } + if len(matchedIDs) > 1 { + return moerr.NewInvalidInputNoCtxf("function %s has multiple catalog identities for the same signature", string(cf.Name.Name.ObjectName)) + } + if len(matchedIDs) == 1 && !cf.Replace { + return moerr.NewUDFAlreadyExistsNoCtx(string(cf.Name.Name.ObjectName)) } if len(matchedIDs) == 1 { // replace id := matchedIDs[0] diff --git a/pkg/frontend/python_udf_catalog_test.go b/pkg/frontend/python_udf_catalog_test.go index 6d7c9fa549789..be7771ec4537b 100644 --- a/pkg/frontend/python_udf_catalog_test.go +++ b/pkg/frontend/python_udf_catalog_test.go @@ -14,8 +14,10 @@ import ( "testing" "github.com/matrixorigin/matrixone/pkg/common/moerr" + "github.com/matrixorigin/matrixone/pkg/common/mpool" "github.com/matrixorigin/matrixone/pkg/config" "github.com/matrixorigin/matrixone/pkg/container/types" + "github.com/matrixorigin/matrixone/pkg/container/vector" "github.com/matrixorigin/matrixone/pkg/defines" "github.com/matrixorigin/matrixone/pkg/fileservice" "github.com/matrixorigin/matrixone/pkg/sql/parsers" @@ -27,6 +29,66 @@ import ( "github.com/stretchr/testify/require" ) +type definitionValidationRuntime struct { + definition *udf.RoutineDefinition + err error +} + +func (r *definitionValidationRuntime) Language() string { return udf.LanguagePython } + +func (r *definitionValidationRuntime) Execute( + context.Context, + *udf.Invocation, + vector.FunctionResultWrapper, + *mpool.MPool, +) error { + return nil +} + +func (r *definitionValidationRuntime) CheckLanguageReady(context.Context, string) error { + return nil +} + +func (r *definitionValidationRuntime) ValidateDefinition( + _ context.Context, + definition *udf.RoutineDefinition, +) error { + r.definition = definition + return r.err +} + +func TestValidatePythonUdfDefinitionAtCreateUsesTypedWorkerGate(t *testing.T) { + const service = "python-definition-validation-test" + InitServerLevelVars(service) + previous := getPuIfPresent(service) + t.Cleanup(func() { setPu(service, previous) }) + + runtime := &definitionValidationRuntime{err: fmt.Errorf("USER_CODE: Python syntax error at line 1, column 22")} + pu := config.NewParameterUnit(&config.FrontendParameters{}, nil, nil, nil) + pu.UdfService = runtime + setPu(service, pu) + + source := "def f(ctx, value): return value" + body, err := function.DecodePythonRoutineBody(pythonCatalogTestBody(t, types.T_int64)) + require.NoError(t, err) + body.Source = source + body.ArtifactDigest = udf.PythonInlineArtifactDigest(body.Handler, source) + body.EnvironmentDigest, err = udf.PythonEnvironmentDigest() + require.NoError(t, err) + + ses := &Session{feSessionImpl: feSessionImpl{service: service}} + tenant := &TenantInfo{TenantID: 27} + err = validatePythonUdfDefinitionAtCreate(context.Background(), ses, tenant, body) + require.ErrorContains(t, err, "Python syntax error") + require.NotNil(t, runtime.definition) + require.Equal(t, uint64(27), runtime.definition.AccountID) + require.Equal(t, body.Handler, runtime.definition.Handler) + require.Equal(t, body.Source, runtime.definition.Source) + require.Equal(t, body.ArtifactDigest, runtime.definition.ArtifactDigest) + require.Equal(t, body.EnvironmentDigest, runtime.definition.EnvironmentDigest) + require.Equal(t, types.T_int64.ToType(), runtime.definition.ReturnType) +} + func TestPersistPythonReplaceRejectsLegacyHeadBeforeWriting(t *testing.T) { ctx := context.Background() functionID := int64(41) diff --git a/pkg/udf/python/arrow.go b/pkg/udf/python/arrow.go index 38e48df652361..1d80e00bd67a7 100644 --- a/pkg/udf/python/arrow.go +++ b/pkg/udf/python/arrow.go @@ -381,6 +381,14 @@ type flightFrameCollector struct { total int64 } +func (c *flightFrameCollector) reset(maxBytes, retainedBytes int64) { + // Do not reuse the frame slice backing array. A successful candidate can be + // retained by the size search while the next candidate is encoded. + c.frames = nil + c.maxBytes = maxBytes + c.total = retainedBytes +} + func (c *flightFrameCollector) Send(data *flight.FlightData) error { if data == nil || len(data.DataHeader) == 0 { return fmt.Errorf("invalid Arrow Flight frame") @@ -398,43 +406,251 @@ func (c *flightFrameCollector) Send(data *flight.FlightData) error { return nil } +// inputBatchWireEncoder keeps one Arrow IPC writer alive for an invocation. +// The writer emits the schema once; later candidates and batches only encode a +// record message. The cached schema is still counted in every candidate's +// limit, because MaxBatchBytes describes the complete Flight payload that the +// receiver would have to admit for that batch. +type inputBatchWireEncoder struct { + schema *arrow.Schema + collector *flightFrameCollector + writer *flight.Writer + schemaFrame ArrowFrame + schemaBytes int64 + schemaReady bool + closed bool +} + +func (e *inputBatchWireEncoder) encode(record arrow.RecordBatch, maxBytes int64) ([]ArrowFrame, error) { + if e == nil || e.closed || record == nil || maxBytes <= 0 { + return nil, fmt.Errorf("invalid Arrow record batch encoder") + } + if e.schema == nil || !record.Schema().Equal(e.schema) { + return nil, fmt.Errorf("Arrow record batch schema does not match the invocation schema") + } + if e.writer == nil { + e.collector = &flightFrameCollector{} + e.writer = flight.NewRecordWriter( + e.collector, + ipc.WithSchema(e.schema), + ipc.WithAllocator(memory.NewGoAllocator()), + ) + } + writerHadSchema := e.schemaReady + e.collector.reset(maxBytes, e.schemaBytes) + if err := e.writer.Write(record); err != nil { + e.captureSchema() + return nil, fmt.Errorf("encode Arrow record batch: %w", err) + } + e.captureSchema() + if writerHadSchema { + if len(e.collector.frames) != 1 { + return nil, fmt.Errorf("Arrow writer did not emit one record frame") + } + return []ArrowFrame{e.schemaFrame, e.collector.frames[0]}, nil + } + if len(e.collector.frames) != 2 || !e.schemaReady { + return nil, fmt.Errorf("Arrow stream did not contain schema and record batch") + } + return append([]ArrowFrame(nil), e.collector.frames...), nil +} + +func (e *inputBatchWireEncoder) captureSchema() { + if e == nil || e.schemaReady || e.collector == nil || len(e.collector.frames) == 0 { + return + } + frame := e.collector.frames[0] + e.schemaFrame = ArrowFrame{ + Header: append([]byte(nil), frame.Header...), + Body: append([]byte(nil), frame.Body...), + } + e.schemaBytes = int64(len(e.schemaFrame.Header) + len(e.schemaFrame.Body)) + e.schemaReady = true +} + +func (e *inputBatchWireEncoder) close() error { + if e == nil || e.closed { + return nil + } + e.closed = true + if e.writer == nil { + return nil + } + if err := e.writer.Close(); err != nil { + return fmt.Errorf("close Arrow record batch encoder: %w", err) + } + return nil +} + func BuildInputRecord(inputs []*vector.Vector, args []types.Type, length int) (arrow.RecordBatch, *arrow.Schema, error) { return BuildInputRecordRange(inputs, args, 0, length) } func BuildInputRecordRange(inputs []*vector.Vector, args []types.Type, start, length int) (arrow.RecordBatch, *arrow.Schema, error) { - if start < 0 || length <= 0 || len(inputs) != len(args) { - return nil, nil, fmt.Errorf("invalid Python UDF input shape") + encoder, err := newInputBatchEncoder(inputs, args) + if err != nil { + return nil, nil, err + } + return encoder.build(start, length) +} + +// inputBatchEncoder owns the immutable descriptor/schema portion of the +// Gateway input boundary for one invocation. Building it once avoids +// re-creating TypeDescriptor, Field metadata, and the Arrow Schema for every +// physical batch and every size probe. The record builder and encoded bytes +// remain per batch, so the encoder does not retain input or output buffers. +type inputBatchEncoder struct { + inputs []*vector.Vector + args []types.Type + descriptors []TypeDescriptor + schema *arrow.Schema + fixedWidth bool + wire *inputBatchWireEncoder +} + +func newInputBatchEncoder(inputs []*vector.Vector, args []types.Type) (*inputBatchEncoder, error) { + if len(inputs) != len(args) { + return nil, fmt.Errorf("invalid Python UDF input shape") } fields := make([]arrow.Field, len(args)) + descriptors := make([]TypeDescriptor, len(args)) + fixedWidth := true for i, typ := range args { descriptor, err := NewTypeDescriptor(typ) if err != nil { - return nil, nil, err + return nil, err } + descriptors[i] = descriptor fields[i], err = descriptor.Field(fmt.Sprintf("arg_%d", i)) if err != nil { - return nil, nil, err + return nil, err } - if inputs[i] == nil || inputs[i].Length() == 0 || (!inputs[i].IsConst() && inputs[i].Length() < start+length) { - return nil, nil, fmt.Errorf("input column %d is shorter than batch range", i) + if typ.IsVarlen() { + fixedWidth = false + } + if inputs[i] == nil || inputs[i].Length() == 0 { + return nil, fmt.Errorf("input column %d is shorter than batch range", i) } } schema := arrow.NewSchema(fields, nil) - if len(fields) == 0 { - return array.NewRecordBatch(schema, nil, int64(length)), schema, nil + return &inputBatchEncoder{ + inputs: inputs, + args: args, + descriptors: descriptors, + schema: schema, + fixedWidth: fixedWidth, + }, nil +} + +func (e *inputBatchEncoder) encode(record arrow.RecordBatch, maxBytes int64) ([]ArrowFrame, error) { + if e == nil { + return nil, fmt.Errorf("invalid Python UDF input encoder") + } + if e.wire == nil { + e.wire = &inputBatchWireEncoder{schema: e.schema} } - builder := array.NewRecordBuilder(memory.NewGoAllocator(), schema) + return e.wire.encode(record, maxBytes) +} + +func (e *inputBatchEncoder) close() error { + if e == nil || e.wire == nil { + return nil + } + return e.wire.close() +} + +func (e *inputBatchEncoder) build(start, length int) (arrow.RecordBatch, *arrow.Schema, error) { + if e == nil || start < 0 || length <= 0 { + return nil, nil, fmt.Errorf("invalid Python UDF input shape") + } + for i, input := range e.inputs { + if !input.IsConst() && input.Length() < start+length { + return nil, nil, fmt.Errorf("input column %d is shorter than batch range", i) + } + } + if len(e.args) == 0 { + return array.NewRecordBatch(e.schema, nil, int64(length)), e.schema, nil + } + builder := array.NewRecordBuilder(memory.NewGoAllocator(), e.schema) defer builder.Release() - for column, typ := range args { + for column, typ := range e.args { for row := 0; row < length; row++ { - if err := appendInputValue(builder.Field(column), inputs[column], typ, start, row); err != nil { + if err := appendInputValue(builder.Field(column), e.inputs[column], typ, start, row); err != nil { return nil, nil, fmt.Errorf("input column %d row %d: %w", column, row, err) } } } - return builder.NewRecordBatch(), schema, nil + return builder.NewRecordBatch(), e.schema, nil +} + +// canBuildFullBatch cheaply decides whether a variable-width full candidate +// is worth materializing. It uses the source values already owned by the +// SQL vector and a deliberately inflated bound. If the bound is not clearly +// below the wire limit, the bounded probe path is retained so a very wide +// input cannot create an oversized temporary Arrow record just to discover +// that it does not fit. +func (e *inputBatchEncoder) canBuildFullBatch(start, length, maxBytes int64) bool { + if e == nil || e.fixedWidth || length <= 0 || maxBytes <= 0 { + return false + } + const safetyNumerator = int64(2) + const safetyDenominator = int64(1) + const perColumnOverhead = int64(4096) + const perRowOverhead = int64(16) + const maxEstimate = int64(^uint64(0) >> 1) + add := func(total, value int64) int64 { + if value < 0 || total > maxEstimate-value { + return maxEstimate + } + return total + value + } + multiply := func(left, right int64) int64 { + if left < 0 || right < 0 || (left != 0 && right > maxEstimate/left) { + return maxEstimate + } + return left * right + } + estimate := int64(128) + for column, typ := range e.args { + if !typ.IsVarlen() { + return false + } + descriptor := e.descriptors[column] + offsetWidth := int64(descriptor.OffsetWidth) + if offsetWidth == 0 { + offsetWidth = 32 + } + offsetWidth /= 8 + if offsetWidth != 4 && offsetWidth != 8 { + return false + } + estimate = add(estimate, perColumnOverhead) + estimate = add(estimate, multiply(add(length, 1), offsetWidth)) + estimate = add(estimate, add(length, 7)/8) + parameter := vector.GenerateFunctionStrParameter(e.inputs[column]) + for row := int64(0); row < length; row++ { + index := sourceRow(e.inputs[column], int(start), int(row)) + if e.inputs[column].IsNull(uint64(index)) { + continue + } + value, _ := parameter.GetStrValue(uint64(index)) + valueBytes := int64(len(value)) + // JSON canonicalization can add escaping and separators. The + // factor is intentionally conservative; a bound that is not + // clearly safe falls back to the ordinary bounded probes. + if typ.Oid == types.T_json { + valueBytes = add(valueBytes, valueBytes) + } + estimate = add(estimate, valueBytes) + estimate = add(estimate, perRowOverhead) + } + } + if estimate > maxEstimate/safetyNumerator { + return false + } + return estimate*safetyNumerator <= maxBytes*safetyDenominator } + func sourceRow(v *vector.Vector, start, row int) int { if v.IsConst() { return 0 diff --git a/pkg/udf/python/arrow_test.go b/pkg/udf/python/arrow_test.go index 0caea06d05b05..cf1f812fdcc88 100644 --- a/pkg/udf/python/arrow_test.go +++ b/pkg/udf/python/arrow_test.go @@ -145,6 +145,101 @@ func TestBuildInputRecordRejectsEmptyConstantVector(t *testing.T) { require.ErrorContains(t, err, "shorter than batch range") } +func TestInputBatchEncoderReusesSchemaAndPreservesRange(t *testing.T) { + mp := mpool.MustNewZeroNoFixed() + input := vector.NewVec(types.T_int64.ToType()) + defer func() { + input.Free(mp) + mpool.DeleteMPool(mp) + }() + for _, value := range []int64{10, 20, 30} { + require.NoError(t, vector.AppendFixed(input, value, false, mp)) + } + encoder, err := newInputBatchEncoder( + []*vector.Vector{input}, []types.Type{types.T_int64.ToType()}, + ) + require.NoError(t, err) + first, firstSchema, err := encoder.build(0, 2) + require.NoError(t, err) + defer first.Release() + second, secondSchema, err := encoder.build(2, 1) + require.NoError(t, err) + defer second.Release() + require.Same(t, firstSchema, secondSchema) + firstValues := first.Column(0).(*array.Int64) + secondValues := second.Column(0).(*array.Int64) + require.Equal(t, int64(10), firstValues.Value(0)) + require.Equal(t, int64(20), firstValues.Value(1)) + require.Equal(t, int64(30), secondValues.Value(0)) +} + +func TestInputBatchWireEncoderReusesSchemaAcrossBatches(t *testing.T) { + descriptor, err := NewTypeDescriptor(types.T_int64.ToType()) + require.NoError(t, err) + field, err := descriptor.Field("arg_0") + require.NoError(t, err) + schema := arrow.NewSchema([]arrow.Field{field}, nil) + makeRecord := func(value int64) arrow.RecordBatch { + builder := array.NewInt64Builder(memory.NewGoAllocator()) + builder.Append(value) + values := builder.NewInt64Array() + record := array.NewRecordBatch(schema, []arrow.Array{values}, 1) + values.Release() + return record + } + + encoder := &inputBatchWireEncoder{schema: schema} + first := makeRecord(1) + second := makeRecord(2) + defer first.Release() + defer second.Release() + defer func() { require.NoError(t, encoder.close()) }() + + firstFrames, err := encoder.encode(first, DefaultMaxBatchBytes) + require.NoError(t, err) + secondFrames, err := encoder.encode(second, DefaultMaxBatchBytes) + require.NoError(t, err) + require.Len(t, firstFrames, 2) + require.Len(t, secondFrames, 2) + require.Equal(t, firstFrames[0].Header, secondFrames[0].Header) + require.Equal(t, firstFrames[0].Body, secondFrames[0].Body) + require.NotEqual(t, firstFrames[1].Body, secondFrames[1].Body) +} + +func TestInputBatchWireEncoderRecoversAfterOversizeCandidate(t *testing.T) { + descriptor, err := NewTypeDescriptor(types.T_int64.ToType()) + require.NoError(t, err) + field, err := descriptor.Field("arg_0") + require.NoError(t, err) + schema := arrow.NewSchema([]arrow.Field{field}, nil) + makeRecord := func(values ...int64) arrow.RecordBatch { + builder := array.NewInt64Builder(memory.NewGoAllocator()) + builder.AppendValues(values, nil) + arrayValues := builder.NewInt64Array() + record := array.NewRecordBatch(schema, []arrow.Array{arrayValues}, int64(len(values))) + arrayValues.Release() + return record + } + + one := makeRecord(1) + defer one.Release() + measure := &inputBatchWireEncoder{schema: schema} + oneFrames, err := measure.encode(one, DefaultMaxBatchBytes) + require.NoError(t, err) + maxBytes := int64(len(oneFrames[0].Header) + len(oneFrames[0].Body) + len(oneFrames[1].Header) + len(oneFrames[1].Body)) + require.NoError(t, measure.close()) + + encoder := &inputBatchWireEncoder{schema: schema} + defer func() { require.NoError(t, encoder.close()) }() + wider := makeRecord(1, 2) + defer wider.Release() + _, err = encoder.encode(wider, maxBytes) + require.ErrorIs(t, err, errArrowBatchTooLarge) + frames, err := encoder.encode(one, maxBytes) + require.NoError(t, err) + require.Len(t, frames, 2) +} + func TestArrowValueDomainRejectsWidthAndTimeOverflow(t *testing.T) { allocator := memory.NewGoAllocator() stringBuilder := array.NewStringBuilder(allocator) diff --git a/pkg/udf/python/gateway.go b/pkg/udf/python/gateway.go index b4de40772d9ff..66edcb1fe3ba2 100644 --- a/pkg/udf/python/gateway.go +++ b/pkg/udf/python/gateway.go @@ -20,6 +20,7 @@ import ( "strings" "sync" "time" + "unicode/utf8" "github.com/apache/arrow-go/v18/arrow/flight/gen/flight" "github.com/matrixorigin/matrixone/pkg/common/mpool" @@ -53,6 +54,8 @@ const ( var errGatewayClosed = errors.New("python udf gateway is closed") +var _ udf.RuntimeDefinitionValidator = (*Gateway)(nil) + type Gateway struct { cfg ClientConfig artifactResolver ArtifactResolver @@ -171,6 +174,194 @@ func (g *Gateway) CheckLanguageReady(ctx context.Context, language string) error return g.ensureCapabilities(ctx, client) } +// ValidateDefinition asks the current worker to compile the exact artifact +// that a CREATE or REPLACE is about to publish. This action has no +// FunctionRef, invocation lease, handler slot, or ledger entry: a definition +// has not acquired a Catalog identity and must not consume execution state. +// The worker performs compile(source, ..., "exec") only; it does not execute +// top-level code, import dependencies, or call the handler. +func (g *Gateway) ValidateDefinition(ctx context.Context, definition *udf.RoutineDefinition) error { + g.mu.Lock() + closed := g.closed + g.mu.Unlock() + if closed { + return errGatewayClosed + } + if !g.cfg.Enabled { + return fmt.Errorf("RESOURCE_UNAVAILABLE: Python UDF runtime is disabled") + } + if !g.cfg.AllowUnisolated { + return fmt.Errorf("RESOURCE_UNAVAILABLE: Python UDF runtime requires explicit unisolated opt-in") + } + if ctx == nil { + ctx = context.Background() + } + if definition == nil { + return fmt.Errorf("UNSUPPORTED_ROUTINE_VERSION: nil Python routine definition") + } + if definition.Language != "" && definition.Language != udf.LanguagePython { + return fmt.Errorf("UNSUPPORTED_ROUTINE_VERSION: unsupported UDF language %q", definition.Language) + } + if strings.TrimSpace(definition.Handler) == "" { + return fmt.Errorf("UNSUPPORTED_ROUTINE_VERSION: Python handler is required") + } + if strings.Contains(definition.Handler, ":") { + return fmt.Errorf("UNSUPPORTED_ROUTINE_VERSION: Python external handler import requires the immutable artifact catalog") + } + if definition.DefinitionSchemaVersion != udf.PythonDefinitionSchemaVersion || + definition.ABIContract != udf.PythonABIContract || + definition.AdapterVersion != udf.PythonAdapterVersion || + definition.SDKVersion != udf.PythonSDKVersion { + return fmt.Errorf("UNSUPPORTED_ROUTINE_VERSION: Python definition contract is not supported") + } + if definition.Mode != ModeScalar && definition.Mode != ModeVector { + return fmt.Errorf("UNSUPPORTED_ROUTINE_VERSION: unsupported Python mode %q", definition.Mode) + } + if definition.NullPolicy != NullCallHandler && definition.NullPolicy != NullReturnNull { + return fmt.Errorf("UNSUPPORTED_ROUTINE_VERSION: unsupported Python NULL policy %q", definition.NullPolicy) + } + if !udf.IsSHA256Digest(definition.ArtifactDigest) || !udf.IsSHA256Digest(definition.EnvironmentDigest) { + return fmt.Errorf("UNSUPPORTED_ROUTINE_VERSION: Python definition digest is invalid") + } + currentEnvironment, err := udf.PythonEnvironmentDigest() + if err != nil || currentEnvironment != definition.EnvironmentDigest { + return fmt.Errorf("UNSUPPORTED_ROUTINE_VERSION: Python environment digest does not match the current contract") + } + if !udf.IsSHA256Digest(definition.DefinitionFingerprint) { + return fmt.Errorf("UNSUPPORTED_ROUTINE_VERSION: Python definition fingerprint is invalid") + } + args := make([]TypeDescriptor, len(definition.Args)) + for index, typ := range definition.Args { + args[index], err = NewTypeDescriptor(typ) + if err != nil { + return fmt.Errorf("UNSUPPORTED_ROUTINE_VERSION: Python argument descriptor %d: %w", index, err) + } + } + returnDescriptor, err := NewTypeDescriptor(definition.ReturnType) + if err != nil { + return fmt.Errorf("UNSUPPORTED_ROUTINE_VERSION: Python return descriptor: %w", err) + } + expectedFingerprint, err := DefinitionFingerprint( + definition.DefinitionSchemaVersion, + definition.Handler, definition.Mode, definition.NullPolicy, + definition.ABIContract, definition.AdapterVersion, + definition.ArtifactDigest, definition.EnvironmentDigest, definition.SDKVersion, + args, returnDescriptor, + ) + if err != nil { + return fmt.Errorf("UNSUPPORTED_ROUTINE_VERSION: Python definition fingerprint cannot be computed: %w", err) + } + if definition.DefinitionFingerprint != expectedFingerprint { + return fmt.Errorf("UNSUPPORTED_ROUTINE_VERSION: Python definition fingerprint does not match the typed definition") + } + + source := definition.Source + if g.artifactResolver != nil { + resolved, resolveErr := g.artifactResolver.Resolve( + ctx, definition.AccountID, definition.Handler, definition.ArtifactDigest, + ) + if resolveErr != nil { + return resolveErr + } + if source != "" && source != resolved { + return fmt.Errorf("UNSUPPORTED_ROUTINE_VERSION: Python artifact source does not match the immutable artifact") + } + source = resolved + } else if !g.allowInlineSource { + return fmt.Errorf("RESOURCE_UNAVAILABLE: Python artifact resolver is not configured") + } + if source == "" || !utf8.ValidString(source) { + return fmt.Errorf("UNSUPPORTED_ROUTINE_VERSION: Python artifact source is empty or not valid UTF-8") + } + if int64(len([]byte(source))) > DefaultMaxArtifactBytes { + return fmt.Errorf("RESOURCE_EXHAUSTED: Python artifact exceeds %d bytes", DefaultMaxArtifactBytes) + } + if udf.PythonInlineArtifactDigest(definition.Handler, source) != definition.ArtifactDigest { + return fmt.Errorf("UNSUPPORTED_ROUTINE_VERSION: Python artifact digest does not match the immutable source") + } + + client, err := g.flightClient() + if err != nil { + return err + } + if err := g.ensureCapabilities(ctx, client); err != nil { + return err + } + payload, err := json.Marshal(definitionValidationPayload{ + AccountID: definition.AccountID, + Handler: definition.Handler, + Source: source, + Mode: definition.Mode, + NullPolicy: definition.NullPolicy, + ABIContract: definition.ABIContract, + AdapterVersion: definition.AdapterVersion, + SDKVersion: definition.SDKVersion, + DefinitionSchemaVersion: definition.DefinitionSchemaVersion, + ArtifactDigest: definition.ArtifactDigest, + EnvironmentDigest: definition.EnvironmentDigest, + DefinitionFingerprint: definition.DefinitionFingerprint, + Args: args, + Return: returnDescriptor, + }) + if err != nil { + return fmt.Errorf("python udf: encode definition validation: %w", err) + } + actionCtx, cancel := context.WithTimeout(ctx, g.cfg.RequestTimeout) + defer cancel() + stream, err := client.DoAction(actionCtx, &flight.Action{Type: "ValidatePythonDefinition", Body: payload}) + if err != nil { + g.invalidateCapabilities() + return fmt.Errorf("python udf: definition validation action: %w", err) + } + result, err := stream.Recv() + if err != nil { + g.invalidateCapabilities() + return fmt.Errorf("python udf: definition validation response: %w", err) + } + if result == nil { + g.invalidateCapabilities() + return fmt.Errorf("python udf: empty definition validation response") + } + var response definitionValidationResponse + decoder := json.NewDecoder(bytes.NewReader(result.Body)) + decoder.DisallowUnknownFields() + if err := decoder.Decode(&response); err != nil { + g.invalidateCapabilities() + return fmt.Errorf("python udf: decode definition validation response: %w", err) + } + var trailing any + if err := decoder.Decode(&trailing); err != io.EOF { + g.invalidateCapabilities() + return fmt.Errorf("python udf: definition validation response has trailing JSON") + } + if response.Status == "ERROR" { + if _, err := stream.Recv(); err != io.EOF { + g.invalidateCapabilities() + if err == nil { + return fmt.Errorf("python udf: definition validation returned multiple results") + } + return fmt.Errorf("python udf: definition validation stream: %w", err) + } + if response.Reason == "" { + return fmt.Errorf("USER_CODE: Python definition validation failed") + } + return errors.New(response.Reason) + } + if response.Status != statusOK || response.ArtifactDigest != definition.ArtifactDigest || + response.DefinitionFingerprint != definition.DefinitionFingerprint { + g.invalidateCapabilities() + return fmt.Errorf("UNSUPPORTED_ROUTINE_VERSION: Python definition validation returned an invalid result") + } + if _, err := stream.Recv(); err != io.EOF { + g.invalidateCapabilities() + if err == nil { + return fmt.Errorf("python udf: definition validation returned multiple results") + } + return fmt.Errorf("python udf: definition validation stream: %w", err) + } + return nil +} + func (g *Gateway) connect() error { _, err := g.flightClient() return err @@ -320,6 +511,23 @@ func (g *Gateway) Execute(ctx context.Context, invocation *udf.Invocation, resul if execution.Length == 0 { return result.PreExtendAndReset(0) } + inputEncoder, err := newInputBatchEncoder(execution.Inputs, execution.Args) + if err != nil { + return err + } + inputEncoderClosed := false + closeInputEncoder := func() error { + if inputEncoderClosed { + return nil + } + inputEncoderClosed = true + return inputEncoder.close() + } + defer func() { + if closeErr := closeInputEncoder(); err == nil && closeErr != nil { + err = closeErr + } + }() client, err := g.flightClient() if err != nil { return err @@ -410,7 +618,7 @@ func (g *Gateway) Execute(ctx context.Context, invocation *udf.Invocation, resul var lastSequence uint64 var inputEnded bool for start < int64(execution.Length) { - batch, err := encodeInputBatch(execution.Inputs, execution.Args, start, int64(execution.Length)-start, g.cfg.MaxBatchBytes, g.cfg.MaxBatchRows) + batch, err := encodeInputBatchWithEncoder(inputEncoder, start, int64(execution.Length)-start, g.cfg.MaxBatchBytes, g.cfg.MaxBatchRows) if err != nil { return err } @@ -471,7 +679,10 @@ func (g *Gateway) Execute(ctx context.Context, invocation *udf.Invocation, resul if !inputEnded || lastSequence != batchIndex { return fmt.Errorf("python udf: input stream did not reach EndInput") } - return g.receiveFinish(streamCtx, client, stream, execution.Tuple, execution.Length, rows, &sequence) + if err := g.receiveFinish(streamCtx, client, stream, execution.Tuple, execution.Length, rows, &sequence); err != nil { + return err + } + return closeInputEncoder() } // invocationAdmission is the real Gateway owner for the CN-side execution @@ -770,6 +981,33 @@ type capabilityRequest struct { ProtocolVersion int `json:"protocol_version"` } +// definitionValidationPayload is a create-time action payload. It is not a +// plan or an invocation envelope: it carries the source only after the +// Gateway has resolved the exact account-scoped artifact digest. +type definitionValidationPayload struct { + AccountID uint64 `json:"account_id"` + Handler string `json:"handler"` + Source string `json:"source"` + Mode string `json:"mode"` + NullPolicy string `json:"null_policy"` + ABIContract string `json:"abi_contract"` + AdapterVersion string `json:"adapter_version"` + SDKVersion string `json:"sdk_version"` + DefinitionSchemaVersion int `json:"definition_schema_version"` + ArtifactDigest string `json:"artifact_digest"` + EnvironmentDigest string `json:"environment_digest"` + DefinitionFingerprint string `json:"definition_fingerprint"` + Args []TypeDescriptor `json:"args"` + Return TypeDescriptor `json:"return"` +} + +type definitionValidationResponse struct { + Status string `json:"status"` + Reason string `json:"reason,omitempty"` + ArtifactDigest string `json:"artifact_digest,omitempty"` + DefinitionFingerprint string `json:"definition_fingerprint,omitempty"` +} + type capabilityResponse struct { ProtocolVersion int `json:"protocol_version"` ABIContract string `json:"abi_contract"` @@ -919,6 +1157,14 @@ func sameStrings(actual, expected []string) bool { } func encodeInputBatch(inputs []*vector.Vector, args []types.Type, start, remaining, maxBytes, maxRows int64) (encodedRecordBatch, error) { + encoder, err := newInputBatchEncoder(inputs, args) + if err != nil { + return encodedRecordBatch{}, err + } + return encodeInputBatchWithEncoder(encoder, start, remaining, maxBytes, maxRows) +} + +func encodeInputBatchWithEncoder(encoder *inputBatchEncoder, start, remaining, maxBytes, maxRows int64) (encodedRecordBatch, error) { if start < 0 || remaining <= 0 { return encodedRecordBatch{}, fmt.Errorf("invalid Python UDF input batch range") } @@ -933,8 +1179,11 @@ func encodeInputBatch(inputs []*vector.Vector, args []types.Type, start, remaini // work without assuming anything about variable-length SQL values. The // slice keeps the same backing buffers, and every candidate is still encoded // and checked against maxBytes before publication. - if fixedWidthInputTypes(args) { - record, _, err := BuildInputRecordRange(inputs, args, int(start), int(remaining)) + if encoder == nil { + return encodedRecordBatch{}, fmt.Errorf("invalid Python UDF input encoder") + } + if encoder.fixedWidth { + record, _, err := encoder.build(int(start), int(remaining)) if err != nil { return encodedRecordBatch{}, err } @@ -942,32 +1191,43 @@ func encodeInputBatch(inputs []*vector.Vector, args []types.Type, start, remaini tryEncode := func(rows int64) ([]ArrowFrame, error) { candidate := record.NewSlice(0, rows) defer candidate.Release() - return EncodeRecordBatch(candidate, maxBytes) + return encoder.encode(candidate, maxBytes) } - return chooseInputBatch(remaining, maxBytes, tryEncode) + // The full fixed-width record is already materialized under the + // invocation's bounded input range. Try publishing it once before the + // size search; the normal SQL batch fits in the limit and avoids a + // logarithmic series of identical IPC encodes. + return chooseInputBatchWithFullCandidate(remaining, maxBytes, tryEncode, true) } tryEncode := func(rows int64) ([]ArrowFrame, error) { - record, _, err := BuildInputRecordRange(inputs, args, int(start), int(rows)) + record, _, err := encoder.build(int(start), int(rows)) if err != nil { return nil, err } - frames, encodeErr := EncodeRecordBatch(record, maxBytes) + frames, encodeErr := encoder.encode(record, maxBytes) record.Release() return frames, encodeErr } - return chooseInputBatch(remaining, maxBytes, tryEncode) + return chooseInputBatchWithFullCandidate( + remaining, maxBytes, tryEncode, + encoder.canBuildFullBatch(start, remaining, maxBytes), + ) } -func fixedWidthInputTypes(args []types.Type) bool { - for _, typ := range args { - if typ.IsVarlen() { - return false - } - } - return true +func chooseInputBatch(remaining, maxBytes int64, tryEncode func(int64) ([]ArrowFrame, error)) (encodedRecordBatch, error) { + return chooseInputBatchWithFullCandidate(remaining, maxBytes, tryEncode, false) } -func chooseInputBatch(remaining, maxBytes int64, tryEncode func(int64) ([]ArrowFrame, error)) (encodedRecordBatch, error) { +func chooseInputBatchWithFullCandidate(remaining, maxBytes int64, tryEncode func(int64) ([]ArrowFrame, error), tryFull bool) (encodedRecordBatch, error) { + if tryFull { + frames, err := tryEncode(remaining) + if err == nil { + return encodedRecordBatch{Frames: frames, Rows: remaining}, nil + } + if !errors.Is(err, errArrowBatchTooLarge) { + return encodedRecordBatch{}, err + } + } best, probe := int64(0), int64(1) var bestFrames []ArrowFrame failedAt := int64(0) diff --git a/pkg/udf/python/gateway_benchmark_test.go b/pkg/udf/python/gateway_benchmark_test.go index 2ab7f7d49dc40..bd95a5bc1a6f3 100644 --- a/pkg/udf/python/gateway_benchmark_test.go +++ b/pkg/udf/python/gateway_benchmark_test.go @@ -47,13 +47,18 @@ func BenchmarkEncodeInputBatchFixedWidthRebuildBaseline(b *testing.B) { input.Free(mp) mpool.DeleteMPool(mp) }() + encoder, err := newInputBatchEncoder([]*vector.Vector{input}, []types.Type{types.T_int64.ToType()}) + if err != nil { + b.Fatal(err) + } + defer func() { _ = encoder.close() }() b.ReportAllocs() b.SetBytes(int64(rows)) b.ResetTimer() for i := 0; i < b.N; i++ { batch, err := encodeInputBatchRebuildBaseline( - []*vector.Vector{input}, []types.Type{types.T_int64.ToType()}, + encoder, 0, rows, DefaultMaxBatchBytes, rows, ) if err != nil { @@ -69,6 +74,37 @@ func BenchmarkEncodeInputBatchVariableWidth(b *testing.B) { benchmarkEncodeInputBatch(b, types.New(types.T_varchar, 64, 0)) } +func BenchmarkEncodeInputBatchVariableWidthRebuildBaseline(b *testing.B) { + const rows = 8192 + typ := types.New(types.T_varchar, 64, 0) + input, mp := benchmarkInputVector(b, typ, rows) + defer func() { + input.Free(mp) + mpool.DeleteMPool(mp) + }() + encoder, err := newInputBatchEncoder([]*vector.Vector{input}, []types.Type{typ}) + if err != nil { + b.Fatal(err) + } + defer func() { _ = encoder.close() }() + + b.ReportAllocs() + b.SetBytes(int64(rows)) + b.ResetTimer() + for i := 0; i < b.N; i++ { + batch, err := encodeInputBatchRebuildBaseline( + encoder, + 0, rows, DefaultMaxBatchBytes, rows, + ) + if err != nil { + b.Fatal(err) + } + if batch.Rows != rows || len(batch.Frames) != 2 { + b.Fatalf("unexpected encoded batch rows=%d frames=%d", batch.Rows, len(batch.Frames)) + } + } +} + func benchmarkEncodeInputBatch(b *testing.B, typ types.Type) { const rows = 8192 input, mp := benchmarkInputVector(b, typ, rows) @@ -76,13 +112,18 @@ func benchmarkEncodeInputBatch(b *testing.B, typ types.Type) { input.Free(mp) mpool.DeleteMPool(mp) }() + encoder, err := newInputBatchEncoder([]*vector.Vector{input}, []types.Type{typ}) + if err != nil { + b.Fatal(err) + } + defer func() { _ = encoder.close() }() b.ReportAllocs() b.SetBytes(int64(rows)) b.ResetTimer() for i := 0; i < b.N; i++ { - batch, err := encodeInputBatch( - []*vector.Vector{input}, []types.Type{typ}, 0, rows, + batch, err := encodeInputBatchWithEncoder( + encoder, 0, rows, DefaultMaxBatchBytes, rows, ) if err != nil { @@ -96,21 +137,22 @@ func benchmarkEncodeInputBatch(b *testing.B, typ types.Type) { // encodeInputBatchRebuildBaseline is a benchmark-only reference for the // pre-optimization path. It intentionally rebuilds the Arrow record for every -// size probe, while production encodeInputBatch slices one fixed-width record. +// size probe while sharing the reusable Flight writer with production. This +// measures input record construction and probe avoidance without counting +// repeated schema serialization as a second optimization. func encodeInputBatchRebuildBaseline( - inputs []*vector.Vector, - args []types.Type, + encoder *inputBatchEncoder, start, remaining, maxBytes, maxRows int64, ) (encodedRecordBatch, error) { if remaining > maxRows { remaining = maxRows } tryEncode := func(rows int64) ([]ArrowFrame, error) { - record, _, err := BuildInputRecordRange(inputs, args, int(start), int(rows)) + record, _, err := BuildInputRecordRange(encoder.inputs, encoder.args, int(start), int(rows)) if err != nil { return nil, err } - frames, encodeErr := EncodeRecordBatch(record, maxBytes) + frames, encodeErr := encoder.encode(record, maxBytes) record.Release() return frames, encodeErr } diff --git a/pkg/udf/python/gateway_integration_test.go b/pkg/udf/python/gateway_integration_test.go index 5e78aaf52da88..95d46fe22d8db 100644 --- a/pkg/udf/python/gateway_integration_test.go +++ b/pkg/udf/python/gateway_integration_test.go @@ -70,6 +70,36 @@ func TestGatewayExecutesAgainstRealPythonWorker(t *testing.T) { require.NoError(t, gateway.CheckLanguageReady(readyContext, udf.LanguagePython)) cancel() + t.Run("definition validation", func(t *testing.T) { + marker := filepath.Join(t.TempDir(), "executed") + source := fmt.Sprintf( + "from pathlib import Path\nPath(%q).write_text('executed')\ndef add(ctx, value): return value\n", + marker, + ) + valid := integrationInvocationForArgs( + ModeScalar, "add", source, + []types.Type{types.T_int64.ToType()}, nil, 0, + protocol.FencingTuple{AccountID: 1, StatementID: "definition-validation", GroupID: "definition-validation-group", GroupEpoch: 1, InvocationID: "valid", LeaseEpoch: 1}, + ) + publishIntegrationArtifact(t, artifactStore, valid) + definition := routineDefinitionForIntegration(valid) + validationContext, cancel := context.WithTimeout(context.Background(), 5*time.Second) + require.NoError(t, gateway.ValidateDefinition(validationContext, definition)) + cancel() + require.NoFileExists(t, marker, "definition validation executed top-level Python code") + + invalid := integrationInvocationForArgs( + ModeScalar, "add", "def add(ctx, value) return value", + []types.Type{types.T_int64.ToType()}, nil, 0, + protocol.FencingTuple{AccountID: 1, StatementID: "definition-validation-invalid", GroupID: "definition-validation-invalid-group", GroupEpoch: 1, InvocationID: "invalid", LeaseEpoch: 1}, + ) + publishIntegrationArtifact(t, artifactStore, invalid) + validationContext, cancel = context.WithTimeout(context.Background(), 5*time.Second) + err := gateway.ValidateDefinition(validationContext, routineDefinitionForIntegration(invalid)) + cancel() + require.ErrorContains(t, err, "USER_CODE: Python syntax error") + }) + t.Run("scalar", func(t *testing.T) { input, mp := integrationInput(t, []int64{1, 2, 3}) defer func() { @@ -629,3 +659,22 @@ func integrationInvocationForArgs( Tuple: tuple, } } + +func routineDefinitionForIntegration(invocation *udf.Invocation) *udf.RoutineDefinition { + return &udf.RoutineDefinition{ + Language: udf.LanguagePython, + AccountID: invocation.FunctionRef.AccountID, + Handler: invocation.Handler, + Args: append([]types.Type(nil), invocation.Args...), + ReturnType: invocation.ReturnType, + Mode: invocation.Mode, + NullPolicy: invocation.NullPolicy, + ABIContract: invocation.ABIContract, + AdapterVersion: invocation.AdapterVersion, + SDKVersion: invocation.SDKVersion, + DefinitionSchemaVersion: invocation.DefinitionSchemaVersion, + ArtifactDigest: invocation.ArtifactDigest, + EnvironmentDigest: invocation.EnvironmentDigest, + DefinitionFingerprint: invocation.DefinitionFingerprint, + } +} diff --git a/pkg/udf/python/worker/test_worker.py b/pkg/udf/python/worker/test_worker.py index c0eb7f0421183..d48139e6721a3 100644 --- a/pkg/udf/python/worker/test_worker.py +++ b/pkg/udf/python/worker/test_worker.py @@ -6,6 +6,7 @@ import json import os import pathlib +import pickle import signal import struct import subprocess @@ -81,7 +82,78 @@ def complete_open_payload(payload): return payload +def complete_definition_validation_payload(source="def f(ctx, value): return value", handler="f"): + descriptor = {"type_id": worker.INT64, "offset_width": 32} + payload = { + "account_id": 1, + "handler": handler, + "source": source, + "mode": worker.MODE_SCALAR, + "null_policy": worker.NULL_CALL, + "abi_contract": worker.ABI_CONTRACT, + "adapter_version": worker.ADAPTER_VERSION, + "sdk_version": worker.SDK_VERSION, + "definition_schema_version": worker.DEFINITION_SCHEMA_VERSION, + "artifact_digest": worker._inline_artifact_digest(handler, source), + "environment_digest": worker._environment_digest(), + "definition_fingerprint": "", + "args": [descriptor], + "return": descriptor, + } + payload["definition_fingerprint"] = worker._definition_fingerprint(payload) + return payload + + class WorkerContractTest(unittest.TestCase): + def test_definition_validation_compiles_without_executing_module_code(self): + with tempfile.TemporaryDirectory(prefix="mo-udf-definition-") as directory: + marker = pathlib.Path(directory) / "executed" + source = ( + "from pathlib import Path\n" + f"Path({str(marker)!r}).write_text('executed')\n" + "def f(ctx, value): return value\n" + ) + payload = complete_definition_validation_payload(source) + server = worker.RoutineFlightServer("grpc://127.0.0.1:0") + try: + results = list( + server.do_action( + None, + flight.Action( + "ValidatePythonDefinition", + json.dumps(payload, separators=(",", ":")).encode(), + ), + ) + ) + self.assertEqual(1, len(results)) + self.assertEqual("OK", json.loads(bytes(results[0].body))["status"]) + self.assertFalse(marker.exists(), "definition validation executed module code") + self.assertEqual({}, server._active) + self.assertEqual({}, server._terminal) + finally: + server.shutdown() + + def test_definition_validation_rejects_syntax_before_catalog_state(self): + payload = complete_definition_validation_payload("def f(ctx, value) return value") + server = worker.RoutineFlightServer("grpc://127.0.0.1:0") + try: + results = list( + server.do_action( + None, + flight.Action( + "ValidatePythonDefinition", + json.dumps(payload, separators=(",", ":")).encode(), + ), + ) + ) + response = json.loads(bytes(results[0].body)) + self.assertEqual("ERROR", response["status"]) + self.assertRegex(response["reason"], r"USER_CODE: Python syntax error at line 1, column") + self.assertEqual({}, server._active) + self.assertEqual({}, server._terminal) + finally: + server.shutdown() + def test_capability_advertises_worker_instance_lease(self): encoded = worker._encode_capabilities( {"protocol_version": worker.PROTOCOL_VERSION}, lease_epoch=17 @@ -929,7 +1001,7 @@ def test_invocation_budget_is_cumulative_across_batches(self): output = pa.RecordBatch.from_arrays( [pa.array([1], type=pa.int64())], schema=result_schema ) - output_wire = worker._serialize_record_batch(output) + output_wire = worker._serialize_record_batch_message(output) output_bytes = pa.ipc.get_record_batch_size(output) for limit_name, limit, error_text, expected_calls in ( @@ -1064,6 +1136,64 @@ def test_handler_process_is_fresh_and_can_be_terminated(self): worker._run_handler_process(None, request, 0.1) self.assertLess(time.monotonic() - started, 2) + def test_record_batch_message_reuses_the_frozen_schema(self): + descriptor = {"type_id": worker.INT64, "offset_width": 32} + schema = worker._schema_from_descriptors([descriptor], "arg") + batch = pa.RecordBatch.from_arrays( + [pa.array([1, 2], type=pa.int64())], schema=schema + ) + stream_wire = worker._serialize_record_batch(batch) + message_wire = worker._serialize_record_batch_message(batch) + self.assertLess(len(message_wire), len(stream_wire)) + + decoded = worker._deserialize_record_batch_message(message_wire, schema) + self.assertEqual([1, 2], decoded.column(0).to_pylist()) + with self.assertRaisesRegex(ValueError, "valid Arrow record batch"): + worker._deserialize_record_batch_message( + message_wire, + worker._schema_from_descriptors( + [{"type_id": worker.VARCHAR, "offset_width": 32}], "arg" + ), + ) + + def test_handler_frame_keeps_arrow_bytes_out_of_pickle_metadata(self): + arrow_wire = b"arrow-payload" + parts, payload_size = worker._encode_execution_request( + { + "arrow_encoding": worker.HANDLER_ARROW_RECORD_BATCH, + "input": arrow_wire, + } + ) + self.assertEqual(4, len(parts)) + self.assertEqual(payload_size, len(parts[1]) + len(parts[2]) + len(parts[3])) + metadata = pickle.loads(parts[2], buffers=[pickle.PickleBuffer(parts[3])]) + self.assertIsInstance(metadata["input"], pickle.PickleBuffer) + self.assertEqual(arrow_wire, bytes(metadata["input"].raw())) + + def test_handler_process_accepts_schema_free_record_batch_messages(self): + descriptor = {"type_id": worker.INT64, "offset_width": 32} + schema = worker._schema_from_descriptors([descriptor], "arg") + result_schema = pa.schema([worker._field("result", descriptor)]) + batch = pa.RecordBatch.from_arrays( + [pa.array([1, 2], type=pa.int64())], schema=schema + ) + request = { + "source": "def f(ctx, x): return x + 1", + "handler": "f", + "mode": worker.MODE_SCALAR, + "null_policy": worker.NULL_CALL, + "sdk_version": worker.SDK_VERSION, + "context": None, + "args": [descriptor], + "return": descriptor, + "max_batch_bytes": 1 << 20, + "arrow_encoding": worker.HANDLER_ARROW_RECORD_BATCH, + "input": worker._serialize_record_batch_message(batch), + } + output_wire = worker._run_handler_process(None, request, 3) + output = worker._deserialize_record_batch_message(output_wire, result_schema) + self.assertEqual([2, 3], output.column(0).to_pylist()) + @unittest.skipUnless(os.name == "posix", "process-group cleanup race") def test_handler_error_after_leader_exit_releases_quota(self): """A normal handler error must not strand H while its leader exits.""" @@ -2530,7 +2660,7 @@ def invoke(): @unittest.skipUnless(os.name == "posix", "handler deadline test") def test_handler_deadline_includes_request_serialization(self): # The session owns one monotonic timestamp for the whole request. A - # deterministic clock jump after pickle.dumps proves that serialization + # deterministic clock jump after request encoding proves that serialization # cannot consume time outside the handler budget. clock = iter((0.0, 0.0, 1.0)) session = None @@ -2538,7 +2668,7 @@ def test_handler_deadline_includes_request_serialization(self): with mock.patch.object(worker.time, "monotonic", side_effect=clock): session = worker._HandlerProcessSession() with self.assertRaisesRegex(TimeoutError, "handler execution timeout"): - session.run(None, {}, 0.05) + session.run(None, {"input": b"x"}, 0.05) finally: if session is not None: session.close() diff --git a/pkg/udf/python/worker/worker.py b/pkg/udf/python/worker/worker.py index 0b4b0d3cf0dad..cc25b3c152414 100644 --- a/pkg/udf/python/worker/worker.py +++ b/pkg/udf/python/worker/worker.py @@ -59,6 +59,18 @@ TERMINAL_TTL_SECONDS = 300.0 ACK_TIMEOUT_SECONDS = 60.0 MAX_EXECUTION_FRAME_BYTES = 1 << 30 +# The handler IPC frame keeps the control metadata in pickle and transports +# the Arrow bytes through protocol 5's out-of-band buffer. The Arrow payload +# itself is a RecordBatch message; its schema is reconstructed from the +# frozen descriptors on both sides, so a schema message is not repeated for +# every batch in a bounded invocation burst. +HANDLER_ARROW_STREAM = "stream" +HANDLER_ARROW_RECORD_BATCH = "record_batch" +# A definition validation action carries one immutable source plus its typed +# contract. The source itself is bounded by the artifact limit below; keep +# the action envelope separate from MAX_CONTROL_BYTES, which bounds the +# smaller per-invocation control frames. +MAX_DEFINITION_VALIDATION_BYTES = (2 << 20) + MAX_CONTROL_BYTES MAX_HANDLER_PROCESSES = 8 # H is a worker-wide child limit. The two narrower limits prevent one # account, or one principal inside an account, from consuming all H slots. @@ -303,6 +315,24 @@ def _definition_fingerprint(payload: Dict[str, Any]) -> str: return hashlib.sha256(_canonical_definition_json(body)).hexdigest() _CAPABILITY_REQUEST_KEYS = frozenset({"protocol_version"}) +_DEFINITION_VALIDATION_KEYS = frozenset( + { + "account_id", + "handler", + "source", + "mode", + "null_policy", + "abi_contract", + "adapter_version", + "sdk_version", + "definition_schema_version", + "artifact_digest", + "environment_digest", + "definition_fingerprint", + "args", + "return", + } +) _CAPABILITY_RESPONSE_KEYS = frozenset( { "protocol_version", @@ -761,6 +791,111 @@ def _decode_capability_request(data: bytes) -> Dict[str, Any]: return value +def _decode_definition_validation(data: bytes) -> Dict[str, Any]: + """Decode the strict, pre-publication definition validation payload.""" + if not data or len(data) > MAX_DEFINITION_VALIDATION_BYTES: + raise ValueError("PROTOCOL: invalid definition validation size") + try: + value = json.loads( + bytes(data).decode("utf-8"), + object_pairs_hook=_reject_duplicate_json_pairs, + parse_constant=_reject_nonstandard_json_constant, + ) + except (UnicodeDecodeError, json.JSONDecodeError, RecursionError) as exc: + raise ValueError("PROTOCOL: invalid definition validation JSON") from exc + if not isinstance(value, dict) or set(value) != _DEFINITION_VALIDATION_KEYS: + raise ValueError("PROTOCOL: invalid definition validation fields") + + account_id = value.get("account_id") + if ( + isinstance(account_id, bool) + or not isinstance(account_id, int) + or account_id < 0 + or account_id > (1 << 64) - 1 + ): + raise ValueError("PROTOCOL: invalid definition validation account") + handler = _required_string(value, "handler") + if ":" in handler: + raise ValueError( + "UNSUPPORTED_ROUTINE_VERSION: Python external handler import requires the immutable artifact catalog" + ) + source = _required_string(value, "source") + try: + source_bytes = source.encode("utf-8") + except UnicodeEncodeError as exc: + raise ValueError("PROTOCOL: definition validation source is not valid UTF-8") from exc + if len(source_bytes) > (1 << 20): + raise ValueError("RESOURCE_EXHAUSTED: Python artifact exceeds 1048576 bytes") + + mode = _required_string(value, "mode") + null_policy = _required_string(value, "null_policy") + abi_contract = _required_string(value, "abi_contract") + adapter_version = _required_string(value, "adapter_version") + sdk_version = _required_string(value, "sdk_version") + if mode not in (MODE_SCALAR, MODE_VECTOR) or null_policy not in (NULL_CALL, NULL_RETURN): + raise ValueError("UNSUPPORTED_ROUTINE_VERSION: unsupported Python mode or NULL policy") + if abi_contract != ABI_CONTRACT or adapter_version != ADAPTER_VERSION: + raise ValueError("UNSUPPORTED_ROUTINE_VERSION: unsupported Python ABI contract") + if sdk_version != SDK_VERSION: + raise ValueError("UNSUPPORTED_ROUTINE_VERSION: unsupported Python SDK") + if value.get("definition_schema_version") != DEFINITION_SCHEMA_VERSION: + raise ValueError("UNSUPPORTED_ROUTINE_VERSION: unsupported Python definition schema") + artifact_digest = _required_digest(value, "artifact_digest") + environment_digest = _required_digest(value, "environment_digest") + definition_fingerprint = _required_digest(value, "definition_fingerprint") + if artifact_digest != _inline_artifact_digest(handler, source): + raise ValueError( + "UNSUPPORTED_ROUTINE_VERSION: Python artifact digest does not match the source" + ) + if environment_digest != _environment_digest(): + raise ValueError( + "UNSUPPORTED_ROUTINE_VERSION: Python environment digest does not match the worker contract" + ) + + args = value.get("args") + result_descriptor = value.get("return") + if not isinstance(args, list) or any(not isinstance(item, dict) for item in args): + raise ValueError("TYPE_CONTRACT: definition validation args must be an array of descriptors") + if not isinstance(result_descriptor, dict): + raise ValueError("TYPE_CONTRACT: definition validation return must be a descriptor") + for index, descriptor in enumerate(args): + _field(f"arg_{index}", descriptor) + _field("return", result_descriptor) + expected_fingerprint = _definition_fingerprint(value) + if definition_fingerprint != expected_fingerprint: + raise ValueError( + "UNSUPPORTED_ROUTINE_VERSION: Python definition fingerprint does not match the typed definition" + ) + return value + + +def _validate_definition_syntax(value: Dict[str, Any]) -> None: + """Compile source without executing its module or resolving its handler.""" + try: + compile(value["source"], "", "exec") + except SyntaxError as exc: + line = exc.lineno or 0 + column = exc.offset or 0 + message = exc.msg or "invalid syntax" + raise ValueError( + f"USER_CODE: Python syntax error at line {line}, column {column}: {message}" + ) from exc + + +def _encode_definition_validation_result( + value: Dict[str, Any], status: str, reason: Optional[str] = None +) -> bytes: + response = {"status": status} + if reason: + response["reason"] = reason + if status == "OK": + response["artifact_digest"] = value["artifact_digest"] + response["definition_fingerprint"] = value["definition_fingerprint"] + return json.dumps(response, separators=(",", ":"), sort_keys=True, allow_nan=False).encode( + "utf-8" + ) + + def _encode_capabilities(request: Dict[str, Any], lease_epoch: int = 1) -> bytes: _decode_capability_request(json.dumps(request, separators=(",", ":")).encode("utf-8")) if isinstance(lease_epoch, bool) or not isinstance(lease_epoch, int) or lease_epoch <= 0 or lease_epoch > (1 << 64) - 1: @@ -1093,6 +1228,14 @@ def _field(name: str, descriptor: Dict[str, Any]) -> pa.Field: return pa.field(name, _arrow_type(descriptor), nullable=True, metadata=metadata) +def _schema_from_descriptors( + descriptors: Iterable[Dict[str, Any]], prefix: str +) -> pa.Schema: + return pa.schema( + [_field(f"{prefix}_{index}", descriptor) for index, descriptor in enumerate(descriptors)] + ) + + TypeMetadataKey = b"mo.udf.type" TypeFingerprintKey = b"mo.udf.type_fingerprint" @@ -1121,9 +1264,15 @@ def _validate_input_batch_schema( # Flight carries one stream schema, but each received RecordBatch still # owns a schema object. Validate every batch so a later frame cannot # replace a fixed-width Arrow type after the first frame was admitted. + # The first batch validates every field against the descriptor. Later + # batches only need the schema equality check in the normal case; rebuilding + # every Field and its metadata for every batch was measurable overhead on + # small W=1 bursts. Retain the detailed validation on the mismatch path so + # a malformed later frame still gets the same contract diagnosis. + if batch.schema == expected_schema: + return _validate_schema(batch.schema, descriptors) - if batch.schema != expected_schema: - raise ValueError("TYPE_CONTRACT: input batch schema changed") + raise ValueError("TYPE_CONTRACT: input batch schema changed") def _load_handler(source: str, handler: str): @@ -1385,8 +1534,21 @@ def _serialize_record_batch(record: pa.RecordBatch) -> bytes: return sink.getvalue().to_pybytes() +def _serialize_record_batch_message(record: pa.RecordBatch) -> memoryview: + """Serialize one Arrow record-batch message without repeating its schema.""" + try: + # Keep the Arrow-owned buffer alive through the returned memoryview so + # the parent can hand it directly to protocol 5's out-of-band buffer + # without first copying it into a Python bytes object. + return memoryview(record.serialize()) + except (pa.ArrowException, OSError, ValueError, TypeError) as exc: + raise ValueError("PROTOCOL: cannot serialize Arrow record batch") from exc + + def _deserialize_record_batch(data: bytes) -> pa.RecordBatch: - if not isinstance(data, (bytes, bytearray)) or not data: + if isinstance(data, pickle.PickleBuffer): + data = data.raw() + if not isinstance(data, (bytes, bytearray, memoryview)) or not data: raise ValueError("PROTOCOL: execution payload is missing an Arrow batch") try: reader = pa.ipc.open_stream(pa.py_buffer(data)) @@ -1406,15 +1568,46 @@ def _deserialize_record_batch(data: bytes) -> pa.RecordBatch: reader.close() +def _deserialize_record_batch_message(data: Any, schema: pa.Schema) -> pa.RecordBatch: + """Decode one schema-free Arrow IPC record-batch message. + + The schema is derived from the already validated routine descriptor. This + is an internal handler IPC format, not a replacement for the Flight + schema, which remains validated independently at the worker boundary. + """ + if isinstance(data, pickle.PickleBuffer): + data = data.raw() + if not isinstance(data, (bytes, bytearray, memoryview)) or not data: + raise ValueError("PROTOCOL: execution payload is missing an Arrow batch") + if not isinstance(schema, pa.Schema): + raise ValueError("PROTOCOL: execution payload schema is invalid") + try: + return pa.ipc.read_record_batch(pa.py_buffer(data), schema) + except (pa.ArrowException, OSError, ValueError, TypeError) as exc: + raise ValueError("PROTOCOL: execution payload is not a valid Arrow record batch") from exc + + def _execute_handler_batch( request: Dict[str, Any], *, handler=None, statement_context: Optional[StatementContext] = None, + input_schema: Optional[pa.Schema] = None, + result_schema: Optional[pa.Schema] = None, ) -> bytes: - batch = _deserialize_record_batch(request["input"]) args = request["args"] result_descriptor = request["return"] + arrow_encoding = request.get("arrow_encoding", HANDLER_ARROW_STREAM) + if arrow_encoding == HANDLER_ARROW_RECORD_BATCH: + if input_schema is None: + input_schema = _schema_from_descriptors(args, "arg") + batch = _deserialize_record_batch_message( + request["input"], input_schema + ) + elif arrow_encoding == HANDLER_ARROW_STREAM: + batch = _deserialize_record_batch(request["input"]) + else: + raise ValueError("PROTOCOL: unsupported handler Arrow encoding") mode = request["mode"] null_policy = request["null_policy"] sdk_version = request["sdk_version"] @@ -1442,12 +1635,15 @@ def _execute_handler_batch( values.append(handler(call_context, *params)) output = values output_array = _output_array(output, result_descriptor, batch.num_rows) - result_field = _field("result", result_descriptor) + if result_schema is None: + result_schema = pa.schema([_field("result", result_descriptor)]) result_batch = pa.RecordBatch.from_arrays( - [output_array], schema=pa.schema([result_field]) + [output_array], schema=result_schema ) if pa.ipc.get_record_batch_size(result_batch) > request["max_batch_bytes"]: raise ValueError("RESOURCE_EXHAUSTED: output batch exceeds byte limit") + if arrow_encoding == HANDLER_ARROW_RECORD_BATCH: + return _serialize_record_batch_message(result_batch) return _serialize_record_batch(result_batch) @@ -1469,6 +1665,78 @@ def _write_execution_frame(stream, payload: bytes) -> None: stream.flush() +def _encode_execution_request(request: Dict[str, Any]): + """Build a handler frame without copying Arrow bytes into pickle. + + The outer length bounds the complete frame. The first eight bytes of the + payload bound the pickle metadata, and protocol 5 carries exactly one + out-of-band Arrow buffer. Returning write parts lets the nonblocking + sender avoid concatenating another full request-sized byte string. + """ + if not isinstance(request, dict) or "input" not in request: + raise ValueError("PROTOCOL: execution request is missing an Arrow batch") + input_wire = request["input"] + if isinstance(input_wire, pickle.PickleBuffer): + input_wire = input_wire.raw() + if not isinstance(input_wire, (bytes, bytearray, memoryview)): + raise ValueError("PROTOCOL: execution request Arrow batch is not bytes-like") + if not input_wire: + raise ValueError("PROTOCOL: execution request Arrow batch is empty") + metadata = dict(request) + metadata["input"] = pickle.PickleBuffer(input_wire) + buffers = [] + try: + metadata_wire = pickle.dumps( + metadata, protocol=5, buffer_callback=buffers.append + ) + except (pickle.PickleError, TypeError, ValueError) as exc: + raise ValueError("PROTOCOL: cannot encode execution request") from exc + if len(buffers) != 1: + raise ValueError("PROTOCOL: execution request must contain one Arrow buffer") + arrow_wire = buffers[0].raw() + payload_size = 8 + len(metadata_wire) + len(arrow_wire) + if payload_size > MAX_EXECUTION_FRAME_BYTES: + raise ValueError("RESOURCE_EXHAUSTED: execution request is too large") + parts = ( + struct.pack(">Q", payload_size), + struct.pack(">Q", len(metadata_wire)), + metadata_wire, + arrow_wire, + ) + return parts, payload_size + + +def _read_execution_request(stream) -> Optional[Dict[str, Any]]: + """Read and validate one metadata plus out-of-band Arrow frame.""" + header = stream.read(8) + if not header: + return None + if len(header) != 8: + raise EOFError("execution request header ended unexpectedly") + payload_size = struct.unpack(">Q", header)[0] + if payload_size > MAX_EXECUTION_FRAME_BYTES or payload_size < 8: + raise ValueError("RESOURCE_EXHAUSTED: execution request is too large") + metadata_size = struct.unpack(">Q", _read_exact(stream, 8))[0] + if metadata_size > payload_size - 8: + raise ValueError("PROTOCOL: execution request metadata is too large") + metadata_wire = _read_exact(stream, metadata_size) + arrow_size = payload_size - 8 - metadata_size + if arrow_size <= 0: + raise ValueError("PROTOCOL: execution request Arrow batch is empty") + arrow_wire = _read_exact(stream, arrow_size) + try: + request = pickle.loads( + metadata_wire, buffers=[pickle.PickleBuffer(arrow_wire)] + ) + except (EOFError, pickle.PickleError, TypeError, ValueError) as exc: + raise ValueError("PROTOCOL: execution request metadata is invalid") from exc + if not isinstance(request, dict) or not isinstance( + request.get("input"), pickle.PickleBuffer + ): + raise ValueError("PROTOCOL: execution request does not contain one Arrow buffer") + return request + + def _watch_parent_liveness(read_fd: int) -> None: """Terminate the whole handler group when the worker disappears. @@ -1546,19 +1814,14 @@ def _execute_handler_subprocess() -> None: frozen = None handler = None statement_context = None + input_schema = None + result_schema = None while True: - header = sys.stdin.buffer.read(8) - if not header: - break - if len(header) != 8: - raise EOFError("execution request header ended unexpectedly") - size = struct.unpack(">Q", header)[0] - if size > MAX_EXECUTION_FRAME_BYTES: - raise ValueError("RESOURCE_EXHAUSTED: execution request is too large") try: - request = pickle.loads(_read_exact(sys.stdin.buffer, size)) - if not isinstance(request, dict): - raise ValueError("PROTOCOL: execution request is not an object") + request = _read_execution_request(sys.stdin.buffer) + if request is None: + # EOF before a new frame is the normal burst shutdown. + break contract = { key: request.get(key) for key in ( @@ -1566,17 +1829,24 @@ def _execute_handler_subprocess() -> None: "adapter_version", "sdk_version", "definition_schema_version", "artifact_digest", "environment_digest", "definition_fingerprint", "context", "args", "return", "max_batch_bytes", "max_batch_rows", - "max_invocation_rows", "max_invocation_result_bytes", + "max_invocation_rows", "max_invocation_result_bytes", "arrow_encoding", ) } if frozen is None: frozen = contract statement_context = _statement_context(request.get("context")) handler = _load_handler(request["source"], request["handler"]) + if request.get("arrow_encoding") == HANDLER_ARROW_RECORD_BATCH: + input_schema = _schema_from_descriptors(request["args"], "arg") + result_schema = pa.schema([_field("result", request["return"])]) elif contract != frozen: raise ValueError("PROTOCOL: handler burst contract changed") response = bytes([_HANDLER_RESPONSE_OK]) + _execute_handler_batch( - request, handler=handler, statement_context=statement_context + request, + handler=handler, + statement_context=statement_context, + input_schema=input_schema, + result_schema=result_schema, ) except Exception as exc: response = bytes([_HANDLER_RESPONSE_ERROR]) + _safe_error(exc).encode("utf-8") @@ -1974,13 +2244,11 @@ def run(self, context, request: Dict[str, Any], timeout_seconds: float) -> bytes # unbounded part of the caller's budget before the deadline was even # installed. deadline = time.monotonic() + timeout_seconds - request_wire = pickle.dumps(request, protocol=5) + request_parts, request_size = _encode_execution_request(request) if time.monotonic() >= deadline: raise TimeoutError("DEADLINE_EXCEEDED: handler execution timeout") - if len(request_wire) > MAX_EXECUTION_FRAME_BYTES: - raise ValueError("RESOURCE_EXHAUSTED: execution request is too large") - request_frame = struct.pack(">Q", len(request_wire)) + request_wire - request_offset = 0 + request_part_index = 0 + request_part_offset = 0 stdin_fd = self._process.stdin.fileno() self._selector.register(stdin_fd, selectors.EVENT_WRITE, "request") try: @@ -1997,13 +2265,25 @@ def run(self, context, request: Dict[str, Any], timeout_seconds: float) -> bytes for event, _ in events: if event.data == "request": try: - written = os.write(stdin_fd, request_frame[request_offset:]) + while request_part_index < len(request_parts): + part = request_parts[request_part_index] + if request_part_offset == len(part): + request_part_index += 1 + request_part_offset = 0 + continue + written = os.write(stdin_fd, part[request_part_offset:]) + if written <= 0: + raise BrokenPipeError("handler request channel made no progress") + request_part_offset += written + if request_part_offset < len(part): + break except BlockingIOError: continue except BrokenPipeError as exc: - raise ValueError("USER_CODE: handler process closed its request channel") from exc - request_offset += written - if request_offset == len(request_frame): + raise ValueError( + "USER_CODE: handler process closed its request channel" + ) from exc + if request_part_index == len(request_parts): self._selector.unregister(stdin_fd) else: try: @@ -2031,7 +2311,7 @@ def run(self, context, request: Dict[str, Any], timeout_seconds: float) -> bytes raise ValueError("PROTOCOL: handler process returned an unknown status") output = response[1:] self._burst_batches += 1 - self._burst_bytes += len(request_wire) + len(response) + self._burst_bytes += request_size + len(response) return output finally: # A failed write/read must not leave a stale registration that a @@ -2727,6 +3007,23 @@ def do_action(self, context, action): # path before any routine is opened. yield flight.Result(_encode_capabilities(request, self._lease_epoch)) return + if action.type == "ValidatePythonDefinition": + value = _decode_definition_validation(action.body) + try: + _validate_definition_syntax(value) + except Exception as exc: + # A syntax error is a definition result, not an invocation + # failure. Returning a bounded structured result lets the + # Gateway reject CREATE/REPLACE without creating an active + # invocation, handler process, or terminal tombstone. + yield flight.Result( + _encode_definition_validation_result( + value, "ERROR", _safe_error(exc) + ) + ) + else: + yield flight.Result(_encode_definition_validation_result(value, "OK")) + return control = _decode_control(action.body) key = _tuple_key(control["tuple"]) self._require_current_lease(key) @@ -2924,7 +3221,8 @@ def do_exchange(self, context, descriptor, reader, writer): "max_batch_rows": max_batch_rows, "max_invocation_rows": max_invocation_rows, "max_invocation_result_bytes": max_invocation_result_bytes, - "input": _serialize_record_batch(batch), + "arrow_encoding": HANDLER_ARROW_RECORD_BATCH, + "input": _serialize_record_batch_message(batch), } if handler_session is None: owner_id = _handler_quota_owner(payload) @@ -2937,7 +3235,9 @@ def do_exchange(self, context, descriptor, reader, writer): output_wire = handler_session.run( context, execution_request, handler_timeout_seconds ) - output_batch = _deserialize_record_batch(output_wire) + output_batch = _deserialize_record_batch_message( + output_wire, result_schema + ) if output_batch.num_rows != batch.num_rows or output_batch.num_columns != 1: raise ValueError("TYPE_CONTRACT: handler result has the wrong shape") _validate_field(output_batch.schema.field(0), "result", result_descriptor) diff --git a/pkg/udf/runtime.go b/pkg/udf/runtime.go index 3c92e352a560d..e56f5e08b78a7 100644 --- a/pkg/udf/runtime.go +++ b/pkg/udf/runtime.go @@ -279,6 +279,42 @@ type RuntimeReadiness interface { CheckLanguageReady(context.Context, string) error } +// RoutineDefinition is the typed, pre-publication contract handed to a +// language runtime for definition validation. It is deliberately separate +// from Invocation: a CREATE or REPLACE has no FunctionRef/revision yet and +// must be validated before the new Catalog revision becomes visible. +// +// Source is transient create-time input. An executable plan never carries +// source; a runtime with an ArtifactResolver must resolve the same immutable +// artifact digest before sending source to the worker. +type RoutineDefinition struct { + Language string + AccountID uint64 + Handler string + Source string + Args []types.Type + ReturnType types.Type + Mode string + NullPolicy string + ABIContract string + AdapterVersion string + SDKVersion string + DefinitionSchemaVersion int + ArtifactDigest string + EnvironmentDigest string + DefinitionFingerprint string +} + +// RuntimeDefinitionValidator is an optional language runtime capability. +// Python CREATE/REPLACE requires it: the worker must compile the exact +// immutable artifact before Catalog publication. Keeping this as a separate +// capability preserves the language-neutral execution interface and lets +// ordinary SQL UDFs continue to use Runtime implementations that do not own +// a source validation protocol. +type RuntimeDefinitionValidator interface { + ValidateDefinition(context.Context, *RoutineDefinition) error +} + // Registry dispatches to the one runtime selected by a routine's language. // A registry is immutable after construction, so plan execution cannot race // service registration or observe a partially initialized adapter. @@ -332,6 +368,21 @@ func (r *runtimeRegistry) CheckLanguageReady(ctx context.Context, language strin return readiness.CheckLanguageReady(ctx, language) } +func (r *runtimeRegistry) ValidateDefinition(ctx context.Context, definition *RoutineDefinition) error { + if definition == nil { + return moerr.NewInvalidInputNoCtx("nil UDF routine definition") + } + runtime := r.byLanguage[definition.Language] + if runtime == nil { + return moerr.NewNotSupportedf(ctx, "%s UDF runtime is not enabled", definition.Language) + } + validator, ok := runtime.(RuntimeDefinitionValidator) + if !ok { + return moerr.NewNotSupportedf(ctx, "%s UDF runtime does not expose definition validation", definition.Language) + } + return validator.ValidateDefinition(ctx, definition) +} + func (r *runtimeRegistry) Close() error { r.closeOnce.Do(func() { languages := make([]string, 0, len(r.byLanguage)) diff --git a/test/distributed/cases/udf_python/udf_python.sql b/test/distributed/cases/udf_python/udf_python.sql index 26799d1531eea..e0f580774427b 100644 --- a/test/distributed/cases/udf_python/udf_python.sql +++ b/test/distributed/cases/udf_python/udf_python.sql @@ -9,6 +9,19 @@ insert into input_values values (1), (null), (3); select v, python_bvt_add(v) as plus_one from input_values order by v is null, v; select case when v is null then 99 else python_bvt_add(v) end as guarded from input_values order by v is null, v; +-- Python source is compiled by the current worker before a Catalog identity +-- or revision is published. +-- @regex("Python syntax error at line",true) +create function python_bvt_bad_syntax (x int) returns int language python as 'def python_bvt_bad_syntax(ctx, x) return x' handler 'python_bvt_bad_syntax'; + +-- A failed replacement keeps the previous immutable active revision usable. +create function python_bvt_replace (x int) returns int language python as 'def python_bvt_replace(ctx, x): return x + 10' handler 'python_bvt_replace'; +select python_bvt_replace(1); +-- @regex("Python syntax error at line",true) +create or replace function python_bvt_replace (x int) returns int language python as 'def python_bvt_replace(ctx, x) return x + 20' handler 'python_bvt_replace'; +select python_bvt_replace(1); + drop function python_bvt_add(int); +drop function python_bvt_replace(int); drop table input_values; drop database udf_python_bvt; From c0ff3489f07b62a58eca208845d53c8de3ddb650 Mon Sep 17 00:00:00 2001 From: iamlinjunhong <1030420200@qq.com> Date: Fri, 11 Sep 2026 22:17:01 +0800 Subject: [PATCH 055/202] fix(udf): read handler frames across pipe short reads --- pkg/udf/python/worker/test_worker.py | 23 +++++++++++++++++++++++ pkg/udf/python/worker/worker.py | 11 ++++++++--- 2 files changed, 31 insertions(+), 3 deletions(-) diff --git a/pkg/udf/python/worker/test_worker.py b/pkg/udf/python/worker/test_worker.py index d48139e6721a3..78812bc17af3f 100644 --- a/pkg/udf/python/worker/test_worker.py +++ b/pkg/udf/python/worker/test_worker.py @@ -1170,6 +1170,29 @@ def test_handler_frame_keeps_arrow_bytes_out_of_pickle_metadata(self): self.assertIsInstance(metadata["input"], pickle.PickleBuffer) self.assertEqual(arrow_wire, bytes(metadata["input"].raw())) + def test_handler_request_reader_accepts_fragmented_headers(self): + parts, _ = worker._encode_execution_request( + { + "arrow_encoding": worker.HANDLER_ARROW_RECORD_BATCH, + "input": b"arrow-payload", + } + ) + + class OneByteReader: + def __init__(self, data): + self.data = data + + def read(self, size=-1): + if not self.data: + return b"" + count = 1 if size < 0 else min(1, size) + value, self.data = self.data[:count], self.data[count:] + return value + + request = worker._read_execution_request(OneByteReader(b"".join(parts))) + self.assertEqual(worker.HANDLER_ARROW_RECORD_BATCH, request["arrow_encoding"]) + self.assertEqual(b"arrow-payload", bytes(request["input"].raw())) + def test_handler_process_accepts_schema_free_record_batch_messages(self): descriptor = {"type_id": worker.INT64, "offset_width": 32} schema = worker._schema_from_descriptors([descriptor], "arg") diff --git a/pkg/udf/python/worker/worker.py b/pkg/udf/python/worker/worker.py index cc25b3c152414..ae2c41f4a41f6 100644 --- a/pkg/udf/python/worker/worker.py +++ b/pkg/udf/python/worker/worker.py @@ -1708,11 +1708,16 @@ def _encode_execution_request(request: Dict[str, Any]): def _read_execution_request(stream) -> Optional[Dict[str, Any]]: """Read and validate one metadata plus out-of-band Arrow frame.""" - header = stream.read(8) - if not header: + # A pipe is a byte stream: one read is allowed to return a prefix even + # when the sender has already written the complete frame. Read one byte + # first so only an empty read means clean burst EOF, then complete the + # fixed-width header through the same exact-read path as the body. + first = stream.read(1) + if not first: return None - if len(header) != 8: + if len(first) != 1: raise EOFError("execution request header ended unexpectedly") + header = bytes(first) + _read_exact(stream, 7) payload_size = struct.unpack(">Q", header)[0] if payload_size > MAX_EXECUTION_FRAME_BYTES or payload_size < 8: raise ValueError("RESOURCE_EXHAUSTED: execution request is too large") From 3873305acf186405a19dac50caeeeb3dc700b135 Mon Sep 17 00:00:00 2001 From: iamlinjunhong <1030420200@qq.com> Date: Fri, 11 Sep 2026 22:17:55 +0800 Subject: [PATCH 056/202] fix(udf): keep Arrow encoder cleanup retryable --- pkg/udf/python/arrow.go | 5 ++++- pkg/udf/python/arrow_test.go | 22 ++++++++++++++++++++++ pkg/udf/python/gateway.go | 5 ++++- 3 files changed, 30 insertions(+), 2 deletions(-) diff --git a/pkg/udf/python/arrow.go b/pkg/udf/python/arrow.go index 1d80e00bd67a7..40da330625b81 100644 --- a/pkg/udf/python/arrow.go +++ b/pkg/udf/python/arrow.go @@ -472,13 +472,16 @@ func (e *inputBatchWireEncoder) close() error { if e == nil || e.closed { return nil } - e.closed = true if e.writer == nil { + e.closed = true return nil } if err := e.writer.Close(); err != nil { return fmt.Errorf("close Arrow record batch encoder: %w", err) } + // Commit the closed state only after the writer has released its resources. + // A failed close remains retryable for the owning invocation cleanup path. + e.closed = true return nil } diff --git a/pkg/udf/python/arrow_test.go b/pkg/udf/python/arrow_test.go index cf1f812fdcc88..5a4ca75bed7ee 100644 --- a/pkg/udf/python/arrow_test.go +++ b/pkg/udf/python/arrow_test.go @@ -240,6 +240,28 @@ func TestInputBatchWireEncoderRecoversAfterOversizeCandidate(t *testing.T) { require.Len(t, frames, 2) } +func TestInputBatchWireEncoderCloseIsIdempotent(t *testing.T) { + descriptor, err := NewTypeDescriptor(types.T_int64.ToType()) + require.NoError(t, err) + field, err := descriptor.Field("arg_0") + require.NoError(t, err) + schema := arrow.NewSchema([]arrow.Field{field}, nil) + builder := array.NewInt64Builder(memory.NewGoAllocator()) + builder.AppendValues([]int64{1}, nil) + values := builder.NewInt64Array() + record := array.NewRecordBatch(schema, []arrow.Array{values}, 1) + values.Release() + defer record.Release() + + encoder := &inputBatchWireEncoder{schema: schema} + _, err = encoder.encode(record, DefaultMaxBatchBytes) + require.NoError(t, err) + require.NoError(t, encoder.close()) + require.NoError(t, encoder.close()) + _, err = encoder.encode(record, DefaultMaxBatchBytes) + require.ErrorContains(t, err, "invalid Arrow record batch encoder") +} + func TestArrowValueDomainRejectsWidthAndTimeOverflow(t *testing.T) { allocator := memory.NewGoAllocator() stringBuilder := array.NewStringBuilder(allocator) diff --git a/pkg/udf/python/gateway.go b/pkg/udf/python/gateway.go index 66edcb1fe3ba2..6ebcf0bfd2958 100644 --- a/pkg/udf/python/gateway.go +++ b/pkg/udf/python/gateway.go @@ -520,8 +520,11 @@ func (g *Gateway) Execute(ctx context.Context, invocation *udf.Invocation, resul if inputEncoderClosed { return nil } + if err := inputEncoder.close(); err != nil { + return err + } inputEncoderClosed = true - return inputEncoder.close() + return nil } defer func() { if closeErr := closeInputEncoder(); err == nil && closeErr != nil { From 5a7c3224703bf0a0b0281d2a159bd5d4998ad4cd Mon Sep 17 00:00:00 2001 From: iamlinjunhong <1030420200@qq.com> Date: Fri, 11 Sep 2026 22:18:42 +0800 Subject: [PATCH 057/202] fix(udf): finalize input before terminal success --- pkg/udf/python/gateway.go | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/pkg/udf/python/gateway.go b/pkg/udf/python/gateway.go index 6ebcf0bfd2958..3b5ff104a3eb6 100644 --- a/pkg/udf/python/gateway.go +++ b/pkg/udf/python/gateway.go @@ -682,10 +682,17 @@ func (g *Gateway) Execute(ctx context.Context, invocation *udf.Invocation, resul if !inputEnded || lastSequence != batchIndex { return fmt.Errorf("python udf: input stream did not reach EndInput") } + // Close the local Arrow writer before accepting the worker's terminal + // Finish. If finalizing the local input owner fails, the admission cleanup + // still has a chance to cancel/close the remote invocation; acknowledging + // Finish first would publish remote success while returning a local error. + if err := closeInputEncoder(); err != nil { + return err + } if err := g.receiveFinish(streamCtx, client, stream, execution.Tuple, execution.Length, rows, &sequence); err != nil { return err } - return closeInputEncoder() + return nil } // invocationAdmission is the real Gateway owner for the CN-side execution From a1b2b484941a3bd1d92609c7503949d8c5f0c612 Mon Sep 17 00:00:00 2001 From: iamlinjunhong <1030420200@qq.com> Date: Fri, 11 Sep 2026 22:19:27 +0800 Subject: [PATCH 058/202] fix(udf): classify invalid handler buffers --- pkg/udf/python/worker/test_worker.py | 6 ++++++ pkg/udf/python/worker/worker.py | 10 +++++----- 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/pkg/udf/python/worker/test_worker.py b/pkg/udf/python/worker/test_worker.py index 78812bc17af3f..b4d1d1c226a64 100644 --- a/pkg/udf/python/worker/test_worker.py +++ b/pkg/udf/python/worker/test_worker.py @@ -1193,6 +1193,12 @@ def read(self, size=-1): self.assertEqual(worker.HANDLER_ARROW_RECORD_BATCH, request["arrow_encoding"]) self.assertEqual(b"arrow-payload", bytes(request["input"].raw())) + def test_handler_request_encoder_rejects_non_contiguous_arrow_buffer(self): + with self.assertRaisesRegex(ValueError, "cannot encode execution request"): + worker._encode_execution_request( + {"input": memoryview(b"arrow-payload")[::2]} + ) + def test_handler_process_accepts_schema_free_record_batch_messages(self): descriptor = {"type_id": worker.INT64, "offset_width": 32} schema = worker._schema_from_descriptors([descriptor], "arg") diff --git a/pkg/udf/python/worker/worker.py b/pkg/udf/python/worker/worker.py index ae2c41f4a41f6..a05f8f6b20cdc 100644 --- a/pkg/udf/python/worker/worker.py +++ b/pkg/udf/python/worker/worker.py @@ -1683,17 +1683,17 @@ def _encode_execution_request(request: Dict[str, Any]): if not input_wire: raise ValueError("PROTOCOL: execution request Arrow batch is empty") metadata = dict(request) - metadata["input"] = pickle.PickleBuffer(input_wire) buffers = [] try: + metadata["input"] = pickle.PickleBuffer(input_wire) metadata_wire = pickle.dumps( metadata, protocol=5, buffer_callback=buffers.append ) - except (pickle.PickleError, TypeError, ValueError) as exc: + if len(buffers) != 1: + raise ValueError("PROTOCOL: execution request must contain one Arrow buffer") + arrow_wire = buffers[0].raw() + except (BufferError, pickle.PickleError, TypeError, ValueError) as exc: raise ValueError("PROTOCOL: cannot encode execution request") from exc - if len(buffers) != 1: - raise ValueError("PROTOCOL: execution request must contain one Arrow buffer") - arrow_wire = buffers[0].raw() payload_size = 8 + len(metadata_wire) + len(arrow_wire) if payload_size > MAX_EXECUTION_FRAME_BYTES: raise ValueError("RESOURCE_EXHAUSTED: execution request is too large") From e8c56553a3e1f48145cbb6d70c233a0919457f5f Mon Sep 17 00:00:00 2001 From: iamlinjunhong <1030420200@qq.com> Date: Fri, 11 Sep 2026 22:22:34 +0800 Subject: [PATCH 059/202] fix(udf): reject trailing handler batch bytes --- pkg/udf/python/worker/test_worker.py | 2 ++ pkg/udf/python/worker/worker.py | 6 +++++- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/pkg/udf/python/worker/test_worker.py b/pkg/udf/python/worker/test_worker.py index b4d1d1c226a64..02188127337d6 100644 --- a/pkg/udf/python/worker/test_worker.py +++ b/pkg/udf/python/worker/test_worker.py @@ -1155,6 +1155,8 @@ def test_record_batch_message_reuses_the_frozen_schema(self): [{"type_id": worker.VARCHAR, "offset_width": 32}], "arg" ), ) + with self.assertRaisesRegex(ValueError, "valid Arrow record batch"): + worker._deserialize_record_batch_message(bytes(message_wire) + b"junk", schema) def test_handler_frame_keeps_arrow_bytes_out_of_pickle_metadata(self): arrow_wire = b"arrow-payload" diff --git a/pkg/udf/python/worker/worker.py b/pkg/udf/python/worker/worker.py index a05f8f6b20cdc..6d99c2ec253c9 100644 --- a/pkg/udf/python/worker/worker.py +++ b/pkg/udf/python/worker/worker.py @@ -1582,7 +1582,11 @@ def _deserialize_record_batch_message(data: Any, schema: pa.Schema) -> pa.Record if not isinstance(schema, pa.Schema): raise ValueError("PROTOCOL: execution payload schema is invalid") try: - return pa.ipc.read_record_batch(pa.py_buffer(data), schema) + reader = pa.BufferReader(pa.py_buffer(data)) + record = pa.ipc.read_record_batch(reader, schema) + if reader.tell() != reader.size(): + raise ValueError("trailing bytes after Arrow record batch") + return record except (pa.ArrowException, OSError, ValueError, TypeError) as exc: raise ValueError("PROTOCOL: execution payload is not a valid Arrow record batch") from exc From 6bdea8caa0021d8b7e27b0b4d190b029034a6fcb Mon Sep 17 00:00:00 2001 From: iamlinjunhong <1030420200@qq.com> Date: Fri, 11 Sep 2026 22:25:35 +0800 Subject: [PATCH 060/202] fix(udf): validate Python handler bindings at create --- pkg/udf/python/worker/test_worker.py | 46 ++++++++++++++++++++++++++++ pkg/udf/python/worker/worker.py | 28 ++++++++++++++++- 2 files changed, 73 insertions(+), 1 deletion(-) diff --git a/pkg/udf/python/worker/test_worker.py b/pkg/udf/python/worker/test_worker.py index 02188127337d6..f30d33304486a 100644 --- a/pkg/udf/python/worker/test_worker.py +++ b/pkg/udf/python/worker/test_worker.py @@ -154,6 +154,52 @@ def test_definition_validation_rejects_syntax_before_catalog_state(self): finally: server.shutdown() + def test_definition_validation_rejects_missing_handler_before_catalog_state(self): + payload = complete_definition_validation_payload( + "def other(ctx, value): return value" + ) + server = worker.RoutineFlightServer("grpc://127.0.0.1:0") + try: + results = list( + server.do_action( + None, + flight.Action( + "ValidatePythonDefinition", + json.dumps(payload, separators=(",", ":")).encode(), + ), + ) + ) + response = json.loads(bytes(results[0].body)) + self.assertEqual("ERROR", response["status"]) + self.assertIn("not a module-level function", response["reason"]) + self.assertEqual({}, server._active) + self.assertEqual({}, server._terminal) + finally: + server.shutdown() + + def test_definition_validation_rejects_async_handler_before_catalog_state(self): + payload = complete_definition_validation_payload( + "async def f(ctx, value): return value" + ) + server = worker.RoutineFlightServer("grpc://127.0.0.1:0") + try: + results = list( + server.do_action( + None, + flight.Action( + "ValidatePythonDefinition", + json.dumps(payload, separators=(",", ":")).encode(), + ), + ) + ) + response = json.loads(bytes(results[0].body)) + self.assertEqual("ERROR", response["status"]) + self.assertIn("must be synchronous", response["reason"]) + self.assertEqual({}, server._active) + self.assertEqual({}, server._terminal) + finally: + server.shutdown() + def test_capability_advertises_worker_instance_lease(self): encoded = worker._encode_capabilities( {"protocol_version": worker.PROTOCOL_VERSION}, lease_epoch=17 diff --git a/pkg/udf/python/worker/worker.py b/pkg/udf/python/worker/worker.py index 6d99c2ec253c9..20d3a6ec4446b 100644 --- a/pkg/udf/python/worker/worker.py +++ b/pkg/udf/python/worker/worker.py @@ -14,6 +14,7 @@ from __future__ import annotations import argparse +import ast import contextlib import datetime as _datetime import decimal as _decimal @@ -870,7 +871,7 @@ def _decode_definition_validation(data: bytes) -> Dict[str, Any]: def _validate_definition_syntax(value: Dict[str, Any]) -> None: - """Compile source without executing its module or resolving its handler.""" + """Validate source and the static handler binding without executing it.""" try: compile(value["source"], "", "exec") except SyntaxError as exc: @@ -880,6 +881,31 @@ def _validate_definition_syntax(value: Dict[str, Any]) -> None: raise ValueError( f"USER_CODE: Python syntax error at line {line}, column {column}: {message}" ) from exc + handler = value["handler"] + if not handler.isidentifier(): + raise ValueError( + "USER_CODE: Python handler must be a module-level identifier" + ) + tree = ast.parse(value["source"], filename="", mode="exec") + binding = None + for node in tree.body: + if isinstance(node, ast.AsyncFunctionDef) and node.name == handler: + raise ValueError("USER_CODE: Python handler must be synchronous") + if isinstance(node, ast.FunctionDef) and node.name == handler: + binding = node + break + if isinstance(node, ast.Assign) and isinstance(node.value, ast.Lambda): + if any(isinstance(target, ast.Name) and target.id == handler for target in node.targets): + binding = node + break + if isinstance(node, ast.AnnAssign) and isinstance(node.value, ast.Lambda): + if isinstance(node.target, ast.Name) and node.target.id == handler: + binding = node + break + if binding is None: + raise ValueError( + f"USER_CODE: Python handler {handler!r} is not a module-level function" + ) def _encode_definition_validation_result( From dbf0432861f27476ff45b627c0b4c877ecd52165 Mon Sep 17 00:00:00 2001 From: iamlinjunhong <1030420200@qq.com> Date: Fri, 11 Sep 2026 22:30:59 +0800 Subject: [PATCH 061/202] fix(udf): validate empty and typed input boundaries --- pkg/udf/python/arrow.go | 18 +++++++++++++++--- pkg/udf/python/arrow_test.go | 14 ++++++++++++++ pkg/udf/python/gateway.go | 18 +++++++++++++++--- pkg/udf/python/gateway_test.go | 18 ++++++++++++++++++ 4 files changed, 62 insertions(+), 6 deletions(-) diff --git a/pkg/udf/python/arrow.go b/pkg/udf/python/arrow.go index 40da330625b81..5697fbc8b22bf 100644 --- a/pkg/udf/python/arrow.go +++ b/pkg/udf/python/arrow.go @@ -531,8 +531,20 @@ func newInputBatchEncoder(inputs []*vector.Vector, args []types.Type) (*inputBat if typ.IsVarlen() { fixedWidth = false } - if inputs[i] == nil || inputs[i].Length() == 0 { - return nil, fmt.Errorf("input column %d is shorter than batch range", i) + if inputs[i] == nil { + return nil, fmt.Errorf("input column %d is missing", i) + } + actualType := inputs[i].GetType() + if actualType == nil { + return nil, fmt.Errorf("input column %d has no type", i) + } + if !actualType.Eq(typ) { + return nil, fmt.Errorf( + "input column %d type %s does not match the frozen argument type %s", + i, + actualType.DescString(), + typ.DescString(), + ) } } schema := arrow.NewSchema(fields, nil) @@ -567,7 +579,7 @@ func (e *inputBatchEncoder) build(start, length int) (arrow.RecordBatch, *arrow. return nil, nil, fmt.Errorf("invalid Python UDF input shape") } for i, input := range e.inputs { - if !input.IsConst() && input.Length() < start+length { + if input.Length() == 0 || (!input.IsConst() && input.Length() < start+length) { return nil, nil, fmt.Errorf("input column %d is shorter than batch range", i) } } diff --git a/pkg/udf/python/arrow_test.go b/pkg/udf/python/arrow_test.go index 5a4ca75bed7ee..7e24f171465c7 100644 --- a/pkg/udf/python/arrow_test.go +++ b/pkg/udf/python/arrow_test.go @@ -145,6 +145,20 @@ func TestBuildInputRecordRejectsEmptyConstantVector(t *testing.T) { require.ErrorContains(t, err, "shorter than batch range") } +func TestInputBatchEncoderRejectsMismatchedVectorType(t *testing.T) { + mp := mpool.MustNewZeroNoFixed() + input := vector.NewVec(types.T_int32.ToType()) + defer func() { + input.Free(mp) + mpool.DeleteMPool(mp) + }() + + _, err := newInputBatchEncoder( + []*vector.Vector{input}, []types.Type{types.T_int64.ToType()}, + ) + require.ErrorContains(t, err, "does not match the frozen argument type") +} + func TestInputBatchEncoderReusesSchemaAndPreservesRange(t *testing.T) { mp := mpool.MustNewZeroNoFixed() input := vector.NewVec(types.T_int64.ToType()) diff --git a/pkg/udf/python/gateway.go b/pkg/udf/python/gateway.go index 3b5ff104a3eb6..243f036a407d7 100644 --- a/pkg/udf/python/gateway.go +++ b/pkg/udf/python/gateway.go @@ -1650,10 +1650,22 @@ func validateInvocationInputs(invocation *udf.Invocation, args []TypeDescriptor) if invocation == nil || len(invocation.Inputs) != len(args) { return fmt.Errorf("python udf: invalid input shape") } - // An empty physical selection has no input backing to inspect. The - // descriptor and arity have already been validated above; keeping this - // case explicit allows zero-row operators to avoid manufacturing vectors. + // An empty physical selection has no input backing to inspect. Nil inputs + // are valid in this case, but a vector supplied by a caller still has to + // match the frozen descriptor. Otherwise a zero-row invocation could + // bypass the same type boundary enforced for non-empty execution. if invocation.Length == 0 { + for index, input := range invocation.Inputs { + if input == nil { + continue + } + if input.GetType() == nil || !input.GetType().Eq(invocation.Args[index]) { + return fmt.Errorf( + "python udf: input column %d type does not match the frozen argument type", + index, + ) + } + } return nil } for index, input := range invocation.Inputs { diff --git a/pkg/udf/python/gateway_test.go b/pkg/udf/python/gateway_test.go index c313e81eed2a7..128ea37a965e6 100644 --- a/pkg/udf/python/gateway_test.go +++ b/pkg/udf/python/gateway_test.go @@ -170,6 +170,24 @@ func validInvocation() *udf.Invocation { } } +func TestValidateInvocationInputsChecksZeroRowVectorTypes(t *testing.T) { + mp := mpool.MustNewZeroNoFixed() + input := vector.NewVec(types.T_int32.ToType()) + defer func() { + input.Free(mp) + mpool.DeleteMPool(mp) + }() + + descriptor, err := NewTypeDescriptor(types.T_int64.ToType()) + require.NoError(t, err) + err = validateInvocationInputs(&udf.Invocation{ + Length: 0, + Args: []types.Type{types.T_int64.ToType()}, + Inputs: []*vector.Vector{input}, + }, []TypeDescriptor{descriptor}) + require.ErrorContains(t, err, "does not match the frozen argument type") +} + func TestValidateInvocationRequiresFrozenContract(t *testing.T) { base := validInvocation() require.NoError(t, validateInvocation(base)) From 9f016b4c6383263e6bc485dd6a1cf2befcce35fc Mon Sep 17 00:00:00 2001 From: iamlinjunhong <1030420200@qq.com> Date: Fri, 11 Sep 2026 22:33:38 +0800 Subject: [PATCH 062/202] fix(udf): reject unsupported routine types before allocation --- pkg/sql/colexec/external_routine_eval.go | 14 +++++++++----- pkg/sql/colexec/external_routine_eval_test.go | 17 +++++++++++++++++ 2 files changed, 26 insertions(+), 5 deletions(-) diff --git a/pkg/sql/colexec/external_routine_eval.go b/pkg/sql/colexec/external_routine_eval.go index b14f4c444f907..e41386047e92f 100644 --- a/pkg/sql/colexec/external_routine_eval.go +++ b/pkg/sql/colexec/external_routine_eval.go @@ -138,13 +138,17 @@ func validateRoutineCall(call *planpb.RoutineCall) error { if !udf.IsSHA256Digest(python.DefinitionFingerprint) { return fmt.Errorf("UNSUPPORTED_ROUTINE_VERSION: typed Python implementation has invalid definition fingerprint") } - if len(call.ArgumentTypes) > 0 { - for i, argumentType := range call.ArgumentTypes { - if argumentType == nil { - return fmt.Errorf("UNSUPPORTED_ROUTINE_VERSION: typed Python argument %d is nil", i) - } + for i, argumentType := range call.ArgumentTypes { + if argumentType == nil { + return fmt.Errorf("UNSUPPORTED_ROUTINE_VERSION: typed Python argument %d is nil", i) + } + if _, err := function.NewPythonTypeDescriptor(planTypeToSQL(*argumentType)); err != nil { + return fmt.Errorf("UNSUPPORTED_ROUTINE_VERSION: typed Python argument %d is unsupported: %w", i, err) } } + if _, err := function.NewPythonTypeDescriptor(planTypeToSQL(call.ReturnType)); err != nil { + return fmt.Errorf("UNSUPPORTED_ROUTINE_VERSION: typed Python return descriptor is unsupported: %w", err) + } return nil } diff --git a/pkg/sql/colexec/external_routine_eval_test.go b/pkg/sql/colexec/external_routine_eval_test.go index 63a54ee27fc5e..31ae3847f95df 100644 --- a/pkg/sql/colexec/external_routine_eval_test.go +++ b/pkg/sql/colexec/external_routine_eval_test.go @@ -226,6 +226,23 @@ func TestExternalRoutineEvalRejectsMissingReturnDescriptor(t *testing.T) { }) } +func TestExternalRoutineEvalRejectsUnsupportedTypeBeforeResultAllocation(t *testing.T) { + proc := testutil.NewProcess(t) + defer proc.Free() + + call := testExternalRoutineCall(t, "SCALAR", udf.NullCallHandler) + call.ReturnType = planpb.Type{Id: int32(types.T_decimal256), Width: 76, Scale: 2} + input := vector.NewVec(types.T_int64.ToType()) + defer input.Free(proc.Mp()) + + require.NotPanics(t, func() { + _, err := newExternalRoutineEval(proc, call, []ExpressionExecutor{ + &externalRoutineTestExecutor{vector: input}, + }, nil) + require.ErrorContains(t, err, "typed Python return descriptor is unsupported") + }) +} + func TestExternalRoutineEvalRejectsExecutionContextInPlan(t *testing.T) { call := testExternalRoutineCall(t, "SCALAR", udf.NullCallHandler) call.Context = map[string]string{"current_user": "attacker"} From 30b174a5835cd4705de13d9e7ddd34ef3393ade0 Mon Sep 17 00:00:00 2001 From: iamlinjunhong <1030420200@qq.com> Date: Fri, 11 Sep 2026 22:34:52 +0800 Subject: [PATCH 063/202] fix(udf): enforce exact external routine selection domain --- pkg/sql/colexec/external_routine_eval.go | 8 ++++++-- pkg/sql/colexec/external_routine_eval_test.go | 19 +++++++++++++++++++ 2 files changed, 25 insertions(+), 2 deletions(-) diff --git a/pkg/sql/colexec/external_routine_eval.go b/pkg/sql/colexec/external_routine_eval.go index e41386047e92f..db8a4537921e0 100644 --- a/pkg/sql/colexec/external_routine_eval.go +++ b/pkg/sql/colexec/external_routine_eval.go @@ -182,8 +182,12 @@ func (e *ExternalRoutineEval) Eval(proc *process.Process, batches []*batch.Batch } return e.result.GetResultVector(), nil } - if selectList != nil && len(selectList) < rowCount { - return nil, fmt.Errorf("external routine selection is shorter than the input batch") + if selectList != nil && len(selectList) != rowCount { + return nil, fmt.Errorf( + "external routine selection has %d rows, expected %d", + len(selectList), + rowCount, + ) } for i, parameter := range e.parameterExecutor { diff --git a/pkg/sql/colexec/external_routine_eval_test.go b/pkg/sql/colexec/external_routine_eval_test.go index 31ae3847f95df..ba187fdb53551 100644 --- a/pkg/sql/colexec/external_routine_eval_test.go +++ b/pkg/sql/colexec/external_routine_eval_test.go @@ -147,6 +147,25 @@ func TestExternalRoutineEvalOwnsSelectionAndStrictNullGuard(t *testing.T) { require.Equal(t, "alice", runtime.invocation.Context["current_user"]) } +func TestExternalRoutineEvalRejectsSelectionWithWrongRowDomain(t *testing.T) { + proc := testutil.NewProcess(t) + defer proc.Free() + + input := vector.NewVec(types.T_int64.ToType()) + defer input.Free(proc.Mp()) + require.NoError(t, vector.AppendFixed(input, int64(1), false, proc.Mp())) + inputBatch := batch.NewWithSize(0) + inputBatch.SetRowCount(1) + evaluator, err := newExternalRoutineEval(proc, testExternalRoutineCall(t, "SCALAR", udf.NullCallHandler), []ExpressionExecutor{ + &externalRoutineTestExecutor{vector: input}, + }, nil) + require.NoError(t, err) + defer evaluator.Free() + + _, err = evaluator.Eval(proc, []*batch.Batch{inputBatch}, []bool{true, false}) + require.ErrorContains(t, err, "selection has 2 rows, expected 1") +} + func TestNewExpressionExecutorDispatchesTypedRoutineCall(t *testing.T) { proc := testutil.NewProcess(t) defer proc.Free() From 16ff99779ea478b6587a24f0d859e5cef993b96d Mon Sep 17 00:00:00 2001 From: iamlinjunhong <1030420200@qq.com> Date: Mon, 14 Sep 2026 10:43:18 +0800 Subject: [PATCH 064/202] fix(udf): transfer external expression result ownership --- pkg/sql/colexec/external_routine_eval.go | 14 ++++++- pkg/sql/colexec/external_routine_eval_test.go | 38 +++++++++++++++++++ 2 files changed, 51 insertions(+), 1 deletion(-) diff --git a/pkg/sql/colexec/external_routine_eval.go b/pkg/sql/colexec/external_routine_eval.go index db8a4537921e0..da90cf800e105 100644 --- a/pkg/sql/colexec/external_routine_eval.go +++ b/pkg/sql/colexec/external_routine_eval.go @@ -153,6 +153,10 @@ func validateRoutineCall(call *planpb.RoutineCall) error { } func (e *ExternalRoutineEval) Eval(proc *process.Process, batches []*batch.Batch, selectList []bool) (*vector.Vector, error) { + return e.eval(proc, batches, selectList, false) +} + +func (e *ExternalRoutineEval) eval(proc *process.Process, batches []*batch.Batch, selectList []bool, transfer bool) (output *vector.Vector, evalErr error) { if e == nil || proc == nil || proc.Base == nil { return nil, fmt.Errorf("python udf: evaluator has no process") } @@ -161,6 +165,14 @@ func (e *ExternalRoutineEval) Eval(proc *process.Process, batches []*batch.Batch if e.freed { return nil, fmt.Errorf("python udf: evaluator is freed") } + // Transfer under executionMu, before a concurrent Eval/Free can reuse the + // backing. The wrapper retains its allocation policy for the next result. + defer func() { + if transfer && evalErr == nil { + e.result.SetResultVector(nil) + } + }() + if err := validateRoutineCall(e.call); err != nil { return nil, err } @@ -462,7 +474,7 @@ func (e *ExternalRoutineEval) scatter(proc *process.Process, selected *vector.Ve } func (e *ExternalRoutineEval) EvalWithoutResultReusing(proc *process.Process, batches []*batch.Batch, selectList []bool) (*vector.Vector, error) { - return e.Eval(proc, batches, selectList) + return e.eval(proc, batches, selectList, true) } func (e *ExternalRoutineEval) ResetForNextQuery() { diff --git a/pkg/sql/colexec/external_routine_eval_test.go b/pkg/sql/colexec/external_routine_eval_test.go index ba187fdb53551..03f3f8f5043e0 100644 --- a/pkg/sql/colexec/external_routine_eval_test.go +++ b/pkg/sql/colexec/external_routine_eval_test.go @@ -402,3 +402,41 @@ func TestExternalRoutineEvalRejectsSourceInExecutablePlan(t *testing.T) { call.GetPython().Source = "def add(ctx, value): return value + 1" require.ErrorContains(t, validateRoutineCall(call), "plan contains source") } + +func TestExternalRoutineTransfersResultOwnership(t *testing.T) { + for _, mode := range []string{"SCALAR", "VECTOR"} { + t.Run(mode, func(t *testing.T) { + proc := testutil.NewProcess(t) + defer proc.Free() + proc.SetQueryId("result-ownership") + proc.GetSessionInfo().TimeZone = time.UTC + proc.GetSessionInfo().User = "root" + proc.Base.StmtProfile = &process.StmtProfile{} + proc.GetStmtProfile().SetQueryStart(time.Date(2026, 9, 14, 0, 0, 0, 0, time.UTC)) + input := vector.NewVec(types.T_int64.ToType()) + defer input.Free(proc.Mp()) + require.NoError(t, vector.AppendFixed(input, int64(10), false, proc.Mp())) + require.NoError(t, vector.AppendFixed(input, int64(20), false, proc.Mp())) + bat := batch.NewWithSize(0) + bat.SetRowCount(2) + proc.Base.UdfService = &externalRoutineCaptureRuntime{} + evaluator, err := newExternalRoutineEval(proc, testExternalRoutineCall(t, mode, udf.NullCallHandler), []ExpressionExecutor{&externalRoutineTestExecutor{vector: input}}, nil) + require.NoError(t, err) + defer evaluator.Free() + owned, err := evaluator.EvalWithoutResultReusing(proc, []*batch.Batch{bat}, []bool{true, false}) + require.NoError(t, err) + // Only free when ownership actually moved, so the pre-fix counterexample + // reports the alias without double freeing the evaluator's vector. + if owned != evaluator.result.GetResultVector() { + defer owned.Free(proc.Mp()) + } + require.NotSame(t, owned, evaluator.result.GetResultVector()) + _, err = evaluator.Eval(proc, []*batch.Batch{bat}, nil) + require.NoError(t, err) + evaluator.Free() + require.Equal(t, 2, owned.Length()) + require.Equal(t, int64(11), vector.GetFixedAtNoTypeCheck[int64](owned, 0)) + require.True(t, owned.IsNull(1)) + }) + } +} From d646a41f806e23b0299eb2787ed919053c54183f Mon Sep 17 00:00:00 2001 From: iamlinjunhong <1030420200@qq.com> Date: Mon, 14 Sep 2026 10:45:13 +0800 Subject: [PATCH 065/202] fix(udf): verify frozen definitions before evaluating arguments --- pkg/sql/colexec/external_routine_eval.go | 21 +++++++++++++++++-- pkg/sql/colexec/external_routine_eval_test.go | 17 +++++++++++++++ 2 files changed, 36 insertions(+), 2 deletions(-) diff --git a/pkg/sql/colexec/external_routine_eval.go b/pkg/sql/colexec/external_routine_eval.go index da90cf800e105..cc90131a427d4 100644 --- a/pkg/sql/colexec/external_routine_eval.go +++ b/pkg/sql/colexec/external_routine_eval.go @@ -24,6 +24,7 @@ import ( planpb "github.com/matrixorigin/matrixone/pkg/pb/plan" "github.com/matrixorigin/matrixone/pkg/sql/plan/function" "github.com/matrixorigin/matrixone/pkg/udf" + pythonudf "github.com/matrixorigin/matrixone/pkg/udf/python" "github.com/matrixorigin/matrixone/pkg/vm/process" ) @@ -138,17 +139,33 @@ func validateRoutineCall(call *planpb.RoutineCall) error { if !udf.IsSHA256Digest(python.DefinitionFingerprint) { return fmt.Errorf("UNSUPPORTED_ROUTINE_VERSION: typed Python implementation has invalid definition fingerprint") } + arguments := make([]pythonudf.TypeDescriptor, len(call.ArgumentTypes)) for i, argumentType := range call.ArgumentTypes { if argumentType == nil { return fmt.Errorf("UNSUPPORTED_ROUTINE_VERSION: typed Python argument %d is nil", i) } - if _, err := function.NewPythonTypeDescriptor(planTypeToSQL(*argumentType)); err != nil { + descriptor, err := function.NewPythonTypeDescriptor(planTypeToSQL(*argumentType)) + if err != nil { return fmt.Errorf("UNSUPPORTED_ROUTINE_VERSION: typed Python argument %d is unsupported: %w", i, err) } + arguments[i] = descriptor } - if _, err := function.NewPythonTypeDescriptor(planTypeToSQL(call.ReturnType)); err != nil { + result, err := function.NewPythonTypeDescriptor(planTypeToSQL(call.ReturnType)) + if err != nil { return fmt.Errorf("UNSUPPORTED_ROUTINE_VERSION: typed Python return descriptor is unsupported: %w", err) } + fingerprint, err := pythonudf.DefinitionFingerprint( + int(python.DefinitionSchemaVersion), python.Handler, python.Mode, python.NullPolicy, + python.AbiContract, python.AdapterVersion, python.ArtifactDigest, + python.EnvironmentDigest, python.SdkVersion, arguments, result, + ) + if err != nil { + return fmt.Errorf("UNSUPPORTED_ROUTINE_VERSION: invalid typed Python definition: %w", err) + } + if fingerprint != python.DefinitionFingerprint { + return fmt.Errorf("UNSUPPORTED_ROUTINE_VERSION: typed Python definition fingerprint mismatch") + } + return nil } diff --git a/pkg/sql/colexec/external_routine_eval_test.go b/pkg/sql/colexec/external_routine_eval_test.go index 03f3f8f5043e0..33982beafb1ce 100644 --- a/pkg/sql/colexec/external_routine_eval_test.go +++ b/pkg/sql/colexec/external_routine_eval_test.go @@ -440,3 +440,20 @@ func TestExternalRoutineTransfersResultOwnership(t *testing.T) { }) } } + +func TestExternalRoutineRejectsDamagedDefinitionBeforeArguments(t *testing.T) { + for _, damage := range []string{"precision", "fingerprint", "argument-type"} { + t.Run(damage, func(t *testing.T) { + call := testExternalRoutineCall(t, "SCALAR", udf.NullCallHandler) + switch damage { + case "precision": + call.ReturnType = planpb.Type{Id: int32(types.T_decimal64), Width: 19, Scale: 2} + case "fingerprint": + call.GetPython().DefinitionFingerprint = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + case "argument-type": + call.ArgumentTypes[0].Id = int32(types.T_int32) + } + require.Error(t, validateRoutineCall(call)) + }) + } +} From f9922a4512cec2f31f408027431b937a48ca92f1 Mon Sep 17 00:00:00 2001 From: iamlinjunhong <1030420200@qq.com> Date: Mon, 14 Sep 2026 10:49:19 +0800 Subject: [PATCH 066/202] fix(udf): skip arguments outside the selected row domain --- pkg/sql/colexec/external_routine_eval.go | 4 ++++ pkg/sql/colexec/external_routine_eval_test.go | 22 +++++++++++++++++++ 2 files changed, 26 insertions(+) diff --git a/pkg/sql/colexec/external_routine_eval.go b/pkg/sql/colexec/external_routine_eval.go index cc90131a427d4..72594708e47cb 100644 --- a/pkg/sql/colexec/external_routine_eval.go +++ b/pkg/sql/colexec/external_routine_eval.go @@ -219,6 +219,10 @@ func (e *ExternalRoutineEval) eval(proc *process.Process, batches []*batch.Batch ) } + if noRowsSelected(selectList, rowCount) { + return e.nullResult(rowCount) + } + for i, parameter := range e.parameterExecutor { var err error e.parameterResults[i], err = parameter.Eval(proc, batches, selectList) diff --git a/pkg/sql/colexec/external_routine_eval_test.go b/pkg/sql/colexec/external_routine_eval_test.go index 33982beafb1ce..36107b842e754 100644 --- a/pkg/sql/colexec/external_routine_eval_test.go +++ b/pkg/sql/colexec/external_routine_eval_test.go @@ -457,3 +457,25 @@ func TestExternalRoutineRejectsDamagedDefinitionBeforeArguments(t *testing.T) { }) } } + +func TestExternalRoutineSkipsArgumentsForEmptySelection(t *testing.T) { + for _, mode := range []string{"SCALAR", "VECTOR"} { + t.Run(mode, func(t *testing.T) { + proc := testutil.NewProcess(t) + defer proc.Free() + input := vector.NewVec(types.T_int64.ToType()) + defer input.Free(proc.Mp()) + require.NoError(t, vector.AppendFixed(input, int64(1), false, proc.Mp())) + seen := 0 + evaluator, err := newExternalRoutineEval(proc, testExternalRoutineCall(t, mode, udf.NullCallHandler), []ExpressionExecutor{&externalRoutineTestExecutor{vector: input, evalSeen: &seen}}, nil) + require.NoError(t, err) + defer evaluator.Free() + bat := batch.NewWithSize(0) + bat.SetRowCount(1) + result, err := evaluator.Eval(proc, []*batch.Batch{bat}, []bool{false}) + require.NoError(t, err) + require.True(t, result.IsNull(0)) + require.Zero(t, seen, "an unselected call must not evaluate any arguments") + }) + } +} From b7f4d779e6732c3558d06431982436e1807f719d Mon Sep 17 00:00:00 2001 From: iamlinjunhong <1030420200@qq.com> Date: Mon, 14 Sep 2026 10:51:28 +0800 Subject: [PATCH 067/202] fix(udf): preserve canonical JSON tokens across adapters --- pkg/udf/python/worker/test_worker.py | 9 +++++++++ pkg/udf/python/worker/worker.py | 25 +++++++++++++++++++++++-- 2 files changed, 32 insertions(+), 2 deletions(-) diff --git a/pkg/udf/python/worker/test_worker.py b/pkg/udf/python/worker/test_worker.py index f30d33304486a..3e3e9925e502f 100644 --- a/pkg/udf/python/worker/test_worker.py +++ b/pkg/udf/python/worker/test_worker.py @@ -708,6 +708,15 @@ def test_json_input_is_canonicalized_before_handler(self): worker._scalar_input(array, 0, descriptor), ) + def test_json_canonical_text_preserves_number_and_string_tokens(self): + descriptor = {"type_id": worker.JSON, "offset_width": 32, "json_encoding": "canonical_text"} + for value in ('1e-7', '1e+20', r'{"escaped":"\u4e2d","number":1e-7}'): + with self.subTest(value=value): + self.assertEqual(value, worker._check_scalar(value, descriptor)) + self.assertEqual(value, worker._scalar_input(pa.array([value]), 0, descriptor)) + self.assertEqual('{"space":"a b","number":1e-7}', + worker._canonical_json_text(' { "space" : "a b", "number" : 1e-7 } ')) + def test_json_rejects_nonstandard_numbers(self): descriptor = {"type_id": worker.JSON, "offset_width": 32, "json_encoding": "canonical_text"} for value in ("NaN", "Infinity", "-Infinity", "1e9999"): diff --git a/pkg/udf/python/worker/worker.py b/pkg/udf/python/worker/worker.py index 20d3a6ec4446b..f77f4b84b984a 100644 --- a/pkg/udf/python/worker/worker.py +++ b/pkg/udf/python/worker/worker.py @@ -1355,14 +1355,35 @@ def reject_nonfinite_float(number: str): return parsed try: - parsed = json.loads( + json.loads( value, parse_constant=reject_nonstandard_number, parse_float=reject_nonfinite_float, ) except Exception as exc: raise ValueError("TYPE_CONTRACT: invalid JSON input") from exc - return json.dumps(parsed, ensure_ascii=False, separators=(",", ":")) + # Match the Gateway's json.Compact contract: remove insignificant + # whitespace without rewriting number or string tokens. A loads/dumps + # round trip changes e.g. 1e-7 to 1e-07 and makes SCALAR and VECTOR + # handlers observe different text for the same SQL value. + compact = [] + in_string = False + escaped = False + for char in value: + if in_string: + compact.append(char) + if escaped: + escaped = False + elif char == "\\": + escaped = True + elif char == '"': + in_string = False + elif char == '"': + in_string = True + compact.append(char) + elif char not in " \t\r\n": + compact.append(char) + return "".join(compact) def _check_json(value: str) -> str: From e87f36131cc681211162de6d4ec230068b219d0d Mon Sep 17 00:00:00 2001 From: iamlinjunhong <1030420200@qq.com> Date: Mon, 14 Sep 2026 10:52:53 +0800 Subject: [PATCH 068/202] fix(udf): avoid re-expanding shared routine query dependencies --- pkg/frontend/routine_plan_validation.go | 8 ++++++++ pkg/frontend/routine_plan_validation_test.go | 12 ++++++++++++ 2 files changed, 20 insertions(+) diff --git a/pkg/frontend/routine_plan_validation.go b/pkg/frontend/routine_plan_validation.go index 0c411455bc538..27101cefe3594 100644 --- a/pkg/frontend/routine_plan_validation.go +++ b/pkg/frontend/routine_plan_validation.go @@ -96,6 +96,7 @@ func routinePlanDependencies(p *planpb.Plan) ([]*planpb.RoutinePlanDependency, e dependencyBytes := 0 stack := []queryFrame{{query: query}} active := map[*planpb.Query]struct{}{query: {}} + completed := make(map[*planpb.Query]struct{}) for len(stack) != 0 { frameIndex := len(stack) - 1 frame := &stack[frameIndex] @@ -119,6 +120,7 @@ func routinePlanDependencies(p *planpb.Plan) ([]*planpb.RoutinePlanDependency, e backgroundQueries := frame.query.GetBackgroundQueries() if frame.next >= len(backgroundQueries) { delete(active, frame.query) + completed[frame.query] = struct{}{} stack = stack[:frameIndex] continue } @@ -133,6 +135,12 @@ func routinePlanDependencies(p *planpb.Plan) ([]*planpb.RoutinePlanDependency, e if _, exists := active[background]; exists { return nil, fmt.Errorf("UNSUPPORTED_ROUTINE_VERSION: routine dependency query graph contains a cycle") } + // Shared background queries contribute their immutable dependencies + // once. Re-expanding a DAG as a tree can turn a small plan into + // exponential work (even when it contains no routine dependencies). + if _, exists := completed[background]; exists { + continue + } active[background] = struct{}{} stack = append(stack, queryFrame{query: background, depth: frame.depth + 1}) } diff --git a/pkg/frontend/routine_plan_validation_test.go b/pkg/frontend/routine_plan_validation_test.go index bcabf01f4dd7d..ab710f975f07b 100644 --- a/pkg/frontend/routine_plan_validation_test.go +++ b/pkg/frontend/routine_plan_validation_test.go @@ -302,3 +302,15 @@ func TestRoutinePlanCatalogIdentityAcceptsAndRejectsSQLRevision(t *testing.T) { udf.SQLDefinitionSchemaVersion, "", "", "", udf.NullCallHandler, )) } + +func TestRoutinePlanDependenciesVisitsSharedQueriesOnce(t *testing.T) { + dependency := testRoutinePlanDependency() + query := &planpb.Query{RoutineDependencies: []*planpb.RoutinePlanDependency{dependency}} + for depth := 0; depth < 12; depth++ { + query = &planpb.Query{BackgroundQueries: []*planpb.Query{query, query}} + } + dependencies, err := routinePlanDependencies(&planpb.Plan{Plan: &planpb.Plan_Query{Query: query}}) + require.NoError(t, err) + require.Len(t, dependencies, 1) + require.Same(t, dependency, dependencies[0]) +} From b5a61f574f22e80e5befdea58fe1321e451642f7 Mon Sep 17 00:00:00 2001 From: iamlinjunhong <1030420200@qq.com> Date: Mon, 14 Sep 2026 10:54:34 +0800 Subject: [PATCH 069/202] fix(udf): own handler pipes before subsequent allocations --- pkg/udf/python/worker/test_worker.py | 35 ++++++++++++++++++++++++++++ pkg/udf/python/worker/worker.py | 6 +++-- 2 files changed, 39 insertions(+), 2 deletions(-) diff --git a/pkg/udf/python/worker/test_worker.py b/pkg/udf/python/worker/test_worker.py index 3e3e9925e502f..d3930d3bf74d2 100644 --- a/pkg/udf/python/worker/test_worker.py +++ b/pkg/udf/python/worker/test_worker.py @@ -1397,6 +1397,41 @@ def test_handler_quota_is_scoped_by_account_and_owner(self): third.release() self.assertEqual(({}, {}), quota.counts()) + def test_handler_initialization_closes_partial_pipe_ownership(self): + real_pipe = os.pipe + for failure in ("second_pipe", "watchdog_dup"): + with self.subTest(failure=failure): + opened = [] + slots = threading.BoundedSemaphore(1) + quota = worker._HandlerQuota(max_account_handlers=1, max_owner_handlers=1) + + def allocate_pipe(): + if failure == "second_pipe" and opened: + raise OSError("injected pipe exhaustion") + pair = real_pipe() + opened.extend(pair) + return pair + + try: + with mock.patch.object(worker.os, "pipe", side_effect=allocate_pipe), mock.patch.object( + worker.os, "dup", side_effect=OSError("injected dup exhaustion") + ), mock.patch.object(worker.subprocess, "Popen") as popen: + with self.assertRaisesRegex(OSError, "injected"): + worker._HandlerProcessSession(slots, handler_quota=quota, account_id=1, owner_id="owner") + popen.assert_not_called() + self.assertEqual(({}, {}), quota.counts()) + self.assertTrue(slots.acquire(blocking=False)) + slots.release() + for fd in opened: + with self.assertRaises(OSError, msg=f"descriptor {fd} leaked after {failure}"): + os.fstat(fd) + finally: + for fd in opened: + try: + os.close(fd) + except OSError: + pass + def test_handler_session_releases_account_owner_quota_idempotently(self): quota = worker._HandlerQuota(max_account_handlers=2, max_owner_handlers=1) session = worker._HandlerProcessSession( diff --git a/pkg/udf/python/worker/worker.py b/pkg/udf/python/worker/worker.py index f77f4b84b984a..f862a62c30f1d 100644 --- a/pkg/udf/python/worker/worker.py +++ b/pkg/udf/python/worker/worker.py @@ -2186,10 +2186,12 @@ def __init__( raise ValueError("RESOURCE_EXHAUSTED: handler execution slots are full") self._slot_acquired = True response_read_fd, response_write_fd = os.pipe() - parent_watch_read_fd, parent_watch_write_fd = os.pipe() - watchdog_read_fd = os.dup(parent_watch_read_fd) + # Publish each acquired descriptor to its cleanup owner before + # the next allocation can fail (in particular under FD pressure). self._response_read_fd = response_read_fd + parent_watch_read_fd, parent_watch_write_fd = os.pipe() self._parent_watch_write_fd = parent_watch_write_fd + watchdog_read_fd = os.dup(parent_watch_read_fd) popen_kwargs = { "stdin": subprocess.PIPE, # Handler output is diagnostic-only. It must never share the From d36fda6e3bdb599d6110326409f5e0bf55c95e43 Mon Sep 17 00:00:00 2001 From: iamlinjunhong <1030420200@qq.com> Date: Mon, 14 Sep 2026 10:55:41 +0800 Subject: [PATCH 070/202] docs(udf): describe CI workers without isolation claims --- pkg/udf/python/worker/requirements.txt | 4 ++-- test/distributed/cases/udf_python/README.md | 8 ++++++-- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/pkg/udf/python/worker/requirements.txt b/pkg/udf/python/worker/requirements.txt index d2a628aab1355..9a2bd0abbb48a 100644 --- a/pkg/udf/python/worker/requirements.txt +++ b/pkg/udf/python/worker/requirements.txt @@ -1,3 +1,3 @@ -# The worker is a separate sandbox process. The runtime image must pin the -# complete environment lock used when the routine revision was published. +# Dependencies for the unisolated external worker adapter. This lock does +# not provide a sandbox or tenant isolation. pyarrow==24.0.0 diff --git a/test/distributed/cases/udf_python/README.md b/test/distributed/cases/udf_python/README.md index 4b253a3642c6f..8e93f01a90baa 100644 --- a/test/distributed/cases/udf_python/README.md +++ b/test/distributed/cases/udf_python/README.md @@ -26,8 +26,10 @@ cd /path/to/mo-tester ``` The ordinary BVT launch starts one worker for the single CN. Compose BVT starts -one isolated worker service per CN from the same pinned image, so adding a SQL -case here does not require another CI workflow. +one dedicated worker service per CN, built from the same Dockerfile and +dependency lock. These workers are unisolated adapters; a separate process or +container does not establish tenant isolation. Adding a SQL case here does +not require another CI workflow. The cases keep the data sets small and target contract boundaries: @@ -47,6 +49,8 @@ The cases keep the data sets small and target contract boundaries: SELECT, and DELETE atomically without publishing partial changes. - `revision_lifecycle.sql` checks create, immutable-revision replacement, prepared-plan invalidation, drop, and recreate on the same SQL signature. +- `vector_mode.sql` checks Arrow VECTOR handlers, zero-argument batch shape, + NULL policies, and handler validation during create and replace. The shape follows established correctness coverage from [DuckDB's scalar UDF tests](https://github.com/duckdb/duckdb-go/blob/main/scalar_udf_test.go) From 1a5ad6d24991a82c6a274559dec32c251427572d Mon Sep 17 00:00:00 2001 From: iamlinjunhong <1030420200@qq.com> Date: Mon, 14 Sep 2026 10:59:23 +0800 Subject: [PATCH 071/202] test(udf): cover canonical JSON through real Flight batches --- pkg/udf/python/gateway_integration_test.go | 47 ++++++++++++++++++++++ 1 file changed, 47 insertions(+) diff --git a/pkg/udf/python/gateway_integration_test.go b/pkg/udf/python/gateway_integration_test.go index 95d46fe22d8db..44bcbdaee67f3 100644 --- a/pkg/udf/python/gateway_integration_test.go +++ b/pkg/udf/python/gateway_integration_test.go @@ -118,6 +118,53 @@ func TestGatewayExecutesAgainstRealPythonWorker(t *testing.T) { require.Equal(t, []int64{11, 12, 13}, vector.MustFixedColNoTypeCheck[int64](result.GetResultVector())) }) + for _, mode := range []string{ModeScalar, ModeVector} { + t.Run("JSON canonical text/"+mode, func(t *testing.T) { + mp := mpool.MustNewZeroNoFixed() + defer mpool.DeleteMPool(mp) + typ := types.T_json.ToType() + input := vector.NewVec(typ) + defer input.Free(mp) + // ByteJson renders this finite number as 0.000001. Python's + // loads/dumps would rewrite it to 1e-06, violating the shared + // compact-text contract before the handler even runs. + value, err := types.ParseStringToByteJson("0.000001") + require.NoError(t, err) + raw, err := value.Marshal() + require.NoError(t, err) + for _, null := range []bool{false, true, false} { + require.NoError(t, vector.AppendBytes(input, raw, null, mp)) + } + result := vector.NewFunctionResultWrapper(typ, mp) + defer result.Free() + invocation := integrationInvocationForArgs( + mode, "identity", "def identity(ctx, value): return value", + []types.Type{typ}, []*vector.Vector{input}, 3, + protocol.FencingTuple{AccountID: 1, StatementID: "json-" + mode, + GroupID: "json-group-" + mode, GroupEpoch: 1, InvocationID: "json-" + mode, LeaseEpoch: 1}, + ) + invocation.ReturnType = typ + descriptor, err := NewTypeDescriptor(typ) + require.NoError(t, err) + invocation.DefinitionFingerprint, err = DefinitionFingerprint( + invocation.DefinitionSchemaVersion, invocation.Handler, invocation.Mode, + invocation.NullPolicy, invocation.ABIContract, invocation.AdapterVersion, + invocation.ArtifactDigest, invocation.EnvironmentDigest, invocation.SDKVersion, + []TypeDescriptor{descriptor}, descriptor, + ) + require.NoError(t, err) + publishIntegrationArtifact(t, artifactStore, invocation) + require.NoError(t, gateway.Execute(context.Background(), invocation, result, mp)) + output := result.GetResultVector() + require.Equal(t, 3, output.Length()) + require.True(t, output.IsNull(1)) + for _, row := range []int{0, 2} { + require.False(t, output.IsNull(uint64(row))) + require.Equal(t, "0.000001", types.DecodeJson(output.GetBytesAt(row)).String()) + } + }) + } + t.Run("zero-argument scalar", func(t *testing.T) { mp := mpool.MustNewZeroNoFixed() defer mpool.DeleteMPool(mp) From 69c2cc6b262330dd89a8676f89126c9fd79ad781 Mon Sep 17 00:00:00 2001 From: iamlinjunhong <1030420200@qq.com> Date: Mon, 14 Sep 2026 13:49:09 +0800 Subject: [PATCH 072/202] perf(udf): reduce scalar batch conversion overhead --- pkg/udf/python/gateway_integration_test.go | 79 ++++++++++++++++++ pkg/udf/python/worker/test_worker.py | 17 ++++ pkg/udf/python/worker/worker.py | 97 ++++++++++++++++++++-- 3 files changed, 188 insertions(+), 5 deletions(-) diff --git a/pkg/udf/python/gateway_integration_test.go b/pkg/udf/python/gateway_integration_test.go index 44bcbdaee67f3..64dec7b9b2b56 100644 --- a/pkg/udf/python/gateway_integration_test.go +++ b/pkg/udf/python/gateway_integration_test.go @@ -554,6 +554,85 @@ def add(ctx, value): }) } +// BenchmarkGatewayRealPythonWorkerLargeBatch compares the two handler modes +// on one 8,192-row batch. The small-batch burst benchmark above is dominated +// by process startup and Flight control traffic; this benchmark makes the +// scalar conversion cost visible while keeping the complete Gateway/Flight/ +// worker/materialization path intact. +func BenchmarkGatewayRealPythonWorkerLargeBatch(b *testing.B) { + python, err := exec.LookPath("python3") + if err != nil { + b.Skip("python3 is required for the real worker benchmark") + } + workerPath := realWorkerPath(b) + port := freeTCPPort(b) + worker := startRealWorker(b, python, workerPath, port) + defer stopRealWorker(worker) + + gateway, err := NewGateway(ClientConfig{ + Enabled: true, + AllowUnisolated: true, + ServerAddress: "127.0.0.1:" + strconv.Itoa(port), + MaxBatchBytes: 1 << 20, + MaxBatchRows: 8192, + MaxActiveInvocations: 2, + MaxInvocationRows: 1 << 20, + MaxInvocationResultBytes: 1 << 20, + RequestTimeout: 30 * time.Second, + MaxTerminalEntries: 1024, + MaxTerminalBytes: 1 << 20, + TerminalRecordTTL: time.Minute, + }) + if err != nil { + b.Fatal(err) + } + defer gateway.Close() + readyContext, cancel := context.WithTimeout(context.Background(), 5*time.Second) + if err := gateway.CheckLanguageReady(readyContext, udf.LanguagePython); err != nil { + cancel() + b.Fatal(err) + } + cancel() + + const rows = 8192 + run := func(b *testing.B, mode, source, label string) { + input, mp := integrationInput(b, make([]int64, rows)) + defer func() { + input.Free(mp) + mpool.DeleteMPool(mp) + }() + result := vector.NewFunctionResultWrapper(types.T_int64.ToType(), mp) + defer result.Free() + b.ReportAllocs() + b.SetBytes(int64(rows * 8)) + b.ResetTimer() + for iteration := 0; iteration < b.N; iteration++ { + runID := realWorkerBenchmarkRun.Add(1) + invocation := integrationInvocationForArgs( + mode, "add", source, []types.Type{types.T_int64.ToType()}, + []*vector.Vector{input}, rows, + protocol.FencingTuple{ + AccountID: 1, StatementID: fmt.Sprintf("large-%s-%d-%d", label, runID, iteration), + GroupID: fmt.Sprintf("large-%s-group-%d-%d", label, runID, iteration), GroupEpoch: 1, + InvocationID: fmt.Sprintf("large-%s-invocation-%d-%d", label, runID, iteration), LeaseEpoch: 1, + }, + ) + if err := gateway.Execute(context.Background(), invocation, result, mp); err != nil { + b.Fatal(err) + } + } + b.StopTimer() + b.ReportMetric(float64(rows*b.N)/float64(b.Elapsed().Nanoseconds())*1e9, "rows/s") + } + + b.Run("scalar", func(b *testing.B) { + run(b, ModeScalar, "def add(ctx, value): return value + 1", "scalar") + }) + b.Run("vector", func(b *testing.B) { + run(b, ModeVector, "import pyarrow.compute as pc\ndef add(ctx, values): return pc.add(values, 1)", "vector") + }) +} + func realWorkerPath(t testing.TB) string { t.Helper() _, sourceFile, _, ok := runtime.Caller(0) diff --git a/pkg/udf/python/worker/test_worker.py b/pkg/udf/python/worker/test_worker.py index d3930d3bf74d2..f32ae1a458e82 100644 --- a/pkg/udf/python/worker/test_worker.py +++ b/pkg/udf/python/worker/test_worker.py @@ -514,6 +514,23 @@ def test_all_type_fixtures_round_trip_scalar_values_and_nulls(self): else: self.assertEqual(value, round_trip, name) + def test_scalar_column_materialization_preserves_fixed_width_values(self): + small_arrays = ( + pa.array([1, None, 3], type=pa.int64()), + pa.array([True, None, False], type=pa.bool_()), + pa.array([1.25, None, 3.5], type=pa.float64()), + ) + for array in small_arrays: + self.assertIsNone(worker._scalar_column_values(array)) + for array in ( + pa.array([1, None] + list(range(2, 64)), type=pa.int64()), + pa.array([True, None] + [False] * 62, type=pa.bool_()), + pa.array([1.25, None] + [float(value) for value in range(2, 64)], type=pa.float64()), + ): + with self.subTest(type=array.type): + self.assertEqual(array.to_pylist(), worker._scalar_column_values(array)) + self.assertIsNone(worker._scalar_column_values(pa.array(["a", None], type=pa.string()))) + def test_go_python_fingerprint_vectors(self): cases = [ ({"type_id": worker.INT64, "offset_width": 32}, "482d3c55b8a9d662c2b41326b3ca55cbae65153e662c47c4919ba020d006262b"), diff --git a/pkg/udf/python/worker/worker.py b/pkg/udf/python/worker/worker.py index f862a62c30f1d..8f2b4c2f0baf0 100644 --- a/pkg/udf/python/worker/worker.py +++ b/pkg/udf/python/worker/worker.py @@ -80,6 +80,11 @@ MAX_ACCOUNT_HANDLER_PROCESSES = MAX_HANDLER_PROCESSES MAX_OWNER_HANDLER_PROCESSES = MAX_HANDLER_PROCESSES // 2 _DEFAULT_HANDLER_SLOTS = threading.BoundedSemaphore(MAX_HANDLER_PROCESSES) +# Materializing a scalar column once is useful for a substantial batch, but +# costs more than indexed access for the tiny batches common in short calls. +# Keep this threshold local to the worker conversion path; it is not a wire +# or admission limit and does not change the handler's batch semantics. +SCALAR_MATERIALIZATION_MIN_ROWS = 64 _CONTROL_KEYS = frozenset( { "version", @@ -1341,6 +1346,54 @@ def _scalar_input(array: pa.Array, row: int, descriptor: Dict[str, Any]): return value +def _scalar_input_value(value: Any, descriptor: Dict[str, Any]): + if value is None: + return None + type_id = int(descriptor["type_id"]) + if type_id in (VECF32, VECF64): + # A scalar vector is exposed as read-only bytes. Consumers can use + # memoryview.cast("f"/"d") without receiving a mutable Arrow buffer. + import struct + values = value + fmt = "f" if type_id == VECF32 else "d" + return memoryview(struct.pack("<" + fmt * len(values), *values)) + if type_id in (DATE, DATETIME, TIMESTAMP): + zero = bool(value["is_zero"]) + child = value["value"] + if type_id == DATE: + return SqlDate(zero, None if zero else child) + if type_id == DATETIME: + return SqlDatetime(zero, None if zero else child) + return SqlTimestamp(zero, None if zero else child) + if type_id == JSON: + return _canonical_json_text(value) + if type_id == UUID: + return _uuid.UUID(bytes=bytes(value)) + return value + + +def _scalar_column_values(array: pa.Array): + """Materialize cheap fixed-width scalar columns once per batch. + + Calling Array.__getitem__().as_py() for every argument and row crosses + the PyArrow/Python boundary repeatedly. Primitive values have a bounded + representation under the batch row/byte limits, so one to_pylist() call + lets the scalar loop reuse those Python objects. Keep small, nested, and + variable-width values on the indexed path: that avoids retaining a second + copy of user data while preserving the same per-row conversion contract. + """ + if len(array) < SCALAR_MATERIALIZATION_MIN_ROWS: + return None + data_type = array.type + if ( + pa.types.is_boolean(data_type) + or pa.types.is_integer(data_type) + or pa.types.is_floating(data_type) + ): + return array.to_pylist() + return None + + def _canonical_json_text(value: str) -> str: if not isinstance(value, str): raise ValueError("TYPE_CONTRACT: JSON value must be text") @@ -1678,12 +1731,46 @@ def _execute_handler_batch( else: call_context = ScalarContext(sdk_version, _CallLogger(), statement_context) values = [] - for row in range(batch.num_rows): - params = [_scalar_input(batch.column(index), row, args[index]) for index in range(batch.num_columns)] - if null_policy == NULL_RETURN and any(value is None for value in params): - values.append(None) + if batch.num_rows < SCALAR_MATERIALIZATION_MIN_ROWS: + for row in range(batch.num_rows): + params = [ + _scalar_input(batch.column(index), row, args[index]) + for index in range(batch.num_columns) + ] + if null_policy == NULL_RETURN and any(value is None for value in params): + values.append(None) + else: + values.append(handler(call_context, *params)) + else: + columns = [batch.column(index) for index in range(batch.num_columns)] + scalar_columns = [ + _scalar_column_values(column) for column in columns + ] + if not any(column_values is not None for column_values in scalar_columns): + for row in range(batch.num_rows): + params = [ + _scalar_input(columns[index], row, args[index]) + for index in range(batch.num_columns) + ] + if null_policy == NULL_RETURN and any(value is None for value in params): + values.append(None) + else: + values.append(handler(call_context, *params)) else: - values.append(handler(call_context, *params)) + for row in range(batch.num_rows): + params = [ + _scalar_input_value( + column_values[row] + if column_values is not None + else columns[index][row].as_py(), + args[index], + ) + for index, column_values in enumerate(scalar_columns) + ] + if null_policy == NULL_RETURN and any(value is None for value in params): + values.append(None) + else: + values.append(handler(call_context, *params)) output = values output_array = _output_array(output, result_descriptor, batch.num_rows) if result_schema is None: From e6378a6439dd8ab8726a89a10dbe19f56ebdc42e Mon Sep 17 00:00:00 2001 From: iamlinjunhong <1030420200@qq.com> Date: Mon, 14 Sep 2026 13:51:47 +0800 Subject: [PATCH 073/202] refactor(udf): share scalar conversion contract --- pkg/udf/python/worker/worker.py | 25 +------------------------ 1 file changed, 1 insertion(+), 24 deletions(-) diff --git a/pkg/udf/python/worker/worker.py b/pkg/udf/python/worker/worker.py index 8f2b4c2f0baf0..8efe0b9a041bd 100644 --- a/pkg/udf/python/worker/worker.py +++ b/pkg/udf/python/worker/worker.py @@ -1320,30 +1320,7 @@ def _load_handler(source: str, handler: str): def _scalar_input(array: pa.Array, row: int, descriptor: Dict[str, Any]): - value = array[row].as_py() - if value is None: - return None - type_id = int(descriptor["type_id"]) - if type_id in (VECF32, VECF64): - # A scalar vector is exposed as read-only bytes. Consumers can use - # memoryview.cast("f"/"d") without receiving a mutable Arrow buffer. - import struct - values = value - fmt = "f" if type_id == VECF32 else "d" - return memoryview(struct.pack("<" + fmt * len(values), *values)) - if type_id in (DATE, DATETIME, TIMESTAMP): - zero = bool(value["is_zero"]) - child = value["value"] - if type_id == DATE: - return SqlDate(zero, None if zero else child) - if type_id == DATETIME: - return SqlDatetime(zero, None if zero else child) - return SqlTimestamp(zero, None if zero else child) - if type_id == JSON: - return _canonical_json_text(value) - if type_id == UUID: - return _uuid.UUID(bytes=bytes(value)) - return value + return _scalar_input_value(array[row].as_py(), descriptor) def _scalar_input_value(value: Any, descriptor: Dict[str, Any]): From a5d55ffde6948aa3ef874df591220a6a8d4a8e37 Mon Sep 17 00:00:00 2001 From: iamlinjunhong <1030420200@qq.com> Date: Mon, 14 Sep 2026 14:01:51 +0800 Subject: [PATCH 074/202] perf(udf): trim handler process startup --- pkg/udf/python/worker/Dockerfile | 2 +- pkg/udf/python/worker/watchdog.py | 45 ++++++++++++++++ pkg/udf/python/worker/worker.py | 90 +++++++++++++------------------ 3 files changed, 83 insertions(+), 54 deletions(-) create mode 100644 pkg/udf/python/worker/watchdog.py diff --git a/pkg/udf/python/worker/Dockerfile b/pkg/udf/python/worker/Dockerfile index 3fdb7d5528114..0a3aab23067dc 100644 --- a/pkg/udf/python/worker/Dockerfile +++ b/pkg/udf/python/worker/Dockerfile @@ -14,7 +14,7 @@ WORKDIR /home/mo # && apt-get update \ # && apt-get install -y vim -COPY requirements.txt worker.py ./ +COPY requirements.txt worker.py watchdog.py ./ RUN pip install --no-cache-dir -r requirements.txt diff --git a/pkg/udf/python/worker/watchdog.py b/pkg/udf/python/worker/watchdog.py new file mode 100644 index 0000000000000..25c417a73b7d4 --- /dev/null +++ b/pkg/udf/python/worker/watchdog.py @@ -0,0 +1,45 @@ +# Copyright 2026 Matrix Origin +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. + +"""Minimal process-group liveness watchdog for a Python UDF handler.""" + +import os +import signal +import sys + + +def _watch(read_fd: int, process_group_id: int) -> None: + try: + while True: + if os.read(read_fd, 1) == b"": + try: + os.killpg(process_group_id, signal.SIGKILL) + except ProcessLookupError: + pass + return + except (OSError, ValueError): + return + finally: + try: + os.close(read_fd) + except OSError: + pass + + +def main() -> None: + if len(sys.argv) != 3: + raise SystemExit("usage: watchdog.py READ_FD PROCESS_GROUP_ID") + try: + read_fd = int(sys.argv[1]) + process_group_id = int(sys.argv[2]) + except ValueError as exc: + raise SystemExit("watchdog arguments must be integers") from exc + if read_fd < 0 or process_group_id <= 0: + raise SystemExit("watchdog arguments are out of range") + _watch(read_fd, process_group_id) + + +if __name__ == "__main__": + main() diff --git a/pkg/udf/python/worker/worker.py b/pkg/udf/python/worker/worker.py index 8efe0b9a041bd..6d68d887b296f 100644 --- a/pkg/udf/python/worker/worker.py +++ b/pkg/udf/python/worker/worker.py @@ -41,7 +41,24 @@ from zoneinfo import TZPATH, ZoneInfo, ZoneInfoNotFoundError import pyarrow as pa -import pyarrow.flight as flight + +# Handler children only need Arrow. Avoid importing Flight on that entry path; +# the annotations in this module are postponed, and the Flight server class is +# never instantiated by the handler child. +_HANDLER_ENTRY = "--execute-handler" in sys.argv + + +class _NoFlightServerBase: + pass + + +if _HANDLER_ENTRY: + flight = None + _FlightServerBase = _NoFlightServerBase +else: + import pyarrow.flight as flight + + _FlightServerBase = flight.FlightServerBase PROTOCOL_VERSION = 1 MAX_CONTROL_BYTES = 1 << 20 @@ -193,7 +210,6 @@ _HANDLER_RESPONSE_OK = 1 _HANDLER_RESPONSE_FD_ENV = "MATRIXONE_HANDLER_RESPONSE_FD" _HANDLER_PARENT_WATCH_FD_ENV = "MATRIXONE_HANDLER_PARENT_WATCH_FD" -_HANDLER_WATCHDOG_FD_ENV = "MATRIXONE_HANDLER_WATCHDOG_FD" _HANDLER_ENV_ALLOWLIST = frozenset({"PATH"}) _MICROS_PER_SECOND = 1_000_000 _MAX_TIME_MICROS = (838 * 60 * 60 + 59 * 60 + 59) * _MICROS_PER_SECOND @@ -1881,34 +1897,6 @@ def _watch_parent_liveness(read_fd: int) -> None: os._exit(137) -def _watch_handler_group(read_fd: int, process_group_id: int) -> None: - """Reap a handler process group even after its leader has exited. - - This watchdog is a worker-owned reliability process, not a security - boundary. It has no user-code imports and only has the group id plus a - read end of a worker-owned pipe. If the worker disappears, the pipe gets - EOF and the watchdog kills the whole handler process group. Keeping this - responsibility outside the handler leader closes the case where the - leader exits first while a descendant keeps running. - """ - try: - while True: - if os.read(read_fd, 1) == b"": - if os.name == "posix": - try: - os.killpg(process_group_id, signal.SIGKILL) - except ProcessLookupError: - pass - return - except (OSError, ValueError): - return - finally: - try: - os.close(read_fd) - except OSError: - pass - - def _execute_handler_subprocess() -> None: response_fd_text = os.environ.pop(_HANDLER_RESPONSE_FD_ENV, None) parent_watch_fd_text = os.environ.pop(_HANDLER_PARENT_WATCH_FD_ENV, None) @@ -2289,17 +2277,19 @@ def __init__( os.close(parent_watch_read_fd) parent_watch_read_fd = -1 if os.name == "posix": + watchdog_path = os.path.join( + os.path.dirname(os.path.abspath(__file__)), "watchdog.py" + ) watchdog_env = { name: value for name, value in os.environ.items() if name in _HANDLER_ENV_ALLOWLIST } - watchdog_env[_HANDLER_WATCHDOG_FD_ENV] = str(watchdog_read_fd) self._watchdog_process = subprocess.Popen( [ sys.executable, - os.path.abspath(__file__), - "--watch-handler-group", + watchdog_path, + str(watchdog_read_fd), str(self._process.pid), ], stdin=subprocess.DEVNULL, @@ -2739,7 +2729,7 @@ def wait_finish_ack(self, context=None) -> None: self.condition.wait(min(remaining, 0.2)) -class RoutineFlightServer(flight.FlightServerBase): +class RoutineFlightServer(_FlightServerBase): def __init__( self, location: str, @@ -3324,6 +3314,19 @@ def do_exchange(self, context, descriptor, reader, writer): for index, descriptor in enumerate(args): _validate_array_values(batch.column(index), descriptor) state.record_input(sequence) + if handler_session is None: + # Start the bounded child while the parent builds the + # handler request. The child imports the frozen + # runtime concurrently with this Arrow snapshot; + # admission and all input validation still happen + # before a handler slot is acquired. + owner_id = _handler_quota_owner(payload) + handler_session = _HandlerProcessSession( + self._handler_slots, + handler_quota=self._handler_quota, + account_id=key[0], + owner_id=owner_id, + ) execution_request = { "source": source, "handler": handler_name, @@ -3346,14 +3349,6 @@ def do_exchange(self, context, descriptor, reader, writer): "arrow_encoding": HANDLER_ARROW_RECORD_BATCH, "input": _serialize_record_batch_message(batch), } - if handler_session is None: - owner_id = _handler_quota_owner(payload) - handler_session = _HandlerProcessSession( - self._handler_slots, - handler_quota=self._handler_quota, - account_id=key[0], - owner_id=owner_id, - ) output_wire = handler_session.run( context, execution_request, handler_timeout_seconds ) @@ -3463,22 +3458,11 @@ def _safe_error(exc: Exception) -> str: def main() -> None: parser = argparse.ArgumentParser() parser.add_argument("--execute-handler", action="store_true") - parser.add_argument("--watch-handler-group", type=int) parser.add_argument("--address") args = parser.parse_args() if args.execute_handler: _execute_handler_subprocess() return - if args.watch_handler_group is not None: - watchdog_fd_text = os.environ.pop(_HANDLER_WATCHDOG_FD_ENV, None) - if watchdog_fd_text is None: - parser.error("--watch-handler-group requires a watchdog pipe") - try: - watchdog_fd = int(watchdog_fd_text) - except ValueError: - parser.error("invalid watchdog pipe") - _watch_handler_group(watchdog_fd, args.watch_handler_group) - return if not args.address: parser.error("--address is required for the Flight server") logging.basicConfig(level=logging.INFO, format="[%(asctime)s] %(levelname)s %(message)s") From 0e528b569fe8c99fd433945ad7a8cc10bc7c290f Mon Sep 17 00:00:00 2001 From: iamlinjunhong <1030420200@qq.com> Date: Mon, 14 Sep 2026 14:37:36 +0800 Subject: [PATCH 075/202] perf(udf): trim bounded handler IPC frames --- pkg/udf/python/worker/test_worker.py | 33 ++++++++++ pkg/udf/python/worker/worker.py | 93 +++++++++++++++++++++------- 2 files changed, 105 insertions(+), 21 deletions(-) diff --git a/pkg/udf/python/worker/test_worker.py b/pkg/udf/python/worker/test_worker.py index f32ae1a458e82..49010aa693101 100644 --- a/pkg/udf/python/worker/test_worker.py +++ b/pkg/udf/python/worker/test_worker.py @@ -2,6 +2,7 @@ import datetime import decimal +import io import importlib.util import json import os @@ -1244,6 +1245,20 @@ def test_handler_frame_keeps_arrow_bytes_out_of_pickle_metadata(self): self.assertIsInstance(metadata["input"], pickle.PickleBuffer) self.assertEqual(arrow_wire, bytes(metadata["input"].raw())) + compact_parts, compact_size = worker._encode_execution_request( + {"source": "must-not-repeat", "input": arrow_wire}, compact=True + ) + compact_metadata = pickle.loads( + compact_parts[2], buffers=[pickle.PickleBuffer(compact_parts[3])] + ) + self.assertEqual( + worker._HANDLER_REQUEST_BATCH, + compact_metadata[worker._HANDLER_REQUEST_KIND_KEY], + ) + self.assertNotIn("source", compact_metadata) + self.assertEqual(arrow_wire, bytes(compact_metadata["input"].raw())) + self.assertLess(compact_size, payload_size) + def test_handler_request_reader_accepts_fragmented_headers(self): parts, _ = worker._encode_execution_request( { @@ -1273,6 +1288,24 @@ def test_handler_request_encoder_rejects_non_contiguous_arrow_buffer(self): {"input": memoryview(b"arrow-payload")[::2]} ) + def test_handler_response_frame_separates_status_from_payload(self): + session = object.__new__(worker._HandlerProcessSession) + session._response_buffer = bytearray(struct.pack(">Q", 4) + b"\x01abc") + self.assertEqual( + (worker._HANDLER_RESPONSE_OK, b"abc"), session._take_response() + ) + self.assertEqual(bytearray(), session._response_buffer) + session._response_buffer = bytearray(struct.pack(">Q", 0)) + with self.assertRaisesRegex(ValueError, "empty response"): + session._take_response() + + def test_handler_response_frame_parts_keep_one_wire_frame(self): + stream = io.BytesIO() + worker._write_execution_frame_parts( + stream, (b"\x01", memoryview(b"abc")) + ) + self.assertEqual(struct.pack(">Q", 4) + b"\x01abc", stream.getvalue()) + def test_handler_process_accepts_schema_free_record_batch_messages(self): descriptor = {"type_id": worker.INT64, "offset_width": 32} schema = worker._schema_from_descriptors([descriptor], "arg") diff --git a/pkg/udf/python/worker/worker.py b/pkg/udf/python/worker/worker.py index 6d68d887b296f..b06500b57a1cd 100644 --- a/pkg/udf/python/worker/worker.py +++ b/pkg/udf/python/worker/worker.py @@ -208,6 +208,9 @@ class _NoFlightServerBase: _DESCRIPTOR_TEXT_KEYS = frozenset({"json_encoding", "temporal_encoding"}) _HANDLER_RESPONSE_ERROR = 0 _HANDLER_RESPONSE_OK = 1 +_HANDLER_REQUEST_KIND_KEY = "__matrixone_handler_request_kind" +_HANDLER_REQUEST_FULL = "full" +_HANDLER_REQUEST_BATCH = "batch" _HANDLER_RESPONSE_FD_ENV = "MATRIXONE_HANDLER_RESPONSE_FD" _HANDLER_PARENT_WATCH_FD_ENV = "MATRIXONE_HANDLER_PARENT_WATCH_FD" _HANDLER_ENV_ALLOWLIST = frozenset({"PATH"}) @@ -1788,15 +1791,22 @@ def _read_exact(stream, size: int) -> bytes: return bytes(result) -def _write_execution_frame(stream, payload: bytes) -> None: - if len(payload) > MAX_EXECUTION_FRAME_BYTES: +def _write_execution_frame_parts(stream, parts: Iterable[bytes]) -> None: + parts = tuple(parts) + payload_size = sum(len(part) for part in parts) + if payload_size > MAX_EXECUTION_FRAME_BYTES: raise ValueError("RESOURCE_EXHAUSTED: execution frame is too large") - stream.write(struct.pack(">Q", len(payload))) - stream.write(payload) + stream.write(struct.pack(">Q", payload_size)) + for part in parts: + stream.write(part) stream.flush() -def _encode_execution_request(request: Dict[str, Any]): +def _write_execution_frame(stream, payload: bytes) -> None: + _write_execution_frame_parts(stream, (payload,)) + + +def _encode_execution_request(request: Dict[str, Any], *, compact: bool = False): """Build a handler frame without copying Arrow bytes into pickle. The outer length bounds the complete frame. The first eight bytes of the @@ -1813,7 +1823,11 @@ def _encode_execution_request(request: Dict[str, Any]): raise ValueError("PROTOCOL: execution request Arrow batch is not bytes-like") if not input_wire: raise ValueError("PROTOCOL: execution request Arrow batch is empty") - metadata = dict(request) + if compact: + metadata = {_HANDLER_REQUEST_KIND_KEY: _HANDLER_REQUEST_BATCH} + else: + metadata = dict(request) + metadata[_HANDLER_REQUEST_KIND_KEY] = _HANDLER_REQUEST_FULL buffers = [] try: metadata["input"] = pickle.PickleBuffer(input_wire) @@ -1920,6 +1934,7 @@ def _execute_handler_subprocess() -> None: # frozen contract; input Arrow batches remain independently validated # by the parent and by _execute_handler_batch. frozen = None + frozen_request = None handler = None statement_context = None input_schema = None @@ -1930,6 +1945,20 @@ def _execute_handler_subprocess() -> None: if request is None: # EOF before a new frame is the normal burst shutdown. break + request_kind = request.get(_HANDLER_REQUEST_KIND_KEY) + if request_kind not in ( + _HANDLER_REQUEST_FULL, + _HANDLER_REQUEST_BATCH, + ): + raise ValueError("PROTOCOL: handler request kind is invalid") + if request_kind == _HANDLER_REQUEST_BATCH: + if frozen is None or frozen_request is None: + raise ValueError( + "PROTOCOL: compact handler request arrived before the full contract" + ) + batch_input = request.get("input") + request = dict(frozen_request) + request["input"] = batch_input contract = { key: request.get(key) for key in ( @@ -1942,6 +1971,12 @@ def _execute_handler_subprocess() -> None: } if frozen is None: frozen = contract + # Do not retain the first Arrow batch while the child is + # reused. The immutable contract is enough to reconstruct + # later compact requests. + frozen_request = { + key: value for key, value in request.items() if key != "input" + } statement_context = _statement_context(request.get("context")) handler = _load_handler(request["source"], request["handler"]) if request.get("arrow_encoding") == HANDLER_ARROW_RECORD_BATCH: @@ -1949,19 +1984,25 @@ def _execute_handler_subprocess() -> None: result_schema = pa.schema([_field("result", request["return"])]) elif contract != frozen: raise ValueError("PROTOCOL: handler burst contract changed") - response = bytes([_HANDLER_RESPONSE_OK]) + _execute_handler_batch( + output = _execute_handler_batch( request, handler=handler, statement_context=statement_context, input_schema=input_schema, result_schema=result_schema, ) + response_parts = (bytes([_HANDLER_RESPONSE_OK]), output) + succeeded = True except Exception as exc: - response = bytes([_HANDLER_RESPONSE_ERROR]) + _safe_error(exc).encode("utf-8") + response_parts = ( + bytes([_HANDLER_RESPONSE_ERROR]), + _safe_error(exc).encode("utf-8"), + ) + succeeded = False # This descriptor is created by the adapter and is distinct from # process stdout/stderr descriptors available to handler code. - _write_execution_frame(response_stream, response) - if response[0] == _HANDLER_RESPONSE_ERROR: + _write_execution_frame_parts(response_stream, response_parts) + if not succeeded: break finally: response_stream.close() @@ -2335,7 +2376,7 @@ def should_rollover(self) -> bool: or time.monotonic() - self._burst_started >= DEFAULT_BURST_SECONDS ) - def _take_response(self) -> Optional[bytes]: + def _take_response(self) -> Optional[tuple[int, bytes]]: if len(self._response_buffer) < 8: return None expected = struct.unpack(">Q", self._response_buffer[:8])[0] @@ -2343,9 +2384,19 @@ def _take_response(self) -> Optional[bytes]: raise ValueError("RESOURCE_EXHAUSTED: execution response is too large") if len(self._response_buffer) < expected + 8: return None - payload = bytes(self._response_buffer[8 : expected + 8]) + if expected < 1: + raise ValueError("PROTOCOL: handler process returned an empty response") + status = self._response_buffer[8] + # A memoryview avoids an intermediate bytearray slice. Release it + # before resizing the receive buffer below; the returned Arrow bytes + # remain the only copy needed by the parent decoder. + response_view = memoryview(self._response_buffer) + try: + payload = bytes(response_view[9 : expected + 8]) + finally: + response_view.release() del self._response_buffer[: expected + 8] - return payload + return status, payload def run(self, context, request: Dict[str, Any], timeout_seconds: float) -> bytes: if self._closed or self._process is None or self._selector is None: @@ -2356,7 +2407,9 @@ def run(self, context, request: Dict[str, Any], timeout_seconds: float) -> bytes # unbounded part of the caller's budget before the deadline was even # installed. deadline = time.monotonic() + timeout_seconds - request_parts, request_size = _encode_execution_request(request) + request_parts, request_size = _encode_execution_request( + request, compact=self._burst_batches > 0 + ) if time.monotonic() >= deadline: raise TimeoutError("DEADLINE_EXCEEDED: handler execution timeout") request_part_index = 0 @@ -2411,19 +2464,17 @@ def run(self, context, request: Dict[str, Any], timeout_seconds: float) -> bytes if _execution_group_alive(self._process): raise ValueError("USER_CODE: handler process left descendant processes") raise ValueError("USER_CODE: handler process exited before the burst completed") - if not response: - raise ValueError("PROTOCOL: handler process returned an empty response") - if response[0] == _HANDLER_RESPONSE_ERROR: + status, output = response + if status == _HANDLER_RESPONSE_ERROR: try: - message = response[1:].decode("utf-8") + message = output.decode("utf-8") except UnicodeDecodeError as exc: raise ValueError("PROTOCOL: handler process returned an invalid error") from exc raise ValueError(message or "USER_CODE: handler process failed") - if response[0] != _HANDLER_RESPONSE_OK: + if status != _HANDLER_RESPONSE_OK: raise ValueError("PROTOCOL: handler process returned an unknown status") - output = response[1:] self._burst_batches += 1 - self._burst_bytes += request_size + len(response) + self._burst_bytes += request_size + len(output) + 1 return output finally: # A failed write/read must not leave a stale registration that a From 15a74314601c964f56a4dd99cc381373c02c7ec7 Mon Sep 17 00:00:00 2001 From: iamlinjunhong <1030420200@qq.com> Date: Mon, 14 Sep 2026 14:37:43 +0800 Subject: [PATCH 076/202] perf(udf): avoid Arrow result stream copies --- pkg/udf/python/arrow.go | 262 +++++++++++++++++++++++------------ pkg/udf/python/arrow_test.go | 181 ++++++++++++++++++++++++ 2 files changed, 357 insertions(+), 86 deletions(-) diff --git a/pkg/udf/python/arrow.go b/pkg/udf/python/arrow.go index 5697fbc8b22bf..84955c57178bd 100644 --- a/pkg/udf/python/arrow.go +++ b/pkg/udf/python/arrow.go @@ -23,6 +23,7 @@ import ( "encoding/json" "errors" "fmt" + "io" "math" "reflect" "unicode/utf8" @@ -34,6 +35,7 @@ import ( "github.com/apache/arrow-go/v18/arrow/ipc" "github.com/apache/arrow-go/v18/arrow/memory" "github.com/matrixorigin/matrixone/pkg/common/mpool" + commonutil "github.com/matrixorigin/matrixone/pkg/common/util" "github.com/matrixorigin/matrixone/pkg/container/arrowipc" "github.com/matrixorigin/matrixone/pkg/container/bytejson" "github.com/matrixorigin/matrixone/pkg/container/types" @@ -919,11 +921,13 @@ func DecodeRecordBatch(schemaFrame, batchFrame ArrowFrame, maxBytes int64) (arro if batchInfo.HeaderType != arrowipc.MessageHeaderRecordBatch { return nil, fmt.Errorf("invalid Arrow record frame header %d", batchInfo.HeaderType) } - stream := make([]byte, 0, len(schemaFrame.Header)+len(batchFrame.Header)+len(batchFrame.Body)+32) - stream = appendIPCFrame(stream, schemaFrame.Header, schemaFrame.Body) - stream = appendIPCFrame(stream, batchFrame.Header, batchFrame.Body) - stream = append(stream, 0, 0, 0, 0, 0, 0, 0, 0) - reader, err := ipc.NewReader(bytes.NewReader(stream), ipc.WithAllocator(memory.NewGoAllocator())) + // The snapshot is already the immutable validation/publication backing. + // Feed that backing directly to Arrow's stream reader instead of assembling + // another stream-sized byte slice around it. The small IPC continuation + // prefixes and alignment padding are separate readers; the body remains + // owned by the validated snapshot until Arrow releases the record. + stream := newIPCStreamReader(schemaFrame, batchFrame) + reader, err := ipc.NewReader(stream, ipc.WithAllocator(memory.NewGoAllocator())) if err != nil { return nil, fmt.Errorf("decode Arrow output: %w", err) } @@ -938,21 +942,57 @@ func DecodeRecordBatch(schemaFrame, batchFrame ArrowFrame, maxBytes int64) (arro record.Retain() return record, nil } -func appendIPCFrame(dst, header, body []byte) []byte { - metadataLength := (len(header) + 7) &^ 7 - var prefix [8]byte - binary.LittleEndian.PutUint32(prefix[:4], math.MaxUint32) - binary.LittleEndian.PutUint32(prefix[4:], uint32(metadataLength)) - dst = append(dst, prefix[:]...) - dst = append(dst, header...) - for len(dst)%8 != 0 { - dst = append(dst, 0) - } - dst = append(dst, body...) - for len(dst)%8 != 0 { - dst = append(dst, 0) - } - return dst + +var ipcZeroPadding [8]byte + +func newIPCStreamReader(frames ...ArrowFrame) io.Reader { + parts := make([][]byte, 0, len(frames)*5+1) + for _, frame := range frames { + metadataLength := (len(frame.Header) + 7) &^ 7 + bodyLength := (len(frame.Body) + 7) &^ 7 + var prefix [8]byte + binary.LittleEndian.PutUint32(prefix[:4], math.MaxUint32) + binary.LittleEndian.PutUint32(prefix[4:], uint32(metadataLength)) + parts = append(parts, + prefix[:], + frame.Header, + ipcZeroPadding[:metadataLength-len(frame.Header)], + frame.Body, + ipcZeroPadding[:bodyLength-len(frame.Body)], + ) + } + // Arrow's stream reader uses an eight-byte zero continuation marker to + // observe the end of the stream after the one record batch. + parts = append(parts, ipcZeroPadding[:]) + return &ipcStreamReader{parts: parts} +} + +type ipcStreamReader struct { + parts [][]byte + part int + offset int +} + +func (r *ipcStreamReader) Read(dst []byte) (int, error) { + if len(dst) == 0 { + return 0, nil + } + read := 0 + for read < len(dst) && r.part < len(r.parts) { + source := r.parts[r.part] + if r.offset >= len(source) { + r.part++ + r.offset = 0 + continue + } + copied := copy(dst[read:], source[r.offset:]) + r.offset += copied + read += copied + } + if read != 0 { + return read, nil + } + return 0, io.EOF } func AppendArrowResult(descriptor TypeDescriptor, input arrow.Array, result vector.FunctionResultWrapper, mp *mpool.MPool) error { @@ -976,53 +1016,39 @@ func AppendArrowResult(descriptor TypeDescriptor, input arrow.Array, result vect } result.GetResultVector().SetTypeScale(descriptor.Scale) typ := types.T(descriptor.TypeID) + switch typ { + case types.T_bool: + return appendBoolArrowResult(input.(*array.Boolean), result) + case types.T_int8: + return appendFixedArrowValues(input.(*array.Int8).Int8Values(), input, result) + case types.T_int16: + return appendFixedArrowValues(input.(*array.Int16).Int16Values(), input, result) + case types.T_int32: + return appendFixedArrowValues(input.(*array.Int32).Int32Values(), input, result) + case types.T_int64: + return appendFixedArrowValues(input.(*array.Int64).Int64Values(), input, result) + case types.T_uint8: + return appendFixedArrowValues(input.(*array.Uint8).Uint8Values(), input, result) + case types.T_uint16: + return appendFixedArrowValues(input.(*array.Uint16).Uint16Values(), input, result) + case types.T_uint32: + return appendFixedArrowValues(input.(*array.Uint32).Uint32Values(), input, result) + case types.T_uint64: + return appendFixedArrowValues(input.(*array.Uint64).Uint64Values(), input, result) + case types.T_float32: + return appendFixedArrowValues(input.(*array.Float32).Float32Values(), input, result) + case types.T_float64: + return appendFixedArrowValues(input.(*array.Float64).Float64Values(), input, result) + case types.T_char, types.T_varchar, types.T_text: + return appendStringArrowResult(input.(*array.String), result) + case types.T_json: + return appendJSONArrowResult(input.(*array.String), result) + case types.T_binary, types.T_varbinary, types.T_blob: + return appendBinaryArrowResult(input.(*array.Binary), result) + } for i := 0; i < input.Len(); i++ { null := input.IsNull(i) switch typ { - case types.T_bool: - if err := vector.MustFunctionResult[bool](result).Append(input.(*array.Boolean).Value(i), null); err != nil { - return err - } - case types.T_int8: - if err := vector.MustFunctionResult[int8](result).Append(input.(*array.Int8).Value(i), null); err != nil { - return err - } - case types.T_int16: - if err := vector.MustFunctionResult[int16](result).Append(input.(*array.Int16).Value(i), null); err != nil { - return err - } - case types.T_int32: - if err := vector.MustFunctionResult[int32](result).Append(input.(*array.Int32).Value(i), null); err != nil { - return err - } - case types.T_int64: - if err := vector.MustFunctionResult[int64](result).Append(input.(*array.Int64).Value(i), null); err != nil { - return err - } - case types.T_uint8: - if err := vector.MustFunctionResult[uint8](result).Append(input.(*array.Uint8).Value(i), null); err != nil { - return err - } - case types.T_uint16: - if err := vector.MustFunctionResult[uint16](result).Append(input.(*array.Uint16).Value(i), null); err != nil { - return err - } - case types.T_uint32: - if err := vector.MustFunctionResult[uint32](result).Append(input.(*array.Uint32).Value(i), null); err != nil { - return err - } - case types.T_uint64: - if err := vector.MustFunctionResult[uint64](result).Append(input.(*array.Uint64).Value(i), null); err != nil { - return err - } - case types.T_float32: - if err := vector.MustFunctionResult[float32](result).Append(input.(*array.Float32).Value(i), null); err != nil { - return err - } - case types.T_float64: - if err := vector.MustFunctionResult[float64](result).Append(input.(*array.Float64).Value(i), null); err != nil { - return err - } case types.T_decimal64: var value types.Decimal64 if !null { @@ -1047,28 +1073,6 @@ func AppendArrowResult(descriptor TypeDescriptor, input arrow.Array, result vect if err := vector.MustFunctionResult[types.Decimal128](result).Append(value, null); err != nil { return err } - case types.T_char, types.T_varchar, types.T_text: - if err := vector.MustFunctionResult[types.Varlena](result).AppendBytes([]byte(input.(*array.String).Value(i)), null); err != nil { - return err - } - case types.T_json: - if null { - if err := vector.MustFunctionResult[types.Varlena](result).AppendBytes(nil, true); err != nil { - return err - } - continue - } - encoded, err := bytejson.ParseJsonByteFromString(input.(*array.String).Value(i)) - if err != nil { - return fmt.Errorf("TYPE_CONTRACT: invalid JSON result: %w", err) - } - if err = vector.MustFunctionResult[types.Varlena](result).AppendBytes(encoded, false); err != nil { - return err - } - case types.T_binary, types.T_varbinary, types.T_blob: - if err := vector.MustFunctionResult[types.Varlena](result).AppendBytes(input.(*array.Binary).Value(i), null); err != nil { - return err - } case types.T_uuid: var value types.Uuid if !null { @@ -1100,6 +1104,92 @@ func AppendArrowResult(descriptor TypeDescriptor, input arrow.Array, result vect return nil } +func appendStringArrowResult(input *array.String, result vector.FunctionResultWrapper) error { + output := vector.MustFunctionResult[types.Varlena](result) + for i := 0; i < input.Len(); i++ { + // AppendBytes copies synchronously into the MO-owned area. The unsafe + // view therefore never escapes the call and does not retain Arrow's + // backing buffer after the decoded record is released. + if err := output.AppendBytes( + commonutil.UnsafeStringToBytes(input.Value(i)), input.IsNull(i), + ); err != nil { + return err + } + } + return nil +} + +func appendJSONArrowResult(input *array.String, result vector.FunctionResultWrapper) error { + output := vector.MustFunctionResult[types.Varlena](result) + for i := 0; i < input.Len(); i++ { + if input.IsNull(i) { + if err := output.AppendBytes(nil, true); err != nil { + return err + } + continue + } + encoded, err := bytejson.ParseJsonByteFromString(input.Value(i)) + if err != nil { + return fmt.Errorf("TYPE_CONTRACT: invalid JSON result: %w", err) + } + if err = output.AppendBytes(encoded, false); err != nil { + return err + } + } + return nil +} + +func appendBinaryArrowResult(input *array.Binary, result vector.FunctionResultWrapper) error { + output := vector.MustFunctionResult[types.Varlena](result) + for i := 0; i < input.Len(); i++ { + if err := output.AppendBytes(input.Value(i), input.IsNull(i)); err != nil { + return err + } + } + return nil +} + +type nullableArrowArray interface { + NullN() int + IsNull(int) bool +} + +func appendBoolArrowResult(input *array.Boolean, result vector.FunctionResultWrapper) error { + output := vector.MustFunctionResult[bool](result) + if input.NullN() == 0 { + for i := 0; i < input.Len(); i++ { + output.AppendMustValue(input.Value(i)) + } + return nil + } + for i := 0; i < input.Len(); i++ { + if input.IsNull(i) { + output.AppendMustNull() + } else { + output.AppendMustValue(input.Value(i)) + } + } + return nil +} + +func appendFixedArrowValues[T types.FixedSizeT](values []T, input nullableArrowArray, result vector.FunctionResultWrapper) error { + output := vector.MustFunctionResult[T](result) + if input.NullN() == 0 { + for _, value := range values { + output.AppendMustValue(value) + } + return nil + } + for i, value := range values { + if input.IsNull(i) { + output.AppendMustNull() + } else { + output.AppendMustValue(value) + } + } + return nil +} + func appendFloat32ArrayResult(input *array.FixedSizeList, row int, null bool, result vector.FunctionResultWrapper, mp *mpool.MPool) error { if null { return vector.AppendArray[float32](result.GetResultVector(), nil, true, mp) diff --git a/pkg/udf/python/arrow_test.go b/pkg/udf/python/arrow_test.go index 7e24f171465c7..d1db4d404a8e9 100644 --- a/pkg/udf/python/arrow_test.go +++ b/pkg/udf/python/arrow_test.go @@ -6,8 +6,10 @@ package python import ( + "bytes" "encoding/binary" "encoding/json" + "fmt" "math" "os" "testing" @@ -15,6 +17,7 @@ import ( "github.com/apache/arrow-go/v18/arrow" "github.com/apache/arrow-go/v18/arrow/array" "github.com/apache/arrow-go/v18/arrow/decimal128" + "github.com/apache/arrow-go/v18/arrow/ipc" "github.com/apache/arrow-go/v18/arrow/memory" "github.com/matrixorigin/matrixone/pkg/common/mpool" "github.com/matrixorigin/matrixone/pkg/container/types" @@ -68,6 +71,184 @@ func TestEncodeRecordBatchUsesFlightPayloadFrames(t *testing.T) { require.Equal(t, int64(3), decodedValues.Value(2)) } +func BenchmarkDecodeRecordBatch(b *testing.B) { + descriptor, err := NewTypeDescriptor(types.T_int64.ToType()) + require.NoError(b, err) + field, err := descriptor.Field("arg_0") + require.NoError(b, err) + builder := array.NewInt64Builder(memory.NewGoAllocator()) + values := make([]int64, 8192) + for i := range values { + values[i] = int64(i) + } + builder.AppendValues(values, nil) + arrayValues := builder.NewInt64Array() + defer arrayValues.Release() + record := array.NewRecordBatch( + arrow.NewSchema([]arrow.Field{field}, nil), + []arrow.Array{arrayValues}, + int64(len(values)), + ) + defer record.Release() + frames, err := EncodeRecordBatch(record, DefaultMaxBatchBytes) + require.NoError(b, err) + b.SetBytes(int64(len(frames[1].Body))) + + b.Run("copy_baseline", func(b *testing.B) { + b.ReportAllocs() + b.ResetTimer() + for i := 0; i < b.N; i++ { + decoded, err := decodeRecordBatchCopyBaseline(frames[0], frames[1], DefaultMaxBatchBytes) + if err != nil { + b.Fatal(err) + } + decoded.Release() + } + }) + b.Run("snapshot_reader", func(b *testing.B) { + b.ReportAllocs() + b.ResetTimer() + for i := 0; i < b.N; i++ { + decoded, err := DecodeRecordBatch(frames[0], frames[1], DefaultMaxBatchBytes) + if err != nil { + b.Fatal(err) + } + decoded.Release() + } + }) +} + +func BenchmarkAppendArrowResultFixedWidth(b *testing.B) { + builder := array.NewInt64Builder(memory.NewGoAllocator()) + values := make([]int64, 8192) + for i := range values { + values[i] = int64(i) + } + builder.AppendValues(values, nil) + input := builder.NewInt64Array() + defer input.Release() + mp := mpool.MustNewZeroNoFixed() + defer mpool.DeleteMPool(mp) + b.SetBytes(int64(len(values) * 8)) + + bench := func(b *testing.B, appendResult func(vector.FunctionResultWrapper) error) { + result := vector.NewFunctionResultWrapper(types.T_int64.ToType(), mp) + defer result.Free() + require.NoError(b, result.PreExtendAndReset(len(values))) + b.ReportAllocs() + b.ResetTimer() + for i := 0; i < b.N; i++ { + if err := result.PreExtendAndReset(len(values)); err != nil { + b.Fatal(err) + } + if err := appendResult(result); err != nil { + b.Fatal(err) + } + } + } + + b.Run("wrapper_each_row", func(b *testing.B) { + bench(b, func(result vector.FunctionResultWrapper) error { + for i := 0; i < input.Len(); i++ { + if err := vector.MustFunctionResult[int64](result).Append(input.Value(i), input.IsNull(i)); err != nil { + return err + } + } + return nil + }) + }) + b.Run("wrapper_once", func(b *testing.B) { + bench(b, func(result vector.FunctionResultWrapper) error { + return appendFixedArrowValues(input.Int64Values(), input, result) + }) + }) +} + +func BenchmarkAppendArrowResultVariableWidth(b *testing.B) { + builder := array.NewStringBuilder(memory.NewGoAllocator()) + values := make([]string, 8192) + for i := range values { + values[i] = fmt.Sprintf("value-%d", i) + } + builder.AppendValues(values, nil) + input := builder.NewStringArray() + defer input.Release() + mp := mpool.MustNewZeroNoFixed() + defer mpool.DeleteMPool(mp) + b.SetBytes(int64(len(values) * 8)) + + bench := func(b *testing.B, appendResult func(vector.FunctionResultWrapper) error) { + result := vector.NewFunctionResultWrapper(types.T_varchar.ToType(), mp) + defer result.Free() + require.NoError(b, result.PreExtendAndReset(len(values))) + b.ReportAllocs() + b.ResetTimer() + for i := 0; i < b.N; i++ { + if err := result.PreExtendAndReset(len(values)); err != nil { + b.Fatal(err) + } + if err := appendResult(result); err != nil { + b.Fatal(err) + } + } + } + + b.Run("string_to_bytes_copy", func(b *testing.B) { + bench(b, func(result vector.FunctionResultWrapper) error { + output := vector.MustFunctionResult[types.Varlena](result) + for i := 0; i < input.Len(); i++ { + if err := output.AppendBytes([]byte(input.Value(i)), input.IsNull(i)); err != nil { + return err + } + } + return nil + }) + }) + b.Run("borrowed_string_view", func(b *testing.B) { + bench(b, func(result vector.FunctionResultWrapper) error { + return appendStringArrowResult(input, result) + }) + }) +} + +func decodeRecordBatchCopyBaseline(schemaFrame, batchFrame ArrowFrame, maxBytes int64) (arrow.RecordBatch, error) { + stream := make([]byte, 0, len(schemaFrame.Header)+len(batchFrame.Header)+len(batchFrame.Body)+32) + stream = appendIPCFrameCopyBaseline(stream, schemaFrame.Header, schemaFrame.Body) + stream = appendIPCFrameCopyBaseline(stream, batchFrame.Header, batchFrame.Body) + stream = append(stream, 0, 0, 0, 0, 0, 0, 0, 0) + reader, err := ipc.NewReader(bytes.NewReader(stream), ipc.WithAllocator(memory.NewGoAllocator())) + if err != nil { + return nil, err + } + defer reader.Release() + if !reader.Next() { + return nil, reader.Err() + } + record := reader.RecordBatch() + if record == nil { + return nil, fmt.Errorf("missing record batch") + } + record.Retain() + return record, nil +} + +func appendIPCFrameCopyBaseline(dst, header, body []byte) []byte { + metadataLength := (len(header) + 7) &^ 7 + var prefix [8]byte + binary.LittleEndian.PutUint32(prefix[:4], math.MaxUint32) + binary.LittleEndian.PutUint32(prefix[4:], uint32(metadataLength)) + dst = append(dst, prefix[:]...) + dst = append(dst, header...) + for len(dst)%8 != 0 { + dst = append(dst, 0) + } + dst = append(dst, body...) + for len(dst)%8 != 0 { + dst = append(dst, 0) + } + return dst +} + func TestDecodeRecordBatchRequiresSchemaAndRecordHeaders(t *testing.T) { descriptor, err := NewTypeDescriptor(types.T_int64.ToType()) require.NoError(t, err) From 120c313e0f984791cc83b8244c1575591b129ac3 Mon Sep 17 00:00:00 2001 From: iamlinjunhong <1030420200@qq.com> Date: Mon, 14 Sep 2026 14:53:59 +0800 Subject: [PATCH 077/202] perf(udf): batch fixed-width input materialization --- pkg/udf/python/arrow.go | 139 ++++++++++++++++++++++++++++++++ pkg/udf/python/arrow_test.go | 57 +++++++++++++ pkg/udf/python/gateway.go | 6 +- pkg/udf/python/worker/worker.py | 37 ++++++--- 4 files changed, 226 insertions(+), 13 deletions(-) diff --git a/pkg/udf/python/arrow.go b/pkg/udf/python/arrow.go index 84955c57178bd..5fab28aed21b9 100644 --- a/pkg/udf/python/arrow.go +++ b/pkg/udf/python/arrow.go @@ -48,6 +48,10 @@ const ( DefaultMaxBatchBytes = 16 << 20 DefaultMaxBatchRows = 65536 DefaultMaxControlBytes = 1 << 20 + // Bulk append is only used for a large fixed-width primitive column. The + // small-batch path avoids allocating a temporary validity slice and keeps + // the ordinary per-value path for constants and special SQL encodings. + bulkInputMinRows = 64 ) type TypeDescriptor struct { @@ -591,6 +595,15 @@ func (e *inputBatchEncoder) build(start, length int) (arrow.RecordBatch, *arrow. builder := array.NewRecordBuilder(memory.NewGoAllocator(), e.schema) defer builder.Release() for column, typ := range e.args { + bulk, err := appendBulkFixedInputValue( + builder.Field(column), e.inputs[column], typ, start, length, + ) + if err != nil { + return nil, nil, fmt.Errorf("input column %d: %w", column, err) + } + if bulk { + continue + } for row := 0; row < length; row++ { if err := appendInputValue(builder.Field(column), e.inputs[column], typ, start, row); err != nil { return nil, nil, fmt.Errorf("input column %d row %d: %w", column, row, err) @@ -600,6 +613,132 @@ func (e *inputBatchEncoder) build(start, length int) (arrow.RecordBatch, *arrow. return builder.NewRecordBatch(), e.schema, nil } +// appendBulkFixedInputValue uses Arrow's typed AppendValues APIs for the +// primitive SQL types whose MatrixOne storage already has the same physical +// representation. It copies only the values that Arrow must own; the source +// vector remains the ownership boundary, and NULL positions are supplied as a +// separate validity slice. The return value says whether the type was +// handled by this path. +func appendBulkFixedInputValue( + builder array.Builder, v *vector.Vector, typ types.Type, start, length int, +) (bool, error) { + if length < bulkInputMinRows { + return false, nil + } + validity := func() []bool { + if v.IsConstNull() { + return make([]bool, length) + } + if v.GetNulls() == nil || v.GetNulls().IsEmpty() { + return nil + } + valid := make([]bool, length) + for row := range valid { + valid[row] = !v.IsNull(uint64(sourceRow(v, start, row))) + } + return valid + } + if builder == nil || v == nil { + return false, fmt.Errorf("input column is missing") + } + + switch typ.Oid { + case types.T_bool: + values, err := bulkFixedInputWindow[bool](v, start, length) + if err != nil { + return false, err + } + builder.(*array.BooleanBuilder).AppendValues(values, validity()) + case types.T_int8: + values, err := bulkFixedInputWindow[int8](v, start, length) + if err != nil { + return false, err + } + builder.(*array.Int8Builder).AppendValues(values, validity()) + case types.T_int16: + values, err := bulkFixedInputWindow[int16](v, start, length) + if err != nil { + return false, err + } + builder.(*array.Int16Builder).AppendValues(values, validity()) + case types.T_int32: + values, err := bulkFixedInputWindow[int32](v, start, length) + if err != nil { + return false, err + } + builder.(*array.Int32Builder).AppendValues(values, validity()) + case types.T_int64: + values, err := bulkFixedInputWindow[int64](v, start, length) + if err != nil { + return false, err + } + builder.(*array.Int64Builder).AppendValues(values, validity()) + case types.T_uint8: + values, err := bulkFixedInputWindow[uint8](v, start, length) + if err != nil { + return false, err + } + builder.(*array.Uint8Builder).AppendValues(values, validity()) + case types.T_uint16: + values, err := bulkFixedInputWindow[uint16](v, start, length) + if err != nil { + return false, err + } + builder.(*array.Uint16Builder).AppendValues(values, validity()) + case types.T_uint32: + values, err := bulkFixedInputWindow[uint32](v, start, length) + if err != nil { + return false, err + } + builder.(*array.Uint32Builder).AppendValues(values, validity()) + case types.T_uint64: + values, err := bulkFixedInputWindow[uint64](v, start, length) + if err != nil { + return false, err + } + builder.(*array.Uint64Builder).AppendValues(values, validity()) + case types.T_float32: + values, err := bulkFixedInputWindow[float32](v, start, length) + if err != nil { + return false, err + } + builder.(*array.Float32Builder).AppendValues(values, validity()) + case types.T_float64: + values, err := bulkFixedInputWindow[float64](v, start, length) + if err != nil { + return false, err + } + builder.(*array.Float64Builder).AppendValues(values, validity()) + default: + return false, nil + } + return true, nil +} + +func bulkFixedInputWindow[T any](v *vector.Vector, start, length int) ([]T, error) { + if v.IsConstNull() { + // A constant NULL intentionally has no physical value slot. Arrow + // still needs one placeholder per logical row so its validity bitmap + // preserves the batch shape. + return make([]T, length), nil + } + values := vector.MustFixedColNoTypeCheck[T](v) + if v.IsConst() { + if len(values) == 0 { + return nil, fmt.Errorf("constant input column has no value") + } + expanded := make([]T, length) + for row := range expanded { + expanded[row] = values[0] + } + return expanded, nil + } + if start < 0 || length <= 0 || start > len(values) || length > len(values)-start { + return nil, fmt.Errorf("input column is shorter than batch range") + } + return values[start : start+length], nil +} + // canBuildFullBatch cheaply decides whether a variable-width full candidate // is worth materializing. It uses the source values already owned by the // SQL vector and a deliberately inflated bound. If the bound is not clearly diff --git a/pkg/udf/python/arrow_test.go b/pkg/udf/python/arrow_test.go index d1db4d404a8e9..8ccdf33615243 100644 --- a/pkg/udf/python/arrow_test.go +++ b/pkg/udf/python/arrow_test.go @@ -368,6 +368,63 @@ func TestInputBatchEncoderReusesSchemaAndPreservesRange(t *testing.T) { require.Equal(t, int64(30), secondValues.Value(0)) } +func TestInputBatchEncoderBulkPrimitivePreservesRangeNullsAndConstants(t *testing.T) { + mp := mpool.MustNewZeroNoFixed() + defer mpool.DeleteMPool(mp) + + input := vector.NewVec(types.T_int64.ToType()) + defer input.Free(mp) + for row := 0; row < 128; row++ { + require.NoError(t, vector.AppendFixed(input, int64(row+100), row%19 == 0, mp)) + } + encoder, err := newInputBatchEncoder( + []*vector.Vector{input}, []types.Type{types.T_int64.ToType()}, + ) + require.NoError(t, err) + record, _, err := encoder.build(7, 100) + require.NoError(t, err) + defer record.Release() + values := record.Column(0).(*array.Int64) + for row := 0; row < 100; row++ { + sourceRow := row + 7 + require.Equal(t, sourceRow%19 == 0, values.IsNull(row), "row %d", row) + if !values.IsNull(row) { + require.Equal(t, int64(sourceRow+100), values.Value(row), "row %d", row) + } + } + + constant, err := vector.NewConstFixed(types.T_int64.ToType(), int64(42), 128, mp) + require.NoError(t, err) + defer constant.Free(mp) + constantEncoder, err := newInputBatchEncoder( + []*vector.Vector{constant}, []types.Type{types.T_int64.ToType()}, + ) + require.NoError(t, err) + constantRecord, _, err := constantEncoder.build(32, 96) + require.NoError(t, err) + defer constantRecord.Release() + constantValues := constantRecord.Column(0).(*array.Int64) + require.Equal(t, 96, int(constantRecord.NumRows())) + for row := 0; row < 96; row++ { + require.False(t, constantValues.IsNull(row)) + require.Equal(t, int64(42), constantValues.Value(row)) + } + + nullConstant := vector.NewConstNull(types.T_int64.ToType(), 128, mp) + defer nullConstant.Free(mp) + nullConstantEncoder, err := newInputBatchEncoder( + []*vector.Vector{nullConstant}, []types.Type{types.T_int64.ToType()}, + ) + require.NoError(t, err) + nullConstantRecord, _, err := nullConstantEncoder.build(0, 96) + require.NoError(t, err) + defer nullConstantRecord.Release() + nullConstantValues := nullConstantRecord.Column(0).(*array.Int64) + for row := 0; row < 96; row++ { + require.True(t, nullConstantValues.IsNull(row)) + } +} + func TestInputBatchWireEncoderReusesSchemaAcrossBatches(t *testing.T) { descriptor, err := NewTypeDescriptor(types.T_int64.ToType()) require.NoError(t, err) diff --git a/pkg/udf/python/gateway.go b/pkg/udf/python/gateway.go index 243f036a407d7..505be4097a32c 100644 --- a/pkg/udf/python/gateway.go +++ b/pkg/udf/python/gateway.go @@ -1341,7 +1341,11 @@ func (g *Gateway) receiveResultBatch( if err != nil { return 0, err } - if err := snapshot.Validate(len(data.DataBody), snapshot.Digest()); err != nil { + // FreezeOutput already stores the digest of its private copy. An + // empty expectedDigest still makes Validate rehash that same + // backing and detect any trusted-boundary mutation, without + // allocating the hexadecimal digest string twice on every batch. + if err := snapshot.Validate(len(data.DataBody), ""); err != nil { return 0, err } decoded, err := DecodeRecordBatch(**schemaFrame, ArrowFrame{Header: append([]byte(nil), data.DataHeader...), Body: snapshot.TrustedBytes()}, g.cfg.MaxBatchBytes) diff --git a/pkg/udf/python/worker/worker.py b/pkg/udf/python/worker/worker.py index b06500b57a1cd..418c212a2f5e4 100644 --- a/pkg/udf/python/worker/worker.py +++ b/pkg/udf/python/worker/worker.py @@ -13,8 +13,13 @@ from __future__ import annotations -import argparse -import ast +import sys + +# Handler children execute only the length-prefixed Arrow/pipe contract. Keep +# service-side modules out of that import path; the child is started for each +# invocation burst and cannot reuse the worker's imported Python heap. +_HANDLER_ENTRY = "--execute-handler" in sys.argv + import contextlib import datetime as _datetime import decimal as _decimal @@ -25,13 +30,8 @@ import math import os import pickle -import queue -import secrets -import selectors import signal import struct -import subprocess -import sys import threading import time import uuid as _uuid @@ -40,13 +40,16 @@ from typing import Any, Dict, Iterable, Optional from zoneinfo import TZPATH, ZoneInfo, ZoneInfoNotFoundError +if not _HANDLER_ENTRY: + import queue + import selectors + import subprocess + import pyarrow as pa # Handler children only need Arrow. Avoid importing Flight on that entry path; # the annotations in this module are postponed, and the Flight server class is # never instantiated by the handler child. -_HANDLER_ENTRY = "--execute-handler" in sys.argv - class _NoFlightServerBase: pass @@ -896,6 +899,8 @@ def _decode_definition_validation(data: bytes) -> Dict[str, Any]: def _validate_definition_syntax(value: Dict[str, Any]) -> None: """Validate source and the static handler binding without executing it.""" + import ast + try: compile(value["source"], "", "exec") except SyntaxError as exc: @@ -2329,6 +2334,10 @@ def __init__( self._watchdog_process = subprocess.Popen( [ sys.executable, + # The watchdog is a standard-library-only helper; + # skipping site-package discovery trims its startup + # without changing the handler's Python environment. + "-S", watchdog_path, str(watchdog_read_fd), str(self._process.pid), @@ -3507,13 +3516,17 @@ def _safe_error(exc: Exception) -> str: def main() -> None: + if _HANDLER_ENTRY: + _execute_handler_subprocess() + return + + import argparse + import secrets + parser = argparse.ArgumentParser() parser.add_argument("--execute-handler", action="store_true") parser.add_argument("--address") args = parser.parse_args() - if args.execute_handler: - _execute_handler_subprocess() - return if not args.address: parser.error("--address is required for the Flight server") logging.basicConfig(level=logging.INFO, format="[%(asctime)s] %(levelname)s %(message)s") From 44c41b842466f2ffba1169276a6888499a940f20 Mon Sep 17 00:00:00 2001 From: iamlinjunhong <1030420200@qq.com> Date: Mon, 14 Sep 2026 15:04:53 +0800 Subject: [PATCH 078/202] perf(udf): reuse variable-width input wrappers --- pkg/udf/python/arrow.go | 99 ++++++++++++++++++------ pkg/udf/python/arrow_test.go | 29 +++++++ pkg/udf/python/gateway_benchmark_test.go | 10 +-- 3 files changed, 110 insertions(+), 28 deletions(-) diff --git a/pkg/udf/python/arrow.go b/pkg/udf/python/arrow.go index 5fab28aed21b9..f8626192be8b2 100644 --- a/pkg/udf/python/arrow.go +++ b/pkg/udf/python/arrow.go @@ -509,12 +509,13 @@ func BuildInputRecordRange(inputs []*vector.Vector, args []types.Type, start, le // physical batch and every size probe. The record builder and encoded bytes // remain per batch, so the encoder does not retain input or output buffers. type inputBatchEncoder struct { - inputs []*vector.Vector - args []types.Type - descriptors []TypeDescriptor - schema *arrow.Schema - fixedWidth bool - wire *inputBatchWireEncoder + inputs []*vector.Vector + args []types.Type + descriptors []TypeDescriptor + stringParameters []vector.FunctionParameterWrapper[types.Varlena] + schema *arrow.Schema + fixedWidth bool + wire *inputBatchWireEncoder } func newInputBatchEncoder(inputs []*vector.Vector, args []types.Type) (*inputBatchEncoder, error) { @@ -523,6 +524,7 @@ func newInputBatchEncoder(inputs []*vector.Vector, args []types.Type) (*inputBat } fields := make([]arrow.Field, len(args)) descriptors := make([]TypeDescriptor, len(args)) + stringParameters := make([]vector.FunctionParameterWrapper[types.Varlena], len(args)) fixedWidth := true for i, typ := range args { descriptor, err := NewTypeDescriptor(typ) @@ -552,14 +554,18 @@ func newInputBatchEncoder(inputs []*vector.Vector, args []types.Type) (*inputBat typ.DescString(), ) } + if isStringInputType(typ.Oid) { + stringParameters[i] = vector.GenerateFunctionStrParameter(inputs[i]) + } } schema := arrow.NewSchema(fields, nil) return &inputBatchEncoder{ - inputs: inputs, - args: args, - descriptors: descriptors, - schema: schema, - fixedWidth: fixedWidth, + inputs: inputs, + args: args, + descriptors: descriptors, + stringParameters: stringParameters, + schema: schema, + fixedWidth: fixedWidth, }, nil } @@ -605,7 +611,10 @@ func (e *inputBatchEncoder) build(start, length int) (arrow.RecordBatch, *arrow. continue } for row := 0; row < length; row++ { - if err := appendInputValue(builder.Field(column), e.inputs[column], typ, start, row); err != nil { + if err := appendInputValue( + builder.Field(column), e.inputs[column], typ, start, row, + e.stringParameters[column], + ); err != nil { return nil, nil, fmt.Errorf("input column %d row %d: %w", column, row, err) } } @@ -783,7 +792,7 @@ func (e *inputBatchEncoder) canBuildFullBatch(start, length, maxBytes int64) boo estimate = add(estimate, perColumnOverhead) estimate = add(estimate, multiply(add(length, 1), offsetWidth)) estimate = add(estimate, add(length, 7)/8) - parameter := vector.GenerateFunctionStrParameter(e.inputs[column]) + parameter := e.stringParameters[column] for row := int64(0); row < length; row++ { index := sourceRow(e.inputs[column], int(start), int(row)) if e.inputs[column].IsNull(uint64(index)) { @@ -813,9 +822,29 @@ func sourceRow(v *vector.Vector, start, row int) int { } return start + row } -func appendInputValue(builder array.Builder, v *vector.Vector, typ types.Type, start, row int) error { + +func isStringInputType(oid types.T) bool { + switch oid { + case types.T_char, types.T_varchar, types.T_text, types.T_json, + types.T_binary, types.T_varbinary, types.T_blob: + return true + default: + return false + } +} + +func appendInputValue( + builder array.Builder, + v *vector.Vector, + typ types.Type, + start, row int, + stringParameter vector.FunctionParameterWrapper[types.Varlena], +) error { index := sourceRow(v, start, row) null := v.IsNull(uint64(index)) + if !null && isStringInputType(typ.Oid) && stringParameter == nil { + return fmt.Errorf("missing cached string input parameter") + } if typ.Oid == types.T_date || typ.Oid == types.T_datetime || typ.Oid == types.T_timestamp { return appendTemporalInput(builder.(*array.StructBuilder), v, typ, index, null) } @@ -926,8 +955,7 @@ func appendInputValue(builder array.Builder, v *vector.Vector, typ types.Type, s if null { b.AppendNull() } else { - p := vector.GenerateFunctionStrParameter(v) - value, _ := p.GetStrValue(uint64(index)) + value, _ := stringParameter.GetStrValue(uint64(index)) if typ.Oid == types.T_json { canonical, err := canonicalJSONInput(value) if err != nil { @@ -942,8 +970,7 @@ func appendInputValue(builder array.Builder, v *vector.Vector, typ types.Type, s if null { b.AppendNull() } else { - p := vector.GenerateFunctionStrParameter(v) - value, _ := p.GetStrValue(uint64(index)) + value, _ := stringParameter.GetStrValue(uint64(index)) b.Append(value) } case types.T_uuid: @@ -1085,6 +1112,32 @@ func DecodeRecordBatch(schemaFrame, batchFrame ArrowFrame, maxBytes int64) (arro var ipcZeroPadding [8]byte func newIPCStreamReader(frames ...ArrowFrame) io.Reader { + reader := &ipcStreamReader{} + if len(frames) <= len(reader.inlineParts)/5 { + reader.parts = reader.inlineParts[:0] + for index, frame := range frames { + metadataLength := (len(frame.Header) + 7) &^ 7 + bodyLength := (len(frame.Body) + 7) &^ 7 + binary.LittleEndian.PutUint32(reader.prefixes[index][:4], math.MaxUint32) + binary.LittleEndian.PutUint32(reader.prefixes[index][4:], uint32(metadataLength)) + reader.parts = append(reader.parts, + reader.prefixes[index][:], + frame.Header, + ipcZeroPadding[:metadataLength-len(frame.Header)], + frame.Body, + ipcZeroPadding[:bodyLength-len(frame.Body)], + ) + } + // Arrow's stream reader uses an eight-byte zero continuation marker to + // observe the end of the stream after the one record batch. + reader.parts = append(reader.parts, ipcZeroPadding[:]) + return reader + } + + // Keep the helper correct for future callers that need more than the + // schema-plus-record pair. The production result path stays on the inline + // storage above, so this fallback does not add a per-batch allocation to + // the current protocol. parts := make([][]byte, 0, len(frames)*5+1) for _, frame := range frames { metadataLength := (len(frame.Header) + 7) &^ 7 @@ -1100,16 +1153,16 @@ func newIPCStreamReader(frames ...ArrowFrame) io.Reader { ipcZeroPadding[:bodyLength-len(frame.Body)], ) } - // Arrow's stream reader uses an eight-byte zero continuation marker to - // observe the end of the stream after the one record batch. parts = append(parts, ipcZeroPadding[:]) return &ipcStreamReader{parts: parts} } type ipcStreamReader struct { - parts [][]byte - part int - offset int + parts [][]byte + inlineParts [11][]byte + prefixes [2][8]byte + part int + offset int } func (r *ipcStreamReader) Read(dst []byte) (int, error) { diff --git a/pkg/udf/python/arrow_test.go b/pkg/udf/python/arrow_test.go index 8ccdf33615243..abeb111d1b941 100644 --- a/pkg/udf/python/arrow_test.go +++ b/pkg/udf/python/arrow_test.go @@ -368,6 +368,35 @@ func TestInputBatchEncoderReusesSchemaAndPreservesRange(t *testing.T) { require.Equal(t, int64(30), secondValues.Value(0)) } +func TestInputBatchEncoderCachesVariableWidthParameterAcrossRanges(t *testing.T) { + mp := mpool.MustNewZeroNoFixed() + defer mpool.DeleteMPool(mp) + + input := vector.NewVec(types.T_varchar.ToType()) + defer input.Free(mp) + for index, value := range []string{"zero", "one", "two", "three"} { + require.NoError(t, vector.AppendBytes(input, []byte(value), index == 2, mp)) + } + encoder, err := newInputBatchEncoder( + []*vector.Vector{input}, []types.Type{types.T_varchar.ToType()}, + ) + require.NoError(t, err) + + first, _, err := encoder.build(0, 2) + require.NoError(t, err) + defer first.Release() + second, _, err := encoder.build(2, 2) + require.NoError(t, err) + defer second.Release() + + firstValues := first.Column(0).(*array.String) + require.Equal(t, "zero", firstValues.Value(0)) + require.Equal(t, "one", firstValues.Value(1)) + secondValues := second.Column(0).(*array.String) + require.True(t, secondValues.IsNull(0)) + require.Equal(t, "three", secondValues.Value(1)) +} + func TestInputBatchEncoderBulkPrimitivePreservesRangeNullsAndConstants(t *testing.T) { mp := mpool.MustNewZeroNoFixed() defer mpool.DeleteMPool(mp) diff --git a/pkg/udf/python/gateway_benchmark_test.go b/pkg/udf/python/gateway_benchmark_test.go index bd95a5bc1a6f3..3ce304fe2ef19 100644 --- a/pkg/udf/python/gateway_benchmark_test.go +++ b/pkg/udf/python/gateway_benchmark_test.go @@ -135,11 +135,11 @@ func benchmarkEncodeInputBatch(b *testing.B, typ types.Type) { } } -// encodeInputBatchRebuildBaseline is a benchmark-only reference for the -// pre-optimization path. It intentionally rebuilds the Arrow record for every -// size probe while sharing the reusable Flight writer with production. This -// measures input record construction and probe avoidance without counting -// repeated schema serialization as a second optimization. +// encodeInputBatchRebuildBaseline is a benchmark-only reference for repeated +// Arrow record construction. It intentionally rebuilds the record and its +// input encoder for every size probe while sharing the reusable Flight writer +// with production. This measures record construction and probe avoidance +// without counting repeated schema serialization as a second optimization. func encodeInputBatchRebuildBaseline( encoder *inputBatchEncoder, start, remaining, maxBytes, maxRows int64, From 6a4f0f886c81f5322c9283a63bcf6134c4762d41 Mon Sep 17 00:00:00 2001 From: iamlinjunhong <1030420200@qq.com> Date: Mon, 14 Sep 2026 15:07:08 +0800 Subject: [PATCH 079/202] fix(udf): guard empty variable-width inputs --- pkg/udf/python/arrow.go | 2 +- pkg/udf/python/arrow_test.go | 19 +++++++++++++++++++ 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/pkg/udf/python/arrow.go b/pkg/udf/python/arrow.go index f8626192be8b2..db22c53348682 100644 --- a/pkg/udf/python/arrow.go +++ b/pkg/udf/python/arrow.go @@ -554,7 +554,7 @@ func newInputBatchEncoder(inputs []*vector.Vector, args []types.Type) (*inputBat typ.DescString(), ) } - if isStringInputType(typ.Oid) { + if inputs[i].Length() > 0 && isStringInputType(typ.Oid) { stringParameters[i] = vector.GenerateFunctionStrParameter(inputs[i]) } } diff --git a/pkg/udf/python/arrow_test.go b/pkg/udf/python/arrow_test.go index abeb111d1b941..278fdb09566aa 100644 --- a/pkg/udf/python/arrow_test.go +++ b/pkg/udf/python/arrow_test.go @@ -314,6 +314,8 @@ func TestZeroArgumentRecordPreservesRows(t *testing.T) { func TestBuildInputRecordRejectsEmptyConstantVector(t *testing.T) { mp := mpool.MustNewZeroNoFixed() + defer mpool.DeleteMPool(mp) + input := vector.NewConstNull(types.T_int64.ToType(), 0, mp) defer input.Free(mp) @@ -324,6 +326,23 @@ func TestBuildInputRecordRejectsEmptyConstantVector(t *testing.T) { 1, ) require.ErrorContains(t, err, "shorter than batch range") + + textInput, err := vector.NewConstBytes(types.T_varchar.ToType(), nil, 0, mp) + require.NoError(t, err) + defer textInput.Free(mp) + var record arrow.RecordBatch + require.NotPanics(t, func() { + record, _, err = BuildInputRecordRange( + []*vector.Vector{textInput}, + []types.Type{types.T_varchar.ToType()}, + 0, + 1, + ) + }) + if record != nil { + record.Release() + } + require.ErrorContains(t, err, "shorter than batch range") } func TestInputBatchEncoderRejectsMismatchedVectorType(t *testing.T) { From 4aece7d75857d1920acab0ab42bae5b52f63ae71 Mon Sep 17 00:00:00 2001 From: iamlinjunhong <1030420200@qq.com> Date: Mon, 14 Sep 2026 15:11:54 +0800 Subject: [PATCH 080/202] perf(udf): reduce handler request copies --- pkg/udf/python/worker/test_worker.py | 12 ++++++++ pkg/udf/python/worker/worker.py | 43 ++++++++++++++++++++++------ 2 files changed, 47 insertions(+), 8 deletions(-) diff --git a/pkg/udf/python/worker/test_worker.py b/pkg/udf/python/worker/test_worker.py index 49010aa693101..b62f332687317 100644 --- a/pkg/udf/python/worker/test_worker.py +++ b/pkg/udf/python/worker/test_worker.py @@ -1282,6 +1282,18 @@ def read(self, size=-1): self.assertEqual(worker.HANDLER_ARROW_RECORD_BATCH, request["arrow_encoding"]) self.assertEqual(b"arrow-payload", bytes(request["input"].raw())) + def test_handler_request_reader_keeps_readinto_payload_as_pickle_buffer(self): + parts, _ = worker._encode_execution_request( + { + "arrow_encoding": worker.HANDLER_ARROW_RECORD_BATCH, + "input": b"arrow-payload", + } + ) + request = worker._read_execution_request(io.BytesIO(b"".join(parts))) + self.assertIsInstance(request["input"], pickle.PickleBuffer) + self.assertEqual(b"arrow-payload", bytes(request["input"].raw())) + self.assertTrue(request["input"].raw().readonly) + def test_handler_request_encoder_rejects_non_contiguous_arrow_buffer(self): with self.assertRaisesRegex(ValueError, "cannot encode execution request"): worker._encode_execution_request( diff --git a/pkg/udf/python/worker/worker.py b/pkg/udf/python/worker/worker.py index 418c212a2f5e4..16e702f0c41a5 100644 --- a/pkg/udf/python/worker/worker.py +++ b/pkg/udf/python/worker/worker.py @@ -1786,14 +1786,34 @@ def _execute_handler_batch( return _serialize_record_batch(result_batch) -def _read_exact(stream, size: int) -> bytes: - result = bytearray() - while len(result) < size: - chunk = stream.read(size - len(result)) +def _read_exact(stream, size: int) -> bytearray: + if size < 0: + raise ValueError("execution frame read size is negative") + result = bytearray(size) + readinto = getattr(stream, "readinto", None) + if callable(readinto): + view = memoryview(result) + offset = 0 + try: + while offset < size: + count = readinto(view[offset:]) + if not isinstance(count, int) or count <= 0: + raise EOFError("execution frame ended unexpectedly") + offset += count + finally: + view.release() + return result + + # Keep compatibility with the small stream doubles used by the contract + # tests and with file-like readers that expose only read(). + offset = 0 + while offset < size: + chunk = stream.read(size - offset) if not chunk: raise EOFError("execution frame ended unexpectedly") - result.extend(chunk) - return bytes(result) + result[offset : offset + len(chunk)] = chunk + offset += len(chunk) + return result def _write_execution_frame_parts(stream, parts: Iterable[bytes]) -> None: @@ -1867,7 +1887,9 @@ def _read_execution_request(stream) -> Optional[Dict[str, Any]]: return None if len(first) != 1: raise EOFError("execution request header ended unexpectedly") - header = bytes(first) + _read_exact(stream, 7) + header = bytearray(8) + header[0] = first[0] + header[1:] = _read_exact(stream, 7) payload_size = struct.unpack(">Q", header)[0] if payload_size > MAX_EXECUTION_FRAME_BYTES or payload_size < 8: raise ValueError("RESOURCE_EXHAUSTED: execution request is too large") @@ -1881,7 +1903,12 @@ def _read_execution_request(stream) -> Optional[Dict[str, Any]]: arrow_wire = _read_exact(stream, arrow_size) try: request = pickle.loads( - metadata_wire, buffers=[pickle.PickleBuffer(arrow_wire)] + metadata_wire, + # A readonly memoryview preserves the protocol-5 PickleBuffer + # type when the exact-read backing is a bytearray, while keeping + # the Arrow payload zero-copy and preventing handler code from + # mutating the receive buffer through the request object. + buffers=[pickle.PickleBuffer(memoryview(arrow_wire).toreadonly())], ) except (EOFError, pickle.PickleError, TypeError, ValueError) as exc: raise ValueError("PROTOCOL: execution request metadata is invalid") from exc From 70c4fc0db7b0ab305a23f5af69123026330aee54 Mon Sep 17 00:00:00 2001 From: iamlinjunhong <1030420200@qq.com> Date: Mon, 14 Sep 2026 15:14:40 +0800 Subject: [PATCH 081/202] refactor(udf): clarify varlena cache naming --- pkg/udf/python/arrow.go | 59 ++++++++++++++++++++++------------------- 1 file changed, 32 insertions(+), 27 deletions(-) diff --git a/pkg/udf/python/arrow.go b/pkg/udf/python/arrow.go index db22c53348682..622b096c1bec0 100644 --- a/pkg/udf/python/arrow.go +++ b/pkg/udf/python/arrow.go @@ -509,13 +509,13 @@ func BuildInputRecordRange(inputs []*vector.Vector, args []types.Type, start, le // physical batch and every size probe. The record builder and encoded bytes // remain per batch, so the encoder does not retain input or output buffers. type inputBatchEncoder struct { - inputs []*vector.Vector - args []types.Type - descriptors []TypeDescriptor - stringParameters []vector.FunctionParameterWrapper[types.Varlena] - schema *arrow.Schema - fixedWidth bool - wire *inputBatchWireEncoder + inputs []*vector.Vector + args []types.Type + descriptors []TypeDescriptor + varlenaParameters []vector.FunctionParameterWrapper[types.Varlena] + schema *arrow.Schema + fixedWidth bool + wire *inputBatchWireEncoder } func newInputBatchEncoder(inputs []*vector.Vector, args []types.Type) (*inputBatchEncoder, error) { @@ -524,7 +524,7 @@ func newInputBatchEncoder(inputs []*vector.Vector, args []types.Type) (*inputBat } fields := make([]arrow.Field, len(args)) descriptors := make([]TypeDescriptor, len(args)) - stringParameters := make([]vector.FunctionParameterWrapper[types.Varlena], len(args)) + varlenaParameters := make([]vector.FunctionParameterWrapper[types.Varlena], len(args)) fixedWidth := true for i, typ := range args { descriptor, err := NewTypeDescriptor(typ) @@ -554,18 +554,18 @@ func newInputBatchEncoder(inputs []*vector.Vector, args []types.Type) (*inputBat typ.DescString(), ) } - if inputs[i].Length() > 0 && isStringInputType(typ.Oid) { - stringParameters[i] = vector.GenerateFunctionStrParameter(inputs[i]) + if inputs[i].Length() > 0 && isVarlenaInputType(typ.Oid) { + varlenaParameters[i] = vector.GenerateFunctionStrParameter(inputs[i]) } } schema := arrow.NewSchema(fields, nil) return &inputBatchEncoder{ - inputs: inputs, - args: args, - descriptors: descriptors, - stringParameters: stringParameters, - schema: schema, - fixedWidth: fixedWidth, + inputs: inputs, + args: args, + descriptors: descriptors, + varlenaParameters: varlenaParameters, + schema: schema, + fixedWidth: fixedWidth, }, nil } @@ -613,7 +613,7 @@ func (e *inputBatchEncoder) build(start, length int) (arrow.RecordBatch, *arrow. for row := 0; row < length; row++ { if err := appendInputValue( builder.Field(column), e.inputs[column], typ, start, row, - e.stringParameters[column], + e.varlenaParameters[column], ); err != nil { return nil, nil, fmt.Errorf("input column %d row %d: %w", column, row, err) } @@ -792,7 +792,7 @@ func (e *inputBatchEncoder) canBuildFullBatch(start, length, maxBytes int64) boo estimate = add(estimate, perColumnOverhead) estimate = add(estimate, multiply(add(length, 1), offsetWidth)) estimate = add(estimate, add(length, 7)/8) - parameter := e.stringParameters[column] + parameter := e.varlenaParameters[column] for row := int64(0); row < length; row++ { index := sourceRow(e.inputs[column], int(start), int(row)) if e.inputs[column].IsNull(uint64(index)) { @@ -823,7 +823,7 @@ func sourceRow(v *vector.Vector, start, row int) int { return start + row } -func isStringInputType(oid types.T) bool { +func isVarlenaInputType(oid types.T) bool { switch oid { case types.T_char, types.T_varchar, types.T_text, types.T_json, types.T_binary, types.T_varbinary, types.T_blob: @@ -838,12 +838,12 @@ func appendInputValue( v *vector.Vector, typ types.Type, start, row int, - stringParameter vector.FunctionParameterWrapper[types.Varlena], + varlenaParameter vector.FunctionParameterWrapper[types.Varlena], ) error { index := sourceRow(v, start, row) null := v.IsNull(uint64(index)) - if !null && isStringInputType(typ.Oid) && stringParameter == nil { - return fmt.Errorf("missing cached string input parameter") + if !null && isVarlenaInputType(typ.Oid) && varlenaParameter == nil { + return fmt.Errorf("missing cached varlena input parameter") } if typ.Oid == types.T_date || typ.Oid == types.T_datetime || typ.Oid == types.T_timestamp { return appendTemporalInput(builder.(*array.StructBuilder), v, typ, index, null) @@ -955,7 +955,7 @@ func appendInputValue( if null { b.AppendNull() } else { - value, _ := stringParameter.GetStrValue(uint64(index)) + value, _ := varlenaParameter.GetStrValue(uint64(index)) if typ.Oid == types.T_json { canonical, err := canonicalJSONInput(value) if err != nil { @@ -970,7 +970,7 @@ func appendInputValue( if null { b.AppendNull() } else { - value, _ := stringParameter.GetStrValue(uint64(index)) + value, _ := varlenaParameter.GetStrValue(uint64(index)) b.Append(value) } case types.T_uuid: @@ -1109,11 +1109,16 @@ func DecodeRecordBatch(schemaFrame, batchFrame ArrowFrame, maxBytes int64) (arro return record, nil } +const ( + inlineIPCFrameCount = 2 + inlineIPCPartCount = inlineIPCFrameCount*5 + 1 +) + var ipcZeroPadding [8]byte func newIPCStreamReader(frames ...ArrowFrame) io.Reader { reader := &ipcStreamReader{} - if len(frames) <= len(reader.inlineParts)/5 { + if len(frames) <= inlineIPCFrameCount { reader.parts = reader.inlineParts[:0] for index, frame := range frames { metadataLength := (len(frame.Header) + 7) &^ 7 @@ -1159,8 +1164,8 @@ func newIPCStreamReader(frames ...ArrowFrame) io.Reader { type ipcStreamReader struct { parts [][]byte - inlineParts [11][]byte - prefixes [2][8]byte + inlineParts [inlineIPCPartCount][]byte + prefixes [inlineIPCFrameCount][8]byte part int offset int } From 99a88aab0ad2041f34da8bd289f93a23fe9b7b07 Mon Sep 17 00:00:00 2001 From: iamlinjunhong <1030420200@qq.com> Date: Mon, 14 Sep 2026 15:23:10 +0800 Subject: [PATCH 082/202] fix(udf): handle vector inputs in batch sizing --- pkg/udf/python/arrow.go | 29 +++++++++++++++++++++++++ pkg/udf/python/arrow_test.go | 42 ++++++++++++++++++++++++++++++++++++ 2 files changed, 71 insertions(+) diff --git a/pkg/udf/python/arrow.go b/pkg/udf/python/arrow.go index 622b096c1bec0..3bf9e60d65f75 100644 --- a/pkg/udf/python/arrow.go +++ b/pkg/udf/python/arrow.go @@ -780,6 +780,24 @@ func (e *inputBatchEncoder) canBuildFullBatch(start, length, maxBytes int64) boo if !typ.IsVarlen() { return false } + if elementWidth, ok := arrayElementByteWidth(typ.Oid); ok { + // Vector arguments use a fixed-size-list physical type. They are + // varlen in MatrixOne's storage, but do not have a Varlena wrapper + // or Arrow string/binary offsets to inspect. Account for every + // child value and the parent validity bitmap instead of treating + // the vector as a byte string. + valueBytes := multiply(int64(typ.Width), elementWidth) + estimate = add(estimate, multiply(valueBytes, length)) + estimate = add(estimate, add(length, 7)/8) + estimate = add(estimate, multiply(length, perRowOverhead)) + continue + } + if !isVarlenaInputType(typ.Oid) { + // An unsupported variable-width physical type must use the + // ordinary bounded probe path. In particular, never call a nil + // Varlena wrapper while estimating a batch. + return false + } descriptor := e.descriptors[column] offsetWidth := int64(descriptor.OffsetWidth) if offsetWidth == 0 { @@ -816,6 +834,17 @@ func (e *inputBatchEncoder) canBuildFullBatch(start, length, maxBytes int64) boo return estimate*safetyNumerator <= maxBytes*safetyDenominator } +func arrayElementByteWidth(oid types.T) (int64, bool) { + switch oid { + case types.T_array_float32: + return 4, true + case types.T_array_float64: + return 8, true + default: + return 0, false + } +} + func sourceRow(v *vector.Vector, start, row int) int { if v.IsConst() { return 0 diff --git a/pkg/udf/python/arrow_test.go b/pkg/udf/python/arrow_test.go index 278fdb09566aa..a3d12adf65cb4 100644 --- a/pkg/udf/python/arrow_test.go +++ b/pkg/udf/python/arrow_test.go @@ -473,6 +473,48 @@ func TestInputBatchEncoderBulkPrimitivePreservesRangeNullsAndConstants(t *testin } } +func TestInputBatchEncoderVectorSizeProbeDoesNotUseVarlenaWrapper(t *testing.T) { + mp := mpool.MustNewZeroNoFixed() + defer mpool.DeleteMPool(mp) + + for _, tc := range []struct { + name string + typ types.Type + }{ + {name: "vecf32", typ: types.New(types.T_array_float32, 3, 0)}, + {name: "vecf64", typ: types.New(types.T_array_float64, 3, 0)}, + } { + t.Run(tc.name, func(t *testing.T) { + input := vector.NewVec(tc.typ) + defer input.Free(mp) + for row := 0; row < 64; row++ { + var err error + switch tc.typ.Oid { + case types.T_array_float32: + err = vector.AppendArray(input, []float32{float32(row), 1, 2}, row%11 == 0, mp) + case types.T_array_float64: + err = vector.AppendArray(input, []float64{float64(row), 1, 2}, row%11 == 0, mp) + } + require.NoError(t, err) + } + + encoder, err := newInputBatchEncoder([]*vector.Vector{input}, []types.Type{tc.typ}) + require.NoError(t, err) + defer func() { require.NoError(t, encoder.close()) }() + + var batch encodedRecordBatch + require.NotPanics(t, func() { + batch, err = encodeInputBatchWithEncoder( + encoder, 0, 64, DefaultMaxBatchBytes, 64, + ) + }) + require.NoError(t, err) + require.Equal(t, int64(64), batch.Rows) + require.Len(t, batch.Frames, 2) + }) + } +} + func TestInputBatchWireEncoderReusesSchemaAcrossBatches(t *testing.T) { descriptor, err := NewTypeDescriptor(types.T_int64.ToType()) require.NoError(t, err) From 588f307845eb4b5847be414a1ce5743f9ad37037 Mon Sep 17 00:00:00 2001 From: iamlinjunhong <1030420200@qq.com> Date: Mon, 14 Sep 2026 15:23:54 +0800 Subject: [PATCH 083/202] perf(udf): reuse result headers during decode --- pkg/udf/python/gateway.go | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/pkg/udf/python/gateway.go b/pkg/udf/python/gateway.go index 505be4097a32c..2e35736cf8f6e 100644 --- a/pkg/udf/python/gateway.go +++ b/pkg/udf/python/gateway.go @@ -1348,7 +1348,11 @@ func (g *Gateway) receiveResultBatch( if err := snapshot.Validate(len(data.DataBody), ""); err != nil { return 0, err } - decoded, err := DecodeRecordBatch(**schemaFrame, ArrowFrame{Header: append([]byte(nil), data.DataHeader...), Body: snapshot.TrustedBytes()}, g.cfg.MaxBatchBytes) + // DecodeRecordBatch consumes the result header synchronously before + // the next Recv. The schema header is retained across batches, but + // this per-batch header has no cross-call lifetime, so keep the + // Flight-owned slice and avoid a redundant allocation/copy. + decoded, err := DecodeRecordBatch(**schemaFrame, ArrowFrame{Header: data.DataHeader, Body: snapshot.TrustedBytes()}, g.cfg.MaxBatchBytes) if err != nil { return 0, fmt.Errorf("python udf: decode result: %w", err) } From 33ca6ec708225134e9959b4d2a22d5cf4c7ac2db Mon Sep 17 00:00:00 2001 From: iamlinjunhong <1030420200@qq.com> Date: Mon, 14 Sep 2026 15:32:09 +0800 Subject: [PATCH 084/202] fix(udf): preserve Arrow metadata across input batches --- pkg/udf/python/worker/test_worker.py | 9 +++++++++ pkg/udf/python/worker/worker.py | 6 +++++- 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/pkg/udf/python/worker/test_worker.py b/pkg/udf/python/worker/test_worker.py index b62f332687317..fa3342bb25c22 100644 --- a/pkg/udf/python/worker/test_worker.py +++ b/pkg/udf/python/worker/test_worker.py @@ -656,6 +656,15 @@ def test_every_input_batch_keeps_the_frozen_schema(self): with self.assertRaisesRegex(ValueError, "Arrow type"): worker._validate_input_batch_schema(invalid_batch, valid_schema, [descriptor]) + metadata_changed = pa.RecordBatch.from_arrays( + [pa.array([1], type=pa.int64())], + schema=pa.schema( + [pa.field("arg_0", pa.int64(), nullable=True, metadata={b"mo.udf.type": b"tampered"})] + ), + ) + with self.assertRaisesRegex(ValueError, "logical metadata"): + worker._validate_input_batch_schema(metadata_changed, valid_schema, [descriptor]) + def test_closing_control_preserves_zero_last_sequence(self): fence = { "account_id": 1, diff --git a/pkg/udf/python/worker/worker.py b/pkg/udf/python/worker/worker.py index 16e702f0c41a5..5263c5bf6db85 100644 --- a/pkg/udf/python/worker/worker.py +++ b/pkg/udf/python/worker/worker.py @@ -1324,7 +1324,11 @@ def _validate_input_batch_schema( # every Field and its metadata for every batch was measurable overhead on # small W=1 bursts. Retain the detailed validation on the mismatch path so # a malformed later frame still gets the same contract diagnosis. - if batch.schema == expected_schema: + # PyArrow's Schema equality ignores metadata by default. The metadata + # carries the frozen logical descriptor and fingerprint, so a later batch + # with the same physical type but a different contract must not pass this + # fast path. + if batch.schema.equals(expected_schema, check_metadata=True): return _validate_schema(batch.schema, descriptors) raise ValueError("TYPE_CONTRACT: input batch schema changed") From c41b9310a4250be6884e5aca72ce4c9ee8977163 Mon Sep 17 00:00:00 2001 From: iamlinjunhong <1030420200@qq.com> Date: Mon, 14 Sep 2026 15:34:31 +0800 Subject: [PATCH 085/202] fix(udf): fence supervisor restart during close --- pkg/udf/python/supervisor.go | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/pkg/udf/python/supervisor.go b/pkg/udf/python/supervisor.go index 4927d3dde7109..815433db35be7 100644 --- a/pkg/udf/python/supervisor.go +++ b/pkg/udf/python/supervisor.go @@ -86,14 +86,16 @@ func (s *Supervisor) wait(cmd *exec.Cmd, done chan struct{}) { err := cmd.Wait() s.mu.Lock() if s.cmd == cmd { - // cmd is cleared so a later Start can launch a new worker, while done is - // retained as the completed notification that Start's caller captured. + // Keep the terminal state transition and Done close under the same lock. + // Otherwise Start can observe cmd == nil in the small interval after + // Wait clears it but before the old Done channel is closed, and launch a + // replacement while a concurrent Close is still waiting for the old + // worker. s.cmd = nil s.lastErr = err - s.closing = false } - s.mu.Unlock() close(done) + s.mu.Unlock() if err != nil { logutil.Errorf("Python UDF worker exited: %v", err) } @@ -122,7 +124,7 @@ func (s *Supervisor) Err() error { func (s *Supervisor) Close() error { s.mu.Lock() cmd, done := s.cmd, s.done - if cmd != nil { + if cmd != nil || done != nil { s.closing = true } s.mu.Unlock() @@ -135,5 +137,13 @@ func (s *Supervisor) Close() error { if done != nil { <-done } + // Keep Start fenced until the worker observed by this Close has published + // its terminal notification. This also clears the fence when Close is + // called after an unexpected exit, where cmd is already nil. + s.mu.Lock() + if s.cmd == nil && s.done == done { + s.closing = false + } + s.mu.Unlock() return closeErr } From 237b88469d4c20ca900e34637aaedc3196243de2 Mon Sep 17 00:00:00 2001 From: iamlinjunhong <1030420200@qq.com> Date: Mon, 14 Sep 2026 15:43:48 +0800 Subject: [PATCH 086/202] fix(udf): require explicit Python invocation language --- pkg/udf/python/gateway.go | 2 +- pkg/udf/python/gateway_test.go | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/pkg/udf/python/gateway.go b/pkg/udf/python/gateway.go index 2e35736cf8f6e..0f949dca8b6fa 100644 --- a/pkg/udf/python/gateway.go +++ b/pkg/udf/python/gateway.go @@ -1556,7 +1556,7 @@ func validateInvocationHeader(invocation *udf.Invocation) error { if invocation == nil { return fmt.Errorf("python udf: nil invocation") } - if invocation.Language != "" && invocation.Language != udf.LanguagePython { + if invocation.Language != udf.LanguagePython { return fmt.Errorf("python udf: unsupported language %q", invocation.Language) } if invocation.Handler == "" { diff --git a/pkg/udf/python/gateway_test.go b/pkg/udf/python/gateway_test.go index 128ea37a965e6..b9fa665e3e062 100644 --- a/pkg/udf/python/gateway_test.go +++ b/pkg/udf/python/gateway_test.go @@ -196,6 +196,7 @@ func TestValidateInvocationRequiresFrozenContract(t *testing.T) { name string mutate func(*udf.Invocation) }{ + {name: "language", mutate: func(invocation *udf.Invocation) { invocation.Language = "" }}, {name: "mode", mutate: func(invocation *udf.Invocation) { invocation.Mode = "" }}, {name: "external handler", mutate: func(invocation *udf.Invocation) { invocation.Handler = "module:add" }}, {name: "null policy", mutate: func(invocation *udf.Invocation) { invocation.NullPolicy = "" }}, From 9d3f85af9c2acd3514b186e2ea91d19972e2b717 Mon Sep 17 00:00:00 2001 From: iamlinjunhong <1030420200@qq.com> Date: Mon, 14 Sep 2026 15:47:49 +0800 Subject: [PATCH 087/202] fix(udf): require explicit Python definition language --- pkg/udf/python/gateway.go | 2 +- pkg/udf/python/gateway_integration_test.go | 7 +++++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/pkg/udf/python/gateway.go b/pkg/udf/python/gateway.go index 0f949dca8b6fa..6b703cf61b95d 100644 --- a/pkg/udf/python/gateway.go +++ b/pkg/udf/python/gateway.go @@ -199,7 +199,7 @@ func (g *Gateway) ValidateDefinition(ctx context.Context, definition *udf.Routin if definition == nil { return fmt.Errorf("UNSUPPORTED_ROUTINE_VERSION: nil Python routine definition") } - if definition.Language != "" && definition.Language != udf.LanguagePython { + if definition.Language != udf.LanguagePython { return fmt.Errorf("UNSUPPORTED_ROUTINE_VERSION: unsupported UDF language %q", definition.Language) } if strings.TrimSpace(definition.Handler) == "" { diff --git a/pkg/udf/python/gateway_integration_test.go b/pkg/udf/python/gateway_integration_test.go index 64dec7b9b2b56..74577c936075f 100644 --- a/pkg/udf/python/gateway_integration_test.go +++ b/pkg/udf/python/gateway_integration_test.go @@ -88,6 +88,13 @@ func TestGatewayExecutesAgainstRealPythonWorker(t *testing.T) { cancel() require.NoFileExists(t, marker, "definition validation executed top-level Python code") + invalidLanguage := routineDefinitionForIntegration(valid) + invalidLanguage.Language = "" + validationContext, cancel = context.WithTimeout(context.Background(), 5*time.Second) + err = gateway.ValidateDefinition(validationContext, invalidLanguage) + cancel() + require.ErrorContains(t, err, "unsupported UDF language") + invalid := integrationInvocationForArgs( ModeScalar, "add", "def add(ctx, value) return value", []types.Type{types.T_int64.ToType()}, nil, 0, From fd896efa774806aac3b88cf5a598d6c1d40bf735 Mon Sep 17 00:00:00 2001 From: iamlinjunhong <1030420200@qq.com> Date: Mon, 14 Sep 2026 15:57:46 +0800 Subject: [PATCH 088/202] fix(udf): validate final Python handler binding --- pkg/udf/python/worker/test_worker.py | 24 ++++++++++++++++++++ pkg/udf/python/worker/worker.py | 33 ++++++++++++++++++---------- 2 files changed, 45 insertions(+), 12 deletions(-) diff --git a/pkg/udf/python/worker/test_worker.py b/pkg/udf/python/worker/test_worker.py index fa3342bb25c22..e21b0a1a4cbdd 100644 --- a/pkg/udf/python/worker/test_worker.py +++ b/pkg/udf/python/worker/test_worker.py @@ -201,6 +201,30 @@ def test_definition_validation_rejects_async_handler_before_catalog_state(self): finally: server.shutdown() + def test_definition_validation_rejects_handler_rebound_after_definition(self): + payload = complete_definition_validation_payload( + "def f(ctx, value): return value\n" + "f = 1\n" + ) + server = worker.RoutineFlightServer("grpc://127.0.0.1:0") + try: + results = list( + server.do_action( + None, + flight.Action( + "ValidatePythonDefinition", + json.dumps(payload, separators=(",", ":")).encode(), + ), + ) + ) + response = json.loads(bytes(results[0].body)) + self.assertEqual("ERROR", response["status"]) + self.assertIn("not a module-level function", response["reason"]) + self.assertEqual({}, server._active) + self.assertEqual({}, server._terminal) + finally: + server.shutdown() + def test_capability_advertises_worker_instance_lease(self): encoded = worker._encode_capabilities( {"protocol_version": worker.PROTOCOL_VERSION}, lease_epoch=17 diff --git a/pkg/udf/python/worker/worker.py b/pkg/udf/python/worker/worker.py index 5263c5bf6db85..06319fdac1e7e 100644 --- a/pkg/udf/python/worker/worker.py +++ b/pkg/udf/python/worker/worker.py @@ -918,20 +918,29 @@ def _validate_definition_syntax(value: Dict[str, Any]) -> None: tree = ast.parse(value["source"], filename="", mode="exec") binding = None for node in tree.body: - if isinstance(node, ast.AsyncFunctionDef) and node.name == handler: - raise ValueError("USER_CODE: Python handler must be synchronous") - if isinstance(node, ast.FunctionDef) and node.name == handler: - binding = node - break - if isinstance(node, ast.Assign) and isinstance(node.value, ast.Lambda): + if isinstance(node, (ast.FunctionDef, ast.AsyncFunctionDef)) and node.name == handler: + # Continue scanning: a later module-level assignment can replace + # the function object that exec() will expose to the runtime. + binding = "async" if isinstance(node, ast.AsyncFunctionDef) else "sync" + continue + if isinstance(node, ast.ClassDef) and node.name == handler: + binding = "other" + continue + if isinstance(node, ast.Assign): if any(isinstance(target, ast.Name) and target.id == handler for target in node.targets): - binding = node - break - if isinstance(node, ast.AnnAssign) and isinstance(node.value, ast.Lambda): + binding = "lambda" if isinstance(node.value, ast.Lambda) else "other" + continue + if isinstance(node, ast.AnnAssign): if isinstance(node.target, ast.Name) and node.target.id == handler: - binding = node - break - if binding is None: + binding = "lambda" if isinstance(node.value, ast.Lambda) else "other" + continue + if isinstance(node, ast.Delete): + if any(isinstance(target, ast.Name) and target.id == handler for target in node.targets): + binding = "other" + continue + if binding == "async": + raise ValueError("USER_CODE: Python handler must be synchronous") + if binding not in ("sync", "lambda"): raise ValueError( f"USER_CODE: Python handler {handler!r} is not a module-level function" ) From 6042725432603889b34761ca365181398c4da4f6 Mon Sep 17 00:00:00 2001 From: iamlinjunhong <1030420200@qq.com> Date: Mon, 14 Sep 2026 16:09:07 +0800 Subject: [PATCH 089/202] fix(udf): fall back when handler group kill is denied --- pkg/udf/python/worker/test_worker.py | 12 ++++++++++++ pkg/udf/python/worker/worker.py | 10 ++++++++++ 2 files changed, 22 insertions(+) diff --git a/pkg/udf/python/worker/test_worker.py b/pkg/udf/python/worker/test_worker.py index e21b0a1a4cbdd..9fc5f52f91280 100644 --- a/pkg/udf/python/worker/test_worker.py +++ b/pkg/udf/python/worker/test_worker.py @@ -1578,6 +1578,18 @@ def test_handler_close_retries_failed_process_cleanup_without_losing_ownership(s if session._slot_acquired: slots.release() + @unittest.skipUnless(os.name == "posix", "process-group cleanup fallback") + def test_process_cleanup_falls_back_to_owned_leader_on_group_permission_error(self): + process = mock.Mock(pid=1234) + process.poll.return_value = None + process.wait.return_value = 0 + + with mock.patch.object(worker.os, "killpg", side_effect=PermissionError): + worker._kill_execution_process(process) + + process.kill.assert_called_once_with() + process.wait.assert_called_once_with(timeout=1.0) + def test_handler_close_retains_process_for_a_later_cleanup_retry(self): session = object.__new__(worker._HandlerProcessSession) session._slots = threading.BoundedSemaphore(1) diff --git a/pkg/udf/python/worker/worker.py b/pkg/udf/python/worker/worker.py index 06319fdac1e7e..0fa4b800d4442 100644 --- a/pkg/udf/python/worker/worker.py +++ b/pkg/udf/python/worker/worker.py @@ -2160,6 +2160,16 @@ def _kill_execution_process(process: subprocess.Popen) -> None: os.killpg(process.pid, signal.SIGKILL) except ProcessLookupError: pass + except PermissionError: + # A process-group kill can be denied during a platform or + # teardown race even though the worker still owns the leader. + # Kill the Popen child directly so the handler slot is not + # stranded; the parent-liveness watchdog remains responsible + # for descendants when the group cannot be addressed here. + try: + process.kill() + except ProcessLookupError: + pass elif process.poll() is None: process.kill() try: From 50f2139d5a779079e916abbde1dfa5173688272b Mon Sep 17 00:00:00 2001 From: iamlinjunhong <1030420200@qq.com> Date: Mon, 14 Sep 2026 18:08:04 +0800 Subject: [PATCH 090/202] fix(udf): include timezone data in runtime image --- optools/images/Dockerfile | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/optools/images/Dockerfile b/optools/images/Dockerfile index 2398f0d5c139c..07e59859da7b7 100644 --- a/optools/images/Dockerfile +++ b/optools/images/Dockerfile @@ -55,6 +55,14 @@ RUN if [ "$TYPECHECK" = "1" ]; then \ FROM matrixorigin/ubuntu:22.04 +# The Python UDF capability contract includes the IANA timezone database +# version. The worker image and the CN image must advertise the same version +# before Python execution is admitted; the minimal MatrixOne runtime image +# does not contain tzdata by default. +RUN apt-get update \ + && DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends tzdata \ + && rm -rf /var/lib/apt/lists/* + COPY --from=builder /go/src/github.com/matrixorigin/matrixone/mo-service /mo-service COPY --from=builder /go/src/github.com/matrixorigin/matrixone/etc /etc COPY --from=builder /go/src/github.com/matrixorigin/matrixone/thirdparties/install/lib/*.so /usr/local/lib/ From e2002b03f2ab3f69e82ec3f828ff645bb8624157 Mon Sep 17 00:00:00 2001 From: iamlinjunhong <1030420200@qq.com> Date: Mon, 14 Sep 2026 18:25:24 +0800 Subject: [PATCH 091/202] fix(udf): fail closed on broken liveness watches --- pkg/udf/python/worker/test_watchdog.py | 29 ++++++++++++++++++++++++++ pkg/udf/python/worker/test_worker.py | 13 ++++++++++++ pkg/udf/python/worker/watchdog.py | 7 +++++++ pkg/udf/python/worker/worker.py | 9 ++++++++ 4 files changed, 58 insertions(+) create mode 100644 pkg/udf/python/worker/test_watchdog.py diff --git a/pkg/udf/python/worker/test_watchdog.py b/pkg/udf/python/worker/test_watchdog.py new file mode 100644 index 0000000000000..56d31f77a5cf7 --- /dev/null +++ b/pkg/udf/python/worker/test_watchdog.py @@ -0,0 +1,29 @@ +#!/usr/bin/env python3 + +import importlib.util +import pathlib +import unittest +from unittest import mock + + +WATCHDOG_PATH = pathlib.Path(__file__).with_name("watchdog.py") +spec = importlib.util.spec_from_file_location("matrixone_python_watchdog", WATCHDOG_PATH) +watchdog = importlib.util.module_from_spec(spec) +spec.loader.exec_module(watchdog) + + +class WatchdogTest(unittest.TestCase): + @unittest.skipUnless(watchdog.os.name == "posix", "process-group watchdog") + def test_parent_watch_read_error_kills_process_group(self): + with ( + mock.patch.object(watchdog.os, "read", side_effect=OSError("closed")), + mock.patch.object(watchdog.os, "killpg") as killpg, + mock.patch.object(watchdog.os, "close"), + ): + watchdog._watch(17, 23) + + killpg.assert_called_once_with(23, watchdog.signal.SIGKILL) + + +if __name__ == "__main__": + unittest.main() diff --git a/pkg/udf/python/worker/test_worker.py b/pkg/udf/python/worker/test_worker.py index 9fc5f52f91280..24df2a4a938e1 100644 --- a/pkg/udf/python/worker/test_worker.py +++ b/pkg/udf/python/worker/test_worker.py @@ -2190,6 +2190,19 @@ def process_is_running(pid): except ProcessLookupError: pass + @unittest.skipUnless(os.name == "posix", "handler process-group cleanup") + def test_parent_watch_read_error_kills_own_group_before_handler_exit(self): + with ( + mock.patch.object(worker.os, "read", side_effect=OSError("closed")), + mock.patch.object(worker.os, "getpgrp", return_value=23), + mock.patch.object(worker.os, "killpg") as killpg, + mock.patch.object(worker.os, "_exit", side_effect=SystemExit(137)), + ): + with self.assertRaises(SystemExit): + worker._watch_parent_liveness(17) + + killpg.assert_called_once_with(23, worker.signal.SIGKILL) + def test_handler_stdout_cannot_inject_parent_protocol(self): descriptor = {"type_id": worker.INT64, "offset_width": 32} batch = pa.RecordBatch.from_arrays([pa.array([1], type=pa.int64())], ["arg_0"]) diff --git a/pkg/udf/python/worker/watchdog.py b/pkg/udf/python/worker/watchdog.py index 25c417a73b7d4..30805ad09c8a5 100644 --- a/pkg/udf/python/worker/watchdog.py +++ b/pkg/udf/python/worker/watchdog.py @@ -20,6 +20,13 @@ def _watch(read_fd: int, process_group_id: int) -> None: pass return except (OSError, ValueError): + # A broken descriptor means the worker death signal can no longer be + # observed. Kill the owned group before the watchdog exits so a + # handler descendant cannot outlive both its owners. + try: + os.killpg(process_group_id, signal.SIGKILL) + except ProcessLookupError: + pass return finally: try: diff --git a/pkg/udf/python/worker/worker.py b/pkg/udf/python/worker/worker.py index 0fa4b800d4442..2bc0ad54268ba 100644 --- a/pkg/udf/python/worker/worker.py +++ b/pkg/udf/python/worker/worker.py @@ -1953,6 +1953,15 @@ def _watch_parent_liveness(read_fd: int) -> None: pass os._exit(137) except (OSError, ValueError): + # A broken watch descriptor is also a lost parent notification. The + # handler must terminate its whole process group before leaving; an + # ordinary thread exit would leave descendants alive after the worker + # has already lost ownership of them. + if os.name == "posix": + try: + os.killpg(os.getpgrp(), signal.SIGKILL) + except ProcessLookupError: + pass os._exit(137) From ca5fa9572e972768626769361125a59590266dd9 Mon Sep 17 00:00:00 2001 From: iamlinjunhong <1030420200@qq.com> Date: Mon, 14 Sep 2026 18:29:12 +0800 Subject: [PATCH 092/202] fix(udf): close gateway admission barrier --- pkg/udf/python/gateway.go | 14 ++++++++++++++ pkg/udf/python/gateway_admission_test.go | 13 +++++++++++++ 2 files changed, 27 insertions(+) diff --git a/pkg/udf/python/gateway.go b/pkg/udf/python/gateway.go index 6b703cf61b95d..217d769363c4a 100644 --- a/pkg/udf/python/gateway.go +++ b/pkg/udf/python/gateway.go @@ -397,9 +397,13 @@ func (g *Gateway) Close() error { g.mu.Unlock() return nil } + // Serialize the close barrier with group admission. Once this lock is + // released, no caller can pass the admission check after Close returns. + g.admissionMu.Lock() g.closed = true conn := g.conn g.conn = nil + g.admissionMu.Unlock() g.mu.Unlock() if conn == nil { return nil @@ -727,15 +731,25 @@ func (g *Gateway) admitInvocation(invocation *udf.Invocation) (*invocationAdmiss if err != nil { return nil, err } + // g.mu -> admissionMu is the lifecycle lock order. Close uses the same + // order so a concurrent shutdown cannot return while this invocation is + // still being admitted. + g.mu.Lock() + if g.closed { + g.mu.Unlock() + return nil, errGatewayClosed + } g.admissionMu.Lock() ledger := g.ledger ttl := g.ledgerTTL active := g.active if err := g.claimGroupLocked(invocation.Tuple.GroupID, invocation.Tuple.GroupEpoch); err != nil { g.admissionMu.Unlock() + g.mu.Unlock() return nil, err } g.admissionMu.Unlock() + g.mu.Unlock() if ledger == nil || active == nil { g.releaseGroupClaim(invocation.Tuple.GroupID, invocation.Tuple.GroupEpoch) return nil, fmt.Errorf("python udf: admission state is not initialized") diff --git a/pkg/udf/python/gateway_admission_test.go b/pkg/udf/python/gateway_admission_test.go index 457f0d7a9308a..0288b0e3fd0ef 100644 --- a/pkg/udf/python/gateway_admission_test.go +++ b/pkg/udf/python/gateway_admission_test.go @@ -55,6 +55,19 @@ func TestGatewayAdmissionFencesDuplicateAndReleasesOnce(t *testing.T) { require.ErrorIs(t, err, protocol.ErrDuplicate) } +func TestGatewayAdmissionRejectsAfterClose(t *testing.T) { + gateway := newAdmissionTestGateway(t, 1) + require.NoError(t, gateway.Close()) + + _, err := gateway.admitInvocation(validInvocation()) + require.ErrorIs(t, err, errGatewayClosed) + if gateway.ledger != nil { + entries, _ := gateway.ledger.Counts() + require.Zero(t, entries) + } + require.Len(t, gateway.active, 0) +} + func TestGatewayAdmissionRejectsAtKWithoutLeavingLedgerEntry(t *testing.T) { gateway := newAdmissionTestGateway(t, 1) first := validInvocation() From 535368ff09b1a6c8886b82346a1ef25a9665fa97 Mon Sep 17 00:00:00 2001 From: iamlinjunhong <1030420200@qq.com> Date: Mon, 14 Sep 2026 18:33:54 +0800 Subject: [PATCH 093/202] fix(udf): validate empty selection domains --- pkg/sql/colexec/external_routine_eval.go | 15 +++++++++------ pkg/sql/colexec/external_routine_eval_test.go | 18 ++++++++++++++++++ 2 files changed, 27 insertions(+), 6 deletions(-) diff --git a/pkg/sql/colexec/external_routine_eval.go b/pkg/sql/colexec/external_routine_eval.go index 72594708e47cb..dffe882326fb3 100644 --- a/pkg/sql/colexec/external_routine_eval.go +++ b/pkg/sql/colexec/external_routine_eval.go @@ -205,12 +205,9 @@ func (e *ExternalRoutineEval) eval(proc *process.Process, batches []*batch.Batch if err != nil { return nil, err } - if rowCount == 0 { - if err := e.result.PreExtendAndReset(0); err != nil { - return nil, err - } - return e.result.GetResultVector(), nil - } + // Keep the row-domain contract strict even for an empty batch. Returning + // early before this check would let a stale selection mask cross the + // physical boundary and hide a relation-position bug in the caller. if selectList != nil && len(selectList) != rowCount { return nil, fmt.Errorf( "external routine selection has %d rows, expected %d", @@ -218,6 +215,12 @@ func (e *ExternalRoutineEval) eval(proc *process.Process, batches []*batch.Batch rowCount, ) } + if rowCount == 0 { + if err := e.result.PreExtendAndReset(0); err != nil { + return nil, err + } + return e.result.GetResultVector(), nil + } if noRowsSelected(selectList, rowCount) { return e.nullResult(rowCount) diff --git a/pkg/sql/colexec/external_routine_eval_test.go b/pkg/sql/colexec/external_routine_eval_test.go index 36107b842e754..42d36d1acbe72 100644 --- a/pkg/sql/colexec/external_routine_eval_test.go +++ b/pkg/sql/colexec/external_routine_eval_test.go @@ -166,6 +166,24 @@ func TestExternalRoutineEvalRejectsSelectionWithWrongRowDomain(t *testing.T) { require.ErrorContains(t, err, "selection has 2 rows, expected 1") } +func TestExternalRoutineEvalRejectsSelectionForEmptyRowDomain(t *testing.T) { + proc := testutil.NewProcess(t) + defer proc.Free() + + input := vector.NewVec(types.T_int64.ToType()) + defer input.Free(proc.Mp()) + evaluator, err := newExternalRoutineEval(proc, testExternalRoutineCall(t, "SCALAR", udf.NullCallHandler), []ExpressionExecutor{ + &externalRoutineTestExecutor{vector: input}, + }, nil) + require.NoError(t, err) + defer evaluator.Free() + + emptyBatch := batch.NewWithSize(0) + emptyBatch.SetRowCount(0) + _, err = evaluator.Eval(proc, []*batch.Batch{emptyBatch}, []bool{true}) + require.ErrorContains(t, err, "selection has 1 rows, expected 0") +} + func TestNewExpressionExecutorDispatchesTypedRoutineCall(t *testing.T) { proc := testutil.NewProcess(t) defer proc.Free() From 5e05a944c7119af422d50a40778fccffa52d7190 Mon Sep 17 00:00:00 2001 From: iamlinjunhong <1030420200@qq.com> Date: Mon, 14 Sep 2026 18:38:24 +0800 Subject: [PATCH 094/202] fix(udf): reject noncanonical JSON results --- pkg/udf/python/arrow.go | 29 +++++++++++++++++++++++++++-- pkg/udf/python/arrow_test.go | 29 +++++++++++++++++++++++++++++ 2 files changed, 56 insertions(+), 2 deletions(-) diff --git a/pkg/udf/python/arrow.go b/pkg/udf/python/arrow.go index 3bf9e60d65f75..69714be8ffa65 100644 --- a/pkg/udf/python/arrow.go +++ b/pkg/udf/python/arrow.go @@ -1039,10 +1039,26 @@ func appendInputValue( // though scalar JSON values happen to work. func canonicalJSONInput(value []byte) ([]byte, error) { text := []byte(types.DecodeJson(value).String()) - var compact bytes.Buffer - if err := json.Compact(&compact, text); err != nil { + canonical, err := canonicalJSONTextBytes(text) + if err != nil { return nil, fmt.Errorf("TYPE_CONTRACT: invalid JSON input: %w", err) } + return canonical, nil +} + +func canonicalJSONText(value string) (string, error) { + canonical, err := canonicalJSONTextBytes([]byte(value)) + if err != nil { + return "", err + } + return string(canonical), nil +} + +func canonicalJSONTextBytes(value []byte) ([]byte, error) { + var compact bytes.Buffer + if err := json.Compact(&compact, value); err != nil { + return nil, err + } return compact.Bytes(), nil } @@ -1471,6 +1487,15 @@ func validateArrowValueDomain(descriptor TypeDescriptor, input arrow.Array) erro if descriptor.Width > 0 && int32(utf8.RuneCountInString(input.(*array.String).Value(row))) > descriptor.Width { return fmt.Errorf("TYPE_CONTRACT: string row %d exceeds width %d", row, descriptor.Width) } + case types.T_json: + value := input.(*array.String).Value(row) + canonical, err := canonicalJSONText(value) + if err != nil { + return fmt.Errorf("TYPE_CONTRACT: JSON row %d is not valid canonical text: %w", row, err) + } + if canonical != value { + return fmt.Errorf("TYPE_CONTRACT: JSON row %d is not canonical text", row) + } case types.T_binary, types.T_varbinary, types.T_blob: if descriptor.Width > 0 && int32(len(input.(*array.Binary).Value(row))) > descriptor.Width { return fmt.Errorf("TYPE_CONTRACT: binary row %d exceeds width %d", row, descriptor.Width) diff --git a/pkg/udf/python/arrow_test.go b/pkg/udf/python/arrow_test.go index a3d12adf65cb4..c74266bd1a80b 100644 --- a/pkg/udf/python/arrow_test.go +++ b/pkg/udf/python/arrow_test.go @@ -705,6 +705,35 @@ func TestBuildInputRecordCanonicalizesJSONText(t *testing.T) { require.Equal(t, `{"a":1,"b":[true,null,"中"]}`, values.Value(0)) } +func TestArrowValueDomainRequiresCanonicalJSONText(t *testing.T) { + descriptor, err := NewTypeDescriptor(types.T_json.ToType()) + require.NoError(t, err) + + for _, test := range []struct { + name string + value string + valid bool + }{ + {name: "canonical", value: `{"a":1,"b":[true,null]}`, valid: true}, + {name: "whitespace", value: ` { "a": 1, "b": [true, null] } `}, + {name: "invalid", value: `{"a":}`}, + } { + t.Run(test.name, func(t *testing.T) { + builder := array.NewStringBuilder(memory.NewGoAllocator()) + builder.Append(test.value) + values := builder.NewStringArray() + defer values.Release() + + err := validateArrowValueDomain(descriptor, values) + if test.valid { + require.NoError(t, err) + } else { + require.ErrorContains(t, err, "JSON row 0") + } + }) + } +} + func TestGoPythonTypeDescriptorFixtures(t *testing.T) { data, err := os.ReadFile("testdata/type_descriptors.json") require.NoError(t, err) From 50f5338b169b3e8c1240029f54e1cbd78593dabe Mon Sep 17 00:00:00 2001 From: iamlinjunhong <1030420200@qq.com> Date: Mon, 14 Sep 2026 18:39:51 +0800 Subject: [PATCH 095/202] fix(udf): reject opaque text descriptors --- pkg/udf/python/arrow.go | 4 +++- pkg/udf/python/arrow_test.go | 1 + pkg/udf/python/worker/test_worker.py | 9 +++++++++ pkg/udf/python/worker/worker.py | 2 +- 4 files changed, 14 insertions(+), 2 deletions(-) diff --git a/pkg/udf/python/arrow.go b/pkg/udf/python/arrow.go index 69714be8ffa65..c7615947b9692 100644 --- a/pkg/udf/python/arrow.go +++ b/pkg/udf/python/arrow.go @@ -211,7 +211,9 @@ func (d TypeDescriptor) Validate() error { return fmt.Errorf("TYPE_CONTRACT: temporal scale or charset is outside the supported range") } case types.T_char, types.T_varchar, types.T_text: - if d.Charset > types.CharsetUTF8 { + switch d.Charset { + case types.CharsetLegacy, types.CharsetUTF8MB4Bin, types.CharsetUTF8: + default: return fmt.Errorf("TYPE_CONTRACT: unsupported text charset %d", d.Charset) } case types.T_binary, types.T_varbinary, types.T_blob: diff --git a/pkg/udf/python/arrow_test.go b/pkg/udf/python/arrow_test.go index c74266bd1a80b..c0fd830d93746 100644 --- a/pkg/udf/python/arrow_test.go +++ b/pkg/udf/python/arrow_test.go @@ -648,6 +648,7 @@ func TestTypeDescriptorRejectsNonCanonicalDomain(t *testing.T) { {TypeID: int32(types.T_json), OffsetWidth: 32}, {TypeID: int32(types.T_date), OffsetWidth: 32}, {TypeID: int32(types.T_array_float32), Width: types.MaxArrayDimension + 1}, + {TypeID: int32(types.T_varchar), Width: 16, Charset: types.CharsetBinary, OffsetWidth: 32}, } for _, descriptor := range invalid { _, err := descriptor.Field("value") diff --git a/pkg/udf/python/worker/test_worker.py b/pkg/udf/python/worker/test_worker.py index 24df2a4a938e1..37f4480a33abb 100644 --- a/pkg/udf/python/worker/test_worker.py +++ b/pkg/udf/python/worker/test_worker.py @@ -626,6 +626,15 @@ def test_type_descriptor_shape_is_strict(self): with self.assertRaisesRegex(ValueError, "offset_width must be an integer"): worker._field("result", {"type_id": worker.INT64, "offset_width": "32"}) + def test_text_descriptor_rejects_opaque_binary_charset(self): + for type_id in (worker.CHAR, worker.VARCHAR, worker.TEXT): + with self.subTest(type_id=type_id): + with self.assertRaisesRegex(ValueError, "unsupported text charset"): + worker._field( + "result", + {"type_id": type_id, "width": 16, "charset": 1, "offset_width": 32}, + ) + def test_malformed_control_is_reported_as_protocol_error(self): with self.assertRaisesRegex(ValueError, "PROTOCOL: invalid control UTF-8"): worker._decode_control(b"\xff") diff --git a/pkg/udf/python/worker/worker.py b/pkg/udf/python/worker/worker.py index 2bc0ad54268ba..81fb7019c23e6 100644 --- a/pkg/udf/python/worker/worker.py +++ b/pkg/udf/python/worker/worker.py @@ -1198,7 +1198,7 @@ def _validate_descriptor_domain(descriptor: Dict[str, Any]) -> None: if scale > 6 or charset: raise ValueError("TYPE_CONTRACT: temporal scale or charset is outside the supported range") elif type_id in (CHAR, VARCHAR, TEXT): - if charset > 3: + if charset not in (0, 2, 3): raise ValueError("TYPE_CONTRACT: unsupported text charset") elif type_id in (BINARY, VARBINARY, BLOB): if charset != 1: From 8bbeabd7614a1431253971bc4255cf61bed5bd78 Mon Sep 17 00:00:00 2001 From: iamlinjunhong <1030420200@qq.com> Date: Mon, 14 Sep 2026 18:44:37 +0800 Subject: [PATCH 096/202] fix(udf): serialize admission with gateway close --- pkg/udf/python/gateway.go | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/pkg/udf/python/gateway.go b/pkg/udf/python/gateway.go index 217d769363c4a..2f999602acd55 100644 --- a/pkg/udf/python/gateway.go +++ b/pkg/udf/python/gateway.go @@ -733,7 +733,9 @@ func (g *Gateway) admitInvocation(invocation *udf.Invocation) (*invocationAdmiss } // g.mu -> admissionMu is the lifecycle lock order. Close uses the same // order so a concurrent shutdown cannot return while this invocation is - // still being admitted. + // still being admitted. Keep g.mu held until the complete claim is either + // published or rolled back; otherwise Close could return in the gap between + // the group claim and the ledger/K reservation becoming owned. g.mu.Lock() if g.closed { g.mu.Unlock() @@ -749,9 +751,9 @@ func (g *Gateway) admitInvocation(invocation *udf.Invocation) (*invocationAdmiss return nil, err } g.admissionMu.Unlock() - g.mu.Unlock() if ledger == nil || active == nil { g.releaseGroupClaim(invocation.Tuple.GroupID, invocation.Tuple.GroupEpoch) + g.mu.Unlock() return nil, fmt.Errorf("python udf: admission state is not initialized") } // Expire is intentionally limited to tombstones by TerminalLedger. An @@ -764,11 +766,13 @@ func (g *Gateway) admitInvocation(invocation *udf.Invocation) (*invocationAdmiss credit, err := ledger.Reserve(invocation.Tuple.GroupID, invocation.Tuple.GroupEpoch, 1, entryBytes) if err != nil { g.releaseGroupClaim(invocation.Tuple.GroupID, invocation.Tuple.GroupEpoch) + g.mu.Unlock() return nil, fmt.Errorf("RESOURCE_EXHAUSTED: Python UDF terminal ledger: %w", err) } if err := credit.Add(key, entryBytes, time.Now().Add(ttl)); err != nil { credit.ReleaseUnused() g.releaseGroupClaim(invocation.Tuple.GroupID, invocation.Tuple.GroupEpoch) + g.mu.Unlock() return nil, err } select { @@ -780,6 +784,7 @@ func (g *Gateway) admitInvocation(invocation *udf.Invocation) (*invocationAdmiss _ = ledger.Abandon(key) credit.ReleaseUnused() g.releaseGroupClaim(invocation.Tuple.GroupID, invocation.Tuple.GroupEpoch) + g.mu.Unlock() return nil, fmt.Errorf("RESOURCE_EXHAUSTED: Python UDF active invocation slots are full") } @@ -797,6 +802,7 @@ func (g *Gateway) admitInvocation(invocation *udf.Invocation) (*invocationAdmiss _ = ledger.Abandon(key) credit.ReleaseUnused() g.releaseGroupClaim(invocation.Tuple.GroupID, invocation.Tuple.GroupEpoch) + g.mu.Unlock() return nil, err } token, err := group.BeginOpen(invocation.Tuple.InvocationID) @@ -804,14 +810,17 @@ func (g *Gateway) admitInvocation(invocation *udf.Invocation) (*invocationAdmiss _ = group.Close(protocol.ReasonPartialOpenError) _ = ledger.Abandon(key) g.releaseGroupClaim(invocation.Tuple.GroupID, invocation.Tuple.GroupEpoch) + g.mu.Unlock() return nil, err } if err := token.Commit(); err != nil { _ = group.Close(protocol.ReasonPartialOpenError) _ = ledger.Abandon(key) g.releaseGroupClaim(invocation.Tuple.GroupID, invocation.Tuple.GroupEpoch) + g.mu.Unlock() return nil, err } + g.mu.Unlock() return &invocationAdmission{ gateway: g, group: group, From d5060ade634e3cb97fc227646510cde01b9e7b7a Mon Sep 17 00:00:00 2001 From: iamlinjunhong <1030420200@qq.com> Date: Mon, 14 Sep 2026 18:48:42 +0800 Subject: [PATCH 097/202] fix(udf): enforce budget during handler writes --- pkg/udf/python/worker/test_worker.py | 51 ++++++++++++++++++++++++++++ pkg/udf/python/worker/worker.py | 19 ++++++++--- 2 files changed, 66 insertions(+), 4 deletions(-) diff --git a/pkg/udf/python/worker/test_worker.py b/pkg/udf/python/worker/test_worker.py index 37f4480a33abb..38ecf5dda8d97 100644 --- a/pkg/udf/python/worker/test_worker.py +++ b/pkg/udf/python/worker/test_worker.py @@ -2923,6 +2923,57 @@ def test_handler_deadline_includes_request_serialization(self): if session is not None: session.close() + @unittest.skipUnless(os.name == "posix", "non-blocking write budget test") + def test_handler_request_write_rechecks_budget_within_one_write_event(self): + class CancelAfterWrites: + def __init__(self): + self.writes = 0 + + def is_cancelled(self): + return self.writes >= 2 + + class FakeStdin: + def fileno(self): + return 123 + + class FakeProcess: + stdin = FakeStdin() + + def poll(self): + return None + + class FakeSelector: + def register(self, *args): + pass + + def unregister(self, *args): + pass + + def select(self, _timeout): + return [(types.SimpleNamespace(data="request"), None)] + + context = CancelAfterWrites() + session = object.__new__(worker._HandlerProcessSession) + session._closed = False + session._process = FakeProcess() + session._selector = FakeSelector() + session._response_buffer = bytearray() + session._burst_batches = 0 + + request_parts = tuple(b"x" for _ in range(100)) + + def write(_fd, data): + context.writes += 1 + return len(data) + + with mock.patch.object( + worker, "_encode_execution_request", return_value=(request_parts, 100) + ), mock.patch.object(worker.os, "write", side_effect=write): + with self.assertRaisesRegex(TimeoutError, "handler execution cancelled"): + session.run(context, {"input": b"x"}, 10) + + self.assertLess(context.writes, len(request_parts)) + def test_zero_argument_vector_uses_context_rows(self): descriptor = {"type_id": worker.INT64, "offset_width": 32} output = pa.array([7, 7, 7], type=pa.int64()) diff --git a/pkg/udf/python/worker/worker.py b/pkg/udf/python/worker/worker.py index 81fb7019c23e6..e7816943179ec 100644 --- a/pkg/udf/python/worker/worker.py +++ b/pkg/udf/python/worker/worker.py @@ -2478,16 +2478,21 @@ def run(self, context, request: Dict[str, Any], timeout_seconds: float) -> bytes request_parts, request_size = _encode_execution_request( request, compact=self._burst_batches > 0 ) - if time.monotonic() >= deadline: - raise TimeoutError("DEADLINE_EXCEEDED: handler execution timeout") + + def check_budget() -> None: + if _context_is_cancelled(context): + raise TimeoutError("DEADLINE_EXCEEDED: handler execution cancelled") + if time.monotonic() >= deadline: + raise TimeoutError("DEADLINE_EXCEEDED: handler execution timeout") + + check_budget() request_part_index = 0 request_part_offset = 0 stdin_fd = self._process.stdin.fileno() self._selector.register(stdin_fd, selectors.EVENT_WRITE, "request") try: while True: - if _context_is_cancelled(context): - raise TimeoutError("DEADLINE_EXCEEDED: handler execution cancelled") + check_budget() remaining = deadline - time.monotonic() if remaining <= 0: raise TimeoutError("DEADLINE_EXCEEDED: handler execution timeout") @@ -2499,6 +2504,11 @@ def run(self, context, request: Dict[str, Any], timeout_seconds: float) -> bytes if event.data == "request": try: while request_part_index < len(request_parts): + # A single writable notification can drain many + # small frame parts. Re-check the caller budget + # inside that loop so serialization/write work + # cannot outrun cancellation or the deadline. + check_budget() part = request_parts[request_part_index] if request_part_offset == len(part): request_part_index += 1 @@ -2508,6 +2518,7 @@ def run(self, context, request: Dict[str, Any], timeout_seconds: float) -> bytes if written <= 0: raise BrokenPipeError("handler request channel made no progress") request_part_offset += written + check_budget() if request_part_offset < len(part): break except BlockingIOError: From a1474169bd58d1deac0530dc631d95eeabb2cc28 Mon Sep 17 00:00:00 2001 From: iamlinjunhong <1030420200@qq.com> Date: Mon, 14 Sep 2026 18:58:10 +0800 Subject: [PATCH 098/202] fix(udf): validate Python catalog signature before publish --- pkg/frontend/authenticate.go | 38 ++++++++++++++++++--- pkg/frontend/python_udf_catalog_test.go | 45 +++++++++++++++++++++++++ 2 files changed, 78 insertions(+), 5 deletions(-) diff --git a/pkg/frontend/authenticate.go b/pkg/frontend/authenticate.go index 49838c9dab995..91121d47689d8 100644 --- a/pkg/frontend/authenticate.go +++ b/pkg/frontend/authenticate.go @@ -12202,17 +12202,45 @@ func normalizePythonFunctionDefinition(definition userDefinedFunctionDefinition) definition.returnDescriptor = returnDescriptor definition.signatureKeySchemaVersion = udf.PythonSignatureKeySchemaVersion definition.signatureFingerprint = signatureFingerprint - // Keep arg_types as the shared logical signature key. The exact Python - // descriptor belongs in canonical_input_descriptor and in the immutable - // revision row; mixing the two encodings makes shared SQL readers and - // upgrade paths depend on a language-specific workaround. + // The base catalog keeps the historical logical signature columns while the + // immutable revision keeps the exact Python descriptors. Both are + // execution authority: accepting a caller-supplied args/retType pair that + // disagrees with the typed body would publish an identity whose shared + // overload key and immutable revision describe different functions. That + // malformed row would only be discovered on a later read or plan bind. logicalArgTypes, err := userDefinedFunctionArgumentTypesFromJSON(definition.args) if err != nil { return userDefinedFunctionDefinition{}, moerr.NewInvalidInputNoCtxf( "UNSUPPORTED_ROUTINE_VERSION: Python logical argument metadata is invalid: %v", err, ) } - definition.argTypes = logicalArgTypes + expectedLogicalTypes := make([]string, len(body.ArgTypes)) + for index, descriptor := range body.ArgTypes { + expectedLogicalTypes[index] = strings.ToLower(descriptor.Type().Oid.String()) + } + expectedLogicalArgTypes, err := userDefinedFunctionArgumentTypes(expectedLogicalTypes) + if err != nil { + return userDefinedFunctionDefinition{}, moerr.NewInvalidInputNoCtxf( + "UNSUPPORTED_ROUTINE_VERSION: Python typed argument metadata is invalid: %v", err, + ) + } + if logicalArgTypes != expectedLogicalArgTypes { + return userDefinedFunctionDefinition{}, moerr.NewInvalidInputNoCtxf( + "UNSUPPORTED_ROUTINE_VERSION: Python catalog arguments %q do not match the typed body %q", + logicalArgTypes, expectedLogicalArgTypes, + ) + } + if !routineRevisionReturnTypeMatches(definition.retType, body.ReturnType.Type().Oid) { + return userDefinedFunctionDefinition{}, moerr.NewInvalidInputNoCtxf( + "UNSUPPORTED_ROUTINE_VERSION: Python catalog return type %q does not match the typed body %q", + definition.retType, body.ReturnType.Type().Oid.String(), + ) + } + // Keep arg_types canonicalized from the typed body. The exact Python + // descriptor belongs in canonical_input_descriptor and in the immutable + // revision row; the shared logical key is derived from the same source so + // SQL readers and Python readers cannot drift. + definition.argTypes = expectedLogicalArgTypes return definition, nil } diff --git a/pkg/frontend/python_udf_catalog_test.go b/pkg/frontend/python_udf_catalog_test.go index be7771ec4537b..9f2ac17cbd174 100644 --- a/pkg/frontend/python_udf_catalog_test.go +++ b/pkg/frontend/python_udf_catalog_test.go @@ -269,6 +269,51 @@ func TestPersistPythonCreateRejectsMalformedDefinitionBeforeWriting(t *testing.T require.Empty(t, bh.executedSqls) } +func TestNormalizePythonDefinitionRejectsCatalogSignatureDrift(t *testing.T) { + body := pythonCatalogTestBody(t, types.T_int64) + cases := []struct { + name string + args string + retType string + message string + }{ + { + name: "arguments", + args: `[{"name":"value","type":"varchar"}]`, + retType: "bigint", + message: "catalog arguments", + }, + { + name: "return", + args: `[{"name":"value","type":"bigint"}]`, + retType: "varchar", + message: "catalog return type", + }, + } + + for _, tc := range cases { + t.Run(tc.name, func(t *testing.T) { + _, err := normalizePythonFunctionDefinition(userDefinedFunctionDefinition{ + args: tc.args, + retType: tc.retType, + body: body, + }) + require.ErrorContains(t, err, "UNSUPPORTED_ROUTINE_VERSION") + require.ErrorContains(t, err, tc.message) + }) + } +} + +func TestNormalizePythonDefinitionDerivesLogicalSignatureFromTypedBody(t *testing.T) { + normalized, err := normalizePythonFunctionDefinition(userDefinedFunctionDefinition{ + args: `[{"name":"value","type":"int"}]`, + retType: "int", + body: pythonCatalogTestBody(t, types.T_int32), + }) + require.NoError(t, err) + require.Equal(t, `["int"]`, normalized.argTypes) +} + func TestPersistPythonCreateIdentityLookupIncludesLanguage(t *testing.T) { definition := userDefinedFunctionDefinition{ name: "python_identity", From 7cc8105f7c43d3a5c273a80625dd0c4072ea19fa Mon Sep 17 00:00:00 2001 From: iamlinjunhong <1030420200@qq.com> Date: Mon, 14 Sep 2026 19:02:14 +0800 Subject: [PATCH 099/202] fix(udf): refresh worker lease for readiness --- pkg/udf/python/gateway.go | 22 ++++++++-- pkg/udf/python/gateway_integration_test.go | 48 ++++++++++++++++++++++ 2 files changed, 66 insertions(+), 4 deletions(-) diff --git a/pkg/udf/python/gateway.go b/pkg/udf/python/gateway.go index 2f999602acd55..4c113a0c807d9 100644 --- a/pkg/udf/python/gateway.go +++ b/pkg/udf/python/gateway.go @@ -171,7 +171,10 @@ func (g *Gateway) CheckLanguageReady(ctx context.Context, language string) error if err != nil { return err } - return g.ensureCapabilities(ctx, client) + // Readiness is an external admission decision. Refresh the worker + // capability record so a worker replacement at the same endpoint cannot + // inherit a stale success from the previous process. + return g.ensureCapabilities(ctx, client, true) } // ValidateDefinition asks the current worker to compile the exact artifact @@ -284,7 +287,11 @@ func (g *Gateway) ValidateDefinition(ctx context.Context, definition *udf.Routin if err != nil { return err } - if err := g.ensureCapabilities(ctx, client); err != nil { + // Definition validation is a Flight Action and therefore has no invocation + // lease in its request. It must perform its own worker-instance handshake; + // relying on a cached readiness result could validate against a replacement + // worker with a different contract. + if err := g.ensureCapabilities(ctx, client, true); err != nil { return err } payload, err := json.Marshal(definitionValidationPayload{ @@ -539,7 +546,7 @@ func (g *Gateway) Execute(ctx context.Context, invocation *udf.Invocation, resul if err != nil { return err } - if err := g.ensureCapabilities(streamCtx, client); err != nil { + if err := g.ensureCapabilities(streamCtx, client, false); err != nil { return err } // The worker instance owns the lease epoch. Copy the invocation before @@ -1065,10 +1072,17 @@ type capabilityResponse struct { // Gateway connection before OpenInvocation, and it caches only a successful // match. A worker with an unknown or different contract therefore cannot run // user code, while ordinary SQL remains independent of this client. -func (g *Gateway) ensureCapabilities(ctx context.Context, client flight.FlightServiceClient) error { +func (g *Gateway) ensureCapabilities(ctx context.Context, client flight.FlightServiceClient, forceRefresh bool) error { g.capabilityMu.Lock() defer g.capabilityMu.Unlock() g.mu.Lock() + if forceRefresh { + // Clear the old lease before probing. If the probe fails, callers must + // observe the worker as unavailable rather than retaining a stale + // success that could authorize a lease-less Flight Action. + g.capabilityReady = false + g.workerLeaseEpoch = 0 + } ready := g.capabilityReady closed := g.closed g.mu.Unlock() diff --git a/pkg/udf/python/gateway_integration_test.go b/pkg/udf/python/gateway_integration_test.go index 74577c936075f..f50913691492b 100644 --- a/pkg/udf/python/gateway_integration_test.go +++ b/pkg/udf/python/gateway_integration_test.go @@ -365,6 +365,54 @@ func TestGatewayRejectsThePreviousWorkerLeaseAfterRestart(t *testing.T) { require.Equal(t, []int64{10}, vector.MustFixedColNoTypeCheck[int64](secondResult.GetResultVector())) } +func TestGatewayReadinessRefreshesLeaseAfterWorkerRestart(t *testing.T) { + python, err := exec.LookPath("python3") + require.NoError(t, err, "python3 is required for the real worker contract test") + workerPath := realWorkerPath(t) + port := freeTCPPort(t) + first := startRealWorker(t, python, workerPath, port) + + gateway, err := NewGateway(ClientConfig{ + Enabled: true, + AllowUnisolated: true, + ServerAddress: "127.0.0.1:" + strconv.Itoa(port), + MaxBatchBytes: 1 << 20, + MaxBatchRows: 1024, + MaxActiveInvocations: 1, + RequestTimeout: 2 * time.Second, + MaxTerminalEntries: 32, + MaxTerminalBytes: 1 << 20, + TerminalRecordTTL: time.Minute, + }) + require.NoError(t, err) + defer gateway.Close() + + readyContext, cancel := context.WithTimeout(context.Background(), 5*time.Second) + require.NoError(t, gateway.CheckLanguageReady(readyContext, udf.LanguagePython)) + cancel() + gateway.mu.Lock() + firstEpoch := gateway.workerLeaseEpoch + gateway.mu.Unlock() + require.NotZero(t, firstEpoch) + + stopRealWorker(first) + second := startRealWorker(t, python, workerPath, port) + defer stopRealWorker(second) + + // CheckLanguageReady is a live admission check. It must probe the + // replacement worker rather than returning the cached success from the + // previous process, even though the endpoint and software contract are the + // same. + readyContext, cancel = context.WithTimeout(context.Background(), 5*time.Second) + require.NoError(t, gateway.CheckLanguageReady(readyContext, udf.LanguagePython)) + cancel() + gateway.mu.Lock() + secondEpoch := gateway.workerLeaseEpoch + gateway.mu.Unlock() + require.NotZero(t, secondEpoch) + require.NotEqual(t, firstEpoch, secondEpoch, "readiness must bind to the replacement worker instance") +} + func TestGatewayCloseCancelsAnInFlightExchange(t *testing.T) { python, err := exec.LookPath("python3") require.NoError(t, err, "python3 is required for the real worker contract test") From fd4c006edcbfa0aa72a34d7eaadda65293cdb2b5 Mon Sep 17 00:00:00 2001 From: iamlinjunhong <1030420200@qq.com> Date: Mon, 14 Sep 2026 19:05:31 +0800 Subject: [PATCH 100/202] fix(udf): validate temporal child validity --- pkg/udf/python/worker/test_worker.py | 12 ++++++++++++ pkg/udf/python/worker/worker.py | 11 ++++++++--- 2 files changed, 20 insertions(+), 3 deletions(-) diff --git a/pkg/udf/python/worker/test_worker.py b/pkg/udf/python/worker/test_worker.py index 38ecf5dda8d97..2e0e11ac03d39 100644 --- a/pkg/udf/python/worker/test_worker.py +++ b/pkg/udf/python/worker/test_worker.py @@ -760,6 +760,18 @@ def test_zero_temporal_is_distinct_from_null(self): self.assertEqual(True, array.field("is_zero")[0].as_py()) self.assertEqual(datetime.date(1970, 1, 1), array.field("value")[0].as_py()) + def test_temporal_valid_row_requires_both_child_fields(self): + descriptor = {"type_id": worker.DATE, "offset_width": 32, "temporal_encoding": "sql_zero_struct"} + array = pa.StructArray.from_arrays( + [ + pa.array([None, False], type=pa.bool_()), + pa.array([datetime.date(1970, 1, 1), datetime.date(2024, 1, 2)], type=pa.date32()), + ], + names=["is_zero", "value"], + ) + with self.assertRaisesRegex(ValueError, "temporal zero flag is null"): + worker._validate_array_values(array, descriptor) + def test_json_input_is_canonicalized_before_handler(self): descriptor = {"type_id": worker.JSON, "offset_width": 32, "json_encoding": "canonical_text"} array = pa.array(['{"b": [true, null, "中"], "a": 1}'], type=pa.string()) diff --git a/pkg/udf/python/worker/worker.py b/pkg/udf/python/worker/worker.py index e7816943179ec..19ea9aa2f318b 100644 --- a/pkg/udf/python/worker/worker.py +++ b/pkg/udf/python/worker/worker.py @@ -1601,9 +1601,14 @@ def _validate_array_values(array: pa.Array, descriptor: Dict[str, Any]) -> None: struct = array for index in range(len(struct)): if not struct[index].is_valid: continue - is_zero = bool(struct.field("is_zero")[index].as_py()) - value = struct.field("value")[index].as_py() - if value is None: raise ValueError("TYPE_CONTRACT: temporal child is null") + is_zero_scalar = struct.field("is_zero")[index] + value_scalar = struct.field("value")[index] + if not is_zero_scalar.is_valid: + raise ValueError("TYPE_CONTRACT: temporal zero flag is null") + if not value_scalar.is_valid: + raise ValueError("TYPE_CONTRACT: temporal child is null") + is_zero = bool(is_zero_scalar.as_py()) + value = value_scalar.as_py() if is_zero and ((type_id == DATE and value != _datetime.date(1970, 1, 1)) or (type_id == DATETIME and value != _datetime.datetime(1970, 1, 1)) or (type_id == TIMESTAMP and value != _datetime.datetime(1970, 1, 1, tzinfo=_datetime.timezone.utc))): raise ValueError("TYPE_CONTRACT: temporal zero placeholder is invalid") if not is_zero: From 6dc2c846831471e45aa603f2019e960b2f5f4f3a Mon Sep 17 00:00:00 2001 From: iamlinjunhong <1030420200@qq.com> Date: Tue, 15 Sep 2026 10:23:53 +0800 Subject: [PATCH 101/202] fix(udf): scope worker group fences by account --- pkg/udf/python/worker/test_worker.py | 33 ++++++++++++++-- pkg/udf/python/worker/worker.py | 57 ++++++++++++++++++---------- 2 files changed, 65 insertions(+), 25 deletions(-) diff --git a/pkg/udf/python/worker/test_worker.py b/pkg/udf/python/worker/test_worker.py index 2e0e11ac03d39..f57d6dfefa548 100644 --- a/pkg/udf/python/worker/test_worker.py +++ b/pkg/udf/python/worker/test_worker.py @@ -873,7 +873,7 @@ def close(self): self.assertEqual("close failed", str(cleanup_error)) self.assertNotIn(key, server._active) self.assertIn(key, server._terminal) - self.assertNotIn(key[2], server._active_groups) + self.assertNotIn((key[0], key[2]), server._active_groups) finally: server.shutdown() @@ -3154,13 +3154,13 @@ def test_active_groups_reserve_future_closed_fences(self): first = (1, "statement", "reserved-first", 1, "first", 1) second = (1, "statement", "reserved-second", 1, "second", 1) first_state = server._admit(first) - self.assertIn(first[2], server._reserved_groups) + self.assertIn((first[0], first[2]), server._reserved_groups) with self.assertRaisesRegex(ValueError, "closed-group fence is full"): server._admit(second) server._finish_invocation(first, first_state) - self.assertNotIn(first[2], server._reserved_groups) - self.assertIn(first[2], server._closed_groups) + self.assertNotIn((first[0], first[2]), server._reserved_groups) + self.assertIn((first[0], first[2]), server._closed_groups) with self.assertRaisesRegex(ValueError, "already closed"): server._admit((1, "statement", first[2], 1, "late", 1)) finally: @@ -3177,6 +3177,31 @@ def test_active_group_rejects_a_second_member_before_handler_creation(self): finally: server._finish_invocation(first, first_state) + def test_group_fence_is_scoped_by_account(self): + server = worker.RoutineFlightServer("grpc://127.0.0.1:0") + first = (1, "statement-a", "same-name", 1, "first", 1) + second = (2, "statement-b", "same-name", 1, "second", 1) + first_state = server._admit(first) + second_state = server._admit(second) + try: + self.assertIn((1, "same-name"), server._active_groups) + self.assertIn((2, "same-name"), server._active_groups) + server._finish_invocation(first, first_state) + self.assertIn(second, server._active) + self.assertIn((1, "same-name"), server._closed_groups) + self.assertNotIn((2, "same-name"), server._closed_groups) + finally: + server._finish_invocation(second, second_state) + + # Each account can advance its own epoch independently after the + # previous same-named group has closed. + self.assertIsNotNone( + server._admit((1, "statement-a-2", "same-name", 2, "third", 1)) + ) + self.assertIsNotNone( + server._admit((2, "statement-b-2", "same-name", 2, "fourth", 1)) + ) + def test_terminal_ack_requires_the_completed_fence(self): server = worker.RoutineFlightServer("grpc://127.0.0.1:0") key = (1, "statement", "group", 1, "completed", 1) diff --git a/pkg/udf/python/worker/worker.py b/pkg/udf/python/worker/worker.py index 19ea9aa2f318b..523b8a8171afc 100644 --- a/pkg/udf/python/worker/worker.py +++ b/pkg/udf/python/worker/worker.py @@ -2881,10 +2881,13 @@ def __init__( self._terminal: OrderedDict[tuple, _TerminalRecord] = OrderedDict() self._terminal_bytes = 0 self._active_bytes = 0 - self._active_groups: Dict[str, int] = {} - self._closed_groups: Dict[str, int] = {} + # A group id is scoped by the account in the fencing tuple. Keeping + # only the textual id here would let one tenant block or close a + # same-named group owned by another tenant. + self._active_groups: Dict[tuple, int] = {} + self._closed_groups: Dict[tuple, int] = {} self._closed_group_bytes = 0 - self._reserved_groups = set() + self._reserved_groups: set[tuple] = set() self._reserved_group_bytes = 0 self._clock = clock self._terminal_ttl_seconds = terminal_ttl_seconds @@ -2912,12 +2915,24 @@ def _entry_bytes(key: tuple) -> int: encoded = json.dumps(key, separators=(",", ":"), ensure_ascii=True).encode("utf-8") return len(encoded) + 128 + @staticmethod + def _group_key(key: tuple) -> tuple: + """Return the account-scoped identity of a group epoch.""" + return key[0], key[2] + + @staticmethod + def _group_fence_bytes(group_key: tuple) -> int: + # Account id is a fixed-width uint64 on the wire. Include it in the + # bounded fence estimate so the capacity accounting matches the + # account-scoped key rather than only charging the display id. + return len(group_key[1].encode("utf-8")) + 16 + def _purge_terminal_locked(self, now: float) -> None: expired = [ key for key, record in self._terminal.items() if record.expires_at <= now - and self._closed_groups.get(key[2], 0) >= key[3] + and self._closed_groups.get(self._group_key(key), 0) >= key[3] ] for key in expired: record = self._terminal.pop(key) @@ -2925,23 +2940,23 @@ def _purge_terminal_locked(self, now: float) -> None: def _admit(self, key: tuple) -> _InvocationState: size = self._entry_bytes(key) - group_id, group_epoch = key[2], key[3] + group_key, group_epoch = self._group_key(key), key[3] with self._lock: - if self._closed_groups.get(group_id, 0) >= group_epoch: + if self._closed_groups.get(group_key, 0) >= group_epoch: raise ValueError("PROTOCOL: execution group epoch is already closed") self._purge_terminal_locked(self._clock()) if key in self._active: raise ValueError("PROTOCOL: invocation fence is active") if key in self._terminal: raise ValueError("PROTOCOL: invocation fence is terminal") - if group_id in self._active_groups: + if group_key in self._active_groups: raise ValueError("PROTOCOL: execution group epoch is already active") if len(self._active) + len(self._terminal) >= MAX_LEDGER_ENTRIES: raise ValueError("RESOURCE_EXHAUSTED: terminal ledger entries are full") if self._active_bytes + self._terminal_bytes + size > MAX_LEDGER_BYTES: raise ValueError("RESOURCE_EXHAUSTED: terminal ledger bytes are full") - if group_id not in self._closed_groups and group_id not in self._reserved_groups: - fence_bytes = len(group_id.encode("utf-8")) + 8 + if group_key not in self._closed_groups and group_key not in self._reserved_groups: + fence_bytes = self._group_fence_bytes(group_key) if ( len(self._closed_groups) + len(self._reserved_groups) >= MAX_CLOSED_GROUP_ENTRIES @@ -2952,12 +2967,12 @@ def _admit(self, key: tuple) -> _InvocationState: # Reserve the future tombstone before creating the handler. # Terminal cleanup converts this reservation into the # closed-generation fence. - self._reserved_groups.add(group_id) + self._reserved_groups.add(group_key) self._reserved_group_bytes += fence_bytes state = _InvocationState({}, size) self._active[key] = state self._active_bytes += size - self._active_groups[group_id] = group_epoch + self._active_groups[group_key] = group_epoch return state def _require_current_lease(self, key: tuple) -> None: @@ -2976,14 +2991,14 @@ def _remember_terminal_locked(self, key: tuple, state: _InvocationState) -> None self._terminal_bytes += state.terminal_bytes def _close_group_epoch_locked(self, key: tuple) -> None: - group_id, group_epoch = key[2], key[3] - current = self._closed_groups.get(group_id, 0) + group_key, group_epoch = self._group_key(key), key[3] + current = self._closed_groups.get(group_key, 0) if group_epoch <= current: return - if group_id not in self._closed_groups: - fence_bytes = len(group_id.encode("utf-8")) + 8 - if group_id in self._reserved_groups: - self._reserved_groups.remove(group_id) + if group_key not in self._closed_groups: + fence_bytes = self._group_fence_bytes(group_key) + if group_key in self._reserved_groups: + self._reserved_groups.remove(group_key) self._reserved_group_bytes -= fence_bytes elif ( len(self._closed_groups) >= MAX_CLOSED_GROUP_ENTRIES @@ -2994,7 +3009,7 @@ def _close_group_epoch_locked(self, key: tuple) -> None: # and refuse collection until the fence is reconstructed. return self._closed_group_bytes += fence_bytes - self._closed_groups[group_id] = group_epoch + self._closed_groups[group_key] = group_epoch def _finish_invocation(self, key: tuple, state: Optional[_InvocationState]) -> None: # A rejected duplicate Open has no ownership of the existing state. @@ -3008,9 +3023,9 @@ def _finish_invocation(self, key: tuple, state: Optional[_InvocationState]) -> N return self._active.pop(key, None) self._active_bytes -= state.terminal_bytes - group_id, group_epoch = key[2], key[3] - if self._active_groups.get(group_id) == group_epoch: - self._active_groups.pop(group_id, None) + group_key, group_epoch = self._group_key(key), key[3] + if self._active_groups.get(group_key) == group_epoch: + self._active_groups.pop(group_key, None) # A started invocation is terminal even when the worker reports an # error. Retaining the fence prevents a late retry from running # user code a second time. From 3bfe379db7990167c3396244109968f7e6b2ec4c Mon Sep 17 00:00:00 2001 From: iamlinjunhong <1030420200@qq.com> Date: Tue, 15 Sep 2026 10:26:16 +0800 Subject: [PATCH 102/202] fix(udf): scope gateway groups by account --- pkg/udf/python/gateway.go | 37 +++++++++++++++++------- pkg/udf/python/gateway_admission_test.go | 33 +++++++++++++++++++-- 2 files changed, 58 insertions(+), 12 deletions(-) diff --git a/pkg/udf/python/gateway.go b/pkg/udf/python/gateway.go index 4c113a0c807d9..6667a824e8c2a 100644 --- a/pkg/udf/python/gateway.go +++ b/pkg/udf/python/gateway.go @@ -17,6 +17,7 @@ import ( "errors" "fmt" "io" + "strconv" "strings" "sync" "time" @@ -716,6 +717,7 @@ type invocationAdmission struct { group *protocol.ExecutionGroup credit *protocol.LedgerCredit key string + groupKey string invocationID string mu sync.Mutex @@ -738,6 +740,7 @@ func (g *Gateway) admitInvocation(invocation *udf.Invocation) (*invocationAdmiss if err != nil { return nil, err } + groupKey := scopedGroupKey(invocation.Tuple.AccountID, invocation.Tuple.GroupID) // g.mu -> admissionMu is the lifecycle lock order. Close uses the same // order so a concurrent shutdown cannot return while this invocation is // still being admitted. Keep g.mu held until the complete claim is either @@ -752,14 +755,14 @@ func (g *Gateway) admitInvocation(invocation *udf.Invocation) (*invocationAdmiss ledger := g.ledger ttl := g.ledgerTTL active := g.active - if err := g.claimGroupLocked(invocation.Tuple.GroupID, invocation.Tuple.GroupEpoch); err != nil { + if err := g.claimGroupLocked(groupKey, invocation.Tuple.GroupEpoch); err != nil { g.admissionMu.Unlock() g.mu.Unlock() return nil, err } g.admissionMu.Unlock() if ledger == nil || active == nil { - g.releaseGroupClaim(invocation.Tuple.GroupID, invocation.Tuple.GroupEpoch) + g.releaseGroupClaim(groupKey, invocation.Tuple.GroupEpoch) g.mu.Unlock() return nil, fmt.Errorf("python udf: admission state is not initialized") } @@ -770,15 +773,15 @@ func (g *Gateway) admitInvocation(invocation *udf.Invocation) (*invocationAdmiss // that permits an expired tombstone to release its reserved capacity. ledger.Expire(time.Now(), g.epochClosed) entryBytes := int64(len(key)) - credit, err := ledger.Reserve(invocation.Tuple.GroupID, invocation.Tuple.GroupEpoch, 1, entryBytes) + credit, err := ledger.Reserve(groupKey, invocation.Tuple.GroupEpoch, 1, entryBytes) if err != nil { - g.releaseGroupClaim(invocation.Tuple.GroupID, invocation.Tuple.GroupEpoch) + g.releaseGroupClaim(groupKey, invocation.Tuple.GroupEpoch) g.mu.Unlock() return nil, fmt.Errorf("RESOURCE_EXHAUSTED: Python UDF terminal ledger: %w", err) } if err := credit.Add(key, entryBytes, time.Now().Add(ttl)); err != nil { credit.ReleaseUnused() - g.releaseGroupClaim(invocation.Tuple.GroupID, invocation.Tuple.GroupEpoch) + g.releaseGroupClaim(groupKey, invocation.Tuple.GroupEpoch) g.mu.Unlock() return nil, err } @@ -790,7 +793,7 @@ func (g *Gateway) admitInvocation(invocation *udf.Invocation) (*invocationAdmiss default: _ = ledger.Abandon(key) credit.ReleaseUnused() - g.releaseGroupClaim(invocation.Tuple.GroupID, invocation.Tuple.GroupEpoch) + g.releaseGroupClaim(groupKey, invocation.Tuple.GroupEpoch) g.mu.Unlock() return nil, fmt.Errorf("RESOURCE_EXHAUSTED: Python UDF active invocation slots are full") } @@ -808,7 +811,7 @@ func (g *Gateway) admitInvocation(invocation *udf.Invocation) (*invocationAdmiss <-active _ = ledger.Abandon(key) credit.ReleaseUnused() - g.releaseGroupClaim(invocation.Tuple.GroupID, invocation.Tuple.GroupEpoch) + g.releaseGroupClaim(groupKey, invocation.Tuple.GroupEpoch) g.mu.Unlock() return nil, err } @@ -816,14 +819,14 @@ func (g *Gateway) admitInvocation(invocation *udf.Invocation) (*invocationAdmiss if err != nil { _ = group.Close(protocol.ReasonPartialOpenError) _ = ledger.Abandon(key) - g.releaseGroupClaim(invocation.Tuple.GroupID, invocation.Tuple.GroupEpoch) + g.releaseGroupClaim(groupKey, invocation.Tuple.GroupEpoch) g.mu.Unlock() return nil, err } if err := token.Commit(); err != nil { _ = group.Close(protocol.ReasonPartialOpenError) _ = ledger.Abandon(key) - g.releaseGroupClaim(invocation.Tuple.GroupID, invocation.Tuple.GroupEpoch) + g.releaseGroupClaim(groupKey, invocation.Tuple.GroupEpoch) g.mu.Unlock() return nil, err } @@ -833,6 +836,7 @@ func (g *Gateway) admitInvocation(invocation *udf.Invocation) (*invocationAdmiss group: group, credit: credit, key: key, + groupKey: groupKey, invocationID: invocation.Tuple.InvocationID, memberOpen: true, }, nil @@ -947,6 +951,13 @@ func (g *Gateway) epochClosed(groupID string, groupEpoch uint64) bool { return ok && groupEpoch <= closedEpoch } +// scopedGroupKey is an internal ownership key. The wire protocol keeps the +// original group id, but group reservations and terminal fences are scoped by +// account because the tuple identity is {account_id, group_id, group_epoch}. +func scopedGroupKey(accountID uint64, groupID string) string { + return strconv.FormatUint(accountID, 10) + "\x00" + groupID +} + func invocationLedgerKey(tuple protocol.FencingTuple) (string, error) { wire, err := json.Marshal(tuple) if err != nil { @@ -1005,7 +1016,13 @@ func (a *invocationAdmission) finish(attempted bool, reason protocol.CloseReason errs = append(errs, err) } if a.group.State() == protocol.GroupReleased { - a.gateway.closeGroupEpoch(a.group.ID(), a.group.Epoch()) + groupKey := a.groupKey + if groupKey == "" { + // Keep manually constructed admission records safe for protocol + // tests; all production admissions set the account-scoped key. + groupKey = a.group.ID() + } + a.gateway.closeGroupEpoch(groupKey, a.group.Epoch()) } if err := errors.Join(errs...); err == nil { a.finished = true diff --git a/pkg/udf/python/gateway_admission_test.go b/pkg/udf/python/gateway_admission_test.go index 0288b0e3fd0ef..c29eb711f1423 100644 --- a/pkg/udf/python/gateway_admission_test.go +++ b/pkg/udf/python/gateway_admission_test.go @@ -159,7 +159,7 @@ func TestGatewayAdmissionRetriesOwnerCleanupAfterGroupReleaseFailure(t *testing. admission := &invocationAdmission{ gateway: gateway, group: group, credit: credit, - key: "release-retry-key", invocationID: "member", memberOpen: true, + key: "release-retry-key", groupKey: "release-retry", invocationID: "member", memberOpen: true, } require.ErrorContains(t, admission.finish(true, protocol.ReasonFailure), "injected group release failure") require.Equal(t, protocol.GroupDraining, group.State()) @@ -194,7 +194,7 @@ func TestGatewayAdmissionReservesClosedGroupCapacityWhileActive(t *testing.T) { gateway.admissionMu.Lock() require.Empty(t, gateway.admittedGroups) require.Zero(t, gateway.reservedGroupBytes) - require.Equal(t, uint64(1), gateway.closedGroups[first.Tuple.GroupID]) + require.Equal(t, uint64(1), gateway.closedGroups[scopedGroupKey(first.Tuple.AccountID, first.Tuple.GroupID)]) gateway.admissionMu.Unlock() // A new epoch of the same group reuses its already-reserved fence. A @@ -211,3 +211,32 @@ func TestGatewayAdmissionReservesClosedGroupCapacityWhileActive(t *testing.T) { _, err = gateway.admitInvocation(second) require.ErrorIs(t, err, protocol.ErrLedgerFull) } + +func TestGatewayAdmissionScopesGroupByAccount(t *testing.T) { + gateway := newAdmissionTestGateway(t, 2) + first := validInvocation() + first.Tuple.GroupID = "same-name" + first.Tuple.InvocationID = "account-one" + second := validInvocation() + second.Tuple.AccountID = 2 + second.Tuple.GroupID = first.Tuple.GroupID + second.Tuple.InvocationID = "account-two" + + firstAdmission, err := gateway.admitInvocation(first) + require.NoError(t, err) + secondAdmission, err := gateway.admitInvocation(second) + require.NoError(t, err) + require.Len(t, gateway.active, 2) + + require.NoError(t, firstAdmission.finish(true, protocol.ReasonInputEOF)) + require.Len(t, gateway.active, 1) + gateway.admissionMu.Lock() + require.Equal(t, uint64(1), gateway.closedGroups[scopedGroupKey(1, "same-name")]) + require.NotContains(t, gateway.closedGroups, scopedGroupKey(2, "same-name")) + gateway.admissionMu.Unlock() + + require.NoError(t, secondAdmission.finish(true, protocol.ReasonInputEOF)) + gateway.admissionMu.Lock() + require.Equal(t, uint64(1), gateway.closedGroups[scopedGroupKey(2, "same-name")]) + gateway.admissionMu.Unlock() +} From ea89da93a267529d45782440901d430754bd6eba Mon Sep 17 00:00:00 2001 From: iamlinjunhong <1030420200@qq.com> Date: Tue, 15 Sep 2026 10:39:26 +0800 Subject: [PATCH 103/202] fix(udf): reject noncanonical descriptor fields --- pkg/udf/python/arrow.go | 11 +++++++---- pkg/udf/python/arrow_test.go | 14 ++++++++++++++ pkg/udf/python/worker/test_worker.py | 13 +++++++++++++ pkg/udf/python/worker/worker.py | 8 +++++--- 4 files changed, 39 insertions(+), 7 deletions(-) diff --git a/pkg/udf/python/arrow.go b/pkg/udf/python/arrow.go index c7615947b9692..0185f4ec768b5 100644 --- a/pkg/udf/python/arrow.go +++ b/pkg/udf/python/arrow.go @@ -203,21 +203,24 @@ func (d TypeDescriptor) Validate() error { return fmt.Errorf("TYPE_CONTRACT: decimal descriptor carries a charset") } case types.T_date: - if d.Scale != 0 || d.Charset != types.CharsetLegacy { - return fmt.Errorf("TYPE_CONTRACT: DATE descriptor has invalid scale or charset") + if d.Width != 0 || d.Scale != 0 || d.Charset != types.CharsetLegacy { + return fmt.Errorf("TYPE_CONTRACT: DATE descriptor has unused or invalid fields") } case types.T_time, types.T_datetime, types.T_timestamp: - if d.Scale > 6 || d.Charset != types.CharsetLegacy { + if d.Width != 0 || d.Scale > 6 || d.Charset != types.CharsetLegacy { return fmt.Errorf("TYPE_CONTRACT: temporal scale or charset is outside the supported range") } case types.T_char, types.T_varchar, types.T_text: + if d.Scale != 0 { + return fmt.Errorf("TYPE_CONTRACT: text descriptor carries an unused scale") + } switch d.Charset { case types.CharsetLegacy, types.CharsetUTF8MB4Bin, types.CharsetUTF8: default: return fmt.Errorf("TYPE_CONTRACT: unsupported text charset %d", d.Charset) } case types.T_binary, types.T_varbinary, types.T_blob: - if d.Charset != types.CharsetBinary { + if d.Scale != 0 || d.Charset != types.CharsetBinary { return fmt.Errorf("TYPE_CONTRACT: binary descriptor must use the binary charset") } case types.T_uuid: diff --git a/pkg/udf/python/arrow_test.go b/pkg/udf/python/arrow_test.go index c0fd830d93746..4b1b3de7c1400 100644 --- a/pkg/udf/python/arrow_test.go +++ b/pkg/udf/python/arrow_test.go @@ -656,6 +656,20 @@ func TestTypeDescriptorRejectsNonCanonicalDomain(t *testing.T) { } } +func TestTypeDescriptorRejectsUnusedDomainFields(t *testing.T) { + invalid := []TypeDescriptor{ + {TypeID: int32(types.T_date), Width: 1, OffsetWidth: 32, TemporalEncoding: "sql_zero_struct"}, + {TypeID: int32(types.T_datetime), Width: 1, OffsetWidth: 32, TemporalEncoding: "sql_zero_struct"}, + {TypeID: int32(types.T_time), Width: 1, Scale: 6, OffsetWidth: 32}, + {TypeID: int32(types.T_varchar), Width: 16, Scale: 1, Charset: types.CharsetUTF8, OffsetWidth: 32}, + {TypeID: int32(types.T_varbinary), Width: 16, Scale: 1, Charset: types.CharsetBinary, OffsetWidth: 32}, + } + for _, descriptor := range invalid { + _, err := descriptor.Field("value") + require.Error(t, err, "%+v", descriptor) + } +} + func TestFloatDescriptorsRoundTripThroughArrowFieldContract(t *testing.T) { for _, typ := range []types.T{types.T_float32, types.T_float64} { descriptor, err := NewTypeDescriptor(typ.ToType()) diff --git a/pkg/udf/python/worker/test_worker.py b/pkg/udf/python/worker/test_worker.py index f57d6dfefa548..08d36d21f0706 100644 --- a/pkg/udf/python/worker/test_worker.py +++ b/pkg/udf/python/worker/test_worker.py @@ -835,6 +835,19 @@ def test_decimal_precision_and_vector_child_validity_are_checked(self): visible = valid.slice(1, 1) self.assertEqual(visible.to_pylist(), worker._output_array(visible, vector_descriptor, 1).to_pylist()) + def test_descriptor_rejects_unused_domain_fields(self): + invalid = [ + {"type_id": worker.DATE, "width": 1, "offset_width": 32, "temporal_encoding": "sql_zero_struct"}, + {"type_id": worker.DATETIME, "width": 1, "offset_width": 32, "temporal_encoding": "sql_zero_struct"}, + {"type_id": worker.TIME, "width": 1, "scale": 6, "offset_width": 32}, + {"type_id": worker.VARCHAR, "width": 16, "scale": 1, "charset": 3, "offset_width": 32}, + {"type_id": worker.VARBINARY, "width": 16, "scale": 1, "charset": 1, "offset_width": 32}, + ] + for descriptor in invalid: + with self.subTest(descriptor=descriptor): + with self.assertRaises(ValueError): + worker._field("value", descriptor) + def test_scalar_vector_null_keeps_fixed_size_child_slots(self): descriptor = {"type_id": worker.VECF32, "width": 2, "offset_width": 0} array = worker._output_array( diff --git a/pkg/udf/python/worker/worker.py b/pkg/udf/python/worker/worker.py index 523b8a8171afc..e18aedb71f34c 100644 --- a/pkg/udf/python/worker/worker.py +++ b/pkg/udf/python/worker/worker.py @@ -1192,16 +1192,18 @@ def _validate_descriptor_domain(descriptor: Dict[str, Any]) -> None: if charset: raise ValueError("TYPE_CONTRACT: decimal descriptor carries a charset") elif type_id == DATE: - if scale or charset: + if width or scale or charset: raise ValueError("TYPE_CONTRACT: DATE descriptor has invalid scale or charset") elif type_id in (TIME, DATETIME, TIMESTAMP): - if scale > 6 or charset: + if width or scale > 6 or charset: raise ValueError("TYPE_CONTRACT: temporal scale or charset is outside the supported range") elif type_id in (CHAR, VARCHAR, TEXT): + if scale: + raise ValueError("TYPE_CONTRACT: text descriptor carries an unused scale") if charset not in (0, 2, 3): raise ValueError("TYPE_CONTRACT: unsupported text charset") elif type_id in (BINARY, VARBINARY, BLOB): - if charset != 1: + if scale or charset != 1: raise ValueError("TYPE_CONTRACT: binary descriptor must use the binary charset") elif type_id == UUID: if width or scale or charset: From 2d63df39775760522292707943cd3757d5a509f2 Mon Sep 17 00:00:00 2001 From: iamlinjunhong <1030420200@qq.com> Date: Tue, 15 Sep 2026 10:42:18 +0800 Subject: [PATCH 104/202] fix(udf): bound worker fencing identities --- pkg/udf/python/worker/test_worker.py | 15 +++++++++++++++ pkg/udf/python/worker/worker.py | 9 ++++++++- 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/pkg/udf/python/worker/test_worker.py b/pkg/udf/python/worker/test_worker.py index 08d36d21f0706..0e490d1069a74 100644 --- a/pkg/udf/python/worker/test_worker.py +++ b/pkg/udf/python/worker/test_worker.py @@ -848,6 +848,21 @@ def test_descriptor_rejects_unused_domain_fields(self): with self.assertRaises(ValueError): worker._field("value", descriptor) + def test_fencing_tuple_rejects_oversized_identity_components(self): + for field in ("statement_id", "group_id", "invocation_id"): + value = { + "account_id": 1, + "statement_id": "statement", + "group_id": "group", + "group_epoch": 1, + "invocation_id": "invocation", + "lease_epoch": 1, + } + value[field] = "x" * (worker.MAX_FENCE_COMPONENT_BYTES + 1) + with self.subTest(field=field): + with self.assertRaisesRegex(ValueError, "component is too large"): + worker._tuple_key(value) + def test_scalar_vector_null_keeps_fixed_size_child_slots(self): descriptor = {"type_id": worker.VECF32, "width": 2, "offset_width": 0} array = worker._output_array( diff --git a/pkg/udf/python/worker/worker.py b/pkg/udf/python/worker/worker.py index e18aedb71f34c..f3192b521a728 100644 --- a/pkg/udf/python/worker/worker.py +++ b/pkg/udf/python/worker/worker.py @@ -65,6 +65,11 @@ class _NoFlightServerBase: PROTOCOL_VERSION = 1 MAX_CONTROL_BYTES = 1 << 20 +# Keep the Python wire boundary identical to protocol.FencingTuple's +# component limit. The control frame limit alone is not sufficient: a +# single oversized identity would otherwise consume most of every ledger +# entry and would be accepted by the worker after Go rejected it. +MAX_FENCE_COMPONENT_BYTES = 256 # Admission bounds cover active fences and retained terminal tombstones. The # names deliberately describe the whole ledger so a future cleanup change # cannot mistake active work for reclaimable terminal state. @@ -707,7 +712,9 @@ def _tuple_key(value: Dict[str, Any]) -> tuple: raise ValueError("PROTOCOL: incomplete fencing tuple") try: for key in string_fields: - value[key].encode("utf-8") + encoded = value[key].encode("utf-8") + if len(encoded) > MAX_FENCE_COMPONENT_BYTES: + raise ValueError("PROTOCOL: fencing tuple component is too large") except UnicodeEncodeError as exc: raise ValueError("PROTOCOL: fencing tuple contains invalid UTF-8") from exc account_id = value.get("account_id") From b0af418531ba6003b6a48dca738cd348d417e298 Mon Sep 17 00:00:00 2001 From: iamlinjunhong <1030420200@qq.com> Date: Tue, 15 Sep 2026 10:47:07 +0800 Subject: [PATCH 105/202] fix(udf): align Python security IDs with uint32 --- pkg/udf/python/worker/test_worker.py | 18 ++++++++++++++++++ pkg/udf/python/worker/worker.py | 12 +++++++++++- 2 files changed, 29 insertions(+), 1 deletion(-) diff --git a/pkg/udf/python/worker/test_worker.py b/pkg/udf/python/worker/test_worker.py index 0e490d1069a74..4b706eedf6be2 100644 --- a/pkg/udf/python/worker/test_worker.py +++ b/pkg/udf/python/worker/test_worker.py @@ -303,6 +303,24 @@ def test_typed_routine_semantics_reject_partial_or_definer_contract(self): with self.assertRaisesRegex(ValueError, "incomplete typed routine semantic contract"): worker._validate_typed_call_contract(payload) + def test_typed_routine_semantics_rejects_non_uint32_principal(self): + payload = { + "callsite_id": "python/1", + "may_error": True, + "security_mode": "INVOKER", + "leakproof": False, + "security_frame": { + "contract_version": 1, + "mode": "INVOKER", + "invoker_user_id": worker.MAX_SECURITY_PRINCIPAL_ID + 1, + "invoker_role_id": 8, + "effective_user_id": worker.MAX_SECURITY_PRINCIPAL_ID + 1, + "effective_role_id": 8, + }, + } + with self.assertRaisesRegex(ValueError, "invalid Python security frame"): + worker._validate_typed_call_contract(payload) + def test_handler_quota_uses_typed_effective_principal(self): payload = complete_open_payload( { diff --git a/pkg/udf/python/worker/worker.py b/pkg/udf/python/worker/worker.py index f3192b521a728..43c568e45fbda 100644 --- a/pkg/udf/python/worker/worker.py +++ b/pkg/udf/python/worker/worker.py @@ -70,6 +70,11 @@ class _NoFlightServerBase: # single oversized identity would otherwise consume most of every ledger # entry and would be accepted by the worker after Go rejected it. MAX_FENCE_COMPONENT_BYTES = 256 +# SecurityFrame uses uint32 principal identifiers in the language-neutral +# contract. Keep the Python decoder's acceptance domain identical to Go; +# checking only for a non-negative Python int would admit values that no Go +# caller can represent and would make quota/security identity non-canonical. +MAX_SECURITY_PRINCIPAL_ID = (1 << 32) - 1 # Admission bounds cover active fences and retained terminal tombstones. The # names deliberately describe the whole ledger so a future cleanup change # cannot mistake active work for reclaimable terminal state. @@ -608,7 +613,12 @@ def _validate_typed_call_contract(payload: Dict[str, Any]) -> None: if frame.get("contract_version") != 1 or frame.get("mode") != "INVOKER": raise ValueError("UNSUPPORTED_ROUTINE_VERSION: unsupported Python security frame") integer_fields = ("invoker_user_id", "invoker_role_id", "effective_user_id", "effective_role_id") - if any(type(frame.get(key)) is not int or frame[key] < 0 for key in integer_fields): + if any( + type(frame.get(key)) is not int + or frame[key] < 0 + or frame[key] > MAX_SECURITY_PRINCIPAL_ID + for key in integer_fields + ): raise ValueError("UNSUPPORTED_ROUTINE_VERSION: invalid Python security frame") if frame["invoker_user_id"] != frame["effective_user_id"] or frame["invoker_role_id"] != frame["effective_role_id"]: raise ValueError("UNSUPPORTED_ROUTINE_VERSION: Python security frame changed effective principal") From 250f8d201fb7c25d5ae6915a10282433ef55841f Mon Sep 17 00:00:00 2001 From: iamlinjunhong <1030420200@qq.com> Date: Tue, 15 Sep 2026 10:48:16 +0800 Subject: [PATCH 106/202] fix(udf): enforce required worker controls on encode --- pkg/udf/python/worker/test_worker.py | 25 +++++++++++++++++++++++++ pkg/udf/python/worker/worker.py | 5 ++++- 2 files changed, 29 insertions(+), 1 deletion(-) diff --git a/pkg/udf/python/worker/test_worker.py b/pkg/udf/python/worker/test_worker.py index 4b706eedf6be2..d22d846ceb9e6 100644 --- a/pkg/udf/python/worker/test_worker.py +++ b/pkg/udf/python/worker/test_worker.py @@ -3471,6 +3471,31 @@ def test_control_encoder_rejects_nonstandard_json_numbers(self): } ) + def test_control_encoder_rejects_missing_required_fields(self): + tuple_value = { + "account_id": 1, + "statement_id": "statement", + "group_id": "group", + "group_epoch": 1, + "invocation_id": "invocation", + "lease_epoch": 1, + } + cases = ( + ("InputBatch", {}, "sequence"), + ("EndInput", {}, "last_sequence"), + ( + "Finish", + {"last_result_sequence": 0, "finish_id": "finish", "status": "OK"}, + "last_sequence", + ), + ("AcknowledgeFinish", {}, "finish_id"), + ) + for kind, fields, field in cases: + with self.assertRaisesRegex(ValueError, f"missing.*{field}"): + worker._encode_control( + {"kind": kind, "tuple": tuple_value, **fields} + ) + def test_control_rejects_unknown_fencing_tuple_fields(self): tuple_value = { "account_id": 0, diff --git a/pkg/udf/python/worker/worker.py b/pkg/udf/python/worker/worker.py index 43c568e45fbda..5d5fa16b56c59 100644 --- a/pkg/udf/python/worker/worker.py +++ b/pkg/udf/python/worker/worker.py @@ -1120,7 +1120,10 @@ def _encode_control(value: Dict[str, Any]) -> bytes: raise ValueError("PROTOCOL: unsupported control field") value.setdefault("version", PROTOCOL_VERSION) _tuple_key(value["tuple"]) - _validate_control_fields(value, wire=False) + # The encoder is the last boundary before bytes leave the worker. It + # must enforce the same required fields as the decoder; otherwise a new + # producer can emit a frame that Go's MarshalControl would reject. + _validate_control_fields(value, wire=True) try: data = json.dumps( value, From 6cf4cfc3f4fd447fa0089b2364fa50deec12c770 Mon Sep 17 00:00:00 2001 From: iamlinjunhong <1030420200@qq.com> Date: Tue, 15 Sep 2026 10:49:05 +0800 Subject: [PATCH 107/202] fix(udf): bound Python statement timestamps --- pkg/udf/python/worker/test_worker.py | 25 +++++++++++++++++++++++++ pkg/udf/python/worker/worker.py | 11 ++++++++++- 2 files changed, 35 insertions(+), 1 deletion(-) diff --git a/pkg/udf/python/worker/test_worker.py b/pkg/udf/python/worker/test_worker.py index d22d846ceb9e6..2d5962e087d65 100644 --- a/pkg/udf/python/worker/test_worker.py +++ b/pkg/udf/python/worker/test_worker.py @@ -280,6 +280,31 @@ def test_typed_statement_context_accepts_unix_epoch(self): statement.statement_timestamp_utc, ) + def test_typed_statement_context_rejects_timestamp_outside_int64(self): + typed = { + "contract_version": 1, + "statement_timestamp_utc": worker.MAX_INT64 + 1, + "timezone_kind": "FIXED_OFFSET", + "timezone_offset_minutes": 0, + "sql_mode": [], + "current_user": "alice", + "connection_collation": "utf8mb4_bin", + } + with self.assertRaisesRegex(ValueError, "invalid typed statement timestamp"): + worker._typed_statement_context(typed) + + with self.assertRaisesRegex(ValueError, "invalid statement_timestamp_utc"): + worker._statement_context( + { + "statement_timestamp_utc": str(worker.MIN_INT64 - 1), + "session_timezone_kind": "FIXED_OFFSET", + "session_timezone_offset_minutes": "0", + "sql_mode": "[]", + "current_user": "alice", + "connection_collation": "utf8mb4_bin", + } + ) + def test_typed_routine_semantics_reject_partial_or_definer_contract(self): payload = { "callsite_id": "python/1", diff --git a/pkg/udf/python/worker/worker.py b/pkg/udf/python/worker/worker.py index 5d5fa16b56c59..39ea4af0f851d 100644 --- a/pkg/udf/python/worker/worker.py +++ b/pkg/udf/python/worker/worker.py @@ -75,6 +75,8 @@ class _NoFlightServerBase: # checking only for a non-negative Python int would admit values that no Go # caller can represent and would make quota/security identity non-canonical. MAX_SECURITY_PRINCIPAL_ID = (1 << 32) - 1 +MIN_INT64 = -(1 << 63) +MAX_INT64 = (1 << 63) - 1 # Admission bounds cover active fences and retained terminal tombstones. The # names deliberately describe the whole ledger so a future cleanup change # cannot mistake active work for reclaimable terminal state. @@ -545,7 +547,12 @@ def _typed_statement_context(raw: Any) -> Optional[Dict[str, Any]]: if raw.get("contract_version") != 1: raise ValueError("UNSUPPORTED_ROUTINE_VERSION: unsupported statement context contract") timestamp = raw.get("statement_timestamp_utc") - if isinstance(timestamp, bool) or not isinstance(timestamp, int): + if ( + isinstance(timestamp, bool) + or not isinstance(timestamp, int) + or timestamp < MIN_INT64 + or timestamp > MAX_INT64 + ): raise ValueError("PROTOCOL: invalid typed statement timestamp") timezone_kind = raw.get("timezone_kind") if not isinstance(timezone_kind, str): @@ -658,6 +665,8 @@ def _statement_context(raw: Any) -> Optional[StatementContext]: timestamp_text = _required_context_value(raw, "statement_timestamp_utc") try: timestamp_micros = int(timestamp_text, 10) + if timestamp_micros < MIN_INT64 or timestamp_micros > MAX_INT64: + raise ValueError("timestamp is outside int64 range") seconds, micros = divmod(timestamp_micros, 1_000_000) timestamp = _datetime.datetime(1970, 1, 1, tzinfo=_datetime.timezone.utc) + _datetime.timedelta(seconds=seconds, microseconds=micros) except (TypeError, ValueError, OverflowError) as exc: From 30f5dce7c0531d010d56204930c4a8850275897a Mon Sep 17 00:00:00 2001 From: iamlinjunhong <1030420200@qq.com> Date: Tue, 15 Sep 2026 10:50:09 +0800 Subject: [PATCH 108/202] fix(udf): bound Python descriptor integers --- pkg/udf/python/worker/test_worker.py | 17 +++++++++++++++++ pkg/udf/python/worker/worker.py | 5 +++++ 2 files changed, 22 insertions(+) diff --git a/pkg/udf/python/worker/test_worker.py b/pkg/udf/python/worker/test_worker.py index 2d5962e087d65..9e0f18f44d217 100644 --- a/pkg/udf/python/worker/test_worker.py +++ b/pkg/udf/python/worker/test_worker.py @@ -707,6 +707,23 @@ def test_descriptor_domain_is_strict(self): with self.assertRaisesRegex(ValueError, "TYPE_CONTRACT"): worker._field("value", descriptor) + def test_descriptor_integer_fields_match_go_int32_wire_domain(self): + descriptor = { + "type_id": worker.VARCHAR, + "width": worker.MAX_INT32, + "offset_width": 32, + } + self.assertEqual(pa.string(), worker._field("value", descriptor).type) + for key in ("type_id", "width", "scale", "offset_width"): + invalid = dict(descriptor) + invalid[key] = worker.MAX_INT32 + 1 + if key == "scale": + invalid["type_id"] = worker.DECIMAL128 + invalid["width"] = 38 + with self.subTest(key=key): + with self.assertRaisesRegex(ValueError, "outside int32 range"): + worker._field("value", invalid) + def test_float_descriptors_round_trip_for_scalar_and_vector_contracts(self): for type_id, arrow_type in ( (worker.FLOAT32, pa.float32()), diff --git a/pkg/udf/python/worker/worker.py b/pkg/udf/python/worker/worker.py index 39ea4af0f851d..f09e2b9242399 100644 --- a/pkg/udf/python/worker/worker.py +++ b/pkg/udf/python/worker/worker.py @@ -77,6 +77,8 @@ class _NoFlightServerBase: MAX_SECURITY_PRINCIPAL_ID = (1 << 32) - 1 MIN_INT64 = -(1 << 63) MAX_INT64 = (1 << 63) - 1 +MIN_INT32 = -(1 << 31) +MAX_INT32 = (1 << 31) - 1 # Admission bounds cover active fences and retained terminal tombstones. The # names deliberately describe the whole ledger so a future cleanup change # cannot mistake active work for reclaimable terminal state. @@ -1158,6 +1160,9 @@ def _canonical_descriptor(descriptor: Dict[str, Any]) -> bytes: for key in _DESCRIPTOR_INT_KEYS - {"type_id"}: if key in descriptor and type(descriptor[key]) is not int: raise ValueError(f"TYPE_CONTRACT: descriptor {key} must be an integer") + for key in _DESCRIPTOR_INT_KEYS: + if key in descriptor and not MIN_INT32 <= descriptor[key] <= MAX_INT32: + raise ValueError(f"TYPE_CONTRACT: descriptor {key} is outside int32 range") for key in _DESCRIPTOR_TEXT_KEYS: if key in descriptor and type(descriptor[key]) is not str: raise ValueError(f"TYPE_CONTRACT: descriptor {key} must be text") From 976b42ea926bb81a344e36d14eab8007ed40c34b Mon Sep 17 00:00:00 2001 From: iamlinjunhong <1030420200@qq.com> Date: Tue, 15 Sep 2026 10:52:42 +0800 Subject: [PATCH 109/202] fix(udf): verify runtime result production --- pkg/container/vector/functionTools.go | 15 ++++++++ pkg/sql/colexec/external_routine_eval.go | 10 ++++++ pkg/sql/colexec/external_routine_eval_test.go | 36 +++++++++++++++++++ 3 files changed, 61 insertions(+) diff --git a/pkg/container/vector/functionTools.go b/pkg/container/vector/functionTools.go index c2026d938eed3..88af90eb0d337 100644 --- a/pkg/container/vector/functionTools.go +++ b/pkg/container/vector/functionTools.go @@ -533,6 +533,11 @@ type FunctionResultWrapper interface { SetResultVector(vec *Vector) GetResultVector() *Vector + // ResultLength is the number of logical values produced through the + // FunctionResult append contract. For fixed-width results the vector is + // pre-sized before execution, so Vector.Length alone describes capacity, + // not whether the producer actually populated every row. + ResultLength() int Free() PreExtendAndReset(size int) error } @@ -794,6 +799,16 @@ func (fr *FunctionResult[T]) GetResultVector() *Vector { return fr.vec } +func (fr *FunctionResult[T]) ResultLength() int { + if fr == nil || fr.vec == nil { + return 0 + } + if fr.isVarlena { + return fr.vec.Length() + } + return int(fr.length) +} + func (fr *FunctionResult[T]) ConvertToStrParameter() FunctionParameterWrapper[types.Varlena] { return GenerateFunctionStrParameter(fr.vec) } diff --git a/pkg/sql/colexec/external_routine_eval.go b/pkg/sql/colexec/external_routine_eval.go index dffe882326fb3..3750676b26c63 100644 --- a/pkg/sql/colexec/external_routine_eval.go +++ b/pkg/sql/colexec/external_routine_eval.go @@ -337,6 +337,16 @@ func (e *ExternalRoutineEval) eval(proc *process.Process, batches []*batch.Batch if err := proc.Base.UdfService.Execute(proc.Ctx, invocation, selectedResult, proc.Mp()); err != nil { return nil, err } + if selectedResult.ResultLength() != len(e.selectedRows) { + return nil, fmt.Errorf( + "python udf: runtime produced %d result rows, expected %d", + selectedResult.ResultLength(), + len(e.selectedRows), + ) + } + if selectedResult.GetResultVector() == nil { + return nil, fmt.Errorf("python udf: runtime returned no result vector") + } if len(e.selectedRows) == rowCount { return e.result.GetResultVector(), nil } diff --git a/pkg/sql/colexec/external_routine_eval_test.go b/pkg/sql/colexec/external_routine_eval_test.go index 42d36d1acbe72..ffeff3bc3b424 100644 --- a/pkg/sql/colexec/external_routine_eval_test.go +++ b/pkg/sql/colexec/external_routine_eval_test.go @@ -50,6 +50,14 @@ type externalRoutineCaptureRuntime struct { invocations []udf.Invocation } +type externalRoutineEmptySuccessRuntime struct{} + +func (r *externalRoutineEmptySuccessRuntime) Language() string { return udf.LanguagePython } + +func (r *externalRoutineEmptySuccessRuntime) Execute(_ context.Context, _ *udf.Invocation, _ vector.FunctionResultWrapper, _ *mpool.MPool) error { + return nil +} + func (r *externalRoutineCaptureRuntime) Language() string { return udf.LanguagePython } func (r *externalRoutineCaptureRuntime) Execute(_ context.Context, invocation *udf.Invocation, result vector.FunctionResultWrapper, mp *mpool.MPool) error { @@ -421,6 +429,34 @@ func TestExternalRoutineEvalRejectsSourceInExecutablePlan(t *testing.T) { require.ErrorContains(t, validateRoutineCall(call), "plan contains source") } +func TestExternalRoutineEvalRejectsSuccessfulRuntimeWithoutResultRows(t *testing.T) { + proc := testutil.NewProcess(t) + defer proc.Free() + proc.SetQueryId("missing-result-query") + proc.GetSessionInfo().TimeZone = time.UTC + proc.GetSessionInfo().User = "root" + proc.Base.StmtProfile = &process.StmtProfile{} + proc.GetStmtProfile().SetQueryStart(time.Date(2026, 9, 14, 0, 0, 0, 0, time.UTC)) + + input := vector.NewVec(types.T_int64.ToType()) + defer input.Free(proc.Mp()) + require.NoError(t, vector.AppendFixed(input, int64(10), false, proc.Mp())) + bat := batch.NewWithSize(0) + bat.SetRowCount(1) + proc.Base.UdfService = &externalRoutineEmptySuccessRuntime{} + evaluator, err := newExternalRoutineEval( + proc, + testExternalRoutineCall(t, python.ModeScalar, udf.NullCallHandler), + []ExpressionExecutor{&externalRoutineTestExecutor{vector: input}}, + nil, + ) + require.NoError(t, err) + defer evaluator.Free() + + _, err = evaluator.Eval(proc, []*batch.Batch{bat}, []bool{true}) + require.ErrorContains(t, err, "runtime produced 0 result rows, expected 1") +} + func TestExternalRoutineTransfersResultOwnership(t *testing.T) { for _, mode := range []string{"SCALAR", "VECTOR"} { t.Run(mode, func(t *testing.T) { From f9491043c6671374b0f5b133c2db6da130341232 Mon Sep 17 00:00:00 2001 From: iamlinjunhong <1030420200@qq.com> Date: Tue, 15 Sep 2026 11:01:11 +0800 Subject: [PATCH 110/202] fix(udf): reject boolean contract versions --- pkg/udf/python/worker/test_worker.py | 30 ++++++++++++++++++++++++++++ pkg/udf/python/worker/worker.py | 8 ++++++-- 2 files changed, 36 insertions(+), 2 deletions(-) diff --git a/pkg/udf/python/worker/test_worker.py b/pkg/udf/python/worker/test_worker.py index 9e0f18f44d217..0539d521bfc10 100644 --- a/pkg/udf/python/worker/test_worker.py +++ b/pkg/udf/python/worker/test_worker.py @@ -305,6 +305,36 @@ def test_typed_statement_context_rejects_timestamp_outside_int64(self): } ) + def test_typed_contract_versions_require_integer_wire_values(self): + typed_context = { + "contract_version": True, + "statement_timestamp_utc": 0, + "timezone_kind": "FIXED_OFFSET", + "timezone_offset_minutes": 0, + "sql_mode": [], + "current_user": "alice", + "connection_collation": "utf8mb4_bin", + } + with self.assertRaisesRegex(ValueError, "unsupported statement context contract"): + worker._typed_statement_context(typed_context) + + payload = { + "callsite_id": "python/1", + "may_error": True, + "security_mode": "INVOKER", + "leakproof": False, + "security_frame": { + "contract_version": True, + "mode": "INVOKER", + "invoker_user_id": 7, + "invoker_role_id": 8, + "effective_user_id": 7, + "effective_role_id": 8, + }, + } + with self.assertRaisesRegex(ValueError, "unsupported Python security frame"): + worker._validate_typed_call_contract(payload) + def test_typed_routine_semantics_reject_partial_or_definer_contract(self): payload = { "callsite_id": "python/1", diff --git a/pkg/udf/python/worker/worker.py b/pkg/udf/python/worker/worker.py index f09e2b9242399..eb076ce771dc6 100644 --- a/pkg/udf/python/worker/worker.py +++ b/pkg/udf/python/worker/worker.py @@ -546,7 +546,7 @@ def _typed_statement_context(raw: Any) -> Optional[Dict[str, Any]]: return None if not isinstance(raw, dict) or set(raw) - _TYPED_STATEMENT_CONTEXT_KEYS: raise ValueError("PROTOCOL: unsupported typed statement context field") - if raw.get("contract_version") != 1: + if type(raw.get("contract_version")) is not int or raw["contract_version"] != 1: raise ValueError("UNSUPPORTED_ROUTINE_VERSION: unsupported statement context contract") timestamp = raw.get("statement_timestamp_utc") if ( @@ -619,7 +619,11 @@ def _validate_typed_call_contract(payload: Dict[str, Any]) -> None: frame = payload.get("security_frame") if not isinstance(frame, dict) or set(frame) != _SECURITY_FRAME_KEYS: raise ValueError("UNSUPPORTED_ROUTINE_VERSION: incomplete Python security frame") - if frame.get("contract_version") != 1 or frame.get("mode") != "INVOKER": + if ( + type(frame.get("contract_version")) is not int + or frame["contract_version"] != 1 + or frame.get("mode") != "INVOKER" + ): raise ValueError("UNSUPPORTED_ROUTINE_VERSION: unsupported Python security frame") integer_fields = ("invoker_user_id", "invoker_role_id", "effective_user_id", "effective_role_id") if any( From 5d48263c80cd0a795750a3be551c314198f42bf2 Mon Sep 17 00:00:00 2001 From: iamlinjunhong <1030420200@qq.com> Date: Tue, 15 Sep 2026 11:01:57 +0800 Subject: [PATCH 111/202] fix(udf): require canonical timezone kinds --- pkg/udf/python/worker/test_worker.py | 25 +++++++++++++++++++++++++ pkg/udf/python/worker/worker.py | 3 +-- 2 files changed, 26 insertions(+), 2 deletions(-) diff --git a/pkg/udf/python/worker/test_worker.py b/pkg/udf/python/worker/test_worker.py index 0539d521bfc10..839e365ac744e 100644 --- a/pkg/udf/python/worker/test_worker.py +++ b/pkg/udf/python/worker/test_worker.py @@ -280,6 +280,31 @@ def test_typed_statement_context_accepts_unix_epoch(self): statement.statement_timestamp_utc, ) + def test_statement_timezone_kind_requires_canonical_spelling(self): + typed = { + "contract_version": 1, + "statement_timestamp_utc": 0, + "timezone_kind": "fixed_offset", + "timezone_offset_minutes": 0, + "sql_mode": [], + "current_user": "alice", + "connection_collation": "utf8mb4_bin", + } + with self.assertRaisesRegex(ValueError, "unsupported typed statement timezone"): + worker._typed_statement_context(typed) + + with self.assertRaisesRegex(ValueError, "unsupported timezone kind"): + worker._statement_context( + { + "statement_timestamp_utc": "0", + "session_timezone_kind": "fixed_offset", + "session_timezone_offset_minutes": "0", + "sql_mode": "[]", + "current_user": "alice", + "connection_collation": "utf8mb4_bin", + } + ) + def test_typed_statement_context_rejects_timestamp_outside_int64(self): typed = { "contract_version": 1, diff --git a/pkg/udf/python/worker/worker.py b/pkg/udf/python/worker/worker.py index eb076ce771dc6..89add9b9f8f4f 100644 --- a/pkg/udf/python/worker/worker.py +++ b/pkg/udf/python/worker/worker.py @@ -559,7 +559,6 @@ def _typed_statement_context(raw: Any) -> Optional[Dict[str, Any]]: timezone_kind = raw.get("timezone_kind") if not isinstance(timezone_kind, str): raise ValueError("PROTOCOL: typed statement timezone is missing") - timezone_kind = timezone_kind.upper() timezone_name = raw.get("timezone_name", "") tzdb_version = raw.get("timezone_database_version", "") offset = raw.get("timezone_offset_minutes", 0) @@ -678,7 +677,7 @@ def _statement_context(raw: Any) -> Optional[StatementContext]: except (TypeError, ValueError, OverflowError) as exc: raise ValueError("PROTOCOL: invalid statement_timestamp_utc") from exc - timezone_kind = _required_context_value(raw, "session_timezone_kind").upper() + timezone_kind = _required_context_value(raw, "session_timezone_kind") if timezone_kind == "IANA": timezone_name = _required_context_value(raw, "session_timezone_name") tzdb_version = _required_context_value(raw, "session_timezone_tzdb_version") From 8511e17a4a0e92a802f0441a32466e98e7723c1c Mon Sep 17 00:00:00 2001 From: iamlinjunhong <1030420200@qq.com> Date: Tue, 15 Sep 2026 11:05:44 +0800 Subject: [PATCH 112/202] fix(udf): validate definition schema type --- pkg/udf/python/worker/test_worker.py | 12 ++++++++++++ pkg/udf/python/worker/worker.py | 6 +++++- 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/pkg/udf/python/worker/test_worker.py b/pkg/udf/python/worker/test_worker.py index 839e365ac744e..786bd93e994e3 100644 --- a/pkg/udf/python/worker/test_worker.py +++ b/pkg/udf/python/worker/test_worker.py @@ -201,6 +201,18 @@ def test_definition_validation_rejects_async_handler_before_catalog_state(self): finally: server.shutdown() + def test_definition_validation_rejects_boolean_schema_version(self): + payload = complete_definition_validation_payload() + payload["definition_schema_version"] = True + # A malformed sender could otherwise recompute its own malformed + # fingerprint and pass the later integrity check. The version domain + # must be rejected before that comparison. + payload["definition_fingerprint"] = worker._definition_fingerprint(payload) + with self.assertRaisesRegex(ValueError, "unsupported Python definition schema"): + worker._decode_definition_validation( + json.dumps(payload, separators=(",", ":")).encode() + ) + def test_definition_validation_rejects_handler_rebound_after_definition(self): payload = complete_definition_validation_payload( "def f(ctx, value): return value\n" diff --git a/pkg/udf/python/worker/worker.py b/pkg/udf/python/worker/worker.py index 89add9b9f8f4f..a45244ec43a4d 100644 --- a/pkg/udf/python/worker/worker.py +++ b/pkg/udf/python/worker/worker.py @@ -897,7 +897,11 @@ def _decode_definition_validation(data: bytes) -> Dict[str, Any]: raise ValueError("UNSUPPORTED_ROUTINE_VERSION: unsupported Python ABI contract") if sdk_version != SDK_VERSION: raise ValueError("UNSUPPORTED_ROUTINE_VERSION: unsupported Python SDK") - if value.get("definition_schema_version") != DEFINITION_SCHEMA_VERSION: + definition_schema_version = value.get("definition_schema_version") + if ( + type(definition_schema_version) is not int + or definition_schema_version != DEFINITION_SCHEMA_VERSION + ): raise ValueError("UNSUPPORTED_ROUTINE_VERSION: unsupported Python definition schema") artifact_digest = _required_digest(value, "artifact_digest") environment_digest = _required_digest(value, "environment_digest") From c4853e1569c5370dfea48b8ada956f9a7e3c8f20 Mon Sep 17 00:00:00 2001 From: iamlinjunhong <1030420200@qq.com> Date: Tue, 15 Sep 2026 11:09:00 +0800 Subject: [PATCH 113/202] fix(udf): cancel exchanges during worker shutdown --- pkg/udf/python/worker/test_worker.py | 89 ++++++++++++++++++++++++++++ pkg/udf/python/worker/worker.py | 34 +++++++++-- 2 files changed, 117 insertions(+), 6 deletions(-) diff --git a/pkg/udf/python/worker/test_worker.py b/pkg/udf/python/worker/test_worker.py index 786bd93e994e3..f6eefc2583107 100644 --- a/pkg/udf/python/worker/test_worker.py +++ b/pkg/udf/python/worker/test_worker.py @@ -570,6 +570,95 @@ def test_worker_rejects_a_tuple_from_another_instance(self): with self.assertRaisesRegex(ValueError, "invalid worker lease epoch"): worker.RoutineFlightServer("grpc://127.0.0.1:0", lease_epoch=0) + @unittest.skipUnless(os.name == "posix", "Flight shutdown cancellation test") + def test_flight_shutdown_cancels_an_active_exchange(self): + entered = threading.Event() + original_reader = worker._ExchangeInputReader + + class NotifyingReader(original_reader): + def __init__(self, reader): + entered.set() + super().__init__(reader) + + server = worker.RoutineFlightServer("grpc://127.0.0.1:0") + client = flight.FlightClient(("127.0.0.1", server.port)) + server_thread = threading.Thread(target=server.serve, daemon=True) + server_thread.start() + writer = None + shutdown_error = [] + descriptor = {"type_id": worker.INT64, "offset_width": 32} + payload = complete_open_payload( + { + "function_ref": { + "account_id": 1, + "database_id": 2, + "function_id": 3, + "revision": 1, + "namespace_version": 1, + }, + "source": "def f(ctx, x): return x", + "handler": "f", + "mode": worker.MODE_SCALAR, + "null_policy": worker.NULL_CALL, + "abi_contract": worker.ABI_CONTRACT, + "adapter_version": worker.ADAPTER_VERSION, + "sdk_version": worker.SDK_VERSION, + "args": [descriptor], + "return": descriptor, + "max_batch_bytes": 1 << 20, + "max_batch_rows": 8, + "handler_timeout_seconds": 60, + } + ) + fence = { + "account_id": 1, + "statement_id": "shutdown", + "group_id": "shutdown-group", + "group_epoch": 1, + "invocation_id": "shutdown-invocation", + "lease_epoch": 1, + } + + def control(kind, **fields): + return worker._encode_control({"kind": kind, "tuple": fence, **fields}) + + try: + with mock.patch.object(worker, "_ExchangeInputReader", NotifyingReader): + writer, reader = client.do_exchange( + flight.FlightDescriptor.for_command( + control("OpenInvocation", payload=payload) + ), + options=flight.FlightCallOptions(timeout=5), + ) + self.assertTrue(entered.wait(2), "exchange did not reach its input reader") + + def shutdown(): + try: + server.shutdown() + except Exception as exc: + shutdown_error.append(exc) + + shutdown_thread = threading.Thread(target=shutdown) + shutdown_thread.start() + shutdown_thread.join(5) + self.assertFalse( + shutdown_thread.is_alive(), + "server shutdown waited for an active exchange", + ) + self.assertEqual([], shutdown_error) + self.assertFalse(server._active) + with server._pending_cleanup_condition: + self.assertFalse(server._pending_cleanups) + finally: + if writer is not None: + try: + writer.close() + except Exception: + pass + client.close() + if server_thread.is_alive(): + server_thread.join(2) + def test_inline_contract_rejects_external_handler_import(self): with self.assertRaisesRegex(ValueError, "immutable artifact catalog"): worker._load_handler("def add(ctx, value): return value", "module:add") diff --git a/pkg/udf/python/worker/worker.py b/pkg/udf/python/worker/worker.py index a45244ec43a4d..9e6665be88d37 100644 --- a/pkg/udf/python/worker/worker.py +++ b/pkg/udf/python/worker/worker.py @@ -2119,6 +2119,19 @@ def _context_is_cancelled(context) -> bool: return False +class _ExchangeContext: + """Combine the Flight cancellation state with server shutdown intent.""" + + __slots__ = ("_context", "_shutdown") + + def __init__(self, context, shutdown: threading.Event): + self._context = context + self._shutdown = shutdown + + def is_cancelled(self) -> bool: + return self._shutdown.is_set() or _context_is_cancelled(self._context) + + class _ExchangeInputReader: """Make a blocking Flight input read observable to cancellation. @@ -2949,6 +2962,11 @@ def __init__( self._pending_cleanups: Dict[tuple, _PendingInvocationCleanup] = {} self._pending_cleanup_thread: Optional[threading.Thread] = None self._pending_cleanup_stopping = False + # FlightServerBase.shutdown waits for active RPC methods to return. + # Set this before entering that wait so input readers, handler loops, + # and ACK waits have an independent cancellation source during a + # graceful worker shutdown. + self._shutdown_event = threading.Event() @staticmethod def _entry_bytes(key: tuple) -> int: @@ -3215,6 +3233,7 @@ def _pending_cleanup_loop(self) -> None: ) def shutdown(self): + self._shutdown_event.set() result = super().shutdown() with self._pending_cleanup_condition: self._pending_cleanup_stopping = True @@ -3375,12 +3394,15 @@ def _ack_control(control: Dict[str, Any]) -> Dict[str, Any]: return value def do_exchange(self, context, descriptor, reader, writer): + exchange_context = _ExchangeContext(context, self._shutdown_event) state = None key = None handler_session = None input_reader = None writer_started = False try: + if exchange_context.is_cancelled(): + raise TimeoutError("DEADLINE_EXCEEDED: Flight server is shutting down") command = getattr(descriptor, "command", None) if command is None: raise ValueError("PROTOCOL: exchange is missing the invocation descriptor") @@ -3475,14 +3497,14 @@ def do_exchange(self, context, descriptor, reader, writer): input_reader = _ExchangeInputReader(reader) while True: try: - chunk = input_reader.next(context) + chunk = input_reader.next(exchange_context) except StopIteration: break # The reader can publish a chunk at the same instant that the # RPC is cancelled. Re-check after the blocking handoff so a # chunk already dequeued after cancellation cannot start user # code or consume another handler slot. - if _context_is_cancelled(context): + if exchange_context.is_cancelled(): raise TimeoutError("DEADLINE_EXCEEDED: input stream cancelled") if chunk is None or (chunk.data is None and not chunk.app_metadata): raise ValueError("PROTOCOL: empty input frame") @@ -3543,7 +3565,7 @@ def do_exchange(self, context, descriptor, reader, writer): "input": _serialize_record_batch_message(batch), } output_wire = handler_session.run( - context, execution_request, handler_timeout_seconds + exchange_context, execution_request, handler_timeout_seconds ) output_batch = _deserialize_record_batch_message( output_wire, result_schema @@ -3575,7 +3597,7 @@ def do_exchange(self, context, descriptor, reader, writer): "released_batches": 1, })) writer.write_with_metadata(output_batch, _encode_control({"kind": "ResultBatch", "tuple": open_control["tuple"], "sequence": sequence})) - state.wait_result_ack(sequence, context) + state.wait_result_ack(sequence, exchange_context) if handler_session.should_rollover: handler_session.close() handler_session = None @@ -3607,9 +3629,9 @@ def do_exchange(self, context, descriptor, reader, writer): "last_sequence": last_input, "last_result_sequence": last_result, })) - state.wait_finish_ack(context) + state.wait_finish_ack(exchange_context) except Exception as exc: - if state is not None and _is_cancellation_error(exc, context): + if state is not None and _is_cancellation_error(exc, exchange_context): state.mark_cancelled() if writer_started and key is not None: try: From c9afd5a0148d2d798e41bd4ceceecb55dc81effc Mon Sep 17 00:00:00 2001 From: iamlinjunhong <1030420200@qq.com> Date: Tue, 15 Sep 2026 11:14:38 +0800 Subject: [PATCH 114/202] fix(udf): require canonical routine language --- pkg/sql/colexec/external_routine_eval.go | 2 +- pkg/sql/colexec/external_routine_eval_test.go | 6 ++++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/pkg/sql/colexec/external_routine_eval.go b/pkg/sql/colexec/external_routine_eval.go index 3750676b26c63..f5274804d779a 100644 --- a/pkg/sql/colexec/external_routine_eval.go +++ b/pkg/sql/colexec/external_routine_eval.go @@ -95,7 +95,7 @@ func validateRoutineCall(call *planpb.RoutineCall) error { if call.ReturnType.Id == int32(types.T_any) { return fmt.Errorf("UNSUPPORTED_ROUTINE_VERSION: typed routine call has no return descriptor") } - if call.ContractVersion != udf.PythonPlanContractVersion || !strings.EqualFold(call.Language, udf.LanguagePython) { + if call.ContractVersion != udf.PythonPlanContractVersion || call.Language != udf.LanguagePython { return fmt.Errorf("UNSUPPORTED_ROUTINE_VERSION: unsupported typed routine call contract") } if call.Volatility != "VOLATILE" { diff --git a/pkg/sql/colexec/external_routine_eval_test.go b/pkg/sql/colexec/external_routine_eval_test.go index ffeff3bc3b424..493d6e3b3e136 100644 --- a/pkg/sql/colexec/external_routine_eval_test.go +++ b/pkg/sql/colexec/external_routine_eval_test.go @@ -429,6 +429,12 @@ func TestExternalRoutineEvalRejectsSourceInExecutablePlan(t *testing.T) { require.ErrorContains(t, validateRoutineCall(call), "plan contains source") } +func TestExternalRoutineEvalRequiresCanonicalLanguage(t *testing.T) { + call := testExternalRoutineCall(t, python.ModeScalar, udf.NullCallHandler) + call.Language = "PYTHON" + require.ErrorContains(t, validateRoutineCall(call), "unsupported typed routine call contract") +} + func TestExternalRoutineEvalRejectsSuccessfulRuntimeWithoutResultRows(t *testing.T) { proc := testutil.NewProcess(t) defer proc.Free() From ecc344246e4d42d461af5a433b59cea984015448 Mon Sep 17 00:00:00 2001 From: iamlinjunhong <1030420200@qq.com> Date: Tue, 15 Sep 2026 11:18:05 +0800 Subject: [PATCH 115/202] fix(udf): reject noncanonical plan languages --- pkg/frontend/routine_plan_validation.go | 4 ++-- pkg/frontend/routine_plan_validation_test.go | 13 +++++++++++++ 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/pkg/frontend/routine_plan_validation.go b/pkg/frontend/routine_plan_validation.go index 27101cefe3594..717f6cbc4b9fe 100644 --- a/pkg/frontend/routine_plan_validation.go +++ b/pkg/frontend/routine_plan_validation.go @@ -456,7 +456,7 @@ func validateRoutinePlanDependencyShape(dependency *planpb.RoutinePlanDependency if dependency.Volatility == "" || dependency.NullPolicy == "" || !dependency.MayError || dependency.Leakproof { return fmt.Errorf("UNSUPPORTED_ROUTINE_VERSION: routine plan dependency semantic contract is incomplete") } - switch strings.ToLower(dependency.Language) { + switch dependency.Language { case udf.LanguagePython: if dependency.SecurityMode != "INVOKER" { return fmt.Errorf("UNSUPPORTED_ROUTINE_VERSION: Python routine plan dependency must use INVOKER security") @@ -506,7 +506,7 @@ func routinePlanDependenciesChanged( (dependency.Language == udf.LanguagePython && expectedSecurityMode != "INVOKER") || (dependency.Language == udf.LanguageSQL && expectedSecurityMode == "") || dependency.MayError != true || dependency.Leakproof || - !strings.EqualFold(state.language, dependency.Language) || + state.language != dependency.Language || state.fingerprint != dependency.DefinitionFingerprint || state.artifactDigest != dependency.ArtifactDigest || state.environmentDigest != dependency.EnvironmentDigest { diff --git a/pkg/frontend/routine_plan_validation_test.go b/pkg/frontend/routine_plan_validation_test.go index ab710f975f07b..e4e3e4441d857 100644 --- a/pkg/frontend/routine_plan_validation_test.go +++ b/pkg/frontend/routine_plan_validation_test.go @@ -152,6 +152,9 @@ func TestValidateRoutinePlanDependencyShapeRejectsCrossAccountAndUnknownContract {name: "missing identity", mutate: func(d *planpb.RoutinePlanDependency) { d.FunctionRef = nil }}, + {name: "noncanonical language", mutate: func(d *planpb.RoutinePlanDependency) { + d.Language = "PYTHON" + }}, } for _, tc := range cases { t.Run(tc.name, func(t *testing.T) { @@ -164,6 +167,16 @@ func TestValidateRoutinePlanDependencyShapeRejectsCrossAccountAndUnknownContract } } +func TestRoutinePlanDependenciesChangedRequiresCanonicalLanguage(t *testing.T) { + dependency := testRoutinePlanDependency() + dependency.Language = "PYTHON" + + require.True(t, routinePlanDependenciesChanged( + []*planpb.RoutinePlanDependency{dependency}, + map[uint64]routinePlanCatalogState{41: testRoutinePlanState()}, + )) +} + func TestRoutinePlanDependenciesIncludesBackgroundQueries(t *testing.T) { dependency := testRoutinePlanDependency() plan := &planpb.Plan{ From a4bff523b5cb7258261d6fbf64294b24513a2962 Mon Sep 17 00:00:00 2001 From: iamlinjunhong <1030420200@qq.com> Date: Tue, 15 Sep 2026 11:19:37 +0800 Subject: [PATCH 116/202] test(udf): inspect scoped gateway group fences --- pkg/udf/python/gateway_integration_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/udf/python/gateway_integration_test.go b/pkg/udf/python/gateway_integration_test.go index f50913691492b..0d26ddb95ca88 100644 --- a/pkg/udf/python/gateway_integration_test.go +++ b/pkg/udf/python/gateway_integration_test.go @@ -494,7 +494,7 @@ def add(ctx, value): gateway.admissionMu.Lock() require.Empty(t, gateway.admittedGroups) require.Empty(t, gateway.active) - closedEpoch := gateway.closedGroups[invocation.Tuple.GroupID] + closedEpoch := gateway.closedGroups[scopedGroupKey(invocation.Tuple.AccountID, invocation.Tuple.GroupID)] gateway.admissionMu.Unlock() require.Equal(t, uint64(1), closedEpoch) entries, _ := gateway.ledger.Counts() From 72b82c939f633c2131c26166c3e442106927c358 Mon Sep 17 00:00:00 2001 From: iamlinjunhong <1030420200@qq.com> Date: Tue, 15 Sep 2026 11:27:35 +0800 Subject: [PATCH 117/202] fix(udf): align gateway timeout with worker limit --- pkg/udf/python/config.go | 8 +++++++- pkg/udf/python/config_test.go | 11 +++++++++++ 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/pkg/udf/python/config.go b/pkg/udf/python/config.go index a5de9d35dcefa..d1e1372d457d8 100644 --- a/pkg/udf/python/config.go +++ b/pkg/udf/python/config.go @@ -15,6 +15,12 @@ import ( "github.com/matrixorigin/matrixone/pkg/common/moerr" ) +// maxHandlerTimeout is part of the current Gateway/worker payload contract. +// The worker rejects a handler timeout above one hour; reject it while loading +// the CN configuration so an enabled runtime cannot appear healthy and then +// fail every invocation at the Flight boundary. +const maxHandlerTimeout = time.Hour + type Config struct { UUID string `toml:"uuid"` Address string `toml:"address"` @@ -89,7 +95,7 @@ func (c *ClientConfig) Validate() error { if c.MaxActiveInvocations < 0 || c.MaxActiveInvocations > 1<<20 { return moerr.NewInternalError(context.Background(), "invalid python udf max active invocations") } - if c.RequestTimeout < 0 { + if c.RequestTimeout < 0 || c.RequestTimeout > maxHandlerTimeout { return moerr.NewInternalError(context.Background(), "invalid python udf request timeout") } if c.MaxTerminalEntries < 0 || c.MaxTerminalEntries > 1<<30 { diff --git a/pkg/udf/python/config_test.go b/pkg/udf/python/config_test.go index 29034df2000e8..88be8ee0ca663 100644 --- a/pkg/udf/python/config_test.go +++ b/pkg/udf/python/config_test.go @@ -48,3 +48,14 @@ func TestClientConfigRejectsInvalidInvocationBudgets(t *testing.T) { config.MaxInvocationResultBytes = -1 require.ErrorContains(t, config.Validate(), "max invocation result bytes") } + +func TestClientConfigMatchesWorkerHandlerTimeoutContract(t *testing.T) { + config := &ClientConfig{ + Enabled: true, AllowUnisolated: true, ServerAddress: "127.0.0.1:50051", + RequestTimeout: maxHandlerTimeout, + } + require.NoError(t, config.Validate()) + + config.RequestTimeout = maxHandlerTimeout + time.Nanosecond + require.ErrorContains(t, config.Validate(), "request timeout") +} From 37519fe5657b221b858c68cff74a261d16d10c4e Mon Sep 17 00:00:00 2001 From: iamlinjunhong <1030420200@qq.com> Date: Tue, 15 Sep 2026 11:28:53 +0800 Subject: [PATCH 118/202] fix(udf): preserve large JSON number tokens --- pkg/udf/python/worker/test_worker.py | 3 ++- pkg/udf/python/worker/worker.py | 13 ++++++------- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/pkg/udf/python/worker/test_worker.py b/pkg/udf/python/worker/test_worker.py index f6eefc2583107..f0082c189185f 100644 --- a/pkg/udf/python/worker/test_worker.py +++ b/pkg/udf/python/worker/test_worker.py @@ -1007,7 +1007,8 @@ def test_json_canonical_text_preserves_number_and_string_tokens(self): def test_json_rejects_nonstandard_numbers(self): descriptor = {"type_id": worker.JSON, "offset_width": 32, "json_encoding": "canonical_text"} - for value in ("NaN", "Infinity", "-Infinity", "1e9999"): + self.assertEqual("1e9999", worker._canonical_json_text("1e9999")) + for value in ("NaN", "Infinity", "-Infinity"): with self.assertRaisesRegex(ValueError, "invalid JSON"): worker._canonical_json_text(value) with self.assertRaisesRegex(ValueError, "JSON result"): diff --git a/pkg/udf/python/worker/worker.py b/pkg/udf/python/worker/worker.py index 9e6665be88d37..4acd357286cf8 100644 --- a/pkg/udf/python/worker/worker.py +++ b/pkg/udf/python/worker/worker.py @@ -1458,17 +1458,16 @@ def _canonical_json_text(value: str) -> str: def reject_nonstandard_number(_constant: str): raise ValueError("non-standard JSON number") - def reject_nonfinite_float(number: str): - parsed = float(number) - if not math.isfinite(parsed): - raise ValueError("non-finite JSON number") - return parsed - try: json.loads( value, parse_constant=reject_nonstandard_number, - parse_float=reject_nonfinite_float, + # JSON values cross the ABI as compact text. Parsing numbers as + # strings keeps validation independent of Python's binary-float + # range; Go's json.Compact accepts valid tokens such as 1e999 and + # the handler must observe that exact token unchanged. + parse_int=str, + parse_float=str, ) except Exception as exc: raise ValueError("TYPE_CONTRACT: invalid JSON input") from exc From 0e8f00798c2c157a89313163c24365e339841aab Mon Sep 17 00:00:00 2001 From: iamlinjunhong <1030420200@qq.com> Date: Tue, 15 Sep 2026 11:30:46 +0800 Subject: [PATCH 119/202] fix(udf): reject noncanonical routine language at bind --- pkg/sql/plan/function/udf.go | 6 +++--- pkg/sql/plan/function/udf_test.go | 6 ++++++ 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/pkg/sql/plan/function/udf.go b/pkg/sql/plan/function/udf.go index f8702f89245a6..4816812125009 100644 --- a/pkg/sql/plan/function/udf.go +++ b/pkg/sql/plan/function/udf.go @@ -304,10 +304,10 @@ func (u *Udf) GetRoutineCall() (*plan.RoutineCall, error) { if u == nil { return nil, fmt.Errorf("routine call requires a non-nil routine") } - if strings.EqualFold(u.Language, udf.LanguageSQL) { + if u.Language == udf.LanguageSQL { return u.getSQLRoutineCall() } - if !strings.EqualFold(u.Language, udf.LanguagePython) { + if u.Language != udf.LanguagePython { return nil, fmt.Errorf("routine call has unsupported language %q", u.Language) } if u.FunctionID <= 0 || u.DatabaseID == 0 || u.Revision == 0 || u.NamespaceVersion == 0 { @@ -343,7 +343,7 @@ func (u *Udf) GetRoutineCall() (*plan.RoutineCall, error) { AccountId: u.AccountID, DatabaseId: u.DatabaseID, }, - Language: strings.ToLower(u.Language), + Language: udf.LanguagePython, ArgumentTypes: arguments, ReturnType: *returnType, Volatility: "VOLATILE", diff --git a/pkg/sql/plan/function/udf_test.go b/pkg/sql/plan/function/udf_test.go index 36d90a1b3057c..93ada8b5ebd10 100644 --- a/pkg/sql/plan/function/udf_test.go +++ b/pkg/sql/plan/function/udf_test.go @@ -338,6 +338,12 @@ func TestPythonRoutineCallCarriesExactCatalogContract(t *testing.T) { require.NotEmpty(t, call.GetPython().DefinitionFingerprint) } +func TestPythonRoutineCallRejectsNoncanonicalLanguage(t *testing.T) { + routine := &Udf{Language: "PYTHON"} + _, err := routine.GetRoutineCall() + require.ErrorContains(t, err, `routine call has unsupported language "PYTHON"`) +} + func TestSQLRoutineFingerprintAndCallUseSharedRevisionContract(t *testing.T) { fingerprint, err := SQLRoutineFingerprint("select value + 1", "[\"bigint\"]", "bigint") require.NoError(t, err) From 41cbc0ae0f2310236bc14b83d2edac906f9e7af4 Mon Sep 17 00:00:00 2001 From: iamlinjunhong <1030420200@qq.com> Date: Tue, 15 Sep 2026 11:32:07 +0800 Subject: [PATCH 120/202] fix(udf): enforce Arrow field nullability --- pkg/udf/python/worker/test_worker.py | 6 ++++++ pkg/udf/python/worker/worker.py | 4 ++++ 2 files changed, 10 insertions(+) diff --git a/pkg/udf/python/worker/test_worker.py b/pkg/udf/python/worker/test_worker.py index f0082c189185f..d9d2772caf2ba 100644 --- a/pkg/udf/python/worker/test_worker.py +++ b/pkg/udf/python/worker/test_worker.py @@ -813,6 +813,12 @@ def test_metadata_is_owned_by_adapter(self): worker._validate_field(with_extra, "result", descriptor) with self.assertRaisesRegex(ValueError, "field name"): worker._validate_field(pa.field("other", field.type, metadata=field.metadata), "result", descriptor) + with self.assertRaisesRegex(ValueError, "nullable"): + worker._validate_field( + pa.field("result", field.type, nullable=False, metadata=field.metadata), + "result", + descriptor, + ) def test_type_descriptor_shape_is_strict(self): with self.assertRaisesRegex(ValueError, "unsupported descriptor field"): diff --git a/pkg/udf/python/worker/worker.py b/pkg/udf/python/worker/worker.py index 4acd357286cf8..21acd5c277270 100644 --- a/pkg/udf/python/worker/worker.py +++ b/pkg/udf/python/worker/worker.py @@ -1353,6 +1353,10 @@ def _validate_field(field: pa.Field, name: str, descriptor: Dict[str, Any]) -> N raise ValueError(f"TYPE_CONTRACT: Arrow field name {field.name!r} does not match {name!r}") if field.type != expected.type: raise ValueError(f"TYPE_CONTRACT: Arrow type {field.type} does not match {expected.type}") + if field.nullable != expected.nullable: + raise ValueError( + f"TYPE_CONTRACT: Arrow field nullable={field.nullable} does not match {expected.nullable}" + ) if field.metadata != expected.metadata: raise ValueError("TYPE_CONTRACT: Arrow logical metadata does not match the frozen descriptor") From 865807c06caeb17740cb9cf4d61a1b761c34a907 Mon Sep 17 00:00:00 2001 From: iamlinjunhong <1030420200@qq.com> Date: Tue, 15 Sep 2026 11:35:52 +0800 Subject: [PATCH 121/202] fix(udf): require object invocation payload --- pkg/udf/protocol/protocol.go | 6 ++++++ pkg/udf/protocol/protocol_test.go | 13 +++++++++++++ pkg/udf/python/worker/test_worker.py | 21 +++++++++++++++++++++ pkg/udf/python/worker/worker.py | 9 +++++---- 4 files changed, 45 insertions(+), 4 deletions(-) diff --git a/pkg/udf/protocol/protocol.go b/pkg/udf/protocol/protocol.go index f06920c8461d8..1d9b4113d13f2 100644 --- a/pkg/udf/protocol/protocol.go +++ b/pkg/udf/protocol/protocol.go @@ -201,6 +201,12 @@ func validateControlFields(control Control, wire map[string]json.RawMessage) err if len(control.Payload) == 0 && wire == nil { return fmt.Errorf("%w: control kind %q is missing field %q", ErrProtocol, control.Kind, "payload") } + if len(control.Payload) != 0 { + var payload map[string]json.RawMessage + if err := json.Unmarshal(control.Payload, &payload); err != nil || payload == nil { + return fmt.Errorf("%w: control kind %q payload must be a JSON object", ErrProtocol, control.Kind) + } + } case "InputBatch", "ResultBatch": if control.Sequence == 0 { return fmt.Errorf("%w: control kind %q requires a positive sequence", ErrProtocol, control.Kind) diff --git a/pkg/udf/protocol/protocol_test.go b/pkg/udf/protocol/protocol_test.go index 1eeee14913a0f..f03e04e608556 100644 --- a/pkg/udf/protocol/protocol_test.go +++ b/pkg/udf/protocol/protocol_test.go @@ -70,6 +70,19 @@ func TestControlRejectsDuplicateJSONFields(t *testing.T) { require.ErrorContains(t, err, "duplicate control JSON field") } +func TestOpenInvocationPayloadMustBeObject(t *testing.T) { + for _, payload := range []string{"null", "[]", `"text"`} { + wire := []byte(`{"version":1,"kind":"OpenInvocation","tuple":{"account_id":1,"statement_id":"statement","group_id":"group","group_epoch":2,"invocation_id":"invocation","lease_epoch":3},"payload":` + payload + `}`) + _, err := UnmarshalControl(wire) + require.ErrorIs(t, err, ErrProtocol) + require.ErrorContains(t, err, "payload must be a JSON object") + + _, err = MarshalControl(Control{Kind: "OpenInvocation", Tuple: testTuple(), Payload: json.RawMessage(payload)}) + require.ErrorIs(t, err, ErrProtocol) + require.ErrorContains(t, err, "payload must be a JSON object") + } +} + func TestControlFieldsBelongToTheirKind(t *testing.T) { wire, err := MarshalControl(Control{Kind: "InputBatch", Tuple: testTuple(), Sequence: 1}) require.NoError(t, err) diff --git a/pkg/udf/python/worker/test_worker.py b/pkg/udf/python/worker/test_worker.py index d9d2772caf2ba..870ce18b480f8 100644 --- a/pkg/udf/python/worker/test_worker.py +++ b/pkg/udf/python/worker/test_worker.py @@ -3657,6 +3657,27 @@ def test_control_fields_belong_to_their_kind(self): ).encode() ) + def test_open_invocation_payload_must_be_object(self): + tuple_value = { + "account_id": 1, + "statement_id": "statement", + "group_id": "group", + "group_epoch": 1, + "invocation_id": "invocation", + "lease_epoch": 1, + } + for payload in (None, [], "text"): + value = { + "version": worker.PROTOCOL_VERSION, + "kind": "OpenInvocation", + "tuple": tuple_value, + "payload": payload, + } + with self.assertRaisesRegex(ValueError, "payload must be an object"): + worker._decode_control(json.dumps(value).encode()) + with self.assertRaisesRegex(ValueError, "payload must be an object"): + worker._encode_control(value) + def test_control_encoder_rejects_nonstandard_json_numbers(self): tuple_value = { "account_id": 1, diff --git a/pkg/udf/python/worker/worker.py b/pkg/udf/python/worker/worker.py index 21acd5c277270..bebf9082fcefb 100644 --- a/pkg/udf/python/worker/worker.py +++ b/pkg/udf/python/worker/worker.py @@ -1126,10 +1126,11 @@ def _validate_control_fields(value: Dict[str, Any], wire: bool) -> None: if kind == "Ack": if ("ack_sequence" in value) == ("finish_id" in value): raise ValueError("PROTOCOL: Ack requires exactly one acknowledgement identity") - elif kind == "OpenInvocation" and ( - "payload" not in value or (wire and value["payload"] is None) - ): - raise ValueError("PROTOCOL: control kind 'OpenInvocation' is missing field 'payload'") + elif kind == "OpenInvocation": + if "payload" not in value: + raise ValueError("PROTOCOL: control kind 'OpenInvocation' is missing field 'payload'") + if not isinstance(value["payload"], dict): + raise ValueError("PROTOCOL: control kind 'OpenInvocation' payload must be an object") def _encode_control(value: Dict[str, Any]) -> bytes: From caae2b4554ce04369ca5dcb65369cabc01aba95d Mon Sep 17 00:00:00 2001 From: iamlinjunhong <1030420200@qq.com> Date: Tue, 15 Sep 2026 11:37:16 +0800 Subject: [PATCH 122/202] fix(udf): reject cancelled worker actions --- pkg/udf/python/worker/test_worker.py | 28 ++++++++++++++++++++++++++++ pkg/udf/python/worker/worker.py | 12 ++++++++++++ 2 files changed, 40 insertions(+) diff --git a/pkg/udf/python/worker/test_worker.py b/pkg/udf/python/worker/test_worker.py index 870ce18b480f8..f850a1b8f4c7f 100644 --- a/pkg/udf/python/worker/test_worker.py +++ b/pkg/udf/python/worker/test_worker.py @@ -3572,6 +3572,34 @@ def test_cancelled_terminal_is_frozen_and_rejects_late_ack(self): next(server.do_action(None, action)) self.assertEqual(worker._TERMINAL_CANCELLED, server._terminal[key].outcome) + def test_cancelled_or_shutdown_action_cannot_ack_active_invocation(self): + server = worker.RoutineFlightServer("grpc://127.0.0.1:0") + key = (1, "statement", "action-cancel", 1, "active", 1) + state = server._admit(key) + state.mark_finish_sent("finish-active") + tuple_value = { + "account_id": key[0], "statement_id": key[1], "group_id": key[2], + "group_epoch": key[3], "invocation_id": key[4], "lease_epoch": key[5], + } + action = types.SimpleNamespace( + type="AcknowledgeFinish", + body=worker._encode_control({ + "kind": "AcknowledgeFinish", "tuple": tuple_value, + "finish_id": "finish-active", + }), + ) + cancelled_context = types.SimpleNamespace(is_cancelled=lambda: True) + with self.assertRaisesRegex(TimeoutError, "DEADLINE_EXCEEDED"): + next(server.do_action(cancelled_context, action)) + self.assertFalse(state.finish_acked) + + server._shutdown_event.set() + with self.assertRaisesRegex(TimeoutError, "DEADLINE_EXCEEDED"): + next(server.do_action(None, action)) + self.assertFalse(state.finish_acked) + state.mark_cancelled() + server._finish_invocation(key, state) + def test_active_fence_cannot_be_admitted_twice(self): server = worker.RoutineFlightServer("grpc://127.0.0.1:0") key = (1, "statement", "group", 1, "invocation", 1) diff --git a/pkg/udf/python/worker/worker.py b/pkg/udf/python/worker/worker.py index bebf9082fcefb..ac3d5d8eae6ab 100644 --- a/pkg/udf/python/worker/worker.py +++ b/pkg/udf/python/worker/worker.py @@ -3328,6 +3328,7 @@ def close_with_retry(resource) -> tuple[bool, Optional[Exception]]: return cleanup_error def do_action(self, context, action): + self._ensure_action_active(context) if action.type == "GetPythonCapabilities": request = _decode_capability_request(action.body) # pyarrow exposes Result as a one positional-buffer value across @@ -3363,6 +3364,13 @@ def do_action(self, context, action): self._purge_terminal_locked(self._clock()) state = self._active.get(key) terminal = self._terminal.get(key) + # A cancelled action must not advance an active invocation. This is + # checked again after decoding and ledger lookup because an action can + # be queued while its Flight context or the worker is being closed. + # If cancellation races this check, the state mutation is the action + # acceptance point; the caller may then safely retry the idempotent + # acknowledgement when its parent context is still alive. + self._ensure_action_active(context) if state is None: if terminal is None: raise ValueError("PROTOCOL: unknown invocation") @@ -3388,6 +3396,10 @@ def do_action(self, context, action): state.ack_finish(_required_string(control, "finish_id")) yield _encode_control(self._ack_control(control)) + def _ensure_action_active(self, context) -> None: + if self._shutdown_event.is_set() or _context_is_cancelled(context): + raise TimeoutError("DEADLINE_EXCEEDED: Flight action is cancelled") + @staticmethod def _ack_control(control: Dict[str, Any]) -> Dict[str, Any]: value = {"kind": "Ack", "tuple": control["tuple"], "status": "OK"} From 550a5ce65eba7cf783b37bc764beffbdd266cdbb Mon Sep 17 00:00:00 2001 From: iamlinjunhong <1030420200@qq.com> Date: Tue, 15 Sep 2026 11:41:23 +0800 Subject: [PATCH 123/202] fix(udf): reject noncanonical catalog languages --- pkg/frontend/compiler_context.go | 10 +++++----- pkg/frontend/routine_plan_validation.go | 8 ++++---- pkg/frontend/routine_plan_validation_test.go | 6 ++++++ pkg/frontend/snapshot_catalog_restore.go | 8 ++++---- pkg/sql/plan/function/udf.go | 19 ++++++++++++++----- pkg/sql/plan/function/udf_test.go | 7 +++++++ pkg/sql/plan/routine_dependency.go | 9 ++++++++- pkg/sql/plan/routine_dependency_test.go | 8 ++++++++ 8 files changed, 56 insertions(+), 19 deletions(-) diff --git a/pkg/frontend/compiler_context.go b/pkg/frontend/compiler_context.go index 9e42516d8a60d..b6241c2b62217 100644 --- a/pkg/frontend/compiler_context.go +++ b/pkg/frontend/compiler_context.go @@ -908,7 +908,7 @@ func (tcc *TxnCompilerContext) ResolveUdf(name string, args []*plan.Expr) (udf * } udf.SQLMode = &mode udf.FunctionID = functionID - if strings.EqualFold(udf.Language, string(tree.PYTHON)) { + if udf.Language == string(tree.PYTHON) { revision, revisionErr := readPythonRevision(queryCtx, bh, functionID, tcc.GetSnapshot()) if revisionErr != nil { return nil, revisionErr @@ -919,7 +919,7 @@ func (tcc *TxnCompilerContext) ResolveUdf(name string, args []*plan.Expr) (udf * udf.RetType = revision.RetType udf.Revision = revision.Revision udf.NamespaceVersion = revision.NamespaceVersion - } else if strings.EqualFold(udf.Language, string(tree.SQL)) { + } else if udf.Language == string(tree.SQL) { revision, found, revisionErr := readSQLRevision(queryCtx, bh, functionID, tcc.GetSnapshot()) if revisionErr != nil { return nil, revisionErr @@ -959,7 +959,7 @@ func (tcc *TxnCompilerContext) ResolveUdf(name string, args []*plan.Expr) (udf * toList := make([]types.T, len(args)) pythonTargetTypes := make([]types.Type, 0) - if strings.EqualFold(udf.Language, string(tree.PYTHON)) { + if udf.Language == string(tree.PYTHON) { pythonTargetTypes = udf.GetArgsType() } for j := range argList { @@ -1260,7 +1260,7 @@ func readSQLRevision( return sqlRevisionCatalogRow{}, false, err } if revisionValue <= 0 || uint64(revisionValue) != uint64(activeRevision) || - !strings.EqualFold(language, string(tree.SQL)) || + language != string(tree.SQL) || definitionSchema != udf.SQLDefinitionSchemaVersion || volatility != "VOLATILE" || nullPolicy != udf.NullCallHandler || !strings.EqualFold(securityType, "DEFINER") || @@ -1393,7 +1393,7 @@ func readPythonRevision(ctx context.Context, bh BackgroundExec, functionID int64 if err != nil { return pythonRevisionCatalogRow{}, err } - if !strings.EqualFold(language, string(tree.PYTHON)) || definitionSchema != udf.PythonDefinitionSchemaVersion || abi != udf.PythonABIContract || adapter != udf.PythonAdapterVersion || sdk != udf.PythonSDKVersion || volatility != "VOLATILE" || !strings.EqualFold(securityType, "INVOKER") || !strings.EqualFold(baseSecurityType, securityType) { + if language != string(tree.PYTHON) || definitionSchema != udf.PythonDefinitionSchemaVersion || abi != udf.PythonABIContract || adapter != udf.PythonAdapterVersion || sdk != udf.PythonSDKVersion || volatility != "VOLATILE" || !strings.EqualFold(securityType, "INVOKER") || !strings.EqualFold(baseSecurityType, securityType) { return pythonRevisionCatalogRow{}, fmt.Errorf("UNSUPPORTED_ROUTINE_VERSION: Python function %d revision contract is not supported", functionID) } bodyDefinition, err := function.DecodePythonRoutineBody(body) diff --git a/pkg/frontend/routine_plan_validation.go b/pkg/frontend/routine_plan_validation.go index 717f6cbc4b9fe..49979f0ca00ef 100644 --- a/pkg/frontend/routine_plan_validation.go +++ b/pkg/frontend/routine_plan_validation.go @@ -313,10 +313,10 @@ func validateRoutinePlanDependencies(ctx context.Context, ses FeSession, p *plan if !strings.EqualFold(revisionSecurityType, baseSecurityType) { return false, fmt.Errorf("UNSUPPORTED_ROUTINE_VERSION: routine %d has inconsistent revision and identity security contracts", id) } - if strings.EqualFold(language, udf.LanguagePython) && !strings.EqualFold(revisionSecurityType, "INVOKER") { + if language == udf.LanguagePython && !strings.EqualFold(revisionSecurityType, "INVOKER") { return false, fmt.Errorf("UNSUPPORTED_ROUTINE_VERSION: Python routine catalog security type is not INVOKER") } - if strings.EqualFold(language, udf.LanguageSQL) && !strings.EqualFold(revisionSecurityType, "DEFINER") { + if language == udf.LanguageSQL && !strings.EqualFold(revisionSecurityType, "DEFINER") { return false, fmt.Errorf("UNSUPPORTED_ROUTINE_VERSION: SQL routine catalog security type is not DEFINER") } identityValid := routinePlanCatalogIdentityValidForLanguage( @@ -372,7 +372,7 @@ func routinePlanCatalogIdentityValidForLanguage( definitionSchema int64, revisionABI, revisionAdapter, revisionSDK, revisionNullPolicy string, ) bool { - if strings.EqualFold(language, udf.LanguageSQL) { + if language == udf.LanguageSQL { expectedFingerprint, err := function.SQLRoutineFingerprint(rawBody, revisionArgTypes, revisionRetType) return err == nil && fingerprint == expectedFingerprint && @@ -380,7 +380,7 @@ func routinePlanCatalogIdentityValidForLanguage( revisionABI == "" && revisionAdapter == "" && revisionSDK == "" && revisionNullPolicy == udf.NullCallHandler } - if !strings.EqualFold(language, udf.LanguagePython) { + if language != udf.LanguagePython { return false } decoded, err := function.DecodePythonRoutineBody(rawBody) diff --git a/pkg/frontend/routine_plan_validation_test.go b/pkg/frontend/routine_plan_validation_test.go index e4e3e4441d857..bcc9cdc1c6c22 100644 --- a/pkg/frontend/routine_plan_validation_test.go +++ b/pkg/frontend/routine_plan_validation_test.go @@ -314,6 +314,12 @@ func TestRoutinePlanCatalogIdentityAcceptsAndRejectsSQLRevision(t *testing.T) { "lua", body, fingerprint, 0, "", "", "", argTypes, retType, udf.SQLDefinitionSchemaVersion, "", "", "", udf.NullCallHandler, )) + require.False(t, routinePlanCatalogIdentityValidForLanguage( + "PYTHON", body, fingerprint, udf.PythonSignatureKeySchemaVersion, + "", "", "", argTypes, retType, udf.PythonDefinitionSchemaVersion, + udf.PythonABIContract, udf.PythonAdapterVersion, udf.PythonSDKVersion, + udf.NullCallHandler, + )) } func TestRoutinePlanDependenciesVisitsSharedQueriesOnce(t *testing.T) { diff --git a/pkg/frontend/snapshot_catalog_restore.go b/pkg/frontend/snapshot_catalog_restore.go index c05103646c332..1dc99c2310d4f 100644 --- a/pkg/frontend/snapshot_catalog_restore.go +++ b/pkg/frontend/snapshot_catalog_restore.go @@ -207,13 +207,13 @@ func validateRestoredFunctionRevisionCatalog( if err != nil { return fmt.Errorf("UNSUPPORTED_ROUTINE_VERSION: restored function %d revision %d has an invalid definition schema", functionID, revision) } - if strings.EqualFold(row[8], udf.LanguageSQL) { + if row[8] == udf.LanguageSQL { if err := validateRestoredSQLFunctionRevision(functionID, revision, row, definitionSchema); err != nil { return err } continue } - if !strings.EqualFold(row[8], udf.LanguagePython) || + if row[8] != udf.LanguagePython || definitionSchema != udf.PythonDefinitionSchemaVersion || row[10] != udf.PythonABIContract || row[11] != udf.PythonAdapterVersion || @@ -517,7 +517,7 @@ func publishRestoredPythonArtifacts( if len(row) != 3 { return fmt.Errorf("UNSUPPORTED_ROUTINE_VERSION: restored revision row %d has %d artifact columns", rowIndex, len(row)) } - if strings.EqualFold(row[0], udf.LanguagePython) { + if row[0] == udf.LanguagePython { needStore = true break } @@ -534,7 +534,7 @@ func publishRestoredPythonArtifacts( return err } for rowIndex, row := range rows { - if !strings.EqualFold(row[0], udf.LanguagePython) { + if row[0] != udf.LanguagePython { continue } body, decodeErr := function.DecodePythonRoutineBody(row[2]) diff --git a/pkg/sql/plan/function/udf.go b/pkg/sql/plan/function/udf.go index 4816812125009..7a79b9d41476a 100644 --- a/pkg/sql/plan/function/udf.go +++ b/pkg/sql/plan/function/udf.go @@ -425,7 +425,7 @@ func (u *Udf) GetArgsPlanType() []*plan.Type { } func (u *Udf) GetRetPlanType() *plan.Type { return type2PlanType(u.GetRetType()) } func (u *Udf) GetArgsType() []types.Type { - if strings.EqualFold(u.Language, "python") { + if u.Language == udf.LanguagePython { // Resolver-created routines always set PythonReturnType when the // canonical descriptor has been loaded. Keeping the fallback makes // the generic Udf value usable by planner tests and by the SQL-side @@ -443,7 +443,7 @@ func (u *Udf) GetArgsType() []types.Type { return u.ArgsType } func (u *Udf) GetRetType() types.Type { - if strings.EqualFold(u.Language, "python") { + if u.Language == udf.LanguagePython { if u.PythonReturnType == nil { return types.Types[u.RetType].ToType() } @@ -457,9 +457,12 @@ func (u *Udf) GetRetType() types.Type { // rejected instead of being reinterpreted through the legacy logical-type // columns. func (u *Udf) LoadPythonTypeContract() error { - if !strings.EqualFold(u.Language, "python") { + if u == nil || u.Language == udf.LanguageSQL { return nil } + if u.Language != udf.LanguagePython { + return fmt.Errorf("UNSUPPORTED_ROUTINE_VERSION: unsupported routine language %q", u.Language) + } u.PythonArgTypes = nil u.PythonReturnType = nil body, err := DecodePythonRoutineBody(u.Body) @@ -477,9 +480,12 @@ func (u *Udf) LoadPythonTypeContract() error { // before that decode during lookup, so this second check closes the only point // where an argument list and its exact ABI could otherwise diverge. func (u *Udf) ValidatePythonTypeContract() error { - if !strings.EqualFold(u.Language, "python") { + if u == nil || u.Language == udf.LanguageSQL { return nil } + if u.Language != udf.LanguagePython { + return fmt.Errorf("UNSUPPORTED_ROUTINE_VERSION: unsupported routine language %q", u.Language) + } if u.PythonReturnType == nil { return fmt.Errorf("python routine is missing return descriptor") } @@ -501,9 +507,12 @@ func (u *Udf) ValidatePythonTypeContract() error { // they still must agree on the logical OID or a damaged catalog row could be // selected by overload resolution and dispatched with a different ABI. func (u *Udf) ValidatePythonCatalogSignature() error { - if u == nil || !strings.EqualFold(u.Language, "python") { + if u == nil || u.Language == udf.LanguageSQL { return nil } + if u.Language != udf.LanguagePython { + return fmt.Errorf("UNSUPPORTED_ROUTINE_VERSION: unsupported routine language %q", u.Language) + } if len(u.Args) != len(u.PythonArgTypes) || u.PythonReturnType == nil { return fmt.Errorf("UNSUPPORTED_ROUTINE_VERSION: Python catalog signature is incomplete") } diff --git a/pkg/sql/plan/function/udf_test.go b/pkg/sql/plan/function/udf_test.go index 93ada8b5ebd10..6015e1a3fd12d 100644 --- a/pkg/sql/plan/function/udf_test.go +++ b/pkg/sql/plan/function/udf_test.go @@ -344,6 +344,13 @@ func TestPythonRoutineCallRejectsNoncanonicalLanguage(t *testing.T) { require.ErrorContains(t, err, `routine call has unsupported language "PYTHON"`) } +func TestPythonContractPreflightRejectsNoncanonicalLanguage(t *testing.T) { + routine := &Udf{Language: "PYTHON"} + require.ErrorContains(t, routine.LoadPythonTypeContract(), "unsupported routine language") + require.ErrorContains(t, routine.ValidatePythonTypeContract(), "unsupported routine language") + require.ErrorContains(t, routine.ValidatePythonCatalogSignature(), "unsupported routine language") +} + func TestSQLRoutineFingerprintAndCallUseSharedRevisionContract(t *testing.T) { fingerprint, err := SQLRoutineFingerprint("select value + 1", "[\"bigint\"]", "bigint") require.NoError(t, err) diff --git a/pkg/sql/plan/routine_dependency.go b/pkg/sql/plan/routine_dependency.go index 3245686641d67..f80920cf8f2dd 100644 --- a/pkg/sql/plan/routine_dependency.go +++ b/pkg/sql/plan/routine_dependency.go @@ -10,6 +10,7 @@ import ( "strings" planpb "github.com/matrixorigin/matrixone/pkg/pb/plan" + "github.com/matrixorigin/matrixone/pkg/udf" ) // maxRoutinePlanDependencies bounds the amount of catalog state retained by @@ -25,6 +26,9 @@ func (builder *QueryBuilder) assignRoutineCallsite(call *planpb.RoutineCall) err if builder == nil || call == nil { return nil } + if call.Language != udf.LanguageSQL && call.Language != udf.LanguagePython { + return fmt.Errorf("UNSUPPORTED_ROUTINE_VERSION: routine call language %q is not canonical", call.Language) + } if call.CallsiteId != "" { if len(call.CallsiteId) > 256 || strings.ContainsAny(call.CallsiteId, "\r\n") { return fmt.Errorf("PROGRAM_LIMIT_EXCEEDED: routine callsite id is too large or invalid") @@ -43,6 +47,9 @@ func (builder *QueryBuilder) recordRoutinePlanDependency(call *planpb.RoutineCal if builder == nil || builder.qry == nil || call == nil || call.FunctionRef == nil { return nil } + if call.Language != udf.LanguageSQL && call.Language != udf.LanguagePython { + return fmt.Errorf("UNSUPPORTED_ROUTINE_VERSION: routine call language %q is not canonical", call.Language) + } ref := call.FunctionRef dependency := &planpb.RoutinePlanDependency{ FunctionRef: &planpb.FunctionRef{ @@ -52,7 +59,7 @@ func (builder *QueryBuilder) recordRoutinePlanDependency(call *planpb.RoutineCal AccountId: ref.AccountId, DatabaseId: ref.DatabaseId, }, - Language: strings.ToLower(call.Language), + Language: call.Language, ContractVersion: call.ContractVersion, DefinitionFingerprint: "", Volatility: call.Volatility, diff --git a/pkg/sql/plan/routine_dependency_test.go b/pkg/sql/plan/routine_dependency_test.go index 4f49ada054540..0a49b099d6e12 100644 --- a/pkg/sql/plan/routine_dependency_test.go +++ b/pkg/sql/plan/routine_dependency_test.go @@ -85,6 +85,14 @@ func TestAssignRoutineCallsiteIsPlanLocalAndIdempotent(t *testing.T) { require.Equal(t, "python/2", second.CallsiteId) } +func TestRoutineDependencyRejectsNoncanonicalLanguage(t *testing.T) { + builder := &QueryBuilder{qry: &planpb.Query{}} + call := testRoutineCallForDependency() + call.Language = "PYTHON" + require.ErrorContains(t, builder.assignRoutineCallsite(call), "language") + require.ErrorContains(t, builder.recordRoutinePlanDependency(call), "language") +} + func TestRecordRoutinePlanDependencyBoundsPlanClosure(t *testing.T) { builder := &QueryBuilder{qry: &planpb.Query{ RoutineDependencies: make([]*planpb.RoutinePlanDependency, maxRoutinePlanDependencies), From 61ab50ac59faf18cdaf04d0a88292ad465098126 Mon Sep 17 00:00:00 2001 From: iamlinjunhong <1030420200@qq.com> Date: Tue, 15 Sep 2026 11:43:10 +0800 Subject: [PATCH 124/202] fix(udf): validate module handler bindings --- pkg/udf/python/worker/test_worker.py | 29 +++++++++ pkg/udf/python/worker/worker.py | 90 ++++++++++++++++++++++++++++ 2 files changed, 119 insertions(+) diff --git a/pkg/udf/python/worker/test_worker.py b/pkg/udf/python/worker/test_worker.py index f850a1b8f4c7f..8777c94263631 100644 --- a/pkg/udf/python/worker/test_worker.py +++ b/pkg/udf/python/worker/test_worker.py @@ -237,6 +237,35 @@ def test_definition_validation_rejects_handler_rebound_after_definition(self): finally: server.shutdown() + def test_definition_validation_rejects_handler_rebound_in_module_control_flow(self): + sources = ( + "def f(ctx, value): return value\n" + "if True:\n" + " f = 1\n", + "def f(ctx, value): return value\n" + "for f in (1,):\n" + " pass\n", + "def f(ctx, value): return value\n" + "from math import pi as f\n", + ) + for source in sources: + with self.subTest(source=source): + with self.assertRaisesRegex(ValueError, "not a module-level function"): + worker._validate_definition_syntax( + complete_definition_validation_payload(source) + ) + + # A same-named local in a nested function does not rebind the module + # handler and must remain valid. + worker._validate_definition_syntax( + complete_definition_validation_payload( + "def f(ctx, value):\n" + " def inner():\n" + " f = 1\n" + " return value\n" + ) + ) + def test_capability_advertises_worker_instance_lease(self): encoded = worker._encode_capabilities( {"protocol_version": worker.PROTOCOL_VERSION}, lease_epoch=17 diff --git a/pkg/udf/python/worker/worker.py b/pkg/udf/python/worker/worker.py index ac3d5d8eae6ab..7b24fcd2ecf82 100644 --- a/pkg/udf/python/worker/worker.py +++ b/pkg/udf/python/worker/worker.py @@ -952,6 +952,92 @@ def _validate_definition_syntax(value: Dict[str, Any]) -> None: ) tree = ast.parse(value["source"], filename="", mode="exec") binding = None + + class NestedModuleBindingVisitor(ast.NodeVisitor): + """Find handler bindings below a module statement. + + A handler must be established by an unconditional module-level + function definition or lambda assignment. Assignments in control + flow, imports, and pattern targets can otherwise leave CREATE looking + valid while the value exposed by exec() is absent or non-callable. + Function/class bodies and comprehension targets have their own scope + and are deliberately not treated as module rebinding. + """ + + def __init__(self): + self.found = False + + def _mark(self): + self.found = True + + def visit_Name(self, node): + if node.id == handler and isinstance(node.ctx, (ast.Store, ast.Del)): + self._mark() + + def visit_FunctionDef(self, node): + if node.name == handler: + self._mark() + + def visit_AsyncFunctionDef(self, node): + if node.name == handler: + self._mark() + + def visit_ClassDef(self, node): + if node.name == handler: + self._mark() + + def visit_Lambda(self, node): + return + + def visit_ListComp(self, node): + self.visit(node.elt) + for generator in node.generators: + self.visit(generator.iter) + for condition in generator.ifs: + self.visit(condition) + + visit_SetComp = visit_ListComp + visit_GeneratorExp = visit_ListComp + + def visit_DictComp(self, node): + self.visit(node.key) + self.visit(node.value) + for generator in node.generators: + self.visit(generator.iter) + for condition in generator.ifs: + self.visit(condition) + + def visit_Import(self, node): + for alias in node.names: + bound = alias.asname or alias.name.split(".", 1)[0] + if bound == handler: + self._mark() + + def visit_ImportFrom(self, node): + for alias in node.names: + if alias.name == "*" or (alias.asname or alias.name) == handler: + self._mark() + + def visit_ExceptHandler(self, node): + if node.name == handler: + self._mark() + self.visit(node.type) + self.visit_nodes(node.body) + + def visit_MatchAs(self, node): + if node.name == handler: + self._mark() + if node.pattern is not None: + self.visit(node.pattern) + + def visit_MatchStar(self, node): + if node.name == handler: + self._mark() + + def visit_nodes(self, nodes): + for child in nodes: + self.visit(child) + for node in tree.body: if isinstance(node, (ast.FunctionDef, ast.AsyncFunctionDef)) and node.name == handler: # Continue scanning: a later module-level assignment can replace @@ -973,6 +1059,10 @@ def _validate_definition_syntax(value: Dict[str, Any]) -> None: if any(isinstance(target, ast.Name) and target.id == handler for target in node.targets): binding = "other" continue + visitor = NestedModuleBindingVisitor() + visitor.visit(node) + if visitor.found: + binding = "other" if binding == "async": raise ValueError("USER_CODE: Python handler must be synchronous") if binding not in ("sync", "lambda"): From 058a1e7d4b81e5cbdc596b706c2fc4d980a0348b Mon Sep 17 00:00:00 2001 From: iamlinjunhong <1030420200@qq.com> Date: Tue, 15 Sep 2026 11:45:32 +0800 Subject: [PATCH 125/202] fix(udf): bound resolved artifact sources --- pkg/udf/python/gateway.go | 11 +++++++++++ pkg/udf/python/gateway_test.go | 18 ++++++++++++++++++ 2 files changed, 29 insertions(+) diff --git a/pkg/udf/python/gateway.go b/pkg/udf/python/gateway.go index 6667a824e8c2a..20204c132f361 100644 --- a/pkg/udf/python/gateway.go +++ b/pkg/udf/python/gateway.go @@ -1595,6 +1595,17 @@ func validateInvocation(invocation *udf.Invocation) error { if invocation.Source == "" { return fmt.Errorf("python udf: handler and source are required") } + // ArtifactResolver is an execution boundary, so the Gateway must enforce + // the same immutable source contract even when a custom resolver does not + // use FileArtifactStore. Do this before hashing or building the Open frame; + // otherwise an oversized or invalid source can consume a large temporary + // allocation and only fail at the Flight control-size boundary. + if !utf8.ValidString(invocation.Source) { + return fmt.Errorf("UNSUPPORTED_ROUTINE_VERSION: Python artifact source is not valid UTF-8") + } + if int64(len([]byte(invocation.Source))) > DefaultMaxArtifactBytes { + return fmt.Errorf("RESOURCE_EXHAUSTED: Python artifact exceeds %d bytes", DefaultMaxArtifactBytes) + } if invocation.ArtifactDigest != udf.PythonInlineArtifactDigest(invocation.Handler, invocation.Source) { return fmt.Errorf("UNSUPPORTED_ROUTINE_VERSION: Python artifact digest does not match the immutable source") } diff --git a/pkg/udf/python/gateway_test.go b/pkg/udf/python/gateway_test.go index b9fa665e3e062..6ead77eb9346d 100644 --- a/pkg/udf/python/gateway_test.go +++ b/pkg/udf/python/gateway_test.go @@ -220,6 +220,24 @@ func TestValidateInvocationRequiresFrozenContract(t *testing.T) { } } +func TestValidateInvocationBoundsResolvedArtifact(t *testing.T) { + oversized := validInvocation() + oversized.Source = strings.Repeat("x", int(DefaultMaxArtifactBytes)+1) + require.ErrorContains( + t, + validateInvocation(oversized), + "Python artifact exceeds", + ) + + invalidUTF8 := validInvocation() + invalidUTF8.Source = string([]byte{0xff}) + require.ErrorContains( + t, + validateInvocation(invalidUTF8), + "source is not valid UTF-8", + ) +} + func TestGatewayValidatesIdentityBeforeResolvingArtifact(t *testing.T) { resolver := &recordingArtifactResolver{source: validInvocation().Source} gateway, err := NewGatewayWithArtifactStore(ClientConfig{ From a4264ee61167222d7aad4704c28db79a159ed14c Mon Sep 17 00:00:00 2001 From: iamlinjunhong <1030420200@qq.com> Date: Tue, 15 Sep 2026 11:47:57 +0800 Subject: [PATCH 126/202] fix(udf): align statement timestamp bounds --- pkg/udf/python/worker/test_worker.py | 28 ++++++++++++++++++++++++++++ pkg/udf/python/worker/worker.py | 13 +++++++++---- pkg/udf/runtime.go | 9 +++++++++ pkg/udf/runtime_test.go | 14 ++++++++++++++ 4 files changed, 60 insertions(+), 4 deletions(-) diff --git a/pkg/udf/python/worker/test_worker.py b/pkg/udf/python/worker/test_worker.py index 8777c94263631..194d5a244e840 100644 --- a/pkg/udf/python/worker/test_worker.py +++ b/pkg/udf/python/worker/test_worker.py @@ -371,6 +371,34 @@ def test_typed_statement_context_rejects_timestamp_outside_int64(self): } ) + def test_statement_context_matches_python_datetime_range(self): + for timestamp in ( + worker.MIN_STATEMENT_TIMESTAMP_UTC - 1, + worker.MAX_STATEMENT_TIMESTAMP_UTC + 1, + ): + typed = { + "contract_version": 1, + "statement_timestamp_utc": timestamp, + "timezone_kind": "FIXED_OFFSET", + "timezone_offset_minutes": 0, + "sql_mode": [], + "current_user": "alice", + "connection_collation": "utf8mb4_bin", + } + with self.assertRaisesRegex(ValueError, "invalid typed statement timestamp"): + worker._typed_statement_context(typed) + with self.assertRaisesRegex(ValueError, "invalid statement_timestamp_utc"): + worker._statement_context( + { + "statement_timestamp_utc": str(timestamp), + "session_timezone_kind": "FIXED_OFFSET", + "session_timezone_offset_minutes": "0", + "sql_mode": "[]", + "current_user": "alice", + "connection_collation": "utf8mb4_bin", + } + ) + def test_typed_contract_versions_require_integer_wire_values(self): typed_context = { "contract_version": True, diff --git a/pkg/udf/python/worker/worker.py b/pkg/udf/python/worker/worker.py index 7b24fcd2ecf82..8b130515ac86d 100644 --- a/pkg/udf/python/worker/worker.py +++ b/pkg/udf/python/worker/worker.py @@ -77,6 +77,11 @@ class _NoFlightServerBase: MAX_SECURITY_PRINCIPAL_ID = (1 << 32) - 1 MIN_INT64 = -(1 << 63) MAX_INT64 = (1 << 63) - 1 +# StatementContext is materialized as datetime.datetime. This is narrower +# than the wire int64 domain and must match the Go context validator before an +# invocation reaches user code. +MIN_STATEMENT_TIMESTAMP_UTC = -62135596800000000 +MAX_STATEMENT_TIMESTAMP_UTC = 253402300799999999 MIN_INT32 = -(1 << 31) MAX_INT32 = (1 << 31) - 1 # Admission bounds cover active fences and retained terminal tombstones. The @@ -552,8 +557,8 @@ def _typed_statement_context(raw: Any) -> Optional[Dict[str, Any]]: if ( isinstance(timestamp, bool) or not isinstance(timestamp, int) - or timestamp < MIN_INT64 - or timestamp > MAX_INT64 + or timestamp < MIN_STATEMENT_TIMESTAMP_UTC + or timestamp > MAX_STATEMENT_TIMESTAMP_UTC ): raise ValueError("PROTOCOL: invalid typed statement timestamp") timezone_kind = raw.get("timezone_kind") @@ -670,8 +675,8 @@ def _statement_context(raw: Any) -> Optional[StatementContext]: timestamp_text = _required_context_value(raw, "statement_timestamp_utc") try: timestamp_micros = int(timestamp_text, 10) - if timestamp_micros < MIN_INT64 or timestamp_micros > MAX_INT64: - raise ValueError("timestamp is outside int64 range") + if timestamp_micros < MIN_STATEMENT_TIMESTAMP_UTC or timestamp_micros > MAX_STATEMENT_TIMESTAMP_UTC: + raise ValueError("timestamp is outside the Python datetime range") seconds, micros = divmod(timestamp_micros, 1_000_000) timestamp = _datetime.datetime(1970, 1, 1, tzinfo=_datetime.timezone.utc) + _datetime.timedelta(seconds=seconds, microseconds=micros) except (TypeError, ValueError, OverflowError) as exc: diff --git a/pkg/udf/runtime.go b/pkg/udf/runtime.go index e56f5e08b78a7..bdfd87a15d690 100644 --- a/pkg/udf/runtime.go +++ b/pkg/udf/runtime.go @@ -41,6 +41,12 @@ const ( PythonABIContract = "PYTHON_ARROW" PythonAdapterVersion = "2026-09" PythonSDKVersion = "1.0" + // Python materializes statement timestamps as datetime.datetime. Keep the + // Go boundary identical to Python's year-1 through year-9999 UTC range; + // accepting the full int64 domain here would let a plan pass CN validation + // and fail only after the worker starts decoding its context. + minStatementTimestampUTC int64 = -62135596800000000 + maxStatementTimestampUTC int64 = 253402300799999999 // These numeric contracts are persisted in catalog and plan records. They // are intentionally separate from the human-facing adapter/SDK names: // changing a wire or persistent shape must fail closed before user code is @@ -86,6 +92,9 @@ func (c StatementContext) Validate() error { if c.ContractVersion != StatementContextContractVersion { return fmt.Errorf("UNSUPPORTED_ROUTINE_VERSION: unsupported statement context contract %d", c.ContractVersion) } + if c.StatementTimestampUTC < minStatementTimestampUTC || c.StatementTimestampUTC > maxStatementTimestampUTC { + return fmt.Errorf("python udf: statement timestamp is outside the Python datetime range") + } if c.CurrentUser == "" || c.ConnectionCollation == "" { return fmt.Errorf("python udf: statement context has no authenticated user or collation") } diff --git a/pkg/udf/runtime_test.go b/pkg/udf/runtime_test.go index 9c937606a8f15..5997e356788f8 100644 --- a/pkg/udf/runtime_test.go +++ b/pkg/udf/runtime_test.go @@ -107,6 +107,20 @@ func TestStatementContextAcceptsUnixEpoch(t *testing.T) { require.NoError(t, snapshot.Validate()) } +func TestStatementContextMatchesPythonDatetimeRange(t *testing.T) { + base := StatementContext{ + ContractVersion: StatementContextContractVersion, + TimezoneKind: "FIXED_OFFSET", + CurrentUser: "alice", + ConnectionCollation: "utf8mb4_bin", + } + for _, timestamp := range []int64{minStatementTimestampUTC - 1, maxStatementTimestampUTC + 1} { + context := base + context.StatementTimestampUTC = timestamp + require.ErrorContains(t, context.Validate(), "outside the Python datetime range") + } +} + func TestSecurityFrameRejectsEffectivePrincipalChange(t *testing.T) { frame := SecurityFrame{ ContractVersion: SecurityFrameContractVersion, From bed268c25838cd763f1b365a00548002c3a552ac Mon Sep 17 00:00:00 2001 From: iamlinjunhong <1030420200@qq.com> Date: Tue, 15 Sep 2026 11:53:51 +0800 Subject: [PATCH 127/202] fix(udf): handle bare except in handler validation --- pkg/udf/python/worker/test_worker.py | 11 +++++++++++ pkg/udf/python/worker/worker.py | 5 ++++- 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/pkg/udf/python/worker/test_worker.py b/pkg/udf/python/worker/test_worker.py index 194d5a244e840..76e3986f10ed9 100644 --- a/pkg/udf/python/worker/test_worker.py +++ b/pkg/udf/python/worker/test_worker.py @@ -266,6 +266,17 @@ def test_definition_validation_rejects_handler_rebound_in_module_control_flow(se ) ) + def test_definition_validation_accepts_bare_except(self): + worker._validate_definition_syntax( + complete_definition_validation_payload( + "def f(ctx, value): return value\n" + "try:\n" + " pass\n" + "except:\n" + " pass\n" + ) + ) + def test_capability_advertises_worker_instance_lease(self): encoded = worker._encode_capabilities( {"protocol_version": worker.PROTOCOL_VERSION}, lease_epoch=17 diff --git a/pkg/udf/python/worker/worker.py b/pkg/udf/python/worker/worker.py index 8b130515ac86d..d42bc59ea3651 100644 --- a/pkg/udf/python/worker/worker.py +++ b/pkg/udf/python/worker/worker.py @@ -1026,7 +1026,10 @@ def visit_ImportFrom(self, node): def visit_ExceptHandler(self, node): if node.name == handler: self._mark() - self.visit(node.type) + # A bare ``except:`` has no exception-expression node. Keep + # walking its body without asking NodeVisitor to visit None. + if node.type is not None: + self.visit(node.type) self.visit_nodes(node.body) def visit_MatchAs(self, node): From f00511d436965d60f9b273b8d51d321079918f34 Mon Sep 17 00:00:00 2001 From: iamlinjunhong <1030420200@qq.com> Date: Tue, 15 Sep 2026 11:58:13 +0800 Subject: [PATCH 128/202] fix(udf): bound artifact validation resolution --- pkg/udf/python/gateway.go | 18 ++++++++++--- pkg/udf/python/gateway_test.go | 48 ++++++++++++++++++++++++++++++++++ 2 files changed, 62 insertions(+), 4 deletions(-) diff --git a/pkg/udf/python/gateway.go b/pkg/udf/python/gateway.go index 20204c132f361..4c529cef3fd7d 100644 --- a/pkg/udf/python/gateway.go +++ b/pkg/udf/python/gateway.go @@ -200,6 +200,12 @@ func (g *Gateway) ValidateDefinition(ctx context.Context, definition *udf.Routin if ctx == nil { ctx = context.Background() } + requestCtx := ctx + var cancel context.CancelFunc + if g.cfg.RequestTimeout > 0 { + requestCtx, cancel = context.WithTimeout(ctx, g.cfg.RequestTimeout) + defer cancel() + } if definition == nil { return fmt.Errorf("UNSUPPORTED_ROUTINE_VERSION: nil Python routine definition") } @@ -262,7 +268,7 @@ func (g *Gateway) ValidateDefinition(ctx context.Context, definition *udf.Routin source := definition.Source if g.artifactResolver != nil { resolved, resolveErr := g.artifactResolver.Resolve( - ctx, definition.AccountID, definition.Handler, definition.ArtifactDigest, + requestCtx, definition.AccountID, definition.Handler, definition.ArtifactDigest, ) if resolveErr != nil { return resolveErr @@ -292,7 +298,7 @@ func (g *Gateway) ValidateDefinition(ctx context.Context, definition *udf.Routin // lease in its request. It must perform its own worker-instance handshake; // relying on a cached readiness result could validate against a replacement // worker with a different contract. - if err := g.ensureCapabilities(ctx, client, true); err != nil { + if err := g.ensureCapabilities(requestCtx, client, true); err != nil { return err } payload, err := json.Marshal(definitionValidationPayload{ @@ -314,8 +320,12 @@ func (g *Gateway) ValidateDefinition(ctx context.Context, definition *udf.Routin if err != nil { return fmt.Errorf("python udf: encode definition validation: %w", err) } - actionCtx, cancel := context.WithTimeout(ctx, g.cfg.RequestTimeout) - defer cancel() + actionCtx := requestCtx + var actionCancel context.CancelFunc + if g.cfg.RequestTimeout > 0 { + actionCtx, actionCancel = context.WithTimeout(requestCtx, g.cfg.RequestTimeout) + defer actionCancel() + } stream, err := client.DoAction(actionCtx, &flight.Action{Type: "ValidatePythonDefinition", Body: payload}) if err != nil { g.invalidateCapabilities() diff --git a/pkg/udf/python/gateway_test.go b/pkg/udf/python/gateway_test.go index 6ead77eb9346d..27102c5cf3e16 100644 --- a/pkg/udf/python/gateway_test.go +++ b/pkg/udf/python/gateway_test.go @@ -61,6 +61,13 @@ type recordingArtifactResolver struct { digest string } +type blockingArtifactResolver struct{} + +func (blockingArtifactResolver) Resolve(ctx context.Context, _ uint64, _, _ string) (string, error) { + <-ctx.Done() + return "", ctx.Err() +} + func (r *recordingArtifactResolver) Resolve(_ context.Context, accountID uint64, handler, digest string) (string, error) { r.calls++ r.account = accountID @@ -69,6 +76,47 @@ func (r *recordingArtifactResolver) Resolve(_ context.Context, accountID uint64, return r.source, nil } +func TestValidateDefinitionBoundsArtifactResolution(t *testing.T) { + invocation := validInvocation() + gateway, err := NewGatewayWithArtifactStore(ClientConfig{ + Enabled: true, + AllowUnisolated: true, + ServerAddress: "127.0.0.1:1", + RequestTimeout: 20 * time.Millisecond, + }, blockingArtifactResolver{}) + require.NoError(t, err) + defer gateway.Close() + + definition := &udf.RoutineDefinition{ + Language: udf.LanguagePython, + AccountID: invocation.FunctionRef.AccountID, + Handler: invocation.Handler, + Args: append([]types.Type(nil), invocation.Args...), + ReturnType: invocation.ReturnType, + Mode: invocation.Mode, + NullPolicy: invocation.NullPolicy, + ABIContract: invocation.ABIContract, + AdapterVersion: invocation.AdapterVersion, + SDKVersion: invocation.SDKVersion, + DefinitionSchemaVersion: invocation.DefinitionSchemaVersion, + ArtifactDigest: invocation.ArtifactDigest, + EnvironmentDigest: invocation.EnvironmentDigest, + DefinitionFingerprint: invocation.DefinitionFingerprint, + } + + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + done := make(chan error, 1) + go func() { done <- gateway.ValidateDefinition(ctx, definition) }() + select { + case err := <-done: + require.ErrorIs(t, err, context.DeadlineExceeded) + case <-time.After(500 * time.Millisecond): + cancel() + require.FailNow(t, "artifact resolution was not bounded by the gateway request timeout") + } +} + func (c *finishAckFlightClient) DoAction( ctx context.Context, action *flight.Action, From 70606cffa4dbdfaad92288268b5466e94c668421 Mon Sep 17 00:00:00 2001 From: iamlinjunhong <1030420200@qq.com> Date: Tue, 15 Sep 2026 12:00:19 +0800 Subject: [PATCH 129/202] fix(udf): inspect mapping pattern handler captures --- pkg/udf/python/worker/test_worker.py | 4 ++++ pkg/udf/python/worker/worker.py | 8 ++++++++ 2 files changed, 12 insertions(+) diff --git a/pkg/udf/python/worker/test_worker.py b/pkg/udf/python/worker/test_worker.py index 76e3986f10ed9..f1e264d7daf82 100644 --- a/pkg/udf/python/worker/test_worker.py +++ b/pkg/udf/python/worker/test_worker.py @@ -247,6 +247,10 @@ def test_definition_validation_rejects_handler_rebound_in_module_control_flow(se " pass\n", "def f(ctx, value): return value\n" "from math import pi as f\n", + "def f(ctx, value): return value\n" + "match {\"value\": 1, \"other\": 2}:\n" + " case {\"value\": value, **f}:\n" + " pass\n", ) for source in sources: with self.subTest(source=source): diff --git a/pkg/udf/python/worker/worker.py b/pkg/udf/python/worker/worker.py index d42bc59ea3651..d61aee870a704 100644 --- a/pkg/udf/python/worker/worker.py +++ b/pkg/udf/python/worker/worker.py @@ -1042,6 +1042,14 @@ def visit_MatchStar(self, node): if node.name == handler: self._mark() + def visit_MatchMapping(self, node): + # ``rest`` is the capture name in a ``**name`` mapping pattern, + # represented as a plain string rather than an ast.Name. + if node.rest == handler: + self._mark() + self.visit_nodes(node.keys) + self.visit_nodes(node.patterns) + def visit_nodes(self, nodes): for child in nodes: self.visit(child) From 04f3d706e40ee72e47da0835f749b364691fe7e8 Mon Sep 17 00:00:00 2001 From: iamlinjunhong <1030420200@qq.com> Date: Tue, 15 Sep 2026 12:02:19 +0800 Subject: [PATCH 130/202] fix(udf): stop cleanup reaper on worker shutdown errors --- pkg/udf/python/worker/test_worker.py | 13 +++++++++++++ pkg/udf/python/worker/worker.py | 15 +++++++++++---- 2 files changed, 24 insertions(+), 4 deletions(-) diff --git a/pkg/udf/python/worker/test_worker.py b/pkg/udf/python/worker/test_worker.py index f1e264d7daf82..f846ad15946a5 100644 --- a/pkg/udf/python/worker/test_worker.py +++ b/pkg/udf/python/worker/test_worker.py @@ -301,6 +301,19 @@ def test_capability_advertises_worker_instance_lease(self): {"protocol_version": worker.PROTOCOL_VERSION}, lease_epoch=0 ) + def test_cleanup_reaper_can_be_stopped_independently(self): + server = worker.RoutineFlightServer("grpc://127.0.0.1:0") + try: + with server._pending_cleanup_condition: + server._ensure_pending_cleanup_thread_locked() + thread = server._pending_cleanup_thread + server._stop_pending_cleanup_reaper() + self.assertTrue(server._pending_cleanup_stopping) + self.assertIsNotNone(thread) + self.assertFalse(thread.is_alive()) + finally: + server.shutdown() + def test_typed_statement_context_matches_canonical_handler_context(self): typed = { "contract_version": 1, diff --git a/pkg/udf/python/worker/worker.py b/pkg/udf/python/worker/worker.py index d61aee870a704..c5091203e2030 100644 --- a/pkg/udf/python/worker/worker.py +++ b/pkg/udf/python/worker/worker.py @@ -3342,9 +3342,10 @@ def _pending_cleanup_loop(self) -> None: finish_error, ) - def shutdown(self): - self._shutdown_event.set() - result = super().shutdown() + def _stop_pending_cleanup_reaper(self) -> None: + # The base Flight server may fail while interrupting an RPC. The + # worker still owns its cleanup reaper in that case, so stopping and + # joining it cannot depend on the base shutdown returning normally. with self._pending_cleanup_condition: self._pending_cleanup_stopping = True self._pending_cleanup_condition.notify_all() @@ -3362,7 +3363,13 @@ def shutdown(self): "Python UDF worker shutdown left %d handler cleanups pending", pending, ) - return result + + def shutdown(self): + self._shutdown_event.set() + try: + return super().shutdown() + finally: + self._stop_pending_cleanup_reaper() def _cleanup_exchange( self, From c79d2aaa7e9b9f77fd4b59eb379a548935dad8ed Mon Sep 17 00:00:00 2001 From: iamlinjunhong <1030420200@qq.com> Date: Tue, 15 Sep 2026 12:05:52 +0800 Subject: [PATCH 131/202] fix(udf): reject null sql mode context --- pkg/udf/runtime.go | 8 ++++++++ pkg/udf/runtime_test.go | 4 ++++ 2 files changed, 12 insertions(+) diff --git a/pkg/udf/runtime.go b/pkg/udf/runtime.go index bdfd87a15d690..ada399a2e5a4a 100644 --- a/pkg/udf/runtime.go +++ b/pkg/udf/runtime.go @@ -160,6 +160,14 @@ func StatementContextFromMap(values map[string]string) (*StatementContext, error if err := json.Unmarshal([]byte(raw), &mode); err != nil { return nil, fmt.Errorf("python udf: invalid statement sql_mode: %w", err) } + // The wire contract is a JSON array, including when it is empty. A + // JSON null decodes successfully into a nil Go slice, but would be + // serialized back as null and rejected by the Python context decoder. + // Reject it at the trusted CN adapter instead of carrying a malformed + // snapshot into the Flight request. + if mode == nil { + return nil, fmt.Errorf("python udf: invalid statement sql_mode: expected a JSON array") + } } context := &StatementContext{ ContractVersion: StatementContextContractVersion, diff --git a/pkg/udf/runtime_test.go b/pkg/udf/runtime_test.go index 5997e356788f8..c5f80fbda6ebf 100644 --- a/pkg/udf/runtime_test.go +++ b/pkg/udf/runtime_test.go @@ -91,6 +91,10 @@ func TestStatementContextFromMapRejectsAmbiguousTimezoneAndMalformedMode(t *test malformed["sql_mode"] = "[\"ANSI\",\"ANSI\"]" _, err = StatementContextFromMap(malformed) require.ErrorContains(t, err, "not canonical") + + malformed["sql_mode"] = "null" + _, err = StatementContextFromMap(malformed) + require.ErrorContains(t, err, "expected a JSON array") } func TestStatementContextAcceptsUnixEpoch(t *testing.T) { From 2e250bcc834dd6569a4d5038e1eae3ba1f13fe5e Mon Sep 17 00:00:00 2001 From: iamlinjunhong <1030420200@qq.com> Date: Tue, 15 Sep 2026 12:06:33 +0800 Subject: [PATCH 132/202] fix(udf): validate IANA zones at CN boundary --- pkg/udf/runtime.go | 4 ++++ pkg/udf/runtime_test.go | 28 ++++++++++++++++++++++++++++ 2 files changed, 32 insertions(+) diff --git a/pkg/udf/runtime.go b/pkg/udf/runtime.go index ada399a2e5a4a..d68bde7f54323 100644 --- a/pkg/udf/runtime.go +++ b/pkg/udf/runtime.go @@ -27,6 +27,7 @@ import ( "strconv" "strings" "sync" + "time" "github.com/matrixorigin/matrixone/pkg/common/moerr" "github.com/matrixorigin/matrixone/pkg/common/mpool" @@ -102,6 +103,9 @@ func (c StatementContext) Validate() error { if c.TimezoneName == "" || c.TimezoneDatabaseVersion == "" || c.TimezoneOffsetMinutes != 0 { return fmt.Errorf("python udf: IANA statement timezone is incomplete") } + if _, err := time.LoadLocation(c.TimezoneName); err != nil { + return fmt.Errorf("python udf: IANA statement timezone is not present in the local tzdb: %w", err) + } } else if c.TimezoneKind == "FIXED_OFFSET" { if c.TimezoneName != "" || c.TimezoneDatabaseVersion != "" || c.TimezoneOffsetMinutes < -839 || c.TimezoneOffsetMinutes > 840 { return fmt.Errorf("python udf: fixed statement timezone is invalid") diff --git a/pkg/udf/runtime_test.go b/pkg/udf/runtime_test.go index c5f80fbda6ebf..3faa79a6dfe99 100644 --- a/pkg/udf/runtime_test.go +++ b/pkg/udf/runtime_test.go @@ -111,6 +111,34 @@ func TestStatementContextAcceptsUnixEpoch(t *testing.T) { require.NoError(t, snapshot.Validate()) } +func TestStatementContextRejectsUnknownIANAZoneBeforeTransport(t *testing.T) { + snapshot := StatementContext{ + ContractVersion: StatementContextContractVersion, + StatementTimestampUTC: 0, + TimezoneKind: "IANA", + TimezoneName: "NoSuch/Zone", + TimezoneDatabaseVersion: "2026a", + CurrentUser: "alice", + ConnectionCollation: "utf8mb4_bin", + } + require.ErrorContains(t, snapshot.Validate(), "not present in the local tzdb") +} + +func TestStatementContextAcceptsKnownIANAZone(t *testing.T) { + version, err := TimezoneDatabaseVersion() + require.NoError(t, err) + snapshot := StatementContext{ + ContractVersion: StatementContextContractVersion, + StatementTimestampUTC: 0, + TimezoneKind: "IANA", + TimezoneName: "UTC", + TimezoneDatabaseVersion: version, + CurrentUser: "alice", + ConnectionCollation: "utf8mb4_bin", + } + require.NoError(t, snapshot.Validate()) +} + func TestStatementContextMatchesPythonDatetimeRange(t *testing.T) { base := StatementContext{ ContractVersion: StatementContextContractVersion, From 404f8d2c1061af6a7443e0fcddad4567aeb247cb Mon Sep 17 00:00:00 2001 From: iamlinjunhong <1030420200@qq.com> Date: Tue, 15 Sep 2026 12:08:25 +0800 Subject: [PATCH 133/202] fix(udf): cap artifact store to wire contract --- pkg/udf/python/artifact.go | 6 ++++++ pkg/udf/python/artifact_test.go | 5 +++++ 2 files changed, 11 insertions(+) diff --git a/pkg/udf/python/artifact.go b/pkg/udf/python/artifact.go index 957eff9deccaf..72912c2173bf0 100644 --- a/pkg/udf/python/artifact.go +++ b/pkg/udf/python/artifact.go @@ -68,6 +68,12 @@ func NewFileArtifactStore(fs fileservice.FileService, maxSize int64) (*FileArtif if maxSize <= 0 { maxSize = DefaultMaxArtifactBytes } + if maxSize > DefaultMaxArtifactBytes { + return nil, fmt.Errorf( + "UNSUPPORTED_ROUTINE_VERSION: Python artifact limit %d exceeds the current contract limit %d", + maxSize, DefaultMaxArtifactBytes, + ) + } return &FileArtifactStore{fs: shared, maxSize: maxSize}, nil } diff --git a/pkg/udf/python/artifact_test.go b/pkg/udf/python/artifact_test.go index cc617f68f52ec..5f56eebd3d568 100644 --- a/pkg/udf/python/artifact_test.go +++ b/pkg/udf/python/artifact_test.go @@ -76,6 +76,11 @@ func TestNewFileArtifactStoreRequiresSharedFileService(t *testing.T) { require.NoError(t, err) _, err = NewFileArtifactStore(local, 0) require.ErrorContains(t, err, "shared FileService") + + shared, err := fileservice.NewMemoryFS(defines.SharedFileServiceName, fileservice.DisabledCacheConfig, nil) + require.NoError(t, err) + _, err = NewFileArtifactStore(shared, DefaultMaxArtifactBytes+1) + require.ErrorContains(t, err, "exceeds the current contract limit") } func TestFileArtifactStoreRejectsInvalidDirectConstruction(t *testing.T) { From a7b507ac3b888277417116f59cab17841581ce22 Mon Sep 17 00:00:00 2001 From: iamlinjunhong <1030420200@qq.com> Date: Tue, 15 Sep 2026 12:12:17 +0800 Subject: [PATCH 134/202] fix(udf): bound control JSON nesting --- pkg/udf/protocol/protocol.go | 18 ++++++++++++++-- pkg/udf/protocol/protocol_test.go | 18 ++++++++++++++++ pkg/udf/python/worker/test_worker.py | 19 +++++++++++++++++ pkg/udf/python/worker/worker.py | 31 ++++++++++++++++++++++++++++ 4 files changed, 84 insertions(+), 2 deletions(-) diff --git a/pkg/udf/protocol/protocol.go b/pkg/udf/protocol/protocol.go index 1d9b4113d13f2..2ada4e2800423 100644 --- a/pkg/udf/protocol/protocol.go +++ b/pkg/udf/protocol/protocol.go @@ -44,6 +44,10 @@ const ( // but allowing an unbounded statement/group/invocation string would make // every bounded entry claim an unbounded memory claim. MaxFenceComponentBytes = 256 + // Control JSON is intentionally shallow. The byte bound alone is not a + // stack bound because duplicate-key validation walks nested arrays/objects + // before encoding/json can decode the envelope. + MaxJSONNesting = 64 ) var ( @@ -206,6 +210,9 @@ func validateControlFields(control Control, wire map[string]json.RawMessage) err if err := json.Unmarshal(control.Payload, &payload); err != nil || payload == nil { return fmt.Errorf("%w: control kind %q payload must be a JSON object", ErrProtocol, control.Kind) } + if err := rejectDuplicateJSONKeys(control.Payload); err != nil { + return fmt.Errorf("%w: invalid control payload: %v", ErrProtocol, err) + } } case "InputBatch", "ResultBatch": if control.Sequence == 0 { @@ -368,6 +375,10 @@ func rejectDuplicateJSONKeys(data []byte) error { } func walkJSONValue(decoder *json.Decoder) error { + return walkJSONValueAtDepth(decoder, 0) +} + +func walkJSONValueAtDepth(decoder *json.Decoder, depth int) error { token, err := decoder.Token() if err != nil { return fmt.Errorf("decode control JSON: %v", err) @@ -376,6 +387,9 @@ func walkJSONValue(decoder *json.Decoder) error { if !ok { return nil } + if depth >= MaxJSONNesting { + return fmt.Errorf("control JSON nesting exceeds %d levels", MaxJSONNesting) + } switch delim { case '{': seen := make(map[string]struct{}) @@ -392,7 +406,7 @@ func walkJSONValue(decoder *json.Decoder) error { return fmt.Errorf("duplicate control JSON field %q", key) } seen[key] = struct{}{} - if err := walkJSONValue(decoder); err != nil { + if err := walkJSONValueAtDepth(decoder, depth+1); err != nil { return err } } @@ -400,7 +414,7 @@ func walkJSONValue(decoder *json.Decoder) error { return err case '[': for decoder.More() { - if err := walkJSONValue(decoder); err != nil { + if err := walkJSONValueAtDepth(decoder, depth+1); err != nil { return err } } diff --git a/pkg/udf/protocol/protocol_test.go b/pkg/udf/protocol/protocol_test.go index f03e04e608556..13334a95b6016 100644 --- a/pkg/udf/protocol/protocol_test.go +++ b/pkg/udf/protocol/protocol_test.go @@ -18,6 +18,7 @@ import ( "bytes" "encoding/json" "errors" + "strings" "sync" "sync/atomic" "testing" @@ -70,6 +71,23 @@ func TestControlRejectsDuplicateJSONFields(t *testing.T) { require.ErrorContains(t, err, "duplicate control JSON field") } +func TestControlRejectsExcessiveJSONNesting(t *testing.T) { + deepPayload := `{"value":` + strings.Repeat("[", MaxJSONNesting) + "0" + strings.Repeat("]", MaxJSONNesting) + "}" + wire, err := MarshalControl(Control{ + Kind: "OpenInvocation", + Tuple: testTuple(), + Payload: json.RawMessage(deepPayload), + }) + require.ErrorContains(t, err, "nesting exceeds") + require.Nil(t, wire) + + // The same bound must protect an incoming frame even when it was produced + // by an older or non-Go peer that did not enforce the outbound check. + outer := `{"version":1,"kind":"OpenInvocation","tuple":{"account_id":1,"statement_id":"statement","group_id":"group","group_epoch":2,"invocation_id":"invocation","lease_epoch":3},"payload":` + deepPayload + `}` + _, err = UnmarshalControl([]byte(outer)) + require.ErrorContains(t, err, "nesting exceeds") +} + func TestOpenInvocationPayloadMustBeObject(t *testing.T) { for _, payload := range []string{"null", "[]", `"text"`} { wire := []byte(`{"version":1,"kind":"OpenInvocation","tuple":{"account_id":1,"statement_id":"statement","group_id":"group","group_epoch":2,"invocation_id":"invocation","lease_epoch":3},"payload":` + payload + `}`) diff --git a/pkg/udf/python/worker/test_worker.py b/pkg/udf/python/worker/test_worker.py index f846ad15946a5..4d81082cd05c4 100644 --- a/pkg/udf/python/worker/test_worker.py +++ b/pkg/udf/python/worker/test_worker.py @@ -3873,6 +3873,25 @@ def test_control_rejects_duplicate_json_fields(self): with self.assertRaisesRegex(ValueError, "duplicate control JSON field"): worker._decode_control(wire) + def test_control_rejects_excessive_json_nesting(self): + nested = "[" * worker.MAX_JSON_NESTING + "0" + "]" * worker.MAX_JSON_NESTING + value = { + "version": 1, + "kind": "OpenInvocation", + "tuple": { + "account_id": 1, + "statement_id": "statement", + "group_id": "group", + "group_epoch": 1, + "invocation_id": "invocation", + "lease_epoch": 1, + }, + "payload": {"value": json.loads(nested)}, + } + wire = json.dumps(value, separators=(",", ":")).encode() + with self.assertRaisesRegex(ValueError, "nesting exceeds"): + worker._decode_control(wire) + def test_result_ack_rejects_zero_sequence(self): state = worker._InvocationState({}, 1) state.last_result = 1 diff --git a/pkg/udf/python/worker/worker.py b/pkg/udf/python/worker/worker.py index c5091203e2030..e5bd4004eace2 100644 --- a/pkg/udf/python/worker/worker.py +++ b/pkg/udf/python/worker/worker.py @@ -65,6 +65,11 @@ class _NoFlightServerBase: PROTOCOL_VERSION = 1 MAX_CONTROL_BYTES = 1 << 20 +# Keep control parsing shallow even when the total JSON envelope is below the +# byte limit. This is the Python counterpart of protocol.MaxJSONNesting; a +# byte scanner makes the bound independent of the interpreter's recursion +# limit and ignores bracket characters inside JSON strings. +MAX_JSON_NESTING = 64 # Keep the Python wire boundary identical to protocol.FencingTuple's # component limit. The control frame limit alone is not sufficient: a # single oversized identity would otherwise consume most of every ledger @@ -805,9 +810,35 @@ def _reject_nonstandard_json_constant(value): raise ValueError(f"PROTOCOL: invalid JSON constant {value}") +def _validate_json_nesting(data: bytes) -> None: + depth = 0 + in_string = False + escaped = False + for byte in data: + if in_string: + if escaped: + escaped = False + elif byte == 0x5C: # backslash + escaped = True + elif byte == 0x22: # double quote + in_string = False + continue + if byte == 0x22: + in_string = True + elif byte == 0x7B or byte == 0x5B: # { or [ + depth += 1 + if depth > MAX_JSON_NESTING: + raise ValueError( + f"PROTOCOL: control JSON nesting exceeds {MAX_JSON_NESTING} levels" + ) + elif byte == 0x7D or byte == 0x5D: # } or ] + depth -= 1 + + def _decode_control(data: bytes) -> Dict[str, Any]: if not data or len(data) > MAX_CONTROL_BYTES: raise ValueError("PROTOCOL: invalid control size") + _validate_json_nesting(data) try: text = bytes(data).decode("utf-8") except UnicodeDecodeError as exc: From 174150d391e20bf1b7c6c432cc6acf422f3b2b65 Mon Sep 17 00:00:00 2001 From: iamlinjunhong <1030420200@qq.com> Date: Tue, 15 Sep 2026 12:12:55 +0800 Subject: [PATCH 135/202] fix(udf): bound validation action JSON --- pkg/udf/python/worker/test_worker.py | 11 +++++++++++ pkg/udf/python/worker/worker.py | 2 ++ 2 files changed, 13 insertions(+) diff --git a/pkg/udf/python/worker/test_worker.py b/pkg/udf/python/worker/test_worker.py index 4d81082cd05c4..037fb08d6156d 100644 --- a/pkg/udf/python/worker/test_worker.py +++ b/pkg/udf/python/worker/test_worker.py @@ -3892,6 +3892,17 @@ def test_control_rejects_excessive_json_nesting(self): with self.assertRaisesRegex(ValueError, "nesting exceeds"): worker._decode_control(wire) + def test_action_decoders_reject_excessive_json_nesting(self): + nested = "[" * worker.MAX_JSON_NESTING + "0" + "]" * worker.MAX_JSON_NESTING + with self.assertRaisesRegex(ValueError, "nesting exceeds"): + worker._decode_capability_request( + ('{"protocol_version":1,"extra":' + nested + '}').encode() + ) + with self.assertRaisesRegex(ValueError, "nesting exceeds"): + worker._decode_definition_validation( + ('{"extra":' + nested + '}').encode() + ) + def test_result_ack_rejects_zero_sequence(self): state = worker._InvocationState({}, 1) state.last_result = 1 diff --git a/pkg/udf/python/worker/worker.py b/pkg/udf/python/worker/worker.py index e5bd4004eace2..a277b0c8bd087 100644 --- a/pkg/udf/python/worker/worker.py +++ b/pkg/udf/python/worker/worker.py @@ -871,6 +871,7 @@ def _decode_control(data: bytes) -> Dict[str, Any]: def _decode_capability_request(data: bytes) -> Dict[str, Any]: if not data or len(data) > MAX_CONTROL_BYTES: raise ValueError("PROTOCOL: invalid capability request size") + _validate_json_nesting(data) try: value = json.loads( bytes(data).decode("utf-8"), @@ -890,6 +891,7 @@ def _decode_definition_validation(data: bytes) -> Dict[str, Any]: """Decode the strict, pre-publication definition validation payload.""" if not data or len(data) > MAX_DEFINITION_VALIDATION_BYTES: raise ValueError("PROTOCOL: invalid definition validation size") + _validate_json_nesting(data) try: value = json.loads( bytes(data).decode("utf-8"), From c394a0aadd6bbaf11114dc184fd8570e26a41e29 Mon Sep 17 00:00:00 2001 From: iamlinjunhong <1030420200@qq.com> Date: Tue, 15 Sep 2026 12:13:54 +0800 Subject: [PATCH 136/202] fix(udf): reject sub-minute session offsets --- pkg/sql/colexec/external_routine_eval.go | 3 +++ pkg/sql/colexec/external_routine_eval_test.go | 9 +++++++++ 2 files changed, 12 insertions(+) diff --git a/pkg/sql/colexec/external_routine_eval.go b/pkg/sql/colexec/external_routine_eval.go index f5274804d779a..d3bf16cfb8c34 100644 --- a/pkg/sql/colexec/external_routine_eval.go +++ b/pkg/sql/colexec/external_routine_eval.go @@ -651,6 +651,9 @@ func buildRoutineContext(proc *process.Process) (map[string]string, error) { } if zoneName == "FixedZone" { _, offset := queryStart.In(location).Zone() + if offset%60 != 0 { + return nil, fmt.Errorf("python udf: fixed session timezone offset must be a whole number of minutes") + } context["session_timezone_kind"] = "FIXED_OFFSET" context["session_timezone_offset_minutes"] = formatTimezoneOffsetMinutes(offset / 60) delete(context, "session_timezone_name") diff --git a/pkg/sql/colexec/external_routine_eval_test.go b/pkg/sql/colexec/external_routine_eval_test.go index 493d6e3b3e136..39ac13d31f227 100644 --- a/pkg/sql/colexec/external_routine_eval_test.go +++ b/pkg/sql/colexec/external_routine_eval_test.go @@ -387,6 +387,15 @@ func TestBuildRoutineContextRejectsUnstableTimezone(t *testing.T) { } } +func TestBuildRoutineContextRejectsSubMinuteFixedTimezone(t *testing.T) { + proc := testutil.NewProcess(t) + defer proc.Free() + proc.GetSessionInfo().TimeZone = time.FixedZone("FixedZone", 8*60*60+30) + + _, err := buildRoutineContext(proc) + require.ErrorContains(t, err, "whole number of minutes") +} + func testExternalRoutineCall(t *testing.T, mode, nullPolicy string) *planpb.RoutineCall { t.Helper() descriptor, err := function.NewPythonTypeDescriptor(types.T_int64.ToType()) From 31e26c37f00f276b2abe91156d98f9dbee0f63fa Mon Sep 17 00:00:00 2001 From: iamlinjunhong <1030420200@qq.com> Date: Tue, 15 Sep 2026 14:52:09 +0800 Subject: [PATCH 137/202] fix(frontend): stabilize Python replace validation --- pkg/bootstrap/versions/v4_0_7/upgrade_test.go | 8 ++++---- pkg/frontend/authenticate.go | 15 +++++++++++---- 2 files changed, 15 insertions(+), 8 deletions(-) diff --git a/pkg/bootstrap/versions/v4_0_7/upgrade_test.go b/pkg/bootstrap/versions/v4_0_7/upgrade_test.go index cb15460b65457..b65bd179adc80 100644 --- a/pkg/bootstrap/versions/v4_0_7/upgrade_test.go +++ b/pkg/bootstrap/versions/v4_0_7/upgrade_test.go @@ -28,14 +28,14 @@ func TestPythonFunctionRevisionUpgradeContract(t *testing.T) { if metadata.Version != "4.0.7" || metadata.MinUpgradeVersion != "4.0.6" { t.Fatalf("unexpected version metadata: %+v", metadata) } - if metadata.UpgradeCluster != versions.No || metadata.UpgradeTenant != versions.Yes { - t.Fatalf("python catalog upgrade must be tenant scoped: %+v", metadata) + if metadata.UpgradeCluster != versions.Yes || metadata.UpgradeTenant != versions.Yes { + t.Fatalf("4.0.7 must run both cluster and tenant upgrades: %+v", metadata) } if metadata.VersionOffset != uint32(len(tenantUpgEntries)) { t.Fatalf("version offset %d does not match tenant entries %d", metadata.VersionOffset, len(tenantUpgEntries)) } - if metadata.RequiredProtocolVersion != defines.MORPCVersion48 { - t.Fatalf("unexpected protocol gate: %v", metadata.RequiredProtocolVersion) + if metadata.RequiredProtocolVersion != defines.MORPCVersion61 { + t.Fatalf("unexpected 4.0.7 protocol gate: %v", metadata.RequiredProtocolVersion) } if len(tenantUpgEntries) != 9 { diff --git a/pkg/frontend/authenticate.go b/pkg/frontend/authenticate.go index 91121d47689d8..81ff1b643a0a5 100644 --- a/pkg/frontend/authenticate.go +++ b/pkg/frontend/authenticate.go @@ -12295,10 +12295,6 @@ func persistUserDefinedFunction( if previousRevision == 0 || previousNamespace == 0 { return moerr.NewInvalidInputNoCtx("UNSUPPORTED_ROUTINE_VERSION: legacy Python definition requires DROP and CREATE") } - definition, err = normalizePythonFunctionDefinition(definition) - if err != nil { - return err - } candidate, err := function.DecodePythonRoutineBody(definition.body) if err != nil { return moerr.NewInvalidInputNoCtxf("UNSUPPORTED_ROUTINE_VERSION: Python definition rejected before publication: %v", err) @@ -12316,6 +12312,17 @@ func persistUserDefinedFunction( "UNSUPPORTED_ROUTINE_VERSION: Python REPLACE cannot change input or return descriptor; use DROP and CREATE", ) } + // Compare the immutable candidate signature with the active revision + // before validating the compatibility columns. A REPLACE that changes + // the descriptor must report the stable identity error even when its + // caller-supplied logical args/return fields are stale; otherwise the + // same invalid operation produces a different error depending on which + // catalog projection is checked first. No catalog write has happened + // at this point. + definition, err = normalizePythonFunctionDefinition(definition) + if err != nil { + return err + } } else if revisionCatalogReady { var readErr error previousRevision, previousNamespace, readErr = readFunctionRevisionHead(ctx, bh, *functionID) From 5df3bf6a8ec5aa27400d7c6f6d1d7a27a5f6eeb1 Mon Sep 17 00:00:00 2001 From: iamlinjunhong <1030420200@qq.com> Date: Tue, 15 Sep 2026 14:55:27 +0800 Subject: [PATCH 138/202] fix(frontend): reject incomplete Python identities --- pkg/frontend/authenticate.go | 13 +++++++++---- pkg/frontend/python_udf_catalog_test.go | 18 ++++++++++++++++++ 2 files changed, 27 insertions(+), 4 deletions(-) diff --git a/pkg/frontend/authenticate.go b/pkg/frontend/authenticate.go index 81ff1b643a0a5..06bff94ce5d4e 100644 --- a/pkg/frontend/authenticate.go +++ b/pkg/frontend/authenticate.go @@ -12150,12 +12150,17 @@ func matchUserDefinedFunctionCandidates( return nil, err } } - if strings.EqualFold(existingLanguage, string(tree.PYTHON)) && existingCanonical != "" { + if strings.EqualFold(existingLanguage, string(tree.PYTHON)) { + if columnCount < 5 || existingCanonical == "" { + return nil, moerr.NewInvalidInputNoCtxf( + "UNSUPPORTED_ROUTINE_VERSION: Python function %d has no exact input descriptor", + functionID, + ) + } isMatch = existingCanonical == exactArgTypes } else { - // A legacy/partially upgraded Python row is rejected as the - // same logical identity. It cannot be silently replaced with a - // different exact descriptor. + // SQL and Python share the historical logical namespace. The + // exact descriptor is only authoritative for a Python row. isMatch = existingLogical == logicalArgTypes } } else { diff --git a/pkg/frontend/python_udf_catalog_test.go b/pkg/frontend/python_udf_catalog_test.go index 9f2ac17cbd174..cde653e5b4936 100644 --- a/pkg/frontend/python_udf_catalog_test.go +++ b/pkg/frontend/python_udf_catalog_test.go @@ -993,6 +993,24 @@ func TestMatchUserDefinedFunctionCandidatesUsesExactPythonDescriptor(t *testing. require.Equal(t, []int64{51, 52, 53}, ids) } +func TestMatchUserDefinedFunctionCandidatesRejectsPythonRowWithoutExactDescriptor(t *testing.T) { + result := &MysqlResultSet{} + for _, name := range []string{"function_id", "args", "language", "arg_types", "canonical_input_descriptor"} { + column := &MysqlColumn{} + column.SetName(name) + if name == "function_id" { + column.SetColumnType(defines.MYSQL_TYPE_LONGLONG) + } else { + column.SetColumnType(defines.MYSQL_TYPE_VAR_STRING) + } + result.AddColumn(column) + } + result.AddRow([]interface{}{int64(54), `[{"name":"value","type":"int"}]`, "python", `["int"]`, ""}) + + _, err := matchUserDefinedFunctionCandidates(context.Background(), []ExecResult{result}, "python", `["int"]`, `{"types":[]}`) + require.ErrorContains(t, err, "has no exact input descriptor") +} + func TestMatchUserDefinedFunctionCandidatesRejectsSplitResultSets(t *testing.T) { result := emptyCatalogProbeResult(5) _, err := matchUserDefinedFunctionCandidates( From 30171c03e4e8f97a3f2f3aded6617c98e1081186 Mon Sep 17 00:00:00 2001 From: iamlinjunhong <1030420200@qq.com> Date: Tue, 15 Sep 2026 14:57:28 +0800 Subject: [PATCH 139/202] fix(udf): cancel exchange before releasing admission --- pkg/udf/python/gateway.go | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/pkg/udf/python/gateway.go b/pkg/udf/python/gateway.go index 4c529cef3fd7d..8dd8169199b77 100644 --- a/pkg/udf/python/gateway.go +++ b/pkg/udf/python/gateway.go @@ -609,6 +609,12 @@ func (g *Gateway) Execute(ctx context.Context, invocation *udf.Invocation, resul openAttempted := false defer func() { reason := admissionReason(streamCtx, openAttempted, err) + // Cancel the Flight exchange before returning the local K slot and + // terminal-ledger ownership. On an input/send/receive failure the + // worker may still be running; releasing admission first would let a + // replacement invocation start while the old handler has not yet seen + // its cancellation. + cancel() if cleanupErr := admission.finish(openAttempted, reason); err == nil && cleanupErr != nil { err = cleanupErr } From 731385af8c854d1780d60910de30cc94f14436cd Mon Sep 17 00:00:00 2001 From: iamlinjunhong <1030420200@qq.com> Date: Tue, 15 Sep 2026 15:55:21 +0800 Subject: [PATCH 140/202] fix(udf): keep native Flight readers on RPC thread --- pkg/udf/python/worker/test_worker.py | 60 +++++++++++++++++--- pkg/udf/python/worker/worker.py | 83 ++++++++++++++++++++-------- 2 files changed, 114 insertions(+), 29 deletions(-) diff --git a/pkg/udf/python/worker/test_worker.py b/pkg/udf/python/worker/test_worker.py index 037fb08d6156d..c37e5677d5db1 100644 --- a/pkg/udf/python/worker/test_worker.py +++ b/pkg/udf/python/worker/test_worker.py @@ -655,15 +655,32 @@ def test_worker_rejects_a_tuple_from_another_instance(self): with self.assertRaisesRegex(ValueError, "invalid worker lease epoch"): worker.RoutineFlightServer("grpc://127.0.0.1:0", lease_epoch=0) - @unittest.skipUnless(os.name == "posix", "Flight shutdown cancellation test") - def test_flight_shutdown_cancels_an_active_exchange(self): + @unittest.skipUnless(os.name == "posix", "Flight shutdown reader lifetime test") + def test_flight_shutdown_does_not_race_native_input_reader(self): + """A native Flight reader must finish before its RPC is destroyed. + + FlightServerBase.shutdown waits for active callbacks; it does not + cancel a client that still owns the input direction. Close that + direction first, then shut down the server and verify the exchange + has no leftover native reader or ledger owner. The real client-side + cancellation path is covered by the Gateway integration test. + """ entered = threading.Event() + reader_closed = threading.Event() + adapters = [] original_reader = worker._ExchangeInputReader class NotifyingReader(original_reader): def __init__(self, reader): entered.set() super().__init__(reader) + adapters.append(self) + + def close(self): + try: + return super().close() + finally: + reader_closed.set() server = worker.RoutineFlightServer("grpc://127.0.0.1:0") client = flight.FlightClient(("127.0.0.1", server.port)) @@ -716,6 +733,26 @@ def control(kind, **fields): options=flight.FlightCallOptions(timeout=5), ) self.assertTrue(entered.wait(2), "exchange did not reach its input reader") + self.assertEqual(1, len(adapters)) + self.assertIsNone( + adapters[0]._thread, + "native Flight reader must stay on the RPC callback thread", + ) + # FlightServerBase.shutdown does not cancel an active client + # RPC. Close the client channel first; this is the same + # transport cancellation used by Gateway.Close and lets the + # native reader finish on its owning callback thread. + try: + writer.close() + except Exception: + # EndInput is intentionally omitted here; the worker may + # report the protocol error while closing the stream. + pass + client.close() + self.assertTrue( + reader_closed.wait(2), + "client cancellation did not finish the native reader", + ) def shutdown(): try: @@ -2385,7 +2422,10 @@ def test_worker_death_reaps_handler_process_group(self): "import json, os, pathlib, subprocess, sys, time\n" "def f(ctx, x):\n" f" child = subprocess.Popen([sys.executable, '-c', 'import time; time.sleep(60)'], stdin=subprocess.DEVNULL, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)\n" - f" pathlib.Path({str(marker)!r}).write_text(json.dumps(dict(handler_pid=os.getpid(), child_pid=child.pid, pgid=os.getpgrp())))\n" + f" marker = pathlib.Path({str(marker)!r})\n" + " temporary = marker.with_suffix('.tmp')\n" + " temporary.write_text(json.dumps(dict(handler_pid=os.getpid(), child_pid=child.pid, pgid=os.getpgrp())))\n" + " os.replace(temporary, marker)\n" " time.sleep(60)\n" " return x\n" )!r}, @@ -2457,7 +2497,7 @@ def test_worker_death_reaps_descendant_after_handler_leader_exits(self): with tempfile.TemporaryDirectory(prefix="mo-udf-leader-exit-") as artifact: marker = pathlib.Path(artifact) / "handler-and-watchdog.json" relay_source = f''' -import importlib.util, json, pathlib, pyarrow as pa, sys, time +import importlib.util, json, os, pathlib, pyarrow as pa, sys, time worker_path = pathlib.Path({str(WORKER_PATH)!r}) spec = importlib.util.spec_from_file_location("relay_worker_leader_exit", worker_path) worker = importlib.util.module_from_spec(spec) @@ -2470,7 +2510,10 @@ def test_worker_death_reaps_descendant_after_handler_leader_exits(self): "import json, os, pathlib, subprocess, sys\n" "def f(ctx, x):\n" f" child = subprocess.Popen([sys.executable, '-c', 'import time; time.sleep(60)'])\n" - f" pathlib.Path({str(marker)!r}).write_text(json.dumps(dict(handler_pid=os.getpid(), child_pid=child.pid, pgid=os.getpgrp())))\n" + f" marker = pathlib.Path({str(marker)!r})\n" + " temporary = marker.with_suffix('.tmp')\n" + " temporary.write_text(json.dumps(dict(handler_pid=os.getpid(), child_pid=child.pid, pgid=os.getpgrp())))\n" + " os.replace(temporary, marker)\n" " os._exit(7)\n" )!r}, "handler": "f", "mode": worker.MODE_SCALAR, "null_policy": worker.NULL_CALL, @@ -2482,9 +2525,12 @@ def test_worker_death_reaps_descendant_after_handler_leader_exits(self): try: session.run(None, request, 60) except Exception: - payload = json.loads(pathlib.Path({str(marker)!r}).read_text()) + marker = pathlib.Path({str(marker)!r}) + payload = json.loads(marker.read_text()) payload["watchdog_pid"] = session._watchdog_process.pid - pathlib.Path({str(marker)!r}).write_text(json.dumps(payload)) + temporary = marker.with_suffix('.watchdog.tmp') + temporary.write_text(json.dumps(payload)) + os.replace(temporary, marker) time.sleep(60) ''' relay = subprocess.Popen( diff --git a/pkg/udf/python/worker/worker.py b/pkg/udf/python/worker/worker.py index a277b0c8bd087..266e4e89795eb 100644 --- a/pkg/udf/python/worker/worker.py +++ b/pkg/udf/python/worker/worker.py @@ -2276,15 +2276,22 @@ def is_cancelled(self) -> bool: class _ExchangeInputReader: - """Make a blocking Flight input read observable to cancellation. - - ``FlightStreamReader.read_chunk`` is a blocking native call. Polling the - Flight context around that call is insufficient: a cancelled RPC can - remain stuck in the native reader and therefore keep the invocation, its - handler, and its ledger entry alive. Keep at most one chunk in flight and - interrupt the native reader through its cancellation API when the server - context is cancelled. The reader thread is bounded by the admitted - exchange, and is joined before the exchange returns. + """Read Flight input without letting native reader ownership escape. + + The reader passed to a Flight server callback is a PyArrow C++ object whose + lifetime is owned by the RPC. It must be read by the callback thread: a + daemon thread that is still inside ``read_chunk`` when + ``FlightServerBase.shutdown`` destroys the RPC can dereference a freed + native reader and crash the worker. The Flight implementation itself + interrupts a direct ``read_chunk`` when the RPC is cancelled. A + ``FlightServerBase.shutdown`` waits for an open input RPC; it is not a + cancellation mechanism, so the owner must cancel the client exchange (or + terminate the worker process) before waiting for server shutdown. + + Some contract tests and non-native adapters expose an explicit + ``cancel``/``close`` operation but do not have Flight's RPC cancellation + semantics. Keep the bounded polling thread only for those cooperative + readers. Never use it for a reader from PyArrow's native Flight module. """ _POLL_SECONDS = 0.05 @@ -2293,12 +2300,31 @@ def __init__(self, reader): self._reader = reader self._stop = threading.Event() self._items = queue.Queue(maxsize=1) - self._thread = threading.Thread( - target=self._read_loop, - name="matrixone-udf-flight-reader", - daemon=True, - ) - self._thread.start() + self._thread = None + self._native_cancel = self._find_cancel(reader) + if not self._is_native_flight_reader(reader) and self._native_cancel is not None: + self._thread = threading.Thread( + target=self._read_loop, + name="matrixone-udf-flight-reader", + daemon=True, + ) + self._thread.start() + + @staticmethod + def _is_native_flight_reader(reader): + # PyArrow exposes the server-side reader as a private extension type + # (currently MetadataRecordBatchReader). The module check is kept at + # this narrow boundary so the execution path does not depend on a + # particular private class name across supported PyArrow releases. + return type(reader).__module__.startswith("pyarrow._flight") + + @staticmethod + def _find_cancel(reader): + cancel = getattr(reader, "cancel", None) + if callable(cancel): + return cancel + close = getattr(reader, "close", None) + return close if callable(close) else None def _publish(self, kind, value=None): item = (kind, value) @@ -2323,18 +2349,29 @@ def _read_loop(self): self._publish("error", exc) def _cancel_native_reader(self): - cancel = getattr(self._reader, "cancel", None) - if not callable(cancel): - cancel = getattr(self._reader, "close", None) - if callable(cancel): + if self._native_cancel is not None: try: - cancel() + self._native_cancel() except Exception: - # The RPC is already being torn down. The join below still - # verifies whether the native read actually returned. + # The RPC may already be tearing down. The join below still + # verifies whether a cooperative reader actually returned. pass def next(self, context): + if self._thread is None: + if _context_is_cancelled(context): + self.cancel() + raise TimeoutError("DEADLINE_EXCEEDED: input stream cancelled") + try: + chunk = self._reader.read_chunk() + except StopIteration: + if _context_is_cancelled(context): + raise TimeoutError("DEADLINE_EXCEEDED: input stream cancelled") + raise + if _context_is_cancelled(context): + self.cancel() + raise TimeoutError("DEADLINE_EXCEEDED: input stream cancelled") + return chunk while True: if _context_is_cancelled(context): self.cancel() @@ -2355,6 +2392,8 @@ def cancel(self): def close(self): self.cancel() + if self._thread is None: + return self._thread.join(timeout=1.0) if self._thread.is_alive(): raise ValueError( From 88ad52287a090f1387e7503366790aaf4e3ef3a0 Mon Sep 17 00:00:00 2001 From: iamlinjunhong <1030420200@qq.com> Date: Tue, 15 Sep 2026 16:05:29 +0800 Subject: [PATCH 141/202] fix(udf): distinguish expected worker shutdown --- pkg/udf/python/supervisor.go | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/pkg/udf/python/supervisor.go b/pkg/udf/python/supervisor.go index 815433db35be7..b16df8411e752 100644 --- a/pkg/udf/python/supervisor.go +++ b/pkg/udf/python/supervisor.go @@ -84,6 +84,7 @@ func (s *Supervisor) Start() error { func (s *Supervisor) wait(cmd *exec.Cmd, done chan struct{}) { err := cmd.Wait() + unplanned := false s.mu.Lock() if s.cmd == cmd { // Keep the terminal state transition and Done close under the same lock. @@ -93,10 +94,14 @@ func (s *Supervisor) wait(cmd *exec.Cmd, done chan struct{}) { // worker. s.cmd = nil s.lastErr = err + // Close marks the supervisor as closing before killing the process + // group. Keep the exit error available through Err(), but do not report + // an intentional SIGKILL as an unexpected worker failure. + unplanned = !s.closing } close(done) s.mu.Unlock() - if err != nil { + if err != nil && unplanned { logutil.Errorf("Python UDF worker exited: %v", err) } } From 0fc7d42df10bf6254df476272cd1e6bc1a3cc291 Mon Sep 17 00:00:00 2001 From: iamlinjunhong <1030420200@qq.com> Date: Tue, 15 Sep 2026 16:13:00 +0800 Subject: [PATCH 142/202] fix(udf): complete handler response frame writes --- pkg/udf/python/worker/test_worker.py | 20 ++++++++++++++++++++ pkg/udf/python/worker/worker.py | 25 +++++++++++++++++++++++-- 2 files changed, 43 insertions(+), 2 deletions(-) diff --git a/pkg/udf/python/worker/test_worker.py b/pkg/udf/python/worker/test_worker.py index c37e5677d5db1..eedb77337082f 100644 --- a/pkg/udf/python/worker/test_worker.py +++ b/pkg/udf/python/worker/test_worker.py @@ -1745,6 +1745,26 @@ def test_handler_response_frame_parts_keep_one_wire_frame(self): ) self.assertEqual(struct.pack(">Q", 4) + b"\x01abc", stream.getvalue()) + def test_handler_response_frame_parts_handle_short_writes(self): + class ShortWriter: + def __init__(self): + self.data = bytearray() + + def write(self, value): + value = bytes(value) + count = max(1, len(value) // 2) + self.data.extend(value[:count]) + return count + + def flush(self): + return None + + stream = ShortWriter() + worker._write_execution_frame_parts(stream, (b"\x01", b"abcdef")) + self.assertEqual( + struct.pack(">Q", 7) + b"\x01abcdef", bytes(stream.data) + ) + def test_handler_process_accepts_schema_free_record_batch_messages(self): descriptor = {"type_id": worker.INT64, "offset_width": 32} schema = worker._schema_from_descriptors([descriptor], "arg") diff --git a/pkg/udf/python/worker/worker.py b/pkg/udf/python/worker/worker.py index 266e4e89795eb..300b3e54877da 100644 --- a/pkg/udf/python/worker/worker.py +++ b/pkg/udf/python/worker/worker.py @@ -2020,14 +2020,35 @@ def _read_exact(stream, size: int) -> bytearray: return result +def _write_all(stream, data) -> None: + """Write one frame component completely to a byte stream. + + The handler response channel is a pipe-backed ``FileIO`` object. A + blocking write normally transfers all bytes, but the file API still + permits a short write (for example when a signal interrupts a large + transfer). Treating that count as success would publish a truncated + length-prefixed frame and desynchronize every later response. + """ + view = memoryview(data) + try: + offset = 0 + while offset < len(view): + written = stream.write(view[offset:]) + if not isinstance(written, int) or written <= 0 or written > len(view) - offset: + raise OSError("execution frame stream made no valid progress") + offset += written + finally: + view.release() + + def _write_execution_frame_parts(stream, parts: Iterable[bytes]) -> None: parts = tuple(parts) payload_size = sum(len(part) for part in parts) if payload_size > MAX_EXECUTION_FRAME_BYTES: raise ValueError("RESOURCE_EXHAUSTED: execution frame is too large") - stream.write(struct.pack(">Q", payload_size)) + _write_all(stream, struct.pack(">Q", payload_size)) for part in parts: - stream.write(part) + _write_all(stream, part) stream.flush() From 51f2f62a00a92b42fb195366c58c2f59859b69bb Mon Sep 17 00:00:00 2001 From: iamlinjunhong <1030420200@qq.com> Date: Tue, 15 Sep 2026 16:15:41 +0800 Subject: [PATCH 143/202] fix(udf): validate handler binding before loading --- pkg/udf/python/worker/test_worker.py | 16 ++++++++++++++++ pkg/udf/python/worker/worker.py | 5 +++++ 2 files changed, 21 insertions(+) diff --git a/pkg/udf/python/worker/test_worker.py b/pkg/udf/python/worker/test_worker.py index eedb77337082f..01099334f9761 100644 --- a/pkg/udf/python/worker/test_worker.py +++ b/pkg/udf/python/worker/test_worker.py @@ -785,6 +785,22 @@ def test_inline_contract_rejects_external_handler_import(self): with self.assertRaisesRegex(ValueError, "immutable artifact catalog"): worker._load_handler("def add(ctx, value): return value", "module:add") + def test_handler_load_revalidates_static_binding_before_exec(self): + with tempfile.TemporaryDirectory(prefix="mo-udf-handler-load-") as directory: + marker = pathlib.Path(directory) / "executed" + source = ( + "from pathlib import Path\n" + f"Path({str(marker)!r}).write_text('executed')\n" + "def f(ctx, value): return value\n" + "f = 1\n" + ) + with self.assertRaisesRegex(ValueError, "not a module-level function"): + worker._load_handler(source, "f") + self.assertFalse( + marker.exists(), + "execution must reject invalid handler binding before running module code", + ) + def test_go_python_type_descriptor_fixtures(self): fixture_path = WORKER_PATH.parent.parent / "testdata" / "type_descriptors.json" fixtures = json.loads(fixture_path.read_text()) diff --git a/pkg/udf/python/worker/worker.py b/pkg/udf/python/worker/worker.py index 300b3e54877da..94cb90e2d1f76 100644 --- a/pkg/udf/python/worker/worker.py +++ b/pkg/udf/python/worker/worker.py @@ -1535,6 +1535,11 @@ def _load_handler(source: str, handler: str): raise ValueError( "UNSUPPORTED_ROUTINE_VERSION: Python external handler import requires an immutable artifact catalog" ) + # CREATE validates the source without executing it, but execution may be + # reached through a direct Flight request or a stale/malformed caller. + # Reapply the static binding contract before exec so invalid module-level + # rebinding cannot run top-level user code before being rejected. + _validate_definition_syntax({"source": source, "handler": handler}) namespace: Dict[str, Any] = {"__name__": "__matrixone_routine__"} exec(compile(source, "", "exec"), namespace, namespace) function = namespace.get(handler) From 01618e62efe3c6d2b4d9ec3c3ed7878ccc161914 Mon Sep 17 00:00:00 2001 From: iamlinjunhong <1030420200@qq.com> Date: Tue, 15 Sep 2026 16:19:00 +0800 Subject: [PATCH 144/202] fix(udf): kill handler groups before reaping leaders --- pkg/udf/python/worker/test_worker.py | 16 ++++++++++++++ pkg/udf/python/worker/worker.py | 31 ++++++++++++++++++---------- 2 files changed, 36 insertions(+), 11 deletions(-) diff --git a/pkg/udf/python/worker/test_worker.py b/pkg/udf/python/worker/test_worker.py index 01099334f9761..53bf45c749004 100644 --- a/pkg/udf/python/worker/test_worker.py +++ b/pkg/udf/python/worker/test_worker.py @@ -2020,6 +2020,22 @@ def test_process_cleanup_falls_back_to_owned_leader_on_group_permission_error(se process.kill.assert_called_once_with() process.wait.assert_called_once_with(timeout=1.0) + @unittest.skipUnless(os.name == "posix", "process-group cleanup after leader exit") + def test_process_cleanup_kills_group_before_reaping_exited_leader(self): + process = mock.Mock(pid=1234) + process.poll.return_value = 7 + process.wait.return_value = 7 + + with mock.patch.object(worker.os, "killpg", side_effect=[None, None]) as killpg: + worker._kill_execution_process(process) + + self.assertEqual( + [mock.call(1234, 0), mock.call(1234, worker.signal.SIGKILL)], + killpg.call_args_list, + ) + process.kill.assert_not_called() + process.wait.assert_called_once_with(timeout=1.0) + def test_handler_close_retains_process_for_a_later_cleanup_retry(self): session = object.__new__(worker._HandlerProcessSession) session._slots = threading.BoundedSemaphore(1) diff --git a/pkg/udf/python/worker/worker.py b/pkg/udf/python/worker/worker.py index 94cb90e2d1f76..7c290d73346e1 100644 --- a/pkg/udf/python/worker/worker.py +++ b/pkg/udf/python/worker/worker.py @@ -2426,14 +2426,16 @@ def close(self): "RESOURCE_EXHAUSTED: Flight input reader did not stop after cancellation" ) def _kill_execution_process(process: subprocess.Popen) -> None: - # The leader can already have exited while one of its descendants keeps - # the execution alive. The session closes the worker-owned liveness pipe - # before calling this helper, so the watchdog owns that leader-exited - # case. Do not call killpg with a reaped PID: macOS may have already - # reused the process-group id and returns EPERM (or could address an - # unrelated group). An un-reaped leader is still killed as a group. + # Check the process group before reaping the leader. A handler can exit + # while a descendant keeps the group alive; polling first would reap the + # leader and skip killpg, leaving that descendant behind until the + # asynchronous watchdog happens to run. Before process.poll() reaps the + # leader, its PID is still owned by this session and the group identity + # cannot have been reused. if os.name == "posix": - if process.poll() is None: + group_alive = _execution_group_alive(process) + leader_alive = process.poll() is None + if group_alive: try: os.killpg(process.pid, signal.SIGKILL) except ProcessLookupError: @@ -2444,10 +2446,17 @@ def _kill_execution_process(process: subprocess.Popen) -> None: # Kill the Popen child directly so the handler slot is not # stranded; the parent-liveness watchdog remains responsible # for descendants when the group cannot be addressed here. - try: - process.kill() - except ProcessLookupError: - pass + if leader_alive: + try: + process.kill() + except ProcessLookupError: + pass + else: + raise + elif leader_alive: + # The group disappeared in the small race between the liveness + # probe and poll(); still make sure the owned leader is stopped. + process.kill() elif process.poll() is None: process.kill() try: From 2bc4185d598edf82f3bf800ce7bb6162073938a6 Mon Sep 17 00:00:00 2001 From: iamlinjunhong <1030420200@qq.com> Date: Tue, 15 Sep 2026 16:20:28 +0800 Subject: [PATCH 145/202] fix(udf): reject stale result acknowledgements --- pkg/udf/python/worker/test_worker.py | 10 ++++++++++ pkg/udf/python/worker/worker.py | 18 ++++++++++++++---- 2 files changed, 24 insertions(+), 4 deletions(-) diff --git a/pkg/udf/python/worker/test_worker.py b/pkg/udf/python/worker/test_worker.py index 53bf45c749004..5dd82d43f60c2 100644 --- a/pkg/udf/python/worker/test_worker.py +++ b/pkg/udf/python/worker/test_worker.py @@ -3730,6 +3730,16 @@ def test_cancellation_after_accepted_finish_ack_keeps_success_idempotent(self): state.ack_finish("finish-race") self.assertEqual(worker._TERMINAL_SUCCESS, state.freeze_terminal()) + def test_frozen_success_rejects_stale_result_ack(self): + state = worker._InvocationState({}, 128) + state.last_result = 2 + state.mark_finish_sent("finish-stale-result") + state.ack_finish("finish-stale-result") + self.assertEqual(worker._TERMINAL_SUCCESS, state.freeze_terminal()) + with self.assertRaisesRegex(ValueError, "completed result"): + state.ack_result(1) + state.ack_result(2) + def test_cancelled_terminal_is_frozen_and_rejects_late_ack(self): server = worker.RoutineFlightServer("grpc://127.0.0.1:0") key = (1, "statement", "cancel-group", 1, "cancelled", 1) diff --git a/pkg/udf/python/worker/worker.py b/pkg/udf/python/worker/worker.py index 7c290d73346e1..fd79b97aace0a 100644 --- a/pkg/udf/python/worker/worker.py +++ b/pkg/udf/python/worker/worker.py @@ -3087,10 +3087,20 @@ def ack_result(self, sequence: int) -> None: with self.condition: if self.cancelled: raise ValueError("PROTOCOL: invocation terminal outcome is CANCELLED") - if self.terminal_outcome is not None and self.terminal_outcome != _TERMINAL_SUCCESS: - raise ValueError( - f"PROTOCOL: invocation terminal outcome is {self.terminal_outcome}" - ) + if self.terminal_outcome is not None: + if self.terminal_outcome != _TERMINAL_SUCCESS: + raise ValueError( + f"PROTOCOL: invocation terminal outcome is {self.terminal_outcome}" + ) + # An action can retain a state reference while cleanup moves + # that state to the terminal ledger. Once SUCCESS is frozen, + # only the final result sequence is an idempotent confirmation; + # accepting an earlier cumulative ACK would bypass the + # terminal ledger's exact-fence rule. + if sequence != self.last_result: + raise ValueError( + "PROTOCOL: terminal result ACK does not match the completed result" + ) if sequence <= 0 or sequence < self.acked_result or sequence > self.last_result: raise ValueError("PROTOCOL: result ACK is outside the received range") self.acked_result = sequence From fdd6467aa3e83da277852e9fef584d30952989f5 Mon Sep 17 00:00:00 2001 From: iamlinjunhong <1030420200@qq.com> Date: Tue, 15 Sep 2026 17:54:41 +0800 Subject: [PATCH 146/202] fix(udf): close Docker contract gaps --- pkg/frontend/authenticate.go | 2 +- pkg/frontend/python_udf_catalog_test.go | 31 +++++++++++++++++++ pkg/sql/plan/function/udf.go | 12 +++++++ pkg/sql/plan/function/udf_test.go | 6 ++++ pkg/udf/python/worker/Dockerfile | 18 +++++++++++ .../cases/udf_python/udf_python.result | 14 +++++++++ 6 files changed, 82 insertions(+), 1 deletion(-) diff --git a/pkg/frontend/authenticate.go b/pkg/frontend/authenticate.go index 06bff94ce5d4e..b70f590e66a70 100644 --- a/pkg/frontend/authenticate.go +++ b/pkg/frontend/authenticate.go @@ -12221,7 +12221,7 @@ func normalizePythonFunctionDefinition(definition userDefinedFunctionDefinition) } expectedLogicalTypes := make([]string, len(body.ArgTypes)) for index, descriptor := range body.ArgTypes { - expectedLogicalTypes[index] = strings.ToLower(descriptor.Type().Oid.String()) + expectedLogicalTypes[index] = function.PythonCatalogTypeName(descriptor.Type()) } expectedLogicalArgTypes, err := userDefinedFunctionArgumentTypes(expectedLogicalTypes) if err != nil { diff --git a/pkg/frontend/python_udf_catalog_test.go b/pkg/frontend/python_udf_catalog_test.go index cde653e5b4936..118a96b2ce759 100644 --- a/pkg/frontend/python_udf_catalog_test.go +++ b/pkg/frontend/python_udf_catalog_test.go @@ -314,6 +314,37 @@ func TestNormalizePythonDefinitionDerivesLogicalSignatureFromTypedBody(t *testin require.Equal(t, `["int"]`, normalized.argTypes) } +func TestNormalizePythonDefinitionAcceptsDecimalLogicalAlias(t *testing.T) { + descriptor, err := function.NewPythonTypeDescriptor(types.New(types.T_decimal128, 38, 10)) + require.NoError(t, err) + source := "def f(ctx, value): return value" + environment, err := udf.PythonEnvironmentDigest() + require.NoError(t, err) + body, err := json.Marshal(function.PythonRoutineBody{ + DefinitionSchemaVersion: udf.PythonDefinitionSchemaVersion, + Handler: "f", + Source: source, + Mode: "SCALAR", + NullPolicy: udf.NullCallHandler, + ABIContract: udf.PythonABIContract, + AdapterVersion: udf.PythonAdapterVersion, + ArtifactDigest: udf.PythonInlineArtifactDigest("f", source), + EnvironmentDigest: environment, + SDKVersion: udf.PythonSDKVersion, + ArgTypes: []function.PythonTypeDescriptor{descriptor}, + ReturnType: &descriptor, + }) + require.NoError(t, err) + + normalized, err := normalizePythonFunctionDefinition(userDefinedFunctionDefinition{ + args: `[{"name":"value","type":"decimal"}]`, + retType: "decimal", + body: string(body), + }) + require.NoError(t, err) + require.Equal(t, `["decimal"]`, normalized.argTypes) +} + func TestPersistPythonCreateIdentityLookupIncludesLanguage(t *testing.T) { definition := userDefinedFunctionDefinition{ name: "python_identity", diff --git a/pkg/sql/plan/function/udf.go b/pkg/sql/plan/function/udf.go index 7a79b9d41476a..5e993968aca08 100644 --- a/pkg/sql/plan/function/udf.go +++ b/pkg/sql/plan/function/udf.go @@ -539,6 +539,18 @@ func pythonCatalogTypeNameMatches(name string, oid types.T) bool { return name == strings.ToLower(oid.String()) } +// PythonCatalogTypeName returns the logical type name stored in the shared +// catalog signature columns for a Python descriptor. The exact width and +// scale remain in the typed descriptor; the historical logical namespace +// intentionally represents both supported decimal physical widths as +// "decimal". +func PythonCatalogTypeName(typ types.Type) string { + if typ.Oid == types.T_decimal64 || typ.Oid == types.T_decimal128 { + return "decimal" + } + return strings.ToLower(typ.Oid.String()) +} + func validatePythonTypeDescriptor(descriptor PythonTypeDescriptor) error { typ := descriptor.Type() canonical, err := NewPythonTypeDescriptor(typ) diff --git a/pkg/sql/plan/function/udf_test.go b/pkg/sql/plan/function/udf_test.go index 6015e1a3fd12d..f7e969cbb8c80 100644 --- a/pkg/sql/plan/function/udf_test.go +++ b/pkg/sql/plan/function/udf_test.go @@ -104,6 +104,12 @@ func TestPythonUdfArgTypeCastKeepsDeclaredDescriptorForSameOID(t *testing.T) { require.Nil(t, PythonUdfArgTypeCast([]types.Type{source}, nil)) } +func TestPythonCatalogTypeNamePreservesLogicalDecimalAlias(t *testing.T) { + require.Equal(t, "decimal", PythonCatalogTypeName(types.New(types.T_decimal64, 18, 2))) + require.Equal(t, "decimal", PythonCatalogTypeName(types.New(types.T_decimal128, 38, 10))) + require.Equal(t, "bigint", PythonCatalogTypeName(types.T_int64.ToType())) +} + func TestPythonCatalogSignatureRejectsLogicalDescriptorDrift(t *testing.T) { argument, err := NewPythonTypeDescriptor(types.T_int64.ToType()) require.NoError(t, err) diff --git a/pkg/udf/python/worker/Dockerfile b/pkg/udf/python/worker/Dockerfile index 0a3aab23067dc..f2403bec94263 100644 --- a/pkg/udf/python/worker/Dockerfile +++ b/pkg/udf/python/worker/Dockerfile @@ -2,8 +2,21 @@ # docker build -t mo-python-udf-worker -f ./pkg/udf/python/worker/Dockerfile ./pkg/udf/python/worker/ # docker run -d --network host mo-python-udf-worker +# Keep the worker's IANA database identical to the MatrixOne runtime image. +# The Python and Go base images are allowed to differ in language/runtime +# versions, but an IANA version mismatch would make the capability handshake +# reject the worker or, worse, let an IANA timezone resolve differently on +# the two sides of the boundary. +FROM matrixorigin/ubuntu:22.04 AS matrixone-tzdata + +RUN apt-get update \ + && DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends tzdata \ + && rm -rf /var/lib/apt/lists/* + FROM python:3.12-slim-bookworm +COPY --from=matrixone-tzdata /usr/share/zoneinfo/ /usr/share/zoneinfo/ + RUN useradd -m -s /bin/bash mo WORKDIR /home/mo @@ -18,6 +31,11 @@ COPY requirements.txt worker.py watchdog.py ./ RUN pip install --no-cache-dir -r requirements.txt +# Fail the image build if the shared tzdata source was not copied into the +# worker image. The Go side performs the same version check at capability +# admission; this makes a broken worker image fail at build time as well. +RUN python -c 'from pathlib import Path; import worker; version = Path("/usr/share/zoneinfo/tzdata.zi").read_text(encoding="utf-8").splitlines()[0].removeprefix("# version ").strip(); assert version and version == worker.TIMEZONE_DATABASE_VERSION, (version, worker.TIMEZONE_DATABASE_VERSION)' + USER mo EXPOSE 50051 diff --git a/test/distributed/cases/udf_python/udf_python.result b/test/distributed/cases/udf_python/udf_python.result index 8534ba2ba2c2d..3f0984316c0bf 100644 --- a/test/distributed/cases/udf_python/udf_python.result +++ b/test/distributed/cases/udf_python/udf_python.result @@ -14,6 +14,20 @@ select case when v is null then 99 else python_bvt_add(v) end as guarded from in 2 𝄀 4 𝄀 99 +create function python_bvt_bad_syntax (x int) returns int language python as 'def python_bvt_bad_syntax(ctx, x) return x' handler 'python_bvt_bad_syntax'; +-- @regex("Python syntax error at line", true) +USER_CODE: Python syntax error at line 1, column 35: expected ':' +create function python_bvt_replace (x int) returns int language python as 'def python_bvt_replace(ctx, x): return x + 10' handler 'python_bvt_replace'; +select python_bvt_replace(1); +➤ python_bvt_replace(1)[4,32,0] 𝄀 +11 +create or replace function python_bvt_replace (x int) returns int language python as 'def python_bvt_replace(ctx, x) return x + 20' handler 'python_bvt_replace'; +-- @regex("Python syntax error at line", true) +USER_CODE: Python syntax error at line 1, column 32: expected ':' +select python_bvt_replace(1); +➤ python_bvt_replace(1)[4,32,0] 𝄀 +11 drop function python_bvt_add(int); +drop function python_bvt_replace(int); drop table input_values; drop database udf_python_bvt; From d6ed6ad0d1d55b18f2ee43aa39cc5025a561142a Mon Sep 17 00:00:00 2001 From: iamlinjunhong <1030420200@qq.com> Date: Tue, 15 Sep 2026 17:58:20 +0800 Subject: [PATCH 147/202] refactor(udf): centralize logical type mapping --- pkg/sql/plan/function/udf.go | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/pkg/sql/plan/function/udf.go b/pkg/sql/plan/function/udf.go index 5e993968aca08..ae19eb8c94de0 100644 --- a/pkg/sql/plan/function/udf.go +++ b/pkg/sql/plan/function/udf.go @@ -533,10 +533,7 @@ func (u *Udf) ValidatePythonCatalogSignature() error { func pythonCatalogTypeNameMatches(name string, oid types.T) bool { name = strings.ToLower(strings.TrimSpace(name)) - if name == "decimal" { - return oid == types.T_decimal64 || oid == types.T_decimal128 - } - return name == strings.ToLower(oid.String()) + return name == PythonCatalogTypeName(types.Type{Oid: oid}) } // PythonCatalogTypeName returns the logical type name stored in the shared From ddc57d898b40a618ac6c38699815f971b6321c87 Mon Sep 17 00:00:00 2001 From: iamlinjunhong <1030420200@qq.com> Date: Wed, 16 Sep 2026 10:50:13 +0800 Subject: [PATCH 148/202] fix(udf): preserve legacy python heads on restore --- pkg/frontend/python_udf_catalog_test.go | 42 ++++++++++++++++++++++++ pkg/frontend/snapshot_catalog_restore.go | 7 ++-- 2 files changed, 47 insertions(+), 2 deletions(-) diff --git a/pkg/frontend/python_udf_catalog_test.go b/pkg/frontend/python_udf_catalog_test.go index 118a96b2ce759..c2cd6c78d87ee 100644 --- a/pkg/frontend/python_udf_catalog_test.go +++ b/pkg/frontend/python_udf_catalog_test.go @@ -873,6 +873,48 @@ func TestValidateRestoredPythonCatalogHeadsBindsIdentityToActiveRevision(t *test ) } +func TestValidateRestoredLegacyPythonHeadIsPreservedButInert(t *testing.T) { + query := `select cast(f.function_id as char), cast(f.active_revision as char), cast(f.namespace_version as char), + cast(coalesce(r.revision, 0) as char), cast(coalesce(r.namespace_version, 0) as char), + f.canonical_input_descriptor, f.return_descriptor, + cast(f.signature_key_schema_version as char), f.signature_fingerprint, + coalesce(r.arg_types, ''), coalesce(r.rettype, ''), coalesce(r.body, ''), + lower(f.language), coalesce(r.definition_fingerprint, ''), + coalesce(r.security_type, ''), coalesce(f.security_type, '') + from mo_catalog.mo_user_defined_function f + left join mo_catalog.mo_function_revisions r + on r.function_id = f.function_id and r.revision = f.active_revision + and r.namespace_version = f.namespace_version + where lower(f.language) in ("python", "sql") + order by f.function_id;` + legacy := func(active, namespace string) *MysqlResultSet { + result := &MysqlResultSet{} + for index := 0; index < 16; index++ { + column := &MysqlColumn{} + column.SetName(fmt.Sprintf("column_%d", index)) + column.SetColumnType(defines.MYSQL_TYPE_VAR_STRING) + result.AddColumn(column) + } + result.AddRow([]interface{}{ + "46", active, namespace, "0", "0", "", "", "0", "", "", "", "", + string(tree.PYTHON), "", "INVOKER", "INVOKER", + }) + return result + } + + b := &backgroundExecTest{} + b.init() + b.sql2result[query] = legacy("0", "0") + require.NoError(t, validateRestoredFunctionCatalogHeads(context.Background(), b, context.Background())) + + b.init() + b.sql2result[query] = legacy("1", "0") + require.ErrorContains(t, + validateRestoredFunctionCatalogHeads(context.Background(), b, context.Background()), + "invalid namespace version", + ) +} + func TestValidateRestoredSharedCatalogHeadsChecksSQLRevision(t *testing.T) { args := `[ {"name":"value","type":"bigint"} ]` argTypes, err := userDefinedFunctionArgumentTypesFromJSON(args) diff --git a/pkg/frontend/snapshot_catalog_restore.go b/pkg/frontend/snapshot_catalog_restore.go index 1dc99c2310d4f..4777df108673c 100644 --- a/pkg/frontend/snapshot_catalog_restore.go +++ b/pkg/frontend/snapshot_catalog_restore.go @@ -334,9 +334,12 @@ func validateRestoredFunctionCatalogHeads( return fmt.Errorf("UNSUPPORTED_ROUTINE_VERSION: restored routine function %d has an invalid namespace version: %v", functionID, err) } language := strings.ToLower(row[12]) - if language == udf.LanguageSQL && activeRevision == 0 && namespaceVersion == 0 { + if (language == udf.LanguageSQL || language == udf.LanguagePython) && activeRevision == 0 && namespaceVersion == 0 { // Legacy SQL rows predate the shared revision table and retain their - // old execution path. A current SQL head is validated below. + // old execution path. Legacy Python demo rows are preserved as inert + // catalog data as well: the current Python resolver has no execution + // path for them and rejects them before user code, while DROP/CREATE + // can replace the row explicitly. A current head is validated below. continue } if activeRevision == 0 { From a10a0b7ae938010b374e97bb12302352a2e4597d Mon Sep 17 00:00:00 2001 From: iamlinjunhong <1030420200@qq.com> Date: Wed, 16 Sep 2026 10:54:20 +0800 Subject: [PATCH 149/202] fix(udf): enforce descriptor validity at result boundaries --- pkg/udf/python/arrow.go | 7 +++++++ pkg/udf/python/arrow_test.go | 16 ++++++++++++++++ pkg/udf/python/worker/test_worker.py | 10 ++++++++++ pkg/udf/python/worker/worker.py | 5 +++++ 4 files changed, 38 insertions(+) diff --git a/pkg/udf/python/arrow.go b/pkg/udf/python/arrow.go index 0185f4ec768b5..b3217761c53f7 100644 --- a/pkg/udf/python/arrow.go +++ b/pkg/udf/python/arrow.go @@ -1249,6 +1249,13 @@ func AppendArrowResult(descriptor TypeDescriptor, input arrow.Array, result vect if result == nil { return fmt.Errorf("python udf: missing result wrapper") } + // AppendArrowResult is also the public adapter boundary used by runtimes + // other than the Flight Gateway. Do not let an invalid manually-built + // descriptor become executable merely because its Arrow physical type can + // be constructed (for example DECIMAL64 precision 19). + if err := descriptor.Validate(); err != nil { + return err + } if err := validateArrayType(descriptor, input); err != nil { return err } diff --git a/pkg/udf/python/arrow_test.go b/pkg/udf/python/arrow_test.go index 4b1b3de7c1400..fecb17e389558 100644 --- a/pkg/udf/python/arrow_test.go +++ b/pkg/udf/python/arrow_test.go @@ -299,6 +299,22 @@ func TestAppendArrowResultValidatesEmptyArrayType(t *testing.T) { require.ErrorContains(t, AppendArrowResult(descriptor, values, result, mp), "Arrow type") } +func TestAppendArrowResultRejectsInvalidSemanticDescriptor(t *testing.T) { + mp := mpool.MustNewZeroNoFixed() + defer mpool.DeleteMPool(mp) + result := vector.NewFunctionResultWrapper(types.T_decimal64.ToType(), mp) + defer result.Free() + + decimalType := &arrow.Decimal128Type{Precision: 19, Scale: 0} + builder := array.NewDecimal128Builder(memory.NewGoAllocator(), decimalType) + builder.Append(decimal128.FromI64(1)) + values := builder.NewDecimal128Array() + defer values.Release() + + descriptor := TypeDescriptor{TypeID: int32(types.T_decimal64), Width: 19, OffsetWidth: 32} + require.ErrorContains(t, AppendArrowResult(descriptor, values, result, mp), "decimal precision") +} + func TestZeroArgumentRecordPreservesRows(t *testing.T) { record := array.NewRecordBatch(arrow.NewSchema(nil, nil), nil, 3) defer record.Release() diff --git a/pkg/udf/python/worker/test_worker.py b/pkg/udf/python/worker/test_worker.py index 5dd82d43f60c2..7986563889d87 100644 --- a/pkg/udf/python/worker/test_worker.py +++ b/pkg/udf/python/worker/test_worker.py @@ -1196,6 +1196,16 @@ def test_decimal_precision_and_vector_child_validity_are_checked(self): visible = valid.slice(1, 1) self.assertEqual(visible.to_pylist(), worker._output_array(visible, vector_descriptor, 1).to_pylist()) + def test_output_array_rejects_invalid_semantic_descriptor(self): + descriptor = { + "type_id": worker.DECIMAL64, + "width": 19, + "scale": 0, + "offset_width": 32, + } + with self.assertRaisesRegex(ValueError, "precision is outside"): + worker._output_array([__import__("decimal").Decimal("1")], descriptor, 1) + def test_descriptor_rejects_unused_domain_fields(self): invalid = [ {"type_id": worker.DATE, "width": 1, "offset_width": 32, "temporal_encoding": "sql_zero_struct"}, diff --git a/pkg/udf/python/worker/worker.py b/pkg/udf/python/worker/worker.py index fd79b97aace0a..047d2dc5fdb12 100644 --- a/pkg/udf/python/worker/worker.py +++ b/pkg/udf/python/worker/worker.py @@ -1749,6 +1749,11 @@ def _temporal_array(values: list, descriptor: Dict[str, Any]) -> pa.Array: def _output_array(values: Any, descriptor: Dict[str, Any], rows: int) -> pa.Array: + # Keep this helper safe when called directly by a runtime adapter or a + # conformance harness. The normal exchange path validates the Open payload + # first, but _arrow_type alone can construct a physical type for an + # invalid semantic descriptor (for example DECIMAL64 precision 19). + _canonical_descriptor(descriptor) if isinstance(values, pa.ChunkedArray): values = values.combine_chunks() if isinstance(values, pa.Array): From 4e084f858d2c3a114f2f0304df2887c04e9bf5b4 Mon Sep 17 00:00:00 2001 From: iamlinjunhong <1030420200@qq.com> Date: Wed, 16 Sep 2026 11:04:58 +0800 Subject: [PATCH 150/202] fix(udf): cancel gateway operations on close --- pkg/udf/python/gateway.go | 50 +++++++++++++++++++++++++------- pkg/udf/python/gateway_test.go | 53 ++++++++++++++++++++++++++++++++++ 2 files changed, 93 insertions(+), 10 deletions(-) diff --git a/pkg/udf/python/gateway.go b/pkg/udf/python/gateway.go index 8dd8169199b77..45ff234ec5a4c 100644 --- a/pkg/udf/python/gateway.go +++ b/pkg/udf/python/gateway.go @@ -64,6 +64,8 @@ type Gateway struct { conn *grpc.ClientConn flight flight.FlightServiceClient mu sync.Mutex + lifecycleCtx context.Context + lifecycleCancel context.CancelFunc capabilityMu sync.Mutex admissionMu sync.Mutex capabilityReady bool @@ -139,10 +141,13 @@ func newGateway(cfg ClientConfig, resolver ArtifactResolver, allowInlineSource b if err != nil { return nil, err } + lifecycleCtx, lifecycleCancel := context.WithCancel(context.Background()) return &Gateway{ cfg: cfg, artifactResolver: resolver, allowInlineSource: allowInlineSource, + lifecycleCtx: lifecycleCtx, + lifecycleCancel: lifecycleCancel, active: make(chan struct{}, cfg.MaxActiveInvocations), ledger: ledger, ledgerTTL: cfg.TerminalRecordTTL, @@ -168,6 +173,8 @@ func (g *Gateway) CheckLanguageReady(ctx context.Context, language string) error if !g.cfg.AllowUnisolated { return fmt.Errorf("RESOURCE_UNAVAILABLE: Python UDF runtime requires explicit unisolated opt-in") } + requestCtx, release := g.withLifecycleContext(ctx) + defer release() client, err := g.flightClient() if err != nil { return err @@ -175,7 +182,7 @@ func (g *Gateway) CheckLanguageReady(ctx context.Context, language string) error // Readiness is an external admission decision. Refresh the worker // capability record so a worker replacement at the same endpoint cannot // inherit a stale success from the previous process. - return g.ensureCapabilities(ctx, client, true) + return g.ensureCapabilities(requestCtx, client, true) } // ValidateDefinition asks the current worker to compile the exact artifact @@ -197,13 +204,11 @@ func (g *Gateway) ValidateDefinition(ctx context.Context, definition *udf.Routin if !g.cfg.AllowUnisolated { return fmt.Errorf("RESOURCE_UNAVAILABLE: Python UDF runtime requires explicit unisolated opt-in") } - if ctx == nil { - ctx = context.Background() - } - requestCtx := ctx + requestCtx, release := g.withLifecycleContext(ctx) + defer release() var cancel context.CancelFunc if g.cfg.RequestTimeout > 0 { - requestCtx, cancel = context.WithTimeout(ctx, g.cfg.RequestTimeout) + requestCtx, cancel = context.WithTimeout(requestCtx, g.cfg.RequestTimeout) defer cancel() } if definition == nil { @@ -415,6 +420,9 @@ func (g *Gateway) Close() error { g.mu.Unlock() return nil } + if g.lifecycleCancel != nil { + g.lifecycleCancel() + } // Serialize the close barrier with group admission. Once this lock is // released, no caller can pass the admission check after Close returns. g.admissionMu.Lock() @@ -433,6 +441,29 @@ func (g *Gateway) Close() error { return conn.Close() } +// withLifecycleContext combines a caller's cancellation with the Gateway +// lifetime. A resolver or a readiness action can run before a Flight stream +// exists, so closing the gRPC connection alone cannot interrupt it. The +// returned release function removes the AfterFunc callback and cancels the +// derived context when the operation completes. +func (g *Gateway) withLifecycleContext(parent context.Context) (context.Context, func()) { + if parent == nil { + parent = context.Background() + } + combined, cancel := context.WithCancel(parent) + g.mu.Lock() + lifecycle := g.lifecycleCtx + g.mu.Unlock() + if lifecycle == nil { + return combined, cancel + } + stop := context.AfterFunc(lifecycle, cancel) + return combined, func() { + stop() + cancel() + } +} + type openPayload struct { FunctionRef udf.FunctionRef `json:"function_ref"` Handler string `json:"handler"` @@ -475,10 +506,9 @@ func (g *Gateway) Execute(ctx context.Context, invocation *udf.Invocation, resul if !g.cfg.AllowUnisolated { return fmt.Errorf("RESOURCE_UNAVAILABLE: Python UDF runtime requires explicit unisolated opt-in") } - if ctx == nil { - ctx = context.Background() - } - streamCtx, cancel := context.WithTimeout(ctx, g.cfg.RequestTimeout) + requestCtx, release := g.withLifecycleContext(ctx) + defer release() + streamCtx, cancel := context.WithTimeout(requestCtx, g.cfg.RequestTimeout) defer cancel() if invocation == nil || result == nil || mp == nil { return fmt.Errorf("python udf: nil invocation, result, or memory pool") diff --git a/pkg/udf/python/gateway_test.go b/pkg/udf/python/gateway_test.go index 27102c5cf3e16..9803ec6838849 100644 --- a/pkg/udf/python/gateway_test.go +++ b/pkg/udf/python/gateway_test.go @@ -68,6 +68,16 @@ func (blockingArtifactResolver) Resolve(ctx context.Context, _ uint64, _, _ stri return "", ctx.Err() } +type closeBlockingArtifactResolver struct { + started chan struct{} +} + +func (r *closeBlockingArtifactResolver) Resolve(ctx context.Context, _ uint64, _, _ string) (string, error) { + close(r.started) + <-ctx.Done() + return "", ctx.Err() +} + func (r *recordingArtifactResolver) Resolve(_ context.Context, accountID uint64, handler, digest string) (string, error) { r.calls++ r.account = accountID @@ -117,6 +127,49 @@ func TestValidateDefinitionBoundsArtifactResolution(t *testing.T) { } } +func TestGatewayCloseCancelsDefinitionResolution(t *testing.T) { + invocation := validInvocation() + resolver := &closeBlockingArtifactResolver{started: make(chan struct{})} + gateway, err := NewGatewayWithArtifactStore(ClientConfig{ + Enabled: true, + AllowUnisolated: true, + ServerAddress: "127.0.0.1:1", + RequestTimeout: 30 * time.Second, + }, resolver) + require.NoError(t, err) + + definition := &udf.RoutineDefinition{ + Language: udf.LanguagePython, + AccountID: invocation.FunctionRef.AccountID, + Handler: invocation.Handler, + Args: append([]types.Type(nil), invocation.Args...), + ReturnType: invocation.ReturnType, + Mode: invocation.Mode, + NullPolicy: invocation.NullPolicy, + ABIContract: invocation.ABIContract, + AdapterVersion: invocation.AdapterVersion, + SDKVersion: invocation.SDKVersion, + DefinitionSchemaVersion: invocation.DefinitionSchemaVersion, + ArtifactDigest: invocation.ArtifactDigest, + EnvironmentDigest: invocation.EnvironmentDigest, + DefinitionFingerprint: invocation.DefinitionFingerprint, + } + done := make(chan error, 1) + go func() { done <- gateway.ValidateDefinition(context.Background(), definition) }() + select { + case <-resolver.started: + case <-time.After(time.Second): + require.FailNow(t, "artifact resolution did not start") + } + require.NoError(t, gateway.Close()) + select { + case err := <-done: + require.ErrorIs(t, err, context.Canceled) + case <-time.After(time.Second): + require.FailNow(t, "Gateway.Close did not cancel artifact resolution") + } +} + func (c *finishAckFlightClient) DoAction( ctx context.Context, action *flight.Action, From 6cbeb6f6fab60623c396924e1ae13f0ce4a045d0 Mon Sep 17 00:00:00 2001 From: iamlinjunhong <1030420200@qq.com> Date: Wed, 16 Sep 2026 11:08:55 +0800 Subject: [PATCH 151/202] fix(udf): reject incompatible vector descriptor casts --- pkg/sql/plan/base_binder.go | 5 +---- pkg/sql/plan/function/python_udf.go | 34 +++++++++++++++++++++++++++-- pkg/sql/plan/function/udf_test.go | 18 +++++++++++++++ 3 files changed, 51 insertions(+), 6 deletions(-) diff --git a/pkg/sql/plan/base_binder.go b/pkg/sql/plan/base_binder.go index 6149df76e5ffc..673ae016cb82e 100644 --- a/pkg/sql/plan/base_binder.go +++ b/pkg/sql/plan/base_binder.go @@ -4547,10 +4547,7 @@ func (b *baseBinder) bindPythonUdf(udf *function.Udf, astArgs []tree.Expr, depth required := udf.GetArgsType()[idx] actual := makeTypeByPlan2Expr(expr) if !actual.Eq(required) { - canCast, _ := function.UdfArgTypeMatch([]types.Type{actual}, []types.T{required.Oid}) - if actual.Oid == required.Oid { - canCast = true - } + canCast, _ := function.PythonUdfArgTypeMatch([]types.Type{actual}, []types.Type{required}) if !canCast { return nil, moerr.NewInvalidInputf(b.GetContext(), "Python routine argument %d has type %s, expected %s", idx+1, actual.String(), required.String()) } diff --git a/pkg/sql/plan/function/python_udf.go b/pkg/sql/plan/function/python_udf.go index 9f4b54554e78e..ddd7764d6a63f 100644 --- a/pkg/sql/plan/function/python_udf.go +++ b/pkg/sql/plan/function/python_udf.go @@ -39,8 +39,13 @@ func checkPythonUdf(overloads []overload, inputs []types.Type) checkResult { needCast := false for i := 0; i < size; i++ { if !pythonTypesEqual(receivedArgs[i], requiredArgs[i]) { - canCast, _ := fixedImplicitTypeCast(receivedArgs[i], requiredArgs[i].Oid) + canCast := false if receivedArgs[i].Oid == requiredArgs[i].Oid { + canCast = pythonSameOIDCanNormalize(receivedArgs[i], requiredArgs[i]) + } else { + canCast, _ = fixedImplicitTypeCast(receivedArgs[i], requiredArgs[i].Oid) + } + if canCast && receivedArgs[i].Oid == requiredArgs[i].Oid { // SQL's cast executor is also the type normalizer for Python's // frozen descriptor. In particular, DECIMAL scale and precision // must be normalized before Arrow encoding; comparing only OID @@ -89,12 +94,15 @@ func PythonUdfArgTypeMatch(from, to []types.Type) (bool, int) { if pythonTypesEqual(from[index], to[index]) { continue } - if from[index].Oid == to[index].Oid { + if pythonSameOIDCanNormalize(from[index], to[index]) { // A same-OID descriptor change is a SQL cast to the declared // width/scale. It is valid, but less specific than an exact match. cost++ continue } + if from[index].Oid == to[index].Oid { + return false, -1 + } canCast, castCost := fixedImplicitTypeCast(from[index], to[index].Oid) if !canCast { return false, -1 @@ -108,6 +116,25 @@ func PythonUdfArgTypeMatch(from, to []types.Type) (bool, int) { return true, cost } +// pythonSameOIDCanNormalize reports whether a descriptor change with the same +// SQL OID has a meaningful SQL cast. Fixed-size vector dimensions are part of +// the value shape: the existing array->array cast preserves bytes when the +// element OID is unchanged, so accepting VECF32(4) for VECF32(3) would publish +// a value with a descriptor that lies about its child count. Other supported +// same-OID metadata (decimal precision/scale, text width/charset, and temporal +// scale) is normalized by the ordinary SQL cast executor. +func pythonSameOIDCanNormalize(from, to types.Type) bool { + if from.Oid != to.Oid || pythonTypesEqual(from, to) { + return false + } + switch from.Oid { + case types.T_array_float32, types.T_array_float64: + return false + default: + return true + } +} + // PythonUdfArgTypeCast returns the exact descriptor that the Python handler // declared for every argument. The generic UDF cast helper only receives an // OID list, so it intentionally preserves a same-OID source type. That is @@ -125,6 +152,9 @@ func PythonUdfArgTypeCast(from, to []types.Type) []types.Type { castTypes[index] = from[index] continue } + if from[index].Oid == to[index].Oid && !pythonSameOIDCanNormalize(from[index], to[index]) { + return nil + } castTypes[index] = to[index] } return castTypes diff --git a/pkg/sql/plan/function/udf_test.go b/pkg/sql/plan/function/udf_test.go index f7e969cbb8c80..b15f995cc6207 100644 --- a/pkg/sql/plan/function/udf_test.go +++ b/pkg/sql/plan/function/udf_test.go @@ -95,6 +95,24 @@ func TestPythonUdfArgTypeMatchPrefersExactDescriptor(t *testing.T) { require.Less(t, exactCost, coerceCost) } +func TestPythonUdfArgTypeMatchRejectsVectorDimensionChange(t *testing.T) { + source := types.New(types.T_array_float32, 4, 0) + target := types.New(types.T_array_float32, 3, 0) + canMatch, cost := PythonUdfArgTypeMatch([]types.Type{source}, []types.Type{target}) + require.False(t, canMatch) + require.Equal(t, -1, cost) + + inputs := []types.Type{ + types.T_text.ToType(), + source, + target, + types.T_int64.ToType(), + } + result := checkPythonUdf(nil, inputs) + require.Equal(t, failedFunctionParametersWrong, result.status) + require.Nil(t, PythonUdfArgTypeCast([]types.Type{source}, []types.Type{target})) +} + func TestPythonUdfArgTypeCastKeepsDeclaredDescriptorForSameOID(t *testing.T) { source := types.New(types.T_decimal64, 18, 2) target := types.New(types.T_decimal64, 18, 6) From d22d9f898ec1726781f8dc9fb469d8a0d0a5a5df Mon Sep 17 00:00:00 2001 From: iamlinjunhong <1030420200@qq.com> Date: Wed, 16 Sep 2026 11:22:09 +0800 Subject: [PATCH 152/202] fix(udf): bound handler response buffering --- pkg/udf/python/worker/test_worker.py | 6 ++++++ pkg/udf/python/worker/worker.py | 26 +++++++++++++++++++++++--- 2 files changed, 29 insertions(+), 3 deletions(-) diff --git a/pkg/udf/python/worker/test_worker.py b/pkg/udf/python/worker/test_worker.py index 7986563889d87..b48b2333ed010 100644 --- a/pkg/udf/python/worker/test_worker.py +++ b/pkg/udf/python/worker/test_worker.py @@ -1764,6 +1764,12 @@ def test_handler_response_frame_separates_status_from_payload(self): with self.assertRaisesRegex(ValueError, "empty response"): session._take_response() + def test_handler_response_frame_obeys_batch_byte_limit(self): + session = object.__new__(worker._HandlerProcessSession) + session._response_buffer = bytearray(struct.pack(">Q", 5) + b"\x01abcd") + with self.assertRaisesRegex(ValueError, "execution response is too large"): + session._take_response(4) + def test_handler_response_frame_parts_keep_one_wire_frame(self): stream = io.BytesIO() worker._write_execution_frame_parts( diff --git a/pkg/udf/python/worker/worker.py b/pkg/udf/python/worker/worker.py index 047d2dc5fdb12..df3d63a551f1b 100644 --- a/pkg/udf/python/worker/worker.py +++ b/pkg/udf/python/worker/worker.py @@ -2727,11 +2727,11 @@ def should_rollover(self) -> bool: or time.monotonic() - self._burst_started >= DEFAULT_BURST_SECONDS ) - def _take_response(self) -> Optional[tuple[int, bytes]]: + def _take_response(self, max_payload_size: int = MAX_EXECUTION_FRAME_BYTES) -> Optional[tuple[int, bytes]]: if len(self._response_buffer) < 8: return None expected = struct.unpack(">Q", self._response_buffer[:8])[0] - if expected > MAX_EXECUTION_FRAME_BYTES: + if expected > max_payload_size or expected > MAX_EXECUTION_FRAME_BYTES: raise ValueError("RESOURCE_EXHAUSTED: execution response is too large") if len(self._response_buffer) < expected + 8: return None @@ -2761,6 +2761,24 @@ def run(self, context, request: Dict[str, Any], timeout_seconds: float) -> bytes request_parts, request_size = _encode_execution_request( request, compact=self._burst_batches > 0 ) + max_batch_bytes = request.get("max_batch_bytes") + if max_batch_bytes is None: + # Direct helper callers from older tests do not carry the full + # exchange contract. The real do_exchange path always supplies + # this field; retaining the global bound here keeps those helper + # calls diagnostic rather than silently weakening the real path. + response_payload_limit = MAX_EXECUTION_FRAME_BYTES + elif type(max_batch_bytes) is not int or max_batch_bytes <= 0: + raise ValueError("PROTOCOL: handler request has an invalid max_batch_bytes") + else: + # The response payload consists of one status byte followed by + # the schema-free Arrow message. The child checks the same + # max_batch_bytes before serializing, but the parent must bound + # its receive buffer independently in case a mismatched or + # malformed child writes an oversized frame. + response_payload_limit = min( + MAX_EXECUTION_FRAME_BYTES, max_batch_bytes + 1 + ) def check_budget() -> None: if _context_is_cancelled(context): @@ -2779,7 +2797,7 @@ def check_budget() -> None: remaining = deadline - time.monotonic() if remaining <= 0: raise TimeoutError("DEADLINE_EXCEEDED: handler execution timeout") - response = self._take_response() + response = self._take_response(response_payload_limit) if response is not None: break events = self._selector.select(min(remaining, 0.1)) @@ -2819,6 +2837,8 @@ def check_budget() -> None: continue if not chunk: raise ValueError("USER_CODE: handler process exited without a response") + if len(self._response_buffer) + len(chunk) > response_payload_limit + 8: + raise ValueError("RESOURCE_EXHAUSTED: execution response exceeds batch byte limit") self._response_buffer.extend(chunk) if self._process.poll() is not None: if self._process.returncode != 0: From c586b4f00cc69605da323895c7ea83a82653ce79 Mon Sep 17 00:00:00 2001 From: iamlinjunhong <1030420200@qq.com> Date: Thu, 17 Sep 2026 18:05:50 +0800 Subject: [PATCH 153/202] udf: add runtime status bridge and finish fencing --- pkg/cnservice/server_query.go | 46 + pkg/pb/query/query.pb.go | 2232 +++++++++++++++++++---- pkg/queryservice/client/query_client.go | 1 + pkg/udf/python/gateway.go | 129 +- pkg/udf/python/gateway_test.go | 61 + pkg/udf/runtime.go | 100 + proto/query.proto | 37 + 7 files changed, 2214 insertions(+), 392 deletions(-) diff --git a/pkg/cnservice/server_query.go b/pkg/cnservice/server_query.go index 5d233ef66f30a..91e17fa9d6e77 100644 --- a/pkg/cnservice/server_query.go +++ b/pkg/cnservice/server_query.go @@ -47,6 +47,7 @@ import ( "github.com/matrixorigin/matrixone/pkg/sql/plan/function/ctl" "github.com/matrixorigin/matrixone/pkg/taskservice" "github.com/matrixorigin/matrixone/pkg/txn/client" + "github.com/matrixorigin/matrixone/pkg/udf" "github.com/matrixorigin/matrixone/pkg/util/fault" "github.com/matrixorigin/matrixone/pkg/vm/engine" "github.com/matrixorigin/matrixone/pkg/vm/engine/disttae" @@ -178,6 +179,7 @@ func (s *service) initQueryCommandHandler() { s.addQueryCommandHandler(query.CmdMethod_ISCPDrainConsumer, s.handleISCPDrainConsumer) s.addQueryCommandHandler(query.CmdMethod_IcebergCacheInvalidate, s.handleIcebergCacheInvalidate) s.addQueryCommandHandler(query.CmdMethod_MongoDBClientRetire, s.handleMongoDBClientRetire) + s.addQueryCommandHandler(query.CmdMethod_GetPythonUdfStatus, s.handleGetPythonUdfStatus) } func (s *service) addQueryCommandHandler( @@ -671,6 +673,50 @@ func (s *service) handleGetPipelineInfo(ctx context.Context, req *query.Request, return nil } +// handleGetPythonUdfStatus exposes the CN-local runtime state to the Operator. +// The runtime provider owns the capability handshake and returns only a +// bounded summary; this handler never accepts source or creates invocation +// state. Returning a status payload for a disabled or mismatched runtime lets +// the Operator fail closed for Python while ordinary CN query traffic remains +// available. +func (s *service) handleGetPythonUdfStatus(ctx context.Context, _ *query.Request, resp *query.Response, _ *morpc.Buffer) error { + status := udf.RuntimeStatusSnapshot{ + Language: udf.LanguagePython, + ErrorClass: udf.RuntimeStatusUnavailable, + Reason: udf.RuntimeStatusReasonStatusUnavailable, + } + if provider, ok := s.udfService.(udf.RuntimeStatusProvider); ok { + status = provider.StatusSnapshot(ctx, udf.LanguagePython) + } + resp.GetPythonUdfStatus = query.GetPythonUdfStatusResponse{ + CNUUID: s.metadata.UUID, + Language: status.Language, + Enabled: status.Enabled, + AllowUnisolated: status.AllowUnisolated, + Ready: status.Ready, + ErrorClass: status.ErrorClass, + Reason: status.Reason, + ProtocolVersion: status.ProtocolVersion, + ABIContract: status.ABIContract, + AdapterVersion: status.AdapterVersion, + SDKVersion: status.SDKVersion, + DefinitionSchemaVersion: status.DefinitionSchemaVersion, + PlanContractVersion: status.PlanContractVersion, + TypeDescriptorContract: status.TypeDescriptorContract, + TimezoneDatabaseVersion: status.TimezoneDatabaseVersion, + WindowBatches: status.WindowBatches, + CumulativeAck: status.CumulativeAck, + MaxExecutionFrameBytes: status.MaxExecutionFrameBytes, + MaxHandlerProcesses: status.MaxHandlerProcesses, + MaxAccountHandlerProcesses: status.MaxAccountHandlerProcesses, + MaxOwnerHandlerProcesses: status.MaxOwnerHandlerProcesses, + LeaseEpoch: status.LeaseEpoch, + Modes: append([]string(nil), status.Modes...), + NullPolicies: append([]string(nil), status.NullPolicies...), + } + return nil +} + func (s *service) handleMigrateConnFrom( ctx context.Context, req *query.Request, resp *query.Response, _ *morpc.Buffer, ) error { diff --git a/pkg/pb/query/query.pb.go b/pkg/pb/query/query.pb.go index 14b14ebaf5e36..6d79753f0c695 100644 --- a/pkg/pb/query/query.pb.go +++ b/pkg/pb/query/query.pb.go @@ -111,6 +111,9 @@ const ( CmdMethod_MongoDBClientRetire CmdMethod = 39 // RefreshSessionAuth revalidates a cached backend against the current catalog. CmdMethod_RefreshSessionAuth CmdMethod = 40 + // GetPythonUdfStatus returns the bounded, read-only Python runtime status of + // the addressed CN. It is consumed by the Operator for same-Pod workers. + CmdMethod_GetPythonUdfStatus CmdMethod = 41 ) var CmdMethod_name = map[int32]string{ @@ -155,6 +158,7 @@ var CmdMethod_name = map[int32]string{ 38: "IcebergCacheInvalidate", 39: "MongoDBClientRetire", 40: "RefreshSessionAuth", + 41: "GetPythonUdfStatus", } var CmdMethod_value = map[string]int32{ @@ -199,6 +203,7 @@ var CmdMethod_value = map[string]int32{ "IcebergCacheInvalidate": 38, "MongoDBClientRetire": 39, "RefreshSessionAuth": 40, + "GetPythonUdfStatus": 41, } func (x CmdMethod) String() string { @@ -1021,6 +1026,8 @@ type Request struct { MongoDBClientRetireRequest MongoDBClientRetireRequest `protobuf:"bytes,41,opt,name=MongoDBClientRetireRequest,proto3" json:"MongoDBClientRetireRequest"` // RefreshSessionAuthRequest reauthenticates a cached backend for a new login. RefreshSessionAuthRequest *RefreshSessionAuthRequest `protobuf:"bytes,42,opt,name=RefreshSessionAuthRequest,proto3" json:"RefreshSessionAuthRequest,omitempty"` + // GetPythonUdfStatusRequest asks this CN for its local Python runtime state. + GetPythonUdfStatusRequest GetPythonUdfStatusRequest `protobuf:"bytes,43,opt,name=GetPythonUdfStatusRequest,proto3" json:"GetPythonUdfStatusRequest"` } func (m *Request) Reset() { *m = Request{} } @@ -1350,6 +1357,13 @@ func (m *Request) GetRefreshSessionAuthRequest() *RefreshSessionAuthRequest { return nil } +func (m *Request) GetGetPythonUdfStatusRequest() GetPythonUdfStatusRequest { + if m != nil { + return m.GetPythonUdfStatusRequest + } + return GetPythonUdfStatusRequest{} +} + // ShowProcessListResponse is the response of command ShowProcessList. type ShowProcessListResponse struct { Sessions []*status.Session `protobuf:"bytes,1,rep,name=Sessions,proto3" json:"Sessions,omitempty"` @@ -1454,6 +1468,8 @@ type Response struct { MongoDBClientRetireResponse MongoDBClientRetireResponse `protobuf:"bytes,42,opt,name=MongoDBClientRetireResponse,proto3" json:"MongoDBClientRetireResponse"` // RefreshSessionAuthResponse contains the current authentication string. RefreshSessionAuthResponse *RefreshSessionAuthResponse `protobuf:"bytes,43,opt,name=RefreshSessionAuthResponse,proto3" json:"RefreshSessionAuthResponse,omitempty"` + // GetPythonUdfStatus is a bounded summary of the CN-local Python contract. + GetPythonUdfStatus GetPythonUdfStatusResponse `protobuf:"bytes,44,opt,name=GetPythonUdfStatus,proto3" json:"GetPythonUdfStatus"` } func (m *Response) Reset() { *m = Response{} } @@ -1790,6 +1806,277 @@ func (m *Response) GetRefreshSessionAuthResponse() *RefreshSessionAuthResponse { return nil } +func (m *Response) GetGetPythonUdfStatus() GetPythonUdfStatusResponse { + if m != nil { + return m.GetPythonUdfStatus + } + return GetPythonUdfStatusResponse{} +} + +type GetPythonUdfStatusRequest struct { +} + +func (m *GetPythonUdfStatusRequest) Reset() { *m = GetPythonUdfStatusRequest{} } +func (m *GetPythonUdfStatusRequest) String() string { return proto.CompactTextString(m) } +func (*GetPythonUdfStatusRequest) ProtoMessage() {} +func (*GetPythonUdfStatusRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_5c6ac9b241082464, []int{19} +} +func (m *GetPythonUdfStatusRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *GetPythonUdfStatusRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_GetPythonUdfStatusRequest.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *GetPythonUdfStatusRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_GetPythonUdfStatusRequest.Merge(m, src) +} +func (m *GetPythonUdfStatusRequest) XXX_Size() int { + return m.ProtoSize() +} +func (m *GetPythonUdfStatusRequest) XXX_DiscardUnknown() { + xxx_messageInfo_GetPythonUdfStatusRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_GetPythonUdfStatusRequest proto.InternalMessageInfo + +type GetPythonUdfStatusResponse struct { + CNUUID string `protobuf:"bytes,1,opt,name=CNUUID,proto3" json:"CNUUID,omitempty"` + Language string `protobuf:"bytes,2,opt,name=Language,proto3" json:"Language,omitempty"` + Enabled bool `protobuf:"varint,3,opt,name=Enabled,proto3" json:"Enabled,omitempty"` + AllowUnisolated bool `protobuf:"varint,4,opt,name=AllowUnisolated,proto3" json:"AllowUnisolated,omitempty"` + Ready bool `protobuf:"varint,5,opt,name=Ready,proto3" json:"Ready,omitempty"` + ErrorClass string `protobuf:"bytes,6,opt,name=ErrorClass,proto3" json:"ErrorClass,omitempty"` + Reason string `protobuf:"bytes,7,opt,name=Reason,proto3" json:"Reason,omitempty"` + ProtocolVersion int32 `protobuf:"varint,8,opt,name=ProtocolVersion,proto3" json:"ProtocolVersion,omitempty"` + ABIContract string `protobuf:"bytes,9,opt,name=ABIContract,proto3" json:"ABIContract,omitempty"` + AdapterVersion string `protobuf:"bytes,10,opt,name=AdapterVersion,proto3" json:"AdapterVersion,omitempty"` + SDKVersion string `protobuf:"bytes,11,opt,name=SDKVersion,proto3" json:"SDKVersion,omitempty"` + DefinitionSchemaVersion int32 `protobuf:"varint,12,opt,name=DefinitionSchemaVersion,proto3" json:"DefinitionSchemaVersion,omitempty"` + PlanContractVersion int32 `protobuf:"varint,13,opt,name=PlanContractVersion,proto3" json:"PlanContractVersion,omitempty"` + TypeDescriptorContract string `protobuf:"bytes,14,opt,name=TypeDescriptorContract,proto3" json:"TypeDescriptorContract,omitempty"` + TimezoneDatabaseVersion string `protobuf:"bytes,15,opt,name=TimezoneDatabaseVersion,proto3" json:"TimezoneDatabaseVersion,omitempty"` + WindowBatches int32 `protobuf:"varint,16,opt,name=WindowBatches,proto3" json:"WindowBatches,omitempty"` + CumulativeAck bool `protobuf:"varint,17,opt,name=CumulativeAck,proto3" json:"CumulativeAck,omitempty"` + MaxExecutionFrameBytes int64 `protobuf:"varint,18,opt,name=MaxExecutionFrameBytes,proto3" json:"MaxExecutionFrameBytes,omitempty"` + MaxHandlerProcesses int32 `protobuf:"varint,19,opt,name=MaxHandlerProcesses,proto3" json:"MaxHandlerProcesses,omitempty"` + MaxAccountHandlerProcesses int32 `protobuf:"varint,20,opt,name=MaxAccountHandlerProcesses,proto3" json:"MaxAccountHandlerProcesses,omitempty"` + MaxOwnerHandlerProcesses int32 `protobuf:"varint,21,opt,name=MaxOwnerHandlerProcesses,proto3" json:"MaxOwnerHandlerProcesses,omitempty"` + LeaseEpoch uint64 `protobuf:"varint,22,opt,name=LeaseEpoch,proto3" json:"LeaseEpoch,omitempty"` + Modes []string `protobuf:"bytes,23,rep,name=Modes,proto3" json:"Modes,omitempty"` + NullPolicies []string `protobuf:"bytes,24,rep,name=NullPolicies,proto3" json:"NullPolicies,omitempty"` +} + +func (m *GetPythonUdfStatusResponse) Reset() { *m = GetPythonUdfStatusResponse{} } +func (m *GetPythonUdfStatusResponse) String() string { return proto.CompactTextString(m) } +func (*GetPythonUdfStatusResponse) ProtoMessage() {} +func (*GetPythonUdfStatusResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_5c6ac9b241082464, []int{20} +} +func (m *GetPythonUdfStatusResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *GetPythonUdfStatusResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_GetPythonUdfStatusResponse.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *GetPythonUdfStatusResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_GetPythonUdfStatusResponse.Merge(m, src) +} +func (m *GetPythonUdfStatusResponse) XXX_Size() int { + return m.ProtoSize() +} +func (m *GetPythonUdfStatusResponse) XXX_DiscardUnknown() { + xxx_messageInfo_GetPythonUdfStatusResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_GetPythonUdfStatusResponse proto.InternalMessageInfo + +func (m *GetPythonUdfStatusResponse) GetCNUUID() string { + if m != nil { + return m.CNUUID + } + return "" +} + +func (m *GetPythonUdfStatusResponse) GetLanguage() string { + if m != nil { + return m.Language + } + return "" +} + +func (m *GetPythonUdfStatusResponse) GetEnabled() bool { + if m != nil { + return m.Enabled + } + return false +} + +func (m *GetPythonUdfStatusResponse) GetAllowUnisolated() bool { + if m != nil { + return m.AllowUnisolated + } + return false +} + +func (m *GetPythonUdfStatusResponse) GetReady() bool { + if m != nil { + return m.Ready + } + return false +} + +func (m *GetPythonUdfStatusResponse) GetErrorClass() string { + if m != nil { + return m.ErrorClass + } + return "" +} + +func (m *GetPythonUdfStatusResponse) GetReason() string { + if m != nil { + return m.Reason + } + return "" +} + +func (m *GetPythonUdfStatusResponse) GetProtocolVersion() int32 { + if m != nil { + return m.ProtocolVersion + } + return 0 +} + +func (m *GetPythonUdfStatusResponse) GetABIContract() string { + if m != nil { + return m.ABIContract + } + return "" +} + +func (m *GetPythonUdfStatusResponse) GetAdapterVersion() string { + if m != nil { + return m.AdapterVersion + } + return "" +} + +func (m *GetPythonUdfStatusResponse) GetSDKVersion() string { + if m != nil { + return m.SDKVersion + } + return "" +} + +func (m *GetPythonUdfStatusResponse) GetDefinitionSchemaVersion() int32 { + if m != nil { + return m.DefinitionSchemaVersion + } + return 0 +} + +func (m *GetPythonUdfStatusResponse) GetPlanContractVersion() int32 { + if m != nil { + return m.PlanContractVersion + } + return 0 +} + +func (m *GetPythonUdfStatusResponse) GetTypeDescriptorContract() string { + if m != nil { + return m.TypeDescriptorContract + } + return "" +} + +func (m *GetPythonUdfStatusResponse) GetTimezoneDatabaseVersion() string { + if m != nil { + return m.TimezoneDatabaseVersion + } + return "" +} + +func (m *GetPythonUdfStatusResponse) GetWindowBatches() int32 { + if m != nil { + return m.WindowBatches + } + return 0 +} + +func (m *GetPythonUdfStatusResponse) GetCumulativeAck() bool { + if m != nil { + return m.CumulativeAck + } + return false +} + +func (m *GetPythonUdfStatusResponse) GetMaxExecutionFrameBytes() int64 { + if m != nil { + return m.MaxExecutionFrameBytes + } + return 0 +} + +func (m *GetPythonUdfStatusResponse) GetMaxHandlerProcesses() int32 { + if m != nil { + return m.MaxHandlerProcesses + } + return 0 +} + +func (m *GetPythonUdfStatusResponse) GetMaxAccountHandlerProcesses() int32 { + if m != nil { + return m.MaxAccountHandlerProcesses + } + return 0 +} + +func (m *GetPythonUdfStatusResponse) GetMaxOwnerHandlerProcesses() int32 { + if m != nil { + return m.MaxOwnerHandlerProcesses + } + return 0 +} + +func (m *GetPythonUdfStatusResponse) GetLeaseEpoch() uint64 { + if m != nil { + return m.LeaseEpoch + } + return 0 +} + +func (m *GetPythonUdfStatusResponse) GetModes() []string { + if m != nil { + return m.Modes + } + return nil +} + +func (m *GetPythonUdfStatusResponse) GetNullPolicies() []string { + if m != nil { + return m.NullPolicies + } + return nil +} + // AlterAccountRequest is the "alter account restricted" query request. type AlterAccountRequest struct { // Tenant is the tenant which to alter. @@ -1802,7 +2089,7 @@ func (m *AlterAccountRequest) Reset() { *m = AlterAccountRequest{} } func (m *AlterAccountRequest) String() string { return proto.CompactTextString(m) } func (*AlterAccountRequest) ProtoMessage() {} func (*AlterAccountRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_5c6ac9b241082464, []int{19} + return fileDescriptor_5c6ac9b241082464, []int{21} } func (m *AlterAccountRequest) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1854,7 +2141,7 @@ func (m *AlterAccountResponse) Reset() { *m = AlterAccountResponse{} } func (m *AlterAccountResponse) String() string { return proto.CompactTextString(m) } func (*AlterAccountResponse) ProtoMessage() {} func (*AlterAccountResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_5c6ac9b241082464, []int{20} + return fileDescriptor_5c6ac9b241082464, []int{22} } func (m *AlterAccountResponse) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1901,7 +2188,7 @@ func (m *KillConnRequest) Reset() { *m = KillConnRequest{} } func (m *KillConnRequest) String() string { return proto.CompactTextString(m) } func (*KillConnRequest) ProtoMessage() {} func (*KillConnRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_5c6ac9b241082464, []int{21} + return fileDescriptor_5c6ac9b241082464, []int{23} } func (m *KillConnRequest) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1953,7 +2240,7 @@ func (m *KillConnResponse) Reset() { *m = KillConnResponse{} } func (m *KillConnResponse) String() string { return proto.CompactTextString(m) } func (*KillConnResponse) ProtoMessage() {} func (*KillConnResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_5c6ac9b241082464, []int{22} + return fileDescriptor_5c6ac9b241082464, []int{24} } func (m *KillConnResponse) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1999,7 +2286,7 @@ func (m *CtlReaderRequest) Reset() { *m = CtlReaderRequest{} } func (m *CtlReaderRequest) String() string { return proto.CompactTextString(m) } func (*CtlReaderRequest) ProtoMessage() {} func (*CtlReaderRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_5c6ac9b241082464, []int{23} + return fileDescriptor_5c6ac9b241082464, []int{25} } func (m *CtlReaderRequest) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2057,7 +2344,7 @@ func (m *CtlReaderResponse) Reset() { *m = CtlReaderResponse{} } func (m *CtlReaderResponse) String() string { return proto.CompactTextString(m) } func (*CtlReaderResponse) ProtoMessage() {} func (*CtlReaderResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_5c6ac9b241082464, []int{24} + return fileDescriptor_5c6ac9b241082464, []int{26} } func (m *CtlReaderResponse) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2101,7 +2388,7 @@ func (m *CtlPrefetchOnSubscribedRequest) Reset() { *m = CtlPrefetchOnSub func (m *CtlPrefetchOnSubscribedRequest) String() string { return proto.CompactTextString(m) } func (*CtlPrefetchOnSubscribedRequest) ProtoMessage() {} func (*CtlPrefetchOnSubscribedRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_5c6ac9b241082464, []int{25} + return fileDescriptor_5c6ac9b241082464, []int{27} } func (m *CtlPrefetchOnSubscribedRequest) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2145,7 +2432,7 @@ func (m *CtlPrefetchOnSubscribedResponse) Reset() { *m = CtlPrefetchOnSu func (m *CtlPrefetchOnSubscribedResponse) String() string { return proto.CompactTextString(m) } func (*CtlPrefetchOnSubscribedResponse) ProtoMessage() {} func (*CtlPrefetchOnSubscribedResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_5c6ac9b241082464, []int{26} + return fileDescriptor_5c6ac9b241082464, []int{28} } func (m *CtlPrefetchOnSubscribedResponse) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2194,7 +2481,7 @@ func (m *ISCPDrainConsumerRequest) Reset() { *m = ISCPDrainConsumerReque func (m *ISCPDrainConsumerRequest) String() string { return proto.CompactTextString(m) } func (*ISCPDrainConsumerRequest) ProtoMessage() {} func (*ISCPDrainConsumerRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_5c6ac9b241082464, []int{27} + return fileDescriptor_5c6ac9b241082464, []int{29} } func (m *ISCPDrainConsumerRequest) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2273,7 +2560,7 @@ func (m *ISCPDrainConsumerResponse) Reset() { *m = ISCPDrainConsumerResp func (m *ISCPDrainConsumerResponse) String() string { return proto.CompactTextString(m) } func (*ISCPDrainConsumerResponse) ProtoMessage() {} func (*ISCPDrainConsumerResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_5c6ac9b241082464, []int{28} + return fileDescriptor_5c6ac9b241082464, []int{30} } func (m *ISCPDrainConsumerResponse) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2319,7 +2606,7 @@ func (m *TraceSpanRequest) Reset() { *m = TraceSpanRequest{} } func (m *TraceSpanRequest) String() string { return proto.CompactTextString(m) } func (*TraceSpanRequest) ProtoMessage() {} func (*TraceSpanRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_5c6ac9b241082464, []int{29} + return fileDescriptor_5c6ac9b241082464, []int{31} } func (m *TraceSpanRequest) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2377,7 +2664,7 @@ func (m *TraceSpanResponse) Reset() { *m = TraceSpanResponse{} } func (m *TraceSpanResponse) String() string { return proto.CompactTextString(m) } func (*TraceSpanResponse) ProtoMessage() {} func (*TraceSpanResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_5c6ac9b241082464, []int{30} + return fileDescriptor_5c6ac9b241082464, []int{32} } func (m *TraceSpanResponse) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2420,7 +2707,7 @@ func (m *GetLockInfoRequest) Reset() { *m = GetLockInfoRequest{} } func (m *GetLockInfoRequest) String() string { return proto.CompactTextString(m) } func (*GetLockInfoRequest) ProtoMessage() {} func (*GetLockInfoRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_5c6ac9b241082464, []int{31} + return fileDescriptor_5c6ac9b241082464, []int{33} } func (m *GetLockInfoRequest) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2462,7 +2749,7 @@ func (m *LockInfo) Reset() { *m = LockInfo{} } func (m *LockInfo) String() string { return proto.CompactTextString(m) } func (*LockInfo) ProtoMessage() {} func (*LockInfo) Descriptor() ([]byte, []int) { - return fileDescriptor_5c6ac9b241082464, []int{32} + return fileDescriptor_5c6ac9b241082464, []int{34} } func (m *LockInfo) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2542,7 +2829,7 @@ func (m *GetLockInfoResponse) Reset() { *m = GetLockInfoResponse{} } func (m *GetLockInfoResponse) String() string { return proto.CompactTextString(m) } func (*GetLockInfoResponse) ProtoMessage() {} func (*GetLockInfoResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_5c6ac9b241082464, []int{33} + return fileDescriptor_5c6ac9b241082464, []int{35} } func (m *GetLockInfoResponse) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2592,7 +2879,7 @@ func (m *GetTxnInfoRequest) Reset() { *m = GetTxnInfoRequest{} } func (m *GetTxnInfoRequest) String() string { return proto.CompactTextString(m) } func (*GetTxnInfoRequest) ProtoMessage() {} func (*GetTxnInfoRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_5c6ac9b241082464, []int{34} + return fileDescriptor_5c6ac9b241082464, []int{36} } func (m *GetTxnInfoRequest) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2638,7 +2925,7 @@ func (m *TxnLockInfo) Reset() { *m = TxnLockInfo{} } func (m *TxnLockInfo) String() string { return proto.CompactTextString(m) } func (*TxnLockInfo) ProtoMessage() {} func (*TxnLockInfo) Descriptor() ([]byte, []int) { - return fileDescriptor_5c6ac9b241082464, []int{35} + return fileDescriptor_5c6ac9b241082464, []int{37} } func (m *TxnLockInfo) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2705,7 +2992,7 @@ func (m *TxnInfo) Reset() { *m = TxnInfo{} } func (m *TxnInfo) String() string { return proto.CompactTextString(m) } func (*TxnInfo) ProtoMessage() {} func (*TxnInfo) Descriptor() ([]byte, []int) { - return fileDescriptor_5c6ac9b241082464, []int{36} + return fileDescriptor_5c6ac9b241082464, []int{38} } func (m *TxnInfo) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2778,7 +3065,7 @@ func (m *GetTxnInfoResponse) Reset() { *m = GetTxnInfoResponse{} } func (m *GetTxnInfoResponse) String() string { return proto.CompactTextString(m) } func (*GetTxnInfoResponse) ProtoMessage() {} func (*GetTxnInfoResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_5c6ac9b241082464, []int{37} + return fileDescriptor_5c6ac9b241082464, []int{39} } func (m *GetTxnInfoResponse) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2828,7 +3115,7 @@ func (m *GetCacheInfoRequest) Reset() { *m = GetCacheInfoRequest{} } func (m *GetCacheInfoRequest) String() string { return proto.CompactTextString(m) } func (*GetCacheInfoRequest) ProtoMessage() {} func (*GetCacheInfoRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_5c6ac9b241082464, []int{38} + return fileDescriptor_5c6ac9b241082464, []int{40} } func (m *GetCacheInfoRequest) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2876,7 +3163,7 @@ func (m *CacheInfo) Reset() { *m = CacheInfo{} } func (m *CacheInfo) String() string { return proto.CompactTextString(m) } func (*CacheInfo) ProtoMessage() {} func (*CacheInfo) Descriptor() ([]byte, []int) { - return fileDescriptor_5c6ac9b241082464, []int{39} + return fileDescriptor_5c6ac9b241082464, []int{41} } func (m *CacheInfo) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2955,7 +3242,7 @@ func (m *GetCacheInfoResponse) Reset() { *m = GetCacheInfoResponse{} } func (m *GetCacheInfoResponse) String() string { return proto.CompactTextString(m) } func (*GetCacheInfoResponse) ProtoMessage() {} func (*GetCacheInfoResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_5c6ac9b241082464, []int{40} + return fileDescriptor_5c6ac9b241082464, []int{42} } func (m *GetCacheInfoResponse) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -3001,7 +3288,7 @@ func (m *RemoveRemoteLockTableRequest) Reset() { *m = RemoveRemoteLockTa func (m *RemoveRemoteLockTableRequest) String() string { return proto.CompactTextString(m) } func (*RemoveRemoteLockTableRequest) ProtoMessage() {} func (*RemoveRemoteLockTableRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_5c6ac9b241082464, []int{41} + return fileDescriptor_5c6ac9b241082464, []int{43} } func (m *RemoveRemoteLockTableRequest) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -3059,7 +3346,7 @@ func (m *RemoveRemoteLockTableResponse) Reset() { *m = RemoveRemoteLockT func (m *RemoveRemoteLockTableResponse) String() string { return proto.CompactTextString(m) } func (*RemoveRemoteLockTableResponse) ProtoMessage() {} func (*RemoveRemoteLockTableResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_5c6ac9b241082464, []int{42} + return fileDescriptor_5c6ac9b241082464, []int{44} } func (m *RemoveRemoteLockTableResponse) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -3104,7 +3391,7 @@ func (m *GetLatestBindRequest) Reset() { *m = GetLatestBindRequest{} } func (m *GetLatestBindRequest) String() string { return proto.CompactTextString(m) } func (*GetLatestBindRequest) ProtoMessage() {} func (*GetLatestBindRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_5c6ac9b241082464, []int{43} + return fileDescriptor_5c6ac9b241082464, []int{45} } func (m *GetLatestBindRequest) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -3155,7 +3442,7 @@ func (m *GetLatestBindResponse) Reset() { *m = GetLatestBindResponse{} } func (m *GetLatestBindResponse) String() string { return proto.CompactTextString(m) } func (*GetLatestBindResponse) ProtoMessage() {} func (*GetLatestBindResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_5c6ac9b241082464, []int{44} + return fileDescriptor_5c6ac9b241082464, []int{46} } func (m *GetLatestBindResponse) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -3204,7 +3491,7 @@ func (m *UnsubscribeTableRequest) Reset() { *m = UnsubscribeTableRequest func (m *UnsubscribeTableRequest) String() string { return proto.CompactTextString(m) } func (*UnsubscribeTableRequest) ProtoMessage() {} func (*UnsubscribeTableRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_5c6ac9b241082464, []int{45} + return fileDescriptor_5c6ac9b241082464, []int{47} } func (m *UnsubscribeTableRequest) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -3256,7 +3543,7 @@ func (m *UnsubscribeTableResponse) Reset() { *m = UnsubscribeTableRespon func (m *UnsubscribeTableResponse) String() string { return proto.CompactTextString(m) } func (*UnsubscribeTableResponse) ProtoMessage() {} func (*UnsubscribeTableResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_5c6ac9b241082464, []int{46} + return fileDescriptor_5c6ac9b241082464, []int{48} } func (m *UnsubscribeTableResponse) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -3302,7 +3589,7 @@ func (m *CacheKey) Reset() { *m = CacheKey{} } func (m *CacheKey) String() string { return proto.CompactTextString(m) } func (*CacheKey) ProtoMessage() {} func (*CacheKey) Descriptor() ([]byte, []int) { - return fileDescriptor_5c6ac9b241082464, []int{47} + return fileDescriptor_5c6ac9b241082464, []int{49} } func (m *CacheKey) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -3360,7 +3647,7 @@ func (m *CacheKeys) Reset() { *m = CacheKeys{} } func (m *CacheKeys) String() string { return proto.CompactTextString(m) } func (*CacheKeys) ProtoMessage() {} func (*CacheKeys) Descriptor() ([]byte, []int) { - return fileDescriptor_5c6ac9b241082464, []int{48} + return fileDescriptor_5c6ac9b241082464, []int{50} } func (m *CacheKeys) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -3405,7 +3692,7 @@ func (m *RequestCacheKey) Reset() { *m = RequestCacheKey{} } func (m *RequestCacheKey) String() string { return proto.CompactTextString(m) } func (*RequestCacheKey) ProtoMessage() {} func (*RequestCacheKey) Descriptor() ([]byte, []int) { - return fileDescriptor_5c6ac9b241082464, []int{49} + return fileDescriptor_5c6ac9b241082464, []int{51} } func (m *RequestCacheKey) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -3458,7 +3745,7 @@ func (m *GetCacheDataRequest) Reset() { *m = GetCacheDataRequest{} } func (m *GetCacheDataRequest) String() string { return proto.CompactTextString(m) } func (*GetCacheDataRequest) ProtoMessage() {} func (*GetCacheDataRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_5c6ac9b241082464, []int{50} + return fileDescriptor_5c6ac9b241082464, []int{52} } func (m *GetCacheDataRequest) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -3505,7 +3792,7 @@ func (m *ResponseCacheData) Reset() { *m = ResponseCacheData{} } func (m *ResponseCacheData) String() string { return proto.CompactTextString(m) } func (*ResponseCacheData) ProtoMessage() {} func (*ResponseCacheData) Descriptor() ([]byte, []int) { - return fileDescriptor_5c6ac9b241082464, []int{51} + return fileDescriptor_5c6ac9b241082464, []int{53} } func (m *ResponseCacheData) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -3564,7 +3851,7 @@ func (m *GetCacheDataResponse) Reset() { *m = GetCacheDataResponse{} } func (m *GetCacheDataResponse) String() string { return proto.CompactTextString(m) } func (*GetCacheDataResponse) ProtoMessage() {} func (*GetCacheDataResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_5c6ac9b241082464, []int{52} + return fileDescriptor_5c6ac9b241082464, []int{54} } func (m *GetCacheDataResponse) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -3608,7 +3895,7 @@ func (m *GetStatsInfoRequest) Reset() { *m = GetStatsInfoRequest{} } func (m *GetStatsInfoRequest) String() string { return proto.CompactTextString(m) } func (*GetStatsInfoRequest) ProtoMessage() {} func (*GetStatsInfoRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_5c6ac9b241082464, []int{53} + return fileDescriptor_5c6ac9b241082464, []int{55} } func (m *GetStatsInfoRequest) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -3652,7 +3939,7 @@ func (m *GetStatsInfoResponse) Reset() { *m = GetStatsInfoResponse{} } func (m *GetStatsInfoResponse) String() string { return proto.CompactTextString(m) } func (*GetStatsInfoResponse) ProtoMessage() {} func (*GetStatsInfoResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_5c6ac9b241082464, []int{54} + return fileDescriptor_5c6ac9b241082464, []int{56} } func (m *GetStatsInfoResponse) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -3698,7 +3985,7 @@ func (m *PrepareStmt) Reset() { *m = PrepareStmt{} } func (m *PrepareStmt) String() string { return proto.CompactTextString(m) } func (*PrepareStmt) ProtoMessage() {} func (*PrepareStmt) Descriptor() ([]byte, []int) { - return fileDescriptor_5c6ac9b241082464, []int{55} + return fileDescriptor_5c6ac9b241082464, []int{57} } func (m *PrepareStmt) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -3758,7 +4045,7 @@ func (m *MigrateTempTable) Reset() { *m = MigrateTempTable{} } func (m *MigrateTempTable) String() string { return proto.CompactTextString(m) } func (*MigrateTempTable) ProtoMessage() {} func (*MigrateTempTable) Descriptor() ([]byte, []int) { - return fileDescriptor_5c6ac9b241082464, []int{56} + return fileDescriptor_5c6ac9b241082464, []int{58} } func (m *MigrateTempTable) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -3823,7 +4110,7 @@ func (m *MigrateConnFromRequest) Reset() { *m = MigrateConnFromRequest{} func (m *MigrateConnFromRequest) String() string { return proto.CompactTextString(m) } func (*MigrateConnFromRequest) ProtoMessage() {} func (*MigrateConnFromRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_5c6ac9b241082464, []int{57} + return fileDescriptor_5c6ac9b241082464, []int{59} } func (m *MigrateConnFromRequest) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -3921,7 +4208,7 @@ func (m *MigrateConnFromResponse) Reset() { *m = MigrateConnFromResponse func (m *MigrateConnFromResponse) String() string { return proto.CompactTextString(m) } func (*MigrateConnFromResponse) ProtoMessage() {} func (*MigrateConnFromResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_5c6ac9b241082464, []int{58} + return fileDescriptor_5c6ac9b241082464, []int{60} } func (m *MigrateConnFromResponse) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -4111,7 +4398,7 @@ func (m *MigrateConnToRequest) Reset() { *m = MigrateConnToRequest{} } func (m *MigrateConnToRequest) String() string { return proto.CompactTextString(m) } func (*MigrateConnToRequest) ProtoMessage() {} func (*MigrateConnToRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_5c6ac9b241082464, []int{59} + return fileDescriptor_5c6ac9b241082464, []int{61} } func (m *MigrateConnToRequest) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -4266,7 +4553,7 @@ func (m *MigrateUserDefinedVar) Reset() { *m = MigrateUserDefinedVar{} } func (m *MigrateUserDefinedVar) String() string { return proto.CompactTextString(m) } func (*MigrateUserDefinedVar) ProtoMessage() {} func (*MigrateUserDefinedVar) Descriptor() ([]byte, []int) { - return fileDescriptor_5c6ac9b241082464, []int{60} + return fileDescriptor_5c6ac9b241082464, []int{62} } func (m *MigrateUserDefinedVar) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -4354,7 +4641,7 @@ func (m *UserLevelLock) Reset() { *m = UserLevelLock{} } func (m *UserLevelLock) String() string { return proto.CompactTextString(m) } func (*UserLevelLock) ProtoMessage() {} func (*UserLevelLock) Descriptor() ([]byte, []int) { - return fileDescriptor_5c6ac9b241082464, []int{61} + return fileDescriptor_5c6ac9b241082464, []int{63} } func (m *UserLevelLock) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -4412,7 +4699,7 @@ func (m *MigrateConnToResponse) Reset() { *m = MigrateConnToResponse{} } func (m *MigrateConnToResponse) String() string { return proto.CompactTextString(m) } func (*MigrateConnToResponse) ProtoMessage() {} func (*MigrateConnToResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_5c6ac9b241082464, []int{62} + return fileDescriptor_5c6ac9b241082464, []int{64} } func (m *MigrateConnToResponse) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -4457,7 +4744,7 @@ func (m *ReloadAutoIncrementCacheRequest) Reset() { *m = ReloadAutoIncre func (m *ReloadAutoIncrementCacheRequest) String() string { return proto.CompactTextString(m) } func (*ReloadAutoIncrementCacheRequest) ProtoMessage() {} func (*ReloadAutoIncrementCacheRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_5c6ac9b241082464, []int{63} + return fileDescriptor_5c6ac9b241082464, []int{65} } func (m *ReloadAutoIncrementCacheRequest) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -4501,7 +4788,7 @@ func (m *ReloadAutoIncrementCacheResponse) Reset() { *m = ReloadAutoIncr func (m *ReloadAutoIncrementCacheResponse) String() string { return proto.CompactTextString(m) } func (*ReloadAutoIncrementCacheResponse) ProtoMessage() {} func (*ReloadAutoIncrementCacheResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_5c6ac9b241082464, []int{64} + return fileDescriptor_5c6ac9b241082464, []int{66} } func (m *ReloadAutoIncrementCacheResponse) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -4538,7 +4825,7 @@ func (m *GetReplicaCountRequest) Reset() { *m = GetReplicaCountRequest{} func (m *GetReplicaCountRequest) String() string { return proto.CompactTextString(m) } func (*GetReplicaCountRequest) ProtoMessage() {} func (*GetReplicaCountRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_5c6ac9b241082464, []int{65} + return fileDescriptor_5c6ac9b241082464, []int{67} } func (m *GetReplicaCountRequest) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -4582,7 +4869,7 @@ func (m *GetReplicaCountResponse) Reset() { *m = GetReplicaCountResponse func (m *GetReplicaCountResponse) String() string { return proto.CompactTextString(m) } func (*GetReplicaCountResponse) ProtoMessage() {} func (*GetReplicaCountResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_5c6ac9b241082464, []int{66} + return fileDescriptor_5c6ac9b241082464, []int{68} } func (m *GetReplicaCountResponse) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -4628,7 +4915,7 @@ func (m *ResetSessionRequest) Reset() { *m = ResetSessionRequest{} } func (m *ResetSessionRequest) String() string { return proto.CompactTextString(m) } func (*ResetSessionRequest) ProtoMessage() {} func (*ResetSessionRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_5c6ac9b241082464, []int{67} + return fileDescriptor_5c6ac9b241082464, []int{69} } func (m *ResetSessionRequest) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -4676,7 +4963,7 @@ func (m *ResetSessionResponse) Reset() { *m = ResetSessionResponse{} } func (m *ResetSessionResponse) String() string { return proto.CompactTextString(m) } func (*ResetSessionResponse) ProtoMessage() {} func (*ResetSessionResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_5c6ac9b241082464, []int{68} + return fileDescriptor_5c6ac9b241082464, []int{70} } func (m *ResetSessionResponse) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -4730,7 +5017,7 @@ func (m *GoMaxProcsRequest) Reset() { *m = GoMaxProcsRequest{} } func (m *GoMaxProcsRequest) String() string { return proto.CompactTextString(m) } func (*GoMaxProcsRequest) ProtoMessage() {} func (*GoMaxProcsRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_5c6ac9b241082464, []int{69} + return fileDescriptor_5c6ac9b241082464, []int{71} } func (m *GoMaxProcsRequest) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -4777,7 +5064,7 @@ func (m *GoMaxProcsResponse) Reset() { *m = GoMaxProcsResponse{} } func (m *GoMaxProcsResponse) String() string { return proto.CompactTextString(m) } func (*GoMaxProcsResponse) ProtoMessage() {} func (*GoMaxProcsResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_5c6ac9b241082464, []int{70} + return fileDescriptor_5c6ac9b241082464, []int{72} } func (m *GoMaxProcsResponse) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -4821,7 +5108,7 @@ func (m *GoMemLimitRequest) Reset() { *m = GoMemLimitRequest{} } func (m *GoMemLimitRequest) String() string { return proto.CompactTextString(m) } func (*GoMemLimitRequest) ProtoMessage() {} func (*GoMemLimitRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_5c6ac9b241082464, []int{71} + return fileDescriptor_5c6ac9b241082464, []int{73} } func (m *GoMemLimitRequest) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -4868,7 +5155,7 @@ func (m *GoMemLimitResponse) Reset() { *m = GoMemLimitResponse{} } func (m *GoMemLimitResponse) String() string { return proto.CompactTextString(m) } func (*GoMemLimitResponse) ProtoMessage() {} func (*GoMemLimitResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_5c6ac9b241082464, []int{72} + return fileDescriptor_5c6ac9b241082464, []int{74} } func (m *GoMemLimitResponse) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -4915,7 +5202,7 @@ func (m *FileServiceCacheRequest) Reset() { *m = FileServiceCacheRequest func (m *FileServiceCacheRequest) String() string { return proto.CompactTextString(m) } func (*FileServiceCacheRequest) ProtoMessage() {} func (*FileServiceCacheRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_5c6ac9b241082464, []int{73} + return fileDescriptor_5c6ac9b241082464, []int{75} } func (m *FileServiceCacheRequest) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -4969,7 +5256,7 @@ func (m *FileServiceCacheResponse) Reset() { *m = FileServiceCacheRespon func (m *FileServiceCacheResponse) String() string { return proto.CompactTextString(m) } func (*FileServiceCacheResponse) ProtoMessage() {} func (*FileServiceCacheResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_5c6ac9b241082464, []int{74} + return fileDescriptor_5c6ac9b241082464, []int{76} } func (m *FileServiceCacheResponse) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -5027,7 +5314,7 @@ func (m *FileServiceCacheEvictRequest) Reset() { *m = FileServiceCacheEv func (m *FileServiceCacheEvictRequest) String() string { return proto.CompactTextString(m) } func (*FileServiceCacheEvictRequest) ProtoMessage() {} func (*FileServiceCacheEvictRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_5c6ac9b241082464, []int{75} + return fileDescriptor_5c6ac9b241082464, []int{77} } func (m *FileServiceCacheEvictRequest) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -5073,7 +5360,7 @@ func (m *FileServiceCacheEvictResponse) Reset() { *m = FileServiceCacheE func (m *FileServiceCacheEvictResponse) String() string { return proto.CompactTextString(m) } func (*FileServiceCacheEvictResponse) ProtoMessage() {} func (*FileServiceCacheEvictResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_5c6ac9b241082464, []int{76} + return fileDescriptor_5c6ac9b241082464, []int{78} } func (m *FileServiceCacheEvictResponse) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -5131,7 +5418,7 @@ func (m *MetadataCacheRequest) Reset() { *m = MetadataCacheRequest{} } func (m *MetadataCacheRequest) String() string { return proto.CompactTextString(m) } func (*MetadataCacheRequest) ProtoMessage() {} func (*MetadataCacheRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_5c6ac9b241082464, []int{77} + return fileDescriptor_5c6ac9b241082464, []int{79} } func (m *MetadataCacheRequest) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -5175,7 +5462,7 @@ func (m *MetadataCacheResponse) Reset() { *m = MetadataCacheResponse{} } func (m *MetadataCacheResponse) String() string { return proto.CompactTextString(m) } func (*MetadataCacheResponse) ProtoMessage() {} func (*MetadataCacheResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_5c6ac9b241082464, []int{78} + return fileDescriptor_5c6ac9b241082464, []int{80} } func (m *MetadataCacheResponse) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -5225,7 +5512,7 @@ func (m *IcebergCacheInvalidateRequest) Reset() { *m = IcebergCacheInval func (m *IcebergCacheInvalidateRequest) String() string { return proto.CompactTextString(m) } func (*IcebergCacheInvalidateRequest) ProtoMessage() {} func (*IcebergCacheInvalidateRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_5c6ac9b241082464, []int{79} + return fileDescriptor_5c6ac9b241082464, []int{81} } func (m *IcebergCacheInvalidateRequest) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -5311,7 +5598,7 @@ func (m *IcebergCacheInvalidateResponse) Reset() { *m = IcebergCacheInva func (m *IcebergCacheInvalidateResponse) String() string { return proto.CompactTextString(m) } func (*IcebergCacheInvalidateResponse) ProtoMessage() {} func (*IcebergCacheInvalidateResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_5c6ac9b241082464, []int{80} + return fileDescriptor_5c6ac9b241082464, []int{82} } func (m *IcebergCacheInvalidateResponse) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -5355,7 +5642,7 @@ func (m *GoGCPercentRequest) Reset() { *m = GoGCPercentRequest{} } func (m *GoGCPercentRequest) String() string { return proto.CompactTextString(m) } func (*GoGCPercentRequest) ProtoMessage() {} func (*GoGCPercentRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_5c6ac9b241082464, []int{81} + return fileDescriptor_5c6ac9b241082464, []int{83} } func (m *GoGCPercentRequest) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -5402,7 +5689,7 @@ func (m *GoGCPercentResponse) Reset() { *m = GoGCPercentResponse{} } func (m *GoGCPercentResponse) String() string { return proto.CompactTextString(m) } func (*GoGCPercentResponse) ProtoMessage() {} func (*GoGCPercentResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_5c6ac9b241082464, []int{82} + return fileDescriptor_5c6ac9b241082464, []int{84} } func (m *GoGCPercentResponse) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -5447,7 +5734,7 @@ func (m *FaultInjectRequest) Reset() { *m = FaultInjectRequest{} } func (m *FaultInjectRequest) String() string { return proto.CompactTextString(m) } func (*FaultInjectRequest) ProtoMessage() {} func (*FaultInjectRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_5c6ac9b241082464, []int{83} + return fileDescriptor_5c6ac9b241082464, []int{85} } func (m *FaultInjectRequest) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -5498,7 +5785,7 @@ func (m *FaultInjectResponse) Reset() { *m = FaultInjectResponse{} } func (m *FaultInjectResponse) String() string { return proto.CompactTextString(m) } func (*FaultInjectResponse) ProtoMessage() {} func (*FaultInjectResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_5c6ac9b241082464, []int{84} + return fileDescriptor_5c6ac9b241082464, []int{86} } func (m *FaultInjectResponse) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -5542,7 +5829,7 @@ func (m *CtlMoTableStatsRequest) Reset() { *m = CtlMoTableStatsRequest{} func (m *CtlMoTableStatsRequest) String() string { return proto.CompactTextString(m) } func (*CtlMoTableStatsRequest) ProtoMessage() {} func (*CtlMoTableStatsRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_5c6ac9b241082464, []int{85} + return fileDescriptor_5c6ac9b241082464, []int{87} } func (m *CtlMoTableStatsRequest) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -5586,7 +5873,7 @@ func (m *CtlMoTableStatsResponse) Reset() { *m = CtlMoTableStatsResponse func (m *CtlMoTableStatsResponse) String() string { return proto.CompactTextString(m) } func (*CtlMoTableStatsResponse) ProtoMessage() {} func (*CtlMoTableStatsResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_5c6ac9b241082464, []int{86} + return fileDescriptor_5c6ac9b241082464, []int{88} } func (m *CtlMoTableStatsResponse) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -5631,7 +5918,7 @@ func (m *WorkspaceThresholdRequest) Reset() { *m = WorkspaceThresholdReq func (m *WorkspaceThresholdRequest) String() string { return proto.CompactTextString(m) } func (*WorkspaceThresholdRequest) ProtoMessage() {} func (*WorkspaceThresholdRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_5c6ac9b241082464, []int{87} + return fileDescriptor_5c6ac9b241082464, []int{89} } func (m *WorkspaceThresholdRequest) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -5683,7 +5970,7 @@ func (m *WorkspaceThresholdResponse) Reset() { *m = WorkspaceThresholdRe func (m *WorkspaceThresholdResponse) String() string { return proto.CompactTextString(m) } func (*WorkspaceThresholdResponse) ProtoMessage() {} func (*WorkspaceThresholdResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_5c6ac9b241082464, []int{88} + return fileDescriptor_5c6ac9b241082464, []int{90} } func (m *WorkspaceThresholdResponse) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -5735,7 +6022,7 @@ func (m *MinTimestampResponse) Reset() { *m = MinTimestampResponse{} } func (m *MinTimestampResponse) String() string { return proto.CompactTextString(m) } func (*MinTimestampResponse) ProtoMessage() {} func (*MinTimestampResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_5c6ac9b241082464, []int{89} + return fileDescriptor_5c6ac9b241082464, []int{91} } func (m *MinTimestampResponse) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -5783,7 +6070,7 @@ func (m *MongoDBClientRetireRequest) Reset() { *m = MongoDBClientRetireR func (m *MongoDBClientRetireRequest) String() string { return proto.CompactTextString(m) } func (*MongoDBClientRetireRequest) ProtoMessage() {} func (*MongoDBClientRetireRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_5c6ac9b241082464, []int{90} + return fileDescriptor_5c6ac9b241082464, []int{92} } func (m *MongoDBClientRetireRequest) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -5841,7 +6128,7 @@ func (m *MongoDBClientRetireResponse) Reset() { *m = MongoDBClientRetire func (m *MongoDBClientRetireResponse) String() string { return proto.CompactTextString(m) } func (*MongoDBClientRetireResponse) ProtoMessage() {} func (*MongoDBClientRetireResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_5c6ac9b241082464, []int{91} + return fileDescriptor_5c6ac9b241082464, []int{93} } func (m *MongoDBClientRetireResponse) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -5892,7 +6179,7 @@ func (m *MigrateSystemVariable) Reset() { *m = MigrateSystemVariable{} } func (m *MigrateSystemVariable) String() string { return proto.CompactTextString(m) } func (*MigrateSystemVariable) ProtoMessage() {} func (*MigrateSystemVariable) Descriptor() ([]byte, []int) { - return fileDescriptor_5c6ac9b241082464, []int{92} + return fileDescriptor_5c6ac9b241082464, []int{94} } func (m *MigrateSystemVariable) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -5964,7 +6251,7 @@ func (m *RefreshSessionAuthRequest) Reset() { *m = RefreshSessionAuthReq func (m *RefreshSessionAuthRequest) String() string { return proto.CompactTextString(m) } func (*RefreshSessionAuthRequest) ProtoMessage() {} func (*RefreshSessionAuthRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_5c6ac9b241082464, []int{93} + return fileDescriptor_5c6ac9b241082464, []int{95} } func (m *RefreshSessionAuthRequest) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -6056,7 +6343,7 @@ func (m *RefreshSessionAuthResponse) Reset() { *m = RefreshSessionAuthRe func (m *RefreshSessionAuthResponse) String() string { return proto.CompactTextString(m) } func (*RefreshSessionAuthResponse) ProtoMessage() {} func (*RefreshSessionAuthResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_5c6ac9b241082464, []int{94} + return fileDescriptor_5c6ac9b241082464, []int{96} } func (m *RefreshSessionAuthResponse) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -6136,6 +6423,8 @@ func init() { proto.RegisterType((*Request)(nil), "query.Request") proto.RegisterType((*ShowProcessListResponse)(nil), "query.ShowProcessListResponse") proto.RegisterType((*Response)(nil), "query.Response") + proto.RegisterType((*GetPythonUdfStatusRequest)(nil), "query.GetPythonUdfStatusRequest") + proto.RegisterType((*GetPythonUdfStatusResponse)(nil), "query.GetPythonUdfStatusResponse") proto.RegisterType((*AlterAccountRequest)(nil), "query.AlterAccountRequest") proto.RegisterType((*AlterAccountResponse)(nil), "query.AlterAccountResponse") proto.RegisterType((*KillConnRequest)(nil), "query.KillConnRequest") @@ -6217,298 +6506,327 @@ func init() { func init() { proto.RegisterFile("query.proto", fileDescriptor_5c6ac9b241082464) } var fileDescriptor_5c6ac9b241082464 = []byte{ - // 4651 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xb4, 0x5c, 0xdd, 0x6f, 0xdc, 0xc6, - 0x76, 0xf7, 0x4a, 0x2b, 0x69, 0xf7, 0x68, 0x25, 0x51, 0xa3, 0x2f, 0x5a, 0x96, 0x25, 0x99, 0x71, - 0x62, 0xd9, 0xbe, 0x91, 0x5c, 0xdf, 0xc4, 0xb9, 0x1f, 0x69, 0x11, 0x69, 0x65, 0xd9, 0x6b, 0x4b, - 0x96, 0x3c, 0x2b, 0xdb, 0xb9, 0xb9, 0x40, 0x00, 0x6a, 0x77, 0xb4, 0x62, 0xbc, 0x4b, 0x6e, 0x48, - 0xae, 0x23, 0x05, 0xe8, 0x3f, 0xd0, 0x87, 0xe2, 0x3e, 0xf5, 0xb9, 0x05, 0x0a, 0xf4, 0xb9, 0x40, - 0x1f, 0xfb, 0x07, 0x5c, 0xa0, 0x40, 0x71, 0x1f, 0x0a, 0xf4, 0x3e, 0xb5, 0x45, 0xf2, 0x27, 0x14, - 0xe8, 0x6b, 0x2f, 0xce, 0x70, 0xc8, 0xe5, 0x0c, 0x87, 0x94, 0x9d, 0xe4, 0xbe, 0x08, 0x9c, 0x33, - 0xe7, 0xfc, 0x66, 0x38, 0x1f, 0x87, 0xe7, 0x6b, 0x05, 0x93, 0x5f, 0x0f, 0x98, 0x7f, 0xb1, 0xd9, - 0xf7, 0xbd, 0xd0, 0x23, 0x63, 0xbc, 0xb1, 0x5c, 0x0b, 0x42, 0x3b, 0x1c, 0x04, 0x11, 0x71, 0x19, - 0xba, 0x5e, 0xeb, 0xb5, 0x78, 0xae, 0x86, 0xe7, 0xae, 0x78, 0x9c, 0x09, 0x9d, 0x1e, 0x0b, 0x42, - 0xbb, 0xd7, 0x8f, 0x09, 0x28, 0x15, 0x38, 0xee, 0xa9, 0x17, 0x0b, 0xf6, 0xbb, 0x76, 0xcc, 0xfd, - 0x61, 0xc7, 0x09, 0xcf, 0x06, 0x27, 0x9b, 0x2d, 0xaf, 0xb7, 0xd5, 0xf1, 0x3a, 0xde, 0x16, 0x27, - 0x9f, 0x0c, 0x4e, 0x79, 0x8b, 0x37, 0xf8, 0x93, 0x60, 0x5f, 0xeb, 0x78, 0x5e, 0xa7, 0xcb, 0x86, - 0x5c, 0xca, 0x60, 0xd6, 0x4d, 0xa8, 0x3d, 0xc7, 0xb9, 0x52, 0xf6, 0xf5, 0x80, 0x05, 0x21, 0x99, - 0x87, 0x31, 0xde, 0x36, 0x4b, 0xeb, 0xa5, 0x8d, 0x2a, 0x8d, 0x1a, 0xd6, 0x33, 0x58, 0x6c, 0x9e, - 0x79, 0xdf, 0x1c, 0xf9, 0x5e, 0x8b, 0x05, 0xc1, 0xbe, 0x13, 0x84, 0x31, 0xff, 0x22, 0x8c, 0x1f, - 0x33, 0xd7, 0x76, 0x43, 0x21, 0x20, 0x5a, 0x64, 0x05, 0xaa, 0xcd, 0x8b, 0x40, 0x74, 0x8d, 0xac, - 0x97, 0x36, 0x2a, 0x74, 0x48, 0xb0, 0x5e, 0xc1, 0x6c, 0xf3, 0xc2, 0x6d, 0xd5, 0xbd, 0x5e, 0xcf, - 0x49, 0xa0, 0x76, 0x60, 0x7a, 0xdf, 0x0e, 0x59, 0x10, 0x46, 0xe4, 0xe3, 0x26, 0x87, 0x9c, 0xbc, - 0x3f, 0xbf, 0x39, 0x9c, 0xf4, 0x71, 0xfc, 0xb4, 0x53, 0xfe, 0xfd, 0x7f, 0xad, 0x5d, 0xa1, 0x8a, - 0x84, 0xf5, 0x05, 0x90, 0x34, 0x70, 0xd0, 0xf7, 0xdc, 0x80, 0x91, 0x5d, 0x98, 0xa9, 0x0f, 0x7c, - 0x9f, 0xb9, 0xef, 0x02, 0xad, 0x8a, 0x58, 0x04, 0x8c, 0x47, 0x2c, 0x94, 0xe6, 0x6c, 0xfd, 0x06, - 0x66, 0x53, 0xb4, 0x9f, 0x74, 0xb8, 0x2d, 0x58, 0xa8, 0x7b, 0x3e, 0xdb, 0x1d, 0xf4, 0xfa, 0x75, - 0xcf, 0x3d, 0x75, 0x3a, 0xa9, 0x25, 0xdf, 0x6e, 0x85, 0x8e, 0xe7, 0xc6, 0x4b, 0x1e, 0xb5, 0x2c, - 0x13, 0x16, 0x55, 0x81, 0x68, 0x42, 0xd6, 0x35, 0xb8, 0xfa, 0x88, 0x85, 0x47, 0xb8, 0xe1, 0x2d, - 0xaf, 0xfb, 0x92, 0xf9, 0x81, 0xe3, 0xb9, 0xf1, 0x2b, 0x3c, 0x80, 0x65, 0x5d, 0xa7, 0x78, 0x17, - 0x13, 0x26, 0x04, 0x89, 0x8f, 0x36, 0x4a, 0xe3, 0xa6, 0xf5, 0x31, 0x5c, 0x6d, 0xe6, 0x81, 0x16, - 0x88, 0x3d, 0x80, 0xe5, 0xe6, 0x0f, 0x19, 0xee, 0x67, 0x30, 0x4d, 0x07, 0xee, 0xb1, 0x1d, 0xbc, - 0x8e, 0xc7, 0x58, 0x86, 0x0a, 0x36, 0xeb, 0x5e, 0x9b, 0x71, 0xe6, 0x31, 0x9a, 0xb4, 0xad, 0xdb, - 0x30, 0x93, 0x70, 0x0b, 0xe8, 0x45, 0x18, 0xa7, 0x2c, 0x18, 0x74, 0x93, 0x93, 0x1a, 0xb5, 0x70, - 0xd9, 0xf0, 0xfd, 0x9d, 0x3e, 0xeb, 0x3a, 0x2e, 0x6b, 0xb8, 0xa7, 0x5e, 0xbc, 0x32, 0x5b, 0xb0, - 0x94, 0xe9, 0x11, 0x60, 0xf3, 0x30, 0x56, 0xf7, 0x06, 0xe2, 0xd4, 0x8f, 0xd2, 0xa8, 0x61, 0xfd, - 0xdd, 0x32, 0x4c, 0xc4, 0xb3, 0x5b, 0x81, 0xaa, 0x78, 0x6c, 0xec, 0x72, 0xae, 0x32, 0x1d, 0x12, - 0xc8, 0x26, 0x54, 0xeb, 0xbd, 0xf6, 0x01, 0x0b, 0xcf, 0xbc, 0x36, 0xbf, 0x1e, 0xd3, 0xf7, 0x8d, - 0xcd, 0x48, 0x83, 0x24, 0x74, 0x3a, 0x64, 0x21, 0x9f, 0xc8, 0xd7, 0xd4, 0x1c, 0xe5, 0xe7, 0x69, - 0x4e, 0x88, 0xa4, 0xbb, 0xa8, 0x7c, 0x9f, 0x5f, 0xe4, 0xdd, 0x5c, 0xb3, 0xcc, 0x21, 0xae, 0x0b, - 0x08, 0x3d, 0x13, 0xcd, 0xbb, 0xf6, 0xfb, 0x30, 0xb7, 0xdd, 0x0d, 0x99, 0xbf, 0xdd, 0x6a, 0xe1, - 0x9b, 0xc7, 0x98, 0x63, 0x1c, 0x73, 0x59, 0x60, 0x6a, 0x38, 0xa8, 0x4e, 0x8c, 0x7c, 0x06, 0x33, - 0x4f, 0x9d, 0x6e, 0xb7, 0xee, 0xb9, 0xf1, 0x01, 0x32, 0xc7, 0x39, 0xd2, 0xa2, 0x40, 0x52, 0x7a, - 0xa9, 0xca, 0x4e, 0xea, 0x60, 0x1c, 0xfb, 0x76, 0x8b, 0x35, 0xfb, 0x76, 0x02, 0x31, 0xc1, 0x21, - 0x96, 0x04, 0x84, 0xda, 0x4d, 0x33, 0x02, 0xa4, 0x01, 0xe4, 0x11, 0x0b, 0xf7, 0xbd, 0xd6, 0xeb, - 0xd4, 0x29, 0x30, 0x2b, 0x1c, 0xe6, 0xaa, 0x80, 0xc9, 0x32, 0x50, 0x8d, 0x10, 0xd9, 0xe3, 0x7a, - 0xe1, 0xf8, 0xdc, 0x4d, 0x23, 0x55, 0x39, 0x92, 0x39, 0x44, 0x92, 0xfb, 0x69, 0x56, 0x04, 0xd7, - 0x19, 0xf5, 0x8b, 0xdd, 0x3a, 0x4b, 0x9f, 0x4c, 0x13, 0xa4, 0x75, 0xd6, 0x70, 0x50, 0x9d, 0x18, - 0xf9, 0x05, 0x40, 0xf3, 0xa2, 0xe5, 0x46, 0x2a, 0xc6, 0x9c, 0x94, 0xa6, 0x93, 0xd1, 0xc7, 0x34, - 0xc5, 0x4b, 0x3e, 0x86, 0x6a, 0xa2, 0xe7, 0xcc, 0x9a, 0xb4, 0xb0, 0xaa, 0x4e, 0xa4, 0x43, 0x4e, - 0x72, 0xc4, 0x57, 0x54, 0xb9, 0xec, 0xe6, 0x14, 0x97, 0x5f, 0x1f, 0xca, 0xeb, 0x95, 0x08, 0xd5, - 0xc8, 0x22, 0x62, 0x56, 0x7d, 0x98, 0xd3, 0x12, 0x62, 0x33, 0x1f, 0x31, 0xdb, 0x45, 0x76, 0x61, - 0x5a, 0x56, 0x9b, 0xe6, 0x0c, 0x47, 0x5b, 0x89, 0xef, 0xa3, 0x4e, 0x09, 0x53, 0x45, 0x86, 0x6c, - 0xc1, 0x84, 0x50, 0x38, 0xa6, 0xc1, 0xc5, 0x17, 0x84, 0xb8, 0xac, 0xb4, 0x68, 0xcc, 0x45, 0x7e, - 0x03, 0x0b, 0x94, 0xf5, 0xbc, 0x37, 0x0c, 0xff, 0x86, 0x0c, 0x0f, 0xd0, 0xb1, 0x7d, 0xd2, 0x65, - 0xe6, 0x2c, 0x17, 0x7f, 0x2f, 0x16, 0xd7, 0xf1, 0xc4, 0x60, 0x7a, 0x04, 0xb2, 0x0d, 0x53, 0x78, - 0x24, 0xf9, 0x97, 0x71, 0xc7, 0x71, 0xdb, 0x26, 0xe1, 0x90, 0xd7, 0x52, 0x47, 0x38, 0xe9, 0x8b, - 0xa1, 0x64, 0x09, 0xf2, 0x04, 0x8c, 0x17, 0x6e, 0x30, 0x38, 0x09, 0x5a, 0xbe, 0x73, 0xc2, 0xa2, - 0x89, 0xcd, 0x71, 0x94, 0x55, 0x81, 0xa2, 0x76, 0x27, 0xd7, 0x4a, 0xed, 0x48, 0x9f, 0xe1, 0x5d, - 0x3b, 0xb4, 0xe3, 0x33, 0x3c, 0xaf, 0x3d, 0xc3, 0x29, 0x0e, 0xaa, 0x13, 0x13, 0x68, 0xcd, 0xd0, - 0x0e, 0x83, 0xf4, 0x8d, 0x58, 0x50, 0xd1, 0x54, 0x0e, 0xaa, 0x13, 0x43, 0xf5, 0xa8, 0x57, 0xfe, - 0xe6, 0xa2, 0xa4, 0x1e, 0xf5, 0x4c, 0x34, 0x47, 0x18, 0x61, 0x0f, 0x9c, 0x8e, 0x6f, 0x87, 0x0c, - 0x95, 0xd4, 0x9e, 0xef, 0xf5, 0x62, 0xd8, 0x25, 0x09, 0x56, 0xcf, 0x44, 0x73, 0x84, 0xc9, 0x21, - 0xcc, 0xa7, 0x7a, 0x8e, 0x93, 0xb9, 0x9a, 0xd2, 0xfe, 0xea, 0x58, 0xa8, 0x56, 0x90, 0x9c, 0x80, - 0x49, 0x59, 0xd7, 0xb3, 0xdb, 0xdb, 0x83, 0xd0, 0x6b, 0xb8, 0x2d, 0x9f, 0xf5, 0x98, 0x1b, 0xad, - 0xb9, 0x79, 0x95, 0x83, 0x7e, 0x90, 0x9c, 0x43, 0x3d, 0x5b, 0x8c, 0x9f, 0x8b, 0x83, 0xaa, 0xb9, - 0x1e, 0x76, 0x29, 0xb3, 0xdb, 0xcc, 0x8f, 0x27, 0xbc, 0x2c, 0x69, 0x10, 0xb5, 0x9b, 0x66, 0x04, - 0xc8, 0x01, 0xcc, 0x3c, 0x62, 0x21, 0x65, 0xfd, 0xae, 0xd3, 0xb2, 0xa3, 0x2f, 0xef, 0x35, 0x75, - 0x83, 0xd2, 0xbd, 0x42, 0x2e, 0xb6, 0xad, 0x94, 0x5e, 0x3c, 0x44, 0x94, 0x05, 0x2c, 0x6c, 0xb2, - 0x20, 0xa5, 0x1e, 0xcc, 0x15, 0xe9, 0x10, 0x69, 0x38, 0xa8, 0x4e, 0x8c, 0xec, 0xc3, 0xec, 0x23, - 0xef, 0xc0, 0x3e, 0xc7, 0xef, 0x64, 0x10, 0x63, 0x5d, 0x97, 0x95, 0xbd, 0xda, 0x2f, 0x66, 0x96, - 0x15, 0x14, 0x68, 0xac, 0xb7, 0xef, 0x0c, 0x75, 0xaa, 0xb9, 0xaa, 0xa2, 0xc9, 0xfd, 0x29, 0x34, - 0xb9, 0x83, 0x7c, 0x09, 0x4b, 0x7b, 0x4e, 0x97, 0x35, 0x99, 0xff, 0xc6, 0x69, 0xb1, 0xf4, 0x96, - 0x99, 0x6b, 0xd2, 0x7d, 0xce, 0xe1, 0x12, 0xc8, 0x79, 0x20, 0xa4, 0x07, 0x2b, 0x6a, 0xd7, 0xc3, - 0x37, 0x4e, 0x2b, 0x99, 0xf8, 0xba, 0xa4, 0xcd, 0x8a, 0x58, 0xc5, 0x48, 0x85, 0x70, 0xe4, 0x05, - 0xcc, 0x1f, 0xb0, 0xd0, 0x6e, 0xdb, 0xa1, 0x2d, 0xbd, 0xcb, 0x0d, 0xf9, 0x06, 0x68, 0x58, 0x04, - 0xbc, 0x56, 0x9c, 0x1c, 0x02, 0x79, 0xe4, 0x3d, 0xaa, 0x1f, 0x31, 0xbf, 0xc5, 0x86, 0xd6, 0x8c, - 0x25, 0x7f, 0xf9, 0x33, 0x0c, 0x02, 0x52, 0x23, 0x8a, 0xa6, 0xc4, 0x9e, 0x3d, 0xe8, 0x86, 0x0d, - 0xf7, 0x2b, 0x36, 0x5c, 0x8c, 0xf7, 0x24, 0xc0, 0x2c, 0x03, 0xd5, 0x08, 0x91, 0xdf, 0xc2, 0x62, - 0x3d, 0xec, 0x1e, 0x78, 0x5c, 0x99, 0x72, 0x05, 0x16, 0xc3, 0xdd, 0x94, 0x6e, 0x80, 0x9e, 0x49, - 0xcc, 0x31, 0x07, 0x82, 0x7c, 0x09, 0x57, 0x5f, 0x79, 0xfe, 0xeb, 0xa0, 0x6f, 0xb7, 0xd8, 0xf1, - 0x99, 0xcf, 0x82, 0x33, 0xaf, 0x1b, 0x7f, 0x13, 0xcc, 0xf7, 0xa5, 0xaf, 0x6a, 0x2e, 0x1f, 0xcd, - 0x87, 0x20, 0x3d, 0x58, 0xad, 0x87, 0xdd, 0x23, 0x9f, 0x9d, 0xb2, 0xb0, 0x75, 0x76, 0xe8, 0x36, - 0xe3, 0x4f, 0x43, 0x32, 0xc8, 0x07, 0x7c, 0x90, 0xf7, 0x87, 0x2f, 0x51, 0xc0, 0x4c, 0x2f, 0x01, - 0x23, 0xbf, 0x05, 0xb3, 0xd1, 0xac, 0x1f, 0xed, 0xfa, 0xb6, 0xe3, 0xd6, 0x3d, 0x37, 0x18, 0xf4, - 0x86, 0x3a, 0xe7, 0x16, 0x1f, 0x68, 0x4d, 0x0c, 0x94, 0xc7, 0x46, 0x73, 0x01, 0x48, 0x1f, 0xae, - 0x37, 0x5a, 0xec, 0x84, 0xf9, 0x1d, 0x61, 0x58, 0xbd, 0xb1, 0xbb, 0x4e, 0xdb, 0x0e, 0x93, 0x43, - 0xb8, 0xc1, 0x47, 0xb8, 0x19, 0x8f, 0x50, 0xc4, 0x2b, 0xb6, 0xa5, 0x18, 0x90, 0x74, 0x60, 0xf9, - 0xc0, 0x73, 0x3b, 0xde, 0xee, 0x4e, 0xbd, 0xeb, 0xf0, 0xd3, 0x15, 0x3a, 0x7e, 0x32, 0xdc, 0x6d, - 0x3e, 0xdc, 0x8d, 0xf8, 0xcc, 0xe7, 0x32, 0x8a, 0xb1, 0x0a, 0xa0, 0xf0, 0x18, 0x50, 0x76, 0x8a, - 0x7b, 0x27, 0x54, 0xdb, 0xf6, 0x20, 0x3c, 0x8b, 0xc7, 0xb9, 0x23, 0x1d, 0x83, 0x5c, 0x3e, 0x9a, - 0x0f, 0x61, 0xed, 0xc1, 0x52, 0xc6, 0x91, 0x10, 0x9e, 0xd4, 0x5d, 0xa8, 0x08, 0x81, 0xc0, 0x2c, - 0xad, 0x8f, 0x6e, 0x4c, 0xde, 0x9f, 0xd9, 0x14, 0x61, 0x13, 0x41, 0xa7, 0x09, 0x83, 0xf5, 0x8f, - 0xd7, 0xa0, 0x92, 0x48, 0xfe, 0xb4, 0x1e, 0xd6, 0x3c, 0x8c, 0x3d, 0xf4, 0x7d, 0xcf, 0xe7, 0xae, - 0x55, 0x8d, 0x46, 0x0d, 0xf2, 0x79, 0xee, 0xc4, 0xcd, 0xb2, 0xa4, 0x3e, 0x73, 0xb8, 0x68, 0xee, - 0x7b, 0x1f, 0xc2, 0xbc, 0xec, 0x0a, 0x09, 0xd8, 0x31, 0x49, 0x93, 0xe9, 0x58, 0xa8, 0x56, 0x10, - 0xbf, 0xb3, 0x43, 0xaf, 0x48, 0x80, 0x8d, 0x4b, 0xdf, 0x59, 0xb5, 0x9b, 0x66, 0x04, 0xd0, 0x6f, - 0x49, 0xb9, 0x45, 0x02, 0x65, 0x42, 0xfa, 0xf8, 0x64, 0xfa, 0x69, 0x56, 0x44, 0x58, 0x69, 0x43, - 0xaf, 0x48, 0x20, 0x55, 0x54, 0x2b, 0x4d, 0xe5, 0xa0, 0x3a, 0x31, 0xe1, 0x98, 0x25, 0xae, 0x91, - 0x00, 0xab, 0xaa, 0x8e, 0x99, 0xc2, 0x40, 0x35, 0x42, 0xb8, 0xec, 0xb2, 0x67, 0x24, 0xc0, 0x40, - 0x35, 0x91, 0x33, 0x2c, 0x54, 0x2b, 0x48, 0x7e, 0x09, 0x30, 0x74, 0x9d, 0xcc, 0x49, 0x69, 0x4e, - 0xd9, 0x50, 0x14, 0x4d, 0x31, 0x93, 0x07, 0x59, 0xa7, 0xca, 0xcc, 0x3a, 0x55, 0x42, 0x70, 0xc8, - 0x4a, 0x9e, 0x17, 0x78, 0x55, 0x37, 0x0a, 0xbc, 0xaa, 0xd4, 0xb2, 0x28, 0x7d, 0x08, 0x99, 0xeb, - 0x56, 0xdd, 0x28, 0x70, 0xab, 0x62, 0xc8, 0x6c, 0x1f, 0x79, 0x98, 0xe3, 0x57, 0x5d, 0xcf, 0xf1, - 0xab, 0x04, 0x94, 0x22, 0x44, 0xee, 0xa9, 0x8e, 0xd5, 0xa2, 0xea, 0x58, 0x09, 0xc1, 0x98, 0x8d, - 0x7c, 0x51, 0xec, 0x59, 0xdd, 0x2c, 0xf6, 0xac, 0x04, 0x5a, 0x8e, 0x6b, 0xb5, 0xa3, 0x77, 0xad, - 0x56, 0xf4, 0xae, 0x95, 0xc0, 0x92, 0x45, 0xc8, 0xd3, 0x5c, 0xdf, 0x6a, 0x2d, 0xd7, 0xb7, 0x8a, - 0x2f, 0xac, 0xda, 0x93, 0x3e, 0xcf, 0x91, 0x97, 0x24, 0xce, 0xf3, 0xbc, 0xf6, 0x3c, 0xa7, 0x59, - 0xa8, 0x56, 0x50, 0x00, 0xa6, 0x1c, 0x25, 0x01, 0xb8, 0xa0, 0x02, 0x66, 0x58, 0xa8, 0x56, 0x10, - 0x55, 0x68, 0x4e, 0x14, 0xcd, 0x5c, 0x94, 0x54, 0x68, 0x0e, 0x17, 0xcd, 0x13, 0x47, 0xe4, 0x8c, - 0xa3, 0x24, 0x90, 0x97, 0x24, 0xe4, 0x1c, 0x2e, 0x9a, 0x27, 0x4e, 0x28, 0x2c, 0x28, 0xfe, 0x92, - 0xc0, 0x35, 0xa5, 0xed, 0xd6, 0xf2, 0x50, 0xbd, 0x28, 0x69, 0x5d, 0xea, 0x6b, 0xdd, 0xba, 0xd4, - 0xd7, 0x12, 0x23, 0xe4, 0x3b, 0x5b, 0x7b, 0x30, 0x9b, 0xf2, 0x9d, 0xc4, 0xa4, 0x97, 0x25, 0xd5, - 0x92, 0xe9, 0xa7, 0x59, 0x11, 0xf2, 0x2c, 0xcf, 0xdf, 0x5a, 0xcd, 0xf3, 0xb7, 0x22, 0xc1, 0x3c, - 0x87, 0xeb, 0x10, 0xe6, 0x65, 0xcf, 0x49, 0x4c, 0x6d, 0x45, 0x3a, 0x55, 0x3a, 0x16, 0xaa, 0x15, - 0x8c, 0x2c, 0xf6, 0xa1, 0xeb, 0x24, 0xe0, 0xae, 0x2b, 0x16, 0xbb, 0xca, 0x30, 0xb4, 0xd8, 0xd5, - 0x1e, 0x01, 0x98, 0x78, 0x4f, 0x02, 0x70, 0x55, 0x05, 0x54, 0x18, 0x52, 0x80, 0x4a, 0x0f, 0xb1, - 0xc1, 0xcc, 0x3a, 0x4d, 0x02, 0x76, 0x4d, 0xba, 0xee, 0x79, 0x6c, 0x02, 0x3c, 0x17, 0x06, 0x2d, - 0xd2, 0x1c, 0x6f, 0x49, 0x8c, 0xb3, 0x2e, 0x69, 0xbc, 0x42, 0xde, 0xd8, 0x22, 0x2d, 0x64, 0x22, - 0x9f, 0xc3, 0x82, 0xe2, 0x40, 0x89, 0x91, 0x6e, 0xc8, 0x17, 0x43, 0xc7, 0x23, 0x46, 0xd0, 0x03, - 0x10, 0x0a, 0x73, 0x92, 0x1f, 0x25, 0x70, 0x2d, 0xd9, 0x62, 0xc8, 0x72, 0x08, 0x54, 0x9d, 0x30, - 0x5a, 0x21, 0x92, 0x43, 0x25, 0x30, 0xdf, 0x93, 0x30, 0x35, 0x1c, 0x54, 0x27, 0x86, 0xae, 0x74, - 0xc6, 0x8b, 0x12, 0x88, 0x37, 0xa5, 0xbb, 0x91, 0xc3, 0x15, 0xbb, 0xd2, 0x39, 0xdd, 0xc4, 0x86, - 0x65, 0x9d, 0x23, 0x25, 0x86, 0x78, 0x5f, 0xfa, 0x16, 0xe7, 0x33, 0xd2, 0x02, 0x90, 0x28, 0x80, - 0xe4, 0x26, 0xa9, 0xa7, 0x04, 0xfc, 0x03, 0x25, 0x80, 0x94, 0x65, 0xa1, 0x5a, 0x41, 0xd2, 0x87, - 0xb5, 0x5c, 0x97, 0x4c, 0x60, 0xdf, 0x92, 0xe2, 0x48, 0x97, 0x70, 0xd3, 0xcb, 0xe0, 0xd0, 0x55, - 0xd1, 0x78, 0x68, 0x62, 0xac, 0x0d, 0xc9, 0x55, 0xc9, 0xe5, 0xa3, 0xf9, 0x10, 0x24, 0x80, 0xd5, - 0x3c, 0xa7, 0x4c, 0x0c, 0x72, 0x5b, 0xf2, 0x58, 0x8b, 0x99, 0xc5, 0x9e, 0x5f, 0x02, 0x49, 0xbe, - 0x82, 0x6b, 0x5a, 0xef, 0x4c, 0x8c, 0x18, 0x79, 0x60, 0x56, 0x91, 0xa7, 0x27, 0x0d, 0x57, 0x04, - 0x86, 0xc7, 0x4c, 0xe7, 0xa8, 0x89, 0xa1, 0xee, 0x4a, 0xc7, 0x2c, 0x9f, 0x91, 0x16, 0x80, 0x58, - 0x0d, 0x6d, 0x76, 0x88, 0x27, 0xec, 0x78, 0xfe, 0xb7, 0xd1, 0x16, 0x79, 0xb3, 0xa4, 0x8d, 0xd9, - 0xb9, 0x26, 0xf7, 0xfa, 0xb8, 0xff, 0x55, 0xa5, 0xa2, 0x65, 0xfd, 0x4a, 0xef, 0x26, 0x11, 0x0b, - 0x6a, 0x36, 0xd2, 0x9b, 0x83, 0x56, 0x8b, 0x05, 0x01, 0xc7, 0xab, 0x50, 0x89, 0x66, 0x35, 0x32, - 0x69, 0x25, 0xf4, 0x19, 0x05, 0x92, 0xf0, 0x19, 0x47, 0xe9, 0x90, 0x90, 0xce, 0x3e, 0x8e, 0x70, - 0x7f, 0x32, 0x95, 0x7d, 0xcc, 0xfa, 0x4a, 0x26, 0x4c, 0xc8, 0xa3, 0xc7, 0x4d, 0x6b, 0x3f, 0x1b, - 0xf2, 0x24, 0x06, 0x8c, 0xd6, 0x7b, 0x6d, 0x91, 0x7b, 0xc4, 0x47, 0x4e, 0x39, 0xed, 0x98, 0x23, - 0x82, 0x72, 0xda, 0xe1, 0x3e, 0xe8, 0x79, 0xe8, 0xdb, 0x89, 0x0f, 0x8a, 0x0d, 0xeb, 0x96, 0xe6, - 0x9b, 0x4e, 0x08, 0x94, 0xf1, 0x59, 0xe0, 0xf1, 0x67, 0xeb, 0xd3, 0xcb, 0x82, 0x2d, 0xb8, 0x03, - 0x47, 0x76, 0x18, 0x32, 0x5f, 0x38, 0xdb, 0x55, 0x9a, 0xb4, 0xad, 0x8f, 0x2f, 0xbd, 0xca, 0xda, - 0x41, 0xff, 0xa3, 0x94, 0x1f, 0x73, 0xc9, 0x2e, 0xf7, 0x94, 0xb2, 0xdc, 0x5c, 0x0d, 0x36, 0x76, - 0xe3, 0xe5, 0x16, 0x4d, 0xec, 0x79, 0xe2, 0x9d, 0x3c, 0xb3, 0x7b, 0x4c, 0x1c, 0x87, 0xb8, 0x89, - 0x4b, 0xf4, 0xc4, 0x3b, 0x69, 0xec, 0x72, 0xf7, 0xbb, 0x4c, 0xa3, 0x06, 0xd9, 0x80, 0x99, 0xc8, - 0x5e, 0xdf, 0x63, 0x6e, 0x8b, 0x1d, 0xba, 0xdd, 0x0b, 0xee, 0x47, 0x57, 0xa8, 0x4a, 0x26, 0x1f, - 0xc0, 0x34, 0x65, 0x2e, 0xfb, 0x66, 0xc8, 0x38, 0xce, 0x19, 0x15, 0x2a, 0x66, 0xb7, 0xf3, 0x75, - 0x44, 0xfe, 0xce, 0x7f, 0x9e, 0xcd, 0x43, 0x6a, 0x76, 0x7e, 0x1e, 0xc6, 0x90, 0x21, 0x10, 0x7b, - 0x1f, 0x35, 0x70, 0xb1, 0x12, 0x8d, 0xcd, 0x5f, 0x7b, 0x94, 0x0e, 0x09, 0x78, 0x0a, 0xb2, 0x6e, - 0xb6, 0x6e, 0x43, 0xe6, 0x75, 0x59, 0x4c, 0xeb, 0x8f, 0x25, 0xa8, 0xc4, 0xb4, 0xe1, 0xc2, 0xb7, - 0x45, 0xdc, 0x24, 0x6e, 0x22, 0xe0, 0x53, 0x76, 0x81, 0x13, 0x1b, 0xdd, 0xa8, 0x51, 0xfe, 0x4c, - 0xee, 0x44, 0x92, 0x07, 0x98, 0x67, 0x1f, 0xe5, 0x81, 0x94, 0xe9, 0x4d, 0x5e, 0xca, 0x12, 0x53, - 0x69, 0xd2, 0x4f, 0xd6, 0x61, 0xd2, 0x09, 0xa8, 0xed, 0x76, 0xb8, 0xcf, 0xc4, 0x37, 0xa9, 0x42, - 0xd3, 0x24, 0x72, 0x0b, 0x26, 0x1e, 0x7b, 0xdd, 0x36, 0xf3, 0x03, 0x73, 0x8c, 0x87, 0x7b, 0xa6, - 0x22, 0xb0, 0x57, 0xb6, 0x83, 0xce, 0x3a, 0x8d, 0x7b, 0x91, 0x11, 0x69, 0xc8, 0x38, 0xae, 0x65, - 0x14, 0xbd, 0xd6, 0x97, 0xda, 0x58, 0x03, 0xbe, 0x4a, 0xdd, 0x6d, 0xc4, 0xeb, 0xce, 0x9f, 0xc9, - 0xcf, 0xa1, 0x16, 0xf3, 0x61, 0x30, 0xc6, 0x1c, 0x11, 0x01, 0xa7, 0x48, 0xdb, 0x25, 0x10, 0x12, - 0x93, 0x35, 0xa7, 0xc9, 0xe5, 0x5a, 0x67, 0x30, 0x79, 0x7c, 0xee, 0xbe, 0xdd, 0x8a, 0x52, 0xef, - 0x9b, 0x64, 0x45, 0xf1, 0x99, 0xdc, 0x85, 0x89, 0xc3, 0x7e, 0xc8, 0x43, 0x5e, 0x51, 0x22, 0x7f, - 0x76, 0xb8, 0xa0, 0xa2, 0x83, 0xc6, 0x1c, 0xd6, 0x7f, 0x96, 0x60, 0x42, 0x0c, 0x4e, 0x3e, 0x83, - 0x4a, 0xdd, 0x67, 0x76, 0xc8, 0xb6, 0x43, 0x51, 0x52, 0xb2, 0xbc, 0x19, 0x55, 0xf8, 0x6c, 0xc6, - 0x15, 0x3e, 0xa9, 0xc2, 0x92, 0x0a, 0x7e, 0x0c, 0x7e, 0xf7, 0xdf, 0x6b, 0x25, 0x9a, 0x48, 0x91, - 0x75, 0x28, 0xa3, 0xfd, 0xc5, 0x4f, 0xde, 0xe4, 0xfd, 0xda, 0x26, 0xd6, 0x21, 0x1d, 0x9f, 0xbb, - 0x48, 0xa3, 0xbc, 0x07, 0x5f, 0xe5, 0x45, 0xc0, 0xfc, 0xe3, 0x73, 0x97, 0x4f, 0xae, 0x42, 0xe3, - 0x26, 0xb9, 0x07, 0x55, 0x5c, 0x73, 0x9c, 0x65, 0x60, 0x96, 0xf9, 0xd2, 0x91, 0x38, 0x28, 0x34, - 0x5c, 0x0b, 0x3a, 0x64, 0x92, 0xef, 0xff, 0x98, 0x72, 0xff, 0xb1, 0x58, 0x47, 0x13, 0xa1, 0xd1, - 0xed, 0xdb, 0x3d, 0x98, 0x14, 0x6c, 0xa9, 0x6d, 0x9b, 0x1e, 0x8e, 0xcd, 0x01, 0xd2, 0x2c, 0xd6, - 0x82, 0x36, 0x71, 0x6e, 0xfd, 0x43, 0x09, 0xaa, 0x09, 0x11, 0xd5, 0xe1, 0x33, 0xaf, 0xcd, 0x8e, - 0x2f, 0xfa, 0x4c, 0x0c, 0x97, 0xb4, 0xf1, 0x83, 0x84, 0xcf, 0x8d, 0xb6, 0xb8, 0xa4, 0xa2, 0x45, - 0x56, 0x04, 0x00, 0x17, 0x8a, 0x94, 0xd3, 0x90, 0x80, 0x93, 0x7f, 0x11, 0xb0, 0xb6, 0xd0, 0x4e, - 0xfc, 0x19, 0x69, 0x7b, 0x3e, 0x8b, 0x22, 0x7b, 0x65, 0xca, 0x9f, 0x71, 0xe4, 0xc7, 0x4e, 0x48, - 0xed, 0xd0, 0xf1, 0xb8, 0x02, 0x1a, 0xa1, 0x49, 0xdb, 0x7a, 0xa6, 0x0f, 0x51, 0x91, 0x07, 0x30, - 0x95, 0x10, 0xf9, 0x32, 0x44, 0xe1, 0xd2, 0x24, 0xaa, 0x99, 0x08, 0xc8, 0x6c, 0x56, 0x17, 0x56, - 0x8a, 0xb2, 0xc8, 0xb8, 0xe1, 0x8f, 0x7c, 0x6f, 0xd0, 0x4f, 0x54, 0x74, 0xdc, 0x2c, 0x56, 0xd0, - 0xf1, 0x97, 0x72, 0x54, 0xfe, 0x52, 0x7e, 0x0c, 0xd7, 0x0b, 0x23, 0x2b, 0x72, 0xe9, 0xcc, 0x58, - 0x5c, 0x3a, 0xf3, 0x04, 0xe6, 0xa5, 0x28, 0xc9, 0x8f, 0x98, 0x9c, 0x75, 0x17, 0x16, 0xb4, 0x81, - 0x18, 0xdc, 0x09, 0x6c, 0xc7, 0x47, 0x0b, 0x9f, 0xad, 0x26, 0x2c, 0xe5, 0xa4, 0xb2, 0xc9, 0x2a, - 0x00, 0x86, 0x46, 0x4e, 0xec, 0x80, 0x25, 0x11, 0xe6, 0x14, 0xa5, 0x60, 0x06, 0x1f, 0x81, 0x99, - 0x17, 0xc3, 0x29, 0xf8, 0x78, 0xec, 0x41, 0x85, 0xef, 0xdc, 0x53, 0x76, 0x81, 0x53, 0x3d, 0xb2, - 0xc3, 0xb3, 0x78, 0xaa, 0xf8, 0x8c, 0x47, 0xf2, 0xf0, 0xf4, 0x34, 0x60, 0x51, 0x41, 0xdd, 0x28, - 0x15, 0x2d, 0x32, 0x0d, 0x23, 0xcd, 0x6f, 0xc5, 0x17, 0x63, 0xa4, 0xf9, 0xad, 0xf5, 0x40, 0x1c, - 0x51, 0xae, 0xbd, 0x6f, 0x43, 0xf9, 0x35, 0x6a, 0xf4, 0x92, 0xa4, 0xea, 0xe2, 0x7e, 0x61, 0x30, - 0x72, 0x16, 0xeb, 0x18, 0x66, 0xc4, 0xab, 0x27, 0xd3, 0x98, 0x87, 0xb1, 0x86, 0xdb, 0x66, 0xe7, - 0xf1, 0x66, 0xf1, 0x06, 0xc6, 0xec, 0x63, 0x0e, 0xa1, 0x48, 0x54, 0x5c, 0x9a, 0x30, 0x58, 0xaf, - 0xb4, 0xe9, 0x7f, 0xac, 0xf9, 0x51, 0x06, 0x13, 0x53, 0x4c, 0xe2, 0x7b, 0x72, 0x2f, 0x55, 0xd9, - 0xad, 0x43, 0x98, 0x8d, 0x17, 0x35, 0x41, 0xcf, 0x99, 0xb0, 0x01, 0xa3, 0x8f, 0x9d, 0xb8, 0x0e, - 0x11, 0x1f, 0x71, 0x7d, 0x91, 0x5f, 0x58, 0x5a, 0xfc, 0xd9, 0xfa, 0x52, 0x1f, 0x4b, 0xc3, 0xa0, - 0x4a, 0x66, 0x20, 0x31, 0x59, 0x33, 0x99, 0xac, 0xd2, 0x4f, 0xb3, 0x22, 0x16, 0xd5, 0x96, 0x2e, - 0x90, 0x5f, 0x43, 0x2d, 0xa1, 0x45, 0xcb, 0x10, 0x05, 0xed, 0x87, 0x65, 0xa0, 0xe9, 0x6e, 0x2a, - 0x31, 0x8b, 0x7b, 0x93, 0x8d, 0xba, 0xdd, 0x87, 0x6a, 0x42, 0x4c, 0xaa, 0x0f, 0x35, 0x88, 0x74, - 0xc8, 0x66, 0x35, 0x61, 0xf2, 0xc8, 0x67, 0x7d, 0xdb, 0x67, 0xcd, 0xb0, 0xc7, 0x97, 0x88, 0x5b, - 0x60, 0xe2, 0x08, 0xe2, 0x33, 0x2e, 0x64, 0xf3, 0xf9, 0x7e, 0x6c, 0xb3, 0x36, 0x9f, 0xef, 0xe3, - 0x25, 0x39, 0xb2, 0x7d, 0xbb, 0x87, 0xea, 0x2f, 0x10, 0xcb, 0x99, 0xa2, 0x58, 0x67, 0x60, 0x88, - 0x78, 0xd8, 0x31, 0xeb, 0xf5, 0xa3, 0xa0, 0xe5, 0x32, 0x54, 0xe2, 0x6b, 0x14, 0xeb, 0xdd, 0xb8, - 0x8d, 0x1b, 0xb8, 0xdd, 0x75, 0xec, 0xc4, 0x36, 0xe2, 0x0d, 0x34, 0xf7, 0x8f, 0xce, 0x2e, 0x02, - 0xa7, 0x65, 0x77, 0x53, 0x56, 0xa1, 0x44, 0xb3, 0xfe, 0xaf, 0x94, 0x57, 0x75, 0x81, 0x37, 0x07, - 0x49, 0x89, 0x12, 0x11, 0x2d, 0xf2, 0x51, 0x52, 0x4a, 0x19, 0x65, 0x88, 0x56, 0xf4, 0x01, 0xc3, - 0x88, 0x27, 0x2e, 0xb4, 0x24, 0x9f, 0xc1, 0xb5, 0xe4, 0x5d, 0x22, 0x4e, 0xc7, 0x73, 0x9b, 0x83, - 0x7e, 0xdf, 0xf3, 0x43, 0xa1, 0xfb, 0x2b, 0xb4, 0x88, 0x85, 0xec, 0xc1, 0xea, 0xbe, 0x1d, 0x84, - 0x0d, 0x37, 0x60, 0x7e, 0xd8, 0xd8, 0xd5, 0x80, 0x44, 0xe6, 0xeb, 0x25, 0x5c, 0x4f, 0xca, 0x95, - 0x51, 0xa3, 0x6c, 0xfd, 0x7b, 0x25, 0x37, 0x10, 0x8a, 0xba, 0x61, 0x77, 0x47, 0x2c, 0xf2, 0xc8, - 0xee, 0x0e, 0x79, 0x00, 0xb5, 0xd4, 0x1e, 0x07, 0xe6, 0x88, 0xf4, 0x19, 0x4f, 0x75, 0x51, 0x89, - 0x8f, 0xdc, 0x01, 0x03, 0xe7, 0xb2, 0x7d, 0x7a, 0xca, 0x5a, 0x21, 0x6b, 0x73, 0x93, 0x26, 0xd2, - 0x38, 0x19, 0x3a, 0xf9, 0x14, 0xa6, 0xd1, 0x64, 0xd8, 0x67, 0x6f, 0x58, 0x37, 0x6d, 0x2c, 0xcc, - 0xc7, 0xe1, 0xed, 0x74, 0x27, 0x55, 0x78, 0xc9, 0x2e, 0x5c, 0x97, 0x19, 0x58, 0x97, 0xd9, 0x01, - 0x53, 0x97, 0xa6, 0x98, 0x89, 0xec, 0xc1, 0x0c, 0x32, 0xec, 0xb2, 0x53, 0xc7, 0x65, 0xed, 0x97, - 0x76, 0x62, 0x45, 0x2a, 0x5b, 0x2c, 0x33, 0x51, 0x55, 0x88, 0xfc, 0x02, 0x96, 0x14, 0xd2, 0xc3, - 0x73, 0x31, 0x8f, 0x09, 0x3e, 0x8f, 0xbc, 0x6e, 0x9c, 0x41, 0xf3, 0x22, 0x08, 0x59, 0xef, 0xa5, - 0xed, 0x3b, 0x78, 0x0e, 0x02, 0xb3, 0xa2, 0x9b, 0x81, 0xcc, 0x44, 0x55, 0x21, 0x9c, 0x81, 0x42, - 0x4a, 0x66, 0x50, 0x8d, 0x66, 0x90, 0xd3, 0x4d, 0x3e, 0x85, 0xab, 0xca, 0xe4, 0x30, 0x26, 0x6b, - 0x5f, 0x20, 0x17, 0x4f, 0x78, 0x55, 0x68, 0x3e, 0x03, 0x4a, 0x2b, 0xc0, 0x29, 0xe9, 0xc9, 0x48, - 0x3a, 0x97, 0x81, 0x3c, 0x86, 0x35, 0xa5, 0xb3, 0xe9, 0xda, 0xfd, 0xe0, 0xcc, 0x0b, 0x8f, 0x3d, - 0x6f, 0xdf, 0xf6, 0x3b, 0x8c, 0x67, 0xbc, 0x2a, 0xf4, 0x32, 0x36, 0x44, 0x52, 0x26, 0x99, 0x41, - 0x9a, 0x8a, 0x90, 0x2e, 0x61, 0xc3, 0x7b, 0x2b, 0xce, 0x74, 0x1b, 0x0f, 0xf5, 0xbe, 0xe7, 0x76, - 0x50, 0xed, 0xd4, 0xcf, 0x58, 0xeb, 0x35, 0x6b, 0xf3, 0x6c, 0x57, 0x85, 0x16, 0xb1, 0xa0, 0xf1, - 0xb7, 0xe7, 0x0d, 0xdc, 0xe8, 0xf8, 0xcf, 0x44, 0x29, 0xe7, 0x84, 0x40, 0x3e, 0x01, 0x48, 0x2e, - 0x7d, 0x60, 0x1a, 0xeb, 0xa3, 0xa9, 0xdc, 0xab, 0xaa, 0x03, 0x69, 0x8a, 0x95, 0x3c, 0x80, 0xc5, - 0xa4, 0x85, 0xea, 0x98, 0x25, 0x3b, 0x3c, 0xcb, 0xe7, 0x94, 0xd3, 0x8b, 0x5a, 0x31, 0xad, 0x20, - 0x78, 0x32, 0xaa, 0x4c, 0x25, 0x1a, 0xb9, 0x0f, 0xf3, 0xe9, 0x76, 0x82, 0x3c, 0xc7, 0x91, 0xb5, - 0x7d, 0xd6, 0x3f, 0x8f, 0xeb, 0x0b, 0xcd, 0x72, 0xf5, 0x68, 0xa4, 0x65, 0x46, 0x12, 0x2d, 0xb3, - 0x0e, 0x93, 0x4d, 0x16, 0xbe, 0xb4, 0xfd, 0x48, 0xc9, 0x8c, 0xf2, 0x50, 0x43, 0x9a, 0x94, 0xd1, - 0x43, 0xe5, 0x1f, 0xa1, 0x87, 0xc6, 0xde, 0x5a, 0x0f, 0x8d, 0xbf, 0x83, 0x1e, 0xd2, 0x68, 0x90, - 0x89, 0x9f, 0x58, 0x83, 0x54, 0xde, 0x59, 0x83, 0x54, 0x7f, 0x62, 0x0d, 0x02, 0x3f, 0x42, 0x83, - 0x4c, 0xfe, 0x28, 0x0d, 0x52, 0xbb, 0x4c, 0x83, 0x48, 0x77, 0x6d, 0xaa, 0xf8, 0xae, 0x4d, 0xbf, - 0xfd, 0x5d, 0x53, 0xef, 0xcc, 0xcc, 0x3b, 0xdc, 0x19, 0xa3, 0xe0, 0xce, 0xfc, 0x6f, 0x29, 0xc9, - 0x19, 0xca, 0x2b, 0xa2, 0xb5, 0xa3, 0xd6, 0x61, 0xec, 0xa5, 0xdd, 0x1d, 0x30, 0x61, 0x3e, 0xc3, - 0x26, 0xff, 0x89, 0xcf, 0xc3, 0xf3, 0xbe, 0x4f, 0xa3, 0x0e, 0x6e, 0x69, 0x7d, 0xdd, 0x15, 0x86, - 0x0e, 0x3e, 0x72, 0xd3, 0x36, 0xd8, 0x71, 0x5c, 0x61, 0x60, 0x44, 0x0d, 0xbc, 0x10, 0xe2, 0x82, - 0x70, 0xa3, 0xeb, 0x29, 0xfa, 0x37, 0x91, 0xa7, 0x9d, 0xa1, 0x93, 0x55, 0x28, 0x73, 0xb7, 0x75, - 0x3c, 0x3d, 0x28, 0x52, 0x28, 0xa7, 0x93, 0x7b, 0x30, 0x47, 0x07, 0x6e, 0xe8, 0xf4, 0x58, 0x33, - 0xf4, 0x1d, 0xb7, 0xb3, 0xeb, 0xf5, 0x6c, 0xc7, 0xe5, 0x1f, 0xba, 0x29, 0xaa, 0xeb, 0xb2, 0x9e, - 0xc3, 0x94, 0x74, 0x6d, 0xb4, 0x2f, 0x9b, 0x78, 0x7c, 0x91, 0x97, 0x14, 0x35, 0xf8, 0x2f, 0x87, - 0xce, 0xdd, 0xc6, 0x6e, 0xa4, 0x1e, 0x6a, 0x54, 0xb4, 0xac, 0xbf, 0xc8, 0xc9, 0xbd, 0x16, 0x38, - 0x4e, 0xbf, 0x86, 0xb5, 0x4b, 0xea, 0x53, 0xd3, 0xbe, 0x5a, 0x49, 0xf6, 0xd5, 0x2c, 0x58, 0xbf, - 0x2c, 0xe1, 0x6a, 0x6d, 0xf0, 0x32, 0x61, 0x4d, 0x81, 0x29, 0x6a, 0xbe, 0xfa, 0xb3, 0xd8, 0xbe, - 0xaa, 0x3f, 0x13, 0xbf, 0x19, 0xd1, 0xa5, 0x46, 0x73, 0x7e, 0x33, 0xf2, 0xa1, 0xb6, 0x14, 0x35, - 0x4f, 0xd3, 0x5a, 0x47, 0xfa, 0x44, 0x6a, 0xfe, 0xe2, 0xa0, 0x81, 0x8e, 0xc1, 0xf9, 0x68, 0xdb, - 0xf8, 0x16, 0xd4, 0x68, 0x8a, 0x62, 0x6d, 0x69, 0xaa, 0x57, 0xd1, 0x42, 0x8f, 0x49, 0xf1, 0x6f, - 0x6b, 0xe2, 0xb6, 0x75, 0x4f, 0x97, 0x7a, 0x2d, 0x94, 0xf8, 0xa5, 0xa6, 0xa4, 0x95, 0xdc, 0x84, - 0xa9, 0x98, 0xb4, 0x73, 0x11, 0xb2, 0x40, 0x2c, 0x8b, 0x4c, 0xb4, 0x7e, 0xa5, 0x4b, 0xcb, 0xbe, - 0xa5, 0xec, 0x59, 0x6e, 0xed, 0x2b, 0xd9, 0x12, 0x37, 0xa1, 0xc4, 0xcd, 0xfe, 0x6b, 0x39, 0x09, - 0xd2, 0xd4, 0xd5, 0x88, 0xc3, 0x3e, 0x4d, 0xe7, 0x5b, 0x26, 0xdc, 0xef, 0x21, 0xc1, 0x3a, 0xcf, - 0xcf, 0xf5, 0xca, 0x92, 0x25, 0x45, 0x12, 0xdf, 0x84, 0x37, 0xea, 0x76, 0xdf, 0x6e, 0x39, 0xe1, - 0x85, 0xc0, 0x96, 0x89, 0xb8, 0xbb, 0x07, 0x2c, 0x08, 0xec, 0x4e, 0x12, 0x0f, 0x17, 0x4d, 0xeb, - 0xb0, 0xb8, 0xfe, 0xf6, 0x9d, 0x5f, 0xd4, 0xfa, 0xeb, 0x4b, 0x72, 0xca, 0x7f, 0xe6, 0xf7, 0xf9, - 0x48, 0x5f, 0xe0, 0x5b, 0x3c, 0xaa, 0xf5, 0x97, 0x39, 0x69, 0xe9, 0xec, 0x74, 0x4a, 0x9a, 0xe9, - 0x58, 0xff, 0x5f, 0xba, 0xa4, 0xb4, 0xf3, 0x92, 0x44, 0x06, 0x9f, 0x5c, 0x68, 0x77, 0xbd, 0x4e, - 0x12, 0x0a, 0x1a, 0x12, 0xb0, 0x17, 0xd5, 0x20, 0x4f, 0xc9, 0xc6, 0x11, 0xc3, 0x84, 0x80, 0x5a, - 0x21, 0x2a, 0x01, 0x2a, 0x47, 0xfe, 0x2e, 0x6f, 0xe0, 0xa5, 0x8d, 0xcd, 0x57, 0x11, 0x39, 0x1d, - 0xa5, 0x29, 0x0a, 0x7e, 0xa1, 0xe2, 0x17, 0xde, 0xf7, 0x5a, 0xdc, 0x2b, 0x7c, 0x6c, 0x07, 0x67, - 0x5c, 0xb3, 0x57, 0xa9, 0xb6, 0x0f, 0x6f, 0x68, 0x54, 0x40, 0xd6, 0xd8, 0xe5, 0x2a, 0xbd, 0x4a, - 0x93, 0xb6, 0xf5, 0xf8, 0xb2, 0xac, 0x67, 0x94, 0x38, 0xc1, 0xb8, 0x5e, 0xfb, 0xa1, 0x1b, 0xfa, - 0x4e, 0x72, 0xe7, 0x14, 0xaa, 0xb5, 0xa9, 0x2b, 0xa5, 0xc6, 0x0d, 0x17, 0x14, 0xa1, 0x1c, 0xe2, - 0xa6, 0xb5, 0xa5, 0xcd, 0xfb, 0x17, 0x08, 0xec, 0xeb, 0x4a, 0xab, 0x51, 0x5f, 0x8a, 0x5a, 0x4f, - 0xf1, 0xeb, 0xbe, 0xa8, 0x95, 0x84, 0x27, 0x18, 0x4f, 0x24, 0x44, 0x16, 0x6a, 0x8a, 0x62, 0xdd, - 0xd6, 0x96, 0x08, 0x68, 0x13, 0x2b, 0x77, 0xf2, 0x0a, 0xb1, 0xb3, 0x19, 0x1e, 0xeb, 0xc3, 0xdc, - 0x5a, 0x01, 0x2d, 0x74, 0xaf, 0xa0, 0x0c, 0x1b, 0x93, 0x5b, 0xe2, 0x47, 0xa1, 0x71, 0x8f, 0xf8, - 0x84, 0xa9, 0x64, 0xdc, 0xa3, 0x57, 0xbe, 0x13, 0x0e, 0x21, 0xc4, 0x61, 0x54, 0xa8, 0x96, 0x5b, - 0x54, 0x69, 0xf0, 0x67, 0x18, 0xef, 0xa5, 0xbe, 0xec, 0x80, 0xfc, 0x15, 0xd4, 0xd2, 0xf4, 0xb7, - 0xf8, 0x95, 0xac, 0xc4, 0x6f, 0xfd, 0x4d, 0xa9, 0xa8, 0x40, 0xfa, 0x92, 0x4b, 0x6b, 0x41, 0x0d, - 0xbf, 0xa9, 0x8c, 0xc7, 0x74, 0x92, 0x7b, 0x2b, 0xd1, 0xd0, 0xb8, 0x12, 0x71, 0xed, 0x87, 0xe7, - 0xad, 0xee, 0x20, 0x70, 0xde, 0x30, 0x11, 0xef, 0xce, 0xd0, 0xad, 0x4f, 0x0a, 0x73, 0xf8, 0x05, - 0xd6, 0xcb, 0x3f, 0x0d, 0x2d, 0x47, 0xd9, 0x1a, 0xfe, 0x81, 0x96, 0xe3, 0x06, 0xcc, 0x3c, 0x63, - 0xe7, 0xe1, 0xb1, 0x6f, 0xbb, 0x81, 0x1d, 0x45, 0xb7, 0xa2, 0x44, 0x8e, 0x4a, 0x26, 0x9b, 0x50, - 0x13, 0x46, 0x5d, 0x04, 0x59, 0xce, 0x40, 0x4a, 0xfd, 0xd6, 0xbf, 0x95, 0x0a, 0xea, 0xc4, 0x73, - 0x9d, 0xc3, 0x15, 0xa8, 0xa2, 0x8d, 0xd8, 0x70, 0xfb, 0x83, 0x50, 0xdc, 0xc0, 0x21, 0x41, 0x8a, - 0x05, 0x8e, 0x2a, 0xb1, 0x40, 0x0b, 0x6a, 0x52, 0x71, 0x42, 0x99, 0x1b, 0x2f, 0x12, 0x0d, 0xd7, - 0xa8, 0x69, 0x77, 0xa3, 0x9f, 0x9e, 0xd6, 0x28, 0x7f, 0xe6, 0x5a, 0x9f, 0xef, 0xc1, 0x76, 0xbb, - 0xed, 0xe3, 0x8a, 0x47, 0x6a, 0x51, 0x26, 0x5a, 0xff, 0x52, 0x2a, 0xaa, 0x84, 0xf8, 0xe1, 0x16, - 0x15, 0x2a, 0x67, 0x6c, 0x31, 0x37, 0x74, 0x22, 0xf5, 0xbb, 0x67, 0x3b, 0x5d, 0xd6, 0x16, 0xbb, - 0xa0, 0xed, 0x8b, 0x32, 0xd8, 0x7c, 0x1d, 0x29, 0xfb, 0x8a, 0xbb, 0xb0, 0xc2, 0xcc, 0x57, 0xc9, - 0x77, 0xfe, 0x75, 0x3c, 0x55, 0xd9, 0x4e, 0xaa, 0xe2, 0xf7, 0xfa, 0xc6, 0x15, 0x32, 0x07, 0x33, - 0x4a, 0xb1, 0xb9, 0x51, 0x22, 0x06, 0xd4, 0xd2, 0xf5, 0x13, 0xc6, 0x08, 0xa9, 0x41, 0x25, 0x2e, - 0x65, 0x30, 0x46, 0xc9, 0x14, 0x54, 0x93, 0xb4, 0xb2, 0x51, 0x26, 0x33, 0x30, 0x99, 0xca, 0xa5, - 0x1a, 0x63, 0x64, 0x1a, 0x60, 0x98, 0xa3, 0x33, 0xc6, 0x11, 0x2f, 0x9d, 0x9c, 0x32, 0x26, 0x90, - 0x63, 0x58, 0xd3, 0x6c, 0x54, 0x10, 0x31, 0x29, 0x55, 0x36, 0xaa, 0x64, 0x51, 0x57, 0xac, 0x6c, - 0x00, 0xd2, 0xb3, 0x45, 0xc3, 0xc6, 0x24, 0x21, 0x6a, 0xd9, 0xb0, 0x51, 0x23, 0x93, 0x49, 0x0d, - 0xb0, 0x31, 0x45, 0xae, 0xe6, 0x94, 0xf7, 0x1a, 0xd3, 0x64, 0x56, 0xa9, 0xce, 0x35, 0x66, 0xc8, - 0x7c, 0xb6, 0xd8, 0xd6, 0x30, 0xd2, 0x6f, 0x81, 0x87, 0xcd, 0x98, 0x15, 0x94, 0x24, 0x16, 0x6e, - 0x10, 0x5c, 0x4e, 0xa5, 0xf0, 0xd4, 0x98, 0x43, 0xa2, 0x12, 0x69, 0x35, 0xe6, 0x71, 0x58, 0xc9, - 0x63, 0x31, 0x16, 0xc8, 0x4a, 0x7e, 0xb1, 0xa7, 0xb1, 0x88, 0x4b, 0x94, 0x54, 0x74, 0x18, 0x4b, - 0x62, 0xa4, 0xb4, 0xcf, 0x60, 0x98, 0x38, 0xa1, 0xb4, 0xa1, 0x6f, 0x5c, 0xe5, 0x5b, 0x71, 0x78, - 0xb0, 0xfd, 0xf9, 0x11, 0x3d, 0xac, 0x37, 0x8d, 0x65, 0xd1, 0x7e, 0x78, 0xb0, 0xdf, 0x38, 0x68, - 0x1c, 0x1b, 0xd7, 0xf0, 0x55, 0x55, 0xcb, 0xcd, 0x58, 0xc1, 0xe5, 0xd2, 0xda, 0x73, 0xc6, 0x75, - 0x3e, 0xef, 0xb4, 0xd5, 0x64, 0xac, 0xf2, 0xfd, 0x3f, 0x4c, 0xbe, 0xc6, 0xc6, 0x1a, 0x12, 0x52, - 0xdf, 0x47, 0x63, 0x1d, 0x27, 0xab, 0x7c, 0xd9, 0x8c, 0x1b, 0xb8, 0x99, 0xd9, 0x0f, 0x8a, 0x61, - 0xe1, 0x4b, 0xa4, 0x15, 0xb6, 0xf1, 0x1e, 0xb9, 0x06, 0x4b, 0x39, 0x55, 0x26, 0xc6, 0x4d, 0xb2, - 0x00, 0xb3, 0x99, 0xa2, 0x0b, 0xe3, 0x7d, 0xb2, 0x0c, 0x8b, 0x7a, 0xe3, 0xc4, 0xf8, 0x80, 0x2c, - 0xc1, 0x9c, 0x46, 0xeb, 0x1a, 0xb7, 0x70, 0x4a, 0xd9, 0xcb, 0x6d, 0x6c, 0xdc, 0xf9, 0xdb, 0x12, - 0x2c, 0x28, 0x5b, 0x28, 0xc2, 0xfa, 0x57, 0x33, 0x1d, 0x91, 0x73, 0x6f, 0x5c, 0x21, 0x77, 0xe1, - 0x96, 0xd2, 0xd5, 0x7c, 0xed, 0xf4, 0x75, 0x11, 0x68, 0xa3, 0x44, 0x3e, 0x84, 0xdb, 0x2a, 0x8e, - 0x8b, 0x8b, 0xa5, 0x65, 0x1f, 0xb9, 0xf3, 0x33, 0x98, 0xd7, 0x99, 0xe3, 0x04, 0xd0, 0xa2, 0xe9, - 0x79, 0xfc, 0x6a, 0x57, 0xa0, 0xbc, 0xeb, 0x04, 0xaf, 0x8d, 0xd2, 0xce, 0xfe, 0xef, 0xbf, 0x5b, - 0x2d, 0xfd, 0xe1, 0xbb, 0xd5, 0xd2, 0xff, 0x7c, 0xb7, 0x7a, 0xe5, 0x77, 0xdf, 0xaf, 0x5e, 0xf9, - 0xfb, 0xef, 0x57, 0x4b, 0x7f, 0xf8, 0x7e, 0xf5, 0xca, 0x1f, 0xbf, 0x5f, 0xbd, 0xf2, 0xc5, 0x66, - 0xea, 0xbf, 0x83, 0xf4, 0xec, 0xd0, 0x77, 0xce, 0x3d, 0xdf, 0xe9, 0x38, 0x6e, 0xdc, 0x70, 0xd9, - 0x56, 0xff, 0x75, 0x67, 0xab, 0x7f, 0xb2, 0xc5, 0xfd, 0x80, 0x93, 0x71, 0x5e, 0x35, 0xf0, 0xf3, - 0x3f, 0x0d, 0x00, 0xe0, 0x63, 0x7e, 0xac, 0xbd, 0x44, 0x00, 0x00, + // 5110 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xb4, 0x5c, 0xdd, 0x6e, 0x1c, 0x47, + 0x76, 0xd6, 0x88, 0x43, 0x72, 0xe6, 0x70, 0x48, 0xb6, 0x8a, 0x7f, 0x2d, 0x8a, 0x22, 0xa9, 0xb6, + 0x6c, 0x51, 0x92, 0x4d, 0x2a, 0x5a, 0x5b, 0xf6, 0x7a, 0x9d, 0x85, 0xc9, 0x21, 0x29, 0x8d, 0x45, + 0x8a, 0x54, 0x0d, 0x25, 0x79, 0xbd, 0x80, 0x81, 0xe6, 0x4c, 0x71, 0xa6, 0xcd, 0x9e, 0xee, 0x71, + 0x77, 0x8f, 0x34, 0x34, 0x90, 0x17, 0xc8, 0x45, 0xb0, 0x8f, 0x90, 0x5c, 0xe5, 0x3a, 0x40, 0x1e, + 0x62, 0x81, 0x00, 0xc1, 0x5e, 0x2c, 0x90, 0xbd, 0x4a, 0x16, 0xf6, 0x23, 0x04, 0xc8, 0x55, 0x82, + 0x2c, 0xaa, 0xba, 0xaa, 0xa7, 0xab, 0xba, 0xba, 0xe9, 0xbf, 0xbd, 0x21, 0xa6, 0x4e, 0x9d, 0xf3, + 0x55, 0x75, 0xfd, 0x9c, 0x3e, 0x5f, 0xd5, 0x69, 0xc2, 0xd4, 0xd7, 0x03, 0x12, 0x5c, 0x6c, 0xf6, + 0x03, 0x3f, 0xf2, 0xd1, 0x38, 0x2b, 0x2c, 0xd7, 0xc2, 0xc8, 0x8e, 0x06, 0x61, 0x2c, 0x5c, 0x06, + 0xd7, 0x6f, 0x9d, 0xf3, 0xdf, 0xd5, 0x68, 0xe8, 0xf1, 0x9f, 0xb3, 0x91, 0xd3, 0x23, 0x61, 0x64, + 0xf7, 0xfa, 0x42, 0x40, 0xad, 0x42, 0xc7, 0x3b, 0xf3, 0x85, 0x61, 0xdf, 0xb5, 0x85, 0xf6, 0x7b, + 0x1d, 0x27, 0xea, 0x0e, 0x4e, 0x37, 0x5b, 0x7e, 0x6f, 0xab, 0xe3, 0x77, 0xfc, 0x2d, 0x26, 0x3e, + 0x1d, 0x9c, 0xb1, 0x12, 0x2b, 0xb0, 0x5f, 0x5c, 0x7d, 0xad, 0xe3, 0xfb, 0x1d, 0x97, 0x8c, 0xb4, + 0x94, 0xc6, 0xac, 0xdb, 0x50, 0x7b, 0x4e, 0xfb, 0x8a, 0xc9, 0xd7, 0x03, 0x12, 0x46, 0x68, 0x1e, + 0xc6, 0x59, 0xd9, 0x2c, 0xad, 0x97, 0x36, 0xaa, 0x38, 0x2e, 0x58, 0xcf, 0x60, 0xb1, 0xd9, 0xf5, + 0xdf, 0x1c, 0x07, 0x7e, 0x8b, 0x84, 0xe1, 0x81, 0x13, 0x46, 0x42, 0x7f, 0x11, 0x26, 0x4e, 0x88, + 0x67, 0x7b, 0x11, 0x37, 0xe0, 0x25, 0xb4, 0x02, 0xd5, 0xe6, 0x45, 0xc8, 0xab, 0xae, 0xae, 0x97, + 0x36, 0x2a, 0x78, 0x24, 0xb0, 0x5e, 0xc1, 0xb5, 0xe6, 0x85, 0xd7, 0xaa, 0xfb, 0xbd, 0x9e, 0x93, + 0x40, 0xed, 0xc0, 0xcc, 0x81, 0x1d, 0x91, 0x30, 0x8a, 0xc5, 0x27, 0x4d, 0x06, 0x39, 0xf5, 0x70, + 0x7e, 0x73, 0xd4, 0xe9, 0x13, 0xf1, 0x6b, 0xa7, 0xfc, 0xfb, 0xff, 0x5c, 0xbb, 0x82, 0x15, 0x0b, + 0xeb, 0x0b, 0x40, 0x69, 0xe0, 0xb0, 0xef, 0x7b, 0x21, 0x41, 0xbb, 0x30, 0x5b, 0x1f, 0x04, 0x01, + 0xf1, 0x7e, 0x08, 0xb4, 0x6a, 0x62, 0x21, 0x30, 0x1e, 0x93, 0x48, 0xea, 0xb3, 0xf5, 0x1b, 0xb8, + 0x96, 0x92, 0xfd, 0xac, 0xcd, 0x6d, 0xc1, 0x42, 0xdd, 0x0f, 0xc8, 0xee, 0xa0, 0xd7, 0xaf, 0xfb, + 0xde, 0x99, 0xd3, 0x49, 0x0d, 0xf9, 0x76, 0x2b, 0x72, 0x7c, 0x4f, 0x0c, 0x79, 0x5c, 0xb2, 0x4c, + 0x58, 0x54, 0x0d, 0xe2, 0x0e, 0x59, 0x37, 0xe0, 0xfa, 0x63, 0x12, 0x1d, 0xd3, 0x09, 0x6f, 0xf9, + 0xee, 0x4b, 0x12, 0x84, 0x8e, 0xef, 0x89, 0x47, 0x78, 0x04, 0xcb, 0xba, 0x4a, 0xfe, 0x2c, 0x26, + 0x4c, 0x72, 0x11, 0x6b, 0x6d, 0x0c, 0x8b, 0xa2, 0xf5, 0x01, 0x5c, 0x6f, 0xe6, 0x81, 0x16, 0x98, + 0x3d, 0x82, 0xe5, 0xe6, 0x8f, 0x69, 0xee, 0x5d, 0x98, 0xc1, 0x03, 0xef, 0xc4, 0x0e, 0xcf, 0x45, + 0x1b, 0xcb, 0x50, 0xa1, 0xc5, 0xba, 0xdf, 0x26, 0x4c, 0x79, 0x1c, 0x27, 0x65, 0xeb, 0x2e, 0xcc, + 0x26, 0xda, 0x1c, 0x7a, 0x11, 0x26, 0x30, 0x09, 0x07, 0x6e, 0xb2, 0x52, 0xe3, 0x12, 0x1d, 0x36, + 0xfa, 0xfc, 0x4e, 0x9f, 0xb8, 0x8e, 0x47, 0x1a, 0xde, 0x99, 0x2f, 0x46, 0x66, 0x0b, 0x96, 0x32, + 0x35, 0x1c, 0x6c, 0x1e, 0xc6, 0xeb, 0xfe, 0x80, 0xaf, 0xfa, 0x31, 0x1c, 0x17, 0xac, 0xff, 0x5d, + 0x86, 0x49, 0xd1, 0xbb, 0x15, 0xa8, 0xf2, 0x9f, 0x8d, 0x5d, 0xa6, 0x55, 0xc6, 0x23, 0x01, 0xda, + 0x84, 0x6a, 0xbd, 0xd7, 0x3e, 0x24, 0x51, 0xd7, 0x6f, 0xb3, 0xed, 0x31, 0xf3, 0xd0, 0xd8, 0x8c, + 0x3d, 0x48, 0x22, 0xc7, 0x23, 0x15, 0xf4, 0xa1, 0xbc, 0x4d, 0xcd, 0x31, 0xb6, 0x9e, 0xe6, 0xb8, + 0x49, 0xba, 0x0a, 0xcb, 0xfb, 0xf9, 0x45, 0xde, 0xce, 0x35, 0xcb, 0x0c, 0xe2, 0x26, 0x87, 0xd0, + 0x2b, 0xe1, 0xbc, 0x6d, 0x7f, 0x00, 0x73, 0xdb, 0x6e, 0x44, 0x82, 0xed, 0x56, 0x8b, 0x3e, 0xb9, + 0xc0, 0x1c, 0x67, 0x98, 0xcb, 0x1c, 0x53, 0xa3, 0x81, 0x75, 0x66, 0xe8, 0x53, 0x98, 0x7d, 0xea, + 0xb8, 0x6e, 0xdd, 0xf7, 0xc4, 0x02, 0x32, 0x27, 0x18, 0xd2, 0x22, 0x47, 0x52, 0x6a, 0xb1, 0xaa, + 0x8e, 0xea, 0x60, 0x9c, 0x04, 0x76, 0x8b, 0x34, 0xfb, 0x76, 0x02, 0x31, 0xc9, 0x20, 0x96, 0x38, + 0x84, 0x5a, 0x8d, 0x33, 0x06, 0xa8, 0x01, 0xe8, 0x31, 0x89, 0x0e, 0xfc, 0xd6, 0x79, 0x6a, 0x15, + 0x98, 0x15, 0x06, 0x73, 0x9d, 0xc3, 0x64, 0x15, 0xb0, 0xc6, 0x08, 0xed, 0x33, 0xbf, 0x70, 0x32, + 0xf4, 0xd2, 0x48, 0x55, 0x86, 0x64, 0x8e, 0x90, 0xe4, 0x7a, 0x9c, 0x35, 0xa1, 0xe3, 0x4c, 0xfd, + 0x8b, 0xdd, 0xea, 0xa6, 0x57, 0xa6, 0x09, 0xd2, 0x38, 0x6b, 0x34, 0xb0, 0xce, 0x0c, 0x7d, 0x04, + 0xd0, 0xbc, 0x68, 0x79, 0xb1, 0x8b, 0x31, 0xa7, 0xa4, 0xee, 0x64, 0xfc, 0x31, 0x4e, 0xe9, 0xa2, + 0x0f, 0xa0, 0x9a, 0xf8, 0x39, 0xb3, 0x26, 0x0d, 0xac, 0xea, 0x13, 0xf1, 0x48, 0x13, 0x1d, 0xb3, + 0x11, 0x55, 0x36, 0xbb, 0x39, 0xcd, 0xec, 0xd7, 0x47, 0xf6, 0x7a, 0x27, 0x82, 0x35, 0xb6, 0x14, + 0x31, 0xeb, 0x3e, 0xcc, 0x19, 0x09, 0xb1, 0x99, 0x8f, 0x98, 0xad, 0x42, 0xbb, 0x30, 0x23, 0xbb, + 0x4d, 0x73, 0x96, 0xa1, 0xad, 0x88, 0xfd, 0xa8, 0x73, 0xc2, 0x58, 0xb1, 0x41, 0x5b, 0x30, 0xc9, + 0x1d, 0x8e, 0x69, 0x30, 0xf3, 0x05, 0x6e, 0x2e, 0x3b, 0x2d, 0x2c, 0xb4, 0xd0, 0x6f, 0x60, 0x01, + 0x93, 0x9e, 0xff, 0x9a, 0xd0, 0xbf, 0x11, 0xa1, 0x0b, 0xe8, 0xc4, 0x3e, 0x75, 0x89, 0x79, 0x8d, + 0x99, 0xbf, 0x25, 0xcc, 0x75, 0x3a, 0x02, 0x4c, 0x8f, 0x80, 0xb6, 0x61, 0x9a, 0x2e, 0x49, 0xf6, + 0x66, 0xdc, 0x71, 0xbc, 0xb6, 0x89, 0x18, 0xe4, 0x8d, 0xd4, 0x12, 0x4e, 0xea, 0x04, 0x94, 0x6c, + 0x81, 0x3e, 0x03, 0xe3, 0x85, 0x17, 0x0e, 0x4e, 0xc3, 0x56, 0xe0, 0x9c, 0x92, 0xb8, 0x63, 0x73, + 0x0c, 0x65, 0x95, 0xa3, 0xa8, 0xd5, 0xc9, 0xb6, 0x52, 0x2b, 0xd2, 0x6b, 0x78, 0xd7, 0x8e, 0x6c, + 0xb1, 0x86, 0xe7, 0xb5, 0x6b, 0x38, 0xa5, 0x81, 0x75, 0x66, 0x1c, 0xad, 0x49, 0x43, 0xa4, 0xf4, + 0x8e, 0x58, 0x50, 0xd1, 0x54, 0x0d, 0xac, 0x33, 0xa3, 0xee, 0x51, 0xef, 0xfc, 0xcd, 0x45, 0xc9, + 0x3d, 0xea, 0x95, 0x70, 0x8e, 0x31, 0x85, 0x3d, 0x74, 0x3a, 0x81, 0x1d, 0x11, 0xea, 0xa4, 0xf6, + 0x03, 0xbf, 0x27, 0x60, 0x97, 0x24, 0x58, 0xbd, 0x12, 0xce, 0x31, 0x46, 0x47, 0x30, 0x9f, 0xaa, + 0x39, 0x49, 0xfa, 0x6a, 0x4a, 0xf3, 0xab, 0x53, 0xc1, 0x5a, 0x43, 0x74, 0x0a, 0x26, 0x26, 0xae, + 0x6f, 0xb7, 0xb7, 0x07, 0x91, 0xdf, 0xf0, 0x5a, 0x01, 0xe9, 0xd1, 0x10, 0x84, 0x8e, 0xb9, 0x79, + 0x9d, 0x81, 0xbe, 0x93, 0xac, 0x43, 0xbd, 0x9a, 0xc0, 0xcf, 0xc5, 0xa1, 0xae, 0xb9, 0x1e, 0xb9, + 0x98, 0xd8, 0x6d, 0x12, 0x88, 0x0e, 0x2f, 0x4b, 0x1e, 0x44, 0xad, 0xc6, 0x19, 0x03, 0x74, 0x08, + 0xb3, 0x8f, 0x49, 0x84, 0x49, 0xdf, 0x75, 0x5a, 0x76, 0xfc, 0xe6, 0xbd, 0xa1, 0x4e, 0x50, 0xba, + 0x96, 0xdb, 0x89, 0xd8, 0x4a, 0xa9, 0xa5, 0x8b, 0x08, 0x93, 0x90, 0x44, 0x4d, 0x12, 0xa6, 0xdc, + 0x83, 0xb9, 0x22, 0x2d, 0x22, 0x8d, 0x06, 0xd6, 0x99, 0xa1, 0x03, 0xb8, 0xf6, 0xd8, 0x3f, 0xb4, + 0x87, 0xf4, 0x3d, 0x19, 0x0a, 0xac, 0x9b, 0xb2, 0xb3, 0x57, 0xeb, 0x79, 0xcf, 0xb2, 0x86, 0x1c, + 0x8d, 0xf4, 0x0e, 0x9c, 0x91, 0x4f, 0x35, 0x57, 0x55, 0x34, 0xb9, 0x3e, 0x85, 0x26, 0x57, 0xa0, + 0x2f, 0x61, 0x69, 0xdf, 0x71, 0x49, 0x93, 0x04, 0xaf, 0x9d, 0x16, 0x49, 0x4f, 0x99, 0xb9, 0x26, + 0xed, 0xe7, 0x1c, 0x2d, 0x8e, 0x9c, 0x07, 0x82, 0x7a, 0xb0, 0xa2, 0x56, 0xed, 0xbd, 0x76, 0x5a, + 0x49, 0xc7, 0xd7, 0x25, 0x6f, 0x56, 0xa4, 0xca, 0x5b, 0x2a, 0x84, 0x43, 0x2f, 0x60, 0xfe, 0x90, + 0x44, 0x76, 0xdb, 0x8e, 0x6c, 0xe9, 0x59, 0x6e, 0xc9, 0x3b, 0x40, 0xa3, 0xc2, 0xe1, 0xb5, 0xe6, + 0xe8, 0x08, 0xd0, 0x63, 0xff, 0x71, 0xfd, 0x98, 0x04, 0x2d, 0x32, 0x8a, 0x66, 0x2c, 0xf9, 0xcd, + 0x9f, 0x51, 0xe0, 0x90, 0x1a, 0x53, 0x1a, 0x4a, 0xec, 0xdb, 0x03, 0x37, 0x6a, 0x78, 0x5f, 0x91, + 0xd1, 0x60, 0xbc, 0x25, 0x01, 0x66, 0x15, 0xb0, 0xc6, 0x08, 0xfd, 0x16, 0x16, 0xeb, 0x91, 0x7b, + 0xe8, 0x33, 0x67, 0xca, 0x1c, 0x98, 0x80, 0xbb, 0x2d, 0xed, 0x00, 0xbd, 0x12, 0xef, 0x63, 0x0e, + 0x04, 0xfa, 0x12, 0xae, 0xbf, 0xf2, 0x83, 0xf3, 0xb0, 0x6f, 0xb7, 0xc8, 0x49, 0x37, 0x20, 0x61, + 0xd7, 0x77, 0xc5, 0x3b, 0xc1, 0x7c, 0x5b, 0x7a, 0xab, 0xe6, 0xea, 0xe1, 0x7c, 0x08, 0xd4, 0x83, + 0xd5, 0x7a, 0xe4, 0x1e, 0x07, 0xe4, 0x8c, 0x44, 0xad, 0xee, 0x91, 0xd7, 0x14, 0xaf, 0x86, 0xa4, + 0x91, 0x77, 0x58, 0x23, 0x6f, 0x8f, 0x1e, 0xa2, 0x40, 0x19, 0x5f, 0x02, 0x86, 0x7e, 0x0b, 0x66, + 0xa3, 0x59, 0x3f, 0xde, 0x0d, 0x6c, 0xc7, 0xab, 0xfb, 0x5e, 0x38, 0xe8, 0x8d, 0x7c, 0xce, 0x1d, + 0xd6, 0xd0, 0x1a, 0x6f, 0x28, 0x4f, 0x0d, 0xe7, 0x02, 0xa0, 0x3e, 0xdc, 0x6c, 0xb4, 0xc8, 0x29, + 0x09, 0x3a, 0x3c, 0xb0, 0x7a, 0x6d, 0xbb, 0x4e, 0xdb, 0x8e, 0x92, 0x45, 0xb8, 0xc1, 0x5a, 0xb8, + 0x2d, 0x5a, 0x28, 0xd2, 0xe5, 0xd3, 0x52, 0x0c, 0x88, 0x3a, 0xb0, 0x7c, 0xe8, 0x7b, 0x1d, 0x7f, + 0x77, 0xa7, 0xee, 0x3a, 0x6c, 0x75, 0x45, 0x4e, 0x90, 0x34, 0x77, 0x97, 0x35, 0x77, 0x4b, 0xac, + 0xf9, 0x5c, 0x45, 0xde, 0x56, 0x01, 0x14, 0x5d, 0x06, 0x98, 0x9c, 0xd1, 0xb9, 0xe3, 0xae, 0x6d, + 0x7b, 0x10, 0x75, 0x45, 0x3b, 0xf7, 0xa4, 0x65, 0x90, 0xab, 0x87, 0xf3, 0x21, 0x50, 0x3b, 0x26, + 0xa0, 0x17, 0x51, 0xd7, 0xf7, 0x5e, 0xb4, 0xcf, 0x9a, 0xec, 0x58, 0x44, 0xe0, 0xdf, 0xcf, 0x84, + 0x83, 0x5a, 0x3d, 0xfe, 0x18, 0xf9, 0x40, 0xd6, 0x3e, 0x2c, 0x65, 0xe8, 0x0a, 0xe7, 0x6b, 0xf7, + 0xa1, 0xc2, 0xbb, 0x15, 0x9a, 0xa5, 0xf5, 0xb1, 0x8d, 0xa9, 0x87, 0xb3, 0x9b, 0xfc, 0x70, 0x46, + 0x38, 0xf3, 0x44, 0xc1, 0xfa, 0xbf, 0x1b, 0x50, 0x49, 0x2c, 0x7f, 0x5e, 0x1e, 0x37, 0x0f, 0xe3, + 0x7b, 0x41, 0xe0, 0x07, 0x8c, 0xc0, 0xd5, 0x70, 0x5c, 0x40, 0x9f, 0xe7, 0x76, 0x9c, 0xb3, 0xb4, + 0xd5, 0x3c, 0x96, 0x16, 0x6b, 0xe1, 0xdc, 0xe7, 0x3e, 0x82, 0x79, 0x99, 0x70, 0x71, 0xd8, 0x71, + 0xc9, 0x5f, 0xea, 0x54, 0xb0, 0xd6, 0x90, 0xbe, 0xcd, 0x47, 0xdc, 0x8b, 0x83, 0x4d, 0x48, 0x6f, + 0x73, 0xb5, 0x1a, 0x67, 0x0c, 0x28, 0x3b, 0x4a, 0x91, 0x2f, 0x8e, 0x32, 0x29, 0xbd, 0xe2, 0x32, + 0xf5, 0x38, 0x6b, 0xc2, 0x63, 0xc1, 0x11, 0xf7, 0xe2, 0x48, 0x15, 0x35, 0x16, 0x54, 0x35, 0xb0, + 0xce, 0x8c, 0xd3, 0xbf, 0x84, 0x80, 0x71, 0xb0, 0xaa, 0x4a, 0xff, 0x14, 0x05, 0xac, 0x31, 0xa2, + 0xc3, 0x2e, 0xf3, 0x2f, 0x0e, 0x06, 0x6a, 0x20, 0x9e, 0x51, 0xc1, 0x5a, 0x43, 0xf4, 0x4b, 0xca, + 0xdc, 0x04, 0x41, 0xe3, 0xcc, 0xed, 0xba, 0x86, 0xb9, 0x71, 0x90, 0x94, 0x32, 0x7a, 0x94, 0xa5, + 0x6e, 0x66, 0x96, 0xba, 0x71, 0xc3, 0x14, 0x77, 0x7b, 0x5e, 0xc0, 0xdd, 0x6e, 0x15, 0x70, 0xb7, + 0xd4, 0xb0, 0xa8, 0x54, 0xeb, 0x79, 0x01, 0x79, 0xbb, 0x55, 0x40, 0xde, 0x04, 0xa4, 0x86, 0xbd, + 0xed, 0xe5, 0xb0, 0xb7, 0x9b, 0x39, 0xec, 0x8d, 0x43, 0xa9, 0xf4, 0xed, 0x81, 0x4a, 0xdf, 0x16, + 0x55, 0xfa, 0xc6, 0x0d, 0x13, 0xfe, 0xf6, 0x45, 0x31, 0x7f, 0xbb, 0x5d, 0xcc, 0xdf, 0x38, 0x5a, + 0x0e, 0x81, 0xdb, 0xd1, 0x13, 0xb8, 0x15, 0x3d, 0x81, 0xe3, 0x58, 0x0a, 0x83, 0x7b, 0x9a, 0xcb, + 0xe0, 0xd6, 0x72, 0x19, 0x9c, 0xd8, 0xb0, 0x19, 0x0a, 0x97, 0x5a, 0xcf, 0x31, 0x17, 0xe3, 0xeb, + 0x79, 0x5e, 0xbb, 0x9e, 0xd3, 0x2a, 0x58, 0x6b, 0xc8, 0x01, 0x53, 0x74, 0x8c, 0x03, 0x2e, 0xa8, + 0x80, 0x19, 0x15, 0xac, 0x35, 0xa4, 0x2e, 0x34, 0xe7, 0xac, 0x8e, 0x33, 0xb9, 0xd5, 0x3c, 0x26, + 0x27, 0x5c, 0x68, 0xde, 0x51, 0xdf, 0xe7, 0xb0, 0x94, 0xa1, 0x63, 0x1c, 0x79, 0x49, 0x42, 0xce, + 0xd1, 0xc2, 0x79, 0xe6, 0x08, 0xc3, 0x82, 0xc2, 0xca, 0x38, 0xae, 0x29, 0x4d, 0xb7, 0x56, 0x07, + 0xeb, 0x4d, 0x51, 0xeb, 0x52, 0x46, 0x77, 0xe7, 0x52, 0x46, 0xc7, 0x5b, 0xc8, 0xa7, 0x74, 0xfb, + 0x70, 0x2d, 0xc5, 0xd0, 0x78, 0xa7, 0x97, 0x25, 0xd7, 0x92, 0xa9, 0xc7, 0x59, 0x13, 0xf4, 0x2c, + 0x8f, 0xd5, 0xad, 0xe6, 0xb1, 0xba, 0xd8, 0x30, 0x8f, 0xd6, 0x1d, 0xc1, 0xbc, 0xcc, 0xcf, 0x78, + 0xd7, 0x56, 0xa4, 0x55, 0xa5, 0x53, 0xc1, 0x5a, 0xc3, 0x98, 0x17, 0x8c, 0x08, 0x1a, 0x87, 0xbb, + 0xa9, 0xf0, 0x02, 0x55, 0x61, 0xc4, 0x0b, 0xd4, 0x1a, 0x0e, 0x98, 0x70, 0x34, 0x0e, 0xb8, 0xaa, + 0x02, 0x2a, 0x0a, 0x29, 0x40, 0xa5, 0x06, 0xd9, 0x60, 0x66, 0xa9, 0x19, 0x87, 0x5d, 0x93, 0xb6, + 0x7b, 0x9e, 0x1a, 0x07, 0xcf, 0x85, 0xa1, 0x71, 0x6f, 0x0e, 0x27, 0xe3, 0xed, 0xac, 0x4b, 0x1e, + 0xaf, 0x50, 0x57, 0xc4, 0xbd, 0x85, 0x4a, 0xe8, 0x73, 0x58, 0x50, 0x68, 0x1a, 0x6f, 0xe9, 0x96, + 0xbc, 0x31, 0x74, 0x3a, 0xbc, 0x05, 0x3d, 0x00, 0xc2, 0x30, 0x27, 0xb1, 0x35, 0x8e, 0x6b, 0xc9, + 0x11, 0x43, 0x56, 0x83, 0xa3, 0xea, 0x8c, 0x69, 0x14, 0x22, 0xd1, 0x36, 0x8e, 0xf9, 0x96, 0x84, + 0xa9, 0xd1, 0xc0, 0x3a, 0x33, 0x4a, 0xd8, 0x33, 0x5c, 0x8d, 0x23, 0xde, 0x96, 0xf6, 0x46, 0x8e, + 0x96, 0x20, 0xec, 0x39, 0xd5, 0xc8, 0x86, 0x65, 0x1d, 0x5d, 0xe3, 0x4d, 0xbc, 0x2d, 0xbd, 0x8b, + 0xf3, 0x15, 0x71, 0x01, 0x48, 0x7c, 0x4c, 0xe5, 0x25, 0x17, 0x5c, 0x09, 0xf8, 0x3b, 0xca, 0x31, + 0x55, 0x56, 0x05, 0x6b, 0x0d, 0x51, 0x1f, 0xd6, 0x72, 0x89, 0x1f, 0xc7, 0xbe, 0x23, 0x9d, 0x56, + 0x5d, 0xa2, 0x8d, 0x2f, 0x83, 0xa3, 0x84, 0x48, 0xc3, 0x03, 0x79, 0x5b, 0x1b, 0x12, 0x61, 0xc9, + 0xd5, 0xc3, 0xf9, 0x10, 0x28, 0x84, 0xd5, 0x3c, 0xea, 0xc7, 0x1b, 0xb9, 0x2b, 0xf1, 0xe2, 0x62, + 0x65, 0x3e, 0xe7, 0x97, 0x40, 0xa2, 0xaf, 0xe0, 0x86, 0x96, 0x03, 0xf2, 0x16, 0x63, 0x9e, 0x67, + 0x15, 0xf1, 0x49, 0xa9, 0xb9, 0x22, 0x30, 0xba, 0xcc, 0x74, 0x74, 0x90, 0x37, 0x75, 0x5f, 0x5a, + 0x66, 0xf9, 0x8a, 0xb8, 0x00, 0x04, 0xbd, 0x8a, 0x03, 0x54, 0x99, 0x0b, 0x9a, 0xef, 0x66, 0x02, + 0x54, 0x95, 0x2c, 0x2a, 0x3e, 0x35, 0xa3, 0x21, 0xae, 0x4b, 0xf5, 0x24, 0xf3, 0xcf, 0x93, 0xf1, + 0x7d, 0xa9, 0x1e, 0x15, 0x2d, 0xc2, 0x44, 0xfd, 0xd9, 0x8b, 0x17, 0x9c, 0x2b, 0x56, 0x31, 0x2f, + 0xa1, 0x65, 0xa8, 0x1c, 0xd8, 0x5e, 0x67, 0x60, 0x77, 0x08, 0xe3, 0x89, 0x55, 0x9c, 0x94, 0x91, + 0x09, 0x93, 0x7b, 0x1e, 0xdd, 0xa9, 0x6d, 0x46, 0x0b, 0x2b, 0x58, 0x14, 0xd1, 0x06, 0xcc, 0x6e, + 0xbb, 0xae, 0xff, 0xe6, 0x85, 0xe7, 0x84, 0xbe, 0x6b, 0x47, 0xa4, 0xcd, 0x08, 0x61, 0x05, 0xab, + 0x62, 0x4a, 0x2c, 0xe9, 0xcb, 0xf5, 0x82, 0x31, 0xbb, 0x0a, 0x8e, 0x0b, 0x68, 0x15, 0x80, 0x31, + 0xcc, 0xba, 0x6b, 0x87, 0x21, 0xe3, 0x69, 0x55, 0x9c, 0x92, 0xc4, 0x77, 0xa2, 0x76, 0xe8, 0x7b, + 0x8c, 0x7d, 0xb1, 0x3b, 0x51, 0x5a, 0xa2, 0xed, 0xaa, 0x51, 0x7a, 0x85, 0xdd, 0xb0, 0xaa, 0x62, + 0xb4, 0x0e, 0x53, 0xdb, 0x3b, 0x8d, 0xba, 0xef, 0x45, 0x81, 0xdd, 0x8a, 0xaf, 0xb8, 0xaa, 0x38, + 0x2d, 0x42, 0xef, 0xc0, 0xcc, 0x76, 0xdb, 0xee, 0x47, 0x24, 0x10, 0x50, 0xc0, 0x94, 0x14, 0x29, + 0xed, 0x6b, 0x73, 0xf7, 0xa9, 0xd0, 0x99, 0x8a, 0xfb, 0x3a, 0x92, 0xa0, 0x8f, 0x60, 0x69, 0x97, + 0x9c, 0x39, 0x9e, 0x13, 0x39, 0xbe, 0xd7, 0x6c, 0x75, 0x49, 0xcf, 0x16, 0xca, 0x35, 0xd6, 0xb7, + 0xbc, 0x6a, 0xf4, 0x00, 0xe6, 0x8e, 0x5d, 0xdb, 0x13, 0x3d, 0x4a, 0x53, 0x99, 0x71, 0xac, 0xab, + 0x42, 0x8f, 0x60, 0xf1, 0xe4, 0xa2, 0x4f, 0x76, 0x09, 0xf5, 0x0b, 0xfd, 0xc8, 0x0f, 0x92, 0x07, + 0x9c, 0x61, 0xfd, 0xca, 0xa9, 0xa5, 0x7d, 0xa4, 0xde, 0xeb, 0x1b, 0xdf, 0x63, 0xe1, 0xee, 0xa9, + 0x1d, 0x12, 0xd1, 0xda, 0x2c, 0x33, 0xcc, 0xab, 0x46, 0xb7, 0x61, 0xfa, 0x95, 0xe3, 0xb5, 0xfd, + 0x37, 0x3b, 0x76, 0xd4, 0xea, 0x92, 0x90, 0xd1, 0x90, 0x71, 0x2c, 0x0b, 0xa9, 0x56, 0x7d, 0xd0, + 0x1b, 0xb8, 0x76, 0xe4, 0xbc, 0x26, 0xdb, 0xad, 0x73, 0x46, 0x36, 0x2a, 0x58, 0x16, 0xd2, 0xde, + 0x1f, 0xda, 0xc3, 0xbd, 0x21, 0x69, 0x0d, 0xe8, 0x60, 0xec, 0x07, 0x76, 0x8f, 0xec, 0x5c, 0x44, + 0x24, 0x64, 0x3c, 0x62, 0x0c, 0xe7, 0xd4, 0xd2, 0x71, 0x3a, 0xb4, 0x87, 0x4f, 0x6c, 0xaf, 0xed, + 0x92, 0x80, 0x9f, 0x26, 0x90, 0x90, 0xb1, 0x86, 0x71, 0xac, 0xab, 0x42, 0xbf, 0x86, 0xe5, 0x43, + 0x7b, 0xc8, 0xcf, 0x08, 0x32, 0x86, 0xf3, 0xcc, 0xb0, 0x40, 0x03, 0x7d, 0x0c, 0xe6, 0xa1, 0x3d, + 0x3c, 0x7a, 0xe3, 0x91, 0x20, 0x63, 0xbd, 0xc0, 0xac, 0x73, 0xeb, 0xe9, 0x7a, 0x39, 0x20, 0x76, + 0x48, 0xf6, 0xfa, 0x7e, 0xab, 0xcb, 0x82, 0xfc, 0x32, 0x4e, 0x49, 0xe8, 0x8e, 0x38, 0xf4, 0xdb, + 0x24, 0x34, 0x97, 0xd6, 0xc7, 0x36, 0xaa, 0x38, 0x2e, 0x20, 0x0b, 0x6a, 0xcf, 0x06, 0xae, 0x7b, + 0xec, 0xbb, 0x4e, 0xcb, 0x21, 0xa1, 0x69, 0xb2, 0x4a, 0x49, 0x66, 0x35, 0xb4, 0x97, 0xdb, 0x2c, + 0xdf, 0x80, 0xa5, 0xaf, 0x34, 0xda, 0xfc, 0xda, 0x3f, 0x29, 0xd3, 0x8d, 0xc4, 0xfd, 0xcf, 0x58, + 0xbc, 0x91, 0xb8, 0x2b, 0xf9, 0x58, 0x7f, 0xfe, 0x42, 0xbb, 0x61, 0x53, 0x79, 0x73, 0xd0, 0xa2, + 0x8f, 0xc3, 0xf0, 0x2a, 0x58, 0x92, 0x59, 0x8d, 0xcc, 0xad, 0x38, 0x5a, 0x81, 0x2a, 0x47, 0xe2, + 0x0e, 0x66, 0x0c, 0x8f, 0x04, 0xe9, 0xe4, 0x89, 0xab, 0x6c, 0x38, 0x52, 0xc9, 0x13, 0xd9, 0x43, + 0x18, 0x13, 0x26, 0xe5, 0xd6, 0x45, 0xd1, 0x3a, 0xc8, 0xde, 0xd8, 0x20, 0x03, 0xc6, 0xea, 0xbd, + 0x36, 0x77, 0x6a, 0xf4, 0x27, 0x93, 0x9c, 0x75, 0xb8, 0x33, 0xa3, 0x3f, 0xd9, 0xe1, 0xd6, 0x30, + 0x0a, 0xec, 0xe4, 0x70, 0x8b, 0x16, 0xac, 0x3b, 0x1a, 0xb2, 0x80, 0x10, 0x94, 0xe9, 0x6f, 0x8e, + 0xc7, 0x7e, 0x5b, 0x9f, 0x5c, 0x76, 0x56, 0x4c, 0x67, 0xe0, 0xd8, 0x8e, 0x22, 0x12, 0xf0, 0x53, + 0xbc, 0x2a, 0x4e, 0xca, 0xd6, 0x07, 0x97, 0xc6, 0x08, 0xda, 0x46, 0xff, 0x58, 0xca, 0x3f, 0x32, + 0xce, 0x0e, 0xf7, 0xb4, 0x32, 0xdc, 0x2c, 0xbe, 0x6a, 0xec, 0x8a, 0xe1, 0xe6, 0x45, 0x5a, 0xf3, + 0x99, 0x7f, 0xfa, 0xcc, 0xee, 0x11, 0xbe, 0x1c, 0x44, 0x91, 0x0e, 0xd1, 0x67, 0xfe, 0x69, 0x63, + 0x97, 0xb9, 0xf1, 0x32, 0x8e, 0x0b, 0xd4, 0xdd, 0xc6, 0x07, 0x01, 0xfb, 0xc4, 0x6b, 0x91, 0x23, + 0xcf, 0x15, 0x6e, 0x5c, 0x15, 0x53, 0x67, 0x8a, 0x89, 0x47, 0xde, 0x8c, 0x14, 0x27, 0x98, 0xa2, + 0x22, 0xb5, 0x3e, 0x28, 0x88, 0x5f, 0x0a, 0x66, 0xfe, 0xf3, 0x6c, 0x1a, 0x85, 0x66, 0xe6, 0xe7, + 0x61, 0x9c, 0x2a, 0x84, 0x7c, 0xee, 0xe3, 0x02, 0x1d, 0xac, 0x24, 0x14, 0x64, 0x8f, 0x3d, 0x86, + 0x47, 0x02, 0xba, 0x0a, 0xb2, 0xe7, 0x77, 0xba, 0x09, 0x99, 0xd7, 0x25, 0x61, 0x58, 0x7f, 0x2a, + 0x41, 0x45, 0xc8, 0x46, 0x03, 0xdf, 0xe6, 0x07, 0xb2, 0xa2, 0x48, 0x01, 0x9f, 0x92, 0x0b, 0xda, + 0xb1, 0xb1, 0x8d, 0x1a, 0x66, 0xbf, 0xd1, 0xbd, 0xd8, 0x92, 0x6e, 0x7f, 0xd6, 0xad, 0x99, 0x87, + 0x33, 0x9b, 0x2c, 0x13, 0x4f, 0x48, 0x71, 0x52, 0x4f, 0xdf, 0x66, 0x4e, 0x88, 0x6d, 0xaf, 0xc3, + 0x0e, 0x63, 0xf8, 0xbb, 0x36, 0x2d, 0x42, 0x77, 0x60, 0xf2, 0x89, 0xef, 0xb6, 0x49, 0x10, 0x9a, + 0xe3, 0xec, 0x1c, 0x79, 0x3a, 0x06, 0x7b, 0x65, 0x3b, 0xd1, 0xc9, 0xd0, 0xc3, 0xa2, 0x96, 0x2a, + 0x52, 0x19, 0x55, 0x9c, 0xd0, 0x2a, 0xf2, 0x5a, 0xeb, 0x4b, 0xed, 0x21, 0x26, 0x7d, 0x94, 0xba, + 0xd7, 0x10, 0xe3, 0xce, 0x7e, 0xa3, 0x5f, 0x40, 0x4d, 0xe8, 0x1d, 0x38, 0x61, 0xc4, 0x1e, 0x73, + 0xea, 0xe1, 0x2c, 0x8f, 0x75, 0x12, 0x08, 0x49, 0xc9, 0x9a, 0xd3, 0xa4, 0xa2, 0x58, 0x5d, 0x98, + 0x3a, 0x19, 0x7a, 0xdf, 0x6f, 0x44, 0xb1, 0xff, 0x26, 0x19, 0x51, 0xfa, 0x1b, 0xdd, 0x87, 0xc9, + 0xa3, 0x7e, 0xc4, 0xce, 0xd2, 0xe3, 0x3c, 0xa4, 0x6b, 0xa3, 0x01, 0xe5, 0x15, 0x58, 0x68, 0x58, + 0xff, 0x51, 0x82, 0x49, 0xde, 0x38, 0xfa, 0x14, 0x2a, 0xf5, 0x80, 0xd8, 0x11, 0xd9, 0x8e, 0x78, + 0x46, 0xdc, 0xf2, 0x66, 0x9c, 0xa0, 0xb8, 0x29, 0x12, 0x14, 0x53, 0x79, 0x71, 0x15, 0x1a, 0xa0, + 0xfd, 0xee, 0xbf, 0xd6, 0x4a, 0x38, 0xb1, 0x42, 0xeb, 0x50, 0xa6, 0xc4, 0x8e, 0xad, 0xbc, 0xa9, + 0x87, 0xb5, 0xcd, 0x68, 0xe8, 0x6d, 0x9e, 0x0c, 0x3d, 0x2a, 0xc3, 0xac, 0x86, 0x3e, 0xca, 0x8b, + 0x90, 0x04, 0x27, 0x43, 0x4f, 0x04, 0x53, 0xbc, 0x88, 0x1e, 0x40, 0x95, 0x8e, 0x39, 0xed, 0x65, + 0x68, 0x96, 0xd9, 0xd0, 0x21, 0x71, 0xda, 0x3c, 0x1a, 0x0b, 0x3c, 0x52, 0x92, 0xf7, 0xff, 0xb8, + 0xb2, 0xff, 0xad, 0x2f, 0x74, 0xe7, 0xc5, 0xda, 0x79, 0x7b, 0xc0, 0x46, 0x5b, 0x99, 0xb6, 0x99, + 0x51, 0xdb, 0x0c, 0x20, 0xad, 0x62, 0x2d, 0x68, 0xf3, 0x7e, 0xac, 0x7f, 0x2a, 0x41, 0x35, 0x11, + 0x52, 0x77, 0xf8, 0xcc, 0x6f, 0x13, 0x1a, 0x8b, 0xf0, 0xe6, 0x92, 0x32, 0x7d, 0x21, 0xd1, 0xdf, + 0x8d, 0x36, 0xdf, 0xa4, 0xbc, 0x44, 0x1f, 0x89, 0x01, 0x30, 0xa3, 0xd8, 0x39, 0x8d, 0x04, 0xb4, + 0xf3, 0x2f, 0x42, 0x1e, 0x64, 0x96, 0x31, 0xfb, 0x4d, 0x65, 0xfb, 0x01, 0x89, 0xaf, 0x0c, 0xca, + 0x98, 0xfd, 0xa6, 0x2d, 0x3f, 0x71, 0x22, 0x6c, 0x47, 0x8e, 0xcf, 0x1c, 0xd0, 0x55, 0x9c, 0x94, + 0xad, 0x67, 0xfa, 0xb3, 0x6f, 0xf4, 0x08, 0xa6, 0x13, 0x21, 0x1b, 0x86, 0xf8, 0x1e, 0x26, 0xb9, + 0x2e, 0x49, 0x0c, 0x64, 0x35, 0xcb, 0x85, 0x95, 0xa2, 0x24, 0x18, 0x3a, 0xe1, 0x8f, 0x03, 0x7f, + 0xd0, 0x4f, 0x5c, 0xb4, 0x28, 0x16, 0x3b, 0x68, 0xf1, 0xa6, 0x1c, 0x93, 0xdf, 0x94, 0x1f, 0xc0, + 0xcd, 0xc2, 0x23, 0x5b, 0x39, 0xf3, 0x6f, 0x5c, 0x64, 0xfe, 0x7d, 0xc6, 0x1e, 0x3a, 0x93, 0x56, + 0xf3, 0x63, 0x3a, 0x67, 0xdd, 0x87, 0x05, 0xed, 0x09, 0x2f, 0x9d, 0x09, 0x76, 0x1a, 0xcc, 0x97, + 0x16, 0xfd, 0x6d, 0x35, 0x61, 0x29, 0x27, 0x13, 0x87, 0x06, 0x48, 0x22, 0xca, 0x4c, 0xae, 0xae, + 0x52, 0x92, 0x82, 0x1e, 0xbc, 0x0f, 0x66, 0xde, 0xe1, 0x70, 0xc1, 0xcb, 0x63, 0x1f, 0x2a, 0x6c, + 0xe6, 0x9e, 0x92, 0x0b, 0xda, 0xd5, 0x63, 0x3b, 0xea, 0x8a, 0xae, 0xd2, 0xdf, 0x74, 0x49, 0x1e, + 0x9d, 0x9d, 0x85, 0x24, 0xce, 0x07, 0x1e, 0xc3, 0xbc, 0x84, 0x66, 0xe0, 0x6a, 0xf3, 0x1b, 0xfe, + 0xc6, 0xb8, 0xda, 0xfc, 0xc6, 0x7a, 0xc4, 0x97, 0x28, 0xf3, 0xde, 0x77, 0xa1, 0x7c, 0x4e, 0x3d, + 0x7a, 0x49, 0x72, 0x75, 0xa2, 0x9e, 0x93, 0x38, 0xa6, 0x62, 0x9d, 0xd0, 0xb7, 0x28, 0x7b, 0xf4, + 0xa4, 0x1b, 0xf3, 0x30, 0xde, 0xf0, 0xda, 0x64, 0x28, 0x26, 0x8b, 0x15, 0xd0, 0xfd, 0x51, 0x47, + 0xb9, 0x23, 0x51, 0x71, 0x71, 0xa2, 0x60, 0xbd, 0xd2, 0x66, 0x2f, 0xa1, 0x4f, 0x33, 0x8d, 0xf1, + 0x2e, 0x26, 0x17, 0x07, 0x72, 0x2d, 0x56, 0xd5, 0xad, 0x23, 0xb8, 0x26, 0x06, 0x35, 0x41, 0xcf, + 0xe9, 0xb0, 0x01, 0x63, 0x4f, 0x1c, 0x91, 0x46, 0x4d, 0x7f, 0xd2, 0xf1, 0xa5, 0xfa, 0x3c, 0xd2, + 0x62, 0xbf, 0xad, 0x2f, 0xf5, 0x87, 0xf4, 0x68, 0x5f, 0xd3, 0x10, 0xef, 0xac, 0x39, 0x3a, 0x12, + 0x95, 0xeb, 0x71, 0xd6, 0xc4, 0xc2, 0xda, 0xcc, 0x2b, 0xf4, 0x2b, 0xa8, 0x25, 0xb2, 0x78, 0x18, + 0xe2, 0xdb, 0xc0, 0x51, 0x16, 0x7b, 0xba, 0x1a, 0x4b, 0xca, 0x7c, 0xdf, 0x64, 0x8f, 0xf3, 0x1f, + 0x42, 0x35, 0x11, 0x26, 0xc9, 0xd3, 0x1a, 0x44, 0x3c, 0x52, 0xb3, 0x9a, 0x30, 0x75, 0x1c, 0x90, + 0xbe, 0x1d, 0x90, 0x66, 0xd4, 0x63, 0x43, 0xc4, 0x22, 0x30, 0xbe, 0x04, 0x59, 0xf8, 0x65, 0xc0, + 0x58, 0xf3, 0xf9, 0x81, 0x88, 0x59, 0x9b, 0xcf, 0x0f, 0xe8, 0x26, 0x39, 0xb6, 0x03, 0xbb, 0x47, + 0xdd, 0x5f, 0xc8, 0x87, 0x33, 0x25, 0xb1, 0xba, 0x60, 0xf0, 0x83, 0xf6, 0x13, 0xd2, 0xeb, 0xc7, + 0xb7, 0x21, 0xcb, 0x50, 0x11, 0xdb, 0x48, 0xf8, 0x5d, 0x51, 0xa6, 0x13, 0xb8, 0xed, 0x3a, 0x76, + 0x12, 0x1b, 0xb1, 0x02, 0x0d, 0xf7, 0x8f, 0xbb, 0x17, 0xa1, 0xd3, 0xb2, 0xdd, 0x54, 0x54, 0x28, + 0xc9, 0xac, 0xff, 0x29, 0xe5, 0x25, 0x8d, 0xb1, 0x43, 0x05, 0xdf, 0xf3, 0x12, 0x27, 0xc2, 0x4b, + 0xe8, 0xfd, 0x24, 0x13, 0x3c, 0xbe, 0x7a, 0x5e, 0xd1, 0xdf, 0x44, 0xc4, 0x3a, 0x22, 0x4f, 0x1c, + 0x7d, 0x0a, 0x37, 0x92, 0x67, 0x89, 0x35, 0x29, 0x63, 0x1e, 0xf4, 0xfb, 0x7e, 0x30, 0x3a, 0x60, + 0x28, 0x52, 0x41, 0xfb, 0xb0, 0x7a, 0x60, 0x87, 0x51, 0xc3, 0x0b, 0x49, 0x10, 0x35, 0x76, 0x35, + 0x20, 0x71, 0xf8, 0x7a, 0x89, 0xd6, 0x67, 0xe5, 0xca, 0x98, 0x51, 0xb6, 0xfe, 0xbd, 0x92, 0x7b, + 0xc3, 0x42, 0x7d, 0xc3, 0xee, 0x0e, 0x1f, 0xe4, 0xab, 0xbb, 0x3b, 0xe8, 0x11, 0xd4, 0x52, 0x73, + 0x1c, 0xf2, 0x57, 0xa9, 0x78, 0x8d, 0xa7, 0xaa, 0xb0, 0xa4, 0x87, 0xee, 0x81, 0x41, 0xfb, 0xb2, + 0x7d, 0x76, 0x46, 0x5a, 0x11, 0x69, 0xb3, 0x90, 0x26, 0xf6, 0x38, 0x19, 0x39, 0xfa, 0x04, 0x66, + 0x68, 0xc8, 0x70, 0x40, 0x5e, 0x13, 0x37, 0x1d, 0x2c, 0xcc, 0x8b, 0x7b, 0xb3, 0x74, 0x25, 0x56, + 0x74, 0xd1, 0x2e, 0xdc, 0x94, 0x15, 0x88, 0x4b, 0x39, 0xa9, 0x3a, 0x34, 0xc5, 0x4a, 0x68, 0x1f, + 0x66, 0xa9, 0x02, 0x3b, 0xd1, 0x20, 0xed, 0x97, 0x76, 0x12, 0x45, 0x2a, 0x53, 0x2c, 0x2b, 0x61, + 0xd5, 0x08, 0x7d, 0x04, 0x4b, 0x8a, 0x68, 0x6f, 0xc8, 0xfb, 0x31, 0xc9, 0xfa, 0x91, 0x57, 0x4d, + 0x7b, 0xd0, 0xbc, 0x08, 0x23, 0xd2, 0x7b, 0x69, 0x07, 0x0e, 0x5d, 0x07, 0xa1, 0x59, 0xd1, 0xf5, + 0x40, 0x56, 0xc2, 0xaa, 0x11, 0xed, 0x81, 0x22, 0x4a, 0x7a, 0x50, 0x8d, 0x7b, 0x90, 0x53, 0x8d, + 0x3e, 0x81, 0xeb, 0x4a, 0xe7, 0x30, 0xe9, 0xbb, 0xf6, 0x05, 0xbb, 0xca, 0x04, 0x66, 0x9b, 0xaf, + 0x40, 0xad, 0x15, 0xe0, 0x94, 0xf5, 0x54, 0x6c, 0x9d, 0xab, 0x80, 0x9e, 0xc0, 0x9a, 0x52, 0xd9, + 0xf4, 0xec, 0x7e, 0xd8, 0xf5, 0xa3, 0x13, 0xdf, 0x3f, 0xb0, 0x83, 0x0e, 0x61, 0x87, 0x4e, 0x15, + 0x7c, 0x99, 0x1a, 0x45, 0x52, 0x3a, 0x99, 0x41, 0x9a, 0x8e, 0x91, 0x2e, 0x51, 0xa3, 0xfb, 0x96, + 0xaf, 0xe9, 0x36, 0x5d, 0xd4, 0x07, 0xbe, 0xd7, 0xa1, 0x6e, 0xa7, 0xde, 0x25, 0xad, 0x73, 0xd2, + 0x66, 0x27, 0x53, 0x15, 0x5c, 0xa4, 0x42, 0x83, 0xbf, 0x7d, 0x7f, 0xe0, 0xc5, 0xcb, 0x7f, 0x36, + 0xce, 0x65, 0x49, 0x04, 0xe8, 0x43, 0x80, 0x64, 0xd3, 0x87, 0xa6, 0xc1, 0x26, 0x7b, 0x49, 0x9e, + 0xec, 0xa4, 0x1e, 0xa7, 0x54, 0xd9, 0x69, 0x99, 0x28, 0x51, 0x77, 0x4c, 0x92, 0x19, 0x8e, 0x8f, + 0xa7, 0x72, 0x6a, 0xa9, 0x57, 0x4c, 0x3b, 0x08, 0x76, 0x3a, 0x55, 0xc6, 0x92, 0x0c, 0x3d, 0x84, + 0xf9, 0x74, 0x39, 0x41, 0x9e, 0x63, 0xc8, 0xda, 0x3a, 0xeb, 0x5f, 0x26, 0xf4, 0x79, 0xb2, 0xb9, + 0x7e, 0x34, 0xf6, 0x32, 0x57, 0x13, 0x2f, 0xb3, 0x0e, 0x53, 0x4d, 0x12, 0xbd, 0xb4, 0x83, 0xd8, + 0xc9, 0x8c, 0xb1, 0xa3, 0x86, 0xb4, 0x28, 0xe3, 0x87, 0xca, 0x3f, 0xc1, 0x0f, 0x8d, 0x7f, 0x6f, + 0x3f, 0x34, 0xf1, 0x03, 0xfc, 0x90, 0xc6, 0x83, 0x4c, 0xfe, 0xcc, 0x1e, 0xa4, 0xf2, 0x83, 0x3d, + 0x48, 0xf5, 0x67, 0xf6, 0x20, 0xf0, 0x13, 0x3c, 0xc8, 0xd4, 0x4f, 0xf2, 0x20, 0xb5, 0xcb, 0x3c, + 0x88, 0xb4, 0xd7, 0xa6, 0x8b, 0xf7, 0xda, 0xcc, 0xf7, 0xdf, 0x6b, 0xea, 0x9e, 0x99, 0xfd, 0x01, + 0x7b, 0xc6, 0x28, 0xd8, 0x33, 0xff, 0x5d, 0x4a, 0x92, 0x11, 0xe4, 0x11, 0xd1, 0xc6, 0x51, 0xeb, + 0x30, 0xfe, 0xd2, 0x76, 0x07, 0x84, 0x87, 0xcf, 0xb0, 0xc9, 0xbe, 0x50, 0xdc, 0x1b, 0xf6, 0x03, + 0x1c, 0x57, 0xb0, 0x48, 0xeb, 0x6b, 0x97, 0x07, 0x3a, 0xf4, 0x27, 0x0b, 0x6d, 0xc3, 0x1d, 0xc7, + 0xe3, 0x01, 0x46, 0x5c, 0xa0, 0x1b, 0x82, 0x6f, 0x10, 0x16, 0x74, 0x3d, 0xa5, 0xfc, 0x26, 0x66, + 0xda, 0x19, 0x39, 0x5a, 0x85, 0x32, 0xa3, 0xad, 0x13, 0xe9, 0x46, 0xa9, 0x04, 0x33, 0x39, 0x7a, + 0x00, 0x73, 0x78, 0xe0, 0x45, 0x4e, 0x8f, 0x34, 0xa3, 0xc0, 0xf1, 0x3a, 0xbb, 0x7e, 0xcf, 0x76, + 0xe2, 0xab, 0x8d, 0x69, 0xac, 0xab, 0xb2, 0x9e, 0xc3, 0xb4, 0xb4, 0x6d, 0xb4, 0x0f, 0x9b, 0x30, + 0xbe, 0x98, 0x25, 0xc5, 0x05, 0xf6, 0xe1, 0xe3, 0xd0, 0x6b, 0xec, 0xc6, 0xee, 0xa1, 0x86, 0x79, + 0xc9, 0xfa, 0x9b, 0x9c, 0xa4, 0x8e, 0x02, 0xe2, 0xf4, 0x2b, 0x58, 0xbb, 0x24, 0xbd, 0x3e, 0xcd, + 0xd5, 0x4a, 0x32, 0x57, 0xb3, 0x60, 0xfd, 0xb2, 0x4c, 0x0e, 0x6b, 0x83, 0x7d, 0xe5, 0xa0, 0xc9, + 0x8f, 0xa7, 0x9e, 0xaf, 0xfe, 0x4c, 0xc4, 0x57, 0xf5, 0x67, 0xfc, 0x93, 0x37, 0x5d, 0xce, 0x45, + 0xce, 0x27, 0x6f, 0xef, 0x69, 0x33, 0xe9, 0xf3, 0x3c, 0xad, 0x75, 0xac, 0xcf, 0xd0, 0xc8, 0x1f, + 0x1c, 0x1a, 0xa0, 0x6f, 0x0f, 0xa2, 0x6e, 0x3c, 0x6d, 0x6c, 0x0a, 0x6a, 0x38, 0x25, 0xb1, 0xb6, + 0x34, 0xc9, 0xf7, 0x34, 0x42, 0x17, 0x22, 0xf1, 0x69, 0xa0, 0x28, 0x5b, 0x0f, 0x74, 0x39, 0x1d, + 0x85, 0x16, 0xbf, 0xd4, 0x64, 0xe4, 0xa3, 0xdb, 0x30, 0x2d, 0x44, 0xf1, 0xdd, 0x4a, 0x3c, 0x2c, + 0xb2, 0xd0, 0xfa, 0x58, 0x97, 0xef, 0xf1, 0x3d, 0x6d, 0xbb, 0xb9, 0xa9, 0xfb, 0x68, 0x8b, 0xef, + 0x84, 0x12, 0x0b, 0xfb, 0x6f, 0xe4, 0x64, 0x5e, 0xa4, 0xb6, 0x86, 0x38, 0xf6, 0x69, 0x3a, 0xdf, + 0x10, 0x4e, 0xbf, 0x47, 0x02, 0x6b, 0x98, 0x9f, 0x44, 0x22, 0x5b, 0x96, 0x14, 0x4b, 0x76, 0x21, + 0x45, 0x0b, 0x75, 0xbb, 0x6f, 0xb7, 0x9c, 0xe8, 0x82, 0x63, 0xcb, 0x42, 0x3a, 0xbb, 0x87, 0x24, + 0x0c, 0xed, 0x4e, 0x72, 0x1e, 0xce, 0x8b, 0xd6, 0x51, 0xf1, 0xe7, 0x03, 0x3f, 0xf8, 0x41, 0xad, + 0xbf, 0xbb, 0x24, 0x59, 0xe5, 0xaf, 0xfc, 0x3c, 0xef, 0xeb, 0xbf, 0x4f, 0x28, 0x6e, 0xd5, 0xfa, + 0xdb, 0x9c, 0x7c, 0x97, 0x6c, 0x77, 0x4a, 0x9a, 0xee, 0x58, 0xff, 0x5f, 0xba, 0x24, 0x33, 0xfd, + 0x92, 0x8b, 0x0c, 0xd6, 0xb9, 0xc8, 0x76, 0xfd, 0x4e, 0x72, 0x14, 0x34, 0x12, 0xd0, 0x5a, 0xea, + 0x06, 0x59, 0xae, 0x87, 0x38, 0x31, 0x4c, 0x04, 0xd4, 0x2b, 0xc4, 0xb9, 0x85, 0xe5, 0x98, 0xef, + 0xc6, 0x0c, 0x79, 0x15, 0x40, 0x84, 0xaf, 0xfc, 0xe4, 0x74, 0x0c, 0xa7, 0x24, 0xf4, 0x0d, 0x25, + 0x1e, 0xf8, 0xc0, 0x6f, 0x31, 0x56, 0xf8, 0xc4, 0x0e, 0xbb, 0xfc, 0x86, 0x5a, 0x5b, 0x47, 0x77, + 0x68, 0x9c, 0x99, 0xda, 0xd8, 0xe5, 0xb7, 0xd5, 0x49, 0xd9, 0x7a, 0x72, 0x59, 0x3a, 0x45, 0x7c, + 0x71, 0xd2, 0xf3, 0x5f, 0x93, 0xf6, 0x9e, 0x17, 0x05, 0x4e, 0xb2, 0xe7, 0x14, 0xa9, 0xb5, 0xa9, + 0xfb, 0x12, 0x84, 0x4e, 0x38, 0x97, 0x70, 0xe7, 0x20, 0x8a, 0xd6, 0x96, 0x36, 0xa1, 0xa8, 0xc0, + 0xe0, 0x40, 0xf7, 0x65, 0x08, 0xf5, 0x97, 0x3c, 0x89, 0x9c, 0xa7, 0x0d, 0xf0, 0x7c, 0x71, 0x71, + 0x3c, 0x41, 0xd8, 0x45, 0x42, 0x1c, 0xa1, 0xa6, 0x24, 0xd6, 0x5d, 0x6d, 0xee, 0x91, 0xf6, 0x62, + 0xe5, 0x5e, 0xde, 0x77, 0x24, 0xd9, 0x1b, 0x1e, 0xeb, 0xbd, 0xdc, 0x24, 0x24, 0x2d, 0x74, 0xaf, + 0xe0, 0x2b, 0x12, 0xb4, 0x01, 0xb3, 0xfc, 0x9b, 0xf6, 0xe4, 0x76, 0x28, 0x7e, 0x85, 0xa9, 0x62, + 0x3a, 0x47, 0xaf, 0x02, 0x27, 0x1a, 0x41, 0xf0, 0xc5, 0xa8, 0x48, 0x2d, 0xaf, 0x28, 0x85, 0xe9, + 0xaf, 0xd0, 0xde, 0x4b, 0x7d, 0x3e, 0x13, 0xfa, 0x35, 0xd4, 0xd2, 0xf2, 0xef, 0xf1, 0x91, 0xbf, + 0xa4, 0x6f, 0xfd, 0x7d, 0xa9, 0xe8, 0xfb, 0x8e, 0x4b, 0x36, 0xad, 0x05, 0x35, 0xfa, 0x4e, 0x25, + 0xec, 0x4c, 0x27, 0xd9, 0xb7, 0x92, 0x8c, 0x06, 0x57, 0xfc, 0x5c, 0x7b, 0x6f, 0xd8, 0x72, 0x07, + 0xa1, 0xf3, 0x9a, 0xf0, 0xf3, 0xee, 0x8c, 0xdc, 0xfa, 0xb0, 0x30, 0x39, 0xa8, 0x20, 0x7a, 0xf9, + 0xe7, 0x51, 0xe4, 0x28, 0x47, 0xc3, 0x3f, 0x32, 0x72, 0xdc, 0x80, 0xd9, 0x67, 0x64, 0x18, 0x9d, + 0x04, 0xb6, 0x17, 0xda, 0xf1, 0xe9, 0x56, 0x7c, 0x91, 0xa3, 0x8a, 0xd1, 0x26, 0xd4, 0x78, 0x50, + 0x17, 0x43, 0x96, 0x33, 0x90, 0x52, 0xbd, 0xf5, 0x6f, 0xa5, 0x82, 0xcf, 0x5c, 0x72, 0xc9, 0xe1, + 0x0a, 0x54, 0x69, 0x8c, 0xd8, 0xf0, 0xfa, 0x83, 0x88, 0xef, 0xc0, 0x91, 0x40, 0x3a, 0x0b, 0x1c, + 0x53, 0xce, 0x02, 0x2d, 0xa8, 0x49, 0x59, 0x4f, 0x65, 0x16, 0xbc, 0x48, 0x32, 0x3a, 0x46, 0x4d, + 0xdb, 0x8d, 0xbf, 0x9c, 0xaf, 0x61, 0xf6, 0x9b, 0x79, 0x7d, 0x36, 0x07, 0xdb, 0xed, 0x76, 0x40, + 0x92, 0xc4, 0x1d, 0x59, 0x68, 0xfd, 0x6b, 0xa9, 0x28, 0xc5, 0xea, 0xc7, 0x47, 0x54, 0xd4, 0x39, + 0xd3, 0x12, 0xf1, 0x22, 0x27, 0x76, 0xbf, 0xfb, 0xb6, 0x33, 0xca, 0x4d, 0xd2, 0xd6, 0xc5, 0x37, + 0xd8, 0xf1, 0x67, 0x40, 0xe4, 0x2b, 0x46, 0x61, 0x45, 0xa2, 0x92, 0x22, 0xbe, 0xf7, 0xc7, 0x89, + 0xd4, 0x27, 0x33, 0xa8, 0xca, 0xff, 0xdd, 0x88, 0x71, 0x05, 0xcd, 0xc1, 0xac, 0xf2, 0x15, 0x8b, + 0x51, 0x42, 0x06, 0xd4, 0xd2, 0xf9, 0x13, 0xc6, 0x55, 0x54, 0x83, 0x8a, 0x48, 0x65, 0x30, 0xc6, + 0xd0, 0x34, 0x54, 0x93, 0x6b, 0x65, 0xa3, 0x8c, 0x66, 0x61, 0x2a, 0x75, 0x97, 0x6a, 0x8c, 0xa3, + 0x19, 0x80, 0xd1, 0x1d, 0x9d, 0x31, 0x41, 0xf1, 0xd2, 0x97, 0x53, 0xc6, 0x24, 0xd5, 0x18, 0x7d, + 0x2c, 0x61, 0x54, 0x28, 0x62, 0xf2, 0x0d, 0x84, 0x51, 0x45, 0x8b, 0xba, 0xaf, 0x20, 0x0c, 0xa0, + 0xf2, 0xec, 0xd7, 0x08, 0xc6, 0x14, 0x42, 0xea, 0xf7, 0x08, 0x46, 0x0d, 0x4d, 0x25, 0x1f, 0x17, + 0x18, 0xd3, 0xe8, 0x7a, 0xce, 0x77, 0x03, 0xc6, 0x0c, 0xba, 0xa6, 0xa4, 0xfd, 0x1b, 0xb3, 0x68, + 0x3e, 0x9b, 0xc5, 0x6f, 0x18, 0xe9, 0xa7, 0xa0, 0x8b, 0xcd, 0xb8, 0xc6, 0x25, 0xc9, 0x59, 0xb8, + 0x81, 0xe8, 0x70, 0x2a, 0x19, 0xed, 0xc6, 0x1c, 0x15, 0x2a, 0x27, 0xad, 0xc6, 0x3c, 0x6d, 0x56, + 0x62, 0x2c, 0xc6, 0x02, 0x5a, 0xc9, 0xcf, 0x22, 0x37, 0x16, 0xe9, 0x10, 0x25, 0x19, 0x1d, 0xc6, + 0x12, 0x6f, 0x29, 0xcd, 0x19, 0x0c, 0x93, 0x76, 0x28, 0x1d, 0xe8, 0x1b, 0xd7, 0xd9, 0x54, 0x1c, + 0x1d, 0x6e, 0x7f, 0x7e, 0x8c, 0x8f, 0xea, 0x4d, 0x63, 0x99, 0x97, 0xf7, 0x0e, 0x0f, 0x1a, 0x87, + 0x8d, 0x13, 0xe3, 0x06, 0x7d, 0x54, 0x35, 0x72, 0x33, 0x56, 0xe8, 0x70, 0x69, 0xe3, 0x39, 0xe3, + 0x26, 0xeb, 0x77, 0x3a, 0x6a, 0x32, 0x56, 0xd9, 0xfc, 0x1f, 0x25, 0x6f, 0x63, 0x63, 0x8d, 0x0a, + 0x52, 0xef, 0x47, 0x63, 0x9d, 0x76, 0x56, 0x79, 0xb3, 0x19, 0xb7, 0xe8, 0x64, 0x66, 0x5f, 0x28, + 0x86, 0x45, 0x1f, 0x22, 0xed, 0xb0, 0x8d, 0xb7, 0xd0, 0x0d, 0xf6, 0x62, 0xd4, 0x65, 0x99, 0x18, + 0xb7, 0xd1, 0x02, 0x5c, 0xcb, 0x24, 0x5d, 0x18, 0x6f, 0xa3, 0x65, 0x58, 0xd4, 0x07, 0x27, 0xc6, + 0x3b, 0x68, 0x09, 0xe6, 0x34, 0x5e, 0xd7, 0xb8, 0x43, 0xbb, 0x94, 0xdd, 0xdc, 0xc6, 0x86, 0x58, + 0x8f, 0x72, 0xf6, 0xa1, 0x71, 0xf7, 0xde, 0x3f, 0x94, 0x24, 0xde, 0x39, 0x3a, 0xf6, 0xa7, 0x23, + 0xa8, 0x54, 0xc4, 0xa4, 0xdf, 0xb8, 0x82, 0xee, 0xc3, 0x1d, 0xa5, 0xaa, 0x79, 0xee, 0xf4, 0x75, + 0x27, 0xd3, 0x46, 0x09, 0xbd, 0x07, 0x77, 0x55, 0x1c, 0x96, 0xa5, 0xa8, 0x55, 0xbf, 0x7a, 0xef, + 0x5d, 0x98, 0xd7, 0x85, 0xe9, 0x08, 0x68, 0xa4, 0xd3, 0xf3, 0xd9, 0x96, 0xaf, 0x40, 0x79, 0xd7, + 0x09, 0xcf, 0x8d, 0xd2, 0xce, 0xc1, 0xef, 0xbf, 0x5d, 0x2d, 0xfd, 0xe1, 0xdb, 0xd5, 0xd2, 0x9f, + 0xbf, 0x5d, 0xbd, 0xf2, 0xbb, 0xef, 0x56, 0xaf, 0xfc, 0xe3, 0x77, 0xab, 0xa5, 0x3f, 0x7c, 0xb7, + 0x7a, 0xe5, 0x4f, 0xdf, 0xad, 0x5e, 0xf9, 0x62, 0x33, 0xf5, 0x4f, 0x8f, 0x7a, 0x76, 0x14, 0x38, + 0x43, 0x3f, 0x70, 0x3a, 0x8e, 0x27, 0x0a, 0x1e, 0xd9, 0xea, 0x9f, 0x77, 0xb6, 0xfa, 0xa7, 0x5b, + 0x8c, 0x1f, 0x9c, 0x4e, 0xb0, 0x6c, 0x82, 0x5f, 0xfc, 0x25, 0x00, 0x00, 0xff, 0xff, 0x0b, 0x43, + 0x9c, 0xb4, 0x94, 0x49, 0x00, 0x00, } func (m *QueryRequest) Marshal() (dAtA []byte, err error) { @@ -6992,6 +7310,18 @@ func (m *Request) MarshalToSizedBuffer(dAtA []byte) (int, error) { _ = i var l int _ = l + { + size, err := m.GetPythonUdfStatusRequest.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintQuery(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x2 + i-- + dAtA[i] = 0xda if m.RefreshSessionAuthRequest != nil { { size, err := m.RefreshSessionAuthRequest.MarshalToSizedBuffer(dAtA[:i]) @@ -7576,6 +7906,18 @@ func (m *Response) MarshalToSizedBuffer(dAtA []byte) (int, error) { _ = i var l int _ = l + { + size, err := m.GetPythonUdfStatus.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintQuery(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x2 + i-- + dAtA[i] = 0xe2 if m.RefreshSessionAuthResponse != nil { { size, err := m.RefreshSessionAuthResponse.MarshalToSizedBuffer(dAtA[:i]) @@ -8112,6 +8454,236 @@ func (m *Response) MarshalToSizedBuffer(dAtA []byte) (int, error) { return len(dAtA) - i, nil } +func (m *GetPythonUdfStatusRequest) Marshal() (dAtA []byte, err error) { + size := m.ProtoSize() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *GetPythonUdfStatusRequest) MarshalTo(dAtA []byte) (int, error) { + size := m.ProtoSize() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *GetPythonUdfStatusRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + return len(dAtA) - i, nil +} + +func (m *GetPythonUdfStatusResponse) Marshal() (dAtA []byte, err error) { + size := m.ProtoSize() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *GetPythonUdfStatusResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.ProtoSize() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *GetPythonUdfStatusResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.NullPolicies) > 0 { + for iNdEx := len(m.NullPolicies) - 1; iNdEx >= 0; iNdEx-- { + i -= len(m.NullPolicies[iNdEx]) + copy(dAtA[i:], m.NullPolicies[iNdEx]) + i = encodeVarintQuery(dAtA, i, uint64(len(m.NullPolicies[iNdEx]))) + i-- + dAtA[i] = 0x1 + i-- + dAtA[i] = 0xc2 + } + } + if len(m.Modes) > 0 { + for iNdEx := len(m.Modes) - 1; iNdEx >= 0; iNdEx-- { + i -= len(m.Modes[iNdEx]) + copy(dAtA[i:], m.Modes[iNdEx]) + i = encodeVarintQuery(dAtA, i, uint64(len(m.Modes[iNdEx]))) + i-- + dAtA[i] = 0x1 + i-- + dAtA[i] = 0xba + } + } + if m.LeaseEpoch != 0 { + i = encodeVarintQuery(dAtA, i, uint64(m.LeaseEpoch)) + i-- + dAtA[i] = 0x1 + i-- + dAtA[i] = 0xb0 + } + if m.MaxOwnerHandlerProcesses != 0 { + i = encodeVarintQuery(dAtA, i, uint64(m.MaxOwnerHandlerProcesses)) + i-- + dAtA[i] = 0x1 + i-- + dAtA[i] = 0xa8 + } + if m.MaxAccountHandlerProcesses != 0 { + i = encodeVarintQuery(dAtA, i, uint64(m.MaxAccountHandlerProcesses)) + i-- + dAtA[i] = 0x1 + i-- + dAtA[i] = 0xa0 + } + if m.MaxHandlerProcesses != 0 { + i = encodeVarintQuery(dAtA, i, uint64(m.MaxHandlerProcesses)) + i-- + dAtA[i] = 0x1 + i-- + dAtA[i] = 0x98 + } + if m.MaxExecutionFrameBytes != 0 { + i = encodeVarintQuery(dAtA, i, uint64(m.MaxExecutionFrameBytes)) + i-- + dAtA[i] = 0x1 + i-- + dAtA[i] = 0x90 + } + if m.CumulativeAck { + i-- + if m.CumulativeAck { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i-- + dAtA[i] = 0x1 + i-- + dAtA[i] = 0x88 + } + if m.WindowBatches != 0 { + i = encodeVarintQuery(dAtA, i, uint64(m.WindowBatches)) + i-- + dAtA[i] = 0x1 + i-- + dAtA[i] = 0x80 + } + if len(m.TimezoneDatabaseVersion) > 0 { + i -= len(m.TimezoneDatabaseVersion) + copy(dAtA[i:], m.TimezoneDatabaseVersion) + i = encodeVarintQuery(dAtA, i, uint64(len(m.TimezoneDatabaseVersion))) + i-- + dAtA[i] = 0x7a + } + if len(m.TypeDescriptorContract) > 0 { + i -= len(m.TypeDescriptorContract) + copy(dAtA[i:], m.TypeDescriptorContract) + i = encodeVarintQuery(dAtA, i, uint64(len(m.TypeDescriptorContract))) + i-- + dAtA[i] = 0x72 + } + if m.PlanContractVersion != 0 { + i = encodeVarintQuery(dAtA, i, uint64(m.PlanContractVersion)) + i-- + dAtA[i] = 0x68 + } + if m.DefinitionSchemaVersion != 0 { + i = encodeVarintQuery(dAtA, i, uint64(m.DefinitionSchemaVersion)) + i-- + dAtA[i] = 0x60 + } + if len(m.SDKVersion) > 0 { + i -= len(m.SDKVersion) + copy(dAtA[i:], m.SDKVersion) + i = encodeVarintQuery(dAtA, i, uint64(len(m.SDKVersion))) + i-- + dAtA[i] = 0x5a + } + if len(m.AdapterVersion) > 0 { + i -= len(m.AdapterVersion) + copy(dAtA[i:], m.AdapterVersion) + i = encodeVarintQuery(dAtA, i, uint64(len(m.AdapterVersion))) + i-- + dAtA[i] = 0x52 + } + if len(m.ABIContract) > 0 { + i -= len(m.ABIContract) + copy(dAtA[i:], m.ABIContract) + i = encodeVarintQuery(dAtA, i, uint64(len(m.ABIContract))) + i-- + dAtA[i] = 0x4a + } + if m.ProtocolVersion != 0 { + i = encodeVarintQuery(dAtA, i, uint64(m.ProtocolVersion)) + i-- + dAtA[i] = 0x40 + } + if len(m.Reason) > 0 { + i -= len(m.Reason) + copy(dAtA[i:], m.Reason) + i = encodeVarintQuery(dAtA, i, uint64(len(m.Reason))) + i-- + dAtA[i] = 0x3a + } + if len(m.ErrorClass) > 0 { + i -= len(m.ErrorClass) + copy(dAtA[i:], m.ErrorClass) + i = encodeVarintQuery(dAtA, i, uint64(len(m.ErrorClass))) + i-- + dAtA[i] = 0x32 + } + if m.Ready { + i-- + if m.Ready { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i-- + dAtA[i] = 0x28 + } + if m.AllowUnisolated { + i-- + if m.AllowUnisolated { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i-- + dAtA[i] = 0x20 + } + if m.Enabled { + i-- + if m.Enabled { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i-- + dAtA[i] = 0x18 + } + if len(m.Language) > 0 { + i -= len(m.Language) + copy(dAtA[i:], m.Language) + i = encodeVarintQuery(dAtA, i, uint64(len(m.Language))) + i-- + dAtA[i] = 0x12 + } + if len(m.CNUUID) > 0 { + i -= len(m.CNUUID) + copy(dAtA[i:], m.CNUUID) + i = encodeVarintQuery(dAtA, i, uint64(len(m.CNUUID))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + func (m *AlterAccountRequest) Marshal() (dAtA []byte, err error) { size := m.ProtoSize() dAtA = make([]byte, size) @@ -8832,12 +9404,12 @@ func (m *TxnInfo) MarshalToSizedBuffer(dAtA []byte) (int, error) { i-- dAtA[i] = 0x12 } - n86, err86 := github_com_gogo_protobuf_types.StdTimeMarshalTo(m.CreateAt, dAtA[i-github_com_gogo_protobuf_types.SizeOfStdTime(m.CreateAt):]) - if err86 != nil { - return 0, err86 + n88, err88 := github_com_gogo_protobuf_types.StdTimeMarshalTo(m.CreateAt, dAtA[i-github_com_gogo_protobuf_types.SizeOfStdTime(m.CreateAt):]) + if err88 != nil { + return 0, err88 } - i -= n86 - i = encodeVarintQuery(dAtA, i, uint64(n86)) + i -= n88 + i = encodeVarintQuery(dAtA, i, uint64(n88)) i-- dAtA[i] = 0xa return len(dAtA) - i, nil @@ -11678,6 +12250,8 @@ func (m *Request) ProtoSize() (n int) { l = m.RefreshSessionAuthRequest.ProtoSize() n += 2 + l + sovQuery(uint64(l)) } + l = m.GetPythonUdfStatusRequest.ProtoSize() + n += 2 + l + sovQuery(uint64(l)) return n } @@ -11852,43 +12426,150 @@ func (m *Response) ProtoSize() (n int) { l = m.RefreshSessionAuthResponse.ProtoSize() n += 2 + l + sovQuery(uint64(l)) } + l = m.GetPythonUdfStatus.ProtoSize() + n += 2 + l + sovQuery(uint64(l)) return n } -func (m *AlterAccountRequest) ProtoSize() (n int) { +func (m *GetPythonUdfStatusRequest) ProtoSize() (n int) { if m == nil { return 0 } var l int _ = l - if m.TenantId != 0 { - n += 1 + sovQuery(uint64(m.TenantId)) - } - l = len(m.Status) - if l > 0 { - n += 1 + l + sovQuery(uint64(l)) - } return n } -func (m *AlterAccountResponse) ProtoSize() (n int) { +func (m *GetPythonUdfStatusResponse) ProtoSize() (n int) { if m == nil { return 0 } var l int _ = l - if m.AlterSuccess { + l = len(m.CNUUID) + if l > 0 { + n += 1 + l + sovQuery(uint64(l)) + } + l = len(m.Language) + if l > 0 { + n += 1 + l + sovQuery(uint64(l)) + } + if m.Enabled { n += 2 } - return n -} - -func (m *KillConnRequest) ProtoSize() (n int) { - if m == nil { - return 0 + if m.AllowUnisolated { + n += 2 } - var l int - _ = l + if m.Ready { + n += 2 + } + l = len(m.ErrorClass) + if l > 0 { + n += 1 + l + sovQuery(uint64(l)) + } + l = len(m.Reason) + if l > 0 { + n += 1 + l + sovQuery(uint64(l)) + } + if m.ProtocolVersion != 0 { + n += 1 + sovQuery(uint64(m.ProtocolVersion)) + } + l = len(m.ABIContract) + if l > 0 { + n += 1 + l + sovQuery(uint64(l)) + } + l = len(m.AdapterVersion) + if l > 0 { + n += 1 + l + sovQuery(uint64(l)) + } + l = len(m.SDKVersion) + if l > 0 { + n += 1 + l + sovQuery(uint64(l)) + } + if m.DefinitionSchemaVersion != 0 { + n += 1 + sovQuery(uint64(m.DefinitionSchemaVersion)) + } + if m.PlanContractVersion != 0 { + n += 1 + sovQuery(uint64(m.PlanContractVersion)) + } + l = len(m.TypeDescriptorContract) + if l > 0 { + n += 1 + l + sovQuery(uint64(l)) + } + l = len(m.TimezoneDatabaseVersion) + if l > 0 { + n += 1 + l + sovQuery(uint64(l)) + } + if m.WindowBatches != 0 { + n += 2 + sovQuery(uint64(m.WindowBatches)) + } + if m.CumulativeAck { + n += 3 + } + if m.MaxExecutionFrameBytes != 0 { + n += 2 + sovQuery(uint64(m.MaxExecutionFrameBytes)) + } + if m.MaxHandlerProcesses != 0 { + n += 2 + sovQuery(uint64(m.MaxHandlerProcesses)) + } + if m.MaxAccountHandlerProcesses != 0 { + n += 2 + sovQuery(uint64(m.MaxAccountHandlerProcesses)) + } + if m.MaxOwnerHandlerProcesses != 0 { + n += 2 + sovQuery(uint64(m.MaxOwnerHandlerProcesses)) + } + if m.LeaseEpoch != 0 { + n += 2 + sovQuery(uint64(m.LeaseEpoch)) + } + if len(m.Modes) > 0 { + for _, s := range m.Modes { + l = len(s) + n += 2 + l + sovQuery(uint64(l)) + } + } + if len(m.NullPolicies) > 0 { + for _, s := range m.NullPolicies { + l = len(s) + n += 2 + l + sovQuery(uint64(l)) + } + } + return n +} + +func (m *AlterAccountRequest) ProtoSize() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.TenantId != 0 { + n += 1 + sovQuery(uint64(m.TenantId)) + } + l = len(m.Status) + if l > 0 { + n += 1 + l + sovQuery(uint64(l)) + } + return n +} + +func (m *AlterAccountResponse) ProtoSize() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.AlterSuccess { + n += 2 + } + return n +} + +func (m *KillConnRequest) ProtoSize() (n int) { + if m == nil { + return 0 + } + var l int + _ = l if m.AccountID != 0 { n += 1 + sovQuery(uint64(m.AccountID)) } @@ -15850,6 +16531,39 @@ func (m *Request) Unmarshal(dAtA []byte) error { return err } iNdEx = postIndex + case 43: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field GetPythonUdfStatusRequest", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthQuery + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthQuery + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.GetPythonUdfStatusRequest.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipQuery(dAtA[iNdEx:]) @@ -17466,6 +18180,742 @@ func (m *Response) Unmarshal(dAtA []byte) error { return err } iNdEx = postIndex + case 44: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field GetPythonUdfStatus", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthQuery + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthQuery + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.GetPythonUdfStatus.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipQuery(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthQuery + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *GetPythonUdfStatusRequest) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: GetPythonUdfStatusRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: GetPythonUdfStatusRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + default: + iNdEx = preIndex + skippy, err := skipQuery(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthQuery + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *GetPythonUdfStatusResponse) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: GetPythonUdfStatusResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: GetPythonUdfStatusResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field CNUUID", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthQuery + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthQuery + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.CNUUID = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Language", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthQuery + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthQuery + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Language = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 3: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Enabled", wireType) + } + var v int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + m.Enabled = bool(v != 0) + case 4: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field AllowUnisolated", wireType) + } + var v int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + m.AllowUnisolated = bool(v != 0) + case 5: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Ready", wireType) + } + var v int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + m.Ready = bool(v != 0) + case 6: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ErrorClass", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthQuery + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthQuery + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.ErrorClass = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 7: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Reason", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthQuery + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthQuery + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Reason = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 8: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field ProtocolVersion", wireType) + } + m.ProtocolVersion = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.ProtocolVersion |= int32(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 9: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ABIContract", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthQuery + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthQuery + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.ABIContract = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 10: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field AdapterVersion", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthQuery + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthQuery + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.AdapterVersion = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 11: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field SDKVersion", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthQuery + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthQuery + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.SDKVersion = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 12: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field DefinitionSchemaVersion", wireType) + } + m.DefinitionSchemaVersion = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.DefinitionSchemaVersion |= int32(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 13: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field PlanContractVersion", wireType) + } + m.PlanContractVersion = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.PlanContractVersion |= int32(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 14: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field TypeDescriptorContract", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthQuery + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthQuery + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.TypeDescriptorContract = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 15: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field TimezoneDatabaseVersion", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthQuery + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthQuery + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.TimezoneDatabaseVersion = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 16: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field WindowBatches", wireType) + } + m.WindowBatches = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.WindowBatches |= int32(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 17: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field CumulativeAck", wireType) + } + var v int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + m.CumulativeAck = bool(v != 0) + case 18: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field MaxExecutionFrameBytes", wireType) + } + m.MaxExecutionFrameBytes = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.MaxExecutionFrameBytes |= int64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 19: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field MaxHandlerProcesses", wireType) + } + m.MaxHandlerProcesses = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.MaxHandlerProcesses |= int32(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 20: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field MaxAccountHandlerProcesses", wireType) + } + m.MaxAccountHandlerProcesses = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.MaxAccountHandlerProcesses |= int32(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 21: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field MaxOwnerHandlerProcesses", wireType) + } + m.MaxOwnerHandlerProcesses = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.MaxOwnerHandlerProcesses |= int32(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 22: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field LeaseEpoch", wireType) + } + m.LeaseEpoch = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.LeaseEpoch |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 23: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Modes", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthQuery + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthQuery + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Modes = append(m.Modes, string(dAtA[iNdEx:postIndex])) + iNdEx = postIndex + case 24: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field NullPolicies", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthQuery + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthQuery + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.NullPolicies = append(m.NullPolicies, string(dAtA[iNdEx:postIndex])) + iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipQuery(dAtA[iNdEx:]) diff --git a/pkg/queryservice/client/query_client.go b/pkg/queryservice/client/query_client.go index eaec510a185fe..4ff2dd104e8be 100644 --- a/pkg/queryservice/client/query_client.go +++ b/pkg/queryservice/client/query_client.go @@ -66,6 +66,7 @@ var methodVersions = map[pb.CmdMethod]int64{ pb.CmdMethod_ISCPDrainConsumer: defines.MORPCVersion4, pb.CmdMethod_IcebergCacheInvalidate: defines.MORPCVersion4, pb.CmdMethod_MongoDBClientRetire: defines.MORPCVersion5, + pb.CmdMethod_GetPythonUdfStatus: defines.MORPCVersion72, } type queryClient struct { diff --git a/pkg/udf/python/gateway.go b/pkg/udf/python/gateway.go index 45ff234ec5a4c..1b4e04bc445c0 100644 --- a/pkg/udf/python/gateway.go +++ b/pkg/udf/python/gateway.go @@ -56,6 +56,7 @@ const ( var errGatewayClosed = errors.New("python udf gateway is closed") var _ udf.RuntimeDefinitionValidator = (*Gateway)(nil) +var _ udf.RuntimeStatusProvider = (*Gateway)(nil) type Gateway struct { cfg ClientConfig @@ -70,6 +71,7 @@ type Gateway struct { admissionMu sync.Mutex capabilityReady bool workerLeaseEpoch uint64 + capability *capabilityResponse closed bool active chan struct{} ledger *protocol.TerminalLedger @@ -185,6 +187,101 @@ func (g *Gateway) CheckLanguageReady(ctx context.Context, language string) error return g.ensureCapabilities(requestCtx, client, true) } +// StatusSnapshot is the control-plane view of the current Python runtime. It +// deliberately reuses CheckLanguageReady, so the status reported for a +// same-Pod CN is the same capability handshake that gates an invocation. The +// action only probes the worker and does not acquire an invocation, handler, +// input credit, or terminal-ledger entry. +func (g *Gateway) StatusSnapshot(ctx context.Context, language string) udf.RuntimeStatusSnapshot { + if ctx == nil { + ctx = context.Background() + } + snapshot := udf.RuntimeStatusSnapshot{ + Language: language, + Enabled: g.cfg.Enabled, + AllowUnisolated: g.cfg.AllowUnisolated, + } + if language != udf.LanguagePython { + snapshot.ErrorClass = udf.RuntimeStatusInvalid + snapshot.Reason = udf.RuntimeStatusReasonInvalidLanguage + return snapshot + } + if !g.cfg.Enabled { + snapshot.ErrorClass = udf.RuntimeStatusDisabled + snapshot.Reason = udf.RuntimeStatusReasonRuntimeDisabled + return snapshot + } + if !g.cfg.AllowUnisolated { + snapshot.ErrorClass = udf.RuntimeStatusNotAllowed + snapshot.Reason = udf.RuntimeStatusReasonUnisolatedNotAllowed + return snapshot + } + + err := g.CheckLanguageReady(ctx, language) + g.mu.Lock() + capability := g.capability + ready := g.capabilityReady && g.workerLeaseEpoch != 0 && !g.closed + if capability != nil { + snapshot.ProtocolVersion = int32(capability.ProtocolVersion) + snapshot.ABIContract = capability.ABIContract + snapshot.AdapterVersion = capability.AdapterVersion + snapshot.SDKVersion = capability.SDKVersion + snapshot.DefinitionSchemaVersion = int32(capability.DefinitionSchemaVersion) + snapshot.PlanContractVersion = int32(capability.PlanContractVersion) + snapshot.TypeDescriptorContract = capability.TypeDescriptorContract + snapshot.TimezoneDatabaseVersion = capability.TimezoneDatabaseVersion + snapshot.WindowBatches = int32(capability.WindowBatches) + snapshot.CumulativeAck = capability.CumulativeAck + snapshot.MaxExecutionFrameBytes = capability.MaxExecutionFrameBytes + snapshot.MaxHandlerProcesses = int32(capability.MaxHandlerProcesses) + snapshot.MaxAccountHandlerProcesses = int32(capability.MaxAccountHandlerProcesses) + snapshot.MaxOwnerHandlerProcesses = int32(capability.MaxOwnerHandlerProcesses) + snapshot.LeaseEpoch = capability.LeaseEpoch + snapshot.Modes = append([]string(nil), capability.Modes...) + snapshot.NullPolicies = append([]string(nil), capability.NullPolicies...) + } + g.mu.Unlock() + + if err == nil && ready { + snapshot.Ready = true + snapshot.ErrorClass = "" + snapshot.Reason = udf.RuntimeStatusReasonReady + return snapshot + } + if err == nil { + snapshot.ErrorClass = udf.RuntimeStatusUnavailable + snapshot.Reason = udf.RuntimeStatusReasonWorkerUnavailable + return snapshot + } + snapshot.ErrorClass, snapshot.Reason = classifyStatusError(err) + return snapshot +} + +func classifyStatusError(err error) (string, string) { + if err == nil { + return "", udf.RuntimeStatusReasonReady + } + if errors.Is(err, context.DeadlineExceeded) { + return udf.RuntimeStatusTimeout, udf.RuntimeStatusReasonRequestTimeout + } + if errors.Is(err, errGatewayClosed) { + return udf.RuntimeStatusClosed, udf.RuntimeStatusReasonRuntimeClosed + } + message := strings.ToLower(err.Error()) + switch { + case strings.Contains(message, "contract does not match"), + strings.Contains(message, "capability set does not match"): + return udf.RuntimeStatusContractMismatch, udf.RuntimeStatusReasonCapabilityMismatch + case strings.Contains(message, "deadline exceeded"), strings.Contains(message, "timeout"): + return udf.RuntimeStatusTimeout, udf.RuntimeStatusReasonRequestTimeout + case strings.Contains(message, "connection"), strings.Contains(message, "transport"), + strings.Contains(message, "flight"), strings.Contains(message, "unavailable"): + return udf.RuntimeStatusUnavailable, udf.RuntimeStatusReasonWorkerUnavailable + default: + return udf.RuntimeStatusInternal, udf.RuntimeStatusReasonRuntimeError + } +} + // ValidateDefinition asks the current worker to compile the exact artifact // that a CREATE or REPLACE is about to publish. This action has no // FunctionRef, invocation lease, handler slot, or ledger entry: a definition @@ -1145,6 +1242,7 @@ func (g *Gateway) ensureCapabilities(ctx context.Context, client flight.FlightSe // success that could authorize a lease-less Flight Action. g.capabilityReady = false g.workerLeaseEpoch = 0 + g.capability = nil } ready := g.capabilityReady closed := g.closed @@ -1203,6 +1301,10 @@ func (g *Gateway) ensureCapabilities(ctx context.Context, client flight.FlightSe if !g.closed { g.capabilityReady = true g.workerLeaseEpoch = response.LeaseEpoch + capability := response + capability.Modes = append([]string(nil), response.Modes...) + capability.NullPolicies = append([]string(nil), response.NullPolicies...) + g.capability = &capability } g.mu.Unlock() return nil @@ -1219,6 +1321,7 @@ func (g *Gateway) invalidateCapabilities() { g.mu.Lock() g.capabilityReady = false g.workerLeaseEpoch = 0 + g.capability = nil g.mu.Unlock() } @@ -1605,13 +1708,37 @@ func (g *Gateway) receiveFinish( if control.LastResultSequence != lastResult { return fmt.Errorf("python udf: invalid Finish result sequence %d, expected %d", control.LastResultSequence, lastResult) } - return g.acknowledgeFinish(ctx, client, "AcknowledgeFinish", protocol.Control{Kind: "AcknowledgeFinish", Tuple: tuple, FinishID: control.FinishID}) + if err := g.acknowledgeFinish(ctx, client, "AcknowledgeFinish", protocol.Control{Kind: "AcknowledgeFinish", Tuple: tuple, FinishID: control.FinishID}); err != nil { + return err + } + // AcknowledgeFinish is accepted by the worker's action RPC before + // the DoExchange callback runs its finally block. The callback owns + // the input reader, handler process, and worker group fence, so the + // action ACK alone is not a safe re-entry boundary. Wait for the + // exchange to reach EOF; Flight reports EOF only after the callback + // has returned and its cleanup owner has completed. + return g.waitExchangeEOF(ctx, stream) default: return fmt.Errorf("python udf: unexpected control %q", control.Kind) } } } +func (g *Gateway) waitExchangeEOF(ctx context.Context, stream flight.FlightService_DoExchangeClient) error { + for { + data, err := stream.Recv() + if err == io.EOF { + return nil + } + if err != nil { + return fmt.Errorf("python udf: wait for exchange completion: %w", err) + } + if data == nil || len(data.DataHeader) != 0 || len(data.DataBody) != 0 || len(data.AppMetadata) != 0 { + return fmt.Errorf("python udf: exchange returned data after Finish ACK") + } + } +} + // acknowledgeFinish retries only the idempotent terminal ACK. A lost ACK // request or response must not replay any STARTED input or handler work. // Once the request budget is exhausted the caller preserves the distinction diff --git a/pkg/udf/python/gateway_test.go b/pkg/udf/python/gateway_test.go index 9803ec6838849..fc47c4d3e48ea 100644 --- a/pkg/udf/python/gateway_test.go +++ b/pkg/udf/python/gateway_test.go @@ -10,6 +10,7 @@ import ( "errors" "io" "strings" + "sync" "testing" "time" @@ -29,6 +30,15 @@ type gatewayResultStream struct { index int } +type gatedGatewayResultStream struct { + grpc.ClientStream + results []*flight.FlightData + index int + release <-chan struct{} + waiting chan<- struct{} + waitOnce sync.Once +} + type finishAckActionStream struct { grpc.ClientStream result *flight.Result @@ -203,6 +213,21 @@ func (s *gatewayResultStream) Send(*flight.FlightData) error { return nil } +func (s *gatedGatewayResultStream) Recv() (*flight.FlightData, error) { + if s.index < len(s.results) { + result := s.results[s.index] + s.index++ + return result, nil + } + s.waitOnce.Do(func() { close(s.waiting) }) + <-s.release + return nil, io.EOF +} + +func (s *gatedGatewayResultStream) Send(*flight.FlightData) error { + return nil +} + func gatewayControl(t *testing.T, kind string, tuple protocol.FencingTuple, fields ...func(*protocol.Control)) *flight.FlightData { t.Helper() control := protocol.Control{Kind: kind, Tuple: tuple} @@ -560,6 +585,42 @@ func TestGatewayFinishRejectsLateHalfStreamControls(t *testing.T) { } } +func TestGatewayFinishWaitsForExchangeEOF(t *testing.T) { + tuple := validInvocation().Tuple + sequence := protocol.Sequence{} + require.NoError(t, sequence.AcceptInput(1)) + require.NoError(t, sequence.EndInput(1)) + require.NoError(t, sequence.AcceptResult(1)) + require.NoError(t, sequence.AcknowledgeResults(1)) + finish := gatewayControl(t, "Finish", tuple, func(value *protocol.Control) { + value.Status = statusOK + value.FinishID = "finish-1" + value.LastSequence = 1 + value.LastResultSequence = 1 + }) + release := make(chan struct{}) + waiting := make(chan struct{}) + stream := &gatedGatewayResultStream{ + results: []*flight.FlightData{finish}, + release: release, + waiting: waiting, + } + gateway := &Gateway{cfg: ClientConfig{RequestTimeout: time.Second}} + done := make(chan error, 1) + go func() { + done <- gateway.receiveFinish(context.Background(), &finishAckFlightClient{}, stream, tuple, 1, 1, &sequence) + }() + + <-waiting + select { + case err := <-done: + require.Failf(t, "Finish returned before the exchange reached EOF", "%v", err) + default: + } + close(release) + require.NoError(t, <-done) +} + func TestGatewayRejectsArrowBodyOnControlFrame(t *testing.T) { tuple := validInvocation().Tuple sequence := protocol.Sequence{} diff --git a/pkg/udf/runtime.go b/pkg/udf/runtime.go index d68bde7f54323..72b3881f83bcc 100644 --- a/pkg/udf/runtime.go +++ b/pkg/udf/runtime.go @@ -300,6 +300,68 @@ type RuntimeReadiness interface { CheckLanguageReady(context.Context, string) error } +// RuntimeStatusSnapshot is the bounded, read-only state exposed to control +// planes that need to observe a language runtime without entering the +// invocation path. A snapshot never contains source, artifacts, secrets, or +// invocation identifiers. The capability fields are a summary of the +// already negotiated worker contract; the runtime remains responsible for +// deciding whether the summary is valid. +type RuntimeStatusSnapshot struct { + Language string + Enabled bool + AllowUnisolated bool + Ready bool + ErrorClass string + Reason string + ProtocolVersion int32 + ABIContract string + AdapterVersion string + SDKVersion string + DefinitionSchemaVersion int32 + PlanContractVersion int32 + TypeDescriptorContract string + TimezoneDatabaseVersion string + WindowBatches int32 + CumulativeAck bool + MaxExecutionFrameBytes int64 + MaxHandlerProcesses int32 + MaxAccountHandlerProcesses int32 + MaxOwnerHandlerProcesses int32 + LeaseEpoch uint64 + Modes []string + NullPolicies []string +} + +const ( + RuntimeStatusReady = "READY" + RuntimeStatusDisabled = "DISABLED" + RuntimeStatusNotAllowed = "NOT_ALLOWED" + RuntimeStatusUnavailable = "UNAVAILABLE" + RuntimeStatusContractMismatch = "CONTRACT_MISMATCH" + RuntimeStatusTimeout = "TIMEOUT" + RuntimeStatusClosed = "CLOSED" + RuntimeStatusInvalid = "INVALID" + RuntimeStatusInternal = "INTERNAL" + + RuntimeStatusReasonReady = "CapabilityHandshakeReady" + RuntimeStatusReasonRuntimeDisabled = "RuntimeDisabled" + RuntimeStatusReasonUnisolatedNotAllowed = "UnisolatedExecutionNotAllowed" + RuntimeStatusReasonWorkerUnavailable = "WorkerUnavailable" + RuntimeStatusReasonCapabilityMismatch = "CapabilityContractMismatch" + RuntimeStatusReasonRequestTimeout = "CapabilityRequestTimeout" + RuntimeStatusReasonRuntimeClosed = "RuntimeClosed" + RuntimeStatusReasonStatusUnavailable = "RuntimeStatusUnavailable" + RuntimeStatusReasonInvalidLanguage = "InvalidLanguage" + RuntimeStatusReasonRuntimeError = "RuntimeError" +) + +// RuntimeStatusProvider supplies the bounded status view used by the CN query +// service. Implementations may refresh a capability handshake, but must not +// execute user code or allocate invocation/ledger state. +type RuntimeStatusProvider interface { + StatusSnapshot(context.Context, string) RuntimeStatusSnapshot +} + // RoutineDefinition is the typed, pre-publication contract handed to a // language runtime for definition validation. It is deliberately separate // from Invocation: a CREATE or REPLACE has no FunctionRef/revision yet and @@ -389,6 +451,44 @@ func (r *runtimeRegistry) CheckLanguageReady(ctx context.Context, language strin return readiness.CheckLanguageReady(ctx, language) } +func (r *runtimeRegistry) StatusSnapshot(ctx context.Context, language string) RuntimeStatusSnapshot { + runtime := r.byLanguage[language] + if runtime == nil { + return RuntimeStatusSnapshot{ + Language: language, + ErrorClass: RuntimeStatusDisabled, + Reason: RuntimeStatusReasonRuntimeDisabled, + } + } + if provider, ok := runtime.(RuntimeStatusProvider); ok { + return provider.StatusSnapshot(ctx, language) + } + readiness, ok := runtime.(RuntimeReadiness) + if !ok { + return RuntimeStatusSnapshot{ + Language: language, + Enabled: true, + ErrorClass: RuntimeStatusUnavailable, + Reason: RuntimeStatusReasonStatusUnavailable, + } + } + if err := readiness.CheckLanguageReady(ctx, language); err != nil { + return RuntimeStatusSnapshot{ + Language: language, + Enabled: true, + ErrorClass: RuntimeStatusUnavailable, + Reason: RuntimeStatusReasonWorkerUnavailable, + } + } + return RuntimeStatusSnapshot{ + Language: language, + Enabled: true, + Ready: true, + ErrorClass: "", + Reason: RuntimeStatusReasonReady, + } +} + func (r *runtimeRegistry) ValidateDefinition(ctx context.Context, definition *RoutineDefinition) error { if definition == nil { return moerr.NewInvalidInputNoCtx("nil UDF routine definition") diff --git a/proto/query.proto b/proto/query.proto index 79dce4531e1ed..836e4c204a9a6 100644 --- a/proto/query.proto +++ b/proto/query.proto @@ -110,6 +110,9 @@ enum CmdMethod { MongoDBClientRetire = 39; // RefreshSessionAuth revalidates a cached backend against the current catalog. RefreshSessionAuth = 40; + // GetPythonUdfStatus returns the bounded, read-only Python runtime status of + // the addressed CN. It is consumed by the Operator for same-Pod workers. + GetPythonUdfStatus = 41; } // QueryRequest is the common query request. It contains the query @@ -255,6 +258,8 @@ message Request { MongoDBClientRetireRequest MongoDBClientRetireRequest = 41 [ (gogoproto.nullable) = false ]; // RefreshSessionAuthRequest reauthenticates a cached backend for a new login. RefreshSessionAuthRequest RefreshSessionAuthRequest = 42; + // GetPythonUdfStatusRequest asks this CN for its local Python runtime state. + GetPythonUdfStatusRequest GetPythonUdfStatusRequest = 43 [ (gogoproto.nullable) = false ]; } // ShowProcessListResponse is the response of command ShowProcessList. @@ -325,6 +330,38 @@ message Response { MongoDBClientRetireResponse MongoDBClientRetireResponse = 42 [ (gogoproto.nullable) = false ]; // RefreshSessionAuthResponse contains the current authentication string. RefreshSessionAuthResponse RefreshSessionAuthResponse = 43; + // GetPythonUdfStatus is a bounded summary of the CN-local Python contract. + GetPythonUdfStatusResponse GetPythonUdfStatus = 44 [ (gogoproto.nullable) = false ]; +} + +message GetPythonUdfStatusRequest { +} + +message GetPythonUdfStatusResponse { + string CNUUID = 1; + string Language = 2; + bool Enabled = 3; + bool AllowUnisolated = 4; + bool Ready = 5; + string ErrorClass = 6; + string Reason = 7; + int32 ProtocolVersion = 8; + string ABIContract = 9; + string AdapterVersion = 10; + string SDKVersion = 11; + int32 DefinitionSchemaVersion = 12; + int32 PlanContractVersion = 13; + string TypeDescriptorContract = 14; + string TimezoneDatabaseVersion = 15; + int32 WindowBatches = 16; + bool CumulativeAck = 17; + int64 MaxExecutionFrameBytes = 18; + int32 MaxHandlerProcesses = 19; + int32 MaxAccountHandlerProcesses = 20; + int32 MaxOwnerHandlerProcesses = 21; + uint64 LeaseEpoch = 22; + repeated string Modes = 23; + repeated string NullPolicies = 24; } // AlterAccountRequest is the "alter account restricted" query request. From 9ae2d0e4c9fc1a3081be667c00fa00cd2f659392 Mon Sep 17 00:00:00 2001 From: iamlinjunhong <1030420200@qq.com> Date: Fri, 18 Sep 2026 10:59:30 +0800 Subject: [PATCH 154/202] test: add Arrow compute correctness cases for Python UDF --- test/distributed/cases/udf_python/README.md | 18 +++++++--- .../arrow_compute_arithmetic.result | 33 +++++++++++++++++ .../udf_python/arrow_compute_arithmetic.sql | 35 +++++++++++++++++++ .../udf_python/arrow_compute_selection.result | 30 ++++++++++++++++ .../udf_python/arrow_compute_selection.sql | 33 +++++++++++++++++ .../udf_python/arrow_compute_strings.result | 30 ++++++++++++++++ .../udf_python/arrow_compute_strings.sql | 32 +++++++++++++++++ 7 files changed, 206 insertions(+), 5 deletions(-) create mode 100644 test/distributed/cases/udf_python/arrow_compute_arithmetic.result create mode 100644 test/distributed/cases/udf_python/arrow_compute_arithmetic.sql create mode 100644 test/distributed/cases/udf_python/arrow_compute_selection.result create mode 100644 test/distributed/cases/udf_python/arrow_compute_selection.sql create mode 100644 test/distributed/cases/udf_python/arrow_compute_strings.result create mode 100644 test/distributed/cases/udf_python/arrow_compute_strings.sql diff --git a/test/distributed/cases/udf_python/README.md b/test/distributed/cases/udf_python/README.md index 8e93f01a90baa..8b698862c6ef6 100644 --- a/test/distributed/cases/udf_python/README.md +++ b/test/distributed/cases/udf_python/README.md @@ -51,11 +51,19 @@ The cases keep the data sets small and target contract boundaries: prepared-plan invalidation, drop, and recreate on the same SQL signature. - `vector_mode.sql` checks Arrow VECTOR handlers, zero-argument batch shape, NULL policies, and handler validation during create and replace. +- `arrow_compute_arithmetic.sql` checks `pyarrow.compute` arithmetic, + conditional, fill-null, and validity kernels while preserving NULLs and + batch length. +- `arrow_compute_selection.sql` checks Arrow take, sort-indices, comparison, + and physical cast kernels with indexed and NULL input. +- `arrow_compute_strings.sql` checks UTF-8 uppercasing, substring matching, + replacement, and code-point length for empty, Unicode, and NULL values. The shape follows established correctness coverage from [DuckDB's scalar UDF tests](https://github.com/duckdb/duckdb-go/blob/main/scalar_udf_test.go) -(NULLs, multiple rows, multiple arguments, and errors) and -[Apache Arrow's extension type tests](https://github.com/apache/arrow/blob/main/python/pyarrow/tests/test_extension_type.py) -(null-aware arrays, fixed-size binary values, and type validation). The SQL -cases adapt those ideas to MatrixOne's Python Arrow boundary rather than -copying another project's implementation. +(NULLs, multiple rows, multiple arguments, and errors), and the added compute +cases use the common kernels documented by +[PyArrow's compute API](https://arrow.apache.org/docs/python/compute.html) +(arithmetic, validity, selection, cast, and UTF-8). The SQL cases adapt those +ideas to MatrixOne's Python Arrow boundary rather than copying another +project's implementation. diff --git a/test/distributed/cases/udf_python/arrow_compute_arithmetic.result b/test/distributed/cases/udf_python/arrow_compute_arithmetic.result new file mode 100644 index 0000000000000..14900615bc75c --- /dev/null +++ b/test/distributed/cases/udf_python/arrow_compute_arithmetic.result @@ -0,0 +1,33 @@ +drop database if exists udf_python_arrow_compute_arithmetic_bvt; +create database udf_python_arrow_compute_arithmetic_bvt; +use udf_python_arrow_compute_arithmetic_bvt; +create function python_bvt_pc_add (x int) returns bigint language python as 'python_bvt_pc_add = lambda ctx, values: __import__("pyarrow.compute", fromlist=["add"]).add(values, 10)' handler 'python_bvt_pc_add' mode vector; +create function python_bvt_pc_multiply (x int, y int) returns int language python as 'python_bvt_pc_multiply = lambda ctx, left, right: __import__("pyarrow.compute", fromlist=["multiply"]).multiply(left, right)' handler 'python_bvt_pc_multiply' mode vector; +create function python_bvt_pc_if_else (flag bool, x int, y int) returns int language python as 'python_bvt_pc_if_else = lambda ctx, flag, left, right: __import__("pyarrow.compute", fromlist=["if_else"]).if_else(flag, left, right)' handler 'python_bvt_pc_if_else' mode vector; +create function python_bvt_pc_fill_null (x int) returns int language python as 'python_bvt_pc_fill_null = lambda ctx, values: __import__("pyarrow.compute", fromlist=["fill_null"]).fill_null(values, -7)' handler 'python_bvt_pc_fill_null' mode vector; +create function python_bvt_pc_is_null (x int) returns bool language python as 'python_bvt_pc_is_null = lambda ctx, values: __import__("pyarrow.compute", fromlist=["is_null"]).is_null(values)' handler 'python_bvt_pc_is_null' mode vector; +create table arithmetic_values (id int, flag bool, left_value int, right_value int); +insert into arithmetic_values values +(1, true, 1, 10), +(2, false, null, 20), +(3, null, 3, null), +(4, true, null, null); +select id, +python_bvt_pc_add(left_value) as added, +python_bvt_pc_multiply(left_value, right_value) as multiplied, +python_bvt_pc_if_else(flag, left_value, right_value) as selected, +python_bvt_pc_fill_null(left_value) as filled, +python_bvt_pc_is_null(left_value) as is_null +from arithmetic_values order by id; +➤ id[4,32,0] ¦ added[-5,64,0] ¦ multiplied[4,32,0] ¦ selected[4,32,0] ¦ filled[4,32,0] ¦ is_null[-7,1,0] 𝄀 +1 ¦ 11 ¦ 10 ¦ 1 ¦ 1 ¦ 0 𝄀 +2 ¦ null ¦ null ¦ 20 ¦ -7 ¦ 1 𝄀 +3 ¦ 13 ¦ null ¦ null ¦ 3 ¦ 0 𝄀 +4 ¦ null ¦ null ¦ null ¦ -7 ¦ 1 +drop function python_bvt_pc_add(int); +drop function python_bvt_pc_multiply(int, int); +drop function python_bvt_pc_if_else(bool, int, int); +drop function python_bvt_pc_fill_null(int); +drop function python_bvt_pc_is_null(int); +drop table arithmetic_values; +drop database udf_python_arrow_compute_arithmetic_bvt; diff --git a/test/distributed/cases/udf_python/arrow_compute_arithmetic.sql b/test/distributed/cases/udf_python/arrow_compute_arithmetic.sql new file mode 100644 index 0000000000000..cfea5b0c1a3e7 --- /dev/null +++ b/test/distributed/cases/udf_python/arrow_compute_arithmetic.sql @@ -0,0 +1,35 @@ +-- Arrow compute arithmetic and validity kernels through the SQL UDF boundary. +drop database if exists udf_python_arrow_compute_arithmetic_bvt; +create database udf_python_arrow_compute_arithmetic_bvt; +use udf_python_arrow_compute_arithmetic_bvt; + +create function python_bvt_pc_add (x int) returns bigint language python as 'python_bvt_pc_add = lambda ctx, values: __import__("pyarrow.compute", fromlist=["add"]).add(values, 10)' handler 'python_bvt_pc_add' mode vector; +create function python_bvt_pc_multiply (x int, y int) returns int language python as 'python_bvt_pc_multiply = lambda ctx, left, right: __import__("pyarrow.compute", fromlist=["multiply"]).multiply(left, right)' handler 'python_bvt_pc_multiply' mode vector; +create function python_bvt_pc_if_else (flag bool, x int, y int) returns int language python as 'python_bvt_pc_if_else = lambda ctx, flag, left, right: __import__("pyarrow.compute", fromlist=["if_else"]).if_else(flag, left, right)' handler 'python_bvt_pc_if_else' mode vector; +create function python_bvt_pc_fill_null (x int) returns int language python as 'python_bvt_pc_fill_null = lambda ctx, values: __import__("pyarrow.compute", fromlist=["fill_null"]).fill_null(values, -7)' handler 'python_bvt_pc_fill_null' mode vector; +create function python_bvt_pc_is_null (x int) returns bool language python as 'python_bvt_pc_is_null = lambda ctx, values: __import__("pyarrow.compute", fromlist=["is_null"]).is_null(values)' handler 'python_bvt_pc_is_null' mode vector; + +create table arithmetic_values (id int, flag bool, left_value int, right_value int); +insert into arithmetic_values values + (1, true, 1, 10), + (2, false, null, 20), + (3, null, 3, null), + (4, true, null, null); + +-- These kernels must preserve Arrow validity instead of converting NULL to a +-- value or changing the batch length. +select id, + python_bvt_pc_add(left_value) as added, + python_bvt_pc_multiply(left_value, right_value) as multiplied, + python_bvt_pc_if_else(flag, left_value, right_value) as selected, + python_bvt_pc_fill_null(left_value) as filled, + python_bvt_pc_is_null(left_value) as is_null +from arithmetic_values order by id; + +drop function python_bvt_pc_add(int); +drop function python_bvt_pc_multiply(int, int); +drop function python_bvt_pc_if_else(bool, int, int); +drop function python_bvt_pc_fill_null(int); +drop function python_bvt_pc_is_null(int); +drop table arithmetic_values; +drop database udf_python_arrow_compute_arithmetic_bvt; diff --git a/test/distributed/cases/udf_python/arrow_compute_selection.result b/test/distributed/cases/udf_python/arrow_compute_selection.result new file mode 100644 index 0000000000000..4c320f4d1e4c2 --- /dev/null +++ b/test/distributed/cases/udf_python/arrow_compute_selection.result @@ -0,0 +1,30 @@ +drop database if exists udf_python_arrow_compute_selection_bvt; +create database udf_python_arrow_compute_selection_bvt; +use udf_python_arrow_compute_selection_bvt; +create function python_bvt_pc_take (x int, index_value int) returns int language python as 'python_bvt_pc_take = lambda ctx, values, indices: __import__("pyarrow.compute", fromlist=["take"]).take(values, indices)' handler 'python_bvt_pc_take' mode vector; +create function python_bvt_pc_sort_take (x int) returns int language python as 'python_bvt_pc_sort_take = lambda ctx, values: __import__("pyarrow.compute", fromlist=["take"]).take(values, __import__("pyarrow.compute", fromlist=["sort_indices"]).sort_indices(values))' handler 'python_bvt_pc_sort_take' mode vector; +create function python_bvt_pc_equal (x int) returns bool language python as 'python_bvt_pc_equal = lambda ctx, values: __import__("pyarrow.compute", fromlist=["equal"]).equal(values, 20)' handler 'python_bvt_pc_equal' mode vector; +create function python_bvt_pc_cast (x int) returns bigint language python as 'python_bvt_pc_cast = lambda ctx, values: __import__("pyarrow.compute", fromlist=["cast"]).cast(values, __import__("pyarrow").int64())' handler 'python_bvt_pc_cast' mode vector; +create table selection_values (id int, value int, index_value int); +insert into selection_values values +(1, 30, 2), +(2, null, 0), +(3, 20, 1), +(4, 10, 3); +select id, +python_bvt_pc_take(value, index_value) as taken, +python_bvt_pc_sort_take(value) as sorted_value, +python_bvt_pc_equal(value) as equals_twenty, +python_bvt_pc_cast(value) as cast_value +from selection_values order by id; +➤ id[4,32,0] ¦ taken[4,32,0] ¦ sorted_value[4,32,0] ¦ equals_twenty[-7,1,0] ¦ cast_value[-5,64,0] 𝄀 +1 ¦ 20 ¦ 10 ¦ 0 ¦ 30 𝄀 +2 ¦ 30 ¦ 20 ¦ null ¦ null 𝄀 +3 ¦ null ¦ 30 ¦ 1 ¦ 20 𝄀 +4 ¦ 10 ¦ null ¦ 0 ¦ 10 +drop function python_bvt_pc_take(int, int); +drop function python_bvt_pc_sort_take(int); +drop function python_bvt_pc_equal(int); +drop function python_bvt_pc_cast(int); +drop table selection_values; +drop database udf_python_arrow_compute_selection_bvt; diff --git a/test/distributed/cases/udf_python/arrow_compute_selection.sql b/test/distributed/cases/udf_python/arrow_compute_selection.sql new file mode 100644 index 0000000000000..a548dc0275272 --- /dev/null +++ b/test/distributed/cases/udf_python/arrow_compute_selection.sql @@ -0,0 +1,33 @@ +-- Arrow compute selection, ordering, comparison, and cast kernels. +drop database if exists udf_python_arrow_compute_selection_bvt; +create database udf_python_arrow_compute_selection_bvt; +use udf_python_arrow_compute_selection_bvt; + +create function python_bvt_pc_take (x int, index_value int) returns int language python as 'python_bvt_pc_take = lambda ctx, values, indices: __import__("pyarrow.compute", fromlist=["take"]).take(values, indices)' handler 'python_bvt_pc_take' mode vector; +create function python_bvt_pc_sort_take (x int) returns int language python as 'python_bvt_pc_sort_take = lambda ctx, values: __import__("pyarrow.compute", fromlist=["take"]).take(values, __import__("pyarrow.compute", fromlist=["sort_indices"]).sort_indices(values))' handler 'python_bvt_pc_sort_take' mode vector; +create function python_bvt_pc_equal (x int) returns bool language python as 'python_bvt_pc_equal = lambda ctx, values: __import__("pyarrow.compute", fromlist=["equal"]).equal(values, 20)' handler 'python_bvt_pc_equal' mode vector; +create function python_bvt_pc_cast (x int) returns bigint language python as 'python_bvt_pc_cast = lambda ctx, values: __import__("pyarrow.compute", fromlist=["cast"]).cast(values, __import__("pyarrow").int64())' handler 'python_bvt_pc_cast' mode vector; + +create table selection_values (id int, value int, index_value int); +insert into selection_values values + (1, 30, 2), + (2, null, 0), + (3, 20, 1), + (4, 10, 3); + +-- take uses an Arrow index array, sort_indices keeps NULL placement explicit, +-- and cast proves that the physical Arrow type can change within the declared +-- SQL type contract. +select id, + python_bvt_pc_take(value, index_value) as taken, + python_bvt_pc_sort_take(value) as sorted_value, + python_bvt_pc_equal(value) as equals_twenty, + python_bvt_pc_cast(value) as cast_value +from selection_values order by id; + +drop function python_bvt_pc_take(int, int); +drop function python_bvt_pc_sort_take(int); +drop function python_bvt_pc_equal(int); +drop function python_bvt_pc_cast(int); +drop table selection_values; +drop database udf_python_arrow_compute_selection_bvt; diff --git a/test/distributed/cases/udf_python/arrow_compute_strings.result b/test/distributed/cases/udf_python/arrow_compute_strings.result new file mode 100644 index 0000000000000..1e757796a0e5b --- /dev/null +++ b/test/distributed/cases/udf_python/arrow_compute_strings.result @@ -0,0 +1,30 @@ +drop database if exists udf_python_arrow_compute_strings_bvt; +create database udf_python_arrow_compute_strings_bvt; +use udf_python_arrow_compute_strings_bvt; +create function python_bvt_pc_upper (x varchar(64)) returns varchar(64) language python as 'python_bvt_pc_upper = lambda ctx, values: __import__("pyarrow.compute", fromlist=["utf8_upper"]).utf8_upper(values)' handler 'python_bvt_pc_upper' mode vector; +create function python_bvt_pc_contains (x varchar(64)) returns bool language python as 'python_bvt_pc_contains = lambda ctx, values: __import__("pyarrow.compute", fromlist=["match_substring"]).match_substring(values, "a")' handler 'python_bvt_pc_contains' mode vector; +create function python_bvt_pc_replace (x varchar(64)) returns varchar(64) language python as 'python_bvt_pc_replace = lambda ctx, values: __import__("pyarrow.compute", fromlist=["replace_substring"]).replace_substring(values, "a", "X")' handler 'python_bvt_pc_replace' mode vector; +create function python_bvt_pc_length (x varchar(64)) returns int language python as 'python_bvt_pc_length = lambda ctx, values: __import__("pyarrow.compute", fromlist=["utf8_length"]).utf8_length(values)' handler 'python_bvt_pc_length' mode vector; +create table string_values (id int, value varchar(64)); +insert into string_values values +(1, ''), +(2, 'Alpha'), +(3, '中a😀'), +(4, null); +select id, +python_bvt_pc_upper(value) as upper_value, +python_bvt_pc_contains(value) as contains_a, +python_bvt_pc_replace(value) as replaced_value, +python_bvt_pc_length(value) as codepoint_length +from string_values order by id; +➤ id[4,32,0] ¦ upper_value[12,64,0] ¦ contains_a[-7,1,0] ¦ replaced_value[12,64,0] ¦ codepoint_length[4,32,0] 𝄀 +1 ¦ ¦ 0 ¦ ¦ 0 𝄀 +2 ¦ ALPHA ¦ 1 ¦ AlphX ¦ 5 𝄀 +3 ¦ 中A😀 ¦ 1 ¦ 中X😀 ¦ 3 𝄀 +4 ¦ null ¦ null ¦ null ¦ null +drop function python_bvt_pc_upper(varchar(64)); +drop function python_bvt_pc_contains(varchar(64)); +drop function python_bvt_pc_replace(varchar(64)); +drop function python_bvt_pc_length(varchar(64)); +drop table string_values; +drop database udf_python_arrow_compute_strings_bvt; diff --git a/test/distributed/cases/udf_python/arrow_compute_strings.sql b/test/distributed/cases/udf_python/arrow_compute_strings.sql new file mode 100644 index 0000000000000..1b8f54964894f --- /dev/null +++ b/test/distributed/cases/udf_python/arrow_compute_strings.sql @@ -0,0 +1,32 @@ +-- Arrow UTF-8 compute kernels with empty, Unicode, and NULL values. +drop database if exists udf_python_arrow_compute_strings_bvt; +create database udf_python_arrow_compute_strings_bvt; +use udf_python_arrow_compute_strings_bvt; + +create function python_bvt_pc_upper (x varchar(64)) returns varchar(64) language python as 'python_bvt_pc_upper = lambda ctx, values: __import__("pyarrow.compute", fromlist=["utf8_upper"]).utf8_upper(values)' handler 'python_bvt_pc_upper' mode vector; +create function python_bvt_pc_contains (x varchar(64)) returns bool language python as 'python_bvt_pc_contains = lambda ctx, values: __import__("pyarrow.compute", fromlist=["match_substring"]).match_substring(values, "a")' handler 'python_bvt_pc_contains' mode vector; +create function python_bvt_pc_replace (x varchar(64)) returns varchar(64) language python as 'python_bvt_pc_replace = lambda ctx, values: __import__("pyarrow.compute", fromlist=["replace_substring"]).replace_substring(values, "a", "X")' handler 'python_bvt_pc_replace' mode vector; +create function python_bvt_pc_length (x varchar(64)) returns int language python as 'python_bvt_pc_length = lambda ctx, values: __import__("pyarrow.compute", fromlist=["utf8_length"]).utf8_length(values)' handler 'python_bvt_pc_length' mode vector; + +create table string_values (id int, value varchar(64)); +insert into string_values values + (1, ''), + (2, 'Alpha'), + (3, '中a😀'), + (4, null); + +-- The kernels must preserve empty strings and NULL validity. utf8_length +-- counts Unicode code points, so the final non-NULL value has length three. +select id, + python_bvt_pc_upper(value) as upper_value, + python_bvt_pc_contains(value) as contains_a, + python_bvt_pc_replace(value) as replaced_value, + python_bvt_pc_length(value) as codepoint_length +from string_values order by id; + +drop function python_bvt_pc_upper(varchar(64)); +drop function python_bvt_pc_contains(varchar(64)); +drop function python_bvt_pc_replace(varchar(64)); +drop function python_bvt_pc_length(varchar(64)); +drop table string_values; +drop database udf_python_arrow_compute_strings_bvt; From f72c6baf09d1ce420419db828fcc8dccb258382f Mon Sep 17 00:00:00 2001 From: iamlinjunhong <1030420200@qq.com> Date: Fri, 18 Sep 2026 11:01:11 +0800 Subject: [PATCH 155/202] test: make Arrow sort null placement explicit --- .../distributed/cases/udf_python/arrow_compute_selection.result | 2 +- test/distributed/cases/udf_python/arrow_compute_selection.sql | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/test/distributed/cases/udf_python/arrow_compute_selection.result b/test/distributed/cases/udf_python/arrow_compute_selection.result index 4c320f4d1e4c2..3fff0e8668889 100644 --- a/test/distributed/cases/udf_python/arrow_compute_selection.result +++ b/test/distributed/cases/udf_python/arrow_compute_selection.result @@ -2,7 +2,7 @@ drop database if exists udf_python_arrow_compute_selection_bvt; create database udf_python_arrow_compute_selection_bvt; use udf_python_arrow_compute_selection_bvt; create function python_bvt_pc_take (x int, index_value int) returns int language python as 'python_bvt_pc_take = lambda ctx, values, indices: __import__("pyarrow.compute", fromlist=["take"]).take(values, indices)' handler 'python_bvt_pc_take' mode vector; -create function python_bvt_pc_sort_take (x int) returns int language python as 'python_bvt_pc_sort_take = lambda ctx, values: __import__("pyarrow.compute", fromlist=["take"]).take(values, __import__("pyarrow.compute", fromlist=["sort_indices"]).sort_indices(values))' handler 'python_bvt_pc_sort_take' mode vector; +create function python_bvt_pc_sort_take (x int) returns int language python as 'python_bvt_pc_sort_take = lambda ctx, values: __import__("pyarrow.compute", fromlist=["take"]).take(values, __import__("pyarrow.compute", fromlist=["sort_indices"]).sort_indices(values, null_placement="at_end"))' handler 'python_bvt_pc_sort_take' mode vector; create function python_bvt_pc_equal (x int) returns bool language python as 'python_bvt_pc_equal = lambda ctx, values: __import__("pyarrow.compute", fromlist=["equal"]).equal(values, 20)' handler 'python_bvt_pc_equal' mode vector; create function python_bvt_pc_cast (x int) returns bigint language python as 'python_bvt_pc_cast = lambda ctx, values: __import__("pyarrow.compute", fromlist=["cast"]).cast(values, __import__("pyarrow").int64())' handler 'python_bvt_pc_cast' mode vector; create table selection_values (id int, value int, index_value int); diff --git a/test/distributed/cases/udf_python/arrow_compute_selection.sql b/test/distributed/cases/udf_python/arrow_compute_selection.sql index a548dc0275272..d1348d7a67ebd 100644 --- a/test/distributed/cases/udf_python/arrow_compute_selection.sql +++ b/test/distributed/cases/udf_python/arrow_compute_selection.sql @@ -4,7 +4,7 @@ create database udf_python_arrow_compute_selection_bvt; use udf_python_arrow_compute_selection_bvt; create function python_bvt_pc_take (x int, index_value int) returns int language python as 'python_bvt_pc_take = lambda ctx, values, indices: __import__("pyarrow.compute", fromlist=["take"]).take(values, indices)' handler 'python_bvt_pc_take' mode vector; -create function python_bvt_pc_sort_take (x int) returns int language python as 'python_bvt_pc_sort_take = lambda ctx, values: __import__("pyarrow.compute", fromlist=["take"]).take(values, __import__("pyarrow.compute", fromlist=["sort_indices"]).sort_indices(values))' handler 'python_bvt_pc_sort_take' mode vector; +create function python_bvt_pc_sort_take (x int) returns int language python as 'python_bvt_pc_sort_take = lambda ctx, values: __import__("pyarrow.compute", fromlist=["take"]).take(values, __import__("pyarrow.compute", fromlist=["sort_indices"]).sort_indices(values, null_placement="at_end"))' handler 'python_bvt_pc_sort_take' mode vector; create function python_bvt_pc_equal (x int) returns bool language python as 'python_bvt_pc_equal = lambda ctx, values: __import__("pyarrow.compute", fromlist=["equal"]).equal(values, 20)' handler 'python_bvt_pc_equal' mode vector; create function python_bvt_pc_cast (x int) returns bigint language python as 'python_bvt_pc_cast = lambda ctx, values: __import__("pyarrow.compute", fromlist=["cast"]).cast(values, __import__("pyarrow").int64())' handler 'python_bvt_pc_cast' mode vector; From 2c32ea4577c3610672bf9ea03da84fba0208dfd4 Mon Sep 17 00:00:00 2001 From: iamlinjunhong <1030420200@qq.com> Date: Fri, 18 Sep 2026 11:02:04 +0800 Subject: [PATCH 156/202] test: cover null Arrow take indices --- .../cases/udf_python/arrow_compute_selection.result | 6 ++++-- .../cases/udf_python/arrow_compute_selection.sql | 3 ++- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/test/distributed/cases/udf_python/arrow_compute_selection.result b/test/distributed/cases/udf_python/arrow_compute_selection.result index 3fff0e8668889..2bc3a00a5c475 100644 --- a/test/distributed/cases/udf_python/arrow_compute_selection.result +++ b/test/distributed/cases/udf_python/arrow_compute_selection.result @@ -10,7 +10,8 @@ insert into selection_values values (1, 30, 2), (2, null, 0), (3, 20, 1), -(4, 10, 3); +(4, 10, 3), +(5, 40, null); select id, python_bvt_pc_take(value, index_value) as taken, python_bvt_pc_sort_take(value) as sorted_value, @@ -21,7 +22,8 @@ from selection_values order by id; 1 ¦ 20 ¦ 10 ¦ 0 ¦ 30 𝄀 2 ¦ 30 ¦ 20 ¦ null ¦ null 𝄀 3 ¦ null ¦ 30 ¦ 1 ¦ 20 𝄀 -4 ¦ 10 ¦ null ¦ 0 ¦ 10 +4 ¦ 10 ¦ 40 ¦ 0 ¦ 10 𝄀 +5 ¦ null ¦ null ¦ 0 ¦ 40 drop function python_bvt_pc_take(int, int); drop function python_bvt_pc_sort_take(int); drop function python_bvt_pc_equal(int); diff --git a/test/distributed/cases/udf_python/arrow_compute_selection.sql b/test/distributed/cases/udf_python/arrow_compute_selection.sql index d1348d7a67ebd..350ee35af2917 100644 --- a/test/distributed/cases/udf_python/arrow_compute_selection.sql +++ b/test/distributed/cases/udf_python/arrow_compute_selection.sql @@ -13,7 +13,8 @@ insert into selection_values values (1, 30, 2), (2, null, 0), (3, 20, 1), - (4, 10, 3); + (4, 10, 3), + (5, 40, null); -- take uses an Arrow index array, sort_indices keeps NULL placement explicit, -- and cast proves that the physical Arrow type can change within the declared From d3732615f1a2b65dc6faf7bcfbb3fcdbe712accb Mon Sep 17 00:00:00 2001 From: iamlinjunhong <1030420200@qq.com> Date: Fri, 18 Sep 2026 11:04:23 +0800 Subject: [PATCH 157/202] test: make Arrow arithmetic scalar type explicit --- .../cases/udf_python/arrow_compute_arithmetic.result | 4 ++-- .../distributed/cases/udf_python/arrow_compute_arithmetic.sql | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/test/distributed/cases/udf_python/arrow_compute_arithmetic.result b/test/distributed/cases/udf_python/arrow_compute_arithmetic.result index 14900615bc75c..7ea3ab8da1444 100644 --- a/test/distributed/cases/udf_python/arrow_compute_arithmetic.result +++ b/test/distributed/cases/udf_python/arrow_compute_arithmetic.result @@ -1,7 +1,7 @@ drop database if exists udf_python_arrow_compute_arithmetic_bvt; create database udf_python_arrow_compute_arithmetic_bvt; use udf_python_arrow_compute_arithmetic_bvt; -create function python_bvt_pc_add (x int) returns bigint language python as 'python_bvt_pc_add = lambda ctx, values: __import__("pyarrow.compute", fromlist=["add"]).add(values, 10)' handler 'python_bvt_pc_add' mode vector; +create function python_bvt_pc_add (x int) returns int language python as 'python_bvt_pc_add = lambda ctx, values: __import__("pyarrow.compute", fromlist=["add"]).add(values, __import__("pyarrow").scalar(10, type=__import__("pyarrow").int32()))' handler 'python_bvt_pc_add' mode vector; create function python_bvt_pc_multiply (x int, y int) returns int language python as 'python_bvt_pc_multiply = lambda ctx, left, right: __import__("pyarrow.compute", fromlist=["multiply"]).multiply(left, right)' handler 'python_bvt_pc_multiply' mode vector; create function python_bvt_pc_if_else (flag bool, x int, y int) returns int language python as 'python_bvt_pc_if_else = lambda ctx, flag, left, right: __import__("pyarrow.compute", fromlist=["if_else"]).if_else(flag, left, right)' handler 'python_bvt_pc_if_else' mode vector; create function python_bvt_pc_fill_null (x int) returns int language python as 'python_bvt_pc_fill_null = lambda ctx, values: __import__("pyarrow.compute", fromlist=["fill_null"]).fill_null(values, -7)' handler 'python_bvt_pc_fill_null' mode vector; @@ -19,7 +19,7 @@ python_bvt_pc_if_else(flag, left_value, right_value) as selected, python_bvt_pc_fill_null(left_value) as filled, python_bvt_pc_is_null(left_value) as is_null from arithmetic_values order by id; -➤ id[4,32,0] ¦ added[-5,64,0] ¦ multiplied[4,32,0] ¦ selected[4,32,0] ¦ filled[4,32,0] ¦ is_null[-7,1,0] 𝄀 +➤ id[4,32,0] ¦ added[4,32,0] ¦ multiplied[4,32,0] ¦ selected[4,32,0] ¦ filled[4,32,0] ¦ is_null[-7,1,0] 𝄀 1 ¦ 11 ¦ 10 ¦ 1 ¦ 1 ¦ 0 𝄀 2 ¦ null ¦ null ¦ 20 ¦ -7 ¦ 1 𝄀 3 ¦ 13 ¦ null ¦ null ¦ 3 ¦ 0 𝄀 diff --git a/test/distributed/cases/udf_python/arrow_compute_arithmetic.sql b/test/distributed/cases/udf_python/arrow_compute_arithmetic.sql index cfea5b0c1a3e7..e5134d2bc41e8 100644 --- a/test/distributed/cases/udf_python/arrow_compute_arithmetic.sql +++ b/test/distributed/cases/udf_python/arrow_compute_arithmetic.sql @@ -3,7 +3,7 @@ drop database if exists udf_python_arrow_compute_arithmetic_bvt; create database udf_python_arrow_compute_arithmetic_bvt; use udf_python_arrow_compute_arithmetic_bvt; -create function python_bvt_pc_add (x int) returns bigint language python as 'python_bvt_pc_add = lambda ctx, values: __import__("pyarrow.compute", fromlist=["add"]).add(values, 10)' handler 'python_bvt_pc_add' mode vector; +create function python_bvt_pc_add (x int) returns int language python as 'python_bvt_pc_add = lambda ctx, values: __import__("pyarrow.compute", fromlist=["add"]).add(values, __import__("pyarrow").scalar(10, type=__import__("pyarrow").int32()))' handler 'python_bvt_pc_add' mode vector; create function python_bvt_pc_multiply (x int, y int) returns int language python as 'python_bvt_pc_multiply = lambda ctx, left, right: __import__("pyarrow.compute", fromlist=["multiply"]).multiply(left, right)' handler 'python_bvt_pc_multiply' mode vector; create function python_bvt_pc_if_else (flag bool, x int, y int) returns int language python as 'python_bvt_pc_if_else = lambda ctx, flag, left, right: __import__("pyarrow.compute", fromlist=["if_else"]).if_else(flag, left, right)' handler 'python_bvt_pc_if_else' mode vector; create function python_bvt_pc_fill_null (x int) returns int language python as 'python_bvt_pc_fill_null = lambda ctx, values: __import__("pyarrow.compute", fromlist=["fill_null"]).fill_null(values, -7)' handler 'python_bvt_pc_fill_null' mode vector; From b57b9f84b99e07103eaf670396bee3d44fd6b2dc Mon Sep 17 00:00:00 2001 From: iamlinjunhong <1030420200@qq.com> Date: Fri, 18 Sep 2026 11:05:18 +0800 Subject: [PATCH 158/202] test: make Arrow string options explicit --- .../distributed/cases/udf_python/arrow_compute_strings.result | 4 ++-- test/distributed/cases/udf_python/arrow_compute_strings.sql | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/test/distributed/cases/udf_python/arrow_compute_strings.result b/test/distributed/cases/udf_python/arrow_compute_strings.result index 1e757796a0e5b..41d40829f5eb1 100644 --- a/test/distributed/cases/udf_python/arrow_compute_strings.result +++ b/test/distributed/cases/udf_python/arrow_compute_strings.result @@ -2,8 +2,8 @@ drop database if exists udf_python_arrow_compute_strings_bvt; create database udf_python_arrow_compute_strings_bvt; use udf_python_arrow_compute_strings_bvt; create function python_bvt_pc_upper (x varchar(64)) returns varchar(64) language python as 'python_bvt_pc_upper = lambda ctx, values: __import__("pyarrow.compute", fromlist=["utf8_upper"]).utf8_upper(values)' handler 'python_bvt_pc_upper' mode vector; -create function python_bvt_pc_contains (x varchar(64)) returns bool language python as 'python_bvt_pc_contains = lambda ctx, values: __import__("pyarrow.compute", fromlist=["match_substring"]).match_substring(values, "a")' handler 'python_bvt_pc_contains' mode vector; -create function python_bvt_pc_replace (x varchar(64)) returns varchar(64) language python as 'python_bvt_pc_replace = lambda ctx, values: __import__("pyarrow.compute", fromlist=["replace_substring"]).replace_substring(values, "a", "X")' handler 'python_bvt_pc_replace' mode vector; +create function python_bvt_pc_contains (x varchar(64)) returns bool language python as 'python_bvt_pc_contains = lambda ctx, values: __import__("pyarrow.compute", fromlist=["match_substring"]).match_substring(values, "a", ignore_case=False)' handler 'python_bvt_pc_contains' mode vector; +create function python_bvt_pc_replace (x varchar(64)) returns varchar(64) language python as 'python_bvt_pc_replace = lambda ctx, values: __import__("pyarrow.compute", fromlist=["replace_substring"]).replace_substring(values, "a", "X", max_replacements=-1)' handler 'python_bvt_pc_replace' mode vector; create function python_bvt_pc_length (x varchar(64)) returns int language python as 'python_bvt_pc_length = lambda ctx, values: __import__("pyarrow.compute", fromlist=["utf8_length"]).utf8_length(values)' handler 'python_bvt_pc_length' mode vector; create table string_values (id int, value varchar(64)); insert into string_values values diff --git a/test/distributed/cases/udf_python/arrow_compute_strings.sql b/test/distributed/cases/udf_python/arrow_compute_strings.sql index 1b8f54964894f..0d9ad9c0a5284 100644 --- a/test/distributed/cases/udf_python/arrow_compute_strings.sql +++ b/test/distributed/cases/udf_python/arrow_compute_strings.sql @@ -4,8 +4,8 @@ create database udf_python_arrow_compute_strings_bvt; use udf_python_arrow_compute_strings_bvt; create function python_bvt_pc_upper (x varchar(64)) returns varchar(64) language python as 'python_bvt_pc_upper = lambda ctx, values: __import__("pyarrow.compute", fromlist=["utf8_upper"]).utf8_upper(values)' handler 'python_bvt_pc_upper' mode vector; -create function python_bvt_pc_contains (x varchar(64)) returns bool language python as 'python_bvt_pc_contains = lambda ctx, values: __import__("pyarrow.compute", fromlist=["match_substring"]).match_substring(values, "a")' handler 'python_bvt_pc_contains' mode vector; -create function python_bvt_pc_replace (x varchar(64)) returns varchar(64) language python as 'python_bvt_pc_replace = lambda ctx, values: __import__("pyarrow.compute", fromlist=["replace_substring"]).replace_substring(values, "a", "X")' handler 'python_bvt_pc_replace' mode vector; +create function python_bvt_pc_contains (x varchar(64)) returns bool language python as 'python_bvt_pc_contains = lambda ctx, values: __import__("pyarrow.compute", fromlist=["match_substring"]).match_substring(values, "a", ignore_case=False)' handler 'python_bvt_pc_contains' mode vector; +create function python_bvt_pc_replace (x varchar(64)) returns varchar(64) language python as 'python_bvt_pc_replace = lambda ctx, values: __import__("pyarrow.compute", fromlist=["replace_substring"]).replace_substring(values, "a", "X", max_replacements=-1)' handler 'python_bvt_pc_replace' mode vector; create function python_bvt_pc_length (x varchar(64)) returns int language python as 'python_bvt_pc_length = lambda ctx, values: __import__("pyarrow.compute", fromlist=["utf8_length"]).utf8_length(values)' handler 'python_bvt_pc_length' mode vector; create table string_values (id int, value varchar(64)); From c38d8a7877cc32da9e17bbe4dfd47fa92df092a6 Mon Sep 17 00:00:00 2001 From: iamlinjunhong <1030420200@qq.com> Date: Fri, 18 Sep 2026 11:06:07 +0800 Subject: [PATCH 159/202] test: make Arrow selection safety explicit --- .../cases/udf_python/arrow_compute_selection.result | 4 ++-- test/distributed/cases/udf_python/arrow_compute_selection.sql | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/test/distributed/cases/udf_python/arrow_compute_selection.result b/test/distributed/cases/udf_python/arrow_compute_selection.result index 2bc3a00a5c475..db81049257e13 100644 --- a/test/distributed/cases/udf_python/arrow_compute_selection.result +++ b/test/distributed/cases/udf_python/arrow_compute_selection.result @@ -1,10 +1,10 @@ drop database if exists udf_python_arrow_compute_selection_bvt; create database udf_python_arrow_compute_selection_bvt; use udf_python_arrow_compute_selection_bvt; -create function python_bvt_pc_take (x int, index_value int) returns int language python as 'python_bvt_pc_take = lambda ctx, values, indices: __import__("pyarrow.compute", fromlist=["take"]).take(values, indices)' handler 'python_bvt_pc_take' mode vector; +create function python_bvt_pc_take (x int, index_value int) returns int language python as 'python_bvt_pc_take = lambda ctx, values, indices: __import__("pyarrow.compute", fromlist=["take"]).take(values, indices, boundscheck=True)' handler 'python_bvt_pc_take' mode vector; create function python_bvt_pc_sort_take (x int) returns int language python as 'python_bvt_pc_sort_take = lambda ctx, values: __import__("pyarrow.compute", fromlist=["take"]).take(values, __import__("pyarrow.compute", fromlist=["sort_indices"]).sort_indices(values, null_placement="at_end"))' handler 'python_bvt_pc_sort_take' mode vector; create function python_bvt_pc_equal (x int) returns bool language python as 'python_bvt_pc_equal = lambda ctx, values: __import__("pyarrow.compute", fromlist=["equal"]).equal(values, 20)' handler 'python_bvt_pc_equal' mode vector; -create function python_bvt_pc_cast (x int) returns bigint language python as 'python_bvt_pc_cast = lambda ctx, values: __import__("pyarrow.compute", fromlist=["cast"]).cast(values, __import__("pyarrow").int64())' handler 'python_bvt_pc_cast' mode vector; +create function python_bvt_pc_cast (x int) returns bigint language python as 'python_bvt_pc_cast = lambda ctx, values: __import__("pyarrow.compute", fromlist=["cast"]).cast(values, __import__("pyarrow").int64(), safe=True)' handler 'python_bvt_pc_cast' mode vector; create table selection_values (id int, value int, index_value int); insert into selection_values values (1, 30, 2), diff --git a/test/distributed/cases/udf_python/arrow_compute_selection.sql b/test/distributed/cases/udf_python/arrow_compute_selection.sql index 350ee35af2917..8671c6cadd10b 100644 --- a/test/distributed/cases/udf_python/arrow_compute_selection.sql +++ b/test/distributed/cases/udf_python/arrow_compute_selection.sql @@ -3,10 +3,10 @@ drop database if exists udf_python_arrow_compute_selection_bvt; create database udf_python_arrow_compute_selection_bvt; use udf_python_arrow_compute_selection_bvt; -create function python_bvt_pc_take (x int, index_value int) returns int language python as 'python_bvt_pc_take = lambda ctx, values, indices: __import__("pyarrow.compute", fromlist=["take"]).take(values, indices)' handler 'python_bvt_pc_take' mode vector; +create function python_bvt_pc_take (x int, index_value int) returns int language python as 'python_bvt_pc_take = lambda ctx, values, indices: __import__("pyarrow.compute", fromlist=["take"]).take(values, indices, boundscheck=True)' handler 'python_bvt_pc_take' mode vector; create function python_bvt_pc_sort_take (x int) returns int language python as 'python_bvt_pc_sort_take = lambda ctx, values: __import__("pyarrow.compute", fromlist=["take"]).take(values, __import__("pyarrow.compute", fromlist=["sort_indices"]).sort_indices(values, null_placement="at_end"))' handler 'python_bvt_pc_sort_take' mode vector; create function python_bvt_pc_equal (x int) returns bool language python as 'python_bvt_pc_equal = lambda ctx, values: __import__("pyarrow.compute", fromlist=["equal"]).equal(values, 20)' handler 'python_bvt_pc_equal' mode vector; -create function python_bvt_pc_cast (x int) returns bigint language python as 'python_bvt_pc_cast = lambda ctx, values: __import__("pyarrow.compute", fromlist=["cast"]).cast(values, __import__("pyarrow").int64())' handler 'python_bvt_pc_cast' mode vector; +create function python_bvt_pc_cast (x int) returns bigint language python as 'python_bvt_pc_cast = lambda ctx, values: __import__("pyarrow.compute", fromlist=["cast"]).cast(values, __import__("pyarrow").int64(), safe=True)' handler 'python_bvt_pc_cast' mode vector; create table selection_values (id int, value int, index_value int); insert into selection_values values From 733429b7f00f0d3068890a64d9e054e4520dbe8c Mon Sep 17 00:00:00 2001 From: iamlinjunhong <1030420200@qq.com> Date: Fri, 18 Sep 2026 11:07:21 +0800 Subject: [PATCH 160/202] test: cover Arrow vector cardinality rejection --- .../cases/udf_python/arrow_compute_selection.result | 4 ++++ .../cases/udf_python/arrow_compute_selection.sql | 7 +++++++ 2 files changed, 11 insertions(+) diff --git a/test/distributed/cases/udf_python/arrow_compute_selection.result b/test/distributed/cases/udf_python/arrow_compute_selection.result index db81049257e13..957f0633c16cb 100644 --- a/test/distributed/cases/udf_python/arrow_compute_selection.result +++ b/test/distributed/cases/udf_python/arrow_compute_selection.result @@ -5,6 +5,7 @@ create function python_bvt_pc_take (x int, index_value int) returns int language create function python_bvt_pc_sort_take (x int) returns int language python as 'python_bvt_pc_sort_take = lambda ctx, values: __import__("pyarrow.compute", fromlist=["take"]).take(values, __import__("pyarrow.compute", fromlist=["sort_indices"]).sort_indices(values, null_placement="at_end"))' handler 'python_bvt_pc_sort_take' mode vector; create function python_bvt_pc_equal (x int) returns bool language python as 'python_bvt_pc_equal = lambda ctx, values: __import__("pyarrow.compute", fromlist=["equal"]).equal(values, 20)' handler 'python_bvt_pc_equal' mode vector; create function python_bvt_pc_cast (x int) returns bigint language python as 'python_bvt_pc_cast = lambda ctx, values: __import__("pyarrow.compute", fromlist=["cast"]).cast(values, __import__("pyarrow").int64(), safe=True)' handler 'python_bvt_pc_cast' mode vector; +create function python_bvt_pc_filter (x int) returns int language python as 'python_bvt_pc_filter = lambda ctx, values: __import__("pyarrow.compute", fromlist=["filter"]).filter(values, __import__("pyarrow.compute", fromlist=["greater_equal"]).greater_equal(values, 20), null_selection_behavior="drop")' handler 'python_bvt_pc_filter' mode vector; create table selection_values (id int, value int, index_value int); insert into selection_values values (1, 30, 2), @@ -24,9 +25,12 @@ from selection_values order by id; 3 ¦ null ¦ 30 ¦ 1 ¦ 20 𝄀 4 ¦ 10 ¦ 40 ¦ 0 ¦ 10 𝄀 5 ¦ null ¦ null ¦ 0 ¦ 40 +select python_bvt_pc_filter(value) from selection_values order by id; +python udf: worker error: TYPE_CONTRACT: result length does not match input rows drop function python_bvt_pc_take(int, int); drop function python_bvt_pc_sort_take(int); drop function python_bvt_pc_equal(int); drop function python_bvt_pc_cast(int); +drop function python_bvt_pc_filter(int); drop table selection_values; drop database udf_python_arrow_compute_selection_bvt; diff --git a/test/distributed/cases/udf_python/arrow_compute_selection.sql b/test/distributed/cases/udf_python/arrow_compute_selection.sql index 8671c6cadd10b..9ec43226facf1 100644 --- a/test/distributed/cases/udf_python/arrow_compute_selection.sql +++ b/test/distributed/cases/udf_python/arrow_compute_selection.sql @@ -7,6 +7,7 @@ create function python_bvt_pc_take (x int, index_value int) returns int language create function python_bvt_pc_sort_take (x int) returns int language python as 'python_bvt_pc_sort_take = lambda ctx, values: __import__("pyarrow.compute", fromlist=["take"]).take(values, __import__("pyarrow.compute", fromlist=["sort_indices"]).sort_indices(values, null_placement="at_end"))' handler 'python_bvt_pc_sort_take' mode vector; create function python_bvt_pc_equal (x int) returns bool language python as 'python_bvt_pc_equal = lambda ctx, values: __import__("pyarrow.compute", fromlist=["equal"]).equal(values, 20)' handler 'python_bvt_pc_equal' mode vector; create function python_bvt_pc_cast (x int) returns bigint language python as 'python_bvt_pc_cast = lambda ctx, values: __import__("pyarrow.compute", fromlist=["cast"]).cast(values, __import__("pyarrow").int64(), safe=True)' handler 'python_bvt_pc_cast' mode vector; +create function python_bvt_pc_filter (x int) returns int language python as 'python_bvt_pc_filter = lambda ctx, values: __import__("pyarrow.compute", fromlist=["filter"]).filter(values, __import__("pyarrow.compute", fromlist=["greater_equal"]).greater_equal(values, 20), null_selection_behavior="drop")' handler 'python_bvt_pc_filter' mode vector; create table selection_values (id int, value int, index_value int); insert into selection_values values @@ -26,9 +27,15 @@ select id, python_bvt_pc_cast(value) as cast_value from selection_values order by id; +-- A filtering kernel shortens the Arrow array. The external evaluator must +-- reject the result instead of silently misaligning rows. +--error +select python_bvt_pc_filter(value) from selection_values order by id; + drop function python_bvt_pc_take(int, int); drop function python_bvt_pc_sort_take(int); drop function python_bvt_pc_equal(int); drop function python_bvt_pc_cast(int); +drop function python_bvt_pc_filter(int); drop table selection_values; drop database udf_python_arrow_compute_selection_bvt; From 231a04a8f42866b7913fb1813c60387c13f37731 Mon Sep 17 00:00:00 2001 From: iamlinjunhong <1030420200@qq.com> Date: Fri, 18 Sep 2026 11:08:30 +0800 Subject: [PATCH 161/202] test: distinguish Arrow string option semantics --- .../cases/udf_python/arrow_compute_strings.result | 8 ++++++-- .../cases/udf_python/arrow_compute_strings.sql | 8 ++++++-- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/test/distributed/cases/udf_python/arrow_compute_strings.result b/test/distributed/cases/udf_python/arrow_compute_strings.result index 41d40829f5eb1..0e094d9bbad58 100644 --- a/test/distributed/cases/udf_python/arrow_compute_strings.result +++ b/test/distributed/cases/udf_python/arrow_compute_strings.result @@ -10,7 +10,9 @@ insert into string_values values (1, ''), (2, 'Alpha'), (3, '中a😀'), -(4, null); +(4, 'banana'), +(5, 'A'), +(6, null); select id, python_bvt_pc_upper(value) as upper_value, python_bvt_pc_contains(value) as contains_a, @@ -21,7 +23,9 @@ from string_values order by id; 1 ¦ ¦ 0 ¦ ¦ 0 𝄀 2 ¦ ALPHA ¦ 1 ¦ AlphX ¦ 5 𝄀 3 ¦ 中A😀 ¦ 1 ¦ 中X😀 ¦ 3 𝄀 -4 ¦ null ¦ null ¦ null ¦ null +4 ¦ BANANA ¦ 1 ¦ bXnXnX ¦ 6 𝄀 +5 ¦ A ¦ 0 ¦ A ¦ 1 𝄀 +6 ¦ null ¦ null ¦ null ¦ null drop function python_bvt_pc_upper(varchar(64)); drop function python_bvt_pc_contains(varchar(64)); drop function python_bvt_pc_replace(varchar(64)); diff --git a/test/distributed/cases/udf_python/arrow_compute_strings.sql b/test/distributed/cases/udf_python/arrow_compute_strings.sql index 0d9ad9c0a5284..eb5c01dd20040 100644 --- a/test/distributed/cases/udf_python/arrow_compute_strings.sql +++ b/test/distributed/cases/udf_python/arrow_compute_strings.sql @@ -13,10 +13,14 @@ insert into string_values values (1, ''), (2, 'Alpha'), (3, '中a😀'), - (4, null); + (4, 'banana'), + (5, 'A'), + (6, null); -- The kernels must preserve empty strings and NULL validity. utf8_length --- counts Unicode code points, so the final non-NULL value has length three. +-- counts Unicode code points, so the Unicode value has length three. The +-- repeated and uppercase samples distinguish all-replacement behavior from +-- a single replacement and case-sensitive matching from case folding. select id, python_bvt_pc_upper(value) as upper_value, python_bvt_pc_contains(value) as contains_a, From 75661038dbe698a0a1d19acdf7adba77cfe4e297 Mon Sep 17 00:00:00 2001 From: iamlinjunhong <1030420200@qq.com> Date: Fri, 18 Sep 2026 11:09:52 +0800 Subject: [PATCH 162/202] test: bind Arrow scalar arguments to SQL types --- .../cases/udf_python/arrow_compute_arithmetic.result | 2 +- test/distributed/cases/udf_python/arrow_compute_arithmetic.sql | 2 +- .../distributed/cases/udf_python/arrow_compute_selection.result | 2 +- test/distributed/cases/udf_python/arrow_compute_selection.sql | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/test/distributed/cases/udf_python/arrow_compute_arithmetic.result b/test/distributed/cases/udf_python/arrow_compute_arithmetic.result index 7ea3ab8da1444..578de28fd8075 100644 --- a/test/distributed/cases/udf_python/arrow_compute_arithmetic.result +++ b/test/distributed/cases/udf_python/arrow_compute_arithmetic.result @@ -4,7 +4,7 @@ use udf_python_arrow_compute_arithmetic_bvt; create function python_bvt_pc_add (x int) returns int language python as 'python_bvt_pc_add = lambda ctx, values: __import__("pyarrow.compute", fromlist=["add"]).add(values, __import__("pyarrow").scalar(10, type=__import__("pyarrow").int32()))' handler 'python_bvt_pc_add' mode vector; create function python_bvt_pc_multiply (x int, y int) returns int language python as 'python_bvt_pc_multiply = lambda ctx, left, right: __import__("pyarrow.compute", fromlist=["multiply"]).multiply(left, right)' handler 'python_bvt_pc_multiply' mode vector; create function python_bvt_pc_if_else (flag bool, x int, y int) returns int language python as 'python_bvt_pc_if_else = lambda ctx, flag, left, right: __import__("pyarrow.compute", fromlist=["if_else"]).if_else(flag, left, right)' handler 'python_bvt_pc_if_else' mode vector; -create function python_bvt_pc_fill_null (x int) returns int language python as 'python_bvt_pc_fill_null = lambda ctx, values: __import__("pyarrow.compute", fromlist=["fill_null"]).fill_null(values, -7)' handler 'python_bvt_pc_fill_null' mode vector; +create function python_bvt_pc_fill_null (x int) returns int language python as 'python_bvt_pc_fill_null = lambda ctx, values: __import__("pyarrow.compute", fromlist=["fill_null"]).fill_null(values, __import__("pyarrow").scalar(-7, type=__import__("pyarrow").int32()))' handler 'python_bvt_pc_fill_null' mode vector; create function python_bvt_pc_is_null (x int) returns bool language python as 'python_bvt_pc_is_null = lambda ctx, values: __import__("pyarrow.compute", fromlist=["is_null"]).is_null(values)' handler 'python_bvt_pc_is_null' mode vector; create table arithmetic_values (id int, flag bool, left_value int, right_value int); insert into arithmetic_values values diff --git a/test/distributed/cases/udf_python/arrow_compute_arithmetic.sql b/test/distributed/cases/udf_python/arrow_compute_arithmetic.sql index e5134d2bc41e8..f1fbc290852ae 100644 --- a/test/distributed/cases/udf_python/arrow_compute_arithmetic.sql +++ b/test/distributed/cases/udf_python/arrow_compute_arithmetic.sql @@ -6,7 +6,7 @@ use udf_python_arrow_compute_arithmetic_bvt; create function python_bvt_pc_add (x int) returns int language python as 'python_bvt_pc_add = lambda ctx, values: __import__("pyarrow.compute", fromlist=["add"]).add(values, __import__("pyarrow").scalar(10, type=__import__("pyarrow").int32()))' handler 'python_bvt_pc_add' mode vector; create function python_bvt_pc_multiply (x int, y int) returns int language python as 'python_bvt_pc_multiply = lambda ctx, left, right: __import__("pyarrow.compute", fromlist=["multiply"]).multiply(left, right)' handler 'python_bvt_pc_multiply' mode vector; create function python_bvt_pc_if_else (flag bool, x int, y int) returns int language python as 'python_bvt_pc_if_else = lambda ctx, flag, left, right: __import__("pyarrow.compute", fromlist=["if_else"]).if_else(flag, left, right)' handler 'python_bvt_pc_if_else' mode vector; -create function python_bvt_pc_fill_null (x int) returns int language python as 'python_bvt_pc_fill_null = lambda ctx, values: __import__("pyarrow.compute", fromlist=["fill_null"]).fill_null(values, -7)' handler 'python_bvt_pc_fill_null' mode vector; +create function python_bvt_pc_fill_null (x int) returns int language python as 'python_bvt_pc_fill_null = lambda ctx, values: __import__("pyarrow.compute", fromlist=["fill_null"]).fill_null(values, __import__("pyarrow").scalar(-7, type=__import__("pyarrow").int32()))' handler 'python_bvt_pc_fill_null' mode vector; create function python_bvt_pc_is_null (x int) returns bool language python as 'python_bvt_pc_is_null = lambda ctx, values: __import__("pyarrow.compute", fromlist=["is_null"]).is_null(values)' handler 'python_bvt_pc_is_null' mode vector; create table arithmetic_values (id int, flag bool, left_value int, right_value int); diff --git a/test/distributed/cases/udf_python/arrow_compute_selection.result b/test/distributed/cases/udf_python/arrow_compute_selection.result index 957f0633c16cb..d522ea6b4e069 100644 --- a/test/distributed/cases/udf_python/arrow_compute_selection.result +++ b/test/distributed/cases/udf_python/arrow_compute_selection.result @@ -3,7 +3,7 @@ create database udf_python_arrow_compute_selection_bvt; use udf_python_arrow_compute_selection_bvt; create function python_bvt_pc_take (x int, index_value int) returns int language python as 'python_bvt_pc_take = lambda ctx, values, indices: __import__("pyarrow.compute", fromlist=["take"]).take(values, indices, boundscheck=True)' handler 'python_bvt_pc_take' mode vector; create function python_bvt_pc_sort_take (x int) returns int language python as 'python_bvt_pc_sort_take = lambda ctx, values: __import__("pyarrow.compute", fromlist=["take"]).take(values, __import__("pyarrow.compute", fromlist=["sort_indices"]).sort_indices(values, null_placement="at_end"))' handler 'python_bvt_pc_sort_take' mode vector; -create function python_bvt_pc_equal (x int) returns bool language python as 'python_bvt_pc_equal = lambda ctx, values: __import__("pyarrow.compute", fromlist=["equal"]).equal(values, 20)' handler 'python_bvt_pc_equal' mode vector; +create function python_bvt_pc_equal (x int) returns bool language python as 'python_bvt_pc_equal = lambda ctx, values: __import__("pyarrow.compute", fromlist=["equal"]).equal(values, __import__("pyarrow").scalar(20, type=__import__("pyarrow").int32()))' handler 'python_bvt_pc_equal' mode vector; create function python_bvt_pc_cast (x int) returns bigint language python as 'python_bvt_pc_cast = lambda ctx, values: __import__("pyarrow.compute", fromlist=["cast"]).cast(values, __import__("pyarrow").int64(), safe=True)' handler 'python_bvt_pc_cast' mode vector; create function python_bvt_pc_filter (x int) returns int language python as 'python_bvt_pc_filter = lambda ctx, values: __import__("pyarrow.compute", fromlist=["filter"]).filter(values, __import__("pyarrow.compute", fromlist=["greater_equal"]).greater_equal(values, 20), null_selection_behavior="drop")' handler 'python_bvt_pc_filter' mode vector; create table selection_values (id int, value int, index_value int); diff --git a/test/distributed/cases/udf_python/arrow_compute_selection.sql b/test/distributed/cases/udf_python/arrow_compute_selection.sql index 9ec43226facf1..3a90980dac758 100644 --- a/test/distributed/cases/udf_python/arrow_compute_selection.sql +++ b/test/distributed/cases/udf_python/arrow_compute_selection.sql @@ -5,7 +5,7 @@ use udf_python_arrow_compute_selection_bvt; create function python_bvt_pc_take (x int, index_value int) returns int language python as 'python_bvt_pc_take = lambda ctx, values, indices: __import__("pyarrow.compute", fromlist=["take"]).take(values, indices, boundscheck=True)' handler 'python_bvt_pc_take' mode vector; create function python_bvt_pc_sort_take (x int) returns int language python as 'python_bvt_pc_sort_take = lambda ctx, values: __import__("pyarrow.compute", fromlist=["take"]).take(values, __import__("pyarrow.compute", fromlist=["sort_indices"]).sort_indices(values, null_placement="at_end"))' handler 'python_bvt_pc_sort_take' mode vector; -create function python_bvt_pc_equal (x int) returns bool language python as 'python_bvt_pc_equal = lambda ctx, values: __import__("pyarrow.compute", fromlist=["equal"]).equal(values, 20)' handler 'python_bvt_pc_equal' mode vector; +create function python_bvt_pc_equal (x int) returns bool language python as 'python_bvt_pc_equal = lambda ctx, values: __import__("pyarrow.compute", fromlist=["equal"]).equal(values, __import__("pyarrow").scalar(20, type=__import__("pyarrow").int32()))' handler 'python_bvt_pc_equal' mode vector; create function python_bvt_pc_cast (x int) returns bigint language python as 'python_bvt_pc_cast = lambda ctx, values: __import__("pyarrow.compute", fromlist=["cast"]).cast(values, __import__("pyarrow").int64(), safe=True)' handler 'python_bvt_pc_cast' mode vector; create function python_bvt_pc_filter (x int) returns int language python as 'python_bvt_pc_filter = lambda ctx, values: __import__("pyarrow.compute", fromlist=["filter"]).filter(values, __import__("pyarrow.compute", fromlist=["greater_equal"]).greater_equal(values, 20), null_selection_behavior="drop")' handler 'python_bvt_pc_filter' mode vector; From 06f17fe587a0a48438f9977fe2341fb042f83ddd Mon Sep 17 00:00:00 2001 From: iamlinjunhong <1030420200@qq.com> Date: Fri, 18 Sep 2026 11:10:23 +0800 Subject: [PATCH 163/202] docs: describe Arrow cardinality coverage --- test/distributed/cases/udf_python/README.md | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/test/distributed/cases/udf_python/README.md b/test/distributed/cases/udf_python/README.md index 8b698862c6ef6..5bf9621efe91c 100644 --- a/test/distributed/cases/udf_python/README.md +++ b/test/distributed/cases/udf_python/README.md @@ -55,9 +55,11 @@ The cases keep the data sets small and target contract boundaries: conditional, fill-null, and validity kernels while preserving NULLs and batch length. - `arrow_compute_selection.sql` checks Arrow take, sort-indices, comparison, - and physical cast kernels with indexed and NULL input. -- `arrow_compute_strings.sql` checks UTF-8 uppercasing, substring matching, - replacement, and code-point length for empty, Unicode, and NULL values. + physical cast kernels with indexed and NULL input, and rejects a shortened + vector result. +- `arrow_compute_strings.sql` checks UTF-8 uppercasing, case-sensitive + substring matching, repeated replacement, and code-point length for empty, + Unicode, and NULL values. The shape follows established correctness coverage from [DuckDB's scalar UDF tests](https://github.com/duckdb/duckdb-go/blob/main/scalar_udf_test.go) From a7b17e92fa51e7aee35371e01ab8cbeac18a149c Mon Sep 17 00:00:00 2001 From: iamlinjunhong <1030420200@qq.com> Date: Fri, 18 Sep 2026 11:19:00 +0800 Subject: [PATCH 164/202] test: clean up BVT service processes --- optools/run_bvt.sh | 26 +++++++++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/optools/run_bvt.sh b/optools/run_bvt.sh index ed1d3f19f8cf5..2782e6200855b 100755 --- a/optools/run_bvt.sh +++ b/optools/run_bvt.sh @@ -22,6 +22,30 @@ PROXY=${3:-} LAUNCH_CONFIG=$LAUNCH MO_PID="" +function cleanup_mo() { + if [[ -z "$MO_PID" ]]; then + return + fi + if kill -0 "$MO_PID" 2>/dev/null; then + kill -TERM "$MO_PID" 2>/dev/null || true + for _ in {1..30}; do + if ! kill -0 "$MO_PID" 2>/dev/null; then + break + fi + sleep 1 + done + if kill -0 "$MO_PID" 2>/dev/null; then + kill -KILL "$MO_PID" 2>/dev/null || true + fi + fi + wait "$MO_PID" 2>/dev/null || true + MO_PID="" +} + +trap cleanup_mo EXIT +trap 'exit 130' INT +trap 'exit 143' TERM + function launch_mo() { cd $MO_WORKSPACE # Ordinary launch BVT remains the single CI entry point, but its Python @@ -39,7 +63,7 @@ function launch_mo() { function wait_system_init() { for num in {1..300} do - if MYSQL_PWD=111 mysql -h 127.0.0.1 -P 6001 -u dump -e "show databases;"; then + if MYSQL_PWD=111 mysql --connect-timeout=2 -h 127.0.0.1 -P 6001 -u dump -e "show databases;"; then echo "ok, cost $num seconds" return 0 fi From 9d113d62303403b86557484ffbf9481b068cc700 Mon Sep 17 00:00:00 2001 From: iamlinjunhong <1030420200@qq.com> Date: Fri, 18 Sep 2026 11:22:41 +0800 Subject: [PATCH 165/202] test: reap BVT child services --- optools/run_bvt.sh | 46 ++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 44 insertions(+), 2 deletions(-) diff --git a/optools/run_bvt.sh b/optools/run_bvt.sh index 2782e6200855b..b87daab0088eb 100755 --- a/optools/run_bvt.sh +++ b/optools/run_bvt.sh @@ -21,11 +21,24 @@ LAUNCH=$2 PROXY=${3:-} LAUNCH_CONFIG=$LAUNCH MO_PID="" +JSTFU_PID="" + +function collect_descendants() { + local parent="$1" + local child + for child in $(pgrep -P "$parent" 2>/dev/null || true); do + BVT_CHILD_PIDS+=("$child") + collect_descendants "$child" + done +} function cleanup_mo() { if [[ -z "$MO_PID" ]]; then return fi + local pid="$MO_PID" + local -a BVT_CHILD_PIDS=() + collect_descendants "$pid" if kill -0 "$MO_PID" 2>/dev/null; then kill -TERM "$MO_PID" 2>/dev/null || true for _ in {1..30}; do @@ -38,11 +51,39 @@ function cleanup_mo() { kill -KILL "$MO_PID" 2>/dev/null || true fi fi - wait "$MO_PID" 2>/dev/null || true + for child in "${BVT_CHILD_PIDS[@]}"; do + kill -KILL "$child" 2>/dev/null || true + done + wait "$pid" 2>/dev/null || true MO_PID="" } -trap cleanup_mo EXIT +function cleanup_jstfu() { + if [[ -z "$JSTFU_PID" ]]; then + return + fi + if kill -0 "$JSTFU_PID" 2>/dev/null; then + kill -TERM "$JSTFU_PID" 2>/dev/null || true + for _ in {1..15}; do + if ! kill -0 "$JSTFU_PID" 2>/dev/null; then + break + fi + sleep 1 + done + if kill -0 "$JSTFU_PID" 2>/dev/null; then + kill -KILL "$JSTFU_PID" 2>/dev/null || true + fi + fi + wait "$JSTFU_PID" 2>/dev/null || true + JSTFU_PID="" +} + +function cleanup_all() { + cleanup_jstfu + cleanup_mo +} + +trap cleanup_all EXIT trap 'exit 130' INT trap 'exit 143' TERM @@ -119,6 +160,7 @@ function launch_jstfu() { } fi nohup ./optools/jstfu_bvt.sh "$MO_WORKSPACE/test/distributed/resources" 127.0.0.1:6001 &>jstfu.log & + JSTFU_PID=$! for _ in {1..30}; do if bash -c 'exec 3<>/dev/tcp/127.0.0.1/4444' 2>/dev/null; then echo "jstfu ready on :4444" From a244b6cd77db5998dd3299f46a061bc52649376b Mon Sep 17 00:00:00 2001 From: iamlinjunhong <1030420200@qq.com> Date: Fri, 18 Sep 2026 11:24:24 +0800 Subject: [PATCH 166/202] test: wait for Python UDF capability handshake --- optools/run_bvt.sh | 33 ++++++++++++++++++++++++++++++--- 1 file changed, 30 insertions(+), 3 deletions(-) diff --git a/optools/run_bvt.sh b/optools/run_bvt.sh index b87daab0088eb..15a06ad901d10 100755 --- a/optools/run_bvt.sh +++ b/optools/run_bvt.sh @@ -128,13 +128,40 @@ function wait_python_udf_worker() { tail -n 160 mo-service.log >&2 || true return 1 fi - if bash -c 'exec 3<>/dev/tcp/127.0.0.1/50051' 2>/dev/null; then - echo "Python UDF worker is ready, cost $num seconds" + if python3 - <<'PY' +import json +import signal + +import pyarrow.flight as flight + + +def _timeout(_signum, _frame): + raise TimeoutError("capability probe timed out") + + +signal.signal(signal.SIGALRM, _timeout) +signal.alarm(2) +try: + client = flight.FlightClient("grpc://127.0.0.1:50051") + reader = client.do_action( + flight.Action("GetPythonCapabilities", b'{"protocol_version":1}') + ) + result = next(iter(reader), None) + if result is None: + raise RuntimeError("empty capability response") + response = json.loads(bytes(result.body)) + if response.get("protocol_version") != 1: + raise RuntimeError("unsupported capability protocol version") +finally: + signal.alarm(0) +PY + then + echo "Python UDF capability handshake is ready, cost $num seconds" return 0 fi sleep 1 done - echo "Python UDF worker did not become ready on 127.0.0.1:50051" >&2 + echo "Python UDF capability handshake did not become ready on 127.0.0.1:50051" >&2 tail -n 160 mo-service.log >&2 || true return 1 } From ee611f17ea669e49de65b9e12efb55ac686fa783 Mon Sep 17 00:00:00 2001 From: iamlinjunhong <1030420200@qq.com> Date: Fri, 18 Sep 2026 11:25:22 +0800 Subject: [PATCH 167/202] test: cover Arrow take bounds errors --- .../cases/udf_python/arrow_compute_selection.result | 5 +++++ .../cases/udf_python/arrow_compute_selection.sql | 8 ++++++++ 2 files changed, 13 insertions(+) diff --git a/test/distributed/cases/udf_python/arrow_compute_selection.result b/test/distributed/cases/udf_python/arrow_compute_selection.result index d522ea6b4e069..98b7cbefa3b36 100644 --- a/test/distributed/cases/udf_python/arrow_compute_selection.result +++ b/test/distributed/cases/udf_python/arrow_compute_selection.result @@ -27,10 +27,15 @@ from selection_values order by id; 5 ¦ null ¦ null ¦ 0 ¦ 40 select python_bvt_pc_filter(value) from selection_values order by id; python udf: worker error: TYPE_CONTRACT: result length does not match input rows +create table invalid_selection_values (value int, index_value int); +insert into invalid_selection_values values (30, 99); +select python_bvt_pc_take(value, index_value) from invalid_selection_values; +python udf: worker error: USER_CODE: handler or runtime failed drop function python_bvt_pc_take(int, int); drop function python_bvt_pc_sort_take(int); drop function python_bvt_pc_equal(int); drop function python_bvt_pc_cast(int); drop function python_bvt_pc_filter(int); +drop table invalid_selection_values; drop table selection_values; drop database udf_python_arrow_compute_selection_bvt; diff --git a/test/distributed/cases/udf_python/arrow_compute_selection.sql b/test/distributed/cases/udf_python/arrow_compute_selection.sql index 3a90980dac758..1693664b4f53a 100644 --- a/test/distributed/cases/udf_python/arrow_compute_selection.sql +++ b/test/distributed/cases/udf_python/arrow_compute_selection.sql @@ -32,10 +32,18 @@ from selection_values order by id; --error select python_bvt_pc_filter(value) from selection_values order by id; +create table invalid_selection_values (value int, index_value int); +insert into invalid_selection_values values (30, 99); + +-- boundscheck=True must turn an out-of-range index into a handler error. +--error +select python_bvt_pc_take(value, index_value) from invalid_selection_values; + drop function python_bvt_pc_take(int, int); drop function python_bvt_pc_sort_take(int); drop function python_bvt_pc_equal(int); drop function python_bvt_pc_cast(int); drop function python_bvt_pc_filter(int); +drop table invalid_selection_values; drop table selection_values; drop database udf_python_arrow_compute_selection_bvt; From a35207c5bcccdb990acd7cd0dbd01db36157cea8 Mon Sep 17 00:00:00 2001 From: iamlinjunhong <1030420200@qq.com> Date: Fri, 18 Sep 2026 11:28:34 +0800 Subject: [PATCH 168/202] test: verify Arrow sort index positions --- .../udf_python/arrow_compute_selection.result | 15 +++++++++------ .../cases/udf_python/arrow_compute_selection.sql | 3 +++ 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/test/distributed/cases/udf_python/arrow_compute_selection.result b/test/distributed/cases/udf_python/arrow_compute_selection.result index 98b7cbefa3b36..52ab101d55735 100644 --- a/test/distributed/cases/udf_python/arrow_compute_selection.result +++ b/test/distributed/cases/udf_python/arrow_compute_selection.result @@ -3,6 +3,7 @@ create database udf_python_arrow_compute_selection_bvt; use udf_python_arrow_compute_selection_bvt; create function python_bvt_pc_take (x int, index_value int) returns int language python as 'python_bvt_pc_take = lambda ctx, values, indices: __import__("pyarrow.compute", fromlist=["take"]).take(values, indices, boundscheck=True)' handler 'python_bvt_pc_take' mode vector; create function python_bvt_pc_sort_take (x int) returns int language python as 'python_bvt_pc_sort_take = lambda ctx, values: __import__("pyarrow.compute", fromlist=["take"]).take(values, __import__("pyarrow.compute", fromlist=["sort_indices"]).sort_indices(values, null_placement="at_end"))' handler 'python_bvt_pc_sort_take' mode vector; +create function python_bvt_pc_sort_indices (x int) returns bigint unsigned language python as 'python_bvt_pc_sort_indices = lambda ctx, values: __import__("pyarrow.compute", fromlist=["sort_indices"]).sort_indices(values, null_placement="at_end")' handler 'python_bvt_pc_sort_indices' mode vector; create function python_bvt_pc_equal (x int) returns bool language python as 'python_bvt_pc_equal = lambda ctx, values: __import__("pyarrow.compute", fromlist=["equal"]).equal(values, __import__("pyarrow").scalar(20, type=__import__("pyarrow").int32()))' handler 'python_bvt_pc_equal' mode vector; create function python_bvt_pc_cast (x int) returns bigint language python as 'python_bvt_pc_cast = lambda ctx, values: __import__("pyarrow.compute", fromlist=["cast"]).cast(values, __import__("pyarrow").int64(), safe=True)' handler 'python_bvt_pc_cast' mode vector; create function python_bvt_pc_filter (x int) returns int language python as 'python_bvt_pc_filter = lambda ctx, values: __import__("pyarrow.compute", fromlist=["filter"]).filter(values, __import__("pyarrow.compute", fromlist=["greater_equal"]).greater_equal(values, 20), null_selection_behavior="drop")' handler 'python_bvt_pc_filter' mode vector; @@ -16,15 +17,16 @@ insert into selection_values values select id, python_bvt_pc_take(value, index_value) as taken, python_bvt_pc_sort_take(value) as sorted_value, +python_bvt_pc_sort_indices(value) as sorted_index, python_bvt_pc_equal(value) as equals_twenty, python_bvt_pc_cast(value) as cast_value from selection_values order by id; -➤ id[4,32,0] ¦ taken[4,32,0] ¦ sorted_value[4,32,0] ¦ equals_twenty[-7,1,0] ¦ cast_value[-5,64,0] 𝄀 -1 ¦ 20 ¦ 10 ¦ 0 ¦ 30 𝄀 -2 ¦ 30 ¦ 20 ¦ null ¦ null 𝄀 -3 ¦ null ¦ 30 ¦ 1 ¦ 20 𝄀 -4 ¦ 10 ¦ 40 ¦ 0 ¦ 10 𝄀 -5 ¦ null ¦ null ¦ 0 ¦ 40 +➤ id[4,32,0] ¦ taken[4,32,0] ¦ sorted_value[4,32,0] ¦ sorted_index[-5,64,0] ¦ equals_twenty[-7,1,0] ¦ cast_value[-5,64,0] 𝄀 +1 ¦ 20 ¦ 10 ¦ 3 ¦ 0 ¦ 30 𝄀 +2 ¦ 30 ¦ 20 ¦ 2 ¦ null ¦ null 𝄀 +3 ¦ null ¦ 30 ¦ 0 ¦ 1 ¦ 20 𝄀 +4 ¦ 10 ¦ 40 ¦ 4 ¦ 0 ¦ 10 𝄀 +5 ¦ null ¦ null ¦ 1 ¦ 0 ¦ 40 select python_bvt_pc_filter(value) from selection_values order by id; python udf: worker error: TYPE_CONTRACT: result length does not match input rows create table invalid_selection_values (value int, index_value int); @@ -33,6 +35,7 @@ select python_bvt_pc_take(value, index_value) from invalid_selection_values; python udf: worker error: USER_CODE: handler or runtime failed drop function python_bvt_pc_take(int, int); drop function python_bvt_pc_sort_take(int); +drop function python_bvt_pc_sort_indices(int); drop function python_bvt_pc_equal(int); drop function python_bvt_pc_cast(int); drop function python_bvt_pc_filter(int); diff --git a/test/distributed/cases/udf_python/arrow_compute_selection.sql b/test/distributed/cases/udf_python/arrow_compute_selection.sql index 1693664b4f53a..224caf9f4fe51 100644 --- a/test/distributed/cases/udf_python/arrow_compute_selection.sql +++ b/test/distributed/cases/udf_python/arrow_compute_selection.sql @@ -5,6 +5,7 @@ use udf_python_arrow_compute_selection_bvt; create function python_bvt_pc_take (x int, index_value int) returns int language python as 'python_bvt_pc_take = lambda ctx, values, indices: __import__("pyarrow.compute", fromlist=["take"]).take(values, indices, boundscheck=True)' handler 'python_bvt_pc_take' mode vector; create function python_bvt_pc_sort_take (x int) returns int language python as 'python_bvt_pc_sort_take = lambda ctx, values: __import__("pyarrow.compute", fromlist=["take"]).take(values, __import__("pyarrow.compute", fromlist=["sort_indices"]).sort_indices(values, null_placement="at_end"))' handler 'python_bvt_pc_sort_take' mode vector; +create function python_bvt_pc_sort_indices (x int) returns bigint unsigned language python as 'python_bvt_pc_sort_indices = lambda ctx, values: __import__("pyarrow.compute", fromlist=["sort_indices"]).sort_indices(values, null_placement="at_end")' handler 'python_bvt_pc_sort_indices' mode vector; create function python_bvt_pc_equal (x int) returns bool language python as 'python_bvt_pc_equal = lambda ctx, values: __import__("pyarrow.compute", fromlist=["equal"]).equal(values, __import__("pyarrow").scalar(20, type=__import__("pyarrow").int32()))' handler 'python_bvt_pc_equal' mode vector; create function python_bvt_pc_cast (x int) returns bigint language python as 'python_bvt_pc_cast = lambda ctx, values: __import__("pyarrow.compute", fromlist=["cast"]).cast(values, __import__("pyarrow").int64(), safe=True)' handler 'python_bvt_pc_cast' mode vector; create function python_bvt_pc_filter (x int) returns int language python as 'python_bvt_pc_filter = lambda ctx, values: __import__("pyarrow.compute", fromlist=["filter"]).filter(values, __import__("pyarrow.compute", fromlist=["greater_equal"]).greater_equal(values, 20), null_selection_behavior="drop")' handler 'python_bvt_pc_filter' mode vector; @@ -23,6 +24,7 @@ insert into selection_values values select id, python_bvt_pc_take(value, index_value) as taken, python_bvt_pc_sort_take(value) as sorted_value, + python_bvt_pc_sort_indices(value) as sorted_index, python_bvt_pc_equal(value) as equals_twenty, python_bvt_pc_cast(value) as cast_value from selection_values order by id; @@ -41,6 +43,7 @@ select python_bvt_pc_take(value, index_value) from invalid_selection_values; drop function python_bvt_pc_take(int, int); drop function python_bvt_pc_sort_take(int); +drop function python_bvt_pc_sort_indices(int); drop function python_bvt_pc_equal(int); drop function python_bvt_pc_cast(int); drop function python_bvt_pc_filter(int); From 8b420b9056a9423d31487d3c75876d770cc072d4 Mon Sep 17 00:00:00 2001 From: iamlinjunhong <1030420200@qq.com> Date: Fri, 18 Sep 2026 11:29:42 +0800 Subject: [PATCH 169/202] test: cover Arrow float arithmetic --- .../udf_python/arrow_compute_arithmetic.result | 18 ++++++++++++++++++ .../udf_python/arrow_compute_arithmetic.sql | 16 ++++++++++++++++ 2 files changed, 34 insertions(+) diff --git a/test/distributed/cases/udf_python/arrow_compute_arithmetic.result b/test/distributed/cases/udf_python/arrow_compute_arithmetic.result index 578de28fd8075..69ee2d94e0b96 100644 --- a/test/distributed/cases/udf_python/arrow_compute_arithmetic.result +++ b/test/distributed/cases/udf_python/arrow_compute_arithmetic.result @@ -6,6 +6,8 @@ create function python_bvt_pc_multiply (x int, y int) returns int language pytho create function python_bvt_pc_if_else (flag bool, x int, y int) returns int language python as 'python_bvt_pc_if_else = lambda ctx, flag, left, right: __import__("pyarrow.compute", fromlist=["if_else"]).if_else(flag, left, right)' handler 'python_bvt_pc_if_else' mode vector; create function python_bvt_pc_fill_null (x int) returns int language python as 'python_bvt_pc_fill_null = lambda ctx, values: __import__("pyarrow.compute", fromlist=["fill_null"]).fill_null(values, __import__("pyarrow").scalar(-7, type=__import__("pyarrow").int32()))' handler 'python_bvt_pc_fill_null' mode vector; create function python_bvt_pc_is_null (x int) returns bool language python as 'python_bvt_pc_is_null = lambda ctx, values: __import__("pyarrow.compute", fromlist=["is_null"]).is_null(values)' handler 'python_bvt_pc_is_null' mode vector; +create function python_bvt_pc_float_add (x float) returns float language python as 'python_bvt_pc_float_add = lambda ctx, values: __import__("pyarrow.compute", fromlist=["add"]).add(values, __import__("pyarrow").scalar(0.5, type=__import__("pyarrow").float32()))' handler 'python_bvt_pc_float_add' mode vector; +create function python_bvt_pc_double_add (x double) returns double language python as 'python_bvt_pc_double_add = lambda ctx, values: __import__("pyarrow.compute", fromlist=["add"]).add(values, __import__("pyarrow").scalar(0.5, type=__import__("pyarrow").float64()))' handler 'python_bvt_pc_double_add' mode vector; create table arithmetic_values (id int, flag bool, left_value int, right_value int); insert into arithmetic_values values (1, true, 1, 10), @@ -24,10 +26,26 @@ from arithmetic_values order by id; 2 ¦ null ¦ null ¦ 20 ¦ -7 ¦ 1 𝄀 3 ¦ 13 ¦ null ¦ null ¦ 3 ¦ 0 𝄀 4 ¦ null ¦ null ¦ null ¦ -7 ¦ 1 +create table float_values (id int, float_value float, double_value double); +insert into float_values values +(1, -1.25, 1.0), +(2, null, -2.5), +(3, 3.5, null); +select id, +python_bvt_pc_float_add(float_value) as float_added, +python_bvt_pc_double_add(double_value) as double_added +from float_values order by id; +➤ id[4,32,0] ¦ float_added[7,23,31] ¦ double_added[8,53,31] 𝄀 +1 ¦ -0.75 ¦ 1.5 𝄀 +2 ¦ null ¦ -2.0 𝄀 +3 ¦ 4.0 ¦ null drop function python_bvt_pc_add(int); drop function python_bvt_pc_multiply(int, int); drop function python_bvt_pc_if_else(bool, int, int); drop function python_bvt_pc_fill_null(int); drop function python_bvt_pc_is_null(int); +drop function python_bvt_pc_float_add(float); +drop function python_bvt_pc_double_add(double); +drop table float_values; drop table arithmetic_values; drop database udf_python_arrow_compute_arithmetic_bvt; diff --git a/test/distributed/cases/udf_python/arrow_compute_arithmetic.sql b/test/distributed/cases/udf_python/arrow_compute_arithmetic.sql index f1fbc290852ae..65f164f789c3e 100644 --- a/test/distributed/cases/udf_python/arrow_compute_arithmetic.sql +++ b/test/distributed/cases/udf_python/arrow_compute_arithmetic.sql @@ -8,6 +8,8 @@ create function python_bvt_pc_multiply (x int, y int) returns int language pytho create function python_bvt_pc_if_else (flag bool, x int, y int) returns int language python as 'python_bvt_pc_if_else = lambda ctx, flag, left, right: __import__("pyarrow.compute", fromlist=["if_else"]).if_else(flag, left, right)' handler 'python_bvt_pc_if_else' mode vector; create function python_bvt_pc_fill_null (x int) returns int language python as 'python_bvt_pc_fill_null = lambda ctx, values: __import__("pyarrow.compute", fromlist=["fill_null"]).fill_null(values, __import__("pyarrow").scalar(-7, type=__import__("pyarrow").int32()))' handler 'python_bvt_pc_fill_null' mode vector; create function python_bvt_pc_is_null (x int) returns bool language python as 'python_bvt_pc_is_null = lambda ctx, values: __import__("pyarrow.compute", fromlist=["is_null"]).is_null(values)' handler 'python_bvt_pc_is_null' mode vector; +create function python_bvt_pc_float_add (x float) returns float language python as 'python_bvt_pc_float_add = lambda ctx, values: __import__("pyarrow.compute", fromlist=["add"]).add(values, __import__("pyarrow").scalar(0.5, type=__import__("pyarrow").float32()))' handler 'python_bvt_pc_float_add' mode vector; +create function python_bvt_pc_double_add (x double) returns double language python as 'python_bvt_pc_double_add = lambda ctx, values: __import__("pyarrow.compute", fromlist=["add"]).add(values, __import__("pyarrow").scalar(0.5, type=__import__("pyarrow").float64()))' handler 'python_bvt_pc_double_add' mode vector; create table arithmetic_values (id int, flag bool, left_value int, right_value int); insert into arithmetic_values values @@ -26,10 +28,24 @@ select id, python_bvt_pc_is_null(left_value) as is_null from arithmetic_values order by id; +create table float_values (id int, float_value float, double_value double); +insert into float_values values + (1, -1.25, 1.0), + (2, null, -2.5), + (3, 3.5, null); + +select id, + python_bvt_pc_float_add(float_value) as float_added, + python_bvt_pc_double_add(double_value) as double_added +from float_values order by id; + drop function python_bvt_pc_add(int); drop function python_bvt_pc_multiply(int, int); drop function python_bvt_pc_if_else(bool, int, int); drop function python_bvt_pc_fill_null(int); drop function python_bvt_pc_is_null(int); +drop function python_bvt_pc_float_add(float); +drop function python_bvt_pc_double_add(double); +drop table float_values; drop table arithmetic_values; drop database udf_python_arrow_compute_arithmetic_bvt; From 77089229bb986caeba0e9839f58a520a6b3fa6a0 Mon Sep 17 00:00:00 2001 From: iamlinjunhong <1030420200@qq.com> Date: Fri, 18 Sep 2026 11:44:58 +0800 Subject: [PATCH 170/202] fix: preserve vector size during Python UDF binding --- pkg/frontend/compiler_context.go | 18 ++++++++++++------ .../cases/udf_python/vector_mode.result | 16 ++++++++++++++++ .../cases/udf_python/vector_mode.sql | 15 +++++++++++++++ 3 files changed, 43 insertions(+), 6 deletions(-) diff --git a/pkg/frontend/compiler_context.go b/pkg/frontend/compiler_context.go index b6241c2b62217..c6b643983ed81 100644 --- a/pkg/frontend/compiler_context.go +++ b/pkg/frontend/compiler_context.go @@ -849,12 +849,18 @@ func (tcc *TxnCompilerContext) ResolveUdf(name string, args []*plan.Expr) (udf * if execResultArrayHasData(erArray) { fromList := make([]types.Type, len(args)) for i, arg := range args { - fromList[i] = types.Type{ - Oid: types.T(arg.Typ.Id), - Charset: uint8(arg.Typ.Charset), - Width: arg.Typ.Width, - Scale: arg.Typ.Scale, - } + // Reconstruct the complete SQL type instead of filling only the + // plan-visible fields. Type.Eq includes the physical size, which is + // not serialized in plan.Type but is required for exact Python ABI + // matching. In particular, a vector type built with Size == 0 is + // rejected against the declared VECF32/VECF64 descriptor because its + // fixed child width is no longer comparable. + fromList[i] = types.NewWithCharset( + types.T(arg.Typ.Id), + arg.Typ.Width, + arg.Typ.Scale, + uint8(arg.Typ.Charset), + ) argTypeStr += strings.ToLower(fromList[i].String()) if i+1 != len(args) { diff --git a/test/distributed/cases/udf_python/vector_mode.result b/test/distributed/cases/udf_python/vector_mode.result index 3c7c9bd29c6e5..aed22dc97f8b7 100644 --- a/test/distributed/cases/udf_python/vector_mode.result +++ b/test/distributed/cases/udf_python/vector_mode.result @@ -4,6 +4,8 @@ use udf_python_vector_bvt; create function python_bvt_vector (x int) returns int language python as 'python_bvt_vector = lambda ctx, values: __import__("pyarrow").array([-99 if value.as_py() is None else value.as_py() + 10 for value in values], type=__import__("pyarrow").int32())' handler 'python_bvt_vector' mode vector; create function python_bvt_vector_strict (x int) returns int language python as 'python_bvt_vector_strict = lambda ctx, values: __import__("pyarrow").array([value.as_py() + 10 for value in values], type=__import__("pyarrow").int32())' handler 'python_bvt_vector_strict' mode vector returns null on null input; create function python_bvt_zero_vector () returns int language python as 'python_bvt_zero_vector = lambda ctx: __import__("pyarrow").array([7] * ctx.num_rows, type=__import__("pyarrow").int32())' handler 'python_bvt_zero_vector' mode vector; +create function python_bvt_vecf32_identity (x vecf32(3)) returns vecf32(3) language python as 'python_bvt_vecf32_identity = lambda ctx, values: values' handler 'python_bvt_vecf32_identity' mode vector; +create function python_bvt_vecf64_identity (x vecf64(3)) returns vecf64(3) language python as 'python_bvt_vecf64_identity = lambda ctx, values: values' handler 'python_bvt_vecf64_identity' mode vector; create table vector_values (id int, value int); insert into vector_values values (1, 1), (2, null), (3, 2); select id, python_bvt_vector(value) as called_result @@ -24,8 +26,22 @@ from vector_values order by id; 1 ¦ 7 𝄀 2 ¦ 7 𝄀 3 ¦ 7 +create table vector_array_values (id int, f32 vecf32(3), f64 vecf64(3)); +insert into vector_array_values values +(1, cast("[1,2,3]" as vecf32(3)), cast("[1.5,2.5,3.5]" as vecf64(3))), +(2, null, null); +select id, +python_bvt_vecf32_identity(f32) as f32_identity, +python_bvt_vecf64_identity(f64) as f64_identity +from vector_array_values order by id; +➤ id[4,32,0] ¦ f32_identity[12,3,0] ¦ f64_identity[12,3,0] 𝄀 +1 ¦ [1, 2, 3] ¦ [1.5, 2.5, 3.5] 𝄀 +2 ¦ null ¦ null drop function python_bvt_vector(int); drop function python_bvt_vector_strict(int); drop function python_bvt_zero_vector(); +drop function python_bvt_vecf32_identity(vecf32(3)); +drop function python_bvt_vecf64_identity(vecf64(3)); +drop table vector_array_values; drop table vector_values; drop database udf_python_vector_bvt; diff --git a/test/distributed/cases/udf_python/vector_mode.sql b/test/distributed/cases/udf_python/vector_mode.sql index 6051e26aa69ab..e3b8d90525587 100644 --- a/test/distributed/cases/udf_python/vector_mode.sql +++ b/test/distributed/cases/udf_python/vector_mode.sql @@ -7,6 +7,8 @@ use udf_python_vector_bvt; create function python_bvt_vector (x int) returns int language python as 'python_bvt_vector = lambda ctx, values: __import__("pyarrow").array([-99 if value.as_py() is None else value.as_py() + 10 for value in values], type=__import__("pyarrow").int32())' handler 'python_bvt_vector' mode vector; create function python_bvt_vector_strict (x int) returns int language python as 'python_bvt_vector_strict = lambda ctx, values: __import__("pyarrow").array([value.as_py() + 10 for value in values], type=__import__("pyarrow").int32())' handler 'python_bvt_vector_strict' mode vector returns null on null input; create function python_bvt_zero_vector () returns int language python as 'python_bvt_zero_vector = lambda ctx: __import__("pyarrow").array([7] * ctx.num_rows, type=__import__("pyarrow").int32())' handler 'python_bvt_zero_vector' mode vector; +create function python_bvt_vecf32_identity (x vecf32(3)) returns vecf32(3) language python as 'python_bvt_vecf32_identity = lambda ctx, values: values' handler 'python_bvt_vecf32_identity' mode vector; +create function python_bvt_vecf64_identity (x vecf64(3)) returns vecf64(3) language python as 'python_bvt_vecf64_identity = lambda ctx, values: values' handler 'python_bvt_vecf64_identity' mode vector; create table vector_values (id int, value int); insert into vector_values values (1, 1), (2, null), (3, 2); @@ -24,8 +26,21 @@ from vector_values order by id; select id, python_bvt_zero_vector() as zero_result from vector_values order by id; +create table vector_array_values (id int, f32 vecf32(3), f64 vecf64(3)); +insert into vector_array_values values + (1, cast("[1,2,3]" as vecf32(3)), cast("[1.5,2.5,3.5]" as vecf64(3))), + (2, null, null); + +select id, + python_bvt_vecf32_identity(f32) as f32_identity, + python_bvt_vecf64_identity(f64) as f64_identity +from vector_array_values order by id; + drop function python_bvt_vector(int); drop function python_bvt_vector_strict(int); drop function python_bvt_zero_vector(); +drop function python_bvt_vecf32_identity(vecf32(3)); +drop function python_bvt_vecf64_identity(vecf64(3)); +drop table vector_array_values; drop table vector_values; drop database udf_python_vector_bvt; From 409733d156901265d2b68eac932a501b35ad1f7b Mon Sep 17 00:00:00 2001 From: iamlinjunhong <1030420200@qq.com> Date: Fri, 18 Sep 2026 11:49:55 +0800 Subject: [PATCH 171/202] fix: keep BVT services alive for test runner --- optools/run_bvt.sh | 28 ++++++++++++++++++++++------ 1 file changed, 22 insertions(+), 6 deletions(-) diff --git a/optools/run_bvt.sh b/optools/run_bvt.sh index 15a06ad901d10..2a8104fec57dd 100755 --- a/optools/run_bvt.sh +++ b/optools/run_bvt.sh @@ -83,9 +83,8 @@ function cleanup_all() { cleanup_mo } -trap cleanup_all EXIT -trap 'exit 130' INT -trap 'exit 143' TERM +trap 'cleanup_all; exit 130' INT +trap 'cleanup_all; exit 143' TERM function launch_mo() { cd $MO_WORKSPACE @@ -204,11 +203,28 @@ launch_mo if [[ "${SKIP_JSTFU:-false}" == "true" ]]; then echo "skip jstfu for this BVT suite" else - launch_jstfu || exit $? + launch_jstfu + status=$? + if [[ "$status" -ne 0 ]]; then + cleanup_all + exit "$status" + fi fi wait_system_init -if [[ "$?" -ne 0 ]]; then +status=$? +if [[ "$status" -ne 0 ]]; then + cleanup_all exit 1 fi wait_python_udf_worker -exit $? +status=$? +if [[ "$status" -ne 0 ]]; then + cleanup_all + exit "$status" +fi + +# This script is a launcher: its caller runs mo-tester after this command +# returns. Keep the services alive on a successful return; signal and startup +# failure paths above own cleanup because no caller can use the deployment in +# those cases. +exit 0 From 3bfcfa712d1e4e1c7bf1759c3ed64324619473df Mon Sep 17 00:00:00 2001 From: iamlinjunhong <1030420200@qq.com> Date: Fri, 18 Sep 2026 11:59:53 +0800 Subject: [PATCH 172/202] fix: clean BVT services after test completion --- optools/bvt_ut/entrypoint.sh | 6 +++++- optools/run_bvt.sh | 11 ++++++++--- 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/optools/bvt_ut/entrypoint.sh b/optools/bvt_ut/entrypoint.sh index e048892438fc9..12c42fba39ada 100755 --- a/optools/bvt_ut/entrypoint.sh +++ b/optools/bvt_ut/entrypoint.sh @@ -40,7 +40,11 @@ function run_bvt() { make build echo ">>>>>>>>>>>>>>>>>>>>>>>> start mo service" - ./optools/run_bvt.sh ./ "${LAUNCH}" + # Source the launcher so this entrypoint owns the service children until + # mo-tester finishes. A standalone run_bvt.sh invocation still returns + # with services alive for callers that launch the tester separately. + source ./optools/run_bvt.sh ./ "${LAUNCH}" + trap 'cleanup_all; packLog' EXIT echo ">>>>>>>>>>>>>>>>>>>>>>>> start bvt" # use test/distributed/cases as default test cases diff --git a/optools/run_bvt.sh b/optools/run_bvt.sh index 2a8104fec57dd..dae492f5d098c 100755 --- a/optools/run_bvt.sh +++ b/optools/run_bvt.sh @@ -51,9 +51,11 @@ function cleanup_mo() { kill -KILL "$MO_PID" 2>/dev/null || true fi fi - for child in "${BVT_CHILD_PIDS[@]}"; do - kill -KILL "$child" 2>/dev/null || true - done + if ((${#BVT_CHILD_PIDS[@]} > 0)); then + for child in "${BVT_CHILD_PIDS[@]}"; do + kill -KILL "$child" 2>/dev/null || true + done + fi wait "$pid" 2>/dev/null || true MO_PID="" } @@ -227,4 +229,7 @@ fi # returns. Keep the services alive on a successful return; signal and startup # failure paths above own cleanup because no caller can use the deployment in # those cases. +if [[ "${BASH_SOURCE[0]}" != "$0" ]]; then + return 0 +fi exit 0 From 014908655c37a3c7014488b738739f6df7c7038a Mon Sep 17 00:00:00 2001 From: iamlinjunhong <1030420200@qq.com> Date: Fri, 18 Sep 2026 14:30:56 +0800 Subject: [PATCH 173/202] fix: harden BVT launcher argument handling --- optools/run_bvt.sh | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/optools/run_bvt.sh b/optools/run_bvt.sh index dae492f5d098c..8de68313867c8 100755 --- a/optools/run_bvt.sh +++ b/optools/run_bvt.sh @@ -16,9 +16,15 @@ set -o nounset +if (( $# < 2 )); then + echo "usage: $0 [proxy-args ...]" >&2 + exit 2 +fi + MO_WORKSPACE=$1 LAUNCH=$2 -PROXY=${3:-} +shift 2 +PROXY_ARGS=("$@") LAUNCH_CONFIG=$LAUNCH MO_PID="" JSTFU_PID="" @@ -89,7 +95,7 @@ trap 'cleanup_all; exit 130' INT trap 'cleanup_all; exit 143' TERM function launch_mo() { - cd $MO_WORKSPACE + cd "$MO_WORKSPACE" # Ordinary launch BVT remains the single CI entry point, but its Python # cases opt into the worker-enabled manifest explicitly. The generic # etc/launch manifest stays safe for users who start mo-service directly. @@ -97,7 +103,11 @@ function launch_mo() { if [[ "$LAUNCH" == "launch" ]]; then LAUNCH_CONFIG=launch-with-python-udf-worker fi - ./mo-service -debug-http=:12345 -launch ./etc/$LAUNCH_CONFIG/launch.toml $PROXY &>mo-service.log & + if ((${#PROXY_ARGS[@]} > 0)); then + ./mo-service -debug-http=:12345 -launch "./etc/${LAUNCH_CONFIG}/launch.toml" "${PROXY_ARGS[@]}" &>mo-service.log & + else + ./mo-service -debug-http=:12345 -launch "./etc/${LAUNCH_CONFIG}/launch.toml" &>mo-service.log & + fi MO_PID=$! } @@ -126,7 +136,7 @@ function wait_python_udf_worker() { do if [[ -n "$MO_PID" ]] && ! kill -0 "$MO_PID" 2>/dev/null; then echo "MatrixOne exited before Python UDF worker became ready" >&2 - tail -n 160 mo-service.log >&2 || true + tail -n 160 "$MO_WORKSPACE/mo-service.log" >&2 || true return 1 fi if python3 - <<'PY' @@ -163,7 +173,7 @@ PY sleep 1 done echo "Python UDF capability handshake did not become ready on 127.0.0.1:50051" >&2 - tail -n 160 mo-service.log >&2 || true + tail -n 160 "$MO_WORKSPACE/mo-service.log" >&2 || true return 1 } @@ -175,7 +185,7 @@ PY # the Maven wrapper bootstraps its own Maven, and the pom emits Java 8 # bytecode on any JDK. function launch_jstfu() { - cd $MO_WORKSPACE + cd "$MO_WORKSPACE" if bash -c 'exec 3<>/dev/tcp/127.0.0.1/4444' 2>/dev/null; then echo "jstfu already listening on :4444, skip" return 0 From a15733961ba02b60cf6576fff493563e9bbff2aa Mon Sep 17 00:00:00 2001 From: iamlinjunhong <1030420200@qq.com> Date: Fri, 18 Sep 2026 14:37:55 +0800 Subject: [PATCH 174/202] fix: freeze complete Arrow result frames --- pkg/udf/python/gateway.go | 53 +++++++++++++++++++++++++--------- pkg/udf/python/gateway_test.go | 17 +++++++++++ 2 files changed, 57 insertions(+), 13 deletions(-) diff --git a/pkg/udf/python/gateway.go b/pkg/udf/python/gateway.go index 1b4e04bc445c0..c619d59137d3e 100644 --- a/pkg/udf/python/gateway.go +++ b/pkg/udf/python/gateway.go @@ -1540,22 +1540,19 @@ func (g *Gateway) receiveResultBatch( if err := sequence.AcceptResult(control.Sequence); err != nil { return 0, err } - snapshot, err := protocol.FreezeOutput(data.DataBody, g.cfg.MaxBatchBytes) + headerSnapshot, bodySnapshot, err := freezeResultFrames( + data.DataHeader, + data.DataBody, + g.cfg.MaxBatchBytes, + ) if err != nil { return 0, err } - // FreezeOutput already stores the digest of its private copy. An - // empty expectedDigest still makes Validate rehash that same - // backing and detect any trusted-boundary mutation, without - // allocating the hexadecimal digest string twice on every batch. - if err := snapshot.Validate(len(data.DataBody), ""); err != nil { - return 0, err - } - // DecodeRecordBatch consumes the result header synchronously before - // the next Recv. The schema header is retained across batches, but - // this per-batch header has no cross-call lifetime, so keep the - // Flight-owned slice and avoid a redundant allocation/copy. - decoded, err := DecodeRecordBatch(**schemaFrame, ArrowFrame{Header: data.DataHeader, Body: snapshot.TrustedBytes()}, g.cfg.MaxBatchBytes) + // Both Flight buffers are now private snapshots. DecodeRecordBatch + // consumes exactly those validated backings; no later mutation of the + // received FlightData can change the bytes that crossed the trusted + // validation/publication boundary. + decoded, err := DecodeRecordBatch(**schemaFrame, ArrowFrame{Header: headerSnapshot.TrustedBytes(), Body: bodySnapshot.TrustedBytes()}, g.cfg.MaxBatchBytes) if err != nil { return 0, fmt.Errorf("python udf: decode result: %w", err) } @@ -1628,6 +1625,36 @@ func (g *Gateway) receiveResultBatch( } } +// freezeResultFrames takes ownership of both IPC pieces before either one is +// validated or decoded. MaxBatchBytes describes the complete per-batch +// payload, including the record message header and body; otherwise a large +// header could bypass the body-only limit used by the old result path. +func freezeResultFrames(header, body []byte, maxBytes int64) (*protocol.OutputSnapshot, *protocol.OutputSnapshot, error) { + if maxBytes <= 0 { + return nil, nil, fmt.Errorf("invalid Python UDF result batch limit") + } + headerBytes := int64(len(header)) + bodyBytes := int64(len(body)) + if headerBytes > maxBytes || bodyBytes > maxBytes-headerBytes { + return nil, nil, fmt.Errorf("RESOURCE_EXHAUSTED: Arrow result batch exceeds %d bytes", maxBytes) + } + headerSnapshot, err := protocol.FreezeOutput(header, maxBytes) + if err != nil { + return nil, nil, err + } + bodySnapshot, err := protocol.FreezeOutput(body, maxBytes) + if err != nil { + return nil, nil, err + } + if err := headerSnapshot.Validate(len(header), headerSnapshot.Digest()); err != nil { + return nil, nil, err + } + if err := bodySnapshot.Validate(len(body), bodySnapshot.Digest()); err != nil { + return nil, nil, err + } + return headerSnapshot, bodySnapshot, nil +} + type executionBudget struct { rows int64 maxRows int64 diff --git a/pkg/udf/python/gateway_test.go b/pkg/udf/python/gateway_test.go index fc47c4d3e48ea..3a9c2cb07e823 100644 --- a/pkg/udf/python/gateway_test.go +++ b/pkg/udf/python/gateway_test.go @@ -643,3 +643,20 @@ func TestGatewayRejectsArrowBodyOnControlFrame(t *testing.T) { ) require.ErrorContains(t, err, "unexpected Arrow data") } + +func TestFreezeResultFramesFreezesBothPartsAndChargesTotal(t *testing.T) { + header := []byte("record-header") + body := []byte("record-body") + headerSnapshot, bodySnapshot, err := freezeResultFrames(header, body, int64(len(header)+len(body))) + require.NoError(t, err) + require.NoError(t, headerSnapshot.Validate(len(header), headerSnapshot.Digest())) + require.NoError(t, bodySnapshot.Validate(len(body), bodySnapshot.Digest())) + + header[0] = 'X' + body[0] = 'Y' + require.Equal(t, "record-header", string(headerSnapshot.TrustedBytes())) + require.Equal(t, "record-body", string(bodySnapshot.TrustedBytes())) + + _, _, err = freezeResultFrames(header, body, int64(len(header)+len(body)-1)) + require.ErrorContains(t, err, "exceeds") +} From 4d38f74f9a98a93a6af745bd5681c6a126b095c3 Mon Sep 17 00:00:00 2001 From: iamlinjunhong <1030420200@qq.com> Date: Fri, 18 Sep 2026 14:39:52 +0800 Subject: [PATCH 175/202] fix: verify BVT service liveness during startup --- optools/run_bvt.sh | 27 ++++++++++++++++++++++++--- 1 file changed, 24 insertions(+), 3 deletions(-) diff --git a/optools/run_bvt.sh b/optools/run_bvt.sh index 8de68313867c8..4c3bda99b063a 100755 --- a/optools/run_bvt.sh +++ b/optools/run_bvt.sh @@ -115,15 +115,36 @@ function launch_mo() { function wait_system_init() { for num in {1..300} do + if [[ -z "$MO_PID" ]] || ! process_is_alive "$MO_PID"; then + echo "MatrixOne exited before SQL readiness" >&2 + tail -n 160 "$MO_WORKSPACE/mo-service.log" >&2 || true + return 1 + fi if MYSQL_PWD=111 mysql --connect-timeout=2 -h 127.0.0.1 -P 6001 -u dump -e "show databases;"; then - echo "ok, cost $num seconds" - return 0 + if process_is_alive "$MO_PID"; then + echo "ok, cost $num seconds" + return 0 + fi + echo "MatrixOne exited while SQL readiness was being verified" >&2 + tail -n 160 "$MO_WORKSPACE/mo-service.log" >&2 || true + return 1 fi sleep 1 done return 1 } +function process_is_alive() { + local pid="$1" + local state + if ! kill -0 "$pid" 2>/dev/null; then + return 1 + fi + state=$(ps -o stat= -p "$pid" 2>/dev/null || true) + state=${state//[[:space:]]/} + [[ -n "$state" && "$state" != Z* ]] +} + # MySQL readiness only proves that the CN frontend accepted connections. The # worker is a separately started service, so Python UDF DDL/execution can still # race its Flight listener. Keep this in the existing BVT launcher instead of @@ -134,7 +155,7 @@ function wait_python_udf_worker() { fi for num in {1..120} do - if [[ -n "$MO_PID" ]] && ! kill -0 "$MO_PID" 2>/dev/null; then + if [[ -n "$MO_PID" ]] && ! process_is_alive "$MO_PID"; then echo "MatrixOne exited before Python UDF worker became ready" >&2 tail -n 160 "$MO_WORKSPACE/mo-service.log" >&2 || true return 1 From 9bf61b635fb7f45e0737bbe78aea339c4bd4a58d Mon Sep 17 00:00:00 2001 From: iamlinjunhong <1030420200@qq.com> Date: Fri, 18 Sep 2026 14:41:51 +0800 Subject: [PATCH 176/202] docs: fix Python UDF BVT generation example --- test/distributed/cases/udf_python/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/distributed/cases/udf_python/README.md b/test/distributed/cases/udf_python/README.md index 5bf9621efe91c..087f65dd69de4 100644 --- a/test/distributed/cases/udf_python/README.md +++ b/test/distributed/cases/udf_python/README.md @@ -22,7 +22,7 @@ the normal worker-enabled launch running and use `mo-tester`'s `genrs` mode: ```bash SKIP_JSTFU=true ./optools/run_bvt.sh . launch cd /path/to/mo-tester -./run.sh -p /path/to/matrixone/test/distributed/cases/udf_python/example.sql -m genrs -g +./run.sh -p /path/to/matrixone/test/distributed/cases/udf_python/control_flow.sql -m genrs -g ``` The ordinary BVT launch starts one worker for the single CN. Compose BVT starts From 2d6e90025b36a8eea4bb99f8feeba8b2430a1c7e Mon Sep 17 00:00:00 2001 From: iamlinjunhong <1030420200@qq.com> Date: Fri, 18 Sep 2026 14:43:34 +0800 Subject: [PATCH 177/202] test: make compose worker readiness verify capabilities --- etc/launch-tae-compose/compose.yaml | 7 +-- pkg/udf/python/worker/Dockerfile | 2 +- pkg/udf/python/worker/healthcheck.py | 33 ++++++++++++ pkg/udf/python/worker/test_healthcheck.py | 64 +++++++++++++++++++++++ 4 files changed, 102 insertions(+), 4 deletions(-) create mode 100644 pkg/udf/python/worker/healthcheck.py create mode 100644 pkg/udf/python/worker/test_healthcheck.py diff --git a/etc/launch-tae-compose/compose.yaml b/etc/launch-tae-compose/compose.yaml index 2b30ae4e3f4c4..713b7e0f9bed3 100644 --- a/etc/launch-tae-compose/compose.yaml +++ b/etc/launch-tae-compose/compose.yaml @@ -35,10 +35,11 @@ x-python-udf-worker: &python-udf-worker - launch - launch-multi-cn healthcheck: - test: ["CMD", "python", "-c", "import socket; s=socket.create_connection(('127.0.0.1', 50051), 1); s.close()"] - interval: 1s - timeout: 2s + test: ["CMD", "python", "healthcheck.py"] + interval: 2s + timeout: 3s retries: 30 + start_period: 1s services: diff --git a/pkg/udf/python/worker/Dockerfile b/pkg/udf/python/worker/Dockerfile index f2403bec94263..4fbcaf7784120 100644 --- a/pkg/udf/python/worker/Dockerfile +++ b/pkg/udf/python/worker/Dockerfile @@ -27,7 +27,7 @@ WORKDIR /home/mo # && apt-get update \ # && apt-get install -y vim -COPY requirements.txt worker.py watchdog.py ./ +COPY requirements.txt worker.py watchdog.py healthcheck.py ./ RUN pip install --no-cache-dir -r requirements.txt diff --git a/pkg/udf/python/worker/healthcheck.py b/pkg/udf/python/worker/healthcheck.py new file mode 100644 index 0000000000000..05cfaab270bae --- /dev/null +++ b/pkg/udf/python/worker/healthcheck.py @@ -0,0 +1,33 @@ +"""Capability health check used by the container orchestrator.""" + +import json + +import pyarrow.flight as flight + + +PROTOCOL_VERSION = 1 +CAPABILITY_ACTION = "GetPythonCapabilities" + + +def check_capabilities( + address="grpc://127.0.0.1:50051", client_factory=flight.FlightClient +): + client = client_factory(address) + reader = client.do_action( + flight.Action(CAPABILITY_ACTION, b'{"protocol_version":1}') + ) + result = next(iter(reader), None) + if result is None: + raise RuntimeError("empty capability response") + capabilities = json.loads(bytes(result.body)) + if capabilities.get("protocol_version") != PROTOCOL_VERSION: + raise RuntimeError("unsupported capability protocol version") + return capabilities + + +def main(): + check_capabilities() + + +if __name__ == "__main__": + main() diff --git a/pkg/udf/python/worker/test_healthcheck.py b/pkg/udf/python/worker/test_healthcheck.py new file mode 100644 index 0000000000000..4cd0fa1dc843e --- /dev/null +++ b/pkg/udf/python/worker/test_healthcheck.py @@ -0,0 +1,64 @@ +import unittest + +import healthcheck + + +class _Result: + def __init__(self, body): + self.body = body + + +class _Reader: + def __init__(self, results): + self.results = iter(results) + + def __iter__(self): + return self + + def __next__(self): + return next(self.results) + + +class _Client: + def __init__(self, address, results): + self.address = address + self.results = results + self.action = None + + def do_action(self, action): + self.action = action + return _Reader(self.results) + + +class HealthcheckTest(unittest.TestCase): + def test_accepts_current_capability_protocol(self): + clients = [] + + def factory(address): + client = _Client( + address, [_Result(b'{"protocol_version":1}')] + ) + clients.append(client) + return client + + capabilities = healthcheck.check_capabilities( + "grpc://worker:50051", client_factory=factory + ) + + self.assertEqual(capabilities["protocol_version"], 1) + self.assertEqual(clients[0].address, "grpc://worker:50051") + self.assertEqual(clients[0].action.type, "GetPythonCapabilities") + self.assertEqual(clients[0].action.body, b'{"protocol_version":1}') + + def test_rejects_empty_or_unknown_protocol(self): + for response in (None, b'{"protocol_version":2}'): + def factory(address, response=response): + results = [] if response is None else [_Result(response)] + return _Client(address, results) + + with self.assertRaises(RuntimeError): + healthcheck.check_capabilities(client_factory=factory) + + +if __name__ == "__main__": + unittest.main() From 4a85376a5ea9e93e496495cd849924a3cad268ab Mon Sep 17 00:00:00 2001 From: iamlinjunhong <1030420200@qq.com> Date: Fri, 18 Sep 2026 15:45:07 +0800 Subject: [PATCH 178/202] fix: clean BVT services on startup failure --- optools/run_bvt.sh | 35 +++++++++++++++++++++++++---------- 1 file changed, 25 insertions(+), 10 deletions(-) diff --git a/optools/run_bvt.sh b/optools/run_bvt.sh index 4c3bda99b063a..93548b124991f 100755 --- a/optools/run_bvt.sh +++ b/optools/run_bvt.sh @@ -236,23 +236,38 @@ launch_mo if [[ "${SKIP_JSTFU:-false}" == "true" ]]; then echo "skip jstfu for this BVT suite" else - launch_jstfu - status=$? - if [[ "$status" -ne 0 ]]; then + # Keep the command in an if condition. The BVT entrypoint sources this + # file with `set -e`; a bare failing function call would exit before the + # cleanup block below can reclaim the already-started services. + if launch_jstfu; then + : + else + status=$? cleanup_all + if [[ "${BASH_SOURCE[0]}" != "$0" ]]; then + return "$status" + fi exit "$status" fi fi -wait_system_init -status=$? -if [[ "$status" -ne 0 ]]; then +if wait_system_init; then + : +else + status=$? cleanup_all - exit 1 + if [[ "${BASH_SOURCE[0]}" != "$0" ]]; then + return "$status" + fi + exit "$status" fi -wait_python_udf_worker -status=$? -if [[ "$status" -ne 0 ]]; then +if wait_python_udf_worker; then + : +else + status=$? cleanup_all + if [[ "${BASH_SOURCE[0]}" != "$0" ]]; then + return "$status" + fi exit "$status" fi From fab4c5971757ceae1490801226920bbb703ff38a Mon Sep 17 00:00:00 2001 From: iamlinjunhong <1030420200@qq.com> Date: Fri, 18 Sep 2026 15:45:59 +0800 Subject: [PATCH 179/202] ci: preserve BVT logs when setup fails --- optools/bvt_ut/entrypoint.sh | 24 +++++++++++++++++++----- 1 file changed, 19 insertions(+), 5 deletions(-) diff --git a/optools/bvt_ut/entrypoint.sh b/optools/bvt_ut/entrypoint.sh index 12c42fba39ada..581c5b715de95 100755 --- a/optools/bvt_ut/entrypoint.sh +++ b/optools/bvt_ut/entrypoint.sh @@ -4,18 +4,32 @@ set -euo pipefail SECONDS=0 -# mv log to mount path +# Move logs to the mounted path without masking the original CI failure when +# preparation or compilation stops before one of the normal artifacts exists. function packLog() { - mv /matrixone-test/mo-service.log /matrixone-test/tester-log - mv /matrixone-test/mo-tester/report /matrixone-test/tester-log - mv /root/scratch /matrixone-test/tester-log + local log_dir=/matrixone-test/tester-log + if ! mkdir -p "$log_dir"; then + echo "failed to create $log_dir; preserving the original CI failure" >&2 + return 0 + fi + for artifact in \ + /matrixone-test/mo-service.log \ + /matrixone-test/mo-tester/report \ + /root/scratch; do + if [[ ! -e "$artifact" ]]; then + continue + fi + if ! mv "$artifact" "$log_dir"; then + echo "failed to archive $artifact" >&2 + fi + done duration=$SECONDS echo "$(($duration / 60)) minutes and $(($duration % 60)) seconds elapsed." } function prepare() { - mkdir /root/scratch + mkdir -p /root/scratch echo ">>>>>>>>>>>>>>>>>>>>>>>> show locale" echo `locale` From ee3ab5a9a96fd2126d50935724ce489127e0b181 Mon Sep 17 00:00:00 2001 From: iamlinjunhong <1030420200@qq.com> Date: Fri, 18 Sep 2026 15:54:02 +0800 Subject: [PATCH 180/202] fix: avoid waiting on exited BVT processes --- optools/run_bvt.sh | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/optools/run_bvt.sh b/optools/run_bvt.sh index 93548b124991f..ea243b05d4ed9 100755 --- a/optools/run_bvt.sh +++ b/optools/run_bvt.sh @@ -45,15 +45,15 @@ function cleanup_mo() { local pid="$MO_PID" local -a BVT_CHILD_PIDS=() collect_descendants "$pid" - if kill -0 "$MO_PID" 2>/dev/null; then + if process_is_alive "$MO_PID"; then kill -TERM "$MO_PID" 2>/dev/null || true for _ in {1..30}; do - if ! kill -0 "$MO_PID" 2>/dev/null; then + if ! process_is_alive "$MO_PID"; then break fi sleep 1 done - if kill -0 "$MO_PID" 2>/dev/null; then + if process_is_alive "$MO_PID"; then kill -KILL "$MO_PID" 2>/dev/null || true fi fi @@ -70,15 +70,15 @@ function cleanup_jstfu() { if [[ -z "$JSTFU_PID" ]]; then return fi - if kill -0 "$JSTFU_PID" 2>/dev/null; then + if process_is_alive "$JSTFU_PID"; then kill -TERM "$JSTFU_PID" 2>/dev/null || true for _ in {1..15}; do - if ! kill -0 "$JSTFU_PID" 2>/dev/null; then + if ! process_is_alive "$JSTFU_PID"; then break fi sleep 1 done - if kill -0 "$JSTFU_PID" 2>/dev/null; then + if process_is_alive "$JSTFU_PID"; then kill -KILL "$JSTFU_PID" 2>/dev/null || true fi fi From 1a689a8d04f15839be0b7037333cb6b19729e96c Mon Sep 17 00:00:00 2001 From: iamlinjunhong <1030420200@qq.com> Date: Fri, 18 Sep 2026 15:58:16 +0800 Subject: [PATCH 181/202] fix: keep handler quota ownership retryable --- pkg/udf/python/worker/test_worker.py | 19 +++++++++++++++++++ pkg/udf/python/worker/worker.py | 7 ++++++- 2 files changed, 25 insertions(+), 1 deletion(-) diff --git a/pkg/udf/python/worker/test_worker.py b/pkg/udf/python/worker/test_worker.py index b48b2333ed010..a0bd3ec02fd6e 100644 --- a/pkg/udf/python/worker/test_worker.py +++ b/pkg/udf/python/worker/test_worker.py @@ -1938,6 +1938,25 @@ def test_handler_quota_is_scoped_by_account_and_owner(self): third.release() self.assertEqual(({}, {}), quota.counts()) + def test_handler_quota_release_can_retry_after_counter_failure(self): + quota = worker._HandlerQuota(max_account_handlers=1, max_owner_handlers=1) + lease = quota.acquire(1, "alice") + real_release = quota._release + attempts = 0 + + def fail_once(account_id, owner_id): + nonlocal attempts + attempts += 1 + if attempts == 1: + raise RuntimeError("injected release failure") + return real_release(account_id, owner_id) + + with mock.patch.object(quota, "_release", side_effect=fail_once): + with self.assertRaisesRegex(RuntimeError, "injected release failure"): + lease.release() + lease.release() + self.assertEqual(({}, {}), quota.counts()) + def test_handler_initialization_closes_partial_pipe_ownership(self): real_pipe = os.pipe for failure in ("second_pipe", "watchdog_dup"): diff --git a/pkg/udf/python/worker/worker.py b/pkg/udf/python/worker/worker.py index df3d63a551f1b..0a081de3e83e8 100644 --- a/pkg/udf/python/worker/worker.py +++ b/pkg/udf/python/worker/worker.py @@ -2519,8 +2519,13 @@ def release(self) -> None: with self._lock: if self._released: return + # Keep ownership until the quota counter has actually been + # decremented. Cleanup callers retry a failed release; marking + # this token first would turn a transient quota error into a + # permanent handler-budget leak because the retry would become a + # no-op. + self._quota._release(self._account_id, self._owner_id) self._released = True - self._quota._release(self._account_id, self._owner_id) class _HandlerQuota: From e7df36b1513f9e16e1d1970246720c138909f839 Mon Sep 17 00:00:00 2001 From: iamlinjunhong <1030420200@qq.com> Date: Fri, 18 Sep 2026 16:20:23 +0800 Subject: [PATCH 182/202] fix: degrade python runtime initialization failures --- pkg/cnservice/python_runtime.go | 105 +++++++++++++++++++++++++++ pkg/cnservice/python_runtime_test.go | 44 +++++++++++ pkg/cnservice/server.go | 25 ++----- 3 files changed, 156 insertions(+), 18 deletions(-) create mode 100644 pkg/cnservice/python_runtime.go create mode 100644 pkg/cnservice/python_runtime_test.go diff --git a/pkg/cnservice/python_runtime.go b/pkg/cnservice/python_runtime.go new file mode 100644 index 0000000000000..d168e583b3e05 --- /dev/null +++ b/pkg/cnservice/python_runtime.go @@ -0,0 +1,105 @@ +// Copyright 2026 Matrix Origin +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. + +package cnservice + +import ( + "context" + "fmt" + + "go.uber.org/zap" + + "github.com/matrixorigin/matrixone/pkg/common/mpool" + "github.com/matrixorigin/matrixone/pkg/container/vector" + "github.com/matrixorigin/matrixone/pkg/fileservice" + "github.com/matrixorigin/matrixone/pkg/udf" + "github.com/matrixorigin/matrixone/pkg/udf/python" +) + +// buildCNRuntime builds the language registry used by the CN. A Python +// initialization failure is kept as a language-scoped unavailable runtime so +// the feature gate remains fail-closed without taking ordinary SQL down with +// it. Config validation still happens before NewService and remains fatal for +// malformed CN configuration. +func buildCNRuntime(cfg python.ClientConfig, fileService fileservice.FileService, logger *zap.Logger) (udf.Runtime, error) { + if !cfg.Enabled { + return udf.NewRuntime() + } + + artifactStore, err := python.NewFileArtifactStore(fileService, python.DefaultMaxArtifactBytes) + if err != nil { + logPythonRuntimeDegraded(logger, err) + return udf.NewRuntime(&unavailablePythonRuntime{cause: err}) + } + runtime, err := python.NewGatewayWithArtifactStore(cfg, artifactStore) + if err != nil { + logPythonRuntimeDegraded(logger, err) + return udf.NewRuntime(&unavailablePythonRuntime{cause: err}) + } + return udf.NewRuntime(runtime) +} + +func logPythonRuntimeDegraded(logger *zap.Logger, err error) { + if logger != nil { + logger.Warn("python udf runtime is unavailable; ordinary SQL remains enabled", zap.Error(err)) + } +} + +// unavailablePythonRuntime preserves a stable language entry in the registry +// after initialization failed. It prevents a caller from mistaking an +// enabled-but-broken feature for a legacy fallback: every Python operation is +// rejected before user code, while StatusSnapshot exposes UNAVAILABLE. +type unavailablePythonRuntime struct { + cause error +} + +var _ udf.Runtime = (*unavailablePythonRuntime)(nil) +var _ udf.RuntimeReadiness = (*unavailablePythonRuntime)(nil) +var _ udf.RuntimeDefinitionValidator = (*unavailablePythonRuntime)(nil) +var _ udf.RuntimeStatusProvider = (*unavailablePythonRuntime)(nil) + +func (r *unavailablePythonRuntime) Language() string { return udf.LanguagePython } + +func (r *unavailablePythonRuntime) unavailableError() error { + if r == nil || r.cause == nil { + return fmt.Errorf("RESOURCE_UNAVAILABLE: Python UDF runtime initialization failed") + } + return fmt.Errorf("RESOURCE_UNAVAILABLE: Python UDF runtime initialization failed: %w", r.cause) +} + +func (r *unavailablePythonRuntime) Execute(context.Context, *udf.Invocation, vector.FunctionResultWrapper, *mpool.MPool) error { + return r.unavailableError() +} + +func (r *unavailablePythonRuntime) CheckLanguageReady(ctx context.Context, language string) error { + if language != udf.LanguagePython { + return fmt.Errorf("UNSUPPORTED_ROUTINE_VERSION: unsupported readiness language %q", language) + } + if ctx != nil { + select { + case <-ctx.Done(): + return ctx.Err() + default: + } + } + return r.unavailableError() +} + +func (r *unavailablePythonRuntime) ValidateDefinition(ctx context.Context, definition *udf.RoutineDefinition) error { + if definition == nil || definition.Language != udf.LanguagePython { + return fmt.Errorf("UNSUPPORTED_ROUTINE_VERSION: invalid Python definition") + } + return r.CheckLanguageReady(ctx, udf.LanguagePython) +} + +func (r *unavailablePythonRuntime) StatusSnapshot(context.Context, string) udf.RuntimeStatusSnapshot { + return udf.RuntimeStatusSnapshot{ + Language: udf.LanguagePython, + Enabled: true, + Ready: false, + ErrorClass: udf.RuntimeStatusUnavailable, + Reason: udf.RuntimeStatusReasonWorkerUnavailable, + } +} diff --git a/pkg/cnservice/python_runtime_test.go b/pkg/cnservice/python_runtime_test.go new file mode 100644 index 0000000000000..1da63a1bd3de6 --- /dev/null +++ b/pkg/cnservice/python_runtime_test.go @@ -0,0 +1,44 @@ +// Copyright 2026 Matrix Origin +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. + +package cnservice + +import ( + "context" + "testing" + + "github.com/matrixorigin/matrixone/pkg/udf" + "github.com/matrixorigin/matrixone/pkg/udf/python" + "github.com/stretchr/testify/require" +) + +func TestBuildCNRuntimeDegradesPythonWhenArtifactServiceIsUnavailable(t *testing.T) { + cfg := python.ClientConfig{ + Enabled: true, + AllowUnisolated: true, + ServerAddress: "127.0.0.1:50051", + } + runtime, err := buildCNRuntime(cfg, nil, nil) + require.NoError(t, err) + + readiness, ok := runtime.(udf.RuntimeReadiness) + require.True(t, ok) + require.ErrorContains(t, readiness.CheckLanguageReady(context.Background(), udf.LanguagePython), "RESOURCE_UNAVAILABLE") + + status, ok := runtime.(udf.RuntimeStatusProvider) + require.True(t, ok) + snapshot := status.StatusSnapshot(context.Background(), udf.LanguagePython) + require.True(t, snapshot.Enabled) + require.False(t, snapshot.Ready) + require.Equal(t, udf.RuntimeStatusUnavailable, snapshot.ErrorClass) +} + +func TestBuildCNRuntimeDisabledDoesNotCreatePythonRuntime(t *testing.T) { + runtime, err := buildCNRuntime(python.ClientConfig{}, nil, nil) + require.NoError(t, err) + readiness, ok := runtime.(udf.RuntimeReadiness) + require.True(t, ok) + require.ErrorContains(t, readiness.CheckLanguageReady(context.Background(), udf.LanguagePython), "not enabled") +} diff --git a/pkg/cnservice/server.go b/pkg/cnservice/server.go index 1304c6fea1bcc..ccb3533e75da7 100644 --- a/pkg/cnservice/server.go +++ b/pkg/cnservice/server.go @@ -71,7 +71,6 @@ import ( "github.com/matrixorigin/matrixone/pkg/txn/rpc" "github.com/matrixorigin/matrixone/pkg/txn/trace" "github.com/matrixorigin/matrixone/pkg/udf" - "github.com/matrixorigin/matrixone/pkg/udf/python" "github.com/matrixorigin/matrixone/pkg/util/address" "github.com/matrixorigin/matrixone/pkg/util/executor" v2 "github.com/matrixorigin/matrixone/pkg/util/metric/v2" @@ -239,24 +238,14 @@ func NewService( MaxSize: pu.SV.AutoIncrCacheSize, } - // init UdfService - var udfServices []udf.Runtime - // add python client to handle python udf - if srv.cfg.PythonUdfClient.Enabled { - artifactStore, storeErr := python.NewFileArtifactStore(srv.fileService, python.DefaultMaxArtifactBytes) - if storeErr != nil { - panic(storeErr) - } - var pc *python.Gateway - pc, err = python.NewGatewayWithArtifactStore(srv.cfg.PythonUdfClient, artifactStore) - if err != nil { - panic(err) - } - udfServices = append(udfServices, pc) - } - srv.udfService, err = udf.NewRuntime(udfServices...) + // init UdfService. Python is an opt-in feature and its transport or + // artifact dependencies must not turn an otherwise usable CN into a + // process-wide crash loop. buildCNRuntime keeps the language gate visible + // to CREATE/EXECUTE while ordinary SQL continues with the runtime in an + // explicit UNAVAILABLE state. + srv.udfService, err = buildCNRuntime(srv.cfg.PythonUdfClient, srv.fileService, srv.logger) if err != nil { - panic(err) + return nil, err } srv.CNMemoryThrottler = rscthrottler.NewMemThrottler( From bab65edc0d76e5f320dfc4a4b0b8627a4055b5af Mon Sep 17 00:00:00 2001 From: iamlinjunhong <1030420200@qq.com> Date: Fri, 18 Sep 2026 16:24:12 +0800 Subject: [PATCH 183/202] fix: clean up compose BVT services --- optools/compose_bvt/compose_bvt.sh | 47 +++++++++++++++++++++++------- optools/compose_bvt/entrypoint.sh | 6 +++- 2 files changed, 42 insertions(+), 11 deletions(-) diff --git a/optools/compose_bvt/compose_bvt.sh b/optools/compose_bvt/compose_bvt.sh index dce8d7ca4ccb8..b729250898e8d 100755 --- a/optools/compose_bvt/compose_bvt.sh +++ b/optools/compose_bvt/compose_bvt.sh @@ -1,29 +1,56 @@ #!/bin/bash -set -e +set -euo pipefail + +if (( $# < 2 )); then + echo "usage: $0 " >&2 + exit 2 +fi MO_WORKSPACE=$1 COMPOSE_LAUNCH=$2 +COMPOSE_FILE="${MO_WORKSPACE}/etc/launch-tae-compose/compose.yaml" function export_logs() { - cd ${MO_WORKSPACE} - curl http://localhost:12345/debug/pprof/goroutine\?debug=2 -o docker-compose-log/cn-0-dump-stacks - curl http://localhost:22345/debug/pprof/goroutine\?debug=2 -o docker-compose-log/cn-1-dump-stacks + cd -- "${MO_WORKSPACE}" || return 0 + curl --fail --silent --show-error "http://localhost:12345/debug/pprof/goroutine?debug=2" \ + -o docker-compose-log/cn-0-dump-stacks || true + curl --fail --silent --show-error "http://localhost:22345/debug/pprof/goroutine?debug=2" \ + -o docker-compose-log/cn-1-dump-stacks || true +} + +function cleanup_compose() { + # Preserve the tester's exit status while making service teardown + # best-effort. This trap owns every service started by this script, + # including both CN-local Python workers. + set +e + export_logs + docker compose -f "${COMPOSE_FILE}" --profile "${COMPOSE_LAUNCH}" down --remove-orphans } function compose_bvt() { - trap "export_logs" EXIT + trap cleanup_compose EXIT - cd ${MO_WORKSPACE} + cd -- "${MO_WORKSPACE}" - docker compose -f etc/launch-tae-compose/compose.yaml --profile "${COMPOSE_LAUNCH}" up -d --build + docker compose -f "${COMPOSE_FILE}" --profile "${COMPOSE_LAUNCH}" up -d --build docker build -t matrixorigin/compose-tester:local -f optools/compose_bvt/Dockerfile.tester . - docker run -it --name compose-tester --privileged --network launch-tae-compose_monet -v ${MO_WORKSPACE}/docker-compose-log:/test --rm matrixorigin/compose-tester:local - exit 0 + docker run -it --name compose-tester --privileged --network launch-tae-compose_monet \ + -v "${MO_WORKSPACE}/docker-compose-log:/test" --rm matrixorigin/compose-tester:local } #create the dir for export logs -rm -rf ${MO_WORKSPACE}/docker-compose-log && mkdir -p ${MO_WORKSPACE}/docker-compose-log +python3 - "${MO_WORKSPACE}/docker-compose-log" <<'PY' +import pathlib +import shutil +import sys + +target = pathlib.Path(sys.argv[1]).resolve() +if target == target.parent or target == pathlib.Path("/"): + raise SystemExit(f"refusing unsafe log directory: {target}") +shutil.rmtree(target, ignore_errors=True) +target.mkdir(parents=True, exist_ok=True) +PY compose_bvt diff --git a/optools/compose_bvt/entrypoint.sh b/optools/compose_bvt/entrypoint.sh index edff22b51a6f0..797faa5633cd1 100755 --- a/optools/compose_bvt/entrypoint.sh +++ b/optools/compose_bvt/entrypoint.sh @@ -6,7 +6,11 @@ SECONDS=0 # mv log to mount path function packLog() { - mv /mo-tester/report /test/ + if [[ -e /mo-tester/report ]]; then + mv /mo-tester/report /test/ + else + echo "mo-tester did not produce a report; preserving the original test status" >&2 + fi duration=$SECONDS echo "$(($duration / 60)) minutes and $(($duration % 60)) seconds elapsed." From 6d703f4eb4e0b92a60eef2c2d62d5ecfea611519 Mon Sep 17 00:00:00 2001 From: iamlinjunhong <1030420200@qq.com> Date: Fri, 18 Sep 2026 16:25:55 +0800 Subject: [PATCH 184/202] test: execute scalar Python UDF BVT case --- test/distributed/cases/udf_python/control_flow.result | 6 ++++++ test/distributed/cases/udf_python/control_flow.sql | 4 ++++ 2 files changed, 10 insertions(+) diff --git a/test/distributed/cases/udf_python/control_flow.result b/test/distributed/cases/udf_python/control_flow.result index 1c55957986b2c..10afe7303cdae 100644 --- a/test/distributed/cases/udf_python/control_flow.result +++ b/test/distributed/cases/udf_python/control_flow.result @@ -18,6 +18,12 @@ from control_values order by id; 1 ¦ 𝄀 2 ¦ <中😀> 𝄀 3 ¦ +select id, python_bvt_add_one(value) as plus_one +from control_values order by id; +➤ id[4,32,0] ¦ plus_one[4,32,0] 𝄀 +1 ¦ 2 𝄀 +2 ¦ 3 𝄀 +3 ¦ null select id, python_bvt_zero_arg() as constant from control_values order by id; ➤ id[4,32,0] ¦ constant[4,32,0] 𝄀 diff --git a/test/distributed/cases/udf_python/control_flow.sql b/test/distributed/cases/udf_python/control_flow.sql index c60cb08619d8a..5b133dbdc9d70 100644 --- a/test/distributed/cases/udf_python/control_flow.sql +++ b/test/distributed/cases/udf_python/control_flow.sql @@ -21,6 +21,10 @@ insert into control_values values select id, python_bvt_mark_null(label) as marked from control_values order by id; +-- A scalar handler must be invoked once per row and preserve a handler NULL. +select id, python_bvt_add_one(value) as plus_one +from control_values order by id; + -- A zero-argument scalar still produces one result per input row. select id, python_bvt_zero_arg() as constant from control_values order by id; From 284a1d971a1d2c763c6aed425d52f751735c409f Mon Sep 17 00:00:00 2001 From: iamlinjunhong <1030420200@qq.com> Date: Sun, 20 Sep 2026 12:29:12 +0800 Subject: [PATCH 185/202] fix(udf): validate overload namespaces and cancel capability waits --- docs/design/routine_namespace_validation.md | 48 + pkg/frontend/compiler_context.go | 13 +- pkg/frontend/routine_namespace.go | 170 ++ pkg/frontend/routine_namespace_test.go | 167 ++ pkg/frontend/routine_plan_validation.go | 82 +- pkg/frontend/routine_plan_validation_test.go | 24 +- pkg/pb/plan/plan.pb.go | 2208 +++++++++-------- pkg/sql/plan/function/udf.go | 17 +- pkg/sql/plan/routine_dependency.go | 4 +- pkg/udf/python/capability_lifecycle_test.go | 198 ++ pkg/udf/python/gateway.go | 228 +- pkg/udf/runtime_status_wire_test.go | 39 + pkg/udf/testdata/python_status_wire.json | 52 + proto/plan.proto | 4 + .../cases/udf/overload_namespace.result | 57 + .../cases/udf/overload_namespace.sql | 35 + .../udf_python/overload_namespace.result | 57 + .../cases/udf_python/overload_namespace.sql | 35 + 18 files changed, 2258 insertions(+), 1180 deletions(-) create mode 100644 docs/design/routine_namespace_validation.md create mode 100644 pkg/frontend/routine_namespace.go create mode 100644 pkg/frontend/routine_namespace_test.go create mode 100644 pkg/udf/python/capability_lifecycle_test.go create mode 100644 pkg/udf/runtime_status_wire_test.go create mode 100644 pkg/udf/testdata/python_status_wire.json create mode 100644 test/distributed/cases/udf/overload_namespace.result create mode 100644 test/distributed/cases/udf/overload_namespace.sql create mode 100644 test/distributed/cases/udf_python/overload_namespace.result create mode 100644 test/distributed/cases/udf_python/overload_namespace.sql diff --git a/docs/design/routine_namespace_validation.md b/docs/design/routine_namespace_validation.md new file mode 100644 index 0000000000000..51ccfb771db19 --- /dev/null +++ b/docs/design/routine_namespace_validation.md @@ -0,0 +1,48 @@ +# Routine overload namespace validation + +A cached or prepared routine call depends on two distinct things: + +* the exact selected FunctionRef and immutable revision contracts; +* the complete candidate set for its account/database/name at binding time. + +The revision-local `namespace_version` stored on an identity/revision cannot be +used as the second dependency: CREATE of a sibling does not update that tuple. +Updating all sibling heads would make them disagree with immutable revisions. + +`RoutineCall.namespace_fingerprint` and `RoutinePlanDependency.namespace_fingerprint` +now carry a separate state fingerprint. The resolver and reuse validator read +all same-name candidates, across SQL/Python and signatures, in their catalog +read transaction (and with the explicit historical snapshot when applicable). +The internal reads are marked derived after BEGIN and restored before finishTxn, +so READ COMMITTED cannot advance the snapshot between enumeration and digest +capture. Database identity uses the same read transaction and snapshot tenant. +The selected ID locates the namespace; account context and the independently +validated database ID fence its scope. Execution continues to use the exact +FunctionRef, never a name or a latest-revision lookup. + +The query deduplicates namespaces before joining candidates, and projects fixed-width +SHA-256 hashes of text/JSON metadata instead of transferring source bodies. +The canonical encoding hashes a versioned JSON tuple per candidate: identity, +active revision, revision-local namespace, base arguments/body/language/return/ +database/SQL mode/security, plus effective revision arguments/body/language/ +return/security. Sorted fixed-length candidate digests form the namespace +state digest. Thus newly introduced better/ambiguous matches invalidate even +when the selected identity is unchanged. DROP, replacement, clone and restore +follow visible catalog state. Restoring exactly the same candidates need not +invalidate: this is not an event counter. Selected revision integrity and +security checks still run independently. + +There is no new persistent catalog or backfill. Older plans without the digest +cannot be reused; the normal bind path regenerates the dependency. A missing +selected identity invalidates rather than reporting catalog corruption. Empty +candidate results are distinct from malformed multi-result responses. + +Validation reads are bounded to 65,536 candidate rows (an extra row detects +overflow) and 64 MiB of decoded metadata per read. Exceeding either marks the plan non-reusable and forces rebind, +never accepts a truncated digest and does not reject otherwise valid SQL. This increases reuse validation cost with the +number of overloads and their definition size. It does not add worker RPCs or +per-row lookups. Dependency count/serialized-byte bounds remain in force. + +This replaces the earlier proposal to advance a separate mutable namespace +generation for every overload-set change. It preserves that proposal's cache +correctness property without introducing a second publication/restore index. diff --git a/pkg/frontend/compiler_context.go b/pkg/frontend/compiler_context.go index c6b643983ed81..31b93006a99f1 100644 --- a/pkg/frontend/compiler_context.go +++ b/pkg/frontend/compiler_context.go @@ -831,6 +831,7 @@ func (tcc *TxnCompilerContext) ResolveUdf(name string, args []*plan.Expr) (udf * } } + defer pinRoutineCatalogReads(bh)() queryCtx, sql := udfCatalogLookup(ctx, tcc.GetSnapshot(), name, tcc.DefaultDatabase()) bh.ClearExecResultSet() err = bh.Exec(queryCtx, sql) @@ -1022,12 +1023,22 @@ func (tcc *TxnCompilerContext) ResolveUdf(name string, args []*plan.Expr) (udf * if accountErr != nil { return nil, accountErr } - databaseID, databaseErr := tcc.GetDatabaseId(matchedList[0].Udf.Db, tcc.GetSnapshot()) + accountID = routineCatalogAccountID(accountID, tcc.GetSnapshot()) + databaseID, databaseErr := routineCatalogDatabaseID(queryCtx, bh, matchedList[0].Udf.Db, accountID, tcc.GetSnapshot()) if databaseErr != nil { return nil, fmt.Errorf("python routine database identity: %w", databaseErr) } matchedList[0].Udf.AccountID = uint64(accountID) matchedList[0].Udf.DatabaseID = databaseID + namespaces, namespaceErr := readRoutineNamespaces(queryCtx, bh, []uint64{uint64(matchedList[0].Udf.FunctionID)}, tcc.GetSnapshot()) + if namespaceErr != nil && !errors.Is(namespaceErr, errRoutineNamespaceBudget) { + return nil, namespaceErr + } + matchedList[0].Udf.NamespaceFingerprint = namespaces[uint64(matchedList[0].Udf.FunctionID)] + if namespaceErr == nil && matchedList[0].Udf.NamespaceFingerprint == "" { + return nil, fmt.Errorf("UNSUPPORTED_ROUTINE_VERSION: missing routine namespace") + } + } return matchedList[0].Udf, err } diff --git a/pkg/frontend/routine_namespace.go b/pkg/frontend/routine_namespace.go new file mode 100644 index 0000000000000..89e494ba8b40f --- /dev/null +++ b/pkg/frontend/routine_namespace.go @@ -0,0 +1,170 @@ +// Copyright 2026 Matrix Origin +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. + +package frontend + +import ( + "context" + "crypto/sha256" + "encoding/hex" + "encoding/json" + "errors" + "fmt" + "sort" + "strconv" + "strings" + + planpb "github.com/matrixorigin/matrixone/pkg/pb/plan" +) + +var errRoutineNamespaceBudget = errors.New("routine namespace cache budget exceeded") + +const maxRoutineNamespaceRows = 65536 +const maxRoutineNamespaceBytes = 64 << 20 + +// readRoutineNamespaces reads the complete candidate set, not just matching +// signatures. The selected identity locates its database/name under the tenant +// context; all languages participate. Revision-local namespace_version remains +// immutable. The separate digest is state-based and naturally follows rollback, +// snapshot/PITR and clone publication without a second mutable catalog index. +func readRoutineNamespaces(ctx context.Context, bh BackgroundExec, ids []uint64, snapshot *planpb.Snapshot) (map[uint64]string, error) { + bh.ClearExecResultSet() + if err := bh.Exec(ctx, routineNamespacesSQL(ids, snapshot)); err != nil { + return nil, err + } + rows, err := getResultSet(ctx, bh) + if err != nil { + return nil, err + } + row, err := candidateCatalogResultSet(rows, "routine namespaces") + if err != nil { + return nil, err + } + if row == nil { + return map[uint64]string{}, nil + } + return decodeRoutineNamespaces(ctx, row) +} + +func routineNamespacesSQL(ids []uint64, snapshot *planpb.Snapshot) string { + values := make([]string, len(ids)) + for i, id := range ids { + values[i] = strconv.FormatUint(id, 10) + } + suffix := "" + if snapshot != nil && snapshot.TS != nil { + suffix = fmt.Sprintf(" {MO_TS = %d}", snapshot.TS.PhysicalTime) + } + // Read both base and revision metadata: legacy SQL resolves the base, typed + // SQL/Python resolves the immutable revision. Never drop an unmatching sibling. + + return fmt.Sprintf(`select selected.function_id, f.function_id, f.active_revision, f.namespace_version, + sha2(cast(f.args as varchar),256), sha2(f.body,256), sha2(f.language,256), sha2(f.rettype,256), sha2(f.db,256), sha2(f.sql_mode,256), sha2(f.security_type,256), + sha2(cast(coalesce(r.args,'[]') as varchar),256), sha2(coalesce(r.body,''),256), sha2(coalesce(r.language,''),256), sha2(coalesce(r.rettype,''),256), sha2(coalesce(r.security_type,''),256) + from (select db,name,min(function_id) as function_id from mo_catalog.mo_user_defined_function%s where function_id in (%s) group by db,name) selected + join mo_catalog.mo_user_defined_function%s f on f.db=selected.db and f.name=selected.name + left join mo_catalog.mo_function_revisions%s r on r.function_id=f.function_id and r.revision=f.active_revision and r.namespace_version=f.namespace_version + limit %d;`, suffix, strings.Join(values, ","), suffix, suffix, maxRoutineNamespaceRows+1) +} + +func decodeRoutineNamespaces(ctx context.Context, rows ExecResult) (map[uint64]string, error) { + if rows.GetRowCount() > maxRoutineNamespaceRows { + return nil, fmt.Errorf("%w: namespace rows exceed %d", errRoutineNamespaceBudget, maxRoutineNamespaceRows) + } + groups := make(map[uint64]map[uint64]string) + remaining := maxRoutineNamespaceBytes + for row := uint64(0); row < rows.GetRowCount(); row++ { + var numbers [4]uint64 + for col := range numbers { + value, err := rows.GetUint64(ctx, row, uint64(col)) + if err != nil { + return nil, err + } + numbers[col] = value + } + var fields [12]string + for col := range fields { + value, err := rows.GetString(ctx, row, uint64(col+4)) + if err != nil { + return nil, err + } + if len(value) > remaining { + return nil, fmt.Errorf("%w: namespace bytes exceed %d", errRoutineNamespaceBudget, maxRoutineNamespaceBytes) + } + remaining -= len(value) + fields[col] = value + } + // JSON arrays give unambiguous length/escaping, with fixed version/domain. + // Exclude selected id so different overloads in one namespace share a token. + encoded, err := json.Marshal(struct { + Version int + Identity [3]uint64 + Fields [12]string + }{1, [3]uint64{numbers[1], numbers[2], numbers[3]}, fields}) + if err != nil { + return nil, err + } + digest := sha256.Sum256(encoded) + if groups[numbers[0]] == nil { + groups[numbers[0]] = make(map[uint64]string) + } + if _, duplicate := groups[numbers[0]][numbers[1]]; duplicate { + return nil, fmt.Errorf("UNSUPPORTED_ROUTINE_VERSION: duplicate routine namespace member") + } + groups[numbers[0]][numbers[1]] = hex.EncodeToString(digest[:]) + } + result := make(map[uint64]string, len(groups)) + for _, members := range groups { + fingerprints := make([]string, 0, len(members)) + for _, fp := range members { + fingerprints = append(fingerprints, fp) + } + sort.Strings(fingerprints) + digest := sha256.Sum256([]byte("mo-routine-namespace-state/1\x00" + strings.Join(fingerprints, ""))) + for id := range members { + result[id] = hex.EncodeToString(digest[:]) + } + } + return result, nil +} + +// begin creates the catalog read transaction. Mark its internal SELECTs derived +// until finishTxn so READ COMMITTED cannot advance between enumeration, revision +// reads and the namespace fence. Restore before COMMIT/ROLLBACK and Close. +func pinRoutineCatalogReads(bh BackgroundExec) func() { + if back, ok := bh.(*backExec); ok { + previous := back.backSes.ReplaceDerivedStmt(true) + return func() { back.backSes.ReplaceDerivedStmt(previous) } + } + // Non-engine executors are deterministic unit-test readers. + return func() {} +} + +func routineCatalogDatabaseID(ctx context.Context, bh BackgroundExec, database string, accountID uint32, snapshot *planpb.Snapshot) (uint64, error) { + table := "mo_catalog.mo_database" + if snapshot != nil && snapshot.TS != nil { + table += fmt.Sprintf(" {MO_TS = %d}", snapshot.TS.PhysicalTime) + } + bh.ClearExecResultSet() + if err := bh.Exec(ctx, fmt.Sprintf("select dat_id from %s where datname = '%s' and account_id = %d;", table, strings.ReplaceAll(database, "'", "''"), accountID)); err != nil { + return 0, err + } + rows, err := getResultSet(ctx, bh) + if err != nil { + return 0, err + } + row, err := exactlyOneCatalogRow(ctx, rows, "routine database identity") + if err != nil { + return 0, err + } + return row.GetUint64(ctx, 0, 0) +} + +func routineCatalogAccountID(sessionAccount uint32, snapshot *planpb.Snapshot) uint32 { + if snapshot != nil && snapshot.Tenant != nil { + return snapshot.Tenant.TenantID + } + return sessionAccount +} diff --git a/pkg/frontend/routine_namespace_test.go b/pkg/frontend/routine_namespace_test.go new file mode 100644 index 0000000000000..e8531c811613a --- /dev/null +++ b/pkg/frontend/routine_namespace_test.go @@ -0,0 +1,167 @@ +// Copyright 2026 Matrix Origin +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +package frontend + +import ( + "context" + "fmt" + "strings" + "testing" + + "github.com/gogo/protobuf/proto" + "github.com/golang/mock/gomock" + "github.com/matrixorigin/matrixone/pkg/container/types" + "github.com/matrixorigin/matrixone/pkg/defines" + mock_frontend "github.com/matrixorigin/matrixone/pkg/frontend/test" + planpb "github.com/matrixorigin/matrixone/pkg/pb/plan" + "github.com/matrixorigin/matrixone/pkg/pb/txn" + "github.com/matrixorigin/matrixone/pkg/sql/plan/function" + "github.com/matrixorigin/matrixone/pkg/txn/client" + "github.com/stretchr/testify/require" +) + +func namespaceRows(data ...[]interface{}) *MysqlResultSet { + r := &MysqlResultSet{} + for i := 0; i < 16; i++ { + c := &MysqlColumn{} + c.SetName(fmt.Sprint(i)) + c.SetColumnType(defines.MYSQL_TYPE_VARCHAR) + if i < 4 { + c.SetColumnType(defines.MYSQL_TYPE_LONGLONG) + } + r.AddColumn(c) + } + for _, row := range data { + r.AddRow(row) + } + return r +} +func namespaceMember(id uint64, arg string) []interface{} { + return []interface{}{uint64(41), id, uint64(1), uint64(1), arg, "body", "python", "bigint", "db", "", "INVOKER", arg, "body", "python", "bigint", "INVOKER"} +} +func TestRoutineNamespaceInvalidatesBetterOverload(t *testing.T) { + input := []types.Type{types.T_int16.ToType()} + oldMatch, oldCost := function.PythonUdfArgTypeMatch(input, []types.Type{types.T_int64.ToType()}) + newMatch, newCost := function.PythonUdfArgTypeMatch(input, input) + require.True(t, oldMatch && newMatch) + require.Less(t, newCost, oldCost) + old := namespaceMember(41, "bigint") + sibling := namespaceMember(42, "smallint") + before, err := decodeRoutineNamespaces(context.Background(), namespaceRows(old)) + require.NoError(t, err) + after, err := decodeRoutineNamespaces(context.Background(), namespaceRows(old, sibling)) + require.NoError(t, err) + dependency := testRoutinePlanDependency() + dependency.NamespaceFingerprint = before[41] + state := testRoutinePlanState() + state.namespaceFingerprint = before[41] + require.False(t, routinePlanDependenciesChanged([]*planpb.RoutinePlanDependency{dependency}, map[uint64]routinePlanCatalogState{41: state})) + state.namespaceFingerprint = after[41] + require.True(t, routinePlanDependenciesChanged([]*planpb.RoutinePlanDependency{dependency}, map[uint64]routinePlanCatalogState{41: state}), "selected identity unchanged, but new better overload must invalidate") + // Rollback/restoring the identical candidate state preserves the dependency. + restored, err := decodeRoutineNamespaces(context.Background(), namespaceRows(old)) + require.NoError(t, err) + require.Equal(t, before, restored) + reordered, err := decodeRoutineNamespaces(context.Background(), namespaceRows(sibling, old)) + require.NoError(t, err) + require.Equal(t, after, reordered) + require.Equal(t, after[41], after[42], "all selected siblings reuse one namespace hash") + // Drop and replace of an unselected member change the name dependency. + sibling[2] = uint64(2) + replaced, err := decodeRoutineNamespaces(context.Background(), namespaceRows(old, sibling)) + require.NoError(t, err) + require.NotEqual(t, after, replaced) + // Effective revision metadata, rather than only initial head projection. + sibling[11] = "int" + effective, err := decodeRoutineNamespaces(context.Background(), namespaceRows(old, sibling)) + require.NoError(t, err) + require.NotEqual(t, replaced, effective) + _, err = decodeRoutineNamespaces(context.Background(), namespaceRows(old, old)) + require.ErrorContains(t, err, "duplicate") + data, err := proto.Marshal(dependency) + require.NoError(t, err) + var decoded planpb.RoutinePlanDependency + require.NoError(t, proto.Unmarshal(data, &decoded)) + require.Equal(t, dependency.NamespaceFingerprint, decoded.NamespaceFingerprint) + decoded.NamespaceFingerprint = "" + require.ErrorContains(t, validateRoutinePlanDependencyShape(&decoded, 9), "incomplete") +} +func TestRoutineNamespaceQueryReadsAllLanguagesAndSignatures(t *testing.T) { + query := routineNamespacesSQL([]uint64{41, 42}, nil) + require.Contains(t, query, "f.db=selected.db and f.name=selected.name") + require.Contains(t, query, "where function_id in (41,42) group by db,name") + require.NotContains(t, query, "f.language =") + require.NotContains(t, query, "f.arg_types =") + require.Contains(t, query, "limit 65537") +} +func TestRoutineNamespaceReadIsBounded(t *testing.T) { + oversized := namespaceMember(41, "bigint") + oversized[5] = strings.Repeat("x", maxRoutineNamespaceBytes+1) + _, err := decodeRoutineNamespaces(context.Background(), namespaceRows(oversized)) + require.ErrorContains(t, err, "namespace bytes") +} + +type routineSnapshotWorkspace struct { + client.Workspace + advances int +} + +func (w *routineSnapshotWorkspace) IncrStatementID(context.Context, bool) error { + w.advances++ + return fmt.Errorf("snapshot advanced") +} + +func TestRoutineCatalogReadsKeepRCSnapshotUntilFinish(t *testing.T) { + ctrl := gomock.NewController(t) + op := mock_frontend.NewMockTxnOperator(ctrl) + workspace := &routineSnapshotWorkspace{} + op.EXPECT().GetWorkspace().Return(workspace).AnyTimes() + op.EXPECT().Status().Return(txn.TxnStatus_Active).AnyTimes() + back := &backExec{backSes: &backSession{}} + restore := pinRoutineCatalogReads(back) + // This is the real statement boundary that would advance an RC snapshot. + // A concurrent DDL commit between these internal SELECTs cannot invoke it. + for i := 0; i < 3; i++ { + require.NoError(t, executeStmtWithIncrStmt(back.backSes, nil, nil, op)) + } + require.Zero(t, workspace.advances) + restore() + require.False(t, back.backSes.IsDerivedStmt()) + err := executeStmtWithIncrStmt(back.backSes, nil, &ExecCtx{reqCtx: context.Background()}, op) + require.ErrorContains(t, err, "snapshot advanced") + require.Equal(t, 1, workspace.advances) + // Nested shared readers restore the existing derived state rather than + // committing/advancing their caller's statement on return. + back.backSes.ReplaceDerivedStmt(true) + restore = pinRoutineCatalogReads(back) + restore() + require.True(t, back.backSes.IsDerivedStmt()) +} + +type namespaceValidationSession struct{ FeSession } + +func (*namespaceValidationSession) GetTxnCompileCtx() *TxnCompilerContext { return nil } +func (*namespaceValidationSession) GetAccountId() uint32 { return 9 } +func TestRoutinePlanWithoutNamespaceRebinds(t *testing.T) { + dependency := testRoutinePlanDependency() + dependency.NamespaceFingerprint = "" + p := &planpb.Plan{Plan: &planpb.Plan_Query{Query: &planpb.Query{RoutineDependencies: []*planpb.RoutinePlanDependency{dependency}}}} + changed, err := validateRoutinePlanDependencies(context.Background(), &namespaceValidationSession{}, p) + require.NoError(t, err) + require.True(t, changed) +} + +func TestRoutineCatalogDatabaseUsesSnapshotTenant(t *testing.T) { + snapshot := &planpb.Snapshot{Tenant: &planpb.SnapshotTenant{TenantID: 7}} + require.Equal(t, uint32(7), routineCatalogAccountID(0, snapshot)) + require.Equal(t, uint32(9), routineCatalogAccountID(9, nil)) + bh := &backgroundExecTestWithHistory{} + bh.init() + bh.sql2result["select dat_id from mo_catalog.mo_database where datname = 'source_db' and account_id = 7;"] = singleInt64Result("dat_id", 88) + ctx := defines.AttachAccountId(context.Background(), 7) + id, err := routineCatalogDatabaseID(ctx, bh, "source_db", routineCatalogAccountID(0, snapshot), snapshot) + require.NoError(t, err) + require.Equal(t, uint64(88), id) + require.Contains(t, bh.executedSqls[0], "account_id = 7") +} diff --git a/pkg/frontend/routine_plan_validation.go b/pkg/frontend/routine_plan_validation.go index 49979f0ca00ef..99ae6506cff28 100644 --- a/pkg/frontend/routine_plan_validation.go +++ b/pkg/frontend/routine_plan_validation.go @@ -7,6 +7,7 @@ package frontend import ( "context" + "errors" "fmt" "sort" "strconv" @@ -30,19 +31,20 @@ const ( ) type routinePlanCatalogState struct { - activeRevision uint64 - namespaceVersion uint64 - databaseID uint64 - revision uint64 - language string - volatility string - nullPolicy string - fingerprint string - artifactDigest string - environmentDigest string - securityType string - identityChecked bool - identityValid bool + namespaceFingerprint string + activeRevision uint64 + namespaceVersion uint64 + databaseID uint64 + revision uint64 + language string + volatility string + nullPolicy string + fingerprint string + artifactDigest string + environmentDigest string + securityType string + identityChecked bool + identityValid bool } func cachedRoutinePlanDependenciesCurrent(ses *Session, cached *cachedPlan) bool { @@ -165,10 +167,17 @@ func validateRoutinePlanDependencies(ctx context.Context, ses FeSession, p *plan if ses == nil { return false, fmt.Errorf("UNSUPPORTED_ROUTINE_VERSION: routine dependency validation has no session") } - accountID := ses.GetAccountId() + var snapshot *planpb.Snapshot + if tcc := ses.GetTxnCompileCtx(); tcc != nil { + snapshot = tcc.GetSnapshot() + } + accountID := routineCatalogAccountID(ses.GetAccountId(), snapshot) ids := make([]uint64, 0, len(dependencies)) seen := make(map[uint64]struct{}, len(dependencies)) for _, dependency := range dependencies { + if dependency != nil && dependency.NamespaceFingerprint == "" { + return true, nil + } if err := validateRoutinePlanDependencyShape(dependency, accountID); err != nil { return false, err } @@ -191,15 +200,31 @@ func validateRoutinePlanDependencies(ctx context.Context, ses FeSession, p *plan // implemented for both client and background sessions; rejecting the latter // here would make a valid typed routine fail only when a statement is // executed through a nested frontend pipeline. - bh := ses.GetBackgroundExec(ctx) + + useCallerTxn := resolvesUdfInCallerTxn(ctx) + var bh BackgroundExec + if useCallerTxn { + bh = ses.GetShareTxnBackgroundExec(ctx, false) + } else { + bh = ses.GetBackgroundExec(ctx) + } defer bh.Close() - if err = bh.Exec(ctx, "begin;"); err != nil { - return false, fmt.Errorf("UNSUPPORTED_ROUTINE_VERSION: routine dependency validation could not start: %w", err) + if !useCallerTxn { + if err = bh.Exec(ctx, "begin;"); err != nil { + return false, fmt.Errorf("UNSUPPORTED_ROUTINE_VERSION: routine dependency validation could not start: %w", err) + } + defer func() { err = finishTxn(ctx, bh, err) }() } - defer func() { err = finishTxn(ctx, bh, err) }() + defer pinRoutineCatalogReads(bh)() bh.ClearExecResultSet() query := routinePlanCatalogSQL(ids, accountID) + if snapshot != nil && snapshot.TS != nil { + for _, table := range []string{"mo_user_defined_function", "mo_database", "mo_function_revisions"} { + name := "mo_catalog." + table + query = strings.ReplaceAll(query, name+" ", fmt.Sprintf("%s {MO_TS = %d} ", name, snapshot.TS.PhysicalTime)) + } + } if err = bh.Exec(ctx, query); err != nil { return false, fmt.Errorf("UNSUPPORTED_ROUTINE_VERSION: routine dependency validation failed: %w", err) } @@ -207,10 +232,13 @@ func validateRoutinePlanDependencies(ctx context.Context, ses FeSession, p *plan if err != nil { return false, err } - catalogResult, err := exactlyOneCatalogResultSet(ctx, rows, "routine dependency catalog") + catalogResult, err := candidateCatalogResultSet(rows, "routine dependency catalog") if err != nil { return false, err } + if catalogResult == nil { + return true, nil + } states := make(map[uint64]routinePlanCatalogState, len(rows)) if catalogResult.GetRowCount() != 0 { for row := uint64(0); row < catalogResult.GetRowCount(); row++ { @@ -339,6 +367,17 @@ func validateRoutinePlanDependencies(ctx context.Context, ses FeSession, p *plan } } } + namespaces, namespaceErr := readRoutineNamespaces(ctx, bh, ids, snapshot) + if errors.Is(namespaceErr, errRoutineNamespaceBudget) { + return true, nil + } + if namespaceErr != nil { + return false, namespaceErr + } + for id, state := range states { + state.namespaceFingerprint = namespaces[id] + states[id] = state + } return routinePlanDependenciesChanged(dependencies, states), nil } @@ -450,7 +489,7 @@ func validateRoutinePlanDependencyShape(dependency *planpb.RoutinePlanDependency return fmt.Errorf("UNSUPPORTED_ROUTINE_VERSION: routine plan dependency has an invalid catalog identity") } if dependency.ContractVersion != udf.RoutinePlanContractVersion || - !udf.IsSHA256Digest(dependency.DefinitionFingerprint) { + !udf.IsSHA256Digest(dependency.DefinitionFingerprint) || !udf.IsSHA256Digest(dependency.NamespaceFingerprint) { return fmt.Errorf("UNSUPPORTED_ROUTINE_VERSION: routine plan dependency contract is incomplete") } if dependency.Volatility == "" || dependency.NullPolicy == "" || !dependency.MayError || dependency.Leakproof { @@ -497,7 +536,8 @@ func routinePlanDependenciesChanged( if expectedSecurityMode == "" { return true } - if state.activeRevision != ref.Revision || state.revision != ref.Revision || + if state.namespaceFingerprint != dependency.NamespaceFingerprint || !udf.IsSHA256Digest(dependency.NamespaceFingerprint) || + state.activeRevision != ref.Revision || state.revision != ref.Revision || state.databaseID != ref.DatabaseId || state.namespaceVersion != ref.NamespaceVersion || state.volatility != dependency.Volatility || diff --git a/pkg/frontend/routine_plan_validation_test.go b/pkg/frontend/routine_plan_validation_test.go index bcc9cdc1c6c22..22369c04d2597 100644 --- a/pkg/frontend/routine_plan_validation_test.go +++ b/pkg/frontend/routine_plan_validation_test.go @@ -39,6 +39,7 @@ func testRoutinePlanDependency() *planpb.RoutinePlanDependency { }, Language: udf.LanguagePython, ContractVersion: udf.PythonPlanContractVersion, + NamespaceFingerprint: strings.Repeat("d", 64), DefinitionFingerprint: strings.Repeat("a", 64), ArtifactDigest: strings.Repeat("b", 64), EnvironmentDigest: strings.Repeat("c", 64), @@ -52,17 +53,18 @@ func testRoutinePlanDependency() *planpb.RoutinePlanDependency { func testRoutinePlanState() routinePlanCatalogState { return routinePlanCatalogState{ - activeRevision: 7, - namespaceVersion: 12, - databaseID: 8, - revision: 7, - language: udf.LanguagePython, - volatility: "VOLATILE", - nullPolicy: udf.NullCallHandler, - fingerprint: strings.Repeat("a", 64), - artifactDigest: strings.Repeat("b", 64), - environmentDigest: strings.Repeat("c", 64), - securityType: "INVOKER", + namespaceFingerprint: strings.Repeat("d", 64), + activeRevision: 7, + namespaceVersion: 12, + databaseID: 8, + revision: 7, + language: udf.LanguagePython, + volatility: "VOLATILE", + nullPolicy: udf.NullCallHandler, + fingerprint: strings.Repeat("a", 64), + artifactDigest: strings.Repeat("b", 64), + environmentDigest: strings.Repeat("c", 64), + securityType: "INVOKER", } } diff --git a/pkg/pb/plan/plan.pb.go b/pkg/pb/plan/plan.pb.go index bced50c135912..5d7d28e045b01 100644 --- a/pkg/pb/plan/plan.pb.go +++ b/pkg/pb/plan/plan.pb.go @@ -16655,10 +16655,13 @@ type RoutineCall struct { // These fields are the common semantic contract. They must survive SQL // lowering and expression/plan cloning; the optimizer may only move a // routine call when the attached contract permits it. - CallsiteId string `protobuf:"bytes,11,opt,name=callsite_id,json=callsiteId,proto3" json:"callsite_id,omitempty"` - MayError bool `protobuf:"varint,12,opt,name=may_error,json=mayError,proto3" json:"may_error,omitempty"` - SecurityMode string `protobuf:"bytes,13,opt,name=security_mode,json=securityMode,proto3" json:"security_mode,omitempty"` - Leakproof bool `protobuf:"varint,14,opt,name=leakproof,proto3" json:"leakproof,omitempty"` + CallsiteId string `protobuf:"bytes,11,opt,name=callsite_id,json=callsiteId,proto3" json:"callsite_id,omitempty"` + MayError bool `protobuf:"varint,12,opt,name=may_error,json=mayError,proto3" json:"may_error,omitempty"` + SecurityMode string `protobuf:"bytes,13,opt,name=security_mode,json=securityMode,proto3" json:"security_mode,omitempty"` + Leakproof bool `protobuf:"varint,14,opt,name=leakproof,proto3" json:"leakproof,omitempty"` + // State fingerprint of the complete overload namespace at binding snapshot. + // Separate from the selected immutable FunctionRef; missing means no reuse. + NamespaceFingerprint string `protobuf:"bytes,15,opt,name=namespace_fingerprint,json=namespaceFingerprint,proto3" json:"namespace_fingerprint,omitempty"` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` @@ -16819,6 +16822,13 @@ func (m *RoutineCall) GetLeakproof() bool { return false } +func (m *RoutineCall) GetNamespaceFingerprint() string { + if m != nil { + return m.NamespaceFingerprint + } + return "" +} + // XXX_OneofWrappers is for the internal use of the proto package. func (*RoutineCall) XXX_OneofWrappers() []interface{} { return []interface{}{ @@ -16843,6 +16853,7 @@ type RoutinePlanDependency struct { MayError bool `protobuf:"varint,9,opt,name=may_error,json=mayError,proto3" json:"may_error,omitempty"` SecurityMode string `protobuf:"bytes,10,opt,name=security_mode,json=securityMode,proto3" json:"security_mode,omitempty"` Leakproof bool `protobuf:"varint,11,opt,name=leakproof,proto3" json:"leakproof,omitempty"` + NamespaceFingerprint string `protobuf:"bytes,12,opt,name=namespace_fingerprint,json=namespaceFingerprint,proto3" json:"namespace_fingerprint,omitempty"` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` @@ -16958,6 +16969,13 @@ func (m *RoutinePlanDependency) GetLeakproof() bool { return false } +func (m *RoutinePlanDependency) GetNamespaceFingerprint() string { + if m != nil { + return m.NamespaceFingerprint + } + return "" +} + func init() { proto.RegisterEnum("plan.StringLiteralForm", StringLiteralForm_name, StringLiteralForm_value) proto.RegisterEnum("plan.AggregateConfigType", AggregateConfigType_name, AggregateConfigType_value) @@ -17170,41 +17188,41 @@ func init() { func init() { proto.RegisterFile("plan.proto", fileDescriptor_2d655ab2f7683c23) } var fileDescriptor_2d655ab2f7683c23 = []byte{ - // 18144 bytes of a gzipped FileDescriptorProto + // 18165 bytes of a gzipped FileDescriptorProto 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xcc, 0xbd, 0x5d, 0x8c, 0x1b, 0x57, - 0x97, 0x20, 0x26, 0x36, 0xc9, 0x6e, 0xf2, 0x90, 0xcd, 0xae, 0xbe, 0x6a, 0x49, 0x2d, 0x59, 0x96, + 0x97, 0x20, 0x26, 0x36, 0xd9, 0x4d, 0xf2, 0x90, 0xcd, 0xae, 0xbe, 0x6a, 0x49, 0x2d, 0x59, 0x96, 0xe4, 0xb2, 0x6c, 0xcb, 0xb2, 0x2d, 0xdb, 0x92, 0x7f, 0x64, 0xcf, 0x37, 0xf3, 0x7d, 0x6c, 0x36, 0x5b, 0x4d, 0x8b, 0x4d, 0xf6, 0x57, 0x64, 0x4b, 0xf6, 0x37, 0x3b, 0x28, 0x54, 0xb3, 0xaa, 0xbb, 0xcb, 0x5d, 0xac, 0xa2, 0xab, 0x8a, 0xea, 0x6e, 0x03, 0x41, 0x3e, 0xe4, 0x21, 0x93, 0x09, 0xf2, - 0xb0, 0xc0, 0x20, 0x9b, 0x87, 0x00, 0xc1, 0xec, 0x02, 0x8b, 0x00, 0x9b, 0x04, 0x41, 0x82, 0x45, - 0x26, 0x3f, 0xd8, 0x24, 0x9b, 0x5d, 0x20, 0x0b, 0x24, 0x08, 0x06, 0xd9, 0x24, 0x93, 0x64, 0x81, - 0xdd, 0x64, 0xe6, 0x3d, 0x09, 0x90, 0xbc, 0x24, 0x2f, 0x09, 0xce, 0x39, 0xf7, 0xd6, 0x0f, 0x49, - 0x59, 0xf6, 0x37, 0x33, 0x40, 0x5e, 0xba, 0x79, 0xcf, 0x39, 0xf7, 0xd6, 0xfd, 0x3d, 0xf7, 0x9c, - 0x73, 0xcf, 0x3d, 0x17, 0x60, 0xe2, 0x59, 0xfe, 0x83, 0x49, 0x18, 0xc4, 0x81, 0x28, 0xe1, 0xef, - 0x1b, 0x1f, 0x1c, 0xbb, 0xf1, 0xc9, 0xf4, 0xf0, 0xc1, 0x28, 0x18, 0x7f, 0x78, 0x1c, 0x1c, 0x07, - 0x1f, 0x12, 0xf2, 0x70, 0x7a, 0x44, 0x29, 0x4a, 0xd0, 0x2f, 0xce, 0x74, 0x03, 0xbc, 0x60, 0x74, - 0x2a, 0x7f, 0xaf, 0xc5, 0xee, 0xd8, 0x89, 0x62, 0x6b, 0x3c, 0x61, 0x80, 0xfe, 0xbf, 0x15, 0xa0, - 0x34, 0xbc, 0x98, 0x38, 0xa2, 0x01, 0x4b, 0xae, 0xbd, 0x59, 0xb8, 0x53, 0xb8, 0x57, 0x36, 0x96, - 0x5c, 0x5b, 0xdc, 0x81, 0x9a, 0x1f, 0xc4, 0xbd, 0xa9, 0xe7, 0x59, 0x87, 0x9e, 0xb3, 0xb9, 0x74, - 0xa7, 0x70, 0xaf, 0x62, 0x64, 0x41, 0xe2, 0x35, 0xa8, 0x5a, 0xd3, 0x38, 0x30, 0x5d, 0x7f, 0x14, - 0x6e, 0x16, 0x09, 0x5f, 0x41, 0x40, 0xc7, 0x1f, 0x85, 0x62, 0x03, 0xca, 0x67, 0xae, 0x1d, 0x9f, - 0x6c, 0x96, 0xa8, 0x44, 0x4e, 0x20, 0x34, 0x1a, 0x59, 0x9e, 0xb3, 0x59, 0x66, 0x28, 0x25, 0x10, - 0x1a, 0xd3, 0x47, 0x96, 0xef, 0x14, 0xee, 0x55, 0x0d, 0x4e, 0x88, 0x5b, 0x00, 0x8e, 0x3f, 0x1d, - 0xbf, 0xb0, 0xbc, 0xa9, 0x13, 0x6d, 0xae, 0x10, 0x2a, 0x03, 0x11, 0x9b, 0xb0, 0x32, 0x3a, 0xb1, - 0xc2, 0xc8, 0x89, 0x37, 0x2b, 0x77, 0x0a, 0xf7, 0x56, 0x0d, 0x95, 0xc4, 0x8a, 0x4d, 0x2c, 0xdb, - 0x8c, 0x26, 0xd6, 0xc8, 0xd9, 0xac, 0x72, 0xc5, 0x26, 0x96, 0x3d, 0xc0, 0xb4, 0xfe, 0x73, 0xa8, - 0x8e, 0xa3, 0xe3, 0x5d, 0xc7, 0xb2, 0x9d, 0x50, 0x5c, 0x83, 0x95, 0x71, 0x74, 0x6c, 0xc6, 0xd6, - 0xb1, 0x6c, 0xf9, 0xf2, 0x38, 0x3a, 0x1e, 0x5a, 0xc7, 0xe2, 0x3a, 0x54, 0x08, 0x71, 0x31, 0xe1, - 0xa6, 0x97, 0x0d, 0x24, 0xc4, 0x8e, 0xd2, 0xff, 0x64, 0x05, 0x56, 0xba, 0x6e, 0xec, 0x84, 0x96, - 0x27, 0xae, 0xc2, 0xb2, 0x1b, 0xf9, 0x53, 0xcf, 0xa3, 0xec, 0x15, 0x43, 0xa6, 0xc4, 0x55, 0x28, - 0xbb, 0x8f, 0x5f, 0x58, 0x1e, 0xe7, 0xdd, 0xbd, 0x64, 0x70, 0x52, 0x6c, 0xc2, 0xb2, 0xfb, 0xf1, - 0x67, 0x88, 0x28, 0x4a, 0x84, 0x4c, 0x13, 0xe6, 0xd1, 0x43, 0xc4, 0x94, 0x12, 0x0c, 0xa5, 0x09, - 0xf3, 0xd9, 0x27, 0x88, 0xc1, 0x4e, 0x2b, 0x12, 0x86, 0xd2, 0xf8, 0x95, 0x29, 0x7d, 0x05, 0xfb, - 0x6d, 0x15, 0xbf, 0x32, 0x55, 0x5f, 0x99, 0xf2, 0x57, 0x56, 0x24, 0x42, 0xa6, 0x09, 0xc3, 0x5f, - 0xa9, 0x24, 0x98, 0xe4, 0x2b, 0x53, 0xfe, 0x0a, 0x76, 0x58, 0x89, 0x30, 0xfc, 0x95, 0x0d, 0x28, - 0xd9, 0x08, 0x87, 0x3b, 0x85, 0x7b, 0x85, 0xdd, 0x4b, 0x06, 0xa5, 0x10, 0x1a, 0x21, 0xb4, 0x86, - 0xe3, 0x82, 0xd0, 0x48, 0x42, 0x0f, 0x11, 0x5a, 0xc7, 0xde, 0x40, 0xe8, 0xa1, 0x84, 0x1e, 0x21, - 0x74, 0xf5, 0x4e, 0xe1, 0xde, 0x12, 0x42, 0x31, 0x25, 0x6e, 0xc0, 0x8a, 0x6d, 0xc5, 0x0e, 0x22, + 0xb0, 0xc0, 0x20, 0x9b, 0x87, 0x00, 0xc1, 0xec, 0x02, 0x8b, 0x00, 0x9b, 0xe4, 0x21, 0xc1, 0x22, + 0x93, 0x1f, 0x6c, 0x92, 0xcd, 0x06, 0xc8, 0x02, 0x09, 0x82, 0x41, 0x36, 0xc9, 0x24, 0x59, 0x60, + 0x37, 0x99, 0xc9, 0x73, 0x12, 0x20, 0x79, 0xd9, 0xbc, 0x24, 0x38, 0xe7, 0xdc, 0x5b, 0x3f, 0x24, + 0x65, 0xd9, 0xdf, 0xcc, 0x00, 0x79, 0xe9, 0xe6, 0x3d, 0xe7, 0xdc, 0x5b, 0xf7, 0xf7, 0xdc, 0x73, + 0xce, 0x3d, 0xf7, 0x5c, 0x80, 0x89, 0x67, 0xf9, 0x0f, 0x26, 0x61, 0x10, 0x07, 0xa2, 0x84, 0xbf, + 0x6f, 0x7c, 0x70, 0xec, 0xc6, 0x27, 0xd3, 0xc3, 0x07, 0xa3, 0x60, 0xfc, 0xe1, 0x71, 0x70, 0x1c, + 0x7c, 0x48, 0xc8, 0xc3, 0xe9, 0x11, 0xa5, 0x28, 0x41, 0xbf, 0x38, 0xd3, 0x0d, 0xf0, 0x82, 0xd1, + 0xa9, 0xfc, 0xbd, 0x16, 0xbb, 0x63, 0x27, 0x8a, 0xad, 0xf1, 0x84, 0x01, 0xfa, 0xff, 0x5e, 0x80, + 0xd2, 0xf0, 0x62, 0xe2, 0x88, 0x06, 0x2c, 0xb9, 0xf6, 0x66, 0xe1, 0x4e, 0xe1, 0xde, 0xb2, 0xb1, + 0xe4, 0xda, 0xe2, 0x0e, 0xd4, 0xfc, 0x20, 0xee, 0x4d, 0x3d, 0xcf, 0x3a, 0xf4, 0x9c, 0xcd, 0xa5, + 0x3b, 0x85, 0x7b, 0x15, 0x23, 0x0b, 0x12, 0xaf, 0x41, 0xd5, 0x9a, 0xc6, 0x81, 0xe9, 0xfa, 0xa3, + 0x70, 0xb3, 0x48, 0xf8, 0x0a, 0x02, 0x3a, 0xfe, 0x28, 0x14, 0x1b, 0xb0, 0x7c, 0xe6, 0xda, 0xf1, + 0xc9, 0x66, 0x89, 0x4a, 0xe4, 0x04, 0x42, 0xa3, 0x91, 0xe5, 0x39, 0x9b, 0xcb, 0x0c, 0xa5, 0x04, + 0x42, 0x63, 0xfa, 0xc8, 0xca, 0x9d, 0xc2, 0xbd, 0xaa, 0xc1, 0x09, 0x71, 0x0b, 0xc0, 0xf1, 0xa7, + 0xe3, 0x17, 0x96, 0x37, 0x75, 0xa2, 0xcd, 0x32, 0xa1, 0x32, 0x10, 0xb1, 0x09, 0xe5, 0xd1, 0x89, + 0x15, 0x46, 0x4e, 0xbc, 0x59, 0xb9, 0x53, 0xb8, 0xb7, 0x6a, 0xa8, 0x24, 0x56, 0x6c, 0x62, 0xd9, + 0x66, 0x34, 0xb1, 0x46, 0xce, 0x66, 0x95, 0x2b, 0x36, 0xb1, 0xec, 0x01, 0xa6, 0xf5, 0x9f, 0x43, + 0x75, 0x1c, 0x1d, 0xef, 0x3a, 0x96, 0xed, 0x84, 0xe2, 0x1a, 0x94, 0xc7, 0xd1, 0xb1, 0x19, 0x5b, + 0xc7, 0xb2, 0xe5, 0x2b, 0xe3, 0xe8, 0x78, 0x68, 0x1d, 0x8b, 0xeb, 0x50, 0x21, 0xc4, 0xc5, 0x84, + 0x9b, 0xbe, 0x6c, 0x20, 0x21, 0x76, 0x94, 0xfe, 0x27, 0x65, 0x28, 0x77, 0xdd, 0xd8, 0x09, 0x2d, + 0x4f, 0x5c, 0x85, 0x15, 0x37, 0xf2, 0xa7, 0x9e, 0x47, 0xd9, 0x2b, 0x86, 0x4c, 0x89, 0xab, 0xb0, + 0xec, 0x3e, 0x7e, 0x61, 0x79, 0x9c, 0x77, 0xf7, 0x92, 0xc1, 0x49, 0xb1, 0x09, 0x2b, 0xee, 0xc7, + 0x9f, 0x21, 0xa2, 0x28, 0x11, 0x32, 0x4d, 0x98, 0x47, 0x0f, 0x11, 0x53, 0x4a, 0x30, 0x94, 0x26, + 0xcc, 0x67, 0x9f, 0x20, 0x06, 0x3b, 0xad, 0x48, 0x18, 0x4a, 0xe3, 0x57, 0xa6, 0xf4, 0x15, 0xec, + 0xb7, 0x55, 0xfc, 0xca, 0x54, 0x7d, 0x65, 0xca, 0x5f, 0x29, 0x4b, 0x84, 0x4c, 0x13, 0x86, 0xbf, + 0x52, 0x49, 0x30, 0xc9, 0x57, 0xa6, 0xfc, 0x15, 0xec, 0xb0, 0x12, 0x61, 0xf8, 0x2b, 0x1b, 0x50, + 0xb2, 0x11, 0x0e, 0x77, 0x0a, 0xf7, 0x0a, 0xbb, 0x97, 0x0c, 0x4a, 0x21, 0x34, 0x42, 0x68, 0x0d, + 0xc7, 0x05, 0xa1, 0x91, 0x84, 0x1e, 0x22, 0xb4, 0x8e, 0xbd, 0x81, 0xd0, 0x43, 0x09, 0x3d, 0x42, + 0xe8, 0xea, 0x9d, 0xc2, 0xbd, 0x25, 0x84, 0x62, 0x4a, 0xdc, 0x80, 0xb2, 0x6d, 0xc5, 0x0e, 0x22, 0x1a, 0xb2, 0xc9, 0x0a, 0x80, 0x38, 0x9c, 0xa8, 0x88, 0x5b, 0x93, 0x8d, 0x56, 0x00, 0xa1, 0x43, 0x0d, 0xc9, 0x14, 0x5e, 0x93, 0xf8, 0x2c, 0x50, 0x7c, 0x0a, 0x75, 0xdb, 0x19, 0xb9, 0x63, 0xcb, 0xe3, 0x36, 0xad, 0xdf, 0x29, 0xdc, 0xab, 0x3d, 0x5c, 0x7b, 0x40, 0x4b, 0x29, 0xc1, 0xec, 0x5e, @@ -17222,51 +17240,51 @@ var fileDescriptor_2d655ab2f7683c23 = []byte{ 0xb9, 0x8c, 0x76, 0x82, 0x70, 0x6c, 0xd4, 0xbc, 0x34, 0x81, 0x1f, 0x88, 0x88, 0xc2, 0x8c, 0x82, 0x69, 0x38, 0x72, 0x36, 0x5f, 0xa7, 0x15, 0x5e, 0x67, 0xe0, 0x80, 0x60, 0xe2, 0xe7, 0x70, 0x53, 0x0e, 0x82, 0xa9, 0x3e, 0x14, 0x3a, 0xdf, 0x4d, 0xdd, 0xd0, 0x89, 0xcc, 0x17, 0x8f, 0x1f, 0x6e, - 0xde, 0xa2, 0x4a, 0x5d, 0x97, 0x34, 0xf2, 0x5b, 0x86, 0xa4, 0x78, 0xf6, 0xf8, 0xe1, 0xd6, 0x0a, - 0x94, 0x89, 0x97, 0xe8, 0x37, 0xa1, 0xb2, 0x6f, 0x85, 0xd6, 0xd8, 0x70, 0x8e, 0x84, 0x06, 0xc5, - 0x49, 0x10, 0x49, 0x76, 0x80, 0x3f, 0xf5, 0x2e, 0x2c, 0x3f, 0xb3, 0x42, 0xc4, 0x09, 0x28, 0xf9, + 0xde, 0xa2, 0x4a, 0x5d, 0x97, 0x34, 0xf2, 0x5b, 0x86, 0xa4, 0x78, 0xf6, 0xf8, 0xe1, 0x56, 0x19, + 0x96, 0x89, 0x97, 0xe8, 0x37, 0xa1, 0xb2, 0x6f, 0x85, 0xd6, 0xd8, 0x70, 0x8e, 0x84, 0x06, 0xc5, + 0x49, 0x10, 0x49, 0x76, 0x80, 0x3f, 0xf5, 0x2e, 0xac, 0x3c, 0xb3, 0x42, 0xc4, 0x09, 0x28, 0xf9, 0xd6, 0xd8, 0x21, 0x64, 0xd5, 0xa0, 0xdf, 0xc8, 0x02, 0xa2, 0x8b, 0x28, 0x76, 0xc6, 0x92, 0x45, 0xca, 0x14, 0xc2, 0x8f, 0xbd, 0xe0, 0x50, 0x2e, 0xf5, 0x8a, 0x21, 0x53, 0xfa, 0xbf, 0x50, 0x80, - 0xe5, 0x56, 0xe0, 0x61, 0x71, 0xd7, 0x60, 0x25, 0x74, 0x3c, 0x33, 0xfd, 0xdc, 0x72, 0xe8, 0x78, - 0xfb, 0x41, 0x84, 0x88, 0x51, 0xc0, 0x08, 0x66, 0x3e, 0xcb, 0xa3, 0x80, 0x10, 0xaa, 0x02, 0xc5, - 0x4c, 0x05, 0xae, 0x43, 0x25, 0x3e, 0xf4, 0x4c, 0x82, 0x97, 0x08, 0xbe, 0x12, 0x1f, 0x7a, 0x3d, - 0x44, 0x5d, 0x83, 0x15, 0xfb, 0x90, 0x31, 0x65, 0xc2, 0x2c, 0xdb, 0x87, 0x88, 0xd0, 0xbf, 0x80, - 0xaa, 0x61, 0x9d, 0xc9, 0x6a, 0x5c, 0x81, 0x65, 0x2c, 0x40, 0x72, 0xff, 0x92, 0x51, 0x8e, 0x0f, - 0xbd, 0x8e, 0x8d, 0x60, 0xac, 0x84, 0x6b, 0x53, 0x1d, 0x4a, 0x46, 0x79, 0x14, 0x78, 0x1d, 0x5b, - 0x1f, 0x02, 0xb4, 0x82, 0x30, 0xfc, 0x8d, 0x9b, 0xb0, 0x01, 0x65, 0xdb, 0x99, 0xc4, 0x27, 0xcc, - 0x01, 0x0d, 0x4e, 0xe8, 0xf7, 0xa1, 0x82, 0xd3, 0xab, 0xeb, 0x46, 0xb1, 0xb8, 0x05, 0x25, 0xcf, - 0x8d, 0xe2, 0xcd, 0xc2, 0x9d, 0xe2, 0xcc, 0xe4, 0x23, 0xb8, 0x7e, 0x07, 0x2a, 0x7b, 0xd6, 0xf9, - 0x33, 0x1c, 0x39, 0x2c, 0x8d, 0x86, 0x50, 0x0e, 0x89, 0x1c, 0xcf, 0x3a, 0xc0, 0xd0, 0x0a, 0x8f, - 0x9d, 0x98, 0x18, 0xf6, 0xff, 0x59, 0x80, 0xda, 0x60, 0x7a, 0xf8, 0xdd, 0xd4, 0x09, 0x2f, 0xb0, - 0xce, 0xf7, 0xa0, 0x18, 0x5f, 0x4c, 0x28, 0x47, 0xe3, 0xe1, 0x55, 0x39, 0x1f, 0x53, 0xfc, 0x03, - 0xcc, 0x64, 0x20, 0x09, 0x36, 0xc2, 0x0f, 0x6c, 0x47, 0xf5, 0x41, 0xd9, 0x58, 0xc6, 0x64, 0xc7, - 0xc6, 0xcd, 0x32, 0x98, 0xc8, 0x51, 0x58, 0x0a, 0x26, 0xe2, 0x0e, 0x94, 0x47, 0x27, 0xae, 0x67, - 0xd3, 0x00, 0xe4, 0xeb, 0xcc, 0x08, 0x1c, 0xa5, 0x30, 0x38, 0x33, 0x23, 0xf7, 0x7b, 0xb5, 0xf9, - 0xad, 0x84, 0xc1, 0xd9, 0xc0, 0xfd, 0xde, 0xd1, 0x87, 0x72, 0x07, 0x06, 0x58, 0x1e, 0xb4, 0x9a, - 0xdd, 0xa6, 0xa1, 0x5d, 0xc2, 0xdf, 0xed, 0xaf, 0x3b, 0x83, 0xe1, 0x40, 0x2b, 0x88, 0x06, 0x40, - 0xaf, 0x3f, 0x34, 0x65, 0x7a, 0x49, 0x2c, 0xc3, 0x52, 0xa7, 0xa7, 0x15, 0x91, 0x06, 0xe1, 0x9d, - 0x9e, 0x56, 0x12, 0x2b, 0x50, 0x6c, 0xf6, 0xbe, 0xd1, 0xca, 0xf4, 0xa3, 0xdb, 0xd5, 0x96, 0xf5, - 0xff, 0x67, 0x09, 0xaa, 0xfd, 0xc3, 0x6f, 0x9d, 0x51, 0x8c, 0x6d, 0xc6, 0x59, 0xea, 0x84, 0x2f, - 0x9c, 0x90, 0x9a, 0x5d, 0x34, 0x64, 0x0a, 0x1b, 0x62, 0x1f, 0x52, 0xe3, 0x8a, 0xc6, 0x92, 0x7d, - 0x48, 0x74, 0xa3, 0x13, 0x67, 0x6c, 0x51, 0xe3, 0x90, 0x8e, 0x52, 0xb8, 0x2a, 0x82, 0xc3, 0x6f, - 0xa9, 0x79, 0x45, 0x03, 0x7f, 0x8a, 0xdb, 0x50, 0xe3, 0x32, 0xb2, 0xf3, 0x0b, 0x18, 0x34, 0x3b, - 0xf9, 0x96, 0xb3, 0x93, 0x8f, 0x72, 0x52, 0xa9, 0x8c, 0x94, 0x3b, 0x3b, 0x83, 0x7a, 0x72, 0x46, - 0x07, 0x87, 0xdf, 0x32, 0xb6, 0xc2, 0x33, 0x3a, 0x38, 0xfc, 0x96, 0x50, 0xef, 0xc1, 0x7a, 0x34, - 0x3d, 0x8c, 0x46, 0xa1, 0x3b, 0x89, 0xdd, 0xc0, 0x67, 0x9a, 0x2a, 0xd1, 0x68, 0x59, 0x04, 0x11, - 0xdf, 0x83, 0xca, 0x64, 0x7a, 0x68, 0xba, 0xfe, 0x51, 0x40, 0xbb, 0x57, 0xed, 0xe1, 0x2a, 0x0f, - 0xcc, 0xfe, 0xf4, 0xb0, 0xe3, 0x1f, 0x05, 0xc6, 0xca, 0x84, 0x7f, 0x08, 0x1d, 0x56, 0xfd, 0x20, - 0x36, 0x51, 0x50, 0x32, 0xc7, 0x4e, 0x6c, 0xd1, 0xb6, 0xc6, 0xe2, 0x4e, 0x37, 0x18, 0x9d, 0xee, - 0x39, 0xb1, 0x25, 0xee, 0x43, 0x25, 0xf2, 0xad, 0x49, 0x74, 0x12, 0xc4, 0xb4, 0xbf, 0xd5, 0x1e, - 0x36, 0xe4, 0xdc, 0x91, 0x50, 0x23, 0xc1, 0xeb, 0x6f, 0xc3, 0x8a, 0xfc, 0x06, 0x0a, 0x23, 0xb1, + 0x95, 0x56, 0xe0, 0x61, 0x71, 0xd7, 0xa0, 0x1c, 0x3a, 0x9e, 0x99, 0x7e, 0x6e, 0x25, 0x74, 0xbc, + 0xfd, 0x20, 0x42, 0xc4, 0x28, 0x60, 0x04, 0x33, 0x9f, 0x95, 0x51, 0x40, 0x08, 0x55, 0x81, 0x62, + 0xa6, 0x02, 0xd7, 0xa1, 0x12, 0x1f, 0x7a, 0x26, 0xc1, 0x4b, 0x04, 0x2f, 0xc7, 0x87, 0x5e, 0x0f, + 0x51, 0xd7, 0xa0, 0x6c, 0x1f, 0x32, 0x66, 0x99, 0x30, 0x2b, 0xf6, 0x21, 0x22, 0xf4, 0x2f, 0xa0, + 0x6a, 0x58, 0x67, 0xb2, 0x1a, 0x57, 0x60, 0x05, 0x0b, 0x90, 0xdc, 0xbf, 0x64, 0x2c, 0xc7, 0x87, + 0x5e, 0xc7, 0x46, 0x30, 0x56, 0xc2, 0xb5, 0xa9, 0x0e, 0x25, 0x63, 0x79, 0x14, 0x78, 0x1d, 0x5b, + 0x1f, 0x02, 0xb4, 0x82, 0x30, 0xfc, 0x8d, 0x9b, 0xb0, 0x01, 0xcb, 0xb6, 0x33, 0x89, 0x4f, 0x98, + 0x03, 0x1a, 0x9c, 0xd0, 0xef, 0x43, 0x05, 0xa7, 0x57, 0xd7, 0x8d, 0x62, 0x71, 0x0b, 0x4a, 0x9e, + 0x1b, 0xc5, 0x9b, 0x85, 0x3b, 0xc5, 0x99, 0xc9, 0x47, 0x70, 0xfd, 0x0e, 0x54, 0xf6, 0xac, 0xf3, + 0x67, 0x38, 0x72, 0x58, 0x1a, 0x0d, 0xa1, 0x1c, 0x12, 0x39, 0x9e, 0x75, 0x80, 0xa1, 0x15, 0x1e, + 0x3b, 0x31, 0x31, 0xec, 0xff, 0xab, 0x00, 0xb5, 0xc1, 0xf4, 0xf0, 0xbb, 0xa9, 0x13, 0x5e, 0x60, + 0x9d, 0xef, 0x41, 0x31, 0xbe, 0x98, 0x50, 0x8e, 0xc6, 0xc3, 0xab, 0x72, 0x3e, 0xa6, 0xf8, 0x07, + 0x98, 0xc9, 0x40, 0x12, 0x6c, 0x84, 0x1f, 0xd8, 0x8e, 0xea, 0x83, 0x65, 0x63, 0x05, 0x93, 0x1d, + 0x1b, 0x37, 0xcb, 0x60, 0x22, 0x47, 0x61, 0x29, 0x98, 0x88, 0x3b, 0xb0, 0x3c, 0x3a, 0x71, 0x3d, + 0x9b, 0x06, 0x20, 0x5f, 0x67, 0x46, 0xe0, 0x28, 0x85, 0xc1, 0x99, 0x19, 0xb9, 0xdf, 0xab, 0xcd, + 0xaf, 0x1c, 0x06, 0x67, 0x03, 0xf7, 0x7b, 0x47, 0x1f, 0xca, 0x1d, 0x18, 0x60, 0x65, 0xd0, 0x6a, + 0x76, 0x9b, 0x86, 0x76, 0x09, 0x7f, 0xb7, 0xbf, 0xee, 0x0c, 0x86, 0x03, 0xad, 0x20, 0x1a, 0x00, + 0xbd, 0xfe, 0xd0, 0x94, 0xe9, 0x25, 0xb1, 0x02, 0x4b, 0x9d, 0x9e, 0x56, 0x44, 0x1a, 0x84, 0x77, + 0x7a, 0x5a, 0x49, 0x94, 0xa1, 0xd8, 0xec, 0x7d, 0xa3, 0x2d, 0xd3, 0x8f, 0x6e, 0x57, 0x5b, 0xd1, + 0xff, 0x9f, 0x25, 0xa8, 0xf6, 0x0f, 0xbf, 0x75, 0x46, 0x31, 0xb6, 0x19, 0x67, 0xa9, 0x13, 0xbe, + 0x70, 0x42, 0x6a, 0x76, 0xd1, 0x90, 0x29, 0x6c, 0x88, 0x7d, 0x48, 0x8d, 0x2b, 0x1a, 0x4b, 0xf6, + 0x21, 0xd1, 0x8d, 0x4e, 0x9c, 0xb1, 0x45, 0x8d, 0x43, 0x3a, 0x4a, 0xe1, 0xaa, 0x08, 0x0e, 0xbf, + 0xa5, 0xe6, 0x15, 0x0d, 0xfc, 0x29, 0x6e, 0x43, 0x8d, 0xcb, 0xc8, 0xce, 0x2f, 0x60, 0xd0, 0xec, + 0xe4, 0x5b, 0xc9, 0x4e, 0x3e, 0xca, 0x49, 0xa5, 0x32, 0x52, 0xee, 0xec, 0x0c, 0xea, 0xc9, 0x19, + 0x1d, 0x1c, 0x7e, 0xcb, 0xd8, 0x0a, 0xcf, 0xe8, 0xe0, 0xf0, 0x5b, 0x42, 0xbd, 0x07, 0xeb, 0xd1, + 0xf4, 0x30, 0x1a, 0x85, 0xee, 0x24, 0x76, 0x03, 0x9f, 0x69, 0xaa, 0x44, 0xa3, 0x65, 0x11, 0x44, + 0x7c, 0x0f, 0x2a, 0x93, 0xe9, 0xa1, 0xe9, 0xfa, 0x47, 0x01, 0xed, 0x5e, 0xb5, 0x87, 0xab, 0x3c, + 0x30, 0xfb, 0xd3, 0xc3, 0x8e, 0x7f, 0x14, 0x18, 0xe5, 0x09, 0xff, 0x10, 0x3a, 0xac, 0xfa, 0x41, + 0x6c, 0xa2, 0xa0, 0x64, 0x8e, 0x9d, 0xd8, 0xa2, 0x6d, 0x8d, 0xc5, 0x9d, 0x6e, 0x30, 0x3a, 0xdd, + 0x73, 0x62, 0x4b, 0xdc, 0x87, 0x4a, 0xe4, 0x5b, 0x93, 0xe8, 0x24, 0x88, 0x69, 0x7f, 0xab, 0x3d, + 0x6c, 0xc8, 0xb9, 0x23, 0xa1, 0x46, 0x82, 0xd7, 0xdf, 0x86, 0xb2, 0xfc, 0x06, 0x0a, 0x23, 0xb1, 0xe3, 0x5b, 0x7e, 0x6c, 0x26, 0xe2, 0x55, 0x85, 0x01, 0x1d, 0x5b, 0xff, 0xbb, 0x05, 0xd0, 0x06, 0x99, 0x6a, 0xd3, 0x87, 0x16, 0x71, 0x99, 0xd7, 0x01, 0xac, 0xd1, 0x28, 0x98, 0x72, 0x31, 0x3c, 0x19, 0xab, 0x12, 0xd2, 0xb1, 0xb3, 0x7d, 0x5d, 0xcc, 0xf5, 0xf5, 0x1b, 0x50, 0x57, 0xf9, 0x32, - 0x0c, 0xa2, 0x26, 0x61, 0xaa, 0xb7, 0xa3, 0x69, 0x8e, 0x4b, 0xac, 0x44, 0x53, 0xce, 0x7d, 0x15, - 0x96, 0x49, 0x16, 0x8b, 0xd4, 0x08, 0x72, 0x4a, 0xff, 0xd3, 0x02, 0xac, 0x76, 0x7c, 0xdb, 0x39, - 0x1f, 0x8c, 0x2c, 0x5f, 0x75, 0xa0, 0x1b, 0x99, 0x2e, 0xc2, 0xcc, 0x68, 0x64, 0xf9, 0x52, 0x1e, - 0xaa, 0xb9, 0x51, 0x42, 0x87, 0x6d, 0x60, 0x02, 0xfa, 0xd4, 0x12, 0x95, 0x58, 0x25, 0x08, 0x7d, - 0xec, 0x6d, 0x58, 0x3b, 0x74, 0xbc, 0xc0, 0x3f, 0x36, 0xe3, 0xc0, 0x64, 0x79, 0x90, 0xdb, 0xb2, - 0xca, 0xe0, 0x61, 0x30, 0x24, 0xb9, 0x70, 0x03, 0xca, 0x13, 0x2b, 0x8c, 0xa3, 0xcd, 0xd2, 0x9d, + 0x0c, 0xa2, 0x26, 0x61, 0xaa, 0xb7, 0xa3, 0x69, 0x8e, 0x4b, 0x94, 0xa3, 0x29, 0xe7, 0xbe, 0x0a, + 0x2b, 0x24, 0x8b, 0x45, 0x6a, 0x04, 0x39, 0xa5, 0xff, 0x69, 0x01, 0x56, 0x3b, 0xbe, 0xed, 0x9c, + 0x0f, 0x46, 0x96, 0xaf, 0x3a, 0xd0, 0x8d, 0x4c, 0x17, 0x61, 0x66, 0x34, 0xb2, 0x7c, 0x29, 0x0f, + 0xd5, 0xdc, 0x28, 0xa1, 0xc3, 0x36, 0x30, 0x01, 0x7d, 0x6a, 0x89, 0x4a, 0xac, 0x12, 0x84, 0x3e, + 0xf6, 0x36, 0xac, 0x1d, 0x3a, 0x5e, 0xe0, 0x1f, 0x9b, 0x71, 0x60, 0xb2, 0x3c, 0xc8, 0x6d, 0x59, + 0x65, 0xf0, 0x30, 0x18, 0x92, 0x5c, 0xb8, 0x01, 0xcb, 0x13, 0x2b, 0x8c, 0xa3, 0xcd, 0xd2, 0x9d, 0x22, 0x2e, 0x79, 0x4a, 0x60, 0x37, 0xbb, 0x91, 0x39, 0xf5, 0xdd, 0xef, 0xa6, 0xdc, 0x8c, 0x8a, 0x51, 0x71, 0xa3, 0x03, 0x4a, 0x8b, 0x7b, 0xa0, 0xf1, 0x97, 0xa9, 0xd8, 0xec, 0x9c, 0x6c, 0x10, 0x9c, 0x0a, 0x26, 0xc6, 0xf8, 0xdf, 0x15, 0xa1, 0xb2, 0x33, 0xf5, 0x47, 0x38, 0x18, 0xe2, 0x4d, @@ -17278,7 +17296,7 @@ var fileDescriptor_2d655ab2f7683c23 = []byte{ 0x37, 0x9d, 0xf3, 0x89, 0xe7, 0x8e, 0xdc, 0xd8, 0x1c, 0x59, 0x51, 0x2c, 0x7b, 0x41, 0x30, 0xae, 0x2d, 0x51, 0x2d, 0x2b, 0x8a, 0xc5, 0x27, 0x50, 0x0f, 0x83, 0x69, 0xec, 0xfa, 0x8e, 0x39, 0xb2, 0x3c, 0x96, 0x1f, 0x6b, 0x0f, 0xd7, 0xf9, 0x8b, 0x06, 0x63, 0x5a, 0x96, 0xe7, 0x19, 0xb5, 0x30, - 0x4d, 0xe8, 0xff, 0x61, 0x81, 0xfb, 0x66, 0xc7, 0xb3, 0x8e, 0x45, 0x05, 0x4a, 0xbd, 0x7e, 0xaf, + 0x4d, 0xe8, 0xff, 0x41, 0x81, 0xfb, 0x66, 0xc7, 0xb3, 0x8e, 0x45, 0x05, 0x4a, 0xbd, 0x7e, 0xaf, 0xad, 0x5d, 0x12, 0x75, 0xa8, 0x74, 0x7a, 0xc3, 0xb6, 0xd1, 0x6b, 0x76, 0xb5, 0x02, 0x31, 0xb1, 0x61, 0x73, 0xab, 0xdb, 0xd6, 0x96, 0x10, 0xf3, 0xac, 0xdf, 0x6d, 0x0e, 0x3b, 0xdd, 0xb6, 0x56, 0x62, 0x8c, 0xd1, 0x69, 0x0d, 0xb5, 0x8a, 0xd0, 0xa0, 0xbe, 0x6f, 0xf4, 0xb7, 0x0f, 0x5a, 0x6d, @@ -17291,50 +17309,50 @@ var fileDescriptor_2d655ab2f7683c23 = []byte{ 0xe5, 0x79, 0x87, 0xd6, 0xe8, 0x54, 0xce, 0xda, 0x24, 0xcd, 0x9a, 0x44, 0x68, 0x8d, 0x33, 0xfb, 0x58, 0x85, 0x00, 0xb8, 0x93, 0xbd, 0x03, 0x6b, 0x8a, 0x50, 0x89, 0x29, 0xbc, 0xd5, 0x37, 0x14, 0x58, 0x0a, 0x2a, 0x9f, 0xc2, 0xb5, 0x19, 0x42, 0x53, 0x6d, 0x2b, 0xac, 0x1d, 0x6d, 0xe4, 0x33, - 0xf4, 0x78, 0x93, 0x59, 0x90, 0x4d, 0x6d, 0xa9, 0xe5, 0x45, 0xd9, 0x5a, 0xbc, 0xc1, 0x7e, 0x06, - 0xd7, 0x26, 0x61, 0xf0, 0xc2, 0x8d, 0xdc, 0xc0, 0x27, 0x91, 0x28, 0x9a, 0x7a, 0x72, 0x46, 0x2c, - 0x53, 0xf5, 0xae, 0x64, 0xd0, 0x06, 0x61, 0x69, 0x52, 0x2c, 0xce, 0x37, 0x71, 0x9c, 0x90, 0x58, - 0xf4, 0xa2, 0x7c, 0xfb, 0x8e, 0x13, 0x8a, 0x6d, 0xb8, 0xfd, 0x92, 0x7c, 0x34, 0x9d, 0xb1, 0x95, - 0x15, 0xaa, 0xee, 0x6b, 0x0b, 0xf3, 0xe3, 0x14, 0xee, 0xd8, 0xa2, 0x09, 0xaf, 0xbf, 0xac, 0x14, - 0xd6, 0x23, 0xab, 0x54, 0xc6, 0x8d, 0x85, 0x65, 0x3c, 0x27, 0xe5, 0xf2, 0x07, 0x8a, 0x60, 0xa5, - 0x13, 0x7e, 0xa0, 0x88, 0x01, 0x69, 0xa2, 0x3f, 0x83, 0x0d, 0x29, 0x77, 0xda, 0xc1, 0xd8, 0x72, - 0x7d, 0x35, 0xae, 0xb5, 0xb9, 0x6d, 0x5d, 0x30, 0xdd, 0x36, 0x91, 0x71, 0xff, 0xeb, 0x7f, 0xaf, - 0x0c, 0x25, 0x44, 0x0a, 0x3d, 0x95, 0x30, 0x92, 0x5c, 0xd8, 0xce, 0xad, 0xd2, 0x3f, 0xfa, 0xa7, - 0xb7, 0x2f, 0xb1, 0x6c, 0xf1, 0x06, 0x14, 0x3d, 0x37, 0xa6, 0x49, 0x95, 0xec, 0x4b, 0x52, 0x46, - 0xdd, 0xbd, 0x64, 0x20, 0x4e, 0xdc, 0x82, 0x02, 0x0b, 0x19, 0xc9, 0x56, 0xa3, 0xa4, 0xd4, 0xdd, - 0x4b, 0x46, 0x61, 0x22, 0x6e, 0x42, 0xe1, 0x85, 0x94, 0x38, 0xea, 0x8c, 0x67, 0x39, 0x15, 0xb1, - 0x2f, 0xc4, 0x1d, 0x28, 0x8e, 0x02, 0x56, 0x1a, 0x13, 0x3c, 0x4b, 0x6d, 0x58, 0xfe, 0x28, 0xf0, - 0xc4, 0x9b, 0x50, 0x0c, 0xad, 0x33, 0xb9, 0xfa, 0x25, 0x7b, 0x4b, 0xc4, 0x42, 0x24, 0x0a, 0xad, - 0x33, 0xac, 0xc4, 0x11, 0x4d, 0x80, 0xa4, 0x12, 0x8a, 0x3f, 0xe2, 0x67, 0x8e, 0xc4, 0x1d, 0x28, - 0x9c, 0xd1, 0x00, 0x27, 0x7a, 0xd2, 0x73, 0xd7, 0xb7, 0x83, 0xb3, 0xc1, 0xc4, 0x19, 0x21, 0xc5, - 0x99, 0x78, 0x0b, 0x8a, 0xd1, 0xf4, 0x90, 0x06, 0x30, 0x61, 0x32, 0x19, 0x79, 0x0b, 0x3f, 0x14, - 0x4d, 0x0f, 0xc5, 0xdb, 0x50, 0x1a, 0x05, 0x61, 0x28, 0x77, 0x6a, 0x4d, 0x55, 0x58, 0x89, 0x9a, - 0xa8, 0x37, 0x22, 0x1e, 0x3f, 0x18, 0xcb, 0x01, 0x91, 0x44, 0xa9, 0xac, 0x87, 0x1f, 0x8c, 0xc5, - 0x5d, 0x29, 0x40, 0xe6, 0x76, 0x69, 0x25, 0x5e, 0x62, 0x39, 0x88, 0xc5, 0x41, 0x1a, 0x5b, 0xe7, - 0xa4, 0x94, 0x26, 0x44, 0x4a, 0xae, 0xc4, 0x3a, 0x8d, 0xad, 0x73, 0x71, 0x17, 0x8a, 0x2f, 0x9c, - 0x11, 0xe9, 0xa7, 0xc9, 0xd7, 0xe4, 0x20, 0x3d, 0xa3, 0xe6, 0x21, 0x9a, 0xf6, 0x89, 0xc0, 0xb3, - 0x49, 0x55, 0x4d, 0xc6, 0x72, 0x27, 0xf0, 0xec, 0x67, 0x34, 0x96, 0x84, 0x44, 0x71, 0xda, 0x9a, - 0x9e, 0xe3, 0x6a, 0xd0, 0x58, 0xf0, 0xb5, 0xa6, 0xe7, 0x1d, 0x1b, 0x05, 0x2b, 0xdf, 0x7e, 0x41, - 0x0a, 0x6a, 0xc1, 0xc0, 0x9f, 0xe2, 0x0e, 0x0a, 0x56, 0x9e, 0x33, 0x8a, 0xdd, 0x17, 0x6e, 0x7c, - 0x41, 0x2a, 0x68, 0xc1, 0xc8, 0x82, 0xc4, 0x2e, 0x68, 0x13, 0xc9, 0xcc, 0x4c, 0x9f, 0xb9, 0x19, - 0x29, 0x9c, 0xb5, 0x87, 0xaf, 0xcb, 0x69, 0xb2, 0x98, 0xd5, 0x19, 0x6b, 0x93, 0x3c, 0x62, 0x6b, - 0x19, 0x4a, 0xce, 0xf9, 0x24, 0xfc, 0xaa, 0x54, 0xd9, 0xd0, 0x36, 0xf5, 0x5d, 0x58, 0x91, 0xb5, - 0x9e, 0xb3, 0x06, 0x5d, 0x87, 0x8a, 0x1b, 0xe1, 0x2e, 0x15, 0xc5, 0x52, 0xcf, 0x59, 0x71, 0xa3, + 0xf4, 0x78, 0x93, 0x59, 0x90, 0x4d, 0x6d, 0xa9, 0xcb, 0x8b, 0xb2, 0xb5, 0x78, 0x83, 0xfd, 0x0c, + 0xae, 0x4d, 0xc2, 0xe0, 0x85, 0x1b, 0xb9, 0x81, 0x4f, 0x22, 0x51, 0x34, 0xf5, 0xe4, 0x8c, 0x58, + 0xa1, 0xea, 0x5d, 0xc9, 0xa0, 0x0d, 0xc2, 0xd2, 0xa4, 0x58, 0x9c, 0x6f, 0xe2, 0x38, 0x21, 0xb1, + 0xe8, 0x45, 0xf9, 0xf6, 0x1d, 0x27, 0x14, 0xdb, 0x70, 0xfb, 0x25, 0xf9, 0x68, 0x3a, 0x63, 0x2b, + 0x2b, 0x54, 0xdd, 0xd7, 0x16, 0xe6, 0xc7, 0x29, 0xdc, 0xb1, 0x45, 0x13, 0x5e, 0x7f, 0x59, 0x29, + 0xac, 0x47, 0x56, 0xa9, 0x8c, 0x1b, 0x0b, 0xcb, 0x78, 0x4e, 0xca, 0xe5, 0x0f, 0x14, 0xc1, 0x4a, + 0x27, 0xfc, 0x40, 0x11, 0x03, 0xd2, 0x44, 0x7f, 0x06, 0x1b, 0x52, 0xee, 0xb4, 0x83, 0xb1, 0xe5, + 0xfa, 0x6a, 0x5c, 0x6b, 0x73, 0xdb, 0xba, 0x60, 0xba, 0x6d, 0x22, 0xe3, 0xfe, 0xd7, 0xff, 0xde, + 0x32, 0x94, 0x10, 0x29, 0xf4, 0x54, 0xc2, 0x48, 0x72, 0x61, 0x3b, 0xb7, 0x4a, 0xff, 0xf0, 0x9f, + 0xdc, 0xbe, 0xc4, 0xb2, 0xc5, 0x1b, 0x50, 0xf4, 0xdc, 0x98, 0x26, 0x55, 0xb2, 0x2f, 0x49, 0x19, + 0x75, 0xf7, 0x92, 0x81, 0x38, 0x71, 0x0b, 0x0a, 0x2c, 0x64, 0x24, 0x5b, 0x8d, 0x92, 0x52, 0x77, + 0x2f, 0x19, 0x85, 0x89, 0xb8, 0x09, 0x85, 0x17, 0x52, 0xe2, 0xa8, 0x33, 0x9e, 0xe5, 0x54, 0xc4, + 0xbe, 0x10, 0x77, 0xa0, 0x38, 0x0a, 0x58, 0x69, 0x4c, 0xf0, 0x2c, 0xb5, 0x61, 0xf9, 0xa3, 0xc0, + 0x13, 0x6f, 0x42, 0x31, 0xb4, 0xce, 0xe4, 0xea, 0x97, 0xec, 0x2d, 0x11, 0x0b, 0x91, 0x28, 0xb4, + 0xce, 0xb0, 0x12, 0x47, 0x34, 0x01, 0x92, 0x4a, 0x28, 0xfe, 0x88, 0x9f, 0x39, 0x12, 0x77, 0xa0, + 0x70, 0x46, 0x03, 0x9c, 0xe8, 0x49, 0xcf, 0x5d, 0xdf, 0x0e, 0xce, 0x06, 0x13, 0x67, 0x84, 0x14, + 0x67, 0xe2, 0x2d, 0x28, 0x46, 0xd3, 0x43, 0x1a, 0xc0, 0x84, 0xc9, 0x64, 0xe4, 0x2d, 0xfc, 0x50, + 0x34, 0x3d, 0x14, 0x6f, 0x43, 0x69, 0x14, 0x84, 0xa1, 0xdc, 0xa9, 0x35, 0x55, 0x61, 0x25, 0x6a, + 0xa2, 0xde, 0x88, 0x78, 0xfc, 0x60, 0x2c, 0x07, 0x44, 0x12, 0xa5, 0xb2, 0x1e, 0x7e, 0x30, 0x16, + 0x77, 0xa5, 0x00, 0x99, 0xdb, 0xa5, 0x95, 0x78, 0x89, 0xe5, 0x20, 0x16, 0x07, 0x69, 0x6c, 0x9d, + 0x93, 0x52, 0x9a, 0x10, 0x29, 0xb9, 0x12, 0xeb, 0x34, 0xb6, 0xce, 0xc5, 0x5d, 0x28, 0xbe, 0x70, + 0x46, 0xa4, 0x9f, 0x26, 0x5f, 0x93, 0x83, 0xf4, 0x8c, 0x9a, 0x87, 0x68, 0xda, 0x27, 0x02, 0xcf, + 0x26, 0x55, 0x35, 0x19, 0xcb, 0x9d, 0xc0, 0xb3, 0x9f, 0xd1, 0x58, 0x12, 0x12, 0xc5, 0x69, 0x6b, + 0x7a, 0x8e, 0xab, 0x41, 0x63, 0xc1, 0xd7, 0x9a, 0x9e, 0x77, 0x6c, 0x14, 0xac, 0x7c, 0xfb, 0x05, + 0x29, 0xa8, 0x05, 0x03, 0x7f, 0x8a, 0x3b, 0x28, 0x58, 0x79, 0xce, 0x28, 0x76, 0x5f, 0xb8, 0xf1, + 0x05, 0xa9, 0xa0, 0x05, 0x23, 0x0b, 0x12, 0xbb, 0xa0, 0x4d, 0x24, 0x33, 0x33, 0x7d, 0xe6, 0x66, + 0xa4, 0x70, 0xd6, 0x1e, 0xbe, 0x2e, 0xa7, 0xc9, 0x62, 0x56, 0x67, 0xac, 0x4d, 0xf2, 0x88, 0xad, + 0x15, 0x28, 0x39, 0xe7, 0x93, 0xf0, 0xab, 0x52, 0x65, 0x43, 0xdb, 0xd4, 0x77, 0xa1, 0x2c, 0x6b, + 0x3d, 0x67, 0x0d, 0xba, 0x0e, 0x15, 0x37, 0xc2, 0x5d, 0x2a, 0x8a, 0xa5, 0x9e, 0x53, 0x76, 0xa3, 0x16, 0x26, 0x51, 0x5c, 0xc1, 0xc2, 0xe5, 0xd6, 0x46, 0xbf, 0xf5, 0xbf, 0x57, 0x00, 0x48, 0xfb, 0x09, 0x1b, 0xe9, 0x39, 0xbe, 0xd2, 0xa9, 0x3c, 0xc7, 0x4f, 0x32, 0x2d, 0xa5, 0x99, 0xe6, 0xb5, - 0xca, 0xe2, 0x02, 0xad, 0x72, 0x4e, 0x33, 0x2c, 0xfd, 0x06, 0x9a, 0x61, 0xf9, 0x15, 0x9a, 0xa1, - 0x7e, 0x1d, 0xaa, 0x89, 0x61, 0x40, 0xd4, 0xa1, 0x60, 0x49, 0xb1, 0xb9, 0x60, 0xe9, 0xf7, 0x50, - 0x4f, 0x57, 0xaa, 0x7f, 0x1e, 0x87, 0x29, 0x25, 0x4c, 0x17, 0x0e, 0xf5, 0x9f, 0x41, 0x5d, 0xb2, - 0xd7, 0xc0, 0xdb, 0x76, 0x8e, 0xc4, 0xfb, 0x00, 0x49, 0x3a, 0x92, 0xda, 0x4d, 0xba, 0x2e, 0xb7, - 0x9d, 0x23, 0x23, 0x83, 0xd7, 0xff, 0x66, 0x99, 0xf4, 0xc4, 0x6d, 0x56, 0xd0, 0xa4, 0x26, 0x56, - 0xc8, 0x68, 0x62, 0x89, 0x9c, 0xb8, 0x94, 0xd7, 0x46, 0x4f, 0x5c, 0xdb, 0x76, 0x7c, 0xa5, 0x75, - 0x72, 0x0a, 0x27, 0xb2, 0xe5, 0x1d, 0x4b, 0xd1, 0x42, 0xa8, 0x8f, 0x8e, 0x27, 0xa1, 0x13, 0x45, - 0xac, 0xef, 0x58, 0xde, 0xb1, 0xe2, 0x5b, 0xe5, 0x1f, 0xe2, 0x5b, 0xd7, 0xa1, 0x82, 0xa2, 0x32, - 0x19, 0xbd, 0x78, 0x3f, 0x59, 0x91, 0x46, 0x41, 0xf1, 0x0e, 0xac, 0x48, 0x73, 0x85, 0x64, 0x18, - 0x72, 0x29, 0x6c, 0x33, 0xd0, 0x50, 0x58, 0x32, 0xdd, 0x05, 0xe3, 0xb1, 0xe3, 0xc7, 0x4a, 0xbe, - 0x97, 0x49, 0xf1, 0x1e, 0x54, 0x03, 0xdf, 0x64, 0x9b, 0x86, 0xe4, 0x18, 0x72, 0x69, 0xf6, 0xfd, - 0x03, 0x82, 0x1a, 0x95, 0x40, 0xfe, 0xc2, 0xaa, 0x78, 0xc1, 0x99, 0x39, 0xb2, 0x42, 0x9b, 0xb8, - 0x46, 0xc5, 0x58, 0xf1, 0x82, 0xb3, 0x96, 0x15, 0xda, 0xac, 0xef, 0x7c, 0xe7, 0x4f, 0xc7, 0xc4, + 0xca, 0xe2, 0x02, 0xad, 0x72, 0x4e, 0x33, 0x2c, 0xfd, 0x06, 0x9a, 0xe1, 0xf2, 0x2b, 0x34, 0x43, + 0xfd, 0x3a, 0x54, 0x13, 0xc3, 0x80, 0xa8, 0x43, 0xc1, 0x92, 0x62, 0x73, 0xc1, 0xd2, 0xef, 0xa1, + 0x9e, 0xae, 0x54, 0xff, 0x3c, 0x0e, 0x53, 0x4a, 0x98, 0x2e, 0x1c, 0xea, 0x3f, 0x83, 0xba, 0x64, + 0xaf, 0x81, 0xb7, 0xed, 0x1c, 0x89, 0xf7, 0x01, 0x92, 0x74, 0x24, 0xb5, 0x9b, 0x74, 0x5d, 0x6e, + 0x3b, 0x47, 0x46, 0x06, 0xaf, 0xff, 0xcd, 0x65, 0xd2, 0x13, 0xb7, 0x59, 0x41, 0x93, 0x9a, 0x58, + 0x21, 0xa3, 0x89, 0x25, 0x72, 0xe2, 0x52, 0x5e, 0x1b, 0x3d, 0x71, 0x6d, 0xdb, 0xf1, 0x95, 0xd6, + 0xc9, 0x29, 0x9c, 0xc8, 0x96, 0x77, 0x2c, 0x45, 0x0b, 0xa1, 0x3e, 0x3a, 0x9e, 0x84, 0x4e, 0x14, + 0xb1, 0xbe, 0x63, 0x79, 0xc7, 0x8a, 0x6f, 0x2d, 0xff, 0x10, 0xdf, 0xba, 0x0e, 0x15, 0x14, 0x95, + 0xc9, 0xe8, 0xc5, 0xfb, 0x49, 0x59, 0x1a, 0x05, 0xc5, 0x3b, 0x50, 0x96, 0xe6, 0x0a, 0xc9, 0x30, + 0xe4, 0x52, 0xd8, 0x66, 0xa0, 0xa1, 0xb0, 0x64, 0xba, 0x0b, 0xc6, 0x63, 0xc7, 0x8f, 0x95, 0x7c, + 0x2f, 0x93, 0xe2, 0x3d, 0xa8, 0x06, 0xbe, 0xc9, 0x36, 0x0d, 0xc9, 0x31, 0xe4, 0xd2, 0xec, 0xfb, + 0x07, 0x04, 0x35, 0x2a, 0x81, 0xfc, 0x85, 0x55, 0xf1, 0x82, 0x33, 0x73, 0x64, 0x85, 0x36, 0x71, + 0x8d, 0x8a, 0x51, 0xf6, 0x82, 0xb3, 0x96, 0x15, 0xda, 0xac, 0xef, 0x7c, 0xe7, 0x4f, 0xc7, 0xc4, 0x29, 0x56, 0x0d, 0x99, 0x12, 0x37, 0xa1, 0x3a, 0xf2, 0xa6, 0x51, 0xec, 0x84, 0x5b, 0x17, 0x6c, 0xa5, 0x32, 0x52, 0x00, 0xd6, 0x6b, 0x12, 0xba, 0x63, 0x2b, 0xbc, 0x20, 0xb6, 0x50, 0x31, 0x54, 0x92, 0x84, 0xce, 0x53, 0xd7, 0x3e, 0x67, 0x53, 0x95, 0xc1, 0x09, 0xa4, 0x3f, 0x21, 0x43, 0x62, @@ -17343,46 +17361,46 @@ var fileDescriptor_2d655ab2f7683c23 = []byte{ 0xb3, 0xbe, 0xc4, 0x20, 0x22, 0xf8, 0x1c, 0x56, 0x8f, 0x1d, 0xdf, 0x09, 0xad, 0xd8, 0xb1, 0x51, 0x44, 0x20, 0xe3, 0x52, 0x4d, 0x0d, 0xf3, 0x13, 0x85, 0x42, 0x3e, 0x5a, 0x3f, 0xce, 0xa4, 0xb0, 0x96, 0x52, 0x62, 0xbe, 0xc2, 0xb3, 0x85, 0x53, 0x28, 0x8a, 0xcb, 0x2f, 0x26, 0x95, 0xbd, 0xca, - 0xa2, 0x38, 0x83, 0x87, 0x5c, 0x65, 0xfd, 0x3b, 0x58, 0x91, 0x63, 0x8b, 0x62, 0x30, 0x72, 0x92, - 0xfc, 0x9e, 0xc7, 0x62, 0x30, 0xc2, 0x71, 0xdd, 0xca, 0x22, 0x79, 0xa5, 0xca, 0x59, 0x5b, 0x67, - 0x20, 0x9b, 0x83, 0x50, 0x5b, 0xc1, 0x79, 0x65, 0x5a, 0x87, 0xae, 0x87, 0xbc, 0xaf, 0x28, 0xb5, - 0xb0, 0xa9, 0xe7, 0x35, 0x19, 0xa4, 0xf7, 0xa1, 0xa2, 0x66, 0xc2, 0x5f, 0xca, 0x37, 0xf5, 0x09, - 0xd4, 0xb3, 0x3d, 0xf4, 0x97, 0xd3, 0x10, 0xd6, 0x46, 0xa2, 0x38, 0x08, 0x13, 0x36, 0x56, 0x71, - 0xa3, 0x01, 0xa5, 0xf5, 0x3f, 0x5a, 0x82, 0x1a, 0x69, 0x45, 0x7d, 0xd2, 0xf9, 0xc4, 0xfb, 0x20, - 0x46, 0xa1, 0x63, 0xc5, 0x8e, 0xe9, 0x9c, 0xc7, 0xa1, 0x25, 0x75, 0x1f, 0x16, 0x45, 0x35, 0xc6, - 0xb4, 0x11, 0xc1, 0xea, 0xcf, 0x6d, 0xa8, 0x4d, 0xac, 0x30, 0x52, 0x7a, 0x37, 0x7f, 0x1d, 0x18, - 0x24, 0xb5, 0x5e, 0xcd, 0x3f, 0x46, 0x99, 0x35, 0x0e, 0x4e, 0x1d, 0x9f, 0x2d, 0x0e, 0x6c, 0x6b, - 0x69, 0x10, 0x7c, 0x88, 0xe0, 0x81, 0xfb, 0x3d, 0x4a, 0x3b, 0x80, 0x82, 0x90, 0xec, 0x6c, 0xe6, - 0x0d, 0x37, 0xb9, 0xc1, 0x99, 0xfa, 0x3d, 0x78, 0x96, 0xd0, 0x18, 0x19, 0x7a, 0xfd, 0x57, 0x00, - 0x29, 0x46, 0xdc, 0x80, 0xab, 0xcf, 0x3a, 0x83, 0xce, 0x56, 0xa7, 0xdb, 0x19, 0x7e, 0x63, 0x1e, - 0xf4, 0x06, 0xfb, 0xed, 0x56, 0x67, 0xa7, 0xd3, 0xde, 0xd6, 0x2e, 0x89, 0xab, 0x20, 0x32, 0x38, - 0xfa, 0xd9, 0x6d, 0x6b, 0x05, 0xb1, 0x09, 0x1b, 0x19, 0x78, 0xa7, 0xa7, 0x30, 0x4b, 0xfa, 0xff, - 0x5c, 0x80, 0xd5, 0x7d, 0x5e, 0x7a, 0x4f, 0x9d, 0x8b, 0x6d, 0x36, 0xbd, 0x8d, 0x14, 0xdb, 0x2c, - 0x19, 0xf4, 0x5b, 0xdc, 0x82, 0xda, 0xe4, 0xd4, 0xb9, 0x30, 0x73, 0x66, 0xaa, 0x2a, 0x82, 0x5a, - 0xc4, 0x20, 0xdf, 0x85, 0xe5, 0x80, 0x9a, 0x20, 0x85, 0xa8, 0xf5, 0xb9, 0xb6, 0x19, 0x92, 0x00, - 0xf5, 0xd7, 0xa4, 0xa8, 0xac, 0xa2, 0x2c, 0x0b, 0xa3, 0x8e, 0xdd, 0x80, 0x32, 0xa2, 0x50, 0xfa, + 0xa2, 0x38, 0x83, 0x87, 0x5c, 0x65, 0xfd, 0x3b, 0x28, 0xcb, 0xb1, 0x45, 0x31, 0x18, 0x39, 0x49, + 0x7e, 0xcf, 0x63, 0x31, 0x18, 0xe1, 0xb8, 0x6e, 0x65, 0x91, 0xbc, 0x52, 0xe5, 0xac, 0xad, 0x33, + 0x90, 0xcd, 0x41, 0xa8, 0xad, 0xe0, 0xbc, 0x32, 0xad, 0x43, 0xd7, 0x43, 0xde, 0x57, 0x94, 0x5a, + 0xd8, 0xd4, 0xf3, 0x9a, 0x0c, 0xd2, 0xfb, 0x50, 0x51, 0x33, 0xe1, 0x2f, 0xe5, 0x9b, 0xfa, 0x04, + 0xea, 0xd9, 0x1e, 0xfa, 0xcb, 0x69, 0x08, 0x6b, 0x23, 0x51, 0x1c, 0x84, 0x09, 0x1b, 0xab, 0xb8, + 0xd1, 0x80, 0xd2, 0xfa, 0x1f, 0x2d, 0x41, 0x8d, 0xb4, 0xa2, 0x3e, 0xe9, 0x7c, 0xe2, 0x7d, 0x10, + 0xa3, 0xd0, 0xb1, 0x62, 0xc7, 0x74, 0xce, 0xe3, 0xd0, 0x92, 0xba, 0x0f, 0x8b, 0xa2, 0x1a, 0x63, + 0xda, 0x88, 0x60, 0xf5, 0xe7, 0x36, 0xd4, 0x26, 0x56, 0x18, 0x29, 0xbd, 0x9b, 0xbf, 0x0e, 0x0c, + 0x92, 0x5a, 0xaf, 0xe6, 0x1f, 0xa3, 0xcc, 0x1a, 0x07, 0xa7, 0x8e, 0xcf, 0x16, 0x07, 0xb6, 0xb5, + 0x34, 0x08, 0x3e, 0x44, 0xf0, 0xc0, 0xfd, 0x1e, 0xa5, 0x1d, 0x40, 0x41, 0x48, 0x76, 0x36, 0xf3, + 0x86, 0x9b, 0xdc, 0xe0, 0x4c, 0xfd, 0x1e, 0x3c, 0x4b, 0x68, 0x8c, 0x0c, 0xbd, 0xfe, 0x2b, 0x80, + 0x14, 0x23, 0x6e, 0xc0, 0xd5, 0x67, 0x9d, 0x41, 0x67, 0xab, 0xd3, 0xed, 0x0c, 0xbf, 0x31, 0x0f, + 0x7a, 0x83, 0xfd, 0x76, 0xab, 0xb3, 0xd3, 0x69, 0x6f, 0x6b, 0x97, 0xc4, 0x55, 0x10, 0x19, 0x1c, + 0xfd, 0xec, 0xb6, 0xb5, 0x82, 0xd8, 0x84, 0x8d, 0x0c, 0xbc, 0xd3, 0x53, 0x98, 0x25, 0xfd, 0x7f, + 0x2e, 0xc0, 0xea, 0x3e, 0x2f, 0xbd, 0xa7, 0xce, 0xc5, 0x36, 0x9b, 0xde, 0x46, 0x8a, 0x6d, 0x96, + 0x0c, 0xfa, 0x2d, 0x6e, 0x41, 0x6d, 0x72, 0xea, 0x5c, 0x98, 0x39, 0x33, 0x55, 0x15, 0x41, 0x2d, + 0x62, 0x90, 0xef, 0xc2, 0x4a, 0x40, 0x4d, 0x90, 0x42, 0xd4, 0xfa, 0x5c, 0xdb, 0x0c, 0x49, 0x80, + 0xfa, 0x6b, 0x52, 0x54, 0x56, 0x51, 0x96, 0x85, 0x51, 0xc7, 0x6e, 0xc0, 0x32, 0xa2, 0x50, 0xfa, 0x26, 0xc5, 0x93, 0x12, 0xe2, 0x23, 0x58, 0x1d, 0x05, 0xe3, 0x89, 0xa9, 0xb2, 0x4b, 0x71, 0x2a, 0xcf, 0xd8, 0x6b, 0x48, 0xb2, 0xcf, 0x65, 0xe9, 0x7f, 0x5a, 0x84, 0x0a, 0xd5, 0x41, 0xf2, 0x76, - 0xd7, 0x3e, 0x57, 0xbc, 0xbd, 0x6a, 0x94, 0x5d, 0x1b, 0xc5, 0x82, 0x57, 0xe8, 0xca, 0x89, 0x0e, - 0x5c, 0xcc, 0xea, 0xc0, 0xe9, 0x72, 0x2e, 0xe5, 0x96, 0xf3, 0x22, 0xf5, 0xb7, 0xbc, 0x48, 0xfd, - 0xc5, 0xc9, 0xc5, 0x34, 0xce, 0xb9, 0x9b, 0xe8, 0x0b, 0x40, 0xa0, 0x36, 0x42, 0xb2, 0x9c, 0x7b, - 0x25, 0xcf, 0xb9, 0x37, 0x61, 0x85, 0x26, 0x87, 0xc7, 0x36, 0x9b, 0x8a, 0xa1, 0x92, 0x99, 0x61, + 0xd7, 0x3e, 0x57, 0xbc, 0xbd, 0x6a, 0x2c, 0xbb, 0x36, 0x8a, 0x05, 0xaf, 0xd0, 0x95, 0x13, 0x1d, + 0xb8, 0x98, 0xd5, 0x81, 0xd3, 0xe5, 0x5c, 0xca, 0x2d, 0xe7, 0x45, 0xea, 0xef, 0xf2, 0x22, 0xf5, + 0x17, 0x27, 0x17, 0xd3, 0x38, 0xe7, 0x6e, 0xa2, 0x2f, 0x00, 0x81, 0xda, 0x08, 0xc9, 0x72, 0xee, + 0x72, 0x9e, 0x73, 0x6f, 0x42, 0x99, 0x26, 0x87, 0xc7, 0x36, 0x9b, 0x8a, 0xa1, 0x92, 0x99, 0x61, 0xa8, 0xbe, 0x6a, 0x18, 0x92, 0x66, 0x5b, 0xde, 0x31, 0xdb, 0x6c, 0x54, 0xb3, 0x9b, 0xde, 0x71, 0x20, 0x3e, 0x86, 0x2b, 0x29, 0x5a, 0xb6, 0x86, 0x54, 0x66, 0x3a, 0x85, 0x30, 0x44, 0x42, 0x49, 0x2d, 0x22, 0xe5, 0xf8, 0x3e, 0xac, 0x67, 0xb2, 0x90, 0xee, 0x16, 0x11, 0xe3, 0xaf, 0x1a, 0x6b, 0x09, 0x39, 0x89, 0xd5, 0x91, 0x78, 0x17, 0xfb, 0x69, 0xe4, 0x4d, 0x6d, 0x66, 0xa3, 0xd3, 0xb1, 0x1f, 0x6d, 0xae, 0x52, 0x07, 0xaf, 0x29, 0x78, 0x8b, 0xc1, 0x5f, 0x95, 0x2a, 0x0d, 0x6d, 0xcd, - 0x68, 0xa4, 0xcb, 0xc1, 0x8c, 0x9c, 0x58, 0xff, 0x83, 0x32, 0xac, 0xee, 0x04, 0xa1, 0xe3, 0x1e, - 0xfb, 0xe9, 0xb4, 0x9d, 0xb3, 0xe5, 0xa8, 0xa9, 0xbc, 0x94, 0x99, 0xca, 0xb7, 0xa1, 0x76, 0xc4, - 0x19, 0x91, 0xe5, 0xd2, 0x7c, 0x2d, 0x19, 0x20, 0x41, 0xc3, 0x43, 0x0f, 0x59, 0xa3, 0x22, 0xa0, - 0xcc, 0x25, 0xca, 0xac, 0x32, 0xa1, 0xc4, 0x20, 0xbe, 0xa4, 0xbd, 0xd3, 0x76, 0x3c, 0x27, 0xe6, - 0xf1, 0x6d, 0x28, 0x79, 0x30, 0x57, 0xa7, 0x07, 0x86, 0x73, 0xd4, 0x24, 0x01, 0x1e, 0xb7, 0xd2, - 0x6d, 0x22, 0x97, 0x79, 0xe5, 0xbe, 0xbb, 0xfc, 0x23, 0xf3, 0x4a, 0x36, 0xfc, 0x10, 0xae, 0x84, - 0xce, 0x91, 0x13, 0x3a, 0xfe, 0xc8, 0xb1, 0xcd, 0xcc, 0xb4, 0xe5, 0x19, 0x72, 0x39, 0x45, 0x76, - 0x92, 0x09, 0xbc, 0x07, 0x5a, 0x52, 0x57, 0x93, 0x59, 0x27, 0x4d, 0x9b, 0xc6, 0xc3, 0x37, 0x7f, - 0xf0, 0xb3, 0x7d, 0x22, 0x35, 0x1a, 0xaa, 0xe2, 0x9c, 0x96, 0xc5, 0x71, 0xf5, 0x55, 0x71, 0xd5, - 0x9f, 0x54, 0x1c, 0xb7, 0x85, 0xd3, 0xfa, 0x10, 0xaa, 0x09, 0x89, 0xa8, 0x43, 0xc5, 0x68, 0x4b, - 0xcb, 0xc5, 0x25, 0x51, 0x83, 0x95, 0x56, 0x73, 0xd0, 0x6a, 0x6e, 0x23, 0x03, 0xab, 0x43, 0x65, + 0x68, 0xa4, 0xcb, 0xc1, 0x8c, 0x9c, 0x58, 0xff, 0x83, 0x65, 0x58, 0xdd, 0x09, 0x42, 0xc7, 0x3d, + 0xf6, 0xd3, 0x69, 0x3b, 0x67, 0xcb, 0x51, 0x53, 0x79, 0x29, 0x33, 0x95, 0x6f, 0x43, 0xed, 0x88, + 0x33, 0x22, 0xcb, 0xa5, 0xf9, 0x5a, 0x32, 0x40, 0x82, 0x86, 0x87, 0x1e, 0xb2, 0x46, 0x45, 0x40, + 0x99, 0x4b, 0x94, 0x59, 0x65, 0x42, 0x89, 0x41, 0x7c, 0x49, 0x7b, 0xa7, 0xed, 0x78, 0x4e, 0xcc, + 0xe3, 0xdb, 0x50, 0xf2, 0x60, 0xae, 0x4e, 0x0f, 0x0c, 0xe7, 0xa8, 0x49, 0x02, 0x3c, 0x6e, 0xa5, + 0xdb, 0x44, 0x2e, 0xf3, 0xca, 0x7d, 0x77, 0xe5, 0x47, 0xe6, 0x95, 0x6c, 0xf8, 0x21, 0x5c, 0x09, + 0x9d, 0x23, 0x27, 0x74, 0xfc, 0x91, 0x63, 0x9b, 0x99, 0x69, 0xcb, 0x33, 0xe4, 0x72, 0x8a, 0xec, + 0x24, 0x13, 0x78, 0x0f, 0xb4, 0xa4, 0xae, 0x26, 0xb3, 0x4e, 0x9a, 0x36, 0x8d, 0x87, 0x6f, 0xfe, + 0xe0, 0x67, 0xfb, 0x44, 0x6a, 0x34, 0x54, 0xc5, 0x39, 0x2d, 0x8b, 0xe3, 0xea, 0xab, 0xe2, 0xaa, + 0x3f, 0xa9, 0x38, 0x6e, 0x0b, 0xa7, 0xf5, 0x21, 0x54, 0x13, 0x12, 0x51, 0x87, 0x8a, 0xd1, 0x96, + 0x96, 0x8b, 0x4b, 0xa2, 0x06, 0xe5, 0x56, 0x73, 0xd0, 0x6a, 0x6e, 0x23, 0x03, 0xab, 0x43, 0x65, 0xd0, 0x1e, 0xb2, 0xb5, 0x62, 0x49, 0xac, 0x41, 0x0d, 0x53, 0xdb, 0xed, 0x9d, 0xe6, 0x41, 0x77, 0xa8, 0x15, 0xc5, 0x2a, 0x54, 0x7b, 0x7d, 0xb3, 0xd9, 0x1a, 0x76, 0xfa, 0x3d, 0xad, 0xa4, 0x7b, 0xb0, 0x36, 0xf3, 0x61, 0xe4, 0x9a, 0x8c, 0x36, 0xfb, 0x46, 0xe7, 0x49, 0xa7, 0x67, 0xb6, 0xbf, @@ -17398,913 +17416,915 @@ var fileDescriptor_2d655ab2f7683c23 = []byte{ 0xb5, 0xfd, 0x30, 0x98, 0x38, 0x61, 0x4c, 0xc5, 0x6a, 0x50, 0x3c, 0x75, 0x2e, 0x64, 0xa9, 0xf8, 0x33, 0x3d, 0x68, 0x58, 0xca, 0x1e, 0x34, 0x3c, 0x84, 0x8a, 0xca, 0xf6, 0xa3, 0xf3, 0xfc, 0x1c, 0xb7, 0x36, 0xca, 0xe3, 0x3a, 0x11, 0x7e, 0xec, 0x01, 0xc0, 0x24, 0x01, 0xc8, 0x8e, 0x53, 0xfa, - 0xbe, 0x2c, 0xdc, 0xc8, 0x50, 0xe8, 0xaf, 0xc3, 0xca, 0x33, 0xd7, 0x39, 0x93, 0xcd, 0x7f, 0xe1, - 0x3a, 0x67, 0xaa, 0xf9, 0xf8, 0x5b, 0xff, 0x87, 0x00, 0x15, 0xe2, 0x7f, 0xdb, 0x2f, 0x3f, 0xdb, + 0xbe, 0x2c, 0xdc, 0xc8, 0x50, 0xe8, 0xaf, 0x43, 0xf9, 0x99, 0xeb, 0x9c, 0xc9, 0xe6, 0xbf, 0x70, + 0x9d, 0x33, 0xd5, 0x7c, 0xfc, 0xad, 0xff, 0x17, 0x00, 0x15, 0xe2, 0x7f, 0xdb, 0x2f, 0x3f, 0xdb, 0xf9, 0x29, 0xaa, 0xc3, 0x1d, 0xc9, 0xae, 0x4a, 0x0b, 0x14, 0x16, 0x66, 0x5e, 0xaf, 0x03, 0x64, 0x78, 0x31, 0xef, 0x2c, 0xd5, 0x38, 0x61, 0xc1, 0x28, 0x73, 0x93, 0x14, 0x83, 0xd3, 0x88, 0xcd, 0xae, 0x29, 0x40, 0x3c, 0x60, 0x89, 0x98, 0x0c, 0xad, 0xac, 0x35, 0x5c, 0x56, 0x5a, 0xfd, 0xa1, 0xe7, 0x28, 0x5b, 0x03, 0x89, 0xc9, 0x98, 0xa0, 0x7d, 0xc6, 0x09, 0x23, 0xdc, 0x4e, 0xe4, 0xb1, 0xbf, 0x4c, 0x8a, 0x77, 0xa0, 0x84, 0x9b, 0xb0, 0xb4, 0x0d, 0x5c, 0x56, 0x3d, 0x98, 0x91, 0x22, - 0x0c, 0x22, 0x10, 0xf7, 0x60, 0x85, 0xb8, 0x94, 0x83, 0x3b, 0x41, 0xa6, 0xb7, 0xd5, 0xa6, 0x6c, - 0x28, 0xb4, 0x78, 0x17, 0xca, 0x47, 0xa7, 0xce, 0x05, 0x6f, 0x03, 0x49, 0x99, 0x39, 0x66, 0x62, + 0x0c, 0x22, 0x10, 0xf7, 0xa0, 0x4c, 0x5c, 0xca, 0xc1, 0x9d, 0x20, 0xd3, 0xdb, 0x6a, 0x53, 0x36, + 0x14, 0x5a, 0xbc, 0x0b, 0xcb, 0x47, 0xa7, 0xce, 0x05, 0x6f, 0x03, 0x49, 0x99, 0x39, 0x66, 0x62, 0x30, 0x85, 0xb8, 0x0b, 0x8d, 0xd0, 0x39, 0x32, 0xe9, 0xb4, 0x07, 0x79, 0x78, 0xb4, 0xd9, 0x20, - 0x16, 0x5d, 0x0f, 0x9d, 0xa3, 0x16, 0x02, 0x87, 0x87, 0x5e, 0x24, 0xde, 0x86, 0x65, 0x5a, 0x5c, + 0x16, 0x5d, 0x0f, 0x9d, 0xa3, 0x16, 0x02, 0x87, 0x87, 0x5e, 0x24, 0xde, 0x86, 0x15, 0x5a, 0x5c, 0xa8, 0x30, 0x64, 0xbe, 0xac, 0x56, 0xaa, 0x21, 0xb1, 0xe2, 0x63, 0x00, 0xa9, 0x96, 0x98, 0x87, - 0x17, 0x24, 0x7c, 0x27, 0x8b, 0x29, 0x3b, 0xff, 0xb3, 0xca, 0xcb, 0x3b, 0x50, 0xc6, 0x49, 0x12, - 0x6d, 0x5e, 0xa3, 0x92, 0xd7, 0xf3, 0x33, 0x88, 0x6a, 0x4a, 0x78, 0x71, 0x0f, 0x2a, 0x38, 0x51, - 0x68, 0x55, 0x6f, 0x66, 0xf5, 0x34, 0x39, 0xab, 0x70, 0xe7, 0x76, 0xce, 0x06, 0xdf, 0x79, 0xe2, - 0x3e, 0x94, 0x6c, 0x5c, 0xcc, 0xd7, 0xa9, 0xc4, 0xab, 0x99, 0x71, 0x41, 0x2e, 0xba, 0xed, 0x1c, - 0x91, 0xea, 0x48, 0x34, 0x62, 0x17, 0x1a, 0x38, 0x8d, 0x1e, 0x92, 0x30, 0x86, 0xdd, 0xb7, 0x79, - 0x83, 0x72, 0xbd, 0x31, 0x93, 0xab, 0x27, 0x89, 0xa8, 0xb3, 0xdb, 0x7e, 0x1c, 0x5e, 0x18, 0xab, - 0x7e, 0x16, 0x26, 0x6e, 0x40, 0xc5, 0x8d, 0xba, 0xc1, 0xe8, 0x34, 0x39, 0x58, 0x4e, 0xd2, 0xe2, - 0x0b, 0x58, 0xa5, 0x89, 0x85, 0x49, 0xfc, 0xb8, 0x3c, 0x55, 0xce, 0x4e, 0x19, 0x85, 0x32, 0xf2, - 0x94, 0xb8, 0x83, 0xba, 0x91, 0x19, 0x3b, 0xe3, 0x49, 0x10, 0xa2, 0x86, 0xf7, 0xba, 0x3a, 0xa1, - 0x18, 0x2a, 0x10, 0x0a, 0x4a, 0x89, 0x47, 0x8b, 0x19, 0x1c, 0x1d, 0x45, 0x4e, 0x4c, 0xa7, 0xc8, - 0x25, 0xa3, 0xa1, 0x1c, 0x5b, 0xfa, 0x04, 0x25, 0x41, 0x25, 0x32, 0xed, 0x0b, 0xdf, 0x1a, 0xbb, - 0xa3, 0xcd, 0xdb, 0xac, 0x48, 0xba, 0xd1, 0x36, 0x03, 0xb2, 0xba, 0xdc, 0x9d, 0x9c, 0x2e, 0x77, - 0x19, 0xca, 0xf6, 0x21, 0x2e, 0xc7, 0x37, 0xa8, 0xd8, 0x92, 0x7d, 0xd8, 0xb1, 0xc5, 0x07, 0x64, - 0x65, 0x66, 0x16, 0xb8, 0xa9, 0x67, 0xad, 0x71, 0x09, 0x67, 0x34, 0x52, 0x0a, 0x71, 0x07, 0x6a, - 0x3b, 0x8e, 0x15, 0x4f, 0x43, 0x67, 0xc7, 0xb3, 0x8e, 0x37, 0xdf, 0xa4, 0x92, 0xb2, 0x20, 0xac, - 0x9d, 0x17, 0x1c, 0xbb, 0x23, 0x8b, 0x56, 0xfe, 0x5d, 0x96, 0x8b, 0x25, 0xa4, 0x63, 0xa7, 0x2a, - 0x8c, 0x25, 0x85, 0xdd, 0xb7, 0xb2, 0x2a, 0x8c, 0xe5, 0xa9, 0xe3, 0x98, 0xb4, 0x2f, 0x9c, 0x49, - 0x30, 0x3a, 0xd9, 0x7c, 0x9b, 0xd6, 0xdb, 0xaa, 0xea, 0x8a, 0x36, 0x02, 0xc5, 0x3b, 0xb0, 0x26, - 0xd5, 0x7a, 0x53, 0xb9, 0xe3, 0xbc, 0x43, 0x74, 0x0d, 0x09, 0x6e, 0x49, 0xaf, 0x1c, 0x1d, 0x56, - 0xb9, 0x40, 0xdb, 0x1c, 0x59, 0xa3, 0x13, 0x67, 0xf3, 0x1e, 0x57, 0x9c, 0x8a, 0xb3, 0x5b, 0x08, - 0xba, 0xf1, 0x84, 0x14, 0x4a, 0x1a, 0xae, 0x4f, 0x67, 0xb8, 0x62, 0x6e, 0x4d, 0x67, 0xd8, 0xe7, - 0xee, 0xa5, 0x2c, 0x73, 0xdc, 0x2a, 0xd3, 0xf6, 0x71, 0xe3, 0x17, 0x20, 0xe6, 0x27, 0xda, 0xab, - 0x58, 0x74, 0x59, 0xb2, 0xe8, 0x2f, 0x97, 0x1e, 0x17, 0xf4, 0xff, 0xa1, 0x00, 0xab, 0x39, 0x16, - 0xb4, 0x70, 0xaf, 0x61, 0x41, 0xdc, 0x1a, 0x4b, 0x43, 0x16, 0x27, 0x94, 0xfa, 0xe7, 0xfa, 0xc7, - 0x9e, 0x93, 0x51, 0xff, 0x28, 0x2d, 0x7e, 0x0e, 0x97, 0x8f, 0xa6, 0x9e, 0x17, 0x3b, 0xe7, 0xb1, - 0x32, 0xeb, 0x87, 0xce, 0x91, 0xb4, 0xe3, 0xce, 0x1d, 0x32, 0xad, 0x2b, 0x5a, 0x36, 0x6d, 0x19, - 0xce, 0x91, 0xf8, 0x6d, 0x10, 0x49, 0x01, 0x2c, 0x6d, 0x61, 0xfe, 0xf2, 0xe2, 0xfc, 0x9a, 0x22, - 0xa5, 0x6e, 0x30, 0x9c, 0x23, 0xfd, 0xbf, 0x29, 0x42, 0x7d, 0xd7, 0x8a, 0x4e, 0xf6, 0xac, 0xc9, - 0x20, 0xb6, 0xe2, 0x08, 0x17, 0xc6, 0x89, 0x15, 0x9d, 0x8c, 0xad, 0x09, 0x2b, 0x8b, 0x05, 0xb6, - 0x38, 0x4a, 0x18, 0x69, 0x8a, 0x37, 0xa0, 0x82, 0xc9, 0xbe, 0xbf, 0xff, 0x54, 0x9a, 0xff, 0x92, - 0x34, 0x32, 0xe4, 0xe8, 0x64, 0x7a, 0x74, 0x94, 0x34, 0x55, 0x25, 0xc5, 0x5d, 0x58, 0x95, 0x3f, - 0x49, 0x1f, 0x3b, 0x97, 0xa7, 0x1d, 0x79, 0xa0, 0x78, 0x04, 0x35, 0x09, 0x18, 0xaa, 0xed, 0xa3, - 0x91, 0x98, 0x89, 0x53, 0x84, 0x91, 0xa5, 0x12, 0xbf, 0x84, 0x2b, 0x99, 0xe4, 0x4e, 0x10, 0xee, - 0x4d, 0xbd, 0xd8, 0x6d, 0xf5, 0xa4, 0xec, 0xfa, 0xda, 0x5c, 0xf6, 0x94, 0xc4, 0x58, 0x9c, 0x33, - 0x5f, 0xdb, 0x3d, 0xd7, 0xa7, 0xdd, 0xa8, 0x68, 0xe4, 0x81, 0x33, 0x54, 0xd6, 0x39, 0x6d, 0x42, - 0x79, 0x2a, 0xeb, 0x1c, 0xd9, 0x94, 0x04, 0xec, 0x39, 0xf1, 0x49, 0x60, 0x4b, 0x61, 0xf4, 0x72, - 0xae, 0x5a, 0x8c, 0x32, 0xf2, 0x94, 0xd8, 0x9d, 0xfe, 0xd4, 0xf3, 0x46, 0x7e, 0x4c, 0xfa, 0x4f, - 0xd1, 0x50, 0x49, 0xdc, 0xa0, 0x43, 0xcb, 0x3f, 0x76, 0xa2, 0xcd, 0xda, 0x9d, 0xe2, 0xbd, 0x82, - 0x21, 0x53, 0xfa, 0xdf, 0x5e, 0x82, 0x32, 0x8f, 0xe4, 0x6b, 0x50, 0x3d, 0xa4, 0x33, 0x6c, 0x7f, - 0x3a, 0x56, 0x67, 0xcd, 0x04, 0xe8, 0x4d, 0xc7, 0xac, 0x76, 0x48, 0xf3, 0x6d, 0xc1, 0xa0, 0xdf, - 0x58, 0x64, 0x30, 0x8d, 0xf1, 0x5b, 0x45, 0x82, 0xca, 0x14, 0x56, 0x22, 0x0c, 0xce, 0x68, 0x36, - 0x94, 0x08, 0xa1, 0x92, 0x74, 0x9c, 0x4d, 0x7b, 0x3d, 0x66, 0x2a, 0x13, 0xae, 0x42, 0x80, 0x96, - 0x1f, 0xcf, 0x9a, 0xae, 0x97, 0xe7, 0x4d, 0xd7, 0xb7, 0x00, 0x95, 0x9a, 0x91, 0xd3, 0xf7, 0x9d, - 0x56, 0x4f, 0x9e, 0x2b, 0x65, 0x20, 0xb8, 0x42, 0xed, 0x60, 0x22, 0x0f, 0x8c, 0xf0, 0xa7, 0xf8, - 0x2c, 0x99, 0x9d, 0xd4, 0x46, 0xa9, 0x43, 0xca, 0xbd, 0x30, 0x3b, 0x8f, 0x8d, 0x1c, 0x1d, 0x96, - 0x84, 0x1b, 0x1c, 0xeb, 0x90, 0xf8, 0x53, 0x6f, 0x03, 0x18, 0xc1, 0x59, 0xe4, 0xc4, 0x74, 0x46, - 0x73, 0x8d, 0x9a, 0x98, 0xf3, 0x5c, 0x09, 0xce, 0xf6, 0x83, 0x28, 0x31, 0x00, 0xcd, 0xcb, 0xc5, - 0x04, 0xd7, 0x3f, 0x84, 0x15, 0x94, 0x7e, 0xac, 0xd8, 0x12, 0x77, 0xa5, 0x15, 0x9b, 0x65, 0x36, - 0x4d, 0x9d, 0x9f, 0xaa, 0x6f, 0x48, 0x63, 0x78, 0x57, 0x7d, 0x97, 0xf2, 0xbc, 0x91, 0x31, 0x64, - 0x24, 0x3b, 0xaf, 0x2c, 0x50, 0xca, 0x53, 0xaf, 0x41, 0x15, 0xab, 0x46, 0x47, 0xf4, 0xea, 0xd4, - 0x31, 0x0c, 0xce, 0x5a, 0x98, 0xd6, 0xff, 0x49, 0x01, 0x6a, 0xfd, 0xd0, 0xc6, 0x2d, 0x7f, 0x30, - 0x71, 0x46, 0xaf, 0xb4, 0x57, 0xa1, 0xf4, 0x15, 0x78, 0x9e, 0x45, 0x9b, 0x8b, 0x34, 0x24, 0x24, - 0x00, 0xf1, 0x31, 0x94, 0x8e, 0x70, 0x13, 0x29, 0x66, 0x55, 0xbe, 0x4c, 0xf1, 0xea, 0x37, 0x6e, - 0x2b, 0x06, 0x91, 0xea, 0xbf, 0x9b, 0x7c, 0x9f, 0xf6, 0x9a, 0xec, 0xf1, 0xef, 0x25, 0x72, 0x3f, - 0x19, 0xb4, 0xb4, 0x82, 0xa8, 0x40, 0x69, 0xbb, 0x3d, 0x68, 0xb1, 0x5a, 0x84, 0x0a, 0xd2, 0xc0, - 0xdc, 0xe9, 0x18, 0x83, 0xa1, 0x56, 0x22, 0x7f, 0x16, 0x02, 0x74, 0x9b, 0x83, 0xa1, 0x56, 0x11, - 0x00, 0xcb, 0x07, 0xbd, 0xce, 0x2f, 0x0f, 0xda, 0x9a, 0xa6, 0xff, 0xe3, 0x02, 0x40, 0x7a, 0x7e, - 0x24, 0xde, 0x83, 0xda, 0x19, 0xa5, 0xcc, 0xcc, 0x41, 0x7c, 0xb6, 0x8d, 0xc0, 0x68, 0x92, 0x0c, - 0x3f, 0x80, 0x7a, 0xb2, 0x49, 0xa2, 0xd4, 0x34, 0x7f, 0x22, 0x5f, 0x4b, 0xf0, 0x5b, 0x17, 0xe2, - 0x7d, 0xa8, 0x04, 0xd8, 0x0e, 0x24, 0x2d, 0x66, 0x45, 0xa6, 0x4c, 0xf3, 0x8d, 0x95, 0x80, 0x13, - 0x28, 0x5d, 0x1d, 0x85, 0xca, 0x30, 0x94, 0x90, 0xee, 0x20, 0xa8, 0xe5, 0x59, 0xd3, 0xc8, 0x31, - 0x18, 0x9f, 0xec, 0x12, 0xe5, 0x74, 0x97, 0xd0, 0x7f, 0x05, 0x8d, 0x81, 0x35, 0x9e, 0xf0, 0x5e, - 0x42, 0x0d, 0x13, 0x50, 0xc2, 0x39, 0x21, 0xa7, 0x1e, 0xfd, 0xc6, 0x45, 0xb7, 0xef, 0x84, 0x23, - 0xc7, 0x57, 0x6b, 0x54, 0x25, 0x91, 0xfd, 0x1e, 0xe0, 0x6e, 0x62, 0x04, 0x67, 0x6a, 0x3b, 0x51, - 0x69, 0xfd, 0xdf, 0x2e, 0x40, 0x2d, 0x53, 0x0d, 0xf1, 0x21, 0x94, 0x48, 0x0c, 0x2f, 0x64, 0x19, - 0x61, 0x86, 0x80, 0x7f, 0xb3, 0xe0, 0x86, 0x84, 0xe2, 0x6d, 0x28, 0x47, 0xb1, 0x15, 0xaa, 0xa3, - 0x48, 0x2d, 0x93, 0x63, 0x2b, 0x98, 0xfa, 0xb6, 0xc1, 0x68, 0xa1, 0x43, 0xd1, 0xf1, 0x6d, 0x69, - 0x4a, 0x9b, 0xa7, 0x42, 0xa4, 0x7e, 0x07, 0xaa, 0x49, 0xf1, 0x38, 0x05, 0x8c, 0xfe, 0xf3, 0x81, - 0x76, 0x49, 0x54, 0xa1, 0x6c, 0x34, 0x7b, 0x4f, 0xda, 0x5a, 0x41, 0xff, 0xbb, 0x05, 0x80, 0x34, - 0x97, 0x78, 0x90, 0xab, 0xed, 0x8d, 0xd9, 0x52, 0x1f, 0xd0, 0xdf, 0x4c, 0x65, 0x6f, 0x42, 0x75, - 0xea, 0x13, 0xd0, 0xb1, 0xe5, 0x4e, 0x94, 0x02, 0x50, 0x77, 0x54, 0xbe, 0xb5, 0x33, 0xba, 0xe3, - 0x0b, 0xcb, 0xd3, 0xbf, 0x84, 0x6a, 0x52, 0x1c, 0xea, 0xe6, 0x3b, 0xfd, 0x6e, 0xb7, 0xff, 0xbc, - 0xd3, 0x7b, 0xa2, 0x5d, 0xc2, 0xe4, 0xbe, 0xd1, 0x6e, 0xb5, 0xb7, 0x31, 0x59, 0xc0, 0x39, 0xdb, - 0x3a, 0x30, 0x8c, 0x76, 0x6f, 0x68, 0x1a, 0xfd, 0xe7, 0xda, 0x92, 0xfe, 0xfb, 0x05, 0x58, 0xed, - 0x7b, 0x76, 0x2b, 0xf0, 0x5a, 0xd6, 0x04, 0xe5, 0x2c, 0xf1, 0x73, 0x58, 0x3f, 0x9c, 0xa2, 0xac, - 0x3f, 0xf1, 0xac, 0x91, 0x73, 0x12, 0x78, 0xb6, 0xa3, 0x16, 0x61, 0xee, 0xb0, 0x55, 0x9e, 0x9d, - 0x68, 0x44, 0xbc, 0x9f, 0xd2, 0x8a, 0x4f, 0xa1, 0x3e, 0x09, 0x83, 0x43, 0x47, 0x1d, 0x64, 0xcd, - 0x2a, 0xa9, 0x69, 0xde, 0x1a, 0xd1, 0xc9, 0x43, 0xe6, 0xff, 0xb2, 0x0c, 0xf5, 0x6d, 0xc7, 0x9e, - 0x4e, 0xbe, 0x0a, 0x5c, 0xbf, 0x15, 0x9f, 0x8b, 0x4f, 0xa0, 0x1e, 0x78, 0x64, 0xb0, 0x32, 0x33, - 0x6e, 0x73, 0x8b, 0xca, 0x81, 0x80, 0x5a, 0x40, 0x4e, 0x76, 0x1f, 0xc0, 0x65, 0x69, 0x2e, 0x61, - 0xeb, 0xe9, 0x39, 0x67, 0xc6, 0x35, 0x53, 0x36, 0x34, 0x46, 0xf1, 0x06, 0x4d, 0xe4, 0xbf, 0x05, - 0x1b, 0x19, 0x72, 0x64, 0x2c, 0x4c, 0x5f, 0x9c, 0x5b, 0x63, 0xeb, 0x49, 0xde, 0xc4, 0xa1, 0xef, - 0x2b, 0xd8, 0x50, 0x35, 0x1c, 0x71, 0xef, 0x71, 0xe6, 0x52, 0x56, 0xa9, 0xca, 0xf5, 0xae, 0xac, - 0xf0, 0x7a, 0x90, 0x05, 0x52, 0x59, 0x1f, 0xc3, 0x15, 0x1b, 0x5b, 0x6f, 0x72, 0xe7, 0x9f, 0x3a, - 0xce, 0xc4, 0xf4, 0x32, 0xde, 0x2e, 0x84, 0xdc, 0x42, 0xdc, 0x53, 0xc7, 0x99, 0x74, 0xad, 0x28, - 0x16, 0x8f, 0x61, 0xdd, 0x3a, 0x3a, 0x72, 0x46, 0xb1, 0x63, 0x9b, 0xb8, 0x8d, 0x2d, 0xb4, 0xd2, - 0xa2, 0xac, 0xb4, 0xa6, 0xc8, 0x70, 0x39, 0xb6, 0x02, 0x4f, 0x3c, 0x81, 0xeb, 0x93, 0x93, 0x8b, - 0x88, 0x04, 0xe9, 0xd1, 0x09, 0xee, 0xb6, 0x99, 0x12, 0x56, 0x16, 0x94, 0x70, 0x55, 0x91, 0xb7, - 0x98, 0x5a, 0x15, 0xf4, 0x18, 0xae, 0xab, 0xee, 0x43, 0xfd, 0x2e, 0xdf, 0xe7, 0x15, 0xea, 0xf3, - 0x2b, 0xb2, 0xdf, 0x10, 0x9f, 0xe9, 0xf8, 0x7b, 0xa0, 0xb1, 0xfb, 0xd6, 0x11, 0x4e, 0x5d, 0xfa, - 0xba, 0x74, 0x69, 0x6f, 0x10, 0x7c, 0x87, 0x16, 0x1f, 0xb2, 0x8f, 0x7b, 0xa0, 0x39, 0x63, 0x37, - 0x36, 0x2d, 0x92, 0x56, 0x99, 0x92, 0x4f, 0xc5, 0x1a, 0x08, 0x97, 0x66, 0x3b, 0xa4, 0xfc, 0x0c, - 0x34, 0x49, 0x74, 0x44, 0x3a, 0x00, 0xb6, 0xa6, 0xb6, 0xa0, 0x35, 0x0d, 0xa6, 0xda, 0x41, 0x22, - 0x6c, 0xc5, 0x1e, 0x08, 0x65, 0x83, 0x24, 0xcb, 0x0a, 0x2b, 0xb2, 0xac, 0x42, 0x4b, 0x77, 0xa5, - 0xfe, 0xf6, 0xd3, 0x83, 0x54, 0x3d, 0xa6, 0xc6, 0xa8, 0x05, 0x70, 0x94, 0x07, 0x47, 0xfa, 0x7f, - 0xb1, 0x0c, 0x55, 0x36, 0xbb, 0xe1, 0x34, 0xbe, 0x07, 0x2b, 0xc1, 0xe1, 0xb7, 0x24, 0xca, 0xbe, - 0xc4, 0xdf, 0x6a, 0x39, 0x38, 0xfc, 0x16, 0xe5, 0xdf, 0xf7, 0x94, 0x08, 0x62, 0x3b, 0x47, 0x72, - 0xd5, 0x34, 0xf2, 0x4a, 0xa6, 0x14, 0x49, 0xb6, 0x9d, 0x23, 0x94, 0x2e, 0x5d, 0x3f, 0x72, 0xc2, - 0x98, 0xcd, 0xa6, 0x2b, 0x2f, 0x5f, 0x1c, 0x4c, 0x46, 0x96, 0xd4, 0x47, 0x50, 0x93, 0xa6, 0x49, - 0xca, 0x54, 0x79, 0x79, 0x26, 0x26, 0xa3, 0x4c, 0x5f, 0x40, 0x23, 0xdd, 0x7e, 0x28, 0x5f, 0xf5, - 0xa5, 0xf9, 0x56, 0x13, 0x4a, 0xca, 0xfa, 0x10, 0xae, 0x46, 0xa7, 0xee, 0xc4, 0x94, 0x35, 0x0d, - 0x7c, 0x3a, 0x5e, 0x35, 0x27, 0xa7, 0x72, 0x00, 0x05, 0x62, 0x3b, 0x84, 0xec, 0xfb, 0xbd, 0xa9, - 0xe7, 0xed, 0x9f, 0x8a, 0x77, 0x61, 0x5d, 0x92, 0x4f, 0x92, 0xf9, 0x44, 0xa3, 0x58, 0x36, 0x1a, - 0x8c, 0xd8, 0x97, 0xf3, 0x48, 0x7c, 0x0e, 0x9b, 0x3c, 0x87, 0x64, 0xa3, 0x78, 0x9a, 0xf3, 0x0c, - 0xe1, 0x33, 0xd0, 0x2b, 0x84, 0x67, 0x93, 0x6a, 0x93, 0xb0, 0x34, 0x51, 0x3e, 0x82, 0x0d, 0xf7, - 0xd8, 0x0f, 0x42, 0x95, 0x45, 0x4e, 0x7f, 0x79, 0x38, 0x2a, 0x18, 0xd7, 0xcc, 0x2c, 0x1a, 0x6c, - 0x89, 0x5c, 0x9e, 0x17, 0x66, 0x4c, 0x6e, 0x19, 0x98, 0x03, 0xb5, 0xd0, 0x46, 0x76, 0x7d, 0x5e, - 0xb0, 0xcb, 0x86, 0x11, 0x9c, 0x75, 0x6c, 0x5c, 0xd2, 0x92, 0x54, 0xad, 0x91, 0xf8, 0x9c, 0x5a, - 0xb3, 0x46, 0xad, 0x11, 0x8c, 0x4c, 0x66, 0x0a, 0xb6, 0xe8, 0x17, 0x20, 0xe6, 0x96, 0x74, 0xb4, - 0xa9, 0xbd, 0xb4, 0xbf, 0xb5, 0x99, 0x95, 0x4d, 0x6b, 0x60, 0x6e, 0x45, 0xaf, 0x2f, 0x5a, 0x03, - 0xa3, 0xfc, 0x4a, 0x6e, 0xc3, 0x66, 0xfe, 0xcb, 0x67, 0x8e, 0x7b, 0x7c, 0x42, 0xb3, 0x4b, 0xde, - 0x16, 0xc8, 0xe7, 0xbf, 0x92, 0xfd, 0xf2, 0x73, 0xa2, 0x7d, 0x25, 0x67, 0xb9, 0xfc, 0xe3, 0x39, - 0x8b, 0xfe, 0xef, 0x15, 0xa0, 0xca, 0x33, 0x03, 0x17, 0xd1, 0x1b, 0x50, 0xfc, 0x81, 0x05, 0x84, - 0x38, 0x71, 0x1f, 0xd6, 0x2d, 0xdb, 0x9e, 0x19, 0x50, 0xde, 0x49, 0xd7, 0x2c, 0xdb, 0xce, 0x8d, - 0xe6, 0x3d, 0xd0, 0xdc, 0xc8, 0x54, 0x86, 0xa8, 0xd4, 0x27, 0xb3, 0x62, 0x34, 0xdc, 0x48, 0xda, - 0xa1, 0xf8, 0x54, 0x32, 0xb7, 0x26, 0x4b, 0x3f, 0xbc, 0x26, 0x75, 0x01, 0x60, 0x38, 0xb4, 0x6d, - 0xb6, 0xe2, 0xf3, 0xaf, 0x4a, 0x95, 0x82, 0x06, 0xfa, 0x7f, 0x55, 0x83, 0x5a, 0xd3, 0xb7, 0xbc, - 0x8b, 0xef, 0x1d, 0x72, 0x28, 0xa5, 0x93, 0xa0, 0xc9, 0x54, 0xce, 0x52, 0xf6, 0xb0, 0xa8, 0x12, - 0x84, 0x6a, 0x76, 0x1b, 0x6a, 0xc1, 0x34, 0x4e, 0xf0, 0xec, 0x73, 0x01, 0x0c, 0x22, 0x82, 0x24, - 0x7f, 0x72, 0xfe, 0xa9, 0xf2, 0x93, 0x42, 0x9b, 0xe6, 0x4f, 0x94, 0x9c, 0x24, 0x3f, 0x11, 0xbc, - 0x09, 0xab, 0xb1, 0x3b, 0x76, 0xc8, 0xe5, 0x65, 0x3a, 0x76, 0x6c, 0xbe, 0x7c, 0xc3, 0xf7, 0x39, - 0x5a, 0x12, 0x86, 0xa5, 0x8c, 0x9d, 0x71, 0x10, 0x5e, 0x70, 0x29, 0xcb, 0x5c, 0x0a, 0x83, 0xa8, - 0x94, 0xf7, 0x41, 0x9c, 0x59, 0x6e, 0x6c, 0xe6, 0x8b, 0x62, 0xc5, 0x52, 0x43, 0xcc, 0x30, 0x5b, - 0xdc, 0x55, 0x58, 0xb6, 0xdd, 0xe8, 0xb4, 0xd3, 0x97, 0x4a, 0xa5, 0x4c, 0x61, 0x5b, 0xa2, 0x91, - 0x85, 0x32, 0x6d, 0xec, 0x30, 0xf7, 0x2f, 0x1a, 0x55, 0x84, 0x6c, 0x21, 0x00, 0x65, 0x22, 0xdf, - 0x89, 0xcf, 0x82, 0x10, 0x73, 0xb2, 0xce, 0x98, 0x02, 0x50, 0x76, 0x44, 0x52, 0xfc, 0x10, 0xb1, - 0x87, 0xa2, 0x91, 0xa4, 0x51, 0x1b, 0x63, 0x56, 0x41, 0xd8, 0x3a, 0x57, 0x3f, 0x85, 0x88, 0xbb, - 0xd0, 0xa0, 0xea, 0x93, 0x4e, 0x89, 0x6d, 0xa0, 0x95, 0x5f, 0x34, 0xea, 0x08, 0x25, 0xc3, 0x1a, - 0x52, 0x7d, 0x01, 0xd7, 0x73, 0xed, 0x33, 0xad, 0x30, 0xb4, 0x2e, 0xcc, 0xb1, 0xf5, 0x6d, 0x10, - 0x92, 0x11, 0xb4, 0x68, 0x5c, 0xcd, 0x76, 0x5b, 0x13, 0xd1, 0x7b, 0x88, 0x7d, 0x69, 0x56, 0xd7, - 0x0f, 0x42, 0xb2, 0x90, 0x2e, 0xcc, 0x8a, 0x58, 0x32, 0xe7, 0xd1, 0x00, 0x93, 0x82, 0x1b, 0xf1, - 0x3d, 0x20, 0xa3, 0x46, 0xb0, 0x2d, 0x02, 0xa1, 0x8a, 0x17, 0x3d, 0xe2, 0x3d, 0x76, 0x5d, 0x7a, - 0xb3, 0x3f, 0xa2, 0x4d, 0x95, 0x11, 0x27, 0x8e, 0x65, 0xd3, 0x9a, 0x25, 0xc4, 0xae, 0x63, 0x91, - 0x93, 0x56, 0xf4, 0xc8, 0x9c, 0x4c, 0x63, 0xbe, 0xc0, 0x63, 0x94, 0xa3, 0x47, 0xfb, 0xd3, 0x58, - 0x82, 0x8f, 0x9d, 0x98, 0x3c, 0x2b, 0x08, 0xfc, 0x84, 0xef, 0x99, 0x45, 0x8f, 0xd4, 0x81, 0xdb, - 0x15, 0xd9, 0xb7, 0x8f, 0xe4, 0x89, 0x9a, 0x0e, 0xab, 0x09, 0xd2, 0x1c, 0x4f, 0xf9, 0xc6, 0x4e, - 0xd1, 0xa8, 0x29, 0x82, 0xbd, 0xa9, 0x87, 0x03, 0x4b, 0xa6, 0x30, 0x33, 0xc4, 0xaa, 0x5c, 0xe3, - 0xa1, 0x23, 0x88, 0x81, 0xb5, 0x79, 0x0d, 0x38, 0x61, 0x9e, 0xb8, 0x31, 0x59, 0x6a, 0x8b, 0x46, - 0x85, 0x00, 0xbb, 0x6e, 0x8c, 0xeb, 0x98, 0x91, 0x72, 0x06, 0x52, 0x11, 0xd7, 0x89, 0x68, 0x8d, - 0x10, 0x7b, 0x04, 0xa7, 0x82, 0x50, 0x88, 0xc8, 0xd2, 0x62, 0x79, 0x37, 0x88, 0xb4, 0x91, 0x21, - 0xc5, 0x52, 0xdf, 0x06, 0xce, 0x6c, 0xe2, 0xd4, 0xe3, 0x32, 0x5f, 0x63, 0x03, 0x07, 0x81, 0xb7, - 0xdd, 0xe8, 0x94, 0x4a, 0xbc, 0x0b, 0x8d, 0x0c, 0x1d, 0x96, 0x77, 0x93, 0x67, 0x46, 0x42, 0x96, - 0xab, 0x63, 0xe8, 0x8c, 0x83, 0x58, 0x36, 0xf3, 0xf5, 0x4c, 0x1d, 0x0d, 0x82, 0xe7, 0xeb, 0x28, - 0x69, 0xb1, 0xcc, 0x5b, 0x99, 0x3a, 0x32, 0x29, 0x96, 0xfa, 0x06, 0xd4, 0xcf, 0x42, 0x37, 0x8e, - 0x1d, 0x29, 0xe4, 0xdc, 0xe6, 0x8e, 0x95, 0x30, 0x5a, 0xfd, 0x6f, 0x40, 0x9d, 0x7b, 0x5e, 0xf2, - 0xb7, 0x3b, 0x4c, 0x22, 0x61, 0x8a, 0x41, 0xc8, 0xde, 0x18, 0xbb, 0x3e, 0x99, 0x63, 0x8b, 0x46, - 0x95, 0x21, 0x7b, 0xae, 0x9f, 0x45, 0x5b, 0xe7, 0x64, 0x94, 0x4d, 0xd1, 0xd6, 0x39, 0x2d, 0xc9, - 0x89, 0xeb, 0x79, 0xbc, 0xf0, 0xdf, 0x94, 0x4b, 0x12, 0x21, 0xb4, 0xee, 0x13, 0x34, 0x7d, 0xfd, - 0x6e, 0x06, 0x4d, 0xdf, 0xc6, 0x89, 0x43, 0x68, 0xfc, 0xf4, 0x5b, 0x72, 0xe2, 0x20, 0x00, 0xbf, - 0x9c, 0x22, 0xad, 0x73, 0x32, 0xb9, 0x26, 0x48, 0xeb, 0x5c, 0xf2, 0x2d, 0xda, 0x29, 0x30, 0xef, - 0x3b, 0x09, 0xdf, 0x42, 0x10, 0xe6, 0xce, 0x12, 0x58, 0xe7, 0x64, 0x63, 0xcd, 0x10, 0x58, 0xe7, - 0x64, 0x5c, 0x70, 0x2c, 0x9b, 0x2b, 0xfe, 0x2e, 0x17, 0x8f, 0x00, 0xaa, 0xf7, 0x1d, 0xa8, 0x47, - 0x8f, 0xcc, 0x14, 0x7f, 0x9f, 0xb3, 0x47, 0x8f, 0x0c, 0x45, 0x71, 0x17, 0x1a, 0xc9, 0xd4, 0x60, - 0x9a, 0xf7, 0x78, 0xe0, 0x6d, 0x39, 0x35, 0xe8, 0x4a, 0xcb, 0xaf, 0x0b, 0x70, 0x83, 0x8f, 0x38, - 0x89, 0xfd, 0xef, 0x39, 0x51, 0x64, 0x1d, 0x3b, 0x3b, 0x41, 0xb8, 0x33, 0xfd, 0xfe, 0xfb, 0x0b, - 0x71, 0x0f, 0xd6, 0xf6, 0xad, 0xd0, 0xf1, 0xe3, 0xc4, 0xf3, 0x40, 0x1a, 0x55, 0x67, 0xc1, 0xe2, - 0x31, 0x68, 0x0c, 0x62, 0x4f, 0xfe, 0x96, 0x3a, 0x37, 0x9f, 0x3d, 0x88, 0x9a, 0xa3, 0xd2, 0xff, - 0x49, 0x01, 0xaa, 0xdb, 0x6e, 0x14, 0x1b, 0xb8, 0x5d, 0x8a, 0x2f, 0x40, 0xf3, 0x82, 0x33, 0x54, - 0xf6, 0x49, 0x8c, 0xce, 0xe8, 0x9c, 0x72, 0x97, 0x4c, 0x15, 0xcd, 0x06, 0x11, 0xa6, 0x9a, 0xe2, - 0x17, 0xa0, 0x4d, 0x27, 0x93, 0x7c, 0xd6, 0xa5, 0x97, 0x64, 0x25, 0xc2, 0x34, 0xeb, 0x7b, 0x50, - 0xcb, 0x7c, 0x75, 0x81, 0x5e, 0x0a, 0xe9, 0xb7, 0x90, 0x38, 0xf3, 0x9d, 0x05, 0x37, 0x89, 0x20, - 0x2d, 0x5d, 0xff, 0x0f, 0x96, 0x40, 0x93, 0x16, 0x5d, 0xcb, 0x76, 0x42, 0x72, 0x60, 0xc8, 0x59, - 0x34, 0x0a, 0xaf, 0xb4, 0x68, 0xdc, 0x81, 0xb2, 0xe7, 0x8e, 0x13, 0x17, 0xe4, 0xdc, 0x01, 0x30, - 0x21, 0x70, 0xac, 0x83, 0xd0, 0x3d, 0x26, 0xdb, 0x4b, 0xf6, 0x72, 0x09, 0x1d, 0x14, 0xec, 0x4c, - 0xfd, 0x11, 0x0d, 0xd1, 0x03, 0x00, 0xdb, 0x8d, 0x62, 0x93, 0xac, 0x91, 0x79, 0x33, 0x76, 0xd2, - 0xff, 0x46, 0xd5, 0x4e, 0x86, 0xe2, 0x1e, 0x68, 0x19, 0x39, 0xd9, 0x4f, 0x0c, 0x89, 0x65, 0x23, - 0x95, 0x9f, 0x5b, 0x7e, 0xcb, 0x8f, 0xe7, 0x28, 0x51, 0x26, 0x5c, 0x9e, 0xa3, 0x44, 0x79, 0xf0, - 0x1e, 0x68, 0xc1, 0x0b, 0x27, 0x34, 0x8f, 0x9c, 0x78, 0x74, 0x62, 0x72, 0xb3, 0x56, 0xf8, 0xe0, - 0x06, 0xe1, 0x3b, 0x08, 0xee, 0x22, 0x54, 0x3f, 0x87, 0xcb, 0xcf, 0x9c, 0x51, 0x1c, 0x84, 0x9d, - 0xc4, 0xf3, 0x45, 0xde, 0xec, 0x0b, 0x03, 0x2f, 0xb1, 0xed, 0xe3, 0x6f, 0xf1, 0x0e, 0xa0, 0xc6, - 0xe1, 0x8c, 0x54, 0x0f, 0x2d, 0x54, 0x48, 0x9c, 0x11, 0xf6, 0x53, 0x39, 0x95, 0x8d, 0xe6, 0x05, - 0x1f, 0x46, 0xea, 0x7f, 0x6b, 0x05, 0xd6, 0x32, 0x9f, 0xa6, 0x2b, 0x31, 0x0f, 0xa1, 0x2e, 0x8f, - 0x00, 0x52, 0xa7, 0xaf, 0x05, 0x1f, 0xaa, 0x31, 0x11, 0x8b, 0x5a, 0x8f, 0x41, 0xcb, 0xe6, 0xf9, - 0x01, 0x2d, 0xa8, 0x91, 0xc9, 0x86, 0xba, 0xd0, 0x5d, 0x28, 0xf3, 0xc9, 0x5c, 0xae, 0x9e, 0xc9, - 0xa9, 0x27, 0x23, 0xc5, 0xef, 0xc0, 0x2a, 0x9f, 0xfc, 0x9a, 0xf2, 0xee, 0x4f, 0x29, 0xab, 0xe0, - 0x2d, 0xe8, 0x3c, 0xa3, 0xce, 0xf4, 0x94, 0x8e, 0xc4, 0x07, 0x50, 0x27, 0xd7, 0x6e, 0xf3, 0x05, - 0x91, 0xe6, 0xbd, 0x49, 0xd9, 0x0c, 0x47, 0x78, 0x2e, 0x49, 0xbc, 0x07, 0xeb, 0x38, 0x37, 0x2c, - 0x7f, 0xe4, 0xd0, 0x44, 0x23, 0x3b, 0x25, 0x9f, 0x12, 0x6b, 0x0a, 0x91, 0x1c, 0xc1, 0xfc, 0x16, - 0x54, 0x6d, 0x37, 0x74, 0x98, 0x68, 0xe5, 0xc7, 0xd8, 0x2c, 0x53, 0x7a, 0xf1, 0x08, 0xf7, 0x36, - 0xdf, 0x76, 0x49, 0xc7, 0xe0, 0x39, 0x52, 0x99, 0xab, 0x5b, 0x23, 0x21, 0xa1, 0xf9, 0x22, 0x3e, - 0x23, 0x7e, 0xc7, 0xd5, 0xe3, 0x19, 0x5e, 0x5d, 0x3c, 0xc3, 0x57, 0x15, 0x19, 0xcf, 0xf2, 0xf7, - 0xa0, 0x36, 0x09, 0x1d, 0xf3, 0xc8, 0xf5, 0x62, 0x27, 0x44, 0x45, 0x7c, 0xd6, 0x4e, 0x02, 0x93, - 0xd0, 0xd9, 0x61, 0xec, 0x42, 0xc7, 0xa3, 0xda, 0x42, 0xc7, 0x23, 0xf1, 0x00, 0x2e, 0xbb, 0xbe, - 0x1b, 0xbb, 0x96, 0x67, 0xb2, 0xf5, 0x88, 0xad, 0xc4, 0x75, 0x3a, 0x72, 0x5b, 0x97, 0xa8, 0x7d, - 0xc4, 0x90, 0xb9, 0x58, 0x7c, 0x06, 0xeb, 0x47, 0x6e, 0x88, 0xcb, 0x93, 0xb8, 0x17, 0x37, 0x7b, - 0x75, 0xae, 0xd9, 0x6b, 0x44, 0x64, 0x20, 0x0d, 0xb7, 0xfb, 0x7d, 0x10, 0x87, 0xd3, 0xd1, 0xa9, - 0x13, 0x9b, 0xce, 0xf9, 0xc4, 0xf2, 0x6d, 0x33, 0x8a, 0x9d, 0x09, 0x29, 0x71, 0xab, 0x86, 0xc6, - 0x98, 0x36, 0x21, 0x06, 0xb1, 0x33, 0x11, 0x6f, 0x41, 0x23, 0x3e, 0xc1, 0x2d, 0x21, 0x32, 0x23, - 0xc7, 0x0a, 0x47, 0x27, 0x7c, 0x6d, 0xdb, 0x58, 0x95, 0xd0, 0x01, 0x01, 0xc5, 0x23, 0x58, 0x25, - 0x41, 0x36, 0xb9, 0x47, 0xa7, 0x2d, 0xbc, 0x47, 0x57, 0x47, 0x22, 0x95, 0x12, 0x8f, 0xe0, 0xea, - 0x24, 0x88, 0x62, 0xd9, 0x95, 0x66, 0xba, 0xce, 0x49, 0xa8, 0xab, 0x18, 0x97, 0x11, 0xcb, 0x3d, - 0xd9, 0x57, 0x6b, 0x5d, 0x7c, 0x06, 0x24, 0x20, 0x9b, 0x28, 0x04, 0x4b, 0xbd, 0x6c, 0x7e, 0x02, - 0xe3, 0x12, 0x7c, 0x1e, 0x84, 0xa7, 0x2c, 0x11, 0xe3, 0x2f, 0xfd, 0x6f, 0xbf, 0x0f, 0xa5, 0x5e, - 0x60, 0x3b, 0xe2, 0x23, 0xa8, 0xd2, 0x1d, 0x9d, 0xcc, 0x4e, 0x21, 0x0d, 0x55, 0x88, 0xa6, 0x3f, - 0xc4, 0xf2, 0x2b, 0xbe, 0xfc, 0xf5, 0xf2, 0xcb, 0xa2, 0x6f, 0x90, 0x81, 0x95, 0x9c, 0xf5, 0xb0, - 0x1e, 0x35, 0x75, 0x03, 0xda, 0x8a, 0x23, 0x83, 0x31, 0x28, 0xa4, 0x93, 0xe3, 0x40, 0xe8, 0xf8, - 0xb4, 0xdc, 0xca, 0x46, 0x92, 0x26, 0xb3, 0x76, 0x18, 0x20, 0x2b, 0x60, 0x51, 0xb6, 0xbc, 0xc0, - 0xac, 0xcd, 0x78, 0x92, 0x6d, 0x3f, 0x82, 0xea, 0xb7, 0x81, 0xeb, 0x73, 0xc5, 0x97, 0xe7, 0x2a, - 0xfe, 0x55, 0xe0, 0xfa, 0x5c, 0xf1, 0x6f, 0xe5, 0x2f, 0xf1, 0x26, 0xac, 0x04, 0x3e, 0x97, 0xbd, - 0x32, 0x57, 0xf6, 0x72, 0xe0, 0x77, 0xf9, 0xb6, 0xc4, 0xaa, 0x1b, 0x99, 0x21, 0xe9, 0xba, 0x98, - 0x53, 0x3a, 0xf9, 0xd5, 0xdc, 0xc8, 0x40, 0x18, 0x16, 0x8b, 0x73, 0x5e, 0x0e, 0x12, 0x15, 0x56, - 0x9d, 0x9f, 0xf3, 0x8c, 0xa6, 0x02, 0xdf, 0x82, 0xca, 0x71, 0x18, 0x90, 0xa5, 0x60, 0xc1, 0xea, - 0x58, 0x21, 0xdc, 0xd6, 0x05, 0xea, 0x61, 0xf4, 0xd3, 0xf5, 0x8f, 0x4d, 0x3a, 0xa9, 0xc0, 0x75, - 0x51, 0x31, 0xea, 0x0a, 0x48, 0x67, 0x10, 0x6f, 0x41, 0xc5, 0x3a, 0x3e, 0x36, 0xe5, 0xa5, 0x8f, - 0xb9, 0xb2, 0xac, 0xe3, 0x63, 0xfa, 0xe4, 0x03, 0x58, 0x3d, 0x73, 0x7d, 0x33, 0x9a, 0x38, 0x23, - 0xa6, 0x5d, 0x9d, 0xef, 0xca, 0x33, 0xd7, 0x47, 0x1e, 0x42, 0xf4, 0xd9, 0xfd, 0xb4, 0xf1, 0xe3, - 0xf7, 0xd3, 0xb5, 0x97, 0xed, 0xa7, 0x3a, 0x2c, 0x4b, 0xa7, 0x02, 0x6d, 0x8e, 0x44, 0x62, 0xc4, - 0xc7, 0x50, 0x0b, 0x2d, 0xff, 0xd4, 0x94, 0x1e, 0x93, 0xdf, 0x64, 0xad, 0xed, 0x86, 0xe5, 0x9f, - 0x4a, 0x87, 0x49, 0x08, 0x93, 0xdf, 0x79, 0xdd, 0x7b, 0xfd, 0x15, 0xf6, 0xb0, 0x8c, 0x99, 0x4d, - 0xfc, 0xb0, 0x99, 0xed, 0x53, 0xb2, 0x67, 0x39, 0x7e, 0x6c, 0xaa, 0x0c, 0x97, 0x17, 0x67, 0xa8, - 0x33, 0x59, 0x9f, 0xb3, 0x61, 0x03, 0xe8, 0xb4, 0xcb, 0xa4, 0xa3, 0xb1, 0x8d, 0x5c, 0x03, 0x92, - 0x63, 0x30, 0x03, 0xc2, 0xf4, 0x48, 0xac, 0x09, 0x6b, 0xe9, 0xcd, 0x51, 0xbe, 0xae, 0x7b, 0x25, - 0x7b, 0xde, 0x9f, 0xbb, 0x6a, 0xaa, 0x2c, 0x68, 0x6e, 0xee, 0xfe, 0xe9, 0x9b, 0xb0, 0xca, 0xce, - 0xd3, 0xdc, 0x6f, 0x91, 0xf4, 0x57, 0xaf, 0x13, 0x90, 0xfb, 0x29, 0x22, 0x49, 0x45, 0x9a, 0xf5, - 0xe2, 0x73, 0x52, 0xb7, 0x52, 0x3e, 0xce, 0x76, 0xbc, 0xf8, 0xdc, 0xa8, 0xda, 0xea, 0x27, 0x6a, - 0x11, 0x87, 0xae, 0x6f, 0xe3, 0xd4, 0x8b, 0xad, 0xe3, 0x68, 0x73, 0x93, 0x56, 0x66, 0x4d, 0xc2, - 0x86, 0xd6, 0x71, 0x24, 0x3e, 0x81, 0xba, 0xc5, 0x56, 0x0b, 0xae, 0xf7, 0xf5, 0xec, 0xe9, 0x50, - 0xc6, 0x9e, 0x61, 0xd4, 0xac, 0x8c, 0x71, 0xe3, 0x73, 0x10, 0xca, 0xe7, 0x29, 0x63, 0x4b, 0xbf, - 0x31, 0x37, 0x1b, 0xd7, 0xa4, 0xd3, 0x53, 0x62, 0x49, 0xbf, 0x0d, 0x52, 0x14, 0x60, 0x76, 0xfc, - 0x1a, 0x55, 0x08, 0x18, 0x44, 0x8c, 0xf8, 0x73, 0x58, 0xcd, 0x9b, 0x6b, 0x6e, 0x2e, 0x70, 0x1d, - 0xe2, 0x8d, 0xbb, 0x3e, 0xca, 0x1a, 0x70, 0xde, 0xe4, 0x1b, 0xd0, 0xa4, 0x6a, 0x51, 0x46, 0x76, - 0x8f, 0xa9, 0xfb, 0x41, 0xdc, 0x52, 0x30, 0xec, 0x40, 0x65, 0x4c, 0x8d, 0xcf, 0x49, 0x3b, 0x4b, - 0x3a, 0x30, 0xb1, 0x41, 0x19, 0x55, 0x37, 0x31, 0x47, 0x6d, 0x83, 0x50, 0x0e, 0x0a, 0x28, 0xa5, - 0xb2, 0xfb, 0x2d, 0x29, 0x63, 0x89, 0x37, 0xd1, 0xac, 0x10, 0x6b, 0xb0, 0xab, 0x72, 0x56, 0xac, - 0x7d, 0x0c, 0x0d, 0xdc, 0x4a, 0x33, 0x5f, 0xd6, 0xb3, 0x8d, 0xda, 0x0f, 0x9d, 0xf4, 0xe3, 0xf5, - 0x49, 0x26, 0x25, 0x7e, 0x0e, 0xeb, 0x99, 0x9c, 0xd3, 0x53, 0xca, 0xfc, 0x26, 0x65, 0xde, 0x98, - 0xc9, 0x7c, 0x70, 0x8a, 0xd9, 0x1b, 0x93, 0x5c, 0x5a, 0x6c, 0x40, 0xb9, 0x13, 0xb5, 0x7d, 0x9b, - 0x94, 0xb4, 0x8a, 0xc1, 0x09, 0xf1, 0x08, 0xea, 0x6c, 0x11, 0x21, 0xc3, 0x64, 0xb4, 0xf9, 0x76, - 0xf6, 0xf8, 0x97, 0xcc, 0x22, 0x6c, 0xdd, 0xac, 0x79, 0xc9, 0xef, 0x08, 0x37, 0x62, 0x3e, 0x9b, - 0xcf, 0xb2, 0xc8, 0x77, 0xe6, 0x87, 0x9c, 0x88, 0x76, 0x52, 0x3e, 0x69, 0xc0, 0xf5, 0x70, 0xea, - 0x93, 0x95, 0x44, 0xe6, 0xe4, 0x7d, 0x9f, 0xf2, 0xdf, 0xa3, 0xfc, 0x32, 0xa6, 0x86, 0xc1, 0x64, - 0x9c, 0x97, 0x78, 0xd3, 0xd5, 0x30, 0x0b, 0x22, 0xa9, 0xe0, 0x25, 0x65, 0xf2, 0x69, 0x0a, 0x95, - 0xf9, 0xee, 0x4f, 0x29, 0x93, 0x4e, 0x5a, 0xa8, 0x4c, 0x01, 0xa5, 0xe9, 0xd4, 0xb5, 0x49, 0x65, - 0xac, 0x1b, 0xf4, 0x1b, 0xc5, 0x82, 0xd0, 0x19, 0x4d, 0xc3, 0xc8, 0x7d, 0xe1, 0x98, 0x91, 0xeb, - 0x9f, 0x92, 0xb2, 0x58, 0x31, 0x56, 0x13, 0xe8, 0xc0, 0xf5, 0x4f, 0x91, 0x65, 0x38, 0xe7, 0xb1, - 0x13, 0xfa, 0x7c, 0x75, 0xfc, 0xfd, 0x2c, 0xcb, 0x68, 0x13, 0x02, 0xd7, 0xb9, 0x01, 0x4e, 0xf2, - 0x7b, 0x66, 0x64, 0x23, 0x1e, 0xd9, 0x07, 0x3f, 0x6a, 0x64, 0x07, 0x34, 0xb2, 0x6f, 0x43, 0xc5, - 0xf5, 0x63, 0x27, 0x7c, 0x61, 0x79, 0x9b, 0x1f, 0xce, 0x71, 0xe3, 0x04, 0x27, 0xee, 0xc2, 0x4a, - 0xe4, 0xb9, 0xb8, 0xde, 0x37, 0x3f, 0x9a, 0x23, 0x53, 0x28, 0x71, 0x0f, 0xaa, 0x49, 0xa0, 0x97, - 0xcd, 0x8f, 0xe7, 0xe8, 0x52, 0xa4, 0xb8, 0x05, 0xa5, 0x33, 0x9c, 0x50, 0x0f, 0xe7, 0x8f, 0xeb, - 0x11, 0x8e, 0xdb, 0xf7, 0x11, 0x2a, 0xff, 0xb4, 0x7d, 0x3f, 0x9a, 0xdb, 0xbe, 0x77, 0x5c, 0xcf, - 0xe3, 0xed, 0xfb, 0x48, 0xfe, 0xc2, 0xcd, 0x8f, 0x72, 0x60, 0x4b, 0x3e, 0x99, 0xdf, 0xfc, 0x10, - 0xf7, 0x8c, 0x42, 0xe2, 0xd4, 0x22, 0x3a, 0x83, 0xe6, 0xa3, 0xf4, 0x4f, 0xb3, 0x7d, 0x95, 0x3f, - 0x9c, 0x36, 0x20, 0x4a, 0xd2, 0xe2, 0x75, 0x90, 0x47, 0xec, 0xa4, 0x7d, 0x7d, 0xc6, 0x81, 0x07, - 0x18, 0x82, 0x8a, 0xd7, 0x47, 0xb0, 0xaa, 0x7c, 0xa7, 0xf1, 0x73, 0xd1, 0xe6, 0xe7, 0x73, 0x35, - 0xc8, 0x13, 0x88, 0x6d, 0xa8, 0x1f, 0x4d, 0xbf, 0xff, 0xfe, 0x62, 0xcc, 0x36, 0x81, 0xcd, 0xc7, - 0x54, 0x91, 0x3b, 0x6a, 0x63, 0x7d, 0x99, 0xcd, 0xc0, 0xc8, 0xe5, 0x12, 0x0f, 0x40, 0xb8, 0x47, - 0x3c, 0x9e, 0x3b, 0x61, 0x30, 0x66, 0xbd, 0x7f, 0xf3, 0x0b, 0x79, 0x2e, 0x31, 0x87, 0x21, 0x87, - 0x1c, 0xc7, 0xb7, 0xcd, 0x71, 0x24, 0xc5, 0x84, 0x2f, 0xa9, 0x9e, 0x92, 0x79, 0x25, 0x01, 0xa1, - 0xd4, 0x81, 0x2b, 0xd2, 0xee, 0x45, 0x2c, 0x35, 0x7c, 0x01, 0x38, 0x5d, 0x5f, 0xa4, 0x59, 0x7f, - 0xeb, 0x07, 0xb3, 0x22, 0xad, 0xca, 0xfa, 0x18, 0x1a, 0x7c, 0x1a, 0x42, 0x12, 0x19, 0x4e, 0xd1, - 0x9f, 0x65, 0x39, 0x57, 0xf6, 0x18, 0xd7, 0xa8, 0xdb, 0xd9, 0x43, 0xdd, 0xcf, 0x61, 0x2d, 0x73, - 0x18, 0x42, 0x9f, 0xfd, 0xed, 0xec, 0x67, 0x93, 0xd3, 0x10, 0x63, 0x75, 0xaa, 0x7e, 0xd2, 0x27, - 0xe7, 0x44, 0xec, 0xdf, 0xf9, 0x11, 0x22, 0xf6, 0x9b, 0x90, 0xae, 0x48, 0x73, 0x14, 0x3b, 0x9b, - 0x3f, 0x67, 0xe6, 0x9f, 0x00, 0x5b, 0xb1, 0x23, 0x1e, 0x01, 0x58, 0x93, 0x89, 0x77, 0xc1, 0x53, - 0xf3, 0x17, 0x34, 0x35, 0x37, 0x32, 0x53, 0xb3, 0x89, 0x48, 0x9a, 0x9b, 0x55, 0x4b, 0xfd, 0x44, - 0x05, 0x97, 0x84, 0x77, 0x7b, 0xec, 0x51, 0xfb, 0x9b, 0xd9, 0xb5, 0xbd, 0x1f, 0x44, 0xf1, 0xf6, - 0xd8, 0xc3, 0x56, 0xc0, 0x24, 0xf9, 0x2d, 0xba, 0x70, 0x39, 0xf0, 0x4d, 0x7b, 0x3a, 0xf1, 0xdc, - 0x11, 0xf6, 0x00, 0x9f, 0x42, 0x6e, 0x6e, 0x65, 0xef, 0x27, 0xd1, 0x17, 0xfb, 0xfe, 0xb6, 0x22, - 0x92, 0xa7, 0x9b, 0xeb, 0xc1, 0x2c, 0x88, 0x0c, 0x56, 0x34, 0x06, 0xc9, 0xd5, 0x9e, 0x16, 0x8b, - 0x06, 0x04, 0x55, 0x77, 0x7b, 0x1e, 0xc3, 0x5a, 0x4a, 0x85, 0x0d, 0x8c, 0x36, 0xb7, 0xb3, 0x33, - 0x39, 0x73, 0x0b, 0x72, 0x55, 0x65, 0x44, 0x58, 0x44, 0x7d, 0x17, 0x78, 0xde, 0x74, 0x22, 0x59, - 0xe9, 0x66, 0x5b, 0xf6, 0x1d, 0x01, 0x99, 0x4b, 0x66, 0x6c, 0x7a, 0xce, 0x78, 0x73, 0x27, 0x6b, - 0xd3, 0x73, 0xc6, 0x28, 0x66, 0x48, 0x3f, 0xfb, 0x17, 0xae, 0x73, 0x16, 0x6d, 0x3e, 0x21, 0xcd, - 0x4f, 0xde, 0xf9, 0x7b, 0x86, 0x20, 0xdc, 0xf7, 0x59, 0x8f, 0x25, 0x92, 0xcd, 0x5d, 0xbe, 0xf4, - 0xc5, 0x20, 0xa4, 0x10, 0x2d, 0xd8, 0x24, 0x76, 0x2e, 0xd7, 0x6a, 0xce, 0x0f, 0xa6, 0x33, 0xb7, - 0x24, 0xaf, 0x20, 0x2d, 0xbb, 0xd9, 0xec, 0x67, 0x3c, 0x62, 0x7e, 0x01, 0xaf, 0x2f, 0x2e, 0x44, - 0x85, 0x1d, 0xf8, 0x8a, 0xe4, 0x8d, 0xeb, 0x0b, 0x72, 0xcb, 0xd8, 0x03, 0x5f, 0xc2, 0xea, 0xb1, - 0x45, 0x3d, 0xe1, 0x99, 0xe3, 0xc0, 0x76, 0x36, 0x9f, 0x66, 0x83, 0xec, 0xd0, 0xa0, 0x3d, 0xb1, - 0xb0, 0x53, 0xbc, 0xbd, 0xc0, 0x76, 0x8c, 0xda, 0x71, 0x9a, 0x10, 0x8f, 0x61, 0x33, 0x9d, 0x84, - 0x53, 0x1f, 0xbf, 0x8b, 0x2a, 0xb5, 0xeb, 0x8f, 0xe2, 0xcd, 0x2e, 0xf5, 0xe9, 0xd5, 0x04, 0x7f, - 0x80, 0xe8, 0x6d, 0x89, 0x15, 0x2d, 0xd0, 0x88, 0x39, 0xc8, 0x5d, 0x2c, 0x72, 0x6d, 0x67, 0x73, - 0x2f, 0x1b, 0x44, 0x83, 0x59, 0x27, 0x92, 0xd0, 0x7e, 0x35, 0x70, 0x6d, 0xc7, 0x68, 0x1c, 0xe5, - 0xd2, 0xe2, 0x5d, 0x58, 0x57, 0xfa, 0x88, 0x79, 0x62, 0x45, 0x27, 0xe6, 0xa9, 0x73, 0xb1, 0xd9, - 0xa3, 0x06, 0x37, 0xa4, 0x32, 0xb2, 0x6b, 0x45, 0x27, 0x4f, 0x9d, 0x0b, 0x71, 0x1f, 0x75, 0x6a, - 0xda, 0x36, 0xdd, 0xc8, 0x3c, 0xb4, 0xc2, 0xd0, 0x75, 0xc2, 0xcd, 0x3e, 0x1f, 0xa1, 0x31, 0xa2, - 0x13, 0x6d, 0x31, 0x18, 0xf5, 0xe8, 0xec, 0x60, 0x48, 0x75, 0x7d, 0x9f, 0xf8, 0xa8, 0x96, 0x71, - 0x47, 0x62, 0x6d, 0xfd, 0x23, 0x68, 0x24, 0xfd, 0xc7, 0x4e, 0x39, 0xbf, 0x9c, 0xdb, 0x27, 0xea, - 0xb2, 0xd3, 0x06, 0xe4, 0x95, 0xf3, 0x3e, 0xd4, 0x93, 0x1c, 0x8e, 0x6f, 0x6f, 0x1a, 0xf3, 0x66, - 0x43, 0x49, 0x8f, 0xbb, 0xcb, 0xa7, 0x70, 0x8d, 0xa7, 0x39, 0x1f, 0x9d, 0x9c, 0x3a, 0x17, 0x49, - 0xcc, 0x94, 0x01, 0xd5, 0x7f, 0x83, 0xd0, 0x1d, 0xc4, 0x3e, 0x75, 0x2e, 0xa2, 0x84, 0x7b, 0x5e, - 0x27, 0xd7, 0x82, 0x91, 0xbc, 0x31, 0xcc, 0x66, 0x75, 0xd9, 0x96, 0x21, 0x8f, 0x0d, 0x12, 0xb4, - 0x12, 0xbc, 0x21, 0xdd, 0xd5, 0x44, 0x13, 0xd6, 0xd9, 0x0e, 0x94, 0x0d, 0x0e, 0x73, 0x40, 0x95, - 0xbc, 0xb2, 0x50, 0x21, 0x37, 0xd6, 0x5e, 0xcc, 0x18, 0xc9, 0x9e, 0xc0, 0x8d, 0xe4, 0xac, 0xd4, - 0xf9, 0x6e, 0x6a, 0xd1, 0x75, 0xab, 0x53, 0xe7, 0x82, 0xd9, 0xe2, 0xf3, 0xb9, 0xd9, 0x7d, 0x4d, - 0x51, 0xb7, 0x25, 0xf1, 0x53, 0xe7, 0x82, 0x78, 0xe3, 0x5d, 0x68, 0x58, 0x51, 0x70, 0x24, 0xb5, - 0xd8, 0x51, 0xe0, 0x6d, 0x3e, 0xa3, 0x71, 0xa8, 0x23, 0xd4, 0x50, 0x47, 0xb3, 0x3d, 0xb8, 0x9c, - 0x8e, 0x98, 0xe5, 0x1d, 0x07, 0xa1, 0x1b, 0x9f, 0x8c, 0x37, 0x7f, 0x95, 0xb5, 0x36, 0xd1, 0x84, - 0x4a, 0x26, 0x7f, 0x53, 0x11, 0x19, 0xe9, 0x58, 0x27, 0x30, 0xf1, 0x18, 0xae, 0xa7, 0xe5, 0xc5, - 0xc1, 0xc4, 0xf4, 0xcd, 0x33, 0x37, 0x3e, 0x31, 0xc9, 0xaf, 0xf9, 0x77, 0x65, 0x64, 0x0d, 0x45, - 0x30, 0x0c, 0x26, 0xbd, 0xe7, 0x6e, 0x7c, 0x32, 0x74, 0x9d, 0x48, 0x6c, 0xc1, 0x2d, 0xcb, 0xb6, - 0x26, 0x31, 0x2e, 0x08, 0xcc, 0x98, 0x44, 0x03, 0x09, 0x7c, 0xd3, 0x19, 0x4f, 0xe2, 0x8b, 0xcd, - 0xbf, 0x46, 0xd9, 0x6f, 0x28, 0xaa, 0x61, 0x30, 0xd9, 0x91, 0x34, 0x7d, 0xbf, 0x8d, 0x14, 0xfa, - 0x3f, 0x2b, 0x43, 0x45, 0x19, 0x2b, 0x44, 0x0d, 0x56, 0x0e, 0x7a, 0x4f, 0x7b, 0xfd, 0xe7, 0x3d, - 0x0e, 0x31, 0xd5, 0x1c, 0x0c, 0xda, 0xc6, 0x50, 0xb3, 0x45, 0x03, 0x80, 0xc2, 0xa9, 0x98, 0x83, - 0x56, 0xb3, 0xc7, 0x21, 0xa7, 0x28, 0x88, 0x0b, 0xa7, 0x97, 0xc4, 0x3a, 0xac, 0xee, 0x1c, 0xf4, - 0xf8, 0xca, 0x11, 0x81, 0x8a, 0x08, 0x6a, 0x7f, 0xcd, 0x7e, 0x7f, 0x0c, 0x2a, 0x21, 0x68, 0xaf, - 0x39, 0x6c, 0x1b, 0x1d, 0x05, 0x2a, 0x8b, 0x2b, 0xb0, 0x9e, 0x50, 0xa9, 0x12, 0xb4, 0x15, 0x04, - 0x3f, 0x6b, 0xb7, 0x86, 0x7d, 0xc3, 0xec, 0xf4, 0xb6, 0xdb, 0x5f, 0x33, 0x75, 0x05, 0xeb, 0xb7, - 0x6f, 0xf4, 0xbf, 0x6a, 0xb7, 0x86, 0x1a, 0x90, 0x2b, 0xe1, 0x93, 0x27, 0x5a, 0x4d, 0xd4, 0xa1, - 0xb2, 0xdd, 0x19, 0x0c, 0x3b, 0xbd, 0xd6, 0x50, 0xab, 0x63, 0xb5, 0x77, 0x3a, 0xdd, 0x61, 0xdb, - 0xd0, 0x56, 0x45, 0x05, 0x4a, 0x5f, 0xf5, 0x3b, 0x3d, 0xad, 0x41, 0xc1, 0x65, 0x9a, 0x7b, 0xfb, - 0xdd, 0xb6, 0xb6, 0x86, 0xd0, 0x41, 0xdf, 0x18, 0x6a, 0x1a, 0x42, 0x9f, 0x77, 0x7a, 0xdb, 0xfd, - 0xe7, 0xda, 0xba, 0xa8, 0x42, 0xf9, 0xa0, 0x87, 0x5f, 0x17, 0x62, 0x15, 0xaa, 0xf4, 0xd3, 0x6c, - 0x76, 0xbb, 0xda, 0xe5, 0x8c, 0xff, 0xe1, 0x06, 0xa2, 0xc8, 0x9b, 0x71, 0x80, 0x75, 0xb8, 0x82, - 0x2d, 0x4a, 0x92, 0x44, 0x7d, 0x15, 0xcb, 0xd9, 0xeb, 0xf4, 0x0e, 0x06, 0xda, 0x35, 0x24, 0xa6, - 0x9f, 0x84, 0xd9, 0xc4, 0x72, 0x3a, 0x3d, 0xea, 0xd0, 0x5b, 0xf8, 0x7b, 0xbb, 0xdd, 0x6d, 0x0f, - 0xdb, 0xda, 0x6d, 0x6c, 0x55, 0xb7, 0xdf, 0x7a, 0x6a, 0xf6, 0xf7, 0xb5, 0x37, 0xb0, 0x67, 0xf7, - 0x8d, 0xb6, 0x29, 0x09, 0xdf, 0x14, 0x1a, 0xd4, 0x77, 0x0e, 0x7e, 0xf5, 0xab, 0x6f, 0x4c, 0xd9, - 0xa8, 0xb7, 0xf0, 0x9b, 0x29, 0x85, 0x79, 0xf0, 0x54, 0x7b, 0x7b, 0x06, 0x34, 0x78, 0xaa, 0xbd, - 0x83, 0x9d, 0xa2, 0xfa, 0x5a, 0xbb, 0x87, 0x04, 0x46, 0xbb, 0x75, 0x60, 0x0c, 0x3a, 0xcf, 0xda, - 0x66, 0x6b, 0xd8, 0xd6, 0xde, 0xa5, 0x5e, 0xe8, 0xf4, 0x9e, 0x6a, 0xf7, 0xb1, 0x9a, 0xf8, 0x8b, - 0x3b, 0xf9, 0x3d, 0x21, 0xa0, 0x91, 0xd2, 0x12, 0xec, 0x7d, 0x24, 0xd9, 0x32, 0xfa, 0xcd, 0xed, - 0x56, 0x73, 0x30, 0xd4, 0x3e, 0xc0, 0x36, 0x0e, 0xf6, 0xbb, 0x9d, 0xa1, 0xf6, 0x00, 0x1b, 0xf2, - 0xa4, 0x39, 0xdc, 0x6d, 0x1b, 0xda, 0x87, 0x62, 0x0d, 0x6a, 0xc3, 0xce, 0x5e, 0xdb, 0x94, 0x7d, - 0xfa, 0x10, 0xbf, 0xb1, 0xd3, 0xe9, 0x76, 0xb5, 0x47, 0xe4, 0x3f, 0xd7, 0x34, 0x86, 0x1d, 0x1a, - 0xdf, 0x4f, 0xb0, 0x80, 0xe6, 0xfe, 0x7e, 0xf7, 0x1b, 0xed, 0x53, 0x6c, 0xe0, 0xde, 0x41, 0x77, - 0xd8, 0x31, 0x0f, 0xf6, 0xb7, 0x9b, 0xc3, 0xb6, 0xf6, 0x19, 0x8d, 0x72, 0x7f, 0x30, 0xdc, 0xde, - 0xeb, 0x6a, 0x9f, 0x53, 0x99, 0x34, 0xd3, 0x5a, 0xdd, 0x7e, 0xaf, 0xad, 0x3d, 0x46, 0xfa, 0xe6, - 0x76, 0x73, 0x7f, 0x88, 0xb5, 0x1b, 0xf6, 0xf7, 0xb5, 0x2f, 0xf4, 0x52, 0x65, 0x59, 0x5b, 0xd6, - 0x4b, 0x95, 0x3b, 0xda, 0x1d, 0xbd, 0x54, 0xd1, 0x35, 0xfd, 0x7e, 0x6d, 0xd0, 0x3f, 0x30, 0x5a, - 0x5c, 0x7b, 0xfd, 0x6f, 0x14, 0xa0, 0xa2, 0xac, 0x5a, 0xf8, 0xe1, 0x4e, 0xaf, 0xd7, 0x36, 0xb4, - 0x4b, 0x58, 0xb9, 0x6e, 0x7b, 0x67, 0xa8, 0x15, 0xc8, 0xfd, 0xb0, 0xf3, 0x64, 0x77, 0xa8, 0x2d, - 0xe1, 0xcf, 0xfe, 0x01, 0xf6, 0x73, 0x91, 0x3a, 0xa8, 0xbd, 0xd7, 0xd1, 0x4a, 0xf8, 0xab, 0xd9, - 0x1b, 0x76, 0xb4, 0x32, 0x4d, 0xa3, 0x4e, 0xef, 0x49, 0xb7, 0xad, 0x2d, 0x23, 0x74, 0xaf, 0x69, - 0x3c, 0xd5, 0x56, 0xb8, 0xd0, 0xed, 0xf6, 0xd7, 0x5a, 0x45, 0x2c, 0xc3, 0x52, 0xf7, 0xa1, 0x56, - 0x45, 0xd0, 0x76, 0x7b, 0xfb, 0x60, 0x5f, 0x03, 0xca, 0x3d, 0xe8, 0xef, 0x68, 0x35, 0xec, 0x04, - 0xfc, 0x65, 0xd2, 0x67, 0xeb, 0xfa, 0x3d, 0x58, 0x69, 0x1e, 0x1f, 0xd3, 0xde, 0x86, 0x1d, 0x75, - 0xd0, 0xed, 0xf2, 0xaa, 0xdb, 0xea, 0x0f, 0x87, 0xfd, 0x3d, 0xad, 0x80, 0x33, 0x1c, 0x5b, 0xb8, - 0xa4, 0x77, 0xa0, 0xa2, 0x04, 0xfb, 0x4c, 0x60, 0xa5, 0x0a, 0x94, 0xf6, 0x8d, 0xf6, 0x33, 0x76, - 0xa6, 0xed, 0xb5, 0xbf, 0xc6, 0xfa, 0xe3, 0x2f, 0x2c, 0xa8, 0x88, 0x35, 0xe0, 0x08, 0x48, 0x14, - 0x59, 0xa9, 0xdb, 0xe9, 0xb5, 0x9b, 0x86, 0x56, 0xd6, 0x1f, 0x43, 0x2d, 0xb3, 0xc3, 0xe2, 0xc4, - 0x78, 0xd2, 0xdc, 0xc7, 0xc9, 0xd5, 0x35, 0x65, 0xb1, 0x57, 0x41, 0x24, 0xa0, 0x74, 0xcc, 0x0a, - 0xfa, 0xa7, 0xb0, 0x3e, 0x27, 0x50, 0x51, 0xc5, 0x9b, 0x1d, 0x59, 0xf1, 0xce, 0x93, 0x5e, 0xdf, - 0x68, 0x73, 0x90, 0x27, 0x39, 0x9a, 0x4b, 0xfa, 0x7b, 0x50, 0x4d, 0x24, 0x3f, 0x9c, 0xdd, 0x2d, - 0xa3, 0x3f, 0x18, 0xf0, 0xe0, 0x5f, 0xc2, 0x34, 0x75, 0x37, 0xa7, 0x0b, 0xba, 0x0b, 0x8d, 0xfc, - 0x36, 0x2c, 0xee, 0xc0, 0x4d, 0x9e, 0xff, 0x5b, 0x07, 0x9d, 0xee, 0xb6, 0x39, 0xe8, 0x6c, 0xb7, - 0x67, 0x6e, 0x8f, 0xdf, 0x80, 0xab, 0x73, 0x14, 0x1c, 0x61, 0xaa, 0x20, 0xae, 0xc3, 0x95, 0x39, - 0x1c, 0xcd, 0xfa, 0x25, 0x7d, 0x1f, 0xc4, 0x3c, 0x83, 0x16, 0x37, 0x61, 0x33, 0x69, 0xb3, 0xd9, - 0xec, 0x3e, 0xe9, 0x1b, 0x9d, 0xe1, 0xee, 0x9e, 0x49, 0xfc, 0xe2, 0xd2, 0xcb, 0xb0, 0xbb, 0xcd, - 0xc1, 0xae, 0x56, 0xf8, 0xaa, 0x54, 0xb9, 0xad, 0xdd, 0xf9, 0xaa, 0x54, 0xb9, 0xab, 0xbd, 0xa5, - 0x0f, 0x60, 0x5d, 0x49, 0xcf, 0x74, 0x37, 0x9f, 0xcc, 0x3a, 0x1b, 0x50, 0xee, 0x3a, 0x2f, 0x1c, - 0x4f, 0x5d, 0xe5, 0xa6, 0x04, 0x42, 0xfb, 0x87, 0xdf, 0x76, 0x92, 0x30, 0x8a, 0x94, 0x40, 0x75, - 0xb9, 0x97, 0x89, 0xe4, 0x48, 0xe1, 0x14, 0xfe, 0xd5, 0x02, 0x54, 0x12, 0x99, 0xfc, 0x2e, 0x2c, - 0x0d, 0x07, 0xf2, 0x40, 0x68, 0xe3, 0x41, 0x1a, 0xb6, 0x77, 0xa8, 0x7e, 0x19, 0x4b, 0xc3, 0x81, - 0x78, 0x1f, 0x96, 0x39, 0x98, 0x9c, 0x3c, 0x02, 0xda, 0xc8, 0xcb, 0xf9, 0x43, 0xc2, 0x19, 0x92, - 0x46, 0x7c, 0x0a, 0xd5, 0xa4, 0xb6, 0xd2, 0x1a, 0x7d, 0x2d, 0x9f, 0x21, 0x41, 0x1b, 0x29, 0xa5, - 0xde, 0x85, 0x46, 0xbe, 0x40, 0x71, 0x0b, 0x80, 0x8b, 0xcc, 0x9c, 0xdd, 0x66, 0x20, 0xe2, 0x06, - 0xa8, 0x18, 0x77, 0xdb, 0x54, 0xb1, 0xd5, 0x24, 0xe6, 0xdd, 0xb6, 0xfe, 0x6f, 0x95, 0x01, 0x52, - 0xad, 0x1e, 0x3b, 0x22, 0xb1, 0xb1, 0x97, 0xa5, 0x97, 0xef, 0x6b, 0x50, 0xf5, 0x02, 0xcb, 0xce, - 0x86, 0xdf, 0xad, 0x20, 0x80, 0xe6, 0x58, 0x36, 0xde, 0x4c, 0x55, 0x86, 0x8e, 0xb9, 0x0a, 0xcb, - 0x47, 0x41, 0x38, 0xb6, 0x62, 0x79, 0x6f, 0x5f, 0xa6, 0x28, 0xa4, 0x0c, 0x39, 0xa9, 0xd9, 0xa6, - 0xe7, 0xfa, 0x0e, 0x07, 0xce, 0x2a, 0x19, 0x75, 0x09, 0xec, 0x22, 0x0c, 0x85, 0x6b, 0xc7, 0x1f, - 0x79, 0x01, 0x8a, 0x35, 0x87, 0x7c, 0x6b, 0xa1, 0x6e, 0x80, 0x02, 0x6d, 0x5d, 0x70, 0x6b, 0xc3, - 0xb1, 0xeb, 0x5b, 0xb1, 0xf4, 0xde, 0xa1, 0xd6, 0x2a, 0x08, 0x56, 0xf7, 0xdb, 0x28, 0x90, 0x26, - 0x77, 0x0e, 0x68, 0x52, 0x41, 0x00, 0x55, 0xf7, 0x75, 0x00, 0x27, 0x1a, 0x59, 0x13, 0x2e, 0xbc, - 0xca, 0xf1, 0xdf, 0x24, 0x64, 0xeb, 0x42, 0x74, 0xa1, 0x31, 0x3c, 0x44, 0x65, 0x22, 0xd8, 0xb6, - 0x62, 0xab, 0x15, 0x78, 0xd2, 0x18, 0x7e, 0x77, 0xd6, 0xfc, 0xf1, 0x20, 0x4f, 0xc6, 0x37, 0xe7, - 0x66, 0xf2, 0x8a, 0x4f, 0xa0, 0xee, 0x8e, 0x9c, 0x43, 0x27, 0x3c, 0x66, 0x41, 0xab, 0x96, 0xbb, - 0x70, 0xcf, 0x18, 0x12, 0xb2, 0x6a, 0x6e, 0x9a, 0x40, 0x25, 0x6d, 0x1c, 0xf8, 0xc7, 0x81, 0x7d, - 0xc8, 0xb9, 0xea, 0x59, 0xc3, 0xde, 0x1e, 0x62, 0x38, 0x8f, 0x24, 0xa2, 0x3c, 0xbf, 0x0d, 0x6b, - 0xd8, 0xf3, 0x51, 0x1c, 0x3a, 0xd6, 0x98, 0xb3, 0xad, 0x66, 0x67, 0x20, 0xd6, 0x68, 0x40, 0x48, - 0xca, 0xdb, 0x48, 0x89, 0x29, 0xfb, 0x27, 0xe9, 0x75, 0x76, 0xca, 0xdb, 0xc8, 0x79, 0xd5, 0x33, - 0x86, 0x3f, 0x7a, 0x94, 0x26, 0xc4, 0x03, 0x80, 0x53, 0xeb, 0xe8, 0xd4, 0xe2, 0x3c, 0x6b, 0xd9, - 0x6a, 0x3e, 0x45, 0x38, 0xe5, 0xa8, 0x9e, 0xaa, 0x9f, 0x37, 0x9a, 0x70, 0x79, 0x41, 0xaf, 0xfd, - 0xa4, 0x6b, 0x60, 0x7f, 0xa7, 0x00, 0x8d, 0x7c, 0x5b, 0x66, 0x62, 0x69, 0x56, 0x93, 0x58, 0x9a, - 0x02, 0x4a, 0x93, 0x20, 0x54, 0x37, 0x36, 0xe8, 0x77, 0x1a, 0xda, 0xba, 0x98, 0x0d, 0x6d, 0xbd, - 0x09, 0x2b, 0xa1, 0xc3, 0xb7, 0xaf, 0x39, 0x52, 0x83, 0x4a, 0xe2, 0x84, 0x9d, 0x4c, 0xa3, 0x13, - 0xc7, 0x56, 0xda, 0x24, 0x5f, 0x23, 0xa8, 0x33, 0x50, 0x6a, 0x93, 0xd7, 0x60, 0xc5, 0x9a, 0xb8, - 0xa4, 0xa0, 0xc8, 0xb8, 0x8c, 0xd6, 0xc4, 0x7d, 0xea, 0x5c, 0xe8, 0xdf, 0x43, 0x2d, 0xd3, 0x77, - 0x58, 0xa1, 0x53, 0xd7, 0x57, 0x91, 0x25, 0xe8, 0x37, 0x56, 0x5e, 0x86, 0x22, 0xe4, 0xbb, 0x20, - 0x32, 0x85, 0x1f, 0x56, 0xd7, 0xf8, 0xe8, 0x74, 0x56, 0x1d, 0xf5, 0x4b, 0xe0, 0x2f, 0x11, 0x86, - 0xcb, 0x1a, 0x2b, 0x62, 0x07, 0x67, 0xbe, 0xac, 0x78, 0x92, 0xd6, 0xff, 0xb3, 0x25, 0xa8, 0x26, - 0x83, 0x80, 0x2d, 0x3c, 0x0c, 0x83, 0x53, 0x27, 0x8c, 0xe4, 0xd7, 0x55, 0x92, 0x7a, 0x24, 0x98, - 0xb8, 0x23, 0x75, 0x29, 0x9a, 0x12, 0xe2, 0x66, 0xf6, 0x0a, 0x24, 0x47, 0x2b, 0xc9, 0xdc, 0x78, - 0xbc, 0x05, 0x60, 0x4d, 0xe3, 0x60, 0x14, 0x8c, 0xc7, 0x6e, 0x2c, 0xbf, 0x9c, 0x81, 0x60, 0x99, - 0xa4, 0xa2, 0xc9, 0xde, 0xe2, 0x44, 0x86, 0x29, 0x2c, 0xe7, 0x98, 0xc2, 0x4d, 0xa8, 0x46, 0xce, - 0xc4, 0x0a, 0xad, 0x38, 0x08, 0xe5, 0x6a, 0x4e, 0x01, 0x14, 0x0e, 0x13, 0x35, 0x2b, 0x15, 0xb7, - 0xae, 0x68, 0xac, 0x50, 0xba, 0x63, 0x8b, 0x3b, 0x74, 0x15, 0xc9, 0x4c, 0xd0, 0xec, 0x87, 0x0d, - 0x27, 0x56, 0x34, 0x90, 0x14, 0x02, 0x4a, 0xe4, 0x13, 0xc3, 0x5e, 0x78, 0xf4, 0x5b, 0xbc, 0x03, - 0x14, 0x74, 0x3d, 0x98, 0xc6, 0x66, 0xe4, 0x8c, 0x02, 0xdf, 0x8e, 0xa4, 0x1f, 0x5e, 0x43, 0x82, - 0x07, 0x0c, 0xd5, 0xff, 0xeb, 0x25, 0xa8, 0x65, 0xd6, 0x28, 0xb9, 0x20, 0x59, 0x13, 0x3a, 0xf9, - 0x4a, 0xee, 0x6f, 0x57, 0x25, 0x84, 0x43, 0x84, 0x8c, 0xac, 0xd8, 0xf2, 0x82, 0xe3, 0x4c, 0xf0, - 0x13, 0x09, 0xe9, 0xd8, 0xe4, 0x15, 0x68, 0x8d, 0x1d, 0x0e, 0x82, 0x2e, 0x2f, 0xef, 0x27, 0x80, - 0x74, 0x5e, 0x96, 0xb2, 0xf3, 0x52, 0x63, 0xf7, 0x51, 0xee, 0x45, 0xf2, 0x16, 0xbd, 0x0d, 0x35, - 0x65, 0x49, 0xc2, 0xaf, 0x48, 0x0f, 0x47, 0x05, 0xea, 0xd8, 0xe2, 0x6d, 0x48, 0x43, 0xca, 0x9b, - 0x56, 0x64, 0x06, 0x47, 0xea, 0xde, 0x5c, 0x02, 0x6e, 0x46, 0xfd, 0xa3, 0xc4, 0xed, 0x88, 0xac, - 0x02, 0x92, 0x47, 0x22, 0x80, 0xa4, 0x94, 0x07, 0x70, 0x59, 0x1e, 0x61, 0xd2, 0xc4, 0x77, 0x3c, - 0xdb, 0x74, 0x6d, 0xf6, 0x9f, 0x2e, 0x1b, 0xeb, 0x09, 0x6a, 0x07, 0x31, 0x1d, 0x9b, 0x6c, 0x2e, - 0x52, 0x01, 0xb7, 0xdd, 0x63, 0x27, 0x8a, 0xe5, 0x2d, 0xaf, 0x3a, 0x03, 0xb7, 0x09, 0xa6, 0xff, - 0xdf, 0xcb, 0x50, 0x4d, 0x98, 0xd7, 0xab, 0x3a, 0xf3, 0x4d, 0x58, 0x1d, 0x05, 0xbe, 0xcf, 0x9e, - 0x02, 0x69, 0x7f, 0xd6, 0x53, 0x20, 0xdd, 0xd3, 0x15, 0x19, 0x22, 0x75, 0x0b, 0x9d, 0x63, 0x75, - 0xac, 0xa7, 0x98, 0x67, 0xf2, 0x3e, 0xfa, 0x0d, 0xa8, 0x20, 0xd7, 0x3b, 0xb4, 0x22, 0xd5, 0xcd, - 0x49, 0x1a, 0x67, 0xf4, 0x28, 0xf0, 0x3c, 0xe9, 0xc9, 0x20, 0x83, 0xe7, 0xa6, 0x10, 0xf1, 0x90, - 0xc2, 0x27, 0x93, 0x23, 0xc0, 0x32, 0xed, 0x07, 0x9b, 0x19, 0x6e, 0xcc, 0xbe, 0x00, 0x7b, 0x5c, - 0x79, 0x43, 0x11, 0x8a, 0x9f, 0x83, 0x26, 0x79, 0xc7, 0x24, 0x74, 0x6c, 0x92, 0xe2, 0xe4, 0x25, - 0x85, 0x8d, 0x4c, 0xe6, 0x7d, 0x85, 0x33, 0xd6, 0x98, 0x3a, 0x01, 0xe0, 0x4c, 0x4d, 0x87, 0x61, - 0x62, 0xc5, 0x27, 0xec, 0xfa, 0x5e, 0x35, 0x1a, 0x09, 0x78, 0x1f, 0xa1, 0xe2, 0x13, 0xb8, 0x1a, - 0x3a, 0x91, 0x6b, 0x4f, 0x2d, 0xcf, 0xcc, 0x0f, 0x04, 0x87, 0xe2, 0xdd, 0x50, 0xd8, 0x9d, 0xcc, - 0x80, 0x60, 0xf1, 0x63, 0xeb, 0x9c, 0x0e, 0x81, 0x3c, 0xcf, 0xf1, 0xdc, 0x68, 0x2c, 0x23, 0x32, - 0x36, 0xc6, 0xd6, 0xf9, 0x7e, 0x0a, 0xa5, 0x1b, 0x49, 0x13, 0xcf, 0x9d, 0x89, 0xf2, 0xc7, 0x63, - 0x89, 0x70, 0x83, 0xd1, 0x42, 0x87, 0x55, 0x2c, 0x90, 0xec, 0x9d, 0x89, 0x33, 0x7b, 0xd1, 0xa8, - 0x8d, 0x2d, 0xb6, 0x24, 0x04, 0x67, 0x74, 0x2d, 0x3f, 0xa1, 0x61, 0xff, 0x59, 0xe9, 0xc2, 0x2a, - 0x89, 0xd8, 0x85, 0x96, 0xaa, 0xc6, 0xb3, 0x43, 0x0d, 0x6b, 0x83, 0xdd, 0x87, 0x24, 0x58, 0x8d, - 0xe9, 0x75, 0xe0, 0xa3, 0x54, 0x9c, 0x22, 0x6b, 0x44, 0xb1, 0x42, 0x69, 0x5e, 0x09, 0xd3, 0xc8, - 0x09, 0x99, 0x7d, 0x9a, 0xc4, 0x78, 0x39, 0xd2, 0xdf, 0x2a, 0x82, 0x89, 0x81, 0x3e, 0x45, 0x0e, - 0x7c, 0x0f, 0x34, 0xa2, 0x53, 0x27, 0x2f, 0x51, 0xe0, 0xd3, 0xa9, 0x6d, 0xdd, 0x68, 0x20, 0x5c, - 0x9e, 0xab, 0x44, 0x81, 0x2f, 0x3e, 0x87, 0x4d, 0xa2, 0x9c, 0xb8, 0x13, 0x07, 0xc5, 0x17, 0xe4, - 0x3c, 0xc7, 0x0e, 0xe7, 0x10, 0x77, 0x8a, 0xf7, 0xea, 0xc6, 0x15, 0xc4, 0xef, 0x4b, 0xf4, 0x00, - 0xb1, 0x94, 0xf1, 0x3e, 0xac, 0x67, 0xaa, 0x22, 0x87, 0x86, 0xe3, 0x86, 0xad, 0x25, 0x95, 0x91, - 0xa3, 0xf2, 0x11, 0x6c, 0x48, 0x1f, 0x13, 0x49, 0xce, 0xd3, 0x89, 0x0e, 0x6e, 0x2b, 0x86, 0x90, - 0x38, 0xca, 0xc1, 0xf3, 0x4e, 0xbc, 0x01, 0x75, 0xb2, 0x3e, 0xc8, 0x08, 0x9b, 0x32, 0x76, 0x58, - 0x8d, 0x60, 0x1c, 0xc0, 0x4e, 0xff, 0xeb, 0x05, 0x10, 0xf3, 0x53, 0xf5, 0x65, 0xd1, 0x70, 0x70, - 0xaa, 0xa9, 0x50, 0x14, 0xf8, 0x5b, 0xbc, 0x0b, 0x2b, 0xe3, 0x80, 0xc5, 0xa9, 0xe2, 0x4b, 0x62, - 0xd1, 0x2d, 0x8f, 0x03, 0x12, 0xaf, 0xde, 0x81, 0xb5, 0x51, 0xe0, 0xcb, 0x41, 0x63, 0xee, 0xc2, - 0x6b, 0xad, 0x91, 0x82, 0x91, 0xc7, 0xe8, 0xff, 0x69, 0x01, 0x1a, 0xf9, 0x05, 0x20, 0xde, 0xa6, - 0x28, 0xde, 0xb9, 0x38, 0xe0, 0x79, 0x8a, 0xfe, 0x84, 0xa2, 0x7b, 0x2f, 0xaa, 0xe2, 0xeb, 0x00, - 0x24, 0x24, 0xf0, 0x68, 0xc8, 0xb0, 0xbe, 0x04, 0xa1, 0x11, 0xb8, 0x0d, 0x35, 0x7e, 0xa6, 0x82, - 0xf1, 0x25, 0x1a, 0x2d, 0xce, 0x11, 0x11, 0xc1, 0x47, 0x19, 0x8f, 0x0f, 0xf6, 0xe8, 0x58, 0xbc, - 0x48, 0x13, 0x2a, 0xfd, 0x8f, 0x0b, 0x00, 0xe9, 0x1a, 0xc0, 0x99, 0x78, 0xea, 0x5c, 0xd0, 0x32, - 0x55, 0x5b, 0xec, 0xa9, 0x73, 0xb1, 0x2f, 0xeb, 0x26, 0xdd, 0x0e, 0x23, 0x79, 0xe7, 0xb3, 0x6e, - 0x54, 0xd9, 0xd3, 0x30, 0xe2, 0x20, 0x4b, 0xd2, 0xd1, 0x30, 0x53, 0x75, 0xf6, 0x2d, 0x8c, 0x28, - 0x8c, 0xc6, 0x1a, 0xe7, 0xa6, 0xa1, 0x8f, 0xdc, 0x17, 0x2a, 0x9c, 0x14, 0x3b, 0x46, 0x76, 0x14, - 0x14, 0x09, 0xb9, 0x9c, 0x94, 0x90, 0x2f, 0x61, 0xb1, 0x1b, 0x64, 0x42, 0xa8, 0x7b, 0x4a, 0xe0, - 0x6f, 0xc6, 0x31, 0x6d, 0xb0, 0x89, 0x29, 0xbe, 0xa0, 0x62, 0x48, 0xb1, 0x15, 0xfe, 0x35, 0xba, - 0xab, 0x2a, 0xc3, 0x47, 0x48, 0xb9, 0x3f, 0x09, 0x0b, 0xf1, 0x36, 0x8e, 0xb4, 0xa7, 0xb6, 0x87, - 0xc4, 0x8f, 0xad, 0x6c, 0xac, 0x8e, 0x02, 0x8f, 0xb7, 0x06, 0x04, 0xea, 0xff, 0x5a, 0x19, 0x20, - 0x3d, 0x84, 0xcd, 0xad, 0xd8, 0x42, 0x7e, 0xc5, 0x3e, 0x84, 0xab, 0x32, 0xbe, 0x5f, 0x72, 0x21, - 0xcb, 0xf5, 0xcd, 0x43, 0x4b, 0x89, 0x70, 0x42, 0x62, 0xf9, 0x1a, 0x4d, 0xc7, 0xdf, 0xb2, 0x62, - 0xf1, 0x18, 0x79, 0x64, 0x9a, 0x27, 0xbe, 0x98, 0xbc, 0x74, 0x8a, 0xae, 0xa6, 0xd9, 0x87, 0x17, - 0x13, 0xf1, 0x11, 0x85, 0x49, 0x0a, 0x9d, 0xe8, 0xc4, 0x8c, 0xa3, 0xec, 0xc7, 0xf8, 0x56, 0xfc, - 0xba, 0x44, 0x0e, 0xa3, 0xe4, 0x5b, 0x1f, 0xc1, 0x15, 0xc9, 0x24, 0x66, 0xaa, 0xc7, 0xee, 0x92, - 0xd2, 0x08, 0x9d, 0xad, 0x1d, 0x8d, 0x3c, 0x9d, 0x4c, 0xab, 0xe7, 0x54, 0x2a, 0x38, 0xf2, 0xd8, - 0x19, 0x32, 0x74, 0x36, 0x1d, 0x2f, 0x4b, 0xc9, 0x85, 0x13, 0x42, 0x87, 0x12, 0xae, 0x10, 0x62, - 0xc6, 0x8d, 0x87, 0x8d, 0x07, 0xf4, 0x5c, 0x0c, 0x85, 0x3d, 0x0f, 0x6c, 0xc7, 0x20, 0x9c, 0xf8, - 0x00, 0x77, 0xe8, 0xb4, 0xd9, 0x2a, 0xe6, 0x7f, 0x4d, 0xda, 0xb1, 0x93, 0x86, 0x1a, 0x1c, 0xfd, - 0xff, 0x3d, 0x10, 0x99, 0x9a, 0x2b, 0xea, 0x3a, 0x51, 0xaf, 0x25, 0xd5, 0x96, 0xc4, 0xef, 0x00, - 0x55, 0x31, 0xbd, 0x5a, 0x34, 0x73, 0x16, 0x8b, 0x48, 0x79, 0x1d, 0xe9, 0x4a, 0xda, 0x3a, 0xd3, - 0x8a, 0xcd, 0xf8, 0xc4, 0x21, 0xa3, 0x37, 0xdf, 0x2d, 0x5a, 0x4f, 0x1a, 0xda, 0x8c, 0x87, 0x27, - 0x4e, 0x9b, 0x78, 0x6d, 0xe2, 0xed, 0xb2, 0xf6, 0xc3, 0xde, 0x2e, 0x9f, 0xc1, 0x66, 0xfe, 0xbc, - 0x23, 0xd3, 0xdd, 0xcc, 0xc6, 0x37, 0xb2, 0x77, 0xb6, 0x92, 0x1e, 0xbf, 0x0f, 0xeb, 0x28, 0x13, - 0xe6, 0xf2, 0x4a, 0xc7, 0xb4, 0xb5, 0x13, 0x2b, 0xca, 0x1e, 0x90, 0xe8, 0xff, 0x60, 0x19, 0x1a, - 0xf9, 0x63, 0x69, 0x8e, 0xa7, 0xc6, 0xfb, 0x7c, 0x81, 0xa4, 0x9d, 0x64, 0x37, 0x7f, 0x0d, 0xaa, - 0x7c, 0xb1, 0x0b, 0x99, 0xb1, 0x0a, 0x3d, 0x7d, 0x2a, 0x59, 0xf0, 0xbb, 0xb0, 0x32, 0x39, 0x7d, - 0x05, 0x83, 0x9c, 0x70, 0xd8, 0x93, 0x77, 0x61, 0x65, 0x7a, 0x9a, 0x06, 0x16, 0x5f, 0x48, 0x3a, - 0x3d, 0x95, 0x9e, 0xce, 0xd7, 0xe5, 0x81, 0xba, 0xbc, 0xf5, 0x35, 0x9e, 0x7a, 0xb1, 0x6b, 0xd2, - 0x91, 0x80, 0x5c, 0xda, 0x57, 0x99, 0xa0, 0x43, 0x78, 0x8a, 0x9c, 0x40, 0x87, 0x98, 0xc8, 0xef, - 0x64, 0xb0, 0xa5, 0x44, 0x66, 0x29, 0x1b, 0xc0, 0xc1, 0x95, 0xa8, 0x39, 0xef, 0x82, 0x86, 0x9a, - 0x86, 0xe7, 0x8e, 0xe2, 0x84, 0x6a, 0x85, 0xa8, 0xd6, 0x14, 0x5c, 0x91, 0xbe, 0x05, 0x0d, 0x79, - 0x37, 0x47, 0x11, 0x72, 0x38, 0x80, 0x55, 0x86, 0x2a, 0xb2, 0xcf, 0xe0, 0x5a, 0x60, 0x9f, 0x4e, - 0xd5, 0x35, 0x33, 0x2b, 0x3c, 0x74, 0xe3, 0xd0, 0x4a, 0xe2, 0xcc, 0x55, 0x8c, 0x2b, 0x88, 0x66, - 0x2e, 0xd0, 0x4c, 0x91, 0xe4, 0xab, 0xc8, 0x59, 0x54, 0xf1, 0x40, 0xf5, 0x58, 0x65, 0xa8, 0x2a, - 0xfe, 0x13, 0xb8, 0x9a, 0xc4, 0x3f, 0x71, 0xc6, 0x8e, 0x1f, 0x9b, 0xa1, 0x43, 0xae, 0x5e, 0xf2, - 0x6d, 0x80, 0x0d, 0x15, 0x06, 0x85, 0x90, 0x06, 0xe3, 0xc4, 0x43, 0xb8, 0x32, 0x93, 0x4b, 0x8e, - 0x20, 0xcf, 0xfd, 0xcb, 0xb9, 0x4c, 0x72, 0x30, 0xdb, 0x70, 0x7b, 0x26, 0x4f, 0x2e, 0x9a, 0x27, - 0xe6, 0x5e, 0xa5, 0xdc, 0x37, 0x73, 0xb9, 0xb3, 0x51, 0x2b, 0xb1, 0x98, 0xcf, 0x61, 0x73, 0xa6, - 0x18, 0x1c, 0x11, 0x66, 0x94, 0x1c, 0xb5, 0xf4, 0x4a, 0x2e, 0xff, 0x53, 0xe7, 0x82, 0x19, 0xeb, - 0xcf, 0xe1, 0xe6, 0x4c, 0xc6, 0x5c, 0xf7, 0xcb, 0x6b, 0x78, 0xd7, 0x73, 0x99, 0xfb, 0x99, 0xa1, - 0xc0, 0xa9, 0x8a, 0x9f, 0xe2, 0xe0, 0x88, 0x1a, 0x49, 0x8c, 0xb8, 0x37, 0xf5, 0x28, 0x3e, 0xe2, - 0x3b, 0x8c, 0xe4, 0x33, 0xd5, 0xf5, 0xd9, 0x33, 0x55, 0x22, 0xe4, 0x83, 0xd4, 0xb7, 0x61, 0x4d, - 0x11, 0xf2, 0xc9, 0x52, 0x44, 0x42, 0x4e, 0xd9, 0x58, 0x95, 0x24, 0x74, 0xa0, 0x14, 0xe9, 0x7f, - 0x58, 0x86, 0x7a, 0xd6, 0xe7, 0xe7, 0xc7, 0x5c, 0x7a, 0xfb, 0x49, 0x57, 0x46, 0xa5, 0x9a, 0x47, - 0x7d, 0x82, 0xab, 0xb9, 0x98, 0xa8, 0x79, 0xcd, 0x69, 0x8c, 0x12, 0x0d, 0xa9, 0x5e, 0x81, 0xa7, - 0x22, 0x04, 0x31, 0xff, 0xc6, 0x9d, 0x4b, 0x06, 0x07, 0xfa, 0x48, 0x06, 0x20, 0xa3, 0x90, 0x90, - 0x14, 0x9c, 0x61, 0xde, 0x07, 0xba, 0xae, 0x22, 0x42, 0x52, 0x30, 0x8a, 0x87, 0xb0, 0x96, 0x86, - 0x7b, 0xe2, 0x2c, 0xcb, 0x73, 0x59, 0x56, 0x93, 0x58, 0x4f, 0x32, 0xc8, 0xf8, 0xaa, 0x1b, 0x99, - 0x81, 0x67, 0xab, 0xb0, 0x7d, 0x2b, 0xca, 0x23, 0xb3, 0xef, 0xd9, 0x32, 0x34, 0x1f, 0xd3, 0xf8, - 0xce, 0x99, 0xa2, 0x49, 0xbc, 0x36, 0x7b, 0xce, 0x99, 0xa4, 0x79, 0x00, 0x97, 0xb1, 0xb9, 0x72, - 0x4d, 0x70, 0xb0, 0x8e, 0x20, 0x94, 0x6b, 0x08, 0x99, 0x1b, 0x2f, 0xa1, 0x81, 0x44, 0x64, 0xae, - 0x6b, 0x86, 0xc1, 0x99, 0xe9, 0x4f, 0xc7, 0x87, 0xcc, 0xe6, 0xa5, 0x30, 0x2f, 0xaf, 0x6b, 0x1a, - 0xc1, 0x59, 0x8f, 0x50, 0xd8, 0x5f, 0xf7, 0x61, 0x5d, 0xad, 0xd2, 0x11, 0x9d, 0x66, 0xa9, 0x1b, - 0xc7, 0x65, 0x63, 0x8d, 0x11, 0x4d, 0x82, 0x23, 0xed, 0xbb, 0x09, 0x2d, 0x5f, 0x1c, 0x25, 0x5a, - 0x5e, 0x3d, 0x8d, 0x38, 0xbd, 0x35, 0x8a, 0xa4, 0x2d, 0xb8, 0x15, 0x87, 0xd6, 0xe8, 0xd4, 0x7c, - 0xd9, 0xf2, 0x91, 0x17, 0x55, 0x5f, 0x23, 0xaa, 0xe6, 0xc2, 0xc5, 0xf3, 0x63, 0x56, 0x5f, 0xe3, - 0xd5, 0xab, 0x4f, 0xff, 0x83, 0x12, 0xac, 0xcf, 0x39, 0x4b, 0xa1, 0x42, 0x9d, 0xbe, 0x2d, 0x86, - 0x3f, 0x51, 0x78, 0x1e, 0x5b, 0xf1, 0xe8, 0x04, 0x75, 0xb4, 0x23, 0xf7, 0x5c, 0xbd, 0xab, 0x46, - 0xb0, 0x7d, 0x02, 0x21, 0x2f, 0x65, 0xb9, 0x8a, 0xdd, 0x49, 0x59, 0xc8, 0x61, 0x91, 0x8d, 0x7d, - 0xb3, 0x55, 0xc8, 0x8f, 0xf9, 0x2b, 0x22, 0x1c, 0xf2, 0xe3, 0x06, 0x54, 0xfd, 0x80, 0xae, 0x11, - 0x4f, 0x4e, 0x25, 0xdf, 0x5e, 0xf1, 0x83, 0xb8, 0xef, 0xef, 0x9f, 0x22, 0x83, 0x9a, 0x46, 0x74, - 0x11, 0xec, 0xd0, 0x09, 0xa3, 0x13, 0x37, 0x71, 0x5b, 0x60, 0x61, 0xe1, 0xf2, 0x34, 0x72, 0xf6, - 0x12, 0x9c, 0xb4, 0x37, 0x35, 0xa1, 0x4e, 0x33, 0xd9, 0x1f, 0x05, 0xe4, 0x08, 0xc5, 0x8e, 0xf7, - 0xb7, 0x16, 0x78, 0x88, 0x3d, 0x75, 0x2e, 0xda, 0x92, 0xca, 0xc0, 0x0d, 0x41, 0x25, 0xc4, 0xbb, - 0x14, 0xcd, 0xe9, 0xd0, 0x49, 0x6d, 0xa4, 0x79, 0x36, 0x50, 0x25, 0x2c, 0xed, 0x42, 0x4f, 0xe1, - 0x3a, 0x6f, 0x25, 0xe3, 0x49, 0xe0, 0xe3, 0x78, 0xa4, 0x19, 0xa3, 0xbc, 0x4f, 0x71, 0x66, 0x0b, - 0xbb, 0x4a, 0x5b, 0x8d, 0xcc, 0xb1, 0xaf, 0xca, 0xc2, 0x6d, 0x07, 0xd8, 0x29, 0x80, 0x3a, 0x0c, - 0xe6, 0x3d, 0xb3, 0x08, 0x4b, 0xeb, 0xe9, 0x5d, 0xd0, 0xa2, 0x91, 0xe5, 0x59, 0x61, 0x46, 0x7d, - 0x66, 0x56, 0xbf, 0xc6, 0xf0, 0x54, 0x71, 0x78, 0x1d, 0x60, 0x3c, 0x8d, 0x62, 0x93, 0xfc, 0x5c, - 0x54, 0x18, 0x69, 0x84, 0xd0, 0x49, 0x88, 0x7e, 0x13, 0x96, 0x3b, 0x89, 0x53, 0x5c, 0x12, 0x93, - 0xa1, 0x28, 0x9f, 0x2f, 0x0a, 0xa0, 0xca, 0xde, 0x12, 0x7b, 0xd6, 0x44, 0xdc, 0x87, 0xe2, 0xd8, - 0x9a, 0xc8, 0xdb, 0x3a, 0x9b, 0xc9, 0x95, 0x26, 0xc6, 0x3e, 0xd8, 0xb3, 0x26, 0x6c, 0xe7, 0x45, - 0xa2, 0x1b, 0x9f, 0x41, 0x45, 0x01, 0x7e, 0x5a, 0x24, 0xab, 0x25, 0xa8, 0x6e, 0x67, 0x9d, 0x5a, - 0x51, 0x17, 0x8e, 0xc3, 0xa9, 0x4f, 0x4d, 0x94, 0x0f, 0xb5, 0x8c, 0x2c, 0x7f, 0x28, 0x41, 0x8a, - 0xa1, 0xbe, 0xe4, 0xd6, 0x0b, 0x31, 0xd4, 0x9b, 0x00, 0x72, 0x79, 0xba, 0xb6, 0x92, 0xc3, 0x2b, - 0x21, 0x2e, 0xcc, 0x8e, 0x7d, 0xbe, 0xf8, 0x8e, 0x71, 0xe9, 0xc7, 0xdf, 0x31, 0x2e, 0x2f, 0xbc, - 0x63, 0xfc, 0x76, 0x2a, 0x7a, 0xd3, 0xce, 0x66, 0x9f, 0xcb, 0x47, 0x21, 0x94, 0xa0, 0x8d, 0x3b, - 0x9a, 0x7d, 0x2e, 0xbe, 0x84, 0x86, 0x6a, 0x9d, 0x2c, 0x0f, 0x72, 0x71, 0x05, 0x25, 0x8e, 0xbd, - 0x60, 0x57, 0xe3, 0x6c, 0x32, 0xbf, 0x51, 0xd4, 0x5e, 0x71, 0x8f, 0xf9, 0x6f, 0x16, 0x40, 0x48, - 0x1f, 0xa6, 0x9d, 0xa9, 0xe7, 0x0d, 0x9d, 0x73, 0xda, 0x8f, 0xee, 0xc3, 0x7a, 0xee, 0x82, 0x4e, - 0x46, 0xd3, 0x59, 0xcb, 0xdc, 0xc8, 0x51, 0xc1, 0x9a, 0xe7, 0x42, 0xf3, 0x2e, 0x2d, 0x0c, 0xcd, - 0xbb, 0x38, 0xe4, 0xef, 0x6d, 0xa8, 0x65, 0x03, 0xdb, 0xb2, 0xea, 0x0b, 0x56, 0x12, 0xd3, 0x56, - 0xff, 0x9f, 0x96, 0x00, 0x52, 0x3f, 0xab, 0xbf, 0xec, 0x0b, 0xe2, 0x0b, 0x86, 0xa4, 0xb8, 0x68, - 0x48, 0xee, 0x81, 0x96, 0xa5, 0xcb, 0x44, 0x58, 0x6e, 0xa4, 0x84, 0x4a, 0x05, 0x74, 0xa3, 0x6c, - 0x10, 0x5b, 0x0a, 0x9d, 0x26, 0xef, 0xd4, 0x32, 0x92, 0x45, 0x50, 0xc9, 0xb0, 0x2a, 0x6e, 0xc4, - 0x92, 0x00, 0x49, 0xaf, 0x2a, 0x27, 0x79, 0x58, 0x04, 0xd3, 0x38, 0xb9, 0x7f, 0xb3, 0x22, 0xa5, - 0x57, 0x59, 0xd2, 0x73, 0x46, 0xab, 0xfb, 0x37, 0x9f, 0x42, 0xf5, 0x68, 0xea, 0x79, 0x66, 0xec, - 0x9c, 0xab, 0x3b, 0x41, 0x9b, 0x39, 0x17, 0xb5, 0xcc, 0xf0, 0x1a, 0x95, 0x23, 0x99, 0xd0, 0xff, - 0xd7, 0x65, 0x28, 0xb3, 0x61, 0xfc, 0x33, 0xa8, 0x46, 0xf1, 0x38, 0xce, 0xde, 0x16, 0x91, 0xae, - 0x47, 0x84, 0xa7, 0xcb, 0x1e, 0xb4, 0x6d, 0xb0, 0xcc, 0x83, 0xb4, 0xc4, 0xeb, 0x36, 0xa0, 0x1c, - 0xc5, 0xce, 0x24, 0x92, 0x71, 0x57, 0x38, 0x21, 0xee, 0x40, 0xd9, 0x0f, 0x6c, 0x27, 0xca, 0x47, - 0x57, 0xe9, 0xa1, 0x36, 0xc6, 0x08, 0xa1, 0xc3, 0x72, 0x32, 0xe2, 0x73, 0x37, 0x36, 0x18, 0x43, - 0x31, 0xdb, 0x1c, 0x0b, 0xb9, 0xaf, 0x8a, 0x58, 0x9d, 0xa4, 0x51, 0xed, 0xa0, 0xf3, 0x34, 0xeb, - 0x58, 0xc5, 0xf0, 0x97, 0x49, 0x71, 0x07, 0x6a, 0xf8, 0xf3, 0x79, 0xe8, 0xc6, 0xce, 0xe0, 0x91, - 0x92, 0x29, 0x32, 0x20, 0x71, 0x0b, 0xc0, 0x76, 0x62, 0x67, 0x14, 0x0f, 0xbe, 0xf3, 0x94, 0x81, - 0x30, 0x03, 0x11, 0x5f, 0x82, 0x38, 0xb4, 0x46, 0xa7, 0xc7, 0x7c, 0xe1, 0xe8, 0xbb, 0xa9, 0x13, - 0xba, 0x09, 0xe3, 0xae, 0x65, 0x3a, 0xc5, 0x58, 0x4f, 0xc9, 0x7e, 0xc9, 0x54, 0xe2, 0x2a, 0x2c, - 0x8f, 0xad, 0xf3, 0xed, 0x60, 0x22, 0x2d, 0xe8, 0x32, 0x25, 0x1e, 0xc1, 0x55, 0x94, 0x51, 0xb2, - 0xd1, 0x47, 0xa4, 0x57, 0x20, 0x73, 0x68, 0x94, 0x60, 0xd2, 0xb0, 0x23, 0xf2, 0xec, 0x7a, 0x0b, - 0x36, 0x94, 0x81, 0xdc, 0x76, 0x26, 0x8e, 0x6f, 0x3b, 0xfe, 0xc8, 0x4d, 0x62, 0x7e, 0xce, 0x4d, - 0xfe, 0xcb, 0x92, 0x78, 0x3b, 0x43, 0x2b, 0xde, 0x84, 0x55, 0xfc, 0x70, 0xe8, 0xc4, 0xd3, 0xd0, - 0xc7, 0xbd, 0x8f, 0x25, 0x0a, 0x14, 0x10, 0x0d, 0x05, 0x63, 0x37, 0x67, 0x99, 0x48, 0xef, 0x49, - 0x95, 0x8d, 0xd5, 0x04, 0x4a, 0xbe, 0xf9, 0x8f, 0xe0, 0x2a, 0x3b, 0x50, 0x46, 0xdf, 0x79, 0x52, - 0xce, 0x32, 0xd3, 0x1b, 0x23, 0x15, 0xe3, 0x32, 0x61, 0x07, 0xdf, 0x79, 0x2c, 0x6a, 0xf1, 0x5e, - 0xdf, 0x87, 0xab, 0x53, 0x3f, 0x74, 0xa2, 0xc0, 0x7b, 0x91, 0x04, 0x57, 0x3e, 0x71, 0x51, 0x38, - 0xd6, 0xb2, 0xd7, 0xf2, 0x0e, 0x12, 0x1a, 0x92, 0xe9, 0x77, 0x5d, 0x3f, 0x36, 0x36, 0xa6, 0xf3, - 0xc0, 0x48, 0xf4, 0x60, 0x43, 0xbd, 0xfd, 0x94, 0xeb, 0x15, 0x16, 0xcd, 0x5f, 0x53, 0x17, 0x35, - 0x88, 0x62, 0xdf, 0xb3, 0xfc, 0xa4, 0x3b, 0x2e, 0x8c, 0xcb, 0x32, 0x63, 0xb6, 0x87, 0xf4, 0xdf, - 0x83, 0xd5, 0xdc, 0xec, 0x9e, 0x73, 0x32, 0x1a, 0xb4, 0xbb, 0xed, 0xd6, 0x90, 0xbd, 0x06, 0xa4, - 0xdb, 0xcb, 0x52, 0xc6, 0x3f, 0xa6, 0x94, 0xf1, 0x26, 0x28, 0x93, 0x77, 0x4d, 0xdb, 0x78, 0xd2, - 0x26, 0x87, 0x8f, 0xa2, 0x56, 0xd4, 0xff, 0xd6, 0x12, 0xac, 0x0f, 0x43, 0xcb, 0x8f, 0x2c, 0xd6, - 0xa2, 0xfd, 0x38, 0x0c, 0x3c, 0xf1, 0x25, 0x54, 0xe2, 0x91, 0x97, 0x5d, 0x6e, 0xb7, 0x15, 0x73, - 0x9f, 0x21, 0x7d, 0x30, 0x1c, 0xb1, 0xc3, 0xf4, 0x4a, 0xcc, 0x3f, 0xc4, 0x07, 0x50, 0x3e, 0x74, - 0x8e, 0x5d, 0x5f, 0xee, 0x6f, 0x57, 0x66, 0x33, 0x6e, 0x21, 0x72, 0xf7, 0x92, 0xc1, 0x54, 0xe2, - 0x23, 0x58, 0x96, 0xe7, 0x4e, 0xc5, 0xec, 0xbd, 0x85, 0xec, 0x87, 0x10, 0xbb, 0x7b, 0xc9, 0x90, - 0x74, 0xe2, 0x33, 0xa8, 0x84, 0x81, 0x7c, 0x50, 0xaa, 0x94, 0x65, 0x26, 0x69, 0x1e, 0x43, 0xe2, - 0x77, 0x2f, 0x19, 0x09, 0xad, 0xfe, 0x00, 0x56, 0x64, 0x65, 0xb1, 0x1b, 0xb6, 0xda, 0x4f, 0x3a, - 0xb2, 0x07, 0x5b, 0xfd, 0xbd, 0xbd, 0xce, 0x90, 0xe3, 0x4d, 0x1b, 0xfd, 0x6e, 0x77, 0xab, 0xd9, - 0x7a, 0xaa, 0x2d, 0x6d, 0x55, 0x60, 0x99, 0x17, 0x81, 0xfe, 0x2f, 0x16, 0x60, 0x6d, 0xa6, 0x01, - 0xe2, 0x31, 0x94, 0xc8, 0x1a, 0xca, 0xdd, 0x73, 0x77, 0x61, 0x2b, 0x33, 0x69, 0xb6, 0xf5, 0x60, - 0x0e, 0xfd, 0x0b, 0x68, 0xe4, 0xe1, 0x19, 0x9f, 0x94, 0x55, 0xa8, 0x1a, 0xed, 0xe6, 0xb6, 0xd9, - 0xef, 0x75, 0xbf, 0x61, 0x3f, 0x31, 0x4a, 0x3e, 0x37, 0x3a, 0xe4, 0x0c, 0xf2, 0xbb, 0xa0, 0xcd, - 0x76, 0x8c, 0x78, 0x02, 0x6b, 0x28, 0xcb, 0x79, 0x0e, 0x3b, 0xc0, 0xa5, 0x43, 0x76, 0x6b, 0x41, - 0x4f, 0x4a, 0x32, 0xbe, 0x4d, 0x3d, 0xca, 0xa5, 0xf5, 0xdf, 0x03, 0x31, 0xdf, 0x83, 0x7f, 0x79, - 0xc5, 0xff, 0x5f, 0x05, 0x28, 0xe1, 0x84, 0x17, 0x6f, 0x42, 0x99, 0x8f, 0x40, 0x0b, 0xd9, 0xfb, - 0x7a, 0xc4, 0xb3, 0x70, 0x5a, 0x10, 0x4e, 0xbc, 0x07, 0xc5, 0x78, 0xa4, 0x22, 0x4b, 0x5f, 0x7b, - 0xc9, 0xe4, 0xdb, 0xbd, 0x64, 0x20, 0x95, 0xb8, 0x07, 0x45, 0xdb, 0x56, 0x71, 0xc9, 0x32, 0x07, - 0xe4, 0xdb, 0xce, 0x11, 0xdd, 0xd8, 0xa4, 0xb7, 0x94, 0x90, 0x44, 0xbc, 0x05, 0x45, 0x7b, 0xe4, - 0xe5, 0x83, 0xcc, 0xf1, 0xd9, 0x75, 0x52, 0xa0, 0x3d, 0xf2, 0x50, 0xaf, 0x8b, 0xc3, 0x0b, 0x33, - 0x9c, 0xfa, 0x14, 0x96, 0x43, 0x3d, 0x08, 0x56, 0x43, 0x59, 0x71, 0x4a, 0xb1, 0x3d, 0x22, 0x19, - 0xa2, 0x56, 0xbe, 0xf8, 0xa3, 0x6c, 0x81, 0x6e, 0x24, 0xdf, 0x06, 0xda, 0x5a, 0x06, 0x7a, 0x4c, - 0x5a, 0x7f, 0x9f, 0x1e, 0x97, 0x41, 0x35, 0x5e, 0x57, 0xbf, 0x16, 0xbc, 0xbc, 0x29, 0x31, 0xfa, - 0x3f, 0x2d, 0x42, 0x2d, 0x53, 0x1f, 0xf1, 0x09, 0x54, 0xec, 0xfc, 0x42, 0xbc, 0x3e, 0x57, 0xe9, - 0x07, 0xdb, 0x6a, 0x09, 0xda, 0x72, 0x7a, 0x93, 0x37, 0x7e, 0x6c, 0xbe, 0xb0, 0x42, 0x97, 0xaf, - 0x18, 0x2f, 0x65, 0xdd, 0xe2, 0x07, 0x4e, 0xfc, 0x4c, 0x61, 0x76, 0x2f, 0x19, 0xf5, 0x28, 0x93, - 0x26, 0xcb, 0x97, 0x6c, 0x52, 0x31, 0x1b, 0xbb, 0x58, 0x36, 0x6b, 0xf7, 0x92, 0xa1, 0xf0, 0x48, - 0xea, 0x9c, 0x3b, 0xa3, 0x69, 0xac, 0x2c, 0x5f, 0xab, 0xaa, 0x41, 0x04, 0xa4, 0xe7, 0x71, 0xf9, - 0xa7, 0x78, 0x88, 0x7b, 0x9a, 0xe5, 0x79, 0x01, 0x89, 0xc4, 0xe5, 0xec, 0xb1, 0xd3, 0x76, 0x02, - 0xe7, 0xe7, 0x78, 0x55, 0x4a, 0xbc, 0x0d, 0xe5, 0x20, 0x3e, 0x71, 0x94, 0xa6, 0xae, 0x9e, 0xa9, - 0x41, 0xd0, 0x76, 0xab, 0x8b, 0x33, 0x85, 0xd0, 0xfa, 0x1f, 0x15, 0x60, 0x45, 0xf6, 0x80, 0x58, - 0x87, 0xd5, 0x41, 0x7b, 0x68, 0x3e, 0x6b, 0x1a, 0x9d, 0xe6, 0x56, 0xb7, 0x2d, 0x63, 0xe3, 0x3d, - 0x31, 0x9a, 0x3d, 0xc9, 0x20, 0x8d, 0xf6, 0xb3, 0xfe, 0xd3, 0x36, 0x3b, 0x7a, 0x6d, 0xb7, 0x7b, - 0xdf, 0x68, 0x45, 0x76, 0x8a, 0x6c, 0xef, 0x37, 0x0d, 0xe4, 0x95, 0x35, 0x58, 0x69, 0x7f, 0xdd, - 0x6e, 0x1d, 0x10, 0xb3, 0x6c, 0x00, 0x6c, 0xb7, 0x9b, 0xdd, 0x6e, 0xbf, 0x85, 0xcc, 0x73, 0x59, - 0x08, 0x68, 0xb4, 0x8c, 0x76, 0x73, 0xd8, 0x36, 0x9b, 0xad, 0x56, 0xff, 0xa0, 0x37, 0xd4, 0x56, - 0xf0, 0x8b, 0xcd, 0xee, 0xb0, 0x6d, 0x24, 0x20, 0x7a, 0x7c, 0x6f, 0xdb, 0xe8, 0xef, 0x27, 0x90, - 0xea, 0x56, 0x15, 0x56, 0x46, 0x3c, 0x56, 0xfa, 0x7f, 0xb2, 0xce, 0xfe, 0x0e, 0xe9, 0xd4, 0x14, - 0x9f, 0x43, 0xc5, 0xb6, 0x73, 0x63, 0x7c, 0x73, 0xd1, 0x14, 0x7e, 0xb0, 0x6d, 0xab, 0x61, 0xe6, - 0x1f, 0xe2, 0x0d, 0xb5, 0x90, 0x96, 0xe6, 0x16, 0x92, 0x5a, 0x46, 0x3f, 0x87, 0x35, 0xf9, 0xf6, - 0x49, 0x72, 0x54, 0x9a, 0x5b, 0x25, 0x2d, 0x42, 0x6e, 0x4b, 0xdc, 0xee, 0x25, 0xa3, 0x31, 0xca, - 0x41, 0xc4, 0xcf, 0xa0, 0x61, 0xf1, 0x01, 0x64, 0xf6, 0xa8, 0x35, 0x11, 0xf6, 0x9b, 0x74, 0xfe, - 0x98, 0x66, 0x5f, 0xb5, 0xb2, 0x00, 0x9c, 0x88, 0x76, 0x18, 0x4c, 0xd2, 0xcc, 0xe5, 0xdc, 0xfd, - 0x8c, 0x30, 0x98, 0x64, 0xf2, 0xd6, 0xed, 0x4c, 0x5a, 0x7c, 0x06, 0x75, 0x59, 0xf3, 0xd4, 0xd8, - 0x9e, 0x2c, 0x59, 0xae, 0x36, 0x09, 0xef, 0xbb, 0x97, 0x8c, 0xda, 0x28, 0x4d, 0x8a, 0x47, 0x28, - 0xb1, 0xa7, 0xaa, 0xce, 0x4a, 0x76, 0xae, 0x51, 0x6d, 0x55, 0x2e, 0xb0, 0x92, 0x94, 0xf8, 0x08, - 0x80, 0xea, 0xc9, 0x79, 0x2a, 0xb9, 0x9b, 0x8b, 0x61, 0x30, 0x51, 0x59, 0xaa, 0xb6, 0x4a, 0x64, - 0xaa, 0xc7, 0x16, 0xc0, 0xea, 0x7c, 0xf5, 0x58, 0x28, 0x48, 0xaa, 0xc7, 0xc6, 0xc0, 0xa4, 0x7a, - 0x9c, 0x0d, 0xe6, 0xaa, 0xa7, 0x72, 0x71, 0xf5, 0x38, 0x93, 0xaa, 0x1e, 0xe7, 0xa9, 0xcd, 0x56, - 0x4f, 0x65, 0xa1, 0xea, 0x71, 0x8e, 0x9f, 0xcd, 0xe9, 0x68, 0xf5, 0x97, 0xea, 0x68, 0x38, 0x6c, - 0x79, 0x2d, 0xed, 0x67, 0xd0, 0x88, 0x4e, 0x82, 0xb3, 0x0c, 0x03, 0x59, 0xcd, 0xe6, 0x1e, 0x9c, - 0x04, 0x67, 0x59, 0x0e, 0xb2, 0x1a, 0x65, 0x01, 0x58, 0x5b, 0x6e, 0x22, 0xdd, 0xa5, 0x68, 0x64, - 0x6b, 0x4b, 0x2d, 0x7c, 0xe6, 0x3a, 0x67, 0x58, 0x5b, 0x4b, 0x25, 0xb0, 0x53, 0xd2, 0x83, 0x87, - 0x48, 0x1e, 0x25, 0xe4, 0xee, 0xfb, 0xc9, 0x2f, 0x41, 0x72, 0x04, 0x11, 0xe1, 0xdc, 0x9a, 0xfa, - 0xd9, 0x6c, 0x5a, 0x76, 0x6e, 0x1d, 0xf8, 0xb9, 0x8c, 0x75, 0x26, 0x95, 0x59, 0xd3, 0x55, 0x11, - 0x39, 0xdf, 0x4d, 0x1d, 0x7f, 0xe4, 0xc8, 0x7b, 0xbd, 0xb9, 0x55, 0x31, 0x90, 0xb8, 0x74, 0x55, - 0x28, 0x48, 0x32, 0xaf, 0x93, 0xec, 0x62, 0x76, 0x5e, 0x67, 0x32, 0xd3, 0xbc, 0x4e, 0xb2, 0x26, - 0x0b, 0x2a, 0xc9, 0x7b, 0x79, 0x6e, 0x41, 0x65, 0x32, 0xf3, 0x82, 0x4a, 0x72, 0x3f, 0x02, 0x39, - 0x9b, 0xb8, 0x73, 0x73, 0xb7, 0x7f, 0xb9, 0xd6, 0xb2, 0x77, 0x61, 0x94, 0xa4, 0x70, 0xae, 0x86, - 0x0e, 0xea, 0x84, 0x72, 0x2a, 0x5c, 0xc9, 0xce, 0x55, 0x83, 0x30, 0xc9, 0x52, 0x0a, 0xd3, 0x64, - 0xe6, 0x63, 0x13, 0x37, 0x0e, 0x37, 0xed, 0xf9, 0x8f, 0xed, 0xbb, 0x71, 0x98, 0x7e, 0x0c, 0x53, - 0xe2, 0x03, 0xa0, 0x69, 0xc8, 0x59, 0x9c, 0x2c, 0xeb, 0xc6, 0x6e, 0x91, 0x19, 0x2a, 0xb6, 0xfc, - 0x8d, 0x93, 0x45, 0x7e, 0x63, 0x64, 0x8f, 0x36, 0x8f, 0xb2, 0x93, 0x85, 0x3f, 0xd1, 0xda, 0x6e, - 0xe1, 0x64, 0x61, 0xa2, 0x96, 0x3d, 0x12, 0xf7, 0x81, 0x72, 0x13, 0xfd, 0x71, 0x76, 0xdf, 0xc1, - 0xf2, 0x99, 0x7a, 0x05, 0x09, 0x90, 0x16, 0x5b, 0xe0, 0x05, 0xbe, 0x6a, 0xf8, 0x49, 0xae, 0x05, - 0x88, 0x48, 0x98, 0xc1, 0x28, 0x49, 0xe9, 0xff, 0xf2, 0x32, 0xac, 0x48, 0x5e, 0x2b, 0x2e, 0xc3, - 0x9a, 0x64, 0xf9, 0xdb, 0xcd, 0x61, 0x73, 0xab, 0x39, 0x40, 0x21, 0x4d, 0x40, 0x83, 0x79, 0x7e, - 0x02, 0x2b, 0xe0, 0x3e, 0x40, 0x4c, 0x3f, 0x01, 0x2d, 0xe1, 0x3e, 0x20, 0xf3, 0xb2, 0x3b, 0x6d, - 0x51, 0xac, 0x41, 0x8d, 0x33, 0x32, 0x80, 0x42, 0xf5, 0x52, 0x2e, 0x4e, 0x97, 0x33, 0x59, 0xd8, - 0x21, 0x7a, 0x39, 0xcd, 0xc2, 0x80, 0x95, 0x24, 0x8b, 0xf2, 0x98, 0x16, 0xd0, 0x18, 0x1a, 0x07, - 0xbd, 0x56, 0xfa, 0x9d, 0x2a, 0x85, 0x57, 0xe5, 0x62, 0x9e, 0x75, 0xda, 0xcf, 0x35, 0xc0, 0x4c, - 0x5c, 0x0a, 0xa5, 0xc9, 0x93, 0x9a, 0x0a, 0xa1, 0x64, 0x5d, 0x5c, 0x83, 0xcb, 0x83, 0xdd, 0xfe, - 0x73, 0x93, 0x33, 0x25, 0x4d, 0x58, 0x15, 0x1b, 0xa0, 0x65, 0x10, 0x5c, 0x7c, 0x03, 0x3f, 0x49, - 0x50, 0x45, 0x38, 0xd0, 0xd6, 0xe8, 0x71, 0x16, 0x84, 0x0d, 0x79, 0xdf, 0xd5, 0xb0, 0x29, 0x9c, - 0xb5, 0xdf, 0x3d, 0xd8, 0xeb, 0x0d, 0xb4, 0x75, 0xac, 0x04, 0x41, 0xb8, 0xe6, 0x22, 0x29, 0x26, - 0xdd, 0xad, 0x2f, 0xd3, 0x06, 0x8e, 0xb0, 0xe7, 0x4d, 0xa3, 0xd7, 0xe9, 0x3d, 0x19, 0x68, 0x1b, - 0x49, 0xc9, 0x6d, 0xc3, 0xe8, 0x1b, 0x03, 0xed, 0x4a, 0x02, 0x18, 0x0c, 0x9b, 0xc3, 0x83, 0x81, - 0x76, 0x35, 0xa9, 0xe5, 0xbe, 0xd1, 0x6f, 0xb5, 0x07, 0x83, 0x6e, 0x67, 0x30, 0xd4, 0xae, 0x89, - 0x2b, 0xb0, 0x9e, 0xd6, 0x48, 0x11, 0x6f, 0x66, 0x2a, 0x6a, 0x3c, 0x69, 0x0f, 0xb5, 0xeb, 0x49, - 0x35, 0x5a, 0xfd, 0x6e, 0xb7, 0x49, 0xbe, 0xda, 0x37, 0x90, 0x88, 0xae, 0x14, 0xc8, 0xd6, 0xbc, - 0x86, 0xf5, 0x3a, 0xe8, 0x65, 0x41, 0x37, 0x33, 0x53, 0x63, 0xd0, 0xfe, 0xe5, 0x41, 0xbb, 0xd7, - 0x6a, 0x6b, 0xaf, 0xa7, 0x53, 0x23, 0x81, 0xdd, 0x4a, 0xa6, 0x46, 0x02, 0xba, 0x9d, 0x7c, 0x53, - 0x81, 0x06, 0xda, 0x9d, 0xa4, 0xc3, 0x0e, 0xf6, 0x9f, 0x18, 0xcd, 0xed, 0xb6, 0xa6, 0x23, 0xc4, - 0x68, 0xf7, 0x9a, 0x7b, 0x6a, 0x60, 0xdf, 0xcc, 0x0c, 0xec, 0x7e, 0x67, 0x68, 0x68, 0x77, 0x93, - 0x81, 0xa4, 0xe4, 0x5b, 0xe2, 0x35, 0xb8, 0x96, 0x9d, 0x72, 0xe6, 0xf3, 0xce, 0x70, 0x57, 0x7a, - 0xfe, 0xbf, 0xcd, 0xce, 0xe3, 0x84, 0x6c, 0x6d, 0xb7, 0xf8, 0x8a, 0x03, 0xe5, 0xc5, 0xd4, 0x3d, - 0xbd, 0x54, 0x79, 0x43, 0x7b, 0xe3, 0xfe, 0x9a, 0xec, 0x8a, 0x5e, 0x8f, 0xae, 0x8f, 0x0c, 0xb6, - 0xea, 0x28, 0xb9, 0x29, 0x11, 0x44, 0xff, 0x0a, 0x44, 0xf6, 0x39, 0x6d, 0x19, 0x20, 0x41, 0x40, - 0xe9, 0x28, 0x0c, 0xc6, 0xca, 0xed, 0x04, 0x7f, 0x8b, 0x3b, 0x50, 0x9b, 0x4c, 0x0f, 0xc9, 0xd7, - 0x3d, 0x0d, 0x8c, 0x9d, 0x05, 0xe9, 0xff, 0x4e, 0x01, 0x1a, 0x79, 0xf1, 0x83, 0x8e, 0x53, 0x8e, - 0x4c, 0x3f, 0x88, 0xf9, 0x7d, 0xac, 0x28, 0x79, 0xe5, 0xfa, 0xa8, 0x17, 0xc4, 0xf4, 0x40, 0x56, - 0x94, 0xf3, 0xfa, 0x5a, 0x9a, 0xf1, 0xfa, 0xea, 0xc0, 0xe5, 0xdc, 0xeb, 0xe5, 0xb9, 0xd7, 0xc9, - 0xa4, 0x92, 0x38, 0x5f, 0x7f, 0x43, 0x44, 0xf3, 0x6d, 0x92, 0xe1, 0xcd, 0x4b, 0x69, 0x78, 0xf3, - 0x5d, 0x58, 0xcd, 0x49, 0x3b, 0x64, 0x2d, 0x3b, 0xca, 0xd7, 0xb4, 0xe2, 0x1e, 0xbd, 0xba, 0x9a, - 0xfa, 0xdf, 0x29, 0x40, 0x3d, 0x2b, 0xfb, 0xfc, 0xc6, 0x25, 0xd1, 0xbd, 0x45, 0xf9, 0xdb, 0x74, - 0x6d, 0xf5, 0xac, 0x95, 0x02, 0x91, 0x4b, 0x65, 0x8d, 0x4f, 0xa6, 0x76, 0x4e, 0x07, 0x49, 0x73, - 0xb2, 0x20, 0xf2, 0x94, 0x3b, 0x71, 0x46, 0xa7, 0x3b, 0x4f, 0x91, 0x40, 0x79, 0xca, 0x25, 0x10, - 0xfd, 0x36, 0x54, 0x77, 0x4e, 0xd5, 0x35, 0xce, 0xec, 0x2b, 0x71, 0x55, 0x8e, 0xa6, 0xae, 0xff, - 0x71, 0x01, 0x1a, 0xa9, 0x6d, 0x88, 0x1c, 0xf5, 0xf9, 0xd5, 0x7b, 0x9e, 0x0e, 0x4b, 0xf6, 0x61, - 0xea, 0x0d, 0xb9, 0x94, 0xf5, 0x86, 0x7c, 0x53, 0x16, 0x56, 0xcc, 0x32, 0xfd, 0xe4, 0x5b, 0x32, - 0x56, 0xfb, 0x23, 0xa8, 0xe3, 0x7f, 0xc3, 0x39, 0x72, 0xc2, 0xd0, 0xb1, 0xf3, 0xf1, 0xaf, 0x52, - 0xe2, 0x1c, 0x11, 0x69, 0x79, 0xc9, 0x93, 0x0d, 0x0b, 0x1f, 0x95, 0xa1, 0xc7, 0x8e, 0xfe, 0x8f, - 0x22, 0xd4, 0x32, 0x92, 0xe4, 0x8f, 0x9a, 0x7e, 0x37, 0xa1, 0x9a, 0x3e, 0x71, 0x22, 0x03, 0x64, - 0x27, 0x80, 0xdc, 0x58, 0x15, 0x67, 0xc6, 0x8a, 0x9c, 0x92, 0x29, 0x2a, 0x67, 0xea, 0x94, 0x4c, - 0xc9, 0xbc, 0x51, 0xbc, 0xfc, 0x8a, 0xd3, 0xd3, 0x8f, 0xa1, 0x9e, 0xb1, 0x68, 0x2b, 0xf7, 0xc5, - 0x59, 0xfa, 0x5a, 0x6a, 0xdd, 0x8e, 0xc4, 0x15, 0x58, 0x3e, 0x3a, 0x35, 0xed, 0x43, 0xf6, 0x08, - 0xa8, 0x1a, 0xe5, 0xa3, 0xd3, 0xed, 0x43, 0xf2, 0x80, 0x38, 0x4a, 0x84, 0x27, 0xb6, 0x33, 0x56, - 0x8e, 0x94, 0x88, 0x74, 0x0f, 0x56, 0x8e, 0x4e, 0xb3, 0x61, 0x76, 0xe7, 0xba, 0x7c, 0xf9, 0xe8, - 0x94, 0x22, 0xbd, 0x7e, 0x08, 0x1b, 0x72, 0x07, 0xb7, 0x22, 0x65, 0x76, 0xc3, 0xa5, 0xc3, 0x6f, - 0xc6, 0xad, 0x33, 0xae, 0x19, 0xb1, 0xd1, 0x0d, 0x67, 0x9c, 0x0e, 0xf5, 0xcc, 0x04, 0x64, 0x7b, - 0x61, 0xd5, 0xc8, 0xc1, 0xc4, 0x63, 0xa8, 0x1f, 0x9d, 0xf2, 0x80, 0x0e, 0x83, 0x3d, 0x47, 0x46, - 0x92, 0xd9, 0x98, 0x1d, 0x4a, 0xba, 0x49, 0x91, 0xa3, 0x14, 0x57, 0x61, 0xd9, 0xb0, 0xce, 0x06, - 0xbf, 0xec, 0x92, 0x18, 0x59, 0x35, 0x64, 0x8a, 0x5f, 0x94, 0xd3, 0xff, 0x41, 0x01, 0x1a, 0xa9, - 0x16, 0x80, 0x8b, 0x50, 0xdc, 0x4f, 0x1f, 0x93, 0x6e, 0x28, 0x26, 0x91, 0x27, 0x79, 0x30, 0xbc, - 0x98, 0xf0, 0xe3, 0xac, 0x8b, 0xde, 0x75, 0x5a, 0x74, 0xc4, 0x50, 0x5c, 0xf8, 0xf8, 0xfd, 0x13, - 0x28, 0x0e, 0x2f, 0x26, 0x6c, 0x71, 0xc2, 0x4d, 0x91, 0xb5, 0x53, 0xde, 0x0e, 0xe9, 0xb6, 0xd2, - 0xd3, 0xf6, 0x37, 0x1c, 0xd0, 0x7f, 0xdf, 0xe8, 0xec, 0x35, 0x8d, 0x6f, 0x4c, 0x04, 0x90, 0xd8, - 0xb0, 0xd3, 0x37, 0xda, 0x9d, 0x27, 0x3d, 0x02, 0x94, 0xc8, 0x1e, 0x95, 0x56, 0xb1, 0x69, 0xdb, - 0x3b, 0xa7, 0xd9, 0x27, 0x71, 0x0a, 0xb9, 0x27, 0x71, 0x92, 0xd7, 0xa3, 0xb2, 0x4f, 0x1d, 0xc6, - 0xc9, 0xb9, 0x87, 0x48, 0x56, 0x61, 0xb2, 0xa4, 0xc5, 0x3b, 0x50, 0x3a, 0x3a, 0x75, 0x2e, 0xf2, - 0xaa, 0x5e, 0x7e, 0x01, 0x11, 0x81, 0xfe, 0x67, 0x05, 0x10, 0xb9, 0x8a, 0xb0, 0xf6, 0xf1, 0x9b, - 0xd6, 0xe5, 0x73, 0xd8, 0x54, 0xef, 0xa2, 0x12, 0x55, 0xe6, 0x4c, 0x43, 0x76, 0xe9, 0x95, 0x20, - 0x8d, 0x60, 0x90, 0x3e, 0x3d, 0x25, 0x3e, 0x04, 0x7e, 0xc5, 0x90, 0x2e, 0xde, 0x94, 0x5e, 0xa2, - 0x29, 0x1a, 0x29, 0x4d, 0xfa, 0x6c, 0x61, 0xf6, 0x39, 0x46, 0x3e, 0x0e, 0x59, 0x4b, 0x47, 0x8d, - 0xd6, 0xbc, 0xfe, 0x37, 0x0a, 0x70, 0x39, 0x3f, 0x21, 0xfe, 0x62, 0xad, 0xcc, 0xbf, 0x3d, 0x59, - 0x9c, 0x7d, 0x7b, 0x72, 0xd1, 0x7c, 0x2a, 0x2d, 0x9c, 0x4f, 0xbf, 0x5f, 0x80, 0x8d, 0x4c, 0xef, - 0xa7, 0xfa, 0xe2, 0x5f, 0x51, 0xcd, 0x32, 0x4f, 0x50, 0x96, 0x72, 0x4f, 0x50, 0xea, 0x7f, 0x5a, - 0x80, 0xab, 0x33, 0x35, 0x31, 0x9c, 0xbf, 0xd2, 0xba, 0xe4, 0x9f, 0xaa, 0xa4, 0x23, 0x19, 0x15, - 0xf4, 0xbe, 0x70, 0xaf, 0x98, 0x79, 0xaa, 0x92, 0xce, 0xe9, 0xe8, 0xb0, 0xf8, 0x75, 0xf9, 0x36, - 0x8a, 0x19, 0x5d, 0xf8, 0x23, 0x39, 0xd8, 0x55, 0x82, 0x0c, 0x2e, 0xfc, 0x11, 0x6e, 0x52, 0x63, - 0x27, 0x3c, 0x56, 0xa6, 0x3c, 0x4e, 0xe8, 0xff, 0x51, 0x01, 0xae, 0xcf, 0xb4, 0xac, 0x39, 0x8d, - 0x03, 0xe9, 0xdb, 0xf1, 0x57, 0xd4, 0xb8, 0xdb, 0x40, 0xaf, 0x36, 0x29, 0xa7, 0x92, 0xcc, 0x85, - 0x0c, 0xf9, 0x5d, 0x0d, 0x8a, 0xb6, 0x75, 0x21, 0x63, 0x26, 0xe3, 0x4f, 0x5c, 0xc6, 0x27, 0xc1, - 0x34, 0x94, 0x37, 0x08, 0xe8, 0xb7, 0xfe, 0x09, 0xac, 0xa7, 0x55, 0x6f, 0xc9, 0x77, 0x44, 0x6f, - 0x43, 0xcd, 0x77, 0xce, 0x4c, 0xf5, 0xca, 0xa8, 0xbc, 0x5b, 0xe6, 0x3b, 0x67, 0x92, 0x40, 0xdf, - 0xc9, 0x72, 0x48, 0xaa, 0xce, 0x75, 0xa8, 0x04, 0x9e, 0x9d, 0xf3, 0x28, 0x0d, 0x3c, 0x5b, 0xa1, - 0xb0, 0xb4, 0x4c, 0x2b, 0x57, 0x7c, 0xe7, 0x8c, 0x66, 0xe7, 0x99, 0x2c, 0xa7, 0x69, 0x2b, 0x0f, - 0xaa, 0x45, 0xee, 0xc9, 0xd7, 0xa1, 0x32, 0x09, 0x73, 0xdd, 0xb4, 0x32, 0x09, 0xf9, 0xb3, 0x77, - 0xe5, 0xcd, 0xb5, 0x97, 0x79, 0xe0, 0xf1, 0x5d, 0x36, 0x0d, 0x8a, 0x93, 0x20, 0x92, 0x4e, 0x42, - 0xf8, 0x53, 0xff, 0x54, 0x32, 0x47, 0xd2, 0x07, 0xf9, 0xcb, 0x1a, 0x14, 0x5d, 0xfb, 0x9c, 0x3e, - 0xbc, 0x6a, 0xe0, 0x4f, 0x92, 0xef, 0x9c, 0xef, 0xe4, 0xe5, 0x39, 0xfc, 0xa9, 0x6f, 0x41, 0xcd, - 0xc8, 0x29, 0xbf, 0xf5, 0x8c, 0x1d, 0x29, 0xca, 0xbf, 0x41, 0x93, 0x76, 0x90, 0x51, 0x4b, 0xcd, - 0x48, 0x91, 0x1e, 0x49, 0x76, 0xf8, 0xcc, 0x0a, 0x47, 0x27, 0x56, 0xd8, 0x75, 0xfc, 0xe3, 0xf8, - 0x04, 0xbb, 0x9c, 0xcd, 0xbb, 0xd9, 0x2e, 0x04, 0x06, 0xa9, 0xe9, 0x80, 0xbd, 0xe8, 0x11, 0xb9, - 0x74, 0x30, 0xa8, 0xfa, 0xce, 0x99, 0xcc, 0xff, 0x3a, 0x00, 0xf6, 0xbf, 0x44, 0xcb, 0xdb, 0x3b, - 0x81, 0x67, 0x33, 0x5a, 0x5f, 0x97, 0xed, 0x95, 0x31, 0xbf, 0xb7, 0x9d, 0x23, 0xdd, 0x93, 0x23, - 0xcf, 0x0d, 0x92, 0x9d, 0xf0, 0x1b, 0x0d, 0xa3, 0x78, 0x03, 0xea, 0xca, 0x52, 0x41, 0xcf, 0x1e, - 0xf1, 0xe7, 0x6b, 0x0a, 0xd6, 0x9b, 0x8e, 0xf5, 0xff, 0xb8, 0x04, 0xf5, 0x26, 0xfb, 0x9c, 0x4e, - 0x2e, 0xfa, 0x93, 0x58, 0xfc, 0x1e, 0x5c, 0xa1, 0x78, 0xfb, 0x1c, 0xfa, 0x80, 0x5c, 0x3d, 0xd9, - 0xc9, 0x91, 0x3b, 0xf1, 0x7e, 0xa6, 0x13, 0x65, 0x96, 0x07, 0x83, 0x53, 0x77, 0xc2, 0xd1, 0x10, - 0x3a, 0xf6, 0x39, 0xf9, 0x3c, 0xb2, 0x67, 0x06, 0x85, 0xe6, 0xcf, 0x23, 0x28, 0xf4, 0x33, 0x16, - 0x3f, 0x39, 0x95, 0xc5, 0x4a, 0x27, 0x1f, 0x04, 0xee, 0x9f, 0x32, 0x4d, 0xea, 0x12, 0x35, 0xb7, - 0x2d, 0x4b, 0x97, 0xa8, 0x74, 0x7e, 0x0f, 0x60, 0x5d, 0x3e, 0x0f, 0x40, 0xaf, 0x12, 0x9a, 0xa3, - 0x60, 0x72, 0x21, 0x0f, 0x7e, 0xdf, 0x79, 0x49, 0x55, 0x3b, 0x4c, 0x8a, 0x20, 0xae, 0xe7, 0x5a, - 0x94, 0x87, 0x8a, 0x67, 0x20, 0xb0, 0x4b, 0x27, 0xde, 0x14, 0xf7, 0x33, 0xf5, 0x1e, 0x22, 0xbb, - 0xa2, 0xdf, 0x5b, 0x50, 0x6a, 0xcf, 0x39, 0xdb, 0x27, 0x5a, 0x59, 0x08, 0x17, 0xab, 0xf9, 0x33, - 0xe0, 0x1b, 0x6d, 0xb8, 0xf6, 0x92, 0xbe, 0x7a, 0x95, 0xd3, 0x4a, 0x25, 0xe3, 0xb4, 0x72, 0x63, - 0x0b, 0x36, 0x16, 0xb5, 0xe3, 0x27, 0x95, 0xd1, 0x82, 0x2b, 0x0b, 0x6b, 0xfd, 0x53, 0x0a, 0xd1, - 0x7f, 0xbf, 0x01, 0x90, 0x2e, 0xa7, 0x9c, 0x04, 0x5d, 0x98, 0x91, 0xa0, 0x7f, 0x92, 0x97, 0xe1, - 0x27, 0xd0, 0xc0, 0x71, 0xcc, 0x04, 0x71, 0x5d, 0x1c, 0x3d, 0xb6, 0x8e, 0x54, 0xc3, 0x34, 0x7c, - 0xdf, 0xbc, 0xb7, 0x4c, 0x69, 0xa1, 0xb7, 0xcc, 0xc7, 0xb0, 0xc2, 0xa7, 0x83, 0x6a, 0x50, 0xaf, - 0xcd, 0xb2, 0x86, 0x07, 0x32, 0x68, 0x8e, 0xa2, 0x13, 0x6d, 0x68, 0x24, 0xf1, 0x2e, 0xb2, 0xf1, - 0x23, 0x6f, 0xcd, 0xe7, 0x54, 0x64, 0xfc, 0xd0, 0xa2, 0x95, 0x4d, 0x66, 0x04, 0xee, 0x78, 0x2c, - 0x4d, 0xd6, 0x24, 0x70, 0xaf, 0x64, 0x05, 0xee, 0xe1, 0x98, 0x0d, 0xd5, 0x28, 0x70, 0x7f, 0x00, - 0x97, 0xa5, 0xdf, 0x31, 0x66, 0xc0, 0xee, 0x24, 0x7a, 0xbe, 0x25, 0xa6, 0xc9, 0x78, 0xf4, 0x63, - 0x52, 0x47, 0x91, 0xfc, 0x6b, 0xd8, 0xe0, 0x27, 0x13, 0xe8, 0x75, 0xfa, 0x51, 0xe0, 0x75, 0x6c, - 0x73, 0x6c, 0x4d, 0xa4, 0x1e, 0xf0, 0xce, 0x5c, 0x65, 0xf9, 0xc5, 0x84, 0xe1, 0xa1, 0x47, 0x7e, - 0xdb, 0x89, 0x4f, 0xd5, 0xfa, 0x68, 0x16, 0x3e, 0xe3, 0xda, 0x00, 0x73, 0xae, 0x0d, 0xb3, 0x9a, - 0x41, 0x6d, 0x81, 0x66, 0x90, 0xca, 0xf7, 0xf5, 0xac, 0x7c, 0x2f, 0xde, 0x87, 0x15, 0x19, 0x87, - 0x50, 0x1a, 0xab, 0xc5, 0xfc, 0x22, 0x33, 0x14, 0x09, 0x7e, 0x49, 0x39, 0xda, 0x50, 0xcc, 0xeb, - 0x06, 0x7f, 0x29, 0x0b, 0x13, 0x5b, 0xd2, 0x52, 0x9b, 0xf8, 0x98, 0x4b, 0xc3, 0xf4, 0x8d, 0x4c, - 0xc1, 0x09, 0x4e, 0x9a, 0x12, 0x66, 0x72, 0xdc, 0xf8, 0xc3, 0x15, 0x58, 0x96, 0xee, 0x12, 0xf7, - 0xa1, 0x64, 0x87, 0xf2, 0x3e, 0x4c, 0xa2, 0xca, 0xe4, 0xe5, 0xca, 0xdd, 0x4b, 0x06, 0xd1, 0x88, - 0x07, 0xb0, 0x6c, 0xd9, 0xb6, 0x79, 0x74, 0x9a, 0x3f, 0x44, 0x9f, 0x91, 0xf9, 0x77, 0x2f, 0x19, - 0x65, 0x8b, 0x84, 0xff, 0xcf, 0xa1, 0x8a, 0xf4, 0xd9, 0xe8, 0xc3, 0x73, 0x9a, 0x8c, 0x92, 0xce, - 0x77, 0x2f, 0x19, 0x15, 0x4b, 0x49, 0xea, 0xbf, 0x9d, 0x3f, 0x8e, 0x28, 0xcd, 0x35, 0x70, 0x46, - 0xb4, 0x9c, 0x39, 0x98, 0xf8, 0x1d, 0x60, 0xfb, 0x74, 0x22, 0x4e, 0x94, 0xb3, 0xe7, 0xb5, 0x73, - 0xc2, 0xc7, 0xee, 0x25, 0x83, 0x37, 0x55, 0x25, 0x8c, 0x7c, 0xaa, 0x8e, 0x0a, 0x88, 0x3d, 0x2f, - 0x2f, 0xee, 0x19, 0xe4, 0xd1, 0xc9, 0x79, 0x01, 0x31, 0x6c, 0xcc, 0x66, 0x27, 0xee, 0xa3, 0x2b, - 0x73, 0xd9, 0x12, 0x91, 0x83, 0xb2, 0x25, 0xf2, 0xc7, 0x63, 0xa8, 0xb1, 0xe5, 0x98, 0xf3, 0x55, - 0xe6, 0xba, 0x36, 0x95, 0x18, 0xe8, 0x2c, 0x32, 0x95, 0x1f, 0x5a, 0xaa, 0x9d, 0xa1, 0x93, 0x3d, - 0xee, 0xb9, 0xb9, 0xb0, 0xa3, 0x8c, 0xe4, 0xe4, 0x87, 0x1b, 0x6b, 0x70, 0x1e, 0xd1, 0x85, 0x0d, - 0x79, 0x2e, 0xc2, 0xd2, 0x81, 0xda, 0xd0, 0x61, 0x6e, 0xbc, 0x72, 0xe2, 0xc3, 0xee, 0x25, 0x43, - 0x58, 0xf3, 0x42, 0x45, 0x0b, 0xd6, 0x55, 0x95, 0x68, 0xdb, 0xcf, 0x78, 0xd4, 0x65, 0x9b, 0x94, - 0x0a, 0x05, 0xbb, 0x97, 0x8c, 0x35, 0x2b, 0x0f, 0x12, 0x1d, 0xb8, 0xac, 0x0a, 0xa1, 0xf3, 0x81, - 0x8c, 0x33, 0x7d, 0x7e, 0x14, 0xb3, 0x82, 0xc4, 0xee, 0x25, 0x63, 0xdd, 0x9a, 0x93, 0x2e, 0xf6, - 0x54, 0x7d, 0xb2, 0x92, 0x2b, 0xaf, 0xc4, 0xdb, 0x0b, 0xbb, 0x29, 0x15, 0xa3, 0x93, 0x9a, 0x65, - 0x24, 0xeb, 0x5f, 0xaa, 0xce, 0xca, 0x3b, 0x0f, 0xcb, 0xe3, 0xa4, 0xd7, 0xe7, 0x4a, 0xcc, 0xfa, - 0x0e, 0x27, 0x3d, 0x96, 0x83, 0xa6, 0xfe, 0x1c, 0x37, 0x0c, 0xb8, 0xba, 0x98, 0x69, 0x65, 0x77, - 0xae, 0x12, 0xef, 0x5c, 0x7a, 0x76, 0xe7, 0x9a, 0x0d, 0x8b, 0x9f, 0xd9, 0xc7, 0x7e, 0x01, 0xab, - 0x39, 0xae, 0x2d, 0x6a, 0xb0, 0xa2, 0x5e, 0x14, 0xa7, 0x58, 0x23, 0xad, 0xfe, 0xfe, 0x37, 0x5a, - 0x01, 0xc1, 0x9d, 0xde, 0x60, 0xd8, 0xec, 0x0d, 0xb5, 0x25, 0x4e, 0xec, 0x77, 0x9b, 0xad, 0xb6, - 0x56, 0xd4, 0xff, 0x7e, 0x11, 0xaa, 0xc9, 0x69, 0xe3, 0x6f, 0x6e, 0x13, 0x5c, 0x7c, 0x25, 0x7e, - 0x46, 0xe1, 0xe5, 0xbb, 0x04, 0x25, 0x15, 0x03, 0x3b, 0xab, 0x56, 0x46, 0xf3, 0x41, 0x4e, 0xcb, - 0x3f, 0x32, 0xc8, 0x69, 0xf6, 0x2e, 0xd8, 0x72, 0xfe, 0x2e, 0xd8, 0xcc, 0x03, 0xfc, 0x2b, 0xf4, - 0x76, 0x73, 0xf6, 0x01, 0xfe, 0xab, 0xb0, 0xec, 0x46, 0xcf, 0x5c, 0xe7, 0x4c, 0x7a, 0xc6, 0xcb, - 0x54, 0x7e, 0xd3, 0x87, 0x57, 0x6c, 0xfa, 0x3f, 0x66, 0x03, 0x79, 0x08, 0x1b, 0x47, 0xa7, 0xc9, - 0x8b, 0xd1, 0xa9, 0x89, 0xa9, 0x4e, 0x55, 0x5a, 0x88, 0x13, 0xef, 0xc0, 0x72, 0xac, 0x0e, 0x42, - 0x8b, 0x0b, 0xce, 0x86, 0x0d, 0x89, 0xd6, 0xff, 0xb0, 0x00, 0x90, 0x9e, 0xc3, 0xfd, 0x85, 0x0d, - 0xda, 0x19, 0x9b, 0x61, 0xf1, 0x07, 0x6c, 0x86, 0xaf, 0x7a, 0x10, 0xe8, 0x3b, 0xa8, 0x26, 0x27, - 0xaf, 0xbf, 0xf9, 0xc4, 0xfa, 0x49, 0x9f, 0xfc, 0xe7, 0x94, 0x71, 0x3f, 0x39, 0xba, 0xfc, 0x8b, - 0xf6, 0x45, 0xee, 0xf3, 0x8b, 0x65, 0xb9, 0xf4, 0xf3, 0xe7, 0x6c, 0x61, 0x4f, 0x3e, 0xfe, 0x97, - 0xbc, 0x9a, 0xb2, 0x13, 0xbd, 0x94, 0x9b, 0xe8, 0xfa, 0x54, 0x1e, 0x13, 0xfc, 0xc5, 0x3f, 0xfd, - 0x93, 0x1a, 0xfc, 0xbf, 0x17, 0x94, 0x2d, 0x3b, 0x79, 0xe4, 0xfb, 0xa5, 0x72, 0xf4, 0x62, 0x73, - 0xfc, 0x4f, 0xf9, 0xdc, 0x0f, 0x5a, 0xea, 0x4a, 0x3f, 0x64, 0xa9, 0x7b, 0x47, 0xbd, 0x91, 0x50, - 0x7e, 0x99, 0x95, 0x4e, 0x3e, 0x93, 0x70, 0x1b, 0x6a, 0x59, 0xdb, 0x1c, 0x1b, 0x65, 0xd8, 0xa8, - 0xc2, 0x66, 0x39, 0x5d, 0xea, 0x0d, 0xdc, 0xde, 0x0d, 0x55, 0xae, 0x8c, 0x0d, 0x44, 0x09, 0xfd, - 0xaf, 0x31, 0x47, 0xfd, 0x4d, 0xbb, 0xe4, 0x87, 0xcd, 0x34, 0xfa, 0xff, 0x5b, 0x80, 0xd5, 0x9c, - 0x27, 0xc5, 0x6f, 0xf0, 0x89, 0x85, 0x7c, 0xb9, 0xf8, 0xff, 0x23, 0xbe, 0x9c, 0x73, 0x18, 0xaf, - 0xe4, 0x1d, 0xc6, 0x91, 0xdd, 0xd5, 0x73, 0x5a, 0xd1, 0x22, 0xfd, 0xa9, 0xb0, 0x50, 0x7f, 0xba, - 0x05, 0x60, 0x8d, 0xe8, 0x1e, 0x5a, 0x67, 0x9b, 0xfd, 0xb3, 0x57, 0x8d, 0x0c, 0x44, 0x7c, 0x01, - 0xd7, 0xa5, 0xd1, 0x84, 0xfb, 0x27, 0x38, 0x32, 0x15, 0xd6, 0x96, 0x46, 0x88, 0xab, 0x4c, 0x40, - 0xa3, 0xdb, 0x3f, 0x6a, 0x2a, 0xac, 0xde, 0x81, 0xd5, 0x9c, 0x8b, 0x0a, 0xee, 0x2d, 0xc7, 0x5e, - 0x70, 0x68, 0x79, 0xb2, 0x2e, 0x32, 0x25, 0xee, 0x40, 0xf9, 0xec, 0xc4, 0x09, 0x9d, 0x05, 0x4f, - 0xd9, 0x32, 0x42, 0xff, 0x19, 0xd4, 0xb3, 0xee, 0x72, 0xe2, 0x7d, 0x28, 0xbb, 0xb1, 0x33, 0x56, - 0xe6, 0xa0, 0xab, 0xf3, 0x1e, 0x75, 0x9d, 0xd8, 0x19, 0x1b, 0x4c, 0xa4, 0xff, 0x51, 0x01, 0xb4, - 0x59, 0x1c, 0x85, 0xb7, 0xb9, 0x88, 0x62, 0x67, 0xac, 0x2a, 0xc3, 0xa9, 0x4c, 0x25, 0x97, 0x72, - 0x95, 0x54, 0x56, 0xb3, 0x62, 0xc6, 0x6a, 0x76, 0x47, 0x09, 0x25, 0xf3, 0x57, 0x94, 0x18, 0x21, - 0xde, 0x86, 0x4a, 0xe8, 0x50, 0xe0, 0x1c, 0x7b, 0xc1, 0xed, 0xb8, 0x04, 0xa7, 0xff, 0x41, 0x01, - 0x56, 0xa4, 0x6f, 0xdf, 0x42, 0xfb, 0xdc, 0xbb, 0xb0, 0x12, 0x8d, 0x4e, 0x9c, 0xb1, 0xa5, 0xde, - 0x05, 0x9a, 0xf3, 0xeb, 0x56, 0x78, 0x71, 0x8b, 0x3d, 0x1e, 0xf3, 0xf6, 0xba, 0x7d, 0xcf, 0xf2, - 0x0d, 0x82, 0xe3, 0x54, 0x63, 0x3b, 0x2d, 0x5f, 0x35, 0xe2, 0x77, 0x18, 0x80, 0x40, 0x74, 0x99, - 0x48, 0xff, 0x6d, 0x58, 0x91, 0xbe, 0x83, 0x0b, 0xab, 0x72, 0x0b, 0x4a, 0x56, 0x78, 0x1c, 0x2d, - 0x18, 0x2c, 0x82, 0xeb, 0x77, 0x00, 0x52, 0x67, 0xc2, 0x45, 0x25, 0xe8, 0xf7, 0xa1, 0xa2, 0xfc, - 0x07, 0x71, 0xfe, 0xa5, 0x9f, 0x96, 0x97, 0x83, 0xb3, 0x95, 0xf1, 0xe4, 0xf3, 0xf4, 0xdd, 0x60, - 0x74, 0x4a, 0x47, 0x06, 0x1f, 0x02, 0xdd, 0x94, 0x1e, 0xce, 0x3d, 0x58, 0x91, 0x90, 0xf1, 0xe5, - 0x03, 0x45, 0x24, 0xee, 0x43, 0xc2, 0x2f, 0x5f, 0x65, 0xad, 0xd0, 0x9b, 0xea, 0x4e, 0x3d, 0xcd, - 0xb2, 0x47, 0xd2, 0x7a, 0xdc, 0xa5, 0x97, 0xd7, 0x0a, 0xd9, 0x67, 0x5c, 0x73, 0x75, 0x32, 0x32, - 0x64, 0x7a, 0x03, 0xea, 0x59, 0xa7, 0x27, 0xbd, 0x09, 0xeb, 0x7b, 0x4e, 0x6c, 0x21, 0xff, 0x51, - 0x61, 0xfc, 0x79, 0xfe, 0xe2, 0x8f, 0xfc, 0xfc, 0x9d, 0xa5, 0x33, 0x98, 0x48, 0xff, 0xfb, 0x25, - 0xd0, 0x66, 0x71, 0x3f, 0x14, 0x5f, 0xe0, 0x36, 0xd4, 0xf8, 0xc9, 0x9e, 0xac, 0x25, 0x11, 0x18, - 0x94, 0xb9, 0x7d, 0xc2, 0xcf, 0xd9, 0xa4, 0x0f, 0xf7, 0xef, 0x52, 0x5a, 0x5c, 0xe3, 0xcb, 0xe4, - 0x5e, 0x30, 0xa2, 0x69, 0x5d, 0xa7, 0xbb, 0xe3, 0xdd, 0x60, 0x44, 0x61, 0x0b, 0xa4, 0xc1, 0x83, - 0x3d, 0x71, 0xeb, 0x46, 0x45, 0x5a, 0x39, 0xe8, 0x14, 0x53, 0xde, 0x49, 0x89, 0x23, 0x19, 0x5b, - 0xac, 0xc2, 0x80, 0x61, 0xa4, 0x1e, 0x21, 0x1f, 0xf9, 0xb1, 0x8c, 0x9a, 0xb3, 0x1c, 0x06, 0x67, - 0x2d, 0x9f, 0xa2, 0x16, 0xd0, 0x13, 0xa0, 0x88, 0xa9, 0xa4, 0xcf, 0xc0, 0x23, 0x8a, 0x42, 0xd5, - 0x70, 0x9c, 0x55, 0x7e, 0x80, 0xab, 0x2a, 0x5f, 0x5e, 0x93, 0xc0, 0xe4, 0x7d, 0x43, 0xde, 0xff, - 0x32, 0xf1, 0x88, 0x80, 0x41, 0x44, 0x70, 0x1d, 0x2a, 0xdf, 0x07, 0xbe, 0x43, 0x86, 0x93, 0x1a, - 0xd5, 0x6a, 0x05, 0xd3, 0x7b, 0xd6, 0x04, 0x37, 0x02, 0x8f, 0xc2, 0xd8, 0xf1, 0xad, 0x4b, 0x4e, - 0xe8, 0xff, 0x7d, 0x01, 0x36, 0x66, 0xfb, 0x9a, 0xa6, 0x51, 0x1d, 0x2a, 0xad, 0x7e, 0xd7, 0xec, - 0x35, 0xf7, 0xda, 0xda, 0x25, 0xb1, 0x06, 0xb5, 0xfe, 0xd6, 0x57, 0xed, 0xd6, 0x90, 0x01, 0x05, - 0x8a, 0xa8, 0x39, 0x30, 0x77, 0x3b, 0xdb, 0xdb, 0xed, 0x1e, 0x6b, 0x14, 0xfd, 0xad, 0xaf, 0xcc, - 0x6e, 0xbf, 0xa5, 0x15, 0x11, 0xa7, 0xdc, 0x41, 0x06, 0x5a, 0x89, 0x9c, 0x45, 0xe8, 0x52, 0x00, - 0x26, 0xcb, 0xec, 0xed, 0xfe, 0x7c, 0x60, 0xb6, 0x7a, 0x43, 0x6d, 0x19, 0x53, 0xbd, 0x83, 0x6e, - 0x97, 0x52, 0xe4, 0xd6, 0xda, 0xea, 0xef, 0xed, 0x1b, 0xed, 0xc1, 0xc0, 0x1c, 0x74, 0x7e, 0xd5, - 0xd6, 0x2a, 0xf4, 0x65, 0xa3, 0xf3, 0xa4, 0xd3, 0x63, 0x40, 0x55, 0xac, 0x40, 0x71, 0xaf, 0xd3, - 0xe3, 0x48, 0xa2, 0x7b, 0xcd, 0xaf, 0xb5, 0x1a, 0xfe, 0x18, 0x1c, 0xec, 0x69, 0x75, 0x51, 0x85, - 0x72, 0xb7, 0xfd, 0xac, 0xdd, 0xd5, 0x56, 0xf5, 0x7f, 0x56, 0x54, 0x12, 0x31, 0xf9, 0x7b, 0xfd, - 0x18, 0x29, 0x70, 0xd1, 0x29, 0x6b, 0xd2, 0x69, 0xc5, 0x4c, 0xa7, 0x89, 0x37, 0xa0, 0x2e, 0x37, - 0x85, 0xec, 0x39, 0x59, 0x4d, 0xc2, 0x68, 0xca, 0xbd, 0x09, 0xab, 0x89, 0x8b, 0x44, 0xe6, 0xe1, - 0xf0, 0xba, 0x02, 0x2e, 0x38, 0xae, 0x59, 0x5e, 0x70, 0x5c, 0x33, 0x71, 0x63, 0x54, 0xdc, 0x91, - 0xe7, 0xf2, 0x4c, 0xaa, 0x22, 0xe4, 0x19, 0xf1, 0xda, 0xd7, 0x80, 0x12, 0xe6, 0xd4, 0x97, 0x4f, - 0x1e, 0x55, 0x8d, 0x0a, 0x02, 0x0e, 0x7c, 0x37, 0x9e, 0x75, 0xd1, 0xa8, 0xce, 0xb9, 0x68, 0x64, - 0x37, 0x67, 0xc8, 0x6f, 0xce, 0xaf, 0x27, 0x7b, 0x24, 0x22, 0x6b, 0x74, 0x56, 0x51, 0x4d, 0x76, - 0x41, 0xf1, 0x3e, 0x88, 0xd1, 0x34, 0xa4, 0x27, 0x44, 0x32, 0x64, 0xfc, 0x54, 0x91, 0x26, 0x31, - 0xc9, 0xae, 0x48, 0x91, 0x5c, 0xf2, 0xd4, 0xa4, 0x9e, 0x57, 0x8d, 0x46, 0x9e, 0x54, 0x3c, 0x80, - 0xcb, 0x72, 0x6e, 0xe7, 0xfa, 0x56, 0x06, 0x81, 0x60, 0x54, 0x33, 0xed, 0x61, 0xfd, 0xb7, 0xa0, - 0xa2, 0x5c, 0xfb, 0x7e, 0x58, 0xd8, 0x5d, 0x30, 0xae, 0xfa, 0xbf, 0xbf, 0x04, 0xd5, 0xc4, 0xd1, - 0xef, 0x47, 0xcd, 0x8e, 0xd7, 0x50, 0x4e, 0x8d, 0x4e, 0xb3, 0x2c, 0xa6, 0x82, 0x00, 0x35, 0x52, - 0xf2, 0x6e, 0xe0, 0x34, 0x74, 0x95, 0xc4, 0xc6, 0x90, 0x83, 0xd0, 0xa5, 0x38, 0xe1, 0xae, 0x9f, - 0x09, 0xd7, 0x50, 0x35, 0x2a, 0x08, 0xa0, 0x85, 0x76, 0x1d, 0xe8, 0x37, 0xe5, 0xe4, 0x49, 0xb2, - 0x82, 0x69, 0xcc, 0x77, 0x35, 0xd1, 0xff, 0x64, 0xa8, 0xb3, 0x38, 0x11, 0x2c, 0xa4, 0x8f, 0x11, - 0x7b, 0x56, 0xc8, 0x14, 0x7e, 0x87, 0x02, 0x04, 0x51, 0x1d, 0xe5, 0x8c, 0x40, 0x80, 0xaa, 0x63, - 0x66, 0xb8, 0xaa, 0xb3, 0xa3, 0x3a, 0x3b, 0xa7, 0x61, 0x6e, 0x4e, 0xeb, 0x31, 0xac, 0x48, 0x67, - 0xc7, 0x1f, 0xee, 0xf0, 0x1f, 0xec, 0x2a, 0x0d, 0x8a, 0x96, 0xa7, 0x6e, 0xdf, 0xe3, 0xcf, 0x99, - 0x8a, 0x95, 0x66, 0x2a, 0xa6, 0xff, 0x9b, 0x4b, 0x00, 0xa9, 0xd3, 0xa4, 0xf8, 0x60, 0xc6, 0x41, - 0xbb, 0x30, 0xb7, 0xed, 0xcf, 0xf8, 0x65, 0xcf, 0x04, 0xce, 0x5f, 0xfa, 0x11, 0x81, 0xf3, 0x1f, - 0xc2, 0x6a, 0x14, 0x8e, 0x5e, 0x69, 0xc3, 0xaf, 0x45, 0xe1, 0x28, 0x31, 0xe1, 0x7f, 0x08, 0x98, - 0xa4, 0x47, 0x75, 0x52, 0x45, 0x75, 0x4e, 0x6a, 0xa9, 0x46, 0xe1, 0xa8, 0x7f, 0xf8, 0xed, 0x36, - 0x5f, 0xc8, 0xb4, 0xa3, 0xd8, 0x5c, 0xc4, 0x25, 0xd6, 0xec, 0x28, 0xde, 0xce, 0x32, 0x8a, 0xbb, - 0xd0, 0x40, 0xda, 0x39, 0x66, 0x51, 0xb7, 0xa3, 0xf4, 0x40, 0x49, 0xff, 0x97, 0xd4, 0xc1, 0xfc, - 0x8c, 0x79, 0x58, 0x7c, 0x26, 0x15, 0xf1, 0x8c, 0x10, 0xb1, 0xb9, 0xc8, 0x9a, 0xcc, 0x77, 0xb6, - 0x13, 0x52, 0xf1, 0x18, 0x56, 0x13, 0xdc, 0xb6, 0x73, 0xa4, 0x64, 0x20, 0xf5, 0x42, 0x4b, 0x06, - 0x65, 0xe4, 0x09, 0x51, 0x28, 0x4a, 0x9f, 0x35, 0xc2, 0x15, 0x98, 0x5c, 0x5a, 0xaa, 0xca, 0xeb, - 0x48, 0x8f, 0xe1, 0xda, 0x4b, 0xec, 0x68, 0xea, 0x5c, 0x52, 0xc6, 0x61, 0x90, 0x41, 0xdd, 0x7c, - 0xe7, 0x8c, 0xe3, 0x30, 0xe8, 0x3e, 0x5c, 0x5e, 0xf0, 0x1e, 0xb9, 0xb8, 0x43, 0x2e, 0x5b, 0xe9, - 0xfb, 0xeb, 0x52, 0xb6, 0x1a, 0xa5, 0x8f, 0xae, 0x7f, 0x0a, 0x6b, 0x8e, 0xe7, 0x1e, 0xbb, 0x87, - 0x2e, 0xc5, 0x1a, 0x1f, 0x05, 0xde, 0xa2, 0xd7, 0xf9, 0x8d, 0x46, 0x86, 0xa8, 0x15, 0x78, 0xfa, - 0xbf, 0x52, 0xc8, 0x3d, 0x2e, 0xa8, 0x9e, 0x17, 0xe3, 0xc7, 0x05, 0xe5, 0x2b, 0xc6, 0x05, 0x83, - 0x7e, 0xe3, 0xca, 0x94, 0x0f, 0xd7, 0xca, 0xc7, 0xc2, 0x38, 0x25, 0x3e, 0x84, 0x0d, 0x19, 0x2f, - 0x9d, 0xa2, 0x95, 0x99, 0x13, 0x27, 0x34, 0xc3, 0xe0, 0x8c, 0x26, 0x56, 0xc1, 0x90, 0xb1, 0xd4, - 0x29, 0x68, 0xd9, 0xbe, 0x13, 0x1a, 0xc1, 0x99, 0xd8, 0x24, 0xa9, 0xc4, 0x19, 0xc5, 0xea, 0x8c, - 0x59, 0x25, 0xf5, 0xdf, 0x85, 0xcb, 0x0b, 0xae, 0x05, 0x8a, 0xb7, 0x94, 0x9e, 0xf7, 0x92, 0x4b, - 0xc0, 0x0b, 0x75, 0xcb, 0xa5, 0x59, 0xdd, 0xf2, 0x8f, 0x0b, 0x50, 0x53, 0xcf, 0xf2, 0x19, 0x1c, - 0x28, 0x50, 0xbd, 0xde, 0x97, 0x46, 0x5a, 0x02, 0x05, 0xea, 0xd8, 0xa8, 0x7a, 0x86, 0xce, 0x0b, - 0x37, 0x52, 0xde, 0x96, 0x25, 0x23, 0x49, 0x8b, 0xf7, 0x60, 0x3d, 0x09, 0x4d, 0x38, 0x13, 0x57, - 0x4f, 0x4b, 0x10, 0x2a, 0x04, 0xdb, 0x3c, 0x17, 0x28, 0x65, 0xd9, 0xd3, 0xcc, 0x7e, 0x56, 0x9e, - 0xdd, 0xcf, 0xf4, 0x7f, 0xa3, 0x00, 0x9b, 0x83, 0xef, 0x3c, 0x79, 0xc5, 0xb1, 0x33, 0x9e, 0x78, - 0x34, 0x99, 0x38, 0x38, 0xcc, 0xa7, 0x70, 0x35, 0x75, 0x27, 0x35, 0x8f, 0x5c, 0xff, 0xd8, 0x09, - 0x27, 0xa1, 0x2b, 0x1d, 0x11, 0xea, 0xc6, 0x95, 0x14, 0xbb, 0x93, 0x22, 0xc5, 0x53, 0xd0, 0x23, - 0x67, 0x6c, 0xf9, 0xb1, 0x3b, 0x32, 0x33, 0xf9, 0x59, 0xf3, 0x48, 0x5a, 0xc4, 0x23, 0x7d, 0x5b, - 0x51, 0xa6, 0x57, 0x67, 0x06, 0x44, 0x27, 0x1b, 0xa8, 0xff, 0xe7, 0x45, 0x78, 0x6d, 0xff, 0x22, - 0x3e, 0x09, 0xfc, 0xc5, 0x75, 0xdc, 0x84, 0x95, 0x13, 0xcb, 0xb7, 0xbd, 0x24, 0x00, 0xa9, 0x4a, - 0x8a, 0x1b, 0xb0, 0xcc, 0x7b, 0x09, 0x8f, 0xd7, 0xd6, 0xd2, 0x66, 0xc1, 0x90, 0x10, 0x62, 0xdb, - 0x87, 0xae, 0x49, 0xf7, 0x7c, 0xac, 0x51, 0x2c, 0xf7, 0x9e, 0x9a, 0x75, 0xe8, 0xb6, 0x24, 0x08, - 0x77, 0x60, 0x8a, 0x24, 0xef, 0x84, 0x49, 0x95, 0xa5, 0x63, 0x8f, 0x04, 0xab, 0x21, 0x40, 0xc2, - 0x30, 0x76, 0x8f, 0xac, 0x51, 0xac, 0xa2, 0xcb, 0x95, 0x25, 0xa1, 0x04, 0xcb, 0xe0, 0x72, 0x1f, - 0x80, 0x70, 0xfc, 0x17, 0x6e, 0x18, 0xf8, 0x14, 0x59, 0x43, 0xd2, 0x32, 0x4b, 0x5a, 0xcf, 0x60, - 0x24, 0xf9, 0x6d, 0xa8, 0x45, 0xf6, 0x69, 0xf2, 0x71, 0x19, 0x69, 0x37, 0xb2, 0x4f, 0xd5, 0x87, - 0xbf, 0x84, 0xeb, 0x2f, 0xef, 0x5e, 0x8e, 0x12, 0x74, 0xcd, 0x5e, 0xdc, 0xad, 0x09, 0x6f, 0xa9, - 0xa6, 0xbc, 0x85, 0x9c, 0x4d, 0xe8, 0xa5, 0xfd, 0xc0, 0x73, 0x47, 0x17, 0x72, 0x2b, 0x03, 0x04, - 0xed, 0x13, 0xe4, 0x07, 0xe6, 0x03, 0xbb, 0x0f, 0x2e, 0x9e, 0x0f, 0xfa, 0xbf, 0x5b, 0x86, 0x9a, - 0x1c, 0xbc, 0x16, 0xee, 0x5c, 0x1c, 0xfd, 0x88, 0x7a, 0x39, 0xa9, 0x2e, 0x07, 0x05, 0x59, 0x53, - 0x70, 0x55, 0xcd, 0x4f, 0xa0, 0x9e, 0x2c, 0xa4, 0x34, 0x0a, 0x83, 0x8a, 0x8c, 0x9b, 0xae, 0x38, - 0x23, 0x59, 0x6f, 0xb8, 0xfc, 0x6e, 0x40, 0xc5, 0xb3, 0xfc, 0xe3, 0xa9, 0x75, 0x9c, 0x38, 0x76, - 0xaa, 0xb4, 0xf8, 0x18, 0x1a, 0x56, 0x78, 0x3c, 0xa5, 0x11, 0x48, 0x55, 0xdb, 0x7c, 0xfc, 0x8d, - 0x55, 0x45, 0xc1, 0x61, 0x33, 0x3e, 0x86, 0x1a, 0xdf, 0x5d, 0x66, 0x49, 0xa4, 0xfc, 0x12, 0x0f, - 0x17, 0x60, 0x22, 0xda, 0x02, 0x6e, 0x01, 0xbc, 0x08, 0x3c, 0x2b, 0x26, 0x6e, 0x28, 0x87, 0x38, - 0x03, 0x99, 0xed, 0xea, 0x95, 0xb9, 0xae, 0x7e, 0xc8, 0x1e, 0xcd, 0x7c, 0xea, 0x25, 0x8f, 0x9c, - 0x5f, 0xb6, 0x4e, 0x77, 0x2f, 0x91, 0xcf, 0xb3, 0xf8, 0x2d, 0x58, 0x9e, 0xd0, 0x4a, 0x91, 0x07, - 0x5e, 0x6f, 0xc8, 0x0d, 0xe7, 0xe5, 0xab, 0x67, 0xf7, 0x92, 0x21, 0xb3, 0x88, 0x9f, 0xf1, 0xad, - 0x37, 0xe7, 0x3c, 0x96, 0x31, 0x97, 0x6f, 0xe5, 0x2e, 0x3f, 0xe3, 0xc0, 0x3d, 0x68, 0x31, 0x01, - 0x1d, 0xbe, 0xd0, 0x72, 0x52, 0x59, 0xc8, 0x69, 0xc6, 0xf2, 0xbc, 0xc8, 0x8d, 0x1d, 0x25, 0xfc, - 0x56, 0x0d, 0x50, 0xa0, 0x0e, 0x45, 0x85, 0x1e, 0x5b, 0x17, 0xa6, 0x13, 0x86, 0x41, 0x28, 0x63, - 0x84, 0x54, 0xc6, 0xd6, 0x45, 0x1b, 0xd3, 0x28, 0xf5, 0x47, 0xce, 0x68, 0x1a, 0xe2, 0xf6, 0x42, - 0xb3, 0x92, 0x45, 0xdd, 0xba, 0x02, 0xd2, 0xb5, 0xdb, 0x9b, 0x50, 0xf5, 0x1c, 0xeb, 0x74, 0x12, - 0x06, 0xc1, 0x91, 0x14, 0x6f, 0x53, 0xc0, 0x8d, 0x2f, 0xa1, 0x9e, 0xad, 0xdd, 0xab, 0x9c, 0x1a, - 0xaa, 0x99, 0xc3, 0xa0, 0x2d, 0x0d, 0x1a, 0x6e, 0xae, 0x5b, 0xf4, 0x7f, 0x58, 0x84, 0x2b, 0x0b, - 0x6f, 0x7d, 0xcf, 0x4d, 0xc8, 0xc2, 0x4f, 0x9e, 0x90, 0x4b, 0x33, 0x13, 0x72, 0xd1, 0x6a, 0x28, - 0x2e, 0x5e, 0x0d, 0x2f, 0x5f, 0x7f, 0xa5, 0x1f, 0x58, 0x7f, 0x7f, 0x65, 0x0c, 0x2a, 0x3f, 0xc9, - 0x57, 0x5e, 0x35, 0xc9, 0x2b, 0x73, 0x93, 0x3c, 0x37, 0x29, 0xaa, 0xaf, 0x9a, 0x14, 0xf0, 0xaa, - 0x49, 0x51, 0x9b, 0x99, 0x14, 0xf7, 0xff, 0xf5, 0x02, 0xac, 0x0f, 0xe2, 0xd0, 0xf5, 0x8f, 0xbb, - 0x6e, 0xec, 0x84, 0x96, 0xb7, 0x13, 0x84, 0x63, 0xba, 0x3f, 0x33, 0x34, 0x3a, 0xbd, 0x27, 0x66, - 0xb7, 0x33, 0x6c, 0x1b, 0xcd, 0xe4, 0x2d, 0x80, 0x79, 0xc4, 0xb0, 0xfd, 0xf5, 0x50, 0x2b, 0x88, - 0xbb, 0x70, 0x67, 0x06, 0xb1, 0xd5, 0xe9, 0x35, 0x8d, 0x6f, 0xcc, 0x4e, 0x6f, 0x68, 0xf4, 0xb7, - 0x0f, 0x5a, 0x6d, 0x43, 0x5b, 0x12, 0x57, 0x41, 0xcc, 0x50, 0xed, 0xb6, 0xbf, 0xd6, 0x8a, 0x0b, - 0xe0, 0x5b, 0x9d, 0xa1, 0x56, 0xba, 0xbf, 0x07, 0x97, 0x9b, 0xc7, 0xc7, 0xa1, 0x73, 0x8c, 0xea, - 0x14, 0xc5, 0x9d, 0x56, 0x97, 0x98, 0x9a, 0x4f, 0x9e, 0x98, 0xad, 0x7e, 0x6f, 0xa7, 0xf3, 0x44, - 0x55, 0xed, 0x0d, 0x78, 0x3d, 0x03, 0x7c, 0x62, 0xf4, 0x0f, 0xf6, 0x31, 0xd1, 0x6a, 0x0e, 0xcd, - 0xbe, 0xb1, 0xdd, 0x36, 0xb4, 0xc2, 0xfd, 0x37, 0x70, 0x05, 0xb0, 0xd1, 0x23, 0x79, 0x3a, 0x21, - 0xf0, 0x1d, 0xed, 0x12, 0x2a, 0xfa, 0xdd, 0xef, 0x3f, 0xd1, 0x0a, 0xf7, 0x75, 0xa8, 0x0d, 0x4e, - 0xa6, 0x47, 0x47, 0x9e, 0xa3, 0x28, 0x76, 0xad, 0xe8, 0x84, 0x1d, 0x9b, 0xe9, 0xa1, 0x5e, 0xad, - 0x70, 0xff, 0x6d, 0x58, 0x95, 0x34, 0x7b, 0x4e, 0x7c, 0x12, 0xd8, 0x02, 0x60, 0xb9, 0x17, 0x84, - 0x63, 0xcb, 0x93, 0x74, 0xce, 0x34, 0x42, 0xba, 0x0f, 0xe1, 0x4a, 0xa6, 0xac, 0x9d, 0x20, 0xa4, - 0xf8, 0x77, 0xad, 0x1e, 0x05, 0x39, 0xa0, 0xe5, 0xc4, 0xd7, 0xf5, 0x77, 0x2f, 0x0e, 0x43, 0xd7, - 0xd6, 0x0a, 0xf7, 0x1f, 0x43, 0x35, 0x7d, 0x0b, 0x9c, 0x1e, 0x1d, 0xd9, 0xea, 0x1f, 0xf4, 0xb6, - 0xe9, 0x55, 0x03, 0x7a, 0x68, 0xa4, 0xd5, 0x3d, 0x18, 0x74, 0x9e, 0x49, 0x2b, 0x49, 0xfb, 0x6b, - 0x95, 0x5c, 0xba, 0xff, 0x07, 0x05, 0x15, 0x32, 0x53, 0x55, 0xbb, 0xdb, 0x6f, 0x6e, 0xb3, 0x79, - 0x25, 0x79, 0x4f, 0x65, 0xb8, 0xa5, 0x15, 0x10, 0x60, 0xb4, 0x07, 0x07, 0xdd, 0xa1, 0x7a, 0xaa, - 0xa5, 0x01, 0xd0, 0x69, 0xb5, 0xb7, 0xda, 0xc6, 0x13, 0x24, 0x28, 0x62, 0x7a, 0xaf, 0xdf, 0x7b, - 0xd2, 0xdf, 0xde, 0xc2, 0x34, 0x3d, 0xd2, 0xb2, 0xdd, 0x1c, 0x36, 0x07, 0x43, 0xa3, 0xdd, 0xdc, - 0x43, 0x10, 0xdd, 0x23, 0x56, 0xfe, 0xdb, 0xc3, 0x2d, 0x36, 0xb5, 0x3c, 0x6d, 0xee, 0x3c, 0x6d, - 0x62, 0x6a, 0xe5, 0xfe, 0x7f, 0xbb, 0x04, 0xda, 0x6c, 0x58, 0x54, 0xf1, 0x1a, 0x5c, 0xa3, 0x52, - 0xcd, 0x7d, 0xa3, 0xbd, 0xdd, 0x69, 0xf1, 0x8d, 0xb0, 0x67, 0xcd, 0x6e, 0x67, 0x9b, 0x67, 0xd5, - 0x2c, 0xb2, 0xd9, 0xdb, 0xe6, 0xe7, 0x1a, 0x66, 0x11, 0xed, 0x5f, 0x1e, 0x34, 0xbb, 0xda, 0x92, - 0x78, 0x1d, 0xae, 0xcf, 0xa2, 0x7a, 0xfd, 0xa1, 0x44, 0x17, 0xc5, 0x26, 0x6c, 0xcc, 0xa2, 0xbb, - 0xed, 0xc1, 0x40, 0x2b, 0x89, 0x5b, 0x70, 0x63, 0x11, 0x46, 0xe6, 0x2c, 0x2f, 0xaa, 0xe9, 0x13, - 0xb2, 0x39, 0x19, 0xda, 0x32, 0x4e, 0xb2, 0x97, 0x20, 0x65, 0xfe, 0x15, 0x9c, 0xcb, 0xf3, 0x2d, - 0xd5, 0x2a, 0x0b, 0x7b, 0x60, 0x60, 0xd2, 0xe3, 0x1c, 0x55, 0x71, 0x1b, 0x5e, 0x5b, 0x84, 0xec, - 0x0f, 0x99, 0x00, 0xee, 0xff, 0xe3, 0x02, 0x6c, 0xbe, 0x2c, 0x72, 0x96, 0xd0, 0xe1, 0x96, 0x71, - 0xd0, 0xa3, 0xa7, 0x56, 0xf8, 0x55, 0x18, 0xf3, 0x69, 0xfb, 0x9b, 0x99, 0x57, 0x31, 0x5e, 0x87, - 0xeb, 0x0b, 0x68, 0x8c, 0xe6, 0x73, 0xf3, 0xd9, 0xc7, 0x5a, 0x41, 0xbc, 0x07, 0xef, 0x2c, 0x40, - 0xef, 0x74, 0xfb, 0xcd, 0xa1, 0xf9, 0xab, 0xb6, 0xd1, 0x37, 0x5b, 0xdd, 0xfe, 0xa0, 0xbd, 0x8d, - 0xc4, 0x4b, 0xe2, 0x0e, 0xdc, 0x5c, 0x40, 0x3c, 0xe0, 0x87, 0x7c, 0x9e, 0x7d, 0xac, 0x15, 0xc5, - 0x5b, 0xf0, 0xc6, 0xcb, 0x29, 0x76, 0x0e, 0xba, 0x44, 0x56, 0xba, 0xff, 0x0b, 0xd8, 0x7c, 0x59, - 0xb8, 0x05, 0x5c, 0x48, 0xad, 0xdd, 0x26, 0x85, 0xb4, 0xa8, 0x43, 0xa5, 0xd7, 0x37, 0x39, 0x45, - 0x0e, 0x07, 0x46, 0xbb, 0xdb, 0xa6, 0x3b, 0x89, 0xf7, 0x7f, 0x5d, 0xc8, 0x98, 0xb5, 0xd5, 0x95, - 0xf9, 0x04, 0x20, 0x57, 0x77, 0x16, 0x64, 0x38, 0x96, 0xad, 0x15, 0x70, 0x94, 0x72, 0xa0, 0x6e, - 0x30, 0xb2, 0x3c, 0x6d, 0x89, 0x6e, 0x1f, 0x2a, 0x38, 0x05, 0xb0, 0xd1, 0x8a, 0xd8, 0x75, 0x09, - 0xac, 0x1b, 0x9c, 0xed, 0x87, 0x6e, 0x80, 0xdc, 0x95, 0xd1, 0xa5, 0xfb, 0xff, 0xbc, 0xf4, 0x91, - 0xcd, 0x29, 0xbb, 0xf8, 0x81, 0xa6, 0x6d, 0xa7, 0x30, 0xb2, 0xaf, 0xf0, 0x5c, 0x27, 0xe3, 0xd2, - 0x0c, 0xa2, 0x80, 0xf3, 0x43, 0x9d, 0xbd, 0xcd, 0x22, 0x97, 0x10, 0x69, 0x38, 0xb4, 0x75, 0xcd, - 0x21, 0x8b, 0x5b, 0x4f, 0xff, 0xd1, 0x9f, 0xdd, 0x2a, 0xfc, 0xc9, 0x9f, 0xdd, 0x2a, 0xfc, 0x2f, - 0x7f, 0x76, 0xeb, 0xd2, 0x5f, 0xff, 0xf3, 0x5b, 0x85, 0x3f, 0xfa, 0xf3, 0x5b, 0x85, 0x3f, 0xf9, - 0xf3, 0x5b, 0x85, 0xff, 0xf1, 0xcf, 0x6f, 0x15, 0x7e, 0xf5, 0xc1, 0xb1, 0x1b, 0x9f, 0x4c, 0x0f, - 0x1f, 0x8c, 0x82, 0xf1, 0x87, 0x63, 0x2b, 0x0e, 0xdd, 0x73, 0xb6, 0x78, 0xa9, 0x84, 0xef, 0x7c, - 0x38, 0x39, 0x3d, 0xfe, 0x70, 0x72, 0xf8, 0x21, 0x6e, 0xd0, 0x87, 0xcb, 0x93, 0x30, 0x88, 0x83, - 0x47, 0xff, 0x5f, 0x00, 0x00, 0x00, 0xff, 0xff, 0x90, 0x13, 0x34, 0xd6, 0x46, 0xbc, 0x00, 0x00, + 0x17, 0x24, 0x7c, 0x27, 0x8b, 0x29, 0x3b, 0xff, 0xb3, 0xca, 0xcb, 0x3b, 0xb0, 0x8c, 0x93, 0x24, + 0xda, 0xbc, 0x46, 0x25, 0xaf, 0xe7, 0x67, 0x10, 0xd5, 0x94, 0xf0, 0xe2, 0x1e, 0x54, 0x70, 0xa2, + 0xd0, 0xaa, 0xde, 0xcc, 0xea, 0x69, 0x72, 0x56, 0xe1, 0xce, 0xed, 0x9c, 0x0d, 0xbe, 0xf3, 0xc4, + 0x7d, 0x28, 0xd9, 0xb8, 0x98, 0xaf, 0x53, 0x89, 0x57, 0x33, 0xe3, 0x82, 0x5c, 0x74, 0xdb, 0x39, + 0x22, 0xd5, 0x91, 0x68, 0xc4, 0x2e, 0x34, 0x70, 0x1a, 0x3d, 0x24, 0x61, 0x0c, 0xbb, 0x6f, 0xf3, + 0x06, 0xe5, 0x7a, 0x63, 0x26, 0x57, 0x4f, 0x12, 0x51, 0x67, 0xb7, 0xfd, 0x38, 0xbc, 0x30, 0x56, + 0xfd, 0x2c, 0x4c, 0xdc, 0x80, 0x8a, 0x1b, 0x75, 0x83, 0xd1, 0x69, 0x72, 0xb0, 0x9c, 0xa4, 0xc5, + 0x17, 0xb0, 0x4a, 0x13, 0x0b, 0x93, 0xf8, 0x71, 0x79, 0xaa, 0x9c, 0x9d, 0x32, 0x0a, 0x65, 0xe4, + 0x29, 0x71, 0x07, 0x75, 0x23, 0x33, 0x76, 0xc6, 0x93, 0x20, 0x44, 0x0d, 0xef, 0x75, 0x75, 0x42, + 0x31, 0x54, 0x20, 0x14, 0x94, 0x12, 0x8f, 0x16, 0x33, 0x38, 0x3a, 0x8a, 0x9c, 0x98, 0x4e, 0x91, + 0x4b, 0x46, 0x43, 0x39, 0xb6, 0xf4, 0x09, 0x4a, 0x82, 0x4a, 0x64, 0xda, 0x17, 0xbe, 0x35, 0x76, + 0x47, 0x9b, 0xb7, 0x59, 0x91, 0x74, 0xa3, 0x6d, 0x06, 0x64, 0x75, 0xb9, 0x3b, 0x39, 0x5d, 0xee, + 0x32, 0x2c, 0xdb, 0x87, 0xb8, 0x1c, 0xdf, 0xa0, 0x62, 0x4b, 0xf6, 0x61, 0xc7, 0x16, 0x1f, 0x90, + 0x95, 0x99, 0x59, 0xe0, 0xa6, 0x9e, 0xb5, 0xc6, 0x25, 0x9c, 0xd1, 0x48, 0x29, 0xc4, 0x1d, 0xa8, + 0xed, 0x38, 0x56, 0x3c, 0x0d, 0x9d, 0x1d, 0xcf, 0x3a, 0xde, 0x7c, 0x93, 0x4a, 0xca, 0x82, 0xb0, + 0x76, 0x5e, 0x70, 0xec, 0x8e, 0x2c, 0x5a, 0xf9, 0x77, 0x59, 0x2e, 0x96, 0x90, 0x8e, 0x9d, 0xaa, + 0x30, 0x96, 0x14, 0x76, 0xdf, 0xca, 0xaa, 0x30, 0x96, 0xa7, 0x8e, 0x63, 0xd2, 0xbe, 0x70, 0x26, + 0xc1, 0xe8, 0x64, 0xf3, 0x6d, 0x5a, 0x6f, 0xab, 0xaa, 0x2b, 0xda, 0x08, 0x14, 0xef, 0xc0, 0x9a, + 0x54, 0xeb, 0x4d, 0xe5, 0x8e, 0xf3, 0x0e, 0xd1, 0x35, 0x24, 0xb8, 0x25, 0xbd, 0x72, 0x74, 0x58, + 0xe5, 0x02, 0x6d, 0x73, 0x64, 0x8d, 0x4e, 0x9c, 0xcd, 0x7b, 0x5c, 0x71, 0x2a, 0xce, 0x6e, 0x21, + 0xe8, 0xc6, 0x13, 0x52, 0x28, 0x69, 0xb8, 0x3e, 0x9d, 0xe1, 0x8a, 0xb9, 0x35, 0x9d, 0x61, 0x9f, + 0xbb, 0x97, 0xb2, 0xcc, 0x71, 0x6b, 0x99, 0xb6, 0x8f, 0x1b, 0xbf, 0x00, 0x31, 0x3f, 0xd1, 0x5e, + 0xc5, 0xa2, 0x97, 0x25, 0x8b, 0xfe, 0x72, 0xe9, 0x71, 0x41, 0xff, 0x1f, 0x0a, 0xb0, 0x9a, 0x63, + 0x41, 0x0b, 0xf7, 0x1a, 0x16, 0xc4, 0xad, 0xb1, 0x34, 0x64, 0x71, 0x42, 0xa9, 0x7f, 0xae, 0x7f, + 0xec, 0x39, 0x19, 0xf5, 0x8f, 0xd2, 0xe2, 0xe7, 0x70, 0xf9, 0x68, 0xea, 0x79, 0xb1, 0x73, 0x1e, + 0x2b, 0xb3, 0x7e, 0xe8, 0x1c, 0x49, 0x3b, 0xee, 0xdc, 0x21, 0xd3, 0xba, 0xa2, 0x65, 0xd3, 0x96, + 0xe1, 0x1c, 0x89, 0xdf, 0x06, 0x91, 0x14, 0xc0, 0xd2, 0x16, 0xe6, 0x5f, 0x5e, 0x9c, 0x5f, 0x53, + 0xa4, 0xd4, 0x0d, 0x86, 0x73, 0xa4, 0xff, 0x37, 0x45, 0xa8, 0xef, 0x5a, 0xd1, 0xc9, 0x9e, 0x35, + 0x19, 0xc4, 0x56, 0x1c, 0xe1, 0xc2, 0x38, 0xb1, 0xa2, 0x93, 0xb1, 0x35, 0x61, 0x65, 0xb1, 0xc0, + 0x16, 0x47, 0x09, 0x23, 0x4d, 0xf1, 0x06, 0x54, 0x30, 0xd9, 0xf7, 0xf7, 0x9f, 0x4a, 0xf3, 0x5f, + 0x92, 0x46, 0x86, 0x1c, 0x9d, 0x4c, 0x8f, 0x8e, 0x92, 0xa6, 0xaa, 0xa4, 0xb8, 0x0b, 0xab, 0xf2, + 0x27, 0xe9, 0x63, 0xe7, 0xf2, 0xb4, 0x23, 0x0f, 0x14, 0x8f, 0xa0, 0x26, 0x01, 0x43, 0xb5, 0x7d, + 0x34, 0x12, 0x33, 0x71, 0x8a, 0x30, 0xb2, 0x54, 0xe2, 0x97, 0x70, 0x25, 0x93, 0xdc, 0x09, 0xc2, + 0xbd, 0xa9, 0x17, 0xbb, 0xad, 0x9e, 0x94, 0x5d, 0x5f, 0x9b, 0xcb, 0x9e, 0x92, 0x18, 0x8b, 0x73, + 0xe6, 0x6b, 0xbb, 0xe7, 0xfa, 0xb4, 0x1b, 0x15, 0x8d, 0x3c, 0x70, 0x86, 0xca, 0x3a, 0xa7, 0x4d, + 0x28, 0x4f, 0x65, 0x9d, 0x23, 0x9b, 0x92, 0x80, 0x3d, 0x27, 0x3e, 0x09, 0x6c, 0x29, 0x8c, 0x5e, + 0xce, 0x55, 0x8b, 0x51, 0x46, 0x9e, 0x12, 0xbb, 0xd3, 0x9f, 0x7a, 0xde, 0xc8, 0x8f, 0x49, 0xff, + 0x29, 0x1a, 0x2a, 0x89, 0x1b, 0x74, 0x68, 0xf9, 0xc7, 0x4e, 0xb4, 0x59, 0xbb, 0x53, 0xbc, 0x57, + 0x30, 0x64, 0x4a, 0xff, 0xdb, 0x4b, 0xb0, 0xcc, 0x23, 0xf9, 0x1a, 0x54, 0x0f, 0xe9, 0x0c, 0xdb, + 0x9f, 0x8e, 0xd5, 0x59, 0x33, 0x01, 0x7a, 0xd3, 0x31, 0xab, 0x1d, 0xd2, 0x7c, 0x5b, 0x30, 0xe8, + 0x37, 0x16, 0x19, 0x4c, 0x63, 0xfc, 0x56, 0x91, 0xa0, 0x32, 0x85, 0x95, 0x08, 0x83, 0x33, 0x9a, + 0x0d, 0x25, 0x42, 0xa8, 0x24, 0x1d, 0x67, 0xd3, 0x5e, 0x8f, 0x99, 0x96, 0x09, 0x57, 0x21, 0x40, + 0xcb, 0x8f, 0x67, 0x4d, 0xd7, 0x2b, 0xf3, 0xa6, 0xeb, 0x5b, 0x80, 0x4a, 0xcd, 0xc8, 0xe9, 0xfb, + 0x4e, 0xab, 0x27, 0xcf, 0x95, 0x32, 0x10, 0x5c, 0xa1, 0x76, 0x30, 0x91, 0x07, 0x46, 0xf8, 0x53, + 0x7c, 0x96, 0xcc, 0x4e, 0x6a, 0xa3, 0xd4, 0x21, 0xe5, 0x5e, 0x98, 0x9d, 0xc7, 0x46, 0x8e, 0x0e, + 0x4b, 0xc2, 0x0d, 0x8e, 0x75, 0x48, 0xfc, 0xa9, 0xb7, 0x01, 0x8c, 0xe0, 0x2c, 0x72, 0x62, 0x3a, + 0xa3, 0xb9, 0x46, 0x4d, 0xcc, 0x79, 0xae, 0x04, 0x67, 0xfb, 0x41, 0x94, 0x18, 0x80, 0xe6, 0xe5, + 0x62, 0x82, 0xeb, 0x1f, 0x42, 0x19, 0xa5, 0x1f, 0x2b, 0xb6, 0xc4, 0x5d, 0x69, 0xc5, 0x66, 0x99, + 0x4d, 0x53, 0xe7, 0xa7, 0xea, 0x1b, 0xd2, 0x18, 0xde, 0x55, 0xdf, 0xa5, 0x3c, 0x6f, 0x64, 0x0c, + 0x19, 0xc9, 0xce, 0x2b, 0x0b, 0x94, 0xf2, 0xd4, 0x6b, 0x50, 0xc5, 0xaa, 0xd1, 0x11, 0xbd, 0x3a, + 0x75, 0x0c, 0x83, 0xb3, 0x16, 0xa6, 0xf5, 0x7f, 0x5c, 0x80, 0x5a, 0x3f, 0xb4, 0x71, 0xcb, 0x1f, + 0x4c, 0x9c, 0xd1, 0x2b, 0xed, 0x55, 0x28, 0x7d, 0x05, 0x9e, 0x67, 0xd1, 0xe6, 0x22, 0x0d, 0x09, + 0x09, 0x40, 0x7c, 0x0c, 0xa5, 0x23, 0xdc, 0x44, 0x8a, 0x59, 0x95, 0x2f, 0x53, 0xbc, 0xfa, 0x8d, + 0xdb, 0x8a, 0x41, 0xa4, 0xfa, 0xef, 0x26, 0xdf, 0xa7, 0xbd, 0x26, 0x7b, 0xfc, 0x7b, 0x89, 0xdc, + 0x4f, 0x06, 0x2d, 0xad, 0x20, 0x2a, 0x50, 0xda, 0x6e, 0x0f, 0x5a, 0xac, 0x16, 0xa1, 0x82, 0x34, + 0x30, 0x77, 0x3a, 0xc6, 0x60, 0xa8, 0x95, 0xc8, 0x9f, 0x85, 0x00, 0xdd, 0xe6, 0x60, 0xa8, 0x55, + 0x04, 0xc0, 0xca, 0x41, 0xaf, 0xf3, 0xcb, 0x83, 0xb6, 0xa6, 0xe9, 0xff, 0xa8, 0x00, 0x90, 0x9e, + 0x1f, 0x89, 0xf7, 0xa0, 0x76, 0x46, 0x29, 0x33, 0x73, 0x10, 0x9f, 0x6d, 0x23, 0x30, 0x9a, 0x24, + 0xc3, 0x0f, 0xa0, 0x9e, 0x6c, 0x92, 0x28, 0x35, 0xcd, 0x9f, 0xc8, 0xd7, 0x12, 0xfc, 0xd6, 0x85, + 0x78, 0x1f, 0x2a, 0x01, 0xb6, 0x03, 0x49, 0x8b, 0x59, 0x91, 0x29, 0xd3, 0x7c, 0xa3, 0x1c, 0x70, + 0x02, 0xa5, 0xab, 0xa3, 0x50, 0x19, 0x86, 0x12, 0xd2, 0x1d, 0x04, 0xb5, 0x3c, 0x6b, 0x1a, 0x39, + 0x06, 0xe3, 0x93, 0x5d, 0x62, 0x39, 0xdd, 0x25, 0xf4, 0x5f, 0x41, 0x63, 0x60, 0x8d, 0x27, 0xbc, + 0x97, 0x50, 0xc3, 0x04, 0x94, 0x70, 0x4e, 0xc8, 0xa9, 0x47, 0xbf, 0x71, 0xd1, 0xed, 0x3b, 0xe1, + 0xc8, 0xf1, 0xd5, 0x1a, 0x55, 0x49, 0x64, 0xbf, 0x07, 0xb8, 0x9b, 0x18, 0xc1, 0x99, 0xda, 0x4e, + 0x54, 0x5a, 0xff, 0xb7, 0x0b, 0x50, 0xcb, 0x54, 0x43, 0x7c, 0x08, 0x25, 0x12, 0xc3, 0x0b, 0x59, + 0x46, 0x98, 0x21, 0xe0, 0xdf, 0x2c, 0xb8, 0x21, 0xa1, 0x78, 0x1b, 0x96, 0xa3, 0xd8, 0x0a, 0xd5, + 0x51, 0xa4, 0x96, 0xc9, 0xb1, 0x15, 0x4c, 0x7d, 0xdb, 0x60, 0xb4, 0xd0, 0xa1, 0xe8, 0xf8, 0xb6, + 0x34, 0xa5, 0xcd, 0x53, 0x21, 0x52, 0xbf, 0x03, 0xd5, 0xa4, 0x78, 0x9c, 0x02, 0x46, 0xff, 0xf9, + 0x40, 0xbb, 0x24, 0xaa, 0xb0, 0x6c, 0x34, 0x7b, 0x4f, 0xda, 0x5a, 0x41, 0xff, 0xbb, 0x05, 0x80, + 0x34, 0x97, 0x78, 0x90, 0xab, 0xed, 0x8d, 0xd9, 0x52, 0x1f, 0xd0, 0xdf, 0x4c, 0x65, 0x6f, 0x42, + 0x75, 0xea, 0x13, 0xd0, 0xb1, 0xe5, 0x4e, 0x94, 0x02, 0x50, 0x77, 0x54, 0xbe, 0xb5, 0x33, 0xba, + 0xe3, 0x0b, 0xcb, 0xd3, 0xbf, 0x84, 0x6a, 0x52, 0x1c, 0xea, 0xe6, 0x3b, 0xfd, 0x6e, 0xb7, 0xff, + 0xbc, 0xd3, 0x7b, 0xa2, 0x5d, 0xc2, 0xe4, 0xbe, 0xd1, 0x6e, 0xb5, 0xb7, 0x31, 0x59, 0xc0, 0x39, + 0xdb, 0x3a, 0x30, 0x8c, 0x76, 0x6f, 0x68, 0x1a, 0xfd, 0xe7, 0xda, 0x92, 0xfe, 0xfb, 0x05, 0x58, + 0xed, 0x7b, 0x76, 0x2b, 0xf0, 0x5a, 0xd6, 0x04, 0xe5, 0x2c, 0xf1, 0x73, 0x58, 0x3f, 0x9c, 0xa2, + 0xac, 0x3f, 0xf1, 0xac, 0x91, 0x73, 0x12, 0x78, 0xb6, 0xa3, 0x16, 0x61, 0xee, 0xb0, 0x55, 0x9e, + 0x9d, 0x68, 0x44, 0xbc, 0x9f, 0xd2, 0x8a, 0x4f, 0xa1, 0x3e, 0x09, 0x83, 0x43, 0x47, 0x1d, 0x64, + 0xcd, 0x2a, 0xa9, 0x69, 0xde, 0x1a, 0xd1, 0xc9, 0x43, 0xe6, 0xff, 0x72, 0x19, 0xea, 0xdb, 0x8e, + 0x3d, 0x9d, 0x7c, 0x15, 0xb8, 0x7e, 0x2b, 0x3e, 0x17, 0x9f, 0x40, 0x3d, 0xf0, 0xc8, 0x60, 0x65, + 0x66, 0xdc, 0xe6, 0x16, 0x95, 0x03, 0x01, 0xb5, 0x80, 0x9c, 0xec, 0x3e, 0x80, 0xcb, 0xd2, 0x5c, + 0xc2, 0xd6, 0xd3, 0x73, 0xce, 0x8c, 0x6b, 0x66, 0xd9, 0xd0, 0x18, 0xc5, 0x1b, 0x34, 0x91, 0xff, + 0x16, 0x6c, 0x64, 0xc8, 0x91, 0xb1, 0x30, 0x7d, 0x71, 0x6e, 0x8d, 0xad, 0x27, 0x79, 0x13, 0x87, + 0xbe, 0xaf, 0x60, 0x43, 0xd5, 0x70, 0xc4, 0xbd, 0xc7, 0x99, 0x4b, 0x59, 0xa5, 0x2a, 0xd7, 0xbb, + 0xb2, 0xc2, 0xeb, 0x41, 0x16, 0x48, 0x65, 0x7d, 0x0c, 0x57, 0x6c, 0x6c, 0xbd, 0xc9, 0x9d, 0x7f, + 0xea, 0x38, 0x13, 0xd3, 0xcb, 0x78, 0xbb, 0x10, 0x72, 0x0b, 0x71, 0x4f, 0x1d, 0x67, 0xd2, 0xb5, + 0xa2, 0x58, 0x3c, 0x86, 0x75, 0xeb, 0xe8, 0xc8, 0x19, 0xc5, 0x8e, 0x6d, 0xe2, 0x36, 0xb6, 0xd0, + 0x4a, 0x8b, 0xb2, 0xd2, 0x9a, 0x22, 0xc3, 0xe5, 0xd8, 0x0a, 0x3c, 0xf1, 0x04, 0xae, 0x4f, 0x4e, + 0x2e, 0x22, 0x12, 0xa4, 0x47, 0x27, 0xb8, 0xdb, 0x66, 0x4a, 0x28, 0x2f, 0x28, 0xe1, 0xaa, 0x22, + 0x6f, 0x31, 0xb5, 0x2a, 0xe8, 0x31, 0x5c, 0x57, 0xdd, 0x87, 0xfa, 0x5d, 0xbe, 0xcf, 0x2b, 0xd4, + 0xe7, 0x57, 0x64, 0xbf, 0x21, 0x3e, 0xd3, 0xf1, 0xf7, 0x40, 0x63, 0xf7, 0xad, 0x23, 0x9c, 0xba, + 0xf4, 0x75, 0xe9, 0xd2, 0xde, 0x20, 0xf8, 0x0e, 0x2d, 0x3e, 0x64, 0x1f, 0xf7, 0x40, 0x73, 0xc6, + 0x6e, 0x6c, 0x5a, 0x24, 0xad, 0x32, 0x25, 0x9f, 0x8a, 0x35, 0x10, 0x2e, 0xcd, 0x76, 0x48, 0xf9, + 0x19, 0x68, 0x92, 0xe8, 0x88, 0x74, 0x00, 0x6c, 0x4d, 0x6d, 0x41, 0x6b, 0x1a, 0x4c, 0xb5, 0x83, + 0x44, 0xd8, 0x8a, 0x3d, 0x10, 0xca, 0x06, 0x49, 0x96, 0x15, 0x56, 0x64, 0x59, 0x85, 0x96, 0xee, + 0x4a, 0xfd, 0xed, 0xa7, 0x07, 0xa9, 0x7a, 0x4c, 0x8d, 0x51, 0x0b, 0xe0, 0x28, 0x0f, 0x8e, 0xf4, + 0xff, 0x7c, 0x05, 0xaa, 0x6c, 0x76, 0xc3, 0x69, 0x7c, 0x0f, 0xca, 0xc1, 0xe1, 0xb7, 0x24, 0xca, + 0xbe, 0xc4, 0xdf, 0x6a, 0x25, 0x38, 0xfc, 0x16, 0xe5, 0xdf, 0xf7, 0x94, 0x08, 0x62, 0x3b, 0x47, + 0x72, 0xd5, 0x34, 0xf2, 0x4a, 0xa6, 0x14, 0x49, 0xb6, 0x9d, 0x23, 0x94, 0x2e, 0x5d, 0x3f, 0x72, + 0xc2, 0x98, 0xcd, 0xa6, 0xe5, 0x97, 0x2f, 0x0e, 0x26, 0x23, 0x4b, 0xea, 0x23, 0xa8, 0x49, 0xd3, + 0x24, 0x65, 0xaa, 0xbc, 0x3c, 0x13, 0x93, 0x51, 0xa6, 0x2f, 0xa0, 0x91, 0x6e, 0x3f, 0x94, 0xaf, + 0xfa, 0xd2, 0x7c, 0xab, 0x09, 0x25, 0x65, 0x7d, 0x08, 0x57, 0xa3, 0x53, 0x77, 0x62, 0xca, 0x9a, + 0x06, 0x3e, 0x1d, 0xaf, 0x9a, 0x93, 0x53, 0x39, 0x80, 0x02, 0xb1, 0x1d, 0x42, 0xf6, 0xfd, 0xde, + 0xd4, 0xf3, 0xf6, 0x4f, 0xc5, 0xbb, 0xb0, 0x2e, 0xc9, 0x27, 0xc9, 0x7c, 0xa2, 0x51, 0x5c, 0x36, + 0x1a, 0x8c, 0xd8, 0x97, 0xf3, 0x48, 0x7c, 0x0e, 0x9b, 0x3c, 0x87, 0x64, 0xa3, 0x78, 0x9a, 0xf3, + 0x0c, 0xe1, 0x33, 0xd0, 0x2b, 0x84, 0x67, 0x93, 0x6a, 0x93, 0xb0, 0x34, 0x51, 0x3e, 0x82, 0x0d, + 0xf7, 0xd8, 0x0f, 0x42, 0x95, 0x45, 0x4e, 0x7f, 0x79, 0x38, 0x2a, 0x18, 0xd7, 0xcc, 0x2c, 0x1a, + 0x6c, 0x89, 0x5c, 0x9e, 0x17, 0x66, 0x4c, 0x6e, 0x19, 0x98, 0x03, 0xb5, 0xd0, 0x46, 0x76, 0x7d, + 0x5e, 0xb0, 0xcb, 0x86, 0x11, 0x9c, 0x75, 0x6c, 0x5c, 0xd2, 0x92, 0x54, 0xad, 0x91, 0xf8, 0x9c, + 0x5a, 0xb3, 0x46, 0xad, 0x11, 0x8c, 0x4c, 0x66, 0x0a, 0xb6, 0xe8, 0x17, 0x20, 0xe6, 0x96, 0x74, + 0xb4, 0xa9, 0xbd, 0xb4, 0xbf, 0xb5, 0x99, 0x95, 0x4d, 0x6b, 0x60, 0x6e, 0x45, 0xaf, 0x2f, 0x5a, + 0x03, 0xa3, 0xfc, 0x4a, 0x6e, 0xc3, 0x66, 0xfe, 0xcb, 0x67, 0x8e, 0x7b, 0x7c, 0x42, 0xb3, 0x4b, + 0xde, 0x16, 0xc8, 0xe7, 0xbf, 0x92, 0xfd, 0xf2, 0x73, 0xa2, 0x7d, 0x25, 0x67, 0xb9, 0xfc, 0xe3, + 0x39, 0x8b, 0xfe, 0xef, 0x16, 0xa0, 0xca, 0x33, 0x03, 0x17, 0xd1, 0x1b, 0x50, 0xfc, 0x81, 0x05, + 0x84, 0x38, 0x71, 0x1f, 0xd6, 0x2d, 0xdb, 0x9e, 0x19, 0x50, 0xde, 0x49, 0xd7, 0x2c, 0xdb, 0xce, + 0x8d, 0xe6, 0x3d, 0xd0, 0xdc, 0xc8, 0x54, 0x86, 0xa8, 0xd4, 0x27, 0xb3, 0x62, 0x34, 0xdc, 0x48, + 0xda, 0xa1, 0xf8, 0x54, 0x32, 0xb7, 0x26, 0x4b, 0x3f, 0xbc, 0x26, 0x75, 0x01, 0x60, 0x38, 0xb4, + 0x6d, 0xb6, 0xe2, 0xf3, 0xaf, 0x4a, 0x95, 0x82, 0x06, 0xfa, 0x7f, 0x55, 0x83, 0x5a, 0xd3, 0xb7, + 0xbc, 0x8b, 0xef, 0x1d, 0x72, 0x28, 0xa5, 0x93, 0xa0, 0xc9, 0x54, 0xce, 0x52, 0xf6, 0xb0, 0xa8, + 0x12, 0x84, 0x6a, 0x76, 0x1b, 0x6a, 0xc1, 0x34, 0x4e, 0xf0, 0xec, 0x73, 0x01, 0x0c, 0x22, 0x82, + 0x24, 0x7f, 0x72, 0xfe, 0xa9, 0xf2, 0x93, 0x42, 0x9b, 0xe6, 0x4f, 0x94, 0x9c, 0x24, 0x3f, 0x11, + 0xbc, 0x09, 0xab, 0xb1, 0x3b, 0x76, 0xc8, 0xe5, 0x65, 0x3a, 0x76, 0x6c, 0xbe, 0x7c, 0xc3, 0xf7, + 0x39, 0x5a, 0x12, 0x86, 0xa5, 0x8c, 0x9d, 0x71, 0x10, 0x5e, 0x70, 0x29, 0x2b, 0x5c, 0x0a, 0x83, + 0xa8, 0x94, 0xf7, 0x41, 0x9c, 0x59, 0x6e, 0x6c, 0xe6, 0x8b, 0x62, 0xc5, 0x52, 0x43, 0xcc, 0x30, + 0x5b, 0xdc, 0x55, 0x58, 0xb1, 0xdd, 0xe8, 0xb4, 0xd3, 0x97, 0x4a, 0xa5, 0x4c, 0x61, 0x5b, 0xa2, + 0x91, 0x85, 0x32, 0x6d, 0xec, 0x30, 0xf7, 0x2f, 0x1a, 0x55, 0x84, 0x6c, 0x21, 0x00, 0x65, 0x22, + 0xdf, 0x89, 0xcf, 0x82, 0x10, 0x73, 0xb2, 0xce, 0x98, 0x02, 0x50, 0x76, 0x44, 0x52, 0xfc, 0x10, + 0xb1, 0x87, 0xa2, 0x91, 0xa4, 0x51, 0x1b, 0x63, 0x56, 0x41, 0xd8, 0x3a, 0x57, 0x3f, 0x85, 0x88, + 0xbb, 0xd0, 0xa0, 0xea, 0x93, 0x4e, 0x89, 0x6d, 0xa0, 0x95, 0x5f, 0x34, 0xea, 0x08, 0x25, 0xc3, + 0x1a, 0x52, 0x7d, 0x01, 0xd7, 0x73, 0xed, 0x33, 0xad, 0x30, 0xb4, 0x2e, 0xcc, 0xb1, 0xf5, 0x6d, + 0x10, 0x92, 0x11, 0xb4, 0x68, 0x5c, 0xcd, 0x76, 0x5b, 0x13, 0xd1, 0x7b, 0x88, 0x7d, 0x69, 0x56, + 0xd7, 0x0f, 0x42, 0xb2, 0x90, 0x2e, 0xcc, 0x8a, 0x58, 0x32, 0xe7, 0xd1, 0x00, 0x93, 0x82, 0x1b, + 0xf1, 0x3d, 0x20, 0xa3, 0x46, 0xb0, 0x2d, 0x02, 0xa1, 0x8a, 0x17, 0x3d, 0xe2, 0x3d, 0x76, 0x5d, + 0x7a, 0xb3, 0x3f, 0xa2, 0x4d, 0x95, 0x11, 0x27, 0x8e, 0x65, 0xd3, 0x9a, 0x25, 0xc4, 0xae, 0x63, + 0x91, 0x93, 0x56, 0xf4, 0xc8, 0x9c, 0x4c, 0x63, 0xbe, 0xc0, 0x63, 0x2c, 0x47, 0x8f, 0xf6, 0xa7, + 0xb1, 0x04, 0x1f, 0x3b, 0x31, 0x79, 0x56, 0x10, 0xf8, 0x09, 0xdf, 0x33, 0x8b, 0x1e, 0xa9, 0x03, + 0xb7, 0x2b, 0xb2, 0x6f, 0x1f, 0xc9, 0x13, 0x35, 0x1d, 0x56, 0x13, 0xa4, 0x39, 0x9e, 0xf2, 0x8d, + 0x9d, 0xa2, 0x51, 0x53, 0x04, 0x7b, 0x53, 0x0f, 0x07, 0x96, 0x4c, 0x61, 0x66, 0x88, 0x55, 0xb9, + 0xc6, 0x43, 0x47, 0x10, 0x03, 0x6b, 0xf3, 0x1a, 0x70, 0xc2, 0x3c, 0x71, 0x63, 0xb2, 0xd4, 0x16, + 0x8d, 0x0a, 0x01, 0x76, 0xdd, 0x18, 0xd7, 0x31, 0x23, 0xe5, 0x0c, 0xa4, 0x22, 0xae, 0x13, 0xd1, + 0x1a, 0x21, 0xf6, 0x08, 0x4e, 0x05, 0xa1, 0x10, 0x91, 0xa5, 0xc5, 0xf2, 0x6e, 0x10, 0x69, 0x23, + 0x43, 0x8a, 0xa5, 0xbe, 0x0d, 0x9c, 0xd9, 0xc4, 0xa9, 0xc7, 0x65, 0xbe, 0xc6, 0x06, 0x0e, 0x02, + 0x6f, 0xbb, 0xd1, 0x29, 0x95, 0x78, 0x17, 0x1a, 0x19, 0x3a, 0x2c, 0xef, 0x26, 0xcf, 0x8c, 0x84, + 0x2c, 0x57, 0xc7, 0xd0, 0x19, 0x07, 0xb1, 0x6c, 0xe6, 0xeb, 0x99, 0x3a, 0x1a, 0x04, 0xcf, 0xd7, + 0x51, 0xd2, 0x62, 0x99, 0xb7, 0x32, 0x75, 0x64, 0x52, 0x2c, 0xf5, 0x0d, 0xa8, 0x9f, 0x85, 0x6e, + 0x1c, 0x3b, 0x52, 0xc8, 0xb9, 0xcd, 0x1d, 0x2b, 0x61, 0xb4, 0xfa, 0xdf, 0x80, 0x3a, 0xf7, 0xbc, + 0xe4, 0x6f, 0x77, 0x98, 0x44, 0xc2, 0x14, 0x83, 0x90, 0xbd, 0x31, 0x76, 0x7d, 0x32, 0xc7, 0x16, + 0x8d, 0x2a, 0x43, 0xf6, 0x5c, 0x3f, 0x8b, 0xb6, 0xce, 0xc9, 0x28, 0x9b, 0xa2, 0xad, 0x73, 0x5a, + 0x92, 0x13, 0xd7, 0xf3, 0x78, 0xe1, 0xbf, 0x29, 0x97, 0x24, 0x42, 0x68, 0xdd, 0x27, 0x68, 0xfa, + 0xfa, 0xdd, 0x0c, 0x9a, 0xbe, 0x8d, 0x13, 0x87, 0xd0, 0xf8, 0xe9, 0xb7, 0xe4, 0xc4, 0x41, 0x00, + 0x7e, 0x39, 0x45, 0x5a, 0xe7, 0x64, 0x72, 0x4d, 0x90, 0xd6, 0xb9, 0xe4, 0x5b, 0xb4, 0x53, 0x60, + 0xde, 0x77, 0x12, 0xbe, 0x85, 0x20, 0xcc, 0x9d, 0x25, 0xb0, 0xce, 0xc9, 0xc6, 0x9a, 0x21, 0xb0, + 0xce, 0xc9, 0xb8, 0xe0, 0x58, 0x36, 0x57, 0xfc, 0x5d, 0x2e, 0x1e, 0x01, 0x54, 0xef, 0x3b, 0x50, + 0x8f, 0x1e, 0x99, 0x29, 0xfe, 0x3e, 0x67, 0x8f, 0x1e, 0x19, 0x8a, 0xe2, 0x2e, 0x34, 0x92, 0xa9, + 0xc1, 0x34, 0xef, 0xf1, 0xc0, 0xdb, 0x72, 0x6a, 0xd0, 0x95, 0x96, 0x5f, 0x17, 0xe0, 0x06, 0x1f, + 0x71, 0x12, 0xfb, 0xdf, 0x73, 0xa2, 0xc8, 0x3a, 0x76, 0x76, 0x82, 0x70, 0x67, 0xfa, 0xfd, 0xf7, + 0x17, 0xe2, 0x1e, 0xac, 0xed, 0x5b, 0xa1, 0xe3, 0xc7, 0x89, 0xe7, 0x81, 0x34, 0xaa, 0xce, 0x82, + 0xc5, 0x63, 0xd0, 0x18, 0xc4, 0x9e, 0xfc, 0x2d, 0x75, 0x6e, 0x3e, 0x7b, 0x10, 0x35, 0x47, 0xa5, + 0xff, 0xe3, 0x02, 0x54, 0xb7, 0xdd, 0x28, 0x36, 0x70, 0xbb, 0x14, 0x5f, 0x80, 0xe6, 0x05, 0x67, + 0xa8, 0xec, 0x93, 0x18, 0x9d, 0xd1, 0x39, 0xe5, 0x2e, 0x99, 0x2a, 0x9a, 0x0d, 0x22, 0x4c, 0x35, + 0xc5, 0x2f, 0x40, 0x9b, 0x4e, 0x26, 0xf9, 0xac, 0x4b, 0x2f, 0xc9, 0x4a, 0x84, 0x69, 0xd6, 0xf7, + 0xa0, 0x96, 0xf9, 0xea, 0x02, 0xbd, 0x14, 0xd2, 0x6f, 0x21, 0x71, 0xe6, 0x3b, 0x0b, 0x6e, 0x12, + 0x41, 0x5a, 0xba, 0xfe, 0xef, 0x2f, 0x81, 0x26, 0x2d, 0xba, 0x96, 0xed, 0x84, 0xe4, 0xc0, 0x90, + 0xb3, 0x68, 0x14, 0x5e, 0x69, 0xd1, 0xb8, 0x03, 0xcb, 0x9e, 0x3b, 0x4e, 0x5c, 0x90, 0x73, 0x07, + 0xc0, 0x84, 0xc0, 0xb1, 0x0e, 0x42, 0xf7, 0x98, 0x6c, 0x2f, 0xd9, 0xcb, 0x25, 0x74, 0x50, 0xb0, + 0x33, 0xf5, 0x47, 0x34, 0x44, 0x0f, 0x00, 0x6c, 0x37, 0x8a, 0x4d, 0xb2, 0x46, 0xe6, 0xcd, 0xd8, + 0x49, 0xff, 0x1b, 0x55, 0x3b, 0x19, 0x8a, 0x7b, 0xa0, 0x65, 0xe4, 0x64, 0x3f, 0x31, 0x24, 0x2e, + 0x1b, 0xa9, 0xfc, 0xdc, 0xf2, 0x5b, 0x7e, 0x3c, 0x47, 0x89, 0x32, 0xe1, 0xca, 0x1c, 0x25, 0xca, + 0x83, 0xf7, 0x40, 0x0b, 0x5e, 0x38, 0xa1, 0x79, 0xe4, 0xc4, 0xa3, 0x13, 0x93, 0x9b, 0x55, 0xe6, + 0x83, 0x1b, 0x84, 0xef, 0x20, 0xb8, 0x8b, 0x50, 0xfd, 0x1c, 0x2e, 0x3f, 0x73, 0x46, 0x71, 0x10, + 0x76, 0x12, 0xcf, 0x17, 0x79, 0xb3, 0x2f, 0x0c, 0xbc, 0xc4, 0xb6, 0x8f, 0xbf, 0xc5, 0x3b, 0x80, + 0x1a, 0x87, 0x33, 0x52, 0x3d, 0xb4, 0x50, 0x21, 0x71, 0x46, 0xd8, 0x4f, 0xcb, 0xa9, 0x6c, 0x34, + 0x2f, 0xf8, 0x30, 0x52, 0xff, 0x5b, 0x65, 0x58, 0xcb, 0x7c, 0x9a, 0xae, 0xc4, 0x3c, 0x84, 0xba, + 0x3c, 0x02, 0x48, 0x9d, 0xbe, 0x16, 0x7c, 0xa8, 0xc6, 0x44, 0x2c, 0x6a, 0x3d, 0x06, 0x2d, 0x9b, + 0xe7, 0x07, 0xb4, 0xa0, 0x46, 0x26, 0x1b, 0xea, 0x42, 0x77, 0x61, 0x99, 0x4f, 0xe6, 0x72, 0xf5, + 0x4c, 0x4e, 0x3d, 0x19, 0x29, 0x7e, 0x07, 0x56, 0xf9, 0xe4, 0xd7, 0x94, 0x77, 0x7f, 0x4a, 0x59, + 0x05, 0x6f, 0x41, 0xe7, 0x19, 0x75, 0xa6, 0xa7, 0x74, 0x24, 0x3e, 0x80, 0x3a, 0xb9, 0x76, 0x9b, + 0x2f, 0x88, 0x34, 0xef, 0x4d, 0xca, 0x66, 0x38, 0xc2, 0x73, 0x49, 0xe2, 0x3d, 0x58, 0xc7, 0xb9, + 0x61, 0xf9, 0x23, 0x87, 0x26, 0x1a, 0xd9, 0x29, 0xf9, 0x94, 0x58, 0x53, 0x88, 0xe4, 0x08, 0xe6, + 0xb7, 0xa0, 0x6a, 0xbb, 0xa1, 0xc3, 0x44, 0xe5, 0x1f, 0x63, 0xb3, 0x4c, 0xe9, 0xc5, 0x23, 0xdc, + 0xdb, 0x7c, 0xdb, 0x25, 0x1d, 0x83, 0xe7, 0x48, 0x65, 0xae, 0x6e, 0x8d, 0x84, 0x84, 0xe6, 0x8b, + 0xf8, 0x8c, 0xf8, 0x1d, 0x57, 0x8f, 0x67, 0x78, 0x75, 0xf1, 0x0c, 0x5f, 0x55, 0x64, 0x3c, 0xcb, + 0xdf, 0x83, 0xda, 0x24, 0x74, 0xcc, 0x23, 0xd7, 0x8b, 0x9d, 0x10, 0x15, 0xf1, 0x59, 0x3b, 0x09, + 0x4c, 0x42, 0x67, 0x87, 0xb1, 0x0b, 0x1d, 0x8f, 0x6a, 0x0b, 0x1d, 0x8f, 0xc4, 0x03, 0xb8, 0xec, + 0xfa, 0x6e, 0xec, 0x5a, 0x9e, 0xc9, 0xd6, 0x23, 0xb6, 0x12, 0xd7, 0xe9, 0xc8, 0x6d, 0x5d, 0xa2, + 0xf6, 0x11, 0x43, 0xe6, 0x62, 0xf1, 0x19, 0xac, 0x1f, 0xb9, 0x21, 0x2e, 0x4f, 0xe2, 0x5e, 0xdc, + 0xec, 0xd5, 0xb9, 0x66, 0xaf, 0x11, 0x91, 0x81, 0x34, 0xdc, 0xee, 0xf7, 0x41, 0x1c, 0x4e, 0x47, + 0xa7, 0x4e, 0x6c, 0x3a, 0xe7, 0x13, 0xcb, 0xb7, 0xcd, 0x28, 0x76, 0x26, 0xa4, 0xc4, 0xad, 0x1a, + 0x1a, 0x63, 0xda, 0x84, 0x18, 0xc4, 0xce, 0x44, 0xbc, 0x05, 0x8d, 0xf8, 0x04, 0xb7, 0x84, 0xc8, + 0x8c, 0x1c, 0x2b, 0x1c, 0x9d, 0xf0, 0xb5, 0x6d, 0x63, 0x55, 0x42, 0x07, 0x04, 0x14, 0x8f, 0x60, + 0x95, 0x04, 0xd9, 0xe4, 0x1e, 0x9d, 0xb6, 0xf0, 0x1e, 0x5d, 0x1d, 0x89, 0x54, 0x4a, 0x3c, 0x82, + 0xab, 0x93, 0x20, 0x8a, 0x65, 0x57, 0x9a, 0xe9, 0x3a, 0x27, 0xa1, 0xae, 0x62, 0x5c, 0x46, 0x2c, + 0xf7, 0x64, 0x5f, 0xad, 0x75, 0xf1, 0x19, 0x90, 0x80, 0x6c, 0xa2, 0x10, 0x2c, 0xf5, 0xb2, 0xf9, + 0x09, 0x8c, 0x4b, 0xf0, 0x79, 0x10, 0x9e, 0xb2, 0x44, 0x8c, 0xbf, 0xf4, 0xbf, 0xfd, 0x3e, 0x94, + 0x7a, 0x81, 0xed, 0x88, 0x8f, 0xa0, 0x4a, 0x77, 0x74, 0x32, 0x3b, 0x85, 0x34, 0x54, 0x21, 0x9a, + 0xfe, 0x10, 0xcb, 0xaf, 0xf8, 0xf2, 0xd7, 0xcb, 0x2f, 0x8b, 0xbe, 0x41, 0x06, 0x56, 0x72, 0xd6, + 0xc3, 0x7a, 0xd4, 0xd4, 0x0d, 0x68, 0x2b, 0x8e, 0x0c, 0xc6, 0xa0, 0x90, 0x4e, 0x8e, 0x03, 0xa1, + 0xe3, 0xd3, 0x72, 0x5b, 0x36, 0x92, 0x34, 0x99, 0xb5, 0xc3, 0x00, 0x59, 0x01, 0x8b, 0xb2, 0xcb, + 0x0b, 0xcc, 0xda, 0x8c, 0x27, 0xd9, 0xf6, 0x23, 0xa8, 0x7e, 0x1b, 0xb8, 0x3e, 0x57, 0x7c, 0x65, + 0xae, 0xe2, 0x5f, 0x05, 0xae, 0xcf, 0x15, 0xff, 0x56, 0xfe, 0x12, 0x6f, 0x42, 0x39, 0xf0, 0xb9, + 0xec, 0xf2, 0x5c, 0xd9, 0x2b, 0x81, 0xdf, 0xe5, 0xdb, 0x12, 0xab, 0x6e, 0x64, 0x86, 0xa4, 0xeb, + 0x62, 0x4e, 0xe9, 0xe4, 0x57, 0x73, 0x23, 0x03, 0x61, 0x58, 0x2c, 0xce, 0x79, 0x39, 0x48, 0x54, + 0x58, 0x75, 0x7e, 0xce, 0x33, 0x9a, 0x0a, 0x7c, 0x0b, 0x2a, 0xc7, 0x61, 0x40, 0x96, 0x82, 0x05, + 0xab, 0xa3, 0x4c, 0xb8, 0xad, 0x0b, 0xd4, 0xc3, 0xe8, 0xa7, 0xeb, 0x1f, 0x9b, 0x74, 0x52, 0x81, + 0xeb, 0xa2, 0x62, 0xd4, 0x15, 0x90, 0xce, 0x20, 0xde, 0x82, 0x8a, 0x75, 0x7c, 0x6c, 0xca, 0x4b, + 0x1f, 0x73, 0x65, 0x59, 0xc7, 0xc7, 0xf4, 0xc9, 0x07, 0xb0, 0x7a, 0xe6, 0xfa, 0x66, 0x34, 0x71, + 0x46, 0x4c, 0xbb, 0x3a, 0xdf, 0x95, 0x67, 0xae, 0x8f, 0x3c, 0x84, 0xe8, 0xb3, 0xfb, 0x69, 0xe3, + 0xc7, 0xef, 0xa7, 0x6b, 0x2f, 0xdb, 0x4f, 0x75, 0x58, 0x91, 0x4e, 0x05, 0xda, 0x1c, 0x89, 0xc4, + 0x88, 0x8f, 0xa1, 0x16, 0x5a, 0xfe, 0xa9, 0x29, 0x3d, 0x26, 0xbf, 0xc9, 0x5a, 0xdb, 0x0d, 0xcb, + 0x3f, 0x95, 0x0e, 0x93, 0x10, 0x26, 0xbf, 0xf3, 0xba, 0xf7, 0xfa, 0x2b, 0xec, 0x61, 0x19, 0x33, + 0x9b, 0xf8, 0x61, 0x33, 0xdb, 0xa7, 0x64, 0xcf, 0x72, 0xfc, 0xd8, 0x54, 0x19, 0x2e, 0x2f, 0xce, + 0x50, 0x67, 0xb2, 0x3e, 0x67, 0xc3, 0x06, 0xd0, 0x69, 0x97, 0x49, 0x47, 0x63, 0x1b, 0xb9, 0x06, + 0x24, 0xc7, 0x60, 0x06, 0x84, 0xe9, 0x91, 0x58, 0x13, 0xd6, 0xd2, 0x9b, 0xa3, 0x7c, 0x5d, 0xf7, + 0x4a, 0xf6, 0xbc, 0x3f, 0x77, 0xd5, 0x54, 0x59, 0xd0, 0xdc, 0xdc, 0xfd, 0xd3, 0x37, 0x61, 0x95, + 0x9d, 0xa7, 0xb9, 0xdf, 0x22, 0xe9, 0xaf, 0x5e, 0x27, 0x20, 0xf7, 0x53, 0x44, 0x92, 0x8a, 0x34, + 0xeb, 0xc5, 0xe7, 0xa4, 0x6e, 0xa5, 0x7c, 0x9c, 0xed, 0x78, 0xf1, 0xb9, 0x51, 0xb5, 0xd5, 0x4f, + 0xd4, 0x22, 0x0e, 0x5d, 0xdf, 0xc6, 0xa9, 0x17, 0x5b, 0xc7, 0xd1, 0xe6, 0x26, 0xad, 0xcc, 0x9a, + 0x84, 0x0d, 0xad, 0xe3, 0x48, 0x7c, 0x02, 0x75, 0x8b, 0xad, 0x16, 0x5c, 0xef, 0xeb, 0xd9, 0xd3, + 0xa1, 0x8c, 0x3d, 0xc3, 0xa8, 0x59, 0x19, 0xe3, 0xc6, 0xe7, 0x20, 0x94, 0xcf, 0x53, 0xc6, 0x96, + 0x7e, 0x63, 0x6e, 0x36, 0xae, 0x49, 0xa7, 0xa7, 0xc4, 0x92, 0x7e, 0x1b, 0xa4, 0x28, 0xc0, 0xec, + 0xf8, 0x35, 0xaa, 0x10, 0x30, 0x88, 0x18, 0xf1, 0xe7, 0xb0, 0x9a, 0x37, 0xd7, 0xdc, 0x5c, 0xe0, + 0x3a, 0xc4, 0x1b, 0x77, 0x7d, 0x94, 0x35, 0xe0, 0xbc, 0xc9, 0x37, 0xa0, 0x49, 0xd5, 0xa2, 0x8c, + 0xec, 0x1e, 0x53, 0xf7, 0x83, 0xb8, 0xa5, 0x60, 0xd8, 0x81, 0xca, 0x98, 0x1a, 0x9f, 0x93, 0x76, + 0x96, 0x74, 0x60, 0x62, 0x83, 0x32, 0xaa, 0x6e, 0x62, 0x8e, 0xda, 0x06, 0xa1, 0x1c, 0x14, 0x50, + 0x4a, 0x65, 0xf7, 0x5b, 0x52, 0xc6, 0x12, 0x6f, 0xa2, 0x59, 0x21, 0xd6, 0x60, 0x57, 0xe5, 0xac, + 0x58, 0xfb, 0x18, 0x1a, 0xb8, 0x95, 0x66, 0xbe, 0xac, 0x67, 0x1b, 0xb5, 0x1f, 0x3a, 0xe9, 0xc7, + 0xeb, 0x93, 0x4c, 0x4a, 0xfc, 0x1c, 0xd6, 0x33, 0x39, 0xa7, 0xa7, 0x94, 0xf9, 0x4d, 0xca, 0xbc, + 0x31, 0x93, 0xf9, 0xe0, 0x14, 0xb3, 0x37, 0x26, 0xb9, 0xb4, 0xd8, 0x80, 0xe5, 0x4e, 0xd4, 0xf6, + 0x6d, 0x52, 0xd2, 0x2a, 0x06, 0x27, 0xc4, 0x23, 0xa8, 0xb3, 0x45, 0x84, 0x0c, 0x93, 0xd1, 0xe6, + 0xdb, 0xd9, 0xe3, 0x5f, 0x32, 0x8b, 0xb0, 0x75, 0xb3, 0xe6, 0x25, 0xbf, 0x23, 0xdc, 0x88, 0xf9, + 0x6c, 0x3e, 0xcb, 0x22, 0xdf, 0x99, 0x1f, 0x72, 0x22, 0xda, 0x49, 0xf9, 0xa4, 0x01, 0xd7, 0xc3, + 0xa9, 0x4f, 0x56, 0x12, 0x99, 0x93, 0xf7, 0x7d, 0xca, 0x7f, 0x8f, 0xf2, 0xcb, 0x98, 0x1a, 0x06, + 0x93, 0x71, 0x5e, 0xe2, 0x4d, 0x57, 0xc3, 0x2c, 0x88, 0xa4, 0x82, 0x97, 0x94, 0xc9, 0xa7, 0x29, + 0x54, 0xe6, 0xbb, 0x3f, 0xa5, 0x4c, 0x3a, 0x69, 0xa1, 0x32, 0x05, 0x94, 0xa6, 0x53, 0xd7, 0x26, + 0x95, 0xb1, 0x6e, 0xd0, 0x6f, 0x14, 0x0b, 0x42, 0x67, 0x34, 0x0d, 0x23, 0xf7, 0x85, 0x63, 0x46, + 0xae, 0x7f, 0x4a, 0xca, 0x62, 0xc5, 0x58, 0x4d, 0xa0, 0x03, 0xd7, 0x3f, 0x45, 0x96, 0xe1, 0x9c, + 0xc7, 0x4e, 0xe8, 0xf3, 0xd5, 0xf1, 0xf7, 0xb3, 0x2c, 0xa3, 0x4d, 0x08, 0x5c, 0xe7, 0x06, 0x38, + 0xc9, 0xef, 0x99, 0x91, 0x8d, 0x78, 0x64, 0x1f, 0xfc, 0xa8, 0x91, 0x1d, 0xd0, 0xc8, 0xbe, 0x0d, + 0x15, 0xd7, 0x8f, 0x9d, 0xf0, 0x85, 0xe5, 0x6d, 0x7e, 0x38, 0xc7, 0x8d, 0x13, 0x9c, 0xb8, 0x0b, + 0xe5, 0xc8, 0x73, 0x71, 0xbd, 0x6f, 0x7e, 0x34, 0x47, 0xa6, 0x50, 0xe2, 0x1e, 0x54, 0x93, 0x40, + 0x2f, 0x9b, 0x1f, 0xcf, 0xd1, 0xa5, 0x48, 0x71, 0x0b, 0x4a, 0x67, 0x38, 0xa1, 0x1e, 0xce, 0x1f, + 0xd7, 0x23, 0x1c, 0xb7, 0xef, 0x23, 0x54, 0xfe, 0x69, 0xfb, 0x7e, 0x34, 0xb7, 0x7d, 0xef, 0xb8, + 0x9e, 0xc7, 0xdb, 0xf7, 0x91, 0xfc, 0x85, 0x9b, 0x1f, 0xe5, 0xc0, 0x96, 0x7c, 0x32, 0xbf, 0xf9, + 0x21, 0xee, 0x19, 0x85, 0xc4, 0xa9, 0x45, 0x74, 0x06, 0xcd, 0x47, 0xe9, 0x9f, 0x66, 0xfb, 0x2a, + 0x7f, 0x38, 0x6d, 0x40, 0x94, 0xa4, 0xc5, 0xeb, 0x20, 0x8f, 0xd8, 0x49, 0xfb, 0xfa, 0x8c, 0x03, + 0x0f, 0x30, 0x04, 0x15, 0xaf, 0x8f, 0x60, 0x55, 0xf9, 0x4e, 0xe3, 0xe7, 0xa2, 0xcd, 0xcf, 0xe7, + 0x6a, 0x90, 0x27, 0x10, 0xdb, 0x50, 0x3f, 0x9a, 0x7e, 0xff, 0xfd, 0xc5, 0x98, 0x6d, 0x02, 0x9b, + 0x8f, 0xa9, 0x22, 0x77, 0xd4, 0xc6, 0xfa, 0x32, 0x9b, 0x81, 0x91, 0xcb, 0x25, 0x1e, 0x80, 0x70, + 0x8f, 0x78, 0x3c, 0x77, 0xc2, 0x60, 0xcc, 0x7a, 0xff, 0xe6, 0x17, 0xf2, 0x5c, 0x62, 0x0e, 0x43, + 0x0e, 0x39, 0x8e, 0x6f, 0x9b, 0xe3, 0x48, 0x8a, 0x09, 0x5f, 0x52, 0x3d, 0x25, 0xf3, 0x4a, 0x02, + 0x42, 0xa9, 0x03, 0x57, 0xa4, 0xdd, 0x8b, 0x58, 0x6a, 0xf8, 0x02, 0x70, 0xba, 0xbe, 0x48, 0xb3, + 0xfe, 0xd6, 0x0f, 0x66, 0x45, 0x5a, 0x95, 0xf5, 0x31, 0x34, 0xf8, 0x34, 0x84, 0x24, 0x32, 0x9c, + 0xa2, 0x3f, 0xcb, 0x72, 0xae, 0xec, 0x31, 0xae, 0x51, 0xb7, 0xb3, 0x87, 0xba, 0x9f, 0xc3, 0x5a, + 0xe6, 0x30, 0x84, 0x3e, 0xfb, 0xdb, 0xd9, 0xcf, 0x26, 0xa7, 0x21, 0xc6, 0xea, 0x54, 0xfd, 0xa4, + 0x4f, 0xce, 0x89, 0xd8, 0xbf, 0xf3, 0x23, 0x44, 0xec, 0x37, 0x21, 0x5d, 0x91, 0xe6, 0x28, 0x76, + 0x36, 0x7f, 0xce, 0xcc, 0x3f, 0x01, 0xb6, 0x62, 0x47, 0x3c, 0x02, 0xb0, 0x26, 0x13, 0xef, 0x82, + 0xa7, 0xe6, 0x2f, 0x68, 0x6a, 0x6e, 0x64, 0xa6, 0x66, 0x13, 0x91, 0x34, 0x37, 0xab, 0x96, 0xfa, + 0x89, 0x0a, 0x2e, 0x09, 0xef, 0xf6, 0xd8, 0xa3, 0xf6, 0x37, 0xb3, 0x6b, 0x7b, 0x3f, 0x88, 0xe2, + 0xed, 0xb1, 0x87, 0xad, 0x80, 0x49, 0xf2, 0x5b, 0x74, 0xe1, 0x72, 0xe0, 0x9b, 0xf6, 0x74, 0xe2, + 0xb9, 0x23, 0xec, 0x01, 0x3e, 0x85, 0xdc, 0xdc, 0xca, 0xde, 0x4f, 0xa2, 0x2f, 0xf6, 0xfd, 0x6d, + 0x45, 0x24, 0x4f, 0x37, 0xd7, 0x83, 0x59, 0x10, 0x19, 0xac, 0x68, 0x0c, 0x92, 0xab, 0x3d, 0x2d, + 0x16, 0x0d, 0x08, 0xaa, 0xee, 0xf6, 0x3c, 0x86, 0xb5, 0x94, 0x0a, 0x1b, 0x18, 0x6d, 0x6e, 0x67, + 0x67, 0x72, 0xe6, 0x16, 0xe4, 0xaa, 0xca, 0x88, 0xb0, 0x88, 0xfa, 0x2e, 0xf0, 0xbc, 0xe9, 0x44, + 0xb2, 0xd2, 0xcd, 0xb6, 0xec, 0x3b, 0x02, 0x32, 0x97, 0xcc, 0xd8, 0xf4, 0x9c, 0xf1, 0xe6, 0x4e, + 0xd6, 0xa6, 0xe7, 0x8c, 0x51, 0xcc, 0x90, 0x7e, 0xf6, 0x2f, 0x5c, 0xe7, 0x2c, 0xda, 0x7c, 0x42, + 0x9a, 0x9f, 0xbc, 0xf3, 0xf7, 0x0c, 0x41, 0xb8, 0xef, 0xb3, 0x1e, 0x4b, 0x24, 0x9b, 0xbb, 0x7c, + 0xe9, 0x8b, 0x41, 0x48, 0x21, 0x5a, 0xb0, 0x49, 0xec, 0x5c, 0xae, 0xd5, 0x9c, 0x1f, 0x4c, 0x67, + 0x6e, 0x49, 0x5e, 0x41, 0x5a, 0x76, 0xb3, 0xd9, 0xcf, 0x78, 0xc4, 0xfc, 0x02, 0x5e, 0x5f, 0x5c, + 0x88, 0x0a, 0x3b, 0xf0, 0x15, 0xc9, 0x1b, 0xd7, 0x17, 0xe4, 0x96, 0xb1, 0x07, 0xbe, 0x84, 0xd5, + 0x63, 0x8b, 0x7a, 0xc2, 0x33, 0xc7, 0x81, 0xed, 0x6c, 0x3e, 0xcd, 0x06, 0xd9, 0xa1, 0x41, 0x7b, + 0x62, 0x61, 0xa7, 0x78, 0x7b, 0x81, 0xed, 0x18, 0xb5, 0xe3, 0x34, 0x21, 0x1e, 0xc3, 0x66, 0x3a, + 0x09, 0xa7, 0x3e, 0x7e, 0x17, 0x55, 0x6a, 0xd7, 0x1f, 0xc5, 0x9b, 0x5d, 0xea, 0xd3, 0xab, 0x09, + 0xfe, 0x00, 0xd1, 0xdb, 0x12, 0x2b, 0x5a, 0xa0, 0x11, 0x73, 0x90, 0xbb, 0x58, 0xe4, 0xda, 0xce, + 0xe6, 0x5e, 0x36, 0x88, 0x06, 0xb3, 0x4e, 0x24, 0xa1, 0xfd, 0x6a, 0xe0, 0xda, 0x8e, 0xd1, 0x38, + 0xca, 0xa5, 0xc5, 0xbb, 0xb0, 0xae, 0xf4, 0x11, 0xf3, 0xc4, 0x8a, 0x4e, 0xcc, 0x53, 0xe7, 0x62, + 0xb3, 0x47, 0x0d, 0x6e, 0x48, 0x65, 0x64, 0xd7, 0x8a, 0x4e, 0x9e, 0x3a, 0x17, 0xe2, 0x3e, 0xea, + 0xd4, 0xb4, 0x6d, 0xba, 0x91, 0x79, 0x68, 0x85, 0xa1, 0xeb, 0x84, 0x9b, 0x7d, 0x3e, 0x42, 0x63, + 0x44, 0x27, 0xda, 0x62, 0x30, 0xea, 0xd1, 0xd9, 0xc1, 0x90, 0xea, 0xfa, 0x3e, 0xf1, 0x51, 0x2d, + 0xe3, 0x8e, 0xc4, 0xda, 0xfa, 0x47, 0xd0, 0x48, 0xfa, 0x8f, 0x9d, 0x72, 0x7e, 0x39, 0xb7, 0x4f, + 0xd4, 0x65, 0xa7, 0x0d, 0xc8, 0x2b, 0xe7, 0x7d, 0xa8, 0x27, 0x39, 0x1c, 0xdf, 0xde, 0x34, 0xe6, + 0xcd, 0x86, 0x92, 0x1e, 0x77, 0x97, 0x4f, 0xe1, 0x1a, 0x4f, 0x73, 0x3e, 0x3a, 0x39, 0x75, 0x2e, + 0x92, 0x98, 0x29, 0x03, 0xaa, 0xff, 0x06, 0xa1, 0x3b, 0x88, 0x7d, 0xea, 0x5c, 0x44, 0x09, 0xf7, + 0xbc, 0x4e, 0xae, 0x05, 0x23, 0x79, 0x63, 0x98, 0xcd, 0xea, 0xb2, 0x2d, 0x43, 0x1e, 0x1b, 0x24, + 0x68, 0x25, 0x78, 0x43, 0xba, 0xab, 0x89, 0x26, 0xac, 0xb3, 0x1d, 0x28, 0x1b, 0x1c, 0xe6, 0x80, + 0x2a, 0x79, 0x65, 0xa1, 0x42, 0x6e, 0xac, 0xbd, 0x98, 0x31, 0x92, 0x3d, 0x81, 0x1b, 0xc9, 0x59, + 0xa9, 0xf3, 0xdd, 0xd4, 0xa2, 0xeb, 0x56, 0xa7, 0xce, 0x05, 0xb3, 0xc5, 0xe7, 0x73, 0xb3, 0xfb, + 0x9a, 0xa2, 0x6e, 0x4b, 0xe2, 0xa7, 0xce, 0x05, 0xf1, 0xc6, 0xbb, 0xd0, 0xb0, 0xa2, 0xe0, 0x48, + 0x6a, 0xb1, 0xa3, 0xc0, 0xdb, 0x7c, 0x46, 0xe3, 0x50, 0x47, 0xa8, 0xa1, 0x8e, 0x66, 0x7b, 0x70, + 0x39, 0x1d, 0x31, 0xcb, 0x3b, 0x0e, 0x42, 0x37, 0x3e, 0x19, 0x6f, 0xfe, 0x2a, 0x6b, 0x6d, 0xa2, + 0x09, 0x95, 0x4c, 0xfe, 0xa6, 0x22, 0x32, 0xd2, 0xb1, 0x4e, 0x60, 0xe2, 0x31, 0x5c, 0x4f, 0xcb, + 0x8b, 0x83, 0x89, 0xe9, 0x9b, 0x67, 0x6e, 0x7c, 0x62, 0x92, 0x5f, 0xf3, 0xef, 0xca, 0xc8, 0x1a, + 0x8a, 0x60, 0x18, 0x4c, 0x7a, 0xcf, 0xdd, 0xf8, 0x64, 0xe8, 0x3a, 0x91, 0xd8, 0x82, 0x5b, 0x96, + 0x6d, 0x4d, 0x62, 0x5c, 0x10, 0x98, 0x31, 0x89, 0x06, 0x12, 0xf8, 0xa6, 0x33, 0x9e, 0xc4, 0x17, + 0x9b, 0x7f, 0x8d, 0xb2, 0xdf, 0x50, 0x54, 0xc3, 0x60, 0xb2, 0x23, 0x69, 0xfa, 0x7e, 0x1b, 0x29, + 0xf4, 0x7f, 0xba, 0x0c, 0x15, 0x65, 0xac, 0x10, 0x35, 0x28, 0x1f, 0xf4, 0x9e, 0xf6, 0xfa, 0xcf, + 0x7b, 0x1c, 0x62, 0xaa, 0x39, 0x18, 0xb4, 0x8d, 0xa1, 0x66, 0x8b, 0x06, 0x00, 0x85, 0x53, 0x31, + 0x07, 0xad, 0x66, 0x8f, 0x43, 0x4e, 0x51, 0x10, 0x17, 0x4e, 0x2f, 0x89, 0x75, 0x58, 0xdd, 0x39, + 0xe8, 0xf1, 0x95, 0x23, 0x02, 0x15, 0x11, 0xd4, 0xfe, 0x9a, 0xfd, 0xfe, 0x18, 0x54, 0x42, 0xd0, + 0x5e, 0x73, 0xd8, 0x36, 0x3a, 0x0a, 0xb4, 0x2c, 0xae, 0xc0, 0x7a, 0x42, 0xa5, 0x4a, 0xd0, 0xca, + 0x08, 0x7e, 0xd6, 0x6e, 0x0d, 0xfb, 0x86, 0xd9, 0xe9, 0x6d, 0xb7, 0xbf, 0x66, 0xea, 0x0a, 0xd6, + 0x6f, 0xdf, 0xe8, 0x7f, 0xd5, 0x6e, 0x0d, 0x35, 0x20, 0x57, 0xc2, 0x27, 0x4f, 0xb4, 0x9a, 0xa8, + 0x43, 0x65, 0xbb, 0x33, 0x18, 0x76, 0x7a, 0xad, 0xa1, 0x56, 0xc7, 0x6a, 0xef, 0x74, 0xba, 0xc3, + 0xb6, 0xa1, 0xad, 0x8a, 0x0a, 0x94, 0xbe, 0xea, 0x77, 0x7a, 0x5a, 0x83, 0x82, 0xcb, 0x34, 0xf7, + 0xf6, 0xbb, 0x6d, 0x6d, 0x0d, 0xa1, 0x83, 0xbe, 0x31, 0xd4, 0x34, 0x84, 0x3e, 0xef, 0xf4, 0xb6, + 0xfb, 0xcf, 0xb5, 0x75, 0x51, 0x85, 0xe5, 0x83, 0x1e, 0x7e, 0x5d, 0x88, 0x55, 0xa8, 0xd2, 0x4f, + 0xb3, 0xd9, 0xed, 0x6a, 0x97, 0x33, 0xfe, 0x87, 0x1b, 0x88, 0x22, 0x6f, 0xc6, 0x01, 0xd6, 0xe1, + 0x0a, 0xb6, 0x28, 0x49, 0x12, 0xf5, 0x55, 0x2c, 0x67, 0xaf, 0xd3, 0x3b, 0x18, 0x68, 0xd7, 0x90, + 0x98, 0x7e, 0x12, 0x66, 0x13, 0xcb, 0xe9, 0xf4, 0xa8, 0x43, 0x6f, 0xe1, 0xef, 0xed, 0x76, 0xb7, + 0x3d, 0x6c, 0x6b, 0xb7, 0xb1, 0x55, 0xdd, 0x7e, 0xeb, 0xa9, 0xd9, 0xdf, 0xd7, 0xde, 0xc0, 0x9e, + 0xdd, 0x37, 0xda, 0xa6, 0x24, 0x7c, 0x53, 0x68, 0x50, 0xdf, 0x39, 0xf8, 0xd5, 0xaf, 0xbe, 0x31, + 0x65, 0xa3, 0xde, 0xc2, 0x6f, 0xa6, 0x14, 0xe6, 0xc1, 0x53, 0xed, 0xed, 0x19, 0xd0, 0xe0, 0xa9, + 0xf6, 0x0e, 0x76, 0x8a, 0xea, 0x6b, 0xed, 0x1e, 0x12, 0x18, 0xed, 0xd6, 0x81, 0x31, 0xe8, 0x3c, + 0x6b, 0x9b, 0xad, 0x61, 0x5b, 0x7b, 0x97, 0x7a, 0xa1, 0xd3, 0x7b, 0xaa, 0xdd, 0xc7, 0x6a, 0xe2, + 0x2f, 0xee, 0xe4, 0xf7, 0x84, 0x80, 0x46, 0x4a, 0x4b, 0xb0, 0xf7, 0x91, 0x64, 0xcb, 0xe8, 0x37, + 0xb7, 0x5b, 0xcd, 0xc1, 0x50, 0xfb, 0x00, 0xdb, 0x38, 0xd8, 0xef, 0x76, 0x86, 0xda, 0x03, 0x6c, + 0xc8, 0x93, 0xe6, 0x70, 0xb7, 0x6d, 0x68, 0x1f, 0x8a, 0x35, 0xa8, 0x0d, 0x3b, 0x7b, 0x6d, 0x53, + 0xf6, 0xe9, 0x43, 0xfc, 0xc6, 0x4e, 0xa7, 0xdb, 0xd5, 0x1e, 0x91, 0xff, 0x5c, 0xd3, 0x18, 0x76, + 0x68, 0x7c, 0x3f, 0xc1, 0x02, 0x9a, 0xfb, 0xfb, 0xdd, 0x6f, 0xb4, 0x4f, 0xb1, 0x81, 0x7b, 0x07, + 0xdd, 0x61, 0xc7, 0x3c, 0xd8, 0xdf, 0x6e, 0x0e, 0xdb, 0xda, 0x67, 0x34, 0xca, 0xfd, 0xc1, 0x70, + 0x7b, 0xaf, 0xab, 0x7d, 0x4e, 0x65, 0xd2, 0x4c, 0x6b, 0x75, 0xfb, 0xbd, 0xb6, 0xf6, 0x18, 0xe9, + 0x9b, 0xdb, 0xcd, 0xfd, 0x21, 0xd6, 0x6e, 0xd8, 0xdf, 0xd7, 0xbe, 0xd0, 0x4b, 0x95, 0x15, 0x6d, + 0x45, 0x2f, 0x55, 0xee, 0x68, 0x77, 0xf4, 0x52, 0x45, 0xd7, 0xf4, 0xfb, 0xb5, 0x41, 0xff, 0xc0, + 0x68, 0x71, 0xed, 0xf5, 0xbf, 0x51, 0x80, 0x8a, 0xb2, 0x6a, 0xe1, 0x87, 0x3b, 0xbd, 0x5e, 0xdb, + 0xd0, 0x2e, 0x61, 0xe5, 0xba, 0xed, 0x9d, 0xa1, 0x56, 0x20, 0xf7, 0xc3, 0xce, 0x93, 0xdd, 0xa1, + 0xb6, 0x84, 0x3f, 0xfb, 0x07, 0xd8, 0xcf, 0x45, 0xea, 0xa0, 0xf6, 0x5e, 0x47, 0x2b, 0xe1, 0xaf, + 0x66, 0x6f, 0xd8, 0xd1, 0x96, 0x69, 0x1a, 0x75, 0x7a, 0x4f, 0xba, 0x6d, 0x6d, 0x05, 0xa1, 0x7b, + 0x4d, 0xe3, 0xa9, 0x56, 0xe6, 0x42, 0xb7, 0xdb, 0x5f, 0x6b, 0x15, 0xb1, 0x02, 0x4b, 0xdd, 0x87, + 0x5a, 0x15, 0x41, 0xdb, 0xed, 0xed, 0x83, 0x7d, 0x0d, 0x28, 0xf7, 0xa0, 0xbf, 0xa3, 0xd5, 0xb0, + 0x13, 0xf0, 0x97, 0x49, 0x9f, 0xad, 0xeb, 0xf7, 0xa0, 0xdc, 0x3c, 0x3e, 0xa6, 0xbd, 0x0d, 0x3b, + 0xea, 0xa0, 0xdb, 0xe5, 0x55, 0xb7, 0xd5, 0x1f, 0x0e, 0xfb, 0x7b, 0x5a, 0x01, 0x67, 0x38, 0xb6, + 0x70, 0x49, 0xef, 0x40, 0x45, 0x09, 0xf6, 0x99, 0xc0, 0x4a, 0x15, 0x28, 0xed, 0x1b, 0xed, 0x67, + 0xec, 0x4c, 0xdb, 0x6b, 0x7f, 0x8d, 0xf5, 0xc7, 0x5f, 0x58, 0x50, 0x11, 0x6b, 0xc0, 0x11, 0x90, + 0x28, 0xb2, 0x52, 0xb7, 0xd3, 0x6b, 0x37, 0x0d, 0x6d, 0x59, 0x7f, 0x0c, 0xb5, 0xcc, 0x0e, 0x8b, + 0x13, 0xe3, 0x49, 0x73, 0x1f, 0x27, 0x57, 0xd7, 0x94, 0xc5, 0x5e, 0x05, 0x91, 0x80, 0xd2, 0x31, + 0x2b, 0xe8, 0x9f, 0xc2, 0xfa, 0x9c, 0x40, 0x45, 0x15, 0x6f, 0x76, 0x64, 0xc5, 0x3b, 0x4f, 0x7a, + 0x7d, 0xa3, 0xcd, 0x41, 0x9e, 0xe4, 0x68, 0x2e, 0xe9, 0xef, 0x41, 0x35, 0x91, 0xfc, 0x70, 0x76, + 0xb7, 0x8c, 0xfe, 0x60, 0xc0, 0x83, 0x7f, 0x09, 0xd3, 0xd4, 0xdd, 0x9c, 0x2e, 0xe8, 0x2e, 0x34, + 0xf2, 0xdb, 0xb0, 0xb8, 0x03, 0x37, 0x79, 0xfe, 0x6f, 0x1d, 0x74, 0xba, 0xdb, 0xe6, 0xa0, 0xb3, + 0xdd, 0x9e, 0xb9, 0x3d, 0x7e, 0x03, 0xae, 0xce, 0x51, 0x70, 0x84, 0xa9, 0x82, 0xb8, 0x0e, 0x57, + 0xe6, 0x70, 0x34, 0xeb, 0x97, 0xf4, 0x7d, 0x10, 0xf3, 0x0c, 0x5a, 0xdc, 0x84, 0xcd, 0xa4, 0xcd, + 0x66, 0xb3, 0xfb, 0xa4, 0x6f, 0x74, 0x86, 0xbb, 0x7b, 0x26, 0xf1, 0x8b, 0x4b, 0x2f, 0xc3, 0xee, + 0x36, 0x07, 0xbb, 0x5a, 0xe1, 0xab, 0x52, 0xe5, 0xb6, 0x76, 0xe7, 0xab, 0x52, 0xe5, 0xae, 0xf6, + 0x96, 0x3e, 0x80, 0x75, 0x25, 0x3d, 0xd3, 0xdd, 0x7c, 0x32, 0xeb, 0x6c, 0xc0, 0x72, 0xd7, 0x79, + 0xe1, 0x78, 0xea, 0x2a, 0x37, 0x25, 0x10, 0xda, 0x3f, 0xfc, 0xb6, 0x93, 0x84, 0x51, 0xa4, 0x04, + 0xaa, 0xcb, 0xbd, 0x4c, 0x24, 0x47, 0x0a, 0xa7, 0xf0, 0xaf, 0x16, 0xa0, 0x92, 0xc8, 0xe4, 0x77, + 0x61, 0x69, 0x38, 0x90, 0x07, 0x42, 0x1b, 0x0f, 0xd2, 0xb0, 0xbd, 0x43, 0xf5, 0xcb, 0x58, 0x1a, + 0x0e, 0xc4, 0xfb, 0xb0, 0xc2, 0xc1, 0xe4, 0xe4, 0x11, 0xd0, 0x46, 0x5e, 0xce, 0x1f, 0x12, 0xce, + 0x90, 0x34, 0xe2, 0x53, 0xa8, 0x26, 0xb5, 0x95, 0xd6, 0xe8, 0x6b, 0xf9, 0x0c, 0x09, 0xda, 0x48, + 0x29, 0xf5, 0x2e, 0x34, 0xf2, 0x05, 0x8a, 0x5b, 0x00, 0x5c, 0x64, 0xe6, 0xec, 0x36, 0x03, 0x11, + 0x37, 0x40, 0xc5, 0xb8, 0xdb, 0xa6, 0x8a, 0xad, 0x26, 0x31, 0xef, 0xb6, 0xf5, 0x7f, 0x6b, 0x19, + 0x20, 0xd5, 0xea, 0xb1, 0x23, 0x12, 0x1b, 0xfb, 0xb2, 0xf4, 0xf2, 0x7d, 0x0d, 0xaa, 0x5e, 0x60, + 0xd9, 0xd9, 0xf0, 0xbb, 0x15, 0x04, 0xd0, 0x1c, 0xcb, 0xc6, 0x9b, 0xa9, 0xca, 0xd0, 0x31, 0x57, + 0x61, 0xe5, 0x28, 0x08, 0xc7, 0x56, 0x2c, 0xef, 0xed, 0xcb, 0x14, 0x85, 0x94, 0x21, 0x27, 0x35, + 0xdb, 0xf4, 0x5c, 0xdf, 0xe1, 0xc0, 0x59, 0x25, 0xa3, 0x2e, 0x81, 0x5d, 0x84, 0xa1, 0x70, 0xed, + 0xf8, 0x23, 0x2f, 0x40, 0xb1, 0xe6, 0x90, 0x6f, 0x2d, 0xd4, 0x0d, 0x50, 0xa0, 0xad, 0x0b, 0x6e, + 0x6d, 0x38, 0x76, 0x7d, 0x2b, 0x96, 0xde, 0x3b, 0xd4, 0x5a, 0x05, 0xc1, 0xea, 0x7e, 0x1b, 0x05, + 0xd2, 0xe4, 0xce, 0x01, 0x4d, 0x2a, 0x08, 0xa0, 0xea, 0xbe, 0x0e, 0xe0, 0x44, 0x23, 0x6b, 0xc2, + 0x85, 0x57, 0x39, 0xfe, 0x9b, 0x84, 0x6c, 0x5d, 0x88, 0x2e, 0x34, 0x86, 0x87, 0xa8, 0x4c, 0x04, + 0xdb, 0x56, 0x6c, 0xb5, 0x02, 0x4f, 0x1a, 0xc3, 0xef, 0xce, 0x9a, 0x3f, 0x1e, 0xe4, 0xc9, 0xf8, + 0xe6, 0xdc, 0x4c, 0x5e, 0xf1, 0x09, 0xd4, 0xdd, 0x91, 0x73, 0xe8, 0x84, 0xc7, 0x2c, 0x68, 0xd5, + 0x72, 0x17, 0xee, 0x19, 0x43, 0x42, 0x56, 0xcd, 0x4d, 0x13, 0xa8, 0xa4, 0x8d, 0x03, 0xff, 0x38, + 0xb0, 0x0f, 0x39, 0x57, 0x3d, 0x6b, 0xd8, 0xdb, 0x43, 0x0c, 0xe7, 0x91, 0x44, 0x94, 0xe7, 0xb7, + 0x61, 0x0d, 0x7b, 0x3e, 0x8a, 0x43, 0xc7, 0x1a, 0x73, 0xb6, 0xd5, 0xec, 0x0c, 0xc4, 0x1a, 0x0d, + 0x08, 0x49, 0x79, 0x1b, 0x29, 0x31, 0x65, 0xff, 0x24, 0xbd, 0xce, 0x4e, 0x79, 0x1b, 0x39, 0xaf, + 0x7a, 0xc6, 0xf0, 0x47, 0x8f, 0xd2, 0x84, 0x78, 0x00, 0x70, 0x6a, 0x1d, 0x9d, 0x5a, 0x9c, 0x67, + 0x2d, 0x5b, 0xcd, 0xa7, 0x08, 0xa7, 0x1c, 0xd5, 0x53, 0xf5, 0xf3, 0x46, 0x13, 0x2e, 0x2f, 0xe8, + 0xb5, 0x9f, 0x74, 0x0d, 0xec, 0xef, 0x14, 0xa0, 0x91, 0x6f, 0xcb, 0x4c, 0x2c, 0xcd, 0x6a, 0x12, + 0x4b, 0x53, 0x40, 0x69, 0x12, 0x84, 0xea, 0xc6, 0x06, 0xfd, 0x4e, 0x43, 0x5b, 0x17, 0xb3, 0xa1, + 0xad, 0x37, 0xa1, 0x1c, 0x3a, 0x7c, 0xfb, 0x9a, 0x23, 0x35, 0xa8, 0x24, 0x4e, 0xd8, 0xc9, 0x34, + 0x3a, 0x71, 0x6c, 0xa5, 0x4d, 0xf2, 0x35, 0x82, 0x3a, 0x03, 0xa5, 0x36, 0x79, 0x0d, 0xca, 0xd6, + 0xc4, 0x25, 0x05, 0x45, 0xc6, 0x65, 0xb4, 0x26, 0xee, 0x53, 0xe7, 0x42, 0xff, 0x1e, 0x6a, 0x99, + 0xbe, 0xc3, 0x0a, 0x9d, 0xba, 0xbe, 0x8a, 0x2c, 0x41, 0xbf, 0xb1, 0xf2, 0x32, 0x14, 0x21, 0xdf, + 0x05, 0x91, 0x29, 0xfc, 0xb0, 0xba, 0xc6, 0x47, 0xa7, 0xb3, 0xea, 0xa8, 0x5f, 0x02, 0x7f, 0x89, + 0x30, 0x5c, 0xd6, 0x58, 0x11, 0x3b, 0x38, 0xf3, 0x65, 0xc5, 0x93, 0xb4, 0xfe, 0x9f, 0x2e, 0x41, + 0x35, 0x19, 0x04, 0x6c, 0xe1, 0x61, 0x18, 0x9c, 0x3a, 0x61, 0x24, 0xbf, 0xae, 0x92, 0xd4, 0x23, + 0xc1, 0xc4, 0x1d, 0xa9, 0x4b, 0xd1, 0x94, 0x10, 0x37, 0xb3, 0x57, 0x20, 0x39, 0x5a, 0x49, 0xe6, + 0xc6, 0xe3, 0x2d, 0x00, 0x6b, 0x1a, 0x07, 0xa3, 0x60, 0x3c, 0x76, 0x63, 0xf9, 0xe5, 0x0c, 0x04, + 0xcb, 0x24, 0x15, 0x4d, 0xf6, 0x16, 0x27, 0x32, 0x4c, 0x61, 0x25, 0xc7, 0x14, 0x6e, 0x42, 0x35, + 0x72, 0x26, 0x56, 0x68, 0xc5, 0x41, 0x28, 0x57, 0x73, 0x0a, 0xa0, 0x70, 0x98, 0xa8, 0x59, 0xa9, + 0xb8, 0x75, 0x45, 0xa3, 0x4c, 0xe9, 0x8e, 0x2d, 0xee, 0xd0, 0x55, 0x24, 0x33, 0x41, 0xb3, 0x1f, + 0x36, 0x9c, 0x58, 0xd1, 0x40, 0x52, 0x08, 0x28, 0x91, 0x4f, 0x0c, 0x7b, 0xe1, 0xd1, 0x6f, 0xf1, + 0x0e, 0x50, 0xd0, 0xf5, 0x60, 0x1a, 0x9b, 0x91, 0x33, 0x0a, 0x7c, 0x3b, 0x92, 0x7e, 0x78, 0x0d, + 0x09, 0x1e, 0x30, 0x54, 0xff, 0xaf, 0x97, 0xa0, 0x96, 0x59, 0xa3, 0xe4, 0x82, 0x64, 0x4d, 0xe8, + 0xe4, 0x2b, 0xb9, 0xbf, 0x5d, 0x95, 0x10, 0x0e, 0x11, 0x32, 0xb2, 0x62, 0xcb, 0x0b, 0x8e, 0x33, + 0xc1, 0x4f, 0x24, 0xa4, 0x63, 0x93, 0x57, 0xa0, 0x35, 0x76, 0x38, 0x08, 0xba, 0xbc, 0xbc, 0x9f, + 0x00, 0xd2, 0x79, 0x59, 0xca, 0xce, 0x4b, 0x8d, 0xdd, 0x47, 0xb9, 0x17, 0xc9, 0x5b, 0xf4, 0x36, + 0xd4, 0x94, 0x25, 0x09, 0xbf, 0x22, 0x3d, 0x1c, 0x15, 0xa8, 0x63, 0x8b, 0xb7, 0x21, 0x0d, 0x29, + 0x6f, 0x5a, 0x91, 0x19, 0x1c, 0xa9, 0x7b, 0x73, 0x09, 0xb8, 0x19, 0xf5, 0x8f, 0x12, 0xb7, 0x23, + 0xb2, 0x0a, 0x48, 0x1e, 0x89, 0x00, 0x92, 0x52, 0x1e, 0xc0, 0x65, 0x79, 0x84, 0x49, 0x13, 0xdf, + 0xf1, 0x6c, 0xd3, 0xb5, 0xd9, 0x7f, 0x7a, 0xd9, 0x58, 0x4f, 0x50, 0x3b, 0x88, 0xe9, 0xd8, 0x64, + 0x73, 0x91, 0x0a, 0xb8, 0xed, 0x1e, 0x3b, 0x51, 0x2c, 0x6f, 0x79, 0xd5, 0x19, 0xb8, 0x4d, 0x30, + 0xfd, 0x9f, 0xad, 0x40, 0x35, 0x61, 0x5e, 0xaf, 0xea, 0xcc, 0x37, 0x61, 0x75, 0x14, 0xf8, 0x3e, + 0x7b, 0x0a, 0xa4, 0xfd, 0x59, 0x4f, 0x81, 0x74, 0x4f, 0x57, 0x64, 0x88, 0xd4, 0x2d, 0x74, 0x8e, + 0xd5, 0xb1, 0x9e, 0x62, 0x9e, 0xc9, 0xfb, 0xe8, 0x37, 0xa0, 0x82, 0x5c, 0xef, 0xd0, 0x8a, 0x54, + 0x37, 0x27, 0x69, 0x9c, 0xd1, 0xa3, 0xc0, 0xf3, 0xa4, 0x27, 0x83, 0x0c, 0x9e, 0x9b, 0x42, 0xc4, + 0x43, 0x0a, 0x9f, 0x4c, 0x8e, 0x00, 0x2b, 0xb4, 0x1f, 0x6c, 0x66, 0xb8, 0x31, 0xfb, 0x02, 0xec, + 0x71, 0xe5, 0x0d, 0x45, 0x28, 0x7e, 0x0e, 0x9a, 0xe4, 0x1d, 0x93, 0xd0, 0xb1, 0x49, 0x8a, 0x93, + 0x97, 0x14, 0x36, 0x32, 0x99, 0xf7, 0x15, 0xce, 0x58, 0x63, 0xea, 0x04, 0x80, 0x33, 0x35, 0x1d, + 0x86, 0x89, 0x15, 0x9f, 0xb0, 0xeb, 0x7b, 0xd5, 0x68, 0x24, 0xe0, 0x7d, 0x84, 0x8a, 0x4f, 0xe0, + 0x6a, 0xe8, 0x44, 0xae, 0x3d, 0xb5, 0x3c, 0x33, 0x3f, 0x10, 0x1c, 0x8a, 0x77, 0x43, 0x61, 0x77, + 0x32, 0x03, 0x82, 0xc5, 0x8f, 0xad, 0x73, 0x3a, 0x04, 0xf2, 0x3c, 0xc7, 0x73, 0xa3, 0xb1, 0x8c, + 0xc8, 0xd8, 0x18, 0x5b, 0xe7, 0xfb, 0x29, 0x94, 0x6e, 0x24, 0x4d, 0x3c, 0x77, 0x26, 0xca, 0x1f, + 0x8f, 0x25, 0xc2, 0x0d, 0x46, 0x0b, 0x1d, 0x56, 0xb1, 0x40, 0xb2, 0x77, 0x26, 0xce, 0xec, 0x45, + 0xa3, 0x36, 0xb6, 0xd8, 0x92, 0x10, 0x9c, 0xd1, 0xb5, 0xfc, 0x84, 0x86, 0xfd, 0x67, 0xa5, 0x0b, + 0xab, 0x24, 0x62, 0x17, 0x5a, 0xaa, 0x1a, 0xcf, 0x0e, 0x35, 0xac, 0x0d, 0x76, 0x1f, 0x92, 0x60, + 0x35, 0xa6, 0xd7, 0x81, 0x8f, 0x52, 0x71, 0x8a, 0xac, 0x11, 0x45, 0x99, 0xd2, 0xbc, 0x12, 0xa6, + 0x91, 0x13, 0x32, 0xfb, 0x34, 0x89, 0xf1, 0x72, 0xa4, 0xbf, 0x55, 0x04, 0x13, 0x03, 0x7d, 0x8a, + 0x1c, 0xf8, 0x1e, 0x68, 0x44, 0xa7, 0x4e, 0x5e, 0xa2, 0xc0, 0xa7, 0x53, 0xdb, 0xba, 0xd1, 0x40, + 0xb8, 0x3c, 0x57, 0x89, 0x02, 0x5f, 0x7c, 0x0e, 0x9b, 0x44, 0x39, 0x71, 0x27, 0x0e, 0x8a, 0x2f, + 0xc8, 0x79, 0x8e, 0x1d, 0xce, 0x21, 0xee, 0x14, 0xef, 0xd5, 0x8d, 0x2b, 0x88, 0xdf, 0x97, 0xe8, + 0x01, 0x62, 0x29, 0xe3, 0x7d, 0x58, 0xcf, 0x54, 0x45, 0x0e, 0x0d, 0xc7, 0x0d, 0x5b, 0x4b, 0x2a, + 0x23, 0x47, 0xe5, 0x23, 0xd8, 0x90, 0x3e, 0x26, 0x92, 0x9c, 0xa7, 0x13, 0x1d, 0xdc, 0x56, 0x0c, + 0x21, 0x71, 0x94, 0x83, 0xe7, 0x9d, 0x78, 0x03, 0xea, 0x64, 0x7d, 0x90, 0x11, 0x36, 0x65, 0xec, + 0xb0, 0x1a, 0xc1, 0x38, 0x80, 0x9d, 0xfe, 0xd7, 0x0b, 0x20, 0xe6, 0xa7, 0xea, 0xcb, 0xa2, 0xe1, + 0xe0, 0x54, 0x53, 0xa1, 0x28, 0xf0, 0xb7, 0x78, 0x17, 0xca, 0xe3, 0x80, 0xc5, 0xa9, 0xe2, 0x4b, + 0x62, 0xd1, 0xad, 0x8c, 0x03, 0x12, 0xaf, 0xde, 0x81, 0xb5, 0x51, 0xe0, 0xcb, 0x41, 0x63, 0xee, + 0xc2, 0x6b, 0xad, 0x91, 0x82, 0x91, 0xc7, 0xe8, 0xff, 0x49, 0x01, 0x1a, 0xf9, 0x05, 0x20, 0xde, + 0xa6, 0x28, 0xde, 0xb9, 0x38, 0xe0, 0x79, 0x8a, 0xfe, 0x84, 0xa2, 0x7b, 0x2f, 0xaa, 0xe2, 0xeb, + 0x00, 0x24, 0x24, 0xf0, 0x68, 0xc8, 0xb0, 0xbe, 0x04, 0xa1, 0x11, 0xb8, 0x0d, 0x35, 0x7e, 0xa6, + 0x82, 0xf1, 0x25, 0x1a, 0x2d, 0xce, 0x11, 0x11, 0xc1, 0x47, 0x19, 0x8f, 0x0f, 0xf6, 0xe8, 0x58, + 0xbc, 0x48, 0x13, 0x2a, 0xfd, 0x8f, 0x0b, 0x00, 0xe9, 0x1a, 0xc0, 0x99, 0x78, 0xea, 0x5c, 0xd0, + 0x32, 0x55, 0x5b, 0xec, 0xa9, 0x73, 0xb1, 0x2f, 0xeb, 0x26, 0xdd, 0x0e, 0x23, 0x79, 0xe7, 0xb3, + 0x6e, 0x54, 0xd9, 0xd3, 0x30, 0xe2, 0x20, 0x4b, 0xd2, 0xd1, 0x30, 0x53, 0x75, 0xf6, 0x2d, 0x8c, + 0x28, 0x8c, 0xc6, 0x1a, 0xe7, 0xa6, 0xa1, 0x8f, 0xdc, 0x17, 0x2a, 0x9c, 0x14, 0x3b, 0x46, 0x76, + 0x14, 0x14, 0x09, 0xb9, 0x9c, 0x94, 0x90, 0x2f, 0x61, 0xb1, 0x1b, 0x64, 0x42, 0xa8, 0x7b, 0x4a, + 0xe0, 0x6f, 0xc6, 0x31, 0x6d, 0xb0, 0x89, 0x29, 0xbe, 0xa0, 0x62, 0x48, 0xb1, 0x15, 0xfe, 0x35, + 0xba, 0xab, 0x2a, 0xc3, 0x47, 0x48, 0xb9, 0x3f, 0x09, 0x0b, 0xf1, 0x36, 0x8e, 0xb4, 0xa7, 0xb6, + 0x87, 0xc4, 0x8f, 0x6d, 0xd9, 0x58, 0x1d, 0x05, 0x1e, 0x6f, 0x0d, 0x08, 0xd4, 0xff, 0xb5, 0x65, + 0x80, 0xf4, 0x10, 0x36, 0xb7, 0x62, 0x0b, 0xf9, 0x15, 0xfb, 0x10, 0xae, 0xca, 0xf8, 0x7e, 0xc9, + 0x85, 0x2c, 0xd7, 0x37, 0x0f, 0x2d, 0x25, 0xc2, 0x09, 0x89, 0xe5, 0x6b, 0x34, 0x1d, 0x7f, 0xcb, + 0x8a, 0xc5, 0x63, 0xe4, 0x91, 0x69, 0x9e, 0xf8, 0x62, 0xf2, 0xd2, 0x29, 0xba, 0x9a, 0x66, 0x1f, + 0x5e, 0x4c, 0xc4, 0x47, 0x14, 0x26, 0x29, 0x74, 0xa2, 0x13, 0x33, 0x8e, 0xb2, 0x1f, 0xe3, 0x5b, + 0xf1, 0xeb, 0x12, 0x39, 0x8c, 0x92, 0x6f, 0x7d, 0x04, 0x57, 0x24, 0x93, 0x98, 0xa9, 0x1e, 0xbb, + 0x4b, 0x4a, 0x23, 0x74, 0xb6, 0x76, 0x34, 0xf2, 0x74, 0x32, 0xad, 0x9e, 0x53, 0xa9, 0xe0, 0xc8, + 0x63, 0x67, 0xc8, 0xd0, 0xd9, 0x74, 0xbc, 0x2c, 0x25, 0x17, 0x4e, 0x08, 0x1d, 0x4a, 0xb8, 0x42, + 0x88, 0x19, 0x37, 0x1e, 0x36, 0x1e, 0xd0, 0x73, 0x31, 0x14, 0xf6, 0x3c, 0xb0, 0x1d, 0x83, 0x70, + 0xe2, 0x03, 0xdc, 0xa1, 0xd3, 0x66, 0xab, 0x98, 0xff, 0x35, 0x69, 0xc7, 0x4e, 0x1a, 0x6a, 0x70, + 0xf4, 0xff, 0xf7, 0x40, 0x64, 0x6a, 0xae, 0xa8, 0xeb, 0x44, 0xbd, 0x96, 0x54, 0x5b, 0x12, 0xbf, + 0x03, 0x54, 0xc5, 0xf4, 0x6a, 0xd1, 0xcc, 0x59, 0x2c, 0x22, 0xe5, 0x75, 0xa4, 0x2b, 0x69, 0xeb, + 0x4c, 0x2b, 0x36, 0xe3, 0x13, 0x87, 0x8c, 0xde, 0x7c, 0xb7, 0x68, 0x3d, 0x69, 0x68, 0x33, 0x1e, + 0x9e, 0x38, 0x6d, 0xe2, 0xb5, 0x89, 0xb7, 0xcb, 0xda, 0x0f, 0x7b, 0xbb, 0x7c, 0x06, 0x9b, 0xf9, + 0xf3, 0x8e, 0x4c, 0x77, 0x33, 0x1b, 0xdf, 0xc8, 0xde, 0xd9, 0x4a, 0x7a, 0xfc, 0x3e, 0xac, 0xa3, + 0x4c, 0x98, 0xcb, 0x2b, 0x1d, 0xd3, 0xd6, 0x4e, 0xac, 0x28, 0x7b, 0x40, 0xa2, 0xff, 0x83, 0x15, + 0x68, 0xe4, 0x8f, 0xa5, 0x39, 0x9e, 0x1a, 0xef, 0xf3, 0x05, 0x92, 0x76, 0x92, 0xdd, 0xfc, 0x35, + 0xa8, 0xf2, 0xc5, 0x2e, 0x64, 0xc6, 0x2a, 0xf4, 0xf4, 0xa9, 0x64, 0xc1, 0xef, 0x42, 0x79, 0x72, + 0xfa, 0x0a, 0x06, 0x39, 0xe1, 0xb0, 0x27, 0xef, 0x42, 0x79, 0x7a, 0x9a, 0x06, 0x16, 0x5f, 0x48, + 0x3a, 0x3d, 0x95, 0x9e, 0xce, 0xd7, 0xe5, 0x81, 0xba, 0xbc, 0xf5, 0x35, 0x9e, 0x7a, 0xb1, 0x6b, + 0xd2, 0x91, 0x80, 0x5c, 0xda, 0x57, 0x99, 0xa0, 0x43, 0x78, 0x8a, 0x9c, 0x40, 0x87, 0x98, 0xc8, + 0xef, 0x64, 0xb0, 0xa5, 0x44, 0x66, 0x59, 0x36, 0x80, 0x83, 0x2b, 0x51, 0x73, 0xde, 0x05, 0x0d, + 0x35, 0x0d, 0xcf, 0x1d, 0xc5, 0x09, 0x55, 0x99, 0xa8, 0xd6, 0x14, 0x5c, 0x91, 0xbe, 0x05, 0x0d, + 0x79, 0x37, 0x47, 0x11, 0x72, 0x38, 0x80, 0x55, 0x86, 0x2a, 0xb2, 0xcf, 0xe0, 0x5a, 0x60, 0x9f, + 0x4e, 0xd5, 0x35, 0x33, 0x2b, 0x3c, 0x74, 0xe3, 0xd0, 0x4a, 0xe2, 0xcc, 0x55, 0x8c, 0x2b, 0x88, + 0x66, 0x2e, 0xd0, 0x4c, 0x91, 0xe4, 0xab, 0xc8, 0x59, 0x54, 0xf1, 0x40, 0xf5, 0x58, 0x65, 0xa8, + 0x2a, 0xfe, 0x13, 0xb8, 0x9a, 0xc4, 0x3f, 0x71, 0xc6, 0x8e, 0x1f, 0x9b, 0xa1, 0x43, 0xae, 0x5e, + 0xf2, 0x6d, 0x80, 0x0d, 0x15, 0x06, 0x85, 0x90, 0x06, 0xe3, 0xc4, 0x43, 0xb8, 0x32, 0x93, 0x4b, + 0x8e, 0x20, 0xcf, 0xfd, 0xcb, 0xb9, 0x4c, 0x72, 0x30, 0xdb, 0x70, 0x7b, 0x26, 0x4f, 0x2e, 0x9a, + 0x27, 0xe6, 0x5e, 0xa5, 0xdc, 0x37, 0x73, 0xb9, 0xb3, 0x51, 0x2b, 0xb1, 0x98, 0xcf, 0x61, 0x73, + 0xa6, 0x18, 0x1c, 0x11, 0x66, 0x94, 0x1c, 0xb5, 0xf4, 0x4a, 0x2e, 0xff, 0x53, 0xe7, 0x82, 0x19, + 0xeb, 0xcf, 0xe1, 0xe6, 0x4c, 0xc6, 0x5c, 0xf7, 0xcb, 0x6b, 0x78, 0xd7, 0x73, 0x99, 0xfb, 0x99, + 0xa1, 0xc0, 0xa9, 0x8a, 0x9f, 0xe2, 0xe0, 0x88, 0x1a, 0x49, 0x8c, 0xb8, 0x37, 0xf5, 0x28, 0x3e, + 0xe2, 0x3b, 0x8c, 0xe4, 0x33, 0xd5, 0xf5, 0xd9, 0x33, 0x55, 0x22, 0xe4, 0x83, 0xd4, 0xb7, 0x61, + 0x4d, 0x11, 0xf2, 0xc9, 0x52, 0x44, 0x42, 0xce, 0xb2, 0xb1, 0x2a, 0x49, 0xe8, 0x40, 0x29, 0xd2, + 0xff, 0x70, 0x19, 0xea, 0x59, 0x9f, 0x9f, 0x1f, 0x73, 0xe9, 0xed, 0x27, 0x5d, 0x19, 0x95, 0x6a, + 0x1e, 0xf5, 0x09, 0xae, 0xe6, 0x62, 0xa2, 0xe6, 0x35, 0xa7, 0x31, 0x4a, 0x34, 0xa4, 0x7a, 0x05, + 0x9e, 0x8a, 0x10, 0xc4, 0xfc, 0x1b, 0x77, 0x2e, 0x19, 0x1c, 0xe8, 0x23, 0x19, 0x80, 0x8c, 0x42, + 0x42, 0x52, 0x70, 0x86, 0x79, 0x1f, 0xe8, 0xba, 0x8a, 0x08, 0x49, 0xc1, 0x28, 0x1e, 0xc2, 0x5a, + 0x1a, 0xee, 0x89, 0xb3, 0xac, 0xcc, 0x65, 0x59, 0x4d, 0x62, 0x3d, 0xc9, 0x20, 0xe3, 0xab, 0x6e, + 0x64, 0x06, 0x9e, 0xad, 0xc2, 0xf6, 0x95, 0x95, 0x47, 0x66, 0xdf, 0xb3, 0x65, 0x68, 0x3e, 0xa6, + 0xf1, 0x9d, 0x33, 0x45, 0x93, 0x78, 0x6d, 0xf6, 0x9c, 0x33, 0x49, 0xf3, 0x00, 0x2e, 0x63, 0x73, + 0xe5, 0x9a, 0xe0, 0x60, 0x1d, 0x41, 0x28, 0xd7, 0x10, 0x32, 0x37, 0x5e, 0x42, 0x03, 0x89, 0xc8, + 0x5c, 0xd7, 0x0c, 0x83, 0x33, 0xd3, 0x9f, 0x8e, 0x0f, 0x99, 0xcd, 0x4b, 0x61, 0x5e, 0x5e, 0xd7, + 0x34, 0x82, 0xb3, 0x1e, 0xa1, 0xb0, 0xbf, 0xee, 0xc3, 0xba, 0x5a, 0xa5, 0x23, 0x3a, 0xcd, 0x52, + 0x37, 0x8e, 0x97, 0x8d, 0x35, 0x46, 0x34, 0x09, 0x8e, 0xb4, 0xef, 0x26, 0xb4, 0x7c, 0x71, 0x94, + 0x68, 0x79, 0xf5, 0x34, 0xe2, 0xf4, 0xd6, 0x28, 0x92, 0xb6, 0xe0, 0x56, 0x1c, 0x5a, 0xa3, 0x53, + 0xf3, 0x65, 0xcb, 0x47, 0x5e, 0x54, 0x7d, 0x8d, 0xa8, 0x9a, 0x0b, 0x17, 0xcf, 0x8f, 0x59, 0x7d, + 0x8d, 0x57, 0xaf, 0x3e, 0xfd, 0x0f, 0x4a, 0xb0, 0x3e, 0xe7, 0x2c, 0x85, 0x0a, 0x75, 0xfa, 0xb6, + 0x18, 0xfe, 0x44, 0xe1, 0x79, 0x6c, 0xc5, 0xa3, 0x13, 0xd4, 0xd1, 0x8e, 0xdc, 0x73, 0xf5, 0xae, + 0x1a, 0xc1, 0xf6, 0x09, 0x84, 0xbc, 0x94, 0xe5, 0x2a, 0x76, 0x27, 0x65, 0x21, 0x87, 0x45, 0x36, + 0xf6, 0xcd, 0x56, 0x21, 0x3f, 0xe6, 0xaf, 0x88, 0x70, 0xc8, 0x8f, 0x1b, 0x50, 0xf5, 0x03, 0xba, + 0x46, 0x3c, 0x39, 0x95, 0x7c, 0xbb, 0xec, 0x07, 0x71, 0xdf, 0xdf, 0x3f, 0x45, 0x06, 0x35, 0x8d, + 0xe8, 0x22, 0xd8, 0xa1, 0x13, 0x46, 0x27, 0x6e, 0xe2, 0xb6, 0xc0, 0xc2, 0xc2, 0xe5, 0x69, 0xe4, + 0xec, 0x25, 0x38, 0x69, 0x6f, 0x6a, 0x42, 0x9d, 0x66, 0xb2, 0x3f, 0x0a, 0xc8, 0x11, 0x8a, 0x1d, + 0xef, 0x6f, 0x2d, 0xf0, 0x10, 0x7b, 0xea, 0x5c, 0xb4, 0x25, 0x95, 0x81, 0x1b, 0x82, 0x4a, 0x88, + 0x77, 0x29, 0x9a, 0xd3, 0xa1, 0x93, 0xda, 0x48, 0xf3, 0x6c, 0xa0, 0x4a, 0x58, 0xda, 0x85, 0x9e, + 0xc2, 0x75, 0xde, 0x4a, 0xc6, 0x93, 0xc0, 0xc7, 0xf1, 0x48, 0x33, 0x46, 0x79, 0x9f, 0xe2, 0xcc, + 0x16, 0x76, 0x95, 0xb6, 0x1a, 0x99, 0x63, 0x5f, 0x95, 0x85, 0xdb, 0x0e, 0xb0, 0x53, 0x00, 0x75, + 0x18, 0xcc, 0x7b, 0x66, 0x11, 0x96, 0xd6, 0xd3, 0xbb, 0xa0, 0x45, 0x23, 0xcb, 0xb3, 0xc2, 0x8c, + 0xfa, 0xcc, 0xac, 0x7e, 0x8d, 0xe1, 0xa9, 0xe2, 0xf0, 0x3a, 0xc0, 0x78, 0x1a, 0xc5, 0x26, 0xf9, + 0xb9, 0xa8, 0x30, 0xd2, 0x08, 0xa1, 0x93, 0x10, 0xfd, 0x26, 0xac, 0x74, 0x12, 0xa7, 0xb8, 0x24, + 0x26, 0x43, 0x51, 0x3e, 0x5f, 0x14, 0x40, 0x95, 0xbd, 0x25, 0xf6, 0xac, 0x89, 0xb8, 0x0f, 0xc5, + 0xb1, 0x35, 0x91, 0xb7, 0x75, 0x36, 0x93, 0x2b, 0x4d, 0x8c, 0x7d, 0xb0, 0x67, 0x4d, 0xd8, 0xce, + 0x8b, 0x44, 0x37, 0x3e, 0x83, 0x8a, 0x02, 0xfc, 0xb4, 0x48, 0x56, 0x4b, 0x50, 0xdd, 0xce, 0x3a, + 0xb5, 0xa2, 0x2e, 0x1c, 0x87, 0x53, 0x9f, 0x9a, 0x28, 0x1f, 0x6a, 0x19, 0x59, 0xfe, 0x50, 0x82, + 0x14, 0x43, 0x7d, 0xc9, 0xad, 0x17, 0x62, 0xa8, 0x37, 0x01, 0xe4, 0xf2, 0x74, 0x6d, 0x25, 0x87, + 0x57, 0x42, 0x5c, 0x98, 0x1d, 0xfb, 0x7c, 0xf1, 0x1d, 0xe3, 0xd2, 0x8f, 0xbf, 0x63, 0xbc, 0xbc, + 0xf0, 0x8e, 0xf1, 0xdb, 0xa9, 0xe8, 0x4d, 0x3b, 0x9b, 0x7d, 0x2e, 0x1f, 0x85, 0x50, 0x82, 0x36, + 0xee, 0x68, 0xf6, 0xb9, 0xf8, 0x12, 0x1a, 0xaa, 0x75, 0xb2, 0x3c, 0xc8, 0xc5, 0x15, 0x94, 0x38, + 0xf6, 0x82, 0x5d, 0x8d, 0xb3, 0xc9, 0xfc, 0x46, 0x51, 0x7b, 0xc5, 0x3d, 0xe6, 0xbf, 0x59, 0x00, + 0x21, 0x7d, 0x98, 0x76, 0xa6, 0x9e, 0x37, 0x74, 0xce, 0x69, 0x3f, 0xba, 0x0f, 0xeb, 0xb9, 0x0b, + 0x3a, 0x19, 0x4d, 0x67, 0x2d, 0x73, 0x23, 0x47, 0x05, 0x6b, 0x9e, 0x0b, 0xcd, 0xbb, 0xb4, 0x30, + 0x34, 0xef, 0xe2, 0x90, 0xbf, 0xb7, 0xa1, 0x96, 0x0d, 0x6c, 0xcb, 0xaa, 0x2f, 0x58, 0x49, 0x4c, + 0x5b, 0xfd, 0x7f, 0x5a, 0x02, 0x48, 0xfd, 0xac, 0xfe, 0xb2, 0x2f, 0x88, 0x2f, 0x18, 0x92, 0xe2, + 0xa2, 0x21, 0xb9, 0x07, 0x5a, 0x96, 0x2e, 0x13, 0x61, 0xb9, 0x91, 0x12, 0x2a, 0x15, 0xd0, 0x8d, + 0xb2, 0x41, 0x6c, 0x29, 0x74, 0x9a, 0xbc, 0x53, 0xcb, 0x48, 0x16, 0x41, 0x25, 0xc3, 0xaa, 0xb8, + 0x11, 0x4b, 0x02, 0x24, 0xbd, 0xaa, 0x9c, 0xe4, 0x61, 0x11, 0x4c, 0xe3, 0xe4, 0xfe, 0x4d, 0x59, + 0x4a, 0xaf, 0xb2, 0xa4, 0xe7, 0x8c, 0x56, 0xf7, 0x6f, 0x3e, 0x85, 0xea, 0xd1, 0xd4, 0xf3, 0xcc, + 0xd8, 0x39, 0x57, 0x77, 0x82, 0x36, 0x73, 0x2e, 0x6a, 0x99, 0xe1, 0x35, 0x2a, 0x47, 0x32, 0xa1, + 0xff, 0xaf, 0x2b, 0xb0, 0xcc, 0x86, 0xf1, 0xcf, 0xa0, 0x1a, 0xc5, 0xe3, 0x38, 0x7b, 0x5b, 0x44, + 0xba, 0x1e, 0x11, 0x9e, 0x2e, 0x7b, 0xd0, 0xb6, 0xc1, 0x32, 0x0f, 0xd2, 0x12, 0xaf, 0xdb, 0x80, + 0xe5, 0x28, 0x76, 0x26, 0x91, 0x8c, 0xbb, 0xc2, 0x09, 0x71, 0x07, 0x96, 0xfd, 0xc0, 0x76, 0xa2, + 0x7c, 0x74, 0x95, 0x1e, 0x6a, 0x63, 0x8c, 0x10, 0x3a, 0xac, 0x24, 0x23, 0x3e, 0x77, 0x63, 0x83, + 0x31, 0x14, 0xb3, 0xcd, 0xb1, 0x90, 0xfb, 0xaa, 0x88, 0xd5, 0x49, 0x1a, 0xd5, 0x0e, 0x3a, 0x4f, + 0xb3, 0x8e, 0x55, 0x0c, 0x7f, 0x99, 0x14, 0x77, 0xa0, 0x86, 0x3f, 0x9f, 0x87, 0x6e, 0xec, 0x0c, + 0x1e, 0x29, 0x99, 0x22, 0x03, 0x12, 0xb7, 0x00, 0x6c, 0x27, 0x76, 0x46, 0xf1, 0xe0, 0x3b, 0x4f, + 0x19, 0x08, 0x33, 0x10, 0xf1, 0x25, 0x88, 0x43, 0x6b, 0x74, 0x7a, 0xcc, 0x17, 0x8e, 0xbe, 0x9b, + 0x3a, 0xa1, 0x9b, 0x30, 0xee, 0x5a, 0xa6, 0x53, 0x8c, 0xf5, 0x94, 0xec, 0x97, 0x4c, 0x25, 0xae, + 0xc2, 0xca, 0xd8, 0x3a, 0xdf, 0x0e, 0x26, 0xd2, 0x82, 0x2e, 0x53, 0xe2, 0x11, 0x5c, 0x45, 0x19, + 0x25, 0x1b, 0x7d, 0x44, 0x7a, 0x05, 0x32, 0x87, 0x46, 0x09, 0x26, 0x0d, 0x3b, 0x22, 0xcf, 0xae, + 0xb7, 0x60, 0x43, 0x19, 0xc8, 0x6d, 0x67, 0xe2, 0xf8, 0xb6, 0xe3, 0x8f, 0xdc, 0x24, 0xe6, 0xe7, + 0xdc, 0xe4, 0xbf, 0x2c, 0x89, 0xb7, 0x33, 0xb4, 0xe2, 0x4d, 0x58, 0xc5, 0x0f, 0x87, 0x4e, 0x3c, + 0x0d, 0x7d, 0xdc, 0xfb, 0x58, 0xa2, 0x40, 0x01, 0xd1, 0x50, 0x30, 0x76, 0x73, 0x96, 0x89, 0xf4, + 0x9e, 0xd4, 0xb2, 0xb1, 0x9a, 0x40, 0xc9, 0x37, 0xff, 0x11, 0x5c, 0x65, 0x07, 0xca, 0xe8, 0x3b, + 0x4f, 0xca, 0x59, 0x66, 0x7a, 0x63, 0xa4, 0x62, 0x5c, 0x26, 0xec, 0xe0, 0x3b, 0x8f, 0x45, 0x2d, + 0xde, 0xeb, 0xfb, 0x70, 0x75, 0xea, 0x87, 0x4e, 0x14, 0x78, 0x2f, 0x92, 0xe0, 0xca, 0x27, 0x2e, + 0x0a, 0xc7, 0x5a, 0xf6, 0x5a, 0xde, 0x41, 0x42, 0x43, 0x32, 0xfd, 0xae, 0xeb, 0xc7, 0xc6, 0xc6, + 0x74, 0x1e, 0x18, 0x89, 0x1e, 0x6c, 0xa8, 0xb7, 0x9f, 0x72, 0xbd, 0xc2, 0xa2, 0xf9, 0x6b, 0xea, + 0xa2, 0x06, 0x51, 0xec, 0x7b, 0x96, 0x9f, 0x74, 0xc7, 0x85, 0x71, 0x59, 0x66, 0xcc, 0xf6, 0x90, + 0xfe, 0x7b, 0xb0, 0x9a, 0x9b, 0xdd, 0x73, 0x4e, 0x46, 0x83, 0x76, 0xb7, 0xdd, 0x1a, 0xb2, 0xd7, + 0x80, 0x74, 0x7b, 0x59, 0xca, 0xf8, 0xc7, 0x94, 0x32, 0xde, 0x04, 0xcb, 0xe4, 0x5d, 0xd3, 0x36, + 0x9e, 0xb4, 0xc9, 0xe1, 0xa3, 0xa8, 0x15, 0xf5, 0xbf, 0xb5, 0x04, 0xeb, 0xc3, 0xd0, 0xf2, 0x23, + 0x8b, 0xb5, 0x68, 0x3f, 0x0e, 0x03, 0x4f, 0x7c, 0x09, 0x95, 0x78, 0xe4, 0x65, 0x97, 0xdb, 0x6d, + 0xc5, 0xdc, 0x67, 0x48, 0x1f, 0x0c, 0x47, 0xec, 0x30, 0x5d, 0x8e, 0xf9, 0x87, 0xf8, 0x00, 0x96, + 0x0f, 0x9d, 0x63, 0xd7, 0x97, 0xfb, 0xdb, 0x95, 0xd9, 0x8c, 0x5b, 0x88, 0xdc, 0xbd, 0x64, 0x30, + 0x95, 0xf8, 0x08, 0x56, 0xe4, 0xb9, 0x53, 0x31, 0x7b, 0x6f, 0x21, 0xfb, 0x21, 0xc4, 0xee, 0x5e, + 0x32, 0x24, 0x9d, 0xf8, 0x0c, 0x2a, 0x61, 0x20, 0x1f, 0x94, 0x2a, 0x65, 0x99, 0x49, 0x9a, 0xc7, + 0x90, 0xf8, 0xdd, 0x4b, 0x46, 0x42, 0xab, 0x3f, 0x80, 0xb2, 0xac, 0x2c, 0x76, 0xc3, 0x56, 0xfb, + 0x49, 0x47, 0xf6, 0x60, 0xab, 0xbf, 0xb7, 0xd7, 0x19, 0x72, 0xbc, 0x69, 0xa3, 0xdf, 0xed, 0x6e, + 0x35, 0x5b, 0x4f, 0xb5, 0xa5, 0xad, 0x0a, 0xac, 0xf0, 0x22, 0xd0, 0xff, 0xc5, 0x02, 0xac, 0xcd, + 0x34, 0x40, 0x3c, 0x86, 0x12, 0x59, 0x43, 0xb9, 0x7b, 0xee, 0x2e, 0x6c, 0x65, 0x26, 0xcd, 0xb6, + 0x1e, 0xcc, 0xa1, 0x7f, 0x01, 0x8d, 0x3c, 0x3c, 0xe3, 0x93, 0xb2, 0x0a, 0x55, 0xa3, 0xdd, 0xdc, + 0x36, 0xfb, 0xbd, 0xee, 0x37, 0xec, 0x27, 0x46, 0xc9, 0xe7, 0x46, 0x87, 0x9c, 0x41, 0x7e, 0x17, + 0xb4, 0xd9, 0x8e, 0x11, 0x4f, 0x60, 0x0d, 0x65, 0x39, 0xcf, 0x61, 0x07, 0xb8, 0x74, 0xc8, 0x6e, + 0x2d, 0xe8, 0x49, 0x49, 0xc6, 0xb7, 0xa9, 0x47, 0xb9, 0xb4, 0xfe, 0x7b, 0x20, 0xe6, 0x7b, 0xf0, + 0x2f, 0xaf, 0xf8, 0xff, 0xbb, 0x00, 0x25, 0x9c, 0xf0, 0xe2, 0x4d, 0x58, 0xe6, 0x23, 0xd0, 0x42, + 0xf6, 0xbe, 0x1e, 0xf1, 0x2c, 0x9c, 0x16, 0x84, 0x13, 0xef, 0x41, 0x31, 0x1e, 0xa9, 0xc8, 0xd2, + 0xd7, 0x5e, 0x32, 0xf9, 0x76, 0x2f, 0x19, 0x48, 0x25, 0xee, 0x41, 0xd1, 0xb6, 0x55, 0x5c, 0xb2, + 0xcc, 0x01, 0xf9, 0xb6, 0x73, 0x44, 0x37, 0x36, 0xe9, 0x2d, 0x25, 0x24, 0x11, 0x6f, 0x41, 0xd1, + 0x1e, 0x79, 0xf9, 0x20, 0x73, 0x7c, 0x76, 0x9d, 0x14, 0x68, 0x8f, 0x3c, 0xd4, 0xeb, 0xe2, 0xf0, + 0xc2, 0x0c, 0xa7, 0x3e, 0x85, 0xe5, 0x50, 0x0f, 0x82, 0xd5, 0x50, 0x56, 0x9c, 0x52, 0x6c, 0x8f, + 0x48, 0x86, 0xa8, 0x95, 0x2f, 0xfe, 0x28, 0x5b, 0xa0, 0x1b, 0xc9, 0xb7, 0x81, 0xb6, 0x56, 0x80, + 0x1e, 0x93, 0xd6, 0xdf, 0xa7, 0xc7, 0x65, 0x50, 0x8d, 0xd7, 0xd5, 0xaf, 0x05, 0x2f, 0x6f, 0x4a, + 0x8c, 0xfe, 0x4f, 0x8a, 0x50, 0xcb, 0xd4, 0x47, 0x7c, 0x02, 0x15, 0x3b, 0xbf, 0x10, 0xaf, 0xcf, + 0x55, 0xfa, 0xc1, 0xb6, 0x5a, 0x82, 0xb6, 0x9c, 0xde, 0xe4, 0x8d, 0x1f, 0x9b, 0x2f, 0xac, 0xd0, + 0xe5, 0x2b, 0xc6, 0x4b, 0x59, 0xb7, 0xf8, 0x81, 0x13, 0x3f, 0x53, 0x98, 0xdd, 0x4b, 0x46, 0x3d, + 0xca, 0xa4, 0xc9, 0xf2, 0x25, 0x9b, 0x54, 0xcc, 0xc6, 0x2e, 0x96, 0xcd, 0xda, 0xbd, 0x64, 0x28, + 0x3c, 0x92, 0x3a, 0xe7, 0xce, 0x68, 0x1a, 0x2b, 0xcb, 0xd7, 0xaa, 0x6a, 0x10, 0x01, 0xe9, 0x79, + 0x5c, 0xfe, 0x29, 0x1e, 0xe2, 0x9e, 0x66, 0x79, 0x5e, 0x40, 0x22, 0xf1, 0x72, 0xf6, 0xd8, 0x69, + 0x3b, 0x81, 0xf3, 0x73, 0xbc, 0x2a, 0x25, 0xde, 0x86, 0xe5, 0x20, 0x3e, 0x71, 0x94, 0xa6, 0xae, + 0x9e, 0xa9, 0x41, 0xd0, 0x76, 0xab, 0x8b, 0x33, 0x85, 0xd0, 0xfa, 0x1f, 0x15, 0xa0, 0x2c, 0x7b, + 0x40, 0xac, 0xc3, 0xea, 0xa0, 0x3d, 0x34, 0x9f, 0x35, 0x8d, 0x4e, 0x73, 0xab, 0xdb, 0x96, 0xb1, + 0xf1, 0x9e, 0x18, 0xcd, 0x9e, 0x64, 0x90, 0x46, 0xfb, 0x59, 0xff, 0x69, 0x9b, 0x1d, 0xbd, 0xb6, + 0xdb, 0xbd, 0x6f, 0xb4, 0x22, 0x3b, 0x45, 0xb6, 0xf7, 0x9b, 0x06, 0xf2, 0xca, 0x1a, 0x94, 0xdb, + 0x5f, 0xb7, 0x5b, 0x07, 0xc4, 0x2c, 0x1b, 0x00, 0xdb, 0xed, 0x66, 0xb7, 0xdb, 0x6f, 0x21, 0xf3, + 0x5c, 0x11, 0x02, 0x1a, 0x2d, 0xa3, 0xdd, 0x1c, 0xb6, 0xcd, 0x66, 0xab, 0xd5, 0x3f, 0xe8, 0x0d, + 0xb5, 0x32, 0x7e, 0xb1, 0xd9, 0x1d, 0xb6, 0x8d, 0x04, 0x44, 0x8f, 0xef, 0x6d, 0x1b, 0xfd, 0xfd, + 0x04, 0x52, 0xdd, 0xaa, 0x42, 0x79, 0xc4, 0x63, 0xa5, 0xff, 0xc7, 0xeb, 0xec, 0xef, 0x90, 0x4e, + 0x4d, 0xf1, 0x39, 0x54, 0x6c, 0x3b, 0x37, 0xc6, 0x37, 0x17, 0x4d, 0xe1, 0x07, 0xdb, 0xb6, 0x1a, + 0x66, 0xfe, 0x21, 0xde, 0x50, 0x0b, 0x69, 0x69, 0x6e, 0x21, 0xa9, 0x65, 0xf4, 0x73, 0x58, 0x93, + 0x6f, 0x9f, 0x24, 0x47, 0xa5, 0xb9, 0x55, 0xd2, 0x22, 0xe4, 0xb6, 0xc4, 0xed, 0x5e, 0x32, 0x1a, + 0xa3, 0x1c, 0x44, 0xfc, 0x0c, 0x1a, 0x16, 0x1f, 0x40, 0x66, 0x8f, 0x5a, 0x13, 0x61, 0xbf, 0x49, + 0xe7, 0x8f, 0x69, 0xf6, 0x55, 0x2b, 0x0b, 0xc0, 0x89, 0x68, 0x87, 0xc1, 0x24, 0xcd, 0xbc, 0x9c, + 0xbb, 0x9f, 0x11, 0x06, 0x93, 0x4c, 0xde, 0xba, 0x9d, 0x49, 0x8b, 0xcf, 0xa0, 0x2e, 0x6b, 0x9e, + 0x1a, 0xdb, 0x93, 0x25, 0xcb, 0xd5, 0x26, 0xe1, 0x7d, 0xf7, 0x92, 0x51, 0x1b, 0xa5, 0x49, 0xf1, + 0x08, 0x25, 0xf6, 0x54, 0xd5, 0x29, 0x67, 0xe7, 0x1a, 0xd5, 0x56, 0xe5, 0x02, 0x2b, 0x49, 0x89, + 0x8f, 0x00, 0xa8, 0x9e, 0x9c, 0xa7, 0x92, 0xbb, 0xb9, 0x18, 0x06, 0x13, 0x95, 0xa5, 0x6a, 0xab, + 0x44, 0xa6, 0x7a, 0x6c, 0x01, 0xac, 0xce, 0x57, 0x8f, 0x85, 0x82, 0xa4, 0x7a, 0x6c, 0x0c, 0x4c, + 0xaa, 0xc7, 0xd9, 0x60, 0xae, 0x7a, 0x2a, 0x17, 0x57, 0x8f, 0x33, 0xa9, 0xea, 0x71, 0x9e, 0xda, + 0x6c, 0xf5, 0x54, 0x16, 0xaa, 0x1e, 0xe7, 0xf8, 0xd9, 0x9c, 0x8e, 0x56, 0x7f, 0xa9, 0x8e, 0x86, + 0xc3, 0x96, 0xd7, 0xd2, 0x7e, 0x06, 0x8d, 0xe8, 0x24, 0x38, 0xcb, 0x30, 0x90, 0xd5, 0x6c, 0xee, + 0xc1, 0x49, 0x70, 0x96, 0xe5, 0x20, 0xab, 0x51, 0x16, 0x80, 0xb5, 0xe5, 0x26, 0xd2, 0x5d, 0x8a, + 0x46, 0xb6, 0xb6, 0xd4, 0xc2, 0x67, 0xae, 0x73, 0x86, 0xb5, 0xb5, 0x54, 0x02, 0x3b, 0x25, 0x3d, + 0x78, 0x88, 0xe4, 0x51, 0x42, 0xee, 0xbe, 0x9f, 0xfc, 0x12, 0x24, 0x47, 0x10, 0x11, 0xce, 0xad, + 0xa9, 0x9f, 0xcd, 0xa6, 0x65, 0xe7, 0xd6, 0x81, 0x9f, 0xcb, 0x58, 0x67, 0x52, 0x99, 0x35, 0x5d, + 0x15, 0x91, 0xf3, 0xdd, 0xd4, 0xf1, 0x47, 0x8e, 0xbc, 0xd7, 0x9b, 0x5b, 0x15, 0x03, 0x89, 0x4b, + 0x57, 0x85, 0x82, 0x24, 0xf3, 0x3a, 0xc9, 0x2e, 0x66, 0xe7, 0x75, 0x26, 0x33, 0xcd, 0xeb, 0x24, + 0x6b, 0xb2, 0xa0, 0x92, 0xbc, 0x97, 0xe7, 0x16, 0x54, 0x26, 0x33, 0x2f, 0xa8, 0x24, 0xf7, 0x23, + 0x90, 0xb3, 0x89, 0x3b, 0x37, 0x77, 0xfb, 0x97, 0x6b, 0x2d, 0x7b, 0x17, 0x46, 0x49, 0x0a, 0xe7, + 0x6a, 0xe8, 0xa0, 0x4e, 0x28, 0xa7, 0xc2, 0x95, 0xec, 0x5c, 0x35, 0x08, 0x93, 0x2c, 0xa5, 0x30, + 0x4d, 0x66, 0x3e, 0x36, 0x71, 0xe3, 0x70, 0xd3, 0x9e, 0xff, 0xd8, 0xbe, 0x1b, 0x87, 0xe9, 0xc7, + 0x30, 0x25, 0x3e, 0x00, 0x9a, 0x86, 0x9c, 0xc5, 0xc9, 0xb2, 0x6e, 0xec, 0x16, 0x99, 0xa1, 0x62, + 0xcb, 0xdf, 0x38, 0x59, 0xe4, 0x37, 0x46, 0xf6, 0x68, 0xf3, 0x28, 0x3b, 0x59, 0xf8, 0x13, 0xad, + 0xed, 0x16, 0x4e, 0x16, 0x26, 0x6a, 0xd9, 0x23, 0x71, 0x1f, 0x28, 0x37, 0xd1, 0x1f, 0x67, 0xf7, + 0x1d, 0x2c, 0x9f, 0xa9, 0xcb, 0x48, 0x80, 0xb4, 0xd8, 0x02, 0x2f, 0xf0, 0x55, 0xc3, 0x4f, 0x72, + 0x2d, 0x40, 0x44, 0xc2, 0x0c, 0x46, 0x49, 0x4a, 0xff, 0x97, 0x57, 0xa0, 0x2c, 0x79, 0xad, 0xb8, + 0x0c, 0x6b, 0x92, 0xe5, 0x6f, 0x37, 0x87, 0xcd, 0xad, 0xe6, 0x00, 0x85, 0x34, 0x01, 0x0d, 0xe6, + 0xf9, 0x09, 0xac, 0x80, 0xfb, 0x00, 0x31, 0xfd, 0x04, 0xb4, 0x84, 0xfb, 0x80, 0xcc, 0xcb, 0xee, + 0xb4, 0x45, 0xb1, 0x06, 0x35, 0xce, 0xc8, 0x00, 0x0a, 0xd5, 0x4b, 0xb9, 0x38, 0xbd, 0x9c, 0xc9, + 0xc2, 0x0e, 0xd1, 0x2b, 0x69, 0x16, 0x06, 0x94, 0x93, 0x2c, 0xca, 0x63, 0x5a, 0x40, 0x63, 0x68, + 0x1c, 0xf4, 0x5a, 0xe9, 0x77, 0xaa, 0x14, 0x5e, 0x95, 0x8b, 0x79, 0xd6, 0x69, 0x3f, 0xd7, 0x00, + 0x33, 0x71, 0x29, 0x94, 0x26, 0x4f, 0x6a, 0x2a, 0x84, 0x92, 0x75, 0x71, 0x0d, 0x2e, 0x0f, 0x76, + 0xfb, 0xcf, 0x4d, 0xce, 0x94, 0x34, 0x61, 0x55, 0x6c, 0x80, 0x96, 0x41, 0x70, 0xf1, 0x0d, 0xfc, + 0x24, 0x41, 0x15, 0xe1, 0x40, 0x5b, 0xa3, 0xc7, 0x59, 0x10, 0x36, 0xe4, 0x7d, 0x57, 0xc3, 0xa6, + 0x70, 0xd6, 0x7e, 0xf7, 0x60, 0xaf, 0x37, 0xd0, 0xd6, 0xb1, 0x12, 0x04, 0xe1, 0x9a, 0x8b, 0xa4, + 0x98, 0x74, 0xb7, 0xbe, 0x4c, 0x1b, 0x38, 0xc2, 0x9e, 0x37, 0x8d, 0x5e, 0xa7, 0xf7, 0x64, 0xa0, + 0x6d, 0x24, 0x25, 0xb7, 0x0d, 0xa3, 0x6f, 0x0c, 0xb4, 0x2b, 0x09, 0x60, 0x30, 0x6c, 0x0e, 0x0f, + 0x06, 0xda, 0xd5, 0xa4, 0x96, 0xfb, 0x46, 0xbf, 0xd5, 0x1e, 0x0c, 0xba, 0x9d, 0xc1, 0x50, 0xbb, + 0x26, 0xae, 0xc0, 0x7a, 0x5a, 0x23, 0x45, 0xbc, 0x99, 0xa9, 0xa8, 0xf1, 0xa4, 0x3d, 0xd4, 0xae, + 0x27, 0xd5, 0x68, 0xf5, 0xbb, 0xdd, 0x26, 0xf9, 0x6a, 0xdf, 0x40, 0x22, 0xba, 0x52, 0x20, 0x5b, + 0xf3, 0x1a, 0xd6, 0xeb, 0xa0, 0x97, 0x05, 0xdd, 0xcc, 0x4c, 0x8d, 0x41, 0xfb, 0x97, 0x07, 0xed, + 0x5e, 0xab, 0xad, 0xbd, 0x9e, 0x4e, 0x8d, 0x04, 0x76, 0x2b, 0x99, 0x1a, 0x09, 0xe8, 0x76, 0xf2, + 0x4d, 0x05, 0x1a, 0x68, 0x77, 0x92, 0x0e, 0x3b, 0xd8, 0x7f, 0x62, 0x34, 0xb7, 0xdb, 0x9a, 0x8e, + 0x10, 0xa3, 0xdd, 0x6b, 0xee, 0xa9, 0x81, 0x7d, 0x33, 0x33, 0xb0, 0xfb, 0x9d, 0xa1, 0xa1, 0xdd, + 0x4d, 0x06, 0x92, 0x92, 0x6f, 0x89, 0xd7, 0xe0, 0x5a, 0x76, 0xca, 0x99, 0xcf, 0x3b, 0xc3, 0x5d, + 0xe9, 0xf9, 0xff, 0x36, 0x3b, 0x8f, 0x13, 0xb2, 0xb5, 0xdd, 0xe2, 0x2b, 0x0e, 0x94, 0x17, 0x53, + 0xf7, 0xf4, 0x52, 0xe5, 0x0d, 0xed, 0x8d, 0xfb, 0x6b, 0xb2, 0x2b, 0x7a, 0x3d, 0xba, 0x3e, 0x32, + 0xd8, 0xaa, 0xa3, 0xe4, 0xa6, 0x44, 0x10, 0xfd, 0x2b, 0x10, 0xd9, 0xe7, 0xb4, 0x65, 0x80, 0x04, + 0x01, 0xa5, 0xa3, 0x30, 0x18, 0x2b, 0xb7, 0x13, 0xfc, 0x2d, 0xee, 0x40, 0x6d, 0x32, 0x3d, 0x24, + 0x5f, 0xf7, 0x34, 0x30, 0x76, 0x16, 0xa4, 0xff, 0x3b, 0x05, 0x68, 0xe4, 0xc5, 0x0f, 0x3a, 0x4e, + 0x39, 0x32, 0xfd, 0x20, 0xe6, 0xf7, 0xb1, 0xa2, 0xe4, 0x95, 0xeb, 0xa3, 0x5e, 0x10, 0xd3, 0x03, + 0x59, 0x51, 0xce, 0xeb, 0x6b, 0x69, 0xc6, 0xeb, 0xab, 0x03, 0x97, 0x73, 0xaf, 0x97, 0xe7, 0x5e, + 0x27, 0x93, 0x4a, 0xe2, 0x7c, 0xfd, 0x0d, 0x11, 0xcd, 0xb7, 0x49, 0x86, 0x37, 0x2f, 0xa5, 0xe1, + 0xcd, 0x77, 0x61, 0x35, 0x27, 0xed, 0x90, 0xb5, 0xec, 0x28, 0x5f, 0xd3, 0x8a, 0x7b, 0xf4, 0xea, + 0x6a, 0xea, 0x7f, 0xa7, 0x00, 0xf5, 0xac, 0xec, 0xf3, 0x1b, 0x97, 0x44, 0xf7, 0x16, 0xe5, 0x6f, + 0xd3, 0xb5, 0xd5, 0xb3, 0x56, 0x0a, 0x44, 0x2e, 0x95, 0x35, 0x3e, 0x99, 0xda, 0x39, 0x1d, 0x24, + 0xcd, 0xc9, 0x82, 0xc8, 0x53, 0xee, 0xc4, 0x19, 0x9d, 0xee, 0x3c, 0x45, 0x02, 0xe5, 0x29, 0x97, + 0x40, 0xf4, 0xdb, 0x50, 0xdd, 0x39, 0x55, 0xd7, 0x38, 0xb3, 0xaf, 0xc4, 0x55, 0x39, 0x9a, 0xba, + 0xfe, 0xc7, 0x05, 0x68, 0xa4, 0xb6, 0x21, 0x72, 0xd4, 0xe7, 0x57, 0xef, 0x79, 0x3a, 0x2c, 0xd9, + 0x87, 0xa9, 0x37, 0xe4, 0x52, 0xd6, 0x1b, 0xf2, 0x4d, 0x59, 0x58, 0x31, 0xcb, 0xf4, 0x93, 0x6f, + 0xc9, 0x58, 0xed, 0x8f, 0xa0, 0x8e, 0xff, 0x0d, 0xe7, 0xc8, 0x09, 0x43, 0xc7, 0xce, 0xc7, 0xbf, + 0x4a, 0x89, 0x73, 0x44, 0xa4, 0xe5, 0x25, 0x4f, 0x36, 0x2c, 0x7c, 0x54, 0x86, 0x1e, 0x3b, 0xfa, + 0x3f, 0x8b, 0x50, 0xcb, 0x48, 0x92, 0x3f, 0x6a, 0xfa, 0xdd, 0x84, 0x6a, 0xfa, 0xc4, 0x89, 0x0c, + 0x90, 0x9d, 0x00, 0x72, 0x63, 0x55, 0x9c, 0x19, 0x2b, 0x72, 0x4a, 0xa6, 0xa8, 0x9c, 0xa9, 0x53, + 0x32, 0x25, 0xf3, 0x46, 0xf1, 0xe5, 0x57, 0x9c, 0x9e, 0x7e, 0x0c, 0xf5, 0x8c, 0x45, 0x5b, 0xb9, + 0x2f, 0xce, 0xd2, 0xd7, 0x52, 0xeb, 0x76, 0x24, 0xae, 0xc0, 0xca, 0xd1, 0xa9, 0x69, 0x1f, 0xb2, + 0x47, 0x40, 0xd5, 0x58, 0x3e, 0x3a, 0xdd, 0x3e, 0x24, 0x0f, 0x88, 0xa3, 0x44, 0x78, 0x62, 0x3b, + 0x63, 0xe5, 0x48, 0x89, 0x48, 0xf7, 0xa0, 0x7c, 0x74, 0x9a, 0x0d, 0xb3, 0x3b, 0xd7, 0xe5, 0x2b, + 0x47, 0xa7, 0x14, 0xe9, 0xf5, 0x43, 0xd8, 0x90, 0x3b, 0xb8, 0x15, 0x29, 0xb3, 0x1b, 0x2e, 0x1d, + 0x7e, 0x33, 0x6e, 0x9d, 0x71, 0xcd, 0x88, 0x8d, 0x6e, 0x38, 0xe3, 0x74, 0xa8, 0x67, 0x26, 0x20, + 0xdb, 0x0b, 0xab, 0x46, 0x0e, 0x26, 0x1e, 0x43, 0xfd, 0xe8, 0x94, 0x07, 0x74, 0x18, 0xec, 0x39, + 0x32, 0x92, 0xcc, 0xc6, 0xec, 0x50, 0xd2, 0x4d, 0x8a, 0x1c, 0xa5, 0xb8, 0x0a, 0x2b, 0x86, 0x75, + 0x36, 0xf8, 0x65, 0x97, 0xc4, 0xc8, 0xaa, 0x21, 0x53, 0xfc, 0xa2, 0x9c, 0xfe, 0x0f, 0x0a, 0xd0, + 0x48, 0xb5, 0x00, 0x5c, 0x84, 0xe2, 0x7e, 0xfa, 0x98, 0x74, 0x43, 0x31, 0x89, 0x3c, 0xc9, 0x83, + 0xe1, 0xc5, 0x84, 0x1f, 0x67, 0x5d, 0xf4, 0xae, 0xd3, 0xa2, 0x23, 0x86, 0xe2, 0xc2, 0xc7, 0xef, + 0x9f, 0x40, 0x71, 0x78, 0x31, 0x61, 0x8b, 0x13, 0x6e, 0x8a, 0xac, 0x9d, 0xf2, 0x76, 0x48, 0xb7, + 0x95, 0x9e, 0xb6, 0xbf, 0xe1, 0x80, 0xfe, 0xfb, 0x46, 0x67, 0xaf, 0x69, 0x7c, 0x63, 0x22, 0x80, + 0xc4, 0x86, 0x9d, 0xbe, 0xd1, 0xee, 0x3c, 0xe9, 0x11, 0xa0, 0x44, 0xf6, 0xa8, 0xb4, 0x8a, 0x4d, + 0xdb, 0xde, 0x39, 0xcd, 0x3e, 0x89, 0x53, 0xc8, 0x3d, 0x89, 0x93, 0xbc, 0x1e, 0x95, 0x7d, 0xea, + 0x30, 0x4e, 0xce, 0x3d, 0x44, 0xb2, 0x0a, 0x93, 0x25, 0x2d, 0xde, 0x81, 0xd2, 0xd1, 0xa9, 0x73, + 0x91, 0x57, 0xf5, 0xf2, 0x0b, 0x88, 0x08, 0xf4, 0x3f, 0x2b, 0x80, 0xc8, 0x55, 0x84, 0xb5, 0x8f, + 0xdf, 0xb4, 0x2e, 0x9f, 0xc3, 0xa6, 0x7a, 0x17, 0x95, 0xa8, 0x32, 0x67, 0x1a, 0xb2, 0x4b, 0xaf, + 0x04, 0x69, 0x04, 0x83, 0xf4, 0xe9, 0x29, 0xf1, 0x21, 0xf0, 0x2b, 0x86, 0x74, 0xf1, 0xa6, 0xf4, + 0x12, 0x4d, 0xd1, 0x48, 0x69, 0xd2, 0x67, 0x0b, 0xb3, 0xcf, 0x31, 0xf2, 0x71, 0xc8, 0x5a, 0x3a, + 0x6a, 0xb4, 0xe6, 0xf5, 0xbf, 0x51, 0x80, 0xcb, 0xf9, 0x09, 0xf1, 0x17, 0x6b, 0x65, 0xfe, 0xed, + 0xc9, 0xe2, 0xec, 0xdb, 0x93, 0x8b, 0xe6, 0x53, 0x69, 0xe1, 0x7c, 0xfa, 0xfd, 0x02, 0x6c, 0x64, + 0x7a, 0x3f, 0xd5, 0x17, 0xff, 0x8a, 0x6a, 0x96, 0x79, 0x82, 0xb2, 0x94, 0x7b, 0x82, 0x52, 0xff, + 0xd3, 0x02, 0x5c, 0x9d, 0xa9, 0x89, 0xe1, 0xfc, 0x95, 0xd6, 0x25, 0xff, 0x54, 0x25, 0x1d, 0xc9, + 0xa8, 0xa0, 0xf7, 0x85, 0x7b, 0xc5, 0xcc, 0x53, 0x95, 0x74, 0x4e, 0x47, 0x87, 0xc5, 0xaf, 0xcb, + 0xb7, 0x51, 0xcc, 0xe8, 0xc2, 0x1f, 0xc9, 0xc1, 0xae, 0x12, 0x64, 0x70, 0xe1, 0x8f, 0x70, 0x93, + 0x1a, 0x3b, 0xe1, 0xb1, 0x32, 0xe5, 0x71, 0x42, 0xff, 0x0f, 0x0b, 0x70, 0x7d, 0xa6, 0x65, 0xcd, + 0x69, 0x1c, 0x48, 0xdf, 0x8e, 0xbf, 0xa2, 0xc6, 0xdd, 0x06, 0x7a, 0xb5, 0x49, 0x39, 0x95, 0x64, + 0x2e, 0x64, 0xc8, 0xef, 0x6a, 0x50, 0xb4, 0xad, 0x0b, 0x19, 0x33, 0x19, 0x7f, 0xe2, 0x32, 0x3e, + 0x09, 0xa6, 0xa1, 0xbc, 0x41, 0x40, 0xbf, 0xf5, 0x4f, 0x60, 0x3d, 0xad, 0x7a, 0x4b, 0xbe, 0x23, + 0x7a, 0x1b, 0x6a, 0xbe, 0x73, 0x66, 0xaa, 0x57, 0x46, 0xe5, 0xdd, 0x32, 0xdf, 0x39, 0x93, 0x04, + 0xfa, 0x4e, 0x96, 0x43, 0x52, 0x75, 0xae, 0x43, 0x25, 0xf0, 0xec, 0x9c, 0x47, 0x69, 0xe0, 0xd9, + 0x0a, 0x85, 0xa5, 0x65, 0x5a, 0x59, 0xf6, 0x9d, 0x33, 0x9a, 0x9d, 0x67, 0xb2, 0x9c, 0xa6, 0xad, + 0x3c, 0xa8, 0x16, 0xb9, 0x27, 0x5f, 0x87, 0xca, 0x24, 0xcc, 0x75, 0x53, 0x79, 0x12, 0xf2, 0x67, + 0xef, 0xca, 0x9b, 0x6b, 0x2f, 0xf3, 0xc0, 0xe3, 0xbb, 0x6c, 0x1a, 0x14, 0x27, 0x41, 0x24, 0x9d, + 0x84, 0xf0, 0xa7, 0xfe, 0xa9, 0x64, 0x8e, 0xa4, 0x0f, 0xf2, 0x97, 0x35, 0x28, 0xba, 0xf6, 0x39, + 0x7d, 0x78, 0xd5, 0xc0, 0x9f, 0x24, 0xdf, 0x39, 0xdf, 0xc9, 0xcb, 0x73, 0xf8, 0x53, 0xdf, 0x82, + 0x9a, 0x91, 0x53, 0x7e, 0xeb, 0x19, 0x3b, 0x52, 0x94, 0x7f, 0x83, 0x26, 0xed, 0x20, 0xa3, 0x96, + 0x9a, 0x91, 0x22, 0x3d, 0x92, 0xec, 0xf0, 0x99, 0x15, 0x8e, 0x4e, 0xac, 0xb0, 0xeb, 0xf8, 0xc7, + 0xf1, 0x09, 0x76, 0x39, 0x9b, 0x77, 0xb3, 0x5d, 0x08, 0x0c, 0x52, 0xd3, 0x01, 0x7b, 0xd1, 0x23, + 0x72, 0xe9, 0x60, 0x50, 0xf5, 0x9d, 0x33, 0x99, 0xff, 0x75, 0x00, 0xec, 0x7f, 0x89, 0x96, 0xb7, + 0x77, 0x02, 0xcf, 0x66, 0xb4, 0xbe, 0x2e, 0xdb, 0x2b, 0x63, 0x7e, 0x6f, 0x3b, 0x47, 0xba, 0x27, + 0x47, 0x9e, 0x1b, 0x24, 0x3b, 0xe1, 0x37, 0x1a, 0x46, 0xf1, 0x06, 0xd4, 0x95, 0xa5, 0x82, 0x9e, + 0x3d, 0xe2, 0xcf, 0xd7, 0x14, 0xac, 0x37, 0x1d, 0xeb, 0xff, 0x51, 0x09, 0xea, 0x4d, 0xf6, 0x39, + 0x9d, 0x5c, 0xf4, 0x27, 0xb1, 0xf8, 0x3d, 0xb8, 0x42, 0xf1, 0xf6, 0x39, 0xf4, 0x01, 0xb9, 0x7a, + 0xb2, 0x93, 0x23, 0x77, 0xe2, 0xfd, 0x4c, 0x27, 0xca, 0x2c, 0x0f, 0x06, 0xa7, 0xee, 0x84, 0xa3, + 0x21, 0x74, 0xec, 0x73, 0xf2, 0x79, 0x64, 0xcf, 0x0c, 0x0a, 0xcd, 0x9f, 0x47, 0x50, 0xe8, 0x67, + 0x2c, 0x7e, 0x72, 0x2a, 0x8b, 0x95, 0x4e, 0x3e, 0x08, 0xdc, 0x3f, 0x65, 0x9a, 0xd4, 0x25, 0x6a, + 0x6e, 0x5b, 0x96, 0x2e, 0x51, 0xe9, 0xfc, 0x1e, 0xc0, 0xba, 0x7c, 0x1e, 0x80, 0x5e, 0x25, 0x34, + 0x47, 0xc1, 0xe4, 0x42, 0x1e, 0xfc, 0xbe, 0xf3, 0x92, 0xaa, 0x76, 0x98, 0x14, 0x41, 0x5c, 0xcf, + 0xb5, 0x28, 0x0f, 0x15, 0xcf, 0x40, 0x60, 0x97, 0x4e, 0xbc, 0x29, 0xee, 0x67, 0xea, 0x3d, 0x44, + 0x76, 0x45, 0xbf, 0xb7, 0xa0, 0xd4, 0x9e, 0x73, 0xb6, 0x4f, 0xb4, 0xb2, 0x10, 0x2e, 0x56, 0xf3, + 0x67, 0xc0, 0x37, 0xda, 0x70, 0xed, 0x25, 0x7d, 0xf5, 0x2a, 0xa7, 0x95, 0x4a, 0xc6, 0x69, 0xe5, + 0xc6, 0x16, 0x6c, 0x2c, 0x6a, 0xc7, 0x4f, 0x2a, 0xa3, 0x05, 0x57, 0x16, 0xd6, 0xfa, 0xa7, 0x14, + 0xa2, 0xff, 0x7e, 0x03, 0x20, 0x5d, 0x4e, 0x39, 0x09, 0xba, 0x30, 0x23, 0x41, 0xff, 0x24, 0x2f, + 0xc3, 0x4f, 0xa0, 0x81, 0xe3, 0x98, 0x09, 0xe2, 0xba, 0x38, 0x7a, 0x6c, 0x1d, 0xa9, 0x86, 0x69, + 0xf8, 0xbe, 0x79, 0x6f, 0x99, 0xd2, 0x42, 0x6f, 0x99, 0x8f, 0xa1, 0xcc, 0xa7, 0x83, 0x6a, 0x50, + 0xaf, 0xcd, 0xb2, 0x86, 0x07, 0x32, 0x68, 0x8e, 0xa2, 0x13, 0x6d, 0x68, 0x24, 0xf1, 0x2e, 0xb2, + 0xf1, 0x23, 0x6f, 0xcd, 0xe7, 0x54, 0x64, 0xfc, 0xd0, 0xa2, 0x95, 0x4d, 0x66, 0x04, 0xee, 0x78, + 0x2c, 0x4d, 0xd6, 0x24, 0x70, 0x97, 0xb3, 0x02, 0xf7, 0x70, 0xcc, 0x86, 0x6a, 0x14, 0xb8, 0x3f, + 0x80, 0xcb, 0xd2, 0xef, 0x18, 0x33, 0x60, 0x77, 0x12, 0x3d, 0xdf, 0x12, 0xd3, 0x64, 0x3c, 0xfa, + 0x31, 0xa9, 0xa3, 0x48, 0xfe, 0x35, 0x6c, 0xf0, 0x93, 0x09, 0xf4, 0x3a, 0xfd, 0x28, 0xf0, 0x3a, + 0xb6, 0x39, 0xb6, 0x26, 0x52, 0x0f, 0x78, 0x67, 0xae, 0xb2, 0xfc, 0x62, 0xc2, 0xf0, 0xd0, 0x23, + 0xbf, 0xed, 0xc4, 0xa7, 0x6a, 0x7d, 0x34, 0x0b, 0x9f, 0x71, 0x6d, 0x80, 0x39, 0xd7, 0x86, 0x59, + 0xcd, 0xa0, 0xb6, 0x40, 0x33, 0x48, 0xe5, 0xfb, 0x7a, 0x56, 0xbe, 0x17, 0xef, 0x43, 0x59, 0xc6, + 0x21, 0x94, 0xc6, 0x6a, 0x31, 0xbf, 0xc8, 0x0c, 0x45, 0x82, 0x5f, 0x52, 0x8e, 0x36, 0x14, 0xf3, + 0xba, 0xc1, 0x5f, 0xca, 0xc2, 0xc4, 0x96, 0xb4, 0xd4, 0x26, 0x3e, 0xe6, 0xd2, 0x30, 0x7d, 0x23, + 0x53, 0x70, 0x82, 0x93, 0xa6, 0x84, 0x99, 0x1c, 0x37, 0xfe, 0xb0, 0x0c, 0x2b, 0xd2, 0x5d, 0xe2, + 0x3e, 0x94, 0xec, 0x50, 0xde, 0x87, 0x49, 0x54, 0x99, 0xbc, 0x5c, 0xb9, 0x7b, 0xc9, 0x20, 0x1a, + 0xf1, 0x00, 0x56, 0x2c, 0xdb, 0x36, 0x8f, 0x4e, 0xf3, 0x87, 0xe8, 0x33, 0x32, 0xff, 0xee, 0x25, + 0x63, 0xd9, 0x22, 0xe1, 0xff, 0x73, 0xa8, 0x22, 0x7d, 0x36, 0xfa, 0xf0, 0x9c, 0x26, 0xa3, 0xa4, + 0xf3, 0xdd, 0x4b, 0x46, 0xc5, 0x52, 0x92, 0xfa, 0x6f, 0xe7, 0x8f, 0x23, 0x4a, 0x73, 0x0d, 0x9c, + 0x11, 0x2d, 0x67, 0x0e, 0x26, 0x7e, 0x07, 0xd8, 0x3e, 0x9d, 0x88, 0x13, 0xcb, 0xd9, 0xf3, 0xda, + 0x39, 0xe1, 0x63, 0xf7, 0x92, 0xc1, 0x9b, 0xaa, 0x12, 0x46, 0x3e, 0x55, 0x47, 0x05, 0xc4, 0x9e, + 0x57, 0x16, 0xf7, 0x0c, 0xf2, 0xe8, 0xe4, 0xbc, 0x80, 0x18, 0x36, 0x66, 0xb3, 0x13, 0xf7, 0xd1, + 0xf2, 0x5c, 0xb6, 0x44, 0xe4, 0xa0, 0x6c, 0x89, 0xfc, 0xf1, 0x18, 0x6a, 0x6c, 0x39, 0xe6, 0x7c, + 0x95, 0xb9, 0xae, 0x4d, 0x25, 0x06, 0x3a, 0x8b, 0x4c, 0xe5, 0x87, 0x96, 0x6a, 0x67, 0xe8, 0x64, + 0x8f, 0x7b, 0x6e, 0x2e, 0xec, 0x28, 0x23, 0x39, 0xf9, 0xe1, 0xc6, 0x1a, 0x9c, 0x47, 0x74, 0x61, + 0x43, 0x9e, 0x8b, 0xb0, 0x74, 0xa0, 0x36, 0x74, 0x98, 0x1b, 0xaf, 0x9c, 0xf8, 0xb0, 0x7b, 0xc9, + 0x10, 0xd6, 0xbc, 0x50, 0xd1, 0x82, 0x75, 0x55, 0x25, 0xda, 0xf6, 0x33, 0x1e, 0x75, 0xd9, 0x26, + 0xa5, 0x42, 0xc1, 0xee, 0x25, 0x63, 0xcd, 0xca, 0x83, 0x44, 0x07, 0x2e, 0xab, 0x42, 0xe8, 0x7c, + 0x20, 0xe3, 0x4c, 0x9f, 0x1f, 0xc5, 0xac, 0x20, 0xb1, 0x7b, 0xc9, 0x58, 0xb7, 0xe6, 0xa4, 0x8b, + 0x3d, 0x55, 0x9f, 0xac, 0xe4, 0xca, 0x2b, 0xf1, 0xf6, 0xc2, 0x6e, 0x4a, 0xc5, 0xe8, 0xa4, 0x66, + 0x19, 0xc9, 0xfa, 0x97, 0xaa, 0xb3, 0xf2, 0xce, 0xc3, 0xf2, 0x38, 0xe9, 0xf5, 0xb9, 0x12, 0xb3, + 0xbe, 0xc3, 0x49, 0x8f, 0xe5, 0xa0, 0xa9, 0x3f, 0xc7, 0x0d, 0x03, 0xae, 0x2e, 0x66, 0x5a, 0xd9, + 0x9d, 0xab, 0xc4, 0x3b, 0x97, 0x9e, 0xdd, 0xb9, 0x66, 0xc3, 0xe2, 0x67, 0xf6, 0xb1, 0x5f, 0xc0, + 0x6a, 0x8e, 0x6b, 0x8b, 0x1a, 0x94, 0xd5, 0x8b, 0xe2, 0x14, 0x6b, 0xa4, 0xd5, 0xdf, 0xff, 0x46, + 0x2b, 0x20, 0xb8, 0xd3, 0x1b, 0x0c, 0x9b, 0xbd, 0xa1, 0xb6, 0xc4, 0x89, 0xfd, 0x6e, 0xb3, 0xd5, + 0xd6, 0x8a, 0xfa, 0xdf, 0x2f, 0x42, 0x35, 0x39, 0x6d, 0xfc, 0xcd, 0x6d, 0x82, 0x8b, 0xaf, 0xc4, + 0xcf, 0x28, 0xbc, 0x7c, 0x97, 0xa0, 0xa4, 0x62, 0x60, 0x67, 0xd5, 0xca, 0x68, 0x3e, 0xc8, 0xe9, + 0xf2, 0x8f, 0x0c, 0x72, 0x9a, 0xbd, 0x0b, 0xb6, 0x92, 0xbf, 0x0b, 0x36, 0xf3, 0x00, 0x7f, 0x99, + 0xde, 0x6e, 0xce, 0x3e, 0xc0, 0x7f, 0x15, 0x56, 0xdc, 0xe8, 0x99, 0xeb, 0x9c, 0x49, 0xcf, 0x78, + 0x99, 0xca, 0x6f, 0xfa, 0xf0, 0x8a, 0x4d, 0xff, 0xc7, 0x6c, 0x20, 0x0f, 0x61, 0xe3, 0xe8, 0x34, + 0x79, 0x31, 0x3a, 0x35, 0x31, 0xd5, 0xa9, 0x4a, 0x0b, 0x71, 0xe2, 0x1d, 0x58, 0x89, 0xd5, 0x41, + 0x68, 0x71, 0xc1, 0xd9, 0xb0, 0x21, 0xd1, 0xfa, 0x1f, 0x16, 0x00, 0xd2, 0x73, 0xb8, 0xbf, 0xb0, + 0x41, 0x3b, 0x63, 0x33, 0x2c, 0xfe, 0x80, 0xcd, 0xf0, 0x55, 0x0f, 0x02, 0x7d, 0x07, 0xd5, 0xe4, + 0xe4, 0xf5, 0x37, 0x9f, 0x58, 0x3f, 0xe9, 0x93, 0xff, 0x9c, 0x32, 0xee, 0x27, 0x47, 0x97, 0x7f, + 0xd1, 0xbe, 0xc8, 0x7d, 0x7e, 0xb1, 0x2c, 0x97, 0x7e, 0xfe, 0x9c, 0x2d, 0xec, 0xc9, 0xc7, 0xff, + 0x92, 0x57, 0x53, 0x76, 0xa2, 0x97, 0x72, 0x13, 0x5d, 0x9f, 0xca, 0x63, 0x82, 0xbf, 0xf8, 0xa7, + 0x7f, 0x52, 0x83, 0xff, 0x8f, 0x82, 0xb2, 0x65, 0x27, 0x8f, 0x7c, 0xbf, 0x54, 0x8e, 0x5e, 0x6c, + 0x8e, 0xff, 0x29, 0x9f, 0xfb, 0x41, 0x4b, 0x5d, 0xe9, 0x87, 0x2c, 0x75, 0xef, 0xa8, 0x37, 0x12, + 0x96, 0x5f, 0x66, 0xa5, 0x93, 0xcf, 0x24, 0xdc, 0x86, 0x5a, 0xd6, 0x36, 0xc7, 0x46, 0x19, 0x36, + 0xaa, 0xb0, 0x59, 0x4e, 0x97, 0x7a, 0x03, 0xb7, 0x77, 0x43, 0x95, 0x2b, 0x63, 0x03, 0x51, 0x42, + 0xff, 0x6b, 0xcc, 0x51, 0x7f, 0xd3, 0x2e, 0xf9, 0x61, 0x33, 0x8d, 0xfe, 0xff, 0x16, 0x60, 0x35, + 0xe7, 0x49, 0xf1, 0x1b, 0x7c, 0x62, 0x21, 0x5f, 0x2e, 0xfe, 0xff, 0x88, 0x2f, 0xe7, 0x1c, 0xc6, + 0x2b, 0x79, 0x87, 0x71, 0x64, 0x77, 0xf5, 0x9c, 0x56, 0xb4, 0x48, 0x7f, 0x2a, 0x2c, 0xd4, 0x9f, + 0x6e, 0x01, 0x58, 0x23, 0xba, 0x87, 0xd6, 0xd9, 0x66, 0xff, 0xec, 0x55, 0x23, 0x03, 0x11, 0x5f, + 0xc0, 0x75, 0x69, 0x34, 0xe1, 0xfe, 0x09, 0x8e, 0x4c, 0x85, 0xb5, 0xa5, 0x11, 0xe2, 0x2a, 0x13, + 0xd0, 0xe8, 0xf6, 0x8f, 0x9a, 0x0a, 0xab, 0x77, 0x60, 0x35, 0xe7, 0xa2, 0x82, 0x7b, 0xcb, 0xb1, + 0x17, 0x1c, 0x5a, 0x9e, 0xac, 0x8b, 0x4c, 0x89, 0x3b, 0xb0, 0x7c, 0x76, 0xe2, 0x84, 0xce, 0x82, + 0xa7, 0x6c, 0x19, 0xa1, 0xff, 0x0c, 0xea, 0x59, 0x77, 0x39, 0xf1, 0x3e, 0x2c, 0xbb, 0xb1, 0x33, + 0x56, 0xe6, 0xa0, 0xab, 0xf3, 0x1e, 0x75, 0x9d, 0xd8, 0x19, 0x1b, 0x4c, 0xa4, 0xff, 0x51, 0x01, + 0xb4, 0x59, 0x1c, 0x85, 0xb7, 0xb9, 0x88, 0x62, 0x67, 0xac, 0x2a, 0xc3, 0xa9, 0x4c, 0x25, 0x97, + 0x72, 0x95, 0x54, 0x56, 0xb3, 0x62, 0xc6, 0x6a, 0x76, 0x47, 0x09, 0x25, 0xf3, 0x57, 0x94, 0x18, + 0x21, 0xde, 0x86, 0x4a, 0xe8, 0x50, 0xe0, 0x1c, 0x7b, 0xc1, 0xed, 0xb8, 0x04, 0xa7, 0xff, 0x41, + 0x01, 0xca, 0xd2, 0xb7, 0x6f, 0xa1, 0x7d, 0xee, 0x5d, 0x28, 0x47, 0xa3, 0x13, 0x67, 0x6c, 0xa9, + 0x77, 0x81, 0xe6, 0xfc, 0xba, 0x15, 0x5e, 0xdc, 0x62, 0x8f, 0xc7, 0xbc, 0xbd, 0x6e, 0xdf, 0xb3, + 0x7c, 0x83, 0xe0, 0x38, 0xd5, 0xd8, 0x4e, 0xcb, 0x57, 0x8d, 0xf8, 0x1d, 0x06, 0x20, 0x10, 0x5d, + 0x26, 0xd2, 0x7f, 0x1b, 0xca, 0xd2, 0x77, 0x70, 0x61, 0x55, 0x6e, 0x41, 0xc9, 0x0a, 0x8f, 0xa3, + 0x05, 0x83, 0x45, 0x70, 0xfd, 0x0e, 0x40, 0xea, 0x4c, 0xb8, 0xa8, 0x04, 0xfd, 0x3e, 0x54, 0x94, + 0xff, 0x20, 0xce, 0xbf, 0xf4, 0xd3, 0xf2, 0x72, 0x70, 0xb6, 0x32, 0x9e, 0x7c, 0x9e, 0xbe, 0x1b, + 0x8c, 0x4e, 0xe9, 0xc8, 0xe0, 0x43, 0xa0, 0x9b, 0xd2, 0xc3, 0xb9, 0x07, 0x2b, 0x12, 0x32, 0xbe, + 0x7c, 0xa0, 0x88, 0xc4, 0x7d, 0x48, 0xf8, 0xe5, 0xab, 0xac, 0x15, 0x7a, 0x53, 0xdd, 0xa9, 0xa7, + 0x59, 0xf6, 0x48, 0x5a, 0x8f, 0xbb, 0xf4, 0xf2, 0x5a, 0x21, 0xfb, 0x8c, 0x6b, 0xae, 0x4e, 0x46, + 0x86, 0x4c, 0x6f, 0x40, 0x3d, 0xeb, 0xf4, 0xa4, 0x37, 0x61, 0x7d, 0xcf, 0x89, 0x2d, 0xe4, 0x3f, + 0x2a, 0x8c, 0x3f, 0xcf, 0x5f, 0xfc, 0x91, 0x9f, 0xbf, 0xb3, 0x74, 0x06, 0x13, 0xe9, 0x7f, 0xbf, + 0x04, 0xda, 0x2c, 0xee, 0x87, 0xe2, 0x0b, 0xdc, 0x86, 0x1a, 0x3f, 0xd9, 0x93, 0xb5, 0x24, 0x02, + 0x83, 0x32, 0xb7, 0x4f, 0xf8, 0x39, 0x9b, 0xf4, 0xe1, 0xfe, 0x5d, 0x4a, 0x8b, 0x6b, 0x7c, 0x99, + 0xdc, 0x0b, 0x46, 0x34, 0xad, 0xeb, 0x74, 0x77, 0xbc, 0x1b, 0x8c, 0x28, 0x6c, 0x81, 0x34, 0x78, + 0xb0, 0x27, 0x6e, 0xdd, 0xa8, 0x48, 0x2b, 0x07, 0x9d, 0x62, 0xca, 0x3b, 0x29, 0x71, 0x24, 0x63, + 0x8b, 0x55, 0x18, 0x30, 0x8c, 0xd4, 0x23, 0xe4, 0x23, 0x3f, 0x96, 0x51, 0x73, 0x56, 0xc2, 0xe0, + 0xac, 0xe5, 0x53, 0xd4, 0x02, 0x7a, 0x02, 0x14, 0x31, 0x95, 0xf4, 0x19, 0x78, 0x44, 0x51, 0xa8, + 0x1a, 0x8e, 0xb3, 0xca, 0x0f, 0x70, 0x55, 0xe5, 0xcb, 0x6b, 0x12, 0x98, 0xbc, 0x6f, 0xc8, 0xfb, + 0x5f, 0x26, 0x1e, 0x11, 0x30, 0x88, 0x08, 0xae, 0x43, 0xe5, 0xfb, 0xc0, 0x77, 0xc8, 0x70, 0x52, + 0xa3, 0x5a, 0x95, 0x31, 0xbd, 0x67, 0x4d, 0x70, 0x23, 0xf0, 0x28, 0x8c, 0x1d, 0xdf, 0xba, 0xe4, + 0x84, 0xfe, 0xdf, 0x17, 0x60, 0x63, 0xb6, 0xaf, 0x69, 0x1a, 0xd5, 0xa1, 0xd2, 0xea, 0x77, 0xcd, + 0x5e, 0x73, 0xaf, 0xad, 0x5d, 0x12, 0x6b, 0x50, 0xeb, 0x6f, 0x7d, 0xd5, 0x6e, 0x0d, 0x19, 0x50, + 0xa0, 0x88, 0x9a, 0x03, 0x73, 0xb7, 0xb3, 0xbd, 0xdd, 0xee, 0xb1, 0x46, 0xd1, 0xdf, 0xfa, 0xca, + 0xec, 0xf6, 0x5b, 0x5a, 0x11, 0x71, 0xca, 0x1d, 0x64, 0xa0, 0x95, 0xc8, 0x59, 0x84, 0x2e, 0x05, + 0x60, 0x72, 0x99, 0xbd, 0xdd, 0x9f, 0x0f, 0xcc, 0x56, 0x6f, 0xa8, 0xad, 0x60, 0xaa, 0x77, 0xd0, + 0xed, 0x52, 0x8a, 0xdc, 0x5a, 0x5b, 0xfd, 0xbd, 0x7d, 0xa3, 0x3d, 0x18, 0x98, 0x83, 0xce, 0xaf, + 0xda, 0x5a, 0x85, 0xbe, 0x6c, 0x74, 0x9e, 0x74, 0x7a, 0x0c, 0xa8, 0x8a, 0x32, 0x14, 0xf7, 0x3a, + 0x3d, 0x8e, 0x24, 0xba, 0xd7, 0xfc, 0x5a, 0xab, 0xe1, 0x8f, 0xc1, 0xc1, 0x9e, 0x56, 0x17, 0x55, + 0x58, 0xee, 0xb6, 0x9f, 0xb5, 0xbb, 0xda, 0xaa, 0xfe, 0x4f, 0x8b, 0x4a, 0x22, 0x26, 0x7f, 0xaf, + 0x1f, 0x23, 0x05, 0x2e, 0x3a, 0x65, 0x4d, 0x3a, 0xad, 0x98, 0xe9, 0x34, 0xf1, 0x06, 0xd4, 0xe5, + 0xa6, 0x90, 0x3d, 0x27, 0xab, 0x49, 0x18, 0x4d, 0xb9, 0x37, 0x61, 0x35, 0x71, 0x91, 0xc8, 0x3c, + 0x1c, 0x5e, 0x57, 0xc0, 0x05, 0xc7, 0x35, 0x2b, 0x0b, 0x8e, 0x6b, 0x26, 0x6e, 0x8c, 0x8a, 0x3b, + 0xf2, 0x5c, 0x9e, 0x49, 0x55, 0x84, 0x3c, 0x23, 0x5e, 0xfb, 0x1a, 0x50, 0xc2, 0x9c, 0xfa, 0xf2, + 0xc9, 0xa3, 0xaa, 0x51, 0x41, 0xc0, 0x81, 0xef, 0xc6, 0xb3, 0x2e, 0x1a, 0xd5, 0x39, 0x17, 0x8d, + 0xec, 0xe6, 0x0c, 0xf9, 0xcd, 0xf9, 0xf5, 0x64, 0x8f, 0x44, 0x64, 0x8d, 0xce, 0x2a, 0xaa, 0xc9, + 0x2e, 0x28, 0xde, 0x07, 0x31, 0x9a, 0x86, 0xf4, 0x84, 0x48, 0x86, 0x8c, 0x9f, 0x2a, 0xd2, 0x24, + 0x26, 0xd9, 0x15, 0x29, 0x92, 0x4b, 0x9e, 0x9a, 0xd4, 0xf3, 0xaa, 0xd1, 0xc8, 0x93, 0x8a, 0x07, + 0x70, 0x59, 0xce, 0xed, 0x5c, 0xdf, 0xca, 0x20, 0x10, 0x8c, 0x6a, 0xa6, 0x3d, 0xac, 0xff, 0x16, + 0x54, 0x94, 0x6b, 0xdf, 0x0f, 0x0b, 0xbb, 0x0b, 0xc6, 0x55, 0xff, 0xf7, 0x96, 0xa0, 0x9a, 0x38, + 0xfa, 0xfd, 0xa8, 0xd9, 0xf1, 0x1a, 0xca, 0xa9, 0xd1, 0x69, 0x96, 0xc5, 0x54, 0x10, 0xa0, 0x46, + 0x4a, 0xde, 0x0d, 0x9c, 0x86, 0xae, 0x92, 0xd8, 0x18, 0x72, 0x10, 0xba, 0x14, 0x27, 0xdc, 0xf5, + 0x33, 0xe1, 0x1a, 0xaa, 0x46, 0x05, 0x01, 0xb4, 0xd0, 0xae, 0x03, 0xfd, 0xa6, 0x9c, 0x3c, 0x49, + 0xca, 0x98, 0xc6, 0x7c, 0x57, 0x13, 0xfd, 0x4f, 0x86, 0x3a, 0x8b, 0x13, 0xc1, 0x42, 0xfa, 0x18, + 0xb1, 0x67, 0x85, 0x4c, 0xe1, 0x77, 0x28, 0x40, 0x10, 0xd5, 0x51, 0xce, 0x08, 0x04, 0xa8, 0x3a, + 0x66, 0x86, 0xab, 0x3a, 0x3b, 0xaa, 0xb3, 0x73, 0x1a, 0xe6, 0xe6, 0xb4, 0x1e, 0x43, 0x59, 0x3a, + 0x3b, 0xfe, 0x70, 0x87, 0xff, 0x60, 0x57, 0x69, 0x50, 0xb4, 0x3c, 0x75, 0xfb, 0x1e, 0x7f, 0xce, + 0x54, 0xac, 0x34, 0x53, 0x31, 0xfd, 0xdf, 0x5c, 0x02, 0x48, 0x9d, 0x26, 0xc5, 0x07, 0x33, 0x0e, + 0xda, 0x85, 0xb9, 0x6d, 0x7f, 0xc6, 0x2f, 0x7b, 0x26, 0x70, 0xfe, 0xd2, 0x8f, 0x08, 0x9c, 0xff, + 0x10, 0x56, 0xa3, 0x70, 0xf4, 0x4a, 0x1b, 0x7e, 0x2d, 0x0a, 0x47, 0x89, 0x09, 0xff, 0x43, 0xc0, + 0x24, 0x3d, 0xaa, 0x93, 0x2a, 0xaa, 0x73, 0x52, 0x4b, 0x35, 0x0a, 0x47, 0xfd, 0xc3, 0x6f, 0xb7, + 0xf9, 0x42, 0xa6, 0x1d, 0xc5, 0xe6, 0x22, 0x2e, 0xb1, 0x66, 0x47, 0xf1, 0x76, 0x96, 0x51, 0xdc, + 0x85, 0x06, 0xd2, 0xce, 0x31, 0x8b, 0xba, 0x1d, 0xa5, 0x07, 0x4a, 0xfa, 0xbf, 0xa4, 0x0e, 0xe6, + 0x67, 0xcc, 0xc3, 0xe2, 0x33, 0xa9, 0x88, 0x67, 0x84, 0x88, 0xcd, 0x45, 0xd6, 0x64, 0xbe, 0xb3, + 0x9d, 0x90, 0x8a, 0xc7, 0xb0, 0x9a, 0xe0, 0xb6, 0x9d, 0x23, 0x25, 0x03, 0xa9, 0x17, 0x5a, 0x32, + 0x28, 0x23, 0x4f, 0x88, 0x42, 0x51, 0xfa, 0xac, 0x11, 0xae, 0xc0, 0xe4, 0xd2, 0x52, 0x55, 0x5e, + 0x47, 0x7a, 0x0c, 0xd7, 0x5e, 0x62, 0x47, 0x53, 0xe7, 0x92, 0x32, 0x0e, 0x83, 0x0c, 0xea, 0xe6, + 0x3b, 0x67, 0x1c, 0x87, 0x41, 0xf7, 0xe1, 0xf2, 0x82, 0xf7, 0xc8, 0xc5, 0x1d, 0x72, 0xd9, 0x4a, + 0xdf, 0x5f, 0x97, 0xb2, 0xd5, 0x28, 0x7d, 0x74, 0xfd, 0x53, 0x58, 0x73, 0x3c, 0xf7, 0xd8, 0x3d, + 0x74, 0x29, 0xd6, 0xf8, 0x28, 0xf0, 0x16, 0xbd, 0xce, 0x6f, 0x34, 0x32, 0x44, 0xad, 0xc0, 0xd3, + 0xff, 0x95, 0x42, 0xee, 0x71, 0x41, 0xf5, 0xbc, 0x18, 0x3f, 0x2e, 0x28, 0x5f, 0x31, 0x2e, 0x18, + 0xf4, 0x1b, 0x57, 0xa6, 0x7c, 0xb8, 0x56, 0x3e, 0x16, 0xc6, 0x29, 0xf1, 0x21, 0x6c, 0xc8, 0x78, + 0xe9, 0x14, 0xad, 0xcc, 0x9c, 0x38, 0xa1, 0x19, 0x06, 0x67, 0x34, 0xb1, 0x0a, 0x86, 0x8c, 0xa5, + 0x4e, 0x41, 0xcb, 0xf6, 0x9d, 0xd0, 0x08, 0xce, 0xc4, 0x26, 0x49, 0x25, 0xce, 0x28, 0x56, 0x67, + 0xcc, 0x2a, 0xa9, 0xff, 0x2e, 0x5c, 0x5e, 0x70, 0x2d, 0x50, 0xbc, 0xa5, 0xf4, 0xbc, 0x97, 0x5c, + 0x02, 0x5e, 0xa8, 0x5b, 0x2e, 0xcd, 0xea, 0x96, 0x7f, 0x5c, 0x80, 0x9a, 0x7a, 0x96, 0xcf, 0xe0, + 0x40, 0x81, 0xea, 0xf5, 0xbe, 0x34, 0xd2, 0x12, 0x28, 0x50, 0xc7, 0x46, 0xd5, 0x33, 0x74, 0x5e, + 0xb8, 0x91, 0xf2, 0xb6, 0x2c, 0x19, 0x49, 0x5a, 0xbc, 0x07, 0xeb, 0x49, 0x68, 0xc2, 0x99, 0xb8, + 0x7a, 0x5a, 0x82, 0x50, 0x21, 0xd8, 0xe6, 0xb9, 0x40, 0x29, 0xcb, 0x9e, 0x66, 0xf6, 0xb3, 0xe5, + 0xd9, 0xfd, 0x4c, 0xff, 0x37, 0x0a, 0xb0, 0x39, 0xf8, 0xce, 0x93, 0x57, 0x1c, 0x3b, 0xe3, 0x89, + 0x47, 0x93, 0x89, 0x83, 0xc3, 0x7c, 0x0a, 0x57, 0x53, 0x77, 0x52, 0xf3, 0xc8, 0xf5, 0x8f, 0x9d, + 0x70, 0x12, 0xba, 0xd2, 0x11, 0xa1, 0x6e, 0x5c, 0x49, 0xb1, 0x3b, 0x29, 0x52, 0x3c, 0x05, 0x3d, + 0x72, 0xc6, 0x96, 0x1f, 0xbb, 0x23, 0x33, 0x93, 0x9f, 0x35, 0x8f, 0xa4, 0x45, 0x3c, 0xd2, 0xb7, + 0x15, 0x65, 0x7a, 0x75, 0x66, 0x40, 0x74, 0xb2, 0x81, 0xfa, 0x7f, 0x56, 0x84, 0xd7, 0xf6, 0x2f, + 0xe2, 0x93, 0xc0, 0x5f, 0x5c, 0xc7, 0x4d, 0x28, 0x9f, 0x58, 0xbe, 0xed, 0x25, 0x01, 0x48, 0x55, + 0x52, 0xdc, 0x80, 0x15, 0xde, 0x4b, 0x78, 0xbc, 0xb6, 0x96, 0x36, 0x0b, 0x86, 0x84, 0x10, 0xdb, + 0x3e, 0x74, 0x4d, 0xba, 0xe7, 0x63, 0x8d, 0x62, 0xb9, 0xf7, 0xd4, 0xac, 0x43, 0xb7, 0x25, 0x41, + 0xb8, 0x03, 0x53, 0x24, 0x79, 0x27, 0x4c, 0xaa, 0x2c, 0x1d, 0x7b, 0x24, 0x58, 0x0d, 0x01, 0x12, + 0x86, 0xb1, 0x7b, 0x64, 0x8d, 0x62, 0x15, 0x5d, 0x6e, 0x59, 0x12, 0x4a, 0xb0, 0x0c, 0x2e, 0xf7, + 0x01, 0x08, 0xc7, 0x7f, 0xe1, 0x86, 0x81, 0x4f, 0x91, 0x35, 0x24, 0x2d, 0xb3, 0xa4, 0xf5, 0x0c, + 0x46, 0x92, 0xdf, 0x86, 0x5a, 0x64, 0x9f, 0x26, 0x1f, 0x97, 0x91, 0x76, 0x23, 0xfb, 0x54, 0x7d, + 0xf8, 0x4b, 0xb8, 0xfe, 0xf2, 0xee, 0xe5, 0x28, 0x41, 0xd7, 0xec, 0xc5, 0xdd, 0x9a, 0xf0, 0x96, + 0x6a, 0xca, 0x5b, 0xc8, 0xd9, 0x84, 0x5e, 0xda, 0x0f, 0x3c, 0x77, 0x74, 0x21, 0xb7, 0x32, 0x40, + 0xd0, 0x3e, 0x41, 0x7e, 0x60, 0x3e, 0xb0, 0xfb, 0xe0, 0xe2, 0xf9, 0xa0, 0xff, 0x6f, 0xcb, 0x50, + 0x93, 0x83, 0xd7, 0xc2, 0x9d, 0x8b, 0xa3, 0x1f, 0x51, 0x2f, 0x27, 0xd5, 0xe5, 0xa0, 0x20, 0x6b, + 0x0a, 0xae, 0xaa, 0xf9, 0x09, 0xd4, 0x93, 0x85, 0x94, 0x46, 0x61, 0x50, 0x91, 0x71, 0xd3, 0x15, + 0x67, 0x24, 0xeb, 0x0d, 0x97, 0xdf, 0x0d, 0xa8, 0x78, 0x96, 0x7f, 0x3c, 0xb5, 0x8e, 0x13, 0xc7, + 0x4e, 0x95, 0x16, 0x1f, 0x43, 0xc3, 0x0a, 0x8f, 0xa7, 0x34, 0x02, 0xa9, 0x6a, 0x9b, 0x8f, 0xbf, + 0xb1, 0xaa, 0x28, 0x38, 0x6c, 0xc6, 0xc7, 0x50, 0xe3, 0xbb, 0xcb, 0x2c, 0x89, 0x2c, 0xbf, 0xc4, + 0xc3, 0x05, 0x98, 0x88, 0xb6, 0x80, 0x5b, 0x00, 0x2f, 0x02, 0xcf, 0x8a, 0x89, 0x1b, 0xca, 0x21, + 0xce, 0x40, 0x66, 0xbb, 0xba, 0x3c, 0xd7, 0xd5, 0x0f, 0xd9, 0xa3, 0x99, 0x4f, 0xbd, 0xe4, 0x91, + 0xf3, 0xcb, 0xd6, 0xe9, 0xee, 0x25, 0xf2, 0x79, 0x16, 0xbf, 0x05, 0x2b, 0x13, 0x5a, 0x29, 0xf2, + 0xc0, 0xeb, 0x0d, 0xb9, 0xe1, 0xbc, 0x7c, 0xf5, 0xec, 0x5e, 0x32, 0x64, 0x16, 0xf1, 0x33, 0xbe, + 0xf5, 0xe6, 0x9c, 0xc7, 0x32, 0xe6, 0xf2, 0xad, 0xdc, 0xe5, 0x67, 0x1c, 0xb8, 0x07, 0x2d, 0x26, + 0xa0, 0xc3, 0x17, 0x5a, 0x4e, 0x2a, 0x0b, 0x39, 0xcd, 0x58, 0x9e, 0x17, 0xb9, 0xb1, 0xa3, 0x84, + 0xdf, 0xaa, 0x01, 0x0a, 0xd4, 0xa1, 0xa8, 0xd0, 0x63, 0xeb, 0xc2, 0x74, 0xc2, 0x30, 0x08, 0x65, + 0x8c, 0x90, 0xca, 0xd8, 0xba, 0x68, 0x63, 0x1a, 0xa5, 0xfe, 0xc8, 0x19, 0x4d, 0x43, 0xdc, 0x5e, + 0x68, 0x56, 0xb2, 0xa8, 0x5b, 0x57, 0x40, 0xba, 0x76, 0x7b, 0x13, 0xaa, 0x9e, 0x63, 0x9d, 0x4e, + 0xc2, 0x20, 0x38, 0x92, 0xe2, 0x6d, 0x0a, 0x10, 0x8f, 0xe0, 0x4a, 0xca, 0x34, 0xb3, 0x33, 0x93, + 0xfd, 0x4a, 0x37, 0x12, 0x64, 0x66, 0x62, 0xde, 0xf8, 0x12, 0xea, 0xd9, 0x26, 0xbd, 0xca, 0x13, + 0xa2, 0x9a, 0x39, 0x41, 0xda, 0xd2, 0xa0, 0xe1, 0xe6, 0xfa, 0x52, 0xff, 0x67, 0x45, 0xb8, 0xb2, + 0xf0, 0xaa, 0xf8, 0xdc, 0x2c, 0x2e, 0xfc, 0xe4, 0x59, 0xbc, 0x34, 0x33, 0x8b, 0x17, 0x2d, 0xa1, + 0xe2, 0xe2, 0x25, 0xf4, 0xf2, 0x45, 0x5b, 0xfa, 0x81, 0x45, 0xfb, 0x57, 0xc6, 0xd5, 0xf2, 0x2b, + 0xa3, 0xfc, 0xaa, 0x95, 0x51, 0x99, 0x5b, 0x19, 0xb9, 0x99, 0x54, 0x7d, 0xd5, 0x4c, 0x82, 0x57, + 0xcd, 0xa4, 0xda, 0x8f, 0x9e, 0x49, 0xf5, 0x97, 0xcf, 0xa4, 0xfb, 0xff, 0x7a, 0x01, 0xd6, 0x07, + 0x71, 0xe8, 0xfa, 0xc7, 0x5d, 0x37, 0x76, 0x42, 0xcb, 0xdb, 0x09, 0xc2, 0x31, 0xdd, 0xd4, 0x19, + 0x1a, 0x9d, 0xde, 0x13, 0xb3, 0xdb, 0x19, 0xb6, 0x8d, 0x66, 0xf2, 0xea, 0xc0, 0x3c, 0x62, 0xd8, + 0xfe, 0x7a, 0xa8, 0x15, 0xc4, 0x5d, 0xb8, 0x33, 0x83, 0xd8, 0xea, 0xf4, 0x9a, 0xc6, 0x37, 0x66, + 0xa7, 0x37, 0x34, 0xfa, 0xdb, 0x07, 0xad, 0xb6, 0xa1, 0x2d, 0x89, 0xab, 0x20, 0x66, 0xa8, 0x76, + 0xdb, 0x5f, 0x6b, 0xc5, 0x05, 0xf0, 0xad, 0xce, 0x50, 0x2b, 0xdd, 0xdf, 0x83, 0xcb, 0xcd, 0xe3, + 0xe3, 0xd0, 0x39, 0x46, 0xc5, 0x8d, 0x22, 0x5c, 0xab, 0xeb, 0x52, 0xcd, 0x27, 0x4f, 0xcc, 0x56, + 0xbf, 0xb7, 0xd3, 0x79, 0xa2, 0xaa, 0xf6, 0x06, 0xbc, 0x9e, 0x01, 0x3e, 0x31, 0xfa, 0x07, 0xfb, + 0x98, 0x68, 0x35, 0x87, 0x66, 0xdf, 0xd8, 0x6e, 0x1b, 0x5a, 0xe1, 0xfe, 0x1b, 0xb8, 0x6c, 0xd8, + 0xbc, 0x92, 0x3c, 0xd2, 0x10, 0xf8, 0x8e, 0x76, 0x49, 0x94, 0xa1, 0xd8, 0xfd, 0xfe, 0x13, 0xad, + 0x70, 0x5f, 0x87, 0xda, 0xe0, 0x64, 0x7a, 0x74, 0xe4, 0x39, 0x8a, 0x62, 0xd7, 0x8a, 0x4e, 0xd8, + 0x85, 0x9a, 0x9e, 0x04, 0xd6, 0x0a, 0xf7, 0xdf, 0x86, 0x55, 0x49, 0xb3, 0xe7, 0xc4, 0x27, 0x81, + 0x2d, 0x00, 0x56, 0x7a, 0x41, 0x38, 0xb6, 0x3c, 0x49, 0xe7, 0x4c, 0x23, 0xa4, 0xfb, 0x10, 0xae, + 0x64, 0xca, 0xda, 0x09, 0x42, 0x8a, 0xb4, 0xd7, 0xea, 0x51, 0x38, 0x05, 0x5a, 0x83, 0x1c, 0x18, + 0x60, 0xf7, 0xe2, 0x30, 0x74, 0x6d, 0xad, 0x70, 0xff, 0x31, 0x54, 0xd3, 0x57, 0xc7, 0xe9, 0x79, + 0x93, 0xad, 0xfe, 0x41, 0x6f, 0x9b, 0xde, 0x4f, 0xa0, 0x27, 0x4d, 0x5a, 0xdd, 0x83, 0x41, 0xe7, + 0x99, 0xb4, 0xc7, 0xb4, 0xbf, 0x56, 0xc9, 0xa5, 0xfb, 0x7f, 0x50, 0x50, 0xc1, 0x39, 0x55, 0xb5, + 0xbb, 0xfd, 0xe6, 0x36, 0x1b, 0x72, 0x92, 0x97, 0x5b, 0x86, 0x5b, 0x5a, 0x01, 0x01, 0x46, 0x7b, + 0x70, 0xd0, 0x1d, 0xaa, 0x47, 0x61, 0x1a, 0x00, 0x9d, 0x56, 0x7b, 0xab, 0x6d, 0x3c, 0x41, 0x82, + 0x22, 0xa6, 0xf7, 0xfa, 0xbd, 0x27, 0xfd, 0xed, 0x2d, 0x4c, 0xd3, 0x73, 0x30, 0xdb, 0xcd, 0x61, + 0x73, 0x30, 0x34, 0xda, 0xcd, 0x3d, 0x04, 0xd1, 0x8d, 0x65, 0xe5, 0x29, 0x3e, 0xdc, 0x62, 0xa3, + 0xce, 0xd3, 0xe6, 0xce, 0xd3, 0x26, 0xa6, 0xca, 0xf7, 0xff, 0xdb, 0x25, 0xd0, 0x66, 0x03, 0xb0, + 0x8a, 0xd7, 0xe0, 0x1a, 0x95, 0x6a, 0xee, 0x1b, 0xed, 0xed, 0x4e, 0x8b, 0xef, 0x9e, 0x3d, 0x6b, + 0x76, 0x3b, 0xdb, 0x3c, 0xab, 0x66, 0x91, 0xcd, 0xde, 0x36, 0x3f, 0x0c, 0x31, 0x8b, 0x68, 0xff, + 0xf2, 0xa0, 0xd9, 0xd5, 0x96, 0xc4, 0xeb, 0x70, 0x7d, 0x16, 0xd5, 0xeb, 0x0f, 0x25, 0xba, 0x28, + 0x36, 0x61, 0x63, 0x16, 0xdd, 0x6d, 0x0f, 0x06, 0x5a, 0x49, 0xdc, 0x82, 0x1b, 0x8b, 0x30, 0x32, + 0xe7, 0xf2, 0xa2, 0x9a, 0x3e, 0x21, 0xeb, 0x96, 0xa1, 0xad, 0xe0, 0x24, 0x7b, 0x09, 0x52, 0xe6, + 0x2f, 0xe3, 0x5c, 0x9e, 0x6f, 0xa9, 0x56, 0x59, 0xd8, 0x03, 0x03, 0x93, 0x9e, 0x01, 0xa9, 0x8a, + 0xdb, 0xf0, 0xda, 0x22, 0x64, 0x7f, 0xc8, 0x04, 0x70, 0xff, 0x1f, 0x15, 0x60, 0xf3, 0x65, 0x31, + 0xba, 0x84, 0x0e, 0xb7, 0x8c, 0x83, 0x1e, 0x3d, 0xea, 0xc2, 0xef, 0xcf, 0x98, 0x4f, 0xdb, 0xdf, + 0xcc, 0xbc, 0xbf, 0xf1, 0x3a, 0x5c, 0x5f, 0x40, 0x63, 0x34, 0x9f, 0x9b, 0xcf, 0x3e, 0xd6, 0x0a, + 0xe2, 0x3d, 0x78, 0x67, 0x01, 0x7a, 0xa7, 0xdb, 0x6f, 0x0e, 0xcd, 0x5f, 0xb5, 0x8d, 0xbe, 0xd9, + 0xea, 0xf6, 0x07, 0xed, 0x6d, 0x24, 0x5e, 0x12, 0x77, 0xe0, 0xe6, 0x02, 0xe2, 0x01, 0x3f, 0x19, + 0xf4, 0xec, 0x63, 0xad, 0x28, 0xde, 0x82, 0x37, 0x5e, 0x4e, 0xb1, 0x73, 0xd0, 0x25, 0xb2, 0xd2, + 0xfd, 0x5f, 0xc0, 0xe6, 0xcb, 0x02, 0x3b, 0xe0, 0x42, 0x6a, 0xed, 0x36, 0x29, 0x78, 0x46, 0x1d, + 0x2a, 0xbd, 0xbe, 0xc9, 0x29, 0x72, 0x6d, 0x30, 0xda, 0xdd, 0x36, 0xdd, 0x7e, 0xbc, 0xff, 0xeb, + 0x42, 0xc6, 0x80, 0xae, 0x2e, 0xe7, 0x27, 0x00, 0xb9, 0xba, 0xb3, 0x20, 0xc3, 0xb1, 0x6c, 0xad, + 0x80, 0xa3, 0x94, 0x03, 0x75, 0x83, 0x91, 0xe5, 0x69, 0x4b, 0x74, 0xcf, 0x51, 0xc1, 0x29, 0x54, + 0x8e, 0x56, 0xc4, 0xae, 0x4b, 0x60, 0xdd, 0xe0, 0x6c, 0x3f, 0x74, 0x03, 0x64, 0xc9, 0x8c, 0x2e, + 0xdd, 0xff, 0xe7, 0xa5, 0x37, 0x6e, 0x4e, 0xad, 0xc6, 0x0f, 0x34, 0x6d, 0x3b, 0x85, 0x91, 0x25, + 0x87, 0xe7, 0x3a, 0x99, 0xb1, 0x66, 0x10, 0x05, 0x9c, 0x1f, 0xea, 0x94, 0x6f, 0x16, 0xb9, 0x84, + 0x48, 0xc3, 0xa1, 0xfd, 0x6e, 0x0e, 0x59, 0xdc, 0x7a, 0xfa, 0x0f, 0xff, 0xec, 0x56, 0xe1, 0x4f, + 0xfe, 0xec, 0x56, 0xe1, 0x7f, 0xf9, 0xb3, 0x5b, 0x97, 0xfe, 0xfa, 0x9f, 0xdf, 0x2a, 0xfc, 0xd1, + 0x9f, 0xdf, 0x2a, 0xfc, 0xc9, 0x9f, 0xdf, 0x2a, 0xfc, 0x8f, 0x7f, 0x7e, 0xab, 0xf0, 0xab, 0x0f, + 0x8e, 0xdd, 0xf8, 0x64, 0x7a, 0xf8, 0x60, 0x14, 0x8c, 0x3f, 0x1c, 0x5b, 0x71, 0xe8, 0x9e, 0xb3, + 0x6d, 0x4d, 0x25, 0x7c, 0xe7, 0xc3, 0xc9, 0xe9, 0xf1, 0x87, 0x93, 0xc3, 0x0f, 0x71, 0x57, 0x3f, + 0x5c, 0x99, 0x84, 0x41, 0x1c, 0x3c, 0xfa, 0xff, 0x02, 0x00, 0x00, 0xff, 0xff, 0x9e, 0xe4, 0x62, + 0x46, 0xb0, 0xbc, 0x00, 0x00, } func (m *Type) Marshal() (dAtA []byte, err error) { @@ -32625,6 +32645,13 @@ func (m *RoutineCall) MarshalToSizedBuffer(dAtA []byte) (int, error) { i -= len(m.XXX_unrecognized) copy(dAtA[i:], m.XXX_unrecognized) } + if len(m.NamespaceFingerprint) > 0 { + i -= len(m.NamespaceFingerprint) + copy(dAtA[i:], m.NamespaceFingerprint) + i = encodeVarintPlan(dAtA, i, uint64(len(m.NamespaceFingerprint))) + i-- + dAtA[i] = 0x7a + } if m.Leakproof { i-- if m.Leakproof { @@ -32818,6 +32845,13 @@ func (m *RoutinePlanDependency) MarshalToSizedBuffer(dAtA []byte) (int, error) { i -= len(m.XXX_unrecognized) copy(dAtA[i:], m.XXX_unrecognized) } + if len(m.NamespaceFingerprint) > 0 { + i -= len(m.NamespaceFingerprint) + copy(dAtA[i:], m.NamespaceFingerprint) + i = encodeVarintPlan(dAtA, i, uint64(len(m.NamespaceFingerprint))) + i-- + dAtA[i] = 0x62 + } if m.Leakproof { i-- if m.Leakproof { @@ -39375,6 +39409,10 @@ func (m *RoutineCall) ProtoSize() (n int) { if m.Leakproof { n += 2 } + l = len(m.NamespaceFingerprint) + if l > 0 { + n += 1 + l + sovPlan(uint64(l)) + } if m.XXX_unrecognized != nil { n += len(m.XXX_unrecognized) } @@ -39452,6 +39490,10 @@ func (m *RoutinePlanDependency) ProtoSize() (n int) { if m.Leakproof { n += 2 } + l = len(m.NamespaceFingerprint) + if l > 0 { + n += 1 + l + sovPlan(uint64(l)) + } if m.XXX_unrecognized != nil { n += len(m.XXX_unrecognized) } @@ -77141,6 +77183,38 @@ func (m *RoutineCall) Unmarshal(dAtA []byte) error { } } m.Leakproof = bool(v != 0) + case 15: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field NamespaceFingerprint", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowPlan + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthPlan + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthPlan + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.NamespaceFingerprint = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipPlan(dAtA[iNdEx:]) @@ -77511,6 +77585,38 @@ func (m *RoutinePlanDependency) Unmarshal(dAtA []byte) error { } } m.Leakproof = bool(v != 0) + case 12: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field NamespaceFingerprint", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowPlan + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthPlan + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthPlan + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.NamespaceFingerprint = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipPlan(dAtA[iNdEx:]) diff --git a/pkg/sql/plan/function/udf.go b/pkg/sql/plan/function/udf.go index ae19eb8c94de0..1a5292a7c6033 100644 --- a/pkg/sql/plan/function/udf.go +++ b/pkg/sql/plan/function/udf.go @@ -34,11 +34,12 @@ type Udf struct { // FunctionID and Revision are resolved from the shared catalog. They are // part of the executable identity; the planner must never re-resolve a // Python routine by name or latest revision at execution time. - FunctionID int64 `json:"function_id,omitempty"` - AccountID uint64 `json:"account_id,omitempty"` - DatabaseID uint64 `json:"database_id,omitempty"` - Revision uint64 `json:"revision,omitempty"` - NamespaceVersion uint64 `json:"namespace_version,omitempty"` + FunctionID int64 `json:"function_id,omitempty"` + AccountID uint64 `json:"account_id,omitempty"` + DatabaseID uint64 `json:"database_id,omitempty"` + Revision uint64 `json:"revision,omitempty"` + NamespaceFingerprint string `json:"namespace_fingerprint,omitempty"` + NamespaceVersion uint64 `json:"namespace_version,omitempty"` // SQL revisions use the same immutable identity envelope as Python. These // fields are empty for legacy SQL rows, which intentionally remain on the // historical text-expansion path until they are explicitly replaced. @@ -335,7 +336,8 @@ func (u *Udf) GetRoutineCall() (*plan.RoutineCall, error) { } returnType := type2PlanType(body.ReturnType.Type()) return &plan.RoutineCall{ - ContractVersion: udf.PythonPlanContractVersion, + ContractVersion: udf.PythonPlanContractVersion, + NamespaceFingerprint: u.NamespaceFingerprint, FunctionRef: &plan.FunctionRef{ FunctionId: uint64(u.FunctionID), Revision: u.Revision, @@ -392,7 +394,8 @@ func (u *Udf) getSQLRoutineCall() (*plan.RoutineCall, error) { nullPolicy = udf.NullCallHandler } return &plan.RoutineCall{ - ContractVersion: udf.RoutinePlanContractVersion, + ContractVersion: udf.RoutinePlanContractVersion, + NamespaceFingerprint: u.NamespaceFingerprint, FunctionRef: &plan.FunctionRef{ FunctionId: uint64(u.FunctionID), Revision: u.Revision, diff --git a/pkg/sql/plan/routine_dependency.go b/pkg/sql/plan/routine_dependency.go index f80920cf8f2dd..31765cfcef242 100644 --- a/pkg/sql/plan/routine_dependency.go +++ b/pkg/sql/plan/routine_dependency.go @@ -60,6 +60,7 @@ func (builder *QueryBuilder) recordRoutinePlanDependency(call *planpb.RoutineCal DatabaseId: ref.DatabaseId, }, Language: call.Language, + NamespaceFingerprint: call.NamespaceFingerprint, ContractVersion: call.ContractVersion, DefinitionFingerprint: "", Volatility: call.Volatility, @@ -94,7 +95,8 @@ func routinePlanDependenciesEqual(left, right *planpb.RoutinePlanDependency) boo if left == nil || right == nil || left.FunctionRef == nil || right.FunctionRef == nil { return left == right } - return left.Language == right.Language && + return left.NamespaceFingerprint == right.NamespaceFingerprint && + left.Language == right.Language && left.ContractVersion == right.ContractVersion && left.DefinitionFingerprint == right.DefinitionFingerprint && left.ArtifactDigest == right.ArtifactDigest && diff --git a/pkg/udf/python/capability_lifecycle_test.go b/pkg/udf/python/capability_lifecycle_test.go new file mode 100644 index 0000000000000..04afe1e595458 --- /dev/null +++ b/pkg/udf/python/capability_lifecycle_test.go @@ -0,0 +1,198 @@ +// Copyright 2026 Matrix Origin +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. + +package python + +import ( + "context" + "encoding/json" + "errors" + "sync" + "testing" + "time" + + "github.com/apache/arrow-go/v18/arrow/flight/gen/flight" + "github.com/matrixorigin/matrixone/pkg/udf" + "github.com/matrixorigin/matrixone/pkg/udf/protocol" + "google.golang.org/grpc" +) + +type blockedCapabilityClient struct { + flight.FlightServiceClient + started chan struct{} + release chan struct{} + once sync.Once +} + +func (c *blockedCapabilityClient) DoAction(ctx context.Context, _ *flight.Action, _ ...grpc.CallOption) (flight.FlightService_DoActionClient, error) { + c.once.Do(func() { close(c.started) }) + select { + case <-c.release: + if err := ctx.Err(); err != nil { + return nil, err + } + return nil, errors.New("released test capability probe") + case <-ctx.Done(): + return nil, ctx.Err() + } +} + +func TestCancelledReadinessDoesNotWaitForOtherProbe(t *testing.T) { + gateway, err := NewGateway(ClientConfig{Enabled: true, AllowUnisolated: true, ServerAddress: "127.0.0.1:1", RequestTimeout: 30 * time.Second}) + if err != nil { + t.Fatal(err) + } + defer gateway.Close() + client := &blockedCapabilityClient{started: make(chan struct{}), release: make(chan struct{})} + gateway.flight = client + firstDone := make(chan error, 1) + go func() { firstDone <- gateway.CheckLanguageReady(context.Background(), udf.LanguagePython) }() + select { + case <-client.started: + case <-time.After(time.Second): + t.Fatal("first probe did not reach the Flight boundary") + } + ctx, cancel := context.WithCancel(context.Background()) + cancel() + secondStarted := make(chan struct{}) + secondDone := make(chan error, 1) + go func() { + close(secondStarted) + secondDone <- gateway.CheckLanguageReady(ctx, udf.LanguagePython) + }() + <-secondStarted + blocked := false + select { + case err := <-secondDone: + if !errors.Is(err, context.Canceled) { + t.Errorf("cancelled probe returned %v", err) + } + case <-time.After(200 * time.Millisecond): + blocked = true + } + close(client.release) + <-firstDone + if blocked { + if err := <-secondDone; !errors.Is(err, context.Canceled) { + t.Errorf("released cancelled probe returned %v", err) + } + t.Fatal("already-cancelled readiness blocked behind another request's capabilityMu/Flight probe; it returned only after the unrelated probe was released") + } +} + +func TestCapabilityInvalidationDoesNotWaitForProbe(t *testing.T) { + g, err := NewGateway(ClientConfig{Enabled: true, AllowUnisolated: true, ServerAddress: "127.0.0.1:1", RequestTimeout: time.Second}) + if err != nil { + t.Fatal(err) + } + defer g.Close() + c := &blockedCapabilityClient{started: make(chan struct{}), release: make(chan struct{})} + done := make(chan error, 1) + go func() { done <- g.ensureCapabilities(context.Background(), c, true) }() + <-c.started + invalidated := make(chan struct{}) + go func() { g.invalidateCapabilities(); close(invalidated) }() + select { + case <-invalidated: + case <-time.After(time.Second): + close(c.release) + <-done + t.Fatal("invalidation blocked on RPC") + } + if err := <-done; err == nil { + t.Fatal("invalidated probe succeeded") + } + g.mu.Lock() + defer g.mu.Unlock() + if g.capabilityReady || g.workerLeaseEpoch != 0 || g.capabilityProbe != nil { + t.Fatal("probe retained state") + } +} + +func TestStaleCapabilityFailurePreservesNewGeneration(t *testing.T) { + g := &Gateway{capabilityGeneration: 2, capabilityReady: true, workerLeaseEpoch: 42} + g.invalidateCapabilities(1) + if !g.capabilityReady || g.workerLeaseEpoch != 42 { + t.Fatal("old failure invalidated new lease") + } + g.invalidateCapabilities(2) + if g.capabilityReady || g.workerLeaseEpoch != 0 { + t.Fatal("current failure retained lease") + } +} + +func currentTestCapabilities() capabilityResponse { + response := capabilityResponse{ + ProtocolVersion: protocol.Version, + ABIContract: udf.PythonABIContract, + AdapterVersion: udf.PythonAdapterVersion, + SDKVersion: udf.PythonSDKVersion, + DefinitionSchemaVersion: udf.PythonDefinitionSchemaVersion, + PlanContractVersion: udf.PythonPlanContractVersion, + TypeDescriptorContract: udf.PythonTypeDescriptorContract, + TimezoneDatabaseVersion: currentTimezoneDatabaseVersion(), + Modes: []string{ModeScalar, ModeVector}, + NullPolicies: []string{NullCallHandler, NullReturnNull}, + WindowBatches: 1, + MaxExecutionFrameBytes: 1 << 30, + MaxHandlerProcesses: DefaultWorkerMaxHandlerProcesses, + MaxAccountHandlerProcesses: DefaultWorkerMaxAccountHandlers, + MaxOwnerHandlerProcesses: DefaultWorkerMaxOwnerHandlers, + } + response.LeaseEpoch = 42 + return response +} + +type delayedCapabilityClient struct { + flight.FlightServiceClient + started chan struct{} + release chan struct{} + response capabilityResponse +} + +func (c *delayedCapabilityClient) DoAction(ctx context.Context, _ *flight.Action, _ ...grpc.CallOption) (flight.FlightService_DoActionClient, error) { + close(c.started) + <-c.release // Deliberately ignore cancellation: exercise the publication fence. + payload, err := json.Marshal(c.response) + return &finishAckActionStream{result: &flight.Result{Body: payload}}, err +} +func TestCapabilityLateResponseCannotRepublish(t *testing.T) { + for _, closeGateway := range []bool{false, true} { + t.Run(map[bool]string{false: "invalidate", true: "close"}[closeGateway], func(t *testing.T) { + g, err := NewGateway(ClientConfig{Enabled: true, AllowUnisolated: true, ServerAddress: "127.0.0.1:1", RequestTimeout: time.Second}) + if err != nil { + t.Fatal(err) + } + defer g.Close() + c := &delayedCapabilityClient{started: make(chan struct{}), release: make(chan struct{}), response: currentTestCapabilities()} + done := make(chan error, 1) + go func() { done <- g.ensureCapabilities(context.Background(), c, true) }() + <-c.started + // Close/invalidation must return while a misbehaving transport is blocked. + if closeGateway { + if err := g.Close(); err != nil { + t.Fatal(err) + } + } else { + g.invalidateCapabilities() + } + g.mu.Lock() + occupied := g.capabilityProbe != nil + g.mu.Unlock() + if !occupied { + t.Error("cancelled physical probe released its slot too early") + } + close(c.release) + if err := <-done; err == nil { + t.Fatal("late success escaped generation fence") + } + g.mu.Lock() + defer g.mu.Unlock() + if g.capabilityReady || g.workerLeaseEpoch != 0 || g.capability != nil || g.capabilityProbe != nil { + t.Fatal("late response republished state") + } + }) + } +} diff --git a/pkg/udf/python/gateway.go b/pkg/udf/python/gateway.go index c619d59137d3e..ea79515ec6d47 100644 --- a/pkg/udf/python/gateway.go +++ b/pkg/udf/python/gateway.go @@ -59,23 +59,24 @@ var _ udf.RuntimeDefinitionValidator = (*Gateway)(nil) var _ udf.RuntimeStatusProvider = (*Gateway)(nil) type Gateway struct { - cfg ClientConfig - artifactResolver ArtifactResolver - allowInlineSource bool - conn *grpc.ClientConn - flight flight.FlightServiceClient - mu sync.Mutex - lifecycleCtx context.Context - lifecycleCancel context.CancelFunc - capabilityMu sync.Mutex - admissionMu sync.Mutex - capabilityReady bool - workerLeaseEpoch uint64 - capability *capabilityResponse - closed bool - active chan struct{} - ledger *protocol.TerminalLedger - ledgerTTL time.Duration + cfg ClientConfig + artifactResolver ArtifactResolver + allowInlineSource bool + conn *grpc.ClientConn + flight flight.FlightServiceClient + mu sync.Mutex + lifecycleCtx context.Context + lifecycleCancel context.CancelFunc + capabilityGeneration uint64 + capabilityProbe *capabilityProbe + admissionMu sync.Mutex + capabilityReady bool + workerLeaseEpoch uint64 + capability *capabilityResponse + closed bool + active chan struct{} + ledger *protocol.TerminalLedger + ledgerTTL time.Duration // admittedGroups and closedGroups are the Gateway's ownership fence for // the current one-member adapter. A group epoch may be admitted only once; // a terminal tombstone is reclaimable only after its exact group epoch has @@ -400,7 +401,8 @@ func (g *Gateway) ValidateDefinition(ctx context.Context, definition *udf.Routin // lease in its request. It must perform its own worker-instance handshake; // relying on a cached readiness result could validate against a replacement // worker with a different contract. - if err := g.ensureCapabilities(requestCtx, client, true); err != nil { + capability, err := g.ensureCapabilitySnapshot(requestCtx, client, true) + if err != nil { return err } payload, err := json.Marshal(definitionValidationPayload{ @@ -430,33 +432,33 @@ func (g *Gateway) ValidateDefinition(ctx context.Context, definition *udf.Routin } stream, err := client.DoAction(actionCtx, &flight.Action{Type: "ValidatePythonDefinition", Body: payload}) if err != nil { - g.invalidateCapabilities() + g.invalidateCapabilities(capability.generation) return fmt.Errorf("python udf: definition validation action: %w", err) } result, err := stream.Recv() if err != nil { - g.invalidateCapabilities() + g.invalidateCapabilities(capability.generation) return fmt.Errorf("python udf: definition validation response: %w", err) } if result == nil { - g.invalidateCapabilities() + g.invalidateCapabilities(capability.generation) return fmt.Errorf("python udf: empty definition validation response") } var response definitionValidationResponse decoder := json.NewDecoder(bytes.NewReader(result.Body)) decoder.DisallowUnknownFields() if err := decoder.Decode(&response); err != nil { - g.invalidateCapabilities() + g.invalidateCapabilities(capability.generation) return fmt.Errorf("python udf: decode definition validation response: %w", err) } var trailing any if err := decoder.Decode(&trailing); err != io.EOF { - g.invalidateCapabilities() + g.invalidateCapabilities(capability.generation) return fmt.Errorf("python udf: definition validation response has trailing JSON") } if response.Status == "ERROR" { if _, err := stream.Recv(); err != io.EOF { - g.invalidateCapabilities() + g.invalidateCapabilities(capability.generation) if err == nil { return fmt.Errorf("python udf: definition validation returned multiple results") } @@ -469,11 +471,11 @@ func (g *Gateway) ValidateDefinition(ctx context.Context, definition *udf.Routin } if response.Status != statusOK || response.ArtifactDigest != definition.ArtifactDigest || response.DefinitionFingerprint != definition.DefinitionFingerprint { - g.invalidateCapabilities() + g.invalidateCapabilities(capability.generation) return fmt.Errorf("UNSUPPORTED_ROUTINE_VERSION: Python definition validation returned an invalid result") } if _, err := stream.Recv(); err != io.EOF { - g.invalidateCapabilities() + g.invalidateCapabilities(capability.generation) if err == nil { return fmt.Errorf("python udf: definition validation returned multiple results") } @@ -524,10 +526,19 @@ func (g *Gateway) Close() error { // released, no caller can pass the admission check after Close returns. g.admissionMu.Lock() g.closed = true + g.capabilityGeneration++ + g.capabilityReady, g.workerLeaseEpoch, g.capability = false, 0, nil + var probeCancel context.CancelFunc + if g.capabilityProbe != nil { + probeCancel = g.capabilityProbe.cancel + } conn := g.conn g.conn = nil g.admissionMu.Unlock() g.mu.Unlock() + if probeCancel != nil { + probeCancel() + } if conn == nil { return nil } @@ -684,7 +695,8 @@ func (g *Gateway) Execute(ctx context.Context, invocation *udf.Invocation, resul if err != nil { return err } - if err := g.ensureCapabilities(streamCtx, client, false); err != nil { + capability, err := g.ensureCapabilitySnapshot(streamCtx, client, false) + if err != nil { return err } // The worker instance owns the lease epoch. Copy the invocation before @@ -692,9 +704,7 @@ func (g *Gateway) Execute(ctx context.Context, invocation *udf.Invocation, resul // another worker process. If the worker was restarted after the cached // handshake, the exchange fails with STALE_LEASE_EPOCH and the deferred // invalidation below forces a fresh handshake on the next call. - g.mu.Lock() - workerLeaseEpoch := g.workerLeaseEpoch - g.mu.Unlock() + workerLeaseEpoch := capability.response.LeaseEpoch if workerLeaseEpoch == 0 { return fmt.Errorf("python udf: capability handshake did not return a worker lease epoch") } @@ -704,7 +714,7 @@ func (g *Gateway) Execute(ctx context.Context, invocation *udf.Invocation, resul } defer func() { if err != nil { - g.invalidateCapabilities() + g.invalidateCapabilities(capability.generation) } }() openBody, err := json.Marshal(openPayload{ @@ -1232,97 +1242,139 @@ type capabilityResponse struct { // Gateway connection before OpenInvocation, and it caches only a successful // match. A worker with an unknown or different contract therefore cannot run // user code, while ordinary SQL remains independent of this client. +type capabilitySnapshot struct { + generation uint64 + response capabilityResponse +} + +type capabilityProbe struct { + generation uint64 + done chan struct{} + cancel context.CancelFunc + snapshot capabilitySnapshot + err error +} + func (g *Gateway) ensureCapabilities(ctx context.Context, client flight.FlightServiceClient, forceRefresh bool) error { - g.capabilityMu.Lock() - defer g.capabilityMu.Unlock() + _, err := g.ensureCapabilitySnapshot(ctx, client, forceRefresh) + return err +} + +// One caller owns the RPC. Joiners wait with their own deadline and cancellation; +// no network operation or wait holds mu. The owner alone retires the probe. +func (g *Gateway) ensureCapabilitySnapshot(ctx context.Context, client flight.FlightServiceClient, forceRefresh bool) (capabilitySnapshot, error) { + ctx, release := g.withLifecycleContext(ctx) + defer release() + ctx, cancel := context.WithTimeout(ctx, g.cfg.RequestTimeout) + defer cancel() + if err := ctx.Err(); err != nil { + return capabilitySnapshot{}, err + } g.mu.Lock() - if forceRefresh { - // Clear the old lease before probing. If the probe fails, callers must - // observe the worker as unavailable rather than retaining a stale - // success that could authorize a lease-less Flight Action. - g.capabilityReady = false - g.workerLeaseEpoch = 0 - g.capability = nil - } - ready := g.capabilityReady - closed := g.closed - g.mu.Unlock() - if closed { - return errGatewayClosed + if g.closed { + g.mu.Unlock() + return capabilitySnapshot{}, errGatewayClosed } - if ready { - g.mu.Lock() - leaseEpoch := g.workerLeaseEpoch + if !forceRefresh && g.capabilityReady && g.capability != nil && g.workerLeaseEpoch != 0 { + snapshot := capabilitySnapshot{g.capabilityGeneration, *g.capability} g.mu.Unlock() - if leaseEpoch != 0 { - return nil + return snapshot, ctx.Err() + } + if p := g.capabilityProbe; p != nil { + g.mu.Unlock() + select { + case <-ctx.Done(): + return capabilitySnapshot{}, ctx.Err() + case <-p.done: + if err := ctx.Err(); err != nil { + return capabilitySnapshot{}, err + } + return p.snapshot, p.err } - // A partially initialized Gateway must not treat readiness as a valid - // worker contract. Fall through and establish the complete capability - // record again. } + g.capabilityGeneration++ + g.capabilityReady, g.workerLeaseEpoch, g.capability = false, 0, nil + p := &capabilityProbe{generation: g.capabilityGeneration, done: make(chan struct{}), cancel: cancel} + g.capabilityProbe = p + g.mu.Unlock() + response, err := readCapabilities(ctx, client) + g.mu.Lock() + if g.closed { + err = errGatewayClosed + } else if p.generation != g.capabilityGeneration { + err = errors.New("python udf: capability probe invalidated") + } else if ctx.Err() != nil { + err = ctx.Err() + } + if err == nil { + // response is immutable after publication, including its decoded slices. + g.capabilityReady, g.workerLeaseEpoch, g.capability = true, response.LeaseEpoch, response + p.snapshot = capabilitySnapshot{p.generation, *response} + } + p.err = err + g.capabilityProbe = nil + close(p.done) + g.mu.Unlock() + return p.snapshot, p.err +} + +func readCapabilities(ctx context.Context, client flight.FlightServiceClient) (*capabilityResponse, error) { request, err := json.Marshal(capabilityRequest{ProtocolVersion: protocol.Version}) if err != nil { - return err + return nil, err } - actionCtx, cancel := context.WithTimeout(ctx, g.cfg.RequestTimeout) - defer cancel() - stream, err := client.DoAction(actionCtx, &flight.Action{Type: "GetPythonCapabilities", Body: request}) + stream, err := client.DoAction(ctx, &flight.Action{Type: "GetPythonCapabilities", Body: request}) if err != nil { - return fmt.Errorf("python udf: capability handshake: %w", err) + return nil, fmt.Errorf("python udf: capability handshake: %w", err) } result, err := stream.Recv() if err != nil { - return fmt.Errorf("python udf: capability handshake response: %w", err) + return nil, fmt.Errorf("python udf: capability handshake response: %w", err) } if result == nil { - return fmt.Errorf("python udf: empty capability handshake response") + return nil, fmt.Errorf("python udf: empty capability handshake response") } var response capabilityResponse decoder := json.NewDecoder(bytes.NewReader(result.Body)) decoder.DisallowUnknownFields() if err := decoder.Decode(&response); err != nil { - return fmt.Errorf("python udf: decode capability handshake: %w", err) + return nil, fmt.Errorf("python udf: decode capability handshake: %w", err) } var trailing any if err := decoder.Decode(&trailing); err != io.EOF { - return fmt.Errorf("python udf: capability handshake has trailing JSON") + return nil, fmt.Errorf("python udf: capability handshake has trailing JSON") } if err := validateCapabilities(response); err != nil { - return err + return nil, err } if _, err := stream.Recv(); err != io.EOF { if err == nil { - return fmt.Errorf("python udf: capability handshake returned multiple results") + return nil, fmt.Errorf("python udf: capability handshake returned multiple results") } - return fmt.Errorf("python udf: capability handshake stream: %w", err) - } - g.mu.Lock() - if !g.closed { - g.capabilityReady = true - g.workerLeaseEpoch = response.LeaseEpoch - capability := response - capability.Modes = append([]string(nil), response.Modes...) - capability.NullPolicies = append([]string(nil), response.NullPolicies...) - g.capability = &capability + return nil, fmt.Errorf("python udf: capability handshake stream: %w", err) } - g.mu.Unlock() - return nil + return &response, nil } -// invalidateCapabilities is called after a post-handshake execution error. -// In particular, a restarted worker keeps the same endpoint but advertises a -// new instance lease epoch; clearing the cached record makes the next -// invocation perform the handshake again. This never retries the failed -// invocation or changes its terminal outcome. -func (g *Gateway) invalidateCapabilities() { - g.capabilityMu.Lock() - defer g.capabilityMu.Unlock() +// Invalidating an old execution must not evict a newer worker handshake. Keep a +// cancelled probe in its physical slot until its owner exits, bounding RPCs even +// if the transport is slow to acknowledge cancellation. +func (g *Gateway) invalidateCapabilities(expected ...uint64) { g.mu.Lock() - g.capabilityReady = false - g.workerLeaseEpoch = 0 - g.capability = nil + if len(expected) != 0 && expected[0] != g.capabilityGeneration { + g.mu.Unlock() + return + } + g.capabilityGeneration++ + g.capabilityReady, g.workerLeaseEpoch, g.capability = false, 0, nil + var cancel context.CancelFunc + if g.capabilityProbe != nil { + cancel = g.capabilityProbe.cancel + } g.mu.Unlock() + if cancel != nil { + cancel() + } } func validateCapabilities(response capabilityResponse) error { diff --git a/pkg/udf/runtime_status_wire_test.go b/pkg/udf/runtime_status_wire_test.go new file mode 100644 index 0000000000000..ef52beffbf94e --- /dev/null +++ b/pkg/udf/runtime_status_wire_test.go @@ -0,0 +1,39 @@ +// Copyright 2026 Matrix Origin +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +package udf + +import ( + "encoding/base64" + "encoding/json" + "github.com/matrixorigin/matrixone/pkg/pb/query" + "github.com/stretchr/testify/require" + "os" + "testing" +) + +// The identical fixture is consumed by Operator's independent wire decoder. +// This prevents successful status alone from concealing fault-enum drift. +func TestPythonStatusWireContract(t *testing.T) { + expected := map[string][]string{ + "": {RuntimeStatusReasonReady}, RuntimeStatusDisabled: {RuntimeStatusReasonRuntimeDisabled}, RuntimeStatusNotAllowed: {RuntimeStatusReasonUnisolatedNotAllowed}, + RuntimeStatusUnavailable: {RuntimeStatusReasonWorkerUnavailable, RuntimeStatusReasonStatusUnavailable}, RuntimeStatusContractMismatch: {RuntimeStatusReasonCapabilityMismatch}, RuntimeStatusTimeout: {RuntimeStatusReasonRequestTimeout}, RuntimeStatusClosed: {RuntimeStatusReasonRuntimeClosed}, RuntimeStatusInvalid: {RuntimeStatusReasonInvalidLanguage}, RuntimeStatusInternal: {RuntimeStatusReasonRuntimeError}, + } + raw, err := os.ReadFile("testdata/python_status_wire.json") + require.NoError(t, err) + var rows []struct{ Class, Reason, Wire string } + require.NoError(t, json.Unmarshal(raw, &rows)) + seen := map[string][]string{} + for _, row := range rows { + wire, err := base64.StdEncoding.DecodeString(row.Wire) + require.NoError(t, err) + var response query.Response + require.NoError(t, response.Unmarshal(wire)) + require.Equal(t, query.CmdMethod_GetPythonUdfStatus, response.CmdMethod) + require.Equal(t, row.Class, response.GetPythonUdfStatus.ErrorClass) + require.Equal(t, row.Reason, response.GetPythonUdfStatus.Reason) + require.Equal(t, row.Class == "", response.GetPythonUdfStatus.Ready) + seen[row.Class] = append(seen[row.Class], row.Reason) + } + require.Equal(t, expected, seen) +} diff --git a/pkg/udf/testdata/python_status_wire.json b/pkg/udf/testdata/python_status_wire.json new file mode 100644 index 0000000000000..6b35d431092a8 --- /dev/null +++ b/pkg/udf/testdata/python_status_wire.json @@ -0,0 +1,52 @@ +[ + { + "Class": "", + "Reason": "CapabilityHandshakeReady", + "Wire": "CAcQKdoBAOoBAPIBAPoBAIICAIoCAJICAKICAMoCANICAOICMQoEY24tMRIGcHl0aG9uGAEgASgBOhhDYXBhYmlsaXR5SGFuZHNoYWtlUmVhZHmwASo=" + }, + { + "Class": "DISABLED", + "Reason": "RuntimeDisabled", + "Wire": "CAcQKdoBAOoBAPIBAPoBAIICAIoCAJICAKICAMoCANICAOICLgoEY24tMRIGcHl0aG9uIAEyCERJU0FCTEVEOg9SdW50aW1lRGlzYWJsZWSwASo=" + }, + { + "Class": "NOT_ALLOWED", + "Reason": "UnisolatedExecutionNotAllowed", + "Wire": "CAcQKdoBAOoBAPIBAPoBAIICAIoCAJICAKICAMoCANICAOICPwoEY24tMRIGcHl0aG9uGAEyC05PVF9BTExPV0VEOh1Vbmlzb2xhdGVkRXhlY3V0aW9uTm90QWxsb3dlZLABKg==" + }, + { + "Class": "UNAVAILABLE", + "Reason": "WorkerUnavailable", + "Wire": "CAcQKdoBAOoBAPIBAPoBAIICAIoCAJICAKICAMoCANICAOICNQoEY24tMRIGcHl0aG9uGAEgATILVU5BVkFJTEFCTEU6EVdvcmtlclVuYXZhaWxhYmxlsAEq" + }, + { + "Class": "UNAVAILABLE", + "Reason": "RuntimeStatusUnavailable", + "Wire": "CAcQKdoBAOoBAPIBAPoBAIICAIoCAJICAKICAMoCANICAOICPAoEY24tMRIGcHl0aG9uGAEgATILVU5BVkFJTEFCTEU6GFJ1bnRpbWVTdGF0dXNVbmF2YWlsYWJsZbABKg==" + }, + { + "Class": "CONTRACT_MISMATCH", + "Reason": "CapabilityContractMismatch", + "Wire": "CAcQKdoBAOoBAPIBAPoBAIICAIoCAJICAKICAMoCANICAOICRAoEY24tMRIGcHl0aG9uGAEgATIRQ09OVFJBQ1RfTUlTTUFUQ0g6GkNhcGFiaWxpdHlDb250cmFjdE1pc21hdGNosAEq" + }, + { + "Class": "TIMEOUT", + "Reason": "CapabilityRequestTimeout", + "Wire": "CAcQKdoBAOoBAPIBAPoBAIICAIoCAJICAKICAMoCANICAOICOAoEY24tMRIGcHl0aG9uGAEgATIHVElNRU9VVDoYQ2FwYWJpbGl0eVJlcXVlc3RUaW1lb3V0sAEq" + }, + { + "Class": "CLOSED", + "Reason": "RuntimeClosed", + "Wire": "CAcQKdoBAOoBAPIBAPoBAIICAIoCAJICAKICAMoCANICAOICLAoEY24tMRIGcHl0aG9uGAEgATIGQ0xPU0VEOg1SdW50aW1lQ2xvc2VksAEq" + }, + { + "Class": "INVALID", + "Reason": "InvalidLanguage", + "Wire": "CAcQKdoBAOoBAPIBAPoBAIICAIoCAJICAKICAMoCANICAOICLwoEY24tMRIGcHl0aG9uGAEgATIHSU5WQUxJRDoPSW52YWxpZExhbmd1YWdlsAEq" + }, + { + "Class": "INTERNAL", + "Reason": "RuntimeError", + "Wire": "CAcQKdoBAOoBAPIBAPoBAIICAIoCAJICAKICAMoCANICAOICLQoEY24tMRIGcHl0aG9uGAEgATIISU5URVJOQUw6DFJ1bnRpbWVFcnJvcrABKg==" + } +] diff --git a/proto/plan.proto b/proto/plan.proto index 69f6461c2d89a..db7a570b3bbbd 100644 --- a/proto/plan.proto +++ b/proto/plan.proto @@ -2450,6 +2450,9 @@ message RoutineCall { bool may_error = 12; string security_mode = 13; bool leakproof = 14; + // State fingerprint of the complete overload namespace at binding snapshot. + // Separate from the selected immutable FunctionRef; missing means no reuse. + string namespace_fingerprint = 15; } // RoutinePlanDependency is the shared cache/prepared-plan validation envelope @@ -2468,4 +2471,5 @@ message RoutinePlanDependency { bool may_error = 9; string security_mode = 10; bool leakproof = 11; + string namespace_fingerprint = 12; } diff --git a/test/distributed/cases/udf/overload_namespace.result b/test/distributed/cases/udf/overload_namespace.result new file mode 100644 index 0000000000000..ff683f260eaef --- /dev/null +++ b/test/distributed/cases/udf/overload_namespace.result @@ -0,0 +1,57 @@ +drop database if exists udf_sql_namespace_bvt; +create database udf_sql_namespace_bvt; +use udf_sql_namespace_bvt; +create table namespace_values(x smallint); +insert into namespace_values values (1),(2); +create function overload(x bigint) returns bigint language sql as '$1 + 100'; +select overload(x) as result from namespace_values order by x; +➤ result[-5,64,0] 𝄀 +101 𝄀 +102 +prepare namespace_plan from 'select overload(x) as result from namespace_values order by x'; +execute namespace_plan; +➤ result[-5,64,0] 𝄀 +101 𝄀 +102 +use udf_sql_namespace_bvt; +create function overload(x smallint) returns bigint language sql as '$1 + 200'; +execute namespace_plan; +➤ result[-5,64,0] 𝄀 +201 𝄀 +202 +select overload(x) as result from namespace_values order by x; +➤ result[-5,64,0] 𝄀 +201 𝄀 +202 +use udf_sql_namespace_bvt; +create or replace function overload(x smallint) returns bigint language sql as '$1 + 300'; +execute namespace_plan; +➤ result[-5,64,0] 𝄀 +301 𝄀 +302 +select overload(x) as result from namespace_values order by x; +➤ result[-5,64,0] 𝄀 +301 𝄀 +302 +use udf_sql_namespace_bvt; +drop function overload(smallint); +execute namespace_plan; +➤ result[-5,64,0] 𝄀 +101 𝄀 +102 +select overload(x) as result from namespace_values order by x; +➤ result[-5,64,0] 𝄀 +101 𝄀 +102 +use udf_sql_namespace_bvt; +create function overload(x smallint) returns bigint language sql as '$1 + 400'; +execute namespace_plan; +➤ result[-5,64,0] 𝄀 +401 𝄀 +402 +select overload(x) as result from namespace_values order by x; +➤ result[-5,64,0] 𝄀 +401 𝄀 +402 +deallocate prepare namespace_plan; +drop database udf_sql_namespace_bvt; diff --git a/test/distributed/cases/udf/overload_namespace.sql b/test/distributed/cases/udf/overload_namespace.sql new file mode 100644 index 0000000000000..fb6f89938b7b5 --- /dev/null +++ b/test/distributed/cases/udf/overload_namespace.sql @@ -0,0 +1,35 @@ +drop database if exists udf_sql_namespace_bvt; +create database udf_sql_namespace_bvt; +use udf_sql_namespace_bvt; +create table namespace_values(x smallint); +insert into namespace_values values (1),(2); +create function overload(x bigint) returns bigint language sql as '$1 + 100'; +select overload(x) as result from namespace_values order by x; +prepare namespace_plan from 'select overload(x) as result from namespace_values order by x'; +execute namespace_plan; +-- @session:id=1 +use udf_sql_namespace_bvt; +create function overload(x smallint) returns bigint language sql as '$1 + 200'; +-- @session +execute namespace_plan; +select overload(x) as result from namespace_values order by x; +-- @session:id=1 +use udf_sql_namespace_bvt; +create or replace function overload(x smallint) returns bigint language sql as '$1 + 300'; +-- @session +execute namespace_plan; +select overload(x) as result from namespace_values order by x; +-- @session:id=1 +use udf_sql_namespace_bvt; +drop function overload(smallint); +-- @session +execute namespace_plan; +select overload(x) as result from namespace_values order by x; +-- @session:id=1 +use udf_sql_namespace_bvt; +create function overload(x smallint) returns bigint language sql as '$1 + 400'; +-- @session +execute namespace_plan; +select overload(x) as result from namespace_values order by x; +deallocate prepare namespace_plan; +drop database udf_sql_namespace_bvt; diff --git a/test/distributed/cases/udf_python/overload_namespace.result b/test/distributed/cases/udf_python/overload_namespace.result new file mode 100644 index 0000000000000..22f90130768aa --- /dev/null +++ b/test/distributed/cases/udf_python/overload_namespace.result @@ -0,0 +1,57 @@ +drop database if exists udf_python_namespace_bvt; +create database udf_python_namespace_bvt; +use udf_python_namespace_bvt; +create table namespace_values(x smallint); +insert into namespace_values values (1),(2); +create function overload(x bigint) returns bigint language python as 'def overload(ctx,x): return x + 100' handler 'overload'; +select overload(x) as result from namespace_values order by x; +➤ result[-5,64,0] 𝄀 +101 𝄀 +102 +prepare namespace_plan from 'select overload(x) as result from namespace_values order by x'; +execute namespace_plan; +➤ result[-5,64,0] 𝄀 +101 𝄀 +102 +use udf_python_namespace_bvt; +create function overload(x smallint) returns bigint language python as 'def overload(ctx,x): return x + 200' handler 'overload'; +execute namespace_plan; +➤ result[-5,64,0] 𝄀 +201 𝄀 +202 +select overload(x) as result from namespace_values order by x; +➤ result[-5,64,0] 𝄀 +201 𝄀 +202 +use udf_python_namespace_bvt; +create or replace function overload(x smallint) returns bigint language python as 'def overload(ctx,x): return x + 300' handler 'overload'; +execute namespace_plan; +➤ result[-5,64,0] 𝄀 +301 𝄀 +302 +select overload(x) as result from namespace_values order by x; +➤ result[-5,64,0] 𝄀 +301 𝄀 +302 +use udf_python_namespace_bvt; +drop function overload(smallint); +execute namespace_plan; +➤ result[-5,64,0] 𝄀 +101 𝄀 +102 +select overload(x) as result from namespace_values order by x; +➤ result[-5,64,0] 𝄀 +101 𝄀 +102 +use udf_python_namespace_bvt; +create function overload(x smallint) returns bigint language python as 'def overload(ctx,x): return x + 400' handler 'overload'; +execute namespace_plan; +➤ result[-5,64,0] 𝄀 +401 𝄀 +402 +select overload(x) as result from namespace_values order by x; +➤ result[-5,64,0] 𝄀 +401 𝄀 +402 +deallocate prepare namespace_plan; +drop database udf_python_namespace_bvt; diff --git a/test/distributed/cases/udf_python/overload_namespace.sql b/test/distributed/cases/udf_python/overload_namespace.sql new file mode 100644 index 0000000000000..5a1634f84bd04 --- /dev/null +++ b/test/distributed/cases/udf_python/overload_namespace.sql @@ -0,0 +1,35 @@ +drop database if exists udf_python_namespace_bvt; +create database udf_python_namespace_bvt; +use udf_python_namespace_bvt; +create table namespace_values(x smallint); +insert into namespace_values values (1),(2); +create function overload(x bigint) returns bigint language python as 'def overload(ctx,x): return x + 100' handler 'overload'; +select overload(x) as result from namespace_values order by x; +prepare namespace_plan from 'select overload(x) as result from namespace_values order by x'; +execute namespace_plan; +-- @session:id=1 +use udf_python_namespace_bvt; +create function overload(x smallint) returns bigint language python as 'def overload(ctx,x): return x + 200' handler 'overload'; +-- @session +execute namespace_plan; +select overload(x) as result from namespace_values order by x; +-- @session:id=1 +use udf_python_namespace_bvt; +create or replace function overload(x smallint) returns bigint language python as 'def overload(ctx,x): return x + 300' handler 'overload'; +-- @session +execute namespace_plan; +select overload(x) as result from namespace_values order by x; +-- @session:id=1 +use udf_python_namespace_bvt; +drop function overload(smallint); +-- @session +execute namespace_plan; +select overload(x) as result from namespace_values order by x; +-- @session:id=1 +use udf_python_namespace_bvt; +create function overload(x smallint) returns bigint language python as 'def overload(ctx,x): return x + 400' handler 'overload'; +-- @session +execute namespace_plan; +select overload(x) as result from namespace_values order by x; +deallocate prepare namespace_plan; +drop database udf_python_namespace_bvt; From 83397d634a851f5c7f8de5ba483a8b21c7221b76 Mon Sep 17 00:00:00 2001 From: iamlinjunhong <1030420200@qq.com> Date: Sun, 20 Sep 2026 12:29:29 +0800 Subject: [PATCH 186/202] test(udf): supply NULL input for scalar control-flow assertion --- test/distributed/cases/udf_python/control_flow.result | 2 +- test/distributed/cases/udf_python/control_flow.sql | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/test/distributed/cases/udf_python/control_flow.result b/test/distributed/cases/udf_python/control_flow.result index 10afe7303cdae..f850f15cf9194 100644 --- a/test/distributed/cases/udf_python/control_flow.result +++ b/test/distributed/cases/udf_python/control_flow.result @@ -18,7 +18,7 @@ from control_values order by id; 1 ¦ 𝄀 2 ¦ <中😀> 𝄀 3 ¦ -select id, python_bvt_add_one(value) as plus_one +select id, python_bvt_add_one(case when id = 3 then null else value end) as plus_one from control_values order by id; ➤ id[4,32,0] ¦ plus_one[4,32,0] 𝄀 1 ¦ 2 𝄀 diff --git a/test/distributed/cases/udf_python/control_flow.sql b/test/distributed/cases/udf_python/control_flow.sql index 5b133dbdc9d70..9e3652cae5c93 100644 --- a/test/distributed/cases/udf_python/control_flow.sql +++ b/test/distributed/cases/udf_python/control_flow.sql @@ -22,7 +22,7 @@ select id, python_bvt_mark_null(label) as marked from control_values order by id; -- A scalar handler must be invoked once per row and preserve a handler NULL. -select id, python_bvt_add_one(value) as plus_one +select id, python_bvt_add_one(case when id = 3 then null else value end) as plus_one from control_values order by id; -- A zero-argument scalar still produces one result per input row. From ae740a858f156d6b90ec33e8556f1fbfd64b74f0 Mon Sep 17 00:00:00 2001 From: iamlinjunhong <1030420200@qq.com> Date: Sun, 20 Sep 2026 14:42:40 +0800 Subject: [PATCH 187/202] fix(udf): preserve exact DROP and failed initialization ownership --- pkg/frontend/authenticate.go | 6 +- pkg/frontend/python_udf_catalog_test.go | 23 +++ pkg/frontend/snapshot_test.go | 8 +- pkg/udf/python/worker/Dockerfile | 9 + pkg/udf/python/worker/test_worker.py | 180 +++++++++++++++++- pkg/udf/python/worker/worker.py | 50 +++-- .../issue_27103_clone_external_table.result | 4 +- .../issue_27103_clone_external_table.sql | 4 +- test/distributed/cases/udf_python/README.md | 5 + .../udf_python/overload_namespace.result | 13 ++ .../cases/udf_python/overload_namespace.sql | 8 + .../cases/udf_python/snapshot_restore.result | 40 ++++ .../cases/udf_python/snapshot_restore.sql | 34 ++++ 13 files changed, 355 insertions(+), 29 deletions(-) create mode 100644 test/distributed/cases/udf_python/snapshot_restore.result create mode 100644 test/distributed/cases/udf_python/snapshot_restore.sql diff --git a/pkg/frontend/authenticate.go b/pkg/frontend/authenticate.go index b70f590e66a70..e6b06004a8161 100644 --- a/pkg/frontend/authenticate.go +++ b/pkg/frontend/authenticate.go @@ -5076,7 +5076,11 @@ func doDropFunction(ctx context.Context, ses *Session, df *tree.DropFunction, rm // overloads would be indistinguishable and DROP could remove the // wrong immutable identity. A non-current body is left on the // logical path so an old demo can still be explicitly removed. - if !pythonDropSignatureMatches(df.Args, argstr) { + body, err := erArray[0].GetString(ctx, i, 2) + if err != nil { + return err + } + if !pythonDropSignatureMatches(df.Args, body) { continue } handleArgMatch := func() (rtnErr error) { diff --git a/pkg/frontend/python_udf_catalog_test.go b/pkg/frontend/python_udf_catalog_test.go index c2cd6c78d87ee..44d12281c6beb 100644 --- a/pkg/frontend/python_udf_catalog_test.go +++ b/pkg/frontend/python_udf_catalog_test.go @@ -13,6 +13,7 @@ import ( "strings" "testing" + "github.com/golang/mock/gomock" "github.com/matrixorigin/matrixone/pkg/common/moerr" "github.com/matrixorigin/matrixone/pkg/common/mpool" "github.com/matrixorigin/matrixone/pkg/config" @@ -26,6 +27,7 @@ import ( "github.com/matrixorigin/matrixone/pkg/sql/plan/function" "github.com/matrixorigin/matrixone/pkg/udf" pythonudf "github.com/matrixorigin/matrixone/pkg/udf/python" + "github.com/prashantv/gostub" "github.com/stretchr/testify/require" ) @@ -1140,4 +1142,25 @@ func TestPythonDropSignatureUsesExactDescriptor(t *testing.T) { require.True(t, pythonDropSignatureMatches(drop.Args, makeBody(decimal2))) require.False(t, pythonDropSignatureMatches(drop.Args, makeBody(decimal6))) + + // Exercise the catalog projection wiring, not only the descriptor helper. + ctrl := gomock.NewController(t) + ses := newSes(nil, ctrl) + ctx := ses.GetTxnHandler().GetTxnCtx() + bh := &backgroundExecTest{} + bh.init() + stub := gostub.StubFunc(&NewBackgroundExec, bh) + defer stub.Reset() + checkDB, err := getSqlForCheckDatabaseByAccount(ctx, "db") + require.NoError(t, err) + bh.sql2result[checkDB] = newMrsForCheckDatabase([][]interface{}{{int64(1)}}) + bh.sql2result[fmt.Sprintf(checkUdfArgs, "f", "db")] = newMrsForCheckUdfArgs([][]interface{}{ + {`[{"name":"value","type":"decimal"}]`, int64(101), makeBody(decimal2)}, + {`[{"name":"value","type":"decimal"}]`, int64(102), makeBody(decimal6)}, + }) + require.NoError(t, doDropFunction(ctx, ses, drop, nil)) + require.Contains(t, bh.executedSQLs, fmt.Sprintf(deleteUserDefinedFunctionFormat, 101)) + require.Contains(t, bh.executedSQLs, fmt.Sprintf(deleteUserDefinedFunctionRevisionsFormat, 101)) + require.NotContains(t, bh.executedSQLs, fmt.Sprintf(deleteUserDefinedFunctionFormat, 102)) + require.NotContains(t, bh.executedSQLs, fmt.Sprintf(deleteUserDefinedFunctionRevisionsFormat, 102)) } diff --git a/pkg/frontend/snapshot_test.go b/pkg/frontend/snapshot_test.go index bf0bc46131d3b..df9b88aa95cde 100644 --- a/pkg/frontend/snapshot_test.go +++ b/pkg/frontend/snapshot_test.go @@ -1814,7 +1814,7 @@ func TestRecreateUserDefinedFunctionCatalogPreservesCurrentSchema(t *testing.T) bh := &backgroundExecTest{} bh.init() ctx := defines.AttachAccountId(t.Context(), sourceAccount) - require.NoError(t, recreateTable(ctx, "", bh, snapshotName, currentUDFTable, sourceAccount, snapshotTS)) + require.NoError(t, recreateTable(ctx, "", bh, snapshotName, currentUDFTable, sourceAccount, snapshotTS, false)) require.Contains(t, bh.executedSQLs[len(bh.executedSQLs)-1], " select "+userDefinedFunctionCatalogSourceColumns+" from ") }) @@ -1912,7 +1912,7 @@ func TestRecreatePythonFunctionRevisionCatalogPreservesCurrentSchema(t *testing. bh.init() bh.sql2result[validationSQL] = &MysqlResultSet{} ctx := defines.AttachAccountId(t.Context(), sourceAccount) - require.NoError(t, recreateTable(ctx, "", bh, snapshotName, currentTable, sourceAccount, snapshotTS)) + require.NoError(t, recreateTable(ctx, "", bh, snapshotName, currentTable, sourceAccount, snapshotTS, false)) require.Equal(t, []string{ dropTableIfExistsSQL(moCatalog, currentTable.tblName), MoCatalogMoFunctionRevisionDDL, @@ -1925,7 +1925,7 @@ func TestRecreatePythonFunctionRevisionCatalogPreservesCurrentSchema(t *testing. bh := &backgroundExecTest{} bh.init() ctx := defines.AttachAccountId(t.Context(), sourceAccount) - require.NoError(t, recreateTable(ctx, "", bh, snapshotName, legacyTable, sourceAccount, snapshotTS)) + require.NoError(t, recreateTable(ctx, "", bh, snapshotName, legacyTable, sourceAccount, snapshotTS, false)) require.Equal(t, []string{ dropTableIfExistsSQL(moCatalog, legacyTable.tblName), MoCatalogMoFunctionRevisionDDL, @@ -1938,7 +1938,7 @@ func TestRecreatePythonFunctionRevisionCatalogPreservesCurrentSchema(t *testing. bh.init() bh.sql2result[validationSQL] = &MysqlResultSet{} ctx := defines.AttachAccountId(t.Context(), sourceAccount) - require.NoError(t, recreateTable(ctx, "", bh, snapshotName, currentTable, targetAccount, snapshotTS)) + require.NoError(t, recreateTable(ctx, "", bh, snapshotName, currentTable, targetAccount, snapshotTS, false)) require.Equal(t, []string{ dropTableIfExistsSQL(moCatalog, currentTable.tblName), MoCatalogMoFunctionRevisionDDL, diff --git a/pkg/udf/python/worker/Dockerfile b/pkg/udf/python/worker/Dockerfile index 4fbcaf7784120..31d3a145d283d 100644 --- a/pkg/udf/python/worker/Dockerfile +++ b/pkg/udf/python/worker/Dockerfile @@ -15,6 +15,12 @@ RUN apt-get update \ FROM python:3.12-slim-bookworm +RUN apt-get update \ + && apt-get install -y --no-install-recommends tini \ + && rm -rf /var/lib/apt/lists/* /usr/share/zoneinfo + +# Replace the complete database: Debian's posix symlinks cannot be overlaid +# safely with Ubuntu's directory layout, nor may stale zones be retained. COPY --from=matrixone-tzdata /usr/share/zoneinfo/ /usr/share/zoneinfo/ RUN useradd -m -s /bin/bash mo @@ -40,4 +46,7 @@ USER mo EXPOSE 50051 +# The worker waits for its direct children; init owns orphaned descendants +# after a handler leader exits. Keep that owner outside Python/Popen. +ENTRYPOINT [ "/usr/bin/tini", "--" ] CMD [ "python", "-u", "worker.py", "--address=grpc://0.0.0.0:50051" ] diff --git a/pkg/udf/python/worker/test_worker.py b/pkg/udf/python/worker/test_worker.py index a0bd3ec02fd6e..3c1a5616c3667 100644 --- a/pkg/udf/python/worker/test_worker.py +++ b/pkg/udf/python/worker/test_worker.py @@ -2,6 +2,7 @@ import datetime import decimal +import errno import io import importlib.util import json @@ -2137,13 +2138,172 @@ def test_handler_close_keeps_h_slot_while_process_cleanup_is_pending(self): if session._slot_acquired: slots.release() + def test_exchange_construction_failure_retains_cleanup_owner(self): + server = worker.RoutineFlightServer("grpc://127.0.0.1:0") + self.addCleanup(server.shutdown) + slots = server._handler_slots = threading.BoundedSemaphore(1) + quota = server._handler_quota = worker._HandlerQuota(1, 1) + allow_kill = threading.Event() + process = mock.Mock(pid=1234, stdin=mock.Mock()) + watchdog = mock.Mock() + construction_error = OSError(errno.EMFILE, "selector exhausted") + descriptor = {"type_id": worker.INT64, "offset_width": 32} + payload = complete_open_payload({ + "function_ref": {"account_id": 1, "database_id": 2, "function_id": 3, + "revision": 1, "namespace_version": 1}, + "source": "def f(ctx, x): return x + 1", "handler": "f", + "mode": worker.MODE_SCALAR, "null_policy": worker.NULL_CALL, + "abi_contract": worker.ABI_CONTRACT, + "adapter_version": worker.ADAPTER_VERSION, "sdk_version": worker.SDK_VERSION, + "args": [descriptor], "return": descriptor, + "max_batch_bytes": 1 << 20, "max_batch_rows": 8, + "handler_timeout_seconds": 10, + }) + fence = {"account_id": 1, "statement_id": "construction", + "group_id": "construction", "group_epoch": 1, + "invocation_id": "failed", "lease_epoch": 1} + batch = pa.RecordBatch.from_arrays( + [pa.array([7], type=pa.int64())], + schema=pa.schema([worker._field("arg_0", descriptor)]), + ) + controls, results = [], [] + + def exchange(): + key = worker._tuple_key(fence) + + def control(kind, **fields): + return worker._encode_control({"kind": kind, "tuple": fence, **fields}) + + chunks = iter([ + types.SimpleNamespace(data=batch, app_metadata=control("InputBatch", sequence=1)), + types.SimpleNamespace(data=None, app_metadata=control("EndInput", last_sequence=1)), + ]) + + def metadata(data): + value = worker._decode_control(data) + controls.append(value) + if value["kind"] == "Finish": + server._active[key].ack_finish(value["finish_id"]) + + def output(record, data): + results.append(record.column(0).to_pylist()) + server._active[key].ack_result(worker._decode_control(data)["sequence"]) + + server.do_exchange( + None, types.SimpleNamespace(command=control("OpenInvocation", payload=payload)), + types.SimpleNamespace(schema=batch.schema, read_chunk=lambda: next(chunks)), + types.SimpleNamespace(begin=lambda schema: None, + write_metadata=metadata, write_with_metadata=output), + ) + + def kill(child): + self.assertIs(child, process) + if not allow_kill.is_set(): + raise OSError("kill blocked") + + key = worker._tuple_key(fence) + owner = worker._handler_quota_owner(payload) + with mock.patch.object(worker.subprocess, "Popen", side_effect=[process, watchdog]), \ + mock.patch.object(worker.selectors, "DefaultSelector", side_effect=construction_error), \ + mock.patch.object(worker, "_kill_execution_process", side_effect=kill), \ + mock.patch.object(slots, "release", wraps=slots.release) as release_slot, \ + mock.patch.object(quota, "_release", wraps=quota._release) as release_quota: + try: + with self.assertRaises(OSError) as raised: + exchange() + self.assertIs(construction_error, raised.exception) + self.assertEqual("Error", controls[-1]["kind"]) + self.assertEqual([], results) + with server._pending_cleanup_condition: + self.assertEqual([key], list(server._pending_cleanups)) + session = server._pending_cleanups[key].handler_session + self.assertIs(process, session._process) + self.assertIn(key, server._active) + self.assertNotIn(key, server._terminal) + self.assertFalse(slots.acquire(blocking=False)) + self.assertEqual(({1: 1}, {(1, owner): 1}), quota.counts()) + release_slot.assert_not_called() + release_quota.assert_not_called() + with self.assertRaisesRegex(ValueError, "account handler budget is full"): + quota.acquire(1, owner) + + allow_kill.set() + with server._pending_cleanup_condition: + self.assertTrue(server._pending_cleanup_condition.wait_for( + lambda: not server._pending_cleanups, timeout=3 + ), "pending cleanup did not complete") + self.assertNotIn(key, server._active) + self.assertIn(key, server._terminal) + self.assertEqual(({}, {}), quota.counts()) + session.close() + release_slot.assert_called_once_with() + release_quota.assert_called_once_with(1, owner) + process.stdin.close.assert_called_once_with() + self.assertTrue(slots.acquire(blocking=False)) + slots.release() + finally: + allow_kill.set() + # Drain while the process doubles and kill hook remain installed, + # including after an early assertion failure. + with server._pending_cleanup_condition: + self.assertTrue(server._pending_cleanup_condition.wait_for( + lambda: not server._pending_cleanups, timeout=3 + ), "test cleanup did not drain") + + # A later actual exchange can acquire the returned budgets and execute. + fence.update(group_epoch=2, invocation_id="next") + exchange() + self.assertEqual([[8]], results) + self.assertEqual("Finish", controls[-1]["kind"]) + self.assertFalse(server._active) + self.assertEqual(({}, {}), quota.counts()) + self.assertTrue(slots.acquire(blocking=False)) + slots.release() + + def test_helper_construction_failure_transfers_cleanup_owner(self): + slots = threading.BoundedSemaphore(1) + process = mock.Mock(pid=1234, stdin=mock.Mock()) + error = OSError(errno.EMFILE, "selector exhausted") + with mock.patch.object(worker.subprocess, "Popen", return_value=process), \ + mock.patch.object(worker.selectors, "DefaultSelector", side_effect=error), \ + mock.patch.object(worker, "_kill_execution_process", side_effect=OSError("kill blocked")) as kill: + with self.assertRaises(worker._HandlerCleanupPending) as raised: + worker._run_handler_process(None, {}, 1, slots) + pending = raised.exception + try: + self.assertIs(error, pending.error) + self.assertIs(error, pending.__cause__) + self.assertIs(process, pending.session._process) + self.assertFalse(slots.acquire(blocking=False)) + self.assertEqual(2, kill.call_count) + finally: + kill.side_effect = None + pending.session.close() + pending.session.close() + self.assertTrue(slots.acquire(blocking=False)) + slots.release() + process.stdin.close.assert_called_once_with() + def test_handler_cleanup_does_not_mask_execution_error(self): session = mock.Mock() session.run.side_effect = ValueError("USER_CODE: handler failed") session.close.side_effect = RuntimeError("close failed") with mock.patch.object(worker, "_HandlerProcessSession", return_value=session): - with self.assertRaisesRegex(ValueError, "handler failed"): + with self.assertRaises(worker._HandlerCleanupPending) as raised: worker._run_handler_process(None, {}, 1) + self.assertIs(session.run.side_effect, raised.exception.error) + self.assertIs(session.run.side_effect, raised.exception.__cause__) + self.assertIs(session, raised.exception.session) + session.close.assert_called_once_with() + + def test_helper_success_with_failed_cleanup_transfers_owner(self): + session = mock.Mock() + session.close.side_effect = OSError("kill blocked") + with mock.patch.object(worker, "_HandlerProcessSession", return_value=session): + with self.assertRaises(worker._HandlerCleanupPending) as raised: + worker._run_handler_process(None, {}, 1) + self.assertIs(session.close.side_effect, raised.exception.error) + self.assertIs(session, raised.exception.session) session.close.assert_called_once_with() def test_real_flight_half_close_delivers_delayed_float_result_and_finish(self): @@ -3319,11 +3479,19 @@ def test_descendant_is_killed_after_handler_leader_exits(self): with self.assertRaisesRegex(ValueError, "handler process"): worker._run_handler_process(None, request, 2) observed = json.loads(witness.read_text()) - try: - survived = os.getpgid(observed["pid"]) == observed["pgid"] - except ProcessLookupError: - survived = False - self.assertFalse(survived) + # SIGKILL is synchronous as a request, but orphan adoption + # and init's waitpid are asynchronous. Require disappearance + # of the owned identity, including zombies, within a bound. + deadline = time.monotonic() + 2 + while True: + try: + survived = os.getpgid(observed["pid"]) == observed["pgid"] + except ProcessLookupError: + survived = False + if not survived or time.monotonic() >= deadline: + break + time.sleep(0.005) + self.assertFalse(survived, "init did not reap the owned descendant") finally: if witness.exists(): owned = json.loads(witness.read_text()) diff --git a/pkg/udf/python/worker/worker.py b/pkg/udf/python/worker/worker.py index 0a081de3e83e8..fee826d826f65 100644 --- a/pkg/udf/python/worker/worker.py +++ b/pkg/udf/python/worker/worker.py @@ -2599,6 +2599,19 @@ def counts(self) -> tuple[Dict[int, int], Dict[tuple[int, str], int]]: return dict(self._account_counts), dict(self._owner_counts) +class _HandlerCleanupPending(Exception): + """Transfer a session whose cleanup failed, preserving the primary error. + + Callers must retain ``session`` until close succeeds. Exchanges hand it to + their bounded cleanup owner; standalone helper callers own the retry. + """ + + def __init__(self, error: BaseException, session): + super().__init__(str(error)) + self.error = error + self.session = session + + class _HandlerProcessSession: """One handler child reused only inside a bounded invocation burst.""" @@ -2704,7 +2717,7 @@ def __init__( os.set_blocking(stdin_fd, False) os.set_blocking(response_read_fd, False) self._selector.register(response_read_fd, selectors.EVENT_READ, "response") - except Exception: + except Exception as error: for fd in ( locals().get("response_write_fd", -1), locals().get("parent_watch_read_fd", -1), @@ -2715,13 +2728,12 @@ def __init__( os.close(fd) except OSError: pass - # Preserve the construction failure. Cleanup is best effort and - # must not hide the contract error that caused session creation - # to fail. try: self.close() except Exception: - pass + # __init__ never returns on this path, so the assignment in + # the caller cannot publish us. Transfer ownership explicitly. + raise _HandlerCleanupPending(error, self) from error raise @property @@ -2991,20 +3003,26 @@ def _run_handler_process( context, request: Dict[str, Any], timeout_seconds: float, execution_slots: Optional[threading.BoundedSemaphore] = None, ) -> bytes: + # Construction failures already transfer their pending session in + # _HandlerCleanupPending. This standalone helper has no server reaper; + # its caller must retain that exception and retry session.close(). session = _HandlerProcessSession(execution_slots) try: result = session.run(context, request, timeout_seconds) - except BaseException: + except BaseException as error: # Preserve the handler/transport failure as the primary diagnosis. # Cleanup is still mandatory, but a close failure must not replace a # deadline, user-code, or protocol error and hide the actual cause. try: session.close() except Exception: - logging.exception("Python UDF handler cleanup failed after an execution error") + raise _HandlerCleanupPending(error, session) from error raise else: - session.close() + try: + session.close() + except Exception as error: + raise _HandlerCleanupPending(error, session) from error return result @@ -3812,12 +3830,16 @@ def do_exchange(self, context, descriptor, reader, writer): # admission and all input validation still happen # before a handler slot is acquired. owner_id = _handler_quota_owner(payload) - handler_session = _HandlerProcessSession( - self._handler_slots, - handler_quota=self._handler_quota, - account_id=key[0], - owner_id=owner_id, - ) + try: + handler_session = _HandlerProcessSession( + self._handler_slots, + handler_quota=self._handler_quota, + account_id=key[0], + owner_id=owner_id, + ) + except _HandlerCleanupPending as pending: + handler_session = pending.session + raise pending.error from None execution_request = { "source": source, "handler": handler_name, diff --git a/test/distributed/cases/git4data/clone/issue_27103_clone_external_table.result b/test/distributed/cases/git4data/clone/issue_27103_clone_external_table.result index 4e69c42722791..14e7e9fbc2fe2 100644 --- a/test/distributed/cases/git4data/clone/issue_27103_clone_external_table.result +++ b/test/distributed/cases/git4data/clone/issue_27103_clone_external_table.result @@ -29,7 +29,7 @@ fields terminated by ','; create function issue_27103_live_src.f_control() returns int language sql as 'select count(*) from issue_27103_live_src.control_t'; create function issue_27103_live_src.py_add(x int) returns int language python as -'return x + 1' handler 'py_add'; +'def py_add(ctx, x): return x + 1' handler 'py_add'; create function issue_27103_live_src.f_external() returns int language sql as 'select count(*) from issue_27103_live_src.ext_t'; create function issue_27103_live_src.f_transitive() returns int language sql as @@ -112,7 +112,7 @@ fields terminated by ','; create function issue_27103_snapshot_src.f_control() returns int language sql as 'select count(*) from issue_27103_snapshot_src.control_t'; create function issue_27103_snapshot_src.py_add(x int) returns int language python as -'return x + 1' handler 'py_add'; +'def py_add(ctx, x): return x + 1' handler 'py_add'; create function issue_27103_snapshot_src.f_external() returns int language sql as 'select count(*) from issue_27103_snapshot_src.ext_t'; create function issue_27103_snapshot_src.f_transitive() returns int language sql as diff --git a/test/distributed/cases/git4data/clone/issue_27103_clone_external_table.sql b/test/distributed/cases/git4data/clone/issue_27103_clone_external_table.sql index d4645ada92d90..a6bdc887a6d96 100644 --- a/test/distributed/cases/git4data/clone/issue_27103_clone_external_table.sql +++ b/test/distributed/cases/git4data/clone/issue_27103_clone_external_table.sql @@ -31,7 +31,7 @@ fields terminated by ','; create function issue_27103_live_src.f_control() returns int language sql as 'select count(*) from issue_27103_live_src.control_t'; create function issue_27103_live_src.py_add(x int) returns int language python as -'return x + 1' handler 'py_add'; +'def py_add(ctx, x): return x + 1' handler 'py_add'; create function issue_27103_live_src.f_external() returns int language sql as 'select count(*) from issue_27103_live_src.ext_t'; create function issue_27103_live_src.f_transitive() returns int language sql as @@ -84,7 +84,7 @@ fields terminated by ','; create function issue_27103_snapshot_src.f_control() returns int language sql as 'select count(*) from issue_27103_snapshot_src.control_t'; create function issue_27103_snapshot_src.py_add(x int) returns int language python as -'return x + 1' handler 'py_add'; +'def py_add(ctx, x): return x + 1' handler 'py_add'; create function issue_27103_snapshot_src.f_external() returns int language sql as 'select count(*) from issue_27103_snapshot_src.ext_t'; create function issue_27103_snapshot_src.f_transitive() returns int language sql as diff --git a/test/distributed/cases/udf_python/README.md b/test/distributed/cases/udf_python/README.md index 087f65dd69de4..3241d9f991a15 100644 --- a/test/distributed/cases/udf_python/README.md +++ b/test/distributed/cases/udf_python/README.md @@ -49,6 +49,11 @@ The cases keep the data sets small and target contract boundaries: SELECT, and DELETE atomically without publishing partial changes. - `revision_lifecycle.sql` checks create, immutable-revision replacement, prepared-plan invalidation, drop, and recreate on the same SQL signature. +- `overload_namespace.sql` checks cross-session overload changes invalidate + prepared/cache plans and DROP distinguishes exact decimal scales. +- `snapshot_restore.sql` restores revision A into a different account after + replacing the source with revision B, checking exact catalog bytes, execution, + account-scoped artifact publication, a SQL UDF control and teardown. - `vector_mode.sql` checks Arrow VECTOR handlers, zero-argument batch shape, NULL policies, and handler validation during create and replace. - `arrow_compute_arithmetic.sql` checks `pyarrow.compute` arithmetic, diff --git a/test/distributed/cases/udf_python/overload_namespace.result b/test/distributed/cases/udf_python/overload_namespace.result index 22f90130768aa..c75e6546a3527 100644 --- a/test/distributed/cases/udf_python/overload_namespace.result +++ b/test/distributed/cases/udf_python/overload_namespace.result @@ -54,4 +54,17 @@ select overload(x) as result from namespace_values order by x; 401 𝄀 402 deallocate prepare namespace_plan; +create function exact_decimal(x decimal(18,2)) returns int language python as 'def exact_decimal(ctx,x): return 2' handler 'exact_decimal'; +create function exact_decimal(x decimal(18,6)) returns int language python as 'def exact_decimal(ctx,x): return 6' handler 'exact_decimal'; +drop function exact_decimal(decimal(18,2)); +select count(*) as remaining_heads from mo_catalog.mo_user_defined_function where db = 'udf_python_namespace_bvt' and name = 'exact_decimal'; +➤ remaining_heads[-5,64,0] 𝄀 +1 +select count(*) as remaining_revisions from mo_catalog.mo_function_revisions r join mo_catalog.mo_user_defined_function f on r.function_id = f.function_id where f.db = 'udf_python_namespace_bvt' and f.name = 'exact_decimal'; +➤ remaining_revisions[-5,64,0] 𝄀 +1 +select exact_decimal(cast(1.23 as decimal(18,6))) as surviving_overload; +➤ surviving_overload[4,32,0] 𝄀 +6 +drop function exact_decimal(decimal(18,6)); drop database udf_python_namespace_bvt; diff --git a/test/distributed/cases/udf_python/overload_namespace.sql b/test/distributed/cases/udf_python/overload_namespace.sql index 5a1634f84bd04..f9dd31556db9b 100644 --- a/test/distributed/cases/udf_python/overload_namespace.sql +++ b/test/distributed/cases/udf_python/overload_namespace.sql @@ -32,4 +32,12 @@ create function overload(x smallint) returns bigint language python as 'def over execute namespace_plan; select overload(x) as result from namespace_values order by x; deallocate prepare namespace_plan; +-- Exact input descriptors, including scale, select the identity removed by DROP. +create function exact_decimal(x decimal(18,2)) returns int language python as 'def exact_decimal(ctx,x): return 2' handler 'exact_decimal'; +create function exact_decimal(x decimal(18,6)) returns int language python as 'def exact_decimal(ctx,x): return 6' handler 'exact_decimal'; +drop function exact_decimal(decimal(18,2)); +select count(*) as remaining_heads from mo_catalog.mo_user_defined_function where db = 'udf_python_namespace_bvt' and name = 'exact_decimal'; +select count(*) as remaining_revisions from mo_catalog.mo_function_revisions r join mo_catalog.mo_user_defined_function f on r.function_id = f.function_id where f.db = 'udf_python_namespace_bvt' and f.name = 'exact_decimal'; +select exact_decimal(cast(1.23 as decimal(18,6))) as surviving_overload; +drop function exact_decimal(decimal(18,6)); drop database udf_python_namespace_bvt; diff --git a/test/distributed/cases/udf_python/snapshot_restore.result b/test/distributed/cases/udf_python/snapshot_restore.result new file mode 100644 index 0000000000000..15b3d027484e5 --- /dev/null +++ b/test/distributed/cases/udf_python/snapshot_restore.result @@ -0,0 +1,40 @@ +drop snapshot if exists python_udf_revision_snapshot; +drop account if exists python_udf_restore_target; +drop account if exists python_udf_restore_source; +create account python_udf_restore_source admin_name = 'udf_admin' identified by '111'; +create account python_udf_restore_target admin_name = 'udf_admin' identified by '111'; +create database udf_restore; +use udf_restore; +create function restored_python(x int) returns int language python as 'def restored_python(ctx,x): return x + 10' handler 'restored_python'; +create function restored_sql(x int) returns int language sql as '$1 + 20'; +create table expected_revisions as select f.name, f.function_id, f.active_revision, r.body from mo_catalog.mo_user_defined_function f join mo_catalog.mo_function_revisions r on f.function_id = r.function_id and f.active_revision = r.revision where f.db = 'udf_restore'; +select restored_python(1) as python_value, restored_sql(1) as sql_value; +➤ python_value[4,32,0] ¦ sql_value[-5,64,0] 𝄀 +11 ¦ 21 +create snapshot python_udf_revision_snapshot for account python_udf_restore_source; +use udf_restore; +create or replace function restored_python(x int) returns int language python as 'def restored_python(ctx,x): return x + 100' handler 'restored_python'; +select restored_python(1) as source_value; +➤ source_value[4,32,0] 𝄀 +101 +restore account python_udf_restore_source{snapshot="python_udf_revision_snapshot"} to account python_udf_restore_target; +use udf_restore; +select restored_python(1) as python_value, restored_sql(1) as sql_value; +➤ python_value[4,32,0] ¦ sql_value[-5,64,0] 𝄀 +11 ¦ 21 +select count(*) as exact_restored_revisions from expected_revisions e join mo_catalog.mo_user_defined_function f on e.function_id = f.function_id and e.name = f.name and e.active_revision = f.active_revision join mo_catalog.mo_function_revisions r on f.function_id = r.function_id and f.active_revision = r.revision where f.db = 'udf_restore' and e.body = r.body; +➤ exact_restored_revisions[-5,64,0] 𝄀 +2 +use udf_restore; +select restored_python(1) as source_unchanged; +➤ source_unchanged[4,32,0] 𝄀 +101 +drop snapshot python_udf_revision_snapshot; +drop account python_udf_restore_target; +drop account python_udf_restore_source; +select count(*) as leftover_accounts from mo_catalog.mo_account where account_name in ('python_udf_restore_source','python_udf_restore_target'); +➤ leftover_accounts[-5,64,0] 𝄀 +0 +select count(*) as leftover_snapshots from mo_catalog.mo_snapshots where sname = 'python_udf_revision_snapshot'; +➤ leftover_snapshots[-5,64,0] 𝄀 +0 diff --git a/test/distributed/cases/udf_python/snapshot_restore.sql b/test/distributed/cases/udf_python/snapshot_restore.sql new file mode 100644 index 0000000000000..b123d80b68ab3 --- /dev/null +++ b/test/distributed/cases/udf_python/snapshot_restore.sql @@ -0,0 +1,34 @@ +-- Restore into a different account must republish the account-scoped artifact. +drop snapshot if exists python_udf_revision_snapshot; +drop account if exists python_udf_restore_target; +drop account if exists python_udf_restore_source; +create account python_udf_restore_source admin_name = 'udf_admin' identified by '111'; +create account python_udf_restore_target admin_name = 'udf_admin' identified by '111'; +-- @session:id=1&user=python_udf_restore_source:udf_admin&password=111 +create database udf_restore; +use udf_restore; +create function restored_python(x int) returns int language python as 'def restored_python(ctx,x): return x + 10' handler 'restored_python'; +create function restored_sql(x int) returns int language sql as '$1 + 20'; +create table expected_revisions as select f.name, f.function_id, f.active_revision, r.body from mo_catalog.mo_user_defined_function f join mo_catalog.mo_function_revisions r on f.function_id = r.function_id and f.active_revision = r.revision where f.db = 'udf_restore'; +select restored_python(1) as python_value, restored_sql(1) as sql_value; +-- @session +create snapshot python_udf_revision_snapshot for account python_udf_restore_source; +-- @session:id=1&user=python_udf_restore_source:udf_admin&password=111 +use udf_restore; +create or replace function restored_python(x int) returns int language python as 'def restored_python(ctx,x): return x + 100' handler 'restored_python'; +select restored_python(1) as source_value; +-- @session +restore account python_udf_restore_source{snapshot="python_udf_revision_snapshot"} to account python_udf_restore_target; +-- @session:id=2&user=python_udf_restore_target:udf_admin&password=111 +use udf_restore; +select restored_python(1) as python_value, restored_sql(1) as sql_value; +select count(*) as exact_restored_revisions from expected_revisions e join mo_catalog.mo_user_defined_function f on e.function_id = f.function_id and e.name = f.name and e.active_revision = f.active_revision join mo_catalog.mo_function_revisions r on f.function_id = r.function_id and f.active_revision = r.revision where f.db = 'udf_restore' and e.body = r.body; +-- @session:id=1&user=python_udf_restore_source:udf_admin&password=111 +use udf_restore; +select restored_python(1) as source_unchanged; +-- @session +drop snapshot python_udf_revision_snapshot; +drop account python_udf_restore_target; +drop account python_udf_restore_source; +select count(*) as leftover_accounts from mo_catalog.mo_account where account_name in ('python_udf_restore_source','python_udf_restore_target'); +select count(*) as leftover_snapshots from mo_catalog.mo_snapshots where sname = 'python_udf_revision_snapshot'; From 61e962ee3cedaa2b9024561f42589984101ba154 Mon Sep 17 00:00:00 2001 From: iamlinjunhong <1030420200@qq.com> Date: Sun, 20 Sep 2026 14:45:16 +0800 Subject: [PATCH 188/202] docs(udf): document execution and rollout scope for review --- docs/design/python_udf.md | 97 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 97 insertions(+) create mode 100644 docs/design/python_udf.md diff --git a/docs/design/python_udf.md b/docs/design/python_udf.md new file mode 100644 index 0000000000000..dde191f159839 --- /dev/null +++ b/docs/design/python_udf.md @@ -0,0 +1,97 @@ +# Python UDF implementation and rollout scope + +This describes the implemented, owner-authorized external Python adapter and its +PR acceptance boundary. It does not approve a production tenant-isolation model. +The shared Function Catalog design owns identity, revision and security semantics; +SQL and Python designs own their language contracts. This summary does not replace +those contracts. + +## Execution and identity + +CREATE/REPLACE validates Python syntax and the handler contract before publishing +a definition. The shared Catalog stores immutable typed revisions and complete +argument/return descriptors. Artifacts are digest-addressed, account-scoped and +published through FileService; worker execution receives the verified artifact +from CN. The worker needs no object-storage credentials or query-time package +installation. Old demo definitions are inert: explicitly recreate them using the +current DDL and SDK. Unknown definition, plan, Arrow, SDK or wire contracts fail +before user execution; no legacy fallback or automatic adapter is provided. + +Planning resolves an exact FunctionRef and RoutineCall. Prepared/cache reuse +validates the selected revision and the complete overload namespace state; see +[routine namespace validation](routine_namespace_validation.md). Execution never +resolves a mutable latest revision. Clone and restore preserve supported catalog +contracts and republish Python artifacts for the destination account. + +CN keeps native MO vectors. The expression framework dispatches typed Python calls +to ExternalRoutineEval; the parent evaluator owns argument evaluation, casts, +CASE/selection and NULL guards. The physical stage compacts selected rows, +admits bounded asynchronous work and scatters verified results, preserving empty +input, NULLs, zero arguments, nested calls and default VOLATILE semantics. + +The Gateway is a CN library, not a separately deployed service. It connects to +Arrow Flight served by the Python worker. No second Flight daemon is required. +A worker creates bounded handler subprocesses; each handler/module can be reused +only within the invocation's bounded burst. It does not pool user heaps across +invocations. SCALAR handlers receive `(ctx, scalar...)`; VECTOR handlers receive +`(ctx, arrays...)` and use `ctx.num_rows` for output shape. Arrow field/schema +metadata comes from the trusted adapter, not handler-supplied identity. + +## Completion and resources + +K bounds active Gateway invocations, H bounds handler execution slots, and W=1 +bounds stream batch progress. Admission, group/member state, terminal records and +per-account/per-owner quotas have independent owners and budgets. Protocol steps +include EndInput, InputConsumed, result ACK and Finish/FinishAck. Started work is +never transparently replayed. Cancellation and capability refresh waiters progress +independently; generation fences reject stale completions. + +Process cleanup must cover both termination and reaping. Handler sessions own +pipes, child/watchdog processes and H/quota until cleanup succeeds; partial +initialization transfers a failed session to the same bounded pending-cleanup +owner. Linux containers run `/usr/bin/tini -- python -u worker.py`; init adopts +orphaned descendants after their handler leader exits. An Operator command override +must retain this init process. Native Supervisor launches rely on the host's init +(or an explicit subreaper in a container test harness). + +## Enablement and deployment boundary + +Generic launch keeps Python disabled. The explicit worker-enabled launch and +ordinary BVT entry point enable the unisolated adapter. See +[Python BVT and local launch](../../test/distributed/cases/udf_python/README.md). +Compose configures a separate worker endpoint for each CN. The `PYTHON_UDF` +mo-service role wraps a Go Supervisor; the standalone worker image runs Python +under init. These are different launch forms for the same Flight service. + +Gateway and worker must match the current capability contract, including tzdata. +The worker image replaces its entire zoneinfo tree from the MO runtime base; +upgrading tzdata also changes the environment digest of definitions. Check existing +definitions before enabling an incompatible environment and explicitly rebuild +where required. Matching software version strings alone are not this check. + +`enabled` and `allow-unisolated` are explicit gates. This implementation does not +provide sandbox isolation or authenticated/TLS Flight transport. Same-Pod Operator +configuration binds the worker to loopback and controls Pod composition; it does +not add broad CN ingress grants. External worker exposure needs a separately +approved security/deployment design. Worker availability is a Python execution +condition, not evidence of production tenant isolation. + +## Verification and remaining scope + +The regression suite covers scalar/vector Arrow computation, types and malformed +values, guarded SQL positions and DML atomicity, revision/overload invalidation, +exact-descriptor DROP and cross-account snapshot restore. Runtime tests cover +ACK/Finish, cancellation, quotas, initialization/cleanup failure, worker death, +stale events and real Flight consumers. Operator status wire fixtures pin the +producer/consumer error contract. + +Linux PR validation uses the repository CGo wrapper, two CNs with distinct +Supervisor/worker endpoints, ordinary SQL/Python BVT, current worker image and +process-tree fault injection. Record the exact revision, selected tests, image +and terminal result with each run; unit or CNI echo tests are not complete +Operator rollout evidence. + +Sandbox, production deployment approval, imported dependency environments and +W>1/cumulative ACK enablement remain separate work. W=1 measurements do not prove +that a wider window has no benefit. This PR is related to issue #28132 and does +not close that issue's broader isolated-runtime acceptance criteria. From 17bb262f48767999ab8175bfd5df69e349cf6c52 Mon Sep 17 00:00:00 2001 From: iamlinjunhong <1030420200@qq.com> Date: Sun, 20 Sep 2026 15:41:43 +0800 Subject: [PATCH 189/202] fix(ci): add missing license headers for Python UDF files --- pkg/cnservice/python_runtime.go | 9 +++++++++ pkg/cnservice/python_runtime_test.go | 9 +++++++++ pkg/frontend/python_udf_catalog_test.go | 9 +++++++++ pkg/frontend/routine_namespace.go | 9 +++++++++ pkg/frontend/routine_namespace_test.go | 11 +++++++++++ pkg/frontend/routine_plan_validation.go | 9 +++++++++ pkg/sql/colexec/external_routine_eval.go | 9 +++++++++ pkg/sql/colexec/external_routine_eval_test.go | 9 +++++++++ pkg/sql/plan/function/python_udf.go | 6 ++++++ pkg/sql/plan/function/udf.go | 6 ++++++ pkg/sql/plan/routine_dependency.go | 9 +++++++++ pkg/udf/python/arrow_test.go | 9 +++++++++ pkg/udf/python/artifact.go | 9 +++++++++ pkg/udf/python/artifact_test.go | 9 +++++++++ pkg/udf/python/capability_lifecycle_test.go | 9 +++++++++ pkg/udf/python/config.go | 6 ++++++ pkg/udf/python/config_test.go | 9 +++++++++ pkg/udf/python/definition.go | 9 +++++++++ pkg/udf/python/definition_test.go | 9 +++++++++ pkg/udf/python/gateway.go | 6 ++++++ pkg/udf/python/gateway_admission_test.go | 9 +++++++++ pkg/udf/python/gateway_benchmark_test.go | 9 +++++++++ pkg/udf/python/gateway_integration_test.go | 9 +++++++++ pkg/udf/python/gateway_test.go | 9 +++++++++ pkg/udf/python/supervisor.go | 8 ++++++++ pkg/udf/python/supervisor_process_other.go | 14 ++++++++++++++ pkg/udf/python/supervisor_process_unix.go | 14 ++++++++++++++ pkg/udf/python/supervisor_test.go | 9 +++++++++ pkg/udf/python_contract.go | 9 +++++++++ pkg/udf/python_contract_test.go | 9 +++++++++ pkg/udf/runtime_status_wire_test.go | 11 +++++++++++ pkg/udf/runtime_test.go | 9 +++++++++ pkg/udf/timezone.go | 9 +++++++++ 33 files changed, 298 insertions(+) diff --git a/pkg/cnservice/python_runtime.go b/pkg/cnservice/python_runtime.go index d168e583b3e05..38a391abef7d3 100644 --- a/pkg/cnservice/python_runtime.go +++ b/pkg/cnservice/python_runtime.go @@ -2,6 +2,15 @@ // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. package cnservice diff --git a/pkg/cnservice/python_runtime_test.go b/pkg/cnservice/python_runtime_test.go index 1da63a1bd3de6..e3eff6af6fed6 100644 --- a/pkg/cnservice/python_runtime_test.go +++ b/pkg/cnservice/python_runtime_test.go @@ -2,6 +2,15 @@ // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. package cnservice diff --git a/pkg/frontend/python_udf_catalog_test.go b/pkg/frontend/python_udf_catalog_test.go index 44d12281c6beb..a454781694c31 100644 --- a/pkg/frontend/python_udf_catalog_test.go +++ b/pkg/frontend/python_udf_catalog_test.go @@ -2,6 +2,15 @@ // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. package frontend diff --git a/pkg/frontend/routine_namespace.go b/pkg/frontend/routine_namespace.go index 89e494ba8b40f..7f5e635cb4d74 100644 --- a/pkg/frontend/routine_namespace.go +++ b/pkg/frontend/routine_namespace.go @@ -2,6 +2,15 @@ // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. package frontend diff --git a/pkg/frontend/routine_namespace_test.go b/pkg/frontend/routine_namespace_test.go index e8531c811613a..6b6478a36a89a 100644 --- a/pkg/frontend/routine_namespace_test.go +++ b/pkg/frontend/routine_namespace_test.go @@ -1,6 +1,17 @@ // Copyright 2026 Matrix Origin +// // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package frontend import ( diff --git a/pkg/frontend/routine_plan_validation.go b/pkg/frontend/routine_plan_validation.go index 99ae6506cff28..d3722613112be 100644 --- a/pkg/frontend/routine_plan_validation.go +++ b/pkg/frontend/routine_plan_validation.go @@ -2,6 +2,15 @@ // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. package frontend diff --git a/pkg/sql/colexec/external_routine_eval.go b/pkg/sql/colexec/external_routine_eval.go index d3bf16cfb8c34..0a7bc02b476df 100644 --- a/pkg/sql/colexec/external_routine_eval.go +++ b/pkg/sql/colexec/external_routine_eval.go @@ -2,6 +2,15 @@ // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. package colexec diff --git a/pkg/sql/colexec/external_routine_eval_test.go b/pkg/sql/colexec/external_routine_eval_test.go index 39ac13d31f227..a7a09181ed299 100644 --- a/pkg/sql/colexec/external_routine_eval_test.go +++ b/pkg/sql/colexec/external_routine_eval_test.go @@ -2,6 +2,15 @@ // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. package colexec diff --git a/pkg/sql/plan/function/python_udf.go b/pkg/sql/plan/function/python_udf.go index ddd7764d6a63f..576fa6dc27615 100644 --- a/pkg/sql/plan/function/python_udf.go +++ b/pkg/sql/plan/function/python_udf.go @@ -5,6 +5,12 @@ // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. package function diff --git a/pkg/sql/plan/function/udf.go b/pkg/sql/plan/function/udf.go index 1a5292a7c6033..a60c78cc0250d 100644 --- a/pkg/sql/plan/function/udf.go +++ b/pkg/sql/plan/function/udf.go @@ -5,6 +5,12 @@ // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. package function diff --git a/pkg/sql/plan/routine_dependency.go b/pkg/sql/plan/routine_dependency.go index 31765cfcef242..a1d34f8950311 100644 --- a/pkg/sql/plan/routine_dependency.go +++ b/pkg/sql/plan/routine_dependency.go @@ -2,6 +2,15 @@ // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. package plan diff --git a/pkg/udf/python/arrow_test.go b/pkg/udf/python/arrow_test.go index fecb17e389558..f0f72f768ab7e 100644 --- a/pkg/udf/python/arrow_test.go +++ b/pkg/udf/python/arrow_test.go @@ -2,6 +2,15 @@ // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. package python diff --git a/pkg/udf/python/artifact.go b/pkg/udf/python/artifact.go index 72912c2173bf0..ad374605bdebb 100644 --- a/pkg/udf/python/artifact.go +++ b/pkg/udf/python/artifact.go @@ -2,6 +2,15 @@ // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. package python diff --git a/pkg/udf/python/artifact_test.go b/pkg/udf/python/artifact_test.go index 5f56eebd3d568..4e1be71b90a12 100644 --- a/pkg/udf/python/artifact_test.go +++ b/pkg/udf/python/artifact_test.go @@ -2,6 +2,15 @@ // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. package python diff --git a/pkg/udf/python/capability_lifecycle_test.go b/pkg/udf/python/capability_lifecycle_test.go index 04afe1e595458..36ca4d7eaf8ec 100644 --- a/pkg/udf/python/capability_lifecycle_test.go +++ b/pkg/udf/python/capability_lifecycle_test.go @@ -2,6 +2,15 @@ // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. package python diff --git a/pkg/udf/python/config.go b/pkg/udf/python/config.go index d1e1372d457d8..021e4f5cc90c8 100644 --- a/pkg/udf/python/config.go +++ b/pkg/udf/python/config.go @@ -5,6 +5,12 @@ // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. package python diff --git a/pkg/udf/python/config_test.go b/pkg/udf/python/config_test.go index 88be8ee0ca663..4873cc8fb5198 100644 --- a/pkg/udf/python/config_test.go +++ b/pkg/udf/python/config_test.go @@ -2,6 +2,15 @@ // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. package python diff --git a/pkg/udf/python/definition.go b/pkg/udf/python/definition.go index 4b118b3b83d9c..2f172098158e8 100644 --- a/pkg/udf/python/definition.go +++ b/pkg/udf/python/definition.go @@ -2,6 +2,15 @@ // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. package python diff --git a/pkg/udf/python/definition_test.go b/pkg/udf/python/definition_test.go index 9a0209935afe0..9dbeace96877d 100644 --- a/pkg/udf/python/definition_test.go +++ b/pkg/udf/python/definition_test.go @@ -2,6 +2,15 @@ // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. package python diff --git a/pkg/udf/python/gateway.go b/pkg/udf/python/gateway.go index ea79515ec6d47..27fb6033a6e68 100644 --- a/pkg/udf/python/gateway.go +++ b/pkg/udf/python/gateway.go @@ -5,6 +5,12 @@ // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. package python diff --git a/pkg/udf/python/gateway_admission_test.go b/pkg/udf/python/gateway_admission_test.go index c29eb711f1423..fd4d96e5e63d5 100644 --- a/pkg/udf/python/gateway_admission_test.go +++ b/pkg/udf/python/gateway_admission_test.go @@ -2,6 +2,15 @@ // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. package python diff --git a/pkg/udf/python/gateway_benchmark_test.go b/pkg/udf/python/gateway_benchmark_test.go index 3ce304fe2ef19..ba245079f8fb3 100644 --- a/pkg/udf/python/gateway_benchmark_test.go +++ b/pkg/udf/python/gateway_benchmark_test.go @@ -2,6 +2,15 @@ // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. package python diff --git a/pkg/udf/python/gateway_integration_test.go b/pkg/udf/python/gateway_integration_test.go index 0d26ddb95ca88..5517b648cda28 100644 --- a/pkg/udf/python/gateway_integration_test.go +++ b/pkg/udf/python/gateway_integration_test.go @@ -2,6 +2,15 @@ // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. package python diff --git a/pkg/udf/python/gateway_test.go b/pkg/udf/python/gateway_test.go index 3a9c2cb07e823..a98d16686c512 100644 --- a/pkg/udf/python/gateway_test.go +++ b/pkg/udf/python/gateway_test.go @@ -2,6 +2,15 @@ // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. package python diff --git a/pkg/udf/python/supervisor.go b/pkg/udf/python/supervisor.go index b16df8411e752..af3de0793fa10 100644 --- a/pkg/udf/python/supervisor.go +++ b/pkg/udf/python/supervisor.go @@ -3,6 +3,14 @@ // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. package python diff --git a/pkg/udf/python/supervisor_process_other.go b/pkg/udf/python/supervisor_process_other.go index 6ec19f6431cc9..bd72579d202c6 100644 --- a/pkg/udf/python/supervisor_process_other.go +++ b/pkg/udf/python/supervisor_process_other.go @@ -1,3 +1,17 @@ +// Copyright 2026 Matrix Origin +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + //go:build !(aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris) package python diff --git a/pkg/udf/python/supervisor_process_unix.go b/pkg/udf/python/supervisor_process_unix.go index 20bbb1f545436..c0930ebcd7026 100644 --- a/pkg/udf/python/supervisor_process_unix.go +++ b/pkg/udf/python/supervisor_process_unix.go @@ -1,3 +1,17 @@ +// Copyright 2026 Matrix Origin +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + //go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris package python diff --git a/pkg/udf/python/supervisor_test.go b/pkg/udf/python/supervisor_test.go index 9105ffb7adb6e..21fe77e45d69e 100644 --- a/pkg/udf/python/supervisor_test.go +++ b/pkg/udf/python/supervisor_test.go @@ -2,6 +2,15 @@ // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. package python diff --git a/pkg/udf/python_contract.go b/pkg/udf/python_contract.go index c9a12dd2eaa61..60bfd4d12f679 100644 --- a/pkg/udf/python_contract.go +++ b/pkg/udf/python_contract.go @@ -2,6 +2,15 @@ // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. package udf diff --git a/pkg/udf/python_contract_test.go b/pkg/udf/python_contract_test.go index 1f9edd7070603..543e489773c52 100644 --- a/pkg/udf/python_contract_test.go +++ b/pkg/udf/python_contract_test.go @@ -2,6 +2,15 @@ // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. package udf diff --git a/pkg/udf/runtime_status_wire_test.go b/pkg/udf/runtime_status_wire_test.go index ef52beffbf94e..3bce3cd16d052 100644 --- a/pkg/udf/runtime_status_wire_test.go +++ b/pkg/udf/runtime_status_wire_test.go @@ -1,6 +1,17 @@ // Copyright 2026 Matrix Origin +// // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package udf import ( diff --git a/pkg/udf/runtime_test.go b/pkg/udf/runtime_test.go index 3faa79a6dfe99..4ee0bd5e7e4c0 100644 --- a/pkg/udf/runtime_test.go +++ b/pkg/udf/runtime_test.go @@ -2,6 +2,15 @@ // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. package udf diff --git a/pkg/udf/timezone.go b/pkg/udf/timezone.go index f4816995f043d..2995f5ca79759 100644 --- a/pkg/udf/timezone.go +++ b/pkg/udf/timezone.go @@ -2,6 +2,15 @@ // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. package udf From 2ac43e75e8c3213795578445d5ece260f345d5d4 Mon Sep 17 00:00:00 2001 From: iamlinjunhong <1030420200@qq.com> Date: Sun, 20 Sep 2026 19:01:16 +0800 Subject: [PATCH 190/202] fix(udf): satisfy static checks and import ownership --- pkg/cnservice/python_runtime.go | 12 +- pkg/frontend/authenticate.go | 3 +- pkg/frontend/compiler_context.go | 47 ++-- pkg/frontend/routine_namespace.go | 11 +- pkg/frontend/routine_plan_validation.go | 44 +-- pkg/frontend/snapshot_catalog_restore.go | 79 +++--- pkg/iceberg/api/import_boundary_test.go | 134 ++++++++-- pkg/sql/colexec/external_routine_eval.go | 111 ++++---- pkg/sql/plan/function/python_udf.go | 29 +- pkg/sql/plan/function/udf.go | 89 ++++--- pkg/sql/plan/routine_dependency.go | 11 +- pkg/udf/protocol/protocol.go | 147 +++++----- pkg/udf/python/arrow.go | 188 +++++++------ pkg/udf/python/artifact.go | 46 ++-- pkg/udf/python/definition.go | 11 +- pkg/udf/python/gateway.go | 326 +++++++++++------------ pkg/udf/python/supervisor.go | 7 +- pkg/udf/runtime.go | 34 +-- pkg/udf/timezone.go | 4 +- pkg/util/errutil/errors.go | 26 ++ pkg/util/errutil/errors_test.go | 7 + 21 files changed, 742 insertions(+), 624 deletions(-) diff --git a/pkg/cnservice/python_runtime.go b/pkg/cnservice/python_runtime.go index 38a391abef7d3..6487d4a863a67 100644 --- a/pkg/cnservice/python_runtime.go +++ b/pkg/cnservice/python_runtime.go @@ -16,8 +16,8 @@ package cnservice import ( "context" - "fmt" - + "github.com/matrixorigin/matrixone/pkg/common/moerr" + "github.com/matrixorigin/matrixone/pkg/util/errutil" "go.uber.org/zap" "github.com/matrixorigin/matrixone/pkg/common/mpool" @@ -73,9 +73,9 @@ func (r *unavailablePythonRuntime) Language() string { return udf.LanguagePython func (r *unavailablePythonRuntime) unavailableError() error { if r == nil || r.cause == nil { - return fmt.Errorf("RESOURCE_UNAVAILABLE: Python UDF runtime initialization failed") + return moerr.NewInternalErrorNoCtxf("RESOURCE_UNAVAILABLE: Python UDF runtime initialization failed") } - return fmt.Errorf("RESOURCE_UNAVAILABLE: Python UDF runtime initialization failed: %w", r.cause) + return errutil.Wrapf(r.cause, "RESOURCE_UNAVAILABLE: Python UDF runtime initialization failed") } func (r *unavailablePythonRuntime) Execute(context.Context, *udf.Invocation, vector.FunctionResultWrapper, *mpool.MPool) error { @@ -84,7 +84,7 @@ func (r *unavailablePythonRuntime) Execute(context.Context, *udf.Invocation, vec func (r *unavailablePythonRuntime) CheckLanguageReady(ctx context.Context, language string) error { if language != udf.LanguagePython { - return fmt.Errorf("UNSUPPORTED_ROUTINE_VERSION: unsupported readiness language %q", language) + return moerr.NewInternalErrorNoCtxf("UNSUPPORTED_ROUTINE_VERSION: unsupported readiness language %q", language) } if ctx != nil { select { @@ -98,7 +98,7 @@ func (r *unavailablePythonRuntime) CheckLanguageReady(ctx context.Context, langu func (r *unavailablePythonRuntime) ValidateDefinition(ctx context.Context, definition *udf.RoutineDefinition) error { if definition == nil || definition.Language != udf.LanguagePython { - return fmt.Errorf("UNSUPPORTED_ROUTINE_VERSION: invalid Python definition") + return moerr.NewInternalErrorNoCtxf("UNSUPPORTED_ROUTINE_VERSION: invalid Python definition") } return r.CheckLanguageReady(ctx, udf.LanguagePython) } diff --git a/pkg/frontend/authenticate.go b/pkg/frontend/authenticate.go index e6b06004a8161..bbd6773c57ee5 100644 --- a/pkg/frontend/authenticate.go +++ b/pkg/frontend/authenticate.go @@ -20,6 +20,7 @@ import ( "encoding/json" "errors" "fmt" + "github.com/matrixorigin/matrixone/pkg/util/errutil" "io" "math" "math/bits" @@ -12488,7 +12489,7 @@ func readPythonRevisionBody(ctx context.Context, bh BackgroundExec, functionID i "select body from mo_catalog.mo_function_revisions where function_id = %d and revision = %d and namespace_version = %d;", functionID, revision, namespace, )); err != nil { - return "", fmt.Errorf("UNSUPPORTED_ROUTINE_VERSION: Python revision %d is unavailable: %w", revision, err) + return "", errutil.Wrapf(err, "UNSUPPORTED_ROUTINE_VERSION: Python revision %d is unavailable", revision) } rows, err := getResultSet(ctx, bh) if err != nil { diff --git a/pkg/frontend/compiler_context.go b/pkg/frontend/compiler_context.go index 31b93006a99f1..45be39d08e7e6 100644 --- a/pkg/frontend/compiler_context.go +++ b/pkg/frontend/compiler_context.go @@ -20,6 +20,7 @@ import ( "encoding/json" "errors" "fmt" + "github.com/matrixorigin/matrixone/pkg/util/errutil" "slices" "strconv" "strings" @@ -844,7 +845,7 @@ func (tcc *TxnCompilerContext) ResolveUdf(name string, args []*plan.Expr) (udf * return nil, err } if len(erArray) > 1 || (len(erArray) == 1 && erArray[0] == nil) { - return nil, fmt.Errorf("UNSUPPORTED_ROUTINE_VERSION: UDF %s lookup returned %d result sets, expected one or an empty candidate result", name, len(erArray)) + return nil, moerr.NewInternalErrorNoCtxf("UNSUPPORTED_ROUTINE_VERSION: UDF %s lookup returned %d result sets, expected one or an empty candidate result", name, len(erArray)) } if execResultArrayHasData(erArray) { @@ -1013,7 +1014,7 @@ func (tcc *TxnCompilerContext) ResolveUdf(name string, args []*plan.Expr) (udf * if strings.EqualFold(matchedList[0].Udf.Language, string(tree.PYTHON)) { matchedList[0].Udf.ArgsType = function.PythonUdfArgTypeCast(fromList, matchedList[0].Udf.GetArgsType()) if len(matchedList[0].Udf.ArgsType) != len(fromList) { - return nil, fmt.Errorf("python udf: argument descriptor count changed during resolution") + return nil, moerr.NewInternalErrorNoCtxf("python udf: argument descriptor count changed during resolution") } } else { matchedList[0].Udf.ArgsType = function.UdfArgTypeCast(fromList, matchedList[0].TypeList) @@ -1026,7 +1027,7 @@ func (tcc *TxnCompilerContext) ResolveUdf(name string, args []*plan.Expr) (udf * accountID = routineCatalogAccountID(accountID, tcc.GetSnapshot()) databaseID, databaseErr := routineCatalogDatabaseID(queryCtx, bh, matchedList[0].Udf.Db, accountID, tcc.GetSnapshot()) if databaseErr != nil { - return nil, fmt.Errorf("python routine database identity: %w", databaseErr) + return nil, errutil.Wrapf(databaseErr, "python routine database identity") } matchedList[0].Udf.AccountID = uint64(accountID) matchedList[0].Udf.DatabaseID = databaseID @@ -1036,7 +1037,7 @@ func (tcc *TxnCompilerContext) ResolveUdf(name string, args []*plan.Expr) (udf * } matchedList[0].Udf.NamespaceFingerprint = namespaces[uint64(matchedList[0].Udf.FunctionID)] if namespaceErr == nil && matchedList[0].Udf.NamespaceFingerprint == "" { - return nil, fmt.Errorf("UNSUPPORTED_ROUTINE_VERSION: missing routine namespace") + return nil, moerr.NewInternalErrorNoCtxf("UNSUPPORTED_ROUTINE_VERSION: missing routine namespace") } } @@ -1083,7 +1084,7 @@ func udfCatalogLookup( // catalog corruption from the planner/cache validator. func exactlyOneCatalogResultSet(ctx context.Context, rows []ExecResult, label string) (ExecResult, error) { if len(rows) != 1 || rows[0] == nil { - return nil, fmt.Errorf("UNSUPPORTED_ROUTINE_VERSION: %s returned %d result sets, expected exactly one", label, len(rows)) + return nil, moerr.NewInternalErrorNoCtxf("UNSUPPORTED_ROUTINE_VERSION: %s returned %d result sets, expected exactly one", label, len(rows)) } return rows[0], nil } @@ -1111,7 +1112,7 @@ func exactlyOneCatalogRow(ctx context.Context, rows []ExecResult, label string) } rowCount := result.GetRowCount() if rowCount != 1 { - return nil, fmt.Errorf("UNSUPPORTED_ROUTINE_VERSION: %s returned %d rows, expected exactly one", label, rowCount) + return nil, moerr.NewInternalErrorNoCtxf("UNSUPPORTED_ROUTINE_VERSION: %s returned %d rows, expected exactly one", label, rowCount) } return result, nil } @@ -1182,7 +1183,7 @@ func readSQLRevision( if isMissingCatalogObjectError(err) { return sqlRevisionCatalogRow{}, false, nil } - return sqlRevisionCatalogRow{}, false, fmt.Errorf("UNSUPPORTED_ROUTINE_VERSION: SQL function %d revision head is unavailable: %w", functionID, err) + return sqlRevisionCatalogRow{}, false, errutil.Wrapf(err, "UNSUPPORTED_ROUTINE_VERSION: SQL function %d revision head is unavailable", functionID) } rows, err := getResultSet(ctx, bh) if err != nil { @@ -1208,7 +1209,7 @@ func readSQLRevision( return sqlRevisionCatalogRow{}, false, nil } if activeRevision <= 0 || namespaceVersion <= 0 { - return sqlRevisionCatalogRow{}, false, fmt.Errorf("UNSUPPORTED_ROUTINE_VERSION: SQL function %d has an invalid revision head", functionID) + return sqlRevisionCatalogRow{}, false, moerr.NewInternalErrorNoCtxf("UNSUPPORTED_ROUTINE_VERSION: SQL function %d has an invalid revision head", functionID) } bh.ClearExecResultSet() @@ -1219,9 +1220,9 @@ func readSQLRevision( revisionTable, functionID, activeRevision, namespaceVersion, )); err != nil { if isMissingCatalogObjectError(err) { - return sqlRevisionCatalogRow{}, false, fmt.Errorf("UNSUPPORTED_ROUTINE_VERSION: SQL function %d revision table is unavailable: %w", functionID, err) + return sqlRevisionCatalogRow{}, false, errutil.Wrapf(err, "UNSUPPORTED_ROUTINE_VERSION: SQL function %d revision table is unavailable", functionID) } - return sqlRevisionCatalogRow{}, false, fmt.Errorf("UNSUPPORTED_ROUTINE_VERSION: SQL function %d revision is unavailable: %w", functionID, err) + return sqlRevisionCatalogRow{}, false, errutil.Wrapf(err, "UNSUPPORTED_ROUTINE_VERSION: SQL function %d revision is unavailable", functionID) } rows, err = getResultSet(ctx, bh) if err != nil { @@ -1282,15 +1283,15 @@ func readSQLRevision( volatility != "VOLATILE" || nullPolicy != udf.NullCallHandler || !strings.EqualFold(securityType, "DEFINER") || !strings.EqualFold(baseSecurityType, securityType) { - return sqlRevisionCatalogRow{}, false, fmt.Errorf("UNSUPPORTED_ROUTINE_VERSION: SQL function %d revision contract is not supported", functionID) + return sqlRevisionCatalogRow{}, false, moerr.NewInternalErrorNoCtxf("UNSUPPORTED_ROUTINE_VERSION: SQL function %d revision contract is not supported", functionID) } logicalArgTypes, err := userDefinedFunctionArgumentTypesFromJSON(args) if err != nil || logicalArgTypes != argTypes { - return sqlRevisionCatalogRow{}, false, fmt.Errorf("UNSUPPORTED_ROUTINE_VERSION: SQL function %d revision argument metadata is invalid", functionID) + return sqlRevisionCatalogRow{}, false, moerr.NewInternalErrorNoCtxf("UNSUPPORTED_ROUTINE_VERSION: SQL function %d revision argument metadata is invalid", functionID) } expectedFingerprint, err := function.SQLRoutineFingerprint(body, argTypes, retType) if err != nil || fingerprint == "" || fingerprint != expectedFingerprint { - return sqlRevisionCatalogRow{}, false, fmt.Errorf("UNSUPPORTED_ROUTINE_VERSION: SQL function %d revision fingerprint mismatch", functionID) + return sqlRevisionCatalogRow{}, false, moerr.NewInternalErrorNoCtxf("UNSUPPORTED_ROUTINE_VERSION: SQL function %d revision fingerprint mismatch", functionID) } return sqlRevisionCatalogRow{ Args: args, ArgTypes: argTypes, Body: body, Language: language, RetType: retType, @@ -1304,7 +1305,7 @@ func readPythonRevision(ctx context.Context, bh BackgroundExec, functionID int64 bh.ClearExecResultSet() query := pythonRevisionCatalogSQL(snapshot, functionID) if err := bh.Exec(ctx, query); err != nil { - return pythonRevisionCatalogRow{}, fmt.Errorf("UNSUPPORTED_ROUTINE_VERSION: Python catalog revision is unavailable: %w", err) + return pythonRevisionCatalogRow{}, errutil.Wrapf(err, "UNSUPPORTED_ROUTINE_VERSION: Python catalog revision is unavailable") } rows, err := getResultSet(ctx, bh) if err != nil { @@ -1327,7 +1328,7 @@ func readPythonRevision(ctx context.Context, bh BackgroundExec, functionID int64 return pythonRevisionCatalogRow{}, err } if activeRevision <= 0 || namespaceVersion <= 0 || revision != activeRevision { - return pythonRevisionCatalogRow{}, fmt.Errorf("UNSUPPORTED_ROUTINE_VERSION: Python function %d has an invalid revision head", functionID) + return pythonRevisionCatalogRow{}, moerr.NewInternalErrorNoCtxf("UNSUPPORTED_ROUTINE_VERSION: Python function %d has an invalid revision head", functionID) } getString := func(column uint64) (string, error) { return row.GetString(ctx, 0, column) } canonicalInput, err := getString(3) @@ -1411,32 +1412,32 @@ func readPythonRevision(ctx context.Context, bh BackgroundExec, functionID int64 return pythonRevisionCatalogRow{}, err } if language != string(tree.PYTHON) || definitionSchema != udf.PythonDefinitionSchemaVersion || abi != udf.PythonABIContract || adapter != udf.PythonAdapterVersion || sdk != udf.PythonSDKVersion || volatility != "VOLATILE" || !strings.EqualFold(securityType, "INVOKER") || !strings.EqualFold(baseSecurityType, securityType) { - return pythonRevisionCatalogRow{}, fmt.Errorf("UNSUPPORTED_ROUTINE_VERSION: Python function %d revision contract is not supported", functionID) + return pythonRevisionCatalogRow{}, moerr.NewInternalErrorNoCtxf("UNSUPPORTED_ROUTINE_VERSION: Python function %d revision contract is not supported", functionID) } bodyDefinition, err := function.DecodePythonRoutineBody(body) if err != nil { - return pythonRevisionCatalogRow{}, fmt.Errorf("UNSUPPORTED_ROUTINE_VERSION: Python function %d revision body is invalid: %w", functionID, err) + return pythonRevisionCatalogRow{}, errutil.Wrapf(err, "UNSUPPORTED_ROUTINE_VERSION: Python function %d revision body is invalid", functionID) } if bodyDefinition.ABIContract != abi || bodyDefinition.AdapterVersion != adapter || bodyDefinition.SDKVersion != sdk || bodyDefinition.NullPolicy != nullPolicy || bodyDefinition.ArtifactDigest != artifact || bodyDefinition.EnvironmentDigest != environment { - return pythonRevisionCatalogRow{}, fmt.Errorf("UNSUPPORTED_ROUTINE_VERSION: Python function %d revision metadata does not match its definition", functionID) + return pythonRevisionCatalogRow{}, moerr.NewInternalErrorNoCtxf("UNSUPPORTED_ROUTINE_VERSION: Python function %d revision metadata does not match its definition", functionID) } actualFingerprint, err := function.PythonRoutineFingerprint(body) if err != nil { - return pythonRevisionCatalogRow{}, fmt.Errorf("UNSUPPORTED_ROUTINE_VERSION: Python function %d revision fingerprint cannot be computed: %w", functionID, err) + return pythonRevisionCatalogRow{}, errutil.Wrapf(err, "UNSUPPORTED_ROUTINE_VERSION: Python function %d revision fingerprint cannot be computed", functionID) } if fingerprint == "" || fingerprint != actualFingerprint { - return pythonRevisionCatalogRow{}, fmt.Errorf("UNSUPPORTED_ROUTINE_VERSION: Python function %d revision fingerprint mismatch", functionID) + return pythonRevisionCatalogRow{}, moerr.NewInternalErrorNoCtxf("UNSUPPORTED_ROUTINE_VERSION: Python function %d revision fingerprint mismatch", functionID) } expectedInput, expectedReturn, expectedSignature, err := function.PythonSignatureMetadata(bodyDefinition.ArgTypes, bodyDefinition.ReturnType) if err != nil { - return pythonRevisionCatalogRow{}, fmt.Errorf("UNSUPPORTED_ROUTINE_VERSION: Python function %d signature cannot be computed: %w", functionID, err) + return pythonRevisionCatalogRow{}, errutil.Wrapf(err, "UNSUPPORTED_ROUTINE_VERSION: Python function %d signature cannot be computed", functionID) } if signatureKeySchemaVersion != udf.PythonSignatureKeySchemaVersion || canonicalInput != expectedInput || returnDescriptor != expectedReturn || signatureFingerprint != expectedSignature { - return pythonRevisionCatalogRow{}, fmt.Errorf("UNSUPPORTED_ROUTINE_VERSION: Python function %d identity descriptor metadata mismatch", functionID) + return pythonRevisionCatalogRow{}, moerr.NewInternalErrorNoCtxf("UNSUPPORTED_ROUTINE_VERSION: Python function %d identity descriptor metadata mismatch", functionID) } if argTypes != expectedInput { - return pythonRevisionCatalogRow{}, fmt.Errorf("UNSUPPORTED_ROUTINE_VERSION: Python function %d revision argument descriptor mismatch", functionID) + return pythonRevisionCatalogRow{}, moerr.NewInternalErrorNoCtxf("UNSUPPORTED_ROUTINE_VERSION: Python function %d revision argument descriptor mismatch", functionID) } return pythonRevisionCatalogRow{ Args: args, ArgTypes: argTypes, Body: body, Language: language, RetType: retType, diff --git a/pkg/frontend/routine_namespace.go b/pkg/frontend/routine_namespace.go index 7f5e635cb4d74..1c9e6d4856986 100644 --- a/pkg/frontend/routine_namespace.go +++ b/pkg/frontend/routine_namespace.go @@ -19,8 +19,9 @@ import ( "crypto/sha256" "encoding/hex" "encoding/json" - "errors" "fmt" + "github.com/matrixorigin/matrixone/pkg/common/moerr" + "github.com/matrixorigin/matrixone/pkg/util/errutil" "sort" "strconv" "strings" @@ -28,7 +29,7 @@ import ( planpb "github.com/matrixorigin/matrixone/pkg/pb/plan" ) -var errRoutineNamespaceBudget = errors.New("routine namespace cache budget exceeded") +var errRoutineNamespaceBudget = moerr.NewInternalErrorNoCtx("routine namespace cache budget exceeded") const maxRoutineNamespaceRows = 65536 const maxRoutineNamespaceBytes = 64 << 20 @@ -80,7 +81,7 @@ func routineNamespacesSQL(ids []uint64, snapshot *planpb.Snapshot) string { func decodeRoutineNamespaces(ctx context.Context, rows ExecResult) (map[uint64]string, error) { if rows.GetRowCount() > maxRoutineNamespaceRows { - return nil, fmt.Errorf("%w: namespace rows exceed %d", errRoutineNamespaceBudget, maxRoutineNamespaceRows) + return nil, errutil.WrapfCauseFirst(errRoutineNamespaceBudget, "namespace rows exceed %d", maxRoutineNamespaceRows) } groups := make(map[uint64]map[uint64]string) remaining := maxRoutineNamespaceBytes @@ -100,7 +101,7 @@ func decodeRoutineNamespaces(ctx context.Context, rows ExecResult) (map[uint64]s return nil, err } if len(value) > remaining { - return nil, fmt.Errorf("%w: namespace bytes exceed %d", errRoutineNamespaceBudget, maxRoutineNamespaceBytes) + return nil, errutil.WrapfCauseFirst(errRoutineNamespaceBudget, "namespace bytes exceed %d", maxRoutineNamespaceBytes) } remaining -= len(value) fields[col] = value @@ -120,7 +121,7 @@ func decodeRoutineNamespaces(ctx context.Context, rows ExecResult) (map[uint64]s groups[numbers[0]] = make(map[uint64]string) } if _, duplicate := groups[numbers[0]][numbers[1]]; duplicate { - return nil, fmt.Errorf("UNSUPPORTED_ROUTINE_VERSION: duplicate routine namespace member") + return nil, moerr.NewInternalErrorNoCtxf("UNSUPPORTED_ROUTINE_VERSION: duplicate routine namespace member") } groups[numbers[0]][numbers[1]] = hex.EncodeToString(digest[:]) } diff --git a/pkg/frontend/routine_plan_validation.go b/pkg/frontend/routine_plan_validation.go index d3722613112be..98dcfa9429b97 100644 --- a/pkg/frontend/routine_plan_validation.go +++ b/pkg/frontend/routine_plan_validation.go @@ -18,6 +18,8 @@ import ( "context" "errors" "fmt" + "github.com/matrixorigin/matrixone/pkg/common/moerr" + "github.com/matrixorigin/matrixone/pkg/util/errutil" "sort" "strconv" "strings" @@ -114,7 +116,7 @@ func routinePlanDependencies(p *planpb.Plan) ([]*planpb.RoutinePlanDependency, e if frame.next == 0 { frameDependencies := frame.query.GetRoutineDependencies() if len(dependencies)+len(frameDependencies) > maxRoutinePlanDependencies { - return nil, fmt.Errorf("PROGRAM_LIMIT_EXCEEDED: routine dependency count exceeds %d", maxRoutinePlanDependencies) + return nil, moerr.NewInternalErrorNoCtxf("PROGRAM_LIMIT_EXCEEDED: routine dependency count exceeds %d", maxRoutinePlanDependencies) } for _, dependency := range frameDependencies { dependencySize := 1 @@ -122,7 +124,7 @@ func routinePlanDependencies(p *planpb.Plan) ([]*planpb.RoutinePlanDependency, e dependencySize = dependency.ProtoSize() } if dependencySize > maxRoutinePlanDependencyBytes-dependencyBytes { - return nil, fmt.Errorf("PROGRAM_LIMIT_EXCEEDED: routine dependency bytes exceed %d", maxRoutinePlanDependencyBytes) + return nil, moerr.NewInternalErrorNoCtxf("PROGRAM_LIMIT_EXCEEDED: routine dependency bytes exceed %d", maxRoutinePlanDependencyBytes) } dependencyBytes += dependencySize } @@ -141,10 +143,10 @@ func routinePlanDependencies(p *planpb.Plan) ([]*planpb.RoutinePlanDependency, e continue } if frame.depth+1 > maxRoutinePlanQueryDepth { - return nil, fmt.Errorf("PROGRAM_LIMIT_EXCEEDED: routine dependency query nesting exceeds %d", maxRoutinePlanQueryDepth) + return nil, moerr.NewInternalErrorNoCtxf("PROGRAM_LIMIT_EXCEEDED: routine dependency query nesting exceeds %d", maxRoutinePlanQueryDepth) } if _, exists := active[background]; exists { - return nil, fmt.Errorf("UNSUPPORTED_ROUTINE_VERSION: routine dependency query graph contains a cycle") + return nil, moerr.NewInternalErrorNoCtxf("UNSUPPORTED_ROUTINE_VERSION: routine dependency query graph contains a cycle") } // Shared background queries contribute their immutable dependencies // once. Re-expanding a DAG as a tree can turn a small plan into @@ -171,10 +173,10 @@ func validateRoutinePlanDependencies(ctx context.Context, ses FeSession, p *plan return false, nil } if len(dependencies) > maxRoutinePlanDependencies { - return false, fmt.Errorf("PROGRAM_LIMIT_EXCEEDED: routine dependency count exceeds %d", maxRoutinePlanDependencies) + return false, moerr.NewInternalErrorNoCtxf("PROGRAM_LIMIT_EXCEEDED: routine dependency count exceeds %d", maxRoutinePlanDependencies) } if ses == nil { - return false, fmt.Errorf("UNSUPPORTED_ROUTINE_VERSION: routine dependency validation has no session") + return false, moerr.NewInternalErrorNoCtxf("UNSUPPORTED_ROUTINE_VERSION: routine dependency validation has no session") } var snapshot *planpb.Snapshot if tcc := ses.GetTxnCompileCtx(); tcc != nil { @@ -220,7 +222,7 @@ func validateRoutinePlanDependencies(ctx context.Context, ses FeSession, p *plan defer bh.Close() if !useCallerTxn { if err = bh.Exec(ctx, "begin;"); err != nil { - return false, fmt.Errorf("UNSUPPORTED_ROUTINE_VERSION: routine dependency validation could not start: %w", err) + return false, errutil.Wrapf(err, "UNSUPPORTED_ROUTINE_VERSION: routine dependency validation could not start") } defer func() { err = finishTxn(ctx, bh, err) }() } @@ -235,7 +237,7 @@ func validateRoutinePlanDependencies(ctx context.Context, ses FeSession, p *plan } } if err = bh.Exec(ctx, query); err != nil { - return false, fmt.Errorf("UNSUPPORTED_ROUTINE_VERSION: routine dependency validation failed: %w", err) + return false, errutil.Wrapf(err, "UNSUPPORTED_ROUTINE_VERSION: routine dependency validation failed") } rows, err := getResultSet(ctx, bh) if err != nil { @@ -348,13 +350,13 @@ func validateRoutinePlanDependencies(ctx context.Context, ses FeSession, p *plan return false, getErr } if !strings.EqualFold(revisionSecurityType, baseSecurityType) { - return false, fmt.Errorf("UNSUPPORTED_ROUTINE_VERSION: routine %d has inconsistent revision and identity security contracts", id) + return false, moerr.NewInternalErrorNoCtxf("UNSUPPORTED_ROUTINE_VERSION: routine %d has inconsistent revision and identity security contracts", id) } if language == udf.LanguagePython && !strings.EqualFold(revisionSecurityType, "INVOKER") { - return false, fmt.Errorf("UNSUPPORTED_ROUTINE_VERSION: Python routine catalog security type is not INVOKER") + return false, moerr.NewInternalErrorNoCtxf("UNSUPPORTED_ROUTINE_VERSION: Python routine catalog security type is not INVOKER") } if language == udf.LanguageSQL && !strings.EqualFold(revisionSecurityType, "DEFINER") { - return false, fmt.Errorf("UNSUPPORTED_ROUTINE_VERSION: SQL routine catalog security type is not DEFINER") + return false, moerr.NewInternalErrorNoCtxf("UNSUPPORTED_ROUTINE_VERSION: SQL routine catalog security type is not DEFINER") } identityValid := routinePlanCatalogIdentityValidForLanguage( language, @@ -364,7 +366,7 @@ func validateRoutinePlanDependencies(ctx context.Context, ses FeSession, p *plan revisionSDK, revisionNullPolicy, ) if _, duplicate := states[id]; duplicate { - return false, fmt.Errorf("UNSUPPORTED_ROUTINE_VERSION: routine catalog returned duplicate state for function %d", id) + return false, moerr.NewInternalErrorNoCtxf("UNSUPPORTED_ROUTINE_VERSION: routine catalog returned duplicate state for function %d", id) } states[id] = routinePlanCatalogState{ activeRevision: activeRevision, namespaceVersion: namespaceVersion, @@ -490,37 +492,37 @@ func routineRevisionReturnTypeMatches(rettype string, oid types.T) bool { func validateRoutinePlanDependencyShape(dependency *planpb.RoutinePlanDependency, accountID uint32) error { if dependency == nil || dependency.FunctionRef == nil { - return fmt.Errorf("UNSUPPORTED_ROUTINE_VERSION: routine plan dependency has no FunctionRef") + return moerr.NewInternalErrorNoCtxf("UNSUPPORTED_ROUTINE_VERSION: routine plan dependency has no FunctionRef") } ref := dependency.FunctionRef if ref.FunctionId == 0 || ref.Revision == 0 || ref.NamespaceVersion == 0 || ref.AccountId != uint64(accountID) || ref.DatabaseId == 0 { - return fmt.Errorf("UNSUPPORTED_ROUTINE_VERSION: routine plan dependency has an invalid catalog identity") + return moerr.NewInternalErrorNoCtxf("UNSUPPORTED_ROUTINE_VERSION: routine plan dependency has an invalid catalog identity") } if dependency.ContractVersion != udf.RoutinePlanContractVersion || !udf.IsSHA256Digest(dependency.DefinitionFingerprint) || !udf.IsSHA256Digest(dependency.NamespaceFingerprint) { - return fmt.Errorf("UNSUPPORTED_ROUTINE_VERSION: routine plan dependency contract is incomplete") + return moerr.NewInternalErrorNoCtxf("UNSUPPORTED_ROUTINE_VERSION: routine plan dependency contract is incomplete") } if dependency.Volatility == "" || dependency.NullPolicy == "" || !dependency.MayError || dependency.Leakproof { - return fmt.Errorf("UNSUPPORTED_ROUTINE_VERSION: routine plan dependency semantic contract is incomplete") + return moerr.NewInternalErrorNoCtxf("UNSUPPORTED_ROUTINE_VERSION: routine plan dependency semantic contract is incomplete") } switch dependency.Language { case udf.LanguagePython: if dependency.SecurityMode != "INVOKER" { - return fmt.Errorf("UNSUPPORTED_ROUTINE_VERSION: Python routine plan dependency must use INVOKER security") + return moerr.NewInternalErrorNoCtxf("UNSUPPORTED_ROUTINE_VERSION: Python routine plan dependency must use INVOKER security") } if !udf.IsSHA256Digest(dependency.ArtifactDigest) || !udf.IsSHA256Digest(dependency.EnvironmentDigest) { - return fmt.Errorf("UNSUPPORTED_ROUTINE_VERSION: Python routine plan dependency integrity is incomplete") + return moerr.NewInternalErrorNoCtxf("UNSUPPORTED_ROUTINE_VERSION: Python routine plan dependency integrity is incomplete") } case udf.LanguageSQL: if dependency.SecurityMode == "" { - return fmt.Errorf("UNSUPPORTED_ROUTINE_VERSION: SQL routine plan dependency has no security mode") + return moerr.NewInternalErrorNoCtxf("UNSUPPORTED_ROUTINE_VERSION: SQL routine plan dependency has no security mode") } if dependency.ArtifactDigest != "" || dependency.EnvironmentDigest != "" { - return fmt.Errorf("UNSUPPORTED_ROUTINE_VERSION: SQL routine plan dependency has external integrity fields") + return moerr.NewInternalErrorNoCtxf("UNSUPPORTED_ROUTINE_VERSION: SQL routine plan dependency has external integrity fields") } default: - return fmt.Errorf("UNSUPPORTED_ROUTINE_VERSION: routine plan dependency language %q is unsupported", dependency.Language) + return moerr.NewInternalErrorNoCtxf("UNSUPPORTED_ROUTINE_VERSION: routine plan dependency language %q is unsupported", dependency.Language) } return nil } diff --git a/pkg/frontend/snapshot_catalog_restore.go b/pkg/frontend/snapshot_catalog_restore.go index 4777df108673c..ac7bd70e1e674 100644 --- a/pkg/frontend/snapshot_catalog_restore.go +++ b/pkg/frontend/snapshot_catalog_restore.go @@ -18,6 +18,7 @@ import ( "context" "encoding/json" "fmt" + "github.com/matrixorigin/matrixone/pkg/util/errutil" "strconv" "strings" @@ -185,27 +186,27 @@ func validateRestoredFunctionRevisionCatalog( ) rows, err := getStringColsList(targetCtx, bh, query, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19) if err != nil { - return fmt.Errorf("UNSUPPORTED_ROUTINE_VERSION: restored Python revision catalog cannot be validated: %w", err) + return errutil.Wrapf(err, "UNSUPPORTED_ROUTINE_VERSION: restored Python revision catalog cannot be validated") } for rowIndex, row := range rows { if len(row) != 20 { - return fmt.Errorf("UNSUPPORTED_ROUTINE_VERSION: restored Python revision row %d has %d columns", rowIndex, len(row)) + return moerr.NewInternalErrorNoCtxf("UNSUPPORTED_ROUTINE_VERSION: restored Python revision row %d has %d columns", rowIndex, len(row)) } functionID, err := strconv.ParseUint(row[0], 10, 64) if err != nil || functionID == 0 { - return fmt.Errorf("UNSUPPORTED_ROUTINE_VERSION: restored Python revision row %d has an invalid function identity", rowIndex) + return moerr.NewInternalErrorNoCtxf("UNSUPPORTED_ROUTINE_VERSION: restored Python revision row %d has an invalid function identity", rowIndex) } revision, err := strconv.ParseUint(row[1], 10, 64) if err != nil || revision == 0 { - return fmt.Errorf("UNSUPPORTED_ROUTINE_VERSION: restored Python function %d has an invalid revision", functionID) + return moerr.NewInternalErrorNoCtxf("UNSUPPORTED_ROUTINE_VERSION: restored Python function %d has an invalid revision", functionID) } namespace, err := strconv.ParseUint(row[2], 10, 64) if err != nil || namespace == 0 { - return fmt.Errorf("UNSUPPORTED_ROUTINE_VERSION: restored Python function %d has an invalid namespace version", functionID) + return moerr.NewInternalErrorNoCtxf("UNSUPPORTED_ROUTINE_VERSION: restored Python function %d has an invalid namespace version", functionID) } definitionSchema, err := strconv.Atoi(row[9]) if err != nil { - return fmt.Errorf("UNSUPPORTED_ROUTINE_VERSION: restored function %d revision %d has an invalid definition schema", functionID, revision) + return moerr.NewInternalErrorNoCtxf("UNSUPPORTED_ROUTINE_VERSION: restored function %d revision %d has an invalid definition schema", functionID, revision) } if row[8] == udf.LanguageSQL { if err := validateRestoredSQLFunctionRevision(functionID, revision, row, definitionSchema); err != nil { @@ -220,12 +221,12 @@ func validateRestoredFunctionRevisionCatalog( row[14] != udf.PythonSDKVersion || !strings.EqualFold(row[16], "VOLATILE") || !strings.EqualFold(row[19], "INVOKER") { - return fmt.Errorf("UNSUPPORTED_ROUTINE_VERSION: restored function %d revision %d has an unsupported execution contract", functionID, revision) + return moerr.NewInternalErrorNoCtxf("UNSUPPORTED_ROUTINE_VERSION: restored function %d revision %d has an unsupported execution contract", functionID, revision) } body, err := function.DecodePythonRoutineBody(row[7]) if err != nil { - return fmt.Errorf("UNSUPPORTED_ROUTINE_VERSION: restored Python function %d revision %d body is invalid: %w", functionID, revision, err) + return errutil.Wrapf(err, "UNSUPPORTED_ROUTINE_VERSION: restored Python function %d revision %d body is invalid", functionID, revision) } if body.DefinitionSchemaVersion != definitionSchema || body.ABIContract != row[10] || @@ -234,11 +235,11 @@ func validateRestoredFunctionRevisionCatalog( body.EnvironmentDigest != row[13] || body.SDKVersion != row[14] || body.NullPolicy != row[15] { - return fmt.Errorf("UNSUPPORTED_ROUTINE_VERSION: restored Python function %d revision %d metadata does not match its definition", functionID, revision) + return moerr.NewInternalErrorNoCtxf("UNSUPPORTED_ROUTINE_VERSION: restored Python function %d revision %d metadata does not match its definition", functionID, revision) } fingerprint, err := function.PythonRoutineFingerprint(row[7]) if err != nil || row[17] == "" || row[17] != fingerprint { - return fmt.Errorf("UNSUPPORTED_ROUTINE_VERSION: restored Python function %d revision %d fingerprint mismatch", functionID, revision) + return moerr.NewInternalErrorNoCtxf("UNSUPPORTED_ROUTINE_VERSION: restored Python function %d revision %d fingerprint mismatch", functionID, revision) } // `args` retains the shared logical names while `arg_types` in the // current Python revision is the exact descriptor identity used for @@ -246,11 +247,11 @@ func validateRestoredFunctionRevisionCatalog( // comparing arg_types with the logical OID list would reject every // current Python revision after restore. if _, err := userDefinedFunctionArgumentTypesFromJSON(row[4]); err != nil { - return fmt.Errorf("UNSUPPORTED_ROUTINE_VERSION: restored Python function %d revision %d arguments are invalid: %w", functionID, revision, err) + return errutil.Wrapf(err, "UNSUPPORTED_ROUTINE_VERSION: restored Python function %d revision %d arguments are invalid", functionID, revision) } var args []*function.Arg if err := json.Unmarshal([]byte(row[4]), &args); err != nil { - return fmt.Errorf("UNSUPPORTED_ROUTINE_VERSION: restored Python function %d revision %d arguments are invalid: %w", functionID, revision, err) + return errutil.Wrapf(err, "UNSUPPORTED_ROUTINE_VERSION: restored Python function %d revision %d arguments are invalid", functionID, revision) } routine := &function.Udf{ Language: udf.LanguagePython, @@ -260,14 +261,14 @@ func validateRestoredFunctionRevisionCatalog( PythonReturnType: body.ReturnType, } if err := routine.ValidatePythonTypeContract(); err != nil { - return fmt.Errorf("UNSUPPORTED_ROUTINE_VERSION: restored Python function %d revision %d type contract mismatch: %w", functionID, revision, err) + return errutil.Wrapf(err, "UNSUPPORTED_ROUTINE_VERSION: restored Python function %d revision %d type contract mismatch", functionID, revision) } if err := routine.ValidatePythonCatalogSignature(); err != nil { - return fmt.Errorf("UNSUPPORTED_ROUTINE_VERSION: restored Python function %d revision %d catalog signature mismatch: %w", functionID, revision, err) + return errutil.Wrapf(err, "UNSUPPORTED_ROUTINE_VERSION: restored Python function %d revision %d catalog signature mismatch", functionID, revision) } expectedInput, _, _, err := function.PythonSignatureMetadata(body.ArgTypes, body.ReturnType) if err != nil || row[5] != expectedInput { - return fmt.Errorf("UNSUPPORTED_ROUTINE_VERSION: restored Python function %d revision %d argument descriptor mismatch", functionID, revision) + return moerr.NewInternalErrorNoCtxf("UNSUPPORTED_ROUTINE_VERSION: restored Python function %d revision %d argument descriptor mismatch", functionID, revision) } } return nil @@ -278,15 +279,15 @@ func validateRestoredSQLFunctionRevision(functionID, revision uint64, row []stri row[10] != "" || row[11] != "" || row[12] != "" || row[13] != "" || row[14] != "" || row[15] != udf.NullCallHandler || !strings.EqualFold(row[16], "VOLATILE") || !strings.EqualFold(row[19], "DEFINER") { - return fmt.Errorf("UNSUPPORTED_ROUTINE_VERSION: restored SQL function %d revision %d has an unsupported execution contract", functionID, revision) + return moerr.NewInternalErrorNoCtxf("UNSUPPORTED_ROUTINE_VERSION: restored SQL function %d revision %d has an unsupported execution contract", functionID, revision) } logicalArgTypes, err := userDefinedFunctionArgumentTypesFromJSON(row[4]) if err != nil || logicalArgTypes != row[5] { - return fmt.Errorf("UNSUPPORTED_ROUTINE_VERSION: restored SQL function %d revision %d argument metadata is invalid", functionID, revision) + return moerr.NewInternalErrorNoCtxf("UNSUPPORTED_ROUTINE_VERSION: restored SQL function %d revision %d argument metadata is invalid", functionID, revision) } fingerprint, err := function.SQLRoutineFingerprint(row[7], row[5], row[6]) if err != nil || row[17] == "" || row[17] != fingerprint { - return fmt.Errorf("UNSUPPORTED_ROUTINE_VERSION: restored SQL function %d revision %d fingerprint mismatch", functionID, revision) + return moerr.NewInternalErrorNoCtxf("UNSUPPORTED_ROUTINE_VERSION: restored SQL function %d revision %d fingerprint mismatch", functionID, revision) } return nil } @@ -315,23 +316,23 @@ func validateRestoredFunctionCatalogHeads( order by f.function_id;` rows, err := getStringColsList(targetCtx, bh, query, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15) if err != nil { - return fmt.Errorf("UNSUPPORTED_ROUTINE_VERSION: restored routine catalog heads cannot be validated: %w", err) + return errutil.Wrapf(err, "UNSUPPORTED_ROUTINE_VERSION: restored routine catalog heads cannot be validated") } for rowIndex, row := range rows { if len(row) != 16 { - return fmt.Errorf("UNSUPPORTED_ROUTINE_VERSION: restored routine catalog head row %d has %d columns", rowIndex, len(row)) + return moerr.NewInternalErrorNoCtxf("UNSUPPORTED_ROUTINE_VERSION: restored routine catalog head row %d has %d columns", rowIndex, len(row)) } functionID, err := strconv.ParseUint(row[0], 10, 64) if err != nil || functionID == 0 { - return fmt.Errorf("UNSUPPORTED_ROUTINE_VERSION: restored routine catalog head row %d has an invalid function identity", rowIndex) + return moerr.NewInternalErrorNoCtxf("UNSUPPORTED_ROUTINE_VERSION: restored routine catalog head row %d has an invalid function identity", rowIndex) } activeRevision, err := strconv.ParseUint(row[1], 10, 64) if err != nil { - return fmt.Errorf("UNSUPPORTED_ROUTINE_VERSION: restored routine function %d has an invalid active revision: %v", functionID, err) + return moerr.NewInternalErrorNoCtxf("UNSUPPORTED_ROUTINE_VERSION: restored routine function %d has an invalid active revision: %v", functionID, err) } namespaceVersion, err := strconv.ParseUint(row[2], 10, 64) if err != nil { - return fmt.Errorf("UNSUPPORTED_ROUTINE_VERSION: restored routine function %d has an invalid namespace version: %v", functionID, err) + return moerr.NewInternalErrorNoCtxf("UNSUPPORTED_ROUTINE_VERSION: restored routine function %d has an invalid namespace version: %v", functionID, err) } language := strings.ToLower(row[12]) if (language == udf.LanguageSQL || language == udf.LanguagePython) && activeRevision == 0 && namespaceVersion == 0 { @@ -343,52 +344,52 @@ func validateRestoredFunctionCatalogHeads( continue } if activeRevision == 0 { - return fmt.Errorf("UNSUPPORTED_ROUTINE_VERSION: restored %s function %d has no active immutable revision", language, functionID) + return moerr.NewInternalErrorNoCtxf("UNSUPPORTED_ROUTINE_VERSION: restored %s function %d has no active immutable revision", language, functionID) } if namespaceVersion == 0 { - return fmt.Errorf("UNSUPPORTED_ROUTINE_VERSION: restored %s function %d has an invalid namespace version", language, functionID) + return moerr.NewInternalErrorNoCtxf("UNSUPPORTED_ROUTINE_VERSION: restored %s function %d has an invalid namespace version", language, functionID) } revision, err := strconv.ParseUint(row[3], 10, 64) if err != nil || revision != activeRevision { - return fmt.Errorf("UNSUPPORTED_ROUTINE_VERSION: restored %s function %d active revision does not exist", language, functionID) + return moerr.NewInternalErrorNoCtxf("UNSUPPORTED_ROUTINE_VERSION: restored %s function %d active revision does not exist", language, functionID) } revisionNamespace, err := strconv.ParseUint(row[4], 10, 64) if err != nil || revisionNamespace != namespaceVersion { - return fmt.Errorf("UNSUPPORTED_ROUTINE_VERSION: restored %s function %d head namespace does not match its revision", language, functionID) + return moerr.NewInternalErrorNoCtxf("UNSUPPORTED_ROUTINE_VERSION: restored %s function %d head namespace does not match its revision", language, functionID) } if language == udf.LanguageSQL { if row[10] == "" || row[11] == "" { - return fmt.Errorf("UNSUPPORTED_ROUTINE_VERSION: restored SQL function %d has incomplete active revision", functionID) + return moerr.NewInternalErrorNoCtxf("UNSUPPORTED_ROUTINE_VERSION: restored SQL function %d has incomplete active revision", functionID) } if !strings.EqualFold(row[14], "DEFINER") || !strings.EqualFold(row[15], "DEFINER") { - return fmt.Errorf("UNSUPPORTED_ROUTINE_VERSION: restored SQL function %d has inconsistent security contract", functionID) + return moerr.NewInternalErrorNoCtxf("UNSUPPORTED_ROUTINE_VERSION: restored SQL function %d has inconsistent security contract", functionID) } expectedFingerprint, fingerprintErr := function.SQLRoutineFingerprint(row[11], row[9], row[10]) if fingerprintErr != nil || row[13] == "" || row[13] != expectedFingerprint { - return fmt.Errorf("UNSUPPORTED_ROUTINE_VERSION: restored SQL function %d head fingerprint does not match its active revision", functionID) + return moerr.NewInternalErrorNoCtxf("UNSUPPORTED_ROUTINE_VERSION: restored SQL function %d head fingerprint does not match its active revision", functionID) } continue } if language != udf.LanguagePython { - return fmt.Errorf("UNSUPPORTED_ROUTINE_VERSION: restored function %d has unsupported language %q", functionID, row[12]) + return moerr.NewInternalErrorNoCtxf("UNSUPPORTED_ROUTINE_VERSION: restored function %d has unsupported language %q", functionID, row[12]) } if row[5] == "" || row[6] == "" || row[7] == "" || row[8] == "" || row[9] == "" || row[10] == "" || row[11] == "" || row[13] == "" || !strings.EqualFold(row[14], "INVOKER") || !strings.EqualFold(row[15], "INVOKER") || !strings.EqualFold(row[14], row[15]) { - return fmt.Errorf("UNSUPPORTED_ROUTINE_VERSION: restored Python function %d has incomplete head identity", functionID) + return moerr.NewInternalErrorNoCtxf("UNSUPPORTED_ROUTINE_VERSION: restored Python function %d has incomplete head identity", functionID) } body, err := function.DecodePythonRoutineBody(row[11]) if err != nil { - return fmt.Errorf("UNSUPPORTED_ROUTINE_VERSION: restored Python function %d head body is invalid: %w", functionID, err) + return errutil.Wrapf(err, "UNSUPPORTED_ROUTINE_VERSION: restored Python function %d head body is invalid", functionID) } input, output, signature, err := function.PythonSignatureMetadata(body.ArgTypes, body.ReturnType) if err != nil { - return fmt.Errorf("UNSUPPORTED_ROUTINE_VERSION: restored Python function %d head signature is invalid: %w", functionID, err) + return errutil.Wrapf(err, "UNSUPPORTED_ROUTINE_VERSION: restored Python function %d head signature is invalid", functionID) } bodyFingerprint, fingerprintErr := function.PythonRoutineFingerprint(row[11]) if fingerprintErr != nil || row[13] != bodyFingerprint || row[5] != input || row[6] != output || row[7] != strconv.Itoa(udf.PythonSignatureKeySchemaVersion) || row[8] != signature || row[9] != input { - return fmt.Errorf("UNSUPPORTED_ROUTINE_VERSION: restored Python function %d head identity does not match its active revision", functionID) + return moerr.NewInternalErrorNoCtxf("UNSUPPORTED_ROUTINE_VERSION: restored Python function %d head identity does not match its active revision", functionID) } } return nil @@ -513,12 +514,12 @@ func publishRestoredPythonArtifacts( 0, 1, 2, ) if err != nil { - return fmt.Errorf("UNSUPPORTED_ROUTINE_VERSION: restored Python artifacts cannot be inspected: %w", err) + return errutil.Wrapf(err, "UNSUPPORTED_ROUTINE_VERSION: restored Python artifacts cannot be inspected") } needStore := false for rowIndex, row := range rows { if len(row) != 3 { - return fmt.Errorf("UNSUPPORTED_ROUTINE_VERSION: restored revision row %d has %d artifact columns", rowIndex, len(row)) + return moerr.NewInternalErrorNoCtxf("UNSUPPORTED_ROUTINE_VERSION: restored revision row %d has %d artifact columns", rowIndex, len(row)) } if row[0] == udf.LanguagePython { needStore = true @@ -542,10 +543,10 @@ func publishRestoredPythonArtifacts( } body, decodeErr := function.DecodePythonRoutineBody(row[2]) if decodeErr != nil { - return fmt.Errorf("UNSUPPORTED_ROUTINE_VERSION: restored Python artifact row %d is invalid: %w", rowIndex, decodeErr) + return errutil.Wrapf(decodeErr, "UNSUPPORTED_ROUTINE_VERSION: restored Python artifact row %d is invalid", rowIndex) } if body.ArtifactDigest != row[1] { - return fmt.Errorf("UNSUPPORTED_ROUTINE_VERSION: restored Python artifact row %d digest does not match its revision", rowIndex) + return moerr.NewInternalErrorNoCtxf("UNSUPPORTED_ROUTINE_VERSION: restored Python artifact row %d digest does not match its revision", rowIndex) } if _, publishErr := store.Publish(ctx, uint64(targetAccount), body.Handler, body.Source); publishErr != nil { return publishErr diff --git a/pkg/iceberg/api/import_boundary_test.go b/pkg/iceberg/api/import_boundary_test.go index d628a98ecfcb1..1b4faad0e293c 100644 --- a/pkg/iceberg/api/import_boundary_test.go +++ b/pkg/iceberg/api/import_boundary_test.go @@ -23,13 +23,42 @@ import ( "testing" ) -func TestIcebergAdapterImportBoundary(t *testing.T) { +type importBoundaryPolicy struct { + name string + importPrefixes []string + allowedDirectories []string + allowedFiles []string +} + +var importBoundaryPolicies = []importBoundaryPolicy{ + { + name: "Iceberg", + importPrefixes: []string{"github.com/apache/iceberg-go"}, + allowedDirectories: []string{ + "pkg/iceberg/adapter/iceberggo", + }, + }, + { + name: "Arrow", + importPrefixes: []string{ + "github.com/apache/arrow-go", + "github.com/apache/arrow/go", + }, + allowedDirectories: []string{ + "pkg/iceberg/adapter/iceberggo", + "pkg/container/arrowbridge", + "pkg/sql/colexec/external/arrowio", + "pkg/udf/python", + }, + allowedFiles: []string{ + "pkg/sql/colexec/external/reader_arrow.go", + "pkg/sql/compile/compile.go", + }, + }, +} + +func TestIcebergAndArrowImportBoundaries(t *testing.T) { root := findRepoRoot(t) - forbidden := []string{ - "github.com/apache/iceberg-go", - "github.com/apache/arrow-go", - "github.com/apache/arrow/go", - } err := filepath.WalkDir(root, func(path string, d os.DirEntry, walkErr error) error { if walkErr != nil { return walkErr @@ -52,20 +81,18 @@ func TestIcebergAdapterImportBoundary(t *testing.T) { if parseErr != nil { return parseErr } - slashPath := filepath.ToSlash(path) - // Arrow has two approved MatrixOne-owned boundaries: the Iceberg - // adapter and the static-file ingestion bridge. Everywhere else remains - // forbidden so an implementation detail cannot leak into SQL layers. - arrowAllowed := strings.Contains(slashPath, "/pkg/iceberg/adapter/iceberggo/") || - strings.Contains(slashPath, "/pkg/container/arrowbridge/") || - strings.Contains(slashPath, "/pkg/sql/colexec/external/arrowio/") || - strings.HasSuffix(slashPath, "/pkg/sql/colexec/external/reader_arrow.go") || - strings.HasSuffix(slashPath, "/pkg/sql/compile/compile.go") + relativePath, relErr := filepath.Rel(root, path) + if relErr != nil { + return relErr + } + slashPath := filepath.ToSlash(relativePath) for _, imp := range file.Imports { importPath := strings.Trim(imp.Path.Value, `"`) - for _, prefix := range forbidden { - if strings.HasPrefix(importPath, prefix) && !arrowAllowed { - t.Fatalf("forbidden Iceberg/Arrow import outside adapter: %s imports %s", slashPath, importPath) + for _, policy := range importBoundaryPolicies { + if importMatchesPrefix(importPath, policy.importPrefixes) && + !policy.allows(slashPath) { + t.Fatalf("forbidden %s import outside an approved owner: %s imports %s", + policy.name, slashPath, importPath) } } } @@ -76,6 +103,77 @@ func TestIcebergAdapterImportBoundary(t *testing.T) { } } +func (p importBoundaryPolicy) allows(relativePath string) bool { + for _, directory := range p.allowedDirectories { + if relativePath == directory || strings.HasPrefix(relativePath, directory+"/") { + return true + } + } + for _, file := range p.allowedFiles { + if relativePath == file { + return true + } + } + return false +} + +func importMatchesPrefix(importPath string, prefixes []string) bool { + for _, prefix := range prefixes { + if importPath == prefix || strings.HasPrefix(importPath, prefix+"/") { + return true + } + } + return false +} + +func TestImportBoundaryPolicies(t *testing.T) { + tests := []struct { + name string + policy importBoundaryPolicy + relativePath string + importPath string + allowed bool + }{ + { + name: "UDF owns Arrow but not Iceberg", + policy: importBoundaryPolicies[1], + relativePath: "pkg/udf/python/gateway.go", + importPath: "github.com/apache/arrow-go/v18/arrow/flight/gen/flight", + allowed: true, + }, + { + name: "UDF cannot own Iceberg", + policy: importBoundaryPolicies[0], + relativePath: "pkg/udf/python/gateway.go", + importPath: "github.com/apache/iceberg-go/io", + allowed: false, + }, + { + name: "Iceberg adapter owns Iceberg", + policy: importBoundaryPolicies[0], + relativePath: "pkg/iceberg/adapter/iceberggo/io.go", + importPath: "github.com/apache/iceberg-go/io", + allowed: true, + }, + { + name: "sibling package is not an Arrow owner", + policy: importBoundaryPolicies[1], + relativePath: "pkg/udf/pythonish/gateway.go", + importPath: "github.com/apache/arrow-go/v18/arrow", + allowed: false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + matches := importMatchesPrefix(tt.importPath, tt.policy.importPrefixes) + if matches && tt.policy.allows(tt.relativePath) != tt.allowed { + t.Fatalf("policy allowed=%v for %s importing %s, want %v", + tt.policy.allows(tt.relativePath), tt.relativePath, tt.importPath, tt.allowed) + } + }) + } +} + func TestIcebergPackagesUseMOErrorAndCauseAwareTimeouts(t *testing.T) { root := findRepoRoot(t) bareFmtErrorf := "fmt." + "Errorf(" diff --git a/pkg/sql/colexec/external_routine_eval.go b/pkg/sql/colexec/external_routine_eval.go index 0a7bc02b476df..993da7c51cdc9 100644 --- a/pkg/sql/colexec/external_routine_eval.go +++ b/pkg/sql/colexec/external_routine_eval.go @@ -16,7 +16,8 @@ package colexec import ( "encoding/json" - "fmt" + "github.com/matrixorigin/matrixone/pkg/common/moerr" + "github.com/matrixorigin/matrixone/pkg/util/errutil" "os" "sort" "strconv" @@ -72,13 +73,13 @@ func newExternalRoutineEval( allocation *vector.AllocationAccountSelection, ) (*ExternalRoutineEval, error) { if proc == nil || proc.Base == nil { - return nil, fmt.Errorf("python udf: evaluator has no process") + return nil, moerr.NewInternalErrorNoCtxf("python udf: evaluator has no process") } if err := validateRoutineCall(call); err != nil { return nil, err } if len(call.ArgumentTypes) != len(parameters) { - return nil, fmt.Errorf("python udf: typed routine has %d argument descriptors but %d bound arguments", len(call.ArgumentTypes), len(parameters)) + return nil, moerr.NewInternalErrorNoCtxf("python udf: typed routine has %d argument descriptors but %d bound arguments", len(call.ArgumentTypes), len(parameters)) } resultType := planTypeToSQL(call.ReturnType) result, err := vector.NewFunctionResultWrapperWithAllocation(resultType, proc.Mp(), allocation) @@ -99,69 +100,69 @@ func newExternalRoutineEval( func validateRoutineCall(call *planpb.RoutineCall) error { if call == nil || call.FunctionRef == nil || call.FunctionRef.FunctionId == 0 || call.FunctionRef.DatabaseId == 0 || call.FunctionRef.Revision == 0 || call.FunctionRef.NamespaceVersion == 0 { - return fmt.Errorf("UNSUPPORTED_ROUTINE_VERSION: typed routine call has no exact FunctionRef") + return moerr.NewInternalErrorNoCtxf("UNSUPPORTED_ROUTINE_VERSION: typed routine call has no exact FunctionRef") } if call.ReturnType.Id == int32(types.T_any) { - return fmt.Errorf("UNSUPPORTED_ROUTINE_VERSION: typed routine call has no return descriptor") + return moerr.NewInternalErrorNoCtxf("UNSUPPORTED_ROUTINE_VERSION: typed routine call has no return descriptor") } if call.ContractVersion != udf.PythonPlanContractVersion || call.Language != udf.LanguagePython { - return fmt.Errorf("UNSUPPORTED_ROUTINE_VERSION: unsupported typed routine call contract") + return moerr.NewInternalErrorNoCtxf("UNSUPPORTED_ROUTINE_VERSION: unsupported typed routine call contract") } if call.Volatility != "VOLATILE" { - return fmt.Errorf("UNSUPPORTED_ROUTINE_VERSION: Python routine must be VOLATILE") + return moerr.NewInternalErrorNoCtxf("UNSUPPORTED_ROUTINE_VERSION: Python routine must be VOLATILE") } if call.MayError != true || call.SecurityMode != "INVOKER" || call.Leakproof { - return fmt.Errorf("UNSUPPORTED_ROUTINE_VERSION: Python routine semantic contract is not supported") + return moerr.NewInternalErrorNoCtxf("UNSUPPORTED_ROUTINE_VERSION: Python routine semantic contract is not supported") } if call.CallsiteId == "" || len(call.CallsiteId) > 256 || strings.ContainsAny(call.CallsiteId, "\r\n") { - return fmt.Errorf("UNSUPPORTED_ROUTINE_VERSION: typed routine call has no valid callsite id") + return moerr.NewInternalErrorNoCtxf("UNSUPPORTED_ROUTINE_VERSION: typed routine call has no valid callsite id") } if len(call.Context) != 0 { - return fmt.Errorf("UNSUPPORTED_ROUTINE_VERSION: typed routine call contains execution context") + return moerr.NewInternalErrorNoCtxf("UNSUPPORTED_ROUTINE_VERSION: typed routine call contains execution context") } python := call.GetPython() if python == nil { - return fmt.Errorf("UNSUPPORTED_ROUTINE_VERSION: typed routine call has no Python implementation") + return moerr.NewInternalErrorNoCtxf("UNSUPPORTED_ROUTINE_VERSION: typed routine call has no Python implementation") } if python.Source != "" { - return fmt.Errorf("UNSUPPORTED_ROUTINE_VERSION: typed Python plan contains source; rebind from the current artifact contract") + return moerr.NewInternalErrorNoCtxf("UNSUPPORTED_ROUTINE_VERSION: typed Python plan contains source; rebind from the current artifact contract") } if python.Handler == "" || python.AbiContract != udf.PythonABIContract || python.AdapterVersion != udf.PythonAdapterVersion || python.SdkVersion != udf.PythonSDKVersion || python.DefinitionSchemaVersion != udf.PythonDefinitionSchemaVersion { - return fmt.Errorf("UNSUPPORTED_ROUTINE_VERSION: typed Python implementation contract is not supported") + return moerr.NewInternalErrorNoCtxf("UNSUPPORTED_ROUTINE_VERSION: typed Python implementation contract is not supported") } if python.Mode != "SCALAR" && python.Mode != "VECTOR" { - return fmt.Errorf("python udf: unsupported typed call mode %q", python.Mode) + return moerr.NewInternalErrorNoCtxf("python udf: unsupported typed call mode %q", python.Mode) } if python.NullPolicy != udf.NullCallHandler && python.NullPolicy != udf.NullReturnNull { - return fmt.Errorf("python udf: unsupported typed NULL policy %q", python.NullPolicy) + return moerr.NewInternalErrorNoCtxf("python udf: unsupported typed NULL policy %q", python.NullPolicy) } if call.NullPolicy != python.NullPolicy { - return fmt.Errorf("UNSUPPORTED_ROUTINE_VERSION: typed call NULL policy does not match its implementation") + return moerr.NewInternalErrorNoCtxf("UNSUPPORTED_ROUTINE_VERSION: typed call NULL policy does not match its implementation") } if !udf.IsSHA256Digest(python.ArtifactDigest) || !udf.IsSHA256Digest(python.EnvironmentDigest) { - return fmt.Errorf("UNSUPPORTED_ROUTINE_VERSION: typed Python implementation has invalid artifact/environment digest") + return moerr.NewInternalErrorNoCtxf("UNSUPPORTED_ROUTINE_VERSION: typed Python implementation has invalid artifact/environment digest") } if !udf.IsSHA256Digest(python.DefinitionFingerprint) { - return fmt.Errorf("UNSUPPORTED_ROUTINE_VERSION: typed Python implementation has invalid definition fingerprint") + return moerr.NewInternalErrorNoCtxf("UNSUPPORTED_ROUTINE_VERSION: typed Python implementation has invalid definition fingerprint") } arguments := make([]pythonudf.TypeDescriptor, len(call.ArgumentTypes)) for i, argumentType := range call.ArgumentTypes { if argumentType == nil { - return fmt.Errorf("UNSUPPORTED_ROUTINE_VERSION: typed Python argument %d is nil", i) + return moerr.NewInternalErrorNoCtxf("UNSUPPORTED_ROUTINE_VERSION: typed Python argument %d is nil", i) } descriptor, err := function.NewPythonTypeDescriptor(planTypeToSQL(*argumentType)) if err != nil { - return fmt.Errorf("UNSUPPORTED_ROUTINE_VERSION: typed Python argument %d is unsupported: %w", i, err) + return errutil.Wrapf(err, "UNSUPPORTED_ROUTINE_VERSION: typed Python argument %d is unsupported", i) } arguments[i] = descriptor } result, err := function.NewPythonTypeDescriptor(planTypeToSQL(call.ReturnType)) if err != nil { - return fmt.Errorf("UNSUPPORTED_ROUTINE_VERSION: typed Python return descriptor is unsupported: %w", err) + return errutil.Wrapf(err, "UNSUPPORTED_ROUTINE_VERSION: typed Python return descriptor is unsupported") } fingerprint, err := pythonudf.DefinitionFingerprint( int(python.DefinitionSchemaVersion), python.Handler, python.Mode, python.NullPolicy, @@ -169,10 +170,10 @@ func validateRoutineCall(call *planpb.RoutineCall) error { python.EnvironmentDigest, python.SdkVersion, arguments, result, ) if err != nil { - return fmt.Errorf("UNSUPPORTED_ROUTINE_VERSION: invalid typed Python definition: %w", err) + return errutil.Wrapf(err, "UNSUPPORTED_ROUTINE_VERSION: invalid typed Python definition") } if fingerprint != python.DefinitionFingerprint { - return fmt.Errorf("UNSUPPORTED_ROUTINE_VERSION: typed Python definition fingerprint mismatch") + return moerr.NewInternalErrorNoCtxf("UNSUPPORTED_ROUTINE_VERSION: typed Python definition fingerprint mismatch") } return nil @@ -184,12 +185,12 @@ func (e *ExternalRoutineEval) Eval(proc *process.Process, batches []*batch.Batch func (e *ExternalRoutineEval) eval(proc *process.Process, batches []*batch.Batch, selectList []bool, transfer bool) (output *vector.Vector, evalErr error) { if e == nil || proc == nil || proc.Base == nil { - return nil, fmt.Errorf("python udf: evaluator has no process") + return nil, moerr.NewInternalErrorNoCtxf("python udf: evaluator has no process") } e.executionMu.Lock() defer e.executionMu.Unlock() if e.freed { - return nil, fmt.Errorf("python udf: evaluator is freed") + return nil, moerr.NewInternalErrorNoCtxf("python udf: evaluator is freed") } // Transfer under executionMu, before a concurrent Eval/Free can reuse the // backing. The wrapper retains its allocation policy for the next result. @@ -207,7 +208,7 @@ func (e *ExternalRoutineEval) eval(proc *process.Process, batches []*batch.Batch return nil, err } if uint64(accountID) != e.call.FunctionRef.AccountId { - return nil, fmt.Errorf("python udf: FunctionRef account does not match the execution account") + return nil, moerr.NewInternalErrorNoCtxf("python udf: FunctionRef account does not match the execution account") } python := e.call.GetPython() rowCount, err := externalRoutineRowCount(python.Mode, batches) @@ -218,11 +219,7 @@ func (e *ExternalRoutineEval) eval(proc *process.Process, batches []*batch.Batch // early before this check would let a stale selection mask cross the // physical boundary and hide a relation-position bug in the caller. if selectList != nil && len(selectList) != rowCount { - return nil, fmt.Errorf( - "external routine selection has %d rows, expected %d", - len(selectList), - rowCount, - ) + return nil, moerr.NewInternalErrorNoCtxf("external routine selection has %d rows, expected %d", len(selectList), rowCount) } if rowCount == 0 { if err := e.result.PreExtendAndReset(0); err != nil { @@ -341,20 +338,16 @@ func (e *ExternalRoutineEval) eval(proc *process.Process, batches []*batch.Batch Tuple: tuple, } if proc.Base.UdfService == nil { - return nil, fmt.Errorf("RESOURCE_UNAVAILABLE: Python UDF runtime is not enabled") + return nil, moerr.NewInternalErrorNoCtxf("RESOURCE_UNAVAILABLE: Python UDF runtime is not enabled") } if err := proc.Base.UdfService.Execute(proc.Ctx, invocation, selectedResult, proc.Mp()); err != nil { return nil, err } if selectedResult.ResultLength() != len(e.selectedRows) { - return nil, fmt.Errorf( - "python udf: runtime produced %d result rows, expected %d", - selectedResult.ResultLength(), - len(e.selectedRows), - ) + return nil, moerr.NewInternalErrorNoCtxf("python udf: runtime produced %d result rows, expected %d", selectedResult.ResultLength(), len(e.selectedRows)) } if selectedResult.GetResultVector() == nil { - return nil, fmt.Errorf("python udf: runtime returned no result vector") + return nil, moerr.NewInternalErrorNoCtxf("python udf: runtime returned no result vector") } if len(e.selectedRows) == rowCount { return e.result.GetResultVector(), nil @@ -371,14 +364,14 @@ func (e *ExternalRoutineEval) eval(proc *process.Process, batches []*batch.Batch // that two calls belong to the same statement. func (e *ExternalRoutineEval) attachInvocationGroup(context map[string]string, queryID string) error { if context == nil { - return fmt.Errorf("python udf: invocation context is unavailable") + return moerr.NewInternalErrorNoCtxf("python udf: invocation context is unavailable") } e.groupMu.Lock() defer e.groupMu.Unlock() if queryID == "" { group, err := uuid.NewV7() if err != nil { - return fmt.Errorf("python udf: create execution group fence: %w", err) + return errutil.Wrapf(err, "python udf: create execution group fence") } context["group_id"] = "python/" + group.String() context["group_epoch"] = "1" @@ -387,14 +380,14 @@ func (e *ExternalRoutineEval) attachInvocationGroup(context map[string]string, q if e.groupID == "" || e.groupStatementID != queryID { group, err := uuid.NewV7() if err != nil { - return fmt.Errorf("python udf: create execution group fence: %w", err) + return errutil.Wrapf(err, "python udf: create execution group fence") } e.groupStatementID = queryID e.groupID = queryID + "/python/" + group.String() e.groupEpoch = 0 } if e.groupEpoch == ^uint64(0) { - return fmt.Errorf("RESOURCE_EXHAUSTED: Python UDF execution group epoch overflow") + return moerr.NewInternalErrorNoCtxf("RESOURCE_EXHAUSTED: Python UDF execution group epoch overflow") } e.groupEpoch++ context["group_id"] = e.groupID @@ -416,7 +409,7 @@ func (e *ExternalRoutineEval) compactParameters(proc *process.Process, rowCount e.selectedParameters = e.selectedParameters[:0] for i, input := range e.parameterResults { if input == nil { - return nil, fmt.Errorf("external routine parameter %d is nil", i) + return nil, moerr.NewInternalErrorNoCtxf("external routine parameter %d is nil", i) } if input.IsConst() || len(e.selectedRows) == rowCount { e.selectedParameters = append(e.selectedParameters, input) @@ -439,22 +432,22 @@ func (e *ExternalRoutineEval) compactParameters(proc *process.Process, rowCount func validateExternalRoutineInputs(inputs []*vector.Vector, call *planpb.RoutineCall, rowCount int) error { if len(inputs) != len(call.ArgumentTypes) { - return fmt.Errorf("python udf: evaluated input count %d does not match routine argument count %d", len(inputs), len(call.ArgumentTypes)) + return moerr.NewInternalErrorNoCtxf("python udf: evaluated input count %d does not match routine argument count %d", len(inputs), len(call.ArgumentTypes)) } for index, input := range inputs { if input == nil || input.GetType() == nil { - return fmt.Errorf("python udf: evaluated input vector %d has no type", index) + return moerr.NewInternalErrorNoCtxf("python udf: evaluated input vector %d has no type", index) } if input.Length() == 0 || (!input.IsConst() && input.Length() < rowCount) { - return fmt.Errorf("python udf: evaluated input vector %d has %d rows, expected at least %d", index, input.Length(), rowCount) + return moerr.NewInternalErrorNoCtxf("python udf: evaluated input vector %d has %d rows, expected at least %d", index, input.Length(), rowCount) } expected, err := function.NewPythonTypeDescriptor(planTypeToSQL(*call.ArgumentTypes[index])) if err != nil { - return fmt.Errorf("python udf: invalid argument descriptor %d: %w", index, err) + return errutil.Wrapf(err, "python udf: invalid argument descriptor %d", index) } actual, err := function.NewPythonTypeDescriptor(*input.GetType()) if err != nil { - return fmt.Errorf("python udf: evaluated input vector %d has unsupported type: %w", index, err) + return errutil.Wrapf(err, "python udf: evaluated input vector %d has unsupported type", index) } expectedFingerprint, err := expected.Fingerprint() if err != nil { @@ -465,7 +458,7 @@ func validateExternalRoutineInputs(inputs []*vector.Vector, call *planpb.Routine return err } if expectedFingerprint != actualFingerprint { - return fmt.Errorf("python udf: evaluated input vector %d type %s does not match the frozen descriptor", index, input.GetType().DescString()) + return moerr.NewInternalErrorNoCtxf("python udf: evaluated input vector %d type %s does not match the frozen descriptor", index, input.GetType().DescString()) } } return nil @@ -595,20 +588,20 @@ func externalRoutineRowCount(mode string, batches []*batch.Batch) (int, error) { rowCount := -1 for index, input := range batches { if input == nil { - return 0, fmt.Errorf("external routine input batch %d is nil", index) + return 0, moerr.NewInternalErrorNoCtxf("external routine input batch %d is nil", index) } if rowCount < 0 { rowCount = input.RowCount() continue } if input.RowCount() != rowCount { - return 0, fmt.Errorf("python udf: external routine input batch %d has %d rows, expected %d for the logical row domain", index, input.RowCount(), rowCount) + return 0, moerr.NewInternalErrorNoCtxf("python udf: external routine input batch %d has %d rows, expected %d for the logical row domain", index, input.RowCount(), rowCount) } } return rowCount, nil } if mode == "VECTOR" { - return 0, fmt.Errorf("python udf: zero-argument VECTOR requires an input batch with num_rows") + return 0, moerr.NewInternalErrorNoCtxf("python udf: zero-argument VECTOR requires an input batch with num_rows") } return 1, nil } @@ -619,7 +612,7 @@ func externalRoutineRowCount(mode string, batches []*batch.Batch) (int, error) { // prepared plan can outlive both. func buildRoutineContext(proc *process.Process) (map[string]string, error) { if proc == nil || proc.Base == nil { - return nil, fmt.Errorf("python udf: process is unavailable for statement context") + return nil, moerr.NewInternalErrorNoCtxf("python udf: process is unavailable for statement context") } context := make(map[string]string) // The current query id is stable for the statement and changes for every @@ -637,7 +630,7 @@ func buildRoutineContext(proc *process.Process) (map[string]string, error) { // the lifetime of that process; a real frontend statement always uses // its query start above. if proc.Base.UnixTime == 0 { - return nil, fmt.Errorf("python udf: statement timestamp is unavailable") + return nil, moerr.NewInternalErrorNoCtxf("python udf: statement timestamp is unavailable") } queryStart = time.Unix(0, proc.Base.UnixTime) } @@ -645,12 +638,12 @@ func buildRoutineContext(proc *process.Process) (map[string]string, error) { location := proc.GetSessionInfo().TimeZone if location == nil { - return nil, fmt.Errorf("python udf: session timezone is unavailable") + return nil, moerr.NewInternalErrorNoCtxf("python udf: session timezone is unavailable") } zoneName := location.String() var timezoneErr error if zoneName == "" || zoneName == "SYSTEM" { - return nil, fmt.Errorf("python udf: session timezone is not a stable IANA identity") + return nil, moerr.NewInternalErrorNoCtxf("python udf: session timezone is not a stable IANA identity") } if zoneName == "Local" { zoneName, location, timezoneErr = resolveSystemTimezone() @@ -661,7 +654,7 @@ func buildRoutineContext(proc *process.Process) (map[string]string, error) { if zoneName == "FixedZone" { _, offset := queryStart.In(location).Zone() if offset%60 != 0 { - return nil, fmt.Errorf("python udf: fixed session timezone offset must be a whole number of minutes") + return nil, moerr.NewInternalErrorNoCtxf("python udf: fixed session timezone offset must be a whole number of minutes") } context["session_timezone_kind"] = "FIXED_OFFSET" context["session_timezone_offset_minutes"] = formatTimezoneOffsetMinutes(offset / 60) @@ -681,7 +674,7 @@ func buildRoutineContext(proc *process.Process) (map[string]string, error) { mode := sessionSQLMode(proc) encodedMode, err := json.Marshal(mode) if err != nil { - return nil, fmt.Errorf("python udf: encode sql_mode context: %w", err) + return nil, errutil.Wrapf(err, "python udf: encode sql_mode context") } context["sql_mode"] = string(encodedMode) info := proc.GetSessionInfo() @@ -727,7 +720,7 @@ func resolveSystemTimezone() (string, *time.Location, error) { return candidate, location, nil } } - return "", nil, fmt.Errorf("python udf: SYSTEM timezone has no stable IANA identity") + return "", nil, moerr.NewInternalErrorNoCtxf("python udf: SYSTEM timezone has no stable IANA identity") } func formatTimezoneOffsetMinutes(offset int) string { diff --git a/pkg/sql/plan/function/python_udf.go b/pkg/sql/plan/function/python_udf.go index 576fa6dc27615..af8f2fa811e6c 100644 --- a/pkg/sql/plan/function/python_udf.go +++ b/pkg/sql/plan/function/python_udf.go @@ -15,7 +15,8 @@ package function import ( - "fmt" + "github.com/matrixorigin/matrixone/pkg/common/moerr" + "github.com/matrixorigin/matrixone/pkg/util/errutil" "strconv" "github.com/google/uuid" @@ -175,42 +176,42 @@ func pythonUdfRetType(parameters []types.Type) types.Type { // historical overload id. Long-term execution enters through the typed // RoutineCall field and ExternalRoutineEval. func rejectPythonJSONPlan(_ []*vector.Vector, _ vector.FunctionResultWrapper, _ *process.Process, _ int, _ *FunctionSelectList) error { - return fmt.Errorf("UNSUPPORTED_ROUTINE_VERSION: Python JSON plan execution is not supported; reprepare the statement") + return moerr.NewInternalErrorNoCtxf("UNSUPPORTED_ROUTINE_VERSION: Python JSON plan execution is not supported; reprepare the statement") } func validatePythonRoutineDescriptor(descriptor *vector.Vector) error { if descriptor == nil { - return fmt.Errorf("python udf: missing routine descriptor") + return moerr.NewInternalErrorNoCtxf("python udf: missing routine descriptor") } if !descriptor.IsConst() { - return fmt.Errorf("python udf: routine descriptor must be constant") + return moerr.NewInternalErrorNoCtxf("python udf: routine descriptor must be constant") } if descriptor.Length() == 0 { - return fmt.Errorf("python udf: routine descriptor is empty") + return moerr.NewInternalErrorNoCtxf("python udf: routine descriptor is empty") } return nil } func validatePythonInputVectors(inputs []*vector.Vector, args []types.Type, length int) error { if len(inputs) != len(args) { - return fmt.Errorf("python udf: input column count %d does not match routine argument count %d", len(inputs), len(args)) + return moerr.NewInternalErrorNoCtxf("python udf: input column count %d does not match routine argument count %d", len(inputs), len(args)) } for index, input := range inputs { if input == nil { - return fmt.Errorf("python udf: input vector %d is nil", index) + return moerr.NewInternalErrorNoCtxf("python udf: input vector %d is nil", index) } if input.Length() == 0 { - return fmt.Errorf("python udf: input vector %d is empty", index) + return moerr.NewInternalErrorNoCtxf("python udf: input vector %d is empty", index) } if !input.IsConst() && input.Length() < length { - return fmt.Errorf("python udf: input vector %d is shorter than invocation length", index) + return moerr.NewInternalErrorNoCtxf("python udf: input vector %d is shorter than invocation length", index) } if input.GetType() == nil || !pythonTypesEqual(*input.GetType(), args[index]) { actual := "" if input.GetType() != nil { actual = input.GetType().String() } - return fmt.Errorf("python udf: input vector %d type %s does not match %s", index, actual, args[index].String()) + return moerr.NewInternalErrorNoCtxf("python udf: input vector %d type %s does not match %s", index, actual, args[index].String()) } } return nil @@ -233,13 +234,13 @@ func invocationTuple(context map[string]string, queryID string, accountID uint32 if statementID == "" { id, err := uuid.NewV7() if err != nil { - return protocol.FencingTuple{}, fmt.Errorf("python udf: create statement fence: %w", err) + return protocol.FencingTuple{}, errutil.Wrapf(err, "python udf: create statement fence") } statementID = id.String() } invocationID, err := uuid.NewV7() if err != nil { - return protocol.FencingTuple{}, fmt.Errorf("python udf: create invocation fence: %w", err) + return protocol.FencingTuple{}, errutil.Wrapf(err, "python udf: create invocation fence") } groupID := context["group_id"] if groupID == "" { @@ -249,7 +250,7 @@ func invocationTuple(context map[string]string, queryID string, accountID uint32 // explicit epoch when it is introduced. groupID = statementID + "/python/" + invocationID.String() } else if context["group_epoch"] == "" { - return protocol.FencingTuple{}, fmt.Errorf("python udf: explicit group_id requires group_epoch") + return protocol.FencingTuple{}, moerr.NewInternalErrorNoCtxf("python udf: explicit group_id requires group_epoch") } groupEpoch, err := positiveContextUint(context, "group_epoch", 1) if err != nil { @@ -283,7 +284,7 @@ func positiveContextUint(context map[string]string, key string, fallback uint64) } parsed, err := strconv.ParseUint(value, 10, 64) if err != nil || parsed == 0 { - return 0, fmt.Errorf("python udf: invalid %s %q", key, value) + return 0, moerr.NewInternalErrorNoCtxf("python udf: invalid %s %q", key, value) } return parsed, nil } diff --git a/pkg/sql/plan/function/udf.go b/pkg/sql/plan/function/udf.go index a60c78cc0250d..e102ce3b9cabd 100644 --- a/pkg/sql/plan/function/udf.go +++ b/pkg/sql/plan/function/udf.go @@ -18,7 +18,8 @@ import ( "crypto/sha256" "encoding/hex" "encoding/json" - "fmt" + "github.com/matrixorigin/matrixone/pkg/common/moerr" + "github.com/matrixorigin/matrixone/pkg/util/errutil" "io" "reflect" "strings" @@ -100,29 +101,29 @@ func PythonSignatureMetadata( returnType *PythonTypeDescriptor, ) (canonicalInput, canonicalReturn, fingerprint string, err error) { if returnType == nil { - return "", "", "", fmt.Errorf("python routine is missing return descriptor") + return "", "", "", moerr.NewInternalErrorNoCtxf("python routine is missing return descriptor") } if args == nil { args = []PythonTypeDescriptor{} } for index, descriptor := range args { if err := validatePythonTypeDescriptor(descriptor); err != nil { - return "", "", "", fmt.Errorf("python routine argument descriptor %d: %w", index, err) + return "", "", "", errutil.Wrapf(err, "python routine argument descriptor %d", index) } } if err := validatePythonTypeDescriptor(*returnType); err != nil { - return "", "", "", fmt.Errorf("python routine return descriptor: %w", err) + return "", "", "", errutil.Wrapf(err, "python routine return descriptor") } inputBytes, err := json.Marshal(args) if err != nil { - return "", "", "", fmt.Errorf("marshal Python input descriptor: %w", err) + return "", "", "", errutil.Wrapf(err, "marshal Python input descriptor") } returnBytes, err := json.Marshal(returnType) if err != nil { - return "", "", "", fmt.Errorf("marshal Python return descriptor: %w", err) + return "", "", "", errutil.Wrapf(err, "marshal Python return descriptor") } if len(inputBytes) > types.MaxStringSize || len(returnBytes) > types.MaxStringSize { - return "", "", "", fmt.Errorf("python signature descriptor exceeds the %d-byte catalog limit", types.MaxStringSize) + return "", "", "", moerr.NewInternalErrorNoCtxf("python signature descriptor exceeds the %d-byte catalog limit", types.MaxStringSize) } keyBytes, err := json.Marshal(struct { SchemaVersion int `json:"schema_version"` @@ -134,7 +135,7 @@ func PythonSignatureMetadata( Return: *returnType, }) if err != nil { - return "", "", "", fmt.Errorf("marshal Python signature key: %w", err) + return "", "", "", errutil.Wrapf(err, "marshal Python signature key") } digest := sha256.New() digest.Write([]byte("matrixone-python-udf-signature\x00")) @@ -156,7 +157,7 @@ func DecodePythonRoutineBody(raw string) (PythonRoutineBody, error) { var extra json.RawMessage if err := decoder.Decode(&extra); err != io.EOF { if err == nil { - return PythonRoutineBody{}, fmt.Errorf("python routine body contains multiple JSON values") + return PythonRoutineBody{}, moerr.NewInternalErrorNoCtxf("python routine body contains multiple JSON values") } return PythonRoutineBody{}, err } @@ -199,7 +200,7 @@ func PythonRoutineBodyFingerprint(body PythonRoutineBody) (string, error) { // independently hashing source text. func SQLRoutineFingerprint(body, argTypes, returnType string) (string, error) { if body == "" || returnType == "" { - return "", fmt.Errorf("SQL routine definition is incomplete") + return "", moerr.NewInternalErrorNoCtxf("SQL routine definition is incomplete") } encoded, err := json.Marshal(struct { SchemaVersion int `json:"schema_version"` @@ -213,7 +214,7 @@ func SQLRoutineFingerprint(body, argTypes, returnType string) (string, error) { ReturnType: returnType, }) if err != nil { - return "", fmt.Errorf("marshal SQL routine definition: %w", err) + return "", errutil.Wrapf(err, "marshal SQL routine definition") } digest := sha256.New() digest.Write([]byte("matrixone-sql-udf-definition\x00")) @@ -227,7 +228,7 @@ func SQLRoutineFingerprint(body, argTypes, returnType string) (string, error) { // catalog contract instead of decoding and dispatching this payload. func DecodeUdfWithContext(raw []byte) (UdfWithContext, error) { _ = raw - return UdfWithContext{}, fmt.Errorf("UNSUPPORTED_ROUTINE_VERSION: Python JSON plan is not executable; rebind the statement from the current typed catalog contract") + return UdfWithContext{}, moerr.NewInternalErrorNoCtxf("UNSUPPORTED_ROUTINE_VERSION: Python JSON plan is not executable; rebind the statement from the current typed catalog contract") } // Validate checks fields that are independent of the catalog argument list. @@ -235,55 +236,55 @@ func DecodeUdfWithContext(raw []byte) (UdfWithContext, error) { // legacy catalog args column has been decoded. func (body PythonRoutineBody) Validate() error { if body.DefinitionSchemaVersion != udf.PythonDefinitionSchemaVersion { - return fmt.Errorf("UNSUPPORTED_ROUTINE_VERSION: python definition schema %d is not supported", body.DefinitionSchemaVersion) + return moerr.NewInternalErrorNoCtxf("UNSUPPORTED_ROUTINE_VERSION: python definition schema %d is not supported", body.DefinitionSchemaVersion) } if body.Handler == "" || body.Source == "" { - return fmt.Errorf("python routine handler and source are required") + return moerr.NewInternalErrorNoCtxf("python routine handler and source are required") } if !utf8.ValidString(body.Source) { - return fmt.Errorf("UNSUPPORTED_ROUTINE_VERSION: Python source is not valid UTF-8") + return moerr.NewInternalErrorNoCtxf("UNSUPPORTED_ROUTINE_VERSION: Python source is not valid UTF-8") } if int64(len([]byte(body.Source))) > pythonudf.DefaultMaxArtifactBytes { - return fmt.Errorf("RESOURCE_EXHAUSTED: Python source exceeds %d bytes", pythonudf.DefaultMaxArtifactBytes) + return moerr.NewInternalErrorNoCtxf("RESOURCE_EXHAUSTED: Python source exceeds %d bytes", pythonudf.DefaultMaxArtifactBytes) } if strings.Contains(body.Handler, ":") { - return fmt.Errorf("UNSUPPORTED_ROUTINE_VERSION: Python external handler import requires an immutable artifact catalog") + return moerr.NewInternalErrorNoCtxf("UNSUPPORTED_ROUTINE_VERSION: Python external handler import requires an immutable artifact catalog") } if body.Mode != "SCALAR" && body.Mode != "VECTOR" { - return fmt.Errorf("python routine has unsupported mode %q", body.Mode) + return moerr.NewInternalErrorNoCtxf("python routine has unsupported mode %q", body.Mode) } if body.NullPolicy != udf.NullCallHandler && body.NullPolicy != udf.NullReturnNull { - return fmt.Errorf("python routine has unsupported NULL policy %q", body.NullPolicy) + return moerr.NewInternalErrorNoCtxf("python routine has unsupported NULL policy %q", body.NullPolicy) } if body.ABIContract != udf.PythonABIContract || body.AdapterVersion != udf.PythonAdapterVersion { - return fmt.Errorf("python routine has unsupported ABI contract %q/%q", body.ABIContract, body.AdapterVersion) + return moerr.NewInternalErrorNoCtxf("python routine has unsupported ABI contract %q/%q", body.ABIContract, body.AdapterVersion) } if body.SDKVersion != udf.PythonSDKVersion { - return fmt.Errorf("python routine has unsupported SDK %q", body.SDKVersion) + return moerr.NewInternalErrorNoCtxf("python routine has unsupported SDK %q", body.SDKVersion) } if !udf.IsSHA256Digest(body.ArtifactDigest) || body.EnvironmentDigest == "" { - return fmt.Errorf("UNSUPPORTED_ROUTINE_VERSION: Python definition has no immutable artifact/environment digest; use DROP and CREATE") + return moerr.NewInternalErrorNoCtxf("UNSUPPORTED_ROUTINE_VERSION: Python definition has no immutable artifact/environment digest; use DROP and CREATE") } if body.ArtifactDigest != udf.PythonInlineArtifactDigest(body.Handler, body.Source) { - return fmt.Errorf("python routine artifact digest does not match its source") + return moerr.NewInternalErrorNoCtxf("python routine artifact digest does not match its source") } environmentDigest, err := udf.PythonEnvironmentDigest() if err != nil { - return fmt.Errorf("python routine environment digest is unavailable: %w", err) + return errutil.Wrapf(err, "python routine environment digest is unavailable") } if body.EnvironmentDigest != environmentDigest { - return fmt.Errorf("python routine environment digest does not match the current contract") + return moerr.NewInternalErrorNoCtxf("python routine environment digest does not match the current contract") } if body.ReturnType == nil { - return fmt.Errorf("python routine is missing return descriptor") + return moerr.NewInternalErrorNoCtxf("python routine is missing return descriptor") } for i, descriptor := range body.ArgTypes { if err := validatePythonTypeDescriptor(descriptor); err != nil { - return fmt.Errorf("python routine argument descriptor %d: %w", i, err) + return errutil.Wrapf(err, "python routine argument descriptor %d", i) } } if err := validatePythonTypeDescriptor(*body.ReturnType); err != nil { - return fmt.Errorf("python routine return descriptor: %w", err) + return errutil.Wrapf(err, "python routine return descriptor") } return nil } @@ -309,16 +310,16 @@ type Arg struct { // process at execution time. func (u *Udf) GetRoutineCall() (*plan.RoutineCall, error) { if u == nil { - return nil, fmt.Errorf("routine call requires a non-nil routine") + return nil, moerr.NewInternalErrorNoCtxf("routine call requires a non-nil routine") } if u.Language == udf.LanguageSQL { return u.getSQLRoutineCall() } if u.Language != udf.LanguagePython { - return nil, fmt.Errorf("routine call has unsupported language %q", u.Language) + return nil, moerr.NewInternalErrorNoCtxf("routine call has unsupported language %q", u.Language) } if u.FunctionID <= 0 || u.DatabaseID == 0 || u.Revision == 0 || u.NamespaceVersion == 0 { - return nil, fmt.Errorf("python routine has no stable catalog identity or revision") + return nil, moerr.NewInternalErrorNoCtxf("python routine has no stable catalog identity or revision") } body, err := DecodePythonRoutineBody(u.Body) if err != nil { @@ -380,10 +381,10 @@ func (u *Udf) GetRoutineCall() (*plan.RoutineCall, error) { // plan dependency so replace/drop cannot silently reuse the old definition. func (u *Udf) getSQLRoutineCall() (*plan.RoutineCall, error) { if u.FunctionID <= 0 || u.DatabaseID == 0 || u.Revision == 0 || u.NamespaceVersion == 0 { - return nil, fmt.Errorf("SQL routine has no stable catalog identity or revision") + return nil, moerr.NewInternalErrorNoCtxf("SQL routine has no stable catalog identity or revision") } if u.SemanticDefinitionSchemaVersion != udf.SQLDefinitionSchemaVersion || !udf.IsSHA256Digest(u.DefinitionFingerprint) { - return nil, fmt.Errorf("UNSUPPORTED_ROUTINE_VERSION: SQL routine has no supported semantic definition") + return nil, moerr.NewInternalErrorNoCtxf("UNSUPPORTED_ROUTINE_VERSION: SQL routine has no supported semantic definition") } argTypes := u.GetArgsType() arguments := make([]*plan.Type, len(argTypes)) @@ -470,7 +471,7 @@ func (u *Udf) LoadPythonTypeContract() error { return nil } if u.Language != udf.LanguagePython { - return fmt.Errorf("UNSUPPORTED_ROUTINE_VERSION: unsupported routine language %q", u.Language) + return moerr.NewInternalErrorNoCtxf("UNSUPPORTED_ROUTINE_VERSION: unsupported routine language %q", u.Language) } u.PythonArgTypes = nil u.PythonReturnType = nil @@ -493,17 +494,17 @@ func (u *Udf) ValidatePythonTypeContract() error { return nil } if u.Language != udf.LanguagePython { - return fmt.Errorf("UNSUPPORTED_ROUTINE_VERSION: unsupported routine language %q", u.Language) + return moerr.NewInternalErrorNoCtxf("UNSUPPORTED_ROUTINE_VERSION: unsupported routine language %q", u.Language) } if u.PythonReturnType == nil { - return fmt.Errorf("python routine is missing return descriptor") + return moerr.NewInternalErrorNoCtxf("python routine is missing return descriptor") } if len(u.PythonArgTypes) != len(u.Args) { - return fmt.Errorf("python routine has %d descriptors for %d arguments", len(u.PythonArgTypes), len(u.Args)) + return moerr.NewInternalErrorNoCtxf("python routine has %d descriptors for %d arguments", len(u.PythonArgTypes), len(u.Args)) } for i, descriptor := range u.PythonArgTypes { if err := validatePythonTypeDescriptor(descriptor); err != nil { - return fmt.Errorf("python routine argument descriptor %d: %w", i, err) + return errutil.Wrapf(err, "python routine argument descriptor %d", i) } } return validatePythonTypeDescriptor(*u.PythonReturnType) @@ -520,10 +521,10 @@ func (u *Udf) ValidatePythonCatalogSignature() error { return nil } if u.Language != udf.LanguagePython { - return fmt.Errorf("UNSUPPORTED_ROUTINE_VERSION: unsupported routine language %q", u.Language) + return moerr.NewInternalErrorNoCtxf("UNSUPPORTED_ROUTINE_VERSION: unsupported routine language %q", u.Language) } if len(u.Args) != len(u.PythonArgTypes) || u.PythonReturnType == nil { - return fmt.Errorf("UNSUPPORTED_ROUTINE_VERSION: Python catalog signature is incomplete") + return moerr.NewInternalErrorNoCtxf("UNSUPPORTED_ROUTINE_VERSION: Python catalog signature is incomplete") } for index, argument := range u.Args { if argument == nil || !pythonCatalogTypeNameMatches(argument.Type, u.PythonArgTypes[index].Type().Oid) { @@ -531,11 +532,11 @@ func (u *Udf) ValidatePythonCatalogSignature() error { if argument != nil { name = argument.Type } - return fmt.Errorf("UNSUPPORTED_ROUTINE_VERSION: Python catalog argument %d type %q does not match its descriptor", index+1, name) + return moerr.NewInternalErrorNoCtxf("UNSUPPORTED_ROUTINE_VERSION: Python catalog argument %d type %q does not match its descriptor", index+1, name) } } if !pythonCatalogTypeNameMatches(u.RetType, u.PythonReturnType.Type().Oid) { - return fmt.Errorf("UNSUPPORTED_ROUTINE_VERSION: Python catalog return type %q does not match its descriptor", u.RetType) + return moerr.NewInternalErrorNoCtxf("UNSUPPORTED_ROUTINE_VERSION: Python catalog return type %q does not match its descriptor", u.RetType) } return nil } @@ -564,10 +565,10 @@ func validatePythonTypeDescriptor(descriptor PythonTypeDescriptor) error { return err } if !reflect.DeepEqual(canonical, descriptor) { - return fmt.Errorf("descriptor is not canonical for %s", typ.String()) + return moerr.NewInternalErrorNoCtxf("descriptor is not canonical for %s", typ.String()) } if _, err := descriptor.Fingerprint(); err != nil { - return fmt.Errorf("descriptor has no valid Arrow physical type: %w", err) + return errutil.Wrapf(err, "descriptor has no valid Arrow physical type") } return nil } diff --git a/pkg/sql/plan/routine_dependency.go b/pkg/sql/plan/routine_dependency.go index a1d34f8950311..aadb7307d7ee0 100644 --- a/pkg/sql/plan/routine_dependency.go +++ b/pkg/sql/plan/routine_dependency.go @@ -16,6 +16,7 @@ package plan import ( "fmt" + "github.com/matrixorigin/matrixone/pkg/common/moerr" "strings" planpb "github.com/matrixorigin/matrixone/pkg/pb/plan" @@ -36,16 +37,16 @@ func (builder *QueryBuilder) assignRoutineCallsite(call *planpb.RoutineCall) err return nil } if call.Language != udf.LanguageSQL && call.Language != udf.LanguagePython { - return fmt.Errorf("UNSUPPORTED_ROUTINE_VERSION: routine call language %q is not canonical", call.Language) + return moerr.NewInternalErrorNoCtxf("UNSUPPORTED_ROUTINE_VERSION: routine call language %q is not canonical", call.Language) } if call.CallsiteId != "" { if len(call.CallsiteId) > 256 || strings.ContainsAny(call.CallsiteId, "\r\n") { - return fmt.Errorf("PROGRAM_LIMIT_EXCEEDED: routine callsite id is too large or invalid") + return moerr.NewInternalErrorNoCtxf("PROGRAM_LIMIT_EXCEEDED: routine callsite id is too large or invalid") } return nil } if builder.nextRoutineCallID == ^uint64(0) { - return fmt.Errorf("PROGRAM_LIMIT_EXCEEDED: routine callsite id exhausted") + return moerr.NewInternalErrorNoCtxf("PROGRAM_LIMIT_EXCEEDED: routine callsite id exhausted") } builder.nextRoutineCallID++ call.CallsiteId = fmt.Sprintf("%s/%d", strings.ToLower(call.Language), builder.nextRoutineCallID) @@ -57,7 +58,7 @@ func (builder *QueryBuilder) recordRoutinePlanDependency(call *planpb.RoutineCal return nil } if call.Language != udf.LanguageSQL && call.Language != udf.LanguagePython { - return fmt.Errorf("UNSUPPORTED_ROUTINE_VERSION: routine call language %q is not canonical", call.Language) + return moerr.NewInternalErrorNoCtxf("UNSUPPORTED_ROUTINE_VERSION: routine call language %q is not canonical", call.Language) } ref := call.FunctionRef dependency := &planpb.RoutinePlanDependency{ @@ -94,7 +95,7 @@ func (builder *QueryBuilder) recordRoutinePlanDependency(call *planpb.RoutineCal } } if len(builder.qry.RoutineDependencies) >= maxRoutinePlanDependencies { - return fmt.Errorf("PROGRAM_LIMIT_EXCEEDED: routine dependency count exceeds %d", maxRoutinePlanDependencies) + return moerr.NewInternalErrorNoCtxf("PROGRAM_LIMIT_EXCEEDED: routine dependency count exceeds %d", maxRoutinePlanDependencies) } builder.qry.RoutineDependencies = append(builder.qry.RoutineDependencies, dependency) return nil diff --git a/pkg/udf/protocol/protocol.go b/pkg/udf/protocol/protocol.go index 2ada4e2800423..405a9bd590604 100644 --- a/pkg/udf/protocol/protocol.go +++ b/pkg/udf/protocol/protocol.go @@ -23,7 +23,8 @@ import ( "encoding/hex" "encoding/json" "errors" - "fmt" + "github.com/matrixorigin/matrixone/pkg/common/moerr" + "github.com/matrixorigin/matrixone/pkg/util/errutil" "io" "sync" "time" @@ -51,12 +52,12 @@ const ( ) var ( - ErrProtocol = errors.New("python udf protocol violation") - ErrSequence = errors.New("python udf sequence violation") - ErrGroupClosed = errors.New("python udf execution group is closed") - ErrDuplicate = errors.New("python udf identity is already present") - ErrLedgerFull = errors.New("python udf terminal ledger is full") - ErrUnknownIdentity = errors.New("python udf identity is unknown") + ErrProtocol = moerr.NewInternalErrorNoCtx("python udf protocol violation") + ErrSequence = moerr.NewInternalErrorNoCtx("python udf sequence violation") + ErrGroupClosed = moerr.NewInternalErrorNoCtx("python udf execution group is closed") + ErrDuplicate = moerr.NewInternalErrorNoCtx("python udf identity is already present") + ErrLedgerFull = moerr.NewInternalErrorNoCtx("python udf terminal ledger is full") + ErrUnknownIdentity = moerr.NewInternalErrorNoCtx("python udf identity is unknown") ) // FencingTuple is carried by every invocation control and data-plane @@ -77,13 +78,13 @@ func (t FencingTuple) Validate() error { // never be zero or empty. if t.StatementID == "" || t.GroupID == "" || t.GroupEpoch == 0 || t.InvocationID == "" || t.LeaseEpoch == 0 { - return fmt.Errorf("%w: incomplete fencing tuple", ErrProtocol) + return errutil.WrapfCauseFirst(ErrProtocol, "incomplete fencing tuple") } if !utf8.ValidString(t.StatementID) || !utf8.ValidString(t.GroupID) || !utf8.ValidString(t.InvocationID) { - return fmt.Errorf("%w: fencing tuple contains invalid UTF-8", ErrProtocol) + return errutil.WrapfCauseFirst(ErrProtocol, "fencing tuple contains invalid UTF-8") } if len(t.StatementID) > MaxFenceComponentBytes || len(t.GroupID) > MaxFenceComponentBytes || len(t.InvocationID) > MaxFenceComponentBytes { - return fmt.Errorf("%w: fencing tuple component is too large", ErrProtocol) + return errutil.WrapfCauseFirst(ErrProtocol, "fencing tuple component is too large") } return nil } @@ -133,7 +134,7 @@ var controlBaseFields = map[string]struct{}{ func validateControlFields(control Control, wire map[string]json.RawMessage) error { rule, ok := controlFieldRules[control.Kind] if !ok { - return fmt.Errorf("%w: unsupported control kind %q", ErrProtocol, control.Kind) + return errutil.WrapfCauseFirst(ErrProtocol, "unsupported control kind %q", control.Kind) } if wire != nil { for field := range wire { @@ -141,106 +142,106 @@ func validateControlFields(control Control, wire map[string]json.RawMessage) err continue } if _, allowed := rule.allowed[field]; !allowed { - return fmt.Errorf("%w: field %q is not valid for control kind %q", ErrProtocol, field, control.Kind) + return errutil.WrapfCauseFirst(ErrProtocol, "field %q is not valid for control kind %q", field, control.Kind) } } for field := range rule.required { if _, present := wire[field]; !present { - return fmt.Errorf("%w: control kind %q is missing field %q", ErrProtocol, control.Kind, field) + return errutil.WrapfCauseFirst(ErrProtocol, "control kind %q is missing field %q", control.Kind, field) } } } if control.Sequence != 0 { if _, allowed := rule.allowed["sequence"]; !allowed { - return fmt.Errorf("%w: field %q is not valid for control kind %q", ErrProtocol, "sequence", control.Kind) + return errutil.WrapfCauseFirst(ErrProtocol, "field %q is not valid for control kind %q", "sequence", control.Kind) } } if control.LastSequence != 0 { if _, allowed := rule.allowed["last_sequence"]; !allowed { - return fmt.Errorf("%w: field %q is not valid for control kind %q", ErrProtocol, "last_sequence", control.Kind) + return errutil.WrapfCauseFirst(ErrProtocol, "field %q is not valid for control kind %q", "last_sequence", control.Kind) } } if control.AckSequence != 0 { if _, allowed := rule.allowed["ack_sequence"]; !allowed { - return fmt.Errorf("%w: field %q is not valid for control kind %q", ErrProtocol, "ack_sequence", control.Kind) + return errutil.WrapfCauseFirst(ErrProtocol, "field %q is not valid for control kind %q", "ack_sequence", control.Kind) } } if control.ReleasedBytes != 0 { if _, allowed := rule.allowed["released_bytes"]; !allowed { - return fmt.Errorf("%w: field %q is not valid for control kind %q", ErrProtocol, "released_bytes", control.Kind) + return errutil.WrapfCauseFirst(ErrProtocol, "field %q is not valid for control kind %q", "released_bytes", control.Kind) } } if control.ReleasedBatches != 0 { if _, allowed := rule.allowed["released_batches"]; !allowed { - return fmt.Errorf("%w: field %q is not valid for control kind %q", ErrProtocol, "released_batches", control.Kind) + return errutil.WrapfCauseFirst(ErrProtocol, "field %q is not valid for control kind %q", "released_batches", control.Kind) } } if control.LastResultSequence != 0 { if _, allowed := rule.allowed["last_result_sequence"]; !allowed { - return fmt.Errorf("%w: field %q is not valid for control kind %q", ErrProtocol, "last_result_sequence", control.Kind) + return errutil.WrapfCauseFirst(ErrProtocol, "field %q is not valid for control kind %q", "last_result_sequence", control.Kind) } } if control.FinishID != "" { if _, allowed := rule.allowed["finish_id"]; !allowed { - return fmt.Errorf("%w: field %q is not valid for control kind %q", ErrProtocol, "finish_id", control.Kind) + return errutil.WrapfCauseFirst(ErrProtocol, "field %q is not valid for control kind %q", "finish_id", control.Kind) } } if control.Status != "" { if _, allowed := rule.allowed["status"]; !allowed { - return fmt.Errorf("%w: field %q is not valid for control kind %q", ErrProtocol, "status", control.Kind) + return errutil.WrapfCauseFirst(ErrProtocol, "field %q is not valid for control kind %q", "status", control.Kind) } } if control.Reason != "" { if _, allowed := rule.allowed["reason"]; !allowed { - return fmt.Errorf("%w: field %q is not valid for control kind %q", ErrProtocol, "reason", control.Kind) + return errutil.WrapfCauseFirst(ErrProtocol, "field %q is not valid for control kind %q", "reason", control.Kind) } } if len(control.Payload) != 0 { if _, allowed := rule.allowed["payload"]; !allowed { - return fmt.Errorf("%w: field %q is not valid for control kind %q", ErrProtocol, "payload", control.Kind) + return errutil.WrapfCauseFirst(ErrProtocol, "field %q is not valid for control kind %q", "payload", control.Kind) } } switch control.Kind { case "OpenInvocation": if len(control.Payload) == 0 && wire == nil { - return fmt.Errorf("%w: control kind %q is missing field %q", ErrProtocol, control.Kind, "payload") + return errutil.WrapfCauseFirst(ErrProtocol, "control kind %q is missing field %q", control.Kind, "payload") } if len(control.Payload) != 0 { var payload map[string]json.RawMessage if err := json.Unmarshal(control.Payload, &payload); err != nil || payload == nil { - return fmt.Errorf("%w: control kind %q payload must be a JSON object", ErrProtocol, control.Kind) + return errutil.WrapfCauseFirst(ErrProtocol, "control kind %q payload must be a JSON object", control.Kind) } if err := rejectDuplicateJSONKeys(control.Payload); err != nil { - return fmt.Errorf("%w: invalid control payload: %v", ErrProtocol, err) + return errutil.WrapfCauseFirst(ErrProtocol, "invalid control payload: %v", err) } } case "InputBatch", "ResultBatch": if control.Sequence == 0 { - return fmt.Errorf("%w: control kind %q requires a positive sequence", ErrProtocol, control.Kind) + return errutil.WrapfCauseFirst(ErrProtocol, "control kind %q requires a positive sequence", control.Kind) } case "InputConsumed": if control.Sequence == 0 || control.ReleasedBytes <= 0 || control.ReleasedBatches == 0 { - return fmt.Errorf("%w: control kind %q requires sequence and positive released bytes/batches", ErrProtocol, control.Kind) + return errutil.WrapfCauseFirst(ErrProtocol, "control kind %q requires sequence and positive released bytes/batches", control.Kind) } case "AcknowledgeResults": if control.AckSequence == 0 { - return fmt.Errorf("%w: control kind %q requires a positive ack_sequence", ErrProtocol, control.Kind) + return errutil.WrapfCauseFirst(ErrProtocol, "control kind %q requires a positive ack_sequence", control.Kind) } case "Finish": if control.FinishID == "" || control.Status == "" { - return fmt.Errorf("%w: control kind %q requires finish_id and status", ErrProtocol, control.Kind) + return errutil.WrapfCauseFirst(ErrProtocol, "control kind %q requires finish_id and status", control.Kind) } case "AcknowledgeFinish": if control.FinishID == "" { - return fmt.Errorf("%w: control kind %q requires finish_id", ErrProtocol, control.Kind) + return errutil.WrapfCauseFirst(ErrProtocol, "control kind %q requires finish_id", control.Kind) } case "Ack": if control.Status == "" || ((control.AckSequence == 0) == (control.FinishID == "")) { - return fmt.Errorf("%w: Ack requires status and exactly one acknowledgement identity", ErrProtocol) + return errutil.WrapfCauseFirst(ErrProtocol, "Ack requires status and exactly one acknowledgement identity") } case "Error": if control.Status == "" || control.Reason == "" { - return fmt.Errorf("%w: control kind %q requires status and reason", ErrProtocol, control.Kind) + return errutil.WrapfCauseFirst(ErrProtocol, "control kind %q requires status and reason", control.Kind) } } return nil @@ -298,7 +299,7 @@ func MarshalControl(control Control) ([]byte, error) { control.Version = Version } if control.Version != Version || control.Kind == "" { - return nil, fmt.Errorf("%w: unsupported control version or empty kind", ErrProtocol) + return nil, errutil.WrapfCauseFirst(ErrProtocol, "unsupported control version or empty kind") } if err := control.Tuple.Validate(); err != nil { return nil, err @@ -308,50 +309,50 @@ func MarshalControl(control Control) ([]byte, error) { } encoded, err := json.Marshal(control) if err != nil { - return nil, fmt.Errorf("%w: encode control: %v", ErrProtocol, err) + return nil, errutil.WrapfCauseFirst(ErrProtocol, "encode control: %v", err) } if len(encoded) > MaxControlBytes { - return nil, fmt.Errorf("%w: control is %d bytes, limit is %d", ErrProtocol, len(encoded), MaxControlBytes) + return nil, errutil.WrapfCauseFirst(ErrProtocol, "control is %d bytes, limit is %d", len(encoded), MaxControlBytes) } return encoded, nil } func UnmarshalControl(data []byte) (Control, error) { if len(data) == 0 || len(data) > MaxControlBytes { - return Control{}, fmt.Errorf("%w: control size %d is outside the allowed range", ErrProtocol, len(data)) + return Control{}, errutil.WrapfCauseFirst(ErrProtocol, "control size %d is outside the allowed range", len(data)) } // encoding/json replaces invalid UTF-8 in JSON strings with U+FFFD. A // fencing tuple is an identity, so accepting that replacement would make a // malformed wire tuple refer to a different invocation than the sender // intended. Reject the bytes before any JSON decoder can normalize them. if !utf8.Valid(data) { - return Control{}, fmt.Errorf("%w: control contains invalid UTF-8", ErrProtocol) + return Control{}, errutil.WrapfCauseFirst(ErrProtocol, "control contains invalid UTF-8") } if err := rejectDuplicateJSONKeys(data); err != nil { - return Control{}, fmt.Errorf("%w: %v", ErrProtocol, err) + return Control{}, errutil.WrapfCauseFirst(ErrProtocol, "%v", err) } var control Control decoder := json.NewDecoder(bytes.NewReader(data)) decoder.DisallowUnknownFields() if err := decoder.Decode(&control); err != nil { - return Control{}, fmt.Errorf("%w: decode control: %v", ErrProtocol, err) + return Control{}, errutil.WrapfCauseFirst(ErrProtocol, "decode control: %v", err) } var trailing any if err := decoder.Decode(&trailing); err != io.EOF { if err == nil { - return Control{}, fmt.Errorf("%w: control contains trailing JSON", ErrProtocol) + return Control{}, errutil.WrapfCauseFirst(ErrProtocol, "control contains trailing JSON") } - return Control{}, fmt.Errorf("%w: decode trailing control data: %v", ErrProtocol, err) + return Control{}, errutil.WrapfCauseFirst(ErrProtocol, "decode trailing control data: %v", err) } if control.Version != Version || control.Kind == "" { - return Control{}, fmt.Errorf("%w: unsupported control version %d", ErrProtocol, control.Version) + return Control{}, errutil.WrapfCauseFirst(ErrProtocol, "unsupported control version %d", control.Version) } if err := control.Tuple.Validate(); err != nil { return Control{}, err } var fields map[string]json.RawMessage if err := json.Unmarshal(data, &fields); err != nil { - return Control{}, fmt.Errorf("%w: control must be an object: %v", ErrProtocol, err) + return Control{}, errutil.WrapfCauseFirst(ErrProtocol, "control must be an object: %v", err) } if err := validateControlFields(control, fields); err != nil { return Control{}, err @@ -367,9 +368,9 @@ func rejectDuplicateJSONKeys(data []byte) error { var trailing any if err := decoder.Decode(&trailing); err != io.EOF { if err == nil { - return errors.New("control contains trailing JSON") + return moerr.NewInternalErrorNoCtx("control contains trailing JSON") } - return fmt.Errorf("decode trailing control data: %v", err) + return moerr.NewInternalErrorNoCtxf("decode trailing control data: %v", err) } return nil } @@ -381,14 +382,14 @@ func walkJSONValue(decoder *json.Decoder) error { func walkJSONValueAtDepth(decoder *json.Decoder, depth int) error { token, err := decoder.Token() if err != nil { - return fmt.Errorf("decode control JSON: %v", err) + return moerr.NewInternalErrorNoCtxf("decode control JSON: %v", err) } delim, ok := token.(json.Delim) if !ok { return nil } if depth >= MaxJSONNesting { - return fmt.Errorf("control JSON nesting exceeds %d levels", MaxJSONNesting) + return moerr.NewInternalErrorNoCtxf("control JSON nesting exceeds %d levels", MaxJSONNesting) } switch delim { case '{': @@ -396,14 +397,14 @@ func walkJSONValueAtDepth(decoder *json.Decoder, depth int) error { for decoder.More() { keyToken, err := decoder.Token() if err != nil { - return fmt.Errorf("decode control object key: %v", err) + return moerr.NewInternalErrorNoCtxf("decode control object key: %v", err) } key, ok := keyToken.(string) if !ok { - return errors.New("control object key is not a string") + return moerr.NewInternalErrorNoCtx("control object key is not a string") } if _, exists := seen[key]; exists { - return fmt.Errorf("duplicate control JSON field %q", key) + return moerr.NewInternalErrorNoCtxf("duplicate control JSON field %q", key) } seen[key] = struct{}{} if err := walkJSONValueAtDepth(decoder, depth+1); err != nil { @@ -421,7 +422,7 @@ func walkJSONValueAtDepth(decoder *json.Decoder, depth int) error { _, err = decoder.Token() return err default: - return fmt.Errorf("unexpected control JSON delimiter %q", delim) + return moerr.NewInternalErrorNoCtxf("unexpected control JSON delimiter %q", delim) } } @@ -442,10 +443,10 @@ func (s *Sequence) AcceptInput(sequence uint64) error { s.mu.Lock() defer s.mu.Unlock() if s.inputEnded { - return fmt.Errorf("%w: input arrived after EndInput", ErrSequence) + return errutil.WrapfCauseFirst(ErrSequence, "input arrived after EndInput") } if sequence != s.nextInput+1 { - return fmt.Errorf("%w: input sequence %d, expected %d", ErrSequence, sequence, s.nextInput+1) + return errutil.WrapfCauseFirst(ErrSequence, "input sequence %d, expected %d", sequence, s.nextInput+1) } s.nextInput = sequence s.lastInput = sequence @@ -459,10 +460,10 @@ func (s *Sequence) EndInput(lastSequence uint64) error { if s.lastInput == lastSequence { return nil } - return fmt.Errorf("%w: EndInput changed from %d to %d", ErrSequence, s.lastInput, lastSequence) + return errutil.WrapfCauseFirst(ErrSequence, "EndInput changed from %d to %d", s.lastInput, lastSequence) } if lastSequence != s.lastInput { - return fmt.Errorf("%w: EndInput last sequence %d, observed %d", ErrSequence, lastSequence, s.lastInput) + return errutil.WrapfCauseFirst(ErrSequence, "EndInput last sequence %d, observed %d", lastSequence, s.lastInput) } s.inputEnded = true return nil @@ -472,7 +473,7 @@ func (s *Sequence) AcceptResult(sequence uint64) error { s.mu.Lock() defer s.mu.Unlock() if sequence != s.nextResult+1 || sequence > s.lastInput { - return fmt.Errorf("%w: result sequence %d, expected %d and at most input %d", ErrSequence, sequence, s.nextResult+1, s.lastInput) + return errutil.WrapfCauseFirst(ErrSequence, "result sequence %d, expected %d and at most input %d", sequence, s.nextResult+1, s.lastInput) } s.nextResult = sequence s.lastResult = sequence @@ -483,7 +484,7 @@ func (s *Sequence) AcknowledgeResults(sequence uint64) error { s.mu.Lock() defer s.mu.Unlock() if sequence == 0 || sequence < s.ackedResult || sequence > s.lastResult { - return fmt.Errorf("%w: result ACK %d outside [%d,%d]", ErrSequence, sequence, s.ackedResult, s.lastResult) + return errutil.WrapfCauseFirst(ErrSequence, "result ACK %d outside [%d,%d]", sequence, s.ackedResult, s.lastResult) } s.ackedResult = sequence return nil @@ -512,7 +513,7 @@ type OutputSnapshot struct { func FreezeOutput(source []byte, maxBytes int64) (*OutputSnapshot, error) { if maxBytes <= 0 || int64(len(source)) > maxBytes { - return nil, fmt.Errorf("%w: output snapshot size %d exceeds limit %d", ErrProtocol, len(source), maxBytes) + return nil, errutil.WrapfCauseFirst(ErrProtocol, "output snapshot size %d exceeds limit %d", len(source), maxBytes) } backing := make([]byte, len(source)) copy(backing, source) @@ -535,17 +536,17 @@ func (s *OutputSnapshot) Digest() string { func (s *OutputSnapshot) Validate(expectedLength int, expectedDigest string) error { if s == nil || s.backing == nil { - return fmt.Errorf("%w: missing output snapshot", ErrProtocol) + return errutil.WrapfCauseFirst(ErrProtocol, "missing output snapshot") } if expectedLength >= 0 && len(s.backing) != expectedLength { - return fmt.Errorf("%w: output snapshot length %d, expected %d", ErrProtocol, len(s.backing), expectedLength) + return errutil.WrapfCauseFirst(ErrProtocol, "output snapshot length %d, expected %d", len(s.backing), expectedLength) } currentDigest := sha256.Sum256(s.backing) if currentDigest != s.digest { - return fmt.Errorf("%w: output snapshot backing changed", ErrProtocol) + return errutil.WrapfCauseFirst(ErrProtocol, "output snapshot backing changed") } if expectedDigest != "" && !equalFoldHex(expectedDigest, hex.EncodeToString(currentDigest[:])) { - return fmt.Errorf("%w: output snapshot digest changed", ErrProtocol) + return errutil.WrapfCauseFirst(ErrProtocol, "output snapshot digest changed") } return nil } @@ -639,7 +640,7 @@ type ExecutionGroup struct { func NewExecutionGroup(id string, epoch uint64, maxMembers int, release func() error) (*ExecutionGroup, error) { if id == "" || epoch == 0 || maxMembers <= 0 || release == nil { - return nil, fmt.Errorf("%w: invalid execution group", ErrProtocol) + return nil, errutil.WrapfCauseFirst(ErrProtocol, "invalid execution group") } return &ExecutionGroup{ id: id, epoch: epoch, maxMembers: maxMembers, @@ -652,7 +653,7 @@ func NewExecutionGroup(id string, epoch uint64, maxMembers int, release func() e // token must be committed or aborted exactly once by the Open owner. func (g *ExecutionGroup) BeginOpen(memberID string) (*OpenToken, error) { if memberID == "" { - return nil, fmt.Errorf("%w: empty member id", ErrProtocol) + return nil, errutil.WrapfCauseFirst(ErrProtocol, "empty member id") } g.mu.Lock() defer g.mu.Unlock() @@ -666,7 +667,7 @@ func (g *ExecutionGroup) BeginOpen(memberID string) (*OpenToken, error) { return nil, ErrDuplicate } if g.registered+g.inFlight >= g.maxMembers { - return nil, fmt.Errorf("%w: member limit %d reached", ErrLedgerFull, g.maxMembers) + return nil, errutil.WrapfCauseFirst(ErrLedgerFull, "member limit %d reached", g.maxMembers) } g.pending[memberID] = struct{}{} g.inFlight++ @@ -736,7 +737,7 @@ func (t *OpenToken) begin(state openTokenState) error { t.mu.Lock() defer t.mu.Unlock() if t.state != openTokenPending { - return fmt.Errorf("%w: open token was already finalized", ErrProtocol) + return errutil.WrapfCauseFirst(ErrProtocol, "open token was already finalized") } t.state = state return nil @@ -744,12 +745,12 @@ func (t *OpenToken) begin(state openTokenState) error { func (g *ExecutionGroup) Close(reason CloseReason) error { if reason == "" { - return fmt.Errorf("%w: close reason is empty", ErrProtocol) + return errutil.WrapfCauseFirst(ErrProtocol, "close reason is empty") } g.mu.Lock() if g.closing && g.reason != reason { g.mu.Unlock() - return fmt.Errorf("%w: close reason changed from %q to %q", ErrProtocol, g.reason, reason) + return errutil.WrapfCauseFirst(ErrProtocol, "close reason changed from %q to %q", g.reason, reason) } if g.released { g.mu.Unlock() @@ -821,7 +822,7 @@ func (g *ExecutionGroup) releaseIfReady() error { err := func() (err error) { defer func() { if recovered := recover(); recovered != nil { - err = fmt.Errorf("%w: group release panicked: %v", ErrProtocol, recovered) + err = errutil.WrapfCauseFirst(ErrProtocol, "group release panicked: %v", recovered) } }() return g.release() @@ -916,14 +917,14 @@ type LedgerCredit struct { func NewTerminalLedger(maxEntries int, maxBytes int64) (*TerminalLedger, error) { if maxEntries <= 0 || maxBytes <= 0 { - return nil, fmt.Errorf("%w: invalid terminal ledger limit", ErrProtocol) + return nil, errutil.WrapfCauseFirst(ErrProtocol, "invalid terminal ledger limit") } return &TerminalLedger{maxEntries: maxEntries, maxBytes: maxBytes, entries: make(map[string]ledgerEntry)}, nil } func (l *TerminalLedger) Reserve(groupID string, groupEpoch uint64, entries int, bytes int64) (*LedgerCredit, error) { if groupID == "" || groupEpoch == 0 || entries <= 0 || bytes <= 0 { - return nil, fmt.Errorf("%w: invalid terminal ledger reservation", ErrProtocol) + return nil, errutil.WrapfCauseFirst(ErrProtocol, "invalid terminal ledger reservation") } l.mu.Lock() defer l.mu.Unlock() @@ -937,7 +938,7 @@ func (l *TerminalLedger) Reserve(groupID string, groupEpoch uint64, entries int, func (c *LedgerCredit) Add(key string, bytes int64, expiry time.Time) error { if c == nil || c.ledger == nil || key == "" || bytes <= 0 || expiry.IsZero() { - return fmt.Errorf("%w: invalid terminal ledger entry", ErrProtocol) + return errutil.WrapfCauseFirst(ErrProtocol, "invalid terminal ledger entry") } c.ledger.mu.Lock() defer c.ledger.mu.Unlock() diff --git a/pkg/udf/python/arrow.go b/pkg/udf/python/arrow.go index b3217761c53f7..11f254a6ad7c6 100644 --- a/pkg/udf/python/arrow.go +++ b/pkg/udf/python/arrow.go @@ -21,8 +21,9 @@ import ( "encoding/binary" "encoding/hex" "encoding/json" - "errors" "fmt" + "github.com/matrixorigin/matrixone/pkg/common/moerr" + "github.com/matrixorigin/matrixone/pkg/util/errutil" "io" "math" "reflect" @@ -104,11 +105,11 @@ func NewTypeDescriptor(typ types.Type) (TypeDescriptor, error) { types.T_binary, types.T_varbinary, types.T_blob, types.T_array_float32, types.T_array_float64: default: - return TypeDescriptor{}, fmt.Errorf("python udf does not support MatrixOne type %s", typ.String()) + return TypeDescriptor{}, moerr.NewInternalErrorNoCtxf("python udf does not support MatrixOne type %s", typ.String()) } if typ.Oid == types.T_array_float32 || typ.Oid == types.T_array_float64 { if typ.Width <= 0 { - return TypeDescriptor{}, fmt.Errorf("python udf requires a positive vector dimension for %s", typ.String()) + return TypeDescriptor{}, moerr.NewInternalErrorNoCtxf("python udf requires a positive vector dimension for %s", typ.String()) } d.OffsetWidth = 0 } @@ -156,29 +157,29 @@ func (d TypeDescriptor) Fingerprint() (string, error) { func (d TypeDescriptor) Validate() error { oid := types.T(d.TypeID) if d.Width < 0 { - return fmt.Errorf("TYPE_CONTRACT: descriptor width must be non-negative") + return moerr.NewInternalErrorNoCtxf("TYPE_CONTRACT: descriptor width must be non-negative") } if d.Scale < 0 { - return fmt.Errorf("TYPE_CONTRACT: descriptor scale must be non-negative") + return moerr.NewInternalErrorNoCtxf("TYPE_CONTRACT: descriptor scale must be non-negative") } expectedOffsetWidth := int32(32) if oid == types.T_uuid || oid == types.T_array_float32 || oid == types.T_array_float64 { expectedOffsetWidth = 0 } if d.OffsetWidth != expectedOffsetWidth { - return fmt.Errorf("TYPE_CONTRACT: descriptor offset_width must be %d for %s", expectedOffsetWidth, oid.String()) + return moerr.NewInternalErrorNoCtxf("TYPE_CONTRACT: descriptor offset_width must be %d for %s", expectedOffsetWidth, oid.String()) } if d.JSONEncoding != "" && (oid != types.T_json || d.JSONEncoding != "canonical_text") { - return fmt.Errorf("TYPE_CONTRACT: unsupported JSON encoding") + return moerr.NewInternalErrorNoCtxf("TYPE_CONTRACT: unsupported JSON encoding") } if oid == types.T_json && d.JSONEncoding != "canonical_text" { - return fmt.Errorf("TYPE_CONTRACT: JSON descriptor must use canonical_text encoding") + return moerr.NewInternalErrorNoCtxf("TYPE_CONTRACT: JSON descriptor must use canonical_text encoding") } if d.TemporalEncoding != "" && (oid != types.T_date && oid != types.T_datetime && oid != types.T_timestamp || d.TemporalEncoding != "sql_zero_struct") { - return fmt.Errorf("TYPE_CONTRACT: unsupported temporal encoding") + return moerr.NewInternalErrorNoCtxf("TYPE_CONTRACT: unsupported temporal encoding") } if (oid == types.T_date || oid == types.T_datetime || oid == types.T_timestamp) && d.TemporalEncoding != "sql_zero_struct" { - return fmt.Errorf("TYPE_CONTRACT: temporal descriptor must use sql_zero_struct encoding") + return moerr.NewInternalErrorNoCtxf("TYPE_CONTRACT: temporal descriptor must use sql_zero_struct encoding") } switch oid { @@ -186,7 +187,7 @@ func (d TypeDescriptor) Validate() error { types.T_uint8, types.T_uint16, types.T_uint32, types.T_uint64, types.T_float32, types.T_float64, types.T_json: if d.Width != 0 || d.Scale != 0 || d.Charset != types.CharsetLegacy { - return fmt.Errorf("TYPE_CONTRACT: descriptor carries unused fields for %s", oid.String()) + return moerr.NewInternalErrorNoCtxf("TYPE_CONTRACT: descriptor carries unused fields for %s", oid.String()) } case types.T_decimal64, types.T_decimal128: maxPrecision := int32(38) @@ -194,45 +195,45 @@ func (d TypeDescriptor) Validate() error { maxPrecision = 18 } if d.Width < 1 || d.Width > maxPrecision { - return fmt.Errorf("TYPE_CONTRACT: decimal precision %d is outside %s range", d.Width, oid.String()) + return moerr.NewInternalErrorNoCtxf("TYPE_CONTRACT: decimal precision %d is outside %s range", d.Width, oid.String()) } if d.Scale > d.Width { - return fmt.Errorf("TYPE_CONTRACT: decimal scale %d exceeds precision %d", d.Scale, d.Width) + return moerr.NewInternalErrorNoCtxf("TYPE_CONTRACT: decimal scale %d exceeds precision %d", d.Scale, d.Width) } if d.Charset != types.CharsetLegacy { - return fmt.Errorf("TYPE_CONTRACT: decimal descriptor carries a charset") + return moerr.NewInternalErrorNoCtxf("TYPE_CONTRACT: decimal descriptor carries a charset") } case types.T_date: if d.Width != 0 || d.Scale != 0 || d.Charset != types.CharsetLegacy { - return fmt.Errorf("TYPE_CONTRACT: DATE descriptor has unused or invalid fields") + return moerr.NewInternalErrorNoCtxf("TYPE_CONTRACT: DATE descriptor has unused or invalid fields") } case types.T_time, types.T_datetime, types.T_timestamp: if d.Width != 0 || d.Scale > 6 || d.Charset != types.CharsetLegacy { - return fmt.Errorf("TYPE_CONTRACT: temporal scale or charset is outside the supported range") + return moerr.NewInternalErrorNoCtxf("TYPE_CONTRACT: temporal scale or charset is outside the supported range") } case types.T_char, types.T_varchar, types.T_text: if d.Scale != 0 { - return fmt.Errorf("TYPE_CONTRACT: text descriptor carries an unused scale") + return moerr.NewInternalErrorNoCtxf("TYPE_CONTRACT: text descriptor carries an unused scale") } switch d.Charset { case types.CharsetLegacy, types.CharsetUTF8MB4Bin, types.CharsetUTF8: default: - return fmt.Errorf("TYPE_CONTRACT: unsupported text charset %d", d.Charset) + return moerr.NewInternalErrorNoCtxf("TYPE_CONTRACT: unsupported text charset %d", d.Charset) } case types.T_binary, types.T_varbinary, types.T_blob: if d.Scale != 0 || d.Charset != types.CharsetBinary { - return fmt.Errorf("TYPE_CONTRACT: binary descriptor must use the binary charset") + return moerr.NewInternalErrorNoCtxf("TYPE_CONTRACT: binary descriptor must use the binary charset") } case types.T_uuid: if d.Width != 0 || d.Scale != 0 || d.Charset != types.CharsetLegacy { - return fmt.Errorf("TYPE_CONTRACT: UUID descriptor carries unused fields") + return moerr.NewInternalErrorNoCtxf("TYPE_CONTRACT: UUID descriptor carries unused fields") } case types.T_array_float32, types.T_array_float64: if d.Width < 1 || d.Width > types.MaxArrayDimension || d.Scale != 0 || d.Charset != types.CharsetLegacy { - return fmt.Errorf("TYPE_CONTRACT: vector dimension or metadata is outside the supported range") + return moerr.NewInternalErrorNoCtxf("TYPE_CONTRACT: vector dimension or metadata is outside the supported range") } default: - return fmt.Errorf("TYPE_CONTRACT: unsupported MatrixOne type id %d", d.TypeID) + return moerr.NewInternalErrorNoCtxf("TYPE_CONTRACT: unsupported MatrixOne type id %d", d.TypeID) } return nil } @@ -270,7 +271,7 @@ func (d TypeDescriptor) arrowType() (arrow.DataType, error) { } } if precision < 1 || precision > decimal128.MaxPrecision { - return nil, fmt.Errorf("decimal precision %d is outside Arrow's supported range", precision) + return nil, moerr.NewInternalErrorNoCtxf("decimal precision %d is outside Arrow's supported range", precision) } return &arrow.Decimal128Type{Precision: precision, Scale: d.Scale}, nil case types.T_char, types.T_varchar, types.T_text, types.T_json: @@ -289,12 +290,12 @@ func (d TypeDescriptor) arrowType() (arrow.DataType, error) { return &arrow.DurationType{Unit: arrow.Microsecond}, nil case types.T_array_float32: if d.Width <= 0 { - return nil, fmt.Errorf("invalid vecf32 dimension %d", d.Width) + return nil, moerr.NewInternalErrorNoCtxf("invalid vecf32 dimension %d", d.Width) } return arrow.FixedSizeListOf(d.Width, arrow.PrimitiveTypes.Float32), nil case types.T_array_float64: if d.Width <= 0 { - return nil, fmt.Errorf("invalid vecf64 dimension %d", d.Width) + return nil, moerr.NewInternalErrorNoCtxf("invalid vecf64 dimension %d", d.Width) } return arrow.FixedSizeListOf(d.Width, arrow.PrimitiveTypes.Float64), nil case types.T_date: @@ -304,7 +305,7 @@ func (d TypeDescriptor) arrowType() (arrow.DataType, error) { case types.T_timestamp: return sqlTemporalType(&arrow.TimestampType{Unit: arrow.Microsecond, TimeZone: "UTC"}), nil default: - return nil, fmt.Errorf("unsupported MatrixOne type id %d", d.TypeID) + return nil, moerr.NewInternalErrorNoCtxf("unsupported MatrixOne type id %d", d.TypeID) } } func sqlTemporalType(value arrow.DataType) *arrow.StructType { @@ -331,10 +332,10 @@ func (d TypeDescriptor) ValidateField(field arrow.Field) error { return err } if field.Type == nil || !sameArrowType(field.Type, want.Type) || field.Nullable != want.Nullable { - return fmt.Errorf("TYPE_CONTRACT: Arrow type %s does not match %s", field.Type, want.Type) + return moerr.NewInternalErrorNoCtxf("TYPE_CONTRACT: Arrow type %s does not match %s", field.Type, want.Type) } if !field.Metadata.Equal(want.Metadata) { - return fmt.Errorf("TYPE_CONTRACT: Arrow logical metadata does not match the frozen descriptor") + return moerr.NewInternalErrorNoCtxf("TYPE_CONTRACT: Arrow logical metadata does not match the frozen descriptor") } return nil } @@ -361,22 +362,22 @@ type encodedRecordBatch struct { Rows int64 } -var errArrowBatchTooLarge = errors.New("Arrow record batch exceeds the configured limit") +var errArrowBatchTooLarge = moerr.NewInternalErrorNoCtx("Arrow record batch exceeds the configured limit") func EncodeRecordBatch(record arrow.RecordBatch, maxBytes int64) ([]ArrowFrame, error) { if record == nil || record.Schema() == nil || maxBytes <= 0 { - return nil, fmt.Errorf("invalid Arrow record batch") + return nil, moerr.NewInternalErrorNoCtxf("invalid Arrow record batch") } collector := &flightFrameCollector{maxBytes: maxBytes} writer := flight.NewRecordWriter(collector, ipc.WithSchema(record.Schema()), ipc.WithAllocator(memory.NewGoAllocator())) if err := writer.Write(record); err != nil { - return nil, fmt.Errorf("encode Arrow record batch: %w", err) + return nil, errutil.Wrapf(err, "encode Arrow record batch") } if err := writer.Close(); err != nil { - return nil, fmt.Errorf("close Arrow record batch: %w", err) + return nil, errutil.Wrapf(err, "close Arrow record batch") } if len(collector.frames) < 2 { - return nil, fmt.Errorf("Arrow stream did not contain schema and record batch") + return nil, moerr.NewInternalErrorNoCtxf("Arrow stream did not contain schema and record batch") } return collector.frames, nil } @@ -402,16 +403,16 @@ func (c *flightFrameCollector) reset(maxBytes, retainedBytes int64) { func (c *flightFrameCollector) Send(data *flight.FlightData) error { if data == nil || len(data.DataHeader) == 0 { - return fmt.Errorf("invalid Arrow Flight frame") + return moerr.NewInternalErrorNoCtxf("invalid Arrow Flight frame") } if data.FlightDescriptor != nil || len(data.AppMetadata) != 0 { - return fmt.Errorf("unexpected metadata on Arrow payload frame") + return moerr.NewInternalErrorNoCtxf("unexpected metadata on Arrow payload frame") } header := append([]byte(nil), data.DataHeader...) body := append([]byte(nil), data.DataBody...) c.total += int64(len(header) + len(body)) if c.total > c.maxBytes { - return fmt.Errorf("%w: stream is %d bytes, limit is %d", errArrowBatchTooLarge, c.total, c.maxBytes) + return errutil.WrapfCauseFirst(errArrowBatchTooLarge, "stream is %d bytes, limit is %d", c.total, c.maxBytes) } c.frames = append(c.frames, ArrowFrame{Header: header, Body: body}) return nil @@ -434,10 +435,10 @@ type inputBatchWireEncoder struct { func (e *inputBatchWireEncoder) encode(record arrow.RecordBatch, maxBytes int64) ([]ArrowFrame, error) { if e == nil || e.closed || record == nil || maxBytes <= 0 { - return nil, fmt.Errorf("invalid Arrow record batch encoder") + return nil, moerr.NewInternalErrorNoCtxf("invalid Arrow record batch encoder") } if e.schema == nil || !record.Schema().Equal(e.schema) { - return nil, fmt.Errorf("Arrow record batch schema does not match the invocation schema") + return nil, moerr.NewInternalErrorNoCtxf("Arrow record batch schema does not match the invocation schema") } if e.writer == nil { e.collector = &flightFrameCollector{} @@ -451,17 +452,17 @@ func (e *inputBatchWireEncoder) encode(record arrow.RecordBatch, maxBytes int64) e.collector.reset(maxBytes, e.schemaBytes) if err := e.writer.Write(record); err != nil { e.captureSchema() - return nil, fmt.Errorf("encode Arrow record batch: %w", err) + return nil, errutil.Wrapf(err, "encode Arrow record batch") } e.captureSchema() if writerHadSchema { if len(e.collector.frames) != 1 { - return nil, fmt.Errorf("Arrow writer did not emit one record frame") + return nil, moerr.NewInternalErrorNoCtxf("Arrow writer did not emit one record frame") } return []ArrowFrame{e.schemaFrame, e.collector.frames[0]}, nil } if len(e.collector.frames) != 2 || !e.schemaReady { - return nil, fmt.Errorf("Arrow stream did not contain schema and record batch") + return nil, moerr.NewInternalErrorNoCtxf("Arrow stream did not contain schema and record batch") } return append([]ArrowFrame(nil), e.collector.frames...), nil } @@ -488,7 +489,7 @@ func (e *inputBatchWireEncoder) close() error { return nil } if err := e.writer.Close(); err != nil { - return fmt.Errorf("close Arrow record batch encoder: %w", err) + return errutil.Wrapf(err, "close Arrow record batch encoder") } // Commit the closed state only after the writer has released its resources. // A failed close remains retryable for the owning invocation cleanup path. @@ -525,7 +526,7 @@ type inputBatchEncoder struct { func newInputBatchEncoder(inputs []*vector.Vector, args []types.Type) (*inputBatchEncoder, error) { if len(inputs) != len(args) { - return nil, fmt.Errorf("invalid Python UDF input shape") + return nil, moerr.NewInternalErrorNoCtxf("invalid Python UDF input shape") } fields := make([]arrow.Field, len(args)) descriptors := make([]TypeDescriptor, len(args)) @@ -545,19 +546,14 @@ func newInputBatchEncoder(inputs []*vector.Vector, args []types.Type) (*inputBat fixedWidth = false } if inputs[i] == nil { - return nil, fmt.Errorf("input column %d is missing", i) + return nil, moerr.NewInternalErrorNoCtxf("input column %d is missing", i) } actualType := inputs[i].GetType() if actualType == nil { - return nil, fmt.Errorf("input column %d has no type", i) + return nil, moerr.NewInternalErrorNoCtxf("input column %d has no type", i) } if !actualType.Eq(typ) { - return nil, fmt.Errorf( - "input column %d type %s does not match the frozen argument type %s", - i, - actualType.DescString(), - typ.DescString(), - ) + return nil, moerr.NewInternalErrorNoCtxf("input column %d type %s does not match the frozen argument type %s", i, actualType.DescString(), typ.DescString()) } if inputs[i].Length() > 0 && isVarlenaInputType(typ.Oid) { varlenaParameters[i] = vector.GenerateFunctionStrParameter(inputs[i]) @@ -576,7 +572,7 @@ func newInputBatchEncoder(inputs []*vector.Vector, args []types.Type) (*inputBat func (e *inputBatchEncoder) encode(record arrow.RecordBatch, maxBytes int64) ([]ArrowFrame, error) { if e == nil { - return nil, fmt.Errorf("invalid Python UDF input encoder") + return nil, moerr.NewInternalErrorNoCtxf("invalid Python UDF input encoder") } if e.wire == nil { e.wire = &inputBatchWireEncoder{schema: e.schema} @@ -593,11 +589,11 @@ func (e *inputBatchEncoder) close() error { func (e *inputBatchEncoder) build(start, length int) (arrow.RecordBatch, *arrow.Schema, error) { if e == nil || start < 0 || length <= 0 { - return nil, nil, fmt.Errorf("invalid Python UDF input shape") + return nil, nil, moerr.NewInternalErrorNoCtxf("invalid Python UDF input shape") } for i, input := range e.inputs { if input.Length() == 0 || (!input.IsConst() && input.Length() < start+length) { - return nil, nil, fmt.Errorf("input column %d is shorter than batch range", i) + return nil, nil, moerr.NewInternalErrorNoCtxf("input column %d is shorter than batch range", i) } } if len(e.args) == 0 { @@ -610,7 +606,7 @@ func (e *inputBatchEncoder) build(start, length int) (arrow.RecordBatch, *arrow. builder.Field(column), e.inputs[column], typ, start, length, ) if err != nil { - return nil, nil, fmt.Errorf("input column %d: %w", column, err) + return nil, nil, errutil.Wrapf(err, "input column %d", column) } if bulk { continue @@ -620,7 +616,7 @@ func (e *inputBatchEncoder) build(start, length int) (arrow.RecordBatch, *arrow. builder.Field(column), e.inputs[column], typ, start, row, e.varlenaParameters[column], ); err != nil { - return nil, nil, fmt.Errorf("input column %d row %d: %w", column, row, err) + return nil, nil, errutil.Wrapf(err, "input column %d row %d", column, row) } } } @@ -653,7 +649,7 @@ func appendBulkFixedInputValue( return valid } if builder == nil || v == nil { - return false, fmt.Errorf("input column is missing") + return false, moerr.NewInternalErrorNoCtxf("input column is missing") } switch typ.Oid { @@ -739,7 +735,7 @@ func bulkFixedInputWindow[T any](v *vector.Vector, start, length int) ([]T, erro values := vector.MustFixedColNoTypeCheck[T](v) if v.IsConst() { if len(values) == 0 { - return nil, fmt.Errorf("constant input column has no value") + return nil, moerr.NewInternalErrorNoCtxf("constant input column has no value") } expanded := make([]T, length) for row := range expanded { @@ -748,7 +744,7 @@ func bulkFixedInputWindow[T any](v *vector.Vector, start, length int) ([]T, erro return expanded, nil } if start < 0 || length <= 0 || start > len(values) || length > len(values)-start { - return nil, fmt.Errorf("input column is shorter than batch range") + return nil, moerr.NewInternalErrorNoCtxf("input column is shorter than batch range") } return values[start : start+length], nil } @@ -877,7 +873,7 @@ func appendInputValue( index := sourceRow(v, start, row) null := v.IsNull(uint64(index)) if !null && isVarlenaInputType(typ.Oid) && varlenaParameter == nil { - return fmt.Errorf("missing cached varlena input parameter") + return moerr.NewInternalErrorNoCtxf("missing cached varlena input parameter") } if typ.Oid == types.T_date || typ.Oid == types.T_datetime || typ.Oid == types.T_timestamp { return appendTemporalInput(builder.(*array.StructBuilder), v, typ, index, null) @@ -1032,7 +1028,7 @@ func appendInputValue( b.ValueBuilder().(*array.Float64Builder).AppendValues(vector.GetArrayAt[float64](v, index), nil) } default: - return fmt.Errorf("unsupported input type %s", typ.String()) + return moerr.NewInternalErrorNoCtxf("unsupported input type %s", typ.String()) } return nil } @@ -1046,7 +1042,7 @@ func canonicalJSONInput(value []byte) ([]byte, error) { text := []byte(types.DecodeJson(value).String()) canonical, err := canonicalJSONTextBytes(text) if err != nil { - return nil, fmt.Errorf("TYPE_CONTRACT: invalid JSON input: %w", err) + return nil, errutil.Wrapf(err, "TYPE_CONTRACT: invalid JSON input") } return canonical, nil } @@ -1105,7 +1101,7 @@ func appendTemporalInput(builder *array.StructBuilder, v *vector.Vector, typ typ builder.FieldBuilder(0).(*array.BooleanBuilder).Append(zero) builder.FieldBuilder(1).(*array.TimestampBuilder).Append(arrow.Timestamp(raw)) default: - return fmt.Errorf("unsupported temporal type %s", typ.String()) + return moerr.NewInternalErrorNoCtxf("unsupported temporal type %s", typ.String()) } return nil } @@ -1118,24 +1114,24 @@ func decimal64ToArrow(value types.Decimal64) decimal128.Num { func DecodeRecordBatch(schemaFrame, batchFrame ArrowFrame, maxBytes int64) (arrow.RecordBatch, error) { if len(schemaFrame.Header) == 0 || len(batchFrame.Header) == 0 || maxBytes <= 0 { - return nil, fmt.Errorf("invalid Arrow output frames") + return nil, moerr.NewInternalErrorNoCtxf("invalid Arrow output frames") } if len(schemaFrame.Body) != 0 { - return nil, fmt.Errorf("invalid Arrow schema frame body") + return nil, moerr.NewInternalErrorNoCtxf("invalid Arrow schema frame body") } schemaInfo, err := arrowipc.InspectMessage(context.Background(), schemaFrame.Header, arrowipc.ValidationOptions{MaxMetadataBytes: arrowipc.DefaultMaxMetadataBytes, MaxBodyBytes: 0, BodyEnvelopeBytes: 0, MaxDecodedRecordBytes: 1}) if err != nil { return nil, err } if schemaInfo.HeaderType != arrowipc.MessageHeaderSchema { - return nil, fmt.Errorf("invalid Arrow schema frame header %d", schemaInfo.HeaderType) + return nil, moerr.NewInternalErrorNoCtxf("invalid Arrow schema frame header %d", schemaInfo.HeaderType) } batchInfo, err := arrowipc.InspectMessage(context.Background(), batchFrame.Header, arrowipc.ValidationOptions{MaxMetadataBytes: arrowipc.DefaultMaxMetadataBytes, MaxBodyBytes: maxBytes, BodyEnvelopeBytes: int64(len(batchFrame.Body)), Body: batchFrame.Body, ValidateBody: true, MaxDecodedRecordBytes: maxBytes}) if err != nil { return nil, err } if batchInfo.HeaderType != arrowipc.MessageHeaderRecordBatch { - return nil, fmt.Errorf("invalid Arrow record frame header %d", batchInfo.HeaderType) + return nil, moerr.NewInternalErrorNoCtxf("invalid Arrow record frame header %d", batchInfo.HeaderType) } // The snapshot is already the immutable validation/publication backing. // Feed that backing directly to Arrow's stream reader instead of assembling @@ -1145,15 +1141,15 @@ func DecodeRecordBatch(schemaFrame, batchFrame ArrowFrame, maxBytes int64) (arro stream := newIPCStreamReader(schemaFrame, batchFrame) reader, err := ipc.NewReader(stream, ipc.WithAllocator(memory.NewGoAllocator())) if err != nil { - return nil, fmt.Errorf("decode Arrow output: %w", err) + return nil, errutil.Wrapf(err, "decode Arrow output") } defer reader.Release() if !reader.Next() { - return nil, fmt.Errorf("decode Arrow output: %v", reader.Err()) + return nil, moerr.NewInternalErrorNoCtxf("decode Arrow output: %v", reader.Err()) } record := reader.RecordBatch() if record == nil { - return nil, fmt.Errorf("decode Arrow output: missing record batch") + return nil, moerr.NewInternalErrorNoCtxf("decode Arrow output: missing record batch") } record.Retain() return record, nil @@ -1244,10 +1240,10 @@ func (r *ipcStreamReader) Read(dst []byte) (int, error) { func AppendArrowResult(descriptor TypeDescriptor, input arrow.Array, result vector.FunctionResultWrapper, mp *mpool.MPool) error { if input == nil { - return fmt.Errorf("TYPE_CONTRACT: missing Arrow result") + return moerr.NewInternalErrorNoCtxf("TYPE_CONTRACT: missing Arrow result") } if result == nil { - return fmt.Errorf("python udf: missing result wrapper") + return moerr.NewInternalErrorNoCtxf("python udf: missing result wrapper") } // AppendArrowResult is also the public adapter boundary used by runtimes // other than the Flight Gateway. Do not let an invalid manually-built @@ -1266,7 +1262,7 @@ func AppendArrowResult(descriptor TypeDescriptor, input arrow.Array, result vect return nil } if mp == nil { - return fmt.Errorf("python udf: missing memory pool for Arrow result") + return moerr.NewInternalErrorNoCtxf("python udf: missing memory pool for Arrow result") } result.GetResultVector().SetTypeScale(descriptor.Scale) typ := types.T(descriptor.TypeID) @@ -1352,7 +1348,7 @@ func AppendArrowResult(descriptor TypeDescriptor, input arrow.Array, result vect return err } default: - return fmt.Errorf("unsupported output type %s", typ.String()) + return moerr.NewInternalErrorNoCtxf("unsupported output type %s", typ.String()) } } return nil @@ -1384,7 +1380,7 @@ func appendJSONArrowResult(input *array.String, result vector.FunctionResultWrap } encoded, err := bytejson.ParseJsonByteFromString(input.Value(i)) if err != nil { - return fmt.Errorf("TYPE_CONTRACT: invalid JSON result: %w", err) + return errutil.Wrapf(err, "TYPE_CONTRACT: invalid JSON result") } if err = output.AppendBytes(encoded, false); err != nil { return err @@ -1453,7 +1449,7 @@ func appendFloat32ArrayResult(input *array.FixedSizeList, row int, null bool, re items := make([]float32, end-start) for i := start; i < end; i++ { if values.IsNull(int(i)) { - return fmt.Errorf("TYPE_CONTRACT: vector child at row %d is null", row) + return moerr.NewInternalErrorNoCtxf("TYPE_CONTRACT: vector child at row %d is null", row) } items[i-start] = values.Value(int(i)) } @@ -1469,7 +1465,7 @@ func appendFloat64ArrayResult(input *array.FixedSizeList, row int, null bool, re items := make([]float64, end-start) for i := start; i < end; i++ { if values.IsNull(int(i)) { - return fmt.Errorf("TYPE_CONTRACT: vector child at row %d is null", row) + return moerr.NewInternalErrorNoCtxf("TYPE_CONTRACT: vector child at row %d is null", row) } items[i-start] = values.Value(int(i)) } @@ -1481,14 +1477,14 @@ func validateArrayType(descriptor TypeDescriptor, input arrow.Array) error { return err } if !sameArrowType(input.DataType(), want) { - return fmt.Errorf("TYPE_CONTRACT: output Arrow type %s does not match %s", input.DataType(), want) + return moerr.NewInternalErrorNoCtxf("TYPE_CONTRACT: output Arrow type %s does not match %s", input.DataType(), want) } return nil } func validateArrowValueDomain(descriptor TypeDescriptor, input arrow.Array) error { if input == nil { - return fmt.Errorf("TYPE_CONTRACT: missing Arrow result") + return moerr.NewInternalErrorNoCtxf("TYPE_CONTRACT: missing Arrow result") } for row := 0; row < input.Len(); row++ { if input.IsNull(row) { @@ -1497,25 +1493,25 @@ func validateArrowValueDomain(descriptor TypeDescriptor, input arrow.Array) erro switch types.T(descriptor.TypeID) { case types.T_char, types.T_varchar, types.T_text: if descriptor.Width > 0 && int32(utf8.RuneCountInString(input.(*array.String).Value(row))) > descriptor.Width { - return fmt.Errorf("TYPE_CONTRACT: string row %d exceeds width %d", row, descriptor.Width) + return moerr.NewInternalErrorNoCtxf("TYPE_CONTRACT: string row %d exceeds width %d", row, descriptor.Width) } case types.T_json: value := input.(*array.String).Value(row) canonical, err := canonicalJSONText(value) if err != nil { - return fmt.Errorf("TYPE_CONTRACT: JSON row %d is not valid canonical text: %w", row, err) + return errutil.Wrapf(err, "TYPE_CONTRACT: JSON row %d is not valid canonical text", row) } if canonical != value { - return fmt.Errorf("TYPE_CONTRACT: JSON row %d is not canonical text", row) + return moerr.NewInternalErrorNoCtxf("TYPE_CONTRACT: JSON row %d is not canonical text", row) } case types.T_binary, types.T_varbinary, types.T_blob: if descriptor.Width > 0 && int32(len(input.(*array.Binary).Value(row))) > descriptor.Width { - return fmt.Errorf("TYPE_CONTRACT: binary row %d exceeds width %d", row, descriptor.Width) + return moerr.NewInternalErrorNoCtxf("TYPE_CONTRACT: binary row %d exceeds width %d", row, descriptor.Width) } case types.T_time: value := types.Time(input.(*array.Duration).Value(row)) if !types.IsMySQLTime(value) || !hasExactMicrosecondScale(int64(value), descriptor.Scale) { - return fmt.Errorf("TYPE_CONTRACT: TIME row %d is outside the declared SQL domain", row) + return moerr.NewInternalErrorNoCtxf("TYPE_CONTRACT: TIME row %d is outside the declared SQL domain", row) } case types.T_decimal64, types.T_decimal128: precision := descriptor.Width @@ -1526,7 +1522,7 @@ func validateArrowValueDomain(descriptor TypeDescriptor, input arrow.Array) erro } } if !input.(*array.Decimal128).Value(row).FitsInPrecision(precision) { - return fmt.Errorf("TYPE_CONTRACT: decimal row %d exceeds precision %d", row, precision) + return moerr.NewInternalErrorNoCtxf("TYPE_CONTRACT: decimal row %d exceeds precision %d", row, precision) } case types.T_array_float32: list := input.(*array.FixedSizeList) @@ -1534,7 +1530,7 @@ func validateArrowValueDomain(descriptor TypeDescriptor, input arrow.Array) erro start, end := list.ValueOffsets(row) for child := start; child < end; child++ { if values.IsNull(int(child)) { - return fmt.Errorf("TYPE_CONTRACT: vector child at row %d is null", row) + return moerr.NewInternalErrorNoCtxf("TYPE_CONTRACT: vector child at row %d is null", row) } } case types.T_array_float64: @@ -1543,54 +1539,54 @@ func validateArrowValueDomain(descriptor TypeDescriptor, input arrow.Array) erro start, end := list.ValueOffsets(row) for child := start; child < end; child++ { if values.IsNull(int(child)) { - return fmt.Errorf("TYPE_CONTRACT: vector child at row %d is null", row) + return moerr.NewInternalErrorNoCtxf("TYPE_CONTRACT: vector child at row %d is null", row) } } case types.T_date, types.T_datetime, types.T_timestamp: structValue := input.(*array.Struct) zeroField, valueField := structValue.Field(0), structValue.Field(1) if zeroField.IsNull(row) || valueField.IsNull(row) { - return fmt.Errorf("TYPE_CONTRACT: temporal row %d has a null child", row) + return moerr.NewInternalErrorNoCtxf("TYPE_CONTRACT: temporal row %d has a null child", row) } zero := zeroField.(*array.Boolean).Value(row) switch types.T(descriptor.TypeID) { case types.T_date: value := valueField.(*array.Date32).Value(row) if zero && value != 0 { - return fmt.Errorf("TYPE_CONTRACT: DATE zero row %d has a non-zero placeholder", row) + return moerr.NewInternalErrorNoCtxf("TYPE_CONTRACT: DATE zero row %d has a non-zero placeholder", row) } if !zero { date := types.DaysFromUnixEpochToDate(int32(value)) year, month, day, _ := date.Calendar(true) if !types.ValidDate(year, month, day) { - return fmt.Errorf("TYPE_CONTRACT: DATE row %d is outside the SQL domain", row) + return moerr.NewInternalErrorNoCtxf("TYPE_CONTRACT: DATE row %d is outside the SQL domain", row) } } case types.T_datetime: value := valueField.(*array.Timestamp).Value(row) if zero && value != 0 { - return fmt.Errorf("TYPE_CONTRACT: DATETIME zero row %d has a non-zero placeholder", row) + return moerr.NewInternalErrorNoCtxf("TYPE_CONTRACT: DATETIME zero row %d has a non-zero placeholder", row) } if !zero { absolute, ok := addUnixEpoch(int64(value)) if !ok || !hasExactMicrosecondScale(absolute, descriptor.Scale) { - return fmt.Errorf("TYPE_CONTRACT: DATETIME row %d is outside the declared SQL domain", row) + return moerr.NewInternalErrorNoCtxf("TYPE_CONTRACT: DATETIME row %d is outside the declared SQL domain", row) } datetime := types.Datetime(absolute) year, month, day, _ := datetime.ToDate().Calendar(true) if !types.ValidDatetime(year, month, day) { - return fmt.Errorf("TYPE_CONTRACT: DATETIME row %d is outside the SQL domain", row) + return moerr.NewInternalErrorNoCtxf("TYPE_CONTRACT: DATETIME row %d is outside the SQL domain", row) } } case types.T_timestamp: value := valueField.(*array.Timestamp).Value(row) if zero && value != 0 { - return fmt.Errorf("TYPE_CONTRACT: TIMESTAMP zero row %d has a non-zero placeholder", row) + return moerr.NewInternalErrorNoCtxf("TYPE_CONTRACT: TIMESTAMP zero row %d has a non-zero placeholder", row) } if !zero { absolute, ok := addUnixEpoch(int64(value)) if !ok || absolute < int64(types.TimestampMinValue) || absolute > int64(types.TimestampMaxValue) || !hasExactMicrosecondScale(absolute, descriptor.Scale) { - return fmt.Errorf("TYPE_CONTRACT: TIMESTAMP row %d is outside the declared SQL domain", row) + return moerr.NewInternalErrorNoCtxf("TYPE_CONTRACT: TIMESTAMP row %d is outside the declared SQL domain", row) } } } @@ -1620,7 +1616,7 @@ func hasExactMicrosecondScale(value int64, scale int32) bool { func decimalFromArray(value decimal128.Num, narrow bool) (any, error) { high, low := uint64(value.HighBits()), value.LowBits() if narrow && !decimal128FitsDecimal64(high, low) { - return nil, fmt.Errorf("TYPE_CONTRACT: Decimal128 result does not fit Decimal64") + return nil, moerr.NewInternalErrorNoCtxf("TYPE_CONTRACT: Decimal128 result does not fit Decimal64") } if narrow { return types.Decimal64(low), nil diff --git a/pkg/udf/python/artifact.go b/pkg/udf/python/artifact.go index ad374605bdebb..c0175ae01d147 100644 --- a/pkg/udf/python/artifact.go +++ b/pkg/udf/python/artifact.go @@ -17,6 +17,7 @@ package python import ( "context" "fmt" + "github.com/matrixorigin/matrixone/pkg/util/errutil" "strings" "unicode/utf8" @@ -68,20 +69,17 @@ var _ ArtifactStore = (*FileArtifactStore)(nil) func NewFileArtifactStore(fs fileservice.FileService, maxSize int64) (*FileArtifactStore, error) { if fs == nil { - return nil, fmt.Errorf("RESOURCE_UNAVAILABLE: Python artifact store has no FileService") + return nil, moerr.NewInternalErrorNoCtxf("RESOURCE_UNAVAILABLE: Python artifact store has no FileService") } shared, err := fileservice.Get[fileservice.FileService](fs, defines.SharedFileServiceName) if err != nil { - return nil, fmt.Errorf("RESOURCE_UNAVAILABLE: Python artifact store has no shared FileService: %w", err) + return nil, errutil.Wrapf(err, "RESOURCE_UNAVAILABLE: Python artifact store has no shared FileService") } if maxSize <= 0 { maxSize = DefaultMaxArtifactBytes } if maxSize > DefaultMaxArtifactBytes { - return nil, fmt.Errorf( - "UNSUPPORTED_ROUTINE_VERSION: Python artifact limit %d exceeds the current contract limit %d", - maxSize, DefaultMaxArtifactBytes, - ) + return nil, moerr.NewInternalErrorNoCtxf("UNSUPPORTED_ROUTINE_VERSION: Python artifact limit %d exceeds the current contract limit %d", maxSize, DefaultMaxArtifactBytes) } return &FileArtifactStore{fs: shared, maxSize: maxSize}, nil } @@ -111,17 +109,17 @@ func (s *FileArtifactStore) Publish(ctx context.Context, accountID uint64, handl return digest, nil } if !moerr.IsMoErrCode(err, moerr.ErrFileAlreadyExists) { - return "", fmt.Errorf("python artifact publish %s: %w", digest, err) + return "", errutil.Wrapf(err, "python artifact publish %s", digest) } // A concurrent or retrying publisher is successful only after reading and // validating the existing object. This closes the write-once collision // boundary instead of treating path existence as proof of content identity. existing, resolveErr := s.Resolve(ctx, accountID, handler, digest) if resolveErr != nil { - return "", fmt.Errorf("python artifact publish %s found an invalid existing object: %w", digest, resolveErr) + return "", errutil.Wrapf(resolveErr, "python artifact publish %s found an invalid existing object", digest) } if existing != source { - return "", fmt.Errorf("python artifact publish %s encountered a digest collision", digest) + return "", moerr.NewInternalErrorNoCtxf("python artifact publish %s encountered a digest collision", digest) } return digest, nil } @@ -134,18 +132,18 @@ func (s *FileArtifactStore) Resolve(ctx context.Context, accountID uint64, handl ctx = context.Background() } if strings.TrimSpace(handler) == "" { - return "", fmt.Errorf("UNSUPPORTED_ROUTINE_VERSION: Python artifact has no handler") + return "", moerr.NewInternalErrorNoCtxf("UNSUPPORTED_ROUTINE_VERSION: Python artifact has no handler") } if !udf.IsSHA256Digest(digest) { - return "", fmt.Errorf("UNSUPPORTED_ROUTINE_VERSION: Python artifact digest is invalid") + return "", moerr.NewInternalErrorNoCtxf("UNSUPPORTED_ROUTINE_VERSION: Python artifact digest is invalid") } path := artifactPath(accountID, digest) entry, err := s.fs.StatFile(ctx, path) if err != nil { - return "", fmt.Errorf("RESOURCE_UNAVAILABLE: Python artifact %s is unavailable: %w", digest, err) + return "", errutil.Wrapf(err, "RESOURCE_UNAVAILABLE: Python artifact %s is unavailable", digest) } if entry == nil || entry.IsDir || entry.Size <= 0 || entry.Size > s.maxSize { - return "", fmt.Errorf("UNSUPPORTED_ROUTINE_VERSION: Python artifact %s has invalid size", digest) + return "", moerr.NewInternalErrorNoCtxf("UNSUPPORTED_ROUTINE_VERSION: Python artifact %s has invalid size", digest) } data := make([]byte, entry.Size) readVector := &fileservice.IOVector{ @@ -153,46 +151,46 @@ func (s *FileArtifactStore) Resolve(ctx context.Context, accountID uint64, handl Entries: []fileservice.IOEntry{{Offset: 0, Size: entry.Size, Data: data}}, } if err := s.fs.Read(ctx, readVector); err != nil { - return "", fmt.Errorf("RESOURCE_UNAVAILABLE: Python artifact %s cannot be read: %w", digest, err) + return "", errutil.Wrapf(err, "RESOURCE_UNAVAILABLE: Python artifact %s cannot be read", digest) } if len(readVector.Entries) != 1 || int64(len(readVector.Entries[0].Data)) != entry.Size { - return "", fmt.Errorf("UNSUPPORTED_ROUTINE_VERSION: Python artifact %s changed size while reading", digest) + return "", moerr.NewInternalErrorNoCtxf("UNSUPPORTED_ROUTINE_VERSION: Python artifact %s changed size while reading", digest) } source := string(readVector.Entries[0].Data) if !utf8.ValidString(source) { - return "", fmt.Errorf("UNSUPPORTED_ROUTINE_VERSION: Python artifact %s is not valid UTF-8", digest) + return "", moerr.NewInternalErrorNoCtxf("UNSUPPORTED_ROUTINE_VERSION: Python artifact %s is not valid UTF-8", digest) } if udf.PythonInlineArtifactDigest(handler, source) != digest { - return "", fmt.Errorf("UNSUPPORTED_ROUTINE_VERSION: Python artifact %s content digest mismatch", digest) + return "", moerr.NewInternalErrorNoCtxf("UNSUPPORTED_ROUTINE_VERSION: Python artifact %s content digest mismatch", digest) } return source, nil } func validateArtifactInput(accountID uint64, handler, source string, maxSize int64) (string, error) { if maxSize <= 0 { - return "", fmt.Errorf("RESOURCE_UNAVAILABLE: Python artifact store has an invalid size limit") + return "", moerr.NewInternalErrorNoCtxf("RESOURCE_UNAVAILABLE: Python artifact store has an invalid size limit") } if strings.TrimSpace(handler) == "" { - return "", fmt.Errorf("UNSUPPORTED_ROUTINE_VERSION: Python artifact has no handler") + return "", moerr.NewInternalErrorNoCtxf("UNSUPPORTED_ROUTINE_VERSION: Python artifact has no handler") } if source == "" { - return "", fmt.Errorf("UNSUPPORTED_ROUTINE_VERSION: Python artifact source is empty") + return "", moerr.NewInternalErrorNoCtxf("UNSUPPORTED_ROUTINE_VERSION: Python artifact source is empty") } if !utf8.ValidString(source) { - return "", fmt.Errorf("UNSUPPORTED_ROUTINE_VERSION: Python artifact source is not valid UTF-8") + return "", moerr.NewInternalErrorNoCtxf("UNSUPPORTED_ROUTINE_VERSION: Python artifact source is not valid UTF-8") } if int64(len([]byte(source))) > maxSize { - return "", fmt.Errorf("RESOURCE_EXHAUSTED: Python artifact exceeds %d bytes", maxSize) + return "", moerr.NewInternalErrorNoCtxf("RESOURCE_EXHAUSTED: Python artifact exceeds %d bytes", maxSize) } return udf.PythonInlineArtifactDigest(handler, source), nil } func (s *FileArtifactStore) validate() error { if s == nil || s.fs == nil { - return fmt.Errorf("RESOURCE_UNAVAILABLE: Python artifact store has no FileService") + return moerr.NewInternalErrorNoCtxf("RESOURCE_UNAVAILABLE: Python artifact store has no FileService") } if s.maxSize <= 0 { - return fmt.Errorf("RESOURCE_UNAVAILABLE: Python artifact store has an invalid size limit") + return moerr.NewInternalErrorNoCtxf("RESOURCE_UNAVAILABLE: Python artifact store has an invalid size limit") } return nil } diff --git a/pkg/udf/python/definition.go b/pkg/udf/python/definition.go index 2f172098158e8..3a2db3e2bbc65 100644 --- a/pkg/udf/python/definition.go +++ b/pkg/udf/python/definition.go @@ -18,7 +18,8 @@ import ( "crypto/sha256" "encoding/hex" "encoding/json" - "fmt" + "github.com/matrixorigin/matrixone/pkg/common/moerr" + "github.com/matrixorigin/matrixone/pkg/util/errutil" ) // definitionFingerprintBody is the canonical executable contract. Source is @@ -55,15 +56,15 @@ func DefinitionFingerprint( mode == "" || nullPolicy == "" || abiContract == "" || adapterVersion == "" || sdkVersion == "" || artifactDigest == "" || environmentDigest == "" { - return "", fmt.Errorf("python definition fingerprint has incomplete metadata") + return "", moerr.NewInternalErrorNoCtxf("python definition fingerprint has incomplete metadata") } for index, descriptor := range args { if err := descriptor.Validate(); err != nil { - return "", fmt.Errorf("argument descriptor %d: %w", index, err) + return "", errutil.Wrapf(err, "argument descriptor %d", index) } } if err := returnType.Validate(); err != nil { - return "", fmt.Errorf("return descriptor: %w", err) + return "", errutil.Wrapf(err, "return descriptor") } body := definitionFingerprintBody{ DefinitionSchemaVersion: definitionSchemaVersion, @@ -80,7 +81,7 @@ func DefinitionFingerprint( } canonical, err := json.Marshal(body) if err != nil { - return "", fmt.Errorf("marshal definition fingerprint: %w", err) + return "", errutil.Wrapf(err, "marshal definition fingerprint") } digest := sha256.Sum256(canonical) return hex.EncodeToString(digest[:]), nil diff --git a/pkg/udf/python/gateway.go b/pkg/udf/python/gateway.go index 27fb6033a6e68..fddb53d318c23 100644 --- a/pkg/udf/python/gateway.go +++ b/pkg/udf/python/gateway.go @@ -21,7 +21,8 @@ import ( "encoding/hex" "encoding/json" "errors" - "fmt" + "github.com/matrixorigin/matrixone/pkg/common/moerr" + "github.com/matrixorigin/matrixone/pkg/util/errutil" "io" "strconv" "strings" @@ -59,7 +60,7 @@ const ( statusOK = "OK" ) -var errGatewayClosed = errors.New("python udf gateway is closed") +var errGatewayClosed = moerr.NewInternalErrorNoCtx("python udf gateway is closed") var _ udf.RuntimeDefinitionValidator = (*Gateway)(nil) var _ udf.RuntimeStatusProvider = (*Gateway)(nil) @@ -110,7 +111,7 @@ func NewGateway(cfg ClientConfig) (*Gateway, error) { // rejected at this boundary. func NewGatewayWithArtifactStore(cfg ClientConfig, resolver ArtifactResolver) (*Gateway, error) { if resolver == nil { - return nil, fmt.Errorf("RESOURCE_UNAVAILABLE: Python artifact resolver is not configured") + return nil, moerr.NewInternalErrorNoCtxf("RESOURCE_UNAVAILABLE: Python artifact resolver is not configured") } return newGateway(cfg, resolver, false) } @@ -174,13 +175,13 @@ func (g *Gateway) CheckLanguageReady(ctx context.Context, language string) error ctx = context.Background() } if language != udf.LanguagePython { - return fmt.Errorf("python udf: unsupported readiness language %q", language) + return moerr.NewInternalErrorNoCtxf("python udf: unsupported readiness language %q", language) } if !g.cfg.Enabled { - return fmt.Errorf("RESOURCE_UNAVAILABLE: Python UDF runtime is disabled") + return moerr.NewInternalErrorNoCtxf("RESOURCE_UNAVAILABLE: Python UDF runtime is disabled") } if !g.cfg.AllowUnisolated { - return fmt.Errorf("RESOURCE_UNAVAILABLE: Python UDF runtime requires explicit unisolated opt-in") + return moerr.NewInternalErrorNoCtxf("RESOURCE_UNAVAILABLE: Python UDF runtime requires explicit unisolated opt-in") } requestCtx, release := g.withLifecycleContext(ctx) defer release() @@ -303,10 +304,10 @@ func (g *Gateway) ValidateDefinition(ctx context.Context, definition *udf.Routin return errGatewayClosed } if !g.cfg.Enabled { - return fmt.Errorf("RESOURCE_UNAVAILABLE: Python UDF runtime is disabled") + return moerr.NewInternalErrorNoCtxf("RESOURCE_UNAVAILABLE: Python UDF runtime is disabled") } if !g.cfg.AllowUnisolated { - return fmt.Errorf("RESOURCE_UNAVAILABLE: Python UDF runtime requires explicit unisolated opt-in") + return moerr.NewInternalErrorNoCtxf("RESOURCE_UNAVAILABLE: Python UDF runtime requires explicit unisolated opt-in") } requestCtx, release := g.withLifecycleContext(ctx) defer release() @@ -316,49 +317,49 @@ func (g *Gateway) ValidateDefinition(ctx context.Context, definition *udf.Routin defer cancel() } if definition == nil { - return fmt.Errorf("UNSUPPORTED_ROUTINE_VERSION: nil Python routine definition") + return moerr.NewInternalErrorNoCtxf("UNSUPPORTED_ROUTINE_VERSION: nil Python routine definition") } if definition.Language != udf.LanguagePython { - return fmt.Errorf("UNSUPPORTED_ROUTINE_VERSION: unsupported UDF language %q", definition.Language) + return moerr.NewInternalErrorNoCtxf("UNSUPPORTED_ROUTINE_VERSION: unsupported UDF language %q", definition.Language) } if strings.TrimSpace(definition.Handler) == "" { - return fmt.Errorf("UNSUPPORTED_ROUTINE_VERSION: Python handler is required") + return moerr.NewInternalErrorNoCtxf("UNSUPPORTED_ROUTINE_VERSION: Python handler is required") } if strings.Contains(definition.Handler, ":") { - return fmt.Errorf("UNSUPPORTED_ROUTINE_VERSION: Python external handler import requires the immutable artifact catalog") + return moerr.NewInternalErrorNoCtxf("UNSUPPORTED_ROUTINE_VERSION: Python external handler import requires the immutable artifact catalog") } if definition.DefinitionSchemaVersion != udf.PythonDefinitionSchemaVersion || definition.ABIContract != udf.PythonABIContract || definition.AdapterVersion != udf.PythonAdapterVersion || definition.SDKVersion != udf.PythonSDKVersion { - return fmt.Errorf("UNSUPPORTED_ROUTINE_VERSION: Python definition contract is not supported") + return moerr.NewInternalErrorNoCtxf("UNSUPPORTED_ROUTINE_VERSION: Python definition contract is not supported") } if definition.Mode != ModeScalar && definition.Mode != ModeVector { - return fmt.Errorf("UNSUPPORTED_ROUTINE_VERSION: unsupported Python mode %q", definition.Mode) + return moerr.NewInternalErrorNoCtxf("UNSUPPORTED_ROUTINE_VERSION: unsupported Python mode %q", definition.Mode) } if definition.NullPolicy != NullCallHandler && definition.NullPolicy != NullReturnNull { - return fmt.Errorf("UNSUPPORTED_ROUTINE_VERSION: unsupported Python NULL policy %q", definition.NullPolicy) + return moerr.NewInternalErrorNoCtxf("UNSUPPORTED_ROUTINE_VERSION: unsupported Python NULL policy %q", definition.NullPolicy) } if !udf.IsSHA256Digest(definition.ArtifactDigest) || !udf.IsSHA256Digest(definition.EnvironmentDigest) { - return fmt.Errorf("UNSUPPORTED_ROUTINE_VERSION: Python definition digest is invalid") + return moerr.NewInternalErrorNoCtxf("UNSUPPORTED_ROUTINE_VERSION: Python definition digest is invalid") } currentEnvironment, err := udf.PythonEnvironmentDigest() if err != nil || currentEnvironment != definition.EnvironmentDigest { - return fmt.Errorf("UNSUPPORTED_ROUTINE_VERSION: Python environment digest does not match the current contract") + return moerr.NewInternalErrorNoCtxf("UNSUPPORTED_ROUTINE_VERSION: Python environment digest does not match the current contract") } if !udf.IsSHA256Digest(definition.DefinitionFingerprint) { - return fmt.Errorf("UNSUPPORTED_ROUTINE_VERSION: Python definition fingerprint is invalid") + return moerr.NewInternalErrorNoCtxf("UNSUPPORTED_ROUTINE_VERSION: Python definition fingerprint is invalid") } args := make([]TypeDescriptor, len(definition.Args)) for index, typ := range definition.Args { args[index], err = NewTypeDescriptor(typ) if err != nil { - return fmt.Errorf("UNSUPPORTED_ROUTINE_VERSION: Python argument descriptor %d: %w", index, err) + return errutil.Wrapf(err, "UNSUPPORTED_ROUTINE_VERSION: Python argument descriptor %d", index) } } returnDescriptor, err := NewTypeDescriptor(definition.ReturnType) if err != nil { - return fmt.Errorf("UNSUPPORTED_ROUTINE_VERSION: Python return descriptor: %w", err) + return errutil.Wrapf(err, "UNSUPPORTED_ROUTINE_VERSION: Python return descriptor") } expectedFingerprint, err := DefinitionFingerprint( definition.DefinitionSchemaVersion, @@ -368,10 +369,10 @@ func (g *Gateway) ValidateDefinition(ctx context.Context, definition *udf.Routin args, returnDescriptor, ) if err != nil { - return fmt.Errorf("UNSUPPORTED_ROUTINE_VERSION: Python definition fingerprint cannot be computed: %w", err) + return errutil.Wrapf(err, "UNSUPPORTED_ROUTINE_VERSION: Python definition fingerprint cannot be computed") } if definition.DefinitionFingerprint != expectedFingerprint { - return fmt.Errorf("UNSUPPORTED_ROUTINE_VERSION: Python definition fingerprint does not match the typed definition") + return moerr.NewInternalErrorNoCtxf("UNSUPPORTED_ROUTINE_VERSION: Python definition fingerprint does not match the typed definition") } source := definition.Source @@ -383,20 +384,20 @@ func (g *Gateway) ValidateDefinition(ctx context.Context, definition *udf.Routin return resolveErr } if source != "" && source != resolved { - return fmt.Errorf("UNSUPPORTED_ROUTINE_VERSION: Python artifact source does not match the immutable artifact") + return moerr.NewInternalErrorNoCtxf("UNSUPPORTED_ROUTINE_VERSION: Python artifact source does not match the immutable artifact") } source = resolved } else if !g.allowInlineSource { - return fmt.Errorf("RESOURCE_UNAVAILABLE: Python artifact resolver is not configured") + return moerr.NewInternalErrorNoCtxf("RESOURCE_UNAVAILABLE: Python artifact resolver is not configured") } if source == "" || !utf8.ValidString(source) { - return fmt.Errorf("UNSUPPORTED_ROUTINE_VERSION: Python artifact source is empty or not valid UTF-8") + return moerr.NewInternalErrorNoCtxf("UNSUPPORTED_ROUTINE_VERSION: Python artifact source is empty or not valid UTF-8") } if int64(len([]byte(source))) > DefaultMaxArtifactBytes { - return fmt.Errorf("RESOURCE_EXHAUSTED: Python artifact exceeds %d bytes", DefaultMaxArtifactBytes) + return moerr.NewInternalErrorNoCtxf("RESOURCE_EXHAUSTED: Python artifact exceeds %d bytes", DefaultMaxArtifactBytes) } if udf.PythonInlineArtifactDigest(definition.Handler, source) != definition.ArtifactDigest { - return fmt.Errorf("UNSUPPORTED_ROUTINE_VERSION: Python artifact digest does not match the immutable source") + return moerr.NewInternalErrorNoCtxf("UNSUPPORTED_ROUTINE_VERSION: Python artifact digest does not match the immutable source") } client, err := g.flightClient() @@ -428,7 +429,7 @@ func (g *Gateway) ValidateDefinition(ctx context.Context, definition *udf.Routin Return: returnDescriptor, }) if err != nil { - return fmt.Errorf("python udf: encode definition validation: %w", err) + return errutil.Wrapf(err, "python udf: encode definition validation") } actionCtx := requestCtx var actionCancel context.CancelFunc @@ -439,53 +440,53 @@ func (g *Gateway) ValidateDefinition(ctx context.Context, definition *udf.Routin stream, err := client.DoAction(actionCtx, &flight.Action{Type: "ValidatePythonDefinition", Body: payload}) if err != nil { g.invalidateCapabilities(capability.generation) - return fmt.Errorf("python udf: definition validation action: %w", err) + return errutil.Wrapf(err, "python udf: definition validation action") } result, err := stream.Recv() if err != nil { g.invalidateCapabilities(capability.generation) - return fmt.Errorf("python udf: definition validation response: %w", err) + return errutil.Wrapf(err, "python udf: definition validation response") } if result == nil { g.invalidateCapabilities(capability.generation) - return fmt.Errorf("python udf: empty definition validation response") + return moerr.NewInternalErrorNoCtxf("python udf: empty definition validation response") } var response definitionValidationResponse decoder := json.NewDecoder(bytes.NewReader(result.Body)) decoder.DisallowUnknownFields() if err := decoder.Decode(&response); err != nil { g.invalidateCapabilities(capability.generation) - return fmt.Errorf("python udf: decode definition validation response: %w", err) + return errutil.Wrapf(err, "python udf: decode definition validation response") } var trailing any if err := decoder.Decode(&trailing); err != io.EOF { g.invalidateCapabilities(capability.generation) - return fmt.Errorf("python udf: definition validation response has trailing JSON") + return moerr.NewInternalErrorNoCtxf("python udf: definition validation response has trailing JSON") } if response.Status == "ERROR" { if _, err := stream.Recv(); err != io.EOF { g.invalidateCapabilities(capability.generation) if err == nil { - return fmt.Errorf("python udf: definition validation returned multiple results") + return moerr.NewInternalErrorNoCtxf("python udf: definition validation returned multiple results") } - return fmt.Errorf("python udf: definition validation stream: %w", err) + return errutil.Wrapf(err, "python udf: definition validation stream") } if response.Reason == "" { - return fmt.Errorf("USER_CODE: Python definition validation failed") + return moerr.NewInternalErrorNoCtxf("USER_CODE: Python definition validation failed") } - return errors.New(response.Reason) + return moerr.NewInternalErrorNoCtx(response.Reason) } if response.Status != statusOK || response.ArtifactDigest != definition.ArtifactDigest || response.DefinitionFingerprint != definition.DefinitionFingerprint { g.invalidateCapabilities(capability.generation) - return fmt.Errorf("UNSUPPORTED_ROUTINE_VERSION: Python definition validation returned an invalid result") + return moerr.NewInternalErrorNoCtxf("UNSUPPORTED_ROUTINE_VERSION: Python definition validation returned an invalid result") } if _, err := stream.Recv(); err != io.EOF { g.invalidateCapabilities(capability.generation) if err == nil { - return fmt.Errorf("python udf: definition validation returned multiple results") + return moerr.NewInternalErrorNoCtxf("python udf: definition validation returned multiple results") } - return fmt.Errorf("python udf: definition validation stream: %w", err) + return errutil.Wrapf(err, "python udf: definition validation stream") } return nil } @@ -615,22 +616,22 @@ func (g *Gateway) Execute(ctx context.Context, invocation *udf.Invocation, resul return errGatewayClosed } if !g.cfg.Enabled { - return fmt.Errorf("RESOURCE_UNAVAILABLE: Python UDF runtime is disabled") + return moerr.NewInternalErrorNoCtxf("RESOURCE_UNAVAILABLE: Python UDF runtime is disabled") } if !g.cfg.AllowUnisolated { - return fmt.Errorf("RESOURCE_UNAVAILABLE: Python UDF runtime requires explicit unisolated opt-in") + return moerr.NewInternalErrorNoCtxf("RESOURCE_UNAVAILABLE: Python UDF runtime requires explicit unisolated opt-in") } requestCtx, release := g.withLifecycleContext(ctx) defer release() streamCtx, cancel := context.WithTimeout(requestCtx, g.cfg.RequestTimeout) defer cancel() if invocation == nil || result == nil || mp == nil { - return fmt.Errorf("python udf: nil invocation, result, or memory pool") + return moerr.NewInternalErrorNoCtxf("python udf: nil invocation, result, or memory pool") } execution := *invocation if g.artifactResolver != nil { if execution.Source != "" { - return fmt.Errorf("UNSUPPORTED_ROUTINE_VERSION: typed Python invocation contains source; use its immutable artifact digest") + return moerr.NewInternalErrorNoCtxf("UNSUPPORTED_ROUTINE_VERSION: typed Python invocation contains source; use its immutable artifact digest") } // Validate the identity, semantic contract, descriptors, and resource // shape before resolving an account-scoped artifact. Resolution is a @@ -650,7 +651,7 @@ func (g *Gateway) Execute(ctx context.Context, invocation *udf.Invocation, resul return err } } else if !g.allowInlineSource && execution.Source == "" { - return fmt.Errorf("RESOURCE_UNAVAILABLE: Python artifact resolver is not configured") + return moerr.NewInternalErrorNoCtxf("RESOURCE_UNAVAILABLE: Python artifact resolver is not configured") } if err := validateInvocation(&execution); err != nil { return err @@ -712,7 +713,7 @@ func (g *Gateway) Execute(ctx context.Context, invocation *udf.Invocation, resul // invalidation below forces a fresh handshake on the next call. workerLeaseEpoch := capability.response.LeaseEpoch if workerLeaseEpoch == 0 { - return fmt.Errorf("python udf: capability handshake did not return a worker lease epoch") + return moerr.NewInternalErrorNoCtxf("python udf: capability handshake did not return a worker lease epoch") } execution.Tuple.LeaseEpoch = workerLeaseEpoch if err := validateInvocation(&execution); err != nil { @@ -739,7 +740,7 @@ func (g *Gateway) Execute(ctx context.Context, invocation *udf.Invocation, resul HandlerTimeoutSeconds: g.cfg.RequestTimeout.Seconds(), }) if err != nil { - return fmt.Errorf("python udf: encode open payload: %w", err) + return errutil.Wrapf(err, "python udf: encode open payload") } open, err := protocol.MarshalControl(protocol.Control{Kind: "OpenInvocation", Tuple: execution.Tuple, Payload: openBody}) if err != nil { @@ -765,7 +766,7 @@ func (g *Gateway) Execute(ctx context.Context, invocation *udf.Invocation, resul stream, err := client.DoExchange(streamCtx) if err != nil { - return fmt.Errorf("python udf: open Arrow Flight exchange: %w", err) + return errutil.Wrapf(err, "python udf: open Arrow Flight exchange") } // Flight carries the invocation command in the descriptor. The descriptor // is delivered to the server separately from the Arrow IPC reader; putting @@ -797,7 +798,7 @@ func (g *Gateway) Execute(ctx context.Context, invocation *udf.Invocation, resul return err } if len(batch.Frames) != 2 { - return fmt.Errorf("python udf: input batch contains %d Arrow frames", len(batch.Frames)) + return moerr.NewInternalErrorNoCtxf("python udf: input batch contains %d Arrow frames", len(batch.Frames)) } if batchIndex == 0 { inputSchema = append([]byte(nil), batch.Frames[0].Header...) @@ -805,7 +806,7 @@ func (g *Gateway) Execute(ctx context.Context, invocation *udf.Invocation, resul return err } } else if !bytes.Equal(inputSchema, batch.Frames[0].Header) { - return fmt.Errorf("python udf: input batches do not share one Arrow schema") + return moerr.NewInternalErrorNoCtxf("python udf: input batches do not share one Arrow schema") } sequenceNumber := batchIndex + 1 if err := sequence.AcceptInput(sequenceNumber); err != nil { @@ -851,7 +852,7 @@ func (g *Gateway) Execute(ctx context.Context, invocation *udf.Invocation, resul } } if !inputEnded || lastSequence != batchIndex { - return fmt.Errorf("python udf: input stream did not reach EndInput") + return moerr.NewInternalErrorNoCtxf("python udf: input stream did not reach EndInput") } // Close the local Arrow writer before accepting the worker's terminal // Finish. If finalizing the local input owner fails, the admission cleanup @@ -886,7 +887,7 @@ type invocationAdmission struct { func (g *Gateway) admitInvocation(invocation *udf.Invocation) (*invocationAdmission, error) { if invocation == nil { - return nil, fmt.Errorf("python udf: cannot admit a nil invocation") + return nil, moerr.NewInternalErrorNoCtxf("python udf: cannot admit a nil invocation") } if err := invocation.Tuple.Validate(); err != nil { return nil, err @@ -923,7 +924,7 @@ func (g *Gateway) admitInvocation(invocation *udf.Invocation) (*invocationAdmiss if ledger == nil || active == nil { g.releaseGroupClaim(groupKey, invocation.Tuple.GroupEpoch) g.mu.Unlock() - return nil, fmt.Errorf("python udf: admission state is not initialized") + return nil, moerr.NewInternalErrorNoCtxf("python udf: admission state is not initialized") } // Expire is intentionally limited to tombstones by TerminalLedger. An // active invocation can leave this path only through finish/abandon. @@ -936,7 +937,7 @@ func (g *Gateway) admitInvocation(invocation *udf.Invocation) (*invocationAdmiss if err != nil { g.releaseGroupClaim(groupKey, invocation.Tuple.GroupEpoch) g.mu.Unlock() - return nil, fmt.Errorf("RESOURCE_EXHAUSTED: Python UDF terminal ledger: %w", err) + return nil, errutil.Wrapf(err, "RESOURCE_EXHAUSTED: Python UDF terminal ledger") } if err := credit.Add(key, entryBytes, time.Now().Add(ttl)); err != nil { credit.ReleaseUnused() @@ -954,7 +955,7 @@ func (g *Gateway) admitInvocation(invocation *udf.Invocation) (*invocationAdmiss credit.ReleaseUnused() g.releaseGroupClaim(groupKey, invocation.Tuple.GroupEpoch) g.mu.Unlock() - return nil, fmt.Errorf("RESOURCE_EXHAUSTED: Python UDF active invocation slots are full") + return nil, moerr.NewInternalErrorNoCtxf("RESOURCE_EXHAUSTED: Python UDF active invocation slots are full") } var releaseOnce sync.Once @@ -1055,10 +1056,10 @@ func (g *Gateway) ensureAdmissionState() error { func (g *Gateway) claimGroupLocked(groupID string, groupEpoch uint64) error { if closedEpoch, ok := g.closedGroups[groupID]; ok && groupEpoch <= closedEpoch { - return fmt.Errorf("%w: execution group %q epoch %d is already closed", protocol.ErrDuplicate, groupID, groupEpoch) + return errutil.WrapfCauseFirst(protocol.ErrDuplicate, "execution group %q epoch %d is already closed", groupID, groupEpoch) } if admittedEpoch, ok := g.admittedGroups[groupID]; ok { - return fmt.Errorf("%w: execution group %q epoch %d is already admitted", protocol.ErrDuplicate, groupID, admittedEpoch) + return errutil.WrapfCauseFirst(protocol.ErrDuplicate, "execution group %q epoch %d is already admitted", groupID, admittedEpoch) } if _, exists := g.closedGroups[groupID]; !exists { // Reserve the future tombstone while the group is active. Checking only @@ -1067,7 +1068,7 @@ func (g *Gateway) claimGroupLocked(groupID string, groupEpoch uint64) error { // drain together. if len(g.closedGroups)+len(g.admittedGroups) >= g.closedGroupLimit || g.closedGroupBytes+g.reservedGroupBytes+int64(len(groupID)+8) > g.closedGroupByteLimit { - return fmt.Errorf("%w: Python UDF closed-group fence is full", protocol.ErrLedgerFull) + return errutil.WrapfCauseFirst(protocol.ErrLedgerFull, "Python UDF closed-group fence is full") } g.reservedGroupBytes += int64(len(groupID) + 8) } @@ -1120,7 +1121,7 @@ func scopedGroupKey(accountID uint64, groupID string) string { func invocationLedgerKey(tuple protocol.FencingTuple) (string, error) { wire, err := json.Marshal(tuple) if err != nil { - return "", fmt.Errorf("python udf: encode invocation identity: %w", err) + return "", errutil.Wrapf(err, "python udf: encode invocation identity") } digest := sha256.Sum256(wire) return hex.EncodeToString(digest[:]), nil @@ -1308,7 +1309,7 @@ func (g *Gateway) ensureCapabilitySnapshot(ctx context.Context, client flight.Fl if g.closed { err = errGatewayClosed } else if p.generation != g.capabilityGeneration { - err = errors.New("python udf: capability probe invalidated") + err = moerr.NewInternalErrorNoCtx("python udf: capability probe invalidated") } else if ctx.Err() != nil { err = ctx.Err() } @@ -1331,33 +1332,33 @@ func readCapabilities(ctx context.Context, client flight.FlightServiceClient) (* } stream, err := client.DoAction(ctx, &flight.Action{Type: "GetPythonCapabilities", Body: request}) if err != nil { - return nil, fmt.Errorf("python udf: capability handshake: %w", err) + return nil, errutil.Wrapf(err, "python udf: capability handshake") } result, err := stream.Recv() if err != nil { - return nil, fmt.Errorf("python udf: capability handshake response: %w", err) + return nil, errutil.Wrapf(err, "python udf: capability handshake response") } if result == nil { - return nil, fmt.Errorf("python udf: empty capability handshake response") + return nil, moerr.NewInternalErrorNoCtxf("python udf: empty capability handshake response") } var response capabilityResponse decoder := json.NewDecoder(bytes.NewReader(result.Body)) decoder.DisallowUnknownFields() if err := decoder.Decode(&response); err != nil { - return nil, fmt.Errorf("python udf: decode capability handshake: %w", err) + return nil, errutil.Wrapf(err, "python udf: decode capability handshake") } var trailing any if err := decoder.Decode(&trailing); err != io.EOF { - return nil, fmt.Errorf("python udf: capability handshake has trailing JSON") + return nil, moerr.NewInternalErrorNoCtxf("python udf: capability handshake has trailing JSON") } if err := validateCapabilities(response); err != nil { return nil, err } if _, err := stream.Recv(); err != io.EOF { if err == nil { - return nil, fmt.Errorf("python udf: capability handshake returned multiple results") + return nil, moerr.NewInternalErrorNoCtxf("python udf: capability handshake returned multiple results") } - return nil, fmt.Errorf("python udf: capability handshake stream: %w", err) + return nil, errutil.Wrapf(err, "python udf: capability handshake stream") } return &response, nil } @@ -1398,11 +1399,11 @@ func validateCapabilities(response capabilityResponse) error { response.MaxAccountHandlerProcesses != DefaultWorkerMaxAccountHandlers || response.MaxOwnerHandlerProcesses != DefaultWorkerMaxOwnerHandlers || response.LeaseEpoch == 0 { - return fmt.Errorf("python udf: worker capability contract does not match the current contract") + return moerr.NewInternalErrorNoCtxf("python udf: worker capability contract does not match the current contract") } if !sameStrings(response.Modes, []string{ModeScalar, ModeVector}) || !sameStrings(response.NullPolicies, []string{NullCallHandler, NullReturnNull}) { - return fmt.Errorf("python udf: worker capability set does not match the current contract") + return moerr.NewInternalErrorNoCtxf("python udf: worker capability set does not match the current contract") } return nil } @@ -1437,10 +1438,10 @@ func encodeInputBatch(inputs []*vector.Vector, args []types.Type, start, remaini func encodeInputBatchWithEncoder(encoder *inputBatchEncoder, start, remaining, maxBytes, maxRows int64) (encodedRecordBatch, error) { if start < 0 || remaining <= 0 { - return encodedRecordBatch{}, fmt.Errorf("invalid Python UDF input batch range") + return encodedRecordBatch{}, moerr.NewInternalErrorNoCtxf("invalid Python UDF input batch range") } if maxRows <= 0 { - return encodedRecordBatch{}, fmt.Errorf("invalid Python UDF max batch rows") + return encodedRecordBatch{}, moerr.NewInternalErrorNoCtxf("invalid Python UDF max batch rows") } if remaining > maxRows { remaining = maxRows @@ -1451,7 +1452,7 @@ func encodeInputBatchWithEncoder(encoder *inputBatchEncoder, start, remaining, m // slice keeps the same backing buffers, and every candidate is still encoded // and checked against maxBytes before publication. if encoder == nil { - return encodedRecordBatch{}, fmt.Errorf("invalid Python UDF input encoder") + return encodedRecordBatch{}, moerr.NewInternalErrorNoCtxf("invalid Python UDF input encoder") } if encoder.fixedWidth { record, _, err := encoder.build(int(start), int(remaining)) @@ -1537,7 +1538,7 @@ func chooseInputBatchWithFullCandidate(remaining, maxBytes int64, tryEncode func high = middle - 1 } if best == 0 { - return encodedRecordBatch{}, fmt.Errorf("%w: one Arrow row exceeds %d bytes", errArrowBatchTooLarge, maxBytes) + return encodedRecordBatch{}, errutil.WrapfCauseFirst(errArrowBatchTooLarge, "one Arrow row exceeds %d bytes", maxBytes) } return encodedRecordBatch{Frames: bestFrames, Rows: best}, nil } @@ -1561,17 +1562,17 @@ func (g *Gateway) receiveResultBatch( data, err := stream.Recv() if err != nil { if err == io.EOF { - return 0, fmt.Errorf("python udf: exchange ended before result sequence %d", sequenceNumber) + return 0, moerr.NewInternalErrorNoCtxf("python udf: exchange ended before result sequence %d", sequenceNumber) } - return 0, fmt.Errorf("python udf: receive result: %w", err) + return 0, errutil.Wrapf(err, "python udf: receive result") } if data == nil || (len(data.AppMetadata) == 0 && len(data.DataHeader) == 0) { - return 0, fmt.Errorf("python udf: empty Flight result") + return 0, moerr.NewInternalErrorNoCtxf("python udf: empty Flight result") } if len(data.DataHeader) > 0 { if *schemaFrame == nil { if len(data.DataBody) != 0 { - return 0, fmt.Errorf("python udf: result schema contains a body") + return 0, moerr.NewInternalErrorNoCtxf("python udf: result schema contains a body") } *schemaFrame = &ArrowFrame{Header: append([]byte(nil), data.DataHeader...)} if len(data.AppMetadata) != 0 { @@ -1583,17 +1584,17 @@ func (g *Gateway) receiveResultBatch( continue } if !*schemaReady { - return 0, fmt.Errorf("python udf: result schema metadata was not received") + return 0, moerr.NewInternalErrorNoCtxf("python udf: result schema metadata was not received") } control, err := protocol.UnmarshalControl(data.AppMetadata) if err != nil { return 0, err } if control.Tuple != tuple || control.Kind != "ResultBatch" || control.Sequence != sequenceNumber { - return 0, fmt.Errorf("python udf: unexpected result sequence %d", control.Sequence) + return 0, moerr.NewInternalErrorNoCtxf("python udf: unexpected result sequence %d", control.Sequence) } if !inputConsumed { - return 0, fmt.Errorf("python udf: result sequence %d arrived before InputConsumed", sequenceNumber) + return 0, moerr.NewInternalErrorNoCtxf("python udf: result sequence %d arrived before InputConsumed", sequenceNumber) } if err := sequence.AcceptResult(control.Sequence); err != nil { return 0, err @@ -1612,16 +1613,16 @@ func (g *Gateway) receiveResultBatch( // validation/publication boundary. decoded, err := DecodeRecordBatch(**schemaFrame, ArrowFrame{Header: headerSnapshot.TrustedBytes(), Body: bodySnapshot.TrustedBytes()}, g.cfg.MaxBatchBytes) if err != nil { - return 0, fmt.Errorf("python udf: decode result: %w", err) + return 0, errutil.Wrapf(err, "python udf: decode result") } decodedRows := decoded.NumRows() if decoded.NumCols() != 1 || decodedRows <= 0 || decodedRows != expectedRows { decoded.Release() - return 0, fmt.Errorf("python udf: result batch row count %d, expected %d", decodedRows, expectedRows) + return 0, moerr.NewInternalErrorNoCtxf("python udf: result batch row count %d, expected %d", decodedRows, expectedRows) } if decoded.Schema().Field(0).Name != "result" { decoded.Release() - return 0, fmt.Errorf("python udf: result field has unexpected name %q", decoded.Schema().Field(0).Name) + return 0, moerr.NewInternalErrorNoCtxf("python udf: result field has unexpected name %q", decoded.Schema().Field(0).Name) } if err := returnDescriptor.ValidateField(decoded.Schema().Field(0)); err != nil { decoded.Release() @@ -1633,7 +1634,7 @@ func (g *Gateway) receiveResultBatch( } if limit := g.cfg.MaxInvocationResultBytes; limit > 0 && result.GetResultVector() != nil && int64(result.GetResultVector().Size()) > limit { decoded.Release() - return 0, fmt.Errorf("RESOURCE_EXHAUSTED: Python UDF invocation result exceeds %d bytes", limit) + return 0, moerr.NewInternalErrorNoCtxf("RESOURCE_EXHAUSTED: Python UDF invocation result exceeds %d bytes", limit) } decoded.Release() if err := g.action(ctx, client, "AcknowledgeResults", protocol.Control{Kind: "AcknowledgeResults", Tuple: tuple, AckSequence: control.Sequence}); err != nil { @@ -1645,40 +1646,40 @@ func (g *Gateway) receiveResultBatch( return int(expectedRows), nil } if len(data.DataBody) != 0 { - return 0, fmt.Errorf("python udf: control frame contains an Arrow body") + return 0, moerr.NewInternalErrorNoCtxf("python udf: control frame contains an Arrow body") } control, err := protocol.UnmarshalControl(data.AppMetadata) if err != nil { return 0, err } if control.Tuple != tuple { - return 0, fmt.Errorf("python udf: result tuple changed") + return 0, moerr.NewInternalErrorNoCtxf("python udf: result tuple changed") } switch control.Kind { case "ResultSchema": if *schemaFrame == nil || *schemaReady { - return 0, fmt.Errorf("python udf: invalid result schema metadata") + return 0, moerr.NewInternalErrorNoCtxf("python udf: invalid result schema metadata") } *schemaReady = true continue case "InputConsumed": if control.Sequence != sequenceNumber { - return 0, fmt.Errorf("python udf: input sequence %d was consumed while waiting for result %d", control.Sequence, sequenceNumber) + return 0, moerr.NewInternalErrorNoCtxf("python udf: input sequence %d was consumed while waiting for result %d", control.Sequence, sequenceNumber) } if control.ReleasedBatches != 1 || control.ReleasedBytes <= 0 || control.ReleasedBytes > g.cfg.MaxBatchBytes { - return 0, fmt.Errorf("python udf: input sequence %d released invalid bytes/batches (%d/%d)", sequenceNumber, control.ReleasedBytes, control.ReleasedBatches) + return 0, moerr.NewInternalErrorNoCtxf("python udf: input sequence %d released invalid bytes/batches (%d/%d)", sequenceNumber, control.ReleasedBytes, control.ReleasedBatches) } if inputConsumed { - return 0, fmt.Errorf("python udf: input sequence %d was consumed more than once", sequenceNumber) + return 0, moerr.NewInternalErrorNoCtxf("python udf: input sequence %d was consumed more than once", sequenceNumber) } inputConsumed = true continue case "Error": - return 0, fmt.Errorf("python udf: worker error: %s", control.Reason) + return 0, moerr.NewInternalErrorNoCtxf("python udf: worker error: %s", control.Reason) case "Finish": - return 0, fmt.Errorf("python udf: Finish arrived before result sequence %d", sequenceNumber) + return 0, moerr.NewInternalErrorNoCtxf("python udf: Finish arrived before result sequence %d", sequenceNumber) default: - return 0, fmt.Errorf("python udf: unexpected control %q", control.Kind) + return 0, moerr.NewInternalErrorNoCtxf("python udf: unexpected control %q", control.Kind) } } } @@ -1689,12 +1690,12 @@ func (g *Gateway) receiveResultBatch( // header could bypass the body-only limit used by the old result path. func freezeResultFrames(header, body []byte, maxBytes int64) (*protocol.OutputSnapshot, *protocol.OutputSnapshot, error) { if maxBytes <= 0 { - return nil, nil, fmt.Errorf("invalid Python UDF result batch limit") + return nil, nil, moerr.NewInternalErrorNoCtxf("invalid Python UDF result batch limit") } headerBytes := int64(len(header)) bodyBytes := int64(len(body)) if headerBytes > maxBytes || bodyBytes > maxBytes-headerBytes { - return nil, nil, fmt.Errorf("RESOURCE_EXHAUSTED: Arrow result batch exceeds %d bytes", maxBytes) + return nil, nil, moerr.NewInternalErrorNoCtxf("RESOURCE_EXHAUSTED: Arrow result batch exceeds %d bytes", maxBytes) } headerSnapshot, err := protocol.FreezeOutput(header, maxBytes) if err != nil { @@ -1722,10 +1723,10 @@ type executionBudget struct { func validateInvocationBudget(budget executionBudget) error { if budget.rows < 0 { - return fmt.Errorf("python udf: invocation row count is negative") + return moerr.NewInternalErrorNoCtxf("python udf: invocation row count is negative") } if budget.maxRows > 0 && budget.rows > budget.maxRows { - return fmt.Errorf("RESOURCE_EXHAUSTED: Python UDF invocation has %d rows, limit is %d", budget.rows, budget.maxRows) + return moerr.NewInternalErrorNoCtxf("RESOURCE_EXHAUSTED: Python UDF invocation has %d rows, limit is %d", budget.rows, budget.maxRows) } if budget.maxResultBytes <= 0 || budget.rows == 0 { return nil @@ -1736,11 +1737,11 @@ func validateInvocationBudget(budget executionBudget) error { // each validated append because their area size depends on user values. validityBytes := (budget.rows-1)/8 + 1 if validityBytes >= budget.maxResultBytes { - return fmt.Errorf("RESOURCE_EXHAUSTED: Python UDF invocation result reservation exceeds %d bytes", budget.maxResultBytes) + return moerr.NewInternalErrorNoCtxf("RESOURCE_EXHAUSTED: Python UDF invocation result reservation exceeds %d bytes", budget.maxResultBytes) } perRow := int64(budget.returnType.TypeSize()) if perRow > 0 && budget.rows > (budget.maxResultBytes-validityBytes)/perRow { - return fmt.Errorf("RESOURCE_EXHAUSTED: Python UDF invocation result reservation exceeds %d bytes", budget.maxResultBytes) + return moerr.NewInternalErrorNoCtxf("RESOURCE_EXHAUSTED: Python UDF invocation result reservation exceeds %d bytes", budget.maxResultBytes) } return nil } @@ -1748,7 +1749,7 @@ func validateInvocationBudget(budget executionBudget) error { func validateResultSchemaMetadata(metadata []byte, tuple protocol.FencingTuple) error { control, err := protocol.UnmarshalControl(metadata) if err != nil || control.Tuple != tuple || control.Kind != "ResultSchema" { - return fmt.Errorf("python udf: invalid result schema metadata") + return moerr.NewInternalErrorNoCtxf("python udf: invalid result schema metadata") } return nil } @@ -1765,33 +1766,33 @@ func (g *Gateway) receiveFinish( data, err := stream.Recv() if err != nil { if err == io.EOF { - return fmt.Errorf("python udf: exchange ended before Finish") + return moerr.NewInternalErrorNoCtxf("python udf: exchange ended before Finish") } - return fmt.Errorf("python udf: receive Finish: %w", err) + return errutil.Wrapf(err, "python udf: receive Finish") } if data == nil || len(data.DataHeader) > 0 || len(data.DataBody) > 0 { - return fmt.Errorf("python udf: unexpected Arrow data while finishing") + return moerr.NewInternalErrorNoCtxf("python udf: unexpected Arrow data while finishing") } control, err := protocol.UnmarshalControl(data.AppMetadata) if err != nil { return err } if control.Tuple != tuple { - return fmt.Errorf("python udf: Finish tuple changed") + return moerr.NewInternalErrorNoCtxf("python udf: Finish tuple changed") } switch control.Kind { case "InputConsumed", "ResultSchema": - return fmt.Errorf("python udf: %s arrived after result stream was drained", control.Kind) + return moerr.NewInternalErrorNoCtxf("python udf: %s arrived after result stream was drained", control.Kind) case "Error": - return fmt.Errorf("python udf: worker error: %s", control.Reason) + return moerr.NewInternalErrorNoCtxf("python udf: worker error: %s", control.Reason) case "Finish": lastInput, lastResult, acked, inputEnded := sequence.State() if control.Status != statusOK || control.FinishID == "" || rows != expectedRows || !inputEnded || control.LastSequence != lastInput || lastResult != lastInput || acked != lastInput || !sequence.ReadyToFinish() { - return fmt.Errorf("python udf: invalid Finish status=%q rows=%d", control.Status, rows) + return moerr.NewInternalErrorNoCtxf("python udf: invalid Finish status=%q rows=%d", control.Status, rows) } if control.LastResultSequence != lastResult { - return fmt.Errorf("python udf: invalid Finish result sequence %d, expected %d", control.LastResultSequence, lastResult) + return moerr.NewInternalErrorNoCtxf("python udf: invalid Finish result sequence %d, expected %d", control.LastResultSequence, lastResult) } if err := g.acknowledgeFinish(ctx, client, "AcknowledgeFinish", protocol.Control{Kind: "AcknowledgeFinish", Tuple: tuple, FinishID: control.FinishID}); err != nil { return err @@ -1804,7 +1805,7 @@ func (g *Gateway) receiveFinish( // has returned and its cleanup owner has completed. return g.waitExchangeEOF(ctx, stream) default: - return fmt.Errorf("python udf: unexpected control %q", control.Kind) + return moerr.NewInternalErrorNoCtxf("python udf: unexpected control %q", control.Kind) } } } @@ -1816,10 +1817,10 @@ func (g *Gateway) waitExchangeEOF(ctx context.Context, stream flight.FlightServi return nil } if err != nil { - return fmt.Errorf("python udf: wait for exchange completion: %w", err) + return errutil.Wrapf(err, "python udf: wait for exchange completion") } if data == nil || len(data.DataHeader) != 0 || len(data.DataBody) != 0 || len(data.AppMetadata) != 0 { - return fmt.Errorf("python udf: exchange returned data after Finish ACK") + return moerr.NewInternalErrorNoCtxf("python udf: exchange returned data after Finish ACK") } } } @@ -1830,19 +1831,19 @@ func (g *Gateway) waitExchangeEOF(ctx context.Context, stream flight.FlightServi // between an accepted terminal result and a locally unconfirmed Finish. func (g *Gateway) acknowledgeFinish(ctx context.Context, client flight.FlightServiceClient, kind string, finish protocol.Control) error { if client == nil { - return fmt.Errorf("FINISH_UNCONFIRMED: finish ACK client is unavailable") + return moerr.NewInternalErrorNoCtxf("FINISH_UNCONFIRMED: finish ACK client is unavailable") } firstErr := g.action(ctx, client, kind, finish) if firstErr == nil { return nil } if ctx != nil && ctx.Err() != nil { - return fmt.Errorf("FINISH_UNCONFIRMED: finish ACK budget expired: %w", firstErr) + return errutil.Wrapf(firstErr, "FINISH_UNCONFIRMED: finish ACK budget expired") } if retryErr := g.action(ctx, client, kind, finish); retryErr == nil { return nil } else { - return fmt.Errorf("FINISH_UNCONFIRMED: finish ACK was not confirmed after idempotent retry: %v; retry: %w", firstErr, retryErr) + return errutil.Wrapf(retryErr, "FINISH_UNCONFIRMED: finish ACK was not confirmed after idempotent retry: %v; retry", firstErr) } } @@ -1851,7 +1852,7 @@ func validateInvocation(invocation *udf.Invocation) error { return err } if invocation.Source == "" { - return fmt.Errorf("python udf: handler and source are required") + return moerr.NewInternalErrorNoCtxf("python udf: handler and source are required") } // ArtifactResolver is an execution boundary, so the Gateway must enforce // the same immutable source contract even when a custom resolver does not @@ -1859,13 +1860,13 @@ func validateInvocation(invocation *udf.Invocation) error { // otherwise an oversized or invalid source can consume a large temporary // allocation and only fail at the Flight control-size boundary. if !utf8.ValidString(invocation.Source) { - return fmt.Errorf("UNSUPPORTED_ROUTINE_VERSION: Python artifact source is not valid UTF-8") + return moerr.NewInternalErrorNoCtxf("UNSUPPORTED_ROUTINE_VERSION: Python artifact source is not valid UTF-8") } if int64(len([]byte(invocation.Source))) > DefaultMaxArtifactBytes { - return fmt.Errorf("RESOURCE_EXHAUSTED: Python artifact exceeds %d bytes", DefaultMaxArtifactBytes) + return moerr.NewInternalErrorNoCtxf("RESOURCE_EXHAUSTED: Python artifact exceeds %d bytes", DefaultMaxArtifactBytes) } if invocation.ArtifactDigest != udf.PythonInlineArtifactDigest(invocation.Handler, invocation.Source) { - return fmt.Errorf("UNSUPPORTED_ROUTINE_VERSION: Python artifact digest does not match the immutable source") + return moerr.NewInternalErrorNoCtxf("UNSUPPORTED_ROUTINE_VERSION: Python artifact digest does not match the immutable source") } return nil } @@ -1877,80 +1878,80 @@ func validateInvocation(invocation *udf.Invocation) error { // be valid. func validateInvocationHeader(invocation *udf.Invocation) error { if invocation == nil { - return fmt.Errorf("python udf: nil invocation") + return moerr.NewInternalErrorNoCtxf("python udf: nil invocation") } if invocation.Language != udf.LanguagePython { - return fmt.Errorf("python udf: unsupported language %q", invocation.Language) + return moerr.NewInternalErrorNoCtxf("python udf: unsupported language %q", invocation.Language) } if invocation.Handler == "" { - return fmt.Errorf("python udf: handler is required") + return moerr.NewInternalErrorNoCtxf("python udf: handler is required") } if strings.Contains(invocation.Handler, ":") { - return fmt.Errorf("UNSUPPORTED_ROUTINE_VERSION: Python external handler import requires an immutable artifact catalog") + return moerr.NewInternalErrorNoCtxf("UNSUPPORTED_ROUTINE_VERSION: Python external handler import requires an immutable artifact catalog") } if invocation.Length < 0 || len(invocation.Args) != len(invocation.Inputs) { - return fmt.Errorf("python udf: invalid input shape") + return moerr.NewInternalErrorNoCtxf("python udf: invalid input shape") } if invocation.Mode != ModeScalar && invocation.Mode != ModeVector { - return fmt.Errorf("python udf: unsupported mode %q", invocation.Mode) + return moerr.NewInternalErrorNoCtxf("python udf: unsupported mode %q", invocation.Mode) } if invocation.CallsiteID == "" || len(invocation.CallsiteID) > 256 || strings.ContainsAny(invocation.CallsiteID, "\r\n") { - return fmt.Errorf("UNSUPPORTED_ROUTINE_VERSION: Python invocation has no valid callsite id") + return moerr.NewInternalErrorNoCtxf("UNSUPPORTED_ROUTINE_VERSION: Python invocation has no valid callsite id") } if !invocation.MayError || invocation.SecurityMode != "INVOKER" || invocation.Leakproof { - return fmt.Errorf("UNSUPPORTED_ROUTINE_VERSION: Python invocation semantic contract is not supported") + return moerr.NewInternalErrorNoCtxf("UNSUPPORTED_ROUTINE_VERSION: Python invocation semantic contract is not supported") } if invocation.StatementContext == nil { - return fmt.Errorf("UNSUPPORTED_ROUTINE_VERSION: Python invocation has no statement context") + return moerr.NewInternalErrorNoCtxf("UNSUPPORTED_ROUTINE_VERSION: Python invocation has no statement context") } if err := invocation.StatementContext.Validate(); err != nil { return err } if invocation.SecurityFrame == nil { - return fmt.Errorf("UNSUPPORTED_ROUTINE_VERSION: Python invocation has no security frame") + return moerr.NewInternalErrorNoCtxf("UNSUPPORTED_ROUTINE_VERSION: Python invocation has no security frame") } if err := invocation.SecurityFrame.Validate(); err != nil { return err } if invocation.SecurityFrame.Mode != invocation.SecurityMode { - return fmt.Errorf("UNSUPPORTED_ROUTINE_VERSION: Python invocation security mode does not match its frame") + return moerr.NewInternalErrorNoCtxf("UNSUPPORTED_ROUTINE_VERSION: Python invocation security mode does not match its frame") } if invocation.NullPolicy != NullCallHandler && invocation.NullPolicy != NullReturnNull { - return fmt.Errorf("python udf: unsupported NULL policy %q", invocation.NullPolicy) + return moerr.NewInternalErrorNoCtxf("python udf: unsupported NULL policy %q", invocation.NullPolicy) } if invocation.ABIContract != udf.PythonABIContract || invocation.AdapterVersion != udf.PythonAdapterVersion { - return fmt.Errorf("python udf: unsupported Python ABI contract %q/%q", invocation.ABIContract, invocation.AdapterVersion) + return moerr.NewInternalErrorNoCtxf("python udf: unsupported Python ABI contract %q/%q", invocation.ABIContract, invocation.AdapterVersion) } if invocation.SDKVersion != udf.PythonSDKVersion { - return fmt.Errorf("python udf: unsupported Python SDK %q", invocation.SDKVersion) + return moerr.NewInternalErrorNoCtxf("python udf: unsupported Python SDK %q", invocation.SDKVersion) } if invocation.DefinitionSchemaVersion != udf.PythonDefinitionSchemaVersion { - return fmt.Errorf("UNSUPPORTED_ROUTINE_VERSION: unsupported Python definition schema %d", invocation.DefinitionSchemaVersion) + return moerr.NewInternalErrorNoCtxf("UNSUPPORTED_ROUTINE_VERSION: unsupported Python definition schema %d", invocation.DefinitionSchemaVersion) } if !udf.IsSHA256Digest(invocation.ArtifactDigest) { - return fmt.Errorf("UNSUPPORTED_ROUTINE_VERSION: Python artifact digest does not match the immutable source") + return moerr.NewInternalErrorNoCtxf("UNSUPPORTED_ROUTINE_VERSION: Python artifact digest does not match the immutable source") } environmentDigest, err := udf.PythonEnvironmentDigest() if err != nil { - return fmt.Errorf("UNSUPPORTED_ROUTINE_VERSION: Python environment digest is unavailable: %w", err) + return errutil.Wrapf(err, "UNSUPPORTED_ROUTINE_VERSION: Python environment digest is unavailable") } if invocation.EnvironmentDigest != environmentDigest { - return fmt.Errorf("UNSUPPORTED_ROUTINE_VERSION: Python environment digest does not match the current worker contract") + return moerr.NewInternalErrorNoCtxf("UNSUPPORTED_ROUTINE_VERSION: Python environment digest does not match the current worker contract") } if !udf.IsSHA256Digest(invocation.DefinitionFingerprint) { - return fmt.Errorf("UNSUPPORTED_ROUTINE_VERSION: Python definition fingerprint is invalid") + return moerr.NewInternalErrorNoCtxf("UNSUPPORTED_ROUTINE_VERSION: Python definition fingerprint is invalid") } args := make([]TypeDescriptor, len(invocation.Args)) for index, typ := range invocation.Args { descriptor, err := NewTypeDescriptor(typ) if err != nil { - return fmt.Errorf("UNSUPPORTED_ROUTINE_VERSION: Python argument descriptor %d: %w", index, err) + return errutil.Wrapf(err, "UNSUPPORTED_ROUTINE_VERSION: Python argument descriptor %d", index) } args[index] = descriptor } returnDescriptor, err := NewTypeDescriptor(invocation.ReturnType) if err != nil { - return fmt.Errorf("UNSUPPORTED_ROUTINE_VERSION: Python return descriptor: %w", err) + return errutil.Wrapf(err, "UNSUPPORTED_ROUTINE_VERSION: Python return descriptor") } if err := validateInvocationInputs(invocation, args); err != nil { return err @@ -1963,23 +1964,23 @@ func validateInvocationHeader(invocation *udf.Invocation) error { args, returnDescriptor, ) if err != nil { - return fmt.Errorf("UNSUPPORTED_ROUTINE_VERSION: Python definition fingerprint cannot be computed: %w", err) + return errutil.Wrapf(err, "UNSUPPORTED_ROUTINE_VERSION: Python definition fingerprint cannot be computed") } if invocation.DefinitionFingerprint != expectedFingerprint { - return fmt.Errorf("UNSUPPORTED_ROUTINE_VERSION: Python definition fingerprint does not match the typed definition") + return moerr.NewInternalErrorNoCtxf("UNSUPPORTED_ROUTINE_VERSION: Python definition fingerprint does not match the typed definition") } if err := invocation.FunctionRef.Validate(); err != nil { - return fmt.Errorf("UNSUPPORTED_ROUTINE_VERSION: %w", err) + return errutil.Wrapf(err, "UNSUPPORTED_ROUTINE_VERSION") } if invocation.FunctionRef.AccountID != invocation.Tuple.AccountID { - return fmt.Errorf("python udf: FunctionRef account does not match the fencing tuple") + return moerr.NewInternalErrorNoCtxf("python udf: FunctionRef account does not match the fencing tuple") } return invocation.Tuple.Validate() } func validateInvocationInputs(invocation *udf.Invocation, args []TypeDescriptor) error { if invocation == nil || len(invocation.Inputs) != len(args) { - return fmt.Errorf("python udf: invalid input shape") + return moerr.NewInternalErrorNoCtxf("python udf: invalid input shape") } // An empty physical selection has no input backing to inspect. Nil inputs // are valid in this case, but a vector supplied by a caller still has to @@ -1991,33 +1992,20 @@ func validateInvocationInputs(invocation *udf.Invocation, args []TypeDescriptor) continue } if input.GetType() == nil || !input.GetType().Eq(invocation.Args[index]) { - return fmt.Errorf( - "python udf: input column %d type does not match the frozen argument type", - index, - ) + return moerr.NewInternalErrorNoCtxf("python udf: input column %d type does not match the frozen argument type", index) } } return nil } for index, input := range invocation.Inputs { if input == nil || input.GetType() == nil || input.Length() == 0 { - return fmt.Errorf("python udf: input column %d has no backing vector", index) + return moerr.NewInternalErrorNoCtxf("python udf: input column %d has no backing vector", index) } if !input.GetType().Eq(invocation.Args[index]) { - return fmt.Errorf( - "python udf: input column %d type %s does not match the frozen argument type %s", - index, - input.GetType().DescString(), - invocation.Args[index].DescString(), - ) + return moerr.NewInternalErrorNoCtxf("python udf: input column %d type %s does not match the frozen argument type %s", index, input.GetType().DescString(), invocation.Args[index].DescString()) } if !input.IsConst() && input.Length() < invocation.Length { - return fmt.Errorf( - "python udf: input column %d has %d rows, expected at least %d", - index, - input.Length(), - invocation.Length, - ) + return moerr.NewInternalErrorNoCtxf("python udf: input column %d has %d rows, expected at least %d", index, input.Length(), invocation.Length) } } return nil @@ -2032,14 +2020,14 @@ func (g *Gateway) action(ctx context.Context, client flight.FlightServiceClient, defer cancel() stream, err := client.DoAction(actionCtx, &flight.Action{Type: kind, Body: body}) if err != nil { - return fmt.Errorf("python udf: %s: %w", kind, err) + return errutil.Wrapf(err, "python udf: %s", kind) } result, err := stream.Recv() if err != nil { return err } if result == nil { - return fmt.Errorf("python udf: empty %s response", kind) + return moerr.NewInternalErrorNoCtxf("python udf: empty %s response", kind) } ack, err := protocol.UnmarshalControl(result.Body) if err != nil { @@ -2049,11 +2037,11 @@ func (g *Gateway) action(ctx context.Context, client flight.FlightServiceClient, return err } if ack.Status != statusOK { - return fmt.Errorf("python udf: %s rejected: %s", kind, ack.Reason) + return moerr.NewInternalErrorNoCtxf("python udf: %s rejected: %s", kind, ack.Reason) } if _, err := stream.Recv(); err != io.EOF { if err == nil { - return fmt.Errorf("python udf: %s returned multiple results", kind) + return moerr.NewInternalErrorNoCtxf("python udf: %s returned multiple results", kind) } return err } @@ -2062,19 +2050,19 @@ func (g *Gateway) action(ctx context.Context, client flight.FlightServiceClient, func validateActionAck(kind string, request, ack protocol.Control) error { if ack.Tuple != request.Tuple || ack.Kind != "Ack" { - return fmt.Errorf("python udf: %s returned an invalid ACK", kind) + return moerr.NewInternalErrorNoCtxf("python udf: %s returned an invalid ACK", kind) } switch kind { case "AcknowledgeResults": if ack.AckSequence != request.AckSequence { - return fmt.Errorf("python udf: %s ACK sequence %d, expected %d", kind, ack.AckSequence, request.AckSequence) + return moerr.NewInternalErrorNoCtxf("python udf: %s ACK sequence %d, expected %d", kind, ack.AckSequence, request.AckSequence) } case "AcknowledgeFinish": if ack.FinishID != request.FinishID { - return fmt.Errorf("python udf: %s ACK finish ID does not match the request", kind) + return moerr.NewInternalErrorNoCtxf("python udf: %s ACK finish ID does not match the request", kind) } default: - return fmt.Errorf("python udf: unsupported action %q", kind) + return moerr.NewInternalErrorNoCtxf("python udf: unsupported action %q", kind) } return nil } diff --git a/pkg/udf/python/supervisor.go b/pkg/udf/python/supervisor.go index af3de0793fa10..ed09bc63dc2b9 100644 --- a/pkg/udf/python/supervisor.go +++ b/pkg/udf/python/supervisor.go @@ -16,7 +16,8 @@ package python import ( "errors" - "fmt" + "github.com/matrixorigin/matrixone/pkg/common/moerr" + "github.com/matrixorigin/matrixone/pkg/util/errutil" "os" "os/exec" "path/filepath" @@ -25,7 +26,7 @@ import ( "github.com/matrixorigin/matrixone/pkg/logutil" ) -var errSupervisorClosing = errors.New("python udf worker is shutting down") +var errSupervisorClosing = moerr.NewInternalErrorNoCtx("python udf worker is shutting down") type Supervisor struct { cfg Config @@ -144,7 +145,7 @@ func (s *Supervisor) Close() error { var closeErr error if cmd != nil { if err := killSupervisorProcess(cmd); err != nil && !errors.Is(err, os.ErrProcessDone) { - closeErr = fmt.Errorf("stop Python UDF worker: %w", err) + closeErr = errutil.Wrapf(err, "stop Python UDF worker") } } if done != nil { diff --git a/pkg/udf/runtime.go b/pkg/udf/runtime.go index 72b3881f83bcc..16bed1eaef382 100644 --- a/pkg/udf/runtime.go +++ b/pkg/udf/runtime.go @@ -22,7 +22,7 @@ import ( "context" "encoding/json" "errors" - "fmt" + "github.com/matrixorigin/matrixone/pkg/util/errutil" "sort" "strconv" "strings" @@ -91,41 +91,41 @@ type StatementContext struct { func (c StatementContext) Validate() error { if c.ContractVersion != StatementContextContractVersion { - return fmt.Errorf("UNSUPPORTED_ROUTINE_VERSION: unsupported statement context contract %d", c.ContractVersion) + return moerr.NewInternalErrorNoCtxf("UNSUPPORTED_ROUTINE_VERSION: unsupported statement context contract %d", c.ContractVersion) } if c.StatementTimestampUTC < minStatementTimestampUTC || c.StatementTimestampUTC > maxStatementTimestampUTC { - return fmt.Errorf("python udf: statement timestamp is outside the Python datetime range") + return moerr.NewInternalErrorNoCtxf("python udf: statement timestamp is outside the Python datetime range") } if c.CurrentUser == "" || c.ConnectionCollation == "" { - return fmt.Errorf("python udf: statement context has no authenticated user or collation") + return moerr.NewInternalErrorNoCtxf("python udf: statement context has no authenticated user or collation") } if c.TimezoneKind == "IANA" { if c.TimezoneName == "" || c.TimezoneDatabaseVersion == "" || c.TimezoneOffsetMinutes != 0 { - return fmt.Errorf("python udf: IANA statement timezone is incomplete") + return moerr.NewInternalErrorNoCtxf("python udf: IANA statement timezone is incomplete") } if _, err := time.LoadLocation(c.TimezoneName); err != nil { - return fmt.Errorf("python udf: IANA statement timezone is not present in the local tzdb: %w", err) + return errutil.Wrapf(err, "python udf: IANA statement timezone is not present in the local tzdb") } } else if c.TimezoneKind == "FIXED_OFFSET" { if c.TimezoneName != "" || c.TimezoneDatabaseVersion != "" || c.TimezoneOffsetMinutes < -839 || c.TimezoneOffsetMinutes > 840 { - return fmt.Errorf("python udf: fixed statement timezone is invalid") + return moerr.NewInternalErrorNoCtxf("python udf: fixed statement timezone is invalid") } } else { - return fmt.Errorf("python udf: unsupported statement timezone kind %q", c.TimezoneKind) + return moerr.NewInternalErrorNoCtxf("python udf: unsupported statement timezone kind %q", c.TimezoneKind) } mode := append([]string(nil), c.SQLMode...) sort.Strings(mode) for i := range mode { if mode[i] == "" || (i > 0 && mode[i] == mode[i-1]) { - return fmt.Errorf("python udf: statement sql_mode is not canonical") + return moerr.NewInternalErrorNoCtxf("python udf: statement sql_mode is not canonical") } } if len(mode) != len(c.SQLMode) { - return fmt.Errorf("python udf: statement sql_mode is not canonical") + return moerr.NewInternalErrorNoCtxf("python udf: statement sql_mode is not canonical") } for i := range mode { if mode[i] != c.SQLMode[i] { - return fmt.Errorf("python udf: statement sql_mode is not sorted") + return moerr.NewInternalErrorNoCtxf("python udf: statement sql_mode is not sorted") } } return nil @@ -157,12 +157,12 @@ func StatementContextFromMap(values map[string]string) (*StatementContext, error } timestamp, err := strconv.ParseInt(values["statement_timestamp_utc"], 10, 64) if err != nil { - return nil, fmt.Errorf("python udf: invalid statement timestamp: %w", err) + return nil, errutil.Wrapf(err, "python udf: invalid statement timestamp") } mode := make([]string, 0) if raw := values["sql_mode"]; raw != "" { if err := json.Unmarshal([]byte(raw), &mode); err != nil { - return nil, fmt.Errorf("python udf: invalid statement sql_mode: %w", err) + return nil, errutil.Wrapf(err, "python udf: invalid statement sql_mode") } // The wire contract is a JSON array, including when it is empty. A // JSON null decodes successfully into a nil Go slice, but would be @@ -170,7 +170,7 @@ func StatementContextFromMap(values map[string]string) (*StatementContext, error // Reject it at the trusted CN adapter instead of carrying a malformed // snapshot into the Flight request. if mode == nil { - return nil, fmt.Errorf("python udf: invalid statement sql_mode: expected a JSON array") + return nil, moerr.NewInternalErrorNoCtxf("python udf: invalid statement sql_mode: expected a JSON array") } } context := &StatementContext{ @@ -188,7 +188,7 @@ func StatementContextFromMap(values map[string]string) (*StatementContext, error if raw := values["session_timezone_offset_minutes"]; raw != "" { offset, parseErr := strconv.ParseInt(raw, 10, 32) if parseErr != nil { - return nil, fmt.Errorf("python udf: invalid fixed timezone offset: %w", parseErr) + return nil, errutil.Wrapf(parseErr, "python udf: invalid fixed timezone offset") } context.TimezoneOffsetMinutes = int32(offset) } @@ -213,10 +213,10 @@ type SecurityFrame struct { func (f SecurityFrame) Validate() error { if f.ContractVersion != SecurityFrameContractVersion || f.Mode != "INVOKER" { - return fmt.Errorf("UNSUPPORTED_ROUTINE_VERSION: unsupported Python security frame") + return moerr.NewInternalErrorNoCtxf("UNSUPPORTED_ROUTINE_VERSION: unsupported Python security frame") } if f.InvokerUserID != f.EffectiveUserID || f.InvokerRoleID != f.EffectiveRoleID { - return fmt.Errorf("UNSUPPORTED_ROUTINE_VERSION: Python invoker security frame changed effective principal") + return moerr.NewInternalErrorNoCtxf("UNSUPPORTED_ROUTINE_VERSION: Python invoker security frame changed effective principal") } return nil } diff --git a/pkg/udf/timezone.go b/pkg/udf/timezone.go index 2995f5ca79759..cafda4adef42f 100644 --- a/pkg/udf/timezone.go +++ b/pkg/udf/timezone.go @@ -16,7 +16,7 @@ package udf import ( "bufio" - "fmt" + "github.com/matrixorigin/matrixone/pkg/common/moerr" "os" "strings" ) @@ -52,5 +52,5 @@ func TimezoneDatabaseVersion() (string, error) { } } } - return "", fmt.Errorf("python udf: IANA timezone database version is unavailable") + return "", moerr.NewInternalErrorNoCtxf("python udf: IANA timezone database version is unavailable") } diff --git a/pkg/util/errutil/errors.go b/pkg/util/errutil/errors.go index 5805b6918fe13..5106c0d03ecec 100644 --- a/pkg/util/errutil/errors.go +++ b/pkg/util/errutil/errors.go @@ -17,6 +17,7 @@ package errutil import ( "context" "errors" + "fmt" "sync/atomic" pkgErr "github.com/pkg/errors" @@ -42,6 +43,31 @@ func Wrapf(err error, format string, args ...any) error { return pkgErr.Wrapf(err, format, args...) } +type causeFirstError struct { + cause error + message string +} + +func (e *causeFirstError) Error() string { + if e.message == "" { + return e.cause.Error() + } + return e.cause.Error() + ": " + e.message +} + +func (e *causeFirstError) Unwrap() error { + return e.cause +} + +// WrapfCauseFirst preserves cause-first wrapped-error message order while +// retaining errors.Is/errors.As identity. +func WrapfCauseFirst(err error, format string, args ...any) error { + if err == nil { + return nil + } + return &causeFirstError{cause: err, message: fmt.Sprintf(format, args...)} +} + // WalkDeep does a depth-first traversal of all errors. // The visitor function can return true to end the traversal early, otherwise false. func WalkDeep(err error, visitor func(err error) bool) bool { diff --git a/pkg/util/errutil/errors_test.go b/pkg/util/errutil/errors_test.go index a36f6cb3a2f6a..21613cc7b182a 100644 --- a/pkg/util/errutil/errors_test.go +++ b/pkg/util/errutil/errors_test.go @@ -181,6 +181,13 @@ func TestWrapf(t *testing.T) { } } +func TestWrapfCauseFirst(t *testing.T) { + cause := context.Canceled + err := WrapfCauseFirst(cause, "detail %d", 7) + require.Equal(t, "context canceled: detail 7", err.Error()) + require.ErrorIs(t, err, cause) +} + func Test_noopReportError(t *testing.T) { type args struct { in0 context.Context From 39bfcd45cef04b2f34fef1e16d26dfc66b2ac105 Mon Sep 17 00:00:00 2001 From: iamlinjunhong <1030420200@qq.com> Date: Sun, 20 Sep 2026 19:46:14 +0800 Subject: [PATCH 191/202] fix(ci): align Python UDF runtime dependencies --- etc/launch-tae-compose/compose.yaml | 3 + optools/images/Dockerfile.prebuilt | 7 +++ optools/run_ut.sh | 61 +++++++++++++++++++ .../cases/dml/select/sp_table.result | 1 + .../cases/dml/show/database_statistics.result | 8 +-- test/distributed/cases/dml/show/show.result | 3 +- .../cases/metadata/information_schema.result | 1 + .../zz_accesscontrol/create_account.result | 1 + 8 files changed, 80 insertions(+), 5 deletions(-) diff --git a/etc/launch-tae-compose/compose.yaml b/etc/launch-tae-compose/compose.yaml index 713b7e0f9bed3..3ca71f49f20c4 100644 --- a/etc/launch-tae-compose/compose.yaml +++ b/etc/launch-tae-compose/compose.yaml @@ -30,6 +30,9 @@ x-mo-common: &mo-common x-python-udf-worker: &python-udf-worker image: matrixorigin/mo-python-udf-worker:latest + # The image is built by python-udf-worker-image in this Compose project. + # Do not race that local build with an unauthenticated registry pull. + pull_policy: never restart: on-failure profiles: - launch diff --git a/optools/images/Dockerfile.prebuilt b/optools/images/Dockerfile.prebuilt index f7d706d8e086c..ba424262a2458 100644 --- a/optools/images/Dockerfile.prebuilt +++ b/optools/images/Dockerfile.prebuilt @@ -13,6 +13,13 @@ ARG RUNTIME_IMAGE=matrixorigin/ubuntu:22.04 FROM ${RUNTIME_IMAGE} +# Keep the prebuilt runtime image's timezone database in sync with the +# Python UDF worker image. The Go and Python capability contracts compare the +# tzdata release, so omitting it makes the CN advertise an empty version. +RUN apt-get update \ + && DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends tzdata \ + && rm -rf /var/lib/apt/lists/* + COPY --chmod=0755 mo-service /mo-service COPY etc /etc COPY lib/*.so /usr/local/lib/ diff --git a/optools/run_ut.sh b/optools/run_ut.sh index 50495017ae1bb..351000d4c5f37 100755 --- a/optools/run_ut.sh +++ b/optools/run_ut.sh @@ -135,6 +135,58 @@ CGO_CFLAGS="-I${BUILD_WKSP}/cgo -I${THIRDPARTIES_INSTALL_DIR}/include" CGO_LDFLAGS="-Wl,-rpath,${THIRDPARTIES_INSTALL_DIR}/lib:${BUILD_WKSP}/cgo -L${THIRDPARTIES_INSTALL_DIR}/lib -L${BUILD_WKSP}/cgo -lmo -lusearch_c -lm" LD_LIBRARY_PATH="${THIRDPARTIES_INSTALL_DIR}/lib:${BUILD_WKSP}/cgo" +PYTHON_UDF_DEPS_DIR="" + +function cleanup_python_udf_dependencies(){ + if [[ -n "${PYTHON_UDF_DEPS_DIR}" ]]; then + rm -rf -- "${PYTHON_UDF_DEPS_DIR}" + PYTHON_UDF_DEPS_DIR="" + fi +} + +# UT starts the real Python UDF worker from the host's python3 executable. +# Keep the fallback dependency installation outside the worktree and clean it +# up even when the run exits through one of the script's signal handlers. +trap cleanup_python_udf_dependencies EXIT + +function prepare_python_udf_dependencies(){ + local python3_bin + python3_bin=$(command -v python3 || true) + if [[ -z "${python3_bin}" ]]; then + logger "ERR" "python3 is required for Python UDF UTs" + return 1 + fi + + if "${python3_bin}" -c 'import pyarrow' >/dev/null 2>&1; then + logger "INF" "Python UDF UT dependencies are already available" + return 0 + fi + + if ! "${python3_bin}" -m pip --version >/dev/null 2>&1; then + logger "ERR" "python3 cannot import pyarrow and its pip module is unavailable" + return 1 + fi + + PYTHON_UDF_DEPS_DIR=$(mktemp -d "${TMPDIR:-/tmp}/matrixone-ut-python.XXXXXX") || { + logger "ERR" "failed to create the temporary Python UDF dependency directory" + return 1 + } + if ! "${python3_bin}" -m pip install \ + --isolated --disable-pip-version-check --no-input --no-cache-dir \ + --only-binary=:all: --target "${PYTHON_UDF_DEPS_DIR}" \ + -r "${BUILD_WKSP}/pkg/udf/python/worker/requirements.txt"; then + logger "ERR" "failed to install Python UDF UT dependencies" + return 1 + fi + + export PYTHONPATH="${PYTHON_UDF_DEPS_DIR}${PYTHONPATH:+:${PYTHONPATH}}" + if ! "${python3_bin}" -c 'import pyarrow' >/dev/null 2>&1; then + logger "ERR" "Python UDF dependency bootstrap completed but pyarrow is still unavailable" + return 1 + fi + logger "INF" "Python UDF UT dependencies installed in ${PYTHON_UDF_DEPS_DIR}" +} + if [[ -n "${MO_CL_CUDA:-}" ]] ; then if [[ ${MO_CL_CUDA} == "1" ]] ; then if [[ -z "${CONDA_PREFIX:-}" ]] ; then @@ -2169,6 +2221,15 @@ function run_tests(){ fi mark_ut_stage "routing" "validate shard and package partition" finish 0 + mark_ut_stage "prepare" "prepare Python UDF dependencies" start + prepare_python_udf_dependencies + local python_udf_dependency_status=$? + mark_ut_stage "prepare" "prepare Python UDF dependencies" finish "${python_udf_dependency_status}" + if (( python_udf_dependency_status != 0 )); then + UT_TEST_STATUS=1 + return 0 + fi + mark_ut_stage "prepare" "clean go test cache" start logger "INF" "Clean go test cache" local testcache_status=0 diff --git a/test/distributed/cases/dml/select/sp_table.result b/test/distributed/cases/dml/select/sp_table.result index 72e09c82198c4..5455bd26b4933 100644 --- a/test/distributed/cases/dml/select/sp_table.result +++ b/test/distributed/cases/dml/select/sp_table.result @@ -19,6 +19,7 @@ mo_database r mo_feature_limit r mo_feature_registry r mo_foreign_keys r +mo_function_revisions r mo_iceberg_catalogs r mo_iceberg_maintenance_jobs r mo_iceberg_orphan_files r diff --git a/test/distributed/cases/dml/show/database_statistics.result b/test/distributed/cases/dml/show/database_statistics.result index 8a44e732fac20..3022ffc09d8cf 100644 --- a/test/distributed/cases/dml/show/database_statistics.result +++ b/test/distributed/cases/dml/show/database_statistics.result @@ -9,7 +9,7 @@ show table_number from mysql; 6 show table_number from mo_catalog; ➤ Number of tables in mo_catalog[-5,64,0] 𝄀 -59 +61 show table_number from system; [unknown result because it is related to issue#23182] use mo_task; @@ -93,7 +93,7 @@ show column_number from mo_role_privs; 10 show column_number from mo_user_defined_function; ➤ Number of columns in mo_user_defined_function[-5,64,0] 𝄀 -19 +25 show column_number from mo_tables; ➤ Number of columns in mo_tables[-5,64,0] 𝄀 19 @@ -350,7 +350,7 @@ show table_number from mysql; 6 show table_number from mo_catalog; ➤ Number of tables in mo_catalog[-5,64,0] 𝄀 -37 +38 show table_number from system_metrics; ➤ Number of tables in system_metrics[-5,64,0] 𝄀 11 @@ -430,7 +430,7 @@ show column_number from mo_role_privs; 10 show column_number from mo_user_defined_function; ➤ Number of columns in mo_user_defined_function[-5,64,0] 𝄀 -19 +25 show column_number from mo_tables; ➤ Number of columns in mo_tables[-5,64,0] 𝄀 19 diff --git a/test/distributed/cases/dml/show/show.result b/test/distributed/cases/dml/show/show.result index 36e8892e598d3..2e69146416996 100644 --- a/test/distributed/cases/dml/show/show.result +++ b/test/distributed/cases/dml/show/show.result @@ -269,6 +269,7 @@ mo_database 𝄀 mo_feature_limit 𝄀 mo_feature_registry 𝄀 mo_foreign_keys 𝄀 +mo_function_revisions 𝄀 mo_iceberg_catalogs 𝄀 mo_iceberg_maintenance_jobs 𝄀 mo_iceberg_orphan_files 𝄀 @@ -315,7 +316,7 @@ mo_view_dependencies 𝄀 mo_view_refresh show table_number from mo_catalog; ➤ Number of tables in mo_catalog[-5,64,0] 𝄀 -59 +61 show column_number from mo_database; ➤ Number of columns in mo_database[-5,64,0] 𝄀 9 diff --git a/test/distributed/cases/metadata/information_schema.result b/test/distributed/cases/metadata/information_schema.result index 596d68a566859..9ed0b41bd8747 100644 --- a/test/distributed/cases/metadata/information_schema.result +++ b/test/distributed/cases/metadata/information_schema.result @@ -38,6 +38,7 @@ def mo_catalog mo_database BASE TABLE Tae def mo_catalog mo_feature_limit BASE TABLE Tae def mo_catalog mo_feature_registry BASE TABLE Tae def mo_catalog mo_foreign_keys BASE TABLE Tae +def mo_catalog mo_function_revisions BASE TABLE Tae def mo_catalog mo_iceberg_catalogs BASE TABLE Tae def mo_catalog mo_iceberg_maintenance_jobs BASE TABLE Tae def mo_catalog mo_iceberg_orphan_files BASE TABLE Tae diff --git a/test/distributed/cases/zz_accesscontrol/create_account.result b/test/distributed/cases/zz_accesscontrol/create_account.result index 667d2096b068c..683702a393f7f 100644 --- a/test/distributed/cases/zz_accesscontrol/create_account.result +++ b/test/distributed/cases/zz_accesscontrol/create_account.result @@ -169,6 +169,7 @@ mo_columns mo_configurations mo_database mo_foreign_keys +mo_function_revisions mo_iceberg_catalogs mo_iceberg_maintenance_jobs mo_iceberg_orphan_files From 85032de3f767242a63b009fa9579d3116b779896 Mon Sep 17 00:00:00 2001 From: iamlinjunhong <1030420200@qq.com> Date: Sun, 20 Sep 2026 19:54:27 +0800 Subject: [PATCH 192/202] fix(udf): integrate concurrent CI contract repair --- .golangci.yml | 21 +- etc/launch-with-python-udf-worker/cn.toml | 13 + optools/images/Dockerfile.ci | 7 + optools/images/Dockerfile.prebuilt | 6 +- pkg/cnservice/python_runtime.go | 9 +- pkg/frontend/authenticate.go | 6 +- pkg/frontend/compiler_context.go | 34 +-- pkg/frontend/routine_error_contract_test.go | 41 +++ pkg/frontend/routine_namespace.go | 9 +- pkg/frontend/routine_plan_validation.go | 45 +-- pkg/frontend/snapshot_catalog_restore.go | 60 ++-- pkg/iceberg/api/import_boundary_test.go | 27 +- pkg/sql/colexec/external_routine_eval.go | 91 ++++--- pkg/sql/plan/function/python_udf.go | 36 +-- pkg/sql/plan/function/udf.go | 69 ++--- pkg/sql/plan/routine_dependency.go | 13 +- pkg/udf/protocol/protocol.go | 37 ++- pkg/udf/python/arrow.go | 163 +++++------ pkg/udf/python/artifact.go | 36 +-- pkg/udf/python/definition.go | 5 +- pkg/udf/python/gateway.go | 257 +++++++++--------- pkg/udf/python/gateway_integration_test.go | 17 +- pkg/udf/python/supervisor.go | 7 +- pkg/udf/runtime.go | 28 +- pkg/udf/timezone.go | 5 +- pkg/udf/udferr/errors.go | 33 +++ pkg/udf/udferr/errors_test.go | 35 +++ pkg/util/errutil/errors_test.go | 15 +- .../clone_sys_db_table_to_new_db_table.result | 2 + .../cases/mo_cloud/mo_cloud.result | 1 + .../cluster/restore_cluster_table.result | 14 +- .../cluster/restore_cluster_table.sql | 6 +- ...ster_level_snapshot_restore_cluster.result | 14 +- ...cluster_level_snapshot_restore_cluster.sql | 6 +- ...shot_restore_system_table_to_nonsys.result | 32 +-- ...napshot_restore_system_table_to_nonsys.sql | 12 +- ...restore_system_table_to_sys_account.result | 32 +-- ...ot_restore_system_table_to_sys_account.sql | 10 +- ...tore_system_table_to_nonsys_account.result | 32 +-- ...restore_system_table_to_nonsys_account.sql | 10 +- ...e_system_table_to_newnonsys_account.result | 32 +-- ...tore_system_table_to_newnonsys_account.sql | 10 +- ...tore_system_table_to_nonsys_account.result | 32 +-- ...restore_system_table_to_nonsys_account.sql | 12 +- ...restore_system_table_to_sys_account.result | 32 +-- ...ys_restore_system_table_to_sys_account.sql | 10 +- .../privilege/create_user_default_role.result | 1 + test/distributed/cases/udf_python/README.md | 16 +- 48 files changed, 825 insertions(+), 616 deletions(-) create mode 100644 pkg/frontend/routine_error_contract_test.go create mode 100644 pkg/udf/udferr/errors.go create mode 100644 pkg/udf/udferr/errors_test.go diff --git a/.golangci.yml b/.golangci.yml index 1d38a9fb11978..b8778e6ab54d9 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -35,20 +35,27 @@ linters: - $all - '!$test' - '!pkg/iceberg/adapter/iceberggo/**/*.go' - # Arrow IPC is isolated to the ingestion bridge and its external - # scan reader. Keep the dependency out of all other production - # packages, including the Iceberg-facing layers. + deny: + - pkg: github.com/apache/iceberg-go + desc: Iceberg dependency must stay behind pkg/iceberg/adapter/iceberggo + arrow-boundary: + list-mode: lax + files: + - $all + - '!$test' + - '!pkg/iceberg/adapter/iceberggo/**/*.go' + # Arrow belongs to the ingestion and external UDF boundaries. + # These owners do not also receive permission to import Iceberg. - '!**/pkg/container/arrowbridge/*.go' - '!**/pkg/sql/colexec/external/arrowio/*.go' - '!**/pkg/sql/colexec/external/reader_arrow.go' - '!**/pkg/sql/compile/compile.go' + - '!**/pkg/udf/python/*.go' deny: - - pkg: github.com/apache/iceberg-go - desc: Iceberg dependency must stay behind pkg/iceberg/adapter/iceberggo - pkg: github.com/apache/arrow-go - desc: Arrow dependency must not leak outside the Iceberg adapter + desc: Arrow dependency must stay behind an approved Arrow owner - pkg: github.com/apache/arrow/go - desc: Arrow dependency must not leak outside the Iceberg adapter + desc: Arrow dependency must stay behind an approved Arrow owner govet: disable: - fieldalignment diff --git a/etc/launch-with-python-udf-worker/cn.toml b/etc/launch-with-python-udf-worker/cn.toml index 76ae8790bf7b5..5cd29947a566a 100644 --- a/etc/launch-with-python-udf-worker/cn.toml +++ b/etc/launch-with-python-udf-worker/cn.toml @@ -1,6 +1,19 @@ service-type = "CN" data-dir = "./mo-data" +# Opt into the checksum-free DISK-V2 backend. The cluster-wide default remains +# DISK (CRC-framed) for backward compatibility with existing data dirs; this +# config explicitly selects DISK-V2 for a fresh deployment. +[[fileservice]] +name = "LOCAL" +backend = "DISK-V2" +data-dir = "./mo-data/local2" + +[[fileservice]] +name = "SHARED" +backend = "DISK-V2" +data-dir = "./mo-data/shared2" + [log] level = "info" diff --git a/optools/images/Dockerfile.ci b/optools/images/Dockerfile.ci index 289971ec8fbaa..e0ec9cc331a08 100644 --- a/optools/images/Dockerfile.ci +++ b/optools/images/Dockerfile.ci @@ -65,6 +65,13 @@ COPY --from=builder /go/pkg/mod /go/pkg/mod FROM ${RUNTIME_IMAGE} +# Python UDF admission compares the system IANA database with the worker. +# Keep every runtime assembly path aligned with optools/images/Dockerfile +# and pkg/udf/python/worker/Dockerfile, including shared-build artifacts. +RUN apt-get update \ + && DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends tzdata \ + && rm -rf /var/lib/apt/lists/* + COPY --from=builder /go/src/github.com/matrixorigin/matrixone/mo-service /mo-service COPY --from=builder /go/src/github.com/matrixorigin/matrixone/etc /etc COPY --from=builder /go/src/github.com/matrixorigin/matrixone/thirdparties/install/lib/*.so /usr/local/lib diff --git a/optools/images/Dockerfile.prebuilt b/optools/images/Dockerfile.prebuilt index ba424262a2458..dd876f57243e9 100644 --- a/optools/images/Dockerfile.prebuilt +++ b/optools/images/Dockerfile.prebuilt @@ -13,9 +13,9 @@ ARG RUNTIME_IMAGE=matrixorigin/ubuntu:22.04 FROM ${RUNTIME_IMAGE} -# Keep the prebuilt runtime image's timezone database in sync with the -# Python UDF worker image. The Go and Python capability contracts compare the -# tzdata release, so omitting it makes the CN advertise an empty version. +# Python UDF admission compares the system IANA database with the worker. +# Keep every runtime assembly path aligned with optools/images/Dockerfile +# and pkg/udf/python/worker/Dockerfile, including shared-build artifacts. RUN apt-get update \ && DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends tzdata \ && rm -rf /var/lib/apt/lists/* diff --git a/pkg/cnservice/python_runtime.go b/pkg/cnservice/python_runtime.go index 6487d4a863a67..9adc8ccd83c35 100644 --- a/pkg/cnservice/python_runtime.go +++ b/pkg/cnservice/python_runtime.go @@ -16,7 +16,8 @@ package cnservice import ( "context" - "github.com/matrixorigin/matrixone/pkg/common/moerr" + + "github.com/matrixorigin/matrixone/pkg/udf/udferr" "github.com/matrixorigin/matrixone/pkg/util/errutil" "go.uber.org/zap" @@ -73,7 +74,7 @@ func (r *unavailablePythonRuntime) Language() string { return udf.LanguagePython func (r *unavailablePythonRuntime) unavailableError() error { if r == nil || r.cause == nil { - return moerr.NewInternalErrorNoCtxf("RESOURCE_UNAVAILABLE: Python UDF runtime initialization failed") + return udferr.Newf("RESOURCE_UNAVAILABLE: Python UDF runtime initialization failed") } return errutil.Wrapf(r.cause, "RESOURCE_UNAVAILABLE: Python UDF runtime initialization failed") } @@ -84,7 +85,7 @@ func (r *unavailablePythonRuntime) Execute(context.Context, *udf.Invocation, vec func (r *unavailablePythonRuntime) CheckLanguageReady(ctx context.Context, language string) error { if language != udf.LanguagePython { - return moerr.NewInternalErrorNoCtxf("UNSUPPORTED_ROUTINE_VERSION: unsupported readiness language %q", language) + return udferr.Newf("UNSUPPORTED_ROUTINE_VERSION: unsupported readiness language %q", language) } if ctx != nil { select { @@ -98,7 +99,7 @@ func (r *unavailablePythonRuntime) CheckLanguageReady(ctx context.Context, langu func (r *unavailablePythonRuntime) ValidateDefinition(ctx context.Context, definition *udf.RoutineDefinition) error { if definition == nil || definition.Language != udf.LanguagePython { - return moerr.NewInternalErrorNoCtxf("UNSUPPORTED_ROUTINE_VERSION: invalid Python definition") + return udferr.Newf("UNSUPPORTED_ROUTINE_VERSION: invalid Python definition") } return r.CheckLanguageReady(ctx, udf.LanguagePython) } diff --git a/pkg/frontend/authenticate.go b/pkg/frontend/authenticate.go index bbd6773c57ee5..d720a8e845de8 100644 --- a/pkg/frontend/authenticate.go +++ b/pkg/frontend/authenticate.go @@ -31,6 +31,8 @@ import ( "sync/atomic" "time" + "github.com/matrixorigin/matrixone/pkg/util/errutil" + "github.com/tidwall/btree" "golang.org/x/sync/errgroup" @@ -12683,10 +12685,6 @@ func activateFunctionRevision(ctx context.Context, bh BackgroundExec, functionID )) } -func activatePythonRevision(ctx context.Context, bh BackgroundExec, functionID int64, revision, namespace uint64) error { - return activateFunctionRevision(ctx, bh, functionID, revision, namespace) -} - // storedProcedureDefinition is the procedure metadata kept outside mo_tables. // Callers control the transaction that persists it. type storedProcedureDefinition struct { diff --git a/pkg/frontend/compiler_context.go b/pkg/frontend/compiler_context.go index 45be39d08e7e6..df29d59ca9b65 100644 --- a/pkg/frontend/compiler_context.go +++ b/pkg/frontend/compiler_context.go @@ -20,13 +20,15 @@ import ( "encoding/json" "errors" "fmt" - "github.com/matrixorigin/matrixone/pkg/util/errutil" "slices" "strconv" "strings" "sync" "time" + "github.com/matrixorigin/matrixone/pkg/udf/udferr" + "github.com/matrixorigin/matrixone/pkg/util/errutil" + "go.uber.org/zap" "github.com/matrixorigin/matrixone/pkg/catalog" @@ -845,7 +847,7 @@ func (tcc *TxnCompilerContext) ResolveUdf(name string, args []*plan.Expr) (udf * return nil, err } if len(erArray) > 1 || (len(erArray) == 1 && erArray[0] == nil) { - return nil, moerr.NewInternalErrorNoCtxf("UNSUPPORTED_ROUTINE_VERSION: UDF %s lookup returned %d result sets, expected one or an empty candidate result", name, len(erArray)) + return nil, udferr.Newf("UNSUPPORTED_ROUTINE_VERSION: UDF %s lookup returned %d result sets, expected one or an empty candidate result", name, len(erArray)) } if execResultArrayHasData(erArray) { @@ -1014,7 +1016,7 @@ func (tcc *TxnCompilerContext) ResolveUdf(name string, args []*plan.Expr) (udf * if strings.EqualFold(matchedList[0].Udf.Language, string(tree.PYTHON)) { matchedList[0].Udf.ArgsType = function.PythonUdfArgTypeCast(fromList, matchedList[0].Udf.GetArgsType()) if len(matchedList[0].Udf.ArgsType) != len(fromList) { - return nil, moerr.NewInternalErrorNoCtxf("python udf: argument descriptor count changed during resolution") + return nil, udferr.Newf("python udf: argument descriptor count changed during resolution") } } else { matchedList[0].Udf.ArgsType = function.UdfArgTypeCast(fromList, matchedList[0].TypeList) @@ -1037,7 +1039,7 @@ func (tcc *TxnCompilerContext) ResolveUdf(name string, args []*plan.Expr) (udf * } matchedList[0].Udf.NamespaceFingerprint = namespaces[uint64(matchedList[0].Udf.FunctionID)] if namespaceErr == nil && matchedList[0].Udf.NamespaceFingerprint == "" { - return nil, moerr.NewInternalErrorNoCtxf("UNSUPPORTED_ROUTINE_VERSION: missing routine namespace") + return nil, udferr.Newf("UNSUPPORTED_ROUTINE_VERSION: missing routine namespace") } } @@ -1084,7 +1086,7 @@ func udfCatalogLookup( // catalog corruption from the planner/cache validator. func exactlyOneCatalogResultSet(ctx context.Context, rows []ExecResult, label string) (ExecResult, error) { if len(rows) != 1 || rows[0] == nil { - return nil, moerr.NewInternalErrorNoCtxf("UNSUPPORTED_ROUTINE_VERSION: %s returned %d result sets, expected exactly one", label, len(rows)) + return nil, udferr.Newf("UNSUPPORTED_ROUTINE_VERSION: %s returned %d result sets, expected exactly one", label, len(rows)) } return rows[0], nil } @@ -1112,7 +1114,7 @@ func exactlyOneCatalogRow(ctx context.Context, rows []ExecResult, label string) } rowCount := result.GetRowCount() if rowCount != 1 { - return nil, moerr.NewInternalErrorNoCtxf("UNSUPPORTED_ROUTINE_VERSION: %s returned %d rows, expected exactly one", label, rowCount) + return nil, udferr.Newf("UNSUPPORTED_ROUTINE_VERSION: %s returned %d rows, expected exactly one", label, rowCount) } return result, nil } @@ -1209,7 +1211,7 @@ func readSQLRevision( return sqlRevisionCatalogRow{}, false, nil } if activeRevision <= 0 || namespaceVersion <= 0 { - return sqlRevisionCatalogRow{}, false, moerr.NewInternalErrorNoCtxf("UNSUPPORTED_ROUTINE_VERSION: SQL function %d has an invalid revision head", functionID) + return sqlRevisionCatalogRow{}, false, udferr.Newf("UNSUPPORTED_ROUTINE_VERSION: SQL function %d has an invalid revision head", functionID) } bh.ClearExecResultSet() @@ -1283,15 +1285,15 @@ func readSQLRevision( volatility != "VOLATILE" || nullPolicy != udf.NullCallHandler || !strings.EqualFold(securityType, "DEFINER") || !strings.EqualFold(baseSecurityType, securityType) { - return sqlRevisionCatalogRow{}, false, moerr.NewInternalErrorNoCtxf("UNSUPPORTED_ROUTINE_VERSION: SQL function %d revision contract is not supported", functionID) + return sqlRevisionCatalogRow{}, false, udferr.Newf("UNSUPPORTED_ROUTINE_VERSION: SQL function %d revision contract is not supported", functionID) } logicalArgTypes, err := userDefinedFunctionArgumentTypesFromJSON(args) if err != nil || logicalArgTypes != argTypes { - return sqlRevisionCatalogRow{}, false, moerr.NewInternalErrorNoCtxf("UNSUPPORTED_ROUTINE_VERSION: SQL function %d revision argument metadata is invalid", functionID) + return sqlRevisionCatalogRow{}, false, udferr.Newf("UNSUPPORTED_ROUTINE_VERSION: SQL function %d revision argument metadata is invalid", functionID) } expectedFingerprint, err := function.SQLRoutineFingerprint(body, argTypes, retType) if err != nil || fingerprint == "" || fingerprint != expectedFingerprint { - return sqlRevisionCatalogRow{}, false, moerr.NewInternalErrorNoCtxf("UNSUPPORTED_ROUTINE_VERSION: SQL function %d revision fingerprint mismatch", functionID) + return sqlRevisionCatalogRow{}, false, udferr.Newf("UNSUPPORTED_ROUTINE_VERSION: SQL function %d revision fingerprint mismatch", functionID) } return sqlRevisionCatalogRow{ Args: args, ArgTypes: argTypes, Body: body, Language: language, RetType: retType, @@ -1328,7 +1330,7 @@ func readPythonRevision(ctx context.Context, bh BackgroundExec, functionID int64 return pythonRevisionCatalogRow{}, err } if activeRevision <= 0 || namespaceVersion <= 0 || revision != activeRevision { - return pythonRevisionCatalogRow{}, moerr.NewInternalErrorNoCtxf("UNSUPPORTED_ROUTINE_VERSION: Python function %d has an invalid revision head", functionID) + return pythonRevisionCatalogRow{}, udferr.Newf("UNSUPPORTED_ROUTINE_VERSION: Python function %d has an invalid revision head", functionID) } getString := func(column uint64) (string, error) { return row.GetString(ctx, 0, column) } canonicalInput, err := getString(3) @@ -1412,21 +1414,21 @@ func readPythonRevision(ctx context.Context, bh BackgroundExec, functionID int64 return pythonRevisionCatalogRow{}, err } if language != string(tree.PYTHON) || definitionSchema != udf.PythonDefinitionSchemaVersion || abi != udf.PythonABIContract || adapter != udf.PythonAdapterVersion || sdk != udf.PythonSDKVersion || volatility != "VOLATILE" || !strings.EqualFold(securityType, "INVOKER") || !strings.EqualFold(baseSecurityType, securityType) { - return pythonRevisionCatalogRow{}, moerr.NewInternalErrorNoCtxf("UNSUPPORTED_ROUTINE_VERSION: Python function %d revision contract is not supported", functionID) + return pythonRevisionCatalogRow{}, udferr.Newf("UNSUPPORTED_ROUTINE_VERSION: Python function %d revision contract is not supported", functionID) } bodyDefinition, err := function.DecodePythonRoutineBody(body) if err != nil { return pythonRevisionCatalogRow{}, errutil.Wrapf(err, "UNSUPPORTED_ROUTINE_VERSION: Python function %d revision body is invalid", functionID) } if bodyDefinition.ABIContract != abi || bodyDefinition.AdapterVersion != adapter || bodyDefinition.SDKVersion != sdk || bodyDefinition.NullPolicy != nullPolicy || bodyDefinition.ArtifactDigest != artifact || bodyDefinition.EnvironmentDigest != environment { - return pythonRevisionCatalogRow{}, moerr.NewInternalErrorNoCtxf("UNSUPPORTED_ROUTINE_VERSION: Python function %d revision metadata does not match its definition", functionID) + return pythonRevisionCatalogRow{}, udferr.Newf("UNSUPPORTED_ROUTINE_VERSION: Python function %d revision metadata does not match its definition", functionID) } actualFingerprint, err := function.PythonRoutineFingerprint(body) if err != nil { return pythonRevisionCatalogRow{}, errutil.Wrapf(err, "UNSUPPORTED_ROUTINE_VERSION: Python function %d revision fingerprint cannot be computed", functionID) } if fingerprint == "" || fingerprint != actualFingerprint { - return pythonRevisionCatalogRow{}, moerr.NewInternalErrorNoCtxf("UNSUPPORTED_ROUTINE_VERSION: Python function %d revision fingerprint mismatch", functionID) + return pythonRevisionCatalogRow{}, udferr.Newf("UNSUPPORTED_ROUTINE_VERSION: Python function %d revision fingerprint mismatch", functionID) } expectedInput, expectedReturn, expectedSignature, err := function.PythonSignatureMetadata(bodyDefinition.ArgTypes, bodyDefinition.ReturnType) if err != nil { @@ -1434,10 +1436,10 @@ func readPythonRevision(ctx context.Context, bh BackgroundExec, functionID int64 } if signatureKeySchemaVersion != udf.PythonSignatureKeySchemaVersion || canonicalInput != expectedInput || returnDescriptor != expectedReturn || signatureFingerprint != expectedSignature { - return pythonRevisionCatalogRow{}, moerr.NewInternalErrorNoCtxf("UNSUPPORTED_ROUTINE_VERSION: Python function %d identity descriptor metadata mismatch", functionID) + return pythonRevisionCatalogRow{}, udferr.Newf("UNSUPPORTED_ROUTINE_VERSION: Python function %d identity descriptor metadata mismatch", functionID) } if argTypes != expectedInput { - return pythonRevisionCatalogRow{}, moerr.NewInternalErrorNoCtxf("UNSUPPORTED_ROUTINE_VERSION: Python function %d revision argument descriptor mismatch", functionID) + return pythonRevisionCatalogRow{}, udferr.Newf("UNSUPPORTED_ROUTINE_VERSION: Python function %d revision argument descriptor mismatch", functionID) } return pythonRevisionCatalogRow{ Args: args, ArgTypes: argTypes, Body: body, Language: language, RetType: retType, diff --git a/pkg/frontend/routine_error_contract_test.go b/pkg/frontend/routine_error_contract_test.go new file mode 100644 index 0000000000000..50d7f732c5246 --- /dev/null +++ b/pkg/frontend/routine_error_contract_test.go @@ -0,0 +1,41 @@ +// Copyright 2026 Matrix Origin +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package frontend + +import ( + "testing" + + "github.com/matrixorigin/matrixone/pkg/common/moerr" + "github.com/matrixorigin/matrixone/pkg/udf/udferr" + "github.com/matrixorigin/matrixone/pkg/util/errutil" + "github.com/stretchr/testify/require" +) + +func TestRoutineDiagnosticSQLContract(t *testing.T) { + cause := udferr.New("TYPE_CONTRACT: result length mismatch") + for _, err := range []error{cause, errutil.Wrapf(cause, "python udf: worker error")} { + code, state, message := RewriteError(err, "") + require.Equal(t, uint16(moerr.ER_INTERNAL_ERROR), code) + require.Equal(t, "HY000", state) + require.Equal(t, err.Error(), message) + } + + // Existing MO-classified errors retain their own SQL mapping. + classified := moerr.NewInternalErrorNoCtx("classified") + code, state, message := RewriteError(classified, "") + require.Equal(t, classified.ErrorCode(), code) + require.Equal(t, classified.SqlState(), state) + require.Equal(t, "internal error: classified", message) +} diff --git a/pkg/frontend/routine_namespace.go b/pkg/frontend/routine_namespace.go index 1c9e6d4856986..6d89b74bc617c 100644 --- a/pkg/frontend/routine_namespace.go +++ b/pkg/frontend/routine_namespace.go @@ -20,16 +20,17 @@ import ( "encoding/hex" "encoding/json" "fmt" - "github.com/matrixorigin/matrixone/pkg/common/moerr" - "github.com/matrixorigin/matrixone/pkg/util/errutil" "sort" "strconv" "strings" + "github.com/matrixorigin/matrixone/pkg/udf/udferr" + "github.com/matrixorigin/matrixone/pkg/util/errutil" + planpb "github.com/matrixorigin/matrixone/pkg/pb/plan" ) -var errRoutineNamespaceBudget = moerr.NewInternalErrorNoCtx("routine namespace cache budget exceeded") +var errRoutineNamespaceBudget = udferr.New("routine namespace cache budget exceeded") const maxRoutineNamespaceRows = 65536 const maxRoutineNamespaceBytes = 64 << 20 @@ -121,7 +122,7 @@ func decodeRoutineNamespaces(ctx context.Context, rows ExecResult) (map[uint64]s groups[numbers[0]] = make(map[uint64]string) } if _, duplicate := groups[numbers[0]][numbers[1]]; duplicate { - return nil, moerr.NewInternalErrorNoCtxf("UNSUPPORTED_ROUTINE_VERSION: duplicate routine namespace member") + return nil, udferr.Newf("UNSUPPORTED_ROUTINE_VERSION: duplicate routine namespace member") } groups[numbers[0]][numbers[1]] = hex.EncodeToString(digest[:]) } diff --git a/pkg/frontend/routine_plan_validation.go b/pkg/frontend/routine_plan_validation.go index 98dcfa9429b97..7329e417a6a29 100644 --- a/pkg/frontend/routine_plan_validation.go +++ b/pkg/frontend/routine_plan_validation.go @@ -18,12 +18,13 @@ import ( "context" "errors" "fmt" - "github.com/matrixorigin/matrixone/pkg/common/moerr" - "github.com/matrixorigin/matrixone/pkg/util/errutil" "sort" "strconv" "strings" + "github.com/matrixorigin/matrixone/pkg/udf/udferr" + "github.com/matrixorigin/matrixone/pkg/util/errutil" + "github.com/matrixorigin/matrixone/pkg/container/types" "github.com/matrixorigin/matrixone/pkg/defines" planpb "github.com/matrixorigin/matrixone/pkg/pb/plan" @@ -116,7 +117,7 @@ func routinePlanDependencies(p *planpb.Plan) ([]*planpb.RoutinePlanDependency, e if frame.next == 0 { frameDependencies := frame.query.GetRoutineDependencies() if len(dependencies)+len(frameDependencies) > maxRoutinePlanDependencies { - return nil, moerr.NewInternalErrorNoCtxf("PROGRAM_LIMIT_EXCEEDED: routine dependency count exceeds %d", maxRoutinePlanDependencies) + return nil, udferr.Newf("PROGRAM_LIMIT_EXCEEDED: routine dependency count exceeds %d", maxRoutinePlanDependencies) } for _, dependency := range frameDependencies { dependencySize := 1 @@ -124,7 +125,7 @@ func routinePlanDependencies(p *planpb.Plan) ([]*planpb.RoutinePlanDependency, e dependencySize = dependency.ProtoSize() } if dependencySize > maxRoutinePlanDependencyBytes-dependencyBytes { - return nil, moerr.NewInternalErrorNoCtxf("PROGRAM_LIMIT_EXCEEDED: routine dependency bytes exceed %d", maxRoutinePlanDependencyBytes) + return nil, udferr.Newf("PROGRAM_LIMIT_EXCEEDED: routine dependency bytes exceed %d", maxRoutinePlanDependencyBytes) } dependencyBytes += dependencySize } @@ -143,10 +144,10 @@ func routinePlanDependencies(p *planpb.Plan) ([]*planpb.RoutinePlanDependency, e continue } if frame.depth+1 > maxRoutinePlanQueryDepth { - return nil, moerr.NewInternalErrorNoCtxf("PROGRAM_LIMIT_EXCEEDED: routine dependency query nesting exceeds %d", maxRoutinePlanQueryDepth) + return nil, udferr.Newf("PROGRAM_LIMIT_EXCEEDED: routine dependency query nesting exceeds %d", maxRoutinePlanQueryDepth) } if _, exists := active[background]; exists { - return nil, moerr.NewInternalErrorNoCtxf("UNSUPPORTED_ROUTINE_VERSION: routine dependency query graph contains a cycle") + return nil, udferr.Newf("UNSUPPORTED_ROUTINE_VERSION: routine dependency query graph contains a cycle") } // Shared background queries contribute their immutable dependencies // once. Re-expanding a DAG as a tree can turn a small plan into @@ -173,10 +174,10 @@ func validateRoutinePlanDependencies(ctx context.Context, ses FeSession, p *plan return false, nil } if len(dependencies) > maxRoutinePlanDependencies { - return false, moerr.NewInternalErrorNoCtxf("PROGRAM_LIMIT_EXCEEDED: routine dependency count exceeds %d", maxRoutinePlanDependencies) + return false, udferr.Newf("PROGRAM_LIMIT_EXCEEDED: routine dependency count exceeds %d", maxRoutinePlanDependencies) } if ses == nil { - return false, moerr.NewInternalErrorNoCtxf("UNSUPPORTED_ROUTINE_VERSION: routine dependency validation has no session") + return false, udferr.Newf("UNSUPPORTED_ROUTINE_VERSION: routine dependency validation has no session") } var snapshot *planpb.Snapshot if tcc := ses.GetTxnCompileCtx(); tcc != nil { @@ -350,13 +351,13 @@ func validateRoutinePlanDependencies(ctx context.Context, ses FeSession, p *plan return false, getErr } if !strings.EqualFold(revisionSecurityType, baseSecurityType) { - return false, moerr.NewInternalErrorNoCtxf("UNSUPPORTED_ROUTINE_VERSION: routine %d has inconsistent revision and identity security contracts", id) + return false, udferr.Newf("UNSUPPORTED_ROUTINE_VERSION: routine %d has inconsistent revision and identity security contracts", id) } if language == udf.LanguagePython && !strings.EqualFold(revisionSecurityType, "INVOKER") { - return false, moerr.NewInternalErrorNoCtxf("UNSUPPORTED_ROUTINE_VERSION: Python routine catalog security type is not INVOKER") + return false, udferr.Newf("UNSUPPORTED_ROUTINE_VERSION: Python routine catalog security type is not INVOKER") } if language == udf.LanguageSQL && !strings.EqualFold(revisionSecurityType, "DEFINER") { - return false, moerr.NewInternalErrorNoCtxf("UNSUPPORTED_ROUTINE_VERSION: SQL routine catalog security type is not DEFINER") + return false, udferr.Newf("UNSUPPORTED_ROUTINE_VERSION: SQL routine catalog security type is not DEFINER") } identityValid := routinePlanCatalogIdentityValidForLanguage( language, @@ -366,7 +367,7 @@ func validateRoutinePlanDependencies(ctx context.Context, ses FeSession, p *plan revisionSDK, revisionNullPolicy, ) if _, duplicate := states[id]; duplicate { - return false, moerr.NewInternalErrorNoCtxf("UNSUPPORTED_ROUTINE_VERSION: routine catalog returned duplicate state for function %d", id) + return false, udferr.Newf("UNSUPPORTED_ROUTINE_VERSION: routine catalog returned duplicate state for function %d", id) } states[id] = routinePlanCatalogState{ activeRevision: activeRevision, namespaceVersion: namespaceVersion, @@ -492,37 +493,37 @@ func routineRevisionReturnTypeMatches(rettype string, oid types.T) bool { func validateRoutinePlanDependencyShape(dependency *planpb.RoutinePlanDependency, accountID uint32) error { if dependency == nil || dependency.FunctionRef == nil { - return moerr.NewInternalErrorNoCtxf("UNSUPPORTED_ROUTINE_VERSION: routine plan dependency has no FunctionRef") + return udferr.Newf("UNSUPPORTED_ROUTINE_VERSION: routine plan dependency has no FunctionRef") } ref := dependency.FunctionRef if ref.FunctionId == 0 || ref.Revision == 0 || ref.NamespaceVersion == 0 || ref.AccountId != uint64(accountID) || ref.DatabaseId == 0 { - return moerr.NewInternalErrorNoCtxf("UNSUPPORTED_ROUTINE_VERSION: routine plan dependency has an invalid catalog identity") + return udferr.Newf("UNSUPPORTED_ROUTINE_VERSION: routine plan dependency has an invalid catalog identity") } if dependency.ContractVersion != udf.RoutinePlanContractVersion || !udf.IsSHA256Digest(dependency.DefinitionFingerprint) || !udf.IsSHA256Digest(dependency.NamespaceFingerprint) { - return moerr.NewInternalErrorNoCtxf("UNSUPPORTED_ROUTINE_VERSION: routine plan dependency contract is incomplete") + return udferr.Newf("UNSUPPORTED_ROUTINE_VERSION: routine plan dependency contract is incomplete") } if dependency.Volatility == "" || dependency.NullPolicy == "" || !dependency.MayError || dependency.Leakproof { - return moerr.NewInternalErrorNoCtxf("UNSUPPORTED_ROUTINE_VERSION: routine plan dependency semantic contract is incomplete") + return udferr.Newf("UNSUPPORTED_ROUTINE_VERSION: routine plan dependency semantic contract is incomplete") } switch dependency.Language { case udf.LanguagePython: if dependency.SecurityMode != "INVOKER" { - return moerr.NewInternalErrorNoCtxf("UNSUPPORTED_ROUTINE_VERSION: Python routine plan dependency must use INVOKER security") + return udferr.Newf("UNSUPPORTED_ROUTINE_VERSION: Python routine plan dependency must use INVOKER security") } if !udf.IsSHA256Digest(dependency.ArtifactDigest) || !udf.IsSHA256Digest(dependency.EnvironmentDigest) { - return moerr.NewInternalErrorNoCtxf("UNSUPPORTED_ROUTINE_VERSION: Python routine plan dependency integrity is incomplete") + return udferr.Newf("UNSUPPORTED_ROUTINE_VERSION: Python routine plan dependency integrity is incomplete") } case udf.LanguageSQL: if dependency.SecurityMode == "" { - return moerr.NewInternalErrorNoCtxf("UNSUPPORTED_ROUTINE_VERSION: SQL routine plan dependency has no security mode") + return udferr.Newf("UNSUPPORTED_ROUTINE_VERSION: SQL routine plan dependency has no security mode") } if dependency.ArtifactDigest != "" || dependency.EnvironmentDigest != "" { - return moerr.NewInternalErrorNoCtxf("UNSUPPORTED_ROUTINE_VERSION: SQL routine plan dependency has external integrity fields") + return udferr.Newf("UNSUPPORTED_ROUTINE_VERSION: SQL routine plan dependency has external integrity fields") } default: - return moerr.NewInternalErrorNoCtxf("UNSUPPORTED_ROUTINE_VERSION: routine plan dependency language %q is unsupported", dependency.Language) + return udferr.Newf("UNSUPPORTED_ROUTINE_VERSION: routine plan dependency language %q is unsupported", dependency.Language) } return nil } @@ -556,7 +557,7 @@ func routinePlanDependenciesChanged( !strings.EqualFold(state.securityType, dependency.SecurityMode) || (dependency.Language == udf.LanguagePython && expectedSecurityMode != "INVOKER") || (dependency.Language == udf.LanguageSQL && expectedSecurityMode == "") || - dependency.MayError != true || dependency.Leakproof || + !dependency.MayError || dependency.Leakproof || state.language != dependency.Language || state.fingerprint != dependency.DefinitionFingerprint || state.artifactDigest != dependency.ArtifactDigest || diff --git a/pkg/frontend/snapshot_catalog_restore.go b/pkg/frontend/snapshot_catalog_restore.go index ac7bd70e1e674..32b58d50db84f 100644 --- a/pkg/frontend/snapshot_catalog_restore.go +++ b/pkg/frontend/snapshot_catalog_restore.go @@ -18,10 +18,12 @@ import ( "context" "encoding/json" "fmt" - "github.com/matrixorigin/matrixone/pkg/util/errutil" "strconv" "strings" + "github.com/matrixorigin/matrixone/pkg/udf/udferr" + "github.com/matrixorigin/matrixone/pkg/util/errutil" + "github.com/matrixorigin/matrixone/pkg/catalog" "github.com/matrixorigin/matrixone/pkg/common/moerr" "github.com/matrixorigin/matrixone/pkg/common/pubsub" @@ -190,23 +192,23 @@ func validateRestoredFunctionRevisionCatalog( } for rowIndex, row := range rows { if len(row) != 20 { - return moerr.NewInternalErrorNoCtxf("UNSUPPORTED_ROUTINE_VERSION: restored Python revision row %d has %d columns", rowIndex, len(row)) + return udferr.Newf("UNSUPPORTED_ROUTINE_VERSION: restored Python revision row %d has %d columns", rowIndex, len(row)) } functionID, err := strconv.ParseUint(row[0], 10, 64) if err != nil || functionID == 0 { - return moerr.NewInternalErrorNoCtxf("UNSUPPORTED_ROUTINE_VERSION: restored Python revision row %d has an invalid function identity", rowIndex) + return udferr.Newf("UNSUPPORTED_ROUTINE_VERSION: restored Python revision row %d has an invalid function identity", rowIndex) } revision, err := strconv.ParseUint(row[1], 10, 64) if err != nil || revision == 0 { - return moerr.NewInternalErrorNoCtxf("UNSUPPORTED_ROUTINE_VERSION: restored Python function %d has an invalid revision", functionID) + return udferr.Newf("UNSUPPORTED_ROUTINE_VERSION: restored Python function %d has an invalid revision", functionID) } namespace, err := strconv.ParseUint(row[2], 10, 64) if err != nil || namespace == 0 { - return moerr.NewInternalErrorNoCtxf("UNSUPPORTED_ROUTINE_VERSION: restored Python function %d has an invalid namespace version", functionID) + return udferr.Newf("UNSUPPORTED_ROUTINE_VERSION: restored Python function %d has an invalid namespace version", functionID) } definitionSchema, err := strconv.Atoi(row[9]) if err != nil { - return moerr.NewInternalErrorNoCtxf("UNSUPPORTED_ROUTINE_VERSION: restored function %d revision %d has an invalid definition schema", functionID, revision) + return udferr.Newf("UNSUPPORTED_ROUTINE_VERSION: restored function %d revision %d has an invalid definition schema", functionID, revision) } if row[8] == udf.LanguageSQL { if err := validateRestoredSQLFunctionRevision(functionID, revision, row, definitionSchema); err != nil { @@ -221,7 +223,7 @@ func validateRestoredFunctionRevisionCatalog( row[14] != udf.PythonSDKVersion || !strings.EqualFold(row[16], "VOLATILE") || !strings.EqualFold(row[19], "INVOKER") { - return moerr.NewInternalErrorNoCtxf("UNSUPPORTED_ROUTINE_VERSION: restored function %d revision %d has an unsupported execution contract", functionID, revision) + return udferr.Newf("UNSUPPORTED_ROUTINE_VERSION: restored function %d revision %d has an unsupported execution contract", functionID, revision) } body, err := function.DecodePythonRoutineBody(row[7]) @@ -235,11 +237,11 @@ func validateRestoredFunctionRevisionCatalog( body.EnvironmentDigest != row[13] || body.SDKVersion != row[14] || body.NullPolicy != row[15] { - return moerr.NewInternalErrorNoCtxf("UNSUPPORTED_ROUTINE_VERSION: restored Python function %d revision %d metadata does not match its definition", functionID, revision) + return udferr.Newf("UNSUPPORTED_ROUTINE_VERSION: restored Python function %d revision %d metadata does not match its definition", functionID, revision) } fingerprint, err := function.PythonRoutineFingerprint(row[7]) if err != nil || row[17] == "" || row[17] != fingerprint { - return moerr.NewInternalErrorNoCtxf("UNSUPPORTED_ROUTINE_VERSION: restored Python function %d revision %d fingerprint mismatch", functionID, revision) + return udferr.Newf("UNSUPPORTED_ROUTINE_VERSION: restored Python function %d revision %d fingerprint mismatch", functionID, revision) } // `args` retains the shared logical names while `arg_types` in the // current Python revision is the exact descriptor identity used for @@ -268,7 +270,7 @@ func validateRestoredFunctionRevisionCatalog( } expectedInput, _, _, err := function.PythonSignatureMetadata(body.ArgTypes, body.ReturnType) if err != nil || row[5] != expectedInput { - return moerr.NewInternalErrorNoCtxf("UNSUPPORTED_ROUTINE_VERSION: restored Python function %d revision %d argument descriptor mismatch", functionID, revision) + return udferr.Newf("UNSUPPORTED_ROUTINE_VERSION: restored Python function %d revision %d argument descriptor mismatch", functionID, revision) } } return nil @@ -279,15 +281,15 @@ func validateRestoredSQLFunctionRevision(functionID, revision uint64, row []stri row[10] != "" || row[11] != "" || row[12] != "" || row[13] != "" || row[14] != "" || row[15] != udf.NullCallHandler || !strings.EqualFold(row[16], "VOLATILE") || !strings.EqualFold(row[19], "DEFINER") { - return moerr.NewInternalErrorNoCtxf("UNSUPPORTED_ROUTINE_VERSION: restored SQL function %d revision %d has an unsupported execution contract", functionID, revision) + return udferr.Newf("UNSUPPORTED_ROUTINE_VERSION: restored SQL function %d revision %d has an unsupported execution contract", functionID, revision) } logicalArgTypes, err := userDefinedFunctionArgumentTypesFromJSON(row[4]) if err != nil || logicalArgTypes != row[5] { - return moerr.NewInternalErrorNoCtxf("UNSUPPORTED_ROUTINE_VERSION: restored SQL function %d revision %d argument metadata is invalid", functionID, revision) + return udferr.Newf("UNSUPPORTED_ROUTINE_VERSION: restored SQL function %d revision %d argument metadata is invalid", functionID, revision) } fingerprint, err := function.SQLRoutineFingerprint(row[7], row[5], row[6]) if err != nil || row[17] == "" || row[17] != fingerprint { - return moerr.NewInternalErrorNoCtxf("UNSUPPORTED_ROUTINE_VERSION: restored SQL function %d revision %d fingerprint mismatch", functionID, revision) + return udferr.Newf("UNSUPPORTED_ROUTINE_VERSION: restored SQL function %d revision %d fingerprint mismatch", functionID, revision) } return nil } @@ -320,19 +322,19 @@ func validateRestoredFunctionCatalogHeads( } for rowIndex, row := range rows { if len(row) != 16 { - return moerr.NewInternalErrorNoCtxf("UNSUPPORTED_ROUTINE_VERSION: restored routine catalog head row %d has %d columns", rowIndex, len(row)) + return udferr.Newf("UNSUPPORTED_ROUTINE_VERSION: restored routine catalog head row %d has %d columns", rowIndex, len(row)) } functionID, err := strconv.ParseUint(row[0], 10, 64) if err != nil || functionID == 0 { - return moerr.NewInternalErrorNoCtxf("UNSUPPORTED_ROUTINE_VERSION: restored routine catalog head row %d has an invalid function identity", rowIndex) + return udferr.Newf("UNSUPPORTED_ROUTINE_VERSION: restored routine catalog head row %d has an invalid function identity", rowIndex) } activeRevision, err := strconv.ParseUint(row[1], 10, 64) if err != nil { - return moerr.NewInternalErrorNoCtxf("UNSUPPORTED_ROUTINE_VERSION: restored routine function %d has an invalid active revision: %v", functionID, err) + return udferr.Newf("UNSUPPORTED_ROUTINE_VERSION: restored routine function %d has an invalid active revision: %v", functionID, err) } namespaceVersion, err := strconv.ParseUint(row[2], 10, 64) if err != nil { - return moerr.NewInternalErrorNoCtxf("UNSUPPORTED_ROUTINE_VERSION: restored routine function %d has an invalid namespace version: %v", functionID, err) + return udferr.Newf("UNSUPPORTED_ROUTINE_VERSION: restored routine function %d has an invalid namespace version: %v", functionID, err) } language := strings.ToLower(row[12]) if (language == udf.LanguageSQL || language == udf.LanguagePython) && activeRevision == 0 && namespaceVersion == 0 { @@ -344,39 +346,39 @@ func validateRestoredFunctionCatalogHeads( continue } if activeRevision == 0 { - return moerr.NewInternalErrorNoCtxf("UNSUPPORTED_ROUTINE_VERSION: restored %s function %d has no active immutable revision", language, functionID) + return udferr.Newf("UNSUPPORTED_ROUTINE_VERSION: restored %s function %d has no active immutable revision", language, functionID) } if namespaceVersion == 0 { - return moerr.NewInternalErrorNoCtxf("UNSUPPORTED_ROUTINE_VERSION: restored %s function %d has an invalid namespace version", language, functionID) + return udferr.Newf("UNSUPPORTED_ROUTINE_VERSION: restored %s function %d has an invalid namespace version", language, functionID) } revision, err := strconv.ParseUint(row[3], 10, 64) if err != nil || revision != activeRevision { - return moerr.NewInternalErrorNoCtxf("UNSUPPORTED_ROUTINE_VERSION: restored %s function %d active revision does not exist", language, functionID) + return udferr.Newf("UNSUPPORTED_ROUTINE_VERSION: restored %s function %d active revision does not exist", language, functionID) } revisionNamespace, err := strconv.ParseUint(row[4], 10, 64) if err != nil || revisionNamespace != namespaceVersion { - return moerr.NewInternalErrorNoCtxf("UNSUPPORTED_ROUTINE_VERSION: restored %s function %d head namespace does not match its revision", language, functionID) + return udferr.Newf("UNSUPPORTED_ROUTINE_VERSION: restored %s function %d head namespace does not match its revision", language, functionID) } if language == udf.LanguageSQL { if row[10] == "" || row[11] == "" { - return moerr.NewInternalErrorNoCtxf("UNSUPPORTED_ROUTINE_VERSION: restored SQL function %d has incomplete active revision", functionID) + return udferr.Newf("UNSUPPORTED_ROUTINE_VERSION: restored SQL function %d has incomplete active revision", functionID) } if !strings.EqualFold(row[14], "DEFINER") || !strings.EqualFold(row[15], "DEFINER") { - return moerr.NewInternalErrorNoCtxf("UNSUPPORTED_ROUTINE_VERSION: restored SQL function %d has inconsistent security contract", functionID) + return udferr.Newf("UNSUPPORTED_ROUTINE_VERSION: restored SQL function %d has inconsistent security contract", functionID) } expectedFingerprint, fingerprintErr := function.SQLRoutineFingerprint(row[11], row[9], row[10]) if fingerprintErr != nil || row[13] == "" || row[13] != expectedFingerprint { - return moerr.NewInternalErrorNoCtxf("UNSUPPORTED_ROUTINE_VERSION: restored SQL function %d head fingerprint does not match its active revision", functionID) + return udferr.Newf("UNSUPPORTED_ROUTINE_VERSION: restored SQL function %d head fingerprint does not match its active revision", functionID) } continue } if language != udf.LanguagePython { - return moerr.NewInternalErrorNoCtxf("UNSUPPORTED_ROUTINE_VERSION: restored function %d has unsupported language %q", functionID, row[12]) + return udferr.Newf("UNSUPPORTED_ROUTINE_VERSION: restored function %d has unsupported language %q", functionID, row[12]) } if row[5] == "" || row[6] == "" || row[7] == "" || row[8] == "" || row[9] == "" || row[10] == "" || row[11] == "" || row[13] == "" || !strings.EqualFold(row[14], "INVOKER") || !strings.EqualFold(row[15], "INVOKER") || !strings.EqualFold(row[14], row[15]) { - return moerr.NewInternalErrorNoCtxf("UNSUPPORTED_ROUTINE_VERSION: restored Python function %d has incomplete head identity", functionID) + return udferr.Newf("UNSUPPORTED_ROUTINE_VERSION: restored Python function %d has incomplete head identity", functionID) } body, err := function.DecodePythonRoutineBody(row[11]) if err != nil { @@ -389,7 +391,7 @@ func validateRestoredFunctionCatalogHeads( bodyFingerprint, fingerprintErr := function.PythonRoutineFingerprint(row[11]) if fingerprintErr != nil || row[13] != bodyFingerprint || row[5] != input || row[6] != output || row[7] != strconv.Itoa(udf.PythonSignatureKeySchemaVersion) || row[8] != signature || row[9] != input { - return moerr.NewInternalErrorNoCtxf("UNSUPPORTED_ROUTINE_VERSION: restored Python function %d head identity does not match its active revision", functionID) + return udferr.Newf("UNSUPPORTED_ROUTINE_VERSION: restored Python function %d head identity does not match its active revision", functionID) } } return nil @@ -519,7 +521,7 @@ func publishRestoredPythonArtifacts( needStore := false for rowIndex, row := range rows { if len(row) != 3 { - return moerr.NewInternalErrorNoCtxf("UNSUPPORTED_ROUTINE_VERSION: restored revision row %d has %d artifact columns", rowIndex, len(row)) + return udferr.Newf("UNSUPPORTED_ROUTINE_VERSION: restored revision row %d has %d artifact columns", rowIndex, len(row)) } if row[0] == udf.LanguagePython { needStore = true @@ -546,7 +548,7 @@ func publishRestoredPythonArtifacts( return errutil.Wrapf(decodeErr, "UNSUPPORTED_ROUTINE_VERSION: restored Python artifact row %d is invalid", rowIndex) } if body.ArtifactDigest != row[1] { - return moerr.NewInternalErrorNoCtxf("UNSUPPORTED_ROUTINE_VERSION: restored Python artifact row %d digest does not match its revision", rowIndex) + return udferr.Newf("UNSUPPORTED_ROUTINE_VERSION: restored Python artifact row %d digest does not match its revision", rowIndex) } if _, publishErr := store.Publish(ctx, uint64(targetAccount), body.Handler, body.Source); publishErr != nil { return publishErr diff --git a/pkg/iceberg/api/import_boundary_test.go b/pkg/iceberg/api/import_boundary_test.go index 1b4faad0e293c..fde304fd1db79 100644 --- a/pkg/iceberg/api/import_boundary_test.go +++ b/pkg/iceberg/api/import_boundary_test.go @@ -166,7 +166,10 @@ func TestImportBoundaryPolicies(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { matches := importMatchesPrefix(tt.importPath, tt.policy.importPrefixes) - if matches && tt.policy.allows(tt.relativePath) != tt.allowed { + if !matches { + t.Fatal("expected policy to match the protected module") + } + if tt.policy.allows(tt.relativePath) != tt.allowed { t.Fatalf("policy allowed=%v for %s importing %s, want %v", tt.policy.allows(tt.relativePath), tt.relativePath, tt.importPath, tt.allowed) } @@ -224,3 +227,25 @@ func findRepoRoot(t *testing.T) string { dir = parent } } + +func TestImportBoundaryModuleNames(t *testing.T) { + for _, tc := range []struct { + path string + want bool + }{ + {"github.com/apache/arrow-go/v18/arrow", true}, + {"github.com/apache/arrow/go/v12/arrow", true}, + {"github.com/apache/arrow-gopher/arrow", false}, + {"github.com/apache/arrow/golang", false}, + {"example.com/arrow", false}, + } { + if got := importMatchesPrefix(tc.path, importBoundaryPolicies[1].importPrefixes); got != tc.want { + t.Errorf("%s: got %v, want %v", tc.path, got, tc.want) + } + } + for _, path := range []string{"pkg/container/arrowbridge/reader.go", "pkg/sql/plan/binder.go"} { + if importBoundaryPolicies[0].allows(path) { + t.Errorf("Iceberg unexpectedly allowed in %s", path) + } + } +} diff --git a/pkg/sql/colexec/external_routine_eval.go b/pkg/sql/colexec/external_routine_eval.go index 993da7c51cdc9..242dd13eb38d7 100644 --- a/pkg/sql/colexec/external_routine_eval.go +++ b/pkg/sql/colexec/external_routine_eval.go @@ -16,8 +16,6 @@ package colexec import ( "encoding/json" - "github.com/matrixorigin/matrixone/pkg/common/moerr" - "github.com/matrixorigin/matrixone/pkg/util/errutil" "os" "sort" "strconv" @@ -25,6 +23,9 @@ import ( "sync" "time" + "github.com/matrixorigin/matrixone/pkg/udf/udferr" + "github.com/matrixorigin/matrixone/pkg/util/errutil" + "github.com/google/uuid" "github.com/matrixorigin/matrixone/pkg/common/mpool" "github.com/matrixorigin/matrixone/pkg/container/batch" @@ -73,13 +74,13 @@ func newExternalRoutineEval( allocation *vector.AllocationAccountSelection, ) (*ExternalRoutineEval, error) { if proc == nil || proc.Base == nil { - return nil, moerr.NewInternalErrorNoCtxf("python udf: evaluator has no process") + return nil, udferr.Newf("python udf: evaluator has no process") } if err := validateRoutineCall(call); err != nil { return nil, err } if len(call.ArgumentTypes) != len(parameters) { - return nil, moerr.NewInternalErrorNoCtxf("python udf: typed routine has %d argument descriptors but %d bound arguments", len(call.ArgumentTypes), len(parameters)) + return nil, udferr.Newf("python udf: typed routine has %d argument descriptors but %d bound arguments", len(call.ArgumentTypes), len(parameters)) } resultType := planTypeToSQL(call.ReturnType) result, err := vector.NewFunctionResultWrapperWithAllocation(resultType, proc.Mp(), allocation) @@ -100,59 +101,59 @@ func newExternalRoutineEval( func validateRoutineCall(call *planpb.RoutineCall) error { if call == nil || call.FunctionRef == nil || call.FunctionRef.FunctionId == 0 || call.FunctionRef.DatabaseId == 0 || call.FunctionRef.Revision == 0 || call.FunctionRef.NamespaceVersion == 0 { - return moerr.NewInternalErrorNoCtxf("UNSUPPORTED_ROUTINE_VERSION: typed routine call has no exact FunctionRef") + return udferr.Newf("UNSUPPORTED_ROUTINE_VERSION: typed routine call has no exact FunctionRef") } if call.ReturnType.Id == int32(types.T_any) { - return moerr.NewInternalErrorNoCtxf("UNSUPPORTED_ROUTINE_VERSION: typed routine call has no return descriptor") + return udferr.Newf("UNSUPPORTED_ROUTINE_VERSION: typed routine call has no return descriptor") } if call.ContractVersion != udf.PythonPlanContractVersion || call.Language != udf.LanguagePython { - return moerr.NewInternalErrorNoCtxf("UNSUPPORTED_ROUTINE_VERSION: unsupported typed routine call contract") + return udferr.Newf("UNSUPPORTED_ROUTINE_VERSION: unsupported typed routine call contract") } if call.Volatility != "VOLATILE" { - return moerr.NewInternalErrorNoCtxf("UNSUPPORTED_ROUTINE_VERSION: Python routine must be VOLATILE") + return udferr.Newf("UNSUPPORTED_ROUTINE_VERSION: Python routine must be VOLATILE") } - if call.MayError != true || call.SecurityMode != "INVOKER" || call.Leakproof { - return moerr.NewInternalErrorNoCtxf("UNSUPPORTED_ROUTINE_VERSION: Python routine semantic contract is not supported") + if !call.MayError || call.SecurityMode != "INVOKER" || call.Leakproof { + return udferr.Newf("UNSUPPORTED_ROUTINE_VERSION: Python routine semantic contract is not supported") } if call.CallsiteId == "" || len(call.CallsiteId) > 256 || strings.ContainsAny(call.CallsiteId, "\r\n") { - return moerr.NewInternalErrorNoCtxf("UNSUPPORTED_ROUTINE_VERSION: typed routine call has no valid callsite id") + return udferr.Newf("UNSUPPORTED_ROUTINE_VERSION: typed routine call has no valid callsite id") } if len(call.Context) != 0 { - return moerr.NewInternalErrorNoCtxf("UNSUPPORTED_ROUTINE_VERSION: typed routine call contains execution context") + return udferr.Newf("UNSUPPORTED_ROUTINE_VERSION: typed routine call contains execution context") } python := call.GetPython() if python == nil { - return moerr.NewInternalErrorNoCtxf("UNSUPPORTED_ROUTINE_VERSION: typed routine call has no Python implementation") + return udferr.Newf("UNSUPPORTED_ROUTINE_VERSION: typed routine call has no Python implementation") } if python.Source != "" { - return moerr.NewInternalErrorNoCtxf("UNSUPPORTED_ROUTINE_VERSION: typed Python plan contains source; rebind from the current artifact contract") + return udferr.Newf("UNSUPPORTED_ROUTINE_VERSION: typed Python plan contains source; rebind from the current artifact contract") } if python.Handler == "" || python.AbiContract != udf.PythonABIContract || python.AdapterVersion != udf.PythonAdapterVersion || python.SdkVersion != udf.PythonSDKVersion || python.DefinitionSchemaVersion != udf.PythonDefinitionSchemaVersion { - return moerr.NewInternalErrorNoCtxf("UNSUPPORTED_ROUTINE_VERSION: typed Python implementation contract is not supported") + return udferr.Newf("UNSUPPORTED_ROUTINE_VERSION: typed Python implementation contract is not supported") } if python.Mode != "SCALAR" && python.Mode != "VECTOR" { - return moerr.NewInternalErrorNoCtxf("python udf: unsupported typed call mode %q", python.Mode) + return udferr.Newf("python udf: unsupported typed call mode %q", python.Mode) } if python.NullPolicy != udf.NullCallHandler && python.NullPolicy != udf.NullReturnNull { - return moerr.NewInternalErrorNoCtxf("python udf: unsupported typed NULL policy %q", python.NullPolicy) + return udferr.Newf("python udf: unsupported typed NULL policy %q", python.NullPolicy) } if call.NullPolicy != python.NullPolicy { - return moerr.NewInternalErrorNoCtxf("UNSUPPORTED_ROUTINE_VERSION: typed call NULL policy does not match its implementation") + return udferr.Newf("UNSUPPORTED_ROUTINE_VERSION: typed call NULL policy does not match its implementation") } if !udf.IsSHA256Digest(python.ArtifactDigest) || !udf.IsSHA256Digest(python.EnvironmentDigest) { - return moerr.NewInternalErrorNoCtxf("UNSUPPORTED_ROUTINE_VERSION: typed Python implementation has invalid artifact/environment digest") + return udferr.Newf("UNSUPPORTED_ROUTINE_VERSION: typed Python implementation has invalid artifact/environment digest") } if !udf.IsSHA256Digest(python.DefinitionFingerprint) { - return moerr.NewInternalErrorNoCtxf("UNSUPPORTED_ROUTINE_VERSION: typed Python implementation has invalid definition fingerprint") + return udferr.Newf("UNSUPPORTED_ROUTINE_VERSION: typed Python implementation has invalid definition fingerprint") } arguments := make([]pythonudf.TypeDescriptor, len(call.ArgumentTypes)) for i, argumentType := range call.ArgumentTypes { if argumentType == nil { - return moerr.NewInternalErrorNoCtxf("UNSUPPORTED_ROUTINE_VERSION: typed Python argument %d is nil", i) + return udferr.Newf("UNSUPPORTED_ROUTINE_VERSION: typed Python argument %d is nil", i) } descriptor, err := function.NewPythonTypeDescriptor(planTypeToSQL(*argumentType)) if err != nil { @@ -173,7 +174,7 @@ func validateRoutineCall(call *planpb.RoutineCall) error { return errutil.Wrapf(err, "UNSUPPORTED_ROUTINE_VERSION: invalid typed Python definition") } if fingerprint != python.DefinitionFingerprint { - return moerr.NewInternalErrorNoCtxf("UNSUPPORTED_ROUTINE_VERSION: typed Python definition fingerprint mismatch") + return udferr.Newf("UNSUPPORTED_ROUTINE_VERSION: typed Python definition fingerprint mismatch") } return nil @@ -185,12 +186,12 @@ func (e *ExternalRoutineEval) Eval(proc *process.Process, batches []*batch.Batch func (e *ExternalRoutineEval) eval(proc *process.Process, batches []*batch.Batch, selectList []bool, transfer bool) (output *vector.Vector, evalErr error) { if e == nil || proc == nil || proc.Base == nil { - return nil, moerr.NewInternalErrorNoCtxf("python udf: evaluator has no process") + return nil, udferr.Newf("python udf: evaluator has no process") } e.executionMu.Lock() defer e.executionMu.Unlock() if e.freed { - return nil, moerr.NewInternalErrorNoCtxf("python udf: evaluator is freed") + return nil, udferr.Newf("python udf: evaluator is freed") } // Transfer under executionMu, before a concurrent Eval/Free can reuse the // backing. The wrapper retains its allocation policy for the next result. @@ -208,7 +209,7 @@ func (e *ExternalRoutineEval) eval(proc *process.Process, batches []*batch.Batch return nil, err } if uint64(accountID) != e.call.FunctionRef.AccountId { - return nil, moerr.NewInternalErrorNoCtxf("python udf: FunctionRef account does not match the execution account") + return nil, udferr.Newf("python udf: FunctionRef account does not match the execution account") } python := e.call.GetPython() rowCount, err := externalRoutineRowCount(python.Mode, batches) @@ -219,7 +220,7 @@ func (e *ExternalRoutineEval) eval(proc *process.Process, batches []*batch.Batch // early before this check would let a stale selection mask cross the // physical boundary and hide a relation-position bug in the caller. if selectList != nil && len(selectList) != rowCount { - return nil, moerr.NewInternalErrorNoCtxf("external routine selection has %d rows, expected %d", len(selectList), rowCount) + return nil, udferr.Newf("external routine selection has %d rows, expected %d", len(selectList), rowCount) } if rowCount == 0 { if err := e.result.PreExtendAndReset(0); err != nil { @@ -338,16 +339,16 @@ func (e *ExternalRoutineEval) eval(proc *process.Process, batches []*batch.Batch Tuple: tuple, } if proc.Base.UdfService == nil { - return nil, moerr.NewInternalErrorNoCtxf("RESOURCE_UNAVAILABLE: Python UDF runtime is not enabled") + return nil, udferr.Newf("RESOURCE_UNAVAILABLE: Python UDF runtime is not enabled") } if err := proc.Base.UdfService.Execute(proc.Ctx, invocation, selectedResult, proc.Mp()); err != nil { return nil, err } if selectedResult.ResultLength() != len(e.selectedRows) { - return nil, moerr.NewInternalErrorNoCtxf("python udf: runtime produced %d result rows, expected %d", selectedResult.ResultLength(), len(e.selectedRows)) + return nil, udferr.Newf("python udf: runtime produced %d result rows, expected %d", selectedResult.ResultLength(), len(e.selectedRows)) } if selectedResult.GetResultVector() == nil { - return nil, moerr.NewInternalErrorNoCtxf("python udf: runtime returned no result vector") + return nil, udferr.Newf("python udf: runtime returned no result vector") } if len(e.selectedRows) == rowCount { return e.result.GetResultVector(), nil @@ -364,7 +365,7 @@ func (e *ExternalRoutineEval) eval(proc *process.Process, batches []*batch.Batch // that two calls belong to the same statement. func (e *ExternalRoutineEval) attachInvocationGroup(context map[string]string, queryID string) error { if context == nil { - return moerr.NewInternalErrorNoCtxf("python udf: invocation context is unavailable") + return udferr.Newf("python udf: invocation context is unavailable") } e.groupMu.Lock() defer e.groupMu.Unlock() @@ -387,7 +388,7 @@ func (e *ExternalRoutineEval) attachInvocationGroup(context map[string]string, q e.groupEpoch = 0 } if e.groupEpoch == ^uint64(0) { - return moerr.NewInternalErrorNoCtxf("RESOURCE_EXHAUSTED: Python UDF execution group epoch overflow") + return udferr.Newf("RESOURCE_EXHAUSTED: Python UDF execution group epoch overflow") } e.groupEpoch++ context["group_id"] = e.groupID @@ -409,7 +410,7 @@ func (e *ExternalRoutineEval) compactParameters(proc *process.Process, rowCount e.selectedParameters = e.selectedParameters[:0] for i, input := range e.parameterResults { if input == nil { - return nil, moerr.NewInternalErrorNoCtxf("external routine parameter %d is nil", i) + return nil, udferr.Newf("external routine parameter %d is nil", i) } if input.IsConst() || len(e.selectedRows) == rowCount { e.selectedParameters = append(e.selectedParameters, input) @@ -432,14 +433,14 @@ func (e *ExternalRoutineEval) compactParameters(proc *process.Process, rowCount func validateExternalRoutineInputs(inputs []*vector.Vector, call *planpb.RoutineCall, rowCount int) error { if len(inputs) != len(call.ArgumentTypes) { - return moerr.NewInternalErrorNoCtxf("python udf: evaluated input count %d does not match routine argument count %d", len(inputs), len(call.ArgumentTypes)) + return udferr.Newf("python udf: evaluated input count %d does not match routine argument count %d", len(inputs), len(call.ArgumentTypes)) } for index, input := range inputs { if input == nil || input.GetType() == nil { - return moerr.NewInternalErrorNoCtxf("python udf: evaluated input vector %d has no type", index) + return udferr.Newf("python udf: evaluated input vector %d has no type", index) } if input.Length() == 0 || (!input.IsConst() && input.Length() < rowCount) { - return moerr.NewInternalErrorNoCtxf("python udf: evaluated input vector %d has %d rows, expected at least %d", index, input.Length(), rowCount) + return udferr.Newf("python udf: evaluated input vector %d has %d rows, expected at least %d", index, input.Length(), rowCount) } expected, err := function.NewPythonTypeDescriptor(planTypeToSQL(*call.ArgumentTypes[index])) if err != nil { @@ -458,7 +459,7 @@ func validateExternalRoutineInputs(inputs []*vector.Vector, call *planpb.Routine return err } if expectedFingerprint != actualFingerprint { - return moerr.NewInternalErrorNoCtxf("python udf: evaluated input vector %d type %s does not match the frozen descriptor", index, input.GetType().DescString()) + return udferr.Newf("python udf: evaluated input vector %d type %s does not match the frozen descriptor", index, input.GetType().DescString()) } } return nil @@ -588,20 +589,20 @@ func externalRoutineRowCount(mode string, batches []*batch.Batch) (int, error) { rowCount := -1 for index, input := range batches { if input == nil { - return 0, moerr.NewInternalErrorNoCtxf("external routine input batch %d is nil", index) + return 0, udferr.Newf("external routine input batch %d is nil", index) } if rowCount < 0 { rowCount = input.RowCount() continue } if input.RowCount() != rowCount { - return 0, moerr.NewInternalErrorNoCtxf("python udf: external routine input batch %d has %d rows, expected %d for the logical row domain", index, input.RowCount(), rowCount) + return 0, udferr.Newf("python udf: external routine input batch %d has %d rows, expected %d for the logical row domain", index, input.RowCount(), rowCount) } } return rowCount, nil } if mode == "VECTOR" { - return 0, moerr.NewInternalErrorNoCtxf("python udf: zero-argument VECTOR requires an input batch with num_rows") + return 0, udferr.Newf("python udf: zero-argument VECTOR requires an input batch with num_rows") } return 1, nil } @@ -612,7 +613,7 @@ func externalRoutineRowCount(mode string, batches []*batch.Batch) (int, error) { // prepared plan can outlive both. func buildRoutineContext(proc *process.Process) (map[string]string, error) { if proc == nil || proc.Base == nil { - return nil, moerr.NewInternalErrorNoCtxf("python udf: process is unavailable for statement context") + return nil, udferr.Newf("python udf: process is unavailable for statement context") } context := make(map[string]string) // The current query id is stable for the statement and changes for every @@ -630,7 +631,7 @@ func buildRoutineContext(proc *process.Process) (map[string]string, error) { // the lifetime of that process; a real frontend statement always uses // its query start above. if proc.Base.UnixTime == 0 { - return nil, moerr.NewInternalErrorNoCtxf("python udf: statement timestamp is unavailable") + return nil, udferr.Newf("python udf: statement timestamp is unavailable") } queryStart = time.Unix(0, proc.Base.UnixTime) } @@ -638,12 +639,12 @@ func buildRoutineContext(proc *process.Process) (map[string]string, error) { location := proc.GetSessionInfo().TimeZone if location == nil { - return nil, moerr.NewInternalErrorNoCtxf("python udf: session timezone is unavailable") + return nil, udferr.Newf("python udf: session timezone is unavailable") } zoneName := location.String() var timezoneErr error if zoneName == "" || zoneName == "SYSTEM" { - return nil, moerr.NewInternalErrorNoCtxf("python udf: session timezone is not a stable IANA identity") + return nil, udferr.Newf("python udf: session timezone is not a stable IANA identity") } if zoneName == "Local" { zoneName, location, timezoneErr = resolveSystemTimezone() @@ -654,7 +655,7 @@ func buildRoutineContext(proc *process.Process) (map[string]string, error) { if zoneName == "FixedZone" { _, offset := queryStart.In(location).Zone() if offset%60 != 0 { - return nil, moerr.NewInternalErrorNoCtxf("python udf: fixed session timezone offset must be a whole number of minutes") + return nil, udferr.Newf("python udf: fixed session timezone offset must be a whole number of minutes") } context["session_timezone_kind"] = "FIXED_OFFSET" context["session_timezone_offset_minutes"] = formatTimezoneOffsetMinutes(offset / 60) @@ -720,7 +721,7 @@ func resolveSystemTimezone() (string, *time.Location, error) { return candidate, location, nil } } - return "", nil, moerr.NewInternalErrorNoCtxf("python udf: SYSTEM timezone has no stable IANA identity") + return "", nil, udferr.Newf("python udf: SYSTEM timezone has no stable IANA identity") } func formatTimezoneOffsetMinutes(offset int) string { diff --git a/pkg/sql/plan/function/python_udf.go b/pkg/sql/plan/function/python_udf.go index af8f2fa811e6c..66d956168a843 100644 --- a/pkg/sql/plan/function/python_udf.go +++ b/pkg/sql/plan/function/python_udf.go @@ -15,10 +15,11 @@ package function import ( - "github.com/matrixorigin/matrixone/pkg/common/moerr" - "github.com/matrixorigin/matrixone/pkg/util/errutil" "strconv" + "github.com/matrixorigin/matrixone/pkg/udf/udferr" + "github.com/matrixorigin/matrixone/pkg/util/errutil" + "github.com/google/uuid" "github.com/matrixorigin/matrixone/pkg/container/types" "github.com/matrixorigin/matrixone/pkg/container/vector" @@ -176,56 +177,47 @@ func pythonUdfRetType(parameters []types.Type) types.Type { // historical overload id. Long-term execution enters through the typed // RoutineCall field and ExternalRoutineEval. func rejectPythonJSONPlan(_ []*vector.Vector, _ vector.FunctionResultWrapper, _ *process.Process, _ int, _ *FunctionSelectList) error { - return moerr.NewInternalErrorNoCtxf("UNSUPPORTED_ROUTINE_VERSION: Python JSON plan execution is not supported; reprepare the statement") + return udferr.Newf("UNSUPPORTED_ROUTINE_VERSION: Python JSON plan execution is not supported; reprepare the statement") } func validatePythonRoutineDescriptor(descriptor *vector.Vector) error { if descriptor == nil { - return moerr.NewInternalErrorNoCtxf("python udf: missing routine descriptor") + return udferr.Newf("python udf: missing routine descriptor") } if !descriptor.IsConst() { - return moerr.NewInternalErrorNoCtxf("python udf: routine descriptor must be constant") + return udferr.Newf("python udf: routine descriptor must be constant") } if descriptor.Length() == 0 { - return moerr.NewInternalErrorNoCtxf("python udf: routine descriptor is empty") + return udferr.Newf("python udf: routine descriptor is empty") } return nil } func validatePythonInputVectors(inputs []*vector.Vector, args []types.Type, length int) error { if len(inputs) != len(args) { - return moerr.NewInternalErrorNoCtxf("python udf: input column count %d does not match routine argument count %d", len(inputs), len(args)) + return udferr.Newf("python udf: input column count %d does not match routine argument count %d", len(inputs), len(args)) } for index, input := range inputs { if input == nil { - return moerr.NewInternalErrorNoCtxf("python udf: input vector %d is nil", index) + return udferr.Newf("python udf: input vector %d is nil", index) } if input.Length() == 0 { - return moerr.NewInternalErrorNoCtxf("python udf: input vector %d is empty", index) + return udferr.Newf("python udf: input vector %d is empty", index) } if !input.IsConst() && input.Length() < length { - return moerr.NewInternalErrorNoCtxf("python udf: input vector %d is shorter than invocation length", index) + return udferr.Newf("python udf: input vector %d is shorter than invocation length", index) } if input.GetType() == nil || !pythonTypesEqual(*input.GetType(), args[index]) { actual := "" if input.GetType() != nil { actual = input.GetType().String() } - return moerr.NewInternalErrorNoCtxf("python udf: input vector %d type %s does not match %s", index, actual, args[index].String()) + return udferr.Newf("python udf: input vector %d type %s does not match %s", index, actual, args[index].String()) } } return nil } -func hasNullInput(inputs []*vector.Vector, row int) bool { - for _, input := range inputs { - if input == nil || input.IsNull(uint64(row)) { - return true - } - } - return false -} - func invocationTuple(context map[string]string, queryID string, accountID uint32) (protocol.FencingTuple, error) { statementID := context["statement_id"] if statementID == "" { @@ -250,7 +242,7 @@ func invocationTuple(context map[string]string, queryID string, accountID uint32 // explicit epoch when it is introduced. groupID = statementID + "/python/" + invocationID.String() } else if context["group_epoch"] == "" { - return protocol.FencingTuple{}, moerr.NewInternalErrorNoCtxf("python udf: explicit group_id requires group_epoch") + return protocol.FencingTuple{}, udferr.Newf("python udf: explicit group_id requires group_epoch") } groupEpoch, err := positiveContextUint(context, "group_epoch", 1) if err != nil { @@ -284,7 +276,7 @@ func positiveContextUint(context map[string]string, key string, fallback uint64) } parsed, err := strconv.ParseUint(value, 10, 64) if err != nil || parsed == 0 { - return 0, moerr.NewInternalErrorNoCtxf("python udf: invalid %s %q", key, value) + return 0, udferr.Newf("python udf: invalid %s %q", key, value) } return parsed, nil } diff --git a/pkg/sql/plan/function/udf.go b/pkg/sql/plan/function/udf.go index e102ce3b9cabd..121a09831321c 100644 --- a/pkg/sql/plan/function/udf.go +++ b/pkg/sql/plan/function/udf.go @@ -18,13 +18,14 @@ import ( "crypto/sha256" "encoding/hex" "encoding/json" - "github.com/matrixorigin/matrixone/pkg/common/moerr" - "github.com/matrixorigin/matrixone/pkg/util/errutil" "io" "reflect" "strings" "unicode/utf8" + "github.com/matrixorigin/matrixone/pkg/udf/udferr" + "github.com/matrixorigin/matrixone/pkg/util/errutil" + "github.com/matrixorigin/matrixone/pkg/container/types" "github.com/matrixorigin/matrixone/pkg/pb/plan" "github.com/matrixorigin/matrixone/pkg/udf" @@ -101,7 +102,7 @@ func PythonSignatureMetadata( returnType *PythonTypeDescriptor, ) (canonicalInput, canonicalReturn, fingerprint string, err error) { if returnType == nil { - return "", "", "", moerr.NewInternalErrorNoCtxf("python routine is missing return descriptor") + return "", "", "", udferr.Newf("python routine is missing return descriptor") } if args == nil { args = []PythonTypeDescriptor{} @@ -123,7 +124,7 @@ func PythonSignatureMetadata( return "", "", "", errutil.Wrapf(err, "marshal Python return descriptor") } if len(inputBytes) > types.MaxStringSize || len(returnBytes) > types.MaxStringSize { - return "", "", "", moerr.NewInternalErrorNoCtxf("python signature descriptor exceeds the %d-byte catalog limit", types.MaxStringSize) + return "", "", "", udferr.Newf("python signature descriptor exceeds the %d-byte catalog limit", types.MaxStringSize) } keyBytes, err := json.Marshal(struct { SchemaVersion int `json:"schema_version"` @@ -157,7 +158,7 @@ func DecodePythonRoutineBody(raw string) (PythonRoutineBody, error) { var extra json.RawMessage if err := decoder.Decode(&extra); err != io.EOF { if err == nil { - return PythonRoutineBody{}, moerr.NewInternalErrorNoCtxf("python routine body contains multiple JSON values") + return PythonRoutineBody{}, udferr.Newf("python routine body contains multiple JSON values") } return PythonRoutineBody{}, err } @@ -200,7 +201,7 @@ func PythonRoutineBodyFingerprint(body PythonRoutineBody) (string, error) { // independently hashing source text. func SQLRoutineFingerprint(body, argTypes, returnType string) (string, error) { if body == "" || returnType == "" { - return "", moerr.NewInternalErrorNoCtxf("SQL routine definition is incomplete") + return "", udferr.Newf("SQL routine definition is incomplete") } encoded, err := json.Marshal(struct { SchemaVersion int `json:"schema_version"` @@ -228,7 +229,7 @@ func SQLRoutineFingerprint(body, argTypes, returnType string) (string, error) { // catalog contract instead of decoding and dispatching this payload. func DecodeUdfWithContext(raw []byte) (UdfWithContext, error) { _ = raw - return UdfWithContext{}, moerr.NewInternalErrorNoCtxf("UNSUPPORTED_ROUTINE_VERSION: Python JSON plan is not executable; rebind the statement from the current typed catalog contract") + return UdfWithContext{}, udferr.Newf("UNSUPPORTED_ROUTINE_VERSION: Python JSON plan is not executable; rebind the statement from the current typed catalog contract") } // Validate checks fields that are independent of the catalog argument list. @@ -236,47 +237,47 @@ func DecodeUdfWithContext(raw []byte) (UdfWithContext, error) { // legacy catalog args column has been decoded. func (body PythonRoutineBody) Validate() error { if body.DefinitionSchemaVersion != udf.PythonDefinitionSchemaVersion { - return moerr.NewInternalErrorNoCtxf("UNSUPPORTED_ROUTINE_VERSION: python definition schema %d is not supported", body.DefinitionSchemaVersion) + return udferr.Newf("UNSUPPORTED_ROUTINE_VERSION: python definition schema %d is not supported", body.DefinitionSchemaVersion) } if body.Handler == "" || body.Source == "" { - return moerr.NewInternalErrorNoCtxf("python routine handler and source are required") + return udferr.Newf("python routine handler and source are required") } if !utf8.ValidString(body.Source) { - return moerr.NewInternalErrorNoCtxf("UNSUPPORTED_ROUTINE_VERSION: Python source is not valid UTF-8") + return udferr.Newf("UNSUPPORTED_ROUTINE_VERSION: Python source is not valid UTF-8") } if int64(len([]byte(body.Source))) > pythonudf.DefaultMaxArtifactBytes { - return moerr.NewInternalErrorNoCtxf("RESOURCE_EXHAUSTED: Python source exceeds %d bytes", pythonudf.DefaultMaxArtifactBytes) + return udferr.Newf("RESOURCE_EXHAUSTED: Python source exceeds %d bytes", pythonudf.DefaultMaxArtifactBytes) } if strings.Contains(body.Handler, ":") { - return moerr.NewInternalErrorNoCtxf("UNSUPPORTED_ROUTINE_VERSION: Python external handler import requires an immutable artifact catalog") + return udferr.Newf("UNSUPPORTED_ROUTINE_VERSION: Python external handler import requires an immutable artifact catalog") } if body.Mode != "SCALAR" && body.Mode != "VECTOR" { - return moerr.NewInternalErrorNoCtxf("python routine has unsupported mode %q", body.Mode) + return udferr.Newf("python routine has unsupported mode %q", body.Mode) } if body.NullPolicy != udf.NullCallHandler && body.NullPolicy != udf.NullReturnNull { - return moerr.NewInternalErrorNoCtxf("python routine has unsupported NULL policy %q", body.NullPolicy) + return udferr.Newf("python routine has unsupported NULL policy %q", body.NullPolicy) } if body.ABIContract != udf.PythonABIContract || body.AdapterVersion != udf.PythonAdapterVersion { - return moerr.NewInternalErrorNoCtxf("python routine has unsupported ABI contract %q/%q", body.ABIContract, body.AdapterVersion) + return udferr.Newf("python routine has unsupported ABI contract %q/%q", body.ABIContract, body.AdapterVersion) } if body.SDKVersion != udf.PythonSDKVersion { - return moerr.NewInternalErrorNoCtxf("python routine has unsupported SDK %q", body.SDKVersion) + return udferr.Newf("python routine has unsupported SDK %q", body.SDKVersion) } if !udf.IsSHA256Digest(body.ArtifactDigest) || body.EnvironmentDigest == "" { - return moerr.NewInternalErrorNoCtxf("UNSUPPORTED_ROUTINE_VERSION: Python definition has no immutable artifact/environment digest; use DROP and CREATE") + return udferr.Newf("UNSUPPORTED_ROUTINE_VERSION: Python definition has no immutable artifact/environment digest; use DROP and CREATE") } if body.ArtifactDigest != udf.PythonInlineArtifactDigest(body.Handler, body.Source) { - return moerr.NewInternalErrorNoCtxf("python routine artifact digest does not match its source") + return udferr.Newf("python routine artifact digest does not match its source") } environmentDigest, err := udf.PythonEnvironmentDigest() if err != nil { return errutil.Wrapf(err, "python routine environment digest is unavailable") } if body.EnvironmentDigest != environmentDigest { - return moerr.NewInternalErrorNoCtxf("python routine environment digest does not match the current contract") + return udferr.Newf("python routine environment digest does not match the current contract") } if body.ReturnType == nil { - return moerr.NewInternalErrorNoCtxf("python routine is missing return descriptor") + return udferr.Newf("python routine is missing return descriptor") } for i, descriptor := range body.ArgTypes { if err := validatePythonTypeDescriptor(descriptor); err != nil { @@ -310,16 +311,16 @@ type Arg struct { // process at execution time. func (u *Udf) GetRoutineCall() (*plan.RoutineCall, error) { if u == nil { - return nil, moerr.NewInternalErrorNoCtxf("routine call requires a non-nil routine") + return nil, udferr.Newf("routine call requires a non-nil routine") } if u.Language == udf.LanguageSQL { return u.getSQLRoutineCall() } if u.Language != udf.LanguagePython { - return nil, moerr.NewInternalErrorNoCtxf("routine call has unsupported language %q", u.Language) + return nil, udferr.Newf("routine call has unsupported language %q", u.Language) } if u.FunctionID <= 0 || u.DatabaseID == 0 || u.Revision == 0 || u.NamespaceVersion == 0 { - return nil, moerr.NewInternalErrorNoCtxf("python routine has no stable catalog identity or revision") + return nil, udferr.Newf("python routine has no stable catalog identity or revision") } body, err := DecodePythonRoutineBody(u.Body) if err != nil { @@ -381,10 +382,10 @@ func (u *Udf) GetRoutineCall() (*plan.RoutineCall, error) { // plan dependency so replace/drop cannot silently reuse the old definition. func (u *Udf) getSQLRoutineCall() (*plan.RoutineCall, error) { if u.FunctionID <= 0 || u.DatabaseID == 0 || u.Revision == 0 || u.NamespaceVersion == 0 { - return nil, moerr.NewInternalErrorNoCtxf("SQL routine has no stable catalog identity or revision") + return nil, udferr.Newf("SQL routine has no stable catalog identity or revision") } if u.SemanticDefinitionSchemaVersion != udf.SQLDefinitionSchemaVersion || !udf.IsSHA256Digest(u.DefinitionFingerprint) { - return nil, moerr.NewInternalErrorNoCtxf("UNSUPPORTED_ROUTINE_VERSION: SQL routine has no supported semantic definition") + return nil, udferr.Newf("UNSUPPORTED_ROUTINE_VERSION: SQL routine has no supported semantic definition") } argTypes := u.GetArgsType() arguments := make([]*plan.Type, len(argTypes)) @@ -471,7 +472,7 @@ func (u *Udf) LoadPythonTypeContract() error { return nil } if u.Language != udf.LanguagePython { - return moerr.NewInternalErrorNoCtxf("UNSUPPORTED_ROUTINE_VERSION: unsupported routine language %q", u.Language) + return udferr.Newf("UNSUPPORTED_ROUTINE_VERSION: unsupported routine language %q", u.Language) } u.PythonArgTypes = nil u.PythonReturnType = nil @@ -494,13 +495,13 @@ func (u *Udf) ValidatePythonTypeContract() error { return nil } if u.Language != udf.LanguagePython { - return moerr.NewInternalErrorNoCtxf("UNSUPPORTED_ROUTINE_VERSION: unsupported routine language %q", u.Language) + return udferr.Newf("UNSUPPORTED_ROUTINE_VERSION: unsupported routine language %q", u.Language) } if u.PythonReturnType == nil { - return moerr.NewInternalErrorNoCtxf("python routine is missing return descriptor") + return udferr.Newf("python routine is missing return descriptor") } if len(u.PythonArgTypes) != len(u.Args) { - return moerr.NewInternalErrorNoCtxf("python routine has %d descriptors for %d arguments", len(u.PythonArgTypes), len(u.Args)) + return udferr.Newf("python routine has %d descriptors for %d arguments", len(u.PythonArgTypes), len(u.Args)) } for i, descriptor := range u.PythonArgTypes { if err := validatePythonTypeDescriptor(descriptor); err != nil { @@ -521,10 +522,10 @@ func (u *Udf) ValidatePythonCatalogSignature() error { return nil } if u.Language != udf.LanguagePython { - return moerr.NewInternalErrorNoCtxf("UNSUPPORTED_ROUTINE_VERSION: unsupported routine language %q", u.Language) + return udferr.Newf("UNSUPPORTED_ROUTINE_VERSION: unsupported routine language %q", u.Language) } if len(u.Args) != len(u.PythonArgTypes) || u.PythonReturnType == nil { - return moerr.NewInternalErrorNoCtxf("UNSUPPORTED_ROUTINE_VERSION: Python catalog signature is incomplete") + return udferr.Newf("UNSUPPORTED_ROUTINE_VERSION: Python catalog signature is incomplete") } for index, argument := range u.Args { if argument == nil || !pythonCatalogTypeNameMatches(argument.Type, u.PythonArgTypes[index].Type().Oid) { @@ -532,11 +533,11 @@ func (u *Udf) ValidatePythonCatalogSignature() error { if argument != nil { name = argument.Type } - return moerr.NewInternalErrorNoCtxf("UNSUPPORTED_ROUTINE_VERSION: Python catalog argument %d type %q does not match its descriptor", index+1, name) + return udferr.Newf("UNSUPPORTED_ROUTINE_VERSION: Python catalog argument %d type %q does not match its descriptor", index+1, name) } } if !pythonCatalogTypeNameMatches(u.RetType, u.PythonReturnType.Type().Oid) { - return moerr.NewInternalErrorNoCtxf("UNSUPPORTED_ROUTINE_VERSION: Python catalog return type %q does not match its descriptor", u.RetType) + return udferr.Newf("UNSUPPORTED_ROUTINE_VERSION: Python catalog return type %q does not match its descriptor", u.RetType) } return nil } @@ -565,7 +566,7 @@ func validatePythonTypeDescriptor(descriptor PythonTypeDescriptor) error { return err } if !reflect.DeepEqual(canonical, descriptor) { - return moerr.NewInternalErrorNoCtxf("descriptor is not canonical for %s", typ.String()) + return udferr.Newf("descriptor is not canonical for %s", typ.String()) } if _, err := descriptor.Fingerprint(); err != nil { return errutil.Wrapf(err, "descriptor has no valid Arrow physical type") diff --git a/pkg/sql/plan/routine_dependency.go b/pkg/sql/plan/routine_dependency.go index aadb7307d7ee0..f589ad616ddc9 100644 --- a/pkg/sql/plan/routine_dependency.go +++ b/pkg/sql/plan/routine_dependency.go @@ -16,9 +16,10 @@ package plan import ( "fmt" - "github.com/matrixorigin/matrixone/pkg/common/moerr" "strings" + "github.com/matrixorigin/matrixone/pkg/udf/udferr" + planpb "github.com/matrixorigin/matrixone/pkg/pb/plan" "github.com/matrixorigin/matrixone/pkg/udf" ) @@ -37,16 +38,16 @@ func (builder *QueryBuilder) assignRoutineCallsite(call *planpb.RoutineCall) err return nil } if call.Language != udf.LanguageSQL && call.Language != udf.LanguagePython { - return moerr.NewInternalErrorNoCtxf("UNSUPPORTED_ROUTINE_VERSION: routine call language %q is not canonical", call.Language) + return udferr.Newf("UNSUPPORTED_ROUTINE_VERSION: routine call language %q is not canonical", call.Language) } if call.CallsiteId != "" { if len(call.CallsiteId) > 256 || strings.ContainsAny(call.CallsiteId, "\r\n") { - return moerr.NewInternalErrorNoCtxf("PROGRAM_LIMIT_EXCEEDED: routine callsite id is too large or invalid") + return udferr.Newf("PROGRAM_LIMIT_EXCEEDED: routine callsite id is too large or invalid") } return nil } if builder.nextRoutineCallID == ^uint64(0) { - return moerr.NewInternalErrorNoCtxf("PROGRAM_LIMIT_EXCEEDED: routine callsite id exhausted") + return udferr.Newf("PROGRAM_LIMIT_EXCEEDED: routine callsite id exhausted") } builder.nextRoutineCallID++ call.CallsiteId = fmt.Sprintf("%s/%d", strings.ToLower(call.Language), builder.nextRoutineCallID) @@ -58,7 +59,7 @@ func (builder *QueryBuilder) recordRoutinePlanDependency(call *planpb.RoutineCal return nil } if call.Language != udf.LanguageSQL && call.Language != udf.LanguagePython { - return moerr.NewInternalErrorNoCtxf("UNSUPPORTED_ROUTINE_VERSION: routine call language %q is not canonical", call.Language) + return udferr.Newf("UNSUPPORTED_ROUTINE_VERSION: routine call language %q is not canonical", call.Language) } ref := call.FunctionRef dependency := &planpb.RoutinePlanDependency{ @@ -95,7 +96,7 @@ func (builder *QueryBuilder) recordRoutinePlanDependency(call *planpb.RoutineCal } } if len(builder.qry.RoutineDependencies) >= maxRoutinePlanDependencies { - return moerr.NewInternalErrorNoCtxf("PROGRAM_LIMIT_EXCEEDED: routine dependency count exceeds %d", maxRoutinePlanDependencies) + return udferr.Newf("PROGRAM_LIMIT_EXCEEDED: routine dependency count exceeds %d", maxRoutinePlanDependencies) } builder.qry.RoutineDependencies = append(builder.qry.RoutineDependencies, dependency) return nil diff --git a/pkg/udf/protocol/protocol.go b/pkg/udf/protocol/protocol.go index 405a9bd590604..4b4418a441b4f 100644 --- a/pkg/udf/protocol/protocol.go +++ b/pkg/udf/protocol/protocol.go @@ -23,12 +23,13 @@ import ( "encoding/hex" "encoding/json" "errors" - "github.com/matrixorigin/matrixone/pkg/common/moerr" - "github.com/matrixorigin/matrixone/pkg/util/errutil" "io" "sync" "time" "unicode/utf8" + + "github.com/matrixorigin/matrixone/pkg/udf/udferr" + "github.com/matrixorigin/matrixone/pkg/util/errutil" ) const ( @@ -52,12 +53,12 @@ const ( ) var ( - ErrProtocol = moerr.NewInternalErrorNoCtx("python udf protocol violation") - ErrSequence = moerr.NewInternalErrorNoCtx("python udf sequence violation") - ErrGroupClosed = moerr.NewInternalErrorNoCtx("python udf execution group is closed") - ErrDuplicate = moerr.NewInternalErrorNoCtx("python udf identity is already present") - ErrLedgerFull = moerr.NewInternalErrorNoCtx("python udf terminal ledger is full") - ErrUnknownIdentity = moerr.NewInternalErrorNoCtx("python udf identity is unknown") + ErrProtocol = udferr.New("python udf protocol violation") + ErrSequence = udferr.New("python udf sequence violation") + ErrGroupClosed = udferr.New("python udf execution group is closed") + ErrDuplicate = udferr.New("python udf identity is already present") + ErrLedgerFull = udferr.New("python udf terminal ledger is full") + ErrUnknownIdentity = udferr.New("python udf identity is unknown") ) // FencingTuple is carried by every invocation control and data-plane @@ -368,9 +369,9 @@ func rejectDuplicateJSONKeys(data []byte) error { var trailing any if err := decoder.Decode(&trailing); err != io.EOF { if err == nil { - return moerr.NewInternalErrorNoCtx("control contains trailing JSON") + return udferr.New("control contains trailing JSON") } - return moerr.NewInternalErrorNoCtxf("decode trailing control data: %v", err) + return udferr.Newf("decode trailing control data: %v", err) } return nil } @@ -382,14 +383,14 @@ func walkJSONValue(decoder *json.Decoder) error { func walkJSONValueAtDepth(decoder *json.Decoder, depth int) error { token, err := decoder.Token() if err != nil { - return moerr.NewInternalErrorNoCtxf("decode control JSON: %v", err) + return udferr.Newf("decode control JSON: %v", err) } delim, ok := token.(json.Delim) if !ok { return nil } if depth >= MaxJSONNesting { - return moerr.NewInternalErrorNoCtxf("control JSON nesting exceeds %d levels", MaxJSONNesting) + return udferr.Newf("control JSON nesting exceeds %d levels", MaxJSONNesting) } switch delim { case '{': @@ -397,14 +398,14 @@ func walkJSONValueAtDepth(decoder *json.Decoder, depth int) error { for decoder.More() { keyToken, err := decoder.Token() if err != nil { - return moerr.NewInternalErrorNoCtxf("decode control object key: %v", err) + return udferr.Newf("decode control object key: %v", err) } key, ok := keyToken.(string) if !ok { - return moerr.NewInternalErrorNoCtx("control object key is not a string") + return udferr.New("control object key is not a string") } if _, exists := seen[key]; exists { - return moerr.NewInternalErrorNoCtxf("duplicate control JSON field %q", key) + return udferr.Newf("duplicate control JSON field %q", key) } seen[key] = struct{}{} if err := walkJSONValueAtDepth(decoder, depth+1); err != nil { @@ -422,7 +423,7 @@ func walkJSONValueAtDepth(decoder *json.Decoder, depth int) error { _, err = decoder.Token() return err default: - return moerr.NewInternalErrorNoCtxf("unexpected control JSON delimiter %q", delim) + return udferr.Newf("unexpected control JSON delimiter %q", delim) } } @@ -788,10 +789,6 @@ func (g *ExecutionGroup) MemberTerminal(memberID string) error { return nil } -func (g *ExecutionGroup) canReleaseLocked() bool { - return g.shouldReleaseLocked() && !g.releaseBusy -} - func (g *ExecutionGroup) shouldReleaseLocked() bool { return g.closing && len(g.members) == 0 && g.inFlight == 0 && !g.released } diff --git a/pkg/udf/python/arrow.go b/pkg/udf/python/arrow.go index 11f254a6ad7c6..a129311757350 100644 --- a/pkg/udf/python/arrow.go +++ b/pkg/udf/python/arrow.go @@ -22,13 +22,14 @@ import ( "encoding/hex" "encoding/json" "fmt" - "github.com/matrixorigin/matrixone/pkg/common/moerr" - "github.com/matrixorigin/matrixone/pkg/util/errutil" "io" "math" "reflect" "unicode/utf8" + "github.com/matrixorigin/matrixone/pkg/udf/udferr" + "github.com/matrixorigin/matrixone/pkg/util/errutil" + "github.com/apache/arrow-go/v18/arrow" "github.com/apache/arrow-go/v18/arrow/array" "github.com/apache/arrow-go/v18/arrow/decimal128" @@ -105,11 +106,11 @@ func NewTypeDescriptor(typ types.Type) (TypeDescriptor, error) { types.T_binary, types.T_varbinary, types.T_blob, types.T_array_float32, types.T_array_float64: default: - return TypeDescriptor{}, moerr.NewInternalErrorNoCtxf("python udf does not support MatrixOne type %s", typ.String()) + return TypeDescriptor{}, udferr.Newf("python udf does not support MatrixOne type %s", typ.String()) } if typ.Oid == types.T_array_float32 || typ.Oid == types.T_array_float64 { if typ.Width <= 0 { - return TypeDescriptor{}, moerr.NewInternalErrorNoCtxf("python udf requires a positive vector dimension for %s", typ.String()) + return TypeDescriptor{}, udferr.Newf("python udf requires a positive vector dimension for %s", typ.String()) } d.OffsetWidth = 0 } @@ -157,29 +158,29 @@ func (d TypeDescriptor) Fingerprint() (string, error) { func (d TypeDescriptor) Validate() error { oid := types.T(d.TypeID) if d.Width < 0 { - return moerr.NewInternalErrorNoCtxf("TYPE_CONTRACT: descriptor width must be non-negative") + return udferr.Newf("TYPE_CONTRACT: descriptor width must be non-negative") } if d.Scale < 0 { - return moerr.NewInternalErrorNoCtxf("TYPE_CONTRACT: descriptor scale must be non-negative") + return udferr.Newf("TYPE_CONTRACT: descriptor scale must be non-negative") } expectedOffsetWidth := int32(32) if oid == types.T_uuid || oid == types.T_array_float32 || oid == types.T_array_float64 { expectedOffsetWidth = 0 } if d.OffsetWidth != expectedOffsetWidth { - return moerr.NewInternalErrorNoCtxf("TYPE_CONTRACT: descriptor offset_width must be %d for %s", expectedOffsetWidth, oid.String()) + return udferr.Newf("TYPE_CONTRACT: descriptor offset_width must be %d for %s", expectedOffsetWidth, oid.String()) } if d.JSONEncoding != "" && (oid != types.T_json || d.JSONEncoding != "canonical_text") { - return moerr.NewInternalErrorNoCtxf("TYPE_CONTRACT: unsupported JSON encoding") + return udferr.Newf("TYPE_CONTRACT: unsupported JSON encoding") } if oid == types.T_json && d.JSONEncoding != "canonical_text" { - return moerr.NewInternalErrorNoCtxf("TYPE_CONTRACT: JSON descriptor must use canonical_text encoding") + return udferr.Newf("TYPE_CONTRACT: JSON descriptor must use canonical_text encoding") } if d.TemporalEncoding != "" && (oid != types.T_date && oid != types.T_datetime && oid != types.T_timestamp || d.TemporalEncoding != "sql_zero_struct") { - return moerr.NewInternalErrorNoCtxf("TYPE_CONTRACT: unsupported temporal encoding") + return udferr.Newf("TYPE_CONTRACT: unsupported temporal encoding") } if (oid == types.T_date || oid == types.T_datetime || oid == types.T_timestamp) && d.TemporalEncoding != "sql_zero_struct" { - return moerr.NewInternalErrorNoCtxf("TYPE_CONTRACT: temporal descriptor must use sql_zero_struct encoding") + return udferr.Newf("TYPE_CONTRACT: temporal descriptor must use sql_zero_struct encoding") } switch oid { @@ -187,7 +188,7 @@ func (d TypeDescriptor) Validate() error { types.T_uint8, types.T_uint16, types.T_uint32, types.T_uint64, types.T_float32, types.T_float64, types.T_json: if d.Width != 0 || d.Scale != 0 || d.Charset != types.CharsetLegacy { - return moerr.NewInternalErrorNoCtxf("TYPE_CONTRACT: descriptor carries unused fields for %s", oid.String()) + return udferr.Newf("TYPE_CONTRACT: descriptor carries unused fields for %s", oid.String()) } case types.T_decimal64, types.T_decimal128: maxPrecision := int32(38) @@ -195,45 +196,45 @@ func (d TypeDescriptor) Validate() error { maxPrecision = 18 } if d.Width < 1 || d.Width > maxPrecision { - return moerr.NewInternalErrorNoCtxf("TYPE_CONTRACT: decimal precision %d is outside %s range", d.Width, oid.String()) + return udferr.Newf("TYPE_CONTRACT: decimal precision %d is outside %s range", d.Width, oid.String()) } if d.Scale > d.Width { - return moerr.NewInternalErrorNoCtxf("TYPE_CONTRACT: decimal scale %d exceeds precision %d", d.Scale, d.Width) + return udferr.Newf("TYPE_CONTRACT: decimal scale %d exceeds precision %d", d.Scale, d.Width) } if d.Charset != types.CharsetLegacy { - return moerr.NewInternalErrorNoCtxf("TYPE_CONTRACT: decimal descriptor carries a charset") + return udferr.Newf("TYPE_CONTRACT: decimal descriptor carries a charset") } case types.T_date: if d.Width != 0 || d.Scale != 0 || d.Charset != types.CharsetLegacy { - return moerr.NewInternalErrorNoCtxf("TYPE_CONTRACT: DATE descriptor has unused or invalid fields") + return udferr.Newf("TYPE_CONTRACT: DATE descriptor has unused or invalid fields") } case types.T_time, types.T_datetime, types.T_timestamp: if d.Width != 0 || d.Scale > 6 || d.Charset != types.CharsetLegacy { - return moerr.NewInternalErrorNoCtxf("TYPE_CONTRACT: temporal scale or charset is outside the supported range") + return udferr.Newf("TYPE_CONTRACT: temporal scale or charset is outside the supported range") } case types.T_char, types.T_varchar, types.T_text: if d.Scale != 0 { - return moerr.NewInternalErrorNoCtxf("TYPE_CONTRACT: text descriptor carries an unused scale") + return udferr.Newf("TYPE_CONTRACT: text descriptor carries an unused scale") } switch d.Charset { case types.CharsetLegacy, types.CharsetUTF8MB4Bin, types.CharsetUTF8: default: - return moerr.NewInternalErrorNoCtxf("TYPE_CONTRACT: unsupported text charset %d", d.Charset) + return udferr.Newf("TYPE_CONTRACT: unsupported text charset %d", d.Charset) } case types.T_binary, types.T_varbinary, types.T_blob: if d.Scale != 0 || d.Charset != types.CharsetBinary { - return moerr.NewInternalErrorNoCtxf("TYPE_CONTRACT: binary descriptor must use the binary charset") + return udferr.Newf("TYPE_CONTRACT: binary descriptor must use the binary charset") } case types.T_uuid: if d.Width != 0 || d.Scale != 0 || d.Charset != types.CharsetLegacy { - return moerr.NewInternalErrorNoCtxf("TYPE_CONTRACT: UUID descriptor carries unused fields") + return udferr.Newf("TYPE_CONTRACT: UUID descriptor carries unused fields") } case types.T_array_float32, types.T_array_float64: if d.Width < 1 || d.Width > types.MaxArrayDimension || d.Scale != 0 || d.Charset != types.CharsetLegacy { - return moerr.NewInternalErrorNoCtxf("TYPE_CONTRACT: vector dimension or metadata is outside the supported range") + return udferr.Newf("TYPE_CONTRACT: vector dimension or metadata is outside the supported range") } default: - return moerr.NewInternalErrorNoCtxf("TYPE_CONTRACT: unsupported MatrixOne type id %d", d.TypeID) + return udferr.Newf("TYPE_CONTRACT: unsupported MatrixOne type id %d", d.TypeID) } return nil } @@ -271,7 +272,7 @@ func (d TypeDescriptor) arrowType() (arrow.DataType, error) { } } if precision < 1 || precision > decimal128.MaxPrecision { - return nil, moerr.NewInternalErrorNoCtxf("decimal precision %d is outside Arrow's supported range", precision) + return nil, udferr.Newf("decimal precision %d is outside Arrow's supported range", precision) } return &arrow.Decimal128Type{Precision: precision, Scale: d.Scale}, nil case types.T_char, types.T_varchar, types.T_text, types.T_json: @@ -290,12 +291,12 @@ func (d TypeDescriptor) arrowType() (arrow.DataType, error) { return &arrow.DurationType{Unit: arrow.Microsecond}, nil case types.T_array_float32: if d.Width <= 0 { - return nil, moerr.NewInternalErrorNoCtxf("invalid vecf32 dimension %d", d.Width) + return nil, udferr.Newf("invalid vecf32 dimension %d", d.Width) } return arrow.FixedSizeListOf(d.Width, arrow.PrimitiveTypes.Float32), nil case types.T_array_float64: if d.Width <= 0 { - return nil, moerr.NewInternalErrorNoCtxf("invalid vecf64 dimension %d", d.Width) + return nil, udferr.Newf("invalid vecf64 dimension %d", d.Width) } return arrow.FixedSizeListOf(d.Width, arrow.PrimitiveTypes.Float64), nil case types.T_date: @@ -305,7 +306,7 @@ func (d TypeDescriptor) arrowType() (arrow.DataType, error) { case types.T_timestamp: return sqlTemporalType(&arrow.TimestampType{Unit: arrow.Microsecond, TimeZone: "UTC"}), nil default: - return nil, moerr.NewInternalErrorNoCtxf("unsupported MatrixOne type id %d", d.TypeID) + return nil, udferr.Newf("unsupported MatrixOne type id %d", d.TypeID) } } func sqlTemporalType(value arrow.DataType) *arrow.StructType { @@ -332,10 +333,10 @@ func (d TypeDescriptor) ValidateField(field arrow.Field) error { return err } if field.Type == nil || !sameArrowType(field.Type, want.Type) || field.Nullable != want.Nullable { - return moerr.NewInternalErrorNoCtxf("TYPE_CONTRACT: Arrow type %s does not match %s", field.Type, want.Type) + return udferr.Newf("TYPE_CONTRACT: Arrow type %s does not match %s", field.Type, want.Type) } if !field.Metadata.Equal(want.Metadata) { - return moerr.NewInternalErrorNoCtxf("TYPE_CONTRACT: Arrow logical metadata does not match the frozen descriptor") + return udferr.Newf("TYPE_CONTRACT: Arrow logical metadata does not match the frozen descriptor") } return nil } @@ -362,11 +363,11 @@ type encodedRecordBatch struct { Rows int64 } -var errArrowBatchTooLarge = moerr.NewInternalErrorNoCtx("Arrow record batch exceeds the configured limit") +var errArrowBatchTooLarge = udferr.New("Arrow record batch exceeds the configured limit") func EncodeRecordBatch(record arrow.RecordBatch, maxBytes int64) ([]ArrowFrame, error) { if record == nil || record.Schema() == nil || maxBytes <= 0 { - return nil, moerr.NewInternalErrorNoCtxf("invalid Arrow record batch") + return nil, udferr.Newf("invalid Arrow record batch") } collector := &flightFrameCollector{maxBytes: maxBytes} writer := flight.NewRecordWriter(collector, ipc.WithSchema(record.Schema()), ipc.WithAllocator(memory.NewGoAllocator())) @@ -377,7 +378,7 @@ func EncodeRecordBatch(record arrow.RecordBatch, maxBytes int64) ([]ArrowFrame, return nil, errutil.Wrapf(err, "close Arrow record batch") } if len(collector.frames) < 2 { - return nil, moerr.NewInternalErrorNoCtxf("Arrow stream did not contain schema and record batch") + return nil, udferr.Newf("Arrow stream did not contain schema and record batch") } return collector.frames, nil } @@ -403,10 +404,10 @@ func (c *flightFrameCollector) reset(maxBytes, retainedBytes int64) { func (c *flightFrameCollector) Send(data *flight.FlightData) error { if data == nil || len(data.DataHeader) == 0 { - return moerr.NewInternalErrorNoCtxf("invalid Arrow Flight frame") + return udferr.Newf("invalid Arrow Flight frame") } if data.FlightDescriptor != nil || len(data.AppMetadata) != 0 { - return moerr.NewInternalErrorNoCtxf("unexpected metadata on Arrow payload frame") + return udferr.Newf("unexpected metadata on Arrow payload frame") } header := append([]byte(nil), data.DataHeader...) body := append([]byte(nil), data.DataBody...) @@ -435,10 +436,10 @@ type inputBatchWireEncoder struct { func (e *inputBatchWireEncoder) encode(record arrow.RecordBatch, maxBytes int64) ([]ArrowFrame, error) { if e == nil || e.closed || record == nil || maxBytes <= 0 { - return nil, moerr.NewInternalErrorNoCtxf("invalid Arrow record batch encoder") + return nil, udferr.Newf("invalid Arrow record batch encoder") } if e.schema == nil || !record.Schema().Equal(e.schema) { - return nil, moerr.NewInternalErrorNoCtxf("Arrow record batch schema does not match the invocation schema") + return nil, udferr.Newf("Arrow record batch schema does not match the invocation schema") } if e.writer == nil { e.collector = &flightFrameCollector{} @@ -457,12 +458,12 @@ func (e *inputBatchWireEncoder) encode(record arrow.RecordBatch, maxBytes int64) e.captureSchema() if writerHadSchema { if len(e.collector.frames) != 1 { - return nil, moerr.NewInternalErrorNoCtxf("Arrow writer did not emit one record frame") + return nil, udferr.Newf("Arrow writer did not emit one record frame") } return []ArrowFrame{e.schemaFrame, e.collector.frames[0]}, nil } if len(e.collector.frames) != 2 || !e.schemaReady { - return nil, moerr.NewInternalErrorNoCtxf("Arrow stream did not contain schema and record batch") + return nil, udferr.Newf("Arrow stream did not contain schema and record batch") } return append([]ArrowFrame(nil), e.collector.frames...), nil } @@ -526,7 +527,7 @@ type inputBatchEncoder struct { func newInputBatchEncoder(inputs []*vector.Vector, args []types.Type) (*inputBatchEncoder, error) { if len(inputs) != len(args) { - return nil, moerr.NewInternalErrorNoCtxf("invalid Python UDF input shape") + return nil, udferr.Newf("invalid Python UDF input shape") } fields := make([]arrow.Field, len(args)) descriptors := make([]TypeDescriptor, len(args)) @@ -546,14 +547,14 @@ func newInputBatchEncoder(inputs []*vector.Vector, args []types.Type) (*inputBat fixedWidth = false } if inputs[i] == nil { - return nil, moerr.NewInternalErrorNoCtxf("input column %d is missing", i) + return nil, udferr.Newf("input column %d is missing", i) } actualType := inputs[i].GetType() if actualType == nil { - return nil, moerr.NewInternalErrorNoCtxf("input column %d has no type", i) + return nil, udferr.Newf("input column %d has no type", i) } if !actualType.Eq(typ) { - return nil, moerr.NewInternalErrorNoCtxf("input column %d type %s does not match the frozen argument type %s", i, actualType.DescString(), typ.DescString()) + return nil, udferr.Newf("input column %d type %s does not match the frozen argument type %s", i, actualType.DescString(), typ.DescString()) } if inputs[i].Length() > 0 && isVarlenaInputType(typ.Oid) { varlenaParameters[i] = vector.GenerateFunctionStrParameter(inputs[i]) @@ -572,7 +573,7 @@ func newInputBatchEncoder(inputs []*vector.Vector, args []types.Type) (*inputBat func (e *inputBatchEncoder) encode(record arrow.RecordBatch, maxBytes int64) ([]ArrowFrame, error) { if e == nil { - return nil, moerr.NewInternalErrorNoCtxf("invalid Python UDF input encoder") + return nil, udferr.Newf("invalid Python UDF input encoder") } if e.wire == nil { e.wire = &inputBatchWireEncoder{schema: e.schema} @@ -589,11 +590,11 @@ func (e *inputBatchEncoder) close() error { func (e *inputBatchEncoder) build(start, length int) (arrow.RecordBatch, *arrow.Schema, error) { if e == nil || start < 0 || length <= 0 { - return nil, nil, moerr.NewInternalErrorNoCtxf("invalid Python UDF input shape") + return nil, nil, udferr.Newf("invalid Python UDF input shape") } for i, input := range e.inputs { if input.Length() == 0 || (!input.IsConst() && input.Length() < start+length) { - return nil, nil, moerr.NewInternalErrorNoCtxf("input column %d is shorter than batch range", i) + return nil, nil, udferr.Newf("input column %d is shorter than batch range", i) } } if len(e.args) == 0 { @@ -649,7 +650,7 @@ func appendBulkFixedInputValue( return valid } if builder == nil || v == nil { - return false, moerr.NewInternalErrorNoCtxf("input column is missing") + return false, udferr.Newf("input column is missing") } switch typ.Oid { @@ -735,7 +736,7 @@ func bulkFixedInputWindow[T any](v *vector.Vector, start, length int) ([]T, erro values := vector.MustFixedColNoTypeCheck[T](v) if v.IsConst() { if len(values) == 0 { - return nil, moerr.NewInternalErrorNoCtxf("constant input column has no value") + return nil, udferr.Newf("constant input column has no value") } expanded := make([]T, length) for row := range expanded { @@ -744,7 +745,7 @@ func bulkFixedInputWindow[T any](v *vector.Vector, start, length int) ([]T, erro return expanded, nil } if start < 0 || length <= 0 || start > len(values) || length > len(values)-start { - return nil, moerr.NewInternalErrorNoCtxf("input column is shorter than batch range") + return nil, udferr.Newf("input column is shorter than batch range") } return values[start : start+length], nil } @@ -873,7 +874,7 @@ func appendInputValue( index := sourceRow(v, start, row) null := v.IsNull(uint64(index)) if !null && isVarlenaInputType(typ.Oid) && varlenaParameter == nil { - return moerr.NewInternalErrorNoCtxf("missing cached varlena input parameter") + return udferr.Newf("missing cached varlena input parameter") } if typ.Oid == types.T_date || typ.Oid == types.T_datetime || typ.Oid == types.T_timestamp { return appendTemporalInput(builder.(*array.StructBuilder), v, typ, index, null) @@ -1028,7 +1029,7 @@ func appendInputValue( b.ValueBuilder().(*array.Float64Builder).AppendValues(vector.GetArrayAt[float64](v, index), nil) } default: - return moerr.NewInternalErrorNoCtxf("unsupported input type %s", typ.String()) + return udferr.Newf("unsupported input type %s", typ.String()) } return nil } @@ -1101,7 +1102,7 @@ func appendTemporalInput(builder *array.StructBuilder, v *vector.Vector, typ typ builder.FieldBuilder(0).(*array.BooleanBuilder).Append(zero) builder.FieldBuilder(1).(*array.TimestampBuilder).Append(arrow.Timestamp(raw)) default: - return moerr.NewInternalErrorNoCtxf("unsupported temporal type %s", typ.String()) + return udferr.Newf("unsupported temporal type %s", typ.String()) } return nil } @@ -1114,24 +1115,24 @@ func decimal64ToArrow(value types.Decimal64) decimal128.Num { func DecodeRecordBatch(schemaFrame, batchFrame ArrowFrame, maxBytes int64) (arrow.RecordBatch, error) { if len(schemaFrame.Header) == 0 || len(batchFrame.Header) == 0 || maxBytes <= 0 { - return nil, moerr.NewInternalErrorNoCtxf("invalid Arrow output frames") + return nil, udferr.Newf("invalid Arrow output frames") } if len(schemaFrame.Body) != 0 { - return nil, moerr.NewInternalErrorNoCtxf("invalid Arrow schema frame body") + return nil, udferr.Newf("invalid Arrow schema frame body") } schemaInfo, err := arrowipc.InspectMessage(context.Background(), schemaFrame.Header, arrowipc.ValidationOptions{MaxMetadataBytes: arrowipc.DefaultMaxMetadataBytes, MaxBodyBytes: 0, BodyEnvelopeBytes: 0, MaxDecodedRecordBytes: 1}) if err != nil { return nil, err } if schemaInfo.HeaderType != arrowipc.MessageHeaderSchema { - return nil, moerr.NewInternalErrorNoCtxf("invalid Arrow schema frame header %d", schemaInfo.HeaderType) + return nil, udferr.Newf("invalid Arrow schema frame header %d", schemaInfo.HeaderType) } batchInfo, err := arrowipc.InspectMessage(context.Background(), batchFrame.Header, arrowipc.ValidationOptions{MaxMetadataBytes: arrowipc.DefaultMaxMetadataBytes, MaxBodyBytes: maxBytes, BodyEnvelopeBytes: int64(len(batchFrame.Body)), Body: batchFrame.Body, ValidateBody: true, MaxDecodedRecordBytes: maxBytes}) if err != nil { return nil, err } if batchInfo.HeaderType != arrowipc.MessageHeaderRecordBatch { - return nil, moerr.NewInternalErrorNoCtxf("invalid Arrow record frame header %d", batchInfo.HeaderType) + return nil, udferr.Newf("invalid Arrow record frame header %d", batchInfo.HeaderType) } // The snapshot is already the immutable validation/publication backing. // Feed that backing directly to Arrow's stream reader instead of assembling @@ -1145,11 +1146,11 @@ func DecodeRecordBatch(schemaFrame, batchFrame ArrowFrame, maxBytes int64) (arro } defer reader.Release() if !reader.Next() { - return nil, moerr.NewInternalErrorNoCtxf("decode Arrow output: %v", reader.Err()) + return nil, udferr.Newf("decode Arrow output: %v", reader.Err()) } record := reader.RecordBatch() if record == nil { - return nil, moerr.NewInternalErrorNoCtxf("decode Arrow output: missing record batch") + return nil, udferr.Newf("decode Arrow output: missing record batch") } record.Retain() return record, nil @@ -1240,10 +1241,10 @@ func (r *ipcStreamReader) Read(dst []byte) (int, error) { func AppendArrowResult(descriptor TypeDescriptor, input arrow.Array, result vector.FunctionResultWrapper, mp *mpool.MPool) error { if input == nil { - return moerr.NewInternalErrorNoCtxf("TYPE_CONTRACT: missing Arrow result") + return udferr.Newf("TYPE_CONTRACT: missing Arrow result") } if result == nil { - return moerr.NewInternalErrorNoCtxf("python udf: missing result wrapper") + return udferr.Newf("python udf: missing result wrapper") } // AppendArrowResult is also the public adapter boundary used by runtimes // other than the Flight Gateway. Do not let an invalid manually-built @@ -1262,7 +1263,7 @@ func AppendArrowResult(descriptor TypeDescriptor, input arrow.Array, result vect return nil } if mp == nil { - return moerr.NewInternalErrorNoCtxf("python udf: missing memory pool for Arrow result") + return udferr.Newf("python udf: missing memory pool for Arrow result") } result.GetResultVector().SetTypeScale(descriptor.Scale) typ := types.T(descriptor.TypeID) @@ -1348,7 +1349,7 @@ func AppendArrowResult(descriptor TypeDescriptor, input arrow.Array, result vect return err } default: - return moerr.NewInternalErrorNoCtxf("unsupported output type %s", typ.String()) + return udferr.Newf("unsupported output type %s", typ.String()) } } return nil @@ -1449,7 +1450,7 @@ func appendFloat32ArrayResult(input *array.FixedSizeList, row int, null bool, re items := make([]float32, end-start) for i := start; i < end; i++ { if values.IsNull(int(i)) { - return moerr.NewInternalErrorNoCtxf("TYPE_CONTRACT: vector child at row %d is null", row) + return udferr.Newf("TYPE_CONTRACT: vector child at row %d is null", row) } items[i-start] = values.Value(int(i)) } @@ -1465,7 +1466,7 @@ func appendFloat64ArrayResult(input *array.FixedSizeList, row int, null bool, re items := make([]float64, end-start) for i := start; i < end; i++ { if values.IsNull(int(i)) { - return moerr.NewInternalErrorNoCtxf("TYPE_CONTRACT: vector child at row %d is null", row) + return udferr.Newf("TYPE_CONTRACT: vector child at row %d is null", row) } items[i-start] = values.Value(int(i)) } @@ -1477,14 +1478,14 @@ func validateArrayType(descriptor TypeDescriptor, input arrow.Array) error { return err } if !sameArrowType(input.DataType(), want) { - return moerr.NewInternalErrorNoCtxf("TYPE_CONTRACT: output Arrow type %s does not match %s", input.DataType(), want) + return udferr.Newf("TYPE_CONTRACT: output Arrow type %s does not match %s", input.DataType(), want) } return nil } func validateArrowValueDomain(descriptor TypeDescriptor, input arrow.Array) error { if input == nil { - return moerr.NewInternalErrorNoCtxf("TYPE_CONTRACT: missing Arrow result") + return udferr.Newf("TYPE_CONTRACT: missing Arrow result") } for row := 0; row < input.Len(); row++ { if input.IsNull(row) { @@ -1493,7 +1494,7 @@ func validateArrowValueDomain(descriptor TypeDescriptor, input arrow.Array) erro switch types.T(descriptor.TypeID) { case types.T_char, types.T_varchar, types.T_text: if descriptor.Width > 0 && int32(utf8.RuneCountInString(input.(*array.String).Value(row))) > descriptor.Width { - return moerr.NewInternalErrorNoCtxf("TYPE_CONTRACT: string row %d exceeds width %d", row, descriptor.Width) + return udferr.Newf("TYPE_CONTRACT: string row %d exceeds width %d", row, descriptor.Width) } case types.T_json: value := input.(*array.String).Value(row) @@ -1502,16 +1503,16 @@ func validateArrowValueDomain(descriptor TypeDescriptor, input arrow.Array) erro return errutil.Wrapf(err, "TYPE_CONTRACT: JSON row %d is not valid canonical text", row) } if canonical != value { - return moerr.NewInternalErrorNoCtxf("TYPE_CONTRACT: JSON row %d is not canonical text", row) + return udferr.Newf("TYPE_CONTRACT: JSON row %d is not canonical text", row) } case types.T_binary, types.T_varbinary, types.T_blob: if descriptor.Width > 0 && int32(len(input.(*array.Binary).Value(row))) > descriptor.Width { - return moerr.NewInternalErrorNoCtxf("TYPE_CONTRACT: binary row %d exceeds width %d", row, descriptor.Width) + return udferr.Newf("TYPE_CONTRACT: binary row %d exceeds width %d", row, descriptor.Width) } case types.T_time: value := types.Time(input.(*array.Duration).Value(row)) if !types.IsMySQLTime(value) || !hasExactMicrosecondScale(int64(value), descriptor.Scale) { - return moerr.NewInternalErrorNoCtxf("TYPE_CONTRACT: TIME row %d is outside the declared SQL domain", row) + return udferr.Newf("TYPE_CONTRACT: TIME row %d is outside the declared SQL domain", row) } case types.T_decimal64, types.T_decimal128: precision := descriptor.Width @@ -1522,7 +1523,7 @@ func validateArrowValueDomain(descriptor TypeDescriptor, input arrow.Array) erro } } if !input.(*array.Decimal128).Value(row).FitsInPrecision(precision) { - return moerr.NewInternalErrorNoCtxf("TYPE_CONTRACT: decimal row %d exceeds precision %d", row, precision) + return udferr.Newf("TYPE_CONTRACT: decimal row %d exceeds precision %d", row, precision) } case types.T_array_float32: list := input.(*array.FixedSizeList) @@ -1530,7 +1531,7 @@ func validateArrowValueDomain(descriptor TypeDescriptor, input arrow.Array) erro start, end := list.ValueOffsets(row) for child := start; child < end; child++ { if values.IsNull(int(child)) { - return moerr.NewInternalErrorNoCtxf("TYPE_CONTRACT: vector child at row %d is null", row) + return udferr.Newf("TYPE_CONTRACT: vector child at row %d is null", row) } } case types.T_array_float64: @@ -1539,54 +1540,54 @@ func validateArrowValueDomain(descriptor TypeDescriptor, input arrow.Array) erro start, end := list.ValueOffsets(row) for child := start; child < end; child++ { if values.IsNull(int(child)) { - return moerr.NewInternalErrorNoCtxf("TYPE_CONTRACT: vector child at row %d is null", row) + return udferr.Newf("TYPE_CONTRACT: vector child at row %d is null", row) } } case types.T_date, types.T_datetime, types.T_timestamp: structValue := input.(*array.Struct) zeroField, valueField := structValue.Field(0), structValue.Field(1) if zeroField.IsNull(row) || valueField.IsNull(row) { - return moerr.NewInternalErrorNoCtxf("TYPE_CONTRACT: temporal row %d has a null child", row) + return udferr.Newf("TYPE_CONTRACT: temporal row %d has a null child", row) } zero := zeroField.(*array.Boolean).Value(row) switch types.T(descriptor.TypeID) { case types.T_date: value := valueField.(*array.Date32).Value(row) if zero && value != 0 { - return moerr.NewInternalErrorNoCtxf("TYPE_CONTRACT: DATE zero row %d has a non-zero placeholder", row) + return udferr.Newf("TYPE_CONTRACT: DATE zero row %d has a non-zero placeholder", row) } if !zero { date := types.DaysFromUnixEpochToDate(int32(value)) year, month, day, _ := date.Calendar(true) if !types.ValidDate(year, month, day) { - return moerr.NewInternalErrorNoCtxf("TYPE_CONTRACT: DATE row %d is outside the SQL domain", row) + return udferr.Newf("TYPE_CONTRACT: DATE row %d is outside the SQL domain", row) } } case types.T_datetime: value := valueField.(*array.Timestamp).Value(row) if zero && value != 0 { - return moerr.NewInternalErrorNoCtxf("TYPE_CONTRACT: DATETIME zero row %d has a non-zero placeholder", row) + return udferr.Newf("TYPE_CONTRACT: DATETIME zero row %d has a non-zero placeholder", row) } if !zero { absolute, ok := addUnixEpoch(int64(value)) if !ok || !hasExactMicrosecondScale(absolute, descriptor.Scale) { - return moerr.NewInternalErrorNoCtxf("TYPE_CONTRACT: DATETIME row %d is outside the declared SQL domain", row) + return udferr.Newf("TYPE_CONTRACT: DATETIME row %d is outside the declared SQL domain", row) } datetime := types.Datetime(absolute) year, month, day, _ := datetime.ToDate().Calendar(true) if !types.ValidDatetime(year, month, day) { - return moerr.NewInternalErrorNoCtxf("TYPE_CONTRACT: DATETIME row %d is outside the SQL domain", row) + return udferr.Newf("TYPE_CONTRACT: DATETIME row %d is outside the SQL domain", row) } } case types.T_timestamp: value := valueField.(*array.Timestamp).Value(row) if zero && value != 0 { - return moerr.NewInternalErrorNoCtxf("TYPE_CONTRACT: TIMESTAMP zero row %d has a non-zero placeholder", row) + return udferr.Newf("TYPE_CONTRACT: TIMESTAMP zero row %d has a non-zero placeholder", row) } if !zero { absolute, ok := addUnixEpoch(int64(value)) if !ok || absolute < int64(types.TimestampMinValue) || absolute > int64(types.TimestampMaxValue) || !hasExactMicrosecondScale(absolute, descriptor.Scale) { - return moerr.NewInternalErrorNoCtxf("TYPE_CONTRACT: TIMESTAMP row %d is outside the declared SQL domain", row) + return udferr.Newf("TYPE_CONTRACT: TIMESTAMP row %d is outside the declared SQL domain", row) } } } @@ -1616,7 +1617,7 @@ func hasExactMicrosecondScale(value int64, scale int32) bool { func decimalFromArray(value decimal128.Num, narrow bool) (any, error) { high, low := uint64(value.HighBits()), value.LowBits() if narrow && !decimal128FitsDecimal64(high, low) { - return nil, moerr.NewInternalErrorNoCtxf("TYPE_CONTRACT: Decimal128 result does not fit Decimal64") + return nil, udferr.Newf("TYPE_CONTRACT: Decimal128 result does not fit Decimal64") } if narrow { return types.Decimal64(low), nil diff --git a/pkg/udf/python/artifact.go b/pkg/udf/python/artifact.go index c0175ae01d147..2c40c9a95edb1 100644 --- a/pkg/udf/python/artifact.go +++ b/pkg/udf/python/artifact.go @@ -17,10 +17,12 @@ package python import ( "context" "fmt" - "github.com/matrixorigin/matrixone/pkg/util/errutil" "strings" "unicode/utf8" + "github.com/matrixorigin/matrixone/pkg/udf/udferr" + "github.com/matrixorigin/matrixone/pkg/util/errutil" + "github.com/matrixorigin/matrixone/pkg/common/moerr" "github.com/matrixorigin/matrixone/pkg/defines" "github.com/matrixorigin/matrixone/pkg/fileservice" @@ -69,7 +71,7 @@ var _ ArtifactStore = (*FileArtifactStore)(nil) func NewFileArtifactStore(fs fileservice.FileService, maxSize int64) (*FileArtifactStore, error) { if fs == nil { - return nil, moerr.NewInternalErrorNoCtxf("RESOURCE_UNAVAILABLE: Python artifact store has no FileService") + return nil, udferr.Newf("RESOURCE_UNAVAILABLE: Python artifact store has no FileService") } shared, err := fileservice.Get[fileservice.FileService](fs, defines.SharedFileServiceName) if err != nil { @@ -79,7 +81,7 @@ func NewFileArtifactStore(fs fileservice.FileService, maxSize int64) (*FileArtif maxSize = DefaultMaxArtifactBytes } if maxSize > DefaultMaxArtifactBytes { - return nil, moerr.NewInternalErrorNoCtxf("UNSUPPORTED_ROUTINE_VERSION: Python artifact limit %d exceeds the current contract limit %d", maxSize, DefaultMaxArtifactBytes) + return nil, udferr.Newf("UNSUPPORTED_ROUTINE_VERSION: Python artifact limit %d exceeds the current contract limit %d", maxSize, DefaultMaxArtifactBytes) } return &FileArtifactStore{fs: shared, maxSize: maxSize}, nil } @@ -119,7 +121,7 @@ func (s *FileArtifactStore) Publish(ctx context.Context, accountID uint64, handl return "", errutil.Wrapf(resolveErr, "python artifact publish %s found an invalid existing object", digest) } if existing != source { - return "", moerr.NewInternalErrorNoCtxf("python artifact publish %s encountered a digest collision", digest) + return "", udferr.Newf("python artifact publish %s encountered a digest collision", digest) } return digest, nil } @@ -132,10 +134,10 @@ func (s *FileArtifactStore) Resolve(ctx context.Context, accountID uint64, handl ctx = context.Background() } if strings.TrimSpace(handler) == "" { - return "", moerr.NewInternalErrorNoCtxf("UNSUPPORTED_ROUTINE_VERSION: Python artifact has no handler") + return "", udferr.Newf("UNSUPPORTED_ROUTINE_VERSION: Python artifact has no handler") } if !udf.IsSHA256Digest(digest) { - return "", moerr.NewInternalErrorNoCtxf("UNSUPPORTED_ROUTINE_VERSION: Python artifact digest is invalid") + return "", udferr.Newf("UNSUPPORTED_ROUTINE_VERSION: Python artifact digest is invalid") } path := artifactPath(accountID, digest) entry, err := s.fs.StatFile(ctx, path) @@ -143,7 +145,7 @@ func (s *FileArtifactStore) Resolve(ctx context.Context, accountID uint64, handl return "", errutil.Wrapf(err, "RESOURCE_UNAVAILABLE: Python artifact %s is unavailable", digest) } if entry == nil || entry.IsDir || entry.Size <= 0 || entry.Size > s.maxSize { - return "", moerr.NewInternalErrorNoCtxf("UNSUPPORTED_ROUTINE_VERSION: Python artifact %s has invalid size", digest) + return "", udferr.Newf("UNSUPPORTED_ROUTINE_VERSION: Python artifact %s has invalid size", digest) } data := make([]byte, entry.Size) readVector := &fileservice.IOVector{ @@ -154,43 +156,43 @@ func (s *FileArtifactStore) Resolve(ctx context.Context, accountID uint64, handl return "", errutil.Wrapf(err, "RESOURCE_UNAVAILABLE: Python artifact %s cannot be read", digest) } if len(readVector.Entries) != 1 || int64(len(readVector.Entries[0].Data)) != entry.Size { - return "", moerr.NewInternalErrorNoCtxf("UNSUPPORTED_ROUTINE_VERSION: Python artifact %s changed size while reading", digest) + return "", udferr.Newf("UNSUPPORTED_ROUTINE_VERSION: Python artifact %s changed size while reading", digest) } source := string(readVector.Entries[0].Data) if !utf8.ValidString(source) { - return "", moerr.NewInternalErrorNoCtxf("UNSUPPORTED_ROUTINE_VERSION: Python artifact %s is not valid UTF-8", digest) + return "", udferr.Newf("UNSUPPORTED_ROUTINE_VERSION: Python artifact %s is not valid UTF-8", digest) } if udf.PythonInlineArtifactDigest(handler, source) != digest { - return "", moerr.NewInternalErrorNoCtxf("UNSUPPORTED_ROUTINE_VERSION: Python artifact %s content digest mismatch", digest) + return "", udferr.Newf("UNSUPPORTED_ROUTINE_VERSION: Python artifact %s content digest mismatch", digest) } return source, nil } func validateArtifactInput(accountID uint64, handler, source string, maxSize int64) (string, error) { if maxSize <= 0 { - return "", moerr.NewInternalErrorNoCtxf("RESOURCE_UNAVAILABLE: Python artifact store has an invalid size limit") + return "", udferr.Newf("RESOURCE_UNAVAILABLE: Python artifact store has an invalid size limit") } if strings.TrimSpace(handler) == "" { - return "", moerr.NewInternalErrorNoCtxf("UNSUPPORTED_ROUTINE_VERSION: Python artifact has no handler") + return "", udferr.Newf("UNSUPPORTED_ROUTINE_VERSION: Python artifact has no handler") } if source == "" { - return "", moerr.NewInternalErrorNoCtxf("UNSUPPORTED_ROUTINE_VERSION: Python artifact source is empty") + return "", udferr.Newf("UNSUPPORTED_ROUTINE_VERSION: Python artifact source is empty") } if !utf8.ValidString(source) { - return "", moerr.NewInternalErrorNoCtxf("UNSUPPORTED_ROUTINE_VERSION: Python artifact source is not valid UTF-8") + return "", udferr.Newf("UNSUPPORTED_ROUTINE_VERSION: Python artifact source is not valid UTF-8") } if int64(len([]byte(source))) > maxSize { - return "", moerr.NewInternalErrorNoCtxf("RESOURCE_EXHAUSTED: Python artifact exceeds %d bytes", maxSize) + return "", udferr.Newf("RESOURCE_EXHAUSTED: Python artifact exceeds %d bytes", maxSize) } return udf.PythonInlineArtifactDigest(handler, source), nil } func (s *FileArtifactStore) validate() error { if s == nil || s.fs == nil { - return moerr.NewInternalErrorNoCtxf("RESOURCE_UNAVAILABLE: Python artifact store has no FileService") + return udferr.Newf("RESOURCE_UNAVAILABLE: Python artifact store has no FileService") } if s.maxSize <= 0 { - return moerr.NewInternalErrorNoCtxf("RESOURCE_UNAVAILABLE: Python artifact store has an invalid size limit") + return udferr.Newf("RESOURCE_UNAVAILABLE: Python artifact store has an invalid size limit") } return nil } diff --git a/pkg/udf/python/definition.go b/pkg/udf/python/definition.go index 3a2db3e2bbc65..a2617025e597e 100644 --- a/pkg/udf/python/definition.go +++ b/pkg/udf/python/definition.go @@ -18,7 +18,8 @@ import ( "crypto/sha256" "encoding/hex" "encoding/json" - "github.com/matrixorigin/matrixone/pkg/common/moerr" + + "github.com/matrixorigin/matrixone/pkg/udf/udferr" "github.com/matrixorigin/matrixone/pkg/util/errutil" ) @@ -56,7 +57,7 @@ func DefinitionFingerprint( mode == "" || nullPolicy == "" || abiContract == "" || adapterVersion == "" || sdkVersion == "" || artifactDigest == "" || environmentDigest == "" { - return "", moerr.NewInternalErrorNoCtxf("python definition fingerprint has incomplete metadata") + return "", udferr.Newf("python definition fingerprint has incomplete metadata") } for index, descriptor := range args { if err := descriptor.Validate(); err != nil { diff --git a/pkg/udf/python/gateway.go b/pkg/udf/python/gateway.go index fddb53d318c23..861885bc9dc7e 100644 --- a/pkg/udf/python/gateway.go +++ b/pkg/udf/python/gateway.go @@ -21,8 +21,6 @@ import ( "encoding/hex" "encoding/json" "errors" - "github.com/matrixorigin/matrixone/pkg/common/moerr" - "github.com/matrixorigin/matrixone/pkg/util/errutil" "io" "strconv" "strings" @@ -30,6 +28,9 @@ import ( "time" "unicode/utf8" + "github.com/matrixorigin/matrixone/pkg/udf/udferr" + "github.com/matrixorigin/matrixone/pkg/util/errutil" + "github.com/apache/arrow-go/v18/arrow/flight/gen/flight" "github.com/matrixorigin/matrixone/pkg/common/mpool" "github.com/matrixorigin/matrixone/pkg/container/types" @@ -60,7 +61,7 @@ const ( statusOK = "OK" ) -var errGatewayClosed = moerr.NewInternalErrorNoCtx("python udf gateway is closed") +var errGatewayClosed = udferr.New("python udf gateway is closed") var _ udf.RuntimeDefinitionValidator = (*Gateway)(nil) var _ udf.RuntimeStatusProvider = (*Gateway)(nil) @@ -111,7 +112,7 @@ func NewGateway(cfg ClientConfig) (*Gateway, error) { // rejected at this boundary. func NewGatewayWithArtifactStore(cfg ClientConfig, resolver ArtifactResolver) (*Gateway, error) { if resolver == nil { - return nil, moerr.NewInternalErrorNoCtxf("RESOURCE_UNAVAILABLE: Python artifact resolver is not configured") + return nil, udferr.Newf("RESOURCE_UNAVAILABLE: Python artifact resolver is not configured") } return newGateway(cfg, resolver, false) } @@ -175,13 +176,13 @@ func (g *Gateway) CheckLanguageReady(ctx context.Context, language string) error ctx = context.Background() } if language != udf.LanguagePython { - return moerr.NewInternalErrorNoCtxf("python udf: unsupported readiness language %q", language) + return udferr.Newf("python udf: unsupported readiness language %q", language) } if !g.cfg.Enabled { - return moerr.NewInternalErrorNoCtxf("RESOURCE_UNAVAILABLE: Python UDF runtime is disabled") + return udferr.Newf("RESOURCE_UNAVAILABLE: Python UDF runtime is disabled") } if !g.cfg.AllowUnisolated { - return moerr.NewInternalErrorNoCtxf("RESOURCE_UNAVAILABLE: Python UDF runtime requires explicit unisolated opt-in") + return udferr.Newf("RESOURCE_UNAVAILABLE: Python UDF runtime requires explicit unisolated opt-in") } requestCtx, release := g.withLifecycleContext(ctx) defer release() @@ -304,10 +305,10 @@ func (g *Gateway) ValidateDefinition(ctx context.Context, definition *udf.Routin return errGatewayClosed } if !g.cfg.Enabled { - return moerr.NewInternalErrorNoCtxf("RESOURCE_UNAVAILABLE: Python UDF runtime is disabled") + return udferr.Newf("RESOURCE_UNAVAILABLE: Python UDF runtime is disabled") } if !g.cfg.AllowUnisolated { - return moerr.NewInternalErrorNoCtxf("RESOURCE_UNAVAILABLE: Python UDF runtime requires explicit unisolated opt-in") + return udferr.Newf("RESOURCE_UNAVAILABLE: Python UDF runtime requires explicit unisolated opt-in") } requestCtx, release := g.withLifecycleContext(ctx) defer release() @@ -317,38 +318,38 @@ func (g *Gateway) ValidateDefinition(ctx context.Context, definition *udf.Routin defer cancel() } if definition == nil { - return moerr.NewInternalErrorNoCtxf("UNSUPPORTED_ROUTINE_VERSION: nil Python routine definition") + return udferr.Newf("UNSUPPORTED_ROUTINE_VERSION: nil Python routine definition") } if definition.Language != udf.LanguagePython { - return moerr.NewInternalErrorNoCtxf("UNSUPPORTED_ROUTINE_VERSION: unsupported UDF language %q", definition.Language) + return udferr.Newf("UNSUPPORTED_ROUTINE_VERSION: unsupported UDF language %q", definition.Language) } if strings.TrimSpace(definition.Handler) == "" { - return moerr.NewInternalErrorNoCtxf("UNSUPPORTED_ROUTINE_VERSION: Python handler is required") + return udferr.Newf("UNSUPPORTED_ROUTINE_VERSION: Python handler is required") } if strings.Contains(definition.Handler, ":") { - return moerr.NewInternalErrorNoCtxf("UNSUPPORTED_ROUTINE_VERSION: Python external handler import requires the immutable artifact catalog") + return udferr.Newf("UNSUPPORTED_ROUTINE_VERSION: Python external handler import requires the immutable artifact catalog") } if definition.DefinitionSchemaVersion != udf.PythonDefinitionSchemaVersion || definition.ABIContract != udf.PythonABIContract || definition.AdapterVersion != udf.PythonAdapterVersion || definition.SDKVersion != udf.PythonSDKVersion { - return moerr.NewInternalErrorNoCtxf("UNSUPPORTED_ROUTINE_VERSION: Python definition contract is not supported") + return udferr.Newf("UNSUPPORTED_ROUTINE_VERSION: Python definition contract is not supported") } if definition.Mode != ModeScalar && definition.Mode != ModeVector { - return moerr.NewInternalErrorNoCtxf("UNSUPPORTED_ROUTINE_VERSION: unsupported Python mode %q", definition.Mode) + return udferr.Newf("UNSUPPORTED_ROUTINE_VERSION: unsupported Python mode %q", definition.Mode) } if definition.NullPolicy != NullCallHandler && definition.NullPolicy != NullReturnNull { - return moerr.NewInternalErrorNoCtxf("UNSUPPORTED_ROUTINE_VERSION: unsupported Python NULL policy %q", definition.NullPolicy) + return udferr.Newf("UNSUPPORTED_ROUTINE_VERSION: unsupported Python NULL policy %q", definition.NullPolicy) } if !udf.IsSHA256Digest(definition.ArtifactDigest) || !udf.IsSHA256Digest(definition.EnvironmentDigest) { - return moerr.NewInternalErrorNoCtxf("UNSUPPORTED_ROUTINE_VERSION: Python definition digest is invalid") + return udferr.Newf("UNSUPPORTED_ROUTINE_VERSION: Python definition digest is invalid") } currentEnvironment, err := udf.PythonEnvironmentDigest() if err != nil || currentEnvironment != definition.EnvironmentDigest { - return moerr.NewInternalErrorNoCtxf("UNSUPPORTED_ROUTINE_VERSION: Python environment digest does not match the current contract") + return udferr.Newf("UNSUPPORTED_ROUTINE_VERSION: Python environment digest does not match the current contract") } if !udf.IsSHA256Digest(definition.DefinitionFingerprint) { - return moerr.NewInternalErrorNoCtxf("UNSUPPORTED_ROUTINE_VERSION: Python definition fingerprint is invalid") + return udferr.Newf("UNSUPPORTED_ROUTINE_VERSION: Python definition fingerprint is invalid") } args := make([]TypeDescriptor, len(definition.Args)) for index, typ := range definition.Args { @@ -372,7 +373,7 @@ func (g *Gateway) ValidateDefinition(ctx context.Context, definition *udf.Routin return errutil.Wrapf(err, "UNSUPPORTED_ROUTINE_VERSION: Python definition fingerprint cannot be computed") } if definition.DefinitionFingerprint != expectedFingerprint { - return moerr.NewInternalErrorNoCtxf("UNSUPPORTED_ROUTINE_VERSION: Python definition fingerprint does not match the typed definition") + return udferr.Newf("UNSUPPORTED_ROUTINE_VERSION: Python definition fingerprint does not match the typed definition") } source := definition.Source @@ -384,20 +385,20 @@ func (g *Gateway) ValidateDefinition(ctx context.Context, definition *udf.Routin return resolveErr } if source != "" && source != resolved { - return moerr.NewInternalErrorNoCtxf("UNSUPPORTED_ROUTINE_VERSION: Python artifact source does not match the immutable artifact") + return udferr.Newf("UNSUPPORTED_ROUTINE_VERSION: Python artifact source does not match the immutable artifact") } source = resolved } else if !g.allowInlineSource { - return moerr.NewInternalErrorNoCtxf("RESOURCE_UNAVAILABLE: Python artifact resolver is not configured") + return udferr.Newf("RESOURCE_UNAVAILABLE: Python artifact resolver is not configured") } if source == "" || !utf8.ValidString(source) { - return moerr.NewInternalErrorNoCtxf("UNSUPPORTED_ROUTINE_VERSION: Python artifact source is empty or not valid UTF-8") + return udferr.Newf("UNSUPPORTED_ROUTINE_VERSION: Python artifact source is empty or not valid UTF-8") } if int64(len([]byte(source))) > DefaultMaxArtifactBytes { - return moerr.NewInternalErrorNoCtxf("RESOURCE_EXHAUSTED: Python artifact exceeds %d bytes", DefaultMaxArtifactBytes) + return udferr.Newf("RESOURCE_EXHAUSTED: Python artifact exceeds %d bytes", DefaultMaxArtifactBytes) } if udf.PythonInlineArtifactDigest(definition.Handler, source) != definition.ArtifactDigest { - return moerr.NewInternalErrorNoCtxf("UNSUPPORTED_ROUTINE_VERSION: Python artifact digest does not match the immutable source") + return udferr.Newf("UNSUPPORTED_ROUTINE_VERSION: Python artifact digest does not match the immutable source") } client, err := g.flightClient() @@ -449,7 +450,7 @@ func (g *Gateway) ValidateDefinition(ctx context.Context, definition *udf.Routin } if result == nil { g.invalidateCapabilities(capability.generation) - return moerr.NewInternalErrorNoCtxf("python udf: empty definition validation response") + return udferr.Newf("python udf: empty definition validation response") } var response definitionValidationResponse decoder := json.NewDecoder(bytes.NewReader(result.Body)) @@ -461,30 +462,30 @@ func (g *Gateway) ValidateDefinition(ctx context.Context, definition *udf.Routin var trailing any if err := decoder.Decode(&trailing); err != io.EOF { g.invalidateCapabilities(capability.generation) - return moerr.NewInternalErrorNoCtxf("python udf: definition validation response has trailing JSON") + return udferr.Newf("python udf: definition validation response has trailing JSON") } if response.Status == "ERROR" { if _, err := stream.Recv(); err != io.EOF { g.invalidateCapabilities(capability.generation) if err == nil { - return moerr.NewInternalErrorNoCtxf("python udf: definition validation returned multiple results") + return udferr.Newf("python udf: definition validation returned multiple results") } return errutil.Wrapf(err, "python udf: definition validation stream") } if response.Reason == "" { - return moerr.NewInternalErrorNoCtxf("USER_CODE: Python definition validation failed") + return udferr.Newf("USER_CODE: Python definition validation failed") } - return moerr.NewInternalErrorNoCtx(response.Reason) + return udferr.New(response.Reason) } if response.Status != statusOK || response.ArtifactDigest != definition.ArtifactDigest || response.DefinitionFingerprint != definition.DefinitionFingerprint { g.invalidateCapabilities(capability.generation) - return moerr.NewInternalErrorNoCtxf("UNSUPPORTED_ROUTINE_VERSION: Python definition validation returned an invalid result") + return udferr.Newf("UNSUPPORTED_ROUTINE_VERSION: Python definition validation returned an invalid result") } if _, err := stream.Recv(); err != io.EOF { g.invalidateCapabilities(capability.generation) if err == nil { - return moerr.NewInternalErrorNoCtxf("python udf: definition validation returned multiple results") + return udferr.Newf("python udf: definition validation returned multiple results") } return errutil.Wrapf(err, "python udf: definition validation stream") } @@ -616,22 +617,22 @@ func (g *Gateway) Execute(ctx context.Context, invocation *udf.Invocation, resul return errGatewayClosed } if !g.cfg.Enabled { - return moerr.NewInternalErrorNoCtxf("RESOURCE_UNAVAILABLE: Python UDF runtime is disabled") + return udferr.Newf("RESOURCE_UNAVAILABLE: Python UDF runtime is disabled") } if !g.cfg.AllowUnisolated { - return moerr.NewInternalErrorNoCtxf("RESOURCE_UNAVAILABLE: Python UDF runtime requires explicit unisolated opt-in") + return udferr.Newf("RESOURCE_UNAVAILABLE: Python UDF runtime requires explicit unisolated opt-in") } requestCtx, release := g.withLifecycleContext(ctx) defer release() streamCtx, cancel := context.WithTimeout(requestCtx, g.cfg.RequestTimeout) defer cancel() if invocation == nil || result == nil || mp == nil { - return moerr.NewInternalErrorNoCtxf("python udf: nil invocation, result, or memory pool") + return udferr.Newf("python udf: nil invocation, result, or memory pool") } execution := *invocation if g.artifactResolver != nil { if execution.Source != "" { - return moerr.NewInternalErrorNoCtxf("UNSUPPORTED_ROUTINE_VERSION: typed Python invocation contains source; use its immutable artifact digest") + return udferr.Newf("UNSUPPORTED_ROUTINE_VERSION: typed Python invocation contains source; use its immutable artifact digest") } // Validate the identity, semantic contract, descriptors, and resource // shape before resolving an account-scoped artifact. Resolution is a @@ -651,7 +652,7 @@ func (g *Gateway) Execute(ctx context.Context, invocation *udf.Invocation, resul return err } } else if !g.allowInlineSource && execution.Source == "" { - return moerr.NewInternalErrorNoCtxf("RESOURCE_UNAVAILABLE: Python artifact resolver is not configured") + return udferr.Newf("RESOURCE_UNAVAILABLE: Python artifact resolver is not configured") } if err := validateInvocation(&execution); err != nil { return err @@ -713,7 +714,7 @@ func (g *Gateway) Execute(ctx context.Context, invocation *udf.Invocation, resul // invalidation below forces a fresh handshake on the next call. workerLeaseEpoch := capability.response.LeaseEpoch if workerLeaseEpoch == 0 { - return moerr.NewInternalErrorNoCtxf("python udf: capability handshake did not return a worker lease epoch") + return udferr.Newf("python udf: capability handshake did not return a worker lease epoch") } execution.Tuple.LeaseEpoch = workerLeaseEpoch if err := validateInvocation(&execution); err != nil { @@ -798,7 +799,7 @@ func (g *Gateway) Execute(ctx context.Context, invocation *udf.Invocation, resul return err } if len(batch.Frames) != 2 { - return moerr.NewInternalErrorNoCtxf("python udf: input batch contains %d Arrow frames", len(batch.Frames)) + return udferr.Newf("python udf: input batch contains %d Arrow frames", len(batch.Frames)) } if batchIndex == 0 { inputSchema = append([]byte(nil), batch.Frames[0].Header...) @@ -806,7 +807,7 @@ func (g *Gateway) Execute(ctx context.Context, invocation *udf.Invocation, resul return err } } else if !bytes.Equal(inputSchema, batch.Frames[0].Header) { - return moerr.NewInternalErrorNoCtxf("python udf: input batches do not share one Arrow schema") + return udferr.Newf("python udf: input batches do not share one Arrow schema") } sequenceNumber := batchIndex + 1 if err := sequence.AcceptInput(sequenceNumber); err != nil { @@ -852,7 +853,7 @@ func (g *Gateway) Execute(ctx context.Context, invocation *udf.Invocation, resul } } if !inputEnded || lastSequence != batchIndex { - return moerr.NewInternalErrorNoCtxf("python udf: input stream did not reach EndInput") + return udferr.Newf("python udf: input stream did not reach EndInput") } // Close the local Arrow writer before accepting the worker's terminal // Finish. If finalizing the local input owner fails, the admission cleanup @@ -887,7 +888,7 @@ type invocationAdmission struct { func (g *Gateway) admitInvocation(invocation *udf.Invocation) (*invocationAdmission, error) { if invocation == nil { - return nil, moerr.NewInternalErrorNoCtxf("python udf: cannot admit a nil invocation") + return nil, udferr.Newf("python udf: cannot admit a nil invocation") } if err := invocation.Tuple.Validate(); err != nil { return nil, err @@ -924,7 +925,7 @@ func (g *Gateway) admitInvocation(invocation *udf.Invocation) (*invocationAdmiss if ledger == nil || active == nil { g.releaseGroupClaim(groupKey, invocation.Tuple.GroupEpoch) g.mu.Unlock() - return nil, moerr.NewInternalErrorNoCtxf("python udf: admission state is not initialized") + return nil, udferr.Newf("python udf: admission state is not initialized") } // Expire is intentionally limited to tombstones by TerminalLedger. An // active invocation can leave this path only through finish/abandon. @@ -955,7 +956,7 @@ func (g *Gateway) admitInvocation(invocation *udf.Invocation) (*invocationAdmiss credit.ReleaseUnused() g.releaseGroupClaim(groupKey, invocation.Tuple.GroupEpoch) g.mu.Unlock() - return nil, moerr.NewInternalErrorNoCtxf("RESOURCE_EXHAUSTED: Python UDF active invocation slots are full") + return nil, udferr.Newf("RESOURCE_EXHAUSTED: Python UDF active invocation slots are full") } var releaseOnce sync.Once @@ -1309,7 +1310,7 @@ func (g *Gateway) ensureCapabilitySnapshot(ctx context.Context, client flight.Fl if g.closed { err = errGatewayClosed } else if p.generation != g.capabilityGeneration { - err = moerr.NewInternalErrorNoCtx("python udf: capability probe invalidated") + err = udferr.New("python udf: capability probe invalidated") } else if ctx.Err() != nil { err = ctx.Err() } @@ -1339,7 +1340,7 @@ func readCapabilities(ctx context.Context, client flight.FlightServiceClient) (* return nil, errutil.Wrapf(err, "python udf: capability handshake response") } if result == nil { - return nil, moerr.NewInternalErrorNoCtxf("python udf: empty capability handshake response") + return nil, udferr.Newf("python udf: empty capability handshake response") } var response capabilityResponse decoder := json.NewDecoder(bytes.NewReader(result.Body)) @@ -1349,14 +1350,14 @@ func readCapabilities(ctx context.Context, client flight.FlightServiceClient) (* } var trailing any if err := decoder.Decode(&trailing); err != io.EOF { - return nil, moerr.NewInternalErrorNoCtxf("python udf: capability handshake has trailing JSON") + return nil, udferr.Newf("python udf: capability handshake has trailing JSON") } if err := validateCapabilities(response); err != nil { return nil, err } if _, err := stream.Recv(); err != io.EOF { if err == nil { - return nil, moerr.NewInternalErrorNoCtxf("python udf: capability handshake returned multiple results") + return nil, udferr.Newf("python udf: capability handshake returned multiple results") } return nil, errutil.Wrapf(err, "python udf: capability handshake stream") } @@ -1399,11 +1400,11 @@ func validateCapabilities(response capabilityResponse) error { response.MaxAccountHandlerProcesses != DefaultWorkerMaxAccountHandlers || response.MaxOwnerHandlerProcesses != DefaultWorkerMaxOwnerHandlers || response.LeaseEpoch == 0 { - return moerr.NewInternalErrorNoCtxf("python udf: worker capability contract does not match the current contract") + return udferr.Newf("python udf: worker capability contract does not match the current contract") } if !sameStrings(response.Modes, []string{ModeScalar, ModeVector}) || !sameStrings(response.NullPolicies, []string{NullCallHandler, NullReturnNull}) { - return moerr.NewInternalErrorNoCtxf("python udf: worker capability set does not match the current contract") + return udferr.Newf("python udf: worker capability set does not match the current contract") } return nil } @@ -1428,20 +1429,12 @@ func sameStrings(actual, expected []string) bool { return true } -func encodeInputBatch(inputs []*vector.Vector, args []types.Type, start, remaining, maxBytes, maxRows int64) (encodedRecordBatch, error) { - encoder, err := newInputBatchEncoder(inputs, args) - if err != nil { - return encodedRecordBatch{}, err - } - return encodeInputBatchWithEncoder(encoder, start, remaining, maxBytes, maxRows) -} - func encodeInputBatchWithEncoder(encoder *inputBatchEncoder, start, remaining, maxBytes, maxRows int64) (encodedRecordBatch, error) { if start < 0 || remaining <= 0 { - return encodedRecordBatch{}, moerr.NewInternalErrorNoCtxf("invalid Python UDF input batch range") + return encodedRecordBatch{}, udferr.Newf("invalid Python UDF input batch range") } if maxRows <= 0 { - return encodedRecordBatch{}, moerr.NewInternalErrorNoCtxf("invalid Python UDF max batch rows") + return encodedRecordBatch{}, udferr.Newf("invalid Python UDF max batch rows") } if remaining > maxRows { remaining = maxRows @@ -1452,7 +1445,7 @@ func encodeInputBatchWithEncoder(encoder *inputBatchEncoder, start, remaining, m // slice keeps the same backing buffers, and every candidate is still encoded // and checked against maxBytes before publication. if encoder == nil { - return encodedRecordBatch{}, moerr.NewInternalErrorNoCtxf("invalid Python UDF input encoder") + return encodedRecordBatch{}, udferr.Newf("invalid Python UDF input encoder") } if encoder.fixedWidth { record, _, err := encoder.build(int(start), int(remaining)) @@ -1562,17 +1555,17 @@ func (g *Gateway) receiveResultBatch( data, err := stream.Recv() if err != nil { if err == io.EOF { - return 0, moerr.NewInternalErrorNoCtxf("python udf: exchange ended before result sequence %d", sequenceNumber) + return 0, udferr.Newf("python udf: exchange ended before result sequence %d", sequenceNumber) } return 0, errutil.Wrapf(err, "python udf: receive result") } if data == nil || (len(data.AppMetadata) == 0 && len(data.DataHeader) == 0) { - return 0, moerr.NewInternalErrorNoCtxf("python udf: empty Flight result") + return 0, udferr.Newf("python udf: empty Flight result") } if len(data.DataHeader) > 0 { if *schemaFrame == nil { if len(data.DataBody) != 0 { - return 0, moerr.NewInternalErrorNoCtxf("python udf: result schema contains a body") + return 0, udferr.Newf("python udf: result schema contains a body") } *schemaFrame = &ArrowFrame{Header: append([]byte(nil), data.DataHeader...)} if len(data.AppMetadata) != 0 { @@ -1584,17 +1577,17 @@ func (g *Gateway) receiveResultBatch( continue } if !*schemaReady { - return 0, moerr.NewInternalErrorNoCtxf("python udf: result schema metadata was not received") + return 0, udferr.Newf("python udf: result schema metadata was not received") } control, err := protocol.UnmarshalControl(data.AppMetadata) if err != nil { return 0, err } if control.Tuple != tuple || control.Kind != "ResultBatch" || control.Sequence != sequenceNumber { - return 0, moerr.NewInternalErrorNoCtxf("python udf: unexpected result sequence %d", control.Sequence) + return 0, udferr.Newf("python udf: unexpected result sequence %d", control.Sequence) } if !inputConsumed { - return 0, moerr.NewInternalErrorNoCtxf("python udf: result sequence %d arrived before InputConsumed", sequenceNumber) + return 0, udferr.Newf("python udf: result sequence %d arrived before InputConsumed", sequenceNumber) } if err := sequence.AcceptResult(control.Sequence); err != nil { return 0, err @@ -1618,11 +1611,11 @@ func (g *Gateway) receiveResultBatch( decodedRows := decoded.NumRows() if decoded.NumCols() != 1 || decodedRows <= 0 || decodedRows != expectedRows { decoded.Release() - return 0, moerr.NewInternalErrorNoCtxf("python udf: result batch row count %d, expected %d", decodedRows, expectedRows) + return 0, udferr.Newf("python udf: result batch row count %d, expected %d", decodedRows, expectedRows) } if decoded.Schema().Field(0).Name != "result" { decoded.Release() - return 0, moerr.NewInternalErrorNoCtxf("python udf: result field has unexpected name %q", decoded.Schema().Field(0).Name) + return 0, udferr.Newf("python udf: result field has unexpected name %q", decoded.Schema().Field(0).Name) } if err := returnDescriptor.ValidateField(decoded.Schema().Field(0)); err != nil { decoded.Release() @@ -1634,7 +1627,7 @@ func (g *Gateway) receiveResultBatch( } if limit := g.cfg.MaxInvocationResultBytes; limit > 0 && result.GetResultVector() != nil && int64(result.GetResultVector().Size()) > limit { decoded.Release() - return 0, moerr.NewInternalErrorNoCtxf("RESOURCE_EXHAUSTED: Python UDF invocation result exceeds %d bytes", limit) + return 0, udferr.Newf("RESOURCE_EXHAUSTED: Python UDF invocation result exceeds %d bytes", limit) } decoded.Release() if err := g.action(ctx, client, "AcknowledgeResults", protocol.Control{Kind: "AcknowledgeResults", Tuple: tuple, AckSequence: control.Sequence}); err != nil { @@ -1646,40 +1639,40 @@ func (g *Gateway) receiveResultBatch( return int(expectedRows), nil } if len(data.DataBody) != 0 { - return 0, moerr.NewInternalErrorNoCtxf("python udf: control frame contains an Arrow body") + return 0, udferr.Newf("python udf: control frame contains an Arrow body") } control, err := protocol.UnmarshalControl(data.AppMetadata) if err != nil { return 0, err } if control.Tuple != tuple { - return 0, moerr.NewInternalErrorNoCtxf("python udf: result tuple changed") + return 0, udferr.Newf("python udf: result tuple changed") } switch control.Kind { case "ResultSchema": if *schemaFrame == nil || *schemaReady { - return 0, moerr.NewInternalErrorNoCtxf("python udf: invalid result schema metadata") + return 0, udferr.Newf("python udf: invalid result schema metadata") } *schemaReady = true continue case "InputConsumed": if control.Sequence != sequenceNumber { - return 0, moerr.NewInternalErrorNoCtxf("python udf: input sequence %d was consumed while waiting for result %d", control.Sequence, sequenceNumber) + return 0, udferr.Newf("python udf: input sequence %d was consumed while waiting for result %d", control.Sequence, sequenceNumber) } if control.ReleasedBatches != 1 || control.ReleasedBytes <= 0 || control.ReleasedBytes > g.cfg.MaxBatchBytes { - return 0, moerr.NewInternalErrorNoCtxf("python udf: input sequence %d released invalid bytes/batches (%d/%d)", sequenceNumber, control.ReleasedBytes, control.ReleasedBatches) + return 0, udferr.Newf("python udf: input sequence %d released invalid bytes/batches (%d/%d)", sequenceNumber, control.ReleasedBytes, control.ReleasedBatches) } if inputConsumed { - return 0, moerr.NewInternalErrorNoCtxf("python udf: input sequence %d was consumed more than once", sequenceNumber) + return 0, udferr.Newf("python udf: input sequence %d was consumed more than once", sequenceNumber) } inputConsumed = true continue case "Error": - return 0, moerr.NewInternalErrorNoCtxf("python udf: worker error: %s", control.Reason) + return 0, udferr.Newf("python udf: worker error: %s", control.Reason) case "Finish": - return 0, moerr.NewInternalErrorNoCtxf("python udf: Finish arrived before result sequence %d", sequenceNumber) + return 0, udferr.Newf("python udf: Finish arrived before result sequence %d", sequenceNumber) default: - return 0, moerr.NewInternalErrorNoCtxf("python udf: unexpected control %q", control.Kind) + return 0, udferr.Newf("python udf: unexpected control %q", control.Kind) } } } @@ -1690,12 +1683,12 @@ func (g *Gateway) receiveResultBatch( // header could bypass the body-only limit used by the old result path. func freezeResultFrames(header, body []byte, maxBytes int64) (*protocol.OutputSnapshot, *protocol.OutputSnapshot, error) { if maxBytes <= 0 { - return nil, nil, moerr.NewInternalErrorNoCtxf("invalid Python UDF result batch limit") + return nil, nil, udferr.Newf("invalid Python UDF result batch limit") } headerBytes := int64(len(header)) bodyBytes := int64(len(body)) if headerBytes > maxBytes || bodyBytes > maxBytes-headerBytes { - return nil, nil, moerr.NewInternalErrorNoCtxf("RESOURCE_EXHAUSTED: Arrow result batch exceeds %d bytes", maxBytes) + return nil, nil, udferr.Newf("RESOURCE_EXHAUSTED: Arrow result batch exceeds %d bytes", maxBytes) } headerSnapshot, err := protocol.FreezeOutput(header, maxBytes) if err != nil { @@ -1723,10 +1716,10 @@ type executionBudget struct { func validateInvocationBudget(budget executionBudget) error { if budget.rows < 0 { - return moerr.NewInternalErrorNoCtxf("python udf: invocation row count is negative") + return udferr.Newf("python udf: invocation row count is negative") } if budget.maxRows > 0 && budget.rows > budget.maxRows { - return moerr.NewInternalErrorNoCtxf("RESOURCE_EXHAUSTED: Python UDF invocation has %d rows, limit is %d", budget.rows, budget.maxRows) + return udferr.Newf("RESOURCE_EXHAUSTED: Python UDF invocation has %d rows, limit is %d", budget.rows, budget.maxRows) } if budget.maxResultBytes <= 0 || budget.rows == 0 { return nil @@ -1737,11 +1730,11 @@ func validateInvocationBudget(budget executionBudget) error { // each validated append because their area size depends on user values. validityBytes := (budget.rows-1)/8 + 1 if validityBytes >= budget.maxResultBytes { - return moerr.NewInternalErrorNoCtxf("RESOURCE_EXHAUSTED: Python UDF invocation result reservation exceeds %d bytes", budget.maxResultBytes) + return udferr.Newf("RESOURCE_EXHAUSTED: Python UDF invocation result reservation exceeds %d bytes", budget.maxResultBytes) } perRow := int64(budget.returnType.TypeSize()) if perRow > 0 && budget.rows > (budget.maxResultBytes-validityBytes)/perRow { - return moerr.NewInternalErrorNoCtxf("RESOURCE_EXHAUSTED: Python UDF invocation result reservation exceeds %d bytes", budget.maxResultBytes) + return udferr.Newf("RESOURCE_EXHAUSTED: Python UDF invocation result reservation exceeds %d bytes", budget.maxResultBytes) } return nil } @@ -1749,7 +1742,7 @@ func validateInvocationBudget(budget executionBudget) error { func validateResultSchemaMetadata(metadata []byte, tuple protocol.FencingTuple) error { control, err := protocol.UnmarshalControl(metadata) if err != nil || control.Tuple != tuple || control.Kind != "ResultSchema" { - return moerr.NewInternalErrorNoCtxf("python udf: invalid result schema metadata") + return udferr.Newf("python udf: invalid result schema metadata") } return nil } @@ -1766,33 +1759,33 @@ func (g *Gateway) receiveFinish( data, err := stream.Recv() if err != nil { if err == io.EOF { - return moerr.NewInternalErrorNoCtxf("python udf: exchange ended before Finish") + return udferr.Newf("python udf: exchange ended before Finish") } return errutil.Wrapf(err, "python udf: receive Finish") } if data == nil || len(data.DataHeader) > 0 || len(data.DataBody) > 0 { - return moerr.NewInternalErrorNoCtxf("python udf: unexpected Arrow data while finishing") + return udferr.Newf("python udf: unexpected Arrow data while finishing") } control, err := protocol.UnmarshalControl(data.AppMetadata) if err != nil { return err } if control.Tuple != tuple { - return moerr.NewInternalErrorNoCtxf("python udf: Finish tuple changed") + return udferr.Newf("python udf: Finish tuple changed") } switch control.Kind { case "InputConsumed", "ResultSchema": - return moerr.NewInternalErrorNoCtxf("python udf: %s arrived after result stream was drained", control.Kind) + return udferr.Newf("python udf: %s arrived after result stream was drained", control.Kind) case "Error": - return moerr.NewInternalErrorNoCtxf("python udf: worker error: %s", control.Reason) + return udferr.Newf("python udf: worker error: %s", control.Reason) case "Finish": lastInput, lastResult, acked, inputEnded := sequence.State() if control.Status != statusOK || control.FinishID == "" || rows != expectedRows || !inputEnded || control.LastSequence != lastInput || lastResult != lastInput || acked != lastInput || !sequence.ReadyToFinish() { - return moerr.NewInternalErrorNoCtxf("python udf: invalid Finish status=%q rows=%d", control.Status, rows) + return udferr.Newf("python udf: invalid Finish status=%q rows=%d", control.Status, rows) } if control.LastResultSequence != lastResult { - return moerr.NewInternalErrorNoCtxf("python udf: invalid Finish result sequence %d, expected %d", control.LastResultSequence, lastResult) + return udferr.Newf("python udf: invalid Finish result sequence %d, expected %d", control.LastResultSequence, lastResult) } if err := g.acknowledgeFinish(ctx, client, "AcknowledgeFinish", protocol.Control{Kind: "AcknowledgeFinish", Tuple: tuple, FinishID: control.FinishID}); err != nil { return err @@ -1805,7 +1798,7 @@ func (g *Gateway) receiveFinish( // has returned and its cleanup owner has completed. return g.waitExchangeEOF(ctx, stream) default: - return moerr.NewInternalErrorNoCtxf("python udf: unexpected control %q", control.Kind) + return udferr.Newf("python udf: unexpected control %q", control.Kind) } } } @@ -1820,7 +1813,7 @@ func (g *Gateway) waitExchangeEOF(ctx context.Context, stream flight.FlightServi return errutil.Wrapf(err, "python udf: wait for exchange completion") } if data == nil || len(data.DataHeader) != 0 || len(data.DataBody) != 0 || len(data.AppMetadata) != 0 { - return moerr.NewInternalErrorNoCtxf("python udf: exchange returned data after Finish ACK") + return udferr.Newf("python udf: exchange returned data after Finish ACK") } } } @@ -1831,7 +1824,7 @@ func (g *Gateway) waitExchangeEOF(ctx context.Context, stream flight.FlightServi // between an accepted terminal result and a locally unconfirmed Finish. func (g *Gateway) acknowledgeFinish(ctx context.Context, client flight.FlightServiceClient, kind string, finish protocol.Control) error { if client == nil { - return moerr.NewInternalErrorNoCtxf("FINISH_UNCONFIRMED: finish ACK client is unavailable") + return udferr.Newf("FINISH_UNCONFIRMED: finish ACK client is unavailable") } firstErr := g.action(ctx, client, kind, finish) if firstErr == nil { @@ -1852,7 +1845,7 @@ func validateInvocation(invocation *udf.Invocation) error { return err } if invocation.Source == "" { - return moerr.NewInternalErrorNoCtxf("python udf: handler and source are required") + return udferr.Newf("python udf: handler and source are required") } // ArtifactResolver is an execution boundary, so the Gateway must enforce // the same immutable source contract even when a custom resolver does not @@ -1860,13 +1853,13 @@ func validateInvocation(invocation *udf.Invocation) error { // otherwise an oversized or invalid source can consume a large temporary // allocation and only fail at the Flight control-size boundary. if !utf8.ValidString(invocation.Source) { - return moerr.NewInternalErrorNoCtxf("UNSUPPORTED_ROUTINE_VERSION: Python artifact source is not valid UTF-8") + return udferr.Newf("UNSUPPORTED_ROUTINE_VERSION: Python artifact source is not valid UTF-8") } if int64(len([]byte(invocation.Source))) > DefaultMaxArtifactBytes { - return moerr.NewInternalErrorNoCtxf("RESOURCE_EXHAUSTED: Python artifact exceeds %d bytes", DefaultMaxArtifactBytes) + return udferr.Newf("RESOURCE_EXHAUSTED: Python artifact exceeds %d bytes", DefaultMaxArtifactBytes) } if invocation.ArtifactDigest != udf.PythonInlineArtifactDigest(invocation.Handler, invocation.Source) { - return moerr.NewInternalErrorNoCtxf("UNSUPPORTED_ROUTINE_VERSION: Python artifact digest does not match the immutable source") + return udferr.Newf("UNSUPPORTED_ROUTINE_VERSION: Python artifact digest does not match the immutable source") } return nil } @@ -1878,68 +1871,68 @@ func validateInvocation(invocation *udf.Invocation) error { // be valid. func validateInvocationHeader(invocation *udf.Invocation) error { if invocation == nil { - return moerr.NewInternalErrorNoCtxf("python udf: nil invocation") + return udferr.Newf("python udf: nil invocation") } if invocation.Language != udf.LanguagePython { - return moerr.NewInternalErrorNoCtxf("python udf: unsupported language %q", invocation.Language) + return udferr.Newf("python udf: unsupported language %q", invocation.Language) } if invocation.Handler == "" { - return moerr.NewInternalErrorNoCtxf("python udf: handler is required") + return udferr.Newf("python udf: handler is required") } if strings.Contains(invocation.Handler, ":") { - return moerr.NewInternalErrorNoCtxf("UNSUPPORTED_ROUTINE_VERSION: Python external handler import requires an immutable artifact catalog") + return udferr.Newf("UNSUPPORTED_ROUTINE_VERSION: Python external handler import requires an immutable artifact catalog") } if invocation.Length < 0 || len(invocation.Args) != len(invocation.Inputs) { - return moerr.NewInternalErrorNoCtxf("python udf: invalid input shape") + return udferr.Newf("python udf: invalid input shape") } if invocation.Mode != ModeScalar && invocation.Mode != ModeVector { - return moerr.NewInternalErrorNoCtxf("python udf: unsupported mode %q", invocation.Mode) + return udferr.Newf("python udf: unsupported mode %q", invocation.Mode) } if invocation.CallsiteID == "" || len(invocation.CallsiteID) > 256 || strings.ContainsAny(invocation.CallsiteID, "\r\n") { - return moerr.NewInternalErrorNoCtxf("UNSUPPORTED_ROUTINE_VERSION: Python invocation has no valid callsite id") + return udferr.Newf("UNSUPPORTED_ROUTINE_VERSION: Python invocation has no valid callsite id") } if !invocation.MayError || invocation.SecurityMode != "INVOKER" || invocation.Leakproof { - return moerr.NewInternalErrorNoCtxf("UNSUPPORTED_ROUTINE_VERSION: Python invocation semantic contract is not supported") + return udferr.Newf("UNSUPPORTED_ROUTINE_VERSION: Python invocation semantic contract is not supported") } if invocation.StatementContext == nil { - return moerr.NewInternalErrorNoCtxf("UNSUPPORTED_ROUTINE_VERSION: Python invocation has no statement context") + return udferr.Newf("UNSUPPORTED_ROUTINE_VERSION: Python invocation has no statement context") } if err := invocation.StatementContext.Validate(); err != nil { return err } if invocation.SecurityFrame == nil { - return moerr.NewInternalErrorNoCtxf("UNSUPPORTED_ROUTINE_VERSION: Python invocation has no security frame") + return udferr.Newf("UNSUPPORTED_ROUTINE_VERSION: Python invocation has no security frame") } if err := invocation.SecurityFrame.Validate(); err != nil { return err } if invocation.SecurityFrame.Mode != invocation.SecurityMode { - return moerr.NewInternalErrorNoCtxf("UNSUPPORTED_ROUTINE_VERSION: Python invocation security mode does not match its frame") + return udferr.Newf("UNSUPPORTED_ROUTINE_VERSION: Python invocation security mode does not match its frame") } if invocation.NullPolicy != NullCallHandler && invocation.NullPolicy != NullReturnNull { - return moerr.NewInternalErrorNoCtxf("python udf: unsupported NULL policy %q", invocation.NullPolicy) + return udferr.Newf("python udf: unsupported NULL policy %q", invocation.NullPolicy) } if invocation.ABIContract != udf.PythonABIContract || invocation.AdapterVersion != udf.PythonAdapterVersion { - return moerr.NewInternalErrorNoCtxf("python udf: unsupported Python ABI contract %q/%q", invocation.ABIContract, invocation.AdapterVersion) + return udferr.Newf("python udf: unsupported Python ABI contract %q/%q", invocation.ABIContract, invocation.AdapterVersion) } if invocation.SDKVersion != udf.PythonSDKVersion { - return moerr.NewInternalErrorNoCtxf("python udf: unsupported Python SDK %q", invocation.SDKVersion) + return udferr.Newf("python udf: unsupported Python SDK %q", invocation.SDKVersion) } if invocation.DefinitionSchemaVersion != udf.PythonDefinitionSchemaVersion { - return moerr.NewInternalErrorNoCtxf("UNSUPPORTED_ROUTINE_VERSION: unsupported Python definition schema %d", invocation.DefinitionSchemaVersion) + return udferr.Newf("UNSUPPORTED_ROUTINE_VERSION: unsupported Python definition schema %d", invocation.DefinitionSchemaVersion) } if !udf.IsSHA256Digest(invocation.ArtifactDigest) { - return moerr.NewInternalErrorNoCtxf("UNSUPPORTED_ROUTINE_VERSION: Python artifact digest does not match the immutable source") + return udferr.Newf("UNSUPPORTED_ROUTINE_VERSION: Python artifact digest does not match the immutable source") } environmentDigest, err := udf.PythonEnvironmentDigest() if err != nil { return errutil.Wrapf(err, "UNSUPPORTED_ROUTINE_VERSION: Python environment digest is unavailable") } if invocation.EnvironmentDigest != environmentDigest { - return moerr.NewInternalErrorNoCtxf("UNSUPPORTED_ROUTINE_VERSION: Python environment digest does not match the current worker contract") + return udferr.Newf("UNSUPPORTED_ROUTINE_VERSION: Python environment digest does not match the current worker contract") } if !udf.IsSHA256Digest(invocation.DefinitionFingerprint) { - return moerr.NewInternalErrorNoCtxf("UNSUPPORTED_ROUTINE_VERSION: Python definition fingerprint is invalid") + return udferr.Newf("UNSUPPORTED_ROUTINE_VERSION: Python definition fingerprint is invalid") } args := make([]TypeDescriptor, len(invocation.Args)) for index, typ := range invocation.Args { @@ -1967,20 +1960,20 @@ func validateInvocationHeader(invocation *udf.Invocation) error { return errutil.Wrapf(err, "UNSUPPORTED_ROUTINE_VERSION: Python definition fingerprint cannot be computed") } if invocation.DefinitionFingerprint != expectedFingerprint { - return moerr.NewInternalErrorNoCtxf("UNSUPPORTED_ROUTINE_VERSION: Python definition fingerprint does not match the typed definition") + return udferr.Newf("UNSUPPORTED_ROUTINE_VERSION: Python definition fingerprint does not match the typed definition") } if err := invocation.FunctionRef.Validate(); err != nil { return errutil.Wrapf(err, "UNSUPPORTED_ROUTINE_VERSION") } if invocation.FunctionRef.AccountID != invocation.Tuple.AccountID { - return moerr.NewInternalErrorNoCtxf("python udf: FunctionRef account does not match the fencing tuple") + return udferr.Newf("python udf: FunctionRef account does not match the fencing tuple") } return invocation.Tuple.Validate() } func validateInvocationInputs(invocation *udf.Invocation, args []TypeDescriptor) error { if invocation == nil || len(invocation.Inputs) != len(args) { - return moerr.NewInternalErrorNoCtxf("python udf: invalid input shape") + return udferr.Newf("python udf: invalid input shape") } // An empty physical selection has no input backing to inspect. Nil inputs // are valid in this case, but a vector supplied by a caller still has to @@ -1992,20 +1985,20 @@ func validateInvocationInputs(invocation *udf.Invocation, args []TypeDescriptor) continue } if input.GetType() == nil || !input.GetType().Eq(invocation.Args[index]) { - return moerr.NewInternalErrorNoCtxf("python udf: input column %d type does not match the frozen argument type", index) + return udferr.Newf("python udf: input column %d type does not match the frozen argument type", index) } } return nil } for index, input := range invocation.Inputs { if input == nil || input.GetType() == nil || input.Length() == 0 { - return moerr.NewInternalErrorNoCtxf("python udf: input column %d has no backing vector", index) + return udferr.Newf("python udf: input column %d has no backing vector", index) } if !input.GetType().Eq(invocation.Args[index]) { - return moerr.NewInternalErrorNoCtxf("python udf: input column %d type %s does not match the frozen argument type %s", index, input.GetType().DescString(), invocation.Args[index].DescString()) + return udferr.Newf("python udf: input column %d type %s does not match the frozen argument type %s", index, input.GetType().DescString(), invocation.Args[index].DescString()) } if !input.IsConst() && input.Length() < invocation.Length { - return moerr.NewInternalErrorNoCtxf("python udf: input column %d has %d rows, expected at least %d", index, input.Length(), invocation.Length) + return udferr.Newf("python udf: input column %d has %d rows, expected at least %d", index, input.Length(), invocation.Length) } } return nil @@ -2027,7 +2020,7 @@ func (g *Gateway) action(ctx context.Context, client flight.FlightServiceClient, return err } if result == nil { - return moerr.NewInternalErrorNoCtxf("python udf: empty %s response", kind) + return udferr.Newf("python udf: empty %s response", kind) } ack, err := protocol.UnmarshalControl(result.Body) if err != nil { @@ -2037,11 +2030,11 @@ func (g *Gateway) action(ctx context.Context, client flight.FlightServiceClient, return err } if ack.Status != statusOK { - return moerr.NewInternalErrorNoCtxf("python udf: %s rejected: %s", kind, ack.Reason) + return udferr.Newf("python udf: %s rejected: %s", kind, ack.Reason) } if _, err := stream.Recv(); err != io.EOF { if err == nil { - return moerr.NewInternalErrorNoCtxf("python udf: %s returned multiple results", kind) + return udferr.Newf("python udf: %s returned multiple results", kind) } return err } @@ -2050,19 +2043,19 @@ func (g *Gateway) action(ctx context.Context, client flight.FlightServiceClient, func validateActionAck(kind string, request, ack protocol.Control) error { if ack.Tuple != request.Tuple || ack.Kind != "Ack" { - return moerr.NewInternalErrorNoCtxf("python udf: %s returned an invalid ACK", kind) + return udferr.Newf("python udf: %s returned an invalid ACK", kind) } switch kind { case "AcknowledgeResults": if ack.AckSequence != request.AckSequence { - return moerr.NewInternalErrorNoCtxf("python udf: %s ACK sequence %d, expected %d", kind, ack.AckSequence, request.AckSequence) + return udferr.Newf("python udf: %s ACK sequence %d, expected %d", kind, ack.AckSequence, request.AckSequence) } case "AcknowledgeFinish": if ack.FinishID != request.FinishID { - return moerr.NewInternalErrorNoCtxf("python udf: %s ACK finish ID does not match the request", kind) + return udferr.Newf("python udf: %s ACK finish ID does not match the request", kind) } default: - return moerr.NewInternalErrorNoCtxf("python udf: unsupported action %q", kind) + return udferr.Newf("python udf: unsupported action %q", kind) } return nil } diff --git a/pkg/udf/python/gateway_integration_test.go b/pkg/udf/python/gateway_integration_test.go index 5517b648cda28..14661b2f88706 100644 --- a/pkg/udf/python/gateway_integration_test.go +++ b/pkg/udf/python/gateway_integration_test.go @@ -743,6 +743,8 @@ func startRealWorker(t testing.TB, python, workerPath string, port int) *exec.Cm cmd.SysProcAttr = &syscall.SysProcAttr{Setpgid: true} } require.NoError(t, cmd.Start()) + // Own cleanup before readiness can fail; Fatal also runs test cleanups. + t.Cleanup(func() { stopRealWorker(cmd) }) waitForWorkerPort(t, port) return cmd } @@ -756,7 +758,7 @@ func stopRealWorker(cmd *exec.Cmd) { } else { _ = cmd.Process.Kill() } - _, _ = cmd.Process.Wait() + _ = cmd.Wait() } func integrationInput(t testing.TB, values []int64) (*vector.Vector, *mpool.MPool) { @@ -868,3 +870,16 @@ func routineDefinitionForIntegration(invocation *udf.Invocation) *udf.RoutineDef DefinitionFingerprint: invocation.DefinitionFingerprint, } } + +func TestRealWorkerStopRecordsCompletion(t *testing.T) { + python, err := exec.LookPath("python3") + require.NoError(t, err) + cmd := startRealWorker(t, python, realWorkerPath(t), freeTCPPort(t)) + stopRealWorker(cmd) + // Process.Wait alone does not publish this state on Cmd, so delayed cleanup + // could send a second signal to a process group whose ID has been reused. + require.NotNil(t, cmd.ProcessState) + completed := cmd.ProcessState + stopRealWorker(cmd) + require.Same(t, completed, cmd.ProcessState) +} diff --git a/pkg/udf/python/supervisor.go b/pkg/udf/python/supervisor.go index ed09bc63dc2b9..b8b143c7610f3 100644 --- a/pkg/udf/python/supervisor.go +++ b/pkg/udf/python/supervisor.go @@ -16,17 +16,18 @@ package python import ( "errors" - "github.com/matrixorigin/matrixone/pkg/common/moerr" - "github.com/matrixorigin/matrixone/pkg/util/errutil" "os" "os/exec" "path/filepath" "sync" + "github.com/matrixorigin/matrixone/pkg/udf/udferr" + "github.com/matrixorigin/matrixone/pkg/util/errutil" + "github.com/matrixorigin/matrixone/pkg/logutil" ) -var errSupervisorClosing = moerr.NewInternalErrorNoCtx("python udf worker is shutting down") +var errSupervisorClosing = udferr.New("python udf worker is shutting down") type Supervisor struct { cfg Config diff --git a/pkg/udf/runtime.go b/pkg/udf/runtime.go index 16bed1eaef382..a9edc4a024492 100644 --- a/pkg/udf/runtime.go +++ b/pkg/udf/runtime.go @@ -22,13 +22,15 @@ import ( "context" "encoding/json" "errors" - "github.com/matrixorigin/matrixone/pkg/util/errutil" "sort" "strconv" "strings" "sync" "time" + "github.com/matrixorigin/matrixone/pkg/udf/udferr" + "github.com/matrixorigin/matrixone/pkg/util/errutil" + "github.com/matrixorigin/matrixone/pkg/common/moerr" "github.com/matrixorigin/matrixone/pkg/common/mpool" "github.com/matrixorigin/matrixone/pkg/container/types" @@ -91,41 +93,41 @@ type StatementContext struct { func (c StatementContext) Validate() error { if c.ContractVersion != StatementContextContractVersion { - return moerr.NewInternalErrorNoCtxf("UNSUPPORTED_ROUTINE_VERSION: unsupported statement context contract %d", c.ContractVersion) + return udferr.Newf("UNSUPPORTED_ROUTINE_VERSION: unsupported statement context contract %d", c.ContractVersion) } if c.StatementTimestampUTC < minStatementTimestampUTC || c.StatementTimestampUTC > maxStatementTimestampUTC { - return moerr.NewInternalErrorNoCtxf("python udf: statement timestamp is outside the Python datetime range") + return udferr.Newf("python udf: statement timestamp is outside the Python datetime range") } if c.CurrentUser == "" || c.ConnectionCollation == "" { - return moerr.NewInternalErrorNoCtxf("python udf: statement context has no authenticated user or collation") + return udferr.Newf("python udf: statement context has no authenticated user or collation") } if c.TimezoneKind == "IANA" { if c.TimezoneName == "" || c.TimezoneDatabaseVersion == "" || c.TimezoneOffsetMinutes != 0 { - return moerr.NewInternalErrorNoCtxf("python udf: IANA statement timezone is incomplete") + return udferr.Newf("python udf: IANA statement timezone is incomplete") } if _, err := time.LoadLocation(c.TimezoneName); err != nil { return errutil.Wrapf(err, "python udf: IANA statement timezone is not present in the local tzdb") } } else if c.TimezoneKind == "FIXED_OFFSET" { if c.TimezoneName != "" || c.TimezoneDatabaseVersion != "" || c.TimezoneOffsetMinutes < -839 || c.TimezoneOffsetMinutes > 840 { - return moerr.NewInternalErrorNoCtxf("python udf: fixed statement timezone is invalid") + return udferr.Newf("python udf: fixed statement timezone is invalid") } } else { - return moerr.NewInternalErrorNoCtxf("python udf: unsupported statement timezone kind %q", c.TimezoneKind) + return udferr.Newf("python udf: unsupported statement timezone kind %q", c.TimezoneKind) } mode := append([]string(nil), c.SQLMode...) sort.Strings(mode) for i := range mode { if mode[i] == "" || (i > 0 && mode[i] == mode[i-1]) { - return moerr.NewInternalErrorNoCtxf("python udf: statement sql_mode is not canonical") + return udferr.Newf("python udf: statement sql_mode is not canonical") } } if len(mode) != len(c.SQLMode) { - return moerr.NewInternalErrorNoCtxf("python udf: statement sql_mode is not canonical") + return udferr.Newf("python udf: statement sql_mode is not canonical") } for i := range mode { if mode[i] != c.SQLMode[i] { - return moerr.NewInternalErrorNoCtxf("python udf: statement sql_mode is not sorted") + return udferr.Newf("python udf: statement sql_mode is not sorted") } } return nil @@ -170,7 +172,7 @@ func StatementContextFromMap(values map[string]string) (*StatementContext, error // Reject it at the trusted CN adapter instead of carrying a malformed // snapshot into the Flight request. if mode == nil { - return nil, moerr.NewInternalErrorNoCtxf("python udf: invalid statement sql_mode: expected a JSON array") + return nil, udferr.Newf("python udf: invalid statement sql_mode: expected a JSON array") } } context := &StatementContext{ @@ -213,10 +215,10 @@ type SecurityFrame struct { func (f SecurityFrame) Validate() error { if f.ContractVersion != SecurityFrameContractVersion || f.Mode != "INVOKER" { - return moerr.NewInternalErrorNoCtxf("UNSUPPORTED_ROUTINE_VERSION: unsupported Python security frame") + return udferr.Newf("UNSUPPORTED_ROUTINE_VERSION: unsupported Python security frame") } if f.InvokerUserID != f.EffectiveUserID || f.InvokerRoleID != f.EffectiveRoleID { - return moerr.NewInternalErrorNoCtxf("UNSUPPORTED_ROUTINE_VERSION: Python invoker security frame changed effective principal") + return udferr.Newf("UNSUPPORTED_ROUTINE_VERSION: Python invoker security frame changed effective principal") } return nil } diff --git a/pkg/udf/timezone.go b/pkg/udf/timezone.go index cafda4adef42f..9053fb472087c 100644 --- a/pkg/udf/timezone.go +++ b/pkg/udf/timezone.go @@ -16,9 +16,10 @@ package udf import ( "bufio" - "github.com/matrixorigin/matrixone/pkg/common/moerr" "os" "strings" + + "github.com/matrixorigin/matrixone/pkg/udf/udferr" ) // TimezoneDatabaseVersion returns the version of the system IANA tz database @@ -52,5 +53,5 @@ func TimezoneDatabaseVersion() (string, error) { } } } - return "", moerr.NewInternalErrorNoCtxf("python udf: IANA timezone database version is unavailable") + return "", udferr.Newf("python udf: IANA timezone database version is unavailable") } diff --git a/pkg/udf/udferr/errors.go b/pkg/udf/udferr/errors.go new file mode 100644 index 0000000000000..24f123728691f --- /dev/null +++ b/pkg/udf/udferr/errors.go @@ -0,0 +1,33 @@ +// Copyright 2026 Matrix Origin +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Package udferr defines diagnostic errors for the routine catalog and external +// execution contract. These errors retain the domain's message verbatim; they +// are not MO SQL error classifications. Callers that own a specific SQL error +// must continue to construct that moerr at the SQL boundary. +package udferr + +import "fmt" + +// Error is a routine-contract diagnostic. Each construction has distinct +// identity so equal text does not accidentally alias independent sentinels. +type Error struct{ message string } + +func (e *Error) Error() string { return e.message } + +func New(message string) error { return &Error{message: message} } + +// Newf formats a leaf diagnostic. Use errutil wrapping for an existing cause; +// this function deliberately does not interpret wrapping verbs. +func Newf(format string, args ...any) error { return New(fmt.Sprintf(format, args...)) } diff --git a/pkg/udf/udferr/errors_test.go b/pkg/udf/udferr/errors_test.go new file mode 100644 index 0000000000000..4a22db7f56d26 --- /dev/null +++ b/pkg/udf/udferr/errors_test.go @@ -0,0 +1,35 @@ +// Copyright 2026 Matrix Origin +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package udferr + +import ( + "errors" + "testing" + + "github.com/matrixorigin/matrixone/pkg/util/errutil" + "github.com/stretchr/testify/require" +) + +func TestDiagnosticIdentity(t *testing.T) { + first, second := New("TYPE_CONTRACT: invalid result"), New("TYPE_CONTRACT: invalid result") + wrapped := errutil.Wrapf(first, "gateway") + require.ErrorIs(t, wrapped, first) + require.NotErrorIs(t, wrapped, second) + var diagnostic *Error + require.True(t, errors.As(wrapped, &diagnostic)) + require.Equal(t, first, diagnostic) + require.EqualError(t, wrapped, "gateway: TYPE_CONTRACT: invalid result") + require.EqualError(t, Newf("invalid dimension %d", 3), "invalid dimension 3") +} diff --git a/pkg/util/errutil/errors_test.go b/pkg/util/errutil/errors_test.go index 21613cc7b182a..6e8f5b3485598 100644 --- a/pkg/util/errutil/errors_test.go +++ b/pkg/util/errutil/errors_test.go @@ -16,10 +16,13 @@ package errutil import ( "context" - "github.com/stretchr/testify/require" + "errors" + "os" "reflect" "sync/atomic" "testing" + + "github.com/stretchr/testify/require" ) func mockReportError(_ context.Context, err error, depth int) {} @@ -186,6 +189,16 @@ func TestWrapfCauseFirst(t *testing.T) { err := WrapfCauseFirst(cause, "detail %d", 7) require.Equal(t, "context canceled: detail 7", err.Error()) require.ErrorIs(t, err, cause) + require.Nil(t, WrapfCauseFirst(nil, "ignored")) + require.EqualError(t, WrapfCauseFirst(cause, ""), cause.Error()) + first, second := errors.New("same"), errors.New("same") + require.NotErrorIs(t, WrapfCauseFirst(first, "detail"), second) + typed := &os.PathError{Op: "read", Path: "artifact", Err: first} + nested := Wrap(WrapfCauseFirst(typed, "detail"), "gateway") + var target *os.PathError + require.ErrorAs(t, nested, &target) + require.Same(t, typed, target) + require.ErrorIs(t, nested, first) } func Test_noopReportError(t *testing.T) { diff --git a/test/distributed/cases/git4data/clone/clone_sys_db_table_to_new_db_table.result b/test/distributed/cases/git4data/clone/clone_sys_db_table_to_new_db_table.result index 6f02baf888448..0196e10e1669d 100644 --- a/test/distributed/cases/git4data/clone/clone_sys_db_table_to_new_db_table.result +++ b/test/distributed/cases/git4data/clone/clone_sys_db_table_to_new_db_table.result @@ -144,6 +144,7 @@ mo_database mo_feature_limit mo_feature_registry mo_foreign_keys +mo_function_revisions mo_iceberg_catalogs mo_iceberg_maintenance_jobs mo_iceberg_orphan_files @@ -214,6 +215,7 @@ mo_database mo_feature_limit mo_feature_registry mo_foreign_keys +mo_function_revisions mo_iceberg_catalogs mo_iceberg_maintenance_jobs mo_iceberg_orphan_files diff --git a/test/distributed/cases/mo_cloud/mo_cloud.result b/test/distributed/cases/mo_cloud/mo_cloud.result index 0d523e8d4125f..47809b077b26c 100644 --- a/test/distributed/cases/mo_cloud/mo_cloud.result +++ b/test/distributed/cases/mo_cloud/mo_cloud.result @@ -810,6 +810,7 @@ mo_catalog ¦ mo_columns ¦ r ¦ - 𝄀 mo_catalog ¦ mo_configurations ¦ v ¦ accountadmin 𝄀 mo_catalog ¦ mo_database ¦ r ¦ - 𝄀 mo_catalog ¦ mo_foreign_keys ¦ r ¦ accountadmin 𝄀 +mo_catalog ¦ mo_function_revisions ¦ r ¦ accountadmin 𝄀 mo_catalog ¦ mo_iceberg_catalogs ¦ r ¦ accountadmin 𝄀 mo_catalog ¦ mo_iceberg_maintenance_jobs ¦ r ¦ accountadmin 𝄀 mo_catalog ¦ mo_iceberg_orphan_files ¦ r ¦ accountadmin 𝄀 diff --git a/test/distributed/cases/snapshot/cluster/restore_cluster_table.result b/test/distributed/cases/snapshot/cluster/restore_cluster_table.result index 05ce577ace6ad..a457338d12e6e 100644 --- a/test/distributed/cases/snapshot/cluster/restore_cluster_table.result +++ b/test/distributed/cases/snapshot/cluster/restore_cluster_table.result @@ -231,8 +231,8 @@ create function `addAB`(x int, y int) returns int language sql as '$1 + $2'; select * from mo_catalog.mo_user_defined_function; -function_id name owner args arg_types rettype body language db definer modified_time created_time type security_type comment character_set_client collation_connection database_collation sql_mode -1 addab 2 [{"name": "x", "type": "int"}, {"name": "y", "type": "int"}] ["int", "int"] int $1 + $2 sql udf_db2 test_account 2025-11-24 15:27:40 2025-11-24 15:27:40 FUNCTION DEFINER utf8mb4 utf8mb4_0900_ai_ci utf8mb4_0900_ai_ci ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION,NO_ZERO_DATE,NO_ZERO_IN_DATE,ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,ENABLE_BOOL_SUMAVG +function_id active_revision namespace_version name owner args arg_types canonical_input_descriptor return_descriptor signature_key_schema_version signature_fingerprint rettype body language db definer modified_time created_time type security_type comment character_set_client collation_connection database_collation sql_mode +1 1 1 addab 2 [{"name": "x", "type": "int"}, {"name": "y", "type": "int"}] ["int", "int"] 0 int $1 + $2 sql udf_db2 test_account 2025-11-24 15:27:40 2025-11-24 15:27:40 FUNCTION DEFINER utf8mb4 utf8mb4_0900_ai_ci utf8mb4_0900_ai_ci ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION,NO_ZERO_DATE,NO_ZERO_IN_DATE,ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,ENABLE_BOOL_SUMAVG drop stage if exists my_ext_stage; create stage my_ext_stage URL='s3://load/files/'; drop stage if exists my_ext_stage1; @@ -421,6 +421,7 @@ mo_database mo_feature_limit mo_feature_registry mo_foreign_keys +mo_function_revisions mo_iceberg_catalogs mo_iceberg_maintenance_jobs mo_iceberg_orphan_files @@ -484,8 +485,8 @@ create function `add`(x int, y int) returns int language sql as '$1 + $2'; select * from mo_catalog.mo_user_defined_function; -function_id name owner args arg_types rettype body language db definer modified_time created_time type security_type comment character_set_client collation_connection database_collation sql_mode -2 add 2 [{"name": "x", "type": "int"}, {"name": "y", "type": "int"}] ["int", "int"] int $1 + $2 sql udf_db2 test_account 2025-11-24 15:27:40 2025-11-24 15:27:40 FUNCTION DEFINER utf8mb4 utf8mb4_0900_ai_ci utf8mb4_0900_ai_ci ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION,NO_ZERO_DATE,NO_ZERO_IN_DATE,ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,ENABLE_BOOL_SUMAVG +function_id active_revision namespace_version name owner args arg_types canonical_input_descriptor return_descriptor signature_key_schema_version signature_fingerprint rettype body language db definer modified_time created_time type security_type comment character_set_client collation_connection database_collation sql_mode +2 1 1 add 2 [{"name": "x", "type": "int"}, {"name": "y", "type": "int"}] ["int", "int"] 0 int $1 + $2 sql udf_db2 test_account 2025-11-24 15:27:40 2025-11-24 15:27:40 FUNCTION DEFINER utf8mb4 utf8mb4_0900_ai_ci utf8mb4_0900_ai_ci ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION,NO_ZERO_DATE,NO_ZERO_IN_DATE,ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,ENABLE_BOOL_SUMAVG drop database test01; use test02; select * from v01; @@ -552,6 +553,7 @@ mo_database mo_feature_limit mo_feature_registry mo_foreign_keys +mo_function_revisions mo_iceberg_catalogs mo_iceberg_maintenance_jobs mo_iceberg_orphan_files @@ -612,8 +614,8 @@ stage_id stage_name url stage_credentials stage_status created_ti 1 my_ext_stage s3://load/files/ in_use 2025-11-24 15:27:40 2 my_ext_stage1 s3://load/files/ AWS_KEY_ID=1a2b3c,AWS_SECRET_KEY=4x5y6z in_use 2025-11-24 15:27:40 select * from mo_catalog.mo_user_defined_function; -function_id name owner args arg_types rettype body language db definer modified_time created_time type security_type comment character_set_client collation_connection database_collation sql_mode -1 addab 2 [{"name": "x", "type": "int"}, {"name": "y", "type": "int"}] ["int", "int"] int $1 + $2 sql udf_db2 test_account 2025-11-24 15:27:40 2025-11-24 15:27:40 FUNCTION DEFINER utf8mb4 utf8mb4_0900_ai_ci utf8mb4_0900_ai_ci ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION,NO_ZERO_DATE,NO_ZERO_IN_DATE,ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,ENABLE_BOOL_SUMAVG +function_id active_revision namespace_version name owner args arg_types canonical_input_descriptor return_descriptor signature_key_schema_version signature_fingerprint rettype body language db definer modified_time created_time type security_type comment character_set_client collation_connection database_collation sql_mode +1 1 1 addab 2 [{"name": "x", "type": "int"}, {"name": "y", "type": "int"}] ["int", "int"] 0 int $1 + $2 sql udf_db2 test_account 2025-11-24 15:27:40 2025-11-24 15:27:40 FUNCTION DEFINER utf8mb4 utf8mb4_0900_ai_ci utf8mb4_0900_ai_ci ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION,NO_ZERO_DATE,NO_ZERO_IN_DATE,ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,ENABLE_BOOL_SUMAVG select * from test01.sales; id sale_date amount 1 2018-12-25 100.00 diff --git a/test/distributed/cases/snapshot/cluster/restore_cluster_table.sql b/test/distributed/cases/snapshot/cluster/restore_cluster_table.sql index bf28a6b2db10d..a34e41ca34850 100644 --- a/test/distributed/cases/snapshot/cluster/restore_cluster_table.sql +++ b/test/distributed/cases/snapshot/cluster/restore_cluster_table.sql @@ -230,7 +230,7 @@ use udf_db2; create function `addAB`(x int, y int) returns int language sql as '$1 + $2'; --- @ignore:0,10,11 +-- @ignore:0,16,17 select * from mo_catalog.mo_user_defined_function; drop stage if exists my_ext_stage; @@ -376,7 +376,7 @@ use udf_db2; create function `add`(x int, y int) returns int language sql as '$1 + $2'; --- @ignore:0,10,11 +-- @ignore:0,16,17 select * from mo_catalog.mo_user_defined_function; -- @session @@ -427,7 +427,7 @@ select * from mo_catalog.mo_stored_procedure; -- @session:id=2&user=acc02:test_account&password=111 -- @ignore:0,5 select * from mo_catalog.mo_stages; --- @ignore:0,10,11 +-- @ignore:0,16,17 select * from mo_catalog.mo_user_defined_function; -- @session diff --git a/test/distributed/cases/snapshot/cluster_level_snapshot_restore_cluster.result b/test/distributed/cases/snapshot/cluster_level_snapshot_restore_cluster.result index d52ecf13024ca..3cbc5e0e46b42 100644 --- a/test/distributed/cases/snapshot/cluster_level_snapshot_restore_cluster.result +++ b/test/distributed/cases/snapshot/cluster_level_snapshot_restore_cluster.result @@ -266,8 +266,8 @@ create function `addAB`(x int, y int) returns int language sql as '$1 + $2'; select * from mo_catalog.mo_user_defined_function; -function_id name owner args arg_types rettype body language db definer modified_time created_time type security_type comment character_set_client collation_connection database_collation sql_mode -1 addab 2 [{"name": "x", "type": "int"}, {"name": "y", "type": "int"}] ["int", "int"] int $1 + $2 sql udf_db2 test_account 2025-11-24 15:26:50 2025-11-24 15:26:50 FUNCTION DEFINER utf8mb4 utf8mb4_0900_ai_ci utf8mb4_0900_ai_ci ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION,NO_ZERO_DATE,NO_ZERO_IN_DATE,ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,ENABLE_BOOL_SUMAVG +function_id active_revision namespace_version name owner args arg_types canonical_input_descriptor return_descriptor signature_key_schema_version signature_fingerprint rettype body language db definer modified_time created_time type security_type comment character_set_client collation_connection database_collation sql_mode +1 1 1 addab 2 [{"name": "x", "type": "int"}, {"name": "y", "type": "int"}] ["int", "int"] 0 int $1 + $2 sql udf_db2 test_account 2025-11-24 15:26:50 2025-11-24 15:26:50 FUNCTION DEFINER utf8mb4 utf8mb4_0900_ai_ci utf8mb4_0900_ai_ci ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION,NO_ZERO_DATE,NO_ZERO_IN_DATE,ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,ENABLE_BOOL_SUMAVG drop stage if exists my_ext_stage; create stage my_ext_stage URL='s3://load/files/'; drop stage if exists my_ext_stage1; @@ -456,6 +456,7 @@ mo_database mo_feature_limit mo_feature_registry mo_foreign_keys +mo_function_revisions mo_iceberg_catalogs mo_iceberg_maintenance_jobs mo_iceberg_orphan_files @@ -520,8 +521,8 @@ create function `add`(x int, y int) returns int language sql as '$1 + $2'; select * from mo_catalog.mo_user_defined_function; -function_id name owner args arg_types rettype body language db definer modified_time created_time type security_type comment character_set_client collation_connection database_collation sql_mode -2 add 2 [{"name": "x", "type": "int"}, {"name": "y", "type": "int"}] ["int", "int"] int $1 + $2 sql udf_db2 test_account 2025-11-24 15:26:50 2025-11-24 15:26:50 FUNCTION DEFINER utf8mb4 utf8mb4_0900_ai_ci utf8mb4_0900_ai_ci ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION,NO_ZERO_DATE,NO_ZERO_IN_DATE,ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,ENABLE_BOOL_SUMAVG +function_id active_revision namespace_version name owner args arg_types canonical_input_descriptor return_descriptor signature_key_schema_version signature_fingerprint rettype body language db definer modified_time created_time type security_type comment character_set_client collation_connection database_collation sql_mode +2 1 1 add 2 [{"name": "x", "type": "int"}, {"name": "y", "type": "int"}] ["int", "int"] 0 int $1 + $2 sql udf_db2 test_account 2025-11-24 15:26:50 2025-11-24 15:26:50 FUNCTION DEFINER utf8mb4 utf8mb4_0900_ai_ci utf8mb4_0900_ai_ci ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION,NO_ZERO_DATE,NO_ZERO_IN_DATE,ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,ENABLE_BOOL_SUMAVG drop database test01; use test02; select * from v01; @@ -593,6 +594,7 @@ mo_database mo_feature_limit mo_feature_registry mo_foreign_keys +mo_function_revisions mo_iceberg_catalogs mo_iceberg_maintenance_jobs mo_iceberg_orphan_files @@ -661,8 +663,8 @@ stage_id stage_name url stage_credentials stage_status created_ti 1 my_ext_stage s3://load/files/ in_use 2025-11-24 15:26:50 2 my_ext_stage1 s3://load/files/ AWS_KEY_ID=1a2b3c,AWS_SECRET_KEY=4x5y6z in_use 2025-11-24 15:26:50 select * from mo_catalog.mo_user_defined_function; -function_id name owner args arg_types rettype body language db definer modified_time created_time type security_type comment character_set_client collation_connection database_collation sql_mode -1 addab 2 [{"name": "x", "type": "int"}, {"name": "y", "type": "int"}] ["int", "int"] int $1 + $2 sql udf_db2 test_account 2025-11-24 15:26:50 2025-11-24 15:26:50 FUNCTION DEFINER utf8mb4 utf8mb4_0900_ai_ci utf8mb4_0900_ai_ci ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION,NO_ZERO_DATE,NO_ZERO_IN_DATE,ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,ENABLE_BOOL_SUMAVG +function_id active_revision namespace_version name owner args arg_types canonical_input_descriptor return_descriptor signature_key_schema_version signature_fingerprint rettype body language db definer modified_time created_time type security_type comment character_set_client collation_connection database_collation sql_mode +1 1 1 addab 2 [{"name": "x", "type": "int"}, {"name": "y", "type": "int"}] ["int", "int"] 0 int $1 + $2 sql udf_db2 test_account 2025-11-24 15:26:50 2025-11-24 15:26:50 FUNCTION DEFINER utf8mb4 utf8mb4_0900_ai_ci utf8mb4_0900_ai_ci ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION,NO_ZERO_DATE,NO_ZERO_IN_DATE,ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,ENABLE_BOOL_SUMAVG use test02; select * from v01; id sale_date amount diff --git a/test/distributed/cases/snapshot/cluster_level_snapshot_restore_cluster.sql b/test/distributed/cases/snapshot/cluster_level_snapshot_restore_cluster.sql index 0e471507c3db9..1aa5f112e674c 100644 --- a/test/distributed/cases/snapshot/cluster_level_snapshot_restore_cluster.sql +++ b/test/distributed/cases/snapshot/cluster_level_snapshot_restore_cluster.sql @@ -245,7 +245,7 @@ use udf_db2; create function `addAB`(x int, y int) returns int language sql as '$1 + $2'; --- @ignore:0,10,11 +-- @ignore:0,16,17 select * from mo_catalog.mo_user_defined_function; drop stage if exists my_ext_stage; @@ -395,7 +395,7 @@ use udf_db2; create function `add`(x int, y int) returns int language sql as '$1 + $2'; --- @ignore:0,10,11 +-- @ignore:0,16,17 select * from mo_catalog.mo_user_defined_function; -- @session @@ -447,7 +447,7 @@ select * from mo_catalog.mo_stored_procedure; -- @session:id=2&user=acc02:test_account&password=111 -- @ignore:0,5 select * from mo_catalog.mo_stages; --- @ignore:0,10,11 +-- @ignore:0,16,17 select * from mo_catalog.mo_user_defined_function; -- @session diff --git a/test/distributed/cases/snapshot/cluster_level_snapshot_restore_system_table_to_nonsys.result b/test/distributed/cases/snapshot/cluster_level_snapshot_restore_system_table_to_nonsys.result index 9400c311f646e..9d09f869d7b46 100644 --- a/test/distributed/cases/snapshot/cluster_level_snapshot_restore_system_table_to_nonsys.result +++ b/test/distributed/cases/snapshot/cluster_level_snapshot_restore_system_table_to_nonsys.result @@ -54,23 +54,23 @@ udf_dsp03 2025-07-19 05:49:43.416853 cluster udf_dsp02 2025-07-19 05:49:43.394563 cluster udf_dsp01 2025-07-19 05:49:43.382694 cluster select * from mo_catalog.mo_user_defined_function; -function_id name owner args arg_types rettype body language db definer modified_time created_time type security_type comment character_set_client collation_connection database_collation sql_mode -1 addab 2 [{"name": "x", "type": "int"}, {"name": "y", "type": "int"}] ["int", "int"] int $1 + $2 sql udf_db test_account 2025-07-19 05:49:43 2025-07-19 05:49:43 FUNCTION DEFINER utf8mb4 utf8mb4_0900_ai_ci utf8mb4_0900_ai_ci ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION,NO_ZERO_DATE,NO_ZERO_IN_DATE,ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,ENABLE_BOOL_SUMAVG -2 concatenate 2 [{"name": "str1", "type": "varchar"}, {"name": "str2", "type": "varchar"}] ["varchar", "varchar"] varchar $1 + $2 sql udf_db test_account 2025-07-19 05:49:43 2025-07-19 05:49:43 FUNCTION DEFINER utf8mb4 utf8mb4_0900_ai_ci utf8mb4_0900_ai_ci ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION,NO_ZERO_DATE,NO_ZERO_IN_DATE,ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,ENABLE_BOOL_SUMAVG -3 subab 2 [{"name": "x", "type": "int"}, {"name": "y", "type": "int"}] ["int", "int"] int $1 - $2 sql udf_db2 test_account 2025-07-19 05:49:43 2025-07-19 05:49:43 FUNCTION DEFINER utf8mb4 utf8mb4_0900_ai_ci utf8mb4_0900_ai_ci ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION,NO_ZERO_DATE,NO_ZERO_IN_DATE,ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,ENABLE_BOOL_SUMAVG +function_id active_revision namespace_version name owner args arg_types canonical_input_descriptor return_descriptor signature_key_schema_version signature_fingerprint rettype body language db definer modified_time created_time type security_type comment character_set_client collation_connection database_collation sql_mode +1 1 1 addab 2 [{"name": "x", "type": "int"}, {"name": "y", "type": "int"}] ["int", "int"] 0 int $1 + $2 sql udf_db test_account 2025-07-19 05:49:43 2025-07-19 05:49:43 FUNCTION DEFINER utf8mb4 utf8mb4_0900_ai_ci utf8mb4_0900_ai_ci ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION,NO_ZERO_DATE,NO_ZERO_IN_DATE,ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,ENABLE_BOOL_SUMAVG +2 1 1 concatenate 2 [{"name": "str1", "type": "varchar"}, {"name": "str2", "type": "varchar"}] ["varchar", "varchar"] 0 varchar $1 + $2 sql udf_db test_account 2025-07-19 05:49:43 2025-07-19 05:49:43 FUNCTION DEFINER utf8mb4 utf8mb4_0900_ai_ci utf8mb4_0900_ai_ci ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION,NO_ZERO_DATE,NO_ZERO_IN_DATE,ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,ENABLE_BOOL_SUMAVG +3 1 1 subab 2 [{"name": "x", "type": "int"}, {"name": "y", "type": "int"}] ["int", "int"] 0 int $1 - $2 sql udf_db2 test_account 2025-07-19 05:49:43 2025-07-19 05:49:43 FUNCTION DEFINER utf8mb4 utf8mb4_0900_ai_ci utf8mb4_0900_ai_ci ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION,NO_ZERO_DATE,NO_ZERO_IN_DATE,ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,ENABLE_BOOL_SUMAVG drop function subab(x int,y int); drop function udf_db.concatenate(str1 varchar(255), str2 varchar(255)); restore account acc01{snapshot="udf_dsp03"}; select * from mo_catalog.mo_user_defined_function; -function_id name owner args arg_types rettype body language db definer modified_time created_time type security_type comment character_set_client collation_connection database_collation sql_mode -1 addab 2 [{"name": "x", "type": "int"}, {"name": "y", "type": "int"}] ["int", "int"] int $1 + $2 sql udf_db test_account 2025-07-19 05:49:43 2025-07-19 05:49:43 FUNCTION DEFINER utf8mb4 utf8mb4_0900_ai_ci utf8mb4_0900_ai_ci ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION,NO_ZERO_DATE,NO_ZERO_IN_DATE,ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,ENABLE_BOOL_SUMAVG -2 concatenate 2 [{"name": "str1", "type": "varchar"}, {"name": "str2", "type": "varchar"}] ["varchar", "varchar"] varchar $1 + $2 sql udf_db test_account 2025-07-19 05:49:43 2025-07-19 05:49:43 FUNCTION DEFINER utf8mb4 utf8mb4_0900_ai_ci utf8mb4_0900_ai_ci ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION,NO_ZERO_DATE,NO_ZERO_IN_DATE,ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,ENABLE_BOOL_SUMAVG -3 subab 2 [{"name": "x", "type": "int"}, {"name": "y", "type": "int"}] ["int", "int"] int $1 - $2 sql udf_db2 test_account 2025-07-19 05:49:43 2025-07-19 05:49:43 FUNCTION DEFINER utf8mb4 utf8mb4_0900_ai_ci utf8mb4_0900_ai_ci ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION,NO_ZERO_DATE,NO_ZERO_IN_DATE,ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,ENABLE_BOOL_SUMAVG +function_id active_revision namespace_version name owner args arg_types canonical_input_descriptor return_descriptor signature_key_schema_version signature_fingerprint rettype body language db definer modified_time created_time type security_type comment character_set_client collation_connection database_collation sql_mode +1 1 1 addab 2 [{"name": "x", "type": "int"}, {"name": "y", "type": "int"}] ["int", "int"] 0 int $1 + $2 sql udf_db test_account 2025-07-19 05:49:43 2025-07-19 05:49:43 FUNCTION DEFINER utf8mb4 utf8mb4_0900_ai_ci utf8mb4_0900_ai_ci ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION,NO_ZERO_DATE,NO_ZERO_IN_DATE,ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,ENABLE_BOOL_SUMAVG +2 1 1 concatenate 2 [{"name": "str1", "type": "varchar"}, {"name": "str2", "type": "varchar"}] ["varchar", "varchar"] 0 varchar $1 + $2 sql udf_db test_account 2025-07-19 05:49:43 2025-07-19 05:49:43 FUNCTION DEFINER utf8mb4 utf8mb4_0900_ai_ci utf8mb4_0900_ai_ci ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION,NO_ZERO_DATE,NO_ZERO_IN_DATE,ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,ENABLE_BOOL_SUMAVG +3 1 1 subab 2 [{"name": "x", "type": "int"}, {"name": "y", "type": "int"}] ["int", "int"] 0 int $1 - $2 sql udf_db2 test_account 2025-07-19 05:49:43 2025-07-19 05:49:43 FUNCTION DEFINER utf8mb4 utf8mb4_0900_ai_ci utf8mb4_0900_ai_ci ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION,NO_ZERO_DATE,NO_ZERO_IN_DATE,ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,ENABLE_BOOL_SUMAVG restore account acc01{snapshot="udf_dsp02"}; select * from mo_catalog.mo_user_defined_function; -function_id name owner args arg_types rettype body language db definer modified_time created_time type security_type comment character_set_client collation_connection database_collation sql_mode -1 addab 2 [{"name": "x", "type": "int"}, {"name": "y", "type": "int"}] ["int", "int"] int $1 + $2 sql udf_db test_account 2025-07-19 05:49:43 2025-07-19 05:49:43 FUNCTION DEFINER utf8mb4 utf8mb4_0900_ai_ci utf8mb4_0900_ai_ci ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION,NO_ZERO_DATE,NO_ZERO_IN_DATE,ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,ENABLE_BOOL_SUMAVG -2 concatenate 2 [{"name": "str1", "type": "varchar"}, {"name": "str2", "type": "varchar"}] ["varchar", "varchar"] varchar $1 + $2 sql udf_db test_account 2025-07-19 05:49:43 2025-07-19 05:49:43 FUNCTION DEFINER utf8mb4 utf8mb4_0900_ai_ci utf8mb4_0900_ai_ci ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION,NO_ZERO_DATE,NO_ZERO_IN_DATE,ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,ENABLE_BOOL_SUMAVG +function_id active_revision namespace_version name owner args arg_types canonical_input_descriptor return_descriptor signature_key_schema_version signature_fingerprint rettype body language db definer modified_time created_time type security_type comment character_set_client collation_connection database_collation sql_mode +1 1 1 addab 2 [{"name": "x", "type": "int"}, {"name": "y", "type": "int"}] ["int", "int"] 0 int $1 + $2 sql udf_db test_account 2025-07-19 05:49:43 2025-07-19 05:49:43 FUNCTION DEFINER utf8mb4 utf8mb4_0900_ai_ci utf8mb4_0900_ai_ci ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION,NO_ZERO_DATE,NO_ZERO_IN_DATE,ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,ENABLE_BOOL_SUMAVG +2 1 1 concatenate 2 [{"name": "str1", "type": "varchar"}, {"name": "str2", "type": "varchar"}] ["varchar", "varchar"] 0 varchar $1 + $2 sql udf_db test_account 2025-07-19 05:49:43 2025-07-19 05:49:43 FUNCTION DEFINER utf8mb4 utf8mb4_0900_ai_ci utf8mb4_0900_ai_ci ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION,NO_ZERO_DATE,NO_ZERO_IN_DATE,ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,ENABLE_BOOL_SUMAVG drop database udf_db; drop snapshot udf_dsp01; drop snapshot udf_dsp02; @@ -82,17 +82,17 @@ create function `addab`(x int, y int) returns int language sql as '$1 + $2'; select * from mo_catalog.mo_user_defined_function; -function_id name owner args arg_types rettype body language db definer modified_time created_time type security_type comment character_set_client collation_connection database_collation sql_mode -10001 addab 2 [{"name": "x", "type": "int"}, {"name": "y", "type": "int"}] ["int", "int"] int $1 + $2 sql udf_db2 test_account 2025-07-19 05:49:43 2025-07-19 05:49:43 FUNCTION DEFINER utf8mb4 utf8mb4_0900_ai_ci utf8mb4_0900_ai_ci ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION,NO_ZERO_DATE,NO_ZERO_IN_DATE,ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,ENABLE_BOOL_SUMAVG +function_id active_revision namespace_version name owner args arg_types canonical_input_descriptor return_descriptor signature_key_schema_version signature_fingerprint rettype body language db definer modified_time created_time type security_type comment character_set_client collation_connection database_collation sql_mode +10001 1 1 addab 2 [{"name": "x", "type": "int"}, {"name": "y", "type": "int"}] ["int", "int"] 0 int $1 + $2 sql udf_db2 test_account 2025-07-19 05:49:43 2025-07-19 05:49:43 FUNCTION DEFINER utf8mb4 utf8mb4_0900_ai_ci utf8mb4_0900_ai_ci ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION,NO_ZERO_DATE,NO_ZERO_IN_DATE,ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,ENABLE_BOOL_SUMAVG drop snapshot if exists udf_sp04; create snapshot udf_sp04 for cluster; drop database udf_db2; select * from mo_catalog.mo_user_defined_function; -function_id name owner args arg_types rettype body language db definer modified_time created_time type security_type comment character_set_client collation_connection database_collation sql_mode +function_id active_revision namespace_version name owner args arg_types canonical_input_descriptor return_descriptor signature_key_schema_version signature_fingerprint rettype body language db definer modified_time created_time type security_type comment character_set_client collation_connection database_collation sql_mode restore account acc01{snapshot="udf_sp04"}; select * from mo_catalog.mo_user_defined_function; -function_id name owner args arg_types rettype body language db definer modified_time created_time type security_type comment character_set_client collation_connection database_collation sql_mode -10001 addab 2 [{"name": "x", "type": "int"}, {"name": "y", "type": "int"}] ["int", "int"] int $1 + $2 sql udf_db2 test_account 2025-07-19 05:49:43 2025-07-19 05:49:43 FUNCTION DEFINER utf8mb4 utf8mb4_0900_ai_ci utf8mb4_0900_ai_ci ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION,NO_ZERO_DATE,NO_ZERO_IN_DATE,ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,ENABLE_BOOL_SUMAVG +function_id active_revision namespace_version name owner args arg_types canonical_input_descriptor return_descriptor signature_key_schema_version signature_fingerprint rettype body language db definer modified_time created_time type security_type comment character_set_client collation_connection database_collation sql_mode +10001 1 1 addab 2 [{"name": "x", "type": "int"}, {"name": "y", "type": "int"}] ["int", "int"] 0 int $1 + $2 sql udf_db2 test_account 2025-07-19 05:49:43 2025-07-19 05:49:43 FUNCTION DEFINER utf8mb4 utf8mb4_0900_ai_ci utf8mb4_0900_ai_ci ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION,NO_ZERO_DATE,NO_ZERO_IN_DATE,ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,ENABLE_BOOL_SUMAVG drop database udf_db2; drop snapshot udf_sp04; drop database if exists procedure_test; diff --git a/test/distributed/cases/snapshot/cluster_level_snapshot_restore_system_table_to_nonsys.sql b/test/distributed/cases/snapshot/cluster_level_snapshot_restore_system_table_to_nonsys.sql index dc3faa13dd891..b786725664269 100644 --- a/test/distributed/cases/snapshot/cluster_level_snapshot_restore_system_table_to_nonsys.sql +++ b/test/distributed/cases/snapshot/cluster_level_snapshot_restore_system_table_to_nonsys.sql @@ -52,7 +52,7 @@ create snapshot udf_dsp03 for cluster; show snapshots; -- @session:id=1&user=acc01:test_account&password=111 --- @ignore:0,10,11 +-- @ignore:0,16,17 select * from mo_catalog.mo_user_defined_function; drop function subab(x int,y int); drop function udf_db.concatenate(str1 varchar(255), str2 varchar(255)); @@ -61,14 +61,14 @@ drop function udf_db.concatenate(str1 varchar(255), str2 varchar(255)); restore account acc01{snapshot="udf_dsp03"}; -- @session:id=1&user=acc01:test_account&password=111 --- @ignore:0,10,11 +-- @ignore:0,16,17 select * from mo_catalog.mo_user_defined_function; -- @session restore account acc01{snapshot="udf_dsp02"}; -- @session:id=1&user=acc01:test_account&password=111 --- @ignore:0,10,11 +-- @ignore:0,16,17 select * from mo_catalog.mo_user_defined_function; -- @session @@ -90,7 +90,7 @@ use udf_db2; create function `addab`(x int, y int) returns int language sql as '$1 + $2'; --- @ignore:0,10,11 +-- @ignore:0,16,17 select * from mo_catalog.mo_user_defined_function; -- @session @@ -99,14 +99,14 @@ create snapshot udf_sp04 for cluster; -- @session:id=1&user=acc01:test_account&password=111 drop database udf_db2; --- @ignore:0,10,11 +-- @ignore:0,16,17 select * from mo_catalog.mo_user_defined_function; -- @session restore account acc01{snapshot="udf_sp04"}; -- @session:id=1&user=acc01:test_account&password=111 --- @ignore:0,10,11 +-- @ignore:0,16,17 select * from mo_catalog.mo_user_defined_function; drop database udf_db2; -- @session diff --git a/test/distributed/cases/snapshot/cluster_level_snapshot_restore_system_table_to_sys_account.result b/test/distributed/cases/snapshot/cluster_level_snapshot_restore_system_table_to_sys_account.result index b45565cde0830..c6f28e62c6c28 100644 --- a/test/distributed/cases/snapshot/cluster_level_snapshot_restore_system_table_to_sys_account.result +++ b/test/distributed/cases/snapshot/cluster_level_snapshot_restore_system_table_to_sys_account.result @@ -52,18 +52,18 @@ udf_dsp03 2025-07-19 05:49:49.217379 cluster udf_dsp02 2025-07-19 05:49:49.192607 cluster udf_dsp01 2025-07-19 05:49:49.180259 cluster select * from mo_catalog.mo_user_defined_function; -function_id name owner args arg_types rettype body language db definer modified_time created_time type security_type comment character_set_client collation_connection database_collation sql_mode -1250000 addab 0 [{"name": "x", "type": "int"}, {"name": "y", "type": "int"}] ["int", "int"] int $1 + $2 sql udf_db dump 2025-07-19 05:49:49 2025-07-19 05:49:49 FUNCTION DEFINER utf8mb4 utf8mb4_0900_ai_ci utf8mb4_0900_ai_ci ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION,NO_ZERO_DATE,NO_ZERO_IN_DATE,ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,ENABLE_BOOL_SUMAVG -1250001 concatenate 0 [{"name": "str1", "type": "varchar"}, {"name": "str2", "type": "varchar"}] ["varchar", "varchar"] varchar $1 + $2 sql udf_db dump 2025-07-19 05:49:49 2025-07-19 05:49:49 FUNCTION DEFINER utf8mb4 utf8mb4_0900_ai_ci utf8mb4_0900_ai_ci ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION,NO_ZERO_DATE,NO_ZERO_IN_DATE,ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,ENABLE_BOOL_SUMAVG -1250002 subab 0 [{"name": "x", "type": "int"}, {"name": "y", "type": "int"}] ["int", "int"] int $1 - $2 sql udf_db2 dump 2025-07-19 05:49:49 2025-07-19 05:49:49 FUNCTION DEFINER utf8mb4 utf8mb4_0900_ai_ci utf8mb4_0900_ai_ci ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION,NO_ZERO_DATE,NO_ZERO_IN_DATE,ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,ENABLE_BOOL_SUMAVG +function_id active_revision namespace_version name owner args arg_types canonical_input_descriptor return_descriptor signature_key_schema_version signature_fingerprint rettype body language db definer modified_time created_time type security_type comment character_set_client collation_connection database_collation sql_mode +1250000 1 1 addab 0 [{"name": "x", "type": "int"}, {"name": "y", "type": "int"}] ["int", "int"] 0 int $1 + $2 sql udf_db dump 2025-07-19 05:49:49 2025-07-19 05:49:49 FUNCTION DEFINER utf8mb4 utf8mb4_0900_ai_ci utf8mb4_0900_ai_ci ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION,NO_ZERO_DATE,NO_ZERO_IN_DATE,ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,ENABLE_BOOL_SUMAVG +1250001 1 1 concatenate 0 [{"name": "str1", "type": "varchar"}, {"name": "str2", "type": "varchar"}] ["varchar", "varchar"] 0 varchar $1 + $2 sql udf_db dump 2025-07-19 05:49:49 2025-07-19 05:49:49 FUNCTION DEFINER utf8mb4 utf8mb4_0900_ai_ci utf8mb4_0900_ai_ci ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION,NO_ZERO_DATE,NO_ZERO_IN_DATE,ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,ENABLE_BOOL_SUMAVG +1250002 1 1 subab 0 [{"name": "x", "type": "int"}, {"name": "y", "type": "int"}] ["int", "int"] 0 int $1 - $2 sql udf_db2 dump 2025-07-19 05:49:49 2025-07-19 05:49:49 FUNCTION DEFINER utf8mb4 utf8mb4_0900_ai_ci utf8mb4_0900_ai_ci ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION,NO_ZERO_DATE,NO_ZERO_IN_DATE,ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,ENABLE_BOOL_SUMAVG drop function subab(x int,y int); drop function udf_db.concatenate(str1 varchar(255), str2 varchar(255)); restore account sys{snapshot="udf_dsp03"}; select * from mo_catalog.mo_user_defined_function; -function_id name owner args arg_types rettype body language db definer modified_time created_time type security_type comment character_set_client collation_connection database_collation sql_mode -1250000 addab 0 [{"name": "x", "type": "int"}, {"name": "y", "type": "int"}] ["int", "int"] int $1 + $2 sql udf_db dump 2025-07-19 05:49:49 2025-07-19 05:49:49 FUNCTION DEFINER utf8mb4 utf8mb4_0900_ai_ci utf8mb4_0900_ai_ci ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION,NO_ZERO_DATE,NO_ZERO_IN_DATE,ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,ENABLE_BOOL_SUMAVG -1250001 concatenate 0 [{"name": "str1", "type": "varchar"}, {"name": "str2", "type": "varchar"}] ["varchar", "varchar"] varchar $1 + $2 sql udf_db dump 2025-07-19 05:49:49 2025-07-19 05:49:49 FUNCTION DEFINER utf8mb4 utf8mb4_0900_ai_ci utf8mb4_0900_ai_ci ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION,NO_ZERO_DATE,NO_ZERO_IN_DATE,ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,ENABLE_BOOL_SUMAVG -1250002 subab 0 [{"name": "x", "type": "int"}, {"name": "y", "type": "int"}] ["int", "int"] int $1 - $2 sql udf_db2 dump 2025-07-19 05:49:49 2025-07-19 05:49:49 FUNCTION DEFINER utf8mb4 utf8mb4_0900_ai_ci utf8mb4_0900_ai_ci ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION,NO_ZERO_DATE,NO_ZERO_IN_DATE,ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,ENABLE_BOOL_SUMAVG +function_id active_revision namespace_version name owner args arg_types canonical_input_descriptor return_descriptor signature_key_schema_version signature_fingerprint rettype body language db definer modified_time created_time type security_type comment character_set_client collation_connection database_collation sql_mode +1250000 1 1 addab 0 [{"name": "x", "type": "int"}, {"name": "y", "type": "int"}] ["int", "int"] 0 int $1 + $2 sql udf_db dump 2025-07-19 05:49:49 2025-07-19 05:49:49 FUNCTION DEFINER utf8mb4 utf8mb4_0900_ai_ci utf8mb4_0900_ai_ci ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION,NO_ZERO_DATE,NO_ZERO_IN_DATE,ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,ENABLE_BOOL_SUMAVG +1250001 1 1 concatenate 0 [{"name": "str1", "type": "varchar"}, {"name": "str2", "type": "varchar"}] ["varchar", "varchar"] 0 varchar $1 + $2 sql udf_db dump 2025-07-19 05:49:49 2025-07-19 05:49:49 FUNCTION DEFINER utf8mb4 utf8mb4_0900_ai_ci utf8mb4_0900_ai_ci ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION,NO_ZERO_DATE,NO_ZERO_IN_DATE,ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,ENABLE_BOOL_SUMAVG +1250002 1 1 subab 0 [{"name": "x", "type": "int"}, {"name": "y", "type": "int"}] ["int", "int"] 0 int $1 - $2 sql udf_db2 dump 2025-07-19 05:49:49 2025-07-19 05:49:49 FUNCTION DEFINER utf8mb4 utf8mb4_0900_ai_ci utf8mb4_0900_ai_ci ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION,NO_ZERO_DATE,NO_ZERO_IN_DATE,ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,ENABLE_BOOL_SUMAVG show snapshots; SNAPSHOT_NAME TIMESTAMP SNAPSHOT_LEVEL ACCOUNT_NAME DATABASE_NAME TABLE_NAME udf_dsp03 2025-07-19 05:49:49.217379 cluster @@ -71,9 +71,9 @@ udf_dsp02 2025-07-19 05:49:49.192607 cluster udf_dsp01 2025-07-19 05:49:49.180259 cluster restore account sys{snapshot="udf_dsp02"}; select * from mo_catalog.mo_user_defined_function; -function_id name owner args arg_types rettype body language db definer modified_time created_time type security_type comment character_set_client collation_connection database_collation sql_mode -1250000 addab 0 [{"name": "x", "type": "int"}, {"name": "y", "type": "int"}] ["int", "int"] int $1 + $2 sql udf_db dump 2025-07-19 05:49:49 2025-07-19 05:49:49 FUNCTION DEFINER utf8mb4 utf8mb4_0900_ai_ci utf8mb4_0900_ai_ci ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION,NO_ZERO_DATE,NO_ZERO_IN_DATE,ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,ENABLE_BOOL_SUMAVG -1250001 concatenate 0 [{"name": "str1", "type": "varchar"}, {"name": "str2", "type": "varchar"}] ["varchar", "varchar"] varchar $1 + $2 sql udf_db dump 2025-07-19 05:49:49 2025-07-19 05:49:49 FUNCTION DEFINER utf8mb4 utf8mb4_0900_ai_ci utf8mb4_0900_ai_ci ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION,NO_ZERO_DATE,NO_ZERO_IN_DATE,ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,ENABLE_BOOL_SUMAVG +function_id active_revision namespace_version name owner args arg_types canonical_input_descriptor return_descriptor signature_key_schema_version signature_fingerprint rettype body language db definer modified_time created_time type security_type comment character_set_client collation_connection database_collation sql_mode +1250000 1 1 addab 0 [{"name": "x", "type": "int"}, {"name": "y", "type": "int"}] ["int", "int"] 0 int $1 + $2 sql udf_db dump 2025-07-19 05:49:49 2025-07-19 05:49:49 FUNCTION DEFINER utf8mb4 utf8mb4_0900_ai_ci utf8mb4_0900_ai_ci ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION,NO_ZERO_DATE,NO_ZERO_IN_DATE,ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,ENABLE_BOOL_SUMAVG +1250001 1 1 concatenate 0 [{"name": "str1", "type": "varchar"}, {"name": "str2", "type": "varchar"}] ["varchar", "varchar"] 0 varchar $1 + $2 sql udf_db dump 2025-07-19 05:49:49 2025-07-19 05:49:49 FUNCTION DEFINER utf8mb4 utf8mb4_0900_ai_ci utf8mb4_0900_ai_ci ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION,NO_ZERO_DATE,NO_ZERO_IN_DATE,ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,ENABLE_BOOL_SUMAVG show snapshots; SNAPSHOT_NAME TIMESTAMP SNAPSHOT_LEVEL ACCOUNT_NAME DATABASE_NAME TABLE_NAME udf_dsp03 2025-07-19 05:49:49.217379 cluster @@ -90,17 +90,17 @@ create function `addab`(x int, y int) returns int language sql as '$1 + $2'; select * from mo_catalog.mo_user_defined_function; -function_id name owner args arg_types rettype body language db definer modified_time created_time type security_type comment character_set_client collation_connection database_collation sql_mode -1260000 addab 0 [{"name": "x", "type": "int"}, {"name": "y", "type": "int"}] ["int", "int"] int $1 + $2 sql udf_db2 dump 2025-07-19 05:49:49 2025-07-19 05:49:49 FUNCTION DEFINER utf8mb4 utf8mb4_0900_ai_ci utf8mb4_0900_ai_ci ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION,NO_ZERO_DATE,NO_ZERO_IN_DATE,ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,ENABLE_BOOL_SUMAVG +function_id active_revision namespace_version name owner args arg_types canonical_input_descriptor return_descriptor signature_key_schema_version signature_fingerprint rettype body language db definer modified_time created_time type security_type comment character_set_client collation_connection database_collation sql_mode +1260000 1 1 addab 0 [{"name": "x", "type": "int"}, {"name": "y", "type": "int"}] ["int", "int"] 0 int $1 + $2 sql udf_db2 dump 2025-07-19 05:49:49 2025-07-19 05:49:49 FUNCTION DEFINER utf8mb4 utf8mb4_0900_ai_ci utf8mb4_0900_ai_ci ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION,NO_ZERO_DATE,NO_ZERO_IN_DATE,ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,ENABLE_BOOL_SUMAVG drop snapshot if exists udf_sp04; create snapshot udf_sp04 for cluster; drop database udf_db2; select * from mo_catalog.mo_user_defined_function; -function_id name owner args arg_types rettype body language db definer modified_time created_time type security_type comment character_set_client collation_connection database_collation sql_mode +function_id active_revision namespace_version name owner args arg_types canonical_input_descriptor return_descriptor signature_key_schema_version signature_fingerprint rettype body language db definer modified_time created_time type security_type comment character_set_client collation_connection database_collation sql_mode restore account sys{snapshot="udf_sp04"}; select * from mo_catalog.mo_user_defined_function; -function_id name owner args arg_types rettype body language db definer modified_time created_time type security_type comment character_set_client collation_connection database_collation sql_mode -1260000 addab 0 [{"name": "x", "type": "int"}, {"name": "y", "type": "int"}] ["int", "int"] int $1 + $2 sql udf_db2 dump 2025-07-19 05:49:49 2025-07-19 05:49:49 FUNCTION DEFINER utf8mb4 utf8mb4_0900_ai_ci utf8mb4_0900_ai_ci ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION,NO_ZERO_DATE,NO_ZERO_IN_DATE,ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,ENABLE_BOOL_SUMAVG +function_id active_revision namespace_version name owner args arg_types canonical_input_descriptor return_descriptor signature_key_schema_version signature_fingerprint rettype body language db definer modified_time created_time type security_type comment character_set_client collation_connection database_collation sql_mode +1260000 1 1 addab 0 [{"name": "x", "type": "int"}, {"name": "y", "type": "int"}] ["int", "int"] 0 int $1 + $2 sql udf_db2 dump 2025-07-19 05:49:49 2025-07-19 05:49:49 FUNCTION DEFINER utf8mb4 utf8mb4_0900_ai_ci utf8mb4_0900_ai_ci ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION,NO_ZERO_DATE,NO_ZERO_IN_DATE,ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,ENABLE_BOOL_SUMAVG drop database udf_db2; drop snapshot udf_sp04; drop database if exists procedure_test; diff --git a/test/distributed/cases/snapshot/cluster_level_snapshot_restore_system_table_to_sys_account.sql b/test/distributed/cases/snapshot/cluster_level_snapshot_restore_system_table_to_sys_account.sql index 102958a93ed79..09e35f428202a 100644 --- a/test/distributed/cases/snapshot/cluster_level_snapshot_restore_system_table_to_sys_account.sql +++ b/test/distributed/cases/snapshot/cluster_level_snapshot_restore_system_table_to_sys_account.sql @@ -43,20 +43,20 @@ create snapshot udf_dsp03 for cluster; -- @ignore:0,1 show snapshots; --- @ignore:0,10,11 +-- @ignore:0,16,17 select * from mo_catalog.mo_user_defined_function; drop function subab(x int,y int); drop function udf_db.concatenate(str1 varchar(255), str2 varchar(255)); restore account sys{snapshot="udf_dsp03"}; --- @ignore:0,10,11 +-- @ignore:0,16,17 select * from mo_catalog.mo_user_defined_function; -- @ignore:0,1 show snapshots; restore account sys{snapshot="udf_dsp02"}; --- @ignore:0,10,11 +-- @ignore:0,16,17 select * from mo_catalog.mo_user_defined_function; -- @ignore:0,1 show snapshots; @@ -76,7 +76,7 @@ use udf_db2; create function `addab`(x int, y int) returns int language sql as '$1 + $2'; --- @ignore:0,10,11 +-- @ignore:0,16,17 select * from mo_catalog.mo_user_defined_function; drop snapshot if exists udf_sp04; @@ -86,7 +86,7 @@ drop database udf_db2; select * from mo_catalog.mo_user_defined_function; restore account sys{snapshot="udf_sp04"}; --- @ignore:0,10,11 +-- @ignore:0,16,17 select * from mo_catalog.mo_user_defined_function; drop database udf_db2; drop snapshot udf_sp04; diff --git a/test/distributed/cases/snapshot/nonsys_restore_system_table_to_nonsys_account.result b/test/distributed/cases/snapshot/nonsys_restore_system_table_to_nonsys_account.result index 90421a7e5c04e..09aa8d9cee4e1 100644 --- a/test/distributed/cases/snapshot/nonsys_restore_system_table_to_nonsys_account.result +++ b/test/distributed/cases/snapshot/nonsys_restore_system_table_to_nonsys_account.result @@ -54,18 +54,18 @@ udf_dsp03 2025-07-19 05:50:25.393332 account acc01 udf_dsp02 2025-07-19 05:50:25.371637 account acc01 udf_dsp01 2025-07-19 05:50:25.354111 account acc01 select * from mo_catalog.mo_user_defined_function; -function_id name owner args arg_types rettype body language db definer modified_time created_time type security_type comment character_set_client collation_connection database_collation sql_mode -1 addab 2 [{"name": "x", "type": "int"}, {"name": "y", "type": "int"}] ["int", "int"] int $1 + $2 sql udf_db test_account 2025-07-19 05:50:25 2025-07-19 05:50:25 FUNCTION DEFINER utf8mb4 utf8mb4_0900_ai_ci utf8mb4_0900_ai_ci ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION,NO_ZERO_DATE,NO_ZERO_IN_DATE,ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,ENABLE_BOOL_SUMAVG -2 concatenate 2 [{"name": "str1", "type": "varchar"}, {"name": "str2", "type": "varchar"}] ["varchar", "varchar"] varchar $1 + $2 sql udf_db test_account 2025-07-19 05:50:25 2025-07-19 05:50:25 FUNCTION DEFINER utf8mb4 utf8mb4_0900_ai_ci utf8mb4_0900_ai_ci ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION,NO_ZERO_DATE,NO_ZERO_IN_DATE,ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,ENABLE_BOOL_SUMAVG -3 subab 2 [{"name": "x", "type": "int"}, {"name": "y", "type": "int"}] ["int", "int"] int $1 - $2 sql udf_db2 test_account 2025-07-19 05:50:25 2025-07-19 05:50:25 FUNCTION DEFINER utf8mb4 utf8mb4_0900_ai_ci utf8mb4_0900_ai_ci ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION,NO_ZERO_DATE,NO_ZERO_IN_DATE,ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,ENABLE_BOOL_SUMAVG +function_id active_revision namespace_version name owner args arg_types canonical_input_descriptor return_descriptor signature_key_schema_version signature_fingerprint rettype body language db definer modified_time created_time type security_type comment character_set_client collation_connection database_collation sql_mode +1 1 1 addab 2 [{"name": "x", "type": "int"}, {"name": "y", "type": "int"}] ["int", "int"] 0 int $1 + $2 sql udf_db test_account 2025-07-19 05:50:25 2025-07-19 05:50:25 FUNCTION DEFINER utf8mb4 utf8mb4_0900_ai_ci utf8mb4_0900_ai_ci ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION,NO_ZERO_DATE,NO_ZERO_IN_DATE,ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,ENABLE_BOOL_SUMAVG +2 1 1 concatenate 2 [{"name": "str1", "type": "varchar"}, {"name": "str2", "type": "varchar"}] ["varchar", "varchar"] 0 varchar $1 + $2 sql udf_db test_account 2025-07-19 05:50:25 2025-07-19 05:50:25 FUNCTION DEFINER utf8mb4 utf8mb4_0900_ai_ci utf8mb4_0900_ai_ci ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION,NO_ZERO_DATE,NO_ZERO_IN_DATE,ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,ENABLE_BOOL_SUMAVG +3 1 1 subab 2 [{"name": "x", "type": "int"}, {"name": "y", "type": "int"}] ["int", "int"] 0 int $1 - $2 sql udf_db2 test_account 2025-07-19 05:50:25 2025-07-19 05:50:25 FUNCTION DEFINER utf8mb4 utf8mb4_0900_ai_ci utf8mb4_0900_ai_ci ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION,NO_ZERO_DATE,NO_ZERO_IN_DATE,ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,ENABLE_BOOL_SUMAVG drop function subab(x int,y int); drop function udf_db.concatenate(str1 varchar(255), str2 varchar(255)); restore account acc01{snapshot="udf_dsp03"}; select * from mo_catalog.mo_user_defined_function; -function_id name owner args arg_types rettype body language db definer modified_time created_time type security_type comment character_set_client collation_connection database_collation sql_mode -1 addab 2 [{"name": "x", "type": "int"}, {"name": "y", "type": "int"}] ["int", "int"] int $1 + $2 sql udf_db test_account 2025-07-19 05:50:25 2025-07-19 05:50:25 FUNCTION DEFINER utf8mb4 utf8mb4_0900_ai_ci utf8mb4_0900_ai_ci ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION,NO_ZERO_DATE,NO_ZERO_IN_DATE,ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,ENABLE_BOOL_SUMAVG -2 concatenate 2 [{"name": "str1", "type": "varchar"}, {"name": "str2", "type": "varchar"}] ["varchar", "varchar"] varchar $1 + $2 sql udf_db test_account 2025-07-19 05:50:25 2025-07-19 05:50:25 FUNCTION DEFINER utf8mb4 utf8mb4_0900_ai_ci utf8mb4_0900_ai_ci ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION,NO_ZERO_DATE,NO_ZERO_IN_DATE,ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,ENABLE_BOOL_SUMAVG -3 subab 2 [{"name": "x", "type": "int"}, {"name": "y", "type": "int"}] ["int", "int"] int $1 - $2 sql udf_db2 test_account 2025-07-19 05:50:25 2025-07-19 05:50:25 FUNCTION DEFINER utf8mb4 utf8mb4_0900_ai_ci utf8mb4_0900_ai_ci ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION,NO_ZERO_DATE,NO_ZERO_IN_DATE,ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,ENABLE_BOOL_SUMAVG +function_id active_revision namespace_version name owner args arg_types canonical_input_descriptor return_descriptor signature_key_schema_version signature_fingerprint rettype body language db definer modified_time created_time type security_type comment character_set_client collation_connection database_collation sql_mode +1 1 1 addab 2 [{"name": "x", "type": "int"}, {"name": "y", "type": "int"}] ["int", "int"] 0 int $1 + $2 sql udf_db test_account 2025-07-19 05:50:25 2025-07-19 05:50:25 FUNCTION DEFINER utf8mb4 utf8mb4_0900_ai_ci utf8mb4_0900_ai_ci ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION,NO_ZERO_DATE,NO_ZERO_IN_DATE,ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,ENABLE_BOOL_SUMAVG +2 1 1 concatenate 2 [{"name": "str1", "type": "varchar"}, {"name": "str2", "type": "varchar"}] ["varchar", "varchar"] 0 varchar $1 + $2 sql udf_db test_account 2025-07-19 05:50:25 2025-07-19 05:50:25 FUNCTION DEFINER utf8mb4 utf8mb4_0900_ai_ci utf8mb4_0900_ai_ci ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION,NO_ZERO_DATE,NO_ZERO_IN_DATE,ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,ENABLE_BOOL_SUMAVG +3 1 1 subab 2 [{"name": "x", "type": "int"}, {"name": "y", "type": "int"}] ["int", "int"] 0 int $1 - $2 sql udf_db2 test_account 2025-07-19 05:50:25 2025-07-19 05:50:25 FUNCTION DEFINER utf8mb4 utf8mb4_0900_ai_ci utf8mb4_0900_ai_ci ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION,NO_ZERO_DATE,NO_ZERO_IN_DATE,ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,ENABLE_BOOL_SUMAVG show snapshots; SNAPSHOT_NAME TIMESTAMP SNAPSHOT_LEVEL ACCOUNT_NAME DATABASE_NAME TABLE_NAME udf_dsp03 2025-07-19 05:50:25.393332 account acc01 @@ -73,9 +73,9 @@ udf_dsp02 2025-07-19 05:50:25.371637 account acc01 udf_dsp01 2025-07-19 05:50:25.354111 account acc01 restore account acc01{snapshot="udf_dsp02"}; select * from mo_catalog.mo_user_defined_function; -function_id name owner args arg_types rettype body language db definer modified_time created_time type security_type comment character_set_client collation_connection database_collation sql_mode -1 addab 2 [{"name": "x", "type": "int"}, {"name": "y", "type": "int"}] ["int", "int"] int $1 + $2 sql udf_db test_account 2025-07-19 05:50:25 2025-07-19 05:50:25 FUNCTION DEFINER utf8mb4 utf8mb4_0900_ai_ci utf8mb4_0900_ai_ci ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION,NO_ZERO_DATE,NO_ZERO_IN_DATE,ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,ENABLE_BOOL_SUMAVG -2 concatenate 2 [{"name": "str1", "type": "varchar"}, {"name": "str2", "type": "varchar"}] ["varchar", "varchar"] varchar $1 + $2 sql udf_db test_account 2025-07-19 05:50:25 2025-07-19 05:50:25 FUNCTION DEFINER utf8mb4 utf8mb4_0900_ai_ci utf8mb4_0900_ai_ci ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION,NO_ZERO_DATE,NO_ZERO_IN_DATE,ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,ENABLE_BOOL_SUMAVG +function_id active_revision namespace_version name owner args arg_types canonical_input_descriptor return_descriptor signature_key_schema_version signature_fingerprint rettype body language db definer modified_time created_time type security_type comment character_set_client collation_connection database_collation sql_mode +1 1 1 addab 2 [{"name": "x", "type": "int"}, {"name": "y", "type": "int"}] ["int", "int"] 0 int $1 + $2 sql udf_db test_account 2025-07-19 05:50:25 2025-07-19 05:50:25 FUNCTION DEFINER utf8mb4 utf8mb4_0900_ai_ci utf8mb4_0900_ai_ci ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION,NO_ZERO_DATE,NO_ZERO_IN_DATE,ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,ENABLE_BOOL_SUMAVG +2 1 1 concatenate 2 [{"name": "str1", "type": "varchar"}, {"name": "str2", "type": "varchar"}] ["varchar", "varchar"] 0 varchar $1 + $2 sql udf_db test_account 2025-07-19 05:50:25 2025-07-19 05:50:25 FUNCTION DEFINER utf8mb4 utf8mb4_0900_ai_ci utf8mb4_0900_ai_ci ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION,NO_ZERO_DATE,NO_ZERO_IN_DATE,ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,ENABLE_BOOL_SUMAVG show snapshots; SNAPSHOT_NAME TIMESTAMP SNAPSHOT_LEVEL ACCOUNT_NAME DATABASE_NAME TABLE_NAME udf_dsp03 2025-07-19 05:50:25.393332 account acc01 @@ -92,17 +92,17 @@ create function `addAB`(x int, y int) returns int language sql as '$1 + $2'; select * from mo_catalog.mo_user_defined_function; -function_id name owner args arg_types rettype body language db definer modified_time created_time type security_type comment character_set_client collation_connection database_collation sql_mode -10001 addab 2 [{"name": "x", "type": "int"}, {"name": "y", "type": "int"}] ["int", "int"] int $1 + $2 sql udf_db2 test_account 2025-07-19 05:50:25 2025-07-19 05:50:25 FUNCTION DEFINER utf8mb4 utf8mb4_0900_ai_ci utf8mb4_0900_ai_ci ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION,NO_ZERO_DATE,NO_ZERO_IN_DATE,ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,ENABLE_BOOL_SUMAVG +function_id active_revision namespace_version name owner args arg_types canonical_input_descriptor return_descriptor signature_key_schema_version signature_fingerprint rettype body language db definer modified_time created_time type security_type comment character_set_client collation_connection database_collation sql_mode +10001 1 1 addab 2 [{"name": "x", "type": "int"}, {"name": "y", "type": "int"}] ["int", "int"] 0 int $1 + $2 sql udf_db2 test_account 2025-07-19 05:50:25 2025-07-19 05:50:25 FUNCTION DEFINER utf8mb4 utf8mb4_0900_ai_ci utf8mb4_0900_ai_ci ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION,NO_ZERO_DATE,NO_ZERO_IN_DATE,ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,ENABLE_BOOL_SUMAVG drop snapshot if exists udf_sp04; create snapshot udf_sp04 for account acc01; drop database udf_db2; select * from mo_catalog.mo_user_defined_function; -function_id name owner args arg_types rettype body language db definer modified_time created_time type security_type comment character_set_client collation_connection database_collation sql_mode +function_id active_revision namespace_version name owner args arg_types canonical_input_descriptor return_descriptor signature_key_schema_version signature_fingerprint rettype body language db definer modified_time created_time type security_type comment character_set_client collation_connection database_collation sql_mode restore account acc01{snapshot="udf_sp04"}; select * from mo_catalog.mo_user_defined_function; -function_id name owner args arg_types rettype body language db definer modified_time created_time type security_type comment character_set_client collation_connection database_collation sql_mode -10001 addab 2 [{"name": "x", "type": "int"}, {"name": "y", "type": "int"}] ["int", "int"] int $1 + $2 sql udf_db2 test_account 2025-07-19 05:50:25 2025-07-19 05:50:25 FUNCTION DEFINER utf8mb4 utf8mb4_0900_ai_ci utf8mb4_0900_ai_ci ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION,NO_ZERO_DATE,NO_ZERO_IN_DATE,ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,ENABLE_BOOL_SUMAVG +function_id active_revision namespace_version name owner args arg_types canonical_input_descriptor return_descriptor signature_key_schema_version signature_fingerprint rettype body language db definer modified_time created_time type security_type comment character_set_client collation_connection database_collation sql_mode +10001 1 1 addab 2 [{"name": "x", "type": "int"}, {"name": "y", "type": "int"}] ["int", "int"] 0 int $1 + $2 sql udf_db2 test_account 2025-07-19 05:50:25 2025-07-19 05:50:25 FUNCTION DEFINER utf8mb4 utf8mb4_0900_ai_ci utf8mb4_0900_ai_ci ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION,NO_ZERO_DATE,NO_ZERO_IN_DATE,ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,ENABLE_BOOL_SUMAVG drop database udf_db2; drop snapshot udf_sp04; drop database if exists procedure_test; diff --git a/test/distributed/cases/snapshot/nonsys_restore_system_table_to_nonsys_account.sql b/test/distributed/cases/snapshot/nonsys_restore_system_table_to_nonsys_account.sql index 8e04ccaf9653c..307ea3c26803c 100644 --- a/test/distributed/cases/snapshot/nonsys_restore_system_table_to_nonsys_account.sql +++ b/test/distributed/cases/snapshot/nonsys_restore_system_table_to_nonsys_account.sql @@ -46,20 +46,20 @@ create snapshot udf_dsp03 for account acc01; -- @ignore:1 show snapshots; --- @ignore:0,10,11 +-- @ignore:0,16,17 select * from mo_catalog.mo_user_defined_function; drop function subab(x int,y int); drop function udf_db.concatenate(str1 varchar(255), str2 varchar(255)); restore account acc01{snapshot="udf_dsp03"}; --- @ignore:0,10,11 +-- @ignore:0,16,17 select * from mo_catalog.mo_user_defined_function; -- @ignore:1 show snapshots; restore account acc01{snapshot="udf_dsp02"}; --- @ignore:0,10,11 +-- @ignore:0,16,17 select * from mo_catalog.mo_user_defined_function; -- @ignore:1 show snapshots; @@ -79,7 +79,7 @@ use udf_db2; create function `addAB`(x int, y int) returns int language sql as '$1 + $2'; --- @ignore:0,10,11 +-- @ignore:0,16,17 select * from mo_catalog.mo_user_defined_function; drop snapshot if exists udf_sp04; @@ -89,7 +89,7 @@ drop database udf_db2; select * from mo_catalog.mo_user_defined_function; restore account acc01{snapshot="udf_sp04"}; --- @ignore:0,10,11 +-- @ignore:0,16,17 select * from mo_catalog.mo_user_defined_function; drop database udf_db2; drop snapshot udf_sp04; diff --git a/test/distributed/cases/snapshot/sys_restore_system_table_to_newnonsys_account.result b/test/distributed/cases/snapshot/sys_restore_system_table_to_newnonsys_account.result index 938b13d74d6a0..9a03d829a5cbe 100644 --- a/test/distributed/cases/snapshot/sys_restore_system_table_to_newnonsys_account.result +++ b/test/distributed/cases/snapshot/sys_restore_system_table_to_newnonsys_account.result @@ -56,23 +56,23 @@ udf_dsp03 2025-07-19 05:52:33.805599 account acc01 udf_dsp02 2025-07-19 05:52:33.782182 account acc01 udf_dsp01 2025-07-19 05:52:33.77066 account acc01 select * from mo_catalog.mo_user_defined_function; -function_id name owner args arg_types rettype body language db definer modified_time created_time type security_type comment character_set_client collation_connection database_collation sql_mode -1 addab 2 [{"name": "x", "type": "int"}, {"name": "y", "type": "int"}] ["int", "int"] int $1 + $2 sql udf_db test_account 2025-07-19 05:52:33 2025-07-19 05:52:33 FUNCTION DEFINER utf8mb4 utf8mb4_0900_ai_ci utf8mb4_0900_ai_ci ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION,NO_ZERO_DATE,NO_ZERO_IN_DATE,ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,ENABLE_BOOL_SUMAVG -2 concatenate 2 [{"name": "str1", "type": "varchar"}, {"name": "str2", "type": "varchar"}] ["varchar", "varchar"] varchar $1 + $2 sql udf_db test_account 2025-07-19 05:52:33 2025-07-19 05:52:33 FUNCTION DEFINER utf8mb4 utf8mb4_0900_ai_ci utf8mb4_0900_ai_ci ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION,NO_ZERO_DATE,NO_ZERO_IN_DATE,ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,ENABLE_BOOL_SUMAVG -3 subab 2 [{"name": "x", "type": "int"}, {"name": "y", "type": "int"}] ["int", "int"] int $1 - $2 sql udf_db2 test_account 2025-07-19 05:52:33 2025-07-19 05:52:33 FUNCTION DEFINER utf8mb4 utf8mb4_0900_ai_ci utf8mb4_0900_ai_ci ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION,NO_ZERO_DATE,NO_ZERO_IN_DATE,ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,ENABLE_BOOL_SUMAVG +function_id active_revision namespace_version name owner args arg_types canonical_input_descriptor return_descriptor signature_key_schema_version signature_fingerprint rettype body language db definer modified_time created_time type security_type comment character_set_client collation_connection database_collation sql_mode +1 1 1 addab 2 [{"name": "x", "type": "int"}, {"name": "y", "type": "int"}] ["int", "int"] 0 int $1 + $2 sql udf_db test_account 2025-07-19 05:52:33 2025-07-19 05:52:33 FUNCTION DEFINER utf8mb4 utf8mb4_0900_ai_ci utf8mb4_0900_ai_ci ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION,NO_ZERO_DATE,NO_ZERO_IN_DATE,ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,ENABLE_BOOL_SUMAVG +2 1 1 concatenate 2 [{"name": "str1", "type": "varchar"}, {"name": "str2", "type": "varchar"}] ["varchar", "varchar"] 0 varchar $1 + $2 sql udf_db test_account 2025-07-19 05:52:33 2025-07-19 05:52:33 FUNCTION DEFINER utf8mb4 utf8mb4_0900_ai_ci utf8mb4_0900_ai_ci ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION,NO_ZERO_DATE,NO_ZERO_IN_DATE,ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,ENABLE_BOOL_SUMAVG +3 1 1 subab 2 [{"name": "x", "type": "int"}, {"name": "y", "type": "int"}] ["int", "int"] 0 int $1 - $2 sql udf_db2 test_account 2025-07-19 05:52:33 2025-07-19 05:52:33 FUNCTION DEFINER utf8mb4 utf8mb4_0900_ai_ci utf8mb4_0900_ai_ci ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION,NO_ZERO_DATE,NO_ZERO_IN_DATE,ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,ENABLE_BOOL_SUMAVG drop function subab(x int,y int); drop function udf_db.concatenate(str1 varchar(255), str2 varchar(255)); restore account acc01{snapshot="udf_dsp03"} to account acc02; select * from mo_catalog.mo_user_defined_function; -function_id name owner args arg_types rettype body language db definer modified_time created_time type security_type comment character_set_client collation_connection database_collation sql_mode -1 addab 2 [{"name": "x", "type": "int"}, {"name": "y", "type": "int"}] ["int", "int"] int $1 + $2 sql udf_db test_account 2025-07-19 05:52:33 2025-07-19 05:52:33 FUNCTION DEFINER utf8mb4 utf8mb4_0900_ai_ci utf8mb4_0900_ai_ci ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION,NO_ZERO_DATE,NO_ZERO_IN_DATE,ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,ENABLE_BOOL_SUMAVG -2 concatenate 2 [{"name": "str1", "type": "varchar"}, {"name": "str2", "type": "varchar"}] ["varchar", "varchar"] varchar $1 + $2 sql udf_db test_account 2025-07-19 05:52:33 2025-07-19 05:52:33 FUNCTION DEFINER utf8mb4 utf8mb4_0900_ai_ci utf8mb4_0900_ai_ci ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION,NO_ZERO_DATE,NO_ZERO_IN_DATE,ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,ENABLE_BOOL_SUMAVG -3 subab 2 [{"name": "x", "type": "int"}, {"name": "y", "type": "int"}] ["int", "int"] int $1 - $2 sql udf_db2 test_account 2025-07-19 05:52:33 2025-07-19 05:52:33 FUNCTION DEFINER utf8mb4 utf8mb4_0900_ai_ci utf8mb4_0900_ai_ci ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION,NO_ZERO_DATE,NO_ZERO_IN_DATE,ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,ENABLE_BOOL_SUMAVG +function_id active_revision namespace_version name owner args arg_types canonical_input_descriptor return_descriptor signature_key_schema_version signature_fingerprint rettype body language db definer modified_time created_time type security_type comment character_set_client collation_connection database_collation sql_mode +1 1 1 addab 2 [{"name": "x", "type": "int"}, {"name": "y", "type": "int"}] ["int", "int"] 0 int $1 + $2 sql udf_db test_account 2025-07-19 05:52:33 2025-07-19 05:52:33 FUNCTION DEFINER utf8mb4 utf8mb4_0900_ai_ci utf8mb4_0900_ai_ci ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION,NO_ZERO_DATE,NO_ZERO_IN_DATE,ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,ENABLE_BOOL_SUMAVG +2 1 1 concatenate 2 [{"name": "str1", "type": "varchar"}, {"name": "str2", "type": "varchar"}] ["varchar", "varchar"] 0 varchar $1 + $2 sql udf_db test_account 2025-07-19 05:52:33 2025-07-19 05:52:33 FUNCTION DEFINER utf8mb4 utf8mb4_0900_ai_ci utf8mb4_0900_ai_ci ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION,NO_ZERO_DATE,NO_ZERO_IN_DATE,ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,ENABLE_BOOL_SUMAVG +3 1 1 subab 2 [{"name": "x", "type": "int"}, {"name": "y", "type": "int"}] ["int", "int"] 0 int $1 - $2 sql udf_db2 test_account 2025-07-19 05:52:33 2025-07-19 05:52:33 FUNCTION DEFINER utf8mb4 utf8mb4_0900_ai_ci utf8mb4_0900_ai_ci ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION,NO_ZERO_DATE,NO_ZERO_IN_DATE,ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,ENABLE_BOOL_SUMAVG restore account acc01{snapshot="udf_dsp02"} to account acc02; select * from mo_catalog.mo_user_defined_function; -function_id name owner args arg_types rettype body language db definer modified_time created_time type security_type comment character_set_client collation_connection database_collation sql_mode -1 addab 2 [{"name": "x", "type": "int"}, {"name": "y", "type": "int"}] ["int", "int"] int $1 + $2 sql udf_db test_account 2025-07-19 05:52:33 2025-07-19 05:52:33 FUNCTION DEFINER utf8mb4 utf8mb4_0900_ai_ci utf8mb4_0900_ai_ci ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION,NO_ZERO_DATE,NO_ZERO_IN_DATE,ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,ENABLE_BOOL_SUMAVG -2 concatenate 2 [{"name": "str1", "type": "varchar"}, {"name": "str2", "type": "varchar"}] ["varchar", "varchar"] varchar $1 + $2 sql udf_db test_account 2025-07-19 05:52:33 2025-07-19 05:52:33 FUNCTION DEFINER utf8mb4 utf8mb4_0900_ai_ci utf8mb4_0900_ai_ci ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION,NO_ZERO_DATE,NO_ZERO_IN_DATE,ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,ENABLE_BOOL_SUMAVG +function_id active_revision namespace_version name owner args arg_types canonical_input_descriptor return_descriptor signature_key_schema_version signature_fingerprint rettype body language db definer modified_time created_time type security_type comment character_set_client collation_connection database_collation sql_mode +1 1 1 addab 2 [{"name": "x", "type": "int"}, {"name": "y", "type": "int"}] ["int", "int"] 0 int $1 + $2 sql udf_db test_account 2025-07-19 05:52:33 2025-07-19 05:52:33 FUNCTION DEFINER utf8mb4 utf8mb4_0900_ai_ci utf8mb4_0900_ai_ci ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION,NO_ZERO_DATE,NO_ZERO_IN_DATE,ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,ENABLE_BOOL_SUMAVG +2 1 1 concatenate 2 [{"name": "str1", "type": "varchar"}, {"name": "str2", "type": "varchar"}] ["varchar", "varchar"] 0 varchar $1 + $2 sql udf_db test_account 2025-07-19 05:52:33 2025-07-19 05:52:33 FUNCTION DEFINER utf8mb4 utf8mb4_0900_ai_ci utf8mb4_0900_ai_ci ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION,NO_ZERO_DATE,NO_ZERO_IN_DATE,ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,ENABLE_BOOL_SUMAVG drop database udf_db; drop snapshot udf_dsp01; drop snapshot udf_dsp02; @@ -86,17 +86,17 @@ create function `addAB`(x int, y int) returns int language sql as '$1 + $2'; select * from mo_catalog.mo_user_defined_function; -function_id name owner args arg_types rettype body language db definer modified_time created_time type security_type comment character_set_client collation_connection database_collation sql_mode -4 addab 2 [{"name": "x", "type": "int"}, {"name": "y", "type": "int"}] ["int", "int"] int $1 + $2 sql udf_db2 test_account 2025-07-19 05:52:34 2025-07-19 05:52:34 FUNCTION DEFINER utf8mb4 utf8mb4_0900_ai_ci utf8mb4_0900_ai_ci ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION,NO_ZERO_DATE,NO_ZERO_IN_DATE,ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,ENABLE_BOOL_SUMAVG +function_id active_revision namespace_version name owner args arg_types canonical_input_descriptor return_descriptor signature_key_schema_version signature_fingerprint rettype body language db definer modified_time created_time type security_type comment character_set_client collation_connection database_collation sql_mode +4 1 1 addab 2 [{"name": "x", "type": "int"}, {"name": "y", "type": "int"}] ["int", "int"] 0 int $1 + $2 sql udf_db2 test_account 2025-07-19 05:52:34 2025-07-19 05:52:34 FUNCTION DEFINER utf8mb4 utf8mb4_0900_ai_ci utf8mb4_0900_ai_ci ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION,NO_ZERO_DATE,NO_ZERO_IN_DATE,ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,ENABLE_BOOL_SUMAVG drop snapshot if exists udf_sp04; create snapshot udf_sp04 for account acc01; drop database udf_db2; select * from mo_catalog.mo_user_defined_function; -function_id name owner args arg_types rettype body language db definer modified_time created_time type security_type comment character_set_client collation_connection database_collation sql_mode +function_id active_revision namespace_version name owner args arg_types canonical_input_descriptor return_descriptor signature_key_schema_version signature_fingerprint rettype body language db definer modified_time created_time type security_type comment character_set_client collation_connection database_collation sql_mode restore account acc01{snapshot="udf_sp04"} to account acc02; select * from mo_catalog.mo_user_defined_function; -function_id name owner args arg_types rettype body language db definer modified_time created_time type security_type comment character_set_client collation_connection database_collation sql_mode -4 addab 2 [{"name": "x", "type": "int"}, {"name": "y", "type": "int"}] ["int", "int"] int $1 + $2 sql udf_db2 test_account 2025-07-19 05:52:34 2025-07-19 05:52:34 FUNCTION DEFINER utf8mb4 utf8mb4_0900_ai_ci utf8mb4_0900_ai_ci ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION,NO_ZERO_DATE,NO_ZERO_IN_DATE,ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,ENABLE_BOOL_SUMAVG +function_id active_revision namespace_version name owner args arg_types canonical_input_descriptor return_descriptor signature_key_schema_version signature_fingerprint rettype body language db definer modified_time created_time type security_type comment character_set_client collation_connection database_collation sql_mode +4 1 1 addab 2 [{"name": "x", "type": "int"}, {"name": "y", "type": "int"}] ["int", "int"] 0 int $1 + $2 sql udf_db2 test_account 2025-07-19 05:52:34 2025-07-19 05:52:34 FUNCTION DEFINER utf8mb4 utf8mb4_0900_ai_ci utf8mb4_0900_ai_ci ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION,NO_ZERO_DATE,NO_ZERO_IN_DATE,ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,ENABLE_BOOL_SUMAVG drop database udf_db2; drop snapshot udf_sp04; drop database if exists procedure_test; diff --git a/test/distributed/cases/snapshot/sys_restore_system_table_to_newnonsys_account.sql b/test/distributed/cases/snapshot/sys_restore_system_table_to_newnonsys_account.sql index 6a92ed8abd7a9..3f0f65d37a318 100644 --- a/test/distributed/cases/snapshot/sys_restore_system_table_to_newnonsys_account.sql +++ b/test/distributed/cases/snapshot/sys_restore_system_table_to_newnonsys_account.sql @@ -54,7 +54,7 @@ create snapshot udf_dsp03 for account acc01; show snapshots; -- @session:id=1&user=acc01:test_account&password=111 --- @ignore:0,10,11 +-- @ignore:0,16,17 select * from mo_catalog.mo_user_defined_function; drop function subab(x int,y int); drop function udf_db.concatenate(str1 varchar(255), str2 varchar(255)); @@ -63,13 +63,13 @@ drop function udf_db.concatenate(str1 varchar(255), str2 varchar(255)); restore account acc01{snapshot="udf_dsp03"} to account acc02; -- @session:id=2&user=acc02:test_account&password=111 --- @ignore:0,10,11 +-- @ignore:0,16,17 select * from mo_catalog.mo_user_defined_function; -- @session restore account acc01{snapshot="udf_dsp02"} to account acc02; -- @session:id=2&user=acc02:test_account&password=111 --- @ignore:0,10,11 +-- @ignore:0,16,17 select * from mo_catalog.mo_user_defined_function; drop database udf_db; -- @session @@ -94,7 +94,7 @@ use udf_db2; create function `addAB`(x int, y int) returns int language sql as '$1 + $2'; --- @ignore:0,10,11 +-- @ignore:0,16,17 select * from mo_catalog.mo_user_defined_function; -- @session @@ -109,7 +109,7 @@ select * from mo_catalog.mo_user_defined_function; restore account acc01{snapshot="udf_sp04"} to account acc02; -- @session:id=2&user=acc02:test_account&password=111 --- @ignore:0,10,11 +-- @ignore:0,16,17 select * from mo_catalog.mo_user_defined_function; drop database udf_db2; -- @session diff --git a/test/distributed/cases/snapshot/sys_restore_system_table_to_nonsys_account.result b/test/distributed/cases/snapshot/sys_restore_system_table_to_nonsys_account.result index 8109f9316452a..542ab75a09ff4 100644 --- a/test/distributed/cases/snapshot/sys_restore_system_table_to_nonsys_account.result +++ b/test/distributed/cases/snapshot/sys_restore_system_table_to_nonsys_account.result @@ -54,23 +54,23 @@ udf_dsp03 2025-07-19 05:52:41.738562 account acc01 udf_dsp02 2025-07-19 05:52:41.609162 account acc01 udf_dsp01 2025-07-19 05:52:41.560453 account acc01 select * from mo_catalog.mo_user_defined_function; -function_id name owner args arg_types rettype body language db definer modified_time created_time type security_type comment character_set_client collation_connection database_collation sql_mode -1 addab 2 [{"name": "x", "type": "int"}, {"name": "y", "type": "int"}] ["int", "int"] int $1 + $2 sql udf_db test_account 2025-07-19 05:52:41 2025-07-19 05:52:41 FUNCTION DEFINER utf8mb4 utf8mb4_0900_ai_ci utf8mb4_0900_ai_ci ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION,NO_ZERO_DATE,NO_ZERO_IN_DATE,ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,ENABLE_BOOL_SUMAVG -2 concatenate 2 [{"name": "str1", "type": "varchar"}, {"name": "str2", "type": "varchar"}] ["varchar", "varchar"] varchar $1 + $2 sql udf_db test_account 2025-07-19 05:52:41 2025-07-19 05:52:41 FUNCTION DEFINER utf8mb4 utf8mb4_0900_ai_ci utf8mb4_0900_ai_ci ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION,NO_ZERO_DATE,NO_ZERO_IN_DATE,ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,ENABLE_BOOL_SUMAVG -3 subab 2 [{"name": "x", "type": "int"}, {"name": "y", "type": "int"}] ["int", "int"] int $1 - $2 sql udf_db2 test_account 2025-07-19 05:52:41 2025-07-19 05:52:41 FUNCTION DEFINER utf8mb4 utf8mb4_0900_ai_ci utf8mb4_0900_ai_ci ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION,NO_ZERO_DATE,NO_ZERO_IN_DATE,ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,ENABLE_BOOL_SUMAVG +function_id active_revision namespace_version name owner args arg_types canonical_input_descriptor return_descriptor signature_key_schema_version signature_fingerprint rettype body language db definer modified_time created_time type security_type comment character_set_client collation_connection database_collation sql_mode +1 1 1 addab 2 [{"name": "x", "type": "int"}, {"name": "y", "type": "int"}] ["int", "int"] 0 int $1 + $2 sql udf_db test_account 2025-07-19 05:52:41 2025-07-19 05:52:41 FUNCTION DEFINER utf8mb4 utf8mb4_0900_ai_ci utf8mb4_0900_ai_ci ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION,NO_ZERO_DATE,NO_ZERO_IN_DATE,ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,ENABLE_BOOL_SUMAVG +2 1 1 concatenate 2 [{"name": "str1", "type": "varchar"}, {"name": "str2", "type": "varchar"}] ["varchar", "varchar"] 0 varchar $1 + $2 sql udf_db test_account 2025-07-19 05:52:41 2025-07-19 05:52:41 FUNCTION DEFINER utf8mb4 utf8mb4_0900_ai_ci utf8mb4_0900_ai_ci ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION,NO_ZERO_DATE,NO_ZERO_IN_DATE,ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,ENABLE_BOOL_SUMAVG +3 1 1 subab 2 [{"name": "x", "type": "int"}, {"name": "y", "type": "int"}] ["int", "int"] 0 int $1 - $2 sql udf_db2 test_account 2025-07-19 05:52:41 2025-07-19 05:52:41 FUNCTION DEFINER utf8mb4 utf8mb4_0900_ai_ci utf8mb4_0900_ai_ci ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION,NO_ZERO_DATE,NO_ZERO_IN_DATE,ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,ENABLE_BOOL_SUMAVG drop function subab(x int,y int); drop function udf_db.concatenate(str1 varchar(255), str2 varchar(255)); restore account acc01{snapshot="udf_dsp03"}; select * from mo_catalog.mo_user_defined_function; -function_id name owner args arg_types rettype body language db definer modified_time created_time type security_type comment character_set_client collation_connection database_collation sql_mode -1 addab 2 [{"name": "x", "type": "int"}, {"name": "y", "type": "int"}] ["int", "int"] int $1 + $2 sql udf_db test_account 2025-07-19 05:52:41 2025-07-19 05:52:41 FUNCTION DEFINER utf8mb4 utf8mb4_0900_ai_ci utf8mb4_0900_ai_ci ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION,NO_ZERO_DATE,NO_ZERO_IN_DATE,ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,ENABLE_BOOL_SUMAVG -2 concatenate 2 [{"name": "str1", "type": "varchar"}, {"name": "str2", "type": "varchar"}] ["varchar", "varchar"] varchar $1 + $2 sql udf_db test_account 2025-07-19 05:52:41 2025-07-19 05:52:41 FUNCTION DEFINER utf8mb4 utf8mb4_0900_ai_ci utf8mb4_0900_ai_ci ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION,NO_ZERO_DATE,NO_ZERO_IN_DATE,ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,ENABLE_BOOL_SUMAVG -3 subab 2 [{"name": "x", "type": "int"}, {"name": "y", "type": "int"}] ["int", "int"] int $1 - $2 sql udf_db2 test_account 2025-07-19 05:52:41 2025-07-19 05:52:41 FUNCTION DEFINER utf8mb4 utf8mb4_0900_ai_ci utf8mb4_0900_ai_ci ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION,NO_ZERO_DATE,NO_ZERO_IN_DATE,ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,ENABLE_BOOL_SUMAVG +function_id active_revision namespace_version name owner args arg_types canonical_input_descriptor return_descriptor signature_key_schema_version signature_fingerprint rettype body language db definer modified_time created_time type security_type comment character_set_client collation_connection database_collation sql_mode +1 1 1 addab 2 [{"name": "x", "type": "int"}, {"name": "y", "type": "int"}] ["int", "int"] 0 int $1 + $2 sql udf_db test_account 2025-07-19 05:52:41 2025-07-19 05:52:41 FUNCTION DEFINER utf8mb4 utf8mb4_0900_ai_ci utf8mb4_0900_ai_ci ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION,NO_ZERO_DATE,NO_ZERO_IN_DATE,ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,ENABLE_BOOL_SUMAVG +2 1 1 concatenate 2 [{"name": "str1", "type": "varchar"}, {"name": "str2", "type": "varchar"}] ["varchar", "varchar"] 0 varchar $1 + $2 sql udf_db test_account 2025-07-19 05:52:41 2025-07-19 05:52:41 FUNCTION DEFINER utf8mb4 utf8mb4_0900_ai_ci utf8mb4_0900_ai_ci ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION,NO_ZERO_DATE,NO_ZERO_IN_DATE,ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,ENABLE_BOOL_SUMAVG +3 1 1 subab 2 [{"name": "x", "type": "int"}, {"name": "y", "type": "int"}] ["int", "int"] 0 int $1 - $2 sql udf_db2 test_account 2025-07-19 05:52:41 2025-07-19 05:52:41 FUNCTION DEFINER utf8mb4 utf8mb4_0900_ai_ci utf8mb4_0900_ai_ci ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION,NO_ZERO_DATE,NO_ZERO_IN_DATE,ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,ENABLE_BOOL_SUMAVG restore account acc01{snapshot="udf_dsp02"}; select * from mo_catalog.mo_user_defined_function; -function_id name owner args arg_types rettype body language db definer modified_time created_time type security_type comment character_set_client collation_connection database_collation sql_mode -1 addab 2 [{"name": "x", "type": "int"}, {"name": "y", "type": "int"}] ["int", "int"] int $1 + $2 sql udf_db test_account 2025-07-19 05:52:41 2025-07-19 05:52:41 FUNCTION DEFINER utf8mb4 utf8mb4_0900_ai_ci utf8mb4_0900_ai_ci ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION,NO_ZERO_DATE,NO_ZERO_IN_DATE,ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,ENABLE_BOOL_SUMAVG -2 concatenate 2 [{"name": "str1", "type": "varchar"}, {"name": "str2", "type": "varchar"}] ["varchar", "varchar"] varchar $1 + $2 sql udf_db test_account 2025-07-19 05:52:41 2025-07-19 05:52:41 FUNCTION DEFINER utf8mb4 utf8mb4_0900_ai_ci utf8mb4_0900_ai_ci ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION,NO_ZERO_DATE,NO_ZERO_IN_DATE,ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,ENABLE_BOOL_SUMAVG +function_id active_revision namespace_version name owner args arg_types canonical_input_descriptor return_descriptor signature_key_schema_version signature_fingerprint rettype body language db definer modified_time created_time type security_type comment character_set_client collation_connection database_collation sql_mode +1 1 1 addab 2 [{"name": "x", "type": "int"}, {"name": "y", "type": "int"}] ["int", "int"] 0 int $1 + $2 sql udf_db test_account 2025-07-19 05:52:41 2025-07-19 05:52:41 FUNCTION DEFINER utf8mb4 utf8mb4_0900_ai_ci utf8mb4_0900_ai_ci ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION,NO_ZERO_DATE,NO_ZERO_IN_DATE,ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,ENABLE_BOOL_SUMAVG +2 1 1 concatenate 2 [{"name": "str1", "type": "varchar"}, {"name": "str2", "type": "varchar"}] ["varchar", "varchar"] 0 varchar $1 + $2 sql udf_db test_account 2025-07-19 05:52:41 2025-07-19 05:52:41 FUNCTION DEFINER utf8mb4 utf8mb4_0900_ai_ci utf8mb4_0900_ai_ci ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION,NO_ZERO_DATE,NO_ZERO_IN_DATE,ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,ENABLE_BOOL_SUMAVG drop database udf_db; drop snapshot udf_dsp01; drop snapshot udf_dsp02; @@ -82,17 +82,17 @@ create function `addab`(x int, y int) returns int language sql as '$1 + $2'; select * from mo_catalog.mo_user_defined_function; -function_id name owner args arg_types rettype body language db definer modified_time created_time type security_type comment character_set_client collation_connection database_collation sql_mode -10001 addab 2 [{"name": "x", "type": "int"}, {"name": "y", "type": "int"}] ["int", "int"] int $1 + $2 sql udf_db2 test_account 2025-07-19 05:52:43 2025-07-19 05:52:43 FUNCTION DEFINER utf8mb4 utf8mb4_0900_ai_ci utf8mb4_0900_ai_ci ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION,NO_ZERO_DATE,NO_ZERO_IN_DATE,ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,ENABLE_BOOL_SUMAVG +function_id active_revision namespace_version name owner args arg_types canonical_input_descriptor return_descriptor signature_key_schema_version signature_fingerprint rettype body language db definer modified_time created_time type security_type comment character_set_client collation_connection database_collation sql_mode +10001 1 1 addab 2 [{"name": "x", "type": "int"}, {"name": "y", "type": "int"}] ["int", "int"] 0 int $1 + $2 sql udf_db2 test_account 2025-07-19 05:52:43 2025-07-19 05:52:43 FUNCTION DEFINER utf8mb4 utf8mb4_0900_ai_ci utf8mb4_0900_ai_ci ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION,NO_ZERO_DATE,NO_ZERO_IN_DATE,ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,ENABLE_BOOL_SUMAVG drop snapshot if exists udf_sp04; create snapshot udf_sp04 for account acc01; drop database udf_db2; select * from mo_catalog.mo_user_defined_function; -function_id name owner args arg_types rettype body language db definer modified_time created_time type security_type comment character_set_client collation_connection database_collation sql_mode +function_id active_revision namespace_version name owner args arg_types canonical_input_descriptor return_descriptor signature_key_schema_version signature_fingerprint rettype body language db definer modified_time created_time type security_type comment character_set_client collation_connection database_collation sql_mode restore account acc01{snapshot="udf_sp04"}; select * from mo_catalog.mo_user_defined_function; -function_id name owner args arg_types rettype body language db definer modified_time created_time type security_type comment character_set_client collation_connection database_collation sql_mode -10001 addab 2 [{"name": "x", "type": "int"}, {"name": "y", "type": "int"}] ["int", "int"] int $1 + $2 sql udf_db2 test_account 2025-07-19 05:52:43 2025-07-19 05:52:43 FUNCTION DEFINER utf8mb4 utf8mb4_0900_ai_ci utf8mb4_0900_ai_ci ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION,NO_ZERO_DATE,NO_ZERO_IN_DATE,ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,ENABLE_BOOL_SUMAVG +function_id active_revision namespace_version name owner args arg_types canonical_input_descriptor return_descriptor signature_key_schema_version signature_fingerprint rettype body language db definer modified_time created_time type security_type comment character_set_client collation_connection database_collation sql_mode +10001 1 1 addab 2 [{"name": "x", "type": "int"}, {"name": "y", "type": "int"}] ["int", "int"] 0 int $1 + $2 sql udf_db2 test_account 2025-07-19 05:52:43 2025-07-19 05:52:43 FUNCTION DEFINER utf8mb4 utf8mb4_0900_ai_ci utf8mb4_0900_ai_ci ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION,NO_ZERO_DATE,NO_ZERO_IN_DATE,ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,ENABLE_BOOL_SUMAVG drop database udf_db2; drop snapshot udf_sp04; drop database if exists procedure_test; diff --git a/test/distributed/cases/snapshot/sys_restore_system_table_to_nonsys_account.sql b/test/distributed/cases/snapshot/sys_restore_system_table_to_nonsys_account.sql index 8d7e630fe7468..7d085efd06d6b 100644 --- a/test/distributed/cases/snapshot/sys_restore_system_table_to_nonsys_account.sql +++ b/test/distributed/cases/snapshot/sys_restore_system_table_to_nonsys_account.sql @@ -52,7 +52,7 @@ create snapshot udf_dsp03 for account acc01; show snapshots; -- @session:id=1&user=acc01:test_account&password=111 --- @ignore:0,10,11 +-- @ignore:0,16,17 select * from mo_catalog.mo_user_defined_function; drop function subab(x int,y int); drop function udf_db.concatenate(str1 varchar(255), str2 varchar(255)); @@ -61,14 +61,14 @@ drop function udf_db.concatenate(str1 varchar(255), str2 varchar(255)); restore account acc01{snapshot="udf_dsp03"}; -- @session:id=1&user=acc01:test_account&password=111 --- @ignore:0,10,11 +-- @ignore:0,16,17 select * from mo_catalog.mo_user_defined_function; -- @session restore account acc01{snapshot="udf_dsp02"}; -- @session:id=1&user=acc01:test_account&password=111 --- @ignore:0,10,11 +-- @ignore:0,16,17 select * from mo_catalog.mo_user_defined_function; -- @session @@ -90,7 +90,7 @@ use udf_db2; create function `addab`(x int, y int) returns int language sql as '$1 + $2'; --- @ignore:0,10,11 +-- @ignore:0,16,17 select * from mo_catalog.mo_user_defined_function; -- @session @@ -99,14 +99,14 @@ create snapshot udf_sp04 for account acc01; -- @session:id=1&user=acc01:test_account&password=111 drop database udf_db2; --- @ignore:0,10,11 +-- @ignore:0,16,17 select * from mo_catalog.mo_user_defined_function; -- @session restore account acc01{snapshot="udf_sp04"}; -- @session:id=1&user=acc01:test_account&password=111 --- @ignore:0,10,11 +-- @ignore:0,16,17 select * from mo_catalog.mo_user_defined_function; drop database udf_db2; -- @session diff --git a/test/distributed/cases/snapshot/sys_restore_system_table_to_sys_account.result b/test/distributed/cases/snapshot/sys_restore_system_table_to_sys_account.result index ff0f04f4581d5..361e8f6a17e88 100644 --- a/test/distributed/cases/snapshot/sys_restore_system_table_to_sys_account.result +++ b/test/distributed/cases/snapshot/sys_restore_system_table_to_sys_account.result @@ -52,18 +52,18 @@ udf_dsp03 2025-07-19 05:52:48.557367 account sys udf_dsp02 2025-07-19 05:52:48.529274 account sys udf_dsp01 2025-07-19 05:52:48.516842 account sys select * from mo_catalog.mo_user_defined_function; -function_id name owner args arg_types rettype body language db definer modified_time created_time type security_type comment character_set_client collation_connection database_collation sql_mode -1890000 addab 0 [{"name": "x", "type": "int"}, {"name": "y", "type": "int"}] ["int", "int"] int $1 + $2 sql udf_db dump 2025-07-19 05:52:48 2025-07-19 05:52:48 FUNCTION DEFINER utf8mb4 utf8mb4_0900_ai_ci utf8mb4_0900_ai_ci ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION,NO_ZERO_DATE,NO_ZERO_IN_DATE,ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,ENABLE_BOOL_SUMAVG -1890001 concatenate 0 [{"name": "str1", "type": "varchar"}, {"name": "str2", "type": "varchar"}] ["varchar", "varchar"] varchar $1 + $2 sql udf_db dump 2025-07-19 05:52:48 2025-07-19 05:52:48 FUNCTION DEFINER utf8mb4 utf8mb4_0900_ai_ci utf8mb4_0900_ai_ci ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION,NO_ZERO_DATE,NO_ZERO_IN_DATE,ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,ENABLE_BOOL_SUMAVG -1890002 subab 0 [{"name": "x", "type": "int"}, {"name": "y", "type": "int"}] ["int", "int"] int $1 - $2 sql udf_db2 dump 2025-07-19 05:52:48 2025-07-19 05:52:48 FUNCTION DEFINER utf8mb4 utf8mb4_0900_ai_ci utf8mb4_0900_ai_ci ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION,NO_ZERO_DATE,NO_ZERO_IN_DATE,ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,ENABLE_BOOL_SUMAVG +function_id active_revision namespace_version name owner args arg_types canonical_input_descriptor return_descriptor signature_key_schema_version signature_fingerprint rettype body language db definer modified_time created_time type security_type comment character_set_client collation_connection database_collation sql_mode +1890000 1 1 addab 0 [{"name": "x", "type": "int"}, {"name": "y", "type": "int"}] ["int", "int"] 0 int $1 + $2 sql udf_db dump 2025-07-19 05:52:48 2025-07-19 05:52:48 FUNCTION DEFINER utf8mb4 utf8mb4_0900_ai_ci utf8mb4_0900_ai_ci ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION,NO_ZERO_DATE,NO_ZERO_IN_DATE,ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,ENABLE_BOOL_SUMAVG +1890001 1 1 concatenate 0 [{"name": "str1", "type": "varchar"}, {"name": "str2", "type": "varchar"}] ["varchar", "varchar"] 0 varchar $1 + $2 sql udf_db dump 2025-07-19 05:52:48 2025-07-19 05:52:48 FUNCTION DEFINER utf8mb4 utf8mb4_0900_ai_ci utf8mb4_0900_ai_ci ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION,NO_ZERO_DATE,NO_ZERO_IN_DATE,ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,ENABLE_BOOL_SUMAVG +1890002 1 1 subab 0 [{"name": "x", "type": "int"}, {"name": "y", "type": "int"}] ["int", "int"] 0 int $1 - $2 sql udf_db2 dump 2025-07-19 05:52:48 2025-07-19 05:52:48 FUNCTION DEFINER utf8mb4 utf8mb4_0900_ai_ci utf8mb4_0900_ai_ci ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION,NO_ZERO_DATE,NO_ZERO_IN_DATE,ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,ENABLE_BOOL_SUMAVG drop function subab(x int,y int); drop function udf_db.concatenate(str1 varchar(255), str2 varchar(255)); restore account sys{snapshot="udf_dsp03"}; select * from mo_catalog.mo_user_defined_function; -function_id name owner args arg_types rettype body language db definer modified_time created_time type security_type comment character_set_client collation_connection database_collation sql_mode -1890000 addab 0 [{"name": "x", "type": "int"}, {"name": "y", "type": "int"}] ["int", "int"] int $1 + $2 sql udf_db dump 2025-07-19 05:52:48 2025-07-19 05:52:48 FUNCTION DEFINER utf8mb4 utf8mb4_0900_ai_ci utf8mb4_0900_ai_ci ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION,NO_ZERO_DATE,NO_ZERO_IN_DATE,ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,ENABLE_BOOL_SUMAVG -1890001 concatenate 0 [{"name": "str1", "type": "varchar"}, {"name": "str2", "type": "varchar"}] ["varchar", "varchar"] varchar $1 + $2 sql udf_db dump 2025-07-19 05:52:48 2025-07-19 05:52:48 FUNCTION DEFINER utf8mb4 utf8mb4_0900_ai_ci utf8mb4_0900_ai_ci ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION,NO_ZERO_DATE,NO_ZERO_IN_DATE,ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,ENABLE_BOOL_SUMAVG -1890002 subab 0 [{"name": "x", "type": "int"}, {"name": "y", "type": "int"}] ["int", "int"] int $1 - $2 sql udf_db2 dump 2025-07-19 05:52:48 2025-07-19 05:52:48 FUNCTION DEFINER utf8mb4 utf8mb4_0900_ai_ci utf8mb4_0900_ai_ci ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION,NO_ZERO_DATE,NO_ZERO_IN_DATE,ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,ENABLE_BOOL_SUMAVG +function_id active_revision namespace_version name owner args arg_types canonical_input_descriptor return_descriptor signature_key_schema_version signature_fingerprint rettype body language db definer modified_time created_time type security_type comment character_set_client collation_connection database_collation sql_mode +1890000 1 1 addab 0 [{"name": "x", "type": "int"}, {"name": "y", "type": "int"}] ["int", "int"] 0 int $1 + $2 sql udf_db dump 2025-07-19 05:52:48 2025-07-19 05:52:48 FUNCTION DEFINER utf8mb4 utf8mb4_0900_ai_ci utf8mb4_0900_ai_ci ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION,NO_ZERO_DATE,NO_ZERO_IN_DATE,ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,ENABLE_BOOL_SUMAVG +1890001 1 1 concatenate 0 [{"name": "str1", "type": "varchar"}, {"name": "str2", "type": "varchar"}] ["varchar", "varchar"] 0 varchar $1 + $2 sql udf_db dump 2025-07-19 05:52:48 2025-07-19 05:52:48 FUNCTION DEFINER utf8mb4 utf8mb4_0900_ai_ci utf8mb4_0900_ai_ci ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION,NO_ZERO_DATE,NO_ZERO_IN_DATE,ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,ENABLE_BOOL_SUMAVG +1890002 1 1 subab 0 [{"name": "x", "type": "int"}, {"name": "y", "type": "int"}] ["int", "int"] 0 int $1 - $2 sql udf_db2 dump 2025-07-19 05:52:48 2025-07-19 05:52:48 FUNCTION DEFINER utf8mb4 utf8mb4_0900_ai_ci utf8mb4_0900_ai_ci ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION,NO_ZERO_DATE,NO_ZERO_IN_DATE,ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,ENABLE_BOOL_SUMAVG show snapshots; SNAPSHOT_NAME TIMESTAMP SNAPSHOT_LEVEL ACCOUNT_NAME DATABASE_NAME TABLE_NAME udf_dsp03 2025-07-19 05:52:48.557367 account sys @@ -71,9 +71,9 @@ udf_dsp02 2025-07-19 05:52:48.529274 account sys udf_dsp01 2025-07-19 05:52:48.516842 account sys restore account sys{snapshot="udf_dsp02"}; select * from mo_catalog.mo_user_defined_function; -function_id name owner args arg_types rettype body language db definer modified_time created_time type security_type comment character_set_client collation_connection database_collation sql_mode -1890000 addab 0 [{"name": "x", "type": "int"}, {"name": "y", "type": "int"}] ["int", "int"] int $1 + $2 sql udf_db dump 2025-07-19 05:52:48 2025-07-19 05:52:48 FUNCTION DEFINER utf8mb4 utf8mb4_0900_ai_ci utf8mb4_0900_ai_ci ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION,NO_ZERO_DATE,NO_ZERO_IN_DATE,ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,ENABLE_BOOL_SUMAVG -1890001 concatenate 0 [{"name": "str1", "type": "varchar"}, {"name": "str2", "type": "varchar"}] ["varchar", "varchar"] varchar $1 + $2 sql udf_db dump 2025-07-19 05:52:48 2025-07-19 05:52:48 FUNCTION DEFINER utf8mb4 utf8mb4_0900_ai_ci utf8mb4_0900_ai_ci ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION,NO_ZERO_DATE,NO_ZERO_IN_DATE,ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,ENABLE_BOOL_SUMAVG +function_id active_revision namespace_version name owner args arg_types canonical_input_descriptor return_descriptor signature_key_schema_version signature_fingerprint rettype body language db definer modified_time created_time type security_type comment character_set_client collation_connection database_collation sql_mode +1890000 1 1 addab 0 [{"name": "x", "type": "int"}, {"name": "y", "type": "int"}] ["int", "int"] 0 int $1 + $2 sql udf_db dump 2025-07-19 05:52:48 2025-07-19 05:52:48 FUNCTION DEFINER utf8mb4 utf8mb4_0900_ai_ci utf8mb4_0900_ai_ci ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION,NO_ZERO_DATE,NO_ZERO_IN_DATE,ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,ENABLE_BOOL_SUMAVG +1890001 1 1 concatenate 0 [{"name": "str1", "type": "varchar"}, {"name": "str2", "type": "varchar"}] ["varchar", "varchar"] 0 varchar $1 + $2 sql udf_db dump 2025-07-19 05:52:48 2025-07-19 05:52:48 FUNCTION DEFINER utf8mb4 utf8mb4_0900_ai_ci utf8mb4_0900_ai_ci ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION,NO_ZERO_DATE,NO_ZERO_IN_DATE,ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,ENABLE_BOOL_SUMAVG show snapshots; SNAPSHOT_NAME TIMESTAMP SNAPSHOT_LEVEL ACCOUNT_NAME DATABASE_NAME TABLE_NAME udf_dsp03 2025-07-19 05:52:48.557367 account sys @@ -90,17 +90,17 @@ create function `addab`(x int, y int) returns int language sql as '$1 + $2'; select * from mo_catalog.mo_user_defined_function; -function_id name owner args arg_types rettype body language db definer modified_time created_time type security_type comment character_set_client collation_connection database_collation sql_mode -1900000 addab 0 [{"name": "x", "type": "int"}, {"name": "y", "type": "int"}] ["int", "int"] int $1 + $2 sql udf_db2 dump 2025-07-19 05:52:48 2025-07-19 05:52:48 FUNCTION DEFINER utf8mb4 utf8mb4_0900_ai_ci utf8mb4_0900_ai_ci ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION,NO_ZERO_DATE,NO_ZERO_IN_DATE,ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,ENABLE_BOOL_SUMAVG +function_id active_revision namespace_version name owner args arg_types canonical_input_descriptor return_descriptor signature_key_schema_version signature_fingerprint rettype body language db definer modified_time created_time type security_type comment character_set_client collation_connection database_collation sql_mode +1900000 1 1 addab 0 [{"name": "x", "type": "int"}, {"name": "y", "type": "int"}] ["int", "int"] 0 int $1 + $2 sql udf_db2 dump 2025-07-19 05:52:48 2025-07-19 05:52:48 FUNCTION DEFINER utf8mb4 utf8mb4_0900_ai_ci utf8mb4_0900_ai_ci ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION,NO_ZERO_DATE,NO_ZERO_IN_DATE,ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,ENABLE_BOOL_SUMAVG drop snapshot if exists udf_sp04; create snapshot udf_sp04 for account; drop database udf_db2; select * from mo_catalog.mo_user_defined_function; -function_id name owner args arg_types rettype body language db definer modified_time created_time type security_type comment character_set_client collation_connection database_collation sql_mode +function_id active_revision namespace_version name owner args arg_types canonical_input_descriptor return_descriptor signature_key_schema_version signature_fingerprint rettype body language db definer modified_time created_time type security_type comment character_set_client collation_connection database_collation sql_mode restore account sys{snapshot="udf_sp04"}; select * from mo_catalog.mo_user_defined_function; -function_id name owner args arg_types rettype body language db definer modified_time created_time type security_type comment character_set_client collation_connection database_collation sql_mode -1900000 addab 0 [{"name": "x", "type": "int"}, {"name": "y", "type": "int"}] ["int", "int"] int $1 + $2 sql udf_db2 dump 2025-07-19 05:52:48 2025-07-19 05:52:48 FUNCTION DEFINER utf8mb4 utf8mb4_0900_ai_ci utf8mb4_0900_ai_ci ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION,NO_ZERO_DATE,NO_ZERO_IN_DATE,ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,ENABLE_BOOL_SUMAVG +function_id active_revision namespace_version name owner args arg_types canonical_input_descriptor return_descriptor signature_key_schema_version signature_fingerprint rettype body language db definer modified_time created_time type security_type comment character_set_client collation_connection database_collation sql_mode +1900000 1 1 addab 0 [{"name": "x", "type": "int"}, {"name": "y", "type": "int"}] ["int", "int"] 0 int $1 + $2 sql udf_db2 dump 2025-07-19 05:52:48 2025-07-19 05:52:48 FUNCTION DEFINER utf8mb4 utf8mb4_0900_ai_ci utf8mb4_0900_ai_ci ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION,NO_ZERO_DATE,NO_ZERO_IN_DATE,ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,ENABLE_BOOL_SUMAVG drop database udf_db2; drop snapshot udf_sp04; drop database if exists procedure_test; diff --git a/test/distributed/cases/snapshot/sys_restore_system_table_to_sys_account.sql b/test/distributed/cases/snapshot/sys_restore_system_table_to_sys_account.sql index 78587e4498806..e9eacb3058ba3 100644 --- a/test/distributed/cases/snapshot/sys_restore_system_table_to_sys_account.sql +++ b/test/distributed/cases/snapshot/sys_restore_system_table_to_sys_account.sql @@ -43,20 +43,20 @@ create snapshot udf_dsp03 for account; -- @ignore:0,1 show snapshots; --- @ignore:0,10,11 +-- @ignore:0,16,17 select * from mo_catalog.mo_user_defined_function; drop function subab(x int,y int); drop function udf_db.concatenate(str1 varchar(255), str2 varchar(255)); restore account sys{snapshot="udf_dsp03"}; --- @ignore:0,10,11 +-- @ignore:0,16,17 select * from mo_catalog.mo_user_defined_function; -- @ignore:0,1 show snapshots; restore account sys{snapshot="udf_dsp02"}; --- @ignore:0,10,11 +-- @ignore:0,16,17 select * from mo_catalog.mo_user_defined_function; -- @ignore:0,1 show snapshots; @@ -76,7 +76,7 @@ use udf_db2; create function `addab`(x int, y int) returns int language sql as '$1 + $2'; --- @ignore:0,10,11 +-- @ignore:0,16,17 select * from mo_catalog.mo_user_defined_function; drop snapshot if exists udf_sp04; @@ -86,7 +86,7 @@ drop database udf_db2; select * from mo_catalog.mo_user_defined_function; restore account sys{snapshot="udf_sp04"}; --- @ignore:0,10,11 +-- @ignore:0,16,17 select * from mo_catalog.mo_user_defined_function; drop database udf_db2; drop snapshot udf_sp04; diff --git a/test/distributed/cases/tenant/privilege/create_user_default_role.result b/test/distributed/cases/tenant/privilege/create_user_default_role.result index 74f2e50aa285c..71daf963301cb 100644 --- a/test/distributed/cases/tenant/privilege/create_user_default_role.result +++ b/test/distributed/cases/tenant/privilege/create_user_default_role.result @@ -37,6 +37,7 @@ mo_database mo_feature_limit mo_feature_registry mo_foreign_keys +mo_function_revisions mo_iceberg_catalogs mo_iceberg_maintenance_jobs mo_iceberg_orphan_files diff --git a/test/distributed/cases/udf_python/README.md b/test/distributed/cases/udf_python/README.md index 3241d9f991a15..3a342a0113043 100644 --- a/test/distributed/cases/udf_python/README.md +++ b/test/distributed/cases/udf_python/README.md @@ -10,12 +10,26 @@ worker-enabled manifest for this ordinary BVT entry point; starting `etc/launch/launch.toml` directly leaves Python disabled. ```bash -python3 -m pip install -r pkg/udf/python/worker/requirements.txt +python3.12 -m venv /path/to/python-udf-test-venv +source /path/to/python-udf-test-venv/bin/activate +python3 -m pip install --only-binary=:all: -r pkg/udf/python/worker/requirements.txt ./optools/run_bvt.sh . launch cd /path/to/mo-tester ./run.sh -n -g -o -p /path/to/matrixone/test/distributed/cases ``` +The Go UDF package tests also start real Flight workers. Keep the same venv on +`PATH` when running them: installing PyArrow into another interpreter will not +satisfy `exec.LookPath("python3")`. The shared CI UT and coverage setup uses +Python 3.12 and the checked-out requirements file; this is a test environment +choice, not an exact-minor production compatibility rule. + +For fresh local data, the worker-enabled CN and its TN both use the same +`DISK-V2` FileService directories. Do not mix that configuration with an +existing `DISK` data directory. Container BVT also requires CN and worker +images with the same IANA database; both full-build and prebuilt runtime image +recipes install tzdata before the strict capability handshake. + To create or refresh a result file after reviewing the expected output, keep the normal worker-enabled launch running and use `mo-tester`'s `genrs` mode: From 1d3f4c1ad6514c539ebe0f1b8921d014aea4ed47 Mon Sep 17 00:00:00 2001 From: iamlinjunhong <1030420200@qq.com> Date: Sun, 20 Sep 2026 20:06:50 +0800 Subject: [PATCH 193/202] fix(udf): remove duplicate frontend import --- pkg/frontend/authenticate.go | 1 - 1 file changed, 1 deletion(-) diff --git a/pkg/frontend/authenticate.go b/pkg/frontend/authenticate.go index d720a8e845de8..cf33f0dd9d83f 100644 --- a/pkg/frontend/authenticate.go +++ b/pkg/frontend/authenticate.go @@ -20,7 +20,6 @@ import ( "encoding/json" "errors" "fmt" - "github.com/matrixorigin/matrixone/pkg/util/errutil" "io" "math" "math/bits" From 98e9542aaf55230b48b9e33feebd85b02817f15f Mon Sep 17 00:00:00 2001 From: iamlinjunhong <1030420200@qq.com> Date: Sun, 20 Sep 2026 22:43:02 +0800 Subject: [PATCH 194/202] fix(udf): preserve Python UDF test dependencies --- cmd/mo-service/main.go | 2 +- optools/run_ut.sh | 22 +++++++++++++------- pkg/udf/python/worker/test_worker.py | 31 ++++++++++++++++++++++++++++ pkg/udf/python/worker/worker.py | 5 ++++- 4 files changed, 51 insertions(+), 9 deletions(-) diff --git a/cmd/mo-service/main.go b/cmd/mo-service/main.go index e62b681634136..fcfa3af25a86f 100644 --- a/cmd/mo-service/main.go +++ b/cmd/mo-service/main.go @@ -782,7 +782,7 @@ func startPythonUdfWorker(cfg *Config, stopper *stopper.Stopper) error { if workerErr == nil { workerErr = errors.New("process exited without an error") } - closeErr = fmt.Errorf("Python UDF worker exited unexpectedly: %w", workerErr) + closeErr = fmt.Errorf("python UDF worker exited unexpectedly: %w", workerErr) serviceLifecycle.notifyFatal(closeErr) } } diff --git a/optools/run_ut.sh b/optools/run_ut.sh index 351000d4c5f37..80cf3dbfc3d52 100755 --- a/optools/run_ut.sh +++ b/optools/run_ut.sh @@ -2221,13 +2221,21 @@ function run_tests(){ fi mark_ut_stage "routing" "validate shard and package partition" finish 0 - mark_ut_stage "prepare" "prepare Python UDF dependencies" start - prepare_python_udf_dependencies - local python_udf_dependency_status=$? - mark_ut_stage "prepare" "prepare Python UDF dependencies" finish "${python_udf_dependency_status}" - if (( python_udf_dependency_status != 0 )); then - UT_TEST_STATUS=1 - return 0 + # The Python UDF package belongs to the light stage. Split shards execute + # only their selected stage groups; preparing a repository-relative + # requirements file in a synthetic heavy-plan harness is both unnecessary + # and invalid because that harness intentionally contains no pkg tree. + if should_run_ut_stage light; then + mark_ut_stage "prepare" "prepare Python UDF dependencies" start + prepare_python_udf_dependencies + local python_udf_dependency_status=$? + mark_ut_stage "prepare" "prepare Python UDF dependencies" finish "${python_udf_dependency_status}" + if (( python_udf_dependency_status != 0 )); then + UT_TEST_STATUS=1 + return 0 + fi + else + logger "INF" "Skip Python UDF dependency preparation for UT_SHARD=${UT_SHARD}" fi mark_ut_stage "prepare" "clean go test cache" start diff --git a/pkg/udf/python/worker/test_worker.py b/pkg/udf/python/worker/test_worker.py index 3c1a5616c3667..3df7f07549055 100644 --- a/pkg/udf/python/worker/test_worker.py +++ b/pkg/udf/python/worker/test_worker.py @@ -2896,6 +2896,37 @@ def test_handler_does_not_inherit_parent_environment(self): ) self.assertEqual([False], result.column(0).to_pylist()) + def test_handler_preserves_runtime_pythonpath(self): + descriptor = {"type_id": worker.BOOL, "offset_width": 32} + batch = pa.RecordBatch.from_arrays( + [pa.array([True], type=pa.bool_())], ["arg_0"] + ) + request = { + "source": ( + "import os\n" + "def f(ctx, x):\n" + " return os.environ.get('PYTHONPATH', '').startswith('matrixone-test-pythonpath')" + ), + "handler": "f", + "mode": worker.MODE_SCALAR, + "null_policy": worker.NULL_CALL, + "sdk_version": worker.SDK_VERSION, + "context": None, + "args": [descriptor], + "return": descriptor, + "max_batch_bytes": 1 << 20, + "input": worker._serialize_record_batch(batch), + } + inherited = os.environ.get("PYTHONPATH", "") + runtime_path = "matrixone-test-pythonpath" + if inherited: + runtime_path += os.pathsep + inherited + with mock.patch.dict(os.environ, {"PYTHONPATH": runtime_path}, clear=False): + result = worker._deserialize_record_batch( + worker._run_handler_process(None, request, 3) + ) + self.assertEqual([True], result.column(0).to_pylist()) + def test_handler_cannot_forge_completion_on_stdout(self): descriptor = {"type_id": worker.INT64, "offset_width": 32} batch = pa.RecordBatch.from_arrays([pa.array([77], type=pa.int64())], ["arg_0"]) diff --git a/pkg/udf/python/worker/worker.py b/pkg/udf/python/worker/worker.py index fee826d826f65..f73ea706dc18d 100644 --- a/pkg/udf/python/worker/worker.py +++ b/pkg/udf/python/worker/worker.py @@ -240,7 +240,10 @@ class _NoFlightServerBase: _HANDLER_REQUEST_BATCH = "batch" _HANDLER_RESPONSE_FD_ENV = "MATRIXONE_HANDLER_RESPONSE_FD" _HANDLER_PARENT_WATCH_FD_ENV = "MATRIXONE_HANDLER_PARENT_WATCH_FD" -_HANDLER_ENV_ALLOWLIST = frozenset({"PATH"}) +# UT may install the checked-out worker requirements into a temporary target +# and expose it through PYTHONPATH. Preserve only that runtime path in handler +# children; the rest of the parent environment remains excluded. +_HANDLER_ENV_ALLOWLIST = frozenset({"PATH", "PYTHONPATH"}) _MICROS_PER_SECOND = 1_000_000 _MAX_TIME_MICROS = (838 * 60 * 60 + 59 * 60 + 59) * _MICROS_PER_SECOND _MIN_TIMESTAMP = _datetime.datetime(1970, 1, 1, 0, 0, 1, tzinfo=_datetime.timezone.utc) From 15ec92f18ba5438f23fc8594a4c0101d7f72c365 Mon Sep 17 00:00:00 2001 From: iamlinjunhong <1030420200@qq.com> Date: Mon, 21 Sep 2026 08:54:13 +0800 Subject: [PATCH 195/202] test(optools): isolate Python dependency probe in scheduler harness --- optools/ut_schedule_test.go | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/optools/ut_schedule_test.go b/optools/ut_schedule_test.go index 069850ad1f602..502b6e2a36c1c 100644 --- a/optools/ut_schedule_test.go +++ b/optools/ut_schedule_test.go @@ -101,6 +101,27 @@ func scheduleHarnessWithMockTransform(t *testing.T, script, mock string, transfo if err := os.WriteFile(filepath.Join(dir, "go"), []byte(mock), 0755); err != nil { t.Fatal(err) } + // The scheduler fixture has no repository package tree, so it cannot run + // the real dependency bootstrap. Simulate only the already-prepared + // pyarrow probe and pass every other Python invocation to the host + // interpreter so the existing report/heartbeat helpers keep their coverage. + python3, err := exec.LookPath("python3") + if err != nil { + python3 = "" + } + pythonStub := `#!/bin/bash +if [[ "$#" == 2 && "$1" == -c && "$2" == "import pyarrow" ]]; then + exit 0 +fi +if [[ -n "${MATRIXONE_TEST_REAL_PYTHON3:-}" ]]; then + exec "${MATRIXONE_TEST_REAL_PYTHON3}" "$@" +fi +echo "python3 is unavailable in the scheduler harness" >&2 +exit 127 +` + if err := os.WriteFile(filepath.Join(dir, "python3"), []byte(pythonStub), 0755); err != nil { + t.Fatal(err) + } ctx, cancel := context.WithTimeout(context.Background(), 15*time.Second) defer cancel() cmd := exec.CommandContext(ctx, "bash", "-c", script) @@ -108,6 +129,9 @@ func scheduleHarnessWithMockTransform(t *testing.T, script, mock string, transfo cmd.WaitDelay = 3 * time.Second cmd.Dir = dir cmd.Env = append(os.Environ(), "PATH="+dir+string(os.PathListSeparator)+os.Getenv("PATH"), "UT_WORKDIR="+root, "CASE_DIR="+root, "UT_LINK_PARALLEL=0") + if python3 != "" { + cmd.Env = append(cmd.Env, "MATRIXONE_TEST_REAL_PYTHON3="+python3) + } cmd.Env = append(cmd.Env, variables...) out, err := cmd.CombinedOutput() if ctx.Err() != nil { From da8fa82da9f794a5b8ac911ca5b9188052ac3911 Mon Sep 17 00:00:00 2001 From: iamlinjunhong <1030420200@qq.com> Date: Mon, 21 Sep 2026 20:58:20 +0800 Subject: [PATCH 196/202] test(bootstrap): preserve historical upgrade worker fixture --- pkg/bootstrap/service_statistics_upgrade_test.go | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/pkg/bootstrap/service_statistics_upgrade_test.go b/pkg/bootstrap/service_statistics_upgrade_test.go index 8ccec6da847ce..f29e4de8cad14 100644 --- a/pkg/bootstrap/service_statistics_upgrade_test.go +++ b/pkg/bootstrap/service_statistics_upgrade_test.go @@ -186,9 +186,13 @@ func TestStatisticsUpgradeOldWorkerCannotCompleteNewTask(t *testing.T) { return executor.Result{AffectedRows: 1}, nil }, txnOp) + // Model the pre-PR 4.0.7 binary explicitly. Its handler had no + // tenant entries, so it could complete the offset-only task without + // running the current Python catalog migrations. + legacy407 := newTestVersionHandler("4.0.7", "4.0.6", versions.Yes, versions.Yes, 0) old := newServiceForTest("", &memLocker{}, clock.NewHLCClock(func() int64 { return 0 }, 0), nil, exec, - func(s *service) { s.handles = append(s.handles, v4_0_7.Handler) }) + func(s *service) { s.handles = append(s.handles, legacy407) }) defer old.stopper.Stop() // Both the worker admission code and this restored 4.0.7 handler are // unchanged from the old binary; no new-worker-only offset check is used. @@ -197,6 +201,11 @@ func TestStatisticsUpgradeOldWorkerCannotCompleteNewTask(t *testing.T) { require.Equal(t, test.oldCanClaim, hasWork) require.Equal(t, test.oldCanClaim, claimed) require.Equal(t, test.oldCanClaim, ready) + wantLegacyCalls := uint64(0) + if test.oldCanClaim { + wantLegacyCalls = 1 + } + require.Equal(t, wantLegacyCalls, legacy407.callHandleTenantUpgrade.Load()) require.Equal(t, legacy, definition) require.Zero(t, creates) From ae4e77a065df81d39e7d89b8433722d9862af504 Mon Sep 17 00:00:00 2001 From: iamlinjunhong <1030420200@qq.com> Date: Wed, 23 Sep 2026 18:38:18 +0800 Subject: [PATCH 197/202] test(udf): cover CI contracts and failure paths --- optools/ut_holder_test.go | 40 +- pkg/cnservice/python_runtime_test.go | 81 ++ pkg/frontend/clone_database_source_test.go | 92 +++ pkg/frontend/compiler_context_test.go | 107 +++ pkg/frontend/python_udf_catalog_test.go | 290 +++++++ pkg/frontend/routine_plan_validation_test.go | 146 ++++ pkg/sql/colexec/external_routine_eval_test.go | 264 +++++- pkg/udf/python/arrow_test.go | 20 +- pkg/udf/python/config_test.go | 40 + pkg/udf/python/gateway_test.go | 776 ++++++++++++++++++ pkg/udf/runtime_test.go | 66 ++ 11 files changed, 1919 insertions(+), 3 deletions(-) diff --git a/optools/ut_holder_test.go b/optools/ut_holder_test.go index f6a77fa6ca871..fc2f5fc235d4e 100644 --- a/optools/ut_holder_test.go +++ b/optools/ut_holder_test.go @@ -40,7 +40,9 @@ func TestUTAdmissionHolderEvidence(t *testing.T) { if err := os.WriteFile(path, []byte(strings.Join(tt.events, "\n")), 0600); err != nil { t.Fatal(err) } - out, err := exec.Command("python3", "summarize_ut_setup.py", path).CombinedOutput() + cmd := exec.Command("python3", "summarize_ut_setup.py", path) + cmd.Env = pythonStdlibEnvironment() + out, err := cmd.CombinedOutput() if err != nil { t.Fatalf("summary: %v\n%s", err, out) } @@ -54,3 +56,39 @@ func TestUTAdmissionHolderEvidence(t *testing.T) { }) } } + +func TestPythonStdlibEnvironmentDropsNativeAndImportOverrides(t *testing.T) { + t.Setenv("LD_LIBRARY_PATH", "/matrixone/cgo") + t.Setenv("PYTHONPATH", "/matrixone/python") + t.Setenv("PYTHONHOME", "/matrixone/python-home") + t.Setenv("PYTHONUSERBASE", "/matrixone/user-site") + + env := pythonStdlibEnvironment() + if !strings.Contains(strings.Join(env, "\n"), "PATH=") { + t.Fatal("sanitized Python environment lost PATH") + } + for _, entry := range env { + for _, forbidden := range []string{"LD_LIBRARY_PATH=", "PYTHONPATH=", "PYTHONHOME=", "PYTHONUSERBASE="} { + if strings.HasPrefix(entry, forbidden) { + t.Fatalf("Python stdlib helper inherited %q", entry) + } + } + } +} + +// pythonStdlibEnvironment keeps the configured interpreter discoverable while +// preventing MatrixOne's native-library and Python import paths from changing +// a standard-library-only report parser's runtime. +func pythonStdlibEnvironment() []string { + env := make([]string, 0, len(os.Environ())) + for _, entry := range os.Environ() { + key, _, _ := strings.Cut(entry, "=") + switch key { + case "LD_LIBRARY_PATH", "PYTHONHOME", "PYTHONPATH", "PYTHONUSERBASE", "PYTHONSTARTUP", "PYTHONINSPECT": + continue + default: + env = append(env, entry) + } + } + return env +} diff --git a/pkg/cnservice/python_runtime_test.go b/pkg/cnservice/python_runtime_test.go index e3eff6af6fed6..1052bdffd7396 100644 --- a/pkg/cnservice/python_runtime_test.go +++ b/pkg/cnservice/python_runtime_test.go @@ -18,6 +18,8 @@ import ( "context" "testing" + "github.com/matrixorigin/matrixone/pkg/pb/metadata" + "github.com/matrixorigin/matrixone/pkg/pb/query" "github.com/matrixorigin/matrixone/pkg/udf" "github.com/matrixorigin/matrixone/pkg/udf/python" "github.com/stretchr/testify/require" @@ -51,3 +53,82 @@ func TestBuildCNRuntimeDisabledDoesNotCreatePythonRuntime(t *testing.T) { require.True(t, ok) require.ErrorContains(t, readiness.CheckLanguageReady(context.Background(), udf.LanguagePython), "not enabled") } + +func TestUnavailablePythonRuntimeRejectsOperationsWithoutAWorker(t *testing.T) { + runtime := &unavailablePythonRuntime{} + require.Equal(t, udf.LanguagePython, runtime.Language()) + require.ErrorContains(t, runtime.Execute( + context.Background(), nil, nil, nil, + ), "Python UDF runtime initialization failed") + require.ErrorContains(t, runtime.CheckLanguageReady(context.Background(), "sql"), "unsupported readiness language") + require.ErrorContains(t, runtime.CheckLanguageReady(context.Background(), udf.LanguagePython), "RESOURCE_UNAVAILABLE") + + canceled, cancel := context.WithCancel(context.Background()) + cancel() + require.ErrorIs(t, runtime.CheckLanguageReady(canceled, udf.LanguagePython), context.Canceled) + require.ErrorContains(t, runtime.ValidateDefinition(context.Background(), nil), "invalid Python definition") + require.ErrorContains(t, runtime.ValidateDefinition(context.Background(), &udf.RoutineDefinition{Language: "sql"}), "invalid Python definition") + require.Equal(t, udf.RuntimeStatusUnavailable, + runtime.StatusSnapshot(context.Background(), "sql").ErrorClass) +} + +type pythonStatusRuntime struct { + udf.Runtime + snapshot udf.RuntimeStatusSnapshot +} + +func (r *pythonStatusRuntime) StatusSnapshot(context.Context, string) udf.RuntimeStatusSnapshot { + return r.snapshot +} + +type pythonRuntimeWithoutStatus struct{ udf.Runtime } + +func TestGetPythonUdfStatusReturnsCNLocalBoundedRuntimeState(t *testing.T) { + status := udf.RuntimeStatusSnapshot{ + Language: udf.LanguagePython, + Enabled: true, + AllowUnisolated: true, + Ready: true, + ProtocolVersion: 1, + ABIContract: udf.PythonABIContract, + AdapterVersion: udf.PythonAdapterVersion, + SDKVersion: udf.PythonSDKVersion, + DefinitionSchemaVersion: 1, + PlanContractVersion: 1, + TypeDescriptorContract: "arrow-sql-v1", + TimezoneDatabaseVersion: "2026c", + WindowBatches: 1, + MaxExecutionFrameBytes: 1 << 30, + MaxHandlerProcesses: 8, + MaxAccountHandlerProcesses: 8, + MaxOwnerHandlerProcesses: 4, + LeaseEpoch: 42, + Modes: []string{"SCALAR", "VECTOR"}, + NullPolicies: []string{"CALLED_ON_NULL_INPUT", "RETURNS_NULL_ON_NULL_INPUT"}, + } + s := &service{ + metadata: metadata.CNStore{UUID: "cn-local"}, + udfService: &pythonStatusRuntime{Runtime: &unavailablePythonRuntime{}, snapshot: status}, + } + response := &query.Response{} + require.NoError(t, s.handleGetPythonUdfStatus(context.Background(), nil, response, nil)) + got := response.GetPythonUdfStatus + require.Equal(t, "cn-local", got.CNUUID) + require.Equal(t, udf.LanguagePython, got.Language) + require.True(t, got.Enabled) + require.True(t, got.AllowUnisolated) + require.True(t, got.Ready) + require.Equal(t, int32(1), got.ProtocolVersion) + require.Equal(t, udf.PythonABIContract, got.ABIContract) + require.Equal(t, uint64(42), got.LeaseEpoch) + require.Equal(t, []string{"SCALAR", "VECTOR"}, got.Modes) + require.Equal(t, []string{"CALLED_ON_NULL_INPUT", "RETURNS_NULL_ON_NULL_INPUT"}, got.NullPolicies) + + // The query service has a stable fail-closed status if an older/custom + // Runtime implementation does not provide the bounded status contract. + s.udfService = pythonRuntimeWithoutStatus{} + response = &query.Response{} + require.NoError(t, s.handleGetPythonUdfStatus(context.Background(), nil, response, nil)) + require.Equal(t, udf.RuntimeStatusUnavailable, response.GetPythonUdfStatus.ErrorClass) + require.Equal(t, udf.RuntimeStatusReasonStatusUnavailable, response.GetPythonUdfStatus.Reason) +} diff --git a/pkg/frontend/clone_database_source_test.go b/pkg/frontend/clone_database_source_test.go index 01a0e16ece6c3..c7290cb953ef4 100644 --- a/pkg/frontend/clone_database_source_test.go +++ b/pkg/frontend/clone_database_source_test.go @@ -22,7 +22,11 @@ import ( "github.com/golang/mock/gomock" "github.com/matrixorigin/matrixone/pkg/catalog" + "github.com/matrixorigin/matrixone/pkg/container/types" "github.com/matrixorigin/matrixone/pkg/defines" + "github.com/matrixorigin/matrixone/pkg/fileservice" + "github.com/matrixorigin/matrixone/pkg/sql/plan/function" + pythonudf "github.com/matrixorigin/matrixone/pkg/udf/python" "github.com/stretchr/testify/require" mock_frontend "github.com/matrixorigin/matrixone/pkg/frontend/test" @@ -343,6 +347,94 @@ func TestRestoreCloneDatabaseUserDefinedFunctions(t *testing.T) { require.Len(t, failing.executedSQLs, 1) } +func TestValidateCloneUserDefinedFunctionsRejectsInertOrMalformedPython(t *testing.T) { + validPython := pythonCatalogTestBody(t, types.T_int64) + for _, tc := range []struct { + name string + definition userDefinedFunctionDefinition + wantError string + }{ + { + name: "current Python definition is cloneable", + definition: userDefinedFunctionDefinition{name: "f", lang: "python", body: validPython}, + }, + { + name: "old Python demo is rejected", + definition: userDefinedFunctionDefinition{name: "legacy", lang: "python", body: `{"handler":"f","source":"old"}`}, + wantError: "unsupported python function legacy", + }, + { + name: "malformed body is rejected", + definition: userDefinedFunctionDefinition{name: "broken", lang: "python", body: "{"}, + wantError: "malformed python function broken", + }, + { + name: "imported non SQL function is rejected", + definition: userDefinedFunctionDefinition{name: "imported", lang: "python", body: `{"import":true}`}, + wantError: "imported python function imported", + }, + { + name: "SQL definition keeps existing clone behavior", + definition: userDefinedFunctionDefinition{name: "sql_f", lang: "sql", body: "select 1"}, + }, + } { + t.Run(tc.name, func(t *testing.T) { + err := validateCloneUserDefinedFunctions([]userDefinedFunctionDefinition{tc.definition}) + if tc.wantError == "" { + require.NoError(t, err) + } else { + require.ErrorContains(t, err, tc.wantError) + } + }) + } +} + +func TestRestoreCloneDatabasePythonRoutineRepublishesTargetArtifact(t *testing.T) { + ctx := context.Background() + const accountID = uint32(27) + tenant := &TenantInfo{User: "root1", TenantID: accountID, DefaultRoleID: accountAdminRoleID} + body := pythonCatalogTestBody(t, types.T_int64) + decoded, err := function.DecodePythonRoutineBody(body) + require.NoError(t, err) + inputDescriptor, _, _, err := function.PythonSignatureMetadata(decoded.ArgTypes, decoded.ReturnType) + require.NoError(t, err) + definition := userDefinedFunctionDefinition{ + name: "cloned_python", + args: `[{"name":"value","type":"bigint"}]`, + argTypes: `["bigint"]`, + retType: "bigint", + body: body, + lang: "python", + dbName: "source_db", + canonicalInputDescriptor: inputDescriptor, + } + findSQL := `select function_id from mo_catalog.mo_user_defined_function where name = "cloned_python" and db = "target_db" and canonical_input_descriptor = "` + escapeSQLStringForDoubleQuotes(inputDescriptor) + `" and language = 'python' order by function_id desc limit 1;` + background := &backgroundExecTest{} + background.init() + background.sql2result[findSQL] = singleInt64Result("function_id", 97) + fs, err := fileservice.NewMemoryFS(defines.SharedFileServiceName, fileservice.DisabledCacheConfig, nil) + require.NoError(t, err) + + require.NoError(t, restoreCloneDatabaseUserDefinedFunctions( + ctx, background, tenant, []userDefinedFunctionDefinition{definition}, "target_db", fs, + )) + require.Contains(t, background.executedSQLs[0], `"cloned_python"`) + require.Contains(t, background.executedSQLs[0], `"target_db"`) + store, err := pythonudf.NewFileArtifactStore(fs, pythonudf.DefaultMaxArtifactBytes) + require.NoError(t, err) + resolved, err := store.Resolve(ctx, uint64(accountID), decoded.Handler, decoded.ArtifactDigest) + require.NoError(t, err) + require.Equal(t, decoded.Source, resolved) + + withoutStore := &backgroundExecTest{} + withoutStore.init() + err = restoreCloneDatabaseUserDefinedFunctions( + ctx, withoutStore, tenant, []userDefinedFunctionDefinition{definition}, "target_db", + ) + require.ErrorContains(t, err, "artifact store is required") + require.Empty(t, withoutStore.executedSQLs, "a routine must not be persisted before its artifact store is available") +} + func TestRestoreCloneDatabaseStoredProcedures(t *testing.T) { ctx := context.Background() tenant := &TenantInfo{User: "root1", DefaultRoleID: accountAdminRoleID} diff --git a/pkg/frontend/compiler_context_test.go b/pkg/frontend/compiler_context_test.go index 9000e43836972..7ad143dbf191a 100644 --- a/pkg/frontend/compiler_context_test.go +++ b/pkg/frontend/compiler_context_test.go @@ -34,6 +34,7 @@ import ( "github.com/matrixorigin/matrixone/pkg/sql/plan/function" "github.com/matrixorigin/matrixone/pkg/testutil" "github.com/matrixorigin/matrixone/pkg/txn/client" + "github.com/matrixorigin/matrixone/pkg/udf" "github.com/matrixorigin/matrixone/pkg/vm/engine" "github.com/stretchr/testify/require" ) @@ -87,6 +88,112 @@ func TestSubscriptionMetasFromSubInfos(t *testing.T) { }, metas) } +func TestResolveUdfBindsExactPythonRevisionAndNamespace(t *testing.T) { + ctx := defines.AttachAccountId(context.Background(), 0) + ses := &Session{feSessionImpl: feSessionImpl{}} + ses.SetAccountId(0) + background := &backgroundExecTestWithHistory{} + background.init() + previousNewBackgroundExec := NewBackgroundExec + NewBackgroundExec = func(context.Context, FeSession, ...*BackgroundExecOption) BackgroundExec { + return background + } + t.Cleanup(func() { NewBackgroundExec = previousNewBackgroundExec }) + + body := pythonCatalogTestBody(t, types.T_int64) + decoded, err := function.DecodePythonRoutineBody(body) + require.NoError(t, err) + logicalArgs := `[{"name":"value","type":"bigint"}]` + compiler := &TxnCompilerContext{execCtx: &ExecCtx{reqCtx: ctx, ses: ses}} + compiler.SetDatabase("app") + _, candidateSQL := udfCatalogLookup(ctx, nil, "f", "app") + background.sql2result[candidateSQL] = resolveUdfCatalogResult( + []string{"function_id", "args", "body", "language", "rettype", "db", "modified_time", "sql_mode"}, + []interface{}{int64(44), logicalArgs, body, "python", "bigint", "app", "2026-09-20 00:00:00", ""}, + ) + + inputDescriptor, returnDescriptor, signatureFingerprint, err := function.PythonSignatureMetadata(decoded.ArgTypes, decoded.ReturnType) + require.NoError(t, err) + definitionFingerprint, err := function.PythonRoutineFingerprint(body) + require.NoError(t, err) + background.sql2result[pythonRevisionCatalogSQL(nil, 44)] = resolveUdfCatalogResult( + []string{ + "active_revision", "namespace_version", "revision", "canonical_input_descriptor", "return_descriptor", + "signature_key_schema_version", "signature_fingerprint", "args", "arg_types", "body", "language", + "rettype", "definition_schema_version", "abi_contract", "adapter_version", "sdk_version", "null_policy", + "volatility", "definition_fingerprint", "artifact_digest", "environment_digest", "revision_security_type", "head_security_type", + }, + []interface{}{ + int64(1), int64(1), int64(1), inputDescriptor, returnDescriptor, int64(udf.PythonSignatureKeySchemaVersion), + signatureFingerprint, logicalArgs, inputDescriptor, body, "python", "bigint", int64(udf.PythonDefinitionSchemaVersion), + udf.PythonABIContract, udf.PythonAdapterVersion, udf.PythonSDKVersion, udf.NullCallHandler, "VOLATILE", + definitionFingerprint, decoded.ArtifactDigest, decoded.EnvironmentDigest, "INVOKER", "INVOKER", + }, + ) + background.sql2result["select dat_id from mo_catalog.mo_database where datname = 'app' and account_id = 0;"] = singleInt64Result("dat_id", 88) + namespaceSQL := routineNamespacesSQL([]uint64{44}, nil) + background.sql2result[namespaceSQL] = namespaceRows(namespaceMember(44, "candidate-state")) + + argumentType := types.T_int64.ToType() + resolved, err := compiler.ResolveUdf("f", []*pbplan.Expr{{Typ: plan2.MakePlan2Type(&argumentType)}}) + require.NoError(t, err) + require.Equal(t, int64(44), resolved.FunctionID) + require.Equal(t, uint64(1), resolved.Revision) + require.Equal(t, uint64(1), resolved.NamespaceVersion) + require.Equal(t, uint64(88), resolved.DatabaseID) + require.NotEmpty(t, resolved.NamespaceFingerprint) + require.Equal(t, []types.Type{types.T_int64.ToType()}, resolved.ArgsType) + require.Contains(t, background.executedSqls, candidateSQL) + require.Contains(t, background.executedSqls, pythonRevisionCatalogSQL(nil, 44)) + require.Contains(t, background.executedSqls, namespaceSQL) +} + +func TestResolveUdfKeepsLegacySQLRoutineReadableWithoutRevisionCatalog(t *testing.T) { + ctx := defines.AttachAccountId(context.Background(), 0) + ses := &Session{feSessionImpl: feSessionImpl{}} + ses.SetAccountId(0) + background := &backgroundExecTestWithHistory{} + background.init() + previousNewBackgroundExec := NewBackgroundExec + NewBackgroundExec = func(context.Context, FeSession, ...*BackgroundExecOption) BackgroundExec { + return background + } + t.Cleanup(func() { NewBackgroundExec = previousNewBackgroundExec }) + + compiler := &TxnCompilerContext{execCtx: &ExecCtx{reqCtx: ctx, ses: ses}} + compiler.SetDatabase("app") + _, candidateSQL := udfCatalogLookup(ctx, nil, "legacy_f", "app") + background.sql2result[candidateSQL] = resolveUdfCatalogResult( + []string{"function_id", "args", "body", "language", "rettype", "db", "modified_time", "sql_mode"}, + []interface{}{int64(52), `[{"name":"value","type":"bigint"}]`, "select value + 1", "sql", "bigint", "app", "2026-09-20 00:00:00", ""}, + ) + background.sql2err["select active_revision, namespace_version, security_type from mo_catalog.mo_user_defined_function where function_id = 52;"] = errors.New("no such table: mo_function_revisions") + argumentType := types.T_int64.ToType() + resolved, err := compiler.ResolveUdf("legacy_f", []*pbplan.Expr{{Typ: plan2.MakePlan2Type(&argumentType)}}) + require.NoError(t, err) + require.Equal(t, int64(52), resolved.FunctionID) + require.Equal(t, "sql", resolved.Language) + require.Zero(t, resolved.Revision, "a legacy SQL definition stays on its existing body while the shared revision table is absent") + require.Equal(t, []types.Type{types.T_int64.ToType()}, resolved.ArgsType) + require.Contains(t, background.executedSqls, "select active_revision, namespace_version, security_type from mo_catalog.mo_user_defined_function where function_id = 52;") +} + +func resolveUdfCatalogResult(columns []string, row []interface{}) *MysqlResultSet { + result := &MysqlResultSet{} + for index, name := range columns { + column := &MysqlColumn{} + column.SetName(name) + if _, ok := row[index].(int64); ok { + column.SetColumnType(defines.MYSQL_TYPE_LONGLONG) + } else { + column.SetColumnType(defines.MYSQL_TYPE_VAR_STRING) + } + result.AddColumn(column) + } + result.AddRow(row) + return result +} + func TestSubscriptionMetadataEnumerationObservesCancellation(t *testing.T) { wantErr := errors.New("stop subscription metadata enumeration") ctx, cancel := context.WithCancelCause(context.Background()) diff --git a/pkg/frontend/python_udf_catalog_test.go b/pkg/frontend/python_udf_catalog_test.go index a454781694c31..849e7bdc85ec0 100644 --- a/pkg/frontend/python_udf_catalog_test.go +++ b/pkg/frontend/python_udf_catalog_test.go @@ -17,6 +17,7 @@ package frontend import ( "context" "encoding/json" + "errors" "fmt" "strconv" "strings" @@ -100,6 +101,123 @@ func TestValidatePythonUdfDefinitionAtCreateUsesTypedWorkerGate(t *testing.T) { require.Equal(t, types.T_int64.ToType(), runtime.definition.ReturnType) } +func TestInitPythonFunctionValidatesArtifactBeforeCatalogReadiness(t *testing.T) { + const service = "python-create-catalog-gate-test" + InitServerLevelVars(service) + previousPU := getPuIfPresent(service) + t.Cleanup(func() { setPu(service, previousPU) }) + + fs, err := fileservice.NewMemoryFS(defines.SharedFileServiceName, fileservice.DisabledCacheConfig, nil) + require.NoError(t, err) + runtime := &definitionValidationRuntime{} + pu := config.NewParameterUnit(&config.FrontendParameters{}, nil, nil, nil) + pu.FileService = fs + pu.UdfService = runtime + setPu(service, pu) + + ctx := defines.AttachAccountId(context.Background(), 27) + statement, err := parsers.ParseOne(ctx, dialect.MYSQL, + "create function app.py_echo(value bigint) returns bigint language python as 'def py_echo(ctx, value): return value' handler 'py_echo' mode scalar called on null input", 1) + require.NoError(t, err) + defer statement.Free() + createFunction, ok := statement.(*tree.CreateFunction) + require.True(t, ok) + + background := &backgroundExecTestWithHistory{} + background.init() + background.sql2result[pythonUdfCatalogIdentitySchemaCheck] = emptyCatalogProbeResult(6) + missingRevisionCatalog := errors.New("revision catalog has not been upgraded") + background.sql2err[functionRevisionCatalogSchemaCheck] = missingRevisionCatalog + previousNewBackgroundExec := NewBackgroundExec + NewBackgroundExec = func(context.Context, FeSession, ...*BackgroundExecOption) BackgroundExec { + return background + } + t.Cleanup(func() { NewBackgroundExec = previousNewBackgroundExec }) + + tenant := &TenantInfo{User: "root", TenantID: 27, DefaultRoleID: accountAdminRoleID} + session := &Session{feSessionImpl: feSessionImpl{service: service, tenant: tenant}} + err = InitFunction(session, &ExecCtx{reqCtx: ctx}, tenant, createFunction) + require.ErrorContains(t, err, "Python UDF catalog contract is not ready") + require.ErrorContains(t, err, missingRevisionCatalog.Error()) + require.NotNil(t, runtime.definition, "CREATE must validate the source with the current worker before opening the catalog transaction") + require.Equal(t, uint64(27), runtime.definition.AccountID) + require.Equal(t, "py_echo", runtime.definition.Handler) + require.Equal(t, "def py_echo(ctx, value): return value", runtime.definition.Source) + require.Equal(t, "SCALAR", runtime.definition.Mode) + require.Equal(t, udf.NullCallHandler, runtime.definition.NullPolicy) + + store, err := pythonudf.NewFileArtifactStore(fs, pythonudf.DefaultMaxArtifactBytes) + require.NoError(t, err) + resolved, err := store.Resolve(ctx, 27, runtime.definition.Handler, runtime.definition.ArtifactDigest) + require.NoError(t, err) + require.Equal(t, runtime.definition.Source, resolved) + for _, query := range background.executedSqls { + require.NotContains(t, query, "insert into mo_catalog.mo_user_defined_function") + require.NotContains(t, query, "insert into mo_catalog.mo_function_revisions") + } +} + +func TestInitPythonFunctionChecksExactOverloadBeforePublishing(t *testing.T) { + const service = "python-create-overload-check-test" + InitServerLevelVars(service) + previousPU := getPuIfPresent(service) + t.Cleanup(func() { setPu(service, previousPU) }) + + fs, err := fileservice.NewMemoryFS(defines.SharedFileServiceName, fileservice.DisabledCacheConfig, nil) + require.NoError(t, err) + runtime := &definitionValidationRuntime{} + pu := config.NewParameterUnit(&config.FrontendParameters{}, nil, nil, nil) + pu.FileService = fs + pu.UdfService = runtime + setPu(service, pu) + + ctx := defines.AttachAccountId(context.Background(), 27) + statement, err := parsers.ParseOne(ctx, dialect.MYSQL, + "create function app.py_echo(value bigint) returns bigint language python as 'def py_echo(ctx, value): return value' handler 'py_echo'", 1) + require.NoError(t, err) + defer statement.Free() + createFunction := statement.(*tree.CreateFunction) + + background := &backgroundExecTestWithHistory{} + background.init() + background.sql2result[pythonUdfCatalogIdentitySchemaCheck] = emptyCatalogProbeResult(6) + background.sql2result[functionRevisionCatalogSchemaCheck] = emptyCatalogProbeResult(20) + databaseSQL, err := getSqlForCheckDatabaseByAccount(ctx, "app") + require.NoError(t, err) + databaseSQL = strings.TrimSuffix(databaseSQL, ";") + " for update;" + background.sql2result[databaseSQL] = singleInt64Result("dat_id", 8) + decoded, err := function.DecodePythonRoutineBody(pythonCatalogTestBody(t, types.T_int64)) + require.NoError(t, err) + canonicalInput, _, _, err := function.PythonSignatureMetadata(decoded.ArgTypes, decoded.ReturnType) + require.NoError(t, err) + checkCandidatesSQL := fmt.Sprintf(checkPythonUdfExistence, "py_echo", "app") + background.sql2result[checkCandidatesSQL] = resolveUdfCatalogResult( + []string{"function_id", "args", "language", "arg_types", "canonical_input_descriptor"}, + []interface{}{int64(55), `[{"name":"value","type":"bigint"}]`, "python", `["bigint"]`, + canonicalInput}, + ) + previousNewBackgroundExec := NewBackgroundExec + NewBackgroundExec = func(context.Context, FeSession, ...*BackgroundExecOption) BackgroundExec { + return background + } + t.Cleanup(func() { NewBackgroundExec = previousNewBackgroundExec }) + + tenant := &TenantInfo{User: "root", TenantID: 27, DefaultRoleID: accountAdminRoleID} + session := &Session{feSessionImpl: feSessionImpl{service: service, tenant: tenant}} + err = InitFunction(session, &ExecCtx{reqCtx: ctx}, tenant, createFunction) + require.ErrorContains(t, err, "already exists") + require.NotNil(t, runtime.definition, "the submitted source must be validated before the locked exact-signature check") + require.Equal(t, uint64(27), runtime.definition.AccountID) + require.Equal(t, "py_echo", runtime.definition.Handler) + require.Contains(t, background.executedSqls, databaseSQL) + require.Contains(t, background.executedSqls, checkCandidatesSQL) + require.Contains(t, background.executedSqls, "rollback;") + for _, query := range background.executedSqls { + require.NotContains(t, query, "insert into mo_catalog.mo_user_defined_function") + require.NotContains(t, query, "insert into mo_catalog.mo_function_revisions") + } +} + func TestPersistPythonReplaceRejectsLegacyHeadBeforeWriting(t *testing.T) { ctx := context.Background() functionID := int64(41) @@ -686,6 +804,114 @@ func TestReadSQLRevisionBindsActiveHeadAndRejectsTampering(t *testing.T) { _, found, err = readSQLRevision(context.Background(), bh, functionID, nil) require.ErrorContains(t, err, "fingerprint mismatch") require.False(t, found) + + for _, tc := range []struct { + name string + mutateBase func(*MysqlResultSet) + mutateVersion func(*MysqlResultSet) + wantError string + wantFound bool + }{ + { + name: "legacy head before shared revision upgrade", + mutateBase: func(result *MysqlResultSet) { + result.Data[0][0], result.Data[0][1] = int64(0), int64(0) + }, + }, + { + name: "partial revision head", + mutateBase: func(result *MysqlResultSet) { + result.Data[0][0] = int64(0) + }, + wantError: "invalid revision head", + }, + { + name: "missing active revision row", + mutateVersion: func(result *MysqlResultSet) { + result.Data = nil + }, + wantError: "expected exactly one", + }, + { + name: "revision id differs from head", + mutateVersion: func(result *MysqlResultSet) { + result.Data[0][0] = int64(2) + }, + wantError: "revision contract is not supported", + }, + { + name: "wrong implementation language", + mutateVersion: func(result *MysqlResultSet) { + result.Data[0][5] = "python" + }, + wantError: "revision contract is not supported", + }, + { + name: "wrong definition schema", + mutateVersion: func(result *MysqlResultSet) { + result.Data[0][6] = int64(udf.SQLDefinitionSchemaVersion + 100) + }, + wantError: "revision contract is not supported", + }, + { + name: "non volatile revision", + mutateVersion: func(result *MysqlResultSet) { + result.Data[0][8] = "IMMUTABLE" + }, + wantError: "revision contract is not supported", + }, + { + name: "wrong null policy", + mutateVersion: func(result *MysqlResultSet) { + result.Data[0][9] = "STRICT" + }, + wantError: "revision contract is not supported", + }, + { + name: "security contract differs from head", + mutateVersion: func(result *MysqlResultSet) { + result.Data[0][10] = "INVOKER" + }, + wantError: "revision contract is not supported", + }, + { + name: "malformed logical argument metadata", + mutateVersion: func(result *MysqlResultSet) { + result.Data[0][1] = "{" + }, + wantError: "argument metadata is invalid", + }, + { + name: "logical argument metadata disagrees", + mutateVersion: func(result *MysqlResultSet) { + result.Data[0][2] = `["int"]` + }, + wantError: "argument metadata is invalid", + }, + } { + t.Run(tc.name, func(t *testing.T) { + background := &backgroundExecTest{} + background.init() + base := newBase() + if tc.mutateBase != nil { + tc.mutateBase(base) + } + background.sql2result[baseSQL] = base + version := newRevision(body, fingerprint) + if tc.mutateVersion != nil { + tc.mutateVersion(version) + } + background.sql2result[revisionSQL] = version + _, found, err := readSQLRevision(context.Background(), background, functionID, nil) + if tc.wantError != "" { + require.ErrorContains(t, err, tc.wantError) + require.False(t, found) + return + } + require.NoError(t, err) + require.Equal(t, tc.wantFound, found) + }) + } } func TestEnsurePythonUdfCatalogReadyRequiresCurrentSchema(t *testing.T) { @@ -780,6 +1006,70 @@ func TestValidateRestoredPythonRevisionCatalogChecksCurrentContract(t *testing.T ) } +func TestValidateRestoredPythonRevisionCatalogRejectsMalformedContracts(t *testing.T) { + body := pythonCatalogTestBody(t, types.T_int64) + decoded, err := function.DecodePythonRoutineBody(body) + require.NoError(t, err) + inputDescriptor, _, _, err := function.PythonSignatureMetadata(decoded.ArgTypes, decoded.ReturnType) + require.NoError(t, err) + fingerprint, err := function.PythonRoutineFingerprint(body) + require.NoError(t, err) + baseRow := []string{ + "44", "1", "1", "f", `[{"name":"value","type":"bigint"}]`, inputDescriptor, "bigint", body, + udf.LanguagePython, strconv.Itoa(udf.PythonDefinitionSchemaVersion), udf.PythonABIContract, + udf.PythonAdapterVersion, decoded.ArtifactDigest, decoded.EnvironmentDigest, udf.PythonSDKVersion, + decoded.NullPolicy, "VOLATILE", fingerprint, "2026-09-11 00:00:00", "INVOKER", + } + query := fmt.Sprintf( + "select %s from %s order by function_id, revision;", + functionRevisionCatalogColumns, + qualifiedTableName(moCatalog, "mo_function_revisions"), + ) + newResult := func(row []string) *MysqlResultSet { + result := &MysqlResultSet{} + for index := range row { + column := &MysqlColumn{} + column.SetName(fmt.Sprintf("column_%d", index)) + column.SetColumnType(defines.MYSQL_TYPE_VAR_STRING) + result.AddColumn(column) + } + values := make([]interface{}, len(row)) + for index := range row { + values[index] = row[index] + } + result.AddRow(values) + return result + } + for _, tc := range []struct { + name string + index int + value string + want string + }{ + {name: "function identity", index: 0, value: "not-an-id", want: "invalid function identity"}, + {name: "zero revision", index: 1, value: "0", want: "invalid revision"}, + {name: "zero namespace", index: 2, value: "0", want: "invalid namespace version"}, + {name: "definition schema", index: 9, value: "future", want: "invalid definition schema"}, + {name: "unsupported ABI", index: 10, value: "future-abi", want: "unsupported execution contract"}, + {name: "body encoding", index: 7, value: "{", want: "body is invalid"}, + {name: "artifact digest binding", index: 12, value: strings.Repeat("a", 64), want: "metadata does not match"}, + {name: "argument JSON", index: 4, value: "{", want: "arguments are invalid"}, + {name: "argument descriptor", index: 5, value: "stale-descriptor", want: "argument descriptor mismatch"}, + {name: "volatility", index: 16, value: "IMMUTABLE", want: "unsupported execution contract"}, + {name: "security mode", index: 19, value: "DEFINER", want: "unsupported execution contract"}, + } { + t.Run(tc.name, func(t *testing.T) { + row := append([]string(nil), baseRow...) + row[tc.index] = tc.value + background := &backgroundExecTest{} + background.init() + background.sql2result[query] = newResult(row) + err := validateRestoredFunctionRevisionCatalog(context.Background(), background, context.Background()) + require.ErrorContains(t, err, tc.want) + }) + } +} + func TestValidateRestoredSharedRevisionCatalogAcceptsSQLAndRejectsTampering(t *testing.T) { args := `[ {"name":"value","type":"bigint"} ]` argTypes, err := userDefinedFunctionArgumentTypesFromJSON(args) diff --git a/pkg/frontend/routine_plan_validation_test.go b/pkg/frontend/routine_plan_validation_test.go index 22369c04d2597..41d26bbfa2234 100644 --- a/pkg/frontend/routine_plan_validation_test.go +++ b/pkg/frontend/routine_plan_validation_test.go @@ -17,6 +17,7 @@ package frontend import ( "context" "encoding/json" + "errors" "strings" "testing" @@ -28,6 +29,68 @@ import ( "github.com/stretchr/testify/require" ) +type routinePlanValidationExec struct { + BackgroundExec + resultSets []interface{} + catalogResultSets []interface{} + namespaceResultSets []interface{} + failOn string + statements []string +} + +func (e *routinePlanValidationExec) Close() {} + +func (e *routinePlanValidationExec) Exec(_ context.Context, statement string) error { + e.statements = append(e.statements, statement) + if e.failOn != "" && strings.Contains(statement, e.failOn) { + return errors.New("injected routine catalog read failure") + } + if strings.Contains(statement, "select f.function_id") { + e.resultSets = e.catalogResultSets + } else if strings.Contains(statement, "select selected.function_id") { + e.resultSets = e.namespaceResultSets + } + return nil +} + +func (e *routinePlanValidationExec) GetExecResultSet() []interface{} { + return e.resultSets +} + +func (e *routinePlanValidationExec) ClearExecResultSet() { + e.resultSets = nil +} + +type routinePlanValidationSession struct { + FeSession + exec BackgroundExec +} + +func (s *routinePlanValidationSession) GetAccountId() uint32 { return 9 } + +func (s *routinePlanValidationSession) GetTxnCompileCtx() *TxnCompilerContext { return nil } + +func (s *routinePlanValidationSession) GetBackgroundExec(context.Context, ...*BackgroundExecOption) BackgroundExec { + return s.exec +} + +func routinePlanCatalogResultSet(values []interface{}) *MysqlResultSet { + result := &MysqlResultSet{} + for range values { + result.AddColumn(&MysqlColumn{}) + } + result.AddRow(values) + return result +} + +func emptyRoutineNamespaceResultSet() *MysqlResultSet { + result := &MysqlResultSet{} + for range 16 { + result.AddColumn(&MysqlColumn{}) + } + return result +} + func testRoutinePlanDependency() *planpb.RoutinePlanDependency { return &planpb.RoutinePlanDependency{ FunctionRef: &planpb.FunctionRef{ @@ -246,6 +309,89 @@ func TestCachedRoutinePlanWithDependencyRequiresCatalogSession(t *testing.T) { require.False(t, cachedRoutinePlanDependenciesCurrent(nil, withRoutine)) } +func TestValidateRoutinePlanDependenciesUsesCatalogTransactionAndFailsClosed(t *testing.T) { + ctx := context.Background() + plainPlan := &planpb.Plan{Plan: &planpb.Plan_Query{Query: &planpb.Query{}}} + changed, err := validateRoutinePlanDependencies(ctx, nil, plainPlan) + require.NoError(t, err) + require.False(t, changed) + + dependency := testRoutinePlanDependency() + plan := &planpb.Plan{Plan: &planpb.Plan_Query{Query: &planpb.Query{ + RoutineDependencies: []*planpb.RoutinePlanDependency{dependency}, + }}} + _, err = validateRoutinePlanDependencies(ctx, nil, plan) + require.ErrorContains(t, err, "has no session") + + incomplete := testRoutinePlanDependency() + incomplete.NamespaceFingerprint = "" + incompletePlan := &planpb.Plan{Plan: &planpb.Plan_Query{Query: &planpb.Query{ + RoutineDependencies: []*planpb.RoutinePlanDependency{incomplete}, + }}} + changed, err = validateRoutinePlanDependencies(ctx, &routinePlanValidationSession{}, incompletePlan) + require.NoError(t, err) + require.True(t, changed, "an incomplete namespace fence must invalidate without a catalog read") + + exec := &routinePlanValidationExec{} + session := &routinePlanValidationSession{exec: exec} + changed, err = validateRoutinePlanDependencies(ctx, session, plan) + require.NoError(t, err) + require.True(t, changed, "a missing active catalog revision invalidates the prepared plan") + require.Len(t, exec.statements, 3) + require.Equal(t, "begin;", exec.statements[0]) + require.Contains(t, exec.statements[1], "where f.function_id in (41)") + require.Equal(t, "commit;", exec.statements[2]) + + // A present but damaged immutable revision is fully decoded and invalidates + // the cached plan; namespace state is still read in the same transaction. + row := []interface{}{ + uint64(41), uint64(7), uint64(12), uint64(8), uint64(7), + "python", "VOLATILE", strings.Repeat("a", 64), strings.Repeat("b", 64), + strings.Repeat("c", 64), "[]", "{}", int64(udf.PythonSignatureKeySchemaVersion), + strings.Repeat("d", 64), "damaged body", "[]", "bigint", + int64(udf.PythonDefinitionSchemaVersion), udf.PythonABIContract, + udf.PythonAdapterVersion, udf.PythonSDKVersion, udf.NullCallHandler, + "INVOKER", "INVOKER", + } + fullExec := &routinePlanValidationExec{ + catalogResultSets: []interface{}{routinePlanCatalogResultSet(row)}, + namespaceResultSets: []interface{}{emptyRoutineNamespaceResultSet()}, + } + changed, err = validateRoutinePlanDependencies(ctx, + &routinePlanValidationSession{exec: fullExec}, plan) + require.NoError(t, err) + require.True(t, changed) + require.Len(t, fullExec.statements, 4) + require.Contains(t, fullExec.statements[2], "select selected.function_id") + require.Equal(t, "commit;", fullExec.statements[3]) + + for _, tc := range []struct { + name string + catalogResultSets []interface{} + failOn string + want string + rollback bool + }{ + {name: "transaction begin error", failOn: "begin;", want: "could not start"}, + {name: "catalog query error", failOn: "mo_user_defined_function", want: "validation failed", rollback: true}, + {name: "malformed result set", catalogResultSets: []interface{}{struct{}{}}, want: "not the type of result set", rollback: true}, + } { + t.Run(tc.name, func(t *testing.T) { + failingExec := &routinePlanValidationExec{catalogResultSets: tc.catalogResultSets, failOn: tc.failOn} + changed, err := validateRoutinePlanDependencies(ctx, + &routinePlanValidationSession{exec: failingExec}, plan) + require.False(t, changed) + require.ErrorContains(t, err, tc.want) + lastStatement := failingExec.statements[len(failingExec.statements)-1] + if tc.rollback { + require.Equal(t, "rollback;", lastStatement) + } else { + require.Equal(t, "begin;", lastStatement) + } + }) + } +} + func TestExactlyOneCatalogResultSetRequiresOneCompleteResultSet(t *testing.T) { ctx := context.Background() _, err := exactlyOneCatalogResultSet(ctx, nil, "routine dependency catalog") diff --git a/pkg/sql/colexec/external_routine_eval_test.go b/pkg/sql/colexec/external_routine_eval_test.go index a7a09181ed299..d905cb55113ac 100644 --- a/pkg/sql/colexec/external_routine_eval_test.go +++ b/pkg/sql/colexec/external_routine_eval_test.go @@ -17,6 +17,8 @@ package colexec import ( "context" "encoding/json" + "errors" + "strconv" "testing" "time" @@ -36,13 +38,14 @@ import ( type externalRoutineTestExecutor struct { vector *vector.Vector evalSeen *int + err error } func (e *externalRoutineTestExecutor) Eval(_ *process.Process, _ []*batch.Batch, _ []bool) (*vector.Vector, error) { if e.evalSeen != nil { *e.evalSeen++ } - return e.vector, nil + return e.vector, e.err } func (e *externalRoutineTestExecutor) EvalWithoutResultReusing(proc *process.Process, batches []*batch.Batch, selectList []bool) (*vector.Vector, error) { @@ -61,12 +64,20 @@ type externalRoutineCaptureRuntime struct { type externalRoutineEmptySuccessRuntime struct{} +type externalRoutineErrorRuntime struct{ err error } + func (r *externalRoutineEmptySuccessRuntime) Language() string { return udf.LanguagePython } func (r *externalRoutineEmptySuccessRuntime) Execute(_ context.Context, _ *udf.Invocation, _ vector.FunctionResultWrapper, _ *mpool.MPool) error { return nil } +func (r *externalRoutineErrorRuntime) Language() string { return udf.LanguagePython } + +func (r *externalRoutineErrorRuntime) Execute(_ context.Context, _ *udf.Invocation, _ vector.FunctionResultWrapper, _ *mpool.MPool) error { + return r.err +} + func (r *externalRoutineCaptureRuntime) Language() string { return udf.LanguagePython } func (r *externalRoutineCaptureRuntime) Execute(_ context.Context, invocation *udf.Invocation, result vector.FunctionResultWrapper, mp *mpool.MPool) error { @@ -254,6 +265,39 @@ func TestExternalRoutineEvalRequiresRowDomainForZeroArgumentVector(t *testing.T) require.ErrorContains(t, err, "input batch 0 is nil") } +func TestExternalRoutineRowCountValidatesAllJoinDomains(t *testing.T) { + first := batch.NewWithSize(0) + first.SetRowCount(3) + second := batch.NewWithSize(0) + second.SetRowCount(3) + short := batch.NewWithSize(0) + short.SetRowCount(2) + for _, tc := range []struct { + name string + mode string + batches []*batch.Batch + want int + err string + }{ + {name: "aligned join row domains", mode: "SCALAR", batches: []*batch.Batch{first, second}, want: 3}, + {name: "scalar without explicit batch has one row", mode: "SCALAR", want: 1}, + {name: "empty vector batch carries zero rows", mode: "VECTOR", batches: []*batch.Batch{{}}, err: ""}, + {name: "nil later relation", mode: "SCALAR", batches: []*batch.Batch{first, nil}, err: "input batch 1 is nil"}, + {name: "unequal join cardinality", mode: "SCALAR", batches: []*batch.Batch{first, short}, err: "has 2 rows, expected 3"}, + } { + t.Run(tc.name, func(t *testing.T) { + rows, err := externalRoutineRowCount(tc.mode, tc.batches) + if tc.err != "" { + require.Zero(t, rows) + require.ErrorContains(t, err, tc.err) + return + } + require.NoError(t, err) + require.Equal(t, tc.want, rows) + }) + } +} + func TestExternalRoutineEvalAcceptsAlignedJoinBatches(t *testing.T) { first := batch.NewWithSize(0) first.SetRowCount(1) @@ -405,6 +449,100 @@ func TestBuildRoutineContextRejectsSubMinuteFixedTimezone(t *testing.T) { require.ErrorContains(t, err, "whole number of minutes") } +func TestBuildRoutineContextRejectsMissingStatementInputs(t *testing.T) { + if _, err := buildRoutineContext(nil); err == nil { + t.Fatal("nil process must not produce an invocation context") + } + if _, err := buildRoutineContext(&process.Process{}); err == nil { + t.Fatal("process without Base must not produce an invocation context") + } + proc := testutil.NewProcess(t) + defer proc.Free() + proc.Base.StmtProfile = &process.StmtProfile{} + proc.GetSessionInfo().TimeZone = time.UTC + proc.Base.UnixTime = 0 + _, err := buildRoutineContext(proc) + require.ErrorContains(t, err, "statement timestamp is unavailable") + + proc.Base.UnixTime = time.Now().UnixNano() + proc.GetSessionInfo().TimeZone = nil + _, err = buildRoutineContext(proc) + require.ErrorContains(t, err, "session timezone is unavailable") +} + +func TestResolveSystemTimezoneUsesExplicitStableZone(t *testing.T) { + t.Setenv("TZ", "posix/UTC") + name, location, err := resolveSystemTimezone() + require.NoError(t, err) + require.Equal(t, "UTC", name) + require.Equal(t, "UTC", location.String()) +} + +func TestBuildRoutineContextUsesStableStatementAndSessionSnapshot(t *testing.T) { + for _, tc := range []struct { + name string + sessionMode string + resolve func() (interface{}, error) + wantMode string + }{ + { + name: "resolved SQL mode overrides session fallback", + sessionMode: "ANSI", + resolve: func() (interface{}, error) { return "strict_trans_tables, ansi,STRICT_TRANS_TABLES", nil }, + wantMode: `["ANSI","STRICT_TRANS_TABLES"]`, + }, + { + name: "resolver error keeps session SQL mode", + sessionMode: "ansi, strict_trans_tables", + resolve: func() (interface{}, error) { return nil, errors.New("variable unavailable") }, + wantMode: `["ANSI","STRICT_TRANS_TABLES"]`, + }, + { + name: "non-string resolution keeps session SQL mode", + sessionMode: "ANSI", + resolve: func() (interface{}, error) { return int64(1), nil }, + wantMode: `["ANSI"]`, + }, + { + name: "empty sentinel becomes empty mode list", + sessionMode: process.EmptySqlModeSentinel, + wantMode: `[]`, + }, + } { + t.Run(tc.name, func(t *testing.T) { + proc := testutil.NewProcess(t) + defer proc.Free() + proc.SetQueryId("") + proc.GetSessionInfo().TimeZone = time.UTC + proc.GetSessionInfo().User = "root" + proc.GetSessionInfo().Database = "" + proc.GetSessionInfo().Role = "" + proc.GetSessionInfo().SqlMode = tc.sessionMode + proc.Base.StmtProfile = &process.StmtProfile{} + proc.Base.UnixTime = time.Date(2026, 9, 23, 1, 2, 3, 4000, time.UTC).UnixNano() + if tc.resolve != nil { + proc.SetResolveVariableFunc(func(name string, system, global bool) (interface{}, error) { + require.Equal(t, "sql_mode", name) + require.True(t, system) + require.False(t, global) + return tc.resolve() + }) + } + + values, err := buildRoutineContext(proc) + require.NoError(t, err) + require.NotContains(t, values, "statement_id") + require.Equal(t, strconv.FormatInt(time.Unix(0, proc.Base.UnixTime).UTC().UnixMicro(), 10), values["statement_timestamp_utc"]) + require.Equal(t, "IANA", values["session_timezone_kind"]) + require.Equal(t, "UTC", values["session_timezone_name"]) + require.NotEmpty(t, values["session_timezone_tzdb_version"]) + require.Equal(t, tc.wantMode, values["sql_mode"]) + require.NotContains(t, values, "current_database") + require.NotContains(t, values, "current_role") + }) + } +} + func testExternalRoutineCall(t *testing.T, mode, nullPolicy string) *planpb.RoutineCall { t.Helper() descriptor, err := function.NewPythonTypeDescriptor(types.T_int64.ToType()) @@ -481,6 +619,48 @@ func TestExternalRoutineEvalRejectsSuccessfulRuntimeWithoutResultRows(t *testing require.ErrorContains(t, err, "runtime produced 0 result rows, expected 1") } +func TestExternalRoutineEvalPropagatesParameterAndRuntimeFailures(t *testing.T) { + for _, tc := range []struct { + name string + parameterFailure bool + runtimeFailure bool + want string + }{ + {name: "parameter evaluation fails", parameterFailure: true, want: "parameter expression failed"}, + {name: "runtime is disabled", want: "runtime is not enabled"}, + {name: "runtime execution fails", runtimeFailure: true, want: "handler process was cancelled"}, + } { + t.Run(tc.name, func(t *testing.T) { + proc := testutil.NewProcess(t) + defer proc.Free() + proc.SetQueryId("error-path-query") + proc.GetSessionInfo().TimeZone = time.UTC + proc.GetSessionInfo().User = "root" + proc.Base.StmtProfile = &process.StmtProfile{} + proc.GetStmtProfile().SetQueryStart(time.Date(2026, 9, 23, 0, 0, 0, 0, time.UTC)) + input := vector.NewVec(types.T_int64.ToType()) + defer input.Free(proc.Mp()) + require.NoError(t, vector.AppendFixed(input, int64(10), false, proc.Mp())) + parameter := &externalRoutineTestExecutor{vector: input} + if tc.parameterFailure { + parameter.err = errors.New(tc.want) + } else if tc.runtimeFailure { + proc.Base.UdfService = &externalRoutineErrorRuntime{err: errors.New(tc.want)} + } + evaluator, err := newExternalRoutineEval( + proc, testExternalRoutineCall(t, python.ModeScalar, udf.NullCallHandler), + []ExpressionExecutor{parameter}, nil, + ) + require.NoError(t, err) + defer evaluator.Free() + bat := batch.NewWithSize(0) + bat.SetRowCount(1) + _, err = evaluator.Eval(proc, []*batch.Batch{bat}, []bool{true}) + require.ErrorContains(t, err, tc.want) + }) + } +} + func TestExternalRoutineTransfersResultOwnership(t *testing.T) { for _, mode := range []string{"SCALAR", "VECTOR"} { t.Run(mode, func(t *testing.T) { @@ -536,6 +716,88 @@ func TestExternalRoutineRejectsDamagedDefinitionBeforeArguments(t *testing.T) { } } +func TestValidateRoutineCallRejectsIncompleteTypedContracts(t *testing.T) { + for _, tc := range []struct { + name string + mutate func(*planpb.RoutineCall) + want string + }{ + {name: "missing FunctionRef", mutate: func(call *planpb.RoutineCall) { call.FunctionRef = nil }, want: "no exact FunctionRef"}, + {name: "zero function identity", mutate: func(call *planpb.RoutineCall) { call.FunctionRef.FunctionId = 0 }, want: "no exact FunctionRef"}, + {name: "missing return descriptor", mutate: func(call *planpb.RoutineCall) { call.ReturnType.Id = int32(types.T_any) }, want: "no return descriptor"}, + {name: "unsupported contract version", mutate: func(call *planpb.RoutineCall) { call.ContractVersion++ }, want: "unsupported typed routine call contract"}, + {name: "non-volatile function", mutate: func(call *planpb.RoutineCall) { call.Volatility = "IMMUTABLE" }, want: "must be VOLATILE"}, + {name: "function cannot error", mutate: func(call *planpb.RoutineCall) { call.MayError = false }, want: "semantic contract is not supported"}, + {name: "unsupported security contract", mutate: func(call *planpb.RoutineCall) { call.SecurityMode = "DEFINER" }, want: "semantic contract is not supported"}, + {name: "leakproof function", mutate: func(call *planpb.RoutineCall) { call.Leakproof = true }, want: "semantic contract is not supported"}, + {name: "invalid callsite newline", mutate: func(call *planpb.RoutineCall) { call.CallsiteId = "python/test\nother" }, want: "valid callsite id"}, + {name: "execution context in plan", mutate: func(call *planpb.RoutineCall) { call.Context = map[string]string{"statement_id": "stale"} }, want: "contains execution context"}, + {name: "missing Python implementation", mutate: func(call *planpb.RoutineCall) { call.Implementation = nil }, want: "no Python implementation"}, + {name: "source in plan", mutate: func(call *planpb.RoutineCall) { call.GetPython().Source = "def add(ctx, value): return value" }, want: "plan contains source"}, + {name: "unsupported SDK contract", mutate: func(call *planpb.RoutineCall) { call.GetPython().SdkVersion = "future" }, want: "implementation contract is not supported"}, + {name: "unsupported mode", mutate: func(call *planpb.RoutineCall) { call.GetPython().Mode = "BATCH" }, want: "unsupported typed call mode"}, + {name: "unsupported NULL policy", mutate: func(call *planpb.RoutineCall) { call.GetPython().NullPolicy = "DEFAULT" }, want: "unsupported typed NULL policy"}, + {name: "NULL policy mismatch", mutate: func(call *planpb.RoutineCall) { call.NullPolicy = udf.NullReturnNull }, want: "NULL policy does not match"}, + {name: "malformed artifact digest", mutate: func(call *planpb.RoutineCall) { call.GetPython().ArtifactDigest = "not-a-digest" }, want: "invalid artifact/environment digest"}, + {name: "malformed definition fingerprint", mutate: func(call *planpb.RoutineCall) { call.GetPython().DefinitionFingerprint = "not-a-digest" }, want: "invalid definition fingerprint"}, + {name: "nil argument descriptor", mutate: func(call *planpb.RoutineCall) { call.ArgumentTypes[0] = nil }, want: "argument 0 is nil"}, + {name: "unsupported argument type", mutate: func(call *planpb.RoutineCall) { call.ArgumentTypes[0].Id = int32(types.T_any) }, want: "argument 0 is unsupported"}, + } { + t.Run(tc.name, func(t *testing.T) { + call := testExternalRoutineCall(t, python.ModeScalar, udf.NullCallHandler) + tc.mutate(call) + require.ErrorContains(t, validateRoutineCall(call), tc.want) + }) + } +} + +func TestValidateExternalRoutineInputsChecksRowAndTypeContracts(t *testing.T) { + proc := testutil.NewProcess(t) + defer proc.Free() + int64Rows := vector.NewVec(types.T_int64.ToType()) + defer int64Rows.Free(proc.Mp()) + require.NoError(t, vector.AppendFixed(int64Rows, int64(1), false, proc.Mp())) + require.NoError(t, vector.AppendFixed(int64Rows, int64(2), false, proc.Mp())) + shortRows := vector.NewVec(types.T_int64.ToType()) + defer shortRows.Free(proc.Mp()) + require.NoError(t, vector.AppendFixed(shortRows, int64(1), false, proc.Mp())) + int32Rows := vector.NewVec(types.T_int32.ToType()) + defer int32Rows.Free(proc.Mp()) + require.NoError(t, vector.AppendFixed(int32Rows, int32(1), false, proc.Mp())) + require.NoError(t, vector.AppendFixed(int32Rows, int32(2), false, proc.Mp())) + emptyRows := vector.NewVec(types.T_int64.ToType()) + defer emptyRows.Free(proc.Mp()) + + for _, tc := range []struct { + name string + inputs []*vector.Vector + rows int + mutate func(*planpb.RoutineCall) + wantErr string + }{ + {name: "valid aligned input", inputs: []*vector.Vector{int64Rows}, rows: 2}, + {name: "argument count mismatch", inputs: nil, rows: 2, wantErr: "evaluated input count 0"}, + {name: "missing input vector", inputs: []*vector.Vector{nil}, rows: 1, wantErr: "has no type"}, + {name: "empty input vector", inputs: []*vector.Vector{emptyRows}, rows: 1, wantErr: "has 0 rows"}, + {name: "short non-constant input", inputs: []*vector.Vector{shortRows}, rows: 2, wantErr: "expected at least 2"}, + {name: "physical SQL type mismatch", inputs: []*vector.Vector{int32Rows}, rows: 2, wantErr: "does not match the frozen descriptor"}, + {name: "unsupported frozen descriptor", inputs: []*vector.Vector{int64Rows}, rows: 2, mutate: func(call *planpb.RoutineCall) { call.ArgumentTypes[0].Id = int32(types.T_enum) }, wantErr: "invalid argument descriptor 0"}, + } { + t.Run(tc.name, func(t *testing.T) { + call := testExternalRoutineCall(t, python.ModeScalar, udf.NullCallHandler) + if tc.mutate != nil { + tc.mutate(call) + } + err := validateExternalRoutineInputs(tc.inputs, call, tc.rows) + if tc.wantErr == "" { + require.NoError(t, err) + return + } + require.ErrorContains(t, err, tc.wantErr) + }) + } +} + func TestExternalRoutineSkipsArgumentsForEmptySelection(t *testing.T) { for _, mode := range []string{"SCALAR", "VECTOR"} { t.Run(mode, func(t *testing.T) { diff --git a/pkg/udf/python/arrow_test.go b/pkg/udf/python/arrow_test.go index f0f72f768ab7e..515206c9564fe 100644 --- a/pkg/udf/python/arrow_test.go +++ b/pkg/udf/python/arrow_test.go @@ -906,7 +906,7 @@ func TestBuildInputRecordCoversEverySupportedType(t *testing.T) { return vector.AppendFixed(v, types.Datetime(0), true, mp) }}, {"timestamp", types.New(types.T_timestamp, 0, 6), func(v *vector.Vector) error { - if err := vector.AppendFixed(v, types.Timestamp(types.GetUnixEpochSecs()+1), false, mp); err != nil { + if err := vector.AppendFixed(v, types.Timestamp(types.TimestampMinValue), false, mp); err != nil { return err } return vector.AppendFixed(v, types.Timestamp(0), true, mp) @@ -988,6 +988,24 @@ func TestBuildInputRecordCoversEverySupportedType(t *testing.T) { require.Equal(t, int64(2), record.NumRows()) require.False(t, record.Column(0).IsNull(0)) require.True(t, record.Column(0).IsNull(1)) + + // Exercise the reverse boundary with the same SQL values. Re-encoding + // the returned MO vector must reproduce the Arrow values and validity + // bitmap for every supported SQL descriptor. + returned := vector.NewFunctionResultWrapper(tc.typ, mp) + defer returned.Free() + require.NoError(t, returned.PreExtendAndReset(2)) + require.NoError(t, AppendArrowResult(descriptor, record.Column(0), returned, mp)) + require.Equal(t, 2, returned.GetResultVector().Length()) + require.False(t, returned.GetResultVector().IsNull(0)) + require.True(t, returned.GetResultVector().IsNull(1)) + + returnedRecord, _, err := BuildInputRecord( + []*vector.Vector{returned.GetResultVector()}, []types.Type{tc.typ}, 2, + ) + require.NoError(t, err) + defer returnedRecord.Release() + require.True(t, array.Equal(record.Column(0), returnedRecord.Column(0)), tc.name) }) } } diff --git a/pkg/udf/python/config_test.go b/pkg/udf/python/config_test.go index 4873cc8fb5198..dad3c7473e9b4 100644 --- a/pkg/udf/python/config_test.go +++ b/pkg/udf/python/config_test.go @@ -68,3 +68,43 @@ func TestClientConfigMatchesWorkerHandlerTimeoutContract(t *testing.T) { config.RequestTimeout = maxHandlerTimeout + time.Nanosecond require.ErrorContains(t, config.Validate(), "request timeout") } + +func TestClientConfigRejectsEveryOutOfRangeBudget(t *testing.T) { + for _, tc := range []struct { + name string + mutate func(*ClientConfig) + want string + }{ + {name: "missing worker address", mutate: func(c *ClientConfig) { c.ServerAddress = "" }, want: "missing python udf address"}, + {name: "negative input bytes", mutate: func(c *ClientConfig) { c.MaxBatchBytes = -1 }, want: "max batch bytes"}, + {name: "input bytes exceed wire bound", mutate: func(c *ClientConfig) { c.MaxBatchBytes = 1<<30 + 1 }, want: "max batch bytes"}, + {name: "negative input rows", mutate: func(c *ClientConfig) { c.MaxBatchRows = -1 }, want: "max batch rows"}, + {name: "input rows exceed bound", mutate: func(c *ClientConfig) { c.MaxBatchRows = 1<<30 + 1 }, want: "max batch rows"}, + {name: "invocation rows exceed bound", mutate: func(c *ClientConfig) { c.MaxInvocationRows = 1<<32 + 1 }, want: "max invocation rows"}, + {name: "result bytes exceed bound", mutate: func(c *ClientConfig) { c.MaxInvocationResultBytes = 1<<40 + 1 }, want: "max invocation result bytes"}, + {name: "active slots exceed bound", mutate: func(c *ClientConfig) { c.MaxActiveInvocations = 1<<20 + 1 }, want: "max active invocations"}, + {name: "negative request timeout", mutate: func(c *ClientConfig) { c.RequestTimeout = -time.Nanosecond }, want: "request timeout"}, + {name: "terminal entries exceed bound", mutate: func(c *ClientConfig) { c.MaxTerminalEntries = 1<<30 + 1 }, want: "max terminal entries"}, + {name: "negative terminal bytes", mutate: func(c *ClientConfig) { c.MaxTerminalBytes = -1 }, want: "max terminal bytes"}, + {name: "terminal bytes exceed bound", mutate: func(c *ClientConfig) { c.MaxTerminalBytes = 1<<40 + 1 }, want: "max terminal bytes"}, + {name: "negative tombstone lifetime", mutate: func(c *ClientConfig) { c.TerminalRecordTTL = -time.Nanosecond }, want: "terminal record ttl"}, + } { + t.Run(tc.name, func(t *testing.T) { + config := &ClientConfig{ + Enabled: true, AllowUnisolated: true, ServerAddress: "127.0.0.1:50051", + } + tc.mutate(config) + require.ErrorContains(t, config.Validate(), tc.want) + }) + } +} + +func TestWorkerConfigRequiresAddressAndPath(t *testing.T) { + config := &Config{Path: "/opt/matrixone/pkg/udf/python/worker/worker.py"} + require.ErrorContains(t, config.Validate(), "missing python udf address") + config.Address = "127.0.0.1:50051" + config.Path = "" + require.ErrorContains(t, config.Validate(), "missing python udf path") + config.Path = "/opt/matrixone/pkg/udf/python/worker/worker.py" + require.NoError(t, config.Validate()) +} diff --git a/pkg/udf/python/gateway_test.go b/pkg/udf/python/gateway_test.go index a98d16686c512..cd2961e876bce 100644 --- a/pkg/udf/python/gateway_test.go +++ b/pkg/udf/python/gateway_test.go @@ -16,6 +16,7 @@ package python import ( "context" + "encoding/json" "errors" "io" "strings" @@ -23,7 +24,10 @@ import ( "testing" "time" + "github.com/apache/arrow-go/v18/arrow" + "github.com/apache/arrow-go/v18/arrow/array" "github.com/apache/arrow-go/v18/arrow/flight/gen/flight" + "github.com/apache/arrow-go/v18/arrow/memory" "github.com/matrixorigin/matrixone/pkg/common/mpool" "github.com/matrixorigin/matrixone/pkg/container/types" "github.com/matrixorigin/matrixone/pkg/container/vector" @@ -39,6 +43,29 @@ type gatewayResultStream struct { index int } +type scriptedGatewayResultStream struct { + grpc.ClientStream + results []*flight.FlightData + errors []error + index int +} + +func (s *scriptedGatewayResultStream) Recv() (*flight.FlightData, error) { + index := s.index + s.index++ + if index < len(s.results) { + return s.results[index], nil + } + if index < len(s.errors) { + return nil, s.errors[index] + } + return nil, io.EOF +} + +func (s *scriptedGatewayResultStream) Send(*flight.FlightData) error { + return nil +} + type gatedGatewayResultStream struct { grpc.ClientStream results []*flight.FlightData @@ -72,6 +99,74 @@ type finishAckFlightClient struct { actions []*flight.Action } +type resultAckFlightClient struct { + flight.FlightServiceClient + response []byte + err error +} + +func (c *resultAckFlightClient) DoAction(context.Context, *flight.Action, ...grpc.CallOption) (flight.FlightService_DoActionClient, error) { + return &finishAckActionStream{result: &flight.Result{Body: c.response}, err: c.err}, nil +} + +type capabilityActionClient struct { + flight.FlightServiceClient + response []byte + err error +} + +func (c *capabilityActionClient) DoAction( + _ context.Context, + action *flight.Action, + _ ...grpc.CallOption, +) (flight.FlightService_DoActionClient, error) { + if c.err != nil { + return nil, c.err + } + if action.Type != "GetPythonCapabilities" { + return nil, errors.New("unexpected capability action") + } + return &finishAckActionStream{result: &flight.Result{Body: c.response}}, nil +} + +type scriptedCapabilityActionStream struct { + grpc.ClientStream + results []*flight.Result + errors []error + index int +} + +func (s *scriptedCapabilityActionStream) Recv() (*flight.Result, error) { + index := s.index + s.index++ + if index < len(s.results) { + return s.results[index], nil + } + if index < len(s.errors) { + return nil, s.errors[index] + } + return nil, io.EOF +} + +type scriptedCapabilityFlightClient struct { + flight.FlightServiceClient + actionErr error + stream *scriptedCapabilityActionStream + actions []*flight.Action +} + +func (c *scriptedCapabilityFlightClient) DoAction( + _ context.Context, + action *flight.Action, + _ ...grpc.CallOption, +) (flight.FlightService_DoActionClient, error) { + c.actions = append(c.actions, action) + if c.actionErr != nil { + return nil, c.actionErr + } + return c.stream, nil +} + type recordingArtifactResolver struct { source string calls int @@ -305,6 +400,367 @@ func validInvocation() *udf.Invocation { } } +func TestValidateInvocationHeaderRejectsUnfrozenContracts(t *testing.T) { + require.NoError(t, validateInvocationHeader(validInvocation())) + + cases := []struct { + name string + mutate func(*udf.Invocation) + }{ + {"nil invocation", nil}, + {"language", func(i *udf.Invocation) { i.Language = "PYTHON" }}, + {"empty handler", func(i *udf.Invocation) { i.Handler = "" }}, + {"external handler", func(i *udf.Invocation) { i.Handler = "package:handler" }}, + {"negative length", func(i *udf.Invocation) { i.Length = -1 }}, + {"argument input count", func(i *udf.Invocation) { i.Inputs = nil }}, + {"unsupported mode", func(i *udf.Invocation) { i.Mode = "BATCH" }}, + {"empty callsite", func(i *udf.Invocation) { i.CallsiteID = "" }}, + {"oversized callsite", func(i *udf.Invocation) { i.CallsiteID = strings.Repeat("x", 257) }}, + {"control character callsite", func(i *udf.Invocation) { i.CallsiteID = "bad\nsite" }}, + {"must be allowed to error", func(i *udf.Invocation) { i.MayError = false }}, + {"unsupported security mode", func(i *udf.Invocation) { i.SecurityMode = "DEFINER" }}, + {"leakproof", func(i *udf.Invocation) { i.Leakproof = true }}, + {"missing statement context", func(i *udf.Invocation) { i.StatementContext = nil }}, + {"invalid statement context", func(i *udf.Invocation) { i.StatementContext.TimezoneKind = "UNKNOWN" }}, + {"missing security frame", func(i *udf.Invocation) { i.SecurityFrame = nil }}, + {"changed security principal", func(i *udf.Invocation) { i.SecurityFrame.EffectiveUserID = 8 }}, + {"security mode mismatch", func(i *udf.Invocation) { i.SecurityFrame.Mode = "DEFINER" }}, + {"unsupported null policy", func(i *udf.Invocation) { i.NullPolicy = "STRICT" }}, + {"unsupported ABI", func(i *udf.Invocation) { i.ABIContract = "PYTHON_NATIVE" }}, + {"unsupported adapter", func(i *udf.Invocation) { i.AdapterVersion = "future" }}, + {"unsupported SDK", func(i *udf.Invocation) { i.SDKVersion = "future" }}, + {"unsupported schema", func(i *udf.Invocation) { i.DefinitionSchemaVersion++ }}, + {"malformed artifact digest", func(i *udf.Invocation) { i.ArtifactDigest = "bad" }}, + {"environment digest mismatch", func(i *udf.Invocation) { i.EnvironmentDigest = strings.Repeat("d", 64) }}, + {"malformed definition fingerprint", func(i *udf.Invocation) { i.DefinitionFingerprint = "bad" }}, + {"unsupported argument type", func(i *udf.Invocation) { i.Args[0] = types.T_any.ToType() }}, + {"unsupported return type", func(i *udf.Invocation) { i.ReturnType = types.T_any.ToType() }}, + {"invalid function identity", func(i *udf.Invocation) { i.FunctionRef.FunctionID = 0 }}, + {"fencing account mismatch", func(i *udf.Invocation) { i.Tuple.AccountID++ }}, + {"invalid fencing tuple", func(i *udf.Invocation) { i.Tuple.StatementID = "" }}, + } + for _, tc := range cases { + t.Run(tc.name, func(t *testing.T) { + invocation := validInvocation() + if tc.mutate == nil { + require.Error(t, validateInvocationHeader(nil)) + return + } + tc.mutate(invocation) + require.Error(t, validateInvocationHeader(invocation)) + }) + } +} + +func TestGatewayStatusSnapshotReportsAdmissionAndWorkerContract(t *testing.T) { + response, err := json.Marshal(currentTestCapabilities()) + require.NoError(t, err) + for _, tc := range []struct { + name string + language string + config ClientConfig + client *capabilityActionClient + closeGateway bool + directConfig bool + wantReady bool + wantClass string + wantReason string + wantProtocol int32 + wantLease uint64 + }{ + { + name: "unsupported language", + language: "sql", + wantClass: udf.RuntimeStatusInvalid, + wantReason: udf.RuntimeStatusReasonInvalidLanguage, + }, + { + name: "disabled", + language: udf.LanguagePython, + wantClass: udf.RuntimeStatusDisabled, + wantReason: udf.RuntimeStatusReasonRuntimeDisabled, + }, + { + name: "unisolated execution not admitted", + language: udf.LanguagePython, + config: ClientConfig{ + Enabled: true, + }, + directConfig: true, + wantClass: udf.RuntimeStatusNotAllowed, + wantReason: udf.RuntimeStatusReasonUnisolatedNotAllowed, + }, + { + name: "worker transport failure", + language: udf.LanguagePython, + config: ClientConfig{ + Enabled: true, AllowUnisolated: true, ServerAddress: "127.0.0.1:1", RequestTimeout: time.Second, + }, + client: &capabilityActionClient{err: errors.New("connection refused")}, + wantClass: udf.RuntimeStatusUnavailable, + wantReason: udf.RuntimeStatusReasonWorkerUnavailable, + }, + { + name: "capability contract mismatch", + language: udf.LanguagePython, + config: ClientConfig{ + Enabled: true, AllowUnisolated: true, ServerAddress: "127.0.0.1:1", RequestTimeout: time.Second, + }, + client: func() *capabilityActionClient { + mismatch := currentTestCapabilities() + mismatch.ProtocolVersion++ + body, marshalErr := json.Marshal(mismatch) + require.NoError(t, marshalErr) + return &capabilityActionClient{response: body} + }(), + wantClass: udf.RuntimeStatusContractMismatch, + wantReason: udf.RuntimeStatusReasonCapabilityMismatch, + }, + { + name: "ready worker", + language: udf.LanguagePython, + config: ClientConfig{ + Enabled: true, AllowUnisolated: true, ServerAddress: "127.0.0.1:1", RequestTimeout: time.Second, + }, + client: &capabilityActionClient{response: response}, + wantReady: true, + wantClass: "", + wantReason: udf.RuntimeStatusReasonReady, + wantProtocol: int32(protocol.Version), + wantLease: 42, + }, + { + name: "closed gateway", + language: udf.LanguagePython, + config: ClientConfig{Enabled: true, AllowUnisolated: true, ServerAddress: "127.0.0.1:1"}, + closeGateway: true, + wantClass: udf.RuntimeStatusClosed, + wantReason: udf.RuntimeStatusReasonRuntimeClosed, + }, + } { + t.Run(tc.name, func(t *testing.T) { + cfg := tc.config + if cfg.ServerAddress == "" && cfg.Enabled { + cfg.ServerAddress = "127.0.0.1:1" + } + if cfg.Enabled && cfg.AllowUnisolated && cfg.RequestTimeout == 0 { + cfg.RequestTimeout = time.Second + } + var gateway *Gateway + var err error + if tc.directConfig { + // NewGateway correctly rejects this production configuration. A + // direct value exercises the status fail-closed branch itself. + gateway = &Gateway{cfg: cfg} + } else { + gateway, err = NewGateway(cfg) + require.NoError(t, err) + } + if tc.client != nil { + gateway.flight = tc.client + } + if tc.closeGateway { + require.NoError(t, gateway.Close()) + } else { + defer gateway.Close() + } + + snapshot := gateway.StatusSnapshot(nil, tc.language) + require.Equal(t, tc.language, snapshot.Language) + require.Equal(t, tc.wantReady, snapshot.Ready) + require.Equal(t, tc.wantClass, snapshot.ErrorClass) + require.Equal(t, tc.wantReason, snapshot.Reason) + if tc.wantReady { + require.Equal(t, tc.wantProtocol, snapshot.ProtocolVersion) + require.Equal(t, tc.wantLease, snapshot.LeaseEpoch) + require.Equal(t, []string{ModeScalar, ModeVector}, snapshot.Modes) + require.Equal(t, []string{NullCallHandler, NullReturnNull}, snapshot.NullPolicies) + require.Equal(t, udf.PythonABIContract, snapshot.ABIContract) + } + }) + } +} + +func TestReadCapabilitiesRequiresOneStrictCurrentResponse(t *testing.T) { + validBody, err := json.Marshal(currentTestCapabilities()) + require.NoError(t, err) + unknownFieldBody := append(append([]byte(nil), validBody[:len(validBody)-1]...), []byte(`,"future_contract":true}`)...) + + for _, tc := range []struct { + name string + actionErr error + stream *scriptedCapabilityActionStream + wantError string + wantResult bool + }{ + { + name: "one current response followed by EOF", + stream: &scriptedCapabilityActionStream{ + results: []*flight.Result{{Body: validBody}}, + }, + wantResult: true, + }, + { + name: "action RPC failure", + actionErr: errors.New("injected action failure"), + wantError: "capability handshake", + }, + { + name: "first response transport failure", + stream: &scriptedCapabilityActionStream{ + errors: []error{errors.New("injected response failure")}, + }, + wantError: "capability handshake response", + }, + { + name: "empty response", + stream: &scriptedCapabilityActionStream{results: []*flight.Result{nil}}, + wantError: "empty capability handshake response", + }, + { + name: "malformed JSON", + stream: &scriptedCapabilityActionStream{ + results: []*flight.Result{{Body: []byte("{")}}, + }, + wantError: "decode capability handshake", + }, + { + name: "unknown contract field", + stream: &scriptedCapabilityActionStream{ + results: []*flight.Result{{Body: unknownFieldBody}}, + }, + wantError: "decode capability handshake", + }, + { + name: "trailing JSON value", + stream: &scriptedCapabilityActionStream{ + results: []*flight.Result{{Body: append(append([]byte(nil), validBody...), []byte(" {}")...)}}, + }, + wantError: "trailing JSON", + }, + { + name: "unsupported current contract", + stream: func() *scriptedCapabilityActionStream { + mismatch := currentTestCapabilities() + mismatch.ProtocolVersion++ + body, marshalErr := json.Marshal(mismatch) + require.NoError(t, marshalErr) + return &scriptedCapabilityActionStream{results: []*flight.Result{{Body: body}}} + }(), + wantError: "contract does not match", + }, + { + name: "multiple responses", + stream: &scriptedCapabilityActionStream{ + results: []*flight.Result{{Body: validBody}, {Body: validBody}}, + }, + wantError: "multiple results", + }, + { + name: "stream failure after response", + stream: &scriptedCapabilityActionStream{ + results: []*flight.Result{{Body: validBody}}, + errors: []error{nil, errors.New("injected trailing stream failure")}, + }, + wantError: "capability handshake stream", + }, + } { + t.Run(tc.name, func(t *testing.T) { + client := &scriptedCapabilityFlightClient{actionErr: tc.actionErr, stream: tc.stream} + got, err := readCapabilities(context.Background(), client) + if tc.wantError != "" { + require.ErrorContains(t, err, tc.wantError) + require.Nil(t, got) + } else { + require.NoError(t, err) + require.Equal(t, tc.wantResult, got != nil) + require.Equal(t, protocol.Version, got.ProtocolVersion) + } + require.Len(t, client.actions, 1) + require.Equal(t, "GetPythonCapabilities", client.actions[0].Type) + var request capabilityRequest + require.NoError(t, json.Unmarshal(client.actions[0].Body, &request)) + require.Equal(t, protocol.Version, request.ProtocolVersion) + }) + } +} + +func TestValidateDefinitionRejectsMalformedCurrentContractsBeforeRPC(t *testing.T) { + valid := func() *udf.RoutineDefinition { + invocation := validInvocation() + return &udf.RoutineDefinition{ + Language: invocation.Language, + AccountID: invocation.FunctionRef.AccountID, + Handler: invocation.Handler, + Source: invocation.Source, + Args: append([]types.Type(nil), invocation.Args...), + ReturnType: invocation.ReturnType, + Mode: invocation.Mode, + NullPolicy: invocation.NullPolicy, + ABIContract: invocation.ABIContract, + AdapterVersion: invocation.AdapterVersion, + SDKVersion: invocation.SDKVersion, + DefinitionSchemaVersion: invocation.DefinitionSchemaVersion, + ArtifactDigest: invocation.ArtifactDigest, + EnvironmentDigest: invocation.EnvironmentDigest, + DefinitionFingerprint: invocation.DefinitionFingerprint, + } + } + for _, tc := range []struct { + name string + mutate func(*udf.RoutineDefinition) + }{ + {"unsupported language", func(d *udf.RoutineDefinition) { d.Language = "sql" }}, + {"empty handler", func(d *udf.RoutineDefinition) { d.Handler = " " }}, + {"external handler", func(d *udf.RoutineDefinition) { d.Handler = "module:handler" }}, + {"unsupported schema", func(d *udf.RoutineDefinition) { d.DefinitionSchemaVersion++ }}, + {"unsupported ABI", func(d *udf.RoutineDefinition) { d.ABIContract = "future" }}, + {"unsupported adapter", func(d *udf.RoutineDefinition) { d.AdapterVersion = "future" }}, + {"unsupported SDK", func(d *udf.RoutineDefinition) { d.SDKVersion = "future" }}, + {"unsupported mode", func(d *udf.RoutineDefinition) { d.Mode = "BATCH" }}, + {"unsupported NULL policy", func(d *udf.RoutineDefinition) { d.NullPolicy = "STRICT" }}, + {"invalid artifact digest", func(d *udf.RoutineDefinition) { d.ArtifactDigest = "invalid" }}, + {"invalid environment digest", func(d *udf.RoutineDefinition) { d.EnvironmentDigest = "invalid" }}, + {"environment digest mismatch", func(d *udf.RoutineDefinition) { d.EnvironmentDigest = strings.Repeat("a", 64) }}, + {"invalid fingerprint", func(d *udf.RoutineDefinition) { d.DefinitionFingerprint = "invalid" }}, + {"unsupported argument descriptor", func(d *udf.RoutineDefinition) { d.Args[0] = types.T_any.ToType() }}, + {"unsupported return descriptor", func(d *udf.RoutineDefinition) { d.ReturnType = types.T_any.ToType() }}, + {"fingerprint does not bind definition", func(d *udf.RoutineDefinition) { d.Mode = ModeVector }}, + } { + t.Run(tc.name, func(t *testing.T) { + gateway, err := NewGateway(ClientConfig{ + Enabled: true, AllowUnisolated: true, ServerAddress: "127.0.0.1:1", RequestTimeout: time.Second, + }) + require.NoError(t, err) + defer gateway.Close() + definition := valid() + tc.mutate(definition) + err = gateway.ValidateDefinition(context.Background(), definition) + require.Error(t, err) + require.Contains(t, err.Error(), "UNSUPPORTED_ROUTINE_VERSION") + gateway.mu.Lock() + defer gateway.mu.Unlock() + require.Nil(t, gateway.conn, "invalid definitions must be rejected before opening a Flight connection") + }) + } + t.Run("nil definition", func(t *testing.T) { + gateway, err := NewGateway(ClientConfig{Enabled: true, AllowUnisolated: true, ServerAddress: "127.0.0.1:1"}) + require.NoError(t, err) + defer gateway.Close() + require.ErrorContains(t, gateway.ValidateDefinition(context.Background(), nil), "nil Python routine definition") + }) + t.Run("disabled runtime", func(t *testing.T) { + gateway, err := NewGateway(ClientConfig{}) + require.NoError(t, err) + defer gateway.Close() + require.ErrorContains(t, gateway.ValidateDefinition(context.Background(), valid()), "runtime is disabled") + }) + t.Run("unisolated opt in is required", func(t *testing.T) { + gateway := &Gateway{cfg: ClientConfig{Enabled: true, ServerAddress: "127.0.0.1:1"}} + require.ErrorContains(t, gateway.ValidateDefinition(context.Background(), valid()), "explicit unisolated opt-in") + }) +} + func TestValidateInvocationInputsChecksZeroRowVectorTypes(t *testing.T) { mp := mpool.MustNewZeroNoFixed() input := vector.NewVec(types.T_int32.ToType()) @@ -653,6 +1109,278 @@ func TestGatewayRejectsArrowBodyOnControlFrame(t *testing.T) { require.ErrorContains(t, err, "unexpected Arrow data") } +func TestReceiveResultBatchRejectsMalformedProtocolProgress(t *testing.T) { + tuple := validInvocation().Tuple + resultBatch := func(controlTuple protocol.FencingTuple) *flight.FlightData { + return &flight.FlightData{ + DataHeader: []byte("record"), + AppMetadata: func() []byte { + encoded, err := protocol.MarshalControl(protocol.Control{ + Kind: "ResultBatch", Tuple: controlTuple, Sequence: 1, + }) + require.NoError(t, err) + return encoded + }(), + } + } + inputConsumed := func(sequence uint64, releasedBytes int64, releasedBatches uint64) *flight.FlightData { + return gatewayControl(t, "InputConsumed", tuple, func(control *protocol.Control) { + control.Sequence = sequence + control.ReleasedBytes = releasedBytes + control.ReleasedBatches = releasedBatches + }) + } + for _, tc := range []struct { + name string + results []*flight.FlightData + recvErrors []error + initialSchema bool + schemaReady bool + want string + }{ + {name: "EOF before result", want: "ended before result sequence"}, + {name: "receive failure", recvErrors: []error{errors.New("injected receive failure")}, want: "receive result"}, + {name: "nil FlightData", results: []*flight.FlightData{nil}, want: "empty Flight result"}, + {name: "empty FlightData", results: []*flight.FlightData{{}}, want: "empty Flight result"}, + { + name: "control frame with Arrow body", + results: []*flight.FlightData{func() *flight.FlightData { + data := inputConsumed(1, 1, 1) + data.DataBody = []byte("unexpected") + return data + }()}, + want: "control frame contains an Arrow body", + }, + {name: "invalid control metadata", results: []*flight.FlightData{{AppMetadata: []byte("not-json")}}, want: "invalid character"}, + { + name: "changed tuple", + results: []*flight.FlightData{gatewayControl(t, "InputConsumed", protocol.FencingTuple{ + AccountID: tuple.AccountID + 1, StatementID: tuple.StatementID, GroupID: tuple.GroupID, + GroupEpoch: tuple.GroupEpoch, InvocationID: tuple.InvocationID, LeaseEpoch: tuple.LeaseEpoch, + }, func(control *protocol.Control) { + control.Sequence = 1 + control.ReleasedBytes = 1 + control.ReleasedBatches = 1 + })}, + want: "result tuple changed", + }, + {name: "result schema control before schema", results: []*flight.FlightData{gatewayControl(t, "ResultSchema", tuple)}, want: "invalid result schema metadata"}, + { + name: "input sequence mismatch", + results: []*flight.FlightData{inputConsumed(2, 1, 1)}, + want: "was consumed while waiting for result", + }, + { + name: "invalid released credits", + results: []*flight.FlightData{inputConsumed(1, 2048, 1)}, + want: "released invalid bytes/batches", + }, + {name: "worker error control", results: []*flight.FlightData{gatewayControl(t, "Error", tuple, func(control *protocol.Control) { + control.Status = "ERROR" + control.Reason = "handler failed" + })}, want: "worker error: handler failed"}, + {name: "Finish before result", results: []*flight.FlightData{gatewayControl(t, "Finish", tuple, func(control *protocol.Control) { + control.FinishID = "finish-1" + control.Status = statusOK + control.LastSequence = 1 + control.LastResultSequence = 0 + })}, want: "Finish arrived before result"}, + {name: "unknown control", results: []*flight.FlightData{{AppMetadata: []byte(`{"version":1,"kind":"Unknown","tuple":{"statement_id":"stmt","group_id":"group","group_epoch":1,"invocation_id":"invocation","lease_epoch":1}}`)}}, want: "unsupported control kind"}, + { + name: "schema frame contains a body", + results: []*flight.FlightData{{DataHeader: []byte("schema"), DataBody: []byte("body")}}, + want: "result schema contains a body", + }, + { + name: "schema metadata has wrong tuple", + results: []*flight.FlightData{func() *flight.FlightData { + data := gatewayControl(t, "ResultSchema", tuple) + data.DataHeader = []byte("schema") + otherTuple := tuple + otherTuple.AccountID++ + data.AppMetadata, _ = protocol.MarshalControl(protocol.Control{Kind: "ResultSchema", Tuple: otherTuple}) + return data + }()}, + want: "invalid result schema metadata", + }, + { + name: "record omitted schema metadata", + results: []*flight.FlightData{ + {DataHeader: []byte("schema")}, + resultBatch(tuple), + }, + want: "result schema metadata was not received", + }, + { + name: "result before input consumed", + initialSchema: true, + schemaReady: true, + results: []*flight.FlightData{resultBatch(tuple)}, + want: "arrived before InputConsumed", + }, + { + name: "duplicate input consumed", + results: []*flight.FlightData{ + inputConsumed(1, 1, 1), + inputConsumed(1, 1, 1), + }, + want: "was consumed more than once", + }, + } { + t.Run(tc.name, func(t *testing.T) { + mp := mpool.MustNewZeroNoFixed() + defer mpool.DeleteMPool(mp) + result := vector.NewFunctionResultWrapper(types.T_int64.ToType(), mp) + defer result.Free() + returnDescriptor, err := NewTypeDescriptor(types.T_int64.ToType()) + require.NoError(t, err) + sequence := protocol.Sequence{} + require.NoError(t, sequence.AcceptInput(1)) + var schemaFrame *ArrowFrame + if tc.initialSchema { + schemaFrame = &ArrowFrame{Header: []byte("schema")} + } + schemaReady := tc.schemaReady + gateway := &Gateway{cfg: ClientConfig{MaxBatchBytes: 1024}} + _, err = gateway.receiveResultBatch( + context.Background(), nil, + &scriptedGatewayResultStream{results: tc.results, errors: tc.recvErrors}, + tuple, 1, 1, &schemaFrame, &schemaReady, returnDescriptor, result, mp, &sequence, + ) + require.ErrorContains(t, err, tc.want) + }) + } +} + +func TestReceiveResultBatchValidatesArrowPayloadAndResultAck(t *testing.T) { + type encodedResult struct { + frames []ArrowFrame + descriptor TypeDescriptor + } + encodeResult := func(t *testing.T, fieldName string) encodedResult { + t.Helper() + descriptor, err := NewTypeDescriptor(types.T_int64.ToType()) + require.NoError(t, err) + field, err := descriptor.Field(fieldName) + require.NoError(t, err) + builder := array.NewInt64Builder(memory.NewGoAllocator()) + builder.AppendValues([]int64{17}, nil) + values := builder.NewInt64Array() + defer values.Release() + record := array.NewRecordBatch(arrow.NewSchema([]arrow.Field{field}, nil), []arrow.Array{values}, 1) + defer record.Release() + frames, err := EncodeRecordBatch(record, DefaultMaxBatchBytes) + require.NoError(t, err) + return encodedResult{frames: frames, descriptor: descriptor} + } + + tuple := validInvocation().Tuple + encoded := encodeResult(t, "result") + wrongName := encodeResult(t, "other") + validAck, err := protocol.MarshalControl(protocol.Control{ + Kind: "Ack", Tuple: tuple, AckSequence: 1, Status: statusOK, + }) + require.NoError(t, err) + wrongAck, err := protocol.MarshalControl(protocol.Control{ + Kind: "Ack", Tuple: tuple, AckSequence: 2, Status: statusOK, + }) + require.NoError(t, err) + resultMetadata, err := protocol.MarshalControl(protocol.Control{ + Kind: "ResultBatch", Tuple: tuple, Sequence: 1, + }) + require.NoError(t, err) + schemaMetadata, err := protocol.MarshalControl(protocol.Control{ + Kind: "ResultSchema", Tuple: tuple, + }) + require.NoError(t, err) + inputConsumedMetadata, err := protocol.MarshalControl(protocol.Control{ + Kind: "InputConsumed", Tuple: tuple, Sequence: 1, ReleasedBytes: 1, ReleasedBatches: 1, + }) + require.NoError(t, err) + + for _, tc := range []struct { + name string + frames []ArrowFrame + descriptor TypeDescriptor + expectedRows int64 + maxBatchBytes int64 + maxResultBytes int64 + resultMetadata []byte + ackResponse []byte + ackError error + resultAlreadySeen bool + wantError string + }{ + {name: "valid one-row result is appended and acknowledged", frames: encoded.frames, descriptor: encoded.descriptor, expectedRows: 1, maxBatchBytes: DefaultMaxBatchBytes, resultMetadata: resultMetadata, ackResponse: validAck}, + {name: "malformed result metadata", frames: encoded.frames, descriptor: encoded.descriptor, expectedRows: 1, maxBatchBytes: DefaultMaxBatchBytes, resultMetadata: []byte("not-json"), wantError: "invalid character"}, + {name: "result sequence does not match input", frames: encoded.frames, descriptor: encoded.descriptor, expectedRows: 1, maxBatchBytes: DefaultMaxBatchBytes, resultMetadata: func() []byte { + metadata, marshalErr := protocol.MarshalControl(protocol.Control{Kind: "ResultBatch", Tuple: tuple, Sequence: 2}) + require.NoError(t, marshalErr) + return metadata + }(), wantError: "unexpected result sequence"}, + {name: "duplicate result sequence", frames: encoded.frames, descriptor: encoded.descriptor, expectedRows: 1, maxBatchBytes: DefaultMaxBatchBytes, resultMetadata: resultMetadata, resultAlreadySeen: true, wantError: "sequence violation"}, + {name: "corrupt Arrow record body", frames: func() []ArrowFrame { + frames := append([]ArrowFrame(nil), encoded.frames...) + frames[1].Body = []byte("not an Arrow body") + return frames + }(), descriptor: encoded.descriptor, expectedRows: 1, maxBatchBytes: DefaultMaxBatchBytes, resultMetadata: resultMetadata, wantError: "decode result"}, + {name: "unexpected row count", frames: encoded.frames, descriptor: encoded.descriptor, expectedRows: 2, maxBatchBytes: DefaultMaxBatchBytes, resultMetadata: resultMetadata, wantError: "result batch row count"}, + {name: "unexpected field name", frames: wrongName.frames, descriptor: wrongName.descriptor, expectedRows: 1, maxBatchBytes: DefaultMaxBatchBytes, resultMetadata: resultMetadata, wantError: "field has unexpected name"}, + {name: "descriptor mismatch", frames: encoded.frames, descriptor: func() TypeDescriptor { + descriptor, descriptorErr := NewTypeDescriptor(types.T_int32.ToType()) + require.NoError(t, descriptorErr) + return descriptor + }(), expectedRows: 1, maxBatchBytes: DefaultMaxBatchBytes, resultMetadata: resultMetadata, wantError: "Arrow type int64 does not match int32"}, + {name: "complete IPC payload exceeds limit", frames: encoded.frames, descriptor: encoded.descriptor, expectedRows: 1, maxBatchBytes: 1, resultMetadata: resultMetadata, wantError: "exceeds"}, + {name: "materialized result exceeds invocation budget", frames: encoded.frames, descriptor: encoded.descriptor, expectedRows: 1, maxBatchBytes: DefaultMaxBatchBytes, maxResultBytes: 1, resultMetadata: resultMetadata, wantError: "invocation result exceeds"}, + {name: "result ACK response lost", frames: encoded.frames, descriptor: encoded.descriptor, expectedRows: 1, maxBatchBytes: DefaultMaxBatchBytes, resultMetadata: resultMetadata, ackError: errors.New("injected ACK response loss"), wantError: "injected ACK response loss"}, + {name: "result ACK sequence mismatch", frames: encoded.frames, descriptor: encoded.descriptor, expectedRows: 1, maxBatchBytes: DefaultMaxBatchBytes, resultMetadata: resultMetadata, ackResponse: wrongAck, wantError: "ACK sequence 2, expected 1"}, + } { + t.Run(tc.name, func(t *testing.T) { + mp := mpool.MustNewZeroNoFixed() + defer mpool.DeleteMPool(mp) + result := vector.NewFunctionResultWrapper(types.T_int64.ToType(), mp) + defer result.Free() + require.NoError(t, result.PreExtendAndReset(int(tc.expectedRows))) + sequence := protocol.Sequence{} + require.NoError(t, sequence.AcceptInput(1)) + require.NoError(t, sequence.EndInput(1)) + if tc.resultAlreadySeen { + require.NoError(t, sequence.AcceptResult(1)) + } + schema := &flight.FlightData{DataHeader: tc.frames[0].Header, AppMetadata: schemaMetadata} + consumed := &flight.FlightData{AppMetadata: inputConsumedMetadata} + record := &flight.FlightData{ + DataHeader: tc.frames[1].Header, DataBody: tc.frames[1].Body, + AppMetadata: tc.resultMetadata, + } + stream := &gatewayResultStream{results: []*flight.FlightData{schema, consumed, record}} + client := &resultAckFlightClient{response: tc.ackResponse, err: tc.ackError} + gateway := &Gateway{cfg: ClientConfig{ + MaxBatchBytes: tc.maxBatchBytes, MaxInvocationResultBytes: tc.maxResultBytes, + }} + schemaFrame := (*ArrowFrame)(nil) + schemaReady := false + rows, callErr := gateway.receiveResultBatch( + context.Background(), client, stream, tuple, 1, tc.expectedRows, + &schemaFrame, &schemaReady, tc.descriptor, result, mp, &sequence, + ) + if tc.wantError != "" { + require.ErrorContains(t, callErr, tc.wantError) + return + } + require.NoError(t, callErr) + require.Equal(t, 1, rows) + require.Equal(t, []int64{17}, vector.MustFixedColNoTypeCheck[int64](result.GetResultVector())) + lastInput, lastResult, ackedResult, inputEnded := sequence.State() + require.Equal(t, uint64(1), lastInput) + require.Equal(t, uint64(1), lastResult) + require.Equal(t, uint64(1), ackedResult) + require.True(t, inputEnded) + }) + } +} + func TestFreezeResultFramesFreezesBothPartsAndChargesTotal(t *testing.T) { header := []byte("record-header") body := []byte("record-body") @@ -669,3 +1397,51 @@ func TestFreezeResultFramesFreezesBothPartsAndChargesTotal(t *testing.T) { _, _, err = freezeResultFrames(header, body, int64(len(header)+len(body)-1)) require.ErrorContains(t, err, "exceeds") } + +func TestChooseInputBatchBoundsEncodingAttemptsAndPropagatesErrors(t *testing.T) { + frame := ArrowFrame{Header: []byte("schema"), Body: []byte("record")} + t.Run("full candidate avoids a search when it fits", func(t *testing.T) { + var attempts []int64 + got, err := chooseInputBatchWithFullCandidate(8, 64, func(rows int64) ([]ArrowFrame, error) { + attempts = append(attempts, rows) + return []ArrowFrame{frame}, nil + }, true) + require.NoError(t, err) + require.Equal(t, int64(8), got.Rows) + require.Equal(t, []ArrowFrame{frame}, got.Frames) + require.Equal(t, []int64{8}, attempts) + }) + + t.Run("size limit uses bounded exponential and binary search", func(t *testing.T) { + var attempts []int64 + got, err := chooseInputBatchWithFullCandidate(10, 64, func(rows int64) ([]ArrowFrame, error) { + attempts = append(attempts, rows) + if rows > 5 { + return nil, errArrowBatchTooLarge + } + return []ArrowFrame{frame}, nil + }, true) + require.NoError(t, err) + require.Equal(t, int64(5), got.Rows) + require.Equal(t, []int64{10, 1, 2, 4, 8, 6, 5}, attempts) + }) + + t.Run("non-size errors do not trigger retries", func(t *testing.T) { + wantErr := errors.New("injected Arrow encoder failure") + attempts := 0 + _, err := chooseInputBatchWithFullCandidate(4, 64, func(int64) ([]ArrowFrame, error) { + attempts++ + return nil, wantErr + }, true) + require.ErrorIs(t, err, wantErr) + require.Equal(t, 1, attempts) + }) + + t.Run("one row larger than the byte limit is rejected", func(t *testing.T) { + got, err := chooseInputBatch(4, 1, func(int64) ([]ArrowFrame, error) { + return nil, errArrowBatchTooLarge + }) + require.ErrorIs(t, err, errArrowBatchTooLarge) + require.Zero(t, got.Rows) + }) +} diff --git a/pkg/udf/runtime_test.go b/pkg/udf/runtime_test.go index 4ee0bd5e7e4c0..d415df5c46a6e 100644 --- a/pkg/udf/runtime_test.go +++ b/pkg/udf/runtime_test.go @@ -45,6 +45,15 @@ func (r *closableRuntime) Close() error { return nil } +type readinessRuntime struct { + *closableRuntime + readyErr error +} + +func (r *readinessRuntime) CheckLanguageReady(context.Context, string) error { + return r.readyErr +} + func TestRuntimeRegistryClosesOwnedRuntimesOnce(t *testing.T) { python := &closableRuntime{language: LanguagePython} runtime, err := NewRuntime(python) @@ -56,6 +65,63 @@ func TestRuntimeRegistryClosesOwnedRuntimesOnce(t *testing.T) { require.Equal(t, int32(1), python.closed.Load()) } +func TestRuntimeRegistryFailsClosedForMissingCapabilities(t *testing.T) { + ctx := context.Background() + registry, err := NewRuntime() + require.NoError(t, err) + provider, ok := registry.(RuntimeStatusProvider) + require.True(t, ok) + snapshot := provider.StatusSnapshot(ctx, LanguagePython) + require.Equal(t, RuntimeStatusDisabled, snapshot.ErrorClass) + require.Equal(t, RuntimeStatusReasonRuntimeDisabled, snapshot.Reason) + require.Error(t, registry.(RuntimeReadiness).CheckLanguageReady(ctx, LanguagePython)) + require.Error(t, registry.(RuntimeDefinitionValidator).ValidateDefinition(ctx, nil)) + require.Error(t, registry.Execute(ctx, nil, nil, nil)) + + plain, err := NewRuntime(&closableRuntime{language: LanguagePython}) + require.NoError(t, err) + snapshot = plain.(RuntimeStatusProvider).StatusSnapshot(ctx, LanguagePython) + require.Equal(t, RuntimeStatusUnavailable, snapshot.ErrorClass) + require.Equal(t, RuntimeStatusReasonStatusUnavailable, snapshot.Reason) + require.Error(t, plain.(RuntimeReadiness).CheckLanguageReady(ctx, LanguagePython)) + require.Error(t, plain.(RuntimeDefinitionValidator).ValidateDefinition(ctx, &RoutineDefinition{Language: LanguagePython})) + require.Error(t, plain.Execute(ctx, &Invocation{Language: "missing"}, nil, nil)) +} + +func TestRuntimeRegistryUsesReadinessFallback(t *testing.T) { + ctx := context.Background() + ready, err := NewRuntime(&readinessRuntime{closableRuntime: &closableRuntime{language: LanguagePython}}) + require.NoError(t, err) + snapshot := ready.(RuntimeStatusProvider).StatusSnapshot(ctx, LanguagePython) + require.True(t, snapshot.Ready) + require.Empty(t, snapshot.ErrorClass) + require.Equal(t, RuntimeStatusReasonReady, snapshot.Reason) + + cause := context.DeadlineExceeded + unavailable, err := NewRuntime(&readinessRuntime{ + closableRuntime: &closableRuntime{language: LanguagePython}, + readyErr: cause, + }) + require.NoError(t, err) + snapshot = unavailable.(RuntimeStatusProvider).StatusSnapshot(ctx, LanguagePython) + require.True(t, snapshot.Enabled) + require.False(t, snapshot.Ready) + require.Equal(t, RuntimeStatusUnavailable, snapshot.ErrorClass) + require.Equal(t, RuntimeStatusReasonWorkerUnavailable, snapshot.Reason) +} + +func TestNewRuntimeRejectsInvalidAndDuplicateLanguageOwners(t *testing.T) { + _, err := NewRuntime(nil) + require.Error(t, err) + _, err = NewRuntime(&closableRuntime{}) + require.Error(t, err) + _, err = NewRuntime( + &closableRuntime{language: LanguagePython}, + &closableRuntime{language: LanguagePython}, + ) + require.Error(t, err) +} + func TestStatementContextFromMapProducesCanonicalTypedSnapshot(t *testing.T) { snapshot, err := StatementContextFromMap(map[string]string{ "statement_timestamp_utc": "1704067200123456", From 8a866814ea5fa7133e0276aed4c8368df1936b2c Mon Sep 17 00:00:00 2001 From: iamlinjunhong <1030420200@qq.com> Date: Wed, 23 Sep 2026 21:44:45 +0800 Subject: [PATCH 198/202] fix(optools): retain Python runtime loader path --- optools/ut_holder_test.go | 46 ++++++++++++++++++++++++++++++--------- 1 file changed, 36 insertions(+), 10 deletions(-) diff --git a/optools/ut_holder_test.go b/optools/ut_holder_test.go index fc2f5fc235d4e..a6b9bc5f00f6b 100644 --- a/optools/ut_holder_test.go +++ b/optools/ut_holder_test.go @@ -57,28 +57,51 @@ func TestUTAdmissionHolderEvidence(t *testing.T) { } } -func TestPythonStdlibEnvironmentDropsNativeAndImportOverrides(t *testing.T) { +func TestPythonStdlibEnvironmentUsesConfiguredRuntimeLibraryPath(t *testing.T) { + t.Setenv("pythonLocation", "/python/toolcache") t.Setenv("LD_LIBRARY_PATH", "/matrixone/cgo") t.Setenv("PYTHONPATH", "/matrixone/python") t.Setenv("PYTHONHOME", "/matrixone/python-home") t.Setenv("PYTHONUSERBASE", "/matrixone/user-site") + t.Setenv("PYTHONSTARTUP", "/matrixone/startup.py") + t.Setenv("PYTHONINSPECT", "1") + t.Setenv("MO_UT_ENV_SENTINEL", "preserved") env := pythonStdlibEnvironment() - if !strings.Contains(strings.Join(env, "\n"), "PATH=") { + values := make(map[string]string, len(env)) + for _, entry := range env { + key, value, _ := strings.Cut(entry, "=") + values[key] = value + } + if values["PATH"] == "" { t.Fatal("sanitized Python environment lost PATH") } - for _, entry := range env { - for _, forbidden := range []string{"LD_LIBRARY_PATH=", "PYTHONPATH=", "PYTHONHOME=", "PYTHONUSERBASE="} { - if strings.HasPrefix(entry, forbidden) { - t.Fatalf("Python stdlib helper inherited %q", entry) - } + if got, want := values["MO_UT_ENV_SENTINEL"], "preserved"; got != want { + t.Fatalf("sanitized Python environment changed unrelated variable: got %q, want %q", got, want) + } + if got, want := values["LD_LIBRARY_PATH"], filepath.Join("/python/toolcache", "lib"); got != want { + t.Fatalf("Python runtime loader path = %q, want %q", got, want) + } + for _, forbidden := range []string{ + "PYTHONPATH", "PYTHONHOME", "PYTHONUSERBASE", "PYTHONSTARTUP", "PYTHONINSPECT", + } { + if _, ok := values[forbidden]; ok { + t.Fatalf("Python stdlib helper inherited %q", forbidden) } } } -// pythonStdlibEnvironment keeps the configured interpreter discoverable while -// preventing MatrixOne's native-library and Python import paths from changing -// a standard-library-only report parser's runtime. +func TestPythonStdlibEnvironmentDropsNativeLoaderPathWithoutPythonLocation(t *testing.T) { + t.Setenv("pythonLocation", "") + t.Setenv("LD_LIBRARY_PATH", "/matrixone/cgo") + if got := pythonStdlibEnvironment(); strings.Contains(strings.Join(got, "\n"), "LD_LIBRARY_PATH=") { + t.Fatalf("Python stdlib helper inherited MatrixOne loader path: %q", got) + } +} + +// pythonStdlibEnvironment keeps the configured interpreter's matching runtime +// library available while preventing MatrixOne's native libraries and Python +// import overrides from changing a standard-library-only report parser. func pythonStdlibEnvironment() []string { env := make([]string, 0, len(os.Environ())) for _, entry := range os.Environ() { @@ -90,5 +113,8 @@ func pythonStdlibEnvironment() []string { env = append(env, entry) } } + if pythonLocation := os.Getenv("pythonLocation"); pythonLocation != "" { + env = append(env, "LD_LIBRARY_PATH="+filepath.Join(pythonLocation, "lib")) + } return env } From 1a26a2f9046643a284df488f57470b811cca747a Mon Sep 17 00:00:00 2001 From: iamlinjunhong <1030420200@qq.com> Date: Thu, 24 Sep 2026 11:52:19 +0800 Subject: [PATCH 199/202] docs(udf): clarify test-stage acceptance scope --- docs/design/python_udf.md | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/docs/design/python_udf.md b/docs/design/python_udf.md index dde191f159839..47fbfdf1e84a3 100644 --- a/docs/design/python_udf.md +++ b/docs/design/python_udf.md @@ -6,6 +6,20 @@ The shared Function Catalog design owns identity, revision and security semantic SQL and Python designs own their language contracts. This summary does not replace those contracts. +## Owner-approved stage scope + +On 2026-09-24, feature owner `iamlinjunhong` approved this PR for testing and +development-stage acceptance. The user guide and SQL examples are recorded in +[issue #28132](https://github.com/matrixorigin/matrixone/issues/28132#issuecomment-5748253137). +This PR does not claim production rollout, sandbox isolation, protection for +untrusted Python, or authenticated/TLS Flight transport. Those remain separate +production acceptance gates. Python stays disabled in generic launch and requires +explicit opt-in in the test/development configurations. + +This stage decision is the feature owner's authorization for the current PR +scope; it is not a claim that Architecture, Security, SQL/Planner, Runtime, +Catalog, or Operator owners have approved the future production design. + ## Execution and identity CREATE/REPLACE validates Python syntax and the handler contract before publishing From c6f9c7682279816872fc3b3f7d5cf41ffe940acc Mon Sep 17 00:00:00 2001 From: iamlinjunhong <1030420200@qq.com> Date: Thu, 24 Sep 2026 14:55:00 +0800 Subject: [PATCH 200/202] docs(udf): approve current-stage design contract --- docs/design/python_udf.md | 9 +- docs/design/python_udf_current_stage.md | 236 ++++++++++++++++++++++++ 2 files changed, 241 insertions(+), 4 deletions(-) create mode 100644 docs/design/python_udf_current_stage.md diff --git a/docs/design/python_udf.md b/docs/design/python_udf.md index 47fbfdf1e84a3..a2b7e02dd11f5 100644 --- a/docs/design/python_udf.md +++ b/docs/design/python_udf.md @@ -1,10 +1,11 @@ # Python UDF implementation and rollout scope This describes the implemented, owner-authorized external Python adapter and its -PR acceptance boundary. It does not approve a production tenant-isolation model. -The shared Function Catalog design owns identity, revision and security semantics; -SQL and Python designs own their language contracts. This summary does not replace -those contracts. +PR acceptance boundary. The relevant current-stage Catalog, SQL/planner, +migration/restore, Python ABI, Flight, and resource contracts are consolidated +in the [versioned current-stage design](python_udf_current_stage.md). Broader +long-term designs continue to inform future work; this page does not approve a +production tenant-isolation model. ## Owner-approved stage scope diff --git a/docs/design/python_udf_current_stage.md b/docs/design/python_udf_current_stage.md new file mode 100644 index 0000000000000..2adcb1ec63ced --- /dev/null +++ b/docs/design/python_udf_current_stage.md @@ -0,0 +1,236 @@ +# Python UDF current-stage design contract + +| Field | Value | +| --- | --- | +| Design revision | `python-udf-current-stage-r1-2026-09-24` | +| Applies to | MatrixOne PR #29152, test/development-stage Python UDF | +| Implementation baseline reviewed | `9847da80d6c7096cb0460e46a5cf710e9714af3a` | +| Approval | Feature owner `iamlinjunhong` approved this exact revision for the current test/development stage on 2026-09-24. The traceable PR approval record is linked from the PR discussion for this revision. | +| Not covered | Production tenant isolation, sandbox, Operator rollout, or cross-version rollback/restore | + +This is the approved governing contract for the current PR stage. It consolidates +the shared Catalog, SQL/planner, Python ABI, Flight, and resource-lifecycle +decisions needed to review this implementation. It does not approve future +sandbox or production deployment. The test/development scope is explicit: +Python is opt-in and unisolated; old demo definitions are rejected and must be +recreated through the current DDL. There is no legacy Python adapter or +fallback execution path. + +## Identity, Catalog, and publication + +Python functions use the shared `mo_user_defined_function` identity namespace. +The current catalog upgrade adds `active_revision`, `namespace_version`, the +canonical input/return descriptors, and signature schema/fingerprint columns. +`mo_function_revisions` stores the immutable typed revision keyed by +`(function_id, revision)`, including language, full argument and return +descriptors, ABI/adapter/SDK/schema contracts, artifact and environment digests, +NULL policy, volatility, security mode, and definition fingerprint. Revision +publication and head activation occur in the caller's catalog transaction. +Python executes as `INVOKER`, is `VOLATILE`, may error, and is not leakproof. + +The artifact is an immutable, account-scoped FileService object addressed by +digest; each artifact is at most 1 MiB. CN resolves the exact account/digest +before Open, and the worker receives verified source bytes without object-store +credentials or query-time dependency installation. CREATE/REPLACE validates the +current contract before committing a catalog definition. Artifact publication +precedes catalog commit, so a failed validation/transaction can leave an +unreferenced content-addressed object. No artifact garbage collector or +per-account total artifact quota exists today; per-object size is bounded, total +retained artifact storage is not. This is an explicit test/development-stage +limitation, not a production storage-retention claim. + +The type descriptor is canonical over SQL type ID and applicable width, scale, +charset, offset-width, JSON, and temporal encodings. The trusted adapter builds +Arrow field/schema metadata from that descriptor; it validates both physical +Arrow type and the SQL value domain. Handler-provided metadata cannot redefine +the function type. CREATE/REPLACE compiles the submitted module and checks the +declared synchronous module-level handler; it does not execute top-level user +code or install packages. The environment digest includes the current runtime +environment, including tzdata. An environment mismatch fails closed; definitions +must be explicitly recreated for a changed contract. + +The current mappings encode JSON as `canonical_text` and DATE/DATETIME/TIMESTAMP +with `sql_zero_struct`; SQL zero temporal values remain explicit values and are +never rewritten as NULL. SCALAR handlers receive `(ctx, scalar...)`; VECTOR +handlers receive `(ctx, arrays...)`. The default NULL policy calls the handler +without filtering NULL rows; `RETURNS NULL ON NULL INPUT` removes those rows +before the call and scatters NULLs into their result positions. + +`REPLACE` preserves language and the exact Python argument/return descriptors; +changing language or signature requires DROP/CREATE. DROP/CREATE receives a new +identity. Old Python demo rows without a current revision head, unsupported +definition/SDK/plan contracts, and corrupt revision heads fail closed before +user code runs. They are not auto-migrated. No query resolves a mutable `latest` +revision or takes source from an executable plan. + +## SQL compatibility, plans, and migration + +The shared revision catalog is additive to the existing SQL UDF contract. +During a rolling tenant upgrade, SQL UDF writers continue using the legacy +`mo_user_defined_function` row while `mo_function_revisions` is absent. Once the +revision catalog is available, SQL writers publish the compatibility row and +revision in the same transaction. Readers use the legacy SQL row only when the +revision schema/head is absent or both head fields are zero. A non-zero but +missing or malformed revision is a catalog error; it never silently falls back. +The SQL UDF language, body, security, and evaluation semantics remain owned by +the existing SQL path. The Python demo's old execution behavior is not a +compatibility promise. + +Upgrade `4.0.7` (minimum source `4.0.6`) changes the shared function identity +columns/index and creates `mo_function_revisions`. The upgrade handler is gated +on common MORPC protocol 61 so a tenant migration does not widen catalog tables +while an old writer can still serve them. Python DDL independently probes the +required tenant schema and refuses creation/replacement until it is present; +worker capability negotiation is a separate exact-contract gate. This is +feature-level admission, not a requirement that every component have the same +software version string. + +The migration has no down handler. Do not manually remove the revision table, +columns, or index, and do not claim that restoring an old binary restores Python +execution. Binary rollback after applying this catalog migration is not a +supported recovery path in this contract; ordinary SQL behavior in that mixed +state has not been verified here. Python definitions must remain intact and +require a current-contract binary to execute. The current evidence does not +include a tested downgrade after Python definitions have been written. +The upgrade-compatibility CI checks were skipped on the reviewed exact head. + +Current-contract snapshot restore preserves the selected revision and +republishes its artifact into the destination account's namespace. The SQL +control function and source-account isolation are covered in +[`snapshot_restore.sql`](../../test/distributed/cases/udf_python/snapshot_restore.sql). +Cross-version restore and downgrade are outside the evidence and remain +unsupported until tested. Restore must not synthesize a revision from a legacy +Python row. + +The plan contains a typed `RoutineCall`, an exact `FunctionRef` (account, +database, identity, revision, namespace), a typed implementation oneof, and a +dependency envelope. Python source is not embedded in the executable plan. +Plan reuse validates both the exact selected revision and a fingerprint of the +complete same-name overload namespace across SQL and Python. The dependency +closure is capped at 1,024 routines and 1 MiB. This invalidates prepared/cache +plans when a newly added overload changes binding, even if the previously +selected identity still exists. Plans without the namespace fingerprint rebind; +an overflowed namespace read makes the plan non-reusable rather than accepting a +truncated fingerprint. Execution uses the bound exact reference and never +resolves by name. Namespace validation reads at most 65,536 candidates (one +extra detects overflow) and 64 MiB of decoded metadata; on overflow it forces +rebind without rejecting otherwise valid SQL. + +## SQL evaluation and result semantics + +`ExternalRoutineEval` is a physical expression evaluator in the existing MO +expression framework. The parent supplies the batch and CASE/selection mask; +the evaluator runs each bound argument expression, including planned casts, +once under that mask, applies +`RETURNS NULL ON NULL INPUT`, compacts selected rows, validates the returned +Arrow schema and SQL value domain, then scatters results to original row +positions. Unselected CASE branches are not invoked. Python's default +volatility prevents planning-time constant folding. Empty input, a batch with +no rows selected, and a batch whose rows are all removed by strict NULL +filtering return locally without acquiring Gateway K, a worker group, or a +ledger entry. Zero-argument VECTOR calls receive `ctx.num_rows` for output +shape. + +The ordinary Python BVT includes SELECT, WHERE, JOIN ON/outer-join predicates, +aggregate arguments, window ordering, nested calls, INSERT/UPDATE/DELETE, and +failure atomicity. It also covers scalar/vector modes, NULL policies, create, +replace, exact-overload invalidation, drop/recreate, Arrow compute, and +current-contract account restore. These are current tested placements; this +contract does not claim every possible SQL syntactic position without a case. + +## Flight protocol and terminal ownership + +Gateway is a CN library; the worker is the Flight server. There is no separately +deployed Gateway daemon. The current capability vector must match exactly +(wire protocol 1, ABI/adapter/SDK/definition/plan/type contracts, tzdata, +modes, NULL policies, frame/handler limits, and worker lease epoch). The worker +advertises `window_batches=1` and `cumulative_ack=false`. A mismatch fails +before handler execution; there is no old-worker fallback. Control envelopes +are limited to 1 MiB, JSON nesting to 64, and each fencing ID component to +256 bytes. + +After admission reserves the group/member, K, and ledger, the Gateway opens the +exchange. With W=1, each non-final batch follows +`InputBatch(i) → InputConsumed(i) → ResultBatch(i) → AcknowledgeResults(i)` +before the Gateway sends batch `i+1`. After sending the final +`InputBatch(last_seq)`, the Gateway sends `EndInput(last_seq)` and half-closes +its send direction; it then receives the final `InputConsumed`, result, and ACK, +followed by `Finish → AcknowledgeFinish(finish_id) → terminal success`. + +`EndInput` closes only the input direction; Flight send-half-close is normal +and the result direction remains readable. `InputConsumed` returns ownership of +the corresponding input backing. The result slot remains charged until it has +been validated and materialized in MO. W=1 permits one batch of progress at a +time. Finish is valid only after EndInput and acknowledgement of all result +sequences. The worker freezes success only after accepting the idempotent +Finish acknowledgement. If the ACK was accepted but its response was lost, the +Gateway retries only that same idempotent ACK; if confirmation remains unknown, +it reports `FINISH_UNCONFIRMED`. It never retries Open or replays work that may +have reached STARTED. + +The CN Gateway owns K, group/member admission, and its terminal ledger. A +member releases only its own resources; the group owner releases the group and +reservation. The current integration uses one invocation member per group. +Empty and all-NULL work is completed locally by the evaluator, without a remote +group. For admitted work, partial Open, cancellation, and close have one owner +and a terminal outcome. The worker owns H, child process/session cleanup, and +its own ledger. Generation/lease fences reject stale callbacks and messages. A +terminal TTL alone never grants a new execution epoch. On cancel, timeout, or +transport failure, Gateway cancels/closes the exchange before returning its +local K or ledger ownership; the worker retains H and cleanup ownership until +the child is terminated and reaped. STARTED work is not transparently replayed. + +Worker handler children are process-group scoped and must be terminated and +reaped by the worker's cleanup owner before H is returned. A failed or partial +cleanup retains bounded cleanup ownership instead of releasing H early. Linux +process-tree failure tests provide implementation evidence; this process model +does not confine a handler's access to the host or its Python heap. + +## Resource budgets and trust boundary + +Zero in an enabled CN client config means the runtime default, not unlimited. +The current defaults are: + +| Resource | Owner and default | Contract | +| --- | --- | --- | +| K, active invocations | CN Gateway: 8 per CN | Non-blocking admission; a full budget rejects rather than queueing while holding input vectors. | +| W, stream window | Gateway/worker: 1 batch | No multiple in-flight batches or cumulative ACK. | +| H, handler subprocesses | Worker: 8 total, 8/account, 4/owner | Worker constants and capability contract; admission is all-or-nothing. | +| Batch | CN/worker: 16 MiB and 65,536 rows | Each Arrow batch is bounded before transfer. | +| Invocation | CN/worker: 1,048,576 rows; CN result: 256 MiB | Result bound is per invocation. At K=8, retained result vectors alone can reach 2 GiB per CN. | +| Request | CN: 30 s default; worker ACK: 60 s | Configured handler timeout and protocol ACK timeout have distinct owners. | +| CN terminal state | 8,192 entries, 8 MiB, 10 min | Active entries plus tombstones and separately reserved closed-group fences are bounded. | +| Worker terminal state | 10,000 entries, 16 MiB, 5 min | Independent bound; worker values intentionally differ from CN values. | +| Worker pending cleanup | 10,000 invocations | Derived from worker ledger capacity; cleanup failure retains ownership rather than releasing H. | +| Same-invocation burst reuse | Worker: 64 batches, 64 MiB, or 30 s | The first reached limit ends reuse; statement deadline and started inputs do not reset. | + +The per-invocation result maximum multiplied by K is a deliberate worst-case +capacity implication, not a process-wide RSS limit. Arrow/IPC, pickle/pipe, +temporary snapshots, materialization, Python heap, queues, file descriptors, +and child processes have different owners; wire-byte limits do not cap arbitrary +Python heap growth. Artifact count and total FileService bytes are not capped. +The CN's completion retention is longer than the worker's (10 versus 5 minutes); +the limits are compatible, not equal. The CN retains its own terminal/group +fences and never transparently resends STARTED work after a worker tombstone +expires. +The adapter is unisolated, Flight has no TLS/authentication in this stage, and +the fencing tuple is not an authentication token. These limits support +test/development correctness and backpressure, not hostile-code containment. + +## Evidence and remaining limits + +| Contract | Evidence on the reviewed branch/head | Status | +| --- | --- | --- | +| Shared revision schema and SQL legacy fallback | `pkg/bootstrap/versions/v4_0_7/upgrade_test.go`; `pkg/frontend/python_udf_catalog_test.go` | Unit/schema contract covered; upgrade-compatibility CI was skipped. | +| Exact FunctionRef and overload invalidation | `pkg/frontend/routine_plan_validation_test.go`; `pkg/frontend/routine_namespace_test.go`; `test/distributed/cases/udf_python/overload_namespace.sql` | Unit and SQL prepared-plan cases present. | +| SQL expression placement and result semantics | `pkg/sql/colexec/external_routine_eval.go`; `test/distributed/cases/udf_python/relational_positions.sql`, `dml_positions.sql`, `vector_mode.sql` | Physical evaluator and ordinary BVT cases present. | +| Current-contract restore | `pkg/frontend/clone_database_source.go`; `pkg/frontend/snapshot_catalog_restore.go`; `snapshot_restore.sql` | Same-contract account restore case present; cross-version restore not verified. | +| Flight state, ACK, cancellation, resource limits | `pkg/udf/python/gateway_test.go`, `gateway_admission_test.go`, `capability_lifecycle_test.go`, `gateway_integration_test.go`, `pkg/udf/python/worker/test_worker.py`, `test_watchdog.py` | Unit, process, and real Flight cases present; this design does not substitute for implementation/lifecycle review. | +| Exact-head CI | PR #29152 validation reported shared build, Linux UT, SCA, coverage, and active multi-CN BVT passed | Upgrade-compatibility checks skipped; no downgrade evidence. | + +The reviewed change is not declared production-ready. Artifact GC/total-storage +quota, cross-version migration rollback/restore, authenticated transport, and +sandboxing remain explicit limits. The owner-approved acceptance is limited to +the test/development stage. This approval closes the current-stage design gate; +the reviewer must still complete the implementation/lifecycle review before the +PR review is complete. From 632dc59e0bcefe1f06e555f5bf3d76f4070a9d18 Mon Sep 17 00:00:00 2001 From: iamlinjunhong <1030420200@qq.com> Date: Thu, 24 Sep 2026 14:56:12 +0800 Subject: [PATCH 201/202] docs(udf): link design approval record --- docs/design/python_udf_current_stage.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/design/python_udf_current_stage.md b/docs/design/python_udf_current_stage.md index 2adcb1ec63ced..35741c3992f2c 100644 --- a/docs/design/python_udf_current_stage.md +++ b/docs/design/python_udf_current_stage.md @@ -5,7 +5,7 @@ | Design revision | `python-udf-current-stage-r1-2026-09-24` | | Applies to | MatrixOne PR #29152, test/development-stage Python UDF | | Implementation baseline reviewed | `9847da80d6c7096cb0460e46a5cf710e9714af3a` | -| Approval | Feature owner `iamlinjunhong` approved this exact revision for the current test/development stage on 2026-09-24. The traceable PR approval record is linked from the PR discussion for this revision. | +| Approval | Feature owner `iamlinjunhong` approved this exact revision for the current test/development stage on 2026-09-24 in [PR comment](https://github.com/matrixorigin/matrixone/pull/29152#issuecomment-5809313638). | | Not covered | Production tenant isolation, sandbox, Operator rollout, or cross-version rollback/restore | This is the approved governing contract for the current PR stage. It consolidates From 4ca229411166b9da09a702dbe1b799de43d8d694 Mon Sep 17 00:00:00 2001 From: iamlinjunhong <1030420200@qq.com> Date: Thu, 24 Sep 2026 18:25:55 +0800 Subject: [PATCH 202/202] docs(udf): record current-stage design approvals --- docs/design/python_udf_current_stage.md | 2 +- .../python_udf_current_stage_approval.md | 67 +++++++++++++++++++ 2 files changed, 68 insertions(+), 1 deletion(-) create mode 100644 docs/design/python_udf_current_stage_approval.md diff --git a/docs/design/python_udf_current_stage.md b/docs/design/python_udf_current_stage.md index 35741c3992f2c..994b8016729d2 100644 --- a/docs/design/python_udf_current_stage.md +++ b/docs/design/python_udf_current_stage.md @@ -5,7 +5,7 @@ | Design revision | `python-udf-current-stage-r1-2026-09-24` | | Applies to | MatrixOne PR #29152, test/development-stage Python UDF | | Implementation baseline reviewed | `9847da80d6c7096cb0460e46a5cf710e9714af3a` | -| Approval | Feature owner `iamlinjunhong` approved this exact revision for the current test/development stage on 2026-09-24 in [PR comment](https://github.com/matrixorigin/matrixone/pull/29152#issuecomment-5809313638). | +| Approval | Feature owner `iamlinjunhong` approved this exact revision for the current test/development stage on 2026-09-24 in [PR comment](https://github.com/matrixorigin/matrixone/pull/29152#issuecomment-5809313638). The immutable artifact and independent-review status are recorded in the [design approval record](python_udf_current_stage_approval.md). | | Not covered | Production tenant isolation, sandbox, Operator rollout, or cross-version rollback/restore | This is the approved governing contract for the current PR stage. It consolidates diff --git a/docs/design/python_udf_current_stage_approval.md b/docs/design/python_udf_current_stage_approval.md new file mode 100644 index 0000000000000..ae17966108032 --- /dev/null +++ b/docs/design/python_udf_current_stage_approval.md @@ -0,0 +1,67 @@ +# Python UDF current-stage design approval record + +| Field | Value | +| --- | --- | +| Record | `python-udf-current-stage-r1-2026-09-24-approval` | +| Issue | [MatrixOne #28132](https://github.com/matrixorigin/matrixone/issues/28132) | +| Implementation PR | [MatrixOne #29152](https://github.com/matrixorigin/matrixone/pull/29152) | +| Current-stage contract | [`python-udf-current-stage-r1-2026-09-24`](python_udf_current_stage.md) | +| Scope | Explicitly enabled test/development adapter; no production rollout or production-readiness claim | + +This record separates the immutable current-stage design artifact, the feature +owner's scope authorization, and independent technical approval facts. It is a +traceability record; it does not create an approval that is not present in the +PR or issue history. + +## Immutable artifacts + +The design content accepted for the current test/development stage was published +in the rebased PR history at commit [`c6f9c7682279816872fc3b3f7d5cf41ffe940acc`](https://github.com/matrixorigin/matrixone/commit/c6f9c7682279816872fc3b3f7d5cf41ffe940acc). +The corresponding pre-rebase review commit was +[`610d1e4dcfe2bad2ed1060babe0acc60c89b1c60`](https://github.com/matrixorigin/matrixone/commit/610d1e4dcfe2bad2ed1060babe0acc60c89b1c60). +The exact Git blob for +[`docs/design/python_udf_current_stage.md`](https://github.com/matrixorigin/matrixone/blob/c6f9c7682279816872fc3b3f7d5cf41ffe940acc/docs/design/python_udf_current_stage.md) +at both design commits is +`2adcb1ec63ced2dfbdedf192509b07d5b7c9db31`. + +The design was written against implementation baseline +[`9847da80d6c7096cb0460e46a5cf710e9714af3a`](https://github.com/matrixorigin/matrixone/commit/9847da80d6c7096cb0460e46a5cf710e9714af3a). +The rebase-equivalent implementation baseline in the final PR history is +[`1a26a2f9046643a284df488f57470b811cca747a`](https://github.com/matrixorigin/matrixone/commit/1a26a2f9046643a284df488f57470b811cca747a). +The pre-rebase exact review head was +[`92a89d81588159a918458b1a930bccc9d0e75d4b`](https://github.com/matrixorigin/matrixone/commit/92a89d81588159a918458b1a930bccc9d0e75d4b). +The repair commit carrying this record is the final documentation commit on +top of the rebased PR history; its exact remote head is verified at delivery. +The final current-stage design blob is +`994b8016729d2e87a6a706a2833eee7faf50c89b`; its only difference from the +approved design blob is the link to this traceability record. + +The namespace-validation companion is the implementation contract from commit +[`284a1d971a1d2c763c6aed425d52f751735c409f`](https://github.com/matrixorigin/matrixone/commit/284a1d971a1d2c763c6aed425d52f751735c409f), +with current-head blob `51ccfb771db19321d03b292cd3d7219f698a11b8`. + +## Approval ledger + +| Decision area | Evidence and scope | Status | +| --- | --- | --- | +| Feature-owner acceptance | [PR comment](https://github.com/matrixorigin/matrixone/pull/29152#issuecomment-5809313638) approves the exact current-stage contract for testing/development use. | Approved for this limited scope | +| Architecture review | No independent approval record naming the reviewer, exact design artifact, date, scope, and decision is present in the PR/issue history examined at pre-rebase head `92a89d81588159a918458b1a930bccc9d0e75d4b`. | Pending; not inferred | +| Security review | The contract records the unisolated, opt-in boundary and explicitly excludes sandboxing and authenticated/TLS Flight. That scope record is not a production security approval. | Pending for any production/security enablement; not inferred | +| SQL/Planner review | The current-stage document contains the shared Catalog, exact revision, overload, evaluation, migration, restore, and compatibility contract. No independent approval record for that exact artifact is present in the PR/issue history examined at the recorded pre-rebase head. | Pending; not inferred | +| Cloud/Operator review | The current-stage document records launch, init, endpoint, and rollout limits, and excludes Operator rollout acceptance. No independent approval record for production/operator rollout is present. | Pending; not inferred | + +The pending rows are external approval facts and are intentionally not replaced +by feature-owner authorization, passing CI, or implementation review. They must +remain separate from the test/development acceptance boundary. This PR does not +authorize production tenant isolation, sandboxing, authenticated transport, +Operator rollout, artifact garbage collection, total artifact quota, or +cross-version downgrade/restore. + +## Evidence boundary + +The design document's evidence table identifies the focused unit, process, Flight, +SQL/BVT, restore, and exact-head CI evidence. Upgrade-compatibility checks were +skipped at the recorded head; cross-version rollback/restore is unsupported in +this stage. Those gaps are recorded as limits and are not represented as passing +approval evidence. Full implementation/lifecycle review remains a separate PR +review step after the design record is accepted by the relevant reviewers.