func (b *planBuilder) buildUnion(union *ast.UnionStmt) Plan { sels := make([]Plan, len(union.SelectList.Selects)) for i, sel := range union.SelectList.Selects { sels[i] = b.buildSelect(sel) } var p Plan p = &Union{ Selects: sels, } unionFields := union.GetResultFields() for _, sel := range sels { for i, f := range sel.Fields() { if i == len(unionFields) { b.err = errors.New("The used SELECT statements have a different number of columns") return nil } uField := unionFields[i] /* * The lengths of the columns in the UNION result take into account the values retrieved by all of the SELECT statements * SELECT REPEAT('a',1) UNION SELECT REPEAT('b',10); * +---------------+ * | REPEAT('a',1) | * +---------------+ * | a | * | bbbbbbbbbb | * +---------------+ */ if f.Column.Flen > uField.Column.Flen { uField.Column.Flen = f.Column.Flen } // For select nul union select "abc", we should not convert "abc" to nil. // And the result field type should be VARCHAR. if uField.Column.Tp == 0 || uField.Column.Tp == mysql.TypeNull { uField.Column.Tp = f.Column.Tp } } addChild(p, sel) } for _, v := range unionFields { v.Expr.SetType(&v.Column.FieldType) } p.SetFields(unionFields) if union.Distinct { p = b.buildDistinct(p) } if union.OrderBy != nil { p = b.buildSort(p, union.OrderBy.Items) } if union.Limit != nil { p = b.buildLimit(p, union.Limit) } return p }
func convertUnion(converter *expressionConverter, u *ast.UnionStmt) (*stmts.UnionStmt, error) { oldUnion := &stmts.UnionStmt{ Text: u.Text(), } oldUnion.Selects = make([]*stmts.SelectStmt, len(u.Selects)) oldUnion.Distincts = make([]bool, len(u.Selects)-1) if u.Distinct { for i := range oldUnion.Distincts { oldUnion.Distincts[i] = true } } for i, val := range u.Selects { oldSelect, err := convertSelect(converter, val) if err != nil { return nil, errors.Trace(err) } oldUnion.Selects[i] = oldSelect } if u.OrderBy != nil { oldOrderBy, err := convertOrderBy(converter, u.OrderBy) if err != nil { return nil, errors.Trace(err) } oldUnion.OrderBy = oldOrderBy } if u.Limit != nil { if u.Limit.Offset > 0 { oldUnion.Offset = &rsets.OffsetRset{Count: u.Limit.Offset} } if u.Limit.Count > 0 { oldUnion.Limit = &rsets.LimitRset{Count: u.Limit.Count} } } // Union order by can not converted to old because it is pushed to select statements. return oldUnion, nil }