Bonjour à tous,
J'ai modifié le script de création de la base de donnée de façon à ce qu'il soit compatible avec SQLite.
Voici ce qu'il y avait avant:
(au passage, il y a une erreur dans le fetch, il est indiqué le champ "id=" au lieu de "name="):
Et voici une version compatible avec SQLite:
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116 void updateDatabaseVersion() { try { int domainVersion = qApp->property("DomainVersion").toInt(); // On se connecte avec un utilisateur de la base de données qui a les droits de modifications du schéma QSqlDatabase db = qx::QxSqlDatabase::getSingleton()->getDatabaseCloned(); db.setUserName("MyAdminLogin"); db.setPassword("MyAdminPassword"); // On s'assure que la session démarre une transaction et lève une exception à la moindre erreur qx::QxSession session(db, true, true); // On "fetch" la version de la base de données avec un verrou pour éviter les modifications concurrentes ! // Si plusieurs utilisateurs lancent l'application en même temps et qu'une mise à jour // est nécessaire, le premier fera la mise à jour, et les autres seront en attente DatabaseVersion dbVersion; session.fetchByQuery(qx_query("WHERE id='MyAppName' FOR UPDATE"), dbVersion); // Pour les autres utilisateurs, une fois le verrou levé, on vérifie si la mise à jour est toujours nécessaire if (dbVersion.version >= domainVersion) { return; } // On exécute chaque instruction SQL avec la variable "query" QSqlQuery query(db); // On récupère toutes les classes persistantes C++ enregistrées dans le contexte QxOrm qx::QxCollection<QString, qx::IxClass *> * pAllClasses = qx::QxClassX::getAllClasses(); if (! pAllClasses) { qAssert(false); return; } // on récupère la liste des tables existantes dans la base (fonction de Qt) QStringList tables = db.tables(); for (long k = 0; k < pAllClasses->count(); k++) { qx::IxClass * pClass = pAllClasses->getByIndex(k); if (! pClass) { continue; } // Filtre les classes non persistantes if (pClass->isKindOf("qx::service::IxParameter") || pClass->isKindOf("qx::service::IxService")) { continue; } // Filtre les classes à jour : si la version de pClass est <= à la version enregistrée dans la base, la mise à jour n'est pas nécessaire if (pClass->getVersion() <= dbVersion.version) { continue; } // On crée la table si elle n'existe pas, et on définit son propriétaire if (! tables.contains(pClass->getName())) { query.exec("CREATE TABLE " + pClass->getName() + " ( ) WITH (OIDS = FALSE);" "ALTER TABLE " + pClass->getName() + " OWNER TO \"MyAdminLogin\";"); session += query.lastError(); } // On ajoute les colonnes à la table si elles n'existent pas qx::IxDataMemberX * pDataMemberX = pClass->getDataMemberX(); for (long l = 0; (pDataMemberX && (l < pDataMemberX->count_WithDaoStrategy())); l++) { qx::IxDataMember * p = pDataMemberX->get_WithDaoStrategy(l); if (! p || (p->getVersion() <= dbVersion.version)) { continue; } query.exec("ALTER TABLE " + pClass->getName() + " ADD COLUMN " + p->getName() + " " + p->getSqlType() + ";"); session += query.lastError(); if (p->getIsPrimaryKey()) // PRIMARY KEY { query.exec("ALTER TABLE " + pClass->getName() + " ADD PRIMARY KEY (" + p->getName() + ");"); session += query.lastError(); } if (p->getAllPropertyBagKeys().contains("INDEX")) // INDEX { query.exec("CREATE INDEX " + pClass->getName() + "_" + p->getName() + "_idx" + " ON " + pClass->getName() + " USING " + p->getPropertyBag("INDEX").toString() + " (" + p->getName() + ");"); session += query.lastError(); } if (p->getNotNull()) // NOT NULL { query.exec("ALTER TABLE " + pClass->getName() + " ALTER COLUMN " + p->getName() + " SET NOT NULL;"); session += query.lastError(); } if (p->getAutoIncrement()) // AUTO INCREMENT { query.exec("CREATE SEQUENCE " + pClass->getName() + "_" + p->getName() + "_seq" + "; " "ALTER TABLE " + pClass->getName() + "_" + p->getName() + "_seq" + " OWNER TO \"MyAdminLogin\"; " "ALTER TABLE " + pClass->getName() + " ALTER COLUMN " + p->getName() + " " + "SET DEFAULT nextval('" + pClass->getName() + "_" + p->getName() + "_seq" + "'::regclass);"); session += query.lastError(); } if (p->getDescription() != "") // DESCRIPTION { // $$ceci est un texte ne nécessitant pas de caractères d'échappement dans postgres grace aux doubles dolars$$ query.exec("COMMENT ON COLUMN " + pClass->getName() + "." + p->getName() + " IS $$" + p->getDescription() + "$$ ;"); session += query.lastError(); } } } // On enregistre la version courante de la base de données dbVersion.version = domainVersion; session.save(dbVersion); // Fin du block "try" : la session est détruite => commit ou rollback automatique // De plus, un commit ou rollback sur la transaction lève automatiquement le verrou posé précédemment } catch (const qx::dao::sql_error & err) { QSqlError sqlError = err.get(); qDebug() << sqlError.databaseText(); qDebug() << sqlError.driverText(); qDebug() << sqlError.number(); qDebug() << sqlError.type(); } }
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179 void updateDatabaseVersion() { try { int domainVersion = qApp->property( "DomainVersion" ).toInt(); // We connect to the database with an user that has rights for the database creation QSqlDatabase db = qx::QxSqlDatabase::getSingleton()->getDatabaseCloned(); db.setUserName( "root" ); db.setPassword( "" ); const bool isSQLite = db.driverName().contains( "SQLITE" ); // Create a transaction that will throw exception on errors qx::QxSession session( db, true, true ); // Fetch database version that will lock concurrent access (when multiple users starts the application at the same time) DatabaseVersion dbVersion; try { session.fetchByQuery( qx_query( QString( "WHERE name='") + kAppName + ( isSQLite ? "'" : QString("' FOR UPDATE") ) ), dbVersion ); // For other users, when the locking will be over, we check if the update is still needed if ( dbVersion.version >= domainVersion ) { return; } } catch ( const qx::dao::sql_error & ) { dbVersion.name = kAppName; dbVersion.version = -1; // Create tables } // Execute each query with this object QSqlQuery query( db ); // On récupère toutes les classes persistantes C++ enregistrées dans le contexte QxOrm qx::QxCollection<QString, qx::IxClass *> * pAllClasses = qx::QxClassX::getAllClasses(); if ( !pAllClasses ) { qAssert(false); return; } // Get database tables QStringList tables = db.tables(); for ( long k = 0; k < pAllClasses->count(); k++ ) { qx::IxClass * pClass = pAllClasses->getByIndex( k ); if ( !pClass ) { continue; } // Filter non persistant classes if ( pClass->isKindOf( "qx::service::IxParameter" ) || pClass->isKindOf( "qx::service::IxService" ) ) { continue; } if (pClass->getVersion() <= dbVersion.version) { continue; } // We create tables if not already existing, and we define its owner if ( !tables.contains( pClass->getName() ) ) { // Create attribute query query section QString attributes; qx::IxDataMemberX * pDataMemberX = pClass->getDataMemberX(); for ( long l = 0; (pDataMemberX && (l < pDataMemberX->count_WithDaoStrategy())); l++ ) { qx::IxDataMember * p = pDataMemberX->get_WithDaoStrategy(l); attributes += p->getName() + " " + p->getSqlType(); if (p->getNotNull()) // NOT NULL { attributes += " NOT NULL"; } if (p->getIsPrimaryKey()) // PRIMARY KEY { attributes += " PRIMARY KEY"; } if (p->getAutoIncrement()) // AUTO INCREMENT { attributes += " AUTOINCREMENT"; } if ( l < pDataMemberX->count_WithDaoStrategy() - 1 ) { attributes += ", "; } } attributes = QString( " (" ) + attributes + ")"; if ( !isSQLite ) { attributes += " WITH ( OIDS = FALSE )"; } query.exec( "CREATE TABLE " + pClass->getName() + attributes + ";" "ALTER TABLE " + pClass->getName() + " OWNER TO \"root\";" ); session += query.lastError(); // Create sequences & index for ( long l = 0; (pDataMemberX && ( l < pDataMemberX->count_WithDaoStrategy() ) ); l++ ) { qx::IxDataMember * p = pDataMemberX->get_WithDaoStrategy(l); if ( p->getAllPropertyBagKeys().contains( "INDEX" ) ) // INDEX { query.exec( "CREATE INDEX " + pClass->getName() + "_" + p->getName() + "_idx" + " ON " + pClass->getName() + " USING " + p->getPropertyBag("INDEX").toString() + " (" + p->getName() + ");" ); session += query.lastError(); } if (p->getAutoIncrement() && !isSQLite ) // AUTO INCREMENT { query.exec("CREATE SEQUENCE " + pClass->getName() + "_" + p->getName() + "_seq" + "; " "ALTER TABLE " + pClass->getName() + "_" + p->getName() + "_seq" + " OWNER TO \"root\"; " "ALTER TABLE " + pClass->getName() + " ALTER COLUMN " + p->getName() + " " + "SET DEFAULT nextval('" + pClass->getName() + "_" + p->getName() + "_seq" + "'::regclass);"); session += query.lastError(); } } } else { // We add columns to table if they don't exists qx::IxDataMemberX * pDataMemberX = pClass->getDataMemberX(); for ( long l = 0; (pDataMemberX && (l < pDataMemberX->count_WithDaoStrategy())); l++ ) { qx::IxDataMember * p = pDataMemberX->get_WithDaoStrategy(l); if (! p || (p->getVersion() <= dbVersion.version)) { continue; } query.exec( "ALTER TABLE " + pClass->getName() + " ADD COLUMN " + p->getName() + " " + p->getSqlType() + ";" ); session += query.lastError(); if ( p->getIsPrimaryKey() ) // PRIMARY KEY { query.exec( "ALTER TABLE " + pClass->getName() + " ADD PRIMARY KEY (" + p->getName() + ");" ); session += query.lastError(); } if ( p->getAllPropertyBagKeys().contains("INDEX") ) // INDEX { query.exec("CREATE INDEX " + pClass->getName() + "_" + p->getName() + "_idx" + " ON " + pClass->getName() + " USING " + p->getPropertyBag("INDEX").toString() + " (" + p->getName() + ");"); session += query.lastError(); } if (p->getNotNull()) // NOT NULL { query.exec("ALTER TABLE " + pClass->getName() + " ALTER COLUMN " + p->getName() + " SET NOT NULL;"); session += query.lastError(); } if (p->getAutoIncrement() && !isSQLite ) // AUTO INCREMENT { query.exec("CREATE SEQUENCE " + pClass->getName() + "_" + p->getName() + "_seq" + "; " "ALTER TABLE " + pClass->getName() + "_" + p->getName() + "_seq" + " OWNER TO \"root\"; " "ALTER TABLE " + pClass->getName() + " ALTER COLUMN " + p->getName() + " " + "SET DEFAULT nextval('" + pClass->getName() + "_" + p->getName() + "_seq" + "'::regclass);"); session += query.lastError(); } if (p->getDescription() != "") // DESCRIPTION { query.exec("COMMENT ON COLUMN " + pClass->getName() + "." + p->getName() + " IS $$" + p->getDescription() + "$$ ;"); session += query.lastError(); } } } } // Update database version dbVersion.version = domainVersion; try { session.save( dbVersion ); } catch (const qx::dao::sql_error & err) { session.insert( dbVersion ); } // End of "try" : Session is destroyed => automatic commit or rollback // commit or rollback will unlock locked table (not for SQLite) } catch (const qx::dao::sql_error & err) { QSqlError sqlError = err.get(); qDebug() << sqlError.databaseText(); qDebug() << sqlError.driverText(); qDebug() << sqlError.number(); qDebug() << sqlError.type(); } }
Si vous avez des suggestions, je suis preneur... J'ai simplement rendu le script fonctionnel avec SQLite, mais je ne connais pas très bien SQL donc je ne sais pas si c'est optimal.
En gros, voici ce qui ne passait pas:
- SELECT .... FOR UPDATE -> passe pas sous SQLite, j'ai l'impression que le locking n'est pas supporté par SQLite.
- query.exec("CREATE TABLE " + pClass->getName() + " ( ) WITH (OIDS = FALSE);" -> la création de table sans champs ne passe pas... Le WITH (OIDS = FALSE) non plus.
- CREATE SEQUENCE -> non supporté à priori. Tout semble être automatique au niveau des séquences (à vérifier).
Partager