From 78f31e90297b70bb263d08c4b8ce4907c1284efe Mon Sep 17 00:00:00 2001 From: jiangxinmeng1 <51114574+jiangxinmeng1@users.noreply.github.com> Date: Thu, 19 Dec 2024 19:35:15 +0800 Subject: [PATCH 01/26] reset create node and delete node in replay (#20825) reset create node and delete node in replay Approved by: @XuPeng-SH, @LeftHandCold, @daviszhen, @qingxinhome --- pkg/backup/backup_test.go | 86 +++++++++++++++++++++- pkg/vm/engine/tae/catalog/catalogreplay.go | 17 ++++- pkg/vm/engine/tae/logtail/backup.go | 12 ++- 3 files changed, 106 insertions(+), 9 deletions(-) diff --git a/pkg/backup/backup_test.go b/pkg/backup/backup_test.go index 8eed272a31c0d..386cfaee9eb22 100644 --- a/pkg/backup/backup_test.go +++ b/pkg/backup/backup_test.go @@ -17,19 +17,21 @@ package backup import ( "context" "fmt" - "github.com/panjf2000/ants/v2" - "github.com/prashantv/gostub" "path" "sync" "testing" "time" + "github.com/panjf2000/ants/v2" + "github.com/prashantv/gostub" + "github.com/matrixorigin/matrixone/pkg/common/runtime" "github.com/matrixorigin/matrixone/pkg/container/types" "github.com/matrixorigin/matrixone/pkg/defines" "github.com/matrixorigin/matrixone/pkg/fileservice" "github.com/matrixorigin/matrixone/pkg/logservice" "github.com/matrixorigin/matrixone/pkg/sql/parsers/tree" + "github.com/matrixorigin/matrixone/pkg/util/fault" "github.com/matrixorigin/matrixone/pkg/vm/engine/tae/catalog" "github.com/matrixorigin/matrixone/pkg/vm/engine/tae/common" "github.com/matrixorigin/matrixone/pkg/vm/engine/tae/db/testutil" @@ -339,6 +341,86 @@ func TestBackupData3(t *testing.T) { } +func TestBackupData4(t *testing.T) { + defer testutils.AfterTest(t)() + testutils.EnsureNoLeak(t) + ctx := context.Background() + + fault.Enable() + defer fault.Disable() + fault.AddFaultPoint(ctx, "back up UT", ":::", "echo", 0, "debug", false) + defer fault.RemoveFaultPoint(ctx, "back up UT") + + opts := config.WithLongScanAndCKPOpts(nil) + db := testutil.NewTestEngine(ctx, ModuleName, t, opts) + defer db.Close() + defer opts.Fs.Close(ctx) + + schema := catalog.MockSchemaAll(13, 3) + schema.Extra.BlockMaxRows = 10 + schema.Extra.ObjectMaxBlocks = 10 + db.BindSchema(schema) + + totalRows := 20 + bat := catalog.MockBatch(schema, int(totalRows)) + defer bat.Close() + db.CreateRelAndAppend2(bat, true) + t.Log(db.Catalog.SimplePPString(common.PPL1)) + + dir := path.Join(db.Dir, "/local") + c := fileservice.Config{ + Name: defines.LocalFileServiceName, + Backend: "DISK", + DataDir: dir, + } + service, err := fileservice.NewFileService(ctx, c, nil) + assert.Nil(t, err) + defer service.Close(ctx) + backupTime := time.Now().UTC() + currTs := types.BuildTS(backupTime.UnixNano(), 0) + locations := make([]string, 0) + locations = append(locations, backupTime.Format(time.DateTime)) + location, err := db.ForceCheckpointForBackup(ctx, currTs, 20*time.Second) + assert.Nil(t, err) + db.BGCheckpointRunner.DisableCheckpoint() + locations = append(locations, location) + compacted := db.BGCheckpointRunner.GetCompacted() + checkpoints := db.BGCheckpointRunner.GetAllCheckpointsForBackup(compacted) + files := make(map[string]string, 0) + for _, candidate := range checkpoints { + if files[candidate.GetLocation().Name().String()] == "" { + var loc string + loc = candidate.GetLocation().String() + loc += ":" + loc += fmt.Sprintf("%d", candidate.GetVersion()) + files[candidate.GetLocation().Name().String()] = loc + } + } + for _, location := range files { + locations = append(locations, location) + } + fileList := make([]*taeFile, 0) + err = execBackup(ctx, "", db.Opts.Fs, service, locations, 1, types.TS{}, "full", &fileList) + assert.Nil(t, err) + fileMap := make(map[string]struct{}) + for _, file := range fileList { + _, ok := fileMap[file.path] + assert.True(t, !ok) + fileMap[file.path] = struct{}{} + } + db.Opts.Fs = service + db.Restart(ctx) + t.Log(db.Catalog.SimplePPString(3)) + txn, rel := testutil.GetDefaultRelation(t, db.DB, schema.Name) + testutil.CheckAllColRowsByScan(t, rel, int(totalRows), true) + assert.NoError(t, txn.Commit(context.Background())) + db.MergeBlocks(true) + db.ForceGlobalCheckpoint(ctx, db.TxnMgr.Now(), time.Second, time.Second) + t.Log(db.Catalog.SimplePPString(3)) + db.Restart(ctx) + +} + func Test_saveTaeFilesList(t *testing.T) { type args struct { ctx context.Context diff --git a/pkg/vm/engine/tae/catalog/catalogreplay.go b/pkg/vm/engine/tae/catalog/catalogreplay.go index 78028cac68bbc..95bf7aed87b45 100644 --- a/pkg/vm/engine/tae/catalog/catalogreplay.go +++ b/pkg/vm/engine/tae/catalog/catalogreplay.go @@ -21,6 +21,7 @@ import ( "github.com/matrixorigin/matrixone/pkg/common/moerr" "github.com/matrixorigin/matrixone/pkg/container/types" "github.com/matrixorigin/matrixone/pkg/logutil" + "github.com/matrixorigin/matrixone/pkg/util/fault" "github.com/matrixorigin/matrixone/pkg/vm/engine/tae/common" "github.com/matrixorigin/matrixone/pkg/vm/engine/tae/containers" "github.com/matrixorigin/matrixone/pkg/vm/engine/tae/iface/txnif" @@ -500,15 +501,25 @@ func (catalog *Catalog) onReplayCheckpointObject( if objid.Offset() == Backup_Object_Offset && entryNode.DeletedAt.IsEmpty() { obj = newObject() rel.AddEntryLocked(obj) - obj.CreateNode = *txnbase.NewTxnMVCCNodeWithTS(obj.CreatedAt) + _, sarg, _ := fault.TriggerFault("back up UT") + if sarg == "" { + obj.CreateNode = *txnbase.NewTxnMVCCNodeWithTS(obj.CreatedAt) + } logutil.Warnf("obj %v, tbl %v-%d create %v, delete %v, end %v", objid.String(), rel.fullName, rel.ID, entryNode.CreatedAt.ToString(), entryNode.DeletedAt.ToString(), txnNode.End.ToString()) } else { if !entryNode.DeletedAt.IsEmpty() { - panic(fmt.Sprintf("logic error: obj %v, tbl %v-%d create %v, delete %v, end %v", + logutil.Warnf("obj %v, tbl %v-%d create %v, delete %v, end %v", objid.String(), rel.fullName, rel.ID, entryNode.CreatedAt.ToString(), - entryNode.DeletedAt.ToString(), txnNode.End.ToString())) + entryNode.DeletedAt.ToString(), txnNode.End.ToString()) + obj, _ = rel.GetObjectByID(objid, isTombstone) + if obj == nil { + obj = newObject() + rel.AddEntryLocked(obj) + } + obj.CreateNode = *txnbase.NewTxnMVCCNodeWithTS(entryNode.CreatedAt) + obj.DeleteNode = *txnbase.NewTxnMVCCNodeWithTS(entryNode.DeletedAt) } } } diff --git a/pkg/vm/engine/tae/logtail/backup.go b/pkg/vm/engine/tae/logtail/backup.go index 724e36e89faa5..176cd5f9e7666 100644 --- a/pkg/vm/engine/tae/logtail/backup.go +++ b/pkg/vm/engine/tae/logtail/backup.go @@ -22,6 +22,7 @@ import ( "github.com/matrixorigin/matrixone/pkg/common/mpool" "github.com/matrixorigin/matrixone/pkg/container/vector" "github.com/matrixorigin/matrixone/pkg/pb/plan" + "github.com/matrixorigin/matrixone/pkg/util/fault" "github.com/matrixorigin/matrixone/pkg/vm/engine" "github.com/matrixorigin/matrixone/pkg/vm/engine/tae/index" "go.uber.org/zap" @@ -736,10 +737,13 @@ func ReWriteCheckpointAndBlockFromKey( objectio.WithSorted()(insertObjData[i].stats) newMeta.GetVectorByName(ObjectAttr_ObjectStats).Update(row, insertObjData[i].stats[:], false) newMeta.GetVectorByName(EntryNode_DeleteAt).Update(row, types.TS{}, false) - createTS := newMeta.GetVectorByName(EntryNode_CreateAt).Get(i).(types.TS) - newMeta.GetVectorByName(txnbase.SnapshotAttr_CommitTS).Update(row, createTS, false) - newMeta.GetVectorByName(txnbase.SnapshotAttr_PrepareTS).Update(row, createTS, false) - newMeta.GetVectorByName(txnbase.SnapshotAttr_StartTS).Update(row, createTS.Prev(), false) + _, sarg, _ := fault.TriggerFault("back up UT") + if sarg == "" { + createTS := newMeta.GetVectorByName(EntryNode_CreateAt).Get(i).(types.TS) + newMeta.GetVectorByName(txnbase.SnapshotAttr_CommitTS).Update(row, createTS, false) + newMeta.GetVectorByName(txnbase.SnapshotAttr_PrepareTS).Update(row, createTS, false) + newMeta.GetVectorByName(txnbase.SnapshotAttr_StartTS).Update(row, createTS.Prev(), false) + } } } } From 93d41c6ad0c9f465b270c8554a8a87f9fd61594b Mon Sep 17 00:00:00 2001 From: Wei Ziran Date: Thu, 19 Dec 2024 20:40:46 +0800 Subject: [PATCH 02/26] remove bvt tag (#20835) revert bvt tag Approved by: @aressu1985 --- test/distributed/cases/log/show_table.sql | 4 +--- test/distributed/cases/mo_cloud/mo_cloud.sql | 2 -- 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/test/distributed/cases/log/show_table.sql b/test/distributed/cases/log/show_table.sql index 643e12e9f8b8b..f682283b304d3 100644 --- a/test/distributed/cases/log/show_table.sql +++ b/test/distributed/cases/log/show_table.sql @@ -1,6 +1,4 @@ -- check system / system_metrics in sys account --- @bvt:issue#19912 SHOW CREATE TABLE system_metrics.metric; SHOW CREATE TABLE system.statement_info; -SHOW CREATE TABLE system.rawlog; --- @bvt:issue \ No newline at end of file +SHOW CREATE TABLE system.rawlog; \ No newline at end of file diff --git a/test/distributed/cases/mo_cloud/mo_cloud.sql b/test/distributed/cases/mo_cloud/mo_cloud.sql index a86a3077494c6..c50827af2d731 100644 --- a/test/distributed/cases/mo_cloud/mo_cloud.sql +++ b/test/distributed/cases/mo_cloud/mo_cloud.sql @@ -64,9 +64,7 @@ SHOW CREATE TABLE mysql.db; SHOW CREATE TABLE mysql.procs_priv; SHOW CREATE TABLE mysql.tables_priv; SHOW CREATE TABLE mysql.user; --- @bvt:issue#19912 SHOW CREATE TABLE system_metrics.metric; --- @bvt:issue SHOW CREATE TABLE system_metrics.server_connections; SHOW CREATE TABLE system_metrics.server_storage_usage; SHOW CREATE TABLE system_metrics.sql_statement_errors; From ddb306b7eb9c955dbbd72ca08d20031eac54bae7 Mon Sep 17 00:00:00 2001 From: davis zhen Date: Thu, 19 Dec 2024 22:28:29 +0800 Subject: [PATCH 03/26] cherrypick from 2.0.1hotfix to main: use fixed function id (#20831) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 原因:原先的function id用的是iota递增值。如果在中间新增了函数,会导致后面的id发生变化。 修改:functionid 用const值。新增函数追加用不重复的值。 Approved by: @badboynt1, @m-schen, @aunjgr --- pkg/sql/plan/build_update.go | 7 +- pkg/sql/plan/build_util.go | 29 +- pkg/sql/plan/build_util_test.go | 61 ++ pkg/sql/plan/function/function.go | 3 + pkg/sql/plan/function/function_id.go | 706 +++++++++++----------- pkg/sql/plan/function/function_id_test.go | 409 +++++++++++++ 6 files changed, 860 insertions(+), 355 deletions(-) create mode 100644 pkg/sql/plan/build_util_test.go create mode 100644 pkg/sql/plan/function/function_id_test.go diff --git a/pkg/sql/plan/build_update.go b/pkg/sql/plan/build_update.go index caeb9cb18e9b2..92595683321ba 100644 --- a/pkg/sql/plan/build_update.go +++ b/pkg/sql/plan/build_update.go @@ -145,7 +145,12 @@ func rewriteUpdateQueryLastNode(builder *QueryBuilder, planCtxs []*dmlPlanCtx, l } else { pos := idx + colIdx if col.OnUpdate != nil && col.OnUpdate.Expr != nil { - lastNode.ProjectList[pos] = col.OnUpdate.Expr + newDefExpr := DeepCopyExpr(col.OnUpdate.Expr) + err = replaceFuncId(builder.GetContext(), newDefExpr) + if err != nil { + return err + } + lastNode.ProjectList[pos] = newDefExpr } if col != nil && col.Typ.Id == int32(types.T_enum) { diff --git a/pkg/sql/plan/build_util.go b/pkg/sql/plan/build_util.go index e15a842a5e586..173bbf4b27473 100644 --- a/pkg/sql/plan/build_util.go +++ b/pkg/sql/plan/build_util.go @@ -30,6 +30,7 @@ import ( "github.com/matrixorigin/matrixone/pkg/sql/colexec" "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/vm/process" ) @@ -478,7 +479,33 @@ func getDefaultExpr(ctx context.Context, d *plan.ColDef) (*Expr, error) { }, }, nil } - return d.Default.Expr, nil + newDefExpr := DeepCopyExpr(d.Default.Expr) + err := replaceFuncId(ctx, newDefExpr) + return newDefExpr, err +} + +func replaceFuncId(ctx context.Context, expr *Expr) error { + switch fun := expr.Expr.(type) { + case *plan.Expr_F: + for _, arg := range fun.F.Args { + err := replaceFuncId(ctx, arg) + if err != nil { + return err + } + } + + fnName := fun.F.Func.ObjName + newFID, err := function.GetFunctionIdByName(ctx, fnName) + if err != nil { + return err + } + oldFID, oldIdx := function.DecodeOverloadID(fun.F.Func.Obj) + if oldFID != newFID { + fun.F.Func.Obj = function.EncodeOverloadID(newFID, oldIdx) + } + default: + } + return nil } func judgeUnixTimestampReturnType(timestr string) types.T { diff --git a/pkg/sql/plan/build_util_test.go b/pkg/sql/plan/build_util_test.go new file mode 100644 index 0000000000000..ba220d26d22a8 --- /dev/null +++ b/pkg/sql/plan/build_util_test.go @@ -0,0 +1,61 @@ +// Copyright 2024 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 ( + "context" + "testing" + + "github.com/stretchr/testify/assert" + + "github.com/matrixorigin/matrixone/pkg/pb/plan" + "github.com/matrixorigin/matrixone/pkg/sql/plan/function" +) + +func Test_replaceFuncId(t *testing.T) { + case1 := &Expr{ + Expr: &plan.Expr_F{ + F: &plan.Function{ + Func: &ObjectRef{ + ObjName: "current_timestamp", + Obj: function.CURRENT_TIMESTAMP, + }, + Args: []*Expr{ + { + Expr: &plan.Expr_Col{ + Col: &plan.ColRef{ + RelPos: 1, + ColPos: 10, + Name: "a", + }, + }, + }, + }, + }, + }, + } + + err := replaceFuncId(context.Background(), case1) + assert.NoError(t, err) + + case1ColDef := &plan.ColDef{ + Default: &plan.Default{ + Expr: case1, + }, + } + case1Expr, err := getDefaultExpr(context.Background(), case1ColDef) + assert.NoError(t, err) + assert.NotNil(t, case1Expr) +} diff --git a/pkg/sql/plan/function/function.go b/pkg/sql/plan/function/function.go index 1ac7d7ac4c48e..bbdb53983bb76 100644 --- a/pkg/sql/plan/function/function.go +++ b/pkg/sql/plan/function/function.go @@ -510,3 +510,6 @@ func (selectList *FunctionSelectList) Contains(row uint64) bool { } return !selectList.SelectList[row] } + +var EncodeOverloadID = encodeOverloadID +var GetFunctionIdByName = getFunctionIdByName diff --git a/pkg/sql/plan/function/function_id.go b/pkg/sql/plan/function/function_id.go index cec2ba1b96a72..9caa1f950fd44 100644 --- a/pkg/sql/plan/function/function_id.go +++ b/pkg/sql/plan/function/function_id.go @@ -21,393 +21,393 @@ const ( // All function IDs const ( - EQUAL = iota // = - NOT_EQUAL // <> - GREAT_THAN // > - GREAT_EQUAL // >= - LESS_THAN // < - LESS_EQUAL // <= - BETWEEN - UNARY_PLUS // UNARY_PLUS + - UNARY_MINUS // UNARY_MINUS - - UNARY_TILDE // UNARY_TILDE ~ - PLUS // + - MINUS // - - MULTI // * - DIV // / - INTEGER_DIV // Div - MOD // % - CONCAT // || - AND - OR - XOR - NOT - CAST - BIT_CAST - IS - ISNOT - ISNULL - ISNOTNULL - ISTRUE - ISNOTTRUE - ISFALSE - ISNOTFALSE - ISEMPTY - NOT_IN_ROWS - OP_BIT_AND // & - OP_BIT_OR // | - OP_BIT_XOR // ^ - OP_BIT_SHIFT_LEFT // << - OP_BIT_SHIFT_RIGHT // >> - - ABS - ACOS - ADDDATE - ADDTIME - AES_DECRYPT - AES_ENCRYPT - ANY_VALUE - APPROX_COUNT - ARRAY_AGG - ARRAY_APPEND - ARRAY_CAT - ARRAY_CONTAINS - ARRAY_POSITION - ARRAY_SIZE - ASCII - ASIN - ASSERT - ATAN - ATAN2 - AVG - AVG_TW_CACHE - AVG_TW_RESULT - BASE64_DECODE - BASE64_ENCODE - BIT_AND - BIT_LENGTH - BIT_NOT - BIT_OR - BIT_XOR - BITAGG_AND - BITAGG_OR - BOOLAGG_AND - BOOLAGG_OR - CASE - CEIL - CHR - COALESCE - FIELD - CONCAT_WS - CONTAINS - CORR - COS - COT - CRC32 - COUNT - COUNT_IF - COVAR_POP - COVAR_SAMPLE - CONVERT_TZ - CUME_DIST - CURRENT_DATE - CURRENT_TIMESTAMP - DATE_FROM_PARTS - DATE_PART - DATEADD - DATEDIFF - TIMEDIFF - TIMESTAMPDIFF - DENSE_RANK - MO_WIN_DIVISOR - EMPTY - ENDSWITH - EXP - FINDINSET - FIRST_VALUE - FLOOR - GREATEST - GROUPING - HASH - HASH_AGG - HEX_DECODE - HEX_ENCODE - HEX - UNHEX - MD5 - IFF - IFNULL - ILIKE - ILIKE_ALL - ILIKE_ANY - IN - LAG - LAST_VALUE - LEAD - LEAST - LEFT - LENGTH - LENGTH_UTF8 - LIKE - LIKE_ALL - LIKE_ANY - LN - NOT_IN - LOG - LOG2 - LOG10 - LOWER - LPAD - LTRIM - MAX - MEDIAN - MIN - MODE - MONTH - NORMAL - NTH_VALUE - NTILE - NULLIF - PERCENT_RANK - PI - POSITION - POW - PREFIX_EQ - PREFIX_IN - PREFIX_BETWEEN - RADIAN - RANDOM - RANK - REGEXP - REGEXP_INSTR - REGEXP_LIKE - REGEXP_REPLACE - REGEXP_SUBSTR - REG_MATCH - NOT_REG_MATCH - REPEAT - REPLACE - REVERSE - RIGHT - ROUND - ROW_NUMBER - RPAD - RTRIM - SIGN - SIN - SINH - SPACE - SPLIT - SPLIT_PART - SQRT - STARCOUNT - STARTSWITH - STDDEV_POP - STDDEV_SAMPLE - SUBSTR - SUM - SYSDATE - GROUP_CONCAT - TAN - TO_DATE - STR_TO_DATE - TO_INTERVAL - TRANSLATE - TRIM - UNIFORM - SHA1 - SHA2 - UTC_TIMESTAMP - UNIX_TIMESTAMP - FROM_UNIXTIME - UPPER - VAR_POP - VAR_SAMPLE + EQUAL = 0 // = + NOT_EQUAL = 1 // <> + GREAT_THAN = 2 // > + GREAT_EQUAL = 3 // >= + LESS_THAN = 4 // < + LESS_EQUAL = 5 // <= + BETWEEN = 6 + UNARY_PLUS = 7 // UNARY_PLUS + + UNARY_MINUS = 8 // UNARY_MINUS - + UNARY_TILDE = 9 // UNARY_TILDE ~ + PLUS = 10 // + + MINUS = 11 // - + MULTI = 12 // * + DIV = 13 // / + INTEGER_DIV = 14 // Div + MOD = 15 // % + CONCAT = 16 // || + AND = 17 + OR = 18 + XOR = 19 + NOT = 20 + CAST = 21 + BIT_CAST = 22 + IS = 23 + ISNOT = 24 + ISNULL = 25 + ISNOTNULL = 26 + ISTRUE = 27 + ISNOTTRUE = 28 + ISFALSE = 29 + ISNOTFALSE = 30 + ISEMPTY = 31 + NOT_IN_ROWS = 32 + OP_BIT_AND = 33 // & + OP_BIT_OR = 34 // | + OP_BIT_XOR = 35 // ^ + OP_BIT_SHIFT_LEFT = 36 // << + OP_BIT_SHIFT_RIGHT = 37 // >> + + ABS = 38 + ACOS = 39 + ADDDATE = 40 + ADDTIME = 41 + AES_DECRYPT = 42 + AES_ENCRYPT = 43 + ANY_VALUE = 44 + APPROX_COUNT = 45 + ARRAY_AGG = 46 + ARRAY_APPEND = 47 + ARRAY_CAT = 48 + ARRAY_CONTAINS = 49 + ARRAY_POSITION = 50 + ARRAY_SIZE = 51 + ASCII = 52 + ASIN = 53 + ASSERT = 54 + ATAN = 55 + ATAN2 = 56 + AVG = 57 + AVG_TW_CACHE = 58 + AVG_TW_RESULT = 59 + BASE64_DECODE = 60 + BASE64_ENCODE = 61 + BIT_AND = 62 + BIT_LENGTH = 63 + BIT_NOT = 64 + BIT_OR = 65 + BIT_XOR = 66 + BITAGG_AND = 67 + BITAGG_OR = 68 + BOOLAGG_AND = 69 + BOOLAGG_OR = 70 + CASE = 71 + CEIL = 72 + CHR = 73 + COALESCE = 74 + FIELD = 75 + CONCAT_WS = 76 + CONTAINS = 77 + CORR = 78 + COS = 79 + COT = 80 + CRC32 = 81 + COUNT = 82 + COUNT_IF = 83 + COVAR_POP = 84 + COVAR_SAMPLE = 85 + CONVERT_TZ = 86 + CUME_DIST = 87 + CURRENT_DATE = 88 + CURRENT_TIMESTAMP = 89 + DATE_FROM_PARTS = 90 + DATE_PART = 91 + DATEADD = 92 + DATEDIFF = 93 + TIMEDIFF = 94 + TIMESTAMPDIFF = 95 + DENSE_RANK = 96 + MO_WIN_DIVISOR = 97 + EMPTY = 98 + ENDSWITH = 99 + EXP = 100 + FINDINSET = 101 + FIRST_VALUE = 102 + FLOOR = 103 + GREATEST = 104 + GROUPING = 105 + HASH = 106 + HASH_AGG = 107 + HEX_DECODE = 108 + HEX_ENCODE = 109 + HEX = 110 + UNHEX = 111 + MD5 = 112 + IFF = 113 + IFNULL = 114 + ILIKE = 115 + ILIKE_ALL = 116 + ILIKE_ANY = 117 + IN = 118 + LAG = 119 + LAST_VALUE = 120 + LEAD = 121 + LEAST = 122 + LEFT = 123 + LENGTH = 124 + LENGTH_UTF8 = 125 + LIKE = 126 + LIKE_ALL = 127 + LIKE_ANY = 128 + LN = 129 + NOT_IN = 130 + LOG = 131 + LOG2 = 132 + LOG10 = 133 + LOWER = 134 + LPAD = 135 + LTRIM = 136 + MAX = 137 + MEDIAN = 138 + MIN = 139 + MODE = 140 + MONTH = 141 + NORMAL = 142 + NTH_VALUE = 143 + NTILE = 144 + NULLIF = 145 + PERCENT_RANK = 146 + PI = 147 + POSITION = 148 + POW = 149 + PREFIX_EQ = 150 + PREFIX_IN = 151 + PREFIX_BETWEEN = 152 + RADIAN = 153 + RANDOM = 154 + RANK = 155 + REGEXP = 156 + REGEXP_INSTR = 157 + REGEXP_LIKE = 158 + REGEXP_REPLACE = 159 + REGEXP_SUBSTR = 160 + REG_MATCH = 161 + NOT_REG_MATCH = 162 + REPEAT = 163 + REPLACE = 164 + REVERSE = 165 + RIGHT = 166 + ROUND = 167 + ROW_NUMBER = 168 + RPAD = 169 + RTRIM = 170 + SIGN = 171 + SIN = 172 + SINH = 173 + SPACE = 174 + SPLIT = 175 + SPLIT_PART = 176 + SQRT = 177 + STARCOUNT = 178 + STARTSWITH = 179 + STDDEV_POP = 180 + STDDEV_SAMPLE = 181 + SUBSTR = 182 + SUM = 183 + SYSDATE = 184 + GROUP_CONCAT = 185 + TAN = 186 + TO_DATE = 187 + STR_TO_DATE = 188 + TO_INTERVAL = 189 + TRANSLATE = 190 + TRIM = 191 + UNIFORM = 192 + SHA1 = 193 + SHA2 = 194 + UTC_TIMESTAMP = 195 + UNIX_TIMESTAMP = 196 + FROM_UNIXTIME = 197 + UPPER = 198 + VAR_POP = 199 + VAR_SAMPLE = 200 // Date and Time functions - LAST_DAY - MAKEDATE - - DATE - TIME - DAY - DAYOFYEAR - INTERVAL - EXTRACT - OCT - SUBSTRING - ENCODE - DECODE - TO_BASE64 - FROM_BASE64 - SUBSTRING_INDEX - WEEK - WEEKDAY - YEAR - HOUR - MINUTE - SECOND - TO_DAYS - TO_SECONDS - - DATE_ADD - DATE_SUB - APPROX_COUNT_DISTINCT - - LOAD_FILE - SAVE_FILE + LAST_DAY = 201 + MAKEDATE = 202 + + DATE = 203 + TIME = 204 + DAY = 205 + DAYOFYEAR = 206 + INTERVAL = 207 + EXTRACT = 208 + OCT = 209 + SUBSTRING = 210 + ENCODE = 211 + DECODE = 212 + TO_BASE64 = 213 + FROM_BASE64 = 214 + SUBSTRING_INDEX = 215 + WEEK = 216 + WEEKDAY = 217 + YEAR = 218 + HOUR = 219 + MINUTE = 220 + SECOND = 221 + TO_DAYS = 222 + TO_SECONDS = 223 + + DATE_ADD = 224 + DATE_SUB = 225 + APPROX_COUNT_DISTINCT = 226 + + LOAD_FILE = 227 + SAVE_FILE = 228 //information functions //Reference to : https://dev.mysql.com/doc/refman/8.0/en/information-functions.html - DATABASE - USER - CONNECTION_ID - CHARSET - CONVERT - CURRENT_ROLE - FOUND_ROWS - ICULIBVERSION - LAST_INSERT_ID - LAST_QUERY_ID - LAST_UUID - ROLES_GRAPHML - ROW_COUNT - VERSION - COLLATION - CURRENT_ACCOUNT_ID - CURRENT_ACCOUNT_NAME - CURRENT_ROLE_ID - CURRENT_ROLE_NAME - CURRENT_USER_ID - CURRENT_USER_NAME - - TIMESTAMP - DATE_FORMAT - JSON_EXTRACT - JSON_EXTRACT_STRING - JSON_EXTRACT_FLOAT64 - JSON_QUOTE - JSON_UNQUOTE - JSON_ROW - - JSON_SET - JSON_INSERT - JSON_REPLACE - - JQ - TRY_JQ - WASM - TRY_WASM - FORMAT - SLEEP - INSTR - LOCATE - - UUID - SERIAL - SERIAL_FULL - SERIAL_EXTRACT - BIN - - ENABLE_FAULT_INJECTION - DISABLE_FAULT_INJECTION - ADD_FAULT_POINT // Add a fault point - REMOVE_FAULT_POINT // Remove - TRIGGER_FAULT_POINT // Trigger. - MO_WIN_TRUNCATE - - MO_MEMORY_USAGE // Dump memory usage - MO_ENABLE_MEMORY_USAGE_DETAIL - MO_DISABLE_MEMORY_USAGE_DETAIL + DATABASE = 229 + USER = 230 + CONNECTION_ID = 231 + CHARSET = 232 + CONVERT = 233 + CURRENT_ROLE = 234 + FOUND_ROWS = 235 + ICULIBVERSION = 236 + LAST_INSERT_ID = 237 + LAST_QUERY_ID = 238 + LAST_UUID = 239 + ROLES_GRAPHML = 240 + ROW_COUNT = 241 + VERSION = 242 + COLLATION = 243 + CURRENT_ACCOUNT_ID = 244 + CURRENT_ACCOUNT_NAME = 245 + CURRENT_ROLE_ID = 246 + CURRENT_ROLE_NAME = 247 + CURRENT_USER_ID = 248 + CURRENT_USER_NAME = 249 + + TIMESTAMP = 250 + DATE_FORMAT = 251 + JSON_EXTRACT = 252 + JSON_EXTRACT_STRING = 253 + JSON_EXTRACT_FLOAT64 = 254 + JSON_QUOTE = 255 + JSON_UNQUOTE = 256 + JSON_ROW = 257 + + JQ = 258 + TRY_JQ = 259 + WASM = 260 + TRY_WASM = 261 + FORMAT = 262 + SLEEP = 263 + INSTR = 264 + LOCATE = 265 + + UUID = 266 + SERIAL = 267 + SERIAL_FULL = 268 + SERIAL_EXTRACT = 269 + BIN = 270 + + ENABLE_FAULT_INJECTION = 271 + DISABLE_FAULT_INJECTION = 272 + ADD_FAULT_POINT = 273 // Add a fault point + REMOVE_FAULT_POINT = 274 // Remove + TRIGGER_FAULT_POINT = 275 // Trigger. + MO_WIN_TRUNCATE = 276 + + MO_MEMORY_USAGE = 277 // Dump memory usage + MO_ENABLE_MEMORY_USAGE_DETAIL = 278 + MO_DISABLE_MEMORY_USAGE_DETAIL = 279 // MO_CTL is used to check some internal status, and issue some ctl commands to the service. // see builtin.ctl.ctl.go to get detail. - MO_CTL + MO_CTL = 280 - MO_SHOW_VISIBLE_BIN // parse type/onUpdate/default []byte to visible string - MO_SHOW_VISIBLE_BIN_ENUM // parse type/onUpdate/default []byte to visible string for enum - MO_SHOW_COL_UNIQUE // show column whether unique key + MO_SHOW_VISIBLE_BIN = 281 // parse type/onUpdate/default []byte to visible string + MO_SHOW_VISIBLE_BIN_ENUM = 282 // parse type/onUpdate/default []byte to visible string for enum + MO_SHOW_COL_UNIQUE = 283 // show column whether unique key - MO_TABLE_ROWS // table rows - MO_TABLE_SIZE // table size - MO_TABLE_COL_MAX // table column max value - MO_TABLE_COL_MIN // table column min value + MO_TABLE_ROWS = 284 // table rows + MO_TABLE_SIZE = 285 // table size + MO_TABLE_COL_MAX = 286 // table column max value + MO_TABLE_COL_MIN = 287 // table column min value - MO_LOG_DATE // parse date from string, like __mo_filepath - PURGE_LOG // purge mo internal log, like rawlog, statement_info, metric - MO_ADMIN_NAME // get mo admin name of account - MO_CU - MO_CU_V1 - MO_EXPLAIN_PHY + MO_LOG_DATE = 288 // parse date from string, like __mo_filepath + PURGE_LOG = 289 // purge mo internal log, like rawlog, statement_info, metric + MO_ADMIN_NAME = 290 // get mo admin name of account + MO_CU = 291 + MO_CU_V1 = 292 + MO_EXPLAIN_PHY = 293 - GIT_VERSION - BUILD_VERSION + GIT_VERSION = 294 + BUILD_VERSION = 295 // be used: insert into t1 values(1,1) on duplicate key update a=values(a)+a+1 - VALUES - BINARY - INTERNAL_CHAR_LENGTH - INTERNAL_CHAR_SIZE - INTERNAL_NUMERIC_PRECISION - INTERNAL_NUMERIC_SCALE - INTERNAL_DATETIME_SCALE - INTERNAL_COLUMN_CHARACTER_SET - INTERNAL_AUTO_INCREMENT + VALUES = 296 + BINARY = 297 + INTERNAL_CHAR_LENGTH = 298 + INTERNAL_CHAR_SIZE = 299 + INTERNAL_NUMERIC_PRECISION = 300 + INTERNAL_NUMERIC_SCALE = 301 + INTERNAL_DATETIME_SCALE = 302 + INTERNAL_COLUMN_CHARACTER_SET = 303 + INTERNAL_AUTO_INCREMENT = 304 // be used: enum - CAST_INDEX_TO_VALUE - CAST_VALUE_TO_INDEX - CAST_INDEX_VALUE_TO_INDEX + CAST_INDEX_TO_VALUE = 305 + CAST_VALUE_TO_INDEX = 306 + CAST_INDEX_VALUE_TO_INDEX = 307 // be used: show snapshots - CAST_NANO_TO_TIMESTAMP + CAST_NANO_TO_TIMESTAMP = 308 // be used: show pitr - CAST_RANGE_VALUE_UNIT + CAST_RANGE_VALUE_UNIT = 309 //Sequence function - NEXTVAL - SETVAL - CURRVAL - LASTVAL + NEXTVAL = 310 + SETVAL = 311 + CURRVAL = 312 + LASTVAL = 313 // Array Function - SUMMATION - L1_NORM // L1_NORMALIZATION - L2_NORM // L2 NORMALIZATION - INNER_PRODUCT - COSINE_SIMILARITY - VECTOR_DIMS //VECTOR DIMENSIONS - NORMALIZE_L2 //NORMALIZE L2 - L2_DISTANCE //L2_DISTANCE - L2_DISTANCE_SQ //L2_DISTANCE_SQ - COSINE_DISTANCE //COSINE_DISTANCE - CLUSTER_CENTERS // CLUSTER_CENTERS - SUB_VECTOR // SUB_VECTOR - - PYTHON_UDF + SUMMATION = 314 + L1_NORM = 315 // L1_NORMALIZATION + L2_NORM = 316 // L2 NORMALIZATION + INNER_PRODUCT = 317 + COSINE_SIMILARITY = 318 + VECTOR_DIMS = 319 //VECTOR DIMENSIONS + NORMALIZE_L2 = 320 //NORMALIZE L2 + L2_DISTANCE = 321 //L2_DISTANCE + L2_DISTANCE_SQ = 322 //L2_DISTANCE_SQ + COSINE_DISTANCE = 323 //COSINE_DISTANCE + CLUSTER_CENTERS = 324 // CLUSTER_CENTERS + SUB_VECTOR = 325 // SUB_VECTOR + + PYTHON_UDF = 326 // observation function - MO_CPU - MO_MEMORY - MO_CPU_DUMP + MO_CPU = 327 + MO_MEMORY = 328 + MO_CPU_DUMP = 329 // bitmap function - BITMAP_BIT_POSITION - BITMAP_BUCKET_NUMBER - BITMAP_COUNT - BITMAP_CONSTRUCT_AGG - BITMAP_OR_AGG + BITMAP_BIT_POSITION = 330 + BITMAP_BUCKET_NUMBER = 331 + BITMAP_COUNT = 332 + BITMAP_CONSTRUCT_AGG = 333 + BITMAP_OR_AGG = 334 // fulltext function - FULLTEXT_MATCH - FULLTEXT_MATCH_SCORE + FULLTEXT_MATCH = 335 + FULLTEXT_MATCH_SCORE = 336 + + JSON_SET = 337 + JSON_INSERT = 338 + JSON_REPLACE = 339 // fault inject function - FAULT_INJECT + FAULT_INJECT = 340 // FUNCTION_END_NUMBER is not a function, just a flag to record the max number of function. // TODO: every one should put the new function id in front of this one if you want to make a new function. - FUNCTION_END_NUMBER + FUNCTION_END_NUMBER = 341 ) // functionIdRegister is what function we have registered already. diff --git a/pkg/sql/plan/function/function_id_test.go b/pkg/sql/plan/function/function_id_test.go new file mode 100644 index 0000000000000..aa5f30e93a23f --- /dev/null +++ b/pkg/sql/plan/function/function_id_test.go @@ -0,0 +1,409 @@ +// Copyright 2021 - 2022 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 ( + "testing" + + "github.com/stretchr/testify/require" +) + +// all fixed function ids defined at 2024-12-12 +var predefinedFunids = map[int]int{ + EQUAL: 0, + NOT_EQUAL: 1, + GREAT_THAN: 2, + GREAT_EQUAL: 3, + LESS_THAN: 4, + LESS_EQUAL: 5, + BETWEEN: 6, + UNARY_PLUS: 7, + UNARY_MINUS: 8, + UNARY_TILDE: 9, + PLUS: 10, + MINUS: 11, + MULTI: 12, + DIV: 13, + INTEGER_DIV: 14, + MOD: 15, + CONCAT: 16, + AND: 17, + OR: 18, + XOR: 19, + NOT: 20, + CAST: 21, + BIT_CAST: 22, + IS: 23, + ISNOT: 24, + ISNULL: 25, + ISNOTNULL: 26, + ISTRUE: 27, + ISNOTTRUE: 28, + ISFALSE: 29, + ISNOTFALSE: 30, + ISEMPTY: 31, + NOT_IN_ROWS: 32, + OP_BIT_AND: 33, + OP_BIT_OR: 34, + OP_BIT_XOR: 35, + OP_BIT_SHIFT_LEFT: 36, + OP_BIT_SHIFT_RIGHT: 37, + + ABS: 38, + ACOS: 39, + ADDDATE: 40, + ADDTIME: 41, + AES_DECRYPT: 42, + AES_ENCRYPT: 43, + ANY_VALUE: 44, + APPROX_COUNT: 45, + ARRAY_AGG: 46, + ARRAY_APPEND: 47, + ARRAY_CAT: 48, + ARRAY_CONTAINS: 49, + ARRAY_POSITION: 50, + ARRAY_SIZE: 51, + ASCII: 52, + ASIN: 53, + ASSERT: 54, + ATAN: 55, + ATAN2: 56, + AVG: 57, + AVG_TW_CACHE: 58, + AVG_TW_RESULT: 59, + BASE64_DECODE: 60, + BASE64_ENCODE: 61, + BIT_AND: 62, + BIT_LENGTH: 63, + BIT_NOT: 64, + BIT_OR: 65, + BIT_XOR: 66, + BITAGG_AND: 67, + BITAGG_OR: 68, + BOOLAGG_AND: 69, + BOOLAGG_OR: 70, + CASE: 71, + CEIL: 72, + CHR: 73, + COALESCE: 74, + FIELD: 75, + CONCAT_WS: 76, + CONTAINS: 77, + CORR: 78, + COS: 79, + COT: 80, + CRC32: 81, + COUNT: 82, + COUNT_IF: 83, + COVAR_POP: 84, + COVAR_SAMPLE: 85, + CONVERT_TZ: 86, + CUME_DIST: 87, + CURRENT_DATE: 88, + CURRENT_TIMESTAMP: 89, + DATE_FROM_PARTS: 90, + DATE_PART: 91, + DATEADD: 92, + DATEDIFF: 93, + TIMEDIFF: 94, + TIMESTAMPDIFF: 95, + DENSE_RANK: 96, + MO_WIN_DIVISOR: 97, + EMPTY: 98, + ENDSWITH: 99, + EXP: 100, + FINDINSET: 101, + FIRST_VALUE: 102, + FLOOR: 103, + GREATEST: 104, + GROUPING: 105, + HASH: 106, + HASH_AGG: 107, + HEX_DECODE: 108, + HEX_ENCODE: 109, + HEX: 110, + UNHEX: 111, + MD5: 112, + IFF: 113, + IFNULL: 114, + ILIKE: 115, + ILIKE_ALL: 116, + ILIKE_ANY: 117, + IN: 118, + LAG: 119, + LAST_VALUE: 120, + LEAD: 121, + LEAST: 122, + LEFT: 123, + LENGTH: 124, + LENGTH_UTF8: 125, + LIKE: 126, + LIKE_ALL: 127, + LIKE_ANY: 128, + LN: 129, + NOT_IN: 130, + LOG: 131, + LOG2: 132, + LOG10: 133, + LOWER: 134, + LPAD: 135, + LTRIM: 136, + MAX: 137, + MEDIAN: 138, + MIN: 139, + MODE: 140, + MONTH: 141, + NORMAL: 142, + NTH_VALUE: 143, + NTILE: 144, + NULLIF: 145, + PERCENT_RANK: 146, + PI: 147, + POSITION: 148, + POW: 149, + PREFIX_EQ: 150, + PREFIX_IN: 151, + PREFIX_BETWEEN: 152, + RADIAN: 153, + RANDOM: 154, + RANK: 155, + REGEXP: 156, + REGEXP_INSTR: 157, + REGEXP_LIKE: 158, + REGEXP_REPLACE: 159, + REGEXP_SUBSTR: 160, + REG_MATCH: 161, + NOT_REG_MATCH: 162, + REPEAT: 163, + REPLACE: 164, + REVERSE: 165, + RIGHT: 166, + ROUND: 167, + ROW_NUMBER: 168, + RPAD: 169, + RTRIM: 170, + SIGN: 171, + SIN: 172, + SINH: 173, + SPACE: 174, + SPLIT: 175, + SPLIT_PART: 176, + SQRT: 177, + STARCOUNT: 178, + STARTSWITH: 179, + STDDEV_POP: 180, + STDDEV_SAMPLE: 181, + SUBSTR: 182, + SUM: 183, + SYSDATE: 184, + GROUP_CONCAT: 185, + TAN: 186, + TO_DATE: 187, + STR_TO_DATE: 188, + TO_INTERVAL: 189, + TRANSLATE: 190, + TRIM: 191, + UNIFORM: 192, + SHA1: 193, + SHA2: 194, + UTC_TIMESTAMP: 195, + UNIX_TIMESTAMP: 196, + FROM_UNIXTIME: 197, + UPPER: 198, + VAR_POP: 199, + VAR_SAMPLE: 200, + LAST_DAY: 201, + MAKEDATE: 202, + DATE: 203, + TIME: 204, + DAY: 205, + DAYOFYEAR: 206, + INTERVAL: 207, + EXTRACT: 208, + OCT: 209, + SUBSTRING: 210, + ENCODE: 211, + DECODE: 212, + TO_BASE64: 213, + FROM_BASE64: 214, + SUBSTRING_INDEX: 215, + WEEK: 216, + WEEKDAY: 217, + YEAR: 218, + HOUR: 219, + MINUTE: 220, + SECOND: 221, + TO_DAYS: 222, + TO_SECONDS: 223, + + DATE_ADD: 224, + DATE_SUB: 225, + APPROX_COUNT_DISTINCT: 226, + + LOAD_FILE: 227, + SAVE_FILE: 228, + DATABASE: 229, + USER: 230, + CONNECTION_ID: 231, + CHARSET: 232, + CONVERT: 233, + CURRENT_ROLE: 234, + FOUND_ROWS: 235, + ICULIBVERSION: 236, + LAST_INSERT_ID: 237, + LAST_QUERY_ID: 238, + LAST_UUID: 239, + ROLES_GRAPHML: 240, + ROW_COUNT: 241, + VERSION: 242, + COLLATION: 243, + CURRENT_ACCOUNT_ID: 244, + CURRENT_ACCOUNT_NAME: 245, + CURRENT_ROLE_ID: 246, + CURRENT_ROLE_NAME: 247, + CURRENT_USER_ID: 248, + CURRENT_USER_NAME: 249, + + TIMESTAMP: 250, + DATE_FORMAT: 251, + JSON_EXTRACT: 252, + JSON_EXTRACT_STRING: 253, + JSON_EXTRACT_FLOAT64: 254, + JSON_QUOTE: 255, + JSON_UNQUOTE: 256, + JSON_ROW: 257, + + JQ: 258, + TRY_JQ: 259, + WASM: 260, + TRY_WASM: 261, + FORMAT: 262, + SLEEP: 263, + INSTR: 264, + LOCATE: 265, + + UUID: 266, + SERIAL: 267, + SERIAL_FULL: 268, + SERIAL_EXTRACT: 269, + BIN: 270, + + ENABLE_FAULT_INJECTION: 271, + DISABLE_FAULT_INJECTION: 272, + ADD_FAULT_POINT: 273, + REMOVE_FAULT_POINT: 274, + TRIGGER_FAULT_POINT: 275, + MO_WIN_TRUNCATE: 276, + + MO_MEMORY_USAGE: 277, + MO_ENABLE_MEMORY_USAGE_DETAIL: 278, + MO_DISABLE_MEMORY_USAGE_DETAIL: 279, + + MO_CTL: 280, + + MO_SHOW_VISIBLE_BIN: 281, + MO_SHOW_VISIBLE_BIN_ENUM: 282, + MO_SHOW_COL_UNIQUE: 283, + + MO_TABLE_ROWS: 284, + MO_TABLE_SIZE: 285, + MO_TABLE_COL_MAX: 286, + MO_TABLE_COL_MIN: 287, + + MO_LOG_DATE: 288, + PURGE_LOG: 289, + MO_ADMIN_NAME: 290, + MO_CU: 291, + MO_CU_V1: 292, + MO_EXPLAIN_PHY: 293, + + GIT_VERSION: 294, + BUILD_VERSION: 295, + + VALUES: 296, + BINARY: 297, + INTERNAL_CHAR_LENGTH: 298, + INTERNAL_CHAR_SIZE: 299, + INTERNAL_NUMERIC_PRECISION: 300, + INTERNAL_NUMERIC_SCALE: 301, + INTERNAL_DATETIME_SCALE: 302, + INTERNAL_COLUMN_CHARACTER_SET: 303, + INTERNAL_AUTO_INCREMENT: 304, + + CAST_INDEX_TO_VALUE: 305, + CAST_VALUE_TO_INDEX: 306, + CAST_INDEX_VALUE_TO_INDEX: 307, + + CAST_NANO_TO_TIMESTAMP: 308, + CAST_RANGE_VALUE_UNIT: 309, + + NEXTVAL: 310, + SETVAL: 311, + CURRVAL: 312, + LASTVAL: 313, + + SUMMATION: 314, + L1_NORM: 315, + L2_NORM: 316, + INNER_PRODUCT: 317, + COSINE_SIMILARITY: 318, + VECTOR_DIMS: 319, + NORMALIZE_L2: 320, + L2_DISTANCE: 321, + L2_DISTANCE_SQ: 322, + COSINE_DISTANCE: 323, + CLUSTER_CENTERS: 324, + SUB_VECTOR: 325, + + PYTHON_UDF: 326, + + MO_CPU: 327, + MO_MEMORY: 328, + MO_CPU_DUMP: 329, + + BITMAP_BIT_POSITION: 330, + BITMAP_BUCKET_NUMBER: 331, + BITMAP_COUNT: 332, + BITMAP_CONSTRUCT_AGG: 333, + BITMAP_OR_AGG: 334, + + FULLTEXT_MATCH: 335, + FULLTEXT_MATCH_SCORE: 336, + + JSON_SET: 337, + JSON_INSERT: 338, + JSON_REPLACE: 339, + + FAULT_INJECT: 340, + FUNCTION_END_NUMBER: 341, +} + +func Test_funids(t *testing.T) { + check := func(fid int) { + if _, ok := predefinedFunids[fid]; !ok { + require.Failf(t, "no functionid in 'predefinedFunids'", "put funtion id %d into 'predefinedFunids'", fid) + } else { + require.Equal(t, fid, predefinedFunids[fid]) + } + require.Greater(t, FUNCTION_END_NUMBER, fid) + } + for _, fun := range allSupportedFunctions { + check(fun.functionId) + } + + for _, fid := range functionIdRegister { + check(int(fid)) + } +} From e57f0c5b1c12fa9c298bcea6d9fd9d9301b9aeb5 Mon Sep 17 00:00:00 2001 From: YANGGMM Date: Fri, 20 Dec 2024 00:03:04 +0800 Subject: [PATCH 04/26] support restore dropped account from pitr (#20803) support restore dropped account from pitr Approved by: @daviszhen --- pkg/frontend/pitr.go | 126 ++++---- pkg/frontend/pitr_test.go | 360 +++++++++++++++++++++++ pkg/frontend/snapshot.go | 18 +- pkg/frontend/snapshot_restore_with_ts.go | 70 ++--- pkg/frontend/snapshot_test.go | 32 +- 5 files changed, 477 insertions(+), 129 deletions(-) diff --git a/pkg/frontend/pitr.go b/pkg/frontend/pitr.go index ff558e8283124..2219fb8c6a40b 100644 --- a/pkg/frontend/pitr.go +++ b/pkg/frontend/pitr.go @@ -891,11 +891,12 @@ func doRestorePitr(ctx context.Context, ses *Session, stmt *tree.RestorePitr) (s }() var ( - restoreLevel tree.RestoreLevel - ts int64 - pitrExist bool - sortedFkTbls []string - fkTableMap map[string]*tableInfo + restoreLevel tree.RestoreLevel + ts int64 + pitrExist bool + sortedFkTbls []string + fkTableMap map[string]*tableInfo + accountRecord *accountRecord ) // resolve timestamp ts, err = doResolveTimeStamp(stmt.TimeStamp) @@ -909,6 +910,9 @@ func doRestorePitr(ctx context.Context, ses *Session, stmt *tree.RestorePitr) (s dbName := string(stmt.DatabaseName) tblName := string(stmt.TableName) + isClusterRestore := false + isNeedToCleanToDatabase := true + // restore as a txn if err = bh.Exec(ctx, "begin;"); err != nil { return stats, err @@ -953,37 +957,61 @@ func doRestorePitr(ctx context.Context, ses *Session, stmt *tree.RestorePitr) (s restoreOtherAccount := func() (rtnErr error) { fromAccount := string(stmt.SrcAccountName) var ( - fromAccountId uint32 - toAccountId uint32 + toAccountId uint32 ) if len(fromAccount) == 0 { + // using account level pitr fromAccount = pitr.accountName - fromAccountId = uint32(pitr.accountId) + accountRecord, rtnErr = getAccountRecordByTs(ctx, ses, bh, pitrName, ts, fromAccount) + if rtnErr != nil { + return + } if fromAccount == accountName { // restore to the same account - toAccountId = fromAccountId + getLogger(ses.GetService()).Info("restore to the same account", zap.String("fromAccount", accountName), zap.String("toAccount", accountName)) + toAccountId, rtnErr = getAccountId(ctx, bh, accountName) + if rtnErr != nil { + // need create a new account + if rtnErr = createDroppedAccount(ctx, ses, bh, pitrName, *accountRecord); rtnErr != nil { + return + } + + if toAccountId, rtnErr = getAccountId(ctx, bh, accountRecord.accountName); rtnErr != nil { + return + } + } } else { // restore to new account + getLogger(ses.GetService()).Info("restore to the same account", zap.String("fromAccount", fromAccount), zap.String("toAccount", accountName)) toAccountId, rtnErr = getAccountId(ctx, bh, accountName) if rtnErr != nil { - return rtnErr + return } } } else { + // using cluster level pitr + accountRecord, rtnErr = getAccountRecordByTs(ctx, ses, bh, pitrName, ts, fromAccount) + if rtnErr != nil { + return + } if fromAccount == accountName { // restore to the same account - fromAccountId, rtnErr = getAccountId(ctx, bh, fromAccount) + getLogger(ses.GetService()).Info("restore to the same account", zap.String("fromAccount", accountName), zap.String("toAccount", accountName)) + toAccountId, rtnErr = getAccountId(ctx, bh, fromAccount) if rtnErr != nil { - return rtnErr + // need create a new account + if rtnErr = createDroppedAccount(ctx, ses, bh, pitrName, *accountRecord); rtnErr != nil { + return + } + + if toAccountId, rtnErr = getAccountId(ctx, bh, accountRecord.accountName); rtnErr != nil { + return + } } - toAccountId = fromAccountId } else { // restore to new account - fromAccountId, rtnErr = getAccountId(ctx, bh, fromAccount) - if rtnErr != nil { - return rtnErr - } + getLogger(ses.GetService()).Info("restore to the same account", zap.String("fromAccount", fromAccount), zap.String("toAccount", accountName)) toAccountId, rtnErr = getAccountId(ctx, bh, accountName) if rtnErr != nil { return rtnErr @@ -992,62 +1020,22 @@ func doRestorePitr(ctx context.Context, ses *Session, stmt *tree.RestorePitr) (s } // check account exists or not - var accountExist bool - if accountExist, rtnErr = doCheckAccountExistsInPitrRestore(ctx, ses.GetService(), bh, pitrName, ts, fromAccount, ses.GetAccountId()); rtnErr != nil { - return rtnErr - } - if !accountExist { - return moerr.NewInternalErrorf(ctx, "account `%s` does not exists at timestamp: %v", fromAccount, nanoTimeFormat(ts)) - } - // mock snapshot - var snapshotName string - snapshotName, rtnErr = insertSnapshotRecord(ctx, ses.GetService(), bh, pitrName, ts, uint64(fromAccountId), fromAccount) - defer func() { - deleteSnapshotRecord(ctx, ses.GetService(), bh, pitrName, snapshotName) - }() - if rtnErr != nil { - return rtnErr - } - - restoreAccount := fromAccountId - // drop foreign key related tables first - rtnErr = deleteCurFkTables(ctx, ses.GetService(), bh, dbName, tblName, toAccountId) - if err != nil { - return - } - - // get topo sorted tables with foreign key - sortedFkTbls, rtnErr = fkTablesTopoSort(ctx, bh, snapshotName, dbName, tblName) - if rtnErr != nil { - return - } - - // get foreign key table infos - fkTableMap, rtnErr = getTableInfoMap(ctx, ses.GetService(), bh, snapshotName, dbName, tblName, sortedFkTbls) - if rtnErr != nil { - return - } - - // collect views and tables during table restoration - viewMap := make(map[string]*tableInfo) - - rtnErr = restoreToAccount(ctx, ses.GetService(), bh, snapshotName, toAccountId, fkTableMap, viewMap, ts, restoreAccount, false, nil) + rtnErr = restoreAccountUsingClusterSnapshotToNew( + ctx, + ses, + bh, + pitrName, + ts, + *accountRecord, + uint64(toAccountId), + nil, + isClusterRestore, + isNeedToCleanToDatabase, + ) if rtnErr != nil { return rtnErr } - if len(fkTableMap) > 0 { - if rtnErr = restoreTablesWithFk(ctx, ses.GetService(), bh, snapshotName, sortedFkTbls, fkTableMap, toAccountId, ts); rtnErr != nil { - return - } - } - - if len(viewMap) > 0 { - if rtnErr = restoreViews(ctx, ses, bh, snapshotName, viewMap, toAccountId); rtnErr != nil { - return - } - } - // checks if the given context has been canceled. if rtnErr = CancelCheck(ctx); rtnErr != nil { return } diff --git a/pkg/frontend/pitr_test.go b/pkg/frontend/pitr_test.go index ff27c5eec708f..d7bf4ba10ef38 100644 --- a/pkg/frontend/pitr_test.go +++ b/pkg/frontend/pitr_test.go @@ -3037,3 +3037,363 @@ func Test_restoreViewsWithPitr(t *testing.T) { assert.NoError(t, err) }) } + +func Test_RestoreOtherAccount(t *testing.T) { + convey.Convey("doRestorePitr fail", t, func() { + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + ses := newTestSession(t, ctrl) + defer ses.Close() + + bh := &backgroundExecTest{} + bh.init() + + bhStub := gostub.StubFunc(&NewBackgroundExec, bh) + defer bhStub.Reset() + + pu := config.NewParameterUnit(&config.FrontendParameters{}, nil, nil, nil) + pu.SV.SetDefaultValues() + pu.SV.KillRountinesInterval = 0 + setPu("", pu) + ctx := context.WithValue(context.TODO(), config.ParameterUnitKey, pu) + rm, _ := NewRoutineManager(ctx, "") + ses.rm = rm + + tenant := &TenantInfo{ + Tenant: sysAccountName, + User: rootName, + DefaultRole: moAdminRoleName, + TenantID: sysAccountID, + UserID: rootID, + DefaultRoleID: moAdminRoleID, + } + ses.SetTenantInfo(tenant) + + ts := time.Now().Add(time.Duration(-2) * time.Hour).UnixNano() + stmt := &tree.RestorePitr{ + Level: tree.RESTORELEVELACCOUNT, + Name: "pitr01", + + AccountName: "acc01", + TimeStamp: nanoTimeFormat(ts), + } + + ses.SetTenantInfo(tenant) + ctx = context.WithValue(ctx, defines.TenantIDKey{}, uint32(sysAccountID)) + + //no result set + bh.sql2result["begin;"] = nil + bh.sql2result["commit;"] = nil + bh.sql2result["rollback;"] = nil + + sql, err := getSqlForCheckPitr(ctx, "pitr01", sysAccountID) + assert.NoError(t, err) + mrs := newMrsForPitrRecord([][]interface{}{{"018ee4cd-5991-7caa-b75d-f9290144bd9f"}}) + bh.sql2result[sql] = mrs + + sql = "select * from mo_catalog.mo_pitr where pitr_name = 'pitr01' and create_account = 0" + mrs = newMrsForPitrRecord([][]interface{}{{ + "018ee4cd-5991-7caa-b75d-f9290144bd9f", + "pitr01", + uint64(0), + "2024-05-01 00:00:00", + "2024-05-01 00:00:00", + "ACCOUNT", + uint64(1), + "acc01", + "", + "", + uint64(1), + uint8(1), + "d", + }}) + bh.sql2result[sql] = mrs + + resovleTs, err := doResolveTimeStamp(stmt.TimeStamp) + assert.NoError(t, err) + + _, err = doRestorePitr(ctx, ses, stmt) + assert.Error(t, err) + + sql = fmt.Sprintf("select account_id, account_name, admin_name, comments from mo_catalog.mo_account {MO_TS = %d } where account_name = '%s';", resovleTs, "acc01") + mrs = newMrsForPitrRecord([][]interface{}{{uint64(1), "acc01", "root", ""}}) + bh.sql2result[sql] = mrs + + sql = "select account_id, account_name, status, version, suspended_time from mo_catalog.mo_account where 1=1 and account_name = 'acc01'" + mrs = newMrsForPitrRecord([][]interface{}{{uint64(1), "acc01", "open", uint64(1), nil}}) + bh.sql2result[sql] = mrs + + _, err = doRestorePitr(ctx, ses, stmt) + assert.Error(t, err) + }) + + convey.Convey("doRestorePitr fail", t, func() { + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + ses := newTestSession(t, ctrl) + defer ses.Close() + + bh := &backgroundExecTest{} + bh.init() + + bhStub := gostub.StubFunc(&NewBackgroundExec, bh) + defer bhStub.Reset() + + pu := config.NewParameterUnit(&config.FrontendParameters{}, nil, nil, nil) + pu.SV.SetDefaultValues() + pu.SV.KillRountinesInterval = 0 + setPu("", pu) + ctx := context.WithValue(context.TODO(), config.ParameterUnitKey, pu) + rm, _ := NewRoutineManager(ctx, "") + ses.rm = rm + + tenant := &TenantInfo{ + Tenant: sysAccountName, + User: rootName, + DefaultRole: moAdminRoleName, + TenantID: sysAccountID, + UserID: rootID, + DefaultRoleID: moAdminRoleID, + } + ses.SetTenantInfo(tenant) + + ts := time.Now().Add(time.Duration(-2) * time.Hour).UnixNano() + stmt := &tree.RestorePitr{ + Level: tree.RESTORELEVELACCOUNT, + Name: "pitr01", + + AccountName: "acc02", + TimeStamp: nanoTimeFormat(ts), + } + + ses.SetTenantInfo(tenant) + ctx = context.WithValue(ctx, defines.TenantIDKey{}, uint32(sysAccountID)) + + //no result set + bh.sql2result["begin;"] = nil + bh.sql2result["commit;"] = nil + bh.sql2result["rollback;"] = nil + + sql, err := getSqlForCheckPitr(ctx, "pitr01", sysAccountID) + assert.NoError(t, err) + mrs := newMrsForPitrRecord([][]interface{}{{"018ee4cd-5991-7caa-b75d-f9290144bd9f"}}) + bh.sql2result[sql] = mrs + + sql = "select * from mo_catalog.mo_pitr where pitr_name = 'pitr01' and create_account = 0" + mrs = newMrsForPitrRecord([][]interface{}{{ + "018ee4cd-5991-7caa-b75d-f9290144bd9f", + "pitr01", + uint64(0), + "2024-05-01 00:00:00", + "2024-05-01 00:00:00", + "ACCOUNT", + uint64(1), + "acc01", + "", + "", + uint64(1), + uint8(1), + "d", + }}) + bh.sql2result[sql] = mrs + + resovleTs, err := doResolveTimeStamp(stmt.TimeStamp) + assert.NoError(t, err) + + _, err = doRestorePitr(ctx, ses, stmt) + assert.Error(t, err) + + sql = fmt.Sprintf("select account_id, account_name, admin_name, comments from mo_catalog.mo_account {MO_TS = %d } where account_name = '%s';", resovleTs, "acc01") + mrs = newMrsForPitrRecord([][]interface{}{{uint64(1), "acc01", "root", ""}}) + bh.sql2result[sql] = mrs + + sql = "select account_id, account_name, status, version, suspended_time from mo_catalog.mo_account where 1=1 and account_name = 'acc01'" + mrs = newMrsForPitrRecord([][]interface{}{{uint64(1), "acc01", "open", uint64(1), nil}}) + bh.sql2result[sql] = mrs + + _, err = doRestorePitr(ctx, ses, stmt) + assert.Error(t, err) + }) + + convey.Convey("doRestorePitr fail", t, func() { + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + ses := newTestSession(t, ctrl) + defer ses.Close() + + bh := &backgroundExecTest{} + bh.init() + + bhStub := gostub.StubFunc(&NewBackgroundExec, bh) + defer bhStub.Reset() + + pu := config.NewParameterUnit(&config.FrontendParameters{}, nil, nil, nil) + pu.SV.SetDefaultValues() + pu.SV.KillRountinesInterval = 0 + setPu("", pu) + ctx := context.WithValue(context.TODO(), config.ParameterUnitKey, pu) + rm, _ := NewRoutineManager(ctx, "") + ses.rm = rm + + tenant := &TenantInfo{ + Tenant: sysAccountName, + User: rootName, + DefaultRole: moAdminRoleName, + TenantID: sysAccountID, + UserID: rootID, + DefaultRoleID: moAdminRoleID, + } + ses.SetTenantInfo(tenant) + + ts := time.Now().Add(time.Duration(-2) * time.Hour).UnixNano() + stmt := &tree.RestorePitr{ + Level: tree.RESTORELEVELACCOUNT, + Name: "pitr01", + + AccountName: "acc01", + SrcAccountName: "acc01", + TimeStamp: nanoTimeFormat(ts), + } + + ses.SetTenantInfo(tenant) + ctx = context.WithValue(ctx, defines.TenantIDKey{}, uint32(sysAccountID)) + + //no result set + bh.sql2result["begin;"] = nil + bh.sql2result["commit;"] = nil + bh.sql2result["rollback;"] = nil + + sql, err := getSqlForCheckPitr(ctx, "pitr01", sysAccountID) + assert.NoError(t, err) + mrs := newMrsForPitrRecord([][]interface{}{{"018ee4cd-5991-7caa-b75d-f9290144bd9f"}}) + bh.sql2result[sql] = mrs + + sql = "select * from mo_catalog.mo_pitr where pitr_name = 'pitr01' and create_account = 0" + mrs = newMrsForPitrRecord([][]interface{}{{ + "018ee4cd-5991-7caa-b75d-f9290144bd9f", + "pitr01", + uint64(0), + "2024-05-01 00:00:00", + "2024-05-01 00:00:00", + "CLUSTER", + uint64(1), + "acc01", + "", + "", + uint64(1), + uint8(1), + "d", + }}) + bh.sql2result[sql] = mrs + + resovleTs, err := doResolveTimeStamp(stmt.TimeStamp) + assert.NoError(t, err) + + _, err = doRestorePitr(ctx, ses, stmt) + assert.Error(t, err) + + sql = fmt.Sprintf("select account_id, account_name, admin_name, comments from mo_catalog.mo_account {MO_TS = %d } where account_name = '%s';", resovleTs, "acc01") + mrs = newMrsForPitrRecord([][]interface{}{{uint64(1), "acc01", "root", ""}}) + bh.sql2result[sql] = mrs + + sql = "select account_id, account_name, status, version, suspended_time from mo_catalog.mo_account where 1=1 and account_name = 'acc01'" + mrs = newMrsForPitrRecord([][]interface{}{{uint64(1), "acc01", "open", uint64(1), nil}}) + bh.sql2result[sql] = mrs + + _, err = doRestorePitr(ctx, ses, stmt) + assert.Error(t, err) + }) + + convey.Convey("doRestorePitr fail", t, func() { + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + ses := newTestSession(t, ctrl) + defer ses.Close() + + bh := &backgroundExecTest{} + bh.init() + + bhStub := gostub.StubFunc(&NewBackgroundExec, bh) + defer bhStub.Reset() + + pu := config.NewParameterUnit(&config.FrontendParameters{}, nil, nil, nil) + pu.SV.SetDefaultValues() + pu.SV.KillRountinesInterval = 0 + setPu("", pu) + ctx := context.WithValue(context.TODO(), config.ParameterUnitKey, pu) + rm, _ := NewRoutineManager(ctx, "") + ses.rm = rm + + tenant := &TenantInfo{ + Tenant: sysAccountName, + User: rootName, + DefaultRole: moAdminRoleName, + TenantID: sysAccountID, + UserID: rootID, + DefaultRoleID: moAdminRoleID, + } + ses.SetTenantInfo(tenant) + + ts := time.Now().Add(time.Duration(-2) * time.Hour).UnixNano() + stmt := &tree.RestorePitr{ + Level: tree.RESTORELEVELACCOUNT, + Name: "pitr01", + + AccountName: "acc02", + SrcAccountName: "acc01", + TimeStamp: nanoTimeFormat(ts), + } + + ses.SetTenantInfo(tenant) + ctx = context.WithValue(ctx, defines.TenantIDKey{}, uint32(sysAccountID)) + + //no result set + bh.sql2result["begin;"] = nil + bh.sql2result["commit;"] = nil + bh.sql2result["rollback;"] = nil + + sql, err := getSqlForCheckPitr(ctx, "pitr01", sysAccountID) + assert.NoError(t, err) + mrs := newMrsForPitrRecord([][]interface{}{{"018ee4cd-5991-7caa-b75d-f9290144bd9f"}}) + bh.sql2result[sql] = mrs + + sql = "select * from mo_catalog.mo_pitr where pitr_name = 'pitr01' and create_account = 0" + mrs = newMrsForPitrRecord([][]interface{}{{ + "018ee4cd-5991-7caa-b75d-f9290144bd9f", + "pitr01", + uint64(0), + "2024-05-01 00:00:00", + "2024-05-01 00:00:00", + "CLUSTER", + uint64(1), + "acc01", + "", + "", + uint64(1), + uint8(1), + "d", + }}) + bh.sql2result[sql] = mrs + + resovleTs, err := doResolveTimeStamp(stmt.TimeStamp) + assert.NoError(t, err) + + _, err = doRestorePitr(ctx, ses, stmt) + assert.Error(t, err) + + sql = fmt.Sprintf("select account_id, account_name, admin_name, comments from mo_catalog.mo_account {MO_TS = %d } where account_name = '%s';", resovleTs, "acc01") + mrs = newMrsForPitrRecord([][]interface{}{{uint64(1), "acc01", "root", ""}}) + bh.sql2result[sql] = mrs + + sql = "select account_id, account_name, status, version, suspended_time from mo_catalog.mo_account where 1=1 and account_name = 'acc01'" + mrs = newMrsForPitrRecord([][]interface{}{{uint64(1), "acc01", "open", uint64(1), nil}}) + bh.sql2result[sql] = mrs + + _, err = doRestorePitr(ctx, ses, stmt) + assert.Error(t, err) + }) +} diff --git a/pkg/frontend/snapshot.go b/pkg/frontend/snapshot.go index 426b6c41ce731..d231cec7deba9 100644 --- a/pkg/frontend/snapshot.go +++ b/pkg/frontend/snapshot.go @@ -1706,7 +1706,7 @@ func restoreToCluster(ctx context.Context, return err } if newAccountId != uint32(account.accountId) { - if err = restoreAccountUsingClusterSnapshotToNew(ctx, ses, bh, snapshotName, snapshotTs, account, subDbToRestore, uint64(newAccountId), true, true); err != nil { + if err = restoreAccountUsingClusterSnapshotToNew(ctx, ses, bh, snapshotName, snapshotTs, account, uint64(newAccountId), subDbToRestore, true, true); err != nil { return err } } else { @@ -1739,7 +1739,7 @@ func restoreToCluster(ctx context.Context, } // 2.0 restore droped account to new account - err = restoreAccountUsingClusterSnapshotToNew(ctx, ses, bh, snapshotName, snapshotTs, account, subDbToRestore, uint64(newAccountId), true, false) + err = restoreAccountUsingClusterSnapshotToNew(ctx, ses, bh, snapshotName, snapshotTs, account, uint64(newAccountId), subDbToRestore, true, false) if err != nil { return err } @@ -1794,7 +1794,7 @@ func restoreToAccountUsingCluster( } } - err = restoreAccountUsingClusterSnapshotToNew(ctx, ses, bh, snapshotName, snapshotTs, *ar, nil, uint64(toAccountId), false, isNeedToCleanToDatabase) + err = restoreAccountUsingClusterSnapshotToNew(ctx, ses, bh, snapshotName, snapshotTs, *ar, uint64(toAccountId), nil, false, isNeedToCleanToDatabase) if err != nil { return err } @@ -1966,8 +1966,8 @@ func restoreAccountUsingClusterSnapshotToNew(ctx context.Context, snapshotName string, snapshotTs int64, account accountRecord, - subDbToRestore map[string]*subDbRestoreRecord, toAccountId uint64, + subDbToRestore map[string]*subDbRestoreRecord, isRestoreCluster bool, isNeedToCleanToDatabase bool, ) (err error) { @@ -1985,12 +1985,12 @@ func restoreAccountUsingClusterSnapshotToNew(ctx context.Context, // get topo sorted tables with foreign key var sortedFkTbls []string var fkTableMap map[string]*tableInfo - sortedFkTbls, err = fkTablesTopoSortWithDropped(ctx, bh, "", "", snapshotTs, uint32(fromAccount), uint32(toAccountId)) + sortedFkTbls, err = fkTablesTopoSortWithTS(ctx, bh, "", "", snapshotTs, uint32(fromAccount), uint32(toAccountId)) if err != nil { return err } // get foreign key table infos - fkTableMap, err = getTableInfoMapFromDropped(ctx, ses.GetService(), bh, "", "", sortedFkTbls, snapshotTs, uint32(fromAccount), uint32(toAccountId)) + fkTableMap, err = getTableInfoMapFromTS(ctx, ses.GetService(), bh, "", "", sortedFkTbls, snapshotTs, uint32(fromAccount), uint32(toAccountId)) if err != nil { return err } @@ -1999,7 +1999,7 @@ func restoreAccountUsingClusterSnapshotToNew(ctx context.Context, viewMap := make(map[string]*tableInfo) // restore to account - if err = restoreToAccountFromDropped( + if err = restoreToAccountFromTS( ctx, ses.GetService(), bh, @@ -2014,7 +2014,7 @@ func restoreAccountUsingClusterSnapshotToNew(ctx context.Context, } if len(fkTableMap) > 0 { - if err = restoreTablesWithFkFromDropped(ctx, + if err = restoreTablesWithFkFromTS(ctx, ses.GetService(), bh, snapshotTs, @@ -2027,7 +2027,7 @@ func restoreAccountUsingClusterSnapshotToNew(ctx context.Context, } if len(viewMap) > 0 { - if err = restoreViewsFromDropped( + if err = restoreViewsFromTS( ctx, ses, bh, diff --git a/pkg/frontend/snapshot_restore_with_ts.go b/pkg/frontend/snapshot_restore_with_ts.go index e8e6deafcfda6..f30aff087a415 100644 --- a/pkg/frontend/snapshot_restore_with_ts.go +++ b/pkg/frontend/snapshot_restore_with_ts.go @@ -29,11 +29,11 @@ import ( "github.com/matrixorigin/matrixone/pkg/sql/plan" ) -func fkTablesTopoSortWithDropped(ctx context.Context, bh BackgroundExec, dbName string, tblName string, ts int64, from, to uint32) (sortedTbls []string, err error) { +func fkTablesTopoSortWithTS(ctx context.Context, bh BackgroundExec, dbName string, tblName string, ts int64, from, to uint32) (sortedTbls []string, err error) { newCtx := defines.AttachAccountId(ctx, from) getLogger("").Info(fmt.Sprintf("[%d:%d] start to get fk tables topo sort from account %d", from, ts, from)) // get foreign key deps from mo_catalog.mo_foreign_keys - fkDeps, err := getFkDepsWithDropped(newCtx, bh, dbName, tblName, ts, from, to) + fkDeps, err := getFkDepsWithTS(newCtx, bh, dbName, tblName, ts, from, to) if err != nil { return } @@ -52,7 +52,7 @@ func fkTablesTopoSortWithDropped(ctx context.Context, bh BackgroundExec, dbName return } -func getFkDepsWithDropped(ctx context.Context, bh BackgroundExec, db string, tbl string, ts int64, from, to uint32) (ans map[string][]string, err error) { +func getFkDepsWithTS(ctx context.Context, bh BackgroundExec, db string, tbl string, ts int64, from, to uint32) (ans map[string][]string, err error) { sql := "select db_name, table_name, refer_db_name, refer_table_name from mo_catalog.mo_foreign_keys" if ts > 0 { sql += fmt.Sprintf(" {MO_TS = %d}", ts) @@ -103,7 +103,7 @@ func getFkDepsWithDropped(ctx context.Context, bh BackgroundExec, db string, tbl return } -func getTableInfoMapFromDropped( +func getTableInfoMapFromTS( ctx context.Context, sid string, bh BackgroundExec, @@ -135,14 +135,14 @@ func getTableInfoMapFromDropped( continue } - if tblInfoMap[key], err = getTableInfoFromDropped(newCtx, sid, bh, d, t, ts, from, to); err != nil { + if tblInfoMap[key], err = getTableInfoFromTS(newCtx, sid, bh, d, t, ts, from, to); err != nil { return } } return } -func getTableInfoFromDropped(ctx context.Context, +func getTableInfoFromTS(ctx context.Context, sid string, bh BackgroundExec, dbName, @@ -151,7 +151,7 @@ func getTableInfoFromDropped(ctx context.Context, from, to uint32) (*tableInfo, error) { getLogger(sid).Info(fmt.Sprintf("[%d:%d]start to get table info: datatabse `%s`, table `%s`", from, ts, dbName, tblName)) - tableInfos, err := getTableInfosFromDropped(ctx, sid, bh, dbName, tblName, ts, from, to) + tableInfos, err := getTableInfosFromTS(ctx, sid, bh, dbName, tblName, ts, from, to) if err != nil { return nil, err } @@ -163,7 +163,7 @@ func getTableInfoFromDropped(ctx context.Context, return tableInfos[0], nil } -func getTableInfosFromDropped(ctx context.Context, +func getTableInfosFromTS(ctx context.Context, sid string, bh BackgroundExec, dbName string, @@ -173,7 +173,7 @@ func getTableInfosFromDropped(ctx context.Context, to uint32) ([]*tableInfo, error) { newCtx := defines.AttachAccountId(ctx, from) getLogger(sid).Info(fmt.Sprintf("[%d:%d] start to get table info: datatabse `%s`, table `%s`", from, ts, dbName, tblName)) - tableInfos, err := showFullTablesFromDropped(newCtx, sid, bh, dbName, tblName, ts, from, to) + tableInfos, err := showFullTablesFromTS(newCtx, sid, bh, dbName, tblName, ts, from, to) if err != nil { return nil, err } @@ -181,7 +181,7 @@ func getTableInfosFromDropped(ctx context.Context, // only recreate snapshoted table need create sql if ts > 0 { for _, tblInfo := range tableInfos { - if tblInfo.createSql, err = getCreateTableSqlFromDropped(newCtx, bh, dbName, tblInfo.tblName, ts, from, to); err != nil { + if tblInfo.createSql, err = getCreateTableSqlFromTS(newCtx, bh, dbName, tblInfo.tblName, ts, from, to); err != nil { return nil, err } } @@ -189,7 +189,7 @@ func getTableInfosFromDropped(ctx context.Context, return tableInfos, nil } -func showFullTablesFromDropped(ctx context.Context, +func showFullTablesFromTS(ctx context.Context, sid string, bh BackgroundExec, dbName string, @@ -211,7 +211,7 @@ func showFullTablesFromDropped(ctx context.Context, getLogger(sid).Info(fmt.Sprintf("[%d:%d] show full table `%s.%s` sql: %s", from, ts, dbName, tblName, sql)) // cols: table name, table type - colsList, err := getStringColsListFromDropped(newCtx, bh, sql, from, to, 0, 1) + colsList, err := getStringColsListFromTS(newCtx, bh, sql, from, to, 0, 1) if err != nil { return nil, err } @@ -228,7 +228,7 @@ func showFullTablesFromDropped(ctx context.Context, return ans, nil } -func getStringColsListFromDropped(ctx context.Context, bh BackgroundExec, sql string, from, to uint32, colIndices ...uint64) (ans [][]string, err error) { +func getStringColsListFromTS(ctx context.Context, bh BackgroundExec, sql string, from, to uint32, colIndices ...uint64) (ans [][]string, err error) { bh.ClearExecResultSet() if err = bh.ExecRestore(ctx, sql, from, to); err != nil { return @@ -253,7 +253,7 @@ func getStringColsListFromDropped(ctx context.Context, bh BackgroundExec, sql st return } -func getCreateTableSqlFromDropped(ctx context.Context, bh BackgroundExec, dbName string, tblName string, ts int64, from, to uint32) (string, error) { +func getCreateTableSqlFromTS(ctx context.Context, bh BackgroundExec, dbName string, tblName string, ts int64, from, to uint32) (string, error) { getLogger("").Info(fmt.Sprintf("[%d:%d] start to get create table sql: datatabse `%s`, table `%s`", from, ts, dbName, tblName)) newCtx := defines.AttachAccountId(ctx, from) sql := fmt.Sprintf("show create table `%s`.`%s`", dbName, tblName) @@ -262,7 +262,7 @@ func getCreateTableSqlFromDropped(ctx context.Context, bh BackgroundExec, dbName } // cols: table_name, create_sql - colsList, err := getStringColsListFromDropped(newCtx, bh, sql, from, to, 1) + colsList, err := getStringColsListFromTS(newCtx, bh, sql, from, to, 1) if err != nil { return "", err } @@ -272,7 +272,7 @@ func getCreateTableSqlFromDropped(ctx context.Context, bh BackgroundExec, dbName return colsList[0][0], nil } -func restoreToAccountFromDropped( +func restoreToAccountFromTS( ctx context.Context, sid string, bh BackgroundExec, @@ -312,7 +312,7 @@ func restoreToAccountFromDropped( } // restore dbs - if dbNames, err = showDatabasesFromDropped(ctx, sid, bh, snapshotTs, restoreAccount, toAccountId); err != nil { + if dbNames, err = showDatabasesFromTS(ctx, sid, bh, snapshotTs, restoreAccount, toAccountId); err != nil { return } @@ -321,7 +321,7 @@ func restoreToAccountFromDropped( getLogger(sid).Info(fmt.Sprintf("[%d:%d] skip restore db: %v", restoreAccount, snapshotTs, dbName)) continue } - if err = restoreDatabaseFromDropped(ctx, + if err = restoreDatabaseFromTS(ctx, sid, bh, dbName, @@ -337,7 +337,7 @@ func restoreToAccountFromDropped( } // restore system db - if err = restoreSystemDatabaseFromDropped(ctx, + if err = restoreSystemDatabaseFromTS(ctx, sid, bh, snapshotTs, @@ -349,7 +349,7 @@ func restoreToAccountFromDropped( return } -func showDatabasesFromDropped(ctx context.Context, sid string, bh BackgroundExec, ts int64, from, to uint32) ([]string, error) { +func showDatabasesFromTS(ctx context.Context, sid string, bh BackgroundExec, ts int64, from, to uint32) ([]string, error) { getLogger(sid).Info(fmt.Sprintf("[%d:%d] start to get all database ", from, ts)) newCtx := defines.AttachAccountId(ctx, from) sql := "show databases" @@ -358,7 +358,7 @@ func showDatabasesFromDropped(ctx context.Context, sid string, bh BackgroundExec } // cols: dbname - colsList, err := getStringColsListFromDropped(newCtx, bh, sql, from, to, 0) + colsList, err := getStringColsListFromTS(newCtx, bh, sql, from, to, 0) if err != nil { return nil, err } @@ -370,7 +370,7 @@ func showDatabasesFromDropped(ctx context.Context, sid string, bh BackgroundExec return dbNames, nil } -func restoreDatabaseFromDropped( +func restoreDatabaseFromTS( ctx context.Context, sid string, bh BackgroundExec, @@ -386,7 +386,7 @@ func restoreDatabaseFromDropped( var createDbSql string var isSubDb bool - createDbSql, err = getCreateDatabaseSqlFromDropped(ctx, sid, bh, dbName, snapshotTs, restoreAccount, toAccountId) + createDbSql, err = getCreateDatabaseSqlFromTS(ctx, sid, bh, dbName, snapshotTs, restoreAccount, toAccountId) if err != nil { return } @@ -452,7 +452,7 @@ func restoreDatabaseFromDropped( } } - tableInfos, err := getTableInfosFromDropped(ctx, sid, bh, dbName, "", snapshotTs, restoreAccount, toAccountId) + tableInfos, err := getTableInfosFromTS(ctx, sid, bh, dbName, "", snapshotTs, restoreAccount, toAccountId) if err != nil { return } @@ -476,7 +476,7 @@ func restoreDatabaseFromDropped( return } - if err = recreateTableFromDropped(ctx, + if err = recreateTableFromTS(ctx, sid, bh, tblInfo, @@ -489,7 +489,7 @@ func restoreDatabaseFromDropped( return } -func getCreateDatabaseSqlFromDropped(ctx context.Context, +func getCreateDatabaseSqlFromTS(ctx context.Context, sid string, bh BackgroundExec, dbName string, @@ -506,7 +506,7 @@ func getCreateDatabaseSqlFromDropped(ctx context.Context, getLogger(sid).Info(fmt.Sprintf("[%d:%d] get create database `%s` sql: %s", from, ts, dbName, sql)) // cols: database_name, create_sql - colsList, err := getStringColsListFromDropped(newCtx, bh, sql, from, to, 0, 1) + colsList, err := getStringColsListFromTS(newCtx, bh, sql, from, to, 0, 1) if err != nil { return "", err } @@ -516,7 +516,7 @@ func getCreateDatabaseSqlFromDropped(ctx context.Context, return colsList[0][1], nil } -func recreateTableFromDropped( +func recreateTableFromTS( ctx context.Context, sid string, bh BackgroundExec, @@ -553,7 +553,7 @@ func recreateTableFromDropped( return } -func restoreSystemDatabaseFromDropped( +func restoreSystemDatabaseFromTS( ctx context.Context, sid string, bh BackgroundExec, @@ -568,7 +568,7 @@ func restoreSystemDatabaseFromDropped( tableInfos []*tableInfo ) - tableInfos, err = showFullTablesFromDropped(ctx, sid, bh, dbName, "", snapshotTs, restoreAccount, toAccountId) + tableInfos, err = showFullTablesFromTS(ctx, sid, bh, dbName, "", snapshotTs, restoreAccount, toAccountId) if err != nil { return err } @@ -581,7 +581,7 @@ func restoreSystemDatabaseFromDropped( } getLogger(sid).Info(fmt.Sprintf("[%d:%d] start to restore system table: %v.%v", restoreAccount, snapshotTs, moCatalog, tblInfo.tblName)) - tblInfo.createSql, err = getCreateTableSqlFromDropped(ctx, bh, dbName, tblInfo.tblName, snapshotTs, restoreAccount, toAccountId) + tblInfo.createSql, err = getCreateTableSqlFromTS(ctx, bh, dbName, tblInfo.tblName, snapshotTs, restoreAccount, toAccountId) if err != nil { return err } @@ -591,14 +591,14 @@ func restoreSystemDatabaseFromDropped( return } - if err = recreateTableFromDropped(ctx, sid, bh, tblInfo, snapshotTs, restoreAccount, toAccountId); err != nil { + if err = recreateTableFromTS(ctx, sid, bh, tblInfo, snapshotTs, restoreAccount, toAccountId); err != nil { return } } return } -func restoreTablesWithFkFromDropped( +func restoreTablesWithFkFromTS( ctx context.Context, sid string, bh BackgroundExec, @@ -616,7 +616,7 @@ func restoreTablesWithFkFromDropped( // e.g. t1.pk <- t2.fk, we only want to restore t2, fkTableMap[t1.key] is nil, ignore t1 if tblInfo := fkTableMap[key]; tblInfo != nil { getLogger(sid).Info(fmt.Sprintf("[%d:%d] start to restore table with fk: %v, restore timestamp: %d", restoreAccount, snapshotTs, tblInfo.tblName, snapshotTs)) - err = recreateTableFromDropped(ctx, sid, bh, tblInfo, snapshotTs, restoreAccount, toAccountId) + err = recreateTableFromTS(ctx, sid, bh, tblInfo, snapshotTs, restoreAccount, toAccountId) if err != nil { return } @@ -625,7 +625,7 @@ func restoreTablesWithFkFromDropped( return } -func restoreViewsFromDropped( +func restoreViewsFromTS( ctx context.Context, ses *Session, bh BackgroundExec, diff --git a/pkg/frontend/snapshot_test.go b/pkg/frontend/snapshot_test.go index fa31572b0a5db..30f52bd31ae25 100644 --- a/pkg/frontend/snapshot_test.go +++ b/pkg/frontend/snapshot_test.go @@ -26,8 +26,8 @@ import ( "github.com/smartystreets/goconvey/convey" ) -func Test_fkTablesTopoSortWithDropped(t *testing.T) { - convey.Convey("fkTablesTopoSortWithDropped ", t, func() { +func Test_fkTablesTopoSortWithTS(t *testing.T) { + convey.Convey("fkTablesTopoSortWithTS ", t, func() { ctrl := gomock.NewController(t) defer ctrl.Finish() @@ -59,26 +59,26 @@ func Test_fkTablesTopoSortWithDropped(t *testing.T) { ctx = context.WithValue(ctx, defines.TenantIDKey{}, uint32(sysAccountID)) - _, err := fkTablesTopoSortWithDropped(ctx, bh, "", "", 0, 0, 0) + _, err := fkTablesTopoSortWithTS(ctx, bh, "", "", 0, 0, 0) convey.So(err, convey.ShouldNotBeNil) sql := "select db_name, table_name, refer_db_name, refer_table_name from mo_catalog.mo_foreign_keys" mrs := newMrsForPitrRecord([][]interface{}{}) bh.sql2result[sql] = mrs - _, err = fkTablesTopoSortWithDropped(ctx, bh, "", "", 0, 0, 0) + _, err = fkTablesTopoSortWithTS(ctx, bh, "", "", 0, 0, 0) convey.So(err, convey.ShouldBeNil) sql = "select db_name, table_name, refer_db_name, refer_table_name from mo_catalog.mo_foreign_keys" mrs = newMrsForPitrRecord([][]interface{}{{"db1", "table1", "db2", "table2"}}) bh.sql2result[sql] = mrs - _, err = fkTablesTopoSortWithDropped(ctx, bh, "", "", 0, 0, 0) + _, err = fkTablesTopoSortWithTS(ctx, bh, "", "", 0, 0, 0) convey.So(err, convey.ShouldBeNil) }) } -func Test_getFkDepsWithDropped(t *testing.T) { - convey.Convey("getFkDepsWithDropped ", t, func() { +func Test_getFkDepsWithTS(t *testing.T) { + convey.Convey("getFkDepsWithTS ", t, func() { ctrl := gomock.NewController(t) defer ctrl.Finish() @@ -110,49 +110,49 @@ func Test_getFkDepsWithDropped(t *testing.T) { ctx = context.WithValue(ctx, defines.TenantIDKey{}, uint32(sysAccountID)) - _, err := getFkDepsWithDropped(ctx, bh, "", "", 0, 0, 0) + _, err := getFkDepsWithTS(ctx, bh, "", "", 0, 0, 0) convey.So(err, convey.ShouldNotBeNil) sql := "select db_name, table_name, refer_db_name, refer_table_name from mo_catalog.mo_foreign_keys" mrs := newMrsForPitrRecord([][]interface{}{}) bh.sql2result[sql] = mrs - _, err = getFkDepsWithDropped(ctx, bh, "", "", 0, 0, 0) + _, err = getFkDepsWithTS(ctx, bh, "", "", 0, 0, 0) convey.So(err, convey.ShouldBeNil) sql = "select db_name, table_name, refer_db_name, refer_table_name from mo_catalog.mo_foreign_keys" mrs = newMrsForPitrRecord([][]interface{}{{"db1", "table1", "db2", "table2"}}) bh.sql2result[sql] = mrs - _, err = getFkDepsWithDropped(ctx, bh, "", "", 0, 0, 0) + _, err = getFkDepsWithTS(ctx, bh, "", "", 0, 0, 0) convey.So(err, convey.ShouldBeNil) sql = "select db_name, table_name, refer_db_name, refer_table_name from mo_catalog.mo_foreign_keys" mrs = newMrsForPitrRecord([][]interface{}{{types.Day_Hour, "table1", "db2", "table2"}}) bh.sql2result[sql] = mrs - _, err = getFkDepsWithDropped(ctx, bh, "", "", 0, 0, 0) + _, err = getFkDepsWithTS(ctx, bh, "", "", 0, 0, 0) convey.So(err, convey.ShouldNotBeNil) sql = "select db_name, table_name, refer_db_name, refer_table_name from mo_catalog.mo_foreign_keys" mrs = newMrsForPitrRecord([][]interface{}{{"db1", types.Day_Hour, "db2", "table2"}}) bh.sql2result[sql] = mrs - _, err = getFkDepsWithDropped(ctx, bh, "", "", 0, 0, 0) + _, err = getFkDepsWithTS(ctx, bh, "", "", 0, 0, 0) convey.So(err, convey.ShouldNotBeNil) sql = "select db_name, table_name, refer_db_name, refer_table_name from mo_catalog.mo_foreign_keys" mrs = newMrsForPitrRecord([][]interface{}{{"db1", "table1", types.Day_Hour, "table2"}}) bh.sql2result[sql] = mrs - _, err = getFkDepsWithDropped(ctx, bh, "", "", 0, 0, 0) + _, err = getFkDepsWithTS(ctx, bh, "", "", 0, 0, 0) convey.So(err, convey.ShouldNotBeNil) sql = "select db_name, table_name, refer_db_name, refer_table_name from mo_catalog.mo_foreign_keys" mrs = newMrsForPitrRecord([][]interface{}{{"db1", "table1", "db2", types.Day_Hour}}) bh.sql2result[sql] = mrs - _, err = getFkDepsWithDropped(ctx, bh, "", "", 0, 0, 0) + _, err = getFkDepsWithTS(ctx, bh, "", "", 0, 0, 0) convey.So(err, convey.ShouldNotBeNil) }) } @@ -190,14 +190,14 @@ func Test_restoreAccountUsingClusterSnapshotToNew(t *testing.T) { ctx = context.WithValue(ctx, defines.TenantIDKey{}, uint32(sysAccountID)) - err := restoreAccountUsingClusterSnapshotToNew(ctx, ses, bh, "sp01", 0, accountRecord{accountName: "sys", accountId: 0}, nil, 0, false, false) + err := restoreAccountUsingClusterSnapshotToNew(ctx, ses, bh, "sp01", 0, accountRecord{accountName: "sys", accountId: 0}, 0, nil, false, false) convey.So(err, convey.ShouldNotBeNil) sql := "select db_name, table_name, refer_db_name, refer_table_name from mo_catalog.mo_foreign_keys" mrs := newMrsForPitrRecord([][]interface{}{{"db1", "table1", "db2", "table2"}}) bh.sql2result[sql] = mrs - err = restoreAccountUsingClusterSnapshotToNew(ctx, ses, bh, "sp01", 0, accountRecord{accountName: "sys", accountId: 0}, nil, 0, false, false) + err = restoreAccountUsingClusterSnapshotToNew(ctx, ses, bh, "sp01", 0, accountRecord{accountName: "sys", accountId: 0}, 0, nil, false, false) convey.So(err, convey.ShouldNotBeNil) }) } From 696ef351986f14110cf9237ec65672c8b50b8172 Mon Sep 17 00:00:00 2001 From: Wei Ziran Date: Fri, 20 Dec 2024 01:06:02 +0800 Subject: [PATCH 05/26] fix panic (#20840) fix panic Approved by: @XuPeng-SH --- pkg/vm/engine/tae/tasks/base.go | 2 +- pkg/vm/engine/tae/tasks/base_test.go | 118 +++++++++++++++++++++++++++ 2 files changed, 119 insertions(+), 1 deletion(-) create mode 100644 pkg/vm/engine/tae/tasks/base_test.go diff --git a/pkg/vm/engine/tae/tasks/base.go b/pkg/vm/engine/tae/tasks/base.go index 302f156a85899..88079cc5dd0f6 100644 --- a/pkg/vm/engine/tae/tasks/base.go +++ b/pkg/vm/engine/tae/tasks/base.go @@ -83,7 +83,7 @@ func (task *BaseTask) onDone(_ base.IOp) { common.ErrorField(task.Err)) } func (task *BaseTask) Type() TaskType { return task.taskType } -func (task *BaseTask) Cancel() (err error) { panic("todo") } +func (task *BaseTask) Cancel() (err error) { return nil } func (task *BaseTask) ID() uint64 { return task.id } func (task *BaseTask) Execute() (err error) { if task.exec != nil { diff --git a/pkg/vm/engine/tae/tasks/base_test.go b/pkg/vm/engine/tae/tasks/base_test.go new file mode 100644 index 0000000000000..76f42795c6086 --- /dev/null +++ b/pkg/vm/engine/tae/tasks/base_test.go @@ -0,0 +1,118 @@ +// Copyright 2024 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 tasks + +import ( + "context" + iops "github.com/matrixorigin/matrixone/pkg/vm/engine/tae/tasks/ops/base" + "github.com/stretchr/testify/require" + "testing" + "time" +) + +type testTaskImpl struct{} + +func (t testTaskImpl) OnExec(ctx context.Context) error { + //TODO implement me + panic("implement me") +} + +func (t testTaskImpl) SetError(err error) { + //TODO implement me + panic("implement me") +} + +func (t testTaskImpl) GetError() error { + //TODO implement me + panic("implement me") +} + +func (t testTaskImpl) WaitDone(ctx context.Context) error { + //TODO implement me + panic("implement me") +} + +func (t testTaskImpl) Waitable() bool { + //TODO implement me + panic("implement me") +} + +func (t testTaskImpl) GetCreateTime() time.Time { + //TODO implement me + panic("implement me") +} + +func (t testTaskImpl) GetStartTime() time.Time { + //TODO implement me + panic("implement me") +} + +func (t testTaskImpl) GetEndTime() time.Time { + //TODO implement me + panic("implement me") +} + +func (t testTaskImpl) GetExecutTime() int64 { + //TODO implement me + panic("implement me") +} + +func (t testTaskImpl) AddObserver(observer iops.Observer) { + //TODO implement me + panic("implement me") +} + +func (t testTaskImpl) ID() uint64 { + //TODO implement me + panic("implement me") +} + +func (t testTaskImpl) Type() TaskType { + //TODO implement me + panic("implement me") +} + +func (t testTaskImpl) Cancel() error { + //TODO implement me + panic("implement me") +} + +func (t testTaskImpl) Name() string { + //TODO implement me + panic("implement me") +} + +func (t testTaskImpl) PreExecute() error { + //TODO implement me + panic("implement me") +} + +func (t testTaskImpl) Execute(ctx context.Context) error { + //TODO implement me + panic("implement me") +} + +func (t testTaskImpl) PostExecute() error { + //TODO implement me + panic("implement me") +} + +func TestBaseTask(t *testing.T) { + task := NewBaseTask(testTaskImpl{}, 0, nil) + cancelFunc := func() { + require.NoError(t, task.Cancel()) + } + require.NotPanics(t, cancelFunc) +} From e271f9bab6f2e9a396ae400c489720c3246be034 Mon Sep 17 00:00:00 2001 From: chenmingsong <59043531+m-schen@users.noreply.github.com> Date: Fri, 20 Dec 2024 05:20:10 +0800 Subject: [PATCH 06/26] optimize the Group operator. (#20818) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## What type of PR is this? - [ ] API-change - [ ] BUG - [x] Improvement - [ ] Documentation - [ ] Feature - [ ] Test and CI - [ ] Code Refactoring ## Which issue(s) this PR fixes: https://github.com/matrixorigin/matrixone/issues/20766 ## What this PR does / why we need it: 1. 当Group算子只需要计算分组聚合的中间结果时,每当group count达到8192 * 4行发送一次。 2. 补充Group算子的result preextend能力。 3. 调整 Group算子的 AppendBatch策略。 --------- Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com> Co-authored-by: fengttt --- pkg/sql/colexec/aggexec/fromBytesRetBytes.go | 24 ++-- pkg/sql/colexec/aggexec/fromBytesRetFixed.go | 24 ++-- pkg/sql/colexec/aggexec/fromFixedRetBytes.go | 24 ++-- pkg/sql/colexec/aggexec/fromFixedRetFixed.go | 25 ++-- pkg/sql/colexec/aggexec/result.go | 16 +-- pkg/sql/colexec/aggexec/result_test.go | 37 ++++++ pkg/sql/colexec/group/exec.go | 119 ++++++++++++------- pkg/sql/colexec/group/exec_test.go | 2 + pkg/sql/colexec/group/execctx.go | 52 ++++++-- pkg/sql/colexec/group/execctx_test.go | 49 ++++++++ 10 files changed, 250 insertions(+), 122 deletions(-) create mode 100644 pkg/sql/colexec/group/execctx_test.go diff --git a/pkg/sql/colexec/aggexec/fromBytesRetBytes.go b/pkg/sql/colexec/aggexec/fromBytesRetBytes.go index a3d66a18626b9..3a0c78c212d65 100644 --- a/pkg/sql/colexec/aggexec/fromBytesRetBytes.go +++ b/pkg/sql/colexec/aggexec/fromBytesRetBytes.go @@ -224,12 +224,11 @@ func (exec *aggregatorFromBytesToBytes) BulkFill( } exec.arg.prepare(vectors[0]) - bs := exec.ret.getEmptyListOnX(x) if exec.arg.w.WithAnyNullValue() { for i, j := uint64(0), uint64(length); i < j; i++ { v, null := exec.arg.w.GetStrValue(i) if !null { - if err := exec.fill(groupContext, commonContext, v, bs[y], getter, setter); err != nil { + if err := exec.fill(groupContext, commonContext, v, exec.ret.bsFromEmptyList[x][y], getter, setter); err != nil { return err } exec.ret.setGroupNotEmpty(x, y) @@ -240,7 +239,7 @@ func (exec *aggregatorFromBytesToBytes) BulkFill( for i, j := uint64(0), uint64(length); i < j; i++ { v, _ := exec.arg.w.GetStrValue(i) - if err := exec.fill(groupContext, commonContext, v, bs[y], getter, setter); err != nil { + if err := exec.fill(groupContext, commonContext, v, exec.ret.bsFromEmptyList[x][y], getter, setter); err != nil { return err } exec.ret.setGroupNotEmpty(x, y) @@ -271,13 +270,12 @@ func (exec *aggregatorFromBytesToBytes) distinctBulkFill( return err } - bs := exec.ret.getEmptyListOnX(x) if exec.arg.w.WithAnyNullValue() { for i, j := uint64(0), uint64(length); i < j; i++ { if needs[i] { v, null := exec.arg.w.GetStrValue(i) if !null { - if err = exec.fill(groupContext, commonContext, v, bs[y], getter, setter); err != nil { + if err = exec.fill(groupContext, commonContext, v, exec.ret.bsFromEmptyList[x][y], getter, setter); err != nil { return err } exec.ret.setGroupNotEmpty(x, y) @@ -290,7 +288,7 @@ func (exec *aggregatorFromBytesToBytes) distinctBulkFill( for i, j := uint64(0), uint64(length); i < j; i++ { if needs[i] { v, _ := exec.arg.w.GetStrValue(i) - if err = exec.fill(groupContext, commonContext, v, bs[y], getter, setter); err != nil { + if err = exec.fill(groupContext, commonContext, v, exec.ret.bsFromEmptyList[x][y], getter, setter); err != nil { return err } exec.ret.setGroupNotEmpty(x, y) @@ -312,7 +310,6 @@ func (exec *aggregatorFromBytesToBytes) BatchFill( getter := exec.ret.get setter := exec.ret.set commonContext := exec.execContext.getCommonContext() - bs := exec.ret.getEmptyList() if vectors[0].IsConst() { value := vectors[0].GetBytesAt(0) @@ -321,7 +318,7 @@ func (exec *aggregatorFromBytesToBytes) BatchFill( idx := int(group - 1) x, y := exec.ret.updateNextAccessIdx(idx) if err := exec.fill( - exec.execContext.getGroupContext(idx), commonContext, value, bs[x][y], getter, setter); err != nil { + exec.execContext.getGroupContext(idx), commonContext, value, exec.ret.bsFromEmptyList[x][y], getter, setter); err != nil { return err } exec.ret.setGroupNotEmpty(x, y) @@ -339,7 +336,7 @@ func (exec *aggregatorFromBytesToBytes) BatchFill( groupIdx := int(groups[idx] - 1) x, y := exec.ret.updateNextAccessIdx(groupIdx) if err := exec.fill( - exec.execContext.getGroupContext(groupIdx), commonContext, v, bs[x][y], getter, setter); err != nil { + exec.execContext.getGroupContext(groupIdx), commonContext, v, exec.ret.bsFromEmptyList[x][y], getter, setter); err != nil { return err } exec.ret.setGroupNotEmpty(x, y) @@ -356,7 +353,7 @@ func (exec *aggregatorFromBytesToBytes) BatchFill( groupIdx := int(groups[idx] - 1) x, y := exec.ret.updateNextAccessIdx(groupIdx) if err := exec.fill( - exec.execContext.getGroupContext(groupIdx), commonContext, v, bs[x][y], getter, setter); err != nil { + exec.execContext.getGroupContext(groupIdx), commonContext, v, exec.ret.bsFromEmptyList[x][y], getter, setter); err != nil { return err } exec.ret.setGroupNotEmpty(x, y) @@ -371,7 +368,6 @@ func (exec *aggregatorFromBytesToBytes) distinctBatchFill( getter := exec.ret.get setter := exec.ret.set commonContext := exec.execContext.getCommonContext() - bs := exec.ret.getEmptyList() needs, err := exec.distinctHash.batchFill(vectors, offset, groups) if err != nil { @@ -385,7 +381,7 @@ func (exec *aggregatorFromBytesToBytes) distinctBatchFill( idx := int(group - 1) x, y := exec.ret.updateNextAccessIdx(idx) if err = exec.fill( - exec.execContext.getGroupContext(idx), commonContext, value, bs[x][y], getter, setter); err != nil { + exec.execContext.getGroupContext(idx), commonContext, value, exec.ret.bsFromEmptyList[x][y], getter, setter); err != nil { return err } exec.ret.setGroupNotEmpty(x, y) @@ -403,7 +399,7 @@ func (exec *aggregatorFromBytesToBytes) distinctBatchFill( groupIdx := int(groups[idx] - 1) x, y := exec.ret.updateNextAccessIdx(groupIdx) if err = exec.fill( - exec.execContext.getGroupContext(groupIdx), commonContext, v, bs[x][y], getter, setter); err != nil { + exec.execContext.getGroupContext(groupIdx), commonContext, v, exec.ret.bsFromEmptyList[x][y], getter, setter); err != nil { return err } exec.ret.setGroupNotEmpty(x, y) @@ -420,7 +416,7 @@ func (exec *aggregatorFromBytesToBytes) distinctBatchFill( groupIdx := int(groups[idx] - 1) x, y := exec.ret.updateNextAccessIdx(groupIdx) if err = exec.fill( - exec.execContext.getGroupContext(groupIdx), commonContext, v, bs[x][y], getter, setter); err != nil { + exec.execContext.getGroupContext(groupIdx), commonContext, v, exec.ret.bsFromEmptyList[x][y], getter, setter); err != nil { return err } exec.ret.setGroupNotEmpty(x, y) diff --git a/pkg/sql/colexec/aggexec/fromBytesRetFixed.go b/pkg/sql/colexec/aggexec/fromBytesRetFixed.go index a500ee2e77648..f557eff9824e5 100644 --- a/pkg/sql/colexec/aggexec/fromBytesRetFixed.go +++ b/pkg/sql/colexec/aggexec/fromBytesRetFixed.go @@ -322,12 +322,11 @@ func (exec *aggregatorFromBytesToFixed[to]) BulkFill( } exec.arg.prepare(vectors[0]) - bs := exec.ret.getEmptyListOnX(x) if exec.arg.w.WithAnyNullValue() { for i, j := uint64(0), uint64(length); i < j; i++ { v, null := exec.arg.w.GetStrValue(i) if !null { - if err := exec.fill(groupContext, commonContext, v, bs[y], getter, setter); err != nil { + if err := exec.fill(groupContext, commonContext, v, exec.ret.bsFromEmptyList[x][y], getter, setter); err != nil { return err } exec.ret.setGroupNotEmpty(x, y) @@ -338,7 +337,7 @@ func (exec *aggregatorFromBytesToFixed[to]) BulkFill( for i, j := uint64(0), uint64(length); i < j; i++ { v, _ := exec.arg.w.GetStrValue(i) - if err := exec.fill(groupContext, commonContext, v, bs[y], getter, setter); err != nil { + if err := exec.fill(groupContext, commonContext, v, exec.ret.bsFromEmptyList[x][y], getter, setter); err != nil { return err } exec.ret.setGroupNotEmpty(x, y) @@ -369,13 +368,12 @@ func (exec *aggregatorFromBytesToFixed[to]) distinctBulkFill( return err } - bs := exec.ret.getEmptyListOnX(x) if exec.arg.w.WithAnyNullValue() { for i, j := uint64(0), uint64(length); i < j; i++ { if needs[i] { v, null := exec.arg.w.GetStrValue(i) if !null { - if err = exec.fill(groupContext, commonContext, v, bs[y], getter, setter); err != nil { + if err = exec.fill(groupContext, commonContext, v, exec.ret.bsFromEmptyList[x][y], getter, setter); err != nil { return err } exec.ret.setGroupNotEmpty(x, y) @@ -388,7 +386,7 @@ func (exec *aggregatorFromBytesToFixed[to]) distinctBulkFill( for i, j := uint64(0), uint64(length); i < j; i++ { if needs[i] { v, _ := exec.arg.w.GetStrValue(i) - if err = exec.fill(groupContext, commonContext, v, bs[y], getter, setter); err != nil { + if err = exec.fill(groupContext, commonContext, v, exec.ret.bsFromEmptyList[x][y], getter, setter); err != nil { return err } exec.ret.setGroupNotEmpty(x, y) @@ -410,7 +408,6 @@ func (exec *aggregatorFromBytesToFixed[to]) BatchFill( getter := exec.ret.get setter := exec.ret.set commonContext := exec.execContext.getCommonContext() - bs := exec.ret.getEmptyList() if vectors[0].IsConst() { value := vectors[0].GetBytesAt(0) @@ -419,7 +416,7 @@ func (exec *aggregatorFromBytesToFixed[to]) BatchFill( idx := int(group - 1) x, y := exec.ret.updateNextAccessIdx(idx) if err := exec.fill( - exec.execContext.getGroupContext(idx), commonContext, value, bs[x][y], getter, setter); err != nil { + exec.execContext.getGroupContext(idx), commonContext, value, exec.ret.bsFromEmptyList[x][y], getter, setter); err != nil { return err } exec.ret.setGroupNotEmpty(x, y) @@ -437,7 +434,7 @@ func (exec *aggregatorFromBytesToFixed[to]) BatchFill( groupIdx := int(groups[idx] - 1) x, y := exec.ret.updateNextAccessIdx(groupIdx) if err := exec.fill( - exec.execContext.getGroupContext(groupIdx), commonContext, v, bs[x][y], getter, setter); err != nil { + exec.execContext.getGroupContext(groupIdx), commonContext, v, exec.ret.bsFromEmptyList[x][y], getter, setter); err != nil { return err } exec.ret.setGroupNotEmpty(x, y) @@ -454,7 +451,7 @@ func (exec *aggregatorFromBytesToFixed[to]) BatchFill( groupIdx := int(groups[idx] - 1) x, y := exec.ret.updateNextAccessIdx(groupIdx) if err := exec.fill( - exec.execContext.getGroupContext(groupIdx), commonContext, v, bs[x][y], getter, setter); err != nil { + exec.execContext.getGroupContext(groupIdx), commonContext, v, exec.ret.bsFromEmptyList[x][y], getter, setter); err != nil { return err } exec.ret.setGroupNotEmpty(x, y) @@ -469,7 +466,6 @@ func (exec *aggregatorFromBytesToFixed[to]) distinctBatchFill( getter := exec.ret.get setter := exec.ret.set commonContext := exec.execContext.getCommonContext() - bs := exec.ret.getEmptyList() needs, err := exec.distinctHash.batchFill(vectors, offset, groups) if err != nil { @@ -483,7 +479,7 @@ func (exec *aggregatorFromBytesToFixed[to]) distinctBatchFill( idx := int(group - 1) x, y := exec.ret.updateNextAccessIdx(idx) if err = exec.fill( - exec.execContext.getGroupContext(idx), commonContext, value, bs[x][y], getter, setter); err != nil { + exec.execContext.getGroupContext(idx), commonContext, value, exec.ret.bsFromEmptyList[x][y], getter, setter); err != nil { return err } exec.ret.setGroupNotEmpty(x, y) @@ -501,7 +497,7 @@ func (exec *aggregatorFromBytesToFixed[to]) distinctBatchFill( groupIdx := int(groups[idx] - 1) x, y := exec.ret.updateNextAccessIdx(groupIdx) if err = exec.fill( - exec.execContext.getGroupContext(groupIdx), commonContext, v, bs[x][y], getter, setter); err != nil { + exec.execContext.getGroupContext(groupIdx), commonContext, v, exec.ret.bsFromEmptyList[x][y], getter, setter); err != nil { return err } exec.ret.setGroupNotEmpty(x, y) @@ -518,7 +514,7 @@ func (exec *aggregatorFromBytesToFixed[to]) distinctBatchFill( groupIdx := int(groups[idx] - 1) x, y := exec.ret.updateNextAccessIdx(groupIdx) if err = exec.fill( - exec.execContext.getGroupContext(groupIdx), commonContext, v, bs[x][y], getter, setter); err != nil { + exec.execContext.getGroupContext(groupIdx), commonContext, v, exec.ret.bsFromEmptyList[x][y], getter, setter); err != nil { return err } exec.ret.setGroupNotEmpty(x, y) diff --git a/pkg/sql/colexec/aggexec/fromFixedRetBytes.go b/pkg/sql/colexec/aggexec/fromFixedRetBytes.go index dfef2526347db..898c8c591b87f 100644 --- a/pkg/sql/colexec/aggexec/fromFixedRetBytes.go +++ b/pkg/sql/colexec/aggexec/fromFixedRetBytes.go @@ -341,12 +341,11 @@ func (exec *aggregatorFromFixedToBytes[from]) BulkFill( } exec.arg.prepare(vectors[0]) - bs := exec.ret.getEmptyListOnX(x) if exec.arg.w.WithAnyNullValue() { for i, j := uint64(0), uint64(length); i < j; i++ { v, null := exec.arg.w.GetValue(i) if !null { - if err := exec.fill(groupContext, commonContext, v, bs[y], getter, setter); err != nil { + if err := exec.fill(groupContext, commonContext, v, exec.ret.bsFromEmptyList[x][y], getter, setter); err != nil { return err } exec.ret.setGroupNotEmpty(x, y) @@ -357,7 +356,7 @@ func (exec *aggregatorFromFixedToBytes[from]) BulkFill( vs := exec.arg.w.UnSafeGetAllValue() for _, v := range vs { - if err := exec.fill(groupContext, commonContext, v, bs[y], getter, setter); err != nil { + if err := exec.fill(groupContext, commonContext, v, exec.ret.bsFromEmptyList[x][y], getter, setter); err != nil { return err } exec.ret.setGroupNotEmpty(x, y) @@ -388,13 +387,12 @@ func (exec *aggregatorFromFixedToBytes[from]) distinctBulkFill( return err } - bs := exec.ret.getEmptyListOnX(x) if exec.arg.w.WithAnyNullValue() { for i, j := uint64(0), uint64(length); i < j; i++ { if needs[i] { v, null := exec.arg.w.GetValue(i) if !null { - if err = exec.fill(groupContext, commonContext, v, bs[y], getter, setter); err != nil { + if err = exec.fill(groupContext, commonContext, v, exec.ret.bsFromEmptyList[x][y], getter, setter); err != nil { return err } exec.ret.setGroupNotEmpty(x, y) @@ -407,7 +405,7 @@ func (exec *aggregatorFromFixedToBytes[from]) distinctBulkFill( vs := exec.arg.w.UnSafeGetAllValue() for i, v := range vs { if needs[i] { - if err = exec.fill(groupContext, commonContext, v, bs[y], getter, setter); err != nil { + if err = exec.fill(groupContext, commonContext, v, exec.ret.bsFromEmptyList[x][y], getter, setter); err != nil { return err } exec.ret.setGroupNotEmpty(x, y) @@ -429,7 +427,6 @@ func (exec *aggregatorFromFixedToBytes[from]) BatchFill( getter := exec.ret.get setter := exec.ret.set commonContext := exec.execContext.getCommonContext() - bs := exec.ret.getEmptyList() if vectors[0].IsConst() { value := vector.MustFixedColWithTypeCheck[from](vectors[0])[0] @@ -438,7 +435,7 @@ func (exec *aggregatorFromFixedToBytes[from]) BatchFill( idx := int(group - 1) x, y := exec.ret.updateNextAccessIdx(idx) if err := exec.fill( - exec.execContext.getGroupContext(idx), commonContext, value, bs[x][y], getter, setter); err != nil { + exec.execContext.getGroupContext(idx), commonContext, value, exec.ret.bsFromEmptyList[x][y], getter, setter); err != nil { return err } exec.ret.setGroupNotEmpty(x, y) @@ -456,7 +453,7 @@ func (exec *aggregatorFromFixedToBytes[from]) BatchFill( groupIdx := int(groups[idx] - 1) x, y := exec.ret.updateNextAccessIdx(groupIdx) if err := exec.fill( - exec.execContext.getGroupContext(groupIdx), commonContext, v, bs[x][y], getter, setter); err != nil { + exec.execContext.getGroupContext(groupIdx), commonContext, v, exec.ret.bsFromEmptyList[x][y], getter, setter); err != nil { return err } exec.ret.setGroupNotEmpty(x, y) @@ -473,7 +470,7 @@ func (exec *aggregatorFromFixedToBytes[from]) BatchFill( groupIdx := int(groups[idx] - 1) x, y := exec.ret.updateNextAccessIdx(groupIdx) if err := exec.fill( - exec.execContext.getGroupContext(groupIdx), commonContext, vs[i], bs[x][y], getter, setter); err != nil { + exec.execContext.getGroupContext(groupIdx), commonContext, vs[i], exec.ret.bsFromEmptyList[x][y], getter, setter); err != nil { return err } exec.ret.setGroupNotEmpty(x, y) @@ -488,7 +485,6 @@ func (exec *aggregatorFromFixedToBytes[from]) distinctBatchFill( getter := exec.ret.get setter := exec.ret.set commonContext := exec.execContext.getCommonContext() - bs := exec.ret.getEmptyList() needs, err := exec.distinctHash.batchFill(vectors, offset, groups) if err != nil { @@ -502,7 +498,7 @@ func (exec *aggregatorFromFixedToBytes[from]) distinctBatchFill( idx := int(group - 1) x, y := exec.ret.updateNextAccessIdx(idx) if err = exec.fill( - exec.execContext.getGroupContext(idx), commonContext, value, bs[x][y], getter, setter); err != nil { + exec.execContext.getGroupContext(idx), commonContext, value, exec.ret.bsFromEmptyList[x][y], getter, setter); err != nil { return err } exec.ret.setGroupNotEmpty(x, y) @@ -520,7 +516,7 @@ func (exec *aggregatorFromFixedToBytes[from]) distinctBatchFill( groupIdx := int(groups[idx] - 1) x, y := exec.ret.updateNextAccessIdx(groupIdx) if err = exec.fill( - exec.execContext.getGroupContext(groupIdx), commonContext, v, bs[x][y], getter, setter); err != nil { + exec.execContext.getGroupContext(groupIdx), commonContext, v, exec.ret.bsFromEmptyList[x][y], getter, setter); err != nil { return err } exec.ret.setGroupNotEmpty(x, y) @@ -537,7 +533,7 @@ func (exec *aggregatorFromFixedToBytes[from]) distinctBatchFill( groupIdx := int(groups[idx] - 1) x, y := exec.ret.updateNextAccessIdx(groupIdx) if err = exec.fill( - exec.execContext.getGroupContext(groupIdx), commonContext, vs[i], bs[x][y], getter, setter); err != nil { + exec.execContext.getGroupContext(groupIdx), commonContext, vs[i], exec.ret.bsFromEmptyList[x][y], getter, setter); err != nil { return err } exec.ret.setGroupNotEmpty(x, y) diff --git a/pkg/sql/colexec/aggexec/fromFixedRetFixed.go b/pkg/sql/colexec/aggexec/fromFixedRetFixed.go index 8125bd09467f4..ce1436e4ebf36 100644 --- a/pkg/sql/colexec/aggexec/fromFixedRetFixed.go +++ b/pkg/sql/colexec/aggexec/fromFixedRetFixed.go @@ -396,12 +396,11 @@ func (exec *aggregatorFromFixedToFixed[from, to]) BulkFill( } exec.arg.prepare(vectors[0]) - bs := exec.ret.getEmptyListOnX(x) if exec.arg.w.WithAnyNullValue() { for i, j := uint64(0), uint64(length); i < j; i++ { v, null := exec.arg.w.GetValue(i) if !null { - if err := exec.fill(groupContext, commonContext, v, bs[y], getter, setter); err != nil { + if err := exec.fill(groupContext, commonContext, v, exec.ret.bsFromEmptyList[x][y], getter, setter); err != nil { return err } exec.ret.setGroupNotEmpty(x, y) @@ -412,7 +411,7 @@ func (exec *aggregatorFromFixedToFixed[from, to]) BulkFill( vs := exec.arg.w.UnSafeGetAllValue() for _, v := range vs { - if err := exec.fill(groupContext, commonContext, v, bs[y], getter, setter); err != nil { + if err := exec.fill(groupContext, commonContext, v, exec.ret.bsFromEmptyList[x][y], getter, setter); err != nil { return err } exec.ret.setGroupNotEmpty(x, y) @@ -443,13 +442,12 @@ func (exec *aggregatorFromFixedToFixed[from, to]) distinctBulkFill( return err } - bs := exec.ret.getEmptyListOnX(x) if exec.arg.w.WithAnyNullValue() { for i, j := uint64(0), uint64(length); i < j; i++ { if needs[i] { v, null := exec.arg.w.GetValue(i) if !null { - if err = exec.fill(groupContext, commonContext, v, bs[y], getter, setter); err != nil { + if err = exec.fill(groupContext, commonContext, v, exec.ret.bsFromEmptyList[x][y], getter, setter); err != nil { return err } exec.ret.setGroupNotEmpty(x, y) @@ -462,7 +460,7 @@ func (exec *aggregatorFromFixedToFixed[from, to]) distinctBulkFill( vs := exec.arg.w.UnSafeGetAllValue() for i, v := range vs { if needs[i] { - if err = exec.fill(groupContext, commonContext, v, bs[y], getter, setter); err != nil { + if err = exec.fill(groupContext, commonContext, v, exec.ret.bsFromEmptyList[x][y], getter, setter); err != nil { return err } exec.ret.setGroupNotEmpty(x, y) @@ -484,7 +482,6 @@ func (exec *aggregatorFromFixedToFixed[from, to]) BatchFill( getter := exec.ret.get setter := exec.ret.set commonContext := exec.execContext.getCommonContext() - bs := exec.ret.getEmptyList() if vectors[0].IsConst() { value := vector.MustFixedColWithTypeCheck[from](vectors[0])[0] @@ -493,7 +490,7 @@ func (exec *aggregatorFromFixedToFixed[from, to]) BatchFill( idx := int(group - 1) x, y := exec.ret.updateNextAccessIdx(idx) if err := exec.fill( - exec.execContext.getGroupContext(idx), commonContext, value, bs[x][y], getter, setter); err != nil { + exec.execContext.getGroupContext(idx), commonContext, value, exec.ret.bsFromEmptyList[x][y], getter, setter); err != nil { return err } exec.ret.setGroupNotEmpty(x, y) @@ -511,7 +508,7 @@ func (exec *aggregatorFromFixedToFixed[from, to]) BatchFill( groupIdx := int(groups[idx] - 1) x, y := exec.ret.updateNextAccessIdx(groupIdx) if err := exec.fill( - exec.execContext.getGroupContext(groupIdx), commonContext, v, bs[x][y], getter, setter); err != nil { + exec.execContext.getGroupContext(groupIdx), commonContext, v, exec.ret.bsFromEmptyList[x][y], getter, setter); err != nil { return err } exec.ret.setGroupNotEmpty(x, y) @@ -528,7 +525,7 @@ func (exec *aggregatorFromFixedToFixed[from, to]) BatchFill( groupIdx := int(groups[idx] - 1) x, y := exec.ret.updateNextAccessIdx(groupIdx) if err := exec.fill( - exec.execContext.getGroupContext(groupIdx), commonContext, vs[i], bs[x][y], getter, setter); err != nil { + exec.execContext.getGroupContext(groupIdx), commonContext, vs[i], exec.ret.bsFromEmptyList[x][y], getter, setter); err != nil { return err } exec.ret.setGroupNotEmpty(x, y) @@ -543,7 +540,6 @@ func (exec *aggregatorFromFixedToFixed[from, to]) distinctBatchFill( getter := exec.ret.get setter := exec.ret.set commonContext := exec.execContext.getCommonContext() - bs := exec.ret.getEmptyList() needs, err := exec.distinctHash.batchFill(vectors, offset, groups) if err != nil { @@ -557,7 +553,7 @@ func (exec *aggregatorFromFixedToFixed[from, to]) distinctBatchFill( idx := int(group - 1) x, y := exec.ret.updateNextAccessIdx(idx) if err = exec.fill( - exec.execContext.getGroupContext(idx), commonContext, value, bs[x][y], getter, setter); err != nil { + exec.execContext.getGroupContext(idx), commonContext, value, exec.ret.bsFromEmptyList[x][y], getter, setter); err != nil { return err } exec.ret.setGroupNotEmpty(x, y) @@ -575,7 +571,7 @@ func (exec *aggregatorFromFixedToFixed[from, to]) distinctBatchFill( groupIdx := int(groups[idx] - 1) x, y := exec.ret.updateNextAccessIdx(groupIdx) if err = exec.fill( - exec.execContext.getGroupContext(groupIdx), commonContext, v, bs[x][y], getter, setter); err != nil { + exec.execContext.getGroupContext(groupIdx), commonContext, v, exec.ret.bsFromEmptyList[x][y], getter, setter); err != nil { return err } exec.ret.setGroupNotEmpty(x, y) @@ -592,7 +588,7 @@ func (exec *aggregatorFromFixedToFixed[from, to]) distinctBatchFill( groupIdx := int(groups[idx] - 1) x, y := exec.ret.updateNextAccessIdx(groupIdx) if err = exec.fill( - exec.execContext.getGroupContext(groupIdx), commonContext, vs[i], bs[x][y], getter, setter); err != nil { + exec.execContext.getGroupContext(groupIdx), commonContext, vs[i], exec.ret.bsFromEmptyList[x][y], getter, setter); err != nil { return err } exec.ret.setGroupNotEmpty(x, y) @@ -664,7 +660,6 @@ func (exec *aggregatorFromFixedToFixed[from, to]) Flush() ([]*vector.Vector, err } } - // todo: 等全部代码改完后修改接口。 return exec.ret.flushAll(), nil } diff --git a/pkg/sql/colexec/aggexec/result.go b/pkg/sql/colexec/aggexec/result.go index c25fa5f2ecb6d..ecf98484fdc17 100644 --- a/pkg/sql/colexec/aggexec/result.go +++ b/pkg/sql/colexec/aggexec/result.go @@ -34,7 +34,7 @@ const ( blockCapacityFor32Byte = 1024 * 1024 * 25 blockCapacityFor64Byte = 1024 * 1024 * 12 blockCapacityFor128Byte = 1024 * 1024 * 6 - blockCapacityForStrType = 8192 * 4 + BlockCapacityForStrType = 8192 * 4 ) var blockCapacityMap = map[int]int{ @@ -53,7 +53,7 @@ var blockCapacityMap = map[int]int{ // GetChunkSizeFromType return the chunk size for the input type. // This chunk size ensures that each chunk with this type does not exceed 1 GB of memory. func GetChunkSizeFromType(typ types.Type) (s int) { - s = blockCapacityForStrType + s = BlockCapacityForStrType if !typ.IsVarlen() { if newCap, ok := blockCapacityMap[typ.TypeSize()]; ok { s = newCap @@ -362,14 +362,6 @@ func (r *optSplitResult) getEachBlockLimitation() int { return r.optInformation.chunkSize } -func (r *optSplitResult) getEmptyList() [][]bool { - return r.bsFromEmptyList -} - -func (r *optSplitResult) getEmptyListOnX(x int) []bool { - return r.bsFromEmptyList[x] -} - // flushAll return all the result. func (r *optSplitResult) flushAll() []*vector.Vector { if r.optInformation.doesThisNeedEmptyList && r.optInformation.shouldSetNullToEmptyGroup { @@ -378,8 +370,8 @@ func (r *optSplitResult) flushAll() []*vector.Vector { } } - ret := r.resultList - r.resultList = nil + var ret []*vector.Vector + ret, r.resultList = r.resultList[:r.nowIdx1+1], r.resultList[r.nowIdx1+1:] return ret } diff --git a/pkg/sql/colexec/aggexec/result_test.go b/pkg/sql/colexec/aggexec/result_test.go index 75ab0a4f4ece3..0b25d9bc8d5ac 100644 --- a/pkg/sql/colexec/aggexec/result_test.go +++ b/pkg/sql/colexec/aggexec/result_test.go @@ -59,6 +59,43 @@ func TestExtendResultPurely(t *testing.T) { } } +func TestFlushAll(t *testing.T) { + blockLimitation := 100 + + mg := SimpleAggMemoryManager{mp: mpool.MustNewZeroNoFixed()} + { + osr := optSplitResult{} + osr.init(mg, types.T_bool.ToType(), false) + osr.optInformation.chunkSize = blockLimitation + + require.NoError(t, osr.preExtend(130)) + vs := osr.flushAll() + require.True(t, len(vs) == 0 || (len(vs) == 1 && vs[0].Length() == 0)) + for i := range vs { + vs[i].Free(mg.mp) + } + + osr.free() + require.Equal(t, int64(0), mg.mp.CurrNB()) + } + + { + osr := optSplitResult{} + osr.init(mg, types.T_bool.ToType(), false) + osr.optInformation.chunkSize = blockLimitation + + require.NoError(t, osr.extendResultPurely(201)) + vs := osr.flushAll() + require.Equal(t, 3, len(vs)) + for i := range vs { + vs[i].Free(mg.mp) + } + + osr.free() + require.Equal(t, int64(0), mg.mp.CurrNB()) + } +} + func checkRowDistribution(t *testing.T, expected []int, src []*vector.Vector) { require.Equal(t, len(expected), len(src)) diff --git a/pkg/sql/colexec/group/exec.go b/pkg/sql/colexec/group/exec.go index 97f1acd18b8db..9b8123a99d1a6 100644 --- a/pkg/sql/colexec/group/exec.go +++ b/pkg/sql/colexec/group/exec.go @@ -28,6 +28,11 @@ import ( var makeAggExec = aggexec.MakeAgg +// intermediateResultSendActionTrigger is the row number to trigger an action +// to send the intermediate result +// if the result row is not less than this number. +var intermediateResultSendActionTrigger = aggexec.BlockCapacityForStrType + func (group *Group) String(buf *bytes.Buffer) { buf.WriteString(thisOperatorName + ": group([") for i, expr := range group.Exprs { @@ -265,8 +270,9 @@ func (group *Group) consumeBatchToGetFinalResult( if err != nil { return err } - group.ctr.result1.InitWithGroupBy(aggexec.GetMinAggregatorsChunkSize(group.ctr.groupByEvaluate.Vec, aggs), aggs, group.ctr.groupByEvaluate.Vec) - if err = preExtendAggExecs(aggs, group.PreAllocSize); err != nil { + if err = group.ctr.result1.InitWithGroupBy( + proc.Mp(), + aggexec.GetMinAggregatorsChunkSize(group.ctr.groupByEvaluate.Vec, aggs), aggs, group.ctr.groupByEvaluate.Vec, group.PreAllocSize); err != nil { return err } } @@ -346,83 +352,105 @@ func preExtendAggExecs(execs []aggexec.AggFuncExec, preAllocated uint64) (err er return nil } -// callToGetIntermediateResult -// if this operator should only return aggregations' intermediate results because one MergeGroup operator was behind. -// we do not engage in any waiting actions at this operator, -// once a batch is received, a batch with the corresponding intermediate result will be returned. +// callToGetIntermediateResult return the intermediate result of grouped data aggregation, +// sending out intermediate results once the group count exceeds intermediateResultSendActionTrigger. +// +// this function will be only called when there is one MergeGroup operator was behind. func (group *Group) callToGetIntermediateResult(proc *process.Process) (*batch.Batch, error) { group.ctr.result2.resetLastPopped() if group.ctr.state == vm.End { return nil, nil } + r, err := group.initCtxToGetIntermediateResult(proc) + if err != nil { + return nil, err + } + + var input *batch.Batch for { - res, err := group.getInputBatch(proc) + if group.ctr.state == vm.End { + return nil, nil + } + + input, err = group.getInputBatch(proc) if err != nil { return nil, err } - if res == nil { + if input == nil { group.ctr.state = vm.End if group.ctr.isDataSourceEmpty() && len(group.Exprs) == 0 { - r, er := group.ctr.result2.getResultBatch( - proc, &group.ctr.groupByEvaluate, group.ctr.aggregateEvaluate, group.Aggs) - if er != nil { - return nil, er - } - for i := range r.Aggs { - if err = r.Aggs[i].GroupGrow(1); err != nil { - return nil, err - } - } r.SetRowCount(1) return r, nil } - - return nil, nil + if r.RowCount() > 0 { + return r, nil + } + continue } - if res.IsEmpty() { + if input.IsEmpty() { continue } group.ctr.dataSourceIsEmpty = false - return group.consumeBatchToGetIntermediateResult(proc, res) + + if next, er := group.consumeBatchToRes(proc, input, r); er != nil { + return nil, er + } else { + if next { + continue + } + return r, nil + } } } -func (group *Group) consumeBatchToGetIntermediateResult( - proc *process.Process, bat *batch.Batch) (*batch.Batch, error) { - - if err := group.evaluateGroupByAndAgg(proc, bat); err != nil { - return nil, err - } +func (group *Group) initCtxToGetIntermediateResult( + proc *process.Process) (*batch.Batch, error) { - res, err := group.ctr.result2.getResultBatch( + r, err := group.ctr.result2.getResultBatch( proc, &group.ctr.groupByEvaluate, group.ctr.aggregateEvaluate, group.Aggs) if err != nil { return nil, err } - switch group.ctr.mtyp { - case H0: - // without group by. - for i := range res.Aggs { - if err = res.Aggs[i].GroupGrow(1); err != nil { + if group.ctr.mtyp == H0 { + for i := range r.Aggs { + if err = r.Aggs[i].GroupGrow(1); err != nil { return nil, err } } - res.SetRowCount(1) + } else { + allocated := max(min(group.PreAllocSize, uint64(intermediateResultSendActionTrigger)), 0) + if err = group.ctr.hr.BuildHashTable(true, group.ctr.mtyp == HStr, group.ctr.keyNullable, allocated); err != nil { + return nil, err + } + err = preExtendAggExecs(r.Aggs, allocated) + } + + return r, err +} + +func (group *Group) consumeBatchToRes( + proc *process.Process, bat *batch.Batch, res *batch.Batch) (receiveNext bool, err error) { + + if err = group.evaluateGroupByAndAgg(proc, bat); err != nil { + return false, err + } + + switch group.ctr.mtyp { + case H0: + // without group by. for i := range res.Aggs { if err = res.Aggs[i].BulkFill(0, group.ctr.aggregateEvaluate[i].Vec); err != nil { - return nil, err + return false, err } } + res.SetRowCount(1) + return false, nil default: // with group by. - if err = group.ctr.hr.BuildHashTable(true, group.ctr.mtyp == HStr, group.ctr.keyNullable, 0); err != nil { - return nil, err - } - count := bat.RowCount() for i := 0; i < count; i += hashmap.UnitLimit { n := count - i @@ -433,7 +461,7 @@ func (group *Group) consumeBatchToGetIntermediateResult( originGroupCount := group.ctr.hr.Hash.GroupCount() vals, _, err1 := group.ctr.hr.Itr.Insert(i, n, group.ctr.groupByEvaluate.Vec) if err1 != nil { - return nil, err1 + return false, err1 } insertList, more := group.ctr.hr.GetBinaryInsertList(vals, originGroupCount) @@ -441,13 +469,13 @@ func (group *Group) consumeBatchToGetIntermediateResult( if cnt > 0 { for j, vec := range res.Vecs { if err = vec.UnionBatch(group.ctr.groupByEvaluate.Vec[j], int64(i), n, insertList, proc.Mp()); err != nil { - return nil, err + return false, err } } for _, ag := range res.Aggs { if err = ag.GroupGrow(cnt); err != nil { - return nil, err + return false, err } } res.AddRowCount(cnt) @@ -455,12 +483,11 @@ func (group *Group) consumeBatchToGetIntermediateResult( for j, ag := range res.Aggs { if err = ag.BatchFill(i, vals[:n], group.ctr.aggregateEvaluate[j].Vec); err != nil { - return nil, err + return false, err } } } + return res.RowCount() < intermediateResultSendActionTrigger, nil } - - return res, nil } diff --git a/pkg/sql/colexec/group/exec_test.go b/pkg/sql/colexec/group/exec_test.go index 0a2b3af190483..016485b90e09e 100644 --- a/pkg/sql/colexec/group/exec_test.go +++ b/pkg/sql/colexec/group/exec_test.go @@ -338,6 +338,7 @@ func TestGroup_GetFinalEvaluation_WithGroupBy(t *testing.T) { func TestGroup_GetIntermediateResult_NoneGroupBy(t *testing.T) { proc := testutil.NewProcess() + intermediateResultSendActionTrigger = 0 // datasource. { @@ -436,6 +437,7 @@ func TestGroup_GetIntermediateResult_NoneGroupBy(t *testing.T) { func TestGroup_GetIntermediateResult_WithGroupBy(t *testing.T) { proc := testutil.NewProcess() + intermediateResultSendActionTrigger = 0 // datasource. { diff --git a/pkg/sql/colexec/group/execctx.go b/pkg/sql/colexec/group/execctx.go index 0ae6eca9a657b..66c0ae79e7750 100644 --- a/pkg/sql/colexec/group/execctx.go +++ b/pkg/sql/colexec/group/execctx.go @@ -123,7 +123,7 @@ type GroupResultBuffer struct { } func (buf *GroupResultBuffer) IsEmpty() bool { - return len(buf.ToPopped) == 0 + return cap(buf.ToPopped) == 0 } func (buf *GroupResultBuffer) InitOnlyAgg(chunkSize int, aggList []aggexec.AggFuncExec) { @@ -135,13 +135,38 @@ func (buf *GroupResultBuffer) InitOnlyAgg(chunkSize int, aggList []aggexec.AggFu buf.ToPopped = append(buf.ToPopped, batch.NewOffHeapEmpty()) } -func (buf *GroupResultBuffer) InitWithGroupBy(chunkSize int, aggList []aggexec.AggFuncExec, groupByVec []*vector.Vector) { +func (buf *GroupResultBuffer) InitWithGroupBy( + mp *mpool.MPool, + chunkSize int, aggList []aggexec.AggFuncExec, groupByVec []*vector.Vector, preAllocated uint64) error { aggexec.SyncAggregatorsToChunkSize(aggList, chunkSize) buf.ChunkSize = chunkSize buf.AggList = aggList - buf.ToPopped = make([]*batch.Batch, 0, 1) - buf.ToPopped = append(buf.ToPopped, getInitialBatchWithSameTypeVecs(groupByVec)) + + if preAllocated > 0 { + fullNum, moreRow := preAllocated/uint64(buf.ChunkSize), preAllocated%uint64(buf.ChunkSize) + for i := uint64(0); i < fullNum; i++ { + vec := getInitialBatchWithSameTypeVecs(groupByVec) + if err := vec.PreExtend(mp, buf.ChunkSize); err != nil { + vec.Clean(mp) + return err + } + buf.ToPopped = append(buf.ToPopped, vec) + } + if moreRow > 0 { + vec := getInitialBatchWithSameTypeVecs(groupByVec) + if err := vec.PreExtend(mp, int(moreRow)); err != nil { + vec.Clean(mp) + return err + } + buf.ToPopped = append(buf.ToPopped, vec) + } + } else { + buf.ToPopped = append(buf.ToPopped, getInitialBatchWithSameTypeVecs(groupByVec)) + } + + buf.ToPopped = buf.ToPopped[:1] + return preExtendAggExecs(buf.AggList, preAllocated) } func (buf *GroupResultBuffer) InitWithBatch(chunkSize int, aggList []aggexec.AggFuncExec, vecExampleBatch *batch.Batch) { @@ -176,7 +201,19 @@ func (buf *GroupResultBuffer) AppendBatch( return toIncrease, err } - buf.ToPopped = append(buf.ToPopped, getInitialBatchWithSameTypeVecs(vs)) + if cap(buf.ToPopped) > len(buf.ToPopped) { + buf.ToPopped = buf.ToPopped[:len(buf.ToPopped)+1] + } else { + buf.ToPopped = append(buf.ToPopped, nil) + } + if buf.ToPopped[len(buf.ToPopped)-1] == nil { + buf.ToPopped[len(buf.ToPopped)-1] = getInitialBatchWithSameTypeVecs(vs) + if len(buf.ToPopped) > 4 { + if err = buf.ToPopped[len(buf.ToPopped)-1].PreExtend(mp, buf.ChunkSize); err != nil { + return toIncrease, err + } + } + } _, err = buf.AppendBatch(mp, vs, offset+k+1, insertList[k+1:]) return toIncrease, err @@ -247,6 +284,7 @@ func (buf *GroupResultBuffer) CleanLastPopped(m *mpool.MPool) { } func (buf *GroupResultBuffer) Free0(m *mpool.MPool) { + buf.ToPopped = buf.ToPopped[:cap(buf.ToPopped)] for i := range buf.ToPopped { if buf.ToPopped[i] != nil { buf.ToPopped[i].Clean(m) @@ -290,9 +328,9 @@ func (r *GroupResultNoneBlock) getResultBatch( // prepare an OK result. if r.res == nil { - r.res = batch.NewOffHeapWithSize(len(gEval.Vec)) + r.res = batch.NewOffHeapWithSize(len(gEval.Typ)) for i := range r.res.Vecs { - r.res.Vecs[i] = vector.NewOffHeapVecWithType(*gEval.Vec[i].GetType()) + r.res.Vecs[i] = vector.NewOffHeapVecWithType(gEval.Typ[i]) } r.res.Aggs = make([]aggexec.AggFuncExec, len(aExpressions)) } else { diff --git a/pkg/sql/colexec/group/execctx_test.go b/pkg/sql/colexec/group/execctx_test.go new file mode 100644 index 0000000000000..ec9aaced9c79c --- /dev/null +++ b/pkg/sql/colexec/group/execctx_test.go @@ -0,0 +1,49 @@ +// Copyright 2024 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 group + +import ( + "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" + "testing" +) + +func TestInitGroupResultBuffer(t *testing.T) { + mp := mpool.MustNewZeroNoFixed() + + { + // pre-extend test. + buf := GroupResultBuffer{} + vec := []*vector.Vector{vector.NewVec(types.T_int64.ToType())} + + require.NoError(t, buf.InitWithGroupBy(mp, 256, nil, vec, 3*256+128)) + + require.Equal(t, 3+1, cap(buf.ToPopped)) + + ol := len(buf.ToPopped) + buf.ToPopped = buf.ToPopped[:cap(buf.ToPopped)] + require.Equal(t, 256, buf.ToPopped[0].Vecs[0].Capacity()) + require.Equal(t, 256, buf.ToPopped[1].Vecs[0].Capacity()) + require.Equal(t, 256, buf.ToPopped[2].Vecs[0].Capacity()) + require.Equal(t, 128, buf.ToPopped[3].Vecs[0].Capacity()) + buf.ToPopped = buf.ToPopped[:ol] + + buf.Free0(mp) + } + + require.Equal(t, int64(0), mp.CurrNB()) +} From 153f2cb2b2526668dbcebfec6f43b833ee269b12 Mon Sep 17 00:00:00 2001 From: Eric Lam Date: Fri, 20 Dec 2024 03:02:33 +0000 Subject: [PATCH 07/26] Fulltext remove slow ORDER BY SQL and reduce memory usage (#20702) - remove ORDER BY - For phrase search, use inner join and use position in runtime filter - group by SQL result with doc_id and produce a vector of document count [N]uint8 where N is the number of keywords in search string. Index of the vector is corresponding to the Pattern.Index. Only one row per doc_id will be stored in hastable to minimize the memory usage - ignore position for OR operation - secondary index table cluster by doc_id - value in hashtable stored in memory pool that can spill - optimize the SQL with JOIN. SQL Generation according to Set Theory - support print SQL with explain verbose - support Chinese in boolean mode - Escape single quote to avoid SQL syntax error. Approved by: @zhangxu19830126, @badboynt1, @daviszhen, @m-schen, @aylei, @qingxinhome, @aunjgr, @sukki37, @ouyuanning, @heni02, @aressu1985 --- go.mod | 8 +- go.sum | 26 +- pkg/common/util/unsafe.go | 5 + pkg/common/util/unsafe_test.go | 9 + pkg/datalink/docx/docx_test.go | 17 +- pkg/datalink/docx/testfiles/wiki.docx | Bin 0 -> 53154 bytes pkg/datalink/docx/types.go | 9 +- pkg/fulltext/fixedbytepool.go | 494 ++++++++ pkg/fulltext/fixedbytepool_test.go | 353 ++++++ pkg/fulltext/fulltext.go | 318 +++-- pkg/fulltext/fulltext_test.go | 1038 ++++++++------- pkg/fulltext/sql.go | 499 ++++++++ pkg/fulltext/sql_test.go | 156 +++ pkg/fulltext/types.go | 85 +- pkg/monlp/tokenizer/simple.go | 2 +- pkg/pb/plan/plan.pb.go | 1115 +++++++++-------- pkg/sql/colexec/table_function/fulltext.go | 293 +++-- .../colexec/table_function/fulltext_test.go | 18 +- pkg/sql/compile/sql_executor.go | 9 +- pkg/sql/plan/apply_indices_fulltext.go | 2 + pkg/sql/plan/build_ddl.go | 5 + pkg/sql/plan/explain/explain_node.go | 17 + pkg/sql/plan/explain/fultext_test.go | 35 + pkg/sql/plan/fulltext.go | 62 +- pkg/sql/plan/function/func_fulltext.go | 2 + pkg/udf/pythonservice/client.go | 2 +- pkg/util/executor/options.go | 7 +- pkg/util/executor/options_test.go | 6 +- pkg/util/executor/types.go | 1 + proto/plan.proto | 1 + test/distributed/cases/ddl/table_kind.result | 12 +- .../cases/fulltext/fulltext.result | 30 +- test/distributed/cases/fulltext/fulltext.sql | 4 + .../cases/fulltext/fulltext1.result | 2 +- .../cases/fulltext/fulltext2.result | 27 +- test/distributed/cases/fulltext/fulltext2.sql | 10 +- 36 files changed, 3389 insertions(+), 1290 deletions(-) create mode 100644 pkg/datalink/docx/testfiles/wiki.docx create mode 100644 pkg/fulltext/fixedbytepool.go create mode 100644 pkg/fulltext/fixedbytepool_test.go create mode 100644 pkg/fulltext/sql.go create mode 100644 pkg/fulltext/sql_test.go create mode 100644 pkg/sql/plan/explain/fultext_test.go diff --git a/go.mod b/go.mod index 0f49efe0f5854..63f0788a91be6 100644 --- a/go.mod +++ b/go.mod @@ -22,7 +22,7 @@ require ( github.com/aws/smithy-go v1.22.1 github.com/axiomhq/hyperloglog v0.0.0-20230201085229-3ddf4bad03dc github.com/cakturk/go-netstat v0.0.0-20200220111822-e5b49efee7a5 - github.com/cespare/xxhash/v2 v2.2.0 + github.com/cespare/xxhash/v2 v2.3.0 github.com/cockroachdb/errors v1.9.1 github.com/confluentinc/confluent-kafka-go/v2 v2.4.0 github.com/containerd/cgroups/v3 v3.0.1 @@ -87,8 +87,8 @@ require ( golang.org/x/sync v0.8.0 golang.org/x/sys v0.26.0 gonum.org/v1/gonum v0.14.0 - google.golang.org/grpc v1.62.1 - google.golang.org/protobuf v1.34.2 + google.golang.org/grpc v1.65.0 + google.golang.org/protobuf v1.36.0 gopkg.in/natefinch/lumberjack.v2 v2.2.1 ) @@ -204,7 +204,7 @@ require ( go.uber.org/multierr v1.11.0 // indirect golang.org/x/mod v0.21.0 // indirect golang.org/x/tools v0.26.0 // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20240318140521-94a12d6c2237 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20240604185151-ef581f913117 // indirect gopkg.in/ini.v1 v1.67.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect diff --git a/go.sum b/go.sum index 43401a821e7b9..6936615fa8361 100644 --- a/go.sum +++ b/go.sum @@ -128,8 +128,8 @@ github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko= github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= -github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= -github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= +github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/chzyer/logex v1.2.1/go.mod h1:JLbx6lG2kDbNRFnfkgvh4eRJRPX1QCoOIWomwysCBrQ= github.com/chzyer/readline v1.5.1/go.mod h1:Eh+b79XXUwfKfcPLepksvw2tcLE/Ct21YObkaSkeBlk= github.com/chzyer/test v1.0.0/go.mod h1:2JlltgoNkt4TW/z9V/IzDdFaMTM2JPIi26O1pF38GC8= @@ -949,8 +949,8 @@ golang.org/x/net v0.20.0/go.mod h1:z8BVo6PvndSri0LbOE3hAn0apkU+1YvI6E70E9jsnvY= golang.org/x/net v0.30.0 h1:AcW1SDZMkb8IpzCdQUaIq2sP4sZ4zw+55h6ynffypl4= golang.org/x/net v0.30.0/go.mod h1:2wGyMJ5iFasEhkwi13ChkO/t1ECNC4X4eBKkVFyYFlU= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= -golang.org/x/oauth2 v0.16.0 h1:aDkGMBSYxElaoP81NpoUoz2oo2R2wHdZpGToUxfyQrQ= -golang.org/x/oauth2 v0.16.0/go.mod h1:hqZ+0LWXsiVoZpeld6jVt06P3adbS2Uu911W1SsJv2o= +golang.org/x/oauth2 v0.20.0 h1:4mQdhULixXKP1rwYBW0vAijoXnkTG0BLCDRzfe1idMo= +golang.org/x/oauth2 v0.20.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -1073,8 +1073,6 @@ gonum.org/v1/netlib v0.0.0-20190313105609-8cb42192e0e0/go.mod h1:wa6Ws7BG/ESfp6d gonum.org/v1/plot v0.0.0-20190515093506-e2840ee46a6b/go.mod h1:Wt8AAjI+ypCyYX3nZBvf6cAIx93T+c/OS2HFAYskSZc= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= -google.golang.org/appengine v1.6.8 h1:IhEN5q69dyKagZPYMSdIjS2HqprW324FRQZJcGqPAsM= -google.golang.org/appengine v1.6.8/go.mod h1:1jJ3jBArFh5pcgW8gCtRJnepW8FzD1V44FJffLiz/Ds= google.golang.org/genproto v0.0.0-20180518175338-11a468237815/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= @@ -1082,10 +1080,10 @@ google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEY google.golang.org/genproto v0.0.0-20210624195500-8bfb893ecb84/go.mod h1:SzzZ/N+nwJDaO1kznhnlzqS8ocJICar6hYhVyhi++24= google.golang.org/genproto v0.0.0-20240325203815-454cdb8f5daa h1:ePqxpG3LVx+feAUOx8YmR5T7rc0rdzK8DyxM8cQ9zq0= google.golang.org/genproto v0.0.0-20240325203815-454cdb8f5daa/go.mod h1:CnZenrTdRJb7jc+jOm0Rkywq+9wh0QC4U8tyiRbEPPM= -google.golang.org/genproto/googleapis/api v0.0.0-20240318140521-94a12d6c2237 h1:RFiFrvy37/mpSpdySBDrUdipW/dHwsRwh3J3+A9VgT4= -google.golang.org/genproto/googleapis/api v0.0.0-20240318140521-94a12d6c2237/go.mod h1:Z5Iiy3jtmioajWHDGFk7CeugTyHtPvMHA4UTmUkyalE= -google.golang.org/genproto/googleapis/rpc v0.0.0-20240318140521-94a12d6c2237 h1:NnYq6UN9ReLM9/Y01KWNOWyI5xQ9kbIms5GGJVwS/Yc= -google.golang.org/genproto/googleapis/rpc v0.0.0-20240318140521-94a12d6c2237/go.mod h1:WtryC6hu0hhx87FDGxWCDptyssuo68sk10vYjF+T9fY= +google.golang.org/genproto/googleapis/api v0.0.0-20240528184218-531527333157 h1:7whR9kGa5LUwFtpLm2ArCEejtnxlGeLbAyjFY8sGNFw= +google.golang.org/genproto/googleapis/api v0.0.0-20240528184218-531527333157/go.mod h1:99sLkeliLXfdj2J75X3Ho+rrVCaJze0uwN7zDDkjPVU= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240604185151-ef581f913117 h1:1GBuWVLM/KMVUv1t1En5Gs+gFZCNd360GGb4sSxtrhU= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240604185151-ef581f913117/go.mod h1:EfXuqaE1J41VCDicxHzUDm+8rk+7ZdXzHV0IhO/I6s0= google.golang.org/grpc v1.12.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= @@ -1093,8 +1091,8 @@ google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQ google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= -google.golang.org/grpc v1.62.1 h1:B4n+nfKzOICUXMgyrNd19h/I9oH0L1pizfk1d4zSgTk= -google.golang.org/grpc v1.62.1/go.mod h1:IWTG0VlJLCh1SkC58F7np9ka9mx/WNkjl4PGJaiq+QE= +google.golang.org/grpc v1.65.0 h1:bs/cUb4lp1G5iImFFd3u5ixQzweKizoZJAwBNLR42lc= +google.golang.org/grpc v1.65.0/go.mod h1:WgYC2ypjlB0EiQi6wdKixMqukr6lBc0Vo+oOgjrM5ZQ= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= @@ -1106,8 +1104,8 @@ google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpAD google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= -google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg= -google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw= +google.golang.org/protobuf v1.36.0 h1:mjIs9gYtt56AzC4ZaffQuh88TZurBGhIJMBZGSxNerQ= +google.golang.org/protobuf v1.36.0/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= diff --git a/pkg/common/util/unsafe.go b/pkg/common/util/unsafe.go index f2d29d13973c4..f799c9863b322 100644 --- a/pkg/common/util/unsafe.go +++ b/pkg/common/util/unsafe.go @@ -31,6 +31,11 @@ func UnsafeToBytes[P *T, T any](p P) []byte { return unsafe.Slice((*byte)(unsafe.Pointer(p)), unsafe.Sizeof(zero)) } +// Wrapper of unsafe.Slice +func UnsafeToBytesWithLength[P *T, T any](p P, length int) []byte { + return unsafe.Slice((*byte)(unsafe.Pointer(p)), length) +} + func UnsafeSliceCast[B any, From []A, A any, To []B](from From) To { return unsafe.Slice( (*B)(unsafe.Pointer(unsafe.SliceData(from))), diff --git a/pkg/common/util/unsafe_test.go b/pkg/common/util/unsafe_test.go index ba1aaef581e22..5b9298206ddb9 100644 --- a/pkg/common/util/unsafe_test.go +++ b/pkg/common/util/unsafe_test.go @@ -44,6 +44,15 @@ func TestUnsafeToBytes(t *testing.T) { assert.Equal(t, 64*8, len(bs)) } +func TestUnsafeToBytesWithLength(t *testing.T) { + b := [10]byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9} + bs := UnsafeToBytesWithLength(&b[4], 4) + assert.Equal(t, 4, len(bs)) + for i := 4; i < 8; i++ { + assert.Equal(t, bs[i-4], byte(i)) + } +} + func TestUnsafeSliceCast(t *testing.T) { s := UnsafeSliceCast[int]([]int64{1, 2}) assert.Equal(t, []int{1, 2}, s) diff --git a/pkg/datalink/docx/docx_test.go b/pkg/datalink/docx/docx_test.go index dd607e07f04ca..da2450b339850 100644 --- a/pkg/datalink/docx/docx_test.go +++ b/pkg/datalink/docx/docx_test.go @@ -67,7 +67,6 @@ func TestOpenWordFileValidFile(t *testing.T) { if !strings.Contains(string(doc), "This is a word file") { t.Errorf("Error reading document.xml %s ", doc) } - fmt.Printf("%s", err) } func TestParseText(t *testing.T) { @@ -196,3 +195,19 @@ func TestParseTextFromReader(t *testing.T) { } //fmt.Printf(doc) } + +func TestWiki(t *testing.T) { + dat, err := os.ReadFile("testfiles/wiki.docx") + if err != nil { + t.Errorf("read file error %s", err) + } + + doctext, err := ParseTextFromReader(bytes.NewReader(dat), int64(len(dat))) + if err != nil { + t.Errorf("parsing test.docx should work \n %s", err) + } + + if !strings.Contains(doctext, "In the philosophy of religion") { + t.Errorf("parsed text does not contain expected text \n %s", doctext) + } +} diff --git a/pkg/datalink/docx/testfiles/wiki.docx b/pkg/datalink/docx/testfiles/wiki.docx new file mode 100644 index 0000000000000000000000000000000000000000..97176866ce5b7bbd35a3efbaf2ddf975c450e1f9 GIT binary patch literal 53154 zcmY(KQ;;Q0w53a3wrzIVwryMIlx^E)mu=g&ZQFJidj31}GWR7jBOlkdcC6StGL>Y( zA<#iUK%haCoy@gsR7w(4K|w$!AwfXU{=3x`wYPIIw{tO6^K>wG)@SgrwP{I}SKJap z4!irtNMjTr_7+9Ls62F_cA!hd6|2j7;nH4bypaR@c$?;lR9{dEhh#|1&Uo`B+-l_S z^=YNl6&1VD*U#20A_Xt=DcUJyxCL;Yh$oUruSp#dERR6ew`cp{%yED+9n}x<7@~&W ziR9}9rD}bE#8K76YoHR1hC-|GPyULiD_YiqaCN_Yk3(gh3oYuikN-X zA4EgdM53PpnaR=}U*i+XTg;H3I#6iX`~3m&^VIm)CiXjgVC|dKS5N7Q>sC;qsT^Gz zjqZ0UK`}~SqVcEqU%0X=95I1`ueGwB6T!(h(!x)4c>@ro|3%T0Qda2wKYF`hARuu6 z?S@X~HqML;|FhO4&B%Z=BM00Gh>ug0*|n;R6>k`boy!*s^#7T@Ew%HLD&6euQ5MtJ z!W8RgchN?4X_{^qglk2_41e27A(n?!B%?gTxln7m`qD4iL)uHowX#Q*)pQ)& zxJSJUwz^0e1qfXxIH0IzD3~#0aTOy{N}tWeRVH*~V|M=SQnJvkdSEyY7FAR= z@F0JNoasg#<^neJl`XU%NuJV^lg(&)%>rC^gO&Xq<_NEu8q)7{TH@Dzp(hq}Iqv?q zKoMbZs+RxReBnQVQ2rCh%-&SV$=<=4(bV3_{C`ZIlQg9;#DpyV<|Do=uMzo#3@tAG z8}u?tMhZP}z2lXQUEXFYmt$t{sMuB~pVp0Vk9X|JL12for48IPG%1uQJn=5jEDBB= z<7dkRbNe$_{5N+^YRKSS|KFuYN+6HvWPh|WE<9{~F1U0Sv%cJF1P|G8BRku2*epp- zyGm)nxri!?{6?6DP!e?~83OSMI?Y93AYo)OSyB^9MO!H$NH$7&0cd2}u(8~%0mRaJ z6-=xDW`92d9fXi;$q^%!(`<9N z@JOJnLdqE`?(G^Z`%yQ}PL6wXZs7V=@NMDnh-o+l3peG}LO})4+q!PM_QNF&W6R5j z+w{&90fH`O?X2}^->p+zRO!Zk**gV@WA6OGaG0Ewx90~-=ByNH^fKXW2-eOU?v5FjAP|34RF2Z#TQ zMQzG%iwU{=lNLhXCCU;?SQ$GhXjOPYbO*OSDZ6cXl2j%UqJ#O+&-Wt&gFB4zC6c#^ z_p^EOb4?fk)vREljxqI7IIiTl1KUy;HFml+b4Kb6QTeL^Lt_L*-_dYxvStO69>yg@ zQ>k{+o3zPf`hgJ2!C8@Z&Seu(Xt&wBv`fLkXa37vNc`HZ;eGf%RjFlt_m47@VpJ9Q zcb!F*XZajIyX4W!rFL2>0-^+%@i!~PlxLMC^+tpQmOL>Yvr*6ev;~9}oVUwMquB*Y zKG0Sdi*Jcc=!%fe;c*H6eHiu_%d~i3$Z!5d!l5eXmU;^WE2{abiCDF?@? zLa4pcN$K%h_oO-krAxr#?P84$A*#!adhN=20nL~zZ68_pz zD)KafPZJ_MT2q=^&9gJnH648g-B^q~c3c!Uii1$U4$u>Os$VJA{Mbs8ZaRlVZx*=U z$P5p*%xC)_uKaM9?vPx5?M*O*={GPo-xD!Ygn{igd+DhHGn#wkyy4kc>fSbriRr^z z(t;(5l0Ds7j9-j|_b^!>Pk8@UUapD$w=wd{Cm=^fKtL=z|6jqn+dG*t{#UiGw*Q6G z|2X5uS2`YFJ84(HZx98XniVf`K(C!LTwRJmoHE+X@y|7>7p$uX$`9!vt#9NjMNv>$ z;%sMZ91k>@3o5ZbxkY|7$V|4&`3FL#A?fGzH9etjLuZQ-1SqTiwA|a7eZ!7G)7zQh zKfU9LnwqTY-+B1iaSDj2x(bzSv_M5fzt4xPqi`tcrA!ufPxVbIDB3N2pYIjHkEauH z9r@0|G+Pm}l$jAHW>1zsZ8wL9vpE2kwopq6agK_r(#+N(Y4z=j(j^;{JGv^K9%nZv zufcyh%PYei2)?%&H1_hW^9NCvQxde(!{1NW$Hye!*{OapGdSvSx{E|*>iU1u{HlS^ z6243HahB7emQ9Xnz#CZ8owuQF0UQ@s&jM5YPtW}vxXk_$+r+N$D&M(Du@}MH*uS!r zGaY^ek;?%8R);c+gn{_haxfc)4WhSi6@8&6J*Gm zmaE*|e5#l={jyCL5g0WwvQk}b#X4&#(R>6$?R1YJUFMitd%ySVr|fiZ+5M;dUwuHA z1tvgbPiwig;iZPa$5oy3W}7nFGi|9E>q%Wj9?fLJMuLu84*o~R#HCRNk%%gkokE_M zyM0FHFF_N1uD^B()R38urnr-~0*lmAdP@uE1kuka(!YzleF;%t8Ir7N%RLK8O=(?Vnr6ag(6PR5 z*6L(TLyEMy|7<4a-9_$Wil=rng(rIv!JOqEaq(f%s@t46=_U9v62;{e+qA_tc!DSz zI^`XN)}mJJ4ZQb!=|Ra0GPF$09<3!rI;W)vphUVIw}*+4q-hzss199mhNf&^P$m&| zRS1P#+<-+S8!u&YgKhdMY)cKJ3mfi33dc_vcO2w|?xvXO({V8S*LGHA5hFhV#a&e% ztf-mQ5`ax^_buW*81>=T0{OQgZ=D5PWK7MVo0Flfva}2_+gGqmnE%NW z;NlM_m=!;N54z4;kwp3n(bGsNfO>W2-%SRasmwH7;VK0=uPdi896Qc@2 zib2hlm!}eUBfELsFIl3Q7#5lx_Af4~$EmXsT3k!GthGANbO8vD_yiEpl!9{x2Zz7| zb(^q5JFV4N8!_=8dC40zh zyG4364EVxjzQ1{H87KWYJA55b60V)VMiB|q=ThY}aQuy1V2y{9)GJO~TVrWqL|`R% zIlIfUW{kb~14n8GpZtoMl6ufBjxSfn7S65;Dh6rCk}SQFm_VF5%X&=WaWZpmshRX6 z_ftWG8J`?4pH9$M3W}>zMg<%)W0tuU;ETmd2wb-qOKJeE5Df^%wm4pF)}MKvWpO^a zru;$1gsL!U%S0@+!9_*gig&%KwpiNVVH%6|yn>gn*!^uekUpJK%nLwvWzDRI2B(5( zpnv?u%|VTcv4mj>OjD&s~NBZROSJ|)&_uVyWyoa)+)+8CO9 zw6Y+w43_d)DmdgvO(aLXDD!b8VKjL~JA}ODW&C zi|~>DO4kT6(b%o`^pIE7b7RHl`<^(KKVk35`#V|-|8T{oN~>fGO2z?~NImi6DgI;P zB;DA?Y5)#FpDQ)C7NJzyG0mjCJFkVGo6uVo1;>_AHFlgV@c@s2ZuIct>-7NTs zs|x)o#Rd#%N57XDkDjV`XnWUF7vrOt|b?GJ`)7$m}%YdPq zp5Xkx$g7*kxS&B1GRkDhV`B4n{G}yWBHJSegKEkW zH4m*Y;cX$tQ1gl9V|90)mrZrU!E+W4YjwZ4?va5aGqX7-klPTev04}sFX)(MTMM|F zcr#OrUJ5yc$Wj)FZoYc)`1MCC3?7eX3}=gj1w4rtX`+nVH-Rr}O)1GYev~Lc#o|cu z9K4%>Wwth5oBP&kUV#52PL2}bS+6<_pVdW1OUR;bI&0FACbo;VYPj-F)psLj5l${2 z35ZX!sr+qsvqo{pC1-HxKW`xlxlPL=GeJPtd;BMi3u)TCUs43A(uQ@(5^<#$q+#6Ol#*Nxl@m^GUu!k(co$5DPBAha-pgp0J^cri(;dwxiNBxRUaqLpFcz)ly#%{#YRq*;|9!jS5!=M?;doj`Os(^ z{)K6LSgK}izaydlby}aR-q}EYj1((2+Nc1VHCTgpZF}O!hL$Mny_7z|c>FEJMVwRU zC~SpTz#jsM%rpdf$cXx~Owvj&WxSF$sxGY---hur*8PcS)57UC$k7itkU(SN%(Rxg znxY`XLEpfAjV=}L#(XLLg{v-Xa(%4#191*sEH$Ec23s){`i?-9OsNQXcZY3Xep|Q# z^^L43%a)3hwVg-A7xHv(p36?0l;ZIUSvWi?3Ms6z&2QrQ$86ylpMgS|ixTHb9n z6rL2seljig9ad>K(O4B3CYTf=gL4!4k-|@LCLjwisHk(3wnPCM8gWU&cP0N`>l}3m z<*gqT$KlqwQb#KMHvxwv`Ig(vqy^661frL@xnXYjZ^VwKMoNhO*#7jDo6 z7^F0RrXll+>q5vaIIBVt#mpr5Y^;AA&&d*wig|kh0`;#hG)bQ+w)n*<;=s_6Ys$z+h>fpeY!U}JG0cd$0THNu%sKDP zG76V890Nh5W2xo&?3@Wa+}3V96lK`!bOoSgtAi4j)<5%$QrlsOkC-F|g$<(LAlmq6 zL&pQYk4}VqTtT)QB^P`e3vHzU^YvQHRv4%HLi0n-AMRZG^AG_fRyO-kll7ta?q55I zaht`hC8jVa)CgcVjdr8tG$E7v#UjbIyZPpIs~;6u6Z=-MC!<}{ zo85spiHZ0m`Uha<#vDp9Xy!QfCuy|G>8LT~nwe!>O0v?u)OYw^fPsy~EG^(sZ41II zxMSOzzEvZP|9VI2F@DMlDo1e>)FOP!^f21ko%zNfD?V5VMJKW;LpG%dx_SwwlD{Zr z3kFmsdd5td-;~oMfzG!q%)i(cG_c#|IPmlC(2R03>?dHE+Mrn?q=PK2d9vAI;Tsg2>a6Jz(-V?~ zs!=2(&S2nFa}Zk2Sk?__mJM%rL>0;VSK1u82mIS^iJtsFc;a&dheO1dUDz0ZC4x+^ zw}jbwR|7=DTydbGh(^-v{MSU+ni=!V?5Q4z64J^i^`r`L46>TH9nwjEJM9iqL?RD>kX_)EY{#9rQ z=cA>S*ehUP!-0oy-|MpiDo0S-9=W)cz=;M-kX9t+ER2}Tw(Z@1GaZ`?IIuMyJm`nfa@qIXr+EZvEaDMMKEznFyl9759&1~W3Z$XlE6;=r`jE=*^jnQJe(tR{}==-H-t zq+XRmj;$73#>TE=;^WV|z%fp)p)xcdjjijoCftKd>W<^OOP`xI{JEs>_;ZZK%Jb#x z#rX%vQO-TS>5*CCc}c*Y=l!q|1H)S^ttLLncb{7R9sJvQr0<70l5`jz4;j^=;9A5K zf;+>4fqIhUl|RHqdT6CWik2nV_?P4uDOsccxN7Chpc}n6ibI%?H=Xft>|3Ndvnuj? zDAj0zhT}grUGZS{<_I+`d8>sYB&(svR53G}pF=Z~B6K2oBX&}7UzpM>`BaE85aWw? zR%cQds*RK9^;^=1t7f00ZQWx!F6@W^Rsl&c(ClCIwH_5Ol5ghnhK{EIEYT>*Q}7oD zQ}%HFH1eA?v3J0xuJdO0o&;{-bQG=tn~UcoLUqIkQ($5_Oy#lo&~xcRY`QY(AMnq2 ziXOmH)W&TMO;66fzPwD_$M-MW&4qFKgXe({eNG}&%kCsU%8Cw>u>_b?Uj&3{M2GI$ zw~QKT27dOcZJBzw~4`ji{D|C!UNdK8N$^~ zS~7TQ3Wvc1XvgKw(0A~c3c)_;sYFmo&2o+6P1+A2b6+;4d5(SH_X=3{z2e{9DnbV6&(zN ze(oWh#>;GCjmEC8JWq2L`W>swO>m_&VM}0#zZGAMqPWGv^S!GVX%fr#Wt5!(KcUTM zwOvGuWy2QtD^WfYeb_K+ZmtIIJB7Fw0tyNF_Cbku_8E(Ykp#R>T_){Q?G@Ech}c#q$`TSQNsL zzE!F3f~^B!B{gKqYT`1SUb2wO6e6axK2DG67t3NqGxmPE8~O1LDuyITQBqTG1gtwd zEk(xkCIIFcfTj=@(z0nVD>5_L2u9Mi91)hb!qIBfr{nY6N4p*w9}jfh!DQV1$?r6l zgf^(=hx<`iOz%b@>m+h0i6^hsvvI}`hxnV027onuWi?K;fbqLYbHcid{KuFN<$KM! znx*WB_a8k;qqZ$2bccIOwnx>Ee5jb@WRm$Wa!k*`OfkQ#5*pj3HXPEP?lwk1m3S6D zDqb$RVxFhA4+i^nzs}*cIO|O-eDX5f+8tNfcu(8&v3QMapHIc#W zkDh!hvE!iGCx_Y2nP^z_3j;G__Wj6tT$#c=l z8@C9HB)KOnE;oP;%c84ULu$GTyoJ+9>L}%?#Y3RdqlWaAJ}3I7w1GYA;M(FXb@Di96Q;b z`wrwF2$?ZvzktFo*7+go&+>hk&bo>9@|=gD%$i#)7?zA0CRWmz!_v-i^>WTlDH%Uv zTf%6Q+m0v{^`U2pWfNUdkyez8ikhP_n8t?sTykUq@gwaTgHDiS`&h7kiS=>gnv8lm zLMOjSZ1_y~TMoply-Ee{nn%O@T3^VQ_NFd<>XQYhWIKd^AS+|zL&q_n+fK9yM4zD2 zln8#=uxhn7A|r02JkaYslZw&mt!t?Q4s6NioUn?_u``i;rvWYGJLoRp=@8h|3lY<| z3py7sjgD{9FZ5pK#W;5~v%3uUUg#CLI=6qpS zHlnuU-q;|D$m?biVH?o6Kb;;8^ZfP!`=opy;%KH;ln+`-9T2~9JwJpq4{C{rZgcW} z{--tW=|XxPb%12VB)_xzLt>VnP)-L=6WReMQm-<8F-PN=Izg;4K=X*YmEah2!kxin z`B+I?9k4{ihfC2z4F5wub|e7lK(dvISHRo|AvI{$fh(@;pl-2w1Ls&-Rus7t{EbwA zVS?5MHmXk2+FycMeBUKOXA%@HuPIh{Dy`MwY; zZp<5Ax!Qg$8|puSB~1p7;R%g;x(6X;875~naSJvnF`26656ggV*Lv;rye-m(bf1}X zqe{amT{+w{g{+vQZZk(Jn?SQgW(QQ!ooARa?n@Xiu{?H@czm{~Qw}Q!A5#=ba37++ zQodU6K^BK?DDfpPQGG=nF@&>$J*I?U#~ua2Q*8i{K0Q>Dk0Q4%YZ`7i+NLvT6uP{| z`8}aD5SVajQS|SKuhHuWB;Sz&tt@fdR5x$Nq27~8I0n$oWXa= zN15&1)8e#@YiT4*CmN=Ts^A4*Z7iz-EO&lEdnTq-p~Z&08^^9+*)N2|Zw}fzjj=}z zEf|c{tGriMqep#@0A-ti6z8EKbW27@qLD^C&jh3KEP%52_JU~t@!llwucspFFPjl+12M;n;7yF*uZ92-sPJ$j=01r~zN7g@ z)9q)6w?b?)*K@ycN#MKV$~=em`Sq|RM8?(Y@Zyy=m;MV&hBZuj_Hh{ME4vu%pdVYp z0TdM0Oc)c{ax!{wsY0b73OC#8eNu_FBR+%KZu!4aMBU9CzF}@))}%zhi&!};a{I)@ zawmW~pVGWZ1x$<1bwhJ9bckN4TQoO-RL`TSQTLHrR42NLhwoZphgss&3~dOZ1sX6g!KcSr$$#mDr%+zZw;N>#7pbBC3)19qvE0!v zd4LXNXi|xHTnFQHbyPA7;l@J*chMWOR<4oI@oGyV)MKY)ed=WiH0Pjgnk#JoO?0Ke zwILaP2Jeix{n1Daa(9M#OQ{_a9f#to3(A_LrPRfRS(33iv&k-1f!cg2hR88 zFBeyf*esmp~4IBpv}#3q^LARiL%W^4wNY+DB93-FfazFw!%hA zIboS3MU^B3Yv_^b8lJ1F2=!JAZIH_r} z{)872HNtFi2O08F-em=NO!PWDq_*B>TabbK5HW3sO;;E0WHe#rHnlA`TY(T#gEU&n z(aIzLEkDpQzN5uq^GxL9a{tBDDAJmMJtK7WheVxnf;FwkAWOWm=u&=F&u`WTjb&`J%tQ2t>xt8i|DzmR(ki%pbKR%-t0f@O5AV*%({(NZ@lKv}tqS zWDbrGOa5W|M+4JZxN#H8(!P=)AZD}JRkfZP1YU$6g{(|=#w+9UN!XrgTe~-NfSj=0 z*+3y`2r&!iq({m)1Y=_4%ra9rutYg&%qSBf%aJ13@N`VvdgOz}apR=|)Z<%l2$7lm z6Sa@xgt_vprB_@F_JAKZnO=N-zY1lebcJ#>V;D3K9%!fW%{9*?295K{k*bw0h3973 zt`a?-!GRKzPb|jC#ryMxS&Bc`hcn@meS9J%S(;iHq<6F&n-)dmXDfxD$JA_S$ld>k z8dx*%1RYSBQ?ewm|8{a0OmB$a9~%@%NV>UHeu-OZE&)IWVbYz_ zTC3FJCP@Y+&@QH3dOG>tF}z;heoU0d8}X7K_l8rK+x z4|0%<6TF8Dox1ILq9k7qGmMA#_LLWhzd9W%Pv|f5=ez#vy`}Gze5J%#not%MotsKKlL_ij9`v_+d(ZQBQqKJt(k>*Kzo1c_S7*dUMP!;`svxXnjt0`eCVD0kv z@l5K+SA~aaE2swehTV|deg_LyPoY*b@re+|YWiT;O^cl)@0v7X$ccqEyO*O2V2P0lBFuq(EI}!}{V_KFP)G`tnHEPRnh6Zfw zLUr@27NxbAC@Kj>@w9L2ZAM3Y7Ajk6RuT90?Ie+P6)c$sm)k2d#w1|<_GE3n==v30 z8&PA^fSPmejLHJ67KP_Azzjz4ieMzzKsNrLvxvNB1 zb6nDZ8{ygoWIpM4+VBH4Bx-S@g1oqR=(wpvvE&AyOwJrj5>Lz+?Q5Gqlh?n2SYs16 zbZ9E^Lx1zI`TJGat={qrxGI{hR;wpUo@Mc-GT@7Re_M7w|4uG?%tH&M9wwns#{qG!B8ICP`54c#@WY`pYG6l82151>%%5MM&Mgn(p{oqQA?0FJTn{=iD}xaRq-0 z$FdTnaX0Bp7xiP~daEVZLDK#(bo=nFVF?wnYiFqqQRR52^8#sB#`M^ExF+%x$xbuF zuDsbHuT!x8D-P#^DqwwMM}X~M5BVMO-H}{|3uHwAlFe~BmG-@cr$k9~?lqj;X@LmP zq}1{aYea0f@A?~aPT!@n)BCi(21rG&#YYhS7$}{3W^fLpytMp{=^Qr_5(a&fiwf8B z?ZRwRjeu-(Ob5<EC;%~FTeQ1TWTAZ{Su8YamOTrm1~46>$v&1 zIwY_gt+Z?7`FiPmx{Xv@V$D{Q&M1d3DTkkzx2T4ncPsXpG$Yv07nNw-gy~5G+Xbm< zgiv#K%GCo!4JI7S(cAvE^n~suAnO5aq^|W9PMZ|*4(dhdv6%|< z_ahMe=0KIWj1z8E?^=m*HLS?PXNFn8_;^47l=S9L_9NX6_X+K@9qJ24aW5u37c&&KWK6^LrU_T8SyM&|8WwWC+uX4@b%WIGaeqgMz(=%zv| z75V3n$4TZem$q+k#+ut)w}s&M*_A3WL8! zB<8)p8Hl2}$3z!YO&^-_Q3kOvU*)(kyc-z&?LZ9|AQtq?PHV+p2%e! zJK{C?Msa$+SzE^ti=;Cl$$$CeEVh?4azCfB0EE;13ufQ6xyC!@?)WNpl?AZAxHdOl z$m-0)T(|gJ>wzFnA-fAIKb-A<`J5?*H78;18zN~u9t@DTZZhp4CC8q`P1 zU$JeeOJqfh@+;M!u~Gqt?mFCM=)v8Y%%|OLk2Q{mm8;F%MxWp?hw?vZ%=q{A&+4-8 z(_MJkUNZ)Sz@W(A#cYi@)B14*hTxBhYUt#ebvF6E{tG4jfoG^ZSgDM z+1h{H;%q}Oh@Z}&UUXilG$CbQ(UOeWard_vW{Fh2i!ux%l_S?4=Pm?qWXT~@Bm5kS zy>Oszkc!n8eqiIN+>Z_{#qBQZG8d8NI2@`|;YkR-Bb@o2usosFpME(%MgQ^byL_l^ zcmbydZ3d+RrGepQ#u2hg{=-)qT1|ugf(wC(;hENEv>G*h?}{`jriaPkkYcA-7Qxvj>6$0yMed0U_a zpH+>-+HlWMviK&NI%;cPgHt*FC@l2H#N&|)Y2xpnXP;<@nDP%v2?{kwdqkEK-x_b{ zp=D+IH>?|S`+v82Ed&qdQ4Bs?T25y-Lc$GdC=Y|)BG8;OsL8V(ip<3wK^g<4;!f0f zI%Y}euA5#GMf4n-YNc)k&_<sd^$-Oj(e;Y__ZJ6 zfCUF2!7jkeV_1N>cjP`IYwp>rq7(8=h)gJJ)ZE1gFsH>r3FG8jFSFc#?)wW&WN`PF_=EAhPS{tFrEwz4@$TCJgD zPezg8*8JkaW>Dm*Q}p}AJXa56c&6IZ*6Hs)Nf`me+!Q;eLbcbHVmo&b#!<9B&iA$y zlUNjsnYK=S@kpz-5V)Cnq&xP7JgJs5vtNA?e?x4nP>!xKZ75VKSa!R-e4kO+TdsD~ zL2=e_8a}%0092T@IeDa*;B-^Ux=j2qRuIJP_}2?2O5kS{J!FG%ZEejYE_1Zm@^eXD z+l>x81NQgHI5WFhGDY)qaw|VZM|5yH7@r2? zLa!66KTmo@wjTQ&ghw*WR+W>F@=k%zBD0>MnI*8IkS)p20|^fqCL=BuU4h!Vbu3eKNCQHV;)!PHiIRYFVtiS=Q?pb6K zbmIK0M~-*JBc>I`QBTrga=#_jFH0mP=H}@eO~WLy17;LcU(lC=plm)_pqOg&xzLy{3{f>MT8P^2KRb#Rn22d8Vg3WjRY8J?!nN$YoeZ%cjamTi$%T_Y`Wig$g< z{D_t!EGL!-ovJ!X&XxSWoa_Xa!s?UdWQKqypa?W#SR;3}FVD$z8%b33n@NZ3f?Y_=e` zPYO)OQJe!WD|>ayj2GGv60KIM>mqe>+@mzV7n9r%inK8{tVWD!TVn~hFm;OS`8&jy zKFm&?Nhw$J9s9(kS4Wf~i4-mP+(4_3F#-TrI<}RjOQUc!qlh66CFfz98lCdvsi1`< zoN7gra`g&~POQts1$jBdb)E$=Jwdbg0lvKrNNHilVk0CEym` zQbt&9AT}85HxspcMRTE>dYT!L%YVG&c);T$oE=?;!TpUjs%+v(y&jKQ&Mt9oq3WSb z#Mqv2OONypjl59{zenTS-z5!W7Q_85=U?4Z`Dzd+j_L^2EjJDlwmi`Dt-j%Q$Rj?bxMmG#G`MS_n=uIoR@u~sa9kA_pEnbT2)Aw0 z3Uvm4&)IAwr!fao3rwRolY(lu!y9x@Ao%5Nu;>-cS=nR@KgjnI>PVEf(=VULIJFu# zi_sQVH<3*;4s{A6c486Rb6xQj$9TO(q+Sk%ZjbAGVzzovZs0F(X>yg88U~#EQaMxi z2#yP?o^?7IFN*9S!%lXNjB;r74ONducM@fmz*@LO!!hd+Ij#nM{Q!7k#tIga+s0ldrETGfJ`MZQ(9OF83@yfx-Cu&b6eMzPmD$8Q z%)$#o-U1f8-G8@nH!PFdDnNMXntNH2m7c+a`ln09kos#bB$4O|km=;}lVC^Zbm!Md z2_!a^oQ&PB25>@K$Fg=jST{iz((Z_dZhHHn=Ms(vUwi!3l(}td72B@p7gW*PWY0+F z7=&Ph56Yt=y4GR{_L4|riCp8{`E%YYF0OG@2i@YyIiVt9uO?g5#D9GIIXx)388~jJ zI{a7+j>vWUUvCr}ph$oQWZDrUZ} zZc;1lTE=#Pkg^z2*&sR3zODdC_$oZRIiNKbGd@Uct<}v>1bc^#NUZE^6 zbPpv%(m;;tW3X8{u}<9 z4H?xaorBIi`b4uW28P@1j6VP|r1>$x%6RDxfn9=N9p+39&K6h!_)iW^hyM&gcb{E)5`oOkWqs@>` zwwZzBS^R%Lo)lNTFpGv03&s=u;gu%BD>ckl{9%w)g_a46piWHG*rqbtHRB|hXgTKj zB=Ucb*PQIZy662v&NsB28ER zF5Wp`pDMnNYPi@or9oLNIl)6tYgr-z@9nB~Sm^aW^pkge>;7uYxM^&WOIk zHJgsP`WuIEx(Nn*P2#$XXN7+p7rLu%cS__B4IG@OknE+V@(0oqlJ3jL1@0Ys>rr$q z(yIO#*w=&L!s;EZ{$?}IT{wusA+$wN3;Ayo7H^DyuItun2|bDMZ?K%u40F1vxG%`d ze!i_YpR9{Z0pmg`ocpx}rG^%~d0Ua!rx#?`rxRp>3~v@3lp6)_@{UGozky3zG70P8 z?&j@o?j!W^7gqbYQESlm3NM>4nRklTC9@vS_|(m8A8aCgeG7l@*Tin4xnttq*g4S~ zEm?6X)3=eIG5DD^&=~F~C4mnF25}uX2GVy<2WU7(z~RA}b2qUqT2|e=#OFw}ktp~x zp0+Ec{y-GuqN~#(FR3T@ZCD*r1RGF^7FzItm3?B<4hLiW@9Op`VCtK-#P>;5urgmd ziBvF~(__!`=0I7l>@uP_Y1qIPR(VUjJ@qTTPlPukEXV1#`I`BQb2BrQ_xpRcJoTD2n#lVSw4@L4Wx3 ziZjQxf}n0=7$jzMKE_4)8rfvY#a5K6XHY@^-|NoZ_9!u`5W2IM!rB{YIW< zBAKiFE%yTTjWor+aiax%93xoU-!ux&g&C_Tjgc1UavO`zMRc4E&FloHZxh{GO5TpE zpWCobQqv5~kDcMn$A}RqKxVslA!a#a_uD+s-QpB$@l*lrv|d!sc`1I5O~tAf3v^nu z+>4y5941}cM${LT@rps*Q9^>J`06ILU0SmP)^+QFKlOH~n-_K^0xT0p#G^!=7pCZ5 z9ArXFhPPB@llgKA_0PG$lQ8Z!OMBy$v#2I zdnuS4R_7BgqU*KFUfP9Ral;F;rb9;kX1r;21(cLW1mfZ^louxm_{z(r5)CtrF-r&| zyuRhslhr5|;enerR{LY7v#aiLIMv5N0VF9(XNF>N|?%%uuc5rJwMje)*#+}Rt^r0`{M^`S0<^Z9(^7m#ND zgyY}s&{prUZOl3Ug*K-I>z!0?hYXtgMdy#(o?N`NL*8y*S(q1>l5l6DKf{BR5w#v6 zP11gLFgatq`C0B<2x~GKs%n;)LKnF|lj5h9%|0}>&ix2u^#S}Q)SV5RGI2OwZTKE~ zc!sw3;uQiNubS;S0{~Q z`;*)|Z%p;^;Gcy~g2i=@tjY$P!WlAcT?ud3AiS{mvx{#Fl8c8#Igy#k1 zLp|D)?vGGaa=XNbDqY*P>2Mp%{fQQIyqf}OCHLBn<{M$zJ8HzILmBnN;QGD*Z0}0a zW``=Bp^2@O()Nt2RPF&km6;l2mAhaw{hSI|#R=kTneVxIM&;XrGK9Iw#9X}T8pgad zAK(?DmJkb~+>WwTY6kn0vK%NKc&jU^YP^5X6U!gaZBe2eXDnNC5f^4bgFY6U|B$;l z_ThN`YyEuyCFRS0)<3m;c9*v_P``a_a2X8<-R3cz(H!JE)OJR_1o9s3BUuNvFhsqq zVq{YD1^i(CAHv=`DvoX27slNoxP}11CAc+CaDq#4*T&tQpn>2T+}+&?!4llv-5O~0 zb@o2{-gEAK@B9AgF<5KPpUh%(t*%-%%QrJrO}KtPvysd|4SV&Np>o~ai8V|uq6JS= z!lidw3-M;1_=4^! zt36*WsE#MJ*LFjK;_gDc5Y%$gboMSid;8X_D$P;(IHo;b+CYs1@7*=zDyGnSm21Ek zW%uu$BvA*?H2i4O4m6oIS&aSLRj5Pg)y5G?VD9kBW&y(^yuRQ>GV!zi?Sd~48uwO| zi8ttz&fz}mEm^gTPj5>rR}1}z>z5gN58pewR^n!Ad;Qusa`=Ud`VK%w)j_&f;= zc3=C5`wo3dbkiY^-nNq*mB9rqbf#P7?Y0qEPt|bdcQ2q6RA(B7O)r^TXy6;@>y+v zSkex&XnC1zHZDbbN(lyH8!RTxK6}wCUbBB02bBT~ah#yKBopssy^)8Pw;kqsrO0lU zu1uPJPle01ygWDcr8Z%u4WW?8+t=!P&4!rU65KDCTW=nO4+csW*gqFBt>bxHFH6i* zuKGm6*}qBj2DH0n@Go|tmbhR4oMA?*B9Fc53N-v%mjDk@XkDQ(TitXag|K#u`nr zFsSoK#FwSX0UqpIG1&Wz+wP0;l?PseG#MaA_XA^|yqNCjJtNE8nHEOtX=O%kFLP@w zr>i-08nNbe4qVE4D`$cz0!|SY9;yzC?zAf5RT+y&OD}MEGSD|s{)H?N z`w2+TWjF;Fgh^4^WqhD|B6KiLGj6y;7upb^Z;r$WSw1LJp6EM2zd>)5pr_yZWi-_7 zfzj_#WrGGPob6Kz81VbeiIUb;YX)C^J~Fp+xaaJozf_;%=ATD^W*PQlQS)H?6LSUb z8Eob1loLg2%SBtubyKr(fr1<~eWz2vDJ^3Ju6(4NJeJHRkMZ=dQ6`p4co1b=-S!;s zNwkn_7ERlnE~CfDtXwWZ{fZ-WR0wc5soX8h38bPv`SHki&Tr<-&0DXjeMXRH)I>!2 zlffe7soxc{C1I9Q+)$PB$m;@n2b~O>V&cGj2UqrzLetI)b{9pt;pT$Y_8PdY`CZg? zU6lTNlngt8t4~uUndHB+zg3TnKgXgaF7Qkr)P}?c952R5Jsg?cs(xs4EuYGj&rVQl zl{s-?a6@rl$a0cFd21wCRYSGU`FY)wbF8A!xtudhl9a(75d7dwO4(WX=8&8z2kjS@ z5xW6bg5=q|9_sz+_Entkxw>bUzq=LK%I=Op?~olnH@$UH&3|hsBQ`p-xHs#)pLp)w zj;-gvOI#)Ezw_%NlkewxC{1#s8cGukB2zs+h71w#JEx)Loc_1aYF91e$0FFu-vRq2{7o!%b+b-QH0UuX>r-8dBa3|pRRM<{{!w|1$p{V4PQE0cS&{S;dU;k`(J^4o6C7yvFev`a~?6BOiV^6cDZQsITTFPSuyOI9mMh;&fjQ&oOu9<0eD)A=<`D z9{T-**D-Q8OTqdUz-0Y-!DbO`LzyNh*Jv<xPzvwN1?8UYA`_W_(1Op^Urh*AtI^@_IQ1ua#hb0*?+VO^|Qa zuk&=K=L=H@r>pV$$GC}Zq85YB>dVo@N4mSooK^$-IaPb4<1gW4GtDmo^^` zULk>Y?;yJUn{#i>!*%zaz-EMXt8)0~mny-&uT~G2o5-)cMUonl_s;gkzYX@2@o>lY zdWl|nCHegC9p@UK^8M^pzRNTT*b4S{E0&LprdQ|_3oxE~BL8R83;O^P91ch~lu<}G6jDexl)su@{N2mv-&m>RYPn-iN;kxt{?=Y{v81Do=kmdRMFN zxyEDh_}O(k(C?YhM*zHc=AmP|9qAKm>&kdfa6}lkMrlT#enAHZM}Q&>ScS% zZ2RN>^dYD{Jbe4e^SMs&#qPCjEtJ)7i9$cQ_+qkuKRji3b9DK*x%_$b_^QEk#g?ZS zKvB`#)2S537ZF|edSg|uHwsfU%1s$RssWqyN|#rE>(48{7nred|~(*4*z6$J!W`y72vtXz*igK z%Nxqt(H>_OO5p$evfpp+kMw-A`s|Ya`KiWlv146OsM7uXEIfthESpE^_|;ec+)t<~ z$A115cc3ddx@D>RRB?Yf4!Cu{^IEIZEtO5u)ut=O4W@tS;r6;?r=fefU*2=$*+=Co zy&QaPk07PJHJ0(zXU!+d;C*{0vk=Ug}AylpN9!Rse4}R^?tD2j9Je_`UxO03SmbtwKoSqA3 zev)d`o1MAn-@Cp(JnMJPU?2(d0e&X z6sS5MC|LYb!~S7iW$%UQ@$zlz! z#{lWNC;%_SQ&j|Ise_&p)o!gKEB_;s3N<56kV8Uq0A=Qmo{w4)}uHKa-Y_N&J5%;Wpm^p+)}{U)4xV!ve?ZiTa~Y zW)0>a>{HGJNGc^Hj{dJy9SB+|1ouBu|E7W~r8~E#^7tPF=bxw{BPf5VNbZ0){rHM>@ZWJGXD>W%#=uUo~THFsh&x6Z^Y{O6ZKz1 z1{S~Gi&?Tf>swl7T4LM*z;Pv?WHk094pJ^Mj#(Wn0TF}LrQ3m(_H_e0Y;)tesG zhqJ;;`@4H1d7Jk!>j1rGkOW1n{T9ojNYdxnbFQz%ua6FB;>@0thFDB1rDX5!a4@wU zaW!jyn^a-K*G}$_)|L#LmE8HCYq>Y9o^*Y#%8}L)WKE1)ZE*7(=3HcsZ+yS)=;O|t zhPcl|_t>d8I&5A)lCWKd*3Q}G%Lf0*ENUR=+td{;Z?}uPs^tX6NK@#sY@Nq6%rJvG z{${DcTt?9I%fLmE(*7vNnhSYkhs@6m71_~@ji-b%9(a23xpM^>6mpLGX-@%>B)QW$ z|4a!S&38pJivu4nW=Faz=BojrR}?J%PgvQH4*W)`7EP7dAyu&JxP=!!Rnt)xk>kOx{crG zvkRj=3n7Fxk7C2cnjcEol#UN3jwa3QZOL5igkLGsgWg3Q%itH$X~Sd>{LlgMY1Z+y z{cH`B;!VMo4|9&&|Lm#hL{zo&wk2WQm4;+G$yQu%$!hxoW*7)onU&bY>vs!@{toe`8dbO$}lphK}l`<7o? z6a*?8pJ5U9i7D@=z8%Y{${VD+^{3E2+~XMY_c=Al%OqVVq1Dr-yNfE`QN})^4X%Xy zuNRk?D2@mXlex-v%e6(VtEl%^x?N0fbJG_A?7P<`U!J*2-E+=w4&xxdXkwy3Zjakn zPiN1Jua(Q7;%V^greUAQv!g%%slk0t9O}&zG1GHSSpaN|N%fyuHOy z>eK3e81a|%WQAnFDXU%-A&-k^>55RHQwqX*u3q^an9k21oCmdS({NuNab*=)&E#7~ z)TVYsgKnEdydcI=Gs^|bqrsfQS*&NRNHUV?c0Pt2y0HI-&(k}z+Su^eqkzLuh!kos zdmcPZ6^y#oEESWUxGU)|$$Nh$C?mOEH)IW9RFFtYxkPo&6v9f|eXHG$G}^`7JTg{} zPCMB$>p-dq2d^jLZtqAJST1W%(-6^4TE*g&L0Sbgc8MX^ZUOJ^S;|{zC@_^qY&_Ov zTMcqX)#J`K&Ref_%oDIp&6?FrMZ=4ZQswZzn$$Ds(Fdz1pC}b%uLi)Xdx`xSy!G7B^8oTKXY1j|0@Dt}0OWaZEd)IIj3Q-L#e-yKc%mA>;pzhRk zk+1&ZMaPP7Y;1Cy(%js(V*c+B_|7&{+?_)f&CEQh7_wC2)RX#S?=I^#>ApT>`|_r8 z-dtQapSq3TKDO^Pn$J2E25kbmy1TfB;g6?a>Ch%vxyjLO)wRY;f^gqNGNfZ*G%=jC zsb8u-3DN1|j@a-Y7|leJ^Zs`9t6e*&DXJ0z!KrO>=c6dPyeli=;tx@0uhM-J<osP?xl0p)P&)ztrQY2Fzip!(f$VtX$Y#q5t*rxCI zxeCME{P~E+tMEsD6@>^@8JZm%HXWm5iStz;$pQ^%EejxqnZ-Y&+=cR8~x)8VcP8S|8bcvQ2Ew ze@pX|wJ#4vQr?->y0vQ7Nsj=f6BHiPM!idI%L$28X0OaTdEOnH%qlVr;5n9i#lfs>ZE!QC;oEZ-WoN@l)L zzhjJTGaP(qHkVnNU~bHcG8Je>ZL;x7%qpN6E=(q+)^=KPl-mNjByw&S%tYh9`=Hon zION8iA+zMHV?9ZqAXKbZQyfOp1=*YbHS<$W^U>~!*LxXbCMII`)8aPg3ynGK6-Q^+ zXeP8!>5ceWL{p?iSSQ^kci~-OVB${Q=MeUVB>3C{D>G4w<0VPXSHpRNZxjmF=t98~ zV*T%}sDHBXm`;Wbz85GMOj4&fX}iVaE%8QKAn~q5&KDMF-0o_-6dt}dnEhUf*w3_3 zF{7YHHytgm;SsKO!BaLdCboB)o*{DO`&Iq=2Ue>!50QL=8M(T%aKxuw-{|sUF@VKx z2xB9>$Orc}kk8)z^eK9oR~?lXe^{$O2@GeLIezf(o=wZWI^ho~t+T6Ns;i9&?94(- zgYeWrwLFiM3|vRB6KgK1M_5%AQY6|jOpO7}pI^KktvIqZMcbhHe7Y2T|hz7r0Gze#QN5u-5 zsO64}qS&o9rP2C+5XpHn*m|O5z;7QQ+_4y%tktdgcn_?J1K`a7oiY-PDF^9bX^NxS zlJg1_MT5c;bt7{e`wktnr)lm%d%MQwk4OEknJz9v^1CU|^I)gD8>8!abwIz3&(Jwi z_6eRU&}N5IZQql;$>uRv!}+yqpPt?Ps5tprh`-;Gtw|oivwkJ{06~4d zsmtkS?^bfkclM21Lg#nk-Cot??>QgclD|32QZ4N&bKM5grJHbx#Ib+)o!&EV9zVmZ zg79vjXmy2U1KqN|J%CBf&Qx2>enI<&d7= zTx!zZ)c;sjB7UH-A;c8bj3Q& z$1iKjZweyTv;;sz2Qi&S2F^F!ZpF!+hIEweatC(&Ig`?=Yr#Rd9=kOmYc84F@Ey>0 zu>|pw2Qf@?2jip;TMu14#zD9O&W+6n=&GV$4k|r{xN$pcF<0hDJ7Zuh__J$qe^tJl zhrZz^YMB_@5_-=YsEHwWpfJp*-$U|i&jVUJWbH>?$XYZso&JWx6EbclZ(6ypB(MZU zMFL38e#6ay-MQEw`5~(w_ZPaLJ5(p%^`i0S6**pJ4`VpZP0=jt_u!(-+4@WWH!LDDeKBP?oT?SPlFTKZO98F)VQ0NTH?|eM+3a*>(c$UK_pQo690Fj}*vl4kXoEd6jqA zqVT|~37xK!1FOzdQpNi^hBu}RtC7h}$vsTZ`6NCCl#@Rld8w)$= z6<3#N))|DXjhl<;D>2))MR@&5H$3=o^mlsaztZ*pNtagolYWDBq<&ie-93>!eqQxB zhb3<S*H>u8xA*m2A^~c16)fJ>2 z&~97@yYK?$c0w#7kSo}CR-B?N09qm-+y z0i>N7R)HPaIxeo*puLK8JN9Xd898WHyo(SoD~#X$g-4Q|rULJlW%Aej$+(qB8%Vn0 z;9FDPOu#vnibnoOZO!&C$$tssXMlF(6Y)fXTq0p7-f3B~6_#S~b$_jUI)AzF!%wFp zHX}2RUUaON?>?ZL&zsN5=ivaf@@K20>}M1!_fa>cOHCz)(&SK$&bkB){p zYM)P`G-Q{u`;7llwXyZ}Oi7S9G?QVtFgw9+Sw63;jZmU{-f=q1VR}CO&_R*h2*!1K zZj$M$-u{8ey0R*7-TDR}!5;p#3o@`#M=Gn5 zr?fzNJdB>S?mDE0_&nv&vt!2QCy27aM4oRhoP>gR? z*AxXflU7($8asa8PgoBwq&cVftzqxpjGu;~nliSs$LEpvg>GQA8#cbSR!I@PKHbWr zPZo4e3@}rRsCXEQVA1)v#BZSK@6*u`_>Cm~P?oLB4O#M{Lm*$E3mKocgXu$Ak)+5~j7Dkpkg%o!cD#=wZcceV z(hq@5Hi#<<#gCFbLF0!GZ~vPOL$%;P*(OW#|G(HIM2Ap2A_1+jnL^S6zUtEuM^fc# zN+vnw)kq(Oiz6pzKf&!}&1SN|^i5h9y)|@1<;|Ma5@M*J%ROkR%eXzXwPNrhBwc;V z4oDAaHpXorjyo-Un`^Bkg1#Nt7x*QBc+DR`OFbT>wEUKGQE|$R1D!he7muGVZs3O77zOH!1}+;sU4KqF~GQW92nWV zI7bDvg*5BTioX{uMOsk&e^ygMl=i}BnB=pxB7y13*#G6=g@1y3a(r&^^afva_@JEE zcyL~TciVSdL!ARzy{k)~+!?_bdFvgG_0qvPsy%{G^K*q}0kWr4DwIj?R#Y{h-~{86 zEjrE3kJniehXkIze{@TtUa_fJDsA}q9Kr#m7%;P?THe-8AaQ;X*7TDDK1kUX@Or9;_AVX`R&~( zZrR9)*;klaJc(qE>`4UFGovIeTto>J)1!bN(DLM1?37J!StIUcHSbX*%>}+{6Ttl- zL(LRoYnvPkC0&J*rbC#%Up#MfBpg@lm`@nQ!_?oeCA_~p9xz5(bij_@H&M*jL$yN* z{0N=pf)EeO_kc!;;1<%}KxxdbXT*+|b}C`+8#*@D8n%eb`Z#9%<2Sm4i!u8HdFqNX z%=F0*eP(U>%N;H9sK6WwIi{j`^!;07_DvU*hTlb45qLlFV7|hpicuPiFiGiS)uvX} z5PlhRdt1+f6)%uV?`^7?qm9Pb1|6%&B(;u9kzvg4mBm*=6Gcz%J8Z&!0ecZKh*!7C znL4(YnI0R=9V$`~fRPFJBQR4084K2!{VFJ8&?R(G*|Z5a%{4G!WdvvOgY?6{Y1E#b z`NGkOp)dvUJ$beX@r^7zVEpCShAt{_zovc@=`Y{RvIsu{nPd?n0#Bp9;!7|UF*1d{ ztUwu$J#_hs+ujDj&1%8v1n6)hOKi+!)DN!!5!4kb1m>p z!@M&6g{2u&@Z>${1}-@UW)z0}YZ$_OLGBK1D>8qIRY{iJngU1cS1(P4``Z4ZNy*v8 zo59>ZadQPpkh)RU2O_72CNst&y|z59((Uw{=@6bsKe;-wha{wcs{F}ZXROsT_vYA* zgsDiLtUv;%FOOd8q&5h2mh;@LcyjNCZmkQhR zVdT{Vyf&Yh9Tm4SR}qK?_(0R09Qze7#;Toqe18T9_}Y^b-qG5?>Pq46Ly{a7|4!oj z-$}MCz*3$Rx(S)gL|zLB)qts;VK8tjQ)`HB!YXs**LD)G1#=kqr_JYyaJ!aT|IW<2vpdG66?LrfuAP&I!|lOWDkN{8=2x{GwgFXr?M)mC_Vv1AMj7N}1EViQk7A9Vc|u#tu}|iWzoSslt)<7~<&wpB`Qzy@lI%sZ z-Dt*B=_m6U$Wh#oG#ZQee;DI#Qv^MqxkNvm{dI<(d_8+H1Ft`CJ~Ku4eca*?`B=Yg zp62&ksY0*X#z2eDinHE=42 zyYYulxM+B3RxNs+l;wdwQH7jm{RrqPzb9(b$)9bGPHolWy6d^ZP5D$o*$BVBSXY~Q z9hJ0`taX*0`raqZhCsRo%$~}Gk{AhqYEPONtiE)`k;MHf|+sj&LY84=|-N~i`Gy;m1~_>G%>r3|L7`Awc++L!BTPR zNE7QfCze9_eV=?M58Cerj9;9Adl*;F2D)U_BDuxuoz7@^R&{$k zunrF0S=oav(<{I+;fk(PoFgrC=|$KIxHVa&>A=h)86|ZxSI=oHonzl)mtw0%#t2K| zh@zxBjD{I+L>z@07aG7_xH3;cP9+ z_DG7Yrc~9X_0}BH=lm#?#;tUoQQoE_sbKJ*nrDo#o2K*U zJI&Gm@z}@6kAo^am{5F%~t)(r0$Z+IkUpwCiyDkyrWAfbY z|IcKia-Ls5C%1v|`7U~Z#D2b!P*Q&v>Kd}i>CccEFt{G1(iV=z^ zFZ9y}A87vSA7Uo&2=>Sqd9OTs14cuh$q>fneh>HLAxy&5!u+oG7qkQl9EHdZ6c&sG z8WMF6yi6XPr>IabvUMe$Ks25qxwux~p;`T<;Ke{^g+fDsSGl!E;Ic1g6v4)-G%_om zcN+XQIJI78%{WY`#DrJ`a+;)roF>!AMbMB~A!u9hf6!2^g?{lz6F|^B0_%A!xwBjQ z^yg^xMLN~FI(qDaQwC8lxIID$P>cZ01X0Kq%|+vA-bnfZ9|JewBS@k210tX|;aezW zIw)C7SUp5biIl=GVa805SZlgZuLOZjShscDmoP__Wt;H!fFdUSfIE}79m=+S1oGz2 z7zz%lMwr$h#CRBMRn&OcLm0?1qYu?VG{HkuUs>rQNJldQV;eqo7)#&KYf&&2ldqgc z``^T&B|yXtQHBtFF%Vb8hn+MEe~uu!%>|)C(f|42i-&x$*MLwBMeB|U3gAKTtnclV z&e-$=Jkd7cZ}S4a@?0&hd)0PS%z{u;o6EuoS_Ee5J)pb9vo6FOyO&*r37z}a%)*2$ zHOWL!ETwrxFiiVt{zvg1cm65fsoC|Q?ND~eKMD!;-y7^B8LaVIA&@J>X7EER42p~X zlG}~PDpBEk50#=8iwr{*lrQ@>)_VUKw!_kAxljcqV4SdvfNIoDx zh%DdUw|U(G@#B6-Q@ifb@@nL|Yrr~RuPN{TSv@?5V4$ezsb^Vu=*@O1`J1@_X_wE7 zu>$Jbw;{)kHLhLgP5bZ~J^Jy|K5_TtmqK>YB0iXdENd$ijv9D}L-$L-^6wL1wNEG> zpKF3=s$Hkk3?=5RL_C85b-I-|eYAeZ%O~N_Ija3IrQSOBsr<8;T470ehPkTj;RX=OEAB`!! zDGV46WreBIJ1+XrePte$?Dav^*pJN6FEQ}Az%9f4?MxsCAW)QQOvz9S>Kd2u&A?O0 zaFVHC5~OtC#$Ty5=zA7m(Vi-Yy6lA19O#YYR0Dz-kul70a7;&!1+2HZ4)80ualJt; z3tXCqchO>qSZeSWRZ~r}m#)@dR1-c;S}i9?WTI&~J-NBQezW~zw_x=+l;wRo{;Ct< z?lQ;6zNd5``|^Y^fF~6NK*J7z?O|r`W+4ttrTC)Pk#>&Qv}&M`PXEylhD)Bhp5 z@bzo7S#{adT-#Vy0bVGKRl|<45-RD1<9=uP21Z6hvF6nmVk+qN0F~6ZZRKtMbi=GB zZl)Wn4uy1p^x5A>#3+WF@cJKz%Hw6mB&99Ys#9`r{zVk#pZW=WJk{Yz0wP{3E8JJi{xJlV1ik_JsTOst@dDmF$TQ!3-xu-i z43PCL87eZ)DV>fhBe8NzCjx66+je>H3ae3QGTtXqOqrKcOi955WUn-36Dap$&Mv53d~kjEbk#2|b z0~q*Da2`bQV|nug zkp_g6gkl?nfa({xFOUgj(;CiF?NzGIEe@jSD~-ZzL}cnZh# z0~|k_G4xz+eVv8p#SpGa-|)!UXt_XkgBwPUb;pivPu|tyrh`I?ZFa~Gf?V;Zw#N$E z9$L-^ICRjP-NM9fGpItx5@3E~>T$dOrhdcbm)k3Sh-yNK4@PDtasw? zy-Y{vfL!Zmc8W&ry{uaJ9{_RQ3=n`%N`C{icjJ=M@vceo+9TdN47?0Y8|}ITZF7;B z;_*&j%CAtu4aYP~`)_!t{yc#v9ab-bA!Q{WQV!VO2qr{|y+e*gy)&ozmjdEw+Ot$# zD7ax=Edk+7-s#pBQKVQBVBAa3Wzn}qRNmNfd3auZ+oaEsZ0?c2=>>|h|E3ov6WE4k zg_!5T9QIw-LtEftE&5;SM7-p~=f%w)8*I>czwUQI?V}{QT2DdpIz3x_A_)Z*oNuj= zLm6HDCr^{)vl$w->zF#P5h|6{`K;kFbvR~&hneiczD(U`Orxn=orDJ&xFnQ=?Qcd4 zkPdD3hbsF^t!@-(%$HAvvq8V>^|YS7sNiB3?e$wNVGKilhGTO560qY>n&UbS2{)h7 zrPxr(L1Q8K;APj!PTtcJ?Mx!yY=;V>vwDyI)^qDk#=W!yu}k1S;2YNa$fV)l)f+J| zN3!j%A&e~(%t#xd?|dso6JR#C+#1GkSwTa|13mJFhOM#R(nR0zruNi8;nT7)T(V_( zrV?mDd;2r8vl|VI(6DsK*QovOpg#-M%xN&Gx>b= zw#!KzAG+?j4h1G@r3o@Ja&zz2a(MWw8k^D9a6rzN74rC@c`>pT@+KPi@;;OMuD#6W zZpLNP% zmDW=-@YBWIEI&S__SkMQEGWyw45n7Dq9VV-b|+4n`_V_loJ7Ic4Cj!mcxC0AL+fW& zMw^}u61lV&lc{GHyUZf3oPXubH zN=h^-{Mzp^vS?Bk2qu440Saa*f-bSWKRCRM*;b9%Ci=45JE{(OQ45mqGEw4efZ}FdR}A_Rejy(`Q|aW9WjXC zdS~-r*o>e&bkxOQbs_k+b{gd9i@@0!n(*NYhm+PKE$-ZfHdKP(dU<|-iv#KLS4VN*~8 z;T%`70xc~G*#XB>gBFV5KaS{q;C4OT*a?p%q_TDDD8^>1MrO|LI~WVUq87tb|DAF4 zCovmaEk{;G;Ly*Y*8Q=?EOsqi6_Ci9=d{VmMdmt!yyVEgwiGgKY!Qx)uGB7m*KwN@Sv86@LJE;4@Mh@6W%DCvYdIOij{SSsHz>Y@$P8F&zu*`=0(mx1zM1TnX?*Yd zfy(RH+O=ps9>MkW0)%|;snAlsI-+%PbnkvtIE9yF< z$MpQP8uE_8J6t57z!vi0^;g1Iq%|%>+_n`wR)Y6l!j8}Ad?6HE#foeBwDDFffXkV+ z46z((Nt-7&B8l#x7sP|Y00aaqARY>Q8IQQsxym}4%tAnoXx4&=UAwaPf#IL8C%44( ziLKy=9Bt|l!}febhOJb+9wc-@CSuvtn0+cDg4;fBN62qfhPR;!o$*!$Q0u5N9|PmX zvObF1H7k36?4=l|yRN}Pa!6Ww2>M+R0J#pDbmjsgPIOMNstQDoNLF=`L^3t?MZ2=d+HZItm58l>^& ztJsL*4^EYU^N@E1A|JnN6y!E&mIxInzdBAmP=)^SQNV_hd6?Qs61uqEXIb#8r+9pRDgsZITde3Sj1ToYDtIvzS4hLRO0iQ&UL5Z%=HCwBy?GsrPO1k}Gd@L2 zp+G9J#vi(*bTWJGU?f?fT!+aKgSYWWiA;~BQi4lga*AgS$@4$S_JcUEm_wq!Z)%S} zbTHynvD`4i#_Xtg#FG)i?C78h9qo>Nz4!mm{rQHTLDPI(P*Bd*2vEfTxj$di%EeOB z+}zB>Ndg)aON3smf zklp$sIRQ97;y(TeIrU4W7a@vo{op)|5pnbD?R zHJ7vQe|>JS4WwINIlYYnZEtzj>vz5GP41p$yn@ePA6|~#+n;kLM_2W(9zkdA)zz5i z=Q+2KE&OYp%R++82KepWofYIw8LNBKFF?$U6!#3J91YA-cLSc~VfP6`Uf!qf`O`h4 z)@7QZy(rLm)ar9-YYMu74Qo{R^{XpFu*}t+wXfCoZ7g^7sH@Y}wE1J*0-e7-fUbMN z&(r&v+v?i2*1-3(Q1z{UksUb2+|S?twMoz4-Fka@)VH7;%+eZo?&t3*Y|v5KcI%N} z{L1%gJnEb{oD-4}-kh@Q<=@ijzP#(~)Sm%5vjTaY@+{6JBr5GoF;MVc*BBUtXD{W3 zFAvV|_*bD{t-NesO0!N53sl=X@@^1*ZHOFY^{}S`Ui;8#U-%{+r`zuPX&f(i19^be z^K%;3mCr3NPF9TC2og*DF`sUKyIJ#GZ(3cZ%jc$qpZh&^tl1J!L39MDzwYOKVGrM7y&sL8-(^Pg> z;D`gp=Gtd{oy=PoPA%GJ-+FWKlgUj23JEy;!8OYk7>MP=v(N3BpjCqr^={Ve^>u-X z@ltTr&Ca#)GuX$e_2qV*qV6t@?}nWn{Nh*peA|Dr)OUBzvFh+ZJ^v{(P*Y>Q6aT^f zuFXS>%-;0Us#|`p$@E^ee6k#mb7BGTdRg7IHqJw9%N z`#{r2j^Nkqy(l0C{rtVP59vI{kwFC+COq z{TsUW=vr|SVEHjK877`Q1;)#3{pw~bq~nqhGr?h%FHc1yH%-GH8wmk$n8tdT+S)>5 zCRS-?BDEw7`ICU^pxbRmIJHW>U5jdV&Z_TwUY?!BK|Fhy6+YELL+=wJgf zL!>YmvTmfj2**adlVSPL3{t>^|0JX5Xf@8{jVw?w@Xf4q0~Ld5VtG!56oV zlkU^vK^ZSK>-H+Qx+bHJNVbx!)k;yT)|$W=V_rW6?X}hQ=JH`3@BD=4%Ar2i$*Az% z={>MxYv-dYcv!#eYYvY)MJ#EFUk|d^Zt?7X@v~lEIE5$E0}X7i*-r7Sy#et1&i583 z2lOffm{nrbS1sYHHT?;p!QQVvDSff3z+u(eL=2S4{^)Np4uX-@#_z~{vgwEzNj5za zBI~f5*CJLJ-mo+o-0y8^**8t{OlF)GH(Q*kn=$e1v%1}rOW7|OL_1V6o3!%f=!Tq| zV`4^%|AaN0oz?33c2vh?_{H28pY3D4GAiLaty=hs5$4rbotLhqEac|74O@zVmBV5OQu6fNonUHA%Y+d7yzJa=6` zIf><}0MVdl%^u=jDJH`h2kE^HUo`=aaQZT`w0^wP*fFDcH);E!aGV;c4S8fO>J=E? z3T%7m@q2BUxrn9!FS!9-sBqCf=74_UP5d6C7zfmii=U-UP#CIekO}%F$OPpnWFj6i zQ6tp?@9&2TfX3@&~2i5By&!{~qw~DF1gAgj~(= z4yk!dFud}=?V(8#teT-Pfqi~aYJi{7eY*w;!-0hTv4&yeR4(&?aeI9g`xv@QwW~|}R4tR(0p;g2 zH8)GkMQ~7gTlxf9F zD@*4l=qk?laAbe>VHCWxc=KdcJO0^;EaEy;we^fQbHWEZ?FnSo>hsLk+U*`{0;rrD z)w|sQ^^e4jDrMMB`L68*Oia6fOW`_m0PWf3_(_1L&nHs^T35u*sDG}^SFWe{F1Ot) z4K`OEj!Xe{ox3|A&&85Y_%El&nw-UXo?TK*J#RcQRRO*fBf?QE37ot+KPL!R*z)3C zC7k9X+K)CB&dI`4G!@RyGW9;55l1m}E9*V6gsZ29+k1Mjfcl}7gXkROvp^N^eQ8P< zVn)dt*kFKRm4iQJ&sCro6Yus2XH^W0FA6NyL|0Ox78IiL(3i2YpgdkeUsONC{IDTd z7IyuSI#b#=yxu}M)49e)kVp8-H0g;4=`_r2H9}c6c8TA5AU_91$%>6lIVHJfutx>< zz;;*g!Z}t%r|3g9(w6U3j3(qYlCswl@B>KMYSbQi*u9Be)E>6NL9?M@qMH&@mW02= zJay1_OQqf`J!0%ef|cjm2yG;v#bK%j#W^tE1g5FRmW~$RhNG#Dft3?t%1}DQ9WOKh zzl9Zg)h;>1@f1L{MeY5rv)77lTc-fw{oIVF=WYHhPXndiuI)s@m)albu1lyJdoivRVmN|KoIRPuVvTP>oOxu|6mYnUgw= z@d^#eKVplDhO~f?97nLXV5nx1KDms>R;#ijf;?n{0dAHpb3`QO>({Ewxdy?*g46BF z_5J75q}RpnKVM`!3);7nv%9`I6Hb_PUr)@tdwMhZZD4p1^&#UO&(wPA3g&pkT-txe z80vSd6Iyfm{2-yYI_GiN((%^M>1#6q)7nX9N)B%!>fVpvO@{i=`ogsb8b7xLE7N4> zDiX=yvAxNR9r+g2f|BhEP2__i&wJG`xFSIIe>2aMrx)@b)0|uvU*vPK?-VBNyIwNuVzsC@;zb6# z)_w2yipw(h{V8PO`hN)f3g9@FZAmdRGcz+=%rIh>#ms1nEM{hAW@cuV#bhzFEVjiv zzW2Si|Nh-|g9_fHa?T8`&*e0l5xuS6*m9Rv)KVuxX5bzl!?6ow2JLGD3u~rt`E;uK@on z@#MB!gPY^ty(<3n_w}k_Z%Sj5vvYyC%t?QS8P(OzVMpokvy0By!}V8idCJS=gz6(T z@uFQO7eb*Nq_z&*i>75WN}J>SN%55Fl1^Kmw5DZ?{`I@N24m-~Y$le2V=3R z+o|w`)2Vl}itasxy9+5^>svtlXEa>%_i$yPFjkh$&)gEoB#`bT(Q~ z5Z7AzOz^92XQ!ZNXAnK;Ud>g|`ku{=ly>RF#^|2UdCcEiziFUB^17ZLqBV9jO}LrP zKZLk1w0BkcU#qDO{CB@N0*1@C;4!K z6JKbui0sOdu`len@%HTWYkYL`=V%*#;xADY^nFkHsBut#O+>h=&ZhD&{s>-)1a*X0PkV7s8PO@B3o>M-nKunT%b>758@}k7snsKQ z<7!novn6T98$C%4iw(y{)NX@av}QKc-Y6cMYQl!F=SHiPh0{hGKwgrALZp3N$j_|kUaq1_#aRoOLLT{?(m`aEvv?Ah56b5=e9 zAsCPmGbOr>i+ho0Q02{+e2N>AgPEI-SnmXsn(Tuyj#WKz{vY)gWP zSUPMz=O*gW<_cF;v1gUh@Y=Tw}E3A&PN|dXUm-ujh z93qQfzLv~?r~PJXbkXzE%x7x7+_$XNi*8i=F~_20vF;gc2mZHP2A1FJ$jDp3fb2VlyoP>l4(%NT zOB0y=1j;a^wB0rr3k#0(-m-D^Q$|S-x+%raQI9Q$iQ#(JqkR>t-TU^qbm>lHN9N;m z93e5%(VDjB1r2GZlhJ5$-?|Zs&#LXm0Lck(PHFpaeNCpn=9ke`OsYom{#6J&d3tU4 z-4%jGp0?))CXDG|vA>)c9Iy<4$^{ZbVX~u*Uw;cL%u2~h6`$)MPXt_5$Jo-&hL!*V z#Fc`T4|<4~YWz_V{?+uN%eNJJul2&-E5u`6mibPWpSoO*4m(>)VjB3(7@gpgWt1EE zhIMub1Kp>bwK01^+6LO%DVoQz1KstmI4~QT^YVcRze>n<4Y@Zm>xJAlc)hqCy)L%o z14(ccCZ8oXyLWAguMjuzoyV*TQ;feF;x-3)fg`j;i?ngS9WD7_1d~(MesPi7-O}O% za*L+k*)nC1{W9N;JG|v8VDb}cB@ghikvZP?%N(HHTh_^)bvfAW?58uYU2B)0u>Oi)Vi!58fy`oUyd!-r4OT44tus0Nh_RWiu`8!nmDQADIzq2WHpA7If&hrlvu(%>MT^57lFoKIM=~l}3Wj`#_G&rPs+1wcd$GaG) z*UKj^CXtFlVr&kRfKL>UA+5_rtr~s0g1EF@%~V~SPem(yDmvU*9GRYT!L8=Sts%UI zCj^%3y5KB2P$3nq8tIAx=hey-=%KAC-BmYg+j|Q$apHRos>mJ_yVabh)tJ{QGu3bo z)vyi~5psX_E#X^4-6s zn-iqa%&^SUCV6eEaSKnP;;XyO+9aOh5Yr7=1FqGUSt(J&*r2Y>eP^@4)@`~2km7Fj zxP{hAyioTp!sTKlQx_+FH|G?n*s<{X7M|Z68;e?Z^Tfe}wnH?T%crj93$y6sWb9m@ z5Yy_^Y)|P3WQS@Qhibo8Z!!ZQMR%K9*nPL6KnI>(frkQkhiO>{M3=r<-r1H`D8nWM z1X_p7pX*B|$*Es>YOk`)&=dQ3#rWP+7H){~nAMJF7hWCkGD797a^%{hxzFF2?7V)} z>AYniZN0pt4*PPg6Pv@L%#WnPe$Kkoh`bm-ab@`Qk)%KNdN^pG%S%v2q&ZA#wPAFo z;&YO}23w#Vi77>!|Cp$zo6mV~w^uL$c~mW#JREZ>UU9Q}Gq(DCsVj2wK#CE2Nc_CRs6cg`8$)GcW#o8;1hS(&9+>_m}hf7)(6}pE^RU`3A+f@K40n9rizz9QW5Fz9q`IOkT zZ=|_Ml@tU-jGbi&V__C?pQdAVVx@YT=n}vJ0<$RUf_KQG;ny|Y2O?}QH0BlxLnjwu zE(U=?DS=8hrXP|}5R!yp_H^2T0K-H?6Op0&8-=XyGkRTV#b>LqzO05XI*=n@DwxQ` zXZJ89m-MF$gJ4j|KnUxc7*Ey0K>B785Lh2LBE+~&v0cZBF9BiyH6#k?zeAp{S^z`3 zUHlc2?=fVt-r`>&v!GDOgv0uf6#8;hCW=YHT&!trFmcvtAEHr2B309v#SLdLMiA_c zpvc7yZ^#3YRy$K%>BK<{Eulj4JkO+*d4OswO6{*KzluT#=V4_m(1g~59oU7yfH=R1 z)bNY%+=Re1Jbw~}7zu*;9oaoO8wMm5XB>LrE%PuCl2+n_4lxMwEtGy3QZRHe2z{2! z898f>SU8^maXME2SuqG>Dpw|59Z1RdW<=6}7r-y}GM7}NxQA{^pGW@0${gl^)?qwoaL z0LW1=D4oF1LH|L&+Dmr{gvGFmBi4E{ar=Q#ZbNa<5^Q0wWdnOm6{FIhntf+ zLB7ZzJ*Xf0KC&D=V@-8a9KEwRVq0DAYzQ^6aftE#3l1X-qZ=2~8$qMn9izk@Mg$U+ z@cNH5-adDpsQSJ?24m7za^Iwp^?O*$XjEIxg4e;aXs^XJc1ls%JcOU6%HEU7gGm``f5(bVtK zuO;3-n=uZYhpudgtuPBw;u)CgXqjgiSVrYLEReuRY97`CBFXEYek8w#m4 zRh&3aZHB2O*7qgX?|n_O^p3YOO1JXKXASAWq{BOwvl={i82C?=juqmOU5bn(2KTkX z>*win3HP^AHc?UikK!|wE}z7%_Zw3|m`{&ToD~~+MO@z~T~vr4%5jpOiWG-)Hom?k zVC_Bt@rKS*bJ3!i*%%qvrx(8BWGzC5|up9hL#*`&Yg^483w zku2ZzEe8aHCQn#_5WT|jR7s;PNH3)u!5Q~}ZOrUVZ+95SFjf{;l%)~71Z9dOZLR&) z4MZh7&hb8mL!8}Zh%(Il0R%if#1aYvhcd&dM@Led1gk*%kuA&|Wtt@8!6g{m#P3tZ z5bWHPewrvppr!fo?Fy*0o9MtC|2)d{6ly9-#u7M!DP94tl3|kN2(UzPepM~P9D^6G zIQt)r=k|d});=Q4hG0u`W_i*K5spTer*n`ZKGNM2LaQ+IqcAy?=`&C$HpDpu8WvfO zeR{ z9!)LT!tRyDq$FArwyu6Lm>?T>r=iB{o&Ft>9eHPjxgDr zTNO1W&_hN&OT0ry)4K1B^hBVChqlB?*hzm>+|scT{;k3TG{{`vHf&Hv?e+ckRq2b$ zzjUbP9Rqc|yZl?n`}jr!8`=Lr!mU6%%Xj$USt&(D|Jz?ypr`$@;!)+_5RYX*E71Q3 zD@KeCf`<>Fjzb#~Z>_y4JSHY@*6@Z`tb2?QT0i-Ke5sCmW5~3g&6u+0m5pk$vulUw z?ESv^6T@I??TvH09>67iq@G~OmxCwZ0BUUwFnaUY!L5-Ev{g3k8$vO$!#n+=@`2Ab zVjeR>fbEp*L*PCe%SHzW zDvXl=#agCnw@hP4NrT=`3%ow0NF{)0g|!GNkfB{N9XgCN3x*NXW(aLUSA@QkX*moL zcL*mMXOL{3{JJ%b(VCQrKh*P zKDYjhZZraYZT;-wc`&K zJYNHOpjpZdW4c(&Q&kV9$A_>Et1&fhLoVKygSrJPJ|RCfOSya=9;jfd=K98Pzu`Tq zk(}fTt6kV39!>gmk+9}2;r(hJQtecjAc*wqAOm6EH!IO*Bd2xt9h|mKxF#b{ic4A> z9ixfG3m2A=;7_vGq=v!Es>Vhwh6v2S;>0MuRPN*Y3P?nfnqeZ!dUMdFjiH20DU^+3 z{HgF{&@J{!ke;a15XF`E$;(xT&mv4cDx{U;5!6_{a1{t^-3g-PoQe6CBJLWm#x!FL zpgWr}L##;aN5)lGOEbxfkW?A=zzJ#O{RPqJPY3J?+$yxPqdzHP(l*ow(wQ?C((+*c z$<%r;)R_ZRAobLyWRi%*NEY1BXngK&NrC6HOpx;9T&O zjF~%-a$UJ zu&A3}?I!Cq-ghu;z@687!+N%1!JRNaF5v3yzkNam43UPLjJgM2Gt~# z8*|e@oRkH-LchpO#?YGs0VTmF7Lrq;qd#GLO+O`=m~ut2CRt3PUW}#&R=Ds=s!$`J z?@)6#^M5pJpoQmPP8HN%^|S0T6$D4IS*`_h%(|W3ci?bkN{k1fto9mNZYC6S2?NGy0{-ZPm4ycqu4X6~LBnTeo$QRGJB@JtV zp(wkhWBhm;X#=p!4x( zl}=K@wESG*Tp|3z7OVo9ky;r^XDi%*9IR&4rz`poY>V2Rft zh9NI#=pSUFxM`&V8=CpAxFBiMvBt-g1Th3W808Kn=?j-MT$>BLwX$)kjjOdht%n%3Jg}wks15(>2uvR zGM?Cc8d`2-T8;>f?8^}4)h_HQD93h++I;kB^&wMQaC7s^wCh8z6(D@O1BL;&#a;%d zw9=5K>UP&>VO*8y`3YpUu}{9o;}DIj!-_wDuov zb)cnTLL~2oe3GcXAjPx9qVR)`BTRZj&Yi&jYfybzOnk~mi3R+Bi*fPz{rTpfPtO+r z7Q+cmv>ebY`!pgN{Tuu@?jAB@VkPh~h-`$f$E!evEcrkdkiN@u4)0&_xOOo9cp*PZ zvxl6A@q?6LsGo(v5$M@}kwWhOdl2St$Ap1po^GJ2Glrj%+>e9#KGY%pb__?Prpu5k zw^4|R@aC?JE*i!K>jrvC+v|wo53yB0)jyJ02n&I-RZeO@Sm2~k`GZFOsm0s>cVO&) zl+67W15SF$pb;vX59>=>0$Rbrt zIk41>amB^z^mEV3Gz%Q`4c2Zkc(0#9EHO`RqfGeDXV9FEz7Th<;A*=I?@ z7DP@3MPNhN7{NM2^m5(gt~Vd2lX7h~c3(>Zng#~P zs?AWsndoniUa5{LxcjSC7HbbuH%ZwL5LeDK8;4wVAffGY;IOG{IsR&{8H9k+HU9@|{s`aY__}IE z^rAYNefNI{`vfTEJ)aw}3R$oXUYJrc-X2xiH~^Q>>?7q1g;91N zmOiVinuQH0tR0T8RwTO|>+Eq602i73JN(l0yfNNCQV@L?rj#L({sWZVZ@CvMrvbq; z{pOspxjU2z^=b$d`MIyO7>A%Onji#Rd`lNXFPO~(7aHwj#^Pq#cR^RL%K zXQx{xmXB(0yH^biiTJY#V+p6AhCUp%gn{5ssxoj7Jnly+D2yuoGvp{FGk;a>-HxHWg_`IyN{H&x!90pYY zjVz)lFRh+sma_^CdmK5UzP(4PG5{|5!@0UMc-broJ^qmrcsR<>x|iq09Zn1*6dYR_ z_ID<$%5r3ueN@t=q5iB4keqWrz}jxDp$UULJaGP@c38wx6#VVoyG`P-2yse;H$G60 z9}@bH6^9h}Ui_z(toxSNIXEJ5W_CTI-4eeFz|g$d#WyOJ0uLW7RZqsQ~-{tDh(BHRd<#PhqXWqxA+w&HD{^HtkDgy678 zra;ph3Gf^Eh@?HcO^uS?&4<(HckC~J zy?1C8qx`I3gatX;N}PZcEG7uXOA~co6xF|9pCv`)IZ^&R!c0htZ&6ub36s2u`-sn= zEOl!xXgEkJP{J6q+7*pk5X8Cy<%Rs~wPc2Ipa#wxy%&_zU;t`pN5d7junHT??xNI2IS*H!Sb={LpgM%&#l#(V;>h*_lTehWC=Iq4 zi(kl`7b7VpDV3EJ0k{P{>^rI?$b>fMYl#4)jPw%3{hp+!6BatuI<=i;<1MfcnjA^T ztcdS&00*Ky>=-s<^Wbu$_3jW$2Nd;hZZ>fi6kijHOA}IyE;3 z!k&`P|4PhE2<&TZBjm#t8B_$)b00oi>dfn3<vAP(7}J6BG9FOk_aCcakDh!mM&yqi@* z149Ct0-BcTu4vs@Po4B>5=Kko*lg^E-jkIb4m4ouMO)ka3bvXZ9O znIw|}PJ=h9BCsSjKBh~w%mI;1QJ@DCW-PQ^hM};^Z{lIJOh51CGej0i3qW9Lx1lgJ zFf*v|jR#&buw4(a>C;5~f$T#zNyZhwFUu06dG{&j!hW|w!ZonUP zVkrMxoh<@T9S=;m-`g4Hf51!+o?YTyOaBJ4$HIlaTZ>X?|2HW%hTB`;#O&7(kbf?? zDio45=miG>IfexRLH*ByD>GMDD|?H-)>$=cY&!qo!RXm8HEi28hi!3^-={sLsM}JY zvITBF0MhUc#O|Iu0U=*;n|wWDJjFci@&fbr>3eUF%h0GIa~9&4Jj7&UK{)1sIS_GL zdOEJVxs_7eS0fIVl&}aET0=oTKiqE}@0a_6oQ90n>7bq{RELM&ZOt0RiQ%-Zec|nK za1a4D<-GO3%em13_b$~V#Rm~uI!cn4Mre>CY!@42Pj$;=mwHjWjLjU*cG)s?D>gc! zzxEmLd7X3g>Isa18ti?Jm zIz~uJp~MNq9!gh?ix6~t4J=$)#m4ERuXhSQGMdeLoq!M3#NK@%z|WI=!}lb zz(6<>n=*Hgj(Vn|B)(W66gh^($yDag;sJ?_Ki@!uhrx%8;`Kz5gM!GuT!^M3NI#?m zZd72S!u}#2u_1f7`Mn@7c#?6hs434yLiWBTZoqoHOP}&1<%>VV((2IiKpdff^{Vo! z(ER)|o{kh^@@6?#+}n*XQbUWB^i~WkA6Ae_y+c`V0%jYSwZcqqY`!Ae`XhiOT(#N? zQn6;d{S(Vr-AI;CAinbE%81JSDncK#>9sx>iQd7CbYy8@07BS#`crNICw2;Jg@&3< z`A_lS#HY%Bs?n85`UbZ5YYEwN^}rpjoaqw6dV(M1{TzUS1BwKQaaSieSmr=&Z!fs; znuOD(TMV}7go`s%C~ofE+d-MkT#N2D!~_b#VAIG0>kPNIr`cbj^W zuGu664SRk-mSldeL;1BkXdCkm&iMI2;Jy<)4ghD;QSTE<(`^J^bC8>hIeh_l_clN* zH)+qh&G$i_Oi}ow+^ky1wKm-PbYFdd=Zc}c4mruIy*zS57k0x@N{O*zhJW+E++jwr zS-RI8fmnKQQxbdVx6M@;^w284*tqv^WZZ3`FRm7RU#o&Kl`G7=>tT<@B)N6*`Pd^3 zFncPerRu}^7_95chBJ^=I*gS&`R^OXCS&eP^tmq0>QjfiFXktiytMbs!V~SBUO8(n zEV9IWU2mIB<*)LeD@T1%+$J{^eks@$8;oy4ab22kp+Qc5*(N78qdy@6VP7r2Hm;w> z6r=_(v>YCX*SAKQ&bk9+a(cZ)*9>Mgc%J8zrTl)o9%)Mt_dVR(0?t$T-hneeA0HMl zMk)tJ%J0gV7C!=0ORNzsSC7!b&{9dFVG%S)ncr}&B3C?XeJXSMPIwTTe$Ty@E8C#| zyxLE64pl*brKVbde;EJ1+P|6^tNxb@K6GQ7;5_Y7rQnWPUk%p=_zUXmM`8k(~eBOT>~348IR@z2mrlMxyi zewb*ij0?D=%k?7t3dm<9(Xi+tGUIZ#2AN|Ny&={1!oznk8$S*o=WnHxkk`iG=S3f=*C}wH|*d+&=&Eo!>T=<+4tu^WHAb z^OAmw0yF>y*Hug!oRs%t=E?Zu{*6$l^(#XIl{g(;+s{5cM}`4#)*|l%e&Xlu$q*CT z=qD7Z0QBv2)WeMyW}O9dnl_D;_UuY3cyF&Nw++|Nm{G8U6;#Fp(KvA9bzG^=2W$*N zR5xZMT2Y*YvteiHksxgiF8Cf{kuYL-ahhb(IJZr5bm!s61&o_#H)3r~xgNJb+u(Uggr+Afd#;Ix z8!LXAgocyO?KlskNB*57SlQUPu11&JVV|B^mVM6f>FMtJ!XWbb&0Xmzv%!ZlHrL}q zf|WZr9d-vZ`h4!NDe_?4R=km$$pE78EXaykH0%ci%BNP=PM!iPD|M0c;^?sXv21F~ zItI&Zdzo1z%duX#AI9%4tRKy7y#`agxq|lc2;r|Yw^V2{;Z+m(EFS!En^w0eLM{3` zYKD#n{V8{XDXODYb$bqwV9e9?KM%i!e|LLL?V&m^^;D>ltoU|=|Ibr0NrbFz1PTPC zlMVy~^WUeWtEHKp8S`I9*1sy5=_oj?bK&&-(nRSvSbSc0i|CNJSiM{>K}2g6i)|9u zwWU!eIi=`9ymadGGuo^hgu7@Ntg9A=znFi^uqSLmIx)bZU5lVmc0L+b_(2IhO7uQ*1o{%@B>(oPbEx8hNU~jm^f-Fi>=pPvyo|R4_KZZaD0SIPyJxdf`?(FS z76#h`M!IdLwy)(74pomfxs5TDv{xMbV6q+6j0od4K3Wbb5ISL*yx6*ZgTVvk1?xIL z^vCk%9Lzcl0!FdhB^ZdyJ5fAw&yF<%K{M=n^I&$OG<^oLCXZM0lj`rO(k?li)zBil z@i#(G7U9qVFbHQ->K_`suZ^;-QZnT67~%{C^0NpKbck0g%50jZz zRuvf>kk|UPE0^l?oz%OtEyWV_PZ+>0u;*oD;=qPEAQN0Cqn$UU6O&6)6dSirUGdJT zGIT;paugTnxX>HlGSF zkf5;?9GJ7p$u%^o%+(eyN@sm>B8w?w#xgB{CRSNaJ#X5C#aM+l#>+!(qO5_3C1UfY z5NU(!A4*uvn!Y?P-5Kb*8^kidp~D^WoC|Xf4s39z-wf zn-kjm(F^mt&ok}s^QW&4Io{16V~Ks+zRwq}2EBTIC^AFmwYhIMdu!(of**IG)Dmro zP@ys#AYNgC{3jq6N{Vy_O_X>Il=X{?W+{9tNxC?N>%oBL7s`i zM|eydMdxG+$#!Iga53?*5K1nt$oXWi;8C0(wnmxC04XqmzC3WJdQxb2nyHh_)x?+_ ztO+JD*~XLQZ?pMDj)MwKwPL0nqAG2$WEt4@ec^uk^%}F}GGlf$8=?|lN~e|yMuQPb zSjjN%35GmKe=}uT(`_a+JBgUu#g~_grS*9Q$F0ZBQ^I}sa zSuH8h60A6`^Gt~ee%}l0h>h235ZxR!Ss{qBHXndv|4;dKn|9lWT=@mI4Qn%mx1R zz1BaHqO`&Z*m01+B!{WB+B%sKisUirn+MZu8ioo<^7OWH8NbmPl-+lnykVrIAYpUf z|B~V(#?W01UtXFx3eGRM`=+{=lGN2sx45`WT`YNdYgMisj-x0YE4!cOdGPzv;1o5w zXh1&u`5;{}MVeNj5zmUc5e@C2j70(RNg!*+IZD$xiwL!2R%BE=wa%ybOG{xHOL^+- zucm_PE}9<$0S;xG5zE|Iu6u!jEzMhHTnvVTd3MnUgi<~aVC}@3)u;*e+by>J^K;f0 zkfXoi?O)tn3QEDNZFN#?9{@b&wy;Qg7}GVoahT9`x{{%+yJds3P@uPf7rUJ)^YNukf@{0I_nG>a&#d(8KbDWT@Z1{Xh_=BmmA#45W-j2JH za7>Dm+5|>dAAkcu!>W3(>q->f+*dr=#nempIeS;sr z17NMAa@Pfni?eEFdAIGOw;OGF`>D4jy;ovzOaX{7WpJ*iXX*5-LHxYTeoqZ2pOq3T1UHjamzaL*H1EQ zrIC|@#-|iAR2$x3+qPBNL1gq{%^{?V2v6U-q*YByd{FfP-9wYooN-TPX?&oF#Ims5 zb1(quy<=m6II-}Yp+GwgSpnp7_9ZEpSWj)|C<`R3p=}XzzicrvvZ4Z`$A$8XL(^mF zhSgbC8#~@*+|XTNTz1V=lNfE+^uuyGbEItwW#nh%b zu-p{QqXkNn>mYABq||Gxi{b0n+;RQl{d~hZ{*T>s23Y4@r3abzA%gh6%=aJe7G8! z=O0iBT?s|TA6sF)JMLc{N000j-oc1D_yDaEGA@pAUYC*p;$L@1!gnaa=fNV-Sv4q@ zsuoh(Zf)+6q>A9!bmQX~`+BTcix3(?U>1($h$q!yuy13II86cQ6~h6MwwftkRLvZC zQp`tIl)Y=d8iY7XsLxC_P&`JL)r>itl)<%aaG9pgK}&1@O_$)r;ZiqH4>eF1(LZ$k zT?O_(>i#ML8$DAG%+FDvcDfZwn=C8LENJqz9?`(oL>);h<7QgIiqSjdq_=tsRKaoC zwg^9dx!Cz4SGI{z#m<$LR3!VIINm#Poms5<+9#8=I5p8rvYdeyJzo`m>bGt;wY&)z z7a_xKXDM~49%n>hP){RCnGE99Vlv5bYq&DDn9b~XpMUBl?W9&EkR-n|vMs_Pg59cj z%IFFt?6C10W2S5BL^I(DeMqb4i0D7UU6r0edDl=-moM+s5PDmA1N&!#Hh0P^>;eTF z1BK)KL--%bO3n_BE`POZc3i!EAPa`X*Pemfy>Zq&2uqAGX%JeQCE3q}?tYZEf?`R3r+XAZbxF#Z8D)HnK&~+Z}?kai`9#xp>zWJ_gxCv@BcPRi<=D%w*k({gafm?|BN~8 z-Rz9boPo14f3mx2txfwq9-Qv&QsH<>z2@*l4hW8>0%48M(URoNcOX za+fC4^i|nHWfAQ1XkR9y(S5gh-@O5^kiTEA0~5B8!ZBR9t^##Sx5XHan_!gM^#e&KDy2P0DlH-oO{KYuk!6iK<$fpCrF>R$^*&1}ne?f1=R48J zeDN^3h$&KbA_rPyHu@Q?u(AMNIc^@_v*jcONN!4*%MeAbPDQ`%@Av6`UYrE5%(TAg zfi&avwH6THA!95f;GAS&9ab3VdB6RBoG#+U{JIHP7lPcTlH%HkxeV{t(5payMbg}z zJ6)dtGA!U=Sb&5QEv0BP0;z|h9nk#*--%Q;mUF=u?+ELui{Z(0^bD{w6-?@kXg$KX_67)IEns*TFegXo7v zPI&mX1Mf#&b6x;Eu6iG0k(V#c z#^;W|j6137kE4fUi2U&px@Dw{sb_A5E0TM6)KY7DWZYNo2ViZwXLqlwu9gYKhhC#WN3j`I`Y4DV0dxSco6>K3msHAwO`A<>_+c@z7- zO}(sQ8n|nEZxepwJFbZN^3emYAc=sFzl#J7s-&;?%l-3>YxC`{5Z`A8_QdRWd(VB_ z_S(8U6AQx{T&%@D0rra;sK=|(N7SR0zN@J^5kv--!7jYjJ_HvV!+cyQYeO;iDc;~U z>W}=b5BPufSOsIt7Tu5_AVDM`AlSej3s|80uWkxB@&aC;_C{8=OxBKO7MB_}j_B$b zf}LL6w06@dG>jO~1D$MrMU0~KU|d!|N|LyW7;BwEWD*t7GpLNdE~EgmM_(@vz(OX0{B!Ugr%TS;4yGU$gFQT1uzW2Z#FiIV3mU64ZdG&uD$hS-EF$e@OmBF z7mOAd49?!7dBL(EfSb9C+&A@1+BerY@>I%RU~;*FoV2zN?1I>USt3jnG%gzJblA4b zY{N4|1#paoaHQ(OVe=AT`UP6=b~v5FuwWo75^sFel1~GBK@ciPk1_Ig{PvZ937azp)eEzkD^HAbr5q)y>A=^DIjP$$m~ z^Y+zhQF<{w5R$;Nj*L@;Gz|3^PS8=G*;+rV)D93pZy#k|w=sTPHvDXs%$Is;3&ZF* z!tt`1fawLbAD?>*`v}KLj3gjhAN>gbgkw;|fR#(1no(hQ!&5D2Q;ZjiuGuj`IMx_n zi!Sz^j?6()&q>G#G~-9^}8-vcywGN!e>F$zVoq9@QJJk7ocWk**c)F^B6;4cJ8W3eWB zcOA#Uv87_fiL%5$9SE|;ViY#VP2r}UOY7v;xP2k{P8VjVR-ucK@~{@4HRO4@2iN=3 zmv~&0LD<1$L7JoJ3@to>G?cuMc)q>z_&z2cMKtgBv3vi`PRXtD{np~QPY-l%_v?62 zfKY$#P$hN*ORHakGWnJdB{D5--~nF8O-g_8jNoE$f~#ir31ZC*z}=EMs5v>6F8`a( zfVH6Y``U)dmhG1H`%MND@()UT^wz)^zxDCUJwa&n*39!G!GlNO-TNnHcP0MMSpToJ zi}1q_AfSQ6EI(NKm27cr(oD;whuBSVoe|AKXisoIU2i3<$khxZPhR8xJlnA; z@6fq_&etv%R^cNET3WgIShZyfIY4{i>}>J7I&2fQSin4w=qt*spkF*Zo1l$7F3k++ z5%vC2^TX{5UyX#KJmEa6=7O(7^YnshnA#s$kC^R~Wp)0w^_;=aLB{=Ax<(5W5N{b*>jD0K;pWdJkDxe;d2y93+5u-^-sCM^tMo<-#2li^|u0U z6u~Z##9xG4ntykLz+=y=607*Jto1FdxzCtLB449XK_fd(X!|w$kg$50*H zyi@ElVsEgtskSIrMTs9a3vQhF;c-#v;V!Y4v8&tq&qq(5lz4MPW%c(7oIlSME5AD8 zymF>+`Zr%IfA3(e-CGac`X8Li+^YJ@itnXNo`rIH%g+Mffy*Tu_?I=YNo6c(#7g`( z?)Y}dKERuiNrV|RJkG%oSd<#MHfZU@slXQMDd3nJsM`Sp4L~slhG=KtU~p<)Nqk6U zL27XYUiALA{<@Rmy*Ffz#EDQ_+Fug#!fw3Ma0@t2cP=L+V^H-c?JLbf| zkhu%p@)^K@D9K}{+ysmw_QPgkOc0s4WGF#QdTGpkTEqa81aZVvi9DZ(7C4KQ<% zCQ;C}qffjbwEx%))s8j|gKh%)L 0 { + text += "\n" + } + text += tv + } } text += "\n" } diff --git a/pkg/fulltext/fixedbytepool.go b/pkg/fulltext/fixedbytepool.go new file mode 100644 index 0000000000000..5752c76c7e33c --- /dev/null +++ b/pkg/fulltext/fixedbytepool.go @@ -0,0 +1,494 @@ +// Copyright 2022 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 fulltext + +import ( + "errors" + "fmt" + "os" + "sort" + "sync" + "time" + + "github.com/matrixorigin/matrixone/pkg/common/moerr" + "github.com/matrixorigin/matrixone/pkg/common/util" + "github.com/matrixorigin/matrixone/pkg/vm/process" +) + +/* + FixedBytePool is the memory pool for store a fixed size byte slice. + data will be stored in multiple partitions. Each partition has the maximum capacity. + When data grow exceed the partition limit, new partition will be created. + + Data can be accessed by address which is uint64 with high 40 bits (partition ID) and low 24 bits (offset in partition). + + There is a memory limit of the pool. If memory in use exceed the limit, partitions will be spilled out to disk. + + + Assumption: + - During hash build, you won't do any delete. +*/ + +// 24 bits low bits - offset in partition (16MB) +// high bits - partition id +var LOWER_BIT_MASK = uint64(0xffffff) +var LOWER_BIT_SHIFT = uint64(24) + +// Least recently use +type Lru struct { + id uint64 + last_update time.Time +} + +// Partition which is able to spill/unspill. Data must be fixed size when init +type Partition struct { + proc *process.Process + id uint64 // id of the partition + nitem uint64 // number of item in partition + used uint64 // number of byte used in partition + capacity uint64 // total capacity of the partition (fixed when init) + refcnt uint64 // reference counter + spilled bool // partition spilled or not + spill_fpath string // filepath of the spill file + dsize uint64 // fixed data size of docvec []uint8 + cpos uint64 // current position for next item + data []byte // data in []byte + full bool // is partition full. If true, no more new item + last_update time.Time // last update time +} + +// FixedBytePool +type FixedBytePool struct { + proc *process.Process + partitions []*Partition // list of partitions + capacity uint64 // total capacity of all partitions + partition_cap uint64 // max capacity of partition (fixed when init) + dsize uint64 // data size (fixed when init) + mem_in_use uint64 // memory in use + mem_limit uint64 // memory limit to check with mem_in_use to see spill or not + spill_size uint64 // total number of spilled partitions for the next round, start from 2 and double each time with max 16. +} + +// FixedBytePoolIterator to tranverse the data in the pool +type FixedBytePoolIterator struct { + pool *FixedBytePool + idx int + offset uint64 +} + +// get offset from address +func GetPartitionOffset(addr uint64) uint64 { + return (addr & LOWER_BIT_MASK) +} + +// get partition ID from address +func GetPartitionId(addr uint64) uint64 { + return (addr >> LOWER_BIT_SHIFT) +} + +// convert partition id and offset into address +func GetPartitionAddr(partid uint64, offset uint64) uint64 { + return (partid << LOWER_BIT_SHIFT) | offset +} + +// New Partition with capacity, fixed data size +func NewPartition(proc *process.Process, id uint64, capacity uint64, dsize uint64) (*Partition, error) { + if capacity > uint64(LOWER_BIT_MASK) { + return nil, moerr.NewInternalError(proc.Ctx, "request capacity is larger than 16MB (24 bits)") + } + p := Partition{proc: proc, id: id, dsize: dsize} + err := p.alloc(capacity) + if err != nil { + return nil, err + } + + p.last_update = time.Now() + return &p, nil +} + +func (part *Partition) Id() uint64 { + return part.id +} + +// memory allocation with mpool.MPool +func (part *Partition) alloc(capacity uint64) (err error) { + part.data, err = part.proc.Mp().Alloc(int(capacity), false) + if err != nil { + return err + } + part.capacity = capacity + return nil +} + +// Close the partition +func (part *Partition) Close() { + if part.data != nil { + part.proc.Mp().Free(part.data) + part.data = nil + } + part.capacity = 0 + part.refcnt = 0 + + //delete the temp file + if part.spilled { + os.Remove(part.spill_fpath) + part.spilled = false + part.spill_fpath = "" + } +} + +// NewItem will return the []byte and address and set full is true when partition is full for next item +func (part *Partition) NewItem() (addr uint64, b []byte, err error) { + if part.cpos+part.dsize > part.capacity { + return 0, nil, moerr.NewInternalError(part.proc.Ctx, "Partition NewItem out of bound") + } + + if part.spilled { + return 0, nil, moerr.NewInternalError(part.proc.Ctx, "NewItem: partition is spillled") + } + + b = util.UnsafeToBytesWithLength(&part.data[part.cpos], int(part.dsize)) + addr = GetPartitionAddr(part.id, part.cpos) + part.cpos += part.dsize + part.used += part.dsize + part.refcnt++ + part.nitem++ + part.last_update = time.Now() + if part.cpos+part.dsize > part.capacity { + part.full = true + } + return addr, b, nil +} + +// GetItem with offset +func (part *Partition) GetItem(offset uint64) ([]byte, error) { + part.last_update = time.Now() + if part.spilled { + return nil, moerr.NewInternalError(part.proc.Ctx, "GetItem: partition is spillled") + } + if offset+part.dsize > part.used { + return nil, moerr.NewInternalError(part.proc.Ctx, "GetItem: offset out of bound") + } + + return util.UnsafeToBytesWithLength(&part.data[offset], int(part.dsize)), nil +} + +// FreeItem simply reduce reference count by one and free the data when refcnt == 0 +func (part *Partition) FreeItem(offfset uint64) (uint64, error) { + if part.refcnt == 0 { + return 0, moerr.NewInternalError(part.proc.Ctx, "FreeItem: refcnt = 0, double free") + } + + part.refcnt-- + ret := uint64(0) + if part.refcnt == 0 { + // no more reference delete the data + ret = part.capacity + if part.data != nil { + part.proc.Mp().Free(part.data) + part.data = nil + } + part.capacity = 0 + part.cpos = 0 + } + return ret, nil +} + +// Spill to spill the memory into disk and free up memory +func (part *Partition) Spill() error { + + if part.data == nil { + return nil + } + + f, err := os.CreateTemp("", "fulltext") + if err != nil { + return err + } + + defer f.Close() + + if _, err := f.Write(part.data); err != nil { + return err + } + + part.spilled = true + part.spill_fpath = f.Name() + part.proc.Mp().Free(part.data) + part.data = nil + return nil +} + +// read the spill file to memory and remove the temp file +// the state of the partition should be the same with spill/unspill so that +// partition can still perform NewItem() and GetItem() after unspill() +func (part *Partition) Unspill() error { + if !part.spilled { + return nil + } + + fpath := part.spill_fpath + + f, err := os.Open(fpath) + if err != nil { + return err + } + defer func() { + f.Close() + os.Remove(fpath) + part.spilled = false + part.spill_fpath = "" + }() + + // alloc memory with capacity + capacity := part.capacity + err = part.alloc(capacity) + if err != nil { + return err + } + + n, err := f.Read(part.data) + if err != nil { + return err + } + if uint64(n) != capacity { + return moerr.NewInternalError(part.proc.Ctx, "Spill file size not match with capacity") + } + + return nil +} + +// check partition is already spilled +func (part *Partition) Spilled() bool { + return part.spilled +} + +// check last update of the partition. Spill always choose LRU partitions +func (part *Partition) LastUpdate() time.Time { + return part.last_update +} + +// FixedBytePool +func NewFixedBytePool(proc *process.Process, dsize uint64, partition_cap uint64, mem_limit uint64) *FixedBytePool { + if partition_cap == 0 { + partition_cap = LOWER_BIT_MASK + } + + if mem_limit == 0 { + mem_limit = uint64(1024 * 1024 * 1024) // 1G + } + + pool := FixedBytePool{proc: proc, dsize: dsize, partition_cap: partition_cap, mem_limit: mem_limit, spill_size: 2} + pool.partitions = make([]*Partition, 0, 32) + return &pool +} + +// NewItem will find a available partition for NewItem. Usually the last item of partition slice +// If memory in use exceed the memory limit, spill +// If no avaiable partition, create a new partition +func (pool *FixedBytePool) NewItem() (addr uint64, b []byte, err error) { + // find last partition to new item + np := len(pool.partitions) + if np > 0 { + p := pool.partitions[np-1] + if !p.full { + return p.NewItem() + } + } + + if pool.mem_in_use+pool.partition_cap > pool.mem_limit { + // spill + err := pool.Spill() + if err != nil { + return 0, nil, err + } + } + + // partition not found and create new partition + id := uint64(len(pool.partitions)) + part, err := NewPartition(pool.proc, id, pool.partition_cap, pool.dsize) + if err != nil { + return 0, nil, err + } + + pool.partitions = append(pool.partitions, part) + pool.capacity += part.capacity + pool.mem_in_use += part.capacity + + return part.NewItem() +} + +// Getitem return item with partitions. If requested partition was spilled, unspill() +func (pool *FixedBytePool) GetItem(addr uint64) ([]byte, error) { + id := GetPartitionId(addr) + offset := GetPartitionOffset(addr) + + if id >= uint64(len(pool.partitions)) { + return nil, moerr.NewInternalError(pool.proc.Ctx, "GetItem: id out of bound") + } + + p := pool.partitions[id] + + if p.Spilled() { + err := p.Unspill() + if err != nil { + return nil, err + } + pool.mem_in_use += pool.partition_cap + } + + return p.GetItem(offset) +} + +// FreeItem call partition.FreeItem +func (pool *FixedBytePool) FreeItem(addr uint64) error { + id := GetPartitionId(addr) + offset := GetPartitionOffset(addr) + if id >= uint64(len(pool.partitions)) { + return moerr.NewInternalError(pool.proc.Ctx, "FreeItem: id out of bound") + } + + p := pool.partitions[id] + freesize, err := p.FreeItem(offset) + if err != nil { + return err + } + pool.mem_in_use -= freesize + return nil +} + +func (pool *FixedBytePool) String() string { + return fmt.Sprintf("FixedBytePool: capacity %d, part_cap %d, npart %d, dsize %d\n", + pool.capacity, pool.partition_cap, len(pool.partitions), pool.dsize) +} + +// Close the pool and cleanup memory and temp files +func (pool *FixedBytePool) Close() { + for i, p := range pool.partitions { + if p != nil { + p.Close() + pool.partitions[i] = nil + } + } +} + +// spill will find LRU partitions to spill and will double the number of partitions to spill for the next time +func (pool *FixedBytePool) Spill() error { + + // find unspilled partitions + lru := make([]Lru, 0, len(pool.partitions)) + + for _, p := range pool.partitions { + if p.Spilled() { + continue + } + lru = append(lru, Lru{id: p.Id(), last_update: p.LastUpdate()}) + } + + if len(lru) == 0 { + return nil + } + + sort.Slice(lru, func(i, j int) bool { + return lru[i].last_update.Before(lru[j].last_update) + }) + + //fmt.Printf("sorted %v\n", lru) + + // double the spill size every time + nspill := pool.spill_size + if nspill > uint64(len(lru)) { + nspill = uint64(len(lru)) + } + // max 16 spill size + if pool.spill_size < 16 { + pool.spill_size *= 2 + } + + // concurrent spill partitions + var wg sync.WaitGroup + var errs error + for i := 0; i < int(nspill); i++ { + wg.Add(1) + + go func() { + defer wg.Done() + err := pool.partitions[lru[i].id].Spill() + if err != nil { + errs = errors.Join(errs, err) + } + }() + } + + wg.Wait() + + if errs != nil { + return errs + } + + pool.mem_in_use -= uint64(nspill) * pool.partition_cap + + //fmt.Printf("%d spilled, mem in use %d\n", nspill, pool.mem_in_use) + return nil +} + +// Iterator +// +// After hash build/aggregate, all data will reside in partitions. You can use FixedBytePoolIterator to tranverse all data +// in partitions. We don't provide you the hash key here. just the values +// +// Iterator will free up the memory once it finish traverse a partition with partition.Close(). If not doing this, we will +// have OOM due to keep all partitions in memory. When partition was spilled into disk, iterator will read the file into +// memory. +func NewFixedBytePoolIterator(p *FixedBytePool) *FixedBytePoolIterator { + return &FixedBytePoolIterator{pool: p} +} + +// Get next []byte from the pool. If no more data, return nil []byte and nil error +func (it *FixedBytePoolIterator) Next() ([]byte, error) { + for { + if it.idx >= len(it.pool.partitions) { + break + } + + p := it.pool.partitions[it.idx] + if p.Spilled() { + err := p.Unspill() + if err != nil { + return nil, err + } + } + + if it.offset >= p.used { + // next partition + it.idx++ + it.offset = 0 + + // close partition + p.Close() + continue + } + + b, err := p.GetItem(it.offset) + if err != nil { + return nil, err + } + + it.offset += it.pool.dsize + return b, nil + } + + return nil, nil +} diff --git a/pkg/fulltext/fixedbytepool_test.go b/pkg/fulltext/fixedbytepool_test.go new file mode 100644 index 0000000000000..4a64de1dc0ea7 --- /dev/null +++ b/pkg/fulltext/fixedbytepool_test.go @@ -0,0 +1,353 @@ +// Copyright 2022 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 fulltext + +import ( + "context" + "fmt" + "testing" + + "github.com/matrixorigin/matrixone/pkg/common/mpool" + "github.com/matrixorigin/matrixone/pkg/defines" + "github.com/matrixorigin/matrixone/pkg/fileservice" + "github.com/matrixorigin/matrixone/pkg/vm/process" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestPool(t *testing.T) { + addrs := make([]uint64, 10) + + fmt.Printf("%v\n", t.TempDir()) + localFS, err := fileservice.NewLocalFS( + context.Background(), + defines.LocalFileServiceName, + t.TempDir(), + fileservice.DisabledCacheConfig, + nil, + ) + assert.Nil(t, err) + m := mpool.MustNewZeroNoFixed() + + proc := &process.Process{ + Base: &process.BaseProcess{ + FileService: localFS, + }, + Ctx: context.Background(), + } + proc.SetMPool(m) + + mp := NewFixedBytePool(proc, 2, 6, 0) + + // total 3 partitions + for i := 0; i < 10; i++ { + addr, data, err := mp.NewItem() + require.Nil(t, err) + + addrs[i] = addr + + for j := range data { + data[j] = byte(i) + } + //id := GetPartitionId(addr) + //offset := GetPartitionOffset(addr) + //fmt.Printf("ID %d, offset %d, data = %v\n", id, offset, data) + } + + for i, addr := range addrs { + data, err := mp.GetItem(addr) + require.Nil(t, err) + //id := GetPartitionId(addr) + //offset := GetPartitionOffset(addr) + //fmt.Printf("ID %d, offset %d, data = %v\n", id, offset, data) + + for j := range data { + require.Equal(t, byte(i), data[j]) + data[j] += 8 + } + } + + for i, addr := range addrs { + data, err := mp.GetItem(addr) + require.Nil(t, err) + //id := GetPartitionId(addr) + //offset := GetPartitionOffset(addr) + //fmt.Printf("ID %d, offset %d, data = %v\n", id, offset, data) + for j := range data { + require.Equal(t, byte(i+8), data[j]) + } + } + + for _, addr := range addrs { + err := mp.FreeItem(addr) + require.Nil(t, err) + } + + err = mp.FreeItem(addrs[0]) + //fmt.Println(err) + require.NotNil(t, err) +} + +func TestPoolSpill(t *testing.T) { + + addrs := make([]uint64, 10) + localFS, err := fileservice.NewLocalFS( + context.Background(), + defines.LocalFileServiceName, + t.TempDir(), + fileservice.DisabledCacheConfig, + nil, + ) + assert.Nil(t, err) + m := mpool.MustNewZeroNoFixed() + + proc := &process.Process{ + Base: &process.BaseProcess{ + FileService: localFS, + }, + Ctx: context.Background(), + } + proc.SetMPool(m) + + mp := NewFixedBytePool(proc, 2, 6, 19) + + for i := 0; i < 10; i++ { + addr, data, err := mp.NewItem() + require.Nil(t, err) + + addrs[i] = addr + + for j := range data { + data[j] = byte(i) + } + //id := GetPartitionId(addr) + //offset := GetPartitionOffset(addr) + //fmt.Printf("ID %d, offset %d, data = %v\n", id, offset, data) + } + + //mp.Spill() + + // New 10 more items + for i := 10; i < 20; i++ { + addr, data, err := mp.NewItem() + require.Nil(t, err) + + addrs = append(addrs, addr) + + for j := range data { + data[j] = byte(i) + } + //id := GetPartitionId(addr) + //offset := GetPartitionOffset(addr) + //fmt.Printf("ID %d, offset %d, data = %v\n", id, offset, data) + } + + // after spill and NewItem directly. It should unspill and GetItem + for i, addr := range addrs { + data, err := mp.GetItem(addr) + require.Nil(t, err) + //id := GetPartitionId(addr) + //offset := GetPartitionOffset(addr) + //fmt.Printf("ID %d, offset %d, data = %v\n", id, offset, data) + for j := range data { + require.Equal(t, byte(i), data[j]) + } + } + + i := 0 + it := NewFixedBytePoolIterator(mp) + for { + data, err := it.Next() + require.Nil(t, err) + if data == nil { + // EOF + break + } + //fmt.Printf("data = %v\n", data) + for j := range data { + require.Equal(t, byte(i), data[j]) + } + i++ + } + + mp.Close() + mp.Close() + mp.Close() + +} + +func TestPartitionSpill(t *testing.T) { + + addrs := make([]uint64, 10) + localFS, err := fileservice.NewLocalFS( + context.Background(), + defines.LocalFileServiceName, + t.TempDir(), + fileservice.DisabledCacheConfig, + nil, + ) + assert.Nil(t, err) + mp := mpool.MustNewZeroNoFixed() + + proc := &process.Process{ + Base: &process.BaseProcess{ + FileService: localFS, + }, + Ctx: context.Background(), + } + proc.SetMPool(mp) + + p, err := NewPartition(proc, 0, LOWER_BIT_MASK, 8) + require.Nil(t, err) + + for i := 0; i < 10; i++ { + addr, data, err := p.NewItem() + require.Nil(t, err) + + addrs[i] = addr + + for j := range data { + data[j] = byte(i) + } + //id := GetPartitionId(addr) + //offset := GetPartitionOffset(addr) + //fmt.Printf("ID %d, offset %d, data = %v\n", id, offset, data) + } + + // no effect + err = p.Unspill() + require.Nil(t, err) + + err = p.Spill() + require.Nil(t, err) + + err = p.Unspill() + require.Nil(t, err) + + for i, addr := range addrs { + data, err := p.GetItem(addr) + require.Nil(t, err) + //id := GetPartitionId(addr) + //offset := GetPartitionOffset(addr) + //fmt.Printf("ID %d, offset %d, data = %v\n", id, offset, data) + + for j := range data { + require.Equal(t, byte(i), data[j]) + } + } + +} + +func TestPartitionSpillError(t *testing.T) { + + addrs := make([]uint64, 10) + + localFS, err := fileservice.NewLocalFS( + context.Background(), + defines.LocalFileServiceName, + t.TempDir(), + fileservice.DisabledCacheConfig, + nil, + ) + assert.Nil(t, err) + mp := mpool.MustNewZeroNoFixed() + + proc := &process.Process{ + Base: &process.BaseProcess{ + FileService: localFS, + }, + Ctx: context.Background(), + } + proc.SetMPool(mp) + + p, err := NewPartition(proc, 0, LOWER_BIT_MASK, 8) + require.Nil(t, err) + + for i := 0; i < 10; i++ { + addr, data, err := p.NewItem() + require.Nil(t, err) + + addrs[i] = addr + + for j := range data { + data[j] = byte(i) + } + //id := GetPartitionId(addr) + //offset := GetPartitionOffset(addr) + //fmt.Printf("ID %d, offset %d, data = %v\n", id, offset, data) + } + + err = p.Unspill() + require.Nil(t, err) + + err = p.Spill() + require.Nil(t, err) + + _, err = p.GetItem(addrs[0]) + require.NotNil(t, err) + +} + +func TestPartitionSpillError2(t *testing.T) { + + addrs := make([]uint64, 10) + localFS, err := fileservice.NewLocalFS( + context.Background(), + defines.LocalFileServiceName, + t.TempDir(), + fileservice.DisabledCacheConfig, + nil, + ) + assert.Nil(t, err) + mp := mpool.MustNewZeroNoFixed() + + proc := &process.Process{ + Base: &process.BaseProcess{ + FileService: localFS, + }, + Ctx: context.Background(), + } + proc.SetMPool(mp) + p, err := NewPartition(proc, 0, LOWER_BIT_MASK, 8) + require.Nil(t, err) + + for i := 0; i < 10; i++ { + addr, data, err := p.NewItem() + require.Nil(t, err) + + addrs[i] = addr + + for j := range data { + data[j] = byte(i) + } + //id := GetPartitionId(addr) + //offset := GetPartitionOffset(addr) + //fmt.Printf("ID %d, offset %d, data = %v\n", id, offset, data) + } + + err = p.Unspill() + require.Nil(t, err) + + err = p.Spill() + require.Nil(t, err) + + _, _, err = p.NewItem() + require.NotNil(t, err) + + p.Close() + p.Close() + p.Close() + +} diff --git a/pkg/fulltext/fulltext.go b/pkg/fulltext/fulltext.go index f280f68676d8c..8c5bedce1ac1e 100644 --- a/pkg/fulltext/fulltext.go +++ b/pkg/fulltext/fulltext.go @@ -26,24 +26,24 @@ import ( /* 1. Parse the search string into list of pattern []*Pattern - 2. With list of pattern, run SQL to get all pattern stats and store in SearchAccum/WordAccum. + 2. With list of pattern, run SQL to get all pattern stats and store in vector []uint8. The value of vector is the document count of the Text node of + the pattern with index Pattern.Index (only Text or Star node will have valid index) + 3. []aggcnt is count(doc_id) group by doc_id of each Text node. Index of []aggcnt corresponds to Pattern.Index of Text or Star node. 3. foreach pattern in the list, call Eval() function to compute the rank score based on previous rank score and the accumulate of the current pattern and return rank score as result i.e. + aggcnt []int32 aggregate count for all document found for keywords in Patterns + docvec document vector []uint8 with the document count for keywords in patterns of a particular doc_id result := nil for p := range searchAccum.Pattern { - wordAccum := searchAccum.WordAccums[p.Text] - result = p.Eval(result, wordAccum) + result = p.Eval(result, docvec, aggcnt) } 4. return result as answer */ -func NewWordAccum() *WordAccum { - return &WordAccum{Words: make(map[any]*Word)} -} - +// Init Search Accum func NewSearchAccum(srctbl string, tblname string, pattern string, mode int64, params string) (*SearchAccum, error) { ps, err := ParsePattern(pattern, mode) @@ -51,9 +51,12 @@ func NewSearchAccum(srctbl string, tblname string, pattern string, mode int64, p return nil, err } - return &SearchAccum{SrcTblName: srctbl, TblName: tblname, Mode: mode, Pattern: ps, Params: params, WordAccums: make(map[string]*WordAccum)}, nil + nwords := GetResultCountFromPattern(ps) + return &SearchAccum{SrcTblName: srctbl, TblName: tblname, Mode: mode, + Pattern: ps, Params: params, Nkeywords: nwords}, nil } +// find pattern by operator func findPatternByOperator(ps []*Pattern, op int) []*Pattern { var result []*Pattern @@ -80,7 +83,7 @@ func findValuePattern(ps []*Pattern) []*Pattern { func (s *SearchAccum) PatternAnyPlus() bool { for _, p := range s.Pattern { - if p.Operator == PLUS { + if p.Operator == PLUS || p.Operator == JOIN { return true } } @@ -88,8 +91,8 @@ func (s *SearchAccum) PatternAnyPlus() bool { } // Evaluate the search string -func (s *SearchAccum) Eval() (map[any]float32, error) { - var result map[any]float32 +func (s *SearchAccum) Eval(docvec []uint8, aggcnt []int64) ([]float32, error) { + var result []float32 var err error if s.Nrow == 0 { @@ -97,7 +100,7 @@ func (s *SearchAccum) Eval() (map[any]float32, error) { } for _, p := range s.Pattern { - result, err = p.Eval(s, float32(1.0), result) + result, err = p.Eval(s, docvec, aggcnt, float32(1.0), result) if err != nil { return nil, err } @@ -109,10 +112,16 @@ func (s *SearchAccum) Eval() (map[any]float32, error) { // Pattern func (p *Pattern) String() string { if p.Operator == TEXT || p.Operator == STAR { - return fmt.Sprintf("(%s %s)", OperatorToString(p.Operator), p.Text) + return fmt.Sprintf("(%s %d %s)", OperatorToString(p.Operator), p.Index, p.Text) } - str := fmt.Sprintf("(%s ", OperatorToString(p.Operator)) + var str string + if p.Operator == JOIN { + str = fmt.Sprintf("(%s %d ", OperatorToString(p.Operator), p.Index) + } else { + str = fmt.Sprintf("(%s ", OperatorToString(p.Operator)) + + } for i, c := range p.Children { if i > 0 { str += " " @@ -126,7 +135,7 @@ func (p *Pattern) String() string { func (p *Pattern) StringWithPosition() string { if p.Operator == TEXT || p.Operator == STAR { - return fmt.Sprintf("(%s %d %s)", OperatorToString(p.Operator), p.Position, p.Text) + return fmt.Sprintf("(%s %d %d %s)", OperatorToString(p.Operator), p.Index, p.Position, p.Text) } str := fmt.Sprintf("(%s ", OperatorToString(p.Operator)) @@ -155,25 +164,29 @@ func (p *Pattern) GetLeafText(operator int) []string { } // Eval leaf node. compute the tfidf from the data in WordAccums and return result as map[doc_id]float32 -func (p *Pattern) EvalLeaf(s *SearchAccum, weight float32, result map[any]float32) (map[any]float32, error) { - key := p.Text - acc, ok := s.WordAccums[key] - if !ok { +func (p *Pattern) EvalLeaf(s *SearchAccum, docvec []uint8, aggcnt []int64, weight float32, result []float32) ([]float32, error) { + index := p.Index + cnt := docvec[index] + + if cnt == 0 { // never return nil result - result = make(map[any]float32) + result = []float32{} return result, nil } if result == nil { - result = make(map[any]float32) + result = []float32{} } - nmatch := float64(len(acc.Words)) + nmatch := float64(aggcnt[index]) idf := math.Log10(float64(s.Nrow) / nmatch) idfSq := float32(idf * idf) - for doc_id := range acc.Words { - tf := float32(acc.Words[doc_id].DocCount) - result[doc_id] = weight * tf * idfSq + tf := float32(docvec[index]) + score := weight * tf * idfSq + if len(result) > 0 { + result[0] = score + } else { + result = append(result, score) } return result, nil @@ -181,85 +194,71 @@ func (p *Pattern) EvalLeaf(s *SearchAccum, weight float32, result map[any]float3 // Eval Plus Plus operation. Basically AND operation between input argument and result from the previous Eval() // e.g. (+ (text apple)) (+ (text banana)) -func (p *Pattern) EvalPlusPlus(s *SearchAccum, arg, result map[any]float32) (map[any]float32, error) { +func (p *Pattern) EvalPlusPlus(s *SearchAccum, docvec []uint8, aggcnt []int64, arg, result []float32) ([]float32, error) { if result == nil { - result = make(map[any]float32) + result = []float32{} return result, nil } - keys := make([]any, 0, len(result)) - for key := range result { - keys = append(keys, key) + if len(arg) == 0 { + return []float32{}, nil } - for _, doc_id := range keys { - _, ok := arg[doc_id] - if ok { - result[doc_id] += arg[doc_id] - } else { - delete(result, doc_id) - } + + if len(result) > 0 { + result[0] += arg[0] } return result, nil } // Eval Plus OR. The previous result from Eval() is a Plus Operator and current Pattern is a Text or Star. // e.g. (+ (text apple)) (text banana) -func (p *Pattern) EvalPlusOR(s *SearchAccum, arg, result map[any]float32) (map[any]float32, error) { +func (p *Pattern) EvalPlusOR(s *SearchAccum, docvec []uint8, aggcnt []int64, arg, result []float32) ([]float32, error) { if result == nil { - result = make(map[any]float32) + result = []float32{} return result, nil } - keys := make([]any, 0, len(result)) - for key := range result { - keys = append(keys, key) - } - for _, doc_id := range keys { - _, ok := arg[doc_id] - if ok { - result[doc_id] += arg[doc_id] - } + if len(arg) > 0 && len(result) > 0 { + result[0] += arg[0] } + return result, nil } // Minus operation. Remove the result when doc_id is present in argument // e.g. (+ (text apple)) (- (text banana)) -func (p *Pattern) EvalMinus(s *SearchAccum, arg, result map[any]float32) (map[any]float32, error) { +func (p *Pattern) EvalMinus(s *SearchAccum, docvec []uint8, aggcnt []int64, arg, result []float32) ([]float32, error) { if result == nil { - result = make(map[any]float32) + result = []float32{} return result, nil } - for doc_id := range arg { - _, ok := result[doc_id] - if ok { - // remove from result - delete(result, doc_id) - } + if len(arg) > 0 { + return []float32{}, nil } + return result, nil } // OR operation. Either apple and banana can be the result // e.g. (text apple) (text banana) -func (p *Pattern) EvalOR(s *SearchAccum, arg, result map[any]float32) (map[any]float32, error) { +func (p *Pattern) EvalOR(s *SearchAccum, docvec []uint8, aggcnt []int64, arg, result []float32) ([]float32, error) { if result == nil { - result = make(map[any]float32) + result = []float32{} } - for doc_id := range arg { - _, ok := result[doc_id] - if ok { - result[doc_id] += arg[doc_id] + if len(arg) > 0 { + if len(result) == 0 { + result = arg } else { - result[doc_id] = arg[doc_id] + result[0] += arg[0] } } return result, nil } +/* func (p *Pattern) EvalPhrase(s *SearchAccum, arg map[any]float32) (map[any]float32, error) { // check word order here @@ -300,6 +299,7 @@ func (p *Pattern) EvalPhrase(s *SearchAccum, arg map[any]float32) (map[any]float return result, nil } +*/ // Get the weight for compute the TFIDF // LESSTHAN is lower the ranking @@ -319,67 +319,77 @@ func (p *Pattern) GetWeight() float32 { } // Combine two score maps into single map. max(float32) will return when same doc_id (key) exists in both arg and result. -func (p *Pattern) Combine(s *SearchAccum, arg, result map[any]float32) (map[any]float32, error) { +func (p *Pattern) Combine(s *SearchAccum, docvec []uint8, aggcnt []int64, arg, result []float32) ([]float32, error) { if result == nil { return arg, nil } - for k1 := range arg { - v1 := arg[k1] - v, ok := result[k1] - if ok { + + if len(arg) > 0 { + if len(result) > 0 { // max - if v1 > v { - result[k1] = v1 + if arg[0] > result[0] { + result[0] = arg[0] } } else { - result[k1] = v1 + result = arg } } return result, nil } // Eval() function to evaluate the previous result from Eval and the current pattern (with data from datasource) and return map[doc_id]float32 -func (p *Pattern) Eval(accum *SearchAccum, weight float32, result map[any]float32) (map[any]float32, error) { +func (p *Pattern) Eval(accum *SearchAccum, docvec []uint8, aggcnt []int64, weight float32, result []float32) ([]float32, error) { switch p.Operator { case TEXT, STAR: // leaf node: TEXT, STAR // calculate the score with weight if result == nil { - return p.EvalLeaf(accum, weight, result) + return p.EvalLeaf(accum, docvec, aggcnt, weight, result) } else { - child_result, err := p.EvalLeaf(accum, weight, nil) + child_result, err := p.EvalLeaf(accum, docvec, aggcnt, weight, nil) if err != nil { return nil, err } if accum.PatternAnyPlus() { - return p.EvalPlusOR(accum, child_result, result) + return p.EvalPlusOR(accum, docvec, aggcnt, child_result, result) } else { - return p.EvalOR(accum, child_result, result) + return p.EvalOR(accum, docvec, aggcnt, child_result, result) } } + case JOIN: + if result == nil { + return p.EvalLeaf(accum, docvec, aggcnt, weight, nil) + } else { + child_result, err := p.EvalLeaf(accum, docvec, aggcnt, weight, nil) + if err != nil { + return nil, err + } + return p.EvalPlusPlus(accum, docvec, aggcnt, child_result, result) + + } case PLUS: if result == nil { - return p.Children[0].Eval(accum, weight, nil) + return p.Children[0].Eval(accum, docvec, aggcnt, weight, nil) } else { - child_result, err := p.Children[0].Eval(accum, weight, nil) + child_result, err := p.Children[0].Eval(accum, docvec, aggcnt, weight, nil) if err != nil { return nil, err } - return p.EvalPlusPlus(accum, child_result, result) + return p.EvalPlusPlus(accum, docvec, aggcnt, child_result, result) } case MINUS: if result == nil { - result = make(map[any]float32) + result = []float32{} return result, nil } else { - child_result, err := p.Children[0].Eval(accum, weight, nil) + child_result, err := p.Children[0].Eval(accum, docvec, aggcnt, weight, nil) if err != nil { return nil, err } - return p.EvalMinus(accum, child_result, result) + return p.EvalMinus(accum, docvec, aggcnt, child_result, result) } case LESSTHAN, GREATERTHAN: @@ -387,44 +397,44 @@ func (p *Pattern) Eval(accum *SearchAccum, weight float32, result map[any]float3 weight *= p.GetWeight() if result == nil { - return p.Children[0].Eval(accum, weight, nil) + return p.Children[0].Eval(accum, docvec, aggcnt, weight, nil) } else { - child_result, err := p.Children[0].Eval(accum, weight, nil) + child_result, err := p.Children[0].Eval(accum, docvec, aggcnt, weight, nil) if err != nil { return nil, err } - return p.EvalOR(accum, child_result, result) + return p.EvalOR(accum, docvec, aggcnt, child_result, result) } case RANKLESS: // get weight by type weight *= p.GetWeight() if result == nil { - return p.Children[0].Eval(accum, weight, nil) + return p.Children[0].Eval(accum, docvec, aggcnt, weight, nil) } else { - child_result, err := p.Children[0].Eval(accum, weight, nil) + child_result, err := p.Children[0].Eval(accum, docvec, aggcnt, weight, nil) if err != nil { return nil, err } // OR if accum.PatternAnyPlus() { - return p.EvalPlusOR(accum, child_result, result) + return p.EvalPlusOR(accum, docvec, aggcnt, child_result, result) } else { - return p.EvalOR(accum, child_result, result) + return p.EvalOR(accum, docvec, aggcnt, child_result, result) } } case GROUP: - result := make(map[any]float32) + result := []float32{} for _, c := range p.Children { - child_result, err := c.Eval(accum, weight, nil) + child_result, err := c.Eval(accum, docvec, aggcnt, weight, nil) if err != nil { return nil, err } // COMBINE results from children - result, err = p.Combine(accum, child_result, result) + result, err = p.Combine(accum, docvec, aggcnt, child_result, result) if err != nil { return nil, err } @@ -434,7 +444,7 @@ func (p *Pattern) Eval(accum *SearchAccum, weight float32, result map[any]float3 case PHRASE: // all children are TEXT and AND operations for i, c := range p.Children { - child_result, err := c.Eval(accum, weight, nil) + child_result, err := c.Eval(accum, docvec, aggcnt, weight, nil) if err != nil { return nil, err } @@ -443,7 +453,7 @@ func (p *Pattern) Eval(accum *SearchAccum, weight float32, result map[any]float3 result = child_result } else { // AND operators with the results - result, err = c.EvalPlusPlus(accum, child_result, result) + result, err = c.EvalPlusPlus(accum, docvec, aggcnt, child_result, result) if err != nil { return nil, err } @@ -451,7 +461,7 @@ func (p *Pattern) Eval(accum *SearchAccum, weight float32, result map[any]float3 } // check word order - return p.EvalPhrase(accum, result) + return result, nil // p.EvalPhrase(accum, result) default: return nil, moerr.NewInternalErrorNoCtx("Eval() not handled") } @@ -625,7 +635,16 @@ func ParsePhrase(pattern string) ([]*Pattern, error) { } children = append(children, &Pattern{Text: string(pattern[offset:]), Operator: TEXT, Position: offset}) - return []*Pattern{{Text: pattern, Operator: PHRASE, Children: children}}, nil + ret := []*Pattern{{Text: pattern, Operator: PHRASE, Children: children}} + + // assign index + idx := int32(0) + for _, p := range ret { + assignPatternIndex(p, &idx) + } + + return ret, nil + } // Parse the search string in boolean mode @@ -734,9 +753,55 @@ func ParsePatternInBooleanMode(pattern string) ([]*Pattern, error) { } } + return tokens, nil } +// assign word index to TEXT and START Node +func assignPatternIndex(pattern *Pattern, idx *int32) { + + if pattern.Operator == TEXT || pattern.Operator == STAR || pattern.Operator == JOIN { + pattern.Index = *idx + (*idx)++ + return + } + for _, p := range pattern.Children { + assignPatternIndex(p, idx) + } +} + +func findTextOrStarFromPattern(pattern *Pattern, out []*Pattern) []*Pattern { + if pattern.Operator == TEXT || pattern.Operator == STAR { + out = append(out, pattern) + return out + } + + for _, p := range pattern.Children { + out = findTextOrStarFromPattern(p, out) + } + return out + +} + +func getResultCount(pattern *Pattern, cnt *int) { + if pattern.Operator == TEXT || pattern.Operator == STAR || pattern.Operator == JOIN { + (*cnt)++ + return + } + + for _, p := range pattern.Children { + getResultCount(p, cnt) + } +} + +func GetResultCountFromPattern(ps []*Pattern) int { + cnt := 0 + for _, p := range ps { + getResultCount(p, &cnt) + } + return cnt +} + // Parse search string in natural language mode func ParsePatternInNLMode(pattern string) ([]*Pattern, error) { runeSlice := []rune(pattern) @@ -753,12 +818,63 @@ func ParsePatternInNLMode(pattern string) ([]*Pattern, error) { slen := t.TokenBytes[0] word := string(t.TokenBytes[1 : slen+1]) - list = append(list, &Pattern{Text: word, Operator: TEXT}) + runeSlice = []rune(word) + if len(runeSlice) < ngram_size { + list = append(list, &Pattern{Text: word + "*", Operator: STAR, Position: t.BytePos}) + } else { + list = append(list, &Pattern{Text: word, Operator: TEXT, Position: t.BytePos}) + } + } + + // assign index + idx := int32(0) + for _, p := range list { + assignPatternIndex(p, &idx) } return list, nil } +func PatternOptimizeJoin(ps []*Pattern) []*Pattern { + + // search for plus with single text child + var join_children []*Pattern + var idxs []int + + for i, p := range ps { + if p.Operator == PLUS { + if len(p.Children) == 1 && (p.Children[0].Operator == TEXT || p.Children[0].Operator == STAR) { + join_children = append(join_children, p) + idxs = append(idxs, i) + } + } + } + + // not enough PLUS, NO JOIN + if len(join_children) <= 1 { + return ps + } + + join := &Pattern{Operator: JOIN, Children: join_children} + var ret []*Pattern + ret = append(ret, join) + + for i := range ps { + removed := false + for _, idx := range idxs { + if i == idx { + removed = true + break + } + } + if !removed { + ret = append(ret, ps[i]) + } + } + + return ret +} + // Parse search string into list of patterns func ParsePattern(pattern string, mode int64) ([]*Pattern, error) { switch mode { @@ -801,6 +917,14 @@ func ParsePattern(pattern string, mode int64) ([]*Pattern, error) { finalp = append(finalp, values...) finalp = append(finalp, minus...) + // optimize with JOIN + finalp = PatternOptimizeJoin(finalp) + + // assign index + idx := int32(0) + for _, p := range finalp { + assignPatternIndex(p, &idx) + } return finalp, nil default: return nil, moerr.NewInternalErrorNoCtx("invalid fulltext search mode") diff --git a/pkg/fulltext/fulltext_test.go b/pkg/fulltext/fulltext_test.go index d8875533ba4fe..0d3b332a0874d 100644 --- a/pkg/fulltext/fulltext_test.go +++ b/pkg/fulltext/fulltext_test.go @@ -15,7 +15,7 @@ package fulltext import ( - "strings" + "fmt" "testing" "github.com/matrixorigin/matrixone/pkg/sql/parsers/tree" @@ -28,63 +28,31 @@ type TestCase struct { expect string } -func PatternListToString(ps []*Pattern) string { - ss := make([]string, 0, len(ps)) - for _, p := range ps { - ss = append(ss, p.String()) - } - - return strings.Join(ss, " ") -} - -func PatternToString(pattern string, mode int64) (string, error) { - ps, err := ParsePattern(pattern, mode) - if err != nil { - return "", err - } - - return PatternListToString(ps), nil -} - -func PatternListToStringWithPosition(ps []*Pattern) string { - ss := make([]string, 0, len(ps)) - for _, p := range ps { - ss = append(ss, p.StringWithPosition()) - } - - return strings.Join(ss, " ") -} - -func PatternToStringWithPosition(pattern string, mode int64) (string, error) { - ps, err := ParsePattern(pattern, mode) - if err != nil { - return "", err - } - - return PatternListToStringWithPosition(ps), nil -} - func TestPatternPhrase(t *testing.T) { tests := []TestCase{ + { + pattern: "\"Ma'trix Origin\"", + expect: "(phrase (text 0 0 ma'trix) (text 1 12 origin))", + }, { pattern: "\"Matrix Origin\"", - expect: "(phrase (text 0 matrix) (text 7 origin))", + expect: "(phrase (text 0 0 matrix) (text 1 7 origin))", }, { pattern: "\"Matrix\"", - expect: "(phrase (text 0 matrix))", + expect: "(phrase (text 0 0 matrix))", }, { pattern: "\" Matrix \"", - expect: "(phrase (text 0 matrix))", + expect: "(phrase (text 0 0 matrix))", }, { pattern: "\"Matrix Origin\"", - expect: "(phrase (text 0 matrix) (text 11 origin))", + expect: "(phrase (text 0 0 matrix) (text 1 11 origin))", }, { pattern: "\" 你好嗎? Hello World 在一起 Happy 再见 \"", - expect: "(phrase (text 0 你好嗎?) (text 11 hello) (text 17 world) (text 24 在一起) (text 35 happy) (text 42 再见))", + expect: "(phrase (text 0 0 你好嗎?) (text 1 11 hello) (text 2 17 world) (text 3 24 在一起) (text 4 35 happy) (text 5 42 再见))", }, } @@ -98,45 +66,49 @@ func TestPatternPhrase(t *testing.T) { func TestPatternBoolean(t *testing.T) { tests := []TestCase{ + { + pattern: "Ma'trix Origin", + expect: "(text 0 ma'trix) (text 1 origin)", + }, { pattern: "Matrix Origin", - expect: "(text matrix) (text origin)", + expect: "(text 0 matrix) (text 1 origin)", }, { pattern: "+Matrix Origin", - expect: "(+ (text matrix)) (text origin)", + expect: "(+ (text 0 matrix)) (text 1 origin)", }, { pattern: "+Matrix -Origin", - expect: "(+ (text matrix)) (- (text origin))", + expect: "(+ (text 0 matrix)) (- (text 1 origin))", }, { pattern: "Matrix ~Origin", - expect: "(text matrix) (~ (text origin))", + expect: "(text 0 matrix) (~ (text 1 origin))", }, { pattern: "Matrix +(One)", - expect: "(+ (group (< (text origin)) (> (text one)))) (text matrix)", + expect: "(+ (group (< (text 0 origin)) (> (text 1 one)))) (text 2 matrix)", }, { pattern: "+Matrix +Origin", - expect: "(+ (text matrix)) (+ (text origin))", + expect: "(join 0 (+ (text 0 matrix)) (+ (text 0 origin)))", }, { pattern: "\"Matrix origin\"", - expect: "(phrase (text matrix) (text origin))", + expect: "(phrase (text 0 matrix) (text 1 origin))", }, { pattern: "Matrix Origin*", - expect: "(text matrix) (* origin*)", + expect: "(text 0 matrix) (* 1 origin*)", }, { pattern: "+Matrix +(Origin (One Two))", - expect: "(+ (text matrix)) (+ (group (text origin) (group (text one) (text two))))", + expect: "(+ (text 0 matrix)) (+ (group (text 1 origin) (group (text 2 one) (text 3 two))))", }, { pattern: "+读写汉字 -学中文", - expect: "(+ (text 读写汉字)) (- (text 学中文))", + expect: "(+ (text 0 读写汉字)) (- (text 1 学中文))", }, } @@ -150,22 +122,26 @@ func TestPatternBoolean(t *testing.T) { func TestPatternNL(t *testing.T) { tests := []TestCase{ + { + pattern: "Ma'trix Origin", + expect: "(* 0 0 ma*) (text 1 3 trix) (text 2 8 origin)", + }, { pattern: "Matrix Origin", - expect: "(text matrix) (text origin)", + expect: "(text 0 0 matrix) (text 1 7 origin)", }, { pattern: "读写汉字 学中文", - expect: "(text 读写汉) (text 写汉字) (text 汉字) (text 字) (text 学中文) (text 中文) (text 文)", + expect: "(text 0 0 读写汉) (text 1 3 写汉字) (* 2 6 汉字*) (* 3 9 字*) (text 4 13 学中文) (* 5 16 中文*) (* 6 19 文*)", }, { pattern: "读写", - expect: "(* 读写*)", + expect: "(* 0 0 读写*)", }, } for _, c := range tests { - result, err := PatternToString(c.pattern, int64(tree.FULLTEXT_NL)) + result, err := PatternToStringWithPosition(c.pattern, int64(tree.FULLTEXT_NL)) require.Nil(t, err) assert.Equal(t, c.expect, result) } @@ -176,11 +152,11 @@ func TestPatternQueryExpansion(t *testing.T) { tests := []TestCase{ { pattern: "Matrix Origin", - expect: "(text matrix) (text origin)", + expect: "(text 0 matrix) (text 1 origin)", }, { pattern: "读写汉字 学中文", - expect: "(+ (text 读写汉字)) (- (text 学中文))", + expect: "(+ (text 0 读写汉字)) (- (text 1 学中文))", }, } @@ -225,39 +201,55 @@ func TestFullTextNL(t *testing.T) { s, err := NewSearchAccum("src", "index", pattern, int64(tree.FULLTEXT_NL), "") require.Nil(t, err) + sql, err := PatternToSql(s.Pattern, int64(tree.FULLTEXT_NL), "indextbl", "") + require.Nil(t, err) + + fmt.Println(sql) + + agghtab := make(map[any][]uint8) + aggcnt := make([]int64, 64) + //fmt.Println(PatternListToString(s.Pattern)) // pretend adding records from database // init the word "apple" - word := "apple" - s.WordAccums[word] = &WordAccum{Words: make(map[any]*Word)} - s.WordAccums[word].Words[0] = &Word{DocId: 0, Position: []int32{0, 4, 6}, DocCount: 2} - s.WordAccums[word].Words[1] = &Word{DocId: 1, Position: []int32{0, 4, 6}, DocCount: 3} - - // init the word "banana" - word = "banana" - s.WordAccums[word] = &WordAccum{Words: make(map[any]*Word)} - s.WordAccums[word].Words[0] = &Word{DocId: 0, Position: []int32{0, 4, 6}, DocCount: 2} - s.WordAccums[word].Words[11] = &Word{DocId: 11, Position: []int32{0, 4, 6}, DocCount: 3} - s.WordAccums[word].Words[12] = &Word{DocId: 12, Position: []int32{0, 4, 6}, DocCount: 4} + //apple_idx := word2idx["apple"] + //banna_idx := word2idx["banna"] + agghtab[0] = []uint8{uint8(2), uint8(2)} // apple, banna + agghtab[1] = []uint8{uint8(3), uint8(0)} // apple + agghtab[11] = []uint8{uint8(0), uint8(3)} // banna + agghtab[12] = []uint8{uint8(0), uint8(4)} // banna + + aggcnt[0] = 2 + aggcnt[1] = 3 s.Nrow = 100 + test_result := make(map[any]float32, 4) // eval - var result map[any]float32 - for _, p := range s.Pattern { - result, err = p.Eval(s, float32(1.0), result) - require.Nil(t, err) + i := 0 + for key := range agghtab { + var result []float32 + docvec := agghtab[key] + for _, p := range s.Pattern { + result, err = p.Eval(s, docvec, aggcnt, float32(1.0), result) + require.Nil(t, err) + } + + if len(result) > 0 { + test_result[key] = result[0] + } + i++ } var ok bool - _, ok = result[0] + _, ok = test_result[0] assert.Equal(t, ok, true) - _, ok = result[1] + _, ok = test_result[1] assert.Equal(t, ok, true) - _, ok = result[11] + _, ok = test_result[11] assert.Equal(t, ok, true) - _, ok = result[12] + _, ok = test_result[12] assert.Equal(t, ok, true) } @@ -268,77 +260,98 @@ func TestFullTextOr(t *testing.T) { s, err := NewSearchAccum("src", "index", pattern, int64(tree.FULLTEXT_BOOLEAN), "") require.Nil(t, err) - //fmt.Println(PatternListToString(s.Pattern)) + agghtab := make(map[any][]uint8) + aggcnt := make([]int64, 64) - // pretend adding records from database - // init the word "apple" - word := "apple" - s.WordAccums[word] = &WordAccum{Words: make(map[any]*Word)} - s.WordAccums[word].Words[0] = &Word{DocId: 0, Position: []int32{0, 4, 6}, DocCount: 2} - s.WordAccums[word].Words[1] = &Word{DocId: 1, Position: []int32{0, 4, 6}, DocCount: 3} - - // init the word "banana" - word = "banana" - s.WordAccums[word] = &WordAccum{Words: make(map[any]*Word)} - s.WordAccums[word].Words[0] = &Word{DocId: 0, Position: []int32{0, 4, 6}, DocCount: 2} - s.WordAccums[word].Words[11] = &Word{DocId: 11, Position: []int32{0, 4, 6}, DocCount: 3} - s.WordAccums[word].Words[12] = &Word{DocId: 12, Position: []int32{0, 4, 6}, DocCount: 4} + agghtab[0] = []uint8{uint8(2), uint8(2)} // apple, banna + agghtab[1] = []uint8{uint8(3), uint8(0)} // apple + agghtab[11] = []uint8{uint8(0), uint8(3)} // banna + agghtab[12] = []uint8{uint8(0), uint8(4)} // banna + + aggcnt[0] = 2 + aggcnt[1] = 3 s.Nrow = 100 + test_result := make(map[any]float32, 4) // eval - var result map[any]float32 - for _, p := range s.Pattern { - result, err = p.Eval(s, float32(1.0), result) - require.Nil(t, err) + i := 0 + for key := range agghtab { + var result []float32 + docvec := agghtab[key] + for _, p := range s.Pattern { + result, err = p.Eval(s, docvec, aggcnt, float32(1.0), result) + require.Nil(t, err) + } + + if len(result) > 0 { + test_result[key] = result[0] + } + i++ } var ok bool - _, ok = result[0] + _, ok = test_result[0] assert.Equal(t, ok, true) - _, ok = result[1] + _, ok = test_result[1] assert.Equal(t, ok, true) - _, ok = result[11] + _, ok = test_result[11] assert.Equal(t, ok, true) - _, ok = result[12] + _, ok = test_result[12] assert.Equal(t, ok, true) } func TestFullTextPlusPlus(t *testing.T) { - pattern := "+apple +banana" + pattern := "+apple -orange" + //pattern := "+apple +banana -orange" s, err := NewSearchAccum("src", "index", pattern, int64(tree.FULLTEXT_BOOLEAN), "") require.Nil(t, err) - //fmt.Println(PatternListToString(s.Pattern)) + agghtab := make(map[any][]uint8) + aggcnt := make([]int64, 64) - // pretend adding records from database - // init the word "apple" - word := "apple" - s.WordAccums[word] = &WordAccum{Words: make(map[any]*Word)} - s.WordAccums[word].Words[0] = &Word{DocId: 0, Position: []int32{0, 4, 6}, DocCount: 2} - s.WordAccums[word].Words[1] = &Word{DocId: 1, Position: []int32{0, 4, 6}, DocCount: 3} - - // init the word "banana" - word = "banana" - s.WordAccums[word] = &WordAccum{Words: make(map[any]*Word)} - s.WordAccums[word].Words[0] = &Word{DocId: 0, Position: []int32{0, 4, 6}, DocCount: 2} - s.WordAccums[word].Words[11] = &Word{DocId: 11, Position: []int32{0, 4, 6}, DocCount: 3} - s.WordAccums[word].Words[12] = &Word{DocId: 12, Position: []int32{0, 4, 6}, DocCount: 4} + //fmt.Printf("PATTERN %v\n", s.Pattern) + // [(join 0 (+ (text 0 apple)) (+ (text 0 banana))) (- (text orange))] + agghtab[0] = []uint8{uint8(2), uint8(2)} // join + agghtab[1] = []uint8{uint8(3), uint8(0)} // join + agghtab[11] = []uint8{uint8(0), uint8(3)} // orange + agghtab[12] = []uint8{uint8(0), uint8(4)} // ornage + + aggcnt[0] = 2 + aggcnt[1] = 3 s.Nrow = 100 + test_result := make(map[any]float32, 4) // eval - var result map[any]float32 - for _, p := range s.Pattern { - result, err = p.Eval(s, float32(1.0), result) - require.Nil(t, err) + i := 0 + for key := range agghtab { + var result []float32 + docvec := agghtab[key] + //fmt.Printf("docvec %v %v\n", key, docvec) + for _, p := range s.Pattern { + result, err = p.Eval(s, docvec, aggcnt, float32(1.0), result) + require.Nil(t, err) + } + + if len(result) > 0 { + //fmt.Printf("result %v %f\n", key, result[0]) + test_result[key] = result[0] + } + i++ } var ok bool - _, ok = result[0] + _, ok = test_result[0] + assert.Equal(t, ok, false) + _, ok = test_result[1] assert.Equal(t, ok, true) + _, ok = test_result[11] + assert.Equal(t, ok, false) + _, ok = test_result[12] + assert.Equal(t, ok, false) } func TestFullTextPlusOr(t *testing.T) { @@ -347,72 +360,97 @@ func TestFullTextPlusOr(t *testing.T) { s, err := NewSearchAccum("src", "index", pattern, int64(tree.FULLTEXT_BOOLEAN), "") require.Nil(t, err) - //fmt.Println(PatternListToString(s.Pattern)) + agghtab := make(map[any][]uint8) + aggcnt := make([]int64, 64) - // pretend adding records from database - // init the word "apple" - word := "apple" - s.WordAccums[word] = &WordAccum{Words: make(map[any]*Word)} - s.WordAccums[word].Words[0] = &Word{DocId: 0, Position: []int32{0, 4, 6}, DocCount: 2} - s.WordAccums[word].Words[1] = &Word{DocId: 1, Position: []int32{0, 4, 6}, DocCount: 3} - - // init the word "banana" - word = "banana" - s.WordAccums[word] = &WordAccum{Words: make(map[any]*Word)} - s.WordAccums[word].Words[0] = &Word{DocId: 0, Position: []int32{0, 4, 6}, DocCount: 2} - s.WordAccums[word].Words[11] = &Word{DocId: 11, Position: []int32{0, 4, 6}, DocCount: 3} - s.WordAccums[word].Words[12] = &Word{DocId: 12, Position: []int32{0, 4, 6}, DocCount: 4} + agghtab[0] = []uint8{uint8(2), uint8(2)} // apple, banna + agghtab[1] = []uint8{uint8(3), uint8(0)} // apple + agghtab[11] = []uint8{uint8(0), uint8(3)} // banna + agghtab[12] = []uint8{uint8(0), uint8(4)} // banna + + aggcnt[0] = 2 + aggcnt[1] = 3 s.Nrow = 100 + test_result := make(map[any]float32, 4) // eval - var result map[any]float32 - for _, p := range s.Pattern { - result, err = p.Eval(s, float32(1.0), result) - require.Nil(t, err) + i := 0 + for key := range agghtab { + var result []float32 + docvec := agghtab[key] + //fmt.Printf("docvec %v %v\n", key, docvec) + for _, p := range s.Pattern { + result, err = p.Eval(s, docvec, aggcnt, float32(1.0), result) + require.Nil(t, err) + } + + if len(result) > 0 { + //fmt.Printf("result %v %f\n", key, result[0]) + test_result[key] = result[0] + } + i++ } var ok bool - _, ok = result[0] + _, ok = test_result[0] assert.Equal(t, ok, true) - _, ok = result[1] + _, ok = test_result[1] assert.Equal(t, ok, true) + _, ok = test_result[11] + assert.Equal(t, ok, false) + _, ok = test_result[12] + assert.Equal(t, ok, false) } func TestFullTextMinus(t *testing.T) { - pattern := "-banana +apple" + pattern := "+apple -banana" s, err := NewSearchAccum("src", "index", pattern, int64(tree.FULLTEXT_BOOLEAN), "") require.Nil(t, err) - //fmt.Println(PatternListToString(s.Pattern)) + agghtab := make(map[any][]uint8) + aggcnt := make([]int64, 64) - // pretend adding records from database - // init the word "apple" - word := "apple" - s.WordAccums[word] = &WordAccum{Words: make(map[any]*Word)} - s.WordAccums[word].Words[0] = &Word{DocId: 0, Position: []int32{0, 4, 6}, DocCount: 2} - s.WordAccums[word].Words[1] = &Word{DocId: 1, Position: []int32{0, 4, 6}, DocCount: 3} - - // init the word "banana" - word = "banana" - s.WordAccums[word] = &WordAccum{Words: make(map[any]*Word)} - s.WordAccums[word].Words[0] = &Word{DocId: 0, Position: []int32{0, 4, 6}, DocCount: 2} - s.WordAccums[word].Words[11] = &Word{DocId: 11, Position: []int32{0, 4, 6}, DocCount: 3} - s.WordAccums[word].Words[12] = &Word{DocId: 12, Position: []int32{0, 4, 6}, DocCount: 4} + agghtab[0] = []uint8{uint8(2), uint8(2)} // apple, banna + agghtab[1] = []uint8{uint8(3), uint8(0)} // apple + agghtab[11] = []uint8{uint8(0), uint8(3)} // banna + agghtab[12] = []uint8{uint8(0), uint8(4)} // banna + + aggcnt[0] = 2 + aggcnt[1] = 3 s.Nrow = 100 + test_result := make(map[any]float32, 4) // eval - var result map[any]float32 - for _, p := range s.Pattern { - result, err = p.Eval(s, float32(1.0), result) - require.Nil(t, err) + i := 0 + for key := range agghtab { + var result []float32 + docvec := agghtab[key] + //fmt.Printf("docvec %v %v\n", key, docvec) + for _, p := range s.Pattern { + result, err = p.Eval(s, docvec, aggcnt, float32(1.0), result) + require.Nil(t, err) + } + + if len(result) > 0 { + //fmt.Printf("result %v %f\n", key, result[0]) + test_result[key] = result[0] + } + i++ } var ok bool - _, ok = result[1] + _, ok = test_result[0] + assert.Equal(t, ok, false) + _, ok = test_result[1] assert.Equal(t, ok, true) + _, ok = test_result[11] + assert.Equal(t, ok, false) + _, ok = test_result[12] + assert.Equal(t, ok, false) + } func TestFullTextTilda(t *testing.T) { @@ -421,36 +459,47 @@ func TestFullTextTilda(t *testing.T) { s, err := NewSearchAccum("src", "index", pattern, int64(tree.FULLTEXT_BOOLEAN), "") require.Nil(t, err) - //fmt.Println(PatternListToString(s.Pattern)) + agghtab := make(map[any][]uint8) + aggcnt := make([]int64, 64) - // pretend adding records from database - // init the word "apple" - word := "apple" - s.WordAccums[word] = &WordAccum{Words: make(map[any]*Word)} - s.WordAccums[word].Words[0] = &Word{DocId: 0, Position: []int32{0, 4, 6}, DocCount: 2} - s.WordAccums[word].Words[1] = &Word{DocId: 1, Position: []int32{0, 4, 6}, DocCount: 3} - - // init the word "banana" - word = "banana" - s.WordAccums[word] = &WordAccum{Words: make(map[any]*Word)} - s.WordAccums[word].Words[0] = &Word{DocId: 0, Position: []int32{0, 4, 6}, DocCount: 2} - s.WordAccums[word].Words[11] = &Word{DocId: 11, Position: []int32{0, 4, 6}, DocCount: 3} - s.WordAccums[word].Words[12] = &Word{DocId: 12, Position: []int32{0, 4, 6}, DocCount: 4} + agghtab[0] = []uint8{uint8(2), uint8(2)} // apple, banna + agghtab[1] = []uint8{uint8(3), uint8(0)} // apple + agghtab[11] = []uint8{uint8(0), uint8(3)} // banna + agghtab[12] = []uint8{uint8(0), uint8(4)} // banna + + aggcnt[0] = 2 + aggcnt[1] = 3 s.Nrow = 100 + test_result := make(map[any]float32, 4) // eval - var result map[any]float32 - for _, p := range s.Pattern { - result, err = p.Eval(s, float32(1.0), result) - require.Nil(t, err) + i := 0 + for key := range agghtab { + var result []float32 + docvec := agghtab[key] + //fmt.Printf("docvec %v %v\n", key, docvec) + for _, p := range s.Pattern { + result, err = p.Eval(s, docvec, aggcnt, float32(1.0), result) + require.Nil(t, err) + } + + if len(result) > 0 { + //fmt.Printf("result %v %f\n", key, result[0]) + test_result[key] = result[0] + } + i++ } var ok bool - _, ok = result[0] + _, ok = test_result[0] assert.Equal(t, ok, true) - _, ok = result[1] + _, ok = test_result[1] assert.Equal(t, ok, true) + _, ok = test_result[11] + assert.Equal(t, ok, false) + _, ok = test_result[12] + assert.Equal(t, ok, false) } func TestFullText1(t *testing.T) { @@ -459,55 +508,65 @@ func TestFullText1(t *testing.T) { s, err := NewSearchAccum("src", "index", pattern, int64(tree.FULLTEXT_BOOLEAN), "") require.Nil(t, err) - //fmt.Println(PatternListToString(s.Pattern)) + agghtab := make(map[any][]uint8) + aggcnt := make([]int64, 64) - // pretend adding records from database - // init the word "we" - word := "we" - s.WordAccums[word] = &WordAccum{Words: make(map[any]*Word)} - s.WordAccums[word].Words[0] = &Word{DocId: 0, Position: []int32{0, 4, 6}, DocCount: 2} - s.WordAccums[word].Words[1] = &Word{DocId: 1, Position: []int32{0, 4, 6}, DocCount: 3} - - // init the word "are" - word = "are" - s.WordAccums[word] = &WordAccum{Words: make(map[any]*Word)} - s.WordAccums[word].Words[10] = &Word{DocId: 10, Position: []int32{0, 4, 6}, DocCount: 2} - s.WordAccums[word].Words[11] = &Word{DocId: 11, Position: []int32{0, 4, 6}, DocCount: 3} - s.WordAccums[word].Words[12] = &Word{DocId: 12, Position: []int32{0, 4, 6}, DocCount: 4} - - // init the word "so" - word = "so" - s.WordAccums[word] = &WordAccum{Words: make(map[any]*Word)} - s.WordAccums[word].Words[20] = &Word{DocId: 20, Position: []int32{0, 4, 6}, DocCount: 5} - s.WordAccums[word].Words[21] = &Word{DocId: 21, Position: []int32{0, 4, 6}, DocCount: 6} - s.WordAccums[word].Words[22] = &Word{DocId: 22, Position: []int32{0, 4, 6}, DocCount: 7} - s.WordAccums[word].Words[23] = &Word{DocId: 23, Position: []int32{0, 4, 6}, DocCount: 8} - - // init the word "happy" - word = "happy" - s.WordAccums[word] = &WordAccum{Words: make(map[any]*Word)} - s.WordAccums[word].Words[30] = &Word{DocId: 30, Position: []int32{0, 4, 6}, DocCount: 1} - s.WordAccums[word].Words[31] = &Word{DocId: 31, Position: []int32{0, 4, 6}, DocCount: 2} - s.WordAccums[word].Words[32] = &Word{DocId: 32, Position: []int32{0, 4, 6}, DocCount: 3} - s.WordAccums[word].Words[33] = &Word{DocId: 33, Position: []int32{0, 4, 6}, DocCount: 4} + // {we, are, so, happy} + // we + agghtab[0] = []uint8{uint8(2), uint8(0), uint8(0), uint8(0)} // we + agghtab[1] = []uint8{uint8(3), uint8(0), uint8(0), uint8(0)} // we + + // are + agghtab[10] = []uint8{uint8(0), uint8(2), uint8(0), uint8(0)} // are + agghtab[11] = []uint8{uint8(0), uint8(3), uint8(0), uint8(0)} // are + agghtab[12] = []uint8{uint8(0), uint8(4), uint8(0), uint8(0)} // are + + // so + agghtab[20] = []uint8{uint8(0), uint8(0), uint8(5), uint8(0)} + agghtab[21] = []uint8{uint8(0), uint8(0), uint8(6), uint8(0)} + agghtab[22] = []uint8{uint8(0), uint8(0), uint8(7), uint8(0)} + agghtab[23] = []uint8{uint8(0), uint8(0), uint8(8), uint8(0)} + + // so + agghtab[30] = []uint8{uint8(0), uint8(0), uint8(0), uint8(1)} + agghtab[31] = []uint8{uint8(0), uint8(0), uint8(0), uint8(2)} + agghtab[32] = []uint8{uint8(0), uint8(0), uint8(0), uint8(3)} + agghtab[33] = []uint8{uint8(0), uint8(0), uint8(0), uint8(4)} + + aggcnt[0] = 2 + aggcnt[1] = 3 + aggcnt[2] = 4 + aggcnt[3] = 4 s.Nrow = 100 + test_result := make(map[any]float32, 13) // eval - var result map[any]float32 - for _, p := range s.Pattern { - result, err = p.Eval(s, float32(1.0), result) - require.Nil(t, err) + i := 0 + for key := range agghtab { + var result []float32 + docvec := agghtab[key] + //fmt.Printf("docvec %v %v\n", key, docvec) + for _, p := range s.Pattern { + result, err = p.Eval(s, docvec, aggcnt, float32(1.0), result) + require.Nil(t, err) + //fmt.Printf("result %v\n", result) + } + + if len(result) > 0 { + //fmt.Printf("result %v %f\n", key, result[0]) + test_result[key] = result[0] + } + i++ } var ok bool ids := []int{0, 1, 10, 11, 12, 20, 21, 22, 23, 30, 31, 32, 33} for _, id := range ids { - _, ok = result[id] + _, ok = test_result[id] assert.Equal(t, ok, true) } - } func TestFullText2(t *testing.T) { @@ -516,50 +575,59 @@ func TestFullText2(t *testing.T) { s, err := NewSearchAccum("src", "index", pattern, int64(tree.FULLTEXT_BOOLEAN), "") require.Nil(t, err) - //fmt.Println(PatternListToString(s.Pattern)) + agghtab := make(map[any][]uint8) + aggcnt := make([]int64, 64) - // pretend adding records from database - // init the word "we" - word := "we" - s.WordAccums[word] = &WordAccum{Words: make(map[any]*Word)} - s.WordAccums[word].Words[0] = &Word{DocId: 0, Position: []int32{0, 4, 6}, DocCount: 2} - s.WordAccums[word].Words[1] = &Word{DocId: 1, Position: []int32{0, 4, 6}, DocCount: 3} - - // init the word "are" - word = "are" - s.WordAccums[word] = &WordAccum{Words: make(map[any]*Word)} - s.WordAccums[word].Words[0] = &Word{DocId: 0, Position: []int32{0, 4, 6}, DocCount: 2} - s.WordAccums[word].Words[11] = &Word{DocId: 11, Position: []int32{0, 4, 6}, DocCount: 3} - s.WordAccums[word].Words[12] = &Word{DocId: 12, Position: []int32{0, 4, 6}, DocCount: 4} - - // init the word "so" - word = "so" - s.WordAccums[word] = &WordAccum{Words: make(map[any]*Word)} - s.WordAccums[word].Words[0] = &Word{DocId: 0, Position: []int32{0, 4, 6}, DocCount: 2} - s.WordAccums[word].Words[21] = &Word{DocId: 21, Position: []int32{0, 4, 6}, DocCount: 6} - s.WordAccums[word].Words[22] = &Word{DocId: 22, Position: []int32{0, 4, 6}, DocCount: 7} - s.WordAccums[word].Words[23] = &Word{DocId: 23, Position: []int32{0, 4, 6}, DocCount: 8} - - // init the word "happy" - word = "happy" - s.WordAccums[word] = &WordAccum{Words: make(map[any]*Word)} - s.WordAccums[word].Words[0] = &Word{DocId: 0, Position: []int32{0, 4, 6}, DocCount: 2} - s.WordAccums[word].Words[31] = &Word{DocId: 31, Position: []int32{0, 4, 6}, DocCount: 2} - s.WordAccums[word].Words[32] = &Word{DocId: 32, Position: []int32{0, 4, 6}, DocCount: 3} - s.WordAccums[word].Words[33] = &Word{DocId: 33, Position: []int32{0, 4, 6}, DocCount: 4} + // {we, are, so, happy} + // we + agghtab[0] = []uint8{uint8(2), uint8(2), uint8(2), uint8(2)} // we, are + agghtab[1] = []uint8{uint8(3), uint8(0), uint8(0), uint8(0)} // we + + // are + agghtab[11] = []uint8{uint8(0), uint8(3), uint8(0), uint8(0)} // are + agghtab[12] = []uint8{uint8(0), uint8(4), uint8(0), uint8(0)} // are + + // so + agghtab[21] = []uint8{uint8(0), uint8(0), uint8(6), uint8(0)} + agghtab[22] = []uint8{uint8(0), uint8(0), uint8(7), uint8(0)} + agghtab[23] = []uint8{uint8(0), uint8(0), uint8(8), uint8(0)} + + // so + agghtab[31] = []uint8{uint8(0), uint8(0), uint8(0), uint8(2)} + agghtab[32] = []uint8{uint8(0), uint8(0), uint8(0), uint8(3)} + agghtab[33] = []uint8{uint8(0), uint8(0), uint8(0), uint8(4)} + + aggcnt[0] = 2 + aggcnt[1] = 3 + aggcnt[2] = 3 + aggcnt[3] = 3 s.Nrow = 100 + test_result := make(map[any]float32, 13) // eval - var result map[any]float32 - for _, p := range s.Pattern { - result, err = p.Eval(s, float32(1.0), result) - require.Nil(t, err) + i := 0 + for key := range agghtab { + var result []float32 + docvec := agghtab[key] + //fmt.Printf("docvec %v %v\n", key, docvec) + for _, p := range s.Pattern { + result, err = p.Eval(s, docvec, aggcnt, float32(1.0), result) + require.Nil(t, err) + //fmt.Printf("result %v\n", result) + } + + if len(result) > 0 { + //fmt.Printf("result %v %f\n", key, result[0]) + test_result[key] = result[0] + } + i++ } var ok bool - _, ok = result[0] + _, ok = test_result[0] assert.Equal(t, ok, true) + } func TestFullText3(t *testing.T) { @@ -568,152 +636,223 @@ func TestFullText3(t *testing.T) { s, err := NewSearchAccum("src", "index", pattern, int64(tree.FULLTEXT_BOOLEAN), "") require.Nil(t, err) - //fmt.Println(PatternListToString(s.Pattern)) + agghtab := make(map[any][]uint8) + aggcnt := make([]int64, 64) - // pretend adding records from database - // init the word "we" - word := "we" - s.WordAccums[word] = &WordAccum{Words: make(map[any]*Word)} - s.WordAccums[word].Words[0] = &Word{DocId: 0, Position: []int32{0, 4, 6}, DocCount: 2} - s.WordAccums[word].Words[1] = &Word{DocId: 1, Position: []int32{0, 4, 6}, DocCount: 3} - - // init the word "are" - word = "are" - s.WordAccums[word] = &WordAccum{Words: make(map[any]*Word)} - s.WordAccums[word].Words[0] = &Word{DocId: 0, Position: []int32{0, 4, 6}, DocCount: 2} - s.WordAccums[word].Words[11] = &Word{DocId: 11, Position: []int32{0, 4, 6}, DocCount: 3} - s.WordAccums[word].Words[12] = &Word{DocId: 12, Position: []int32{0, 4, 6}, DocCount: 4} - - // init the word "so" - word = "so" - s.WordAccums[word] = &WordAccum{Words: make(map[any]*Word)} - s.WordAccums[word].Words[20] = &Word{DocId: 20, Position: []int32{0, 4, 6}, DocCount: 5} - s.WordAccums[word].Words[21] = &Word{DocId: 21, Position: []int32{0, 4, 6}, DocCount: 6} - s.WordAccums[word].Words[22] = &Word{DocId: 22, Position: []int32{0, 4, 6}, DocCount: 7} - s.WordAccums[word].Words[23] = &Word{DocId: 23, Position: []int32{0, 4, 6}, DocCount: 8} - - // init the word "happy" - word = "happy" - s.WordAccums[word] = &WordAccum{Words: make(map[any]*Word)} - s.WordAccums[word].Words[30] = &Word{DocId: 30, Position: []int32{0, 4, 6}, DocCount: 1} - s.WordAccums[word].Words[31] = &Word{DocId: 31, Position: []int32{0, 4, 6}, DocCount: 2} - s.WordAccums[word].Words[32] = &Word{DocId: 32, Position: []int32{0, 4, 6}, DocCount: 3} - s.WordAccums[word].Words[33] = &Word{DocId: 33, Position: []int32{0, 4, 6}, DocCount: 4} + // {we, are, so, happy} + // we + agghtab[0] = []uint8{uint8(2), uint8(2), uint8(0), uint8(0)} // we, are + agghtab[1] = []uint8{uint8(3), uint8(0), uint8(0), uint8(0)} // we + + // are + agghtab[11] = []uint8{uint8(0), uint8(3), uint8(0), uint8(0)} // are + agghtab[12] = []uint8{uint8(0), uint8(4), uint8(0), uint8(0)} // are + + // so + agghtab[20] = []uint8{uint8(0), uint8(0), uint8(5), uint8(0)} + agghtab[21] = []uint8{uint8(0), uint8(0), uint8(6), uint8(0)} + agghtab[22] = []uint8{uint8(0), uint8(0), uint8(7), uint8(0)} + agghtab[23] = []uint8{uint8(0), uint8(0), uint8(8), uint8(0)} + + // happy + agghtab[30] = []uint8{uint8(0), uint8(0), uint8(0), uint8(1)} + agghtab[31] = []uint8{uint8(0), uint8(0), uint8(0), uint8(2)} + agghtab[32] = []uint8{uint8(0), uint8(0), uint8(0), uint8(3)} + agghtab[33] = []uint8{uint8(0), uint8(0), uint8(0), uint8(4)} + + aggcnt[0] = 2 + aggcnt[1] = 3 + aggcnt[2] = 4 + aggcnt[3] = 4 s.Nrow = 100 + test_result := make(map[any]float32, 13) // eval - var result map[any]float32 - for _, p := range s.Pattern { - result, err = p.Eval(s, float32(1.0), result) - require.Nil(t, err) + i := 0 + for key := range agghtab { + var result []float32 + docvec := agghtab[key] + //fmt.Printf("docvec %v %v\n", key, docvec) + for _, p := range s.Pattern { + result, err = p.Eval(s, docvec, aggcnt, float32(1.0), result) + require.Nil(t, err) + //fmt.Printf("result %v\n", result) + } + + if len(result) > 0 { + //fmt.Printf("result %v %f\n", key, result[0]) + test_result[key] = result[0] + } + i++ } var ok bool - _, ok = result[1] + _, ok = test_result[1] assert.Equal(t, ok, true) } -func TestFullText4(t *testing.T) { +func TestFullText5(t *testing.T) { - pattern := "we -aRe so Happy" + pattern := "we aRe so +Happy" s, err := NewSearchAccum("src", "index", pattern, int64(tree.FULLTEXT_BOOLEAN), "") require.Nil(t, err) - //fmt.Println(PatternListToString(s.Pattern)) + agghtab := make(map[any][]uint8) + aggcnt := make([]int64, 64) - // no words found + // [(+ (text 0 happy)) (text 1 we) (text 2 are) (text 3 so)] + // {we, are, so, happy} + // we + agghtab[0] = []uint8{uint8(0), uint8(2), uint8(0), uint8(0)} // we + agghtab[1] = []uint8{uint8(0), uint8(3), uint8(0), uint8(0)} // we + + // are + agghtab[11] = []uint8{uint8(0), uint8(0), uint8(3), uint8(0)} // are + agghtab[12] = []uint8{uint8(0), uint8(0), uint8(4), uint8(0)} // are + + aggcnt[1] = 2 + aggcnt[2] = 2 s.Nrow = 100 + test_result := make(map[any]float32, 13) // eval - var result map[any]float32 - for _, p := range s.Pattern { - result, err = p.Eval(s, float32(1.0), result) - require.Nil(t, err) + i := 0 + for key := range agghtab { + var result []float32 + docvec := agghtab[key] + //fmt.Printf("docvec %v %v\n", key, docvec) + for _, p := range s.Pattern { + result, err = p.Eval(s, docvec, aggcnt, float32(1.0), result) + require.Nil(t, err) + //fmt.Printf("result %v\n", result) + } + + if len(result) > 0 { + //fmt.Printf("result %v %f\n", key, result[0]) + test_result[key] = result[0] + } + i++ } - assert.Equal(t, len(result), int(0)) + assert.Equal(t, len(test_result), 0) + } -func TestFullText5(t *testing.T) { +func TestFullTextGroup(t *testing.T) { - pattern := "we aRe so +Happy" + pattern := "+we +(so)" s, err := NewSearchAccum("src", "index", pattern, int64(tree.FULLTEXT_BOOLEAN), "") require.Nil(t, err) - //fmt.Println(PatternListToString(s.Pattern)) + agghtab := make(map[any][]uint8) + aggcnt := make([]int64, 64) - // pretend adding records from database - // init the word "we" - word := "we" - s.WordAccums[word] = &WordAccum{Words: make(map[any]*Word)} - s.WordAccums[word].Words[0] = &Word{DocId: 0, Position: []int32{0, 4, 6}, DocCount: 2} - s.WordAccums[word].Words[1] = &Word{DocId: 1, Position: []int32{0, 4, 6}, DocCount: 3} - - // init the word "are" - word = "are" - s.WordAccums[word] = &WordAccum{Words: make(map[any]*Word)} - s.WordAccums[word].Words[10] = &Word{DocId: 10, Position: []int32{0, 4, 6}, DocCount: 2} - s.WordAccums[word].Words[11] = &Word{DocId: 11, Position: []int32{0, 4, 6}, DocCount: 3} - s.WordAccums[word].Words[12] = &Word{DocId: 12, Position: []int32{0, 4, 6}, DocCount: 4} + // {we, are, so, happy} + // we + agghtab[0] = []uint8{uint8(2), uint8(2), uint8(0), uint8(0)} // we, are + agghtab[1] = []uint8{uint8(3), uint8(0), uint8(5), uint8(0)} // we, so + + // are + agghtab[11] = []uint8{uint8(0), uint8(3), uint8(0), uint8(0)} // are + agghtab[12] = []uint8{uint8(0), uint8(4), uint8(0), uint8(0)} // are + + // so + agghtab[20] = []uint8{uint8(0), uint8(0), uint8(5), uint8(0)} + agghtab[21] = []uint8{uint8(0), uint8(0), uint8(6), uint8(0)} + agghtab[22] = []uint8{uint8(0), uint8(0), uint8(7), uint8(0)} + agghtab[23] = []uint8{uint8(0), uint8(0), uint8(8), uint8(0)} + + aggcnt[0] = 2 + aggcnt[1] = 3 + aggcnt[2] = 6 s.Nrow = 100 + test_result := make(map[any]float32, 13) // eval - var result map[any]float32 - for _, p := range s.Pattern { - result, err = p.Eval(s, float32(1.0), result) - require.Nil(t, err) + i := 0 + for key := range agghtab { + var result []float32 + docvec := agghtab[key] + //fmt.Printf("docvec %v %v\n", key, docvec) + for _, p := range s.Pattern { + result, err = p.Eval(s, docvec, aggcnt, float32(1.0), result) + require.Nil(t, err) + //fmt.Printf("result %v\n", result) + } + + if len(result) > 0 { + //fmt.Printf("result %v %f\n", key, result[0]) + test_result[key] = result[0] + } + i++ } - assert.Equal(t, len(result), 0) + var ok bool + _, ok = test_result[0] + assert.Equal(t, ok, true) + _, ok = test_result[1] + assert.Equal(t, ok, true) } -func TestFullTextGroup(t *testing.T) { +func TestFullTextJoinGroupTilda(t *testing.T) { - pattern := "+we +(so)" + pattern := "+we +also ~(so)" s, err := NewSearchAccum("src", "index", pattern, int64(tree.FULLTEXT_BOOLEAN), "") require.Nil(t, err) - //fmt.Println(PatternListToString(s.Pattern)) + agghtab := make(map[any][]uint8) + aggcnt := make([]int64, 64) - // pretend adding records from database - // init the word "we" - word := "we" - s.WordAccums[word] = &WordAccum{Words: make(map[any]*Word)} - s.WordAccums[word].Words[0] = &Word{DocId: 0, Position: []int32{0, 4, 6}, DocCount: 2} - s.WordAccums[word].Words[1] = &Word{DocId: 1, Position: []int32{0, 4, 6}, DocCount: 3} - - // init the word "are" - word = "are" - s.WordAccums[word] = &WordAccum{Words: make(map[any]*Word)} - s.WordAccums[word].Words[0] = &Word{DocId: 0, Position: []int32{0, 4, 6}, DocCount: 2} - s.WordAccums[word].Words[11] = &Word{DocId: 11, Position: []int32{0, 4, 6}, DocCount: 3} - s.WordAccums[word].Words[12] = &Word{DocId: 12, Position: []int32{0, 4, 6}, DocCount: 4} - - // init the word "so" - word = "so" - s.WordAccums[word] = &WordAccum{Words: make(map[any]*Word)} - s.WordAccums[word].Words[1] = &Word{DocId: 1, Position: []int32{0, 4, 6}, DocCount: 5} - s.WordAccums[word].Words[21] = &Word{DocId: 21, Position: []int32{0, 4, 6}, DocCount: 6} - s.WordAccums[word].Words[22] = &Word{DocId: 22, Position: []int32{0, 4, 6}, DocCount: 7} - s.WordAccums[word].Words[23] = &Word{DocId: 23, Position: []int32{0, 4, 6}, DocCount: 8} + // {(we, also), are, so} + // (we, also) + agghtab[0] = []uint8{uint8(2), uint8(2), uint8(0)} // (we, also), are + agghtab[1] = []uint8{uint8(3), uint8(0), uint8(5)} // (we, also), so + + // are + agghtab[11] = []uint8{uint8(0), uint8(3), uint8(0)} // are + agghtab[12] = []uint8{uint8(0), uint8(4), uint8(0)} // are + + // so + agghtab[20] = []uint8{uint8(0), uint8(0), uint8(5)} + agghtab[21] = []uint8{uint8(0), uint8(0), uint8(6)} + agghtab[22] = []uint8{uint8(0), uint8(0), uint8(7)} + agghtab[23] = []uint8{uint8(0), uint8(0), uint8(8)} + + aggcnt[0] = 2 + aggcnt[1] = 3 + aggcnt[2] = 6 s.Nrow = 100 + test_result := make(map[any]float32, 13) // eval - var result map[any]float32 - for _, p := range s.Pattern { - result, err = p.Eval(s, float32(1.0), result) - require.Nil(t, err) + i := 0 + for key := range agghtab { + var result []float32 + docvec := agghtab[key] + //fmt.Printf("docvec %v %v\n", key, docvec) + for _, p := range s.Pattern { + result, err = p.Eval(s, docvec, aggcnt, float32(1.0), result) + require.Nil(t, err) + //fmt.Printf("result %v\n", result) + } + + if len(result) > 0 { + //fmt.Printf("result %v %f\n", key, result[0]) + test_result[key] = result[0] + } + i++ } var ok bool - _, ok = result[0] + _, ok = test_result[0] assert.Equal(t, ok, true) - _, ok = result[1] + _, ok = test_result[1] assert.Equal(t, ok, true) + assert.Equal(t, 2, len(test_result)) } func TestFullTextGroupTilda(t *testing.T) { @@ -722,44 +861,56 @@ func TestFullTextGroupTilda(t *testing.T) { s, err := NewSearchAccum("src", "index", pattern, int64(tree.FULLTEXT_BOOLEAN), "") require.Nil(t, err) - //fmt.Println(PatternListToString(s.Pattern)) + agghtab := make(map[any][]uint8) + aggcnt := make([]int64, 64) - // pretend adding records from database - // init the word "we" - word := "we" - s.WordAccums[word] = &WordAccum{Words: make(map[any]*Word)} - s.WordAccums[word].Words[0] = &Word{DocId: 0, Position: []int32{0, 4, 6}, DocCount: 2} - s.WordAccums[word].Words[1] = &Word{DocId: 1, Position: []int32{0, 4, 6}, DocCount: 3} - - // init the word "are" - word = "are" - s.WordAccums[word] = &WordAccum{Words: make(map[any]*Word)} - s.WordAccums[word].Words[0] = &Word{DocId: 0, Position: []int32{0, 4, 6}, DocCount: 2} - s.WordAccums[word].Words[11] = &Word{DocId: 11, Position: []int32{0, 4, 6}, DocCount: 3} - s.WordAccums[word].Words[12] = &Word{DocId: 12, Position: []int32{0, 4, 6}, DocCount: 4} - - // init the word "so" - word = "so" - s.WordAccums[word] = &WordAccum{Words: make(map[any]*Word)} - s.WordAccums[word].Words[1] = &Word{DocId: 1, Position: []int32{0, 4, 6}, DocCount: 5} - s.WordAccums[word].Words[21] = &Word{DocId: 21, Position: []int32{0, 4, 6}, DocCount: 6} - s.WordAccums[word].Words[22] = &Word{DocId: 22, Position: []int32{0, 4, 6}, DocCount: 7} - s.WordAccums[word].Words[23] = &Word{DocId: 23, Position: []int32{0, 4, 6}, DocCount: 8} + // {we, are, so} + // we + agghtab[0] = []uint8{uint8(2), uint8(2), uint8(0)} // we, are + agghtab[1] = []uint8{uint8(3), uint8(0), uint8(5)} // we, so + + // are + agghtab[11] = []uint8{uint8(0), uint8(3), uint8(0)} // are + agghtab[12] = []uint8{uint8(0), uint8(4), uint8(0)} // are + + // so + agghtab[20] = []uint8{uint8(0), uint8(0), uint8(5)} + agghtab[21] = []uint8{uint8(0), uint8(0), uint8(6)} + agghtab[22] = []uint8{uint8(0), uint8(0), uint8(7)} + agghtab[23] = []uint8{uint8(0), uint8(0), uint8(8)} + + aggcnt[0] = 2 + aggcnt[1] = 3 + aggcnt[2] = 6 s.Nrow = 100 + test_result := make(map[any]float32, 13) // eval - var result map[any]float32 - for _, p := range s.Pattern { - result, err = p.Eval(s, float32(1.0), result) - require.Nil(t, err) + i := 0 + for key := range agghtab { + var result []float32 + docvec := agghtab[key] + //fmt.Printf("docvec %v %v\n", key, docvec) + for _, p := range s.Pattern { + result, err = p.Eval(s, docvec, aggcnt, float32(1.0), result) + require.Nil(t, err) + //fmt.Printf("result %v\n", result) + } + + if len(result) > 0 { + //fmt.Printf("result %v %f\n", key, result[0]) + test_result[key] = result[0] + } + i++ } var ok bool - _, ok = result[0] + _, ok = test_result[0] assert.Equal(t, ok, true) - _, ok = result[1] + _, ok = test_result[1] assert.Equal(t, ok, true) + assert.Equal(t, 2, len(test_result)) } func TestFullTextStar(t *testing.T) { @@ -769,28 +920,44 @@ func TestFullTextStar(t *testing.T) { require.Nil(t, err) //fmt.Println(PatternListToString(s.Pattern)) + agghtab := make(map[any][]uint8) + aggcnt := make([]int64, 64) - // pretend adding records from database - // init the word "apple" - word := "apple*" - s.WordAccums[word] = &WordAccum{Words: make(map[any]*Word)} - s.WordAccums[word].Words[0] = &Word{DocId: 0, Position: []int32{0, 4, 6}, DocCount: 2} - s.WordAccums[word].Words[1] = &Word{DocId: 1, Position: []int32{0, 4, 6}, DocCount: 3} + // {apple*} + // apple* + agghtab[0] = []uint8{uint8(2), uint8(2), uint8(0), uint8(0)} // we, are + agghtab[1] = []uint8{uint8(3), uint8(0), uint8(5), uint8(0)} // we, so + + aggcnt[0] = 2 s.Nrow = 100 + test_result := make(map[any]float32, 13) // eval - var result map[any]float32 - for _, p := range s.Pattern { - result, err = p.Eval(s, float32(1.0), result) - require.Nil(t, err) + i := 0 + for key := range agghtab { + var result []float32 + docvec := agghtab[key] + //fmt.Printf("docvec %v %v\n", key, docvec) + for _, p := range s.Pattern { + result, err = p.Eval(s, docvec, aggcnt, float32(1.0), result) + require.Nil(t, err) + //fmt.Printf("result %v\n", result) + } + + if len(result) > 0 { + //fmt.Printf("result %v %f\n", key, result[0]) + test_result[key] = result[0] + } + i++ } var ok bool - _, ok = result[0] + _, ok = test_result[0] assert.Equal(t, ok, true) - _, ok = result[1] + _, ok = test_result[1] assert.Equal(t, ok, true) + } func TestFullTextPhrase(t *testing.T) { @@ -799,68 +966,75 @@ func TestFullTextPhrase(t *testing.T) { s, err := NewSearchAccum("src", "index", pattern, int64(tree.FULLTEXT_BOOLEAN), "") require.Nil(t, err) - //fmt.Println(PatternListToString(s.Pattern)) - - // pretend adding records from database - // init the word "we" - word := "we" - s.WordAccums[word] = &WordAccum{Words: make(map[any]*Word)} - s.WordAccums[word].Words[0] = &Word{DocId: 0, Position: []int32{0, 4, 6}, DocCount: 2} - s.WordAccums[word].Words[1] = &Word{DocId: 1, Position: []int32{0, 4, 6}, DocCount: 3} - - // init the word "are" - word = "are" - s.WordAccums[word] = &WordAccum{Words: make(map[any]*Word)} - s.WordAccums[word].Words[0] = &Word{DocId: 0, Position: []int32{3, 9, 10}, DocCount: 2} - s.WordAccums[word].Words[1] = &Word{DocId: 1, Position: []int32{0, 4, 6}, DocCount: 3} - s.WordAccums[word].Words[12] = &Word{DocId: 12, Position: []int32{0, 4, 6}, DocCount: 4} - - // init the word "so" - word = "so" - s.WordAccums[word] = &WordAccum{Words: make(map[any]*Word)} - s.WordAccums[word].Words[0] = &Word{DocId: 0, Position: []int32{0, 4, 7}, DocCount: 5} - s.WordAccums[word].Words[1] = &Word{DocId: 1, Position: []int32{0, 4, 6}, DocCount: 6} - s.WordAccums[word].Words[22] = &Word{DocId: 22, Position: []int32{0, 4, 6}, DocCount: 7} - s.WordAccums[word].Words[23] = &Word{DocId: 23, Position: []int32{0, 4, 6}, DocCount: 8} - - // init the word "happy" - word = "happy" - s.WordAccums[word] = &WordAccum{Words: make(map[any]*Word)} - s.WordAccums[word].Words[0] = &Word{DocId: 0, Position: []int32{0, 4, 10}, DocCount: 1} - s.WordAccums[word].Words[31] = &Word{DocId: 31, Position: []int32{0, 4, 6}, DocCount: 2} - s.WordAccums[word].Words[32] = &Word{DocId: 32, Position: []int32{0, 4, 6}, DocCount: 3} - s.WordAccums[word].Words[33] = &Word{DocId: 33, Position: []int32{0, 4, 6}, DocCount: 4} + sql, err := PatternToSql(s.Pattern, int64(tree.FULLTEXT_BOOLEAN), "idxtbl", "") + require.Nil(t, err) + fmt.Println(sql) + + agghtab := make(map[any][]uint8) + aggcnt := make([]int64, 64) + + // {we, are, so, happy} + // we + agghtab[0] = []uint8{uint8(2), uint8(2), uint8(2), uint8(2)} // we + agghtab[1] = []uint8{uint8(3), uint8(2), uint8(0), uint8(0)} // we + + // are + agghtab[10] = []uint8{uint8(0), uint8(2), uint8(0), uint8(0)} // are + agghtab[11] = []uint8{uint8(0), uint8(3), uint8(0), uint8(0)} // are + agghtab[12] = []uint8{uint8(0), uint8(4), uint8(0), uint8(0)} // are + + // so + agghtab[20] = []uint8{uint8(0), uint8(0), uint8(5), uint8(0)} + agghtab[21] = []uint8{uint8(0), uint8(0), uint8(6), uint8(0)} + agghtab[22] = []uint8{uint8(0), uint8(0), uint8(7), uint8(0)} + agghtab[23] = []uint8{uint8(0), uint8(0), uint8(8), uint8(0)} + + // so + agghtab[30] = []uint8{uint8(0), uint8(0), uint8(0), uint8(1)} + agghtab[31] = []uint8{uint8(0), uint8(0), uint8(0), uint8(2)} + agghtab[32] = []uint8{uint8(0), uint8(0), uint8(0), uint8(3)} + agghtab[33] = []uint8{uint8(0), uint8(0), uint8(0), uint8(4)} + + aggcnt[0] = 2 + aggcnt[1] = 5 + aggcnt[2] = 5 + aggcnt[3] = 5 s.Nrow = 100 + test_result := make(map[any]float32, 13) // eval - var result map[any]float32 - for _, p := range s.Pattern { - result, err = p.Eval(s, float32(1.0), result) - require.Nil(t, err) + i := 0 + for key := range agghtab { + var result []float32 + docvec := agghtab[key] + //fmt.Printf("docvec %v %v\n", key, docvec) + for _, p := range s.Pattern { + result, err = p.Eval(s, docvec, aggcnt, float32(1.0), result) + require.Nil(t, err) + //fmt.Printf("result %v\n", result) + } + + if len(result) > 0 { + //fmt.Printf("result %v %f\n", key, result[0]) + test_result[key] = result[0] + } + i++ } var ok bool - _, ok = result[0] + _, ok = test_result[0] assert.Equal(t, ok, true) } func TestFullTextCombine(t *testing.T) { p := &Pattern{} - s1 := make(map[any]float32) - s1[0] = 1 - s1[2] = 2 - s2 := make(map[any]float32) - s2[0] = 2 - s2[2] = 1 - s2[1] = 4 + s1 := []float32{1} + s2 := []float32{2} - result, err := p.Combine(nil, s1, s2) + result, err := p.Combine(nil, nil, nil, s1, s2) require.Nil(t, err) - assert.Equal(t, len(result), 3) assert.Equal(t, result[0], float32(2)) - assert.Equal(t, result[2], float32(2)) - assert.Equal(t, result[1], float32(4)) } diff --git a/pkg/fulltext/sql.go b/pkg/fulltext/sql.go new file mode 100644 index 0000000000000..1607659d2d945 --- /dev/null +++ b/pkg/fulltext/sql.go @@ -0,0 +1,499 @@ +// Copyright 2022 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 fulltext + +import ( + "fmt" + "strings" + + "github.com/matrixorigin/matrixone/pkg/common/moerr" + "github.com/matrixorigin/matrixone/pkg/sql/parsers/tree" +) + +/* +fulltext SQL generation + +For natural language mode, it is a phrase search. The search will be AND operation and with position as filter. + +For boolean mode, the rule are the followings: + - operation JOIN contains a list of + operators which is single TEXT or STAR values + - operator + is considered as AND Operation. + Operators can be a single TEXT/STAR or Group. Operator + with children Group cannot be optimized to under JOIN. + - operator - is not considered as AND operation because NOT filter is slow in SQL. + Change the formula from A INTERSECT (NOT B) into A UNION (A INTERSECT B) and process the negative filter afterwards. + - other operators are OR operation. + +SQL generation from boolean search string is a Set theory. + +e.g. search string +hello -world + +the search string will convert into Pattern/Plan + +((+ (TEXT hello)) (- (TEXT world))) + +with set theory, we can formulae the above plans into + +A\B = A - (A INTERSECT B) + +Since NOT filter is slow in SQL, we change the plan into A UNION (A INTERSECT B) and process the negative later. + +# The result SQL will be + +A UNION ALL (A JOIN B) + +In case there are multiple + operators like the search string "+A +B -(D)" +JOIN will be used to optimize the SQL with Pattern/Plan + +(((JOIN (+ (TEXT A)) (+ (TEXT B))) (- (GROUP (< (TEXT C)) (> (TEXT D))))) + +# With plan above, we can formula the SQL like belows + +(A INTERSECT B) UNION ((A INTERSECT B) INTERSECT C) UNION ((A INTERSECT B) INTERSECT D) + +WITH t0 (A JOIN B) +(t0) UNION ALL (t0 JOIN C) UNION ALL (t0 JOIN D) + +(A JOIN B) is the result contain both A and B. Consider it is the appearance of A and B (denoted as index 0 in SQL)). +(t0 JOIN C) is the result contain both A, B and C. Consider it is the appearance of A, B and C (denoted as index 1 in SQL). +(t0 JOIN D) is the result contain both A, B and D. Consider it is the appearance of A, B and D (denoted as index 2 in SQL). + +For TD-IDF calculation, + +Instead of caculating the TD-IDF from the words A, B, C and D, +we will calculate the score from word groups (A & B) -> "0" , (A & B & C) -> "1", (A & B & D) -> "2". +The ordering of the result remain the same and of course the score is different. + +In case + operator with GROUP such as "+(A B) ~C -D", + +Although operator + with GROUP cannot be optimized with JOIN Pattern node, we can still optimize the SQL with JOIN like below + +(+ (GROUP (TEXT A) (TEXT B))) (~ (TEXT C)) (- (TEXT D))) + +# The generated SQL is like + +A UNION B UNION (A INTERSECT C) UNION (A INTERSECT D) UNION (B INTERSECT C) UNION (B INTERSECT D) + +(A) UNION ALL (B) UNION ALL (A JOIN C) UNION ALL (A JOIN D) UNION ALL (B JOIN C) UNION ALL (B JOIN D) + +In case multiple + operator with single values and + operator with GROUP like "+A +B +(C D) E" + +The plan like the followings: +((JOIN (+ (TEXT A)) (+ (TEXT B))) (+ (GROUP (TEXT C) (TEXT D))) (TEXT E)) + +To simplify the SQL, (+ (GROUP (TEXT C) (TEXT D))) will not considered as JOIN. + +(A INTERSECT B) UNION (A INTERSECT B INTERSECT C) UNION (A INTEREST B INTERSECT D) UNION (A INTERSECT B INTERSECT E) + +WITH t0 (A JOIN B) +(t0) UNION ALL (t0 JOIN C) UNION ALL (t0 JOIN D) UNION ALL (t0 JOIN E) +*/ +type SqlNode struct { + Index int32 + Label string + Sql string + IsJoin bool + Children []*SqlNode +} + +func escape(src string) string { + return strings.ReplaceAll(src, "'", `\'`) +} + +// GenTextSql that support ngram in boolean mode +// TEXT is a word. For english, it is fine to have a simple filter word = 'word". +// For Chinese, word should be tokenize into ngram +func GenTextSql(p *Pattern, mode int64, idxtbl string, parser string) (string, error) { + + if parser == "json_value" { + sql := fmt.Sprintf("SELECT doc_id FROM %s WHERE word = '%s'", idxtbl, escape(p.Text)) + return sql, nil + } + + ps, err := ParsePatternInNLMode(p.Text) + if err != nil { + return "", err + } + + sql, err := SqlPhrase(ps, mode, idxtbl, false) + if err != nil { + return "", err + } + + return sql, nil +} + +// PLUS node as JOIN. Index is from TEXT/STAR node +// children of PLUS node can be a single TEXT/STAR or GROUP. +// In case of GROUP, mutiple SqlNodes will be generated. +func GenJoinPlusSql(p *Pattern, mode int64, idxtbl string, parser string) ([]*SqlNode, error) { + + var sql string + var textps []*Pattern + + textps = findTextOrStarFromPattern(p, textps) + sqlns := make([]*SqlNode, 0, len(textps)) + for _, tp := range textps { + kw := tp.Text + alias := fmt.Sprintf("t%d", tp.Index) + sqlnode := &SqlNode{IsJoin: true, Index: tp.Index, Label: alias} + if tp.Operator == TEXT { + // TEXT + textsql, err := GenTextSql(tp, mode, idxtbl, parser) + if err != nil { + return nil, err + } + sql = fmt.Sprintf("%s AS (%s)", alias, textsql) + + //sql = fmt.Sprintf("%s AS (SELECT doc_id FROM %s WHERE word = '%s')", alias, idxtbl, kw) + sqlnode.Children = append(sqlnode.Children, &SqlNode{Index: tp.Index, Label: alias, IsJoin: true, Sql: sql}) + + } else { + // STAR + if kw[len(kw)-1] != '*' { + return nil, moerr.NewInternalErrorNoCtx("wildcard search without character *") + } + prefix := kw[0 : len(kw)-1] + sql = fmt.Sprintf("%s AS (SELECT doc_id FROM %s WHERE prefix_eq(word,'%s'))", alias, idxtbl, escape(prefix)) + sqlnode.Children = append(sqlnode.Children, &SqlNode{Index: tp.Index, Label: alias, IsJoin: true, Sql: sql}) + } + + sqlnode.Sql = fmt.Sprintf("SELECT %s.doc_id, CAST(%d as int) FROM %s", alias, sqlnode.Index, alias) + sqlns = append(sqlns, sqlnode) + } + + return sqlns, nil +} + +// JOIN node. Index is from JOIN node. Index of TEXT/STAR is invalid +// Generate a JOIN subsql in children and union sql in Sql. +// subsql t0 is the table for all JOIN +func GenJoinSql(p *Pattern, mode int64, idxtbl string, parser string) ([]*SqlNode, error) { + + var sql string + var textps []*Pattern + tables := make([]string, 0) + + sqlnode := &SqlNode{IsJoin: true, Index: p.Index} + idx := p.Index + subidx := 0 + textps = findTextOrStarFromPattern(p, textps) + + for _, tp := range textps { + kw := tp.Text + alias := fmt.Sprintf("t%d%d", idx, subidx) + tables = append(tables, alias) + if tp.Operator == TEXT { + textsql, err := GenTextSql(tp, mode, idxtbl, parser) + if err != nil { + return nil, err + } + sql = fmt.Sprintf("%s AS (%s)", alias, textsql) + + //sql = fmt.Sprintf("%s AS (SELECT doc_id FROM %s WHERE word = '%s')", alias, idxtbl, kw) + sqlnode.Children = append(sqlnode.Children, &SqlNode{Index: idx, Label: alias, IsJoin: true, Sql: sql}) + subidx++ + } else { + if kw[len(kw)-1] != '*' { + return nil, moerr.NewInternalErrorNoCtx("wildcard search without character *") + } + prefix := kw[0 : len(kw)-1] + sql = fmt.Sprintf("%s AS (SELECT doc_id FROM %s WHERE prefix_eq(word,'%s'))", alias, idxtbl, escape(prefix)) + sqlnode.Children = append(sqlnode.Children, &SqlNode{Index: idx, Label: alias, IsJoin: true, Sql: sql}) + subidx++ + } + } + + oncond := make([]string, 0, len(sqlnode.Children)-1) + for i := range tables { + if i > 0 { + oncond = append(oncond, fmt.Sprintf("%s.doc_id = %s.doc_id", tables[0], tables[i])) + } + } + + label := fmt.Sprintf("t%d", p.Index) + sql = fmt.Sprintf("%s AS (SELECT %s.doc_id FROM %s WHERE %s)", label, tables[0], strings.Join(tables, ", "), + strings.Join(oncond, " AND ")) + sqlnode.Children = append(sqlnode.Children, &SqlNode{Index: idx, Label: label, IsJoin: true, Sql: sql}) + + sqlnode.Sql = fmt.Sprintf("SELECT %s.doc_id, CAST(%d as int) FROM %s", label, sqlnode.Index, label) + sqlnode.Label = label + + return []*SqlNode{sqlnode}, nil +} + +// generate the sql with the pattern. +// isJoin flag is true, geneate the SQL in JOIN mode (only for JOIN and PLUS node) +// if joinsql is not NILL, all the OR TEXT/STAR node will be joined with joinsql +func GenSql(p *Pattern, mode int64, idxtbl string, joinsql []*SqlNode, isJoin bool, parser string) ([]*SqlNode, error) { + + var sqls []*SqlNode + var textps []*Pattern + + if isJoin { + if p.Operator == JOIN { + return GenJoinSql(p, mode, idxtbl, parser) + } else { + return GenJoinPlusSql(p, mode, idxtbl, parser) + } + } + + textps = findTextOrStarFromPattern(p, textps) + + if len(joinsql) == 0 { + // NO JOIN + + for _, tp := range textps { + var sql string + idx := tp.Index + kw := tp.Text + alias := fmt.Sprintf("t%d", idx) + sqlnode := &SqlNode{Index: idx, Label: alias, IsJoin: isJoin} + if tp.Operator == TEXT { + textsql, err := GenTextSql(tp, mode, idxtbl, parser) + if err != nil { + return nil, err + } + subsql := fmt.Sprintf("%s AS (%s)", alias, textsql) + sql = fmt.Sprintf("SELECT doc_id, CAST(%d as int) FROM %s", idx, alias) + sqlnode.Sql = sql + sqlnode.Children = append(sqlnode.Children, &SqlNode{Index: idx, Label: alias, IsJoin: isJoin, Sql: subsql}) + + } else { + if kw[len(kw)-1] != '*' { + return nil, moerr.NewInternalErrorNoCtx("wildcard search without character *") + } + prefix := kw[0 : len(kw)-1] + sql = fmt.Sprintf("SELECT doc_id, CAST(%d as int) FROM %s WHERE prefix_eq(word,'%s')", idx, idxtbl, escape(prefix)) + sqlnode.Sql = sql + + } + sqls = append(sqls, sqlnode) + } + + } else { + + for _, jn := range joinsql { + for _, tp := range textps { + var sql string + idx := tp.Index + kw := tp.Text + alias := fmt.Sprintf("t%d", idx) + sqlnode := &SqlNode{Index: idx, Label: alias, IsJoin: isJoin} + if tp.Operator == TEXT { + textsql, err := GenTextSql(tp, mode, idxtbl, parser) + if err != nil { + return nil, err + } + subsql := fmt.Sprintf("%s AS (%s)", alias, textsql) + sql = fmt.Sprintf("SELECT %s.doc_id, CAST(%d as int) FROM %s, %s WHERE %s.doc_id = %s.doc_id", + jn.Label, idx, alias, jn.Label, jn.Label, alias) + sqlnode.Sql = sql + sqlnode.Children = append(sqlnode.Children, &SqlNode{Index: idx, Label: alias, IsJoin: isJoin, Sql: subsql}) + + } else { + if kw[len(kw)-1] != '*' { + return nil, moerr.NewInternalErrorNoCtx("wildcard search without character *") + } + prefix := kw[0 : len(kw)-1] + sql = fmt.Sprintf("SELECT %s.doc_id, CAST(%d as int) FROM %s as %s, %s WHERE %s.doc_id = %s.doc_id AND prefix_eq(%s.word, '%s')", + jn.Label, idx, idxtbl, alias, jn.Label, jn.Label, alias, alias, escape(prefix)) + sqlnode.Sql = sql + + } + sqls = append(sqls, sqlnode) + } + + } + } + + return sqls, nil +} + +// Generate SQL in boolean mode +func SqlBoolean(ps []*Pattern, mode int64, idxtbl string, parser string) (string, error) { + + var err error + var join []*SqlNode + var sqls []*SqlNode + // check JOIN + + if len(ps) == 1 { + if ps[0].Operator == JOIN { + join, err = GenSql(ps[0], mode, idxtbl, nil, true, parser) + if err != nil { + return "", err + } + sqls = append(sqls, join...) + } else { + s, err := GenSql(ps[0], mode, idxtbl, nil, false, parser) + if err != nil { + return "", err + } + sqls = append(sqls, s...) + } + } else { + startidx := 0 + if ps[0].Operator == JOIN { + join, err = GenSql(ps[0], mode, idxtbl, nil, true, parser) + if err != nil { + return "", err + } + sqls = append(sqls, join...) + startidx++ + } else if ps[0].Operator == PLUS { + // Plus with Group also make as JOIN + join, err = GenSql(ps[0], mode, idxtbl, nil, true, parser) + if err != nil { + return "", err + } + sqls = append(sqls, join...) + startidx++ + } + + for i := startidx; i < len(ps); i++ { + p := ps[i] + s, err := GenSql(p, mode, idxtbl, join, false, parser) + if err != nil { + return "", err + } + sqls = append(sqls, s...) + } + } + + // generate final sql + + subsql := make([]string, 0) + union := make([]string, 0) + duplicate := make(map[string]bool) + + for _, s := range sqls { + union = append(union, s.Sql) + for _, c := range s.Children { + _, ok := duplicate[c.Label] + if !ok { + subsql = append(subsql, c.Sql) + duplicate[c.Label] = true + } + } + } + + ret := "" + if len(subsql) > 0 { + ret = "WITH " + ret += strings.Join(subsql, ", ") + ret += " " + } + ret += strings.Join(union, " UNION ALL ") + + return ret, nil +} + +// Generate SQL in phrase mode. It is the same for natural language mode and phrase search in boolean mode +func SqlPhrase(ps []*Pattern, mode int64, idxtbl string, withIndex bool) (string, error) { + + var sql string + var union []string + + // get plain text + if len(ps) == 1 { + tp := ps[0] + kw := tp.Text + + if tp.Operator == TEXT { + if withIndex { + sql = fmt.Sprintf("SELECT doc_id, CAST(%d as int) FROM %s WHERE word = '%s'", + tp.Index, idxtbl, escape(kw)) + } else { + sql = fmt.Sprintf("SELECT doc_id FROM %s WHERE word = '%s'", + idxtbl, escape(kw)) + + } + } else { + if kw[len(kw)-1] != '*' { + return "", moerr.NewInternalErrorNoCtx("wildcard search without character *") + } + prefix := kw[0 : len(kw)-1] + if withIndex { + sql = fmt.Sprintf("SELECT doc_id, CAST(%d as int) FROM %s WHERE prefix_eq(word,'%s')", + tp.Index, idxtbl, escape(prefix)) + } else { + sql = fmt.Sprintf("SELECT doc_id FROM %s WHERE prefix_eq(word,'%s')", + idxtbl, escape(prefix)) + + } + + } + } else { + + oncond := make([]string, len(ps)-1) + tables := make([]string, len(ps)) + for i, tp := range ps { + var subsql string + kw := tp.Text + tblname := fmt.Sprintf("kw%d", i) + tables[i] = tblname + if tp.Operator == TEXT { + subsql = fmt.Sprintf("%s AS (SELECT doc_id, pos FROM %s WHERE word = '%s')", + tblname, idxtbl, escape(kw)) + } else { + if kw[len(kw)-1] != '*' { + return "", moerr.NewInternalErrorNoCtx("wildcard search without character *") + } + prefix := kw[0 : len(kw)-1] + subsql = fmt.Sprintf("%s AS (SELECT doc_id, pos FROM %s WHERE prefix_eq(word,'%s'))", + tblname, idxtbl, escape(prefix)) + + } + union = append(union, subsql) + if i > 0 { + oncond[i-1] = fmt.Sprintf("%s.doc_id = %s.doc_id AND %s.pos - %s.pos = %d", + tables[0], tables[i], tables[i], tables[0], ps[i].Position-ps[0].Position) + } + } + sql = "WITH " + sql += strings.Join(union, ", ") + if withIndex { + sql += fmt.Sprintf(" SELECT %s.doc_id, CAST(0 as int) FROM ", tables[0]) + } else { + sql += fmt.Sprintf(" SELECT %s.doc_id FROM ", tables[0]) + } + + sql += strings.Join(tables, ", ") + sql += " WHERE " + sql += strings.Join(oncond, " AND ") + } + + //logutil.Infof("SQL is %s", sql) + + return sql, nil +} + +// API for generate SQL from pattern +func PatternToSql(ps []*Pattern, mode int64, idxtbl string, parser string) (string, error) { + + switch mode { + case int64(tree.FULLTEXT_NL), int64(tree.FULLTEXT_DEFAULT): + return SqlPhrase(ps, mode, idxtbl, true) + case int64(tree.FULLTEXT_BOOLEAN): + if ps[0].Operator == PHRASE { + return SqlPhrase(ps[0].Children, mode, idxtbl, true) + } else { + return SqlBoolean(ps, mode, idxtbl, parser) + } + case int64(tree.FULLTEXT_QUERY_EXPANSION), int64(tree.FULLTEXT_NL_QUERY_EXPANSION): + return "", moerr.NewInternalErrorNoCtx("Query Expansion mode not supported") + default: + return "", moerr.NewInternalErrorNoCtx("invalid fulltext search mode") + } +} diff --git a/pkg/fulltext/sql_test.go b/pkg/fulltext/sql_test.go new file mode 100644 index 0000000000000..8a9b100d7cb6f --- /dev/null +++ b/pkg/fulltext/sql_test.go @@ -0,0 +1,156 @@ +// Copyright 2022 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 fulltext + +import ( + "testing" + + "github.com/matrixorigin/matrixone/pkg/sql/parsers/tree" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestSqlPhrase(t *testing.T) { + tests := []TestCase{ + { + pattern: "\"Ma'trix Origin\"", + expect: "WITH kw0 AS (SELECT doc_id, pos FROM `__mo_index_secondary_` WHERE word = 'ma\\'trix'), kw1 AS (SELECT doc_id, pos FROM `__mo_index_secondary_` WHERE word = 'origin') SELECT kw0.doc_id, CAST(0 as int) FROM kw0, kw1 WHERE kw0.doc_id = kw1.doc_id AND kw1.pos - kw0.pos = 8", + }, + { + pattern: "\"Matrix Origin\"", + expect: "WITH kw0 AS (SELECT doc_id, pos FROM `__mo_index_secondary_` WHERE word = 'matrix'), kw1 AS (SELECT doc_id, pos FROM `__mo_index_secondary_` WHERE word = 'origin') SELECT kw0.doc_id, CAST(0 as int) FROM kw0, kw1 WHERE kw0.doc_id = kw1.doc_id AND kw1.pos - kw0.pos = 7", + }, + { + pattern: "\"Matrix\"", + expect: "SELECT doc_id, CAST(0 as int) FROM `__mo_index_secondary_` WHERE word = 'matrix'", + }, + { + pattern: "\" Matrix \"", + expect: "SELECT doc_id, CAST(0 as int) FROM `__mo_index_secondary_` WHERE word = 'matrix'", + }, + { + pattern: "\"Matrix Origin\"", + expect: "WITH kw0 AS (SELECT doc_id, pos FROM `__mo_index_secondary_` WHERE word = 'matrix'), kw1 AS (SELECT doc_id, pos FROM `__mo_index_secondary_` WHERE word = 'origin') SELECT kw0.doc_id, CAST(0 as int) FROM kw0, kw1 WHERE kw0.doc_id = kw1.doc_id AND kw1.pos - kw0.pos = 11", + }, + { + pattern: "\" 你好嗎? Hello World 在一起 Happy 再见 \"", + expect: "WITH kw0 AS (SELECT doc_id, pos FROM `__mo_index_secondary_` WHERE word = '你好嗎?'), kw1 AS (SELECT doc_id, pos FROM `__mo_index_secondary_` WHERE word = 'hello'), kw2 AS (SELECT doc_id, pos FROM `__mo_index_secondary_` WHERE word = 'world'), kw3 AS (SELECT doc_id, pos FROM `__mo_index_secondary_` WHERE word = '在一起'), kw4 AS (SELECT doc_id, pos FROM `__mo_index_secondary_` WHERE word = 'happy'), kw5 AS (SELECT doc_id, pos FROM `__mo_index_secondary_` WHERE word = '再见') SELECT kw0.doc_id, CAST(0 as int) FROM kw0, kw1, kw2, kw3, kw4, kw5 WHERE kw0.doc_id = kw1.doc_id AND kw1.pos - kw0.pos = 11 AND kw0.doc_id = kw2.doc_id AND kw2.pos - kw0.pos = 17 AND kw0.doc_id = kw3.doc_id AND kw3.pos - kw0.pos = 24 AND kw0.doc_id = kw4.doc_id AND kw4.pos - kw0.pos = 35 AND kw0.doc_id = kw5.doc_id AND kw5.pos - kw0.pos = 42", + }, + } + + for _, c := range tests { + s, err := NewSearchAccum("src", "index", c.pattern, int64(tree.FULLTEXT_BOOLEAN), "") + require.Nil(t, err) + result, err := PatternToSql(s.Pattern, int64(tree.FULLTEXT_BOOLEAN), "`__mo_index_secondary_`", "") + require.Nil(t, err) + //fmt.Println(result) + assert.Equal(t, c.expect, result) + } +} + +func TestSqlBoolean(t *testing.T) { + + tests := []TestCase{ + { + pattern: "Ma'trix Origin", + expect: "WITH t0 AS (WITH kw0 AS (SELECT doc_id, pos FROM `__mo_index_secondary_` WHERE prefix_eq(word,'ma')), kw1 AS (SELECT doc_id, pos FROM `__mo_index_secondary_` WHERE word = 'trix') SELECT kw0.doc_id FROM kw0, kw1 WHERE kw0.doc_id = kw1.doc_id AND kw1.pos - kw0.pos = 3), t1 AS (SELECT doc_id FROM `__mo_index_secondary_` WHERE word = 'origin') SELECT doc_id, CAST(0 as int) FROM t0 UNION ALL SELECT doc_id, CAST(1 as int) FROM t1", + }, + { + pattern: "Matrix Origin", + expect: "WITH t0 AS (SELECT doc_id FROM `__mo_index_secondary_` WHERE word = 'matrix'), t1 AS (SELECT doc_id FROM `__mo_index_secondary_` WHERE word = 'origin') SELECT doc_id, CAST(0 as int) FROM t0 UNION ALL SELECT doc_id, CAST(1 as int) FROM t1", + }, + { + pattern: "+Matrix Origin", + expect: "WITH t0 AS (SELECT doc_id FROM `__mo_index_secondary_` WHERE word = 'matrix'), t1 AS (SELECT doc_id FROM `__mo_index_secondary_` WHERE word = 'origin') SELECT t0.doc_id, CAST(0 as int) FROM t0 UNION ALL SELECT t0.doc_id, CAST(1 as int) FROM t1, t0 WHERE t0.doc_id = t1.doc_id", + }, + { + pattern: "+Matrix -Origin", + expect: "WITH t0 AS (SELECT doc_id FROM `__mo_index_secondary_` WHERE word = 'matrix'), t1 AS (SELECT doc_id FROM `__mo_index_secondary_` WHERE word = 'origin') SELECT t0.doc_id, CAST(0 as int) FROM t0 UNION ALL SELECT t0.doc_id, CAST(1 as int) FROM t1, t0 WHERE t0.doc_id = t1.doc_id", + }, + { + pattern: "Matrix ~Origin", + expect: "WITH t0 AS (SELECT doc_id FROM `__mo_index_secondary_` WHERE word = 'matrix'), t1 AS (SELECT doc_id FROM `__mo_index_secondary_` WHERE word = 'origin') SELECT doc_id, CAST(0 as int) FROM t0 UNION ALL SELECT doc_id, CAST(1 as int) FROM t1", + }, + { + pattern: "Matrix +(One)", + expect: "WITH t0 AS (SELECT doc_id FROM `__mo_index_secondary_` WHERE word = 'origin'), t1 AS (SELECT doc_id FROM `__mo_index_secondary_` WHERE word = 'one'), t2 AS (SELECT doc_id FROM `__mo_index_secondary_` WHERE word = 'matrix') SELECT t0.doc_id, CAST(0 as int) FROM t0 UNION ALL SELECT t1.doc_id, CAST(1 as int) FROM t1 UNION ALL SELECT t0.doc_id, CAST(2 as int) FROM t2, t0 WHERE t0.doc_id = t2.doc_id UNION ALL SELECT t1.doc_id, CAST(2 as int) FROM t2, t1 WHERE t1.doc_id = t2.doc_id", + }, + { + pattern: "+Matrix +Origin", + expect: "WITH t00 AS (SELECT doc_id FROM `__mo_index_secondary_` WHERE word = 'matrix'), t01 AS (SELECT doc_id FROM `__mo_index_secondary_` WHERE word = 'origin'), t0 AS (SELECT t00.doc_id FROM t00, t01 WHERE t00.doc_id = t01.doc_id) SELECT t0.doc_id, CAST(0 as int) FROM t0", + }, + { + pattern: "\"Matrix origin\"", + expect: "WITH kw0 AS (SELECT doc_id, pos FROM `__mo_index_secondary_` WHERE word = 'matrix'), kw1 AS (SELECT doc_id, pos FROM `__mo_index_secondary_` WHERE word = 'origin') SELECT kw0.doc_id, CAST(0 as int) FROM kw0, kw1 WHERE kw0.doc_id = kw1.doc_id AND kw1.pos - kw0.pos = 7", + }, + { + pattern: "Matrix Origin*", + expect: "WITH t0 AS (SELECT doc_id FROM `__mo_index_secondary_` WHERE word = 'matrix') SELECT doc_id, CAST(0 as int) FROM t0 UNION ALL SELECT doc_id, CAST(1 as int) FROM `__mo_index_secondary_` WHERE prefix_eq(word,'origin')", + }, + { + pattern: "+Matrix* Origin*", + expect: "WITH t0 AS (SELECT doc_id FROM `__mo_index_secondary_` WHERE prefix_eq(word,'matrix')) SELECT t0.doc_id, CAST(0 as int) FROM t0 UNION ALL SELECT t0.doc_id, CAST(1 as int) FROM `__mo_index_secondary_` as t1, t0 WHERE t0.doc_id = t1.doc_id AND prefix_eq(t1.word, 'origin')", + }, + { + pattern: "+Matrix +(Origin (One Two))", + expect: "WITH t0 AS (SELECT doc_id FROM `__mo_index_secondary_` WHERE word = 'matrix'), t1 AS (SELECT doc_id FROM `__mo_index_secondary_` WHERE word = 'origin'), t2 AS (SELECT doc_id FROM `__mo_index_secondary_` WHERE word = 'one'), t3 AS (SELECT doc_id FROM `__mo_index_secondary_` WHERE word = 'two') SELECT t0.doc_id, CAST(0 as int) FROM t0 UNION ALL SELECT t0.doc_id, CAST(1 as int) FROM t1, t0 WHERE t0.doc_id = t1.doc_id UNION ALL SELECT t0.doc_id, CAST(2 as int) FROM t2, t0 WHERE t0.doc_id = t2.doc_id UNION ALL SELECT t0.doc_id, CAST(3 as int) FROM t3, t0 WHERE t0.doc_id = t3.doc_id", + }, + { + pattern: "+读写汉字 -学中文", + expect: "WITH t0 AS (WITH kw0 AS (SELECT doc_id, pos FROM `__mo_index_secondary_` WHERE word = '读写汉'), kw1 AS (SELECT doc_id, pos FROM `__mo_index_secondary_` WHERE word = '写汉字'), kw2 AS (SELECT doc_id, pos FROM `__mo_index_secondary_` WHERE prefix_eq(word,'汉字')), kw3 AS (SELECT doc_id, pos FROM `__mo_index_secondary_` WHERE prefix_eq(word,'字')) SELECT kw0.doc_id FROM kw0, kw1, kw2, kw3 WHERE kw0.doc_id = kw1.doc_id AND kw1.pos - kw0.pos = 3 AND kw0.doc_id = kw2.doc_id AND kw2.pos - kw0.pos = 6 AND kw0.doc_id = kw3.doc_id AND kw3.pos - kw0.pos = 9), t1 AS (WITH kw0 AS (SELECT doc_id, pos FROM `__mo_index_secondary_` WHERE word = '学中文'), kw1 AS (SELECT doc_id, pos FROM `__mo_index_secondary_` WHERE prefix_eq(word,'中文')), kw2 AS (SELECT doc_id, pos FROM `__mo_index_secondary_` WHERE prefix_eq(word,'文')) SELECT kw0.doc_id FROM kw0, kw1, kw2 WHERE kw0.doc_id = kw1.doc_id AND kw1.pos - kw0.pos = 3 AND kw0.doc_id = kw2.doc_id AND kw2.pos - kw0.pos = 6) SELECT t0.doc_id, CAST(0 as int) FROM t0 UNION ALL SELECT t0.doc_id, CAST(1 as int) FROM t1, t0 WHERE t0.doc_id = t1.doc_id", + }, + } + + for _, c := range tests { + s, err := NewSearchAccum("src", "index", c.pattern, int64(tree.FULLTEXT_BOOLEAN), "") + require.Nil(t, err) + result, err := PatternToSql(s.Pattern, int64(tree.FULLTEXT_BOOLEAN), "`__mo_index_secondary_`", "") + //fmt.Println(PatternListToStringWithPosition(s.Pattern)) + require.Nil(t, err) + //fmt.Println(result) + assert.Equal(t, c.expect, result) + } +} + +func TestSqlNL(t *testing.T) { + + tests := []TestCase{ + { + pattern: "Ma'trix Origin", + expect: "WITH kw0 AS (SELECT doc_id, pos FROM `__mo_index_secondary_` WHERE prefix_eq(word,'ma')), kw1 AS (SELECT doc_id, pos FROM `__mo_index_secondary_` WHERE word = 'trix'), kw2 AS (SELECT doc_id, pos FROM `__mo_index_secondary_` WHERE word = 'origin') SELECT kw0.doc_id, CAST(0 as int) FROM kw0, kw1, kw2 WHERE kw0.doc_id = kw1.doc_id AND kw1.pos - kw0.pos = 3 AND kw0.doc_id = kw2.doc_id AND kw2.pos - kw0.pos = 8", + }, + { + pattern: "Matrix Origin", + expect: "WITH kw0 AS (SELECT doc_id, pos FROM `__mo_index_secondary_` WHERE word = 'matrix'), kw1 AS (SELECT doc_id, pos FROM `__mo_index_secondary_` WHERE word = 'origin') SELECT kw0.doc_id, CAST(0 as int) FROM kw0, kw1 WHERE kw0.doc_id = kw1.doc_id AND kw1.pos - kw0.pos = 7", + }, + { + pattern: "读写汉字 学中文", + expect: "WITH kw0 AS (SELECT doc_id, pos FROM `__mo_index_secondary_` WHERE word = '读写汉'), kw1 AS (SELECT doc_id, pos FROM `__mo_index_secondary_` WHERE word = '写汉字'), kw2 AS (SELECT doc_id, pos FROM `__mo_index_secondary_` WHERE prefix_eq(word,'汉字')), kw3 AS (SELECT doc_id, pos FROM `__mo_index_secondary_` WHERE prefix_eq(word,'字')), kw4 AS (SELECT doc_id, pos FROM `__mo_index_secondary_` WHERE word = '学中文'), kw5 AS (SELECT doc_id, pos FROM `__mo_index_secondary_` WHERE prefix_eq(word,'中文')), kw6 AS (SELECT doc_id, pos FROM `__mo_index_secondary_` WHERE prefix_eq(word,'文')) SELECT kw0.doc_id, CAST(0 as int) FROM kw0, kw1, kw2, kw3, kw4, kw5, kw6 WHERE kw0.doc_id = kw1.doc_id AND kw1.pos - kw0.pos = 3 AND kw0.doc_id = kw2.doc_id AND kw2.pos - kw0.pos = 6 AND kw0.doc_id = kw3.doc_id AND kw3.pos - kw0.pos = 9 AND kw0.doc_id = kw4.doc_id AND kw4.pos - kw0.pos = 13 AND kw0.doc_id = kw5.doc_id AND kw5.pos - kw0.pos = 16 AND kw0.doc_id = kw6.doc_id AND kw6.pos - kw0.pos = 19", + }, + { + pattern: "读写", + expect: "SELECT doc_id, CAST(0 as int) FROM `__mo_index_secondary_` WHERE prefix_eq(word,'读写')", + }, + } + + for _, c := range tests { + s, err := NewSearchAccum("src", "index", c.pattern, int64(tree.FULLTEXT_NL), "") + require.Nil(t, err) + result, err := PatternToSql(s.Pattern, int64(tree.FULLTEXT_NL), "`__mo_index_secondary_`", "") + require.Nil(t, err) + //fmt.Println(result) + assert.Equal(t, c.expect, result) + } +} diff --git a/pkg/fulltext/types.go b/pkg/fulltext/types.go index 6767842ecc10b..1b077100a2706 100644 --- a/pkg/fulltext/types.go +++ b/pkg/fulltext/types.go @@ -14,6 +14,8 @@ package fulltext +import "strings" + /* The following examples demonstrate some search strings that use boolean full-text operators: @@ -48,6 +50,35 @@ Find rows that contain words such as “apple”, “apples”, “applesauce” '"some words"' Find rows that contain the exact phrase “some words” (for example, rows that contain “some words of wisdom” but not “some noise words”). Note that the " characters that enclose the phrase are operator characters that delimit the phrase. They are not the quotation marks that enclose the search string itself. + + +# Step 1 +Pattern/Plan is generated from search string. + +In boolean mode, + +search string: 'Matrix Origin' -> pattern: "(text 0 matrix) (text 1 origin)" +search string: '"Matrix Origin"' -> pattern: "(phrase (text 0 0 matrix) (text 1 7 origin))" +search string: '+Matrix +Origin' -> pattern: "(join 0 (+ (text 0 matrix)) (+ (text 0 origin)))" +search string: '+读写汉字 -学中文' -> pattern: "(+ (text 0 读写汉字)) (- (text 1 学中文))" +search string: 'Matrix +(One)' -> pattern: "(+ (group (< (text 0 origin)) (> (text 1 one)))) (text 2 matrix)" + +In natural language mode, + +search string: '读写汉字 学中文' -> pattern: "(text 0 0 读写汉) (text 1 3 写汉字) (* 2 6 汉字*) (* 3 9 字*) (text 4 13 学中文) (* 5 16 中文*) (* 6 19 文*)" + +# Step 2 + +Generate the SQL from pattern + +# Step 3 + +Run the SQL and get the statistics + +# Step 4 + +Run Eval() to get final answer and score + */ // Parser parameters @@ -55,18 +86,6 @@ type FullTextParserParam struct { Parser string `json:"parser"` } -// Word is associated with particular DocId (index.doc_id) and could have multiple positions -type Word struct { - DocId any - Position []int32 - DocCount int32 -} - -// Word accumulator accumulate the same word appeared in multiple (index.doc_id). -type WordAccum struct { - Words map[any]*Word -} - // Search accumulator is to parse the search string into list of pattern and each pattern will associate with WordAccum by pattern.Text type SearchAccum struct { SrcTblName string @@ -74,8 +93,8 @@ type SearchAccum struct { Mode int64 Pattern []*Pattern Params string - WordAccums map[string]*WordAccum Nrow int64 + Nkeywords int } // Boolean mode search string parsing @@ -91,6 +110,7 @@ var ( RANKLESS = 6 GROUP = 7 PHRASE = 8 + JOIN = 9 ) func OperatorToString(op int) string { @@ -113,6 +133,8 @@ func OperatorToString(op int) string { return "group" case PHRASE: return "phrase" + case JOIN: + return "join" default: return "" } @@ -124,4 +146,41 @@ type Pattern struct { Operator int Children []*Pattern Position int32 + Index int32 +} + +func PatternListToString(ps []*Pattern) string { + ss := make([]string, 0, len(ps)) + for _, p := range ps { + ss = append(ss, p.String()) + } + + return strings.Join(ss, " ") +} + +func PatternToString(pattern string, mode int64) (string, error) { + ps, err := ParsePattern(pattern, mode) + if err != nil { + return "", err + } + + return PatternListToString(ps), nil +} + +func PatternListToStringWithPosition(ps []*Pattern) string { + ss := make([]string, 0, len(ps)) + for _, p := range ps { + ss = append(ss, p.StringWithPosition()) + } + + return strings.Join(ss, " ") +} + +func PatternToStringWithPosition(pattern string, mode int64) (string, error) { + ps, err := ParsePattern(pattern, mode) + if err != nil { + return "", err + } + + return PatternListToStringWithPosition(ps), nil } diff --git a/pkg/monlp/tokenizer/simple.go b/pkg/monlp/tokenizer/simple.go index dd75e64a608fc..955ccaaea52f9 100644 --- a/pkg/monlp/tokenizer/simple.go +++ b/pkg/monlp/tokenizer/simple.go @@ -144,7 +144,7 @@ func (t *SimpleTokenizer) outputLatin(pos int, yield func(Token) bool) { if t.input[t.begin+MAX_TOKEN_SIZE-1] <= 127 { bs = t.input[t.begin : t.begin+MAX_TOKEN_SIZE] } else { - bs = t.input[t.begin : pos+MAX_TOKEN_SIZE-1] + bs = t.input[t.begin : t.begin+MAX_TOKEN_SIZE-1] } } diff --git a/pkg/pb/plan/plan.pb.go b/pkg/pb/plan/plan.pb.go index b24e1dc13c160..2e590ea414591 100644 --- a/pkg/pb/plan/plan.pb.go +++ b/pkg/pb/plan/plan.pb.go @@ -4991,6 +4991,7 @@ type Stats struct { Selectivity float64 `protobuf:"fixed64,6,opt,name=selectivity,proto3" json:"selectivity,omitempty"` ForceOneCN bool `protobuf:"varint,7,opt,name=forceOneCN,proto3" json:"forceOneCN,omitempty"` HashmapStats *HashMapStats `protobuf:"bytes,8,opt,name=hashmapStats,proto3" json:"hashmapStats,omitempty"` + Sql string `protobuf:"bytes,9,opt,name=sql,proto3" json:"sql,omitempty"` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` @@ -5085,6 +5086,13 @@ func (m *Stats) GetHashmapStats() *HashMapStats { return nil } +func (m *Stats) GetSql() string { + if m != nil { + return m.Sql + } + return "" +} + type RowsetExpr struct { RowPos int32 `protobuf:"varint,1,opt,name=row_pos,json=rowPos,proto3" json:"row_pos,omitempty"` Expr *Expr `protobuf:"bytes,2,opt,name=expr,proto3" json:"expr,omitempty"` @@ -12556,12 +12564,12 @@ func init() { func init() { proto.RegisterFile("plan.proto", fileDescriptor_2d655ab2f7683c23) } var fileDescriptor_2d655ab2f7683c23 = []byte{ - // 11889 bytes of a gzipped FileDescriptorProto + // 11894 bytes of a gzipped FileDescriptorProto 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xd4, 0xbd, 0x5d, 0x8c, 0x23, 0x57, 0x76, 0x18, 0xdc, 0x6c, 0xfe, 0x1f, 0xfe, 0x74, 0xf5, 0x9d, 0x3f, 0xce, 0x68, 0x34, 0xd3, 0x53, 0x1a, 0x49, 0xa3, 0x91, 0x34, 0x92, 0x66, 0xf4, 0x33, 0x5a, 0xef, 0x7a, 0x97, 0xcd, 0x66, 0x4f, 0x73, 0x87, 0x4d, 0xf6, 0x16, 0xd9, 0x33, 0xd2, 0x1a, 0xdf, 0x57, 0x28, 0xb2, 0x8a, 0xdd, 0xa5, - 0x2e, 0x56, 0x51, 0x55, 0xc5, 0xe9, 0x6e, 0x01, 0x06, 0x36, 0x31, 0xe0, 0xc4, 0x79, 0x0d, 0xe0, + 0x2e, 0x56, 0x51, 0x55, 0xc5, 0xe9, 0x6e, 0x01, 0x06, 0x16, 0x31, 0xe0, 0xc4, 0x79, 0x35, 0xe0, 0xa7, 0x38, 0x58, 0xfb, 0x29, 0x30, 0x62, 0x20, 0x40, 0x0c, 0x38, 0x08, 0xf2, 0x96, 0x3c, 0x38, 0x46, 0x10, 0x04, 0xc8, 0x43, 0x90, 0x04, 0x70, 0x82, 0xcd, 0x83, 0x9f, 0x62, 0x3f, 0x38, 0x2f, 0x41, 0x5e, 0x82, 0x73, 0xee, 0xad, 0xaa, 0x5b, 0x24, 0x5b, 0x23, 0x69, 0xd7, 0x48, 0xf2, 0xd2, @@ -12569,7 +12577,7 @@ var fileDescriptor_2d655ab2f7683c23 = []byte{ 0x66, 0xbe, 0x17, 0x7a, 0x2c, 0x87, 0xdf, 0x37, 0xde, 0x3d, 0xb2, 0xc3, 0xe3, 0xf9, 0xe8, 0xc1, 0xd8, 0x9b, 0xbe, 0x77, 0xe4, 0x1d, 0x79, 0xef, 0x11, 0x72, 0x34, 0x9f, 0x50, 0x8a, 0x12, 0xf4, 0xc5, 0x33, 0xdd, 0x00, 0xc7, 0x1b, 0x9f, 0x88, 0xef, 0x8d, 0xd0, 0x9e, 0x5a, 0x41, 0x68, 0x4c, - 0x67, 0x1c, 0xa0, 0xfe, 0x49, 0x06, 0x72, 0xc3, 0xf3, 0x99, 0xc5, 0xea, 0xb0, 0x6e, 0x9b, 0x8d, + 0x67, 0x1c, 0xa0, 0xfe, 0x69, 0x06, 0x72, 0xc3, 0xf3, 0x99, 0xc5, 0xea, 0xb0, 0x6e, 0x9b, 0x8d, 0xcc, 0x56, 0xe6, 0x5e, 0x5e, 0x5b, 0xb7, 0x4d, 0xb6, 0x05, 0x15, 0xd7, 0x0b, 0x7b, 0x73, 0xc7, 0x31, 0x46, 0x8e, 0xd5, 0x58, 0xdf, 0xca, 0xdc, 0x2b, 0x69, 0x32, 0x88, 0xbd, 0x02, 0x65, 0x63, 0x1e, 0x7a, 0xba, 0xed, 0x8e, 0xfd, 0x46, 0x96, 0xf0, 0x25, 0x04, 0x74, 0xdc, 0xb1, 0xcf, 0x2e, @@ -12578,7 +12586,7 @@ var fileDescriptor_2d655ab2f7683c23 = []byte{ 0xdd, 0x02, 0xb0, 0xdc, 0xf9, 0xf4, 0x85, 0xe1, 0xcc, 0xad, 0xa0, 0x51, 0x24, 0x94, 0x04, 0x51, 0x7f, 0x08, 0xe5, 0x69, 0x70, 0xb4, 0x67, 0x19, 0xa6, 0xe5, 0xb3, 0x6b, 0x50, 0x9c, 0x06, 0x47, 0x7a, 0x68, 0x1c, 0x89, 0x2e, 0x14, 0xa6, 0xc1, 0xd1, 0xd0, 0x38, 0x62, 0xd7, 0xa1, 0x44, 0x88, - 0xf3, 0x19, 0xef, 0x43, 0x5e, 0x43, 0x42, 0xec, 0xb1, 0xfa, 0x57, 0x79, 0x28, 0x76, 0xed, 0xd0, + 0xf3, 0x19, 0xef, 0x43, 0x5e, 0x43, 0x42, 0xec, 0xb1, 0xfa, 0xd7, 0x79, 0x28, 0x76, 0xed, 0xd0, 0xf2, 0x0d, 0x87, 0x5d, 0x85, 0x82, 0x1d, 0xb8, 0x73, 0xc7, 0xa1, 0xec, 0x25, 0x4d, 0xa4, 0xd8, 0x55, 0xc8, 0xdb, 0x8f, 0x5f, 0x18, 0x0e, 0xcf, 0xbb, 0xb7, 0xa6, 0xf1, 0x24, 0x6b, 0x40, 0xc1, 0xfe, 0xe0, 0x63, 0x44, 0x64, 0x05, 0x42, 0xa4, 0x09, 0xf3, 0xe8, 0x21, 0x62, 0x72, 0x31, 0x86, @@ -12602,7 +12610,7 @@ var fileDescriptor_2d655ab2f7683c23 = []byte{ 0x81, 0xe1, 0x1b, 0x53, 0xcd, 0x9a, 0x30, 0x05, 0xb2, 0x33, 0x2f, 0x10, 0xbb, 0x05, 0x3f, 0xd5, 0x2e, 0x14, 0x9e, 0x19, 0x3e, 0xe2, 0x18, 0xe4, 0x5c, 0x63, 0x6a, 0x11, 0xb2, 0xac, 0xd1, 0x37, 0xee, 0x90, 0xe0, 0x3c, 0x08, 0xad, 0xa9, 0x60, 0x05, 0x22, 0x85, 0xf0, 0x23, 0xc7, 0x1b, 0x89, - 0x9d, 0x50, 0xd2, 0x44, 0x4a, 0xfd, 0xdb, 0x19, 0x28, 0xb4, 0x3c, 0x07, 0x8b, 0xbb, 0x06, 0x45, + 0x9d, 0x50, 0xd2, 0x44, 0x4a, 0xfd, 0x3b, 0x19, 0x28, 0xb4, 0x3c, 0x07, 0x8b, 0xbb, 0x06, 0x45, 0xdf, 0x72, 0xf4, 0xa4, 0xba, 0x82, 0x6f, 0x39, 0x07, 0x5e, 0x80, 0x88, 0xb1, 0xc7, 0x11, 0x7c, 0x6f, 0x16, 0xc6, 0x1e, 0x21, 0xa2, 0x06, 0x64, 0xa5, 0x06, 0x5c, 0x87, 0x52, 0x38, 0x72, 0x74, 0x82, 0xe7, 0x08, 0x5e, 0x0c, 0x47, 0x4e, 0x0f, 0x51, 0xd7, 0xa0, 0x68, 0x8e, 0x38, 0x26, 0x4f, @@ -12612,7 +12620,7 @@ var fileDescriptor_2d655ab2f7683c23 = []byte{ 0x19, 0xf2, 0xa6, 0x35, 0x0b, 0x8f, 0x39, 0x83, 0xd0, 0x78, 0x42, 0xbd, 0x0f, 0x25, 0x9c, 0x97, 0xae, 0x1d, 0x84, 0xec, 0x16, 0xe4, 0x1c, 0x3b, 0x08, 0x1b, 0x99, 0xad, 0xec, 0xc2, 0xac, 0x11, 0x5c, 0xdd, 0x82, 0xd2, 0xbe, 0x71, 0xf6, 0x0c, 0x67, 0x0e, 0x4b, 0xa3, 0x29, 0x14, 0x53, 0x22, - 0xe6, 0xb3, 0x0a, 0x30, 0x34, 0xfc, 0x23, 0x2b, 0x24, 0x7e, 0xf6, 0xd7, 0x19, 0xa8, 0x0c, 0xe6, + 0xe6, 0xb3, 0x0a, 0x30, 0x34, 0xfc, 0x23, 0x2b, 0x24, 0x7e, 0xf6, 0x37, 0x19, 0xa8, 0x0c, 0xe6, 0xa3, 0x2f, 0xe7, 0x96, 0x7f, 0x8e, 0x6d, 0xbe, 0x07, 0xd9, 0xf0, 0x7c, 0x46, 0x39, 0xea, 0x0f, 0xaf, 0xf2, 0xe2, 0x25, 0xfc, 0x03, 0xcc, 0xa4, 0x21, 0x09, 0x76, 0xc2, 0xf5, 0x4c, 0x2b, 0x1a, 0x83, 0xbc, 0x56, 0xc0, 0x64, 0xc7, 0x44, 0xa1, 0xe0, 0xcd, 0xc4, 0x2c, 0xac, 0x7b, 0x33, 0xb6, @@ -12621,7 +12629,7 @@ var fileDescriptor_2d655ab2f7683c23 = []byte{ 0x34, 0x00, 0x85, 0x41, 0xab, 0xd9, 0x6d, 0x6a, 0xca, 0x1a, 0x7e, 0xb7, 0x3f, 0xeb, 0x0c, 0x86, 0x03, 0x25, 0xc3, 0xea, 0x00, 0xbd, 0xfe, 0x50, 0x17, 0xe9, 0x75, 0x56, 0x80, 0xf5, 0x4e, 0x4f, 0xc9, 0x22, 0x0d, 0xc2, 0x3b, 0x3d, 0x25, 0xc7, 0x8a, 0x90, 0x6d, 0xf6, 0x3e, 0x57, 0xf2, 0xf4, - 0xd1, 0xed, 0x2a, 0x05, 0xf5, 0x0f, 0xd7, 0xa1, 0xdc, 0x1f, 0x7d, 0x61, 0x8d, 0x43, 0xec, 0x33, + 0xd1, 0xed, 0x2a, 0x05, 0xf5, 0x8f, 0xd6, 0xa1, 0xdc, 0x1f, 0x7d, 0x61, 0x8d, 0x43, 0xec, 0x33, 0xae, 0x52, 0xcb, 0x7f, 0x61, 0xf9, 0xd4, 0xed, 0xac, 0x26, 0x52, 0xd8, 0x11, 0x73, 0x44, 0x9d, 0xcb, 0x6a, 0xeb, 0xe6, 0x88, 0xe8, 0xc6, 0xc7, 0xd6, 0xd4, 0xa0, 0xce, 0x21, 0x1d, 0xa5, 0x70, 0x57, 0x78, 0xa3, 0x2f, 0xa8, 0x7b, 0x59, 0x0d, 0x3f, 0xd9, 0x6d, 0xa8, 0xf0, 0x32, 0xe4, 0xf5, @@ -12631,11 +12639,11 @@ var fileDescriptor_2d655ab2f7683c23 = []byte{ 0x14, 0x19, 0x41, 0xc4, 0xf7, 0xa0, 0x34, 0x9b, 0x8f, 0x74, 0xdb, 0x9d, 0x78, 0xc4, 0xdc, 0x2b, 0x0f, 0x6b, 0x7c, 0x62, 0x0e, 0xe6, 0xa3, 0x8e, 0x3b, 0xf1, 0xb4, 0xe2, 0x8c, 0x7f, 0xa8, 0x6f, 0x40, 0x51, 0xc0, 0x50, 0x7a, 0x87, 0x96, 0x6b, 0xb8, 0xa1, 0x1e, 0x8b, 0xfd, 0x12, 0x07, 0x74, - 0x4c, 0xf5, 0x8f, 0x33, 0xa0, 0x0c, 0xa4, 0x6a, 0xf6, 0xad, 0xd0, 0x58, 0xc9, 0x15, 0x5e, 0x05, + 0x4c, 0xf5, 0x4f, 0x32, 0xa0, 0x0c, 0xa4, 0x6a, 0xf6, 0xad, 0xd0, 0x58, 0xc9, 0x15, 0x5e, 0x05, 0x30, 0xc6, 0x63, 0x6f, 0xce, 0x8b, 0xe1, 0x8b, 0xa7, 0x2c, 0x20, 0x1d, 0x53, 0x1e, 0x9b, 0x6c, 0x6a, 0x6c, 0xee, 0x40, 0x35, 0xca, 0x27, 0x6d, 0xe8, 0x8a, 0x80, 0x45, 0xa3, 0x13, 0xcc, 0x53, 0xbb, 0xba, 0x18, 0xcc, 0x79, 0xee, 0xab, 0x50, 0x20, 0x1d, 0x21, 0x88, 0x46, 0x9c, 0xa7, 0xd4, - 0xbf, 0xb7, 0x0e, 0xa5, 0xdd, 0xb9, 0x3b, 0xc6, 0x26, 0xb3, 0xd7, 0x20, 0x37, 0x99, 0xbb, 0x63, + 0xbf, 0xbf, 0x0e, 0xa5, 0xdd, 0xb9, 0x3b, 0xc6, 0x26, 0xb3, 0xd7, 0x20, 0x37, 0x99, 0xbb, 0x63, 0x6a, 0x6e, 0x2c, 0x32, 0xe2, 0x95, 0xa2, 0x11, 0x12, 0xf7, 0xa0, 0xe1, 0x1f, 0xe1, 0xde, 0x5d, 0xda, 0x83, 0x08, 0x57, 0xff, 0x59, 0x86, 0x97, 0xb8, 0xeb, 0x18, 0x47, 0xac, 0x04, 0xb9, 0x5e, 0xbf, 0xd7, 0x56, 0xd6, 0x58, 0x15, 0x4a, 0x9d, 0xde, 0xb0, 0xad, 0xf5, 0x9a, 0x5d, 0x25, 0x43, @@ -12646,7 +12654,7 @@ var fileDescriptor_2d655ab2f7683c23 = []byte{ 0x9f, 0xe1, 0xde, 0x28, 0x3f, 0xef, 0xf4, 0xf4, 0x67, 0xcd, 0xee, 0x61, 0x5b, 0xf9, 0xd9, 0x7a, 0x94, 0xee, 0x6b, 0x3b, 0x6d, 0x4d, 0xf9, 0x59, 0x8e, 0x6d, 0x42, 0xf5, 0xa7, 0xfd, 0x5e, 0x7b, 0xbf, 0x79, 0x70, 0x40, 0x0d, 0xf9, 0x59, 0x49, 0xfd, 0xef, 0x39, 0xc8, 0x61, 0x4f, 0x98, 0x9a, - 0xf0, 0x81, 0xb8, 0x8b, 0xb8, 0x11, 0xb7, 0x73, 0x7f, 0xfa, 0xe7, 0xb7, 0xd7, 0x38, 0x07, 0xb8, + 0xf0, 0x81, 0xb8, 0x8b, 0xb8, 0x11, 0xb7, 0x73, 0x7f, 0xf6, 0x17, 0xb7, 0xd7, 0x38, 0x07, 0xb8, 0x03, 0x59, 0xc7, 0x0e, 0x69, 0x02, 0xe3, 0xd5, 0x23, 0x74, 0xa3, 0xbd, 0x35, 0x0d, 0x71, 0xec, 0x16, 0x64, 0x38, 0x2b, 0xa8, 0x3c, 0xac, 0x8b, 0xe5, 0x25, 0x64, 0xc9, 0xde, 0x9a, 0x96, 0x99, 0xb1, 0x9b, 0x90, 0x79, 0x21, 0xf8, 0x42, 0x95, 0xe3, 0xb9, 0x34, 0x41, 0xec, 0x0b, 0xb6, 0x05, @@ -12698,7 +12706,7 @@ var fileDescriptor_2d655ab2f7683c23 = []byte{ 0x8f, 0x3a, 0x22, 0x64, 0x9c, 0x10, 0x0d, 0x52, 0x0f, 0x35, 0x41, 0xc0, 0x54, 0xa8, 0xc5, 0x45, 0xc9, 0xda, 0x8b, 0x28, 0x8c, 0x9a, 0x7f, 0x19, 0xf2, 0x88, 0x0a, 0x1a, 0xf9, 0xad, 0x2c, 0x2a, 0xec, 0x94, 0x60, 0xef, 0x43, 0x6d, 0xec, 0x4d, 0x67, 0x7a, 0x94, 0x5d, 0x48, 0xbb, 0x34, 0x4f, - 0xa9, 0x20, 0xc9, 0x01, 0x2f, 0x4b, 0xfd, 0xdd, 0x2c, 0x94, 0xa8, 0x0d, 0x82, 0xad, 0xd8, 0xe6, + 0xa9, 0x20, 0xc9, 0x01, 0x2f, 0x4b, 0xfd, 0xbd, 0x2c, 0x94, 0xa8, 0x0d, 0x82, 0xad, 0xd8, 0xe6, 0x59, 0xc4, 0x56, 0xca, 0x5a, 0xde, 0x36, 0x91, 0x6b, 0xbf, 0x0a, 0x60, 0x23, 0x89, 0x3c, 0x94, 0x65, 0x82, 0x44, 0x4d, 0x99, 0x19, 0x7e, 0x18, 0x34, 0xb2, 0xbc, 0x29, 0x94, 0xc0, 0xf5, 0x3e, 0x77, 0xed, 0x2f, 0xe7, 0xbc, 0xf5, 0x25, 0x4d, 0xa4, 0x70, 0xdc, 0x79, 0x61, 0x34, 0x7f, 0xb2, @@ -12723,7 +12731,7 @@ var fileDescriptor_2d655ab2f7683c23 = []byte{ 0x8f, 0xa0, 0x72, 0xe0, 0x7b, 0x33, 0xcb, 0x0f, 0xa9, 0x58, 0x05, 0xb2, 0x27, 0xd6, 0xb9, 0x28, 0x15, 0x3f, 0x13, 0xc3, 0x7c, 0x5d, 0x36, 0xcc, 0x1f, 0x42, 0x29, 0xca, 0xf6, 0x8d, 0xf3, 0xfc, 0x10, 0xb9, 0x18, 0xe5, 0xb1, 0xad, 0x00, 0x2b, 0x7b, 0x00, 0x30, 0x8b, 0x01, 0x42, 0xfb, 0x88, - 0x34, 0x6f, 0x51, 0xb8, 0x26, 0x51, 0xa8, 0x7f, 0x9d, 0x85, 0xfa, 0x81, 0xe1, 0x87, 0x36, 0x4e, + 0x34, 0x6f, 0x51, 0xb8, 0x26, 0x51, 0xa8, 0x7f, 0x93, 0x85, 0xfa, 0x81, 0xe1, 0x87, 0x36, 0x4e, 0x0e, 0x1f, 0x86, 0x37, 0x21, 0x47, 0x4b, 0x9e, 0xfb, 0x00, 0x2e, 0xc5, 0x6a, 0x3b, 0xa7, 0x21, 0x35, 0x82, 0x08, 0xd8, 0xf7, 0xa0, 0x3e, 0x8b, 0xc0, 0x3a, 0xc9, 0x06, 0x3e, 0x36, 0x8b, 0x59, 0x68, 0xcc, 0x6b, 0x33, 0x39, 0xc9, 0x7e, 0x00, 0x97, 0xd3, 0x79, 0xad, 0x20, 0x48, 0xf8, 0xa8, @@ -12737,14 +12745,14 @@ var fileDescriptor_2d655ab2f7683c23 = []byte{ 0x40, 0x9c, 0x3b, 0x19, 0xbe, 0x98, 0x09, 0x07, 0xea, 0x8f, 0xa1, 0x96, 0x9a, 0x9d, 0x97, 0x0a, 0xf7, 0xeb, 0x50, 0xc2, 0xff, 0x28, 0xda, 0xc5, 0x02, 0x2c, 0x62, 0x7a, 0x10, 0xfa, 0xaa, 0x05, 0xca, 0xe2, 0x58, 0xb3, 0xbb, 0xe4, 0xe0, 0xa2, 0x49, 0x59, 0x76, 0x54, 0x45, 0x28, 0xf6, 0xf6, - 0xaa, 0x49, 0x5c, 0xa7, 0x56, 0x2f, 0x4d, 0x96, 0xfa, 0xfb, 0xeb, 0x52, 0x9b, 0x71, 0xc4, 0xd9, + 0xaa, 0x49, 0x5c, 0xa7, 0x56, 0x2f, 0x4d, 0x96, 0xfa, 0x07, 0xeb, 0x52, 0x9b, 0x71, 0xc4, 0xd9, 0xeb, 0xf2, 0xf2, 0x93, 0x36, 0x6e, 0x32, 0x66, 0x24, 0x71, 0xde, 0x02, 0xc5, 0xf3, 0x4d, 0xdb, 0x35, 0xc8, 0xe1, 0xc6, 0x87, 0x7b, 0x9d, 0xb4, 0xc5, 0x0d, 0x01, 0x3f, 0x10, 0x60, 0xb4, 0x5b, 0x4c, 0x2b, 0xf6, 0x5f, 0x08, 0xef, 0x83, 0x0c, 0x92, 0xa5, 0x53, 0x2e, 0x2d, 0x9d, 0xde, 0x84, 0xb2, 0x63, 0x05, 0x81, 0x1e, 0x1e, 0x1b, 0x2e, 0xc9, 0xef, 0x74, 0xa7, 0x4b, 0x88, 0x1c, 0x1e, 0x1b, 0x2e, 0x12, 0xda, 0xae, 0x2e, 0x4e, 0x28, 0x0a, 0xcb, 0x84, 0xb6, 0x4b, 0xf6, 0x1b, 0xca, 0xfd, 0xcb, 0xab, 0x26, 0x56, 0x88, 0x45, 0xb6, 0x3c, 0xaf, 0xea, 0xab, 0x50, 0x7c, 0x66, 0x5b, - 0xa7, 0x82, 0x97, 0xbd, 0xb0, 0xad, 0xd3, 0x88, 0x97, 0xe1, 0xb7, 0xfa, 0x97, 0x25, 0x28, 0x11, + 0xa7, 0x82, 0x97, 0xbd, 0xb0, 0xad, 0xd3, 0x88, 0x97, 0xe1, 0xb7, 0xfa, 0x57, 0x25, 0x28, 0x11, 0xf1, 0xce, 0xc5, 0x8e, 0xcd, 0x6f, 0x63, 0x6d, 0x6c, 0x09, 0x39, 0x95, 0x5b, 0x61, 0xe3, 0x70, 0xa9, 0xf5, 0x2a, 0x80, 0x24, 0x43, 0xb9, 0x46, 0x50, 0x0e, 0x63, 0xd1, 0x89, 0x6a, 0x3a, 0xe9, 0x78, 0xc1, 0x97, 0x8e, 0xf0, 0xca, 0x24, 0x00, 0xf6, 0x80, 0x2b, 0xd1, 0xe4, 0x8f, 0x29, 0xca, @@ -12783,524 +12791,524 @@ var fileDescriptor_2d655ab2f7683c23 = []byte{ 0x48, 0x03, 0x17, 0xa8, 0x8c, 0x33, 0x62, 0x88, 0x69, 0x2a, 0xe3, 0x0c, 0x97, 0xbf, 0x00, 0xec, 0x5b, 0xe1, 0xb1, 0x67, 0x92, 0x7a, 0x11, 0x2f, 0xff, 0x81, 0x8c, 0xd2, 0xd2, 0x94, 0x38, 0x9c, 0xee, 0xdc, 0x71, 0xc6, 0x6e, 0x48, 0x36, 0x54, 0x56, 0x8b, 0x92, 0x28, 0x2c, 0x7c, 0xc3, 0x3d, - 0xb2, 0x82, 0x46, 0x65, 0x2b, 0x7b, 0x2f, 0xa3, 0x89, 0x94, 0xfa, 0xb7, 0xd6, 0x21, 0xcf, 0x67, - 0xf2, 0x15, 0x28, 0x8f, 0x1c, 0x6f, 0x7c, 0xa2, 0xbb, 0xf3, 0x69, 0x74, 0x88, 0x40, 0x00, 0xd4, - 0xb7, 0xc8, 0xf6, 0x11, 0x1e, 0xbf, 0x8c, 0x46, 0xdf, 0x58, 0xa4, 0x37, 0x0f, 0xb1, 0xae, 0x2c, - 0x41, 0x45, 0x0a, 0x1b, 0xe1, 0x7b, 0xa7, 0xb4, 0x1a, 0x72, 0x84, 0x88, 0x92, 0x74, 0x4e, 0x41, - 0x72, 0x07, 0x33, 0xe5, 0x09, 0x57, 0x22, 0x40, 0xcb, 0x0d, 0x17, 0xbd, 0x93, 0x85, 0x25, 0xef, - 0x24, 0xbb, 0x05, 0x68, 0x59, 0x8d, 0xad, 0xbe, 0x6b, 0xb5, 0x7a, 0x34, 0xc2, 0x25, 0x4d, 0x82, - 0xb0, 0x8f, 0xe3, 0xb5, 0x48, 0x3d, 0x12, 0xbe, 0x63, 0xc1, 0x51, 0xe5, 0x55, 0xab, 0xa5, 0xe8, - 0xd4, 0x36, 0x80, 0xe6, 0x9d, 0x06, 0x56, 0x48, 0x3a, 0xd7, 0x35, 0x6a, 0x7e, 0xea, 0x78, 0xd0, - 0x3b, 0x3d, 0xf0, 0x82, 0x58, 0x19, 0x5b, 0x5f, 0xad, 0x8c, 0xa9, 0xef, 0x41, 0x11, 0xa5, 0xac, - 0x11, 0x1a, 0xec, 0xae, 0xf0, 0x6a, 0x72, 0x2d, 0x4b, 0xb8, 0x77, 0x93, 0x3a, 0x84, 0x9f, 0xb3, - 0x1b, 0xd5, 0x4b, 0x79, 0xee, 0x48, 0x8e, 0x8e, 0x98, 0x5b, 0x8b, 0x02, 0x85, 0xdc, 0x7e, 0x05, - 0xca, 0xd8, 0x34, 0x3a, 0x57, 0x11, 0xdb, 0xba, 0xe4, 0x7b, 0xa7, 0x2d, 0x4c, 0xab, 0xff, 0x39, - 0x03, 0x95, 0xbe, 0x6f, 0xa2, 0x98, 0x18, 0xcc, 0xac, 0xf1, 0x4b, 0x75, 0x47, 0x94, 0xf2, 0x9e, - 0xe3, 0x18, 0xb1, 0xe6, 0x85, 0x52, 0x3e, 0x02, 0xb0, 0x0f, 0x20, 0x37, 0x71, 0x8c, 0x23, 0x9a, - 0xda, 0xd8, 0xa6, 0x94, 0x8a, 0x8f, 0xbe, 0x77, 0x1d, 0xe3, 0x48, 0x23, 0x52, 0xf5, 0x37, 0xe2, - 0xfa, 0xe9, 0x84, 0x45, 0x3e, 0x57, 0x59, 0xa3, 0x33, 0xbe, 0x41, 0x4b, 0xc9, 0xb0, 0x12, 0xe4, - 0x76, 0xda, 0x83, 0x16, 0xb7, 0x24, 0xd1, 0xa6, 0x1c, 0xe8, 0xbb, 0x1d, 0x6d, 0x30, 0x54, 0x72, - 0x74, 0x68, 0x48, 0x80, 0x6e, 0x73, 0x30, 0x54, 0x4a, 0x0c, 0xa0, 0x70, 0xd8, 0xeb, 0xfc, 0xe4, - 0xb0, 0xad, 0x28, 0xea, 0xbf, 0xcf, 0x00, 0x24, 0xee, 0x7f, 0xf6, 0x36, 0x54, 0x4e, 0x29, 0xa5, - 0x4b, 0xe7, 0x42, 0x72, 0x1f, 0x81, 0xa3, 0x49, 0x03, 0x79, 0x57, 0x32, 0x28, 0x50, 0xd2, 0x2e, - 0x1f, 0x10, 0x55, 0x66, 0x89, 0x90, 0x66, 0xef, 0x40, 0xc9, 0xc3, 0x7e, 0x20, 0x69, 0x56, 0x16, - 0xb3, 0x52, 0xf7, 0xb5, 0xa2, 0xc7, 0x13, 0x28, 0x91, 0x27, 0x7e, 0xe4, 0x38, 0x8a, 0x49, 0x77, - 0x11, 0xd4, 0x72, 0x8c, 0x79, 0x60, 0x69, 0x1c, 0x1f, 0x33, 0xd9, 0x7c, 0xc2, 0x64, 0xd5, 0x9f, - 0x42, 0x7d, 0x60, 0x4c, 0x67, 0x9c, 0x15, 0x53, 0xc7, 0x18, 0xe4, 0x70, 0x4d, 0x88, 0xa5, 0x47, - 0xdf, 0xb8, 0xa1, 0x0e, 0x2c, 0x7f, 0x6c, 0xb9, 0xd1, 0xfe, 0x8b, 0x92, 0xc8, 0x5a, 0x0f, 0x03, - 0xdb, 0x3d, 0xd2, 0xbc, 0xd3, 0x28, 0x6a, 0x27, 0x4a, 0xab, 0xff, 0x38, 0x03, 0x15, 0xa9, 0x19, - 0xec, 0xbd, 0x94, 0xfd, 0xf8, 0xca, 0x52, 0x3b, 0xf9, 0xb7, 0x64, 0x47, 0xbe, 0x01, 0xf9, 0x20, - 0x34, 0xfc, 0xe8, 0x24, 0x49, 0x91, 0x72, 0x6c, 0x7b, 0x73, 0xd7, 0xd4, 0x38, 0x9a, 0xa9, 0x90, - 0xb5, 0x5c, 0x53, 0x98, 0x88, 0xcb, 0x54, 0x88, 0x54, 0xb7, 0xa0, 0x1c, 0x17, 0x8f, 0x4b, 0x40, - 0xeb, 0x3f, 0x1f, 0x28, 0x6b, 0xac, 0x0c, 0x79, 0xad, 0xd9, 0x7b, 0xd2, 0x56, 0x32, 0xea, 0x1f, - 0x67, 0x00, 0x92, 0x5c, 0xec, 0x41, 0xaa, 0xb5, 0x37, 0x16, 0x4b, 0x7d, 0x40, 0x7f, 0xa5, 0xc6, - 0xde, 0x84, 0xf2, 0xdc, 0x25, 0xa0, 0x65, 0x0a, 0x29, 0x93, 0x00, 0xd8, 0x4d, 0xc8, 0x46, 0xf1, - 0x3d, 0x0b, 0x31, 0x15, 0x2f, 0x0c, 0x47, 0xfd, 0x1e, 0x94, 0xe3, 0xe2, 0x58, 0x0d, 0xca, 0xbb, - 0xfd, 0x6e, 0xb7, 0xff, 0xbc, 0xd3, 0x7b, 0xa2, 0xac, 0x61, 0xf2, 0x40, 0x6b, 0xb7, 0xda, 0x3b, - 0x98, 0xcc, 0xe0, 0x9a, 0x6d, 0x1d, 0x6a, 0x5a, 0xbb, 0x37, 0xd4, 0xb5, 0xfe, 0x73, 0x65, 0x5d, - 0xfd, 0xad, 0x1c, 0x6c, 0xf6, 0xdd, 0x9d, 0xf9, 0xcc, 0xb1, 0xc7, 0x46, 0x68, 0x3d, 0xb5, 0xce, - 0x5b, 0xe1, 0x19, 0x0a, 0x4f, 0x23, 0x0c, 0x7d, 0xbe, 0x99, 0xcb, 0x1a, 0x4f, 0x70, 0x77, 0x5c, - 0x60, 0xf9, 0x21, 0x79, 0x1b, 0xe5, 0x5d, 0x5c, 0xe7, 0xf0, 0x96, 0xe7, 0xd0, 0x5e, 0x66, 0x3f, - 0x80, 0x2b, 0xdc, 0x85, 0xc7, 0x29, 0x51, 0xc5, 0xe4, 0x96, 0x7c, 0x76, 0x69, 0xe9, 0x32, 0x4e, - 0x88, 0x59, 0x91, 0x8c, 0x58, 0xd8, 0x6d, 0xa8, 0x24, 0xd9, 0xb9, 0x21, 0x50, 0xd6, 0x20, 0x26, - 0xa4, 0x96, 0x78, 0xae, 0x6e, 0x46, 0xad, 0xd6, 0x6d, 0xf3, 0x8c, 0x8c, 0xa3, 0xbc, 0x56, 0xf7, - 0x92, 0xce, 0xa0, 0x80, 0xfd, 0x0c, 0x36, 0x53, 0x94, 0xd4, 0x0a, 0x6e, 0x1e, 0xbd, 0x13, 0x1d, - 0x0d, 0x2c, 0xf4, 0x5e, 0x86, 0x60, 0x73, 0xb8, 0xfe, 0xb7, 0xe1, 0xa5, 0xa1, 0xc8, 0xcc, 0xec, - 0x40, 0xb7, 0x8f, 0x5c, 0xcf, 0xb7, 0x04, 0x33, 0x2f, 0xd9, 0x41, 0x87, 0xd2, 0x89, 0x85, 0x22, - 0x1d, 0xa8, 0x73, 0xd9, 0x11, 0x9d, 0x27, 0x73, 0xb4, 0xcd, 0xa5, 0x63, 0x4e, 0x2b, 0x52, 0xba, - 0x63, 0xa2, 0x71, 0xce, 0x51, 0x91, 0xd1, 0x01, 0x64, 0x74, 0x54, 0x09, 0xf8, 0x8c, 0xc3, 0x6e, - 0xf4, 0xe0, 0xf2, 0xaa, 0x46, 0xae, 0xd0, 0xa2, 0xb6, 0x64, 0x2d, 0x6a, 0xc1, 0x5d, 0x95, 0x68, - 0x54, 0xff, 0x24, 0x03, 0xd5, 0x1d, 0xcb, 0x9c, 0xcf, 0x7e, 0xec, 0xd9, 0x2e, 0x2e, 0x80, 0x0f, - 0xa1, 0xea, 0x39, 0x26, 0xcd, 0x9e, 0x14, 0x17, 0x92, 0x3a, 0x2b, 0x15, 0xc7, 0x3a, 0xe0, 0x39, - 0x66, 0xcb, 0x73, 0x28, 0x8a, 0xe4, 0x5d, 0xb8, 0xc4, 0x5d, 0x79, 0xc2, 0xb3, 0x7d, 0xc6, 0x33, - 0xaf, 0xd3, 0xcc, 0x28, 0x1c, 0xc5, 0x15, 0x1f, 0x22, 0xff, 0x35, 0xb8, 0x2c, 0x91, 0x93, 0x23, - 0x80, 0xe8, 0x97, 0x17, 0xc9, 0x66, 0x9c, 0x37, 0x3a, 0xac, 0x54, 0xff, 0x6e, 0x16, 0xca, 0xdc, - 0x11, 0x88, 0xed, 0xbd, 0x07, 0x45, 0x6f, 0xf4, 0x85, 0xee, 0x5b, 0x93, 0x8b, 0xce, 0xd8, 0x0b, - 0xde, 0xe8, 0x0b, 0xcd, 0x9a, 0xb0, 0xb7, 0x23, 0x19, 0x6e, 0x5a, 0x13, 0x31, 0x28, 0xf5, 0xb4, - 0xf6, 0x2f, 0x64, 0x3a, 0x77, 0x7b, 0x5d, 0x5a, 0xb4, 0x95, 0x6d, 0x93, 0x3b, 0xaf, 0x73, 0xda, - 0x66, 0xda, 0x54, 0xee, 0x98, 0xc1, 0xc5, 0x4e, 0x93, 0xdc, 0x85, 0x4e, 0x13, 0x76, 0x1f, 0x36, - 0x71, 0xa8, 0x93, 0x7c, 0x7c, 0x31, 0xe3, 0xb6, 0xda, 0xf0, 0x1c, 0x33, 0x71, 0x4e, 0x98, 0x67, - 0x48, 0xeb, 0x5a, 0xa7, 0x0b, 0xb4, 0x05, 0x4e, 0xeb, 0x5a, 0xa7, 0x29, 0xda, 0x47, 0x50, 0x49, - 0x76, 0x6b, 0xd0, 0x28, 0x5e, 0x3c, 0x83, 0xf1, 0xe6, 0x0d, 0x30, 0x13, 0x77, 0xe4, 0xf2, 0x4c, - 0xa5, 0x8b, 0x33, 0x71, 0x32, 0x3a, 0x6c, 0xfc, 0xe7, 0xeb, 0x50, 0xee, 0xf0, 0x32, 0xc2, 0x33, - 0x76, 0x07, 0xb2, 0x5f, 0x33, 0x0d, 0x88, 0xc3, 0x6e, 0x18, 0xa6, 0xa9, 0x1b, 0x93, 0x89, 0x35, - 0x0e, 0x2d, 0x53, 0x47, 0xfd, 0x4a, 0x30, 0xbd, 0x0d, 0xc3, 0x34, 0x9b, 0x02, 0x4e, 0xc2, 0x83, - 0xbb, 0xb5, 0x22, 0x3b, 0x93, 0x1f, 0xe4, 0x64, 0x23, 0xb7, 0x96, 0x30, 0x33, 0xf9, 0x31, 0x4e, - 0x6a, 0x66, 0x73, 0xdf, 0x6d, 0x66, 0xf3, 0xdf, 0x7a, 0x66, 0x0b, 0x17, 0xcf, 0x6c, 0xca, 0xcf, - 0x86, 0x33, 0x55, 0xa4, 0x99, 0x4a, 0x84, 0x79, 0xc7, 0x3c, 0x53, 0xff, 0x61, 0x16, 0x40, 0xb3, - 0x66, 0x8e, 0x31, 0xb6, 0xfe, 0xdf, 0x19, 0xbd, 0xdb, 0xd2, 0x32, 0x71, 0xcd, 0x28, 0x10, 0x29, - 0x5a, 0x12, 0x24, 0xfe, 0x56, 0x0e, 0x6f, 0xe1, 0x5b, 0x0f, 0x6f, 0xf1, 0x5b, 0x0c, 0x6f, 0x69, - 0x79, 0x78, 0xd9, 0x8f, 0xe0, 0x55, 0xdf, 0x3a, 0xf5, 0xed, 0xd0, 0xd2, 0x27, 0xbe, 0x37, 0xd5, - 0x53, 0xc2, 0x00, 0x79, 0x65, 0x99, 0x46, 0xe3, 0xba, 0x20, 0xda, 0xf5, 0xbd, 0x69, 0x5a, 0x20, - 0xa8, 0x7f, 0x59, 0x82, 0x4a, 0xd3, 0x35, 0x9c, 0xf3, 0xaf, 0x2c, 0x0a, 0x56, 0xa2, 0xa3, 0x9e, - 0xd9, 0x3c, 0xe4, 0xe3, 0xce, 0x4f, 0xef, 0xcb, 0x04, 0xa1, 0x11, 0xbf, 0x0d, 0x15, 0x6f, 0x1e, - 0xc6, 0x78, 0x7e, 0x9e, 0x0f, 0x1c, 0x44, 0x04, 0x71, 0xfe, 0xf8, 0x18, 0x31, 0xca, 0x4f, 0xd6, - 0x66, 0x92, 0x3f, 0xb6, 0x40, 0xe2, 0xfc, 0x44, 0x80, 0x02, 0xc2, 0x9e, 0xd2, 0xc8, 0x07, 0xf3, - 0xa9, 0xc5, 0x47, 0x3f, 0xcb, 0x83, 0x42, 0x5b, 0x02, 0x86, 0xa5, 0x4c, 0xad, 0xa9, 0xe7, 0x9f, - 0xf3, 0x52, 0x0a, 0xbc, 0x14, 0x0e, 0xa2, 0x52, 0xde, 0x01, 0x76, 0x6a, 0xd8, 0xa1, 0x9e, 0x2e, - 0x8a, 0x5b, 0x7d, 0x0a, 0x62, 0x86, 0x72, 0x71, 0x57, 0xa1, 0x60, 0xda, 0xc1, 0x49, 0xa7, 0x2f, - 0x2c, 0x3e, 0x91, 0xc2, 0xbe, 0x04, 0x63, 0x03, 0x95, 0xd2, 0xd0, 0x0a, 0x68, 0x28, 0xb3, 0x5a, - 0x19, 0x21, 0xdb, 0x08, 0x40, 0xa5, 0xc6, 0xb5, 0xc2, 0x53, 0xcf, 0xc7, 0x9c, 0xdc, 0xa0, 0x4b, - 0x00, 0xa8, 0xfc, 0x21, 0x29, 0x56, 0x44, 0x2e, 0xb4, 0xac, 0x16, 0xa7, 0xd1, 0x54, 0xe2, 0x5c, - 0x89, 0xb0, 0x55, 0xde, 0xfc, 0x04, 0xc2, 0xee, 0x42, 0x9d, 0x9a, 0x4f, 0x06, 0x1f, 0xf6, 0x81, - 0x8e, 0xdc, 0xb3, 0x5a, 0x15, 0xa1, 0xe4, 0x4d, 0x41, 0xaa, 0x4f, 0xe1, 0x7a, 0xaa, 0x7f, 0xba, - 0xe1, 0xfb, 0xc6, 0xb9, 0x3e, 0x35, 0xbe, 0xf0, 0x7c, 0xf2, 0x96, 0x65, 0xb5, 0xab, 0xf2, 0xb0, - 0x35, 0x11, 0xbd, 0x8f, 0xd8, 0x0b, 0xb3, 0xda, 0xae, 0xe7, 0x93, 0x2b, 0x6d, 0x65, 0x56, 0xc4, - 0x92, 0x0f, 0x87, 0x26, 0x98, 0xac, 0xcf, 0x80, 0x07, 0x13, 0x6b, 0x15, 0x82, 0x6d, 0x13, 0x08, - 0x6d, 0xb4, 0xe0, 0x11, 0x17, 0x76, 0x9b, 0x22, 0xe6, 0xef, 0x11, 0x89, 0x44, 0x8e, 0x38, 0xb6, - 0x0c, 0x93, 0x8e, 0xf1, 0x09, 0xb1, 0x67, 0x19, 0x14, 0x24, 0x13, 0x3c, 0xd2, 0x67, 0xf3, 0x90, - 0x47, 0x01, 0x6b, 0xf9, 0xe0, 0xd1, 0xc1, 0x3c, 0x14, 0xe0, 0x23, 0x2b, 0xa4, 0xd8, 0x5f, 0x02, - 0x3f, 0xb1, 0x42, 0xd4, 0x4d, 0x82, 0x47, 0xd1, 0x91, 0xdc, 0x15, 0x31, 0xb6, 0x8f, 0xc4, 0x99, - 0x9b, 0x0a, 0xb5, 0x18, 0xa9, 0x4f, 0xe7, 0x3c, 0xec, 0x37, 0xab, 0x55, 0x22, 0x82, 0xfd, 0xb9, - 0x83, 0x13, 0x3b, 0x36, 0xc6, 0xc7, 0x96, 0xee, 0x63, 0x53, 0xae, 0xf1, 0xa9, 0x23, 0x88, 0x86, - 0xad, 0x79, 0x05, 0x78, 0x42, 0x3f, 0xb6, 0x43, 0x72, 0xcf, 0x65, 0xb5, 0x12, 0x01, 0xf6, 0xec, - 0x10, 0xf9, 0x13, 0x47, 0x8a, 0x15, 0x48, 0x45, 0x5c, 0x27, 0xa2, 0x0d, 0x42, 0xec, 0x13, 0x9c, - 0x0a, 0xba, 0x07, 0x4a, 0x8a, 0x16, 0xcb, 0xbb, 0x41, 0xa4, 0x75, 0x89, 0x14, 0x4b, 0x7d, 0x03, - 0x78, 0x66, 0x1d, 0x97, 0x1e, 0x2f, 0xf3, 0x15, 0xee, 0x7d, 0x20, 0xf0, 0x8e, 0x1d, 0x9c, 0x50, - 0x89, 0x77, 0xa1, 0x2e, 0xd1, 0x61, 0x79, 0x37, 0xf9, 0xca, 0x88, 0xc9, 0x52, 0x6d, 0xf4, 0xad, - 0xa9, 0x17, 0x8a, 0x6e, 0xbe, 0x2a, 0xb5, 0x51, 0x23, 0x78, 0xba, 0x8d, 0x82, 0x16, 0xcb, 0xbc, - 0x25, 0xb5, 0x91, 0x93, 0x62, 0xa9, 0x77, 0xa0, 0x8a, 0x5c, 0x24, 0xb4, 0x5c, 0xbe, 0xf9, 0x6f, - 0xf3, 0x81, 0x15, 0x30, 0xda, 0xfd, 0x77, 0xa0, 0xca, 0x47, 0x5e, 0xf0, 0xed, 0x2d, 0x4e, 0x22, - 0x60, 0x48, 0xa2, 0xfa, 0xd2, 0xd1, 0xd9, 0x81, 0x3f, 0x77, 0x2d, 0xee, 0x6c, 0xa4, 0x4f, 0x53, - 0x04, 0x31, 0xc4, 0x69, 0xb6, 0x03, 0x97, 0xb8, 0x8f, 0xc1, 0x92, 0x74, 0x88, 0x28, 0x88, 0x70, - 0xe5, 0x91, 0x12, 0x8b, 0xe8, 0x63, 0x70, 0xa0, 0xfe, 0x2c, 0x03, 0x37, 0xfa, 0x14, 0x51, 0x41, - 0x0c, 0x76, 0xdf, 0x0a, 0x02, 0xe3, 0xc8, 0xda, 0xf5, 0xfc, 0xdd, 0xf9, 0x57, 0x5f, 0x9d, 0xb3, - 0x7b, 0xb0, 0x71, 0x60, 0xf8, 0x96, 0x1b, 0xc6, 0xec, 0x57, 0xe8, 0x98, 0x8b, 0x60, 0xf6, 0x98, - 0x8e, 0x6d, 0x2c, 0x37, 0x3c, 0x8c, 0xb5, 0x75, 0xd1, 0x96, 0xb4, 0x23, 0x7f, 0x89, 0x4a, 0xfd, - 0x5f, 0x5b, 0x90, 0xeb, 0x79, 0xa6, 0xc5, 0xde, 0x87, 0x32, 0x45, 0x00, 0x2f, 0x9f, 0x16, 0x22, - 0x9a, 0xfe, 0x90, 0xe1, 0x54, 0x72, 0xc5, 0xd7, 0xc5, 0x31, 0xc3, 0x77, 0xc8, 0x04, 0xa4, 0x70, - 0x03, 0x14, 0x68, 0x15, 0xe1, 0x82, 0x22, 0x1f, 0x0a, 0xc7, 0xe0, 0xd8, 0x92, 0x0b, 0xdd, 0xb7, - 0x5c, 0xd2, 0xd2, 0xf2, 0x5a, 0x9c, 0x26, 0xc3, 0xdb, 0xf7, 0x50, 0xf8, 0xf2, 0xbd, 0x9a, 0x5f, - 0x61, 0x78, 0x73, 0x3c, 0x6d, 0xde, 0xf7, 0xa1, 0xfc, 0x85, 0x67, 0xbb, 0xbc, 0xe1, 0x85, 0xa5, - 0x86, 0xa3, 0x6e, 0xcd, 0x1b, 0xfe, 0x85, 0xf8, 0x62, 0xaf, 0x41, 0xd1, 0x73, 0x79, 0xd9, 0xc5, - 0xa5, 0xb2, 0x0b, 0x9e, 0xdb, 0xe5, 0xe1, 0x78, 0xb5, 0xd1, 0xdc, 0x76, 0x4c, 0x94, 0x5d, 0x8e, - 0x35, 0x09, 0xc5, 0xa9, 0x5e, 0x85, 0x80, 0x7d, 0xb7, 0x6b, 0x4d, 0x42, 0xf6, 0x36, 0x54, 0x26, - 0xb6, 0x83, 0x32, 0x9e, 0x0a, 0x2b, 0x2f, 0x15, 0x06, 0x1c, 0x4d, 0x05, 0xbe, 0x0e, 0xa5, 0x23, - 0xdf, 0x9b, 0xcf, 0xf4, 0xd1, 0x39, 0x9d, 0xe6, 0x2d, 0x9c, 0xa3, 0x11, 0x6e, 0xfb, 0x1c, 0x05, - 0x0d, 0x7d, 0xda, 0xee, 0x91, 0x4e, 0xbe, 0x94, 0xca, 0x56, 0xf6, 0x5e, 0x49, 0xab, 0x46, 0x40, - 0xf2, 0x92, 0xbc, 0x0e, 0x25, 0xe3, 0xe8, 0x48, 0x17, 0x51, 0x85, 0x4b, 0x65, 0x19, 0x47, 0x47, - 0x54, 0xe5, 0x03, 0xa8, 0x9d, 0xda, 0xae, 0x1e, 0xcc, 0xac, 0x31, 0xa7, 0xad, 0x2d, 0x0f, 0xe5, - 0xa9, 0xed, 0x0e, 0x66, 0xd6, 0x98, 0xe8, 0x65, 0x1f, 0x46, 0xfd, 0xa5, 0x3e, 0x8c, 0x2d, 0xc8, - 0x3b, 0xf6, 0xd4, 0x0e, 0x45, 0x9c, 0x61, 0xca, 0xc8, 0x21, 0x04, 0x53, 0xa1, 0x20, 0x5c, 0xe5, - 0xca, 0x12, 0x89, 0xc0, 0xa4, 0x35, 0xa0, 0xcd, 0x97, 0x68, 0x40, 0x92, 0xc1, 0xc1, 0xbe, 0xde, - 0xe0, 0xf8, 0x88, 0xce, 0x13, 0x2d, 0x37, 0xd4, 0xa3, 0x0c, 0x97, 0x56, 0x67, 0xa8, 0x72, 0xb2, - 0x3e, 0xcf, 0xf6, 0x01, 0x54, 0x7c, 0x72, 0xae, 0xe9, 0xe4, 0x89, 0xbb, 0x2c, 0x7b, 0x27, 0x12, - 0xaf, 0x9b, 0x06, 0x7e, 0xe2, 0x81, 0x7b, 0x0d, 0x6a, 0x3c, 0xdc, 0x89, 0x07, 0xa5, 0x04, 0xc4, - 0xd8, 0xcb, 0x5a, 0x95, 0x80, 0x3c, 0x60, 0x25, 0x60, 0x0f, 0x00, 0x22, 0xd5, 0x2d, 0x3c, 0x23, - 0xce, 0x1e, 0x37, 0x85, 0xb3, 0xff, 0x56, 0x78, 0xa6, 0x95, 0xcd, 0xe8, 0x13, 0x19, 0xd6, 0xc8, - 0x76, 0x4d, 0x5c, 0x04, 0xa1, 0x71, 0x14, 0x34, 0x1a, 0xb4, 0x47, 0x2a, 0x02, 0x36, 0x34, 0x8e, - 0x02, 0x34, 0x16, 0x0d, 0xae, 0x20, 0xf1, 0xb8, 0xef, 0xeb, 0xb2, 0x27, 0x49, 0x52, 0x9d, 0xb4, - 0x8a, 0x21, 0xe9, 0x51, 0x9f, 0x00, 0x8b, 0xce, 0xe1, 0x24, 0xdb, 0xef, 0xc6, 0xd2, 0xba, 0xd8, - 0x10, 0x07, 0x71, 0xf1, 0x5d, 0x85, 0x4f, 0xa0, 0x96, 0x56, 0x68, 0x6f, 0xae, 0x38, 0x79, 0xa2, - 0x29, 0xd3, 0xaa, 0x63, 0x59, 0xc5, 0x7d, 0x0d, 0x6a, 0xae, 0x17, 0xea, 0xc4, 0xb4, 0x29, 0x23, - 0x3f, 0x5d, 0xa9, 0xba, 0x5e, 0xd8, 0x8a, 0x60, 0x38, 0x3e, 0x91, 0xd9, 0x14, 0x9e, 0x11, 0x9f, - 0x8f, 0xc7, 0x27, 0xb6, 0x71, 0x50, 0x5f, 0x8b, 0xcc, 0x1d, 0x9c, 0x27, 0xae, 0xbe, 0x53, 0x86, - 0xdb, 0xa9, 0x79, 0x8a, 0xf5, 0x7a, 0x0d, 0xfc, 0x44, 0xc7, 0xbf, 0x0d, 0x95, 0xc0, 0x9b, 0xfb, - 0x63, 0x4b, 0x0f, 0x42, 0x6b, 0xd6, 0xd8, 0xa2, 0x11, 0x05, 0x0e, 0x1a, 0x84, 0xd6, 0x8c, 0x3d, - 0x86, 0xfa, 0xcc, 0xb7, 0x74, 0x69, 0x9e, 0xee, 0xc8, 0x5d, 0x3c, 0xf0, 0xad, 0x64, 0xaa, 0xaa, - 0x33, 0x29, 0x15, 0xe5, 0x94, 0x7a, 0xa0, 0x2e, 0xe4, 0x4c, 0x3a, 0x81, 0x39, 0x13, 0xb3, 0xed, - 0x87, 0xb0, 0x29, 0xe5, 0x9c, 0x9f, 0x50, 0xe6, 0xd7, 0x52, 0x07, 0x81, 0x11, 0xf9, 0xe1, 0x09, - 0x66, 0xaf, 0xcf, 0x52, 0x69, 0xd6, 0x5c, 0xf0, 0xc9, 0xa0, 0x72, 0x7d, 0x97, 0xf2, 0x5f, 0xbb, - 0xc0, 0xd1, 0x92, 0x72, 0xd6, 0x3c, 0xe5, 0x47, 0x3e, 0x9d, 0xa0, 0xed, 0x9a, 0x8d, 0xd7, 0xf9, - 0x85, 0x22, 0x4a, 0xb0, 0x47, 0x50, 0xe5, 0x6a, 0x1e, 0x05, 0x33, 0x07, 0x8d, 0x37, 0x64, 0xa7, - 0x34, 0xe9, 0x7a, 0x84, 0xd0, 0x2a, 0x4e, 0xfc, 0x1d, 0xb0, 0x8f, 0x61, 0x93, 0x9f, 0x06, 0xc8, - 0x6c, 0xf1, 0xcd, 0xe5, 0xc5, 0x45, 0x44, 0xbb, 0x09, 0x6f, 0xd4, 0xe0, 0xba, 0x3f, 0x77, 0x49, - 0xf5, 0x13, 0x39, 0x67, 0xbe, 0x37, 0xb2, 0x78, 0xfe, 0x7b, 0x94, 0x5f, 0x74, 0x47, 0xe3, 0x64, - 0x3c, 0x2f, 0xf1, 0xa3, 0xab, 0xbe, 0x0c, 0x3a, 0xc0, 0x7c, 0x17, 0x94, 0xc9, 0xf9, 0x39, 0x95, - 0xf9, 0xd6, 0xb7, 0x29, 0x73, 0x1b, 0xf3, 0x51, 0x99, 0x0c, 0x72, 0xf3, 0xb9, 0x6d, 0x36, 0xee, - 0xf3, 0xb8, 0x63, 0xfc, 0x66, 0xaf, 0x43, 0xdd, 0xb7, 0xc6, 0x73, 0x3f, 0xb0, 0x5f, 0x58, 0x7a, - 0x60, 0xbb, 0x27, 0x8d, 0xb7, 0x69, 0x1c, 0x6b, 0x31, 0x74, 0x60, 0xbb, 0x27, 0xb8, 0x62, 0xad, - 0xb3, 0xd0, 0xf2, 0x5d, 0x1d, 0xd5, 0xed, 0xc6, 0x3b, 0xf2, 0x8a, 0x6d, 0x13, 0x62, 0x30, 0x36, - 0x5c, 0x0d, 0xac, 0xf8, 0x9b, 0xfd, 0x00, 0x36, 0x12, 0x53, 0x6b, 0x86, 0x8a, 0x47, 0xe3, 0xdd, - 0x95, 0x67, 0xc4, 0xa4, 0x94, 0x68, 0x49, 0x00, 0x05, 0xd7, 0x5f, 0xd2, 0x6b, 0x2b, 0xe0, 0x6b, - 0xeb, 0xc1, 0x37, 0x5a, 0x5b, 0x03, 0x5a, 0x5b, 0x6f, 0x40, 0xc9, 0x76, 0x43, 0xcb, 0x7f, 0x61, - 0x38, 0x8d, 0xf7, 0x96, 0x18, 0x78, 0x8c, 0x63, 0x77, 0xa1, 0x18, 0x38, 0x36, 0x32, 0xa6, 0xc6, - 0xfb, 0x4b, 0x64, 0x11, 0x8a, 0xdd, 0x83, 0x72, 0x7c, 0x83, 0xae, 0xf1, 0xc1, 0x12, 0x5d, 0x82, - 0x64, 0xb7, 0x20, 0x77, 0x8a, 0xeb, 0xf1, 0xe1, 0xf2, 0x19, 0x04, 0xc2, 0x51, 0xe2, 0x4f, 0x6c, - 0xc7, 0xe1, 0x12, 0xff, 0xd1, 0x92, 0xc4, 0xdf, 0xb5, 0x1d, 0x87, 0x4b, 0xfc, 0x89, 0xf8, 0x42, - 0x79, 0x49, 0x39, 0xb0, 0x27, 0x1f, 0x2e, 0xcb, 0x4b, 0xc4, 0x3d, 0xa3, 0xbb, 0x86, 0x95, 0x80, - 0x1c, 0xeb, 0xfc, 0x7c, 0xe0, 0x23, 0x79, 0xac, 0xd2, 0x1e, 0x77, 0x0d, 0x82, 0x38, 0x8d, 0x6a, - 0xbb, 0x38, 0x56, 0x40, 0x7b, 0xf8, 0x63, 0x7e, 0x05, 0x86, 0x43, 0xd0, 0x18, 0x7e, 0x1f, 0x6a, - 0x51, 0xf4, 0x1c, 0x56, 0x17, 0x34, 0x3e, 0x59, 0x6a, 0x41, 0x9a, 0x80, 0xed, 0x40, 0x75, 0x82, - 0x1a, 0xe0, 0x94, 0x2b, 0x84, 0x8d, 0xc7, 0xd4, 0x90, 0xad, 0x48, 0x16, 0x5f, 0xa4, 0x30, 0x6a, - 0xa9, 0x5c, 0xec, 0x01, 0x30, 0x7b, 0xc2, 0xe7, 0x13, 0x0d, 0x6c, 0xae, 0xf4, 0x35, 0x3e, 0xa5, - 0xc5, 0xb9, 0x02, 0xc3, 0x1e, 0x41, 0x2d, 0xb0, 0x5c, 0x53, 0x9f, 0x06, 0x42, 0xb3, 0xf8, 0x1e, - 0xb5, 0x53, 0xb0, 0xe1, 0xf8, 0xa6, 0xad, 0x56, 0x41, 0xaa, 0xfd, 0x80, 0xab, 0x18, 0x8f, 0x00, - 0xd7, 0xf9, 0x8b, 0x24, 0xd3, 0xaf, 0x5d, 0x90, 0x09, 0xa9, 0xa2, 0x4c, 0x8f, 0xa1, 0x6e, 0x5a, - 0xe6, 0x7c, 0xa6, 0x93, 0xe2, 0x86, 0xcb, 0xf2, 0xfb, 0x32, 0xbf, 0x94, 0x7d, 0xa2, 0x5a, 0xd5, - 0x94, 0x3d, 0xa4, 0x9f, 0xc0, 0x46, 0xe4, 0xbc, 0x0c, 0x85, 0x9f, 0xf3, 0x07, 0x72, 0x85, 0xb1, - 0x6f, 0x52, 0xab, 0xcd, 0xa3, 0xcf, 0xa8, 0x9d, 0x64, 0x33, 0x07, 0xae, 0x31, 0x0b, 0x8e, 0xbd, - 0xb0, 0xf1, 0xeb, 0xb2, 0xaa, 0x31, 0x10, 0x50, 0xad, 0x8a, 0x44, 0x51, 0x0a, 0x45, 0x57, 0xb2, - 0xb5, 0xc7, 0xa1, 0xd5, 0xf8, 0x21, 0x17, 0x5d, 0x31, 0xb0, 0x15, 0xe2, 0xb0, 0x81, 0x31, 0x9b, - 0x39, 0xe7, 0x7c, 0x39, 0xfe, 0x88, 0x96, 0xe3, 0x65, 0x69, 0x39, 0x36, 0x11, 0x49, 0xeb, 0xb1, - 0x6c, 0x44, 0x9f, 0xec, 0x21, 0x54, 0x67, 0x5e, 0x10, 0xea, 0xe6, 0xd4, 0xa1, 0xfe, 0x37, 0x65, - 0x76, 0x70, 0xe0, 0x05, 0xe1, 0xce, 0xd4, 0x21, 0x01, 0x36, 0x8b, 0xbf, 0x59, 0x17, 0x2e, 0xa5, - 0x58, 0xbd, 0x41, 0xc7, 0xf0, 0x8d, 0x6d, 0xaa, 0xf1, 0xa6, 0x54, 0xa3, 0xc4, 0xf2, 0x45, 0xf8, - 0xe6, 0xa6, 0xb7, 0x08, 0x42, 0x8b, 0x8d, 0xcf, 0x41, 0x1c, 0xc3, 0xdc, 0xe2, 0x7a, 0x0b, 0x41, - 0xa3, 0x20, 0xe6, 0xc7, 0xb0, 0x91, 0x50, 0x61, 0x07, 0x83, 0xc6, 0x8e, 0xbc, 0x7a, 0xa5, 0x9b, - 0x06, 0xb5, 0x28, 0x23, 0xc2, 0x02, 0xf5, 0xcf, 0xf2, 0x50, 0x8a, 0x8c, 0x06, 0x56, 0x81, 0xe2, - 0x61, 0xef, 0x69, 0xaf, 0xff, 0xbc, 0xc7, 0x6f, 0xfc, 0x35, 0x07, 0x83, 0xb6, 0x36, 0x54, 0x4c, - 0x56, 0x07, 0xa0, 0x1b, 0x4d, 0xfa, 0xa0, 0xd5, 0xec, 0xf1, 0x1b, 0x80, 0x74, 0x8f, 0x8a, 0xa7, - 0xd7, 0xd9, 0x26, 0xd4, 0x76, 0x0f, 0x7b, 0x14, 0x25, 0xca, 0x41, 0x59, 0x04, 0xb5, 0x3f, 0xe3, - 0x27, 0x84, 0x1c, 0x94, 0x43, 0xd0, 0x7e, 0x73, 0xd8, 0xd6, 0x3a, 0x11, 0x28, 0x4f, 0x01, 0xa7, - 0xfd, 0x43, 0xad, 0x25, 0x4a, 0x2a, 0xb0, 0x2b, 0xb0, 0x19, 0x67, 0x8b, 0x8a, 0x54, 0x8a, 0xd8, - 0xb2, 0x03, 0xad, 0xff, 0xe3, 0x76, 0x6b, 0xa8, 0x00, 0x1d, 0x37, 0x3e, 0x79, 0xa2, 0x54, 0x58, - 0x15, 0x4a, 0x3b, 0x9d, 0xc1, 0xb0, 0xd3, 0x6b, 0x0d, 0x95, 0x2a, 0x36, 0x78, 0xb7, 0xd3, 0x1d, - 0xb6, 0x35, 0xa5, 0xc6, 0x4a, 0x90, 0xfb, 0x71, 0xbf, 0xd3, 0x53, 0xea, 0x74, 0xb3, 0xab, 0xb9, - 0x7f, 0xd0, 0x6d, 0x2b, 0x1b, 0x08, 0x1d, 0xf4, 0xb5, 0xa1, 0xa2, 0x20, 0xf4, 0x79, 0xa7, 0xb7, - 0xd3, 0x7f, 0xae, 0x6c, 0xb2, 0x32, 0xe4, 0x0f, 0x7b, 0x58, 0x0d, 0x63, 0x35, 0x28, 0xd3, 0xa7, - 0xde, 0xec, 0x76, 0x95, 0x4b, 0xd2, 0x19, 0xe5, 0x65, 0x44, 0xd1, 0x89, 0xe7, 0x00, 0xdb, 0x70, - 0x05, 0xfb, 0x12, 0x27, 0x89, 0xfa, 0x2a, 0x96, 0xb3, 0xdf, 0xe9, 0x1d, 0x0e, 0x94, 0x6b, 0x48, - 0x4c, 0x9f, 0x84, 0x69, 0x60, 0x39, 0x9d, 0x1e, 0x0d, 0xe5, 0x2d, 0xfc, 0xde, 0x69, 0x77, 0xdb, - 0xc3, 0xb6, 0x72, 0x1b, 0x7b, 0xa5, 0xb5, 0x0f, 0xba, 0xcd, 0x56, 0x5b, 0xd9, 0xc2, 0x44, 0xb7, - 0xdf, 0x7a, 0xaa, 0xf7, 0x0f, 0x94, 0x3b, 0xec, 0x32, 0x28, 0xfd, 0x9e, 0xbe, 0x73, 0x78, 0xd0, - 0xed, 0xb4, 0x9a, 0xc3, 0xb6, 0xfe, 0xb4, 0xfd, 0xb9, 0xa2, 0xe2, 0xb0, 0x1f, 0x68, 0x6d, 0x5d, - 0x94, 0xf5, 0x5a, 0x94, 0x16, 0xe5, 0xdd, 0x65, 0x0a, 0x54, 0x77, 0x0f, 0x7f, 0xfa, 0xd3, 0xcf, - 0x75, 0x31, 0x0e, 0xaf, 0x63, 0x33, 0x93, 0x1c, 0xfa, 0xe1, 0x53, 0xe5, 0x8d, 0x05, 0xd0, 0xe0, - 0xa9, 0xf2, 0x26, 0x8e, 0x63, 0x34, 0x31, 0xca, 0x3d, 0x24, 0xd0, 0xda, 0xad, 0x43, 0x6d, 0xd0, - 0x79, 0xd6, 0xd6, 0x5b, 0xc3, 0xb6, 0xf2, 0x16, 0x0d, 0x5c, 0xa7, 0xf7, 0x54, 0xb9, 0x8f, 0x3d, - 0xc3, 0x2f, 0x3e, 0x5d, 0x6f, 0x33, 0x06, 0xf5, 0x84, 0x96, 0x60, 0xef, 0x20, 0xc9, 0xb6, 0xd6, - 0x6f, 0xee, 0xb4, 0x9a, 0x83, 0xa1, 0xf2, 0x2e, 0x0e, 0xcb, 0xe0, 0xa0, 0xdb, 0x19, 0x2a, 0x0f, - 0xb0, 0xef, 0x4f, 0x9a, 0xc3, 0xbd, 0xb6, 0xa6, 0xbc, 0x87, 0x33, 0x3f, 0xec, 0xec, 0xb7, 0x75, - 0x31, 0x0d, 0x0f, 0xb1, 0x8e, 0xdd, 0x4e, 0xb7, 0xab, 0x3c, 0xa2, 0x63, 0xb9, 0xa6, 0x36, 0xec, - 0xd0, 0xdc, 0x7f, 0x88, 0x05, 0x34, 0x0f, 0x0e, 0xba, 0x9f, 0x2b, 0x1f, 0x61, 0x07, 0xf7, 0x0f, - 0xbb, 0xc3, 0x8e, 0x7e, 0x78, 0xb0, 0xd3, 0x1c, 0xb6, 0x95, 0x8f, 0x69, 0x61, 0xf4, 0x07, 0xc3, - 0x9d, 0xfd, 0xae, 0xf2, 0x89, 0xfa, 0x9b, 0x50, 0x8a, 0xec, 0x48, 0xcc, 0xd5, 0xe9, 0xf5, 0xda, - 0x9a, 0xb2, 0x86, 0x25, 0x77, 0xdb, 0xbb, 0x43, 0x25, 0x43, 0x47, 0x92, 0x9d, 0x27, 0x7b, 0x43, - 0x65, 0x1d, 0x3f, 0xfb, 0x87, 0x38, 0x48, 0x59, 0xea, 0x5d, 0x7b, 0xbf, 0xa3, 0xe4, 0xf0, 0xab, - 0xd9, 0x1b, 0x76, 0x94, 0x3c, 0x2d, 0x9b, 0x4e, 0xef, 0x49, 0xb7, 0xad, 0x14, 0x10, 0xba, 0xdf, - 0xd4, 0x9e, 0x2a, 0x45, 0x5e, 0xe8, 0x4e, 0xfb, 0x33, 0xa5, 0xc4, 0x0a, 0xb0, 0xde, 0x7d, 0xa8, - 0x94, 0x11, 0xb4, 0xd3, 0xde, 0x39, 0x3c, 0x50, 0x40, 0xbd, 0x07, 0xc5, 0xe6, 0xd1, 0xd1, 0x3e, - 0x9a, 0xe9, 0xd8, 0x99, 0xc3, 0x6e, 0x97, 0x6f, 0xa3, 0xed, 0xfe, 0x70, 0xd8, 0xdf, 0x57, 0x32, - 0xb8, 0x70, 0x87, 0xfd, 0x03, 0x65, 0x5d, 0xed, 0x40, 0x29, 0x12, 0x7f, 0xd2, 0x65, 0xc5, 0x12, - 0xe4, 0x0e, 0xb4, 0xf6, 0x33, 0x7e, 0x8e, 0xde, 0x6b, 0x7f, 0x86, 0xcd, 0xc4, 0x2f, 0x2c, 0x28, - 0x8b, 0x15, 0xf1, 0x5b, 0x85, 0x74, 0x5b, 0xb1, 0xdb, 0xe9, 0xb5, 0x9b, 0x9a, 0x92, 0x57, 0x3f, - 0x4a, 0x1d, 0x51, 0x0a, 0xae, 0x81, 0xd5, 0x37, 0x3b, 0xa2, 0xfa, 0xce, 0x93, 0x5e, 0x5f, 0x6b, - 0xf3, 0xeb, 0x8f, 0x62, 0xdc, 0xd6, 0xd5, 0xb7, 0xa1, 0x1c, 0x73, 0x3c, 0x5c, 0x47, 0x2d, 0xad, - 0x3f, 0x18, 0xf0, 0x61, 0x5e, 0xc3, 0x34, 0x8d, 0x0d, 0x4f, 0x67, 0xd4, 0xff, 0x1f, 0x4a, 0x31, - 0xb3, 0xbd, 0x0b, 0xeb, 0xc3, 0x81, 0x70, 0xc1, 0x5f, 0x7e, 0x90, 0xbc, 0x52, 0x31, 0x8c, 0xbe, - 0xb4, 0xf5, 0xe1, 0x80, 0xbd, 0x03, 0x05, 0x7e, 0x47, 0x55, 0x9c, 0x22, 0x5d, 0x4e, 0x33, 0xf0, - 0x21, 0xe1, 0x34, 0x41, 0xa3, 0x76, 0xa1, 0x9e, 0xc6, 0xb0, 0x5b, 0x00, 0x1c, 0x27, 0xb9, 0x53, - 0x24, 0x08, 0xbb, 0x01, 0xd1, 0x1d, 0xd8, 0x1d, 0x11, 0x58, 0x1a, 0xa7, 0xd5, 0x7f, 0x90, 0x05, - 0x48, 0x54, 0x35, 0x54, 0x06, 0x63, 0x67, 0x49, 0x5e, 0x1c, 0x28, 0xbf, 0x02, 0x65, 0xc7, 0x33, - 0x4c, 0xf9, 0xb5, 0x89, 0x12, 0x02, 0x68, 0x34, 0xe4, 0x9b, 0x6e, 0x65, 0x1e, 0xcd, 0xc1, 0xae, - 0x42, 0x61, 0xe2, 0xf9, 0x53, 0x23, 0x0a, 0x41, 0x15, 0x29, 0x14, 0x3d, 0xfc, 0x90, 0x13, 0x15, - 0x56, 0x97, 0x6e, 0x91, 0x50, 0x3c, 0xb3, 0x00, 0x76, 0x11, 0x86, 0x26, 0x8d, 0xe5, 0x8e, 0x1d, - 0x2f, 0xb0, 0x4c, 0x34, 0xd9, 0x0b, 0xa4, 0x95, 0x42, 0x04, 0xda, 0x3e, 0xe7, 0xbd, 0xf5, 0xa7, - 0xb6, 0x6b, 0x84, 0xc2, 0xcf, 0x4c, 0xbd, 0x8d, 0x20, 0xd8, 0xdc, 0x2f, 0x02, 0x4f, 0xf8, 0x4e, - 0xf8, 0x79, 0x69, 0x09, 0x01, 0xd4, 0xdc, 0x57, 0x01, 0xac, 0x60, 0x6c, 0xcc, 0x78, 0xe1, 0x65, - 0x2a, 0xbc, 0x2c, 0x20, 0xdb, 0xe7, 0xac, 0x0b, 0xf5, 0xe1, 0x08, 0xd9, 0xbd, 0x87, 0x66, 0x70, - 0xcb, 0x73, 0x84, 0x57, 0xe3, 0xee, 0xa2, 0x4e, 0xfb, 0x20, 0x4d, 0xc6, 0x0f, 0x76, 0x17, 0xf2, - 0xde, 0x68, 0xc2, 0xa5, 0x15, 0x64, 0xdf, 0x2a, 0x40, 0xcd, 0x89, 0x66, 0xa7, 0x19, 0x86, 0x14, - 0xe4, 0x1c, 0x4b, 0xb6, 0x4c, 0x14, 0xdd, 0xcb, 0x85, 0xda, 0x2b, 0x14, 0xc3, 0x22, 0x42, 0x11, - 0xc5, 0x24, 0xc5, 0x21, 0x86, 0x6f, 0xc0, 0x06, 0x22, 0x27, 0xb6, 0xe5, 0x98, 0x82, 0x84, 0x5f, - 0x3a, 0xaa, 0x8d, 0x3d, 0x67, 0x17, 0xa1, 0x44, 0xa7, 0xfe, 0x45, 0x0e, 0x20, 0x31, 0x83, 0x52, - 0x67, 0xcb, 0x99, 0xf4, 0xd9, 0xf2, 0x43, 0xb8, 0x2a, 0xae, 0xa4, 0xc5, 0x07, 0xb4, 0xb6, 0xab, - 0x8f, 0x8c, 0xe8, 0x18, 0x9f, 0x09, 0x2c, 0x3f, 0xa3, 0xed, 0xb8, 0xdb, 0x06, 0x6a, 0x48, 0x1b, - 0x72, 0x9e, 0xf0, 0x7c, 0x96, 0x0e, 0x43, 0x90, 0xe5, 0x6e, 0x92, 0x7d, 0x78, 0x3e, 0x63, 0xef, - 0xc3, 0x15, 0xdf, 0x9a, 0xf8, 0x56, 0x70, 0xac, 0x87, 0x81, 0x5c, 0x19, 0x8f, 0x84, 0xdb, 0x14, - 0xc8, 0x61, 0x10, 0xd7, 0xf5, 0x3e, 0x5c, 0x11, 0x06, 0xd2, 0x42, 0xf3, 0xf8, 0x71, 0xe8, 0x26, - 0x47, 0xca, 0xad, 0x7b, 0x15, 0x40, 0xd8, 0x86, 0xd1, 0x9b, 0x2d, 0x25, 0xad, 0xcc, 0xed, 0x40, - 0x34, 0xe6, 0xdf, 0x01, 0x66, 0x07, 0xfa, 0xc2, 0xc9, 0x92, 0x38, 0xac, 0x57, 0xec, 0xe0, 0x20, - 0x75, 0xaa, 0x74, 0xd1, 0xa1, 0x55, 0xe9, 0xa2, 0x43, 0xab, 0xcb, 0x90, 0x27, 0xf3, 0x51, 0x9c, - 0x21, 0xf1, 0x04, 0x53, 0x21, 0x87, 0xfc, 0x91, 0xce, 0x3b, 0xea, 0x0f, 0xeb, 0x0f, 0xe8, 0xc5, - 0x1b, 0x9c, 0x1f, 0x84, 0x6a, 0x84, 0x63, 0xef, 0xc2, 0x25, 0x79, 0x50, 0xa3, 0xe7, 0x1c, 0x2a, - 0xd4, 0x4d, 0x25, 0x19, 0x46, 0x8d, 0x3f, 0xec, 0xf0, 0x36, 0x30, 0x69, 0x5c, 0x22, 0xea, 0x2a, - 0x3f, 0xf7, 0x8d, 0x07, 0x45, 0x10, 0xbf, 0x09, 0x34, 0x00, 0xdc, 0xbd, 0x5c, 0x5b, 0x36, 0x96, - 0x10, 0x49, 0xae, 0xe8, 0xf7, 0xe1, 0x4a, 0x32, 0x76, 0xba, 0x11, 0xea, 0xe1, 0xb1, 0xa5, 0x5b, - 0xae, 0x49, 0xb7, 0x14, 0x4b, 0xda, 0x66, 0x3c, 0x8c, 0xcd, 0x70, 0x78, 0x6c, 0xb5, 0x5d, 0x53, - 0xfd, 0xbd, 0x0c, 0xd4, 0xd3, 0x96, 0x1a, 0x8f, 0x5c, 0x4f, 0x42, 0xf2, 0xf3, 0x49, 0x18, 0xfe, - 0x2b, 0x50, 0x9e, 0x9d, 0x88, 0xf8, 0xfb, 0x68, 0x6d, 0xcf, 0x4e, 0x78, 0xdc, 0x3d, 0x7b, 0x0b, - 0x8a, 0xb3, 0x13, 0xbe, 0xd9, 0x2f, 0x5a, 0x4d, 0x85, 0x19, 0x0f, 0x89, 0x7d, 0x0b, 0x8a, 0x73, - 0x41, 0x9a, 0xbb, 0x88, 0x74, 0x4e, 0xa4, 0xea, 0x16, 0x54, 0x65, 0xdf, 0x08, 0xee, 0x59, 0xb4, - 0x83, 0x78, 0xc3, 0xf0, 0x53, 0xfd, 0xad, 0x75, 0x22, 0xf9, 0x56, 0xa7, 0xd5, 0xdf, 0x2a, 0x62, - 0x60, 0x8b, 0x62, 0xf8, 0x74, 0x8a, 0xd0, 0x1d, 0x7b, 0xd1, 0x43, 0x24, 0x70, 0x6c, 0x04, 0xcd, - 0x79, 0xe8, 0xb5, 0x3c, 0x47, 0xc4, 0x8d, 0x88, 0x2b, 0x4f, 0xb9, 0xc8, 0x9b, 0x2f, 0x6e, 0x43, - 0xbe, 0x2f, 0xee, 0x05, 0xd1, 0xa5, 0x3c, 0x8a, 0x55, 0xc9, 0x2f, 0xcd, 0x60, 0x35, 0xba, 0x93, - 0x47, 0x61, 0x28, 0x0f, 0x61, 0x23, 0x09, 0xc2, 0x8e, 0xc2, 0x5b, 0x16, 0xb3, 0xd4, 0xe2, 0x08, - 0x6c, 0x4c, 0xaa, 0xbf, 0x93, 0x81, 0xcd, 0x25, 0x57, 0x03, 0x8e, 0x56, 0xf2, 0x66, 0x11, 0x7e, - 0xb2, 0x3b, 0x50, 0x9d, 0x1a, 0xe1, 0xf8, 0x58, 0x9f, 0xf9, 0xd6, 0xc4, 0x3e, 0x8b, 0x1e, 0x5e, - 0x22, 0xd8, 0x01, 0x81, 0x28, 0x54, 0x67, 0x36, 0x23, 0x07, 0xcb, 0xd4, 0x0e, 0x05, 0x83, 0x02, - 0x02, 0x75, 0xc9, 0xf3, 0x1a, 0x85, 0xf1, 0xe5, 0x2e, 0x88, 0x3a, 0xbc, 0x09, 0x85, 0x4e, 0xec, - 0xd2, 0x88, 0x63, 0x4d, 0xb2, 0xe2, 0xdd, 0x11, 0x0f, 0xca, 0x2d, 0x7a, 0xc3, 0x64, 0xdf, 0x98, - 0xb1, 0xfb, 0x90, 0x9d, 0x1a, 0x33, 0x11, 0x8b, 0xd2, 0x88, 0x8f, 0x13, 0x38, 0xf6, 0xc1, 0xbe, - 0x31, 0xe3, 0x0c, 0x1d, 0x89, 0x6e, 0x7c, 0x0c, 0xa5, 0x08, 0xf0, 0xad, 0x58, 0xf7, 0x7f, 0xc9, - 0x42, 0x79, 0x47, 0x76, 0x7e, 0xa2, 0xa9, 0x16, 0xfa, 0x73, 0x17, 0x55, 0x0f, 0x71, 0xf8, 0x52, - 0x19, 0x1b, 0xee, 0x50, 0x80, 0xa2, 0x05, 0xb4, 0xfe, 0x35, 0x0b, 0xe8, 0x26, 0x80, 0x4f, 0x26, - 0x39, 0x59, 0xe5, 0xd9, 0x38, 0xee, 0xb1, 0x63, 0x8a, 0x98, 0x8e, 0xe5, 0xe3, 0xfc, 0xdc, 0x37, - 0x3f, 0xce, 0xcf, 0xaf, 0x3c, 0xce, 0xff, 0xbf, 0xe6, 0x00, 0xfe, 0x8d, 0x44, 0x7e, 0xe0, 0x9a, - 0x46, 0xb2, 0x32, 0x97, 0x62, 0xb3, 0xf8, 0xa6, 0x04, 0xd2, 0x7d, 0x0f, 0xea, 0xd1, 0x30, 0x8b, - 0x8e, 0x41, 0xea, 0x72, 0x86, 0xc0, 0x71, 0xbf, 0x6e, 0x2d, 0x94, 0x93, 0xe9, 0x1d, 0x5a, 0xf9, - 0xfa, 0x1d, 0xaa, 0xfe, 0x7e, 0x06, 0x98, 0xb0, 0x6b, 0x77, 0xe7, 0x8e, 0x33, 0xb4, 0xce, 0x88, - 0x11, 0xdc, 0x87, 0x4d, 0xe1, 0x94, 0x95, 0x02, 0xb7, 0xc4, 0x21, 0x17, 0x47, 0x24, 0x87, 0x5c, - 0xab, 0xee, 0xa5, 0xae, 0xaf, 0xbc, 0x97, 0xba, 0xfa, 0xbe, 0xeb, 0x6d, 0xa8, 0xc8, 0xb7, 0x3a, - 0xb9, 0xbe, 0x05, 0x46, 0x72, 0xa1, 0xf3, 0x3f, 0xae, 0x03, 0x24, 0xb6, 0xf7, 0xaf, 0x3a, 0x28, - 0x64, 0xc5, 0x94, 0x64, 0x57, 0x4d, 0xc9, 0x3d, 0x50, 0x64, 0x3a, 0xe9, 0x7a, 0x71, 0x3d, 0x21, - 0x8c, 0xf4, 0x18, 0x3b, 0x90, 0xaf, 0x80, 0x12, 0x4f, 0x13, 0xe7, 0xcd, 0x22, 0x50, 0x8e, 0x58, - 0xae, 0x10, 0xd1, 0x25, 0x3b, 0xe0, 0x2c, 0x98, 0x7d, 0x0a, 0xd7, 0xe3, 0x9c, 0xfa, 0xa9, 0x1d, - 0x1e, 0x7b, 0xf3, 0x50, 0x78, 0x49, 0x03, 0x21, 0xa8, 0xaf, 0x46, 0x25, 0x3d, 0xe7, 0x68, 0xce, - 0xb2, 0x02, 0xf6, 0x11, 0x94, 0x27, 0x73, 0xc7, 0xd1, 0x43, 0xeb, 0x2c, 0x14, 0xb1, 0xd2, 0x8d, - 0x94, 0xdb, 0x42, 0x9a, 0x5e, 0xad, 0x34, 0x11, 0x09, 0xf5, 0x7f, 0xae, 0x43, 0xfe, 0x27, 0x73, - 0xcb, 0x3f, 0x67, 0x1f, 0x43, 0x39, 0x08, 0xa7, 0xa1, 0x7c, 0xd0, 0x78, 0x9d, 0x17, 0x40, 0x78, - 0x3a, 0x27, 0xb4, 0xa6, 0x96, 0x1b, 0x72, 0x1f, 0x1e, 0xd2, 0x92, 0x44, 0xba, 0x0c, 0xf9, 0x20, - 0xb4, 0x66, 0x81, 0x08, 0x6c, 0xe3, 0x09, 0xb6, 0x05, 0x79, 0xd7, 0x33, 0xad, 0x20, 0x1d, 0xbe, - 0xd6, 0x43, 0xa1, 0xcf, 0x11, 0x4c, 0x85, 0x42, 0x3c, 0xe3, 0x4b, 0x87, 0x7d, 0x1c, 0x43, 0x97, - 0x0d, 0x2c, 0xc3, 0xb4, 0xdd, 0xa3, 0xe8, 0xba, 0x76, 0x9c, 0x46, 0x59, 0x4b, 0x1a, 0xbc, 0x71, - 0x14, 0xbd, 0x9d, 0x20, 0x92, 0x6c, 0x0b, 0x2a, 0xf8, 0xf9, 0xdc, 0xb7, 0x43, 0x6b, 0xf0, 0x48, - 0x8c, 0x9b, 0x0c, 0x42, 0xfd, 0xdb, 0xb4, 0x42, 0x6b, 0x1c, 0x0e, 0xbe, 0x14, 0x71, 0x5d, 0x14, - 0xb0, 0x13, 0x41, 0x54, 0x13, 0x6a, 0xa9, 0xee, 0x2e, 0x39, 0x4a, 0x06, 0xed, 0x6e, 0xbb, 0x35, - 0xe4, 0x26, 0x96, 0xb0, 0xce, 0xd7, 0x65, 0xeb, 0x3e, 0x2b, 0x99, 0xfd, 0x39, 0xc9, 0x0e, 0xcb, - 0x93, 0xd3, 0xa0, 0xad, 0x3d, 0x69, 0x2b, 0x05, 0xf5, 0x0f, 0xd6, 0x61, 0x73, 0xe8, 0x1b, 0x6e, - 0x60, 0xf0, 0x3b, 0x78, 0x6e, 0xe8, 0x7b, 0x0e, 0xfb, 0x1e, 0x94, 0xc2, 0xb1, 0x23, 0x4f, 0xc3, - 0xed, 0x68, 0xd3, 0x2f, 0x90, 0x3e, 0x18, 0x8e, 0xb9, 0x43, 0xb5, 0x18, 0xf2, 0x0f, 0xf6, 0x2e, - 0xe4, 0x47, 0xd6, 0x91, 0xed, 0x0a, 0x06, 0x7c, 0x65, 0x31, 0xe3, 0x36, 0x22, 0xf7, 0xd6, 0x34, - 0x4e, 0xc5, 0xde, 0x87, 0xc2, 0xd8, 0x9b, 0x46, 0x92, 0x2a, 0xb9, 0x2e, 0x24, 0x55, 0x84, 0xd8, - 0xbd, 0x35, 0x4d, 0xd0, 0xb1, 0x8f, 0xa1, 0xe4, 0x7b, 0x8e, 0x33, 0x32, 0xc6, 0x27, 0x42, 0x86, - 0x35, 0x16, 0xf3, 0x68, 0x02, 0xbf, 0xb7, 0xa6, 0xc5, 0xb4, 0xea, 0x03, 0x28, 0x8a, 0xc6, 0xe2, - 0x00, 0x6c, 0xb7, 0x9f, 0x74, 0xc4, 0x40, 0xb6, 0xfa, 0xfb, 0xfb, 0x9d, 0x21, 0xbf, 0x97, 0xac, - 0xf5, 0xbb, 0xdd, 0xed, 0x66, 0xeb, 0xa9, 0xb2, 0xbe, 0x5d, 0x82, 0x02, 0x77, 0xa3, 0xa9, 0xbf, - 0x9d, 0x81, 0x8d, 0x85, 0x0e, 0xb0, 0xc7, 0x90, 0x9b, 0xa2, 0x52, 0xc9, 0x87, 0xe7, 0xee, 0xca, - 0x5e, 0x4a, 0x69, 0xae, 0x6a, 0x62, 0x0e, 0xf5, 0x53, 0xa8, 0xa7, 0xe1, 0x92, 0x35, 0x5e, 0x83, - 0xb2, 0xd6, 0x6e, 0xee, 0xe8, 0xfd, 0x1e, 0xda, 0xc0, 0x68, 0x13, 0x53, 0xf2, 0xb9, 0xd6, 0x21, - 0x03, 0xfa, 0x37, 0x40, 0x59, 0x1c, 0x18, 0xf6, 0x04, 0x8d, 0x92, 0xe9, 0xcc, 0xb1, 0xb8, 0xa0, - 0x48, 0xa6, 0xec, 0xd6, 0x8a, 0x91, 0x14, 0x64, 0x34, 0x63, 0xf5, 0x71, 0x2a, 0xad, 0xfe, 0x7f, - 0xc0, 0x96, 0x47, 0xf0, 0x57, 0x57, 0xfc, 0xff, 0xc8, 0x40, 0xee, 0xc0, 0x31, 0x5c, 0xf6, 0x1a, - 0xe4, 0xe9, 0x3d, 0x1d, 0xc1, 0x3d, 0x2b, 0xd2, 0x06, 0xc7, 0x65, 0x41, 0x38, 0xf6, 0x36, 0x64, - 0xc3, 0x71, 0x74, 0x07, 0xfb, 0xda, 0x05, 0x8b, 0x6f, 0x6f, 0x4d, 0x43, 0x2a, 0x76, 0x0f, 0xb2, - 0xa6, 0x19, 0x05, 0x63, 0x0b, 0xab, 0x1f, 0x4d, 0xc5, 0x1d, 0x6b, 0x62, 0xbb, 0xb6, 0x78, 0xff, - 0x07, 0x49, 0xd8, 0xeb, 0x90, 0x35, 0xc7, 0x4e, 0x3a, 0xb2, 0x9e, 0x1b, 0x95, 0x71, 0x81, 0xe6, - 0xd8, 0x61, 0x2a, 0xd4, 0x42, 0xff, 0x5c, 0xf7, 0xe7, 0x2e, 0x85, 0x32, 0x05, 0xc2, 0xdc, 0xa9, - 0xa0, 0x32, 0x33, 0xa7, 0x78, 0xa8, 0x40, 0xdc, 0xe5, 0x9a, 0xf9, 0xd6, 0xcc, 0xf0, 0x63, 0x43, - 0xc7, 0x0e, 0x0e, 0x38, 0x60, 0xbb, 0x00, 0xf4, 0x1c, 0xa7, 0xfa, 0x0e, 0x3d, 0xf6, 0x82, 0x1a, - 0xb6, 0x1a, 0x7d, 0xad, 0xb8, 0x2a, 0x2b, 0x30, 0xea, 0x9f, 0x67, 0xa1, 0x22, 0xb5, 0x87, 0x7d, - 0x08, 0x25, 0x33, 0xbd, 0x11, 0xaf, 0x2f, 0x35, 0xfa, 0xc1, 0x4e, 0xb4, 0x05, 0x4d, 0xb1, 0xbc, - 0x3f, 0x85, 0x5a, 0x60, 0x85, 0xfa, 0x0b, 0xc3, 0xb7, 0xf9, 0x43, 0x58, 0xeb, 0xb2, 0x0b, 0x7d, - 0x60, 0x85, 0xcf, 0x22, 0xcc, 0xde, 0x9a, 0x56, 0x0d, 0xa4, 0x34, 0x99, 0x01, 0xa2, 0x4b, 0xd9, - 0xd4, 0x63, 0x61, 0x1c, 0xb8, 0xb7, 0xa6, 0x45, 0x78, 0x24, 0xb5, 0xce, 0xac, 0xf1, 0x3c, 0x8c, - 0xcc, 0x80, 0x5a, 0xd4, 0x21, 0x02, 0xd2, 0xbb, 0x84, 0xfc, 0x93, 0x3d, 0x44, 0x5e, 0x67, 0x38, - 0x8e, 0x47, 0x3a, 0x5b, 0x5e, 0x76, 0x68, 0xef, 0xc4, 0x70, 0xfe, 0x0e, 0x62, 0x94, 0x62, 0x6f, - 0x40, 0xde, 0x0b, 0x8f, 0xad, 0x48, 0x79, 0x8e, 0x9e, 0x8d, 0x41, 0xd0, 0x4e, 0xab, 0x8b, 0x2b, - 0x85, 0xd0, 0xea, 0xcf, 0x33, 0x50, 0x14, 0x23, 0xc0, 0x36, 0xa1, 0x36, 0x68, 0x0f, 0xf5, 0x67, - 0x4d, 0xad, 0xd3, 0xdc, 0xee, 0xb6, 0xc5, 0x85, 0x80, 0x27, 0x5a, 0xb3, 0x27, 0xf8, 0xa4, 0xd6, - 0x7e, 0xd6, 0x7f, 0xda, 0xe6, 0x2e, 0xae, 0x9d, 0x76, 0xef, 0x73, 0x25, 0xcb, 0xbd, 0xbc, 0xed, - 0x83, 0xa6, 0x86, 0x5c, 0xb2, 0x02, 0xc5, 0xf6, 0x67, 0xed, 0xd6, 0x21, 0xb1, 0xc9, 0x3a, 0xc0, - 0x4e, 0xbb, 0xd9, 0xed, 0xf6, 0x5b, 0xc8, 0x36, 0x0b, 0x8c, 0x41, 0xbd, 0xa5, 0xb5, 0x9b, 0xc3, - 0xb6, 0xde, 0x6c, 0xb5, 0xfa, 0x87, 0xbd, 0xa1, 0x52, 0xc4, 0x1a, 0x9b, 0xdd, 0x61, 0x5b, 0x8b, - 0x41, 0xf4, 0x94, 0xd7, 0x8e, 0xd6, 0x3f, 0x88, 0x21, 0xe5, 0xed, 0x32, 0x9a, 0x64, 0x34, 0x57, - 0xea, 0x5f, 0xd4, 0xa1, 0x9e, 0x5e, 0x9a, 0xec, 0x13, 0x28, 0x99, 0x66, 0x6a, 0x8e, 0x6f, 0xae, - 0x5a, 0xc2, 0x0f, 0x76, 0xcc, 0x68, 0x9a, 0xf9, 0x07, 0xbb, 0x13, 0x6d, 0xa4, 0xf5, 0xa5, 0x8d, - 0x14, 0x6d, 0xa3, 0x1f, 0xc2, 0x86, 0x78, 0x76, 0xc5, 0x34, 0x42, 0x63, 0x64, 0x04, 0x56, 0x7a, - 0x97, 0xb4, 0x08, 0xb9, 0x23, 0x70, 0x7b, 0x6b, 0x5a, 0x7d, 0x9c, 0x82, 0xb0, 0xef, 0x43, 0xdd, - 0x20, 0x3b, 0x37, 0xce, 0x9f, 0x93, 0x95, 0xc0, 0x26, 0xe2, 0xa4, 0xec, 0x35, 0x43, 0x06, 0xe0, - 0x42, 0x34, 0x7d, 0x6f, 0x96, 0x64, 0xce, 0xa7, 0xce, 0x72, 0x7c, 0x6f, 0x26, 0xe5, 0xad, 0x9a, - 0x52, 0x9a, 0x7d, 0x0c, 0x55, 0xd1, 0xf2, 0xc4, 0x93, 0x10, 0x6f, 0x59, 0xde, 0x6c, 0x52, 0xea, - 0xf6, 0xd6, 0xb4, 0xca, 0x38, 0x49, 0xb2, 0x47, 0xa8, 0xc9, 0x25, 0xba, 0x78, 0x51, 0x5e, 0x6b, - 0xd4, 0xda, 0x28, 0x17, 0x18, 0x71, 0x8a, 0xbd, 0x0f, 0x40, 0xed, 0xe4, 0x79, 0x4a, 0xa9, 0x10, - 0x0c, 0xdf, 0x9b, 0x45, 0x59, 0xca, 0x66, 0x94, 0x90, 0x9a, 0xc7, 0xfd, 0x40, 0xe5, 0xe5, 0xe6, - 0x91, 0x2f, 0x28, 0x69, 0x1e, 0x77, 0x21, 0xc5, 0xcd, 0xe3, 0xd9, 0x60, 0xa9, 0x79, 0x51, 0x2e, - 0xde, 0x3c, 0x9e, 0x29, 0x6a, 0x1e, 0xcf, 0x53, 0x59, 0x6c, 0x5e, 0x94, 0x85, 0x9a, 0xc7, 0x73, - 0x7c, 0x7f, 0x49, 0x77, 0xaf, 0x5e, 0xa8, 0xbb, 0xe3, 0xb4, 0xa5, 0xb5, 0xf7, 0xef, 0x43, 0x3d, - 0x38, 0xf6, 0x4e, 0x25, 0x06, 0x52, 0x93, 0x73, 0x0f, 0x8e, 0xbd, 0x53, 0x99, 0x83, 0xd4, 0x02, - 0x19, 0x80, 0xad, 0xe5, 0x5d, 0xa4, 0x7b, 0xe9, 0x75, 0xb9, 0xb5, 0xd4, 0xc3, 0x67, 0xb6, 0x75, - 0x8a, 0xad, 0x35, 0xa2, 0x04, 0x0e, 0x4a, 0xe2, 0xf7, 0x08, 0x44, 0xcc, 0x50, 0x2a, 0x9c, 0x40, - 0xd4, 0x04, 0xb1, 0x07, 0x24, 0xc0, 0xb5, 0x35, 0x77, 0xe5, 0x6c, 0x8a, 0xbc, 0xb6, 0x0e, 0xdd, - 0x54, 0xc6, 0x2a, 0x27, 0x15, 0x59, 0x93, 0x5d, 0x11, 0x58, 0x5f, 0xce, 0x2d, 0x77, 0x6c, 0x89, - 0xe8, 0xa2, 0xd4, 0xae, 0x18, 0x08, 0x5c, 0xb2, 0x2b, 0x22, 0x48, 0xbc, 0xae, 0xe3, 0xec, 0x6c, - 0x71, 0x5d, 0x4b, 0x99, 0x69, 0x5d, 0xc7, 0x59, 0xe3, 0x0d, 0x15, 0xe7, 0xbd, 0xb4, 0xb4, 0xa1, - 0xa4, 0xcc, 0x7c, 0x43, 0xc5, 0xb9, 0x1f, 0x81, 0x58, 0x4d, 0x7c, 0x70, 0x53, 0x31, 0x48, 0xbc, - 0xd5, 0x62, 0x74, 0x61, 0x1c, 0xa7, 0x70, 0xad, 0xfa, 0x16, 0xda, 0x0a, 0x62, 0x29, 0x5c, 0x91, - 0xd7, 0xaa, 0x46, 0x98, 0x78, 0x2b, 0xf9, 0x49, 0x52, 0xfd, 0xa3, 0x3c, 0x14, 0x05, 0xd3, 0x61, - 0x97, 0x60, 0x43, 0xf0, 0xbe, 0x9d, 0xe6, 0xb0, 0xb9, 0xdd, 0x1c, 0xa0, 0xb6, 0xc2, 0xa0, 0xce, - 0x99, 0x5f, 0x0c, 0xcb, 0x20, 0x43, 0x24, 0xee, 0x17, 0x83, 0xd6, 0x91, 0x21, 0x8a, 0xbc, 0xfc, - 0x1d, 0xc4, 0x2c, 0xdb, 0x80, 0x0a, 0xcf, 0xc8, 0x01, 0x74, 0x51, 0x8f, 0x72, 0xf1, 0x74, 0x5e, - 0xca, 0xc2, 0x8f, 0x3e, 0x0a, 0x49, 0x16, 0x0e, 0x28, 0xc6, 0x59, 0xa2, 0xb3, 0x11, 0x06, 0xf5, - 0xa1, 0x76, 0xd8, 0x6b, 0x25, 0xf5, 0x94, 0xe9, 0x72, 0x15, 0x2f, 0xe6, 0x59, 0xa7, 0xfd, 0x5c, - 0x01, 0xcc, 0xc4, 0x4b, 0xa1, 0x74, 0x05, 0xf5, 0x2d, 0x2a, 0x84, 0x92, 0x55, 0x76, 0x0d, 0x2e, - 0x0d, 0xf6, 0xfa, 0xcf, 0x75, 0x9e, 0x29, 0xee, 0x42, 0x8d, 0x5d, 0x06, 0x45, 0x42, 0xf0, 0xe2, - 0xeb, 0x58, 0x25, 0x41, 0x23, 0xc2, 0x81, 0xb2, 0x41, 0x87, 0x8b, 0x08, 0x1b, 0x72, 0x01, 0xa4, - 0x60, 0x57, 0x78, 0xd6, 0x7e, 0xf7, 0x70, 0xbf, 0x37, 0x50, 0x36, 0xb1, 0x11, 0x04, 0xe1, 0x2d, - 0x67, 0x71, 0x31, 0x89, 0xd8, 0xba, 0x44, 0x92, 0x0c, 0x61, 0xcf, 0x9b, 0x5a, 0xaf, 0xd3, 0x7b, - 0x32, 0x50, 0x2e, 0xc7, 0x25, 0xb7, 0x35, 0xad, 0xaf, 0x0d, 0x94, 0x2b, 0x31, 0x60, 0x30, 0x6c, - 0x0e, 0x0f, 0x07, 0xca, 0xd5, 0xb8, 0x95, 0x07, 0x5a, 0xbf, 0xd5, 0x1e, 0x0c, 0xba, 0x9d, 0xc1, - 0x50, 0xb9, 0xc6, 0xae, 0xc0, 0x66, 0xd2, 0xa2, 0x88, 0xb8, 0x21, 0x35, 0x54, 0x7b, 0xd2, 0x1e, - 0x2a, 0xd7, 0xe3, 0x66, 0xb4, 0xfa, 0xdd, 0x6e, 0x93, 0x8e, 0xc1, 0x6e, 0x20, 0x11, 0x9d, 0x0f, - 0x8a, 0xde, 0xbc, 0x82, 0xed, 0x3a, 0xec, 0xc9, 0xa0, 0x9b, 0xd2, 0xd2, 0x18, 0xb4, 0x7f, 0x72, - 0xd8, 0xee, 0xb5, 0xda, 0xca, 0xab, 0xc9, 0xd2, 0x88, 0x61, 0xb7, 0xe2, 0xa5, 0x11, 0x83, 0x6e, - 0xc7, 0x75, 0x46, 0xa0, 0x81, 0xb2, 0x85, 0xe5, 0x89, 0x76, 0xf4, 0x7a, 0xed, 0xd6, 0x10, 0xfb, - 0x7a, 0x27, 0x1e, 0xc5, 0xc3, 0x83, 0x27, 0x5a, 0x73, 0xa7, 0xad, 0xa8, 0x08, 0xd1, 0xda, 0xbd, - 0xe6, 0x7e, 0x34, 0xdb, 0xaf, 0x6d, 0x57, 0xe9, 0x6d, 0x65, 0x21, 0x2e, 0xd5, 0x1f, 0x03, 0x93, - 0x1f, 0x29, 0x15, 0xef, 0x90, 0x31, 0xc8, 0x4d, 0x7c, 0x6f, 0x1a, 0x5d, 0x4c, 0xc7, 0x6f, 0xb4, - 0xd5, 0x66, 0xf3, 0x11, 0x9d, 0x65, 0x25, 0x37, 0x57, 0x65, 0x90, 0xfa, 0x47, 0x19, 0xa8, 0xa7, - 0x45, 0x25, 0xaa, 0x88, 0xf6, 0x44, 0x77, 0xbd, 0x90, 0x3f, 0x70, 0x15, 0x44, 0x9e, 0x28, 0x7b, - 0xd2, 0xf3, 0x42, 0x7a, 0xe1, 0x8a, 0x4c, 0xc7, 0x58, 0xf2, 0xf1, 0x52, 0xe3, 0x34, 0xeb, 0xc0, - 0xa5, 0xd4, 0x1b, 0xae, 0xa9, 0xe7, 0xc5, 0x1a, 0xf1, 0xcb, 0x93, 0x0b, 0xed, 0xd7, 0x58, 0xb0, - 0xdc, 0x27, 0x05, 0xb2, 0xc1, 0x97, 0x8e, 0x70, 0x04, 0xe0, 0xa7, 0xba, 0x07, 0xb5, 0x94, 0x64, - 0x26, 0x8b, 0x7f, 0x92, 0x6e, 0x69, 0xc9, 0x9e, 0xbc, 0xbc, 0x99, 0xea, 0x1f, 0x66, 0xa0, 0x2a, - 0xcb, 0xe9, 0xef, 0x5c, 0x12, 0xdd, 0x50, 0x11, 0xdf, 0xba, 0x6d, 0x46, 0x0f, 0x5b, 0x45, 0xa0, - 0x0e, 0xbd, 0x29, 0xcf, 0x7d, 0xb0, 0xbb, 0x27, 0x83, 0xb8, 0x3b, 0x32, 0x08, 0x4d, 0x66, 0xba, - 0xb9, 0xb8, 0xfb, 0x14, 0x09, 0xc4, 0x1d, 0x97, 0x04, 0xa2, 0xde, 0x86, 0xf2, 0xee, 0x49, 0x14, - 0x9e, 0x20, 0x3f, 0xf3, 0x56, 0xe6, 0xd7, 0x9d, 0xd5, 0x3f, 0xc9, 0x40, 0x3d, 0x79, 0xba, 0x83, - 0x82, 0x1e, 0xf9, 0xdb, 0xbf, 0x7c, 0x39, 0xac, 0x9b, 0xa3, 0xe4, 0xb9, 0xf9, 0x75, 0xf9, 0xb9, - 0xf9, 0xd7, 0x44, 0x61, 0x59, 0x59, 0x9a, 0xc5, 0x75, 0x89, 0xcb, 0xd4, 0x8f, 0xa0, 0x8a, 0xff, - 0x35, 0x6b, 0x62, 0xf9, 0xbe, 0x15, 0x3d, 0x83, 0xbc, 0x44, 0x9c, 0x22, 0x22, 0x8b, 0xc4, 0x9a, - 0x08, 0xc5, 0x68, 0xe5, 0xeb, 0x22, 0x88, 0x57, 0xff, 0x45, 0x0e, 0x2a, 0x92, 0xd6, 0xf3, 0x8d, - 0x96, 0xdf, 0x4d, 0x28, 0x27, 0xef, 0x56, 0x88, 0x1b, 0xac, 0x31, 0x20, 0x35, 0x57, 0xd9, 0x85, - 0xb9, 0x6a, 0x40, 0x51, 0x44, 0x47, 0x0a, 0xbf, 0x67, 0x94, 0x4c, 0x3b, 0xf6, 0xf2, 0x2f, 0x71, - 0xbd, 0x7f, 0x00, 0x55, 0xc9, 0x2b, 0x17, 0x3d, 0x82, 0xb3, 0x48, 0x5f, 0x49, 0x3c, 0x74, 0x01, - 0xbb, 0x02, 0x85, 0xc9, 0x89, 0x6e, 0x8e, 0x22, 0x37, 0x67, 0x7e, 0x72, 0xb2, 0x33, 0xa2, 0xa3, - 0x8b, 0x49, 0x2c, 0xe8, 0xb9, 0xaf, 0xa4, 0x34, 0x89, 0xc4, 0xf9, 0x3d, 0x28, 0x4e, 0x4e, 0xf8, - 0xf5, 0xb8, 0xb2, 0x1c, 0xf0, 0x93, 0x0c, 0x79, 0x61, 0x72, 0x42, 0x97, 0xe9, 0x3e, 0x05, 0x65, - 0xc1, 0xa7, 0x1a, 0x88, 0x93, 0xc9, 0xc5, 0x46, 0x6d, 0xa4, 0xdd, 0xab, 0x01, 0x7b, 0x0f, 0x2e, - 0x0b, 0xc9, 0x6b, 0x04, 0x3a, 0x8f, 0xd7, 0xa7, 0xa7, 0x50, 0xf8, 0x7b, 0x71, 0x9b, 0x1c, 0xd7, - 0x0c, 0x06, 0x84, 0xc1, 0xc5, 0xaa, 0x42, 0x55, 0x5a, 0xbb, 0xfc, 0x9d, 0x99, 0xb2, 0x96, 0x82, - 0xb1, 0xc7, 0x50, 0x9d, 0x9c, 0xf0, 0xb5, 0x30, 0xf4, 0xf6, 0x2d, 0x11, 0x83, 0x7d, 0x79, 0x71, - 0x15, 0x50, 0xa8, 0x6e, 0x8a, 0x92, 0xbd, 0x0b, 0xcc, 0xb7, 0x42, 0xcb, 0xa5, 0x9e, 0x98, 0x96, - 0x61, 0x3a, 0xb6, 0x6b, 0x91, 0xb2, 0x95, 0xd5, 0x36, 0x63, 0xcc, 0x8e, 0x40, 0xa8, 0xff, 0x32, - 0x03, 0xf5, 0x44, 0xfb, 0xc5, 0x0d, 0xcd, 0xee, 0xcb, 0x0f, 0x80, 0x37, 0x16, 0x15, 0x64, 0x24, - 0x79, 0x30, 0x3c, 0x9f, 0xf1, 0x47, 0x42, 0x57, 0x3d, 0x16, 0xb4, 0xca, 0xe5, 0x9a, 0x5d, 0xe5, - 0x72, 0x55, 0x9f, 0x40, 0x76, 0x78, 0x3e, 0xe3, 0x9e, 0x16, 0x94, 0x81, 0xdc, 0x2a, 0xe3, 0xd2, - 0x8f, 0xe2, 0x13, 0x9e, 0xb6, 0x3f, 0xe7, 0xb7, 0xf7, 0x0f, 0xb4, 0xce, 0x7e, 0x53, 0xfb, 0x9c, - 0x22, 0x4f, 0x48, 0x4b, 0xd8, 0xed, 0x6b, 0xed, 0xce, 0x93, 0x1e, 0x01, 0x72, 0xe4, 0x87, 0x49, - 0x9a, 0xd8, 0x34, 0xcd, 0xdd, 0x13, 0xf9, 0xcd, 0x94, 0x4c, 0xea, 0xcd, 0x94, 0xf4, 0x85, 0xdf, - 0xf5, 0xc5, 0x0b, 0xbf, 0x2c, 0xde, 0xd1, 0x31, 0x7b, 0x60, 0x6f, 0x42, 0x6e, 0x72, 0x62, 0x9d, - 0xa7, 0x4d, 0x9c, 0xf4, 0x66, 0x24, 0x02, 0xf5, 0x17, 0x19, 0x60, 0xa9, 0x86, 0x70, 0xad, 0xfb, - 0xbb, 0xb6, 0xe5, 0x13, 0x68, 0x88, 0x17, 0x35, 0x39, 0x95, 0xe4, 0xe3, 0x15, 0x43, 0x7a, 0xc5, - 0x4b, 0x22, 0xfb, 0x92, 0xf7, 0x8c, 0xd8, 0x7b, 0xc0, 0x9f, 0x34, 0xc4, 0x05, 0x92, 0x76, 0x6a, - 0x48, 0xbc, 0x42, 0x4b, 0x68, 0x92, 0x37, 0x0c, 0xe5, 0xb7, 0x19, 0xb9, 0x7b, 0x78, 0x23, 0x99, - 0x35, 0xe2, 0x1f, 0xea, 0xef, 0x66, 0xe0, 0x52, 0x7a, 0x41, 0xfc, 0x72, 0xbd, 0x4c, 0x3f, 0x44, - 0x99, 0x5d, 0x7c, 0x88, 0x72, 0xd5, 0x7a, 0xca, 0xad, 0x5c, 0x4f, 0x7f, 0x27, 0x03, 0x97, 0xa5, - 0xd1, 0x4f, 0xec, 0xa4, 0xbf, 0xa1, 0x96, 0x49, 0xef, 0x51, 0xe6, 0x52, 0xef, 0x51, 0xaa, 0x7f, - 0x90, 0x81, 0xab, 0x0b, 0x2d, 0xd1, 0xac, 0xbf, 0xd1, 0xb6, 0xa4, 0xdf, 0xad, 0x24, 0x17, 0x35, - 0x0f, 0x75, 0xe4, 0xd7, 0x12, 0x59, 0xfa, 0x21, 0x4a, 0xba, 0x97, 0xfd, 0xaf, 0xd2, 0x8d, 0x34, - 0x93, 0x5b, 0x46, 0xec, 0x23, 0xa8, 0x24, 0x1a, 0x53, 0xf4, 0x50, 0xc8, 0xca, 0x2b, 0x4a, 0x32, - 0xdd, 0x4a, 0x36, 0xba, 0xfe, 0xcd, 0xd8, 0xe8, 0x63, 0xa8, 0xc6, 0x05, 0xef, 0x58, 0x93, 0xb4, - 0x37, 0x62, 0xe1, 0x61, 0xab, 0x14, 0xa5, 0xfa, 0x21, 0x6c, 0x26, 0xbd, 0x68, 0x89, 0xc7, 0xd8, - 0x6e, 0x43, 0xc5, 0xb5, 0x4e, 0xf5, 0xe8, 0xa9, 0x36, 0x11, 0xb3, 0xe3, 0x5a, 0xa7, 0x82, 0x40, - 0xdd, 0x95, 0xf9, 0x5e, 0xfc, 0xae, 0xbe, 0x63, 0xa6, 0x82, 0x3f, 0x3c, 0xc7, 0x8c, 0x50, 0x58, - 0x9a, 0x34, 0x31, 0x45, 0xd7, 0x3a, 0xa5, 0x35, 0x77, 0x2a, 0xca, 0x69, 0x9a, 0xa6, 0x38, 0x30, - 0x5f, 0xf5, 0xc4, 0xd1, 0x75, 0x28, 0xcd, 0xfc, 0xd4, 0xcc, 0x16, 0x67, 0x3e, 0xaf, 0xf6, 0xae, - 0x88, 0x08, 0xba, 0xe8, 0x70, 0x9d, 0xc7, 0x08, 0x89, 0xdf, 0xdd, 0xc8, 0x25, 0xbf, 0xbb, 0xf1, - 0x91, 0x60, 0x79, 0xb8, 0xff, 0x44, 0xcd, 0xf1, 0x21, 0x7a, 0xe6, 0x5e, 0x8d, 0x0e, 0xd1, 0x49, - 0x03, 0xb4, 0xbe, 0x14, 0x41, 0x49, 0xf8, 0xa9, 0x6e, 0x43, 0x45, 0xb2, 0xec, 0x50, 0x35, 0x91, - 0xbc, 0x22, 0x41, 0xfa, 0x19, 0x99, 0x64, 0x80, 0xb4, 0x4a, 0xe2, 0x14, 0x09, 0xd4, 0xdf, 0x03, - 0x80, 0x04, 0x97, 0x52, 0x18, 0x32, 0x0b, 0x0a, 0xc3, 0xb7, 0x3a, 0x91, 0xff, 0x10, 0xea, 0x63, - 0x6f, 0x76, 0xae, 0x27, 0x39, 0xb2, 0x2b, 0x73, 0x54, 0x91, 0x6a, 0x98, 0xdc, 0xef, 0x59, 0x3e, - 0x69, 0xcd, 0xad, 0x3c, 0x69, 0xfd, 0x00, 0x8a, 0xdc, 0x71, 0x1f, 0x88, 0xfb, 0x61, 0xd7, 0x16, - 0xfb, 0xf9, 0x40, 0xc4, 0xbe, 0x46, 0x74, 0xac, 0x8d, 0x56, 0xb9, 0x78, 0xb7, 0x51, 0xbe, 0x2d, - 0x76, 0x6b, 0x39, 0x67, 0x44, 0xc6, 0x1f, 0x0b, 0x33, 0xe4, 0xa4, 0xa4, 0x24, 0x84, 0x53, 0xe1, - 0x4d, 0x22, 0x25, 0xa1, 0x28, 0x2b, 0x09, 0xc3, 0x29, 0xf7, 0x21, 0xa1, 0x92, 0xf0, 0x2e, 0x5c, - 0x12, 0x31, 0xf8, 0x98, 0x01, 0x87, 0x93, 0xe8, 0x79, 0xb8, 0x95, 0x78, 0xdb, 0x63, 0x38, 0x25, - 0xed, 0x1b, 0xc9, 0x3f, 0x83, 0xcb, 0xe3, 0x63, 0xc3, 0x3d, 0xb2, 0xf4, 0x70, 0xe4, 0xe8, 0xf4, - 0x6c, 0xb8, 0x3e, 0x35, 0x66, 0x42, 0xed, 0x79, 0x73, 0xa9, 0xb1, 0x2d, 0x22, 0x1e, 0x8e, 0x1c, - 0x8a, 0xd0, 0x89, 0xcf, 0xe3, 0x37, 0xc7, 0x8b, 0xf0, 0x85, 0xd3, 0x28, 0x58, 0x3c, 0x8d, 0x5a, - 0xd2, 0x66, 0x2a, 0xcb, 0xda, 0xcc, 0x8d, 0xff, 0x90, 0x83, 0x82, 0x88, 0x05, 0xbc, 0x0f, 0x39, - 0xd3, 0xf7, 0x66, 0x71, 0xc8, 0xde, 0x0a, 0xed, 0x82, 0x7e, 0x63, 0x08, 0x15, 0x91, 0x07, 0x50, - 0x30, 0x4c, 0x53, 0x9f, 0x9c, 0xa4, 0x4f, 0x8c, 0x16, 0x04, 0xfd, 0xde, 0x9a, 0x96, 0x37, 0x48, - 0xe2, 0x7f, 0x02, 0x65, 0xa4, 0x4f, 0xe2, 0xaf, 0x2a, 0xcb, 0xea, 0x4b, 0x24, 0x92, 0xf7, 0xd6, - 0xb4, 0x92, 0x11, 0x89, 0xe7, 0x1f, 0xa4, 0x7d, 0x6f, 0x5c, 0x5e, 0xde, 0x58, 0xca, 0x7a, 0x91, - 0x17, 0xee, 0xd7, 0x81, 0x3b, 0x63, 0x62, 0x6e, 0x93, 0x97, 0x0f, 0x27, 0x96, 0x78, 0xd3, 0xde, - 0x9a, 0xc6, 0xf7, 0x5c, 0xc4, 0xab, 0x3e, 0x8a, 0xfc, 0x62, 0xf1, 0xaf, 0x81, 0xac, 0x18, 0x19, - 0xe4, 0x15, 0xb1, 0x73, 0x8c, 0x18, 0x07, 0x66, 0x33, 0xcd, 0x28, 0x6c, 0xa7, 0xb8, 0x94, 0x2d, - 0xe6, 0x48, 0x94, 0x2d, 0x66, 0x4f, 0x8f, 0xa1, 0x42, 0x2e, 0x2a, 0x91, 0xaf, 0xb4, 0x34, 0xb4, - 0x09, 0x43, 0x21, 0xc7, 0x7b, 0xc2, 0x5e, 0x5a, 0x51, 0x3f, 0x7d, 0x4b, 0xf6, 0x6d, 0xde, 0x5c, - 0x39, 0x50, 0x5a, 0xec, 0xe6, 0xe4, 0x9d, 0xd5, 0x78, 0x1e, 0xb6, 0x0d, 0x55, 0x43, 0x92, 0x34, - 0xc2, 0xd1, 0x79, 0x73, 0xc5, 0x3c, 0xc5, 0x34, 0x54, 0x86, 0x94, 0x4e, 0x0e, 0xe0, 0x6e, 0x68, - 0x70, 0x75, 0xf5, 0x52, 0x96, 0x23, 0x49, 0x72, 0x3c, 0x92, 0x44, 0x4d, 0xbf, 0xaf, 0x92, 0xbe, - 0xe4, 0x2a, 0xc5, 0x95, 0xfc, 0x08, 0x6d, 0x64, 0x79, 0xf3, 0x56, 0xa0, 0x18, 0xbd, 0x41, 0x4c, - 0x61, 0xb1, 0xad, 0xfe, 0xc1, 0xe7, 0x4a, 0x06, 0xc1, 0x9d, 0xde, 0x60, 0xd8, 0xec, 0x89, 0xe3, - 0xd5, 0x4e, 0x4f, 0x1c, 0xaf, 0xaa, 0xff, 0x26, 0x0b, 0xe5, 0xd8, 0x3d, 0xfc, 0xdd, 0x0d, 0xe3, - 0xd8, 0xe2, 0xcc, 0xca, 0x16, 0xe7, 0x82, 0xa6, 0x26, 0x3f, 0x5a, 0xb2, 0x91, 0xd6, 0x87, 0x82, - 0xe5, 0xfb, 0x77, 0xf9, 0x6f, 0x78, 0xff, 0x4e, 0x8e, 0x4c, 0x2c, 0xa4, 0x23, 0x13, 0x17, 0xde, - 0xa1, 0x2e, 0x52, 0x98, 0x8a, 0xfc, 0x0e, 0xf5, 0x85, 0xf1, 0x29, 0xa5, 0x8b, 0xe3, 0x53, 0xe8, - 0x87, 0xd4, 0x9e, 0xd9, 0xd6, 0xa9, 0x08, 0xd0, 0x13, 0xa9, 0xb4, 0xf8, 0x80, 0x97, 0x88, 0x8f, - 0x6f, 0xc0, 0x8a, 0xd8, 0x43, 0xb8, 0x3c, 0x39, 0x89, 0xdf, 0xdc, 0x4c, 0x0c, 0xac, 0x2a, 0x75, - 0x63, 0x25, 0x4e, 0xfd, 0xfb, 0x19, 0x80, 0xc4, 0x87, 0xfa, 0x4b, 0x3b, 0x78, 0x24, 0x1b, 0x3a, - 0xfb, 0x35, 0x36, 0xf4, 0x4b, 0x1e, 0xf6, 0x50, 0xbf, 0x84, 0x72, 0xec, 0x35, 0xff, 0xee, 0x6b, - 0xec, 0x5b, 0x55, 0xf9, 0x9b, 0x91, 0xb3, 0x2b, 0x76, 0x3b, 0xff, 0xb2, 0x63, 0x91, 0xaa, 0x3e, - 0xfb, 0x92, 0xea, 0xcf, 0xb8, 0xc7, 0x29, 0xae, 0xfc, 0x57, 0xbc, 0xb1, 0xe4, 0x35, 0x9f, 0x4b, - 0xad, 0x79, 0x75, 0x2e, 0xdc, 0x66, 0xbf, 0x7c, 0xd5, 0xdf, 0xaa, 0xc3, 0x7f, 0x95, 0x89, 0x7c, - 0x3b, 0xf1, 0x4b, 0xa6, 0x17, 0x2a, 0x5a, 0xab, 0xdd, 0x53, 0xdf, 0xa6, 0xba, 0xaf, 0xb5, 0x36, - 0x73, 0x5f, 0x67, 0x6d, 0xbe, 0x09, 0x79, 0x2e, 0x10, 0xf2, 0x17, 0x59, 0x9a, 0x1c, 0xff, 0xd2, - 0xb7, 0xff, 0x55, 0x55, 0x28, 0x96, 0xbc, 0xbf, 0x97, 0xa3, 0x72, 0xa3, 0xdf, 0x2d, 0xa0, 0x20, - 0xea, 0xdf, 0xce, 0x70, 0xee, 0xfa, 0x5d, 0xc7, 0xe4, 0x57, 0x66, 0x6e, 0xfe, 0xd3, 0x75, 0xa8, - 0xa5, 0x0e, 0xcc, 0xbe, 0x43, 0x63, 0x56, 0x72, 0xf3, 0xec, 0x6a, 0x6e, 0xfe, 0x5d, 0x9e, 0xac, - 0xfa, 0x3f, 0x22, 0x01, 0x52, 0x31, 0x66, 0xa5, 0x74, 0x8c, 0x19, 0x72, 0xd3, 0x6a, 0x4a, 0x2b, - 0x5f, 0xa5, 0xbf, 0x67, 0x56, 0xea, 0xef, 0xb7, 0xe2, 0x9f, 0x0d, 0xeb, 0xec, 0x70, 0xc3, 0xb2, - 0xa6, 0x49, 0x10, 0xf6, 0x29, 0x5c, 0xe7, 0x5a, 0x0d, 0x57, 0xe4, 0x74, 0x6f, 0xa2, 0xc7, 0x3f, - 0x2a, 0x26, 0xe2, 0xe6, 0xae, 0x72, 0x02, 0xfe, 0xc3, 0x10, 0x93, 0x66, 0x84, 0x55, 0x3b, 0x50, - 0x4b, 0x9d, 0x5e, 0x4a, 0x3f, 0x50, 0x98, 0x91, 0x7f, 0xa0, 0x90, 0x6d, 0x41, 0xfe, 0xf4, 0xd8, - 0xf2, 0xad, 0x15, 0x4f, 0x3b, 0x72, 0x84, 0xfa, 0x7d, 0xa8, 0xca, 0x91, 0x14, 0xec, 0x1d, 0xc8, - 0xdb, 0xa1, 0x35, 0x8d, 0x6c, 0xab, 0xab, 0xcb, 0xc1, 0x16, 0x64, 0x48, 0x73, 0x22, 0xf5, 0xe7, - 0x19, 0x50, 0x16, 0x71, 0xd2, 0xaf, 0x28, 0x66, 0x2e, 0xf8, 0x15, 0xc5, 0xf5, 0x54, 0x23, 0x57, - 0xfd, 0x10, 0x62, 0xfc, 0xbc, 0x5c, 0xee, 0x82, 0xe7, 0xe5, 0xd8, 0x1b, 0x50, 0xf2, 0x2d, 0xfa, - 0x89, 0x3a, 0x73, 0x45, 0x2c, 0x73, 0x8c, 0x53, 0x7f, 0x27, 0x03, 0x45, 0x11, 0xf6, 0xb1, 0xd2, - 0xd8, 0x7d, 0x0b, 0x8a, 0xfc, 0xe7, 0xea, 0x22, 0xe3, 0x7f, 0x29, 0x0e, 0x32, 0xc2, 0xb3, 0x5b, - 0x3c, 0x18, 0x26, 0x6d, 0xfc, 0x1e, 0x38, 0x86, 0xab, 0x11, 0x5c, 0xfc, 0xde, 0x8b, 0x31, 0x15, - 0xd7, 0x08, 0xf9, 0xab, 0x1f, 0x40, 0x20, 0x7e, 0x63, 0xf0, 0x07, 0x50, 0x14, 0x61, 0x25, 0x2b, - 0x9b, 0xf2, 0xb2, 0x1f, 0x6a, 0xdb, 0x02, 0x48, 0xe2, 0x4c, 0x56, 0x95, 0xa0, 0xde, 0x87, 0x52, - 0x14, 0x5a, 0x82, 0xeb, 0x2f, 0xa9, 0x5a, 0xc4, 0xaa, 0xcb, 0x8d, 0x71, 0xc4, 0x6b, 0xc7, 0x5d, - 0x6f, 0x7c, 0x42, 0x5e, 0xb5, 0xf7, 0x80, 0x62, 0xf8, 0x87, 0x4b, 0xcf, 0xa3, 0xa4, 0x5f, 0x96, - 0x8e, 0x89, 0xd8, 0x7d, 0x88, 0xd9, 0xf1, 0xcb, 0xac, 0x65, 0xb5, 0x19, 0xdd, 0x25, 0xa1, 0x55, - 0xf6, 0x48, 0x78, 0x8f, 0xba, 0xf4, 0x90, 0x51, 0xca, 0x61, 0x93, 0x6a, 0x93, 0x26, 0x91, 0xa9, - 0x75, 0xa8, 0xca, 0xe7, 0xe1, 0x6a, 0x13, 0x36, 0xf7, 0xad, 0xd0, 0x40, 0x9e, 0x35, 0x18, 0x1b, - 0x2e, 0xd2, 0xf3, 0xf5, 0x8b, 0x1f, 0xe9, 0xf5, 0xbb, 0x48, 0xa7, 0x71, 0x22, 0xf5, 0xe7, 0x39, - 0x50, 0x16, 0x71, 0x5f, 0x77, 0xaf, 0xe6, 0x36, 0x54, 0x3c, 0x5a, 0x17, 0xa9, 0x5f, 0xf4, 0xe1, - 0x20, 0x29, 0x60, 0x35, 0xf5, 0xfc, 0x7c, 0xc9, 0x0e, 0xf6, 0xf8, 0x03, 0xf4, 0xd7, 0xf8, 0xdb, - 0x1e, 0x8e, 0x37, 0xa6, 0x65, 0x5d, 0xa5, 0xa7, 0x3c, 0xba, 0xde, 0x98, 0xae, 0xeb, 0x08, 0x83, - 0x9b, 0x07, 0x69, 0x55, 0xb5, 0x92, 0xb0, 0xb2, 0xe9, 0xd0, 0x40, 0x84, 0xb1, 0x86, 0x81, 0xb8, - 0x00, 0x55, 0xe2, 0x80, 0x61, 0x10, 0x3d, 0xca, 0x3b, 0x16, 0x3f, 0x3f, 0x93, 0xa5, 0x47, 0x79, - 0x5b, 0x2e, 0xdd, 0xd6, 0xa1, 0x5f, 0x4b, 0x1a, 0x8b, 0x5f, 0xb3, 0x12, 0x4f, 0x1e, 0x23, 0xea, - 0x35, 0xfe, 0x03, 0x3d, 0xbe, 0x15, 0x04, 0xfc, 0x15, 0xaf, 0xb2, 0x78, 0xc8, 0x48, 0x00, 0xe3, - 0xe7, 0xc2, 0xc4, 0xcf, 0x23, 0x21, 0x09, 0x88, 0xe7, 0xc2, 0xf8, 0x8f, 0x23, 0x21, 0xc1, 0x75, - 0x28, 0x7d, 0xe5, 0xb9, 0x16, 0x19, 0xee, 0x15, 0x6a, 0x55, 0x11, 0xd3, 0xfb, 0xc6, 0x4c, 0xfd, - 0xb3, 0x0c, 0x5c, 0x5e, 0x1c, 0x55, 0x5a, 0x30, 0x55, 0x28, 0xb5, 0xfa, 0x5d, 0xbd, 0xd7, 0xdc, - 0x6f, 0x2b, 0x6b, 0x6c, 0x03, 0x2a, 0xfd, 0xed, 0x1f, 0xb7, 0x5b, 0x43, 0x0e, 0xc8, 0xd0, 0x3d, - 0xd1, 0x81, 0xbe, 0xd7, 0xd9, 0xd9, 0x69, 0xf7, 0xb8, 0x95, 0xd2, 0xdf, 0xfe, 0xb1, 0xde, 0xed, - 0xb7, 0xf8, 0xaf, 0xa9, 0x44, 0xa7, 0xef, 0x03, 0x25, 0x47, 0x27, 0xde, 0x14, 0x13, 0x8a, 0xc9, - 0x3c, 0x0f, 0x79, 0x7c, 0x3e, 0xd0, 0x5b, 0xbd, 0xa1, 0x52, 0xc0, 0x54, 0xef, 0xb0, 0xdb, 0xa5, - 0x14, 0xc5, 0x36, 0xb5, 0xfa, 0xfb, 0x07, 0x5a, 0x7b, 0x30, 0xd0, 0x07, 0x9d, 0x9f, 0xb6, 0x95, - 0x12, 0xd5, 0xac, 0x75, 0x9e, 0x74, 0x7a, 0x1c, 0x50, 0x66, 0x45, 0xc8, 0xee, 0x77, 0x7a, 0xfc, - 0x7e, 0xec, 0x7e, 0xf3, 0x33, 0xa5, 0x82, 0x1f, 0x83, 0xc3, 0x7d, 0xa5, 0x7a, 0xff, 0x0e, 0x54, - 0xe5, 0x9f, 0x24, 0xa3, 0x28, 0x47, 0xcf, 0xb5, 0xf8, 0xd3, 0xbd, 0xdd, 0xaf, 0x3e, 0x54, 0x32, - 0xf7, 0x7f, 0x53, 0xfa, 0xa9, 0x07, 0xa2, 0x11, 0x87, 0x01, 0x74, 0x1b, 0x90, 0xdf, 0x36, 0x24, - 0xd7, 0x3f, 0x5d, 0x4e, 0xdc, 0x6b, 0x0e, 0xf6, 0xf8, 0x31, 0x81, 0xc0, 0x10, 0x20, 0x9b, 0x3c, - 0xf9, 0x4a, 0x97, 0x7d, 0xe9, 0x33, 0x3e, 0x6c, 0xcf, 0xd3, 0x3d, 0xcc, 0xce, 0x00, 0x3b, 0xa7, - 0x40, 0x15, 0xbf, 0x62, 0x5c, 0xf1, 0xbe, 0x0a, 0x15, 0xe9, 0x4d, 0x6e, 0xaa, 0xc3, 0x08, 0x8e, - 0xc5, 0x2b, 0xb2, 0x68, 0x6e, 0x2a, 0x99, 0xfb, 0x6f, 0xa0, 0xc4, 0x90, 0x5f, 0xc4, 0x06, 0x28, - 0xf4, 0x3c, 0x7f, 0x6a, 0x38, 0x82, 0xce, 0x9a, 0x07, 0x48, 0xf7, 0x1e, 0x5c, 0x59, 0xf9, 0xbe, - 0x37, 0x45, 0xea, 0xda, 0xd3, 0x99, 0x63, 0xf1, 0x60, 0xd3, 0xbd, 0xf3, 0x91, 0x6f, 0x9b, 0x4a, - 0xe6, 0xfe, 0xe3, 0xe8, 0x4a, 0x5a, 0x54, 0x77, 0xb7, 0xdf, 0xdc, 0xe1, 0x93, 0x1b, 0x5f, 0x46, - 0x1e, 0x6e, 0xf3, 0x17, 0x62, 0xb5, 0xf6, 0xe0, 0xb0, 0x3b, 0x14, 0x17, 0x9f, 0xef, 0xff, 0x08, - 0x1a, 0x17, 0x45, 0x5d, 0x62, 0x8b, 0x5a, 0x7b, 0x4d, 0x8a, 0x6c, 0xc5, 0xc9, 0xec, 0xeb, 0x3c, - 0x95, 0xe1, 0x81, 0xc1, 0xdd, 0x36, 0x45, 0x64, 0xdc, 0xff, 0x59, 0x46, 0x62, 0x61, 0x51, 0xe4, - 0x5c, 0x0c, 0x10, 0xb3, 0x24, 0x83, 0x34, 0xcb, 0x30, 0x95, 0x0c, 0xbb, 0x0a, 0x2c, 0x05, 0xea, - 0x7a, 0x63, 0xc3, 0x51, 0xd6, 0x29, 0xf6, 0x22, 0x82, 0x53, 0x7c, 0xb3, 0x92, 0x65, 0xaf, 0xc2, - 0xf5, 0x18, 0xd6, 0xf5, 0x4e, 0x0f, 0x7c, 0x1b, 0x6d, 0xed, 0x73, 0x8e, 0xce, 0x6d, 0xff, 0xf0, - 0x4f, 0x7f, 0x71, 0x2b, 0xf3, 0xef, 0x7e, 0x71, 0x2b, 0xf3, 0x5f, 0x7f, 0x71, 0x6b, 0xed, 0xe7, - 0xff, 0xed, 0x56, 0xe6, 0xa7, 0xf2, 0x0f, 0x97, 0x4f, 0x8d, 0xd0, 0xb7, 0xcf, 0xf8, 0xa6, 0x89, - 0x12, 0xae, 0xf5, 0xde, 0xec, 0xe4, 0xe8, 0xbd, 0xd9, 0xe8, 0x3d, 0xe4, 0x4c, 0xa3, 0x02, 0xfd, - 0x44, 0xf9, 0xa3, 0xff, 0x1d, 0x00, 0x00, 0xff, 0xff, 0x34, 0xe8, 0x0c, 0xfd, 0x02, 0x7d, 0x00, - 0x00, + 0xb2, 0x82, 0x46, 0x65, 0x2b, 0x7b, 0x2f, 0xa3, 0x89, 0x94, 0xfa, 0xbb, 0xeb, 0x90, 0xe7, 0x33, + 0xf9, 0x0a, 0x94, 0x47, 0x8e, 0x37, 0x3e, 0xd1, 0xdd, 0xf9, 0x34, 0x3a, 0x44, 0x20, 0x00, 0xea, + 0x5b, 0x64, 0xfb, 0x08, 0x8f, 0x5f, 0x46, 0xa3, 0x6f, 0x2c, 0xd2, 0x9b, 0x87, 0x58, 0x57, 0x96, + 0xa0, 0x22, 0x85, 0x8d, 0xf0, 0xbd, 0x53, 0x5a, 0x0d, 0x39, 0x42, 0x44, 0x49, 0x3a, 0xa7, 0x20, + 0xb9, 0x83, 0x99, 0xf2, 0x84, 0x2b, 0x11, 0xa0, 0xe5, 0x86, 0x8b, 0xde, 0xc9, 0xc2, 0x92, 0x77, + 0x92, 0xdd, 0x02, 0xb4, 0xac, 0xc6, 0x56, 0xdf, 0xb5, 0x5a, 0x3d, 0x1a, 0xe1, 0x92, 0x26, 0x41, + 0xd8, 0xc7, 0xf1, 0x5a, 0xa4, 0x1e, 0x09, 0xdf, 0xb1, 0xe0, 0xa8, 0xf2, 0xaa, 0xd5, 0x52, 0x74, + 0xb8, 0x77, 0x90, 0x4d, 0x72, 0x2d, 0x0e, 0x3f, 0xd5, 0x36, 0x80, 0xe6, 0x9d, 0x06, 0x56, 0x48, + 0x5a, 0xd8, 0x35, 0xea, 0x50, 0xea, 0xc0, 0xd0, 0x3b, 0x3d, 0xf0, 0x82, 0x58, 0x3d, 0x5b, 0x5f, + 0xad, 0x9e, 0xa9, 0xef, 0x41, 0x11, 0xe5, 0xae, 0x11, 0x1a, 0xec, 0xae, 0xf0, 0x73, 0x72, 0xbd, + 0x4b, 0x38, 0x7c, 0x93, 0x3a, 0x84, 0xe7, 0xb3, 0x1b, 0xd5, 0x4b, 0x79, 0xee, 0x48, 0xae, 0x8f, + 0x98, 0x7f, 0x8b, 0x02, 0x85, 0x24, 0x7f, 0x05, 0xca, 0xd8, 0x34, 0x3a, 0x69, 0x11, 0x1b, 0xbd, + 0xe4, 0x7b, 0xa7, 0x2d, 0x4c, 0xab, 0xff, 0x39, 0x03, 0x95, 0xbe, 0x6f, 0xa2, 0xe0, 0x18, 0xcc, + 0xac, 0xf1, 0x4b, 0xb5, 0x49, 0x94, 0xfb, 0x9e, 0xe3, 0x18, 0xb1, 0x2e, 0x86, 0x72, 0x3f, 0x02, + 0xb0, 0x0f, 0x20, 0x37, 0x71, 0x8c, 0x23, 0x9a, 0xec, 0xd8, 0xca, 0x94, 0x8a, 0x8f, 0xbe, 0x77, + 0x1d, 0xe3, 0x48, 0x23, 0x52, 0xf5, 0x37, 0xe2, 0xfa, 0xe9, 0xcc, 0x45, 0x3e, 0x69, 0x59, 0xa3, + 0x53, 0xbf, 0x41, 0x4b, 0xc9, 0xb0, 0x12, 0xe4, 0x76, 0xda, 0x83, 0x16, 0xb7, 0x2d, 0xd1, 0xca, + 0x1c, 0xe8, 0xbb, 0x1d, 0x6d, 0x30, 0x54, 0x72, 0x74, 0x8c, 0x48, 0x80, 0x6e, 0x73, 0x30, 0x54, + 0x4a, 0x0c, 0xa0, 0x70, 0xd8, 0xeb, 0xfc, 0xe4, 0xb0, 0xad, 0x28, 0xea, 0xbf, 0xcf, 0x00, 0x24, + 0x07, 0x02, 0xec, 0x6d, 0xa8, 0x9c, 0x52, 0x4a, 0x97, 0x4e, 0x8a, 0xe4, 0x3e, 0x02, 0x47, 0x93, + 0x4e, 0xf2, 0xae, 0x64, 0x62, 0xa0, 0xec, 0x5d, 0x3e, 0x32, 0xaa, 0xcc, 0x12, 0xb1, 0xcd, 0xde, + 0x81, 0x92, 0x87, 0xfd, 0x40, 0xd2, 0xac, 0x2c, 0x78, 0xa5, 0xee, 0x6b, 0x45, 0x8f, 0x27, 0x50, + 0x46, 0x4f, 0xfc, 0xc8, 0x95, 0x14, 0x93, 0xee, 0x22, 0xa8, 0xe5, 0x18, 0xf3, 0xc0, 0xd2, 0x38, + 0x3e, 0x66, 0xbb, 0xf9, 0x84, 0xed, 0xaa, 0x3f, 0x85, 0xfa, 0xc0, 0x98, 0xce, 0x38, 0x73, 0xa6, + 0x8e, 0x31, 0xc8, 0xe1, 0x9a, 0x10, 0x4b, 0x8f, 0xbe, 0x71, 0x8b, 0x1d, 0x58, 0xfe, 0xd8, 0x72, + 0xa3, 0x1d, 0x19, 0x25, 0x91, 0xd9, 0x1e, 0x06, 0xb6, 0x7b, 0xa4, 0x79, 0xa7, 0x51, 0x1c, 0x4f, + 0x94, 0x56, 0xff, 0x71, 0x06, 0x2a, 0x52, 0x33, 0xd8, 0x7b, 0x29, 0x8b, 0xf2, 0x95, 0xa5, 0x76, + 0xf2, 0x6f, 0xc9, 0xb2, 0x7c, 0x03, 0xf2, 0x41, 0x68, 0xf8, 0xd1, 0xd9, 0x92, 0x22, 0xe5, 0xd8, + 0xf6, 0xe6, 0xae, 0xa9, 0x71, 0x34, 0x53, 0x21, 0x6b, 0xb9, 0xa6, 0x30, 0x1a, 0x97, 0xa9, 0x10, + 0xa9, 0x6e, 0x41, 0x39, 0x2e, 0x1e, 0x97, 0x80, 0xd6, 0x7f, 0x3e, 0x50, 0xd6, 0x58, 0x19, 0xf2, + 0x5a, 0xb3, 0xf7, 0xa4, 0xad, 0x64, 0xd4, 0x3f, 0xc9, 0x00, 0x24, 0xb9, 0xd8, 0x83, 0x54, 0x6b, + 0x6f, 0x2c, 0x96, 0xfa, 0x80, 0xfe, 0x4a, 0x8d, 0xbd, 0x09, 0xe5, 0xb9, 0x4b, 0x40, 0xcb, 0x14, + 0x72, 0x27, 0x01, 0xb0, 0x9b, 0x90, 0x8d, 0x22, 0x7e, 0x16, 0xa2, 0x2c, 0x5e, 0x18, 0x8e, 0xfa, + 0x3d, 0x28, 0xc7, 0xc5, 0xb1, 0x1a, 0x94, 0x77, 0xfb, 0xdd, 0x6e, 0xff, 0x79, 0xa7, 0xf7, 0x44, + 0x59, 0xc3, 0xe4, 0x81, 0xd6, 0x6e, 0xb5, 0x77, 0x30, 0x99, 0xc1, 0x35, 0xdb, 0x3a, 0xd4, 0xb4, + 0x76, 0x6f, 0xa8, 0x6b, 0xfd, 0xe7, 0xca, 0xba, 0xfa, 0x5b, 0x39, 0xd8, 0xec, 0xbb, 0x3b, 0xf3, + 0x99, 0x63, 0x8f, 0x8d, 0xd0, 0x7a, 0x6a, 0x9d, 0xb7, 0xc2, 0x33, 0x14, 0xa7, 0x46, 0x18, 0xfa, + 0x7c, 0x33, 0x97, 0x35, 0x9e, 0xe0, 0x0e, 0xba, 0xc0, 0xf2, 0x43, 0xf2, 0x3f, 0xca, 0xbb, 0xb8, + 0xce, 0xe1, 0x2d, 0xcf, 0xa1, 0xbd, 0xcc, 0x7e, 0x00, 0x57, 0xb8, 0x53, 0x8f, 0x53, 0xa2, 0xd2, + 0xc9, 0x6d, 0xfb, 0xec, 0xd2, 0xd2, 0x65, 0x9c, 0x10, 0xb3, 0x22, 0x19, 0xb1, 0xb0, 0xdb, 0x50, + 0x49, 0xb2, 0x73, 0xd3, 0xa0, 0xac, 0x41, 0x4c, 0x48, 0x2d, 0xf1, 0x5c, 0xdd, 0x8c, 0x5a, 0xad, + 0xdb, 0xe6, 0x19, 0x99, 0x4b, 0x79, 0xad, 0xee, 0x25, 0x9d, 0x41, 0x91, 0xfb, 0x19, 0x6c, 0xa6, + 0x28, 0xa9, 0x15, 0xdc, 0x60, 0x7a, 0x27, 0x3a, 0x2c, 0x58, 0xe8, 0xbd, 0x0c, 0xc1, 0xe6, 0x70, + 0x8d, 0x70, 0xc3, 0x4b, 0x43, 0x91, 0x99, 0xd9, 0x81, 0x6e, 0x1f, 0xb9, 0x9e, 0x6f, 0x09, 0xf6, + 0x5e, 0xb2, 0x83, 0x0e, 0xa5, 0x13, 0x9b, 0x45, 0x3a, 0x62, 0xe7, 0xd2, 0x24, 0x3a, 0x61, 0xe6, + 0x68, 0x9b, 0xcb, 0xcb, 0x9c, 0x56, 0xa4, 0x74, 0xc7, 0x44, 0x73, 0x9d, 0xa3, 0x22, 0x33, 0x04, + 0xc8, 0x0c, 0xa9, 0x12, 0xf0, 0x19, 0x87, 0xdd, 0xe8, 0xc1, 0xe5, 0x55, 0x8d, 0x5c, 0xa1, 0x57, + 0x6d, 0xc9, 0x7a, 0xd5, 0x82, 0x03, 0x2b, 0xd1, 0xb1, 0xfe, 0x49, 0x06, 0xaa, 0x3b, 0x96, 0x39, + 0x9f, 0xfd, 0xd8, 0xb3, 0x5d, 0x5c, 0x00, 0x1f, 0x42, 0xd5, 0x73, 0x4c, 0x9a, 0x3d, 0x29, 0x52, + 0x24, 0x75, 0x7a, 0x2a, 0x0e, 0x7a, 0xc0, 0x73, 0xcc, 0x96, 0xe7, 0x50, 0x5c, 0xc9, 0xbb, 0x70, + 0x89, 0x3b, 0xf7, 0x84, 0xaf, 0xfb, 0x8c, 0x67, 0x5e, 0xa7, 0x99, 0x51, 0x38, 0x8a, 0xab, 0x42, + 0x44, 0xfe, 0x6b, 0x70, 0x59, 0x22, 0x27, 0xd7, 0x00, 0xd1, 0x2f, 0x2f, 0x92, 0xcd, 0x38, 0x6f, + 0x74, 0x7c, 0xa9, 0xfe, 0xbd, 0x2c, 0x94, 0xb9, 0x6b, 0x10, 0xdb, 0x7b, 0x0f, 0x8a, 0xde, 0xe8, + 0x0b, 0xdd, 0xb7, 0x26, 0x17, 0x9d, 0xba, 0x17, 0xbc, 0xd1, 0x17, 0x9a, 0x35, 0x61, 0x6f, 0x47, + 0x52, 0xdd, 0xb4, 0x26, 0x62, 0x50, 0xea, 0x69, 0x7b, 0x40, 0x48, 0x79, 0xee, 0x08, 0xbb, 0xb4, + 0x68, 0x3d, 0xdb, 0x26, 0x77, 0x67, 0xe7, 0xb4, 0xcd, 0xb4, 0xf1, 0xdc, 0x31, 0x83, 0x8b, 0xdd, + 0x28, 0xb9, 0x0b, 0xdd, 0x28, 0xec, 0x3e, 0x6c, 0xe2, 0x50, 0x27, 0xf9, 0xf8, 0x62, 0xc6, 0x6d, + 0xb5, 0xe1, 0x39, 0x66, 0xe2, 0xae, 0x30, 0xcf, 0x90, 0xd6, 0xb5, 0x4e, 0x17, 0x68, 0x0b, 0x9c, + 0xd6, 0xb5, 0x4e, 0x53, 0xb4, 0x8f, 0xa0, 0x92, 0xec, 0xd6, 0xa0, 0x51, 0xbc, 0x78, 0x06, 0xe3, + 0xcd, 0x1b, 0x60, 0x26, 0xee, 0xda, 0xe5, 0x99, 0x4a, 0x17, 0x67, 0xe2, 0x64, 0x74, 0xfc, 0xf8, + 0xcf, 0xd7, 0xa1, 0xdc, 0xe1, 0x65, 0x84, 0x67, 0xec, 0x0e, 0x64, 0xbf, 0x66, 0x1a, 0x10, 0x87, + 0xdd, 0x30, 0x4c, 0x53, 0x37, 0x26, 0x13, 0x6b, 0x1c, 0x5a, 0xa6, 0x8e, 0x1a, 0x97, 0x60, 0x7a, + 0x1b, 0x86, 0x69, 0x36, 0x05, 0x9c, 0x84, 0x07, 0x77, 0x74, 0x45, 0x96, 0x27, 0x3f, 0xda, 0xc9, + 0x46, 0x8e, 0x2e, 0x61, 0x78, 0xf2, 0x83, 0x9d, 0xd4, 0xcc, 0xe6, 0xbe, 0xdb, 0xcc, 0xe6, 0xbf, + 0xf5, 0xcc, 0x16, 0x2e, 0x9e, 0xd9, 0x94, 0xe7, 0x0d, 0x67, 0xaa, 0x48, 0x33, 0x95, 0x08, 0xf3, + 0x8e, 0x79, 0xa6, 0xfe, 0xc3, 0x2c, 0x80, 0x66, 0xcd, 0x1c, 0x63, 0x6c, 0xfd, 0xbf, 0x33, 0x7a, + 0xb7, 0xa5, 0x65, 0xe2, 0x9a, 0x51, 0x68, 0x52, 0xb4, 0x24, 0x48, 0xfc, 0xad, 0x1c, 0xde, 0xc2, + 0xb7, 0x1e, 0xde, 0xe2, 0xb7, 0x18, 0xde, 0xd2, 0xf2, 0xf0, 0xb2, 0x1f, 0xc1, 0xab, 0xbe, 0x75, + 0xea, 0xdb, 0xa1, 0xa5, 0x4f, 0x7c, 0x6f, 0xaa, 0xa7, 0x84, 0x01, 0xf2, 0xca, 0x32, 0x8d, 0xc6, + 0x75, 0x41, 0xb4, 0xeb, 0x7b, 0xd3, 0xb4, 0x40, 0x50, 0xff, 0xaa, 0x04, 0x95, 0xa6, 0x6b, 0x38, + 0xe7, 0x5f, 0x59, 0x14, 0xbe, 0x44, 0x87, 0x3f, 0xb3, 0x79, 0xc8, 0xc7, 0x9d, 0x9f, 0xe7, 0x97, + 0x09, 0x42, 0x23, 0x7e, 0x1b, 0x2a, 0xde, 0x3c, 0x8c, 0xf1, 0xfc, 0x84, 0x1f, 0x38, 0x88, 0x08, + 0xe2, 0xfc, 0xf1, 0xc1, 0x62, 0x94, 0x9f, 0xec, 0xcf, 0x24, 0x7f, 0x6c, 0x93, 0xc4, 0xf9, 0x89, + 0x00, 0x05, 0x84, 0x3d, 0xa5, 0x91, 0x0f, 0xe6, 0x53, 0x8b, 0x8f, 0x7e, 0x96, 0x87, 0x89, 0xb6, + 0x04, 0x0c, 0x4b, 0x99, 0x5a, 0x53, 0xcf, 0x3f, 0xe7, 0xa5, 0x14, 0x78, 0x29, 0x1c, 0x44, 0xa5, + 0xbc, 0x03, 0xec, 0xd4, 0xb0, 0x43, 0x3d, 0x5d, 0x14, 0xb7, 0x03, 0x15, 0xc4, 0x0c, 0xe5, 0xe2, + 0xae, 0x42, 0xc1, 0xb4, 0x83, 0x93, 0x4e, 0x5f, 0xd8, 0x80, 0x22, 0x85, 0x7d, 0x09, 0xc6, 0x06, + 0x2a, 0xa5, 0xa1, 0x15, 0xd0, 0x50, 0x66, 0xb5, 0x32, 0x42, 0xb6, 0x11, 0x80, 0x4a, 0x8d, 0x6b, + 0x85, 0xa7, 0x9e, 0x8f, 0x39, 0xb9, 0x89, 0x97, 0x00, 0x50, 0xf9, 0x43, 0x52, 0xac, 0x88, 0x9c, + 0x6a, 0x59, 0x2d, 0x4e, 0xa3, 0xf1, 0xc4, 0xb9, 0x12, 0x61, 0xab, 0xbc, 0xf9, 0x09, 0x84, 0xdd, + 0x85, 0x3a, 0x35, 0x9f, 0x4c, 0x40, 0xec, 0x03, 0x1d, 0xc2, 0x67, 0xb5, 0x2a, 0x42, 0xc9, 0xbf, + 0x82, 0x54, 0x9f, 0xc2, 0xf5, 0x54, 0xff, 0x74, 0xc3, 0xf7, 0x8d, 0x73, 0x7d, 0x6a, 0x7c, 0xe1, + 0xf9, 0xe4, 0x3f, 0xcb, 0x6a, 0x57, 0xe5, 0x61, 0x6b, 0x22, 0x7a, 0x1f, 0xb1, 0x17, 0x66, 0xb5, + 0x5d, 0xcf, 0x27, 0xe7, 0xda, 0xca, 0xac, 0x88, 0x25, 0xaf, 0x0e, 0x4d, 0x30, 0xd9, 0xa3, 0x01, + 0x0f, 0x2f, 0xd6, 0x2a, 0x04, 0xdb, 0x26, 0x10, 0xda, 0x68, 0xc1, 0x23, 0x2e, 0xec, 0x36, 0x45, + 0x14, 0xe0, 0x23, 0x12, 0x89, 0x1c, 0x71, 0x6c, 0x19, 0x26, 0x1d, 0xec, 0x13, 0x62, 0xcf, 0x32, + 0x28, 0x6c, 0x26, 0x78, 0xa4, 0xcf, 0xe6, 0x21, 0x8f, 0x0b, 0xd6, 0xf2, 0xc1, 0xa3, 0x83, 0x79, + 0x28, 0xc0, 0x47, 0x56, 0x48, 0xd1, 0xc0, 0x04, 0x7e, 0x62, 0x85, 0xa8, 0x9b, 0x04, 0x8f, 0xa2, + 0x43, 0xba, 0x2b, 0x62, 0x6c, 0x1f, 0x89, 0x53, 0x38, 0x15, 0x6a, 0x31, 0x52, 0x9f, 0xce, 0x79, + 0x20, 0x70, 0x56, 0xab, 0x44, 0x04, 0xfb, 0x73, 0x07, 0x27, 0x76, 0x6c, 0x8c, 0x8f, 0x2d, 0xdd, + 0xc7, 0xa6, 0x5c, 0xe3, 0x53, 0x47, 0x10, 0x0d, 0x5b, 0xf3, 0x0a, 0xf0, 0x84, 0x7e, 0x6c, 0x87, + 0xe4, 0xb0, 0xcb, 0x6a, 0x25, 0x02, 0xec, 0xd9, 0x21, 0xf2, 0x27, 0x8e, 0x14, 0x2b, 0x90, 0x8a, + 0xb8, 0x4e, 0x44, 0x1b, 0x84, 0xd8, 0x27, 0x38, 0x15, 0x74, 0x0f, 0x94, 0x14, 0x2d, 0x96, 0x77, + 0x83, 0x48, 0xeb, 0x12, 0x29, 0x96, 0xfa, 0x06, 0xf0, 0xcc, 0x3a, 0x2e, 0x3d, 0x5e, 0xe6, 0x2b, + 0xdc, 0x1f, 0x41, 0xe0, 0x1d, 0x3b, 0x38, 0xa1, 0x12, 0xef, 0x42, 0x5d, 0xa2, 0xc3, 0xf2, 0x6e, + 0xf2, 0x95, 0x11, 0x93, 0xa5, 0xda, 0xe8, 0x5b, 0x53, 0x2f, 0x14, 0xdd, 0x7c, 0x55, 0x6a, 0xa3, + 0x46, 0xf0, 0x74, 0x1b, 0x05, 0x2d, 0x96, 0x79, 0x4b, 0x6a, 0x23, 0x27, 0xc5, 0x52, 0xef, 0x40, + 0x15, 0xb9, 0x48, 0x68, 0xb9, 0x7c, 0xf3, 0xdf, 0xe6, 0x03, 0x2b, 0x60, 0xb4, 0xfb, 0xef, 0x40, + 0x95, 0x8f, 0xbc, 0xe0, 0xdb, 0x5b, 0x9c, 0x44, 0xc0, 0x90, 0x44, 0xf5, 0xa5, 0xc3, 0xb4, 0x03, + 0x7f, 0xee, 0x5a, 0xdc, 0xfd, 0x48, 0x9f, 0xa6, 0x08, 0x6b, 0x88, 0xd3, 0x6c, 0x07, 0x2e, 0x71, + 0xaf, 0x83, 0x25, 0xe9, 0x10, 0x51, 0x58, 0xe1, 0xca, 0x43, 0x26, 0x16, 0xd1, 0xc7, 0xe0, 0x40, + 0xfd, 0x59, 0x06, 0x6e, 0xf4, 0x29, 0xc6, 0x82, 0x18, 0xec, 0xbe, 0x15, 0x04, 0xc6, 0x91, 0xb5, + 0xeb, 0xf9, 0xbb, 0xf3, 0xaf, 0xbe, 0x3a, 0x67, 0xf7, 0x60, 0xe3, 0xc0, 0xf0, 0x2d, 0x37, 0x8c, + 0xd9, 0xaf, 0xd0, 0x31, 0x17, 0xc1, 0xec, 0x31, 0x1d, 0xe4, 0x58, 0x6e, 0x78, 0x18, 0x6b, 0xeb, + 0xa2, 0x2d, 0x69, 0xd7, 0xfe, 0x12, 0x95, 0xfa, 0xbf, 0xb6, 0x20, 0xd7, 0xf3, 0x4c, 0x8b, 0xbd, + 0x0f, 0x65, 0x8a, 0x09, 0x5e, 0x3e, 0x3f, 0x44, 0x34, 0xfd, 0x21, 0xc3, 0xa9, 0xe4, 0x8a, 0xaf, + 0x8b, 0xa3, 0x88, 0xef, 0x90, 0x09, 0x48, 0x01, 0x08, 0x28, 0xd0, 0x2a, 0xc2, 0x29, 0x45, 0x5e, + 0x15, 0x8e, 0xc1, 0xb1, 0x25, 0xa7, 0xba, 0x6f, 0xb9, 0xa4, 0xa5, 0xe5, 0xb5, 0x38, 0x4d, 0x86, + 0xb7, 0xef, 0xa1, 0xf0, 0xe5, 0x7b, 0x35, 0xbf, 0xc2, 0xf0, 0xe6, 0x78, 0xda, 0xbc, 0xef, 0x43, + 0xf9, 0x0b, 0xcf, 0x76, 0x79, 0xc3, 0x0b, 0x4b, 0x0d, 0x47, 0xdd, 0x9a, 0x37, 0xfc, 0x0b, 0xf1, + 0xc5, 0x5e, 0x83, 0xa2, 0xe7, 0xf2, 0xb2, 0x8b, 0x4b, 0x65, 0x17, 0x3c, 0xb7, 0xcb, 0x03, 0xf4, + 0x6a, 0xa3, 0xb9, 0xed, 0x98, 0x28, 0xbb, 0x1c, 0x6b, 0x12, 0x8a, 0x73, 0xbe, 0x0a, 0x01, 0xfb, + 0x6e, 0xd7, 0x9a, 0x84, 0xec, 0x6d, 0xa8, 0x4c, 0x6c, 0x07, 0x65, 0x3c, 0x15, 0x56, 0x5e, 0x2a, + 0x0c, 0x38, 0x9a, 0x0a, 0x7c, 0x1d, 0x4a, 0x47, 0xbe, 0x37, 0x9f, 0xe9, 0xa3, 0x73, 0x3a, 0xdf, + 0x5b, 0x38, 0x59, 0x23, 0xdc, 0xf6, 0x39, 0x0a, 0x1a, 0xfa, 0xb4, 0xdd, 0x23, 0x9d, 0x7c, 0x29, + 0x95, 0xad, 0xec, 0xbd, 0x92, 0x56, 0x8d, 0x80, 0xe4, 0x25, 0x79, 0x1d, 0x4a, 0xc6, 0xd1, 0x91, + 0x2e, 0xe2, 0x0c, 0x97, 0xca, 0x32, 0x8e, 0x8e, 0xa8, 0xca, 0x07, 0x50, 0x3b, 0xb5, 0x5d, 0x3d, + 0x98, 0x59, 0x63, 0x4e, 0x5b, 0x5b, 0x1e, 0xca, 0x53, 0xdb, 0x1d, 0xcc, 0xac, 0x31, 0xd1, 0xcb, + 0x3e, 0x8c, 0xfa, 0x4b, 0x7d, 0x18, 0x5b, 0x90, 0x77, 0xec, 0xa9, 0x1d, 0x8a, 0xc8, 0xc3, 0x94, + 0x91, 0x43, 0x08, 0xa6, 0x42, 0x41, 0x38, 0xcf, 0x95, 0x25, 0x12, 0x81, 0x49, 0x6b, 0x40, 0x9b, + 0x2f, 0xd1, 0x80, 0x24, 0x83, 0x83, 0x7d, 0xbd, 0xc1, 0xf1, 0x11, 0x9d, 0x30, 0x5a, 0x6e, 0xa8, + 0x47, 0x19, 0x2e, 0xad, 0xce, 0x50, 0xe5, 0x64, 0x7d, 0x9e, 0xed, 0x03, 0xa8, 0xf8, 0xe4, 0x5c, + 0xd3, 0xc9, 0x13, 0x77, 0x59, 0xf6, 0x4e, 0x24, 0x5e, 0x37, 0x0d, 0xfc, 0xc4, 0x03, 0xf7, 0x1a, + 0xd4, 0x78, 0x00, 0x14, 0x0f, 0x53, 0x09, 0x88, 0xb1, 0x97, 0xb5, 0x2a, 0x01, 0x79, 0x08, 0x4b, + 0xc0, 0x1e, 0x00, 0x44, 0xaa, 0x5b, 0x78, 0x46, 0x9c, 0x3d, 0x6e, 0x0a, 0x67, 0xff, 0xad, 0xf0, + 0x4c, 0x2b, 0x9b, 0xd1, 0x27, 0x32, 0xac, 0x91, 0xed, 0x9a, 0xb8, 0x08, 0x42, 0xe3, 0x28, 0x68, + 0x34, 0x68, 0x8f, 0x54, 0x04, 0x6c, 0x68, 0x1c, 0x05, 0x68, 0x2c, 0x1a, 0x5c, 0x41, 0xe2, 0x91, + 0xe0, 0xd7, 0x65, 0x4f, 0x92, 0xa4, 0x3a, 0x69, 0x15, 0x43, 0xd2, 0xa3, 0x3e, 0x01, 0x16, 0x9d, + 0xcc, 0x49, 0xb6, 0xdf, 0x8d, 0xa5, 0x75, 0xb1, 0x21, 0x8e, 0xe6, 0xe2, 0xdb, 0x0b, 0x9f, 0x40, + 0x2d, 0xad, 0xd0, 0xde, 0x5c, 0x71, 0x16, 0x45, 0x53, 0xa6, 0x55, 0xc7, 0xb2, 0x8a, 0xfb, 0x1a, + 0xd4, 0x5c, 0x2f, 0xd4, 0x89, 0x69, 0x53, 0x46, 0x7e, 0xde, 0x52, 0x75, 0xbd, 0xb0, 0x15, 0xc1, + 0x70, 0x7c, 0x22, 0xb3, 0x29, 0x3c, 0x23, 0x3e, 0x1f, 0x8f, 0x4f, 0x6c, 0xe3, 0xa0, 0xbe, 0x16, + 0x99, 0x3b, 0x38, 0x4f, 0x5c, 0x7d, 0xa7, 0x0c, 0xb7, 0x53, 0xf3, 0x14, 0xeb, 0xf5, 0x1a, 0xf8, + 0x89, 0x8e, 0x7f, 0x1b, 0x2a, 0x81, 0x37, 0xf7, 0xc7, 0x96, 0x1e, 0x84, 0xd6, 0xac, 0xb1, 0x45, + 0x23, 0x0a, 0x1c, 0x34, 0x08, 0xad, 0x19, 0x7b, 0x0c, 0xf5, 0x99, 0x6f, 0xe9, 0xd2, 0x3c, 0xdd, + 0x91, 0xbb, 0x78, 0xe0, 0x5b, 0xc9, 0x54, 0x55, 0x67, 0x52, 0x2a, 0xca, 0x29, 0xf5, 0x40, 0x5d, + 0xc8, 0x99, 0x74, 0x02, 0x73, 0x26, 0x66, 0xdb, 0x0f, 0x61, 0x53, 0xca, 0x39, 0x3f, 0xa1, 0xcc, + 0xaf, 0xa5, 0x8e, 0x06, 0x23, 0xf2, 0xc3, 0x13, 0xcc, 0x5e, 0x9f, 0xa5, 0xd2, 0xac, 0xb9, 0xe0, + 0x93, 0x41, 0xe5, 0xfa, 0x2e, 0xe5, 0xbf, 0x76, 0x81, 0xa3, 0x25, 0xe5, 0xac, 0x79, 0xca, 0x0f, + 0x81, 0x3a, 0x41, 0xdb, 0x35, 0x1b, 0xaf, 0xf3, 0x2b, 0x46, 0x94, 0x60, 0x8f, 0xa0, 0xca, 0xd5, + 0x3c, 0x0a, 0x6f, 0x0e, 0x1a, 0x6f, 0xc8, 0x4e, 0x69, 0xd2, 0xf5, 0x08, 0xa1, 0x55, 0x9c, 0xf8, + 0x3b, 0x60, 0x1f, 0xc3, 0x26, 0x3f, 0x1f, 0x90, 0xd9, 0xe2, 0x9b, 0xcb, 0x8b, 0x8b, 0x88, 0x76, + 0x13, 0xde, 0xa8, 0xc1, 0x75, 0x7f, 0xee, 0x92, 0xea, 0x27, 0x72, 0xce, 0x7c, 0x6f, 0x64, 0xf1, + 0xfc, 0xf7, 0x28, 0xbf, 0xe8, 0x8e, 0xc6, 0xc9, 0x78, 0x5e, 0xe2, 0x47, 0x57, 0x7d, 0x19, 0x74, + 0x80, 0xf9, 0x2e, 0x28, 0x93, 0xf3, 0x73, 0x2a, 0xf3, 0xad, 0x6f, 0x53, 0xe6, 0x36, 0xe6, 0xa3, + 0x32, 0x19, 0xe4, 0xe6, 0x73, 0xdb, 0x6c, 0xdc, 0xe7, 0x91, 0xc8, 0xf8, 0xcd, 0x5e, 0x87, 0xba, + 0x6f, 0x8d, 0xe7, 0x7e, 0x60, 0xbf, 0xb0, 0xf4, 0xc0, 0x76, 0x4f, 0x1a, 0x6f, 0xd3, 0x38, 0xd6, + 0x62, 0xe8, 0xc0, 0x76, 0x4f, 0x70, 0xc5, 0x5a, 0x67, 0xa1, 0xe5, 0xbb, 0x3a, 0xaa, 0xdb, 0x8d, + 0x77, 0xe4, 0x15, 0xdb, 0x26, 0xc4, 0x60, 0x6c, 0xb8, 0x1a, 0x58, 0xf1, 0x37, 0xfb, 0x01, 0x6c, + 0x24, 0xa6, 0xd6, 0x0c, 0x15, 0x8f, 0xc6, 0xbb, 0x2b, 0x4f, 0x8d, 0x49, 0x29, 0xd1, 0x92, 0x90, + 0x0a, 0xae, 0xbf, 0xa4, 0xd7, 0x56, 0xc0, 0xd7, 0xd6, 0x83, 0x6f, 0xb4, 0xb6, 0x06, 0xb4, 0xb6, + 0xde, 0x80, 0x92, 0xed, 0x86, 0x96, 0xff, 0xc2, 0x70, 0x1a, 0xef, 0x2d, 0x31, 0xf0, 0x18, 0xc7, + 0xee, 0x42, 0x31, 0x70, 0x6c, 0x64, 0x4c, 0x8d, 0xf7, 0x97, 0xc8, 0x22, 0x14, 0xbb, 0x07, 0xe5, + 0xf8, 0x4e, 0x5d, 0xe3, 0x83, 0x25, 0xba, 0x04, 0xc9, 0x6e, 0x41, 0xee, 0x14, 0xd7, 0xe3, 0xc3, + 0xe5, 0x33, 0x08, 0x84, 0xa3, 0xc4, 0x9f, 0xd8, 0x8e, 0xc3, 0x25, 0xfe, 0xa3, 0x25, 0x89, 0xbf, + 0x6b, 0x3b, 0x0e, 0x97, 0xf8, 0x13, 0xf1, 0x85, 0xf2, 0x92, 0x72, 0x60, 0x4f, 0x3e, 0x5c, 0x96, + 0x97, 0x88, 0x7b, 0x46, 0xb7, 0x0f, 0x2b, 0x01, 0x39, 0xd6, 0xf9, 0xf9, 0xc0, 0x47, 0xf2, 0x58, + 0xa5, 0x3d, 0xee, 0x1a, 0x04, 0x71, 0x1a, 0xd5, 0x76, 0x71, 0xac, 0x80, 0xf6, 0xf0, 0xc7, 0xfc, + 0x52, 0x0c, 0x87, 0xa0, 0x31, 0xfc, 0x3e, 0xd4, 0xa2, 0x78, 0x3a, 0xac, 0x2e, 0x68, 0x7c, 0xb2, + 0xd4, 0x82, 0x34, 0x01, 0xdb, 0x81, 0xea, 0x04, 0x35, 0xc0, 0x29, 0x57, 0x08, 0x1b, 0x8f, 0xa9, + 0x21, 0x5b, 0x91, 0x2c, 0xbe, 0x48, 0x61, 0xd4, 0x52, 0xb9, 0xd8, 0x03, 0x60, 0xf6, 0x84, 0xcf, + 0x27, 0x1a, 0xd8, 0x5c, 0xe9, 0x6b, 0x7c, 0x4a, 0x8b, 0x73, 0x05, 0x86, 0x3d, 0x82, 0x5a, 0x60, + 0xb9, 0xa6, 0x3e, 0x0d, 0x84, 0x66, 0xf1, 0x3d, 0x6a, 0xa7, 0x60, 0xc3, 0xf1, 0xdd, 0x5b, 0xad, + 0x82, 0x54, 0xfb, 0x01, 0x57, 0x31, 0x1e, 0x01, 0xae, 0xf3, 0x17, 0x49, 0xa6, 0x5f, 0xbb, 0x20, + 0x13, 0x52, 0x45, 0x99, 0x1e, 0x43, 0xdd, 0xb4, 0xcc, 0xf9, 0x4c, 0x27, 0xc5, 0x0d, 0x97, 0xe5, + 0xf7, 0x65, 0x7e, 0x29, 0xfb, 0x44, 0xb5, 0xaa, 0x29, 0x7b, 0x48, 0x3f, 0x81, 0x8d, 0xc8, 0x79, + 0x19, 0x0a, 0x3f, 0xe7, 0x0f, 0xe4, 0x0a, 0x63, 0xdf, 0xa4, 0x56, 0x9b, 0x47, 0x9f, 0x51, 0x3b, + 0xc9, 0x66, 0x0e, 0x5c, 0x63, 0x16, 0x1c, 0x7b, 0x61, 0xe3, 0xd7, 0x65, 0x55, 0x63, 0x20, 0xa0, + 0x5a, 0x15, 0x89, 0xa2, 0x14, 0x8a, 0xae, 0x64, 0x6b, 0x8f, 0x43, 0xab, 0xf1, 0x43, 0x2e, 0xba, + 0x62, 0x60, 0x2b, 0xc4, 0x61, 0x03, 0x63, 0x36, 0x73, 0xce, 0xf9, 0x72, 0xfc, 0x11, 0x2d, 0xc7, + 0xcb, 0xd2, 0x72, 0x6c, 0x22, 0x92, 0xd6, 0x63, 0xd9, 0x88, 0x3e, 0xd9, 0x43, 0xa8, 0xce, 0xbc, + 0x20, 0xd4, 0xcd, 0xa9, 0x43, 0xfd, 0x6f, 0xca, 0xec, 0xe0, 0xc0, 0x0b, 0xc2, 0x9d, 0xa9, 0x43, + 0x02, 0x6c, 0x16, 0x7f, 0xb3, 0x2e, 0x5c, 0x4a, 0xb1, 0x7a, 0x83, 0x0e, 0xe6, 0x1b, 0xdb, 0x54, + 0xe3, 0x4d, 0xa9, 0x46, 0x89, 0xe5, 0x8b, 0x80, 0xce, 0x4d, 0x6f, 0x11, 0x84, 0x16, 0x1b, 0x9f, + 0x83, 0x38, 0xaa, 0xb9, 0xc5, 0xf5, 0x16, 0x82, 0x46, 0x61, 0xcd, 0x8f, 0x61, 0x23, 0xa1, 0xc2, + 0x0e, 0x06, 0x8d, 0x1d, 0x79, 0xf5, 0x4a, 0x77, 0x0f, 0x6a, 0x51, 0x46, 0x84, 0x05, 0xea, 0x9f, + 0xe7, 0xa1, 0x14, 0x19, 0x0d, 0xac, 0x02, 0xc5, 0xc3, 0xde, 0xd3, 0x5e, 0xff, 0x79, 0x8f, 0xdf, + 0x01, 0x6c, 0x0e, 0x06, 0x6d, 0x6d, 0xa8, 0x98, 0xac, 0x0e, 0x40, 0x77, 0x9c, 0xf4, 0x41, 0xab, + 0xd9, 0xe3, 0x77, 0x02, 0xe9, 0x66, 0x15, 0x4f, 0xaf, 0xb3, 0x4d, 0xa8, 0xed, 0x1e, 0xf6, 0x28, + 0x6e, 0x94, 0x83, 0xb2, 0x08, 0x6a, 0x7f, 0xc6, 0x4f, 0x08, 0x39, 0x28, 0x87, 0xa0, 0xfd, 0xe6, + 0xb0, 0xad, 0x75, 0x22, 0x50, 0x9e, 0x42, 0x50, 0xfb, 0x87, 0x5a, 0x4b, 0x94, 0x54, 0x60, 0x57, + 0x60, 0x33, 0xce, 0x16, 0x15, 0xa9, 0x14, 0xb1, 0x65, 0x07, 0x5a, 0xff, 0xc7, 0xed, 0xd6, 0x50, + 0x01, 0x3a, 0x6e, 0x7c, 0xf2, 0x44, 0xa9, 0xb0, 0x2a, 0x94, 0x76, 0x3a, 0x83, 0x61, 0xa7, 0xd7, + 0x1a, 0x2a, 0x55, 0x6c, 0xf0, 0x6e, 0xa7, 0x3b, 0x6c, 0x6b, 0x4a, 0x8d, 0x95, 0x20, 0xf7, 0xe3, + 0x7e, 0xa7, 0xa7, 0xd4, 0xe9, 0xae, 0x57, 0x73, 0xff, 0xa0, 0xdb, 0x56, 0x36, 0x10, 0x3a, 0xe8, + 0x6b, 0x43, 0x45, 0x41, 0xe8, 0xf3, 0x4e, 0x6f, 0xa7, 0xff, 0x5c, 0xd9, 0x64, 0x65, 0xc8, 0x1f, + 0xf6, 0xb0, 0x1a, 0xc6, 0x6a, 0x50, 0xa6, 0x4f, 0xbd, 0xd9, 0xed, 0x2a, 0x97, 0xa4, 0x33, 0xca, + 0xcb, 0x88, 0xa2, 0x13, 0xcf, 0x01, 0xb6, 0xe1, 0x0a, 0xf6, 0x25, 0x4e, 0x12, 0xf5, 0x55, 0x2c, + 0x67, 0xbf, 0xd3, 0x3b, 0x1c, 0x28, 0xd7, 0x90, 0x98, 0x3e, 0x09, 0xd3, 0xc0, 0x72, 0x3a, 0x3d, + 0x1a, 0xca, 0x5b, 0xf8, 0xbd, 0xd3, 0xee, 0xb6, 0x87, 0x6d, 0xe5, 0x36, 0xf6, 0x4a, 0x6b, 0x1f, + 0x74, 0x9b, 0xad, 0xb6, 0xb2, 0x85, 0x89, 0x6e, 0xbf, 0xf5, 0x54, 0xef, 0x1f, 0x28, 0x77, 0xd8, + 0x65, 0x50, 0xfa, 0x3d, 0x7d, 0xe7, 0xf0, 0xa0, 0xdb, 0x69, 0x35, 0x87, 0x6d, 0xfd, 0x69, 0xfb, + 0x73, 0x45, 0xc5, 0x61, 0x3f, 0xd0, 0xda, 0xba, 0x28, 0xeb, 0xb5, 0x28, 0x2d, 0xca, 0xbb, 0xcb, + 0x14, 0xa8, 0xee, 0x1e, 0xfe, 0xf4, 0xa7, 0x9f, 0xeb, 0x62, 0x1c, 0x5e, 0xc7, 0x66, 0x26, 0x39, + 0xf4, 0xc3, 0xa7, 0xca, 0x1b, 0x0b, 0xa0, 0xc1, 0x53, 0xe5, 0x4d, 0x1c, 0xc7, 0x68, 0x62, 0x94, + 0x7b, 0x48, 0xa0, 0xb5, 0x5b, 0x87, 0xda, 0xa0, 0xf3, 0xac, 0xad, 0xb7, 0x86, 0x6d, 0xe5, 0x2d, + 0x1a, 0xb8, 0x4e, 0xef, 0xa9, 0x72, 0x1f, 0x7b, 0x86, 0x5f, 0x7c, 0xba, 0xde, 0x66, 0x0c, 0xea, + 0x09, 0x2d, 0xc1, 0xde, 0x41, 0x92, 0x6d, 0xad, 0xdf, 0xdc, 0x69, 0x35, 0x07, 0x43, 0xe5, 0x5d, + 0x1c, 0x96, 0xc1, 0x41, 0xb7, 0x33, 0x54, 0x1e, 0x60, 0xdf, 0x9f, 0x34, 0x87, 0x7b, 0x6d, 0x4d, + 0x79, 0x0f, 0x67, 0x7e, 0xd8, 0xd9, 0x6f, 0xeb, 0x62, 0x1a, 0x1e, 0x62, 0x1d, 0xbb, 0x9d, 0x6e, + 0x57, 0x79, 0x44, 0xc7, 0x72, 0x4d, 0x6d, 0xd8, 0xa1, 0xb9, 0xff, 0x10, 0x0b, 0x68, 0x1e, 0x1c, + 0x74, 0x3f, 0x57, 0x3e, 0xc2, 0x0e, 0xee, 0x1f, 0x76, 0x87, 0x1d, 0xfd, 0xf0, 0x60, 0xa7, 0x39, + 0x6c, 0x2b, 0x1f, 0xd3, 0xc2, 0xe8, 0x0f, 0x86, 0x3b, 0xfb, 0x5d, 0xe5, 0x13, 0xf5, 0x37, 0xa1, + 0x14, 0xd9, 0x91, 0x98, 0xab, 0xd3, 0xeb, 0xb5, 0x35, 0x65, 0x0d, 0x4b, 0xee, 0xb6, 0x77, 0x87, + 0x4a, 0x86, 0x8e, 0x24, 0x3b, 0x4f, 0xf6, 0x86, 0xca, 0x3a, 0x7e, 0xf6, 0x0f, 0x71, 0x90, 0xb2, + 0xd4, 0xbb, 0xf6, 0x7e, 0x47, 0xc9, 0xe1, 0x57, 0xb3, 0x37, 0xec, 0x28, 0x79, 0x5a, 0x36, 0x9d, + 0xde, 0x93, 0x6e, 0x5b, 0x29, 0x20, 0x74, 0xbf, 0xa9, 0x3d, 0x55, 0x8a, 0xbc, 0xd0, 0x9d, 0xf6, + 0x67, 0x4a, 0x89, 0x15, 0x60, 0xbd, 0xfb, 0x50, 0x29, 0x23, 0x68, 0xa7, 0xbd, 0x73, 0x78, 0xa0, + 0x80, 0x7a, 0x0f, 0x8a, 0xcd, 0xa3, 0xa3, 0x7d, 0x34, 0xd3, 0xb1, 0x33, 0x87, 0xdd, 0x2e, 0xdf, + 0x46, 0xdb, 0xfd, 0xe1, 0xb0, 0xbf, 0xaf, 0x64, 0x70, 0xe1, 0x0e, 0xfb, 0x07, 0xca, 0xba, 0xda, + 0x81, 0x52, 0x24, 0xfe, 0xa4, 0xeb, 0x8b, 0x25, 0xc8, 0x1d, 0x68, 0xed, 0x67, 0xfc, 0x1c, 0xbd, + 0xd7, 0xfe, 0x0c, 0x9b, 0x89, 0x5f, 0x58, 0x50, 0x16, 0x2b, 0xe2, 0xf7, 0x0c, 0xe9, 0xfe, 0x62, + 0xb7, 0xd3, 0x6b, 0x37, 0x35, 0x25, 0xaf, 0x7e, 0x94, 0x3a, 0xa2, 0x14, 0x5c, 0x03, 0xab, 0x6f, + 0x76, 0x44, 0xf5, 0x9d, 0x27, 0xbd, 0xbe, 0xd6, 0xe6, 0x17, 0x22, 0xc5, 0xb8, 0xad, 0xab, 0x6f, + 0x43, 0x39, 0xe6, 0x78, 0xb8, 0x8e, 0x5a, 0x5a, 0x7f, 0x30, 0xe0, 0xc3, 0xbc, 0x86, 0x69, 0x1a, + 0x1b, 0x9e, 0xce, 0xa8, 0xff, 0x3f, 0x94, 0x62, 0x66, 0x7b, 0x17, 0xd6, 0x87, 0x03, 0xe1, 0x82, + 0xbf, 0xfc, 0x20, 0x79, 0xb7, 0x62, 0x18, 0x7d, 0x69, 0xeb, 0xc3, 0x01, 0x7b, 0x07, 0x0a, 0xfc, + 0xd6, 0xaa, 0x38, 0x45, 0xba, 0x9c, 0x66, 0xe0, 0x43, 0xc2, 0x69, 0x82, 0x46, 0xed, 0x42, 0x3d, + 0x8d, 0x61, 0xb7, 0x00, 0x38, 0x4e, 0x72, 0xa7, 0x48, 0x10, 0x76, 0x03, 0xa2, 0x5b, 0xb1, 0x3b, + 0x22, 0xd4, 0x34, 0x4e, 0xab, 0xff, 0x20, 0x0b, 0x90, 0xa8, 0x6a, 0xa8, 0x0c, 0xc6, 0xce, 0x92, + 0xbc, 0x38, 0x50, 0x7e, 0x05, 0xca, 0x8e, 0x67, 0x98, 0xf2, 0xfb, 0x13, 0x25, 0x04, 0xd0, 0x68, + 0xc8, 0x77, 0xdf, 0xca, 0x3c, 0x9a, 0x83, 0x5d, 0x85, 0xc2, 0xc4, 0xf3, 0xa7, 0x46, 0x14, 0x94, + 0x2a, 0x52, 0x28, 0x7a, 0xf8, 0x21, 0x27, 0x2a, 0xac, 0x2e, 0xdd, 0x2b, 0xa1, 0x08, 0x67, 0x01, + 0xec, 0x22, 0x0c, 0x4d, 0x1a, 0xcb, 0x1d, 0x3b, 0x5e, 0x60, 0x99, 0x68, 0xb2, 0x17, 0x48, 0x2b, + 0x85, 0x08, 0xb4, 0x7d, 0xce, 0x7b, 0xeb, 0x4f, 0x6d, 0xd7, 0x08, 0x85, 0x9f, 0x99, 0x7a, 0x1b, + 0x41, 0xb0, 0xb9, 0x5f, 0x04, 0x9e, 0xf0, 0x9d, 0xf0, 0xf3, 0xd2, 0x12, 0x02, 0xa8, 0xb9, 0xaf, + 0x02, 0x58, 0xc1, 0xd8, 0x98, 0xf1, 0xc2, 0xcb, 0x54, 0x78, 0x59, 0x40, 0xb6, 0xcf, 0x59, 0x17, + 0xea, 0xc3, 0x11, 0xb2, 0x7b, 0x0f, 0xcd, 0xe0, 0x96, 0xe7, 0x08, 0xaf, 0xc6, 0xdd, 0x45, 0x9d, + 0xf6, 0x41, 0x9a, 0x8c, 0x1f, 0xec, 0x2e, 0xe4, 0xbd, 0xd1, 0x84, 0x4b, 0x2b, 0xc8, 0xbe, 0x55, + 0xc8, 0x9a, 0x13, 0xcd, 0x4e, 0x33, 0x0c, 0x29, 0xec, 0x39, 0x96, 0x6c, 0x99, 0x28, 0xde, 0x97, + 0x0b, 0xb5, 0x57, 0x28, 0x86, 0x45, 0x04, 0x27, 0x8a, 0x49, 0x8a, 0x83, 0x0e, 0xdf, 0x80, 0x0d, + 0x44, 0x4e, 0x6c, 0xcb, 0x31, 0x05, 0x09, 0xbf, 0x86, 0x54, 0x1b, 0x7b, 0xce, 0x2e, 0x42, 0x89, + 0x4e, 0xfd, 0xcb, 0x1c, 0x40, 0x62, 0x06, 0xa5, 0xce, 0x96, 0x33, 0xe9, 0xb3, 0xe5, 0x87, 0x70, + 0x55, 0x5c, 0x52, 0x8b, 0x0f, 0x68, 0x6d, 0x57, 0x1f, 0x19, 0xd1, 0x31, 0x3e, 0x13, 0x58, 0x7e, + 0x46, 0xdb, 0x71, 0xb7, 0x0d, 0xd4, 0x90, 0x36, 0xe4, 0x3c, 0xe1, 0xf9, 0x2c, 0x1d, 0x86, 0x20, + 0xcb, 0xdd, 0x24, 0xfb, 0xf0, 0x7c, 0xc6, 0xde, 0x87, 0x2b, 0xbe, 0x35, 0xf1, 0xad, 0xe0, 0x58, + 0x0f, 0x03, 0xb9, 0x32, 0x1e, 0x1b, 0xb7, 0x29, 0x90, 0xc3, 0x20, 0xae, 0xeb, 0x7d, 0xb8, 0x22, + 0x0c, 0xa4, 0x85, 0xe6, 0xf1, 0xe3, 0xd0, 0x4d, 0x8e, 0x94, 0x5b, 0xf7, 0x2a, 0x80, 0xb0, 0x0d, + 0xa3, 0x57, 0x5c, 0x4a, 0x5a, 0x99, 0xdb, 0x81, 0x68, 0xcc, 0xbf, 0x03, 0xcc, 0x0e, 0xf4, 0x85, + 0x93, 0x25, 0x71, 0x58, 0xaf, 0xd8, 0xc1, 0x41, 0xea, 0x54, 0xe9, 0xa2, 0x43, 0xab, 0xd2, 0x45, + 0x87, 0x56, 0x97, 0x21, 0x4f, 0xe6, 0xa3, 0x38, 0x43, 0xe2, 0x09, 0xa6, 0x42, 0x0e, 0xf9, 0x23, + 0x9d, 0x77, 0xd4, 0x1f, 0xd6, 0x1f, 0xd0, 0x1b, 0x38, 0x38, 0x3f, 0x08, 0xd5, 0x08, 0xc7, 0xde, + 0x85, 0x4b, 0xf2, 0xa0, 0x46, 0x0f, 0x3c, 0x54, 0xa8, 0x9b, 0x4a, 0x32, 0x8c, 0x1a, 0x7f, 0xea, + 0xe1, 0x6d, 0x60, 0xd2, 0xb8, 0x44, 0xd4, 0x55, 0x7e, 0xee, 0x1b, 0x0f, 0x8a, 0x20, 0x7e, 0x13, + 0x68, 0x00, 0xb8, 0x7b, 0xb9, 0xb6, 0x6c, 0x2c, 0x21, 0x92, 0x5c, 0xd1, 0xef, 0xc3, 0x95, 0x64, + 0xec, 0x74, 0x23, 0xd4, 0xc3, 0x63, 0x4b, 0xb7, 0x5c, 0x93, 0xee, 0x2d, 0x96, 0xb4, 0xcd, 0x78, + 0x18, 0x9b, 0xe1, 0xf0, 0xd8, 0x6a, 0xbb, 0xa6, 0xfa, 0xfb, 0x19, 0xa8, 0xa7, 0x2d, 0x35, 0x1e, + 0xcb, 0x9e, 0x04, 0xe9, 0xe7, 0x93, 0xc0, 0xfc, 0x57, 0xa0, 0x3c, 0x3b, 0x11, 0x11, 0xf9, 0xd1, + 0xda, 0x9e, 0x9d, 0xf0, 0x48, 0x7c, 0xf6, 0x16, 0x14, 0x67, 0x27, 0x7c, 0xb3, 0x5f, 0xb4, 0x9a, + 0x0a, 0x33, 0x1e, 0x24, 0xfb, 0x16, 0x14, 0xe7, 0x82, 0x34, 0x77, 0x11, 0xe9, 0x9c, 0x48, 0xd5, + 0x2d, 0xa8, 0xca, 0xbe, 0x11, 0xdc, 0xb3, 0x68, 0x07, 0xf1, 0x86, 0xe1, 0xa7, 0xfa, 0x5b, 0xeb, + 0x44, 0xf2, 0xad, 0x4e, 0xab, 0xbf, 0x55, 0xc4, 0xc0, 0x16, 0x45, 0xf5, 0xe9, 0x14, 0xb3, 0x3b, + 0xf6, 0xa2, 0xa7, 0x49, 0xe0, 0xd8, 0x08, 0x9a, 0xf3, 0xd0, 0x6b, 0x79, 0x8e, 0x88, 0x1b, 0x11, + 0x97, 0xa0, 0x72, 0x91, 0x37, 0x5f, 0xdc, 0x8f, 0x7c, 0x5f, 0xdc, 0x14, 0xa2, 0x6b, 0x7a, 0x14, + 0xab, 0x92, 0x5f, 0x9a, 0xc1, 0x6a, 0x74, 0x4b, 0x8f, 0xc2, 0x50, 0x1e, 0xc2, 0x46, 0x12, 0x96, + 0x1d, 0x85, 0xb7, 0x2c, 0x66, 0xa9, 0xc5, 0x31, 0xd9, 0x98, 0x54, 0x7f, 0x27, 0x03, 0x9b, 0x4b, + 0xae, 0x06, 0x1c, 0xad, 0xe4, 0x15, 0x23, 0xfc, 0x64, 0x77, 0xa0, 0x3a, 0x35, 0xc2, 0xf1, 0xb1, + 0x3e, 0xf3, 0xad, 0x89, 0x7d, 0x16, 0x3d, 0xc5, 0x44, 0xb0, 0x03, 0x02, 0x51, 0xa8, 0xce, 0x6c, + 0x46, 0x0e, 0x96, 0xa9, 0x1d, 0x0a, 0x06, 0x05, 0x04, 0xea, 0x92, 0xe7, 0x35, 0x0a, 0xe3, 0xcb, + 0x5d, 0x10, 0x75, 0x78, 0x13, 0x0a, 0x9d, 0xd8, 0xa5, 0x11, 0xc7, 0x9a, 0x64, 0xc5, 0x4b, 0x24, + 0x1e, 0x94, 0x5b, 0xf4, 0xaa, 0xc9, 0xbe, 0x31, 0x63, 0xf7, 0x21, 0x3b, 0x35, 0x66, 0x22, 0x16, + 0xa5, 0x11, 0x1f, 0x27, 0x70, 0xec, 0x83, 0x7d, 0x63, 0xc6, 0x19, 0x3a, 0x12, 0xdd, 0xf8, 0x18, + 0x4a, 0x11, 0xe0, 0x5b, 0xb1, 0xee, 0xff, 0x92, 0x85, 0xf2, 0x8e, 0xec, 0xfc, 0x44, 0x53, 0x2d, + 0xf4, 0xe7, 0x2e, 0xaa, 0x1e, 0xe2, 0xf0, 0xa5, 0x32, 0x36, 0xdc, 0xa1, 0x00, 0x45, 0x0b, 0x68, + 0xfd, 0x6b, 0x16, 0xd0, 0x4d, 0x00, 0x9f, 0x4c, 0x72, 0xb2, 0xca, 0xb3, 0x71, 0xdc, 0x63, 0xc7, + 0x14, 0x31, 0x1d, 0xcb, 0xc7, 0xf9, 0xb9, 0x6f, 0x7e, 0x9c, 0x9f, 0x5f, 0x79, 0x9c, 0xff, 0x7f, + 0xcd, 0x01, 0xfc, 0x1b, 0x89, 0xfc, 0xc0, 0x35, 0x8d, 0x64, 0x65, 0x2e, 0xc5, 0x66, 0xf1, 0xdd, + 0x09, 0xa4, 0xfb, 0x1e, 0xd4, 0xa3, 0x61, 0x16, 0x1d, 0x83, 0xd4, 0x75, 0x0d, 0x81, 0xe3, 0x7e, + 0xdd, 0x5a, 0x28, 0x27, 0xd3, 0x3b, 0xb4, 0xf2, 0xf5, 0x3b, 0x54, 0xfd, 0x83, 0x0c, 0x30, 0x61, + 0xd7, 0xee, 0xce, 0x1d, 0x67, 0x68, 0x9d, 0x11, 0x23, 0xb8, 0x0f, 0x9b, 0xc2, 0x29, 0x2b, 0x05, + 0x6e, 0x89, 0x43, 0x2e, 0x8e, 0x48, 0x0e, 0xb9, 0x56, 0xdd, 0x54, 0x5d, 0x5f, 0x79, 0x53, 0x75, + 0xf5, 0x0d, 0xd8, 0xdb, 0x50, 0x91, 0xef, 0x79, 0x72, 0x7d, 0x0b, 0x8c, 0xe4, 0x8a, 0xe7, 0x7f, + 0x5c, 0x07, 0x48, 0x6c, 0xef, 0x5f, 0x75, 0x50, 0xc8, 0x8a, 0x29, 0xc9, 0xae, 0x9a, 0x92, 0x7b, + 0xa0, 0xc8, 0x74, 0xd2, 0x85, 0xe3, 0x7a, 0x42, 0x18, 0xe9, 0x31, 0x76, 0x20, 0x5f, 0x0a, 0x25, + 0x9e, 0x26, 0xce, 0x9b, 0x45, 0xa0, 0x1c, 0xb1, 0x5c, 0x21, 0xa2, 0x4b, 0x76, 0xc0, 0x59, 0x30, + 0xfb, 0x14, 0xae, 0xc7, 0x39, 0xf5, 0x53, 0x3b, 0x3c, 0xf6, 0xe6, 0xa1, 0xf0, 0x92, 0x06, 0x42, + 0x50, 0x5f, 0x8d, 0x4a, 0x7a, 0xce, 0xd1, 0x9c, 0x65, 0x05, 0xec, 0x23, 0x28, 0x4f, 0xe6, 0x8e, + 0xa3, 0x87, 0xd6, 0x59, 0x28, 0xa2, 0xa7, 0x1b, 0x29, 0xb7, 0x85, 0x34, 0xbd, 0x5a, 0x69, 0x22, + 0x12, 0xea, 0xff, 0x5c, 0x87, 0xfc, 0x4f, 0xe6, 0x96, 0x7f, 0xce, 0x3e, 0x86, 0x72, 0x10, 0x4e, + 0x43, 0xf9, 0xa0, 0xf1, 0x3a, 0x2f, 0x80, 0xf0, 0x74, 0x4e, 0x68, 0x4d, 0x2d, 0x37, 0xe4, 0x3e, + 0x3c, 0xa4, 0x25, 0x89, 0x74, 0x19, 0xf2, 0x41, 0x68, 0xcd, 0x02, 0x11, 0xd8, 0xc6, 0x13, 0x6c, + 0x0b, 0xf2, 0xae, 0x67, 0x5a, 0x41, 0x3a, 0x7c, 0xad, 0x87, 0x42, 0x9f, 0x23, 0x98, 0x0a, 0x85, + 0x78, 0xc6, 0x97, 0x0e, 0xfb, 0x38, 0x86, 0xae, 0x1f, 0x58, 0x86, 0x69, 0xbb, 0x47, 0xd1, 0x05, + 0xee, 0x38, 0x8d, 0xb2, 0x96, 0x34, 0x78, 0xe3, 0x28, 0x7a, 0x4d, 0x41, 0x24, 0xd9, 0x16, 0x54, + 0xf0, 0xf3, 0xb9, 0x6f, 0x87, 0xd6, 0xe0, 0x91, 0x18, 0x37, 0x19, 0x84, 0xfa, 0xb7, 0x69, 0x85, + 0xd6, 0x38, 0x1c, 0x7c, 0x29, 0xe2, 0xba, 0x28, 0x60, 0x27, 0x82, 0xa8, 0x26, 0xd4, 0x52, 0xdd, + 0x5d, 0x72, 0x94, 0x0c, 0xda, 0xdd, 0x76, 0x6b, 0xc8, 0x4d, 0x2c, 0x61, 0x9d, 0xaf, 0xcb, 0xd6, + 0x7d, 0x56, 0x32, 0xfb, 0x73, 0x92, 0x1d, 0x96, 0x27, 0xa7, 0x41, 0x5b, 0x7b, 0xd2, 0x56, 0x0a, + 0xea, 0x1f, 0xae, 0xc3, 0xe6, 0xd0, 0x37, 0xdc, 0xc0, 0xe0, 0xb7, 0xf2, 0xdc, 0xd0, 0xf7, 0x1c, + 0xf6, 0x3d, 0x28, 0x85, 0x63, 0x47, 0x9e, 0x86, 0xdb, 0xd1, 0xa6, 0x5f, 0x20, 0x7d, 0x30, 0x1c, + 0x73, 0x87, 0x6a, 0x31, 0xe4, 0x1f, 0xec, 0x5d, 0xc8, 0x8f, 0xac, 0x23, 0xdb, 0x15, 0x0c, 0xf8, + 0xca, 0x62, 0xc6, 0x6d, 0x44, 0xee, 0xad, 0x69, 0x9c, 0x8a, 0xbd, 0x0f, 0x85, 0xb1, 0x37, 0x8d, + 0x24, 0x55, 0x72, 0x81, 0x48, 0xaa, 0x08, 0xb1, 0x7b, 0x6b, 0x9a, 0xa0, 0x63, 0x1f, 0x43, 0xc9, + 0xf7, 0x1c, 0x67, 0x64, 0x8c, 0x4f, 0x84, 0x0c, 0x6b, 0x2c, 0xe6, 0xd1, 0x04, 0x7e, 0x6f, 0x4d, + 0x8b, 0x69, 0xd5, 0x07, 0x50, 0x14, 0x8d, 0xc5, 0x01, 0xd8, 0x6e, 0x3f, 0xe9, 0x88, 0x81, 0x6c, + 0xf5, 0xf7, 0xf7, 0x3b, 0x43, 0x7e, 0x53, 0x59, 0xeb, 0x77, 0xbb, 0xdb, 0xcd, 0xd6, 0x53, 0x65, + 0x7d, 0xbb, 0x04, 0x05, 0xee, 0x46, 0x53, 0x7f, 0x3b, 0x03, 0x1b, 0x0b, 0x1d, 0x60, 0x8f, 0x21, + 0x37, 0x45, 0xa5, 0x92, 0x0f, 0xcf, 0xdd, 0x95, 0xbd, 0x94, 0xd2, 0x5c, 0xd5, 0xc4, 0x1c, 0xea, + 0xa7, 0x50, 0x4f, 0xc3, 0x25, 0x6b, 0xbc, 0x06, 0x65, 0xad, 0xdd, 0xdc, 0xd1, 0xfb, 0x3d, 0xb4, + 0x81, 0xd1, 0x26, 0xa6, 0xe4, 0x73, 0xad, 0x43, 0x06, 0xf4, 0x6f, 0x80, 0xb2, 0x38, 0x30, 0xec, + 0x09, 0x1a, 0x25, 0xd3, 0x99, 0x63, 0x71, 0x41, 0x91, 0x4c, 0xd9, 0xad, 0x15, 0x23, 0x29, 0xc8, + 0x68, 0xc6, 0xea, 0xe3, 0x54, 0x5a, 0xfd, 0xff, 0x80, 0x2d, 0x8f, 0xe0, 0xaf, 0xae, 0xf8, 0xff, + 0x91, 0x81, 0xdc, 0x81, 0x63, 0xb8, 0xec, 0x35, 0xc8, 0xd3, 0x0b, 0x3b, 0x82, 0x7b, 0x56, 0xa4, + 0x0d, 0x8e, 0xcb, 0x82, 0x70, 0xec, 0x6d, 0xc8, 0x86, 0xe3, 0xe8, 0x56, 0xf6, 0xb5, 0x0b, 0x16, + 0xdf, 0xde, 0x9a, 0x86, 0x54, 0xec, 0x1e, 0x64, 0x4d, 0x33, 0x0a, 0xc6, 0x16, 0x56, 0x3f, 0x9a, + 0x8a, 0x3b, 0xd6, 0xc4, 0x76, 0x6d, 0xf1, 0x22, 0x10, 0x92, 0xb0, 0xd7, 0x21, 0x6b, 0x8e, 0x9d, + 0x74, 0x64, 0x3d, 0x37, 0x2a, 0xe3, 0x02, 0xcd, 0xb1, 0xc3, 0x54, 0xa8, 0x85, 0xfe, 0xb9, 0xee, + 0xcf, 0x5d, 0x0a, 0x65, 0x0a, 0x84, 0xb9, 0x53, 0x41, 0x65, 0x66, 0x4e, 0xf1, 0x50, 0x81, 0xb8, + 0xdd, 0x35, 0xf3, 0xad, 0x99, 0xe1, 0xc7, 0x86, 0x8e, 0x1d, 0x1c, 0x70, 0xc0, 0x76, 0x01, 0xe8, + 0x81, 0x4e, 0xf5, 0x1d, 0x7a, 0xfe, 0x05, 0x35, 0x6c, 0x35, 0xfa, 0x5a, 0x71, 0x79, 0x56, 0x60, + 0xd4, 0xbf, 0xc8, 0x42, 0x45, 0x6a, 0x0f, 0xfb, 0x10, 0x4a, 0x66, 0x7a, 0x23, 0x5e, 0x5f, 0x6a, + 0xf4, 0x83, 0x9d, 0x68, 0x0b, 0x9a, 0x62, 0x79, 0x7f, 0x0a, 0xb5, 0xc0, 0x0a, 0xf5, 0x17, 0x86, + 0x6f, 0xf3, 0xa7, 0xb1, 0xd6, 0x65, 0x17, 0xfa, 0xc0, 0x0a, 0x9f, 0x45, 0x98, 0xbd, 0x35, 0xad, + 0x1a, 0x48, 0x69, 0x32, 0x03, 0x44, 0x97, 0xb2, 0xa9, 0xe7, 0xc3, 0x38, 0x70, 0x6f, 0x4d, 0x8b, + 0xf0, 0x48, 0x6a, 0x9d, 0x59, 0xe3, 0x79, 0x18, 0x99, 0x01, 0xb5, 0xa8, 0x43, 0x04, 0xa4, 0x97, + 0x0a, 0xf9, 0x27, 0x7b, 0x88, 0xbc, 0xce, 0x70, 0x1c, 0x8f, 0x74, 0xb6, 0xbc, 0xec, 0xd0, 0xde, + 0x89, 0xe1, 0xfc, 0x65, 0xc4, 0x28, 0xc5, 0xde, 0x80, 0xbc, 0x17, 0x1e, 0x5b, 0x91, 0xf2, 0x1c, + 0x3d, 0x24, 0x83, 0xa0, 0x9d, 0x56, 0x17, 0x57, 0x0a, 0xa1, 0xd5, 0x9f, 0x67, 0xa0, 0x28, 0x46, + 0x80, 0x6d, 0x42, 0x6d, 0xd0, 0x1e, 0xea, 0xcf, 0x9a, 0x5a, 0xa7, 0xb9, 0xdd, 0x6d, 0x8b, 0x0b, + 0x01, 0x4f, 0xb4, 0x66, 0x4f, 0xf0, 0x49, 0xad, 0xfd, 0xac, 0xff, 0xb4, 0xcd, 0x5d, 0x5c, 0x3b, + 0xed, 0xde, 0xe7, 0x4a, 0x96, 0x7b, 0x79, 0xdb, 0x07, 0x4d, 0x0d, 0xb9, 0x64, 0x05, 0x8a, 0xed, + 0xcf, 0xda, 0xad, 0x43, 0x62, 0x93, 0x75, 0x80, 0x9d, 0x76, 0xb3, 0xdb, 0xed, 0xb7, 0x90, 0x6d, + 0x16, 0x18, 0x83, 0x7a, 0x4b, 0x6b, 0x37, 0x87, 0x6d, 0xbd, 0xd9, 0x6a, 0xf5, 0x0f, 0x7b, 0x43, + 0xa5, 0x88, 0x35, 0x36, 0xbb, 0xc3, 0xb6, 0x16, 0x83, 0xe8, 0x71, 0xaf, 0x1d, 0xad, 0x7f, 0x10, + 0x43, 0xca, 0xdb, 0x65, 0x34, 0xc9, 0x68, 0xae, 0xd4, 0xbf, 0xac, 0x43, 0x3d, 0xbd, 0x34, 0xd9, + 0x27, 0x50, 0x32, 0xcd, 0xd4, 0x1c, 0xdf, 0x5c, 0xb5, 0x84, 0x1f, 0xec, 0x98, 0xd1, 0x34, 0xf3, + 0x0f, 0x76, 0x27, 0xda, 0x48, 0xeb, 0x4b, 0x1b, 0x29, 0xda, 0x46, 0x3f, 0x84, 0x0d, 0xf1, 0x10, + 0x8b, 0x69, 0x84, 0xc6, 0xc8, 0x08, 0xac, 0xf4, 0x2e, 0x69, 0x11, 0x72, 0x47, 0xe0, 0xf6, 0xd6, + 0xb4, 0xfa, 0x38, 0x05, 0x61, 0xdf, 0x87, 0xba, 0x41, 0x76, 0x6e, 0x9c, 0x3f, 0x27, 0x2b, 0x81, + 0x4d, 0xc4, 0x49, 0xd9, 0x6b, 0x86, 0x0c, 0xc0, 0x85, 0x68, 0xfa, 0xde, 0x2c, 0xc9, 0x9c, 0x4f, + 0x9d, 0xe5, 0xf8, 0xde, 0x4c, 0xca, 0x5b, 0x35, 0xa5, 0x34, 0xfb, 0x18, 0xaa, 0xa2, 0xe5, 0x89, + 0x27, 0x21, 0xde, 0xb2, 0xbc, 0xd9, 0xa4, 0xd4, 0xed, 0xad, 0x69, 0x95, 0x71, 0x92, 0x64, 0x8f, + 0x50, 0x93, 0x4b, 0x74, 0xf1, 0xa2, 0xbc, 0xd6, 0xa8, 0xb5, 0x51, 0x2e, 0x30, 0xe2, 0x14, 0x7b, + 0x1f, 0x80, 0xda, 0xc9, 0xf3, 0x94, 0x52, 0x21, 0x18, 0xbe, 0x37, 0x8b, 0xb2, 0x94, 0xcd, 0x28, + 0x21, 0x35, 0x8f, 0xfb, 0x81, 0xca, 0xcb, 0xcd, 0x23, 0x5f, 0x50, 0xd2, 0x3c, 0xee, 0x42, 0x8a, + 0x9b, 0xc7, 0xb3, 0xc1, 0x52, 0xf3, 0xa2, 0x5c, 0xbc, 0x79, 0x3c, 0x53, 0xd4, 0x3c, 0x9e, 0xa7, + 0xb2, 0xd8, 0xbc, 0x28, 0x0b, 0x35, 0x8f, 0xe7, 0xf8, 0xfe, 0x92, 0xee, 0x5e, 0xbd, 0x50, 0x77, + 0xc7, 0x69, 0x4b, 0x6b, 0xef, 0xdf, 0x87, 0x7a, 0x70, 0xec, 0x9d, 0x4a, 0x0c, 0xa4, 0x26, 0xe7, + 0x1e, 0x1c, 0x7b, 0xa7, 0x32, 0x07, 0xa9, 0x05, 0x32, 0x00, 0x5b, 0xcb, 0xbb, 0x48, 0x37, 0xd5, + 0xeb, 0x72, 0x6b, 0xa9, 0x87, 0xcf, 0x6c, 0xeb, 0x14, 0x5b, 0x6b, 0x44, 0x09, 0x1c, 0x94, 0xc4, + 0xef, 0x11, 0x88, 0x98, 0xa1, 0x54, 0x38, 0x81, 0xa8, 0x09, 0x62, 0x0f, 0x48, 0x80, 0x6b, 0x6b, + 0xee, 0xca, 0xd9, 0x14, 0x79, 0x6d, 0x1d, 0xba, 0xa9, 0x8c, 0x55, 0x4e, 0x2a, 0xb2, 0x26, 0xbb, + 0x22, 0xb0, 0xbe, 0x9c, 0x5b, 0xee, 0xd8, 0x12, 0xd1, 0x45, 0xa9, 0x5d, 0x31, 0x10, 0xb8, 0x64, + 0x57, 0x44, 0x90, 0x78, 0x5d, 0xc7, 0xd9, 0xd9, 0xe2, 0xba, 0x96, 0x32, 0xd3, 0xba, 0x8e, 0xb3, + 0xc6, 0x1b, 0x2a, 0xce, 0x7b, 0x69, 0x69, 0x43, 0x49, 0x99, 0xf9, 0x86, 0x8a, 0x73, 0x3f, 0x02, + 0xb1, 0x9a, 0xf8, 0xe0, 0xa6, 0x62, 0x90, 0x78, 0xab, 0xc5, 0xe8, 0xc2, 0x38, 0x4e, 0xe1, 0x5a, + 0xf5, 0x2d, 0xb4, 0x15, 0xc4, 0x52, 0xb8, 0x22, 0xaf, 0x55, 0x8d, 0x30, 0xf1, 0x56, 0xf2, 0x93, + 0xa4, 0xfa, 0xc7, 0x79, 0x28, 0x0a, 0xa6, 0xc3, 0x2e, 0xc1, 0x86, 0xe0, 0x7d, 0x3b, 0xcd, 0x61, + 0x73, 0xbb, 0x39, 0x40, 0x6d, 0x85, 0x41, 0x9d, 0x33, 0xbf, 0x18, 0x96, 0x41, 0x86, 0x48, 0xdc, + 0x2f, 0x06, 0xad, 0x23, 0x43, 0x14, 0x79, 0xf9, 0xcb, 0x88, 0x59, 0xb6, 0x01, 0x15, 0x9e, 0x91, + 0x03, 0xe8, 0xa2, 0x1e, 0xe5, 0xe2, 0xe9, 0xbc, 0x94, 0x85, 0x1f, 0x7d, 0x14, 0x92, 0x2c, 0x1c, + 0x50, 0x8c, 0xb3, 0x44, 0x67, 0x23, 0x0c, 0xea, 0x43, 0xed, 0xb0, 0xd7, 0x4a, 0xea, 0x29, 0xd3, + 0xe5, 0x2a, 0x5e, 0xcc, 0xb3, 0x4e, 0xfb, 0xb9, 0x02, 0x98, 0x89, 0x97, 0x42, 0xe9, 0x0a, 0xea, + 0x5b, 0x54, 0x08, 0x25, 0xab, 0xec, 0x1a, 0x5c, 0x1a, 0xec, 0xf5, 0x9f, 0xeb, 0x3c, 0x53, 0xdc, + 0x85, 0x1a, 0xbb, 0x0c, 0x8a, 0x84, 0xe0, 0xc5, 0xd7, 0xb1, 0x4a, 0x82, 0x46, 0x84, 0x03, 0x65, + 0x83, 0x0e, 0x17, 0x11, 0x36, 0xe4, 0x02, 0x48, 0xc1, 0xae, 0xf0, 0xac, 0xfd, 0xee, 0xe1, 0x7e, + 0x6f, 0xa0, 0x6c, 0x62, 0x23, 0x08, 0xc2, 0x5b, 0xce, 0xe2, 0x62, 0x12, 0xb1, 0x75, 0x89, 0x24, + 0x19, 0xc2, 0x9e, 0x37, 0xb5, 0x5e, 0xa7, 0xf7, 0x64, 0xa0, 0x5c, 0x8e, 0x4b, 0x6e, 0x6b, 0x5a, + 0x5f, 0x1b, 0x28, 0x57, 0x62, 0xc0, 0x60, 0xd8, 0x1c, 0x1e, 0x0e, 0x94, 0xab, 0x71, 0x2b, 0x0f, + 0xb4, 0x7e, 0xab, 0x3d, 0x18, 0x74, 0x3b, 0x83, 0xa1, 0x72, 0x8d, 0x5d, 0x81, 0xcd, 0xa4, 0x45, + 0x11, 0x71, 0x43, 0x6a, 0xa8, 0xf6, 0xa4, 0x3d, 0x54, 0xae, 0xc7, 0xcd, 0x68, 0xf5, 0xbb, 0xdd, + 0x26, 0x1d, 0x83, 0xdd, 0x40, 0x22, 0x3a, 0x1f, 0x14, 0xbd, 0x79, 0x05, 0xdb, 0x75, 0xd8, 0x93, + 0x41, 0x37, 0xa5, 0xa5, 0x31, 0x68, 0xff, 0xe4, 0xb0, 0xdd, 0x6b, 0xb5, 0x95, 0x57, 0x93, 0xa5, + 0x11, 0xc3, 0x6e, 0xc5, 0x4b, 0x23, 0x06, 0xdd, 0x8e, 0xeb, 0x8c, 0x40, 0x03, 0x65, 0x0b, 0xcb, + 0x13, 0xed, 0xe8, 0xf5, 0xda, 0xad, 0x21, 0xf6, 0xf5, 0x4e, 0x3c, 0x8a, 0x87, 0x07, 0x4f, 0xb4, + 0xe6, 0x4e, 0x5b, 0x51, 0x11, 0xa2, 0xb5, 0x7b, 0xcd, 0xfd, 0x68, 0xb6, 0x5f, 0xdb, 0xae, 0xd2, + 0x6b, 0xcb, 0x42, 0x5c, 0xaa, 0x3f, 0x06, 0x26, 0x3f, 0x5b, 0x2a, 0x5e, 0x26, 0x63, 0x90, 0x9b, + 0xf8, 0xde, 0x34, 0xba, 0xaa, 0x8e, 0xdf, 0x68, 0xab, 0xcd, 0xe6, 0x23, 0x3a, 0xcb, 0x4a, 0x6e, + 0xae, 0xca, 0x20, 0xf5, 0x8f, 0x33, 0x50, 0x4f, 0x8b, 0x4a, 0x54, 0x11, 0xed, 0x89, 0xee, 0x7a, + 0x21, 0x7f, 0xf2, 0x2a, 0x88, 0x3c, 0x51, 0xf6, 0xa4, 0xe7, 0x85, 0xf4, 0xe6, 0x15, 0x99, 0x8e, + 0xb1, 0xe4, 0xe3, 0xa5, 0xc6, 0x69, 0xd6, 0x81, 0x4b, 0xa9, 0x57, 0x5d, 0x53, 0x0f, 0x8e, 0x35, + 0xe2, 0xb7, 0x28, 0x17, 0xda, 0xaf, 0xb1, 0x60, 0xb9, 0x4f, 0xe2, 0xfe, 0x71, 0x2e, 0xb9, 0x7f, + 0xbc, 0x07, 0xb5, 0x94, 0x64, 0x26, 0x8b, 0x7f, 0x92, 0x6e, 0x69, 0xc9, 0x9e, 0xbc, 0xbc, 0x99, + 0xea, 0x1f, 0x65, 0xa0, 0x2a, 0xcb, 0xe9, 0xef, 0x5c, 0x12, 0xdd, 0x50, 0x11, 0xdf, 0xba, 0x6d, + 0x46, 0x4f, 0x5d, 0x45, 0xa0, 0x0e, 0xbd, 0x32, 0xcf, 0x7d, 0xb0, 0xbb, 0x27, 0x83, 0xb8, 0x3b, + 0x32, 0x08, 0x4d, 0x66, 0xba, 0xb9, 0xb8, 0xfb, 0x14, 0x09, 0xc4, 0x1d, 0x97, 0x04, 0xa2, 0xde, + 0x86, 0xf2, 0xee, 0x49, 0x14, 0x9e, 0x20, 0x3f, 0xfc, 0x56, 0xe6, 0xd7, 0x9d, 0xd5, 0x3f, 0xcd, + 0x40, 0x3d, 0x79, 0xcc, 0x83, 0x82, 0x1e, 0xf9, 0x6b, 0xc0, 0x7c, 0x39, 0xac, 0x9b, 0xa3, 0xe4, + 0x01, 0xfa, 0x75, 0xf9, 0x01, 0xfa, 0xd7, 0x44, 0x61, 0x59, 0x59, 0x9a, 0xc5, 0x75, 0x89, 0xcb, + 0xd4, 0x8f, 0xa0, 0x8a, 0xff, 0x35, 0x6b, 0x62, 0xf9, 0xbe, 0x15, 0x3d, 0x8c, 0xbc, 0x44, 0x9c, + 0x22, 0x22, 0x8b, 0xc4, 0x9a, 0x08, 0xc5, 0x68, 0xe5, 0x7b, 0x23, 0x88, 0x57, 0xff, 0x45, 0x0e, + 0x2a, 0x92, 0xd6, 0xf3, 0x8d, 0x96, 0xdf, 0x4d, 0x28, 0x27, 0x2f, 0x59, 0x88, 0x1b, 0xac, 0x31, + 0x20, 0x35, 0x57, 0xd9, 0x85, 0xb9, 0x6a, 0x40, 0x51, 0x44, 0x47, 0x0a, 0xbf, 0x67, 0x94, 0x4c, + 0x3b, 0xf6, 0xf2, 0x2f, 0x71, 0xbd, 0x7f, 0x00, 0x55, 0xc9, 0x2b, 0x17, 0x3d, 0x8b, 0xb3, 0x48, + 0x5f, 0x49, 0x3c, 0x74, 0x01, 0xbb, 0x02, 0x85, 0xc9, 0x89, 0x6e, 0x8e, 0x22, 0x37, 0x67, 0x7e, + 0x72, 0xb2, 0x33, 0xa2, 0xa3, 0x8b, 0x49, 0x2c, 0xe8, 0xb9, 0xaf, 0xa4, 0x34, 0x89, 0xc4, 0xf9, + 0x3d, 0x28, 0x4e, 0x4e, 0xf8, 0xf5, 0xb8, 0xb2, 0x1c, 0xf0, 0x93, 0x0c, 0x79, 0x61, 0x72, 0x42, + 0x97, 0xe9, 0x3e, 0x05, 0x65, 0xc1, 0xa7, 0x1a, 0x88, 0x93, 0xc9, 0xc5, 0x46, 0x6d, 0xa4, 0xdd, + 0xab, 0x01, 0x7b, 0x0f, 0x2e, 0x0b, 0xc9, 0x6b, 0x04, 0x3a, 0x8f, 0xd7, 0xa7, 0xc7, 0x51, 0xf8, + 0x0b, 0x72, 0x9b, 0x1c, 0xd7, 0x0c, 0x06, 0x84, 0xc1, 0xc5, 0xaa, 0x42, 0x55, 0x5a, 0xbb, 0xfc, + 0xe5, 0x99, 0xb2, 0x96, 0x82, 0xb1, 0xc7, 0x50, 0x9d, 0x9c, 0xf0, 0xb5, 0x30, 0xf4, 0xf6, 0x2d, + 0x11, 0x83, 0x7d, 0x79, 0x71, 0x15, 0x50, 0xa8, 0x6e, 0x8a, 0x92, 0xbd, 0x0b, 0xcc, 0xb7, 0x42, + 0xcb, 0xa5, 0x9e, 0x98, 0x96, 0x61, 0x3a, 0xb6, 0x6b, 0x91, 0xb2, 0x95, 0xd5, 0x36, 0x63, 0xcc, + 0x8e, 0x40, 0xa8, 0xff, 0x32, 0x03, 0xf5, 0x44, 0xfb, 0xc5, 0x0d, 0xcd, 0xee, 0xcb, 0x4f, 0x82, + 0x37, 0x16, 0x15, 0x64, 0x24, 0x79, 0x30, 0x3c, 0x9f, 0xf1, 0x67, 0x43, 0x57, 0x3d, 0x1f, 0xb4, + 0xca, 0xe5, 0x9a, 0x5d, 0xe5, 0x72, 0x55, 0x9f, 0x40, 0x76, 0x78, 0x3e, 0xe3, 0x9e, 0x16, 0x94, + 0x81, 0xdc, 0x2a, 0xe3, 0xd2, 0x8f, 0xe2, 0x13, 0x9e, 0xb6, 0x3f, 0xe7, 0xb7, 0xf7, 0x0f, 0xb4, + 0xce, 0x7e, 0x53, 0xfb, 0x9c, 0x22, 0x4f, 0x48, 0x4b, 0xd8, 0xed, 0x6b, 0xed, 0xce, 0x93, 0x1e, + 0x01, 0x72, 0xe4, 0x87, 0x49, 0x9a, 0xd8, 0x34, 0xcd, 0xdd, 0x13, 0xf9, 0x15, 0x95, 0x4c, 0xea, + 0x15, 0x95, 0xf4, 0x85, 0xdf, 0xf5, 0xc5, 0x0b, 0xbf, 0x2c, 0xde, 0xd1, 0x31, 0x7b, 0x60, 0x6f, + 0x42, 0x6e, 0x72, 0x62, 0x9d, 0xa7, 0x4d, 0x9c, 0xf4, 0x66, 0x24, 0x02, 0xf5, 0x17, 0x19, 0x60, + 0xa9, 0x86, 0x70, 0xad, 0xfb, 0xbb, 0xb6, 0xe5, 0x13, 0x68, 0x88, 0x37, 0x36, 0x39, 0x95, 0xe4, + 0xe3, 0x15, 0x43, 0x7a, 0xc5, 0x4b, 0x22, 0xfb, 0x92, 0x17, 0x8e, 0xd8, 0x7b, 0xc0, 0x1f, 0x39, + 0xc4, 0x05, 0x92, 0x76, 0x6a, 0x48, 0xbc, 0x42, 0x4b, 0x68, 0x92, 0x57, 0x0d, 0xe5, 0xd7, 0x1a, + 0xb9, 0x7b, 0x78, 0x23, 0x99, 0x35, 0xe2, 0x1f, 0xea, 0xef, 0x65, 0xe0, 0x52, 0x7a, 0x41, 0xfc, + 0x72, 0xbd, 0x4c, 0x3f, 0x4d, 0x99, 0x5d, 0x7c, 0x9a, 0x72, 0xd5, 0x7a, 0xca, 0xad, 0x5c, 0x4f, + 0x7f, 0x37, 0x03, 0x97, 0xa5, 0xd1, 0x4f, 0xec, 0xa4, 0xbf, 0xa5, 0x96, 0x49, 0x2f, 0x54, 0xe6, + 0x52, 0x2f, 0x54, 0xaa, 0x7f, 0x98, 0x81, 0xab, 0x0b, 0x2d, 0xd1, 0xac, 0xbf, 0xd5, 0xb6, 0xa4, + 0x5f, 0xb2, 0x24, 0x17, 0x35, 0x0f, 0x75, 0xe4, 0xd7, 0x12, 0x59, 0xfa, 0x69, 0x4a, 0xba, 0x97, + 0xfd, 0xaf, 0xd2, 0x8d, 0x34, 0x93, 0x5b, 0x46, 0xec, 0x23, 0xa8, 0x24, 0x1a, 0x53, 0xf4, 0x50, + 0xc8, 0xca, 0x2b, 0x4a, 0x32, 0xdd, 0x4a, 0x36, 0xba, 0xfe, 0xcd, 0xd8, 0xe8, 0x63, 0xa8, 0xc6, + 0x05, 0xef, 0x58, 0x93, 0xb4, 0x37, 0x62, 0xe1, 0xa9, 0xab, 0x14, 0xa5, 0xfa, 0x21, 0x6c, 0x26, + 0xbd, 0x68, 0x89, 0xe7, 0xd9, 0x6e, 0x43, 0xc5, 0xb5, 0x4e, 0xf5, 0xe8, 0xf1, 0x36, 0x11, 0xb3, + 0xe3, 0x5a, 0xa7, 0x82, 0x40, 0xdd, 0x95, 0xf9, 0x5e, 0xfc, 0xd2, 0xbe, 0x63, 0xa6, 0x82, 0x3f, + 0x3c, 0xc7, 0x8c, 0x50, 0x58, 0x9a, 0x34, 0x31, 0x45, 0xd7, 0x3a, 0xa5, 0x35, 0x77, 0x2a, 0xca, + 0x69, 0x9a, 0xa6, 0x38, 0x30, 0x5f, 0xf5, 0xe8, 0xd1, 0x75, 0x28, 0xcd, 0xfc, 0xd4, 0xcc, 0x16, + 0x67, 0x3e, 0xaf, 0xf6, 0xae, 0x88, 0x08, 0xba, 0xe8, 0x70, 0x9d, 0xc7, 0x08, 0x89, 0x5f, 0xe2, + 0xc8, 0x25, 0xbf, 0xc4, 0xf1, 0x91, 0x60, 0x79, 0xb8, 0xff, 0x44, 0xcd, 0xf1, 0x21, 0x7a, 0xe6, + 0x5e, 0x8d, 0x0e, 0xd1, 0x49, 0x03, 0xb4, 0xbe, 0x14, 0x41, 0x49, 0xf8, 0xa9, 0x6e, 0x43, 0x45, + 0xb2, 0xec, 0x50, 0x35, 0x91, 0xbc, 0x22, 0x41, 0xfa, 0x19, 0x99, 0x64, 0x80, 0xb4, 0x4a, 0xe2, + 0x14, 0x09, 0xd4, 0xdf, 0x07, 0x80, 0x04, 0x97, 0x52, 0x18, 0x32, 0x0b, 0x0a, 0xc3, 0xb7, 0x3a, + 0x91, 0xff, 0x10, 0xea, 0x63, 0x6f, 0x76, 0xae, 0x27, 0x39, 0xb2, 0x2b, 0x73, 0x54, 0x91, 0x6a, + 0x98, 0xdc, 0xef, 0x59, 0x3e, 0x69, 0xcd, 0xad, 0x3c, 0x69, 0xfd, 0x00, 0x8a, 0xdc, 0x71, 0x1f, + 0x88, 0xfb, 0x61, 0xd7, 0x16, 0xfb, 0xf9, 0x40, 0xc4, 0xbe, 0x46, 0x74, 0xac, 0x8d, 0x56, 0xb9, + 0x78, 0xc9, 0x51, 0xbe, 0x2d, 0x76, 0x6b, 0x39, 0x67, 0x44, 0xc6, 0x9f, 0x0f, 0x33, 0xe4, 0xa4, + 0xa4, 0x24, 0x84, 0x53, 0xe1, 0x4d, 0x22, 0x25, 0xa1, 0x28, 0x2b, 0x09, 0xc3, 0x29, 0xf7, 0x21, + 0xa1, 0x92, 0xf0, 0x2e, 0x5c, 0x12, 0x31, 0xf8, 0x98, 0x01, 0x87, 0x93, 0xe8, 0x79, 0xb8, 0x95, + 0x78, 0xdb, 0x63, 0x38, 0x25, 0xed, 0x1b, 0xc9, 0x3f, 0x83, 0xcb, 0xe3, 0x63, 0xc3, 0x3d, 0xb2, + 0xf4, 0x70, 0xe4, 0xe8, 0xf4, 0x90, 0xb8, 0x3e, 0x35, 0x66, 0x42, 0xed, 0x79, 0x73, 0xa9, 0xb1, + 0x2d, 0x22, 0x1e, 0x8e, 0x1c, 0x8a, 0xd0, 0x89, 0xcf, 0xe3, 0x37, 0xc7, 0x8b, 0xf0, 0x85, 0xd3, + 0x28, 0x58, 0x3c, 0x8d, 0x5a, 0xd2, 0x66, 0x2a, 0xcb, 0xda, 0xcc, 0x8d, 0xff, 0x90, 0x83, 0x82, + 0x88, 0x05, 0xbc, 0x0f, 0x39, 0xd3, 0xf7, 0x66, 0x71, 0xc8, 0xde, 0x0a, 0xed, 0x82, 0x7e, 0x75, + 0x08, 0x15, 0x91, 0x07, 0x50, 0x30, 0x4c, 0x53, 0x9f, 0x9c, 0xa4, 0x4f, 0x8c, 0x16, 0x04, 0xfd, + 0xde, 0x9a, 0x96, 0x37, 0x48, 0xe2, 0x7f, 0x02, 0x65, 0xa4, 0x4f, 0xe2, 0xaf, 0x2a, 0xcb, 0xea, + 0x4b, 0x24, 0x92, 0xf7, 0xd6, 0xb4, 0x92, 0x11, 0x89, 0xe7, 0x1f, 0xa4, 0x7d, 0x6f, 0x5c, 0x5e, + 0xde, 0x58, 0xca, 0x7a, 0x91, 0x17, 0xee, 0xd7, 0x81, 0x3b, 0x63, 0x62, 0x6e, 0x93, 0x97, 0x0f, + 0x27, 0x96, 0x78, 0xd3, 0xde, 0x9a, 0xc6, 0xf7, 0x5c, 0xc4, 0xab, 0x3e, 0x8a, 0xfc, 0x62, 0xf1, + 0xef, 0x83, 0xac, 0x18, 0x19, 0xe4, 0x15, 0xb1, 0x73, 0x8c, 0x18, 0x07, 0x66, 0x33, 0xcd, 0x28, + 0x6c, 0xa7, 0xb8, 0x94, 0x2d, 0xe6, 0x48, 0x94, 0x2d, 0x66, 0x4f, 0x8f, 0xa1, 0x42, 0x2e, 0x2a, + 0x91, 0xaf, 0xb4, 0x34, 0xb4, 0x09, 0x43, 0x21, 0xc7, 0x7b, 0xc2, 0x5e, 0x5a, 0x51, 0x3f, 0x7d, + 0x4b, 0xf6, 0x6d, 0xde, 0x5c, 0x39, 0x50, 0x5a, 0xec, 0xe6, 0xe4, 0x9d, 0xd5, 0x78, 0x1e, 0xb6, + 0x0d, 0x55, 0x43, 0x92, 0x34, 0xc2, 0xd1, 0x79, 0x73, 0xc5, 0x3c, 0xc5, 0x34, 0x54, 0x86, 0x94, + 0x4e, 0x0e, 0xe0, 0x6e, 0x68, 0x70, 0x75, 0xf5, 0x52, 0x96, 0x23, 0x49, 0x72, 0x3c, 0x92, 0x44, + 0x4d, 0xbf, 0xaf, 0x92, 0xbe, 0xe4, 0x2a, 0xc5, 0x95, 0xfc, 0x08, 0x6d, 0x64, 0x79, 0xf3, 0x56, + 0xa0, 0x18, 0xbd, 0x4a, 0x4c, 0x61, 0xb1, 0xad, 0xfe, 0xc1, 0xe7, 0x4a, 0x06, 0xc1, 0x9d, 0xde, + 0x60, 0xd8, 0xec, 0x89, 0xe3, 0xd5, 0x4e, 0x4f, 0x1c, 0xaf, 0xaa, 0xff, 0x26, 0x0b, 0xe5, 0xd8, + 0x3d, 0xfc, 0xdd, 0x0d, 0xe3, 0xd8, 0xe2, 0xcc, 0xca, 0x16, 0xe7, 0x82, 0xa6, 0x26, 0x3f, 0x5a, + 0xb2, 0x91, 0xd6, 0x87, 0x82, 0xe5, 0xfb, 0x77, 0xf9, 0x6f, 0x78, 0xff, 0x4e, 0x8e, 0x4c, 0x2c, + 0xa4, 0x23, 0x13, 0x17, 0x5e, 0xa6, 0x2e, 0x52, 0x98, 0x8a, 0xfc, 0x32, 0xf5, 0x85, 0xf1, 0x29, + 0xa5, 0x8b, 0xe3, 0x53, 0xe8, 0xa7, 0xd5, 0x9e, 0xd9, 0xd6, 0xa9, 0x08, 0xd0, 0x13, 0xa9, 0xb4, + 0xf8, 0x80, 0x97, 0x88, 0x8f, 0x6f, 0xc0, 0x8a, 0xd8, 0x43, 0xb8, 0x3c, 0x39, 0x89, 0x5f, 0xe1, + 0x4c, 0x0c, 0xac, 0x2a, 0x75, 0x63, 0x25, 0x4e, 0xfd, 0xdd, 0x0c, 0x40, 0xe2, 0x43, 0xfd, 0xa5, + 0x1d, 0x3c, 0x92, 0x0d, 0x9d, 0xfd, 0x1a, 0x1b, 0xfa, 0x25, 0x0f, 0x7b, 0xa8, 0x5f, 0x42, 0x39, + 0xf6, 0x9a, 0x7f, 0xf7, 0x35, 0xf6, 0xad, 0xaa, 0xfc, 0xcd, 0xc8, 0xd9, 0x15, 0xbb, 0x9d, 0x7f, + 0xd9, 0xb1, 0x48, 0x55, 0x9f, 0x7d, 0x49, 0xf5, 0x67, 0xdc, 0xe3, 0x14, 0x57, 0xfe, 0x2b, 0xde, + 0x58, 0xf2, 0x9a, 0xcf, 0xa5, 0xd6, 0xbc, 0x3a, 0x17, 0x6e, 0xb3, 0x5f, 0xbe, 0xea, 0x6f, 0xd5, + 0xe1, 0xbf, 0xce, 0x44, 0xbe, 0x9d, 0xf8, 0x6d, 0xd3, 0x0b, 0x15, 0xad, 0xd5, 0xee, 0xa9, 0x6f, + 0x53, 0xdd, 0xd7, 0x5a, 0x9b, 0xb9, 0xaf, 0xb3, 0x36, 0xdf, 0x84, 0x3c, 0x17, 0x08, 0xf9, 0x8b, + 0x2c, 0x4d, 0x8e, 0x7f, 0xe9, 0xaf, 0x01, 0xa8, 0xaa, 0x50, 0x2c, 0x79, 0x7f, 0x2f, 0x47, 0xe5, + 0x46, 0xbf, 0x64, 0x40, 0x41, 0xd4, 0xbf, 0x9d, 0xe1, 0xdc, 0xf5, 0xbb, 0x8e, 0xc9, 0xaf, 0xcc, + 0xdc, 0xfc, 0xa7, 0xeb, 0x50, 0x4b, 0x1d, 0x98, 0x7d, 0x87, 0xc6, 0xac, 0xe4, 0xe6, 0xd9, 0xd5, + 0xdc, 0xfc, 0xbb, 0x3c, 0x59, 0xf5, 0x7f, 0x44, 0x02, 0xa4, 0x62, 0xcc, 0x4a, 0xe9, 0x18, 0x33, + 0xe4, 0xa6, 0xd5, 0x94, 0x56, 0xbe, 0x4a, 0x7f, 0xcf, 0xac, 0xd4, 0xdf, 0x6f, 0xc5, 0x3f, 0x24, + 0xd6, 0xd9, 0xe1, 0x86, 0x65, 0x4d, 0x93, 0x20, 0xec, 0x53, 0xb8, 0xce, 0xb5, 0x1a, 0xae, 0xc8, + 0xe9, 0xde, 0x44, 0x8f, 0x7f, 0x66, 0x4c, 0xc4, 0xcd, 0x5d, 0xe5, 0x04, 0xfc, 0xa7, 0x22, 0x26, + 0xcd, 0x08, 0xab, 0x76, 0xa0, 0x96, 0x3a, 0xbd, 0x94, 0x7e, 0xb2, 0x30, 0x23, 0xff, 0x64, 0x21, + 0xdb, 0x82, 0xfc, 0xe9, 0xb1, 0xe5, 0x5b, 0x2b, 0x9e, 0x76, 0xe4, 0x08, 0xf5, 0xfb, 0x50, 0x95, + 0x23, 0x29, 0xd8, 0x3b, 0x90, 0xb7, 0x43, 0x6b, 0x1a, 0xd9, 0x56, 0x57, 0x97, 0x83, 0x2d, 0xc8, + 0x90, 0xe6, 0x44, 0xea, 0xcf, 0x33, 0xa0, 0x2c, 0xe2, 0xa4, 0xdf, 0x55, 0xcc, 0x5c, 0xf0, 0xbb, + 0x8a, 0xeb, 0xa9, 0x46, 0xae, 0xfa, 0x69, 0xc4, 0xf8, 0x79, 0xb9, 0xdc, 0x05, 0xcf, 0xcb, 0xb1, + 0x37, 0xa0, 0xe4, 0x5b, 0xf4, 0xa3, 0x75, 0xe6, 0x8a, 0x58, 0xe6, 0x18, 0xa7, 0xfe, 0x4e, 0x06, + 0x8a, 0x22, 0xec, 0x63, 0xa5, 0xb1, 0xfb, 0x16, 0x14, 0xf9, 0x0f, 0xd8, 0x45, 0xc6, 0xff, 0x52, + 0x1c, 0x64, 0x84, 0x67, 0xb7, 0x78, 0x30, 0x4c, 0xda, 0xf8, 0x3d, 0x70, 0x0c, 0x57, 0x23, 0xb8, + 0xf8, 0x05, 0x18, 0x63, 0x2a, 0xae, 0x11, 0xf2, 0x57, 0x3f, 0x80, 0x40, 0xfc, 0xc6, 0xe0, 0x0f, + 0xa0, 0x28, 0xc2, 0x4a, 0x56, 0x36, 0xe5, 0x65, 0x3f, 0xdd, 0xb6, 0x05, 0x90, 0xc4, 0x99, 0xac, + 0x2a, 0x41, 0xbd, 0x0f, 0xa5, 0x28, 0xb4, 0x04, 0xd7, 0x5f, 0x52, 0xb5, 0x88, 0x55, 0x97, 0x1b, + 0xe3, 0x88, 0xf7, 0x8f, 0xbb, 0xde, 0xf8, 0x84, 0xbc, 0x6a, 0xef, 0x01, 0xc5, 0xf0, 0x0f, 0x97, + 0x9e, 0x47, 0x49, 0xbf, 0x35, 0x1d, 0x13, 0xb1, 0xfb, 0x10, 0xb3, 0xe3, 0x97, 0x59, 0xcb, 0x6a, + 0x33, 0xba, 0x4b, 0x42, 0xab, 0xec, 0x91, 0xf0, 0x1e, 0x75, 0xe9, 0x21, 0xa3, 0x94, 0xc3, 0x26, + 0xd5, 0x26, 0x4d, 0x22, 0x53, 0xeb, 0x50, 0x95, 0xcf, 0xc3, 0xd5, 0x26, 0x6c, 0xee, 0x5b, 0xa1, + 0x81, 0x3c, 0x6b, 0x30, 0x36, 0x5c, 0xa4, 0xe7, 0xeb, 0x17, 0x3f, 0xd2, 0xeb, 0x77, 0x91, 0x4e, + 0xe3, 0x44, 0xea, 0xcf, 0x73, 0xa0, 0x2c, 0xe2, 0xbe, 0xee, 0x5e, 0xcd, 0x6d, 0xa8, 0x78, 0xb4, + 0x2e, 0x52, 0xbf, 0xf1, 0xc3, 0x41, 0x52, 0xc0, 0x6a, 0xea, 0x41, 0xfa, 0x92, 0x1d, 0xec, 0xf1, + 0x27, 0xe9, 0xaf, 0xf1, 0xb7, 0x3d, 0x1c, 0x6f, 0x4c, 0xcb, 0xba, 0x4a, 0x4f, 0x79, 0x74, 0xbd, + 0x31, 0x5d, 0xd7, 0x11, 0x06, 0x37, 0x0f, 0xd2, 0xaa, 0x6a, 0x25, 0x61, 0x65, 0xd3, 0xa1, 0x81, + 0x08, 0x63, 0x0d, 0x03, 0x71, 0x01, 0xaa, 0xc4, 0x01, 0xc3, 0x20, 0x7a, 0x94, 0x77, 0x2c, 0x7e, + 0x90, 0x26, 0x4b, 0x8f, 0xf2, 0xb6, 0x5c, 0xba, 0xad, 0x43, 0xbf, 0x9f, 0x34, 0x16, 0xbf, 0x6f, + 0x25, 0x1e, 0x41, 0x46, 0xd4, 0x6b, 0xfc, 0x27, 0x7b, 0x7c, 0x2b, 0x08, 0xf8, 0x2b, 0x5e, 0x65, + 0xf1, 0x90, 0x91, 0x00, 0xc6, 0xcf, 0x85, 0x89, 0x1f, 0x4c, 0x42, 0x12, 0x10, 0xcf, 0x85, 0xf1, + 0x9f, 0x4b, 0x42, 0x82, 0xeb, 0x50, 0xfa, 0xca, 0x73, 0x2d, 0x32, 0xdc, 0x2b, 0xd4, 0xaa, 0x22, + 0xa6, 0xf7, 0x8d, 0x99, 0xfa, 0xe7, 0x19, 0xb8, 0xbc, 0x38, 0xaa, 0xb4, 0x60, 0xaa, 0x50, 0x6a, + 0xf5, 0xbb, 0x7a, 0xaf, 0xb9, 0xdf, 0x56, 0xd6, 0xd8, 0x06, 0x54, 0xfa, 0xdb, 0x3f, 0x6e, 0xb7, + 0x86, 0x1c, 0x90, 0xa1, 0x7b, 0xa2, 0x03, 0x7d, 0xaf, 0xb3, 0xb3, 0xd3, 0xee, 0x71, 0x2b, 0xa5, + 0xbf, 0xfd, 0x63, 0xbd, 0xdb, 0x6f, 0xf1, 0xdf, 0x57, 0x89, 0x4e, 0xdf, 0x07, 0x4a, 0x8e, 0x4e, + 0xbc, 0x29, 0x26, 0x14, 0x93, 0x79, 0x1e, 0xf2, 0xf8, 0x7c, 0xa0, 0xb7, 0x7a, 0x43, 0xa5, 0x80, + 0xa9, 0xde, 0x61, 0xb7, 0x4b, 0x29, 0x8a, 0x6d, 0x6a, 0xf5, 0xf7, 0x0f, 0xb4, 0xf6, 0x60, 0xa0, + 0x0f, 0x3a, 0x3f, 0x6d, 0x2b, 0x25, 0xaa, 0x59, 0xeb, 0x3c, 0xe9, 0xf4, 0x38, 0xa0, 0xcc, 0x8a, + 0x90, 0xdd, 0xef, 0xf4, 0xf8, 0xfd, 0xd8, 0xfd, 0xe6, 0x67, 0x4a, 0x05, 0x3f, 0x06, 0x87, 0xfb, + 0x4a, 0xf5, 0xfe, 0x1d, 0xa8, 0xca, 0x3f, 0x52, 0x46, 0x51, 0x8e, 0x9e, 0x6b, 0xf1, 0xa7, 0x7b, + 0xbb, 0x5f, 0x7d, 0xa8, 0x64, 0xee, 0xff, 0xa6, 0xf4, 0xe3, 0x0f, 0x44, 0x23, 0x0e, 0x03, 0xe8, + 0x36, 0x20, 0xbf, 0x6d, 0x48, 0xae, 0x7f, 0xba, 0x9c, 0xb8, 0xd7, 0x1c, 0xec, 0xf1, 0x63, 0x02, + 0x81, 0x21, 0x40, 0x36, 0x79, 0xf2, 0x95, 0x2e, 0xfb, 0xd2, 0x67, 0x7c, 0xd8, 0x9e, 0xa7, 0x7b, + 0x98, 0x9d, 0x01, 0x76, 0x4e, 0x81, 0x2a, 0x7e, 0xc5, 0xb8, 0xe2, 0x7d, 0x15, 0x2a, 0xd2, 0x2b, + 0xdd, 0x54, 0x87, 0x11, 0x1c, 0x8b, 0x57, 0x64, 0xd1, 0xdc, 0x54, 0x32, 0xf7, 0xdf, 0x40, 0x89, + 0x21, 0xbf, 0x91, 0x0d, 0x50, 0xe8, 0x79, 0xfe, 0xd4, 0x70, 0x04, 0x9d, 0x35, 0x0f, 0x90, 0xee, + 0x3d, 0xb8, 0xb2, 0xf2, 0xc5, 0x6f, 0x8a, 0xd4, 0xb5, 0xa7, 0x33, 0xc7, 0xe2, 0xc1, 0xa6, 0x7b, + 0xe7, 0x23, 0xdf, 0x36, 0x95, 0xcc, 0xfd, 0xc7, 0xd1, 0x95, 0xb4, 0xa8, 0xee, 0x6e, 0xbf, 0xb9, + 0xc3, 0x27, 0x37, 0xbe, 0x8c, 0x3c, 0xdc, 0xe6, 0x2f, 0xc4, 0x6a, 0xed, 0xc1, 0x61, 0x77, 0x28, + 0x2e, 0x3e, 0xdf, 0xff, 0x11, 0x34, 0x2e, 0x8a, 0xba, 0xc4, 0x16, 0xb5, 0xf6, 0x9a, 0x14, 0xd9, + 0x8a, 0x93, 0xd9, 0xd7, 0x79, 0x2a, 0xc3, 0x03, 0x83, 0xbb, 0x6d, 0x8a, 0xc8, 0xb8, 0xff, 0xb3, + 0x8c, 0xc4, 0xc2, 0xa2, 0xc8, 0xb9, 0x18, 0x20, 0x66, 0x49, 0x06, 0x69, 0x96, 0x61, 0x2a, 0x19, + 0x76, 0x15, 0x58, 0x0a, 0xd4, 0xf5, 0xc6, 0x86, 0xa3, 0xac, 0x53, 0xec, 0x45, 0x04, 0xa7, 0xf8, + 0x66, 0x25, 0xcb, 0x5e, 0x85, 0xeb, 0x31, 0xac, 0xeb, 0x9d, 0x1e, 0xf8, 0x36, 0xda, 0xda, 0xe7, + 0x1c, 0x9d, 0xdb, 0xfe, 0xe1, 0x9f, 0xfd, 0xe2, 0x56, 0xe6, 0xdf, 0xfd, 0xe2, 0x56, 0xe6, 0xbf, + 0xfe, 0xe2, 0xd6, 0xda, 0xcf, 0xff, 0xdb, 0xad, 0xcc, 0x4f, 0xe5, 0x9f, 0x32, 0x9f, 0x1a, 0xa1, + 0x6f, 0x9f, 0xf1, 0x4d, 0x13, 0x25, 0x5c, 0xeb, 0xbd, 0xd9, 0xc9, 0xd1, 0x7b, 0xb3, 0xd1, 0x7b, + 0xc8, 0x99, 0x46, 0x05, 0xfa, 0xd1, 0xf2, 0x47, 0xff, 0x3b, 0x00, 0x00, 0xff, 0xff, 0xf8, 0x24, + 0x7a, 0x5d, 0x14, 0x7d, 0x00, 0x00, } func (m *Type) Marshal() (dAtA []byte, err error) { @@ -16740,6 +16748,13 @@ func (m *Stats) MarshalToSizedBuffer(dAtA []byte) (int, error) { i -= len(m.XXX_unrecognized) copy(dAtA[i:], m.XXX_unrecognized) } + if len(m.Sql) > 0 { + i -= len(m.Sql) + copy(dAtA[i:], m.Sql) + i = encodeVarintPlan(dAtA, i, uint64(len(m.Sql))) + i-- + dAtA[i] = 0x4a + } if m.HashmapStats != nil { { size, err := m.HashmapStats.MarshalToSizedBuffer(dAtA[:i]) @@ -25736,6 +25751,10 @@ func (m *Stats) ProtoSize() (n int) { l = m.HashmapStats.ProtoSize() n += 1 + l + sovPlan(uint64(l)) } + l = len(m.Sql) + if l > 0 { + n += 1 + l + sovPlan(uint64(l)) + } if m.XXX_unrecognized != nil { n += len(m.XXX_unrecognized) } @@ -37639,6 +37658,38 @@ func (m *Stats) Unmarshal(dAtA []byte) error { return err } iNdEx = postIndex + case 9: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Sql", 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.Sql = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipPlan(dAtA[iNdEx:]) diff --git a/pkg/sql/colexec/table_function/fulltext.go b/pkg/sql/colexec/table_function/fulltext.go index 7e0d50f9769ff..4870969c81295 100644 --- a/pkg/sql/colexec/table_function/fulltext.go +++ b/pkg/sql/colexec/table_function/fulltext.go @@ -15,8 +15,8 @@ package table_function import ( + "encoding/json" "fmt" - "strings" "github.com/matrixorigin/matrixone/pkg/common/moerr" moruntime "github.com/matrixorigin/matrixone/pkg/common/runtime" @@ -26,6 +26,7 @@ import ( "github.com/matrixorigin/matrixone/pkg/fulltext" "github.com/matrixorigin/matrixone/pkg/pb/plan" "github.com/matrixorigin/matrixone/pkg/sql/colexec" + "github.com/matrixorigin/matrixone/pkg/sql/parsers/tree" "github.com/matrixorigin/matrixone/pkg/util/executor" "github.com/matrixorigin/matrixone/pkg/vm" "github.com/matrixorigin/matrixone/pkg/vm/process" @@ -39,14 +40,15 @@ type fulltextState struct { inited bool errors chan error stream_chan chan executor.Result - score_array []map[any]float32 - n_doc_id int - last_doc_id any n_result uint64 - sql_closed bool sacc *fulltext.SearchAccum limit uint64 nrows int + idx2word map[int]string + agghtab map[any]uint64 + aggcnt []int64 + mpool *fulltext.FixedBytePool + param fulltext.FullTextParserParam // holding output batch batch *batch.Batch @@ -63,6 +65,10 @@ func (u *fulltextState) free(tf *TableFunction, proc *process.Process, pipelineF u.batch.Clean(proc.Mp()) } + if u.mpool != nil { + u.mpool.Close() + } + for { select { case res, ok := <-u.stream_chan: @@ -76,6 +82,8 @@ func (u *fulltextState) free(tf *TableFunction, proc *process.Process, pipelineF } } +// return (doc_id, score) as result +// when scoremap is empty, return result end. func (u *fulltextState) returnResult(proc *process.Process, scoremap map[any]float32) (vm.CallResult, error) { // return result if u.batch.VectorCount() == 1 { @@ -120,7 +128,6 @@ func (u *fulltextState) returnResult(proc *process.Process, scoremap map[any]flo func (u *fulltextState) call(tf *TableFunction, proc *process.Process) (vm.CallResult, error) { - var scoremap map[any]float32 u.batch.CleanOnlyData() // number of result more than pushdown limit and exit @@ -128,24 +135,14 @@ func (u *fulltextState) call(tf *TableFunction, proc *process.Process) (vm.CallR return vm.CancelResult, nil } - // return scoremap when array is not empty - if len(u.score_array) > 0 { - scoremap, u.score_array = u.score_array[0], u.score_array[1:] - return u.returnResult(proc, scoremap) - } - // array is empty, try to get batch from SQL executor - for !u.sql_closed { - sql_closed, err := getResults(u, proc, u.sacc) - if err != nil { - return vm.CancelResult, err - } - u.sql_closed = sql_closed + scoremap, err := evaluate(u, proc, u.sacc) + if err != nil { + return vm.CancelResult, err + } - if len(u.score_array) > 0 { - scoremap, u.score_array = u.score_array[0], u.score_array[1:] - return u.returnResult(proc, scoremap) - } + if scoremap != nil { + return u.returnResult(proc, scoremap) } return vm.CancelResult, nil } @@ -155,10 +152,16 @@ func (u *fulltextState) call(tf *TableFunction, proc *process.Process) (vm.CallR func (u *fulltextState) start(tf *TableFunction, proc *process.Process, nthRow int, analyzer process.Analyzer) error { if !u.inited { + if len(tf.Params) > 0 { + err := json.Unmarshal([]byte(tf.Params), &u.param) + if err != nil { + return err + } + } u.batch = tf.createResultBatch() u.errors = make(chan error) u.stream_chan = make(chan executor.Result, 8) - u.score_array = make([]map[any]float32, 0, 512) + u.idx2word = make(map[int]string) u.inited = true } @@ -208,6 +211,7 @@ func fulltextIndexScanPrepare(proc *process.Process, tableFunction *TableFunctio var ft_runSql = ft_runSql_fn +// run SQL in batch mode. Result batches will stored in memory and return once all result batches received. func ft_runSql_fn(proc *process.Process, sql string) (executor.Result, error) { v, ok := moruntime.ServiceRuntime(proc.GetService()).GetGlobalVariables(moruntime.InternalSQLExecutor) if !ok { @@ -228,7 +232,7 @@ func ft_runSql_fn(proc *process.Process, sql string) (executor.Result, error) { var ft_runSql_streaming = ft_runSql_streaming_fn // run SQL in WithStreaming() and pass the channel to SQL executor -func ft_runSql_streaming_fn(proc *process.Process, sql string, stream_chan chan executor.Result) (executor.Result, error) { +func ft_runSql_streaming_fn(proc *process.Process, sql string, stream_chan chan executor.Result, error_chan chan error) (executor.Result, error) { v, ok := moruntime.ServiceRuntime(proc.GetService()).GetGlobalVariables(moruntime.InternalSQLExecutor) if !ok { panic("missing lock service") @@ -242,79 +246,81 @@ func ft_runSql_streaming_fn(proc *process.Process, sql string, stream_chan chan WithDatabase(proc.GetSessionInfo().Database). WithTimeZone(proc.GetSessionInfo().TimeZone). WithAccountID(proc.GetSessionInfo().AccountId). - WithStreaming(stream_chan) + WithStreaming(stream_chan, error_chan) return exec.Exec(proc.GetTopContext(), sql, opts) } -// run SQL to get the (doc_id, pos) of all patterns (words) in the search string +// run SQL to get the (doc_id, word_index) of all patterns (words) in the search string func runWordStats(u *fulltextState, proc *process.Process, s *fulltext.SearchAccum) (executor.Result, error) { - var union []string - - var keywords []string - for _, p := range s.Pattern { - ssNoOp := p.GetLeafText(fulltext.TEXT) - for _, w := range ssNoOp { - keywords = append(keywords, "'"+w+"'") - } + sql, err := fulltext.PatternToSql(s.Pattern, s.Mode, s.TblName, u.param.Parser) + if err != nil { + return executor.Result{}, err } - if len(keywords) > 0 { - union = append(union, fmt.Sprintf("SELECT doc_id, pos, word FROM %s WHERE word IN (%s)", - s.TblName, strings.Join(keywords, ","))) + //logutil.Infof("SQL is %s", sql) + res, err := ft_runSql_streaming(proc, sql, u.stream_chan, u.errors) + if err != nil { + return executor.Result{}, err } - sqlfmt := "SELECT doc_id, pos, '%s' FROM %s WHERE prefix_eq(word, '%s')" - for _, p := range s.Pattern { - ssStar := p.GetLeafText(fulltext.STAR) - for _, w := range ssStar { - // remove the last character which should be '*' for prefix search - slen := len(w) - if w[slen-1] != '*' { - return executor.Result{}, moerr.NewInternalError(proc.Ctx, "wildcard search without character *") - } - // prefix search - prefix := w[0 : slen-1] - union = append(union, fmt.Sprintf(sqlfmt, w, s.TblName, prefix)) + return res, nil +} + +// evaluate the score for all document vectors in Agg hashtable. +// whenever there is 8192 results, return it immediately. +func evaluate(u *fulltextState, proc *process.Process, s *fulltext.SearchAccum) (scoremap map[any]float32, err error) { + + scoremap = make(map[any]float32, 8192) + keys := make([]any, 0, 8192) + + aggcnt := u.aggcnt + + for doc_id, addr := range u.agghtab { + docvec, err := u.mpool.GetItem(addr) + if err != nil { + return nil, err } - } - sql := strings.Join(union, " UNION ") + score, err := s.Eval(docvec, aggcnt) + if err != nil { + return nil, err + } - if len(union) == 1 { - sql += " ORDER BY doc_id" - } else { - sql = fmt.Sprintf("SELECT * FROM (%s) ORDER BY doc_id", sql) + keys = append(keys, doc_id) + + if len(score) > 0 { + scoremap[doc_id] = score[0] + } + + if len(scoremap) >= 8192 { + break + } } - //logutil.Infof("SQL is %s", sql) - res, err := ft_runSql_streaming(proc, sql, u.stream_chan) - if err != nil { - return executor.Result{}, err + for _, k := range keys { + u.mpool.FreeItem(u.agghtab[k]) + delete(u.agghtab, k) } - return res, nil + return scoremap, nil } -// get one batch and evaluate the result in mini batches with size 8192 -func getResults(u *fulltextState, proc *process.Process, s *fulltext.SearchAccum) (stream_closed bool, err error) { +// result from SQL is (doc_id, index constant (refer to Pattern.Index)) +// Two group by happens here +// 1. Group by the result into []uint8 which is DocCount[Pattern.Index]. +// 2. Aggregate the total number of documents contain the word index (Pattern.Index). AggCnt[Pattern.Index]. +func groupby(u *fulltextState, proc *process.Process, s *fulltext.SearchAccum) (stream_closed bool, err error) { // first receive the batch and calculate the scoremap // We don't need to calculate mini-batch????? var res executor.Result var ok bool + select { case res, ok = <-u.stream_chan: if !ok { // channel closed and evaluate the rest of result - scoremap, err := s.Eval() - if err != nil { - return false, err - } - if len(scoremap) > 0 { - u.score_array = append(u.score_array, scoremap) - } - return true, nil } case err = <-u.errors: @@ -326,7 +332,7 @@ func getResults(u *fulltextState, proc *process.Process, s *fulltext.SearchAccum bat := res.Batches[0] defer res.Close() - if len(bat.Vecs) != 3 { + if len(bat.Vecs) != 2 { return false, moerr.NewInternalError(proc.Ctx, "output vector columns not match") } @@ -343,49 +349,73 @@ func getResults(u *fulltextState, proc *process.Process, s *fulltext.SearchAccum doc_id = key } - // pos int32 - pos := vector.GetFixedAtWithTypeCheck[int32](bat.Vecs[1], i) - // word string - word := bat.Vecs[2].GetStringAt(i) - - if u.last_doc_id == nil { - u.last_doc_id = doc_id - } else if u.last_doc_id != doc_id { - // new doc_id - u.last_doc_id = doc_id - u.n_doc_id++ - } + widx := vector.GetFixedAtWithTypeCheck[int32](bat.Vecs[1], i) + + var docvec []uint8 + if s.Mode == int64(tree.FULLTEXT_NL) || s.Pattern[0].Operator == fulltext.PHRASE { + // phrase search widx is dummy and fill in value 1 for all keywords + nwords := s.Nkeywords + addr, ok := u.agghtab[doc_id] + if ok { + docvec, err = u.mpool.GetItem(addr) + if err != nil { + return false, err + } + for i := 0; i < nwords; i++ { + if docvec[i] < 255 { + docvec[i]++ + } + } + } else { + //docvec = make([]uint8, s.Nkeywords) + addr, docvec, err = u.mpool.NewItem() + if err != nil { + return false, err + } + + for i := 0; i < nwords; i++ { + docvec[i] = 1 + } + u.agghtab[doc_id] = addr + } - //logutil.Infof("WORD:%s, DOC_ID:%v, POS:%d, LAST: %v", word, doc_id, pos, last_doc_id) - if u.n_doc_id >= 8192 { - scoremap, err := s.Eval() - if err != nil { - return false, err + // update only once per doc_id + for i := 0; i < nwords; i++ { + if docvec[i] == 1 { + u.aggcnt[i]++ + } } + } else { - if len(scoremap) > 0 { - u.score_array = append(u.score_array, scoremap) + addr, ok := u.agghtab[doc_id] + if ok { + docvec, err = u.mpool.GetItem(addr) + if err != nil { + return false, err + } + if docvec[widx] < 255 { + // limit doc count to 255 to fit uint8 + docvec[widx]++ + } + } else { + //docvec = make([]uint8, s.Nkeywords) + addr, docvec, err = u.mpool.NewItem() + if err != nil { + return false, err + } + docvec[widx] = 1 + u.agghtab[doc_id] = addr } - clear(s.WordAccums) - u.n_doc_id = 0 - } + // update only once per doc_id + if docvec[widx] == 1 { + u.aggcnt[widx]++ + } - w, ok := s.WordAccums[word] - if !ok { - s.WordAccums[word] = fulltext.NewWordAccum() - w = s.WordAccums[word] - } - _, ok = w.Words[doc_id] - if ok { - w.Words[doc_id].Position = append(w.Words[doc_id].Position, pos) - w.Words[doc_id].DocCount += 1 - } else { - positions := make([]int32, 0, 128) - positions = append(positions, pos) - w.Words[doc_id] = &fulltext.Word{DocId: doc_id, Position: positions, DocCount: 1} } + //logutil.Infof("ROW widx=%d, docid = %v", widx, doc_id) + } return false, nil @@ -417,31 +447,54 @@ func runCountStar(proc *process.Process, s *fulltext.SearchAccum) (int64, error) } func fulltextIndexMatch(u *fulltextState, proc *process.Process, tableFunction *TableFunction, srctbl, tblname, pattern string, - mode int64, bat *batch.Batch) error { + mode int64, bat *batch.Batch) (err error) { - // parse the search string to []Pattern and create SearchAccum - s, err := fulltext.NewSearchAccum(srctbl, tblname, pattern, mode, "") - if err != nil { - return err - } + if u.sacc == nil { + // parse the search string to []Pattern and create SearchAccum + s, err := fulltext.NewSearchAccum(srctbl, tblname, pattern, mode, "") + if err != nil { + return err + } - // count(*) to get number of records in source table - nrow, err := runCountStar(proc, s) - if err != nil { - return err - } - s.Nrow = nrow + u.mpool = fulltext.NewFixedBytePool(proc, uint64(s.Nkeywords), 0, 0) + u.agghtab = make(map[any]uint64, 1024) + u.aggcnt = make([]int64, s.Nkeywords) - u.sacc = s + // count(*) to get number of records in source table + nrow, err := runCountStar(proc, s) + if err != nil { + return err + } + s.Nrow = nrow + + u.sacc = s + + } + //t1 := time.Now() go func() { // get the statistic of search string ([]Pattern) and store in SearchAccum - _, err = runWordStats(u, proc, u.sacc) + _, err := runWordStats(u, proc, u.sacc) if err != nil { u.errors <- err return } }() + // get batch from SQL executor + sql_closed := false + for !sql_closed { + sql_closed, err = groupby(u, proc, u.sacc) + if err != nil { + return err + } + } + + /* + t2 := time.Now() + diff := t2.Sub(t1) + os.Stderr.WriteString(fmt.Sprintf("FULLTEXT: diff %v\n", diff)) + os.Stderr.WriteString(u.mpool.String()) + */ return nil } diff --git a/pkg/sql/colexec/table_function/fulltext_test.go b/pkg/sql/colexec/table_function/fulltext_test.go index ade2b6348315f..a0081e3d04785 100644 --- a/pkg/sql/colexec/table_function/fulltext_test.go +++ b/pkg/sql/colexec/table_function/fulltext_test.go @@ -105,7 +105,7 @@ func fake_runSql(proc *process.Process, sql string) (executor.Result, error) { return executor.Result{Mp: proc.Mp(), Batches: []*batch.Batch{makeCountBatchFT(proc)}}, nil } -func fake_runSql_streaming(proc *process.Process, sql string, ch chan executor.Result) (executor.Result, error) { +func fake_runSql_streaming(proc *process.Process, sql string, ch chan executor.Result, err_chan chan error) (executor.Result, error) { defer close(ch) res := executor.Result{Mp: proc.Mp(), Batches: []*batch.Batch{makeTextBatchFT(proc)}} @@ -349,23 +349,19 @@ func makeCountBatchFT(proc *process.Process) *batch.Batch { return bat } -// create (doc_id, pos, text) +// create (doc_id, text) func makeTextBatchFT(proc *process.Process) *batch.Batch { - bat := batch.NewWithSize(3) - bat.Vecs[0] = vector.NewVec(types.New(types.T_int32, 4, 0)) // doc_id - bat.Vecs[1] = vector.NewVec(types.New(types.T_int32, 4, 0)) // pos - bat.Vecs[2] = vector.NewVec(types.New(types.T_varchar, 256, 0)) // text + bat := batch.NewWithSize(2) + bat.Vecs[0] = vector.NewVec(types.New(types.T_int32, 4, 0)) // doc_id + bat.Vecs[1] = vector.NewVec(types.New(types.T_int32, 4, 0)) // word index nitem := 8192*3 + 1 for i := 0; i < nitem; i++ { // doc_id vector.AppendFixed[int32](bat.Vecs[0], int32(i), false, proc.Mp()) - // pos - vector.AppendFixed[int32](bat.Vecs[1], int32(i+1), false, proc.Mp()) - - // word - vector.AppendBytes(bat.Vecs[2], []byte("pattern"), false, proc.Mp()) + // word index + vector.AppendFixed[int32](bat.Vecs[1], int32(0), false, proc.Mp()) } bat.SetRowCount(nitem) diff --git a/pkg/sql/compile/sql_executor.go b/pkg/sql/compile/sql_executor.go index d24f6d247f51c..c88bf3f483fe8 100644 --- a/pkg/sql/compile/sql_executor.go +++ b/pkg/sql/compile/sql_executor.go @@ -336,7 +336,7 @@ func (exec *txnExecutor) Exec( result := executor.NewResult(exec.s.mp) - stream_chan, streaming := exec.opts.Streaming() + stream_chan, err_chan, streaming := exec.opts.Streaming() if streaming { defer close(stream_chan) } @@ -359,6 +359,7 @@ func (exec *txnExecutor) Exec( for len(stream_chan) == cap(stream_chan) { select { case <-proc.Ctx.Done(): + err_chan <- moerr.NewInternalError(proc.Ctx, "context cancelled") return moerr.NewInternalError(proc.Ctx, "context cancelled") default: time.Sleep(1 * time.Millisecond) @@ -374,11 +375,17 @@ func (exec *txnExecutor) Exec( }) if err != nil { + if streaming { + err_chan <- err + } return executor.Result{}, err } var runResult *util.RunResult runResult, err = c.Run(0) if err != nil { + if streaming { + err_chan <- err + } for _, bat := range batches { if bat != nil { bat.Clean(exec.s.mp) diff --git a/pkg/sql/plan/apply_indices_fulltext.go b/pkg/sql/plan/apply_indices_fulltext.go index 691071a5cf50d..b70172ae9478d 100644 --- a/pkg/sql/plan/apply_indices_fulltext.go +++ b/pkg/sql/plan/apply_indices_fulltext.go @@ -236,8 +236,10 @@ func (builder *QueryBuilder) applyJoinFullTextIndices(nodeID int32, projNode *pl fulltext_func := tree.NewCStr(fulltext_index_scan_func_name, 1) alias_name := fmt.Sprintf("mo_fulltext_alias_%d", i) + params := idxdef.IndexAlgoParams var exprs tree.Exprs + exprs = append(exprs, tree.NewNumVal[string](params, params, false, tree.P_char)) exprs = append(exprs, tree.NewNumVal[string](srctblname, srctblname, false, tree.P_char)) exprs = append(exprs, tree.NewNumVal[string](idxtblname, idxtblname, false, tree.P_char)) exprs = append(exprs, tree.NewNumVal[string](pattern, pattern, false, tree.P_char)) diff --git a/pkg/sql/plan/build_ddl.go b/pkg/sql/plan/build_ddl.go index 47db278ee3f2c..c8ddda8da511c 100644 --- a/pkg/sql/plan/build_ddl.go +++ b/pkg/sql/plan/build_ddl.go @@ -1602,6 +1602,7 @@ func getRefAction(typ tree.ReferenceOptionType) plan.ForeignKeyDef_RefAction { } } +// buildFullTextIndexTable create a secondary table with schema (doc_id, word, pos) cluster by (word) func buildFullTextIndexTable(createTable *plan.CreateTable, indexInfos []*tree.FullTextIndex, colMap map[string]*ColDef, pkeyName string, ctx CompilerContext) error { if pkeyName == "" || pkeyName == catalog.FakePrimaryKeyColName { return moerr.NewInternalErrorNoCtx("primary key cannot be empty for fulltext index") @@ -1753,6 +1754,10 @@ func buildFullTextIndexTable(createTable *plan.CreateTable, indexInfos []*tree.F PkeyColName: keyName, } + tableDef.ClusterBy = &ClusterByDef{ + Name: "word", + } + properties := []*plan.Property{ { Key: catalog.SystemRelAttr_Kind, diff --git a/pkg/sql/plan/explain/explain_node.go b/pkg/sql/plan/explain/explain_node.go index 605ac7d0069f8..59336b89a0995 100644 --- a/pkg/sql/plan/explain/explain_node.go +++ b/pkg/sql/plan/explain/explain_node.go @@ -448,9 +448,26 @@ func (ndesc *NodeDescribeImpl) GetExtraInfo(ctx context.Context, options *Explai } } + if ndesc.Node.NodeType == plan.Node_FUNCTION_SCAN { + msg, err := ndesc.GetFullTextSql(ctx, options) + if err != nil { + return nil, err + } + if len(msg) > 0 { + lines = append(lines, msg) + } + } return lines, nil } +func (ndesc *NodeDescribeImpl) GetFullTextSql(ctx context.Context, options *ExplainOptions) (string, error) { + if options.Verbose && len(ndesc.Node.GetStats().Sql) > 0 { + result := "Sql: " + ndesc.Node.GetStats().Sql + return result, nil + } + return "", nil +} + func (ndesc *NodeDescribeImpl) GetProjectListInfo(ctx context.Context, options *ExplainOptions) (string, error) { buf := bytes.NewBuffer(make([]byte, 0, 400)) buf.WriteString("Output: ") diff --git a/pkg/sql/plan/explain/fultext_test.go b/pkg/sql/plan/explain/fultext_test.go new file mode 100644 index 0000000000000..6c2a7f23f64be --- /dev/null +++ b/pkg/sql/plan/explain/fultext_test.go @@ -0,0 +1,35 @@ +// Copyright 2021 - 2022 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 explain + +import ( + "context" + "testing" + + "github.com/matrixorigin/matrixone/pkg/pb/plan" + "github.com/stretchr/testify/require" +) + +func TestFullTextSql(t *testing.T) { + node := &plan.Node{ + NodeType: plan.Node_FUNCTION_SCAN, + Stats: &plan.Stats{Sql: "SELECT * FROM TABLE"}, + } + nodedesc := &NodeDescribeImpl{Node: node} + lines, err := nodedesc.GetExtraInfo(context.TODO(), &ExplainOptions{Verbose: true}) + require.Nil(t, err) + require.Equal(t, 1, len(lines)) + require.Equal(t, "Sql: SELECT * FROM TABLE", lines[0]) +} diff --git a/pkg/sql/plan/fulltext.go b/pkg/sql/plan/fulltext.go index f6374c1439dcb..4c97997f4b113 100644 --- a/pkg/sql/plan/fulltext.go +++ b/pkg/sql/plan/fulltext.go @@ -15,8 +15,11 @@ package plan import ( + "encoding/json" + "github.com/matrixorigin/matrixone/pkg/common/moerr" "github.com/matrixorigin/matrixone/pkg/container/types" + "github.com/matrixorigin/matrixone/pkg/fulltext" "github.com/matrixorigin/matrixone/pkg/pb/plan" "github.com/matrixorigin/matrixone/pkg/sql/parsers/tree" ) @@ -72,24 +75,37 @@ var ( } ) -// arg list [source_table_name, index_table_name, search_against, mode] +// arg list [param, source_table_name, index_table_name, search_against, mode] func (builder *QueryBuilder) buildFullTextIndexScan(tbl *tree.TableFunction, ctx *BindContext, exprs []*plan.Expr, childId int32) (int32, error) { - if len(exprs) != 4 { - return 0, moerr.NewInvalidInput(builder.GetContext(), "Invalid number of arguments (NARGS != 4).") + if len(exprs) != 5 { + return 0, moerr.NewInvalidInput(builder.GetContext(), "Invalid number of arguments (NARGS != 5).") } colDefs := _getColDefs(ftIndexColdefs) + params, err := builder.getFullTextParams(tbl.Func) + if err != nil { + return 0, err + } + // remove the first argment and put the first argument to Param + exprs = exprs[1:] + + // get the generated SQL + sql, err := builder.getFullTextSql(tbl.Func, params) + if err != nil { + return 0, err + } + node := &plan.Node{ NodeType: plan.Node_FUNCTION_SCAN, - Stats: &plan.Stats{}, + Stats: &plan.Stats{Sql: sql}, TableDef: &plan.TableDef{ TableType: "func_table", //test if ok //Name: tbl.String(), TblFunc: &plan.TableFunction{ Name: fulltext_index_scan_func_name, - Param: []byte(""), + Param: []byte(params), }, Cols: colDefs, }, @@ -100,6 +116,42 @@ func (builder *QueryBuilder) buildFullTextIndexScan(tbl *tree.TableFunction, ctx return builder.appendNode(node, ctx), nil } +func (builder *QueryBuilder) getFullTextSql(fn *tree.FuncExpr, params string) (string, error) { + var param fulltext.FullTextParserParam + if len(params) > 0 { + err := json.Unmarshal([]byte(params), ¶m) + if err != nil { + return "", err + } + } + + if _, ok := fn.Exprs[2].(*tree.NumVal); !ok { + return "", moerr.NewInvalidInput(builder.GetContext(), "index table name is not a constant") + } + + idxtbl := fn.Exprs[2].String() + + if _, ok := fn.Exprs[3].(*tree.NumVal); !ok { + return "", moerr.NewInvalidInput(builder.GetContext(), "pattern is not a constant") + } + + pattern := fn.Exprs[3].String() + modeVal, ok := fn.Exprs[4].(*tree.NumVal) + if !ok { + return "", moerr.NewInvalidInput(builder.GetContext(), "mode is not a constant") + } + mode, ok := modeVal.Int64() + if !ok { + return "", moerr.NewInvalidInput(builder.GetContext(), "mode is not an integer") + } + + ps, err := fulltext.ParsePattern(pattern, mode) + if err != nil { + return "", err + } + return fulltext.PatternToSql(ps, mode, idxtbl, param.Parser) +} + // select * from index_table, fulltext_index_tokenize(doc_id, concat(body, ' ', title)) // arg list [params, doc_id, content] func (builder *QueryBuilder) buildFullTextIndexTokenize(tbl *tree.TableFunction, ctx *BindContext, exprs []*plan.Expr, childId int32) (int32, error) { diff --git a/pkg/sql/plan/function/func_fulltext.go b/pkg/sql/plan/function/func_fulltext.go index d3b7b41c1ec3d..68611e0c8b14a 100644 --- a/pkg/sql/plan/function/func_fulltext.go +++ b/pkg/sql/plan/function/func_fulltext.go @@ -20,10 +20,12 @@ import ( "github.com/matrixorigin/matrixone/pkg/vm/process" ) +// return true or false for filter. arg = [search_string, mode, key1, key2, ..., KeyN] func fullTextMatch(ivecs []*vector.Vector, result vector.FunctionResultWrapper, proc *process.Process, length int, selectList *FunctionSelectList) error { return moerr.NewNotSupported(proc.Ctx, "MATCH() AGAINST() function cannot be replaced by FULLTEXT INDEX and full table scan with fulltext search is not supported yet.") } +// return score. arg = [search_string, mode, key1, key2, ..., KeyN] func fullTextMatchScore(ivecs []*vector.Vector, result vector.FunctionResultWrapper, proc *process.Process, length int, selectList *FunctionSelectList) error { return moerr.NewNotSupported(proc.Ctx, "MATCH() AGAINST() function cannot be replaced by FULLTEXT INDEX and full table scan with fulltext search is not supported yet.") } diff --git a/pkg/udf/pythonservice/client.go b/pkg/udf/pythonservice/client.go index f34b444b1b5f6..0abaae0e42bc6 100644 --- a/pkg/udf/pythonservice/client.go +++ b/pkg/udf/pythonservice/client.go @@ -47,7 +47,7 @@ func (c *Client) init() error { c.mutex.Lock() defer c.mutex.Unlock() if c.sc == nil { - conn, err := grpc.Dial(c.cfg.ServerAddress, + conn, err := grpc.NewClient(c.cfg.ServerAddress, grpc.WithTransportCredentials(insecure.NewCredentials()), grpc.WithDefaultCallOptions( grpc.MaxCallSendMsgSize(math.MaxInt32), diff --git a/pkg/util/executor/options.go b/pkg/util/executor/options.go index 5bfad40d7b9eb..2c601a6a5ec6f 100644 --- a/pkg/util/executor/options.go +++ b/pkg/util/executor/options.go @@ -222,12 +222,13 @@ func (opts Options) LowerCaseTableNames() int64 { return 1 } -func (opts Options) WithStreaming(stream_chan chan Result) Options { +func (opts Options) WithStreaming(stream_chan chan Result, error_chan chan error) Options { opts.stream_chan = stream_chan + opts.error_chan = error_chan opts.streaming = true return opts } -func (opts Options) Streaming() (chan Result, bool) { - return opts.stream_chan, opts.streaming +func (opts Options) Streaming() (chan Result, chan error, bool) { + return opts.stream_chan, opts.error_chan, opts.streaming } diff --git a/pkg/util/executor/options_test.go b/pkg/util/executor/options_test.go index 849bcab0e4dd5..606c7412fc957 100644 --- a/pkg/util/executor/options_test.go +++ b/pkg/util/executor/options_test.go @@ -23,9 +23,11 @@ import ( func TestOptionsStreaming(t *testing.T) { var opts Options var ch chan Result + var errors chan error - opts = opts.WithStreaming(ch) - ret, streaming := opts.Streaming() + opts = opts.WithStreaming(ch, errors) + ret, err_chan, streaming := opts.Streaming() require.True(t, ret == ch) require.True(t, streaming) + require.True(t, err_chan == errors) } diff --git a/pkg/util/executor/types.go b/pkg/util/executor/types.go index cb702ed277484..09709fb4b77ef 100644 --- a/pkg/util/executor/types.go +++ b/pkg/util/executor/types.go @@ -64,6 +64,7 @@ type Options struct { lower *int64 streaming bool stream_chan chan Result + error_chan chan error } // StatementOption statement execute option. diff --git a/proto/plan.proto b/proto/plan.proto index f0766c75ce0dc..9a073571e5d8c 100644 --- a/proto/plan.proto +++ b/proto/plan.proto @@ -532,6 +532,7 @@ message Stats { bool forceOneCN = 7; HashMapStats hashmapStats = 8; + string sql = 9; } message RowsetExpr { diff --git a/test/distributed/cases/ddl/table_kind.result b/test/distributed/cases/ddl/table_kind.result index 7f5cd3615f7e7..2766b82773d32 100644 --- a/test/distributed/cases/ddl/table_kind.result +++ b/test/distributed/cases/ddl/table_kind.result @@ -237,10 +237,7 @@ id body title 3 blue is not red colorful select *, match(body, title) against('is red' in natural language mode) as score from src1; id body title score -0 color is red t1 0.74115014 -3 blue is not red colorful 0.74115014 -1 car is yellow crazy car 0.19301322 -2 sky is blue no limit 0.19301322 +0 color is red t1 2.1689975 select * from src1 where match(body, title) against('教學指引'); id body title 6 各個單元主題內容涵蓋中華文化及生活應用的介紹。本套教材含課本、教學指引、生字卡、學生作業本與CD,中英對照,精美大字版。本系列有繁體字及簡體字兩種版本印行。 中文短篇小說 @@ -284,10 +281,7 @@ id body title 3 blue is not red colorful select *, match(body, title) against('is red' in natural language mode) as score from src2; id body title score -0 color is red t1 0.74115014 -3 blue is not red colorful 0.74115014 -1 car is yellow crazy car 0.19301322 -2 sky is blue no limit 0.19301322 +0 color is red t1 2.1689975 select * from src2 where match(body, title) against('教學指引'); id body title 6 各個單元主題內容涵蓋中華文化及生活應用的介紹。本套教材含課本、教學指引、生字卡、學生作業本與CD,中英對照,精美大字版。本系列有繁體字及簡體字兩種版本印行。 中文短篇小說\n @@ -303,7 +297,7 @@ id body title 4 遠東兒童中文是針對6到9歲的小朋友精心設計的中文學習教材,共三冊,目前已出版一、二冊。 遠東兒童中文 select *, match(body, title) against('遠東兒童中文' in natural language mode) as score from src2; id body title score -4 遠東兒童中文是針對6到9歲的小朋友精心設計的中文學習教材,共三冊,目前已出版一、二冊。 遠東兒童中文 5.4813685 +4 遠東兒童中文是針對6到9歲的小朋友精心設計的中文學習教材,共三冊,目前已出版一、二冊。 遠東兒童中文 6.577643 5 每冊均採用近百張全幅彩圖及照片,生動活潑、自然真實,加深兒童學習印象,洋溢學習樂趣。 遠東兒童中文 3.2888215 drop table src2; DROP DATABASE IF EXISTS db1; diff --git a/test/distributed/cases/fulltext/fulltext.result b/test/distributed/cases/fulltext/fulltext.result index d257ebb800c16..23b56545414cf 100644 --- a/test/distributed/cases/fulltext/fulltext.result +++ b/test/distributed/cases/fulltext/fulltext.result @@ -20,10 +20,7 @@ id body title 3 blue is not red colorful select *, match(body, title) against('is red' in natural language mode) as score from src; id body title score -0 color is red t1 0.74115014 -3 blue is not red colorful 0.74115014 -1 car is yellow crazy car 0.19301322 -2 sky is blue no limit 0.19301322 +0 color is red t1 2.1689975 select * from src where match(body, title) against('教學指引'); id body title 6 各個單元主題內容涵蓋中華文化及生活應用的介紹。本套教材含課本、教學指引、生字卡、學生作業本與CD,中英對照,精美大字版。本系列有繁體字及簡體字兩種版本印行。 中文短篇小說 @@ -39,11 +36,11 @@ id body title 4 遠東兒童中文是針對6到9歲的小朋友精心設計的中文學習教材,共三冊,目前已出版一、二冊。 遠東兒童中文 select *, match(body, title) against('遠東兒童中文' in natural language mode) as score from src; id body title score -4 遠東兒童中文是針對6到9歲的小朋友精心設計的中文學習教材,共三冊,目前已出版一、二冊。 遠東兒童中文 5.4813685 +4 遠東兒童中文是針對6到9歲的小朋友精心設計的中文學習教材,共三冊,目前已出版一、二冊。 遠東兒童中文 6.577643 5 每冊均採用近百張全幅彩圖及照片,生動活潑、自然真實,加深兒童學習印象,洋溢學習樂趣。 遠東兒童中文 3.2888215 select *, match(body) against('遠東兒童中文' in natural language mode) as score from src; id body title score -4 遠東兒童中文是針對6到9歲的小朋友精心設計的中文學習教材,共三冊,目前已出版一、二冊。 遠東兒童中文 4.337995 +4 遠東兒童中文是針對6到9歲的小朋友精心設計的中文學習教材,共三冊,目前已出版一、二冊。 遠東兒童中文 6.506993 select * from src where match(body, title) against('+red blue' in boolean mode); id body title 3 blue is not red colorful @@ -75,6 +72,11 @@ select * from src where match(body, title) against('"red"' in boolean mode); id body title 0 color is red t1 3 blue is not red colorful +select * from src where match(body, title) against('+教學指引 +短篇小說' in boolean mode); +id body title +6 各個單元主題內容涵蓋中華文化及生活應用的介紹。本套教材含課本、教學指引、生字卡、學生作業本與CD,中英對照,精美大字版。本系列有繁體字及簡體字兩種版本印行。 中文短篇小說 +select * from src where match(body, title) against('+教學指引 -短篇小說' in boolean mode); +id body title select * from src where match(body, title) against('"is not red"' in boolean mode); id body title select * from src where match(body, title) against('"blue is red"' in boolean mode); @@ -124,10 +126,7 @@ id body title 3 blue is not red colorful select *, match(body, title) against('is red' in natural language mode) as score from src; id body title score -0 color is red t1 0.74115014 -3 blue is not red colorful 0.74115014 -1 car is yellow crazy car 0.19301322 -2 sky is blue no limit 0.19301322 +0 color is red t1 2.1689975 select * from src where match(body, title) against('教學指引'); id body title 6 各個單元主題內容涵蓋中華文化及生活應用的介紹。本套教材含課本、教學指引、生字卡、學生作業本與CD,中英對照,精美大字版。本系列有繁體字及簡體字兩種版本印行。 中文短>篇小說 @@ -143,11 +142,11 @@ id body title 4 遠東兒童中文是針對6到9歲的小朋友精心設計的中文學習教材,共三冊,目前已出版一、二冊。 遠東兒童中文 select *, match(body, title) against('遠東兒童中文' in natural language mode) as score from src; id body title score -4 遠東兒童中文是針對6到9歲的小朋友精心設計的中文學習教材,共三冊,目前已出版一、二冊。 遠東兒童中文 5.4813685 +4 遠東兒童中文是針對6到9歲的小朋友精心設計的中文學習教材,共三冊,目前已出版一、二冊。 遠東兒童中文 6.577643 5 每冊均採用近百張全幅彩圖及照片,生動活潑、自然真實,加深兒童學習印象,洋溢學習樂趣。 遠東兒童中文 3.2888215 select *, match(body) against('遠東兒童中文' in natural language mode) as score from src; id body title score -4 遠東兒童中文是針對6到9歲的小朋友精心設計的中文學習教材,共三冊,目前已出版一、二冊。 遠東兒童中文 4.337995 +4 遠東兒童中文是針對6到9歲的小朋友精心設計的中文學習教材,共三冊,目前已出版一、二冊。 遠東兒童中文 6.506993 select * from src where match(body, title) against('+red blue' in boolean mode); id body title 3 blue is not red colorful @@ -303,10 +302,7 @@ id body title 3 blue is not red colorful select *, match(body, title) against('is red' in natural language mode) as score from src; id body title score -0 color is red t1 0.74115014 -3 blue is not red colorful 0.74115014 -1 car is yellow crazy car 0.19301322 -2 sky is blue no limit 0.19301322 +0 color is red t1 2.1689975 select * from src where match(body, title) against('教學指引'); id body title 6 各個單元主題內容涵蓋中華文化及生活應用的介紹。本套教材含課本、教學指引、生字卡、學生作業本與CD,中英對照,精美大字版。本系列有繁體字及簡體字兩種版本印行。 中文短篇小說\n @@ -322,7 +318,7 @@ id body title 4 遠東兒童中文是針對6到9歲的小朋友精心設計的中文學習教材,共三冊,目前已出版一、二冊。 遠東兒童中文 select *, match(body, title) against('遠東兒童中文' in natural language mode) as score from src; id body title score -4 遠東兒童中文是針對6到9歲的小朋友精心設計的中文學習教材,共三冊,目前已出版一、二冊。 遠東兒童中文 5.4813685 +4 遠東兒童中文是針對6到9歲的小朋友精心設計的中文學習教材,共三冊,目前已出版一、二冊。 遠東兒童中文 6.577643 5 每冊均採用近百張全幅彩圖及照片,生動活潑、自然真實,加深兒童學習印象,洋溢學習樂趣。 遠東兒童中文 3.2888215 select * from src where match(body, title) against('+red blue' in boolean mode); id body title diff --git a/test/distributed/cases/fulltext/fulltext.sql b/test/distributed/cases/fulltext/fulltext.sql index 1ff41d3d5c4c8..72ee21b4dd1b9 100644 --- a/test/distributed/cases/fulltext/fulltext.sql +++ b/test/distributed/cases/fulltext/fulltext.sql @@ -59,6 +59,10 @@ select * from src where match(body, title) against('"is not red"' in boolean mod select * from src where match(body, title) against('"red"' in boolean mode); +-- boolean mode in Chinese +select * from src where match(body, title) against('+教學指引 +短篇小說' in boolean mode); +select * from src where match(body, title) against('+教學指引 -短篇小說' in boolean mode); + -- phrase exact match. double space cannot be matched and empty result select * from src where match(body, title) against('"is not red"' in boolean mode); diff --git a/test/distributed/cases/fulltext/fulltext1.result b/test/distributed/cases/fulltext/fulltext1.result index c47facc0f731a..3557fd614cf0d 100644 --- a/test/distributed/cases/fulltext/fulltext1.result +++ b/test/distributed/cases/fulltext/fulltext1.result @@ -150,4 +150,4 @@ Project Filter Cond: prefix_eq(#[0,0]) Block Filter Cond: prefix_eq(#[0,0]) drop table t1; -drop database test; \ No newline at end of file +drop database test; diff --git a/test/distributed/cases/fulltext/fulltext2.result b/test/distributed/cases/fulltext/fulltext2.result index 8160de47cede0..d930aa3e358e0 100644 --- a/test/distributed/cases/fulltext/fulltext2.result +++ b/test/distributed/cases/fulltext/fulltext2.result @@ -396,27 +396,17 @@ id score 1 0.2276447 3 0.2276447 select id, body, match (title, body) -against ('Security implications of running MO as root' in natural language mode) as score +against ('MO tutorial DBMS stands for' in natural language mode) as score from articles; id body score -4 1. Never run MOd as root. 2. ... 1.2110387 -6 When configured properly, MO ... 0.60551935 -1 DBMS stands for DataBase ... 0.0 -2 After you went through a ... 0.0 -3 In this tutorial, we show ... 0.0 -5 In the following database comparison ... 0.0 +1 DBMS stands for DataBase ... 3.0275967 select id, body, match (title, body) -against ('Security implications of running MO as root' in natural language mode) as score +against ('MO tutorial DBMS stands for' in natural language mode) as score from articles where match (title, body) -against ('Security implications of running MO as root' in natural language mode); +against ('MO tutorial DBMS stands for' in natural language mode); id body score -4 1. Never run MOd as root. 2. ... 1.2110387 -6 When configured properly, MO ... 0.60551935 -1 DBMS stands for DataBase ... 0.0 -2 After you went through a ... 0.0 -3 In this tutorial, we show ... 0.0 -5 In the following database comparison ... 0.0 +1 DBMS stands for DataBase ... 3.0275967 drop table articles; drop table if exists article; create table article ( @@ -445,13 +435,12 @@ id score 1 0.2276447 5 0.2276447 select id, body, match (body) -against ('Security implications of running MO as root' in natural language mode) as score +against ('DBMS stands for Database' in natural language mode) as score from article where match (body) -against ('Security implications of running MO as root' in natural language mode); +against ('DBMS stands for Database' in natural language mode); id body score -4 1. Never run MOd as root. 2. ... 1.2110387 -6 When configured properly, MO ... 0.60551935 +1 DBMS stands for DataBase ... 2.4220774 drop table article; drop table if exists example1; create table example1 ( diff --git a/test/distributed/cases/fulltext/fulltext2.sql b/test/distributed/cases/fulltext/fulltext2.sql index f3737c8d58f1f..09baeeb2b77c9 100644 --- a/test/distributed/cases/fulltext/fulltext2.sql +++ b/test/distributed/cases/fulltext/fulltext2.sql @@ -324,14 +324,14 @@ against ('Tutorial' in natural language mode) as score from articles; -- fulltext index query match multi and calculate score select id, body, match (title, body) -against ('Security implications of running MO as root' in natural language mode) as score +against ('MO tutorial DBMS stands for' in natural language mode) as score from articles; -- filter with where clause and select select id, body, match (title, body) -against ('Security implications of running MO as root' in natural language mode) as score +against ('MO tutorial DBMS stands for' in natural language mode) as score from articles where match (title, body) -against ('Security implications of running MO as root' in natural language mode); +against ('MO tutorial DBMS stands for' in natural language mode); drop table articles; @@ -362,10 +362,10 @@ against ('DataBase' in natural language mode) as score from article; -- filter with where clause and select select id, body, match (body) -against ('Security implications of running MO as root' in natural language mode) as score +against ('DBMS stands for Database' in natural language mode) as score from article where match (body) -against ('Security implications of running MO as root' in natural language mode); +against ('DBMS stands for Database' in natural language mode); drop table article; From 123bc8f7f49470636be1e479a8b081f67b6c150b Mon Sep 17 00:00:00 2001 From: LiuBo Date: Fri, 20 Dec 2024 13:15:13 +0800 Subject: [PATCH 08/26] [improvement] proxy: filter duplicate set stmts. (#20828) filter duplicate set stmts in proxy. Approved by: @zhangxu19830126 --- pkg/proxy/client_conn.go | 15 +++++++++++++- pkg/proxy/client_conn_test.go | 38 +++++++++++++++++++++++++++++++++++ 2 files changed, 52 insertions(+), 1 deletion(-) diff --git a/pkg/proxy/client_conn.go b/pkg/proxy/client_conn.go index da134e30d92b9..974be97e46411 100644 --- a/pkg/proxy/client_conn.go +++ b/pkg/proxy/client_conn.go @@ -107,7 +107,8 @@ type ClientConn interface { } type migration struct { - setVarStmts []string + setVarStmtMap map[string]struct{} + setVarStmts []string } // clientConn is the connection between proxy and client. @@ -231,6 +232,7 @@ func newClientConn( } c.tlsConfig = tlsConfig } + c.migration.setVarStmtMap = make(map[string]struct{}) return c, nil } @@ -408,6 +410,17 @@ func (c *clientConn) handleKill(e *killEvent, resp chan<- []byte) error { // handleSetVar handles the set variable event. func (c *clientConn) handleSetVar(e *setVarEvent) error { defer e.notify() + _, ok := c.migration.setVarStmtMap[e.stmt] + if ok { + for i := 0; i < len(c.migration.setVarStmts); i++ { + if c.migration.setVarStmts[i] == e.stmt { + c.migration.setVarStmts = append(c.migration.setVarStmts[:i], c.migration.setVarStmts[i+1:]...) + i-- + } + } + } else { + c.migration.setVarStmtMap[e.stmt] = struct{}{} + } c.migration.setVarStmts = append(c.migration.setVarStmts, e.stmt) return nil } diff --git a/pkg/proxy/client_conn_test.go b/pkg/proxy/client_conn_test.go index e37be86e89af9..21b79351dbce7 100644 --- a/pkg/proxy/client_conn_test.go +++ b/pkg/proxy/client_conn_test.go @@ -708,3 +708,41 @@ func Test_connectToBackend(t *testing.T) { require.Error(t, err) require.Nil(t, sConn) } + +func TestHandleSetVar(t *testing.T) { + defer leaktest.AfterTest(t)() + var cc clientConn + cc.migration.setVarStmtMap = make(map[string]struct{}) + e0 := &setVarEvent{ + baseEvent: baseEvent{waitC: make(chan struct{}, 5)}, + stmt: "set autocommit=0", + } + require.NoError(t, cc.handleSetVar(e0)) + require.Equal(t, 1, len(cc.migration.setVarStmtMap)) + require.Equal(t, 1, len(cc.migration.setVarStmts)) + require.Equal(t, e0.stmt, cc.migration.setVarStmts[len(cc.migration.setVarStmts)-1]) + + require.NoError(t, cc.handleSetVar(e0)) + require.Equal(t, 1, len(cc.migration.setVarStmtMap)) + require.Equal(t, 1, len(cc.migration.setVarStmts)) + require.Equal(t, e0.stmt, cc.migration.setVarStmts[len(cc.migration.setVarStmts)-1]) + + e1 := &setVarEvent{ + baseEvent: baseEvent{waitC: make(chan struct{}, 5)}, + stmt: "set autocommit=1", + } + require.NoError(t, cc.handleSetVar(e1)) + require.Equal(t, 2, len(cc.migration.setVarStmtMap)) + require.Equal(t, 2, len(cc.migration.setVarStmts)) + require.Equal(t, e1.stmt, cc.migration.setVarStmts[len(cc.migration.setVarStmts)-1]) + + require.NoError(t, cc.handleSetVar(e0)) + require.Equal(t, 2, len(cc.migration.setVarStmtMap)) + require.Equal(t, 2, len(cc.migration.setVarStmts)) + require.Equal(t, e0.stmt, cc.migration.setVarStmts[len(cc.migration.setVarStmts)-1]) + + require.NoError(t, cc.handleSetVar(e1)) + require.Equal(t, 2, len(cc.migration.setVarStmtMap)) + require.Equal(t, 2, len(cc.migration.setVarStmts)) + require.Equal(t, e1.stmt, cc.migration.setVarStmts[len(cc.migration.setVarStmts)-1]) +} From eeed8d961eae86be3fddb82282665986a6b9fc5f Mon Sep 17 00:00:00 2001 From: qingxinhome <70939751+qingxinhome@users.noreply.github.com> Date: Fri, 20 Dec 2024 14:13:30 +0800 Subject: [PATCH 09/26] Print index name when doing explain or explain analyze (#20696) Print index name when doing explain or explain analyze Approved by: @badboynt1, @daviszhen, @heni02, @ouyuanning, @aunjgr --- pkg/frontend/mysql_cmd_executor.go | 6 +- pkg/pb/plan/plan.pb.go | 2466 ++++++++++------- pkg/sql/plan/apply_indices.go | 67 +- pkg/sql/plan/deepcopy.go | 9 + pkg/sql/plan/explain/explain_node.go | 20 +- pkg/sql/plan/explain/types.go | 13 +- proto/plan.proto | 11 + .../cases/fulltext/fulltext1.result | 2 +- .../cases/optimizer/explain_index.result | 36 +- test/distributed/cases/optimizer/index.result | 6 +- test/distributed/cases/optimizer/like.result | 2 +- 11 files changed, 1583 insertions(+), 1055 deletions(-) diff --git a/pkg/frontend/mysql_cmd_executor.go b/pkg/frontend/mysql_cmd_executor.go index 1e27a7cf6a801..1c7e3c566f1ad 100644 --- a/pkg/frontend/mysql_cmd_executor.go +++ b/pkg/frontend/mysql_cmd_executor.go @@ -34,6 +34,9 @@ import ( "github.com/confluentinc/confluent-kafka-go/v2/kafka" "github.com/google/uuid" + "go.uber.org/zap" + "go.uber.org/zap/zapcore" + "github.com/matrixorigin/matrixone/pkg/catalog" "github.com/matrixorigin/matrixone/pkg/clusterservice" "github.com/matrixorigin/matrixone/pkg/common/moerr" @@ -71,8 +74,6 @@ import ( "github.com/matrixorigin/matrixone/pkg/vm/engine/disttae" "github.com/matrixorigin/matrixone/pkg/vm/engine/disttae/route" "github.com/matrixorigin/matrixone/pkg/vm/process" - "go.uber.org/zap" - "go.uber.org/zap/zapcore" ) func createDropDatabaseErrorInfo() string { @@ -1033,7 +1034,6 @@ func doExplainStmt(reqCtx context.Context, ses *Session, stmt *tree.ExplainStmt) if err != nil { return err } - es.CmpContext = ses.GetTxnCompileCtx() //get query optimizer and execute Optimize exPlan, err := buildPlan(reqCtx, ses, ses.GetTxnCompileCtx(), stmt.Statement) diff --git a/pkg/pb/plan/plan.pb.go b/pkg/pb/plan/plan.pb.go index 2e590ea414591..8881e339364f7 100644 --- a/pkg/pb/plan/plan.pb.go +++ b/pkg/pb/plan/plan.pb.go @@ -360,7 +360,7 @@ func (x Function_FuncFlag) String() string { } func (Function_FuncFlag) EnumDescriptor() ([]byte, []int) { - return fileDescriptor_2d655ab2f7683c23, []int{15, 0} + return fileDescriptor_2d655ab2f7683c23, []int{16, 0} } type ForeignKeyDef_RefAction int32 @@ -394,7 +394,7 @@ func (x ForeignKeyDef_RefAction) String() string { } func (ForeignKeyDef_RefAction) EnumDescriptor() ([]byte, []int) { - return fileDescriptor_2d655ab2f7683c23, []int{28, 0} + return fileDescriptor_2d655ab2f7683c23, []int{29, 0} } type OrderBySpec_OrderByFlag int32 @@ -431,7 +431,7 @@ func (x OrderBySpec_OrderByFlag) String() string { } func (OrderBySpec_OrderByFlag) EnumDescriptor() ([]byte, []int) { - return fileDescriptor_2d655ab2f7683c23, []int{46, 0} + return fileDescriptor_2d655ab2f7683c23, []int{47, 0} } type FrameClause_FrameType int32 @@ -456,7 +456,7 @@ func (x FrameClause_FrameType) String() string { } func (FrameClause_FrameType) EnumDescriptor() ([]byte, []int) { - return fileDescriptor_2d655ab2f7683c23, []int{49, 0} + return fileDescriptor_2d655ab2f7683c23, []int{50, 0} } type FrameBound_BoundType int32 @@ -484,7 +484,7 @@ func (x FrameBound_BoundType) String() string { } func (FrameBound_BoundType) EnumDescriptor() ([]byte, []int) { - return fileDescriptor_2d655ab2f7683c23, []int{50, 0} + return fileDescriptor_2d655ab2f7683c23, []int{51, 0} } type Node_NodeType int32 @@ -652,7 +652,7 @@ func (x Node_NodeType) String() string { } func (Node_NodeType) EnumDescriptor() ([]byte, []int) { - return fileDescriptor_2d655ab2f7683c23, []int{59, 0} + return fileDescriptor_2d655ab2f7683c23, []int{60, 0} } type Node_JoinType int32 @@ -704,7 +704,7 @@ func (x Node_JoinType) String() string { } func (Node_JoinType) EnumDescriptor() ([]byte, []int) { - return fileDescriptor_2d655ab2f7683c23, []int{59, 1} + return fileDescriptor_2d655ab2f7683c23, []int{60, 1} } type Node_AggMode int32 @@ -732,7 +732,7 @@ func (x Node_AggMode) String() string { } func (Node_AggMode) EnumDescriptor() ([]byte, []int) { - return fileDescriptor_2d655ab2f7683c23, []int{59, 2} + return fileDescriptor_2d655ab2f7683c23, []int{60, 2} } type Node_FillType int32 @@ -769,7 +769,7 @@ func (x Node_FillType) String() string { } func (Node_FillType) EnumDescriptor() ([]byte, []int) { - return fileDescriptor_2d655ab2f7683c23, []int{59, 3} + return fileDescriptor_2d655ab2f7683c23, []int{60, 3} } type Node_OnDuplicateAction int32 @@ -797,7 +797,7 @@ func (x Node_OnDuplicateAction) String() string { } func (Node_OnDuplicateAction) EnumDescriptor() ([]byte, []int) { - return fileDescriptor_2d655ab2f7683c23, []int{59, 4} + return fileDescriptor_2d655ab2f7683c23, []int{60, 4} } type Node_ApplyType int32 @@ -822,7 +822,7 @@ func (x Node_ApplyType) String() string { } func (Node_ApplyType) EnumDescriptor() ([]byte, []int) { - return fileDescriptor_2d655ab2f7683c23, []int{59, 5} + return fileDescriptor_2d655ab2f7683c23, []int{60, 5} } type Query_StatementType int32 @@ -862,7 +862,7 @@ func (x Query_StatementType) String() string { } func (Query_StatementType) EnumDescriptor() ([]byte, []int) { - return fileDescriptor_2d655ab2f7683c23, []int{74, 0} + return fileDescriptor_2d655ab2f7683c23, []int{75, 0} } type TransationControl_TclType int32 @@ -890,7 +890,7 @@ func (x TransationControl_TclType) String() string { } func (TransationControl_TclType) EnumDescriptor() ([]byte, []int) { - return fileDescriptor_2d655ab2f7683c23, []int{75, 0} + return fileDescriptor_2d655ab2f7683c23, []int{76, 0} } type TransationBegin_TransationMode int32 @@ -918,7 +918,7 @@ func (x TransationBegin_TransationMode) String() string { } func (TransationBegin_TransationMode) EnumDescriptor() ([]byte, []int) { - return fileDescriptor_2d655ab2f7683c23, []int{76, 0} + return fileDescriptor_2d655ab2f7683c23, []int{77, 0} } type DataControl_DclType int32 @@ -967,7 +967,7 @@ func (x DataControl_DclType) String() string { } func (DataControl_DclType) EnumDescriptor() ([]byte, []int) { - return fileDescriptor_2d655ab2f7683c23, []int{81, 0} + return fileDescriptor_2d655ab2f7683c23, []int{82, 0} } type DataDefinition_DdlType int32 @@ -1094,7 +1094,7 @@ func (x DataDefinition_DdlType) String() string { } func (DataDefinition_DdlType) EnumDescriptor() ([]byte, []int) { - return fileDescriptor_2d655ab2f7683c23, []int{82, 0} + return fileDescriptor_2d655ab2f7683c23, []int{83, 0} } type AlterTableDrop_Typ int32 @@ -1128,7 +1128,7 @@ func (x AlterTableDrop_Typ) String() string { } func (AlterTableDrop_Typ) EnumDescriptor() ([]byte, []int) { - return fileDescriptor_2d655ab2f7683c23, []int{90, 0} + return fileDescriptor_2d655ab2f7683c23, []int{91, 0} } type AlterTable_AlgorithmType int32 @@ -1159,7 +1159,7 @@ func (x AlterTable_AlgorithmType) String() string { } func (AlterTable_AlgorithmType) EnumDescriptor() ([]byte, []int) { - return fileDescriptor_2d655ab2f7683c23, []int{102, 0} + return fileDescriptor_2d655ab2f7683c23, []int{103, 0} } type MetadataScanInfo_MetadataScanInfoType int32 @@ -1217,7 +1217,7 @@ func (x MetadataScanInfo_MetadataScanInfoType) String() string { } func (MetadataScanInfo_MetadataScanInfoType) EnumDescriptor() ([]byte, []int) { - return fileDescriptor_2d655ab2f7683c23, []int{125, 0} + return fileDescriptor_2d655ab2f7683c23, []int{126, 0} } type Type struct { @@ -2525,6 +2525,93 @@ func (m *SubscriptionMeta) GetTables() string { return "" } +type IndexScanInfo struct { + IsIndexScan bool `protobuf:"varint,1,opt,name=is_index_scan,json=isIndexScan,proto3" json:"is_index_scan,omitempty"` + IndexName string `protobuf:"bytes,2,opt,name=index_name,json=indexName,proto3" json:"index_name,omitempty"` + BelongToTable string `protobuf:"bytes,3,opt,name=belong_to_table,json=belongToTable,proto3" json:"belong_to_table,omitempty"` + Parts []string `protobuf:"bytes,4,rep,name=parts,proto3" json:"parts,omitempty"` + IsUnique bool `protobuf:"varint,5,opt,name=is_unique,json=isUnique,proto3" json:"is_unique,omitempty"` + IndexTableName string `protobuf:"bytes,6,opt,name=index_table_name,json=indexTableName,proto3" json:"index_table_name,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *IndexScanInfo) Reset() { *m = IndexScanInfo{} } +func (m *IndexScanInfo) String() string { return proto.CompactTextString(m) } +func (*IndexScanInfo) ProtoMessage() {} +func (*IndexScanInfo) Descriptor() ([]byte, []int) { + return fileDescriptor_2d655ab2f7683c23, []int{15} +} +func (m *IndexScanInfo) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *IndexScanInfo) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_IndexScanInfo.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 *IndexScanInfo) XXX_Merge(src proto.Message) { + xxx_messageInfo_IndexScanInfo.Merge(m, src) +} +func (m *IndexScanInfo) XXX_Size() int { + return m.ProtoSize() +} +func (m *IndexScanInfo) XXX_DiscardUnknown() { + xxx_messageInfo_IndexScanInfo.DiscardUnknown(m) +} + +var xxx_messageInfo_IndexScanInfo proto.InternalMessageInfo + +func (m *IndexScanInfo) GetIsIndexScan() bool { + if m != nil { + return m.IsIndexScan + } + return false +} + +func (m *IndexScanInfo) GetIndexName() string { + if m != nil { + return m.IndexName + } + return "" +} + +func (m *IndexScanInfo) GetBelongToTable() string { + if m != nil { + return m.BelongToTable + } + return "" +} + +func (m *IndexScanInfo) GetParts() []string { + if m != nil { + return m.Parts + } + return nil +} + +func (m *IndexScanInfo) GetIsUnique() bool { + if m != nil { + return m.IsUnique + } + return false +} + +func (m *IndexScanInfo) GetIndexTableName() string { + if m != nil { + return m.IndexTableName + } + return "" +} + type Function struct { Func *ObjectRef `protobuf:"bytes,1,opt,name=func,proto3" json:"func,omitempty"` Args []*Expr `protobuf:"bytes,2,rep,name=args,proto3" json:"args,omitempty"` @@ -2537,7 +2624,7 @@ func (m *Function) Reset() { *m = Function{} } func (m *Function) String() string { return proto.CompactTextString(m) } func (*Function) ProtoMessage() {} func (*Function) Descriptor() ([]byte, []int) { - return fileDescriptor_2d655ab2f7683c23, []int{15} + return fileDescriptor_2d655ab2f7683c23, []int{16} } func (m *Function) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2583,6 +2670,7 @@ func (m *Function) GetArgs() []*Expr { type Expr struct { Typ Type `protobuf:"bytes,1,opt,name=typ,proto3" json:"typ"` // Types that are valid to be assigned to Expr: + // // *Expr_Lit // *Expr_P // *Expr_V @@ -2610,7 +2698,7 @@ func (m *Expr) Reset() { *m = Expr{} } func (m *Expr) String() string { return proto.CompactTextString(m) } func (*Expr) ProtoMessage() {} func (*Expr) Descriptor() ([]byte, []int) { - return fileDescriptor_2d655ab2f7683c23, []int{16} + return fileDescriptor_2d655ab2f7683c23, []int{17} } func (m *Expr) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2869,7 +2957,7 @@ func (m *FoldVal) Reset() { *m = FoldVal{} } func (m *FoldVal) String() string { return proto.CompactTextString(m) } func (*FoldVal) ProtoMessage() {} func (*FoldVal) Descriptor() ([]byte, []int) { - return fileDescriptor_2d655ab2f7683c23, []int{17} + return fileDescriptor_2d655ab2f7683c23, []int{18} } func (m *FoldVal) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2931,7 +3019,7 @@ func (m *LiteralVec) Reset() { *m = LiteralVec{} } func (m *LiteralVec) String() string { return proto.CompactTextString(m) } func (*LiteralVec) ProtoMessage() {} func (*LiteralVec) Descriptor() ([]byte, []int) { - return fileDescriptor_2d655ab2f7683c23, []int{18} + return fileDescriptor_2d655ab2f7683c23, []int{19} } func (m *LiteralVec) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2985,7 +3073,7 @@ func (m *Decimal64) Reset() { *m = Decimal64{} } func (m *Decimal64) String() string { return proto.CompactTextString(m) } func (*Decimal64) ProtoMessage() {} func (*Decimal64) Descriptor() ([]byte, []int) { - return fileDescriptor_2d655ab2f7683c23, []int{19} + return fileDescriptor_2d655ab2f7683c23, []int{20} } func (m *Decimal64) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -3033,7 +3121,7 @@ func (m *Decimal128) Reset() { *m = Decimal128{} } func (m *Decimal128) String() string { return proto.CompactTextString(m) } func (*Decimal128) ProtoMessage() {} func (*Decimal128) Descriptor() ([]byte, []int) { - return fileDescriptor_2d655ab2f7683c23, []int{20} + return fileDescriptor_2d655ab2f7683c23, []int{21} } func (m *Decimal128) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -3087,7 +3175,7 @@ func (m *ResultColDef) Reset() { *m = ResultColDef{} } func (m *ResultColDef) String() string { return proto.CompactTextString(m) } func (*ResultColDef) ProtoMessage() {} func (*ResultColDef) Descriptor() ([]byte, []int) { - return fileDescriptor_2d655ab2f7683c23, []int{21} + return fileDescriptor_2d655ab2f7683c23, []int{22} } func (m *ResultColDef) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -3155,7 +3243,7 @@ func (m *ColDef) Reset() { *m = ColDef{} } func (m *ColDef) String() string { return proto.CompactTextString(m) } func (*ColDef) ProtoMessage() {} func (*ColDef) Descriptor() ([]byte, []int) { - return fileDescriptor_2d655ab2f7683c23, []int{22} + return fileDescriptor_2d655ab2f7683c23, []int{23} } func (m *ColDef) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -3331,7 +3419,7 @@ func (m *Default) Reset() { *m = Default{} } func (m *Default) String() string { return proto.CompactTextString(m) } func (*Default) ProtoMessage() {} func (*Default) Descriptor() ([]byte, []int) { - return fileDescriptor_2d655ab2f7683c23, []int{23} + return fileDescriptor_2d655ab2f7683c23, []int{24} } func (m *Default) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -3393,7 +3481,7 @@ func (m *OnUpdate) Reset() { *m = OnUpdate{} } func (m *OnUpdate) String() string { return proto.CompactTextString(m) } func (*OnUpdate) ProtoMessage() {} func (*OnUpdate) Descriptor() ([]byte, []int) { - return fileDescriptor_2d655ab2f7683c23, []int{24} + return fileDescriptor_2d655ab2f7683c23, []int{25} } func (m *OnUpdate) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -3449,7 +3537,7 @@ func (m *IndexOption) Reset() { *m = IndexOption{} } func (m *IndexOption) String() string { return proto.CompactTextString(m) } func (*IndexOption) ProtoMessage() {} func (*IndexOption) Descriptor() ([]byte, []int) { - return fileDescriptor_2d655ab2f7683c23, []int{25} + return fileDescriptor_2d655ab2f7683c23, []int{26} } func (m *IndexOption) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -3521,7 +3609,7 @@ func (m *PrimaryKeyDef) Reset() { *m = PrimaryKeyDef{} } func (m *PrimaryKeyDef) String() string { return proto.CompactTextString(m) } func (*PrimaryKeyDef) ProtoMessage() {} func (*PrimaryKeyDef) Descriptor() ([]byte, []int) { - return fileDescriptor_2d655ab2f7683c23, []int{26} + return fileDescriptor_2d655ab2f7683c23, []int{27} } func (m *PrimaryKeyDef) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -3617,7 +3705,7 @@ func (m *IndexDef) Reset() { *m = IndexDef{} } func (m *IndexDef) String() string { return proto.CompactTextString(m) } func (*IndexDef) ProtoMessage() {} func (*IndexDef) Descriptor() ([]byte, []int) { - return fileDescriptor_2d655ab2f7683c23, []int{27} + return fileDescriptor_2d655ab2f7683c23, []int{28} } func (m *IndexDef) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -3749,7 +3837,7 @@ func (m *ForeignKeyDef) Reset() { *m = ForeignKeyDef{} } func (m *ForeignKeyDef) String() string { return proto.CompactTextString(m) } func (*ForeignKeyDef) ProtoMessage() {} func (*ForeignKeyDef) Descriptor() ([]byte, []int) { - return fileDescriptor_2d655ab2f7683c23, []int{28} + return fileDescriptor_2d655ab2f7683c23, []int{29} } func (m *ForeignKeyDef) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -3833,7 +3921,7 @@ func (m *CheckDef) Reset() { *m = CheckDef{} } func (m *CheckDef) String() string { return proto.CompactTextString(m) } func (*CheckDef) ProtoMessage() {} func (*CheckDef) Descriptor() ([]byte, []int) { - return fileDescriptor_2d655ab2f7683c23, []int{29} + return fileDescriptor_2d655ab2f7683c23, []int{30} } func (m *CheckDef) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -3890,7 +3978,7 @@ func (m *ClusterByDef) Reset() { *m = ClusterByDef{} } func (m *ClusterByDef) String() string { return proto.CompactTextString(m) } func (*ClusterByDef) ProtoMessage() {} func (*ClusterByDef) Descriptor() ([]byte, []int) { - return fileDescriptor_2d655ab2f7683c23, []int{30} + return fileDescriptor_2d655ab2f7683c23, []int{31} } func (m *ClusterByDef) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -3945,7 +4033,7 @@ func (m *PropertyDef) Reset() { *m = PropertyDef{} } func (m *PropertyDef) String() string { return proto.CompactTextString(m) } func (*PropertyDef) ProtoMessage() {} func (*PropertyDef) Descriptor() ([]byte, []int) { - return fileDescriptor_2d655ab2f7683c23, []int{31} + return fileDescriptor_2d655ab2f7683c23, []int{32} } func (m *PropertyDef) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -4000,7 +4088,7 @@ func (m *Property) Reset() { *m = Property{} } func (m *Property) String() string { return proto.CompactTextString(m) } func (*Property) ProtoMessage() {} func (*Property) Descriptor() ([]byte, []int) { - return fileDescriptor_2d655ab2f7683c23, []int{32} + return fileDescriptor_2d655ab2f7683c23, []int{33} } func (m *Property) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -4054,7 +4142,7 @@ func (m *PropertiesDef) Reset() { *m = PropertiesDef{} } func (m *PropertiesDef) String() string { return proto.CompactTextString(m) } func (*PropertiesDef) ProtoMessage() {} func (*PropertiesDef) Descriptor() ([]byte, []int) { - return fileDescriptor_2d655ab2f7683c23, []int{33} + return fileDescriptor_2d655ab2f7683c23, []int{34} } func (m *PropertiesDef) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -4110,7 +4198,7 @@ func (m *PartitionByDef) Reset() { *m = PartitionByDef{} } func (m *PartitionByDef) String() string { return proto.CompactTextString(m) } func (*PartitionByDef) ProtoMessage() {} func (*PartitionByDef) Descriptor() ([]byte, []int) { - return fileDescriptor_2d655ab2f7683c23, []int{34} + return fileDescriptor_2d655ab2f7683c23, []int{35} } func (m *PartitionByDef) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -4221,7 +4309,7 @@ func (m *PartitionExpr) Reset() { *m = PartitionExpr{} } func (m *PartitionExpr) String() string { return proto.CompactTextString(m) } func (*PartitionExpr) ProtoMessage() {} func (*PartitionExpr) Descriptor() ([]byte, []int) { - return fileDescriptor_2d655ab2f7683c23, []int{35} + return fileDescriptor_2d655ab2f7683c23, []int{36} } func (m *PartitionExpr) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -4277,7 +4365,7 @@ func (m *PartitionColumns) Reset() { *m = PartitionColumns{} } func (m *PartitionColumns) String() string { return proto.CompactTextString(m) } func (*PartitionColumns) ProtoMessage() {} func (*PartitionColumns) Descriptor() ([]byte, []int) { - return fileDescriptor_2d655ab2f7683c23, []int{36} + return fileDescriptor_2d655ab2f7683c23, []int{37} } func (m *PartitionColumns) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -4337,7 +4425,7 @@ func (m *PartitionItem) Reset() { *m = PartitionItem{} } func (m *PartitionItem) String() string { return proto.CompactTextString(m) } func (*PartitionItem) ProtoMessage() {} func (*PartitionItem) Descriptor() ([]byte, []int) { - return fileDescriptor_2d655ab2f7683c23, []int{37} + return fileDescriptor_2d655ab2f7683c23, []int{38} } func (m *PartitionItem) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -4426,7 +4514,7 @@ func (m *ViewDef) Reset() { *m = ViewDef{} } func (m *ViewDef) String() string { return proto.CompactTextString(m) } func (*ViewDef) ProtoMessage() {} func (*ViewDef) Descriptor() ([]byte, []int) { - return fileDescriptor_2d655ab2f7683c23, []int{38} + return fileDescriptor_2d655ab2f7683c23, []int{39} } func (m *ViewDef) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -4500,7 +4588,7 @@ func (m *TableDef) Reset() { *m = TableDef{} } func (m *TableDef) String() string { return proto.CompactTextString(m) } func (*TableDef) ProtoMessage() {} func (*TableDef) Descriptor() ([]byte, []int) { - return fileDescriptor_2d655ab2f7683c23, []int{39} + return fileDescriptor_2d655ab2f7683c23, []int{40} } func (m *TableDef) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -4726,7 +4814,7 @@ func (m *TableDef_DefType) Reset() { *m = TableDef_DefType{} } func (m *TableDef_DefType) String() string { return proto.CompactTextString(m) } func (*TableDef_DefType) ProtoMessage() {} func (*TableDef_DefType) Descriptor() ([]byte, []int) { - return fileDescriptor_2d655ab2f7683c23, []int{39, 0} + return fileDescriptor_2d655ab2f7683c23, []int{40, 0} } func (m *TableDef_DefType) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -4800,7 +4888,7 @@ func (m *TableFunction) Reset() { *m = TableFunction{} } func (m *TableFunction) String() string { return proto.CompactTextString(m) } func (*TableFunction) ProtoMessage() {} func (*TableFunction) Descriptor() ([]byte, []int) { - return fileDescriptor_2d655ab2f7683c23, []int{40} + return fileDescriptor_2d655ab2f7683c23, []int{41} } func (m *TableFunction) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -4869,7 +4957,7 @@ func (m *HashMapStats) Reset() { *m = HashMapStats{} } func (m *HashMapStats) String() string { return proto.CompactTextString(m) } func (*HashMapStats) ProtoMessage() {} func (*HashMapStats) Descriptor() ([]byte, []int) { - return fileDescriptor_2d655ab2f7683c23, []int{41} + return fileDescriptor_2d655ab2f7683c23, []int{42} } func (m *HashMapStats) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -5001,7 +5089,7 @@ func (m *Stats) Reset() { *m = Stats{} } func (m *Stats) String() string { return proto.CompactTextString(m) } func (*Stats) ProtoMessage() {} func (*Stats) Descriptor() ([]byte, []int) { - return fileDescriptor_2d655ab2f7683c23, []int{42} + return fileDescriptor_2d655ab2f7683c23, []int{43} } func (m *Stats) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -5105,7 +5193,7 @@ func (m *RowsetExpr) Reset() { *m = RowsetExpr{} } func (m *RowsetExpr) String() string { return proto.CompactTextString(m) } func (*RowsetExpr) ProtoMessage() {} func (*RowsetExpr) Descriptor() ([]byte, []int) { - return fileDescriptor_2d655ab2f7683c23, []int{43} + return fileDescriptor_2d655ab2f7683c23, []int{44} } func (m *RowsetExpr) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -5159,7 +5247,7 @@ func (m *ColData) Reset() { *m = ColData{} } func (m *ColData) String() string { return proto.CompactTextString(m) } func (*ColData) ProtoMessage() {} func (*ColData) Descriptor() ([]byte, []int) { - return fileDescriptor_2d655ab2f7683c23, []int{44} + return fileDescriptor_2d655ab2f7683c23, []int{45} } func (m *ColData) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -5207,7 +5295,7 @@ func (m *RowsetData) Reset() { *m = RowsetData{} } func (m *RowsetData) String() string { return proto.CompactTextString(m) } func (*RowsetData) ProtoMessage() {} func (*RowsetData) Descriptor() ([]byte, []int) { - return fileDescriptor_2d655ab2f7683c23, []int{45} + return fileDescriptor_2d655ab2f7683c23, []int{46} } func (m *RowsetData) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -5263,7 +5351,7 @@ func (m *OrderBySpec) Reset() { *m = OrderBySpec{} } func (m *OrderBySpec) String() string { return proto.CompactTextString(m) } func (*OrderBySpec) ProtoMessage() {} func (*OrderBySpec) Descriptor() ([]byte, []int) { - return fileDescriptor_2d655ab2f7683c23, []int{46} + return fileDescriptor_2d655ab2f7683c23, []int{47} } func (m *OrderBySpec) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -5328,7 +5416,7 @@ func (m *WindowSpec) Reset() { *m = WindowSpec{} } func (m *WindowSpec) String() string { return proto.CompactTextString(m) } func (*WindowSpec) ProtoMessage() {} func (*WindowSpec) Descriptor() ([]byte, []int) { - return fileDescriptor_2d655ab2f7683c23, []int{47} + return fileDescriptor_2d655ab2f7683c23, []int{48} } func (m *WindowSpec) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -5405,7 +5493,7 @@ func (m *SampleFuncSpec) Reset() { *m = SampleFuncSpec{} } func (m *SampleFuncSpec) String() string { return proto.CompactTextString(m) } func (*SampleFuncSpec) ProtoMessage() {} func (*SampleFuncSpec) Descriptor() ([]byte, []int) { - return fileDescriptor_2d655ab2f7683c23, []int{48} + return fileDescriptor_2d655ab2f7683c23, []int{49} } func (m *SampleFuncSpec) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -5468,7 +5556,7 @@ func (m *FrameClause) Reset() { *m = FrameClause{} } func (m *FrameClause) String() string { return proto.CompactTextString(m) } func (*FrameClause) ProtoMessage() {} func (*FrameClause) Descriptor() ([]byte, []int) { - return fileDescriptor_2d655ab2f7683c23, []int{49} + return fileDescriptor_2d655ab2f7683c23, []int{50} } func (m *FrameClause) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -5531,7 +5619,7 @@ func (m *FrameBound) Reset() { *m = FrameBound{} } func (m *FrameBound) String() string { return proto.CompactTextString(m) } func (*FrameBound) ProtoMessage() {} func (*FrameBound) Descriptor() ([]byte, []int) { - return fileDescriptor_2d655ab2f7683c23, []int{50} + return fileDescriptor_2d655ab2f7683c23, []int{51} } func (m *FrameBound) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -5602,7 +5690,7 @@ func (m *OnDuplicateKeyCtx) Reset() { *m = OnDuplicateKeyCtx{} } func (m *OnDuplicateKeyCtx) String() string { return proto.CompactTextString(m) } func (*OnDuplicateKeyCtx) ProtoMessage() {} func (*OnDuplicateKeyCtx) Descriptor() ([]byte, []int) { - return fileDescriptor_2d655ab2f7683c23, []int{51} + return fileDescriptor_2d655ab2f7683c23, []int{52} } func (m *OnDuplicateKeyCtx) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -5714,7 +5802,7 @@ func (m *DedupJoinCtx) Reset() { *m = DedupJoinCtx{} } func (m *DedupJoinCtx) String() string { return proto.CompactTextString(m) } func (*DedupJoinCtx) ProtoMessage() {} func (*DedupJoinCtx) Descriptor() ([]byte, []int) { - return fileDescriptor_2d655ab2f7683c23, []int{52} + return fileDescriptor_2d655ab2f7683c23, []int{53} } func (m *DedupJoinCtx) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -5782,7 +5870,7 @@ func (m *UpdateCtx) Reset() { *m = UpdateCtx{} } func (m *UpdateCtx) String() string { return proto.CompactTextString(m) } func (*UpdateCtx) ProtoMessage() {} func (*UpdateCtx) Descriptor() ([]byte, []int) { - return fileDescriptor_2d655ab2f7683c23, []int{53} + return fileDescriptor_2d655ab2f7683c23, []int{54} } func (m *UpdateCtx) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -5885,7 +5973,7 @@ func (m *InsertCtx) Reset() { *m = InsertCtx{} } func (m *InsertCtx) String() string { return proto.CompactTextString(m) } func (*InsertCtx) ProtoMessage() {} func (*InsertCtx) Descriptor() ([]byte, []int) { - return fileDescriptor_2d655ab2f7683c23, []int{54} + return fileDescriptor_2d655ab2f7683c23, []int{55} } func (m *InsertCtx) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -5983,7 +6071,7 @@ func (m *ReplaceCtx) Reset() { *m = ReplaceCtx{} } func (m *ReplaceCtx) String() string { return proto.CompactTextString(m) } func (*ReplaceCtx) ProtoMessage() {} func (*ReplaceCtx) Descriptor() ([]byte, []int) { - return fileDescriptor_2d655ab2f7683c23, []int{55} + return fileDescriptor_2d655ab2f7683c23, []int{56} } func (m *ReplaceCtx) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -6119,7 +6207,7 @@ func (m *AnalyzeInfo) Reset() { *m = AnalyzeInfo{} } func (m *AnalyzeInfo) String() string { return proto.CompactTextString(m) } func (*AnalyzeInfo) ProtoMessage() {} func (*AnalyzeInfo) Descriptor() ([]byte, []int) { - return fileDescriptor_2d655ab2f7683c23, []int{56} + return fileDescriptor_2d655ab2f7683c23, []int{57} } func (m *AnalyzeInfo) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -6384,7 +6472,7 @@ func (m *PartitionPrune) Reset() { *m = PartitionPrune{} } func (m *PartitionPrune) String() string { return proto.CompactTextString(m) } func (*PartitionPrune) ProtoMessage() {} func (*PartitionPrune) Descriptor() ([]byte, []int) { - return fileDescriptor_2d655ab2f7683c23, []int{57} + return fileDescriptor_2d655ab2f7683c23, []int{58} } func (m *PartitionPrune) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -6439,7 +6527,7 @@ func (m *OriginTableMessageForFuzzy) Reset() { *m = OriginTableMessageFo func (m *OriginTableMessageForFuzzy) String() string { return proto.CompactTextString(m) } func (*OriginTableMessageForFuzzy) ProtoMessage() {} func (*OriginTableMessageForFuzzy) Descriptor() ([]byte, []int) { - return fileDescriptor_2d655ab2f7683c23, []int{58} + return fileDescriptor_2d655ab2f7683c23, []int{59} } func (m *OriginTableMessageForFuzzy) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -6510,6 +6598,7 @@ type Node struct { ObjRef *ObjectRef `protobuf:"bytes,18,opt,name=obj_ref,json=objRef,proto3" json:"obj_ref,omitempty"` ParentObjRef *ObjectRef `protobuf:"bytes,19,opt,name=parent_obj_ref,json=parentObjRef,proto3" json:"parent_obj_ref,omitempty"` RowsetData *RowsetData `protobuf:"bytes,20,opt,name=rowset_data,json=rowsetData,proto3" json:"rowset_data,omitempty"` + IndexScanInfo IndexScanInfo `protobuf:"bytes,21,opt,name=index_scan_info,json=indexScanInfo,proto3" json:"index_scan_info"` ExtraOptions string `protobuf:"bytes,22,opt,name=extra_options,json=extraOptions,proto3" json:"extra_options,omitempty"` DeleteCtx *DeleteCtx `protobuf:"bytes,23,opt,name=delete_ctx,json=deleteCtx,proto3" json:"delete_ctx,omitempty"` BindingTags []int32 `protobuf:"varint,24,rep,packed,name=binding_tags,json=bindingTags,proto3" json:"binding_tags,omitempty"` @@ -6570,7 +6659,7 @@ func (m *Node) Reset() { *m = Node{} } func (m *Node) String() string { return proto.CompactTextString(m) } func (*Node) ProtoMessage() {} func (*Node) Descriptor() ([]byte, []int) { - return fileDescriptor_2d655ab2f7683c23, []int{59} + return fileDescriptor_2d655ab2f7683c23, []int{60} } func (m *Node) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -6739,6 +6828,13 @@ func (m *Node) GetRowsetData() *RowsetData { return nil } +func (m *Node) GetIndexScanInfo() IndexScanInfo { + if m != nil { + return m.IndexScanInfo + } + return IndexScanInfo{} +} + func (m *Node) GetExtraOptions() string { if m != nil { return m.ExtraOptions @@ -7076,7 +7172,7 @@ func (m *Snapshot) Reset() { *m = Snapshot{} } func (m *Snapshot) String() string { return proto.CompactTextString(m) } func (*Snapshot) ProtoMessage() {} func (*Snapshot) Descriptor() ([]byte, []int) { - return fileDescriptor_2d655ab2f7683c23, []int{60} + return fileDescriptor_2d655ab2f7683c23, []int{61} } func (m *Snapshot) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -7132,7 +7228,7 @@ func (m *SnapshotTenant) Reset() { *m = SnapshotTenant{} } func (m *SnapshotTenant) String() string { return proto.CompactTextString(m) } func (*SnapshotTenant) ProtoMessage() {} func (*SnapshotTenant) Descriptor() ([]byte, []int) { - return fileDescriptor_2d655ab2f7683c23, []int{61} + return fileDescriptor_2d655ab2f7683c23, []int{62} } func (m *SnapshotTenant) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -7195,7 +7291,7 @@ func (m *ExternScan) Reset() { *m = ExternScan{} } func (m *ExternScan) String() string { return proto.CompactTextString(m) } func (*ExternScan) ProtoMessage() {} func (*ExternScan) Descriptor() ([]byte, []int) { - return fileDescriptor_2d655ab2f7683c23, []int{62} + return fileDescriptor_2d655ab2f7683c23, []int{63} } func (m *ExternScan) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -7307,7 +7403,7 @@ func (m *ExternAttr) Reset() { *m = ExternAttr{} } func (m *ExternAttr) String() string { return proto.CompactTextString(m) } func (*ExternAttr) ProtoMessage() {} func (*ExternAttr) Descriptor() ([]byte, []int) { - return fileDescriptor_2d655ab2f7683c23, []int{63} + return fileDescriptor_2d655ab2f7683c23, []int{64} } func (m *ExternAttr) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -7381,7 +7477,7 @@ func (m *LockTarget) Reset() { *m = LockTarget{} } func (m *LockTarget) String() string { return proto.CompactTextString(m) } func (*LockTarget) ProtoMessage() {} func (*LockTarget) Descriptor() ([]byte, []int) { - return fileDescriptor_2d655ab2f7683c23, []int{64} + return fileDescriptor_2d655ab2f7683c23, []int{65} } func (m *LockTarget) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -7523,7 +7619,7 @@ func (m *PreInsertUkCtx) Reset() { *m = PreInsertUkCtx{} } func (m *PreInsertUkCtx) String() string { return proto.CompactTextString(m) } func (*PreInsertUkCtx) ProtoMessage() {} func (*PreInsertUkCtx) Descriptor() ([]byte, []int) { - return fileDescriptor_2d655ab2f7683c23, []int{65} + return fileDescriptor_2d655ab2f7683c23, []int{66} } func (m *PreInsertUkCtx) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -7592,7 +7688,7 @@ func (m *PreDeleteCtx) Reset() { *m = PreDeleteCtx{} } func (m *PreDeleteCtx) String() string { return proto.CompactTextString(m) } func (*PreDeleteCtx) ProtoMessage() {} func (*PreDeleteCtx) Descriptor() ([]byte, []int) { - return fileDescriptor_2d655ab2f7683c23, []int{66} + return fileDescriptor_2d655ab2f7683c23, []int{67} } func (m *PreDeleteCtx) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -7644,7 +7740,7 @@ func (m *PreInsertCtx) Reset() { *m = PreInsertCtx{} } func (m *PreInsertCtx) String() string { return proto.CompactTextString(m) } func (*PreInsertCtx) ProtoMessage() {} func (*PreInsertCtx) Descriptor() ([]byte, []int) { - return fileDescriptor_2d655ab2f7683c23, []int{67} + return fileDescriptor_2d655ab2f7683c23, []int{68} } func (m *PreInsertCtx) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -7729,7 +7825,7 @@ func (m *RuntimeFilterSpec) Reset() { *m = RuntimeFilterSpec{} } func (m *RuntimeFilterSpec) String() string { return proto.CompactTextString(m) } func (*RuntimeFilterSpec) ProtoMessage() {} func (*RuntimeFilterSpec) Descriptor() ([]byte, []int) { - return fileDescriptor_2d655ab2f7683c23, []int{68} + return fileDescriptor_2d655ab2f7683c23, []int{69} } func (m *RuntimeFilterSpec) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -7797,7 +7893,7 @@ func (m *IdList) Reset() { *m = IdList{} } func (m *IdList) String() string { return proto.CompactTextString(m) } func (*IdList) ProtoMessage() {} func (*IdList) Descriptor() ([]byte, []int) { - return fileDescriptor_2d655ab2f7683c23, []int{69} + return fileDescriptor_2d655ab2f7683c23, []int{70} } func (m *IdList) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -7844,7 +7940,7 @@ func (m *ColPosMap) Reset() { *m = ColPosMap{} } func (m *ColPosMap) String() string { return proto.CompactTextString(m) } func (*ColPosMap) ProtoMessage() {} func (*ColPosMap) Descriptor() ([]byte, []int) { - return fileDescriptor_2d655ab2f7683c23, []int{70} + return fileDescriptor_2d655ab2f7683c23, []int{71} } func (m *ColPosMap) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -7902,7 +7998,7 @@ func (m *DeleteCtx) Reset() { *m = DeleteCtx{} } func (m *DeleteCtx) String() string { return proto.CompactTextString(m) } func (*DeleteCtx) ProtoMessage() {} func (*DeleteCtx) Descriptor() ([]byte, []int) { - return fileDescriptor_2d655ab2f7683c23, []int{71} + return fileDescriptor_2d655ab2f7683c23, []int{72} } func (m *DeleteCtx) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -8022,7 +8118,7 @@ func (m *PostDmlFullTextCtx) Reset() { *m = PostDmlFullTextCtx{} } func (m *PostDmlFullTextCtx) String() string { return proto.CompactTextString(m) } func (*PostDmlFullTextCtx) ProtoMessage() {} func (*PostDmlFullTextCtx) Descriptor() ([]byte, []int) { - return fileDescriptor_2d655ab2f7683c23, []int{72} + return fileDescriptor_2d655ab2f7683c23, []int{73} } func (m *PostDmlFullTextCtx) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -8097,7 +8193,7 @@ func (m *PostDmlCtx) Reset() { *m = PostDmlCtx{} } func (m *PostDmlCtx) String() string { return proto.CompactTextString(m) } func (*PostDmlCtx) ProtoMessage() {} func (*PostDmlCtx) Descriptor() ([]byte, []int) { - return fileDescriptor_2d655ab2f7683c23, []int{73} + return fileDescriptor_2d655ab2f7683c23, []int{74} } func (m *PostDmlCtx) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -8209,7 +8305,7 @@ func (m *Query) Reset() { *m = Query{} } func (m *Query) String() string { return proto.CompactTextString(m) } func (*Query) ProtoMessage() {} func (*Query) Descriptor() ([]byte, []int) { - return fileDescriptor_2d655ab2f7683c23, []int{74} + return fileDescriptor_2d655ab2f7683c23, []int{75} } func (m *Query) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -8312,7 +8408,7 @@ func (m *TransationControl) Reset() { *m = TransationControl{} } func (m *TransationControl) String() string { return proto.CompactTextString(m) } func (*TransationControl) ProtoMessage() {} func (*TransationControl) Descriptor() ([]byte, []int) { - return fileDescriptor_2d655ab2f7683c23, []int{75} + return fileDescriptor_2d655ab2f7683c23, []int{76} } func (m *TransationControl) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -8416,7 +8512,7 @@ func (m *TransationBegin) Reset() { *m = TransationBegin{} } func (m *TransationBegin) String() string { return proto.CompactTextString(m) } func (*TransationBegin) ProtoMessage() {} func (*TransationBegin) Descriptor() ([]byte, []int) { - return fileDescriptor_2d655ab2f7683c23, []int{76} + return fileDescriptor_2d655ab2f7683c23, []int{77} } func (m *TransationBegin) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -8463,7 +8559,7 @@ func (m *TransationCommit) Reset() { *m = TransationCommit{} } func (m *TransationCommit) String() string { return proto.CompactTextString(m) } func (*TransationCommit) ProtoMessage() {} func (*TransationCommit) Descriptor() ([]byte, []int) { - return fileDescriptor_2d655ab2f7683c23, []int{77} + return fileDescriptor_2d655ab2f7683c23, []int{78} } func (m *TransationCommit) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -8510,7 +8606,7 @@ func (m *TransationRollback) Reset() { *m = TransationRollback{} } func (m *TransationRollback) String() string { return proto.CompactTextString(m) } func (*TransationRollback) ProtoMessage() {} func (*TransationRollback) Descriptor() ([]byte, []int) { - return fileDescriptor_2d655ab2f7683c23, []int{78} + return fileDescriptor_2d655ab2f7683c23, []int{79} } func (m *TransationRollback) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -8565,7 +8661,7 @@ func (m *Plan) Reset() { *m = Plan{} } func (m *Plan) String() string { return proto.CompactTextString(m) } func (*Plan) ProtoMessage() {} func (*Plan) Descriptor() ([]byte, []int) { - return fileDescriptor_2d655ab2f7683c23, []int{79} + return fileDescriptor_2d655ab2f7683c23, []int{80} } func (m *Plan) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -8688,7 +8784,7 @@ func (m *Column) Reset() { *m = Column{} } func (m *Column) String() string { return proto.CompactTextString(m) } func (*Column) ProtoMessage() {} func (*Column) Descriptor() ([]byte, []int) { - return fileDescriptor_2d655ab2f7683c23, []int{80} + return fileDescriptor_2d655ab2f7683c23, []int{81} } func (m *Column) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -8744,7 +8840,7 @@ func (m *DataControl) Reset() { *m = DataControl{} } func (m *DataControl) String() string { return proto.CompactTextString(m) } func (*DataControl) ProtoMessage() {} func (*DataControl) Descriptor() ([]byte, []int) { - return fileDescriptor_2d655ab2f7683c23, []int{81} + return fileDescriptor_2d655ab2f7683c23, []int{82} } func (m *DataControl) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -8899,7 +8995,7 @@ func (m *DataDefinition) Reset() { *m = DataDefinition{} } func (m *DataDefinition) String() string { return proto.CompactTextString(m) } func (*DataDefinition) ProtoMessage() {} func (*DataDefinition) Descriptor() ([]byte, []int) { - return fileDescriptor_2d655ab2f7683c23, []int{82} + return fileDescriptor_2d655ab2f7683c23, []int{83} } func (m *DataDefinition) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -9203,7 +9299,7 @@ func (m *SubscriptionOption) Reset() { *m = SubscriptionOption{} } func (m *SubscriptionOption) String() string { return proto.CompactTextString(m) } func (*SubscriptionOption) ProtoMessage() {} func (*SubscriptionOption) Descriptor() ([]byte, []int) { - return fileDescriptor_2d655ab2f7683c23, []int{83} + return fileDescriptor_2d655ab2f7683c23, []int{84} } func (m *SubscriptionOption) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -9260,7 +9356,7 @@ func (m *CreateDatabase) Reset() { *m = CreateDatabase{} } func (m *CreateDatabase) String() string { return proto.CompactTextString(m) } func (*CreateDatabase) ProtoMessage() {} func (*CreateDatabase) Descriptor() ([]byte, []int) { - return fileDescriptor_2d655ab2f7683c23, []int{84} + return fileDescriptor_2d655ab2f7683c23, []int{85} } func (m *CreateDatabase) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -9329,7 +9425,7 @@ func (m *AlterDatabase) Reset() { *m = AlterDatabase{} } func (m *AlterDatabase) String() string { return proto.CompactTextString(m) } func (*AlterDatabase) ProtoMessage() {} func (*AlterDatabase) Descriptor() ([]byte, []int) { - return fileDescriptor_2d655ab2f7683c23, []int{85} + return fileDescriptor_2d655ab2f7683c23, []int{86} } func (m *AlterDatabase) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -9391,7 +9487,7 @@ func (m *DropDatabase) Reset() { *m = DropDatabase{} } func (m *DropDatabase) String() string { return proto.CompactTextString(m) } func (*DropDatabase) ProtoMessage() {} func (*DropDatabase) Descriptor() ([]byte, []int) { - return fileDescriptor_2d655ab2f7683c23, []int{86} + return fileDescriptor_2d655ab2f7683c23, []int{87} } func (m *DropDatabase) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -9467,7 +9563,7 @@ func (m *FkColName) Reset() { *m = FkColName{} } func (m *FkColName) String() string { return proto.CompactTextString(m) } func (*FkColName) ProtoMessage() {} func (*FkColName) Descriptor() ([]byte, []int) { - return fileDescriptor_2d655ab2f7683c23, []int{87} + return fileDescriptor_2d655ab2f7683c23, []int{88} } func (m *FkColName) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -9533,7 +9629,7 @@ func (m *ForeignKeyInfo) Reset() { *m = ForeignKeyInfo{} } func (m *ForeignKeyInfo) String() string { return proto.CompactTextString(m) } func (*ForeignKeyInfo) ProtoMessage() {} func (*ForeignKeyInfo) Descriptor() ([]byte, []int) { - return fileDescriptor_2d655ab2f7683c23, []int{88} + return fileDescriptor_2d655ab2f7683c23, []int{89} } func (m *ForeignKeyInfo) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -9626,7 +9722,7 @@ func (m *CreateTable) Reset() { *m = CreateTable{} } func (m *CreateTable) String() string { return proto.CompactTextString(m) } func (*CreateTable) ProtoMessage() {} func (*CreateTable) Descriptor() ([]byte, []int) { - return fileDescriptor_2d655ab2f7683c23, []int{89} + return fileDescriptor_2d655ab2f7683c23, []int{90} } func (m *CreateTable) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -9766,7 +9862,7 @@ func (m *AlterTableDrop) Reset() { *m = AlterTableDrop{} } func (m *AlterTableDrop) String() string { return proto.CompactTextString(m) } func (*AlterTableDrop) ProtoMessage() {} func (*AlterTableDrop) Descriptor() ([]byte, []int) { - return fileDescriptor_2d655ab2f7683c23, []int{90} + return fileDescriptor_2d655ab2f7683c23, []int{91} } func (m *AlterTableDrop) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -9831,7 +9927,7 @@ func (m *AlterTableAddFk) Reset() { *m = AlterTableAddFk{} } func (m *AlterTableAddFk) String() string { return proto.CompactTextString(m) } func (*AlterTableAddFk) ProtoMessage() {} func (*AlterTableAddFk) Descriptor() ([]byte, []int) { - return fileDescriptor_2d655ab2f7683c23, []int{91} + return fileDescriptor_2d655ab2f7683c23, []int{92} } func (m *AlterTableAddFk) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -9903,7 +9999,7 @@ func (m *AlterTableAddIndex) Reset() { *m = AlterTableAddIndex{} } func (m *AlterTableAddIndex) String() string { return proto.CompactTextString(m) } func (*AlterTableAddIndex) ProtoMessage() {} func (*AlterTableAddIndex) Descriptor() ([]byte, []int) { - return fileDescriptor_2d655ab2f7683c23, []int{92} + return fileDescriptor_2d655ab2f7683c23, []int{93} } func (m *AlterTableAddIndex) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -9981,7 +10077,7 @@ func (m *AlterTableDropIndex) Reset() { *m = AlterTableDropIndex{} } func (m *AlterTableDropIndex) String() string { return proto.CompactTextString(m) } func (*AlterTableDropIndex) ProtoMessage() {} func (*AlterTableDropIndex) Descriptor() ([]byte, []int) { - return fileDescriptor_2d655ab2f7683c23, []int{93} + return fileDescriptor_2d655ab2f7683c23, []int{94} } func (m *AlterTableDropIndex) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -10052,7 +10148,7 @@ func (m *AlterTableAlterIndex) Reset() { *m = AlterTableAlterIndex{} } func (m *AlterTableAlterIndex) String() string { return proto.CompactTextString(m) } func (*AlterTableAlterIndex) ProtoMessage() {} func (*AlterTableAlterIndex) Descriptor() ([]byte, []int) { - return fileDescriptor_2d655ab2f7683c23, []int{94} + return fileDescriptor_2d655ab2f7683c23, []int{95} } func (m *AlterTableAlterIndex) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -10123,7 +10219,7 @@ func (m *AlterTableAlterReIndex) Reset() { *m = AlterTableAlterReIndex{} func (m *AlterTableAlterReIndex) String() string { return proto.CompactTextString(m) } func (*AlterTableAlterReIndex) ProtoMessage() {} func (*AlterTableAlterReIndex) Descriptor() ([]byte, []int) { - return fileDescriptor_2d655ab2f7683c23, []int{95} + return fileDescriptor_2d655ab2f7683c23, []int{96} } func (m *AlterTableAlterReIndex) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -10193,7 +10289,7 @@ func (m *AlterTableAddPartition) Reset() { *m = AlterTableAddPartition{} func (m *AlterTableAddPartition) String() string { return proto.CompactTextString(m) } func (*AlterTableAddPartition) ProtoMessage() {} func (*AlterTableAddPartition) Descriptor() ([]byte, []int) { - return fileDescriptor_2d655ab2f7683c23, []int{96} + return fileDescriptor_2d655ab2f7683c23, []int{97} } func (m *AlterTableAddPartition) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -10254,7 +10350,7 @@ func (m *AlterTableComment) Reset() { *m = AlterTableComment{} } func (m *AlterTableComment) String() string { return proto.CompactTextString(m) } func (*AlterTableComment) ProtoMessage() {} func (*AlterTableComment) Descriptor() ([]byte, []int) { - return fileDescriptor_2d655ab2f7683c23, []int{97} + return fileDescriptor_2d655ab2f7683c23, []int{98} } func (m *AlterTableComment) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -10302,7 +10398,7 @@ func (m *AlterTableName) Reset() { *m = AlterTableName{} } func (m *AlterTableName) String() string { return proto.CompactTextString(m) } func (*AlterTableName) ProtoMessage() {} func (*AlterTableName) Descriptor() ([]byte, []int) { - return fileDescriptor_2d655ab2f7683c23, []int{98} + return fileDescriptor_2d655ab2f7683c23, []int{99} } func (m *AlterTableName) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -10359,7 +10455,7 @@ func (m *AlterAddColumn) Reset() { *m = AlterAddColumn{} } func (m *AlterAddColumn) String() string { return proto.CompactTextString(m) } func (*AlterAddColumn) ProtoMessage() {} func (*AlterAddColumn) Descriptor() ([]byte, []int) { - return fileDescriptor_2d655ab2f7683c23, []int{99} + return fileDescriptor_2d655ab2f7683c23, []int{100} } func (m *AlterAddColumn) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -10428,7 +10524,7 @@ func (m *AlterDropColumn) Reset() { *m = AlterDropColumn{} } func (m *AlterDropColumn) String() string { return proto.CompactTextString(m) } func (*AlterDropColumn) ProtoMessage() {} func (*AlterDropColumn) Descriptor() ([]byte, []int) { - return fileDescriptor_2d655ab2f7683c23, []int{100} + return fileDescriptor_2d655ab2f7683c23, []int{101} } func (m *AlterDropColumn) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -10482,7 +10578,7 @@ func (m *RenameTable) Reset() { *m = RenameTable{} } func (m *RenameTable) String() string { return proto.CompactTextString(m) } func (*RenameTable) ProtoMessage() {} func (*RenameTable) Descriptor() ([]byte, []int) { - return fileDescriptor_2d655ab2f7683c23, []int{101} + return fileDescriptor_2d655ab2f7683c23, []int{102} } func (m *RenameTable) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -10542,7 +10638,7 @@ func (m *AlterTable) Reset() { *m = AlterTable{} } func (m *AlterTable) String() string { return proto.CompactTextString(m) } func (*AlterTable) ProtoMessage() {} func (*AlterTable) Descriptor() ([]byte, []int) { - return fileDescriptor_2d655ab2f7683c23, []int{102} + return fileDescriptor_2d655ab2f7683c23, []int{103} } func (m *AlterTable) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -10671,7 +10767,7 @@ func (m *AlterTable_Action) Reset() { *m = AlterTable_Action{} } func (m *AlterTable_Action) String() string { return proto.CompactTextString(m) } func (*AlterTable_Action) ProtoMessage() {} func (*AlterTable_Action) Descriptor() ([]byte, []int) { - return fileDescriptor_2d655ab2f7683c23, []int{102, 0} + return fileDescriptor_2d655ab2f7683c23, []int{103, 0} } func (m *AlterTable_Action) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -10866,7 +10962,7 @@ func (m *DropTable) Reset() { *m = DropTable{} } func (m *DropTable) String() string { return proto.CompactTextString(m) } func (*DropTable) ProtoMessage() {} func (*DropTable) Descriptor() ([]byte, []int) { - return fileDescriptor_2d655ab2f7683c23, []int{103} + return fileDescriptor_2d655ab2f7683c23, []int{104} } func (m *DropTable) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -10993,7 +11089,7 @@ func (m *CreateView) Reset() { *m = CreateView{} } func (m *CreateView) String() string { return proto.CompactTextString(m) } func (*CreateView) ProtoMessage() {} func (*CreateView) Descriptor() ([]byte, []int) { - return fileDescriptor_2d655ab2f7683c23, []int{104} + return fileDescriptor_2d655ab2f7683c23, []int{105} } func (m *CreateView) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -11063,7 +11159,7 @@ func (m *AlterView) Reset() { *m = AlterView{} } func (m *AlterView) String() string { return proto.CompactTextString(m) } func (*AlterView) ProtoMessage() {} func (*AlterView) Descriptor() ([]byte, []int) { - return fileDescriptor_2d655ab2f7683c23, []int{105} + return fileDescriptor_2d655ab2f7683c23, []int{106} } func (m *AlterView) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -11126,7 +11222,7 @@ func (m *CreateSequence) Reset() { *m = CreateSequence{} } func (m *CreateSequence) String() string { return proto.CompactTextString(m) } func (*CreateSequence) ProtoMessage() {} func (*CreateSequence) Descriptor() ([]byte, []int) { - return fileDescriptor_2d655ab2f7683c23, []int{106} + return fileDescriptor_2d655ab2f7683c23, []int{107} } func (m *CreateSequence) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -11190,7 +11286,7 @@ func (m *DropSequence) Reset() { *m = DropSequence{} } func (m *DropSequence) String() string { return proto.CompactTextString(m) } func (*DropSequence) ProtoMessage() {} func (*DropSequence) Descriptor() ([]byte, []int) { - return fileDescriptor_2d655ab2f7683c23, []int{107} + return fileDescriptor_2d655ab2f7683c23, []int{108} } func (m *DropSequence) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -11260,7 +11356,7 @@ func (m *AlterSequence) Reset() { *m = AlterSequence{} } func (m *AlterSequence) String() string { return proto.CompactTextString(m) } func (*AlterSequence) ProtoMessage() {} func (*AlterSequence) Descriptor() ([]byte, []int) { - return fileDescriptor_2d655ab2f7683c23, []int{108} + return fileDescriptor_2d655ab2f7683c23, []int{109} } func (m *AlterSequence) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -11326,7 +11422,7 @@ func (m *CreateIndex) Reset() { *m = CreateIndex{} } func (m *CreateIndex) String() string { return proto.CompactTextString(m) } func (*CreateIndex) ProtoMessage() {} func (*CreateIndex) Descriptor() ([]byte, []int) { - return fileDescriptor_2d655ab2f7683c23, []int{109} + return fileDescriptor_2d655ab2f7683c23, []int{110} } func (m *CreateIndex) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -11408,7 +11504,7 @@ func (m *AlterIndex) Reset() { *m = AlterIndex{} } func (m *AlterIndex) String() string { return proto.CompactTextString(m) } func (*AlterIndex) ProtoMessage() {} func (*AlterIndex) Descriptor() ([]byte, []int) { - return fileDescriptor_2d655ab2f7683c23, []int{110} + return fileDescriptor_2d655ab2f7683c23, []int{111} } func (m *AlterIndex) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -11458,7 +11554,7 @@ func (m *DropIndex) Reset() { *m = DropIndex{} } func (m *DropIndex) String() string { return proto.CompactTextString(m) } func (*DropIndex) ProtoMessage() {} func (*DropIndex) Descriptor() ([]byte, []int) { - return fileDescriptor_2d655ab2f7683c23, []int{111} + return fileDescriptor_2d655ab2f7683c23, []int{112} } func (m *DropIndex) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -11533,7 +11629,7 @@ func (m *TruncateTable) Reset() { *m = TruncateTable{} } func (m *TruncateTable) String() string { return proto.CompactTextString(m) } func (*TruncateTable) ProtoMessage() {} func (*TruncateTable) Descriptor() ([]byte, []int) { - return fileDescriptor_2d655ab2f7683c23, []int{112} + return fileDescriptor_2d655ab2f7683c23, []int{113} } func (m *TruncateTable) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -11631,7 +11727,7 @@ func (m *ClusterTable) Reset() { *m = ClusterTable{} } func (m *ClusterTable) String() string { return proto.CompactTextString(m) } func (*ClusterTable) ProtoMessage() {} func (*ClusterTable) Descriptor() ([]byte, []int) { - return fileDescriptor_2d655ab2f7683c23, []int{113} + return fileDescriptor_2d655ab2f7683c23, []int{114} } func (m *ClusterTable) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -11693,7 +11789,7 @@ func (m *ShowVariables) Reset() { *m = ShowVariables{} } func (m *ShowVariables) String() string { return proto.CompactTextString(m) } func (*ShowVariables) ProtoMessage() {} func (*ShowVariables) Descriptor() ([]byte, []int) { - return fileDescriptor_2d655ab2f7683c23, []int{114} + return fileDescriptor_2d655ab2f7683c23, []int{115} } func (m *ShowVariables) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -11747,7 +11843,7 @@ func (m *SetVariables) Reset() { *m = SetVariables{} } func (m *SetVariables) String() string { return proto.CompactTextString(m) } func (*SetVariables) ProtoMessage() {} func (*SetVariables) Descriptor() ([]byte, []int) { - return fileDescriptor_2d655ab2f7683c23, []int{115} + return fileDescriptor_2d655ab2f7683c23, []int{116} } func (m *SetVariables) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -11798,7 +11894,7 @@ func (m *SetVariablesItem) Reset() { *m = SetVariablesItem{} } func (m *SetVariablesItem) String() string { return proto.CompactTextString(m) } func (*SetVariablesItem) ProtoMessage() {} func (*SetVariablesItem) Descriptor() ([]byte, []int) { - return fileDescriptor_2d655ab2f7683c23, []int{116} + return fileDescriptor_2d655ab2f7683c23, []int{117} } func (m *SetVariablesItem) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -11876,7 +11972,7 @@ func (m *Prepare) Reset() { *m = Prepare{} } func (m *Prepare) String() string { return proto.CompactTextString(m) } func (*Prepare) ProtoMessage() {} func (*Prepare) Descriptor() ([]byte, []int) { - return fileDescriptor_2d655ab2f7683c23, []int{117} + return fileDescriptor_2d655ab2f7683c23, []int{118} } func (m *Prepare) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -11945,7 +12041,7 @@ func (m *Execute) Reset() { *m = Execute{} } func (m *Execute) String() string { return proto.CompactTextString(m) } func (*Execute) ProtoMessage() {} func (*Execute) Descriptor() ([]byte, []int) { - return fileDescriptor_2d655ab2f7683c23, []int{118} + return fileDescriptor_2d655ab2f7683c23, []int{119} } func (m *Execute) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -11999,7 +12095,7 @@ func (m *Deallocate) Reset() { *m = Deallocate{} } func (m *Deallocate) String() string { return proto.CompactTextString(m) } func (*Deallocate) ProtoMessage() {} func (*Deallocate) Descriptor() ([]byte, []int) { - return fileDescriptor_2d655ab2f7683c23, []int{119} + return fileDescriptor_2d655ab2f7683c23, []int{120} } func (m *Deallocate) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -12046,7 +12142,7 @@ func (m *OtherDCL) Reset() { *m = OtherDCL{} } func (m *OtherDCL) String() string { return proto.CompactTextString(m) } func (*OtherDCL) ProtoMessage() {} func (*OtherDCL) Descriptor() ([]byte, []int) { - return fileDescriptor_2d655ab2f7683c23, []int{120} + return fileDescriptor_2d655ab2f7683c23, []int{121} } func (m *OtherDCL) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -12094,7 +12190,7 @@ func (m *TableLockInfo) Reset() { *m = TableLockInfo{} } func (m *TableLockInfo) String() string { return proto.CompactTextString(m) } func (*TableLockInfo) ProtoMessage() {} func (*TableLockInfo) Descriptor() ([]byte, []int) { - return fileDescriptor_2d655ab2f7683c23, []int{121} + return fileDescriptor_2d655ab2f7683c23, []int{122} } func (m *TableLockInfo) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -12148,7 +12244,7 @@ func (m *LockTables) Reset() { *m = LockTables{} } func (m *LockTables) String() string { return proto.CompactTextString(m) } func (*LockTables) ProtoMessage() {} func (*LockTables) Descriptor() ([]byte, []int) { - return fileDescriptor_2d655ab2f7683c23, []int{122} + return fileDescriptor_2d655ab2f7683c23, []int{123} } func (m *LockTables) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -12194,7 +12290,7 @@ func (m *UnLockTables) Reset() { *m = UnLockTables{} } func (m *UnLockTables) String() string { return proto.CompactTextString(m) } func (*UnLockTables) ProtoMessage() {} func (*UnLockTables) Descriptor() ([]byte, []int) { - return fileDescriptor_2d655ab2f7683c23, []int{123} + return fileDescriptor_2d655ab2f7683c23, []int{124} } func (m *UnLockTables) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -12234,7 +12330,7 @@ func (m *MetadataScanInfos) Reset() { *m = MetadataScanInfos{} } func (m *MetadataScanInfos) String() string { return proto.CompactTextString(m) } func (*MetadataScanInfos) ProtoMessage() {} func (*MetadataScanInfos) Descriptor() ([]byte, []int) { - return fileDescriptor_2d655ab2f7683c23, []int{124} + return fileDescriptor_2d655ab2f7683c23, []int{125} } func (m *MetadataScanInfos) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -12291,7 +12387,7 @@ func (m *MetadataScanInfo) Reset() { *m = MetadataScanInfo{} } func (m *MetadataScanInfo) String() string { return proto.CompactTextString(m) } func (*MetadataScanInfo) ProtoMessage() {} func (*MetadataScanInfo) Descriptor() ([]byte, []int) { - return fileDescriptor_2d655ab2f7683c23, []int{125} + return fileDescriptor_2d655ab2f7683c23, []int{126} } func (m *MetadataScanInfo) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -12441,6 +12537,7 @@ func init() { proto.RegisterType((*ObjectRef)(nil), "plan.ObjectRef") proto.RegisterType((*PubInfo)(nil), "plan.PubInfo") proto.RegisterType((*SubscriptionMeta)(nil), "plan.SubscriptionMeta") + proto.RegisterType((*IndexScanInfo)(nil), "plan.IndexScanInfo") proto.RegisterType((*Function)(nil), "plan.Function") proto.RegisterType((*Expr)(nil), "plan.Expr") proto.RegisterType((*FoldVal)(nil), "plan.FoldVal") @@ -12564,751 +12661,757 @@ func init() { func init() { proto.RegisterFile("plan.proto", fileDescriptor_2d655ab2f7683c23) } var fileDescriptor_2d655ab2f7683c23 = []byte{ - // 11894 bytes of a gzipped FileDescriptorProto + // 11991 bytes of a gzipped FileDescriptorProto 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xd4, 0xbd, 0x5d, 0x8c, 0x23, 0x57, - 0x76, 0x18, 0xdc, 0x6c, 0xfe, 0x1f, 0xfe, 0x74, 0xf5, 0x9d, 0x3f, 0xce, 0x68, 0x34, 0xd3, 0x53, - 0x1a, 0x49, 0xa3, 0x91, 0x34, 0x92, 0x66, 0xf4, 0x33, 0x5a, 0xef, 0x7a, 0x97, 0xcd, 0x66, 0x4f, + 0x76, 0x18, 0xdc, 0x6c, 0xfe, 0x1f, 0xfe, 0x74, 0xf5, 0x9d, 0x9e, 0x11, 0x67, 0x34, 0x9a, 0xe9, + 0x29, 0x8d, 0xa4, 0xd1, 0x48, 0x1a, 0x49, 0x33, 0xfa, 0x5d, 0xef, 0x7a, 0x97, 0xcd, 0x66, 0x4f, 0x73, 0x87, 0x4d, 0xf6, 0x16, 0xd9, 0x33, 0xd2, 0x1a, 0xdf, 0x57, 0x28, 0xb2, 0x8a, 0xdd, 0xa5, - 0x2e, 0x56, 0x51, 0x55, 0xc5, 0xe9, 0x6e, 0x01, 0x06, 0x16, 0x31, 0xe0, 0xc4, 0x79, 0x35, 0xe0, - 0xa7, 0x38, 0x58, 0xfb, 0x29, 0x30, 0x62, 0x20, 0x40, 0x0c, 0x38, 0x08, 0xf2, 0x96, 0x3c, 0x38, - 0x46, 0x10, 0x04, 0xc8, 0x43, 0x90, 0x04, 0x70, 0x82, 0xcd, 0x83, 0x9f, 0x62, 0x3f, 0x38, 0x2f, - 0x41, 0x5e, 0x82, 0x73, 0xee, 0xad, 0xaa, 0x5b, 0x24, 0x5b, 0x23, 0x69, 0xd7, 0x48, 0xf2, 0xd2, - 0x5d, 0xf7, 0x9c, 0x73, 0xff, 0xef, 0x3d, 0x7f, 0xf7, 0xdc, 0x4b, 0x80, 0x99, 0x63, 0xb8, 0x0f, - 0x66, 0xbe, 0x17, 0x7a, 0x2c, 0x87, 0xdf, 0x37, 0xde, 0x3d, 0xb2, 0xc3, 0xe3, 0xf9, 0xe8, 0xc1, - 0xd8, 0x9b, 0xbe, 0x77, 0xe4, 0x1d, 0x79, 0xef, 0x11, 0x72, 0x34, 0x9f, 0x50, 0x8a, 0x12, 0xf4, - 0xc5, 0x33, 0xdd, 0x00, 0xc7, 0x1b, 0x9f, 0x88, 0xef, 0x8d, 0xd0, 0x9e, 0x5a, 0x41, 0x68, 0x4c, - 0x67, 0x1c, 0xa0, 0xfe, 0x69, 0x06, 0x72, 0xc3, 0xf3, 0x99, 0xc5, 0xea, 0xb0, 0x6e, 0x9b, 0x8d, - 0xcc, 0x56, 0xe6, 0x5e, 0x5e, 0x5b, 0xb7, 0x4d, 0xb6, 0x05, 0x15, 0xd7, 0x0b, 0x7b, 0x73, 0xc7, - 0x31, 0x46, 0x8e, 0xd5, 0x58, 0xdf, 0xca, 0xdc, 0x2b, 0x69, 0x32, 0x88, 0xbd, 0x02, 0x65, 0x63, - 0x1e, 0x7a, 0xba, 0xed, 0x8e, 0xfd, 0x46, 0x96, 0xf0, 0x25, 0x04, 0x74, 0xdc, 0xb1, 0xcf, 0x2e, - 0x43, 0xfe, 0xd4, 0x36, 0xc3, 0xe3, 0x46, 0x8e, 0x4a, 0xe4, 0x09, 0x84, 0x06, 0x63, 0xc3, 0xb1, - 0x1a, 0x79, 0x0e, 0xa5, 0x04, 0x42, 0x43, 0xaa, 0xa4, 0xb0, 0x95, 0xb9, 0x57, 0xd6, 0x78, 0x82, - 0xdd, 0x02, 0xb0, 0xdc, 0xf9, 0xf4, 0x85, 0xe1, 0xcc, 0xad, 0xa0, 0x51, 0x24, 0x94, 0x04, 0x51, - 0x7f, 0x08, 0xe5, 0x69, 0x70, 0xb4, 0x67, 0x19, 0xa6, 0xe5, 0xb3, 0x6b, 0x50, 0x9c, 0x06, 0x47, - 0x7a, 0x68, 0x1c, 0x89, 0x2e, 0x14, 0xa6, 0xc1, 0xd1, 0xd0, 0x38, 0x62, 0xd7, 0xa1, 0x44, 0x88, - 0xf3, 0x19, 0xef, 0x43, 0x5e, 0x43, 0x42, 0xec, 0xb1, 0xfa, 0xd7, 0x79, 0x28, 0x76, 0xed, 0xd0, - 0xf2, 0x0d, 0x87, 0x5d, 0x85, 0x82, 0x1d, 0xb8, 0x73, 0xc7, 0xa1, 0xec, 0x25, 0x4d, 0xa4, 0xd8, - 0x55, 0xc8, 0xdb, 0x8f, 0x5f, 0x18, 0x0e, 0xcf, 0xbb, 0xb7, 0xa6, 0xf1, 0x24, 0x6b, 0x40, 0xc1, - 0xfe, 0xe0, 0x63, 0x44, 0x64, 0x05, 0x42, 0xa4, 0x09, 0xf3, 0xe8, 0x21, 0x62, 0x72, 0x31, 0x86, - 0xd2, 0x84, 0xf9, 0xf8, 0x43, 0xc4, 0x60, 0xef, 0xb3, 0x84, 0xa1, 0x34, 0xd6, 0x32, 0xa7, 0x5a, - 0x70, 0x00, 0x6a, 0x58, 0xcb, 0x3c, 0xaa, 0x65, 0xce, 0x6b, 0x29, 0x0a, 0x84, 0x48, 0x13, 0x86, - 0xd7, 0x52, 0x8a, 0x31, 0x71, 0x2d, 0x73, 0x5e, 0x4b, 0x79, 0x2b, 0x73, 0x2f, 0x47, 0x18, 0x5e, - 0xcb, 0x65, 0xc8, 0x99, 0x08, 0x87, 0xad, 0xcc, 0xbd, 0xcc, 0xde, 0x9a, 0x46, 0x29, 0x84, 0x06, - 0x08, 0xad, 0xe0, 0x00, 0x23, 0x34, 0x10, 0xd0, 0x11, 0x42, 0xab, 0x38, 0x1a, 0x08, 0x1d, 0x09, - 0xe8, 0x04, 0xa1, 0xb5, 0xad, 0xcc, 0xbd, 0x75, 0x84, 0x62, 0x8a, 0xdd, 0x80, 0xa2, 0x69, 0x84, - 0x16, 0x22, 0xea, 0xa2, 0xcb, 0x11, 0x00, 0x71, 0xb8, 0xe2, 0x10, 0xb7, 0x21, 0x3a, 0x1d, 0x01, - 0x98, 0x0a, 0x15, 0x24, 0x8b, 0xf0, 0x8a, 0xc0, 0xcb, 0x40, 0xf6, 0x11, 0x54, 0x4d, 0x6b, 0x6c, - 0x4f, 0x0d, 0x87, 0xf7, 0x69, 0x73, 0x2b, 0x73, 0xaf, 0xf2, 0x70, 0xe3, 0x01, 0xed, 0x89, 0x18, - 0xb3, 0xb7, 0xa6, 0xa5, 0xc8, 0xd8, 0x63, 0xa8, 0x89, 0xf4, 0x07, 0x0f, 0x69, 0x60, 0x19, 0xe5, - 0x53, 0x52, 0xf9, 0x3e, 0x78, 0xf8, 0x78, 0x6f, 0x4d, 0x4b, 0x13, 0xb2, 0xbb, 0x50, 0x8d, 0xb7, - 0x08, 0x66, 0xbc, 0x24, 0x5a, 0x95, 0x82, 0x62, 0xb7, 0xbe, 0x08, 0x3c, 0x17, 0x09, 0x2e, 0x8b, - 0x71, 0x8b, 0x00, 0x6c, 0x0b, 0xc0, 0xb4, 0x26, 0xc6, 0xdc, 0x09, 0x11, 0x7d, 0x45, 0x0c, 0xa0, - 0x04, 0x63, 0xb7, 0xa0, 0x3c, 0x9f, 0x61, 0x2f, 0x9f, 0x19, 0x4e, 0xe3, 0xaa, 0x20, 0x48, 0x40, - 0x58, 0x3a, 0xae, 0x73, 0xc4, 0x5e, 0x13, 0xb3, 0x1b, 0x01, 0x70, 0xaf, 0xd8, 0xc1, 0xb6, 0xed, - 0x36, 0x1a, 0xb4, 0x4e, 0x79, 0x82, 0xdd, 0x84, 0x6c, 0xe0, 0x8f, 0x1b, 0xd7, 0xa9, 0x97, 0xc0, - 0x7b, 0xd9, 0x3e, 0x9b, 0xf9, 0x1a, 0x82, 0xb7, 0x8b, 0x90, 0xa7, 0x3d, 0xa3, 0xde, 0x84, 0xd2, - 0x81, 0xe1, 0x1b, 0x53, 0xcd, 0x9a, 0x30, 0x05, 0xb2, 0x33, 0x2f, 0x10, 0xbb, 0x05, 0x3f, 0xd5, - 0x2e, 0x14, 0x9e, 0x19, 0x3e, 0xe2, 0x18, 0xe4, 0x5c, 0x63, 0x6a, 0x11, 0xb2, 0xac, 0xd1, 0x37, - 0xee, 0x90, 0xe0, 0x3c, 0x08, 0xad, 0xa9, 0x60, 0x05, 0x22, 0x85, 0xf0, 0x23, 0xc7, 0x1b, 0x89, - 0x9d, 0x50, 0xd2, 0x44, 0x4a, 0xfd, 0x3b, 0x19, 0x28, 0xb4, 0x3c, 0x07, 0x8b, 0xbb, 0x06, 0x45, - 0xdf, 0x72, 0xf4, 0xa4, 0xba, 0x82, 0x6f, 0x39, 0x07, 0x5e, 0x80, 0x88, 0xb1, 0xc7, 0x11, 0x7c, - 0x6f, 0x16, 0xc6, 0x1e, 0x21, 0xa2, 0x06, 0x64, 0xa5, 0x06, 0x5c, 0x87, 0x52, 0x38, 0x72, 0x74, - 0x82, 0xe7, 0x08, 0x5e, 0x0c, 0x47, 0x4e, 0x0f, 0x51, 0xd7, 0xa0, 0x68, 0x8e, 0x38, 0x26, 0x4f, - 0x98, 0x82, 0x39, 0x42, 0x84, 0xfa, 0x29, 0x94, 0x35, 0xe3, 0x54, 0x34, 0xe3, 0x0a, 0x14, 0xb0, - 0x00, 0xc1, 0xe5, 0x72, 0x5a, 0x3e, 0x1c, 0x39, 0x1d, 0x13, 0xc1, 0xd8, 0x08, 0xdb, 0xa4, 0x36, - 0xe4, 0xb4, 0xfc, 0xd8, 0x73, 0x3a, 0xa6, 0x3a, 0x04, 0x68, 0x79, 0xbe, 0xff, 0x9d, 0xbb, 0x70, - 0x19, 0xf2, 0xa6, 0x35, 0x0b, 0x8f, 0x39, 0x83, 0xd0, 0x78, 0x42, 0xbd, 0x0f, 0x25, 0x9c, 0x97, - 0xae, 0x1d, 0x84, 0xec, 0x16, 0xe4, 0x1c, 0x3b, 0x08, 0x1b, 0x99, 0xad, 0xec, 0xc2, 0xac, 0x11, - 0x5c, 0xdd, 0x82, 0xd2, 0xbe, 0x71, 0xf6, 0x0c, 0x67, 0x0e, 0x4b, 0xa3, 0x29, 0x14, 0x53, 0x22, - 0xe6, 0xb3, 0x0a, 0x30, 0x34, 0xfc, 0x23, 0x2b, 0x24, 0x7e, 0xf6, 0x37, 0x19, 0xa8, 0x0c, 0xe6, - 0xa3, 0x2f, 0xe7, 0x96, 0x7f, 0x8e, 0x6d, 0xbe, 0x07, 0xd9, 0xf0, 0x7c, 0x46, 0x39, 0xea, 0x0f, - 0xaf, 0xf2, 0xe2, 0x25, 0xfc, 0x03, 0xcc, 0xa4, 0x21, 0x09, 0x76, 0xc2, 0xf5, 0x4c, 0x2b, 0x1a, - 0x83, 0xbc, 0x56, 0xc0, 0x64, 0xc7, 0x44, 0xa1, 0xe0, 0xcd, 0xc4, 0x2c, 0xac, 0x7b, 0x33, 0xb6, - 0x05, 0xf9, 0xf1, 0xb1, 0xed, 0x98, 0x34, 0x01, 0xe9, 0x36, 0x73, 0x04, 0xce, 0x92, 0xef, 0x9d, - 0xea, 0x81, 0xfd, 0x55, 0xc4, 0xe4, 0x8b, 0xbe, 0x77, 0x3a, 0xb0, 0xbf, 0xb2, 0xd4, 0xa1, 0x90, - 0x34, 0x00, 0x85, 0x41, 0xab, 0xd9, 0x6d, 0x6a, 0xca, 0x1a, 0x7e, 0xb7, 0x3f, 0xeb, 0x0c, 0x86, - 0x03, 0x25, 0xc3, 0xea, 0x00, 0xbd, 0xfe, 0x50, 0x17, 0xe9, 0x75, 0x56, 0x80, 0xf5, 0x4e, 0x4f, - 0xc9, 0x22, 0x0d, 0xc2, 0x3b, 0x3d, 0x25, 0xc7, 0x8a, 0x90, 0x6d, 0xf6, 0x3e, 0x57, 0xf2, 0xf4, - 0xd1, 0xed, 0x2a, 0x05, 0xf5, 0x8f, 0xd6, 0xa1, 0xdc, 0x1f, 0x7d, 0x61, 0x8d, 0x43, 0xec, 0x33, - 0xae, 0x52, 0xcb, 0x7f, 0x61, 0xf9, 0xd4, 0xed, 0xac, 0x26, 0x52, 0xd8, 0x11, 0x73, 0x44, 0x9d, - 0xcb, 0x6a, 0xeb, 0xe6, 0x88, 0xe8, 0xc6, 0xc7, 0xd6, 0xd4, 0xa0, 0xce, 0x21, 0x1d, 0xa5, 0x70, - 0x57, 0x78, 0xa3, 0x2f, 0xa8, 0x7b, 0x59, 0x0d, 0x3f, 0xd9, 0x6d, 0xa8, 0xf0, 0x32, 0xe4, 0xf5, - 0x05, 0x1c, 0xb4, 0xb8, 0xf8, 0x0a, 0xf2, 0xe2, 0xa3, 0x9c, 0x54, 0x2a, 0x47, 0x0a, 0x09, 0xc6, - 0x41, 0x3d, 0xb1, 0xa2, 0xbd, 0xd1, 0x17, 0x1c, 0x5b, 0xe2, 0x2b, 0xda, 0x1b, 0x7d, 0x41, 0xa8, - 0xb7, 0x61, 0x33, 0x98, 0x8f, 0x82, 0xb1, 0x6f, 0xcf, 0x42, 0xdb, 0x73, 0x39, 0x4d, 0x99, 0x68, - 0x14, 0x19, 0x41, 0xc4, 0xf7, 0xa0, 0x34, 0x9b, 0x8f, 0x74, 0xdb, 0x9d, 0x78, 0xc4, 0xdc, 0x2b, - 0x0f, 0x6b, 0x7c, 0x62, 0x0e, 0xe6, 0xa3, 0x8e, 0x3b, 0xf1, 0xb4, 0xe2, 0x8c, 0x7f, 0xa8, 0x6f, - 0x40, 0x51, 0xc0, 0x50, 0x7a, 0x87, 0x96, 0x6b, 0xb8, 0xa1, 0x1e, 0x8b, 0xfd, 0x12, 0x07, 0x74, - 0x4c, 0xf5, 0x4f, 0x32, 0xa0, 0x0c, 0xa4, 0x6a, 0xf6, 0xad, 0xd0, 0x58, 0xc9, 0x15, 0x5e, 0x05, - 0x30, 0xc6, 0x63, 0x6f, 0xce, 0x8b, 0xe1, 0x8b, 0xa7, 0x2c, 0x20, 0x1d, 0x53, 0x1e, 0x9b, 0x6c, - 0x6a, 0x6c, 0xee, 0x40, 0x35, 0xca, 0x27, 0x6d, 0xe8, 0x8a, 0x80, 0x45, 0xa3, 0x13, 0xcc, 0x53, - 0xbb, 0xba, 0x18, 0xcc, 0x79, 0xee, 0xab, 0x50, 0x20, 0x1d, 0x21, 0x88, 0x46, 0x9c, 0xa7, 0xd4, - 0xbf, 0xbf, 0x0e, 0xa5, 0xdd, 0xb9, 0x3b, 0xc6, 0x26, 0xb3, 0xd7, 0x20, 0x37, 0x99, 0xbb, 0x63, - 0x6a, 0x6e, 0x2c, 0x32, 0xe2, 0x95, 0xa2, 0x11, 0x12, 0xf7, 0xa0, 0xe1, 0x1f, 0xe1, 0xde, 0x5d, - 0xda, 0x83, 0x08, 0x57, 0xff, 0x59, 0x86, 0x97, 0xb8, 0xeb, 0x18, 0x47, 0xac, 0x04, 0xb9, 0x5e, - 0xbf, 0xd7, 0x56, 0xd6, 0x58, 0x15, 0x4a, 0x9d, 0xde, 0xb0, 0xad, 0xf5, 0x9a, 0x5d, 0x25, 0x43, - 0x0b, 0x7a, 0xd8, 0xdc, 0xee, 0xb6, 0x95, 0x75, 0xc4, 0x3c, 0xeb, 0x77, 0x9b, 0xc3, 0x4e, 0xb7, - 0xad, 0xe4, 0x38, 0x46, 0xeb, 0xb4, 0x86, 0x4a, 0x89, 0x29, 0x50, 0x3d, 0xd0, 0xfa, 0x3b, 0x87, - 0xad, 0xb6, 0xde, 0x3b, 0xec, 0x76, 0x15, 0x85, 0x5d, 0x82, 0x8d, 0x18, 0xd2, 0xe7, 0xc0, 0x2d, - 0xcc, 0xf2, 0xac, 0xa9, 0x35, 0xb5, 0x27, 0xca, 0x8f, 0x58, 0x09, 0xb2, 0xcd, 0x27, 0x4f, 0x94, - 0x9f, 0xe1, 0xde, 0x28, 0x3f, 0xef, 0xf4, 0xf4, 0x67, 0xcd, 0xee, 0x61, 0x5b, 0xf9, 0xd9, 0x7a, - 0x94, 0xee, 0x6b, 0x3b, 0x6d, 0x4d, 0xf9, 0x59, 0x8e, 0x6d, 0x42, 0xf5, 0xa7, 0xfd, 0x5e, 0x7b, - 0xbf, 0x79, 0x70, 0x40, 0x0d, 0xf9, 0x59, 0x49, 0xfd, 0xef, 0x39, 0xc8, 0x61, 0x4f, 0x98, 0x9a, - 0xf0, 0x81, 0xb8, 0x8b, 0xb8, 0x11, 0xb7, 0x73, 0x7f, 0xf6, 0x17, 0xb7, 0xd7, 0x38, 0x07, 0xb8, - 0x03, 0x59, 0xc7, 0x0e, 0x69, 0x02, 0xe3, 0xd5, 0x23, 0x74, 0xa3, 0xbd, 0x35, 0x0d, 0x71, 0xec, - 0x16, 0x64, 0x38, 0x2b, 0xa8, 0x3c, 0xac, 0x8b, 0xe5, 0x25, 0x64, 0xc9, 0xde, 0x9a, 0x96, 0x99, - 0xb1, 0x9b, 0x90, 0x79, 0x21, 0xf8, 0x42, 0x95, 0xe3, 0xb9, 0x34, 0x41, 0xec, 0x0b, 0xb6, 0x05, - 0xd9, 0xb1, 0xc7, 0x35, 0x9f, 0x18, 0xcf, 0x79, 0x2b, 0x96, 0x3f, 0xf6, 0x1c, 0xf6, 0x1a, 0x64, - 0x7d, 0xe3, 0x94, 0x66, 0x34, 0x9e, 0xae, 0x98, 0x79, 0x23, 0x91, 0x6f, 0x9c, 0x62, 0x23, 0x26, - 0xb4, 0x93, 0xe2, 0x46, 0x44, 0xf3, 0x8d, 0xd5, 0x4c, 0xd8, 0x16, 0x64, 0x4e, 0x69, 0x2f, 0xc5, - 0xc2, 0xfe, 0xb9, 0xed, 0x9a, 0xde, 0xe9, 0x60, 0x66, 0x8d, 0x91, 0xe2, 0x94, 0xbd, 0x0e, 0xd9, - 0x60, 0x3e, 0xa2, 0xbd, 0x54, 0x79, 0xb8, 0xb9, 0xc4, 0x15, 0xb1, 0xa2, 0x60, 0x3e, 0x62, 0x6f, - 0x40, 0x6e, 0xec, 0xf9, 0xbe, 0xd8, 0x4f, 0x4a, 0xd4, 0xe0, 0x48, 0x20, 0xa0, 0xf2, 0x83, 0x78, - 0xac, 0x30, 0x24, 0xdd, 0x29, 0x26, 0x4a, 0x38, 0x32, 0x56, 0x18, 0xb2, 0xbb, 0x82, 0xcd, 0x57, - 0xe5, 0x56, 0x47, 0x42, 0x00, 0xcb, 0x41, 0x2c, 0x4e, 0xd2, 0xd4, 0x38, 0x23, 0xcd, 0x2a, 0x26, - 0x8a, 0xb8, 0x3f, 0xb6, 0x69, 0x6a, 0x9c, 0xb1, 0xbb, 0x90, 0x7d, 0x61, 0x8d, 0x49, 0xc9, 0x8a, - 0x6b, 0x13, 0x93, 0xf4, 0x8c, 0xba, 0x87, 0x68, 0x5a, 0xf7, 0x9e, 0x63, 0x92, 0xbe, 0x15, 0xcf, - 0xe5, 0xae, 0xe7, 0x98, 0xcf, 0x68, 0x2e, 0x09, 0x89, 0x42, 0xcf, 0x98, 0x9f, 0xe1, 0x9e, 0x55, - 0xb8, 0x78, 0x32, 0xe6, 0x67, 0x1d, 0x13, 0xd9, 0x9f, 0x6b, 0xbe, 0x20, 0x2d, 0x2b, 0xa3, 0xe1, - 0x27, 0x9a, 0x01, 0x81, 0xe5, 0x58, 0xe3, 0xd0, 0x7e, 0x61, 0x87, 0xe7, 0xa4, 0x47, 0x65, 0x34, - 0x19, 0xb4, 0x5d, 0x80, 0x9c, 0x75, 0x36, 0xf3, 0xd5, 0x3d, 0x28, 0x8a, 0x5a, 0x96, 0x6c, 0x89, - 0xeb, 0x50, 0xb2, 0x03, 0x7d, 0xec, 0xb9, 0x41, 0x28, 0xb4, 0x87, 0xa2, 0x1d, 0xb4, 0x30, 0x89, - 0x4c, 0xc5, 0x34, 0x42, 0xce, 0x86, 0xab, 0x1a, 0x7d, 0xab, 0x0f, 0x01, 0x92, 0x6e, 0x61, 0x9b, - 0x1c, 0xcb, 0x8d, 0x14, 0x15, 0xc7, 0x72, 0xe3, 0x3c, 0xeb, 0x52, 0x9e, 0xeb, 0x50, 0x8e, 0x35, - 0x40, 0x56, 0x85, 0x8c, 0x21, 0x04, 0x40, 0xc6, 0x50, 0xef, 0xa1, 0x42, 0x16, 0xe9, 0x78, 0x69, - 0x1c, 0xa6, 0x22, 0xb1, 0x90, 0x19, 0xa9, 0xdf, 0x87, 0xaa, 0x66, 0x05, 0x73, 0x27, 0x6c, 0x79, - 0xce, 0x8e, 0x35, 0x61, 0xef, 0x00, 0xc4, 0xe9, 0x40, 0xc8, 0xe9, 0x64, 0xed, 0xee, 0x58, 0x13, - 0x4d, 0xc2, 0xab, 0xff, 0x28, 0x47, 0x1a, 0xcf, 0x0e, 0x57, 0x35, 0x84, 0x4e, 0x91, 0x91, 0x74, - 0x8a, 0x98, 0x83, 0xae, 0xa7, 0xf5, 0xaa, 0x63, 0xdb, 0x34, 0x2d, 0x37, 0xd2, 0x9f, 0x78, 0x0a, - 0x27, 0xdb, 0x70, 0x8e, 0x68, 0x43, 0xd5, 0x1f, 0xb2, 0xa8, 0xd2, 0xe9, 0xcc, 0xb7, 0x82, 0x80, - 0x4b, 0x6e, 0xc3, 0x39, 0x8a, 0xf6, 0x76, 0xfe, 0xeb, 0xf6, 0xf6, 0x75, 0x28, 0xb9, 0x5e, 0xa8, - 0x93, 0x75, 0x53, 0xe0, 0xa3, 0x2f, 0xcc, 0x38, 0xf6, 0x26, 0x14, 0x85, 0x5e, 0x2a, 0x36, 0x95, - 0x58, 0x2e, 0x3b, 0x1c, 0xa8, 0x45, 0x58, 0xd6, 0x40, 0x35, 0x67, 0x3a, 0xb5, 0xdc, 0x30, 0x92, - 0x54, 0x22, 0xc9, 0xde, 0x86, 0xb2, 0xe7, 0xea, 0x5c, 0x79, 0x15, 0xbb, 0x4a, 0x2c, 0xdf, 0xbe, - 0x7b, 0x48, 0x50, 0xad, 0xe4, 0x89, 0x2f, 0x6c, 0x8a, 0xe3, 0x9d, 0xea, 0x63, 0xc3, 0x37, 0x69, - 0x67, 0x95, 0xb4, 0xa2, 0xe3, 0x9d, 0xb6, 0x0c, 0xdf, 0xe4, 0x92, 0xfb, 0x4b, 0x77, 0x3e, 0xa5, - 0xdd, 0x54, 0xd3, 0x44, 0x8a, 0xdd, 0x84, 0xf2, 0xd8, 0x99, 0x07, 0xa1, 0xe5, 0x6f, 0x9f, 0x73, - 0x73, 0x44, 0x4b, 0x00, 0xd8, 0xae, 0x99, 0x6f, 0x4f, 0x0d, 0xff, 0x9c, 0xb6, 0x4e, 0x49, 0x8b, - 0x92, 0xa8, 0x31, 0xcd, 0x4e, 0x6c, 0xf3, 0x8c, 0xdb, 0x24, 0x1a, 0x4f, 0x20, 0xfd, 0x31, 0x59, - 0x8c, 0x01, 0xed, 0x8f, 0x92, 0x16, 0x25, 0x69, 0x1e, 0xe8, 0x93, 0x76, 0x44, 0x59, 0x13, 0xa9, - 0x94, 0xda, 0xb9, 0x79, 0xa1, 0xda, 0xc9, 0x16, 0x25, 0xbf, 0xe7, 0xdb, 0x47, 0xb6, 0x90, 0xdb, - 0x97, 0xb8, 0xe4, 0xe7, 0x20, 0xd2, 0x4b, 0xbf, 0x84, 0xa2, 0x18, 0x62, 0x94, 0x40, 0xb8, 0x7d, - 0xd2, 0xec, 0x99, 0x4b, 0x20, 0x84, 0xb3, 0xd7, 0xa0, 0x26, 0xca, 0x0a, 0x42, 0xdf, 0x76, 0x8f, - 0xc4, 0xe2, 0xa9, 0x72, 0xe0, 0x80, 0x60, 0x28, 0x4e, 0x71, 0x7a, 0x75, 0x63, 0x64, 0x3b, 0xb8, - 0x4d, 0xb3, 0xc2, 0x5a, 0x9f, 0x3b, 0x4e, 0x93, 0x83, 0xd4, 0x3e, 0x94, 0xa2, 0x09, 0xf9, 0x95, - 0xd4, 0xa9, 0xfe, 0x76, 0x06, 0x2a, 0x1d, 0xd7, 0xb4, 0xce, 0xfa, 0xa4, 0x22, 0xb0, 0x77, 0x80, - 0x8d, 0x7d, 0xcb, 0x08, 0x2d, 0xdd, 0x3a, 0x0b, 0x7d, 0x43, 0xe7, 0x26, 0x3d, 0x37, 0xa7, 0x15, - 0x8e, 0x69, 0x23, 0x62, 0x48, 0xd6, 0xfd, 0x6d, 0xa8, 0xcc, 0x0c, 0x3f, 0x88, 0xd4, 0x2a, 0x5e, - 0x01, 0x70, 0x90, 0x50, 0x6a, 0x14, 0xf7, 0xc8, 0x37, 0xa6, 0x7a, 0xe8, 0x9d, 0x58, 0x2e, 0x57, - 0x28, 0xb9, 0x2a, 0x5d, 0x27, 0xf8, 0x10, 0xc1, 0xa4, 0x57, 0xfe, 0xa7, 0x0c, 0xd4, 0x0e, 0xf8, - 0xac, 0x3f, 0xb5, 0xce, 0x77, 0xb8, 0xfd, 0x32, 0x8e, 0x76, 0x6c, 0x4e, 0xa3, 0x6f, 0x76, 0x0b, - 0x2a, 0xb3, 0x13, 0xeb, 0x5c, 0x4f, 0xe9, 0xfa, 0x65, 0x04, 0xb5, 0x68, 0x6f, 0xbe, 0x05, 0x05, - 0x8f, 0x3a, 0x22, 0x64, 0x9c, 0x10, 0x0d, 0x52, 0x0f, 0x35, 0x41, 0xc0, 0x54, 0xa8, 0xc5, 0x45, - 0xc9, 0xda, 0x8b, 0x28, 0x8c, 0x9a, 0x7f, 0x19, 0xf2, 0x88, 0x0a, 0x1a, 0xf9, 0xad, 0x2c, 0x2a, - 0xec, 0x94, 0x60, 0xef, 0x43, 0x6d, 0xec, 0x4d, 0x67, 0x7a, 0x94, 0x5d, 0x48, 0xbb, 0x34, 0x4f, - 0xa9, 0x20, 0xc9, 0x01, 0x2f, 0x4b, 0xfd, 0xbd, 0x2c, 0x94, 0xa8, 0x0d, 0x82, 0xad, 0xd8, 0xe6, - 0x59, 0xc4, 0x56, 0xca, 0x5a, 0xde, 0x36, 0x91, 0x6b, 0xbf, 0x0a, 0x60, 0x23, 0x89, 0x3c, 0x94, - 0x65, 0x82, 0x44, 0x4d, 0x99, 0x19, 0x7e, 0x18, 0x34, 0xb2, 0xbc, 0x29, 0x94, 0xc0, 0xf5, 0x3e, - 0x77, 0xed, 0x2f, 0xe7, 0xbc, 0xf5, 0x25, 0x4d, 0xa4, 0x70, 0xdc, 0x79, 0x61, 0x34, 0x7f, 0xb2, - 0xfa, 0x55, 0x27, 0x38, 0x4d, 0x5f, 0xb4, 0xca, 0x39, 0x8d, 0x75, 0x86, 0xf2, 0x8d, 0xb3, 0x16, - 0x20, 0x50, 0x1b, 0x21, 0x32, 0xd3, 0x28, 0xa6, 0x99, 0x46, 0x03, 0x8a, 0x2f, 0xec, 0xc0, 0xc6, - 0x05, 0x52, 0xe2, 0xdb, 0x50, 0x24, 0xa5, 0x69, 0x28, 0xbf, 0x6c, 0x1a, 0xe2, 0x6e, 0x1b, 0xce, - 0x11, 0x57, 0x7c, 0xa3, 0x6e, 0x37, 0x9d, 0x23, 0x8f, 0x7d, 0x00, 0x57, 0x12, 0xb4, 0xe8, 0x0d, - 0xb9, 0x81, 0xc8, 0xd3, 0xa1, 0xb1, 0x98, 0x92, 0x7a, 0x44, 0x96, 0xc9, 0x7d, 0xd8, 0x94, 0xb2, - 0xcc, 0x50, 0xbd, 0x09, 0x88, 0xe7, 0x94, 0xb5, 0x8d, 0x98, 0x9c, 0xb4, 0x9e, 0x40, 0xfd, 0xd7, - 0xeb, 0x50, 0xdb, 0xf5, 0x7c, 0xcb, 0x3e, 0x72, 0x93, 0x55, 0xb7, 0xa4, 0x1f, 0x47, 0x2b, 0x71, - 0x5d, 0x5a, 0x89, 0xb7, 0xa1, 0x32, 0xe1, 0x19, 0xf5, 0x70, 0xc4, 0xcd, 0xe6, 0x9c, 0x06, 0x02, - 0x34, 0x1c, 0x39, 0xb8, 0x9b, 0x23, 0x02, 0xca, 0x9c, 0xa3, 0xcc, 0x51, 0x26, 0x94, 0x35, 0xec, - 0x7b, 0xc4, 0x75, 0x4d, 0xcb, 0xb1, 0x42, 0x3e, 0x3d, 0xf5, 0x87, 0xaf, 0x46, 0x92, 0x5e, 0x6a, - 0xd3, 0x03, 0xcd, 0x9a, 0x34, 0x49, 0x3d, 0x42, 0x26, 0xbc, 0x43, 0xe4, 0x22, 0xaf, 0xe0, 0xd8, - 0x85, 0x6f, 0x98, 0x97, 0x73, 0x0e, 0x75, 0x08, 0xe5, 0x18, 0x8c, 0xba, 0xae, 0xd6, 0x16, 0xfa, - 0xed, 0x1a, 0xab, 0x40, 0xb1, 0xd5, 0x1c, 0xb4, 0x9a, 0x3b, 0x6d, 0x25, 0x83, 0xa8, 0x41, 0x7b, - 0xc8, 0x75, 0xda, 0x75, 0xb6, 0x01, 0x15, 0x4c, 0xed, 0xb4, 0x77, 0x9b, 0x87, 0xdd, 0xa1, 0x92, - 0x65, 0x35, 0x28, 0xf7, 0xfa, 0x7a, 0xb3, 0x35, 0xec, 0xf4, 0x7b, 0x4a, 0x4e, 0xfd, 0x11, 0x94, - 0x5a, 0xc7, 0xd6, 0xf8, 0xe4, 0xa2, 0x51, 0x24, 0xb3, 0xd3, 0x1a, 0x9f, 0x08, 0xfd, 0x74, 0xc1, - 0xec, 0xb4, 0xc6, 0x27, 0xea, 0x33, 0xa8, 0xb6, 0x22, 0xa1, 0x70, 0x51, 0x29, 0x0f, 0xa1, 0x4e, - 0x9b, 0x6f, 0x3c, 0x8a, 0x76, 0xdf, 0xfa, 0x8a, 0xdd, 0x57, 0x45, 0x9a, 0xd6, 0x48, 0x6c, 0xbf, - 0x8f, 0xa0, 0x72, 0xe0, 0x7b, 0x33, 0xcb, 0x0f, 0xa9, 0x58, 0x05, 0xb2, 0x27, 0xd6, 0xb9, 0x28, - 0x15, 0x3f, 0x13, 0xc3, 0x7c, 0x5d, 0x36, 0xcc, 0x1f, 0x42, 0x29, 0xca, 0xf6, 0x8d, 0xf3, 0xfc, - 0x10, 0xb9, 0x18, 0xe5, 0xb1, 0xad, 0x00, 0x2b, 0x7b, 0x00, 0x30, 0x8b, 0x01, 0x42, 0xfb, 0x88, - 0x34, 0x6f, 0x51, 0xb8, 0x26, 0x51, 0xa8, 0x7f, 0x93, 0x85, 0xfa, 0x81, 0xe1, 0x87, 0x36, 0x4e, - 0x0e, 0x1f, 0x86, 0x37, 0x21, 0x47, 0x4b, 0x9e, 0xfb, 0x00, 0x2e, 0xc5, 0x6a, 0x3b, 0xa7, 0x21, - 0x35, 0x82, 0x08, 0xd8, 0xf7, 0xa0, 0x3e, 0x8b, 0xc0, 0x3a, 0xc9, 0x06, 0x3e, 0x36, 0x8b, 0x59, - 0x68, 0xcc, 0x6b, 0x33, 0x39, 0xc9, 0x7e, 0x00, 0x97, 0xd3, 0x79, 0xad, 0x20, 0x48, 0xf8, 0xa8, - 0x3c, 0x59, 0x97, 0x52, 0x19, 0x39, 0x19, 0x6b, 0xc1, 0x66, 0x92, 0x7d, 0xec, 0x39, 0xf3, 0xa9, - 0x1b, 0x08, 0x3b, 0xe2, 0xea, 0x42, 0xed, 0x2d, 0x8e, 0xd5, 0x94, 0xd9, 0x02, 0x84, 0xa9, 0x50, - 0x8d, 0x61, 0xbd, 0xf9, 0x94, 0xb6, 0x44, 0x4e, 0x4b, 0xc1, 0xd8, 0x23, 0x80, 0x38, 0x8d, 0x96, - 0x63, 0x76, 0x45, 0xff, 0x3a, 0xa1, 0x35, 0xd5, 0x24, 0x32, 0x54, 0x3f, 0x90, 0x19, 0xf8, 0x76, - 0x78, 0x3c, 0x25, 0x2e, 0x96, 0xd5, 0x12, 0x00, 0x31, 0xcb, 0x40, 0x47, 0x33, 0x35, 0xce, 0x22, - 0x18, 0x5a, 0xdd, 0x0e, 0x06, 0xf3, 0x51, 0x5c, 0x2e, 0x8a, 0xd4, 0xa4, 0x97, 0xd3, 0xe0, 0x48, - 0x18, 0xf3, 0x49, 0x0b, 0xf7, 0x83, 0x23, 0xf6, 0x10, 0xae, 0x24, 0x44, 0x09, 0xff, 0x0d, 0x1a, - 0x40, 0x9c, 0x3b, 0x19, 0xbe, 0x98, 0x09, 0x07, 0xea, 0x8f, 0xa1, 0x96, 0x9a, 0x9d, 0x97, 0x0a, - 0xf7, 0xeb, 0x50, 0xc2, 0xff, 0x28, 0xda, 0xc5, 0x02, 0x2c, 0x62, 0x7a, 0x10, 0xfa, 0xaa, 0x05, - 0xca, 0xe2, 0x58, 0xb3, 0xbb, 0xe4, 0xe0, 0xa2, 0x49, 0x59, 0x76, 0x54, 0x45, 0x28, 0xf6, 0xf6, - 0xaa, 0x49, 0x5c, 0xa7, 0x56, 0x2f, 0x4d, 0x96, 0xfa, 0x07, 0xeb, 0x52, 0x9b, 0x71, 0xc4, 0xd9, - 0xeb, 0xf2, 0xf2, 0x93, 0x36, 0x6e, 0x32, 0x66, 0x24, 0x71, 0xde, 0x02, 0xc5, 0xf3, 0x4d, 0xdb, - 0x35, 0xc8, 0xe1, 0xc6, 0x87, 0x7b, 0x9d, 0xb4, 0xc5, 0x0d, 0x01, 0x3f, 0x10, 0x60, 0xb4, 0x5b, - 0x4c, 0x2b, 0xf6, 0x5f, 0x08, 0xef, 0x83, 0x0c, 0x92, 0xa5, 0x53, 0x2e, 0x2d, 0x9d, 0xde, 0x84, - 0xb2, 0x63, 0x05, 0x81, 0x1e, 0x1e, 0x1b, 0x2e, 0xc9, 0xef, 0x74, 0xa7, 0x4b, 0x88, 0x1c, 0x1e, - 0x1b, 0x2e, 0x12, 0xda, 0xae, 0x2e, 0x4e, 0x28, 0x0a, 0xcb, 0x84, 0xb6, 0x4b, 0xf6, 0x1b, 0xca, - 0xfd, 0xcb, 0xab, 0x26, 0x56, 0x88, 0x45, 0xb6, 0x3c, 0xaf, 0xea, 0xab, 0x50, 0x7c, 0x66, 0x5b, - 0xa7, 0x82, 0x97, 0xbd, 0xb0, 0xad, 0xd3, 0x88, 0x97, 0xe1, 0xb7, 0xfa, 0x57, 0x25, 0x28, 0x11, - 0xf1, 0xce, 0xc5, 0x8e, 0xcd, 0x6f, 0x63, 0x6d, 0x6c, 0x09, 0x39, 0x95, 0x5b, 0x61, 0xe3, 0x70, - 0xa9, 0xf5, 0x2a, 0x80, 0x24, 0x43, 0xb9, 0x46, 0x50, 0x0e, 0x63, 0xd1, 0x89, 0x6a, 0x3a, 0xe9, - 0x78, 0xc1, 0x97, 0x8e, 0xf0, 0xca, 0x24, 0x00, 0xf6, 0x80, 0x2b, 0xd1, 0xe4, 0x8f, 0x29, 0xca, - 0x8c, 0x85, 0xfa, 0x10, 0x99, 0xf0, 0xa4, 0x59, 0x63, 0x82, 0xf4, 0x03, 0xcb, 0x0f, 0xa2, 0xed, - 0x54, 0xd3, 0xa2, 0x24, 0x72, 0x34, 0x54, 0x9e, 0x84, 0xc9, 0x1d, 0x6d, 0x5f, 0x59, 0xfb, 0xd3, - 0x88, 0x80, 0xdd, 0x83, 0x22, 0x89, 0x6c, 0x0b, 0x25, 0xb8, 0xc4, 0x3a, 0x23, 0x65, 0x4a, 0x8b, - 0xd0, 0xec, 0x2d, 0xc8, 0x4f, 0x4e, 0xac, 0xf3, 0xa0, 0x51, 0x93, 0x59, 0x42, 0x4a, 0x16, 0x6a, - 0x9c, 0x82, 0xdd, 0x85, 0xba, 0x6f, 0x4d, 0x74, 0x72, 0x75, 0xa2, 0xf0, 0x0e, 0x1a, 0x75, 0x92, - 0xcd, 0x55, 0xdf, 0x9a, 0xb4, 0x10, 0x38, 0x1c, 0x39, 0x01, 0x7b, 0x03, 0x0a, 0x24, 0x95, 0xd0, - 0xc6, 0x90, 0x6a, 0x8e, 0x44, 0x9c, 0x26, 0xb0, 0xec, 0x21, 0x94, 0x13, 0xb6, 0x71, 0x85, 0x3a, - 0x74, 0x79, 0x81, 0x1f, 0x11, 0x1b, 0xd7, 0x12, 0x32, 0xf6, 0x01, 0x80, 0xb0, 0x7e, 0xf4, 0xd1, - 0x39, 0x1d, 0x1e, 0x54, 0x62, 0xeb, 0x50, 0x12, 0x80, 0xb2, 0x8d, 0xf4, 0x26, 0xe4, 0x51, 0x4a, - 0x04, 0x8d, 0x6b, 0xd4, 0x9a, 0xcd, 0xb4, 0x08, 0xa1, 0xde, 0x11, 0x9e, 0xdd, 0x83, 0x12, 0x2e, - 0x2e, 0x1d, 0xa7, 0xb0, 0x21, 0x9b, 0x83, 0x62, 0x25, 0xa2, 0x96, 0x66, 0x9d, 0x0e, 0xbe, 0x74, - 0xd8, 0x7d, 0xc8, 0x99, 0xd6, 0x24, 0x68, 0x5c, 0xa7, 0x12, 0xaf, 0x4a, 0x73, 0x89, 0x8a, 0xc3, - 0x8e, 0x35, 0xe1, 0xa2, 0x05, 0x69, 0xd8, 0x1e, 0xd4, 0x71, 0xe9, 0x3d, 0x24, 0xc5, 0x1b, 0x87, - 0xbc, 0x71, 0x83, 0x72, 0xdd, 0x59, 0xc8, 0xd5, 0x13, 0x44, 0x34, 0x41, 0x6d, 0x37, 0xf4, 0xcf, - 0xb5, 0x9a, 0x2b, 0xc3, 0xd8, 0x0d, 0x28, 0xd9, 0x41, 0xd7, 0x1b, 0x9f, 0x58, 0x66, 0xe3, 0x15, - 0x7e, 0xde, 0x18, 0xa5, 0xd9, 0xa7, 0x50, 0xa3, 0xc5, 0x88, 0x49, 0xac, 0xbc, 0x71, 0x53, 0x16, - 0x79, 0x43, 0x19, 0xa5, 0xa5, 0x29, 0x51, 0xdd, 0xb2, 0x03, 0x3d, 0xb4, 0xa6, 0x33, 0xcf, 0x47, - 0x43, 0xf2, 0x55, 0x6e, 0x3c, 0xd9, 0xc1, 0x30, 0x02, 0x21, 0x9f, 0x8f, 0x8f, 0x3a, 0x75, 0x6f, - 0x32, 0x09, 0xac, 0xb0, 0x71, 0x8b, 0xf6, 0x5a, 0x3d, 0x3a, 0xf1, 0xec, 0x13, 0x94, 0x94, 0xd2, - 0x40, 0x37, 0xcf, 0x5d, 0x63, 0x6a, 0x8f, 0x1b, 0xb7, 0xb9, 0xbd, 0x6a, 0x07, 0x3b, 0x1c, 0x20, - 0x9b, 0x8c, 0x5b, 0x29, 0x93, 0xf1, 0x12, 0xe4, 0xcd, 0x11, 0x6e, 0xe1, 0x3b, 0x54, 0x6c, 0xce, - 0x1c, 0x75, 0xcc, 0x1b, 0x4f, 0xc8, 0x4c, 0xa4, 0x46, 0x7e, 0xb4, 0xa0, 0x0c, 0xa4, 0x56, 0xbf, - 0xa4, 0x35, 0xec, 0xad, 0xc9, 0x3a, 0xc1, 0x76, 0x1e, 0xb2, 0xa6, 0x35, 0xb9, 0xf1, 0x23, 0x60, - 0xcb, 0xc3, 0xfb, 0x32, 0xcd, 0x24, 0x2f, 0x34, 0x93, 0xef, 0xad, 0x3f, 0xce, 0xa8, 0x9f, 0x42, - 0x2d, 0xb5, 0x57, 0x57, 0x6a, 0x58, 0xdc, 0xd2, 0x30, 0xa6, 0xc2, 0x33, 0xc3, 0x13, 0xea, 0xbf, - 0xcd, 0x42, 0x75, 0xcf, 0x08, 0x8e, 0xf7, 0x8d, 0xd9, 0x20, 0x34, 0xc2, 0x00, 0x07, 0xfc, 0xd8, - 0x08, 0x8e, 0xa7, 0xc6, 0x8c, 0x9b, 0x75, 0x19, 0xee, 0x54, 0x12, 0x30, 0xb4, 0xe9, 0x70, 0xaa, - 0x31, 0xd9, 0x77, 0x0f, 0x9e, 0x0a, 0x8f, 0x51, 0x9c, 0x46, 0xe6, 0x10, 0x1c, 0xcf, 0x27, 0x13, - 0xc7, 0x12, 0x4c, 0x2c, 0x4a, 0xb2, 0xbb, 0x50, 0x13, 0x9f, 0x64, 0xd3, 0x9d, 0x89, 0xc3, 0xe7, - 0x34, 0x90, 0x3d, 0x82, 0x8a, 0x00, 0x0c, 0x23, 0x56, 0x56, 0x8f, 0x3d, 0x81, 0x09, 0x42, 0x93, - 0xa9, 0xd8, 0x4f, 0xe0, 0x8a, 0x94, 0xdc, 0xf5, 0xfc, 0xfd, 0xb9, 0x13, 0xda, 0xad, 0x9e, 0x50, - 0xa0, 0x5f, 0x59, 0xca, 0x9e, 0x90, 0x68, 0xab, 0x73, 0xa6, 0x5b, 0xbb, 0x6f, 0xbb, 0x42, 0xbd, - 0x48, 0x03, 0x17, 0xa8, 0x8c, 0x33, 0x62, 0x88, 0x69, 0x2a, 0xe3, 0x0c, 0x97, 0xbf, 0x00, 0xec, - 0x5b, 0xe1, 0xb1, 0x67, 0x92, 0x7a, 0x11, 0x2f, 0xff, 0x81, 0x8c, 0xd2, 0xd2, 0x94, 0x38, 0x9c, - 0xee, 0xdc, 0x71, 0xc6, 0x6e, 0x48, 0x36, 0x54, 0x56, 0x8b, 0x92, 0x28, 0x2c, 0x7c, 0xc3, 0x3d, - 0xb2, 0x82, 0x46, 0x65, 0x2b, 0x7b, 0x2f, 0xa3, 0x89, 0x94, 0xfa, 0xbb, 0xeb, 0x90, 0xe7, 0x33, - 0xf9, 0x0a, 0x94, 0x47, 0x8e, 0x37, 0x3e, 0xd1, 0xdd, 0xf9, 0x34, 0x3a, 0x44, 0x20, 0x00, 0xea, - 0x5b, 0x64, 0xfb, 0x08, 0x8f, 0x5f, 0x46, 0xa3, 0x6f, 0x2c, 0xd2, 0x9b, 0x87, 0x58, 0x57, 0x96, - 0xa0, 0x22, 0x85, 0x8d, 0xf0, 0xbd, 0x53, 0x5a, 0x0d, 0x39, 0x42, 0x44, 0x49, 0x3a, 0xa7, 0x20, - 0xb9, 0x83, 0x99, 0xf2, 0x84, 0x2b, 0x11, 0xa0, 0xe5, 0x86, 0x8b, 0xde, 0xc9, 0xc2, 0x92, 0x77, - 0x92, 0xdd, 0x02, 0xb4, 0xac, 0xc6, 0x56, 0xdf, 0xb5, 0x5a, 0x3d, 0x1a, 0xe1, 0x92, 0x26, 0x41, - 0xd8, 0xc7, 0xf1, 0x5a, 0xa4, 0x1e, 0x09, 0xdf, 0xb1, 0xe0, 0xa8, 0xf2, 0xaa, 0xd5, 0x52, 0x74, - 0xb8, 0x77, 0x90, 0x4d, 0x72, 0x2d, 0x0e, 0x3f, 0xd5, 0x36, 0x80, 0xe6, 0x9d, 0x06, 0x56, 0x48, - 0x5a, 0xd8, 0x35, 0xea, 0x50, 0xea, 0xc0, 0xd0, 0x3b, 0x3d, 0xf0, 0x82, 0x58, 0x3d, 0x5b, 0x5f, - 0xad, 0x9e, 0xa9, 0xef, 0x41, 0x11, 0xe5, 0xae, 0x11, 0x1a, 0xec, 0xae, 0xf0, 0x73, 0x72, 0xbd, - 0x4b, 0x38, 0x7c, 0x93, 0x3a, 0x84, 0xe7, 0xb3, 0x1b, 0xd5, 0x4b, 0x79, 0xee, 0x48, 0xae, 0x8f, - 0x98, 0x7f, 0x8b, 0x02, 0x85, 0x24, 0x7f, 0x05, 0xca, 0xd8, 0x34, 0x3a, 0x69, 0x11, 0x1b, 0xbd, - 0xe4, 0x7b, 0xa7, 0x2d, 0x4c, 0xab, 0xff, 0x39, 0x03, 0x95, 0xbe, 0x6f, 0xa2, 0xe0, 0x18, 0xcc, - 0xac, 0xf1, 0x4b, 0xb5, 0x49, 0x94, 0xfb, 0x9e, 0xe3, 0x18, 0xb1, 0x2e, 0x86, 0x72, 0x3f, 0x02, - 0xb0, 0x0f, 0x20, 0x37, 0x71, 0x8c, 0x23, 0x9a, 0xec, 0xd8, 0xca, 0x94, 0x8a, 0x8f, 0xbe, 0x77, - 0x1d, 0xe3, 0x48, 0x23, 0x52, 0xf5, 0x37, 0xe2, 0xfa, 0xe9, 0xcc, 0x45, 0x3e, 0x69, 0x59, 0xa3, - 0x53, 0xbf, 0x41, 0x4b, 0xc9, 0xb0, 0x12, 0xe4, 0x76, 0xda, 0x83, 0x16, 0xb7, 0x2d, 0xd1, 0xca, - 0x1c, 0xe8, 0xbb, 0x1d, 0x6d, 0x30, 0x54, 0x72, 0x74, 0x8c, 0x48, 0x80, 0x6e, 0x73, 0x30, 0x54, - 0x4a, 0x0c, 0xa0, 0x70, 0xd8, 0xeb, 0xfc, 0xe4, 0xb0, 0xad, 0x28, 0xea, 0xbf, 0xcf, 0x00, 0x24, - 0x07, 0x02, 0xec, 0x6d, 0xa8, 0x9c, 0x52, 0x4a, 0x97, 0x4e, 0x8a, 0xe4, 0x3e, 0x02, 0x47, 0x93, - 0x4e, 0xf2, 0xae, 0x64, 0x62, 0xa0, 0xec, 0x5d, 0x3e, 0x32, 0xaa, 0xcc, 0x12, 0xb1, 0xcd, 0xde, - 0x81, 0x92, 0x87, 0xfd, 0x40, 0xd2, 0xac, 0x2c, 0x78, 0xa5, 0xee, 0x6b, 0x45, 0x8f, 0x27, 0x50, - 0x46, 0x4f, 0xfc, 0xc8, 0x95, 0x14, 0x93, 0xee, 0x22, 0xa8, 0xe5, 0x18, 0xf3, 0xc0, 0xd2, 0x38, - 0x3e, 0x66, 0xbb, 0xf9, 0x84, 0xed, 0xaa, 0x3f, 0x85, 0xfa, 0xc0, 0x98, 0xce, 0x38, 0x73, 0xa6, - 0x8e, 0x31, 0xc8, 0xe1, 0x9a, 0x10, 0x4b, 0x8f, 0xbe, 0x71, 0x8b, 0x1d, 0x58, 0xfe, 0xd8, 0x72, - 0xa3, 0x1d, 0x19, 0x25, 0x91, 0xd9, 0x1e, 0x06, 0xb6, 0x7b, 0xa4, 0x79, 0xa7, 0x51, 0x1c, 0x4f, - 0x94, 0x56, 0xff, 0x71, 0x06, 0x2a, 0x52, 0x33, 0xd8, 0x7b, 0x29, 0x8b, 0xf2, 0x95, 0xa5, 0x76, - 0xf2, 0x6f, 0xc9, 0xb2, 0x7c, 0x03, 0xf2, 0x41, 0x68, 0xf8, 0xd1, 0xd9, 0x92, 0x22, 0xe5, 0xd8, - 0xf6, 0xe6, 0xae, 0xa9, 0x71, 0x34, 0x53, 0x21, 0x6b, 0xb9, 0xa6, 0x30, 0x1a, 0x97, 0xa9, 0x10, - 0xa9, 0x6e, 0x41, 0x39, 0x2e, 0x1e, 0x97, 0x80, 0xd6, 0x7f, 0x3e, 0x50, 0xd6, 0x58, 0x19, 0xf2, - 0x5a, 0xb3, 0xf7, 0xa4, 0xad, 0x64, 0xd4, 0x3f, 0xc9, 0x00, 0x24, 0xb9, 0xd8, 0x83, 0x54, 0x6b, - 0x6f, 0x2c, 0x96, 0xfa, 0x80, 0xfe, 0x4a, 0x8d, 0xbd, 0x09, 0xe5, 0xb9, 0x4b, 0x40, 0xcb, 0x14, - 0x72, 0x27, 0x01, 0xb0, 0x9b, 0x90, 0x8d, 0x22, 0x7e, 0x16, 0xa2, 0x2c, 0x5e, 0x18, 0x8e, 0xfa, - 0x3d, 0x28, 0xc7, 0xc5, 0xb1, 0x1a, 0x94, 0x77, 0xfb, 0xdd, 0x6e, 0xff, 0x79, 0xa7, 0xf7, 0x44, - 0x59, 0xc3, 0xe4, 0x81, 0xd6, 0x6e, 0xb5, 0x77, 0x30, 0x99, 0xc1, 0x35, 0xdb, 0x3a, 0xd4, 0xb4, - 0x76, 0x6f, 0xa8, 0x6b, 0xfd, 0xe7, 0xca, 0xba, 0xfa, 0x5b, 0x39, 0xd8, 0xec, 0xbb, 0x3b, 0xf3, - 0x99, 0x63, 0x8f, 0x8d, 0xd0, 0x7a, 0x6a, 0x9d, 0xb7, 0xc2, 0x33, 0x14, 0xa7, 0x46, 0x18, 0xfa, - 0x7c, 0x33, 0x97, 0x35, 0x9e, 0xe0, 0x0e, 0xba, 0xc0, 0xf2, 0x43, 0xf2, 0x3f, 0xca, 0xbb, 0xb8, - 0xce, 0xe1, 0x2d, 0xcf, 0xa1, 0xbd, 0xcc, 0x7e, 0x00, 0x57, 0xb8, 0x53, 0x8f, 0x53, 0xa2, 0xd2, - 0xc9, 0x6d, 0xfb, 0xec, 0xd2, 0xd2, 0x65, 0x9c, 0x10, 0xb3, 0x22, 0x19, 0xb1, 0xb0, 0xdb, 0x50, - 0x49, 0xb2, 0x73, 0xd3, 0xa0, 0xac, 0x41, 0x4c, 0x48, 0x2d, 0xf1, 0x5c, 0xdd, 0x8c, 0x5a, 0xad, - 0xdb, 0xe6, 0x19, 0x99, 0x4b, 0x79, 0xad, 0xee, 0x25, 0x9d, 0x41, 0x91, 0xfb, 0x19, 0x6c, 0xa6, - 0x28, 0xa9, 0x15, 0xdc, 0x60, 0x7a, 0x27, 0x3a, 0x2c, 0x58, 0xe8, 0xbd, 0x0c, 0xc1, 0xe6, 0x70, - 0x8d, 0x70, 0xc3, 0x4b, 0x43, 0x91, 0x99, 0xd9, 0x81, 0x6e, 0x1f, 0xb9, 0x9e, 0x6f, 0x09, 0xf6, - 0x5e, 0xb2, 0x83, 0x0e, 0xa5, 0x13, 0x9b, 0x45, 0x3a, 0x62, 0xe7, 0xd2, 0x24, 0x3a, 0x61, 0xe6, - 0x68, 0x9b, 0xcb, 0xcb, 0x9c, 0x56, 0xa4, 0x74, 0xc7, 0x44, 0x73, 0x9d, 0xa3, 0x22, 0x33, 0x04, - 0xc8, 0x0c, 0xa9, 0x12, 0xf0, 0x19, 0x87, 0xdd, 0xe8, 0xc1, 0xe5, 0x55, 0x8d, 0x5c, 0xa1, 0x57, - 0x6d, 0xc9, 0x7a, 0xd5, 0x82, 0x03, 0x2b, 0xd1, 0xb1, 0xfe, 0x49, 0x06, 0xaa, 0x3b, 0x96, 0x39, - 0x9f, 0xfd, 0xd8, 0xb3, 0x5d, 0x5c, 0x00, 0x1f, 0x42, 0xd5, 0x73, 0x4c, 0x9a, 0x3d, 0x29, 0x52, - 0x24, 0x75, 0x7a, 0x2a, 0x0e, 0x7a, 0xc0, 0x73, 0xcc, 0x96, 0xe7, 0x50, 0x5c, 0xc9, 0xbb, 0x70, - 0x89, 0x3b, 0xf7, 0x84, 0xaf, 0xfb, 0x8c, 0x67, 0x5e, 0xa7, 0x99, 0x51, 0x38, 0x8a, 0xab, 0x42, - 0x44, 0xfe, 0x6b, 0x70, 0x59, 0x22, 0x27, 0xd7, 0x00, 0xd1, 0x2f, 0x2f, 0x92, 0xcd, 0x38, 0x6f, - 0x74, 0x7c, 0xa9, 0xfe, 0xbd, 0x2c, 0x94, 0xb9, 0x6b, 0x10, 0xdb, 0x7b, 0x0f, 0x8a, 0xde, 0xe8, - 0x0b, 0xdd, 0xb7, 0x26, 0x17, 0x9d, 0xba, 0x17, 0xbc, 0xd1, 0x17, 0x9a, 0x35, 0x61, 0x6f, 0x47, - 0x52, 0xdd, 0xb4, 0x26, 0x62, 0x50, 0xea, 0x69, 0x7b, 0x40, 0x48, 0x79, 0xee, 0x08, 0xbb, 0xb4, - 0x68, 0x3d, 0xdb, 0x26, 0x77, 0x67, 0xe7, 0xb4, 0xcd, 0xb4, 0xf1, 0xdc, 0x31, 0x83, 0x8b, 0xdd, - 0x28, 0xb9, 0x0b, 0xdd, 0x28, 0xec, 0x3e, 0x6c, 0xe2, 0x50, 0x27, 0xf9, 0xf8, 0x62, 0xc6, 0x6d, - 0xb5, 0xe1, 0x39, 0x66, 0xe2, 0xae, 0x30, 0xcf, 0x90, 0xd6, 0xb5, 0x4e, 0x17, 0x68, 0x0b, 0x9c, - 0xd6, 0xb5, 0x4e, 0x53, 0xb4, 0x8f, 0xa0, 0x92, 0xec, 0xd6, 0xa0, 0x51, 0xbc, 0x78, 0x06, 0xe3, - 0xcd, 0x1b, 0x60, 0x26, 0xee, 0xda, 0xe5, 0x99, 0x4a, 0x17, 0x67, 0xe2, 0x64, 0x74, 0xfc, 0xf8, - 0xcf, 0xd7, 0xa1, 0xdc, 0xe1, 0x65, 0x84, 0x67, 0xec, 0x0e, 0x64, 0xbf, 0x66, 0x1a, 0x10, 0x87, - 0xdd, 0x30, 0x4c, 0x53, 0x37, 0x26, 0x13, 0x6b, 0x1c, 0x5a, 0xa6, 0x8e, 0x1a, 0x97, 0x60, 0x7a, - 0x1b, 0x86, 0x69, 0x36, 0x05, 0x9c, 0x84, 0x07, 0x77, 0x74, 0x45, 0x96, 0x27, 0x3f, 0xda, 0xc9, - 0x46, 0x8e, 0x2e, 0x61, 0x78, 0xf2, 0x83, 0x9d, 0xd4, 0xcc, 0xe6, 0xbe, 0xdb, 0xcc, 0xe6, 0xbf, - 0xf5, 0xcc, 0x16, 0x2e, 0x9e, 0xd9, 0x94, 0xe7, 0x0d, 0x67, 0xaa, 0x48, 0x33, 0x95, 0x08, 0xf3, - 0x8e, 0x79, 0xa6, 0xfe, 0xc3, 0x2c, 0x80, 0x66, 0xcd, 0x1c, 0x63, 0x6c, 0xfd, 0xbf, 0x33, 0x7a, - 0xb7, 0xa5, 0x65, 0xe2, 0x9a, 0x51, 0x68, 0x52, 0xb4, 0x24, 0x48, 0xfc, 0xad, 0x1c, 0xde, 0xc2, - 0xb7, 0x1e, 0xde, 0xe2, 0xb7, 0x18, 0xde, 0xd2, 0xf2, 0xf0, 0xb2, 0x1f, 0xc1, 0xab, 0xbe, 0x75, - 0xea, 0xdb, 0xa1, 0xa5, 0x4f, 0x7c, 0x6f, 0xaa, 0xa7, 0x84, 0x01, 0xf2, 0xca, 0x32, 0x8d, 0xc6, - 0x75, 0x41, 0xb4, 0xeb, 0x7b, 0xd3, 0xb4, 0x40, 0x50, 0xff, 0xaa, 0x04, 0x95, 0xa6, 0x6b, 0x38, - 0xe7, 0x5f, 0x59, 0x14, 0xbe, 0x44, 0x87, 0x3f, 0xb3, 0x79, 0xc8, 0xc7, 0x9d, 0x9f, 0xe7, 0x97, - 0x09, 0x42, 0x23, 0x7e, 0x1b, 0x2a, 0xde, 0x3c, 0x8c, 0xf1, 0xfc, 0x84, 0x1f, 0x38, 0x88, 0x08, - 0xe2, 0xfc, 0xf1, 0xc1, 0x62, 0x94, 0x9f, 0xec, 0xcf, 0x24, 0x7f, 0x6c, 0x93, 0xc4, 0xf9, 0x89, - 0x00, 0x05, 0x84, 0x3d, 0xa5, 0x91, 0x0f, 0xe6, 0x53, 0x8b, 0x8f, 0x7e, 0x96, 0x87, 0x89, 0xb6, - 0x04, 0x0c, 0x4b, 0x99, 0x5a, 0x53, 0xcf, 0x3f, 0xe7, 0xa5, 0x14, 0x78, 0x29, 0x1c, 0x44, 0xa5, - 0xbc, 0x03, 0xec, 0xd4, 0xb0, 0x43, 0x3d, 0x5d, 0x14, 0xb7, 0x03, 0x15, 0xc4, 0x0c, 0xe5, 0xe2, - 0xae, 0x42, 0xc1, 0xb4, 0x83, 0x93, 0x4e, 0x5f, 0xd8, 0x80, 0x22, 0x85, 0x7d, 0x09, 0xc6, 0x06, - 0x2a, 0xa5, 0xa1, 0x15, 0xd0, 0x50, 0x66, 0xb5, 0x32, 0x42, 0xb6, 0x11, 0x80, 0x4a, 0x8d, 0x6b, - 0x85, 0xa7, 0x9e, 0x8f, 0x39, 0xb9, 0x89, 0x97, 0x00, 0x50, 0xf9, 0x43, 0x52, 0xac, 0x88, 0x9c, - 0x6a, 0x59, 0x2d, 0x4e, 0xa3, 0xf1, 0xc4, 0xb9, 0x12, 0x61, 0xab, 0xbc, 0xf9, 0x09, 0x84, 0xdd, - 0x85, 0x3a, 0x35, 0x9f, 0x4c, 0x40, 0xec, 0x03, 0x1d, 0xc2, 0x67, 0xb5, 0x2a, 0x42, 0xc9, 0xbf, - 0x82, 0x54, 0x9f, 0xc2, 0xf5, 0x54, 0xff, 0x74, 0xc3, 0xf7, 0x8d, 0x73, 0x7d, 0x6a, 0x7c, 0xe1, - 0xf9, 0xe4, 0x3f, 0xcb, 0x6a, 0x57, 0xe5, 0x61, 0x6b, 0x22, 0x7a, 0x1f, 0xb1, 0x17, 0x66, 0xb5, - 0x5d, 0xcf, 0x27, 0xe7, 0xda, 0xca, 0xac, 0x88, 0x25, 0xaf, 0x0e, 0x4d, 0x30, 0xd9, 0xa3, 0x01, - 0x0f, 0x2f, 0xd6, 0x2a, 0x04, 0xdb, 0x26, 0x10, 0xda, 0x68, 0xc1, 0x23, 0x2e, 0xec, 0x36, 0x45, - 0x14, 0xe0, 0x23, 0x12, 0x89, 0x1c, 0x71, 0x6c, 0x19, 0x26, 0x1d, 0xec, 0x13, 0x62, 0xcf, 0x32, - 0x28, 0x6c, 0x26, 0x78, 0xa4, 0xcf, 0xe6, 0x21, 0x8f, 0x0b, 0xd6, 0xf2, 0xc1, 0xa3, 0x83, 0x79, - 0x28, 0xc0, 0x47, 0x56, 0x48, 0xd1, 0xc0, 0x04, 0x7e, 0x62, 0x85, 0xa8, 0x9b, 0x04, 0x8f, 0xa2, - 0x43, 0xba, 0x2b, 0x62, 0x6c, 0x1f, 0x89, 0x53, 0x38, 0x15, 0x6a, 0x31, 0x52, 0x9f, 0xce, 0x79, - 0x20, 0x70, 0x56, 0xab, 0x44, 0x04, 0xfb, 0x73, 0x07, 0x27, 0x76, 0x6c, 0x8c, 0x8f, 0x2d, 0xdd, - 0xc7, 0xa6, 0x5c, 0xe3, 0x53, 0x47, 0x10, 0x0d, 0x5b, 0xf3, 0x0a, 0xf0, 0x84, 0x7e, 0x6c, 0x87, - 0xe4, 0xb0, 0xcb, 0x6a, 0x25, 0x02, 0xec, 0xd9, 0x21, 0xf2, 0x27, 0x8e, 0x14, 0x2b, 0x90, 0x8a, - 0xb8, 0x4e, 0x44, 0x1b, 0x84, 0xd8, 0x27, 0x38, 0x15, 0x74, 0x0f, 0x94, 0x14, 0x2d, 0x96, 0x77, - 0x83, 0x48, 0xeb, 0x12, 0x29, 0x96, 0xfa, 0x06, 0xf0, 0xcc, 0x3a, 0x2e, 0x3d, 0x5e, 0xe6, 0x2b, - 0xdc, 0x1f, 0x41, 0xe0, 0x1d, 0x3b, 0x38, 0xa1, 0x12, 0xef, 0x42, 0x5d, 0xa2, 0xc3, 0xf2, 0x6e, - 0xf2, 0x95, 0x11, 0x93, 0xa5, 0xda, 0xe8, 0x5b, 0x53, 0x2f, 0x14, 0xdd, 0x7c, 0x55, 0x6a, 0xa3, - 0x46, 0xf0, 0x74, 0x1b, 0x05, 0x2d, 0x96, 0x79, 0x4b, 0x6a, 0x23, 0x27, 0xc5, 0x52, 0xef, 0x40, - 0x15, 0xb9, 0x48, 0x68, 0xb9, 0x7c, 0xf3, 0xdf, 0xe6, 0x03, 0x2b, 0x60, 0xb4, 0xfb, 0xef, 0x40, - 0x95, 0x8f, 0xbc, 0xe0, 0xdb, 0x5b, 0x9c, 0x44, 0xc0, 0x90, 0x44, 0xf5, 0xa5, 0xc3, 0xb4, 0x03, - 0x7f, 0xee, 0x5a, 0xdc, 0xfd, 0x48, 0x9f, 0xa6, 0x08, 0x6b, 0x88, 0xd3, 0x6c, 0x07, 0x2e, 0x71, - 0xaf, 0x83, 0x25, 0xe9, 0x10, 0x51, 0x58, 0xe1, 0xca, 0x43, 0x26, 0x16, 0xd1, 0xc7, 0xe0, 0x40, - 0xfd, 0x59, 0x06, 0x6e, 0xf4, 0x29, 0xc6, 0x82, 0x18, 0xec, 0xbe, 0x15, 0x04, 0xc6, 0x91, 0xb5, - 0xeb, 0xf9, 0xbb, 0xf3, 0xaf, 0xbe, 0x3a, 0x67, 0xf7, 0x60, 0xe3, 0xc0, 0xf0, 0x2d, 0x37, 0x8c, - 0xd9, 0xaf, 0xd0, 0x31, 0x17, 0xc1, 0xec, 0x31, 0x1d, 0xe4, 0x58, 0x6e, 0x78, 0x18, 0x6b, 0xeb, - 0xa2, 0x2d, 0x69, 0xd7, 0xfe, 0x12, 0x95, 0xfa, 0xbf, 0xb6, 0x20, 0xd7, 0xf3, 0x4c, 0x8b, 0xbd, - 0x0f, 0x65, 0x8a, 0x09, 0x5e, 0x3e, 0x3f, 0x44, 0x34, 0xfd, 0x21, 0xc3, 0xa9, 0xe4, 0x8a, 0xaf, - 0x8b, 0xa3, 0x88, 0xef, 0x90, 0x09, 0x48, 0x01, 0x08, 0x28, 0xd0, 0x2a, 0xc2, 0x29, 0x45, 0x5e, - 0x15, 0x8e, 0xc1, 0xb1, 0x25, 0xa7, 0xba, 0x6f, 0xb9, 0xa4, 0xa5, 0xe5, 0xb5, 0x38, 0x4d, 0x86, - 0xb7, 0xef, 0xa1, 0xf0, 0xe5, 0x7b, 0x35, 0xbf, 0xc2, 0xf0, 0xe6, 0x78, 0xda, 0xbc, 0xef, 0x43, - 0xf9, 0x0b, 0xcf, 0x76, 0x79, 0xc3, 0x0b, 0x4b, 0x0d, 0x47, 0xdd, 0x9a, 0x37, 0xfc, 0x0b, 0xf1, - 0xc5, 0x5e, 0x83, 0xa2, 0xe7, 0xf2, 0xb2, 0x8b, 0x4b, 0x65, 0x17, 0x3c, 0xb7, 0xcb, 0x03, 0xf4, - 0x6a, 0xa3, 0xb9, 0xed, 0x98, 0x28, 0xbb, 0x1c, 0x6b, 0x12, 0x8a, 0x73, 0xbe, 0x0a, 0x01, 0xfb, - 0x6e, 0xd7, 0x9a, 0x84, 0xec, 0x6d, 0xa8, 0x4c, 0x6c, 0x07, 0x65, 0x3c, 0x15, 0x56, 0x5e, 0x2a, - 0x0c, 0x38, 0x9a, 0x0a, 0x7c, 0x1d, 0x4a, 0x47, 0xbe, 0x37, 0x9f, 0xe9, 0xa3, 0x73, 0x3a, 0xdf, - 0x5b, 0x38, 0x59, 0x23, 0xdc, 0xf6, 0x39, 0x0a, 0x1a, 0xfa, 0xb4, 0xdd, 0x23, 0x9d, 0x7c, 0x29, - 0x95, 0xad, 0xec, 0xbd, 0x92, 0x56, 0x8d, 0x80, 0xe4, 0x25, 0x79, 0x1d, 0x4a, 0xc6, 0xd1, 0x91, - 0x2e, 0xe2, 0x0c, 0x97, 0xca, 0x32, 0x8e, 0x8e, 0xa8, 0xca, 0x07, 0x50, 0x3b, 0xb5, 0x5d, 0x3d, - 0x98, 0x59, 0x63, 0x4e, 0x5b, 0x5b, 0x1e, 0xca, 0x53, 0xdb, 0x1d, 0xcc, 0xac, 0x31, 0xd1, 0xcb, - 0x3e, 0x8c, 0xfa, 0x4b, 0x7d, 0x18, 0x5b, 0x90, 0x77, 0xec, 0xa9, 0x1d, 0x8a, 0xc8, 0xc3, 0x94, - 0x91, 0x43, 0x08, 0xa6, 0x42, 0x41, 0x38, 0xcf, 0x95, 0x25, 0x12, 0x81, 0x49, 0x6b, 0x40, 0x9b, - 0x2f, 0xd1, 0x80, 0x24, 0x83, 0x83, 0x7d, 0xbd, 0xc1, 0xf1, 0x11, 0x9d, 0x30, 0x5a, 0x6e, 0xa8, - 0x47, 0x19, 0x2e, 0xad, 0xce, 0x50, 0xe5, 0x64, 0x7d, 0x9e, 0xed, 0x03, 0xa8, 0xf8, 0xe4, 0x5c, - 0xd3, 0xc9, 0x13, 0x77, 0x59, 0xf6, 0x4e, 0x24, 0x5e, 0x37, 0x0d, 0xfc, 0xc4, 0x03, 0xf7, 0x1a, - 0xd4, 0x78, 0x00, 0x14, 0x0f, 0x53, 0x09, 0x88, 0xb1, 0x97, 0xb5, 0x2a, 0x01, 0x79, 0x08, 0x4b, - 0xc0, 0x1e, 0x00, 0x44, 0xaa, 0x5b, 0x78, 0x46, 0x9c, 0x3d, 0x6e, 0x0a, 0x67, 0xff, 0xad, 0xf0, - 0x4c, 0x2b, 0x9b, 0xd1, 0x27, 0x32, 0xac, 0x91, 0xed, 0x9a, 0xb8, 0x08, 0x42, 0xe3, 0x28, 0x68, - 0x34, 0x68, 0x8f, 0x54, 0x04, 0x6c, 0x68, 0x1c, 0x05, 0x68, 0x2c, 0x1a, 0x5c, 0x41, 0xe2, 0x91, - 0xe0, 0xd7, 0x65, 0x4f, 0x92, 0xa4, 0x3a, 0x69, 0x15, 0x43, 0xd2, 0xa3, 0x3e, 0x01, 0x16, 0x9d, - 0xcc, 0x49, 0xb6, 0xdf, 0x8d, 0xa5, 0x75, 0xb1, 0x21, 0x8e, 0xe6, 0xe2, 0xdb, 0x0b, 0x9f, 0x40, - 0x2d, 0xad, 0xd0, 0xde, 0x5c, 0x71, 0x16, 0x45, 0x53, 0xa6, 0x55, 0xc7, 0xb2, 0x8a, 0xfb, 0x1a, - 0xd4, 0x5c, 0x2f, 0xd4, 0x89, 0x69, 0x53, 0x46, 0x7e, 0xde, 0x52, 0x75, 0xbd, 0xb0, 0x15, 0xc1, - 0x70, 0x7c, 0x22, 0xb3, 0x29, 0x3c, 0x23, 0x3e, 0x1f, 0x8f, 0x4f, 0x6c, 0xe3, 0xa0, 0xbe, 0x16, - 0x99, 0x3b, 0x38, 0x4f, 0x5c, 0x7d, 0xa7, 0x0c, 0xb7, 0x53, 0xf3, 0x14, 0xeb, 0xf5, 0x1a, 0xf8, - 0x89, 0x8e, 0x7f, 0x1b, 0x2a, 0x81, 0x37, 0xf7, 0xc7, 0x96, 0x1e, 0x84, 0xd6, 0xac, 0xb1, 0x45, - 0x23, 0x0a, 0x1c, 0x34, 0x08, 0xad, 0x19, 0x7b, 0x0c, 0xf5, 0x99, 0x6f, 0xe9, 0xd2, 0x3c, 0xdd, - 0x91, 0xbb, 0x78, 0xe0, 0x5b, 0xc9, 0x54, 0x55, 0x67, 0x52, 0x2a, 0xca, 0x29, 0xf5, 0x40, 0x5d, - 0xc8, 0x99, 0x74, 0x02, 0x73, 0x26, 0x66, 0xdb, 0x0f, 0x61, 0x53, 0xca, 0x39, 0x3f, 0xa1, 0xcc, - 0xaf, 0xa5, 0x8e, 0x06, 0x23, 0xf2, 0xc3, 0x13, 0xcc, 0x5e, 0x9f, 0xa5, 0xd2, 0xac, 0xb9, 0xe0, - 0x93, 0x41, 0xe5, 0xfa, 0x2e, 0xe5, 0xbf, 0x76, 0x81, 0xa3, 0x25, 0xe5, 0xac, 0x79, 0xca, 0x0f, - 0x81, 0x3a, 0x41, 0xdb, 0x35, 0x1b, 0xaf, 0xf3, 0x2b, 0x46, 0x94, 0x60, 0x8f, 0xa0, 0xca, 0xd5, - 0x3c, 0x0a, 0x6f, 0x0e, 0x1a, 0x6f, 0xc8, 0x4e, 0x69, 0xd2, 0xf5, 0x08, 0xa1, 0x55, 0x9c, 0xf8, - 0x3b, 0x60, 0x1f, 0xc3, 0x26, 0x3f, 0x1f, 0x90, 0xd9, 0xe2, 0x9b, 0xcb, 0x8b, 0x8b, 0x88, 0x76, - 0x13, 0xde, 0xa8, 0xc1, 0x75, 0x7f, 0xee, 0x92, 0xea, 0x27, 0x72, 0xce, 0x7c, 0x6f, 0x64, 0xf1, - 0xfc, 0xf7, 0x28, 0xbf, 0xe8, 0x8e, 0xc6, 0xc9, 0x78, 0x5e, 0xe2, 0x47, 0x57, 0x7d, 0x19, 0x74, - 0x80, 0xf9, 0x2e, 0x28, 0x93, 0xf3, 0x73, 0x2a, 0xf3, 0xad, 0x6f, 0x53, 0xe6, 0x36, 0xe6, 0xa3, - 0x32, 0x19, 0xe4, 0xe6, 0x73, 0xdb, 0x6c, 0xdc, 0xe7, 0x91, 0xc8, 0xf8, 0xcd, 0x5e, 0x87, 0xba, - 0x6f, 0x8d, 0xe7, 0x7e, 0x60, 0xbf, 0xb0, 0xf4, 0xc0, 0x76, 0x4f, 0x1a, 0x6f, 0xd3, 0x38, 0xd6, - 0x62, 0xe8, 0xc0, 0x76, 0x4f, 0x70, 0xc5, 0x5a, 0x67, 0xa1, 0xe5, 0xbb, 0x3a, 0xaa, 0xdb, 0x8d, - 0x77, 0xe4, 0x15, 0xdb, 0x26, 0xc4, 0x60, 0x6c, 0xb8, 0x1a, 0x58, 0xf1, 0x37, 0xfb, 0x01, 0x6c, - 0x24, 0xa6, 0xd6, 0x0c, 0x15, 0x8f, 0xc6, 0xbb, 0x2b, 0x4f, 0x8d, 0x49, 0x29, 0xd1, 0x92, 0x90, - 0x0a, 0xae, 0xbf, 0xa4, 0xd7, 0x56, 0xc0, 0xd7, 0xd6, 0x83, 0x6f, 0xb4, 0xb6, 0x06, 0xb4, 0xb6, - 0xde, 0x80, 0x92, 0xed, 0x86, 0x96, 0xff, 0xc2, 0x70, 0x1a, 0xef, 0x2d, 0x31, 0xf0, 0x18, 0xc7, - 0xee, 0x42, 0x31, 0x70, 0x6c, 0x64, 0x4c, 0x8d, 0xf7, 0x97, 0xc8, 0x22, 0x14, 0xbb, 0x07, 0xe5, - 0xf8, 0x4e, 0x5d, 0xe3, 0x83, 0x25, 0xba, 0x04, 0xc9, 0x6e, 0x41, 0xee, 0x14, 0xd7, 0xe3, 0xc3, - 0xe5, 0x33, 0x08, 0x84, 0xa3, 0xc4, 0x9f, 0xd8, 0x8e, 0xc3, 0x25, 0xfe, 0xa3, 0x25, 0x89, 0xbf, - 0x6b, 0x3b, 0x0e, 0x97, 0xf8, 0x13, 0xf1, 0x85, 0xf2, 0x92, 0x72, 0x60, 0x4f, 0x3e, 0x5c, 0x96, - 0x97, 0x88, 0x7b, 0x46, 0xb7, 0x0f, 0x2b, 0x01, 0x39, 0xd6, 0xf9, 0xf9, 0xc0, 0x47, 0xf2, 0x58, - 0xa5, 0x3d, 0xee, 0x1a, 0x04, 0x71, 0x1a, 0xd5, 0x76, 0x71, 0xac, 0x80, 0xf6, 0xf0, 0xc7, 0xfc, - 0x52, 0x0c, 0x87, 0xa0, 0x31, 0xfc, 0x3e, 0xd4, 0xa2, 0x78, 0x3a, 0xac, 0x2e, 0x68, 0x7c, 0xb2, - 0xd4, 0x82, 0x34, 0x01, 0xdb, 0x81, 0xea, 0x04, 0x35, 0xc0, 0x29, 0x57, 0x08, 0x1b, 0x8f, 0xa9, - 0x21, 0x5b, 0x91, 0x2c, 0xbe, 0x48, 0x61, 0xd4, 0x52, 0xb9, 0xd8, 0x03, 0x60, 0xf6, 0x84, 0xcf, - 0x27, 0x1a, 0xd8, 0x5c, 0xe9, 0x6b, 0x7c, 0x4a, 0x8b, 0x73, 0x05, 0x86, 0x3d, 0x82, 0x5a, 0x60, - 0xb9, 0xa6, 0x3e, 0x0d, 0x84, 0x66, 0xf1, 0x3d, 0x6a, 0xa7, 0x60, 0xc3, 0xf1, 0xdd, 0x5b, 0xad, - 0x82, 0x54, 0xfb, 0x01, 0x57, 0x31, 0x1e, 0x01, 0xae, 0xf3, 0x17, 0x49, 0xa6, 0x5f, 0xbb, 0x20, - 0x13, 0x52, 0x45, 0x99, 0x1e, 0x43, 0xdd, 0xb4, 0xcc, 0xf9, 0x4c, 0x27, 0xc5, 0x0d, 0x97, 0xe5, - 0xf7, 0x65, 0x7e, 0x29, 0xfb, 0x44, 0xb5, 0xaa, 0x29, 0x7b, 0x48, 0x3f, 0x81, 0x8d, 0xc8, 0x79, - 0x19, 0x0a, 0x3f, 0xe7, 0x0f, 0xe4, 0x0a, 0x63, 0xdf, 0xa4, 0x56, 0x9b, 0x47, 0x9f, 0x51, 0x3b, - 0xc9, 0x66, 0x0e, 0x5c, 0x63, 0x16, 0x1c, 0x7b, 0x61, 0xe3, 0xd7, 0x65, 0x55, 0x63, 0x20, 0xa0, - 0x5a, 0x15, 0x89, 0xa2, 0x14, 0x8a, 0xae, 0x64, 0x6b, 0x8f, 0x43, 0xab, 0xf1, 0x43, 0x2e, 0xba, - 0x62, 0x60, 0x2b, 0xc4, 0x61, 0x03, 0x63, 0x36, 0x73, 0xce, 0xf9, 0x72, 0xfc, 0x11, 0x2d, 0xc7, - 0xcb, 0xd2, 0x72, 0x6c, 0x22, 0x92, 0xd6, 0x63, 0xd9, 0x88, 0x3e, 0xd9, 0x43, 0xa8, 0xce, 0xbc, - 0x20, 0xd4, 0xcd, 0xa9, 0x43, 0xfd, 0x6f, 0xca, 0xec, 0xe0, 0xc0, 0x0b, 0xc2, 0x9d, 0xa9, 0x43, - 0x02, 0x6c, 0x16, 0x7f, 0xb3, 0x2e, 0x5c, 0x4a, 0xb1, 0x7a, 0x83, 0x0e, 0xe6, 0x1b, 0xdb, 0x54, - 0xe3, 0x4d, 0xa9, 0x46, 0x89, 0xe5, 0x8b, 0x80, 0xce, 0x4d, 0x6f, 0x11, 0x84, 0x16, 0x1b, 0x9f, - 0x83, 0x38, 0xaa, 0xb9, 0xc5, 0xf5, 0x16, 0x82, 0x46, 0x61, 0xcd, 0x8f, 0x61, 0x23, 0xa1, 0xc2, - 0x0e, 0x06, 0x8d, 0x1d, 0x79, 0xf5, 0x4a, 0x77, 0x0f, 0x6a, 0x51, 0x46, 0x84, 0x05, 0xea, 0x9f, - 0xe7, 0xa1, 0x14, 0x19, 0x0d, 0xac, 0x02, 0xc5, 0xc3, 0xde, 0xd3, 0x5e, 0xff, 0x79, 0x8f, 0xdf, - 0x01, 0x6c, 0x0e, 0x06, 0x6d, 0x6d, 0xa8, 0x98, 0xac, 0x0e, 0x40, 0x77, 0x9c, 0xf4, 0x41, 0xab, - 0xd9, 0xe3, 0x77, 0x02, 0xe9, 0x66, 0x15, 0x4f, 0xaf, 0xb3, 0x4d, 0xa8, 0xed, 0x1e, 0xf6, 0x28, - 0x6e, 0x94, 0x83, 0xb2, 0x08, 0x6a, 0x7f, 0xc6, 0x4f, 0x08, 0x39, 0x28, 0x87, 0xa0, 0xfd, 0xe6, - 0xb0, 0xad, 0x75, 0x22, 0x50, 0x9e, 0x42, 0x50, 0xfb, 0x87, 0x5a, 0x4b, 0x94, 0x54, 0x60, 0x57, - 0x60, 0x33, 0xce, 0x16, 0x15, 0xa9, 0x14, 0xb1, 0x65, 0x07, 0x5a, 0xff, 0xc7, 0xed, 0xd6, 0x50, - 0x01, 0x3a, 0x6e, 0x7c, 0xf2, 0x44, 0xa9, 0xb0, 0x2a, 0x94, 0x76, 0x3a, 0x83, 0x61, 0xa7, 0xd7, - 0x1a, 0x2a, 0x55, 0x6c, 0xf0, 0x6e, 0xa7, 0x3b, 0x6c, 0x6b, 0x4a, 0x8d, 0x95, 0x20, 0xf7, 0xe3, - 0x7e, 0xa7, 0xa7, 0xd4, 0xe9, 0xae, 0x57, 0x73, 0xff, 0xa0, 0xdb, 0x56, 0x36, 0x10, 0x3a, 0xe8, - 0x6b, 0x43, 0x45, 0x41, 0xe8, 0xf3, 0x4e, 0x6f, 0xa7, 0xff, 0x5c, 0xd9, 0x64, 0x65, 0xc8, 0x1f, - 0xf6, 0xb0, 0x1a, 0xc6, 0x6a, 0x50, 0xa6, 0x4f, 0xbd, 0xd9, 0xed, 0x2a, 0x97, 0xa4, 0x33, 0xca, - 0xcb, 0x88, 0xa2, 0x13, 0xcf, 0x01, 0xb6, 0xe1, 0x0a, 0xf6, 0x25, 0x4e, 0x12, 0xf5, 0x55, 0x2c, - 0x67, 0xbf, 0xd3, 0x3b, 0x1c, 0x28, 0xd7, 0x90, 0x98, 0x3e, 0x09, 0xd3, 0xc0, 0x72, 0x3a, 0x3d, - 0x1a, 0xca, 0x5b, 0xf8, 0xbd, 0xd3, 0xee, 0xb6, 0x87, 0x6d, 0xe5, 0x36, 0xf6, 0x4a, 0x6b, 0x1f, - 0x74, 0x9b, 0xad, 0xb6, 0xb2, 0x85, 0x89, 0x6e, 0xbf, 0xf5, 0x54, 0xef, 0x1f, 0x28, 0x77, 0xd8, - 0x65, 0x50, 0xfa, 0x3d, 0x7d, 0xe7, 0xf0, 0xa0, 0xdb, 0x69, 0x35, 0x87, 0x6d, 0xfd, 0x69, 0xfb, - 0x73, 0x45, 0xc5, 0x61, 0x3f, 0xd0, 0xda, 0xba, 0x28, 0xeb, 0xb5, 0x28, 0x2d, 0xca, 0xbb, 0xcb, - 0x14, 0xa8, 0xee, 0x1e, 0xfe, 0xf4, 0xa7, 0x9f, 0xeb, 0x62, 0x1c, 0x5e, 0xc7, 0x66, 0x26, 0x39, - 0xf4, 0xc3, 0xa7, 0xca, 0x1b, 0x0b, 0xa0, 0xc1, 0x53, 0xe5, 0x4d, 0x1c, 0xc7, 0x68, 0x62, 0x94, - 0x7b, 0x48, 0xa0, 0xb5, 0x5b, 0x87, 0xda, 0xa0, 0xf3, 0xac, 0xad, 0xb7, 0x86, 0x6d, 0xe5, 0x2d, - 0x1a, 0xb8, 0x4e, 0xef, 0xa9, 0x72, 0x1f, 0x7b, 0x86, 0x5f, 0x7c, 0xba, 0xde, 0x66, 0x0c, 0xea, - 0x09, 0x2d, 0xc1, 0xde, 0x41, 0x92, 0x6d, 0xad, 0xdf, 0xdc, 0x69, 0x35, 0x07, 0x43, 0xe5, 0x5d, - 0x1c, 0x96, 0xc1, 0x41, 0xb7, 0x33, 0x54, 0x1e, 0x60, 0xdf, 0x9f, 0x34, 0x87, 0x7b, 0x6d, 0x4d, - 0x79, 0x0f, 0x67, 0x7e, 0xd8, 0xd9, 0x6f, 0xeb, 0x62, 0x1a, 0x1e, 0x62, 0x1d, 0xbb, 0x9d, 0x6e, - 0x57, 0x79, 0x44, 0xc7, 0x72, 0x4d, 0x6d, 0xd8, 0xa1, 0xb9, 0xff, 0x10, 0x0b, 0x68, 0x1e, 0x1c, - 0x74, 0x3f, 0x57, 0x3e, 0xc2, 0x0e, 0xee, 0x1f, 0x76, 0x87, 0x1d, 0xfd, 0xf0, 0x60, 0xa7, 0x39, - 0x6c, 0x2b, 0x1f, 0xd3, 0xc2, 0xe8, 0x0f, 0x86, 0x3b, 0xfb, 0x5d, 0xe5, 0x13, 0xf5, 0x37, 0xa1, - 0x14, 0xd9, 0x91, 0x98, 0xab, 0xd3, 0xeb, 0xb5, 0x35, 0x65, 0x0d, 0x4b, 0xee, 0xb6, 0x77, 0x87, - 0x4a, 0x86, 0x8e, 0x24, 0x3b, 0x4f, 0xf6, 0x86, 0xca, 0x3a, 0x7e, 0xf6, 0x0f, 0x71, 0x90, 0xb2, - 0xd4, 0xbb, 0xf6, 0x7e, 0x47, 0xc9, 0xe1, 0x57, 0xb3, 0x37, 0xec, 0x28, 0x79, 0x5a, 0x36, 0x9d, - 0xde, 0x93, 0x6e, 0x5b, 0x29, 0x20, 0x74, 0xbf, 0xa9, 0x3d, 0x55, 0x8a, 0xbc, 0xd0, 0x9d, 0xf6, - 0x67, 0x4a, 0x89, 0x15, 0x60, 0xbd, 0xfb, 0x50, 0x29, 0x23, 0x68, 0xa7, 0xbd, 0x73, 0x78, 0xa0, - 0x80, 0x7a, 0x0f, 0x8a, 0xcd, 0xa3, 0xa3, 0x7d, 0x34, 0xd3, 0xb1, 0x33, 0x87, 0xdd, 0x2e, 0xdf, - 0x46, 0xdb, 0xfd, 0xe1, 0xb0, 0xbf, 0xaf, 0x64, 0x70, 0xe1, 0x0e, 0xfb, 0x07, 0xca, 0xba, 0xda, - 0x81, 0x52, 0x24, 0xfe, 0xa4, 0xeb, 0x8b, 0x25, 0xc8, 0x1d, 0x68, 0xed, 0x67, 0xfc, 0x1c, 0xbd, - 0xd7, 0xfe, 0x0c, 0x9b, 0x89, 0x5f, 0x58, 0x50, 0x16, 0x2b, 0xe2, 0xf7, 0x0c, 0xe9, 0xfe, 0x62, - 0xb7, 0xd3, 0x6b, 0x37, 0x35, 0x25, 0xaf, 0x7e, 0x94, 0x3a, 0xa2, 0x14, 0x5c, 0x03, 0xab, 0x6f, - 0x76, 0x44, 0xf5, 0x9d, 0x27, 0xbd, 0xbe, 0xd6, 0xe6, 0x17, 0x22, 0xc5, 0xb8, 0xad, 0xab, 0x6f, - 0x43, 0x39, 0xe6, 0x78, 0xb8, 0x8e, 0x5a, 0x5a, 0x7f, 0x30, 0xe0, 0xc3, 0xbc, 0x86, 0x69, 0x1a, - 0x1b, 0x9e, 0xce, 0xa8, 0xff, 0x3f, 0x94, 0x62, 0x66, 0x7b, 0x17, 0xd6, 0x87, 0x03, 0xe1, 0x82, - 0xbf, 0xfc, 0x20, 0x79, 0xb7, 0x62, 0x18, 0x7d, 0x69, 0xeb, 0xc3, 0x01, 0x7b, 0x07, 0x0a, 0xfc, - 0xd6, 0xaa, 0x38, 0x45, 0xba, 0x9c, 0x66, 0xe0, 0x43, 0xc2, 0x69, 0x82, 0x46, 0xed, 0x42, 0x3d, - 0x8d, 0x61, 0xb7, 0x00, 0x38, 0x4e, 0x72, 0xa7, 0x48, 0x10, 0x76, 0x03, 0xa2, 0x5b, 0xb1, 0x3b, - 0x22, 0xd4, 0x34, 0x4e, 0xab, 0xff, 0x20, 0x0b, 0x90, 0xa8, 0x6a, 0xa8, 0x0c, 0xc6, 0xce, 0x92, - 0xbc, 0x38, 0x50, 0x7e, 0x05, 0xca, 0x8e, 0x67, 0x98, 0xf2, 0xfb, 0x13, 0x25, 0x04, 0xd0, 0x68, - 0xc8, 0x77, 0xdf, 0xca, 0x3c, 0x9a, 0x83, 0x5d, 0x85, 0xc2, 0xc4, 0xf3, 0xa7, 0x46, 0x14, 0x94, - 0x2a, 0x52, 0x28, 0x7a, 0xf8, 0x21, 0x27, 0x2a, 0xac, 0x2e, 0xdd, 0x2b, 0xa1, 0x08, 0x67, 0x01, - 0xec, 0x22, 0x0c, 0x4d, 0x1a, 0xcb, 0x1d, 0x3b, 0x5e, 0x60, 0x99, 0x68, 0xb2, 0x17, 0x48, 0x2b, - 0x85, 0x08, 0xb4, 0x7d, 0xce, 0x7b, 0xeb, 0x4f, 0x6d, 0xd7, 0x08, 0x85, 0x9f, 0x99, 0x7a, 0x1b, - 0x41, 0xb0, 0xb9, 0x5f, 0x04, 0x9e, 0xf0, 0x9d, 0xf0, 0xf3, 0xd2, 0x12, 0x02, 0xa8, 0xb9, 0xaf, - 0x02, 0x58, 0xc1, 0xd8, 0x98, 0xf1, 0xc2, 0xcb, 0x54, 0x78, 0x59, 0x40, 0xb6, 0xcf, 0x59, 0x17, - 0xea, 0xc3, 0x11, 0xb2, 0x7b, 0x0f, 0xcd, 0xe0, 0x96, 0xe7, 0x08, 0xaf, 0xc6, 0xdd, 0x45, 0x9d, - 0xf6, 0x41, 0x9a, 0x8c, 0x1f, 0xec, 0x2e, 0xe4, 0xbd, 0xd1, 0x84, 0x4b, 0x2b, 0xc8, 0xbe, 0x55, - 0xc8, 0x9a, 0x13, 0xcd, 0x4e, 0x33, 0x0c, 0x29, 0xec, 0x39, 0x96, 0x6c, 0x99, 0x28, 0xde, 0x97, - 0x0b, 0xb5, 0x57, 0x28, 0x86, 0x45, 0x04, 0x27, 0x8a, 0x49, 0x8a, 0x83, 0x0e, 0xdf, 0x80, 0x0d, - 0x44, 0x4e, 0x6c, 0xcb, 0x31, 0x05, 0x09, 0xbf, 0x86, 0x54, 0x1b, 0x7b, 0xce, 0x2e, 0x42, 0x89, - 0x4e, 0xfd, 0xcb, 0x1c, 0x40, 0x62, 0x06, 0xa5, 0xce, 0x96, 0x33, 0xe9, 0xb3, 0xe5, 0x87, 0x70, - 0x55, 0x5c, 0x52, 0x8b, 0x0f, 0x68, 0x6d, 0x57, 0x1f, 0x19, 0xd1, 0x31, 0x3e, 0x13, 0x58, 0x7e, - 0x46, 0xdb, 0x71, 0xb7, 0x0d, 0xd4, 0x90, 0x36, 0xe4, 0x3c, 0xe1, 0xf9, 0x2c, 0x1d, 0x86, 0x20, - 0xcb, 0xdd, 0x24, 0xfb, 0xf0, 0x7c, 0xc6, 0xde, 0x87, 0x2b, 0xbe, 0x35, 0xf1, 0xad, 0xe0, 0x58, - 0x0f, 0x03, 0xb9, 0x32, 0x1e, 0x1b, 0xb7, 0x29, 0x90, 0xc3, 0x20, 0xae, 0xeb, 0x7d, 0xb8, 0x22, - 0x0c, 0xa4, 0x85, 0xe6, 0xf1, 0xe3, 0xd0, 0x4d, 0x8e, 0x94, 0x5b, 0xf7, 0x2a, 0x80, 0xb0, 0x0d, - 0xa3, 0x57, 0x5c, 0x4a, 0x5a, 0x99, 0xdb, 0x81, 0x68, 0xcc, 0xbf, 0x03, 0xcc, 0x0e, 0xf4, 0x85, - 0x93, 0x25, 0x71, 0x58, 0xaf, 0xd8, 0xc1, 0x41, 0xea, 0x54, 0xe9, 0xa2, 0x43, 0xab, 0xd2, 0x45, - 0x87, 0x56, 0x97, 0x21, 0x4f, 0xe6, 0xa3, 0x38, 0x43, 0xe2, 0x09, 0xa6, 0x42, 0x0e, 0xf9, 0x23, - 0x9d, 0x77, 0xd4, 0x1f, 0xd6, 0x1f, 0xd0, 0x1b, 0x38, 0x38, 0x3f, 0x08, 0xd5, 0x08, 0xc7, 0xde, - 0x85, 0x4b, 0xf2, 0xa0, 0x46, 0x0f, 0x3c, 0x54, 0xa8, 0x9b, 0x4a, 0x32, 0x8c, 0x1a, 0x7f, 0xea, - 0xe1, 0x6d, 0x60, 0xd2, 0xb8, 0x44, 0xd4, 0x55, 0x7e, 0xee, 0x1b, 0x0f, 0x8a, 0x20, 0x7e, 0x13, - 0x68, 0x00, 0xb8, 0x7b, 0xb9, 0xb6, 0x6c, 0x2c, 0x21, 0x92, 0x5c, 0xd1, 0xef, 0xc3, 0x95, 0x64, - 0xec, 0x74, 0x23, 0xd4, 0xc3, 0x63, 0x4b, 0xb7, 0x5c, 0x93, 0xee, 0x2d, 0x96, 0xb4, 0xcd, 0x78, - 0x18, 0x9b, 0xe1, 0xf0, 0xd8, 0x6a, 0xbb, 0xa6, 0xfa, 0xfb, 0x19, 0xa8, 0xa7, 0x2d, 0x35, 0x1e, - 0xcb, 0x9e, 0x04, 0xe9, 0xe7, 0x93, 0xc0, 0xfc, 0x57, 0xa0, 0x3c, 0x3b, 0x11, 0x11, 0xf9, 0xd1, - 0xda, 0x9e, 0x9d, 0xf0, 0x48, 0x7c, 0xf6, 0x16, 0x14, 0x67, 0x27, 0x7c, 0xb3, 0x5f, 0xb4, 0x9a, - 0x0a, 0x33, 0x1e, 0x24, 0xfb, 0x16, 0x14, 0xe7, 0x82, 0x34, 0x77, 0x11, 0xe9, 0x9c, 0x48, 0xd5, - 0x2d, 0xa8, 0xca, 0xbe, 0x11, 0xdc, 0xb3, 0x68, 0x07, 0xf1, 0x86, 0xe1, 0xa7, 0xfa, 0x5b, 0xeb, - 0x44, 0xf2, 0xad, 0x4e, 0xab, 0xbf, 0x55, 0xc4, 0xc0, 0x16, 0x45, 0xf5, 0xe9, 0x14, 0xb3, 0x3b, - 0xf6, 0xa2, 0xa7, 0x49, 0xe0, 0xd8, 0x08, 0x9a, 0xf3, 0xd0, 0x6b, 0x79, 0x8e, 0x88, 0x1b, 0x11, - 0x97, 0xa0, 0x72, 0x91, 0x37, 0x5f, 0xdc, 0x8f, 0x7c, 0x5f, 0xdc, 0x14, 0xa2, 0x6b, 0x7a, 0x14, - 0xab, 0x92, 0x5f, 0x9a, 0xc1, 0x6a, 0x74, 0x4b, 0x8f, 0xc2, 0x50, 0x1e, 0xc2, 0x46, 0x12, 0x96, - 0x1d, 0x85, 0xb7, 0x2c, 0x66, 0xa9, 0xc5, 0x31, 0xd9, 0x98, 0x54, 0x7f, 0x27, 0x03, 0x9b, 0x4b, - 0xae, 0x06, 0x1c, 0xad, 0xe4, 0x15, 0x23, 0xfc, 0x64, 0x77, 0xa0, 0x3a, 0x35, 0xc2, 0xf1, 0xb1, - 0x3e, 0xf3, 0xad, 0x89, 0x7d, 0x16, 0x3d, 0xc5, 0x44, 0xb0, 0x03, 0x02, 0x51, 0xa8, 0xce, 0x6c, - 0x46, 0x0e, 0x96, 0xa9, 0x1d, 0x0a, 0x06, 0x05, 0x04, 0xea, 0x92, 0xe7, 0x35, 0x0a, 0xe3, 0xcb, - 0x5d, 0x10, 0x75, 0x78, 0x13, 0x0a, 0x9d, 0xd8, 0xa5, 0x11, 0xc7, 0x9a, 0x64, 0xc5, 0x4b, 0x24, - 0x1e, 0x94, 0x5b, 0xf4, 0xaa, 0xc9, 0xbe, 0x31, 0x63, 0xf7, 0x21, 0x3b, 0x35, 0x66, 0x22, 0x16, - 0xa5, 0x11, 0x1f, 0x27, 0x70, 0xec, 0x83, 0x7d, 0x63, 0xc6, 0x19, 0x3a, 0x12, 0xdd, 0xf8, 0x18, - 0x4a, 0x11, 0xe0, 0x5b, 0xb1, 0xee, 0xff, 0x92, 0x85, 0xf2, 0x8e, 0xec, 0xfc, 0x44, 0x53, 0x2d, - 0xf4, 0xe7, 0x2e, 0xaa, 0x1e, 0xe2, 0xf0, 0xa5, 0x32, 0x36, 0xdc, 0xa1, 0x00, 0x45, 0x0b, 0x68, - 0xfd, 0x6b, 0x16, 0xd0, 0x4d, 0x00, 0x9f, 0x4c, 0x72, 0xb2, 0xca, 0xb3, 0x71, 0xdc, 0x63, 0xc7, - 0x14, 0x31, 0x1d, 0xcb, 0xc7, 0xf9, 0xb9, 0x6f, 0x7e, 0x9c, 0x9f, 0x5f, 0x79, 0x9c, 0xff, 0x7f, - 0xcd, 0x01, 0xfc, 0x1b, 0x89, 0xfc, 0xc0, 0x35, 0x8d, 0x64, 0x65, 0x2e, 0xc5, 0x66, 0xf1, 0xdd, - 0x09, 0xa4, 0xfb, 0x1e, 0xd4, 0xa3, 0x61, 0x16, 0x1d, 0x83, 0xd4, 0x75, 0x0d, 0x81, 0xe3, 0x7e, - 0xdd, 0x5a, 0x28, 0x27, 0xd3, 0x3b, 0xb4, 0xf2, 0xf5, 0x3b, 0x54, 0xfd, 0x83, 0x0c, 0x30, 0x61, - 0xd7, 0xee, 0xce, 0x1d, 0x67, 0x68, 0x9d, 0x11, 0x23, 0xb8, 0x0f, 0x9b, 0xc2, 0x29, 0x2b, 0x05, - 0x6e, 0x89, 0x43, 0x2e, 0x8e, 0x48, 0x0e, 0xb9, 0x56, 0xdd, 0x54, 0x5d, 0x5f, 0x79, 0x53, 0x75, - 0xf5, 0x0d, 0xd8, 0xdb, 0x50, 0x91, 0xef, 0x79, 0x72, 0x7d, 0x0b, 0x8c, 0xe4, 0x8a, 0xe7, 0x7f, - 0x5c, 0x07, 0x48, 0x6c, 0xef, 0x5f, 0x75, 0x50, 0xc8, 0x8a, 0x29, 0xc9, 0xae, 0x9a, 0x92, 0x7b, - 0xa0, 0xc8, 0x74, 0xd2, 0x85, 0xe3, 0x7a, 0x42, 0x18, 0xe9, 0x31, 0x76, 0x20, 0x5f, 0x0a, 0x25, - 0x9e, 0x26, 0xce, 0x9b, 0x45, 0xa0, 0x1c, 0xb1, 0x5c, 0x21, 0xa2, 0x4b, 0x76, 0xc0, 0x59, 0x30, - 0xfb, 0x14, 0xae, 0xc7, 0x39, 0xf5, 0x53, 0x3b, 0x3c, 0xf6, 0xe6, 0xa1, 0xf0, 0x92, 0x06, 0x42, - 0x50, 0x5f, 0x8d, 0x4a, 0x7a, 0xce, 0xd1, 0x9c, 0x65, 0x05, 0xec, 0x23, 0x28, 0x4f, 0xe6, 0x8e, - 0xa3, 0x87, 0xd6, 0x59, 0x28, 0xa2, 0xa7, 0x1b, 0x29, 0xb7, 0x85, 0x34, 0xbd, 0x5a, 0x69, 0x22, - 0x12, 0xea, 0xff, 0x5c, 0x87, 0xfc, 0x4f, 0xe6, 0x96, 0x7f, 0xce, 0x3e, 0x86, 0x72, 0x10, 0x4e, - 0x43, 0xf9, 0xa0, 0xf1, 0x3a, 0x2f, 0x80, 0xf0, 0x74, 0x4e, 0x68, 0x4d, 0x2d, 0x37, 0xe4, 0x3e, - 0x3c, 0xa4, 0x25, 0x89, 0x74, 0x19, 0xf2, 0x41, 0x68, 0xcd, 0x02, 0x11, 0xd8, 0xc6, 0x13, 0x6c, - 0x0b, 0xf2, 0xae, 0x67, 0x5a, 0x41, 0x3a, 0x7c, 0xad, 0x87, 0x42, 0x9f, 0x23, 0x98, 0x0a, 0x85, - 0x78, 0xc6, 0x97, 0x0e, 0xfb, 0x38, 0x86, 0xae, 0x1f, 0x58, 0x86, 0x69, 0xbb, 0x47, 0xd1, 0x05, - 0xee, 0x38, 0x8d, 0xb2, 0x96, 0x34, 0x78, 0xe3, 0x28, 0x7a, 0x4d, 0x41, 0x24, 0xd9, 0x16, 0x54, - 0xf0, 0xf3, 0xb9, 0x6f, 0x87, 0xd6, 0xe0, 0x91, 0x18, 0x37, 0x19, 0x84, 0xfa, 0xb7, 0x69, 0x85, - 0xd6, 0x38, 0x1c, 0x7c, 0x29, 0xe2, 0xba, 0x28, 0x60, 0x27, 0x82, 0xa8, 0x26, 0xd4, 0x52, 0xdd, - 0x5d, 0x72, 0x94, 0x0c, 0xda, 0xdd, 0x76, 0x6b, 0xc8, 0x4d, 0x2c, 0x61, 0x9d, 0xaf, 0xcb, 0xd6, - 0x7d, 0x56, 0x32, 0xfb, 0x73, 0x92, 0x1d, 0x96, 0x27, 0xa7, 0x41, 0x5b, 0x7b, 0xd2, 0x56, 0x0a, - 0xea, 0x1f, 0xae, 0xc3, 0xe6, 0xd0, 0x37, 0xdc, 0xc0, 0xe0, 0xb7, 0xf2, 0xdc, 0xd0, 0xf7, 0x1c, - 0xf6, 0x3d, 0x28, 0x85, 0x63, 0x47, 0x9e, 0x86, 0xdb, 0xd1, 0xa6, 0x5f, 0x20, 0x7d, 0x30, 0x1c, - 0x73, 0x87, 0x6a, 0x31, 0xe4, 0x1f, 0xec, 0x5d, 0xc8, 0x8f, 0xac, 0x23, 0xdb, 0x15, 0x0c, 0xf8, - 0xca, 0x62, 0xc6, 0x6d, 0x44, 0xee, 0xad, 0x69, 0x9c, 0x8a, 0xbd, 0x0f, 0x85, 0xb1, 0x37, 0x8d, - 0x24, 0x55, 0x72, 0x81, 0x48, 0xaa, 0x08, 0xb1, 0x7b, 0x6b, 0x9a, 0xa0, 0x63, 0x1f, 0x43, 0xc9, - 0xf7, 0x1c, 0x67, 0x64, 0x8c, 0x4f, 0x84, 0x0c, 0x6b, 0x2c, 0xe6, 0xd1, 0x04, 0x7e, 0x6f, 0x4d, - 0x8b, 0x69, 0xd5, 0x07, 0x50, 0x14, 0x8d, 0xc5, 0x01, 0xd8, 0x6e, 0x3f, 0xe9, 0x88, 0x81, 0x6c, - 0xf5, 0xf7, 0xf7, 0x3b, 0x43, 0x7e, 0x53, 0x59, 0xeb, 0x77, 0xbb, 0xdb, 0xcd, 0xd6, 0x53, 0x65, - 0x7d, 0xbb, 0x04, 0x05, 0xee, 0x46, 0x53, 0x7f, 0x3b, 0x03, 0x1b, 0x0b, 0x1d, 0x60, 0x8f, 0x21, - 0x37, 0x45, 0xa5, 0x92, 0x0f, 0xcf, 0xdd, 0x95, 0xbd, 0x94, 0xd2, 0x5c, 0xd5, 0xc4, 0x1c, 0xea, - 0xa7, 0x50, 0x4f, 0xc3, 0x25, 0x6b, 0xbc, 0x06, 0x65, 0xad, 0xdd, 0xdc, 0xd1, 0xfb, 0x3d, 0xb4, - 0x81, 0xd1, 0x26, 0xa6, 0xe4, 0x73, 0xad, 0x43, 0x06, 0xf4, 0x6f, 0x80, 0xb2, 0x38, 0x30, 0xec, - 0x09, 0x1a, 0x25, 0xd3, 0x99, 0x63, 0x71, 0x41, 0x91, 0x4c, 0xd9, 0xad, 0x15, 0x23, 0x29, 0xc8, - 0x68, 0xc6, 0xea, 0xe3, 0x54, 0x5a, 0xfd, 0xff, 0x80, 0x2d, 0x8f, 0xe0, 0xaf, 0xae, 0xf8, 0xff, - 0x91, 0x81, 0xdc, 0x81, 0x63, 0xb8, 0xec, 0x35, 0xc8, 0xd3, 0x0b, 0x3b, 0x82, 0x7b, 0x56, 0xa4, - 0x0d, 0x8e, 0xcb, 0x82, 0x70, 0xec, 0x6d, 0xc8, 0x86, 0xe3, 0xe8, 0x56, 0xf6, 0xb5, 0x0b, 0x16, - 0xdf, 0xde, 0x9a, 0x86, 0x54, 0xec, 0x1e, 0x64, 0x4d, 0x33, 0x0a, 0xc6, 0x16, 0x56, 0x3f, 0x9a, - 0x8a, 0x3b, 0xd6, 0xc4, 0x76, 0x6d, 0xf1, 0x22, 0x10, 0x92, 0xb0, 0xd7, 0x21, 0x6b, 0x8e, 0x9d, - 0x74, 0x64, 0x3d, 0x37, 0x2a, 0xe3, 0x02, 0xcd, 0xb1, 0xc3, 0x54, 0xa8, 0x85, 0xfe, 0xb9, 0xee, - 0xcf, 0x5d, 0x0a, 0x65, 0x0a, 0x84, 0xb9, 0x53, 0x41, 0x65, 0x66, 0x4e, 0xf1, 0x50, 0x81, 0xb8, - 0xdd, 0x35, 0xf3, 0xad, 0x99, 0xe1, 0xc7, 0x86, 0x8e, 0x1d, 0x1c, 0x70, 0xc0, 0x76, 0x01, 0xe8, - 0x81, 0x4e, 0xf5, 0x1d, 0x7a, 0xfe, 0x05, 0x35, 0x6c, 0x35, 0xfa, 0x5a, 0x71, 0x79, 0x56, 0x60, - 0xd4, 0xbf, 0xc8, 0x42, 0x45, 0x6a, 0x0f, 0xfb, 0x10, 0x4a, 0x66, 0x7a, 0x23, 0x5e, 0x5f, 0x6a, - 0xf4, 0x83, 0x9d, 0x68, 0x0b, 0x9a, 0x62, 0x79, 0x7f, 0x0a, 0xb5, 0xc0, 0x0a, 0xf5, 0x17, 0x86, - 0x6f, 0xf3, 0xa7, 0xb1, 0xd6, 0x65, 0x17, 0xfa, 0xc0, 0x0a, 0x9f, 0x45, 0x98, 0xbd, 0x35, 0xad, - 0x1a, 0x48, 0x69, 0x32, 0x03, 0x44, 0x97, 0xb2, 0xa9, 0xe7, 0xc3, 0x38, 0x70, 0x6f, 0x4d, 0x8b, - 0xf0, 0x48, 0x6a, 0x9d, 0x59, 0xe3, 0x79, 0x18, 0x99, 0x01, 0xb5, 0xa8, 0x43, 0x04, 0xa4, 0x97, - 0x0a, 0xf9, 0x27, 0x7b, 0x88, 0xbc, 0xce, 0x70, 0x1c, 0x8f, 0x74, 0xb6, 0xbc, 0xec, 0xd0, 0xde, - 0x89, 0xe1, 0xfc, 0x65, 0xc4, 0x28, 0xc5, 0xde, 0x80, 0xbc, 0x17, 0x1e, 0x5b, 0x91, 0xf2, 0x1c, - 0x3d, 0x24, 0x83, 0xa0, 0x9d, 0x56, 0x17, 0x57, 0x0a, 0xa1, 0xd5, 0x9f, 0x67, 0xa0, 0x28, 0x46, - 0x80, 0x6d, 0x42, 0x6d, 0xd0, 0x1e, 0xea, 0xcf, 0x9a, 0x5a, 0xa7, 0xb9, 0xdd, 0x6d, 0x8b, 0x0b, - 0x01, 0x4f, 0xb4, 0x66, 0x4f, 0xf0, 0x49, 0xad, 0xfd, 0xac, 0xff, 0xb4, 0xcd, 0x5d, 0x5c, 0x3b, - 0xed, 0xde, 0xe7, 0x4a, 0x96, 0x7b, 0x79, 0xdb, 0x07, 0x4d, 0x0d, 0xb9, 0x64, 0x05, 0x8a, 0xed, - 0xcf, 0xda, 0xad, 0x43, 0x62, 0x93, 0x75, 0x80, 0x9d, 0x76, 0xb3, 0xdb, 0xed, 0xb7, 0x90, 0x6d, - 0x16, 0x18, 0x83, 0x7a, 0x4b, 0x6b, 0x37, 0x87, 0x6d, 0xbd, 0xd9, 0x6a, 0xf5, 0x0f, 0x7b, 0x43, - 0xa5, 0x88, 0x35, 0x36, 0xbb, 0xc3, 0xb6, 0x16, 0x83, 0xe8, 0x71, 0xaf, 0x1d, 0xad, 0x7f, 0x10, - 0x43, 0xca, 0xdb, 0x65, 0x34, 0xc9, 0x68, 0xae, 0xd4, 0xbf, 0xac, 0x43, 0x3d, 0xbd, 0x34, 0xd9, - 0x27, 0x50, 0x32, 0xcd, 0xd4, 0x1c, 0xdf, 0x5c, 0xb5, 0x84, 0x1f, 0xec, 0x98, 0xd1, 0x34, 0xf3, - 0x0f, 0x76, 0x27, 0xda, 0x48, 0xeb, 0x4b, 0x1b, 0x29, 0xda, 0x46, 0x3f, 0x84, 0x0d, 0xf1, 0x10, - 0x8b, 0x69, 0x84, 0xc6, 0xc8, 0x08, 0xac, 0xf4, 0x2e, 0x69, 0x11, 0x72, 0x47, 0xe0, 0xf6, 0xd6, - 0xb4, 0xfa, 0x38, 0x05, 0x61, 0xdf, 0x87, 0xba, 0x41, 0x76, 0x6e, 0x9c, 0x3f, 0x27, 0x2b, 0x81, - 0x4d, 0xc4, 0x49, 0xd9, 0x6b, 0x86, 0x0c, 0xc0, 0x85, 0x68, 0xfa, 0xde, 0x2c, 0xc9, 0x9c, 0x4f, - 0x9d, 0xe5, 0xf8, 0xde, 0x4c, 0xca, 0x5b, 0x35, 0xa5, 0x34, 0xfb, 0x18, 0xaa, 0xa2, 0xe5, 0x89, - 0x27, 0x21, 0xde, 0xb2, 0xbc, 0xd9, 0xa4, 0xd4, 0xed, 0xad, 0x69, 0x95, 0x71, 0x92, 0x64, 0x8f, - 0x50, 0x93, 0x4b, 0x74, 0xf1, 0xa2, 0xbc, 0xd6, 0xa8, 0xb5, 0x51, 0x2e, 0x30, 0xe2, 0x14, 0x7b, - 0x1f, 0x80, 0xda, 0xc9, 0xf3, 0x94, 0x52, 0x21, 0x18, 0xbe, 0x37, 0x8b, 0xb2, 0x94, 0xcd, 0x28, - 0x21, 0x35, 0x8f, 0xfb, 0x81, 0xca, 0xcb, 0xcd, 0x23, 0x5f, 0x50, 0xd2, 0x3c, 0xee, 0x42, 0x8a, - 0x9b, 0xc7, 0xb3, 0xc1, 0x52, 0xf3, 0xa2, 0x5c, 0xbc, 0x79, 0x3c, 0x53, 0xd4, 0x3c, 0x9e, 0xa7, - 0xb2, 0xd8, 0xbc, 0x28, 0x0b, 0x35, 0x8f, 0xe7, 0xf8, 0xfe, 0x92, 0xee, 0x5e, 0xbd, 0x50, 0x77, - 0xc7, 0x69, 0x4b, 0x6b, 0xef, 0xdf, 0x87, 0x7a, 0x70, 0xec, 0x9d, 0x4a, 0x0c, 0xa4, 0x26, 0xe7, - 0x1e, 0x1c, 0x7b, 0xa7, 0x32, 0x07, 0xa9, 0x05, 0x32, 0x00, 0x5b, 0xcb, 0xbb, 0x48, 0x37, 0xd5, - 0xeb, 0x72, 0x6b, 0xa9, 0x87, 0xcf, 0x6c, 0xeb, 0x14, 0x5b, 0x6b, 0x44, 0x09, 0x1c, 0x94, 0xc4, - 0xef, 0x11, 0x88, 0x98, 0xa1, 0x54, 0x38, 0x81, 0xa8, 0x09, 0x62, 0x0f, 0x48, 0x80, 0x6b, 0x6b, - 0xee, 0xca, 0xd9, 0x14, 0x79, 0x6d, 0x1d, 0xba, 0xa9, 0x8c, 0x55, 0x4e, 0x2a, 0xb2, 0x26, 0xbb, - 0x22, 0xb0, 0xbe, 0x9c, 0x5b, 0xee, 0xd8, 0x12, 0xd1, 0x45, 0xa9, 0x5d, 0x31, 0x10, 0xb8, 0x64, - 0x57, 0x44, 0x90, 0x78, 0x5d, 0xc7, 0xd9, 0xd9, 0xe2, 0xba, 0x96, 0x32, 0xd3, 0xba, 0x8e, 0xb3, - 0xc6, 0x1b, 0x2a, 0xce, 0x7b, 0x69, 0x69, 0x43, 0x49, 0x99, 0xf9, 0x86, 0x8a, 0x73, 0x3f, 0x02, - 0xb1, 0x9a, 0xf8, 0xe0, 0xa6, 0x62, 0x90, 0x78, 0xab, 0xc5, 0xe8, 0xc2, 0x38, 0x4e, 0xe1, 0x5a, - 0xf5, 0x2d, 0xb4, 0x15, 0xc4, 0x52, 0xb8, 0x22, 0xaf, 0x55, 0x8d, 0x30, 0xf1, 0x56, 0xf2, 0x93, - 0xa4, 0xfa, 0xc7, 0x79, 0x28, 0x0a, 0xa6, 0xc3, 0x2e, 0xc1, 0x86, 0xe0, 0x7d, 0x3b, 0xcd, 0x61, - 0x73, 0xbb, 0x39, 0x40, 0x6d, 0x85, 0x41, 0x9d, 0x33, 0xbf, 0x18, 0x96, 0x41, 0x86, 0x48, 0xdc, - 0x2f, 0x06, 0xad, 0x23, 0x43, 0x14, 0x79, 0xf9, 0xcb, 0x88, 0x59, 0xb6, 0x01, 0x15, 0x9e, 0x91, - 0x03, 0xe8, 0xa2, 0x1e, 0xe5, 0xe2, 0xe9, 0xbc, 0x94, 0x85, 0x1f, 0x7d, 0x14, 0x92, 0x2c, 0x1c, - 0x50, 0x8c, 0xb3, 0x44, 0x67, 0x23, 0x0c, 0xea, 0x43, 0xed, 0xb0, 0xd7, 0x4a, 0xea, 0x29, 0xd3, - 0xe5, 0x2a, 0x5e, 0xcc, 0xb3, 0x4e, 0xfb, 0xb9, 0x02, 0x98, 0x89, 0x97, 0x42, 0xe9, 0x0a, 0xea, - 0x5b, 0x54, 0x08, 0x25, 0xab, 0xec, 0x1a, 0x5c, 0x1a, 0xec, 0xf5, 0x9f, 0xeb, 0x3c, 0x53, 0xdc, - 0x85, 0x1a, 0xbb, 0x0c, 0x8a, 0x84, 0xe0, 0xc5, 0xd7, 0xb1, 0x4a, 0x82, 0x46, 0x84, 0x03, 0x65, - 0x83, 0x0e, 0x17, 0x11, 0x36, 0xe4, 0x02, 0x48, 0xc1, 0xae, 0xf0, 0xac, 0xfd, 0xee, 0xe1, 0x7e, - 0x6f, 0xa0, 0x6c, 0x62, 0x23, 0x08, 0xc2, 0x5b, 0xce, 0xe2, 0x62, 0x12, 0xb1, 0x75, 0x89, 0x24, - 0x19, 0xc2, 0x9e, 0x37, 0xb5, 0x5e, 0xa7, 0xf7, 0x64, 0xa0, 0x5c, 0x8e, 0x4b, 0x6e, 0x6b, 0x5a, - 0x5f, 0x1b, 0x28, 0x57, 0x62, 0xc0, 0x60, 0xd8, 0x1c, 0x1e, 0x0e, 0x94, 0xab, 0x71, 0x2b, 0x0f, - 0xb4, 0x7e, 0xab, 0x3d, 0x18, 0x74, 0x3b, 0x83, 0xa1, 0x72, 0x8d, 0x5d, 0x81, 0xcd, 0xa4, 0x45, - 0x11, 0x71, 0x43, 0x6a, 0xa8, 0xf6, 0xa4, 0x3d, 0x54, 0xae, 0xc7, 0xcd, 0x68, 0xf5, 0xbb, 0xdd, - 0x26, 0x1d, 0x83, 0xdd, 0x40, 0x22, 0x3a, 0x1f, 0x14, 0xbd, 0x79, 0x05, 0xdb, 0x75, 0xd8, 0x93, - 0x41, 0x37, 0xa5, 0xa5, 0x31, 0x68, 0xff, 0xe4, 0xb0, 0xdd, 0x6b, 0xb5, 0x95, 0x57, 0x93, 0xa5, - 0x11, 0xc3, 0x6e, 0xc5, 0x4b, 0x23, 0x06, 0xdd, 0x8e, 0xeb, 0x8c, 0x40, 0x03, 0x65, 0x0b, 0xcb, - 0x13, 0xed, 0xe8, 0xf5, 0xda, 0xad, 0x21, 0xf6, 0xf5, 0x4e, 0x3c, 0x8a, 0x87, 0x07, 0x4f, 0xb4, - 0xe6, 0x4e, 0x5b, 0x51, 0x11, 0xa2, 0xb5, 0x7b, 0xcd, 0xfd, 0x68, 0xb6, 0x5f, 0xdb, 0xae, 0xd2, - 0x6b, 0xcb, 0x42, 0x5c, 0xaa, 0x3f, 0x06, 0x26, 0x3f, 0x5b, 0x2a, 0x5e, 0x26, 0x63, 0x90, 0x9b, - 0xf8, 0xde, 0x34, 0xba, 0xaa, 0x8e, 0xdf, 0x68, 0xab, 0xcd, 0xe6, 0x23, 0x3a, 0xcb, 0x4a, 0x6e, - 0xae, 0xca, 0x20, 0xf5, 0x8f, 0x33, 0x50, 0x4f, 0x8b, 0x4a, 0x54, 0x11, 0xed, 0x89, 0xee, 0x7a, - 0x21, 0x7f, 0xf2, 0x2a, 0x88, 0x3c, 0x51, 0xf6, 0xa4, 0xe7, 0x85, 0xf4, 0xe6, 0x15, 0x99, 0x8e, - 0xb1, 0xe4, 0xe3, 0xa5, 0xc6, 0x69, 0xd6, 0x81, 0x4b, 0xa9, 0x57, 0x5d, 0x53, 0x0f, 0x8e, 0x35, - 0xe2, 0xb7, 0x28, 0x17, 0xda, 0xaf, 0xb1, 0x60, 0xb9, 0x4f, 0xe2, 0xfe, 0x71, 0x2e, 0xb9, 0x7f, - 0xbc, 0x07, 0xb5, 0x94, 0x64, 0x26, 0x8b, 0x7f, 0x92, 0x6e, 0x69, 0xc9, 0x9e, 0xbc, 0xbc, 0x99, - 0xea, 0x1f, 0x65, 0xa0, 0x2a, 0xcb, 0xe9, 0xef, 0x5c, 0x12, 0xdd, 0x50, 0x11, 0xdf, 0xba, 0x6d, - 0x46, 0x4f, 0x5d, 0x45, 0xa0, 0x0e, 0xbd, 0x32, 0xcf, 0x7d, 0xb0, 0xbb, 0x27, 0x83, 0xb8, 0x3b, - 0x32, 0x08, 0x4d, 0x66, 0xba, 0xb9, 0xb8, 0xfb, 0x14, 0x09, 0xc4, 0x1d, 0x97, 0x04, 0xa2, 0xde, - 0x86, 0xf2, 0xee, 0x49, 0x14, 0x9e, 0x20, 0x3f, 0xfc, 0x56, 0xe6, 0xd7, 0x9d, 0xd5, 0x3f, 0xcd, - 0x40, 0x3d, 0x79, 0xcc, 0x83, 0x82, 0x1e, 0xf9, 0x6b, 0xc0, 0x7c, 0x39, 0xac, 0x9b, 0xa3, 0xe4, - 0x01, 0xfa, 0x75, 0xf9, 0x01, 0xfa, 0xd7, 0x44, 0x61, 0x59, 0x59, 0x9a, 0xc5, 0x75, 0x89, 0xcb, - 0xd4, 0x8f, 0xa0, 0x8a, 0xff, 0x35, 0x6b, 0x62, 0xf9, 0xbe, 0x15, 0x3d, 0x8c, 0xbc, 0x44, 0x9c, - 0x22, 0x22, 0x8b, 0xc4, 0x9a, 0x08, 0xc5, 0x68, 0xe5, 0x7b, 0x23, 0x88, 0x57, 0xff, 0x45, 0x0e, - 0x2a, 0x92, 0xd6, 0xf3, 0x8d, 0x96, 0xdf, 0x4d, 0x28, 0x27, 0x2f, 0x59, 0x88, 0x1b, 0xac, 0x31, - 0x20, 0x35, 0x57, 0xd9, 0x85, 0xb9, 0x6a, 0x40, 0x51, 0x44, 0x47, 0x0a, 0xbf, 0x67, 0x94, 0x4c, - 0x3b, 0xf6, 0xf2, 0x2f, 0x71, 0xbd, 0x7f, 0x00, 0x55, 0xc9, 0x2b, 0x17, 0x3d, 0x8b, 0xb3, 0x48, - 0x5f, 0x49, 0x3c, 0x74, 0x01, 0xbb, 0x02, 0x85, 0xc9, 0x89, 0x6e, 0x8e, 0x22, 0x37, 0x67, 0x7e, - 0x72, 0xb2, 0x33, 0xa2, 0xa3, 0x8b, 0x49, 0x2c, 0xe8, 0xb9, 0xaf, 0xa4, 0x34, 0x89, 0xc4, 0xf9, - 0x3d, 0x28, 0x4e, 0x4e, 0xf8, 0xf5, 0xb8, 0xb2, 0x1c, 0xf0, 0x93, 0x0c, 0x79, 0x61, 0x72, 0x42, - 0x97, 0xe9, 0x3e, 0x05, 0x65, 0xc1, 0xa7, 0x1a, 0x88, 0x93, 0xc9, 0xc5, 0x46, 0x6d, 0xa4, 0xdd, - 0xab, 0x01, 0x7b, 0x0f, 0x2e, 0x0b, 0xc9, 0x6b, 0x04, 0x3a, 0x8f, 0xd7, 0xa7, 0xc7, 0x51, 0xf8, - 0x0b, 0x72, 0x9b, 0x1c, 0xd7, 0x0c, 0x06, 0x84, 0xc1, 0xc5, 0xaa, 0x42, 0x55, 0x5a, 0xbb, 0xfc, - 0xe5, 0x99, 0xb2, 0x96, 0x82, 0xb1, 0xc7, 0x50, 0x9d, 0x9c, 0xf0, 0xb5, 0x30, 0xf4, 0xf6, 0x2d, - 0x11, 0x83, 0x7d, 0x79, 0x71, 0x15, 0x50, 0xa8, 0x6e, 0x8a, 0x92, 0xbd, 0x0b, 0xcc, 0xb7, 0x42, - 0xcb, 0xa5, 0x9e, 0x98, 0x96, 0x61, 0x3a, 0xb6, 0x6b, 0x91, 0xb2, 0x95, 0xd5, 0x36, 0x63, 0xcc, - 0x8e, 0x40, 0xa8, 0xff, 0x32, 0x03, 0xf5, 0x44, 0xfb, 0xc5, 0x0d, 0xcd, 0xee, 0xcb, 0x4f, 0x82, - 0x37, 0x16, 0x15, 0x64, 0x24, 0x79, 0x30, 0x3c, 0x9f, 0xf1, 0x67, 0x43, 0x57, 0x3d, 0x1f, 0xb4, - 0xca, 0xe5, 0x9a, 0x5d, 0xe5, 0x72, 0x55, 0x9f, 0x40, 0x76, 0x78, 0x3e, 0xe3, 0x9e, 0x16, 0x94, - 0x81, 0xdc, 0x2a, 0xe3, 0xd2, 0x8f, 0xe2, 0x13, 0x9e, 0xb6, 0x3f, 0xe7, 0xb7, 0xf7, 0x0f, 0xb4, - 0xce, 0x7e, 0x53, 0xfb, 0x9c, 0x22, 0x4f, 0x48, 0x4b, 0xd8, 0xed, 0x6b, 0xed, 0xce, 0x93, 0x1e, - 0x01, 0x72, 0xe4, 0x87, 0x49, 0x9a, 0xd8, 0x34, 0xcd, 0xdd, 0x13, 0xf9, 0x15, 0x95, 0x4c, 0xea, - 0x15, 0x95, 0xf4, 0x85, 0xdf, 0xf5, 0xc5, 0x0b, 0xbf, 0x2c, 0xde, 0xd1, 0x31, 0x7b, 0x60, 0x6f, - 0x42, 0x6e, 0x72, 0x62, 0x9d, 0xa7, 0x4d, 0x9c, 0xf4, 0x66, 0x24, 0x02, 0xf5, 0x17, 0x19, 0x60, - 0xa9, 0x86, 0x70, 0xad, 0xfb, 0xbb, 0xb6, 0xe5, 0x13, 0x68, 0x88, 0x37, 0x36, 0x39, 0x95, 0xe4, - 0xe3, 0x15, 0x43, 0x7a, 0xc5, 0x4b, 0x22, 0xfb, 0x92, 0x17, 0x8e, 0xd8, 0x7b, 0xc0, 0x1f, 0x39, - 0xc4, 0x05, 0x92, 0x76, 0x6a, 0x48, 0xbc, 0x42, 0x4b, 0x68, 0x92, 0x57, 0x0d, 0xe5, 0xd7, 0x1a, - 0xb9, 0x7b, 0x78, 0x23, 0x99, 0x35, 0xe2, 0x1f, 0xea, 0xef, 0x65, 0xe0, 0x52, 0x7a, 0x41, 0xfc, - 0x72, 0xbd, 0x4c, 0x3f, 0x4d, 0x99, 0x5d, 0x7c, 0x9a, 0x72, 0xd5, 0x7a, 0xca, 0xad, 0x5c, 0x4f, - 0x7f, 0x37, 0x03, 0x97, 0xa5, 0xd1, 0x4f, 0xec, 0xa4, 0xbf, 0xa5, 0x96, 0x49, 0x2f, 0x54, 0xe6, - 0x52, 0x2f, 0x54, 0xaa, 0x7f, 0x98, 0x81, 0xab, 0x0b, 0x2d, 0xd1, 0xac, 0xbf, 0xd5, 0xb6, 0xa4, - 0x5f, 0xb2, 0x24, 0x17, 0x35, 0x0f, 0x75, 0xe4, 0xd7, 0x12, 0x59, 0xfa, 0x69, 0x4a, 0xba, 0x97, - 0xfd, 0xaf, 0xd2, 0x8d, 0x34, 0x93, 0x5b, 0x46, 0xec, 0x23, 0xa8, 0x24, 0x1a, 0x53, 0xf4, 0x50, - 0xc8, 0xca, 0x2b, 0x4a, 0x32, 0xdd, 0x4a, 0x36, 0xba, 0xfe, 0xcd, 0xd8, 0xe8, 0x63, 0xa8, 0xc6, - 0x05, 0xef, 0x58, 0x93, 0xb4, 0x37, 0x62, 0xe1, 0xa9, 0xab, 0x14, 0xa5, 0xfa, 0x21, 0x6c, 0x26, - 0xbd, 0x68, 0x89, 0xe7, 0xd9, 0x6e, 0x43, 0xc5, 0xb5, 0x4e, 0xf5, 0xe8, 0xf1, 0x36, 0x11, 0xb3, - 0xe3, 0x5a, 0xa7, 0x82, 0x40, 0xdd, 0x95, 0xf9, 0x5e, 0xfc, 0xd2, 0xbe, 0x63, 0xa6, 0x82, 0x3f, - 0x3c, 0xc7, 0x8c, 0x50, 0x58, 0x9a, 0x34, 0x31, 0x45, 0xd7, 0x3a, 0xa5, 0x35, 0x77, 0x2a, 0xca, - 0x69, 0x9a, 0xa6, 0x38, 0x30, 0x5f, 0xf5, 0xe8, 0xd1, 0x75, 0x28, 0xcd, 0xfc, 0xd4, 0xcc, 0x16, - 0x67, 0x3e, 0xaf, 0xf6, 0xae, 0x88, 0x08, 0xba, 0xe8, 0x70, 0x9d, 0xc7, 0x08, 0x89, 0x5f, 0xe2, - 0xc8, 0x25, 0xbf, 0xc4, 0xf1, 0x91, 0x60, 0x79, 0xb8, 0xff, 0x44, 0xcd, 0xf1, 0x21, 0x7a, 0xe6, - 0x5e, 0x8d, 0x0e, 0xd1, 0x49, 0x03, 0xb4, 0xbe, 0x14, 0x41, 0x49, 0xf8, 0xa9, 0x6e, 0x43, 0x45, - 0xb2, 0xec, 0x50, 0x35, 0x91, 0xbc, 0x22, 0x41, 0xfa, 0x19, 0x99, 0x64, 0x80, 0xb4, 0x4a, 0xe2, - 0x14, 0x09, 0xd4, 0xdf, 0x07, 0x80, 0x04, 0x97, 0x52, 0x18, 0x32, 0x0b, 0x0a, 0xc3, 0xb7, 0x3a, - 0x91, 0xff, 0x10, 0xea, 0x63, 0x6f, 0x76, 0xae, 0x27, 0x39, 0xb2, 0x2b, 0x73, 0x54, 0x91, 0x6a, - 0x98, 0xdc, 0xef, 0x59, 0x3e, 0x69, 0xcd, 0xad, 0x3c, 0x69, 0xfd, 0x00, 0x8a, 0xdc, 0x71, 0x1f, - 0x88, 0xfb, 0x61, 0xd7, 0x16, 0xfb, 0xf9, 0x40, 0xc4, 0xbe, 0x46, 0x74, 0xac, 0x8d, 0x56, 0xb9, - 0x78, 0xc9, 0x51, 0xbe, 0x2d, 0x76, 0x6b, 0x39, 0x67, 0x44, 0xc6, 0x9f, 0x0f, 0x33, 0xe4, 0xa4, - 0xa4, 0x24, 0x84, 0x53, 0xe1, 0x4d, 0x22, 0x25, 0xa1, 0x28, 0x2b, 0x09, 0xc3, 0x29, 0xf7, 0x21, - 0xa1, 0x92, 0xf0, 0x2e, 0x5c, 0x12, 0x31, 0xf8, 0x98, 0x01, 0x87, 0x93, 0xe8, 0x79, 0xb8, 0x95, - 0x78, 0xdb, 0x63, 0x38, 0x25, 0xed, 0x1b, 0xc9, 0x3f, 0x83, 0xcb, 0xe3, 0x63, 0xc3, 0x3d, 0xb2, - 0xf4, 0x70, 0xe4, 0xe8, 0xf4, 0x90, 0xb8, 0x3e, 0x35, 0x66, 0x42, 0xed, 0x79, 0x73, 0xa9, 0xb1, - 0x2d, 0x22, 0x1e, 0x8e, 0x1c, 0x8a, 0xd0, 0x89, 0xcf, 0xe3, 0x37, 0xc7, 0x8b, 0xf0, 0x85, 0xd3, - 0x28, 0x58, 0x3c, 0x8d, 0x5a, 0xd2, 0x66, 0x2a, 0xcb, 0xda, 0xcc, 0x8d, 0xff, 0x90, 0x83, 0x82, - 0x88, 0x05, 0xbc, 0x0f, 0x39, 0xd3, 0xf7, 0x66, 0x71, 0xc8, 0xde, 0x0a, 0xed, 0x82, 0x7e, 0x75, - 0x08, 0x15, 0x91, 0x07, 0x50, 0x30, 0x4c, 0x53, 0x9f, 0x9c, 0xa4, 0x4f, 0x8c, 0x16, 0x04, 0xfd, - 0xde, 0x9a, 0x96, 0x37, 0x48, 0xe2, 0x7f, 0x02, 0x65, 0xa4, 0x4f, 0xe2, 0xaf, 0x2a, 0xcb, 0xea, - 0x4b, 0x24, 0x92, 0xf7, 0xd6, 0xb4, 0x92, 0x11, 0x89, 0xe7, 0x1f, 0xa4, 0x7d, 0x6f, 0x5c, 0x5e, - 0xde, 0x58, 0xca, 0x7a, 0x91, 0x17, 0xee, 0xd7, 0x81, 0x3b, 0x63, 0x62, 0x6e, 0x93, 0x97, 0x0f, - 0x27, 0x96, 0x78, 0xd3, 0xde, 0x9a, 0xc6, 0xf7, 0x5c, 0xc4, 0xab, 0x3e, 0x8a, 0xfc, 0x62, 0xf1, - 0xef, 0x83, 0xac, 0x18, 0x19, 0xe4, 0x15, 0xb1, 0x73, 0x8c, 0x18, 0x07, 0x66, 0x33, 0xcd, 0x28, - 0x6c, 0xa7, 0xb8, 0x94, 0x2d, 0xe6, 0x48, 0x94, 0x2d, 0x66, 0x4f, 0x8f, 0xa1, 0x42, 0x2e, 0x2a, - 0x91, 0xaf, 0xb4, 0x34, 0xb4, 0x09, 0x43, 0x21, 0xc7, 0x7b, 0xc2, 0x5e, 0x5a, 0x51, 0x3f, 0x7d, - 0x4b, 0xf6, 0x6d, 0xde, 0x5c, 0x39, 0x50, 0x5a, 0xec, 0xe6, 0xe4, 0x9d, 0xd5, 0x78, 0x1e, 0xb6, - 0x0d, 0x55, 0x43, 0x92, 0x34, 0xc2, 0xd1, 0x79, 0x73, 0xc5, 0x3c, 0xc5, 0x34, 0x54, 0x86, 0x94, - 0x4e, 0x0e, 0xe0, 0x6e, 0x68, 0x70, 0x75, 0xf5, 0x52, 0x96, 0x23, 0x49, 0x72, 0x3c, 0x92, 0x44, - 0x4d, 0xbf, 0xaf, 0x92, 0xbe, 0xe4, 0x2a, 0xc5, 0x95, 0xfc, 0x08, 0x6d, 0x64, 0x79, 0xf3, 0x56, - 0xa0, 0x18, 0xbd, 0x4a, 0x4c, 0x61, 0xb1, 0xad, 0xfe, 0xc1, 0xe7, 0x4a, 0x06, 0xc1, 0x9d, 0xde, - 0x60, 0xd8, 0xec, 0x89, 0xe3, 0xd5, 0x4e, 0x4f, 0x1c, 0xaf, 0xaa, 0xff, 0x26, 0x0b, 0xe5, 0xd8, - 0x3d, 0xfc, 0xdd, 0x0d, 0xe3, 0xd8, 0xe2, 0xcc, 0xca, 0x16, 0xe7, 0x82, 0xa6, 0x26, 0x3f, 0x5a, - 0xb2, 0x91, 0xd6, 0x87, 0x82, 0xe5, 0xfb, 0x77, 0xf9, 0x6f, 0x78, 0xff, 0x4e, 0x8e, 0x4c, 0x2c, - 0xa4, 0x23, 0x13, 0x17, 0x5e, 0xa6, 0x2e, 0x52, 0x98, 0x8a, 0xfc, 0x32, 0xf5, 0x85, 0xf1, 0x29, - 0xa5, 0x8b, 0xe3, 0x53, 0xe8, 0xa7, 0xd5, 0x9e, 0xd9, 0xd6, 0xa9, 0x08, 0xd0, 0x13, 0xa9, 0xb4, - 0xf8, 0x80, 0x97, 0x88, 0x8f, 0x6f, 0xc0, 0x8a, 0xd8, 0x43, 0xb8, 0x3c, 0x39, 0x89, 0x5f, 0xe1, - 0x4c, 0x0c, 0xac, 0x2a, 0x75, 0x63, 0x25, 0x4e, 0xfd, 0xdd, 0x0c, 0x40, 0xe2, 0x43, 0xfd, 0xa5, - 0x1d, 0x3c, 0x92, 0x0d, 0x9d, 0xfd, 0x1a, 0x1b, 0xfa, 0x25, 0x0f, 0x7b, 0xa8, 0x5f, 0x42, 0x39, - 0xf6, 0x9a, 0x7f, 0xf7, 0x35, 0xf6, 0xad, 0xaa, 0xfc, 0xcd, 0xc8, 0xd9, 0x15, 0xbb, 0x9d, 0x7f, - 0xd9, 0xb1, 0x48, 0x55, 0x9f, 0x7d, 0x49, 0xf5, 0x67, 0xdc, 0xe3, 0x14, 0x57, 0xfe, 0x2b, 0xde, - 0x58, 0xf2, 0x9a, 0xcf, 0xa5, 0xd6, 0xbc, 0x3a, 0x17, 0x6e, 0xb3, 0x5f, 0xbe, 0xea, 0x6f, 0xd5, - 0xe1, 0xbf, 0xce, 0x44, 0xbe, 0x9d, 0xf8, 0x6d, 0xd3, 0x0b, 0x15, 0xad, 0xd5, 0xee, 0xa9, 0x6f, - 0x53, 0xdd, 0xd7, 0x5a, 0x9b, 0xb9, 0xaf, 0xb3, 0x36, 0xdf, 0x84, 0x3c, 0x17, 0x08, 0xf9, 0x8b, - 0x2c, 0x4d, 0x8e, 0x7f, 0xe9, 0xaf, 0x01, 0xa8, 0xaa, 0x50, 0x2c, 0x79, 0x7f, 0x2f, 0x47, 0xe5, - 0x46, 0xbf, 0x64, 0x40, 0x41, 0xd4, 0xbf, 0x9d, 0xe1, 0xdc, 0xf5, 0xbb, 0x8e, 0xc9, 0xaf, 0xcc, - 0xdc, 0xfc, 0xa7, 0xeb, 0x50, 0x4b, 0x1d, 0x98, 0x7d, 0x87, 0xc6, 0xac, 0xe4, 0xe6, 0xd9, 0xd5, - 0xdc, 0xfc, 0xbb, 0x3c, 0x59, 0xf5, 0x7f, 0x44, 0x02, 0xa4, 0x62, 0xcc, 0x4a, 0xe9, 0x18, 0x33, - 0xe4, 0xa6, 0xd5, 0x94, 0x56, 0xbe, 0x4a, 0x7f, 0xcf, 0xac, 0xd4, 0xdf, 0x6f, 0xc5, 0x3f, 0x24, - 0xd6, 0xd9, 0xe1, 0x86, 0x65, 0x4d, 0x93, 0x20, 0xec, 0x53, 0xb8, 0xce, 0xb5, 0x1a, 0xae, 0xc8, - 0xe9, 0xde, 0x44, 0x8f, 0x7f, 0x66, 0x4c, 0xc4, 0xcd, 0x5d, 0xe5, 0x04, 0xfc, 0xa7, 0x22, 0x26, - 0xcd, 0x08, 0xab, 0x76, 0xa0, 0x96, 0x3a, 0xbd, 0x94, 0x7e, 0xb2, 0x30, 0x23, 0xff, 0x64, 0x21, - 0xdb, 0x82, 0xfc, 0xe9, 0xb1, 0xe5, 0x5b, 0x2b, 0x9e, 0x76, 0xe4, 0x08, 0xf5, 0xfb, 0x50, 0x95, - 0x23, 0x29, 0xd8, 0x3b, 0x90, 0xb7, 0x43, 0x6b, 0x1a, 0xd9, 0x56, 0x57, 0x97, 0x83, 0x2d, 0xc8, - 0x90, 0xe6, 0x44, 0xea, 0xcf, 0x33, 0xa0, 0x2c, 0xe2, 0xa4, 0xdf, 0x55, 0xcc, 0x5c, 0xf0, 0xbb, - 0x8a, 0xeb, 0xa9, 0x46, 0xae, 0xfa, 0x69, 0xc4, 0xf8, 0x79, 0xb9, 0xdc, 0x05, 0xcf, 0xcb, 0xb1, - 0x37, 0xa0, 0xe4, 0x5b, 0xf4, 0xa3, 0x75, 0xe6, 0x8a, 0x58, 0xe6, 0x18, 0xa7, 0xfe, 0x4e, 0x06, - 0x8a, 0x22, 0xec, 0x63, 0xa5, 0xb1, 0xfb, 0x16, 0x14, 0xf9, 0x0f, 0xd8, 0x45, 0xc6, 0xff, 0x52, - 0x1c, 0x64, 0x84, 0x67, 0xb7, 0x78, 0x30, 0x4c, 0xda, 0xf8, 0x3d, 0x70, 0x0c, 0x57, 0x23, 0xb8, - 0xf8, 0x05, 0x18, 0x63, 0x2a, 0xae, 0x11, 0xf2, 0x57, 0x3f, 0x80, 0x40, 0xfc, 0xc6, 0xe0, 0x0f, - 0xa0, 0x28, 0xc2, 0x4a, 0x56, 0x36, 0xe5, 0x65, 0x3f, 0xdd, 0xb6, 0x05, 0x90, 0xc4, 0x99, 0xac, - 0x2a, 0x41, 0xbd, 0x0f, 0xa5, 0x28, 0xb4, 0x04, 0xd7, 0x5f, 0x52, 0xb5, 0x88, 0x55, 0x97, 0x1b, - 0xe3, 0x88, 0xf7, 0x8f, 0xbb, 0xde, 0xf8, 0x84, 0xbc, 0x6a, 0xef, 0x01, 0xc5, 0xf0, 0x0f, 0x97, - 0x9e, 0x47, 0x49, 0xbf, 0x35, 0x1d, 0x13, 0xb1, 0xfb, 0x10, 0xb3, 0xe3, 0x97, 0x59, 0xcb, 0x6a, - 0x33, 0xba, 0x4b, 0x42, 0xab, 0xec, 0x91, 0xf0, 0x1e, 0x75, 0xe9, 0x21, 0xa3, 0x94, 0xc3, 0x26, - 0xd5, 0x26, 0x4d, 0x22, 0x53, 0xeb, 0x50, 0x95, 0xcf, 0xc3, 0xd5, 0x26, 0x6c, 0xee, 0x5b, 0xa1, - 0x81, 0x3c, 0x6b, 0x30, 0x36, 0x5c, 0xa4, 0xe7, 0xeb, 0x17, 0x3f, 0xd2, 0xeb, 0x77, 0x91, 0x4e, - 0xe3, 0x44, 0xea, 0xcf, 0x73, 0xa0, 0x2c, 0xe2, 0xbe, 0xee, 0x5e, 0xcd, 0x6d, 0xa8, 0x78, 0xb4, - 0x2e, 0x52, 0xbf, 0xf1, 0xc3, 0x41, 0x52, 0xc0, 0x6a, 0xea, 0x41, 0xfa, 0x92, 0x1d, 0xec, 0xf1, - 0x27, 0xe9, 0xaf, 0xf1, 0xb7, 0x3d, 0x1c, 0x6f, 0x4c, 0xcb, 0xba, 0x4a, 0x4f, 0x79, 0x74, 0xbd, - 0x31, 0x5d, 0xd7, 0x11, 0x06, 0x37, 0x0f, 0xd2, 0xaa, 0x6a, 0x25, 0x61, 0x65, 0xd3, 0xa1, 0x81, - 0x08, 0x63, 0x0d, 0x03, 0x71, 0x01, 0xaa, 0xc4, 0x01, 0xc3, 0x20, 0x7a, 0x94, 0x77, 0x2c, 0x7e, - 0x90, 0x26, 0x4b, 0x8f, 0xf2, 0xb6, 0x5c, 0xba, 0xad, 0x43, 0xbf, 0x9f, 0x34, 0x16, 0xbf, 0x6f, - 0x25, 0x1e, 0x41, 0x46, 0xd4, 0x6b, 0xfc, 0x27, 0x7b, 0x7c, 0x2b, 0x08, 0xf8, 0x2b, 0x5e, 0x65, - 0xf1, 0x90, 0x91, 0x00, 0xc6, 0xcf, 0x85, 0x89, 0x1f, 0x4c, 0x42, 0x12, 0x10, 0xcf, 0x85, 0xf1, - 0x9f, 0x4b, 0x42, 0x82, 0xeb, 0x50, 0xfa, 0xca, 0x73, 0x2d, 0x32, 0xdc, 0x2b, 0xd4, 0xaa, 0x22, - 0xa6, 0xf7, 0x8d, 0x99, 0xfa, 0xe7, 0x19, 0xb8, 0xbc, 0x38, 0xaa, 0xb4, 0x60, 0xaa, 0x50, 0x6a, - 0xf5, 0xbb, 0x7a, 0xaf, 0xb9, 0xdf, 0x56, 0xd6, 0xd8, 0x06, 0x54, 0xfa, 0xdb, 0x3f, 0x6e, 0xb7, - 0x86, 0x1c, 0x90, 0xa1, 0x7b, 0xa2, 0x03, 0x7d, 0xaf, 0xb3, 0xb3, 0xd3, 0xee, 0x71, 0x2b, 0xa5, - 0xbf, 0xfd, 0x63, 0xbd, 0xdb, 0x6f, 0xf1, 0xdf, 0x57, 0x89, 0x4e, 0xdf, 0x07, 0x4a, 0x8e, 0x4e, - 0xbc, 0x29, 0x26, 0x14, 0x93, 0x79, 0x1e, 0xf2, 0xf8, 0x7c, 0xa0, 0xb7, 0x7a, 0x43, 0xa5, 0x80, - 0xa9, 0xde, 0x61, 0xb7, 0x4b, 0x29, 0x8a, 0x6d, 0x6a, 0xf5, 0xf7, 0x0f, 0xb4, 0xf6, 0x60, 0xa0, - 0x0f, 0x3a, 0x3f, 0x6d, 0x2b, 0x25, 0xaa, 0x59, 0xeb, 0x3c, 0xe9, 0xf4, 0x38, 0xa0, 0xcc, 0x8a, - 0x90, 0xdd, 0xef, 0xf4, 0xf8, 0xfd, 0xd8, 0xfd, 0xe6, 0x67, 0x4a, 0x05, 0x3f, 0x06, 0x87, 0xfb, - 0x4a, 0xf5, 0xfe, 0x1d, 0xa8, 0xca, 0x3f, 0x52, 0x46, 0x51, 0x8e, 0x9e, 0x6b, 0xf1, 0xa7, 0x7b, - 0xbb, 0x5f, 0x7d, 0xa8, 0x64, 0xee, 0xff, 0xa6, 0xf4, 0xe3, 0x0f, 0x44, 0x23, 0x0e, 0x03, 0xe8, - 0x36, 0x20, 0xbf, 0x6d, 0x48, 0xae, 0x7f, 0xba, 0x9c, 0xb8, 0xd7, 0x1c, 0xec, 0xf1, 0x63, 0x02, - 0x81, 0x21, 0x40, 0x36, 0x79, 0xf2, 0x95, 0x2e, 0xfb, 0xd2, 0x67, 0x7c, 0xd8, 0x9e, 0xa7, 0x7b, - 0x98, 0x9d, 0x01, 0x76, 0x4e, 0x81, 0x2a, 0x7e, 0xc5, 0xb8, 0xe2, 0x7d, 0x15, 0x2a, 0xd2, 0x2b, - 0xdd, 0x54, 0x87, 0x11, 0x1c, 0x8b, 0x57, 0x64, 0xd1, 0xdc, 0x54, 0x32, 0xf7, 0xdf, 0x40, 0x89, - 0x21, 0xbf, 0x91, 0x0d, 0x50, 0xe8, 0x79, 0xfe, 0xd4, 0x70, 0x04, 0x9d, 0x35, 0x0f, 0x90, 0xee, - 0x3d, 0xb8, 0xb2, 0xf2, 0xc5, 0x6f, 0x8a, 0xd4, 0xb5, 0xa7, 0x33, 0xc7, 0xe2, 0xc1, 0xa6, 0x7b, - 0xe7, 0x23, 0xdf, 0x36, 0x95, 0xcc, 0xfd, 0xc7, 0xd1, 0x95, 0xb4, 0xa8, 0xee, 0x6e, 0xbf, 0xb9, - 0xc3, 0x27, 0x37, 0xbe, 0x8c, 0x3c, 0xdc, 0xe6, 0x2f, 0xc4, 0x6a, 0xed, 0xc1, 0x61, 0x77, 0x28, - 0x2e, 0x3e, 0xdf, 0xff, 0x11, 0x34, 0x2e, 0x8a, 0xba, 0xc4, 0x16, 0xb5, 0xf6, 0x9a, 0x14, 0xd9, - 0x8a, 0x93, 0xd9, 0xd7, 0x79, 0x2a, 0xc3, 0x03, 0x83, 0xbb, 0x6d, 0x8a, 0xc8, 0xb8, 0xff, 0xb3, - 0x8c, 0xc4, 0xc2, 0xa2, 0xc8, 0xb9, 0x18, 0x20, 0x66, 0x49, 0x06, 0x69, 0x96, 0x61, 0x2a, 0x19, - 0x76, 0x15, 0x58, 0x0a, 0xd4, 0xf5, 0xc6, 0x86, 0xa3, 0xac, 0x53, 0xec, 0x45, 0x04, 0xa7, 0xf8, - 0x66, 0x25, 0xcb, 0x5e, 0x85, 0xeb, 0x31, 0xac, 0xeb, 0x9d, 0x1e, 0xf8, 0x36, 0xda, 0xda, 0xe7, - 0x1c, 0x9d, 0xdb, 0xfe, 0xe1, 0x9f, 0xfd, 0xe2, 0x56, 0xe6, 0xdf, 0xfd, 0xe2, 0x56, 0xe6, 0xbf, - 0xfe, 0xe2, 0xd6, 0xda, 0xcf, 0xff, 0xdb, 0xad, 0xcc, 0x4f, 0xe5, 0x9f, 0x32, 0x9f, 0x1a, 0xa1, - 0x6f, 0x9f, 0xf1, 0x4d, 0x13, 0x25, 0x5c, 0xeb, 0xbd, 0xd9, 0xc9, 0xd1, 0x7b, 0xb3, 0xd1, 0x7b, - 0xc8, 0x99, 0x46, 0x05, 0xfa, 0xd1, 0xf2, 0x47, 0xff, 0x3b, 0x00, 0x00, 0xff, 0xff, 0xf8, 0x24, - 0x7a, 0x5d, 0x14, 0x7d, 0x00, 0x00, + 0x2e, 0x56, 0x51, 0x55, 0xc5, 0xe9, 0x6e, 0x01, 0x06, 0x16, 0x9f, 0x01, 0x7f, 0x71, 0x5e, 0x0d, + 0xf8, 0x29, 0x0e, 0xd6, 0x7e, 0x0a, 0x8c, 0x18, 0x08, 0x10, 0x03, 0x0e, 0x82, 0x20, 0x2f, 0xc9, + 0x83, 0x63, 0x04, 0x41, 0x80, 0x3c, 0x18, 0x49, 0x00, 0x27, 0xd8, 0x3c, 0xf8, 0x29, 0xf6, 0x83, + 0xf3, 0x92, 0xb7, 0xe0, 0x9c, 0x7b, 0xab, 0xea, 0x16, 0xc9, 0xd6, 0x48, 0xda, 0x35, 0x92, 0xbc, + 0x74, 0xd7, 0x3d, 0xe7, 0xdc, 0xff, 0x7b, 0xcf, 0xdf, 0x3d, 0xf7, 0x12, 0x60, 0xe6, 0x18, 0xee, + 0x83, 0x99, 0xef, 0x85, 0x1e, 0xcb, 0xe1, 0xf7, 0x8d, 0x77, 0x8e, 0xed, 0xf0, 0x64, 0x3e, 0x7a, + 0x30, 0xf6, 0xa6, 0xef, 0x1e, 0x7b, 0xc7, 0xde, 0xbb, 0x84, 0x1c, 0xcd, 0x27, 0x94, 0xa2, 0x04, + 0x7d, 0xf1, 0x4c, 0x37, 0xc0, 0xf1, 0xc6, 0xa7, 0xe2, 0x7b, 0x23, 0xb4, 0xa7, 0x56, 0x10, 0x1a, + 0xd3, 0x19, 0x07, 0xa8, 0x7f, 0x9a, 0x81, 0xdc, 0xf0, 0x62, 0x66, 0xb1, 0x3a, 0xac, 0xdb, 0x66, + 0x23, 0xb3, 0x9d, 0xb9, 0x97, 0xd7, 0xd6, 0x6d, 0x93, 0x6d, 0x43, 0xc5, 0xf5, 0xc2, 0xde, 0xdc, + 0x71, 0x8c, 0x91, 0x63, 0x35, 0xd6, 0xb7, 0x33, 0xf7, 0x4a, 0x9a, 0x0c, 0x62, 0x2f, 0x43, 0xd9, + 0x98, 0x87, 0x9e, 0x6e, 0xbb, 0x63, 0xbf, 0x91, 0x25, 0x7c, 0x09, 0x01, 0x1d, 0x77, 0xec, 0xb3, + 0x2d, 0xc8, 0x9f, 0xd9, 0x66, 0x78, 0xd2, 0xc8, 0x51, 0x89, 0x3c, 0x81, 0xd0, 0x60, 0x6c, 0x38, + 0x56, 0x23, 0xcf, 0xa1, 0x94, 0x40, 0x68, 0x48, 0x95, 0x14, 0xb6, 0x33, 0xf7, 0xca, 0x1a, 0x4f, + 0xb0, 0x5b, 0x00, 0x96, 0x3b, 0x9f, 0x3e, 0x37, 0x9c, 0xb9, 0x15, 0x34, 0x8a, 0x84, 0x92, 0x20, + 0xea, 0x0f, 0xa1, 0x3c, 0x0d, 0x8e, 0xf7, 0x2d, 0xc3, 0xb4, 0x7c, 0xf6, 0x12, 0x14, 0xa7, 0xc1, + 0xb1, 0x1e, 0x1a, 0xc7, 0xa2, 0x0b, 0x85, 0x69, 0x70, 0x3c, 0x34, 0x8e, 0xd9, 0x75, 0x28, 0x11, + 0xe2, 0x62, 0xc6, 0xfb, 0x90, 0xd7, 0x90, 0x10, 0x7b, 0xac, 0xfe, 0x4d, 0x1e, 0x8a, 0x5d, 0x3b, + 0xb4, 0x7c, 0xc3, 0x61, 0xd7, 0xa0, 0x60, 0x07, 0xee, 0xdc, 0x71, 0x28, 0x7b, 0x49, 0x13, 0x29, + 0x76, 0x0d, 0xf2, 0xf6, 0x27, 0xcf, 0x0d, 0x87, 0xe7, 0xdd, 0x5f, 0xd3, 0x78, 0x92, 0x35, 0xa0, + 0x60, 0xbf, 0xff, 0x11, 0x22, 0xb2, 0x02, 0x21, 0xd2, 0x84, 0x79, 0xf4, 0x10, 0x31, 0xb9, 0x18, + 0x43, 0x69, 0xc2, 0x7c, 0xf4, 0x01, 0x62, 0xb0, 0xf7, 0x59, 0xc2, 0x50, 0x1a, 0x6b, 0x99, 0x53, + 0x2d, 0x38, 0x00, 0x35, 0xac, 0x65, 0x1e, 0xd5, 0x32, 0xe7, 0xb5, 0x14, 0x05, 0x42, 0xa4, 0x09, + 0xc3, 0x6b, 0x29, 0xc5, 0x98, 0xb8, 0x96, 0x39, 0xaf, 0xa5, 0xbc, 0x9d, 0xb9, 0x97, 0x23, 0x0c, + 0xaf, 0x65, 0x0b, 0x72, 0x26, 0xc2, 0x61, 0x3b, 0x73, 0x2f, 0xb3, 0xbf, 0xa6, 0x51, 0x0a, 0xa1, + 0x01, 0x42, 0x2b, 0x38, 0xc0, 0x08, 0x0d, 0x04, 0x74, 0x84, 0xd0, 0x2a, 0x8e, 0x06, 0x42, 0x47, + 0x02, 0x3a, 0x41, 0x68, 0x6d, 0x3b, 0x73, 0x6f, 0x1d, 0xa1, 0x98, 0x62, 0x37, 0xa0, 0x68, 0x1a, + 0xa1, 0x85, 0x88, 0xba, 0xe8, 0x72, 0x04, 0x40, 0x1c, 0xae, 0x38, 0xc4, 0x6d, 0x88, 0x4e, 0x47, + 0x00, 0xa6, 0x42, 0x05, 0xc9, 0x22, 0xbc, 0x22, 0xf0, 0x32, 0x90, 0x7d, 0x08, 0x55, 0xd3, 0x1a, + 0xdb, 0x53, 0xc3, 0xe1, 0x7d, 0xda, 0xdc, 0xce, 0xdc, 0xab, 0x3c, 0xdc, 0x78, 0x40, 0x7b, 0x22, + 0xc6, 0xec, 0xaf, 0x69, 0x29, 0x32, 0xf6, 0x09, 0xd4, 0x44, 0xfa, 0xfd, 0x87, 0x34, 0xb0, 0x8c, + 0xf2, 0x29, 0xa9, 0x7c, 0xef, 0x3f, 0xfc, 0x64, 0x7f, 0x4d, 0x4b, 0x13, 0xb2, 0xbb, 0x50, 0x8d, + 0xb7, 0x08, 0x66, 0xbc, 0x22, 0x5a, 0x95, 0x82, 0x62, 0xb7, 0xbe, 0x08, 0x3c, 0x17, 0x09, 0xb6, + 0xc4, 0xb8, 0x45, 0x00, 0xb6, 0x0d, 0x60, 0x5a, 0x13, 0x63, 0xee, 0x84, 0x88, 0xbe, 0x2a, 0x06, + 0x50, 0x82, 0xb1, 0x5b, 0x50, 0x9e, 0xcf, 0xb0, 0x97, 0x4f, 0x0d, 0xa7, 0x71, 0x4d, 0x10, 0x24, + 0x20, 0x2c, 0x1d, 0xd7, 0x39, 0x62, 0x5f, 0x12, 0xb3, 0x1b, 0x01, 0x70, 0xaf, 0xd8, 0xc1, 0x8e, + 0xed, 0x36, 0x1a, 0xb4, 0x4e, 0x79, 0x82, 0xdd, 0x84, 0x6c, 0xe0, 0x8f, 0x1b, 0xd7, 0xa9, 0x97, + 0xc0, 0x7b, 0xd9, 0x3e, 0x9f, 0xf9, 0x1a, 0x82, 0x77, 0x8a, 0x90, 0xa7, 0x3d, 0xa3, 0xde, 0x84, + 0xd2, 0xa1, 0xe1, 0x1b, 0x53, 0xcd, 0x9a, 0x30, 0x05, 0xb2, 0x33, 0x2f, 0x10, 0xbb, 0x05, 0x3f, + 0xd5, 0x2e, 0x14, 0x9e, 0x1a, 0x3e, 0xe2, 0x18, 0xe4, 0x5c, 0x63, 0x6a, 0x11, 0xb2, 0xac, 0xd1, + 0x37, 0xee, 0x90, 0xe0, 0x22, 0x08, 0xad, 0xa9, 0x60, 0x05, 0x22, 0x85, 0xf0, 0x63, 0xc7, 0x1b, + 0x89, 0x9d, 0x50, 0xd2, 0x44, 0x4a, 0xfd, 0xff, 0x32, 0x50, 0x68, 0x79, 0x0e, 0x16, 0xf7, 0x12, + 0x14, 0x7d, 0xcb, 0xd1, 0x93, 0xea, 0x0a, 0xbe, 0xe5, 0x1c, 0x7a, 0x01, 0x22, 0xc6, 0x1e, 0x47, + 0xf0, 0xbd, 0x59, 0x18, 0x7b, 0x84, 0x88, 0x1a, 0x90, 0x95, 0x1a, 0x70, 0x1d, 0x4a, 0xe1, 0xc8, + 0xd1, 0x09, 0x9e, 0x23, 0x78, 0x31, 0x1c, 0x39, 0x3d, 0x44, 0xbd, 0x04, 0x45, 0x73, 0xc4, 0x31, + 0x79, 0xc2, 0x14, 0xcc, 0x11, 0x22, 0xd4, 0x4f, 0xa1, 0xac, 0x19, 0x67, 0xa2, 0x19, 0x57, 0xa1, + 0x80, 0x05, 0x08, 0x2e, 0x97, 0xd3, 0xf2, 0xe1, 0xc8, 0xe9, 0x98, 0x08, 0xc6, 0x46, 0xd8, 0x26, + 0xb5, 0x21, 0xa7, 0xe5, 0xc7, 0x9e, 0xd3, 0x31, 0xd5, 0x21, 0x40, 0xcb, 0xf3, 0xfd, 0xef, 0xdc, + 0x85, 0x2d, 0xc8, 0x9b, 0xd6, 0x2c, 0x3c, 0xe1, 0x0c, 0x42, 0xe3, 0x09, 0xf5, 0x3e, 0x94, 0x70, + 0x5e, 0xba, 0x76, 0x10, 0xb2, 0x5b, 0x90, 0x73, 0xec, 0x20, 0x6c, 0x64, 0xb6, 0xb3, 0x0b, 0xb3, + 0x46, 0x70, 0x75, 0x1b, 0x4a, 0x07, 0xc6, 0xf9, 0x53, 0x9c, 0x39, 0x2c, 0x8d, 0xa6, 0x50, 0x4c, + 0x89, 0x98, 0xcf, 0x2a, 0xc0, 0xd0, 0xf0, 0x8f, 0xad, 0x90, 0xf8, 0xd9, 0xdf, 0x66, 0xa0, 0x32, + 0x98, 0x8f, 0xbe, 0x9c, 0x5b, 0xfe, 0x05, 0xb6, 0xf9, 0x1e, 0x64, 0xc3, 0x8b, 0x19, 0xe5, 0xa8, + 0x3f, 0xbc, 0xc6, 0x8b, 0x97, 0xf0, 0x0f, 0x30, 0x93, 0x86, 0x24, 0xd8, 0x09, 0xd7, 0x33, 0xad, + 0x68, 0x0c, 0xf2, 0x5a, 0x01, 0x93, 0x1d, 0x13, 0x85, 0x82, 0x37, 0x13, 0xb3, 0xb0, 0xee, 0xcd, + 0xd8, 0x36, 0xe4, 0xc7, 0x27, 0xb6, 0x63, 0xd2, 0x04, 0xa4, 0xdb, 0xcc, 0x11, 0x38, 0x4b, 0xbe, + 0x77, 0xa6, 0x07, 0xf6, 0x57, 0x11, 0x93, 0x2f, 0xfa, 0xde, 0xd9, 0xc0, 0xfe, 0xca, 0x52, 0x87, + 0x42, 0xd2, 0x00, 0x14, 0x06, 0xad, 0x66, 0xb7, 0xa9, 0x29, 0x6b, 0xf8, 0xdd, 0xfe, 0xac, 0x33, + 0x18, 0x0e, 0x94, 0x0c, 0xab, 0x03, 0xf4, 0xfa, 0x43, 0x5d, 0xa4, 0xd7, 0x59, 0x01, 0xd6, 0x3b, + 0x3d, 0x25, 0x8b, 0x34, 0x08, 0xef, 0xf4, 0x94, 0x1c, 0x2b, 0x42, 0xb6, 0xd9, 0xfb, 0x5c, 0xc9, + 0xd3, 0x47, 0xb7, 0xab, 0x14, 0xd4, 0x3f, 0x5a, 0x87, 0x72, 0x7f, 0xf4, 0x85, 0x35, 0x0e, 0xb1, + 0xcf, 0xb8, 0x4a, 0x2d, 0xff, 0xb9, 0xe5, 0x53, 0xb7, 0xb3, 0x9a, 0x48, 0x61, 0x47, 0xcc, 0x11, + 0x75, 0x2e, 0xab, 0xad, 0x9b, 0x23, 0xa2, 0x1b, 0x9f, 0x58, 0x53, 0x83, 0x3a, 0x87, 0x74, 0x94, + 0xc2, 0x5d, 0xe1, 0x8d, 0xbe, 0xa0, 0xee, 0x65, 0x35, 0xfc, 0x64, 0xb7, 0xa1, 0xc2, 0xcb, 0x90, + 0xd7, 0x17, 0x70, 0xd0, 0xe2, 0xe2, 0x2b, 0xc8, 0x8b, 0x8f, 0x72, 0x52, 0xa9, 0x1c, 0x29, 0x24, + 0x18, 0x07, 0xf5, 0xc4, 0x8a, 0xf6, 0x46, 0x5f, 0x70, 0x6c, 0x89, 0xaf, 0x68, 0x6f, 0xf4, 0x05, + 0xa1, 0xde, 0x82, 0xcd, 0x60, 0x3e, 0x0a, 0xc6, 0xbe, 0x3d, 0x0b, 0x6d, 0xcf, 0xe5, 0x34, 0x65, + 0xa2, 0x51, 0x64, 0x04, 0x11, 0xdf, 0x83, 0xd2, 0x6c, 0x3e, 0xd2, 0x6d, 0x77, 0xe2, 0x11, 0x73, + 0xaf, 0x3c, 0xac, 0xf1, 0x89, 0x39, 0x9c, 0x8f, 0x3a, 0xee, 0xc4, 0xd3, 0x8a, 0x33, 0xfe, 0xa1, + 0xbe, 0x0e, 0x45, 0x01, 0x43, 0xe9, 0x1d, 0x5a, 0xae, 0xe1, 0x86, 0x7a, 0x2c, 0xf6, 0x4b, 0x1c, + 0xd0, 0x31, 0xd5, 0x3f, 0xc9, 0x80, 0x32, 0x90, 0xaa, 0x39, 0xb0, 0x42, 0x63, 0x25, 0x57, 0x78, + 0x05, 0xc0, 0x18, 0x8f, 0xbd, 0x39, 0x2f, 0x86, 0x2f, 0x9e, 0xb2, 0x80, 0x74, 0x4c, 0x79, 0x6c, + 0xb2, 0xa9, 0xb1, 0xb9, 0x03, 0xd5, 0x28, 0x9f, 0xb4, 0xa1, 0x2b, 0x02, 0x16, 0x8d, 0x4e, 0x30, + 0x4f, 0xed, 0xea, 0x62, 0x30, 0xe7, 0xb9, 0xaf, 0x41, 0x81, 0x74, 0x84, 0x20, 0x1a, 0x71, 0x9e, + 0x52, 0xff, 0x22, 0x03, 0xb5, 0x8e, 0x6b, 0x5a, 0xe7, 0x83, 0xb1, 0xe1, 0x52, 0x2f, 0x55, 0xa8, + 0xd9, 0x81, 0x6e, 0x23, 0x4c, 0x0f, 0xc6, 0x86, 0x2b, 0xc4, 0x7b, 0xc5, 0x0e, 0x62, 0x3a, 0xec, + 0x03, 0x27, 0xa0, 0xaa, 0xd6, 0xa9, 0xc4, 0x32, 0x41, 0xa8, 0xb2, 0xd7, 0x61, 0x63, 0x64, 0x39, + 0x9e, 0x7b, 0xac, 0x87, 0x9e, 0xce, 0xf5, 0x14, 0xde, 0x97, 0x1a, 0x07, 0x0f, 0xbd, 0x21, 0xe9, + 0x2b, 0x5b, 0x90, 0x9f, 0x19, 0x7e, 0x18, 0x34, 0x72, 0xdb, 0x59, 0xdc, 0xa2, 0x94, 0xc0, 0x61, + 0xb6, 0x03, 0x7d, 0xee, 0xda, 0x5f, 0xce, 0x79, 0x37, 0x4a, 0x5a, 0xc9, 0x0e, 0x8e, 0x28, 0xcd, + 0xee, 0x81, 0xc2, 0x6b, 0xa6, 0x62, 0xe5, 0x35, 0x54, 0x27, 0x38, 0x15, 0x4c, 0x8c, 0xec, 0xef, + 0xaf, 0x43, 0x69, 0x6f, 0xee, 0x8e, 0x71, 0x32, 0xd8, 0xab, 0x90, 0x9b, 0xcc, 0xdd, 0x31, 0xf5, + 0x25, 0x16, 0x86, 0xf1, 0x1e, 0xd0, 0x08, 0x89, 0xdc, 0xc5, 0xf0, 0x8f, 0x91, 0x2b, 0x2d, 0x71, + 0x17, 0x84, 0xab, 0xff, 0x2c, 0xc3, 0x4b, 0xdc, 0x73, 0x8c, 0x63, 0x56, 0x82, 0x5c, 0xaf, 0xdf, + 0x6b, 0x2b, 0x6b, 0xac, 0x0a, 0xa5, 0x4e, 0x6f, 0xd8, 0xd6, 0x7a, 0xcd, 0xae, 0x92, 0xa1, 0xad, + 0x3a, 0x6c, 0xee, 0x74, 0xdb, 0xca, 0x3a, 0x62, 0x9e, 0xf6, 0xbb, 0xcd, 0x61, 0xa7, 0xdb, 0x56, + 0x72, 0x1c, 0xa3, 0x75, 0x5a, 0x43, 0xa5, 0xc4, 0x14, 0xa8, 0x1e, 0x6a, 0xfd, 0xdd, 0xa3, 0x56, + 0x5b, 0xef, 0x1d, 0x75, 0xbb, 0x8a, 0xc2, 0xae, 0xc0, 0x46, 0x0c, 0xe9, 0x73, 0xe0, 0x36, 0x66, + 0x79, 0xda, 0xd4, 0x9a, 0xda, 0x63, 0xe5, 0x47, 0xac, 0x04, 0xd9, 0xe6, 0xe3, 0xc7, 0xca, 0xcf, + 0x70, 0xd7, 0x97, 0x9f, 0x75, 0x7a, 0xfa, 0xd3, 0x66, 0xf7, 0xa8, 0xad, 0xfc, 0x6c, 0x3d, 0x4a, + 0xf7, 0xb5, 0xdd, 0xb6, 0xa6, 0xfc, 0x2c, 0xc7, 0x36, 0xa1, 0xfa, 0xd3, 0x7e, 0xaf, 0x7d, 0xd0, + 0x3c, 0x3c, 0xa4, 0x86, 0xfc, 0xac, 0xa4, 0xfe, 0xf7, 0x1c, 0xe4, 0xb0, 0x27, 0x4c, 0x4d, 0x38, + 0x5c, 0xdc, 0x45, 0x64, 0x31, 0x3b, 0xb9, 0x3f, 0xfb, 0xcb, 0xdb, 0x6b, 0x9c, 0xb7, 0xdd, 0x81, + 0xac, 0x63, 0x87, 0x34, 0xad, 0xf1, 0xbe, 0x10, 0x5a, 0xdf, 0xfe, 0x9a, 0x86, 0x38, 0x76, 0x0b, + 0x32, 0x9c, 0xc9, 0x55, 0x1e, 0xd6, 0xc5, 0xc6, 0x11, 0x52, 0x72, 0x7f, 0x4d, 0xcb, 0xcc, 0xd8, + 0x4d, 0xc8, 0x3c, 0x17, 0x1c, 0xaf, 0xca, 0xf1, 0x5c, 0x4e, 0x22, 0xf6, 0x39, 0xdb, 0x86, 0xec, + 0xd8, 0xe3, 0x3a, 0x5d, 0x8c, 0xe7, 0x52, 0x03, 0xcb, 0x1f, 0x7b, 0x0e, 0x7b, 0x15, 0xb2, 0xbe, + 0x71, 0x46, 0x33, 0x1b, 0x4f, 0x57, 0x2c, 0x96, 0x90, 0xc8, 0x37, 0xce, 0xb0, 0x11, 0x13, 0xe2, + 0x11, 0x71, 0x23, 0xa2, 0xf9, 0xc6, 0x6a, 0x26, 0x6c, 0x1b, 0x32, 0x67, 0xc4, 0x25, 0x62, 0x35, + 0xe6, 0x99, 0xed, 0x9a, 0xde, 0xd9, 0x60, 0x66, 0x8d, 0x91, 0xe2, 0x8c, 0xbd, 0x06, 0xd9, 0x60, + 0x3e, 0x22, 0x2e, 0x51, 0x79, 0xb8, 0xb9, 0xc4, 0xef, 0xb1, 0xa2, 0x60, 0x3e, 0x62, 0xaf, 0x43, + 0x6e, 0xec, 0xf9, 0xbe, 0xe0, 0x14, 0x4a, 0xd4, 0xe0, 0x48, 0xd4, 0xa1, 0x5a, 0x87, 0x78, 0xac, + 0x30, 0x24, 0xad, 0x30, 0x26, 0x4a, 0x64, 0x0d, 0x56, 0x18, 0xb2, 0xbb, 0x42, 0x80, 0x55, 0xe5, + 0x56, 0x47, 0xe2, 0x0d, 0xcb, 0x41, 0x2c, 0x4e, 0xd2, 0xd4, 0x38, 0x27, 0x9d, 0x31, 0x26, 0x8a, + 0xe4, 0x1a, 0xb6, 0x69, 0x6a, 0x9c, 0xb3, 0xbb, 0x90, 0x7d, 0x6e, 0x8d, 0x49, 0x7d, 0x8c, 0x6b, + 0x13, 0x93, 0xf4, 0x94, 0xba, 0x87, 0x68, 0x5a, 0xf7, 0x9e, 0x63, 0x92, 0x26, 0x19, 0xcf, 0xe5, + 0x9e, 0xe7, 0x98, 0x4f, 0x69, 0x2e, 0x09, 0x89, 0xe2, 0xdc, 0x98, 0x9f, 0x23, 0x37, 0x52, 0xb8, + 0xe0, 0x35, 0xe6, 0xe7, 0x1d, 0x13, 0x19, 0xbb, 0x6b, 0x3e, 0x27, 0xfd, 0x31, 0xa3, 0xe1, 0x27, + 0x1a, 0x38, 0x81, 0xe5, 0x58, 0xe3, 0xd0, 0x7e, 0x6e, 0x87, 0x17, 0xa4, 0x21, 0x66, 0x34, 0x19, + 0xb4, 0x53, 0x80, 0x9c, 0x75, 0x3e, 0xf3, 0xd5, 0x7d, 0x28, 0x8a, 0x5a, 0x96, 0xac, 0xa4, 0xeb, + 0x50, 0xb2, 0x03, 0x7d, 0xec, 0xb9, 0x41, 0x28, 0xf4, 0xa2, 0xa2, 0x1d, 0xb4, 0x30, 0x89, 0xec, + 0xd2, 0x34, 0x42, 0x2e, 0x60, 0xaa, 0x1a, 0x7d, 0xab, 0x0f, 0x01, 0x92, 0x6e, 0x61, 0x9b, 0x1c, + 0xcb, 0x8d, 0x54, 0x30, 0xc7, 0x72, 0xe3, 0x3c, 0xeb, 0x52, 0x9e, 0xeb, 0x50, 0x8e, 0x75, 0x5b, + 0x56, 0x85, 0x8c, 0x21, 0x44, 0x5b, 0xc6, 0x50, 0xef, 0xa1, 0xaa, 0x19, 0x69, 0xaf, 0x69, 0x1c, + 0xa6, 0x22, 0x81, 0x97, 0x19, 0xa9, 0xdf, 0x87, 0xaa, 0x66, 0x05, 0x73, 0x27, 0x6c, 0x79, 0xce, + 0xae, 0x35, 0x61, 0x6f, 0x03, 0xc4, 0xe9, 0x40, 0x68, 0x20, 0xc9, 0xda, 0xdd, 0xb5, 0x26, 0x9a, + 0x84, 0x57, 0xff, 0x51, 0x8e, 0x74, 0xb9, 0x5d, 0xae, 0x44, 0x09, 0x6d, 0x29, 0x23, 0x69, 0x4b, + 0xb1, 0x6c, 0x58, 0x4f, 0x6b, 0x8c, 0x27, 0xb6, 0x69, 0x5a, 0x6e, 0xa4, 0x19, 0xf2, 0x14, 0x4e, + 0xb6, 0xe1, 0x1c, 0xd3, 0x86, 0xaa, 0x3f, 0x64, 0x51, 0xa5, 0xd3, 0x99, 0x6f, 0x05, 0x01, 0xd7, + 0x49, 0x0c, 0xe7, 0x38, 0xda, 0xdb, 0xf9, 0xaf, 0xdb, 0xdb, 0xd7, 0xa1, 0xe4, 0x7a, 0xa1, 0x4e, + 0x76, 0x5b, 0x81, 0x8f, 0xbe, 0x30, 0x50, 0xd9, 0x1b, 0x50, 0x14, 0x1a, 0xb7, 0xd8, 0x54, 0x62, + 0xb9, 0xec, 0x72, 0xa0, 0x16, 0x61, 0x59, 0x03, 0x15, 0xb8, 0xe9, 0xd4, 0x72, 0xc3, 0x48, 0x06, + 0x8b, 0x24, 0x7b, 0x0b, 0xca, 0x9e, 0xab, 0x73, 0xb5, 0x5c, 0xec, 0x2a, 0xb1, 0x7c, 0xfb, 0xee, + 0x11, 0x41, 0xb5, 0x92, 0x27, 0xbe, 0xb0, 0x29, 0x8e, 0x77, 0xa6, 0x8f, 0x0d, 0xdf, 0xa4, 0x9d, + 0x55, 0xd2, 0x8a, 0x8e, 0x77, 0xd6, 0x32, 0x7c, 0x93, 0xeb, 0x24, 0x5f, 0xba, 0xf3, 0x29, 0xed, + 0xa6, 0x9a, 0x26, 0x52, 0xec, 0x26, 0x94, 0xc7, 0xce, 0x3c, 0x08, 0x2d, 0x7f, 0xe7, 0x82, 0x1b, + 0x5a, 0x5a, 0x02, 0xc0, 0x76, 0xcd, 0x7c, 0x7b, 0x6a, 0xf8, 0x17, 0xb4, 0x75, 0x4a, 0x5a, 0x94, + 0x24, 0x41, 0x73, 0x6a, 0x9b, 0xe7, 0xdc, 0xda, 0xd2, 0x78, 0x02, 0xe9, 0x4f, 0xc8, 0x16, 0x0e, + 0x68, 0x7f, 0x94, 0xb4, 0x28, 0x49, 0xf3, 0x40, 0x9f, 0xb4, 0x23, 0xca, 0x9a, 0x48, 0xa5, 0x14, + 0xea, 0xcd, 0x4b, 0x15, 0x6a, 0xb6, 0xa8, 0xd3, 0x78, 0xbe, 0x7d, 0x6c, 0x0b, 0x8d, 0xe4, 0x0a, + 0xd7, 0x69, 0x38, 0x88, 0x04, 0xd5, 0x97, 0x50, 0x14, 0x43, 0x8c, 0x12, 0x08, 0xb7, 0x4f, 0x9a, + 0x3d, 0x73, 0x09, 0x84, 0x70, 0xf6, 0x2a, 0xd4, 0x44, 0x59, 0x41, 0xe8, 0xdb, 0xee, 0xb1, 0x58, + 0x3c, 0x55, 0x0e, 0x1c, 0x10, 0x0c, 0x15, 0x05, 0x9c, 0x5e, 0xdd, 0x18, 0xd9, 0x0e, 0x6e, 0xd3, + 0xac, 0xf0, 0x43, 0xcc, 0x1d, 0xa7, 0xc9, 0x41, 0x6a, 0x1f, 0x4a, 0xd1, 0x84, 0xfc, 0x4a, 0xea, + 0x54, 0x7f, 0x3b, 0x03, 0x15, 0x52, 0x0f, 0xfa, 0xa4, 0xfc, 0xb0, 0xb7, 0x81, 0x8d, 0x7d, 0xcb, + 0x08, 0x2d, 0xdd, 0x3a, 0x0f, 0x7d, 0x43, 0x28, 0x01, 0x5c, 0x93, 0x50, 0x38, 0xa6, 0x8d, 0x08, + 0xae, 0x07, 0xdc, 0x86, 0xca, 0xcc, 0xf0, 0x83, 0x48, 0x61, 0xe4, 0x15, 0x00, 0x07, 0x09, 0x75, + 0x4d, 0x71, 0x8f, 0x7d, 0x63, 0xaa, 0x87, 0xde, 0xa9, 0xe5, 0x72, 0x55, 0x99, 0x1b, 0x09, 0x75, + 0x82, 0x0f, 0x11, 0x4c, 0x1a, 0xf3, 0x7f, 0xca, 0x40, 0xed, 0x90, 0xcf, 0xfa, 0x13, 0xeb, 0x62, + 0x97, 0x5b, 0x66, 0xe3, 0x68, 0xc7, 0xe6, 0x34, 0xfa, 0x66, 0xb7, 0xa0, 0x32, 0x3b, 0xb5, 0x2e, + 0xf4, 0x94, 0x15, 0x53, 0x46, 0x50, 0x8b, 0xf6, 0xe6, 0x9b, 0x50, 0xf0, 0xa8, 0x23, 0x42, 0xc6, + 0x09, 0xd1, 0x20, 0xf5, 0x50, 0x13, 0x04, 0xa8, 0x2e, 0xc5, 0x45, 0xc9, 0x7a, 0x99, 0x28, 0x8c, + 0x9a, 0xbf, 0x05, 0x79, 0x44, 0x05, 0x8d, 0x3c, 0xd7, 0x73, 0x28, 0xc1, 0xde, 0x83, 0xda, 0xd8, + 0x9b, 0xce, 0xf4, 0x28, 0xbb, 0x90, 0x76, 0x69, 0x9e, 0x52, 0x41, 0x92, 0x43, 0x5e, 0x96, 0xfa, + 0x7b, 0x59, 0x28, 0x51, 0x1b, 0x04, 0x5b, 0xb1, 0xcd, 0xf3, 0x88, 0xad, 0x94, 0xb5, 0xbc, 0x6d, + 0x22, 0xd7, 0x7e, 0x81, 0x6a, 0x16, 0xab, 0x5c, 0x59, 0x59, 0xe5, 0xba, 0x06, 0x05, 0xa1, 0x6f, + 0xe5, 0x38, 0xdf, 0x99, 0x5f, 0xae, 0x6d, 0xe5, 0x57, 0x69, 0x5b, 0x38, 0x85, 0x9c, 0xc6, 0x3a, + 0x47, 0xf9, 0xc6, 0x59, 0x0b, 0x10, 0xa8, 0x8d, 0x10, 0x99, 0x69, 0x14, 0xd3, 0x4c, 0xa3, 0x01, + 0xc5, 0xe7, 0x76, 0x60, 0xe3, 0x02, 0x29, 0xf1, 0x6d, 0x28, 0x92, 0xd2, 0x34, 0x94, 0x5f, 0x34, + 0x0d, 0x71, 0xb7, 0x0d, 0xe7, 0x98, 0xab, 0xf4, 0x51, 0xb7, 0x9b, 0xce, 0xb1, 0xc7, 0xde, 0x87, + 0xab, 0x09, 0x5a, 0xf4, 0x86, 0x1c, 0x5c, 0xe4, 0xc3, 0xd1, 0x58, 0x4c, 0x49, 0x3d, 0x22, 0x9b, + 0xeb, 0x3e, 0x6c, 0x4a, 0x59, 0x66, 0xa8, 0xde, 0x04, 0xc4, 0x73, 0xca, 0xda, 0x46, 0x4c, 0x4e, + 0x5a, 0x4f, 0xa0, 0xfe, 0x9b, 0x75, 0xa8, 0xed, 0x79, 0xbe, 0x65, 0x1f, 0xbb, 0xc9, 0xaa, 0x5b, + 0xd2, 0xfc, 0xa3, 0x95, 0xb8, 0x2e, 0xad, 0xc4, 0xdb, 0x50, 0x99, 0xf0, 0x8c, 0x7a, 0x38, 0xe2, + 0x0e, 0x81, 0x9c, 0x06, 0x02, 0x34, 0x1c, 0x39, 0xb8, 0x9b, 0x23, 0x02, 0xca, 0x9c, 0xa3, 0xcc, + 0x51, 0x26, 0x94, 0x35, 0xec, 0x7b, 0xc4, 0x75, 0x4d, 0xcb, 0xb1, 0x42, 0x3e, 0x3d, 0xf5, 0x87, + 0xaf, 0x44, 0x92, 0x5e, 0x6a, 0xd3, 0x03, 0xcd, 0x9a, 0x34, 0x49, 0x3d, 0x42, 0x26, 0xbc, 0x4b, + 0xe4, 0x22, 0xaf, 0xe0, 0xd8, 0x85, 0x6f, 0x98, 0x97, 0x73, 0x0e, 0x75, 0x08, 0xe5, 0x18, 0x8c, + 0xba, 0xae, 0xd6, 0x16, 0xfa, 0xed, 0x1a, 0xab, 0x40, 0xb1, 0xd5, 0x1c, 0xb4, 0x9a, 0xbb, 0x6d, + 0x25, 0x83, 0xa8, 0x41, 0x7b, 0xc8, 0x75, 0xda, 0x75, 0xb6, 0x01, 0x15, 0x4c, 0xed, 0xb6, 0xf7, + 0x9a, 0x47, 0xdd, 0xa1, 0x92, 0x65, 0x35, 0x28, 0xf7, 0xfa, 0x7a, 0xb3, 0x35, 0xec, 0xf4, 0x7b, + 0x4a, 0x4e, 0xfd, 0x11, 0x94, 0x5a, 0x27, 0xd6, 0xf8, 0xf4, 0xb2, 0x51, 0x24, 0x83, 0xda, 0x1a, + 0x9f, 0x0a, 0xfd, 0x74, 0xc1, 0xa0, 0xb6, 0xc6, 0xa7, 0xea, 0x53, 0xa8, 0xb6, 0x22, 0xa1, 0x70, + 0x59, 0x29, 0x0f, 0xa1, 0x4e, 0x9b, 0x6f, 0x3c, 0x8a, 0x76, 0xdf, 0xfa, 0x8a, 0xdd, 0x57, 0x45, + 0x9a, 0xd6, 0x48, 0x6c, 0xbf, 0x0f, 0xa1, 0x72, 0xe8, 0x7b, 0x33, 0xcb, 0x0f, 0xa9, 0x58, 0x05, + 0xb2, 0xa7, 0xd6, 0x85, 0x28, 0x15, 0x3f, 0x13, 0x97, 0xc3, 0xba, 0xec, 0x72, 0x78, 0x08, 0xa5, + 0x28, 0xdb, 0x37, 0xce, 0xf3, 0x43, 0xe4, 0x62, 0x94, 0xc7, 0xb6, 0x02, 0xac, 0xec, 0x01, 0xc0, + 0x2c, 0x06, 0x08, 0xed, 0x23, 0xd2, 0xbc, 0x45, 0xe1, 0x9a, 0x44, 0xa1, 0xfe, 0x6d, 0x16, 0xea, + 0x87, 0x86, 0x1f, 0xda, 0x38, 0x39, 0x7c, 0x18, 0xde, 0x80, 0x1c, 0x2d, 0x79, 0xee, 0xdd, 0xb8, + 0x12, 0xab, 0xed, 0x9c, 0x86, 0xd4, 0x08, 0x22, 0x60, 0xdf, 0x83, 0xfa, 0x2c, 0x02, 0xeb, 0x24, + 0x1b, 0xf8, 0xd8, 0x2c, 0x66, 0xa1, 0x31, 0xaf, 0xcd, 0xe4, 0x24, 0xfb, 0x01, 0x6c, 0xa5, 0xf3, + 0x5a, 0x41, 0x90, 0xf0, 0x51, 0x79, 0xb2, 0xae, 0xa4, 0x32, 0x72, 0x32, 0xd6, 0x82, 0xcd, 0x24, + 0xfb, 0xd8, 0x73, 0xe6, 0x53, 0x37, 0x10, 0x76, 0xc4, 0xb5, 0x85, 0xda, 0x5b, 0x1c, 0xab, 0x29, + 0xb3, 0x05, 0x08, 0x53, 0xa1, 0x1a, 0xc3, 0x7a, 0xf3, 0x29, 0x6d, 0x89, 0x9c, 0x96, 0x82, 0xb1, + 0x47, 0x00, 0x71, 0x1a, 0x6d, 0xe2, 0xec, 0x8a, 0xfe, 0x75, 0x42, 0x6b, 0xaa, 0x49, 0x64, 0xa8, + 0x7e, 0x20, 0x33, 0xf0, 0xed, 0xf0, 0x64, 0x4a, 0x5c, 0x2c, 0xab, 0x25, 0x00, 0x62, 0x96, 0x81, + 0x8e, 0x06, 0x78, 0x9c, 0x45, 0x30, 0xb4, 0xba, 0x1d, 0x0c, 0xe6, 0xa3, 0xb8, 0x5c, 0x14, 0xa9, + 0x49, 0x2f, 0xa7, 0xc1, 0xb1, 0x70, 0x53, 0x24, 0x2d, 0x3c, 0x08, 0x8e, 0xd9, 0x43, 0xb8, 0x9a, + 0x10, 0x25, 0xfc, 0x37, 0x68, 0x00, 0x71, 0xee, 0x64, 0xf8, 0x62, 0x26, 0x1c, 0xa8, 0x3f, 0x86, + 0x5a, 0x6a, 0x76, 0x5e, 0x28, 0xdc, 0xaf, 0x43, 0x09, 0xff, 0xa3, 0x68, 0x17, 0x0b, 0xb0, 0x88, + 0xe9, 0x41, 0xe8, 0xab, 0x16, 0x28, 0x8b, 0x63, 0xcd, 0xee, 0x92, 0xeb, 0x8e, 0x26, 0x65, 0xd9, + 0x05, 0x17, 0xa1, 0xd8, 0x5b, 0xab, 0x26, 0x71, 0x9d, 0x5a, 0xbd, 0x34, 0x59, 0xea, 0x1f, 0xac, + 0x4b, 0x6d, 0xc6, 0x11, 0x67, 0xaf, 0xc9, 0xcb, 0x4f, 0xda, 0xb8, 0xc9, 0x98, 0x91, 0xc4, 0x79, + 0x13, 0x14, 0xcf, 0x37, 0x6d, 0xd7, 0x20, 0x57, 0x22, 0x1f, 0xee, 0x75, 0xd2, 0x16, 0x37, 0x04, + 0xfc, 0x50, 0x80, 0xd1, 0x6e, 0x31, 0xad, 0xd8, 0x33, 0x23, 0x7c, 0x11, 0x32, 0x48, 0x96, 0x4e, + 0xb9, 0xb4, 0x74, 0x7a, 0x03, 0xca, 0x8e, 0x15, 0x04, 0x7a, 0x78, 0x62, 0xb8, 0x24, 0xbf, 0xd3, + 0x9d, 0x2e, 0x21, 0x72, 0x78, 0x62, 0xb8, 0x48, 0x68, 0xbb, 0xba, 0x38, 0x7b, 0x29, 0x2c, 0x13, + 0xda, 0x2e, 0xd9, 0x6f, 0x28, 0xf7, 0xb7, 0x56, 0x4d, 0xac, 0x10, 0x8b, 0x6c, 0x79, 0x5e, 0xd5, + 0x57, 0xa0, 0xf8, 0xd4, 0xb6, 0xce, 0x04, 0x2f, 0x7b, 0x6e, 0x5b, 0x67, 0x11, 0x2f, 0xc3, 0x6f, + 0xf5, 0xaf, 0x4b, 0x50, 0x22, 0xe2, 0xdd, 0xcb, 0x5d, 0xb6, 0xdf, 0xc6, 0xda, 0xd8, 0x16, 0x72, + 0x2a, 0xb7, 0xc2, 0xc6, 0xe1, 0x52, 0xeb, 0x15, 0x00, 0x49, 0x86, 0x72, 0x8d, 0xa0, 0x1c, 0xc6, + 0xa2, 0x13, 0xd5, 0x74, 0xd2, 0xf1, 0x82, 0x2f, 0x1d, 0xe1, 0x9d, 0x49, 0x00, 0xec, 0x01, 0x57, + 0xa2, 0xc9, 0x1f, 0x53, 0x94, 0x19, 0x0b, 0xf5, 0x21, 0x32, 0xe1, 0x49, 0xb3, 0xc6, 0x04, 0xe9, + 0x07, 0x96, 0x1f, 0x44, 0xdb, 0xa9, 0xa6, 0x45, 0x49, 0xe4, 0x68, 0xa8, 0x3c, 0x09, 0x93, 0x3b, + 0xda, 0xbe, 0xb2, 0xf6, 0xa7, 0x11, 0x01, 0xbb, 0x07, 0x45, 0x12, 0xd9, 0x16, 0x4a, 0x70, 0x89, + 0x75, 0x46, 0xca, 0x94, 0x16, 0xa1, 0xd9, 0x9b, 0x90, 0x9f, 0x9c, 0x5a, 0x17, 0x41, 0xa3, 0x26, + 0xb3, 0x84, 0x94, 0x2c, 0xd4, 0x38, 0x05, 0xbb, 0x0b, 0x75, 0xdf, 0x9a, 0xe8, 0xe4, 0xc4, 0x45, + 0xe1, 0x1d, 0x34, 0xea, 0x24, 0x9b, 0xab, 0xbe, 0x35, 0x69, 0x21, 0x70, 0x38, 0x72, 0x02, 0xf6, + 0x3a, 0x14, 0x48, 0x2a, 0xa1, 0x8d, 0x21, 0xd5, 0x1c, 0x89, 0x38, 0x4d, 0x60, 0xd9, 0x43, 0x28, + 0x27, 0x6c, 0xe3, 0x2a, 0x75, 0x68, 0x6b, 0x81, 0x1f, 0x11, 0x1b, 0xd7, 0x12, 0x32, 0xf6, 0x3e, + 0x80, 0xb0, 0x7e, 0xf4, 0xd1, 0x05, 0x1d, 0x8b, 0x54, 0x62, 0xeb, 0x50, 0x12, 0x80, 0xb2, 0x8d, + 0xf4, 0x06, 0xe4, 0x51, 0x4a, 0x04, 0x8d, 0x97, 0xa8, 0x35, 0x9b, 0x69, 0x11, 0x42, 0xbd, 0x23, + 0x3c, 0xbb, 0x07, 0x25, 0x5c, 0x5c, 0x3a, 0x4e, 0x61, 0x43, 0x36, 0x07, 0xc5, 0x4a, 0x44, 0x2d, + 0xcd, 0x3a, 0x1b, 0x7c, 0xe9, 0xb0, 0xfb, 0x90, 0x33, 0xad, 0x49, 0xd0, 0xb8, 0x4e, 0x25, 0x5e, + 0x93, 0xe6, 0x12, 0x15, 0x87, 0x5d, 0x6b, 0xc2, 0x45, 0x0b, 0xd2, 0xb0, 0x7d, 0xa8, 0xe3, 0xd2, + 0x7b, 0x48, 0x8a, 0x37, 0x0e, 0x79, 0xe3, 0x06, 0xe5, 0xba, 0xb3, 0x90, 0xab, 0x27, 0x88, 0x68, + 0x82, 0xda, 0x6e, 0xe8, 0x5f, 0x68, 0x35, 0x57, 0x86, 0xb1, 0x1b, 0x50, 0xb2, 0x83, 0xae, 0x37, + 0x3e, 0xb5, 0xcc, 0xc6, 0xcb, 0x91, 0x93, 0x90, 0xa7, 0xd9, 0xa7, 0x50, 0xa3, 0xc5, 0x88, 0x49, + 0xac, 0xbc, 0x71, 0x53, 0x16, 0x79, 0x43, 0x19, 0xa5, 0xa5, 0x29, 0x51, 0xdd, 0xb2, 0x03, 0x3d, + 0xb4, 0xa6, 0x33, 0xcf, 0x47, 0x43, 0xf2, 0x95, 0xc8, 0xf9, 0x39, 0x8c, 0x40, 0xc8, 0xe7, 0xe3, + 0x43, 0x5c, 0xdd, 0x9b, 0x4c, 0x02, 0x2b, 0x6c, 0xdc, 0xa2, 0xbd, 0x56, 0x8f, 0xce, 0x72, 0xfb, + 0x04, 0x25, 0xa5, 0x34, 0xd0, 0xcd, 0x0b, 0xd7, 0x98, 0xda, 0xe3, 0xc6, 0x6d, 0x6e, 0xaf, 0xda, + 0xc1, 0x2e, 0x07, 0xc8, 0x26, 0xe3, 0x76, 0xca, 0x64, 0xbc, 0x02, 0x79, 0x73, 0x84, 0x5b, 0xf8, + 0x0e, 0x15, 0x9b, 0x33, 0x47, 0x1d, 0xf3, 0xc6, 0x63, 0x32, 0x13, 0xa9, 0x91, 0x1f, 0x2e, 0x28, + 0x03, 0xa9, 0xd5, 0x2f, 0x69, 0x0d, 0xfb, 0x6b, 0xb2, 0x4e, 0xb0, 0x93, 0x87, 0xac, 0x69, 0x4d, + 0x6e, 0xfc, 0x08, 0xd8, 0xf2, 0xf0, 0xbe, 0x48, 0x33, 0xc9, 0x0b, 0xcd, 0xe4, 0x7b, 0xeb, 0x9f, + 0x64, 0xd4, 0x4f, 0xa1, 0x96, 0xda, 0xab, 0x2b, 0x35, 0x2c, 0x6e, 0x69, 0x18, 0x53, 0xe1, 0x99, + 0xe1, 0x09, 0xf5, 0xdf, 0x65, 0xa1, 0xba, 0x6f, 0x04, 0x27, 0x07, 0xc6, 0x6c, 0x10, 0x1a, 0x61, + 0x80, 0x03, 0x7e, 0x62, 0x04, 0x27, 0x53, 0x63, 0xc6, 0xcd, 0xba, 0x0c, 0x77, 0x2a, 0x09, 0x18, + 0xda, 0x74, 0x38, 0xd5, 0x98, 0xec, 0xbb, 0x87, 0x4f, 0x84, 0xc7, 0x28, 0x4e, 0x23, 0x73, 0x08, + 0x4e, 0xe6, 0x93, 0x89, 0x70, 0x31, 0x97, 0xb4, 0x28, 0xc9, 0xee, 0x42, 0x4d, 0x7c, 0x92, 0x4d, + 0x77, 0x2e, 0x8e, 0xd5, 0xd3, 0x40, 0xf6, 0x08, 0x2a, 0x02, 0x30, 0x8c, 0x58, 0x59, 0x3d, 0xf6, + 0x04, 0x26, 0x08, 0x4d, 0xa6, 0x62, 0x3f, 0x81, 0xab, 0x52, 0x72, 0xcf, 0xf3, 0x0f, 0xe6, 0x4e, + 0x68, 0xb7, 0x7a, 0x42, 0x81, 0x7e, 0x79, 0x29, 0x7b, 0x42, 0xa2, 0xad, 0xce, 0x99, 0x6e, 0xed, + 0x81, 0xed, 0x0a, 0xf5, 0x22, 0x0d, 0x5c, 0xa0, 0x32, 0xce, 0x89, 0x21, 0xa6, 0xa9, 0x8c, 0x73, + 0x5c, 0xfe, 0x02, 0x70, 0x60, 0x85, 0x27, 0x9e, 0x49, 0xea, 0x45, 0xbc, 0xfc, 0x07, 0x32, 0x4a, + 0x4b, 0x53, 0xe2, 0x70, 0xba, 0x73, 0xc7, 0x19, 0xbb, 0x21, 0xd9, 0x50, 0x59, 0x2d, 0x4a, 0xa2, + 0xb0, 0xf0, 0x0d, 0xf7, 0xd8, 0x0a, 0x1a, 0x95, 0xed, 0xec, 0xbd, 0x8c, 0x26, 0x52, 0xea, 0xef, + 0xae, 0x43, 0x9e, 0xcf, 0xe4, 0xcb, 0x50, 0x1e, 0x39, 0xde, 0xf8, 0x54, 0x77, 0xe7, 0xd3, 0xe8, + 0x78, 0x84, 0x00, 0xa8, 0x6f, 0x91, 0xed, 0x23, 0x3c, 0x7e, 0x19, 0x8d, 0xbe, 0xb1, 0x48, 0x6f, + 0x1e, 0x62, 0x5d, 0x59, 0x82, 0x8a, 0x14, 0x36, 0xc2, 0xf7, 0xce, 0x68, 0x35, 0xe4, 0x08, 0x11, + 0x25, 0xe9, 0x04, 0x86, 0xe4, 0x0e, 0x66, 0xca, 0x13, 0xae, 0x44, 0x80, 0x96, 0x1b, 0x2e, 0x7a, + 0x27, 0x0b, 0x4b, 0xde, 0x49, 0x76, 0x0b, 0xd0, 0xb2, 0x1a, 0x5b, 0x7d, 0xd7, 0x6a, 0xf5, 0x68, + 0x84, 0x4b, 0x9a, 0x04, 0x61, 0x1f, 0xc5, 0x6b, 0x91, 0x7a, 0x24, 0x7c, 0xc7, 0x82, 0xa3, 0xca, + 0xab, 0x56, 0x4b, 0xd1, 0xe1, 0xde, 0x41, 0x36, 0xc9, 0xb5, 0x38, 0xfc, 0x54, 0xdb, 0x00, 0x9a, + 0x77, 0x16, 0x58, 0x21, 0x69, 0x61, 0x2f, 0x51, 0x87, 0x52, 0x47, 0xa1, 0xde, 0xd9, 0xa1, 0x17, + 0xc4, 0xea, 0xd9, 0xfa, 0x6a, 0xf5, 0x4c, 0x7d, 0x17, 0x8a, 0x28, 0x77, 0x8d, 0xd0, 0x60, 0x77, + 0x85, 0x9f, 0x93, 0xeb, 0x5d, 0xc2, 0xe1, 0x9b, 0xd4, 0x21, 0x3c, 0x9f, 0xdd, 0xa8, 0x5e, 0xca, + 0x73, 0x47, 0x72, 0x7d, 0xc4, 0xfc, 0x5b, 0x14, 0x28, 0x24, 0xf9, 0xcb, 0x50, 0xc6, 0xa6, 0xd1, + 0x19, 0x92, 0xd8, 0xe8, 0x25, 0xdf, 0x3b, 0x6b, 0x61, 0x5a, 0xfd, 0xcf, 0x19, 0xa8, 0xf4, 0x7d, + 0x13, 0x05, 0xc7, 0x60, 0x66, 0x8d, 0x5f, 0xa8, 0x4d, 0xa2, 0xdc, 0xf7, 0x1c, 0xc7, 0x88, 0x75, + 0x31, 0x94, 0xfb, 0x11, 0x80, 0xbd, 0x0f, 0xb9, 0x89, 0x63, 0x1c, 0xd3, 0x64, 0xc7, 0x56, 0xa6, + 0x54, 0x7c, 0xf4, 0xbd, 0xe7, 0x18, 0xc7, 0x1a, 0x91, 0xaa, 0xbf, 0x11, 0xd7, 0x4f, 0x67, 0x2e, + 0xf2, 0x49, 0xcb, 0x1a, 0x9d, 0x67, 0x0e, 0x5a, 0x4a, 0x86, 0x95, 0x20, 0xb7, 0xdb, 0x1e, 0xb4, + 0xb8, 0x6d, 0x89, 0x56, 0xe6, 0x40, 0xdf, 0xeb, 0x68, 0x83, 0xa1, 0x92, 0xa3, 0x03, 0x52, 0x02, + 0x74, 0x9b, 0x83, 0xa1, 0x52, 0x62, 0x00, 0x85, 0xa3, 0x5e, 0xe7, 0x27, 0x47, 0x6d, 0x45, 0x51, + 0xff, 0x43, 0x06, 0x20, 0x39, 0x10, 0x60, 0x6f, 0x41, 0xe5, 0x8c, 0x52, 0xba, 0x74, 0x52, 0x24, + 0xf7, 0x11, 0x38, 0x9a, 0x74, 0x92, 0x77, 0x24, 0x13, 0x03, 0x65, 0xef, 0xf2, 0x91, 0x51, 0x65, + 0x96, 0x88, 0x6d, 0xf6, 0x36, 0x94, 0x3c, 0xec, 0x07, 0x92, 0x66, 0x65, 0xc1, 0x2b, 0x75, 0x5f, + 0x2b, 0x7a, 0x3c, 0x81, 0x32, 0x7a, 0xe2, 0x47, 0xae, 0xa4, 0x98, 0x74, 0x0f, 0x41, 0x2d, 0xc7, + 0x98, 0x07, 0x96, 0xc6, 0xf1, 0x31, 0xdb, 0xcd, 0x27, 0x6c, 0x57, 0xfd, 0x29, 0xd4, 0x07, 0xc6, + 0x74, 0xc6, 0x99, 0x33, 0x75, 0x8c, 0x41, 0x0e, 0xd7, 0x84, 0x58, 0x7a, 0xf4, 0x8d, 0x5b, 0xec, + 0xd0, 0xf2, 0xc7, 0x96, 0x1b, 0xed, 0xc8, 0x28, 0x89, 0xcc, 0xf6, 0x28, 0xb0, 0xdd, 0x63, 0xcd, + 0x3b, 0x8b, 0x22, 0x94, 0xa2, 0xb4, 0xfa, 0x8f, 0x33, 0x50, 0x91, 0x9a, 0xc1, 0xde, 0x4d, 0x59, + 0x94, 0x2f, 0x2f, 0xb5, 0x93, 0x7f, 0x4b, 0x96, 0xe5, 0xeb, 0x90, 0x0f, 0x42, 0xc3, 0x8f, 0xce, + 0x96, 0x14, 0x29, 0xc7, 0x8e, 0x37, 0x77, 0x4d, 0x8d, 0xa3, 0x99, 0x0a, 0x59, 0xcb, 0x35, 0x85, + 0xd1, 0xb8, 0x4c, 0x85, 0x48, 0x75, 0x1b, 0xca, 0x71, 0xf1, 0xb8, 0x04, 0xb4, 0xfe, 0xb3, 0x81, + 0xb2, 0xc6, 0xca, 0x90, 0xd7, 0x9a, 0xbd, 0xc7, 0x6d, 0x25, 0xa3, 0xfe, 0x49, 0x06, 0x20, 0xc9, + 0xc5, 0x1e, 0xa4, 0x5a, 0x7b, 0x63, 0xb1, 0xd4, 0x07, 0xf4, 0x57, 0x6a, 0xec, 0x4d, 0x28, 0xcf, + 0x5d, 0x02, 0x5a, 0xa6, 0x90, 0x3b, 0x09, 0x80, 0xdd, 0x84, 0x6c, 0x14, 0xcb, 0xb4, 0x10, 0x3f, + 0xf2, 0xdc, 0x70, 0xd4, 0xef, 0x41, 0x39, 0x2e, 0x8e, 0xd5, 0xa0, 0xbc, 0xd7, 0xef, 0x76, 0xfb, + 0xcf, 0x3a, 0xbd, 0xc7, 0xca, 0x1a, 0x26, 0x0f, 0xb5, 0x76, 0xab, 0xbd, 0x8b, 0xc9, 0x0c, 0xae, + 0xd9, 0xd6, 0x91, 0xa6, 0xb5, 0x7b, 0x43, 0x5d, 0xeb, 0x3f, 0x53, 0xd6, 0xd5, 0xdf, 0xca, 0xc1, + 0x66, 0xdf, 0xdd, 0x9d, 0xcf, 0x1c, 0x7b, 0x6c, 0x84, 0xd6, 0x13, 0xeb, 0xa2, 0x15, 0x9e, 0xa3, + 0x38, 0x35, 0xc2, 0xd0, 0xe7, 0x9b, 0xb9, 0xac, 0xf1, 0x04, 0x77, 0xd0, 0x05, 0x96, 0x1f, 0x92, + 0xff, 0x51, 0xde, 0xc5, 0x75, 0x0e, 0x6f, 0x79, 0x0e, 0xed, 0x65, 0xf6, 0x03, 0xb8, 0xca, 0x9d, + 0x7a, 0x9c, 0x12, 0x95, 0x4e, 0x6e, 0xdb, 0x67, 0x97, 0x96, 0x2e, 0xe3, 0x84, 0x98, 0x15, 0xc9, + 0x88, 0x85, 0xdd, 0x86, 0x4a, 0x92, 0x3d, 0x3a, 0xb0, 0x85, 0x98, 0x90, 0x5a, 0xe2, 0xb9, 0xba, + 0x19, 0xb5, 0x5a, 0xb7, 0xcd, 0x73, 0x32, 0x97, 0xf2, 0x5a, 0xdd, 0x4b, 0x3a, 0x83, 0x22, 0xf7, + 0x33, 0xd8, 0x4c, 0x51, 0x52, 0x2b, 0xb8, 0xc1, 0xf4, 0x76, 0x74, 0x58, 0xb0, 0xd0, 0x7b, 0x19, + 0x82, 0xcd, 0xe1, 0x1a, 0xe1, 0x86, 0x97, 0x86, 0x8a, 0x93, 0x63, 0xfb, 0xd8, 0xf5, 0x7c, 0x4b, + 0xb0, 0xf7, 0x92, 0x1d, 0x74, 0x28, 0x9d, 0xd8, 0x2c, 0x52, 0xf0, 0x00, 0x97, 0x26, 0xd1, 0xd9, + 0x39, 0x47, 0xdb, 0x5c, 0x5e, 0xe6, 0xb4, 0x22, 0xa5, 0x3b, 0x26, 0x9a, 0xeb, 0x1c, 0x15, 0x99, + 0x21, 0x40, 0x66, 0x48, 0x95, 0x80, 0x4f, 0x39, 0xec, 0x46, 0x0f, 0xb6, 0x56, 0x35, 0x72, 0x85, + 0x5e, 0xb5, 0x2d, 0xeb, 0x55, 0x0b, 0x0e, 0xac, 0x44, 0xc7, 0xfa, 0x27, 0x19, 0xa8, 0xee, 0x5a, + 0xe6, 0x7c, 0xf6, 0x63, 0xcf, 0x76, 0x71, 0x01, 0x7c, 0x00, 0x55, 0xcf, 0x31, 0x69, 0xf6, 0xa4, + 0x18, 0x98, 0xd4, 0xe9, 0xa9, 0x38, 0xe8, 0x01, 0xcf, 0x31, 0x5b, 0x9e, 0x43, 0x11, 0x33, 0xef, + 0xc0, 0x15, 0xee, 0xdc, 0x13, 0xbe, 0xee, 0x73, 0x9e, 0x79, 0x9d, 0x66, 0x46, 0xe1, 0x28, 0xae, + 0x0a, 0x11, 0xf9, 0xaf, 0xc1, 0x96, 0x44, 0x4e, 0xae, 0x01, 0xa2, 0x5f, 0x5e, 0x24, 0x9b, 0x71, + 0xde, 0xe8, 0xf8, 0x52, 0xfd, 0x7b, 0x59, 0x28, 0x73, 0xd7, 0x20, 0xb6, 0xf7, 0x1e, 0x14, 0xbd, + 0xd1, 0x17, 0xba, 0x6f, 0x4d, 0x2e, 0x3b, 0x75, 0x2f, 0x78, 0xa3, 0x2f, 0x34, 0x6b, 0xc2, 0xde, + 0x8a, 0xa4, 0xba, 0x69, 0x4d, 0xc4, 0xa0, 0xd4, 0xd3, 0xf6, 0x80, 0x90, 0xf2, 0xdc, 0x11, 0x76, + 0x65, 0xd1, 0x7a, 0xb6, 0x4d, 0xee, 0xce, 0xce, 0x69, 0x9b, 0x69, 0xe3, 0xb9, 0x63, 0x06, 0x97, + 0xbb, 0x51, 0x72, 0x97, 0xba, 0x51, 0xd8, 0x7d, 0xd8, 0xc4, 0xa1, 0x4e, 0xf2, 0xf1, 0xc5, 0x8c, + 0xdb, 0x6a, 0xc3, 0x73, 0xcc, 0xc4, 0x5d, 0x61, 0x9e, 0x23, 0xad, 0x6b, 0x9d, 0x2d, 0xd0, 0x16, + 0x38, 0xad, 0x6b, 0x9d, 0xa5, 0x68, 0x1f, 0x41, 0x25, 0xd9, 0xad, 0x41, 0xa3, 0x78, 0xf9, 0x0c, + 0xc6, 0x9b, 0x37, 0xc0, 0x4c, 0xdc, 0xb5, 0xcb, 0x33, 0x95, 0x2e, 0xcf, 0xc4, 0xc9, 0xe8, 0xf8, + 0xf1, 0x9f, 0xaf, 0x43, 0xb9, 0xc3, 0xcb, 0x08, 0xcf, 0xd9, 0x1d, 0xc8, 0x7e, 0xcd, 0x34, 0x20, + 0x0e, 0xbb, 0x61, 0x98, 0xa6, 0x6e, 0x4c, 0x26, 0xd6, 0x38, 0xb4, 0x4c, 0x1d, 0x35, 0x2e, 0xc1, + 0xf4, 0x36, 0x0c, 0xd3, 0x6c, 0x0a, 0x38, 0x09, 0x0f, 0xee, 0xe8, 0x8a, 0x2c, 0xcf, 0x24, 0xbe, + 0x83, 0x1c, 0x5d, 0xc2, 0xf0, 0xe4, 0x07, 0x3b, 0xa9, 0x99, 0xcd, 0x7d, 0xb7, 0x99, 0xcd, 0x7f, + 0xeb, 0x99, 0x2d, 0x5c, 0x3e, 0xb3, 0x29, 0xcf, 0x1b, 0xce, 0x54, 0x91, 0x66, 0x2a, 0x11, 0xe6, + 0x1d, 0xf3, 0x5c, 0xfd, 0x87, 0x59, 0x00, 0xcd, 0x9a, 0x39, 0xc6, 0xd8, 0xfa, 0xbf, 0x67, 0xf4, + 0x6e, 0x4b, 0xcb, 0xc4, 0x35, 0xa3, 0xa0, 0xab, 0x68, 0x49, 0x90, 0xf8, 0x5b, 0x39, 0xbc, 0x85, + 0x6f, 0x3d, 0xbc, 0xc5, 0x6f, 0x31, 0xbc, 0xa5, 0xe5, 0xe1, 0x65, 0x3f, 0x82, 0x57, 0x7c, 0xeb, + 0xcc, 0xb7, 0x43, 0x4b, 0x9f, 0xf8, 0xde, 0x54, 0x4f, 0x09, 0x03, 0xe4, 0x95, 0x65, 0x1a, 0x8d, + 0xeb, 0x82, 0x68, 0xcf, 0xf7, 0xa6, 0x69, 0x81, 0xa0, 0xfe, 0x75, 0x09, 0x2a, 0x4d, 0xd7, 0x70, + 0x2e, 0xbe, 0xb2, 0x28, 0x64, 0x89, 0x0e, 0x7f, 0x66, 0xf3, 0x90, 0x8f, 0x3b, 0x3f, 0xcf, 0x2f, + 0x13, 0x84, 0x46, 0xfc, 0x36, 0x54, 0xbc, 0x79, 0x18, 0xe3, 0xf9, 0x09, 0x3f, 0x70, 0x10, 0x11, + 0xc4, 0xf9, 0xe3, 0x83, 0xc5, 0x28, 0x3f, 0xd9, 0x9f, 0x49, 0xfe, 0xd8, 0x26, 0x89, 0xf3, 0x13, + 0x01, 0x0a, 0x08, 0x7b, 0x4a, 0x23, 0x1f, 0xcc, 0xa7, 0x16, 0x1f, 0xfd, 0x2c, 0x0f, 0x80, 0x6d, + 0x09, 0x18, 0x96, 0x32, 0xb5, 0xa6, 0x9e, 0x7f, 0xc1, 0x4b, 0x29, 0xf0, 0x52, 0x38, 0x88, 0x4a, + 0x79, 0x1b, 0xd8, 0x99, 0x61, 0x87, 0x7a, 0xba, 0x28, 0x6e, 0x07, 0x2a, 0x88, 0x19, 0xca, 0xc5, + 0x5d, 0x83, 0x82, 0x69, 0x07, 0xa7, 0x9d, 0xbe, 0xb0, 0x01, 0x45, 0x0a, 0xfb, 0x12, 0x8c, 0x0d, + 0x54, 0x4a, 0x43, 0x2b, 0xa0, 0xa1, 0xcc, 0x6a, 0x65, 0x84, 0xec, 0x20, 0x00, 0x95, 0x1a, 0xd7, + 0x0a, 0xcf, 0x3c, 0x1f, 0x73, 0x72, 0x13, 0x2f, 0x01, 0xa0, 0xf2, 0x87, 0xa4, 0x58, 0x11, 0x39, + 0xd5, 0xb2, 0x5a, 0x9c, 0x46, 0xe3, 0x89, 0x73, 0x25, 0xc2, 0x56, 0x79, 0xf3, 0x13, 0x08, 0xbb, + 0x0b, 0x75, 0x6a, 0x3e, 0x99, 0x80, 0xd8, 0x07, 0x3a, 0x84, 0xcf, 0x6a, 0x55, 0x84, 0x92, 0x7f, + 0x05, 0xa9, 0x3e, 0x85, 0xeb, 0xa9, 0xfe, 0xe9, 0x86, 0xef, 0x1b, 0x17, 0xfa, 0xd4, 0xf8, 0xc2, + 0xf3, 0xc9, 0x7f, 0x96, 0xd5, 0xae, 0xc9, 0xc3, 0xd6, 0x44, 0xf4, 0x01, 0x62, 0x2f, 0xcd, 0x6a, + 0xbb, 0x9e, 0x4f, 0xce, 0xb5, 0x95, 0x59, 0x11, 0x4b, 0x5e, 0x1d, 0x9a, 0x60, 0xb2, 0x47, 0x03, + 0x1e, 0x38, 0xad, 0x55, 0x08, 0xb6, 0x43, 0x20, 0xb4, 0xd1, 0x82, 0x47, 0x5c, 0xd8, 0x6d, 0x8a, + 0xf8, 0xc6, 0x47, 0x24, 0x12, 0x39, 0xe2, 0xc4, 0x32, 0x4c, 0x3a, 0xd8, 0x27, 0xc4, 0xbe, 0x65, + 0x50, 0xd8, 0x4c, 0xf0, 0x48, 0x9f, 0xcd, 0x43, 0x1e, 0xf1, 0xac, 0xe5, 0x83, 0x47, 0x87, 0xf3, + 0x50, 0x80, 0x8f, 0xad, 0x90, 0xe2, 0x9c, 0x09, 0xfc, 0xd8, 0x0a, 0x51, 0x37, 0x09, 0x1e, 0x45, + 0x87, 0x74, 0x57, 0xc5, 0xd8, 0x3e, 0x12, 0xa7, 0x70, 0x2a, 0xd4, 0x62, 0xa4, 0x3e, 0x9d, 0xf3, + 0x10, 0xe7, 0xac, 0x56, 0x89, 0x08, 0x0e, 0xe6, 0x0e, 0x4e, 0xec, 0xd8, 0x18, 0x9f, 0x58, 0xba, + 0x8f, 0x4d, 0x79, 0x89, 0x4f, 0x1d, 0x41, 0x34, 0x6c, 0xcd, 0xcb, 0xc0, 0x13, 0xfa, 0x89, 0x1d, + 0x92, 0xc3, 0x2e, 0xab, 0x95, 0x08, 0xb0, 0x6f, 0x87, 0xc8, 0x9f, 0x38, 0x52, 0xac, 0x40, 0x2a, + 0xe2, 0x3a, 0x11, 0x6d, 0x10, 0xe2, 0x80, 0xe0, 0x54, 0xd0, 0x3d, 0x50, 0x52, 0xb4, 0x58, 0xde, + 0x0d, 0x22, 0xad, 0x4b, 0xa4, 0x58, 0xea, 0xeb, 0xc0, 0x33, 0xeb, 0xb8, 0xf4, 0x78, 0x99, 0x2f, + 0x73, 0x7f, 0x04, 0x81, 0x77, 0xed, 0xe0, 0x94, 0x4a, 0xbc, 0x0b, 0x75, 0x89, 0x0e, 0xcb, 0xbb, + 0xc9, 0x57, 0x46, 0x4c, 0x96, 0x6a, 0xa3, 0x6f, 0x4d, 0xbd, 0x50, 0x74, 0xf3, 0x15, 0xa9, 0x8d, + 0x1a, 0xc1, 0xd3, 0x6d, 0x14, 0xb4, 0x58, 0xe6, 0x2d, 0xa9, 0x8d, 0x9c, 0x14, 0x4b, 0xbd, 0x03, + 0x55, 0xe4, 0x22, 0xa1, 0xe5, 0xf2, 0xcd, 0x7f, 0x9b, 0x0f, 0xac, 0x80, 0xd1, 0xee, 0xbf, 0x03, + 0x55, 0x3e, 0xf2, 0x82, 0x6f, 0x6f, 0x73, 0x12, 0x01, 0x43, 0x12, 0xd5, 0x97, 0x0e, 0xd3, 0x0e, + 0xfd, 0xb9, 0x6b, 0x71, 0xf7, 0x23, 0x7d, 0x9a, 0x22, 0xac, 0x21, 0x4e, 0xb3, 0x5d, 0xb8, 0xc2, + 0xbd, 0x0e, 0x96, 0xa4, 0x43, 0x44, 0x61, 0x85, 0x2b, 0x0f, 0x99, 0x58, 0x44, 0x1f, 0x83, 0x03, + 0xf5, 0x67, 0x19, 0xb8, 0xd1, 0xa7, 0x18, 0x0b, 0x62, 0xb0, 0x07, 0x56, 0x10, 0x18, 0xc7, 0xd6, + 0x9e, 0xe7, 0xef, 0xcd, 0xbf, 0xfa, 0xea, 0x82, 0xdd, 0x83, 0x8d, 0x43, 0xc3, 0xb7, 0xdc, 0x30, + 0x66, 0xbf, 0x42, 0xc7, 0x5c, 0x04, 0xb3, 0x4f, 0xe8, 0x20, 0xc7, 0x72, 0xc3, 0xa3, 0x58, 0x5b, + 0x17, 0x6d, 0x49, 0xbb, 0xf6, 0x97, 0xa8, 0xd4, 0x7f, 0x79, 0x07, 0x72, 0x3d, 0xcf, 0xb4, 0xd8, + 0x7b, 0x50, 0xa6, 0x68, 0xe7, 0xe5, 0xf3, 0x43, 0x44, 0xd3, 0x1f, 0x32, 0x9c, 0x4a, 0xae, 0xf8, + 0xba, 0x3c, 0x3e, 0xfa, 0x0e, 0x99, 0x80, 0x14, 0x80, 0x80, 0x02, 0xad, 0x22, 0x9c, 0x52, 0xe4, + 0x55, 0xe1, 0x18, 0x1c, 0x5b, 0x72, 0xaa, 0xfb, 0x96, 0x4b, 0x5a, 0x5a, 0x5e, 0x8b, 0xd3, 0x64, + 0x78, 0xfb, 0x1e, 0x0a, 0x5f, 0xbe, 0x57, 0xf3, 0x2b, 0x0c, 0x6f, 0x8e, 0xa7, 0xcd, 0xfb, 0x1e, + 0x94, 0xbf, 0xf0, 0x6c, 0x97, 0x37, 0xbc, 0xb0, 0xd4, 0x70, 0xd4, 0xad, 0x79, 0xc3, 0xbf, 0x10, + 0x5f, 0xec, 0x55, 0x28, 0x7a, 0x2e, 0x2f, 0xbb, 0xb8, 0x54, 0x76, 0xc1, 0x73, 0xbb, 0x3c, 0x40, + 0xaf, 0x36, 0x9a, 0xdb, 0x8e, 0x89, 0xb2, 0xcb, 0xb1, 0x26, 0xa1, 0x38, 0xe7, 0xab, 0x10, 0xb0, + 0xef, 0x76, 0xad, 0x49, 0xc8, 0xde, 0x82, 0xca, 0xc4, 0x76, 0x50, 0xc6, 0x53, 0x61, 0xe5, 0xa5, + 0xc2, 0x80, 0xa3, 0xa9, 0xc0, 0xd7, 0xa0, 0x74, 0xec, 0x7b, 0xf3, 0x99, 0x3e, 0xba, 0xa0, 0xf3, + 0xbd, 0x85, 0x93, 0x35, 0xc2, 0xed, 0x5c, 0xa0, 0xa0, 0xa1, 0x4f, 0xdb, 0x3d, 0xd6, 0xc9, 0x97, + 0x52, 0xd9, 0xce, 0xde, 0x2b, 0x69, 0xd5, 0x08, 0x48, 0x5e, 0x92, 0xd7, 0xa0, 0x64, 0x1c, 0x1f, + 0xeb, 0x22, 0xce, 0x70, 0xa9, 0x2c, 0xe3, 0xf8, 0x98, 0xaa, 0x7c, 0x00, 0xb5, 0x33, 0xdb, 0xd5, + 0x83, 0x99, 0x35, 0xe6, 0xb4, 0xb5, 0xe5, 0xa1, 0x3c, 0xb3, 0xdd, 0xc1, 0xcc, 0x1a, 0x13, 0xbd, + 0xec, 0xc3, 0xa8, 0xbf, 0xd0, 0x87, 0xb1, 0x0d, 0x79, 0xc7, 0x9e, 0xda, 0xa1, 0x88, 0x3c, 0x4c, + 0x19, 0x39, 0x84, 0x60, 0x2a, 0x14, 0x84, 0xf3, 0x5c, 0x59, 0x22, 0x11, 0x98, 0xb4, 0x06, 0xb4, + 0xf9, 0x02, 0x0d, 0x48, 0x32, 0x38, 0xd8, 0xd7, 0x1b, 0x1c, 0x1f, 0xd2, 0x09, 0xa3, 0xe5, 0x86, + 0x7a, 0x94, 0xe1, 0xca, 0xea, 0x0c, 0x55, 0x4e, 0xd6, 0xe7, 0xd9, 0xde, 0x87, 0x8a, 0x4f, 0xce, + 0x35, 0x9d, 0x3c, 0x71, 0x5b, 0xb2, 0x77, 0x22, 0xf1, 0xba, 0x69, 0xe0, 0x27, 0x1e, 0xb8, 0x26, + 0x6c, 0x24, 0x91, 0xd4, 0x3c, 0xdc, 0xfc, 0xaa, 0xec, 0xae, 0x4f, 0x85, 0x5e, 0x0b, 0x3d, 0xbe, + 0x66, 0xa7, 0xe2, 0xb1, 0x5f, 0x85, 0x1a, 0x8f, 0xa1, 0xe2, 0x91, 0x2e, 0x01, 0xc9, 0x86, 0xb2, + 0x56, 0x25, 0x20, 0x8f, 0x82, 0x09, 0xd8, 0x03, 0x80, 0x48, 0xfb, 0x0b, 0xcf, 0x49, 0x38, 0xc4, + 0xbd, 0xe1, 0x12, 0xa4, 0x15, 0x9e, 0x6b, 0x65, 0x33, 0xfa, 0x44, 0x9e, 0x37, 0xb2, 0x5d, 0x13, + 0xd7, 0x51, 0x68, 0x1c, 0x07, 0x8d, 0x06, 0x6d, 0xb3, 0x8a, 0x80, 0x0d, 0x8d, 0xe3, 0x00, 0xed, + 0x4d, 0x83, 0xeb, 0x58, 0xbc, 0xdd, 0xd7, 0x65, 0x67, 0x94, 0xa4, 0x7d, 0x69, 0x15, 0x43, 0x52, + 0xc5, 0x3e, 0x06, 0x16, 0x1d, 0xee, 0x49, 0xe6, 0xe3, 0x8d, 0xa5, 0xa5, 0xb5, 0x21, 0x4e, 0xf7, + 0xe2, 0xab, 0x1d, 0x1f, 0x43, 0x2d, 0xad, 0x13, 0xdf, 0x5c, 0x71, 0x9c, 0x45, 0xb3, 0xae, 0x55, + 0xc7, 0xb2, 0x96, 0xfc, 0x2a, 0xd4, 0x5c, 0x2f, 0xd4, 0x89, 0xef, 0x53, 0x46, 0x7e, 0x64, 0x53, + 0x75, 0xbd, 0xb0, 0x15, 0xc1, 0x70, 0x7c, 0x22, 0xcb, 0x2b, 0x3c, 0x27, 0x51, 0x11, 0x8f, 0x4f, + 0x6c, 0x26, 0xa1, 0xca, 0x17, 0x59, 0x4c, 0x38, 0xd5, 0xdc, 0x02, 0xa0, 0x0c, 0xb7, 0x53, 0x53, + 0x1d, 0x9b, 0x06, 0x1a, 0xf8, 0x89, 0x99, 0x70, 0x1b, 0x2a, 0x81, 0x37, 0xf7, 0xc7, 0x96, 0x1e, + 0x84, 0xd6, 0xac, 0xb1, 0x4d, 0x23, 0x0a, 0x1c, 0x34, 0x08, 0xad, 0x19, 0xfb, 0x04, 0xea, 0x33, + 0xdf, 0xd2, 0xa5, 0x79, 0xba, 0x23, 0x77, 0xf1, 0xd0, 0xb7, 0x92, 0xa9, 0xaa, 0xce, 0xa4, 0x54, + 0x94, 0x53, 0xea, 0x81, 0xba, 0x90, 0x33, 0xe9, 0x04, 0xe6, 0x4c, 0x2c, 0xbf, 0x1f, 0xc2, 0xa6, + 0x94, 0x73, 0x7e, 0x4a, 0x99, 0x5f, 0x4d, 0x9d, 0x2e, 0x46, 0xe4, 0x47, 0xa7, 0x98, 0xbd, 0x3e, + 0x4b, 0xa5, 0x59, 0x73, 0xc1, 0xad, 0x83, 0xfa, 0xf9, 0x5d, 0xca, 0xff, 0xd2, 0x25, 0xbe, 0x9a, + 0x94, 0xbf, 0xe7, 0x09, 0x3f, 0x47, 0xea, 0x04, 0x6d, 0xd7, 0x6c, 0xbc, 0xc6, 0xef, 0x5f, 0x51, + 0x82, 0x3d, 0x82, 0x2a, 0xd7, 0x14, 0x29, 0x42, 0x3a, 0x68, 0xbc, 0x2e, 0xfb, 0xb5, 0x49, 0x5d, + 0x24, 0x84, 0x56, 0x71, 0xe2, 0xef, 0x80, 0x7d, 0x04, 0x9b, 0xfc, 0x88, 0x41, 0xe6, 0xac, 0x6f, + 0x2c, 0x2f, 0x2e, 0x22, 0xda, 0x4b, 0xd8, 0xab, 0x06, 0xd7, 0xfd, 0xb9, 0x4b, 0xda, 0xa3, 0xc8, + 0x39, 0xf3, 0xbd, 0x91, 0xc5, 0xf3, 0xdf, 0xa3, 0xfc, 0xa2, 0x3b, 0x1a, 0x27, 0xe3, 0x79, 0x89, + 0xa5, 0x5d, 0xf3, 0x65, 0xd0, 0x21, 0xe6, 0xbb, 0xa4, 0x4c, 0x2e, 0x12, 0xa8, 0xcc, 0x37, 0xbf, + 0x4d, 0x99, 0x3b, 0x98, 0x8f, 0xca, 0x64, 0x90, 0x9b, 0xcf, 0x6d, 0xb3, 0x71, 0x9f, 0x07, 0x33, + 0xe3, 0x37, 0x7b, 0x0d, 0xea, 0xbe, 0x35, 0x9e, 0xfb, 0x81, 0xfd, 0xdc, 0xd2, 0x03, 0xdb, 0x3d, + 0x6d, 0xbc, 0x45, 0xe3, 0x58, 0x8b, 0xa1, 0x03, 0xdb, 0x3d, 0xc5, 0x15, 0x6b, 0x9d, 0x87, 0x96, + 0xef, 0xf2, 0x4b, 0x1b, 0x6f, 0xcb, 0x2b, 0xb6, 0x4d, 0x08, 0xe4, 0x28, 0x1a, 0x58, 0xf1, 0x37, + 0xfb, 0x01, 0x6c, 0x24, 0xd6, 0xda, 0x0c, 0x75, 0x97, 0xc6, 0x3b, 0x2b, 0x0f, 0x9e, 0x49, 0xaf, + 0xd1, 0x92, 0xa8, 0x0c, 0xae, 0x02, 0xa5, 0xd7, 0x56, 0xc0, 0xd7, 0xd6, 0x83, 0x6f, 0xb4, 0xb6, + 0x06, 0xb4, 0xb6, 0x5e, 0x87, 0x92, 0xed, 0x86, 0x96, 0xff, 0xdc, 0x70, 0x1a, 0xef, 0x2e, 0xc9, + 0x80, 0x18, 0xc7, 0xee, 0x42, 0x31, 0x70, 0x6c, 0x64, 0x4c, 0x8d, 0xf7, 0x96, 0xc8, 0x22, 0x14, + 0xbb, 0x07, 0xe5, 0xf8, 0xc2, 0x61, 0xe3, 0xfd, 0x25, 0xba, 0x04, 0xc9, 0x6e, 0x41, 0xee, 0x0c, + 0xd7, 0xe3, 0xc3, 0xe5, 0x63, 0x0c, 0x84, 0xa3, 0xd2, 0x30, 0xb1, 0x1d, 0x87, 0x2b, 0x0d, 0x8f, + 0x96, 0x94, 0x86, 0x3d, 0xdb, 0x71, 0xb8, 0xd2, 0x30, 0x11, 0x5f, 0x28, 0x72, 0x29, 0x07, 0xf6, + 0xe4, 0x83, 0x65, 0x91, 0x8b, 0xb8, 0xa7, 0x74, 0x35, 0xb3, 0x12, 0x90, 0x6f, 0x9e, 0x1f, 0x31, + 0x7c, 0x28, 0x8f, 0x55, 0xda, 0x69, 0xaf, 0x41, 0x10, 0xa7, 0x51, 0xf3, 0x17, 0x27, 0x13, 0x68, + 0x52, 0x7f, 0xc4, 0x6f, 0x0c, 0x71, 0x08, 0xda, 0xd3, 0xef, 0x41, 0x2d, 0x0a, 0xc9, 0xc3, 0xea, + 0x82, 0xc6, 0xc7, 0x4b, 0x2d, 0x48, 0x13, 0xb0, 0x5d, 0xa8, 0x4e, 0x50, 0x89, 0x9c, 0x72, 0x9d, + 0xb2, 0xf1, 0x09, 0x35, 0x64, 0x3b, 0x12, 0xe7, 0x97, 0xe9, 0x9c, 0x5a, 0x2a, 0x17, 0x7b, 0x00, + 0xcc, 0x9e, 0xf0, 0xf9, 0x44, 0x1b, 0x9d, 0xeb, 0x8d, 0x8d, 0x4f, 0x69, 0x71, 0xae, 0xc0, 0xb0, + 0x47, 0x50, 0x0b, 0x2c, 0xd7, 0xd4, 0xa7, 0x81, 0x50, 0x4e, 0xbe, 0x47, 0xed, 0x14, 0x6c, 0x38, + 0xbe, 0x98, 0xac, 0x55, 0x90, 0xea, 0x20, 0xe0, 0x5a, 0xca, 0x23, 0xc0, 0x75, 0xfe, 0x3c, 0xc9, + 0xf4, 0x6b, 0x97, 0x64, 0x42, 0xaa, 0x28, 0xd3, 0x27, 0x50, 0x37, 0x2d, 0x73, 0x3e, 0xd3, 0x49, + 0xf7, 0xc3, 0x65, 0xf9, 0x7d, 0x99, 0x5f, 0xca, 0x6e, 0x55, 0xad, 0x6a, 0xca, 0x4e, 0xd6, 0x8f, + 0x61, 0x23, 0xf2, 0x7f, 0x86, 0xc2, 0x55, 0xfa, 0x03, 0xb9, 0xc2, 0xd8, 0xbd, 0xa9, 0xd5, 0xe6, + 0xd1, 0x67, 0xd4, 0x4e, 0x12, 0xf1, 0x81, 0x6b, 0xcc, 0x82, 0x13, 0x2f, 0x6c, 0xfc, 0xba, 0xac, + 0xad, 0x0c, 0x04, 0x54, 0xab, 0x22, 0x51, 0x94, 0x42, 0xd1, 0x95, 0x6c, 0xed, 0x71, 0x68, 0x35, + 0x7e, 0xc8, 0x45, 0x57, 0x0c, 0x6c, 0x85, 0x38, 0x6c, 0x60, 0xcc, 0x66, 0xce, 0x05, 0x5f, 0x8e, + 0x3f, 0xa2, 0xe5, 0xb8, 0x25, 0x2d, 0xc7, 0x26, 0x22, 0x69, 0x3d, 0x96, 0x8d, 0xe8, 0x93, 0x3d, + 0x84, 0xea, 0xcc, 0x0b, 0x42, 0xdd, 0x9c, 0x3a, 0xd4, 0xff, 0xa6, 0xcc, 0x0e, 0x0e, 0xbd, 0x20, + 0xdc, 0x9d, 0x3a, 0x24, 0xc0, 0x66, 0xf1, 0x37, 0xeb, 0xc2, 0x95, 0x14, 0xab, 0x37, 0xe8, 0x6c, + 0xbf, 0xb1, 0x43, 0x35, 0xde, 0x94, 0x6a, 0x94, 0x58, 0xbe, 0x88, 0x09, 0xdd, 0xf4, 0x16, 0x41, + 0x68, 0xf4, 0xf1, 0x39, 0x88, 0x03, 0xa3, 0x5b, 0x5c, 0x6f, 0x21, 0x68, 0x14, 0x19, 0xfd, 0x09, + 0x6c, 0x24, 0x54, 0xd8, 0xc1, 0xa0, 0xb1, 0x2b, 0xaf, 0x5e, 0xe9, 0xfa, 0x42, 0x2d, 0xca, 0x88, + 0xb0, 0x40, 0xfd, 0xf3, 0x3c, 0x94, 0x22, 0xbb, 0x83, 0x55, 0xa0, 0x78, 0xd4, 0x7b, 0xd2, 0xeb, + 0x3f, 0xeb, 0xf1, 0x0b, 0x92, 0xcd, 0xc1, 0xa0, 0xad, 0x0d, 0x15, 0x93, 0xd5, 0x01, 0xe8, 0x9a, + 0x94, 0x3e, 0x68, 0x35, 0x7b, 0xfc, 0xc2, 0x24, 0x5d, 0xce, 0xe2, 0xe9, 0x75, 0xb6, 0x09, 0xb5, + 0xbd, 0xa3, 0x1e, 0x85, 0x9e, 0x72, 0x50, 0x16, 0x41, 0xed, 0xcf, 0xf8, 0x21, 0x23, 0x07, 0xe5, + 0x10, 0x74, 0xd0, 0x1c, 0xb6, 0xb5, 0x4e, 0x04, 0xca, 0x53, 0x14, 0x6b, 0xff, 0x48, 0x6b, 0x89, + 0x92, 0x0a, 0xec, 0x2a, 0x6c, 0xc6, 0xd9, 0xa2, 0x22, 0x95, 0x22, 0xb6, 0xec, 0x50, 0xeb, 0xff, + 0xb8, 0xdd, 0x1a, 0x2a, 0x40, 0x27, 0x96, 0x8f, 0x1f, 0x2b, 0x15, 0x56, 0x85, 0xd2, 0x6e, 0x67, + 0x30, 0xec, 0xf4, 0x5a, 0x43, 0xa5, 0x8a, 0x0d, 0xde, 0xeb, 0x74, 0x87, 0x6d, 0x4d, 0xa9, 0xb1, + 0x12, 0xe4, 0x7e, 0xdc, 0xef, 0xf4, 0x94, 0x3a, 0x5d, 0x17, 0x6b, 0x1e, 0x1c, 0x76, 0xdb, 0xca, + 0x06, 0x42, 0x07, 0x7d, 0x6d, 0xa8, 0x28, 0x08, 0x7d, 0xd6, 0xe9, 0xed, 0xf6, 0x9f, 0x29, 0x9b, + 0xac, 0x0c, 0xf9, 0xa3, 0x1e, 0x56, 0xc3, 0x58, 0x0d, 0xca, 0xf4, 0xa9, 0x37, 0xbb, 0x5d, 0xe5, + 0x8a, 0x74, 0xcc, 0xb9, 0x85, 0x28, 0x3a, 0x34, 0x1d, 0x60, 0x1b, 0xae, 0x62, 0x5f, 0xe2, 0x24, + 0x51, 0x5f, 0xc3, 0x72, 0x0e, 0x3a, 0xbd, 0xa3, 0x81, 0xf2, 0x12, 0x12, 0xd3, 0x27, 0x61, 0x1a, + 0x58, 0x4e, 0xa7, 0x47, 0x43, 0x79, 0x0b, 0xbf, 0x77, 0xdb, 0xdd, 0xf6, 0xb0, 0xad, 0xdc, 0xc6, + 0x5e, 0x69, 0xed, 0xc3, 0x6e, 0xb3, 0xd5, 0x56, 0xb6, 0x31, 0xd1, 0xed, 0xb7, 0x9e, 0xe8, 0xfd, + 0x43, 0xe5, 0x0e, 0xdb, 0x02, 0xa5, 0xdf, 0xd3, 0x77, 0x8f, 0x0e, 0xbb, 0x9d, 0x56, 0x73, 0xd8, + 0xd6, 0x9f, 0xb4, 0x3f, 0x57, 0x54, 0x1c, 0xf6, 0x43, 0xad, 0xad, 0x8b, 0xb2, 0x5e, 0x8d, 0xd2, + 0xa2, 0xbc, 0xbb, 0x4c, 0x81, 0xea, 0xde, 0xd1, 0x4f, 0x7f, 0xfa, 0xb9, 0x2e, 0xc6, 0xe1, 0x35, + 0x6c, 0x66, 0x92, 0x43, 0x3f, 0x7a, 0xa2, 0xbc, 0xbe, 0x00, 0x1a, 0x3c, 0x51, 0xde, 0xc0, 0x71, + 0x8c, 0x26, 0x46, 0xb9, 0x87, 0x04, 0x5a, 0xbb, 0x75, 0xa4, 0x0d, 0x3a, 0x4f, 0xdb, 0x7a, 0x6b, + 0xd8, 0x56, 0xde, 0xa4, 0x81, 0xeb, 0xf4, 0x9e, 0x28, 0xf7, 0xb1, 0x67, 0xf8, 0xc5, 0xa7, 0xeb, + 0x2d, 0xc6, 0xa0, 0x9e, 0xd0, 0x12, 0xec, 0x6d, 0x24, 0xd9, 0xd1, 0xfa, 0xcd, 0xdd, 0x56, 0x73, + 0x30, 0x54, 0xde, 0xc1, 0x61, 0x19, 0x1c, 0x76, 0x3b, 0x43, 0xe5, 0x01, 0xf6, 0xfd, 0x71, 0x73, + 0xb8, 0xdf, 0xd6, 0x94, 0x77, 0x71, 0xe6, 0x87, 0x9d, 0x83, 0xb6, 0x2e, 0xa6, 0xe1, 0x21, 0xd6, + 0xb1, 0xd7, 0xe9, 0x76, 0x95, 0x47, 0x74, 0xb2, 0xd7, 0xd4, 0x86, 0x1d, 0x9a, 0xfb, 0x0f, 0xb0, + 0x80, 0xe6, 0xe1, 0x61, 0xf7, 0x73, 0xe5, 0x43, 0xec, 0xe0, 0xc1, 0x51, 0x77, 0xd8, 0xd1, 0x8f, + 0x0e, 0x77, 0x9b, 0xc3, 0xb6, 0xf2, 0x11, 0x2d, 0x8c, 0xfe, 0x60, 0xb8, 0x7b, 0xd0, 0x55, 0x3e, + 0x56, 0x7f, 0x13, 0x4a, 0x91, 0x29, 0x8a, 0xb9, 0x3a, 0xbd, 0x5e, 0x5b, 0x53, 0xd6, 0xb0, 0xe4, + 0x6e, 0x7b, 0x6f, 0xa8, 0x64, 0xe8, 0x54, 0xb3, 0xf3, 0x78, 0x7f, 0xa8, 0xac, 0xe3, 0x67, 0xff, + 0x08, 0x07, 0x29, 0x4b, 0xbd, 0x6b, 0x1f, 0x74, 0x94, 0x1c, 0x7e, 0x35, 0x7b, 0xc3, 0x8e, 0x92, + 0xa7, 0x65, 0xd3, 0xe9, 0x3d, 0xee, 0xb6, 0x95, 0x02, 0x42, 0x0f, 0x9a, 0xda, 0x13, 0xa5, 0xc8, + 0x0b, 0xdd, 0x6d, 0x7f, 0xa6, 0x94, 0x58, 0x01, 0xd6, 0xbb, 0x0f, 0x95, 0x32, 0x82, 0x76, 0xdb, + 0xbb, 0x47, 0x87, 0x0a, 0xa8, 0xf7, 0xa0, 0xd8, 0x3c, 0x3e, 0x3e, 0x40, 0x4b, 0x1f, 0x3b, 0x73, + 0xd4, 0xed, 0xf2, 0x6d, 0xb4, 0xd3, 0x1f, 0x0e, 0xfb, 0x07, 0x4a, 0x06, 0x17, 0xee, 0xb0, 0x7f, + 0xa8, 0xac, 0xab, 0x1d, 0x28, 0x45, 0xe2, 0x4f, 0xba, 0x01, 0x59, 0x82, 0xdc, 0xa1, 0xd6, 0x7e, + 0xca, 0x8f, 0xe2, 0x7b, 0xed, 0xcf, 0xb0, 0x99, 0xf8, 0x85, 0x05, 0x65, 0xb1, 0x22, 0x7e, 0x55, + 0x91, 0xae, 0x40, 0x76, 0x3b, 0xbd, 0x76, 0x53, 0x53, 0xf2, 0xea, 0x87, 0xa9, 0x53, 0x4e, 0xc1, + 0x35, 0xb0, 0xfa, 0x66, 0x47, 0x54, 0xdf, 0x79, 0xdc, 0xeb, 0x6b, 0x6d, 0x7e, 0xa7, 0x52, 0x8c, + 0xdb, 0xba, 0xfa, 0x16, 0x94, 0x63, 0x8e, 0x87, 0xeb, 0xa8, 0xa5, 0xf5, 0x07, 0x03, 0x3e, 0xcc, + 0x6b, 0x98, 0xa6, 0xb1, 0xe1, 0xe9, 0x8c, 0xfa, 0xff, 0x42, 0x29, 0x66, 0xb6, 0x77, 0x61, 0x7d, + 0x38, 0x10, 0x5e, 0xfc, 0xad, 0x07, 0xc9, 0xa3, 0x1e, 0xc3, 0xe8, 0x4b, 0x5b, 0x1f, 0x0e, 0xd8, + 0xdb, 0x50, 0xe0, 0x57, 0x7a, 0xc5, 0x41, 0xd4, 0x56, 0x9a, 0x81, 0x0f, 0x09, 0xa7, 0x09, 0x1a, + 0xb5, 0x0b, 0xf5, 0x34, 0x86, 0xdd, 0x02, 0xe0, 0x38, 0xc9, 0x23, 0x23, 0x41, 0xd8, 0x0d, 0x88, + 0xae, 0x0c, 0xef, 0x8a, 0x68, 0xd5, 0x38, 0xad, 0xfe, 0x83, 0x2c, 0x40, 0xa2, 0xaa, 0xa1, 0x32, + 0x18, 0xfb, 0x5b, 0xf2, 0xe2, 0x4c, 0xfa, 0x65, 0x28, 0x3b, 0x9e, 0x61, 0xca, 0x8f, 0x73, 0x94, + 0x10, 0x40, 0xa3, 0x21, 0x5f, 0x9f, 0x2b, 0xf3, 0x80, 0x10, 0x76, 0x0d, 0x0a, 0x13, 0xcf, 0x9f, + 0x1a, 0x51, 0x5c, 0xab, 0x48, 0xa1, 0xe8, 0xe1, 0xe7, 0xa4, 0xa8, 0xb0, 0xba, 0x74, 0x35, 0x85, + 0x82, 0xa4, 0x05, 0xb0, 0x8b, 0x30, 0x34, 0x69, 0x2c, 0x77, 0xec, 0x78, 0x81, 0x65, 0xa2, 0xd5, + 0x5f, 0x20, 0xad, 0x14, 0x22, 0xd0, 0xce, 0x05, 0xef, 0xad, 0x3f, 0xb5, 0x5d, 0x23, 0x14, 0xae, + 0x6a, 0xea, 0x6d, 0x04, 0xc1, 0xe6, 0x7e, 0x11, 0x78, 0xc2, 0xfd, 0xc2, 0x8f, 0x5c, 0x4b, 0x08, + 0xa0, 0xe6, 0xbe, 0x02, 0x60, 0x05, 0x63, 0x63, 0xc6, 0x0b, 0x2f, 0x53, 0xe1, 0x65, 0x01, 0xd9, + 0xb9, 0x60, 0x5d, 0xa8, 0x0f, 0x47, 0xc8, 0xee, 0x3d, 0xb4, 0xa4, 0x5b, 0x9e, 0x23, 0x1c, 0x23, + 0x77, 0x17, 0x75, 0xda, 0x07, 0x69, 0x32, 0x7e, 0x36, 0xbc, 0x90, 0xf7, 0x46, 0x13, 0xae, 0xac, + 0x20, 0xfb, 0x56, 0x51, 0x6f, 0x4e, 0x34, 0x3b, 0xcd, 0x30, 0xa4, 0xc8, 0xe9, 0x58, 0xb2, 0x65, + 0xa2, 0x90, 0x61, 0x2e, 0xd4, 0x5e, 0xa6, 0x30, 0x18, 0x11, 0xdf, 0x28, 0x26, 0x29, 0x8e, 0x5b, + 0x7c, 0x1d, 0x36, 0x10, 0x39, 0xb1, 0x2d, 0xc7, 0x14, 0x24, 0xfc, 0x26, 0x53, 0x6d, 0xec, 0x39, + 0x7b, 0x08, 0x25, 0x3a, 0xf5, 0xaf, 0x72, 0x00, 0x89, 0x19, 0x94, 0x3a, 0x9e, 0xce, 0xa4, 0x8f, + 0xa7, 0x1f, 0xc2, 0x35, 0x71, 0xcf, 0x2d, 0x3e, 0xe3, 0xb5, 0x5d, 0x7d, 0x64, 0x44, 0x91, 0x00, + 0x4c, 0x60, 0xf9, 0x31, 0x6f, 0xc7, 0xdd, 0x31, 0x50, 0x43, 0xda, 0x90, 0xf3, 0x84, 0x17, 0xb3, + 0x74, 0x24, 0x83, 0x2c, 0x77, 0x93, 0xec, 0xc3, 0x8b, 0x19, 0x7b, 0x0f, 0xae, 0xfa, 0xd6, 0xc4, + 0xb7, 0x82, 0x13, 0x3d, 0x0c, 0xe4, 0xca, 0x78, 0x78, 0xdd, 0xa6, 0x40, 0x0e, 0x83, 0xb8, 0xae, + 0xf7, 0xe0, 0xaa, 0x30, 0x90, 0x16, 0x9a, 0xc7, 0x4f, 0x54, 0x37, 0x39, 0x52, 0x6e, 0xdd, 0x2b, + 0x00, 0xc2, 0x36, 0x8c, 0x9e, 0xb8, 0x29, 0x69, 0x65, 0x6e, 0x07, 0xa2, 0x31, 0xff, 0x36, 0x30, + 0x3b, 0xd0, 0x17, 0x0e, 0xa7, 0xc4, 0x79, 0xbf, 0x62, 0x07, 0x87, 0xa9, 0x83, 0xa9, 0xcb, 0xce, + 0xbd, 0x4a, 0x97, 0x9d, 0x7b, 0x6d, 0x41, 0x9e, 0xcc, 0x47, 0x71, 0x0c, 0xc5, 0x13, 0x4c, 0x85, + 0x1c, 0xf2, 0x47, 0x3a, 0x32, 0xa9, 0x3f, 0xac, 0x3f, 0xa0, 0x07, 0x82, 0x70, 0x7e, 0x10, 0xaa, + 0x11, 0x8e, 0xbd, 0x03, 0x57, 0xe4, 0x41, 0x8d, 0x5e, 0xbf, 0xa8, 0x50, 0x37, 0x95, 0x64, 0x18, + 0x35, 0xfe, 0x0e, 0xc6, 0x5b, 0xc0, 0xa4, 0x71, 0x89, 0xa8, 0xab, 0xfc, 0xe8, 0x38, 0x1e, 0x14, + 0x41, 0xfc, 0x06, 0xd0, 0x00, 0x70, 0x0f, 0x75, 0x6d, 0xd9, 0x58, 0x42, 0x24, 0x79, 0xb3, 0xdf, + 0x83, 0xab, 0xc9, 0xd8, 0xe9, 0x46, 0xa8, 0x87, 0x27, 0x96, 0x6e, 0xb9, 0x26, 0x5d, 0x7d, 0x2c, + 0x69, 0x9b, 0xf1, 0x30, 0x36, 0xc3, 0xe1, 0x89, 0xd5, 0x76, 0x4d, 0xf5, 0xf7, 0x33, 0x50, 0x4f, + 0x5b, 0x6a, 0x3c, 0x1c, 0x3e, 0x89, 0xf3, 0xcf, 0x27, 0xb1, 0xfd, 0x2f, 0x43, 0x79, 0x76, 0x2a, + 0x82, 0xfa, 0xa3, 0xb5, 0x3d, 0x3b, 0xe5, 0xc1, 0xfc, 0xec, 0x4d, 0x28, 0xce, 0x4e, 0xf9, 0x66, + 0xbf, 0x6c, 0x35, 0x15, 0x66, 0x3c, 0xce, 0xf6, 0x4d, 0x28, 0xce, 0x05, 0x69, 0xee, 0x32, 0xd2, + 0x39, 0x91, 0xaa, 0xdb, 0x50, 0x95, 0x7d, 0x23, 0xb8, 0x67, 0xd1, 0x0e, 0xe2, 0x0d, 0xc3, 0x4f, + 0xf5, 0xb7, 0xd6, 0x89, 0xe4, 0x5b, 0x1d, 0x78, 0x7f, 0xab, 0xa0, 0x83, 0x6d, 0x0a, 0x0c, 0xd4, + 0x29, 0xec, 0x77, 0xec, 0x45, 0xef, 0xb6, 0xc0, 0x89, 0x11, 0x34, 0xe7, 0xa1, 0xd7, 0xf2, 0x9c, + 0xe8, 0xd1, 0x02, 0x7e, 0x8f, 0x2a, 0x17, 0x3f, 0x5a, 0xc0, 0xaf, 0x58, 0xbe, 0x27, 0x2e, 0x1b, + 0xd1, 0x4d, 0x3f, 0x0a, 0x77, 0xc9, 0x2f, 0xcd, 0x60, 0x35, 0xba, 0xe8, 0x47, 0x91, 0x2c, 0x0f, + 0x61, 0x23, 0x89, 0xec, 0x8e, 0x22, 0x64, 0x16, 0xb3, 0xd4, 0xe2, 0xb0, 0x6e, 0x4c, 0xaa, 0xbf, + 0x93, 0x81, 0xcd, 0x25, 0x57, 0x03, 0x8e, 0x56, 0xf2, 0xc4, 0x13, 0x7e, 0xb2, 0x3b, 0x50, 0x9d, + 0x1a, 0xe1, 0xf8, 0x44, 0x9f, 0xf9, 0xd6, 0xc4, 0x3e, 0x8f, 0xde, 0xa9, 0x22, 0xd8, 0x21, 0x81, + 0x28, 0xda, 0x67, 0x36, 0x23, 0x07, 0xcb, 0xd4, 0x0e, 0x05, 0x83, 0x02, 0x02, 0x75, 0xc9, 0x79, + 0x1b, 0x45, 0x02, 0xe6, 0x2e, 0x09, 0x5c, 0xbc, 0x09, 0x85, 0x4e, 0xec, 0xd2, 0x88, 0xc3, 0x55, + 0xb2, 0xe2, 0x99, 0x16, 0x0f, 0xca, 0x2d, 0x7a, 0xf2, 0xe5, 0xc0, 0x98, 0xb1, 0xfb, 0x90, 0x9d, + 0x1a, 0x33, 0x11, 0xce, 0xd2, 0x88, 0x4f, 0x24, 0x38, 0xf6, 0xc1, 0x81, 0x31, 0xe3, 0x0c, 0x1d, + 0x89, 0x6e, 0x7c, 0x04, 0xa5, 0x08, 0xf0, 0xad, 0x58, 0xf7, 0x7f, 0xc9, 0x42, 0x79, 0x57, 0x76, + 0x7e, 0xa2, 0xa9, 0x16, 0xfa, 0x73, 0x17, 0x55, 0x8f, 0xe8, 0x81, 0x8b, 0xb1, 0xe1, 0x0e, 0x05, + 0x28, 0x5a, 0x40, 0xeb, 0x5f, 0xb3, 0x80, 0x6e, 0x02, 0xf8, 0x64, 0x92, 0x93, 0x55, 0x9e, 0x8d, + 0x43, 0x27, 0x3b, 0xa6, 0x08, 0x0b, 0x59, 0x8e, 0x08, 0xc8, 0x7d, 0xf3, 0x88, 0x80, 0xfc, 0xca, + 0x88, 0x80, 0xff, 0x63, 0xce, 0xf0, 0x5f, 0x4f, 0xe4, 0x07, 0xae, 0x69, 0x24, 0x2b, 0x73, 0x29, + 0x36, 0x8b, 0xaf, 0x5f, 0x20, 0xdd, 0xf7, 0xa0, 0x1e, 0x0d, 0xb3, 0xe8, 0x18, 0xa4, 0x6e, 0x7c, + 0x08, 0x1c, 0xf7, 0xeb, 0xd6, 0x42, 0x39, 0x99, 0xde, 0xa1, 0x95, 0xaf, 0xdf, 0xa1, 0xea, 0x1f, + 0x64, 0x80, 0x09, 0xbb, 0x76, 0x6f, 0xee, 0x38, 0x43, 0xeb, 0x9c, 0x18, 0xc1, 0x7d, 0xd8, 0x14, + 0x4e, 0x59, 0x29, 0xf6, 0x4b, 0x9c, 0x93, 0x71, 0x44, 0x72, 0x4e, 0xb6, 0xea, 0xb2, 0xeb, 0xfa, + 0xca, 0xcb, 0xae, 0xab, 0x2f, 0xd1, 0xde, 0x86, 0x8a, 0x7c, 0x55, 0x94, 0xeb, 0x5b, 0x60, 0x24, + 0xb7, 0x44, 0xff, 0xe3, 0x3a, 0x40, 0x62, 0x7b, 0xff, 0xaa, 0xe3, 0x4a, 0x56, 0x4c, 0x49, 0x76, + 0xd5, 0x94, 0xdc, 0x03, 0x45, 0xa6, 0x93, 0xee, 0x2c, 0xd7, 0x13, 0xc2, 0x48, 0x8f, 0xb1, 0x03, + 0xf9, 0x5e, 0x29, 0xf1, 0x34, 0x71, 0x64, 0x2d, 0x62, 0xed, 0x88, 0xe5, 0x0a, 0x11, 0x5d, 0xb2, + 0x03, 0xce, 0x82, 0xd9, 0xa7, 0x70, 0x3d, 0xce, 0xa9, 0x9f, 0xd9, 0xe1, 0x89, 0x37, 0x0f, 0x85, + 0x97, 0x34, 0x10, 0x82, 0xfa, 0x5a, 0x54, 0xd2, 0x33, 0x8e, 0xe6, 0x2c, 0x2b, 0x60, 0x1f, 0x42, + 0x79, 0x32, 0x77, 0x1c, 0x3d, 0xb4, 0xce, 0x43, 0x11, 0x80, 0xdd, 0x48, 0xb9, 0x2d, 0xa4, 0xe9, + 0xd5, 0x4a, 0x13, 0x91, 0x50, 0xff, 0xe7, 0x3a, 0xe4, 0x7f, 0x32, 0xb7, 0xfc, 0x0b, 0xf6, 0x11, + 0x94, 0x83, 0x70, 0x1a, 0xca, 0x67, 0x95, 0xd7, 0x79, 0x01, 0x84, 0xa7, 0xa3, 0x46, 0x6b, 0x6a, + 0xb9, 0x21, 0xf7, 0xe1, 0x21, 0x2d, 0x49, 0xa4, 0x2d, 0xc8, 0x07, 0xa1, 0x35, 0x0b, 0x44, 0x6c, + 0x1c, 0x4f, 0xb0, 0x6d, 0xc8, 0xbb, 0x9e, 0x69, 0x05, 0xe9, 0x08, 0xb8, 0x1e, 0x0a, 0x7d, 0x8e, + 0x60, 0x2a, 0x14, 0xe2, 0x19, 0x5f, 0x3a, 0x2f, 0xe4, 0x18, 0xba, 0xc1, 0x60, 0x19, 0xa6, 0xed, + 0x1e, 0x47, 0x77, 0xc0, 0xe3, 0x34, 0xca, 0x5a, 0xd2, 0xe0, 0x8d, 0xe3, 0xe8, 0x41, 0x06, 0x91, + 0x64, 0xdb, 0x50, 0xc1, 0xcf, 0x67, 0xbe, 0x1d, 0x5a, 0x83, 0x47, 0x62, 0xdc, 0x64, 0x10, 0xea, + 0xdf, 0xa6, 0x15, 0x5a, 0xe3, 0x70, 0xf0, 0xa5, 0x08, 0x0d, 0xa3, 0x98, 0x9f, 0x08, 0xa2, 0x9a, + 0x50, 0x4b, 0x75, 0x77, 0xc9, 0x51, 0x32, 0x68, 0x77, 0xdb, 0xad, 0x21, 0x37, 0xb1, 0x84, 0x75, + 0xbe, 0x2e, 0x5b, 0xf7, 0x59, 0xc9, 0xec, 0xcf, 0x49, 0x76, 0x58, 0x9e, 0x9c, 0x06, 0x6d, 0xed, + 0x71, 0x5b, 0x29, 0xa8, 0x7f, 0xb8, 0x0e, 0x9b, 0x43, 0xdf, 0x70, 0x03, 0x83, 0x5f, 0xec, 0x73, + 0x43, 0xdf, 0x73, 0xd8, 0xf7, 0xa0, 0x14, 0x8e, 0x1d, 0x79, 0x1a, 0x6e, 0x47, 0x9b, 0x7e, 0x81, + 0xf4, 0xc1, 0x70, 0xcc, 0x1d, 0xaa, 0xc5, 0x90, 0x7f, 0xb0, 0x77, 0x20, 0x3f, 0xb2, 0x8e, 0x6d, + 0x57, 0x30, 0xe0, 0xab, 0x8b, 0x19, 0x77, 0x10, 0xb9, 0xbf, 0xa6, 0x71, 0x2a, 0xf6, 0x1e, 0x14, + 0xc6, 0xde, 0x34, 0x92, 0x54, 0xc9, 0x1d, 0x24, 0xa9, 0x22, 0xc4, 0xee, 0xaf, 0x69, 0x82, 0x8e, + 0x7d, 0x04, 0x25, 0xdf, 0x73, 0x9c, 0x91, 0x31, 0x3e, 0x15, 0x32, 0xac, 0xb1, 0x98, 0x47, 0x13, + 0xf8, 0xfd, 0x35, 0x2d, 0xa6, 0x55, 0x1f, 0x40, 0x51, 0x34, 0x16, 0x07, 0x60, 0xa7, 0xfd, 0xb8, + 0x23, 0x06, 0xb2, 0xd5, 0x3f, 0x38, 0xe8, 0x0c, 0xf9, 0x65, 0x67, 0xad, 0xdf, 0xed, 0xee, 0x34, + 0x5b, 0x4f, 0x94, 0xf5, 0x9d, 0x12, 0x14, 0xb8, 0x1b, 0x4d, 0xfd, 0xed, 0x0c, 0x6c, 0x2c, 0x74, + 0x80, 0x7d, 0x02, 0xb9, 0x29, 0x2a, 0x95, 0x7c, 0x78, 0xee, 0xae, 0xec, 0xa5, 0x94, 0xe6, 0xaa, + 0x26, 0xe6, 0x50, 0x3f, 0x85, 0x7a, 0x1a, 0x2e, 0x59, 0xe3, 0x35, 0x28, 0x6b, 0xed, 0xe6, 0xae, + 0xde, 0xef, 0xa1, 0x0d, 0x8c, 0x36, 0x31, 0x25, 0x9f, 0x69, 0x1d, 0x32, 0xa0, 0x7f, 0x03, 0x94, + 0xc5, 0x81, 0x61, 0x8f, 0xd1, 0x28, 0x99, 0xce, 0x1c, 0x8b, 0x0b, 0x8a, 0x64, 0xca, 0x6e, 0xad, + 0x18, 0x49, 0x41, 0x46, 0x33, 0x56, 0x1f, 0xa7, 0xd2, 0xea, 0xff, 0x03, 0x6c, 0x79, 0x04, 0x7f, + 0x75, 0xc5, 0xff, 0x8f, 0x0c, 0xe4, 0x0e, 0x1d, 0xc3, 0x65, 0xaf, 0x42, 0x9e, 0x1e, 0xe9, 0x11, + 0xdc, 0xb3, 0x22, 0x6d, 0x70, 0x5c, 0x16, 0x84, 0x63, 0x6f, 0x41, 0x36, 0x1c, 0x47, 0x17, 0xbb, + 0x5f, 0xba, 0x64, 0xf1, 0xed, 0xaf, 0x69, 0x48, 0xc5, 0xee, 0x41, 0xd6, 0x34, 0xa3, 0x78, 0x6e, + 0x61, 0xf5, 0xa3, 0xa9, 0xb8, 0x6b, 0x4d, 0x6c, 0xd7, 0x16, 0x8f, 0x0a, 0x21, 0x09, 0x7b, 0x0d, + 0xb2, 0xe6, 0xd8, 0x49, 0x07, 0xe7, 0x73, 0xa3, 0x32, 0x2e, 0xd0, 0x1c, 0x3b, 0x4c, 0x85, 0x5a, + 0xe8, 0x5f, 0xe8, 0xfe, 0xdc, 0xa5, 0x68, 0xa8, 0x40, 0x98, 0x3b, 0x15, 0x54, 0x66, 0xe6, 0x14, + 0x52, 0x15, 0x88, 0x0b, 0x62, 0x33, 0xdf, 0x9a, 0x19, 0x7e, 0x6c, 0xe8, 0xd8, 0xc1, 0x21, 0x07, + 0xec, 0x14, 0x80, 0x5e, 0x2f, 0x55, 0xdf, 0xa6, 0x17, 0x64, 0x50, 0xc3, 0x56, 0xa3, 0xaf, 0x15, + 0xf7, 0x6f, 0x05, 0x46, 0xfd, 0xcb, 0x2c, 0x54, 0xa4, 0xf6, 0xb0, 0x0f, 0xa0, 0x64, 0xa6, 0x37, + 0xe2, 0xf5, 0xa5, 0x46, 0x3f, 0xd8, 0x8d, 0xb6, 0xa0, 0x29, 0x96, 0xf7, 0xa7, 0x50, 0x0b, 0xac, + 0x50, 0x7f, 0x6e, 0xf8, 0x36, 0x7f, 0x37, 0x6c, 0x5d, 0x76, 0xa1, 0x0f, 0xac, 0xf0, 0x69, 0x84, + 0xd9, 0x5f, 0xd3, 0xaa, 0x81, 0x94, 0x26, 0x33, 0x40, 0x74, 0x29, 0x9b, 0x7a, 0x5b, 0x8d, 0x03, + 0xf7, 0xd7, 0xb4, 0x08, 0x8f, 0xa4, 0xd6, 0xb9, 0x35, 0x9e, 0x87, 0x91, 0x19, 0x50, 0x8b, 0x3a, + 0x44, 0x40, 0x7a, 0xc6, 0x91, 0x7f, 0xb2, 0x87, 0xc8, 0xeb, 0x0c, 0xc7, 0xf1, 0x48, 0x67, 0xcb, + 0xcb, 0x0e, 0xed, 0xdd, 0x18, 0xce, 0x9f, 0x8d, 0x8c, 0x52, 0xec, 0x75, 0xc8, 0x7b, 0xe1, 0x89, + 0x15, 0x29, 0xcf, 0xd1, 0x5b, 0x34, 0x08, 0xda, 0x6d, 0x75, 0x71, 0xa5, 0x10, 0x5a, 0xfd, 0x79, + 0x06, 0x8a, 0x62, 0x04, 0xd8, 0x26, 0xd4, 0x06, 0xed, 0xa1, 0xfe, 0xb4, 0xa9, 0x75, 0x9a, 0x3b, + 0xdd, 0xb6, 0xb8, 0x53, 0xf0, 0x58, 0x6b, 0xf6, 0x04, 0x9f, 0xd4, 0xda, 0x4f, 0xfb, 0x4f, 0xda, + 0xdc, 0xc5, 0xb5, 0xdb, 0xee, 0x7d, 0xae, 0x64, 0xb9, 0x97, 0xb7, 0x7d, 0xd8, 0xd4, 0x90, 0x4b, + 0x56, 0xa0, 0xd8, 0xfe, 0xac, 0xdd, 0x3a, 0x22, 0x36, 0x59, 0x07, 0xd8, 0x6d, 0x37, 0xbb, 0xdd, + 0x7e, 0x0b, 0xd9, 0x66, 0x81, 0x31, 0xa8, 0xb7, 0xb4, 0x76, 0x73, 0xd8, 0xd6, 0x9b, 0xad, 0x56, + 0xff, 0xa8, 0x37, 0x54, 0x8a, 0x58, 0x63, 0xb3, 0x3b, 0x6c, 0x6b, 0x31, 0x88, 0xde, 0x07, 0xdb, + 0xd5, 0xfa, 0x87, 0x31, 0xa4, 0xbc, 0x53, 0x46, 0x93, 0x8c, 0xe6, 0x4a, 0xfd, 0xab, 0x3a, 0xd4, + 0xd3, 0x4b, 0x93, 0x7d, 0x0c, 0x25, 0xd3, 0x4c, 0xcd, 0xf1, 0xcd, 0x55, 0x4b, 0xf8, 0xc1, 0xae, + 0x19, 0x4d, 0x33, 0xff, 0x60, 0x77, 0xa2, 0x8d, 0xb4, 0xbe, 0xb4, 0x91, 0xa2, 0x6d, 0xf4, 0x43, + 0xd8, 0x10, 0x6f, 0xb9, 0x98, 0x46, 0x68, 0x8c, 0x8c, 0xc0, 0x4a, 0xef, 0x92, 0x16, 0x21, 0x77, + 0x05, 0x6e, 0x7f, 0x4d, 0xab, 0x8f, 0x53, 0x10, 0xf6, 0x7d, 0xa8, 0x1b, 0x64, 0xe7, 0xc6, 0xf9, + 0x73, 0xb2, 0x12, 0xd8, 0x44, 0x9c, 0x94, 0xbd, 0x66, 0xc8, 0x00, 0x5c, 0x88, 0xa6, 0xef, 0xcd, + 0x92, 0xcc, 0xf9, 0xd4, 0x59, 0x8e, 0xef, 0xcd, 0xa4, 0xbc, 0x55, 0x53, 0x4a, 0xb3, 0x8f, 0xa0, + 0x2a, 0x5a, 0x9e, 0x78, 0x12, 0xe2, 0x2d, 0xcb, 0x9b, 0x4d, 0x4a, 0xdd, 0xfe, 0x9a, 0x56, 0x19, + 0x27, 0x49, 0xf6, 0x08, 0x35, 0xb9, 0x44, 0x17, 0x2f, 0xca, 0x6b, 0x8d, 0x5a, 0x1b, 0xe5, 0x02, + 0x23, 0x4e, 0xb1, 0xf7, 0x00, 0xa8, 0x9d, 0x3c, 0x4f, 0x29, 0x15, 0x82, 0xe1, 0x7b, 0xb3, 0x28, + 0x4b, 0xd9, 0x8c, 0x12, 0x52, 0xf3, 0xb8, 0x1f, 0xa8, 0xbc, 0xdc, 0x3c, 0xf2, 0x05, 0x25, 0xcd, + 0xe3, 0x2e, 0xa4, 0xb8, 0x79, 0x3c, 0x1b, 0x2c, 0x35, 0x2f, 0xca, 0xc5, 0x9b, 0xc7, 0x33, 0x45, + 0xcd, 0xe3, 0x79, 0x2a, 0x8b, 0xcd, 0x8b, 0xb2, 0x50, 0xf3, 0x78, 0x8e, 0xef, 0x2f, 0xe9, 0xee, + 0xd5, 0x4b, 0x75, 0x77, 0x9c, 0xb6, 0xb4, 0xf6, 0xfe, 0x7d, 0xa8, 0x07, 0x27, 0xde, 0x99, 0xc4, + 0x40, 0x6a, 0x72, 0xee, 0xc1, 0x89, 0x77, 0x26, 0x73, 0x90, 0x5a, 0x20, 0x03, 0xb0, 0xb5, 0xbc, + 0x8b, 0x74, 0xd9, 0xbd, 0x2e, 0xb7, 0x96, 0x7a, 0xf8, 0xd4, 0xb6, 0xce, 0xb0, 0xb5, 0x46, 0x94, + 0xc0, 0x41, 0x49, 0xfc, 0x1e, 0x81, 0x08, 0x3b, 0x4a, 0x85, 0x13, 0x88, 0x9a, 0x20, 0xf6, 0x80, + 0x04, 0xb8, 0xb6, 0xe6, 0xae, 0x9c, 0x4d, 0x91, 0xd7, 0xd6, 0x91, 0x9b, 0xca, 0x58, 0xe5, 0xa4, + 0x22, 0x6b, 0xb2, 0x2b, 0x02, 0xeb, 0xcb, 0xb9, 0xe5, 0x8e, 0x2d, 0x11, 0xa0, 0x94, 0xda, 0x15, + 0x03, 0x81, 0x4b, 0x76, 0x45, 0x04, 0x89, 0xd7, 0x75, 0x9c, 0x9d, 0x2d, 0xae, 0x6b, 0x29, 0x33, + 0xad, 0xeb, 0x38, 0x6b, 0xbc, 0xa1, 0xe2, 0xbc, 0x57, 0x96, 0x36, 0x94, 0x94, 0x99, 0x6f, 0xa8, + 0x38, 0xf7, 0x23, 0x10, 0xab, 0x89, 0x0f, 0x6e, 0x2a, 0x8c, 0x89, 0xb7, 0x5a, 0x8c, 0x2e, 0x8c, + 0xe3, 0x14, 0xae, 0x55, 0xdf, 0x42, 0x5b, 0x41, 0x2c, 0x85, 0xab, 0xf2, 0x5a, 0xd5, 0x08, 0x13, + 0x6f, 0x25, 0x3f, 0x49, 0xaa, 0x7f, 0x9c, 0x87, 0xa2, 0x60, 0x3a, 0xec, 0x0a, 0x6c, 0x08, 0xde, + 0xb7, 0xdb, 0x1c, 0x36, 0x77, 0x9a, 0x03, 0xd4, 0x56, 0x18, 0xd4, 0x39, 0xf3, 0x8b, 0x61, 0x19, + 0x64, 0x88, 0xc4, 0xfd, 0x62, 0xd0, 0x3a, 0x32, 0x44, 0x91, 0x97, 0x3f, 0xae, 0x98, 0x65, 0x1b, + 0x50, 0xe1, 0x19, 0x39, 0x80, 0xee, 0xfa, 0x51, 0x2e, 0x9e, 0xce, 0x4b, 0x59, 0xf8, 0xd1, 0x47, + 0x21, 0xc9, 0xc2, 0x01, 0xc5, 0x38, 0x4b, 0x74, 0x36, 0xc2, 0xa0, 0x3e, 0xd4, 0x8e, 0x7a, 0xad, + 0xa4, 0x9e, 0x32, 0xdd, 0xcf, 0xe2, 0xc5, 0x3c, 0xed, 0xb4, 0x9f, 0x29, 0x80, 0x99, 0x78, 0x29, + 0x94, 0xae, 0xa0, 0xbe, 0x45, 0x85, 0x50, 0xb2, 0xca, 0x5e, 0x82, 0x2b, 0x83, 0xfd, 0xfe, 0x33, + 0x9d, 0x67, 0x8a, 0xbb, 0x50, 0x63, 0x5b, 0xa0, 0x48, 0x08, 0x5e, 0x7c, 0x1d, 0xab, 0x24, 0x68, + 0x44, 0x38, 0x50, 0x36, 0xe8, 0x70, 0x11, 0x61, 0x43, 0x2e, 0x80, 0x14, 0xec, 0x0a, 0xcf, 0xda, + 0xef, 0x1e, 0x1d, 0xf4, 0x06, 0xca, 0x26, 0x36, 0x82, 0x20, 0xbc, 0xe5, 0x2c, 0x2e, 0x26, 0x11, + 0x5b, 0x57, 0x48, 0x92, 0x21, 0xec, 0x59, 0x53, 0xeb, 0x75, 0x7a, 0x8f, 0x07, 0xca, 0x56, 0x5c, + 0x72, 0x5b, 0xd3, 0xfa, 0xda, 0x40, 0xb9, 0x1a, 0x03, 0x06, 0xc3, 0xe6, 0xf0, 0x68, 0xa0, 0x5c, + 0x8b, 0x5b, 0x79, 0xa8, 0xf5, 0x5b, 0xed, 0xc1, 0xa0, 0xdb, 0x19, 0x0c, 0x95, 0x97, 0xd8, 0x55, + 0xd8, 0x4c, 0x5a, 0x14, 0x11, 0x37, 0xa4, 0x86, 0x6a, 0x8f, 0xdb, 0x43, 0xe5, 0x7a, 0xdc, 0x8c, + 0x56, 0xbf, 0xdb, 0x6d, 0xd2, 0x31, 0xd8, 0x0d, 0x24, 0xa2, 0xf3, 0x41, 0xd1, 0x9b, 0x97, 0xb1, + 0x5d, 0x47, 0x3d, 0x19, 0x74, 0x53, 0x5a, 0x1a, 0x83, 0xf6, 0x4f, 0x8e, 0xda, 0xbd, 0x56, 0x5b, + 0x79, 0x25, 0x59, 0x1a, 0x31, 0xec, 0x56, 0xbc, 0x34, 0x62, 0xd0, 0xed, 0xb8, 0xce, 0x08, 0x34, + 0x50, 0xb6, 0xb1, 0x3c, 0xd1, 0x8e, 0x5e, 0xaf, 0xdd, 0x1a, 0x62, 0x5f, 0xef, 0xc4, 0xa3, 0x78, + 0x74, 0xf8, 0x58, 0x6b, 0xee, 0xb6, 0x15, 0x15, 0x21, 0x5a, 0xbb, 0xd7, 0x3c, 0x88, 0x66, 0xfb, + 0xd5, 0x9d, 0x2a, 0x3d, 0x45, 0x2d, 0xc4, 0xa5, 0xfa, 0x63, 0x60, 0xf2, 0x9b, 0xae, 0xe2, 0x71, + 0x33, 0x06, 0xb9, 0x89, 0xef, 0x4d, 0xa3, 0xdb, 0xee, 0xf8, 0x8d, 0xb6, 0xda, 0x6c, 0x3e, 0xa2, + 0xb3, 0xac, 0xe4, 0xf2, 0xab, 0x0c, 0x52, 0xff, 0x38, 0x03, 0xf5, 0xb4, 0xa8, 0xa4, 0xa7, 0x56, + 0x27, 0xba, 0xeb, 0x85, 0xfc, 0xd5, 0xac, 0x20, 0x7e, 0x6a, 0x75, 0xd2, 0xf3, 0x42, 0x7a, 0x36, + 0x8b, 0x4c, 0xc7, 0x58, 0xf2, 0xf1, 0x52, 0xe3, 0x34, 0xeb, 0xc0, 0x95, 0xd4, 0x93, 0xb7, 0xa9, + 0x37, 0xcb, 0x1a, 0xf1, 0x73, 0x96, 0x0b, 0xed, 0xd7, 0x58, 0xb0, 0xdc, 0x27, 0x71, 0x85, 0x39, + 0x97, 0x5c, 0x61, 0xde, 0x87, 0x5a, 0x4a, 0x32, 0x93, 0xc5, 0x3f, 0x49, 0xb7, 0xb4, 0x64, 0x4f, + 0x5e, 0xdc, 0x4c, 0xf5, 0x8f, 0x32, 0x50, 0x95, 0xe5, 0xf4, 0x77, 0x2e, 0x89, 0x2e, 0xb9, 0x88, + 0x6f, 0xdd, 0x36, 0xa3, 0xd7, 0xb2, 0x22, 0x50, 0x87, 0x9e, 0xe0, 0xe7, 0x3e, 0xd8, 0xbd, 0xd3, + 0x41, 0xdc, 0x1d, 0x19, 0x84, 0x26, 0x33, 0x5d, 0x7e, 0xdc, 0x7b, 0x82, 0x04, 0xe2, 0x9a, 0x4c, + 0x02, 0x51, 0x6f, 0x43, 0x79, 0xef, 0x34, 0x0a, 0x4f, 0x90, 0xdf, 0x8e, 0x2b, 0xf3, 0x1b, 0xd3, + 0xea, 0x9f, 0x66, 0xa0, 0x9e, 0xbc, 0x07, 0x42, 0x41, 0x8f, 0xfc, 0xa9, 0x64, 0xbe, 0x1c, 0xd6, + 0xcd, 0x51, 0xf2, 0x3a, 0xff, 0xba, 0xfc, 0x3a, 0xff, 0xab, 0xa2, 0xb0, 0xac, 0x2c, 0xcd, 0xe2, + 0xba, 0xc4, 0x7d, 0xec, 0x47, 0x50, 0xc5, 0xff, 0x9a, 0x35, 0xb1, 0x7c, 0xdf, 0x8a, 0x5e, 0x8d, + 0x5e, 0x22, 0x4e, 0x11, 0x91, 0x45, 0x62, 0x4d, 0x84, 0x62, 0xb4, 0xf2, 0xc9, 0x12, 0xc4, 0xab, + 0xff, 0x22, 0x07, 0x15, 0x49, 0xeb, 0xf9, 0x46, 0xcb, 0xef, 0x26, 0x94, 0x93, 0xc7, 0x30, 0xc4, + 0x25, 0xd8, 0x18, 0x90, 0x9a, 0xab, 0xec, 0xc2, 0x5c, 0x35, 0xa0, 0x28, 0xa2, 0x23, 0x85, 0xdf, + 0x33, 0x4a, 0xa6, 0x1d, 0x7b, 0xf9, 0x17, 0xb8, 0xde, 0xdf, 0x87, 0xaa, 0xe4, 0x95, 0x8b, 0x5e, + 0xd6, 0x59, 0xa4, 0xaf, 0x24, 0x1e, 0xba, 0x80, 0x5d, 0x85, 0xc2, 0xe4, 0x54, 0x37, 0x47, 0x91, + 0x9b, 0x33, 0x3f, 0x39, 0xdd, 0x1d, 0xd1, 0xd1, 0xc5, 0x24, 0x16, 0xf4, 0xdc, 0x57, 0x52, 0x9a, + 0x44, 0xe2, 0xfc, 0x1e, 0x14, 0x27, 0xa7, 0xfc, 0x86, 0x5d, 0x59, 0x0e, 0xf8, 0x49, 0x86, 0xbc, + 0x30, 0x39, 0xa5, 0xfb, 0x78, 0x9f, 0x82, 0xb2, 0xe0, 0x53, 0x0d, 0xc4, 0xc9, 0xe4, 0x62, 0xa3, + 0x36, 0xd2, 0xee, 0xd5, 0x80, 0xbd, 0x0b, 0x5b, 0x42, 0xf2, 0x1a, 0x81, 0xce, 0x43, 0xfe, 0xe9, + 0x7d, 0x15, 0xfe, 0x08, 0xdd, 0x26, 0xc7, 0x35, 0x83, 0x01, 0x61, 0x70, 0xb1, 0xaa, 0x50, 0x95, + 0xd6, 0x2e, 0x7f, 0xbc, 0xa6, 0xac, 0xa5, 0x60, 0xec, 0x13, 0xa8, 0x4e, 0x4e, 0xf9, 0x5a, 0x18, + 0x7a, 0x07, 0x96, 0x08, 0xe3, 0xde, 0x5a, 0x5c, 0x05, 0x14, 0xaa, 0x9b, 0xa2, 0x64, 0xef, 0x00, + 0xf3, 0xad, 0xd0, 0x72, 0xa9, 0x27, 0xa6, 0x65, 0x98, 0x8e, 0xed, 0x5a, 0xa4, 0x6c, 0x65, 0xb5, + 0xcd, 0x18, 0xb3, 0x2b, 0x10, 0xea, 0xbf, 0xca, 0x40, 0x3d, 0xd1, 0x7e, 0x71, 0x43, 0xb3, 0xfb, + 0xf2, 0x7b, 0xe9, 0x8d, 0x45, 0x05, 0x19, 0x49, 0x1e, 0x0c, 0x2f, 0x66, 0xfc, 0xe5, 0xd1, 0x55, + 0x2f, 0x10, 0xad, 0x72, 0xb9, 0x66, 0x57, 0xbe, 0xe6, 0xfc, 0x18, 0xb2, 0xc3, 0x8b, 0x19, 0xf7, + 0xb4, 0xa0, 0x0c, 0xe4, 0x56, 0x19, 0x97, 0x7e, 0x14, 0x9f, 0xf0, 0xa4, 0xfd, 0x39, 0x7f, 0x00, + 0xe0, 0x50, 0xeb, 0x1c, 0x34, 0xb5, 0xcf, 0x29, 0xf2, 0x84, 0xb4, 0x84, 0xbd, 0xbe, 0xd6, 0xee, + 0x3c, 0xee, 0x11, 0x20, 0x47, 0x7e, 0x98, 0xa4, 0x89, 0x4d, 0xd3, 0xdc, 0x3b, 0x95, 0x1f, 0x62, + 0xc9, 0xa4, 0x1e, 0x62, 0x49, 0xdf, 0x19, 0x5e, 0x5f, 0xbc, 0x33, 0xcc, 0xe2, 0x1d, 0x1d, 0xb3, + 0x07, 0xf6, 0x06, 0xe4, 0x26, 0xa7, 0xd6, 0x45, 0xda, 0xc4, 0x49, 0x6f, 0x46, 0x22, 0x50, 0x7f, + 0x91, 0x01, 0x96, 0x6a, 0x08, 0xd7, 0xba, 0xbf, 0x6b, 0x5b, 0x3e, 0x86, 0x86, 0x78, 0xa6, 0x93, + 0x53, 0x49, 0x3e, 0x5e, 0x31, 0xa4, 0x57, 0xbd, 0x24, 0xb2, 0x2f, 0x79, 0x24, 0x89, 0xbd, 0x0b, + 0xfc, 0x9d, 0x44, 0x5c, 0x20, 0x69, 0xa7, 0x86, 0xc4, 0x2b, 0xb4, 0x84, 0x26, 0x79, 0x18, 0x51, + 0x7e, 0xf0, 0x91, 0xbb, 0x87, 0x37, 0x92, 0x59, 0x23, 0xfe, 0xa1, 0xfe, 0x5e, 0x06, 0xae, 0xa4, + 0x17, 0xc4, 0x2f, 0xd7, 0xcb, 0xf4, 0xeb, 0x96, 0xd9, 0xc5, 0xd7, 0x2d, 0x57, 0xad, 0xa7, 0xdc, + 0xca, 0xf5, 0xf4, 0xff, 0x67, 0x60, 0x4b, 0x1a, 0xfd, 0xc4, 0x4e, 0xfa, 0x3b, 0x6a, 0x99, 0xf4, + 0xc8, 0x65, 0x2e, 0xf5, 0xc8, 0xa5, 0xfa, 0x87, 0x19, 0xb8, 0xb6, 0xd0, 0x12, 0xcd, 0xfa, 0x3b, + 0x6d, 0x4b, 0xfa, 0x31, 0x4c, 0x72, 0x51, 0xf3, 0x50, 0x47, 0x7e, 0xb3, 0x91, 0xa5, 0x5f, 0xb7, + 0xa4, 0xab, 0xdd, 0xff, 0x3a, 0xdd, 0x48, 0x33, 0xb9, 0xa8, 0xc4, 0x3e, 0x84, 0x4a, 0xa2, 0x31, + 0x45, 0x6f, 0x8d, 0xac, 0xbc, 0xe5, 0x24, 0xd3, 0xad, 0x64, 0xa3, 0xeb, 0xdf, 0x8c, 0x8d, 0x7e, + 0x02, 0xd5, 0xb8, 0xe0, 0x5d, 0x6b, 0x92, 0xf6, 0x46, 0x2c, 0xbc, 0x96, 0x95, 0xa2, 0x54, 0x3f, + 0x80, 0xcd, 0xa4, 0x17, 0x2d, 0xf1, 0xc2, 0xdb, 0x6d, 0xa8, 0xb8, 0xd6, 0x99, 0x1e, 0xbd, 0xff, + 0x26, 0x62, 0x76, 0x5c, 0xeb, 0x4c, 0x10, 0xa8, 0x7b, 0x32, 0xdf, 0x8b, 0x7f, 0x86, 0xc0, 0x31, + 0x53, 0xc1, 0x1f, 0x9e, 0x63, 0x46, 0x28, 0x2c, 0x4d, 0x9a, 0x98, 0xa2, 0x6b, 0x9d, 0xd1, 0x9a, + 0x3b, 0x13, 0xe5, 0x34, 0x4d, 0x53, 0x1c, 0x98, 0xaf, 0x7a, 0x37, 0xe9, 0x3a, 0x94, 0x66, 0x7e, + 0x6a, 0x66, 0x8b, 0x33, 0x9f, 0x57, 0x7b, 0x57, 0x44, 0x04, 0x5d, 0x76, 0xb8, 0xce, 0x63, 0x84, + 0xc4, 0xcf, 0x94, 0xe4, 0x92, 0x9f, 0x29, 0xf9, 0x50, 0xb0, 0x3c, 0xdc, 0x7f, 0xa2, 0xe6, 0xf8, + 0x10, 0x3d, 0x73, 0xaf, 0x46, 0x87, 0xe8, 0xa4, 0x01, 0x5a, 0x5f, 0x8a, 0xa0, 0x24, 0xfc, 0x54, + 0x77, 0xa0, 0x22, 0x59, 0x76, 0xa8, 0x9a, 0x48, 0x5e, 0x91, 0x20, 0xfd, 0x12, 0x4d, 0x32, 0x40, + 0x5a, 0x25, 0x71, 0x8a, 0x04, 0xea, 0xef, 0x03, 0x40, 0x82, 0x4b, 0x29, 0x0c, 0x99, 0x05, 0x85, + 0xe1, 0x5b, 0x9d, 0xc8, 0x7f, 0x00, 0xf5, 0xb1, 0x37, 0xbb, 0xd0, 0x93, 0x1c, 0xd9, 0x95, 0x39, + 0xaa, 0x48, 0x35, 0x4c, 0xae, 0x08, 0x2d, 0x9f, 0xb4, 0xe6, 0x56, 0x9e, 0xb4, 0xbe, 0x0f, 0x45, + 0xee, 0xb8, 0x0f, 0xc4, 0x15, 0xb3, 0x97, 0x16, 0xfb, 0xf9, 0x40, 0xc4, 0xbe, 0x46, 0x74, 0xac, + 0x8d, 0x56, 0xb9, 0x78, 0x0c, 0x52, 0xbe, 0x70, 0x76, 0x6b, 0x39, 0x67, 0x44, 0xc6, 0x5f, 0x20, + 0x33, 0xe4, 0xa4, 0xa4, 0x24, 0x84, 0x53, 0xe1, 0x4d, 0x22, 0x25, 0xa1, 0x28, 0x2b, 0x09, 0xc3, + 0x29, 0xf7, 0x21, 0xa1, 0x92, 0xf0, 0x0e, 0x5c, 0x11, 0x31, 0xf8, 0x98, 0x01, 0x87, 0x93, 0xe8, + 0x79, 0xb8, 0x95, 0x78, 0x1e, 0x64, 0x38, 0x25, 0xed, 0x1b, 0xc9, 0x3f, 0x83, 0xad, 0xf1, 0x89, + 0xe1, 0x1e, 0x5b, 0x7a, 0x38, 0x72, 0x74, 0x7a, 0x8b, 0x5c, 0x9f, 0x1a, 0x33, 0xa1, 0xf6, 0xbc, + 0xb1, 0xd4, 0xd8, 0x16, 0x11, 0x0f, 0x47, 0x0e, 0x45, 0xe8, 0xc4, 0xe7, 0xf1, 0x9b, 0xe3, 0x45, + 0xf8, 0xc2, 0x69, 0x14, 0x2c, 0x9e, 0x46, 0x2d, 0x69, 0x33, 0x95, 0x65, 0x6d, 0xe6, 0xc6, 0x5f, + 0xe4, 0xa0, 0x20, 0x62, 0x01, 0xef, 0x43, 0xce, 0xf4, 0xbd, 0x59, 0x1c, 0xb2, 0xb7, 0x42, 0xbb, + 0xa0, 0x9f, 0x64, 0x42, 0x45, 0xe4, 0x01, 0x14, 0x0c, 0xd3, 0xd4, 0x27, 0xa7, 0xe9, 0x13, 0xa3, + 0x05, 0x41, 0xbf, 0xbf, 0xa6, 0xe5, 0x0d, 0x92, 0xf8, 0x1f, 0x43, 0x19, 0xe9, 0x93, 0xf8, 0xab, + 0xca, 0xb2, 0xfa, 0x12, 0x89, 0xe4, 0xfd, 0x35, 0xad, 0x64, 0x44, 0xe2, 0xf9, 0x07, 0x69, 0xdf, + 0x1b, 0x97, 0x97, 0x37, 0x96, 0xb2, 0x5e, 0xe6, 0x85, 0xfb, 0x75, 0xe0, 0xce, 0x98, 0x98, 0xdb, + 0xe4, 0xe5, 0xc3, 0x89, 0x25, 0xde, 0xb4, 0xbf, 0xa6, 0xf1, 0x3d, 0x17, 0xf1, 0xaa, 0x0f, 0x23, + 0xbf, 0x58, 0xfc, 0xc3, 0x17, 0x2b, 0x46, 0x06, 0x79, 0x45, 0xec, 0x1c, 0x23, 0xc6, 0x81, 0xd9, + 0x4c, 0x33, 0x0a, 0xdb, 0x29, 0x2e, 0x65, 0x8b, 0x39, 0x12, 0x65, 0x8b, 0xd9, 0xd3, 0x27, 0x50, + 0x21, 0x17, 0x95, 0xc8, 0x57, 0x5a, 0x1a, 0xda, 0x84, 0xa1, 0x90, 0xe3, 0x3d, 0x61, 0x2f, 0xad, + 0xa8, 0x9f, 0xbe, 0x25, 0xfb, 0x36, 0x6f, 0xae, 0x1c, 0x28, 0x2d, 0x76, 0x73, 0xf2, 0xce, 0x6a, + 0x3c, 0x0f, 0xdb, 0x81, 0xaa, 0x21, 0x49, 0x1a, 0xe1, 0xe8, 0xbc, 0xb9, 0x62, 0x9e, 0x62, 0x1a, + 0x2a, 0x43, 0x4a, 0x27, 0x07, 0x70, 0x37, 0x34, 0xb8, 0xb6, 0x7a, 0x29, 0xcb, 0x91, 0x24, 0x39, + 0x1e, 0x49, 0xa2, 0xa6, 0x9f, 0x68, 0x49, 0xdf, 0x93, 0x95, 0xe2, 0x4a, 0x7e, 0x84, 0x36, 0xb2, + 0xbc, 0x79, 0x2b, 0x50, 0x8c, 0x1e, 0x36, 0xa6, 0xb0, 0xd8, 0x56, 0xff, 0xf0, 0x73, 0x25, 0x83, + 0xe0, 0x4e, 0x6f, 0x30, 0x6c, 0xf6, 0xc4, 0xf1, 0x6a, 0xa7, 0x27, 0x8e, 0x57, 0xd5, 0x7f, 0x9b, + 0x85, 0x72, 0xec, 0x1e, 0xfe, 0xee, 0x86, 0x71, 0x6c, 0x71, 0x66, 0x65, 0x8b, 0x73, 0x41, 0x53, + 0x93, 0xdf, 0x3d, 0xd9, 0x48, 0xeb, 0x43, 0xc1, 0xf2, 0xfd, 0xbb, 0xfc, 0x37, 0xbc, 0x7f, 0x27, + 0x47, 0x26, 0x16, 0xd2, 0x91, 0x89, 0x0b, 0x8f, 0x5b, 0x17, 0x29, 0x4c, 0x45, 0x7e, 0xdc, 0xfa, + 0xd2, 0xf8, 0x94, 0xd2, 0xe5, 0xf1, 0x29, 0xf4, 0xbb, 0x73, 0x4f, 0x6d, 0xeb, 0x4c, 0x04, 0xe8, + 0x89, 0x54, 0x5a, 0x7c, 0xc0, 0x0b, 0xc4, 0xc7, 0x37, 0x60, 0x45, 0xec, 0x21, 0x6c, 0x4d, 0x4e, + 0xe3, 0x87, 0x3c, 0x13, 0x03, 0xab, 0x4a, 0xdd, 0x58, 0x89, 0x53, 0x7f, 0x37, 0x03, 0x90, 0xf8, + 0x50, 0x7f, 0x69, 0x07, 0x8f, 0x64, 0x43, 0x67, 0xbf, 0xc6, 0x86, 0x7e, 0xc1, 0xdb, 0x20, 0xea, + 0x97, 0x50, 0x8e, 0xbd, 0xe6, 0xdf, 0x7d, 0x8d, 0x7d, 0xab, 0x2a, 0x7f, 0x33, 0x72, 0x76, 0xc5, + 0x6e, 0xe7, 0x5f, 0x76, 0x2c, 0x52, 0xd5, 0x67, 0x5f, 0x50, 0xfd, 0x39, 0xf7, 0x38, 0xc5, 0x95, + 0xff, 0x8a, 0x37, 0x96, 0xbc, 0xe6, 0x73, 0xa9, 0x35, 0xaf, 0xce, 0x85, 0xdb, 0xec, 0x97, 0xaf, + 0xfa, 0x5b, 0x75, 0xf8, 0x6f, 0x32, 0x91, 0x6f, 0x27, 0x7e, 0x1e, 0xf5, 0x52, 0x45, 0x6b, 0xb5, + 0x7b, 0xea, 0xdb, 0x54, 0xf7, 0xb5, 0xd6, 0x66, 0xee, 0xeb, 0xac, 0xcd, 0x37, 0x20, 0xcf, 0x05, + 0x42, 0xfe, 0x32, 0x4b, 0x93, 0xe3, 0x5f, 0xf8, 0x83, 0x02, 0xaa, 0x2a, 0x14, 0x4b, 0xde, 0xdf, + 0xad, 0xa8, 0xdc, 0xe8, 0xc7, 0x10, 0x28, 0x88, 0xfa, 0xb7, 0x33, 0x9c, 0xbb, 0x7e, 0xd7, 0x31, + 0xf9, 0x95, 0x99, 0x9b, 0xff, 0x74, 0x1d, 0x6a, 0xa9, 0x03, 0xb3, 0xef, 0xd0, 0x98, 0x95, 0xdc, + 0x3c, 0xbb, 0x9a, 0x9b, 0x7f, 0x97, 0x57, 0xaf, 0xfe, 0xb7, 0x48, 0x80, 0x54, 0x8c, 0x59, 0x29, + 0x1d, 0x63, 0x86, 0xdc, 0xb4, 0x9a, 0xd2, 0xca, 0x57, 0xe9, 0xef, 0x99, 0x95, 0xfa, 0xfb, 0xad, + 0xf8, 0x57, 0xd6, 0x3a, 0xbb, 0xdc, 0xb0, 0xac, 0x69, 0x12, 0x84, 0x7d, 0x0a, 0xd7, 0xb9, 0x56, + 0x23, 0x7e, 0xe9, 0xcc, 0x9b, 0xe8, 0xf1, 0x6f, 0xb0, 0x89, 0xb8, 0xb9, 0x6b, 0x9c, 0x80, 0xff, + 0xda, 0xc4, 0xa4, 0x19, 0x61, 0xd5, 0x0e, 0xd4, 0x52, 0xa7, 0x97, 0xd2, 0xef, 0x39, 0x66, 0xe4, + 0xdf, 0x73, 0x64, 0xdb, 0x90, 0x3f, 0x3b, 0xb1, 0x7c, 0x6b, 0xc5, 0xeb, 0x90, 0x1c, 0xa1, 0x7e, + 0x1f, 0xaa, 0x72, 0x24, 0x05, 0x7b, 0x1b, 0xf2, 0x76, 0x68, 0x4d, 0x23, 0xdb, 0xea, 0xda, 0x72, + 0xb0, 0x05, 0x19, 0xd2, 0x9c, 0x48, 0xfd, 0x79, 0x06, 0x94, 0x45, 0x9c, 0xf4, 0xa3, 0x93, 0x99, + 0x4b, 0x7e, 0x74, 0x72, 0x3d, 0xd5, 0xc8, 0x55, 0xbf, 0x1b, 0x19, 0xbf, 0x50, 0x97, 0xbb, 0xe4, + 0x85, 0x3a, 0xf6, 0x3a, 0x94, 0x7c, 0x8b, 0x7e, 0xd1, 0xcf, 0x5c, 0x11, 0xcb, 0x1c, 0xe3, 0xd4, + 0xdf, 0xc9, 0x40, 0x51, 0x84, 0x7d, 0xac, 0x34, 0x76, 0xdf, 0x84, 0x22, 0xff, 0x75, 0xbf, 0xc8, + 0xf8, 0x5f, 0x8a, 0x83, 0x8c, 0xf0, 0xec, 0x16, 0x0f, 0x86, 0x49, 0x1b, 0xbf, 0x87, 0x8e, 0xe1, + 0x6a, 0x04, 0x17, 0x3f, 0x22, 0x63, 0x4c, 0xc5, 0x35, 0x42, 0xfe, 0x70, 0x08, 0x10, 0x88, 0xdf, + 0x18, 0xfc, 0x01, 0x14, 0x45, 0x58, 0xc9, 0xca, 0xa6, 0xbc, 0xe8, 0xd7, 0xdf, 0xb6, 0x01, 0x92, + 0x38, 0x93, 0x55, 0x25, 0xa8, 0xf7, 0xa1, 0x14, 0x85, 0x96, 0xe0, 0xfa, 0x4b, 0xaa, 0x16, 0xb1, + 0xea, 0x72, 0x63, 0x1c, 0xf1, 0x84, 0x72, 0xd7, 0x1b, 0x9f, 0x92, 0x57, 0xed, 0x5d, 0xa0, 0x18, + 0xfe, 0xe1, 0xd2, 0x0b, 0x2b, 0xe9, 0xe7, 0xaa, 0x63, 0x22, 0x76, 0x1f, 0x62, 0x76, 0xfc, 0x22, + 0x6b, 0x59, 0x6d, 0x46, 0x77, 0x49, 0x68, 0x95, 0x3d, 0x12, 0xde, 0xa3, 0x2e, 0xbd, 0x85, 0x94, + 0x72, 0xd8, 0xa4, 0xda, 0xa4, 0x49, 0x64, 0x6a, 0x1d, 0xaa, 0xf2, 0x79, 0xb8, 0xda, 0x84, 0xcd, + 0x03, 0x2b, 0x34, 0x90, 0x67, 0x45, 0x4f, 0x55, 0xf0, 0xf5, 0x8b, 0x1f, 0xe9, 0xf5, 0xbb, 0x48, + 0xa7, 0x71, 0x22, 0xf5, 0xe7, 0x39, 0x50, 0x16, 0x71, 0x5f, 0x77, 0xaf, 0xe6, 0x36, 0x54, 0x3c, + 0x5a, 0x17, 0xa9, 0x9f, 0x09, 0xe2, 0x20, 0x29, 0x60, 0x35, 0xf5, 0xa6, 0x7d, 0xc9, 0x0e, 0xf6, + 0xf9, 0xab, 0xf6, 0x2f, 0xf1, 0xe7, 0x41, 0x1c, 0x6f, 0x4c, 0xcb, 0xba, 0x4a, 0xaf, 0x81, 0x74, + 0xbd, 0x31, 0x5d, 0xd7, 0x11, 0x06, 0x37, 0x0f, 0xd2, 0xaa, 0x6a, 0x25, 0x61, 0x65, 0xd3, 0xa1, + 0x81, 0x08, 0x63, 0x0d, 0x03, 0x71, 0x01, 0xaa, 0xc4, 0x01, 0xc3, 0x20, 0x7a, 0xd7, 0x77, 0x2c, + 0x7e, 0xd3, 0x26, 0x4b, 0xef, 0xfa, 0xb6, 0x5c, 0xba, 0xad, 0x43, 0x3f, 0xc1, 0x34, 0x16, 0x3f, + 0x91, 0x25, 0xde, 0x51, 0x46, 0xd4, 0xab, 0xfc, 0x57, 0x7f, 0x7c, 0x2b, 0x08, 0xf8, 0x43, 0x60, + 0x65, 0xf1, 0x16, 0x92, 0x00, 0xc6, 0x2f, 0x8e, 0x89, 0xdf, 0x5c, 0x42, 0x12, 0x10, 0x2f, 0x8e, + 0xf1, 0x5f, 0x5c, 0x42, 0x82, 0xeb, 0x50, 0xfa, 0xca, 0x73, 0x2d, 0x32, 0xdc, 0x2b, 0xd4, 0xaa, + 0x22, 0xa6, 0x0f, 0x8c, 0x99, 0xfa, 0xe7, 0x19, 0xd8, 0x5a, 0x1c, 0x55, 0x5a, 0x30, 0x55, 0x28, + 0xb5, 0xfa, 0x5d, 0xbd, 0xd7, 0x3c, 0x68, 0x2b, 0x6b, 0x6c, 0x03, 0x2a, 0xfd, 0x9d, 0x1f, 0xb7, + 0x5b, 0x43, 0x0e, 0xc8, 0xd0, 0x3d, 0xd1, 0x81, 0xbe, 0xdf, 0xd9, 0xdd, 0x6d, 0xf7, 0xb8, 0x95, + 0xd2, 0xdf, 0xf9, 0xb1, 0xde, 0xed, 0xb7, 0xf8, 0x4f, 0xb4, 0x44, 0xa7, 0xef, 0x03, 0x25, 0x47, + 0x27, 0xde, 0x14, 0x13, 0x8a, 0xc9, 0x3c, 0x0f, 0x79, 0x7c, 0x36, 0xd0, 0x5b, 0xbd, 0xa1, 0x52, + 0xc0, 0x54, 0xef, 0xa8, 0xdb, 0xa5, 0x14, 0xc5, 0x36, 0xb5, 0xfa, 0x07, 0x87, 0x5a, 0x7b, 0x30, + 0xd0, 0x07, 0x9d, 0x9f, 0xb6, 0x95, 0x12, 0xd5, 0xac, 0x75, 0x1e, 0x77, 0x7a, 0x1c, 0x50, 0x66, + 0x45, 0xc8, 0x1e, 0x74, 0x7a, 0xfc, 0x7e, 0xec, 0x41, 0xf3, 0x33, 0xa5, 0x82, 0x1f, 0x83, 0xa3, + 0x03, 0xa5, 0x7a, 0xff, 0x0e, 0x54, 0xe5, 0xdf, 0x39, 0xa3, 0x28, 0x47, 0xcf, 0xb5, 0xf8, 0xeb, + 0xbf, 0xdd, 0xaf, 0x3e, 0x50, 0x32, 0xf7, 0x7f, 0x53, 0xfa, 0xfd, 0x08, 0xa2, 0x11, 0x87, 0x01, + 0x74, 0x1b, 0x90, 0xdf, 0x36, 0x24, 0xd7, 0x3f, 0x5d, 0x4e, 0xdc, 0x6f, 0x0e, 0xf6, 0xf9, 0x31, + 0x81, 0xc0, 0x10, 0x20, 0x9b, 0xbc, 0x1a, 0x4b, 0x97, 0x7d, 0xe9, 0x33, 0x3e, 0x6c, 0xcf, 0xd3, + 0x3d, 0xcc, 0xce, 0x00, 0x3b, 0xa7, 0x40, 0x15, 0xbf, 0x62, 0x5c, 0xf1, 0xbe, 0x0a, 0x15, 0xe9, + 0xa1, 0x6f, 0xaa, 0xc3, 0x08, 0x4e, 0xc4, 0x43, 0xb4, 0x68, 0x6e, 0x2a, 0x99, 0xfb, 0xaf, 0xa3, + 0xc4, 0x90, 0x9f, 0xd9, 0x06, 0x28, 0xf4, 0x3c, 0x7f, 0x6a, 0x38, 0x82, 0xce, 0x9a, 0x07, 0x48, + 0xf7, 0x2e, 0x5c, 0x5d, 0xf9, 0x68, 0x38, 0x45, 0xea, 0xda, 0xd3, 0x99, 0x63, 0xf1, 0x60, 0xd3, + 0xfd, 0x8b, 0x91, 0x6f, 0x9b, 0x4a, 0xe6, 0xfe, 0x27, 0xd1, 0x95, 0xb4, 0xa8, 0xee, 0x6e, 0xbf, + 0xb9, 0xcb, 0x27, 0x37, 0xbe, 0x8c, 0x3c, 0xdc, 0xe1, 0x8f, 0xcc, 0x6a, 0xed, 0xc1, 0x51, 0x77, + 0x28, 0x2e, 0x3e, 0xdf, 0xff, 0x11, 0x34, 0x2e, 0x8b, 0xba, 0xc4, 0x16, 0xb5, 0xf6, 0x9b, 0x14, + 0xd9, 0x8a, 0x93, 0xd9, 0xd7, 0x79, 0x2a, 0xc3, 0x03, 0x83, 0xbb, 0x6d, 0x8a, 0xc8, 0xb8, 0xff, + 0xb3, 0x8c, 0xc4, 0xc2, 0xa2, 0xc8, 0xb9, 0x18, 0x20, 0x66, 0x49, 0x06, 0x69, 0x96, 0x61, 0x2a, + 0x19, 0x76, 0x0d, 0x58, 0x0a, 0xd4, 0xf5, 0xc6, 0x86, 0xa3, 0xac, 0x53, 0xec, 0x45, 0x04, 0xa7, + 0xf8, 0x66, 0x25, 0xcb, 0x5e, 0x81, 0xeb, 0x31, 0xac, 0xeb, 0x9d, 0x1d, 0xfa, 0x36, 0xda, 0xda, + 0x17, 0x1c, 0x9d, 0xdb, 0xf9, 0xe1, 0x9f, 0xfd, 0xe2, 0x56, 0xe6, 0xdf, 0xff, 0xe2, 0x56, 0xe6, + 0xbf, 0xfe, 0xe2, 0xd6, 0xda, 0xcf, 0xff, 0xdb, 0xad, 0xcc, 0x4f, 0xe5, 0xdf, 0x79, 0x9f, 0x1a, + 0xa1, 0x6f, 0x9f, 0xf3, 0x4d, 0x13, 0x25, 0x5c, 0xeb, 0xdd, 0xd9, 0xe9, 0xf1, 0xbb, 0xb3, 0xd1, + 0xbb, 0xc8, 0x99, 0x46, 0x05, 0xfa, 0x45, 0xf7, 0x47, 0xff, 0x2b, 0x00, 0x00, 0xff, 0xff, 0x7a, + 0xdd, 0xdd, 0xf7, 0x31, 0x7e, 0x00, 0x00, } func (m *Type) Marshal() (dAtA []byte, err error) { @@ -14394,6 +14497,83 @@ func (m *SubscriptionMeta) MarshalToSizedBuffer(dAtA []byte) (int, error) { return len(dAtA) - i, nil } +func (m *IndexScanInfo) 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 *IndexScanInfo) MarshalTo(dAtA []byte) (int, error) { + size := m.ProtoSize() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *IndexScanInfo) 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.IndexTableName) > 0 { + i -= len(m.IndexTableName) + copy(dAtA[i:], m.IndexTableName) + i = encodeVarintPlan(dAtA, i, uint64(len(m.IndexTableName))) + i-- + dAtA[i] = 0x32 + } + if m.IsUnique { + i-- + if m.IsUnique { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i-- + dAtA[i] = 0x28 + } + if len(m.Parts) > 0 { + for iNdEx := len(m.Parts) - 1; iNdEx >= 0; iNdEx-- { + i -= len(m.Parts[iNdEx]) + copy(dAtA[i:], m.Parts[iNdEx]) + i = encodeVarintPlan(dAtA, i, uint64(len(m.Parts[iNdEx]))) + i-- + dAtA[i] = 0x22 + } + } + if len(m.BelongToTable) > 0 { + i -= len(m.BelongToTable) + copy(dAtA[i:], m.BelongToTable) + i = encodeVarintPlan(dAtA, i, uint64(len(m.BelongToTable))) + i-- + dAtA[i] = 0x1a + } + if len(m.IndexName) > 0 { + i -= len(m.IndexName) + copy(dAtA[i:], m.IndexName) + i = encodeVarintPlan(dAtA, i, uint64(len(m.IndexName))) + i-- + dAtA[i] = 0x12 + } + if m.IsIndexScan { + i-- + if m.IsIndexScan { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i-- + dAtA[i] = 0x8 + } + return len(dAtA) - i, nil +} + func (m *Function) Marshal() (dAtA []byte, err error) { size := m.ProtoSize() dAtA = make([]byte, size) @@ -18789,6 +18969,18 @@ func (m *Node) MarshalToSizedBuffer(dAtA []byte) (int, error) { i-- dAtA[i] = 0xb2 } + { + size, err := m.IndexScanInfo.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintPlan(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x1 + i-- + dAtA[i] = 0xaa if m.RowsetData != nil { { size, err := m.RowsetData.MarshalToSizedBuffer(dAtA[:i]) @@ -18998,21 +19190,21 @@ func (m *Node) MarshalToSizedBuffer(dAtA []byte) (int, error) { } } if len(m.Children) > 0 { - dAtA112 := make([]byte, len(m.Children)*10) - var j111 int + dAtA113 := make([]byte, len(m.Children)*10) + var j112 int for _, num1 := range m.Children { num := uint64(num1) for num >= 1<<7 { - dAtA112[j111] = uint8(uint64(num)&0x7f | 0x80) + dAtA113[j112] = uint8(uint64(num)&0x7f | 0x80) num >>= 7 - j111++ + j112++ } - dAtA112[j111] = uint8(num) - j111++ + dAtA113[j112] = uint8(num) + j112++ } - i -= j111 - copy(dAtA[i:], dAtA112[:j111]) - i = encodeVarintPlan(dAtA, i, uint64(j111)) + i -= j112 + copy(dAtA[i:], dAtA113[:j112]) + i = encodeVarintPlan(dAtA, i, uint64(j112)) i-- dAtA[i] = 0x22 } @@ -19348,20 +19540,20 @@ func (m *LockTarget) MarshalToSizedBuffer(dAtA []byte) (int, error) { dAtA[i] = 0x48 } if len(m.PartitionTableIds) > 0 { - dAtA118 := make([]byte, len(m.PartitionTableIds)*10) - var j117 int + dAtA119 := make([]byte, len(m.PartitionTableIds)*10) + var j118 int for _, num := range m.PartitionTableIds { for num >= 1<<7 { - dAtA118[j117] = uint8(uint64(num)&0x7f | 0x80) + dAtA119[j118] = uint8(uint64(num)&0x7f | 0x80) num >>= 7 - j117++ + j118++ } - dAtA118[j117] = uint8(num) - j117++ + dAtA119[j118] = uint8(num) + j118++ } - i -= j117 - copy(dAtA[i:], dAtA118[:j117]) - i = encodeVarintPlan(dAtA, i, uint64(j117)) + i -= j118 + copy(dAtA[i:], dAtA119[:j118]) + i = encodeVarintPlan(dAtA, i, uint64(j118)) i-- dAtA[i] = 0x42 } @@ -19468,21 +19660,21 @@ func (m *PreInsertUkCtx) MarshalToSizedBuffer(dAtA []byte) (int, error) { dAtA[i] = 0x10 } if len(m.Columns) > 0 { - dAtA123 := make([]byte, len(m.Columns)*10) - var j122 int + dAtA124 := make([]byte, len(m.Columns)*10) + var j123 int for _, num1 := range m.Columns { 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] = 0xa } @@ -19514,21 +19706,21 @@ func (m *PreDeleteCtx) MarshalToSizedBuffer(dAtA []byte) (int, error) { copy(dAtA[i:], m.XXX_unrecognized) } if len(m.Idx) > 0 { - dAtA125 := make([]byte, len(m.Idx)*10) - var j124 int + dAtA126 := make([]byte, len(m.Idx)*10) + var j125 int for _, num1 := range m.Idx { num := uint64(num1) for num >= 1<<7 { - dAtA125[j124] = uint8(uint64(num)&0x7f | 0x80) + dAtA126[j125] = uint8(uint64(num)&0x7f | 0x80) num >>= 7 - j124++ + j125++ } - dAtA125[j124] = uint8(num) - j124++ + dAtA126[j125] = uint8(num) + j125++ } - i -= j124 - copy(dAtA[i:], dAtA125[:j124]) - i = encodeVarintPlan(dAtA, i, uint64(j124)) + i -= j125 + copy(dAtA[i:], dAtA126[:j125]) + i = encodeVarintPlan(dAtA, i, uint64(j125)) i-- dAtA[i] = 0xa } @@ -19714,21 +19906,21 @@ func (m *IdList) MarshalToSizedBuffer(dAtA []byte) (int, error) { copy(dAtA[i:], m.XXX_unrecognized) } if len(m.List) > 0 { - dAtA132 := make([]byte, len(m.List)*10) - var j131 int + dAtA133 := make([]byte, len(m.List)*10) + var j132 int for _, num1 := range m.List { num := uint64(num1) for num >= 1<<7 { - dAtA132[j131] = uint8(uint64(num)&0x7f | 0x80) + dAtA133[j132] = uint8(uint64(num)&0x7f | 0x80) num >>= 7 - j131++ + j132++ } - dAtA132[j131] = uint8(num) - j131++ + dAtA133[j132] = uint8(num) + j132++ } - i -= j131 - copy(dAtA[i:], dAtA132[:j131]) - i = encodeVarintPlan(dAtA, i, uint64(j131)) + i -= j132 + copy(dAtA[i:], dAtA133[:j132]) + i = encodeVarintPlan(dAtA, i, uint64(j132)) i-- dAtA[i] = 0xa } @@ -19847,20 +20039,20 @@ func (m *DeleteCtx) MarshalToSizedBuffer(dAtA []byte) (int, error) { } } if len(m.PartitionTableIds) > 0 { - dAtA136 := make([]byte, len(m.PartitionTableIds)*10) - var j135 int + dAtA137 := make([]byte, len(m.PartitionTableIds)*10) + var j136 int for _, num := range m.PartitionTableIds { 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] = 0x32 } @@ -20165,21 +20357,21 @@ func (m *Query) MarshalToSizedBuffer(dAtA []byte) (int, error) { } } if len(m.Steps) > 0 { - dAtA141 := make([]byte, len(m.Steps)*10) - var j140 int + dAtA142 := make([]byte, len(m.Steps)*10) + var j141 int for _, num1 := range m.Steps { num := uint64(num1) for num >= 1<<7 { - dAtA141[j140] = uint8(uint64(num)&0x7f | 0x80) + dAtA142[j141] = uint8(uint64(num)&0x7f | 0x80) num >>= 7 - j140++ + j141++ } - dAtA141[j140] = uint8(num) - j140++ + dAtA142[j141] = uint8(num) + j141++ } - i -= j140 - copy(dAtA[i:], dAtA141[:j140]) - i = encodeVarintPlan(dAtA, i, uint64(j140)) + i -= j141 + copy(dAtA[i:], dAtA142[:j141]) + i = encodeVarintPlan(dAtA, i, uint64(j141)) i-- dAtA[i] = 0x12 } @@ -22706,20 +22898,20 @@ func (m *DropTable) MarshalToSizedBuffer(dAtA []byte) (int, error) { copy(dAtA[i:], m.XXX_unrecognized) } if len(m.FkChildTblsReferToMe) > 0 { - dAtA197 := make([]byte, len(m.FkChildTblsReferToMe)*10) - var j196 int + dAtA198 := make([]byte, len(m.FkChildTblsReferToMe)*10) + var j197 int for _, num := range m.FkChildTblsReferToMe { for num >= 1<<7 { - dAtA197[j196] = uint8(uint64(num)&0x7f | 0x80) + dAtA198[j197] = uint8(uint64(num)&0x7f | 0x80) num >>= 7 - j196++ + j197++ } - dAtA197[j196] = uint8(num) - j196++ + dAtA198[j197] = uint8(num) + j197++ } - i -= j196 - copy(dAtA[i:], dAtA197[:j196]) - i = encodeVarintPlan(dAtA, i, uint64(j196)) + i -= j197 + copy(dAtA[i:], dAtA198[:j197]) + i = encodeVarintPlan(dAtA, i, uint64(j197)) i-- dAtA[i] = 0x62 } @@ -22764,20 +22956,20 @@ func (m *DropTable) MarshalToSizedBuffer(dAtA []byte) (int, error) { } } if len(m.ForeignTbl) > 0 { - dAtA200 := make([]byte, len(m.ForeignTbl)*10) - var j199 int + dAtA201 := make([]byte, len(m.ForeignTbl)*10) + var j200 int for _, num := range m.ForeignTbl { for num >= 1<<7 { - dAtA200[j199] = uint8(uint64(num)&0x7f | 0x80) + dAtA201[j200] = uint8(uint64(num)&0x7f | 0x80) num >>= 7 - j199++ + j200++ } - dAtA200[j199] = uint8(num) - j199++ + dAtA201[j200] = uint8(num) + j200++ } - i -= j199 - copy(dAtA[i:], dAtA200[:j199]) - i = encodeVarintPlan(dAtA, i, uint64(j199)) + i -= j200 + copy(dAtA[i:], dAtA201[:j200]) + i = encodeVarintPlan(dAtA, i, uint64(j200)) i-- dAtA[i] = 0x3a } @@ -23330,20 +23522,20 @@ func (m *TruncateTable) MarshalToSizedBuffer(dAtA []byte) (int, error) { dAtA[i] = 0x40 } if len(m.ForeignTbl) > 0 { - dAtA209 := make([]byte, len(m.ForeignTbl)*10) - var j208 int + dAtA210 := make([]byte, len(m.ForeignTbl)*10) + var j209 int for _, num := range m.ForeignTbl { for num >= 1<<7 { - dAtA209[j208] = uint8(uint64(num)&0x7f | 0x80) + dAtA210[j209] = uint8(uint64(num)&0x7f | 0x80) num >>= 7 - j208++ + j209++ } - dAtA209[j208] = uint8(num) - j208++ + dAtA210[j209] = uint8(num) + j209++ } - i -= j208 - copy(dAtA[i:], dAtA209[:j208]) - i = encodeVarintPlan(dAtA, i, uint64(j208)) + i -= j209 + copy(dAtA[i:], dAtA210[:j209]) + i = encodeVarintPlan(dAtA, i, uint64(j209)) i-- dAtA[i] = 0x3a } @@ -23429,20 +23621,20 @@ func (m *ClusterTable) MarshalToSizedBuffer(dAtA []byte) (int, error) { dAtA[i] = 0x18 } if len(m.AccountIDs) > 0 { - dAtA212 := make([]byte, len(m.AccountIDs)*10) - var j211 int + dAtA213 := make([]byte, len(m.AccountIDs)*10) + var j212 int for _, num := range m.AccountIDs { for num >= 1<<7 { - dAtA212[j211] = uint8(uint64(num)&0x7f | 0x80) + dAtA213[j212] = uint8(uint64(num)&0x7f | 0x80) num >>= 7 - j211++ + j212++ } - dAtA212[j211] = uint8(num) - j211++ + dAtA213[j212] = uint8(num) + j212++ } - i -= j211 - copy(dAtA[i:], dAtA212[:j211]) - i = encodeVarintPlan(dAtA, i, uint64(j211)) + i -= j212 + copy(dAtA[i:], dAtA213[:j212]) + i = encodeVarintPlan(dAtA, i, uint64(j212)) i-- dAtA[i] = 0x12 } @@ -23654,21 +23846,21 @@ func (m *Prepare) MarshalToSizedBuffer(dAtA []byte) (int, error) { copy(dAtA[i:], m.XXX_unrecognized) } if len(m.ParamTypes) > 0 { - dAtA216 := make([]byte, len(m.ParamTypes)*10) - var j215 int + dAtA217 := make([]byte, len(m.ParamTypes)*10) + var j216 int for _, num1 := range m.ParamTypes { num := uint64(num1) for num >= 1<<7 { - dAtA216[j215] = uint8(uint64(num)&0x7f | 0x80) + dAtA217[j216] = uint8(uint64(num)&0x7f | 0x80) num >>= 7 - j215++ + j216++ } - dAtA216[j215] = uint8(num) - j215++ + dAtA217[j216] = uint8(num) + j216++ } - i -= j215 - copy(dAtA[i:], dAtA216[:j215]) - i = encodeVarintPlan(dAtA, i, uint64(j215)) + i -= j216 + copy(dAtA[i:], dAtA217[:j216]) + i = encodeVarintPlan(dAtA, i, uint64(j216)) i-- dAtA[i] = 0x22 } @@ -23815,21 +24007,21 @@ func (m *OtherDCL) MarshalToSizedBuffer(dAtA []byte) (int, error) { copy(dAtA[i:], m.XXX_unrecognized) } if len(m.ParamTypes) > 0 { - dAtA219 := make([]byte, len(m.ParamTypes)*10) - var j218 int + dAtA220 := make([]byte, len(m.ParamTypes)*10) + var j219 int for _, num1 := range m.ParamTypes { num := uint64(num1) for num >= 1<<7 { - dAtA219[j218] = uint8(uint64(num)&0x7f | 0x80) + dAtA220[j219] = uint8(uint64(num)&0x7f | 0x80) num >>= 7 - j218++ + j219++ } - dAtA219[j218] = uint8(num) - j218++ + dAtA220[j219] = uint8(num) + j219++ } - i -= j218 - copy(dAtA[i:], dAtA219[:j218]) - i = encodeVarintPlan(dAtA, i, uint64(j218)) + i -= j219 + copy(dAtA[i:], dAtA220[:j219]) + i = encodeVarintPlan(dAtA, i, uint64(j219)) i-- dAtA[i] = 0xa } @@ -24662,6 +24854,42 @@ func (m *SubscriptionMeta) ProtoSize() (n int) { return n } +func (m *IndexScanInfo) ProtoSize() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.IsIndexScan { + n += 2 + } + l = len(m.IndexName) + if l > 0 { + n += 1 + l + sovPlan(uint64(l)) + } + l = len(m.BelongToTable) + if l > 0 { + n += 1 + l + sovPlan(uint64(l)) + } + if len(m.Parts) > 0 { + for _, s := range m.Parts { + l = len(s) + n += 1 + l + sovPlan(uint64(l)) + } + } + if m.IsUnique { + n += 2 + } + l = len(m.IndexTableName) + if l > 0 { + n += 1 + l + sovPlan(uint64(l)) + } + if m.XXX_unrecognized != nil { + n += len(m.XXX_unrecognized) + } + return n +} + func (m *Function) ProtoSize() (n int) { if m == nil { return 0 @@ -26440,6 +26668,8 @@ func (m *Node) ProtoSize() (n int) { l = m.RowsetData.ProtoSize() n += 2 + l + sovPlan(uint64(l)) } + l = m.IndexScanInfo.ProtoSize() + n += 2 + l + sovPlan(uint64(l)) l = len(m.ExtraOptions) if l > 0 { n += 2 + l + sovPlan(uint64(l)) @@ -31499,6 +31729,225 @@ func (m *SubscriptionMeta) Unmarshal(dAtA []byte) error { } return nil } +func (m *IndexScanInfo) 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: IndexScanInfo: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: IndexScanInfo: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field IsIndexScan", 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.IsIndexScan = bool(v != 0) + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field IndexName", 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.IndexName = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field BelongToTable", 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.BelongToTable = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Parts", 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.Parts = append(m.Parts, string(dAtA[iNdEx:postIndex])) + iNdEx = postIndex + case 5: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field IsUnique", 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.IsUnique = bool(v != 0) + case 6: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field IndexTableName", 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.IndexTableName = 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 *Function) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 @@ -42101,6 +42550,39 @@ func (m *Node) Unmarshal(dAtA []byte) error { return err } iNdEx = postIndex + case 21: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field IndexScanInfo", 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.IndexScanInfo.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex case 22: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field ExtraOptions", wireType) diff --git a/pkg/sql/plan/apply_indices.go b/pkg/sql/plan/apply_indices.go index dffd34d661be2..4dac70f2a8c74 100644 --- a/pkg/sql/plan/apply_indices.go +++ b/pkg/sql/plan/apply_indices.go @@ -16,6 +16,7 @@ package plan import ( "fmt" + "slices" "sort" "github.com/matrixorigin/matrixone/pkg/catalog" @@ -727,16 +728,27 @@ func (builder *QueryBuilder) tryIndexOnlyScan(idxDef *IndexDef, node *plan.Node, newFilterList = append(newFilterList, replaceColumnsForExpr(node.FilterList[idx], idxColMap)) } + // recod index table scan info + idxScanInfo := plan.IndexScanInfo{ + IsIndexScan: true, + IndexName: idxDef.IndexName, + BelongToTable: node.ObjRef.ObjName, + Parts: slices.Clone(idxDef.Parts), + IsUnique: idxDef.Unique, + IndexTableName: idxDef.IndexTableName, + } + idxTableNodeID := builder.appendNode(&plan.Node{ - NodeType: plan.Node_TABLE_SCAN, - TableDef: idxTableDef, - ObjRef: idxObjRef, - ParentObjRef: node.ObjRef, - FilterList: newFilterList, - Limit: node.Limit, - Offset: node.Offset, - BindingTags: []int32{idxTag}, - ScanSnapshot: node.ScanSnapshot, + NodeType: plan.Node_TABLE_SCAN, + TableDef: idxTableDef, + IndexScanInfo: idxScanInfo, + ObjRef: idxObjRef, + ParentObjRef: node.ObjRef, + FilterList: newFilterList, + Limit: node.Limit, + Offset: node.Offset, + BindingTags: []int32{idxTag}, + ScanSnapshot: node.ScanSnapshot, }, builder.ctxByNode[node.NodeId]) forceScanNodeStatsTP(idxTableNodeID, builder) @@ -783,14 +795,25 @@ func (builder *QueryBuilder) applyIndexJoin(idxDef *IndexDef, node *plan.Node, f idxFilter = builder.replaceNonEqualCondition(node.FilterList[filterIdx[0]], idxTag, idxTableDef, numParts) } + // recod index table scan info + idxScanInfo := plan.IndexScanInfo{ + IsIndexScan: true, + IndexName: idxDef.IndexName, + BelongToTable: node.ObjRef.ObjName, + Parts: slices.Clone(idxDef.Parts), + IsUnique: idxDef.Unique, + IndexTableName: idxDef.IndexTableName, + } + idxTableNode := &plan.Node{ - NodeType: plan.Node_TABLE_SCAN, - TableDef: idxTableDef, - ObjRef: idxObjRef, - ParentObjRef: DeepCopyObjectRef(node.ObjRef), - FilterList: []*plan.Expr{idxFilter}, - BindingTags: []int32{idxTag}, - ScanSnapshot: node.ScanSnapshot, + NodeType: plan.Node_TABLE_SCAN, + TableDef: idxTableDef, + ObjRef: idxObjRef, + IndexScanInfo: idxScanInfo, + ParentObjRef: DeepCopyObjectRef(node.ObjRef), + FilterList: []*plan.Expr{idxFilter}, + BindingTags: []int32{idxTag}, + ScanSnapshot: node.ScanSnapshot, } idxTableNodeID := builder.appendNode(idxTableNode, builder.ctxByNode[node.NodeId]) forceScanNodeStatsTP(idxTableNodeID, builder) @@ -1026,10 +1049,22 @@ func (builder *QueryBuilder) applyIndicesForJoins(nodeID int32, node *plan.Node, }, }, } + + // recod index table scan info + idxScanInfo := plan.IndexScanInfo{ + IsIndexScan: true, + IndexName: idxDef.IndexName, + BelongToTable: leftChild.ObjRef.ObjName, + Parts: slices.Clone(idxDef.Parts), + IsUnique: idxDef.Unique, + IndexTableName: idxDef.IndexTableName, + } + idxTableNodeID := builder.appendNode(&plan.Node{ NodeType: plan.Node_TABLE_SCAN, TableDef: idxTableDef, ObjRef: idxObjRef, + IndexScanInfo: idxScanInfo, ParentObjRef: DeepCopyObjectRef(leftChild.ObjRef), BindingTags: []int32{idxTag}, ScanSnapshot: leftChild.ScanSnapshot, diff --git a/pkg/sql/plan/deepcopy.go b/pkg/sql/plan/deepcopy.go index 5060870f7e480..c4cdb906d65c9 100644 --- a/pkg/sql/plan/deepcopy.go +++ b/pkg/sql/plan/deepcopy.go @@ -259,6 +259,15 @@ func DeepCopyNode(node *plan.Node) *plan.Node { newNode.ObjRef = DeepCopyObjectRef(node.ObjRef) newNode.ParentObjRef = DeepCopyObjectRef(node.ParentObjRef) + newNode.IndexScanInfo = plan.IndexScanInfo{ + IsIndexScan: node.IndexScanInfo.IsIndexScan, + IndexName: node.IndexScanInfo.IndexName, + BelongToTable: node.IndexScanInfo.BelongToTable, + Parts: slices.Clone(node.IndexScanInfo.Parts), + IsUnique: node.IndexScanInfo.IsUnique, + IndexTableName: node.IndexScanInfo.IndexTableName, + } + if node.WinSpecList != nil { newNode.WinSpecList = make([]*Expr, len(node.WinSpecList)) for i, w := range node.WinSpecList { diff --git a/pkg/sql/plan/explain/explain_node.go b/pkg/sql/plan/explain/explain_node.go index 59336b89a0995..bf8ec6ddbbe8a 100644 --- a/pkg/sql/plan/explain/explain_node.go +++ b/pkg/sql/plan/explain/explain_node.go @@ -44,6 +44,7 @@ func NewNodeDescriptionImpl(node *plan.Node) *NodeDescribeImpl { } const TableScan = "Table Scan" +const IndexTableScan = "Index Table Scan" const ExternalScan = "External Scan" func (ndesc *NodeDescribeImpl) GetNodeBasicInfo(ctx context.Context, options *ExplainOptions) (string, error) { @@ -58,6 +59,9 @@ func (ndesc *NodeDescribeImpl) GetNodeBasicInfo(ctx context.Context, options *Ex pname = "Values Scan" case plan.Node_TABLE_SCAN: pname = TableScan + if ndesc.Node.IndexScanInfo.IsIndexScan { + pname = IndexTableScan + } case plan.Node_EXTERNAL_SCAN: pname = ExternalScan case plan.Node_SOURCE_SCAN: @@ -164,20 +168,10 @@ func (ndesc *NodeDescribeImpl) GetNodeBasicInfo(ctx context.Context, options *Ex case plan.Node_TABLE_SCAN, plan.Node_EXTERNAL_SCAN, plan.Node_MATERIAL_SCAN, plan.Node_INSERT, plan.Node_SOURCE_SCAN: buf.WriteString(" on ") if ndesc.Node.ObjRef != nil { - if ndesc.Node.ParentObjRef == nil || options.CmpContext == nil { // original table + if ndesc.Node.IndexScanInfo.IsIndexScan { + buf.WriteString(ndesc.Node.IndexScanInfo.BelongToTable + "." + ndesc.Node.IndexScanInfo.IndexName) + } else { buf.WriteString(ndesc.Node.ObjRef.GetSchemaName() + "." + ndesc.Node.ObjRef.GetObjName()) - } else { // index table, need to get index table name - scanSnapshot := ndesc.Node.ScanSnapshot - if scanSnapshot == nil { - scanSnapshot = &plan.Snapshot{} - } - _, origTableDef := options.CmpContext.Resolve(ndesc.Node.ParentObjRef.GetSchemaName(), ndesc.Node.ParentObjRef.GetObjName(), scanSnapshot) - for i := range origTableDef.Indexes { - if origTableDef.Indexes[i].IndexTableName == ndesc.Node.ObjRef.GetObjName() { - buf.WriteString(ndesc.Node.ObjRef.GetSchemaName() + "." + origTableDef.Indexes[i].IndexName + "(index)") - break - } - } } } else if ndesc.Node.TableDef != nil { buf.WriteString(ndesc.Node.TableDef.GetName()) diff --git a/pkg/sql/plan/explain/types.go b/pkg/sql/plan/explain/types.go index 9b2977b1e3f2f..6711b339f7a06 100644 --- a/pkg/sql/plan/explain/types.go +++ b/pkg/sql/plan/explain/types.go @@ -19,10 +19,8 @@ import ( "context" "strings" - "github.com/matrixorigin/matrixone/pkg/pb/plan" - plan2 "github.com/matrixorigin/matrixone/pkg/sql/plan" - "github.com/matrixorigin/matrixone/pkg/logutil" + "github.com/matrixorigin/matrixone/pkg/pb/plan" ) type ExplainQuery interface { @@ -141,11 +139,10 @@ const ( ) type ExplainOptions struct { - Verbose bool - Analyze bool - Format ExplainFormat - NodeType plan.Node_NodeType - CmpContext plan2.CompilerContext + Verbose bool + Analyze bool + Format ExplainFormat + NodeType plan.Node_NodeType } func NewExplainDefaultOptions() *ExplainOptions { diff --git a/proto/plan.proto b/proto/plan.proto index 9a073571e5d8c..2cd91cd98132f 100644 --- a/proto/plan.proto +++ b/proto/plan.proto @@ -165,6 +165,15 @@ message SubscriptionMeta{ string tables = 6; // pubTables (separated by ',') } +message IndexScanInfo { + bool is_index_scan = 1; + string index_name = 2; + string belong_to_table = 3; + repeated string parts = 4; + bool is_unique = 5; + string index_table_name = 6; +} + message Function { // Function flags enum FuncFlag { @@ -843,6 +852,8 @@ message Node { ObjectRef parent_obj_ref = 19; RowsetData rowset_data = 20; + IndexScanInfo index_scan_info = 21 [(gogoproto.nullable) = false]; + string extra_options = 22; DeleteCtx delete_ctx = 23; diff --git a/test/distributed/cases/fulltext/fulltext1.result b/test/distributed/cases/fulltext/fulltext1.result index 3557fd614cf0d..ad86cfd2f9fcf 100644 --- a/test/distributed/cases/fulltext/fulltext1.result +++ b/test/distributed/cases/fulltext/fulltext1.result @@ -146,7 +146,7 @@ Project Filter Cond: (t1.c = 100) Block Filter Cond: (t1.c = 100) Runtime Filter Probe: t1.a - -> Table Scan on test.index2(index) [ForceOneCN] + -> Index Table Scan on t1.index2 [ForceOneCN] Filter Cond: prefix_eq(#[0,0]) Block Filter Cond: prefix_eq(#[0,0]) drop table t1; diff --git a/test/distributed/cases/optimizer/explain_index.result b/test/distributed/cases/optimizer/explain_index.result index 4a24c5018ae79..eefccee505f52 100644 --- a/test/distributed/cases/optimizer/explain_index.result +++ b/test/distributed/cases/optimizer/explain_index.result @@ -15,7 +15,7 @@ Sleep(1) explain select c3,c4,c5 from t1 where c3=1; TP QUERY PLAN Project - -> Table Scan on d1.t1i1(index) + -> Index Table Scan on t1.t1i1 Filter Cond: prefix_eq(#[0,0]) Block Filter Cond: prefix_eq(#[0,0]) select c3,c4,c5 from t1 where c3=1; @@ -43,7 +43,7 @@ count(*) explain select c3,c4,c5 from t1 where c3 in (1,5,10,20); TP QUERY PLAN Project - -> Table Scan on d1.t1i1(index) + -> Index Table Scan on t1.t1i1 Filter Cond: prefix_in(#[0,0]) Block Filter Cond: prefix_in(#[0,0]) select c3,c4,c5 from t1 where c3 in (1,5,10,20); @@ -91,7 +91,7 @@ c3 c4 c5 explain select c3,c4,c5 from t1 where c3 between 4 and 7 and c5=5; TP QUERY PLAN Project - -> Table Scan on d1.t1i1(index) + -> Index Table Scan on t1.t1i1 Filter Cond: prefix_between(#[0,0]), (serial_extract(#[0,0], 2, INT)) = 5) Block Filter Cond: prefix_between(#[0,0]) select c3,c4,c5 from t1 where c3 between 4 and 7 and c5=5; @@ -160,7 +160,7 @@ Project Filter Cond: (t1.c3 = 1) Block Filter Cond: (t1.c3 = 1) Runtime Filter Probe: t1.__mo_cpkey_col - -> Table Scan on d1.t1i1(index) [ForceOneCN] + -> Index Table Scan on t1.t1i1 [ForceOneCN] Filter Cond: prefix_eq(#[0,0]) Block Filter Cond: prefix_eq(#[0,0]) select * from t1 where c3=1; @@ -189,7 +189,7 @@ TP QUERY PLAN Project -> Aggregate Aggregate Functions: starcount(1) - -> Table Scan on d1.t1i1(index) + -> Index Table Scan on t1.t1i1 Filter Cond: prefix_between(#[0,0]) Block Filter Cond: prefix_between(#[0,0]) select count(*) from t1 where c3 between 100 and 200; @@ -210,7 +210,7 @@ TP QUERY PLAN Project -> Aggregate Aggregate Functions: starcount(1) - -> Table Scan on d1.t1i1(index) + -> Index Table Scan on t1.t1i1 Filter Cond: prefix_in(#[0,0]) Block Filter Cond: prefix_in(#[0,0]) select count(*) from t1 where c3 in(1,13,15,90,99); @@ -221,7 +221,7 @@ TP QUERY PLAN Project -> Aggregate Aggregate Functions: starcount(1) - -> Table Scan on d1.t1i1(index) + -> Index Table Scan on t1.t1i1 Filter Cond: prefix_between(#[0,0]), (serial_extract(#[0,0], 2, INT)) < 100) Block Filter Cond: prefix_between(#[0,0]) select count(*) from t1 where c3 between 1 and 100 and c5 <100; @@ -232,7 +232,7 @@ TP QUERY PLAN Project -> Aggregate Aggregate Functions: starcount(1) - -> Table Scan on d1.t1i1(index) + -> Index Table Scan on t1.t1i1 Filter Cond: prefix_between(#[0,0]), (serial_extract(#[0,0], 2, INT)) = -1) Block Filter Cond: prefix_between(#[0,0]) select count(*) from t1 where c3 between 100 and 200 and c5 =-1; @@ -249,7 +249,7 @@ Project Filter Cond: (t1.c2 < 650), t1.c3 BETWEEN 200 AND 300 Block Filter Cond: (t1.c2 < 650) Runtime Filter Probe: t1.__mo_cpkey_col - -> Table Scan on d1.t1i1(index) [ForceOneCN] + -> Index Table Scan on t1.t1i1 [ForceOneCN] Filter Cond: prefix_between(#[0,0]), (serial_extract(#[0,1], 1, INT)) < 650) Block Filter Cond: prefix_between(#[0,0]) select * from t1 where c3 between 200 and 300 and c2 <650; @@ -282,7 +282,7 @@ Project Filter Cond: t1.c2 in ([271386 271461 271485]), t1.c3 BETWEEN 100 AND 500 Block Filter Cond: t1.c2 in ([271386 271461 271485]) Runtime Filter Probe: t1.__mo_cpkey_col - -> Table Scan on d1.t1i1(index) [ForceOneCN] + -> Index Table Scan on t1.t1i1 [ForceOneCN] Filter Cond: prefix_between(#[0,0]), serial_extract(#[0,1], 1, INT)) in ([271386 271461 271485]) Block Filter Cond: prefix_between(#[0,0]) select * from t1 where c3 between 100 and 500 and c2 in (271461, 271485, 271386); @@ -300,7 +300,7 @@ Project -> Table Scan on d1.t1 [ForceOneCN] Filter Cond: (t1.c3 BETWEEN 100 AND 500 or t1.c3 BETWEEN 1000 AND 1100 or t1.c3 BETWEEN 1300 AND 1500) Runtime Filter Probe: t1.__mo_cpkey_col - -> Table Scan on d1.t1i1(index) [ForceOneCN] + -> Index Table Scan on t1.t1i1 [ForceOneCN] Filter Cond: (prefix_between(#[0,0]) or prefix_between(#[0,0]) or prefix_between(#[0,0])) Block Filter Cond: (prefix_between(#[0,0]) or prefix_between(#[0,0]) or prefix_between(#[0,0])) explain select count(*) from t1 where c3 between 100 and 500 or c3 between 1000 and 1100 or c3 between 1300 and 1500; @@ -308,7 +308,7 @@ TP QUERY PLAN Project -> Aggregate Aggregate Functions: starcount(1) - -> Table Scan on d1.t1i1(index) + -> Index Table Scan on t1.t1i1 Filter Cond: (prefix_between(#[0,0]) or prefix_between(#[0,0]) or prefix_between(#[0,0])) Block Filter Cond: (prefix_between(#[0,0]) or prefix_between(#[0,0]) or prefix_between(#[0,0])) select count(*) from t1 where c3 between 100 and 500 or c3 between 1000 and 1100 or c3 between 1300 and 1500; @@ -341,7 +341,7 @@ Project -> Table Scan on d1.t1 [ForceOneCN] Filter Cond: (t1.c3 BETWEEN 100 AND 500 or t1.c3 BETWEEN 1000 AND 1100 or t1.c3 in ([271386 271461 271485])) Runtime Filter Probe: t1.__mo_cpkey_col - -> Table Scan on d1.t1i1(index) [ForceOneCN] + -> Index Table Scan on t1.t1i1 [ForceOneCN] Filter Cond: (prefix_between(#[0,0]) or prefix_between(#[0,0]) or prefix_in(#[0,0])) Block Filter Cond: (prefix_between(#[0,0]) or prefix_between(#[0,0]) or prefix_in(#[0,0])) explain select count(*) from t1 where c3 between 100 and 500 or c3 between 1000 and 1100 or c3 in (271461, 271485, 271386); @@ -349,7 +349,7 @@ TP QUERY PLAN Project -> Aggregate Aggregate Functions: starcount(1) - -> Table Scan on d1.t1i1(index) + -> Index Table Scan on t1.t1i1 Filter Cond: (prefix_between(#[0,0]) or prefix_between(#[0,0]) or prefix_in(#[0,0])) Block Filter Cond: (prefix_between(#[0,0]) or prefix_between(#[0,0]) or prefix_in(#[0,0])) select count(*) from t1 where c3 between 100 and 500 or c3 between 1000 and 1100 or c3 in (271461, 271485, 271386); @@ -377,7 +377,7 @@ Project Filter Cond: t2.c2 in ([1 2 3 4 5 6 7 8 9]) Block Filter Cond: t2.c2 in ([1 2 3 4 5 6 7 8 9]) Runtime Filter Probe: t2.c1 - -> Table Scan on d1.t2i1(index) [ForceOneCN] + -> Index Table Scan on t2.t2i1 [ForceOneCN] Filter Cond: prefix_in(#[0,0]) Block Filter Cond: prefix_in(#[0,0]) select * from t2 where c2 in (1,2,3,4,5,6,7,8,9); @@ -411,7 +411,7 @@ Project Filter Cond: t2.c2 in ([1 2 3 4 5 6 7 8 9]), t2.c3 in ([1 2 3]) Block Filter Cond: t2.c2 in ([1 2 3 4 5 6 7 8 9]) Runtime Filter Probe: t2.c1 - -> Table Scan on d1.t2i1(index) [ForceOneCN] + -> Index Table Scan on t2.t2i1 [ForceOneCN] Filter Cond: prefix_in(#[0,0]), serial_extract(#[0,0], 1, INT)) in ([1 2 3]) Block Filter Cond: prefix_in(#[0,0]) select * from t2 where c2 in (1,2,3,4,5,6,7,8,9) and c3 in (1,2,3); @@ -433,7 +433,7 @@ Project Filter Cond: t2.c4 in ([1 2 3 4 5 6 7 8 9]), t2.c5 in ([2 3 4]) Block Filter Cond: t2.c4 in ([1 2 3 4 5 6 7 8 9]) Runtime Filter Probe: t2.c1 - -> Table Scan on d1.t2i2(index) [ForceOneCN] + -> Index Table Scan on t2.t2i2 [ForceOneCN] Filter Cond: prefix_in(#[0,0]), serial_extract(#[0,0], 1, INT)) in ([2 3 4]) Block Filter Cond: prefix_in(#[0,0]) select * from t2 where c4 in (1,2,3,4,5,6,7,8,9) and c5 in (2,3,4); @@ -458,7 +458,7 @@ Project Filter Cond: t2.c4 in ([1 2 3 4 5 6 7 8 9]), t2.c1 BETWEEN 1 AND 10000 Block Filter Cond: t2.c4 in ([1 2 3 4 5 6 7 8 9]), t2.c1 BETWEEN 1 AND 10000 Runtime Filter Probe: t2.c1 - -> Table Scan on d1.t2i2(index) [ForceOneCN] + -> Index Table Scan on t2.t2i2 [ForceOneCN] Filter Cond: prefix_in(#[0,0]), serial_extract(#[0,0], 2, INT)) BETWEEN 1 AND 10000 Block Filter Cond: prefix_in(#[0,0]) select * from t2 where c4 in (1,2,3,4,5,6,7,8,9) and c1 between 1 and 10000; diff --git a/test/distributed/cases/optimizer/index.result b/test/distributed/cases/optimizer/index.result index 4ef4caa8b79e2..6d5d94121459e 100644 --- a/test/distributed/cases/optimizer/index.result +++ b/test/distributed/cases/optimizer/index.result @@ -35,7 +35,7 @@ Project Filter Cond: (t1.c3 = 11) Block Filter Cond: (t1.c3 = 11) Runtime Filter Probe: t1.c1 - -> Table Scan on d1.id1(index) [ForceOneCN] + -> Index Table Scan on t1.id1 [ForceOneCN] Filter Cond: (#[0,0] = 11) Block Filter Cond: (#[0,0] = 11) select * from t1,t2 where t1.c1=t2.c0 and t1.c3=11; @@ -213,7 +213,7 @@ select * from t1 where c2=1; c1 c2 c3 1 1 1 explain select * from t1 where c3<2000; -TP QURERY PLAN +TP QUERY PLAN Project -> Table Scan on d1.t1 Filter Cond: (t1.c3 < 2000) @@ -250,4 +250,4 @@ insert into t1 select *,1111,* from generate_series(100001,150000) g; update t1 set c1=c1+1000000, c2=1,c3=c3-1000000 where c2=1; delete from t1; drop table t1; -drop database d1; \ No newline at end of file +drop database d1; diff --git a/test/distributed/cases/optimizer/like.result b/test/distributed/cases/optimizer/like.result index abba4239ea6f6..100d641813800 100644 --- a/test/distributed/cases/optimizer/like.result +++ b/test/distributed/cases/optimizer/like.result @@ -82,7 +82,7 @@ Project -> Table Scan on d1.t1 [ForceOneCN] Filter Cond: (t1.c2 = '123') Runtime Filter Probe: t1.c1 - -> Table Scan on d1.c2(index) [ForceOneCN] + -> Index Table Scan on t1.c2 [ForceOneCN] Filter Cond: prefix_eq(#[0,0]) Block Filter Cond: prefix_eq(#[0,0]) select * from t1 where c2 like '123'; From b2b0864b66f4b0bbaa0e0762bfceccb0122ab18d Mon Sep 17 00:00:00 2001 From: Kai Cao Date: Fri, 20 Dec 2024 15:11:19 +0800 Subject: [PATCH 10/26] [Cherry-pick] revert maximum value of max_allowed_packet to 1G (#20852) revert maximum value of max_allowed_packet to 1G Approved by: @daviszhen --- pkg/frontend/variables.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/frontend/variables.go b/pkg/frontend/variables.go index 48b4a777a7048..84d62ec3993bd 100644 --- a/pkg/frontend/variables.go +++ b/pkg/frontend/variables.go @@ -1040,7 +1040,7 @@ var gSysVarsDefs = map[string]SystemVariable{ Scope: ScopeBoth, Dynamic: true, SetVarHintApplies: false, - Type: InitSystemVariableIntType("max_allowed_packet", 1024, 67108864, false), + Type: InitSystemVariableIntType("max_allowed_packet", 1024, 1073741824, false), Default: int64(67108864), }, "version_comment": { From 2b6ab7e70c18feab704ec7bf92f998587a5952d6 Mon Sep 17 00:00:00 2001 From: qingxinhome <70939751+qingxinhome@users.noreply.github.com> Date: Fri, 20 Dec 2024 16:09:45 +0800 Subject: [PATCH 11/26] Locate sql parser error new-main (#20813) debug SQL Parser error issue when alter table Approved by: @badboynt1, @m-schen, @ouyuanning, @heni02 --- pkg/sql/compile/alter.go | 32 +- pkg/sql/compile/alter_test.go | 778 +++++++++++++++++ pkg/sql/compile/compile_test.go | 19 + pkg/sql/compile/ddl.go | 95 ++- pkg/sql/compile/ddl_test.go | 802 +++++++++++++++++- pkg/sql/compile/util.go | 2 +- .../transaction_enhance.result | 2 +- 7 files changed, 1696 insertions(+), 34 deletions(-) create mode 100644 pkg/sql/compile/alter_test.go diff --git a/pkg/sql/compile/alter.go b/pkg/sql/compile/alter.go index 5ea1fc37a681d..14eeb649bb532 100644 --- a/pkg/sql/compile/alter.go +++ b/pkg/sql/compile/alter.go @@ -35,10 +35,6 @@ func (s *Scope) AlterTableCopy(c *Compile) error { dbName = c.db } tblName := qry.GetTableDef().GetName() - - if err := lockMoDatabase(c, dbName, lock.LockMode_Shared); err != nil { - return err - } dbSource, err := c.e.Database(c.proc.Ctx, dbName, c.proc.GetTxnOperator()) if err != nil { return moerr.NewBadDB(c.proc.Ctx, dbName) @@ -51,13 +47,20 @@ func (s *Scope) AlterTableCopy(c *Compile) error { if c.proc.GetTxnOperator().Txn().IsPessimistic() { var retryErr error + // 0. lock origin database metadata in catalog + if err = lockMoDatabase(c, dbName, lock.LockMode_Shared); err != nil { + return err + } + // 1. lock origin table metadata in catalog if err = lockMoTable(c, dbName, tblName, lock.LockMode_Exclusive); err != nil { if !moerr.IsMoErrCode(err, moerr.ErrTxnNeedRetry) && !moerr.IsMoErrCode(err, moerr.ErrTxnNeedRetryWithDefChanged) { return err } - retryErr = err + // The changes recorded in the data dictionary table imply a change in the structure of the corresponding entity table, + // therefore it is necessary to rebuild the logical plan and redirect err to ErrTxnNeedRetryWithDefChanged + retryErr = moerr.NewTxnNeedRetryWithDefChanged(c.proc.Ctx) } // 2. lock origin table @@ -69,16 +72,31 @@ func (s *Scope) AlterTableCopy(c *Compile) error { if err = lockTable(c.proc.Ctx, c.e, c.proc, originRel, dbName, partitionTableNames, true); err != nil { if !moerr.IsMoErrCode(err, moerr.ErrTxnNeedRetry) && !moerr.IsMoErrCode(err, moerr.ErrTxnNeedRetryWithDefChanged) { + c.proc.Error(c.proc.Ctx, "lock origin table for alter table", + zap.String("databaseName", c.db), + zap.String("origin tableName", qry.GetTableDef().Name), + zap.Error(err)) return err } - retryErr = err + retryErr = moerr.NewTxnNeedRetryWithDefChanged(c.proc.Ctx) } if qry.TableDef.Indexes != nil { for _, indexdef := range qry.TableDef.Indexes { if indexdef.TableExist { if err = lockIndexTable(c.proc.Ctx, dbSource, c.e, c.proc, indexdef.IndexTableName, true); err != nil { - return err + if !moerr.IsMoErrCode(err, moerr.ErrParseError) && + !moerr.IsMoErrCode(err, moerr.ErrTxnNeedRetry) && + !moerr.IsMoErrCode(err, moerr.ErrTxnNeedRetryWithDefChanged) { + c.proc.Error(c.proc.Ctx, "lock index table for alter table", + zap.String("databaseName", c.db), + zap.String("origin tableName", qry.GetTableDef().Name), + zap.String("index name", indexdef.IndexName), + zap.String("index tableName", indexdef.IndexTableName), + zap.Error(err)) + return err + } + retryErr = moerr.NewTxnNeedRetryWithDefChanged(c.proc.Ctx) } } } diff --git a/pkg/sql/compile/alter_test.go b/pkg/sql/compile/alter_test.go new file mode 100644 index 0000000000000..bfd6e7738cca6 --- /dev/null +++ b/pkg/sql/compile/alter_test.go @@ -0,0 +1,778 @@ +// Copyright 2024 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 compile + +import ( + "context" + "testing" + "time" + + "github.com/golang/mock/gomock" + "github.com/prashantv/gostub" + "github.com/smartystreets/goconvey/convey" + "github.com/stretchr/testify/assert" + + "github.com/matrixorigin/matrixone/pkg/common/buffer" + "github.com/matrixorigin/matrixone/pkg/common/moerr" + mock_frontend "github.com/matrixorigin/matrixone/pkg/frontend/test" + "github.com/matrixorigin/matrixone/pkg/pb/lock" + plan2 "github.com/matrixorigin/matrixone/pkg/pb/plan" + "github.com/matrixorigin/matrixone/pkg/sql/plan" + "github.com/matrixorigin/matrixone/pkg/testutil" + "github.com/matrixorigin/matrixone/pkg/vm/engine" + "github.com/matrixorigin/matrixone/pkg/vm/process" +) + +func TestScope_AlterTableInplace(t *testing.T) { + tableDef := &plan.TableDef{ + TblId: 282826, + Name: "dept", + Cols: []*plan.ColDef{ + { + ColId: 0, + Name: "deptno", + Alg: plan2.CompressType_Lz4, + Typ: plan.Type{ + Id: 27, + NotNullable: false, + AutoIncr: true, + Width: 32, + Scale: -1, + }, + Default: &plan2.Default{}, + NotNull: true, + Primary: true, + Pkidx: 0, + }, + { + ColId: 1, + Name: "dname", + Alg: plan2.CompressType_Lz4, + Typ: plan.Type{ + Id: 61, + NotNullable: false, + AutoIncr: false, + Width: 15, + Scale: 0, + }, + Default: &plan2.Default{}, + NotNull: false, + Primary: false, + Pkidx: 0, + }, + { + ColId: 2, + Name: "loc", + Alg: plan2.CompressType_Lz4, + Typ: plan.Type{ + Id: 61, + NotNullable: false, + AutoIncr: false, + Width: 50, + Scale: 0, + }, + Default: &plan2.Default{}, + NotNull: false, + Primary: false, + Pkidx: 0, + }, + }, + Pkey: &plan.PrimaryKeyDef{ + Cols: nil, + PkeyColId: 0, + PkeyColName: "deptno", + Names: []string{"deptno"}, + }, + Indexes: []*plan.IndexDef{ + { + IndexName: "idxloc", + Parts: []string{"loc", "__mo_alias_deptno"}, + Unique: false, + IndexTableName: "__mo_index_secondary_0193dc98-4148-74f4-808a", + TableExist: true, + }, + }, + Defs: []*plan2.TableDef_DefType{ + { + Def: &plan.TableDef_DefType_Properties{ + Properties: &plan.PropertiesDef{ + Properties: []*plan.Property{ + { + Key: "relkind", + Value: "r", + }, + }, + }, + }, + }, + }, + } + + alterTable := &plan2.AlterTable{ + Database: "test", + TableDef: tableDef, + Actions: []*plan2.AlterTable_Action{ + { + Action: &plan2.AlterTable_Action_AddIndex{ + AddIndex: &plan2.AlterTableAddIndex{ + DbName: "test", + TableName: "dept", + OriginTablePrimaryKey: "deptno", + IndexTableExist: true, + IndexInfo: &plan2.CreateTable{ + TableDef: &plan.TableDef{ + Indexes: []*plan.IndexDef{ + { + IndexName: "idx", + Parts: []string{"dname", "__mo_alias_deptno"}, + Unique: false, + IndexTableName: "__mo_index_secondary_0193d918", + TableExist: true, + }, + }, + }, + IndexTables: []*plan.TableDef{ + { + Name: "__mo_index_secondary_0193d918-3e7b", + Cols: []*plan.ColDef{ + { + Name: "__mo_index_idx_col", + Alg: plan2.CompressType_Lz4, + Typ: plan.Type{ + Id: 61, + NotNullable: false, + AutoIncr: false, + Width: 65535, + Scale: 0, + }, + NotNull: false, + Default: &plan2.Default{ + NullAbility: false, + }, + Pkidx: 0, + }, + { + Name: "__mo_index_pri_col", + Alg: plan2.CompressType_Lz4, + Typ: plan.Type{ + Id: 27, + NotNullable: false, + AutoIncr: false, + Width: 32, + Scale: -1, + }, + NotNull: false, + Default: &plan2.Default{ + NullAbility: false, + }, + Pkidx: 0, + }, + }, + Pkey: &plan2.PrimaryKeyDef{ + PkeyColName: "__mo_index_idx_col", + Names: []string{"__mo_index_idx_col"}, + }, + }, + }, + }, + }, + }, + }, + }, + } + + cplan := &plan.Plan{ + Plan: &plan2.Plan_Ddl{ + Ddl: &plan2.DataDefinition{ + DdlType: plan2.DataDefinition_ALTER_TABLE, + Definition: &plan2.DataDefinition_AlterTable{ + AlterTable: alterTable, + }, + }, + }, + } + + s := &Scope{ + Magic: AlterTable, + Plan: cplan, + TxnOffset: 0, + } + + sql := `alter table dept add index idx(dname)` + + convey.Convey("create table lock mo_database", t, func() { + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + proc := testutil.NewProcess() + proc.Base.SessionInfo.Buf = buffer.New() + + ctx := context.Background() + proc.Ctx = context.Background() + txnCli, txnOp := newTestTxnClientAndOpWithPessimistic(ctrl) + proc.Base.TxnClient = txnCli + proc.Base.TxnOperator = txnOp + proc.ReplaceTopCtx(ctx) + + relation := mock_frontend.NewMockRelation(ctrl) + relation.EXPECT().GetTableID(gomock.Any()).Return(uint64(1)).AnyTimes() + + mockDb := mock_frontend.NewMockDatabase(ctrl) + mockDb.EXPECT().GetDatabaseId(gomock.Any()).Return("12").AnyTimes() + mockDb.EXPECT().Relation(gomock.Any(), gomock.Any(), gomock.Any()).Return(relation, nil).AnyTimes() + + eng := mock_frontend.NewMockEngine(ctrl) + eng.EXPECT().Database(gomock.Any(), gomock.Any(), gomock.Any()).Return(mockDb, nil).AnyTimes() + + getConstraintDef := gostub.Stub(&GetConstraintDef, func(_ context.Context, _ engine.Relation) (*engine.ConstraintDef, error) { + cstrDef := &engine.ConstraintDef{} + cstrDef.Cts = make([]engine.Constraint, 0) + return cstrDef, nil + }) + defer getConstraintDef.Reset() + + lockMoDb := gostub.Stub(&lockMoDatabase, func(_ *Compile, _ string, _ lock.LockMode) error { + return moerr.NewTxnNeedRetryWithDefChangedNoCtx() + }) + defer lockMoDb.Reset() + + c := NewCompile("test", "test", sql, "", "", eng, proc, nil, false, nil, time.Now()) + assert.Error(t, s.AlterTableInplace(c)) + }) + + convey.Convey("create table lock mo_tables", t, func() { + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + proc := testutil.NewProcess() + proc.Base.SessionInfo.Buf = buffer.New() + + ctx := context.Background() + proc.Ctx = context.Background() + txnCli, txnOp := newTestTxnClientAndOpWithPessimistic(ctrl) + proc.Base.TxnClient = txnCli + proc.Base.TxnOperator = txnOp + proc.ReplaceTopCtx(ctx) + + relation := mock_frontend.NewMockRelation(ctrl) + relation.EXPECT().GetTableID(gomock.Any()).Return(uint64(1)).AnyTimes() + + mockDb := mock_frontend.NewMockDatabase(ctrl) + mockDb.EXPECT().GetDatabaseId(gomock.Any()).Return("12").AnyTimes() + mockDb.EXPECT().Relation(gomock.Any(), gomock.Any(), gomock.Any()).Return(relation, nil).AnyTimes() + + eng := mock_frontend.NewMockEngine(ctrl) + eng.EXPECT().Database(gomock.Any(), gomock.Any(), gomock.Any()).Return(mockDb, nil).AnyTimes() + + getConstraintDef := gostub.Stub(&GetConstraintDef, func(_ context.Context, _ engine.Relation) (*engine.ConstraintDef, error) { + cstrDef := &engine.ConstraintDef{} + cstrDef.Cts = make([]engine.Constraint, 0) + return cstrDef, nil + }) + defer getConstraintDef.Reset() + + lockMoDb := gostub.Stub(&lockMoDatabase, func(_ *Compile, _ string, _ lock.LockMode) error { + return nil + }) + defer lockMoDb.Reset() + + lockMoTbl := gostub.Stub(&lockMoTable, func(_ *Compile, _ string, _ string, _ lock.LockMode) error { + return moerr.NewTxnNeedRetryNoCtx() + }) + defer lockMoTbl.Reset() + + lockTbl := gostub.Stub(&lockTable, func(_ context.Context, _ engine.Engine, _ *process.Process, _ engine.Relation, _ string, _ []string, _ bool) error { + return moerr.NewTxnNeedRetryNoCtx() + }) + defer lockTbl.Reset() + + lockIdxTbl := gostub.Stub(&lockIndexTable, func(_ context.Context, _ engine.Database, _ engine.Engine, _ *process.Process, _ string, _ bool) error { + return moerr.NewParseErrorNoCtx("table \"__mo_index_unique_0192748f-6868-7182-a6de-2e457c2975c6\" does not exist") + }) + defer lockIdxTbl.Reset() + + c := NewCompile("test", "test", sql, "", "", eng, proc, nil, false, nil, time.Now()) + assert.Error(t, s.AlterTableInplace(c)) + }) + + convey.Convey("create table lock index table1", t, func() { + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + proc := testutil.NewProcess() + proc.Base.SessionInfo.Buf = buffer.New() + + ctx := context.Background() + proc.Ctx = context.Background() + txnCli, txnOp := newTestTxnClientAndOpWithPessimistic(ctrl) + proc.Base.TxnClient = txnCli + proc.Base.TxnOperator = txnOp + proc.ReplaceTopCtx(ctx) + + relation := mock_frontend.NewMockRelation(ctrl) + relation.EXPECT().GetTableID(gomock.Any()).Return(uint64(1)).AnyTimes() + + mockDb := mock_frontend.NewMockDatabase(ctrl) + mockDb.EXPECT().GetDatabaseId(gomock.Any()).Return("12").AnyTimes() + mockDb.EXPECT().Relation(gomock.Any(), gomock.Any(), gomock.Any()).Return(relation, nil).AnyTimes() + + eng := mock_frontend.NewMockEngine(ctrl) + eng.EXPECT().Database(gomock.Any(), gomock.Any(), gomock.Any()).Return(mockDb, nil).AnyTimes() + + getConstraintDef := gostub.Stub(&GetConstraintDef, func(_ context.Context, _ engine.Relation) (*engine.ConstraintDef, error) { + cstrDef := &engine.ConstraintDef{} + cstrDef.Cts = make([]engine.Constraint, 0) + return cstrDef, nil + }) + defer getConstraintDef.Reset() + + lockMoDb := gostub.Stub(&lockMoDatabase, func(_ *Compile, _ string, _ lock.LockMode) error { + return nil + }) + defer lockMoDb.Reset() + + lockMoTbl := gostub.Stub(&lockMoTable, func(_ *Compile, _ string, _ string, _ lock.LockMode) error { + return moerr.NewTxnNeedRetryNoCtx() + }) + defer lockMoTbl.Reset() + + lockTbl := gostub.Stub(&lockTable, func(_ context.Context, _ engine.Engine, _ *process.Process, _ engine.Relation, _ string, _ []string, _ bool) error { + return moerr.NewTxnNeedRetryNoCtx() + }) + defer lockTbl.Reset() + + lockIdxTbl := gostub.Stub(&lockIndexTable, func(_ context.Context, _ engine.Database, _ engine.Engine, _ *process.Process, _ string, _ bool) error { + return moerr.NewParseErrorNoCtx("table \"__mo_index_unique_0192748f-6868-7182-a6de-2e457c2975c6\" does not exist") + }) + defer lockIdxTbl.Reset() + + c := NewCompile("test", "test", sql, "", "", eng, proc, nil, false, nil, time.Now()) + assert.Error(t, s.AlterTableCopy(c)) + }) + + convey.Convey("create table lock index table2", t, func() { + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + proc := testutil.NewProcess() + proc.Base.SessionInfo.Buf = buffer.New() + + ctx := context.Background() + proc.Ctx = context.Background() + txnCli, txnOp := newTestTxnClientAndOpWithPessimistic(ctrl) + proc.Base.TxnClient = txnCli + proc.Base.TxnOperator = txnOp + proc.ReplaceTopCtx(ctx) + + relation := mock_frontend.NewMockRelation(ctrl) + relation.EXPECT().GetTableID(gomock.Any()).Return(uint64(1)).AnyTimes() + + mockDb := mock_frontend.NewMockDatabase(ctrl) + mockDb.EXPECT().GetDatabaseId(gomock.Any()).Return("12").AnyTimes() + mockDb.EXPECT().Relation(gomock.Any(), gomock.Any(), gomock.Any()).Return(relation, nil).AnyTimes() + + eng := mock_frontend.NewMockEngine(ctrl) + eng.EXPECT().Database(gomock.Any(), gomock.Any(), gomock.Any()).Return(mockDb, nil).AnyTimes() + + getConstraintDef := gostub.Stub(&GetConstraintDef, func(_ context.Context, _ engine.Relation) (*engine.ConstraintDef, error) { + cstrDef := &engine.ConstraintDef{} + cstrDef.Cts = make([]engine.Constraint, 0) + return cstrDef, nil + }) + defer getConstraintDef.Reset() + + lockMoDb := gostub.Stub(&lockMoDatabase, func(_ *Compile, _ string, _ lock.LockMode) error { + return nil + }) + defer lockMoDb.Reset() + + lockMoTbl := gostub.Stub(&lockMoTable, func(_ *Compile, _ string, _ string, _ lock.LockMode) error { + return moerr.NewTxnNeedRetryNoCtx() + }) + defer lockMoTbl.Reset() + + lockTbl := gostub.Stub(&lockTable, func(_ context.Context, _ engine.Engine, _ *process.Process, _ engine.Relation, _ string, _ []string, _ bool) error { + return moerr.NewTxnNeedRetryNoCtx() + }) + defer lockTbl.Reset() + + lockIdxTbl := gostub.Stub(&lockIndexTable, func(_ context.Context, _ engine.Database, _ engine.Engine, _ *process.Process, _ string, _ bool) error { + return moerr.NewTxnNeedRetryNoCtx() + }) + defer lockIdxTbl.Reset() + + c := NewCompile("test", "test", sql, "", "", eng, proc, nil, false, nil, time.Now()) + assert.Error(t, s.AlterTableInplace(c)) + }) +} + +func TestScope_AlterTableCopy(t *testing.T) { + tableDef := &plan.TableDef{ + TblId: 282826, + Name: "dept", + Cols: []*plan.ColDef{ + { + ColId: 0, + Name: "deptno", + Alg: plan2.CompressType_Lz4, + Typ: plan.Type{ + Id: 27, + NotNullable: false, + AutoIncr: true, + Width: 32, + Scale: -1, + }, + Default: &plan2.Default{}, + NotNull: true, + Primary: true, + Pkidx: 0, + }, + { + ColId: 1, + Name: "dname", + Alg: plan2.CompressType_Lz4, + Typ: plan.Type{ + Id: 61, + NotNullable: false, + AutoIncr: false, + Width: 15, + Scale: 0, + }, + Default: &plan2.Default{}, + NotNull: false, + Primary: false, + Pkidx: 0, + }, + { + ColId: 2, + Name: "loc", + Alg: plan2.CompressType_Lz4, + Typ: plan.Type{ + Id: 61, + NotNullable: false, + AutoIncr: false, + Width: 50, + Scale: 0, + }, + Default: &plan2.Default{}, + NotNull: false, + Primary: false, + Pkidx: 0, + }, + }, + Pkey: &plan.PrimaryKeyDef{ + Cols: nil, + PkeyColId: 0, + PkeyColName: "deptno", + Names: []string{"deptno"}, + }, + Indexes: []*plan.IndexDef{ + { + IndexName: "idxloc", + Parts: []string{"loc", "__mo_alias_deptno"}, + Unique: false, + IndexTableName: "__mo_index_secondary_0193dc98-4148-74f4-808a", + TableExist: true, + }, + }, + Defs: []*plan2.TableDef_DefType{ + { + Def: &plan.TableDef_DefType_Properties{ + Properties: &plan.PropertiesDef{ + Properties: []*plan.Property{ + { + Key: "relkind", + Value: "r", + }, + }, + }, + }, + }, + }, + } + + copyTableDef := &plan.TableDef{ + TblId: 282826, + Name: "dept_copy_0193dcb4-4c07-77d8", + Cols: []*plan.ColDef{ + { + ColId: 1, + Name: "deptno", + Alg: plan2.CompressType_Lz4, + Typ: plan.Type{ + Id: 27, + NotNullable: false, + AutoIncr: true, + Width: 32, + Scale: -1, + }, + Default: &plan2.Default{}, + NotNull: true, + Primary: true, + Pkidx: 0, + }, + { + ColId: 2, + Name: "dname", + Alg: plan2.CompressType_Lz4, + Typ: plan.Type{ + Id: 61, + NotNullable: false, + AutoIncr: false, + Width: 20, + Scale: 0, + }, + Default: &plan2.Default{}, + NotNull: false, + Primary: false, + Pkidx: 0, + }, + { + ColId: 3, + Name: "loc", + Alg: plan2.CompressType_Lz4, + Typ: plan.Type{ + Id: 61, + NotNullable: false, + AutoIncr: false, + Width: 50, + Scale: 0, + }, + Default: &plan2.Default{}, + NotNull: false, + Primary: false, + Pkidx: 0, + }, + { + ColId: 4, + Name: "__mo_rowid", + Hidden: true, + Alg: plan2.CompressType_Lz4, + Typ: plan.Type{ + Id: 101, + NotNullable: true, + AutoIncr: false, + Width: 0, + Scale: 0, + Table: "dept", + }, + Default: &plan2.Default{}, + NotNull: false, + Primary: false, + Pkidx: 0, + }, + }, + TableType: "r", + Createsql: `create table dept (deptno int unsigned auto_increment comment "部门编号", dname varchar(15) comment "部门名称", loc varchar(50) comment "部门所在位置", index idxloc (loc), primary key (deptno)) comment = '部门表'`, + Pkey: &plan.PrimaryKeyDef{ + Cols: nil, + PkeyColId: 0, + PkeyColName: "deptno", + Names: []string{"deptno"}, + }, + Indexes: []*plan.IndexDef{ + { + IndexName: "idxloc", + Parts: []string{"loc", "__mo_alias_deptno"}, + Unique: false, + IndexTableName: "__mo_index_secondary_0193dc98-4148-74f4-808a", + TableExist: true, + }, + }, + Defs: []*plan2.TableDef_DefType{ + { + Def: &plan.TableDef_DefType_Properties{ + Properties: &plan.PropertiesDef{ + Properties: []*plan.Property{ + { + Key: "relkind", + Value: "r", + }, + }, + }, + }, + }, + }, + } + + alterTable := &plan2.AlterTable{ + Database: "test", + TableDef: tableDef, + CopyTableDef: copyTableDef, + } + + cplan := &plan.Plan{ + Plan: &plan2.Plan_Ddl{ + Ddl: &plan2.DataDefinition{ + DdlType: plan2.DataDefinition_ALTER_TABLE, + Definition: &plan2.DataDefinition_AlterTable{ + AlterTable: alterTable, + }, + }, + }, + } + + s := &Scope{ + Magic: AlterTable, + Plan: cplan, + TxnOffset: 0, + } + + sql := `alter table dept add index idx(dname)` + + convey.Convey("create table lock mo_database", t, func() { + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + proc := testutil.NewProcess() + proc.Base.SessionInfo.Buf = buffer.New() + + ctx := context.Background() + proc.Ctx = context.Background() + txnCli, txnOp := newTestTxnClientAndOpWithPessimistic(ctrl) + proc.Base.TxnClient = txnCli + proc.Base.TxnOperator = txnOp + proc.ReplaceTopCtx(ctx) + + relation := mock_frontend.NewMockRelation(ctrl) + relation.EXPECT().GetTableID(gomock.Any()).Return(uint64(1)).AnyTimes() + + mockDb := mock_frontend.NewMockDatabase(ctrl) + mockDb.EXPECT().GetDatabaseId(gomock.Any()).Return("12").AnyTimes() + mockDb.EXPECT().Relation(gomock.Any(), gomock.Any(), gomock.Any()).Return(relation, nil).AnyTimes() + + eng := mock_frontend.NewMockEngine(ctrl) + eng.EXPECT().Database(gomock.Any(), gomock.Any(), gomock.Any()).Return(mockDb, nil).AnyTimes() + + getConstraintDef := gostub.Stub(&GetConstraintDef, func(_ context.Context, _ engine.Relation) (*engine.ConstraintDef, error) { + return nil, nil + }) + defer getConstraintDef.Reset() + + lockMoDb := gostub.Stub(&lockMoDatabase, func(_ *Compile, _ string, _ lock.LockMode) error { + return moerr.NewTxnNeedRetryWithDefChangedNoCtx() + }) + defer lockMoDb.Reset() + + c := NewCompile("test", "test", sql, "", "", eng, proc, nil, false, nil, time.Now()) + assert.Error(t, s.AlterTableCopy(c)) + }) + + convey.Convey("create table lock index table1", t, func() { + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + proc := testutil.NewProcess() + proc.Base.SessionInfo.Buf = buffer.New() + + ctx := context.Background() + proc.Ctx = context.Background() + txnCli, txnOp := newTestTxnClientAndOpWithPessimistic(ctrl) + proc.Base.TxnClient = txnCli + proc.Base.TxnOperator = txnOp + proc.ReplaceTopCtx(ctx) + + relation := mock_frontend.NewMockRelation(ctrl) + relation.EXPECT().GetTableID(gomock.Any()).Return(uint64(1)).AnyTimes() + + mockDb := mock_frontend.NewMockDatabase(ctrl) + mockDb.EXPECT().GetDatabaseId(gomock.Any()).Return("12").AnyTimes() + mockDb.EXPECT().Relation(gomock.Any(), gomock.Any(), gomock.Any()).Return(relation, nil).AnyTimes() + + eng := mock_frontend.NewMockEngine(ctrl) + eng.EXPECT().Database(gomock.Any(), gomock.Any(), gomock.Any()).Return(mockDb, nil).AnyTimes() + + getConstraintDef := gostub.Stub(&GetConstraintDef, func(_ context.Context, _ engine.Relation) (*engine.ConstraintDef, error) { + return nil, nil + }) + defer getConstraintDef.Reset() + + lockMoDb := gostub.Stub(&lockMoDatabase, func(_ *Compile, _ string, _ lock.LockMode) error { + return nil + }) + defer lockMoDb.Reset() + + lockMoTbl := gostub.Stub(&lockMoTable, func(_ *Compile, _ string, _ string, _ lock.LockMode) error { + return moerr.NewTxnNeedRetryNoCtx() + }) + defer lockMoTbl.Reset() + + lockTbl := gostub.Stub(&lockTable, func(_ context.Context, _ engine.Engine, _ *process.Process, _ engine.Relation, _ string, _ []string, _ bool) error { + return moerr.NewTxnNeedRetryNoCtx() + }) + defer lockTbl.Reset() + + lockIdxTbl := gostub.Stub(&lockIndexTable, func(_ context.Context, _ engine.Database, _ engine.Engine, _ *process.Process, _ string, _ bool) error { + return moerr.NewParseErrorNoCtx("table \"__mo_index_unique_0192748f-6868-7182-a6de-2e457c2975c6\" does not exist") + }) + defer lockIdxTbl.Reset() + + c := NewCompile("test", "test", sql, "", "", eng, proc, nil, false, nil, time.Now()) + assert.Error(t, s.AlterTableCopy(c)) + }) + + convey.Convey("create table lock index table2", t, func() { + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + proc := testutil.NewProcess() + proc.Base.SessionInfo.Buf = buffer.New() + + ctx := context.Background() + proc.Ctx = context.Background() + txnCli, txnOp := newTestTxnClientAndOpWithPessimistic(ctrl) + proc.Base.TxnClient = txnCli + proc.Base.TxnOperator = txnOp + proc.ReplaceTopCtx(ctx) + + relation := mock_frontend.NewMockRelation(ctrl) + relation.EXPECT().GetTableID(gomock.Any()).Return(uint64(1)).AnyTimes() + + mockDb := mock_frontend.NewMockDatabase(ctrl) + mockDb.EXPECT().GetDatabaseId(gomock.Any()).Return("12").AnyTimes() + mockDb.EXPECT().Relation(gomock.Any(), gomock.Any(), gomock.Any()).Return(relation, nil).AnyTimes() + + eng := mock_frontend.NewMockEngine(ctrl) + eng.EXPECT().Database(gomock.Any(), gomock.Any(), gomock.Any()).Return(mockDb, nil).AnyTimes() + + getConstraintDef := gostub.Stub(&GetConstraintDef, func(_ context.Context, _ engine.Relation) (*engine.ConstraintDef, error) { + return nil, nil + }) + defer getConstraintDef.Reset() + + lockMoDb := gostub.Stub(&lockMoDatabase, func(_ *Compile, _ string, _ lock.LockMode) error { + return nil + }) + defer lockMoDb.Reset() + + lockMoTbl := gostub.Stub(&lockMoTable, func(_ *Compile, _ string, _ string, _ lock.LockMode) error { + return moerr.NewTxnNeedRetryNoCtx() + }) + defer lockMoTbl.Reset() + + lockTbl := gostub.Stub(&lockTable, func(_ context.Context, _ engine.Engine, _ *process.Process, _ engine.Relation, _ string, _ []string, _ bool) error { + return moerr.NewTxnNeedRetryNoCtx() + }) + defer lockTbl.Reset() + + lockIdxTbl := gostub.Stub(&lockIndexTable, func(_ context.Context, _ engine.Database, _ engine.Engine, _ *process.Process, _ string, _ bool) error { + return moerr.NewTxnNeedRetryNoCtx() + }) + defer lockIdxTbl.Reset() + + c := NewCompile("test", "test", sql, "", "", eng, proc, nil, false, nil, time.Now()) + assert.Error(t, s.AlterTableCopy(c)) + }) +} diff --git a/pkg/sql/compile/compile_test.go b/pkg/sql/compile/compile_test.go index 1ab8d8fb823ee..7b97529023fd7 100644 --- a/pkg/sql/compile/compile_test.go +++ b/pkg/sql/compile/compile_test.go @@ -241,6 +241,25 @@ func newTestTxnClientAndOp(ctrl *gomock.Controller) (client.TxnClient, client.Tx return txnClient, txnOperator } +func newTestTxnClientAndOpWithPessimistic(ctrl *gomock.Controller) (client.TxnClient, client.TxnOperator) { + txnOperator := mock_frontend.NewMockTxnOperator(ctrl) + txnOperator.EXPECT().Commit(gomock.Any()).Return(nil).AnyTimes() + txnOperator.EXPECT().Rollback(gomock.Any()).Return(nil).AnyTimes() + txnOperator.EXPECT().GetWorkspace().Return(&Ws{}).AnyTimes() + txnOperator.EXPECT().Txn().Return(txn.TxnMeta{ + Mode: txn.TxnMode_Pessimistic, + }).AnyTimes() + txnOperator.EXPECT().TxnOptions().Return(txn.TxnOptions{}).AnyTimes() + txnOperator.EXPECT().NextSequence().Return(uint64(0)).AnyTimes() + txnOperator.EXPECT().EnterRunSql().Return().AnyTimes() + txnOperator.EXPECT().ExitRunSql().Return().AnyTimes() + txnOperator.EXPECT().Snapshot().Return(txn.CNTxnSnapshot{}, nil).AnyTimes() + txnOperator.EXPECT().Status().Return(txn.TxnStatus_Active).AnyTimes() + txnClient := mock_frontend.NewMockTxnClient(ctrl) + txnClient.EXPECT().New(gomock.Any(), gomock.Any()).Return(txnOperator, nil).AnyTimes() + return txnClient, txnOperator +} + func newTestCase(sql string, t *testing.T) compileTestCase { proc := testutil.NewProcess() proc.GetSessionInfo().Buf = buffer.New() diff --git a/pkg/sql/compile/ddl.go b/pkg/sql/compile/ddl.go index d47bf33b2bc91..57616333eae95 100644 --- a/pkg/sql/compile/ddl.go +++ b/pkg/sql/compile/ddl.go @@ -385,10 +385,6 @@ func (s *Scope) AlterTableInplace(c *Compile) error { } tblName := qry.GetTableDef().GetName() - - if err := lockMoDatabase(c, dbName, lock.LockMode_Shared); err != nil { - return err - } dbSource, err := c.e.Database(c.proc.Ctx, dbName, c.proc.GetTxnOperator()) if err != nil { return moerr.NewBadDB(c.proc.Ctx, dbName) @@ -428,13 +424,20 @@ func (s *Scope) AlterTableInplace(c *Compile) error { if c.proc.GetTxnOperator().Txn().IsPessimistic() { var retryErr error + // 0. lock origin database metadata in catalog + if err = lockMoDatabase(c, dbName, lock.LockMode_Shared); err != nil { + return err + } + // 1. lock origin table metadata in catalog if err = lockMoTable(c, dbName, tblName, lock.LockMode_Exclusive); err != nil { if !moerr.IsMoErrCode(err, moerr.ErrTxnNeedRetry) && !moerr.IsMoErrCode(err, moerr.ErrTxnNeedRetryWithDefChanged) { return err } - retryErr = err + // The changes recorded in the data dictionary table imply a change in the structure of the corresponding entity table, + // therefore it is necessary to rebuild the logical plan and redirect err to ErrTxnNeedRetryWithDefChanged + retryErr = moerr.NewTxnNeedRetryWithDefChanged(c.proc.Ctx) } // 2. lock origin table @@ -447,7 +450,28 @@ func (s *Scope) AlterTableInplace(c *Compile) error { !moerr.IsMoErrCode(err, moerr.ErrTxnNeedRetryWithDefChanged) { return err } - retryErr = err + retryErr = moerr.NewTxnNeedRetryWithDefChanged(c.proc.Ctx) + } + + if qry.TableDef.Indexes != nil { + for _, indexdef := range qry.TableDef.Indexes { + if indexdef.TableExist { + if err = lockIndexTable(c.proc.Ctx, dbSource, c.e, c.proc, indexdef.IndexTableName, true); err != nil { + if !moerr.IsMoErrCode(err, moerr.ErrParseError) && + !moerr.IsMoErrCode(err, moerr.ErrTxnNeedRetry) && + !moerr.IsMoErrCode(err, moerr.ErrTxnNeedRetryWithDefChanged) { + c.proc.Error(c.proc.Ctx, "lock index table for alter table", + zap.String("databaseName", c.db), + zap.String("origin tableName", qry.GetTableDef().Name), + zap.String("index name", indexdef.IndexName), + zap.String("index tableName", indexdef.IndexTableName), + zap.Error(err)) + return err + } + retryErr = moerr.NewTxnNeedRetryWithDefChanged(c.proc.Ctx) + } + } + } } // 3. lock foreign key's table @@ -970,7 +994,7 @@ func (s *Scope) CreateTable(c *Compile) error { // convert the plan's defs to the execution's defs exeDefs, err := planDefsToExeDefs(qry.GetTableDef()) if err != nil { - c.proc.Info(c.proc.Ctx, "createTable", + c.proc.Error(c.proc.Ctx, "createTable", zap.String("databaseName", c.db), zap.String("tableName", qry.GetTableDef().GetName()), zap.Error(err), @@ -1033,7 +1057,7 @@ func (s *Scope) CreateTable(c *Compile) error { } if err = lockMoTable(c, dbName, tblName, lock.LockMode_Exclusive); err != nil { - c.proc.Info(c.proc.Ctx, "createTable", + c.proc.Error(c.proc.Ctx, "createTable", zap.String("databaseName", c.db), zap.String("tableName", qry.GetTableDef().GetName()), zap.Error(err), @@ -1042,7 +1066,7 @@ func (s *Scope) CreateTable(c *Compile) error { } if err = dbSource.Create(context.WithValue(c.proc.Ctx, defines.SqlKey{}, c.sql), tblName, append(exeCols, exeDefs...)); err != nil { - c.proc.Info(c.proc.Ctx, "createTable", + c.proc.Error(c.proc.Ctx, "createTable", zap.String("databaseName", c.db), zap.String("tableName", qry.GetTableDef().GetName()), zap.Error(err), @@ -1055,7 +1079,7 @@ func (s *Scope) CreateTable(c *Compile) error { storageCols := planColsToExeCols(table.GetCols()) storageDefs, err := planDefsToExeDefs(table) if err != nil { - c.proc.Info(c.proc.Ctx, "createTable", + c.proc.Error(c.proc.Ctx, "createTable", zap.String("databaseName", c.db), zap.String("tableName", qry.GetTableDef().GetName()), zap.Error(err), @@ -1064,7 +1088,7 @@ func (s *Scope) CreateTable(c *Compile) error { } err = dbSource.Create(c.proc.Ctx, table.GetName(), append(storageCols, storageDefs...)) if err != nil { - c.proc.Info(c.proc.Ctx, "createTable", + c.proc.Error(c.proc.Ctx, "createTable", zap.String("databaseName", c.db), zap.String("tableName", qry.GetTableDef().GetName()), zap.Error(err), @@ -1340,7 +1364,7 @@ func (s *Scope) CreateTable(c *Compile) error { exeCols = planColsToExeCols(planCols) exeDefs, err = planDefsToExeDefs(def) if err != nil { - c.proc.Info(c.proc.Ctx, "createTable", + c.proc.Error(c.proc.Ctx, "createTable", zap.String("databaseName", c.db), zap.String("tableName", qry.GetTableDef().GetName()), zap.Error(err), @@ -1362,7 +1386,7 @@ func (s *Scope) CreateTable(c *Compile) error { } if err := dbSource.Create(c.proc.Ctx, def.Name, append(exeCols, exeDefs...)); err != nil { - c.proc.Info(c.proc.Ctx, "createTable", + c.proc.Error(c.proc.Ctx, "createTable", zap.String("databaseName", c.db), zap.String("tableName", qry.GetTableDef().GetName()), zap.Error(err), @@ -1379,6 +1403,12 @@ func (s *Scope) CreateTable(c *Compile) error { nil, ) if err != nil { + c.proc.Error(c.proc.Ctx, "create index table for maybeCreateAutoIncrement", + zap.String("databaseName", c.db), + zap.String("tableName", qry.GetTableDef().GetName()), + zap.String("index tableName", def.Name), + zap.Error(err), + ) return err } @@ -1403,15 +1433,21 @@ func (s *Scope) CreateTable(c *Compile) error { } err = c.runSql(initSQL) if err != nil { + c.proc.Error(c.proc.Ctx, "create index table for execute initSQL", + zap.String("databaseName", c.db), + zap.String("tableName", qry.GetTableDef().GetName()), + zap.String("index tableName", def.Name), + zap.String("initSQL", initSQL), + zap.Error(err), + ) return err } - } if checkIndexInitializable(dbName, tblName) { newRelation, err := dbSource.Relation(c.proc.Ctx, tblName, nil) if err != nil { - c.proc.Info(c.proc.Ctx, "createTable", + c.proc.Error(c.proc.Ctx, "createTable", zap.String("databaseName", c.db), zap.String("tableName", qry.GetTableDef().GetName()), zap.Error(err), @@ -1442,7 +1478,7 @@ func (s *Scope) CreateTable(c *Compile) error { insertSQL2, err := makeInsertTablePartitionsSQL(c.proc.Ctx, dbSource, newRelation) if err != nil { - c.proc.Info(c.proc.Ctx, "createTable", + c.proc.Error(c.proc.Ctx, "createTable", zap.String("databaseName", c.db), zap.String("tableName", qry.GetTableDef().GetName()), zap.Error(err), @@ -1451,7 +1487,7 @@ func (s *Scope) CreateTable(c *Compile) error { } err = c.runSql(insertSQL2) if err != nil { - c.proc.Info(c.proc.Ctx, "createTable", + c.proc.Error(c.proc.Ctx, "createTable", zap.String("databaseName", c.db), zap.String("tableName", qry.GetTableDef().GetName()), zap.Error(err), @@ -1470,6 +1506,11 @@ func (s *Scope) CreateTable(c *Compile) error { nil, ) if err != nil { + c.proc.Error(c.proc.Ctx, "create table for maybeCreateAutoIncrement", + zap.String("databaseName", c.db), + zap.String("tableName", qry.GetTableDef().GetName()), + zap.Error(err), + ) return err } @@ -1478,6 +1519,12 @@ func (s *Scope) CreateTable(c *Compile) error { catalog.MO_CATALOG, catalog.MO_RETENTION, dbName, tblName, qry.RetentionDeadline) err = c.runSql(insertRetention) if err != nil { + c.proc.Error(c.proc.Ctx, "create table for RetentionDeadline", + zap.String("databaseName", c.db), + zap.String("tableName", qry.GetTableDef().GetName()), + zap.String("insertRetention sql", insertRetention), + zap.Error(err), + ) return err } } @@ -1633,7 +1680,7 @@ func (s *Scope) CreateView(c *Compile) error { return nil } -func checkIndexInitializable(dbName string, tblName string) bool { +var checkIndexInitializable = func(dbName string, tblName string) bool { if dbName == catalog.MOTaskDB { return false } else if dbName == catalog.MO_CATALOG && strings.HasPrefix(tblName, catalog.MO_INDEXES) { @@ -2711,7 +2758,7 @@ func (s *Scope) DropTable(c *Compile) error { return err } -func planDefsToExeDefs(tableDef *plan.TableDef) ([]engine.TableDef, error) { +var planDefsToExeDefs = func(tableDef *plan.TableDef) ([]engine.TableDef, error) { planDefs := tableDef.GetDefs() var exeDefs []engine.TableDef c := new(engine.ConstraintDef) @@ -3545,7 +3592,7 @@ func doLockTable( return err } -func lockTable( +var lockTable = func( ctx context.Context, eng engine.Engine, proc *process.Process, @@ -3577,7 +3624,7 @@ func lockTable( } // lockIndexTable -func lockIndexTable(ctx context.Context, dbSource engine.Database, eng engine.Engine, proc *process.Process, tableName string, defChanged bool) error { +var lockIndexTable = func(ctx context.Context, dbSource engine.Database, eng engine.Engine, proc *process.Process, tableName string, defChanged bool) error { rel, err := dbSource.Relation(ctx, tableName, nil) if err != nil { return err @@ -3612,7 +3659,7 @@ func lockRows( return err } -func maybeCreateAutoIncrement( +var maybeCreateAutoIncrement = func( ctx context.Context, sid string, db engine.Database, @@ -3765,7 +3812,7 @@ func getLockVector(proc *process.Process, accountId uint32, names []string) (*ve return vec, nil } -func lockMoDatabase(c *Compile, dbName string, lockMode lock.LockMode) error { +var lockMoDatabase = func(c *Compile, dbName string, lockMode lock.LockMode) error { dbRel, err := getRelFromMoCatalog(c, catalog.MO_DATABASE) if err != nil { return err @@ -3782,7 +3829,7 @@ func lockMoDatabase(c *Compile, dbName string, lockMode lock.LockMode) error { return nil } -func lockMoTable( +var lockMoTable = func( c *Compile, dbName string, tblName string, diff --git a/pkg/sql/compile/ddl_test.go b/pkg/sql/compile/ddl_test.go index 9e6f0642a9c0d..7f666ea0a3847 100644 --- a/pkg/sql/compile/ddl_test.go +++ b/pkg/sql/compile/ddl_test.go @@ -20,6 +20,7 @@ import ( "time" "github.com/golang/mock/gomock" + "github.com/prashantv/gostub" "github.com/smartystreets/goconvey/convey" "github.com/stretchr/testify/assert" @@ -28,9 +29,11 @@ import ( "github.com/matrixorigin/matrixone/pkg/common/moerr" "github.com/matrixorigin/matrixone/pkg/defines" mock_frontend "github.com/matrixorigin/matrixone/pkg/frontend/test" + "github.com/matrixorigin/matrixone/pkg/pb/lock" plan2 "github.com/matrixorigin/matrixone/pkg/pb/plan" "github.com/matrixorigin/matrixone/pkg/sql/plan" "github.com/matrixorigin/matrixone/pkg/testutil" + "github.com/matrixorigin/matrixone/pkg/txn/client" "github.com/matrixorigin/matrixone/pkg/vm/engine" "github.com/matrixorigin/matrixone/pkg/vm/process" ) @@ -220,7 +223,7 @@ func TestScope_CreateTable(t *testing.T) { assert.Error(t, s.CreateTable(c)) }) - convey.Convey("create table FaultTolerance1", t, func() { + convey.Convey("create table FaultTolerance2", t, func() { ctrl := gomock.NewController(t) defer ctrl.Finish() @@ -259,6 +262,803 @@ func TestScope_CreateTable(t *testing.T) { assert.Error(t, s.CreateTable(c)) }) + convey.Convey("create table FaultTolerance3", t, func() { + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + proc := testutil.NewProcess() + proc.Base.SessionInfo.Buf = buffer.New() + + ctx := context.Background() + proc.Ctx = context.Background() + txnCli, txnOp := newTestTxnClientAndOp(ctrl) + proc.Base.TxnClient = txnCli + proc.Base.TxnOperator = txnOp + proc.ReplaceTopCtx(ctx) + + relation := mock_frontend.NewMockRelation(ctrl) + relation.EXPECT().GetTableID(gomock.Any()).Return(uint64(1)).AnyTimes() + + mockDbMeta := mock_frontend.NewMockDatabase(ctrl) + mockDbMeta.EXPECT().Relation(gomock.Any(), catalog.MO_DATABASE, gomock.Any()).Return(relation, nil).AnyTimes() + + eng := mock_frontend.NewMockEngine(ctrl) + eng.EXPECT().Database(gomock.Any(), gomock.Any(), gomock.Any()).Return(mockDbMeta, nil).AnyTimes() + + planDef2ExecDef := gostub.Stub(&planDefsToExeDefs, func(_ *plan.TableDef) ([]engine.TableDef, error) { + return nil, moerr.NewInternalErrorNoCtx("test error") + }) + defer planDef2ExecDef.Reset() + + c := NewCompile("test", "test", sql, "", "", eng, proc, nil, false, nil, time.Now()) + assert.Error(t, s.CreateTable(c)) + }) + + convey.Convey("create table FaultTolerance4", t, func() { + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + proc := testutil.NewProcess() + proc.Base.SessionInfo.Buf = buffer.New() + + ctx := context.Background() + proc.Ctx = context.Background() + txnCli, txnOp := newTestTxnClientAndOp(ctrl) + proc.Base.TxnClient = txnCli + proc.Base.TxnOperator = txnOp + proc.ReplaceTopCtx(ctx) + + relation := mock_frontend.NewMockRelation(ctrl) + relation.EXPECT().GetTableID(gomock.Any()).Return(uint64(1)).AnyTimes() + + mockDbMeta := mock_frontend.NewMockDatabase(ctrl) + mockDbMeta.EXPECT().Relation(gomock.Any(), catalog.MO_DATABASE, gomock.Any()).Return(relation, nil).AnyTimes() + mockDbMeta.EXPECT().RelationExists(gomock.Any(), gomock.Any(), gomock.Any()).Return(false, nil).AnyTimes() + + eng := mock_frontend.NewMockEngine(ctrl) + eng.EXPECT().Database(gomock.Any(), gomock.Any(), gomock.Any()).Return(mockDbMeta, nil).AnyTimes() + + lockMoDb := gostub.Stub(&lockMoDatabase, func(_ *Compile, _ string, _ lock.LockMode) error { + return nil + }) + defer lockMoDb.Reset() + + lockMoTbl := gostub.Stub(&lockMoTable, func(_ *Compile, _ string, _ string, _ lock.LockMode) error { + return moerr.NewTxnNeedRetryNoCtx() + }) + defer lockMoTbl.Reset() + + c := NewCompile("test", "test", sql, "", "", eng, proc, nil, false, nil, time.Now()) + assert.Error(t, s.CreateTable(c)) + }) + + convey.Convey("create table FaultTolerance5", t, func() { + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + proc := testutil.NewProcess() + proc.Base.SessionInfo.Buf = buffer.New() + + ctx := context.Background() + proc.Ctx = context.Background() + txnCli, txnOp := newTestTxnClientAndOp(ctrl) + proc.Base.TxnClient = txnCli + proc.Base.TxnOperator = txnOp + proc.ReplaceTopCtx(ctx) + + relation := mock_frontend.NewMockRelation(ctrl) + relation.EXPECT().GetTableID(gomock.Any()).Return(uint64(1)).AnyTimes() + + mockDbMeta := mock_frontend.NewMockDatabase(ctrl) + mockDbMeta.EXPECT().Relation(gomock.Any(), catalog.MO_DATABASE, gomock.Any()).Return(relation, nil).AnyTimes() + mockDbMeta.EXPECT().RelationExists(gomock.Any(), gomock.Any(), gomock.Any()).Return(false, nil).AnyTimes() + mockDbMeta.EXPECT().Create(gomock.Any(), gomock.Any(), gomock.Any()).Return(moerr.NewInternalErrorNoCtx("test err")).AnyTimes() + + eng := mock_frontend.NewMockEngine(ctrl) + eng.EXPECT().Database(gomock.Any(), gomock.Any(), gomock.Any()).Return(mockDbMeta, nil).AnyTimes() + + lockMoDb := gostub.Stub(&lockMoDatabase, func(_ *Compile, _ string, _ lock.LockMode) error { + return nil + }) + defer lockMoDb.Reset() + + lockMoTbl := gostub.Stub(&lockMoTable, func(_ *Compile, _ string, _ string, _ lock.LockMode) error { + return nil + }) + defer lockMoTbl.Reset() + + c := NewCompile("test", "test", sql, "", "", eng, proc, nil, false, nil, time.Now()) + assert.Error(t, s.CreateTable(c)) + }) + + convey.Convey("create table FaultTolerance10", t, func() { + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + proc := testutil.NewProcess() + proc.Base.SessionInfo.Buf = buffer.New() + + ctx := context.Background() + proc.Ctx = context.Background() + txnCli, txnOp := newTestTxnClientAndOp(ctrl) + proc.Base.TxnClient = txnCli + proc.Base.TxnOperator = txnOp + proc.ReplaceTopCtx(ctx) + + relation := mock_frontend.NewMockRelation(ctrl) + relation.EXPECT().GetTableID(gomock.Any()).Return(uint64(1)).AnyTimes() + + mockDbMeta := mock_frontend.NewMockDatabase(ctrl) + mockDbMeta.EXPECT().Relation(gomock.Any(), catalog.MO_DATABASE, gomock.Any()).Return(relation, nil).AnyTimes() + mockDbMeta.EXPECT().RelationExists(gomock.Any(), gomock.Any(), gomock.Any()).Return(false, nil).AnyTimes() + mockDbMeta.EXPECT().Create(gomock.Any(), gomock.Any(), gomock.Any()).DoAndReturn(func(_ context.Context, tblName string, _ []engine.TableDef) error { + if tblName == "dept" { + return nil + } else if tblName == "%!%p0%!%dept" || tblName == "%!%p1%!%dept" { + return nil + } else if tblName == "__mo_index_secondary_0193d918-3e7b-7506-9f70-64fbcf055c19" { + return nil + } + return nil + }).AnyTimes() + + eng := mock_frontend.NewMockEngine(ctrl) + eng.EXPECT().Database(gomock.Any(), gomock.Any(), gomock.Any()).Return(mockDbMeta, nil).AnyTimes() + + planDef2ExecDef := gostub.Stub(&planDefsToExeDefs, func(tbl *plan.TableDef) ([]engine.TableDef, error) { + if tbl.Name == "dept" { + return nil, nil + } else if tbl.Name == "%!%p0%!%dept" || tbl.Name == "%!%p1%!%dept" { + return nil, nil + } else if tbl.Name == "__mo_index_secondary_0193d918-3e7b-7506-9f70-64fbcf055c19" { + return nil, nil + } + return nil, nil + }) + defer planDef2ExecDef.Reset() + + lockMoDb := gostub.Stub(&lockMoDatabase, func(_ *Compile, _ string, _ lock.LockMode) error { + return nil + }) + defer lockMoDb.Reset() + + lockMoTbl := gostub.Stub(&lockMoTable, func(_ *Compile, _ string, _ string, _ lock.LockMode) error { + return nil + }) + defer lockMoTbl.Reset() + + checkIndexInit := gostub.Stub(&checkIndexInitializable, func(_ string, _ string) bool { + return false + }) + defer checkIndexInit.Reset() + + createAutoIncrement := gostub.Stub(&maybeCreateAutoIncrement, func(_ context.Context, _ string, _ engine.Database, _ *plan.TableDef, _ client.TxnOperator, _ func() string) error { + return moerr.NewInternalErrorNoCtx("test err") + }) + defer createAutoIncrement.Reset() + + c := NewCompile("test", "test", sql, "", "", eng, proc, nil, false, nil, time.Now()) + assert.Error(t, s.CreateTable(c)) + }) +} + +func TestScope_CreateTable2(t *testing.T) { + tableDef := &plan.TableDef{ + Name: "dept", + Cols: []*plan.ColDef{ + { + ColId: 0, + Name: "deptno", + Alg: plan2.CompressType_Lz4, + Typ: plan.Type{ + Id: 27, + NotNullable: false, + AutoIncr: true, + Width: 32, + Scale: -1, + }, + Default: &plan2.Default{}, + NotNull: true, + Primary: true, + Pkidx: 0, + }, + { + ColId: 1, + Name: "dname", + Alg: plan2.CompressType_Lz4, + Typ: plan.Type{ + Id: 61, + NotNullable: false, + AutoIncr: false, + Width: 15, + Scale: 0, + }, + Default: &plan2.Default{}, + NotNull: false, + Primary: false, + Pkidx: 0, + }, + { + ColId: 2, + Name: "loc", + Alg: plan2.CompressType_Lz4, + Typ: plan.Type{ + Id: 61, + NotNullable: false, + AutoIncr: false, + Width: 50, + Scale: 0, + }, + Default: &plan2.Default{}, + NotNull: false, + Primary: false, + Pkidx: 0, + }, + }, + Pkey: &plan.PrimaryKeyDef{ + Cols: nil, + PkeyColId: 0, + PkeyColName: "deptno", + Names: []string{"deptno"}, + }, + Indexes: []*plan.IndexDef{ + { + IndexName: "idxloc", + Parts: []string{"loc", "__mo_alias_deptno"}, + Unique: false, + IndexTableName: "__mo_index_secondary_0193dc98-4148-74f4-808a", + TableExist: true, + }, + }, + Partition: &plan2.PartitionByDef{ + Type: plan2.PartitionType_KEY, + PartitionNum: 2, + Partitions: []*plan2.PartitionItem{ + { + PartitionName: "p0", + OrdinalPosition: 1, + PartitionTableName: "%!%p0%!%dept", + }, + { + PartitionName: "p1", + OrdinalPosition: 2, + PartitionTableName: "%!%p1%!%dept", + }, + }, + PartitionTableNames: []string{ + "%!%p0%!%dept", + "%!%p1%!%dept", + }, + }, + Defs: []*plan2.TableDef_DefType{ + { + Def: &plan.TableDef_DefType_Properties{ + Properties: &plan.PropertiesDef{ + Properties: []*plan.Property{ + { + Key: "relkind", + Value: "r", + }, + }, + }, + }, + }, + }, + } + + partitionTable1Def := &plan.TableDef{ + Name: "%!%p0%!%dept", + Cols: []*plan.ColDef{ + { + ColId: 0, + Name: "deptno", + Alg: plan2.CompressType_Lz4, + Typ: plan.Type{ + Id: 27, + NotNullable: false, + AutoIncr: true, + Width: 32, + Scale: -1, + }, + Default: &plan2.Default{}, + NotNull: true, + Primary: true, + Pkidx: 0, + }, + { + ColId: 1, + Name: "dname", + Alg: plan2.CompressType_Lz4, + Typ: plan.Type{ + Id: 61, + NotNullable: false, + AutoIncr: false, + Width: 15, + Scale: 0, + }, + Default: &plan2.Default{}, + NotNull: false, + Primary: false, + Pkidx: 0, + }, + { + ColId: 2, + Name: "loc", + Alg: plan2.CompressType_Lz4, + Typ: plan.Type{ + Id: 61, + NotNullable: false, + AutoIncr: false, + Width: 50, + Scale: 0, + }, + Default: &plan2.Default{}, + NotNull: false, + Primary: false, + Pkidx: 0, + }, + }, + Pkey: &plan.PrimaryKeyDef{ + Cols: nil, + PkeyColId: 0, + PkeyColName: "deptno", + Names: []string{"deptno"}, + }, + Defs: []*plan2.TableDef_DefType{ + { + Def: &plan.TableDef_DefType_Properties{ + Properties: &plan.PropertiesDef{ + Properties: []*plan.Property{ + { + Key: "relkind", + Value: "r", + }, + }, + }, + }, + }, + }, + } + + partitionTable2Def := &plan.TableDef{ + Name: "%!%p0%!%dept", + Cols: []*plan.ColDef{ + { + ColId: 0, + Name: "deptno", + Alg: plan2.CompressType_Lz4, + Typ: plan.Type{ + Id: 27, + NotNullable: false, + AutoIncr: true, + Width: 32, + Scale: -1, + }, + Default: &plan2.Default{}, + NotNull: true, + Primary: true, + Pkidx: 0, + }, + { + ColId: 1, + Name: "dname", + Alg: plan2.CompressType_Lz4, + Typ: plan.Type{ + Id: 61, + NotNullable: false, + AutoIncr: false, + Width: 15, + Scale: 0, + }, + Default: &plan2.Default{}, + NotNull: false, + Primary: false, + Pkidx: 0, + }, + { + ColId: 2, + Name: "loc", + Alg: plan2.CompressType_Lz4, + Typ: plan.Type{ + Id: 61, + NotNullable: false, + AutoIncr: false, + Width: 50, + Scale: 0, + }, + Default: &plan2.Default{}, + NotNull: false, + Primary: false, + Pkidx: 0, + }, + }, + Pkey: &plan.PrimaryKeyDef{ + Cols: nil, + PkeyColId: 0, + PkeyColName: "deptno", + Names: []string{"deptno"}, + }, + Defs: []*plan2.TableDef_DefType{ + { + Def: &plan.TableDef_DefType_Properties{ + Properties: &plan.PropertiesDef{ + Properties: []*plan.Property{ + { + Key: "relkind", + Value: "r", + }, + }, + }, + }, + }, + }, + } + + createTableDef := &plan2.CreateTable{ + IfNotExists: false, + Database: "test", + Replace: false, + TableDef: tableDef, + IndexTables: []*plan.TableDef{ + { + Name: "__mo_index_secondary_0193d918-3e7b-7506-9f70-64fbcf055c19", + Cols: []*plan.ColDef{ + { + Name: "__mo_index_idx_col", + Alg: plan2.CompressType_Lz4, + Typ: plan.Type{ + Id: 61, + NotNullable: false, + AutoIncr: false, + Width: 65535, + Scale: 0, + }, + NotNull: false, + Default: &plan2.Default{ + NullAbility: false, + }, + Pkidx: 0, + }, + { + Name: "__mo_index_pri_col", + Alg: plan2.CompressType_Lz4, + Typ: plan.Type{ + Id: 27, + NotNullable: false, + AutoIncr: false, + Width: 32, + Scale: -1, + }, + NotNull: false, + Default: &plan2.Default{ + NullAbility: false, + }, + Pkidx: 0, + }, + }, + Pkey: &plan2.PrimaryKeyDef{ + PkeyColName: "__mo_index_idx_col", + Names: []string{"__mo_index_idx_col"}, + }, + }, + }, + PartitionTables: []*plan.TableDef{ + partitionTable1Def, + partitionTable2Def, + }, + } + + cplan := &plan.Plan{ + Plan: &plan2.Plan_Ddl{ + Ddl: &plan2.DataDefinition{ + DdlType: plan2.DataDefinition_CREATE_TABLE, + Definition: &plan2.DataDefinition_CreateTable{ + CreateTable: createTableDef, + }, + }, + }, + } + + s := &Scope{ + Magic: CreateTable, + Plan: cplan, + TxnOffset: 0, + } + + sql := `create table dept( + deptno int unsigned auto_increment COMMENT '部门编号', + dname varchar(15) COMMENT '部门名称', + loc varchar(50) COMMENT '部门所在位置', + key idxloc (loc), + primary key(deptno) + ) partition by key(deptno) partitions 2` + + convey.Convey("create table FaultTolerance6", t, func() { + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + proc := testutil.NewProcess() + proc.Base.SessionInfo.Buf = buffer.New() + + ctx := context.Background() + proc.Ctx = context.Background() + txnCli, txnOp := newTestTxnClientAndOp(ctrl) + proc.Base.TxnClient = txnCli + proc.Base.TxnOperator = txnOp + proc.ReplaceTopCtx(ctx) + + relation := mock_frontend.NewMockRelation(ctrl) + relation.EXPECT().GetTableID(gomock.Any()).Return(uint64(1)).AnyTimes() + + mockDbMeta := mock_frontend.NewMockDatabase(ctrl) + mockDbMeta.EXPECT().Relation(gomock.Any(), catalog.MO_DATABASE, gomock.Any()).Return(relation, nil).AnyTimes() + mockDbMeta.EXPECT().RelationExists(gomock.Any(), gomock.Any(), gomock.Any()).Return(false, nil).AnyTimes() + mockDbMeta.EXPECT().Create(gomock.Any(), gomock.Any(), gomock.Any()).DoAndReturn(func(_ context.Context, tblName string, _ []engine.TableDef) error { + if tblName == "dept" { + return nil + } else if tblName == "%!%p0%!%dept" || tblName == "%!%p1%!%dept" { + return moerr.NewInternalErrorNoCtx("test err") + } + return nil + }).AnyTimes() + + eng := mock_frontend.NewMockEngine(ctrl) + eng.EXPECT().Database(gomock.Any(), gomock.Any(), gomock.Any()).Return(mockDbMeta, nil).AnyTimes() + + planDef2ExecDef := gostub.Stub(&planDefsToExeDefs, func(tbl *plan.TableDef) ([]engine.TableDef, error) { + if tbl.Name == "dept" { + return nil, nil + } else if tbl.Name == "%!%p0%!%dept" || tbl.Name == "%!%p1%!%dept" { + return nil, moerr.NewInternalErrorNoCtx("test err") + } + return nil, nil + }) + defer planDef2ExecDef.Reset() + + lockMoDb := gostub.Stub(&lockMoDatabase, func(_ *Compile, _ string, _ lock.LockMode) error { + return nil + }) + defer lockMoDb.Reset() + + lockMoTbl := gostub.Stub(&lockMoTable, func(_ *Compile, _ string, _ string, _ lock.LockMode) error { + return nil + }) + defer lockMoTbl.Reset() + + c := NewCompile("test", "test", sql, "", "", eng, proc, nil, false, nil, time.Now()) + assert.Error(t, s.CreateTable(c)) + }) + + convey.Convey("create table FaultTolerance7", t, func() { + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + proc := testutil.NewProcess() + proc.Base.SessionInfo.Buf = buffer.New() + + ctx := context.Background() + proc.Ctx = context.Background() + txnCli, txnOp := newTestTxnClientAndOp(ctrl) + proc.Base.TxnClient = txnCli + proc.Base.TxnOperator = txnOp + proc.ReplaceTopCtx(ctx) + + relation := mock_frontend.NewMockRelation(ctrl) + relation.EXPECT().GetTableID(gomock.Any()).Return(uint64(1)).AnyTimes() + + mockDbMeta := mock_frontend.NewMockDatabase(ctrl) + mockDbMeta.EXPECT().Relation(gomock.Any(), catalog.MO_DATABASE, gomock.Any()).Return(relation, nil).AnyTimes() + mockDbMeta.EXPECT().RelationExists(gomock.Any(), gomock.Any(), gomock.Any()).Return(false, nil).AnyTimes() + mockDbMeta.EXPECT().Create(gomock.Any(), gomock.Any(), gomock.Any()).DoAndReturn(func(_ context.Context, tblName string, _ []engine.TableDef) error { + if tblName == "dept" { + return nil + } else if tblName == "%!%p0%!%dept" || tblName == "%!%p1%!%dept" { + return moerr.NewInternalErrorNoCtx("test err") + } + return nil + }).AnyTimes() + + eng := mock_frontend.NewMockEngine(ctrl) + eng.EXPECT().Database(gomock.Any(), gomock.Any(), gomock.Any()).Return(mockDbMeta, nil).AnyTimes() + + lockMoDb := gostub.Stub(&lockMoDatabase, func(_ *Compile, _ string, _ lock.LockMode) error { + return nil + }) + defer lockMoDb.Reset() + + lockMoTbl := gostub.Stub(&lockMoTable, func(_ *Compile, _ string, _ string, _ lock.LockMode) error { + return nil + }) + defer lockMoTbl.Reset() + + c := NewCompile("test", "test", sql, "", "", eng, proc, nil, false, nil, time.Now()) + assert.Error(t, s.CreateTable(c)) + }) + + convey.Convey("create table FaultTolerance8", t, func() { + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + proc := testutil.NewProcess() + proc.Base.SessionInfo.Buf = buffer.New() + + ctx := context.Background() + proc.Ctx = context.Background() + txnCli, txnOp := newTestTxnClientAndOp(ctrl) + proc.Base.TxnClient = txnCli + proc.Base.TxnOperator = txnOp + proc.ReplaceTopCtx(ctx) + + relation := mock_frontend.NewMockRelation(ctrl) + relation.EXPECT().GetTableID(gomock.Any()).Return(uint64(1)).AnyTimes() + + mockDbMeta := mock_frontend.NewMockDatabase(ctrl) + mockDbMeta.EXPECT().Relation(gomock.Any(), catalog.MO_DATABASE, gomock.Any()).Return(relation, nil).AnyTimes() + mockDbMeta.EXPECT().RelationExists(gomock.Any(), gomock.Any(), gomock.Any()).Return(false, nil).AnyTimes() + mockDbMeta.EXPECT().Create(gomock.Any(), gomock.Any(), gomock.Any()).DoAndReturn(func(_ context.Context, tblName string, _ []engine.TableDef) error { + if tblName == "dept" { + return nil + } else if tblName == "%!%p0%!%dept" || tblName == "%!%p1%!%dept" { + return nil + } else if tblName == "__mo_index_secondary_0193d918-3e7b-7506-9f70-64fbcf055c19" { + return moerr.NewInternalErrorNoCtx("test err") + } + return nil + }).AnyTimes() + + eng := mock_frontend.NewMockEngine(ctrl) + eng.EXPECT().Database(gomock.Any(), gomock.Any(), gomock.Any()).Return(mockDbMeta, nil).AnyTimes() + + planDef2ExecDef := gostub.Stub(&planDefsToExeDefs, func(tbl *plan.TableDef) ([]engine.TableDef, error) { + if tbl.Name == "dept" { + return nil, nil + } else if tbl.Name == "%!%p0%!%dept" || tbl.Name == "%!%p1%!%dept" { + return nil, nil + } else if tbl.Name == "__mo_index_secondary_0193d918-3e7b-7506-9f70-64fbcf055c19" { + return nil, moerr.NewInternalErrorNoCtx("test err") + } + return nil, nil + }) + defer planDef2ExecDef.Reset() + + lockMoDb := gostub.Stub(&lockMoDatabase, func(_ *Compile, _ string, _ lock.LockMode) error { + return nil + }) + defer lockMoDb.Reset() + + lockMoTbl := gostub.Stub(&lockMoTable, func(_ *Compile, _ string, _ string, _ lock.LockMode) error { + return nil + }) + defer lockMoTbl.Reset() + + c := NewCompile("test", "test", sql, "", "", eng, proc, nil, false, nil, time.Now()) + assert.Error(t, s.CreateTable(c)) + }) + + convey.Convey("create table FaultTolerance9", t, func() { + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + proc := testutil.NewProcess() + proc.Base.SessionInfo.Buf = buffer.New() + + ctx := context.Background() + proc.Ctx = context.Background() + txnCli, txnOp := newTestTxnClientAndOp(ctrl) + proc.Base.TxnClient = txnCli + proc.Base.TxnOperator = txnOp + proc.ReplaceTopCtx(ctx) + + relation := mock_frontend.NewMockRelation(ctrl) + relation.EXPECT().GetTableID(gomock.Any()).Return(uint64(1)).AnyTimes() + + mockDbMeta := mock_frontend.NewMockDatabase(ctrl) + mockDbMeta.EXPECT().Relation(gomock.Any(), catalog.MO_DATABASE, gomock.Any()).Return(relation, nil).AnyTimes() + mockDbMeta.EXPECT().RelationExists(gomock.Any(), gomock.Any(), gomock.Any()).Return(false, nil).AnyTimes() + mockDbMeta.EXPECT().Create(gomock.Any(), gomock.Any(), gomock.Any()).DoAndReturn(func(_ context.Context, tblName string, _ []engine.TableDef) error { + if tblName == "dept" { + return nil + } else if tblName == "%!%p0%!%dept" || tblName == "%!%p1%!%dept" { + return nil + } else if tblName == "__mo_index_secondary_0193d918-3e7b-7506-9f70-64fbcf055c19" { + return moerr.NewInternalErrorNoCtx("test err") + } + return nil + }).AnyTimes() + + eng := mock_frontend.NewMockEngine(ctrl) + eng.EXPECT().Database(gomock.Any(), gomock.Any(), gomock.Any()).Return(mockDbMeta, nil).AnyTimes() + + planDef2ExecDef := gostub.Stub(&planDefsToExeDefs, func(tbl *plan.TableDef) ([]engine.TableDef, error) { + if tbl.Name == "dept" { + return nil, nil + } else if tbl.Name == "%!%p0%!%dept" || tbl.Name == "%!%p1%!%dept" { + return nil, nil + } else if tbl.Name == "__mo_index_secondary_0193d918-3e7b-7506-9f70-64fbcf055c19" { + return nil, nil + } + return nil, nil + }) + defer planDef2ExecDef.Reset() + + lockMoDb := gostub.Stub(&lockMoDatabase, func(_ *Compile, _ string, _ lock.LockMode) error { + return nil + }) + defer lockMoDb.Reset() + + lockMoTbl := gostub.Stub(&lockMoTable, func(_ *Compile, _ string, _ string, _ lock.LockMode) error { + return nil + }) + defer lockMoTbl.Reset() + + c := NewCompile("test", "test", sql, "", "", eng, proc, nil, false, nil, time.Now()) + assert.Error(t, s.CreateTable(c)) + }) + + convey.Convey("create table FaultTolerance10", t, func() { + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + proc := testutil.NewProcess() + proc.Base.SessionInfo.Buf = buffer.New() + + ctx := context.Background() + proc.Ctx = context.Background() + txnCli, txnOp := newTestTxnClientAndOp(ctrl) + proc.Base.TxnClient = txnCli + proc.Base.TxnOperator = txnOp + proc.ReplaceTopCtx(ctx) + + relation := mock_frontend.NewMockRelation(ctrl) + relation.EXPECT().GetTableID(gomock.Any()).Return(uint64(1)).AnyTimes() + + mockDbMeta := mock_frontend.NewMockDatabase(ctrl) + mockDbMeta.EXPECT().Relation(gomock.Any(), catalog.MO_DATABASE, gomock.Any()).Return(relation, nil).AnyTimes() + mockDbMeta.EXPECT().RelationExists(gomock.Any(), gomock.Any(), gomock.Any()).Return(false, nil).AnyTimes() + mockDbMeta.EXPECT().Create(gomock.Any(), gomock.Any(), gomock.Any()).DoAndReturn(func(_ context.Context, tblName string, _ []engine.TableDef) error { + if tblName == "dept" { + return nil + } else if tblName == "%!%p0%!%dept" || tblName == "%!%p1%!%dept" { + return nil + } else if tblName == "__mo_index_secondary_0193d918-3e7b-7506-9f70-64fbcf055c19" { + return nil + } + return nil + }).AnyTimes() + + eng := mock_frontend.NewMockEngine(ctrl) + eng.EXPECT().Database(gomock.Any(), gomock.Any(), gomock.Any()).Return(mockDbMeta, nil).AnyTimes() + + planDef2ExecDef := gostub.Stub(&planDefsToExeDefs, func(tbl *plan.TableDef) ([]engine.TableDef, error) { + if tbl.Name == "dept" { + return nil, nil + } else if tbl.Name == "%!%p0%!%dept" || tbl.Name == "%!%p1%!%dept" { + return nil, nil + } else if tbl.Name == "__mo_index_secondary_0193d918-3e7b-7506-9f70-64fbcf055c19" { + return nil, nil + } + return nil, nil + }) + defer planDef2ExecDef.Reset() + + lockMoDb := gostub.Stub(&lockMoDatabase, func(_ *Compile, _ string, _ lock.LockMode) error { + return nil + }) + defer lockMoDb.Reset() + + lockMoTbl := gostub.Stub(&lockMoTable, func(_ *Compile, _ string, _ string, _ lock.LockMode) error { + return nil + }) + defer lockMoTbl.Reset() + + createAutoIncrement := gostub.Stub(&maybeCreateAutoIncrement, func(_ context.Context, _ string, _ engine.Database, _ *plan.TableDef, _ client.TxnOperator, _ func() string) error { + return moerr.NewInternalErrorNoCtx("test err") + }) + defer createAutoIncrement.Reset() + + c := NewCompile("test", "test", sql, "", "", eng, proc, nil, false, nil, time.Now()) + assert.Error(t, s.CreateTable(c)) + }) } func TestScope_CreateView(t *testing.T) { diff --git a/pkg/sql/compile/util.go b/pkg/sql/compile/util.go index b631f6d369d20..511e1e9bbfda0 100644 --- a/pkg/sql/compile/util.go +++ b/pkg/sql/compile/util.go @@ -511,7 +511,7 @@ func genInsertMoTablePartitionsSql(databaseId string, tableId uint64, partitionB return buffer.String() } -func GetConstraintDef(ctx context.Context, rel engine.Relation) (*engine.ConstraintDef, error) { +var GetConstraintDef = func(ctx context.Context, rel engine.Relation) (*engine.ConstraintDef, error) { defs, err := rel.TableDefs(ctx) if err != nil { return nil, err diff --git a/test/distributed/cases/pessimistic_transaction/transaction_enhance.result b/test/distributed/cases/pessimistic_transaction/transaction_enhance.result index 50f7bef8860b9..46f390d70cd74 100644 --- a/test/distributed/cases/pessimistic_transaction/transaction_enhance.result +++ b/test/distributed/cases/pessimistic_transaction/transaction_enhance.result @@ -276,7 +276,7 @@ insert into atomic_table_17 values (6,"a"),(7,"b"); drop table atomic_table_17; use transaction_enhance; alter table atomic_table_17 add constraint unique key (c1); -SQL parser error: table "atomic_table_17" does not exist +no such table transaction_enhance.atomic_table_17 update atomic_table_17 set c1=8 where c2="b"; no such table transaction_enhance.atomic_table_17 commit; From b4cb3634fda307595ca2507afaccd95f699f8397 Mon Sep 17 00:00:00 2001 From: YANGGMM Date: Fri, 20 Dec 2024 17:10:54 +0800 Subject: [PATCH 12/26] support create database/table level snapshot (#20850) support create database/table level snapshot Approved by: @heni02, @daviszhen --- pkg/frontend/snapshot.go | 216 +++++++++++++++--- .../snapshot/snapshot_database_level.result | 80 +++++++ .../snapshot/snapshot_database_level.sql | 92 ++++++++ 3 files changed, 357 insertions(+), 31 deletions(-) create mode 100644 test/distributed/cases/snapshot/snapshot_database_level.result create mode 100644 test/distributed/cases/snapshot/snapshot_database_level.sql diff --git a/pkg/frontend/snapshot.go b/pkg/frontend/snapshot.go index d231cec7deba9..e6b828a3d4ca7 100644 --- a/pkg/frontend/snapshot.go +++ b/pkg/frontend/snapshot.go @@ -17,11 +17,13 @@ package frontend import ( "context" "fmt" + "math" "slices" "strings" "time" "github.com/google/uuid" + "go.uber.org/zap" "github.com/matrixorigin/matrixone/pkg/catalog" "github.com/matrixorigin/matrixone/pkg/common/moerr" @@ -188,21 +190,56 @@ func doCreateSnapshot(ctx context.Context, ses *Session, stmt *tree.CreateSnapSh return err } - // 2.only sys can create cluster level snapshot tenantInfo := ses.GetTenantInfo() currentAccount := tenantInfo.GetTenant() snapshotLevel = stmt.Object.SLevel.Level - if snapshotLevel == tree.SNAPSHOTLEVELCLUSTER && currentAccount != sysAccountName { - return moerr.NewInternalError(ctx, "only sys tenant can create cluster level snapshot") + + // 1.check create snapshot priv + err = doCheckCreateSnapshotPriv(ctx, ses, stmt) + if err != nil { + return err } - // 3.only sys can create tenant level snapshot for other tenant - if snapshotLevel == tree.SNAPSHOTLEVELACCOUNT { - snapshotForAccount = string(stmt.Object.ObjName) - if currentAccount != sysAccountName && currentAccount != snapshotForAccount { - return moerr.NewInternalError(ctx, "only sys tenant can create tenant level snapshot for other tenant") + // 2. check snapshot exists or not + snapshotName = string(stmt.Name) + snapshotExist, err = checkSnapShotExistOrNot(ctx, bh, snapshotName) + if err != nil { + return err + } + if snapshotExist { + if !stmt.IfNotExists { + return moerr.NewInternalErrorf(ctx, "snapshot %s already exists", snapshotName) + } else { + return nil } + } + + // 3.1 generate snapshot id + newUUid, err := uuid.NewV7() + if err != nil { + return err + } + snapshotId = newUUid.String() + // 3. get database name , table name and objId according to the snapshot level + switch snapshotLevel { + case tree.SNAPSHOTLEVELCLUSTER: + sql, err = getSqlForCreateSnapshot( + ctx, + snapshotId, + snapshotName, + time.Now().UTC().UnixNano(), + snapshotLevel.String(), + "", + "", + "", + math.MaxUint64, + ) + if err != nil { + return err + } + case tree.SNAPSHOTLEVELACCOUNT: + snapshotForAccount = string(stmt.Object.ObjName) // check account exists or not and get accountId getAccountIdFunc := func(accountName string) (accountId uint64, rtnErr error) { var erArray []ExecResult @@ -244,48 +281,165 @@ func doCreateSnapshot(ctx context.Context, ses *Session, stmt *tree.CreateSnapSh } else { objId = uint64(tenantInfo.GetTenantID()) } - } - // check snapshot exists or not - snapshotName = string(stmt.Name) - snapshotExist, err = checkSnapShotExistOrNot(ctx, bh, snapshotName) - if err != nil { - return err - } - if snapshotExist { - if !stmt.IfNotExists { - return moerr.NewInternalErrorf(ctx, "snapshot %s already exists", snapshotName) - } else { - return nil + sql, err = getSqlForCreateSnapshot( + ctx, + snapshotId, + snapshotName, + time.Now().UTC().UnixNano(), + snapshotLevel.String(), + snapshotForAccount, + "", + "", + objId, + ) + if err != nil { + return err } - } else { - // insert record to the system table + case tree.SNAPSHOTLEVELDATABASE: + databaseName = string(stmt.Object.ObjName) + if len(databaseName) > 0 && needSkipDb(databaseName) { + return moerr.NewInternalError(ctx, fmt.Sprintf("can not create snapshot for current database %s", databaseName)) + } + + getDatabaseIdFunc := func(dbName string) (dbId uint64, rtnErr error) { + var erArray []ExecResult + sql, rtnErr = getSqlForCheckDatabase(ctx, dbName) + if rtnErr != nil { + return 0, rtnErr + } + bh.ClearExecResultSet() + rtnErr = bh.Exec(ctx, sql) + if rtnErr != nil { + return 0, rtnErr + } + + erArray, rtnErr = getResultSet(ctx, bh) + if rtnErr != nil { + return 0, rtnErr + } - // 1. get snapshot id - newUUid, err := uuid.NewV7() + if execResultArrayHasData(erArray) { + for i := uint64(0); i < erArray[0].GetRowCount(); i++ { + dbId, rtnErr = erArray[0].GetUint64(ctx, i, 0) + if rtnErr != nil { + return 0, rtnErr + } + } + } else { + return 0, moerr.NewInternalErrorf(ctx, "database %s does not exist", dbName) + } + return dbId, rtnErr + } + objId, err = getDatabaseIdFunc(databaseName) + if err != nil { + return err + } + + sql, err = getSqlForCreateSnapshot( + ctx, + snapshotId, + snapshotName, + time.Now().UTC().UnixNano(), + snapshotLevel.String(), + currentAccount, + databaseName, + "", + objId, + ) if err != nil { return err } - snapshotId = newUUid.String() - // 2. get snapshot ts - // ts := ses.proc.TxnOperator.SnapshotTS() - // snapshotTs = ts.String() + case tree.SNAPSHOTLEVELTABLE: + objectName := string(stmt.Object.ObjName) + objects := strings.Split(objectName, ".") + if len(objects) != 2 { + return moerr.NewInternalError(ctx, fmt.Sprintf("invalid table name %s", objectName)) + } + databaseName = objects[0] + tableName = objects[1] + if len(databaseName) > 0 && needSkipDb(databaseName) { + return moerr.NewInternalError(ctx, fmt.Sprintf("can not create pitr for current table %s.%s", databaseName, tableName)) + } + + getTableIdFunc := func(dbName, tblName string) (tblId uint64, rtnErr error) { + var erArray []ExecResult + sql, rtnErr = getSqlForCheckDatabaseTable(ctx, dbName, tblName) + if rtnErr != nil { + return 0, rtnErr + } + bh.ClearExecResultSet() + rtnErr = bh.Exec(ctx, sql) + if rtnErr != nil { + return 0, rtnErr + } + + erArray, rtnErr = getResultSet(ctx, bh) + if rtnErr != nil { + return 0, rtnErr + } - sql, err = getSqlForCreateSnapshot(ctx, snapshotId, snapshotName, time.Now().UTC().UnixNano(), snapshotLevel.String(), string(stmt.Object.ObjName), databaseName, tableName, objId) + if execResultArrayHasData(erArray) { + for i := uint64(0); i < erArray[0].GetRowCount(); i++ { + tblId, rtnErr = erArray[0].GetUint64(ctx, i, 0) + if rtnErr != nil { + return 0, rtnErr + } + } + } else { + return 0, moerr.NewInternalErrorf(ctx, "table %s.%s does not exist", dbName, tblName) + } + return tblId, rtnErr + } + objId, err = getTableIdFunc(databaseName, tableName) if err != nil { return err } - err = bh.Exec(ctx, sql) + sql, err = getSqlForCreateSnapshot( + ctx, + snapshotId, + snapshotName, + time.Now().UTC().UnixNano(), + snapshotLevel.String(), + currentAccount, + databaseName, + tableName, + objId, + ) if err != nil { return err } } + + getLogger(ses.GetService()).Info("create pitr", zap.String("sql", sql)) + err = bh.Exec(ctx, sql) + if err != nil { + return err + } + getLogger(ses.GetService()).Info(fmt.Sprintf("create snapshot %s success", snapshotName)) + return err +} - // insert record to the system table +func doCheckCreateSnapshotPriv(ctx context.Context, ses *Session, stmt *tree.CreateSnapShot) error { + var err error + snapshotLevel := stmt.Object.SLevel.Level + tenantInfo := ses.GetTenantInfo() + currentAccount := tenantInfo.GetTenant() + switch snapshotLevel { + case tree.SNAPSHOTLEVELCLUSTER: + if currentAccount != sysAccountName { + return moerr.NewInternalError(ctx, "only sys tenant can create cluster level snapshot") + } + case tree.SNAPSHOTLEVELACCOUNT: + snapshotForAccount := string(stmt.Object.ObjName) + if currentAccount != sysAccountName && currentAccount != snapshotForAccount { + return moerr.NewInternalError(ctx, "only sys tenant can create tenant level snapshot for other tenant") + } + } return err } diff --git a/test/distributed/cases/snapshot/snapshot_database_level.result b/test/distributed/cases/snapshot/snapshot_database_level.result new file mode 100644 index 0000000000000..cf5e3861891be --- /dev/null +++ b/test/distributed/cases/snapshot/snapshot_database_level.result @@ -0,0 +1,80 @@ +drop snapshot if exists sn1; +create snapshot sn1 for database db1; +internal error: database db1 does not exist +show snapshots; +SNAPSHOT_NAME TIMESTAMP SNAPSHOT_LEVEL ACCOUNT_NAME DATABASE_NAME TABLE_NAME +drop database if exists db1; +create database if not exists db1; +create snapshot sn2 for database db1; +show snapshots; +SNAPSHOT_NAME TIMESTAMP SNAPSHOT_LEVEL ACCOUNT_NAME DATABASE_NAME TABLE_NAME +sn2 2024-12-20 02:58:51.442498 database sys db1 +drop database if exists db1; +drop snapshot if exists sn2; +drop snapshot if exists sn1; +show snapshots; +SNAPSHOT_NAME TIMESTAMP SNAPSHOT_LEVEL ACCOUNT_NAME DATABASE_NAME TABLE_NAME +drop account if exists acc01; +create account acc01 admin_name = 'test_account' identified by '111'; +drop snapshot if exists sn1; +create snapshot sn1 for database db1; +internal error: database db1 does not exist +show snapshots; +SNAPSHOT_NAME TIMESTAMP SNAPSHOT_LEVEL ACCOUNT_NAME DATABASE_NAME TABLE_NAME +drop database if exists db1; +create database if not exists db1; +create snapshot sn2 for database db1; +show snapshots; +SNAPSHOT_NAME TIMESTAMP SNAPSHOT_LEVEL ACCOUNT_NAME DATABASE_NAME TABLE_NAME +sn2 2024-12-20 02:58:51.951431 database acc01 db1 +drop database if exists db1; +drop snapshot if exists sn2; +drop snapshot if exists sn1; +drop account if exists acc1; +drop snapshot if exists sn1; +create snapshot sn1 for table db1 tbl1; +internal error: table db1.tbl1 does not exist +show snapshots; +SNAPSHOT_NAME TIMESTAMP SNAPSHOT_LEVEL ACCOUNT_NAME DATABASE_NAME TABLE_NAME +drop database if exists db1; +create database if not exists db1; +create table db1.tbl1 (a int); +insert into db1.tbl1 values (1), (2), (3); +create snapshot sn2 for table db1 tbl1; +show snapshots; +SNAPSHOT_NAME TIMESTAMP SNAPSHOT_LEVEL ACCOUNT_NAME DATABASE_NAME TABLE_NAME +sn2 2024-12-20 02:58:52.013035 table sys db1 tbl1 +drop database if exists db1; +drop snapshot if exists sn2; +drop snapshot if exists sn1; +show snapshots; +SNAPSHOT_NAME TIMESTAMP SNAPSHOT_LEVEL ACCOUNT_NAME DATABASE_NAME TABLE_NAME +drop account if exists acc01; +create account acc01 admin_name = 'test_account' identified by '111'; +drop snapshot if exists sn1; +create snapshot sn1 for table db1 tbl1; +internal error: table db1.tbl1 does not exist +show snapshots; +SNAPSHOT_NAME TIMESTAMP SNAPSHOT_LEVEL ACCOUNT_NAME DATABASE_NAME TABLE_NAME +drop database if exists db1; +create database if not exists db1; +create table db1.tbl1 (a int); +insert into db1.tbl1 values (1), (2), (3); +create snapshot sn2 for table db1 tbl1; +show snapshots; +SNAPSHOT_NAME TIMESTAMP SNAPSHOT_LEVEL ACCOUNT_NAME DATABASE_NAME TABLE_NAME +sn2 2024-12-20 02:58:52.689975 table acc01 db1 tbl1 +drop database if exists db1; +drop snapshot if exists sn2; +drop snapshot if exists sn1; +drop account if exists acc1; +create snapshot sn1 for account sys; +create snapshot sn1 for account sys; +internal error: snapshot sn1 already exists +create snapshot if not exists sn1 for account sys; +drop snapshot if exists sn1; +create snapshot sn1 for database mo_catalog; +internal error: can not create snapshot for current database mo_catalog +create snapshot sn1 for table mo_catalog mo_user; +internal error: can not create pitr for current table mo_catalog.mo_user +drop snapshot if exists sn1; diff --git a/test/distributed/cases/snapshot/snapshot_database_level.sql b/test/distributed/cases/snapshot/snapshot_database_level.sql new file mode 100644 index 0000000000000..b38d7f5e97c51 --- /dev/null +++ b/test/distributed/cases/snapshot/snapshot_database_level.sql @@ -0,0 +1,92 @@ +drop snapshot if exists sn1; +create snapshot sn1 for database db1; +-- @ignore:1 +show snapshots; + +drop database if exists db1; +create database if not exists db1; +create snapshot sn2 for database db1; +-- @ignore:1 +show snapshots; + +drop database if exists db1; +drop snapshot if exists sn2; +drop snapshot if exists sn1; +-- @ignore:1 +show snapshots; + +drop account if exists acc01; +create account acc01 admin_name = 'test_account' identified by '111'; + +-- @session:id=1&user=acc01:test_account&password=111 +drop snapshot if exists sn1; +create snapshot sn1 for database db1; +-- @ignore:1 +show snapshots; + +drop database if exists db1; +create database if not exists db1; +create snapshot sn2 for database db1; +-- @ignore:1 +show snapshots; + +drop database if exists db1; +drop snapshot if exists sn2; +drop snapshot if exists sn1; +-- @session + +drop account if exists acc1; + +drop snapshot if exists sn1; +create snapshot sn1 for table db1 tbl1; +-- @ignore:1 +show snapshots; + +drop database if exists db1; +create database if not exists db1; +create table db1.tbl1 (a int); +insert into db1.tbl1 values (1), (2), (3); +create snapshot sn2 for table db1 tbl1; +-- @ignore:1 +show snapshots; + +drop database if exists db1; +drop snapshot if exists sn2; +drop snapshot if exists sn1; +-- @ignore:1 +show snapshots; + +drop account if exists acc01; +create account acc01 admin_name = 'test_account' identified by '111'; + +-- @session:id=2&user=acc01:test_account&password=111 +drop snapshot if exists sn1; +create snapshot sn1 for table db1 tbl1; +-- @ignore:1 +show snapshots; + +drop database if exists db1; +create database if not exists db1; +create table db1.tbl1 (a int); +insert into db1.tbl1 values (1), (2), (3); +create snapshot sn2 for table db1 tbl1; +-- @ignore:1 +show snapshots; + +drop database if exists db1; +drop snapshot if exists sn2; +drop snapshot if exists sn1; +-- @session + +drop account if exists acc1; + +create snapshot sn1 for account sys; +create snapshot sn1 for account sys; +create snapshot if not exists sn1 for account sys; + +drop snapshot if exists sn1; + +create snapshot sn1 for database mo_catalog; +create snapshot sn1 for table mo_catalog mo_user; + +drop snapshot if exists sn1; From 51fda33589d1f7848867c9db7788f84368fa3680 Mon Sep 17 00:00:00 2001 From: ou yuanning <45346669+ouyuanning@users.noreply.github.com> Date: Fri, 20 Dec 2024 18:09:29 +0800 Subject: [PATCH 13/26] remove unused fields in s3writer (#20844) remove unused fields in s3writer Approved by: @badboynt1, @m-schen, @qingxinhome, @aunjgr, @zhangxu19830126 --- pkg/common/morpc/backend_test.go | 8 +- pkg/pb/pipeline/pipeline.pb.go | 876 ++++++++++------------- pkg/sql/colexec/multi_update/s3writer.go | 1 - pkg/sql/colexec/multi_update/types.go | 2 +- pkg/sql/compile/operator.go | 2 - pkg/sql/compile/remoterun.go | 2 - proto/pipeline.proto | 3 +- 7 files changed, 370 insertions(+), 524 deletions(-) diff --git a/pkg/common/morpc/backend_test.go b/pkg/common/morpc/backend_test.go index 8306c3e5ccd1c..2a955fccd3d9f 100644 --- a/pkg/common/morpc/backend_test.go +++ b/pkg/common/morpc/backend_test.go @@ -819,10 +819,10 @@ func TestCannotBusyLoopIfWriteCIsFull(t *testing.T) { for i := 0; i < 10; i++ { req := newTestMessage(1) f, err := b.Send(ctx, req) - assert.NoError(t, err) - - _, err = f.Get() - assert.NoError(t, err) + if err == nil { //ignore timeout + _, err = f.Get() + assert.NoError(t, err) + } } }() } diff --git a/pkg/pb/pipeline/pipeline.pb.go b/pkg/pb/pipeline/pipeline.pb.go index 288e5ba656551..d1e48388d7ce4 100644 --- a/pkg/pb/pipeline/pipeline.pb.go +++ b/pkg/pb/pipeline/pipeline.pb.go @@ -1043,8 +1043,7 @@ type MultiUpdate struct { Action uint32 `protobuf:"varint,2,opt,name=Action,proto3" json:"Action,omitempty"` IBucket uint32 `protobuf:"varint,3,opt,name=IBucket,proto3" json:"IBucket,omitempty"` NBucket uint32 `protobuf:"varint,4,opt,name=NBucket,proto3" json:"NBucket,omitempty"` - SegmentMap map[string]int32 `protobuf:"bytes,5,rep,name=SegmentMap,proto3" json:"SegmentMap,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"varint,2,opt,name=value,proto3"` - UpdateCtxList []*plan.UpdateCtx `protobuf:"bytes,6,rep,name=update_ctx_list,json=updateCtxList,proto3" json:"update_ctx_list,omitempty"` + UpdateCtxList []*plan.UpdateCtx `protobuf:"bytes,5,rep,name=update_ctx_list,json=updateCtxList,proto3" json:"update_ctx_list,omitempty"` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` @@ -1111,13 +1110,6 @@ func (m *MultiUpdate) GetNBucket() uint32 { return 0 } -func (m *MultiUpdate) GetSegmentMap() map[string]int32 { - if m != nil { - return m.SegmentMap - } - return nil -} - func (m *MultiUpdate) GetUpdateCtxList() []*plan.UpdateCtx { if m != nil { return m.UpdateCtxList @@ -5867,7 +5859,6 @@ func init() { proto.RegisterType((*Group)(nil), "pipeline.Group") proto.RegisterType((*Insert)(nil), "pipeline.Insert") proto.RegisterType((*MultiUpdate)(nil), "pipeline.MultiUpdate") - proto.RegisterMapType((map[string]int32)(nil), "pipeline.MultiUpdate.SegmentMapEntry") proto.RegisterType((*Array)(nil), "pipeline.Array") proto.RegisterType((*Map)(nil), "pipeline.Map") proto.RegisterMapType((map[string]int32)(nil), "pipeline.Map.MpEntry") @@ -5925,370 +5916,369 @@ func init() { func init() { proto.RegisterFile("pipeline.proto", fileDescriptor_7ac67a7adf3df9c7) } var fileDescriptor_7ac67a7adf3df9c7 = []byte{ - // 5803 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xec, 0x3c, 0x4b, 0x8c, 0x1d, 0x49, - 0x52, 0xf3, 0xfe, 0xf5, 0xe2, 0x7d, 0xfa, 0x75, 0xfa, 0xf7, 0xc6, 0xf6, 0xd8, 0xed, 0x1a, 0xdb, - 0xd3, 0xeb, 0x19, 0xb7, 0x67, 0x7a, 0xd6, 0xec, 0xc0, 0xb0, 0x3b, 0xdb, 0xee, 0xb6, 0x77, 0x7b, - 0xc6, 0xf6, 0x34, 0xd9, 0x6d, 0x46, 0xec, 0x81, 0x52, 0x75, 0x55, 0xbe, 0xd7, 0xb5, 0x5d, 0xaf, - 0xaa, 0x5c, 0x95, 0x65, 0x77, 0xfb, 0x84, 0x04, 0x57, 0x4e, 0x9c, 0x10, 0x42, 0x42, 0x7b, 0x00, - 0x71, 0x40, 0x20, 0x10, 0x27, 0xc4, 0x7d, 0xf7, 0x82, 0x38, 0x71, 0x44, 0x68, 0xb9, 0xf1, 0xb9, - 0x2d, 0x88, 0x0b, 0x12, 0x8a, 0xc8, 0xac, 0xcf, 0xfb, 0xb8, 0xfd, 0x99, 0x19, 0xb4, 0x2b, 0xed, - 0x2d, 0x33, 0x22, 0x32, 0x2b, 0x33, 0x22, 0x32, 0x32, 0x32, 0x32, 0xb2, 0xa0, 0x1f, 0x79, 0x91, - 0xf0, 0xbd, 0x40, 0xac, 0x45, 0x71, 0x28, 0x43, 0x66, 0x64, 0xf5, 0xf3, 0x37, 0xc7, 0x9e, 0x3c, - 0x48, 0xf7, 0xd7, 0x9c, 0x70, 0x72, 0x6b, 0x1c, 0x8e, 0xc3, 0x5b, 0x44, 0xb0, 0x9f, 0x8e, 0xa8, - 0x46, 0x15, 0x2a, 0xa9, 0x86, 0xe7, 0x21, 0xf2, 0xed, 0x40, 0x97, 0x97, 0xa4, 0x37, 0x11, 0x89, - 0xb4, 0x27, 0x51, 0x86, 0xf4, 0x43, 0xe7, 0x50, 0x97, 0xdb, 0xf2, 0x48, 0xd3, 0x99, 0x7f, 0x54, - 0x85, 0xd6, 0x03, 0x91, 0x24, 0xf6, 0x58, 0x30, 0x13, 0x6a, 0x89, 0xe7, 0x0e, 0x2b, 0x2b, 0x95, - 0xd5, 0xfe, 0xfa, 0x60, 0x2d, 0x1f, 0xd6, 0xae, 0xb4, 0x65, 0x9a, 0x70, 0x44, 0x22, 0x8d, 0x33, - 0x71, 0x87, 0xd5, 0x59, 0x9a, 0x07, 0x42, 0x1e, 0x84, 0x2e, 0x47, 0x24, 0x1b, 0x40, 0x4d, 0xc4, - 0xf1, 0xb0, 0xb6, 0x52, 0x59, 0xed, 0x72, 0x2c, 0x32, 0x06, 0x75, 0xd7, 0x96, 0xf6, 0xb0, 0x4e, - 0x20, 0x2a, 0xb3, 0xab, 0xd0, 0x8f, 0xe2, 0xd0, 0xb1, 0xbc, 0x60, 0x14, 0x5a, 0x84, 0x6d, 0x10, - 0xb6, 0x8b, 0xd0, 0xed, 0x60, 0x14, 0x6e, 0x21, 0xd5, 0x10, 0x5a, 0x76, 0x60, 0xfb, 0xc7, 0x89, - 0x18, 0x36, 0x09, 0x9d, 0x55, 0x59, 0x1f, 0xaa, 0x9e, 0x3b, 0x6c, 0xad, 0x54, 0x56, 0xeb, 0xbc, - 0xea, 0xb9, 0xf8, 0x8d, 0x34, 0xf5, 0xdc, 0xa1, 0xa1, 0xbe, 0x81, 0x65, 0x66, 0x42, 0x37, 0x10, - 0xc2, 0x7d, 0x18, 0x4a, 0x2e, 0x22, 0xff, 0x78, 0xd8, 0x5e, 0xa9, 0xac, 0x1a, 0x7c, 0x0a, 0xc6, - 0xce, 0x83, 0xe1, 0x8a, 0xfd, 0x74, 0xfc, 0x20, 0x19, 0x0f, 0x61, 0xa5, 0xb2, 0xda, 0xe6, 0x79, - 0xdd, 0x7c, 0x04, 0xed, 0xcd, 0x30, 0x08, 0x84, 0x23, 0xc3, 0x98, 0x5d, 0x86, 0x4e, 0x36, 0x5d, - 0x4b, 0xb3, 0xa9, 0xc1, 0x21, 0x03, 0x6d, 0xbb, 0xec, 0x1d, 0x58, 0x72, 0x32, 0x6a, 0xcb, 0x0b, - 0x5c, 0x71, 0x44, 0x7c, 0x6a, 0xf0, 0x7e, 0x0e, 0xde, 0x46, 0xa8, 0xf9, 0x1f, 0x55, 0x68, 0xed, - 0x1e, 0xa4, 0xa3, 0x91, 0x2f, 0xd8, 0x55, 0xe8, 0xe9, 0xe2, 0x66, 0xe8, 0x6f, 0xbb, 0x47, 0xba, - 0xdf, 0x69, 0x20, 0x5b, 0x81, 0x8e, 0x06, 0xec, 0x1d, 0x47, 0x42, 0x77, 0x5b, 0x06, 0x4d, 0xf7, - 0xf3, 0xc0, 0x0b, 0x88, 0xfd, 0x35, 0x3e, 0x0d, 0x9c, 0xa1, 0xb2, 0x8f, 0x48, 0x22, 0xd3, 0x54, - 0x36, 0x7d, 0x6d, 0xc3, 0xf7, 0x9e, 0x08, 0x2e, 0xc6, 0x9b, 0x81, 0x24, 0xb9, 0x34, 0x78, 0x19, - 0xc4, 0xd6, 0xe1, 0x4c, 0xa2, 0x9a, 0x58, 0xb1, 0x1d, 0x8c, 0x45, 0x62, 0xa5, 0x5e, 0x20, 0x7f, - 0xe5, 0x9b, 0xc3, 0xe6, 0x4a, 0x6d, 0xb5, 0xce, 0x4f, 0x69, 0x24, 0x27, 0xdc, 0x23, 0x42, 0xb1, - 0xf7, 0xe1, 0xf4, 0x4c, 0x1b, 0xd5, 0xa4, 0xb5, 0x52, 0x5b, 0xad, 0x71, 0x36, 0xd5, 0x64, 0x9b, - 0x5a, 0xdc, 0x85, 0xe5, 0x38, 0x0d, 0x50, 0x93, 0xef, 0x79, 0xbe, 0x14, 0xf1, 0x6e, 0x24, 0x1c, - 0x92, 0x6f, 0x67, 0xfd, 0xdc, 0x1a, 0x29, 0x3b, 0x9f, 0x45, 0xf3, 0xf9, 0x16, 0xe6, 0xff, 0x54, - 0xc1, 0xd8, 0xf2, 0x92, 0xc8, 0x96, 0xce, 0x01, 0x3b, 0x07, 0xad, 0x51, 0x1a, 0x38, 0x85, 0x04, - 0x9b, 0x58, 0xdd, 0x76, 0xd9, 0xaf, 0xc3, 0x92, 0x1f, 0x3a, 0xb6, 0x6f, 0xe5, 0xc2, 0x1a, 0x56, - 0x57, 0x6a, 0xab, 0x9d, 0xf5, 0x53, 0x85, 0x96, 0xe7, 0xca, 0xc0, 0xfb, 0x44, 0x5b, 0x28, 0xc7, - 0xb7, 0x61, 0x10, 0x8b, 0x49, 0x28, 0x45, 0xa9, 0x79, 0x8d, 0x9a, 0xb3, 0xa2, 0xf9, 0x17, 0xb1, - 0x1d, 0x3d, 0x0c, 0x5d, 0xc1, 0x97, 0x14, 0x6d, 0xd1, 0xfc, 0x83, 0x12, 0x3f, 0xc5, 0xd8, 0xf2, - 0xdc, 0x23, 0x8b, 0x3e, 0x30, 0xac, 0xaf, 0xd4, 0x56, 0x1b, 0x05, 0x73, 0xc4, 0x78, 0xdb, 0x3d, - 0xba, 0x8f, 0x18, 0xf6, 0x21, 0x9c, 0x9d, 0x6d, 0xa2, 0x7a, 0x1d, 0x36, 0xa8, 0xcd, 0xa9, 0xa9, - 0x36, 0x9c, 0x50, 0xec, 0x0a, 0x74, 0xb3, 0x46, 0x12, 0x15, 0xa9, 0xa9, 0x44, 0x9b, 0x94, 0x14, - 0xe9, 0x1c, 0xb4, 0xbc, 0xc4, 0x4a, 0xbc, 0xe0, 0x90, 0x16, 0x97, 0xc1, 0x9b, 0x5e, 0xb2, 0xeb, - 0x05, 0x87, 0xec, 0x4d, 0x30, 0x62, 0xe1, 0x28, 0x8c, 0x41, 0x98, 0x56, 0x2c, 0x1c, 0x42, 0x9d, - 0x03, 0x2c, 0x5a, 0x8e, 0x14, 0x7a, 0x89, 0x35, 0x63, 0xe1, 0x6c, 0x4a, 0x61, 0x26, 0xd0, 0x78, - 0x20, 0xe2, 0xb1, 0xc0, 0x55, 0x86, 0x0d, 0x77, 0x1d, 0x3b, 0x20, 0xbe, 0x1b, 0x3c, 0xaf, 0xe3, - 0x1a, 0x8f, 0xec, 0x58, 0x7a, 0xb6, 0x4f, 0x8a, 0x6d, 0xf0, 0xac, 0xca, 0x2e, 0x40, 0x3b, 0x91, - 0x76, 0x2c, 0x71, 0x76, 0xa4, 0xd0, 0x0d, 0x6e, 0x10, 0x00, 0xd7, 0xc4, 0x39, 0x68, 0x89, 0xc0, - 0x25, 0x54, 0x5d, 0x49, 0x52, 0x04, 0xee, 0xb6, 0x7b, 0x64, 0xfe, 0x4d, 0x05, 0x7a, 0x0f, 0x52, - 0x5f, 0x7a, 0x1b, 0xf1, 0x38, 0x15, 0x93, 0x40, 0xa2, 0x6d, 0xd8, 0xf2, 0x12, 0xa9, 0xbf, 0x4c, - 0x65, 0xb6, 0x0a, 0xed, 0xef, 0xc5, 0x61, 0x1a, 0xdd, 0x3d, 0x8a, 0x32, 0x49, 0x83, 0x52, 0x2a, - 0x84, 0xf0, 0x02, 0xc9, 0xde, 0x83, 0xce, 0xe7, 0xb1, 0x2b, 0xe2, 0x3b, 0xc7, 0x44, 0x5b, 0x9b, - 0xa3, 0x2d, 0xa3, 0xd9, 0x45, 0x68, 0xef, 0x8a, 0xc8, 0x8e, 0x6d, 0x54, 0x81, 0x3a, 0x19, 0x94, - 0x02, 0x80, 0x73, 0x25, 0xe2, 0x6d, 0x57, 0x2f, 0xab, 0xac, 0x6a, 0x8e, 0xa1, 0xbd, 0x31, 0x1e, - 0xc7, 0x62, 0x6c, 0x4b, 0x32, 0x6e, 0x61, 0x44, 0xc3, 0xad, 0xf1, 0x6a, 0x18, 0x91, 0x01, 0xc5, - 0x09, 0x28, 0xfe, 0x50, 0x99, 0x5d, 0x82, 0xba, 0x58, 0x3c, 0x1e, 0x82, 0xb3, 0xb3, 0xd0, 0x74, - 0xc2, 0x60, 0xe4, 0x8d, 0xb5, 0xd9, 0xd5, 0x35, 0xf3, 0xf7, 0x6b, 0xd0, 0xa0, 0xc9, 0x21, 0x7b, - 0xd1, 0x14, 0x5a, 0xe2, 0x89, 0xed, 0x67, 0x52, 0x41, 0xc0, 0xdd, 0x27, 0xb6, 0xcf, 0x56, 0xa0, - 0x81, 0xdd, 0x24, 0x0b, 0x78, 0xa3, 0x10, 0xec, 0x3a, 0x34, 0x50, 0x89, 0x92, 0xe9, 0x11, 0xa0, - 0x12, 0xdd, 0xa9, 0xff, 0xf8, 0x9f, 0x2f, 0xbf, 0xc1, 0x15, 0x9a, 0xbd, 0x03, 0x75, 0x7b, 0x3c, - 0x4e, 0x48, 0x97, 0xa7, 0x96, 0x53, 0x3e, 0x5f, 0x4e, 0x04, 0xec, 0x36, 0xb4, 0x95, 0xdc, 0x90, - 0xba, 0x41, 0xd4, 0xe7, 0x4a, 0x5b, 0x4c, 0x59, 0xa4, 0xbc, 0xa0, 0x44, 0x8e, 0x7b, 0x89, 0xb6, - 0x60, 0xa4, 0xd1, 0x06, 0x2f, 0x00, 0xb8, 0x07, 0x44, 0xb1, 0xd8, 0xf0, 0xfd, 0xd0, 0xd9, 0xf5, - 0x9e, 0x09, 0xbd, 0x63, 0x4c, 0xc1, 0xd8, 0x75, 0xe8, 0xef, 0x28, 0x95, 0xe3, 0x22, 0x49, 0x7d, - 0x99, 0xe8, 0x5d, 0x64, 0x06, 0xca, 0xd6, 0x80, 0x4d, 0x41, 0xf6, 0x68, 0xfa, 0xed, 0x95, 0xda, - 0x6a, 0x8f, 0x2f, 0xc0, 0xb0, 0xb7, 0xa1, 0x37, 0x46, 0x4e, 0x7b, 0xc1, 0xd8, 0x1a, 0xf9, 0x36, - 0x6e, 0x30, 0x35, 0xdc, 0x80, 0x32, 0xe0, 0x3d, 0xdf, 0x1e, 0x9b, 0x3f, 0xab, 0x42, 0x73, 0x3b, - 0x48, 0x44, 0x2c, 0x71, 0x95, 0xd8, 0xa3, 0x91, 0x70, 0xa4, 0x50, 0xd6, 0xa9, 0xce, 0xf3, 0x3a, - 0xce, 0x72, 0x2f, 0xfc, 0x22, 0xf6, 0xa4, 0xd8, 0xfd, 0x50, 0xeb, 0x41, 0x01, 0x60, 0x37, 0x60, - 0xd9, 0x76, 0x5d, 0x2b, 0xa3, 0xb6, 0xe2, 0xf0, 0x69, 0x42, 0x2b, 0xc6, 0xe0, 0x4b, 0xb6, 0xeb, - 0x6e, 0x68, 0x38, 0x0f, 0x9f, 0x26, 0xec, 0x0a, 0xd4, 0x62, 0x31, 0x22, 0xad, 0xe8, 0xac, 0x2f, - 0x29, 0xa9, 0x7d, 0xbe, 0xff, 0x43, 0xe1, 0x48, 0x2e, 0x46, 0x1c, 0x71, 0xec, 0x34, 0x34, 0x6c, - 0x29, 0x63, 0x25, 0x85, 0x36, 0x57, 0x15, 0xb6, 0x06, 0xa7, 0x68, 0x65, 0x4a, 0x2f, 0x0c, 0x2c, - 0x69, 0xef, 0xfb, 0xb8, 0x11, 0x26, 0xda, 0xe6, 0x2f, 0xe7, 0xa8, 0x3d, 0xc4, 0x6c, 0xbb, 0x09, - 0xee, 0x12, 0xb3, 0xf4, 0x81, 0x3d, 0x11, 0x09, 0x99, 0xfc, 0x36, 0x3f, 0x35, 0xdd, 0xe2, 0x21, - 0xa2, 0x90, 0x65, 0x45, 0x1b, 0x5c, 0xdb, 0x06, 0x2d, 0x93, 0x6e, 0x0e, 0xc4, 0xa5, 0x7f, 0x06, - 0x9a, 0x5e, 0x62, 0x89, 0xc0, 0xd5, 0xe6, 0xa6, 0xe1, 0x25, 0x77, 0x03, 0x97, 0xbd, 0x0b, 0x6d, - 0xf5, 0x15, 0x57, 0x8c, 0x68, 0x2f, 0xef, 0xac, 0xf7, 0xb5, 0x52, 0x22, 0x78, 0x4b, 0x8c, 0xb8, - 0x21, 0x75, 0xc9, 0xfc, 0xfb, 0x2a, 0x74, 0x48, 0x87, 0x1e, 0x45, 0x2e, 0x2e, 0xb9, 0xb7, 0xa1, - 0x37, 0xcd, 0x3d, 0x25, 0x80, 0xae, 0x5d, 0x66, 0xdd, 0x59, 0x68, 0x6e, 0x38, 0x38, 0x0a, 0x92, - 0x40, 0x8f, 0xeb, 0x1a, 0x2e, 0xeb, 0xed, 0x3b, 0xa9, 0x73, 0x28, 0x24, 0x31, 0xbd, 0xc7, 0xb3, - 0x2a, 0x62, 0x1e, 0x6a, 0x4c, 0x5d, 0x61, 0x74, 0x95, 0xdd, 0x05, 0xd8, 0x15, 0xe3, 0x89, 0x08, - 0xe4, 0x03, 0x3b, 0xd2, 0xea, 0x7e, 0x6d, 0x46, 0xdd, 0xd5, 0xd8, 0xd6, 0x0a, 0xba, 0xbb, 0x81, - 0x8c, 0x8f, 0x79, 0xa9, 0x21, 0xfb, 0x16, 0x2c, 0xa5, 0x44, 0x65, 0x39, 0xf2, 0xc8, 0xf2, 0xd1, - 0x4a, 0x34, 0xa9, 0x2f, 0x2d, 0x59, 0xd5, 0xc5, 0xa6, 0x3c, 0xe2, 0xbd, 0x34, 0x2b, 0xde, 0xf7, - 0x12, 0x79, 0xfe, 0xdb, 0xb0, 0x34, 0xd3, 0x2f, 0x7a, 0x6e, 0x87, 0xe2, 0x98, 0x66, 0xde, 0xe6, - 0x58, 0x44, 0x45, 0x78, 0x62, 0xfb, 0x69, 0xe6, 0x72, 0xa8, 0xca, 0xaf, 0x55, 0x3f, 0xaa, 0x98, - 0x6f, 0x41, 0x63, 0x23, 0x8e, 0x6d, 0x22, 0xb1, 0xb1, 0x30, 0xac, 0xd0, 0xbe, 0xa3, 0x2a, 0xa6, - 0x03, 0x35, 0x1c, 0xdd, 0x35, 0xa8, 0x4e, 0x22, 0xc2, 0x74, 0xd6, 0xcf, 0x94, 0x26, 0x67, 0x47, - 0x6b, 0x0f, 0xf4, 0x64, 0xaa, 0x93, 0xe8, 0xfc, 0x6d, 0x68, 0x3d, 0x78, 0x8d, 0x31, 0xfc, 0x57, - 0x1d, 0x8c, 0x2d, 0xe1, 0x0b, 0x92, 0x81, 0x09, 0xdd, 0xb2, 0x9a, 0x67, 0xf2, 0x9b, 0x52, 0x7d, - 0x13, 0xba, 0x6a, 0x27, 0xa4, 0x56, 0x42, 0xaf, 0xa3, 0x29, 0xd8, 0x6b, 0xc9, 0xf2, 0x22, 0x40, - 0x1c, 0x3e, 0xb5, 0x3c, 0xb5, 0x1d, 0x29, 0xcb, 0x6e, 0xc4, 0xe1, 0xd3, 0x6d, 0xdc, 0x90, 0xfe, - 0x5f, 0xd6, 0xcd, 0xb7, 0x60, 0x58, 0x5a, 0x37, 0xe8, 0x66, 0x5a, 0x5e, 0x60, 0xed, 0xa3, 0xcf, - 0xa3, 0x97, 0x50, 0xd1, 0x27, 0x79, 0xa1, 0xdb, 0xc1, 0x1d, 0x72, 0x88, 0xb4, 0x35, 0x68, 0x9f, - 0x60, 0x0d, 0x16, 0x1a, 0x17, 0x58, 0x6c, 0x5c, 0xee, 0x4c, 0x69, 0x75, 0x87, 0x04, 0x6f, 0x16, - 0x82, 0xcf, 0xa4, 0x75, 0xa2, 0x4a, 0x5f, 0x81, 0xae, 0x63, 0x07, 0x96, 0x8c, 0xd3, 0xc0, 0xb1, - 0xa5, 0x18, 0x76, 0xe9, 0x53, 0x1d, 0xc7, 0x0e, 0xf6, 0x34, 0xa8, 0x64, 0x01, 0x7a, 0x65, 0x0b, - 0x70, 0x1d, 0x96, 0xa2, 0xd8, 0x9b, 0xd8, 0xf1, 0xb1, 0x75, 0x28, 0x8e, 0x49, 0x18, 0x7d, 0xe5, - 0x4f, 0x6b, 0xf0, 0x67, 0xe2, 0x78, 0xdb, 0x3d, 0xfa, 0xb2, 0xba, 0xff, 0x4f, 0x55, 0x68, 0xef, - 0xc4, 0x42, 0x5b, 0xed, 0xcb, 0xd0, 0x49, 0x9c, 0x03, 0x31, 0xb1, 0x49, 0x4a, 0xba, 0x07, 0x50, - 0x20, 0x14, 0xce, 0xb4, 0x5d, 0xaa, 0x9e, 0x6c, 0x97, 0x70, 0x1c, 0xca, 0xdb, 0xc1, 0xc5, 0x84, - 0xc5, 0xc2, 0x18, 0xd7, 0xcb, 0xc6, 0x78, 0x05, 0xba, 0x07, 0x76, 0x62, 0xd9, 0xa9, 0x0c, 0x2d, - 0x27, 0xf4, 0x49, 0xe9, 0x0c, 0x0e, 0x07, 0x76, 0xb2, 0x91, 0xca, 0x70, 0x33, 0x24, 0xef, 0xc9, - 0x4b, 0x2c, 0xb5, 0xe8, 0xf5, 0xbe, 0x68, 0x78, 0x89, 0x36, 0x77, 0x6b, 0x70, 0x4a, 0x24, 0xd2, - 0x9b, 0xd8, 0x5a, 0xa0, 0x96, 0x13, 0xa6, 0x81, 0xa4, 0xdd, 0xb1, 0xc6, 0x97, 0x73, 0x14, 0x0f, - 0x9f, 0x6e, 0x22, 0x82, 0xbd, 0x0f, 0x7d, 0x27, 0x9c, 0x44, 0x56, 0x84, 0x7c, 0x25, 0xbf, 0x43, - 0x39, 0xe2, 0x65, 0xbf, 0xa0, 0x8b, 0x14, 0x3b, 0x87, 0x42, 0x39, 0x42, 0xeb, 0xb0, 0xe4, 0xf8, - 0x69, 0x22, 0x45, 0x6c, 0xed, 0xeb, 0x26, 0xed, 0xb9, 0x26, 0x3d, 0x4d, 0xa2, 0x9c, 0x27, 0x64, - 0x6c, 0x6b, 0x27, 0x4c, 0xe4, 0xd6, 0xc4, 0xcf, 0x14, 0xb3, 0xf2, 0xaa, 0x8a, 0x59, 0x5d, 0xac, - 0x98, 0x0b, 0x54, 0xa3, 0xb6, 0x40, 0x35, 0xd8, 0x2a, 0x0c, 0xca, 0x74, 0x24, 0x52, 0xe5, 0xc6, - 0xf5, 0x0b, 0x42, 0x12, 0xab, 0xe2, 0xaf, 0xab, 0x2c, 0x49, 0x23, 0xe3, 0xaf, 0xb6, 0x22, 0x0a, - 0xe9, 0x91, 0x86, 0x14, 0xcc, 0xd7, 0x1a, 0xf3, 0xab, 0xf0, 0x66, 0xde, 0xd2, 0x7a, 0xea, 0xc9, - 0x83, 0x30, 0x95, 0xd6, 0x88, 0x4e, 0x2c, 0x89, 0xf6, 0xba, 0xcf, 0x66, 0x3d, 0x7d, 0xa1, 0xd0, - 0xea, 0x3c, 0x43, 0x3e, 0xd2, 0x28, 0xf5, 0x7d, 0x4b, 0x8a, 0x23, 0xa9, 0x45, 0x30, 0x54, 0xbc, - 0xd1, 0x7c, 0xbb, 0x97, 0xfa, 0xfe, 0x9e, 0x38, 0x92, 0x68, 0xf1, 0x8d, 0x91, 0xae, 0x98, 0x7f, - 0x5b, 0x03, 0xb8, 0x1f, 0x3a, 0x87, 0x7b, 0x76, 0x3c, 0x16, 0x12, 0x7d, 0xf9, 0xcc, 0x0e, 0x69, - 0x3b, 0xd9, 0x92, 0xca, 0xfa, 0xb0, 0x75, 0x38, 0x9b, 0xcd, 0xdf, 0x09, 0x7d, 0x3a, 0x57, 0x28, - 0x43, 0xa2, 0x97, 0x01, 0xd3, 0x58, 0x75, 0x32, 0x25, 0x2b, 0xc2, 0x3e, 0x2a, 0x78, 0x8b, 0x6d, - 0xe4, 0x71, 0x44, 0xbc, 0x5d, 0xe4, 0x13, 0xf6, 0x8a, 0xe6, 0x7b, 0xc7, 0x11, 0x7b, 0x1f, 0xce, - 0xc4, 0x62, 0x14, 0x8b, 0xe4, 0xc0, 0x92, 0x49, 0xf9, 0x63, 0xca, 0xa5, 0x5f, 0xd6, 0xc8, 0xbd, - 0x24, 0xff, 0xd6, 0xfb, 0x70, 0x46, 0x71, 0x6a, 0x76, 0x78, 0xca, 0xea, 0x2e, 0x2b, 0x64, 0x79, - 0x74, 0x6f, 0x01, 0x05, 0x3f, 0x94, 0x25, 0xcd, 0x1c, 0x44, 0x9f, 0x98, 0xb1, 0xef, 0x0b, 0x74, - 0xac, 0x36, 0x0f, 0xf0, 0xd4, 0xb9, 0x25, 0x46, 0x9a, 0xf9, 0x05, 0x80, 0x99, 0x50, 0x7f, 0x10, - 0xba, 0x82, 0x58, 0xdd, 0x5f, 0xef, 0xaf, 0x51, 0x18, 0x05, 0x39, 0x89, 0x50, 0x4e, 0x38, 0xf6, - 0x0e, 0x50, 0x77, 0x4a, 0xfd, 0xe6, 0x75, 0xdc, 0x40, 0x24, 0xe9, 0xe0, 0xfb, 0x70, 0xa6, 0x18, - 0x89, 0x65, 0x4b, 0x4b, 0x1e, 0x08, 0x32, 0x62, 0xca, 0x98, 0x2e, 0xe7, 0x83, 0xda, 0x90, 0x7b, - 0x07, 0xe2, 0x6e, 0xe0, 0x9a, 0x1f, 0x41, 0x13, 0x3f, 0xf6, 0x79, 0xc4, 0xd6, 0xa0, 0x25, 0x49, - 0x78, 0x89, 0xde, 0x4e, 0x4f, 0x17, 0x56, 0xb5, 0x90, 0x2c, 0xcf, 0x88, 0x4c, 0x0e, 0x4b, 0xb9, - 0x89, 0x7a, 0x14, 0x78, 0x8f, 0x53, 0xc1, 0x3e, 0x81, 0xe5, 0x28, 0x16, 0x5a, 0x29, 0xad, 0xf4, - 0x10, 0x3d, 0x06, 0xbd, 0xbe, 0x4e, 0x6b, 0x1d, 0xca, 0x5b, 0x1c, 0xa2, 0xfe, 0xf4, 0xa3, 0xa9, - 0xba, 0xf9, 0x03, 0x38, 0x97, 0x53, 0xec, 0x0a, 0x27, 0x0c, 0x5c, 0x3b, 0x3e, 0xa6, 0xdd, 0x64, - 0xa6, 0xef, 0xe4, 0x55, 0xfa, 0xde, 0xa5, 0xbe, 0x7f, 0x54, 0x83, 0xfe, 0xe7, 0xc1, 0x56, 0x1a, - 0xf9, 0x1e, 0x5a, 0xf8, 0xcf, 0x94, 0x01, 0x56, 0x86, 0xaf, 0x52, 0x36, 0x7c, 0xab, 0x30, 0xd0, - 0x5f, 0x41, 0x05, 0x50, 0x66, 0x4b, 0xc7, 0x59, 0x14, 0x7c, 0x33, 0xf4, 0x95, 0xcd, 0xfa, 0x36, - 0x9c, 0x49, 0x69, 0xe6, 0x8a, 0xf2, 0x40, 0x38, 0x87, 0xd6, 0x73, 0x8e, 0x4c, 0x4c, 0x11, 0x62, - 0x53, 0x24, 0x23, 0x03, 0x76, 0x19, 0x3a, 0x45, 0xf3, 0xcc, 0xfa, 0x42, 0x4e, 0x48, 0x23, 0x09, - 0x03, 0xcb, 0xcd, 0x86, 0xac, 0xf7, 0x7e, 0xb4, 0xdb, 0xfd, 0xb0, 0x98, 0x09, 0x1a, 0x95, 0xdf, - 0x82, 0xe5, 0x29, 0x4a, 0x1a, 0x85, 0x72, 0xd3, 0x6e, 0x16, 0x62, 0x9c, 0x9e, 0x7e, 0xb9, 0x8a, - 0xe3, 0x51, 0xfb, 0xe4, 0x52, 0x38, 0x0d, 0xcd, 0x0c, 0xcd, 0x38, 0x08, 0x63, 0xa1, 0xd5, 0x17, - 0x0d, 0x0d, 0xd5, 0xcf, 0x3f, 0x84, 0xd3, 0x8b, 0x7a, 0x59, 0xb0, 0xd9, 0xad, 0x94, 0x37, 0xbb, - 0x99, 0xe3, 0x5e, 0xb1, 0xf1, 0xfd, 0x59, 0x05, 0x3a, 0xf7, 0xd2, 0x67, 0xcf, 0x8e, 0x95, 0x39, - 0x62, 0x5d, 0xa8, 0x3c, 0xa4, 0x5e, 0xaa, 0xbc, 0xf2, 0x10, 0xbd, 0xe3, 0x9d, 0x43, 0x34, 0x8d, - 0xd4, 0x49, 0x9b, 0xeb, 0x1a, 0x1e, 0x14, 0x77, 0x0e, 0xf7, 0x4e, 0x30, 0x0a, 0x0a, 0x8d, 0xc7, - 0x9f, 0x3b, 0xa9, 0xe7, 0xa3, 0xcf, 0xa4, 0xd7, 0x7f, 0x5e, 0xc7, 0xa3, 0xd7, 0xf6, 0x48, 0xe9, - 0xcb, 0xbd, 0x38, 0x9c, 0x28, 0x8d, 0xd6, 0x56, 0x77, 0x01, 0xc6, 0xfc, 0x49, 0x0d, 0xea, 0x9f, - 0x86, 0x5e, 0xa0, 0xc2, 0x16, 0xbe, 0x72, 0x8c, 0x95, 0x87, 0xda, 0x8a, 0x85, 0x8f, 0x1e, 0x30, - 0xa2, 0x50, 0x31, 0x7c, 0x75, 0xb2, 0x26, 0x94, 0x13, 0x2a, 0x54, 0x71, 0xb8, 0xae, 0x2c, 0x3c, - 0x5c, 0xe7, 0x67, 0xdf, 0xfa, 0x8b, 0xce, 0xbe, 0x6d, 0x5f, 0x8c, 0x50, 0x55, 0x03, 0x57, 0xfb, - 0xf8, 0xd3, 0xa6, 0x41, 0x8c, 0xe4, 0x66, 0x18, 0xb8, 0xec, 0x1b, 0x00, 0xb1, 0x37, 0x3e, 0xd0, - 0x94, 0xcd, 0xf9, 0x78, 0x04, 0x61, 0x89, 0x94, 0xc3, 0x9b, 0x3a, 0xc8, 0xa5, 0xf7, 0x0c, 0x6b, - 0x1f, 0xb9, 0xa4, 0xe6, 0xd1, 0xca, 0x8e, 0xcd, 0x8b, 0xc3, 0x63, 0x67, 0xa7, 0xc2, 0x63, 0xc4, - 0x5d, 0x9a, 0xef, 0x45, 0x40, 0xcf, 0xe1, 0xc0, 0x0a, 0x03, 0x2b, 0xca, 0xc2, 0x3b, 0x06, 0x42, - 0x3e, 0x0f, 0x76, 0x0e, 0xd1, 0x82, 0x7a, 0x89, 0xa5, 0xa3, 0x44, 0xfa, 0xcc, 0x55, 0x3a, 0x62, - 0xaf, 0x40, 0xf7, 0x87, 0xa1, 0x17, 0x58, 0x13, 0x3b, 0xb2, 0xa4, 0xad, 0xc2, 0xa8, 0x0d, 0x0e, - 0x08, 0x7b, 0x60, 0x47, 0x7b, 0xf6, 0x98, 0x5c, 0x24, 0x1d, 0x77, 0xc2, 0x45, 0xd2, 0x51, 0x04, - 0x1a, 0x84, 0xe2, 0xbd, 0x00, 0x6d, 0xea, 0x82, 0xa2, 0x52, 0x5d, 0x25, 0x7b, 0x04, 0x20, 0x47, - 0xcd, 0x7f, 0xab, 0x82, 0xb1, 0x11, 0x48, 0x8f, 0xe4, 0x79, 0x16, 0x9a, 0x31, 0x1d, 0xb1, 0xb5, - 0x34, 0x75, 0x2d, 0x97, 0x58, 0xf5, 0x39, 0x12, 0x9b, 0x92, 0x44, 0xed, 0xa5, 0x25, 0x51, 0x3f, - 0x49, 0x12, 0xd3, 0x5c, 0x6b, 0x9c, 0xc8, 0xb5, 0xb9, 0xc0, 0xc4, 0xd7, 0x21, 0xc6, 0x59, 0x49, - 0x18, 0x2f, 0x92, 0x44, 0x7b, 0x56, 0x12, 0xe6, 0x5f, 0xd5, 0xc0, 0xb8, 0x2f, 0x46, 0xf2, 0x97, - 0x8b, 0xe7, 0x17, 0x65, 0xf1, 0x98, 0xff, 0x59, 0x83, 0x36, 0xc7, 0x19, 0x7e, 0x8d, 0x32, 0xbb, - 0x05, 0x40, 0xb2, 0x38, 0x59, 0x70, 0x24, 0x2f, 0x15, 0xfb, 0xfa, 0x00, 0x3a, 0x4a, 0x26, 0xaa, - 0x45, 0xe3, 0x39, 0x2d, 0x94, 0xe0, 0xf6, 0xe6, 0xe5, 0xdd, 0x7c, 0x69, 0x79, 0xb7, 0x5e, 0x5b, - 0xde, 0xc6, 0x57, 0x21, 0xef, 0xf6, 0x89, 0xf2, 0x86, 0x17, 0xc9, 0xbb, 0xf3, 0x22, 0x79, 0x77, - 0xe7, 0xe4, 0xfd, 0xa3, 0x1a, 0xf4, 0x48, 0xde, 0xbb, 0x62, 0xf2, 0xe5, 0x8c, 0xe2, 0x8c, 0x90, - 0x6a, 0xaf, 0x2a, 0xa4, 0xfa, 0x4b, 0x0b, 0xa9, 0xf1, 0xda, 0x42, 0x6a, 0x7e, 0x15, 0x42, 0x6a, - 0x9d, 0x28, 0x24, 0xe3, 0x45, 0x42, 0x6a, 0xbf, 0xfa, 0xa2, 0xcc, 0x85, 0xf4, 0xa5, 0x77, 0xae, - 0x5f, 0x0a, 0xe9, 0x2b, 0x12, 0x12, 0xcc, 0x09, 0x09, 0x3d, 0x8b, 0x2f, 0xbd, 0x88, 0xbe, 0x0e, - 0xcf, 0xe2, 0x44, 0x66, 0x37, 0xbe, 0x0a, 0x66, 0x37, 0x4f, 0x64, 0x76, 0xeb, 0x45, 0xcc, 0x7e, - 0x0d, 0xcf, 0xe2, 0xaf, 0x6b, 0x00, 0xbb, 0x5e, 0x30, 0xf6, 0xc5, 0x2f, 0x7d, 0x8b, 0x5f, 0x18, - 0xdf, 0xe2, 0xef, 0xaa, 0x60, 0x3c, 0xb0, 0xe3, 0xc3, 0x9f, 0xbb, 0x15, 0xf2, 0x36, 0xb4, 0xc2, - 0xa0, 0xbc, 0x1e, 0xca, 0x74, 0xcd, 0x30, 0xf8, 0xb9, 0x50, 0xf9, 0x9f, 0x34, 0xa0, 0xbd, 0x25, - 0xdc, 0x34, 0xfa, 0x12, 0x1a, 0xff, 0x8b, 0x62, 0x5e, 0x5e, 0x70, 0xdc, 0x99, 0xe5, 0x66, 0xeb, - 0x45, 0xdc, 0x34, 0xe6, 0x0e, 0x89, 0xf7, 0xe1, 0xd4, 0x54, 0x14, 0xc5, 0x56, 0x57, 0x71, 0x6d, - 0x0a, 0xcd, 0x5d, 0x54, 0xe3, 0x7d, 0x18, 0xba, 0x53, 0x81, 0x14, 0x75, 0x41, 0xc7, 0x97, 0xc3, - 0x59, 0x10, 0xbb, 0x0a, 0x7d, 0x17, 0x45, 0x43, 0xc1, 0x21, 0x0a, 0xf3, 0xaa, 0xf4, 0x9f, 0x2e, - 0x41, 0x37, 0x43, 0x9f, 0x62, 0x17, 0x1f, 0xc1, 0x52, 0x41, 0xa5, 0x2c, 0x4b, 0xe7, 0x39, 0x96, - 0xa5, 0x97, 0x35, 0x54, 0x7b, 0xf0, 0xb4, 0xc7, 0xdc, 0x7d, 0x65, 0x8f, 0xb9, 0xf7, 0x12, 0xfb, - 0xfc, 0x4d, 0x38, 0x95, 0x5d, 0xfe, 0xe9, 0x60, 0x28, 0x49, 0xb0, 0x4f, 0x1a, 0x34, 0xd0, 0xf7, - 0x7d, 0x14, 0x0a, 0x25, 0x11, 0x7d, 0x0c, 0xa7, 0x4b, 0xe4, 0xb8, 0x34, 0x15, 0xfd, 0xd2, 0x9c, - 0xae, 0x2c, 0xe7, 0x6d, 0xb1, 0x8a, 0x8d, 0xcd, 0xdf, 0xa9, 0x40, 0x6b, 0x27, 0x0e, 0xdd, 0xd4, - 0x91, 0xaf, 0xa9, 0xc9, 0xd3, 0x1a, 0x52, 0x7b, 0x91, 0x86, 0xd4, 0x67, 0x35, 0xc4, 0xfc, 0xdd, - 0x0a, 0xb4, 0xf5, 0x10, 0xee, 0xaf, 0x7f, 0x4d, 0x1b, 0xc8, 0x8b, 0x47, 0xf1, 0x14, 0xda, 0x14, - 0xf3, 0x3c, 0xd1, 0x24, 0x9e, 0xb8, 0xc2, 0xaa, 0xaf, 0xb5, 0xc2, 0xcc, 0x3f, 0xa8, 0x40, 0x8f, - 0xc2, 0xc3, 0xf7, 0xd2, 0x40, 0xe9, 0xf0, 0xe2, 0x08, 0xe9, 0x0a, 0xd4, 0x63, 0x21, 0xb3, 0xcc, - 0x8d, 0xae, 0xfa, 0xcc, 0x66, 0xe8, 0x6f, 0x89, 0x11, 0x27, 0x0c, 0x32, 0xc1, 0x8e, 0xc7, 0xc9, - 0xa2, 0xdc, 0x11, 0x84, 0xe3, 0xac, 0x22, 0x3b, 0xb6, 0x27, 0x49, 0x96, 0x3b, 0xa2, 0x6a, 0x8c, - 0x41, 0x9d, 0x56, 0x4a, 0x83, 0x56, 0x0a, 0x95, 0xcd, 0x0d, 0x38, 0x73, 0xf7, 0x48, 0x8a, 0x38, - 0xb0, 0x69, 0xc5, 0xac, 0xa3, 0xbe, 0x51, 0x48, 0x38, 0x23, 0xae, 0x14, 0xc4, 0x38, 0xe0, 0x72, - 0x66, 0x9c, 0xaa, 0x98, 0xd7, 0xa0, 0x33, 0xf2, 0x7c, 0x61, 0x85, 0xa3, 0x51, 0x22, 0x24, 0x7e, - 0x5d, 0x95, 0x68, 0x5a, 0x35, 0xae, 0x6b, 0xe6, 0x1f, 0xd7, 0xa0, 0x9b, 0x7d, 0x8a, 0x32, 0x87, - 0xde, 0x2b, 0x4f, 0xbf, 0xb3, 0x3e, 0xc8, 0xe6, 0x81, 0x24, 0x1b, 0x52, 0xc6, 0xd9, 0x96, 0xaf, - 0xd8, 0x72, 0x01, 0xda, 0xf4, 0x95, 0xc4, 0x7b, 0x26, 0x88, 0x37, 0x35, 0x6e, 0x20, 0x80, 0x52, - 0x40, 0x36, 0x60, 0xb9, 0x34, 0x04, 0x4b, 0x86, 0xd2, 0xf6, 0x35, 0x7b, 0x4a, 0xf7, 0xd6, 0x25, - 0x12, 0xbe, 0x84, 0x95, 0xcf, 0xa9, 0xbc, 0x87, 0xd4, 0xc8, 0xf6, 0x3c, 0x50, 0x3c, 0xc7, 0x76, - 0xc4, 0xe0, 0x02, 0x70, 0x62, 0x81, 0xeb, 0x2f, 0x79, 0xec, 0x6b, 0x26, 0xb6, 0x15, 0x64, 0xf7, - 0xb1, 0x9f, 0x0f, 0x30, 0xf7, 0xa8, 0xdb, 0x6a, 0x80, 0xa4, 0xb7, 0x37, 0xa1, 0x13, 0xc6, 0xde, - 0xd8, 0x0b, 0x54, 0x34, 0xba, 0xb5, 0xe0, 0x23, 0xa0, 0x08, 0x28, 0x36, 0x6d, 0x42, 0x53, 0xe9, - 0xdd, 0x82, 0x7b, 0x3a, 0x8d, 0x61, 0xd7, 0x61, 0x29, 0x91, 0xb1, 0xe7, 0x48, 0x1c, 0x8e, 0x35, - 0x09, 0xdd, 0xcc, 0x85, 0xe8, 0x29, 0xf0, 0xee, 0x63, 0x9f, 0xee, 0x37, 0xae, 0xc3, 0x92, 0x13, - 0xfa, 0xe9, 0x44, 0x6d, 0xb7, 0x96, 0x2f, 0x02, 0xed, 0x49, 0xf4, 0x14, 0x18, 0xc7, 0x77, 0x5f, - 0x04, 0xa6, 0x03, 0xb0, 0x2b, 0x63, 0x61, 0x4f, 0x48, 0x38, 0xef, 0x40, 0x4b, 0xee, 0xfb, 0x74, - 0xe7, 0x59, 0x59, 0x78, 0xe7, 0xd9, 0x94, 0xfb, 0x38, 0xec, 0x92, 0xb8, 0xab, 0x74, 0xfb, 0xa8, - 0x6b, 0xa8, 0x2b, 0xbe, 0x37, 0xf1, 0xa4, 0x4e, 0x65, 0x54, 0x15, 0xf3, 0x43, 0x68, 0x53, 0x0f, - 0xf4, 0x8d, 0xdc, 0xdb, 0xab, 0x9c, 0xe8, 0xed, 0x99, 0xef, 0x41, 0xfb, 0x37, 0x6d, 0x3f, 0x55, - 0x8d, 0x2e, 0x43, 0x87, 0xee, 0xc5, 0xad, 0x7d, 0x3f, 0x74, 0x0e, 0xb3, 0xfb, 0x5a, 0x02, 0xdd, - 0x41, 0x88, 0x09, 0x60, 0x3c, 0x0a, 0xbc, 0x30, 0xd8, 0xf0, 0x7d, 0xf3, 0x0f, 0xeb, 0xd0, 0xfe, - 0xbe, 0x9d, 0x1c, 0xd0, 0x2a, 0x64, 0x2b, 0xd0, 0x79, 0x28, 0x84, 0x8b, 0x80, 0x07, 0x76, 0xa4, - 0x73, 0xa6, 0xca, 0x20, 0x76, 0x1e, 0x8c, 0xef, 0x2b, 0xff, 0xe2, 0x33, 0x7d, 0x13, 0x99, 0xd7, - 0xb3, 0xd6, 0x74, 0xef, 0x2e, 0xb2, 0xf4, 0x9c, 0x32, 0x88, 0xdd, 0x80, 0x01, 0x56, 0x29, 0x33, - 0x09, 0x95, 0x42, 0xf8, 0x6a, 0x05, 0x1a, 0x7c, 0x0e, 0xce, 0x6e, 0x00, 0xe0, 0x5e, 0x4e, 0x37, - 0xfa, 0xc9, 0x02, 0x1f, 0xa8, 0x84, 0x65, 0x97, 0x00, 0x3e, 0xcd, 0x0d, 0x98, 0xce, 0xfa, 0x2b, - 0x41, 0xd8, 0x55, 0xe8, 0xe9, 0x1a, 0x17, 0xa3, 0x4d, 0x7d, 0x0f, 0xdc, 0xe0, 0xd3, 0x40, 0x76, - 0x17, 0x96, 0xf9, 0x2b, 0xe7, 0x63, 0xce, 0x81, 0xd0, 0x38, 0xd3, 0xed, 0xa7, 0x9b, 0x46, 0x5a, - 0xdf, 0x5a, 0x5e, 0x42, 0x5e, 0xd2, 0xf3, 0x76, 0x78, 0xf8, 0xaa, 0x76, 0xf8, 0xce, 0xcb, 0xed, - 0xf0, 0xdd, 0x97, 0xda, 0xe1, 0xcd, 0x9f, 0xd5, 0xa0, 0xab, 0x37, 0x2f, 0x32, 0xee, 0x53, 0xc2, - 0xaf, 0x9c, 0x2c, 0xfc, 0xea, 0xcb, 0x09, 0xbf, 0xf6, 0x52, 0xc2, 0xaf, 0x9f, 0x28, 0xfc, 0x85, - 0x62, 0x6b, 0xbc, 0xb2, 0xd8, 0x5e, 0xa4, 0x43, 0x97, 0x00, 0x76, 0x73, 0x5f, 0x2d, 0x73, 0xef, - 0x0a, 0xc8, 0x94, 0xd8, 0x8d, 0x97, 0x12, 0xfb, 0xcf, 0xa7, 0x63, 0x67, 0xee, 0x02, 0xd0, 0x06, - 0xa7, 0x64, 0xbe, 0x90, 0xbb, 0x95, 0x57, 0xe5, 0xae, 0xf9, 0xbf, 0x15, 0x80, 0x5d, 0x7b, 0x12, - 0xa9, 0xcd, 0x9d, 0x7d, 0x17, 0x3a, 0x09, 0xd5, 0xd4, 0x8d, 0x88, 0xca, 0xc9, 0xbf, 0x5c, 0xca, - 0xc9, 0xcf, 0x49, 0x75, 0x11, 0x87, 0xc6, 0x21, 0xc9, 0xcb, 0xe4, 0x4d, 0xab, 0x1e, 0xf2, 0xac, - 0x88, 0x46, 0x46, 0x40, 0x97, 0xd1, 0xd7, 0xa0, 0xaf, 0x09, 0x22, 0x11, 0x3b, 0x22, 0x50, 0x76, - 0xb6, 0xc2, 0x7b, 0x0a, 0xba, 0xa3, 0x80, 0xec, 0x83, 0x9c, 0x4c, 0x19, 0xfb, 0x45, 0xda, 0xa6, - 0x9b, 0x6c, 0x2a, 0x02, 0x73, 0x3d, 0x9b, 0x0a, 0x0d, 0xc4, 0x80, 0x3a, 0x7e, 0x6f, 0xf0, 0x06, - 0xeb, 0x40, 0x4b, 0xf7, 0x3a, 0xa8, 0xb0, 0x1e, 0xb4, 0x29, 0x35, 0x98, 0x70, 0x55, 0xf3, 0x4f, - 0x4f, 0x41, 0x67, 0x3b, 0x48, 0x64, 0x9c, 0x2a, 0x21, 0x16, 0x19, 0xb0, 0x0d, 0xca, 0x80, 0xd5, - 0x69, 0x31, 0x6a, 0x1a, 0x94, 0x16, 0x73, 0x1d, 0xea, 0x76, 0x20, 0x3d, 0xed, 0xc8, 0x95, 0xd2, - 0xac, 0xb3, 0x80, 0x1b, 0x27, 0x3c, 0xbb, 0x09, 0x2d, 0x9d, 0x93, 0xad, 0x53, 0x1e, 0x17, 0x26, - 0x74, 0x67, 0x34, 0x6c, 0x0d, 0x0c, 0x57, 0x27, 0x8b, 0xeb, 0x45, 0x52, 0xea, 0x3a, 0x4b, 0x23, - 0xe7, 0x39, 0x0d, 0xbb, 0x02, 0x35, 0x7b, 0xac, 0xd6, 0x03, 0xa5, 0xa9, 0x64, 0xa4, 0x94, 0x62, - 0xcb, 0x11, 0xc7, 0x4c, 0xa8, 0xa3, 0xfb, 0x48, 0x6b, 0x82, 0xb6, 0xc1, 0x8c, 0x46, 0x8d, 0x12, - 0x71, 0xec, 0x96, 0x3e, 0xe5, 0x11, 0xa1, 0x31, 0xfb, 0xdd, 0xec, 0x42, 0x46, 0x9d, 0xf6, 0x3e, - 0xd5, 0x0d, 0x12, 0x31, 0xf1, 0x54, 0x83, 0xf6, 0x6c, 0x83, 0x2c, 0xa8, 0xc5, 0x8d, 0x24, 0x0b, - 0x6f, 0xdd, 0x86, 0x4e, 0x42, 0xd1, 0x17, 0xd5, 0x04, 0xb2, 0xbb, 0xf9, 0xbc, 0x49, 0x1e, 0x9a, - 0xe1, 0x90, 0x14, 0x61, 0x9a, 0x5b, 0xd0, 0x9e, 0xd8, 0xf1, 0xa1, 0x6a, 0xd4, 0x99, 0xfd, 0x4e, - 0x16, 0x1a, 0xe0, 0xc6, 0x24, 0x0b, 0x12, 0xac, 0x03, 0xa8, 0x85, 0x45, 0x2d, 0xba, 0xb3, 0x2c, - 0xcf, 0x8f, 0xc3, 0xbc, 0xed, 0xe6, 0x27, 0xe3, 0x77, 0xa1, 0x15, 0x29, 0xbf, 0x9e, 0xf2, 0xb9, - 0x3a, 0xeb, 0xcb, 0x45, 0x03, 0xed, 0xf0, 0xf3, 0x8c, 0x82, 0x7d, 0x07, 0xfa, 0x2a, 0x81, 0x62, - 0xa4, 0xdd, 0x60, 0xca, 0xf1, 0x9a, 0xca, 0x15, 0x9e, 0xf2, 0x92, 0x79, 0x4f, 0x4e, 0x39, 0xcd, - 0x1f, 0x43, 0x4f, 0x68, 0x2f, 0xd2, 0x4a, 0x1c, 0x3b, 0x18, 0x0e, 0xa8, 0xf9, 0xd9, 0xa2, 0x79, - 0xd9, 0xc9, 0xe4, 0x5d, 0x51, 0x76, 0x39, 0x57, 0xa1, 0xa9, 0x93, 0x7a, 0x96, 0xa9, 0x55, 0xe9, - 0x0d, 0x8c, 0xba, 0x83, 0xe6, 0x1a, 0xcf, 0xee, 0xcc, 0x64, 0x07, 0x1c, 0x8a, 0xe3, 0x21, 0xcb, - 0x12, 0x76, 0x16, 0x5f, 0xf9, 0x4f, 0xe5, 0x0d, 0x7c, 0x26, 0x8e, 0x91, 0x97, 0x45, 0x56, 0xc5, - 0xf0, 0xd4, 0x2c, 0x2f, 0xf3, 0x94, 0x0a, 0xde, 0xce, 0xb3, 0x29, 0xd0, 0x20, 0x95, 0xb3, 0x3c, - 0xd4, 0x45, 0xf9, 0x69, 0x6a, 0xfa, 0xe6, 0x82, 0xa6, 0xea, 0xbe, 0x9c, 0x2f, 0x45, 0x33, 0xc9, - 0x22, 0xef, 0x81, 0x11, 0xc6, 0x2e, 0x25, 0x6f, 0x0d, 0xcf, 0xd0, 0x8a, 0x5f, 0xd6, 0x39, 0x58, - 0x2a, 0xd9, 0x9d, 0x0c, 0x59, 0x2b, 0x54, 0x15, 0x76, 0x13, 0xba, 0x51, 0x1c, 0xfe, 0x50, 0x38, - 0x52, 0x79, 0xaf, 0x67, 0xe7, 0x93, 0xe4, 0x35, 0x9e, 0x9c, 0xd9, 0xc2, 0x3b, 0x3d, 0xf7, 0x5c, - 0xef, 0x74, 0x25, 0x73, 0xff, 0x86, 0xf3, 0x19, 0x09, 0x84, 0xc0, 0x5e, 0xb4, 0xe3, 0xf8, 0xe6, - 0x7c, 0x2f, 0xda, 0x89, 0x1c, 0x42, 0xcb, 0x4b, 0xee, 0x79, 0x71, 0x22, 0x87, 0xe7, 0xb3, 0x4d, - 0x87, 0xaa, 0xe8, 0x76, 0x7a, 0xc9, 0x7d, 0x3b, 0x91, 0xc3, 0x0b, 0xd9, 0x3b, 0x07, 0xac, 0x21, - 0xcf, 0xd5, 0x31, 0x9c, 0xf4, 0xf7, 0xe2, 0x2c, 0xcf, 0xf3, 0x8b, 0x36, 0x1d, 0x4f, 0x21, 0xfd, - 0xfd, 0x04, 0x96, 0x54, 0x9b, 0x62, 0x49, 0xbe, 0x35, 0xab, 0x93, 0x53, 0x37, 0x36, 0xbc, 0x17, - 0x4f, 0x5d, 0xe0, 0xe4, 0x1d, 0xa0, 0xc9, 0x52, 0x1d, 0x5c, 0x5a, 0xd8, 0x41, 0x6e, 0xdc, 0x54, - 0x07, 0xf9, 0xe5, 0xc2, 0x0d, 0x68, 0xea, 0x4c, 0xb4, 0xcb, 0x73, 0x46, 0x4b, 0xe7, 0x5c, 0x72, - 0x4d, 0xc1, 0xbe, 0x01, 0x2d, 0x4a, 0x43, 0x0a, 0xa3, 0xe1, 0xca, 0xac, 0x12, 0xab, 0x6c, 0x23, - 0xde, 0xf4, 0x55, 0xd6, 0xd1, 0xbb, 0xd0, 0xca, 0xce, 0xeb, 0x57, 0x66, 0x17, 0xa6, 0xde, 0xdb, - 0x79, 0x46, 0xc1, 0xae, 0x41, 0x63, 0x82, 0x26, 0x7d, 0x68, 0xce, 0x1a, 0x43, 0x65, 0xe9, 0x15, - 0x96, 0x0c, 0x11, 0x1d, 0x13, 0xd4, 0xea, 0x7b, 0x7b, 0xce, 0x10, 0xe5, 0x67, 0x08, 0x0e, 0x49, - 0x71, 0x9e, 0xf8, 0x6d, 0x38, 0x5f, 0xce, 0x30, 0xca, 0xd2, 0x8f, 0xf4, 0x4b, 0xab, 0xab, 0xd4, - 0xcb, 0x95, 0x05, 0x0a, 0x3e, 0x9d, 0xa8, 0xc4, 0xcf, 0x45, 0xcf, 0xc9, 0x60, 0xba, 0x9d, 0x6f, - 0x98, 0x68, 0x57, 0x86, 0xd7, 0xe6, 0x86, 0x95, 0x6f, 0xb9, 0xd9, 0x36, 0x4a, 0x3b, 0xf5, 0x47, - 0xd0, 0x1d, 0xa5, 0xcf, 0x9e, 0x1d, 0xeb, 0x63, 0xfe, 0xf0, 0x3a, 0xb5, 0x2b, 0x9d, 0x19, 0x4b, - 0xf9, 0x32, 0xbc, 0x33, 0x2a, 0x25, 0xcf, 0x9c, 0x83, 0x96, 0x13, 0x58, 0xb6, 0xeb, 0xc6, 0xc3, - 0x77, 0x54, 0xbe, 0x8c, 0x13, 0x6c, 0xb8, 0x2e, 0x25, 0x1e, 0x85, 0x91, 0xa0, 0x07, 0x23, 0x96, - 0xe7, 0x0e, 0x57, 0xd5, 0xd6, 0x9d, 0x81, 0xb6, 0x5d, 0x7a, 0x8a, 0x66, 0xc7, 0xb6, 0xef, 0x0b, - 0x1f, 0x09, 0xbe, 0xa1, 0x9f, 0xa2, 0x69, 0xd0, 0xb6, 0xcb, 0xae, 0x40, 0x77, 0x62, 0x1f, 0x59, - 0x19, 0x64, 0x78, 0x43, 0xbd, 0xf3, 0x99, 0xd8, 0x47, 0x3b, 0x1a, 0x84, 0x6a, 0xae, 0xd2, 0x84, - 0x49, 0xd9, 0xde, 0x9d, 0x55, 0xf3, 0x3c, 0xc2, 0xc1, 0xdb, 0x5e, 0x1e, 0xec, 0x20, 0x73, 0x44, - 0x46, 0xd8, 0xf2, 0xd7, 0x87, 0xef, 0xcd, 0x9b, 0x23, 0x1d, 0x9a, 0x41, 0x73, 0x94, 0x45, 0x69, - 0xd6, 0x01, 0x94, 0xb5, 0x26, 0x61, 0xdf, 0x9c, 0x6d, 0x93, 0x9f, 0xe5, 0xb8, 0xca, 0x91, 0x25, - 0x51, 0xaf, 0x03, 0x50, 0xce, 0x91, 0x6a, 0xb3, 0x36, 0xdb, 0x26, 0x3f, 0xca, 0xf1, 0xf6, 0x93, - 0xfc, 0x54, 0x77, 0x0b, 0xda, 0x29, 0x1e, 0xda, 0x2c, 0xdb, 0xf7, 0x87, 0xb7, 0x66, 0xd7, 0x40, - 0x76, 0x9e, 0xe3, 0x46, 0xaa, 0x4b, 0xf8, 0x11, 0x8a, 0x0d, 0x93, 0x1b, 0x37, 0x7c, 0x7f, 0xf6, - 0x23, 0xf9, 0xa1, 0x8f, 0xb7, 0x0f, 0xf2, 0xf3, 0xdf, 0xc7, 0xd0, 0xcb, 0x42, 0x94, 0xaa, 0xd9, - 0x07, 0xb3, 0x5b, 0x47, 0xf9, 0x3c, 0xc0, 0xb3, 0xc7, 0x56, 0xaa, 0xf1, 0x6d, 0xe8, 0x28, 0x8e, - 0xab, 0xa6, 0xeb, 0xb3, 0x0a, 0x56, 0x38, 0x95, 0x5c, 0x89, 0x46, 0x35, 0xbb, 0x06, 0x0d, 0x3b, - 0x8a, 0xfc, 0xe3, 0xe1, 0x87, 0xb3, 0xab, 0x6a, 0x03, 0xc1, 0x5c, 0x61, 0x51, 0x0f, 0x27, 0xa9, - 0x2f, 0xbd, 0x2c, 0xe1, 0xf7, 0x9b, 0xb3, 0x7a, 0x58, 0x7a, 0x50, 0xc0, 0x3b, 0x93, 0xd2, 0xcb, - 0x87, 0xf7, 0xc0, 0x88, 0xc2, 0x44, 0x5a, 0xee, 0xc4, 0x1f, 0xde, 0x9e, 0xdb, 0x7d, 0x55, 0x56, - 0x29, 0x6f, 0x45, 0xaa, 0x60, 0xde, 0x86, 0xee, 0x06, 0x3d, 0xc1, 0xf4, 0x12, 0x32, 0xe5, 0xd7, - 0xa0, 0x9e, 0x47, 0xe0, 0xf2, 0x3d, 0x82, 0x28, 0x9e, 0x89, 0xed, 0x60, 0x14, 0x72, 0x42, 0x9b, - 0xff, 0x50, 0x83, 0xe6, 0x6e, 0x98, 0xc6, 0x8e, 0x78, 0x71, 0xbe, 0xf4, 0x5b, 0x99, 0xca, 0x04, - 0x45, 0x2e, 0x99, 0xd2, 0x0e, 0x42, 0x97, 0x83, 0x7b, 0x35, 0x8a, 0x92, 0xe4, 0xc1, 0xbd, 0xd3, - 0xd0, 0x50, 0x87, 0x7a, 0x95, 0xb1, 0xab, 0x2a, 0xb4, 0x5c, 0xd2, 0xe4, 0xc0, 0x0d, 0x9f, 0x06, - 0xb8, 0x5c, 0x1a, 0x94, 0xf0, 0x0a, 0x19, 0x68, 0xdb, 0xa5, 0x47, 0x27, 0x19, 0x01, 0xad, 0xc7, - 0xa6, 0x3a, 0x30, 0x64, 0x40, 0x5a, 0x95, 0x59, 0xe0, 0xb0, 0xf5, 0x9c, 0xc0, 0xe1, 0x25, 0xa8, - 0x07, 0x59, 0xa6, 0x68, 0x8e, 0xa7, 0xe7, 0x7e, 0x04, 0x67, 0x37, 0x20, 0x4f, 0xf2, 0xd6, 0xfe, - 0xda, 0xf3, 0x93, 0xc0, 0xd7, 0xa1, 0x9d, 0x3f, 0xe0, 0xcd, 0x3d, 0xb5, 0xe2, 0x49, 0xef, 0x5e, - 0x56, 0xe2, 0x05, 0xd9, 0x82, 0x88, 0x63, 0x14, 0x87, 0xfb, 0x3a, 0x9a, 0xd4, 0x79, 0x95, 0x88, - 0xe3, 0x0e, 0xb6, 0xcb, 0xe2, 0xa8, 0x5e, 0x62, 0x39, 0x61, 0x90, 0x48, 0x9d, 0x85, 0xdf, 0xf2, - 0x92, 0x4d, 0xac, 0x9a, 0x11, 0x18, 0x38, 0x39, 0x14, 0x31, 0x63, 0x50, 0x9f, 0x38, 0x51, 0xaa, - 0xdd, 0x75, 0x2a, 0xeb, 0xf7, 0xb9, 0x4a, 0x78, 0xfa, 0x7d, 0x2e, 0xb1, 0xb6, 0xa6, 0xa2, 0x81, - 0x58, 0x66, 0x67, 0xa0, 0xe9, 0x04, 0x96, 0x13, 0x64, 0xe9, 0xbe, 0x0d, 0x27, 0xd8, 0x0c, 0xa4, - 0x06, 0x17, 0x2f, 0x29, 0x1a, 0x4e, 0xb0, 0xed, 0x1e, 0x99, 0x7f, 0x51, 0x81, 0xe5, 0x9d, 0x38, - 0x74, 0x44, 0x92, 0xdc, 0xc7, 0xfd, 0xdf, 0x26, 0x6f, 0x8e, 0x41, 0x9d, 0x02, 0x7a, 0xea, 0xb1, - 0x1c, 0x95, 0x51, 0x81, 0x54, 0x84, 0x27, 0x3f, 0xfa, 0xd4, 0x78, 0x9b, 0x20, 0x74, 0xf2, 0xc9, - 0xd1, 0xd4, 0xb0, 0x56, 0x42, 0x53, 0x28, 0xf0, 0x1a, 0xf4, 0x8b, 0xa7, 0x14, 0xd4, 0x83, 0x7e, - 0x25, 0x9b, 0x43, 0xa9, 0x97, 0xcb, 0xd0, 0x89, 0x85, 0x8d, 0x1e, 0x12, 0x75, 0xd3, 0x20, 0x1a, - 0x50, 0x20, 0xec, 0xc7, 0x3c, 0x80, 0xc1, 0x4e, 0x2c, 0x22, 0x3b, 0x16, 0x68, 0x74, 0x27, 0xc4, - 0xa9, 0xb3, 0xd0, 0xf4, 0x45, 0x30, 0x96, 0x07, 0x7a, 0xbc, 0xba, 0x96, 0xbf, 0x90, 0xae, 0x96, - 0x5e, 0x48, 0x23, 0xc7, 0x62, 0x61, 0xeb, 0x87, 0xd4, 0x54, 0x46, 0x05, 0x0f, 0x52, 0x5f, 0x07, - 0x19, 0x0d, 0xae, 0x2a, 0xe6, 0x9f, 0xd7, 0xa0, 0xa3, 0x39, 0x43, 0x5f, 0x51, 0xbc, 0xaf, 0xe4, - 0xbc, 0x1f, 0x40, 0x2d, 0x79, 0xec, 0x6b, 0x61, 0x60, 0x91, 0x7d, 0x08, 0x35, 0xdf, 0x9b, 0xe8, - 0xb3, 0xd3, 0x85, 0x29, 0x13, 0x3e, 0xcd, 0x5f, 0x7d, 0x04, 0x46, 0x6a, 0x76, 0x81, 0x4c, 0xec, - 0x91, 0x85, 0x9a, 0xa2, 0x79, 0x82, 0xe6, 0xf4, 0x08, 0xd5, 0x11, 0x99, 0x6a, 0x3b, 0x94, 0x8d, - 0x9b, 0xad, 0xb1, 0x1e, 0x6f, 0x6b, 0xc8, 0xb6, 0xcb, 0xbe, 0x09, 0x46, 0x12, 0xd8, 0x51, 0x72, - 0x10, 0x4a, 0x7d, 0x56, 0x62, 0x6b, 0xf2, 0x28, 0x58, 0xdb, 0x7c, 0xb8, 0x77, 0x14, 0xec, 0x6a, - 0x8c, 0xfe, 0x58, 0x4e, 0xc9, 0xbe, 0x03, 0xdd, 0x44, 0x24, 0x89, 0x7a, 0xd3, 0x32, 0x0a, 0xf5, - 0xda, 0x3b, 0x53, 0x3e, 0xe7, 0x10, 0x16, 0x67, 0xad, 0x1b, 0x77, 0x92, 0x02, 0xc4, 0xbe, 0x0f, - 0xfd, 0xac, 0xbd, 0x1f, 0x8e, 0xc7, 0x79, 0x34, 0xf4, 0xc2, 0x5c, 0x0f, 0xf7, 0x09, 0x5d, 0xea, - 0xa7, 0x97, 0x94, 0x11, 0xec, 0x7b, 0xd0, 0x8f, 0x94, 0x30, 0x2d, 0x1d, 0x19, 0x57, 0x6b, 0xf8, - 0xfc, 0x94, 0xc7, 0x31, 0x25, 0xec, 0x22, 0xe3, 0xbd, 0x80, 0x27, 0xe6, 0x7f, 0x57, 0xa0, 0x53, - 0x1a, 0x35, 0xbd, 0x5b, 0x4f, 0x44, 0x9c, 0x45, 0xc9, 0xb1, 0x8c, 0xb0, 0x83, 0x50, 0x3f, 0xf7, - 0x6c, 0x73, 0x2a, 0x23, 0x2c, 0x0e, 0xf5, 0xb5, 0x49, 0x9b, 0x53, 0x19, 0xed, 0x96, 0x3e, 0xb6, - 0xaa, 0xd7, 0x72, 0x24, 0x94, 0x3a, 0xef, 0x16, 0xc0, 0x6d, 0x0a, 0x4a, 0xa1, 0x3a, 0xed, 0xdb, - 0x49, 0x16, 0xb7, 0xcf, 0xeb, 0xe8, 0x1d, 0x3f, 0x11, 0x31, 0x8e, 0x45, 0x9b, 0xbc, 0xac, 0x8a, - 0xb2, 0x26, 0x53, 0xf2, 0x2c, 0x0c, 0xd4, 0xd5, 0x68, 0x97, 0x1b, 0x08, 0xf8, 0x41, 0x18, 0x50, - 0x33, 0x2d, 0x59, 0xe2, 0x67, 0x9b, 0x67, 0x55, 0x34, 0x18, 0x8f, 0x53, 0x81, 0x5e, 0x99, 0x4b, - 0xef, 0x22, 0xdb, 0xbc, 0x45, 0xf5, 0x6d, 0xd7, 0xfc, 0xf7, 0x0a, 0x2c, 0xcf, 0x31, 0x1b, 0x9d, - 0x20, 0x64, 0x74, 0xf6, 0x10, 0xa1, 0xcb, 0x9b, 0x58, 0xdd, 0x76, 0x09, 0x21, 0x27, 0xa4, 0x4c, - 0x55, 0x8d, 0x90, 0x13, 0xd4, 0xa4, 0x33, 0xd0, 0x94, 0x47, 0x34, 0x5b, 0xb5, 0x30, 0x1a, 0xf2, - 0x08, 0xa7, 0xb9, 0x01, 0x6d, 0x3f, 0x1c, 0x5b, 0xbe, 0x78, 0x22, 0x7c, 0xe2, 0x43, 0x7f, 0xfd, - 0xea, 0x09, 0x52, 0x5e, 0xbb, 0x1f, 0x8e, 0xef, 0x23, 0x2d, 0x37, 0x7c, 0x5d, 0x32, 0x3f, 0x05, - 0x23, 0x83, 0xb2, 0x36, 0x34, 0xb6, 0xc4, 0x7e, 0x3a, 0x1e, 0xbc, 0xc1, 0x0c, 0xa8, 0x63, 0x8b, - 0x41, 0x05, 0x4b, 0x5f, 0xd8, 0x71, 0x30, 0xa8, 0x22, 0xfa, 0x6e, 0x1c, 0x87, 0xf1, 0xa0, 0x86, - 0xc5, 0x1d, 0x3b, 0xf0, 0x9c, 0x41, 0x1d, 0x8b, 0xf7, 0x6c, 0x69, 0xfb, 0x83, 0x86, 0xf9, 0x97, - 0x0d, 0x30, 0x76, 0xf4, 0xd7, 0xd9, 0x16, 0xf4, 0xf2, 0x5f, 0x07, 0x2c, 0x8e, 0xe7, 0xec, 0xcc, - 0x16, 0x28, 0x9e, 0xd3, 0x8d, 0x4a, 0xb5, 0xd9, 0x1f, 0x10, 0x54, 0xe7, 0x7e, 0x40, 0x70, 0x11, - 0x6a, 0x8f, 0xe3, 0xe3, 0xe9, 0x9b, 0xad, 0x1d, 0xdf, 0x0e, 0x38, 0x82, 0xd9, 0x07, 0xd0, 0x41, - 0xb9, 0x5b, 0x09, 0xed, 0xc2, 0x3a, 0x16, 0x52, 0xfe, 0xcd, 0x03, 0xc1, 0x39, 0x20, 0x91, 0xde, - 0xa9, 0xd7, 0xc0, 0x70, 0x0e, 0x3c, 0xdf, 0x8d, 0x45, 0xa0, 0x03, 0xcc, 0x6c, 0x7e, 0xc8, 0x3c, - 0xa7, 0x61, 0xdf, 0xa5, 0xd4, 0xfc, 0x2c, 0x86, 0x53, 0xce, 0x0c, 0x3a, 0x33, 0x75, 0x4c, 0xce, - 0x28, 0xf8, 0x52, 0x89, 0x9c, 0x36, 0x9c, 0xe2, 0x5d, 0x57, 0xab, 0xfc, 0xae, 0x4b, 0x3d, 0x4a, - 0xf7, 0x43, 0xdb, 0xd5, 0xa1, 0x43, 0x3c, 0xac, 0x85, 0xb6, 0xcb, 0xae, 0xeb, 0x4d, 0x77, 0x2e, - 0x00, 0x92, 0xed, 0x4d, 0x7a, 0xf3, 0xbd, 0x0a, 0x7d, 0xdc, 0xcc, 0x2d, 0xe5, 0x03, 0xa0, 0x29, - 0x01, 0xfd, 0xae, 0x34, 0x4d, 0x0e, 0xb6, 0xd0, 0x0b, 0x40, 0x65, 0xbc, 0x06, 0xfd, 0x6c, 0x2e, - 0xfa, 0x61, 0x41, 0x47, 0xdf, 0x73, 0x68, 0xa8, 0x7a, 0x57, 0xb0, 0x06, 0xa7, 0x9c, 0x03, 0x3b, - 0x08, 0x84, 0x6f, 0xed, 0xa7, 0xa3, 0x51, 0xb6, 0x03, 0x74, 0xe9, 0x02, 0x70, 0x59, 0xa3, 0xee, - 0x10, 0x86, 0x36, 0x14, 0x13, 0x7a, 0x81, 0xe7, 0xab, 0xc7, 0x78, 0xb4, 0xdb, 0xf5, 0x88, 0xb2, - 0x13, 0x78, 0x3e, 0xc5, 0x7e, 0x71, 0xcf, 0xfb, 0x04, 0x06, 0x69, 0xea, 0xb9, 0x89, 0x25, 0xc3, - 0xec, 0x3d, 0x3f, 0x5d, 0xe3, 0x4e, 0x39, 0x97, 0x8f, 0x52, 0xcf, 0xdd, 0x0b, 0xf5, 0x8b, 0xfe, - 0x1e, 0xd1, 0x67, 0x55, 0xf3, 0x13, 0xe8, 0x96, 0x75, 0x07, 0x75, 0x91, 0x4e, 0x5d, 0x83, 0x37, - 0x18, 0x40, 0xf3, 0x61, 0x18, 0x4f, 0x6c, 0x7f, 0x50, 0xc1, 0xb2, 0x7a, 0xed, 0x38, 0xa8, 0xb2, - 0x2e, 0x18, 0xd9, 0x71, 0x60, 0x50, 0x33, 0x3f, 0x06, 0x23, 0xfb, 0x41, 0x01, 0xbd, 0x0c, 0x0f, - 0x5d, 0xa1, 0x9c, 0x21, 0x65, 0x99, 0x0c, 0x04, 0x90, 0x23, 0x94, 0xfd, 0x69, 0xa3, 0x5a, 0xfc, - 0x69, 0xc3, 0xfc, 0x0d, 0xe8, 0x96, 0x07, 0x97, 0x85, 0xeb, 0x2a, 0x45, 0xb8, 0x6e, 0x41, 0x2b, - 0xba, 0xf0, 0x8a, 0xc3, 0x89, 0x55, 0x72, 0x0c, 0x0c, 0x04, 0xe0, 0x67, 0xcc, 0xdf, 0xab, 0x40, - 0x83, 0x3c, 0x5c, 0xda, 0x5a, 0xb0, 0x50, 0xac, 0x9d, 0x06, 0x6f, 0x13, 0x84, 0x66, 0x5a, 0xbe, - 0x07, 0xae, 0x3e, 0xff, 0x1e, 0xb8, 0x36, 0x7d, 0x0f, 0xfc, 0x92, 0x89, 0x42, 0x37, 0x1e, 0x43, - 0x53, 0xfd, 0xdc, 0x84, 0x2d, 0x43, 0xef, 0x51, 0x70, 0x18, 0x84, 0x4f, 0x03, 0x05, 0x18, 0xbc, - 0xc1, 0x4e, 0xc1, 0x52, 0xc6, 0x74, 0xfd, 0x17, 0x95, 0x41, 0x85, 0x0d, 0xa0, 0x4b, 0x62, 0xcd, - 0x20, 0x55, 0x76, 0x11, 0x86, 0x7a, 0x73, 0xd8, 0x0a, 0x03, 0xf1, 0x30, 0x94, 0xde, 0xe8, 0x38, - 0xc3, 0xd6, 0xd8, 0x12, 0x74, 0x76, 0x65, 0x18, 0xed, 0x8a, 0xc0, 0xf5, 0x82, 0xf1, 0xa0, 0x7e, - 0xe3, 0x1e, 0x34, 0xd5, 0x3f, 0x57, 0x4a, 0x9f, 0x54, 0x80, 0xc1, 0x1b, 0x48, 0xfd, 0x85, 0xed, - 0x49, 0x2f, 0x18, 0x3f, 0x14, 0x47, 0x52, 0x19, 0xa5, 0xfb, 0x76, 0x22, 0x07, 0x55, 0xd6, 0x07, - 0xd0, 0xbd, 0xde, 0x0d, 0xdc, 0x41, 0xed, 0xce, 0xe6, 0x8f, 0x7f, 0x7a, 0xa9, 0xf2, 0x8f, 0x3f, - 0xbd, 0x54, 0xf9, 0x97, 0x9f, 0x5e, 0x7a, 0xe3, 0x4f, 0xfe, 0xf5, 0x52, 0xe5, 0x07, 0x1f, 0x94, - 0xfe, 0x28, 0x33, 0xb1, 0x65, 0xec, 0x1d, 0xa9, 0x2b, 0xc3, 0xac, 0x12, 0x88, 0x5b, 0xd1, 0xe1, - 0xf8, 0x56, 0xb4, 0x7f, 0x2b, 0xd3, 0xb9, 0xfd, 0x26, 0xfd, 0x28, 0xe6, 0xc3, 0xff, 0x0b, 0x00, - 0x00, 0xff, 0xff, 0x18, 0x47, 0x33, 0xc1, 0xa7, 0x46, 0x00, 0x00, + // 5791 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xec, 0x3c, 0x4d, 0x8f, 0x1d, 0x49, + 0x52, 0xf3, 0xbe, 0xeb, 0xc5, 0xfb, 0xe8, 0xd7, 0xe9, 0xaf, 0x37, 0xb6, 0xc7, 0xee, 0xa9, 0xb1, + 0x3d, 0xbd, 0x9e, 0x71, 0x7b, 0xa6, 0x67, 0xcd, 0x0e, 0x0c, 0xbb, 0xb3, 0xed, 0x6e, 0x7b, 0xb7, + 0x67, 0xec, 0x9e, 0x26, 0xbb, 0xcd, 0x88, 0x3d, 0x50, 0xaa, 0xae, 0xca, 0xf7, 0xba, 0xb6, 0xeb, + 0x55, 0x95, 0xab, 0xb2, 0xec, 0x6e, 0x9f, 0x90, 0xe0, 0xca, 0x89, 0x13, 0x42, 0x48, 0x68, 0x0f, + 0x20, 0x0e, 0x08, 0x04, 0x42, 0x1c, 0xf8, 0x03, 0xbb, 0x17, 0xc4, 0x89, 0x23, 0x42, 0xcb, 0x8d, + 0x8f, 0xdb, 0x82, 0xb8, 0x20, 0xa1, 0x88, 0xcc, 0xfa, 0x78, 0x1f, 0x6e, 0xdb, 0xf3, 0x81, 0x76, + 0xa5, 0xbd, 0x65, 0x46, 0x44, 0x56, 0x65, 0x45, 0x44, 0x46, 0x44, 0x46, 0x46, 0x16, 0xf4, 0x23, + 0x2f, 0x12, 0xbe, 0x17, 0x88, 0xb5, 0x28, 0x0e, 0x65, 0xc8, 0x8c, 0xac, 0x7f, 0xf1, 0xd6, 0xd8, + 0x93, 0x87, 0xe9, 0xc1, 0x9a, 0x13, 0x4e, 0x6e, 0x8f, 0xc3, 0x71, 0x78, 0x9b, 0x08, 0x0e, 0xd2, + 0x11, 0xf5, 0xa8, 0x43, 0x2d, 0x35, 0xf0, 0x22, 0x44, 0xbe, 0x1d, 0xe8, 0xf6, 0x92, 0xf4, 0x26, + 0x22, 0x91, 0xf6, 0x24, 0xca, 0x90, 0x7e, 0xe8, 0x1c, 0xe9, 0x76, 0x5b, 0x1e, 0x6b, 0x3a, 0xf3, + 0x8f, 0xaa, 0xd0, 0x7a, 0x28, 0x92, 0xc4, 0x1e, 0x0b, 0x66, 0x42, 0x2d, 0xf1, 0xdc, 0x61, 0x65, + 0xa5, 0xb2, 0xda, 0x5f, 0x1f, 0xac, 0xe5, 0xd3, 0xda, 0x93, 0xb6, 0x4c, 0x13, 0x8e, 0x48, 0xa4, + 0x71, 0x26, 0xee, 0xb0, 0x3a, 0x4b, 0xf3, 0x50, 0xc8, 0xc3, 0xd0, 0xe5, 0x88, 0x64, 0x03, 0xa8, + 0x89, 0x38, 0x1e, 0xd6, 0x56, 0x2a, 0xab, 0x5d, 0x8e, 0x4d, 0xc6, 0xa0, 0xee, 0xda, 0xd2, 0x1e, + 0xd6, 0x09, 0x44, 0x6d, 0x76, 0x0d, 0xfa, 0x51, 0x1c, 0x3a, 0x96, 0x17, 0x8c, 0x42, 0x8b, 0xb0, + 0x0d, 0xc2, 0x76, 0x11, 0xba, 0x1d, 0x8c, 0xc2, 0x2d, 0xa4, 0x1a, 0x42, 0xcb, 0x0e, 0x6c, 0xff, + 0x24, 0x11, 0xc3, 0x26, 0xa1, 0xb3, 0x2e, 0xeb, 0x43, 0xd5, 0x73, 0x87, 0xad, 0x95, 0xca, 0x6a, + 0x9d, 0x57, 0x3d, 0x17, 0xdf, 0x91, 0xa6, 0x9e, 0x3b, 0x34, 0xd4, 0x3b, 0xb0, 0xcd, 0x4c, 0xe8, + 0x06, 0x42, 0xb8, 0x3b, 0xa1, 0xe4, 0x22, 0xf2, 0x4f, 0x86, 0xed, 0x95, 0xca, 0xaa, 0xc1, 0xa7, + 0x60, 0xec, 0x22, 0x18, 0xae, 0x38, 0x48, 0xc7, 0x0f, 0x93, 0xf1, 0x10, 0x56, 0x2a, 0xab, 0x6d, + 0x9e, 0xf7, 0xcd, 0x47, 0xd0, 0xde, 0x0c, 0x83, 0x40, 0x38, 0x32, 0x8c, 0xd9, 0x55, 0xe8, 0x64, + 0x9f, 0x6b, 0x69, 0x36, 0x35, 0x38, 0x64, 0xa0, 0x6d, 0x97, 0xbd, 0x0d, 0x4b, 0x4e, 0x46, 0x6d, + 0x79, 0x81, 0x2b, 0x8e, 0x89, 0x4f, 0x0d, 0xde, 0xcf, 0xc1, 0xdb, 0x08, 0x35, 0xff, 0xa3, 0x0a, + 0xad, 0xbd, 0xc3, 0x74, 0x34, 0xf2, 0x05, 0xbb, 0x06, 0x3d, 0xdd, 0xdc, 0x0c, 0xfd, 0x6d, 0xf7, + 0x58, 0x3f, 0x77, 0x1a, 0xc8, 0x56, 0xa0, 0xa3, 0x01, 0xfb, 0x27, 0x91, 0xd0, 0x8f, 0x2d, 0x83, + 0xa6, 0x9f, 0xf3, 0xd0, 0x0b, 0x88, 0xfd, 0x35, 0x3e, 0x0d, 0x9c, 0xa1, 0xb2, 0x8f, 0x49, 0x22, + 0xd3, 0x54, 0x36, 0xbd, 0x6d, 0xc3, 0xf7, 0x9e, 0x08, 0x2e, 0xc6, 0x9b, 0x81, 0x24, 0xb9, 0x34, + 0x78, 0x19, 0xc4, 0xd6, 0xe1, 0x5c, 0xa2, 0x86, 0x58, 0xb1, 0x1d, 0x8c, 0x45, 0x62, 0xa5, 0x5e, + 0x20, 0x7f, 0xe5, 0x9b, 0xc3, 0xe6, 0x4a, 0x6d, 0xb5, 0xce, 0xcf, 0x68, 0x24, 0x27, 0xdc, 0x23, + 0x42, 0xb1, 0xf7, 0xe0, 0xec, 0xcc, 0x18, 0x35, 0xa4, 0xb5, 0x52, 0x5b, 0xad, 0x71, 0x36, 0x35, + 0x64, 0x9b, 0x46, 0xdc, 0x83, 0xe5, 0x38, 0x0d, 0x50, 0x93, 0xef, 0x7b, 0xbe, 0x14, 0xf1, 0x5e, + 0x24, 0x1c, 0x92, 0x6f, 0x67, 0xfd, 0xc2, 0x1a, 0x29, 0x3b, 0x9f, 0x45, 0xf3, 0xf9, 0x11, 0xe6, + 0xff, 0x54, 0xc1, 0xd8, 0xf2, 0x92, 0xc8, 0x96, 0xce, 0x21, 0xbb, 0x00, 0xad, 0x51, 0x1a, 0x38, + 0x85, 0x04, 0x9b, 0xd8, 0xdd, 0x76, 0xd9, 0xaf, 0xc3, 0x92, 0x1f, 0x3a, 0xb6, 0x6f, 0xe5, 0xc2, + 0x1a, 0x56, 0x57, 0x6a, 0xab, 0x9d, 0xf5, 0x33, 0x85, 0x96, 0xe7, 0xca, 0xc0, 0xfb, 0x44, 0x5b, + 0x28, 0xc7, 0xb7, 0x61, 0x10, 0x8b, 0x49, 0x28, 0x45, 0x69, 0x78, 0x8d, 0x86, 0xb3, 0x62, 0xf8, + 0xe7, 0xb1, 0x1d, 0xed, 0x84, 0xae, 0xe0, 0x4b, 0x8a, 0xb6, 0x18, 0xfe, 0x7e, 0x89, 0x9f, 0x62, + 0x6c, 0x79, 0xee, 0xb1, 0x45, 0x2f, 0x18, 0xd6, 0x57, 0x6a, 0xab, 0x8d, 0x82, 0x39, 0x62, 0xbc, + 0xed, 0x1e, 0x3f, 0x40, 0x0c, 0xfb, 0x00, 0xce, 0xcf, 0x0e, 0x51, 0x4f, 0x1d, 0x36, 0x68, 0xcc, + 0x99, 0xa9, 0x31, 0x9c, 0x50, 0xec, 0x4d, 0xe8, 0x66, 0x83, 0x24, 0x2a, 0x52, 0x53, 0x89, 0x36, + 0x29, 0x29, 0xd2, 0x05, 0x68, 0x79, 0x89, 0x95, 0x78, 0xc1, 0x11, 0x2d, 0x2e, 0x83, 0x37, 0xbd, + 0x64, 0xcf, 0x0b, 0x8e, 0xd8, 0xeb, 0x60, 0xc4, 0xc2, 0x51, 0x18, 0x83, 0x30, 0xad, 0x58, 0x38, + 0x84, 0xba, 0x00, 0xd8, 0xb4, 0x1c, 0x29, 0xf4, 0x12, 0x6b, 0xc6, 0xc2, 0xd9, 0x94, 0xc2, 0x4c, + 0xa0, 0xf1, 0x50, 0xc4, 0x63, 0x81, 0xab, 0x0c, 0x07, 0xee, 0x39, 0x76, 0x40, 0x7c, 0x37, 0x78, + 0xde, 0xc7, 0x35, 0x1e, 0xd9, 0xb1, 0xf4, 0x6c, 0x9f, 0x14, 0xdb, 0xe0, 0x59, 0x97, 0x5d, 0x82, + 0x76, 0x22, 0xed, 0x58, 0xe2, 0xd7, 0x91, 0x42, 0x37, 0xb8, 0x41, 0x00, 0x5c, 0x13, 0x17, 0xa0, + 0x25, 0x02, 0x97, 0x50, 0x75, 0x25, 0x49, 0x11, 0xb8, 0xdb, 0xee, 0xb1, 0xf9, 0x37, 0x15, 0xe8, + 0x3d, 0x4c, 0x7d, 0xe9, 0x6d, 0xc4, 0xe3, 0x54, 0x4c, 0x02, 0x89, 0xb6, 0x61, 0xcb, 0x4b, 0xa4, + 0x7e, 0x33, 0xb5, 0xd9, 0x2a, 0xb4, 0xbf, 0x17, 0x87, 0x69, 0x74, 0xef, 0x38, 0xca, 0x24, 0x0d, + 0x4a, 0xa9, 0x10, 0xc2, 0x0b, 0x24, 0x7b, 0x17, 0x3a, 0x9f, 0xc5, 0xae, 0x88, 0xef, 0x9e, 0x10, + 0x6d, 0x6d, 0x8e, 0xb6, 0x8c, 0x66, 0x97, 0xa1, 0xbd, 0x27, 0x22, 0x3b, 0xb6, 0x51, 0x05, 0xea, + 0x64, 0x50, 0x0a, 0x00, 0x7e, 0x2b, 0x11, 0x6f, 0xbb, 0x7a, 0x59, 0x65, 0x5d, 0x73, 0x0c, 0xed, + 0x8d, 0xf1, 0x38, 0x16, 0x63, 0x5b, 0x92, 0x71, 0x0b, 0x23, 0x9a, 0x6e, 0x8d, 0x57, 0xc3, 0x88, + 0x0c, 0x28, 0x7e, 0x80, 0xe2, 0x0f, 0xb5, 0xd9, 0x15, 0xa8, 0x8b, 0xc5, 0xf3, 0x21, 0x38, 0x3b, + 0x0f, 0x4d, 0x27, 0x0c, 0x46, 0xde, 0x58, 0x9b, 0x5d, 0xdd, 0x33, 0x7f, 0xbf, 0x06, 0x0d, 0xfa, + 0x38, 0x64, 0x2f, 0x9a, 0x42, 0x4b, 0x3c, 0xb1, 0xfd, 0x4c, 0x2a, 0x08, 0xb8, 0xf7, 0xc4, 0xf6, + 0xd9, 0x0a, 0x34, 0xf0, 0x31, 0xc9, 0x02, 0xde, 0x28, 0x04, 0xbb, 0x01, 0x0d, 0x54, 0xa2, 0x64, + 0x7a, 0x06, 0xa8, 0x44, 0x77, 0xeb, 0x3f, 0xfe, 0xe7, 0xab, 0xaf, 0x71, 0x85, 0x66, 0x6f, 0x43, + 0xdd, 0x1e, 0x8f, 0x13, 0xd2, 0xe5, 0xa9, 0xe5, 0x94, 0x7f, 0x2f, 0x27, 0x02, 0x76, 0x07, 0xda, + 0x4a, 0x6e, 0x48, 0xdd, 0x20, 0xea, 0x0b, 0x25, 0x17, 0x53, 0x16, 0x29, 0x2f, 0x28, 0x91, 0xe3, + 0x5e, 0xa2, 0x2d, 0x18, 0x69, 0xb4, 0xc1, 0x0b, 0x00, 0xfa, 0x80, 0x28, 0x16, 0x1b, 0xbe, 0x1f, + 0x3a, 0x7b, 0xde, 0x33, 0xa1, 0x3d, 0xc6, 0x14, 0x8c, 0xdd, 0x80, 0xfe, 0xae, 0x52, 0x39, 0x2e, + 0x92, 0xd4, 0x97, 0x89, 0xf6, 0x22, 0x33, 0x50, 0xb6, 0x06, 0x6c, 0x0a, 0xb2, 0x4f, 0x9f, 0xdf, + 0x5e, 0xa9, 0xad, 0xf6, 0xf8, 0x02, 0x0c, 0x7b, 0x0b, 0x7a, 0x63, 0xe4, 0xb4, 0x17, 0x8c, 0xad, + 0x91, 0x6f, 0xa3, 0x83, 0xa9, 0xa1, 0x03, 0xca, 0x80, 0xf7, 0x7d, 0x7b, 0x6c, 0xfe, 0xac, 0x0a, + 0xcd, 0xed, 0x20, 0x11, 0xb1, 0xc4, 0x55, 0x62, 0x8f, 0x46, 0xc2, 0x91, 0x42, 0x59, 0xa7, 0x3a, + 0xcf, 0xfb, 0xf8, 0x95, 0xfb, 0xe1, 0xe7, 0xb1, 0x27, 0xc5, 0xde, 0x07, 0x5a, 0x0f, 0x0a, 0x00, + 0xbb, 0x09, 0xcb, 0xb6, 0xeb, 0x5a, 0x19, 0xb5, 0x15, 0x87, 0x4f, 0x13, 0x5a, 0x31, 0x06, 0x5f, + 0xb2, 0x5d, 0x77, 0x43, 0xc3, 0x79, 0xf8, 0x34, 0x61, 0x6f, 0x42, 0x2d, 0x16, 0x23, 0xd2, 0x8a, + 0xce, 0xfa, 0x92, 0x92, 0xda, 0x67, 0x07, 0x3f, 0x14, 0x8e, 0xe4, 0x62, 0xc4, 0x11, 0xc7, 0xce, + 0x42, 0xc3, 0x96, 0x32, 0x56, 0x52, 0x68, 0x73, 0xd5, 0x61, 0x6b, 0x70, 0x86, 0x56, 0xa6, 0xf4, + 0xc2, 0xc0, 0x92, 0xf6, 0x81, 0x8f, 0x8e, 0x30, 0xd1, 0x36, 0x7f, 0x39, 0x47, 0xed, 0x23, 0x66, + 0xdb, 0x4d, 0xd0, 0x4b, 0xcc, 0xd2, 0x07, 0xf6, 0x44, 0x24, 0x64, 0xf2, 0xdb, 0xfc, 0xcc, 0xf4, + 0x88, 0x1d, 0x44, 0x21, 0xcb, 0x8a, 0x31, 0xb8, 0xb6, 0x0d, 0x5a, 0x26, 0xdd, 0x1c, 0x88, 0x4b, + 0xff, 0x1c, 0x34, 0xbd, 0xc4, 0x12, 0x81, 0xab, 0xcd, 0x4d, 0xc3, 0x4b, 0xee, 0x05, 0x2e, 0x7b, + 0x07, 0xda, 0xea, 0x2d, 0xae, 0x18, 0x91, 0x2f, 0xef, 0xac, 0xf7, 0xb5, 0x52, 0x22, 0x78, 0x4b, + 0x8c, 0xb8, 0x21, 0x75, 0xcb, 0xfc, 0xbb, 0x0a, 0x74, 0x48, 0x87, 0x1e, 0x45, 0x2e, 0x2e, 0xb9, + 0xb7, 0xa0, 0x37, 0xcd, 0x3d, 0x25, 0x80, 0xae, 0x5d, 0x66, 0xdd, 0x79, 0x68, 0x6e, 0x38, 0x38, + 0x0b, 0x92, 0x40, 0x8f, 0xeb, 0x1e, 0x2e, 0xeb, 0xed, 0xbb, 0xa9, 0x73, 0x24, 0x24, 0x31, 0xbd, + 0xc7, 0xb3, 0x2e, 0x62, 0x76, 0x34, 0xa6, 0xae, 0x30, 0xba, 0xcb, 0xbe, 0x05, 0x4b, 0x29, 0xbd, + 0xda, 0x72, 0xe4, 0xb1, 0xe5, 0xe3, 0xf2, 0x56, 0x3a, 0xaf, 0x45, 0xa2, 0xe6, 0xb5, 0x29, 0x8f, + 0x79, 0x2f, 0xcd, 0x9a, 0x0f, 0xbc, 0x44, 0x9a, 0x6f, 0x40, 0x63, 0x23, 0x8e, 0xed, 0x13, 0x92, + 0x12, 0x36, 0x86, 0x15, 0xb2, 0xf8, 0xaa, 0x63, 0x3a, 0x50, 0x7b, 0x68, 0x47, 0xec, 0x3a, 0x54, + 0x27, 0x11, 0x61, 0x3a, 0xeb, 0xe7, 0x4a, 0xab, 0xc8, 0x8e, 0xd6, 0x1e, 0x46, 0xf7, 0x02, 0x19, + 0x9f, 0xf0, 0xea, 0x24, 0xba, 0x78, 0x07, 0x5a, 0xba, 0x8b, 0x71, 0xdb, 0x91, 0x38, 0xa1, 0xef, + 0x6e, 0x73, 0x6c, 0xe2, 0x0b, 0x9e, 0xd8, 0x7e, 0x9a, 0x05, 0x1c, 0xaa, 0xf3, 0x6b, 0xd5, 0x0f, + 0x2b, 0xe6, 0x7f, 0xd5, 0xc1, 0xd8, 0x12, 0xbe, 0xa0, 0xaf, 0x37, 0xa1, 0x5b, 0x56, 0xb0, 0x8c, + 0x73, 0x53, 0x4a, 0x67, 0x42, 0x57, 0xf9, 0x20, 0x1a, 0x25, 0xb4, 0x06, 0x4f, 0xc1, 0xbe, 0x10, + 0x17, 0x2f, 0x03, 0xc4, 0xe1, 0x53, 0xcb, 0x53, 0x8e, 0x40, 0xd9, 0x54, 0x23, 0x0e, 0x9f, 0x6e, + 0xa3, 0x2b, 0xf8, 0x7f, 0xd1, 0xd8, 0x6f, 0xc1, 0xb0, 0xa4, 0xb1, 0x18, 0xe0, 0x59, 0x5e, 0x60, + 0x1d, 0x60, 0xb4, 0xa1, 0x95, 0xb7, 0x78, 0x26, 0xc5, 0x7f, 0xdb, 0xc1, 0x5d, 0x0a, 0x45, 0xf4, + 0x3a, 0x6c, 0x9f, 0xb2, 0x0e, 0x17, 0x2e, 0x6b, 0x58, 0xbc, 0xac, 0xef, 0x02, 0xec, 0x89, 0xf1, + 0x44, 0x04, 0xf2, 0xa1, 0x1d, 0x0d, 0x3b, 0x24, 0x78, 0xb3, 0x10, 0x7c, 0x26, 0xad, 0xb5, 0x82, + 0x48, 0x69, 0x41, 0x69, 0x14, 0xc6, 0x07, 0x8e, 0x1d, 0x58, 0x32, 0x4e, 0x03, 0xc7, 0x96, 0x62, + 0xd8, 0xa5, 0x57, 0x75, 0x1c, 0x3b, 0xd8, 0xd7, 0xa0, 0xd2, 0xda, 0xeb, 0x95, 0xd7, 0xde, 0x0d, + 0x58, 0x8a, 0x62, 0x6f, 0x62, 0xc7, 0x27, 0xd6, 0x91, 0x38, 0x21, 0x61, 0xf4, 0x55, 0x24, 0xab, + 0xc1, 0x9f, 0x8a, 0x93, 0x6d, 0xf7, 0xf8, 0xe2, 0xb7, 0x61, 0x69, 0x66, 0x02, 0xaf, 0xa4, 0x77, + 0xff, 0x54, 0x85, 0xf6, 0x6e, 0x2c, 0xb4, 0xbd, 0xbc, 0x0a, 0x9d, 0xc4, 0x39, 0x14, 0x13, 0x9b, + 0xa4, 0xa4, 0x9f, 0x00, 0x0a, 0x84, 0xc2, 0x99, 0xb6, 0x08, 0xd5, 0xd3, 0x2d, 0x02, 0xce, 0x43, + 0xc5, 0x19, 0xb8, 0x98, 0xb0, 0x59, 0x98, 0xc1, 0x7a, 0xd9, 0x0c, 0xae, 0x40, 0xf7, 0xd0, 0x4e, + 0x2c, 0x3b, 0x95, 0xa1, 0xe5, 0x84, 0x3e, 0x29, 0x9d, 0xc1, 0xe1, 0xd0, 0x4e, 0x36, 0x52, 0x19, + 0x6e, 0x86, 0x14, 0xb7, 0x78, 0x89, 0xa5, 0x56, 0xad, 0xf6, 0x48, 0x86, 0x97, 0x68, 0x43, 0xb3, + 0x06, 0x67, 0x44, 0x22, 0xbd, 0x89, 0xad, 0x05, 0x6a, 0x39, 0x61, 0x1a, 0x48, 0xf2, 0x4b, 0x35, + 0xbe, 0x9c, 0xa3, 0x78, 0xf8, 0x74, 0x13, 0x11, 0xec, 0x3d, 0xe8, 0x3b, 0xe1, 0x24, 0xb2, 0x22, + 0xe4, 0x2b, 0x79, 0x7c, 0x15, 0x02, 0x97, 0x3d, 0x72, 0x17, 0x29, 0x76, 0x8f, 0x84, 0x0a, 0x41, + 0xd6, 0x61, 0xc9, 0xf1, 0xd3, 0x44, 0x8a, 0xd8, 0x3a, 0xd0, 0x43, 0xda, 0x73, 0x43, 0x7a, 0x9a, + 0x44, 0x85, 0x2d, 0xc8, 0xd8, 0xd6, 0x6e, 0x98, 0xc8, 0xad, 0x89, 0x9f, 0x29, 0x66, 0xe5, 0x55, + 0x15, 0xb3, 0xba, 0x58, 0x31, 0x17, 0xa8, 0x46, 0x6d, 0x81, 0x6a, 0xb0, 0x55, 0x18, 0x94, 0xe9, + 0x48, 0xa4, 0x2a, 0x80, 0xea, 0x17, 0x84, 0x24, 0x56, 0xc5, 0x5f, 0x57, 0x59, 0x92, 0x46, 0xc6, + 0x5f, 0x6d, 0x45, 0x14, 0xd2, 0x23, 0x0d, 0x29, 0x98, 0xaf, 0x35, 0xe6, 0x57, 0xe1, 0xf5, 0x7c, + 0xa4, 0xf5, 0xd4, 0x93, 0x87, 0x61, 0x2a, 0xad, 0x11, 0xed, 0x15, 0x12, 0x1d, 0xef, 0x9e, 0xcf, + 0x9e, 0xf4, 0xb9, 0x42, 0xab, 0x9d, 0x04, 0x45, 0x27, 0xa3, 0xd4, 0xf7, 0x2d, 0x29, 0x8e, 0xa5, + 0x16, 0xc1, 0x50, 0xf1, 0x46, 0xf3, 0xed, 0x7e, 0xea, 0xfb, 0xfb, 0xe2, 0x58, 0xa2, 0xc9, 0x36, + 0x46, 0xba, 0x63, 0xfe, 0x6d, 0x0d, 0xe0, 0x41, 0xe8, 0x1c, 0xed, 0xdb, 0xf1, 0x58, 0x48, 0x8c, + 0xa2, 0x33, 0x3b, 0xa4, 0xed, 0x64, 0x4b, 0x2a, 0xeb, 0xc3, 0xd6, 0xe1, 0x7c, 0xf6, 0xfd, 0x4e, + 0xe8, 0x53, 0x44, 0xaf, 0x0c, 0x89, 0x5e, 0x06, 0x4c, 0x63, 0xd5, 0x9e, 0x90, 0xac, 0x08, 0xfb, + 0xb0, 0xe0, 0x2d, 0x8e, 0x91, 0x27, 0x11, 0xf1, 0x76, 0x51, 0x34, 0xd6, 0x2b, 0x86, 0xef, 0x9f, + 0x44, 0xec, 0x3d, 0x38, 0x17, 0x8b, 0x51, 0x2c, 0x92, 0x43, 0x4b, 0x26, 0xe5, 0x97, 0xa9, 0x60, + 0x7a, 0x59, 0x23, 0xf7, 0x93, 0xfc, 0x5d, 0xef, 0xc1, 0x39, 0xc5, 0xa9, 0xd9, 0xe9, 0x29, 0xab, + 0xbb, 0xac, 0x90, 0xe5, 0xd9, 0xbd, 0x01, 0x94, 0x76, 0x50, 0x96, 0x34, 0x0b, 0xcd, 0x7c, 0x62, + 0xc6, 0x81, 0x2f, 0x30, 0xa4, 0xd9, 0x3c, 0xc4, 0xfd, 0xde, 0x96, 0x18, 0x69, 0xe6, 0x17, 0x00, + 0x66, 0x42, 0xfd, 0x61, 0xe8, 0x0a, 0x62, 0x75, 0x7f, 0xbd, 0xbf, 0x46, 0x09, 0x0c, 0xe4, 0x24, + 0x42, 0x39, 0xe1, 0xd8, 0xdb, 0x40, 0x8f, 0x53, 0xea, 0x37, 0xaf, 0xe3, 0x06, 0x22, 0x49, 0x07, + 0xdf, 0x83, 0x73, 0xc5, 0x4c, 0x2c, 0x5b, 0x5a, 0xf2, 0x50, 0x90, 0x11, 0x53, 0xc6, 0x74, 0x39, + 0x9f, 0xd4, 0x86, 0xdc, 0x3f, 0x14, 0xf7, 0x02, 0xd7, 0xfc, 0x10, 0x9a, 0xf8, 0xb2, 0xcf, 0x22, + 0xb6, 0x06, 0x2d, 0x49, 0xc2, 0x4b, 0xb4, 0x3b, 0x3d, 0x5b, 0x58, 0xd5, 0x42, 0xb2, 0x3c, 0x23, + 0x32, 0x39, 0x2c, 0xe5, 0x26, 0xea, 0x51, 0xe0, 0x3d, 0x4e, 0x05, 0xfb, 0x18, 0x96, 0xa3, 0x58, + 0x68, 0xa5, 0xb4, 0xd2, 0x23, 0x74, 0xf9, 0x7a, 0x7d, 0x9d, 0xd5, 0x3a, 0x94, 0x8f, 0x38, 0x42, + 0xfd, 0xe9, 0x47, 0x53, 0x7d, 0xf3, 0x07, 0x70, 0x21, 0xa7, 0xd8, 0x13, 0x4e, 0x18, 0xb8, 0x76, + 0x7c, 0x42, 0xde, 0x64, 0xe6, 0xd9, 0xc9, 0xab, 0x3c, 0x7b, 0x8f, 0x9e, 0xfd, 0xa3, 0x1a, 0xf4, + 0x3f, 0x0b, 0xb6, 0xd2, 0xc8, 0xf7, 0xd0, 0xc2, 0x7f, 0xaa, 0x0c, 0xb0, 0x32, 0x7c, 0x95, 0xb2, + 0xe1, 0x5b, 0x85, 0x81, 0x7e, 0x0b, 0x2a, 0x80, 0x32, 0x5b, 0x3a, 0xc3, 0xa1, 0xe0, 0x9b, 0xa1, + 0xaf, 0x6c, 0xd6, 0xb7, 0xe1, 0x5c, 0x4a, 0x5f, 0xae, 0x28, 0x0f, 0x85, 0x73, 0x64, 0x3d, 0x67, + 0xb3, 0xc2, 0x14, 0x21, 0x0e, 0x45, 0x32, 0x32, 0x60, 0x57, 0xa1, 0x53, 0x0c, 0xcf, 0xac, 0x2f, + 0xe4, 0x84, 0x34, 0x93, 0x30, 0xb0, 0xdc, 0x6c, 0xca, 0xda, 0xf7, 0xa3, 0xdd, 0xee, 0x87, 0xc5, + 0x97, 0xa0, 0x51, 0xf9, 0x2d, 0x58, 0x9e, 0xa2, 0xa4, 0x59, 0x34, 0x69, 0x16, 0xb7, 0x0a, 0x31, + 0x4e, 0x7f, 0x7e, 0xb9, 0x8b, 0xf3, 0x51, 0x7e, 0x72, 0x29, 0x9c, 0x86, 0x66, 0x86, 0x66, 0x1c, + 0x84, 0xb1, 0xd0, 0xea, 0x8b, 0x86, 0x86, 0xfa, 0x17, 0x77, 0xe0, 0xec, 0xa2, 0xa7, 0x2c, 0x70, + 0x76, 0x2b, 0x65, 0x67, 0x37, 0xb3, 0xd1, 0x2a, 0x1c, 0xdf, 0x9f, 0x55, 0xa0, 0x73, 0x3f, 0x7d, + 0xf6, 0xec, 0x44, 0x99, 0x23, 0xd6, 0x85, 0xca, 0x0e, 0x3d, 0xa5, 0xca, 0x2b, 0x3b, 0x18, 0x97, + 0xee, 0x1e, 0xa1, 0x69, 0xa4, 0x87, 0xb4, 0xb9, 0xee, 0xe1, 0x16, 0x6d, 0xf7, 0x68, 0xff, 0x14, + 0xa3, 0xa0, 0xd0, 0xb8, 0xf1, 0xb8, 0x9b, 0x7a, 0x3e, 0xc6, 0x4c, 0x7a, 0xfd, 0xe7, 0x7d, 0xdc, + 0xf4, 0x6c, 0x8f, 0x94, 0xbe, 0xdc, 0x8f, 0xc3, 0x89, 0xd2, 0x68, 0x6d, 0x75, 0x17, 0x60, 0xcc, + 0x9f, 0xd4, 0xa0, 0xfe, 0x49, 0xe8, 0x05, 0x2a, 0x61, 0xe0, 0xab, 0xc8, 0x56, 0x45, 0xa8, 0xad, + 0x58, 0xf8, 0x18, 0xc2, 0x22, 0x0a, 0x15, 0xc3, 0x57, 0x7b, 0x5a, 0x42, 0x39, 0xa1, 0x42, 0x15, + 0xdb, 0xda, 0xca, 0xc2, 0x6d, 0x6d, 0xbe, 0xeb, 0xac, 0xbf, 0x68, 0xd7, 0xd9, 0xf6, 0xc5, 0x08, + 0x55, 0x35, 0x70, 0x75, 0x60, 0x3d, 0x6d, 0x1a, 0xc4, 0x48, 0x6e, 0x86, 0x81, 0xcb, 0xbe, 0x01, + 0x10, 0x7b, 0xe3, 0x43, 0x4d, 0xd9, 0x9c, 0xcf, 0x04, 0x10, 0x96, 0x48, 0x39, 0xbc, 0xae, 0xd3, + 0x4b, 0xda, 0x67, 0x58, 0x07, 0xc8, 0x25, 0xf5, 0x1d, 0xad, 0x6c, 0xc3, 0xba, 0x38, 0x31, 0x75, + 0x7e, 0x2a, 0x31, 0x45, 0xdc, 0xa5, 0xef, 0xbd, 0x0c, 0x18, 0x39, 0x1c, 0x5a, 0x61, 0x60, 0x45, + 0x59, 0x62, 0xc5, 0x40, 0xc8, 0x67, 0xc1, 0xee, 0x11, 0x5a, 0x50, 0x2f, 0xb1, 0x74, 0x7e, 0x46, + 0xef, 0x76, 0x4a, 0x9b, 0xdb, 0x15, 0xe8, 0xfe, 0x30, 0xf4, 0x02, 0x6b, 0x62, 0x47, 0x96, 0xb4, + 0x55, 0x02, 0xb3, 0xc1, 0x01, 0x61, 0x0f, 0xed, 0x68, 0xdf, 0x1e, 0x53, 0x88, 0xa4, 0x33, 0x3e, + 0xb8, 0x48, 0x3a, 0x8a, 0x40, 0x83, 0x50, 0xbc, 0x97, 0xa0, 0x4d, 0x8f, 0xa0, 0x7c, 0x50, 0x57, + 0xc9, 0x1e, 0x01, 0xc8, 0x51, 0xf3, 0xdf, 0xaa, 0x60, 0x6c, 0x04, 0xd2, 0x23, 0x79, 0x9e, 0x87, + 0x66, 0x4c, 0x9b, 0x5b, 0x2d, 0x4d, 0xdd, 0xcb, 0x25, 0x56, 0x7d, 0x8e, 0xc4, 0xa6, 0x24, 0x51, + 0x7b, 0x69, 0x49, 0xd4, 0x4f, 0x93, 0xc4, 0x34, 0xd7, 0x1a, 0xa7, 0x72, 0x6d, 0x2e, 0x25, 0xf0, + 0x75, 0x88, 0x71, 0x56, 0x12, 0xc6, 0x8b, 0x24, 0xd1, 0x9e, 0x95, 0x84, 0xf9, 0x57, 0x35, 0x30, + 0x1e, 0x88, 0x91, 0xfc, 0xe5, 0xe2, 0xf9, 0x45, 0x59, 0x3c, 0xe6, 0x7f, 0xd6, 0xa0, 0xcd, 0xf1, + 0x0b, 0xbf, 0x46, 0x99, 0xdd, 0x06, 0x20, 0x59, 0x9c, 0x2e, 0x38, 0x92, 0x97, 0xca, 0x3a, 0xbd, + 0x0f, 0x1d, 0x25, 0x13, 0x35, 0xa2, 0xf1, 0x9c, 0x11, 0x4a, 0x70, 0xfb, 0xf3, 0xf2, 0x6e, 0xbe, + 0xb4, 0xbc, 0x5b, 0x5f, 0x58, 0xde, 0xc6, 0x57, 0x21, 0xef, 0xf6, 0xa9, 0xf2, 0x86, 0x17, 0xc9, + 0xbb, 0xf3, 0x22, 0x79, 0x77, 0xe7, 0xe4, 0xfd, 0xa3, 0x1a, 0xf4, 0x48, 0xde, 0x7b, 0x62, 0xf2, + 0xe5, 0x8c, 0xe2, 0x8c, 0x90, 0x6a, 0xaf, 0x2a, 0xa4, 0xfa, 0x4b, 0x0b, 0xa9, 0xf1, 0x85, 0x85, + 0xd4, 0xfc, 0x2a, 0x84, 0xd4, 0x3a, 0x55, 0x48, 0xc6, 0x8b, 0x84, 0xd4, 0x7e, 0xf5, 0x45, 0x99, + 0x0b, 0xe9, 0x4b, 0x7b, 0xae, 0x5f, 0x0a, 0xe9, 0x2b, 0x12, 0x12, 0xcc, 0x09, 0x09, 0x23, 0x8b, + 0x2f, 0xbd, 0x88, 0xbe, 0x8e, 0xc8, 0xe2, 0x54, 0x66, 0x37, 0xbe, 0x0a, 0x66, 0x37, 0x4f, 0x65, + 0x76, 0xeb, 0x45, 0xcc, 0xfe, 0x02, 0x91, 0xc5, 0x5f, 0xd7, 0x00, 0xf6, 0xbc, 0x60, 0xec, 0x8b, + 0x5f, 0xc6, 0x16, 0xbf, 0x30, 0xb1, 0xc5, 0xdf, 0x57, 0xc1, 0x78, 0x68, 0xc7, 0x47, 0x3f, 0x77, + 0x2b, 0xe4, 0x2d, 0x68, 0x85, 0x41, 0x79, 0x3d, 0x94, 0xe9, 0x9a, 0x61, 0xf0, 0x73, 0xa1, 0xf2, + 0x3f, 0x69, 0x40, 0x7b, 0x4b, 0xb8, 0x69, 0xf4, 0x25, 0x34, 0xfe, 0x17, 0xc5, 0xbc, 0xbc, 0x60, + 0xbb, 0x33, 0xcb, 0xcd, 0xd6, 0x8b, 0xb8, 0x69, 0xcc, 0x6d, 0x12, 0x1f, 0xc0, 0x99, 0xa9, 0x2c, + 0x8a, 0xad, 0x0e, 0xc1, 0xda, 0x94, 0x9a, 0xbb, 0xac, 0xe6, 0xbb, 0x13, 0xba, 0x53, 0x89, 0x14, + 0x75, 0x34, 0xc6, 0x97, 0xc3, 0x59, 0x10, 0xbb, 0x06, 0x7d, 0x17, 0x45, 0x43, 0xc9, 0x21, 0x4a, + 0xf3, 0xaa, 0xc2, 0x9b, 0x2e, 0x41, 0x37, 0x43, 0x9f, 0x72, 0x17, 0x1f, 0xc2, 0x52, 0x41, 0xa5, + 0x2c, 0x4b, 0xe7, 0x39, 0x96, 0xa5, 0x97, 0x0d, 0x54, 0x3e, 0x78, 0x3a, 0x62, 0xee, 0xbe, 0x72, + 0xc4, 0xdc, 0x7b, 0x09, 0x3f, 0x7f, 0x0b, 0xce, 0x64, 0xa7, 0x77, 0x3a, 0x19, 0x4a, 0x12, 0xec, + 0x93, 0x06, 0x0d, 0xf4, 0x81, 0x1d, 0xa5, 0x42, 0x49, 0x44, 0x1f, 0xc1, 0xd9, 0x12, 0x39, 0x2e, + 0x4d, 0x45, 0xbf, 0x34, 0xa7, 0x2b, 0xcb, 0xf9, 0x58, 0xec, 0xd2, 0x81, 0xdf, 0xef, 0x54, 0xa0, + 0xb5, 0x1b, 0x87, 0x6e, 0xea, 0xc8, 0x2f, 0xa8, 0xc9, 0xd3, 0x1a, 0x52, 0x7b, 0x91, 0x86, 0xd4, + 0x67, 0x35, 0xc4, 0xfc, 0xdd, 0x0a, 0xb4, 0xf5, 0x14, 0x1e, 0xac, 0x7f, 0x4d, 0x0e, 0xe4, 0xc5, + 0xb3, 0x78, 0x0a, 0x6d, 0xca, 0x79, 0x9e, 0x6a, 0x12, 0x4f, 0x5d, 0x61, 0xd5, 0x2f, 0xb4, 0xc2, + 0xcc, 0x3f, 0xa8, 0x40, 0x8f, 0xd2, 0xc3, 0xf7, 0xd3, 0x40, 0xe9, 0xf0, 0xe2, 0x0c, 0xe9, 0x0a, + 0xd4, 0x63, 0x21, 0xb3, 0x9a, 0x89, 0xae, 0x7a, 0xcd, 0x66, 0xe8, 0x6f, 0x89, 0x11, 0x27, 0x0c, + 0x32, 0xc1, 0x8e, 0xc7, 0xc9, 0xa2, 0xaa, 0x0d, 0x84, 0xe3, 0x57, 0x45, 0x76, 0x6c, 0x4f, 0x92, + 0xac, 0x6a, 0x43, 0xf5, 0x18, 0x83, 0x3a, 0xad, 0x94, 0x06, 0xad, 0x14, 0x6a, 0x9b, 0x1b, 0x70, + 0xee, 0xde, 0xb1, 0x14, 0x71, 0x60, 0xd3, 0x8a, 0x59, 0x47, 0x7d, 0xa3, 0x94, 0x70, 0x46, 0x5c, + 0x29, 0x88, 0x71, 0xc2, 0xe5, 0x9a, 0x34, 0xd5, 0x31, 0xaf, 0x43, 0x67, 0xe4, 0xf9, 0xc2, 0x0a, + 0x47, 0xa3, 0x44, 0x48, 0x7c, 0xbb, 0x6a, 0xd1, 0x67, 0xd5, 0xb8, 0xee, 0x99, 0x7f, 0x5c, 0x83, + 0x6e, 0xf6, 0x2a, 0xaa, 0xd9, 0x79, 0xb7, 0xfc, 0xf9, 0x9d, 0xf5, 0x41, 0xf6, 0x1d, 0x48, 0xb2, + 0x21, 0x65, 0x9c, 0xb9, 0x7c, 0xc5, 0x96, 0x4b, 0xd0, 0xa6, 0xb7, 0x24, 0xde, 0x33, 0x41, 0xbc, + 0xa9, 0x71, 0x03, 0x01, 0x54, 0x7c, 0xb1, 0x01, 0xcb, 0xa5, 0x29, 0x58, 0x32, 0x94, 0xb6, 0xaf, + 0xd9, 0x53, 0x3a, 0xb7, 0x2e, 0x91, 0xf0, 0x25, 0xec, 0x7c, 0x46, 0xed, 0x7d, 0xa4, 0x46, 0xb6, + 0xe7, 0x89, 0xe2, 0x39, 0xb6, 0x23, 0x06, 0x17, 0x80, 0x13, 0x0b, 0x5c, 0x7f, 0xc9, 0x63, 0x5f, + 0x33, 0xb1, 0xad, 0x20, 0x7b, 0x8f, 0xfd, 0x7c, 0x82, 0x79, 0x44, 0xdd, 0x56, 0x13, 0x24, 0xbd, + 0xbd, 0x05, 0x9d, 0x30, 0xf6, 0xc6, 0x5e, 0xa0, 0xb2, 0xd1, 0xad, 0x05, 0x2f, 0x01, 0x45, 0x40, + 0xb9, 0x69, 0x13, 0x9a, 0x4a, 0xef, 0x16, 0x9c, 0xd3, 0x69, 0x0c, 0xbb, 0x01, 0x4b, 0x89, 0x8c, + 0x3d, 0x47, 0xe2, 0x74, 0xac, 0x49, 0xe8, 0x66, 0x21, 0x44, 0x4f, 0x81, 0xf7, 0x1e, 0xfb, 0x74, + 0xbe, 0x71, 0x03, 0x96, 0x9c, 0xd0, 0x4f, 0x27, 0xca, 0xdd, 0x5a, 0xbe, 0x08, 0x74, 0x24, 0xd1, + 0x53, 0x60, 0x9c, 0xdf, 0x03, 0x11, 0x98, 0x0e, 0xc0, 0x9e, 0x8c, 0x85, 0x3d, 0x21, 0xe1, 0xbc, + 0x0d, 0x2d, 0x79, 0xe0, 0xd3, 0x99, 0x67, 0x65, 0xe1, 0x99, 0x67, 0x53, 0x1e, 0xe0, 0xb4, 0x4b, + 0xe2, 0xae, 0xd2, 0xe9, 0xa3, 0xee, 0xa1, 0xae, 0xf8, 0xde, 0xc4, 0x93, 0xba, 0x88, 0x50, 0x75, + 0xcc, 0x0f, 0xa0, 0x4d, 0x4f, 0xa0, 0x77, 0xe4, 0xd1, 0x5e, 0xe5, 0xd4, 0x68, 0xcf, 0x7c, 0x17, + 0xda, 0xbf, 0x69, 0xfb, 0xa9, 0x1a, 0x74, 0x15, 0x3a, 0x74, 0x2e, 0x6e, 0x1d, 0xf8, 0xa1, 0x73, + 0x94, 0x9d, 0xd7, 0x12, 0xe8, 0x2e, 0x42, 0x4c, 0x00, 0xe3, 0x51, 0xe0, 0x85, 0xc1, 0x86, 0xef, + 0x9b, 0x7f, 0x58, 0x87, 0xf6, 0xf7, 0xed, 0xe4, 0x90, 0x56, 0x21, 0x5b, 0x81, 0xce, 0x8e, 0x10, + 0x2e, 0x02, 0x1e, 0xda, 0x91, 0xae, 0x56, 0x2a, 0x83, 0xd8, 0x45, 0x30, 0xbe, 0xaf, 0xe2, 0x8b, + 0x4f, 0xf5, 0x49, 0x64, 0xde, 0xcf, 0x46, 0xd3, 0xb9, 0xbb, 0xc8, 0x0a, 0x63, 0xca, 0x20, 0x76, + 0x13, 0x06, 0xd8, 0xa5, 0x9a, 0x20, 0x54, 0x0a, 0xe1, 0xab, 0x15, 0x68, 0xf0, 0x39, 0x38, 0xbb, + 0x09, 0x80, 0xbe, 0x9c, 0x4e, 0xf4, 0x93, 0x05, 0x31, 0x50, 0x09, 0xcb, 0xae, 0x00, 0x7c, 0x92, + 0x1b, 0x30, 0x5d, 0x6f, 0x57, 0x82, 0xb0, 0x6b, 0xd0, 0xd3, 0x3d, 0x2e, 0x46, 0x9b, 0xfa, 0x1c, + 0xb8, 0xc1, 0xa7, 0x81, 0xec, 0x1e, 0x2c, 0xf3, 0x57, 0xae, 0x84, 0x9c, 0x03, 0xa1, 0x71, 0xa6, + 0xd3, 0x4f, 0x37, 0x8d, 0xb4, 0xbe, 0xb5, 0xbc, 0x84, 0xa2, 0xa4, 0xe7, 0x79, 0x78, 0xf8, 0xaa, + 0x3c, 0x7c, 0xe7, 0xe5, 0x3c, 0x7c, 0xf7, 0xa5, 0x3c, 0xbc, 0xf9, 0xb3, 0x1a, 0x74, 0xb5, 0xf3, + 0x22, 0xe3, 0x3e, 0x25, 0xfc, 0xca, 0xe9, 0xc2, 0xaf, 0xbe, 0x9c, 0xf0, 0x6b, 0x2f, 0x25, 0xfc, + 0xfa, 0xa9, 0xc2, 0x5f, 0x28, 0xb6, 0xc6, 0x2b, 0x8b, 0xed, 0x45, 0x3a, 0x74, 0x05, 0x60, 0x2f, + 0x8f, 0xd5, 0xb2, 0xf0, 0xae, 0x80, 0x4c, 0x89, 0xdd, 0x78, 0x29, 0xb1, 0xff, 0x7c, 0x06, 0x76, + 0xe6, 0x1e, 0x00, 0x39, 0x38, 0x25, 0xf3, 0x85, 0xdc, 0xad, 0xbc, 0x2a, 0x77, 0xcd, 0xff, 0xad, + 0x00, 0xec, 0xd9, 0x93, 0x48, 0x39, 0x77, 0xf6, 0x5d, 0xe8, 0x24, 0xd4, 0x53, 0x27, 0x22, 0xaa, + 0x1a, 0xfe, 0x6a, 0xa9, 0x1a, 0x3e, 0x27, 0xd5, 0x4d, 0x9c, 0x1a, 0x87, 0x24, 0x6f, 0x53, 0x34, + 0xad, 0x9e, 0x90, 0x57, 0x45, 0x34, 0x32, 0x02, 0x3a, 0x8c, 0xbe, 0x0e, 0x7d, 0x4d, 0x10, 0x89, + 0xd8, 0x11, 0x81, 0xb2, 0xb3, 0x15, 0xde, 0x53, 0xd0, 0x5d, 0x05, 0x64, 0xef, 0xe7, 0x64, 0xca, + 0xd8, 0x2f, 0xd2, 0x36, 0x3d, 0x64, 0x53, 0x11, 0x98, 0xeb, 0xd9, 0xa7, 0xd0, 0x44, 0x0c, 0xa8, + 0xe3, 0xfb, 0x06, 0xaf, 0xb1, 0x0e, 0xb4, 0xf4, 0x53, 0x07, 0x15, 0xd6, 0x83, 0x36, 0x15, 0xe5, + 0x12, 0xae, 0x6a, 0xfe, 0xe9, 0x19, 0xe8, 0x6c, 0x07, 0x89, 0x8c, 0x53, 0x25, 0xc4, 0xa2, 0xf6, + 0xb4, 0x41, 0xb5, 0xa7, 0xba, 0x2c, 0x46, 0x7d, 0x06, 0x95, 0xc5, 0xdc, 0x80, 0xba, 0x1d, 0x48, + 0x4f, 0x07, 0x72, 0xa5, 0x02, 0xe7, 0x2c, 0xe1, 0xc6, 0x09, 0xcf, 0x6e, 0x41, 0x4b, 0x57, 0x43, + 0xeb, 0x62, 0xc3, 0x85, 0xa5, 0xd4, 0x19, 0x0d, 0x5b, 0x03, 0xc3, 0xd5, 0x65, 0xda, 0x7a, 0x91, + 0x94, 0x1e, 0x9d, 0x15, 0x70, 0xf3, 0x9c, 0x86, 0xbd, 0x09, 0x35, 0x7b, 0xac, 0xd6, 0x03, 0x95, + 0xa9, 0x64, 0xa4, 0x54, 0xdc, 0xca, 0x11, 0xc7, 0x4c, 0xa8, 0x63, 0xf8, 0x48, 0x6b, 0x82, 0xdc, + 0x60, 0x46, 0xa3, 0x66, 0x89, 0x38, 0x76, 0x5b, 0xef, 0xf2, 0x88, 0xd0, 0x98, 0x7d, 0x6f, 0x76, + 0x20, 0xa3, 0x76, 0x7b, 0x9f, 0xe8, 0x01, 0x89, 0x98, 0x78, 0x6a, 0x40, 0x7b, 0x76, 0x40, 0x96, + 0xd4, 0xe2, 0x46, 0x92, 0xa5, 0xb7, 0xee, 0x40, 0x27, 0xa1, 0xec, 0x8b, 0x1a, 0x02, 0xd9, 0xd9, + 0x7c, 0x3e, 0x24, 0x4f, 0xcd, 0x70, 0x48, 0x8a, 0x34, 0xcd, 0x6d, 0x68, 0x4f, 0xec, 0xf8, 0x48, + 0x0d, 0xea, 0xcc, 0xbe, 0x27, 0x4b, 0x0d, 0x70, 0x63, 0x92, 0x25, 0x09, 0xd6, 0x01, 0xd4, 0xc2, + 0xa2, 0x11, 0xdd, 0x59, 0x96, 0xe7, 0xdb, 0x61, 0xde, 0x76, 0xf3, 0x9d, 0xf1, 0x3b, 0xd0, 0x8a, + 0x54, 0x5c, 0x4f, 0xf5, 0x5c, 0x9d, 0xf5, 0xe5, 0x62, 0x80, 0x0e, 0xf8, 0x79, 0x46, 0xc1, 0xbe, + 0x03, 0x7d, 0x55, 0x40, 0x31, 0xd2, 0x61, 0x30, 0xd5, 0x78, 0x4d, 0x55, 0xe9, 0x4e, 0x45, 0xc9, + 0xbc, 0x27, 0xa7, 0x82, 0xe6, 0x8f, 0xa0, 0x27, 0x74, 0x14, 0x69, 0x25, 0x8e, 0x1d, 0x0c, 0x07, + 0x34, 0xfc, 0x7c, 0x31, 0xbc, 0x1c, 0x64, 0xf2, 0xae, 0x28, 0x87, 0x9c, 0xab, 0xd0, 0xd4, 0x45, + 0x3d, 0xcb, 0x34, 0xaa, 0x74, 0xfb, 0x44, 0x9d, 0x41, 0x73, 0x8d, 0x67, 0x77, 0x67, 0xaa, 0x03, + 0x8e, 0xc4, 0xc9, 0x90, 0x65, 0x05, 0x3b, 0x8b, 0x8f, 0xfc, 0xa7, 0xea, 0x06, 0x3e, 0x15, 0x27, + 0xc8, 0xcb, 0xa2, 0xaa, 0x62, 0x78, 0x66, 0x96, 0x97, 0x79, 0x49, 0x05, 0x6f, 0xe7, 0xd5, 0x14, + 0x68, 0x90, 0xca, 0x55, 0x1e, 0xea, 0xa0, 0xfc, 0x2c, 0x0d, 0x7d, 0x7d, 0xc1, 0x50, 0x75, 0x5e, + 0xce, 0x97, 0xa2, 0x99, 0x62, 0x91, 0x77, 0xc1, 0x08, 0x63, 0x97, 0x8a, 0xb7, 0x86, 0xe7, 0x68, + 0xc5, 0x2f, 0xeb, 0x1a, 0x2c, 0x55, 0x66, 0x4e, 0x86, 0xac, 0x15, 0xaa, 0x0e, 0xbb, 0x05, 0xdd, + 0x28, 0x0e, 0x7f, 0x28, 0x1c, 0xa9, 0xa2, 0xd7, 0xf3, 0xf3, 0xe5, 0xe9, 0x1a, 0x4f, 0xc1, 0x6c, + 0x11, 0x9d, 0x5e, 0x78, 0x6e, 0x74, 0xba, 0x92, 0x85, 0x7f, 0xc3, 0xf9, 0x8a, 0x04, 0x42, 0xe0, + 0x53, 0x74, 0xe0, 0xf8, 0xfa, 0xfc, 0x53, 0x74, 0x10, 0x39, 0x84, 0x96, 0x97, 0xdc, 0xf7, 0xe2, + 0x44, 0x0e, 0x2f, 0x66, 0x4e, 0x87, 0xba, 0x18, 0x76, 0x7a, 0xc9, 0x03, 0x3b, 0x91, 0xc3, 0x4b, + 0xd9, 0x0d, 0x03, 0xec, 0x21, 0xcf, 0xd5, 0x36, 0x9c, 0xf4, 0xf7, 0xf2, 0x2c, 0xcf, 0xf3, 0x83, + 0x36, 0x9d, 0x4f, 0x21, 0xfd, 0xfd, 0x18, 0x96, 0xd4, 0x98, 0x62, 0x49, 0xbe, 0x31, 0xab, 0x93, + 0x53, 0x27, 0x36, 0xbc, 0x17, 0x4f, 0x1d, 0xe0, 0xe4, 0x0f, 0x40, 0x93, 0xa5, 0x1e, 0x70, 0x65, + 0xe1, 0x03, 0x72, 0xe3, 0xa6, 0x1e, 0x90, 0x1f, 0x2e, 0xdc, 0x84, 0xa6, 0xae, 0x44, 0xbb, 0x3a, + 0x67, 0xb4, 0x74, 0xcd, 0x25, 0xd7, 0x14, 0xec, 0x1b, 0xd0, 0xa2, 0x32, 0xa4, 0x30, 0x1a, 0xae, + 0xcc, 0x2a, 0xb1, 0xaa, 0x36, 0xe2, 0x4d, 0x5f, 0x55, 0x1d, 0xbd, 0x03, 0xad, 0x6c, 0xbf, 0xfe, + 0xe6, 0xec, 0xc2, 0xd4, 0xbe, 0x9d, 0x67, 0x14, 0xec, 0x3a, 0x34, 0x26, 0x68, 0xd2, 0x87, 0xe6, + 0xac, 0x31, 0x54, 0x96, 0x5e, 0x61, 0xc9, 0x10, 0xd1, 0x36, 0x41, 0xad, 0xbe, 0xb7, 0xe6, 0x0c, + 0x51, 0xbe, 0x87, 0xe0, 0x90, 0x14, 0xfb, 0x89, 0xdf, 0x86, 0x8b, 0xe5, 0x0a, 0xa3, 0xac, 0xfc, + 0x48, 0xdf, 0x71, 0xba, 0x46, 0x4f, 0x79, 0x73, 0x81, 0x82, 0x4f, 0x17, 0x2a, 0xf1, 0x0b, 0xd1, + 0x73, 0x2a, 0x98, 0xee, 0xe4, 0x0e, 0x13, 0xed, 0xca, 0xf0, 0xfa, 0xdc, 0xb4, 0x72, 0x97, 0x9b, + 0xb9, 0x51, 0xf2, 0xd4, 0x1f, 0x42, 0x77, 0x94, 0x3e, 0x7b, 0x76, 0xa2, 0xb7, 0xf9, 0xc3, 0x1b, + 0x34, 0xae, 0xb4, 0x67, 0x2c, 0xd5, 0xcb, 0xf0, 0xce, 0xa8, 0x54, 0x3c, 0x73, 0x01, 0x5a, 0x4e, + 0x60, 0xd9, 0xae, 0x1b, 0x0f, 0xdf, 0x56, 0xf5, 0x32, 0x4e, 0xb0, 0xe1, 0xba, 0x54, 0x78, 0x14, + 0x46, 0x82, 0xae, 0x6a, 0x58, 0x9e, 0x3b, 0x5c, 0x55, 0xae, 0x3b, 0x03, 0x6d, 0xbb, 0x74, 0x09, + 0xcc, 0x8e, 0x6d, 0xdf, 0x17, 0x3e, 0x12, 0x7c, 0x43, 0x5f, 0x02, 0xd3, 0xa0, 0x6d, 0x97, 0xbd, + 0x09, 0xdd, 0x89, 0x7d, 0x6c, 0x65, 0x90, 0xe1, 0x4d, 0x75, 0xc3, 0x66, 0x62, 0x1f, 0xef, 0x6a, + 0x10, 0xaa, 0xb9, 0x2a, 0x13, 0x26, 0x65, 0x7b, 0x67, 0x56, 0xcd, 0xf3, 0x0c, 0x07, 0x6f, 0x7b, + 0x79, 0xb2, 0x83, 0xcc, 0x11, 0x19, 0x61, 0xcb, 0x5f, 0x1f, 0xbe, 0x3b, 0x6f, 0x8e, 0x74, 0x6a, + 0x06, 0xcd, 0x51, 0x96, 0xa5, 0x59, 0x07, 0x50, 0xd6, 0x9a, 0x84, 0x7d, 0x6b, 0x76, 0x4c, 0xbe, + 0x97, 0xe3, 0xaa, 0x46, 0x96, 0x44, 0xbd, 0x0e, 0x40, 0x35, 0x47, 0x6a, 0xcc, 0xda, 0xec, 0x98, + 0x7c, 0x2b, 0xc7, 0xdb, 0x4f, 0xf2, 0x5d, 0xdd, 0x6d, 0x68, 0xa7, 0xb8, 0x69, 0xb3, 0x6c, 0xdf, + 0x1f, 0xde, 0x9e, 0x5d, 0x03, 0xd9, 0x7e, 0x8e, 0x1b, 0xa9, 0x6e, 0xe1, 0x4b, 0x28, 0x37, 0x4c, + 0x61, 0xdc, 0xf0, 0xbd, 0xd9, 0x97, 0xe4, 0x9b, 0x3e, 0xde, 0x3e, 0xcc, 0xf7, 0x7f, 0x1f, 0x41, + 0x2f, 0x4b, 0x51, 0xaa, 0x61, 0xef, 0xcf, 0xba, 0x8e, 0xf2, 0x7e, 0x80, 0x67, 0xd7, 0x9c, 0xd4, + 0xe0, 0x3b, 0xd0, 0x51, 0x1c, 0x57, 0x43, 0xd7, 0x67, 0x15, 0xac, 0x08, 0x2a, 0xb9, 0x12, 0x8d, + 0x1a, 0x76, 0x1d, 0x1a, 0x76, 0x14, 0xf9, 0x27, 0xc3, 0x0f, 0x66, 0x57, 0xd5, 0x06, 0x82, 0xb9, + 0xc2, 0xa2, 0x1e, 0x4e, 0x52, 0x5f, 0x7a, 0x59, 0xc1, 0xef, 0x37, 0x67, 0xf5, 0xb0, 0x74, 0xcd, + 0x80, 0x77, 0x26, 0xa5, 0x3b, 0x07, 0xef, 0x82, 0x11, 0x85, 0x89, 0xb4, 0xdc, 0x89, 0x3f, 0xbc, + 0x33, 0xe7, 0x7d, 0x55, 0x55, 0x29, 0x6f, 0x45, 0xaa, 0x61, 0xde, 0x81, 0xee, 0x06, 0x5d, 0x7e, + 0xf4, 0x12, 0x32, 0xe5, 0xd7, 0xa1, 0x9e, 0x67, 0xe0, 0x72, 0x1f, 0x41, 0x14, 0xcf, 0xc4, 0x76, + 0x30, 0x0a, 0x39, 0xa1, 0xcd, 0x7f, 0xa8, 0x41, 0x73, 0x2f, 0x4c, 0x63, 0x47, 0xbc, 0xb8, 0x5e, + 0xfa, 0x8d, 0x4c, 0x65, 0x82, 0xa2, 0x96, 0x4c, 0x69, 0x07, 0xa1, 0xcb, 0xc9, 0xbd, 0x1a, 0x65, + 0x49, 0xf2, 0xe4, 0xde, 0x59, 0x68, 0xa8, 0x4d, 0xbd, 0xaa, 0xd8, 0x55, 0x1d, 0x5a, 0x2e, 0x69, + 0x72, 0xe8, 0x86, 0x4f, 0x03, 0x5c, 0x2e, 0x0d, 0x2a, 0x78, 0x85, 0x0c, 0xb4, 0xed, 0xd2, 0x75, + 0x8f, 0x8c, 0x80, 0xd6, 0x63, 0x53, 0x6d, 0x18, 0x32, 0x20, 0xad, 0xca, 0x2c, 0x71, 0xd8, 0x7a, + 0x4e, 0xe2, 0xf0, 0x0a, 0xd4, 0x83, 0xac, 0x52, 0x34, 0xc7, 0xd3, 0x45, 0x3b, 0x82, 0xb3, 0x9b, + 0x90, 0x17, 0x79, 0xeb, 0x78, 0xed, 0xf9, 0x45, 0xe0, 0xeb, 0xd0, 0xce, 0xaf, 0xce, 0xe6, 0x91, + 0x5a, 0x71, 0x99, 0x76, 0x3f, 0x6b, 0xf1, 0x82, 0x6c, 0x41, 0xc6, 0x31, 0x8a, 0xc3, 0x03, 0x9d, + 0x4d, 0xea, 0xbc, 0x4a, 0xc6, 0x71, 0x17, 0xc7, 0x65, 0x79, 0x54, 0x2f, 0xb1, 0x9c, 0x30, 0x48, + 0xa4, 0xae, 0xc2, 0x6f, 0x79, 0xc9, 0x26, 0x76, 0xcd, 0x08, 0x0c, 0xfc, 0x38, 0x14, 0x31, 0x63, + 0x50, 0x9f, 0x38, 0x51, 0xaa, 0xc3, 0x75, 0x6a, 0xeb, 0x9b, 0xb1, 0x4a, 0x78, 0xfa, 0x66, 0x2c, + 0xb1, 0xb6, 0xa6, 0xb2, 0x81, 0xd8, 0x66, 0xe7, 0xa0, 0xe9, 0x04, 0x96, 0x13, 0x64, 0xe5, 0xbe, + 0x0d, 0x27, 0xd8, 0x0c, 0xa4, 0x06, 0x17, 0x37, 0x29, 0x1a, 0x4e, 0xb0, 0xed, 0x1e, 0x9b, 0x7f, + 0x51, 0x81, 0xe5, 0xdd, 0x38, 0x74, 0x44, 0x92, 0x3c, 0x40, 0xff, 0x6f, 0x53, 0x34, 0xc7, 0xa0, + 0x4e, 0x09, 0x3d, 0x75, 0x4d, 0x8d, 0xda, 0xa8, 0x40, 0x2a, 0xc3, 0x93, 0x6f, 0x7d, 0x6a, 0xbc, + 0x4d, 0x10, 0xda, 0xf9, 0xe4, 0x68, 0x1a, 0x58, 0x2b, 0xa1, 0x29, 0x15, 0x78, 0x1d, 0xfa, 0xc5, + 0x55, 0x0a, 0x7a, 0x82, 0xbe, 0x9f, 0x9a, 0x43, 0xe9, 0x29, 0x57, 0xa1, 0x13, 0x0b, 0x1b, 0x23, + 0x24, 0x7a, 0x4c, 0x83, 0x68, 0x40, 0x81, 0xf0, 0x39, 0xe6, 0x21, 0x0c, 0x76, 0x63, 0x11, 0xd9, + 0xb1, 0x40, 0xa3, 0x3b, 0x21, 0x4e, 0x9d, 0x87, 0xa6, 0x2f, 0x82, 0xb1, 0x3c, 0xd4, 0xf3, 0xd5, + 0xbd, 0xfc, 0x6e, 0x72, 0xb5, 0x74, 0x37, 0x19, 0x39, 0x16, 0x0b, 0x5b, 0x5f, 0x61, 0xa6, 0x36, + 0x2a, 0x78, 0x90, 0xfa, 0x3a, 0xc9, 0x68, 0x70, 0xd5, 0x31, 0xff, 0xbc, 0x06, 0x1d, 0xcd, 0x19, + 0x7a, 0x8b, 0xe2, 0x7d, 0x25, 0xe7, 0xfd, 0x00, 0x6a, 0xc9, 0x63, 0x5f, 0x0b, 0x03, 0x9b, 0xec, + 0x03, 0xa8, 0xf9, 0xde, 0x44, 0xef, 0x9d, 0x2e, 0x4d, 0x99, 0xf0, 0x69, 0xfe, 0xea, 0x2d, 0x30, + 0x52, 0xb3, 0x4b, 0x64, 0x62, 0x8f, 0x2d, 0xd4, 0x14, 0xcd, 0x13, 0x34, 0xa7, 0xc7, 0xa8, 0x8e, + 0xc8, 0x54, 0xdb, 0xa1, 0x6a, 0xdc, 0x6c, 0x8d, 0xf5, 0x78, 0x5b, 0x43, 0xb6, 0x5d, 0xf6, 0x4d, + 0x30, 0x92, 0xc0, 0x8e, 0x92, 0xc3, 0x50, 0xea, 0xbd, 0x12, 0x5b, 0x93, 0xc7, 0xc1, 0xda, 0xe6, + 0xce, 0xfe, 0x71, 0xb0, 0xa7, 0x31, 0xfa, 0x65, 0x39, 0x25, 0xfb, 0x0e, 0x74, 0x13, 0x91, 0x24, + 0xea, 0x4e, 0xcb, 0x28, 0xd4, 0x6b, 0xef, 0x5c, 0x79, 0x9f, 0x43, 0x58, 0xfc, 0x6a, 0x3d, 0xb8, + 0x93, 0x14, 0x20, 0xf6, 0x7d, 0xe8, 0x67, 0xe3, 0xfd, 0x70, 0x3c, 0xce, 0xb3, 0xa1, 0x97, 0xe6, + 0x9e, 0xf0, 0x80, 0xd0, 0xa5, 0xe7, 0xf4, 0x92, 0x32, 0x82, 0x7d, 0x0f, 0xfa, 0x91, 0x12, 0xa6, + 0xa5, 0x33, 0xe3, 0x6a, 0x0d, 0x5f, 0x9c, 0x8a, 0x38, 0xa6, 0x84, 0x5d, 0x54, 0xbc, 0x17, 0xf0, + 0xc4, 0xfc, 0xef, 0x0a, 0x74, 0x4a, 0xb3, 0xa6, 0x1b, 0xe3, 0x89, 0x88, 0xb3, 0x2c, 0x39, 0xb6, + 0x11, 0x76, 0x18, 0xea, 0x8b, 0x96, 0x6d, 0x4e, 0x6d, 0x84, 0xc5, 0xa1, 0x3e, 0x36, 0x69, 0x73, + 0x6a, 0xa3, 0xdd, 0xd2, 0xdb, 0x56, 0x75, 0x4f, 0x8d, 0x84, 0x52, 0xe7, 0xdd, 0x02, 0xb8, 0x4d, + 0x49, 0x29, 0x54, 0xa7, 0x03, 0x3b, 0xc9, 0xf2, 0xf6, 0x79, 0x1f, 0xa3, 0xe3, 0x27, 0x22, 0xc6, + 0xb9, 0x68, 0x93, 0x97, 0x75, 0x51, 0xd6, 0x64, 0x4a, 0x9e, 0x85, 0x81, 0x3a, 0x1a, 0xed, 0x72, + 0x03, 0x01, 0x3f, 0x08, 0x03, 0x1a, 0xa6, 0x25, 0x4b, 0xfc, 0x6c, 0xf3, 0xac, 0x8b, 0x06, 0xe3, + 0x71, 0x2a, 0x30, 0x2a, 0x73, 0xe9, 0x46, 0x62, 0x9b, 0xb7, 0xa8, 0xbf, 0xed, 0x9a, 0xff, 0x5e, + 0x81, 0xe5, 0x39, 0x66, 0x63, 0x10, 0x84, 0x8c, 0xce, 0x2e, 0x22, 0x74, 0x79, 0x13, 0xbb, 0xdb, + 0x2e, 0x21, 0xe4, 0x84, 0x94, 0xa9, 0xaa, 0x11, 0x72, 0x82, 0x9a, 0x74, 0x0e, 0x9a, 0xf2, 0x98, + 0xbe, 0x56, 0x2d, 0x8c, 0x86, 0x3c, 0xc6, 0xcf, 0xdc, 0x80, 0xb6, 0x1f, 0x8e, 0x2d, 0x5f, 0x3c, + 0x11, 0x3e, 0xf1, 0xa1, 0xbf, 0x7e, 0xed, 0x14, 0x29, 0xaf, 0x3d, 0x08, 0xc7, 0x0f, 0x90, 0x96, + 0x1b, 0xbe, 0x6e, 0x99, 0x9f, 0x80, 0x91, 0x41, 0x59, 0x1b, 0x1a, 0x5b, 0xe2, 0x20, 0x1d, 0x0f, + 0x5e, 0x63, 0x06, 0xd4, 0x71, 0xc4, 0xa0, 0x82, 0xad, 0xcf, 0xed, 0x38, 0x18, 0x54, 0x11, 0x7d, + 0x2f, 0x8e, 0xc3, 0x78, 0x50, 0xc3, 0xe6, 0xae, 0x1d, 0x78, 0xce, 0xa0, 0x8e, 0xcd, 0xfb, 0xb6, + 0xb4, 0xfd, 0x41, 0xc3, 0xfc, 0xcb, 0x06, 0x18, 0xbb, 0xfa, 0xed, 0x6c, 0x0b, 0x7a, 0xf9, 0xa5, + 0xfd, 0xc5, 0xf9, 0x9c, 0xdd, 0xd9, 0x06, 0xe5, 0x73, 0xba, 0x51, 0xa9, 0x37, 0x7b, 0xf5, 0xbf, + 0x3a, 0x77, 0xf5, 0xff, 0x32, 0xd4, 0x1e, 0xc7, 0x27, 0xd3, 0x27, 0x5b, 0xbb, 0xbe, 0x1d, 0x70, + 0x04, 0xb3, 0xf7, 0xa1, 0x83, 0x72, 0xb7, 0x12, 0xf2, 0xc2, 0x3a, 0x17, 0x52, 0xfe, 0xc1, 0x02, + 0xc1, 0x39, 0x20, 0x91, 0xf6, 0xd4, 0x6b, 0x60, 0x38, 0x87, 0x9e, 0xef, 0xc6, 0x22, 0xd0, 0x09, + 0x66, 0x36, 0x3f, 0x65, 0x9e, 0xd3, 0xb0, 0xef, 0x52, 0x69, 0x7e, 0x96, 0xc3, 0x29, 0x57, 0x06, + 0x9d, 0x9b, 0xda, 0x26, 0x67, 0x14, 0x7c, 0xa9, 0x44, 0x4e, 0x0e, 0xa7, 0xb8, 0xd7, 0xd5, 0x2a, + 0xdf, 0xeb, 0x52, 0xd7, 0xc1, 0xfd, 0xd0, 0x76, 0x75, 0xea, 0x10, 0x37, 0x6b, 0xa1, 0xed, 0xb2, + 0x1b, 0xda, 0xe9, 0xce, 0x25, 0x40, 0x32, 0xdf, 0xa4, 0x9d, 0xef, 0x35, 0xe8, 0xa3, 0x33, 0xb7, + 0x54, 0x0c, 0x80, 0xa6, 0x04, 0xf4, 0x8d, 0xce, 0x34, 0x39, 0xdc, 0xc2, 0x28, 0x00, 0x95, 0xf1, + 0x3a, 0xf4, 0xb3, 0x6f, 0xd1, 0x17, 0x0b, 0x3a, 0xfa, 0x9c, 0x43, 0x43, 0xd5, 0xbd, 0x82, 0x35, + 0x38, 0xe3, 0x1c, 0xda, 0x41, 0x20, 0x7c, 0xeb, 0x20, 0x1d, 0x8d, 0x32, 0x0f, 0xd0, 0xa5, 0x03, + 0xc0, 0x65, 0x8d, 0xba, 0x4b, 0x18, 0x72, 0x28, 0x26, 0xf4, 0x02, 0xcf, 0x57, 0x97, 0xf1, 0xc8, + 0xdb, 0xf5, 0x88, 0xb2, 0x13, 0x78, 0x3e, 0xe5, 0x7e, 0xd1, 0xe7, 0x7d, 0x0c, 0x83, 0x34, 0xf5, + 0xdc, 0xc4, 0x92, 0x61, 0x76, 0x93, 0x9e, 0x8e, 0x71, 0xa7, 0x82, 0xcb, 0x47, 0xa9, 0xe7, 0xee, + 0x87, 0xfa, 0x2e, 0x7d, 0x8f, 0xe8, 0xb3, 0xae, 0xf9, 0x31, 0x74, 0xcb, 0xba, 0x83, 0xba, 0x48, + 0xbb, 0xae, 0xc1, 0x6b, 0x0c, 0xa0, 0xb9, 0x13, 0xc6, 0x13, 0xdb, 0x1f, 0x54, 0xb0, 0xad, 0x6e, + 0x3b, 0x0e, 0xaa, 0xac, 0x0b, 0x46, 0xb6, 0x1d, 0x18, 0xd4, 0xcc, 0x8f, 0xc0, 0xc8, 0x7e, 0x0d, + 0x40, 0x77, 0xb2, 0x43, 0x57, 0xa8, 0x60, 0x48, 0x59, 0x26, 0x03, 0x01, 0x14, 0x08, 0x65, 0xff, + 0xb8, 0xa8, 0x16, 0xff, 0xb8, 0x30, 0x7f, 0x03, 0xba, 0xe5, 0xc9, 0x65, 0xe9, 0xba, 0x4a, 0x91, + 0xae, 0x5b, 0x30, 0x8a, 0x0e, 0xbc, 0xe2, 0x70, 0x62, 0x95, 0x02, 0x03, 0x03, 0x01, 0xf8, 0x1a, + 0xf3, 0xf7, 0x2a, 0xd0, 0xa0, 0x08, 0x97, 0x5c, 0x0b, 0x36, 0x8a, 0xb5, 0xd3, 0xe0, 0x6d, 0x82, + 0xd0, 0x97, 0x96, 0xcf, 0x81, 0xab, 0xcf, 0x3f, 0x07, 0xae, 0x4d, 0x9f, 0x03, 0xbf, 0x64, 0xa1, + 0xd0, 0xcd, 0xc7, 0xd0, 0x54, 0xbf, 0x15, 0x61, 0xcb, 0xd0, 0x7b, 0x14, 0x1c, 0x05, 0xe1, 0xd3, + 0x40, 0x01, 0x06, 0xaf, 0xb1, 0x33, 0xb0, 0x94, 0x31, 0x5d, 0xff, 0xbf, 0x64, 0x50, 0x61, 0x03, + 0xe8, 0x92, 0x58, 0x33, 0x48, 0x95, 0x5d, 0x86, 0xa1, 0x76, 0x0e, 0x5b, 0x61, 0x20, 0x76, 0x42, + 0xe9, 0x8d, 0x4e, 0x32, 0x6c, 0x8d, 0x2d, 0x41, 0x67, 0x4f, 0x86, 0xd1, 0x9e, 0x08, 0x5c, 0x2f, + 0x18, 0x0f, 0xea, 0x37, 0xef, 0x43, 0x53, 0xfd, 0xed, 0xa4, 0xf4, 0x4a, 0x05, 0x18, 0xbc, 0x86, + 0xd4, 0x9f, 0xdb, 0x9e, 0xf4, 0x82, 0xf1, 0x8e, 0x38, 0x96, 0xca, 0x28, 0x3d, 0xb0, 0x13, 0x39, + 0xa8, 0xb2, 0x3e, 0x80, 0x7e, 0xea, 0xbd, 0xc0, 0x1d, 0xd4, 0xee, 0x6e, 0xfe, 0xf8, 0xa7, 0x57, + 0x2a, 0xff, 0xf8, 0xd3, 0x2b, 0x95, 0x7f, 0xf9, 0xe9, 0x95, 0xd7, 0xfe, 0xe4, 0x5f, 0xaf, 0x54, + 0x7e, 0xf0, 0x7e, 0xe9, 0x5f, 0x2e, 0x13, 0x5b, 0xc6, 0xde, 0xb1, 0x3a, 0x32, 0xcc, 0x3a, 0x81, + 0xb8, 0x1d, 0x1d, 0x8d, 0x6f, 0x47, 0x07, 0xb7, 0x33, 0x9d, 0x3b, 0x68, 0xd2, 0x2f, 0x5a, 0x3e, + 0xf8, 0xbf, 0x00, 0x00, 0x00, 0xff, 0xff, 0x4b, 0x5a, 0x37, 0x9b, 0x21, 0x46, 0x00, 0x00, } func (m *Message) Marshal() (dAtA []byte, err error) { @@ -7161,23 +7151,6 @@ func (m *MultiUpdate) MarshalToSizedBuffer(dAtA []byte) (int, error) { i = encodeVarintPipeline(dAtA, i, uint64(size)) } i-- - dAtA[i] = 0x32 - } - } - if len(m.SegmentMap) > 0 { - for k := range m.SegmentMap { - v := m.SegmentMap[k] - baseI := i - i = encodeVarintPipeline(dAtA, i, uint64(v)) - i-- - dAtA[i] = 0x10 - i -= len(k) - copy(dAtA[i:], k) - i = encodeVarintPipeline(dAtA, i, uint64(len(k))) - i-- - dAtA[i] = 0xa - i = encodeVarintPipeline(dAtA, i, uint64(baseI-i)) - i-- dAtA[i] = 0x2a } } @@ -12714,14 +12687,6 @@ func (m *MultiUpdate) ProtoSize() (n int) { if m.NBucket != 0 { n += 1 + sovPipeline(uint64(m.NBucket)) } - if len(m.SegmentMap) > 0 { - for k, v := range m.SegmentMap { - _ = k - _ = v - mapEntrySize := 1 + len(k) + sovPipeline(uint64(len(k))) + 1 + sovPipeline(uint64(v)) - n += mapEntrySize + 1 + sovPipeline(uint64(mapEntrySize)) - } - } if len(m.UpdateCtxList) > 0 { for _, e := range m.UpdateCtxList { l = e.ProtoSize() @@ -17335,119 +17300,6 @@ func (m *MultiUpdate) Unmarshal(dAtA []byte) error { } } case 5: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field SegmentMap", wireType) - } - var msglen int - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowPipeline - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - msglen |= int(b&0x7F) << shift - if b < 0x80 { - break - } - } - if msglen < 0 { - return ErrInvalidLengthPipeline - } - postIndex := iNdEx + msglen - if postIndex < 0 { - return ErrInvalidLengthPipeline - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - if m.SegmentMap == nil { - m.SegmentMap = make(map[string]int32) - } - var mapkey string - var mapvalue int32 - for iNdEx < postIndex { - entryPreIndex := iNdEx - var wire uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowPipeline - } - 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 ErrIntOverflowPipeline - } - 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 ErrInvalidLengthPipeline - } - postStringIndexmapkey := iNdEx + intStringLenmapkey - if postStringIndexmapkey < 0 { - return ErrInvalidLengthPipeline - } - if postStringIndexmapkey > l { - return io.ErrUnexpectedEOF - } - mapkey = string(dAtA[iNdEx:postStringIndexmapkey]) - iNdEx = postStringIndexmapkey - } else if fieldNum == 2 { - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowPipeline - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - mapvalue |= int32(b&0x7F) << shift - if b < 0x80 { - break - } - } - } else { - iNdEx = entryPreIndex - skippy, err := skipPipeline(dAtA[iNdEx:]) - if err != nil { - return err - } - if (skippy < 0) || (iNdEx+skippy) < 0 { - return ErrInvalidLengthPipeline - } - if (iNdEx + skippy) > postIndex { - return io.ErrUnexpectedEOF - } - iNdEx += skippy - } - } - m.SegmentMap[mapkey] = mapvalue - iNdEx = postIndex - case 6: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field UpdateCtxList", wireType) } diff --git a/pkg/sql/colexec/multi_update/s3writer.go b/pkg/sql/colexec/multi_update/s3writer.go index 8c3813aa6c789..e47a2be75ae93 100644 --- a/pkg/sql/colexec/multi_update/s3writer.go +++ b/pkg/sql/colexec/multi_update/s3writer.go @@ -104,7 +104,6 @@ func newS3Writer(update *MultiUpdate) (*s3Writer, error) { tableCount := len(update.MultiUpdateCtx) writer := &s3Writer{ cacheBatchs: batch.NewCompactBatchs(), - segmentMap: update.SegmentMap, updateCtxInfos: update.ctr.updateCtxInfos, seqnums: make([][]uint16, 0, tableCount), sortIdxs: make([]int, 0, tableCount), diff --git a/pkg/sql/colexec/multi_update/types.go b/pkg/sql/colexec/multi_update/types.go index 8b2c66e94511d..302aec677c3a5 100644 --- a/pkg/sql/colexec/multi_update/types.go +++ b/pkg/sql/colexec/multi_update/types.go @@ -73,7 +73,7 @@ type MultiUpdate struct { Engine engine.Engine - SegmentMap map[string]int32 + // SegmentMap map[string]int32 vm.OperatorBase } diff --git a/pkg/sql/compile/operator.go b/pkg/sql/compile/operator.go index 4b45fe1db6519..e7a3a444ea515 100644 --- a/pkg/sql/compile/operator.go +++ b/pkg/sql/compile/operator.go @@ -566,7 +566,6 @@ func dupOperator(sourceOp vm.Operator, index int, maxParallel int) vm.Operator { op.Action = t.Action op.IsOnduplicateKeyUpdate = t.IsOnduplicateKeyUpdate op.Engine = t.Engine - op.SegmentMap = t.SegmentMap op.SetInfo(&info) return op case vm.DedupJoin: @@ -794,7 +793,6 @@ func constructLockOp(n *plan.Node, eng engine.Engine) (*lockop.LockOp, error) { func constructMultiUpdate(n *plan.Node, eg engine.Engine) *multi_update.MultiUpdate { arg := multi_update.NewArgument() arg.Engine = eg - arg.SegmentMap = colexec.Get().GetCnSegmentMap() arg.MultiUpdateCtx = make([]*multi_update.MultiUpdateCtx, len(n.UpdateCtxList)) for i, updateCtx := range n.UpdateCtxList { diff --git a/pkg/sql/compile/remoterun.go b/pkg/sql/compile/remoterun.go index c7cda54932380..145d7385fb84a 100644 --- a/pkg/sql/compile/remoterun.go +++ b/pkg/sql/compile/remoterun.go @@ -839,7 +839,6 @@ func convertToPipelineInstruction(op vm.Operator, proc *process.Process, ctx *sc AffectedRows: t.GetAffectedRows(), Action: uint32(t.Action), UpdateCtxList: updateCtxList, - SegmentMap: t.SegmentMap, } case *postdml.PostDml: in.PostDml = &pipeline.PostDml{ @@ -1341,7 +1340,6 @@ func convertToVmOperator(opr *pipeline.Instruction, ctx *scopeContext, eng engin t := opr.GetMultiUpdate() arg.SetAffectedRows(t.AffectedRows) arg.Action = multi_update.UpdateAction(t.Action) - arg.SegmentMap = t.SegmentMap arg.MultiUpdateCtx = make([]*multi_update.MultiUpdateCtx, len(t.UpdateCtxList)) for i, muCtx := range t.UpdateCtxList { diff --git a/proto/pipeline.proto b/proto/pipeline.proto index 41832cc987e77..2f2d181b9de5a 100644 --- a/proto/pipeline.proto +++ b/proto/pipeline.proto @@ -137,8 +137,7 @@ message MultiUpdate { uint32 Action = 2; uint32 IBucket = 3; uint32 NBucket = 4; - map SegmentMap = 5; - repeated plan.UpdateCtx update_ctx_list = 6; + repeated plan.UpdateCtx update_ctx_list = 5; } message Array{ From ea69d577dce6927a3d84aa1833e3e865f5fa300e Mon Sep 17 00:00:00 2001 From: Kai Cao Date: Fri, 20 Dec 2024 19:08:17 +0800 Subject: [PATCH 14/26] add pitr checking (#20855) add pitr checking when creating cdc task Approved by: @daviszhen --- pkg/cdc/util.go | 2 +- pkg/frontend/cdc.go | 77 ++++++++++++++++++++++++++++++++++----- pkg/frontend/cdc_test.go | 52 +++++++++++++++++++++++--- pkg/frontend/pitr.go | 53 +++++++++++++++++++++++++++ pkg/frontend/pitr_test.go | 29 +++++++++++++++ 5 files changed, 197 insertions(+), 16 deletions(-) diff --git a/pkg/cdc/util.go b/pkg/cdc/util.go index 72dd13d9ce0d2..2d07976bc1af1 100644 --- a/pkg/cdc/util.go +++ b/pkg/cdc/util.go @@ -672,7 +672,7 @@ func AesCFBDecode(ctx context.Context, data string) (string, error) { return AesCFBDecodeWithKey(ctx, data, []byte(AesKey)) } -func AesCFBDecodeWithKey(ctx context.Context, data string, aesKey []byte) (string, error) { +var AesCFBDecodeWithKey = func(ctx context.Context, data string, aesKey []byte) (string, error) { if len(aesKey) == 0 { return "", moerr.NewInternalErrorNoCtx("AesKey is not initialized") } diff --git a/pkg/frontend/cdc.go b/pkg/frontend/cdc.go index 0247877300450..4a3600e4f68c5 100644 --- a/pkg/frontend/cdc.go +++ b/pkg/frontend/cdc.go @@ -297,6 +297,13 @@ func doCreateCdc(ctx context.Context, ses *Session, create *tree.CreateCDC) (err if err != nil { return err } + + bh := ses.GetBackgroundExec(ctx) + defer bh.Close() + if err = checkPitr(ctx, bh, ses.GetTenantName(), tablePts); err != nil { + return + } + jsonTables, err := cdc2.JsonEncode(tablePts) if err != nil { return @@ -398,7 +405,7 @@ func doCreateCdc(ctx context.Context, ses *Session, create *tree.CreateCDC) (err var encodedSinkPwd string if !useConsole { // TODO replace with creatorAccountId - if err = initAesKeyWrapper(ctx, tx, catalog.System_Account, service); err != nil { + if err = initAesKeyBySqlExecutor(ctx, tx, catalog.System_Account, service); err != nil { return } @@ -470,7 +477,7 @@ func cdcTaskMetadata(cdcId string) task.TaskMetadata { } } -func queryTable( +var queryTable = func( ctx context.Context, tx taskservice.SqlExecutor, query string, @@ -501,6 +508,60 @@ func queryTable( return false, nil } +var checkPitr = func(ctx context.Context, bh BackgroundExec, accName string, pts *cdc2.PatternTuples) error { + // TODO min length + minPitrLen := int64(2) + checkPitrByLevel := func(level, dbName, tblName string) (bool, error) { + length, unit, ok, err := getPitrLengthAndUnit(ctx, bh, level, accName, dbName, tblName) + if err != nil { + return false, err + } + if !ok { + return false, nil + } + return !(unit == "h" && length < minPitrLen), nil + } + + for _, pt := range pts.Pts { + dbName := pt.Source.Database + tblName := pt.Source.Table + level := cdc2.TableLevel + if dbName == cdc2.MatchAll && tblName == cdc2.MatchAll { // account level + level = cdc2.AccountLevel + } else if tblName == cdc2.MatchAll { // db level + level = cdc2.DbLevel + } + + if ok, err := checkPitrByLevel(cdc2.AccountLevel, dbName, tblName); err != nil { + return err + } else if ok { + // covered by account level pitr + continue + } + + if level == cdc2.DbLevel || level == cdc2.TableLevel { + if ok, err := checkPitrByLevel(cdc2.DbLevel, dbName, tblName); err != nil { + return err + } else if ok { + // covered by db level pitr + continue + } + } + + if level == cdc2.TableLevel { + if ok, err := checkPitrByLevel(cdc2.TableLevel, dbName, tblName); err != nil { + return err + } else if ok { + // covered by table level pitr + continue + } + } + + return moerr.NewInternalErrorf(ctx, "no account/db/table level pitr with enough length found for pattern: %s, min pitr length: %d h", pt.OriginString, minPitrLen) + } + return nil +} + // getPatternTuple pattern example: // // db1 @@ -890,7 +951,7 @@ func (cdc *CdcTask) initAesKeyByInternalExecutor(ctx context.Context, accountId return err } - cdc2.AesKey, err = decrypt(ctx, encryptedKey, []byte(getGlobalPuWrapper(cdc.cnUUID).SV.KeyEncryptionKey)) + cdc2.AesKey, err = cdc2.AesCFBDecodeWithKey(ctx, encryptedKey, []byte(getGlobalPuWrapper(cdc.cnUUID).SV.KeyEncryptionKey)) return } @@ -1140,6 +1201,7 @@ func handleDropCdc(ses *Session, execCtx *ExecCtx, st *tree.DropCDC) error { } func handlePauseCdc(ses *Session, execCtx *ExecCtx, st *tree.PauseCDC) error { + ses.GetResponser() return updateCdc(execCtx.reqCtx, ses, st) } @@ -1554,13 +1616,10 @@ func getTaskCkp(ctx context.Context, bh BackgroundExec, accountId uint32, taskId } var ( - queryTableWrapper = queryTable - decrypt = cdc2.AesCFBDecodeWithKey getGlobalPuWrapper = getPu - initAesKeyWrapper = initAesKeyBySqlExecutor ) -func initAesKeyBySqlExecutor(ctx context.Context, executor taskservice.SqlExecutor, accountId uint32, service string) (err error) { +var initAesKeyBySqlExecutor = func(ctx context.Context, executor taskservice.SqlExecutor, accountId uint32, service string) (err error) { if len(cdc2.AesKey) > 0 { return nil } @@ -1569,7 +1628,7 @@ func initAesKeyBySqlExecutor(ctx context.Context, executor taskservice.SqlExecut var ret bool querySql := fmt.Sprintf(getDataKeyFormat, accountId, cdc2.InitKeyId) - ret, err = queryTableWrapper(ctx, executor, querySql, func(ctx context.Context, rows *sql.Rows) (bool, error) { + ret, err = queryTable(ctx, executor, querySql, func(ctx context.Context, rows *sql.Rows) (bool, error) { if err = rows.Scan(&encryptedKey); err != nil { return false, err } @@ -1581,6 +1640,6 @@ func initAesKeyBySqlExecutor(ctx context.Context, executor taskservice.SqlExecut return moerr.NewInternalError(ctx, "no data key") } - cdc2.AesKey, err = decrypt(ctx, encryptedKey, []byte(getGlobalPuWrapper(service).SV.KeyEncryptionKey)) + cdc2.AesKey, err = cdc2.AesCFBDecodeWithKey(ctx, encryptedKey, []byte(getGlobalPuWrapper(service).SV.KeyEncryptionKey)) return } diff --git a/pkg/frontend/cdc_test.go b/pkg/frontend/cdc_test.go index ea0d35d64323b..7e095cb312b9f 100644 --- a/pkg/frontend/cdc_test.go +++ b/pkg/frontend/cdc_test.go @@ -411,7 +411,7 @@ func Test_handleCreateCdc(t *testing.T) { cdc2.AesKey = "test-aes-key-not-use-it-in-cloud" defer func() { cdc2.AesKey = "" }() - stub := gostub.Stub(&initAesKeyWrapper, func(context.Context, taskservice.SqlExecutor, uint32, string) (err error) { + stub := gostub.Stub(&initAesKeyBySqlExecutor, func(context.Context, taskservice.SqlExecutor, uint32, string) (err error) { return nil }) defer stub.Reset() @@ -421,6 +421,11 @@ func Test_handleCreateCdc(t *testing.T) { }) defer stubOpenDbConn.Reset() + stubCheckPitr := gostub.Stub(&checkPitr, func(ctx context.Context, bh BackgroundExec, accName string, pts *cdc2.PatternTuples) error { + return nil + }) + defer stubCheckPitr.Reset() + tests := []struct { name string args args @@ -2472,7 +2477,7 @@ func Test_initAesKey(t *testing.T) { { e := moerr.NewInternalErrorNoCtx("error") - queryTableStub := gostub.Stub(&queryTableWrapper, func(context.Context, taskservice.SqlExecutor, string, func(ctx context.Context, rows *sql.Rows) (bool, error)) (bool, error) { + queryTableStub := gostub.Stub(&queryTable, func(context.Context, taskservice.SqlExecutor, string, func(ctx context.Context, rows *sql.Rows) (bool, error)) (bool, error) { return true, e }) defer queryTableStub.Reset() @@ -2482,7 +2487,7 @@ func Test_initAesKey(t *testing.T) { } { - queryTableStub := gostub.Stub(&queryTableWrapper, func(context.Context, taskservice.SqlExecutor, string, func(ctx context.Context, rows *sql.Rows) (bool, error)) (bool, error) { + queryTableStub := gostub.Stub(&queryTable, func(context.Context, taskservice.SqlExecutor, string, func(ctx context.Context, rows *sql.Rows) (bool, error)) (bool, error) { return false, nil }) defer queryTableStub.Reset() @@ -2492,12 +2497,12 @@ func Test_initAesKey(t *testing.T) { } { - queryTableStub := gostub.Stub(&queryTableWrapper, func(context.Context, taskservice.SqlExecutor, string, func(ctx context.Context, rows *sql.Rows) (bool, error)) (bool, error) { + queryTableStub := gostub.Stub(&queryTable, func(context.Context, taskservice.SqlExecutor, string, func(ctx context.Context, rows *sql.Rows) (bool, error)) (bool, error) { return true, nil }) defer queryTableStub.Reset() - decryptStub := gostub.Stub(&decrypt, func(context.Context, string, []byte) (string, error) { + decryptStub := gostub.Stub(&cdc2.AesCFBDecodeWithKey, func(context.Context, string, []byte) (string, error) { return "aesKey", nil }) defer decryptStub.Reset() @@ -2618,7 +2623,7 @@ func TestCdcTask_initAesKeyByInternalExecutor(t *testing.T) { ie: mie, } - decryptStub := gostub.Stub(&decrypt, func(context.Context, string, []byte) (string, error) { + decryptStub := gostub.Stub(&cdc2.AesCFBDecodeWithKey, func(context.Context, string, []byte) (string, error) { return "aesKey", nil }) defer decryptStub.Reset() @@ -2823,3 +2828,38 @@ func TestCdcTask_addExecPipelineForTable(t *testing.T) { assert.NoError(t, cdc.addExecPipelineForTable(context.Background(), info, txnOperator)) } + +func TestCdcTask_checkPitr(t *testing.T) { + stubGetPitrLength := gostub.Stub(&getPitrLengthAndUnit, + func(_ context.Context, _ BackgroundExec, level, _, _, _ string) (int64, string, bool, error) { + return 0, "", level == "table", nil + }, + ) + defer stubGetPitrLength.Reset() + + pts := &cdc2.PatternTuples{ + Pts: []*cdc2.PatternTuple{ + { + Source: cdc2.PatternTable{ + Database: "db1", + Table: "tb1", + }, + }, + { + Source: cdc2.PatternTable{ + Database: "db2", + Table: cdc2.MatchAll, + }, + }, + { + Source: cdc2.PatternTable{ + Database: cdc2.MatchAll, + Table: cdc2.MatchAll, + }, + }, + }, + } + + err := checkPitr(context.Background(), nil, "acc1", pts) + assert.Error(t, err) +} diff --git a/pkg/frontend/pitr.go b/pkg/frontend/pitr.go index 2219fb8c6a40b..c59753e37e48e 100644 --- a/pkg/frontend/pitr.go +++ b/pkg/frontend/pitr.go @@ -74,6 +74,8 @@ var ( // update mo_pitr object id updateMoPitrAccountObjectIdFmt = `update mo_catalog.mo_pitr set obj_id = %d, modified_time = '%s' where account_name = '%s';` + + getLengthAndUnitFmt = `select pitr_length, pitr_unit from mo_catalog.mo_pitr where account_id = %d and level = '%s'` ) type pitrRecord struct { @@ -160,6 +162,18 @@ func getSqlForUpdateMoPitrAccountObjectId(accountName string, objId uint64, modi return fmt.Sprintf(updateMoPitrAccountObjectIdFmt, objId, modifiedTime, accountName) } +func getSqlForGetLengthAndUnitFmt(accountId uint32, level, accName, dbName, tblName string) string { + sql := fmt.Sprintf(getLengthAndUnitFmt, accountId, level) + if level == "account" { + sql += fmt.Sprintf(" and account_name = '%s'", accName) + } else if level == "database" { + sql += fmt.Sprintf(" and database_name = '%s'", dbName) + } else if level == "table" { + sql += fmt.Sprintf(" and table_name = '%s'", tblName) + } + return sql +} + func checkPitrDup(ctx context.Context, bh BackgroundExec, createAccount string, createAccountId uint64, stmt *tree.CreatePitr) (bool, error) { sql := getSqlForCheckPitrDup(createAccount, createAccountId, stmt) @@ -2368,3 +2382,42 @@ func updatePitrObjectId(ctx context.Context, } return } + +var getPitrLengthAndUnit = func( + ctx context.Context, + bh BackgroundExec, + level string, + accName, dbName, tblName string, +) (length int64, unit string, ok bool, err error) { + accountId, err := defines.GetAccountId(ctx) + if err != nil { + return + } + + sql := getSqlForGetLengthAndUnitFmt(accountId, level, accName, dbName, tblName) + ctx = defines.AttachAccountId(ctx, sysAccountID) + bh.ClearExecResultSet() + if err = bh.Exec(ctx, sql); err != nil { + return + } + + erArray, err := getResultSet(ctx, bh) + if err != nil { + return + } + + if !execResultArrayHasData(erArray) { + return + } + + if length, err = erArray[0].GetInt64(ctx, 0, 0); err != nil { + return + } + + if unit, err = erArray[0].GetString(ctx, 0, 1); err != nil { + return + } + + ok = true + return +} diff --git a/pkg/frontend/pitr_test.go b/pkg/frontend/pitr_test.go index d7bf4ba10ef38..6e96ed0347f5b 100644 --- a/pkg/frontend/pitr_test.go +++ b/pkg/frontend/pitr_test.go @@ -3397,3 +3397,32 @@ func Test_RestoreOtherAccount(t *testing.T) { assert.Error(t, err) }) } + +func Test_getPitrLengthAndUnit(t *testing.T) { + ctx := defines.AttachAccountId(context.Background(), sysAccountID) + + bh := &backgroundExecTest{} + bh.init() + + bhStub := gostub.StubFunc(&NewBackgroundExec, bh) + defer bhStub.Reset() + + sql := getSqlForGetLengthAndUnitFmt(0, "account", "acc1", "", "") + bh.sql2result[sql] = newMrsForPitrRecord([][]interface{}{ + {1, "h"}, + }) + length, unit, ok, err := getPitrLengthAndUnit(ctx, bh, "account", "acc1", "", "") + assert.NoError(t, err) + assert.Equal(t, int64(1), length) + assert.Equal(t, "h", unit) + assert.True(t, ok) + + sql = getSqlForGetLengthAndUnitFmt(0, "database", "", "db", "") + bh.sql2result[sql] = newMrsForPitrRecord([][]interface{}{}) + _, _, ok, err = getPitrLengthAndUnit(ctx, bh, "database", "", "db", "") + assert.NoError(t, err) + assert.False(t, ok) + + _, _, _, err = getPitrLengthAndUnit(ctx, bh, "table", "", "", "tbl") + assert.Error(t, err) +} From c02cad7713f1fb44d6c5dab467ca32a0c1dd0c3a Mon Sep 17 00:00:00 2001 From: YANGGMM Date: Fri, 20 Dec 2024 20:06:57 +0800 Subject: [PATCH 15/26] optimize restore cluster (#20857) optimize restore cluster Approved by: @daviszhen --- pkg/frontend/snapshot.go | 139 ++++----------------------------------- 1 file changed, 13 insertions(+), 126 deletions(-) diff --git a/pkg/frontend/snapshot.go b/pkg/frontend/snapshot.go index e6b828a3d4ca7..24a1eb8d41e44 100644 --- a/pkg/frontend/snapshot.go +++ b/pkg/frontend/snapshot.go @@ -1797,6 +1797,8 @@ func restoreToCluster(ctx context.Context, ) (err error) { getLogger(ses.GetService()).Info(fmt.Sprintf("[%s] start to restore cluster, restore timestamp: %d", snapshotName, snapshotTs)) + var isRestoreToCluster bool + var isNeedToCleanToDatabase bool // drop account which not in snapshot var currentExistsAccount []accountRecord currentExistsAccount, err = getRestoreAcurrentExistsAccount(ctx, ses.GetService(), bh, snapshotName) @@ -1852,6 +1854,8 @@ func restoreToCluster(ctx context.Context, // get restore accounts exists in snapshot // restore to each account for _, account := range toRestoreAccount { + isRestoreToCluster = true + isNeedToCleanToDatabase = true getLogger(ses.GetService()).Info(fmt.Sprintf("[%s] cluster restore start to restore account: %v, account id: %d", snapshotName, account.accountName, account.accountId)) // the account id may change var newAccountId uint32 @@ -1859,14 +1863,9 @@ func restoreToCluster(ctx context.Context, if err != nil { return err } - if newAccountId != uint32(account.accountId) { - if err = restoreAccountUsingClusterSnapshotToNew(ctx, ses, bh, snapshotName, snapshotTs, account, uint64(newAccountId), subDbToRestore, true, true); err != nil { - return err - } - } else { - if err = restoreAccountUsingClusterSnapshot(ctx, ses, bh, snapshotName, snapshotTs, account, subDbToRestore, newAccountId); err != nil { - return err - } + + if err = restoreAccountUsingClusterSnapshotToNew(ctx, ses, bh, snapshotName, snapshotTs, account, uint64(newAccountId), subDbToRestore, isRestoreToCluster, isNeedToCleanToDatabase); err != nil { + return err } getLogger(ses.GetService()).Info(fmt.Sprintf("[%s] restore account: %v, account id: %d success", snapshotName, account.accountName, account.accountId)) @@ -1892,8 +1891,11 @@ func restoreToCluster(ctx context.Context, return err } + isRestoreToCluster = true + isNeedToCleanToDatabase = false + // 2.0 restore droped account to new account - err = restoreAccountUsingClusterSnapshotToNew(ctx, ses, bh, snapshotName, snapshotTs, account, uint64(newAccountId), subDbToRestore, true, false) + err = restoreAccountUsingClusterSnapshotToNew(ctx, ses, bh, snapshotName, snapshotTs, account, uint64(newAccountId), subDbToRestore, isRestoreToCluster, isNeedToCleanToDatabase) if err != nil { return err } @@ -1916,6 +1918,7 @@ func restoreToAccountUsingCluster( getLogger(ses.GetService()).Info(fmt.Sprintf("[%s] start to restore account using cluster snapshot: %v, restore timestamp: %d", snapshotName, srcAccount, snapshotTs)) var toAccountId uint32 + isRestoreToCluster := false // get account id var ar *accountRecord @@ -1948,7 +1951,7 @@ func restoreToAccountUsingCluster( } } - err = restoreAccountUsingClusterSnapshotToNew(ctx, ses, bh, snapshotName, snapshotTs, *ar, uint64(toAccountId), nil, false, isNeedToCleanToDatabase) + err = restoreAccountUsingClusterSnapshotToNew(ctx, ses, bh, snapshotName, snapshotTs, *ar, uint64(toAccountId), nil, isRestoreToCluster, isNeedToCleanToDatabase) if err != nil { return err } @@ -2036,84 +2039,6 @@ func dropExistsAccount( return } -func restoreAccountUsingClusterSnapshot(ctx context.Context, - ses *Session, - bh BackgroundExec, - snapshotName string, - snapshotTs int64, - account accountRecord, - subDbToRestore map[string]*subDbRestoreRecord, - toAccountId uint32, -) (err error) { - fromAccount := account.accountId - - newSnapshot, err := insertSnapshotRecord(ctx, ses.GetService(), bh, snapshotName, snapshotTs, fromAccount, account.accountName) - if err != nil { - return err - } - defer func() { - if err != nil { - deleteSnapshotRecord(ctx, ses.GetService(), bh, snapshotName, newSnapshot) - } - }() - - // pre restore account - // drop foreign key related tables first - if err = deleteCurFkTables(ctx, ses.GetService(), bh, "", "", uint32(toAccountId)); err != nil { - return err - } - // get topo sorted tables with foreign key - var sortedFkTbls []string - var fkTableMap map[string]*tableInfo - sortedFkTbls, err = fkTablesTopoSort(ctx, bh, newSnapshot, "", "") - if err != nil { - return err - } - // get foreign key table infos - fkTableMap, err = getTableInfoMap(ctx, ses.GetService(), bh, newSnapshot, "", "", sortedFkTbls) - if err != nil { - return err - } - - // collect views and tables during table restoration - viewMap := make(map[string]*tableInfo) - - // restore to account - if err = restoreToAccount(ctx, - ses.GetService(), - bh, - newSnapshot, - uint32(toAccountId), - fkTableMap, - viewMap, - snapshotTs, - uint32(fromAccount), - true, - subDbToRestore); err != nil { - return err - } - - if len(fkTableMap) > 0 { - if err = restoreTablesWithFk(ctx, ses.GetService(), bh, newSnapshot, sortedFkTbls, fkTableMap, uint32(toAccountId), snapshotTs); err != nil { - return err - } - } - - if len(viewMap) > 0 { - if err = restoreViews(ctx, ses, bh, newSnapshot, viewMap, uint32(toAccountId)); err != nil { - return err - } - } - - deleteSnapshotRecord(ctx, ses.GetService(), bh, snapshotName, newSnapshot) - - // checks if the given context has been canceled. - if err = CancelCheck(ctx); err != nil { - return err - } - return -} - func restoreAccountUsingClusterSnapshotToNew(ctx context.Context, ses *Session, bh BackgroundExec, @@ -2319,44 +2244,6 @@ func getPastExistsAccounts( return } -func insertSnapshotRecord(ctx context.Context, sid string, bh BackgroundExec, spName string, spTs int64, toAccountId uint64, accountName string) (snapshotName string, err error) { - // mock snapshot id and snapshot name - snapshotUId, err := uuid.NewV7() - if err != nil { - return "", err - } - snapshotId := snapshotUId.String() - - snapshotName = snapshotId + "_" + spName + "_mock" - var sql string - sql, err = getSqlForCreateSnapshot(ctx, - snapshotId, - snapshotName, - spTs, - tree.SNAPSHOTLEVELACCOUNT.String(), - accountName, - "", - "", - toAccountId) - if err != nil { - return "", err - } - getLogger(sid).Info(fmt.Sprintf("[%s] mock insert snapshot record sql: %s", spName, sql)) - if err = bh.Exec(ctx, sql); err != nil { - return "", err - } - return -} - -func deleteSnapshotRecord(ctx context.Context, sid string, bh BackgroundExec, spName string, snapshotName string) (err error) { - sql := getSqlForDropSnapshot(snapshotName) - getLogger(sid).Info(fmt.Sprintf("[%s] mock delete snapshot record sql: %s", spName, sql)) - if err = bh.Exec(ctx, sql); err != nil { - return err - } - return -} - func getSnapshotPlanWithSharedBh(ctx context.Context, bh BackgroundExec, snapshotName string) (snapshot *pbplan.Snapshot, err error) { var record *snapshotRecord if record, err = getSnapshotByName(ctx, bh, snapshotName); err != nil { From f491bcde51e312954466ad68e8cc1fdf6d71c5ea Mon Sep 17 00:00:00 2001 From: jiangxinmeng1 <51114574+jiangxinmeng1@users.noreply.github.com> Date: Fri, 20 Dec 2024 21:07:57 +0800 Subject: [PATCH 16/26] Optimize replay (#20837) Optimize replay, replay checkpoint with 10 workers Approved by: @XuPeng-SH --- pkg/vm/engine/tae/catalog/basemvccnode.go | 6 +- pkg/vm/engine/tae/catalog/catalogreplay.go | 91 ++++++++++++++-------- pkg/vm/engine/tae/catalog/metamvccnode.go | 4 +- pkg/vm/engine/tae/db/checkpoint/replay.go | 82 ++++++++++++++++--- pkg/vm/engine/tae/db/open.go | 1 + pkg/vm/engine/tae/db/test/db_test.go | 50 +++++++++++- pkg/vm/engine/tae/logtail/utils.go | 5 +- pkg/vm/engine/tae/txn/txnbase/mvccnode.go | 12 --- 8 files changed, 189 insertions(+), 62 deletions(-) diff --git a/pkg/vm/engine/tae/catalog/basemvccnode.go b/pkg/vm/engine/tae/catalog/basemvccnode.go index 812b44134bbad..ec9eb7608f33a 100644 --- a/pkg/vm/engine/tae/catalog/basemvccnode.go +++ b/pkg/vm/engine/tae/catalog/basemvccnode.go @@ -179,10 +179,10 @@ func (un EntryMVCCNode) AppendTupleWithCommitTS(bat *containers.Batch, ts types. ) } -func ReadEntryNodeTuple(bat *containers.Batch, row int) (un *EntryMVCCNode) { +func ReadEntryNodeTuple(createAt, deleteAt types.TS) (un *EntryMVCCNode) { un = &EntryMVCCNode{ - CreatedAt: bat.GetVectorByName(EntryNode_CreateAt).Get(row).(types.TS), - DeletedAt: bat.GetVectorByName(EntryNode_DeleteAt).Get(row).(types.TS), + CreatedAt: createAt, + DeletedAt: deleteAt, } return } diff --git a/pkg/vm/engine/tae/catalog/catalogreplay.go b/pkg/vm/engine/tae/catalog/catalogreplay.go index 95bf7aed87b45..3d18f37af297d 100644 --- a/pkg/vm/engine/tae/catalog/catalogreplay.go +++ b/pkg/vm/engine/tae/catalog/catalogreplay.go @@ -20,6 +20,7 @@ import ( pkgcatalog "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/container/vector" "github.com/matrixorigin/matrixone/pkg/logutil" "github.com/matrixorigin/matrixone/pkg/util/fault" "github.com/matrixorigin/matrixone/pkg/vm/engine/tae/common" @@ -33,6 +34,10 @@ const ( Backup_Object_Offset uint16 = 1000 ) +type ObjectListReplayer interface { + Submit(uint64, func()) +} + //#region Replay WAL related func (catalog *Catalog) ReplayCmd( @@ -421,27 +426,35 @@ func (catalog *Catalog) onReplayCreateTable(dbid, tid uint64, schema *Schema, tx tbl.InsertLocked(un) } -func (catalog *Catalog) OnReplayObjectBatch(objectInfo *containers.Batch, isTombstone bool, dataFactory DataFactory, forSys bool) { - for i := 0; i < objectInfo.Length(); i++ { - tid := objectInfo.GetVectorByName(SnapshotAttr_TID).Get(i).(uint64) +func (catalog *Catalog) OnReplayObjectBatch(replayer ObjectListReplayer, objectInfo *containers.Batch, isTombstone bool, dataFactory DataFactory, forSys bool) { + tids := vector.MustFixedColNoTypeCheck[uint64](objectInfo.GetVectorByName(SnapshotAttr_TID).GetDownstreamVector()) + dbids := vector.MustFixedColNoTypeCheck[uint64](objectInfo.GetVectorByName(SnapshotAttr_DBID).GetDownstreamVector()) + commitTSs := vector.MustFixedColNoTypeCheck[types.TS](objectInfo.GetVectorByName(txnbase.SnapshotAttr_CommitTS).GetDownstreamVector()) + prepareTSs := vector.MustFixedColNoTypeCheck[types.TS](objectInfo.GetVectorByName(txnbase.SnapshotAttr_PrepareTS).GetDownstreamVector()) + startTSs := vector.MustFixedColNoTypeCheck[types.TS](objectInfo.GetVectorByName(txnbase.SnapshotAttr_StartTS).GetDownstreamVector()) + createTSs := vector.MustFixedColNoTypeCheck[types.TS](objectInfo.GetVectorByName(EntryNode_CreateAt).GetDownstreamVector()) + deleteTSs := vector.MustFixedColNoTypeCheck[types.TS](objectInfo.GetVectorByName(EntryNode_DeleteAt).GetDownstreamVector()) + for i, tid := range tids { if forSys != pkgcatalog.IsSystemTable(tid) { continue } - dbid := objectInfo.GetVectorByName(SnapshotAttr_DBID).Get(i).(uint64) - objectNode := ReadObjectInfoTuple(objectInfo, i) - sid := objectNode.ObjectName().ObjectId() - txnNode := txnbase.ReadTuple(objectInfo, i) - entryNode := ReadEntryNodeTuple(objectInfo, i) - catalog.onReplayCheckpointObject(dbid, tid, sid, objectNode, entryNode, txnNode, isTombstone, dataFactory) + replayFn := func() { + dbid := dbids[i] + objectNode := ReadObjectInfoTuple(objectInfo, i) + sid := objectNode.ObjectName().ObjectId() + catalog.onReplayCheckpointObject( + dbid, tid, sid, createTSs[i], deleteTSs[i], startTSs[i], prepareTSs[i], commitTSs[i], objectNode, isTombstone, dataFactory) + } + replayer.Submit(tid, replayFn) } } func (catalog *Catalog) onReplayCheckpointObject( dbid, tbid uint64, objid *types.Objectid, + createTS, deleteTS types.TS, + start, prepare, end types.TS, objNode *ObjectMVCCNode, - entryNode *EntryMVCCNode, - txnNode *txnbase.TxnMVCCNode, isTombstone bool, dataFactory DataFactory, ) { @@ -470,35 +483,49 @@ func (catalog *Catalog) onReplayCheckpointObject( SortHint: catalog.NextObject(), IsTombstone: isTombstone, } - object.EntryMVCCNode = *entryNode + object.EntryMVCCNode = EntryMVCCNode{ + CreatedAt: createTS, + DeletedAt: deleteTS, + } object.ObjectMVCCNode = *objNode - object.CreateNode = *txnNode + object.CreateNode = txnbase.TxnMVCCNode{ + Start: start, + Prepare: prepare, + End: end, + } object.ObjectState = ObjectState_Create_ApplyCommit object.forcePNode = true // any object replayed from checkpoint is forced to be created return object } var obj *ObjectEntry - if entryNode.CreatedAt.Equal(&txnNode.End) { + if createTS.Equal(&end) { obj = newObject() rel.AddEntryLocked(obj) } - if entryNode.DeletedAt.Equal(&txnNode.End) { + if deleteTS.Equal(&end) { obj, err = rel.GetObjectByID(objid, isTombstone) if err != nil { - panic(fmt.Sprintf("obj %v(%v), [%v %v %v] not existed, table:\n%v", objid.String(), - entryNode.String(), isTombstone, objNode.String(), - txnNode.String(), rel.StringWithLevel(3))) + panic(fmt.Sprintf("obj %v(%v %v), [%v %v %v %v %v] not existed, table:\n%v", objid.String(), + createTS.ToString(), deleteTS.ToString(), isTombstone, objNode.String(), + start.ToString(), prepare.ToString(), end.ToString(), rel.StringWithLevel(3))) + } + obj.EntryMVCCNode = EntryMVCCNode{ + CreatedAt: createTS, + DeletedAt: deleteTS, } - obj.EntryMVCCNode = *entryNode obj.ObjectMVCCNode = *objNode - obj.DeleteNode = *txnNode + obj.DeleteNode = txnbase.TxnMVCCNode{ + Start: start, + Prepare: prepare, + End: end, + } obj.ObjectState = ObjectState_Delete_ApplyCommit } - if !entryNode.CreatedAt.Equal(&txnNode.End) && !entryNode.DeletedAt.Equal(&txnNode.End) { + if !createTS.Equal(&end) && !deleteTS.Equal(&end) { // In back up, aobj is replaced with naobj and its DeleteAt is removed. // Before back up, txnNode.End equals DeleteAt of naobj. // After back up, DeleteAt is empty. - if objid.Offset() == Backup_Object_Offset && entryNode.DeletedAt.IsEmpty() { + if objid.Offset() == Backup_Object_Offset && deleteTS.IsEmpty() { obj = newObject() rel.AddEntryLocked(obj) _, sarg, _ := fault.TriggerFault("back up UT") @@ -506,29 +533,29 @@ func (catalog *Catalog) onReplayCheckpointObject( obj.CreateNode = *txnbase.NewTxnMVCCNodeWithTS(obj.CreatedAt) } logutil.Warnf("obj %v, tbl %v-%d create %v, delete %v, end %v", - objid.String(), rel.fullName, rel.ID, entryNode.CreatedAt.ToString(), - entryNode.DeletedAt.ToString(), txnNode.End.ToString()) + objid.String(), rel.fullName, rel.ID, createTS.ToString(), + deleteTS.ToString(), end.ToString()) } else { - if !entryNode.DeletedAt.IsEmpty() { + if !deleteTS.IsEmpty() { logutil.Warnf("obj %v, tbl %v-%d create %v, delete %v, end %v", - objid.String(), rel.fullName, rel.ID, entryNode.CreatedAt.ToString(), - entryNode.DeletedAt.ToString(), txnNode.End.ToString()) + objid.String(), rel.fullName, rel.ID, createTS.ToString(), + deleteTS.ToString(), end.ToString()) obj, _ = rel.GetObjectByID(objid, isTombstone) if obj == nil { obj = newObject() rel.AddEntryLocked(obj) } - obj.CreateNode = *txnbase.NewTxnMVCCNodeWithTS(entryNode.CreatedAt) - obj.DeleteNode = *txnbase.NewTxnMVCCNodeWithTS(entryNode.DeletedAt) + obj.CreateNode = *txnbase.NewTxnMVCCNodeWithTS(createTS) + obj.DeleteNode = *txnbase.NewTxnMVCCNodeWithTS(deleteTS) } } } if obj == nil { obj, err = rel.GetObjectByID(objid, isTombstone) if err != nil { - panic(fmt.Sprintf("obj %v(%v), [%v %v %v] not existed, table:\n%v", objid.String(), - entryNode.String(), isTombstone, objNode.String(), - txnNode.String(), rel.StringWithLevel(3))) + panic(fmt.Sprintf("obj %v(%v %v), [%v %v %v %v %v] not existed, table:\n%v", objid.String(), + createTS.ToString(), deleteTS.ToString(), isTombstone, objNode.String(), + start.ToString(), prepare.ToString(), end.ToString(), rel.StringWithLevel(3))) } } if obj.objData == nil { diff --git a/pkg/vm/engine/tae/catalog/metamvccnode.go b/pkg/vm/engine/tae/catalog/metamvccnode.go index 96aeebe96f6d1..858abc36ff57c 100644 --- a/pkg/vm/engine/tae/catalog/metamvccnode.go +++ b/pkg/vm/engine/tae/catalog/metamvccnode.go @@ -173,8 +173,10 @@ func (e *ObjectMVCCNode) AppendTuple(sid *types.Objectid, batch *containers.Batc func ReadObjectInfoTuple(bat *containers.Batch, row int) (e *ObjectMVCCNode) { buf := bat.GetVectorByName(ObjectAttr_ObjectStats).Get(row).([]byte) + buf2 := make([]byte, len(buf)) + copy(buf2, buf) e = &ObjectMVCCNode{ - ObjectStats: (objectio.ObjectStats)(buf), + ObjectStats: (objectio.ObjectStats)(buf2), } return } diff --git a/pkg/vm/engine/tae/db/checkpoint/replay.go b/pkg/vm/engine/tae/db/checkpoint/replay.go index 2b25566f20cba..116021cb6014e 100644 --- a/pkg/vm/engine/tae/db/checkpoint/replay.go +++ b/pkg/vm/engine/tae/db/checkpoint/replay.go @@ -18,6 +18,7 @@ import ( "context" "fmt" "sort" + "sync" "time" "github.com/matrixorigin/matrixone/pkg/common/moerr" @@ -30,6 +31,7 @@ import ( "github.com/matrixorigin/matrixone/pkg/vm/engine/tae/common" "github.com/matrixorigin/matrixone/pkg/vm/engine/tae/containers" "github.com/matrixorigin/matrixone/pkg/vm/engine/tae/iface/txnif" + "github.com/matrixorigin/matrixone/pkg/vm/engine/tae/logstore/sm" "github.com/matrixorigin/matrixone/pkg/vm/engine/tae/logtail" "github.com/matrixorigin/matrixone/pkg/vm/engine/tae/mergesort" "github.com/matrixorigin/matrixone/pkg/vm/engine/tae/tables" @@ -43,6 +45,10 @@ const ( ReadData ) +const ( + DefaultObjectReplayWorkerCount = 10 +) + type CkpReplayer struct { r *runner dataF catalog.DataFactory @@ -55,12 +61,24 @@ type CkpReplayer struct { readDuration, applyDuration time.Duration readCount, applyCount, totalCount int + + objectReplayWorker []sm.Queue + wg sync.WaitGroup + objectCountMap map[uint64]int } func (c *CkpReplayer) Close() { for _, close := range c.closes { close() } + for _, worker := range c.objectReplayWorker { + worker.Stop() + } + for _, data := range c.ckpdatas { + if data != nil { + data.Close() + } + } } func (c *CkpReplayer) ReadCkpFiles() (err error) { @@ -187,7 +205,7 @@ func (c *CkpReplayer) ReadCkpFiles() (err error) { closecbs := make([]func(), 0) c.totalCount = len(entries) - readfn := func(i int, readType uint16) { + readfn := func(i int, readType uint16) (err error) { checkpointEntry := entries[i] checkpointEntry.sid = r.rt.SID() if checkpointEntry.end.LT(&maxGlobalEnd) { @@ -218,6 +236,7 @@ func (c *CkpReplayer) ReadCkpFiles() (err error) { closecbs = append(closecbs, func() { datas[i].CloseWhenLoadFromCache(checkpointEntry.version) }) } } + return nil } c.closes = append(c.closes, closecbs...) t0 = time.Now() @@ -229,16 +248,24 @@ func (c *CkpReplayer) ReadCkpFiles() (err error) { } } for i := 0; i < bat.Length(); i++ { - readfn(i, PrefetchMetaIdx) + if err = readfn(i, PrefetchMetaIdx); err != nil { + return + } } for i := 0; i < bat.Length(); i++ { - readfn(i, ReadMetaIdx) + if err = readfn(i, ReadMetaIdx); err != nil { + return + } } for i := 0; i < bat.Length(); i++ { - readfn(i, PrefetchData) + if err = readfn(i, PrefetchData); err != nil { + return + } } for i := 0; i < bat.Length(); i++ { - readfn(i, ReadData) + if err = readfn(i, ReadData); err != nil { + return + } } c.ckpdatas = datas c.readDuration += time.Since(t0) @@ -286,7 +313,7 @@ func (c *CkpReplayer) ReplayThreeTablesObjectlist(phase string) ( dataFactory := c.dataF maxGlobal := r.MaxGlobalCheckpoint() if maxGlobal != nil { - err = datas[c.globalCkpIdx].ApplyReplayTo(r.catalog, dataFactory, true) + err = datas[c.globalCkpIdx].ApplyReplayTo(c, r.catalog, dataFactory, true) c.applyCount++ logger := logutil.Info if err != nil { @@ -332,7 +359,7 @@ func (c *CkpReplayer) ReplayThreeTablesObjectlist(phase string) ( continue } start := time.Now() - if err = datas[i].ApplyReplayTo(r.catalog, dataFactory, true); err != nil { + if err = datas[i].ApplyReplayTo(c, r.catalog, dataFactory, true); err != nil { logger = logutil.Error } logger( @@ -363,6 +390,7 @@ func (c *CkpReplayer) ReplayThreeTablesObjectlist(phase string) ( isLSNValid = false } } + c.wg.Wait() return } @@ -419,7 +447,7 @@ func (c *CkpReplayer) ReplayObjectlist(phase string) (err error) { var ckpVers []uint32 var ckpDatas []*logtail.CheckpointData if maxGlobal := r.MaxGlobalCheckpoint(); maxGlobal != nil { - err = datas[c.globalCkpIdx].ApplyReplayTo(r.catalog, dataFactory, false) + err = datas[c.globalCkpIdx].ApplyReplayTo(c, r.catalog, dataFactory, false) if err != nil { return } @@ -436,6 +464,7 @@ func (c *CkpReplayer) ReplayObjectlist(phase string) (err error) { continue } err = datas[i].ApplyReplayTo( + c, r.catalog, dataFactory, false) @@ -448,12 +477,22 @@ func (c *CkpReplayer) ReplayObjectlist(phase string) (err error) { ckpVers = append(ckpVers, checkpointEntry.version) ckpDatas = append(ckpDatas, datas[i]) } + c.wg.Wait() c.applyDuration += time.Since(t0) r.catalog.GetUsageMemo().(*logtail.TNUsageMemo).PrepareReplay(ckpDatas, ckpVers) r.source.Init(maxTs) + maxTableID, maxObjectCount := uint64(0), 0 + for tid, count := range c.objectCountMap { + if count > maxObjectCount { + maxTableID = tid + maxObjectCount = count + } + } logutil.Info( "Replay-Checkpoints", zap.String("phase", phase), + zap.Uint64("max table tid", maxTableID), + zap.Int("object count (create count + delete count)", maxObjectCount), zap.Duration("apply-cost", c.applyDuration), zap.Duration("read-cost", c.readDuration), zap.Int("apply-count", c.applyCount), @@ -463,11 +502,32 @@ func (c *CkpReplayer) ReplayObjectlist(phase string) (err error) { return } +func (c *CkpReplayer) Submit(tid uint64, replayFn func()) { + c.wg.Add(1) + workerOffset := tid % uint64(len(c.objectReplayWorker)) + c.objectCountMap[tid] = c.objectCountMap[tid] + 1 + c.objectReplayWorker[workerOffset].Enqueue(replayFn) +} + func (r *runner) Replay(dataFactory catalog.DataFactory) *CkpReplayer { - return &CkpReplayer{ - r: r, - dataF: dataFactory, + replayer := &CkpReplayer{ + r: r, + dataF: dataFactory, + objectCountMap: make(map[uint64]int), + } + objectWorker := make([]sm.Queue, DefaultObjectReplayWorkerCount) + for i := 0; i < DefaultObjectReplayWorkerCount; i++ { + objectWorker[i] = sm.NewSafeQueue(10000, 100, func(items ...any) { + for _, item := range items { + fn := item.(func()) + fn() + replayer.wg.Done() + } + }) + objectWorker[i].Start() } + replayer.objectReplayWorker = objectWorker + return replayer } func MergeCkpMeta( diff --git a/pkg/vm/engine/tae/db/open.go b/pkg/vm/engine/tae/db/open.go index 95bee1419e0ed..25ef723b7d7e4 100644 --- a/pkg/vm/engine/tae/db/open.go +++ b/pkg/vm/engine/tae/db/open.go @@ -221,6 +221,7 @@ func Open( now := time.Now() ckpReplayer := db.BGCheckpointRunner.Replay(dataFactory) + defer ckpReplayer.Close() if err = ckpReplayer.ReadCkpFiles(); err != nil { panic(err) } diff --git a/pkg/vm/engine/tae/db/test/db_test.go b/pkg/vm/engine/tae/db/test/db_test.go index 0f4d7833826ac..439c75bb0f49a 100644 --- a/pkg/vm/engine/tae/db/test/db_test.go +++ b/pkg/vm/engine/tae/db/test/db_test.go @@ -10502,7 +10502,55 @@ func TestDedup5(t *testing.T) { assert.Error(t, err) assert.NoError(t, insertTxn.Commit(ctx)) } - +func TestCheckpointObjectList(t *testing.T) { + ctx := context.Background() + opts := config.WithLongScanAndCKPOpts(nil) + tae := testutil.NewTestEngine(ctx, ModuleName, t, opts) + defer tae.Close() + txn, _ := tae.StartTxn(nil) + testutil.CreateDatabase2(ctx, txn, "db") + txn.Commit(ctx) + var wg sync.WaitGroup + pool, _ := ants.NewPool(80) + defer pool.Release() + var tblNameIndex atomic.Int32 + createRelAndAppend := func() { + defer wg.Done() + schema := catalog.MockSchemaAll(3, -1) + schema.Name = fmt.Sprintf("tbl%d", tblNameIndex.Add(1)) + schema.Extra.BlockMaxRows = 1 + schema.Extra.ObjectMaxBlocks = 256 + bat := catalog.MockBatch(schema, 1) + txn, _ := tae.StartTxn(nil) + db, _ := txn.GetDatabase("db") + testutil.CreateRelation2(ctx, txn, db, schema) + txn.Commit(ctx) + for i := 0; i < 10; i++ { + txn, rel := testutil.GetRelation(t, 0, tae.DB, "db", schema.Name) + rel.Append(ctx, bat) + txn.Commit(ctx) + testutil.CompactBlocks(t, 0, tae.DB, "db", schema, true) + } + bat.Close() + } + for i := 0; i < 10; i++ { + wg.Add(1) + pool.Submit(createRelAndAppend) + } + wg.Wait() + tae.ForceCheckpoint() + for i := 1; i <= 10; i++ { + txn, rel := testutil.GetRelation(t, 0, tae.DB, "db", fmt.Sprintf("tbl%d", i)) + testutil.CheckAllColRowsByScan(t, rel, 10, false) + txn.Commit(ctx) + } + tae.Restart(ctx) + for i := 1; i <= 10; i++ { + txn, rel := testutil.GetRelation(t, 0, tae.DB, "db", fmt.Sprintf("tbl%d", i)) + testutil.CheckAllColRowsByScan(t, rel, 10, false) + txn.Commit(ctx) + } +} func TestReplayDebugLog(t *testing.T) { ctx := context.Background() diff --git a/pkg/vm/engine/tae/logtail/utils.go b/pkg/vm/engine/tae/logtail/utils.go index 37523aa19081d..5d063a20a5fd1 100644 --- a/pkg/vm/engine/tae/logtail/utils.go +++ b/pkg/vm/engine/tae/logtail/utils.go @@ -492,14 +492,15 @@ func NewGlobalCollector( } func (data *CheckpointData) ApplyReplayTo( + objectlistReplayer catalog.ObjectListReplayer, c *catalog.Catalog, dataFactory catalog.DataFactory, forSys bool, ) (err error) { objectInfo := data.GetTombstoneObjectBatchs() - c.OnReplayObjectBatch(objectInfo, true, dataFactory, forSys) + c.OnReplayObjectBatch(objectlistReplayer, objectInfo, true, dataFactory, forSys) objectInfo = data.GetObjectBatchs() - c.OnReplayObjectBatch(objectInfo, false, dataFactory, forSys) + c.OnReplayObjectBatch(objectlistReplayer, objectInfo, false, dataFactory, forSys) return } diff --git a/pkg/vm/engine/tae/txn/txnbase/mvccnode.go b/pkg/vm/engine/tae/txn/txnbase/mvccnode.go index fde44f6f237f8..5ab40bac85560 100644 --- a/pkg/vm/engine/tae/txn/txnbase/mvccnode.go +++ b/pkg/vm/engine/tae/txn/txnbase/mvccnode.go @@ -464,15 +464,3 @@ func (un *TxnMVCCNode) AppendTupleWithCommitTS(bat *containers.Batch, commitTS t func (un *TxnMVCCNode) ReadTuple(bat *containers.Batch, offset int) { // TODO } - -func ReadTuple(bat *containers.Batch, row int) (un *TxnMVCCNode) { - end := bat.GetVectorByName(SnapshotAttr_CommitTS).Get(row).(types.TS) - start := bat.GetVectorByName(SnapshotAttr_StartTS).Get(row).(types.TS) - prepare := bat.GetVectorByName(SnapshotAttr_PrepareTS).Get(row).(types.TS) - un = &TxnMVCCNode{ - Start: start, - Prepare: prepare, - End: end, - } - return -} From 599d364a6dcdfa8da869c3d7e27fd6fa13da2635 Mon Sep 17 00:00:00 2001 From: gouhongshen Date: Fri, 20 Dec 2024 22:07:21 +0800 Subject: [PATCH 17/26] support subscription in mo table size/rows. (#20785) mo table size/rows currently do not support subscription db and it never was. Approved by: @qingxinhome, @daviszhen, @aunjgr, @heni02, @m-schen --- pkg/frontend/back_exec.go | 17 +- pkg/sql/plan/function/func_mo.go | 182 +++++++++++++++++- pkg/vm/process/types.go | 1 + .../distributed/cases/function/func_mo.result | 67 +++++++ test/distributed/cases/function/func_mo.sql | 64 ++++++ 5 files changed, 319 insertions(+), 12 deletions(-) create mode 100644 test/distributed/cases/function/func_mo.result create mode 100644 test/distributed/cases/function/func_mo.sql diff --git a/pkg/frontend/back_exec.go b/pkg/frontend/back_exec.go index 75564ee68c787..509841970435e 100644 --- a/pkg/frontend/back_exec.go +++ b/pkg/frontend/back_exec.go @@ -1140,11 +1140,12 @@ func (sh *SqlHelper) GetSubscriptionMeta(dbName string) (*plan.SubscriptionMeta, return sh.ses.txnCompileCtx.GetSubscriptionMeta(dbName, nil) } -// Made for sequence func. nextval, setval. -func (sh *SqlHelper) ExecSql(sql string) (ret [][]interface{}, err error) { +func (sh *SqlHelper) execSql( + ctx context.Context, + sql string, +) (ret [][]interface{}, err error) { var erArray []ExecResult - ctx := sh.ses.txnCompileCtx.execCtx.reqCtx /* if we run the transaction statement (BEGIN, ect) here , it creates an independent transaction. if we do not run the transaction statement (BEGIN, ect) here, it runs the sql in the share transaction @@ -1171,3 +1172,13 @@ func (sh *SqlHelper) ExecSql(sql string) (ret [][]interface{}, err error) { return erArray[0].(*MysqlResultSet).Data, nil } + +// Made for sequence func. nextval, setval. +func (sh *SqlHelper) ExecSql(sql string) (ret [][]interface{}, err error) { + ctx := sh.ses.txnCompileCtx.execCtx.reqCtx + return sh.execSql(ctx, sql) +} + +func (sh *SqlHelper) ExecSqlWithCtx(ctx context.Context, sql string) ([][]interface{}, error) { + return sh.execSql(ctx, sql) +} diff --git a/pkg/sql/plan/function/func_mo.go b/pkg/sql/plan/function/func_mo.go index 57af31effbd1b..716b78662d58a 100644 --- a/pkg/sql/plan/function/func_mo.go +++ b/pkg/sql/plan/function/func_mo.go @@ -176,6 +176,113 @@ type GetMoTableSizeRowsFuncType = func() func( var GetMoTableSizeFunc atomic.Pointer[GetMoTableSizeRowsFuncType] var GetMoTableRowsFunc atomic.Pointer[GetMoTableSizeRowsFuncType] +type subscription struct { + valid bool + + oriAccId uint64 + oriDatabaseId uint64 + oriTableId uint64 + + oriTableName string + oriDatabaseName string +} + +func (s subscription) String() string { + return fmt.Sprintf("valid: %v, oriAcc(%d), oriDatabase(%d-%s), oriTable(%d-%s)", + s.valid, + s.oriAccId, + s.oriDatabaseId, + s.oriDatabaseName, + s.oriTableId, + s.oriTableName) +} + +func isSubscribedTable( + proc *process.Process, + reqAcc uint32, + db engine.Database, + dbName, tblName string, +) (sub subscription, err error) { + + var ( + sql string + ret [][]interface{} + meta *plan.SubscriptionMeta + ) + + if db.IsSubscription(proc.Ctx) { + defer func() { + if err != nil { + sub.valid = false + + metaInfo := "" + if meta != nil { + metaInfo = fmt.Sprintf("ACC(%s,%d)-DB(%s)-TBLS(%s)", + meta.AccountName, meta.AccountId, meta.DbName, meta.Tables) + } + + logutil.Error("MO_TABLE_SIZE/ROWS", + zap.String("source", "isSubscribedTable"), + zap.Error(err), + zap.String("sub meta", metaInfo), + zap.Uint32("request acc", reqAcc), + zap.String("db name", dbName), + zap.String("tbl name", tblName), + zap.String("subscription", sub.String()), + zap.String("sql", sql), + ) + } + }() + + meta, err = proc.GetSessionInfo().SqlHelper.GetSubscriptionMeta(dbName) + if err != nil { + return sub, + moerr.NewInternalErrorNoCtx(fmt.Sprintf("get subscription meta failed, err: %v", err)) + } + + if meta.Tables != pubsub.TableAll && !strings.Contains(meta.Tables, tblName) { + return sub, moerr.NewInternalErrorNoCtx("no such subscribed table") + } + + // check passed, get acc, db, tbl info + ctx, cancel := context.WithTimeout(context.Background(), time.Minute) + ctx = defines.AttachAccountId(ctx, uint32(sysAccountID)) + defer cancel() + + sql = fmt.Sprintf(` + select + reldatabase_id, rel_id + from + mo_catalog.mo_tables + where + account_id = %d and reldatabase = '%s' and relname = '%s';`, + meta.AccountId, meta.DbName, tblName) + + ret, err = proc.GetSessionInfo().SqlHelper.ExecSqlWithCtx(ctx, sql) + if err != nil { + return sub, + moerr.NewInternalErrorNoCtx(fmt.Sprintf("exec get subscribed tbl info sql failed, err: %v", err)) + } + + if len(ret) != 1 { + return sub, + moerr.NewInternalErrorNoCtx(fmt.Sprintf("get the subscribed tbl info empty: %s", tblName)) + } + + sub.valid = true + sub.oriAccId = uint64(meta.AccountId) + sub.oriDatabaseId = ret[0][0].(uint64) + sub.oriTableId = ret[0][1].(uint64) + sub.oriTableName = tblName + sub.oriDatabaseName = meta.DbName + + return sub, nil + } + + sub.valid = false + return sub, nil +} + func MoTableSizeRowsHelper( iVecs []*vector.Vector, result vector.FunctionResultWrapper, @@ -283,17 +390,28 @@ func MoTableSizeRowsHelper( return err } - if rel, err = db.Relation(proc.Ctx, tblName, nil); err != nil { - if moerr.IsMoErrCode(err, moerr.OkExpectedEOB) { - return moerr.NewInternalErrorNoCtxf("tbl not exist: %s-%s(%s)", - dbName, tblName, "OkExpectedEOB") - } + var sub subscription + if sub, err = isSubscribedTable( + proc, accountId, db, dbName, tblName); err != nil { return err - } + } else if sub.valid { + // is subscription + accIds = append(accIds, sub.oriAccId) + dbIds = append(dbIds, sub.oriDatabaseId) + tblIds = append(tblIds, sub.oriTableId) + } else { + if rel, err = db.Relation(proc.Ctx, tblName, nil); err != nil { + if moerr.IsMoErrCode(err, moerr.OkExpectedEOB) { + return moerr.NewInternalErrorNoCtxf("tbl not exist: %s-%s(%s)", + dbName, tblName, "OkExpectedEOB") + } + return err + } - accIds = append(accIds, uint64(accountId)) - dbIds = append(dbIds, uint64(rel.GetDBID(proc.Ctx))) - tblIds = append(tblIds, uint64(rel.GetTableID(proc.Ctx))) + accIds = append(accIds, uint64(accountId)) + dbIds = append(dbIds, uint64(rel.GetDBID(proc.Ctx))) + tblIds = append(tblIds, uint64(rel.GetTableID(proc.Ctx))) + } } ret, err = (*executor.Load())()( @@ -399,6 +517,29 @@ func MoTableRowsOld(ivecs []*vector.Vector, result vector.FunctionResultWrapper, } return err } + + var accId uint32 + accId, err = defines.GetAccountId(foolCtx) + if err != nil { + return err + } + + var sub subscription + if sub, err = isSubscribedTable( + proc, accId, dbo, dbStr, tblStr); err != nil { + logutil.Error("MoTableRowsOld", + zap.String("source", "isSubscribeTable"), + zap.Error(err)) + return err + } else if sub.valid { + // subscription + foolCtx = defines.AttachAccountId(foolCtx, uint32(sub.oriAccId)) + dbo, err = e.Database(foolCtx, sub.oriDatabaseName, txn) + if err != nil { + return err + } + } + rel, err = dbo.Relation(foolCtx, tblStr, nil) if err != nil { return err @@ -518,6 +659,29 @@ func MoTableSizeOld(ivecs []*vector.Vector, result vector.FunctionResultWrapper, } return err } + + var accId uint32 + accId, err = defines.GetAccountId(foolCtx) + if err != nil { + return err + } + + var sub subscription + if sub, err = isSubscribedTable( + proc, accId, dbo, dbStr, tblStr); err != nil { + logutil.Error("MoTableSizeOld", + zap.String("source", "isSubscribeTable"), + zap.Error(err)) + return err + } else if sub.valid { + // subscription + foolCtx = defines.AttachAccountId(foolCtx, uint32(sub.oriAccId)) + dbo, err = e.Database(foolCtx, sub.oriDatabaseName, txn) + if err != nil { + return err + } + } + rel, err = dbo.Relation(foolCtx, tblStr, nil) if err != nil { return err diff --git a/pkg/vm/process/types.go b/pkg/vm/process/types.go index 62ace13c8f81f..f4ce0c0b6d638 100644 --- a/pkg/vm/process/types.go +++ b/pkg/vm/process/types.go @@ -319,6 +319,7 @@ type Process struct { type sqlHelper interface { GetCompilerContext() any ExecSql(string) ([][]interface{}, error) + ExecSqlWithCtx(context.Context, string) ([][]interface{}, error) GetSubscriptionMeta(string) (sub *plan.SubscriptionMeta, err error) } diff --git a/test/distributed/cases/function/func_mo.result b/test/distributed/cases/function/func_mo.result new file mode 100644 index 0000000000000..e0835dbcee155 --- /dev/null +++ b/test/distributed/cases/function/func_mo.result @@ -0,0 +1,67 @@ +drop database if exists testdb; +create database testdb; +use testdb; +create account acc admin_name "root" identified by "111"; +create publication pub1 database testdb account all; +create table t1 (a int); +create table t2 (a int); +create table t3 (a int); +insert into t1 select * from generate_series(1, 1000)g; +insert into t2 select * from generate_series(1, 1000)g; +insert into t3 select * from generate_series(1, 1000)g; +drop database if exists testdb_sub; +create database testdb_sub from sys publication pub1; +drop database if exists testdb_nor; +create database testdb_nor; +use testdb_nor; +create table t1 (a int); +create table t2 (a int); +create table t3 (a int); +insert into t1 select * from generate_series(1, 1001)g; +insert into t2 select * from generate_series(1, 1001)g; +insert into t3 select * from generate_series(1, 1001)g; +create table tmp(dbName varchar, tblName varchar); +insert into tmp values ("testdb_nor", "t1"), ("testdb_nor", "t2"), ("testdb_nor", "t3"); +insert into tmp values ("testdb_sub", "t1"), ("testdb_sub", "t2"), ("testdb_sub", "t3"); +set mo_table_stats.force_update = yes; +select mo_table_rows(dbName, tblName) from (select * from testdb_nor.tmp order by dbName, tblName asc); +mo_table_rows(dbName, tblName) +1001 +1001 +1001 +1000 +1000 +1000 +insert into tmp values ("testdb_sub", "t4"); +select mo_table_rows(dbName, tblName) from (select * from testdb_nor.tmp order by dbName, tblName asc); +internal error: get the subscribed tbl info empty: t4 +set mo_table_stats.force_update = no; +delete from tmp where dbName = "testdb_sub" and tblName = "t4"; +set mo_table_stats.use_old_impl = yes; +select mo_table_rows(dbName, tblName) from (select * from testdb_nor.tmp order by dbName, tblName asc); +mo_table_rows(dbName, tblName) +1001 +1001 +1001 +1000 +1000 +1000 +select mo_table_size(dbName, tblName) from (select * from testdb_nor.tmp order by dbName, tblName asc); +mo_table_size(dbName, tblName) +36036 +36036 +36036 +36000 +36000 +36000 +insert into tmp values ("testdb_sub", "t4"); +select mo_table_rows(dbName, tblName) from (select * from testdb_nor.tmp order by dbName, tblName asc); +internal error: get the subscribed tbl info empty: t4 +select mo_table_size(dbName, tblName) from (select * from testdb_nor.tmp order by dbName, tblName asc); +internal error: get the subscribed tbl info empty: t4 +set mo_table_stats.use_old_impl = no; +drop database testdb_nor; +drop database testdb_sub; +drop account acc; +drop publication pub1; +drop database testdb; \ No newline at end of file diff --git a/test/distributed/cases/function/func_mo.sql b/test/distributed/cases/function/func_mo.sql new file mode 100644 index 0000000000000..9f1cdb3bca72f --- /dev/null +++ b/test/distributed/cases/function/func_mo.sql @@ -0,0 +1,64 @@ +drop database if exists testdb; +create database testdb; +use testdb; + +create account acc admin_name "root" identified by "111"; +create publication pub1 database testdb account all; + +create table t1 (a int); +create table t2 (a int); +create table t3 (a int); + +insert into t1 select * from generate_series(1, 1000)g; +insert into t2 select * from generate_series(1, 1000)g; +insert into t3 select * from generate_series(1, 1000)g; + +-- @session:id=2&user=acc:root&password=111 +drop database if exists testdb_sub; +create database testdb_sub from sys publication pub1; + +drop database if exists testdb_nor; +create database testdb_nor; +use testdb_nor; + +create table t1 (a int); +create table t2 (a int); +create table t3 (a int); + +insert into t1 select * from generate_series(1, 1001)g; +insert into t2 select * from generate_series(1, 1001)g; +insert into t3 select * from generate_series(1, 1001)g; + +create table tmp(dbName varchar, tblName varchar); +insert into tmp values ("testdb_nor", "t1"), ("testdb_nor", "t2"), ("testdb_nor", "t3"); +insert into tmp values ("testdb_sub", "t1"), ("testdb_sub", "t2"), ("testdb_sub", "t3"); + +set mo_table_stats.force_update = yes; +select mo_table_rows(dbName, tblName) from (select * from testdb_nor.tmp order by dbName, tblName asc); + +insert into tmp values ("testdb_sub", "t4"); +select mo_table_rows(dbName, tblName) from (select * from testdb_nor.tmp order by dbName, tblName asc); + +set mo_table_stats.force_update = no; +delete from tmp where dbName = "testdb_sub" and tblName = "t4"; + +set mo_table_stats.use_old_impl = yes; +select mo_table_rows(dbName, tblName) from (select * from testdb_nor.tmp order by dbName, tblName asc); +-- @ignore:0 +select mo_table_size(dbName, tblName) from (select * from testdb_nor.tmp order by dbName, tblName asc); + +insert into tmp values ("testdb_sub", "t4"); +select mo_table_rows(dbName, tblName) from (select * from testdb_nor.tmp order by dbName, tblName asc); +-- @ignore:0 +select mo_table_size(dbName, tblName) from (select * from testdb_nor.tmp order by dbName, tblName asc); + +set mo_table_stats.use_old_impl = no; + +drop database testdb_nor; +drop database testdb_sub; + +-- @session + +drop account acc; +drop publication pub1; +drop database testdb; \ No newline at end of file From f0301a78ca24886030d4b1a8024700fb84298f5e Mon Sep 17 00:00:00 2001 From: XuPeng-SH Date: Sun, 22 Dec 2024 18:42:06 +0800 Subject: [PATCH 18/26] fix force global checkpoint (#20865) fix force global checkpoint implementation Approved by: @LeftHandCold --- pkg/vm/engine/tae/db/checkpoint/testutils.go | 49 +++++++++++--------- 1 file changed, 27 insertions(+), 22 deletions(-) diff --git a/pkg/vm/engine/tae/db/checkpoint/testutils.go b/pkg/vm/engine/tae/db/checkpoint/testutils.go index 4d9bd3f426961..6d08b66f14889 100644 --- a/pkg/vm/engine/tae/db/checkpoint/testutils.go +++ b/pkg/vm/engine/tae/db/checkpoint/testutils.go @@ -59,25 +59,35 @@ func (r *runner) ForceGlobalCheckpoint(end types.TS, interval time.Duration) err interval = r.options.globalVersionInterval } if r.GetPenddingIncrementalCount() != 0 { - end = r.MaxIncrementalCheckpoint().GetEnd() - r.globalCheckpointQueue.Enqueue(&globalCheckpointContext{ - force: true, - end: end, - interval: interval, - }) - return nil + end2 := r.MaxIncrementalCheckpoint().GetEnd() + if end2.GE(&end) { + r.globalCheckpointQueue.Enqueue(&globalCheckpointContext{ + force: true, + end: end, + interval: interval, + }) + return nil + } } - retryTime := 0 - timeout := time.After(interval) - var err error + var ( + retryTime int + now = time.Now() + err error + ) defer func() { - if err != nil || retryTime > 0 { - logutil.Error("ForceGlobalCheckpoint-End", - zap.Error(err), - zap.Uint64("retryTime", uint64(retryTime))) - return + logger := logutil.Info + if err != nil { + logger = logutil.Error } + logger( + "ForceGlobalCheckpoint-End", + zap.Int("retry-time", retryTime), + zap.Duration("cost", time.Since(now)), + zap.Error(err), + ) }() + + timeout := time.After(interval) for { select { case <-timeout: @@ -104,12 +114,6 @@ func (r *runner) ForceGlobalCheckpoint(end types.TS, interval time.Duration) err } func (r *runner) ForceGlobalCheckpointSynchronously(ctx context.Context, end types.TS, versionInterval time.Duration) error { - prevGlobalEnd := types.TS{} - global := r.store.MaxGlobalCheckpoint() - if global != nil { - prevGlobalEnd = global.end - } - r.ForceGlobalCheckpoint(end, versionInterval) op := func() (ok bool, err error) { @@ -117,7 +121,8 @@ func (r *runner) ForceGlobalCheckpointSynchronously(ctx context.Context, end typ if global == nil { return false, nil } - return global.end.GT(&prevGlobalEnd), nil + ok = global.end.GE(&end) + return } err := common.RetryWithIntervalAndTimeout( op, From c4ab91069e961b8f8136f4148b5c24eb9274ac43 Mon Sep 17 00:00:00 2001 From: qingxinhome <70939751+qingxinhome@users.noreply.github.com> Date: Mon, 23 Dec 2024 10:43:06 +0800 Subject: [PATCH 19/26] TxnExecuter context missing roleId when executes the DDL statement (#20856) 1. TxnExecuter context supplements roleID and userID when executes the DDL statement 2. Schema upgrade framework supplements roleID and userID when exec DDL upgrade SQL Approved by: @m-schen, @daviszhen, @ouyuanning, @LeftHandCold, @badboynt1, @zhangxu19830126 --- pkg/bootstrap/versions/upgrade_strategy.go | 23 +++++++-- pkg/bootstrap/versions/v2_0_1/upgrade_test.go | 4 +- pkg/sql/compile/sql_executor.go | 32 ++++++------ pkg/sql/compile/sql_executor_context_test.go | 6 +-- pkg/tests/txnexecutor/txn_executor_test.go | 49 +++++++++++++++++++ pkg/util/executor/options.go | 26 ++++++++++ pkg/util/executor/types.go | 2 + 7 files changed, 117 insertions(+), 25 deletions(-) create mode 100644 pkg/tests/txnexecutor/txn_executor_test.go diff --git a/pkg/bootstrap/versions/upgrade_strategy.go b/pkg/bootstrap/versions/upgrade_strategy.go index da647885e9999..be3ecb8a54895 100644 --- a/pkg/bootstrap/versions/upgrade_strategy.go +++ b/pkg/bootstrap/versions/upgrade_strategy.go @@ -25,6 +25,16 @@ import ( "github.com/matrixorigin/matrixone/pkg/util/executor" ) +const ( + // system tenant default role ID and user ID + sysAdminRoleID = 0 + sysRootID = 0 + + // general tenant default role ID and user ID + accountAdminRoleID = 2 + accountAdminUserID = 2 +) + const ( T_any = "ANY" T_bool = "BOOL" @@ -131,6 +141,13 @@ type UpgradeEntry struct { // Upgrade entity execution upgrade entrance func (u *UpgradeEntry) Upgrade(txn executor.TxnExecutor, accountId uint32) error { + userId := uint32(sysRootID) + roleId := uint32(sysAdminRoleID) + if accountId != catalog.System_Account { + userId = accountAdminUserID + roleId = accountAdminRoleID + } + exist, err := u.CheckFunc(txn, accountId) if err != nil { getLogger(txn.Txn().TxnOptions().CN).Error("execute upgrade entry check error", zap.Error(err), zap.String("upgrade entry", u.String())) @@ -142,7 +159,7 @@ func (u *UpgradeEntry) Upgrade(txn executor.TxnExecutor, accountId uint32) error } else { // 1. First, judge whether there is prefix sql if u.PreSql != "" { - res, err := txn.Exec(u.PreSql, executor.StatementOption{}.WithAccountID(accountId)) + res, err := txn.Exec(u.PreSql, executor.StatementOption{}.WithAccountID(accountId).WithUserID(userId).WithRoleID(roleId)) if err != nil { getLogger(txn.Txn().TxnOptions().CN).Error("execute upgrade entry pre-sql error", zap.Error(err), zap.String("upgrade entry", u.String())) return err @@ -151,7 +168,7 @@ func (u *UpgradeEntry) Upgrade(txn executor.TxnExecutor, accountId uint32) error } // 2. Second, Execute upgrade sql - res, err := txn.Exec(u.UpgSql, executor.StatementOption{}.WithAccountID(accountId)) + res, err := txn.Exec(u.UpgSql, executor.StatementOption{}.WithAccountID(accountId).WithUserID(userId).WithRoleID(roleId)) if err != nil { getLogger(txn.Txn().TxnOptions().CN).Error("execute upgrade entry sql error", zap.Error(err), zap.String("upgrade entry", u.String())) return err @@ -160,7 +177,7 @@ func (u *UpgradeEntry) Upgrade(txn executor.TxnExecutor, accountId uint32) error // 2. Third, after the upgrade is completed, judge whether there is post-sql if u.PostSql != "" { - res, err = txn.Exec(u.PostSql, executor.StatementOption{}.WithAccountID(accountId)) + res, err = txn.Exec(u.PostSql, executor.StatementOption{}.WithAccountID(accountId).WithUserID(userId).WithRoleID(roleId)) if err != nil { getLogger(txn.Txn().TxnOptions().CN).Error("execute upgrade entry post-sql error", zap.Error(err), zap.String("upgrade entry", u.String())) return err diff --git a/pkg/bootstrap/versions/v2_0_1/upgrade_test.go b/pkg/bootstrap/versions/v2_0_1/upgrade_test.go index c5c1766580fba..5488911b2db85 100644 --- a/pkg/bootstrap/versions/v2_0_1/upgrade_test.go +++ b/pkg/bootstrap/versions/v2_0_1/upgrade_test.go @@ -215,8 +215,8 @@ func Test_HandleTenantUpgrade(t *testing.T) { return executor.Result{}, nil }, txnOperator) - upg_mo_user_add_password_last_changed.Upgrade(executor, uint32(0)) - upg_mo_user_add_lock_time.Upgrade(executor, uint32(0)) + upg_mo_user_add_password_last_changed.Upgrade(executor, uint32(1)) + upg_mo_user_add_lock_time.Upgrade(executor, uint32(1)) }, ) runtime.RunTest( diff --git a/pkg/sql/compile/sql_executor.go b/pkg/sql/compile/sql_executor.go index c88bf3f483fe8..f1867fd4590e3 100644 --- a/pkg/sql/compile/sql_executor.go +++ b/pkg/sql/compile/sql_executor.go @@ -22,7 +22,6 @@ import ( "go.uber.org/zap" - "github.com/matrixorigin/matrixone/pkg/catalog" "github.com/matrixorigin/matrixone/pkg/common/buffer" "github.com/matrixorigin/matrixone/pkg/common/moerr" "github.com/matrixorigin/matrixone/pkg/common/mpool" @@ -241,27 +240,26 @@ func (exec *txnExecutor) Exec( sql string, statementOption executor.StatementOption, ) (executor.Result, error) { - - //----------------------------------------------------------------------------------------- // NOTE: This code is to restore tenantID information in the Context when temporarily switching tenants // so that it can be restored to its original state after completing the task. - recoverAccount := func(exec *txnExecutor, accId uint32) { - exec.ctx = context.WithValue(exec.ctx, defines.TenantIDKey{}, accId) - } - + var originCtx context.Context if statementOption.HasAccountID() { - originAccountID := catalog.System_Account - if v := exec.ctx.Value(defines.TenantIDKey{}); v != nil { - originAccountID = v.(uint32) + // save the current context + originCtx = exec.ctx + // switch tenantID + exec.ctx = context.WithValue(exec.ctx, defines.TenantIDKey{}, statementOption.AccountID()) + if statementOption.HasUserID() { + exec.ctx = context.WithValue(exec.ctx, defines.UserIDKey{}, statementOption.UserID()) } - - exec.ctx = context.WithValue(exec.ctx, - defines.TenantIDKey{}, - statementOption.AccountID()) - // NOTE: Restore AccountID information in context.Context - defer recoverAccount(exec, originAccountID) + if statementOption.HasRoleID() { + exec.ctx = context.WithValue(exec.ctx, defines.RoleIDKey{}, statementOption.RoleID()) + } + defer func() { + // restore context at the end of the function + exec.ctx = originCtx + }() } - //----------------------------------------------------------------------------------------- + //------------------------------------------------------------------------------------------------------------------ if statementOption.IgnoreForeignKey() { exec.ctx = context.WithValue(exec.ctx, defines.IgnoreForeignKey{}, diff --git a/pkg/sql/compile/sql_executor_context_test.go b/pkg/sql/compile/sql_executor_context_test.go index 44096b6dade29..1ac3dd0ab4254 100644 --- a/pkg/sql/compile/sql_executor_context_test.go +++ b/pkg/sql/compile/sql_executor_context_test.go @@ -17,12 +17,12 @@ package compile import ( "testing" - "github.com/matrixorigin/matrixone/pkg/common/mpool" - "github.com/matrixorigin/matrixone/pkg/testutil" - "github.com/golang/mock/gomock" "github.com/stretchr/testify/require" + "github.com/matrixorigin/matrixone/pkg/common/mpool" + "github.com/matrixorigin/matrixone/pkg/testutil" + mock_frontend "github.com/matrixorigin/matrixone/pkg/frontend/test" "github.com/matrixorigin/matrixone/pkg/sql/plan" ) diff --git a/pkg/tests/txnexecutor/txn_executor_test.go b/pkg/tests/txnexecutor/txn_executor_test.go new file mode 100644 index 0000000000000..3d9a683119db1 --- /dev/null +++ b/pkg/tests/txnexecutor/txn_executor_test.go @@ -0,0 +1,49 @@ +// Copyright 2024 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 txnexecutor + +import ( + "context" + "testing" + "time" + + "github.com/stretchr/testify/require" + + "github.com/matrixorigin/matrixone/pkg/embed" + "github.com/matrixorigin/matrixone/pkg/tests/testutils" + "github.com/matrixorigin/matrixone/pkg/util/executor" +) + +func Test_TxnExecutorExec(t *testing.T) { + c, err := embed.NewCluster(embed.WithCNCount(1)) + require.NoError(t, err) + require.NoError(t, c.Start()) + + svc, err := c.GetCNService(0) + require.NoError(t, err) + + exec := testutils.GetSQLExecutor(svc) + require.NotNil(t, exec) + + ctx, cancel := context.WithTimeout(context.Background(), time.Minute*5) + defer cancel() + + err = exec.ExecTxn(ctx, func(txn executor.TxnExecutor) error { + _, err = txn.Exec("select count(*) from mo_catalog.mo_tables", executor.StatementOption{}.WithAccountID(1).WithUserID(2).WithRoleID(2)) + require.NoError(t, err) + return nil + }, executor.Options{}.WithWaitCommittedLogApplied()) + require.NoError(t, err) +} diff --git a/pkg/util/executor/options.go b/pkg/util/executor/options.go index 2c601a6a5ec6f..d89ead9c62efe 100644 --- a/pkg/util/executor/options.go +++ b/pkg/util/executor/options.go @@ -169,6 +169,32 @@ func (opts StatementOption) HasAccountID() bool { return opts.accountId > 0 } +func (opts StatementOption) WithRoleID(roleID uint32) StatementOption { + opts.roleId = roleID + return opts +} + +func (opts StatementOption) RoleID() uint32 { + return opts.roleId +} + +func (opts StatementOption) HasRoleID() bool { + return opts.roleId > 0 +} + +func (opts StatementOption) WithUserID(userID uint32) StatementOption { + opts.userId = userID + return opts +} + +func (opts StatementOption) UserID() uint32 { + return opts.userId +} + +func (opts StatementOption) HasUserID() bool { + return opts.userId > 0 +} + func (opts StatementOption) WithDisableLog() StatementOption { opts.disableLog = true return opts diff --git a/pkg/util/executor/types.go b/pkg/util/executor/types.go index 09709fb4b77ef..1aca13449ac26 100644 --- a/pkg/util/executor/types.go +++ b/pkg/util/executor/types.go @@ -71,6 +71,8 @@ type Options struct { type StatementOption struct { waitPolicy lock.WaitPolicy accountId uint32 + roleId uint32 + userId uint32 disableLog bool ignoreForeignKey bool } From 28cc3060a8592dfa93922e7f781396c4964abb6e Mon Sep 17 00:00:00 2001 From: Wenbin <85331908+Wenbin1002@users.noreply.github.com> Date: Mon, 23 Dec 2024 12:16:23 +0800 Subject: [PATCH 20/26] Dynamic workspace threshold (#20843) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 支持workspace write threshold动态扩容 Approved by: @aptend, @zhangxu19830126, @LeftHandCold, @triump2020, @XuPeng-SH --- .../db/merge => objectio}/meminfo_darwin.go | 6 +- .../db/merge => objectio}/meminfo_linux.go | 4 +- .../v2/dashboard/grafana_dashboard_txn.go | 12 + pkg/util/metric/v2/metrics.go | 1 + pkg/util/metric/v2/txn.go | 10 + pkg/vm/engine/disttae/engine.go | 31 +++ pkg/vm/engine/disttae/txn.go | 37 +++ pkg/vm/engine/disttae/types.go | 14 +- pkg/vm/engine/tae/db/merge/utils.go | 2 +- pkg/vm/engine/test/disttae_engine_test.go | 229 ++++++++++++++++++ pkg/vm/engine/test/testutil/disttae_engine.go | 4 + pkg/vm/engine/test/testutil/util.go | 5 + pkg/vm/engine/test/workspace_test.go | 1 + 13 files changed, 348 insertions(+), 8 deletions(-) rename pkg/{vm/engine/tae/db/merge => objectio}/meminfo_darwin.go (89%) rename pkg/{vm/engine/tae/db/merge => objectio}/meminfo_linux.go (94%) diff --git a/pkg/vm/engine/tae/db/merge/meminfo_darwin.go b/pkg/objectio/meminfo_darwin.go similarity index 89% rename from pkg/vm/engine/tae/db/merge/meminfo_darwin.go rename to pkg/objectio/meminfo_darwin.go index 6d7dee835ef34..6f1d61b7fe060 100644 --- a/pkg/vm/engine/tae/db/merge/meminfo_darwin.go +++ b/pkg/objectio/meminfo_darwin.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 // -// http://www.apache.org/licenses/LICENSE-2.0 +// 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, @@ -12,14 +12,14 @@ // See the License for the specific language governing permissions and // limitations under the License. -package merge +package objectio import ( "syscall" "unsafe" ) -func totalMem() uint64 { +func TotalMem() uint64 { s, err := syscall.Sysctl("hw.memsize") if err != nil { return 0 diff --git a/pkg/vm/engine/tae/db/merge/meminfo_linux.go b/pkg/objectio/meminfo_linux.go similarity index 94% rename from pkg/vm/engine/tae/db/merge/meminfo_linux.go rename to pkg/objectio/meminfo_linux.go index 57a10daf214ac..2dd458f800b24 100644 --- a/pkg/vm/engine/tae/db/merge/meminfo_linux.go +++ b/pkg/objectio/meminfo_linux.go @@ -12,11 +12,11 @@ // See the License for the specific language governing permissions and // limitations under the License. -package merge +package objectio import "syscall" -func totalMem() uint64 { +func TotalMem() uint64 { in := new(syscall.Sysinfo_t) err := syscall.Sysinfo(in) if err != nil { diff --git a/pkg/util/metric/v2/dashboard/grafana_dashboard_txn.go b/pkg/util/metric/v2/dashboard/grafana_dashboard_txn.go index 861ea24a1a7e7..0e9bc3555234d 100644 --- a/pkg/util/metric/v2/dashboard/grafana_dashboard_txn.go +++ b/pkg/util/metric/v2/dashboard/grafana_dashboard_txn.go @@ -53,6 +53,7 @@ func (c *DashboardCreator) initTxnDashboard() error { c.initTxnShowAccountsRow(), c.initCNCommittedObjectQuantityRow(), c.initTombstoneTransferRow(), + c.initTxnExtraWorkspaceQuota(), c.initTxnCheckPKChangedRow(), )...) if err != nil { @@ -590,3 +591,14 @@ func (c *DashboardCreator) initTombstoneTransferRow() dashboard.Option { ), ) } + +func (c *DashboardCreator) initTxnExtraWorkspaceQuota() dashboard.Option { + return dashboard.Row( + "Extra Workspace Quota", + c.withGraph( + "Extra Workspace Quota", + 12, + `sum(`+c.getMetricWithFilter("mo_txn_extra_workspace_quota", ``)+`)`, + "{{ "+c.by+" }}", axis.Unit("decbytes")), + ) +} diff --git a/pkg/util/metric/v2/metrics.go b/pkg/util/metric/v2/metrics.go index db959e1ef7571..22a49be0104f6 100644 --- a/pkg/util/metric/v2/metrics.go +++ b/pkg/util/metric/v2/metrics.go @@ -162,6 +162,7 @@ func initTxnMetrics() { registry.MustRegister(txnReaderTombstoneSelectivityHistogram) registry.MustRegister(txnTransferDurationHistogram) registry.MustRegister(TransferTombstonesCountHistogram) + registry.MustRegister(TxnExtraWorkspaceQuotaGauge) } func initRPCMetrics() { diff --git a/pkg/util/metric/v2/txn.go b/pkg/util/metric/v2/txn.go index d4c7d1c011e4c..68b577546e1bc 100644 --- a/pkg/util/metric/v2/txn.go +++ b/pkg/util/metric/v2/txn.go @@ -408,3 +408,13 @@ var ( TransferTombstonesDurationHistogram = txnTransferDurationHistogram.WithLabelValues("tombstones") BatchTransferTombstonesDurationHistogram = txnTransferDurationHistogram.WithLabelValues("batch") ) + +var ( + TxnExtraWorkspaceQuotaGauge = prometheus.NewGauge( + prometheus.GaugeOpts{ + Namespace: "mo", + Subsystem: "txn", + Name: "extra_workspace_quota", + Help: "Extra workspace quota for txn.", + }) +) diff --git a/pkg/vm/engine/disttae/engine.go b/pkg/vm/engine/disttae/engine.go index 4e66b03f18ad5..fca32769ad6d8 100644 --- a/pkg/vm/engine/disttae/engine.go +++ b/pkg/vm/engine/disttae/engine.go @@ -39,6 +39,7 @@ import ( "github.com/matrixorigin/matrixone/pkg/lockservice" "github.com/matrixorigin/matrixone/pkg/logservice" "github.com/matrixorigin/matrixone/pkg/logutil" + "github.com/matrixorigin/matrixone/pkg/objectio" "github.com/matrixorigin/matrixone/pkg/pb/metadata" "github.com/matrixorigin/matrixone/pkg/pb/plan" pb "github.com/matrixorigin/matrixone/pkg/pb/statsinfo" @@ -163,15 +164,24 @@ func (e *Engine) fillDefaults() { if e.config.writeWorkspaceThreshold <= 0 { e.config.writeWorkspaceThreshold = WriteWorkspaceThreshold } + if e.config.extraWorkspaceThreshold <= 0 { + e.config.extraWorkspaceThreshold = ExtraWorkspaceThreshold + } if e.config.cnTransferTxnLifespanThreshold <= 0 { e.config.cnTransferTxnLifespanThreshold = CNTransferTxnLifespanThreshold } + if e.config.quota.Load() <= 0 { + mem := objectio.TotalMem() / 100 * 5 + e.config.quota.Store(mem) + v2.TxnExtraWorkspaceQuotaGauge.Set(float64(mem)) + } logutil.Info( "INIT-ENGINE-CONFIG", zap.Int("InsertEntryMaxCount", e.config.insertEntryMaxCount), zap.Uint64("CommitWorkspaceThreshold", e.config.commitWorkspaceThreshold), zap.Uint64("WriteWorkspaceThreshold", e.config.writeWorkspaceThreshold), + zap.Uint64("ExtraWorkspaceThresholdQuota", e.config.quota.Load()), zap.Duration("CNTransferTxnLifespanThreshold", e.config.cnTransferTxnLifespanThreshold), ) } @@ -191,6 +201,27 @@ func (e *Engine) SetWorkspaceThreshold(commitThreshold, writeThreshold uint64) ( return } +func (e *Engine) AcquireQuota(v uint64) (uint64, bool) { + for { + oldRemaining := e.config.quota.Load() + if oldRemaining < v { + return 0, false + } + remaining := oldRemaining - v + if e.config.quota.CompareAndSwap(oldRemaining, remaining) { + v2.TxnExtraWorkspaceQuotaGauge.Set(float64(remaining)) + return remaining, true + } + } +} + +func (e *Engine) ReleaseQuota(quota uint64) (remaining uint64) { + e.config.quota.Add(quota) + remaining = e.config.quota.Load() + v2.TxnExtraWorkspaceQuotaGauge.Set(float64(remaining)) + return +} + func (e *Engine) GetService() string { return e.service } diff --git a/pkg/vm/engine/disttae/txn.go b/pkg/vm/engine/disttae/txn.go index 7bd38e9ce2ce1..de474d6b445ce 100644 --- a/pkg/vm/engine/disttae/txn.go +++ b/pkg/vm/engine/disttae/txn.go @@ -482,6 +482,23 @@ func (txn *Transaction) dumpBatchLocked(ctx context.Context, offset int) error { if size < txn.writeWorkspaceThreshold { return nil } + + if size < txn.engine.config.extraWorkspaceThreshold { + // try to increase the write threshold from quota, if failed, then dump all + // acquire 5M more than we need + quota := size - txn.writeWorkspaceThreshold + txn.engine.config.writeWorkspaceThreshold + remaining, acquired := txn.engine.AcquireQuota(quota) + if acquired { + logutil.Info( + "WORKSPACE-QUOTA-ACQUIRE", + zap.Uint64("quota", quota), + zap.Uint64("remaining", remaining), + ) + txn.writeWorkspaceThreshold += quota + txn.extraWriteWorkspaceThreshold += quota + return nil + } + } size = 0 } txn.hasS3Op.Store(true) @@ -489,6 +506,16 @@ func (txn *Transaction) dumpBatchLocked(ctx context.Context, offset int) error { if err := txn.dumpInsertBatchLocked(ctx, offset, &size, &pkCount); err != nil { return err } + // release the extra quota + if txn.extraWriteWorkspaceThreshold > 0 { + remaining := txn.engine.ReleaseQuota(txn.extraWriteWorkspaceThreshold) + logutil.Info( + "WORKSPACE-QUOTA-RELEASE", + zap.Uint64("quota", txn.extraWriteWorkspaceThreshold), + zap.Uint64("remaining", remaining), + ) + txn.extraWriteWorkspaceThreshold = 0 + } if dumpAll { if txn.approximateInMemDeleteCnt >= txn.engine.config.insertEntryMaxCount { @@ -1463,6 +1490,16 @@ func (txn *Transaction) delTransaction() { txn.transfer.timestamps = nil txn.transfer.lastTransferred = types.TS{} txn.transfer.pendingTransfer = false + + if txn.extraWriteWorkspaceThreshold > 0 { + remaining := txn.engine.ReleaseQuota(txn.extraWriteWorkspaceThreshold) + logutil.Info( + "WORKSPACE-QUOTA-RELEASE", + zap.Uint64("quota", txn.extraWriteWorkspaceThreshold), + zap.Uint64("remaining", remaining), + ) + txn.extraWriteWorkspaceThreshold = 0 + } } func (txn *Transaction) rollbackTableOpLocked() { diff --git a/pkg/vm/engine/disttae/types.go b/pkg/vm/engine/disttae/types.go index 37a504ecb3ac4..81cb9ebed15b8 100644 --- a/pkg/vm/engine/disttae/types.go +++ b/pkg/vm/engine/disttae/types.go @@ -134,6 +134,7 @@ const ( const ( CommitWorkspaceThreshold uint64 = 1 * mpool.MB WriteWorkspaceThreshold uint64 = 5 * mpool.MB + ExtraWorkspaceThreshold uint64 = 500 * mpool.MB InsertEntryThreshold = 5000 GCBatchOfFileCount int = 1000 GCPoolSize int = 5 @@ -168,6 +169,12 @@ func WithWriteWorkspaceThreshold(th uint64) EngineOptions { } } +func WithExtraWorkspaceThresholdQuota(quota uint64) EngineOptions { + return func(e *Engine) { + e.config.quota.Store(quota) + } +} + func WithInsertEntryMaxCount(th int) EngineOptions { return func(e *Engine) { e.config.insertEntryMaxCount = th @@ -215,6 +222,8 @@ type Engine struct { insertEntryMaxCount int commitWorkspaceThreshold uint64 writeWorkspaceThreshold uint64 + extraWorkspaceThreshold uint64 + quota atomic.Uint64 cnTransferTxnLifespanThreshold time.Duration @@ -364,8 +373,9 @@ type Transaction struct { haveDDL atomic.Bool - writeWorkspaceThreshold uint64 - commitWorkspaceThreshold uint64 + writeWorkspaceThreshold uint64 + commitWorkspaceThreshold uint64 + extraWriteWorkspaceThreshold uint64 // acquired from engine quota } type Pos struct { diff --git a/pkg/vm/engine/tae/db/merge/utils.go b/pkg/vm/engine/tae/db/merge/utils.go index 8327646f93978..aa015d41df36d 100644 --- a/pkg/vm/engine/tae/db/merge/utils.go +++ b/pkg/vm/engine/tae/db/merge/utils.go @@ -228,7 +228,7 @@ func (c *resourceController) setMemLimit(total uint64) { func (c *resourceController) refresh() { if c.limit == 0 { - c.setMemLimit(totalMem()) + c.setMemLimit(objectio.TotalMem()) } if c.proc == nil { diff --git a/pkg/vm/engine/test/disttae_engine_test.go b/pkg/vm/engine/test/disttae_engine_test.go index db004aaea5baa..2a4456a9ddc4d 100644 --- a/pkg/vm/engine/test/disttae_engine_test.go +++ b/pkg/vm/engine/test/disttae_engine_test.go @@ -25,6 +25,7 @@ import ( "github.com/stretchr/testify/require" "github.com/matrixorigin/matrixone/pkg/catalog" + "github.com/matrixorigin/matrixone/pkg/common/mpool" "github.com/matrixorigin/matrixone/pkg/common/runtime" "github.com/matrixorigin/matrixone/pkg/container/batch" "github.com/matrixorigin/matrixone/pkg/container/types" @@ -33,6 +34,7 @@ import ( "github.com/matrixorigin/matrixone/pkg/objectio" "github.com/matrixorigin/matrixone/pkg/pb/api" "github.com/matrixorigin/matrixone/pkg/pb/timestamp" + "github.com/matrixorigin/matrixone/pkg/txn/client" "github.com/matrixorigin/matrixone/pkg/util/executor" "github.com/matrixorigin/matrixone/pkg/util/fault" "github.com/matrixorigin/matrixone/pkg/vm/engine" @@ -1254,3 +1256,230 @@ func TestRelationExists(t *testing.T) { require.Error(t, err) require.Nil(t, rel) } + +func TestWorkspaceQuota(t *testing.T) { + quotaSize := uint64(1000) + + p := testutil.InitEnginePack(testutil.TestOptions{ + DisttaeOptions: []testutil.TestDisttaeEngineOptions{testutil.WithDisttaeEngineQuota(quotaSize)}}, t) + defer p.Close() + e := p.D.Engine + var wg sync.WaitGroup + wg.Add(10) + for i := range uint64(10) { + go func(size uint64, wg *sync.WaitGroup) { + e.AcquireQuota(100) + e.ReleaseQuota(100) + wg.Done() + }(i*100, &wg) + } + + wg.Wait() + remaining, _ := e.AcquireQuota(0) + require.Equal(t, int(quotaSize), int(remaining)) + _, acquired := e.AcquireQuota(quotaSize + 1) + require.False(t, acquired) +} + +func TestWorkspaceQuota2(t *testing.T) { + + var ( + err error + mp *mpool.MPool + txn client.TxnOperator + accountId = catalog.System_Account + tableName = "test_table" + databaseName = "test_database" + + primaryKeyIdx = 3 + + relation engine.Relation + _ engine.Database + + taeEngine *testutil.TestTxnStorage + rpcAgent *testutil.MockRPCAgent + disttaeEngine *testutil.TestDisttaeEngine + ) + + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + ctx = context.WithValue(ctx, defines.TenantIDKey{}, accountId) + + schema := catalog2.MockSchemaAll(4, primaryKeyIdx) + schema.Name = tableName + + disttaeEngine, taeEngine, rpcAgent, mp = testutil.CreateEngines( + ctx, + testutil.TestOptions{}, + t, + testutil.WithDisttaeEngineWriteWorkspaceThreshold(1), + testutil.WithDisttaeEngineQuota(800), + ) + defer func() { + disttaeEngine.Close(ctx) + taeEngine.Close(true) + rpcAgent.Close() + }() + + ctx, cancel = context.WithTimeout(ctx, time.Minute) + defer cancel() + _, _, err = disttaeEngine.CreateDatabaseAndTable(ctx, databaseName, tableName, schema) + require.NoError(t, err) + + rowsCount := 30 + bat := catalog2.MockBatch(schema, rowsCount) + bat1, _ := containers.ToCNBatch(bat).Window(0, 10) + bat2, _ := containers.ToCNBatch(bat).Window(10, 20) + bat3, _ := containers.ToCNBatch(bat).Window(20, 30) + + { + _, relation, txn, err = disttaeEngine.GetTable(ctx, databaseName, tableName) + require.NoError(t, err) + // exceed workspace write threshold, acquire quota success, do not write s3 + require.NoError(t, relation.Write(ctx, bat1)) + // exceed workspace write threshold, acquire quota success, do not write s3 + require.NoError(t, relation.Write(ctx, bat2)) + // exceed workspace write threshold, acquire quota failed, write s3 + require.NoError(t, relation.Write(ctx, bat3)) + // exceed workspace commit threshold, write s3 + require.NoError(t, txn.Commit(ctx)) + } + + require.NoError(t, disttaeEngine.SubscribeTable(ctx, relation.GetDBID(ctx), relation.GetTableID(ctx), false)) + state, err := disttaeEngine.GetPartitionStateStats(ctx, relation.GetDBID(ctx), relation.GetTableID(ctx)) + require.NoError(t, err) + t.Log(state.String()) + require.Equal(t, 1, state.DataObjectsVisible.ObjCnt) + + _, relation, txn, err = disttaeEngine.GetTable(ctx, databaseName, tableName) + + require.NoError(t, err) + reader, err := testutil.GetRelationReader( + ctx, + disttaeEngine, + txn, + relation, + nil, + mp, + t, + ) + require.NoError(t, err) + + ret := testutil.EmptyBatchFromSchema(schema, primaryKeyIdx) + cnt := 0 + for { + ok, err := reader.Read(ctx, ret.Attrs, nil, mp, ret) + require.NoError(t, err) + cnt += ret.RowCount() + if ok { + break + } + } + require.NoError(t, err) + + require.Equal(t, rowsCount, cnt) + require.NoError(t, txn.Commit(ctx)) +} + +func TestWorkspaceQuota3(t *testing.T) { + + var ( + err error + mp *mpool.MPool + txn client.TxnOperator + accountId = catalog.System_Account + tableName = "test_table" + databaseName = "test_database" + + primaryKeyIdx = 3 + + relation engine.Relation + _ engine.Database + + taeEngine *testutil.TestTxnStorage + rpcAgent *testutil.MockRPCAgent + disttaeEngine *testutil.TestDisttaeEngine + ) + + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + ctx = context.WithValue(ctx, defines.TenantIDKey{}, accountId) + + schema := catalog2.MockSchemaAll(4, primaryKeyIdx) + schema.Name = tableName + + disttaeEngine, taeEngine, rpcAgent, mp = testutil.CreateEngines( + ctx, + testutil.TestOptions{}, + t, + testutil.WithDisttaeEngineWriteWorkspaceThreshold(1), + testutil.WithDisttaeEngineQuota(10000), + ) + defer func() { + disttaeEngine.Close(ctx) + taeEngine.Close(true) + rpcAgent.Close() + }() + + ctx, cancel = context.WithTimeout(ctx, time.Minute) + defer cancel() + _, _, err = disttaeEngine.CreateDatabaseAndTable(ctx, databaseName, tableName, schema) + require.NoError(t, err) + + rowsCount := 30 + bat := catalog2.MockBatch(schema, rowsCount) + bat1, _ := containers.ToCNBatch(bat).Window(0, 10) + bat2, _ := containers.ToCNBatch(bat).Window(10, 20) + bat3, _ := containers.ToCNBatch(bat).Window(20, 30) + + { + _, relation, txn, err = disttaeEngine.GetTable(ctx, databaseName, tableName) + require.NoError(t, err) + require.NoError(t, relation.Write(ctx, bat1)) + require.NoError(t, relation.Write(ctx, bat2)) + require.NoError(t, relation.Write(ctx, bat3)) + require.NoError(t, txn.Rollback(ctx)) + } + + { + _, relation, txn, err = disttaeEngine.GetTable(ctx, databaseName, tableName) + require.NoError(t, err) + require.NoError(t, relation.Write(ctx, bat1)) + require.NoError(t, relation.Write(ctx, bat2)) + require.NoError(t, relation.Write(ctx, bat3)) + require.NoError(t, txn.Commit(ctx)) + } + + // test quota leak + quota, _ := disttaeEngine.Engine.AcquireQuota(0) + require.Equal(t, 10000, int(quota)) + + _, relation, txn, err = disttaeEngine.GetTable(ctx, databaseName, tableName) + + require.NoError(t, err) + reader, err := testutil.GetRelationReader( + ctx, + disttaeEngine, + txn, + relation, + nil, + mp, + t, + ) + require.NoError(t, err) + + ret := testutil.EmptyBatchFromSchema(schema, primaryKeyIdx) + cnt := 0 + for { + ok, err := reader.Read(ctx, ret.Attrs, nil, mp, ret) + require.NoError(t, err) + cnt += ret.RowCount() + if ok { + break + } + } + require.NoError(t, err) + + require.Equal(t, rowsCount, cnt) + require.NoError(t, txn.Commit(ctx)) +} diff --git a/pkg/vm/engine/test/testutil/disttae_engine.go b/pkg/vm/engine/test/testutil/disttae_engine.go index 926a8c8060469..145dad3cdf354 100644 --- a/pkg/vm/engine/test/testutil/disttae_engine.go +++ b/pkg/vm/engine/test/testutil/disttae_engine.go @@ -70,6 +70,7 @@ type TestDisttaeEngine struct { mp *mpool.MPool commitWorkspaceThreshold uint64 writeWorkspaceThreshold uint64 + quota uint64 insertEntryMaxCount int rootDir string @@ -134,6 +135,9 @@ func NewTestDisttaeEngine( if de.writeWorkspaceThreshold != 0 { engineOpts = append(engineOpts, disttae.WithWriteWorkspaceThreshold(de.writeWorkspaceThreshold)) } + if de.quota != 0 { + engineOpts = append(engineOpts, disttae.WithExtraWorkspaceThresholdQuota(de.quota)) + } internalExecutorFactory := func() ie.InternalExecutor { return frontend.NewInternalExecutor("") diff --git a/pkg/vm/engine/test/testutil/util.go b/pkg/vm/engine/test/testutil/util.go index d40a3e3697443..6932863b143f6 100644 --- a/pkg/vm/engine/test/testutil/util.go +++ b/pkg/vm/engine/test/testutil/util.go @@ -81,6 +81,11 @@ func WithDisttaeEngineWriteWorkspaceThreshold(v uint64) TestDisttaeEngineOptions e.writeWorkspaceThreshold = v } } +func WithDisttaeEngineQuota(v uint64) TestDisttaeEngineOptions { + return func(e *TestDisttaeEngine) { + e.quota = v + } +} func CreateEngines( ctx context.Context, diff --git a/pkg/vm/engine/test/workspace_test.go b/pkg/vm/engine/test/workspace_test.go index ad22d52d0d7f0..47f5aa654b97f 100644 --- a/pkg/vm/engine/test/workspace_test.go +++ b/pkg/vm/engine/test/workspace_test.go @@ -1442,6 +1442,7 @@ func Test_DeleteUncommittedBlock(t *testing.T) { testutil.WithDisttaeEngineInsertEntryMaxCount(1), testutil.WithDisttaeEngineCommitWorkspaceThreshold(1), testutil.WithDisttaeEngineWriteWorkspaceThreshold(1), + testutil.WithDisttaeEngineQuota(1), ) defer func() { disttaeEngine.Close(ctx) From e098406dbd409cded7ab0ea31cc24e2a1395af5a Mon Sep 17 00:00:00 2001 From: XuPeng-SH Date: Mon, 23 Dec 2024 14:35:50 +0800 Subject: [PATCH 21/26] refactor ickp impl phase 1 (#20866) refactor ickp implementation phase 1 Approved by: @LeftHandCold --- pkg/vm/engine/tae/db/checkpoint/entry.go | 134 ++++++++- pkg/vm/engine/tae/db/checkpoint/flusher.go | 4 +- pkg/vm/engine/tae/db/checkpoint/runner.go | 226 +++++++++++---- .../engine/tae/db/checkpoint/runner_test.go | 272 ++++++++++++++++++ pkg/vm/engine/tae/db/checkpoint/store.go | 244 ++++++++++++++++ pkg/vm/engine/tae/db/checkpoint/testutils.go | 131 +++------ pkg/vm/engine/tae/db/checkpoint/types.go | 5 +- pkg/vm/engine/tae/db/db.go | 13 +- pkg/vm/engine/tae/db/dbutils/error.go | 2 +- pkg/vm/engine/tae/db/test/catalog_test.go | 2 +- pkg/vm/engine/tae/db/test/db_test.go | 16 +- pkg/vm/engine/tae/db/test/replay_test.go | 24 +- pkg/vm/engine/tae/db/testutil/engine.go | 8 +- 13 files changed, 890 insertions(+), 191 deletions(-) diff --git a/pkg/vm/engine/tae/db/checkpoint/entry.go b/pkg/vm/engine/tae/db/checkpoint/entry.go index fc4051f53521c..d14fbdb3060d3 100644 --- a/pkg/vm/engine/tae/db/checkpoint/entry.go +++ b/pkg/vm/engine/tae/db/checkpoint/entry.go @@ -30,6 +30,32 @@ import ( "github.com/matrixorigin/matrixone/pkg/vm/engine/tae/logtail" ) +type Intent interface { + String() string + Wait() <-chan struct{} +} + +type EntryOption func(*CheckpointEntry) + +func WithEndEntryOption(end types.TS) EntryOption { + return func(e *CheckpointEntry) { + e.end = end + } +} + +func WithStateEntryOption(state State) EntryOption { + return func(e *CheckpointEntry) { + e.state = state + } +} + +func WithCheckedEntryOption(policyChecked, flushedChecked bool) EntryOption { + return func(e *CheckpointEntry) { + e.policyChecked = policyChecked + e.flushChecked = flushedChecked + } +} + type CheckpointEntry struct { sync.RWMutex sid string @@ -43,13 +69,20 @@ type CheckpointEntry struct { ckpLSN uint64 truncateLSN uint64 + policyChecked bool + flushChecked bool + // only for new entry logic procedure bornTime time.Time refreshCnt uint32 + + doneC chan struct{} } -func NewCheckpointEntry(sid string, start, end types.TS, typ EntryType) *CheckpointEntry { - return &CheckpointEntry{ +func NewCheckpointEntry( + sid string, start, end types.TS, typ EntryType, opts ...EntryOption, +) *CheckpointEntry { + e := &CheckpointEntry{ sid: sid, start: start, end: end, @@ -57,7 +90,45 @@ func NewCheckpointEntry(sid string, start, end types.TS, typ EntryType) *Checkpo entryType: typ, version: logtail.CheckpointCurrentVersion, bornTime: time.Now(), + doneC: make(chan struct{}), + } + for _, opt := range opts { + opt(e) } + return e +} + +func InheritCheckpointEntry( + from *CheckpointEntry, + replaceOpts ...EntryOption, +) *CheckpointEntry { + from.RLock() + defer from.RUnlock() + e := &CheckpointEntry{ + sid: from.sid, + start: from.start, + end: from.end, + state: from.state, + entryType: from.entryType, + version: from.version, + bornTime: from.bornTime, + refreshCnt: from.refreshCnt, + policyChecked: from.policyChecked, + flushChecked: from.flushChecked, + doneC: from.doneC, + } + for _, opt := range replaceOpts { + opt(e) + } + return e +} + +func (e *CheckpointEntry) Wait() <-chan struct{} { + return e.doneC +} + +func (e *CheckpointEntry) Done() { + close(e.doneC) } // e.start >= o.end @@ -65,6 +136,36 @@ func (e *CheckpointEntry) AllGE(o *CheckpointEntry) bool { return e.start.GE(&o.end) } +func (e *CheckpointEntry) SetPolicyChecked() { + e.Lock() + defer e.Unlock() + e.policyChecked = true +} + +func (e *CheckpointEntry) IsPolicyChecked() bool { + e.RLock() + defer e.RUnlock() + return e.policyChecked +} + +func (e *CheckpointEntry) SetFlushChecked() { + e.Lock() + defer e.Unlock() + e.flushChecked = true +} + +func (e *CheckpointEntry) IsFlushChecked() bool { + e.RLock() + defer e.RUnlock() + return e.flushChecked +} + +func (e *CheckpointEntry) AllChecked() bool { + e.RLock() + defer e.RUnlock() + return e.policyChecked && e.flushChecked +} + func (e *CheckpointEntry) SetVersion(version uint32) { e.Lock() defer e.Unlock() @@ -72,6 +173,8 @@ func (e *CheckpointEntry) SetVersion(version uint32) { } func (e *CheckpointEntry) SetLSN(ckpLSN, truncateLSN uint64) { + e.Lock() + defer e.Unlock() e.ckpLSN = ckpLSN e.truncateLSN = truncateLSN } @@ -85,13 +188,21 @@ func (e *CheckpointEntry) Age() time.Duration { defer e.RUnlock() return time.Since(e.bornTime) } +func (e *CheckpointEntry) ResetAge() { + e.Lock() + defer e.Unlock() + e.bornTime = time.Now() + e.refreshCnt = 0 +} func (e *CheckpointEntry) TooOld() bool { e.RLock() defer e.RUnlock() - return time.Since(e.bornTime) > time.Minute*4*time.Duration(e.refreshCnt+1) + return time.Since(e.bornTime) > time.Minute*3*time.Duration(e.refreshCnt+1) } func (e *CheckpointEntry) LSNString() string { - return fmt.Sprintf("ckp %d, truncate %d", e.ckpLSN, e.truncateLSN) + e.RLock() + defer e.RUnlock() + return fmt.Sprintf("%d-%d", e.ckpLSN, e.truncateLSN) } func (e *CheckpointEntry) LSN() uint64 { @@ -195,7 +306,16 @@ func (e *CheckpointEntry) String() string { t = "G" } state := e.GetState() - return fmt.Sprintf("CKP[%s][%v][%s](%s->%s)", t, state, e.LSNString(), e.start.ToString(), e.end.ToString()) + return fmt.Sprintf( + "CKP[%s][%v][%v:%v][%s](%s->%s)", + t, + state, + e.IsPolicyChecked(), + e.IsFlushChecked(), + e.LSNString(), + e.start.ToString(), + e.end.ToString(), + ) } func (e *CheckpointEntry) Prefetch( @@ -259,7 +379,9 @@ func (e *CheckpointEntry) ReadMetaIdx( return data.ReadTNMetaBatch(ctx, e.version, e.tnLocation, reader) } -func (e *CheckpointEntry) GetByTableID(ctx context.Context, fs *objectio.ObjectFS, tid uint64) (ins, del, dataObject, tombstoneObject *api.Batch, err error) { +func (e *CheckpointEntry) GetTableByID( + ctx context.Context, fs *objectio.ObjectFS, tid uint64, +) (ins, del, dataObject, tombstoneObject *api.Batch, err error) { reader, err := blockio.NewObjectReader(e.sid, fs.Service, e.cnLocation) if err != nil { return diff --git a/pkg/vm/engine/tae/db/checkpoint/flusher.go b/pkg/vm/engine/tae/db/checkpoint/flusher.go index f4c3e1321d0ce..c0db9f5953b80 100644 --- a/pkg/vm/engine/tae/db/checkpoint/flusher.go +++ b/pkg/vm/engine/tae/db/checkpoint/flusher.go @@ -371,8 +371,8 @@ func (flusher *flushImpl) triggerJob(ctx context.Context) { request.tree = entry flusher.flushRequestQ.Enqueue(request) } - _, endTS := entry.GetTimeRange() - flusher.checkpointSchduler.TryScheduleCheckpoint(endTS) + _, ts := entry.GetTimeRange() + flusher.checkpointSchduler.TryScheduleCheckpoint(ts, false) } func (flusher *flushImpl) onFlushRequest(items ...any) { diff --git a/pkg/vm/engine/tae/db/checkpoint/runner.go b/pkg/vm/engine/tae/db/checkpoint/runner.go index 28fdb72db28d2..d3459c6dd7569 100644 --- a/pkg/vm/engine/tae/db/checkpoint/runner.go +++ b/pkg/vm/engine/tae/db/checkpoint/runner.go @@ -22,9 +22,10 @@ import ( "sync/atomic" "time" + "github.com/matrixorigin/matrixone/pkg/common/moerr" + v2 "github.com/matrixorigin/matrixone/pkg/util/metric/v2" "github.com/matrixorigin/matrixone/pkg/vm/engine/tae/logstore/store" - v2 "github.com/matrixorigin/matrixone/pkg/util/metric/v2" "go.uber.org/zap" "github.com/matrixorigin/matrixone/pkg/perfcounter" @@ -231,7 +232,7 @@ func NewRunner( } r.fillDefaults() - r.store = newRunnerStore(r.rt.SID(), r.options.globalVersionInterval) + r.store = newRunnerStore(r.rt.SID(), r.options.globalVersionInterval, time.Minute*2) r.incrementalPolicy = &timeBasedPolicy{interval: r.options.minIncrementalInterval} r.globalPolicy = &countBasedPolicy{minCount: r.options.globalMinCount} @@ -321,11 +322,8 @@ func (r *runner) onGCCheckpointEntries(items ...any) { func (r *runner) onIncrementalCheckpointEntries(items ...any) { now := time.Now() - entry := r.MaxIncrementalCheckpoint() - // In some unit tests, ckp is managed manually, and ckp deletion (CleanPendingCheckpoint) - // can be called when the queue still has unexecuted task. - // Add `entry == nil` here as protective codes - if entry == nil || entry.GetState() != ST_Running { + entry, rollback := r.store.TakeICKPIntent() + if entry == nil { return } var ( @@ -339,7 +337,7 @@ func (r *runner) onIncrementalCheckpointEntries(items ...any) { now = time.Now() logutil.Info( - "Checkpoint-Start", + "ICKP-Execute-Start", zap.String("entry", entry.String()), ) @@ -352,7 +350,7 @@ func (r *runner) onIncrementalCheckpointEntries(items ...any) { logger = logutil.Error } logger( - "Checkpoint-Error", + "ICKP-Execute-Error", zap.String("entry", entry.String()), zap.Error(err), zap.String("phase", errPhase), @@ -364,8 +362,9 @@ func (r *runner) onIncrementalCheckpointEntries(items ...any) { fields = append(fields, zap.Uint64("lsn", lsn)) fields = append(fields, zap.Uint64("reserve", r.options.reservedWALEntryCount)) fields = append(fields, zap.String("entry", entry.String())) + fields = append(fields, zap.Duration("age", entry.Age())) logutil.Info( - "Checkpoint-End", + "ICKP-Execute-End", fields..., ) } @@ -375,6 +374,7 @@ func (r *runner) onIncrementalCheckpointEntries(items ...any) { var file string if fields, files, err = r.doIncrementalCheckpoint(entry); err != nil { errPhase = "do-ckp" + rollback() return } @@ -382,17 +382,28 @@ func (r *runner) onIncrementalCheckpointEntries(items ...any) { if lsn > r.options.reservedWALEntryCount { lsnToTruncate = lsn - r.options.reservedWALEntryCount } + entry.SetLSN(lsn, lsnToTruncate) - entry.SetState(ST_Finished) + if !r.store.CommitICKPIntent(entry, false) { + errPhase = "commit" + rollback() + err = moerr.NewInternalErrorNoCtxf("cannot commit ickp") + return + } + v2.TaskCkpEntryPendingDurationHistogram.Observe(entry.Age().Seconds()) + defer entry.Done() if file, err = r.saveCheckpoint( entry.start, entry.end, lsn, lsnToTruncate, ); err != nil { errPhase = "save-ckp" + rollback() return } + files = append(files, file) + // PXU TODO: if crash here, the checkpoint log entry will be lost var logEntry wal.LogEntry if logEntry, err = r.wal.RangeCheckpoint(1, lsnToTruncate, files...); err != nil { errPhase = "wal-ckp" @@ -571,70 +582,177 @@ func (r *runner) onPostCheckpointEntries(entries ...any) { } } -func (r *runner) tryScheduleIncrementalCheckpoint(start, end types.TS) { - // ts := types.BuildTS(time.Now().UTC().UnixNano(), 0) - _, count := r.source.ScanInRange(start, end) - if count < r.options.minCount { - return +func (r *runner) softScheduleCheckpoint(ts *types.TS) (ret *CheckpointEntry, err error) { + var ( + updated bool + ) + intent := r.store.GetICKPIntent() + + check := func() (done bool) { + if !r.source.IsCommitted(intent.GetStart(), intent.GetEnd()) { + return false + } + tree := r.source.ScanInRangePruned(intent.GetStart(), intent.GetEnd()) + tree.GetTree().Compact() + if !tree.IsEmpty() && intent.TooOld() { + logutil.Warn( + "CheckPoint-Wait-TooOld", + zap.String("entry", intent.String()), + zap.Duration("age", intent.Age()), + ) + intent.DeferRetirement() + } + return tree.IsEmpty() } - entry := NewCheckpointEntry(r.rt.SID(), start, end, ET_Incremental) - r.store.TryAddNewIncrementalCheckpointEntry(entry) -} -func (r *runner) TryScheduleCheckpoint(endts types.TS) { - if r.disabled.Load() { + now := time.Now() + + defer func() { + logger := logutil.Info + if err != nil { + logger = logutil.Error + } else { + ret = intent + } + intentInfo := "nil" + if intent != nil { + intentInfo = intent.String() + } + if (err != nil && err != ErrPendingCheckpoint) || intent.TooOld() { + logger( + "ICKP-Schedule-Soft", + zap.String("intent", intentInfo), + zap.String("ts", ts.ToString()), + zap.Duration("cost", time.Since(now)), + zap.Error(err), + ) + } + }() + + if intent == nil { + start := r.store.GetCheckpointed() + if ts.LT(&start) { + return + } + if !r.incrementalPolicy.Check(start) { + return + } + _, count := r.source.ScanInRange(start, *ts) + if count < r.options.minCount { + return + } + intent, updated = r.store.UpdateICKPIntent(ts, true, false) + if updated { + logutil.Info( + "ICKP-Schedule-Soft-Updated", + zap.String("intent", intent.String()), + zap.String("ts", ts.ToString()), + ) + } + } + + // [intent == nil] + // if intent is nil, it means no need to do checkpoint + if intent == nil { return } - entry := r.MaxIncrementalCheckpoint() - global := r.MaxGlobalCheckpoint() - // no prev checkpoint found. try schedule the first - // checkpoint - if entry == nil { - if global == nil { - r.tryScheduleIncrementalCheckpoint(types.TS{}, endts) + // [intent != nil] + + var ( + policyChecked bool + flushedChecked bool + ) + policyChecked = intent.IsPolicyChecked() + if !policyChecked { + if !r.incrementalPolicy.Check(intent.GetStart()) { return - } else { - maxTS := global.end.Prev() - if r.incrementalPolicy.Check(maxTS) { - r.tryScheduleIncrementalCheckpoint(maxTS.Next(), endts) - } + } + _, count := r.source.ScanInRange(intent.GetStart(), intent.GetEnd()) + if count < r.options.minCount { return } + policyChecked = true } - if entry.IsPendding() { - check := func() (done bool) { - if !r.source.IsCommitted(entry.GetStart(), entry.GetEnd()) { - return false - } - tree := r.source.ScanInRangePruned(entry.GetStart(), entry.GetEnd()) - tree.GetTree().Compact() - if !tree.IsEmpty() && entry.TooOld() { - logutil.Infof("waiting for dirty tree %s", tree.String()) - entry.DeferRetirement() - } - return tree.IsEmpty() - } + flushedChecked = intent.IsFlushChecked() + if !flushedChecked && check() { + flushedChecked = true + } - if !check() { - logutil.Debugf("%s is waiting", entry.String()) - return + if policyChecked != intent.IsPolicyChecked() || flushedChecked != intent.IsFlushChecked() { + endTS := intent.GetEnd() + intent, updated = r.store.UpdateICKPIntent(&endTS, policyChecked, flushedChecked) + + if updated { + logutil.Info( + "ICKP-Schedule-Soft-Updated", + zap.String("intent", intent.String()), + zap.String("endTS", ts.ToString()), + ) } - entry.SetState(ST_Running) - v2.TaskCkpEntryPendingDurationHistogram.Observe(entry.Age().Seconds()) + } + + // no need to do checkpoint + if intent == nil { + return + } + + if intent.end.LT(ts) { + err = ErrPendingCheckpoint r.incrementalCheckpointQueue.Enqueue(struct{}{}) return } - if entry.IsRunning() { + if intent.AllChecked() { r.incrementalCheckpointQueue.Enqueue(struct{}{}) + } + return +} + +// NOTE: +// when `force` is true, it must be called after force flush till the given ts +// force: if true, not to check the validness of the checkpoint +func (r *runner) TryScheduleCheckpoint( + ts types.TS, force bool, +) (ret Intent, err error) { + if r.disabled.Load() { + return + } + if !force { + return r.softScheduleCheckpoint(&ts) + } + + intent, updated := r.store.UpdateICKPIntent(&ts, true, true) + if intent == nil { return } - if r.incrementalPolicy.Check(entry.end) { - r.tryScheduleIncrementalCheckpoint(entry.end.Next(), endts) + now := time.Now() + defer func() { + logger := logutil.Info + if err != nil { + logger = logutil.Error + } + + logger( + "ICKP-Schedule-Force", + zap.String("intent", intent.String()), + zap.String("ts", ts.ToString()), + zap.Bool("updated", updated), + zap.Duration("cost", time.Since(now)), + zap.Error(err), + ) + }() + + r.incrementalCheckpointQueue.Enqueue(struct{}{}) + + if intent.end.LT(&ts) { + err = ErrPendingCheckpoint + return } + + return intent, nil } func (r *runner) fillDefaults() { diff --git a/pkg/vm/engine/tae/db/checkpoint/runner_test.go b/pkg/vm/engine/tae/db/checkpoint/runner_test.go index a58097163dcaf..d9024cf0377bd 100644 --- a/pkg/vm/engine/tae/db/checkpoint/runner_test.go +++ b/pkg/vm/engine/tae/db/checkpoint/runner_test.go @@ -17,7 +17,9 @@ package checkpoint import ( "context" "fmt" + "sync" "testing" + "time" "github.com/matrixorigin/matrixone/pkg/container/types" "github.com/matrixorigin/matrixone/pkg/objectio" @@ -371,3 +373,273 @@ func TestICKPSeekLT(t *testing.T) { } assert.Equal(t, 0, len(ckps)) } + +func Test_RunnerStore1(t *testing.T) { + store := newRunnerStore("", time.Second, time.Second*1000) + _ = types.NextGlobalTsForTest() + _ = types.NextGlobalTsForTest() + t3 := types.NextGlobalTsForTest() + intent, updated := store.UpdateICKPIntent(&t3, true, true) + assert.True(t, updated) + assert.True(t, intent.IsPolicyChecked()) + assert.True(t, intent.IsFlushChecked()) + assert.True(t, intent.IsPendding()) + assert.True(t, intent.AllChecked()) + assert.True(t, intent.end.EQ(&t3)) + + taken, rollback := store.TakeICKPIntent() + assert.NotNil(t, taken) + assert.NotNil(t, rollback) + assert.True(t, taken.IsRunning()) + assert.True(t, taken.end.EQ(&t3)) + + committed := store.CommitICKPIntent(taken, true) + assert.True(t, committed) + + intent, updated = store.UpdateICKPIntent(&t3, true, true) + assert.False(t, updated) + assert.Nilf(t, intent, intent.String()) +} + +func Test_RunnerStore2(t *testing.T) { + store := newRunnerStore("", time.Second, time.Second*1000) + t1 := types.NextGlobalTsForTest() + intent, updated := store.UpdateICKPIntent(&t1, false, false) + assert.True(t, updated) + assert.True(t, intent.start.IsEmpty()) + assert.True(t, intent.end.EQ(&t1)) + assert.True(t, intent.IsPendding()) + assert.False(t, intent.AllChecked()) + + intent, updated = store.UpdateICKPIntent(&t1, true, false) + assert.True(t, updated) + assert.True(t, intent.IsPolicyChecked()) + assert.False(t, intent.IsFlushChecked()) + assert.True(t, intent.IsPendding()) + assert.False(t, intent.AllChecked()) + + intent, updated = store.UpdateICKPIntent(&t1, false, true) + assert.False(t, updated) + assert.True(t, intent.IsPolicyChecked()) + assert.False(t, intent.IsFlushChecked()) + assert.True(t, intent.IsPendding()) + assert.False(t, intent.AllChecked()) + bornTime := intent.bornTime + + intent, updated = store.UpdateICKPIntent(&t1, true, true) + assert.True(t, updated) + assert.True(t, intent.IsPolicyChecked()) + assert.True(t, intent.IsFlushChecked()) + assert.True(t, intent.IsPendding()) + assert.True(t, intent.AllChecked()) + assert.True(t, intent.end.EQ(&t1)) + assert.True(t, bornTime.Equal(intent.bornTime)) + + t2 := types.NextGlobalTsForTest() + intent2, updated := store.UpdateICKPIntent(&t2, true, false) + assert.False(t, updated) + assert.Equal(t, intent, intent2) + intent2, updated = store.UpdateICKPIntent(&t2, false, true) + assert.False(t, updated) + assert.Equal(t, intent, intent2) + + intent2, updated = store.UpdateICKPIntent(&t2, true, true) + assert.True(t, updated) + assert.True(t, intent2.IsPolicyChecked()) + assert.True(t, intent2.IsFlushChecked()) + assert.True(t, intent2.IsPendding()) + assert.True(t, intent2.AllChecked()) + assert.True(t, intent2.end.EQ(&t2)) +} + +func Test_RunnerStore3(t *testing.T) { + store := newRunnerStore("", time.Second, time.Second*1000) + + t1 := types.NextGlobalTsForTest() + intent, updated := store.UpdateICKPIntent(&t1, false, false) + assert.True(t, updated) + assert.True(t, intent.start.IsEmpty()) + assert.True(t, intent.end.EQ(&t1)) + assert.True(t, intent.IsPendding()) + + intent2 := store.incrementalIntent.Load() + assert.Equal(t, intent, intent2) + + t2 := types.NextGlobalTsForTest() + intent3, updated := store.UpdateICKPIntent(&t2, true, true) + assert.True(t, updated) + assert.True(t, intent3.start.IsEmpty()) + assert.True(t, intent3.end.EQ(&t2)) + assert.True(t, intent3.IsPendding()) + intent4 := store.incrementalIntent.Load() + assert.Equal(t, intent3, intent4) + + ii, updated := store.UpdateICKPIntent(&t1, true, true) + assert.False(t, updated) + assert.Equal(t, ii, intent4) + + taken, rollback := store.TakeICKPIntent() + assert.NotNil(t, taken) + assert.NotNil(t, rollback) + assert.True(t, taken.IsRunning()) + intent5 := store.incrementalIntent.Load() + assert.Equal(t, intent5, taken) + + taken2, rollback2 := store.TakeICKPIntent() + assert.Nil(t, taken2) + assert.Nil(t, rollback2) + + t3 := types.NextGlobalTsForTest() + ii2, updated := store.UpdateICKPIntent(&t3, true, true) + assert.False(t, updated) + assert.Equal(t, ii2, intent5) + + rollback() + intent6 := store.incrementalIntent.Load() + assert.True(t, intent6.IsPendding()) + assert.True(t, intent6.end.EQ(&t2)) + assert.True(t, intent6.start.IsEmpty()) + assert.Equal(t, intent6.bornTime, intent5.bornTime) + assert.Equal(t, intent6.refreshCnt, intent5.refreshCnt) + assert.Equal(t, intent6.policyChecked, intent5.policyChecked) + assert.Equal(t, intent6.flushChecked, intent5.flushChecked) + + ii2, updated = store.UpdateICKPIntent(&t3, true, true) + assert.True(t, updated) + assert.True(t, ii2.IsPendding()) + assert.True(t, ii2.end.EQ(&t3)) + assert.True(t, ii2.start.IsEmpty()) + + var wg sync.WaitGroup + wg.Add(1) + go func() { + defer wg.Done() + <-ii2.Wait() + }() + + taken, rollback = store.TakeICKPIntent() + assert.NotNil(t, taken) + assert.NotNil(t, rollback) + assert.True(t, taken.IsRunning()) + intent7 := store.incrementalIntent.Load() + assert.Equal(t, intent7, taken) + + maxEntry := store.MaxIncrementalCheckpoint() + assert.Nil(t, maxEntry) + + committed := store.CommitICKPIntent(taken, true) + assert.True(t, committed) + assert.True(t, taken.IsFinished()) + + wg.Wait() + + maxEntry = store.MaxIncrementalCheckpoint() + assert.Equal(t, maxEntry, taken) + + intent8 := store.incrementalIntent.Load() + assert.Nil(t, intent8) + + // UpdateICKPIntent with a smaller ts than the finished one + intent9, updated := store.UpdateICKPIntent(&t3, true, true) + assert.False(t, updated) + assert.Nil(t, intent9) + + t4 := types.NextGlobalTsForTest() + t4 = t4.Next() + + // UpdateICKPIntent with a larger ts than the finished one + // check if the intent is updated + // check if the start ts is equal to the last end ts + intent10, updated := store.UpdateICKPIntent(&t4, true, true) + assert.True(t, updated) + assert.True(t, intent10.IsPendding()) + assert.True(t, intent10.end.EQ(&t4)) + prev := intent10.start.Prev() + assert.True(t, prev.EQ(&t3)) + + taken2, rollback2 = store.TakeICKPIntent() + assert.NotNil(t, taken2) + assert.NotNil(t, rollback2) + assert.True(t, taken2.IsRunning()) + intent11 := store.incrementalIntent.Load() + assert.Equal(t, intent11, taken2) + + // cannot commit a different intent with the incremental intent + t5 := types.NextGlobalTsForTest() + taken2_1 := InheritCheckpointEntry(taken2, WithEndEntryOption(t5)) + committed = store.CommitICKPIntent(taken2_1, true) + assert.False(t, committed) + + taken2.start = taken2.start.Next() + committed = store.CommitICKPIntent(taken2, true) + assert.False(t, committed) + + taken2.start = taken2.start.Prev() + committed = store.CommitICKPIntent(taken2, true) + assert.True(t, committed) + assert.True(t, taken2.IsFinished()) + + timer := time.After(time.Second * 10) + select { + case <-intent10.Wait(): + case <-timer: + assert.Equal(t, 1, 0) + } +} + +func Test_RunnerStore4(t *testing.T) { + store := newRunnerStore("", time.Second, time.Second*1000) + + t1 := types.NextGlobalTsForTest() + intent, updated := store.UpdateICKPIntent(&t1, true, false) + assert.True(t, updated) + assert.True(t, intent.start.IsEmpty()) + assert.True(t, intent.end.EQ(&t1)) + assert.True(t, intent.IsPendding()) + + t2 := types.NextGlobalTsForTest() + intent2, updated := store.UpdateICKPIntent(&t2, true, true) + assert.True(t, updated) + assert.True(t, intent2.start.IsEmpty()) + assert.True(t, intent2.end.EQ(&t2)) + assert.True(t, intent2.IsPendding()) + assert.True(t, intent2.AllChecked()) + + taken, rollback := store.TakeICKPIntent() + assert.NotNil(t, taken) + assert.NotNil(t, rollback) + + t3 := types.NextGlobalTsForTest() + intent3, updated := store.UpdateICKPIntent(&t3, true, true) + assert.False(t, updated) + assert.True(t, intent3.IsRunning()) + assert.True(t, intent3.end.EQ(&t2)) + + rollback() + intent4 := store.incrementalIntent.Load() + assert.True(t, intent4.IsPendding()) + assert.True(t, intent4.end.EQ(&t2)) + assert.True(t, intent4.start.IsEmpty()) + assert.True(t, intent4.AllChecked()) +} + +func Test_RunnerStore5(t *testing.T) { + store := newRunnerStore("", time.Second, time.Second*1000) + + t1 := types.NextGlobalTsForTest() + t2 := types.NextGlobalTsForTest() + intent, updated := store.UpdateICKPIntent(&t2, true, false) + assert.True(t, updated) + assert.True(t, intent.start.IsEmpty()) + assert.True(t, intent.end.EQ(&t2)) + t.Log(intent.String()) + + intent2, updated := store.UpdateICKPIntent(&t1, true, true) + assert.True(t, updated) + assert.True(t, intent2.end.EQ(&t1)) + assert.True(t, intent2.start.IsEmpty()) + assert.True(t, intent2.IsPendding()) + assert.True(t, intent2.AllChecked()) + assert.True(t, intent2.bornTime.After(intent.bornTime)) + t.Log(intent2.String()) +} diff --git a/pkg/vm/engine/tae/db/checkpoint/store.go b/pkg/vm/engine/tae/db/checkpoint/store.go index d02b2bf9f3780..29b02b9c412d9 100644 --- a/pkg/vm/engine/tae/db/checkpoint/store.go +++ b/pkg/vm/engine/tae/db/checkpoint/store.go @@ -33,10 +33,12 @@ import ( func newRunnerStore( sid string, globalHistoryDuration time.Duration, + intentOldAge time.Duration, ) *runnerStore { s := new(runnerStore) s.sid = sid s.globalHistoryDuration = globalHistoryDuration + s.intentOldAge = intentOldAge s.incrementals = btree.NewBTreeGOptions( func(a, b *CheckpointEntry) bool { return a.end.LT(&b.end) @@ -59,6 +61,9 @@ type runnerStore struct { sid string globalHistoryDuration time.Duration + intentOldAge time.Duration + + incrementalIntent atomic.Pointer[CheckpointEntry] incrementals *btree.BTreeG[*CheckpointEntry] globals *btree.BTreeG[*CheckpointEntry] @@ -71,6 +76,245 @@ type runnerStore struct { gcWatermark atomic.Value } +func (s *runnerStore) GetICKPIntent() *CheckpointEntry { + return s.incrementalIntent.Load() +} + +func (s *runnerStore) GetCheckpointed() types.TS { + s.RLock() + defer s.RUnlock() + return s.GetCheckpointedLocked() +} + +func (s *runnerStore) GetCheckpointedLocked() types.TS { + var ret types.TS + maxICKP, _ := s.incrementals.Max() + maxGCKP, _ := s.globals.Max() + if maxICKP == nil { + // no ickp and no gckp, it's the first ickp + if maxGCKP == nil { + ret = types.TS{} + } else { + ret = maxGCKP.end + } + } else { + ret = maxICKP.end.Next() + } + return ret +} + +// updated: +// true: updated and intent must contain the updated ts +// false: not updated and intent is the old intent +// policyChecked, flushChecked: +// it cannot update the intent if the intent is checked by policy or flush +func (s *runnerStore) UpdateICKPIntent( + ts *types.TS, policyChecked, flushChecked bool, +) (intent *CheckpointEntry, updated bool) { + for { + old := s.incrementalIntent.Load() + // in the case we will decrease the end ts of the old intent + if old != nil && !old.AllChecked() && policyChecked && flushChecked { + checkpointed := s.GetCheckpointed() + // no need to do checkpoint + if checkpointed.GE(ts) { + intent = nil + return + } + newIntent := InheritCheckpointEntry( + old, + WithEndEntryOption(*ts), + WithCheckedEntryOption(policyChecked, flushChecked), + ) + if old.end.GT(ts) { + newIntent.ResetAge() + } + if s.incrementalIntent.CompareAndSwap(old, newIntent) { + intent = newIntent + updated = true + return + } + continue + } + // Scenario 1: + // there is already an intent meets one of the following conditions: + // 1. the range of the old intent contains the ts, no need to update + // 2. the intent is not pendding: Running or Finished, cannot update + if old != nil && (old.end.GT(ts) || !old.IsPendding() || old.Age() > s.intentOldAge) { + intent = old + return + } + + // Here + // 1. old == nil + // 2. old.end <= ts && old.IsPendding() && old.Age() <= s.intentOldAge + + if old != nil { + // if the old intent is checked by policy and the incoming intent is not checked by policy + // incoming vs old: false vs true + // it cannot update the intent in this case + + if !policyChecked && old.IsPolicyChecked() { + intent = old + return + } + if !flushChecked && old.IsFlushChecked() { + intent = old + return + } + } + + var start types.TS + if old != nil { + // Scenario 2: + // there is an pendding intent with smaller end ts. we need to update + // the intent to extend the end ts to the given ts + start = old.start + } else { + // Scenario 3: + // there is no intent, we need to create a new intent + // start-ts: + // 1. if there is no ickp and no gckp, it's the first ickp, start ts is empty + // 2. if there is no ickp but has gckp, start ts is the end ts of the max gckp + // 3. if there is ickp, start ts is the end ts of the max ickp + // end-ts: the given ts + start = s.GetCheckpointed() + } + + if old != nil && old.end.EQ(ts) { + if old.IsPolicyChecked() == policyChecked && old.IsFlushChecked() == flushChecked { + intent = old + return + } + } + + // if the start ts is larger equal to the given ts, no need to update + if start.GE(ts) { + intent = old + return + } + var newIntent *CheckpointEntry + if old == nil { + newIntent = NewCheckpointEntry( + s.sid, + start, + *ts, + ET_Incremental, + WithCheckedEntryOption(policyChecked, flushChecked), + ) + } else { + // the incoming checked status can override the old status + // it is impossible that the old is checked and the incoming is not checked here + // false -> true: impossible here + newIntent = InheritCheckpointEntry( + old, + WithEndEntryOption(*ts), + WithCheckedEntryOption(policyChecked, flushChecked), + ) + } + if s.incrementalIntent.CompareAndSwap(old, newIntent) { + intent = newIntent + updated = true + return + } + } +} + +func (s *runnerStore) TakeICKPIntent() (taken *CheckpointEntry, rollback func()) { + for { + old := s.incrementalIntent.Load() + if old == nil || !old.IsPendding() || !old.AllChecked() { + return + } + taken = InheritCheckpointEntry( + old, + WithStateEntryOption(ST_Running), + ) + if s.incrementalIntent.CompareAndSwap(old, taken) { + rollback = func() { + // rollback the intent + putBack := InheritCheckpointEntry( + taken, + WithStateEntryOption(ST_Pending), + ) + s.incrementalIntent.Store(putBack) + } + break + } + taken = nil + rollback = nil + } + return +} + +// intent must be in Running state +func (s *runnerStore) CommitICKPIntent(intent *CheckpointEntry, done bool) (committed bool) { + defer func() { + if done && committed { + intent.Done() + } + }() + old := s.incrementalIntent.Load() + // should not happen + if old != intent { + logutil.Error( + "CommitICKPIntent-Error", + zap.String("intent", intent.String()), + zap.String("expected", old.String()), + ) + return + } + s.Lock() + defer s.Unlock() + maxICKP, _ := s.incrementals.Max() + maxGCKP, _ := s.globals.Max() + var ( + maxICKPEndNext types.TS + maxGCKPEnd types.TS + ) + if maxICKP != nil { + maxICKPEndNext = maxICKP.end.Next() + } + if maxGCKP != nil { + maxGCKPEnd = maxGCKP.end + } + if maxICKP == nil && maxGCKP == nil { + if !intent.start.IsEmpty() { + logutil.Error( + "CommitICKPIntent-Error", + zap.String("intent", intent.String()), + zap.String("max-i", "nil"), + zap.String("max-g", "nil"), + ) + // PXU TODO: err = xxx + return + } + } else if (maxICKP == nil && !maxGCKPEnd.EQ(&intent.start)) || + (maxICKP != nil && !maxICKPEndNext.EQ(&intent.start)) { + maxi := "nil" + maxg := "nil" + if maxICKP != nil { + maxi = maxICKP.String() + } + if maxGCKP != nil { + maxg = maxGCKP.String() + } + logutil.Error( + "CommitICKPIntent-Error", + zap.String("intent", intent.String()), + zap.String("max-i", maxi), + zap.String("max-g", maxg), + ) + // PXU TODO: err = xxx + return + } + s.incrementalIntent.Store(nil) + intent.SetState(ST_Finished) + s.incrementals.Set(intent) + committed = true + return +} + func (s *runnerStore) ExportStatsLocked() []zap.Field { fields := make([]zap.Field, 0, 8) fields = append(fields, zap.Int("gc-count", s.gcCount)) diff --git a/pkg/vm/engine/tae/db/checkpoint/testutils.go b/pkg/vm/engine/tae/db/checkpoint/testutils.go index 6d08b66f14889..289228826e99a 100644 --- a/pkg/vm/engine/tae/db/checkpoint/testutils.go +++ b/pkg/vm/engine/tae/db/checkpoint/testutils.go @@ -23,7 +23,6 @@ import ( "github.com/matrixorigin/matrixone/pkg/logutil" "github.com/matrixorigin/matrixone/pkg/vm/engine/tae/common" "github.com/matrixorigin/matrixone/pkg/vm/engine/tae/db/dbutils" - "github.com/matrixorigin/matrixone/pkg/vm/engine/tae/wal" "go.uber.org/zap" ) @@ -35,7 +34,7 @@ type TestRunner interface { ForceGlobalCheckpoint(end types.TS, versionInterval time.Duration) error ForceGlobalCheckpointSynchronously(ctx context.Context, end types.TS, versionInterval time.Duration) error ForceCheckpointForBackup(end types.TS) (string, error) - ForceIncrementalCheckpoint(end types.TS, truncate bool) error + ForceIncrementalCheckpoint(end types.TS) error MaxLSNInRange(end types.TS) uint64 GCNeeded() bool @@ -63,7 +62,7 @@ func (r *runner) ForceGlobalCheckpoint(end types.TS, interval time.Duration) err if end2.GE(&end) { r.globalCheckpointQueue.Enqueue(&globalCheckpointContext{ force: true, - end: end, + end: end2, interval: interval, }) return nil @@ -83,6 +82,7 @@ func (r *runner) ForceGlobalCheckpoint(end types.TS, interval time.Duration) err "ForceGlobalCheckpoint-End", zap.Int("retry-time", retryTime), zap.Duration("cost", time.Since(now)), + zap.String("ts", end.ToString()), zap.Error(err), ) }() @@ -93,12 +93,11 @@ func (r *runner) ForceGlobalCheckpoint(end types.TS, interval time.Duration) err case <-timeout: return moerr.NewInternalError(r.ctx, "timeout") default: - err = r.ForceIncrementalCheckpoint(end, false) + err = r.ForceIncrementalCheckpoint(end) if err != nil { - if dbutils.IsRetrieableCheckpoint(err) { + if dbutils.IsCheckpointRetryableErr(err) { retryTime++ - interval := interval.Milliseconds() / 400 - time.Sleep(time.Duration(interval)) + time.Sleep(interval / 20) break } return err @@ -136,104 +135,50 @@ func (r *runner) ForceGlobalCheckpointSynchronously(ctx context.Context, end typ return nil } -func (r *runner) ForceIncrementalCheckpoint(end types.TS, truncate bool) error { - now := time.Now() - prev := r.MaxIncrementalCheckpoint() - if prev != nil && !prev.IsFinished() { - return moerr.NewPrevCheckpointNotFinished() +func (r *runner) ForceIncrementalCheckpoint(ts types.TS) (err error) { + var intent Intent + if intent, err = r.TryScheduleCheckpoint(ts, true); err != nil { + return } - - if prev != nil && end.LE(&prev.end) { - return nil + if intent == nil { + return } - var ( - err error - errPhase string - start types.TS - fatal bool - fields []zap.Field - ) - if prev == nil { - global := r.MaxGlobalCheckpoint() - if global != nil { - start = global.end - } - } else { - start = prev.end.Next() - } + entry := intent.(*CheckpointEntry) - entry := NewCheckpointEntry(r.rt.SID(), start, end, ET_Incremental) - logutil.Info( - "Checkpoint-Start-Force", - zap.String("entry", entry.String()), - ) + if entry.end.LT(&ts) || !entry.AllChecked() { + err = ErrPendingCheckpoint + return + } + // TODO: use context + timeout := time.After(time.Minute * 2) + now := time.Now() defer func() { + logger := logutil.Info if err != nil { - logger := logutil.Error - if fatal { - logger = logutil.Fatal - } - logger( - "Checkpoint-Error-Force", - zap.String("entry", entry.String()), - zap.String("phase", errPhase), - zap.Error(err), - zap.Duration("cost", time.Since(now)), - ) - } else { - fields = append(fields, zap.Duration("cost", time.Since(now))) - fields = append(fields, zap.String("entry", entry.String())) - logutil.Info( - "Checkpoint-End-Force", - fields..., - ) + logger = logutil.Error } + logger( + "ICKP-Schedule-Force-Wait-End", + zap.String("entry", intent.String()), + zap.Duration("cost", time.Since(now)), + zap.Error(err), + ) }() - // TODO: change me - r.store.AddNewIncrementalEntry(entry) + r.incrementalCheckpointQueue.Enqueue(struct{}{}) - var files []string - if fields, files, err = r.doIncrementalCheckpoint(entry); err != nil { - errPhase = "do-ckp" - return err - } - - var lsn, lsnToTruncate uint64 - if truncate { - lsn = r.source.GetMaxLSN(entry.start, entry.end) - if lsn > r.options.reservedWALEntryCount { - lsnToTruncate = lsn - r.options.reservedWALEntryCount - } - entry.ckpLSN = lsn - entry.truncateLSN = lsnToTruncate - } - - var file string - if file, err = r.saveCheckpoint( - entry.start, entry.end, lsn, lsnToTruncate, - ); err != nil { - errPhase = "save-ckp" - return err - } - files = append(files, file) - entry.SetState(ST_Finished) - if truncate { - var e wal.LogEntry - if e, err = r.wal.RangeCheckpoint(1, lsnToTruncate, files...); err != nil { - errPhase = "wal-ckp" - fatal = true - return err - } - if err = e.WaitDone(); err != nil { - errPhase = "wait-wal-ckp" - fatal = true - return err - } + select { + case <-r.ctx.Done(): + err = context.Cause(r.ctx) + return + case <-timeout: + err = moerr.NewInternalErrorNoCtx("timeout") + return + case <-intent.Wait(): } - return nil + return } func (r *runner) ForceCheckpointForBackup(end types.TS) (location string, err error) { diff --git a/pkg/vm/engine/tae/db/checkpoint/types.go b/pkg/vm/engine/tae/db/checkpoint/types.go index 8b42fb90b9da5..b343a76184772 100644 --- a/pkg/vm/engine/tae/db/checkpoint/types.go +++ b/pkg/vm/engine/tae/db/checkpoint/types.go @@ -17,12 +17,15 @@ package checkpoint import ( "context" + "github.com/matrixorigin/matrixone/pkg/common/moerr" "github.com/matrixorigin/matrixone/pkg/container/types" "github.com/matrixorigin/matrixone/pkg/vm/engine/tae/catalog" "github.com/matrixorigin/matrixone/pkg/vm/engine/tae/common" "github.com/matrixorigin/matrixone/pkg/vm/engine/tae/containers" ) +var ErrPendingCheckpoint = moerr.NewPrevCheckpointNotFinished() + type State int8 const ( @@ -41,7 +44,7 @@ const ( ) type CheckpointScheduler interface { - TryScheduleCheckpoint(types.TS) + TryScheduleCheckpoint(types.TS, bool) (Intent, error) } type Runner interface { diff --git a/pkg/vm/engine/tae/db/db.go b/pkg/vm/engine/tae/db/db.go index dc22d6432a0e9..8c136074cb826 100644 --- a/pkg/vm/engine/tae/db/db.go +++ b/pkg/vm/engine/tae/db/db.go @@ -170,10 +170,6 @@ func (db *DB) ForceCheckpoint( ts types.TS, flushDuration time.Duration, ) (err error) { - // FIXME: cannot disable with a running job - db.BGCheckpointRunner.DisableCheckpoint() - defer db.BGCheckpointRunner.EnableCheckpoint() - db.BGCheckpointRunner.CleanPenddingCheckpoint() if flushDuration == 0 { flushDuration = time.Minute * 3 / 2 } @@ -187,7 +183,7 @@ func (db *DB) ForceCheckpoint( logger = logutil.Error } logger( - "Control-Force-Checkpoint", + "ICKP-Control-Force-End", zap.Error(err), zap.Duration("total-cost", time.Since(t0)), zap.String("ts", ts.ToString()), @@ -212,11 +208,10 @@ func (db *DB) ForceCheckpoint( err = moerr.NewInternalError(ctx, "force checkpoint timeout") return default: - err = db.BGCheckpointRunner.ForceIncrementalCheckpoint(ts, true) - if dbutils.IsRetrieableCheckpoint(err) { + err = db.BGCheckpointRunner.ForceIncrementalCheckpoint(ts) + if dbutils.IsCheckpointRetryableErr(err) { db.BGCheckpointRunner.CleanPenddingCheckpoint() - interval := flushDuration.Milliseconds() / 400 - time.Sleep(time.Duration(interval)) + time.Sleep(flushDuration / 20) break } return diff --git a/pkg/vm/engine/tae/db/dbutils/error.go b/pkg/vm/engine/tae/db/dbutils/error.go index 1c148cf4edc02..92e615217ea56 100644 --- a/pkg/vm/engine/tae/db/dbutils/error.go +++ b/pkg/vm/engine/tae/db/dbutils/error.go @@ -18,6 +18,6 @@ import ( "github.com/matrixorigin/matrixone/pkg/common/moerr" ) -func IsRetrieableCheckpoint(err error) bool { +func IsCheckpointRetryableErr(err error) bool { return moerr.IsMoErrCode(err, moerr.ErrPrevCheckpointNotFinished) } diff --git a/pkg/vm/engine/tae/db/test/catalog_test.go b/pkg/vm/engine/tae/db/test/catalog_test.go index d61ca4d9a4a4f..056273b9893c3 100644 --- a/pkg/vm/engine/tae/db/test/catalog_test.go +++ b/pkg/vm/engine/tae/db/test/catalog_test.go @@ -185,7 +185,7 @@ func TestCheckpointCatalog2(t *testing.T) { } wg.Wait() ts := types.BuildTS(time.Now().UTC().UnixNano(), 0) - err = tae.BGCheckpointRunner.ForceIncrementalCheckpoint(ts, false) + err = tae.BGCheckpointRunner.ForceIncrementalCheckpoint(ts) assert.NoError(t, err) lsn := tae.BGCheckpointRunner.MaxLSNInRange(ts) entry, err := tae.Wal.RangeCheckpoint(1, lsn) diff --git a/pkg/vm/engine/tae/db/test/db_test.go b/pkg/vm/engine/tae/db/test/db_test.go index 439c75bb0f49a..6f869696de8d4 100644 --- a/pkg/vm/engine/tae/db/test/db_test.go +++ b/pkg/vm/engine/tae/db/test/db_test.go @@ -2710,14 +2710,14 @@ func TestSegDelLogtail(t *testing.T) { testutil.CompactBlocks(t, 0, tae.DB, pkgcatalog.MO_CATALOG, catalog.SystemDBSchema, false) testutil.CompactBlocks(t, 0, tae.DB, pkgcatalog.MO_CATALOG, catalog.SystemTableSchema, false) testutil.CompactBlocks(t, 0, tae.DB, pkgcatalog.MO_CATALOG, catalog.SystemColumnSchema, false) - err = tae.BGCheckpointRunner.ForceIncrementalCheckpoint(tae.TxnMgr.Now(), false) + err = tae.BGCheckpointRunner.ForceIncrementalCheckpoint(tae.TxnMgr.Now()) assert.NoError(t, err) check := func() { ckpEntries := tae.BGCheckpointRunner.GetAllIncrementalCheckpoints() assert.Equal(t, 1, len(ckpEntries)) entry := ckpEntries[0] - ins, del, dataObj, tombstoneObj, err := entry.GetByTableID(context.Background(), tae.Runtime.Fs, tid) + ins, del, dataObj, tombstoneObj, err := entry.GetTableByID(context.Background(), tae.Runtime.Fs, tid) assert.NoError(t, err) assert.Nil(t, ins) // 0 ins assert.Nil(t, del) // 0 del @@ -3687,7 +3687,7 @@ func TestDropCreated3(t *testing.T) { testutil.CompactBlocks(t, 0, tae.DB, pkgcatalog.MO_CATALOG, catalog.SystemDBSchema, false) testutil.CompactBlocks(t, 0, tae.DB, pkgcatalog.MO_CATALOG, catalog.SystemTableSchema, false) testutil.CompactBlocks(t, 0, tae.DB, pkgcatalog.MO_CATALOG, catalog.SystemColumnSchema, false) - err = tae.BGCheckpointRunner.ForceIncrementalCheckpoint(tae.TxnMgr.Now(), false) + err = tae.BGCheckpointRunner.ForceIncrementalCheckpoint(tae.TxnMgr.Now()) assert.Nil(t, err) tae.Restart(ctx) @@ -3744,7 +3744,7 @@ func TestDropCreated4(t *testing.T) { assert.Nil(t, err) assert.Nil(t, txn.Commit(context.Background())) - err = tae.BGCheckpointRunner.ForceIncrementalCheckpoint(tae.TxnMgr.Now(), false) + err = tae.BGCheckpointRunner.ForceIncrementalCheckpoint(tae.TxnMgr.Now()) assert.Nil(t, err) tae.Restart(ctx) @@ -4628,7 +4628,7 @@ func TestReadCheckpoint(t *testing.T) { } for _, entry := range entries { for _, tid := range tids { - ins, del, _, _, err := entry.GetByTableID(context.Background(), tae.Runtime.Fs, tid) + ins, del, _, _, err := entry.GetTableByID(context.Background(), tae.Runtime.Fs, tid) assert.NoError(t, err) t.Logf("table %d", tid) if ins != nil { @@ -4644,7 +4644,7 @@ func TestReadCheckpoint(t *testing.T) { entries = tae.BGCheckpointRunner.GetAllGlobalCheckpoints() entry := entries[len(entries)-1] for _, tid := range tids { - ins, del, _, _, err := entry.GetByTableID(context.Background(), tae.Runtime.Fs, tid) + ins, del, _, _, err := entry.GetTableByID(context.Background(), tae.Runtime.Fs, tid) assert.NoError(t, err) t.Logf("table %d", tid) if ins != nil { @@ -8033,7 +8033,7 @@ func TestForceCheckpoint(t *testing.T) { err = tae.BGFlusher.ForceFlushWithInterval(tae.TxnMgr.Now(), context.Background(), time.Second*2, time.Millisecond*10) assert.Error(t, err) - err = tae.BGCheckpointRunner.ForceIncrementalCheckpoint(tae.TxnMgr.Now(), false) + err = tae.BGCheckpointRunner.ForceIncrementalCheckpoint(tae.TxnMgr.Now()) assert.NoError(t, err) } @@ -8129,7 +8129,7 @@ func TestMarshalPartioned(t *testing.T) { testutil.CompactBlocks(t, 0, tae.DB, pkgcatalog.MO_CATALOG, catalog.SystemDBSchema, false) testutil.CompactBlocks(t, 0, tae.DB, pkgcatalog.MO_CATALOG, catalog.SystemTableSchema, false) testutil.CompactBlocks(t, 0, tae.DB, pkgcatalog.MO_CATALOG, catalog.SystemColumnSchema, false) - err := tae.BGCheckpointRunner.ForceIncrementalCheckpoint(tae.TxnMgr.Now(), false) + err := tae.BGCheckpointRunner.ForceIncrementalCheckpoint(tae.TxnMgr.Now()) assert.NoError(t, err) lsn := tae.BGCheckpointRunner.MaxLSNInRange(tae.TxnMgr.Now()) entry, err := tae.Wal.RangeCheckpoint(1, lsn) diff --git a/pkg/vm/engine/tae/db/test/replay_test.go b/pkg/vm/engine/tae/db/test/replay_test.go index 1bae1ed51faaa..edfa793aab01d 100644 --- a/pkg/vm/engine/tae/db/test/replay_test.go +++ b/pkg/vm/engine/tae/db/test/replay_test.go @@ -87,7 +87,7 @@ func TestReplayCatalog1(t *testing.T) { testutil.CompactBlocks(t, 0, tae, pkgcatalog.MO_CATALOG, catalog.SystemDBSchema, false) testutil.CompactBlocks(t, 0, tae, pkgcatalog.MO_CATALOG, catalog.SystemTableSchema, false) testutil.CompactBlocks(t, 0, tae, pkgcatalog.MO_CATALOG, catalog.SystemColumnSchema, false) - err := tae.BGCheckpointRunner.ForceIncrementalCheckpoint(tae.TxnMgr.Now(), false) + err := tae.BGCheckpointRunner.ForceIncrementalCheckpoint(tae.TxnMgr.Now()) assert.NoError(t, err) } } @@ -186,7 +186,7 @@ func TestReplayCatalog2(t *testing.T) { assert.Nil(t, err) assert.Nil(t, txn.Commit(context.Background())) t.Log(tae.Catalog.SimplePPString(common.PPL1)) - err = tae.BGCheckpointRunner.ForceIncrementalCheckpoint(tae.TxnMgr.Now(), false) + err = tae.BGCheckpointRunner.ForceIncrementalCheckpoint(tae.TxnMgr.Now()) assert.NoError(t, err) tae.Close() @@ -461,7 +461,7 @@ func TestReplay2(t *testing.T) { err = tae2.ForceFlush(tae2.TxnMgr.Now(), context.Background(), time.Second*10) assert.NoError(t, err) - err = tae2.BGCheckpointRunner.ForceIncrementalCheckpoint(tae2.TxnMgr.Now(), false) + err = tae2.BGCheckpointRunner.ForceIncrementalCheckpoint(tae2.TxnMgr.Now()) assert.NoError(t, err) txn, rel = testutil.GetRelation(t, 0, tae2, "db", schema.Name) @@ -562,7 +562,7 @@ func TestReplay3(t *testing.T) { assert.NoError(t, txn.Commit(context.Background())) txn, _ = tae.GetRelation() - err = tae.BGCheckpointRunner.ForceIncrementalCheckpoint(tae.TxnMgr.Now(), false) + err = tae.BGCheckpointRunner.ForceIncrementalCheckpoint(tae.TxnMgr.Now()) assert.NoError(t, err) assert.NoError(t, txn.Commit(context.Background())) } @@ -816,7 +816,7 @@ func TestReplay5(t *testing.T) { testutil.CompactBlocks(t, 0, tae, testutil.DefaultTestDB, schema, false) err = tae.BGFlusher.ForceFlushWithInterval(tae.TxnMgr.Now(), context.Background(), time.Second*2, time.Millisecond*10) assert.NoError(t, err) - err = tae.BGCheckpointRunner.ForceIncrementalCheckpoint(tae.TxnMgr.Now(), false) + err = tae.BGCheckpointRunner.ForceIncrementalCheckpoint(tae.TxnMgr.Now()) assert.NoError(t, err) lsn := tae.BGCheckpointRunner.MaxLSNInRange(tae.TxnMgr.Now()) entry, err := tae.Wal.RangeCheckpoint(1, lsn) @@ -843,7 +843,7 @@ func TestReplay5(t *testing.T) { testutil.CompactBlocks(t, 0, tae, testutil.DefaultTestDB, schema, false) err = tae.BGFlusher.ForceFlushWithInterval(tae.TxnMgr.Now(), context.Background(), time.Second*2, time.Millisecond*10) assert.NoError(t, err) - err = tae.BGCheckpointRunner.ForceIncrementalCheckpoint(tae.TxnMgr.Now(), false) + err = tae.BGCheckpointRunner.ForceIncrementalCheckpoint(tae.TxnMgr.Now()) assert.NoError(t, err) lsn = tae.BGCheckpointRunner.MaxLSNInRange(tae.TxnMgr.Now()) entry, err = tae.Wal.RangeCheckpoint(1, lsn) @@ -880,7 +880,7 @@ func TestReplay5(t *testing.T) { err = tae.BGFlusher.ForceFlushWithInterval(tae.TxnMgr.Now(), context.Background(), time.Second*2, time.Millisecond*10) assert.NoError(t, err) - err = tae.BGCheckpointRunner.ForceIncrementalCheckpoint(tae.TxnMgr.Now(), false) + err = tae.BGCheckpointRunner.ForceIncrementalCheckpoint(tae.TxnMgr.Now()) assert.NoError(t, err) lsn = tae.BGCheckpointRunner.MaxLSNInRange(tae.TxnMgr.Now()) entry, err = tae.Wal.RangeCheckpoint(1, lsn) @@ -941,7 +941,7 @@ func TestReplay6(t *testing.T) { testutil.MergeBlocks(t, 0, tae, testutil.DefaultTestDB, schema, false) err = tae.BGFlusher.ForceFlushWithInterval(tae.TxnMgr.Now(), context.Background(), time.Second*2, time.Millisecond*10) assert.NoError(t, err) - err = tae.BGCheckpointRunner.ForceIncrementalCheckpoint(tae.TxnMgr.Now(), false) + err = tae.BGCheckpointRunner.ForceIncrementalCheckpoint(tae.TxnMgr.Now()) assert.NoError(t, err) _ = tae.Close() @@ -1121,7 +1121,7 @@ func TestReplay8(t *testing.T) { _ = txn.Rollback(context.Background()) tae.CompactBlocks(false) - err = tae.BGCheckpointRunner.ForceIncrementalCheckpoint(tae.TxnMgr.Now(), false) + err = tae.BGCheckpointRunner.ForceIncrementalCheckpoint(tae.TxnMgr.Now()) assert.NoError(t, err) tae.Restart(ctx) @@ -1322,7 +1322,7 @@ func TestReplaySnapshots(t *testing.T) { assert.NoError(t, err) assert.NoError(t, txn.Commit(context.Background())) - err = tae.BGCheckpointRunner.ForceIncrementalCheckpoint(tae.TxnMgr.Now(), false) + err = tae.BGCheckpointRunner.ForceIncrementalCheckpoint(tae.TxnMgr.Now()) assert.NoError(t, err) txn, err = tae.StartTxn(nil) @@ -1346,7 +1346,7 @@ func TestReplaySnapshots(t *testing.T) { assert.False(t, baseNode.IsEmpty()) assert.NoError(t, txn.Commit(context.Background())) - err = tae.BGCheckpointRunner.ForceIncrementalCheckpoint(tae.TxnMgr.Now(), false) + err = tae.BGCheckpointRunner.ForceIncrementalCheckpoint(tae.TxnMgr.Now()) assert.NoError(t, err) t.Log(tae.Catalog.SimplePPString(3)) @@ -1382,7 +1382,7 @@ func TestReplayDatabaseEntry(t *testing.T) { assert.Equal(t, createSqlStr, dbEntry.GetCreateSql()) testutil.CompactBlocks(t, 0, tae.DB, pkgcatalog.MO_CATALOG, catalog.SystemDBSchema, false) - err = tae.BGCheckpointRunner.ForceIncrementalCheckpoint(tae.TxnMgr.Now(), false) + err = tae.BGCheckpointRunner.ForceIncrementalCheckpoint(tae.TxnMgr.Now()) assert.NoError(t, err) tae.Restart(ctx) diff --git a/pkg/vm/engine/tae/db/testutil/engine.go b/pkg/vm/engine/tae/db/testutil/engine.go index c422d9538fd9a..10b747e00f3fe 100644 --- a/pkg/vm/engine/tae/db/testutil/engine.go +++ b/pkg/vm/engine/tae/db/testutil/engine.go @@ -164,21 +164,21 @@ func (e *TestEngine) CheckRowsByScan(exp int, applyDelete bool) { func (e *TestEngine) ForceCheckpoint() { err := e.BGFlusher.ForceFlushWithInterval(e.TxnMgr.Now(), context.Background(), time.Second*2, time.Millisecond*10) assert.NoError(e.T, err) - err = e.BGCheckpointRunner.ForceIncrementalCheckpoint(e.TxnMgr.Now(), false) + err = e.BGCheckpointRunner.ForceIncrementalCheckpoint(e.TxnMgr.Now()) assert.NoError(e.T, err) } func (e *TestEngine) ForceLongCheckpoint() { err := e.BGFlusher.ForceFlush(e.TxnMgr.Now(), context.Background(), 20*time.Second) assert.NoError(e.T, err) - err = e.BGCheckpointRunner.ForceIncrementalCheckpoint(e.TxnMgr.Now(), false) + err = e.BGCheckpointRunner.ForceIncrementalCheckpoint(e.TxnMgr.Now()) assert.NoError(e.T, err) } func (e *TestEngine) ForceLongCheckpointTruncate() { err := e.BGFlusher.ForceFlush(e.TxnMgr.Now(), context.Background(), 20*time.Second) assert.NoError(e.T, err) - err = e.BGCheckpointRunner.ForceIncrementalCheckpoint(e.TxnMgr.Now(), true) + err = e.BGCheckpointRunner.ForceIncrementalCheckpoint(e.TxnMgr.Now()) assert.NoError(e.T, err) } @@ -309,7 +309,7 @@ func (e *TestEngine) IncrementalCheckpoint( flushed := e.DB.BGFlusher.IsAllChangesFlushed(types.TS{}, end, true) require.True(e.T, flushed) } - err := e.DB.BGCheckpointRunner.ForceIncrementalCheckpoint(end, false) + err := e.DB.BGCheckpointRunner.ForceIncrementalCheckpoint(end) require.NoError(e.T, err) if truncate { lsn := e.DB.BGCheckpointRunner.MaxLSNInRange(end) From 368fa482f3032411863f5378e137305f885aa1db Mon Sep 17 00:00:00 2001 From: ou yuanning <45346669+ouyuanning@users.noreply.github.com> Date: Mon, 23 Dec 2024 16:24:29 +0800 Subject: [PATCH 22/26] change code owner (#20874) --- CODEOWNERS | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/CODEOWNERS b/CODEOWNERS index 907b53965ed40..d43b81aa96375 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -51,9 +51,9 @@ # pkg/common @fengttt /pkg/common @zhangxu19830126 -/pkg/common/mpool @m-schen +/pkg/common/mpool @reusee @XuPeng-SH /pkg/common/malloc @reusee -/pkg/common/spool @reusee @m-schen +/pkg/common/spool @reusee @ouyuanning @aunjgr @badboynt1 /pkg/common/async @zhangxu19830126 /pkg/common/bitmap @aunjgr /pkg/common/hashmap @aunjgr @@ -77,7 +77,7 @@ # pkg/container /pkg/container @XuPeng-SH /pkg/container/vector @aunjgr @XuPeng-SH -/pkg/container/pSpool @m-schen +/pkg/container/pSpool @reusee @aunjgr @ouyuanning @badboynt1 # pkg/defines /pkg/defines @daviszhen @@ -116,7 +116,7 @@ /pkg/objectio @LeftHandCold @XuPeng-SH # pkg/partition -/pkg/partition @aunjgr @m-schen +/pkg/partition @aunjgr @iamlinjunhong # folders under pkg/pb contain code generated by pb, owners will be responsible # for compatibility issues required for rolling upgrade. @@ -124,7 +124,7 @@ /pkg/pb/logservice @zhangxu19830126 /pkg/pb/metadata @zhangxu19830126 /pkg/pb/metric @aptend -/pkg/pb/pipeline @m-schen +/pkg/pb/pipeline @badboynt1 @ouyuanning @aunjgr /pkg/pb/plan @ouyuanning @aunjgr /pkg/pb/timestamp @zhangxu19830126 /pkg/pb/txn @zhangxu19830126 @@ -133,19 +133,19 @@ /pkg/perfcounter @reusee # pkg/sort -/pkg/sort @aunjgr @m-schen +/pkg/sort @aunjgr @badboynt1 # pkg/bootstrap /pkg/bootstrap @daviszhen @zhangxu19830126 @qingxinhome @LeftHandCold # pkg/sql /pkg/sql @aunjgr -/pkg/sql/colexec @m-schen -/pkg/sql/compile @m-schen @ouyuanning @aunjgr @badboynt1 @qingxinhome +/pkg/sql/colexec @badboynt1 @ouyuanning @aunjgr +/pkg/sql/compile @ouyuanning @aunjgr @badboynt1 @qingxinhome /pkg/sql/models @qingxinhome /pkg/sql/parsers @iamlinjunhong /pkg/sql/plan @ouyuanning @aunjgr @badboynt1 -/pkg/sql/plan/function @m-schen +/pkg/sql/plan/function @ouyuanning @qingxinhome @aujngr /pkg/sql/plan/explain @ouyuanning @aunjgr @badboynt1 @qingxinhome /pkg/sql/plan/tools @daviszhen @qingxinhome /pkg/sql/colexec/indexjoin @aunjgr @badboynt1 @@ -181,7 +181,7 @@ /pkg/tests/txn @zhangxu19830126 # pkg/testutil -/pkg/testutil @m-schen +/pkg/testutil @ouyuanning @qingxinhome @daviszhen # pkg/txn /pkg/txn @zhangxu19830126 @@ -203,8 +203,8 @@ # engines and engine related stuff /pkg/vm @XuPeng-SH /pkg/vm/engine @XuPeng-SH -/pkg/vm/pipeline @m-schen -/pkg/vm/process @m-schen @aunjgr +/pkg/vm/pipeline @ouyuanning @badboynt1 @aunjgr +/pkg/vm/process @reusee @aunjgr @badboynt1 @XuPeng-SH /pkg/vm/message @badboynt1 /pkg/vm/engine/disttae @XuPeng-SH @triump2020 /pkg/vm/engine/disttae/logtailreplay @triump2020 @XuPeng-SH @@ -230,7 +230,7 @@ /proto/logservice.proto @zhangxu19830126 /proto/metadata.proto @zhangxu19830126 /proto/metric.proto @aptend -/proto/pipeline.proto @m-schen +/proto/pipeline.proto @ouyuanning @aunjgr @badboynt1 /proto/plan.proto @aunjgr /proto/timestamp.proto @zhangxu19830126 /proto/txn.proto @zhangxu19830126 From 1774c8f5e4144f3ce50f14e9328513de8543b8ab Mon Sep 17 00:00:00 2001 From: YANGGMM Date: Mon, 23 Dec 2024 17:25:48 +0800 Subject: [PATCH 23/26] support restore database or table by database or table level snapshot (#20869) support restore database or table by database or table level snapshot Approved by: @heni02, @daviszhen --- pkg/frontend/snapshot.go | 29 ++- .../restore_database_using_cluster_sp.result | 17 +- .../cases/snapshot/restore_fk_table.result | 5 +- .../cases/snapshot/restore_fk_table.sql | 1 + .../snapshot_database_level_restore.result | 228 ++++++++++++++++++ .../snapshot_database_level_restore.sql | 197 +++++++++++++++ .../snapshot_restore_database_level.result | 4 + .../snapshot_restore_database_level.sql | 2 + .../snapshot_restore_table_level.result | 2 + .../snapshot/snapshot_restore_table_level.sql | 1 + ...snapshot_restore_to_table_partition.result | 1 + .../sys_restore_view_to_nonsys_account.result | 16 +- .../sys_restore_view_to_nonsys_account.sql | 2 + 13 files changed, 493 insertions(+), 12 deletions(-) create mode 100644 test/distributed/cases/snapshot/snapshot_database_level_restore.result create mode 100644 test/distributed/cases/snapshot/snapshot_database_level_restore.sql diff --git a/pkg/frontend/snapshot.go b/pkg/frontend/snapshot.go index 24a1eb8d41e44..0a655278a4732 100644 --- a/pkg/frontend/snapshot.go +++ b/pkg/frontend/snapshot.go @@ -669,6 +669,9 @@ func checkRestorePriv(ctx context.Context, ses *Session, snapshot *snapshotRecor return moerr.NewInternalError(ctx, "non-sys account's snapshot can't restore to sys account") } } + if snapshot.level == tree.RESTORELEVELDATABASE.String() || snapshot.level == tree.RESTORELEVELTABLE.String() { + return moerr.NewInternalError(ctx, "can't restore account from db or table level snapshot") + } case tree.RESTORELEVELDATABASE: dbname := string(stmt.DatabaseName) if len(dbname) > 0 && needSkipDb(dbname) { @@ -678,6 +681,15 @@ func checkRestorePriv(ctx context.Context, ses *Session, snapshot *snapshotRecor if snapshot.level == tree.RESTORELEVELCLUSTER.String() { return moerr.NewInternalError(ctx, "can't restore db from cluster level snapshot") } + if snapshot.level == tree.RESTORELEVELTABLE.String() { + return moerr.NewInternalError(ctx, "can't restore db from table level snapshot") + } + if string(stmt.AccountName) != ses.GetTenantInfo().GetTenant() { + return moerr.NewInternalError(ctx, "can't restore database from other account's snapshot") + } + if snapshot.level == tree.RESTORELEVELDATABASE.String() && snapshot.databaseName != string(stmt.DatabaseName) { + return moerr.NewInternalErrorf(ctx, "databaseName(%v) does not match snapshot.databaseName(%v)", string(stmt.DatabaseName), snapshot.databaseName) + } case tree.RESTORELEVELTABLE: dbname := string(stmt.DatabaseName) if len(dbname) > 0 && needSkipDb(dbname) { @@ -686,6 +698,14 @@ func checkRestorePriv(ctx context.Context, ses *Session, snapshot *snapshotRecor if snapshot.level == tree.RESTORELEVELCLUSTER.String() { return moerr.NewInternalError(ctx, "can't restore db from cluster level snapshot") } + if string(stmt.AccountName) != ses.GetTenantInfo().GetTenant() { + return moerr.NewInternalError(ctx, "can't restore table from other account's snapshot") + } + if snapshot.level == tree.RESTORELEVELTABLE.String() { + if snapshot.databaseName != string(stmt.DatabaseName) || snapshot.tableName != string(stmt.TableName) { + return moerr.NewInternalErrorf(ctx, "tableName(%v) does not match snapshot.tableName(%v)", string(stmt.TableName), snapshot.tableName) + } + } default: return moerr.NewInternalErrorf(ctx, "unknown restore level: %v", restoreLevel) } @@ -1473,7 +1493,14 @@ func doResolveSnapshotWithSnapshotName(ctx context.Context, ses FeSession, snaps var accountId uint32 // cluster level record has no accountName, so accountId is 0 if len(record.accountName) != 0 { - accountId = uint32(record.objId) + if record.level == tree.RESTORELEVELACCOUNT.String() { + accountId = uint32(record.objId) + } else { + accountId, err = defines.GetAccountId(ctx) + if err != nil { + return + } + } } return &pbplan.Snapshot{ diff --git a/test/distributed/cases/snapshot/cluster/restore_database_using_cluster_sp.result b/test/distributed/cases/snapshot/cluster/restore_database_using_cluster_sp.result index d5f5d8b56a756..7f804135b959a 100644 --- a/test/distributed/cases/snapshot/cluster/restore_database_using_cluster_sp.result +++ b/test/distributed/cases/snapshot/cluster/restore_database_using_cluster_sp.result @@ -128,7 +128,7 @@ SNAPSHOT_NAME TIMESTAMP SNAPSHOT_LEVEL ACCOUNT_NAME DATABASE_NAME create snapshot cluster_sp for cluster; show snapshots; SNAPSHOT_NAME TIMESTAMP SNAPSHOT_LEVEL ACCOUNT_NAME DATABASE_NAME TABLE_NAME -cluster_sp 2024-06-24 08:47:05.296059 cluster +cluster_sp 2024-12-23 03:02:41.03828 cluster drop database if exists snapshot_read; drop database if exists test_snapshot_restore; restore account acc01 database snapshot_read from snapshot cluster_sp; @@ -478,7 +478,7 @@ SNAPSHOT_NAME TIMESTAMP SNAPSHOT_LEVEL ACCOUNT_NAME DATABASE_NAME create snapshot cluster_sp for cluster; show snapshots; SNAPSHOT_NAME TIMESTAMP SNAPSHOT_LEVEL ACCOUNT_NAME DATABASE_NAME TABLE_NAME -cluster_sp 2024-10-21 06:37:13.526895 cluster +cluster_sp 2024-12-23 03:02:42.498253 cluster drop database if exists Payroll; drop database if exists Projects; drop database if exists Company; @@ -928,13 +928,16 @@ SNAPSHOT_NAME TIMESTAMP SNAPSHOT_LEVEL ACCOUNT_NAME DATABASE_NAME create snapshot account_sp for account acc01; show snapshots; SNAPSHOT_NAME TIMESTAMP SNAPSHOT_LEVEL ACCOUNT_NAME DATABASE_NAME TABLE_NAME -account_sp 2024-10-21 06:37:16.701327 account acc01 +account_sp 2024-12-23 03:02:44.173044 account acc01 drop database if exists Payroll; drop database if exists Projects; drop database if exists Company; restore account acc01 database Company from snapshot account_sp; +internal error: can't restore database from other account's snapshot restore account acc01 database Projects from snapshot account_sp; +internal error: can't restore database from other account's snapshot restore account acc01 database Payroll from snapshot account_sp; +internal error: can't restore database from other account's snapshot restore account acc01 from snapshot account_sp; select * from Company.Departments; departmentid name managerid @@ -1293,10 +1296,10 @@ Dr. Charlie Computer Science Introduction to Computer Science Fall 2024 Dr. Delta Mathematics Advanced Mathematics Spring 2024 select * from StudentOverallPerformance; studentname studentgrade averagegrade -Eve null 3.50000000 -Frank null 3.60000000 Alice Smith 3.50 3.60000000 Bob Johnson 3.70 3.60000000 +Eve null 3.50000000 +Frank null 3.60000000 drop snapshot if exists cluster_sp; show snapshots; SNAPSHOT_NAME TIMESTAMP SNAPSHOT_LEVEL ACCOUNT_NAME DATABASE_NAME TABLE_NAME @@ -1379,11 +1382,11 @@ Eve 3.50000000 Frank 3.60000000 select * from EducationSystem.ComprehensiveStudentCourseInfo; studentname studentgrade coursetitle coursegrade -Eve null Introduction to Computer Science 3.50 -Frank null Advanced Mathematics 3.60 Alice Smith 3.50 Calculus 3.50 Bob Johnson 3.70 Calculus 3.60 Alice Smith 3.50 Physics 3.70 +Eve null Introduction to Computer Science 3.50 +Frank null Advanced Mathematics 3.60 select * from EducationSystem.ComprehensiveEducatorInfo; professorname departmentname coursetitle semester Dr. Charlie Computer Science Introduction to Computer Science Fall 2024 diff --git a/test/distributed/cases/snapshot/restore_fk_table.result b/test/distributed/cases/snapshot/restore_fk_table.result index 5753128a5682b..4894e60f5c553 100644 --- a/test/distributed/cases/snapshot/restore_fk_table.result +++ b/test/distributed/cases/snapshot/restore_fk_table.result @@ -386,7 +386,8 @@ insert into f1 values (4,600); use test07; insert into c1 values (2,9); restore account acc01 database test06 table f1 from snapshot sp06; -Unknown database test06 +internal error: can't restore table from other account's snapshot +restore account acc01 from snapshot sp06; use test06; show tables; Tables_in_test06 @@ -489,4 +490,4 @@ drop snapshot sp08; drop database test08; drop account acc01; drop account acc02; -drop account acc03; \ No newline at end of file +drop account acc03; diff --git a/test/distributed/cases/snapshot/restore_fk_table.sql b/test/distributed/cases/snapshot/restore_fk_table.sql index c3bd6ab468cf4..a14f2f82caf06 100644 --- a/test/distributed/cases/snapshot/restore_fk_table.sql +++ b/test/distributed/cases/snapshot/restore_fk_table.sql @@ -351,6 +351,7 @@ use test07; insert into c1 values (2,9); restore account acc01 database test06 table f1 from snapshot sp06; +restore account acc01 from snapshot sp06; use test06; show tables; diff --git a/test/distributed/cases/snapshot/snapshot_database_level_restore.result b/test/distributed/cases/snapshot/snapshot_database_level_restore.result new file mode 100644 index 0000000000000..77baba2b4a0a3 --- /dev/null +++ b/test/distributed/cases/snapshot/snapshot_database_level_restore.result @@ -0,0 +1,228 @@ +drop database if exists snapshot_read; +create database if not exists snapshot_read; +use snapshot_read; +create table test_snapshot_read (a int); +INSERT INTO test_snapshot_read (a) VALUES(1), (2), (3), (4), (5),(6), (7), (8), (9), (10), (11), (12),(13), (14), (15), (16), (17), (18), (19), (20),(21), (22), (23), (24), (25), (26), (27), (28), (29), (30),(31), (32), (33), (34), (35), (36), (37), (38), (39), (40),(41), (42), (43), (44), (45), (46), (47), (48), (49), (50),(51), (52), (53), (54), (55), (56), (57), (58), (59), (60),(61), (62), (63), (64), (65), (66), (67), (68), (69), (70),(71), (72), (73), (74), (75), (76), (77), (78), (79), (80), (81), (82), (83), (84), (85), (86), (87), (88), (89), (90),(91), (92), (93), (94), (95), (96), (97), (98), (99), (100); +CREATE TABLE users ( +id INT AUTO_INCREMENT PRIMARY KEY, +username VARCHAR(255) NOT NULL, +email VARCHAR(255) NOT NULL UNIQUE, +password VARCHAR(255) NOT NULL, +created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP +); +INSERT INTO users (username, email, password) VALUES ('john_doe', 'john@example.com', 'securepassword123'); +INSERT INTO users (username, email, password) VALUES ('jane_smith', 'jane.smith@example.com', 'password123'),('alice_jones', 'alice.jones@gmail.com', 'ilovecats'),('bob_brown', 'bob.brown@yahoo.com', 'mysecretpassword'),('charlie_lee', 'charlie.lee@protonmail.ch', 'secure123'),('diana_wilson', 'diana.wilson@outlook.com', 'D1anaPass'); +INSERT INTO users (username, email, password) VALUES ('emily_adams', 'emily.adams@icloud.com', 'Em1Ly123'), ('francis_nguyen', 'francis.nguyen@domain.com', 'fNguyenPass'), ('grace_parker', 'grace.parker@server.com', 'G1race123'), ('henry_miller', 'henry.miller@company.org', 'hMillerSecret'), ('isabella_grant', 'isabella.grant@university.edu', 'iGrantPass'); +select count(*) from snapshot_read.test_snapshot_read; +count(*) +100 +select count(*) from snapshot_read.users; +count(*) +11 +drop snapshot if exists sp_01; +create snapshot sp_01 for database snapshot_read; +delete from test_snapshot_read where a <= 50; +DELETE FROM users where email = 'john@example.com'; +UPDATE users SET password = 'newsecurepassword123' WHERE email = 'alice.jones@gmail.com'; +INSERT INTO test_snapshot_read (a) VALUES(1), (2), (3), (4), (5),(6), (7), (8), (9), (10), (11), (12),(13), (14), (15), (16), (17), (18), (19), (20),(21), (22), (23), (24), (25), (26), (27), (28), (29), (30),(31), (32), (33), (34), (35), (36), (37), (38), (39), (40); +select count(*) from snapshot_read.test_snapshot_read; +count(*) +90 +select count(*) from snapshot_read.users; +count(*) +10 +restore account sys database snapshot_read from snapshot sp_01; +restore account sys from snapshot sp_01; +internal error: can't restore account from db or table level snapshot +restore account sys database mo_catalog from snapshot sp_01; +internal error: can't restore db: mo_catalog +select count(*) from snapshot_read.test_snapshot_read; +count(*) +100 +select count(*) from snapshot_read.users; +count(*) +11 +drop database if exists snapshot_read; +drop snapshot if exists sp_01; +drop account if exists acc01; +create account acc01 admin_name = 'test_account' identified by '111'; +drop database if exists snapshot_read; +create database if not exists snapshot_read; +use snapshot_read; +create table test_snapshot_read (a int); +INSERT INTO test_snapshot_read (a) VALUES(1), (2), (3), (4), (5),(6), (7), (8), (9), (10), (11), (12),(13), (14), (15), (16), (17), (18), (19), (20),(21), (22), (23), (24), (25), (26), (27), (28), (29), (30),(31), (32), (33), (34), (35), (36), (37), (38), (39), (40),(41), (42), (43), (44), (45), (46), (47), (48), (49), (50),(51), (52), (53), (54), (55), (56), (57), (58), (59), (60),(61), (62), (63), (64), (65), (66), (67), (68), (69), (70),(71), (72), (73), (74), (75), (76), (77), (78), (79), (80), (81), (82), (83), (84), (85), (86), (87), (88), (89), (90),(91), (92), (93), (94), (95), (96), (97), (98), (99), (100); +CREATE TABLE users ( +id INT AUTO_INCREMENT PRIMARY KEY, +username VARCHAR(255) NOT NULL, +email VARCHAR(255) NOT NULL UNIQUE, +password VARCHAR(255) NOT NULL, +created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP +); +INSERT INTO users (username, email, password) VALUES ('john_doe', 'john@example.com', 'securepassword123'); +INSERT INTO users (username, email, password) VALUES ('jane_smith', 'jane.smith@example.com', 'password123'),('alice_jones', 'alice.jones@gmail.com', 'ilovecats'),('bob_brown', 'bob.brown@yahoo.com', 'mysecretpassword'),('charlie_lee', 'charlie.lee@protonmail.ch', 'secure123'),('diana_wilson', 'diana.wilson@outlook.com', 'D1anaPass'); +INSERT INTO users (username, email, password) VALUES ('emily_adams', 'emily.adams@icloud.com', 'Em1Ly123'), ('francis_nguyen', 'francis.nguyen@domain.com', 'fNguyenPass'), ('grace_parker', 'grace.parker@server.com', 'G1race123'), ('henry_miller', 'henry.miller@company.org', 'hMillerSecret'), ('isabella_grant', 'isabella.grant@university.edu', 'iGrantPass'); +select count(*) from snapshot_read.test_snapshot_read; +count(*) +100 +select count(*) from snapshot_read.users; +count(*) +11 +drop snapshot if exists sp_01; +create snapshot sp_01 for database snapshot_read; +delete from test_snapshot_read where a <= 50; +DELETE FROM users where email = 'john@example.com'; +UPDATE users SET password = 'newsecurepassword123' WHERE email = 'alice.jones@gmail.com'; +INSERT INTO test_snapshot_read (a) VALUES(1), (2), (3), (4), (5),(6), (7), (8), (9), (10), (11), (12),(13), (14), (15), (16), (17), (18), (19), (20),(21), (22), (23), (24), (25), (26), (27), (28), (29), (30),(31), (32), (33), (34), (35), (36), (37), (38), (39), (40); +select count(*) from snapshot_read.test_snapshot_read; +count(*) +90 +select count(*) from snapshot_read.users; +count(*) +10 +restore account acc01 database snapshot_read from snapshot sp_01; +select count(*) from snapshot_read.test_snapshot_read; +count(*) +100 +select count(*) from snapshot_read.users; +count(*) +11 +drop database if exists snapshot_read; +drop snapshot if exists sp_01; +drop account if exists acc1; +drop database if exists snapshot_read; +create database if not exists snapshot_read; +use snapshot_read; +create table test_snapshot_read (a int); +INSERT INTO test_snapshot_read (a) VALUES(1), (2), (3), (4), (5),(6), (7), (8), (9), (10), (11), (12),(13), (14), (15), (16), (17), (18), (19), (20),(21), (22), (23), (24), (25), (26), (27), (28), (29), (30),(31), (32), (33), (34), (35), (36), (37), (38), (39), (40),(41), (42), (43), (44), (45), (46), (47), (48), (49), (50),(51), (52), (53), (54), (55), (56), (57), (58), (59), (60),(61), (62), (63), (64), (65), (66), (67), (68), (69), (70),(71), (72), (73), (74), (75), (76), (77), (78), (79), (80), (81), (82), (83), (84), (85), (86), (87), (88), (89), (90),(91), (92), (93), (94), (95), (96), (97), (98), (99), (100); +CREATE TABLE users ( +id INT AUTO_INCREMENT PRIMARY KEY, +username VARCHAR(255) NOT NULL, +email VARCHAR(255) NOT NULL UNIQUE, +password VARCHAR(255) NOT NULL, +created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP +); +INSERT INTO users (username, email, password) VALUES ('john_doe', 'john@example.com', 'securepassword123'); +INSERT INTO users (username, email, password) VALUES ('jane_smith', 'jane.smith@example.com', 'password123'),('alice_jones', 'alice.jones@gmail.com', 'ilovecats'),('bob_brown', 'bob.brown@yahoo.com', 'mysecretpassword'),('charlie_lee', 'charlie.lee@protonmail.ch', 'secure123'),('diana_wilson', 'diana.wilson@outlook.com', 'D1anaPass'); +INSERT INTO users (username, email, password) VALUES ('emily_adams', 'emily.adams@icloud.com', 'Em1Ly123'), ('francis_nguyen', 'francis.nguyen@domain.com', 'fNguyenPass'), ('grace_parker', 'grace.parker@server.com', 'G1race123'), ('henry_miller', 'henry.miller@company.org', 'hMillerSecret'), ('isabella_grant', 'isabella.grant@university.edu', 'iGrantPass'); +select count(*) from snapshot_read.test_snapshot_read; +count(*) +100 +select count(*) from snapshot_read.users; +count(*) +11 +drop snapshot if exists sp_01; +create snapshot sp_01 for database snapshot_read; +delete from test_snapshot_read where a <= 50; +DELETE FROM users where email = 'john@example.com'; +UPDATE users SET password = 'newsecurepassword123' WHERE email = 'alice.jones@gmail.com'; +INSERT INTO test_snapshot_read (a) VALUES(1), (2), (3), (4), (5),(6), (7), (8), (9), (10), (11), (12),(13), (14), (15), (16), (17), (18), (19), (20),(21), (22), (23), (24), (25), (26), (27), (28), (29), (30),(31), (32), (33), (34), (35), (36), (37), (38), (39), (40); +select count(*) from snapshot_read.test_snapshot_read; +count(*) +90 +select count(*) from snapshot_read.users; +count(*) +10 +restore account sys database snapshot_read table test_snapshot_read from snapshot sp_01; +restore account sys database snapshot_read table users from snapshot sp_01; +select count(*) from snapshot_read.test_snapshot_read; +count(*) +100 +select count(*) from snapshot_read.users; +count(*) +11 +drop database if exists snapshot_read; +drop snapshot if exists sp_01; +drop account if exists acc01; +create account acc01 admin_name = 'test_account' identified by '111'; +drop database if exists snapshot_read; +create database if not exists snapshot_read; +use snapshot_read; +create table test_snapshot_read (a int); +INSERT INTO test_snapshot_read (a) VALUES(1), (2), (3), (4), (5),(6), (7), (8), (9), (10), (11), (12),(13), (14), (15), (16), (17), (18), (19), (20),(21), (22), (23), (24), (25), (26), (27), (28), (29), (30),(31), (32), (33), (34), (35), (36), (37), (38), (39), (40),(41), (42), (43), (44), (45), (46), (47), (48), (49), (50),(51), (52), (53), (54), (55), (56), (57), (58), (59), (60),(61), (62), (63), (64), (65), (66), (67), (68), (69), (70),(71), (72), (73), (74), (75), (76), (77), (78), (79), (80), (81), (82), (83), (84), (85), (86), (87), (88), (89), (90),(91), (92), (93), (94), (95), (96), (97), (98), (99), (100); +CREATE TABLE users ( +id INT AUTO_INCREMENT PRIMARY KEY, +username VARCHAR(255) NOT NULL, +email VARCHAR(255) NOT NULL UNIQUE, +password VARCHAR(255) NOT NULL, +created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP +); +INSERT INTO users (username, email, password) VALUES ('john_doe', 'john@example.com', 'securepassword123'); +INSERT INTO users (username, email, password) VALUES ('jane_smith', 'jane.smith@example.com', 'password123'),('alice_jones', 'alice.jones@gmail.com', 'ilovecats'),('bob_brown', 'bob.brown@yahoo.com', 'mysecretpassword'),('charlie_lee', 'charlie.lee@protonmail.ch', 'secure123'),('diana_wilson', 'diana.wilson@outlook.com', 'D1anaPass'); +INSERT INTO users (username, email, password) VALUES ('emily_adams', 'emily.adams@icloud.com', 'Em1Ly123'), ('francis_nguyen', 'francis.nguyen@domain.com', 'fNguyenPass'), ('grace_parker', 'grace.parker@server.com', 'G1race123'), ('henry_miller', 'henry.miller@company.org', 'hMillerSecret'), ('isabella_grant', 'isabella.grant@university.edu', 'iGrantPass'); +select count(*) from snapshot_read.test_snapshot_read; +count(*) +100 +select count(*) from snapshot_read.users; +count(*) +11 +drop snapshot if exists sp_01; +create snapshot sp_01 for database snapshot_read; +delete from test_snapshot_read where a <= 50; +DELETE FROM users where email = 'john@example.com'; +UPDATE users SET password = 'newsecurepassword123' WHERE email = 'alice.jones@gmail.com'; +INSERT INTO test_snapshot_read (a) VALUES(1), (2), (3), (4), (5),(6), (7), (8), (9), (10), (11), (12),(13), (14), (15), (16), (17), (18), (19), (20),(21), (22), (23), (24), (25), (26), (27), (28), (29), (30),(31), (32), (33), (34), (35), (36), (37), (38), (39), (40); +select count(*) from snapshot_read.test_snapshot_read; +count(*) +90 +select count(*) from snapshot_read.users; +count(*) +10 +restore account acc01 database snapshot_read table test_snapshot_read from snapshot sp_01; +restore account acc01 database snapshot_read table users from snapshot sp_01; +select count(*) from snapshot_read.test_snapshot_read; +count(*) +100 +select count(*) from snapshot_read.users; +count(*) +11 +drop database if exists snapshot_read; +drop snapshot if exists sp_01; +drop account if exists acc1; +drop database if exists snapshot_read; +create database if not exists snapshot_read; +use snapshot_read; +create table test_snapshot_read (a int); +INSERT INTO test_snapshot_read (a) VALUES(1), (2), (3), (4), (5),(6), (7), (8), (9), (10), (11), (12),(13), (14), (15), (16), (17), (18), (19), (20),(21), (22), (23), (24), (25), (26), (27), (28), (29), (30),(31), (32), (33), (34), (35), (36), (37), (38), (39), (40),(41), (42), (43), (44), (45), (46), (47), (48), (49), (50),(51), (52), (53), (54), (55), (56), (57), (58), (59), (60),(61), (62), (63), (64), (65), (66), (67), (68), (69), (70),(71), (72), (73), (74), (75), (76), (77), (78), (79), (80), (81), (82), (83), (84), (85), (86), (87), (88), (89), (90),(91), (92), (93), (94), (95), (96), (97), (98), (99), (100); +CREATE TABLE users ( +id INT AUTO_INCREMENT PRIMARY KEY, +username VARCHAR(255) NOT NULL, +email VARCHAR(255) NOT NULL UNIQUE, +password VARCHAR(255) NOT NULL, +created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP +); +INSERT INTO users (username, email, password) VALUES ('john_doe', 'john@example.com', 'securepassword123'); +INSERT INTO users (username, email, password) VALUES ('jane_smith', 'jane.smith@example.com', 'password123'),('alice_jones', 'alice.jones@gmail.com', 'ilovecats'),('bob_brown', 'bob.brown@yahoo.com', 'mysecretpassword'),('charlie_lee', 'charlie.lee@protonmail.ch', 'secure123'),('diana_wilson', 'diana.wilson@outlook.com', 'D1anaPass'); +INSERT INTO users (username, email, password) VALUES ('emily_adams', 'emily.adams@icloud.com', 'Em1Ly123'), ('francis_nguyen', 'francis.nguyen@domain.com', 'fNguyenPass'), ('grace_parker', 'grace.parker@server.com', 'G1race123'), ('henry_miller', 'henry.miller@company.org', 'hMillerSecret'), ('isabella_grant', 'isabella.grant@university.edu', 'iGrantPass'); +select count(*) from snapshot_read.test_snapshot_read; +count(*) +100 +select count(*) from snapshot_read.users; +count(*) +11 +drop snapshot if exists sp_01; +create snapshot sp_01 for table snapshot_read test_snapshot_read; +delete from test_snapshot_read where a <= 50; +DELETE FROM users where email = 'john@example.com'; +UPDATE users SET password = 'newsecurepassword123' WHERE email = 'alice.jones@gmail.com'; +INSERT INTO test_snapshot_read (a) VALUES(1), (2), (3), (4), (5),(6), (7), (8), (9), (10), (11), (12),(13), (14), (15), (16), (17), (18), (19), (20),(21), (22), (23), (24), (25), (26), (27), (28), (29), (30),(31), (32), (33), (34), (35), (36), (37), (38), (39), (40); +select count(*) from snapshot_read.test_snapshot_read; +count(*) +90 +select count(*) from snapshot_read.users; +count(*) +10 +restore account sys database snapshot_read table test_snapshot_read from snapshot sp_01; +restore account sys database snapshot_read table users from snapshot sp_01; +internal error: tableName(users) does not match snapshot.tableName(test_snapshot_read) +restore account sys database mo_catalog from snapshot sp_01; +internal error: can't restore db: mo_catalog +restore account sys from snapshot sp_01; +internal error: can't restore account from db or table level snapshot +select count(*) from snapshot_read.test_snapshot_read; +count(*) +100 +select count(*) from snapshot_read.users; +count(*) +10 +drop database if exists snapshot_read; +drop snapshot if exists sp_01; diff --git a/test/distributed/cases/snapshot/snapshot_database_level_restore.sql b/test/distributed/cases/snapshot/snapshot_database_level_restore.sql new file mode 100644 index 0000000000000..bda6756bcd314 --- /dev/null +++ b/test/distributed/cases/snapshot/snapshot_database_level_restore.sql @@ -0,0 +1,197 @@ +drop database if exists snapshot_read; +create database if not exists snapshot_read; +use snapshot_read; +create table test_snapshot_read (a int); +INSERT INTO test_snapshot_read (a) VALUES(1), (2), (3), (4), (5),(6), (7), (8), (9), (10), (11), (12),(13), (14), (15), (16), (17), (18), (19), (20),(21), (22), (23), (24), (25), (26), (27), (28), (29), (30),(31), (32), (33), (34), (35), (36), (37), (38), (39), (40),(41), (42), (43), (44), (45), (46), (47), (48), (49), (50),(51), (52), (53), (54), (55), (56), (57), (58), (59), (60),(61), (62), (63), (64), (65), (66), (67), (68), (69), (70),(71), (72), (73), (74), (75), (76), (77), (78), (79), (80), (81), (82), (83), (84), (85), (86), (87), (88), (89), (90),(91), (92), (93), (94), (95), (96), (97), (98), (99), (100); +CREATE TABLE users ( + id INT AUTO_INCREMENT PRIMARY KEY, + username VARCHAR(255) NOT NULL, + email VARCHAR(255) NOT NULL UNIQUE, + password VARCHAR(255) NOT NULL, + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP +); +INSERT INTO users (username, email, password) VALUES ('john_doe', 'john@example.com', 'securepassword123'); +INSERT INTO users (username, email, password) VALUES ('jane_smith', 'jane.smith@example.com', 'password123'),('alice_jones', 'alice.jones@gmail.com', 'ilovecats'),('bob_brown', 'bob.brown@yahoo.com', 'mysecretpassword'),('charlie_lee', 'charlie.lee@protonmail.ch', 'secure123'),('diana_wilson', 'diana.wilson@outlook.com', 'D1anaPass'); +INSERT INTO users (username, email, password) VALUES ('emily_adams', 'emily.adams@icloud.com', 'Em1Ly123'), ('francis_nguyen', 'francis.nguyen@domain.com', 'fNguyenPass'), ('grace_parker', 'grace.parker@server.com', 'G1race123'), ('henry_miller', 'henry.miller@company.org', 'hMillerSecret'), ('isabella_grant', 'isabella.grant@university.edu', 'iGrantPass'); + +select count(*) from snapshot_read.test_snapshot_read; +select count(*) from snapshot_read.users; +drop snapshot if exists sp_01; +create snapshot sp_01 for database snapshot_read; +delete from test_snapshot_read where a <= 50; +DELETE FROM users where email = 'john@example.com'; +UPDATE users SET password = 'newsecurepassword123' WHERE email = 'alice.jones@gmail.com'; +INSERT INTO test_snapshot_read (a) VALUES(1), (2), (3), (4), (5),(6), (7), (8), (9), (10), (11), (12),(13), (14), (15), (16), (17), (18), (19), (20),(21), (22), (23), (24), (25), (26), (27), (28), (29), (30),(31), (32), (33), (34), (35), (36), (37), (38), (39), (40); +select count(*) from snapshot_read.test_snapshot_read; +select count(*) from snapshot_read.users; + +restore account sys database snapshot_read from snapshot sp_01; +restore account sys from snapshot sp_01; +restore account sys database mo_catalog from snapshot sp_01; + +select count(*) from snapshot_read.test_snapshot_read; +select count(*) from snapshot_read.users; + +drop database if exists snapshot_read; +drop snapshot if exists sp_01; + +drop account if exists acc01; +create account acc01 admin_name = 'test_account' identified by '111'; + +-- @session:id=1&user=acc01:test_account&password=111 +drop database if exists snapshot_read; +create database if not exists snapshot_read; +use snapshot_read; +create table test_snapshot_read (a int); +INSERT INTO test_snapshot_read (a) VALUES(1), (2), (3), (4), (5),(6), (7), (8), (9), (10), (11), (12),(13), (14), (15), (16), (17), (18), (19), (20),(21), (22), (23), (24), (25), (26), (27), (28), (29), (30),(31), (32), (33), (34), (35), (36), (37), (38), (39), (40),(41), (42), (43), (44), (45), (46), (47), (48), (49), (50),(51), (52), (53), (54), (55), (56), (57), (58), (59), (60),(61), (62), (63), (64), (65), (66), (67), (68), (69), (70),(71), (72), (73), (74), (75), (76), (77), (78), (79), (80), (81), (82), (83), (84), (85), (86), (87), (88), (89), (90),(91), (92), (93), (94), (95), (96), (97), (98), (99), (100); +CREATE TABLE users ( + id INT AUTO_INCREMENT PRIMARY KEY, + username VARCHAR(255) NOT NULL, + email VARCHAR(255) NOT NULL UNIQUE, + password VARCHAR(255) NOT NULL, + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP +); +INSERT INTO users (username, email, password) VALUES ('john_doe', 'john@example.com', 'securepassword123'); +INSERT INTO users (username, email, password) VALUES ('jane_smith', 'jane.smith@example.com', 'password123'),('alice_jones', 'alice.jones@gmail.com', 'ilovecats'),('bob_brown', 'bob.brown@yahoo.com', 'mysecretpassword'),('charlie_lee', 'charlie.lee@protonmail.ch', 'secure123'),('diana_wilson', 'diana.wilson@outlook.com', 'D1anaPass'); +INSERT INTO users (username, email, password) VALUES ('emily_adams', 'emily.adams@icloud.com', 'Em1Ly123'), ('francis_nguyen', 'francis.nguyen@domain.com', 'fNguyenPass'), ('grace_parker', 'grace.parker@server.com', 'G1race123'), ('henry_miller', 'henry.miller@company.org', 'hMillerSecret'), ('isabella_grant', 'isabella.grant@university.edu', 'iGrantPass'); + +select count(*) from snapshot_read.test_snapshot_read; +select count(*) from snapshot_read.users; +drop snapshot if exists sp_01; +create snapshot sp_01 for database snapshot_read; +delete from test_snapshot_read where a <= 50; +DELETE FROM users where email = 'john@example.com'; +UPDATE users SET password = 'newsecurepassword123' WHERE email = 'alice.jones@gmail.com'; +INSERT INTO test_snapshot_read (a) VALUES(1), (2), (3), (4), (5),(6), (7), (8), (9), (10), (11), (12),(13), (14), (15), (16), (17), (18), (19), (20),(21), (22), (23), (24), (25), (26), (27), (28), (29), (30),(31), (32), (33), (34), (35), (36), (37), (38), (39), (40); +select count(*) from snapshot_read.test_snapshot_read; +select count(*) from snapshot_read.users; + +restore account acc01 database snapshot_read from snapshot sp_01; +select count(*) from snapshot_read.test_snapshot_read; +select count(*) from snapshot_read.users; + +drop database if exists snapshot_read; +drop snapshot if exists sp_01; +-- @session + +drop account if exists acc1; + + +drop database if exists snapshot_read; +create database if not exists snapshot_read; +use snapshot_read; +create table test_snapshot_read (a int); +INSERT INTO test_snapshot_read (a) VALUES(1), (2), (3), (4), (5),(6), (7), (8), (9), (10), (11), (12),(13), (14), (15), (16), (17), (18), (19), (20),(21), (22), (23), (24), (25), (26), (27), (28), (29), (30),(31), (32), (33), (34), (35), (36), (37), (38), (39), (40),(41), (42), (43), (44), (45), (46), (47), (48), (49), (50),(51), (52), (53), (54), (55), (56), (57), (58), (59), (60),(61), (62), (63), (64), (65), (66), (67), (68), (69), (70),(71), (72), (73), (74), (75), (76), (77), (78), (79), (80), (81), (82), (83), (84), (85), (86), (87), (88), (89), (90),(91), (92), (93), (94), (95), (96), (97), (98), (99), (100); +CREATE TABLE users ( + id INT AUTO_INCREMENT PRIMARY KEY, + username VARCHAR(255) NOT NULL, + email VARCHAR(255) NOT NULL UNIQUE, + password VARCHAR(255) NOT NULL, + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP +); +INSERT INTO users (username, email, password) VALUES ('john_doe', 'john@example.com', 'securepassword123'); +INSERT INTO users (username, email, password) VALUES ('jane_smith', 'jane.smith@example.com', 'password123'),('alice_jones', 'alice.jones@gmail.com', 'ilovecats'),('bob_brown', 'bob.brown@yahoo.com', 'mysecretpassword'),('charlie_lee', 'charlie.lee@protonmail.ch', 'secure123'),('diana_wilson', 'diana.wilson@outlook.com', 'D1anaPass'); +INSERT INTO users (username, email, password) VALUES ('emily_adams', 'emily.adams@icloud.com', 'Em1Ly123'), ('francis_nguyen', 'francis.nguyen@domain.com', 'fNguyenPass'), ('grace_parker', 'grace.parker@server.com', 'G1race123'), ('henry_miller', 'henry.miller@company.org', 'hMillerSecret'), ('isabella_grant', 'isabella.grant@university.edu', 'iGrantPass'); + +select count(*) from snapshot_read.test_snapshot_read; +select count(*) from snapshot_read.users; +drop snapshot if exists sp_01; +create snapshot sp_01 for database snapshot_read; +delete from test_snapshot_read where a <= 50; +DELETE FROM users where email = 'john@example.com'; +UPDATE users SET password = 'newsecurepassword123' WHERE email = 'alice.jones@gmail.com'; +INSERT INTO test_snapshot_read (a) VALUES(1), (2), (3), (4), (5),(6), (7), (8), (9), (10), (11), (12),(13), (14), (15), (16), (17), (18), (19), (20),(21), (22), (23), (24), (25), (26), (27), (28), (29), (30),(31), (32), (33), (34), (35), (36), (37), (38), (39), (40); +select count(*) from snapshot_read.test_snapshot_read; +select count(*) from snapshot_read.users; + +restore account sys database snapshot_read table test_snapshot_read from snapshot sp_01; +restore account sys database snapshot_read table users from snapshot sp_01; + +select count(*) from snapshot_read.test_snapshot_read; +select count(*) from snapshot_read.users; + +drop database if exists snapshot_read; +drop snapshot if exists sp_01; + +drop account if exists acc01; +create account acc01 admin_name = 'test_account' identified by '111'; + +-- @session:id=2&user=acc01:test_account&password=111 +drop database if exists snapshot_read; +create database if not exists snapshot_read; +use snapshot_read; +create table test_snapshot_read (a int); +INSERT INTO test_snapshot_read (a) VALUES(1), (2), (3), (4), (5),(6), (7), (8), (9), (10), (11), (12),(13), (14), (15), (16), (17), (18), (19), (20),(21), (22), (23), (24), (25), (26), (27), (28), (29), (30),(31), (32), (33), (34), (35), (36), (37), (38), (39), (40),(41), (42), (43), (44), (45), (46), (47), (48), (49), (50),(51), (52), (53), (54), (55), (56), (57), (58), (59), (60),(61), (62), (63), (64), (65), (66), (67), (68), (69), (70),(71), (72), (73), (74), (75), (76), (77), (78), (79), (80), (81), (82), (83), (84), (85), (86), (87), (88), (89), (90),(91), (92), (93), (94), (95), (96), (97), (98), (99), (100); +CREATE TABLE users ( + id INT AUTO_INCREMENT PRIMARY KEY, + username VARCHAR(255) NOT NULL, + email VARCHAR(255) NOT NULL UNIQUE, + password VARCHAR(255) NOT NULL, + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP +); +INSERT INTO users (username, email, password) VALUES ('john_doe', 'john@example.com', 'securepassword123'); +INSERT INTO users (username, email, password) VALUES ('jane_smith', 'jane.smith@example.com', 'password123'),('alice_jones', 'alice.jones@gmail.com', 'ilovecats'),('bob_brown', 'bob.brown@yahoo.com', 'mysecretpassword'),('charlie_lee', 'charlie.lee@protonmail.ch', 'secure123'),('diana_wilson', 'diana.wilson@outlook.com', 'D1anaPass'); +INSERT INTO users (username, email, password) VALUES ('emily_adams', 'emily.adams@icloud.com', 'Em1Ly123'), ('francis_nguyen', 'francis.nguyen@domain.com', 'fNguyenPass'), ('grace_parker', 'grace.parker@server.com', 'G1race123'), ('henry_miller', 'henry.miller@company.org', 'hMillerSecret'), ('isabella_grant', 'isabella.grant@university.edu', 'iGrantPass'); + +select count(*) from snapshot_read.test_snapshot_read; +select count(*) from snapshot_read.users; +drop snapshot if exists sp_01; +create snapshot sp_01 for database snapshot_read; +delete from test_snapshot_read where a <= 50; +DELETE FROM users where email = 'john@example.com'; +UPDATE users SET password = 'newsecurepassword123' WHERE email = 'alice.jones@gmail.com'; +INSERT INTO test_snapshot_read (a) VALUES(1), (2), (3), (4), (5),(6), (7), (8), (9), (10), (11), (12),(13), (14), (15), (16), (17), (18), (19), (20),(21), (22), (23), (24), (25), (26), (27), (28), (29), (30),(31), (32), (33), (34), (35), (36), (37), (38), (39), (40); +select count(*) from snapshot_read.test_snapshot_read; +select count(*) from snapshot_read.users; + +restore account acc01 database snapshot_read table test_snapshot_read from snapshot sp_01; +restore account acc01 database snapshot_read table users from snapshot sp_01; + +select count(*) from snapshot_read.test_snapshot_read; +select count(*) from snapshot_read.users; + +drop database if exists snapshot_read; +drop snapshot if exists sp_01; +-- @session + +drop account if exists acc1; + + + +drop database if exists snapshot_read; +create database if not exists snapshot_read; +use snapshot_read; +create table test_snapshot_read (a int); +INSERT INTO test_snapshot_read (a) VALUES(1), (2), (3), (4), (5),(6), (7), (8), (9), (10), (11), (12),(13), (14), (15), (16), (17), (18), (19), (20),(21), (22), (23), (24), (25), (26), (27), (28), (29), (30),(31), (32), (33), (34), (35), (36), (37), (38), (39), (40),(41), (42), (43), (44), (45), (46), (47), (48), (49), (50),(51), (52), (53), (54), (55), (56), (57), (58), (59), (60),(61), (62), (63), (64), (65), (66), (67), (68), (69), (70),(71), (72), (73), (74), (75), (76), (77), (78), (79), (80), (81), (82), (83), (84), (85), (86), (87), (88), (89), (90),(91), (92), (93), (94), (95), (96), (97), (98), (99), (100); +CREATE TABLE users ( + id INT AUTO_INCREMENT PRIMARY KEY, + username VARCHAR(255) NOT NULL, + email VARCHAR(255) NOT NULL UNIQUE, + password VARCHAR(255) NOT NULL, + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP +); +INSERT INTO users (username, email, password) VALUES ('john_doe', 'john@example.com', 'securepassword123'); +INSERT INTO users (username, email, password) VALUES ('jane_smith', 'jane.smith@example.com', 'password123'),('alice_jones', 'alice.jones@gmail.com', 'ilovecats'),('bob_brown', 'bob.brown@yahoo.com', 'mysecretpassword'),('charlie_lee', 'charlie.lee@protonmail.ch', 'secure123'),('diana_wilson', 'diana.wilson@outlook.com', 'D1anaPass'); +INSERT INTO users (username, email, password) VALUES ('emily_adams', 'emily.adams@icloud.com', 'Em1Ly123'), ('francis_nguyen', 'francis.nguyen@domain.com', 'fNguyenPass'), ('grace_parker', 'grace.parker@server.com', 'G1race123'), ('henry_miller', 'henry.miller@company.org', 'hMillerSecret'), ('isabella_grant', 'isabella.grant@university.edu', 'iGrantPass'); + +select count(*) from snapshot_read.test_snapshot_read; +select count(*) from snapshot_read.users; +drop snapshot if exists sp_01; +create snapshot sp_01 for table snapshot_read test_snapshot_read; +delete from test_snapshot_read where a <= 50; +DELETE FROM users where email = 'john@example.com'; +UPDATE users SET password = 'newsecurepassword123' WHERE email = 'alice.jones@gmail.com'; +INSERT INTO test_snapshot_read (a) VALUES(1), (2), (3), (4), (5),(6), (7), (8), (9), (10), (11), (12),(13), (14), (15), (16), (17), (18), (19), (20),(21), (22), (23), (24), (25), (26), (27), (28), (29), (30),(31), (32), (33), (34), (35), (36), (37), (38), (39), (40); +select count(*) from snapshot_read.test_snapshot_read; +select count(*) from snapshot_read.users; + +restore account sys database snapshot_read table test_snapshot_read from snapshot sp_01; +restore account sys database snapshot_read table users from snapshot sp_01; +restore account sys database mo_catalog from snapshot sp_01; +restore account sys from snapshot sp_01; + +select count(*) from snapshot_read.test_snapshot_read; +select count(*) from snapshot_read.users; + +drop database if exists snapshot_read; +drop snapshot if exists sp_01; diff --git a/test/distributed/cases/snapshot/snapshot_restore_database_level.result b/test/distributed/cases/snapshot/snapshot_restore_database_level.result index 82fe0288e79b7..f8a8a4f40b9e1 100644 --- a/test/distributed/cases/snapshot/snapshot_restore_database_level.result +++ b/test/distributed/cases/snapshot/snapshot_restore_database_level.result @@ -279,6 +279,8 @@ use snapshot_read; drop table students; drop table users; restore account test_account database snapshot_read from snapshot sp_01; +internal error: can't restore database from other account's snapshot +restore account test_account from snapshot sp_01; select count(*) from snapshot_read.students; count(*) 50 @@ -287,6 +289,8 @@ count(*) 11 drop database snapshot_read; restore account test_account database snapshot_read from snapshot sp_01; +internal error: can't restore database from other account's snapshot +restore account test_account from snapshot sp_01; select count(*) from snapshot_read.students; count(*) 50 diff --git a/test/distributed/cases/snapshot/snapshot_restore_database_level.sql b/test/distributed/cases/snapshot/snapshot_restore_database_level.sql index 48a41f395f501..21112741e085d 100644 --- a/test/distributed/cases/snapshot/snapshot_restore_database_level.sql +++ b/test/distributed/cases/snapshot/snapshot_restore_database_level.sql @@ -274,6 +274,7 @@ drop table users; -- @session restore account test_account database snapshot_read from snapshot sp_01; +restore account test_account from snapshot sp_01; -- @session:id=3&user=test_account:test_user&password=111 select count(*) from snapshot_read.students; @@ -286,6 +287,7 @@ drop database snapshot_read; -- @session restore account test_account database snapshot_read from snapshot sp_01; +restore account test_account from snapshot sp_01; -- @session:id=3&user=test_account:test_user&password=111 select count(*) from snapshot_read.students; diff --git a/test/distributed/cases/snapshot/snapshot_restore_table_level.result b/test/distributed/cases/snapshot/snapshot_restore_table_level.result index ec2fe351ca188..7d98c12dcc02f 100644 --- a/test/distributed/cases/snapshot/snapshot_restore_table_level.result +++ b/test/distributed/cases/snapshot/snapshot_restore_table_level.result @@ -298,6 +298,8 @@ select count(*) from snapshot_read.users; count(*) 0 restore account test_account database snapshot_read table users from snapshot sp_01; +internal error: can't restore table from other account's snapshot +restore account test_account from snapshot sp_01; select count(*) from snapshot_read.users; count(*) 11 diff --git a/test/distributed/cases/snapshot/snapshot_restore_table_level.sql b/test/distributed/cases/snapshot/snapshot_restore_table_level.sql index 60e9701c95b30..e31136550ec6b 100644 --- a/test/distributed/cases/snapshot/snapshot_restore_table_level.sql +++ b/test/distributed/cases/snapshot/snapshot_restore_table_level.sql @@ -268,6 +268,7 @@ select count(*) from snapshot_read.users; -- @session restore account test_account database snapshot_read table users from snapshot sp_01; +restore account test_account from snapshot sp_01; -- @session:id=5&user=test_account:test_user&password=111 select count(*) from snapshot_read.users; diff --git a/test/distributed/cases/snapshot/snapshot_restore_to_table_partition.result b/test/distributed/cases/snapshot/snapshot_restore_to_table_partition.result index 0b7cf6f192a32..9495dbcc33dc3 100644 --- a/test/distributed/cases/snapshot/snapshot_restore_to_table_partition.result +++ b/test/distributed/cases/snapshot/snapshot_restore_to_table_partition.result @@ -34,6 +34,7 @@ show create table acc_test04.index03; Table Create Table index03 CREATE TABLE `index03` (\n `emp_no` int NOT NULL,\n `birth_date` date NOT NULL,\n `first_name` varchar(14) NOT NULL,\n `last_name` varchar(16) NOT NULL,\n `gender` varchar(5) NOT NULL,\n `hire_date` date NOT NULL,\n PRIMARY KEY (`emp_no`)\n) partition by range columns (emp_no) (partition p01 values less than (100001), partition p02 values less than (200001), partition p03 values less than (300001), partition p04 values less than (400001)) restore account acc01 database acc_test04 table index03 from snapshot sp04; +internal error: can't restore table from other account's snapshot restore account acc01 from snapshot sp04 to account acc02; select count(*) from acc_test04.index03; count(*) diff --git a/test/distributed/cases/snapshot/sys_restore_view_to_nonsys_account.result b/test/distributed/cases/snapshot/sys_restore_view_to_nonsys_account.result index fb598d8bb045b..4f5c60e1679bf 100644 --- a/test/distributed/cases/snapshot/sys_restore_view_to_nonsys_account.result +++ b/test/distributed/cases/snapshot/sys_restore_view_to_nonsys_account.result @@ -506,6 +506,8 @@ drop table students; select * from StudentCoursesView; SQL parser error: table "students" does not exist restore account acc01 database test02 table students from snapshot sp07; +internal error: can't restore table from other account's snapshot +restore account acc01 from snapshot sp07; use test02; select * from Students; studentid name grade @@ -513,10 +515,20 @@ studentid name grade 2 Bob 3.00 3 Charlie 3.70 select * from Enrollments; -SQL parser error: table "enrollments" does not exist +studentid courseid enrollmentdate +1 101 2024-01-10 +2 102 2024-01-15 +1 103 2024-01-20 +3 101 2024-02-01 select * from StudentCoursesView; -SQL parser error: table "enrollments" does not exist +studentname coursetitle teacher enrollmentdate +Alice Chemistry Ms. Lee 2024-01-20 +Alice Mathematics Mr. Smith 2024-01-10 +Bob Physics Dr. Johnson 2024-01-15 +Charlie Mathematics Mr. Smith 2024-02-01 restore account acc01 database test02 table Enrollments from snapshot sp07; +internal error: can't restore table from other account's snapshot +restore account acc01 from snapshot sp07; use test02; select * from Enrollments; studentid courseid enrollmentdate diff --git a/test/distributed/cases/snapshot/sys_restore_view_to_nonsys_account.sql b/test/distributed/cases/snapshot/sys_restore_view_to_nonsys_account.sql index 6b3a84988ff08..2fb0dc37028c6 100644 --- a/test/distributed/cases/snapshot/sys_restore_view_to_nonsys_account.sql +++ b/test/distributed/cases/snapshot/sys_restore_view_to_nonsys_account.sql @@ -477,6 +477,7 @@ select * from StudentCoursesView; -- @session restore account acc01 database test02 table students from snapshot sp07; +restore account acc01 from snapshot sp07; -- @session:id=1&user=acc01:test_account&password=111 use test02; @@ -486,6 +487,7 @@ select * from StudentCoursesView; -- @session restore account acc01 database test02 table Enrollments from snapshot sp07; +restore account acc01 from snapshot sp07; -- @session:id=1&user=acc01:test_account&password=111 use test02; From a5fbab6138eea78330c1bc5b643bb73209b23bc9 Mon Sep 17 00:00:00 2001 From: GreatRiver <14086886+LeftHandCold@users.noreply.github.com> Date: Mon, 23 Dec 2024 18:25:15 +0800 Subject: [PATCH 24/26] Fix the bug that the file cannot be found when restoring using incremental backup (#20871) Fix the bug that the file cannot be found when restoring using incremental backup. The reason is that the create ts of the tombstone is filled with the commit ts Approved by: @XuPeng-SH --- pkg/vm/engine/tae/logtail/backup.go | 83 ++++++++++++----------------- 1 file changed, 35 insertions(+), 48 deletions(-) diff --git a/pkg/vm/engine/tae/logtail/backup.go b/pkg/vm/engine/tae/logtail/backup.go index 176cd5f9e7666..0c42cbb5f825b 100644 --- a/pkg/vm/engine/tae/logtail/backup.go +++ b/pkg/vm/engine/tae/logtail/backup.go @@ -418,61 +418,48 @@ func LoadCheckpointEntriesFromKey( NeedCopy: true, }) } - for i := 0; i < data.bats[ObjectInfoIDX].Length(); i++ { - var objectStats objectio.ObjectStats - buf := data.bats[ObjectInfoIDX].GetVectorByName(ObjectAttr_ObjectStats).Get(i).([]byte) - objectStats.UnMarshal(buf) - deletedAt := data.bats[ObjectInfoIDX].GetVectorByName(EntryNode_DeleteAt).Get(i).(types.TS) - createAt := data.bats[ObjectInfoIDX].GetVectorByName(EntryNode_CreateAt).Get(i).(types.TS) - commitAt := data.bats[ObjectInfoIDX].GetVectorByName(txnbase.SnapshotAttr_CommitTS).Get(i).(types.TS) - isAblk := objectStats.GetAppendable() - if objectStats.Extent().End() == 0 { - // tn obj is in the batch too - continue - } - if deletedAt.IsEmpty() && isAblk { - // no flush, no need to copy - continue - } + collectObject := func(bat *containers.Batch) { + for i := 0; i < bat.Length(); i++ { + var objectStats objectio.ObjectStats + buf := bat.GetVectorByName(ObjectAttr_ObjectStats).Get(i).([]byte) + objectStats.UnMarshal(buf) + deletedAt := bat.GetVectorByName(EntryNode_DeleteAt).Get(i).(types.TS) + createAt := bat.GetVectorByName(EntryNode_CreateAt).Get(i).(types.TS) + commitAt := bat.GetVectorByName(txnbase.SnapshotAttr_CommitTS).Get(i).(types.TS) + isAblk := objectStats.GetAppendable() + if objectStats.Extent().End() == 0 { + // tn obj is in the batch too + continue + } - bo := &objectio.BackupObject{ - Location: objectStats.ObjectLocation(), - CrateTS: createAt, - DropTS: deletedAt, - } - if baseTS.IsEmpty() || (!baseTS.IsEmpty() && - (createAt.GE(baseTS) || commitAt.GE(baseTS))) { - bo.NeedCopy = true - } - locations = append(locations, bo) - if !deletedAt.IsEmpty() { - if softDeletes != nil { - if !(*softDeletes)[objectStats.ObjectName().String()] { - (*softDeletes)[objectStats.ObjectName().String()] = true + if deletedAt.IsEmpty() && isAblk { + // no flush, no need to copy + continue + } + + bo := &objectio.BackupObject{ + Location: objectStats.ObjectLocation(), + CrateTS: createAt, + DropTS: deletedAt, + } + if baseTS.IsEmpty() || (!baseTS.IsEmpty() && + (createAt.GE(baseTS) || commitAt.GE(baseTS))) { + bo.NeedCopy = true + } + locations = append(locations, bo) + if !deletedAt.IsEmpty() { + if softDeletes != nil { + if !(*softDeletes)[objectStats.ObjectName().String()] { + (*softDeletes)[objectStats.ObjectName().String()] = true + } } } } } - for i := 0; i < data.bats[TombstoneObjectInfoIDX].Length(); i++ { - var objectStats objectio.ObjectStats - buf := data.bats[TombstoneObjectInfoIDX].GetVectorByName(ObjectAttr_ObjectStats).Get(i).([]byte) - objectStats.UnMarshal(buf) - commitTS := data.bats[TombstoneObjectInfoIDX].GetVectorByName(txnbase.SnapshotAttr_CommitTS).Get(i).(types.TS) - if objectStats.ObjectLocation().IsEmpty() { - continue - } - bo := &objectio.BackupObject{ - Location: objectStats.ObjectLocation(), - CrateTS: commitTS, - } - if baseTS.IsEmpty() || - (!baseTS.IsEmpty() && commitTS.GE(baseTS)) { - bo.NeedCopy = true - } - locations = append(locations, bo) - } + collectObject(data.bats[ObjectInfoIDX]) + collectObject(data.bats[TombstoneObjectInfoIDX]) return locations, data, nil } From ba1b49017068dd2f9767db5e918d8384191528c3 Mon Sep 17 00:00:00 2001 From: gouhongshen Date: Mon, 23 Dec 2024 19:24:22 +0800 Subject: [PATCH 25/26] remove the preCommitWrite in the commit routine. (#20819) 1. pre commit write is designed for 2pc, current TN is running in the 1PC mode. Approved by: @daviszhen, @zhangxu19830126, @w-zr, @XuPeng-SH, @reusee --- pkg/cnservice/server.go | 2 +- pkg/tnservice/store_rpc_handler.go | 11 +- pkg/txn/service/service_cn_handler.go | 7 +- pkg/txn/service/service_dn_handler.go | 2 +- pkg/txn/storage/mem/kv_txn_storage.go | 7 +- pkg/txn/storage/mem/kv_txn_storage_test.go | 2 +- .../storage/memorystorage/dynamic_storage.go | 9 +- pkg/txn/storage/memorystorage/storage.go | 7 +- pkg/txn/storage/memorystorage/storage_test.go | 2 +- .../memorystorage/storage_txn_client.go | 2 +- pkg/txn/storage/tae/storage.go | 9 +- pkg/txn/storage/tae/write.go | 4 +- pkg/txn/storage/types.go | 6 +- pkg/vm/engine/tae/iface/rpchandle/handler.go | 2 + pkg/vm/engine/tae/rpc/base_test.go | 2 +- pkg/vm/engine/tae/rpc/handle.go | 293 ++++++++++++------ pkg/vm/engine/tae/rpc/handle_2pc.go | 24 +- pkg/vm/engine/tae/rpc/handle_test.go | 32 +- pkg/vm/engine/test/testutil/tae_engine.go | 14 +- 19 files changed, 255 insertions(+), 182 deletions(-) diff --git a/pkg/cnservice/server.go b/pkg/cnservice/server.go index ef1ec435f4062..6deeb2bf509e7 100644 --- a/pkg/cnservice/server.go +++ b/pkg/cnservice/server.go @@ -614,7 +614,7 @@ func (s *service) getTxnSender() (sender rpc.TxnSender, err error) { resp.CNOpResponse = &txn.CNOpResponse{Payload: payload} } case txn.TxnMethod_Commit: - _, err = storage.Commit(ctx, req.Txn) + _, err = storage.Commit(ctx, req.Txn, nil, nil) if err == nil { resp.Txn.Status = txn.TxnStatus_Committed } diff --git a/pkg/tnservice/store_rpc_handler.go b/pkg/tnservice/store_rpc_handler.go index 1105c193f540b..d49d5a2c6dcd4 100644 --- a/pkg/tnservice/store_rpc_handler.go +++ b/pkg/tnservice/store_rpc_handler.go @@ -110,16 +110,7 @@ func (s *store) handleCommit(ctx context.Context, request *txn.TxnRequest, respo return nil } r.waitStarted() - if request.CommitRequest != nil { - for _, req := range request.CommitRequest.Payload { - //response is shared by all requests - prepareResponse(req, response) - err := s.handleWrite(ctx, req, response) - if err != nil { - return err - } - } - } + prepareResponse(request, response) return r.service.Commit(ctx, request, response) } diff --git a/pkg/txn/service/service_cn_handler.go b/pkg/txn/service/service_cn_handler.go index 62aaa5cc3b9c6..a8f5b6731a8a7 100644 --- a/pkg/txn/service/service_cn_handler.go +++ b/pkg/txn/service/service_cn_handler.go @@ -217,7 +217,8 @@ func (s *service) Commit(ctx context.Context, request *txn.TxnRequest, response } txnID := request.Txn.ID - txnCtx := s.getTxnContext(txnID) + txnCtx, _ := s.maybeAddTxn(request.Txn) + if txnCtx == nil { util.LogTxnNotFoundOn(s.logger, request.Txn, s.shard) response.TxnError = txn.WrapError(moerr.NewTNShardNotFound(ctx, "", request.GetTargetTN().ShardID), 0) @@ -261,7 +262,7 @@ func (s *service) Commit(ctx context.Context, request *txn.TxnRequest, response if len(newTxn.TNShards) == 1 { util.LogTxnStart1PCCommit(s.logger, newTxn) - commitTS, err := s.storage.Commit(ctx, newTxn) + commitTS, err := s.storage.Commit(ctx, newTxn, response, request.CommitRequest) v2.TxnTNCommitHandledCounter.Inc() if err != nil { util.LogTxnStart1PCCommitFailed(s.logger, newTxn, err) @@ -469,7 +470,7 @@ func (s *service) startAsyncCommitTask(txnCtx *txnContext) error { util.TxnIDFieldWithID(txnMeta.ID)) } - if _, err := s.storage.Commit(ctx, txnMeta); err != nil { + if _, err := s.storage.Commit(ctx, txnMeta, nil, nil); err != nil { err = moerr.AttachCause(ctx, err) s.logger.Fatal("commit failed after prepared", util.TxnIDFieldWithID(txnMeta.ID), diff --git a/pkg/txn/service/service_dn_handler.go b/pkg/txn/service/service_dn_handler.go index 60bb7786f051a..734c7de3fd38c 100644 --- a/pkg/txn/service/service_dn_handler.go +++ b/pkg/txn/service/service_dn_handler.go @@ -147,7 +147,7 @@ func (s *service) CommitTNShard(ctx context.Context, request *txn.TxnRequest, re } newTxn.CommitTS = request.Txn.CommitTS - if _, err := s.storage.Commit(ctx, newTxn); err != nil { + if _, err := s.storage.Commit(ctx, newTxn, response, request.CommitRequest); err != nil { response.TxnError = txn.WrapError(err, moerr.ErrTAECommit) return nil } diff --git a/pkg/txn/storage/mem/kv_txn_storage.go b/pkg/txn/storage/mem/kv_txn_storage.go index 9f1ee55ce4ec7..0d621644a21b7 100644 --- a/pkg/txn/storage/mem/kv_txn_storage.go +++ b/pkg/txn/storage/mem/kv_txn_storage.go @@ -330,7 +330,12 @@ func (kv *KVTxnStorage) Committing(ctx context.Context, txnMeta txn.TxnMeta) err return nil } -func (kv *KVTxnStorage) Commit(ctx context.Context, txnMeta txn.TxnMeta) (timestamp.Timestamp, error) { +func (kv *KVTxnStorage) Commit( + ctx context.Context, + txnMeta txn.TxnMeta, + response *txn.TxnResponse, + commitRequests *txn.TxnCommitRequest, +) (timestamp.Timestamp, error) { kv.Lock() defer kv.Unlock() diff --git a/pkg/txn/storage/mem/kv_txn_storage_test.go b/pkg/txn/storage/mem/kv_txn_storage_test.go index 1f3d743f374ac..198c61d3f40a8 100644 --- a/pkg/txn/storage/mem/kv_txn_storage_test.go +++ b/pkg/txn/storage/mem/kv_txn_storage_test.go @@ -320,7 +320,7 @@ func committingTestTxn(t *testing.T, s *KVTxnStorage, wTxn *txn.TxnMeta, ts int6 func commitTestTxn(t *testing.T, s *KVTxnStorage, wTxn *txn.TxnMeta, ts int64, errCode uint16) { wTxn.CommitTS = newTimestamp(ts) - _, e := s.Commit(context.TODO(), *wTxn) + _, e := s.Commit(context.TODO(), *wTxn, nil, nil) assert.True(t, moerr.IsMoErrCode(e, errCode)) wTxn.Status = txn.TxnStatus_Committed } diff --git a/pkg/txn/storage/memorystorage/dynamic_storage.go b/pkg/txn/storage/memorystorage/dynamic_storage.go index f7b24832a7d25..646e3f945c6a6 100644 --- a/pkg/txn/storage/memorystorage/dynamic_storage.go +++ b/pkg/txn/storage/memorystorage/dynamic_storage.go @@ -59,12 +59,17 @@ func (d *DynamicStorage) Close(ctx context.Context) error { return storage.Close(ctx) } -func (d *DynamicStorage) Commit(ctx context.Context, txnMeta txn.TxnMeta) (timestamp.Timestamp, error) { +func (d *DynamicStorage) Commit( + ctx context.Context, + txnMeta txn.TxnMeta, + response *txn.TxnResponse, + commitRequests *txn.TxnCommitRequest, +) (timestamp.Timestamp, error) { storage, err := d.get(ctx) if err != nil { return timestamp.Timestamp{}, err } - return storage.Commit(ctx, txnMeta) + return storage.Commit(ctx, txnMeta, response, commitRequests) } func (d *DynamicStorage) Committing(ctx context.Context, txnMeta txn.TxnMeta) error { diff --git a/pkg/txn/storage/memorystorage/storage.go b/pkg/txn/storage/memorystorage/storage.go index be501979aee39..4467686802e5c 100644 --- a/pkg/txn/storage/memorystorage/storage.go +++ b/pkg/txn/storage/memorystorage/storage.go @@ -38,7 +38,12 @@ func New( var _ storage.TxnStorage = new(Storage) -func (s *Storage) Commit(ctx context.Context, txnMeta txn.TxnMeta) (timestamp.Timestamp, error) { +func (s *Storage) Commit( + ctx context.Context, + txnMeta txn.TxnMeta, + response *txn.TxnResponse, + commitRequests *txn.TxnCommitRequest, +) (timestamp.Timestamp, error) { return s.handler.HandleCommit(ctx, txnMeta) } diff --git a/pkg/txn/storage/memorystorage/storage_test.go b/pkg/txn/storage/memorystorage/storage_test.go index efce2d23c6579..af0ea9480b190 100644 --- a/pkg/txn/storage/memorystorage/storage_test.go +++ b/pkg/txn/storage/memorystorage/storage_test.go @@ -58,7 +58,7 @@ func testDatabase( }, } defer func() { - _, err := s.Commit(ctx, txnMeta) + _, err := s.Commit(ctx, txnMeta, nil, nil) assert.Nil(t, err) }() diff --git a/pkg/txn/storage/memorystorage/storage_txn_client.go b/pkg/txn/storage/memorystorage/storage_txn_client.go index 553953b6a2947..609e5f7de628e 100644 --- a/pkg/txn/storage/memorystorage/storage_txn_client.go +++ b/pkg/txn/storage/memorystorage/storage_txn_client.go @@ -198,7 +198,7 @@ func (s *StorageTxnOperator) Debug(ctx context.Context, ops []txn.TxnRequest) (* func (s *StorageTxnOperator) Commit(ctx context.Context) error { for _, storage := range s.storages { - if _, err := storage.Commit(ctx, s.meta); err != nil { + if _, err := storage.Commit(ctx, s.meta, nil, nil); err != nil { return err } } diff --git a/pkg/txn/storage/tae/storage.go b/pkg/txn/storage/tae/storage.go index 3bf717ed60ced..bbab55edbb8e8 100644 --- a/pkg/txn/storage/tae/storage.go +++ b/pkg/txn/storage/tae/storage.go @@ -86,8 +86,13 @@ func (s *taeStorage) Close(ctx context.Context) error { } // Commit implements storage.TxnTAEStorage -func (s *taeStorage) Commit(ctx context.Context, txnMeta txn.TxnMeta) (timestamp.Timestamp, error) { - return s.taeHandler.HandleCommit(ctx, txnMeta) +func (s *taeStorage) Commit( + ctx context.Context, + txnMeta txn.TxnMeta, + response *txn.TxnResponse, + commitRequests *txn.TxnCommitRequest, +) (timestamp.Timestamp, error) { + return s.taeHandler.HandleCommit(ctx, txnMeta, response, commitRequests) } // Committing implements storage.TxnTAEStorage diff --git a/pkg/txn/storage/tae/write.go b/pkg/txn/storage/tae/write.go index 393f25fcb0b28..355810301f76d 100644 --- a/pkg/txn/storage/tae/write.go +++ b/pkg/txn/storage/tae/write.go @@ -16,7 +16,6 @@ package taestorage import ( "context" - "github.com/matrixorigin/matrixone/pkg/common/moerr" apipb "github.com/matrixorigin/matrixone/pkg/pb/api" "github.com/matrixorigin/matrixone/pkg/pb/txn" @@ -28,9 +27,8 @@ func (s *taeStorage) Write( txnMeta txn.TxnMeta, op uint32, payload []byte) (result []byte, err error) { + switch op { - case uint32(apipb.OpCode_OpPreCommit): - return HandleWrite(ctx, txnMeta, payload, s.taeHandler.HandlePreCommitWrite) case uint32(apipb.OpCode_OpCommitMerge): return HandleWrite(ctx, txnMeta, payload, s.taeHandler.HandleCommitMerge) default: diff --git a/pkg/txn/storage/types.go b/pkg/txn/storage/types.go index 2859a26b28d56..6c2ff312a23a7 100644 --- a/pkg/txn/storage/types.go +++ b/pkg/txn/storage/types.go @@ -59,7 +59,11 @@ type TxnStorage interface { // of the transaction is logged to the LogService. Committing(ctx context.Context, txnMeta txn.TxnMeta) error // Commit commit the transaction. TxnStorage needs to do conflict locally. - Commit(ctx context.Context, txnMeta txn.TxnMeta) (timestamp.Timestamp, error) + Commit( + ctx context.Context, + txnMeta txn.TxnMeta, + response *txn.TxnResponse, + commitRequests *txn.TxnCommitRequest) (timestamp.Timestamp, error) // Rollback rollback the transaction. Rollback(ctx context.Context, txnMeta txn.TxnMeta) error diff --git a/pkg/vm/engine/tae/iface/rpchandle/handler.go b/pkg/vm/engine/tae/iface/rpchandle/handler.go index 46e93e56ef519..78d9f183cf139 100644 --- a/pkg/vm/engine/tae/iface/rpchandle/handler.go +++ b/pkg/vm/engine/tae/iface/rpchandle/handler.go @@ -27,6 +27,8 @@ type Handler interface { HandleCommit( ctx context.Context, meta txn.TxnMeta, + response *txn.TxnResponse, + commitRequests *txn.TxnCommitRequest, ) (timestamp.Timestamp, error) HandleRollback( diff --git a/pkg/vm/engine/tae/rpc/base_test.go b/pkg/vm/engine/tae/rpc/base_test.go index 66915aeccc5ad..6c09fa2d1efb8 100644 --- a/pkg/vm/engine/tae/rpc/base_test.go +++ b/pkg/vm/engine/tae/rpc/base_test.go @@ -70,7 +70,7 @@ func (h *mockHandle) HandleCommit(ctx context.Context, meta *txn.TxnMeta) (times if len(meta.TNShards) > 1 && meta.CommitTS.IsEmpty() { meta.CommitTS = meta.PreparedTS.Next() } - return h.Handle.HandleCommit(ctx, *meta) + return h.Handle.HandleCommit(ctx, *meta, nil, nil) } func (h *mockHandle) HandleCommitting(ctx context.Context, meta *txn.TxnMeta) error { diff --git a/pkg/vm/engine/tae/rpc/handle.go b/pkg/vm/engine/tae/rpc/handle.go index 06469ce34a946..68208f0f682d6 100644 --- a/pkg/vm/engine/tae/rpc/handle.go +++ b/pkg/vm/engine/tae/rpc/handle.go @@ -53,8 +53,6 @@ import ( "github.com/matrixorigin/matrixone/pkg/vm/engine/tae/iface/txnif" "github.com/matrixorigin/matrixone/pkg/vm/engine/tae/logtail" "github.com/matrixorigin/matrixone/pkg/vm/engine/tae/options" - "github.com/matrixorigin/matrixone/pkg/vm/engine/tae/tasks" - "go.uber.org/zap" ) @@ -64,9 +62,10 @@ const ( ) type Handle struct { - db *db.DB + db *db.DB + // only used for UT txnCtxs *common.Map[string, *txnContext] - GCJob *tasks.CancelableJob + //GCJob *tasks.CancelableJob interceptMatchRegexp atomic.Pointer[regexp.Regexp] } @@ -120,18 +119,9 @@ func NewTAEHandle(ctx context.Context, path string, opt *options.Options) *Handl h := &Handle{ db: tae, } + h.txnCtxs = common.NewMap[string, *txnContext](runtime.GOMAXPROCS(0)) h.interceptMatchRegexp.Store(regexp.MustCompile(`.*bmsql_stock.*`)) - h.GCJob = tasks.NewCancelableCronJob( - "clean-txn-cache", - MAX_TXN_COMMIT_LATENCY, - func(ctx context.Context) { - h.GCCache(time.Now()) - }, - true, - 1, - ) - h.GCJob.Start() return h } @@ -164,29 +154,6 @@ func (h *Handle) UpdateInterceptMatchRegexp(name string) { h.interceptMatchRegexp.Store(regexp.MustCompile(fmt.Sprintf(`.*%s.*`, name))) } -// TODO: vast items within h.mu.txnCtxs would incur performance penality. -func (h *Handle) GCCache(now time.Time) error { - - var ( - cnt, deleteCnt int - ) - - h.txnCtxs.DeleteIf(func(k string, v *txnContext) bool { - cnt++ - ok := v.deadline.Before(now) - if ok { - deleteCnt++ - } - return ok - }) - logutil.Info( - "GC-RPC-Cache", - zap.Int("total", cnt), - zap.Int("deleted", deleteCnt), - ) - return nil -} - func (h *Handle) CacheTxnRequest( ctx context.Context, meta txn.TxnMeta, @@ -274,23 +241,114 @@ func (h *Handle) tryLockMergeForBulkDelete(reqs []any, txn txnif.AsyncTxn) (rele return } +type txnCommitRequestsIter struct { + cursor int + curNorReq *api.PrecommitWriteCmd + commitRequests *txn.TxnCommitRequest + + // cache requests only used in ut + cached []any +} + +func (h *Handle) newTxnCommitRequestsIter( + cr *txn.TxnCommitRequest, + meta txn.TxnMeta, +) *txnCommitRequestsIter { + + // in the normal commit processes, the new logic won't cache the write requests anymore. + // however, there exist massive ut code that verified the preCommit-commit 2PC logic, + // which cached the write requests in the preCommit call. + // to keep that, there also leave the commiting code of the cached requests un-changed, but only for ut. + if cr == nil { + // for now, only test will into this logic + key := util.UnsafeBytesToString(meta.GetID()) + txnCtx, ok := h.txnCtxs.Load(key) + if !ok { + // no requests + return nil + } + + defer h.txnCtxs.Delete(key) + + return &txnCommitRequestsIter{ + cursor: 0, + cached: txnCtx.reqs, + commitRequests: nil, + } + + } else { + return &txnCommitRequestsIter{ + cursor: 0, + cached: nil, + commitRequests: cr, + } + } +} + +func (cri *txnCommitRequestsIter) Next() bool { + if cri.commitRequests == nil { + return cri.cursor < len(cri.cached) + } + return cri.cursor < len(cri.commitRequests.Payload) +} + +func (cri *txnCommitRequestsIter) Entry() (entry any, err error) { + + if cri.commitRequests == nil { + entry = cri.cached[cri.cursor] + cri.cursor++ + return + } + + cnReq := cri.commitRequests.Payload[cri.cursor].CNRequest + + if cri.curNorReq == nil { + cri.curNorReq = new(api.PrecommitWriteCmd) + } + + if len(cri.curNorReq.EntryList) == 0 { + if err = cri.curNorReq.UnmarshalBinary(cnReq.Payload); err != nil { + return + } + } + + entry, cri.curNorReq.EntryList, err = pkgcatalog.ParseEntryList(cri.curNorReq.EntryList) + if len(cri.curNorReq.EntryList) == 0 { + cri.cursor++ + } + + return +} + func (h *Handle) handleRequests( ctx context.Context, txn txnif.AsyncTxn, - txnCtx *txnContext, + commitRequests *txn.TxnCommitRequest, + response *txn.TxnResponse, + txnMeta txn.TxnMeta, ) (releaseF []func(), hasDDL bool, err error) { + var ( + entry any + + iter *txnCommitRequestsIter + inMemoryInsertRows int persistedMemoryInsertRows int inMemoryTombstoneRows int persistedTombstoneRows int ) - releaseF, err = h.tryLockMergeForBulkDelete(txnCtx.reqs, txn) - if err != nil { - logutil.Warn("failed to lock merging", zap.Error(err)) + + if iter = h.newTxnCommitRequestsIter(commitRequests, txnMeta); iter == nil { + return } - for _, e := range txnCtx.reqs { - switch req := e.(type) { + + for iter.Next() { + if entry, err = iter.Entry(); err != nil { + return + } + + switch req := entry.(type) { case *pkgcatalog.CreateDatabaseReq: hasDDL = true err = h.HandleCreateDatabase(ctx, txn, req) @@ -306,18 +364,45 @@ func (h *Handle) handleRequests( case *api.AlterTableReq: hasDDL = true err = h.HandleAlterTable(ctx, txn, req) - case *cmd_util.WriteReq: + case []*api.AlterTableReq: + hasDDL = true + for _, r := range req { + if err = h.HandleAlterTable(ctx, txn, r); err != nil { + return + } + } + + case *cmd_util.WriteReq, *api.Entry: + var wr *cmd_util.WriteReq + if ae, ok := req.(*api.Entry); ok { + wr = h.apiEntryToWriteEntry(ctx, txnMeta, ae, true) + } else { + wr = req.(*cmd_util.WriteReq) + } + + if wr.Type == cmd_util.EntryDelete { + var f []func() + if f, err = h.tryLockMergeForBulkDelete([]any{req}, txn); err != nil { + logutil.Warn("failed to lock merging", zap.Error(err)) + return + } + + releaseF = append(releaseF, f...) + } + var r1, r2, r3, r4 int - r1, r2, r3, r4, err = h.HandleWrite(ctx, txn, req) + r1, r2, r3, r4, err = h.HandleWrite(ctx, txn, wr) if err == nil { inMemoryInsertRows += r1 persistedMemoryInsertRows += r2 inMemoryTombstoneRows += r3 persistedTombstoneRows += r4 } + default: err = moerr.NewNotSupportedf(ctx, "unknown txn request type: %T", req) } + //Need to roll back the txn. if err != nil { txn.Rollback(ctx) @@ -342,7 +427,57 @@ func (h *Handle) handleRequests( //#region Impl TxnStorage interface //order by call frequency +func (h *Handle) apiEntryToWriteEntry( + ctx context.Context, + meta txn.TxnMeta, + pe *api.Entry, + prefetch bool, +) *cmd_util.WriteReq { + + moBat, err := batch.ProtoBatchToBatch(pe.GetBat()) + if err != nil { + panic(err) + } + req := &cmd_util.WriteReq{ + Type: cmd_util.EntryType(pe.EntryType), + DatabaseId: pe.GetDatabaseId(), + TableID: pe.GetTableId(), + DatabaseName: pe.GetDatabaseName(), + TableName: pe.GetTableName(), + FileName: pe.GetFileName(), + Batch: moBat, + PkCheck: cmd_util.PKCheckType(pe.GetPkCheckByTn()), + } + + if req.FileName != "" { + col := req.Batch.Vecs[0] + for i := 0; i < req.Batch.RowCount(); i++ { + stats := objectio.ObjectStats(col.GetBytesAt(i)) + if req.Type == cmd_util.EntryInsert { + req.DataObjectStats = append(req.DataObjectStats, stats) + } else { + req.TombstoneStats = append(req.TombstoneStats, stats) + } + } + } + + if prefetch { + if req.Type == cmd_util.EntryDelete { + if err = h.prefetchDeleteRowID(ctx, req, &meta); err != nil { + return nil + } + } else { + if err = h.prefetchMetadata(ctx, req, &meta); err != nil { + return nil + } + } + } + + return req +} + // HandlePreCommitWrite impls TxnStorage:Write +// only ut call this func (h *Handle) HandlePreCommitWrite( ctx context.Context, meta txn.TxnMeta, @@ -369,34 +504,8 @@ func (h *Handle) HandlePreCommitWrite( } case *api.Entry: //Handle DML - pe := e.(*api.Entry) - moBat, err := batch.ProtoBatchToBatch(pe.GetBat()) - if err != nil { - panic(err) - } - req := &cmd_util.WriteReq{ - Type: cmd_util.EntryType(pe.EntryType), - DatabaseId: pe.GetDatabaseId(), - TableID: pe.GetTableId(), - DatabaseName: pe.GetDatabaseName(), - TableName: pe.GetTableName(), - FileName: pe.GetFileName(), - Batch: moBat, - PkCheck: cmd_util.PKCheckType(pe.GetPkCheckByTn()), - } - - if req.FileName != "" { - col := req.Batch.Vecs[0] - for i := 0; i < req.Batch.RowCount(); i++ { - stats := objectio.ObjectStats(col.GetBytesAt(i)) - if req.Type == cmd_util.EntryInsert { - req.DataObjectStats = append(req.DataObjectStats, stats) - } else { - req.TombstoneStats = append(req.TombstoneStats, stats) - } - } - } - if err = h.CacheTxnRequest(ctx, meta, req); err != nil { + wr := h.apiEntryToWriteEntry(ctx, meta, e.(*api.Entry), false) + if err = h.CacheTxnRequest(ctx, meta, wr); err != nil { return err } default: @@ -411,9 +520,11 @@ func (h *Handle) HandlePreCommitWrite( func (h *Handle) HandleCommit( ctx context.Context, meta txn.TxnMeta, + response *txn.TxnResponse, + commitRequests *txn.TxnCommitRequest, ) (cts timestamp.Timestamp, err error) { start := time.Now() - txnCtx, ok := h.txnCtxs.Load(util.UnsafeBytesToString(meta.GetID())) + var ( txn txnif.AsyncTxn releaseF []func() @@ -423,10 +534,7 @@ func (h *Handle) HandleCommit( for _, f := range releaseF { f() } - if ok { - //delete the txn's context. - h.txnCtxs.Delete(util.UnsafeBytesToString(meta.GetID())) - } + common.DoIfInfoEnabled(func() { _, _, injected := fault.TriggerFault(objectio.FJ_CommitSlowLog) if time.Since(start) > MAX_ALLOWED_TXN_LATENCY || err != nil || hasDDL || injected { @@ -453,18 +561,17 @@ func (h *Handle) HandleCommit( } }) }() - if ok { - //Handle precommit-write command for 1PC - txn, err = h.db.GetOrCreateTxnWithMeta(nil, meta.GetID(), - types.TimestampToTS(meta.GetSnapshotTS())) - if err != nil { - return - } - releaseF, hasDDL, err = h.handleRequests(ctx, txn, txnCtx) - if err != nil { - return - } + + if txn, err = h.db.GetOrCreateTxnWithMeta( + nil, meta.GetID(), types.TimestampToTS(meta.GetSnapshotTS())); err != nil { + return } + + if releaseF, hasDDL, err = h.handleRequests( + ctx, txn, commitRequests, response, meta); err != nil { + return + } + txn, err = h.db.GetTxnByID(meta.GetID()) if err != nil { return @@ -498,7 +605,7 @@ func (h *Handle) HandleCommit( zap.String("new-txn", txn.GetID()), ) //Handle precommit-write command for 1PC - releaseF, hasDDL, err = h.handleRequests(ctx, txn, txnCtx) + releaseF, hasDDL, err = h.handleRequests(ctx, txn, commitRequests, response, meta) if err != nil && !moerr.IsMoErrCode(err, moerr.ErrTAENeedRetry) { break } @@ -556,9 +663,9 @@ func (h *Handle) HandleRollback( func (h *Handle) HandleClose(ctx context.Context) (err error) { //FIXME::should wait txn request's job done? - if h.GCJob != nil { - h.GCJob.Stop() - } + //if h.GCJob != nil { + // h.GCJob.Stop() + //} return h.db.Close() } diff --git a/pkg/vm/engine/tae/rpc/handle_2pc.go b/pkg/vm/engine/tae/rpc/handle_2pc.go index 13d7b4108ae36..adb0e5cbd11e8 100644 --- a/pkg/vm/engine/tae/rpc/handle_2pc.go +++ b/pkg/vm/engine/tae/rpc/handle_2pc.go @@ -17,7 +17,6 @@ package rpc import ( "context" - "github.com/matrixorigin/matrixone/pkg/common/util" "github.com/matrixorigin/matrixone/pkg/container/types" "github.com/matrixorigin/matrixone/pkg/pb/timestamp" "github.com/matrixorigin/matrixone/pkg/pb/txn" @@ -45,23 +44,16 @@ func (h *Handle) HandleCommitting( func (h *Handle) HandlePrepare( ctx context.Context, meta txn.TxnMeta) (pts timestamp.Timestamp, err error) { - txnCtx, ok := h.txnCtxs.Load(util.UnsafeBytesToString(meta.GetID())) var txn txnif.AsyncTxn - defer func() { - if ok { - //delete the txn's context. - h.txnCtxs.Delete(util.UnsafeBytesToString(meta.GetID())) - } - }() - if ok { - //handle pre-commit write for 2PC - txn, err = h.db.GetOrCreateTxnWithMeta(nil, meta.GetID(), - types.TimestampToTS(meta.GetSnapshotTS())) - if err != nil { - return - } - h.handleRequests(ctx, txn, txnCtx) + + //handle pre-commit write for 2PC + txn, err = h.db.GetOrCreateTxnWithMeta(nil, meta.GetID(), + types.TimestampToTS(meta.GetSnapshotTS())) + if err != nil { + return } + h.handleRequests(ctx, txn, nil, nil, meta) + txn, err = h.db.GetTxnByID(meta.GetID()) if err != nil { return timestamp.Timestamp{}, err diff --git a/pkg/vm/engine/tae/rpc/handle_test.go b/pkg/vm/engine/tae/rpc/handle_test.go index de28d4a318f94..9b4e3cfb3cf4a 100644 --- a/pkg/vm/engine/tae/rpc/handle_test.go +++ b/pkg/vm/engine/tae/rpc/handle_test.go @@ -16,47 +16,17 @@ package rpc import ( "context" - "testing" - "time" - "github.com/matrixorigin/matrixone/pkg/container/types" apipb "github.com/matrixorigin/matrixone/pkg/pb/api" "github.com/matrixorigin/matrixone/pkg/pb/txn" "github.com/matrixorigin/matrixone/pkg/vm/engine/cmd_util" "github.com/matrixorigin/matrixone/pkg/vm/engine/tae/catalog" - "github.com/matrixorigin/matrixone/pkg/vm/engine/tae/common" "github.com/matrixorigin/matrixone/pkg/vm/engine/tae/db/testutil" "github.com/matrixorigin/matrixone/pkg/vm/engine/tae/options" "github.com/stretchr/testify/require" + "testing" ) -func TestHandleGCCache(t *testing.T) { - now := time.Now() - expired := now.Add(-MAX_TXN_COMMIT_LATENCY).Add(-time.Second) - - handle := Handle{} - handle.txnCtxs = common.NewMap[string, *txnContext](10) - handle.txnCtxs.Store("now", &txnContext{ - deadline: now, - }) - handle.txnCtxs.Store("expired", &txnContext{ - deadline: expired, - }) - handle.GCCache(now) - - cnt := 0 - handle.txnCtxs.Range(func(key string, value *txnContext) bool { - cnt++ - return true - }) - - require.Equal(t, 1, cnt) - _, ok := handle.txnCtxs.Load("expired") - require.False(t, ok) - _, ok = handle.txnCtxs.Load("now") - require.True(t, ok) -} - func TestHandleInspectPolicy(t *testing.T) { handle := mockTAEHandle(context.Background(), t, &options.Options{}) asyncTxn, err := handle.db.StartTxn(nil) diff --git a/pkg/vm/engine/test/testutil/tae_engine.go b/pkg/vm/engine/test/testutil/tae_engine.go index b2e8ae75954c7..79783a06a3009 100644 --- a/pkg/vm/engine/test/testutil/tae_engine.go +++ b/pkg/vm/engine/test/testutil/tae_engine.go @@ -70,7 +70,6 @@ func (ts *TestTxnStorage) Close(destroy bool) error { firstErr = err } } - ts.txnHandler.GCJob.Stop() blockio.Stop("") return firstErr } @@ -98,20 +97,9 @@ func (ts *TestTxnStorage) Commit(ctx context.Context, request *txn.TxnRequest, r resp.Txn = &req.Txn } - if request.CommitRequest != nil { - for _, req := range request.CommitRequest.Payload { - //response is shared by all requests - prepareResponse(req, response) - err := ts.Write(ctx, req, response) - if err != nil { - return err - } - } - } - prepareResponse(request, response) - cts, err := ts.txnHandler.HandleCommit(ctx, request.Txn) + cts, err := ts.txnHandler.HandleCommit(ctx, request.Txn, response, request.CommitRequest) if err == nil { response.Txn.Status = txn.TxnStatus_Committed response.Txn.CommitTS = cts From 2219aa01fd4f226e0209540d099f9ed6eda9b8a7 Mon Sep 17 00:00:00 2001 From: aptend <49832303+aptend@users.noreply.github.com> Date: Mon, 23 Dec 2024 20:22:48 +0800 Subject: [PATCH 26/26] fix panic log in cancelable job (#20877) fix nil panic Approved by: @XuPeng-SH --- pkg/vm/engine/tae/db/checkpoint/runner.go | 2 +- pkg/vm/engine/tae/tasks/cancelablejob.go | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/pkg/vm/engine/tae/db/checkpoint/runner.go b/pkg/vm/engine/tae/db/checkpoint/runner.go index d3459c6dd7569..a9d65ec88c104 100644 --- a/pkg/vm/engine/tae/db/checkpoint/runner.go +++ b/pkg/vm/engine/tae/db/checkpoint/runner.go @@ -618,7 +618,7 @@ func (r *runner) softScheduleCheckpoint(ts *types.TS) (ret *CheckpointEntry, err if intent != nil { intentInfo = intent.String() } - if (err != nil && err != ErrPendingCheckpoint) || intent.TooOld() { + if (err != nil && err != ErrPendingCheckpoint) || (intent != nil && intent.TooOld()) { logger( "ICKP-Schedule-Soft", zap.String("intent", intentInfo), diff --git a/pkg/vm/engine/tae/tasks/cancelablejob.go b/pkg/vm/engine/tae/tasks/cancelablejob.go index daa8ae661ff7d..8a89aa47da598 100644 --- a/pkg/vm/engine/tae/tasks/cancelablejob.go +++ b/pkg/vm/engine/tae/tasks/cancelablejob.go @@ -17,6 +17,7 @@ package tasks import ( "context" "fmt" + "runtime/debug" "sync" "time" @@ -186,6 +187,7 @@ func (ctl *CancelableJob) Start() { "Panic-In-CronJob", zap.String("name", ctl.name), zap.Any("reason", r), + zap.String("stack", string(debug.Stack())), ) } }()