github.com/dolthub/go-mysql-server@v0.18.0/sql/memo/rel_props_test.go (about) 1 package memo 2 3 import ( 4 "fmt" 5 "testing" 6 7 "github.com/stretchr/testify/require" 8 9 "github.com/dolthub/go-mysql-server/sql" 10 "github.com/dolthub/go-mysql-server/sql/expression" 11 "github.com/dolthub/go-mysql-server/sql/plan" 12 "github.com/dolthub/go-mysql-server/sql/types" 13 ) 14 15 func TestPopulateFDs(t *testing.T) { 16 // source relations 17 tests := []struct { 18 name string 19 in RelExpr 20 all sql.ColSet 21 notNull sql.ColSet 22 indexes []*Index 23 key sql.ColSet 24 }{ 25 { 26 name: "tablescan", 27 in: &TableScan{ 28 sourceBase: &sourceBase{relBase: &relBase{}}, 29 Table: plan.NewResolvedTable( 30 &dummyTable{ 31 schema: sql.NewPrimaryKeySchema(sql.Schema{ 32 {Name: "x", Source: "t", Type: types.Int64, Nullable: false}, 33 {Name: "y", Source: "t", Type: types.Int64, Nullable: false}, 34 {Name: "z", Source: "t", Type: types.Int64, Nullable: false}, 35 }, 1, 0), 36 }, nil, nil).WithId(1).WithColumns(sql.NewColSet(1, 2, 3)), 37 }, 38 all: sql.NewColSet(1, 2, 3), 39 notNull: sql.NewColSet(1, 2, 3), 40 indexes: []*Index{ 41 { 42 order: []sql.ColumnId{2, 1}, 43 set: sql.NewColSet(1, 2), 44 }, 45 }, 46 key: sql.NewColSet(1, 2), 47 }, 48 { 49 name: "table alias", 50 in: &TableAlias{ 51 sourceBase: &sourceBase{relBase: &relBase{}}, 52 Table: plan.NewTableAlias("tab", plan.NewResolvedTable( 53 &dummyTable{ 54 schema: sql.NewPrimaryKeySchema(sql.Schema{ 55 {Name: "x", Source: "t", Type: types.Int64, Nullable: false}, 56 {Name: "y", Source: "t", Type: types.Int64, Nullable: false}, 57 {Name: "z", Source: "t", Type: types.Int64, Nullable: false}, 58 }, 0, 1, 2), 59 }, nil, nil).WithId(1).WithColumns(sql.NewColSet(1, 2, 3))), 60 }, 61 all: sql.NewColSet(1, 2, 3), 62 notNull: sql.NewColSet(1, 2, 3), 63 indexes: []*Index{ 64 { 65 order: []sql.ColumnId{1, 2, 3}, 66 set: sql.NewColSet(1, 2, 3), 67 }, 68 }, 69 key: sql.NewColSet(1, 2, 3), 70 }, 71 { 72 name: "empty table", 73 in: &EmptyTable{ 74 sourceBase: &sourceBase{relBase: &relBase{}}, 75 Table: plan.NewEmptyTableWithSchema( 76 sql.Schema{ 77 {Name: "x", Source: "t", Type: types.Int64, Nullable: false}, 78 {Name: "y", Source: "t", Type: types.Int64, Nullable: false}, 79 {Name: "z", Source: "t", Type: types.Int64, Nullable: false}, 80 }).(*plan.EmptyTable).WithId(1).WithColumns(sql.NewColSet(1, 2, 3)).(*plan.EmptyTable), 81 }, 82 // planning ignores empty tables for now 83 all: sql.NewColSet(), 84 notNull: sql.NewColSet(), 85 }, 86 { 87 name: "max1Row", 88 in: &Max1Row{ 89 relBase: &relBase{}, 90 Child: newExprGroup(NewMemo(nil, nil, nil, 0, nil), 0, &TableScan{ 91 sourceBase: &sourceBase{relBase: &relBase{}}, 92 Table: plan.NewResolvedTable( 93 &dummyTable{ 94 schema: sql.NewPrimaryKeySchema(sql.Schema{ 95 {Name: "x", Source: "t", Type: types.Int64, Nullable: false}, 96 {Name: "y", Source: "t", Type: types.Int64, Nullable: false}, 97 {Name: "z", Source: "t", Type: types.Int64, Nullable: false}, 98 }), 99 }, nil, nil).WithId(1).WithColumns(sql.NewColSet(1, 2, 3)), 100 }, 101 ), 102 }, 103 all: sql.NewColSet(1, 2, 3), 104 notNull: sql.NewColSet(1, 2, 3), 105 key: sql.ColSet{}, 106 }, 107 { 108 name: "values", 109 in: &Values{ 110 sourceBase: &sourceBase{relBase: &relBase{}}, 111 Table: plan.NewValueDerivedTable(plan.NewValues([][]sql.Expression{{expression.NewLiteral(1, types.Int64)}}), "values").WithId(1).WithColumns(sql.NewColSet(1)).(*plan.ValueDerivedTable), 112 }, 113 all: sql.NewColSet(1), 114 notNull: sql.NewColSet(1), 115 }, 116 } 117 118 for _, tt := range tests { 119 t.Run(tt.name, func(t *testing.T) { 120 tt.in.SetGroup(&ExprGroup{First: tt.in, m: NewMemo(nil, nil, nil, 0, nil)}) 121 props := newRelProps(tt.in) 122 require.Equal(t, tt.all, props.fds.All()) 123 require.Equal(t, tt.notNull, props.fds.NotNull()) 124 125 cmp, ok := props.fds.StrictKey() 126 if !tt.key.Empty() { 127 require.True(t, ok) 128 require.Equal(t, tt.key, cmp) 129 } else if ok { 130 require.True(t, props.fds.HasMax1Row()) 131 } 132 133 if src, ok := tt.in.(SourceRel); ok { 134 cmpIdx := src.Indexes() 135 require.Equal(t, len(tt.indexes), len(cmpIdx)) 136 for i, ii := range tt.indexes { 137 cmp := cmpIdx[i] 138 require.Equal(t, ii.Cols(), cmp.Cols()) 139 require.Equal(t, ii.ColSet(), cmp.ColSet()) 140 } 141 } 142 }) 143 } 144 } 145 146 type dummyTable struct { 147 schema sql.PrimaryKeySchema 148 } 149 150 var _ sql.Table = (*dummyTable)(nil) 151 var _ sql.PrimaryKeyTable = (*dummyTable)(nil) 152 var _ sql.IndexAddressable = (*dummyTable)(nil) 153 154 func (t *dummyTable) IndexedAccess(sql.IndexLookup) sql.IndexedTable { 155 panic("implement me") 156 } 157 158 func (t *dummyTable) PreciseMatch() bool { 159 return true 160 } 161 162 func (t *dummyTable) GetIndexes(*sql.Context) ([]sql.Index, error) { 163 var exprs []string 164 for _, i := range t.schema.PkOrdinals { 165 exprs = append(exprs, fmt.Sprintf("%s.%s", t.Name(), t.schema.Schema[i].Name)) 166 } 167 return []sql.Index{dummyIndex{cols: exprs}}, nil 168 } 169 170 func (t *dummyTable) PrimaryKeySchema() sql.PrimaryKeySchema { 171 return t.schema 172 } 173 174 func (t *dummyTable) Name() string { return "dummy" } 175 176 func (t *dummyTable) String() string { 177 return "name" 178 } 179 180 func (*dummyTable) Insert(*sql.Context, sql.Row) error { 181 panic("not implemented") 182 } 183 184 func (t *dummyTable) Schema() sql.Schema { return t.schema.Schema } 185 186 func (t *dummyTable) Collation() sql.CollationID { return sql.Collation_Default } 187 188 func (t *dummyTable) Partitions(ctx *sql.Context) (sql.PartitionIter, error) { 189 panic("not implemented") 190 } 191 192 func (t *dummyTable) PartitionRows(ctx *sql.Context, partition sql.Partition) (sql.RowIter, error) { 193 panic("not implemented") 194 } 195 196 type dummyIndex struct { 197 cols []string 198 } 199 200 func (dummyIndex) CanSupport(...sql.Range) bool { 201 return true 202 } 203 204 func (dummyIndex) ID() string { 205 return "test_index" 206 } 207 208 func (dummyIndex) Database() string { 209 return "database" 210 } 211 212 func (dummyIndex) Table() string { 213 return "table" 214 } 215 216 func (i dummyIndex) Expressions() []string { 217 return i.cols 218 } 219 220 func (dummyIndex) IsUnique() bool { 221 return true 222 } 223 224 func (dummyIndex) IsSpatial() bool { 225 return false 226 } 227 228 func (dummyIndex) IsFullText() bool { 229 return false 230 } 231 232 func (dummyIndex) Comment() string { 233 return "" 234 } 235 236 func (dummyIndex) IndexType() string { 237 return "FAKE" 238 } 239 240 func (dummyIndex) IsGenerated() bool { 241 return false 242 } 243 244 func (i dummyIndex) ColumnExpressionTypes() []sql.ColumnExpressionType { 245 es := i.Expressions() 246 res := make([]sql.ColumnExpressionType, len(es)) 247 for i := range es { 248 res[i] = sql.ColumnExpressionType{Expression: es[i], Type: types.Int8} 249 } 250 return res 251 } 252 253 func (dummyIndex) PrefixLengths() []uint16 { 254 return nil 255 } 256 257 var _ sql.Index = dummyIndex{}