Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,236 @@
// Copyright GoFrame Author(https://goframe.org). All Rights Reserved.
//
// This Source Code Form is subject to the terms of the MIT License.
// If a copy of the MIT was not distributed with this file,
// You can obtain one at https://github.com/gogf/gf.

package mysql_test

import (
"testing"

"github.com/gogf/gf/v2/database/gdb"
"github.com/gogf/gf/v2/frame/g"
"github.com/gogf/gf/v2/os/gtime"
"github.com/gogf/gf/v2/test/gtest"
)

// Test_Model_Group_WithJoin tests GROUP BY with JOIN queries
func Test_Model_Group_WithJoin(t *testing.T) {
var (
table1 = gtime.TimestampNanoStr() + "_user"
table2 = gtime.TimestampNanoStr() + "_user_detail"
)
createInitTable(table1)
defer dropTable(table1)
createInitTable(table2)
defer dropTable(table2)

gtest.C(t, func(t *gtest.T) {
// Test basic GROUP BY with JOIN - unqualified column should be auto-prefixed
// This prevents "Column 'id' in group statement is ambiguous" error
r, err := db.Model(table1+" u").
Fields("u.id", "u.nickname", "COUNT(*) as count").
LeftJoin(table2+" ud", "u.id = ud.id").
Where("u.id", g.Slice{1, 2}).
Group("id").
Order("u.id asc").All()
t.AssertNil(err)
t.Assert(len(r), 2)
t.Assert(r[0]["id"], "1")
t.Assert(r[1]["id"], "2")

// Test GROUP BY with already qualified column
r, err = db.Model(table1+" u").
Fields("u.id", "u.nickname", "COUNT(*) as count").
LeftJoin(table2+" ud", "u.id = ud.id").
Where("u.id", g.Slice{1, 2}).
Group("u.id").
Order("u.id asc").All()
t.AssertNil(err)
t.Assert(len(r), 2)
t.Assert(r[0]["id"], "1")
t.Assert(r[1]["id"], "2")

// Test GROUP BY with multiple columns
r, err = db.Model(table1+" u").
Fields("u.id", "u.nickname", "COUNT(*) as count").
LeftJoin(table2+" ud", "u.id = ud.id").
Where("u.id", g.Slice{1, 2}).
Group("id", "nickname").
Order("u.id asc").All()
t.AssertNil(err)
t.Assert(len(r), 2)

// Test GROUP BY with Raw expression
r, err = db.Model(table1+" u").
Fields("u.id", "u.nickname", "COUNT(*) as count").
LeftJoin(table2+" ud", "u.id = ud.id").
Where("u.id", g.Slice{1, 2}).
Group(gdb.Raw("u.id")).
Order("u.id asc").All()
t.AssertNil(err)
t.Assert(len(r), 2)
t.Assert(r[0]["id"], "1")
t.Assert(r[1]["id"], "2")

// Test GROUP BY on non-primary table should work correctly
r, err = db.Model(table1+" u").
Fields("ud.id", "COUNT(*) as count").
LeftJoin(table2+" ud", "u.id = ud.id").
Where("u.id", g.Slice{1, 2}).
Group("ud.id").
Order("ud.id asc").All()
t.AssertNil(err)
// Should have results from the joined table
t.Assert(len(r) > 0, true)
})
}

// Test_Model_Order_WithJoin tests ORDER BY with JOIN queries
func Test_Model_Order_WithJoin(t *testing.T) {
var (
table1 = gtime.TimestampNanoStr() + "_user"
table2 = gtime.TimestampNanoStr() + "_user_detail"
)
createInitTable(table1)
defer dropTable(table1)
createInitTable(table2)
defer dropTable(table2)

gtest.C(t, func(t *gtest.T) {
// Test ORDER BY with JOIN - unqualified column should be auto-prefixed
r, err := db.Model(table1+" u").
LeftJoin(table2+" ud", "u.id = ud.id").
Where("u.id", g.Slice{1, 2}).
Order("id desc").All()
t.AssertNil(err)
t.Assert(len(r), 2)
t.Assert(r[0]["id"], "2")
t.Assert(r[1]["id"], "1")

// Test ORDER BY with already qualified column
r, err = db.Model(table1+" u").
LeftJoin(table2+" ud", "u.id = ud.id").
Where("u.id", g.Slice{1, 2}).
Order("u.id asc").All()
t.AssertNil(err)
t.Assert(len(r), 2)
t.Assert(r[0]["id"], "1")
t.Assert(r[1]["id"], "2")

// Test ORDER BY with Raw expression
r, err = db.Model(table1+" u").
LeftJoin(table2+" ud", "u.id = ud.id").
Where("u.id", g.Slice{1, 2}).
Order(gdb.Raw("u.id asc")).All()
t.AssertNil(err)
t.Assert(len(r), 2)
t.Assert(r[0]["id"], "1")
t.Assert(r[1]["id"], "2")

// Test multiple ORDER BY clauses with JOIN
r, err = db.Model(table1+" u").
LeftJoin(table2+" ud", "u.id = ud.id").
Order("id asc").Order("nickname asc").All()
t.AssertNil(err)
t.Assert(len(r) > 0, true)

// Test ORDER BY with asc/desc keywords
r, err = db.Model(table1+" u").
LeftJoin(table2+" ud", "u.id = ud.id").
Where("u.id", g.Slice{1, 2}).
Order("id asc").All()
t.AssertNil(err)
t.Assert(len(r), 2)
t.Assert(r[0]["id"], "1")
t.Assert(r[1]["id"], "2")
})
}

