mirror of
https://github.com/gogf/gf.git
synced 2025-04-05 11:18:50 +08:00
feat(contrib/drivers/mssql): enable unit testing (#4043)
This commit is contained in:
parent
ac53170884
commit
a09454accf
15
.github/workflows/ci-main.yml
vendored
15
.github/workflows/ci-main.yml
vendored
@ -109,22 +109,19 @@ jobs:
|
|||||||
# -p 1433:1433 \
|
# -p 1433:1433 \
|
||||||
# -e ACCEPT_EULA=Y \
|
# -e ACCEPT_EULA=Y \
|
||||||
# -e SA_PASSWORD=LoremIpsum86 \
|
# -e SA_PASSWORD=LoremIpsum86 \
|
||||||
# -e MSSQL_DB=test \
|
|
||||||
# -e MSSQL_USER=root \
|
# -e MSSQL_USER=root \
|
||||||
# -e MSSQL_PASSWORD=LoremIpsum86 \
|
# -e MSSQL_PASSWORD=LoremIpsum86 \
|
||||||
# loads/mssqldocker:14.0.3391.2
|
# mcr.microsoft.com/mssql/server:2022-latest
|
||||||
mssql:
|
mssql:
|
||||||
image: loads/mssqldocker:14.0.3391.2
|
image: mcr.microsoft.com/mssql/server:2022-latest
|
||||||
env:
|
env:
|
||||||
ACCEPT_EULA: Y
|
TZ: Asia/Shanghai
|
||||||
SA_PASSWORD: LoremIpsum86
|
ACCEPT_EULA: Y
|
||||||
MSSQL_DB: test
|
MSSQL_SA_PASSWORD: LoremIpsum86
|
||||||
MSSQL_USER: root
|
|
||||||
MSSQL_PASSWORD: LoremIpsum86
|
|
||||||
ports:
|
ports:
|
||||||
- 1433:1433
|
- 1433:1433
|
||||||
options: >-
|
options: >-
|
||||||
--health-cmd="/opt/mssql-tools/bin/sqlcmd -S localhost -U sa -P LoremIpsum86 -l 30 -Q \"SELECT 1\" || exit 1"
|
--health-cmd="/opt/mssql-tools18/bin/sqlcmd -S localhost -U sa -P ${MSSQL_SA_PASSWORD} -N -C -l 30 -Q \"SELECT 1\" || exit 1"
|
||||||
--health-start-period 10s
|
--health-start-period 10s
|
||||||
--health-interval 10s
|
--health-interval 10s
|
||||||
--health-timeout 5s
|
--health-timeout 5s
|
||||||
|
@ -18,12 +18,18 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
orderBySqlTmp = `SELECT %s %s OFFSET %d ROWS FETCH NEXT %d ROWS ONLY`
|
|
||||||
withoutOrderBySqlTmp = `SELECT %s OFFSET %d ROWS FETCH NEXT %d ROWS ONLY`
|
|
||||||
selectWithOrderSqlTmp = `
|
selectWithOrderSqlTmp = `
|
||||||
SELECT * FROM (SELECT ROW_NUMBER() OVER (ORDER BY %s) as ROW_NUMBER__, %s ) as TMP_
|
SELECT * FROM (
|
||||||
WHERE TMP_.ROW_NUMBER__ > %d AND TMP_.ROW_NUMBER__ <= %d
|
SELECT ROW_NUMBER() OVER (ORDER BY %s) as ROW_NUMBER__, %s
|
||||||
`
|
FROM (%s) as InnerQuery
|
||||||
|
) as TMP_
|
||||||
|
WHERE TMP_.ROW_NUMBER__ > %d AND TMP_.ROW_NUMBER__ <= %d`
|
||||||
|
selectWithoutOrderSqlTmp = `
|
||||||
|
SELECT * FROM (
|
||||||
|
SELECT ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) as ROW_NUMBER__, %s
|
||||||
|
FROM (%s) as InnerQuery
|
||||||
|
) as TMP_
|
||||||
|
WHERE TMP_.ROW_NUMBER__ > %d AND TMP_.ROW_NUMBER__ <= %d`
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
@ -32,6 +38,10 @@ func init() {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
selectWithoutOrderSqlTmp, err = gdb.FormatMultiLineSqlToSingle(selectWithoutOrderSqlTmp)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// DoFilter deals with the sql string before commits it to underlying sql driver.
|
// DoFilter deals with the sql string before commits it to underlying sql driver.
|
||||||
@ -98,14 +108,19 @@ func (d *Driver) handleSelectSqlReplacement(toBeCommittedSql string) (newSql str
|
|||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
|
|
||||||
// SELECT and ORDER BY
|
// Extract SELECT part
|
||||||
selectStr := strings.TrimSpace(allMatch[1])
|
selectStr := strings.TrimSpace(allMatch[1])
|
||||||
|
|
||||||
|
// Extract ORDER BY part
|
||||||
orderStr := ""
|
orderStr := ""
|
||||||
if len(allMatch[2]) > 0 {
|
if len(allMatch[2]) > 0 {
|
||||||
orderStr = strings.TrimSpace(allMatch[2])
|
orderStr = strings.TrimSpace(allMatch[2])
|
||||||
|
// Remove "ORDER BY" prefix as it will be used in OVER clause
|
||||||
|
orderStr = strings.TrimPrefix(orderStr, "ORDER BY")
|
||||||
|
orderStr = strings.TrimSpace(orderStr)
|
||||||
}
|
}
|
||||||
|
|
||||||
// LIMIT and OFFSET value
|
// Calculate LIMIT and OFFSET values
|
||||||
first, _ := strconv.Atoi(allMatch[3]) // LIMIT first parameter
|
first, _ := strconv.Atoi(allMatch[3]) // LIMIT first parameter
|
||||||
limit := 0
|
limit := 0
|
||||||
if len(allMatch) > 4 && allMatch[4] != "" {
|
if len(allMatch) > 4 && allMatch[4] != "" {
|
||||||
@ -115,19 +130,26 @@ func (d *Driver) handleSelectSqlReplacement(toBeCommittedSql string) (newSql str
|
|||||||
first = 0
|
first = 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Build the final query
|
||||||
if orderStr != "" {
|
if orderStr != "" {
|
||||||
// have ORDER BY clause
|
// Have ORDER BY clause
|
||||||
newSql = fmt.Sprintf(
|
newSql = fmt.Sprintf(
|
||||||
orderBySqlTmp,
|
selectWithOrderSqlTmp,
|
||||||
selectStr, orderStr, first, limit,
|
orderStr, // ORDER BY clause for ROW_NUMBER
|
||||||
|
"*", // Select all columns
|
||||||
|
fmt.Sprintf("SELECT %s", selectStr), // Original SELECT
|
||||||
|
first, // OFFSET
|
||||||
|
first+limit, // OFFSET + LIMIT
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
// without ORDER BY clause
|
// Without ORDER BY clause
|
||||||
newSql = fmt.Sprintf(
|
newSql = fmt.Sprintf(
|
||||||
withoutOrderBySqlTmp,
|
selectWithoutOrderSqlTmp,
|
||||||
selectStr, first, limit,
|
"*", // Select all columns
|
||||||
|
fmt.Sprintf("SELECT %s", selectStr), // Original SELECT
|
||||||
|
first, // OFFSET
|
||||||
|
first+limit, // OFFSET + LIMIT
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
return newSql, nil
|
return newSql, nil
|
||||||
}
|
}
|
||||||
|
@ -54,7 +54,6 @@ func TestDriver_DoFilter(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestDriver_handleSelectSqlReplacement(t *testing.T) {
|
func TestDriver_handleSelectSqlReplacement(t *testing.T) {
|
||||||
|
|
||||||
gtest.C(t, func(t *gtest.T) {
|
gtest.C(t, func(t *gtest.T) {
|
||||||
d := &Driver{}
|
d := &Driver{}
|
||||||
|
|
||||||
@ -67,7 +66,7 @@ func TestDriver_handleSelectSqlReplacement(t *testing.T) {
|
|||||||
|
|
||||||
// LIMIT query with offset and number of rows
|
// LIMIT query with offset and number of rows
|
||||||
inputSql = "SELECT * FROM User ORDER BY ID DESC LIMIT 100, 200"
|
inputSql = "SELECT * FROM User ORDER BY ID DESC LIMIT 100, 200"
|
||||||
expectedSql = "SELECT * FROM User ORDER BY ID DESC OFFSET 100 ROWS FETCH NEXT 200 ROWS ONLY"
|
expectedSql = "SELECT * FROM ( SELECT ROW_NUMBER() OVER (ORDER BY ID DESC) as ROW_NUMBER__, * FROM (SELECT * FROM User) as InnerQuery ) as TMP_ WHERE TMP_.ROW_NUMBER__ > 100 AND TMP_.ROW_NUMBER__ <= 300"
|
||||||
resultSql, err = d.handleSelectSqlReplacement(inputSql)
|
resultSql, err = d.handleSelectSqlReplacement(inputSql)
|
||||||
t.AssertNil(err)
|
t.AssertNil(err)
|
||||||
t.Assert(resultSql, expectedSql)
|
t.Assert(resultSql, expectedSql)
|
||||||
@ -88,42 +87,42 @@ func TestDriver_handleSelectSqlReplacement(t *testing.T) {
|
|||||||
|
|
||||||
// LIMIT query with only rows
|
// LIMIT query with only rows
|
||||||
inputSql = "SELECT * FROM User LIMIT 50"
|
inputSql = "SELECT * FROM User LIMIT 50"
|
||||||
expectedSql = "SELECT * FROM User OFFSET 0 ROWS FETCH NEXT 50 ROWS ONLY"
|
expectedSql = "SELECT * FROM ( SELECT ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) as ROW_NUMBER__, * FROM (SELECT * FROM User) as InnerQuery ) as TMP_ WHERE TMP_.ROW_NUMBER__ > 0 AND TMP_.ROW_NUMBER__ <= 50"
|
||||||
resultSql, err = d.handleSelectSqlReplacement(inputSql)
|
resultSql, err = d.handleSelectSqlReplacement(inputSql)
|
||||||
t.AssertNil(err)
|
t.AssertNil(err)
|
||||||
t.Assert(resultSql, expectedSql)
|
t.Assert(resultSql, expectedSql)
|
||||||
|
|
||||||
// LIMIT query without ORDER BY
|
// LIMIT query without ORDER BY
|
||||||
inputSql = "SELECT * FROM User LIMIT 30"
|
inputSql = "SELECT * FROM User LIMIT 30"
|
||||||
expectedSql = "SELECT * FROM User OFFSET 0 ROWS FETCH NEXT 30 ROWS ONLY"
|
expectedSql = "SELECT * FROM ( SELECT ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) as ROW_NUMBER__, * FROM (SELECT * FROM User) as InnerQuery ) as TMP_ WHERE TMP_.ROW_NUMBER__ > 0 AND TMP_.ROW_NUMBER__ <= 30"
|
||||||
resultSql, err = d.handleSelectSqlReplacement(inputSql)
|
resultSql, err = d.handleSelectSqlReplacement(inputSql)
|
||||||
t.AssertNil(err)
|
t.AssertNil(err)
|
||||||
t.Assert(resultSql, expectedSql)
|
t.Assert(resultSql, expectedSql)
|
||||||
|
|
||||||
// Complex query with ORDER BY and LIMIT
|
// Complex query with ORDER BY and LIMIT
|
||||||
inputSql = "SELECT name, age FROM User WHERE age > 18 ORDER BY age ASC LIMIT 10, 5"
|
inputSql = "SELECT name, age FROM User WHERE age > 18 ORDER BY age ASC LIMIT 10, 5"
|
||||||
expectedSql = "SELECT name, age FROM User WHERE age > 18 ORDER BY age ASC OFFSET 10 ROWS FETCH NEXT 5 ROWS ONLY"
|
expectedSql = "SELECT * FROM ( SELECT ROW_NUMBER() OVER (ORDER BY age ASC) as ROW_NUMBER__, * FROM (SELECT name, age FROM User WHERE age > 18) as InnerQuery ) as TMP_ WHERE TMP_.ROW_NUMBER__ > 10 AND TMP_.ROW_NUMBER__ <= 15"
|
||||||
resultSql, err = d.handleSelectSqlReplacement(inputSql)
|
resultSql, err = d.handleSelectSqlReplacement(inputSql)
|
||||||
t.AssertNil(err)
|
t.AssertNil(err)
|
||||||
t.Assert(resultSql, expectedSql)
|
t.Assert(resultSql, expectedSql)
|
||||||
|
|
||||||
// Complex conditional queries have limits
|
// Complex conditional queries have limits
|
||||||
inputSql = "SELECT * FROM User WHERE age > 18 AND status = 'active' LIMIT 100, 50"
|
inputSql = "SELECT * FROM User WHERE age > 18 AND status = 'active' LIMIT 100, 50"
|
||||||
expectedSql = "SELECT * FROM User WHERE age > 18 AND status = 'active' OFFSET 100 ROWS FETCH NEXT 50 ROWS ONLY"
|
expectedSql = "SELECT * FROM ( SELECT ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) as ROW_NUMBER__, * FROM (SELECT * FROM User WHERE age > 18 AND status = 'active') as InnerQuery ) as TMP_ WHERE TMP_.ROW_NUMBER__ > 100 AND TMP_.ROW_NUMBER__ <= 150"
|
||||||
resultSql, err = d.handleSelectSqlReplacement(inputSql)
|
resultSql, err = d.handleSelectSqlReplacement(inputSql)
|
||||||
t.AssertNil(err)
|
t.AssertNil(err)
|
||||||
t.Assert(resultSql, expectedSql)
|
t.Assert(resultSql, expectedSql)
|
||||||
|
|
||||||
// A LIMIT query that contains subquery
|
// A LIMIT query that contains subquery
|
||||||
inputSql = "SELECT * FROM (SELECT * FROM User WHERE age > 18) AS subquery LIMIT 10"
|
inputSql = "SELECT * FROM (SELECT * FROM User WHERE age > 18) AS subquery LIMIT 10"
|
||||||
expectedSql = "SELECT * FROM (SELECT * FROM User WHERE age > 18) AS subquery OFFSET 0 ROWS FETCH NEXT 10 ROWS ONLY"
|
expectedSql = "SELECT * FROM ( SELECT ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) as ROW_NUMBER__, * FROM (SELECT * FROM (SELECT * FROM User WHERE age > 18) AS subquery) as InnerQuery ) as TMP_ WHERE TMP_.ROW_NUMBER__ > 0 AND TMP_.ROW_NUMBER__ <= 10"
|
||||||
resultSql, err = d.handleSelectSqlReplacement(inputSql)
|
resultSql, err = d.handleSelectSqlReplacement(inputSql)
|
||||||
t.AssertNil(err)
|
t.AssertNil(err)
|
||||||
t.Assert(resultSql, expectedSql)
|
t.Assert(resultSql, expectedSql)
|
||||||
|
|
||||||
// Queries with complex ORDER BY and LIMIT
|
// Queries with complex ORDER BY and LIMIT
|
||||||
inputSql = "SELECT name, age FROM User WHERE age > 18 ORDER BY age DESC, name ASC LIMIT 20, 10"
|
inputSql = "SELECT name, age FROM User WHERE age > 18 ORDER BY age DESC, name ASC LIMIT 20, 10"
|
||||||
expectedSql = "SELECT name, age FROM User WHERE age > 18 ORDER BY age DESC, name ASC OFFSET 20 ROWS FETCH NEXT 10 ROWS ONLY"
|
expectedSql = "SELECT * FROM ( SELECT ROW_NUMBER() OVER (ORDER BY age DESC, name ASC) as ROW_NUMBER__, * FROM (SELECT name, age FROM User WHERE age > 18) as InnerQuery ) as TMP_ WHERE TMP_.ROW_NUMBER__ > 20 AND TMP_.ROW_NUMBER__ <= 30"
|
||||||
resultSql, err = d.handleSelectSqlReplacement(inputSql)
|
resultSql, err = d.handleSelectSqlReplacement(inputSql)
|
||||||
t.AssertNil(err)
|
t.AssertNil(err)
|
||||||
t.Assert(resultSql, expectedSql)
|
t.Assert(resultSql, expectedSql)
|
||||||
|
@ -42,7 +42,7 @@ func TestTables(t *testing.T) {
|
|||||||
gtest.AssertEQ(find, true)
|
gtest.AssertEQ(find, true)
|
||||||
}
|
}
|
||||||
|
|
||||||
result, err = db.Tables(context.Background(), "test")
|
result, err = db.Tables(context.Background(), "master")
|
||||||
gtest.AssertNil(err)
|
gtest.AssertNil(err)
|
||||||
for i := 0; i < len(tables); i++ {
|
for i := 0; i < len(tables); i++ {
|
||||||
find := false
|
find := false
|
||||||
@ -88,7 +88,7 @@ func TestTableFields(t *testing.T) {
|
|||||||
gtest.AssertEQ(res[k].Comment, v[5])
|
gtest.AssertEQ(res[k].Comment, v[5])
|
||||||
}
|
}
|
||||||
|
|
||||||
res, err = db.TableFields(context.Background(), "t_user", "test")
|
res, err = db.TableFields(context.Background(), "t_user", "master")
|
||||||
gtest.AssertNil(err)
|
gtest.AssertNil(err)
|
||||||
|
|
||||||
for k, v := range expect {
|
for k, v := range expect {
|
||||||
|
@ -25,14 +25,9 @@ var (
|
|||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
TableSize = 10
|
TableSize = 10
|
||||||
TableName = "t_user"
|
TestDbUser = "sa"
|
||||||
TestSchema1 = "test1"
|
TestDbPass = "LoremIpsum86"
|
||||||
TestSchema2 = "test2"
|
|
||||||
TableNamePrefix1 = "gf_"
|
|
||||||
TestDbUser = "sa"
|
|
||||||
TestDbPass = "LoremIpsum86"
|
|
||||||
CreateTime = "2018-10-24 10:00:00"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
@ -41,7 +36,7 @@ func init() {
|
|||||||
Port: "1433",
|
Port: "1433",
|
||||||
User: TestDbUser,
|
User: TestDbUser,
|
||||||
Pass: TestDbPass,
|
Pass: TestDbPass,
|
||||||
Name: "test",
|
Name: "master",
|
||||||
Type: "mssql",
|
Type: "mssql",
|
||||||
Role: "master",
|
Role: "master",
|
||||||
Charset: "utf8",
|
Charset: "utf8",
|
||||||
@ -52,7 +47,7 @@ func init() {
|
|||||||
|
|
||||||
nodeLink := gdb.ConfigNode{
|
nodeLink := gdb.ConfigNode{
|
||||||
Type: "mssql",
|
Type: "mssql",
|
||||||
Name: "test",
|
Name: "master",
|
||||||
Link: fmt.Sprintf(
|
Link: fmt.Sprintf(
|
||||||
"mssql:%s:%s@tcp(%s:%s)/%s?encrypt=disable",
|
"mssql:%s:%s@tcp(%s:%s)/%s?encrypt=disable",
|
||||||
node.User, node.Pass, node.Host, node.Port, node.Name,
|
node.User, node.Pass, node.Host, node.Port, node.Name,
|
||||||
|
@ -51,7 +51,6 @@ func Test_Page(t *testing.T) {
|
|||||||
t.AssertNil(err)
|
t.AssertNil(err)
|
||||||
gtest.Assert(len(result), 3)
|
gtest.Assert(len(result), 3)
|
||||||
})
|
})
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func Test_Model_Insert(t *testing.T) {
|
func Test_Model_Insert(t *testing.T) {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user