diff --git a/builder.go b/builder.go index 0ec4d5e..4855094 100644 --- a/builder.go +++ b/builder.go @@ -7,23 +7,21 @@ // (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 +// 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 sqlxb -// // TO build sql, like: SELECT * FROM .... // Can add L2Cache // // @author Sim type Builder struct { - ConditionBuilder + CondBuilder pageBuilder *PageBuilder sorts []Sort @@ -55,10 +53,11 @@ func (builder *Builder) Sort(orderBy string, direction Direction) *Builder { return builder } -func (builder *Builder) Paged() *PageBuilder { +func (builder *Builder) Paged(page func(pb *PageBuilder)) *Builder { pageBuilder := new(PageBuilder) builder.pageBuilder = pageBuilder - return pageBuilder + page(pageBuilder) + return builder } func (builder *Builder) Build() *Built { diff --git a/builder_x.go b/builder_x.go index 93cf52d..a1d0036 100644 --- a/builder_x.go +++ b/builder_x.go @@ -7,7 +7,7 @@ // (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 +// 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, @@ -20,14 +20,14 @@ package sqlxb // Sql for MySQL, Clickhouse.... // // @author Sim -// -func Sub() *BuilderX { - return NewBuilderX(nil, "") +func Sub(po Po) *BuilderX { + return NewBuilderX(po, "") } type BuilderX struct { Builder resultKeys []string + orSourceSql string sbs []*SourceBuilder svs []interface{} havings []Bb @@ -58,6 +58,23 @@ func (x *BuilderX) SourceBuilder() *SourceBuilder { return &sb } +func Source() *SourceBuilder { + var sb = SourceBuilder{} + return &sb +} + +func (x *BuilderX) SourceX(source func(*SourceBuilder)) *BuilderX { + var b = Source() + x.sbs = append(x.sbs, b) + source(b) + return x +} + +func (x *BuilderX) SourceScript(sqlScript string) *BuilderX { + x.orSourceSql = sqlScript + return x +} + func (x *BuilderX) ResultKey(resultKey string) *BuilderX { if resultKey != "" { x.resultKeys = append(x.resultKeys, resultKey) @@ -124,14 +141,15 @@ func (builder *BuilderX) Build() *Built { } builder.optimizeSourceBuilder() built := Built{ - ResultKeys: builder.resultKeys, - ConditionX: builder.bbs, - Sorts: builder.sorts, - Aggs: builder.aggs, - Havings: builder.havings, - GroupBys: builder.groupBys, - Sbs: builder.sbs, - Svs: builder.svs, + ResultKeys: builder.resultKeys, + ConditionX: builder.bbs, + Sorts: builder.sorts, + Aggs: builder.aggs, + Havings: builder.havings, + GroupBys: builder.groupBys, + OrSourceSql: builder.orSourceSql, + Sbs: builder.sbs, + Svs: builder.svs, Po: builder.po, } diff --git a/condition_builder.go b/condition_builder.go index ebb50ec..55dc7fb 100644 --- a/condition_builder.go +++ b/condition_builder.go @@ -7,7 +7,7 @@ // (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 +// 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, @@ -18,19 +18,19 @@ package sqlxb import "time" -type ConditionBuilder struct { +type CondBuilder struct { bbs []Bb } type BoolFunc func() bool -func SubCondition() *ConditionBuilder { - c := new(ConditionBuilder) +func SubCond() *CondBuilder { + c := new(CondBuilder) c.bbs = []Bb{} return c } -func (builder *ConditionBuilder) X(k string, vs ...interface{}) *ConditionBuilder { +func (builder *CondBuilder) X(k string, vs ...interface{}) *CondBuilder { bb := Bb{ op: X, key: k, @@ -40,7 +40,7 @@ func (builder *ConditionBuilder) X(k string, vs ...interface{}) *ConditionBuilde return builder } -func (builder *ConditionBuilder) doIn(p string, k string, vs ...interface{}) *ConditionBuilder { +func (builder *CondBuilder) doIn(p string, k string, vs ...interface{}) *CondBuilder { if vs == nil || len(vs) == 0 { return builder } @@ -90,7 +90,7 @@ func (builder *ConditionBuilder) doIn(p string, k string, vs ...interface{}) *Co return builder } -func (builder *ConditionBuilder) doLike(p string, k string, v string) *ConditionBuilder { +func (builder *CondBuilder) doLike(p string, k string, v string) *CondBuilder { bb := Bb{ op: p, @@ -102,7 +102,7 @@ func (builder *ConditionBuilder) doLike(p string, k string, v string) *Condition return builder } -func (builder *ConditionBuilder) doGLE(p string, k string, v interface{}) *ConditionBuilder { +func (builder *CondBuilder) doGLE(p string, k string, v interface{}) *CondBuilder { switch v.(type) { case string: @@ -132,7 +132,7 @@ func (builder *ConditionBuilder) doGLE(p string, k string, v interface{}) *Condi return builder.addBb(p, k, v) } -func (builder *ConditionBuilder) addBb(op string, key string, v interface{}) *ConditionBuilder { +func (builder *CondBuilder) addBb(op string, key string, v interface{}) *CondBuilder { bb := Bb{ op: op, key: key, @@ -143,7 +143,7 @@ func (builder *ConditionBuilder) addBb(op string, key string, v interface{}) *Co return builder } -func (builder *ConditionBuilder) null(op string, k string) *ConditionBuilder { +func (builder *CondBuilder) null(op string, k string) *CondBuilder { bb := Bb{ op: op, key: k, @@ -152,7 +152,7 @@ func (builder *ConditionBuilder) null(op string, k string) *ConditionBuilder { return builder } -func (builder *ConditionBuilder) orAndSub(orAnd string, sub *ConditionBuilder) *ConditionBuilder { +func (builder *CondBuilder) orAndSub(orAnd string, sub *CondBuilder) *CondBuilder { if sub.bbs == nil || len(sub.bbs) == 0 { return builder } @@ -166,7 +166,7 @@ func (builder *ConditionBuilder) orAndSub(orAnd string, sub *ConditionBuilder) * return builder } -func (builder *ConditionBuilder) orAnd(orAnd string) *ConditionBuilder { +func (builder *CondBuilder) orAnd(orAnd string) *CondBuilder { length := len(builder.bbs) if length == 0 { return builder @@ -182,77 +182,77 @@ func (builder *ConditionBuilder) orAnd(orAnd string) *ConditionBuilder { return builder } -func (builder *ConditionBuilder) And(subCondition *ConditionBuilder) *ConditionBuilder { +func (builder *CondBuilder) And(subCondition *CondBuilder) *CondBuilder { return builder.orAndSub(AND_SUB, subCondition) } -func (builder *ConditionBuilder) Or(sub *ConditionBuilder) *ConditionBuilder { +func (builder *CondBuilder) Or(sub *CondBuilder) *CondBuilder { return builder.orAndSub(OR_SUB, sub) } -func (builder *ConditionBuilder) OR() *ConditionBuilder { +func (builder *CondBuilder) OR() *CondBuilder { return builder.orAnd(OR) } -func (builder *ConditionBuilder) Bool(preCondition BoolFunc, then func(cb *ConditionBuilder)) *ConditionBuilder { +func (builder *CondBuilder) Bool(preCondition BoolFunc, then func(cb *CondBuilder)) *CondBuilder { if preCondition == nil { - panic("ConditionBuilder.Bool para of BoolFunc can not nil") + panic("CondBuilder.Bool para of BoolFunc can not nil") } if !preCondition() { return builder } if then == nil { - panic("ConditionBuilder.Bool para of func(k string, vs... interface{}) can not nil") + panic("CondBuilder.Bool para of func(k string, vs... interface{}) can not nil") } then(builder) return builder } -func (builder *ConditionBuilder) Eq(k string, v interface{}) *ConditionBuilder { +func (builder *CondBuilder) Eq(k string, v interface{}) *CondBuilder { return builder.doGLE(EQ, k, v) } -func (builder *ConditionBuilder) Ne(k string, v interface{}) *ConditionBuilder { +func (builder *CondBuilder) Ne(k string, v interface{}) *CondBuilder { return builder.doGLE(NE, k, v) } -func (builder *ConditionBuilder) Gt(k string, v interface{}) *ConditionBuilder { +func (builder *CondBuilder) Gt(k string, v interface{}) *CondBuilder { return builder.doGLE(GT, k, v) } -func (builder *ConditionBuilder) Lt(k string, v interface{}) *ConditionBuilder { +func (builder *CondBuilder) Lt(k string, v interface{}) *CondBuilder { return builder.doGLE(LT, k, v) } -func (builder *ConditionBuilder) Gte(k string, v interface{}) *ConditionBuilder { +func (builder *CondBuilder) Gte(k string, v interface{}) *CondBuilder { return builder.doGLE(GTE, k, v) } -func (builder *ConditionBuilder) Lte(k string, v interface{}) *ConditionBuilder { +func (builder *CondBuilder) Lte(k string, v interface{}) *CondBuilder { return builder.doGLE(LTE, k, v) } -func (builder *ConditionBuilder) Like(k string, v string) *ConditionBuilder { +func (builder *CondBuilder) Like(k string, v string) *CondBuilder { if v == "" { return builder } return builder.doLike(LIKE, k, "%"+v+"%") } -func (builder *ConditionBuilder) NotLike(k string, v string) *ConditionBuilder { +func (builder *CondBuilder) NotLike(k string, v string) *CondBuilder { if v == "" { return builder } return builder.doLike(NOT_LIKE, k, "%"+v+"%") } -func (builder *ConditionBuilder) LikeRight(k string, v string) *ConditionBuilder { +func (builder *CondBuilder) LikeRight(k string, v string) *CondBuilder { if v == "" { return builder } return builder.doLike(LIKE, k, v+"%") } -func (builder *ConditionBuilder) In(k string, vs ...interface{}) *ConditionBuilder { +func (builder *CondBuilder) In(k string, vs ...interface{}) *CondBuilder { return builder.doIn(IN, k, vs...) } -func (builder *ConditionBuilder) Nin(k string, vs ...interface{}) *ConditionBuilder { +func (builder *CondBuilder) Nin(k string, vs ...interface{}) *CondBuilder { return builder.doIn(NIN, k, vs...) } -func (builder *ConditionBuilder) IsNull(key string) *ConditionBuilder { +func (builder *CondBuilder) IsNull(key string) *CondBuilder { return builder.null(IS_NULL, key) } -func (builder *ConditionBuilder) NonNull(key string) *ConditionBuilder { +func (builder *CondBuilder) NonNull(key string) *CondBuilder { return builder.null(NON_NULL, key) } diff --git a/filter_last.go b/filter_last.go index 36d387e..7776220 100644 --- a/filter_last.go +++ b/filter_last.go @@ -7,7 +7,7 @@ // (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 +// 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, @@ -20,7 +20,11 @@ func (built *Built) filterLast() *Bb { if built.PageCondition == nil { return nil } + if built.PageCondition.rows == 0 { + panic("page.rows must be greater than 0") + } if built.PageCondition.last > 0 { + if built.Sorts == nil || len(built.Sorts) == 0 { panic("last > 0, Numeric sorts[0] required") } diff --git a/page_builder.go b/page_builder.go index 4d55a8a..992e4c3 100644 --- a/page_builder.go +++ b/page_builder.go @@ -7,7 +7,7 @@ // (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 +// 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, @@ -27,10 +27,6 @@ type PageBuilder struct { condition PageCondition } -func (pb *PageBuilder) Paged() *PageBuilder { - return new(PageBuilder) -} - func (pb *PageBuilder) Page(page uint) *PageBuilder { pb.condition.page = page return pb diff --git a/source_builder.go b/source_builder.go index c656188..2b6fb09 100644 --- a/source_builder.go +++ b/source_builder.go @@ -7,7 +7,7 @@ // (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 +// 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, @@ -21,7 +21,7 @@ type SourceBuilder struct { alia string join *Join sub *BuilderX - bbs []Bb + s string } func (sb *SourceBuilder) Source(po Po) *SourceBuilder { @@ -35,70 +35,54 @@ func (sb *SourceBuilder) Alia(alia string) *SourceBuilder { } type Join struct { - join string - on *On + join string + target string + alia string + on *On } type On struct { - key string - targetAlia string - targetKey string + CondBuilder + orUsingKey string } type Using struct { key string } -func ON(key string, targetOrAlia string, targetKey string) *On { - if key == "" || targetOrAlia == "" || targetKey == "" { - panic("On.key, On.targetOrAlia, On.targetKey can not blank") - } - return &On{ - key, - targetOrAlia, - targetKey, - } +func (join *Join) ON(k string) *On { + join.on = &On{} + join.on.X(k) + return join.on } -func USING(key string) *Using { +func (join *Join) USING(key string) { if key == "" { panic("Using.key can not blank") } - return &Using{ - key: key, - } + join.on = &On{} + join.on.orUsingKey = key } -func (sb *SourceBuilder) JoinOn(join JOIN, on *On) *SourceBuilder { - if join == nil || on == nil { - panic("join, on can not nil") - } - if sb.join != nil { - panic("call Join repeated") - } - sb.join = &Join{ - join: join(), - on: on, - } - return sb +func (join *Join) Alia(alia string) *Join { + join.alia = alia + return join } -func (sb *SourceBuilder) JoinUsing(join JOIN, using *Using) *SourceBuilder { - if join == nil || using == nil { - panic("join, using can not nil") +func (sb *SourceBuilder) Join(join JOIN, po Po) *Join { + if join == nil { + panic("join, on can not nil") } if sb.join != nil { panic("call Join repeated") } sb.join = &Join{ - join: join(), - on: &On{ - key: using.key, - }, + join: join(), + target: po.TableName(), } - return sb + return sb.join } -func (sb *SourceBuilder) More(cb *ConditionBuilder) { - sb.bbs = cb.bbs +func (sb *SourceBuilder) JoinScript(joinScript string) { + sb.s = joinScript } func (sb *SourceBuilder) Sub(sub *BuilderX) *SourceBuilder { diff --git a/source_builder_optimization.go b/source_builder_optimization.go index 8c0aa12..23831dc 100644 --- a/source_builder_optimization.go +++ b/source_builder_optimization.go @@ -7,7 +7,7 @@ // (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 +// 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, @@ -45,7 +45,7 @@ func (builder *BuilderX) optimizeSourceBuilder() { } } for _, v := range *builder.conds() { - if ele.sub == nil && strings.Contains(v, ele.po.TableName()+".") { //has return or condition + if strings.Contains(v, ele.po.TableName()+".") { //has return or condition return false } if strings.Contains(v, ele.alia+".") { ////has return or condition @@ -62,7 +62,7 @@ func (builder *BuilderX) conds() *[]string { condArr = append(condArr, v) } - bbps := builder.ConditionBuilder.bbs + bbps := builder.CondBuilder.bbs if bbps != nil { for _, v := range bbps { @@ -72,9 +72,11 @@ func (builder *BuilderX) conds() *[]string { if len(builder.sbs) > 0 { for _, sb := range builder.sbs { - if sb.bbs != nil { - for _, bb := range sb.bbs { - condArr = append(condArr, bb.key) + if sb.join != nil && sb.join.on != nil && sb.join.on.bbs != nil { + for i, bb := range sb.join.on.bbs { + if i > 0 { + condArr = append(condArr, bb.key) + } } } } diff --git a/to_source_script_by_builder.go b/to_source_script_by_builder.go index b85ce9b..b23ef65 100644 --- a/to_source_script_by_builder.go +++ b/to_source_script_by_builder.go @@ -21,6 +21,18 @@ import ( "strings" ) +func (built *Built) toSourceScriptBySql(bp * strings.Builder) bool{ + if (len(built.Sbs) == 1) && (built.OrSourceSql != "") { + var sql = strings.Trim(built.OrSourceSql, SPACE) + if strings.HasPrefix(sql, "FROM") { + sql = strings.Replace(sql, "FROM ","",1) + } + bp.WriteString(sql) + return true + } + return false +} + func (built *Built) toSourceScriptByBuilder(vs *[]interface{}, sb *SourceBuilder, bp *strings.Builder) { if sb.join != nil { //JOIN bp.WriteString(SPACE) @@ -41,23 +53,16 @@ func (built *Built) toSourceScriptByBuilder(vs *[]interface{}, sb *SourceBuilder } if sb.join != nil && sb.join.on != nil { //ON - if sb.join.on.targetKey == "" { + if sb.join.on.orUsingKey != "" { bp.WriteString(USING_SCRIPT_LEFT) - bp.WriteString(sb.join.on.key) + bp.WriteString(sb.join.on.orUsingKey) bp.WriteString(END_SUB) - } else { + }else if sb.s != "" { + bp.WriteString(SPACE) + bp.WriteString(sb.s) + }else { bp.WriteString(ON_SCRIPT) - if sb.alia != "" { - bp.WriteString(sb.alia) - } else { - bp.WriteString(sb.po.TableName()) - } - bp.WriteString(DOT) - bp.WriteString(sb.join.on.key) - bp.WriteString(EQ_SCRIPT) - bp.WriteString(sb.join.on.targetAlia) - bp.WriteString(DOT) - bp.WriteString(sb.join.on.targetKey) + built.toConditionScript(sb.join.on.bbs,bp,vs,nil) } } } diff --git a/to_sql.go b/to_sql.go index dc497b5..ebc132e 100644 --- a/to_sql.go +++ b/to_sql.go @@ -7,7 +7,7 @@ // (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 +// 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, @@ -30,8 +30,9 @@ type Built struct { GroupBys []string Aggs []Bb - Sbs []*SourceBuilder - Svs []interface{} + OrSourceSql string + Sbs []*SourceBuilder + Svs []interface{} PageCondition *PageCondition @@ -52,6 +53,11 @@ func (built *Built) toGroupBySqlOfCount(bpCount *strings.Builder) { func (built *Built) toSourceScript(vs *[]interface{}, bp *strings.Builder) { if built.Po == nil { + + if built.toSourceScriptBySql(bp) { + return + } + for _, sb := range built.Sbs { built.toSourceScriptByBuilder(vs, sb, bp) }