// Test_Model_Group_And_Order_WithJoin tests combined GROUP BY and ORDER BY with JOINs
func Test_Model_Group_And_Order_WithJoin(t *testing.T) {
var (
table1 = gtime.TimestampNanoStr() + "_user"
table2 = gtime.TimestampNanoStr() + "_user_detail"
)
createInitTable(table1)
defer dropTable(table1)
createInitTable(table2)
defer dropTable(table2)

gtest.C(t, func(t *gtest.T) {
// Test combined GROUP BY and ORDER BY with JOIN
r, err := db.Model(table1+" u").
Fields("u.id", "COUNT(*) as count").
LeftJoin(table2+" ud", "u.id = ud.id").
Where("u.id", g.Slice{1, 2}).
Group("id").
Order("id desc").All()
t.AssertNil(err)
t.Assert(len(r), 2)
t.Assert(r[0]["id"], "2")
t.Assert(r[1]["id"], "1")

// Test with already qualified GROUP BY and unqualified ORDER BY
r, err = db.Model(table1+" u").
Fields("u.id", "COUNT(*) as count").
LeftJoin(table2+" ud", "u.id = ud.id").
Where("u.id", g.Slice{1, 2}).
Group("u.id").
Order("id asc").All()
t.AssertNil(err)
t.Assert(len(r), 2)
t.Assert(r[0]["id"], "1")
t.Assert(r[1]["id"], "2")

// Test with unqualified GROUP BY and qualified ORDER BY
r, err = db.Model(table1+" u").
Fields("u.id", "COUNT(*) as count").
LeftJoin(table2+" ud", "u.id = ud.id").
Where("u.id", g.Slice{1, 2}).
Group("id").
Order("u.id desc").All()
t.AssertNil(err)
t.Assert(len(r), 2)
t.Assert(r[0]["id"], "2")
t.Assert(r[1]["id"], "1")

// Test with both unqualified
r, err = db.Model(table1+" u").
Fields("u.id", "COUNT(*) as count").
LeftJoin(table2+" ud", "u.id = ud.id").
Where("u.id", g.Slice{1, 2}).
Group("id").
Order("id").All()
t.AssertNil(err)
t.Assert(len(r), 2)
})
}

