@@ -2,6 +2,7 @@ package mssqlstore
2
2
3
3
import (
4
4
"database/sql"
5
+ "fmt"
5
6
"log"
6
7
"time"
7
8
)
@@ -10,31 +11,59 @@ import (
10
11
type MSSQLStore struct {
11
12
db * sql.DB
12
13
stopCleanup chan bool
14
+ tableName string
15
+ }
16
+
17
+ type Config struct {
18
+ // CleanUpInterval is the interval between each cleanup operation.
19
+ // If set to 0, the cleanup operation is disabled.
20
+ CleanUpInterval time.Duration
21
+
22
+ // TableName is the name of the table where the session data will be stored.
23
+ // If not set, it will default to "sessions".
24
+ TableName string
13
25
}
14
26
15
27
// New returns a new MSSQLStore instance, with a background cleanup goroutine
16
28
// that runs every 5 minutes to remove expired session data.
17
29
func New (db * sql.DB ) * MSSQLStore {
18
- return NewWithCleanupInterval (db , 5 * time .Minute )
30
+ return NewWithConfig (db , Config {
31
+ CleanUpInterval : 5 * time .Minute ,
32
+ })
19
33
}
20
34
21
35
// NewWithCleanupInterval returns a new MSSQLStore instance. The cleanupInterval
22
36
// parameter controls how frequently expired session data is removed by the
23
37
// background cleanup goroutine. Setting it to 0 prevents the cleanup goroutine
24
38
// from running (i.e. expired sessions will not be removed).
25
39
func NewWithCleanupInterval (db * sql.DB , cleanupInterval time.Duration ) * MSSQLStore {
26
- m := & MSSQLStore {db : db }
27
- if cleanupInterval > 0 {
28
- go m .startCleanup (cleanupInterval )
40
+ return NewWithConfig (db , Config {
41
+ CleanUpInterval : cleanupInterval ,
42
+ })
43
+ }
44
+
45
+ // NewWithConfig returns a new MSSQLStore instance with the given configuration.
46
+ // If the TableName field is empty, it will be set to "sessions".
47
+ // If the CleanUpInterval field is 0, the cleanup goroutine will not be started.
48
+ func NewWithConfig (db * sql.DB , config Config ) * MSSQLStore {
49
+ if config .TableName == "" {
50
+ config .TableName = "sessions"
51
+ }
52
+
53
+ m := & MSSQLStore {db : db , tableName : config .TableName }
54
+ if config .CleanUpInterval > 0 {
55
+ go m .startCleanup (config .CleanUpInterval )
29
56
}
57
+
30
58
return m
31
59
}
32
60
33
61
// Find returns the data for a given session token from the MSSQLStore instance.
34
62
// If the session token is not found or is expired, the returned exists flag will
35
63
// be set to false.
36
64
func (m * MSSQLStore ) Find (token string ) (b []byte , exists bool , err error ) {
37
- row := m .db .QueryRow ("SELECT data FROM sessions WHERE token = @p1 AND GETUTCDATE() < expiry" , token )
65
+ query := fmt .Sprintf ("SELECT data FROM %s WHERE token = @p1 AND GETUTCDATE() < expiry" , m .tableName )
66
+ row := m .db .QueryRow (query , token )
38
67
err = row .Scan (& b )
39
68
if err == sql .ErrNoRows {
40
69
return nil , false , nil
@@ -48,26 +77,26 @@ func (m *MSSQLStore) Find(token string) (b []byte, exists bool, err error) {
48
77
// given expiry time. If the session token already exists, then the data and expiry
49
78
// time are updated.
50
79
func (m * MSSQLStore ) Commit (token string , b []byte , expiry time.Time ) error {
51
- _ , err := m .db .Exec (`MERGE INTO sessions WITH (HOLDLOCK) AS T USING (VALUES(@p1)) AS S (token) ON (T.token = S.token)
52
- WHEN MATCHED THEN UPDATE SET data = @p2, expiry = @p3
53
- WHEN NOT MATCHED THEN INSERT (token, data, expiry) VALUES(@p1, @p2, @p3);` , token , b , expiry .UTC ())
54
- if err != nil {
55
- return err
56
- }
57
- return nil
80
+ query := fmt .Sprintf (`MERGE INTO %s WITH (HOLDLOCK) AS T USING (VALUES(@p1)) AS S (token) ON (T.token = S.token)
81
+ WHEN MATCHED THEN UPDATE SET data = @p2, expiry = @p3
82
+ WHEN NOT MATCHED THEN INSERT (token, data, expiry) VALUES(@p1, @p2, @p3);` , m .tableName )
83
+ _ , err := m .db .Exec (query , token , b , expiry .UTC ())
84
+ return err
58
85
}
59
86
60
87
// Delete removes a session token and corresponding data from the MSSQLStore
61
88
// instance.
62
89
func (m * MSSQLStore ) Delete (token string ) error {
63
- _ , err := m .db .Exec ("DELETE FROM sessions WHERE token = @p1" , token )
90
+ query := fmt .Sprintf ("DELETE FROM %s WHERE token = @p1" , m .tableName )
91
+ _ , err := m .db .Exec (query , token )
64
92
return err
65
93
}
66
94
67
95
// All returns a map containing the token and data for all active (i.e.
68
96
// not expired) sessions in the MSSQLStore instance.
69
97
func (m * MSSQLStore ) All () (map [string ][]byte , error ) {
70
- rows , err := m .db .Query ("SELECT token, data FROM sessions WHERE GETUTCDATE() < expiry" )
98
+ query := fmt .Sprintf ("SELECT token, data FROM %s WHERE GETUTCDATE() < expiry" , m .tableName )
99
+ rows , err := m .db .Query (query )
71
100
if err != nil {
72
101
return nil , err
73
102
}
@@ -115,22 +144,15 @@ func (m *MSSQLStore) startCleanup(interval time.Duration) {
115
144
}
116
145
117
146
// StopCleanup terminates the background cleanup goroutine for the MSSQLStore
118
- // instance. It's rare to terminate this; generally MSSQLStore instances and
119
- // their cleanup goroutines are intended to be long-lived and run for the lifetime
120
- // of your application.
121
- //
122
- // There may be occasions though when your use of the MSSQLStore is transient.
123
- // An example is creating a new MSSQLStore instance in a test function. In this
124
- // scenario, the cleanup goroutine (which will run forever) will prevent the
125
- // MSSQLStore object from being garbage collected even after the test function
126
- // has finished. You can prevent this by manually calling StopCleanup.
147
+ // instance.
127
148
func (m * MSSQLStore ) StopCleanup () {
128
149
if m .stopCleanup != nil {
129
150
m .stopCleanup <- true
130
151
}
131
152
}
132
153
133
154
func (m * MSSQLStore ) deleteExpired () error {
134
- _ , err := m .db .Exec ("DELETE FROM sessions WHERE expiry < GETUTCDATE()" )
155
+ query := fmt .Sprintf ("DELETE FROM %s WHERE expiry < GETUTCDATE()" , m .tableName )
156
+ _ , err := m .db .Exec (query )
135
157
return err
136
158
}
0 commit comments