1 /*******************************************************************
2 
3 Part of the Fritzing project - http://fritzing.org
4 Copyright (c) 2007-2014 Fachhochschule Potsdam - http://fh-potsdam.de
5 
6 Fritzing is free software: you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation, either version 3 of the License, or
9 (at your option) any later version.
10 
11 Fritzing is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 GNU General Public License for more details.floadfrom
15 
16 You should have received a copy of the GNU General Public License
17 along with Fritzing.  If not, see <http://www.gnu.org/licenses/>.
18 
19 ********************************************************************
20 
21 $Revision: 6956 $:
22 $Author: irascibl@gmail.com $:
23 $Date: 2013-04-07 12:14:50 +0200 (So, 07. Apr 2013) $
24 
25 ********************************************************************/
26 
27 // copying a table from one SQWL database to another http://sqlite.phxsoftware.com/forums/t/285.aspx
28 
29 #include <QSqlRecord>
30 #include <QSqlError>
31 #include <QMessageBox>
32 #include <QVector>
33 #include <QSqlResult>
34 #include <limits>
35 
36 #include "sqlitereferencemodel.h"
37 #include "../debugdialog.h"
38 #include "../connectors/svgidlayer.h"
39 #include "../connectors/connector.h"
40 #include "../connectors/connectorshared.h"
41 #include "../connectors/busshared.h"
42 #include "../utils/folderutils.h"
43 #include "../utils/fmessagebox.h"
44 
45 
46 #define MAX_CONN_TRIES 3
47 
48 static const qulonglong NO_ID = std::numeric_limits<qulonglong>::max();
49 
debugError(bool result,QSqlQuery & query)50 void debugError(bool result, QSqlQuery & query) {
51     if (result) return;
52 
53     QSqlError error = query.lastError();
54     DebugDialog::debug(QString("%1 %2 %3").arg(error.text()).arg(error.number()).arg(error.type()));
55 }
56 
57 static ModelPart * DebugModelPart = NULL;
58 
debugExec(const QString & msg,QSqlQuery & query)59 void debugExec(const QString & msg, QSqlQuery & query) {
60     DebugDialog::debug(
61 			"SQLITE: " + msg + "\n"
62 			"\t "+ query.lastQuery() + "\n"
63 			"\t ERROR DRIVER: "+ query.lastError().driverText() + "\n"
64 			"\t ERROR DB: " + query.lastError().databaseText() + "\n"
65             "\t moduleid:" + (DebugModelPart == NULL ? "" : DebugModelPart->moduleID()) + ""
66 		);
67     QMap<QString, QVariant> map = query.boundValues();
68     foreach (QString name, map.keys()) {
69         DebugDialog::debug(QString("\t%1:%2").arg(name).arg(map.value(name).toString()));
70     }
71 }
72 
killConnectors(QVector<Connector * > & connectors)73 void killConnectors(QVector<Connector *> & connectors) {
74     foreach (Connector * connector, connectors) {
75         delete connector->connectorShared();
76         delete connector;
77     }
78     connectors.clear();
79 }
80 
killBuses(QVector<BusShared * > & buses)81 void killBuses(QVector<BusShared *> & buses) {
82     foreach (BusShared * bus, buses) {
83         delete bus;
84     }
85     buses.clear();
86 }
87 
88 QStringList FailurePartMessages;
89 QStringList FailurePropertyMessages;
90 
noSwappingMessage()91 void noSwappingMessage()
92 {
93 	FMessageBox::warning(NULL,
94 			QObject::tr("Oops!"),
95 			QObject::tr("Sorry, we have a problem with the swapping mechanism.\nFritzing still works, but you won't be able to change parts properties."),
96 			QMessageBox::Ok);
97 }
98 
99 ///////////////////////////////////////////////////
100 
SqliteReferenceModel()101 SqliteReferenceModel::SqliteReferenceModel() {
102 	m_swappingEnabled = false;
103 	m_lastWasExactMatch = true;
104 }
105 
loadAll(const QString & databaseName,bool fullLoad,bool dbExists)106 bool SqliteReferenceModel::loadAll(const QString & databaseName, bool fullLoad, bool dbExists)
107 {
108     FailurePartMessages.clear();
109     FailurePropertyMessages.clear();
110     m_fullLoad = fullLoad;
111 	initParts(dbExists);
112 
113 	int tries = 0;
114     m_keepGoing = true;
115 	while(!m_swappingEnabled && tries < MAX_CONN_TRIES && m_keepGoing) {
116 		createConnection(databaseName, fullLoad);
117 		if(!m_swappingEnabled) {
118 			deleteConnection();
119 		}
120 		tries++;
121 	}
122 	/* TODO Mariano: perhaps we should check that there are no parts with
123 	 * the same family and providing exactly the same properties set
124 	 */
125 
126 	if (!m_swappingEnabled) {
127         noSwappingMessage();
128 	}
129     else if (FailurePartMessages.count() > 0) {
130         QString message = tr("The swapping mechanism is disabled for:\n\n");
131         if (FailurePartMessages.count() == 1) {
132             message += FailurePartMessages.at(0);
133         }
134         else if (FailurePartMessages.count() < 6) {
135             message += FailurePartMessages.join("\n");
136         }
137         else {
138             for (int i = 0; i < 4; i++) {
139                 message += FailurePartMessages.at(i);
140                 message += ("\n");
141             }
142             message += "\n" + tr("and %1 other parts").arg(FailurePartMessages.count() - 4);
143         }
144 	    FMessageBox::warning(NULL, QObject::tr("Oops!"), message, QMessageBox::Ok);
145     }
146     else if (FailurePropertyMessages.count() > 0) {
147         QString message = tr("The swapping mechanism is disabled for:\n\n");
148         if (FailurePropertyMessages.count() == 1) {
149             message += FailurePropertyMessages.at(0);
150         }
151         else if (FailurePropertyMessages.count() < 6) {
152             message += FailurePropertyMessages.join("\n");
153         }
154         else {
155             for (int i = 0; i < 4; i++) {
156                 message += FailurePropertyMessages.at(i);
157                 message += ("\n");
158             }
159             message += "\n" + tr("and %1 other properties").arg(FailurePropertyMessages.count() - 4);
160         }
161 	    FMessageBox::warning(NULL, QObject::tr("Oops!"), message, QMessageBox::Ok);
162     }
163     return m_swappingEnabled;
164 
165 }
166 
loadFromDB(const QString & databaseName)167 bool SqliteReferenceModel::loadFromDB(const QString & databaseName)
168 {
169     QSqlDatabase db = QSqlDatabase::addDatabase("QSQLITE", "temporary");
170 	db.setDatabaseName(databaseName);
171 
172     m_swappingEnabled = loadFromDB(m_database, db);
173     if (db.isOpen()) db.close();
174     if (!m_swappingEnabled) {
175         killParts();
176         noSwappingMessage();
177     }
178 
179     return m_swappingEnabled;
180 }
181 
loadFromDB(QSqlDatabase & keep_db,QSqlDatabase & db)182 bool SqliteReferenceModel::loadFromDB(QSqlDatabase & keep_db, QSqlDatabase & db)
183 {
184     bool opened = false;
185     for (int i = 0; i < MAX_CONN_TRIES; i++) {
186 	    if (!db.open()) continue;
187 
188         opened = true;
189         break;
190     }
191 
192     if (!opened) {
193         return false;
194     }
195 
196     QSqlQuery query = db.exec("SELECT COUNT(*) FROM parts");
197     debugError(query.isActive(), query);
198     if (!query.isActive() || !query.next()) return false;
199 
200     int count = query.value(0).toInt();
201     if (count == 0) {
202         return false;
203     }
204 
205     DebugDialog::debug(QString("parts count %1").arg(count));
206 
207 
208     QVector<ModelPart *> parts(count + 1, NULL);
209     QVector<qulonglong > oldToNew(count + 1, 0);
210 
211     query = db.exec("SELECT path, moduleID, id, family, version, replacedby, fritzingversion, author, title, label, date, description, spice, spicemodel, taxonomy, itemtype FROM parts");
212     debugError(query.isActive(), query);
213     if (!query.isActive()) return false;
214 
215     QSqlQuery q2(keep_db);
216     bool result = q2.prepare("INSERT INTO parts(moduleID, family, core) VALUES (:moduleID, :family, :core)");
217     debugError(result, q2);
218 
219     QFileInfo info(db.databaseName());
220     QDir partsDir = info.absoluteDir();  // parts folder
221 
222     while (query.next()) {
223         int ix = 0;
224 
225         QString path = query.value(ix++).toString();
226         QString moduleID = query.value(ix++).toString();
227         qulonglong dbid = query.value(ix++).toULongLong();
228 
229         if (m_partHash.value(moduleID, NULL) != NULL) {
230             // a part with this moduleID was already loaded--the file version overrides the db version
231             continue;
232         }
233 
234         if (!path.startsWith(ResourcePath)) {        // not the resources path
235             path = partsDir.absoluteFilePath(path);
236             if (QFileInfo(path).exists()) {
237                 // assume this is a later version of the fzp so load it later via xml
238                 CoreList << moduleID;
239                 continue;
240             }
241 
242             path.replace("/parts/", "/pdb/");
243         }
244 
245 		ModelPart * modelPart = new ModelPart();
246         ModelPartShared * modelPartShared = new ModelPartShared();
247         modelPart->setModelPartShared(modelPartShared);
248 
249         modelPartShared->setModuleID(moduleID);
250         modelPartShared->setDBID(dbid);
251         QString family = query.value(ix++).toString();
252         modelPartShared->setFamily(family);
253         modelPartShared->setVersion(query.value(ix++).toString());
254         modelPartShared->setReplacedby(query.value(ix++).toString());
255         modelPartShared->setFritzingVersion(query.value(ix++).toString());
256         modelPartShared->setAuthor(query.value(ix++).toString());
257         modelPartShared->setTitle(query.value(ix++).toString());
258         modelPartShared->setLabel(query.value(ix++).toString());
259         modelPartShared->setDate(query.value(ix++).toString());
260         modelPartShared->setDescription(query.value(ix++).toString());
261         modelPartShared->setSpice(query.value(ix++).toString());
262         modelPartShared->setSpiceModel(query.value(ix++).toString());
263         modelPartShared->setTaxonomy(query.value(ix++).toString());
264         modelPart->setItemType((ModelPart::ItemType) query.value(ix++).toInt());
265         modelPartShared->setPath(path);
266         modelPart->setCore(true);
267 
268         modelPartShared->setConnectorsInitialized(true);
269 
270         m_partHash.insert(modelPartShared->moduleID(), modelPart);
271         parts[dbid] = modelPart;
272 
273 	    q2.bindValue(":moduleID", modelPartShared->moduleID());
274 	    q2.bindValue(":family", family);
275 	    q2.bindValue(":core", "1");
276         bool result = q2.exec();
277         if (!result) debugExec("unable to add part to memory", q2);
278 
279         qulonglong newid = q2.lastInsertId().toULongLong();
280         oldToNew[dbid] = newid;
281 	}
282 
283     query = db.exec("SELECT viewid, image, layers, sticky, flipvertical, fliphorizontal, part_id FROM viewimages");
284     debugError(query.isActive(), query);
285     if (!query.isActive()) return false;
286 
287     while (query.next()) {
288         int ix = 0;
289         ViewImage * viewImage = new ViewImage(ViewLayer::BreadboardView);
290         viewImage->viewID = (ViewLayer::ViewID) query.value(ix++).toInt();
291         viewImage->image = query.value(ix++).toString();
292         viewImage->layers = query.value(ix++).toULongLong();
293         viewImage->sticky = query.value(ix++).toULongLong();
294         viewImage->canFlipVertical = query.value(ix++).toInt() == 0 ? false : true;
295         viewImage->canFlipHorizontal = query.value(ix++).toInt() == 0 ? false : true;
296         qulonglong dbid = query.value(ix++).toULongLong();
297 
298         ModelPart * modelPart = parts.at(dbid);
299         if (modelPart) {
300             parts.at(dbid)->setViewImage(viewImage);
301         }
302     }
303 
304     query = db.exec("SELECT tag, part_id FROM tags");
305     debugError(query.isActive(), query);
306     if (!query.isActive()) return false;
307 
308     while (query.next()) {
309         int ix = 0;
310         QString tag = query.value(ix++).toString();
311         qulonglong dbid = query.value(ix++).toULongLong();
312         ModelPart * modelPart = parts.at(dbid);
313         if (modelPart) {
314             parts.at(dbid)->setTag(tag);
315         }
316     }
317 
318     query = db.exec("SELECT name, value, part_id, show_in_label FROM properties");
319     debugError(query.isActive(), query);
320     if (!query.isActive()) return false;
321 
322     QSqlQuery q3(keep_db);
323 	result = q3.prepare("INSERT INTO properties(name, value, part_id, show_in_label) VALUES (:name, :value, :part_id, :show_in_label)");
324     debugError(result, q3);
325 
326     while (query.next()) {
327         int ix = 0;
328         QString name = query.value(ix++).toString();
329         QString value = query.value(ix++).toString();
330         qulonglong dbid = query.value(ix++).toULongLong();
331         int showInLabel = query.value(ix++).toInt();
332         ModelPart * modelPart = parts.at(dbid);
333         if (modelPart) {
334             parts.at(dbid)->setProperty(name, value, showInLabel);
335 	        q3.bindValue(":name", name.toLower().trimmed());
336 	        q3.bindValue(":value", value);
337 	        q3.bindValue(":part_id", oldToNew[dbid]);
338 	        q3.bindValue(":show_in_label", showInLabel);
339             bool result = q3.exec();
340             if (!result) debugExec("unable to add property to memory", q3);
341         }
342     }
343 
344     query = db.exec("SELECT COUNT(*) FROM connectors");
345     debugError(query.isActive(), query);
346     if (!query.isActive() || !query.next()) return false;
347 
348     int connectorCount = query.value(0).toInt();
349     if (connectorCount == 0) return false;
350 
351     QVector<Connector *> connectors(connectorCount + 1, NULL);
352 
353     query = db.exec("SELECT id, connectorid, type, name, description, replacedby, part_id FROM connectors");
354     debugError(query.isActive(), query);
355     if (!query.isActive()) {
356         killConnectors(connectors);
357         return false;
358     }
359 
360     while (query.next()) {
361         int ix = 0;
362         qulonglong cid = query.value(ix++).toULongLong();
363         QString connectorid = query.value(ix++).toString();
364         Connector::ConnectorType type = (Connector::ConnectorType) query.value(ix++).toInt();
365         QString name = query.value(ix++).toString();
366         QString description = query.value(ix++).toString();
367         QString replacedby = query.value(ix++).toString();
368         qulonglong dbid = query.value(ix++).toULongLong();
369         ModelPart * modelPart = parts.at(dbid);
370         if (modelPart) {
371             ConnectorShared * connectorShared = new ConnectorShared();
372             connectorShared->setConnectorType(type);
373             connectorShared->setDescription(description);
374             connectorShared->setReplacedby(replacedby);
375             connectorShared->setSharedName(name);
376             connectorShared->setId(connectorid);
377 
378             Connector * connector = new Connector(connectorShared, modelPart);
379             modelPart->addConnector(connector);
380 
381             connectors[cid] = connector;
382         }
383     }
384 
385     query = db.exec("SELECT view, layer, svgid, hybrid, terminalid, legid, connector_id FROM connectorlayers");
386     debugError(query.isActive(), query);
387     if (!query.isActive()) {
388         killConnectors(connectors);
389         return false;
390     }
391 
392     while (query.next()) {
393         int ix = 0;
394         ViewLayer::ViewID viewID = (ViewLayer::ViewID) query.value(ix++).toInt();
395         ViewLayer::ViewLayerID viewLayerID = (ViewLayer::ViewLayerID) query.value(ix++).toInt();
396         QString svgID = query.value(ix++).toString();
397         bool hybrid = query.value(ix++).toInt() == 0 ? false : true;
398         QString terminalID = query.value(ix++).toString();
399         QString legID = query.value(ix++).toString();
400         qulonglong cid = query.value(ix++).toULongLong();
401         Connector * connector = connectors.at(cid);
402         if (connector) {
403             connectors[cid]->addPin(viewID, svgID, viewLayerID, terminalID, legID, hybrid);
404         }
405     }
406 
407     query = db.exec("SELECT COUNT(*) FROM buses");
408     debugError(query.isActive(), query);
409     if (!query.isActive() || !query.next()) return false;
410 
411     int busCount = query.value(0).toInt();
412     if (busCount == 0) return false;
413 
414     QVector<BusShared *> buses(busCount + 1, NULL);
415     QHash<BusShared *, qulonglong> busids;
416 
417     query = db.exec("SELECT id, name, part_id FROM buses");
418     debugError(query.isActive(), query);
419     if (!query.isActive()) {
420         killConnectors(connectors);
421         killBuses(buses);
422         return false;
423     }
424 
425     while (query.next()) {
426         int ix = 0;
427         qulonglong bid = query.value(ix++).toULongLong();
428         QString name = query.value(ix++).toString();
429         qulonglong dbid = query.value(ix++).toULongLong();
430         ModelPart * modelPart = parts.at(dbid);
431         if (modelPart) {
432             BusShared * busShared = new BusShared(name);
433             ModelPart * modelPart = parts.at(dbid);
434             modelPart->modelPartShared()->insertBus(busShared);
435 
436             buses[bid] = busShared;
437             busids.insert(busShared, dbid);
438         }
439     }
440 
441     query = db.exec("SELECT connectorid, bus_id FROM busmembers");
442     debugError(query.isActive(), query);
443     if (!query.isActive()) {
444         killConnectors(connectors);
445         killBuses(buses);
446         return false;
447     }
448 
449     while (query.next()) {
450         int ix = 0;
451 
452         QString connectorid = query.value(ix++).toString();
453         qulonglong bid = query.value(ix++).toULongLong();
454         BusShared * busShared = buses[bid];
455         if (busShared) {
456             qulonglong dbid = busids.value(busShared);
457             ModelPart * modelPart = parts.at(dbid);
458             if (modelPart) {
459                 ConnectorShared * connectorShared = modelPart->modelPartShared()->getConnectorShared(connectorid);
460                 busShared->addConnectorShared(connectorShared);
461             }
462         }
463     }
464 
465     query = db.exec("SELECT subpart_id, part_id FROM schematic_subparts");
466     debugError(query.isActive(), query);
467     if (query.isActive()) {
468         while (query.next()) {
469             int ix = 0;
470             QString subpartID = query.value(ix++).toString();
471             qulonglong dbid = query.value(ix++).toULongLong();
472             ModelPart * modelPart = parts.at(dbid);
473             if (modelPart) {
474                 QString subModuleID = modelPart->moduleID() + "_" + subpartID;
475                 ModelPart * subModelPart = m_partHash.value(subModuleID);
476                 if (subModelPart) {
477                     subModelPart->setSubpartID(subpartID);
478                     modelPart->modelPartShared()->addSubpart(subModelPart->modelPartShared());
479                 }
480             }
481         }
482     }
483 
484     if (m_root == NULL) {
485         m_root = new ModelPart();
486     }
487     foreach (ModelPart * modelPart, m_partHash.values()) {
488         if (modelPart->dbid() != 0) {
489             // initConnectors is not redundant here
490             // there may be parts in m_partHash loaded from a file rather from the database
491             //
492             modelPart->initConnectors();
493             modelPart->flipSMDAnd();
494             modelPart->initBuses();
495             modelPart->setParent(m_root);
496             modelPart->lookForZeroConnector();
497         }
498     }
499 
500     return true;
501 }
502 
503 
~SqliteReferenceModel()504 SqliteReferenceModel::~SqliteReferenceModel() {
505 	deleteConnection();
506 }
507 
initParts(bool dbExists)508 void SqliteReferenceModel::initParts(bool dbExists) {
509 	m_init = true;
510 	PaletteModel::initParts(dbExists);
511 	m_init = false;
512 }
513 
createConnection(const QString & databaseName,bool fullLoad)514 bool SqliteReferenceModel::createConnection(const QString & databaseName, bool fullLoad) {
515 	m_swappingEnabled = true;
516 	m_database = QSqlDatabase::addDatabase("QSQLITE");
517 	m_database.setDatabaseName(databaseName.isEmpty() ? ":memory:" : databaseName);
518 	if (!m_database.open()) {
519 		m_swappingEnabled = false;
520 	}
521     else {
522         m_keepGoing = false;
523         bool gotTransaction = m_database.transaction();
524         DebugDialog::debug(gotTransaction ? "got transaction" : "no transaction");
525 
526 		bool result = createParts(m_database, fullLoad);
527 
528         QSqlQuery query;
529         result = query.exec("CREATE TABLE viewimages (\n"
530 			"id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,\n"
531 			"viewid INTEGER NOT NULL,\n"                     // ViewLayer::ViewID
532 			"image TEXT NOT NULL,\n"
533 			"layers INTEGER NOT NULL,\n"                     // bit flag (max 8 bytes = 64 layers)
534 			"sticky INTEGER NOT NULL,\n"                     // bit flag (max 8 bytes = 64 layers)
535 			"flipvertical INTEGER NOT NULL,\n"               // boolean
536 			"fliphorizontal INTEGER NOT NULL,\n"             // boolean
537             "part_id INTEGER NOT NULL"
538 		")");
539         debugError(result, query);
540 
541         result = query.exec("CREATE TABLE connectors (\n"
542 			"id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,\n"
543 			"connectorid TEXT NOT NULL,\n"
544 			"type INTEGER NOT NULL,\n"
545 			"name TEXT NOT NULL,\n"
546 			"description TEXT,\n"
547 			"replacedby TEXT,\n"
548             "part_id INTEGER NOT NULL"
549 		")");
550         debugError(result, query);
551 
552         result = query.exec("CREATE TABLE connectorlayers (\n"
553 			"id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,\n"
554 			"view INTEGER NOT NULL,\n"                      // ViewLayer::ViewID
555 			"layer INTEGER NOT NULL,\n"                     // ViewLayer::ViewLayerID
556 			"svgid TEXT NOT NULL,\n"
557 			"hybrid INTEGER NOT NULL,\n"
558 			"terminalid TEXT,\n"
559 			"legid TEXT,\n"
560             "connector_id INTEGER NOT NULL"
561 		")");
562         debugError(result, query);
563 
564         result = query.exec("CREATE TABLE buses (\n"
565 			"id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,\n"
566 			"name TEXT NOT NULL,\n"
567             "part_id INTEGER NOT NULL"
568 		")");
569         debugError(result, query);
570 
571         result = query.exec("CREATE TABLE busmembers (\n"
572 			"id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,\n"
573 			"connectorid TEXT NOT NULL,\n"
574             "bus_id INTEGER NOT NULL"
575 		")");
576         debugError(result, query);
577 
578         result = query.exec("CREATE TABLE schematic_subparts (\n"
579 			"id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,\n"
580 			"label TEXT NOT NULL,\n"
581 			"subpart_id TEXT NOT NULL,\n"
582             "part_id INTEGER NOT NULL"
583 		")");
584         debugError(result, query);
585 
586         // TODO: create a dictionary table so redundant tags, property names, and property values aren't stored multiple times
587 
588 		result = query.exec("CREATE TABLE tags (\n"
589 			"id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL ,\n"
590 			"tag TEXT NOT NULL,\n"
591             "part_id INTEGER NOT NULL"
592 		")");
593         debugError(result, query);
594 
595         result = createProperties(m_database);
596 
597 		result = query.exec("CREATE TRIGGER unique_part__moduleID \n"
598 			"BEFORE INSERT ON parts \n"
599 			"FOR EACH ROW BEGIN \n"
600 				"SELECT RAISE(ROLLBACK, 'insert on table \"parts\" violates unique constraint \"unique_part__moduleID\"') \n"
601 					"WHERE (SELECT count(*) FROM parts WHERE moduleID = NEW.moduleID) > 0; \n"
602 			"END; "
603 		);
604         debugError(result, query);
605 
606         if (fullLoad) {
607 		    foreach(ModelPart* mp, m_partHash.values()) {
608 			    mp->initConnectors(false);
609             }
610 		}
611 		foreach(ModelPart* mp, m_partHash.values()) {
612 			addPartAux(mp, fullLoad);
613 		}
614 
615         createIndexes();
616         createMoreIndexes(m_database);
617 
618 		m_database.commit();
619 
620 	}
621 	return m_swappingEnabled;
622 }
623 
deleteConnection()624 void SqliteReferenceModel::deleteConnection() {
625 	QSqlDatabase::removeDatabase("SQLITE");
626 }
627 
loadPart(const QString & path,bool update)628 ModelPart *SqliteReferenceModel::loadPart(const QString & path, bool update) {
629 	ModelPart *modelPart = PaletteModel::loadPart(path, update);
630 	if (modelPart == NULL) return modelPart;
631 
632 	if (!m_init) addPart(modelPart, update);
633 	return modelPart;
634 }
635 
retrieveModelPart(const QString & moduleID)636 ModelPart *SqliteReferenceModel::retrieveModelPart(const QString &moduleID) {
637 	if (moduleID.isEmpty()) {
638 		return NULL;
639 	}
640 	return m_partHash.value(moduleID, NULL);
641 }
642 
retrieveModuleIdWith(const QString & family,const QString & propertyName,bool closestMatch)643 QString SqliteReferenceModel::retrieveModuleIdWith(const QString &family, const QString &propertyName, bool closestMatch) {
644 	QString moduleID = retrieveModuleId(family,m_recordedProperties,propertyName, closestMatch);
645 	m_recordedProperties.clear();
646 	return moduleID;
647 }
648 
retrieveModuleId(const QString & family,const QMultiHash<QString,QString> & properties,const QString & propertyName,bool closestMatch)649 QString SqliteReferenceModel::retrieveModuleId(const QString &family, const QMultiHash<QString, QString> &properties, const QString &propertyName, bool closestMatch)
650 {
651 	QString propertyValue;
652 
653 	if(properties.size() > 0) {
654 		QString queryStr =
655 			"SELECT moduleID FROM parts part \n"
656 			"WHERE part.family = :family AND (1=1 ";
657 		QHash<QString, QString> params;
658         int ix = 0;
659 		foreach (QString name, properties.uniqueKeys()) {
660             foreach (QString value, properties.values(name)) {
661 			    QString propParam = QString(":prop%1").arg(ix++);
662 			    queryStr += QString(" AND EXISTS (SELECT * FROM properties prop WHERE prop.part_id = part.id AND prop.name = %1_name AND prop.value = %1_value)").arg(propParam);
663 			    params[propParam+"_name"] = name;
664 			    params[propParam+"_value"] = value;
665 			    if(name == propertyName) {
666 				    propertyValue = value;
667 			    }
668             }
669 		}
670 		queryStr += ") order by part.core desc";
671 
672 		QSqlQuery query;
673 		query.prepare(queryStr);
674 
675 		query.bindValue(":family",family.toLower().trimmed());
676 		foreach(QString name, params.uniqueKeys()) {
677 			query.bindValue(name,params[name].toLower().trimmed());
678 		}
679 
680 		QString moduleId;
681 		if(query.exec()) {
682 			if(query.next()) {
683 				moduleId =  query.value(0).toString(); //grab the first
684 			}
685 			//DebugDialog::debug("SQLITE: found: "+moduleId);
686 		} else {
687             debugExec("couldn't retrieve part", query);
688 		}
689 
690 		if(!moduleId.isEmpty()) {
691 			m_lastWasExactMatch = true;
692 			return moduleId;
693 		} else if(closestMatch || !propertyName.isEmpty()) {
694 			m_lastWasExactMatch = false;
695 			return closestMatchId(family, properties, propertyName, propertyValue);
696 		} else {
697 			return ___emptyString___;
698 		}
699 	} else {
700 		return ___emptyString___;
701 	}
702 }
703 
closestMatchId(const QString & family,const QMultiHash<QString,QString> & properties,const QString & propertyName,const QString & propertyValue)704 QString SqliteReferenceModel::closestMatchId(const QString &family, const QMultiHash<QString, QString> &properties, const QString &propertyName, const QString &propertyValue) {
705 	QStringList possibleMatches = getPossibleMatches(family, properties, propertyName, propertyValue);
706 	return getClosestMatch(family, properties, possibleMatches);
707 }
708 
getPossibleMatches(const QString & family,const QMultiHash<QString,QString> & properties,const QString & propertyName,const QString & propertyValue)709 QStringList SqliteReferenceModel::getPossibleMatches(const QString &family, const QMultiHash<QString, QString> &properties, const QString &propertyName, const QString &propertyValue) {
710 	Q_UNUSED(properties);
711 
712     QStringList result;
713 	QString queryStr =
714 		"SELECT moduleID FROM parts part \n"
715 		"WHERE part.family = :family AND EXISTS ( \n"
716 			"SELECT * FROM properties prop \n"
717 			"WHERE prop.part_id = part.id %1 \n"
718 		") ";
719 	queryStr = queryStr.arg((propertyName.isEmpty()) ? "" : "AND prop.name = :prop_name  AND prop.value = :prop_value ");
720 	QSqlQuery query;
721 	query.prepare(queryStr);
722 
723 	query.bindValue(":family", family.toLower().trimmed());
724 	if (!propertyName.isEmpty()) {
725 		query.bindValue(":prop_name", propertyName.toLower().trimmed());
726 		query.bindValue(":prop_value", propertyValue);
727 	}
728 
729 
730 	if(query.exec()) {
731 		while(query.next()) {
732 			result << query.value(0).toString();
733 		}
734 		//DebugDialog::debug(QString("SQLITE: %1 possible matches found").arg(result.size()));
735 	} else {
736         debugExec("couldn't get possible match", query);
737 	}
738 
739 	return result;
740 }
741 
getClosestMatch(const QString & family,const QMultiHash<QString,QString> & properties,QStringList possibleMatches)742 QString SqliteReferenceModel::getClosestMatch(const QString &family, const QMultiHash<QString, QString> &properties, QStringList possibleMatches) {
743 	int propsInCommonCount = 0;
744 	int propsInCommonCountAux = 0;
745 	QString result;
746 	foreach(QString modId, possibleMatches) {
747 		propsInCommonCountAux = countPropsInCommon(family, properties, retrieveModelPart(modId));
748 		if(propsInCommonCountAux > propsInCommonCount) {
749 			result = modId;
750 			propsInCommonCount = propsInCommonCountAux;
751 		}
752 	}
753 	return result;
754 }
755 
countPropsInCommon(const QString & family,const QMultiHash<QString,QString> & properties,const ModelPart * part2)756 int SqliteReferenceModel::countPropsInCommon(const QString &family, const QMultiHash<QString, QString> &properties, const ModelPart *part2) {
757 	Q_UNUSED(family)
758 
759     if (part2 == NULL) {
760 		DebugDialog::debug("countPropsInCommon failure");
761 		return 0;
762 	}
763 
764 	int result = 0;
765 	QMultiHash<QString,QString> props2 = part2->properties();
766 	foreach(QString prop, properties.uniqueKeys()) {
767         QStringList values1 = properties.values(prop);
768 		QStringList values2 = props2.values(prop);
769         foreach (QString value1, values1) {
770 		    if (values2.contains(value1)) {
771 			 result++;
772             }
773 		}
774 	}
775 	return result;
776 }
777 
lastWasExactMatch()778 bool SqliteReferenceModel::lastWasExactMatch() {
779 	return m_lastWasExactMatch;
780 }
781 
addPartAux(ModelPart * newModel,bool fullLoad)782 bool SqliteReferenceModel::addPartAux(ModelPart * newModel, bool fullLoad) {
783 	try {
784 		bool result = insertPart(newModel, fullLoad);
785 		return result;
786 	}
787 	catch (const char * msg) {
788 		DebugDialog::debug(msg);
789 	}
790 	catch (const QString & msg) {
791 		DebugDialog::debug(msg);
792 	}
793 	catch (...) {
794 		DebugDialog::debug("SqliteReferenceModel::addPartAux failure");
795 	}
796 
797     return false;
798 }
799 
addPart(ModelPart * newModel,bool update)800 bool SqliteReferenceModel::addPart(ModelPart * newModel, bool update) {
801 	bool result;
802 	if(update && containsModelPart(newModel->moduleID())) {
803 		result = updatePart(newModel);
804 	} else {
805 		result = addPartAux(newModel, false);
806 	}
807 	return result;
808 }
809 
addPart(QString newPartPath,bool addToReference,bool updateIdAlreadyExists)810 ModelPart * SqliteReferenceModel::addPart(QString newPartPath, bool addToReference, bool updateIdAlreadyExists)
811 {
812 	return PaletteModel::addPart(newPartPath, addToReference, updateIdAlreadyExists);
813 }
814 
815 
reloadPart(const QString & path,const QString & moduleID)816 ModelPart * SqliteReferenceModel::reloadPart(const QString & path, const QString & moduleID) {
817     m_partHash.remove(moduleID);
818     ModelPart *modelPart = PaletteModel::loadPart(path, false);
819 	if (modelPart == NULL) return modelPart;
820 
821 	updatePart(modelPart);
822 	return modelPart;
823 }
824 
825 
updatePart(ModelPart * newModel)826 bool SqliteReferenceModel::updatePart(ModelPart * newModel) {
827 	if(m_swappingEnabled) {
828 		qulonglong partId = this->partId(newModel->moduleID());
829 		if(partId != NO_ID) {
830 			removePart(partId);
831 			removeProperties(partId);
832 			return addPartAux(newModel, false);
833 		} else {
834 			return false;
835 		}
836 	} else {
837 		return false;
838 	}
839 
840 }
841 
insertPart(ModelPart * modelPart,bool fullLoad)842 bool SqliteReferenceModel::insertPart(ModelPart * modelPart, bool fullLoad) {
843     DebugModelPart = modelPart;
844 
845     QHash<QString, QString> properties = modelPart->properties();
846 	QSqlQuery query;
847     QString fields;
848     QString values;
849     if (fullLoad) {
850         fields =  " version,  replacedby,  fritzingversion,  author,  title,  label,  date,  description,  spice,  spicemodel,  taxonomy,  itemtype,  path";
851         values = " :version, :replacedby, :fritzingversion, :author, :title, :label, :date, :description, :spice, :spicemodel, :taxonomy, :itemtype, :path";
852     }
853     else {
854         fields =  " core";
855         values = " :core";
856     }
857 	query.prepare(QString("INSERT INTO parts(moduleID, family, %1) VALUES (:moduleID, :family, %2)").arg(fields).arg(values));
858 	query.bindValue(":moduleID", modelPart->moduleID());
859 	query.bindValue(":family", properties.value("family").toLower().trimmed());
860     if (fullLoad) {
861         QString path = modelPart->path();
862         QString prefix = FolderUtils::getApplicationSubFolderPath("parts");
863 
864         if (path.startsWith(ResourcePath)) {
865         }
866         else if (path.startsWith(prefix)) {
867             // copy the fzp away so it's not consulted at normal load time
868             QString newPath = path;
869             newPath.replace("/parts/", "/pdb/");
870             QFile::remove(newPath);
871             QFile::rename(path, newPath);
872             path = path.mid(prefix.count() + 1);  // + 1 to remove the beginning "/"
873         }
874         else {
875             bool bail = true;
876             if (modelPart->itemType() == ModelPart::SchematicSubpart) {
877                 ModelPartShared * mps = modelPart->modelPartShared();
878                 if (mps && mps->superpart() && mps->superpart()->path().startsWith(prefix)) {
879                     bail = false;
880                 }
881             }
882 
883             if (bail) {
884                 DebugDialog::debug(QString("part path not in parts:%1").arg(path));
885                 return true;
886             }
887         }
888 
889 
890         query.bindValue(":version", modelPart->version());
891         query.bindValue(":replacedby", modelPart->replacedby());
892 	    query.bindValue(":fritzingversion", modelPart->fritzingVersion());
893 	    query.bindValue(":author", modelPart->author());
894 	    query.bindValue(":title", modelPart->title());
895 	    query.bindValue(":label", modelPart->label());
896 	    query.bindValue(":date", modelPart->date());
897 	    query.bindValue(":description", modelPart->description());
898 	    query.bindValue(":spice", modelPart->spice());
899 	    query.bindValue(":spicemodel", modelPart->spiceModel());
900 	    query.bindValue(":taxonomy", modelPart->taxonomy());
901 	    query.bindValue(":itemtype", (int) modelPart->itemType());
902 
903 	    query.bindValue(":path", path);
904     }
905     else {
906 	    query.bindValue(":core", modelPart->isCore() ? "1" : "0");
907     }
908 
909 
910 	if (query.exec()) {
911         qulonglong id = query.lastInsertId().toULongLong();
912         modelPart->setDBID(id);
913         foreach (QString prop, properties.keys()) {
914             if (prop == "family") continue;
915 
916 			bool result = insertProperty(prop, properties.value(prop), id, modelPart->showInLabel(prop));
917             if (fullLoad && !result) {
918                 m_swappingEnabled = false;
919             }
920             if (!result) {
921                 FailurePropertyMessages << tr("property '%1' in part '%2' with id '%3'.")
922                     .arg(prop).arg(modelPart->path()).arg(modelPart->moduleID());
923             }
924 		}
925 
926         if (fullLoad) {
927             foreach (QString tag, modelPart->tags()) {
928                 insertTag(tag, id);
929             }
930 
931             foreach (ViewImage * viewImage, modelPart->viewImages()) {
932                 insertViewImage(viewImage, id);
933             }
934 
935             foreach (Connector * connector, modelPart->connectors().values()) {
936                 insertConnector(connector, id);
937             }
938 
939             foreach (Bus * bus, modelPart->buses().values()) {
940                 insertBus(bus, id);
941             }
942 
943             ModelPartShared * mps = modelPart->modelPartShared();
944             if (mps) {
945                 foreach (ModelPartShared * sub, mps->subparts()) {
946                     insertSubpart(sub, id);
947                 }
948             }
949         }
950 	} else {
951         debugExec("couldn't insert part", query);
952         FailurePartMessages << tr("part '%1' with id '%2'; possibly because it has no 'family' property.")
953                                 .arg(modelPart->path()).arg(modelPart->moduleID());
954 	}
955 
956     DebugModelPart = NULL;
957 	return true;
958 }
959 
insertProperty(const QString & name,const QString & value,qulonglong id,bool showInLabel)960 bool SqliteReferenceModel::insertProperty(const QString & name, const QString & value, qulonglong id, bool showInLabel) {
961 	QSqlQuery query;
962 	query.prepare("INSERT INTO properties(name, value, part_id, show_in_label) VALUES (:name, :value, :part_id, :show_in_label)");
963 	query.bindValue(":name", name.toLower().trimmed());
964 	query.bindValue(":value", value);
965 	query.bindValue(":part_id", id);
966 	query.bindValue(":show_in_label", showInLabel ? 1 : 0);
967 	if(!query.exec()) {
968         debugExec("couldn't insert property", query);
969 		return false;
970 	} else {
971 		// qulonglong id = query.lastInsertId().toULongLong();
972 	}
973 
974 	return true;
975 }
976 
insertTag(const QString & tag,qulonglong id)977 bool SqliteReferenceModel::insertTag(const QString & tag, qulonglong id)
978 {
979 	QSqlQuery query;
980 	query.prepare("INSERT INTO tags(tag, part_id) VALUES (:tag, :part_id)");
981 	query.bindValue(":tag", tag.toLower().trimmed());
982 	query.bindValue(":part_id", id);
983 	if(!query.exec()) {
984         debugExec("couldn't insert tag", query);
985 		m_swappingEnabled = false;
986 	} else {
987 		// qulonglong id = query.lastInsertId().toULongLong();
988 	}
989 
990 	return true;
991 }
992 
insertViewImage(const ViewImage * viewImage,qulonglong id)993 bool SqliteReferenceModel::insertViewImage(const ViewImage * viewImage, qulonglong id)
994 {
995     if (viewImage->image.isEmpty() && viewImage->layers == 0) return true;
996 
997 	QSqlQuery query;
998 	query.prepare("INSERT INTO viewimages(viewid, image, layers, sticky, flipvertical, fliphorizontal, part_id) "
999                     "VALUES (:viewid, :image, :layers, :sticky, :flipvertical, :fliphorizontal, :part_id)");
1000 	query.bindValue(":viewid", viewImage->viewID);
1001 	query.bindValue(":image", viewImage->image);
1002 	query.bindValue(":layers", viewImage->layers);
1003 	query.bindValue(":sticky", viewImage->sticky);
1004 	query.bindValue(":flipvertical", viewImage->canFlipVertical ? 1 : 0);
1005 	query.bindValue(":fliphorizontal", viewImage->canFlipHorizontal ? 1 : 0);
1006 	query.bindValue(":part_id", id);
1007 	if(!query.exec()) {
1008         debugExec("couldn't insert viewimage", query);
1009 		m_swappingEnabled = false;
1010 	} else {
1011 		// qulonglong id = query.lastInsertId().toULongLong();
1012 	}
1013 
1014 	return true;
1015 }
1016 
insertBus(const Bus * bus,qulonglong id)1017 bool SqliteReferenceModel::insertBus(const Bus * bus, qulonglong id)
1018 {
1019 	QSqlQuery query;
1020 	query.prepare("INSERT INTO buses(name, part_id) VALUES (:name, :part_id)");
1021 	query.bindValue(":name", bus->id());
1022 	query.bindValue(":part_id", id);
1023 	if(!query.exec()) {
1024         debugExec("couldn't insert bus", query);
1025 		m_swappingEnabled = false;
1026 	} else {
1027 		qulonglong bid = query.lastInsertId().toULongLong();
1028         foreach (Connector * connector, bus->connectors()) {
1029             insertBusMember(connector, bid);
1030         }
1031 	}
1032 
1033 	return true;
1034 }
1035 
insertBusMember(const Connector * connector,qulonglong id)1036 bool SqliteReferenceModel::insertBusMember(const Connector * connector, qulonglong id)
1037 {
1038 	QSqlQuery query;
1039 	query.prepare("INSERT INTO busmembers(connectorid, bus_id) VALUES (:connectorid, :bus_id)");
1040 	query.bindValue(":connectorid", connector->connectorSharedID());
1041 	query.bindValue(":bus_id", id);
1042 	if(!query.exec()) {
1043         debugExec("couldn't insert busmember", query);
1044 		m_swappingEnabled = false;
1045 	} else {
1046 		//qulonglong id = query.lastInsertId().toULongLong();
1047 	}
1048 
1049 	return true;
1050 }
1051 
insertConnector(const Connector * connector,qulonglong id)1052 bool SqliteReferenceModel::insertConnector(const Connector * connector, qulonglong id)
1053 {
1054 	QSqlQuery query;
1055 	query.prepare("INSERT INTO connectors(connectorid, type, name, description, replacedby, part_id) VALUES (:connectorid, :type, :name, :description, :replacedby, :part_id)");
1056 	query.bindValue(":connectorid", connector->connectorSharedID());
1057 	query.bindValue(":type", (int) connector->connectorType());
1058 	query.bindValue(":name", connector->connectorSharedName());
1059 	query.bindValue(":description", connector->connectorSharedDescription());
1060 	query.bindValue(":replacedby", connector->connectorSharedReplacedby());
1061 	query.bindValue(":part_id", id);
1062 	if(!query.exec()) {
1063         debugExec("couldn't insert connector", query);
1064 		m_swappingEnabled = false;
1065 	} else {
1066 		qulonglong id = query.lastInsertId().toULongLong();
1067         foreach (SvgIdLayer * layer, connector->svgIdLayers()) {
1068             insertConnectorLayer(layer, id);
1069         }
1070 	}
1071 
1072 	return true;
1073 }
1074 
1075 
insertConnectorLayer(const SvgIdLayer * svgIdLayer,qulonglong id)1076 bool SqliteReferenceModel::insertConnectorLayer(const SvgIdLayer * svgIdLayer, qulonglong id)
1077 {
1078 
1079 	QSqlQuery query;
1080 	query.prepare("INSERT INTO connectorLayers(view, layer, svgid, hybrid, terminalid, legid, connector_id) VALUES "
1081                                             "(:view, :layer, :svgid, :hybrid, :terminalid, :legid, :connector_id)");
1082 	query.bindValue(":view", svgIdLayer->m_viewID);
1083 	query.bindValue(":layer", svgIdLayer->m_svgViewLayerID);
1084 	query.bindValue(":svgid", svgIdLayer->m_svgId);
1085 	query.bindValue(":hybrid", svgIdLayer->m_hybrid ? 1 : 0);
1086 	query.bindValue(":terminalid", svgIdLayer->m_terminalId);
1087 	query.bindValue(":legid", svgIdLayer->m_legId);
1088 	query.bindValue(":connector_id", id);
1089 	if(!query.exec()) {
1090         debugExec("couldn't insert viewimage", query);
1091 		m_swappingEnabled = false;
1092 	} else {
1093 		// qulonglong id = query.lastInsertId().toULongLong();
1094 	}
1095 
1096 	return true;
1097 }
1098 
propValues(const QString & family,const QString & propName,bool distinct)1099 QStringList SqliteReferenceModel::propValues(const QString &family, const QString &propName, bool distinct) {
1100 	QStringList retval;
1101 
1102 	QSqlQuery query;
1103 	query.prepare(QString(
1104 		"SELECT %1 prop.value FROM properties prop JOIN parts PART ON part.id = prop.part_id \n"
1105 		"WHERE part.family = :family AND prop.name = :propName ORDER BY prop.value \n"
1106 		).arg(distinct ? " DISTINCT ":"")
1107 	);
1108 	query.bindValue(":family",family.toLower().trimmed());
1109 	query.bindValue(":propName",propName.toLower().trimmed());
1110 
1111 	if(query.exec()) {
1112 		while(query.next()) {
1113             QString value = query.value(0).toString();
1114             if (!value.isEmpty()) retval << value;
1115 		}
1116 	} else {
1117         debugExec("couldn't retrieve values", query);
1118 		m_swappingEnabled = false;
1119 	}
1120 
1121 	return retval;
1122 }
1123 
allPropValues(const QString & family,const QString & propName)1124 QMultiHash<QString, QString> SqliteReferenceModel::allPropValues(const QString &family, const QString &propName) {
1125 	QMultiHash<QString, QString> retval;
1126 
1127 	QSqlQuery query;
1128 	query.prepare(QString(
1129 		"SELECT value, moduleID FROM properties prop JOIN parts part ON part.id = prop.part_id \n"
1130 		"WHERE part.family = :family AND prop.name = :propName\n"
1131 		)
1132 	);
1133 	query.bindValue(":family",family.toLower().trimmed());
1134 	query.bindValue(":propName",propName.toLower().trimmed());
1135 
1136 	if(query.exec()) {
1137 		while(query.next()) {
1138 			//QSqlRecord record = query.record();
1139 			//for (int i = 0; i < record.count(); i++) {
1140 			//	DebugDialog::debug("result " + record.fieldName(i) + " " + record.value(i).toString());
1141 			//}
1142             QString prop = query.value(0).toString();
1143             if (!prop.isEmpty()) retval.insert(prop, query.value(1).toString());
1144 		}
1145 	} else {
1146         debugExec("couldn't retrieve values", query);
1147 		m_swappingEnabled = false;
1148 	}
1149 
1150 	return retval;
1151 }
1152 
1153 
1154 
recordProperty(const QString & name,const QString & value)1155 void SqliteReferenceModel::recordProperty(const QString &name, const QString &value) {
1156 	DebugDialog::debug(QString("RECORDING PROPERTY %1:%2").arg(name).arg(value));
1157 	m_recordedProperties.insert(name,value);
1158 }
1159 
swapEnabled()1160 bool SqliteReferenceModel::swapEnabled() {
1161 	return m_swappingEnabled;
1162 }
1163 
containsModelPart(const QString & moduleID)1164 bool SqliteReferenceModel::containsModelPart(const QString & moduleID) {
1165 	return partId(moduleID) != NO_ID;
1166 }
1167 
partId(QString moduleID)1168 qulonglong SqliteReferenceModel::partId(QString moduleID) {
1169 	qulonglong partId = NO_ID;
1170 
1171 	QSqlQuery query;
1172 	query.prepare(
1173 		"SELECT id FROM parts \n"
1174 		"WHERE moduleID = :moduleID "
1175 	);
1176 	query.bindValue(":moduleID",moduleID);
1177 
1178 	if(query.exec()) {
1179 		//DebugDialog::debug("SQLITE: retrieving id: "+moduleID);
1180 		if(query.next()) {
1181 			partId =  query.value(0).toULongLong(); //grab the first
1182 		}
1183 		//DebugDialog::debug(QString("SQLITE: found: %1").arg(partId));
1184 	} else {
1185         debugExec("couldn't retrieve part", query);
1186 	}
1187 
1188 	return partId;
1189 }
1190 
removePart(qulonglong partId)1191 bool SqliteReferenceModel::removePart(qulonglong partId) {
1192 	bool result = true;
1193 
1194 	QSqlQuery query;
1195 	query.prepare(
1196 		"DELETE FROM parts \n"
1197 		"WHERE id = :id "
1198 	);
1199 	query.bindValue(":id",partId);
1200 
1201 	if(query.exec()) {
1202 		result = true;
1203 	} else {
1204 		DebugDialog::debug(
1205 			"SQLITE: couldn't delete part\n"
1206 			"\t "+query.lastQuery()+"\n"
1207 			"\t ERROR DRIVER: "+query.lastError().driverText()+"\n"
1208 			"\t ERROR DB: "+query.lastError().databaseText()+"\n"
1209 		);
1210 		result = false;
1211 	}
1212 
1213 	return result;
1214 }
1215 
removeProperties(qulonglong partId)1216 bool SqliteReferenceModel::removeProperties(qulonglong partId) {
1217 	bool result = true;
1218 
1219 	QSqlQuery query;
1220 	query.prepare(
1221 		"DELETE FROM properties \n"
1222 		"WHERE part_id = :id "
1223 	);
1224 	query.bindValue(":id",partId);
1225 
1226 	if(query.exec()) {
1227 		result = true;
1228 	} else {
1229 		DebugDialog::debug(
1230 			"SQLITE: couldn't delete properties \n"
1231 			"\t "+query.lastQuery()+"\n"
1232 			"\t ERROR DRIVER: "+query.lastError().driverText()+"\n"
1233 			"\t ERROR DB: "+query.lastError().databaseText()+"\n"
1234 		);
1235 		result = false;
1236 	}
1237 
1238 	return result;
1239 }
1240 
partTitle(const QString & moduleID)1241 QString SqliteReferenceModel::partTitle(const QString & moduleID) {
1242 	ModelPart *mp = retrieveModelPart(moduleID);
1243 	if(mp) {
1244 		return mp->modelPartShared()->title();
1245 	} else {
1246 		return ___emptyString___;
1247 	}
1248 }
1249 
killParts()1250 void SqliteReferenceModel::killParts()
1251 {
1252     foreach (ModelPart * modelPart, m_partHash.values()) {
1253         delete modelPart;
1254     }
1255     m_partHash.clear();
1256 }
1257 
createProperties(QSqlDatabase & db)1258 bool SqliteReferenceModel::createProperties(QSqlDatabase & db) {
1259 	QSqlQuery query = db.exec("CREATE TABLE properties (\n"
1260 			"id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL ,\n"
1261 			"name TEXT NOT NULL,\n"
1262 			"value TEXT NOT NULL,\n"
1263 			"show_in_label INTEGER NOT NULL,\n"
1264 			"part_id INTEGER NOT NULL"
1265 		")");
1266     debugError(query.isActive(), query);
1267     return query.isActive();
1268 }
1269 
createParts(QSqlDatabase & db,bool fullLoad)1270 bool SqliteReferenceModel::createParts(QSqlDatabase & db, bool fullLoad)
1271 {
1272     QString extra;
1273     if (fullLoad) {
1274         extra = "version TEXT,\n"
1275             "replacedby TEXT,\n"
1276 	        "fritzingversion TEXT,\n"
1277             "author TEXT,\n"
1278 			"title TEXT,\n"
1279 			"label TEXT,\n"
1280 			"date TEXT,\n"
1281 			"description TEXT,\n"
1282 			"spice TEXT,\n"
1283 			"spicemodel TEXT,\n"
1284 			"taxonomy TEXT,\n"
1285 			"itemtype INTEGER NOT NULL,\n"
1286 			"path TEXT\n"
1287           ;
1288     }
1289     else {
1290         extra = "core TEXT NOT NULL\n"
1291         ;
1292     }
1293 
1294 
1295     QString string = QString("CREATE TABLE parts (\n"
1296 			"id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,\n"
1297 			"moduleID TEXT NOT NULL,\n"
1298 			"family TEXT NOT NULL,\n"
1299             "%1"
1300 		    ")")
1301           .arg(extra);
1302 
1303     QSqlQuery query = db.exec(string);
1304     debugError(query.isActive(), query);
1305     return query.isActive();
1306 }
1307 
insertSubpart(ModelPartShared * mps,qulonglong id)1308 bool SqliteReferenceModel::insertSubpart(ModelPartShared * mps, qulonglong id)
1309 {
1310 	QSqlQuery query;
1311 	query.prepare("INSERT INTO schematic_subparts(label, subpart_id, part_id) VALUES (:label, :subpart_id, :part_id)");
1312 	query.bindValue(":label", mps->label());
1313 	query.bindValue(":subpart_id", mps->subpartID());
1314 	query.bindValue(":part_id", id);
1315 	if(!query.exec()) {
1316         debugExec("couldn't insert bus", query);
1317 		m_swappingEnabled = false;
1318 	}
1319     //else {
1320 	//	qulonglong sid = query.lastInsertId().toULongLong();
1321 	//}
1322 
1323 	return true;
1324 }
1325 
createIndexes()1326 void SqliteReferenceModel::createIndexes() {
1327     // supposedly faster to add these after the data is inserted
1328 
1329     QSqlQuery query;
1330 	bool result = query.exec("CREATE INDEX idx_viewimage_part_id ON viewimages (part_id ASC)");
1331     debugError(result, query);
1332 	result = query.exec("CREATE INDEX idx_connector_part_id ON connectors (part_id ASC)");
1333     debugError(result, query);
1334 	result = query.exec("CREATE INDEX idx_connectorlayer_connector_id ON connectorlayers (connector_id ASC)");
1335     debugError(result, query);
1336 	result = query.exec("CREATE INDEX idx_bus_part_id ON buses (part_id ASC)");
1337     debugError(result, query);
1338 	result = query.exec("CREATE INDEX idx_busmember_bus_id ON busmembers (bus_id ASC)");
1339     debugError(result, query);
1340 	result = query.exec("CREATE INDEX idx_schematic_subpart_part_id ON schematic_subparts (part_id ASC)");
1341     debugError(result, query);
1342 	result = query.exec("CREATE INDEX idx_tag_part_id ON tags (part_id ASC)");
1343     debugError(result, query);
1344 }
1345 
createMoreIndexes(QSqlDatabase & db)1346 void SqliteReferenceModel::createMoreIndexes(QSqlDatabase & db)
1347 {
1348     // supposedly faster to add these after the data is inserted
1349 
1350     QSqlQuery query = db.exec("CREATE INDEX idx_property_name ON properties (name ASC)");
1351     debugError(query.isActive(), query);
1352 
1353 	query = db.exec("CREATE INDEX idx_part_id ON parts (id ASC)");
1354     debugError(query.isActive(), query);
1355 
1356     query = db.exec("CREATE INDEX idx_part_moduleID ON parts (moduleID ASC)");
1357     debugError(query.isActive(), query);
1358 
1359     query = db.exec("CREATE INDEX idx_part_family ON parts (family ASC)");
1360     debugError(query.isActive(), query);
1361 }
1362 
1363