@@ -10,20 +10,32 @@ import (
10
10
"github.com/jmoiron/sqlx"
11
11
)
12
12
13
- //DeploySchema deploys the database schema by running the list of DeployQueries defined
14
- //on a database config. This will create the database if needed. Typically this is used
15
- //to deploy an empty, or near empty, database. A database connection must not already
16
- //be established; this func will establish the connection the leave it open for further
17
- //use.
13
+ //DeploySchemaOptions provides options when deploying a schema.
14
+ type DeploySchemaOptions struct {
15
+ SkipInsert bool
16
+ CloseConnection bool
17
+ }
18
+
19
+ //DeploySchemaWithOps deploys the database schema by running the list of DeployQueries
20
+ //and DeployFuncs defined in config. This will create the database if needed. This is
21
+ //typically used to deploy an empty, or near empty, database. A database connection
22
+ //must not already be established; this func will establish the connection.
18
23
//
19
- //Typically this func would be called when your app is passed a flag, such as --deploy-db,
20
- //so that your database is only deployed when needed, not as part of every start of
21
- //your app.
24
+ //Although each DeployQuery and DeployFunc should be indempotent (ex.: using CREATE
25
+ //TABLE IF NOT EXISTS), you should still not call this func each time your app starts
26
+ //or otherwise. Typically you would check if the database already exists or use a
27
+ //flag, such as --deploy-db, to run this func.
22
28
//
23
- //The dontInsert parameter is used prevent any DeployQueries with "INSERT INTO" statements
24
- //from running. This is used to deploy a completely empty database.
25
- func (c * Config ) DeploySchema (dontInsert bool ) (err error ) {
26
- //Make sure the connection isn't already established to prevent overwriting anything.
29
+ //skipInsert is used prevent any DeployQueries with "INSERT INTO" statements from
30
+ //running. This is used to deploy a completely empty database and is useful for
31
+ //migrating data or backups.
32
+ //
33
+ //closeConnection determines if the database connection should be closed after this
34
+ //func successfully completes. This was added to support SQLite in-memory databases
35
+ //since each connection to an im-memory db uses a new database, so if we deploy with
36
+ //a connection we need to reuse it to run queries.
37
+ func (c * Config ) DeploySchemaWithOps (ops DeploySchemaOptions ) (err error ) {
38
+ //Make sure a connection isn't already established to prevent overwriting anything.
27
39
//This forces users to call Close() first to prevent any incorrect db usage.
28
40
if c .Connected () {
29
41
return ErrConnected
@@ -55,9 +67,9 @@ func (c *Config) DeploySchema(dontInsert bool) (err error) {
55
67
}
56
68
defer conn .Close ()
57
69
58
- //Handle any database-type specific stuff .
59
- //For non-sqlite dbs , we need to create the actual database on the server.
60
- //For sqlite dbs , we need to Ping() the connection so the db file is created on disk.
70
+ //Create the database.
71
+ //For mariadb/mysql , we need to create the actual database on the server.
72
+ //For SQLite , we need to Ping() the connection so the file is created on disk.
61
73
switch c .Type {
62
74
case DBTypeMySQL , DBTypeMariaDB :
63
75
q := `CREATE DATABASE IF NOT EXISTS ` + c .Name
@@ -75,95 +87,105 @@ func (c *Config) DeploySchema(dontInsert bool) (err error) {
75
87
76
88
//Reconnect to the database since the previously used connection didn't include
77
89
//the database name in the connection string. This will connect us to the specific
78
- //database, not the database server. This connects using Connect(), the same func
79
- //that would be used to connect to the db for normal usage.
80
- //
81
- //This is not necessary for SQLite since SQLite always connects to the filepath
82
- //that was provided, not a server.
83
- if ! c .IsSQLite () {
84
- err = conn .Close ()
85
- if err != nil {
86
- return
87
- }
90
+ //database, not just the database server. This connects using Connect(), the same
91
+ //func that would be used to connect to the db for normal usage.
92
+ err = conn .Close ()
93
+ if err != nil {
94
+ return
95
+ }
88
96
89
- //Note, no `defer Close()` since we want to leave the connection to the db
90
- //open upon successfully deploying the db so that db can be used without
91
- //calling `Connect()` after this func.
92
- err = c . Connect ()
93
- if err != nil {
94
- return
95
- }
97
+ err = c . Connect ()
98
+ if err != nil {
99
+ return
100
+ }
101
+
102
+ if ops . CloseConnection {
103
+ defer c . Close ()
96
104
}
97
105
98
106
//Run each deploy query.
99
- c .log ("sqldb.DeploySchema (DeployQueries)..." )
107
+ c .debugPrintln ("sqldb.DeploySchema (DeployQueries)..." )
108
+ connection := c .Connection ()
100
109
for _ , q := range c .DeployQueries {
101
110
//Translate the query if needed. This will only translate queries with
102
111
//CREATE TABLE in the text.
103
112
q = c .translateCreateTable (q )
104
113
105
- //skip queries that insert data if needed. This will skip any query with
106
- //INSERT INTO in the text.
107
- if strings .Contains (strings .ToUpper (q ), "INSERT INTO" ) && dontInsert {
114
+ //Skip queries that insert data if needed.
115
+ if strings .Contains (strings .ToUpper (q ), "INSERT INTO" ) && ops .SkipInsert {
108
116
continue
109
117
}
110
118
111
- if c .Debug {
112
- if strings .Contains (q , "CREATE TABLE" ) {
113
- idx := strings .Index (q , "(" )
114
- log .Println (strings .TrimSpace (q [:idx ]) + "..." )
115
- } else {
116
- log .Println (q )
117
- }
118
-
119
+ //Log out some info about the query being run for diagnostics.
120
+ if strings .Contains (q , "CREATE TABLE" ) {
121
+ idx := strings .Index (q , "(" )
122
+ c .debugPrintln (strings .TrimSpace (q [:idx ]) + "..." )
123
+ } else {
124
+ c .debugPrintln (q )
119
125
}
120
126
121
- //Execute the query.
122
- //Logging on error so users can identify query in question.
123
- connection := c .Connection ()
127
+ //Execute the query. Always log on error so users can identify query that has
128
+ //an error.
124
129
_ , innerErr := connection .Exec (q )
125
130
if innerErr != nil {
126
131
err = innerErr
127
132
log .Println ("sqldb.DeploySchema() error with query" , q )
133
+ c .Close ()
128
134
return
129
135
}
130
136
}
131
- if c .Debug {
132
- log .Println ("sqldb.DeploySchema (DeployQueries)...done" )
133
- }
137
+ c .debugPrintln ("sqldb.DeploySchema (DeployQueries)...done" )
134
138
135
139
//Run each deploy func.
136
- if c .Debug {
137
- log .Println ("sqldb.DeploySchema (DeployFuncs)..." )
138
- }
140
+ c .debugPrintln ("sqldb.DeploySchema (DeployFuncs)..." )
139
141
for _ , f := range c .DeployFuncs {
140
- //get function name for diagnostics
142
+ //Get function name for diagnostics.
141
143
rawNameWithPath := runtime .FuncForPC (reflect .ValueOf (f ).Pointer ()).Name ()
142
144
funcName := path .Base (rawNameWithPath )
143
145
144
- if c .Debug {
145
- log .Println (funcName )
146
- }
146
+ //Log out some infor about the func being run for diagnostics.
147
+ c .debugPrintln (funcName )
147
148
149
+ //Execute the func. Always log on error so users can identify query that has
150
+ //an error.
148
151
innerErr := f ()
149
152
if innerErr != nil {
150
- log .Println ("Error with deploy func" , funcName )
153
+ log .Println ("sqldb.DeploySchema() error with deploy func" , funcName )
154
+ c .Close ()
151
155
return innerErr
152
156
}
153
157
154
158
}
155
- if c .Debug {
156
- log .Println ("sqldb.DeploySchema (DeployFuncs)...done" )
157
- }
159
+ c .debugPrintln ("sqldb.DeploySchema (DeployFuncs)...done" )
158
160
159
- //Not closing the connection upon success since user may want to start interacting
160
- //with the db right away and this removes the need to call Connect() right after
161
- //this func.
161
+ if ops .CloseConnection {
162
+ //close is handled by defer above.
163
+ c .debugPrintln ("Connection closed upon successful deploy." )
164
+ } else {
165
+ c .debugPrintln ("Connection left open after successful deploy." )
166
+ }
162
167
163
168
return
164
169
}
165
170
166
- //DeploySchema deploys the database for the default package level config.
167
- func DeploySchema (dontInsert bool ) (err error ) {
168
- return config .DeploySchema (dontInsert )
171
+ //DeploySchemaWithOps deploys the database for the default package level config.
172
+ func DeploySchemaWithOps (ops DeploySchemaOptions ) (err error ) {
173
+ return config .DeploySchemaWithOps (ops )
174
+ }
175
+
176
+ //DeploySchema runs DeploySchemaWithOps with some defaults set. This was implemented
177
+ //to support legacy compatibility while expanding the feature set with deploy options.
178
+ func (c * Config ) DeploySchema (skipInsert bool ) (err error ) {
179
+ ops := DeploySchemaOptions {
180
+ SkipInsert : skipInsert ,
181
+ CloseConnection : true , //legacy
182
+ }
183
+ return c .DeploySchemaWithOps (ops )
184
+ }
185
+
186
+ //DeploySchema runs DeploySchemaWithOps with some defaults set for the default package
187
+ //level config. This was implemented to support legacy compatibility while expanding
188
+ //the feature set with deploy options.
189
+ func DeploySchema (skipInsert bool ) (err error ) {
190
+ return config .DeploySchema (skipInsert )
169
191
}
0 commit comments