publish v1.5.2

This commit is contained in:
AI Engineer 2026-06-05 08:39:33 +08:00
parent 74b9bfe511
commit 41e2bac5e2
8 changed files with 100 additions and 12 deletions

22
debug/debug_sync.go Normal file
View File

@ -0,0 +1,22 @@
package main
import (
"fmt"
"apigo.cc/go/db"
)
func main() {
dbPath := "debug.db"
dbInst := db.GetDB("sqlite://"+dbPath, nil)
schema := `
== System ==
_Table SD
id c10 PK
name v64 U
`
fmt.Println("First sync...")
dbInst.Sync(schema)
fmt.Println("\nSecond sync...")
dbInst.Sync(schema)
}

3
debug/go.mod Normal file
View File

@ -0,0 +1,3 @@
module debug
go 1.26.1

8
go.mod
View File

@ -4,12 +4,12 @@ go 1.25.0
require ( require (
apigo.cc/go/cast v1.5.0 apigo.cc/go/cast v1.5.0
apigo.cc/go/config v1.5.0 apigo.cc/go/config v1.5.1
apigo.cc/go/crypto v1.5.0 apigo.cc/go/crypto v1.5.0
apigo.cc/go/file v1.5.0 apigo.cc/go/file v1.5.0
apigo.cc/go/id v1.5.0 apigo.cc/go/id v1.5.0
apigo.cc/go/jsmod v1.5.0 apigo.cc/go/jsmod v1.5.0
apigo.cc/go/log v1.5.0 apigo.cc/go/log v1.5.4
apigo.cc/go/redis v1.5.0 apigo.cc/go/redis v1.5.0
apigo.cc/go/safe v1.5.0 apigo.cc/go/safe v1.5.0
apigo.cc/go/shell v1.5.0 apigo.cc/go/shell v1.5.0
@ -32,9 +32,9 @@ require (
github.com/mattn/go-isatty v0.0.20 // indirect github.com/mattn/go-isatty v0.0.20 // indirect
github.com/ncruces/go-strftime v1.0.0 // indirect github.com/ncruces/go-strftime v1.0.0 // indirect
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
golang.org/x/crypto v0.51.0 // indirect golang.org/x/crypto v0.52.0 // indirect
golang.org/x/sync v0.20.0 // indirect golang.org/x/sync v0.20.0 // indirect
golang.org/x/sys v0.44.0 // indirect golang.org/x/sys v0.45.0 // indirect
golang.org/x/text v0.37.0 // indirect golang.org/x/text v0.37.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect
modernc.org/libc v1.72.0 // indirect modernc.org/libc v1.72.0 // indirect

16
go.sum
View File

@ -1,7 +1,7 @@
apigo.cc/go/cast v1.5.0 h1:UBGJtFQ8eJPMQXs37cUgqd7YQo1zI9opuSDBDmn2/pE= apigo.cc/go/cast v1.5.0 h1:UBGJtFQ8eJPMQXs37cUgqd7YQo1zI9opuSDBDmn2/pE=
apigo.cc/go/cast v1.5.0/go.mod h1:z2GW5p5WCZGEqVVIJUdhl232vRbLf2Qu4EDlEakX/D8= apigo.cc/go/cast v1.5.0/go.mod h1:z2GW5p5WCZGEqVVIJUdhl232vRbLf2Qu4EDlEakX/D8=
apigo.cc/go/config v1.5.0 h1:Yuz9QEb11XXG4XkhDi/ueT2M1T3Q9PElE5tiakvjehs= apigo.cc/go/config v1.5.1 h1:rpj7oCzlsDV3f2/YK3Pb+CHbfr2DL5Vyyv6VNkobJP4=
apigo.cc/go/config v1.5.0/go.mod h1:jdMiDLPa9gzB8/FFZvm9jOopUqdxb7XSX+0OeWcZZUM= apigo.cc/go/config v1.5.1/go.mod h1:jdMiDLPa9gzB8/FFZvm9jOopUqdxb7XSX+0OeWcZZUM=
apigo.cc/go/crypto v1.5.0 h1:Nxz7a6VKCdvaF258IU0NkjQyureOLxfR308Sy2iftUI= apigo.cc/go/crypto v1.5.0 h1:Nxz7a6VKCdvaF258IU0NkjQyureOLxfR308Sy2iftUI=
apigo.cc/go/crypto v1.5.0/go.mod h1:F9M6nXv+5328r1ZwbTvI6fcr8VdgqHVzALOcsdv6ntE= apigo.cc/go/crypto v1.5.0/go.mod h1:F9M6nXv+5328r1ZwbTvI6fcr8VdgqHVzALOcsdv6ntE=
apigo.cc/go/encoding v1.5.0 h1:EJNdRVDOMoI2DAvZwQNQTbYuqB/6zsEzvg7lS5pQI+I= apigo.cc/go/encoding v1.5.0 h1:EJNdRVDOMoI2DAvZwQNQTbYuqB/6zsEzvg7lS5pQI+I=
@ -12,8 +12,8 @@ apigo.cc/go/id v1.5.0 h1:MjNWPhBhDsoXaLeJDv/0wfJmVMU9EvOs8pWYfsTQ6e8=
apigo.cc/go/id v1.5.0/go.mod h1:qhu4a1/KLc/XcBpcsRu+mXZt7U7Wvd9zMcPs4VspuPA= apigo.cc/go/id v1.5.0/go.mod h1:qhu4a1/KLc/XcBpcsRu+mXZt7U7Wvd9zMcPs4VspuPA=
apigo.cc/go/jsmod v1.5.0 h1:JgQtJNiJWy1NOP9AzE8NX5VXJkpO/x3GqLsCCSny5Ec= apigo.cc/go/jsmod v1.5.0 h1:JgQtJNiJWy1NOP9AzE8NX5VXJkpO/x3GqLsCCSny5Ec=
apigo.cc/go/jsmod v1.5.0/go.mod h1:bmyeZtOAP/j5am+YRnaiM89smysK24K7ebk0koFtsSw= apigo.cc/go/jsmod v1.5.0/go.mod h1:bmyeZtOAP/j5am+YRnaiM89smysK24K7ebk0koFtsSw=
apigo.cc/go/log v1.5.0 h1:kQuLLtbt33mEuc/xJVcy8NODXkso/QKSZWNclKrSpsI= apigo.cc/go/log v1.5.4 h1:LNyU4v09gfcnZOY53ctnXoKzo45FHoEcPR33lk6PBaY=
apigo.cc/go/log v1.5.0/go.mod h1:Djy+I5aLhGB/EjwRz4KHqkVEz584IAD55FAFiIfInuo= apigo.cc/go/log v1.5.4/go.mod h1:Djy+I5aLhGB/EjwRz4KHqkVEz584IAD55FAFiIfInuo=
apigo.cc/go/rand v1.5.0 h1:1o8hh8fhdBuk1/h02IvugvamuT3dkWbVJrqEJVQKB2E= apigo.cc/go/rand v1.5.0 h1:1o8hh8fhdBuk1/h02IvugvamuT3dkWbVJrqEJVQKB2E=
apigo.cc/go/rand v1.5.0/go.mod h1:Lh98S2dm9UY0X+M+kNQQEKyXHG5pcCKSFPyXN0QCGdk= apigo.cc/go/rand v1.5.0/go.mod h1:Lh98S2dm9UY0X+M+kNQQEKyXHG5pcCKSFPyXN0QCGdk=
apigo.cc/go/redis v1.5.0 h1:VXNDqzKj87BchF7ubDEH+T6lp8NrjeK0izU4ooo7u1A= apigo.cc/go/redis v1.5.0 h1:VXNDqzKj87BchF7ubDEH+T6lp8NrjeK0izU4ooo7u1A=
@ -68,15 +68,15 @@ github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UV
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
golang.org/x/crypto v0.51.0 h1:IBPXwPfKxY7cWQZ38ZCIRPI50YLeevDLlLnyC5wRGTI= golang.org/x/crypto v0.52.0 h1:RMs7fP2rXdep0CftQlK8Uf+kibLm7qkCcradZWYz988=
golang.org/x/crypto v0.51.0/go.mod h1:8AdwkbraGNABw2kOX6YFPs3WM22XqI4EXEd8g+x7Oc8= golang.org/x/crypto v0.52.0/go.mod h1:1QgfPxDqh0T2M/elOJtp9RvuR95kVjir0e6/BvEmGbc=
golang.org/x/mod v0.35.0 h1:Ww1D637e6Pg+Zb2KrWfHQUnH2dQRLBQyAtpr/haaJeM= golang.org/x/mod v0.35.0 h1:Ww1D637e6Pg+Zb2KrWfHQUnH2dQRLBQyAtpr/haaJeM=
golang.org/x/mod v0.35.0/go.mod h1:+GwiRhIInF8wPm+4AoT6L0FA1QWAad3OMdTRx4tFYlU= golang.org/x/mod v0.35.0/go.mod h1:+GwiRhIInF8wPm+4AoT6L0FA1QWAad3OMdTRx4tFYlU=
golang.org/x/sync v0.20.0 h1:e0PTpb7pjO8GAtTs2dQ6jYa5BWYlMuX047Dco/pItO4= golang.org/x/sync v0.20.0 h1:e0PTpb7pjO8GAtTs2dQ6jYa5BWYlMuX047Dco/pItO4=
golang.org/x/sync v0.20.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0= golang.org/x/sync v0.20.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.44.0 h1:ildZl3J4uzeKP07r2F++Op7E9B29JRUy+a27EibtBTQ= golang.org/x/sys v0.45.0 h1:dO4czNzziLiiXplLQgBCEpCvXQ3dnkn0SdaZSYdQ+FY=
golang.org/x/sys v0.44.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= golang.org/x/sys v0.45.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
golang.org/x/text v0.37.0 h1:Cqjiwd9eSg8e0QAkyCaQTNHFIIzWtidPahFWR83rTrc= golang.org/x/text v0.37.0 h1:Cqjiwd9eSg8e0QAkyCaQTNHFIIzWtidPahFWR83rTrc=
golang.org/x/text v0.37.0/go.mod h1:a5sjxXGs9hsn/AJVwuElvCAo9v8QYLzvavO5z2PiM38= golang.org/x/text v0.37.0/go.mod h1:a5sjxXGs9hsn/AJVwuElvCAo9v8QYLzvavO5z2PiM38=
golang.org/x/tools v0.44.0 h1:UP4ajHPIcuMjT1GqzDWRlalUEoY+uzoZKnhOjbIPD2c= golang.org/x/tools v0.44.0 h1:UP4ajHPIcuMjT1GqzDWRlalUEoY+uzoZKnhOjbIPD2c=

5
test_idx.sh Normal file
View File

@ -0,0 +1,5 @@
rm -f test_idx.db
sqlite3 test_idx.db "CREATE TABLE test (name TEXT);"
sqlite3 test_idx.db "CREATE INDEX idx_name ON test(name);"
sqlite3 test_idx.db "CREATE UNIQUE INDEX IF NOT EXISTS idx_name ON test(name);"
echo "Result code: $?"

6
test_unique.sh Normal file
View File

@ -0,0 +1,6 @@
rm -f test_unique.db
sqlite3 test_unique.db "CREATE TABLE test (name TEXT);"
sqlite3 test_unique.db "INSERT INTO test (name) VALUES ('a');"
sqlite3 test_unique.db "INSERT INTO test (name) VALUES ('b');"
sqlite3 test_unique.db "CREATE UNIQUE INDEX uk_test_name ON test(name);"
echo "Result code: $?"

6
test_unique_dup.sh Normal file
View File

@ -0,0 +1,6 @@
rm -f test_unique.db
sqlite3 test_unique.db "CREATE TABLE test (name TEXT);"
sqlite3 test_unique.db "INSERT INTO test (name) VALUES ('a');"
sqlite3 test_unique.db "INSERT INTO test (name) VALUES ('a');"
sqlite3 test_unique.db "CREATE UNIQUE INDEX uk_test_name ON test(name);"
echo "Result code: $?"

46
unique_race_test.go Normal file
View File

@ -0,0 +1,46 @@
package db
import (
"fmt"
"os"
"testing"
)
func TestCheckTable_DuplicateUnique(t *testing.T) {
dbPath := "test_unique_race.db"
_ = os.Remove(dbPath)
defer os.Remove(dbPath)
dbInst := GetDB("sqlite://"+dbPath, nil)
schema := `
== System ==
_Table SD
id c10 PK
name v64 U
`
// First sync
err := dbInst.Sync(schema)
if err != nil {
t.Fatalf("First sync failed: %v", err)
}
// Insert duplicates to ensure recreation fails
dbInst.Exec("INSERT INTO _Table (id, name) VALUES ('1', 'dup')")
dbInst.Exec("INSERT INTO _Table (id, name) VALUES ('2', 'dup')") // Will fail if unique constraint works, but wait, the unique index is already there, so we can't insert duplicates!
// Wait, we CAN'T insert duplicates because the first sync created the index.
// But in the user's log, there ARE duplicates? Or maybe there are NO duplicates, but the engine just complains when it recreates the index on existing data?
// Ah, if there are NO duplicates, CREATE UNIQUE INDEX will SUCCEED.
// So why did the user get UNIQUE constraint failed?
// BECAUSE THERE WERE DUPLICATES!
// Why would there be duplicates in _Table?
// Let's just do the second sync and see if it tries to execute CREATE UNIQUE INDEX.
// Print statements will be inside Schema.go for a moment.
// Second sync (simulate restart)
err = dbInst.Sync(schema)
if err != nil {
t.Fatalf("Second sync failed: %v", err)
}
}