Skip to content

Commit db80775

Browse files
committed
init: commit init
0 parents  commit db80775

File tree

14 files changed

+1157
-0
lines changed

14 files changed

+1157
-0
lines changed

.gitignore

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
my.cnf
2+
init.sql
3+
logs/*
4+
example/*
5+
.DS_Store
6+
docker-compose.yml

GetData.go

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
package golangMysqlPool
2+
3+
import (
4+
"database/sql"
5+
"fmt"
6+
"strings"
7+
)
8+
9+
func (q *QueryBuilder) Get() (*sql.Rows, error) {
10+
if q.TableName == nil {
11+
q.Logger.Action(true, "Table is required")
12+
return nil, fmt.Errorf("Table is required")
13+
}
14+
15+
fieldNames := make([]string, len(q.SelectList))
16+
for i, field := range q.SelectList {
17+
switch {
18+
case field == "*":
19+
fieldNames[i] = "*"
20+
case strings.ContainsAny(field, ".()"):
21+
fieldNames[i] = field
22+
default:
23+
fieldNames[i] = fmt.Sprintf("`%s`", field)
24+
}
25+
}
26+
27+
query := fmt.Sprintf("SELECT %s FROM `%s`", strings.Join(fieldNames, ", "), *q.TableName)
28+
29+
if len(q.JoinList) > 0 {
30+
query += " " + strings.Join(q.JoinList, " ")
31+
}
32+
33+
if len(q.WhereList) > 0 {
34+
query += " WHERE " + strings.Join(q.WhereList, " AND ")
35+
}
36+
37+
if q.WithTotal {
38+
query = fmt.Sprintf("SELECT COUNT(*) OVER() AS total, data.* FROM (%s) AS data", query)
39+
}
40+
41+
if len(q.OrderList) > 0 {
42+
query += " ORDER BY " + strings.Join(q.OrderList, ", ")
43+
}
44+
45+
if q.QueryLimit != nil {
46+
query += fmt.Sprintf(" LIMIT %d", *q.QueryLimit)
47+
}
48+
49+
if q.QueryOffset != nil {
50+
query += fmt.Sprintf(" OFFSET %d", *q.QueryOffset)
51+
}
52+
53+
return q.query(query, q.BindingList...)
54+
}

InsertData.go

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
package golangMysqlPool
2+
3+
import (
4+
"fmt"
5+
"strings"
6+
)
7+
8+
func (q *QueryBuilder) Insert(data map[string]interface{}) (int64, error) {
9+
if q.TableName == nil {
10+
q.Logger.Action(true, "Table is required")
11+
return 0, fmt.Errorf("Table is required")
12+
}
13+
14+
columns := make([]string, 0, len(data))
15+
values := make([]interface{}, 0, len(data))
16+
placeholders := make([]string, 0, len(data))
17+
18+
for column, value := range data {
19+
columns = append(columns, fmt.Sprintf("`%s`", column))
20+
values = append(values, value)
21+
placeholders = append(placeholders, "?")
22+
}
23+
24+
query := fmt.Sprintf("INSERT INTO `%s` (%s) VALUES (%s)",
25+
*q.TableName,
26+
strings.Join(columns, ", "),
27+
strings.Join(placeholders, ", "),
28+
)
29+
30+
result, err := q.exec(query, values...)
31+
if err != nil {
32+
return 0, err
33+
}
34+
35+
return result.LastInsertId()
36+
}

LICENSE

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
MIT License
2+
3+
Copyright (c) 2025 邱敬幃 Pardn Chiu
4+
5+
Permission is hereby granted, free of charge, to any person obtaining a copy
6+
of this software and associated documentation files (the "Software"), to deal
7+
in the Software without restriction, including without limitation the rights
8+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
copies of the Software, and to permit persons to whom the Software is
10+
furnished to do so, subject to the following conditions:
11+
12+
The above copyright notice and this permission notice shall be included in all
13+
copies or substantial portions of the Software.
14+
15+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21+
SOFTWARE.

Logger.go

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
package golangMysqlPool
2+
3+
import (
4+
"fmt"
5+
"io"
6+
"log"
7+
"os"
8+
"path/filepath"
9+
)
10+
11+
// * private method
12+
func newLogger(path string) (*Logger, error) {
13+
if err := os.MkdirAll(path, 0755); err != nil {
14+
return nil, fmt.Errorf("failed to create log directory: %v", err)
15+
}
16+
17+
initLog, initErr := openLog(path, "init.log")
18+
actionLog, createErr := openLog(path, "action.log")
19+
20+
if initErr != nil || createErr != nil {
21+
return nil, fmt.Errorf("failed to open log files: init=%v, action=%v",
22+
initErr, actionLog)
23+
}
24+
25+
return &Logger{
26+
InitLogger: log.New(io.MultiWriter(initLog, os.Stdout), "", log.LstdFlags),
27+
ActionLogger: log.New(io.MultiWriter(actionLog, os.Stdout), "", log.LstdFlags),
28+
Path: path,
29+
}, nil
30+
}
31+
32+
func openLog(path string, target string) (*os.File, error) {
33+
file, err := os.OpenFile(filepath.Join(path, target), os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666)
34+
if err != nil {
35+
return nil, fmt.Errorf("failed to open %s: %v", target, err)
36+
}
37+
return file, nil
38+
}
39+
40+
func writeToLog(target *log.Logger, isError bool, message ...string) {
41+
if len(message) == 0 {
42+
return
43+
}
44+
45+
state := ""
46+
if isError {
47+
state = "[ERROR] "
48+
}
49+
for i, msg := range message {
50+
if i == 0 {
51+
target.Printf("%s%s", state, message[i])
52+
} else if i == len(message)-1 {
53+
target.Printf("└── %s", msg)
54+
} else {
55+
target.Printf("├── %s", msg)
56+
}
57+
}
58+
}
59+
60+
func (l *Logger) Init(isError bool, message ...string) {
61+
writeToLog(l.InitLogger, isError, message...)
62+
}
63+
64+
func (l *Logger) Action(isError bool, message ...string) {
65+
writeToLog(l.ActionLogger, isError, message...)
66+
}

NewInstance.go

Lines changed: 157 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,157 @@
1+
package golangMysqlPool
2+
3+
import (
4+
"database/sql"
5+
"fmt"
6+
"os"
7+
"os/signal"
8+
"syscall"
9+
"time"
10+
)
11+
12+
func New(c *ConfigList) (*PoolList, error) {
13+
if c == nil {
14+
return nil, fmt.Errorf("Config is required")
15+
}
16+
17+
if c.LogPath == "" {
18+
c.LogPath = "./logs/golangMysqlPool"
19+
}
20+
21+
logger, err := newLogger(c.LogPath)
22+
if err != nil {
23+
return nil, fmt.Errorf("Failed to init logger: %v", err)
24+
}
25+
26+
var pool = &PoolList{
27+
Read: nil,
28+
Write: nil,
29+
Logger: logger,
30+
}
31+
32+
readConfig := c.Read
33+
34+
if readConfig.Host == "" {
35+
readConfig.Host = "localhost"
36+
}
37+
38+
if readConfig.Port == 0 {
39+
readConfig.Port = 3306
40+
}
41+
42+
if readConfig.User == "" {
43+
readConfig.User = "root"
44+
}
45+
46+
if readConfig.Password == "" {
47+
readConfig.Password = ""
48+
}
49+
50+
if readConfig.Charset == "" {
51+
readConfig.Charset = "utf8mb4"
52+
}
53+
54+
if readConfig.Connection == 0 {
55+
readConfig.Connection = 4
56+
}
57+
58+
read, err := sql.Open("mysql",
59+
fmt.Sprintf(
60+
"%s:%s@tcp(%s:%d)/?charset=%s&parseTime=true",
61+
readConfig.User,
62+
readConfig.Password,
63+
readConfig.Host,
64+
readConfig.Port,
65+
readConfig.Charset,
66+
),
67+
)
68+
if err != nil {
69+
logger.Init(true, "Failed to create read pool", err.Error())
70+
return nil, fmt.Errorf("Failed to create read pool: %w", err)
71+
}
72+
73+
read.SetMaxOpenConns(readConfig.Connection)
74+
read.SetMaxIdleConns(readConfig.Connection / 2)
75+
read.SetConnMaxLifetime(time.Hour)
76+
77+
if err := read.Ping(); err != nil {
78+
logger.Init(true, "Failed to connect read pool", err.Error())
79+
return nil, fmt.Errorf("Failed to connect read pool: %w", err)
80+
}
81+
82+
pool.Read = &Pool{db: read}
83+
84+
writeConfig := c.Write
85+
if writeConfig == nil {
86+
writeConfig = readConfig
87+
}
88+
89+
writeDB, err := sql.Open(
90+
"mysql",
91+
fmt.Sprintf(
92+
"%s:%s@tcp(%s:%d)/?charset=%s&parseTime=true",
93+
writeConfig.User,
94+
writeConfig.Password,
95+
writeConfig.Host,
96+
writeConfig.Port,
97+
writeConfig.Charset,
98+
),
99+
)
100+
if err != nil {
101+
logger.Init(true, "Failed to create write pool", err.Error())
102+
return nil, fmt.Errorf("Failed to create write pool: %w", err)
103+
}
104+
105+
writeDB.SetMaxOpenConns(writeConfig.Connection)
106+
writeDB.SetMaxIdleConns(writeConfig.Connection / 2)
107+
writeDB.SetConnMaxLifetime(time.Hour)
108+
109+
if err := writeDB.Ping(); err != nil {
110+
logger.Init(true, "Failed to connect write pool", err.Error())
111+
return nil, fmt.Errorf("Failed to connect write pool: %w", err)
112+
}
113+
114+
pool.Write = &Pool{db: writeDB}
115+
116+
pool.listenShutdownSignal()
117+
pool.Write.Logger = logger
118+
pool.Read.Logger = logger
119+
return pool, nil
120+
}
121+
122+
func (p *PoolList) Close() error {
123+
var readErr, writeErr error
124+
125+
if p.Read != nil && p.Read.db != nil {
126+
readErr = p.Read.db.Close()
127+
p.Read = nil
128+
}
129+
130+
if p.Write != nil && p.Write.db != nil {
131+
writeErr = p.Write.db.Close()
132+
p.Write = nil
133+
}
134+
135+
if readErr != nil {
136+
p.Write.Logger.Action(true, "Failed to close read pool", readErr.Error())
137+
return fmt.Errorf("Failed to close read pool: %w", readErr)
138+
}
139+
if writeErr != nil {
140+
p.Write.Logger.Action(true, "Failed to close write pool", writeErr.Error())
141+
return fmt.Errorf("Failed to close write pool: %w", writeErr)
142+
}
143+
144+
return nil
145+
}
146+
147+
// * private method
148+
func (p *PoolList) listenShutdownSignal() {
149+
c := make(chan os.Signal, 1)
150+
signal.Notify(c, os.Interrupt, syscall.SIGTERM)
151+
152+
go func() {
153+
<-c
154+
_ = p.Close()
155+
os.Exit(0)
156+
}()
157+
}

0 commit comments

Comments
 (0)