EasyQtSql
Easy SQL data access helper for QtSql
EasyQtSql_SqlFactory.h
Go to the documentation of this file.
1 #ifndef EASYQTSQL_SQLFACTORY_H
2 #define EASYQTSQL_SQLFACTORY_H
3 
4 /*
5  * The MIT License (MIT)
6  * Copyright 2018 Alexey Kramin
7  *
8  * Permission is hereby granted, free of charge, to any person obtaining
9  * a copy of this software and associated documentation files (the
10  * "Software"), to deal in the Software without restriction, including
11  * without limitation the rights to use, copy, modify, merge, publish,
12  * distribute, sublicense, and/or sell copies of the Software, and to
13  * permit persons to whom the Software is furnished to do so, subject to
14  * the following conditions:
15  *
16  * The above copyright notice and this permission notice shall be
17  * included in all copies or substantial portions of the Software.
18  *
19  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
20  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
21  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
22  * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
23  * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
24  * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
25  * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
26  *
27 */
28 
29 #ifndef EASY_QT_SQL_MAIN
30 
31 #include <QtSql>
32 #include <QException>
33 
34 #endif
35 
37 {
38 public:
39 
41  {
42  static SqlFactory instance;
43  return &instance;
44  }
45 
46  class ThreadDBPool;
47 
48  struct DBSetting
49  {
50  friend class ThreadDBPool;
51 
52  typedef QPair<QString, QString> KeywordValuePair;
53  typedef QVector<KeywordValuePair> ConnParameters;
54 
56  { }
57 
58  DBSetting(const QString &type, const QString &connectionString)
59  : type(type), dbName(connectionString)
60  { }
61 
62  DBSetting(const QString &type, const ConnParameters &params)
63  : DBSetting(type, createConnectionString(params))
64  { }
65 
66  DBSetting(const QString &type, const QString &host, int port, const QString &username, const QString &password, const QString &dbname)
67  : type(type), port(port), host(host), username(username), password(password), dbName(dbname)
68  { }
69 
71  {
72  return DBSetting("QSQLITE", ":memory:");
73  }
74 
75  static DBSetting sqlite(const QString &fileName)
76  {
77  return DBSetting("QSQLITE", fileName);
78  }
79 
80  //https://docs.microsoft.com/ru-ru/dotnet/framework/data/adonet/connection-strings
81  //https://www.connectionstrings.com/sql-server/
82 
83  //MS SQL Server 2000 / 7.0 Standard Security
84  static DBSetting msSqlServerODBC(const QString &server, const QString &database, const QString &userID, const QString &password, const ConnParameters &extraParams = ConnParameters())
85  {
86  return DBSetting("QODBC",
87  {
88  {"Driver", "{SQL Server}"}
89  , {"Server", server}
90  , {"Database", database}
91  , {"Uid", userID}
92  , {"Pwd", password}
93  });
94  }
95 
96  //MS SQL Server 2000 / 7.0 Trusted connection
97  static DBSetting msSqlServerODBC(const QString &server, const QString &database, const ConnParameters &extraParams = ConnParameters())
98  {
99  return DBSetting("QODBC",
100  {
101  {"Driver", "{SQL Server}"}
102  , {"Server", server}
103  , {"Database", database}
104  , {"Trusted_Connection", "Yes"}
105  });
106  }
107 
108  //MS SQL Server 2000 / 2005 / 7.0 SQL Native Client 9.0 Standard Security
109  static DBSetting msSqlServerNativeClient90ODBC(const QString &server, const QString &database, const QString &userID, const QString &password, const ConnParameters &extraParams = ConnParameters())
110  {
111  return DBSetting("QODBC",
112  {
113  {"Driver", "{SQL Native Client}"}
114  , {"Server", server}
115  , {"Database", database}
116  , {"Uid", userID}
117  , {"Pwd", password}
118  });
119  }
120 
121  //MS SQL Server 2000 / 2005 / 7.0 SQL Native Client 9.0 Trusted connection
122  static DBSetting msSqlServerNativeClient90ODBC(const QString &server, const QString &database, const ConnParameters &extraParams = ConnParameters())
123  {
124  return DBSetting("QODBC",
125  {
126  {"Driver", "{SQL Native Client}"}
127  , {"Server", server}
128  , {"Database", database}
129  , {"Trusted_Connection", "Yes"}
130  });
131  }
132 
133 
134 
135  QString getType() const
136  {
137  return type.isEmpty() ? "QODBC" : type;
138  }
139 
140  private:
141  QString type;
142  QVariant port;
143  QString host;
144  QString username;
145  QString password;
146  QString dbName;
147 
148  static QString createConnectionString(const ConnParameters &params)
149  {
150  QString res;
151  res.reserve(256);
152 
153  for (const KeywordValuePair &pair: params)
154  {
155  const QString &key = pair.first;
156  const QString &value = pair.second;
157 
158  res.append(key).append('=');
159 
160  auto containsControlChar = [](const QString &value) -> bool
161  {
162  for (int i = 0; i < value.size(); ++i)
163  {
164  if (value[i].category() == QChar::Other_Control)
165  return true;
166  }
167 
168  return false;
169  };
170 
171  if ( value.startsWith(QChar::Space) || value.endsWith(QChar::Space) || value.contains(';') || containsControlChar(value) )
172  {
173  const bool containsSingleQuotes = value.contains(QChar('\''));
174  const bool containsDoubleQuotes = value.contains(QChar('\"'));
175 
176  if (containsSingleQuotes && containsDoubleQuotes)
177  {
178  res.append(';');
179  continue;
180  }
181  else if (containsSingleQuotes)
182  {
183  res.append('\"').append(value).append('\"');
184  }
185  else if (containsDoubleQuotes)
186  {
187  res.append('\'').append(value).append('\'');
188  }
189  }
190  else
191  {
192  res.append(value);
193  }
194 
195  res.append(';');
196  }
197 
198  return res;
199  }
200  };
201 
203  {
204  public:
205 
207  {
208  for (const QString &uniqueConnName : m_connNameMap.values())
209  {
210  {
211  QSqlDatabase db = QSqlDatabase::database(uniqueConnName);
212  if (db.isOpen())
213  {
214  db.close();
215  }
216  }
217 
218  QSqlDatabase::removeDatabase(uniqueConnName);
219  }
220  }
221 
222  void addConnection(const DBSetting &settings, const QString &connectionName)
223  {
224  const QString uniqueConnectionName = connectionName + QUuid::createUuid().toString();
225 
226  m_connNameMap.insert(connectionName, uniqueConnectionName);
227 
228  QSqlDatabase db = QSqlDatabase::addDatabase(settings.getType(), uniqueConnectionName);
229 
230  db.setDatabaseName(settings.dbName);
231  db.setHostName(settings.host);
232  db.setUserName(settings.username);
233  db.setPassword(settings.password);
234 
235  if (settings.port.isValid())
236  {
237  db.setPort(settings.port.toInt());
238  }
239  }
240 
241  bool connectionExists(const QString &connectionName) const
242  {
243  return m_connNameMap.contains(connectionName);
244  }
245 
246  QSqlDatabase getDatabase(const QString &connectionName) const
247  {
248  const QString &uniqueConnectionName = m_connNameMap.value(connectionName);
249 
250  QSqlDatabase db = QSqlDatabase::database(uniqueConnectionName);
251 
252  if (!db.isOpen())
253  {
254  if (!db.open())
255  {
256  qCritical() << db.lastError().text();
257  }
258  }
259 
260  return db;
261  }
262 
263  private:
264  QMap<QString, QString> m_connNameMap;
265  };
266 
267  SqlFactory *config(const DBSetting &settings, const QString &connectionName = QSqlDatabase::defaultConnection)
268  {
269  QMutexLocker locker(&mutex);
270 
271  m_settings.insert(connectionName, settings);
272 
273  return this;
274  }
275 
276  SqlFactory *clone(const QString &newConnectionName, const QString &connectionName)
277  {
278  QMutexLocker locker(&mutex);
279 
280  if (newConnectionName == connectionName)
281  return this;
282 
283  if (m_settings.contains(connectionName))
284  {
285  const DBSetting &setting = m_settings[connectionName];
286  m_settings.insert(newConnectionName, setting);
287  }
288 
289  return this;
290  }
291 
292  QSqlDatabase getDatabase(const QString &connectionName = QSqlDatabase::defaultConnection)
293  {
294  QMutexLocker locker(&mutex);
295 
296  if (m_settings.contains(connectionName))
297  {
298  ThreadDBPool *threadDbPool = nullptr;
299 
300  if (!m_dbPool.hasLocalData())
301  {
302  threadDbPool = new ThreadDBPool(); //new pool for current thread
303 
304  m_dbPool.setLocalData(threadDbPool);
305  }
306  else
307  {
308  threadDbPool = m_dbPool.localData();
309  }
310 
311  if (threadDbPool)
312  {
313  if (!threadDbPool->connectionExists(connectionName)) //if no connection created for current thread
314  {
315  const DBSetting &settings = m_settings[connectionName];
316 
317  threadDbPool->addConnection(settings, connectionName); //create new connection with specified settings for current thread
318  }
319 
320  return threadDbPool->getDatabase(connectionName);
321  }
322  }
323 
324  return QSqlDatabase();
325  }
326 
327 private:
328  QMutex mutex;
329 
330  QMap<QString, DBSetting> m_settings;
331  QThreadStorage<ThreadDBPool*> m_dbPool;
332 
333  QString m_defaultConnName;
334 
335 private:
336  SqlFactory()
337  {}
338 };
339 
340 
341 #endif // EASYQTSQL_SQLFACTORY_H
static DBSetting msSqlServerODBC(const QString &server, const QString &database, const QString &userID, const QString &password, const ConnParameters &extraParams=ConnParameters())
Definition: EasyQtSql_SqlFactory.h:84
Definition: EasyQtSql_SqlFactory.h:202
SqlFactory * config(const DBSetting &settings, const QString &connectionName=QSqlDatabase::defaultConnection)
Definition: EasyQtSql_SqlFactory.h:267
static DBSetting msSqlServerNativeClient90ODBC(const QString &server, const QString &database, const QString &userID, const QString &password, const ConnParameters &extraParams=ConnParameters())
Definition: EasyQtSql_SqlFactory.h:109
void addConnection(const DBSetting &settings, const QString &connectionName)
Definition: EasyQtSql_SqlFactory.h:222
DBSetting(const QString &type, const QString &connectionString)
Definition: EasyQtSql_SqlFactory.h:58
static DBSetting msSqlServerODBC(const QString &server, const QString &database, const ConnParameters &extraParams=ConnParameters())
Definition: EasyQtSql_SqlFactory.h:97
static DBSetting msSqlServerNativeClient90ODBC(const QString &server, const QString &database, const ConnParameters &extraParams=ConnParameters())
Definition: EasyQtSql_SqlFactory.h:122
~ThreadDBPool()
Definition: EasyQtSql_SqlFactory.h:206
static DBSetting sqlite(const QString &fileName)
Definition: EasyQtSql_SqlFactory.h:75
static SqlFactory * getInstance()
Definition: EasyQtSql_SqlFactory.h:40
DBSetting()
Definition: EasyQtSql_SqlFactory.h:55
QSqlDatabase getDatabase(const QString &connectionName) const
Definition: EasyQtSql_SqlFactory.h:246
QSqlDatabase getDatabase(const QString &connectionName=QSqlDatabase::defaultConnection)
Definition: EasyQtSql_SqlFactory.h:292
DBSetting(const QString &type, const ConnParameters &params)
Definition: EasyQtSql_SqlFactory.h:62
bool connectionExists(const QString &connectionName) const
Definition: EasyQtSql_SqlFactory.h:241
static DBSetting sqliteMemory()
Definition: EasyQtSql_SqlFactory.h:70
SqlFactory * clone(const QString &newConnectionName, const QString &connectionName)
Definition: EasyQtSql_SqlFactory.h:276
Definition: EasyQtSql_SqlFactory.h:36
QVector< KeywordValuePair > ConnParameters
Definition: EasyQtSql_SqlFactory.h:53
DBSetting(const QString &type, const QString &host, int port, const QString &username, const QString &password, const QString &dbname)
Definition: EasyQtSql_SqlFactory.h:66
QString getType() const
Definition: EasyQtSql_SqlFactory.h:135
QPair< QString, QString > KeywordValuePair
Definition: EasyQtSql_SqlFactory.h:52
Definition: EasyQtSql_SqlFactory.h:48