From 37ed2f47b4b22b67f49896da9efa07324e2b85af Mon Sep 17 00:00:00 2001 From: sumeerbhola Date: Fri, 30 Oct 2020 16:18:13 -0400 Subject: [PATCH] sql: add support for joinReader to be the first join in paired joins This is to allow lookup joins to be used for left outer/semi/anti joins with non-covering indexes. Currently only semi joins for this case can use the index (by doing two inner joins and a DistinctOn) Paired joins with a non-covering index will be used as follows: - Left outer join: will become a pair of left outer lookup joins. - Left anti join: will be a left outer lookup join followed by a left anti lookup join. - Left semi join: will be an inner lookup join followed by a left semi lookup join. This PR does not contain the optimizer changes. Informs #55452 Release note: None --- pkg/sql/execinfra/version.go | 9 +- pkg/sql/execinfrapb/processors_sql.pb.go | 452 +++++++++++++---------- pkg/sql/execinfrapb/processors_sql.proto | 15 +- pkg/sql/rowexec/hashjoiner.go | 1 + pkg/sql/rowexec/joinerbase.go | 31 +- pkg/sql/rowexec/joinreader.go | 59 +-- pkg/sql/rowexec/joinreader_strategies.go | 24 +- pkg/sql/rowexec/joinreader_test.go | 451 +++++++++++++--------- pkg/sql/rowexec/mergejoiner.go | 3 +- pkg/sql/rowexec/zigzagjoiner.go | 1 + 10 files changed, 633 insertions(+), 413 deletions(-) diff --git a/pkg/sql/execinfra/version.go b/pkg/sql/execinfra/version.go index 6371c0897e39..f018c67ce1c8 100644 --- a/pkg/sql/execinfra/version.go +++ b/pkg/sql/execinfra/version.go @@ -39,7 +39,7 @@ import "github.com/cockroachdb/cockroach/pkg/sql/execinfrapb" // // ATTENTION: When updating these fields, add a brief description of what // changed to the version history below. -const Version execinfrapb.DistSQLVersion = 40 +const Version execinfrapb.DistSQLVersion = 41 // MinAcceptedVersion is the oldest version that the server is compatible with. // A server will not accept flows with older versions. @@ -51,6 +51,11 @@ const MinAcceptedVersion execinfrapb.DistSQLVersion = 40 Please add new entries at the top. +- Version: 41 (MinAcceptedVersion: 40) + - A paired joiner approach for lookup joins was added, for left + outer/semi/anti joins involving a pair of joinReaders, where the + first join uses a non-covering index. + - Version: 40 (MinAcceptedVersion: 40) - A new field was added execinfrapb.ProcessorSpec to propagate the result column types of a processor. This change is not backwards compatible @@ -62,7 +67,7 @@ Please add new entries at the top. protobuf struct for easier propagation to the remote nodes during the execution. -- Version: 38 (MinAcceptedVersion: 38) +- Version: 38 (MinAcceptedVersion: 37) - A paired joiner approach for inverted joins was added, for left outer/semi/anti joins involving the invertedJoiner and joinReader. diff --git a/pkg/sql/execinfrapb/processors_sql.pb.go b/pkg/sql/execinfrapb/processors_sql.pb.go index e913f3f14962..17bcb93bf133 100644 --- a/pkg/sql/execinfrapb/processors_sql.pb.go +++ b/pkg/sql/execinfrapb/processors_sql.pb.go @@ -64,7 +64,7 @@ func (x *ScanVisibility) UnmarshalJSON(data []byte) error { return nil } func (ScanVisibility) EnumDescriptor() ([]byte, []int) { - return fileDescriptor_processors_sql_aba6ce960cf7b022, []int{0} + return fileDescriptor_processors_sql_99047956bc65c02c, []int{0} } // These mirror the aggregate functions supported by sql/parser. See @@ -205,7 +205,7 @@ func (x *AggregatorSpec_Func) UnmarshalJSON(data []byte) error { return nil } func (AggregatorSpec_Func) EnumDescriptor() ([]byte, []int) { - return fileDescriptor_processors_sql_aba6ce960cf7b022, []int{12, 0} + return fileDescriptor_processors_sql_99047956bc65c02c, []int{12, 0} } type AggregatorSpec_Type int32 @@ -251,7 +251,7 @@ func (x *AggregatorSpec_Type) UnmarshalJSON(data []byte) error { return nil } func (AggregatorSpec_Type) EnumDescriptor() ([]byte, []int) { - return fileDescriptor_processors_sql_aba6ce960cf7b022, []int{12, 1} + return fileDescriptor_processors_sql_99047956bc65c02c, []int{12, 1} } type WindowerSpec_WindowFunc int32 @@ -315,7 +315,7 @@ func (x *WindowerSpec_WindowFunc) UnmarshalJSON(data []byte) error { return nil } func (WindowerSpec_WindowFunc) EnumDescriptor() ([]byte, []int) { - return fileDescriptor_processors_sql_aba6ce960cf7b022, []int{14, 0} + return fileDescriptor_processors_sql_99047956bc65c02c, []int{14, 0} } // Mode indicates which mode of framing is used. @@ -359,7 +359,7 @@ func (x *WindowerSpec_Frame_Mode) UnmarshalJSON(data []byte) error { return nil } func (WindowerSpec_Frame_Mode) EnumDescriptor() ([]byte, []int) { - return fileDescriptor_processors_sql_aba6ce960cf7b022, []int{14, 1, 0} + return fileDescriptor_processors_sql_99047956bc65c02c, []int{14, 1, 0} } // BoundType indicates which type of boundary is used. @@ -406,7 +406,7 @@ func (x *WindowerSpec_Frame_BoundType) UnmarshalJSON(data []byte) error { return nil } func (WindowerSpec_Frame_BoundType) EnumDescriptor() ([]byte, []int) { - return fileDescriptor_processors_sql_aba6ce960cf7b022, []int{14, 1, 1} + return fileDescriptor_processors_sql_99047956bc65c02c, []int{14, 1, 1} } // Exclusion specifies the type of frame exclusion. @@ -449,7 +449,7 @@ func (x *WindowerSpec_Frame_Exclusion) UnmarshalJSON(data []byte) error { return nil } func (WindowerSpec_Frame_Exclusion) EnumDescriptor() ([]byte, []int) { - return fileDescriptor_processors_sql_aba6ce960cf7b022, []int{14, 1, 2} + return fileDescriptor_processors_sql_99047956bc65c02c, []int{14, 1, 2} } // ValuesCoreSpec is the core of a processor that has no inputs and generates @@ -469,7 +469,7 @@ func (m *ValuesCoreSpec) Reset() { *m = ValuesCoreSpec{} } func (m *ValuesCoreSpec) String() string { return proto.CompactTextString(m) } func (*ValuesCoreSpec) ProtoMessage() {} func (*ValuesCoreSpec) Descriptor() ([]byte, []int) { - return fileDescriptor_processors_sql_aba6ce960cf7b022, []int{0} + return fileDescriptor_processors_sql_99047956bc65c02c, []int{0} } func (m *ValuesCoreSpec) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -576,7 +576,7 @@ func (m *TableReaderSpec) Reset() { *m = TableReaderSpec{} } func (m *TableReaderSpec) String() string { return proto.CompactTextString(m) } func (*TableReaderSpec) ProtoMessage() {} func (*TableReaderSpec) Descriptor() ([]byte, []int) { - return fileDescriptor_processors_sql_aba6ce960cf7b022, []int{1} + return fileDescriptor_processors_sql_99047956bc65c02c, []int{1} } func (m *TableReaderSpec) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -631,7 +631,7 @@ func (m *IndexSkipTableReaderSpec) Reset() { *m = IndexSkipTableReaderSp func (m *IndexSkipTableReaderSpec) String() string { return proto.CompactTextString(m) } func (*IndexSkipTableReaderSpec) ProtoMessage() {} func (*IndexSkipTableReaderSpec) Descriptor() ([]byte, []int) { - return fileDescriptor_processors_sql_aba6ce960cf7b022, []int{2} + return fileDescriptor_processors_sql_99047956bc65c02c, []int{2} } func (m *IndexSkipTableReaderSpec) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -727,6 +727,11 @@ var xxx_messageInfo_IndexSkipTableReaderSpec proto.InternalMessageInfo // The output for LEFT_SEMI: // a2, b1, c1, d4, true // Again, the d, cont columns will be projected away after the join. +// +// The example above is for a lookup join as the second join in the +// paired-joins. The lookup join can also be the first join in the +// paired-joins, which is indicated by both +// OutputGroupContinuationForLeftRow and MaintainOrdering set to true. type JoinReaderSpec struct { Table descpb.TableDescriptor `protobuf:"bytes,1,opt,name=table" json:"table"` // If 0, we use the primary index; each row in the input stream has a value @@ -782,15 +787,22 @@ type JoinReaderSpec struct { // expected to contain the materialized system columns. HasSystemColumns bool `protobuf:"varint,13,opt,name=has_system_columns,json=hasSystemColumns" json:"has_system_columns"` // LeftJoinWithPairedJoiner is used when a left {outer,anti,semi} join is - // being achieved by pairing two joins. See the comment above. + // being achieved by pairing two joins, and this is the second join. See + // the comment above. LeftJoinWithPairedJoiner bool `protobuf:"varint,14,opt,name=left_join_with_paired_joiner,json=leftJoinWithPairedJoiner" json:"left_join_with_paired_joiner"` + // OutputGroupContinuationForLeftRow indicates that this join is the first + // join in the paired-joins. At most one of OutputGroupContinuationForLeftRow + // and LeftJoinWithPairedJoiner must be true. Additionally, if + // OutputGroupContinuationForLeftRow is true, MaintainOrdering must also + // be true. + OutputGroupContinuationForLeftRow bool `protobuf:"varint,15,opt,name=output_group_continuation_for_left_row,json=outputGroupContinuationForLeftRow" json:"output_group_continuation_for_left_row"` } func (m *JoinReaderSpec) Reset() { *m = JoinReaderSpec{} } func (m *JoinReaderSpec) String() string { return proto.CompactTextString(m) } func (*JoinReaderSpec) ProtoMessage() {} func (*JoinReaderSpec) Descriptor() ([]byte, []int) { - return fileDescriptor_processors_sql_aba6ce960cf7b022, []int{3} + return fileDescriptor_processors_sql_99047956bc65c02c, []int{3} } func (m *JoinReaderSpec) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -834,7 +846,7 @@ func (m *SorterSpec) Reset() { *m = SorterSpec{} } func (m *SorterSpec) String() string { return proto.CompactTextString(m) } func (*SorterSpec) ProtoMessage() {} func (*SorterSpec) Descriptor() ([]byte, []int) { - return fileDescriptor_processors_sql_aba6ce960cf7b022, []int{4} + return fileDescriptor_processors_sql_99047956bc65c02c, []int{4} } func (m *SorterSpec) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -896,7 +908,7 @@ func (m *DistinctSpec) Reset() { *m = DistinctSpec{} } func (m *DistinctSpec) String() string { return proto.CompactTextString(m) } func (*DistinctSpec) ProtoMessage() {} func (*DistinctSpec) Descriptor() ([]byte, []int) { - return fileDescriptor_processors_sql_aba6ce960cf7b022, []int{5} + return fileDescriptor_processors_sql_99047956bc65c02c, []int{5} } func (m *DistinctSpec) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -931,7 +943,7 @@ func (m *OrdinalitySpec) Reset() { *m = OrdinalitySpec{} } func (m *OrdinalitySpec) String() string { return proto.CompactTextString(m) } func (*OrdinalitySpec) ProtoMessage() {} func (*OrdinalitySpec) Descriptor() ([]byte, []int) { - return fileDescriptor_processors_sql_aba6ce960cf7b022, []int{6} + return fileDescriptor_processors_sql_99047956bc65c02c, []int{6} } func (m *OrdinalitySpec) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -989,7 +1001,7 @@ func (m *ZigzagJoinerSpec) Reset() { *m = ZigzagJoinerSpec{} } func (m *ZigzagJoinerSpec) String() string { return proto.CompactTextString(m) } func (*ZigzagJoinerSpec) ProtoMessage() {} func (*ZigzagJoinerSpec) Descriptor() ([]byte, []int) { - return fileDescriptor_processors_sql_aba6ce960cf7b022, []int{7} + return fileDescriptor_processors_sql_99047956bc65c02c, []int{7} } func (m *ZigzagJoinerSpec) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1065,7 +1077,7 @@ func (m *MergeJoinerSpec) Reset() { *m = MergeJoinerSpec{} } func (m *MergeJoinerSpec) String() string { return proto.CompactTextString(m) } func (*MergeJoinerSpec) ProtoMessage() {} func (*MergeJoinerSpec) Descriptor() ([]byte, []int) { - return fileDescriptor_processors_sql_aba6ce960cf7b022, []int{8} + return fileDescriptor_processors_sql_99047956bc65c02c, []int{8} } func (m *MergeJoinerSpec) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1135,7 +1147,7 @@ func (m *HashJoinerSpec) Reset() { *m = HashJoinerSpec{} } func (m *HashJoinerSpec) String() string { return proto.CompactTextString(m) } func (*HashJoinerSpec) ProtoMessage() {} func (*HashJoinerSpec) Descriptor() ([]byte, []int) { - return fileDescriptor_processors_sql_aba6ce960cf7b022, []int{9} + return fileDescriptor_processors_sql_99047956bc65c02c, []int{9} } func (m *HashJoinerSpec) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1251,7 +1263,7 @@ func (m *InvertedJoinerSpec) Reset() { *m = InvertedJoinerSpec{} } func (m *InvertedJoinerSpec) String() string { return proto.CompactTextString(m) } func (*InvertedJoinerSpec) ProtoMessage() {} func (*InvertedJoinerSpec) Descriptor() ([]byte, []int) { - return fileDescriptor_processors_sql_aba6ce960cf7b022, []int{10} + return fileDescriptor_processors_sql_99047956bc65c02c, []int{10} } func (m *InvertedJoinerSpec) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1312,7 +1324,7 @@ func (m *InvertedFiltererSpec) Reset() { *m = InvertedFiltererSpec{} } func (m *InvertedFiltererSpec) String() string { return proto.CompactTextString(m) } func (*InvertedFiltererSpec) ProtoMessage() {} func (*InvertedFiltererSpec) Descriptor() ([]byte, []int) { - return fileDescriptor_processors_sql_aba6ce960cf7b022, []int{11} + return fileDescriptor_processors_sql_99047956bc65c02c, []int{11} } func (m *InvertedFiltererSpec) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1350,7 +1362,7 @@ func (m *InvertedFiltererSpec_PreFiltererSpec) Reset() { *m = InvertedFi func (m *InvertedFiltererSpec_PreFiltererSpec) String() string { return proto.CompactTextString(m) } func (*InvertedFiltererSpec_PreFiltererSpec) ProtoMessage() {} func (*InvertedFiltererSpec_PreFiltererSpec) Descriptor() ([]byte, []int) { - return fileDescriptor_processors_sql_aba6ce960cf7b022, []int{11, 0} + return fileDescriptor_processors_sql_99047956bc65c02c, []int{11, 0} } func (m *InvertedFiltererSpec_PreFiltererSpec) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1396,7 +1408,7 @@ func (m *AggregatorSpec) Reset() { *m = AggregatorSpec{} } func (m *AggregatorSpec) String() string { return proto.CompactTextString(m) } func (*AggregatorSpec) ProtoMessage() {} func (*AggregatorSpec) Descriptor() ([]byte, []int) { - return fileDescriptor_processors_sql_aba6ce960cf7b022, []int{12} + return fileDescriptor_processors_sql_99047956bc65c02c, []int{12} } func (m *AggregatorSpec) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1447,7 +1459,7 @@ func (m *AggregatorSpec_Aggregation) Reset() { *m = AggregatorSpec_Aggre func (m *AggregatorSpec_Aggregation) String() string { return proto.CompactTextString(m) } func (*AggregatorSpec_Aggregation) ProtoMessage() {} func (*AggregatorSpec_Aggregation) Descriptor() ([]byte, []int) { - return fileDescriptor_processors_sql_aba6ce960cf7b022, []int{12, 0} + return fileDescriptor_processors_sql_99047956bc65c02c, []int{12, 0} } func (m *AggregatorSpec_Aggregation) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1487,7 +1499,7 @@ func (m *ProjectSetSpec) Reset() { *m = ProjectSetSpec{} } func (m *ProjectSetSpec) String() string { return proto.CompactTextString(m) } func (*ProjectSetSpec) ProtoMessage() {} func (*ProjectSetSpec) Descriptor() ([]byte, []int) { - return fileDescriptor_processors_sql_aba6ce960cf7b022, []int{13} + return fileDescriptor_processors_sql_99047956bc65c02c, []int{13} } func (m *ProjectSetSpec) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1529,7 +1541,7 @@ func (m *WindowerSpec) Reset() { *m = WindowerSpec{} } func (m *WindowerSpec) String() string { return proto.CompactTextString(m) } func (*WindowerSpec) ProtoMessage() {} func (*WindowerSpec) Descriptor() ([]byte, []int) { - return fileDescriptor_processors_sql_aba6ce960cf7b022, []int{14} + return fileDescriptor_processors_sql_99047956bc65c02c, []int{14} } func (m *WindowerSpec) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1565,7 +1577,7 @@ func (m *WindowerSpec_Func) Reset() { *m = WindowerSpec_Func{} } func (m *WindowerSpec_Func) String() string { return proto.CompactTextString(m) } func (*WindowerSpec_Func) ProtoMessage() {} func (*WindowerSpec_Func) Descriptor() ([]byte, []int) { - return fileDescriptor_processors_sql_aba6ce960cf7b022, []int{14, 0} + return fileDescriptor_processors_sql_99047956bc65c02c, []int{14, 0} } func (m *WindowerSpec_Func) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1601,7 +1613,7 @@ func (m *WindowerSpec_Frame) Reset() { *m = WindowerSpec_Frame{} } func (m *WindowerSpec_Frame) String() string { return proto.CompactTextString(m) } func (*WindowerSpec_Frame) ProtoMessage() {} func (*WindowerSpec_Frame) Descriptor() ([]byte, []int) { - return fileDescriptor_processors_sql_aba6ce960cf7b022, []int{14, 1} + return fileDescriptor_processors_sql_99047956bc65c02c, []int{14, 1} } func (m *WindowerSpec_Frame) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1642,7 +1654,7 @@ func (m *WindowerSpec_Frame_Bound) Reset() { *m = WindowerSpec_Frame_Bou func (m *WindowerSpec_Frame_Bound) String() string { return proto.CompactTextString(m) } func (*WindowerSpec_Frame_Bound) ProtoMessage() {} func (*WindowerSpec_Frame_Bound) Descriptor() ([]byte, []int) { - return fileDescriptor_processors_sql_aba6ce960cf7b022, []int{14, 1, 0} + return fileDescriptor_processors_sql_99047956bc65c02c, []int{14, 1, 0} } func (m *WindowerSpec_Frame_Bound) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1678,7 +1690,7 @@ func (m *WindowerSpec_Frame_Bounds) Reset() { *m = WindowerSpec_Frame_Bo func (m *WindowerSpec_Frame_Bounds) String() string { return proto.CompactTextString(m) } func (*WindowerSpec_Frame_Bounds) ProtoMessage() {} func (*WindowerSpec_Frame_Bounds) Descriptor() ([]byte, []int) { - return fileDescriptor_processors_sql_aba6ce960cf7b022, []int{14, 1, 1} + return fileDescriptor_processors_sql_99047956bc65c02c, []int{14, 1, 1} } func (m *WindowerSpec_Frame_Bounds) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1728,7 +1740,7 @@ func (m *WindowerSpec_WindowFn) Reset() { *m = WindowerSpec_WindowFn{} } func (m *WindowerSpec_WindowFn) String() string { return proto.CompactTextString(m) } func (*WindowerSpec_WindowFn) ProtoMessage() {} func (*WindowerSpec_WindowFn) Descriptor() ([]byte, []int) { - return fileDescriptor_processors_sql_aba6ce960cf7b022, []int{14, 2} + return fileDescriptor_processors_sql_99047956bc65c02c, []int{14, 2} } func (m *WindowerSpec_WindowFn) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2066,6 +2078,14 @@ func (m *JoinReaderSpec) MarshalTo(dAtA []byte) (int, error) { dAtA[i] = 0 } i++ + dAtA[i] = 0x78 + i++ + if m.OutputGroupContinuationForLeftRow { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i++ return i, nil } @@ -3032,6 +3052,7 @@ func (m *JoinReaderSpec) Size() (n int) { n += 2 n += 2 n += 2 + n += 2 return n } @@ -4409,6 +4430,26 @@ func (m *JoinReaderSpec) Unmarshal(dAtA []byte) error { } } m.LeftJoinWithPairedJoiner = bool(v != 0) + case 15: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field OutputGroupContinuationForLeftRow", wireType) + } + var v int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowProcessorsSql + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + m.OutputGroupContinuationForLeftRow = bool(v != 0) default: iNdEx = preIndex skippy, err := skipProcessorsSql(dAtA[iNdEx:]) @@ -7684,183 +7725,184 @@ var ( ) func init() { - proto.RegisterFile("sql/execinfrapb/processors_sql.proto", fileDescriptor_processors_sql_aba6ce960cf7b022) + proto.RegisterFile("sql/execinfrapb/processors_sql.proto", fileDescriptor_processors_sql_99047956bc65c02c) } -var fileDescriptor_processors_sql_aba6ce960cf7b022 = []byte{ - // 2779 bytes of a gzipped FileDescriptorProto +var fileDescriptor_processors_sql_99047956bc65c02c = []byte{ + // 2785 bytes of a gzipped FileDescriptorProto 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xc4, 0x19, 0x4d, 0x6f, 0x1b, 0xc7, - 0x55, 0xcb, 0x0f, 0x89, 0x7c, 0xfc, 0xd0, 0x78, 0xec, 0xc4, 0x8c, 0x92, 0xca, 0x32, 0xed, 0x3a, + 0x55, 0xcb, 0x0f, 0x89, 0x7c, 0xfc, 0xd0, 0x78, 0xac, 0xc4, 0x8c, 0x92, 0xca, 0x32, 0xed, 0x3a, 0xb2, 0xeb, 0x48, 0x8d, 0x5a, 0xa4, 0x48, 0x5a, 0x14, 0x5d, 0x91, 0x4b, 0x99, 0x32, 0xb5, 0x4b, - 0x2f, 0x97, 0x92, 0x93, 0x00, 0x19, 0xac, 0xc8, 0x11, 0xb5, 0xf1, 0x72, 0x97, 0xda, 0x5d, 0x5a, - 0x52, 0xfe, 0x40, 0x4f, 0x05, 0x0a, 0xf4, 0xd2, 0x53, 0x91, 0x16, 0x28, 0xfa, 0x07, 0x7a, 0xee, - 0xa9, 0x07, 0x1f, 0x73, 0x6a, 0x03, 0x14, 0x08, 0x1a, 0xe7, 0x96, 0x3f, 0x50, 0xf4, 0x56, 0xcc, - 0xec, 0xec, 0x6a, 0x29, 0x88, 0x6e, 0xe8, 0x38, 0xf1, 0x45, 0xe0, 0xbc, 0xaf, 0x79, 0xdf, 0xef, - 0xcd, 0x0a, 0x6e, 0xfa, 0x47, 0xf6, 0x3a, 0x3d, 0xa1, 0x3d, 0xcb, 0x39, 0xf0, 0xcc, 0xd1, 0xfe, - 0xfa, 0xc8, 0x73, 0x7b, 0xd4, 0xf7, 0x5d, 0xcf, 0x27, 0xfe, 0x91, 0xbd, 0x36, 0xf2, 0xdc, 0xc0, - 0xc5, 0x95, 0x9e, 0xdb, 0x7b, 0xe4, 0xb9, 0x66, 0xef, 0x70, 0x8d, 0x01, 0xfb, 0x96, 0x1f, 0xf8, - 0x47, 0xb6, 0x37, 0x76, 0x96, 0x6e, 0x30, 0xfe, 0x9e, 0x19, 0x98, 0xb6, 0x3b, 0x58, 0xef, 0x53, - 0xbf, 0x37, 0xda, 0x5f, 0xf7, 0x03, 0x6f, 0xdc, 0x0b, 0xc6, 0x1e, 0xed, 0x87, 0xec, 0x4b, 0xd5, - 0x0b, 0x88, 0x3e, 0x76, 0x2d, 0x87, 0x04, 0xa7, 0x23, 0x2a, 0x68, 0x56, 0x2e, 0xa0, 0xb1, 0xdd, - 0xde, 0x23, 0xcb, 0x19, 0x08, 0x8a, 0x57, 0x18, 0x05, 0xe3, 0xf0, 0xc3, 0xbf, 0x02, 0xbc, 0x74, - 0xde, 0x82, 0xbe, 0x19, 0x98, 0x02, 0xf7, 0xc3, 0x67, 0x58, 0xb7, 0x6f, 0xfa, 0xd1, 0xdd, 0x77, - 0x18, 0x99, 0x3b, 0x0a, 0xd6, 0x2d, 0xe7, 0x31, 0xf5, 0x02, 0xda, 0xa7, 0x27, 0x23, 0x6f, 0xdd, - 0x1f, 0x99, 0x0e, 0x61, 0xbf, 0xa8, 0xef, 0x5b, 0xae, 0x23, 0x68, 0xaf, 0x0c, 0xdc, 0x81, 0xcb, - 0x7f, 0xae, 0xb3, 0x5f, 0x21, 0xb4, 0xfa, 0x3b, 0x09, 0xca, 0xbb, 0xa6, 0x3d, 0xa6, 0x7e, 0xcd, - 0xf5, 0x68, 0x67, 0x44, 0x7b, 0xb8, 0x06, 0x0b, 0x3d, 0xd7, 0x1e, 0x0f, 0x1d, 0xbf, 0x22, 0xad, - 0xa4, 0x57, 0x0b, 0x1b, 0x37, 0xd6, 0xa6, 0x79, 0x71, 0xad, 0x6e, 0x06, 0xe3, 0x61, 0xd3, 0x39, - 0x70, 0x37, 0x33, 0x4f, 0xbe, 0xb8, 0x36, 0xa7, 0x47, 0x9c, 0xf8, 0x75, 0xc8, 0x7b, 0xe6, 0x31, - 0xd9, 0x3f, 0x0d, 0xa8, 0x5f, 0x49, 0xad, 0xa4, 0x57, 0x8b, 0x7a, 0xce, 0x33, 0x8f, 0x37, 0xd9, - 0x19, 0x5f, 0x83, 0x9c, 0x33, 0x1e, 0x12, 0xcf, 0x3d, 0xf6, 0x2b, 0xe9, 0x15, 0x69, 0x35, 0x13, - 0x71, 0x3b, 0xe3, 0xa1, 0xee, 0x1e, 0xfb, 0xd5, 0x7f, 0x65, 0x61, 0xd1, 0x30, 0xf7, 0x6d, 0xaa, - 0x53, 0xb3, 0x4f, 0x3d, 0xae, 0xd6, 0x26, 0x64, 0x03, 0x06, 0xaa, 0x48, 0x2b, 0xd2, 0x6a, 0x61, - 0xe3, 0xd6, 0x39, 0xa5, 0xfc, 0x23, 0x9b, 0x3b, 0x86, 0xb3, 0xd5, 0xa9, 0xdf, 0xf3, 0xac, 0x51, - 0xe0, 0x7a, 0x42, 0x72, 0xc8, 0x8a, 0xaf, 0x43, 0xde, 0x72, 0xfa, 0xf4, 0x84, 0x58, 0xfd, 0x93, - 0x4a, 0x6a, 0x45, 0x5a, 0x2d, 0x09, 0x7c, 0x8e, 0x83, 0x9b, 0xfd, 0x13, 0xbc, 0x0c, 0x0b, 0x1e, - 0x7d, 0x4c, 0x3d, 0x9f, 0x72, 0xd5, 0x72, 0x91, 0x6a, 0x02, 0x88, 0x15, 0xc8, 0x32, 0xff, 0xfa, - 0x95, 0x0c, 0xf7, 0xcd, 0xed, 0xe9, 0xbe, 0x99, 0x30, 0xc0, 0x74, 0x22, 0x4d, 0x38, 0x37, 0xbe, - 0x01, 0x60, 0x5b, 0x43, 0x2b, 0x20, 0x87, 0x96, 0x13, 0x54, 0xb2, 0x2b, 0xd2, 0x6a, 0x5a, 0x10, - 0xe4, 0x39, 0xfc, 0x9e, 0xe5, 0x04, 0xcc, 0x4f, 0x96, 0x4f, 0x7a, 0x87, 0xb4, 0xf7, 0xa8, 0x32, - 0x9f, 0x54, 0xc6, 0xf2, 0x6b, 0x0c, 0x88, 0x55, 0x80, 0xc7, 0x96, 0x6f, 0xed, 0x5b, 0xb6, 0x15, - 0x9c, 0x56, 0x16, 0x56, 0xa4, 0xd5, 0xf2, 0xc6, 0xea, 0x74, 0x8d, 0x3a, 0x3d, 0xd3, 0xd9, 0x8d, - 0xe9, 0x85, 0xb0, 0x84, 0x04, 0xfc, 0x73, 0xb8, 0x3a, 0x34, 0x4f, 0x48, 0x60, 0x0d, 0xa9, 0x1f, - 0x98, 0xc3, 0x11, 0x31, 0x07, 0x94, 0x38, 0xa6, 0xe3, 0xfa, 0x95, 0x7c, 0x22, 0x4e, 0x57, 0x86, - 0xe6, 0x89, 0x11, 0xd1, 0xc8, 0x03, 0xaa, 0x32, 0x0a, 0xfc, 0x21, 0x20, 0x91, 0xf7, 0xc4, 0x0f, - 0x3c, 0xea, 0x0c, 0x82, 0xc3, 0x0a, 0x70, 0x95, 0xee, 0x4c, 0x89, 0x15, 0xd3, 0xa7, 0x15, 0xb2, - 0x74, 0x04, 0x87, 0xb8, 0x61, 0xd1, 0x9e, 0x04, 0xe3, 0x7d, 0xb8, 0x1c, 0x09, 0x3f, 0x36, 0xad, - 0x80, 0x8c, 0x5c, 0xdb, 0xea, 0x9d, 0x56, 0x0a, 0x5c, 0xfe, 0xdd, 0xff, 0x2f, 0x7f, 0xcf, 0xb4, - 0x82, 0x36, 0xe7, 0x11, 0x37, 0x5c, 0xb2, 0xcf, 0x23, 0xf0, 0x2d, 0x28, 0x8c, 0x4c, 0xcf, 0xb4, - 0x6d, 0x6a, 0x5b, 0x9f, 0xd0, 0x4a, 0x31, 0xe1, 0xf1, 0x24, 0x02, 0x6f, 0x00, 0x3e, 0x34, 0x7d, - 0xe2, 0x9f, 0xfa, 0x01, 0x1d, 0x92, 0xa8, 0x56, 0xca, 0x09, 0x72, 0x74, 0x68, 0xfa, 0x1d, 0x8e, - 0xae, 0x85, 0xd8, 0xed, 0x4c, 0x2e, 0x87, 0xf2, 0xdb, 0x99, 0x5c, 0x09, 0x95, 0xab, 0xff, 0x4d, - 0x43, 0xa5, 0xc9, 0xf2, 0xad, 0xf3, 0xc8, 0x1a, 0xbd, 0xa4, 0x34, 0x8f, 0xd3, 0x38, 0xfd, 0xad, - 0xd2, 0x78, 0x32, 0x01, 0x33, 0xdf, 0x3a, 0x01, 0x13, 0xd5, 0x97, 0xbd, 0xa8, 0xfa, 0x2e, 0xca, - 0xb1, 0xf9, 0xef, 0x38, 0xc7, 0x16, 0x5e, 0x60, 0x8e, 0x55, 0xff, 0x32, 0x0f, 0xe5, 0x6d, 0xd7, - 0x72, 0xbe, 0xff, 0x88, 0xdf, 0x86, 0xb2, 0xed, 0xba, 0x8f, 0xc6, 0xa3, 0x38, 0x63, 0x59, 0xe8, - 0x4b, 0x9b, 0x29, 0x24, 0xe9, 0xa5, 0x10, 0x23, 0x92, 0x95, 0x4d, 0x00, 0x37, 0x9c, 0x20, 0x3c, - 0xa4, 0x85, 0x8d, 0x9b, 0xd3, 0x43, 0xaa, 0xc4, 0x73, 0x46, 0xdc, 0x38, 0xef, 0x3a, 0x0c, 0x86, - 0xdf, 0x85, 0x0c, 0x9b, 0x76, 0x22, 0x3c, 0xd7, 0xa6, 0x58, 0xc5, 0x7c, 0x61, 0x9c, 0x8e, 0xa8, - 0x60, 0xe6, 0x2c, 0x2f, 0xbc, 0xad, 0xbd, 0x0b, 0xaf, 0x4e, 0x9a, 0x4e, 0x4c, 0x8f, 0x92, 0x47, - 0xf4, 0xb4, 0x92, 0x4b, 0x24, 0xd9, 0xe5, 0x09, 0x27, 0xc8, 0x1e, 0xbd, 0x4f, 0x4f, 0x2f, 0x4c, - 0xb8, 0xfc, 0x77, 0x9c, 0x70, 0xf0, 0x22, 0x9b, 0xda, 0xdb, 0x70, 0x69, 0x68, 0x5a, 0x4e, 0x60, - 0x5a, 0x0e, 0x71, 0xbd, 0x3e, 0xf5, 0x2c, 0x67, 0xc0, 0xdb, 0x66, 0xdc, 0xab, 0x22, 0xb4, 0x26, - 0xb0, 0x53, 0xfa, 0x5b, 0xe9, 0x59, 0xfd, 0x0d, 0xd7, 0xe1, 0x0d, 0x9b, 0x1e, 0x04, 0x84, 0x6f, - 0x47, 0xc7, 0x56, 0x70, 0x48, 0x46, 0xa6, 0xe5, 0xd1, 0x3e, 0x07, 0x50, 0x6f, 0xa2, 0x3b, 0x56, - 0x18, 0x25, 0x0b, 0xfc, 0x9e, 0x15, 0x1c, 0xb6, 0x39, 0xd9, 0x36, 0xa7, 0xda, 0xce, 0xe4, 0xb2, - 0x68, 0x7e, 0x3b, 0x93, 0x2b, 0xa2, 0x12, 0xdb, 0x4c, 0xa0, 0xe3, 0x7a, 0x81, 0xa8, 0x92, 0x07, - 0xb0, 0xe8, 0x8e, 0x83, 0xd1, 0x38, 0x38, 0xb3, 0x22, 0xac, 0x97, 0xea, 0xf4, 0xc4, 0x88, 0x2c, - 0x12, 0xf7, 0x96, 0x43, 0x01, 0x49, 0x3b, 0x23, 0x59, 0x64, 0x68, 0x06, 0xbd, 0x43, 0x62, 0x53, - 0x67, 0xa2, 0x7a, 0x50, 0x84, 0xdf, 0x61, 0xe8, 0x16, 0x75, 0xaa, 0x7f, 0x93, 0xa0, 0x58, 0xb7, - 0xfc, 0xc0, 0x72, 0x7a, 0x01, 0xd7, 0xeb, 0x4d, 0x58, 0xe4, 0x44, 0xb4, 0x4f, 0x92, 0x5b, 0x53, - 0x49, 0x2f, 0x0b, 0x70, 0xe4, 0xa1, 0xdb, 0x80, 0xfa, 0x82, 0x31, 0xa6, 0x4c, 0x71, 0xca, 0xc5, - 0x08, 0x1e, 0x91, 0x6e, 0x00, 0x76, 0xc6, 0xb6, 0x1d, 0xa6, 0x69, 0x84, 0x9c, 0x58, 0x47, 0x10, - 0xc7, 0xcb, 0x1e, 0x8d, 0x74, 0xc1, 0xb7, 0xa0, 0x48, 0x3d, 0xcf, 0xf5, 0x88, 0xeb, 0x90, 0xfe, - 0x78, 0xc4, 0x0b, 0x37, 0x1f, 0xd5, 0x02, 0xc7, 0x68, 0x4e, 0x7d, 0x3c, 0xaa, 0x22, 0x28, 0x6b, - 0x5e, 0xdf, 0x72, 0x4c, 0x56, 0x19, 0xcc, 0x82, 0xea, 0xef, 0xd3, 0x80, 0x3e, 0xb0, 0x06, 0x9f, - 0x98, 0x83, 0x30, 0x0a, 0xdc, 0xac, 0x3a, 0xcc, 0xf3, 0xce, 0x12, 0xed, 0x80, 0xb3, 0x75, 0x25, - 0xc1, 0x8b, 0x1b, 0x00, 0xf4, 0x68, 0xc2, 0xda, 0xc2, 0xc6, 0xf5, 0xe9, 0xf1, 0x12, 0xf6, 0x47, - 0x8b, 0x10, 0x3d, 0x3a, 0xf3, 0x5d, 0x39, 0x6c, 0x6f, 0x6e, 0xa8, 0xfa, 0x44, 0xef, 0xe2, 0x18, - 0x61, 0xd3, 0x0b, 0xea, 0x5d, 0xf7, 0xa1, 0x78, 0x60, 0x9d, 0xd0, 0x3e, 0x79, 0xcc, 0x57, 0xe3, - 0x4a, 0x96, 0x6b, 0xfe, 0x8c, 0x16, 0x34, 0xb9, 0x42, 0xeb, 0x05, 0xce, 0x1d, 0x02, 0xbf, 0x45, - 0x23, 0xac, 0xfe, 0x23, 0x0d, 0x8b, 0x3b, 0xd4, 0x1b, 0xd0, 0x44, 0x64, 0x76, 0xa0, 0xc4, 0x2b, - 0xed, 0xb9, 0xcb, 0xa0, 0xc8, 0xd8, 0xe3, 0x22, 0xd0, 0xa0, 0xec, 0x59, 0x83, 0xc3, 0x84, 0xbc, - 0xd4, 0x8c, 0xf2, 0x4a, 0x9c, 0x3f, 0x16, 0x98, 0x08, 0x40, 0xf6, 0x65, 0x0c, 0x8f, 0xdb, 0x50, - 0x62, 0xc5, 0x41, 0xe8, 0xd1, 0xd8, 0x8c, 0xe7, 0x47, 0x54, 0x37, 0x45, 0x86, 0x52, 0x04, 0x06, - 0xbf, 0x07, 0x57, 0xb9, 0x2b, 0xcf, 0x72, 0x74, 0xca, 0x60, 0xa0, 0x07, 0x81, 0x72, 0x34, 0x39, - 0x18, 0x7e, 0x01, 0x95, 0xd0, 0x6f, 0x17, 0x30, 0xe7, 0x13, 0xcc, 0x57, 0x38, 0xd5, 0x39, 0xee, - 0xea, 0xd7, 0x29, 0x28, 0xdf, 0x33, 0xfd, 0xc3, 0x44, 0x5c, 0xef, 0xc0, 0xe2, 0x39, 0x65, 0xc2, - 0x46, 0x22, 0x06, 0x74, 0x52, 0x05, 0x7c, 0x17, 0xd0, 0xf9, 0xcb, 0xc3, 0x5e, 0xc2, 0x89, 0xcb, - 0x93, 0x57, 0xbe, 0xf4, 0x88, 0xbc, 0x34, 0x37, 0x6f, 0x67, 0x72, 0x0b, 0x28, 0x57, 0xfd, 0x22, - 0x0d, 0xb8, 0x29, 0x1e, 0xc8, 0x09, 0x87, 0x7f, 0x4f, 0x7b, 0x97, 0x06, 0xa5, 0xe8, 0x75, 0xfe, - 0xbc, 0x6d, 0xa9, 0x18, 0x09, 0xe0, 0x91, 0x78, 0xd9, 0xe1, 0xbc, 0x70, 0xa3, 0x58, 0x78, 0xe6, - 0x46, 0xb1, 0x07, 0xb7, 0xc4, 0xf0, 0x1e, 0x78, 0x2e, 0x5f, 0xc3, 0x9c, 0xc0, 0x72, 0xc6, 0x66, - 0x60, 0xb9, 0x0e, 0x39, 0x70, 0x3d, 0xc2, 0xf3, 0xc3, 0x73, 0x8f, 0x27, 0x12, 0xe2, 0x7a, 0xc8, - 0xb3, 0xc5, 0x58, 0x6a, 0x09, 0x8e, 0x86, 0xeb, 0xb5, 0xe8, 0x41, 0xa0, 0xbb, 0xc7, 0xdb, 0x99, - 0x5c, 0x1a, 0x65, 0xaa, 0x7f, 0x4a, 0xc3, 0x95, 0x28, 0xc0, 0x0d, 0xcb, 0x0e, 0xa8, 0x27, 0x42, - 0xbc, 0x06, 0x28, 0xf6, 0x7d, 0xcf, 0xb5, 0x79, 0x94, 0xa4, 0x44, 0x94, 0xca, 0x11, 0xb6, 0xe6, - 0xda, 0x2c, 0x56, 0x1f, 0x9d, 0x8f, 0x55, 0xd8, 0x0b, 0x7f, 0x72, 0xce, 0x3d, 0xee, 0x28, 0x58, - 0x4b, 0x7e, 0x71, 0x59, 0x63, 0x0f, 0xa3, 0x33, 0x57, 0xb7, 0x3d, 0x37, 0x70, 0x2f, 0x0c, 0xdd, - 0xc7, 0x70, 0x69, 0xe4, 0x51, 0x72, 0x20, 0x74, 0x24, 0xfe, 0x88, 0xf6, 0x78, 0x08, 0x0a, 0x1b, - 0xbf, 0x9c, 0x1e, 0xc4, 0x8b, 0x4c, 0x5b, 0x6b, 0x7b, 0x34, 0x79, 0xd6, 0x17, 0x47, 0x93, 0x80, - 0xa5, 0xdf, 0x48, 0xb0, 0x78, 0x8e, 0x08, 0x6f, 0x03, 0x9c, 0x7d, 0x17, 0x12, 0x79, 0x3f, 0x4b, - 0xf6, 0x24, 0xb8, 0xf1, 0x9a, 0xc8, 0xa0, 0xd0, 0x45, 0x4b, 0xe7, 0x33, 0x88, 0x0e, 0xd7, 0xc2, - 0x8f, 0x5d, 0x86, 0x98, 0x65, 0x5f, 0xe7, 0xa0, 0x2c, 0x0f, 0x06, 0x1e, 0x1d, 0x98, 0x81, 0x1b, - 0xaa, 0x73, 0x1d, 0x20, 0xca, 0x07, 0x3b, 0xd9, 0xc0, 0xf2, 0x83, 0x30, 0xe4, 0xb6, 0x8f, 0x3f, - 0x82, 0xa2, 0x29, 0x98, 0x2c, 0x37, 0x7e, 0xae, 0xfe, 0x74, 0xba, 0xce, 0x93, 0x57, 0xc4, 0xc7, - 0x44, 0x31, 0x25, 0xe5, 0xe1, 0x1f, 0x8b, 0x1d, 0x90, 0xf6, 0x49, 0x42, 0x95, 0x4c, 0xac, 0x0a, - 0x12, 0xd8, 0xad, 0x58, 0xa3, 0x2d, 0x61, 0x77, 0x96, 0x57, 0xce, 0x5b, 0xdf, 0x58, 0x93, 0xf3, - 0x75, 0xb4, 0xf4, 0xeb, 0x14, 0x14, 0x12, 0xea, 0x31, 0xc1, 0x07, 0x63, 0xa7, 0xc7, 0xc3, 0x32, - 0x8b, 0xe0, 0xc6, 0xd8, 0xe9, 0x45, 0x82, 0x99, 0x00, 0xbc, 0x02, 0xb9, 0x78, 0x69, 0x4c, 0x25, - 0xea, 0x29, 0x86, 0xe2, 0x9b, 0x50, 0x0e, 0x73, 0x30, 0xae, 0x0a, 0xd6, 0x94, 0x4a, 0x7a, 0x31, - 0x84, 0x8a, 0x6a, 0xb8, 0xca, 0x3f, 0x04, 0x72, 0x74, 0x96, 0x2f, 0xaa, 0xf3, 0xbd, 0x10, 0x71, - 0x0f, 0xf2, 0xa6, 0x37, 0x18, 0x0f, 0xa9, 0x13, 0xf8, 0x95, 0x79, 0x1e, 0x91, 0x59, 0xb2, 0xe8, - 0x8c, 0x59, 0xd4, 0xef, 0x1f, 0x33, 0x90, 0x61, 0x56, 0x60, 0x04, 0x45, 0x59, 0x7d, 0x9f, 0xa8, - 0x9a, 0x41, 0xd4, 0x6e, 0xab, 0x85, 0xe6, 0xf0, 0x02, 0xa4, 0xe5, 0xdd, 0x2d, 0x24, 0xe1, 0x22, - 0xe4, 0x36, 0x35, 0xad, 0x45, 0x64, 0xb5, 0x8e, 0x52, 0xb8, 0x00, 0x0b, 0xfc, 0xa4, 0xe9, 0x28, - 0x8d, 0xcb, 0x00, 0x35, 0x4d, 0xad, 0xc9, 0x06, 0x91, 0xb7, 0xb6, 0x50, 0x06, 0xe7, 0x21, 0x5b, - 0xd3, 0xba, 0xaa, 0x81, 0xb2, 0x8c, 0x7d, 0x47, 0x7e, 0x88, 0x16, 0xf8, 0x8f, 0xa6, 0x8a, 0x72, - 0x18, 0x60, 0xbe, 0x63, 0xd4, 0xeb, 0xca, 0x2e, 0xca, 0x33, 0x60, 0xa7, 0xbb, 0x83, 0x80, 0x89, - 0xeb, 0x74, 0x77, 0x48, 0x53, 0x35, 0x50, 0x81, 0xdd, 0xb4, 0x2b, 0xeb, 0x4d, 0x59, 0xad, 0x29, - 0xa8, 0xc8, 0x50, 0x0f, 0x35, 0x9d, 0x4b, 0x2e, 0x85, 0x37, 0x75, 0x55, 0x83, 0xe8, 0xda, 0x5e, - 0x07, 0x95, 0x39, 0xdf, 0x03, 0xbd, 0xde, 0x6c, 0x34, 0xd0, 0x22, 0xc6, 0x50, 0x6e, 0x34, 0x55, - 0xb9, 0x45, 0x62, 0x6e, 0xc4, 0x0c, 0x0a, 0x61, 0xe2, 0xce, 0x4b, 0xb8, 0x04, 0x79, 0x59, 0xd7, - 0xe5, 0xf7, 0xb9, 0x44, 0xcc, 0x2e, 0xdb, 0xee, 0x68, 0x2a, 0x3f, 0x5d, 0x66, 0x48, 0x76, 0xda, - 0xe4, 0xc7, 0x2b, 0xec, 0xba, 0x8e, 0xa1, 0x37, 0xd5, 0x2d, 0x7e, 0x7e, 0x85, 0x5b, 0xdd, 0x34, - 0xb8, 0x0b, 0x5e, 0x65, 0x86, 0xb0, 0x83, 0xa6, 0xa3, 0xab, 0x38, 0x07, 0x99, 0x9a, 0xa6, 0xeb, - 0xa8, 0x82, 0x2b, 0x70, 0xa5, 0xad, 0xe8, 0x35, 0x45, 0x35, 0x9a, 0x2d, 0x85, 0xd4, 0x9b, 0x9d, - 0x1a, 0x69, 0xee, 0xb4, 0x5b, 0xe8, 0xb5, 0x73, 0x98, 0x9a, 0xa6, 0x1a, 0x21, 0x66, 0x09, 0x5f, - 0x86, 0x45, 0xae, 0x83, 0xb6, 0xb9, 0xad, 0xd4, 0x42, 0x27, 0xbe, 0x8e, 0xaf, 0x00, 0x0a, 0x55, - 0x49, 0x40, 0xdf, 0x60, 0x1a, 0xec, 0xca, 0x3a, 0x69, 0x6b, 0x6d, 0xf4, 0x83, 0x50, 0x3d, 0x66, - 0x16, 0x3f, 0x2f, 0xe3, 0x45, 0x28, 0x74, 0x0c, 0xb2, 0x23, 0xdf, 0x57, 0x5a, 0x4d, 0x55, 0x41, - 0xd7, 0x98, 0x39, 0x1d, 0x83, 0x28, 0x0f, 0x0d, 0x45, 0x35, 0xd0, 0x0a, 0xb3, 0xb5, 0x63, 0x90, - 0xae, 0xda, 0xd4, 0x54, 0x74, 0x3d, 0xe4, 0x26, 0x35, 0xad, 0xd5, 0x52, 0x6a, 0x06, 0xaa, 0x32, - 0xe2, 0x9a, 0x16, 0x09, 0xbf, 0x11, 0xba, 0x9a, 0x1d, 0x3b, 0xf2, 0x4e, 0x1b, 0xdd, 0xac, 0xde, - 0x85, 0x0c, 0xab, 0x20, 0x66, 0xaa, 0xdc, 0x35, 0x34, 0x34, 0xc7, 0x23, 0x59, 0x93, 0x5b, 0xb2, - 0x8e, 0x24, 0x46, 0xad, 0x6a, 0x2a, 0x11, 0xe7, 0x54, 0xf5, 0xef, 0x12, 0x94, 0xdb, 0x9e, 0xfb, - 0x31, 0xed, 0x05, 0x1d, 0x1a, 0x3e, 0xd4, 0x7e, 0x05, 0x59, 0xd6, 0xbd, 0xa2, 0x07, 0xcd, 0x2c, - 0x09, 0x1b, 0x32, 0xe2, 0x2d, 0xb8, 0x34, 0xa0, 0x0e, 0xf5, 0xcc, 0x20, 0xf1, 0xd8, 0x0b, 0x1f, - 0x35, 0xcf, 0x6a, 0x7f, 0x28, 0x66, 0x8a, 0x16, 0xb2, 0x37, 0x01, 0x39, 0x63, 0xfe, 0xb2, 0xf6, - 0xc9, 0x88, 0x7a, 0x64, 0x40, 0x9d, 0xf0, 0x41, 0xa3, 0x97, 0x9c, 0x31, 0x7b, 0x52, 0xfb, 0x6d, - 0xea, 0x6d, 0x51, 0xa7, 0xfa, 0x65, 0x09, 0x8a, 0x7b, 0x96, 0xd3, 0x77, 0x8f, 0x45, 0x03, 0x5f, - 0xe1, 0x9f, 0x28, 0x03, 0x8b, 0xf7, 0xb3, 0x53, 0xf1, 0xd2, 0x4c, 0x82, 0x70, 0x07, 0xf2, 0xc7, - 0x9c, 0xa3, 0x11, 0x2b, 0xb7, 0x3e, 0xdd, 0xd4, 0xa4, 0x70, 0x71, 0x68, 0xc4, 0x65, 0x1a, 0xcb, - 0x59, 0xfa, 0xab, 0x24, 0x0a, 0xb4, 0x03, 0xa5, 0xa8, 0x7d, 0xd2, 0xc6, 0xf3, 0x36, 0x2b, 0x7d, - 0x52, 0x06, 0x7e, 0x00, 0x20, 0xae, 0x62, 0x12, 0x53, 0x5c, 0xe2, 0xdb, 0xb3, 0xe9, 0xcc, 0xa4, - 0x26, 0x84, 0xbc, 0x97, 0x79, 0xf2, 0xe9, 0x35, 0x69, 0xe9, 0xd3, 0x05, 0xc8, 0x36, 0x3c, 0x73, - 0x48, 0xf1, 0x7d, 0xc8, 0x0c, 0xdd, 0x3e, 0x15, 0xea, 0x7e, 0x53, 0xe1, 0x9c, 0x77, 0x6d, 0xc7, - 0xed, 0xc7, 0x8d, 0x9b, 0x09, 0xc1, 0x0f, 0x60, 0x7e, 0xdf, 0x1d, 0x3b, 0x7d, 0x7f, 0xca, 0x7a, - 0xf0, 0x6c, 0x71, 0x9b, 0x9c, 0x35, 0x5a, 0xc7, 0x42, 0x41, 0xf8, 0x03, 0xc8, 0xd3, 0x93, 0x9e, - 0x3d, 0xe6, 0x73, 0x39, 0xcd, 0x95, 0x7c, 0x67, 0x26, 0xa9, 0x4a, 0xc4, 0x1d, 0x3f, 0x9e, 0x23, - 0xc0, 0xd2, 0x7f, 0x24, 0xc8, 0xf2, 0x4b, 0xd9, 0x2d, 0xfc, 0x3e, 0x56, 0x48, 0xc2, 0x15, 0xef, - 0xcc, 0xae, 0x7b, 0x62, 0x90, 0x9d, 0x89, 0xc3, 0x37, 0x00, 0x2c, 0x27, 0x20, 0xee, 0xc1, 0x81, - 0x4f, 0xc3, 0xb1, 0x13, 0xfd, 0xb7, 0x20, 0x6f, 0x39, 0x81, 0xc6, 0xc1, 0xf8, 0x3a, 0x14, 0x59, - 0x55, 0xf4, 0x23, 0x32, 0x66, 0x69, 0x51, 0x2f, 0x70, 0x98, 0x20, 0xd9, 0x86, 0x42, 0x88, 0xe4, - 0xff, 0x63, 0x13, 0xcb, 0xf2, 0x0c, 0xff, 0x81, 0x82, 0x90, 0x9b, 0xe9, 0xb4, 0xf4, 0x07, 0x09, - 0xe6, 0x43, 0x77, 0x63, 0x15, 0xb2, 0x7e, 0x60, 0x7a, 0x81, 0x58, 0x7a, 0x36, 0x66, 0x37, 0x3b, - 0xfe, 0xf0, 0xcd, 0xc4, 0xe0, 0x3a, 0xa4, 0xa9, 0xd3, 0x17, 0x09, 0xf0, 0x1c, 0xd2, 0x74, 0xc6, - 0x5e, 0x7d, 0x13, 0x32, 0x2c, 0xbb, 0xd8, 0xc4, 0xd2, 0x65, 0x75, 0x4b, 0x41, 0x73, 0xac, 0xbf, - 0xf1, 0xe1, 0x22, 0xb1, 0xfe, 0xb6, 0xa5, 0x6b, 0xdd, 0x76, 0x07, 0xa5, 0xaa, 0x9f, 0x40, 0x3e, - 0xf6, 0x3d, 0xbe, 0x0a, 0x97, 0xbb, 0xea, 0xa6, 0xd6, 0x55, 0xeb, 0x4a, 0x9d, 0xb4, 0x75, 0xa5, - 0xa6, 0xd4, 0x9b, 0xea, 0x16, 0x9a, 0x9b, 0x44, 0x34, 0xb4, 0x56, 0x4b, 0xdb, 0x63, 0x08, 0x89, - 0x35, 0x73, 0xad, 0xd1, 0xe8, 0x28, 0x46, 0x82, 0x3c, 0x95, 0x80, 0x9e, 0xd1, 0xa6, 0x59, 0x17, - 0xaf, 0x75, 0x75, 0x5d, 0x09, 0xa7, 0x1c, 0xca, 0x54, 0x3f, 0x84, 0x7c, 0x9c, 0x5d, 0x6c, 0xa0, - 0xa9, 0x1a, 0x51, 0x1e, 0xd6, 0x5a, 0xdd, 0x0e, 0xeb, 0xe3, 0xfc, 0x52, 0x7e, 0xac, 0x2b, 0x24, - 0xc9, 0x27, 0xe1, 0x4b, 0x50, 0x8a, 0x10, 0xdc, 0x0e, 0x94, 0x62, 0xdc, 0x11, 0xc8, 0x68, 0x2a, - 0x1d, 0x94, 0x5e, 0xfa, 0x67, 0x0a, 0x72, 0x51, 0xdf, 0xc1, 0x4a, 0x62, 0x03, 0x2a, 0x6c, 0xfc, - 0xe8, 0x9b, 0x7a, 0xf5, 0xfc, 0xfe, 0x53, 0x87, 0x5c, 0xfc, 0x2e, 0xc9, 0xcc, 0xf8, 0x31, 0x23, - 0xe6, 0x64, 0xcf, 0xc3, 0x03, 0x16, 0x2f, 0xf1, 0xc8, 0xba, 0x3b, 0x4b, 0x8c, 0xf5, 0x90, 0x15, - 0xaf, 0xc2, 0xc4, 0x46, 0xc5, 0x57, 0xfd, 0x6c, 0xb4, 0x87, 0x4e, 0xec, 0x5a, 0x4b, 0x90, 0x33, - 0xbd, 0x81, 0xdf, 0xec, 0x9f, 0xf8, 0x95, 0x05, 0xde, 0xd5, 0xe3, 0x33, 0x93, 0x12, 0xbe, 0x84, - 0x84, 0x94, 0x5c, 0xe2, 0x05, 0x33, 0x81, 0xd9, 0xce, 0xe4, 0x52, 0x28, 0x2d, 0x96, 0xaa, 0x3f, - 0x4b, 0x00, 0x67, 0xdd, 0x91, 0x4d, 0x48, 0x5d, 0xdb, 0x23, 0x6a, 0x77, 0x67, 0x53, 0xd1, 0x45, - 0x9e, 0xc9, 0xea, 0xfd, 0x70, 0x76, 0xd6, 0x15, 0xb5, 0xa3, 0x10, 0x7e, 0xe6, 0x41, 0x12, 0x8b, - 0x42, 0x08, 0x49, 0xf3, 0xd1, 0xdc, 0xdd, 0xe1, 0xeb, 0x84, 0x11, 0xee, 0x57, 0x7c, 0x89, 0x08, - 0xf7, 0xab, 0x96, 0xbc, 0x85, 0xe6, 0x99, 0xb8, 0x96, 0x22, 0xd7, 0xd1, 0x02, 0xcb, 0x9f, 0x46, - 0x53, 0xef, 0x18, 0x64, 0x57, 0x6e, 0x75, 0x15, 0x94, 0x63, 0xf2, 0x5b, 0x72, 0x7c, 0xce, 0x33, - 0x69, 0xaa, 0x71, 0x4f, 0x1c, 0xe1, 0xce, 0xcf, 0xa0, 0x3c, 0xf9, 0x01, 0x9f, 0x25, 0x7e, 0xbb, - 0xbb, 0xd9, 0x6a, 0xd6, 0xd0, 0x1c, 0x7e, 0x0d, 0x5e, 0x09, 0x7f, 0xb3, 0xad, 0x87, 0x2f, 0x86, - 0x02, 0x25, 0x6d, 0xbe, 0xf5, 0xe4, 0xcb, 0xe5, 0xb9, 0x27, 0x4f, 0x97, 0xa5, 0xcf, 0x9e, 0x2e, - 0x4b, 0x9f, 0x3f, 0x5d, 0x96, 0xfe, 0xfd, 0x74, 0x59, 0xfa, 0xed, 0x57, 0xcb, 0x73, 0x9f, 0x7d, - 0xb5, 0x3c, 0xf7, 0xf9, 0x57, 0xcb, 0x73, 0x1f, 0x14, 0x12, 0xff, 0x41, 0xff, 0x5f, 0x00, 0x00, - 0x00, 0xff, 0xff, 0x9c, 0x96, 0x51, 0xa8, 0x31, 0x20, 0x00, 0x00, + 0x2f, 0x49, 0xc9, 0x49, 0x80, 0x0c, 0x56, 0xe4, 0x88, 0xda, 0x78, 0xb9, 0x4b, 0xed, 0x2e, 0x2d, + 0x29, 0xa7, 0xde, 0x7a, 0x2a, 0x50, 0xa0, 0x97, 0x9e, 0x8a, 0xb4, 0x40, 0xff, 0x41, 0xcf, 0x3d, + 0xf5, 0xe0, 0x63, 0x4e, 0x6d, 0x80, 0x02, 0x41, 0xe3, 0xdc, 0xf2, 0x07, 0x8a, 0xde, 0x8a, 0x99, + 0x9d, 0x5d, 0x2d, 0x09, 0xd1, 0x35, 0x6d, 0x27, 0xbe, 0x08, 0x9a, 0xf7, 0x35, 0xef, 0xfb, 0xbd, + 0x59, 0xc2, 0x0d, 0xef, 0xd8, 0xda, 0xa0, 0xa7, 0xb4, 0x6b, 0xda, 0x87, 0xae, 0x31, 0x3c, 0xd8, + 0x18, 0xba, 0x4e, 0x97, 0x7a, 0x9e, 0xe3, 0x7a, 0xc4, 0x3b, 0xb6, 0xd6, 0x87, 0xae, 0xe3, 0x3b, + 0xb8, 0xd4, 0x75, 0xba, 0x0f, 0x5d, 0xc7, 0xe8, 0x1e, 0xad, 0x33, 0x60, 0xcf, 0xf4, 0x7c, 0xef, + 0xd8, 0x72, 0x47, 0xf6, 0xf2, 0x75, 0xc6, 0xdf, 0x35, 0x7c, 0xc3, 0x72, 0xfa, 0x1b, 0x3d, 0xea, + 0x75, 0x87, 0x07, 0x1b, 0x9e, 0xef, 0x8e, 0xba, 0xfe, 0xc8, 0xa5, 0xbd, 0x80, 0x7d, 0xb9, 0x7c, + 0x01, 0xd1, 0xa7, 0x8e, 0x69, 0x13, 0xff, 0x6c, 0x48, 0x05, 0xcd, 0xea, 0x05, 0x34, 0x96, 0xd3, + 0x7d, 0x68, 0xda, 0x7d, 0x41, 0xf1, 0x1a, 0xa3, 0x60, 0x1c, 0x5e, 0xf0, 0x57, 0x80, 0x97, 0x27, + 0x2d, 0xe8, 0x19, 0xbe, 0x21, 0x70, 0x3f, 0x7c, 0x8a, 0x75, 0x07, 0x86, 0x17, 0xde, 0x7d, 0x9b, + 0x91, 0x39, 0x43, 0x7f, 0xc3, 0xb4, 0x1f, 0x51, 0xd7, 0xa7, 0x3d, 0x7a, 0x3a, 0x74, 0x37, 0xbc, + 0xa1, 0x61, 0x13, 0xf6, 0x1f, 0xf5, 0x3c, 0xd3, 0xb1, 0x05, 0xed, 0x52, 0xdf, 0xe9, 0x3b, 0xfc, + 0xdf, 0x0d, 0xf6, 0x5f, 0x00, 0x2d, 0xff, 0x5e, 0x82, 0xe2, 0x9e, 0x61, 0x8d, 0xa8, 0x57, 0x71, + 0x5c, 0xda, 0x1a, 0xd2, 0x2e, 0xae, 0xc0, 0x42, 0xd7, 0xb1, 0x46, 0x03, 0xdb, 0x2b, 0x49, 0xab, + 0xc9, 0xb5, 0xdc, 0xe6, 0xf5, 0xf5, 0x69, 0x5e, 0x5c, 0xaf, 0x1a, 0xfe, 0x68, 0x50, 0xb7, 0x0f, + 0x9d, 0xad, 0xd4, 0xe3, 0xaf, 0xae, 0xce, 0xe9, 0x21, 0x27, 0x7e, 0x13, 0xb2, 0xae, 0x71, 0x42, + 0x0e, 0xce, 0x7c, 0xea, 0x95, 0x12, 0xab, 0xc9, 0xb5, 0xbc, 0x9e, 0x71, 0x8d, 0x93, 0x2d, 0x76, + 0xc6, 0x57, 0x21, 0x63, 0x8f, 0x06, 0xc4, 0x75, 0x4e, 0xbc, 0x52, 0x72, 0x55, 0x5a, 0x4b, 0x85, + 0xdc, 0xf6, 0x68, 0xa0, 0x3b, 0x27, 0x5e, 0xf9, 0x5f, 0x69, 0x58, 0x6c, 0x1b, 0x07, 0x16, 0xd5, + 0xa9, 0xd1, 0xa3, 0x2e, 0x57, 0x6b, 0x0b, 0xd2, 0x3e, 0x03, 0x95, 0xa4, 0x55, 0x69, 0x2d, 0xb7, + 0x79, 0x73, 0x42, 0x29, 0xef, 0xd8, 0xe2, 0x8e, 0xe1, 0x6c, 0x55, 0xea, 0x75, 0x5d, 0x73, 0xe8, + 0x3b, 0xae, 0x90, 0x1c, 0xb0, 0xe2, 0x6b, 0x90, 0x35, 0xed, 0x1e, 0x3d, 0x25, 0x66, 0xef, 0xb4, + 0x94, 0x58, 0x95, 0xd6, 0x0a, 0x02, 0x9f, 0xe1, 0xe0, 0x7a, 0xef, 0x14, 0xaf, 0xc0, 0x82, 0x4b, + 0x1f, 0x51, 0xd7, 0xa3, 0x5c, 0xb5, 0x4c, 0xa8, 0x9a, 0x00, 0x62, 0x05, 0xd2, 0xcc, 0xbf, 0x5e, + 0x29, 0xc5, 0x7d, 0x73, 0x6b, 0xba, 0x6f, 0xc6, 0x0c, 0x30, 0xec, 0x50, 0x13, 0xce, 0x8d, 0xaf, + 0x03, 0x58, 0xe6, 0xc0, 0xf4, 0xc9, 0x91, 0x69, 0xfb, 0xa5, 0xf4, 0xaa, 0xb4, 0x96, 0x14, 0x04, + 0x59, 0x0e, 0xbf, 0x6b, 0xda, 0x3e, 0xf3, 0x93, 0xe9, 0x91, 0xee, 0x11, 0xed, 0x3e, 0x2c, 0xcd, + 0xc7, 0x95, 0x31, 0xbd, 0x0a, 0x03, 0x62, 0x15, 0xe0, 0x91, 0xe9, 0x99, 0x07, 0xa6, 0x65, 0xfa, + 0x67, 0xa5, 0x85, 0x55, 0x69, 0xad, 0xb8, 0xb9, 0x36, 0x5d, 0xa3, 0x56, 0xd7, 0xb0, 0xf7, 0x22, + 0x7a, 0x21, 0x2c, 0x26, 0x01, 0xff, 0x1c, 0xae, 0x0c, 0x8c, 0x53, 0xe2, 0x9b, 0x03, 0xea, 0xf9, + 0xc6, 0x60, 0x48, 0x8c, 0x3e, 0x25, 0xb6, 0x61, 0x3b, 0x5e, 0x29, 0x1b, 0x8b, 0xd3, 0xd2, 0xc0, + 0x38, 0x6d, 0x87, 0x34, 0x72, 0x9f, 0xaa, 0x8c, 0x02, 0x7f, 0x0c, 0x48, 0xe4, 0x3d, 0xf1, 0x7c, + 0x97, 0xda, 0x7d, 0xff, 0xa8, 0x04, 0x5c, 0xa5, 0xdb, 0x53, 0x62, 0xc5, 0xf4, 0x69, 0x04, 0x2c, + 0x2d, 0xc1, 0x21, 0x6e, 0x58, 0xb4, 0xc6, 0xc1, 0xf8, 0x00, 0x2e, 0x87, 0xc2, 0x4f, 0x0c, 0xd3, + 0x27, 0x43, 0xc7, 0x32, 0xbb, 0x67, 0xa5, 0x1c, 0x97, 0x7f, 0xe7, 0xff, 0xcb, 0xdf, 0x37, 0x4c, + 0xbf, 0xc9, 0x79, 0xc4, 0x0d, 0x97, 0xac, 0x49, 0x04, 0xbe, 0x09, 0xb9, 0xa1, 0xe1, 0x1a, 0x96, + 0x45, 0x2d, 0xf3, 0x33, 0x5a, 0xca, 0xc7, 0x3c, 0x1e, 0x47, 0xe0, 0x4d, 0xc0, 0x47, 0x86, 0x47, + 0xbc, 0x33, 0xcf, 0xa7, 0x03, 0x12, 0xd6, 0x4a, 0x31, 0x46, 0x8e, 0x8e, 0x0c, 0xaf, 0xc5, 0xd1, + 0x95, 0x00, 0xbb, 0x93, 0xca, 0x64, 0x50, 0x76, 0x27, 0x95, 0x29, 0xa0, 0x62, 0xf9, 0xbf, 0x49, + 0x28, 0xd5, 0x59, 0xbe, 0xb5, 0x1e, 0x9a, 0xc3, 0x57, 0x94, 0xe6, 0x51, 0x1a, 0x27, 0x5f, 0x28, + 0x8d, 0xc7, 0x13, 0x30, 0xf5, 0xc2, 0x09, 0x18, 0xab, 0xbe, 0xf4, 0x45, 0xd5, 0x77, 0x51, 0x8e, + 0xcd, 0x7f, 0xc7, 0x39, 0xb6, 0xf0, 0x12, 0x73, 0xac, 0xfc, 0xeb, 0x05, 0x28, 0xee, 0x38, 0xa6, + 0xfd, 0xfd, 0x47, 0xfc, 0x16, 0x14, 0x2d, 0xc7, 0x79, 0x38, 0x1a, 0x46, 0x19, 0xcb, 0x42, 0x5f, + 0xd8, 0x4a, 0x20, 0x49, 0x2f, 0x04, 0x18, 0x91, 0xac, 0x6c, 0x02, 0x38, 0xc1, 0x04, 0xe1, 0x21, + 0xcd, 0x6d, 0xde, 0x98, 0x1e, 0x52, 0x25, 0x9a, 0x33, 0xe2, 0xc6, 0x79, 0xc7, 0x66, 0x30, 0xfc, + 0x3e, 0xa4, 0xd8, 0xb4, 0x13, 0xe1, 0xb9, 0x3a, 0xc5, 0x2a, 0xe6, 0x8b, 0xf6, 0xd9, 0x90, 0x0a, + 0x66, 0xce, 0xf2, 0xd2, 0xdb, 0xda, 0xfb, 0xf0, 0xfa, 0xb8, 0xe9, 0xc4, 0x70, 0x29, 0x79, 0x48, + 0xcf, 0x4a, 0x99, 0x58, 0x92, 0x5d, 0x1e, 0x73, 0x82, 0xec, 0xd2, 0x7b, 0xf4, 0xec, 0xc2, 0x84, + 0xcb, 0x7e, 0xc7, 0x09, 0x07, 0x2f, 0xb3, 0xa9, 0xbd, 0x0b, 0x97, 0x06, 0x86, 0x69, 0xfb, 0x86, + 0x69, 0x13, 0xc7, 0xed, 0x51, 0xd7, 0xb4, 0xfb, 0xbc, 0x6d, 0x46, 0xbd, 0x2a, 0x44, 0x6b, 0x02, + 0x3b, 0xa5, 0xbf, 0x15, 0x9e, 0xd6, 0xdf, 0x70, 0x15, 0xde, 0xb2, 0xe8, 0xa1, 0x4f, 0xf8, 0x76, + 0x74, 0x62, 0xfa, 0x47, 0x64, 0x68, 0x98, 0x2e, 0xed, 0x71, 0x00, 0x75, 0xc7, 0xba, 0x63, 0x89, + 0x51, 0xb2, 0xc0, 0xef, 0x9b, 0xfe, 0x51, 0x93, 0x93, 0xed, 0x70, 0x2a, 0xbc, 0x0f, 0x37, 0x9d, + 0x91, 0x3f, 0x1c, 0xf9, 0xa4, 0xef, 0x3a, 0x3c, 0x5c, 0xb6, 0x6f, 0xda, 0x23, 0xc3, 0x37, 0x1d, + 0x9b, 0x1c, 0x3a, 0x2e, 0xe1, 0x77, 0xb8, 0xce, 0x49, 0x69, 0x31, 0x26, 0xef, 0x5a, 0xc0, 0xb3, + 0xcd, 0x58, 0x2a, 0x31, 0x8e, 0x9a, 0xe3, 0x36, 0xe8, 0xa1, 0xaf, 0x3b, 0x27, 0x3b, 0xa9, 0x4c, + 0x1a, 0xcd, 0xef, 0xa4, 0x32, 0x79, 0x54, 0x60, 0x2b, 0x0f, 0xb4, 0x1c, 0xd7, 0x17, 0xe5, 0x77, + 0x1f, 0x16, 0xc5, 0x9d, 0x91, 0x7b, 0x82, 0x42, 0x2c, 0x4f, 0xcf, 0xb8, 0xd0, 0x55, 0x42, 0x81, + 0x62, 0x20, 0x20, 0xee, 0xc0, 0x50, 0x16, 0x19, 0x18, 0x7e, 0xf7, 0x88, 0x58, 0xd4, 0x1e, 0x2b, + 0x4b, 0x14, 0xe2, 0x77, 0x19, 0xba, 0x41, 0xed, 0xf2, 0xdf, 0x24, 0xc8, 0x57, 0x4d, 0xcf, 0x37, + 0xed, 0xae, 0xcf, 0xf5, 0x7a, 0x1b, 0x16, 0x39, 0x11, 0xed, 0x91, 0xf8, 0x3a, 0x56, 0xd0, 0x8b, + 0x02, 0x1c, 0xba, 0xfe, 0x16, 0xa0, 0x9e, 0x60, 0x8c, 0x28, 0x13, 0x9c, 0x72, 0x31, 0x84, 0x87, + 0xa4, 0x9b, 0x80, 0xed, 0x91, 0x65, 0x05, 0xf9, 0x1f, 0x22, 0xc7, 0xf6, 0x1c, 0xc4, 0xf1, 0xb2, + 0x4b, 0x43, 0x5d, 0xf0, 0x4d, 0xc8, 0x53, 0xd7, 0x75, 0x5c, 0xe2, 0xd8, 0xa4, 0x37, 0x1a, 0xf2, + 0x8e, 0x90, 0x0d, 0x8b, 0x8c, 0x63, 0x34, 0xbb, 0x3a, 0x1a, 0x96, 0x11, 0x14, 0x35, 0xb7, 0x67, + 0xda, 0x06, 0x2b, 0x39, 0x66, 0x41, 0xf9, 0x0f, 0x49, 0x40, 0x1f, 0x99, 0xfd, 0xcf, 0x8c, 0x7e, + 0x10, 0x5e, 0x6e, 0x56, 0x15, 0xe6, 0x79, 0xcb, 0x0a, 0x97, 0xcb, 0xd9, 0xda, 0x9d, 0xe0, 0xc5, + 0x35, 0x00, 0x7a, 0x3c, 0x66, 0x6d, 0x6e, 0xf3, 0xda, 0xf4, 0x78, 0x09, 0xfb, 0xc3, 0x0d, 0x8b, + 0x1e, 0x9f, 0xfb, 0xae, 0x18, 0xf4, 0x4d, 0x27, 0x50, 0x7d, 0xac, 0x29, 0x72, 0x8c, 0xb0, 0xe9, + 0x25, 0x35, 0xc5, 0x7b, 0x90, 0x3f, 0x34, 0x4f, 0x69, 0x8f, 0x3c, 0xe2, 0x3b, 0x77, 0x29, 0xcd, + 0x35, 0x7f, 0x4a, 0x6f, 0x1b, 0xdf, 0xcd, 0xf5, 0x1c, 0xe7, 0x0e, 0x80, 0x2f, 0xd0, 0x61, 0xcb, + 0xff, 0x48, 0xc2, 0xe2, 0x2e, 0x75, 0xfb, 0x34, 0x16, 0x99, 0x5d, 0x28, 0xf0, 0xf2, 0x7a, 0xee, + 0x32, 0xc8, 0x33, 0xf6, 0xa8, 0x08, 0x34, 0x28, 0xba, 0x66, 0xff, 0x28, 0x26, 0x2f, 0x31, 0xa3, + 0xbc, 0x02, 0xe7, 0x8f, 0x04, 0xc6, 0x02, 0x90, 0x7e, 0x15, 0x53, 0xe9, 0x16, 0x14, 0x58, 0x71, + 0x10, 0x7a, 0x3c, 0x32, 0xa2, 0xc1, 0x14, 0xd6, 0x4d, 0x9e, 0xa1, 0x14, 0x81, 0xc1, 0x1f, 0xc0, + 0x15, 0xee, 0xca, 0xf3, 0x1c, 0x9d, 0x32, 0x71, 0xe8, 0xa1, 0xaf, 0x1c, 0x8f, 0x4f, 0x9c, 0x5f, + 0x40, 0x29, 0xf0, 0xdb, 0x05, 0xcc, 0xd9, 0x18, 0xf3, 0x12, 0xa7, 0x9a, 0xe0, 0x2e, 0x7f, 0x9b, + 0x80, 0xe2, 0x5d, 0xc3, 0x3b, 0x8a, 0xc5, 0xf5, 0x36, 0x2c, 0x4e, 0x28, 0x13, 0x34, 0x12, 0x31, + 0xf9, 0xe3, 0x2a, 0xe0, 0x3b, 0x80, 0x26, 0x2f, 0x0f, 0x7a, 0x09, 0x27, 0x2e, 0x8e, 0x5f, 0xf9, + 0xca, 0x23, 0xf2, 0xca, 0xdc, 0xbc, 0x93, 0xca, 0x2c, 0xa0, 0x4c, 0xf9, 0xab, 0x24, 0xe0, 0xba, + 0x78, 0x79, 0xc7, 0x1c, 0xfe, 0x3d, 0x2d, 0x74, 0x1a, 0x14, 0xc2, 0x67, 0xff, 0xf3, 0xb6, 0xa5, + 0x7c, 0x28, 0x80, 0x47, 0xe2, 0x55, 0x87, 0xf3, 0xc2, 0x55, 0x65, 0xe1, 0xa9, 0xab, 0xca, 0xb3, + 0x2f, 0x0c, 0x99, 0x59, 0x17, 0x86, 0x24, 0x4a, 0x95, 0xff, 0x9c, 0x84, 0xa5, 0x30, 0xc0, 0x35, + 0xd3, 0xf2, 0xa9, 0x2b, 0x42, 0xbc, 0x0e, 0x28, 0xf2, 0x7d, 0xd7, 0xb1, 0x78, 0x94, 0xa4, 0x58, + 0x94, 0x8a, 0x21, 0xb6, 0xe2, 0x58, 0x2c, 0x56, 0x9f, 0x4c, 0xc6, 0x2a, 0xe8, 0x85, 0x3f, 0x99, + 0x70, 0x8f, 0x33, 0xf4, 0xd7, 0xe3, 0x9f, 0x72, 0xd6, 0xd9, 0x8b, 0xeb, 0xdc, 0xd5, 0x4d, 0xd7, + 0xf1, 0x9d, 0x0b, 0x43, 0xf7, 0x29, 0x5c, 0x1a, 0xba, 0x94, 0x1c, 0x0a, 0x1d, 0x89, 0x37, 0xa4, + 0x5d, 0x1e, 0x82, 0xdc, 0xe6, 0x2f, 0xa7, 0x07, 0xf1, 0x22, 0xd3, 0xd6, 0x9b, 0x2e, 0x8d, 0x9f, + 0xf5, 0xc5, 0xe1, 0x38, 0x60, 0xf9, 0xb7, 0x12, 0x2c, 0x4e, 0x10, 0xe1, 0x1d, 0x80, 0xf3, 0x0f, + 0x4e, 0x22, 0xef, 0x67, 0xc9, 0x9e, 0x18, 0x37, 0x5e, 0x17, 0x19, 0x14, 0xb8, 0x68, 0x79, 0x32, + 0x83, 0xe8, 0x60, 0x3d, 0xf8, 0x8a, 0xd6, 0x16, 0xb3, 0xec, 0xdb, 0x0c, 0x14, 0xe5, 0x7e, 0xdf, + 0xa5, 0x7d, 0xc3, 0x77, 0x02, 0x75, 0xae, 0x01, 0x84, 0xf9, 0x60, 0xc5, 0x1b, 0x58, 0xb6, 0x1f, + 0x84, 0xdc, 0xf2, 0xf0, 0x27, 0x90, 0x37, 0x04, 0x93, 0xe9, 0x44, 0xef, 0xe0, 0x9f, 0x4e, 0xd7, + 0x79, 0xfc, 0x8a, 0xe8, 0x18, 0x2b, 0xa6, 0xb8, 0x3c, 0xfc, 0x63, 0xb1, 0x03, 0xd2, 0x1e, 0x89, + 0xa9, 0x92, 0x8a, 0x54, 0x41, 0x02, 0xbb, 0x1d, 0x69, 0xb4, 0x2d, 0xec, 0x4e, 0xf3, 0xca, 0x79, + 0xe7, 0x99, 0x35, 0x99, 0xac, 0xa3, 0xe5, 0xdf, 0x24, 0x20, 0x17, 0x53, 0x8f, 0x09, 0x3e, 0x1c, + 0xd9, 0x5d, 0x1e, 0x96, 0x59, 0x04, 0xd7, 0x46, 0x76, 0x37, 0x14, 0xcc, 0x04, 0xe0, 0x55, 0xc8, + 0x44, 0x4b, 0x63, 0x22, 0x56, 0x4f, 0x11, 0x14, 0xdf, 0x80, 0x62, 0x90, 0x83, 0x51, 0x55, 0xb0, + 0xa6, 0x54, 0xd0, 0xf3, 0x01, 0x54, 0x54, 0xc3, 0x15, 0xfe, 0x85, 0x91, 0xa3, 0xd3, 0x7c, 0x51, + 0x9d, 0xef, 0x06, 0x88, 0xbb, 0x90, 0x35, 0xdc, 0xfe, 0x68, 0x40, 0x6d, 0xdf, 0x2b, 0xcd, 0xf3, + 0x88, 0xcc, 0x92, 0x45, 0xe7, 0xcc, 0xa2, 0x7e, 0xff, 0x94, 0x82, 0x14, 0xb3, 0x02, 0x23, 0xc8, + 0xcb, 0xea, 0x87, 0x44, 0xd5, 0xda, 0x44, 0xed, 0x34, 0x1a, 0x68, 0x0e, 0x2f, 0x40, 0x52, 0xde, + 0xdb, 0x46, 0x12, 0xce, 0x43, 0x66, 0x4b, 0xd3, 0x1a, 0x44, 0x56, 0xab, 0x28, 0x81, 0x73, 0xb0, + 0xc0, 0x4f, 0x9a, 0x8e, 0x92, 0xb8, 0x08, 0x50, 0xd1, 0xd4, 0x8a, 0xdc, 0x26, 0xf2, 0xf6, 0x36, + 0x4a, 0xe1, 0x2c, 0xa4, 0x2b, 0x5a, 0x47, 0x6d, 0xa3, 0x34, 0x63, 0xdf, 0x95, 0x1f, 0xa0, 0x05, + 0xfe, 0x4f, 0x5d, 0x45, 0x19, 0x0c, 0x30, 0xdf, 0x6a, 0x57, 0xab, 0xca, 0x1e, 0xca, 0x32, 0x60, + 0xab, 0xb3, 0x8b, 0x80, 0x89, 0x6b, 0x75, 0x76, 0x49, 0x5d, 0x6d, 0xa3, 0x1c, 0xbb, 0x69, 0x4f, + 0xd6, 0xeb, 0xb2, 0x5a, 0x51, 0x50, 0x9e, 0xa1, 0x1e, 0x68, 0x3a, 0x97, 0x5c, 0x08, 0x6e, 0xea, + 0xa8, 0x6d, 0xa2, 0x6b, 0xfb, 0x2d, 0x54, 0xe4, 0x7c, 0xf7, 0xf5, 0x6a, 0xbd, 0x56, 0x43, 0x8b, + 0x18, 0x43, 0xb1, 0x56, 0x57, 0xe5, 0x06, 0x89, 0xb8, 0x11, 0x33, 0x28, 0x80, 0x89, 0x3b, 0x2f, + 0xe1, 0x02, 0x64, 0x65, 0x5d, 0x97, 0x3f, 0xe4, 0x12, 0x31, 0xbb, 0x6c, 0xa7, 0xa5, 0xa9, 0xfc, + 0x74, 0x99, 0x21, 0xd9, 0x69, 0x8b, 0x1f, 0x97, 0xd8, 0x75, 0xad, 0xb6, 0x5e, 0x57, 0xb7, 0xf9, + 0xf9, 0x35, 0x6e, 0x75, 0xbd, 0xcd, 0x5d, 0xf0, 0x3a, 0x33, 0x84, 0x1d, 0x34, 0x1d, 0x5d, 0xc1, + 0x19, 0x48, 0x55, 0x34, 0x5d, 0x47, 0x25, 0x5c, 0x82, 0xa5, 0xa6, 0xa2, 0x57, 0x14, 0xb5, 0x5d, + 0x6f, 0x28, 0xa4, 0x5a, 0x6f, 0x55, 0x48, 0x7d, 0xb7, 0xd9, 0x40, 0x6f, 0x4c, 0x60, 0x2a, 0x9a, + 0xda, 0x0e, 0x30, 0xcb, 0xf8, 0x32, 0x2c, 0x72, 0x1d, 0xb4, 0xad, 0x1d, 0xa5, 0x12, 0x38, 0xf1, + 0x4d, 0xbc, 0x04, 0x28, 0x50, 0x25, 0x06, 0x7d, 0x8b, 0x69, 0xb0, 0x27, 0xeb, 0xa4, 0xa9, 0x35, + 0xd1, 0x0f, 0x02, 0xf5, 0x98, 0x59, 0xfc, 0xbc, 0x82, 0x17, 0x21, 0xd7, 0x6a, 0x93, 0x5d, 0xf9, + 0x9e, 0xd2, 0xa8, 0xab, 0x0a, 0xba, 0xca, 0xcc, 0x69, 0xb5, 0x89, 0xf2, 0xa0, 0xad, 0xa8, 0x6d, + 0xb4, 0xca, 0x6c, 0x6d, 0xb5, 0x49, 0x47, 0xad, 0x6b, 0x2a, 0xba, 0x16, 0x70, 0x93, 0x8a, 0xd6, + 0x68, 0x28, 0x95, 0x36, 0x2a, 0x33, 0xe2, 0x8a, 0x16, 0x0a, 0xbf, 0x1e, 0xb8, 0x9a, 0x1d, 0x5b, + 0xf2, 0x6e, 0x13, 0xdd, 0x28, 0xdf, 0x81, 0x14, 0xab, 0x20, 0x66, 0xaa, 0xdc, 0x69, 0x6b, 0x68, + 0x8e, 0x47, 0xb2, 0x22, 0x37, 0x64, 0x1d, 0x49, 0x8c, 0x5a, 0xd5, 0x54, 0x22, 0xce, 0x89, 0xf2, + 0xdf, 0x25, 0x28, 0x36, 0x5d, 0xe7, 0x53, 0xda, 0xf5, 0x5b, 0x34, 0x78, 0xa8, 0xfd, 0x0a, 0xd2, + 0xac, 0x7b, 0x85, 0x0f, 0x9a, 0x59, 0x12, 0x36, 0x60, 0xc4, 0xdb, 0x70, 0xa9, 0x4f, 0x6d, 0xea, + 0x1a, 0x7e, 0xec, 0xb1, 0x17, 0x3c, 0x6a, 0x9e, 0xd6, 0xfe, 0x50, 0xc4, 0x14, 0x2e, 0x64, 0x6f, + 0x03, 0xb2, 0x47, 0xfc, 0xc9, 0xee, 0x91, 0x21, 0x75, 0x49, 0x9f, 0xda, 0xc1, 0x83, 0x46, 0x2f, + 0xd8, 0x23, 0xf6, 0x56, 0xf7, 0x9a, 0xd4, 0xdd, 0xa6, 0x76, 0xf9, 0xeb, 0x02, 0xe4, 0xf7, 0x4d, + 0xbb, 0xe7, 0x9c, 0x88, 0x06, 0xbe, 0xca, 0xbf, 0x7d, 0xfa, 0x26, 0xef, 0x67, 0x67, 0xe2, 0xa5, + 0x19, 0x07, 0xe1, 0x16, 0x64, 0x4f, 0x38, 0x47, 0x2d, 0x52, 0x6e, 0x63, 0xba, 0xa9, 0x71, 0xe1, + 0xe2, 0x50, 0x8b, 0xca, 0x34, 0x92, 0xb3, 0xfc, 0x57, 0x49, 0x14, 0x68, 0x0b, 0x0a, 0x61, 0xfb, + 0xa4, 0xb5, 0xe7, 0x6d, 0x56, 0xfa, 0xb8, 0x0c, 0x7c, 0x1f, 0x40, 0x5c, 0xc5, 0x24, 0x26, 0xb8, + 0xc4, 0x77, 0x67, 0xd3, 0x99, 0x49, 0x8d, 0x09, 0xf9, 0x20, 0xf5, 0xf8, 0xf3, 0xab, 0xd2, 0xf2, + 0xe7, 0x0b, 0x90, 0xae, 0xb9, 0xc6, 0x80, 0xe2, 0x7b, 0x90, 0x1a, 0x38, 0x3d, 0x2a, 0xd4, 0x7d, + 0x56, 0xe1, 0x9c, 0x77, 0x7d, 0xd7, 0xe9, 0x45, 0x8d, 0x9b, 0x09, 0xc1, 0xf7, 0x61, 0xfe, 0xc0, + 0x19, 0xd9, 0x3d, 0x6f, 0xca, 0x7a, 0xf0, 0x74, 0x71, 0x5b, 0x9c, 0x35, 0x5c, 0xc7, 0x02, 0x41, + 0xf8, 0x23, 0xc8, 0xd2, 0xd3, 0xae, 0x35, 0xe2, 0x73, 0x39, 0xc9, 0x95, 0x7c, 0x6f, 0x26, 0xa9, + 0x4a, 0xc8, 0x1d, 0x3d, 0x9e, 0x43, 0xc0, 0xf2, 0x7f, 0x24, 0x48, 0xf3, 0x4b, 0xd9, 0x2d, 0xfc, + 0x3e, 0x56, 0x48, 0xc2, 0x15, 0xef, 0xcd, 0xae, 0x7b, 0x6c, 0x90, 0x9d, 0x8b, 0xc3, 0xd7, 0x01, + 0x4c, 0xdb, 0x27, 0xce, 0xe1, 0xa1, 0x47, 0x83, 0xb1, 0x13, 0xfe, 0x0c, 0x91, 0x35, 0x6d, 0x5f, + 0xe3, 0x60, 0x7c, 0x0d, 0xf2, 0xac, 0x2a, 0x7a, 0x21, 0x19, 0xb3, 0x34, 0xaf, 0xe7, 0x38, 0x4c, + 0x90, 0xec, 0x40, 0x2e, 0x40, 0xf2, 0x1f, 0xef, 0xc4, 0xb2, 0x3c, 0xc3, 0x4f, 0x5b, 0x10, 0x70, + 0x33, 0x9d, 0x96, 0xff, 0x28, 0xc1, 0x7c, 0xe0, 0x6e, 0xac, 0x42, 0xda, 0xf3, 0x0d, 0xd7, 0x17, + 0x4b, 0xcf, 0xe6, 0xec, 0x66, 0x47, 0x5f, 0xd4, 0x99, 0x18, 0x5c, 0x85, 0x24, 0xb5, 0x7b, 0x22, + 0x01, 0x9e, 0x43, 0x9a, 0xce, 0xd8, 0xcb, 0x6f, 0x43, 0x8a, 0x65, 0x17, 0x9b, 0x58, 0xba, 0xac, + 0x6e, 0x2b, 0x68, 0x8e, 0xf5, 0x37, 0x3e, 0x5c, 0x24, 0xd6, 0xdf, 0xb6, 0x75, 0xad, 0xd3, 0x6c, + 0xa1, 0x44, 0xf9, 0x33, 0xc8, 0x46, 0xbe, 0xc7, 0x57, 0xe0, 0x72, 0x47, 0xdd, 0xd2, 0x3a, 0x6a, + 0x55, 0xa9, 0x92, 0xa6, 0xae, 0x54, 0x94, 0x6a, 0x5d, 0xdd, 0x46, 0x73, 0xe3, 0x88, 0x9a, 0xd6, + 0x68, 0x68, 0xfb, 0x0c, 0x21, 0xb1, 0x66, 0xae, 0xd5, 0x6a, 0x2d, 0xa5, 0x1d, 0x23, 0x4f, 0xc4, + 0xa0, 0xe7, 0xb4, 0x49, 0xd6, 0xc5, 0x2b, 0x1d, 0x5d, 0x57, 0x82, 0x29, 0x87, 0x52, 0xe5, 0x8f, + 0x21, 0x1b, 0x65, 0x17, 0x1b, 0x68, 0xaa, 0x46, 0x94, 0x07, 0x95, 0x46, 0xa7, 0xc5, 0xfa, 0x38, + 0xbf, 0x94, 0x1f, 0xab, 0x0a, 0x89, 0xf3, 0x49, 0xf8, 0x12, 0x14, 0x42, 0x04, 0xb7, 0x03, 0x25, + 0x18, 0x77, 0x08, 0x6a, 0xd7, 0x95, 0x16, 0x4a, 0x2e, 0xff, 0x33, 0x01, 0x99, 0xb0, 0xef, 0x60, + 0x25, 0xb6, 0x01, 0xe5, 0x36, 0x7f, 0xf4, 0xac, 0x5e, 0x9d, 0xdc, 0x7f, 0xaa, 0x90, 0x89, 0xde, + 0x25, 0xa9, 0x19, 0x3f, 0x66, 0x44, 0x9c, 0xec, 0x79, 0x78, 0xc8, 0xe2, 0x25, 0x1e, 0x59, 0x77, + 0x66, 0x89, 0xb1, 0x1e, 0xb0, 0xe2, 0x35, 0x18, 0xdb, 0xa8, 0xf8, 0xaa, 0x9f, 0x0e, 0xf7, 0xd0, + 0xb1, 0x5d, 0x6b, 0x19, 0x32, 0x86, 0xdb, 0xf7, 0xea, 0xbd, 0x53, 0xaf, 0xb4, 0xc0, 0xbb, 0x7a, + 0x74, 0x66, 0x52, 0x82, 0x97, 0x90, 0x90, 0x92, 0x89, 0xbd, 0x60, 0xc6, 0x30, 0x3b, 0xa9, 0x4c, + 0x02, 0x25, 0xc5, 0x52, 0xf5, 0x17, 0x09, 0xe0, 0xbc, 0x3b, 0xb2, 0x09, 0xa9, 0x6b, 0xfb, 0x44, + 0xed, 0xec, 0x6e, 0x29, 0xba, 0xc8, 0x33, 0x59, 0xbd, 0x17, 0xcc, 0xce, 0xaa, 0xa2, 0xb6, 0x14, + 0xc2, 0xcf, 0x3c, 0x48, 0x62, 0x51, 0x08, 0x20, 0x49, 0x3e, 0x9a, 0x3b, 0xbb, 0x7c, 0x9d, 0x68, + 0x07, 0xfb, 0x15, 0x5f, 0x22, 0x82, 0xfd, 0xaa, 0x21, 0x6f, 0xa3, 0x79, 0x26, 0xae, 0xa1, 0xc8, + 0x55, 0xb4, 0xc0, 0xf2, 0xa7, 0x56, 0xd7, 0x5b, 0x6d, 0xb2, 0x27, 0x37, 0x3a, 0x0a, 0xca, 0x30, + 0xf9, 0x0d, 0x39, 0x3a, 0x67, 0x99, 0x34, 0xb5, 0x7d, 0x57, 0x1c, 0xe1, 0xf6, 0xcf, 0xa0, 0x38, + 0xfe, 0xcb, 0x00, 0x4b, 0xfc, 0x66, 0x67, 0xab, 0x51, 0xaf, 0xa0, 0x39, 0xfc, 0x06, 0xbc, 0x16, + 0xfc, 0xcf, 0xb6, 0x1e, 0xbe, 0x18, 0x0a, 0x94, 0xb4, 0xf5, 0xce, 0xe3, 0xaf, 0x57, 0xe6, 0x1e, + 0x3f, 0x59, 0x91, 0xbe, 0x78, 0xb2, 0x22, 0x7d, 0xf9, 0x64, 0x45, 0xfa, 0xf7, 0x93, 0x15, 0xe9, + 0x77, 0xdf, 0xac, 0xcc, 0x7d, 0xf1, 0xcd, 0xca, 0xdc, 0x97, 0xdf, 0xac, 0xcc, 0x7d, 0x94, 0x8b, + 0xfd, 0x34, 0xff, 0xbf, 0x00, 0x00, 0x00, 0xff, 0xff, 0x72, 0xff, 0x51, 0xac, 0x8a, 0x20, 0x00, + 0x00, } diff --git a/pkg/sql/execinfrapb/processors_sql.proto b/pkg/sql/execinfrapb/processors_sql.proto index b44ec1a34082..bba9bc71bb09 100644 --- a/pkg/sql/execinfrapb/processors_sql.proto +++ b/pkg/sql/execinfrapb/processors_sql.proto @@ -243,6 +243,11 @@ message IndexSkipTableReaderSpec { // The output for LEFT_SEMI: // a2, b1, c1, d4, true // Again, the d, cont columns will be projected away after the join. +// +// The example above is for a lookup join as the second join in the +// paired-joins. The lookup join can also be the first join in the +// paired-joins, which is indicated by both +// OutputGroupContinuationForLeftRow and MaintainOrdering set to true. message JoinReaderSpec { optional sqlbase.TableDescriptor table = 1 [(gogoproto.nullable) = false]; @@ -314,8 +319,16 @@ message JoinReaderSpec { optional bool has_system_columns = 13 [(gogoproto.nullable) = false]; // LeftJoinWithPairedJoiner is used when a left {outer,anti,semi} join is - // being achieved by pairing two joins. See the comment above. + // being achieved by pairing two joins, and this is the second join. See + // the comment above. optional bool left_join_with_paired_joiner = 14 [(gogoproto.nullable) = false]; + + // OutputGroupContinuationForLeftRow indicates that this join is the first + // join in the paired-joins. At most one of OutputGroupContinuationForLeftRow + // and LeftJoinWithPairedJoiner must be true. Additionally, if + // OutputGroupContinuationForLeftRow is true, MaintainOrdering must also + // be true. + optional bool output_group_continuation_for_left_row = 15 [(gogoproto.nullable) = false]; } // SorterSpec is the specification for a "sorting aggregator". A sorting diff --git a/pkg/sql/rowexec/hashjoiner.go b/pkg/sql/rowexec/hashjoiner.go index a79e7894f93c..ab775bd9f64c 100644 --- a/pkg/sql/rowexec/hashjoiner.go +++ b/pkg/sql/rowexec/hashjoiner.go @@ -158,6 +158,7 @@ func newHashJoiner( spec.OnExpr, spec.LeftEqColumns, spec.RightEqColumns, + false, /* outputContinuationColumn */ post, output, execinfra.ProcStateOpts{ diff --git a/pkg/sql/rowexec/joinerbase.go b/pkg/sql/rowexec/joinerbase.go index 22067c2af5fa..eef9e663c0ef 100644 --- a/pkg/sql/rowexec/joinerbase.go +++ b/pkg/sql/rowexec/joinerbase.go @@ -50,6 +50,7 @@ func (jb *joinerBase) init( onExpr execinfrapb.Expression, leftEqColumns []uint32, rightEqColumns []uint32, + outputContinuationColumn bool, post *execinfrapb.PostProcessSpec, output execinfra.RowReceiver, opts execinfra.ProcStateOpts, @@ -74,18 +75,26 @@ func (jb *joinerBase) init( jb.eqCols[leftSide] = leftEqColumns jb.eqCols[rightSide] = rightEqColumns - size := len(leftTypes) + len(rightTypes) - jb.combinedRow = make(rowenc.EncDatumRow, size) + rowSize := len(leftTypes) + len(rightTypes) + if outputContinuationColumn { + // NB: Can only be true for inner joins and left outer joins. + rowSize++ + } + jb.combinedRow = make(rowenc.EncDatumRow, rowSize) - condTypes := make([]*types.T, 0, size) + // condTypes does not include the continuation column, but the slice has the + // capacity for it, since outputTypes later adds the continuation column. + condTypes := make([]*types.T, 0, rowSize) condTypes = append(condTypes, leftTypes...) condTypes = append(condTypes, rightTypes...) - outputSize := len(leftTypes) - if jb.joinType.ShouldIncludeRightColsInOutput() { - outputSize += len(rightTypes) + outputTypes := condTypes + // NB: outputContinuationCol implies jb.joinType.ShouldIncludeRightColsInOutput() + if !jb.joinType.ShouldIncludeRightColsInOutput() { + outputTypes = outputTypes[:len(leftTypes)] + } else if outputContinuationColumn { + outputTypes = append(outputTypes, types.Bool) } - outputTypes := condTypes[:outputSize] if err := jb.ProcessorBase.Init( self, post, outputTypes, flowCtx, processorID, output, nil /* memMonitor */, opts, @@ -119,7 +128,9 @@ func (j joinSide) String() string { } // renderUnmatchedRow creates a result row given an unmatched row on either -// side. Only used for outer joins. +// side. Only used for outer joins. Note that if the join is outputting a +// continuation column, the returned slice does not include the continuation +// column, but has the capacity for it. func (jb *joinerBase) renderUnmatchedRow(row rowenc.EncDatumRow, side joinSide) rowenc.EncDatumRow { lrow, rrow := jb.emptyLeft, jb.emptyRight if side == leftSide { @@ -158,7 +169,9 @@ func shouldEmitUnmatchedRow(side joinSide, joinType descpb.JoinType) bool { } // render constructs a row with columns from both sides. The ON condition is -// evaluated; if it fails, returns nil. +// evaluated; if it fails, returns nil. Note that if the join is outputting a +// continuation column, the returned slice does not include the continuation +// column, but has the capacity for it. func (jb *joinerBase) render(lrow, rrow rowenc.EncDatumRow) (rowenc.EncDatumRow, error) { jb.combinedRow = jb.combinedRow[:len(lrow)+len(rrow)] copy(jb.combinedRow, lrow) diff --git a/pkg/sql/rowexec/joinreader.go b/pkg/sql/rowexec/joinreader.go index 7d738fd7ee44..8374a89a5720 100644 --- a/pkg/sql/rowexec/joinreader.go +++ b/pkg/sql/rowexec/joinreader.go @@ -110,19 +110,14 @@ type joinReader struct { // (or evaluate it accurately, as is sometimes the case with inverted // indexes). The first join is running a left outer or inner join, and each // group of rows seen by the second join correspond to one left row. - // - // TODO(sumeer): add support for joinReader to also be the first - // join in this pair, say when the index can evaluate most of the - // join condition (the part with low selectivity), but not all. - // Currently only the invertedJoiner can serve as the first join - // in the pair. // The input rows in the current batch belong to groups which are tracked in // groupingState. The last row from the last batch is in // lastInputRowFromLastBatch -- it is tracked because we don't know if it // was the last row in a group until we get to the next batch. NB: // groupingState is used even when there is no grouping -- we simply have - // groups of one. + // groups of one. The no grouping cases include the case of this join being + // the first join in the paired joins. groupingState *inputBatchGroupingState lastBatchState struct { @@ -130,6 +125,11 @@ type joinReader struct { lastGroupMatched bool lastGroupContinued bool } + + // Set to true when this is the first join in the paired-joins (see the + // detailed comment in the spec). This can never be true for index joins, + // and requires that the spec has MaintainOrdering set to true. + outputGroupContinuationForLeftRow bool } var _ execinfra.Processor = &joinReader{} @@ -153,6 +153,10 @@ func newJoinReader( if spec.IndexIdx != 0 && readerType == indexJoinReaderType { return nil, errors.AssertionFailedf("index join must be against primary index") } + if spec.OutputGroupContinuationForLeftRow && !spec.MaintainOrdering { + return nil, errors.AssertionFailedf( + "lookup join must maintain ordering since it is first join in paired-joins") + } var lookupCols []uint32 switch readerType { @@ -168,15 +172,14 @@ func newJoinReader( return nil, errors.Errorf("unsupported joinReaderType") } jr := &joinReader{ - desc: tabledesc.MakeImmutable(spec.Table), - maintainOrdering: spec.MaintainOrdering, - input: input, - inputTypes: input.OutputTypes(), - lookupCols: lookupCols, + desc: tabledesc.MakeImmutable(spec.Table), + maintainOrdering: spec.MaintainOrdering, + input: input, + inputTypes: input.OutputTypes(), + lookupCols: lookupCols, + outputGroupContinuationForLeftRow: spec.OutputGroupContinuationForLeftRow, } if readerType != indexJoinReaderType { - // TODO(sumeer): When LeftJoinWithPairedJoiner, the lookup columns and the - // bool column must be projected away by the optimizer after this join. jr.groupingState = &inputBatchGroupingState{doGrouping: spec.LeftJoinWithPairedJoiner} } var err error @@ -239,6 +242,7 @@ func newJoinReader( spec.OnExpr, leftEqCols, indexCols, + spec.OutputGroupContinuationForLeftRow, post, output, execinfra.ProcStateOpts{ @@ -361,11 +365,12 @@ func (jr *joinReader) initJoinReaderStrategy( drc.DisableCache = true } jr.strategy = &joinReaderOrderingStrategy{ - joinerBase: &jr.joinerBase, - defaultSpanGenerator: spanGenerator, - isPartialJoin: jr.joinType == descpb.LeftSemiJoin || jr.joinType == descpb.LeftAntiJoin, - lookedUpRows: drc, - groupingState: jr.groupingState, + joinerBase: &jr.joinerBase, + defaultSpanGenerator: spanGenerator, + isPartialJoin: jr.joinType == descpb.LeftSemiJoin || jr.joinType == descpb.LeftAntiJoin, + lookedUpRows: drc, + groupingState: jr.groupingState, + outputGroupContinuationForLeftRow: jr.outputGroupContinuationForLeftRow, } } @@ -404,8 +409,14 @@ func (jr *joinReader) neededRightCols() util.FastIntSet { // Get the columns from the right side of the join and shift them over by // the size of the left side so the right side starts at 0. neededRightCols := util.MakeFastIntSet() + var lastCol int for i, ok := neededCols.Next(len(jr.inputTypes)); ok; i, ok = neededCols.Next(i + 1) { - neededRightCols.Add(i - len(jr.inputTypes)) + lastCol = i - len(jr.inputTypes) + neededRightCols.Add(lastCol) + } + if jr.outputGroupContinuationForLeftRow { + // The lastCol is the bool continuation column and not a right column. + neededRightCols.Remove(lastCol) } // Add columns needed by OnExpr. @@ -580,7 +591,7 @@ func (jr *joinReader) performLookup() (joinReaderState, *execinfrapb.ProducerMet } } - // Fetch the next row and copy it into the row container. + // Fetch the next row and tell the strategy to process it. lookedUpRow, _, _, err := jr.fetcher.NextRow(jr.Ctx) if err != nil { jr.MoveToDraining(scrub.UnwrapScrubError(err)) @@ -868,12 +879,16 @@ func (ib *inputBatchGroupingState) setFirstGroupMatched() { ib.groupState[0].matched = true } -func (ib *inputBatchGroupingState) setMatched(rowIndex int) { +// setMatched records that the given rowIndex has matched. It returns the +// previous value of the matched field. +func (ib *inputBatchGroupingState) setMatched(rowIndex int) bool { groupIndex := rowIndex if ib.doGrouping { groupIndex = ib.batchRowToGroupIndex[rowIndex] } + rv := ib.groupState[groupIndex].matched ib.groupState[groupIndex].matched = true + return rv } func (ib *inputBatchGroupingState) getMatched(rowIndex int) bool { diff --git a/pkg/sql/rowexec/joinreader_strategies.go b/pkg/sql/rowexec/joinreader_strategies.go index ae099eed47d9..45d246d8e8cf 100644 --- a/pkg/sql/rowexec/joinreader_strategies.go +++ b/pkg/sql/rowexec/joinreader_strategies.go @@ -390,6 +390,12 @@ type joinReaderOrderingStrategy struct { } groupingState *inputBatchGroupingState + + // outputGroupContinuationForLeftRow is true when this join is the first + // join in paired-joins. Note that in this case the input batches will + // always be of size 1 (real input batching only happens when this join is + // the second join in paired-joins). + outputGroupContinuationForLeftRow bool } func (s *joinReaderOrderingStrategy) getLookupRowsBatchSizeHint() int64 { @@ -445,7 +451,8 @@ func (s *joinReaderOrderingStrategy) processLookedUpRow( // During a SemiJoin or AntiJoin, we only output if we've seen no match // for this input row yet. Additionally, since we don't have to render // anything to output a Semi or Anti join match, we can evaluate our - // on condition now. + // on condition now. NB: the first join in paired-joins is never a + // SemiJoin or AntiJoin. if !s.groupingState.getMatched(inputRowIdx) { renderedRow, err := s.render(s.inputRows[inputRowIdx], row) if err != nil { @@ -500,6 +507,10 @@ func (s *joinReaderOrderingStrategy) nextRowToEmit( // An outer-join non-match means we emit the input row with NULLs for // the right side. if renderedRow := s.renderUnmatchedRow(inputRow, leftSide); renderedRow != nil { + if s.outputGroupContinuationForLeftRow { + // This must be the first row being output for this input row. + renderedRow = append(renderedRow, falseEncDatum) + } return renderedRow, jrEmittingRows, nil } case descpb.LeftAntiJoin: @@ -532,7 +543,16 @@ func (s *joinReaderOrderingStrategy) nextRowToEmit( return nil, jrStateUnknown, err } if outputRow != nil { - s.groupingState.setMatched(s.emitCursor.inputRowIdx) + wasAlreadyMatched := s.groupingState.setMatched(s.emitCursor.inputRowIdx) + if s.outputGroupContinuationForLeftRow { + if wasAlreadyMatched { + // Not the first row output for this input row. + outputRow = append(outputRow, trueEncDatum) + } else { + // First row output for this input row. + outputRow = append(outputRow, falseEncDatum) + } + } } return outputRow, jrEmittingRows, nil } diff --git a/pkg/sql/rowexec/joinreader_test.go b/pkg/sql/rowexec/joinreader_test.go index 6fa5945a7703..483ababf6fad 100644 --- a/pkg/sql/rowexec/joinreader_test.go +++ b/pkg/sql/rowexec/joinreader_test.go @@ -107,17 +107,25 @@ func TestJoinReader(t *testing.T) { tdInterleaved := catalogkv.TestingGetTableDescriptor(kvDB, keys.SystemSQLCodec, "test", "t3") testCases := []struct { - description string - indexIdx uint32 - post execinfrapb.PostProcessSpec - onExpr string - input [][]tree.Datum - lookupCols []uint32 - joinType descpb.JoinType - inputTypes []*types.T - outputTypes []*types.T - leftJoinPaired bool - expected string + description string + indexIdx uint32 + // The OutputColumns in post are the ones without continuation. For tests + // that include the continuation column, the test adds the column position + // using outputColumnForContinuation. + post execinfrapb.PostProcessSpec + onExpr string + input [][]tree.Datum + lookupCols []uint32 + joinType descpb.JoinType + inputTypes []*types.T + // The output types for the case without continuation. The test adds the + // bool type for the case with continuation. + outputTypes []*types.T + secondJoinInPairedJoin bool + // Without and with continuation output. + expected string + expectedWithContinuation string + outputColumnForContinuation uint32 }{ { description: "Test selecting columns from second table", @@ -131,10 +139,12 @@ func TestJoinReader(t *testing.T) { {aFn(10), bFn(10)}, {aFn(15), bFn(15)}, }, - lookupCols: []uint32{0, 1}, - inputTypes: rowenc.TwoIntCols, - outputTypes: rowenc.ThreeIntCols, - expected: "[[0 2 2] [0 5 5] [1 0 1] [1 5 6]]", + lookupCols: []uint32{0, 1}, + inputTypes: rowenc.TwoIntCols, + outputTypes: rowenc.ThreeIntCols, + expected: "[[0 2 2] [0 5 5] [1 0 1] [1 5 6]]", + expectedWithContinuation: "[[0 2 2 false] [0 5 5 false] [1 0 1 false] [1 5 6 false]]", + outputColumnForContinuation: 6, }, { description: "Test duplicates in the input of lookup joins", @@ -149,10 +159,12 @@ func TestJoinReader(t *testing.T) { {aFn(10), bFn(10)}, {aFn(15), bFn(15)}, }, - lookupCols: []uint32{0, 1}, - inputTypes: rowenc.TwoIntCols, - outputTypes: rowenc.ThreeIntCols, - expected: "[[0 2 2] [0 2 2] [0 5 5] [1 0 0] [1 5 5]]", + lookupCols: []uint32{0, 1}, + inputTypes: rowenc.TwoIntCols, + outputTypes: rowenc.ThreeIntCols, + expected: "[[0 2 2] [0 2 2] [0 5 5] [1 0 0] [1 5 5]]", + expectedWithContinuation: "[[0 2 2 false] [0 2 2 false] [0 5 5 false] [1 0 0 false] [1 5 5 false]]", + outputColumnForContinuation: 6, }, { description: "Test lookup join queries with separate families", @@ -166,10 +178,12 @@ func TestJoinReader(t *testing.T) { {aFn(10), bFn(10)}, {aFn(15), bFn(15)}, }, - lookupCols: []uint32{0, 1}, - inputTypes: rowenc.TwoIntCols, - outputTypes: rowenc.FourIntCols, - expected: "[[0 2 2 2] [0 5 5 5] [1 0 0 1] [1 5 5 6]]", + lookupCols: []uint32{0, 1}, + inputTypes: rowenc.TwoIntCols, + outputTypes: rowenc.FourIntCols, + expected: "[[0 2 2 2] [0 5 5 5] [1 0 0 1] [1 5 5 6]]", + expectedWithContinuation: "[[0 2 2 2 false] [0 5 5 5 false] [1 0 0 1 false] [1 5 5 6 false]]", + outputColumnForContinuation: 6, }, { description: "Test lookup joins preserve order of left input", @@ -184,10 +198,12 @@ func TestJoinReader(t *testing.T) { {aFn(10), bFn(10)}, {aFn(15), bFn(15)}, }, - lookupCols: []uint32{0, 1}, - inputTypes: rowenc.TwoIntCols, - outputTypes: rowenc.ThreeIntCols, - expected: "[[0 2 2] [0 5 5] [0 2 2] [1 0 0] [1 5 5]]", + lookupCols: []uint32{0, 1}, + inputTypes: rowenc.TwoIntCols, + outputTypes: rowenc.ThreeIntCols, + expected: "[[0 2 2] [0 5 5] [0 2 2] [1 0 0] [1 5 5]]", + expectedWithContinuation: "[[0 2 2 false] [0 5 5 false] [0 2 2 false] [1 0 0 false] [1 5 5 false]]", + outputColumnForContinuation: 6, }, { description: "Test lookup join with onExpr", @@ -201,11 +217,13 @@ func TestJoinReader(t *testing.T) { {aFn(10), bFn(10)}, {aFn(15), bFn(15)}, }, - lookupCols: []uint32{0, 1}, - inputTypes: rowenc.TwoIntCols, - outputTypes: rowenc.ThreeIntCols, - onExpr: "@2 < @5", - expected: "[[1 0 1] [1 5 6]]", + lookupCols: []uint32{0, 1}, + inputTypes: rowenc.TwoIntCols, + outputTypes: rowenc.ThreeIntCols, + onExpr: "@2 < @5", + expected: "[[1 0 1] [1 5 6]]", + expectedWithContinuation: "[[1 0 1 false] [1 5 6 false]]", + outputColumnForContinuation: 6, }, { description: "Test left outer lookup join on primary index", @@ -217,11 +235,66 @@ func TestJoinReader(t *testing.T) { {aFn(100), bFn(100)}, {aFn(2), bFn(2)}, }, - lookupCols: []uint32{0, 1}, + lookupCols: []uint32{0, 1}, + joinType: descpb.LeftOuterJoin, + inputTypes: rowenc.TwoIntCols, + outputTypes: rowenc.ThreeIntCols, + expected: "[[10 0 NULL] [0 2 2]]", + expectedWithContinuation: "[[10 0 NULL false] [0 2 2 false]]", + outputColumnForContinuation: 6, + }, + { + description: "Test lookup join with multiple matches for a row", + post: execinfrapb.PostProcessSpec{ + Projection: true, + OutputColumns: []uint32{0, 1, 2, 4}, + }, + input: [][]tree.Datum{ + {aFn(2), bFn(2)}, + // No match for this row. + {aFn(200), bFn(200)}, + {aFn(12), bFn(12)}, + }, + lookupCols: []uint32{0}, + inputTypes: rowenc.TwoIntCols, + outputTypes: rowenc.FourIntCols, + expected: "[[0 2 0 1] [0 2 0 2] [0 2 0 3] [0 2 0 4] [0 2 0 5] [0 2 0 6] [0 2 0 7] " + + "[0 2 0 8] [0 2 0 9] " + + "[1 2 1 1] [1 2 1 2] [1 2 1 3] [1 2 1 4] [1 2 1 5] [1 2 1 6] [1 2 1 7] [1 2 1 8] " + + "[1 2 1 9] [1 2 1 10]", + expectedWithContinuation: "[[0 2 0 1 false] [0 2 0 2 true] [0 2 0 3 true] [0 2 0 4 true] " + + "[0 2 0 5 true] [0 2 0 6 true] [0 2 0 7 true] [0 2 0 8 true] [0 2 0 9 true] " + + "[1 2 1 1 false] [1 2 1 2 true] [1 2 1 3 true] [1 2 1 4 true] [1 2 1 5 true] " + + "[1 2 1 6 true] [1 2 1 7 true] [1 2 1 8 true] [1 2 1 9 true] [1 2 1 10 true]", + outputColumnForContinuation: 6, + }, + { + description: "Test left outer lookup join with multiple matches for a row", + post: execinfrapb.PostProcessSpec{ + Projection: true, + OutputColumns: []uint32{0, 1, 2, 4}, + }, + input: [][]tree.Datum{ + {aFn(2), bFn(2)}, + // No match for this row. + {aFn(200), bFn(200)}, + {aFn(12), bFn(12)}, + }, + lookupCols: []uint32{0}, joinType: descpb.LeftOuterJoin, inputTypes: rowenc.TwoIntCols, - outputTypes: rowenc.ThreeIntCols, - expected: "[[10 0 NULL] [0 2 2]]", + outputTypes: rowenc.FourIntCols, + expected: "[[0 2 0 1] [0 2 0 2] [0 2 0 3] [0 2 0 4] [0 2 0 5] [0 2 0 6] [0 2 0 7] " + + "[0 2 0 8] [0 2 0 9] " + + "[20 0 NULL NULL] " + + "[1 2 1 1] [1 2 1 2] [1 2 1 3] [1 2 1 4] [1 2 1 5] [1 2 1 6] [1 2 1 7] [1 2 1 8] " + + "[1 2 1 9] [1 2 1 10]", + expectedWithContinuation: "[[0 2 0 1 false] [0 2 0 2 true] [0 2 0 3 true] [0 2 0 4 true] " + + "[0 2 0 5 true] [0 2 0 6 true] [0 2 0 7 true] [0 2 0 8 true] [0 2 0 9 true] " + + "[20 0 NULL NULL false] " + + "[1 2 1 1 false] [1 2 1 2 true] [1 2 1 3 true] [1 2 1 4 true] [1 2 1 5 true] " + + "[1 2 1 6 true] [1 2 1 7 true] [1 2 1 8 true] [1 2 1 9 true] [1 2 1 10 true]", + outputColumnForContinuation: 6, }, { description: "Test lookup join on secondary index with NULL lookup value", @@ -233,10 +306,12 @@ func TestJoinReader(t *testing.T) { input: [][]tree.Datum{ {tree.NewDInt(0), tree.DNull}, }, - lookupCols: []uint32{0, 1}, - inputTypes: rowenc.TwoIntCols, - outputTypes: rowenc.OneIntCol, - expected: "[]", + lookupCols: []uint32{0, 1}, + inputTypes: rowenc.TwoIntCols, + outputTypes: rowenc.OneIntCol, + expected: "[]", + expectedWithContinuation: "[]", + outputColumnForContinuation: 6, }, { description: "Test left outer lookup join on secondary index with NULL lookup value", @@ -248,11 +323,13 @@ func TestJoinReader(t *testing.T) { input: [][]tree.Datum{ {tree.NewDInt(0), tree.DNull}, }, - lookupCols: []uint32{0, 1}, - joinType: descpb.LeftOuterJoin, - inputTypes: rowenc.TwoIntCols, - outputTypes: rowenc.TwoIntCols, - expected: "[[0 NULL]]", + lookupCols: []uint32{0, 1}, + joinType: descpb.LeftOuterJoin, + inputTypes: rowenc.TwoIntCols, + outputTypes: rowenc.TwoIntCols, + expected: "[[0 NULL]]", + expectedWithContinuation: "[[0 NULL false]]", + outputColumnForContinuation: 6, }, { description: "Test lookup join on secondary index with an implicit key column", @@ -264,10 +341,12 @@ func TestJoinReader(t *testing.T) { input: [][]tree.Datum{ {aFn(2), bFn(2), sqlutils.RowEnglishFn(2)}, }, - lookupCols: []uint32{1, 2, 0}, - inputTypes: []*types.T{types.Int, types.Int, types.String}, - outputTypes: rowenc.OneIntCol, - expected: "[['two']]", + lookupCols: []uint32{1, 2, 0}, + inputTypes: []*types.T{types.Int, types.Int, types.String}, + outputTypes: rowenc.OneIntCol, + expected: "[['two']]", + expectedWithContinuation: "[['two' false]]", + outputColumnForContinuation: 7, }, { description: "Test left semi lookup join", @@ -398,7 +477,7 @@ func TestJoinReader(t *testing.T) { expected: "[[0 NULL]]", }, { - description: "Test paired join with outer join", + description: "Test second join in paired-joins with outer join", post: execinfrapb.PostProcessSpec{ Projection: true, OutputColumns: []uint32{0, 1, 2, 4, 5, 6, 7}, @@ -414,16 +493,16 @@ func TestJoinReader(t *testing.T) { {tree.NewDInt(tree.DInt(34)), aFn(110), bFn(110), tree.DBoolTrue}, {tree.NewDInt(tree.DInt(34)), aFn(120), bFn(120), tree.DBoolTrue}, }, - lookupCols: []uint32{1, 2}, - joinType: descpb.LeftOuterJoin, - inputTypes: threeIntColsAndBoolCol, - outputTypes: sixIntColsAndStringCol, - leftJoinPaired: true, + lookupCols: []uint32{1, 2}, + joinType: descpb.LeftOuterJoin, + inputTypes: threeIntColsAndBoolCol, + outputTypes: sixIntColsAndStringCol, + secondJoinInPairedJoin: true, expected: "[[12 0 2 0 2 2 'two'] [12 0 5 0 5 5 'five'] [23 NULL NULL NULL NULL NULL NULL] " + "[26 0 7 0 7 7 'seven'] [34 12 0 NULL NULL NULL NULL]]", }, { - description: "Test paired join with semi join", + description: "Test second join in paired-joins with semi join", post: execinfrapb.PostProcessSpec{ Projection: true, OutputColumns: []uint32{0, 1, 2}, @@ -438,15 +517,15 @@ func TestJoinReader(t *testing.T) { {tree.NewDInt(tree.DInt(34)), aFn(110), bFn(110), tree.DBoolTrue}, {tree.NewDInt(tree.DInt(34)), aFn(120), bFn(120), tree.DBoolTrue}, }, - lookupCols: []uint32{1, 2}, - joinType: descpb.LeftSemiJoin, - inputTypes: threeIntColsAndBoolCol, - outputTypes: rowenc.ThreeIntCols, - leftJoinPaired: true, - expected: "[[12 0 2] [26 0 7]]", + lookupCols: []uint32{1, 2}, + joinType: descpb.LeftSemiJoin, + inputTypes: threeIntColsAndBoolCol, + outputTypes: rowenc.ThreeIntCols, + secondJoinInPairedJoin: true, + expected: "[[12 0 2] [26 0 7]]", }, { - description: "Test paired join with anti join", + description: "Test second join in paired-joins with anti join", post: execinfrapb.PostProcessSpec{ Projection: true, OutputColumns: []uint32{0, 1, 2}, @@ -462,16 +541,16 @@ func TestJoinReader(t *testing.T) { {tree.NewDInt(tree.DInt(34)), aFn(110), bFn(110), tree.DBoolTrue}, {tree.NewDInt(tree.DInt(34)), aFn(120), bFn(120), tree.DBoolTrue}, }, - lookupCols: []uint32{1, 2}, - joinType: descpb.LeftAntiJoin, - inputTypes: threeIntColsAndBoolCol, - outputTypes: rowenc.ThreeIntCols, - leftJoinPaired: true, - expected: "[[23 NULL NULL] [34 12 0]]", + lookupCols: []uint32{1, 2}, + joinType: descpb.LeftAntiJoin, + inputTypes: threeIntColsAndBoolCol, + outputTypes: rowenc.ThreeIntCols, + secondJoinInPairedJoin: true, + expected: "[[23 NULL NULL] [34 12 0]]", }, { // Group will span batches when we SetBatchSizeBytes to ~2 rows below. - description: "Test paired join with outer join with group spanning batches", + description: "Test second join in paired-joins with outer join with group spanning batches", post: execinfrapb.PostProcessSpec{ Projection: true, OutputColumns: []uint32{0, 1, 2, 4, 5, 6, 7}, @@ -491,16 +570,16 @@ func TestJoinReader(t *testing.T) { {tree.NewDInt(tree.DInt(34)), aFn(5), bFn(5), tree.DBoolFalse}, {tree.NewDInt(tree.DInt(43)), aFn(105), bFn(105), tree.DBoolFalse}, }, - lookupCols: []uint32{1, 2}, - joinType: descpb.LeftOuterJoin, - inputTypes: threeIntColsAndBoolCol, - outputTypes: sixIntColsAndStringCol, - leftJoinPaired: true, - expected: "[[12 0 2 0 2 2 'two'] [34 0 5 0 5 5 'five'] [43 10 5 NULL NULL NULL NULL]]", + lookupCols: []uint32{1, 2}, + joinType: descpb.LeftOuterJoin, + inputTypes: threeIntColsAndBoolCol, + outputTypes: sixIntColsAndStringCol, + secondJoinInPairedJoin: true, + expected: "[[12 0 2 0 2 2 'two'] [34 0 5 0 5 5 'five'] [43 10 5 NULL NULL NULL NULL]]", }, { // Group will span batches when we SetBatchSizeBytes to ~2 rows below. - description: "Test paired join with semi join with group spanning batches", + description: "Test second join in paired-joins with semi join with group spanning batches", post: execinfrapb.PostProcessSpec{ Projection: true, OutputColumns: []uint32{0, 1, 2}, @@ -520,16 +599,16 @@ func TestJoinReader(t *testing.T) { {tree.NewDInt(tree.DInt(34)), aFn(5), bFn(5), tree.DBoolFalse}, {tree.NewDInt(tree.DInt(43)), aFn(105), bFn(105), tree.DBoolFalse}, }, - lookupCols: []uint32{1, 2}, - joinType: descpb.LeftSemiJoin, - inputTypes: threeIntColsAndBoolCol, - outputTypes: rowenc.ThreeIntCols, - leftJoinPaired: true, - expected: "[[12 0 2] [34 0 5]]", + lookupCols: []uint32{1, 2}, + joinType: descpb.LeftSemiJoin, + inputTypes: threeIntColsAndBoolCol, + outputTypes: rowenc.ThreeIntCols, + secondJoinInPairedJoin: true, + expected: "[[12 0 2] [34 0 5]]", }, { // Group will span batches since we SetBatchSizeBytes to ~2 rows below. - description: "Test paired join with anti join with group spanning batches", + description: "Test second join in paired-joins with anti join with group spanning batches", post: execinfrapb.PostProcessSpec{ Projection: true, OutputColumns: []uint32{0, 1, 2}, @@ -549,12 +628,12 @@ func TestJoinReader(t *testing.T) { {tree.NewDInt(tree.DInt(34)), aFn(5), bFn(5), tree.DBoolFalse}, {tree.NewDInt(tree.DInt(43)), aFn(105), bFn(105), tree.DBoolFalse}, }, - lookupCols: []uint32{1, 2}, - joinType: descpb.LeftAntiJoin, - inputTypes: threeIntColsAndBoolCol, - outputTypes: rowenc.ThreeIntCols, - leftJoinPaired: true, - expected: "[[43 10 5]]", + lookupCols: []uint32{1, 2}, + joinType: descpb.LeftAntiJoin, + inputTypes: threeIntColsAndBoolCol, + outputTypes: rowenc.ThreeIntCols, + secondJoinInPairedJoin: true, + expected: "[[43 10 5]]", }, } st := cluster.MakeTestingClusterSettings() @@ -580,101 +659,131 @@ func TestJoinReader(t *testing.T) { // Small and large batches exercise different paths of interest for // paired joins, so do both. for _, smallBatch := range []bool{true, false} { - t.Run(fmt.Sprintf("%d/reqOrdering=%t/%s/smallBatch=%t", i, reqOrdering, c.description, smallBatch), func(t *testing.T) { - evalCtx := tree.MakeTestingEvalContext(st) - defer evalCtx.Stop(ctx) - flowCtx := execinfra.FlowCtx{ - EvalCtx: &evalCtx, - Cfg: &execinfra.ServerConfig{ - Settings: st, - TempStorage: tempEngine, - DiskMonitor: diskMonitor, - }, - Txn: kv.NewTxn(ctx, s.DB(), s.NodeID()), + for _, outputContinuation := range []bool{false, true} { + if outputContinuation && c.secondJoinInPairedJoin { + // outputContinuation is for the first join in paired-joins, so + // can't do that when this test case is for the second join in + // paired-joins. + continue } - encRows := make(rowenc.EncDatumRows, len(c.input)) - for rowIdx, row := range c.input { - encRow := make(rowenc.EncDatumRow, len(row)) - for i, d := range row { - encRow[i] = rowenc.DatumToEncDatum(c.inputTypes[i], d) - } - encRows[rowIdx] = encRow + if outputContinuation && !reqOrdering { + // The first join in paired-joins must preserve ordering. + continue } - in := distsqlutils.NewRowBuffer(c.inputTypes, encRows, distsqlutils.RowBufferArgs{}) - - out := &distsqlutils.RowBuffer{} - jr, err := newJoinReader( - &flowCtx, - 0, /* processorID */ - &execinfrapb.JoinReaderSpec{ - Table: *td.TableDesc(), - IndexIdx: c.indexIdx, - LookupColumns: c.lookupCols, - OnExpr: execinfrapb.Expression{Expr: c.onExpr}, - Type: c.joinType, - MaintainOrdering: reqOrdering, - LeftJoinWithPairedJoiner: c.leftJoinPaired, - }, - in, - &c.post, - out, - lookupJoinReaderType, - ) - if err != nil { - t.Fatal(err) + if outputContinuation && len(c.expectedWithContinuation) == 0 { + continue } + t.Run(fmt.Sprintf("%d/reqOrdering=%t/%s/smallBatch=%t/cont=%t", + i, reqOrdering, c.description, smallBatch, outputContinuation), func(t *testing.T) { + evalCtx := tree.MakeTestingEvalContext(st) + defer evalCtx.Stop(ctx) + flowCtx := execinfra.FlowCtx{ + EvalCtx: &evalCtx, + Cfg: &execinfra.ServerConfig{ + Settings: st, + TempStorage: tempEngine, + DiskMonitor: diskMonitor, + }, + Txn: kv.NewTxn(ctx, s.DB(), s.NodeID()), + } + encRows := make(rowenc.EncDatumRows, len(c.input)) + for rowIdx, row := range c.input { + encRow := make(rowenc.EncDatumRow, len(row)) + for i, d := range row { + encRow[i] = rowenc.DatumToEncDatum(c.inputTypes[i], d) + } + encRows[rowIdx] = encRow + } + in := distsqlutils.NewRowBuffer(c.inputTypes, encRows, distsqlutils.RowBufferArgs{}) - if smallBatch { - // Set a lower batch size to force multiple batches. - jr.(*joinReader).SetBatchSizeBytes(int64(encRows[0].Size() * 2)) - } - // Else, use the default. + out := &distsqlutils.RowBuffer{} + post := c.post + if outputContinuation { + post.OutputColumns = append(post.OutputColumns, c.outputColumnForContinuation) + } + jr, err := newJoinReader( + &flowCtx, + 0, /* processorID */ + &execinfrapb.JoinReaderSpec{ + Table: *td.TableDesc(), + IndexIdx: c.indexIdx, + LookupColumns: c.lookupCols, + OnExpr: execinfrapb.Expression{Expr: c.onExpr}, + Type: c.joinType, + MaintainOrdering: reqOrdering, + LeftJoinWithPairedJoiner: c.secondJoinInPairedJoin, + OutputGroupContinuationForLeftRow: outputContinuation, + }, + in, + &post, + out, + lookupJoinReaderType, + ) + if err != nil { + t.Fatal(err) + } - jr.Run(ctx) + if smallBatch { + // Set a lower batch size to force multiple batches. + jr.(*joinReader).SetBatchSizeBytes(int64(encRows[0].Size() * 2)) + } + // Else, use the default. - if !in.Done { - t.Fatal("joinReader didn't consume all the rows") - } - if !out.ProducerClosed() { - t.Fatalf("output RowReceiver not closed") - } + jr.Run(ctx) - var res rowenc.EncDatumRows - for { - row, meta := out.Next() - if meta != nil && meta.Metrics == nil { - t.Fatalf("unexpected metadata %+v", meta) + if !in.Done { + t.Fatal("joinReader didn't consume all the rows") } - if row == nil { - break + if !out.ProducerClosed() { + t.Fatalf("output RowReceiver not closed") } - res = append(res, row) - } - // processOutputRows is a helper function that takes a stringified - // EncDatumRows output (e.g. [[1 2] [3 1]]) and returns a slice of - // stringified rows without brackets (e.g. []string{"1 2", "3 1"}). - processOutputRows := func(output string) []string { - // Comma-separate the rows. - output = strings.ReplaceAll(output, "] [", ",") - // Remove leading and trailing bracket. - output = strings.Trim(output, "[]") - // Split on the commas that were introduced and return that. - return strings.Split(output, ",") - } + var res rowenc.EncDatumRows + for { + row, meta := out.Next() + if meta != nil && meta.Metrics == nil { + t.Fatalf("unexpected metadata %+v", meta) + } + if row == nil { + break + } + res = append(res, row) + } - result := processOutputRows(res.String(c.outputTypes)) - expected := processOutputRows(c.expected) + // processOutputRows is a helper function that takes a stringified + // EncDatumRows output (e.g. [[1 2] [3 1]]) and returns a slice of + // stringified rows without brackets (e.g. []string{"1 2", "3 1"}). + processOutputRows := func(output string) []string { + // Comma-separate the rows. + output = strings.ReplaceAll(output, "] [", ",") + // Remove leading and trailing bracket. + output = strings.Trim(output, "[]") + // Split on the commas that were introduced and return that. + return strings.Split(output, ",") + } - if !reqOrdering { - // An ordering was not required, so sort both the result and - // expected slice to reuse equality comparison. - sort.Strings(result) - sort.Strings(expected) - } + outputTypes := c.outputTypes + if outputContinuation { + outputTypes = append(outputTypes, types.Bool) + } + result := processOutputRows(res.String(outputTypes)) + var expected []string + if outputContinuation { + expected = processOutputRows(c.expectedWithContinuation) + } else { + expected = processOutputRows(c.expected) + } - require.Equal(t, expected, result) - }) + if !reqOrdering { + // An ordering was not required, so sort both the result and + // expected slice to reuse equality comparison. + sort.Strings(result) + sort.Strings(expected) + } + + require.Equal(t, expected, result) + }) + } } } } diff --git a/pkg/sql/rowexec/mergejoiner.go b/pkg/sql/rowexec/mergejoiner.go index 57bf911fcb55..b615df6b4a0e 100644 --- a/pkg/sql/rowexec/mergejoiner.go +++ b/pkg/sql/rowexec/mergejoiner.go @@ -82,7 +82,8 @@ func newMergeJoiner( if err := m.joinerBase.init( m /* self */, flowCtx, processorID, leftSource.OutputTypes(), rightSource.OutputTypes(), - spec.Type, spec.OnExpr, leftEqCols, rightEqCols, post, output, + spec.Type, spec.OnExpr, leftEqCols, rightEqCols, false, /* outputContinuationColumn */ + post, output, execinfra.ProcStateOpts{ InputsToDrain: []execinfra.RowSource{leftSource, rightSource}, TrailingMetaCallback: func(context.Context) []execinfrapb.ProducerMetadata { diff --git a/pkg/sql/rowexec/zigzagjoiner.go b/pkg/sql/rowexec/zigzagjoiner.go index 70c650cd0bce..a8e1da6990af 100644 --- a/pkg/sql/rowexec/zigzagjoiner.go +++ b/pkg/sql/rowexec/zigzagjoiner.go @@ -300,6 +300,7 @@ func newZigzagJoiner( spec.OnExpr, leftEqCols, rightEqCols, + false, /* outputContinuationColumn */ post, output, execinfra.ProcStateOpts{