WARNING: USE THIS SOFTWARE AT YOUR OWN RISK! THIS IS EXPERIMENTAL SOFTWARE NOT INTENDED FOR PRODUCTION USE! Zuble is currently an early stage prototype. As such Zuble is minimally tested and inherently unstable. It is provided for experimental, development, and demonstration purposes only. Zuble QML Types   |  Zuble C++ Classes   |  Zuble Overview
Zuble  0.1
Zuble Framework C++/QML extension API
ZsqlDatabase.cpp
Go to the documentation of this file.
1 /*
2  * Zuble - A run-time system for QML/Javascript applications
3  * Copyright (C) 2013, 2014 Bob Dinitto
4  *
5  * ZsqlDatabase.cpp
6  *
7  * Created on: 06-Aug, 2014
8  * Author: Bob Dinitto bob@ninzo.com
9  *
10  * Zuble is free software; you can redistribute it and/or
11  * modify it under the terms of the GNU Lesser General Public
12  * License as published by the Free Software Foundation; either
13  * version 2.1 of the License, or (at your option) any later version.
14  *
15  * This library is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18  * Lesser General Public License for more details.
19  *
20  * You should have received a copy of the GNU Lesser General Public
21  * License along with this library; if not, write to the Free Software
22  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
23  *
24  */
25 
26 #include "ZsqlDatabase.h"
27 #include "zglobal.h"
28 #include "ZsqlQuery.h"
29 #include "ZsqlRecord.h"
30 #include "ZsqlError.h"
31 #include <QQmlEngine>
32 
33 
34 
35 namespace Zbl
36 {
37 
38 QReadWriteLock ZsqlDatabase::m_lock(QReadWriteLock::Recursive);
39 
41 
42 QVariant ZsqlDatabase::m_tags;
43 
45  : QObject(parent), m_db(NULL)
46 {
47  qDebug("ZsqlDatabase::ZsqlDatabase");
48 
49  QQmlEngine::setObjectOwnership(this, QQmlEngine::CppOwnership);
50 
51  createTags();
52 
53 }
54 
56 {
57  release();
58 }
59 
61 {
62  if(!m_tags.isNull())
63  return;
64 
65  QVariantMap map;
66 
67  map.insert("tabletypeTables", Tables);
68  map.insert("tabletypeSystemTables", SystemTables);
69  map.insert("tabletypeViews", Views);
70  map.insert("tabletypeAllTables", AllTables);
71 
72  map.insert("numbersLowPrecisionInt32", LowPrecisionInt32);
73  map.insert("numbersLowPrecisionInt64", LowPrecisionInt64);
74  map.insert("numbersLowPrecisionDouble", LowPrecisionDouble);
75  map.insert("numbersHighPrecision", HighPrecision);
76 
77  m_tags = QVariant::fromValue(map);
78 }
79 
80 /*********************************
81 **********************************
82 ** Property Access Methods
83 **********************************
84 *********************************/
85 
86 bool ZsqlDatabase::isValid() const
87 {
89  validateDb();
90  ZBL_SLOT_END_RETURN(m_db->isValid(), false,
92 }
93 
94 bool ZsqlDatabase::isOpen() const
95 {
97  validateDb();
98  ZBL_SLOT_END_RETURN(m_db->isOpen(), false,
100 }
101 
102 bool ZsqlDatabase::isOpenError() const
103 {
105  validateDb();
106  ZBL_SLOT_END_RETURN(m_db->isOpenError(), false,
108 }
109 
111 {
113  validateDb();
114  ZBL_SLOT_END_RETURN(m_db->hostName(), QString(),
116 
117 void ZsqlDatabase::setHostname(const QString& hostname)
118 {
120  validateDb();
121  m_db->setHostName(hostname);
123  ZsqlDatabase::setHostName, setHostName failed)
124 }
125 
126 QString ZsqlDatabase::getDbName() const
127 {
129  validateDb();
130  ZBL_SLOT_END_RETURN(m_db->databaseName(), QString(),
132 
133 void ZsqlDatabase::setDbName(const QString& dbName)
134 {
136  validateDb();
137  m_db->setDatabaseName(dbName);
140 }
141 
143 {
145  validateDb();
146  ZBL_SLOT_END_RETURN(m_db->port(), false,
148 
150 {
152  validateDb();
153  m_db->setPort(portNumber);
156 }
157 
158 
159 void ZsqlDatabase::setConnectOptions(const QString& options)
160 {
162  validateDb();
163  m_db->setConnectOptions(options);
166 }
167 
169 {
170  return m_db->connectOptions();
172  validateDb();
173  ZBL_SLOT_END_RETURN(m_db->connectOptions(), QString(),
175 }
176 
178 {
179  ZsqlError* error = NULL;
180  bool ok = false;
182  validateDb();
183  error = new ZsqlError();
184  *error = m_db->lastError();
185  ok = true;
188 
189  if(!ok)
190  {
191  if(error)
192  error->deleteLater();
193 
194  return NULL;
195  }
196 
197  return error;
198 }
199 
200 
201 /*********************************
202 **********************************
203 ** Property Access Methods
204 **********************************
205 *********************************/
206 
207 bool ZsqlDatabase::open(const QString & user, const QString & password)
208 {
210  validateDb();
211  ZBL_SLOT_END_RETURN(m_db->open(user, password), false,
213 }
214 
216 {
218  validateDb();
219  m_db->close();
221 }
222 
224 {
225  if(!m_db)
226  return; // done!
227 
228  // release() must be thread-safe so a connection can be removed
229  // by any thread, not just the one that created it
230 
231  if(!inObjectThread(this))
232  {
233  // not in this object's thread so perform a recursive
234  // blocking invocation on the object's owner thread
235 
236  QMetaObject::invokeMethod(this, "release",
237  Qt::BlockingQueuedConnection
238  );
239  return;
240  }
241 
243 
244  // child QSqlQuery objects must be deleted before deleting QSqlDatabase
245  // objects or a SQL resource leak will occur and warning messages
246  // printed. calling QSqlQuery::close() is apparently not good enough.
247 
248  const QObjectList& childList(children());
249  const int childCount = childList.count();
250  for(int i=0; i < childCount; i++)
251  {
252  ZsqlQuery* query = qobject_cast<ZsqlQuery*>(childList.at(i));
253 
254  if(query)
255  query->release();
256  }
257 
258  m_db->close();
259  delete m_db;
260  m_db = NULL;
262 }
263 
264 
265 QStringList ZsqlDatabase::getTableNames(int tableType) const
266 {
268  validateDb();
269  switch(tableType)
270  {
271 
272  case Tables:
273  case SystemTables:
274  case Views:
275  case AllTables:
276 
277  return m_db->tables(QSql::TableType(tableType));
278  }
280 
281  return QStringList();
282  }
283 
285 {
286  return m_tags;
287 }
288 
290 {
291  ZsqlQuery* query = NULL;
292 
294  validateDb();
295 
296  validateThread();
297 
298  query = ZsqlQuery::newQuery(*this);
299 
300  //m_queryObjects.add(query);
301 
303 
304  if(zThreadErr.isError())
305  {
306  if(query)
307  query->deleteLater();
308  return NULL;
309  }
310  return query;
311 }
312 
313 #if 0
314 void ZsqlDatabase::removeQuery(QObject* query)
315 {
316 
318  validateDb();
319 
320  ZsqlQuery* q = qobject_cast<ZsqlQuery*>(query);
321 
322  if(!q)
323  throw ZblException("Specified query object isn't a ZsqlQuery object.")
324 
325  validateThread();
326 
327  query = ZsqlQuery::newQuery(*this);
328 
329  //m_queryObjects.add(query);
330 
331  ZBL_SLOT_END_VOID(Z_FAC_JS, ZsqlDatabase::removeQuery, removeQuery failed)
332 
333  if(zThreadErr.isError())
334  {
335  if(query)
336  query->deleteLater();
337  return NULL;
338  }
339  return query;
340 }
341 #endif
342 
343 
344 QObject* ZsqlDatabase::addDatabase(const QString& driverType,
345  const QString& connectionName )
346 {
347  QWriteLocker lock(&m_lock);
348  ZsqlDatabase* db = NULL;
349 
351 
352  // "default" connections are bad practice - they break
353  // modularity so we don't allow them
354 
355  if(connectionName.isEmpty())
356  throw ZblException("Invalid connection name");
357 
358  if(m_databaseObjects.contains(connectionName))
359  {
360  qDebug("ZsqlDatabase::addDatabase - "
361  "Warning: replacing existing database connection: %s",
362  cStr(connectionName));
363 
364  qDebug("ZsqlDatabase::addDatabase - "
365  "Warning: current references on connection: %s will dangle",
366  cStr(connectionName));
367 
368  removeDatabase(connectionName); // which thread created it?
369  }
370 
371  db = new ZsqlDatabase(NULL);
372 
373  db->m_db = new QSqlDatabase(
374  QSqlDatabase::addDatabase(driverType, connectionName));
375 
376  m_databaseObjects.insert(connectionName, db);
377 
379 
380  if(zThreadErr.isError())
381  {
382  if(db)
383  db->deleteLater();
384  return NULL;
385  }
386  return db;
387 }
388 
389 void ZsqlDatabase::removeDatabase(const QString& connectionName)
390 {
391 
393 
394  if(connectionName.isEmpty())
395  throw ZblException("Invalid connection name");
396 
397  QWriteLocker lock(&m_lock);
398 
399  if(m_databaseObjects.contains(connectionName))
400  {
401  ZsqlDatabase* db = m_databaseObjects.value(connectionName);
402 
403  m_databaseObjects.remove(connectionName);
404 
405  db->release();
406 
407  QSqlDatabase::removeDatabase(connectionName);
408 
409  db->deleteLater();
410  }
412 }
413 
414 
415 QObject* ZsqlDatabase::database(const QString& connectionName )
416 {
417  ZsqlDatabase* db = NULL;
418 
420 
421  if(connectionName.isEmpty())
422  throw ZblException("Invalid connection name");
423 
424  QReadLocker lock(&m_lock);
425 
426  if(m_databaseObjects.contains(connectionName))
427  {
428  db = m_databaseObjects.value(connectionName);
429 
430  if(!inObjectThread(db))
431  throw ZblException(
432  "Database can only be accessed by same thread that created it.");
433  }
434 
435  ZBL_SLOT_END_RETURN(db, NULL,
437 }
438 
440 {
441  const QThread* currentThread = QThread::currentThread();
442 
444 
445  QWriteLocker lock(&m_lock);
446 
447  ZsqlDbMap::iterator i = m_databaseObjects.begin();
448 
449  while(i != m_databaseObjects.end())
450  {
451  ZsqlDatabase* db = *i;
452 
453  if(currentThread == db->thread())
454  {
455  db->release();
456 
457  db->deleteLater();
458 
459  QSqlDatabase::removeDatabase(i.key());
460 
461  i = m_databaseObjects.erase(i);
462  }
463  else
464  {
465  i++;
466  }
467  }
468 
470  removeThreadDatabases failed)
471 
472 }
473 
474 
475 
476 
478 {
480  validateDb();
481  ZBL_SLOT_END_RETURN(m_db->transaction(), false,
483 }
484 
486 {
488  validateDb();
489  ZBL_SLOT_END_RETURN(m_db->commit(), false,
491 }
492 
494 {
496  validateDb();
497  ZBL_SLOT_END_RETURN(m_db->rollback(), false,
499 }
500 
501 QObject* ZsqlDatabase::record(const QString& tablename)
502 {
503 
504  ZsqlRecord* r = NULL;
505 
507  validateDb();
508  r = new ZsqlRecord();
509  r->m_r = m_db->record(tablename);
511 
512  if(zThreadErr.isError())
513  {
514  if(r)
515  {
516  r->deleteLater();
517  r = NULL;
518  }
519  }
520 
521  return r;
522 }
523 
524 
525 
526 } // Zbl
bool isValid() const
static QVariant m_tags
A QVariantMap used to pass ZsqlDatabase enumeration values to Javascript programs.
Definition: ZsqlDatabase.h:412
Q_INVOKABLE bool commit()
Commits a database transaction if supported by the database driver and a transaction() has been start...
QString getDbName() const
void validateDb() const
Checks that the embedded QSqlDatabase object exists and throws a ZblException if not.
Definition: ZsqlDatabase.h:423
static QObject * database(const QString &connectionName)
Obtains a SQL database from the list of database connections. The named connection must have previous...
void setDbName(const QString &hostname)
Q_INVOKABLE void close()
Closes the database connection, freeing associated resources. ZsqlQuery objects created by this datab...
#define Z_FAC_JS
Definition: zglobal.h:123
static QObject * addDatabase(const QString &driverType, const QString &connectionName)
Adds a SQL database to the list of database connections.
Q_INVOKABLE bool rollback()
Rolls back a database transaction if supported by the database driver and a transaction() has been st...
static void removeDatabase(const QString &connectionName)
Removes a SQL database from the list of database connections.
ZsqlDatabase(QObject *parent=0)
QSqlRecord m_r
Encapsulated QSqlRecord object.
Definition: ZsqlRecord.h:107
A javascript wrapper class for QSqlDatabase objects. This object represents a database connection...
Definition: ZsqlDatabase.h:71
QString getConnectOptions()
void setConnectOptions(const QString &options)
Definition: ZAndGate.cpp:6
A javascript wrapper class for QSqlQuery objects. This object represents a query on a database connec...
Definition: ZsqlQuery.h:67
Javascript wrapper for a QSqlRecord object.
Definition: ZsqlRecord.h:39
#define ZBL_SLOT_BEGIN_TRY
Definition: zglobal.h:128
Q_INVOKABLE void release()
Releases references from this object to wrapped Qt C++ objects. The C++ objects may be deleted by thi...
Q_INVOKABLE QObject * record(const QString &tablename)
Creates a new ZsqlRecord object for the specified table.
static void removeThreadDatabases()
Removes all SQL databases that were created by the current thread.
QObject * getLastError()
#define zThreadErr
where does this show up?
Definition: ZblThreadErr.h:39
Q_INVOKABLE bool open(const QString &user, const QString &password)
Opens a database connection using the given user name and password.
QString getHostname() const
void validateThread()
Checks to see if this object was created in the current thread and throws a ZblException if not...
Definition: ZsqlDatabase.h:416
QMap< QString, ZsqlDatabase * > ZsqlDbMap
Definition: ZsqlDatabase.h:80
#define ZBL_SLOT_END_VOID(facility, code, error_message)
Definition: zglobal.h:134
static ZsqlQuery * newQuery(ZsqlDatabase &database)
Constructs an empty ZsqlQuery object for the specified database connection.
Definition: ZsqlQuery.cpp:149
int getPortNumber() const
static ZsqlDbMap m_databaseObjects
A map of SQL database connection wrapper objects.
Definition: ZsqlDatabase.h:398
void createTags()
Create the m_tag object that presents a Javascript interface to ZsqlDatabase enumeration values...
#define cStr(qStr)
Definition: zglobal.h:49
bool inObjectThread(const QObject &object)
Definition: zglobal.h:173
static QReadWriteLock m_lock
A lock for multi-threaded data access to m_databaseObjects.
Definition: ZsqlDatabase.h:403
void setPortNumber(int)
Zuble&#39;s Qt Exception Object.
Definition: ZblException.h:45
void setHostname(const QString &hostname)
QSqlDatabase * m_db
An encapsulated SQL database object.
Definition: ZsqlDatabase.h:387
bool isOpenError() const
Q_INVOKABLE QObject * newQuery()
Creates a new ZsqlQuery object for this database connection.
int portNumber
Number of the port to which the database connection is made.
Definition: ZsqlDatabase.h:138
Q_INVOKABLE bool transaction()
Begins a database transaction if supported by the database driver.
Q_INVOKABLE QObject * record()
Obtains a ZsqlRecord object representing the query result.
Definition: ZsqlQuery.cpp:630
Q_INVOKABLE QStringList getTableNames(int tableType) const
Obtain names of tables in the database.
bool isOpen() const
virtual ~ZsqlDatabase()
Q_INVOKABLE void release()
Releases references from this object to wrapped Qt C++ objects.
Definition: ZsqlQuery.cpp:194
#define ZBL_SLOT_END_RETURN(return_success, return_failed, facility, code, error_message)
Definition: zglobal.h:141
A Javascript wrapper for QSqlError objects. (database sprocket)
Definition: ZsqlError.h:40