mirror of
https://hubproxy.babadafafafafa.cn/https://github.com/yunionio/cloudpods.git
synced 2026-09-20 08:03:53 +08:00
225 lines
5.6 KiB
Go
225 lines
5.6 KiB
Go
// Copyright 2019 Yunion
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
package sqlchemy
|
|
|
|
import (
|
|
"database/sql"
|
|
"fmt"
|
|
"strings"
|
|
|
|
"yunion.io/x/log"
|
|
"yunion.io/x/pkg/errors"
|
|
)
|
|
|
|
// SUnionQueryField represents a field of a union query
|
|
type SUnionQueryField struct {
|
|
union *SUnion
|
|
name string
|
|
alias string
|
|
}
|
|
|
|
// Expression implementation of SUnionQueryField for IQueryField
|
|
func (sqf *SUnionQueryField) Expression() string {
|
|
if len(sqf.alias) > 0 {
|
|
return fmt.Sprintf("`%s`.`%s` as `%s`", sqf.union.Alias(), sqf.name, sqf.alias)
|
|
}
|
|
return fmt.Sprintf("`%s`.`%s`", sqf.union.Alias(), sqf.name)
|
|
}
|
|
|
|
// Name implementation of SUnionQueryField for IQueryField
|
|
func (sqf *SUnionQueryField) Name() string {
|
|
if len(sqf.alias) > 0 {
|
|
return sqf.alias
|
|
}
|
|
return sqf.name
|
|
}
|
|
|
|
// Reference implementation of SUnionQueryField for IQueryField
|
|
func (sqf *SUnionQueryField) Reference() string {
|
|
return fmt.Sprintf("`%s`.`%s`", sqf.union.Alias(), sqf.Name())
|
|
}
|
|
|
|
// Label implementation of SUnionQueryField for IQueryField
|
|
func (sqf *SUnionQueryField) Label(label string) IQueryField {
|
|
if len(label) > 0 && label != sqf.name {
|
|
sqf.alias = label
|
|
}
|
|
return sqf
|
|
}
|
|
|
|
// Variables implementation of SUnionQueryField for IQueryField
|
|
func (sqf *SUnionQueryField) Variables() []interface{} {
|
|
return nil
|
|
}
|
|
|
|
// SUnion is the struct to store state of a Union query, which implementation the interface of IQuerySource
|
|
type SUnion struct {
|
|
alias string
|
|
queries []IQuery
|
|
fields []IQueryField
|
|
orderBy []sQueryOrder
|
|
limit int
|
|
offset int
|
|
}
|
|
|
|
// Alias implementation of SUnion for IQuerySource
|
|
func (uq *SUnion) Alias() string {
|
|
return uq.alias
|
|
}
|
|
|
|
// Expression implementation of SUnion for IQuerySource
|
|
func (uq *SUnion) Expression() string {
|
|
var buf strings.Builder
|
|
buf.WriteByte('(')
|
|
for i := range uq.queries {
|
|
if i != 0 {
|
|
buf.WriteString(" UNION ")
|
|
}
|
|
buf.WriteByte('(')
|
|
buf.WriteString(uq.queries[i].String())
|
|
buf.WriteByte(')')
|
|
}
|
|
if uq.orderBy != nil && len(uq.orderBy) > 0 {
|
|
buf.WriteString(" ORDER BY ")
|
|
for i, f := range uq.orderBy {
|
|
if i > 0 {
|
|
buf.WriteString(", ")
|
|
}
|
|
buf.WriteString(fmt.Sprintf("%s %s", f.field.Reference(), f.order))
|
|
}
|
|
}
|
|
if uq.limit > 0 {
|
|
buf.WriteString(fmt.Sprintf(" LIMIT %d", uq.limit))
|
|
}
|
|
if uq.offset > 0 {
|
|
buf.WriteString(fmt.Sprintf(" OFFSET %d", uq.offset))
|
|
}
|
|
buf.WriteByte(')')
|
|
return buf.String()
|
|
}
|
|
|
|
/*func (tq *SUnion) _orderBy(order QueryOrderType, fields []IQueryField) *SUnion {
|
|
if tq.orderBy == nil {
|
|
tq.orderBy = make([]SQueryOrder, 0)
|
|
}
|
|
for _, f := range fields {
|
|
tq.orderBy = append(tq.orderBy, SQueryOrder{field: f, order: order})
|
|
}
|
|
return tq
|
|
}
|
|
|
|
func (tq *SUnion) Asc(fields ...interface{}) *SUnion {
|
|
return tq._orderBy(SQL_ORDER_ASC, convertQueryField(tq, fields))
|
|
}
|
|
|
|
func (tq *SUnion) Desc(fields ...interface{}) *SUnion {
|
|
return tq._orderBy(SQL_ORDER_DESC, convertQueryField(tq, fields))
|
|
}
|
|
*/
|
|
|
|
// Limit adds limit to a union query
|
|
func (uq *SUnion) Limit(limit int) *SUnion {
|
|
uq.limit = limit
|
|
return uq
|
|
}
|
|
|
|
// Offset adds offset to a union query
|
|
func (uq *SUnion) Offset(offset int) *SUnion {
|
|
uq.offset = offset
|
|
return uq
|
|
}
|
|
|
|
// Fields implementation of SUnion for IQuerySource
|
|
func (uq *SUnion) Fields() []IQueryField {
|
|
return uq.fields
|
|
}
|
|
|
|
// Field implementation of SUnion for IQuerySource
|
|
func (uq *SUnion) Field(name string, alias ...string) IQueryField {
|
|
for i := range uq.fields {
|
|
if name == uq.fields[i].Name() {
|
|
if len(alias) > 0 {
|
|
uq.fields[i].Label(alias[0])
|
|
}
|
|
return uq.fields[i]
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// Variables implementation of SUnion for IQuerySource
|
|
func (uq *SUnion) Variables() []interface{} {
|
|
ret := make([]interface{}, 0)
|
|
for i := range uq.queries {
|
|
ret = append(ret, uq.queries[i].Variables()...)
|
|
}
|
|
return ret
|
|
}
|
|
|
|
// Union method returns union query of several queries.
|
|
// Require the fields of all queries should exactly match
|
|
// deprecated
|
|
func Union(query ...IQuery) *SUnion {
|
|
u, err := UnionWithError(query...)
|
|
if err != nil {
|
|
log.Fatalf("Fatal: %s", err.Error())
|
|
}
|
|
return u
|
|
}
|
|
|
|
// UnionWithError constructs union query of several Queries
|
|
// Require the fields of all queries should exactly match
|
|
func UnionWithError(query ...IQuery) (*SUnion, error) {
|
|
if len(query) == 0 {
|
|
return nil, errors.Wrap(sql.ErrNoRows, "empty union query")
|
|
}
|
|
|
|
fieldNames := make([]string, 0)
|
|
for _, f := range query[0].QueryFields() {
|
|
fieldNames = append(fieldNames, f.Name())
|
|
}
|
|
|
|
for i := 1; i < len(query); i++ {
|
|
qfields := query[i].QueryFields()
|
|
if len(fieldNames) != len(qfields) {
|
|
return nil, errors.Wrap(ErrUnionFieldsNotMatch, "number not match")
|
|
}
|
|
for i := range qfields {
|
|
if fieldNames[i] != qfields[i].Name() {
|
|
return nil, errors.Wrapf(ErrUnionFieldsNotMatch, "name %s:%s not match", fieldNames[i], qfields[i].Name())
|
|
}
|
|
}
|
|
}
|
|
|
|
fields := make([]IQueryField, len(fieldNames))
|
|
|
|
uq := &SUnion{
|
|
alias: getTableAliasName(),
|
|
queries: query,
|
|
fields: fields,
|
|
}
|
|
|
|
for i := range fieldNames {
|
|
fields[i] = &SUnionQueryField{name: fieldNames[i], union: uq}
|
|
}
|
|
|
|
return uq, nil
|
|
}
|
|
|
|
// Query of SUnion returns a SQuery of a union query
|
|
func (uq *SUnion) Query(f ...IQueryField) *SQuery {
|
|
return DoQuery(uq, f...)
|
|
}
|