| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258 |
- // 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 (
- "bytes"
- "fmt"
- "sort"
- )
- // IQuery is an interface that reprsents a SQL query, e.g.
- // SELECT ... FROM ... WHERE ...
- type IQuery interface {
- // String returns the queryString
- String(fields ...IQueryField) string
- // QueryFields returns fields in the select clause
- QueryFields() []IQueryField
- // Variables returns variables in statement
- Variables() []interface{}
- // SubQuery convert this SQL to a subquery
- SubQuery() *SSubQuery
- // Field reference to a field by name
- Field(name string) IQueryField
- // Database returns the database for this query
- database() *SDatabase
- }
- // IQuerySource is an interface that represents a data source of a SQL query. the source can be a table or a subquery
- // e.g. SELECT ... FROM (SELECT * FROM tbl) AS A
- type IQuerySource interface {
- // Expression string in select ... from (expresson here)
- Expression() string
- // Alias is the alias in select ... from (express) as alias
- Alias() string
- // variables in statement
- Variables() []interface{}
- // Field reference to a field by name, optionally giving an alias name
- Field(id string, alias ...string) IQueryField
- // Fields return all the fields that this source provides
- Fields() []IQueryField
- // Database returns the database of this IQuerySource
- database() *SDatabase
- }
- // IQueryField is an interface that represents a select field in a SQL query
- type IQueryField interface {
- // the string after select
- Expression() string
- // the name of thie field
- Name() string
- // the reference string in where clause
- Reference() string
- // give this field an alias name
- Label(label string) IQueryField
- // return variables
- Variables() []interface{}
- // ConvertFromValue returns the SQL representation of a value for this
- ConvertFromValue(val interface{}) interface{}
- // Database returns the database of this IQuerySource
- database() *SDatabase
- }
- // DoQuery returns a SQuery instance that query specified fields from a query source
- func DoQuery(from IQuerySource, f ...IQueryField) *SQuery {
- if from.database() == nil {
- panic("DoQuery IQuerySource with empty database")
- }
- // if len(f) == 0 {
- // f = from.Fields()
- // }
- tq := SQuery{fields: f, from: from, db: from.database()}
- return &tq
- }
- func queryString(tq *SQuery, tmpFields ...IQueryField) string {
- if len(tq.rawSql) > 0 {
- return tq.rawSql
- }
- qChar := tq.database().backend.QuoteChar()
- var buf bytes.Buffer
- buf.WriteString("SELECT ")
- if tq.distinct {
- buf.WriteString("DISTINCT ")
- }
- fields := tmpFields
- if len(fields) == 0 {
- if len(tq.fields) > 0 {
- fields = append(fields, tq.fields...)
- } else {
- fields = append(fields, tq.from.Fields()...)
- for i := range fields {
- tq.from.Field(fields[i].Name())
- }
- }
- }
- {
- // add reference query fields
- queryFields := make(map[string]IQueryField)
- for i, f := range fields {
- queryFields[f.Name()] = fields[i]
- }
- refFields := make([]IQueryField, 0)
- for _, f := range tq.refFieldMap {
- if _, ok := queryFields[f.Name()]; !ok {
- queryFields[f.Name()] = f
- refFields = append(refFields, f)
- }
- }
- sort.Sort(queryFieldList(refFields))
- fields = append(fields, refFields...)
- }
- groupFields := make(map[string]IQueryField)
- if len(tq.groupBy) > 0 {
- for i := range tq.groupBy {
- f := tq.groupBy[i]
- groupFields[f.Name()] = f
- }
- }
- for i := range fields {
- if i > 0 {
- buf.WriteString(", ")
- }
- f := fields[i]
- if len(tq.groupBy) > 0 {
- if gf, ok := groupFields[f.Name()]; ok && f.Reference() == gf.Reference() {
- // in group by, normal
- buf.WriteString(f.Expression())
- } else {
- // not in group by, check if the field is a group aggregate function
- if gf, ok := f.(IFunctionQueryField); ok && gf.IsAggregate() {
- // is a aggregate function field
- buf.WriteString(f.Expression())
- } else {
- f = MAX(f.Name(), f)
- buf.WriteString(f.Expression())
- }
- }
- } else {
- // normal
- buf.WriteString(f.Expression())
- }
- if f.Name() != "" {
- buf.WriteString(fmt.Sprintf(" AS %s%s%s", qChar, f.Name(), qChar))
- }
- }
- buf.WriteString(" FROM ")
- buf.WriteString(fmt.Sprintf("%s AS %s%s%s", tq.from.Expression(), qChar, tq.from.Alias(), qChar))
- for _, join := range tq.joins {
- buf.WriteByte(' ')
- buf.WriteString(string(join.jointype))
- buf.WriteByte(' ')
- buf.WriteString(fmt.Sprintf("%s AS %s%s%s", join.from.Expression(), qChar, join.from.Alias(), qChar))
- whereCls := join.condition.WhereClause()
- if len(whereCls) > 0 {
- buf.WriteString(" ON ")
- buf.WriteString(whereCls)
- }
- }
- if tq.where != nil {
- whereCls := tq.where.WhereClause()
- if len(whereCls) > 0 {
- buf.WriteString(" WHERE ")
- buf.WriteString(whereCls)
- }
- }
- if tq.groupBy != nil && len(tq.groupBy) > 0 {
- buf.WriteString(" GROUP BY ")
- groupByFields := make(map[string]IQueryField)
- for i := range tq.groupBy {
- f := tq.groupBy[i]
- if _, ok := groupByFields[f.Reference()]; ok {
- continue
- }
- if i > 0 {
- buf.WriteString(", ")
- }
- buf.WriteString(f.Reference())
- groupByFields[f.Reference()] = f
- }
- // DAMENG SQL Compatibility, all order by fields should be in group by
- for i := range tq.orderBy {
- f := tq.orderBy[i]
- if _, ok := groupByFields[f.field.Reference()]; ok {
- continue
- }
- if ff, ok := f.field.(IFunctionQueryField); ok && ff.IsAggregate() {
- continue
- }
- buf.WriteString(", ")
- buf.WriteString(f.field.Reference())
- groupByFields[f.field.Reference()] = f.field
- }
- }
- /*if tq.having != nil {
- buf.WriteString(" HAVING ")
- buf.WriteString(tq.having.WhereClause())
- }*/
- if tq.orderBy != nil && len(tq.orderBy) > 0 {
- buf.WriteString(" ORDER BY ")
- for i := range tq.orderBy {
- f := tq.orderBy[i]
- if i > 0 {
- buf.WriteString(", ")
- }
- buf.WriteString(fmt.Sprintf("%s %s", f.field.Reference(), f.order))
- }
- }
- if tq.limit > 0 {
- buf.WriteString(fmt.Sprintf(" LIMIT %d", tq.limit))
- }
- if tq.offset > 0 {
- buf.WriteString(fmt.Sprintf(" OFFSET %d", tq.offset))
- }
- return buf.String()
- }
- func getFieldBackend(fields ...IQueryField) IBackend {
- for _, f := range fields {
- db := f.database()
- if db != nil {
- return db.backend
- }
- }
- return defaultBackend
- }
|