// Test_Model_Join_Without_Alias tests JOIN without table aliases
func Test_Model_Join_Without_Alias(t *testing.T) {
var (
table1 = gtime.TimestampNanoStr() + "_user"
table2 = gtime.TimestampNanoStr() + "_user_detail"
)
createInitTable(table1)
defer dropTable(table1)
createInitTable(table2)
defer dropTable(table2)

gtest.C(t, func(t *gtest.T) {
// Test GROUP BY and ORDER BY with JOIN but without aliases
// This should still work correctly
r, err := db.Model(table1).
Fields(table1+".id", "COUNT(*) as count").
LeftJoin(table2, table1+".id = "+table2+".id").
Where(table1+".id", g.Slice{1, 2}).
Group(table1 + ".id").
Order(table1 + ".id asc").All()
t.AssertNil(err)
t.Assert(len(r), 2)
t.Assert(r[0]["id"], "1")
t.Assert(r[1]["id"], "2")
})
}
1 change: 1 addition & 0 deletions contrib/drivers/mysql/mysql_z_unit_model_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3783,6 +3783,7 @@ func Test_Model_FixGdbJoin(t *testing.T) {
FieldsPrefix(`rules_template`, "name").
FieldsPrefix(`common_resource`, `src_instance_id`, "database_kind", "source_type", "ip", "port")
all, err := orm.OrderAsc("src_instance_id").All()
t.Assert(err, nil)
t.Assert(len(all), 4)
t.Assert(all[0]["pay_mode"], 1)
t.Assert(all[0]["src_instance_id"], 2)
Expand Down
69 changes: 62 additions & 7 deletions database/gdb/gdb_model_order_group.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
package gdb

import (
"strings"
"fmt"

"github.com/gogf/gf/v2/text/gstr"
"github.com/gogf/gf/v2/util/gconv"
Expand All @@ -30,6 +30,7 @@ func (m *Model) Order(orderBy ...any) *Model {
core = m.db.GetCore()
model = m.getModel()
)

for _, v := range orderBy {
if model.orderBy != "" {
model.orderBy += ","
Expand All @@ -40,13 +41,47 @@ func (m *Model) Order(orderBy ...any) *Model {
default:
orderByStr := gconv.String(v)
if gstr.Contains(orderByStr, " ") {
model.orderBy += core.QuoteString(orderByStr)
// Handle "column asc/desc" format
parts := gstr.SplitAndTrim(orderByStr, " ")
if len(parts) >= 2 {
columnPart := parts[0]
orderPart := gstr.Join(parts[1:], " ")

// Check if column part is qualified
if gstr.Contains(columnPart, ".") {
model.orderBy += core.QuoteString(columnPart) + " " + orderPart
} else {
// Try to get the correct prefix for this field
prefix := m.getPrefixByField(columnPart)
if prefix != "" {
model.orderBy += core.QuoteString(fmt.Sprintf("%s.%s", prefix, columnPart)) + " " + orderPart
} else {
// If we can't determine the table, just quote the field
model.orderBy += core.QuoteWord(columnPart) + " " + orderPart
}
Comment thread
hailaz marked this conversation as resolved.
}
} else {
// Fallback for complex expressions
model.orderBy += core.QuoteString(orderByStr)
}
} else {
if gstr.Equal(orderByStr, "ASC") || gstr.Equal(orderByStr, "DESC") {
model.orderBy = gstr.TrimRight(model.orderBy, ",")
model.orderBy += " " + orderByStr
} else {
model.orderBy += core.QuoteWord(orderByStr)
// Check if column is already qualified
if gstr.Contains(orderByStr, ".") {
model.orderBy += core.QuoteString(orderByStr)
} else {
// Try to get the correct prefix for this field
prefix := m.getPrefixByField(orderByStr)
if prefix != "" {
model.orderBy += core.QuoteString(fmt.Sprintf("%s.%s", prefix, orderByStr))
} else {
// If we can't determine the table, just quote the field
model.orderBy += core.QuoteWord(orderByStr)
}
Comment thread
hailaz marked this conversation as resolved.
}
Comment thread
hailaz marked this conversation as resolved.
}
}
}
Expand Down Expand Up @@ -78,7 +113,7 @@ func (m *Model) OrderRandom() *Model {
}

// Group sets the "GROUP BY" statement for the model.
func (m *Model) Group(groupBy ...string) *Model {
func (m *Model) Group(groupBy ...any) *Model {
if len(groupBy) == 0 {
return m
}
Expand All @@ -87,9 +122,29 @@ func (m *Model) Group(groupBy ...string) *Model {
model = m.getModel()
)

if model.groupBy != "" {
model.groupBy += ","
for _, v := range groupBy {
if model.groupBy != "" {
model.groupBy += ","
}
switch v.(type) {
case Raw, *Raw:
model.groupBy += gconv.String(v)
default:
groupByStr := gconv.String(v)
if gstr.Contains(groupByStr, ".") {
// Already qualified (e.g., "table.column")
model.groupBy += core.QuoteString(groupByStr)
Comment thread
hailaz marked this conversation as resolved.
} else {
// Try to get the correct prefix for this field
prefix := m.getPrefixByField(groupByStr)
if prefix != "" {
model.groupBy += core.QuoteString(fmt.Sprintf("%s.%s", prefix, groupByStr))
} else {
// If we can't determine the table, just quote the field
model.groupBy += core.QuoteWord(groupByStr)
Comment thread
hailaz marked this conversation as resolved.
}
}
}
}
model.groupBy += core.QuoteString(strings.Join(groupBy, ","))
return model
}
Loading
Loading