mirror of
https://github.com/hanzoai/sqlbuilder.git
synced 2026-10-01 00:37:42 +00:00
Merge pull request #116 from huandu/feature-generate-sql-only-when-value-is-set
Fix #115: Generate SQL only when necessary values are set
This commit is contained in:
@@ -97,7 +97,7 @@ func (args *Args) Compile(format string, initialValue ...interface{}) (query str
|
||||
//
|
||||
// See doc for `Compile` to learn details.
|
||||
func (args *Args) CompileWithFlavor(format string, flavor Flavor, initialValue ...interface{}) (query string, values []interface{}) {
|
||||
buf := &strings.Builder{}
|
||||
buf := newStringBuilder()
|
||||
idx := strings.IndexRune(format, '$')
|
||||
offset := 0
|
||||
values = initialValue
|
||||
@@ -160,7 +160,7 @@ func (args *Args) CompileWithFlavor(format string, flavor Flavor, initialValue .
|
||||
return
|
||||
}
|
||||
|
||||
func (args *Args) compileNamed(buf *strings.Builder, flavor Flavor, format string, values []interface{}) (string, []interface{}) {
|
||||
func (args *Args) compileNamed(buf *stringBuilder, flavor Flavor, format string, values []interface{}) (string, []interface{}) {
|
||||
i := 1
|
||||
|
||||
for ; i < len(format) && format[i] != '}'; i++ {
|
||||
@@ -182,7 +182,7 @@ func (args *Args) compileNamed(buf *strings.Builder, flavor Flavor, format strin
|
||||
return format, values
|
||||
}
|
||||
|
||||
func (args *Args) compileDigits(buf *strings.Builder, flavor Flavor, format string, values []interface{}, offset int) (string, []interface{}, int) {
|
||||
func (args *Args) compileDigits(buf *stringBuilder, flavor Flavor, format string, values []interface{}, offset int) (string, []interface{}, int) {
|
||||
i := 1
|
||||
|
||||
for ; i < len(format) && '0' <= format[i] && format[i] <= '9'; i++ {
|
||||
@@ -199,7 +199,7 @@ func (args *Args) compileDigits(buf *strings.Builder, flavor Flavor, format stri
|
||||
return format, values, offset
|
||||
}
|
||||
|
||||
func (args *Args) compileSuccessive(buf *strings.Builder, flavor Flavor, format string, values []interface{}, offset int) (string, []interface{}, int) {
|
||||
func (args *Args) compileSuccessive(buf *stringBuilder, flavor Flavor, format string, values []interface{}, offset int) (string, []interface{}, int) {
|
||||
if offset >= len(args.args) {
|
||||
return format, values, offset
|
||||
}
|
||||
@@ -210,7 +210,7 @@ func (args *Args) compileSuccessive(buf *strings.Builder, flavor Flavor, format
|
||||
return format, values, offset + 1
|
||||
}
|
||||
|
||||
func (args *Args) compileArg(buf *strings.Builder, flavor Flavor, values []interface{}, arg interface{}) []interface{} {
|
||||
func (args *Args) compileArg(buf *stringBuilder, flavor Flavor, values []interface{}, arg interface{}) []interface{} {
|
||||
switch a := arg.(type) {
|
||||
case Builder:
|
||||
var s string
|
||||
|
||||
@@ -14,7 +14,7 @@ type Cond struct {
|
||||
|
||||
// Equal represents "field = value".
|
||||
func (c *Cond) Equal(field string, value interface{}) string {
|
||||
buf := &strings.Builder{}
|
||||
buf := newStringBuilder()
|
||||
buf.WriteString(Escape(field))
|
||||
buf.WriteString(" = ")
|
||||
buf.WriteString(c.Args.Add(value))
|
||||
@@ -28,7 +28,7 @@ func (c *Cond) E(field string, value interface{}) string {
|
||||
|
||||
// NotEqual represents "field <> value".
|
||||
func (c *Cond) NotEqual(field string, value interface{}) string {
|
||||
buf := &strings.Builder{}
|
||||
buf := newStringBuilder()
|
||||
buf.WriteString(Escape(field))
|
||||
buf.WriteString(" <> ")
|
||||
buf.WriteString(c.Args.Add(value))
|
||||
@@ -42,7 +42,7 @@ func (c *Cond) NE(field string, value interface{}) string {
|
||||
|
||||
// GreaterThan represents "field > value".
|
||||
func (c *Cond) GreaterThan(field string, value interface{}) string {
|
||||
buf := &strings.Builder{}
|
||||
buf := newStringBuilder()
|
||||
buf.WriteString(Escape(field))
|
||||
buf.WriteString(" > ")
|
||||
buf.WriteString(c.Args.Add(value))
|
||||
@@ -56,7 +56,7 @@ func (c *Cond) G(field string, value interface{}) string {
|
||||
|
||||
// GreaterEqualThan represents "field >= value".
|
||||
func (c *Cond) GreaterEqualThan(field string, value interface{}) string {
|
||||
buf := &strings.Builder{}
|
||||
buf := newStringBuilder()
|
||||
buf.WriteString(Escape(field))
|
||||
buf.WriteString(" >= ")
|
||||
buf.WriteString(c.Args.Add(value))
|
||||
@@ -70,7 +70,7 @@ func (c *Cond) GE(field string, value interface{}) string {
|
||||
|
||||
// LessThan represents "field < value".
|
||||
func (c *Cond) LessThan(field string, value interface{}) string {
|
||||
buf := &strings.Builder{}
|
||||
buf := newStringBuilder()
|
||||
buf.WriteString(Escape(field))
|
||||
buf.WriteString(" < ")
|
||||
buf.WriteString(c.Args.Add(value))
|
||||
@@ -84,7 +84,7 @@ func (c *Cond) L(field string, value interface{}) string {
|
||||
|
||||
// LessEqualThan represents "field <= value".
|
||||
func (c *Cond) LessEqualThan(field string, value interface{}) string {
|
||||
buf := &strings.Builder{}
|
||||
buf := newStringBuilder()
|
||||
buf.WriteString(Escape(field))
|
||||
buf.WriteString(" <= ")
|
||||
buf.WriteString(c.Args.Add(value))
|
||||
@@ -104,7 +104,7 @@ func (c *Cond) In(field string, value ...interface{}) string {
|
||||
vs = append(vs, c.Args.Add(v))
|
||||
}
|
||||
|
||||
buf := &strings.Builder{}
|
||||
buf := newStringBuilder()
|
||||
buf.WriteString(Escape(field))
|
||||
buf.WriteString(" IN (")
|
||||
buf.WriteString(strings.Join(vs, ", "))
|
||||
@@ -120,7 +120,7 @@ func (c *Cond) NotIn(field string, value ...interface{}) string {
|
||||
vs = append(vs, c.Args.Add(v))
|
||||
}
|
||||
|
||||
buf := &strings.Builder{}
|
||||
buf := newStringBuilder()
|
||||
buf.WriteString(Escape(field))
|
||||
buf.WriteString(" NOT IN (")
|
||||
buf.WriteString(strings.Join(vs, ", "))
|
||||
@@ -130,7 +130,7 @@ func (c *Cond) NotIn(field string, value ...interface{}) string {
|
||||
|
||||
// Like represents "field LIKE value".
|
||||
func (c *Cond) Like(field string, value interface{}) string {
|
||||
buf := &strings.Builder{}
|
||||
buf := newStringBuilder()
|
||||
buf.WriteString(Escape(field))
|
||||
buf.WriteString(" LIKE ")
|
||||
buf.WriteString(c.Args.Add(value))
|
||||
@@ -139,7 +139,7 @@ func (c *Cond) Like(field string, value interface{}) string {
|
||||
|
||||
// NotLike represents "field NOT LIKE value".
|
||||
func (c *Cond) NotLike(field string, value interface{}) string {
|
||||
buf := &strings.Builder{}
|
||||
buf := newStringBuilder()
|
||||
buf.WriteString(Escape(field))
|
||||
buf.WriteString(" NOT LIKE ")
|
||||
buf.WriteString(c.Args.Add(value))
|
||||
@@ -148,7 +148,7 @@ func (c *Cond) NotLike(field string, value interface{}) string {
|
||||
|
||||
// IsNull represents "field IS NULL".
|
||||
func (c *Cond) IsNull(field string) string {
|
||||
buf := &strings.Builder{}
|
||||
buf := newStringBuilder()
|
||||
buf.WriteString(Escape(field))
|
||||
buf.WriteString(" IS NULL")
|
||||
return buf.String()
|
||||
@@ -156,7 +156,7 @@ func (c *Cond) IsNull(field string) string {
|
||||
|
||||
// IsNotNull represents "field IS NOT NULL".
|
||||
func (c *Cond) IsNotNull(field string) string {
|
||||
buf := &strings.Builder{}
|
||||
buf := newStringBuilder()
|
||||
buf.WriteString(Escape(field))
|
||||
buf.WriteString(" IS NOT NULL")
|
||||
return buf.String()
|
||||
@@ -164,7 +164,7 @@ func (c *Cond) IsNotNull(field string) string {
|
||||
|
||||
// Between represents "field BETWEEN lower AND upper".
|
||||
func (c *Cond) Between(field string, lower, upper interface{}) string {
|
||||
buf := &strings.Builder{}
|
||||
buf := newStringBuilder()
|
||||
buf.WriteString(Escape(field))
|
||||
buf.WriteString(" BETWEEN ")
|
||||
buf.WriteString(c.Args.Add(lower))
|
||||
@@ -175,7 +175,7 @@ func (c *Cond) Between(field string, lower, upper interface{}) string {
|
||||
|
||||
// NotBetween represents "field NOT BETWEEN lower AND upper".
|
||||
func (c *Cond) NotBetween(field string, lower, upper interface{}) string {
|
||||
buf := &strings.Builder{}
|
||||
buf := newStringBuilder()
|
||||
buf.WriteString(Escape(field))
|
||||
buf.WriteString(" NOT BETWEEN ")
|
||||
buf.WriteString(c.Args.Add(lower))
|
||||
@@ -186,7 +186,7 @@ func (c *Cond) NotBetween(field string, lower, upper interface{}) string {
|
||||
|
||||
// Or represents OR logic like "expr1 OR expr2 OR expr3".
|
||||
func (c *Cond) Or(orExpr ...string) string {
|
||||
buf := &strings.Builder{}
|
||||
buf := newStringBuilder()
|
||||
buf.WriteString("(")
|
||||
buf.WriteString(strings.Join(orExpr, " OR "))
|
||||
buf.WriteString(")")
|
||||
@@ -195,7 +195,7 @@ func (c *Cond) Or(orExpr ...string) string {
|
||||
|
||||
// And represents AND logic like "expr1 AND expr2 AND expr3".
|
||||
func (c *Cond) And(andExpr ...string) string {
|
||||
buf := &strings.Builder{}
|
||||
buf := newStringBuilder()
|
||||
buf.WriteString("(")
|
||||
buf.WriteString(strings.Join(andExpr, " AND "))
|
||||
buf.WriteString(")")
|
||||
@@ -204,7 +204,7 @@ func (c *Cond) And(andExpr ...string) string {
|
||||
|
||||
// Exists represents "EXISTS (subquery)".
|
||||
func (c *Cond) Exists(subquery interface{}) string {
|
||||
buf := &strings.Builder{}
|
||||
buf := newStringBuilder()
|
||||
buf.WriteString("EXISTS (")
|
||||
buf.WriteString(c.Args.Add(subquery))
|
||||
buf.WriteString(")")
|
||||
@@ -213,7 +213,7 @@ func (c *Cond) Exists(subquery interface{}) string {
|
||||
|
||||
// NotExists represents "NOT EXISTS (subquery)".
|
||||
func (c *Cond) NotExists(subquery interface{}) string {
|
||||
buf := &strings.Builder{}
|
||||
buf := newStringBuilder()
|
||||
buf.WriteString("NOT EXISTS (")
|
||||
buf.WriteString(c.Args.Add(subquery))
|
||||
buf.WriteString(")")
|
||||
@@ -228,7 +228,7 @@ func (c *Cond) Any(field, op string, value ...interface{}) string {
|
||||
vs = append(vs, c.Args.Add(v))
|
||||
}
|
||||
|
||||
buf := &strings.Builder{}
|
||||
buf := newStringBuilder()
|
||||
buf.WriteString(Escape(field))
|
||||
buf.WriteString(" ")
|
||||
buf.WriteString(op)
|
||||
@@ -246,7 +246,7 @@ func (c *Cond) All(field, op string, value ...interface{}) string {
|
||||
vs = append(vs, c.Args.Add(v))
|
||||
}
|
||||
|
||||
buf := &strings.Builder{}
|
||||
buf := newStringBuilder()
|
||||
buf.WriteString(Escape(field))
|
||||
buf.WriteString(" ")
|
||||
buf.WriteString(op)
|
||||
@@ -264,7 +264,7 @@ func (c *Cond) Some(field, op string, value ...interface{}) string {
|
||||
vs = append(vs, c.Args.Add(v))
|
||||
}
|
||||
|
||||
buf := &strings.Builder{}
|
||||
buf := newStringBuilder()
|
||||
buf.WriteString(Escape(field))
|
||||
buf.WriteString(" ")
|
||||
buf.WriteString(op)
|
||||
|
||||
+12
-9
@@ -100,20 +100,25 @@ func (ctb *CreateTableBuilder) Build() (sql string, args []interface{}) {
|
||||
// BuildWithFlavor returns compiled CREATE TABLE string and args with flavor and initial args.
|
||||
// They can be used in `DB#Query` of package `database/sql` directly.
|
||||
func (ctb *CreateTableBuilder) BuildWithFlavor(flavor Flavor, initialArg ...interface{}) (sql string, args []interface{}) {
|
||||
buf := &strings.Builder{}
|
||||
buf := newStringBuilder()
|
||||
ctb.injection.WriteTo(buf, createTableMarkerInit)
|
||||
buf.WriteString(ctb.verb)
|
||||
|
||||
if len(ctb.verb) > 0 {
|
||||
buf.WriteLeadingString(ctb.verb)
|
||||
}
|
||||
|
||||
if ctb.ifNotExists {
|
||||
buf.WriteString(" IF NOT EXISTS")
|
||||
buf.WriteLeadingString("IF NOT EXISTS")
|
||||
}
|
||||
|
||||
if len(ctb.table) > 0 {
|
||||
buf.WriteLeadingString(ctb.table)
|
||||
}
|
||||
|
||||
buf.WriteRune(' ')
|
||||
buf.WriteString(ctb.table)
|
||||
ctb.injection.WriteTo(buf, createTableMarkerAfterCreate)
|
||||
|
||||
if len(ctb.defs) > 0 {
|
||||
buf.WriteString(" (")
|
||||
buf.WriteLeadingString("(")
|
||||
|
||||
defs := make([]string, 0, len(ctb.defs))
|
||||
|
||||
@@ -128,15 +133,13 @@ func (ctb *CreateTableBuilder) BuildWithFlavor(flavor Flavor, initialArg ...inte
|
||||
}
|
||||
|
||||
if len(ctb.options) > 0 {
|
||||
buf.WriteRune(' ')
|
||||
|
||||
opts := make([]string, 0, len(ctb.options))
|
||||
|
||||
for _, opt := range ctb.options {
|
||||
opts = append(opts, strings.Join(opt, " "))
|
||||
}
|
||||
|
||||
buf.WriteString(strings.Join(opts, ", "))
|
||||
buf.WriteLeadingString(strings.Join(opts, ", "))
|
||||
ctb.injection.WriteTo(buf, createTableMarkerAfterOption)
|
||||
}
|
||||
|
||||
|
||||
@@ -113,21 +113,25 @@ func (db *DeleteBuilder) Build() (sql string, args []interface{}) {
|
||||
// BuildWithFlavor returns compiled DELETE string and args with flavor and initial args.
|
||||
// They can be used in `DB#Query` of package `database/sql` directly.
|
||||
func (db *DeleteBuilder) BuildWithFlavor(flavor Flavor, initialArg ...interface{}) (sql string, args []interface{}) {
|
||||
buf := &strings.Builder{}
|
||||
buf := newStringBuilder()
|
||||
db.injection.WriteTo(buf, deleteMarkerInit)
|
||||
buf.WriteString("DELETE FROM ")
|
||||
buf.WriteString(db.table)
|
||||
|
||||
if len(db.table) > 0 {
|
||||
buf.WriteLeadingString("DELETE FROM ")
|
||||
buf.WriteString(db.table)
|
||||
}
|
||||
|
||||
db.injection.WriteTo(buf, deleteMarkerAfterDeleteFrom)
|
||||
|
||||
if len(db.whereExprs) > 0 {
|
||||
buf.WriteString(" WHERE ")
|
||||
buf.WriteLeadingString("WHERE ")
|
||||
buf.WriteString(strings.Join(db.whereExprs, " AND "))
|
||||
|
||||
db.injection.WriteTo(buf, deleteMarkerAfterWhere)
|
||||
}
|
||||
|
||||
if len(db.orderByCols) > 0 {
|
||||
buf.WriteString(" ORDER BY ")
|
||||
buf.WriteLeadingString("ORDER BY ")
|
||||
buf.WriteString(strings.Join(db.orderByCols, ", "))
|
||||
|
||||
if db.order != "" {
|
||||
@@ -139,7 +143,7 @@ func (db *DeleteBuilder) BuildWithFlavor(flavor Flavor, initialArg ...interface{
|
||||
}
|
||||
|
||||
if db.limit >= 0 {
|
||||
buf.WriteString(" LIMIT ")
|
||||
buf.WriteLeadingString("LIMIT ")
|
||||
buf.WriteString(strconv.Itoa(db.limit))
|
||||
|
||||
db.injection.WriteTo(buf, deleteMarkerAfterLimit)
|
||||
|
||||
+2
-11
@@ -29,22 +29,13 @@ func (injection *injection) SQL(marker injectionMarker, sql string) {
|
||||
|
||||
// WriteTo joins all SQL strings at the same marker value with blank (" ")
|
||||
// and writes the joined value to buf.
|
||||
func (injection *injection) WriteTo(buf *strings.Builder, marker injectionMarker) {
|
||||
func (injection *injection) WriteTo(buf *stringBuilder, marker injectionMarker) {
|
||||
sqls := injection.markerSQLs[marker]
|
||||
notEmpty := buf.Len() > 0
|
||||
|
||||
if len(sqls) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
if notEmpty {
|
||||
buf.WriteRune(' ')
|
||||
}
|
||||
|
||||
s := strings.Join(sqls, " ")
|
||||
buf.WriteString(s)
|
||||
|
||||
if !notEmpty {
|
||||
buf.WriteRune(' ')
|
||||
}
|
||||
buf.WriteLeadingString(s)
|
||||
}
|
||||
|
||||
@@ -117,29 +117,36 @@ func (ib *InsertBuilder) Build() (sql string, args []interface{}) {
|
||||
// BuildWithFlavor returns compiled INSERT string and args with flavor and initial args.
|
||||
// They can be used in `DB#Query` of package `database/sql` directly.
|
||||
func (ib *InsertBuilder) BuildWithFlavor(flavor Flavor, initialArg ...interface{}) (sql string, args []interface{}) {
|
||||
buf := &strings.Builder{}
|
||||
buf := newStringBuilder()
|
||||
ib.injection.WriteTo(buf, insertMarkerInit)
|
||||
buf.WriteString(ib.verb)
|
||||
buf.WriteString(" INTO ")
|
||||
buf.WriteString(ib.table)
|
||||
|
||||
if len(ib.table) > 0 {
|
||||
buf.WriteLeadingString(ib.verb)
|
||||
buf.WriteString(" INTO ")
|
||||
buf.WriteString(ib.table)
|
||||
}
|
||||
|
||||
ib.injection.WriteTo(buf, insertMarkerAfterInsertInto)
|
||||
|
||||
if len(ib.cols) > 0 {
|
||||
buf.WriteString(" (")
|
||||
buf.WriteLeadingString("(")
|
||||
buf.WriteString(strings.Join(ib.cols, ", "))
|
||||
buf.WriteString(")")
|
||||
|
||||
ib.injection.WriteTo(buf, insertMarkerAfterCols)
|
||||
}
|
||||
|
||||
buf.WriteString(" VALUES ")
|
||||
values := make([]string, 0, len(ib.values))
|
||||
if len(ib.values) > 0 {
|
||||
buf.WriteLeadingString("VALUES ")
|
||||
values := make([]string, 0, len(ib.values))
|
||||
|
||||
for _, v := range ib.values {
|
||||
values = append(values, fmt.Sprintf("(%v)", strings.Join(v, ", ")))
|
||||
for _, v := range ib.values {
|
||||
values = append(values, fmt.Sprintf("(%v)", strings.Join(v, ", ")))
|
||||
}
|
||||
|
||||
buf.WriteString(strings.Join(values, ", "))
|
||||
}
|
||||
|
||||
buf.WriteString(strings.Join(values, ", "))
|
||||
ib.injection.WriteTo(buf, insertMarkerAfterValues)
|
||||
|
||||
return ib.args.CompileWithFlavor(buf.String(), flavor, initialArg...)
|
||||
|
||||
@@ -234,35 +234,40 @@ func (sb *SelectBuilder) Build() (sql string, args []interface{}) {
|
||||
// BuildWithFlavor returns compiled SELECT string and args with flavor and initial args.
|
||||
// They can be used in `DB#Query` of package `database/sql` directly.
|
||||
func (sb *SelectBuilder) BuildWithFlavor(flavor Flavor, initialArg ...interface{}) (sql string, args []interface{}) {
|
||||
buf := &strings.Builder{}
|
||||
buf := newStringBuilder()
|
||||
sb.injection.WriteTo(buf, selectMarkerInit)
|
||||
buf.WriteString("SELECT ")
|
||||
|
||||
if sb.distinct {
|
||||
buf.WriteString("DISTINCT ")
|
||||
if len(sb.selectCols) > 0 {
|
||||
buf.WriteLeadingString("SELECT ")
|
||||
|
||||
if sb.distinct {
|
||||
buf.WriteString("DISTINCT ")
|
||||
}
|
||||
|
||||
buf.WriteString(strings.Join(sb.selectCols, ", "))
|
||||
}
|
||||
|
||||
buf.WriteString(strings.Join(sb.selectCols, ", "))
|
||||
sb.injection.WriteTo(buf, selectMarkerAfterSelect)
|
||||
|
||||
buf.WriteString(" FROM ")
|
||||
buf.WriteString(strings.Join(sb.tables, ", "))
|
||||
if len(sb.tables) > 0 {
|
||||
buf.WriteLeadingString("FROM ")
|
||||
buf.WriteString(strings.Join(sb.tables, ", "))
|
||||
}
|
||||
|
||||
sb.injection.WriteTo(buf, selectMarkerAfterFrom)
|
||||
|
||||
for i := range sb.joinTables {
|
||||
if option := sb.joinOptions[i]; option != "" {
|
||||
buf.WriteRune(' ')
|
||||
buf.WriteString(string(option))
|
||||
buf.WriteLeadingString(string(option))
|
||||
}
|
||||
|
||||
buf.WriteString(" JOIN ")
|
||||
buf.WriteLeadingString("JOIN ")
|
||||
buf.WriteString(sb.joinTables[i])
|
||||
|
||||
if exprs := sb.joinExprs[i]; len(exprs) > 0 {
|
||||
buf.WriteString(" ON ")
|
||||
buf.WriteString(strings.Join(sb.joinExprs[i], " AND "))
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if len(sb.joinTables) > 0 {
|
||||
@@ -270,14 +275,14 @@ func (sb *SelectBuilder) BuildWithFlavor(flavor Flavor, initialArg ...interface{
|
||||
}
|
||||
|
||||
if len(sb.whereExprs) > 0 {
|
||||
buf.WriteString(" WHERE ")
|
||||
buf.WriteLeadingString("WHERE ")
|
||||
buf.WriteString(strings.Join(sb.whereExprs, " AND "))
|
||||
|
||||
sb.injection.WriteTo(buf, selectMarkerAfterWhere)
|
||||
}
|
||||
|
||||
if len(sb.groupByCols) > 0 {
|
||||
buf.WriteString(" GROUP BY ")
|
||||
buf.WriteLeadingString("GROUP BY ")
|
||||
buf.WriteString(strings.Join(sb.groupByCols, ", "))
|
||||
|
||||
if len(sb.havingExprs) > 0 {
|
||||
@@ -289,7 +294,7 @@ func (sb *SelectBuilder) BuildWithFlavor(flavor Flavor, initialArg ...interface{
|
||||
}
|
||||
|
||||
if len(sb.orderByCols) > 0 {
|
||||
buf.WriteString(" ORDER BY ")
|
||||
buf.WriteLeadingString("ORDER BY ")
|
||||
buf.WriteString(strings.Join(sb.orderByCols, ", "))
|
||||
|
||||
if sb.order != "" {
|
||||
@@ -303,27 +308,27 @@ func (sb *SelectBuilder) BuildWithFlavor(flavor Flavor, initialArg ...interface{
|
||||
switch flavor {
|
||||
case MySQL, SQLite, ClickHouse:
|
||||
if sb.limit >= 0 {
|
||||
buf.WriteString(" LIMIT ")
|
||||
buf.WriteLeadingString("LIMIT ")
|
||||
buf.WriteString(strconv.Itoa(sb.limit))
|
||||
|
||||
if sb.offset >= 0 {
|
||||
buf.WriteString(" OFFSET ")
|
||||
buf.WriteLeadingString("OFFSET ")
|
||||
buf.WriteString(strconv.Itoa(sb.offset))
|
||||
}
|
||||
}
|
||||
case CQL:
|
||||
if sb.limit >= 0 {
|
||||
buf.WriteString(" LIMIT ")
|
||||
buf.WriteLeadingString("LIMIT ")
|
||||
buf.WriteString(strconv.Itoa(sb.limit))
|
||||
}
|
||||
case PostgreSQL, Presto:
|
||||
if sb.limit >= 0 {
|
||||
buf.WriteString(" LIMIT ")
|
||||
buf.WriteLeadingString("LIMIT ")
|
||||
buf.WriteString(strconv.Itoa(sb.limit))
|
||||
}
|
||||
|
||||
if sb.offset >= 0 {
|
||||
buf.WriteString(" OFFSET ")
|
||||
buf.WriteLeadingString("OFFSET ")
|
||||
buf.WriteString(strconv.Itoa(sb.offset))
|
||||
}
|
||||
|
||||
@@ -331,21 +336,21 @@ func (sb *SelectBuilder) BuildWithFlavor(flavor Flavor, initialArg ...interface{
|
||||
// If ORDER BY is not set, sort column #1 by default.
|
||||
// It's required to make OFFSET...FETCH work.
|
||||
if len(sb.orderByCols) == 0 && (sb.limit >= 0 || sb.offset >= 0) {
|
||||
buf.WriteString(" ORDER BY 1")
|
||||
buf.WriteLeadingString("ORDER BY 1")
|
||||
}
|
||||
|
||||
if sb.offset >= 0 {
|
||||
buf.WriteString(" OFFSET ")
|
||||
buf.WriteLeadingString("OFFSET ")
|
||||
buf.WriteString(strconv.Itoa(sb.offset))
|
||||
buf.WriteString(" ROWS")
|
||||
}
|
||||
|
||||
if sb.limit >= 0 {
|
||||
if sb.offset < 0 {
|
||||
buf.WriteString(" OFFSET 0 ROWS")
|
||||
buf.WriteLeadingString("OFFSET 0 ROWS")
|
||||
}
|
||||
|
||||
buf.WriteString(" FETCH NEXT ")
|
||||
buf.WriteLeadingString("FETCH NEXT ")
|
||||
buf.WriteString(strconv.Itoa(sb.limit))
|
||||
buf.WriteString(" ROWS ONLY")
|
||||
}
|
||||
@@ -356,7 +361,7 @@ func (sb *SelectBuilder) BuildWithFlavor(flavor Flavor, initialArg ...interface{
|
||||
}
|
||||
|
||||
if sb.forWhat != "" {
|
||||
buf.WriteString(" FOR ")
|
||||
buf.WriteLeadingString("FOR ")
|
||||
buf.WriteString(sb.forWhat)
|
||||
|
||||
sb.injection.WriteTo(buf, selectMarkerAfterFor)
|
||||
|
||||
@@ -275,3 +275,21 @@ func ExampleSelectBuilder_SQL() {
|
||||
// Output:
|
||||
// /* before */ SELECT u.id, u.name, c.type, p.nickname /* after select */ FROM user u /* after from */ JOIN contract c ON u.id = c.user_id RIGHT OUTER JOIN person p ON u.id = p.user_id /* after join */ WHERE u.modified_at > u.created_at /* after where */ ORDER BY id /* after order by */ LIMIT 10 /* after limit */ FOR SHARE /* after for */
|
||||
}
|
||||
|
||||
// Example for issue #115.
|
||||
func ExampleSelectBuilder_customSELECT() {
|
||||
sb := NewSelectBuilder()
|
||||
|
||||
// Set a custom SELECT clause.
|
||||
sb.SQL("SELECT id, name FROM user").Where(
|
||||
sb.In("id", 1, 2, 3),
|
||||
)
|
||||
|
||||
s, args := sb.Build()
|
||||
fmt.Println(s)
|
||||
fmt.Println(args)
|
||||
|
||||
// Output:
|
||||
// SELECT id, name FROM user WHERE id IN (?, ?, ?)
|
||||
// [1 2 3]
|
||||
}
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
// Copyright 2023 Huan Du. All rights reserved.
|
||||
// Licensed under the MIT license that can be found in the LICENSE file.
|
||||
|
||||
package sqlbuilder
|
||||
|
||||
import (
|
||||
"io"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type stringBuilder struct {
|
||||
builder *strings.Builder
|
||||
}
|
||||
|
||||
var _ io.Writer = new(stringBuilder)
|
||||
|
||||
func newStringBuilder() *stringBuilder {
|
||||
return &stringBuilder{
|
||||
builder: &strings.Builder{},
|
||||
}
|
||||
}
|
||||
|
||||
// WriteLeadingString writes s to internal buffer.
|
||||
// If it's not the first time to write the string, a blank (" ") will be written before s.
|
||||
func (sb *stringBuilder) WriteLeadingString(s string) {
|
||||
if sb.builder.Len() > 0 {
|
||||
sb.builder.WriteString(" ")
|
||||
}
|
||||
|
||||
sb.builder.WriteString(s)
|
||||
}
|
||||
|
||||
func (sb *stringBuilder) WriteString(s string) {
|
||||
sb.builder.WriteString(s)
|
||||
}
|
||||
|
||||
func (sb *stringBuilder) WriteRune(r rune) {
|
||||
sb.builder.WriteRune(r)
|
||||
}
|
||||
|
||||
func (sb *stringBuilder) Write(data []byte) (int, error) {
|
||||
return sb.builder.Write(data)
|
||||
}
|
||||
|
||||
func (sb *stringBuilder) String() string {
|
||||
return sb.builder.String()
|
||||
}
|
||||
|
||||
func (sb *stringBuilder) Reset() {
|
||||
sb.builder.Reset()
|
||||
}
|
||||
@@ -9,7 +9,6 @@ import (
|
||||
"reflect"
|
||||
"regexp"
|
||||
"sort"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var (
|
||||
@@ -305,7 +304,7 @@ func (s *Struct) selectFromWithTags(table string, with, without []string) (sb *S
|
||||
return
|
||||
}
|
||||
|
||||
buf := &strings.Builder{}
|
||||
buf := newStringBuilder()
|
||||
cols := make([]string, 0, len(tagged.ForRead))
|
||||
|
||||
for _, sf := range tagged.ForRead {
|
||||
|
||||
@@ -129,20 +129,18 @@ func (ub *UnionBuilder) Build() (sql string, args []interface{}) {
|
||||
// BuildWithFlavor returns compiled SELECT string and args with flavor and initial args.
|
||||
// They can be used in `DB#Query` of package `database/sql` directly.
|
||||
func (ub *UnionBuilder) BuildWithFlavor(flavor Flavor, initialArg ...interface{}) (sql string, args []interface{}) {
|
||||
buf := &strings.Builder{}
|
||||
buf := newStringBuilder()
|
||||
ub.injection.WriteTo(buf, unionMarkerInit)
|
||||
|
||||
if len(ub.builders) > 0 {
|
||||
needParen := flavor != SQLite
|
||||
|
||||
if needParen {
|
||||
buf.WriteRune('(')
|
||||
}
|
||||
|
||||
buf.WriteString(ub.Var(ub.builders[0]))
|
||||
|
||||
if needParen {
|
||||
buf.WriteLeadingString("(")
|
||||
buf.WriteString(ub.Var(ub.builders[0]))
|
||||
buf.WriteRune(')')
|
||||
} else {
|
||||
buf.WriteLeadingString(ub.Var(ub.builders[0]))
|
||||
}
|
||||
|
||||
for _, b := range ub.builders[1:] {
|
||||
@@ -163,7 +161,7 @@ func (ub *UnionBuilder) BuildWithFlavor(flavor Flavor, initialArg ...interface{}
|
||||
ub.injection.WriteTo(buf, unionMarkerAfterUnion)
|
||||
|
||||
if len(ub.orderByCols) > 0 {
|
||||
buf.WriteString(" ORDER BY ")
|
||||
buf.WriteLeadingString("ORDER BY ")
|
||||
buf.WriteString(strings.Join(ub.orderByCols, ", "))
|
||||
|
||||
if ub.order != "" {
|
||||
@@ -175,14 +173,14 @@ func (ub *UnionBuilder) BuildWithFlavor(flavor Flavor, initialArg ...interface{}
|
||||
}
|
||||
|
||||
if ub.limit >= 0 {
|
||||
buf.WriteString(" LIMIT ")
|
||||
buf.WriteLeadingString("LIMIT ")
|
||||
buf.WriteString(strconv.Itoa(ub.limit))
|
||||
|
||||
}
|
||||
|
||||
if MySQL == flavor && ub.limit >= 0 || PostgreSQL == flavor {
|
||||
if ub.offset >= 0 {
|
||||
buf.WriteString(" OFFSET ")
|
||||
buf.WriteLeadingString("OFFSET ")
|
||||
buf.WriteString(strconv.Itoa(ub.offset))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -171,36 +171,42 @@ func (ub *UpdateBuilder) Build() (sql string, args []interface{}) {
|
||||
// BuildWithFlavor returns compiled UPDATE string and args with flavor and initial args.
|
||||
// They can be used in `DB#Query` of package `database/sql` directly.
|
||||
func (ub *UpdateBuilder) BuildWithFlavor(flavor Flavor, initialArg ...interface{}) (sql string, args []interface{}) {
|
||||
buf := &strings.Builder{}
|
||||
buf := newStringBuilder()
|
||||
ub.injection.WriteTo(buf, updateMarkerInit)
|
||||
buf.WriteString("UPDATE ")
|
||||
buf.WriteString(ub.table)
|
||||
|
||||
if len(ub.table) > 0 {
|
||||
buf.WriteLeadingString("UPDATE ")
|
||||
buf.WriteString(ub.table)
|
||||
}
|
||||
|
||||
ub.injection.WriteTo(buf, updateMarkerAfterUpdate)
|
||||
|
||||
buf.WriteString(" SET ")
|
||||
buf.WriteString(strings.Join(ub.assignments, ", "))
|
||||
if len(ub.assignments) > 0 {
|
||||
buf.WriteLeadingString("SET ")
|
||||
buf.WriteString(strings.Join(ub.assignments, ", "))
|
||||
}
|
||||
|
||||
ub.injection.WriteTo(buf, updateMarkerAfterSet)
|
||||
|
||||
if len(ub.whereExprs) > 0 {
|
||||
buf.WriteString(" WHERE ")
|
||||
buf.WriteLeadingString("WHERE ")
|
||||
buf.WriteString(strings.Join(ub.whereExprs, " AND "))
|
||||
ub.injection.WriteTo(buf, updateMarkerAfterWhere)
|
||||
}
|
||||
|
||||
if len(ub.orderByCols) > 0 {
|
||||
buf.WriteString(" ORDER BY ")
|
||||
buf.WriteLeadingString("ORDER BY ")
|
||||
buf.WriteString(strings.Join(ub.orderByCols, ", "))
|
||||
|
||||
if ub.order != "" {
|
||||
buf.WriteRune(' ')
|
||||
buf.WriteString(ub.order)
|
||||
buf.WriteLeadingString(ub.order)
|
||||
}
|
||||
|
||||
ub.injection.WriteTo(buf, updateMarkerAfterOrderBy)
|
||||
}
|
||||
|
||||
if ub.limit >= 0 {
|
||||
buf.WriteString(" LIMIT ")
|
||||
buf.WriteLeadingString("LIMIT ")
|
||||
buf.WriteString(strconv.Itoa(ub.limit))
|
||||
|
||||
ub.injection.WriteTo(buf, updateMarkerAfterLimit)
|
||||
|
||||
Reference in New Issue
Block a user