/** * EXM Database Administartion (Create Schema/Tables/Views etc) * $Id$ * $HeadURL$ * $Author$ * Created by Hanna E. Rathje, SLSE, 2015-09-15 */ #include "exercise_table_generator.h" #include #include #include #include #include #include #include #include namespace DBAdministration { ExerciseTableGenerator::ExerciseTableGenerator(NGL::INGLLogger &logger, const QString &xmlPath, QObject *parent) : QObject(parent) , m_xmlPath(xmlPath) , m_ownSchemaName(QLatin1String("exm")) , m_logger(logger) { } // -------------------------------------------------------------------------------------------------------- ExerciseTableGenerator::~ExerciseTableGenerator() { qDeleteAll(m_sqlGeneratorMap); } // -------------------------------------------------------------------------------------------------------- bool ExerciseTableGenerator::existsSchema(const QString &dbSchema, const QSqlDatabase &dbConn) const { NSqlQuery q(dbConn); // Does service schema exist? QString stmt = QLatin1String("SELECT EXISTS(SELECT 1 FROM information_schema.schemata") + QLatin1String(" WHERE schema_name = '") + dbSchema + QLatin1String("')"); if (!q.execAndFetch(stmt)) return false; return q.value(0).toBool(); } // -------------------------------------------------------------------------------------------------------- //bool ExerciseTableGenerator::createDbSchemaIfNotExists(const QString &dbSchema) const //{ // // TODO: make product-dependent // QString dbUser = QLatin1String("ets_") + dbSchema; // return createDbSchemaIfNotExists(dbSchema, dbUser); //} // -------------------------------------------------------------------------------------------------------- bool ExerciseTableGenerator::createDbSchemaIfNotExists(const QString &dbSchema, QString dbUser) const { DEBUG_LOG(m_logger) << "createDbSchema: dbSchema = " << dbSchema << ", dbUser = " << dbUser; if (existsSchema(dbSchema, m_database)) return true; // Schema doesn't exist => create QString stmt = QLatin1String("CREATE SCHEMA IF NOT EXISTS ") + dbSchema; DEBUG_LOG(m_logger) << "# " << stmt; NSqlQuery q(m_database); if (!q.exec(stmt)) { ERROR_LOG(m_logger) << "Can't create schema '" << dbSchema << "' : " << q.getLastSqlError(); return false; } // Create specific user for schema (if exercise-related) if (dbSchema.startsWith(QLatin1String("ex_"))) { dbUser = QStringLiteral("ets_ex_00"); stmt = QLatin1String("SELECT create_user_if_not_exists('") + dbUser + QLatin1String("');"); DEBUG_LOG(m_logger) << "# " << stmt; // Use stored procedure (=> create user only if not exists) if (!q.exec(stmt)) return false; // Add user to group _ro: not needed anymore, it is included in stored procedure // stmt = QLatin1String(" GRANT ets_ro TO ") + dbUser; // DEBUG_LOG(m_logger) << "# " << stmt; // if (!q.exec(stmt)) // return false; } // TODO: make dba user & db groups depend on product => Check/product Name needed somewhere: lts-cc.conf? // HR: 09.03.2016 stmt = QLatin1String("GRANT USAGE ON SCHEMA ") + dbSchema + QLatin1String(" TO ets_ro, ets_admin"); DEBUG_LOG(m_logger) << "# " << stmt; if (!q.exec(stmt)) return false; // create db_version table needed later for DB Updates QString stmtDbVersion = QLatin1String("CREATE TABLE IF NOT EXISTS ") + dbSchema + QLatin1String(".db_version (")+ QLatin1String("id serial, ")+ QLatin1String("description text, ")+ QLatin1String("version text, ")+ QLatin1String("updated bigint DEFAULT (extract(epoch from now()) * 1000), ")+ QLatin1String("success boolean DEFAULT true, ")+ QLatin1String("message text);"); stmtDbVersion += QLatin1String("\nINSERT INTO ") + dbSchema + QLatin1String(".db_version (description, version) VALUES ('Create db_version table for all existent DB schemas', '1.00')"); if (!q.exec(stmtDbVersion)) WARNING_LOG(m_logger) << "DB: Could not create db_version table for schema " << dbSchema << "! Error code: " << q.lastError().nativeErrorCode(); else INFO_LOG(m_logger) << "DB: created db_version table for schema " << dbSchema; INFO_LOG(m_logger) << "########## DbSchema " << dbSchema << " created with corresponding rights."; return true; } // -------------------------------------------------------------------------------------------------------- bool ExerciseTableGenerator::dropDbSchema(const QString &dbSchema, const QString &dbUser) const { // Drop Schema QString stmt = QLatin1String("DROP SCHEMA IF EXISTS ") + dbSchema + QLatin1String(" CASCADE"); DEBUG_LOG(m_logger) << "# " << stmt; NSqlQuery q(m_database); if (!q.exec(stmt)) { ERROR_LOG(m_logger) << "Can't drop schema '" << dbSchema << "' : " << q.getLastSqlError(); return false; } // drop only "old" users: ets_ex_42 etc if (!dbUser.isEmpty() && dbUser != QStringLiteral("ets_ex_00")) { // Drop related user stmt = QLatin1String("DROP USER ") + dbUser; if (!q.exec(stmt)) WARNING_LOG(m_logger) << "Can't drop user '" << dbUser << "' : " << q.getLastSqlError(); } return true; } /** * @brief Checks if table exists / is available for dbUser * (dbUser has the right to see it) * @param tableName * @param dbSchema * @return true on success */ // -------------------------------------------------------------------------------------------------------- bool ExerciseTableGenerator::existsTable(const QString &tableName, const QString &dbSchema) const { QString stmt = QLatin1String("SELECT EXISTS (" " SELECT 1 FROM information_schema.tables " " WHERE table_schema = '") + dbSchema + QLatin1String("'" " AND table_name = '") + tableName + QLatin1String("')"); DEBUG_LOG(m_logger) << "stmt: " << stmt; NSqlQuery q(m_database); if (!q.execAndFetch(stmt)) { INFO_LOG(m_logger) << "Table " << tableName << " does not exist in schema " << dbSchema; return false; } return q.value(0).toBool(); } /** * @brief ExerciseTableGenerator::init * @param createOwnSchema * @return */ // -------------------------------------------------------------------------------------------------------- bool ExerciseTableGenerator::init(const QSqlDatabase &database, const QString &ownSchemaName, bool createOwnSchema) { m_database = database; m_ownSchemaName = ownSchemaName; if (!createOwnSchema && !existsSchema(m_ownSchemaName, m_database)) { ERROR_LOG(m_logger) << "Schema of the service (" << m_ownSchemaName << ") doesn't exist, aborting." ; return false; } resetSearchPath(); // Read all xmlFiles in xmlPath DEBUG_LOG(m_logger) << "xmlPath: " << m_xmlPath; QFileInfo fi(m_xmlPath); if (!fi.isDir()) { ERROR_LOG(m_logger) << "Given xml path '" << m_xmlPath << "' is not a directory"; return false; } QDir xmlDir(m_xmlPath); // Read xmlFiles only QStringList filters; filters << QLatin1String("*.xml"); xmlDir.setNameFilters(filters); qDeleteAll(m_sqlGeneratorMap); m_sqlGeneratorMap.clear(); QDirIterator dirIt(xmlDir); while (dirIt.hasNext()) { const QString xmlFile = dirIt.next(); DEBUG_LOG(m_logger) << "xmlFile: " << xmlFile; // Init sqlGenerator for xmlFile CodeGen::SqlGenerator *sqlGen = new CodeGen::SqlGenerator(xmlFile); if (sqlGen->generateData(0) && sqlGen->isValid()) m_sqlGeneratorMap.insert(sqlGen->getServiceAbbr(), sqlGen); else delete sqlGen; } DEBUG_LOG(m_logger) << "sqlGen mapSize: " << m_sqlGeneratorMap.size(); emit aliveSignal(); if (createOwnSchema) { // Create Schema exm (i.e. for testing) // TODO: make product-dependent QString dbUser = QLatin1String("ets_exm"); if (!createDbSchemaIfNotExists(m_ownSchemaName, dbUser)) return false; if (!executeSqlTableRelated(QStringLiteral("exm"), m_ownSchemaName, dbUser)) return false; } return true; } void ExerciseTableGenerator::close() { m_database = QSqlDatabase(); } // Write sql-stmts for a service into a file // -------------------------------------------------------------------------------------------------------- bool ExerciseTableGenerator::writeServiceSqlToFile(const QString &svcName, const QString &dbSchema) { if (!m_sqlGeneratorMap.contains(svcName)) return false; DEBUG_LOG(m_logger) << "Write sql for service '" << svcName << "' with schema '" << dbSchema << "'"; CodeGen::SqlGenerator *sqlGen = m_sqlGeneratorMap.value(svcName); // do not write to share/Lts/sql to not overwrite the generated files from codegen QString fullSqlName = QDir::tempPath() + QLatin1Char('/') + svcName + QLatin1Char('_') + dbSchema + QLatin1String(".sql"); if (!sqlGen->writeSqlToFile(dbSchema, fullSqlName, CodeGen::ExerciseDependency::Exercise)) { ERROR_LOG(m_logger) << "Failed to write sql-stmt to file " << fullSqlName; return false; } DEBUG_LOG(m_logger) << "## => Written sql-stmt to '" << fullSqlName << "'"; return true; } // -------------------------------------------------------------------------------------------------------- bool ExerciseTableGenerator::executeSql(const QString &svcName, const QString &dbSchema, QStringList &handledServices, const CodeGen::ExerciseDependency &ed) { // TODO: make product-dependent QString dbUser; if (dbSchema.startsWith(QLatin1String("ex_"))) dbUser = QLatin1String("ets_ex_00"); else dbUser = QLatin1String("ets_") + dbSchema; return executeSql(svcName, dbSchema, dbUser, handledServices, ed); } /** * @brief Execute all sql-stmts for a service * @param svcName * @param dbSchema * @param dbUser * @return */ // -------------------------------------------------------------------------------------------------------- bool ExerciseTableGenerator::executeSql(const QString &svcName, const QString &dbSchema, const QString &dbUser, QStringList &handledServices, const CodeGen::ExerciseDependency &ed) { CodeGen::SqlGenerator *sqlGen = m_sqlGeneratorMap.value(svcName); if (!sqlGen) return false; // check if this service was already handled if (handledServices.contains(svcName)) { INFO_LOG(m_logger) << "Service already in handledServices: " << svcName; return true; } handledServices += svcName; INFO_LOG(m_logger) << "Added service to handledServices: " << svcName; //check if schema is empty at beginning, after executing some statements is not empty anymore // for exercise dependent services we execute all statements bool schemaEmpty = schemaIsEmpty(dbSchema) || ed == CodeGen::ExerciseDependency::Exercise; // first execute all depedent services const auto deps = sqlGen->getReferencedServices(); for (const QString &depSvc : deps) { if (!executeSql(depSvc, dbSchema, dbUser, handledServices, ed)) { ERROR_LOG(m_logger) << "Failed to execute sql " << depSvc; return false; } } const QStringList stmts = sqlGen->getAllSqlStmts(dbSchema, ed); NSqlQuery q(m_database); int stmtExecuted = 0; for (const QString &stmt : stmts) { if (stmt.isEmpty() || stmt.startsWith(QLatin1String("--")) || stmt.startsWith(QLatin1String("\n--"))) continue; if ((stmt.startsWith(QLatin1String("DROP TABLE")) || stmt.startsWith(QLatin1String("INSERT INTO"))) && !schemaEmpty) continue; if ((stmt.startsWith(QLatin1String("DROP SEQUENCE")) || stmt.startsWith(QLatin1String("CREATE SEQUENCE"))) && !schemaEmpty) continue; if (!q.exec(stmt)) { ERROR_LOG(m_logger) << "Error while executing stmt: '" << stmt << "' : " << q.getLastSqlError(); return false; } checkAliveTimer(); NOTICE_LOG(m_logger) << "Statement executed: " << stmt; ++stmtExecuted; } DEBUG_LOG(m_logger) << "#" << stmtExecuted << " stmts executed."; return true; } // -------------------------------------------------------------------------------------------------------- bool ExerciseTableGenerator::schemaIsEmpty(const QString &dbSchema) const { NSqlQuery q(m_database); QString stmt = QLatin1String("SELECT COUNT(*) FROM information_schema.tables") + QLatin1String(" WHERE table_schema = '") + dbSchema + QLatin1String("'"); if (!q.exec(stmt)) { INFO_LOG(m_logger) << "Schema does not exist : " << dbSchema; return false; } q.next(); if (q.value(0).toInt() > 0) return false; return true; } /** * @brief Execute only sql stmts for tables & table-related items (Sequences/PK/FK/Indexes) * * Note: sql stmts have an 'CREATE <> IF NOT EXISTS' * Additional check for tableExists necessary for inserting data * @param svcName * @param dbSchema * @param dbUser * @return */ // -------------------------------------------------------------------------------------------------------- bool ExerciseTableGenerator::executeSqlTableRelated(const QString &svcName, const QString &dbSchema, const QString &dbUser) { if (!m_sqlGeneratorMap.contains(svcName)) return false; const CodeGen::SqlGenerator *sqlGen = m_sqlGeneratorMap.value(svcName); const CodeGen::SqlGenerator::SqlStatements sqlStatements = sqlGen->getStatements(CodeGen::ExerciseDependency::Global); const CodeGen::SqlGenerator::SqlItems sqlItems = sqlGen->getSqlItems(CodeGen::ExerciseDependency::Global); // 1. Step: Check which tables will be created QStringList tablesNewlyCreated; for (const QString &tableName : sqlItems.tables) { if (!existsTable(tableName, dbSchema)) tablesNewlyCreated += tableName; } // 2. Step: Set db-environment (privileges/search path) INFO_LOG(m_logger) << "Create TABLES for dbSchema = " << dbSchema << ", dbUser = " << dbUser; QStringList sqlStmts = sqlGen->getDbSettings(dbSchema); // 3. Step: execute stmts // Note: create sequence if not exists in postgresql 9.5 sqlStmts += sqlStatements.createTables; if (!executeStmts(sqlStmts)) return false; INFO_LOG(m_logger) << " #" << tablesNewlyCreated.size() << " tables created. "; // Primary Keys if (!executsSqlConstraints(sqlItems.primaryKeys, sqlStatements.createPrimaryKeys, dbSchema)) return false; // TODO: Foreign Keys //if (!executsSqlConstraints(sqlGen->getForeignKeys(), sqlStatements.createForeignKeys, dbSchema)) // return false; // Indexes if (!executsSqlConstraints(sqlItems.constraints, sqlStatements.createConstraints, dbSchema)) return false; // 4. Step: Handle sequences separately sqlStmts = sqlStatements.createSequences; int listIndexStmts = 0; for (QString sequenceName : sqlItems.sequences) { // TODO: remove when using postgresql 9.5 feature create sequence if not exists sequenceName.remove(dbSchema + QLatin1Char('.')); QString stmt = QLatin1String("SELECT exists_sequence('") + sequenceName + QLatin1String("', '") + dbSchema + QLatin1String("')"); NSqlQuery q(m_database); if (!q.execAndFetch(stmt)) return false; // Sequence exists? if (!q.value(0).toBool()) q.exec(sqlStmts.at(listIndexStmts)); listIndexStmts++; } bool ok = true; for (const QString &table : qAsConst(tablesNewlyCreated)) { if (!insertTableData(table, dbSchema, sqlGen)) { ERROR_LOG(m_logger) << "Failed to insert data for table " << dbSchema << "." << table; ok &= false; } } return ok; } // -------------------------------------------------------------------------------------------------------- bool ExerciseTableGenerator::executsSqlConstraints(const QStringList &constraintList, const QStringList &sqlStmts, const QString &dbSchema) { int listIndexStmts = 0; int createdConstraints = 0; for (const QString &constraintName : constraintList) { DEBUG_LOG(m_logger) << "Check constraint '" << constraintName << "'"; // TODO: remove when using postgresql 9.6 feature create constraint if not exists QString stmt = QLatin1String("SELECT exists_constraint('") + constraintName.toLower() + QLatin1String("', '") + dbSchema + QLatin1String("', '") + QLatin1String("i')"); //DEBUG_LOG(m_logger) << "stmt = " << stmt; NSqlQuery q(m_database); if (!q.execAndFetch(stmt)) return false; // Constraint exists? if (!q.value(0).toBool()) { DEBUG_LOG(m_logger) << "Constraint '" << constraintName << "' does not exist, creating .."; if (!q.exec(sqlStmts.at(listIndexStmts))) return false; createdConstraints++; } listIndexStmts++; } INFO_LOG(m_logger) << " #" << createdConstraints << " constraints created. "; return true; } void ExerciseTableGenerator::checkAliveTimer() { if (m_emitAliveTimer.elapsed() > 2000) // longer than 2 sec -> send heartbeat { emit aliveSignal(); m_emitAliveTimer.start(); } } // (Re)Create sql statements for given exercise // -------------------------------------------------------------------------------------------------------- void ExerciseTableGenerator::generateSqlDataForExercise(int exerciseId) { for (CodeGen::SqlGenerator *sqlGen : qAsConst(m_sqlGeneratorMap)) { // recreate sql statement with correct schema name (e.g. for serials) if (!sqlGen->generateData(exerciseId)) ERROR_LOG(m_logger) << "Error generating sql for exercise " << exerciseId; } } /** * @brief ExerciseTableGenerator::executeStmts * @param stmts * @return true on success */ // -------------------------------------------------------------------------------------------------------- bool ExerciseTableGenerator::executeStmts(const QStringList &stmts) { NSqlQuery q(m_database); int stmtExecuted = 0; for (const QString &stmt : stmts) { if (stmt.isEmpty() || stmt.startsWith(QLatin1String("--")) || stmt.startsWith(QLatin1String("\n--"))) continue; if (!q.exec(stmt)) { ERROR_LOG(m_logger) << "Error while executing stmt: '" << stmt << "' : " << q.getLastSqlError(); return false; } DEBUG_LOG(m_logger) << "Executed statement: " << stmt; ++stmtExecuted; } DEBUG_LOG(m_logger) << "#" << stmtExecuted << " stmts executed."; return true; } /** * @brief ExerciseTableGenerator::insertTableData * insert possible static data for newly created table * @param table * @param dbSchema * @return true on success */ // -------------------------------------------------------------------------------------------------------- bool ExerciseTableGenerator::insertTableData(const QString &table, const QString &dbSchema, const CodeGen::SqlGenerator *sqlGen) { // inserts are only enumValues const QStringList &enumInserts = sqlGen->getEnumerationInsertValues(table, dbSchema); if (!executeStmts(enumInserts)) return false; INFO_LOG(m_logger) << " #" << enumInserts.count() << " rows inserted for " << dbSchema << "." << table; // TODO: add other static data return true; } // -------------------------------------------------------------------------------------------------------- // Write sql-stmts to a file for a specific exercise // -------------------------------------------------------------------------------------------------------- bool ExerciseTableGenerator::writeExerciseServicesSql(qint32 exerciseId) { bool ok = false; QString schemaName = DBCommon::getExerciseSchemaName(exerciseId); for (auto it = m_sqlGeneratorMap.constBegin(); it != m_sqlGeneratorMap.constEnd(); ++it) { if (!it.value()->isServiceExerciseIndependent()) ok &= writeServiceSqlToFile(it.key(), schemaName); } return ok; } /** * @brief getAvailableExerciseServices * @return */ // -------------------------------------------------------------------------------------------------------- QStringList ExerciseTableGenerator::getAvailableExerciseServices() const { QStringList services; for (auto it = m_sqlGeneratorMap.constBegin(); it != m_sqlGeneratorMap.constEnd(); ++it) { if (!it.value()->isServiceExerciseIndependent()) services.append(it.key()); } return services; } // -------------------------------------------------------------------------------------------------------- QStringList ExerciseTableGenerator::getAvailableGlobalServices() const { QStringList services; for (auto it = m_sqlGeneratorMap.constBegin(); it != m_sqlGeneratorMap.constEnd(); ++it) { // if (it.value()->isServiceExerciseIndependent() || it.value()->getExerciseDependency() == CodeGen::ExerciseDependency::GlobalAndExercise) if (it.value()->isServiceGlobal()) { services.append(it.key()); DEBUG_LOG(m_logger) << "Global service found: " << it.key(); } } return services; } // Execute sql-stmts for a specific exercise (for all exercise-depended services) // -------------------------------------------------------------------------------------------------------- bool ExerciseTableGenerator::executeSql(qint32 exerciseId) { const QStringList services = getAvailableExerciseServices(); // Define schema for given exerciseId QString schemaName = DBCommon::getExerciseSchemaName(exerciseId); DEBUG_LOG(m_logger) << "Create schema for EXERCISE " << schemaName; generateSqlDataForExercise(exerciseId); m_emitAliveTimer.start(); // Create dbSchema & tables, .. in a transaction // => can be reverted if an error occured DBTransaction trans(m_database); QStringList handledServices; for (const QString &svcName : services) { CodeGen::SqlGenerator *sqlGen = m_sqlGeneratorMap.value(svcName); if (!sqlGen) { ERROR_LOG(m_logger) << "Can not find sqlgen for service " << svcName << " - Skipping!"; continue; } DEBUG_LOG(m_logger) << "-----------------------------------------------------------------------------"; DEBUG_LOG(m_logger) << "Create exercise dependent tables/views/indexes/.. for service '" << svcName << "', exercise: " << exerciseId; if (!executeSql(svcName, schemaName, handledServices, CodeGen::ExerciseDependency::Exercise)) { ERROR_LOG(m_logger) << "Failed to create db-environment (tables/views/indexes/..) for service '" << svcName << "'"; trans.rollback(); return false; } checkAliveTimer(); QFile predefinedSql; QString filePath = m_xmlPath; filePath.replace(QLatin1String("xml"), QLatin1String("sql")); DEBUG_LOG(m_logger) << "File Path: " << filePath; predefinedSql.setFileName(filePath + svcName + QLatin1String("_predefined_data.sql")); DEBUG_LOG(m_logger) << "Predefined filename " << predefinedSql.fileName(); if (predefinedSql.exists()) { if (!predefinedSql.open(QIODevice::ReadOnly | QIODevice::Text)) return false; QStringList predefinedInserts; QTextStream textStream(&predefinedSql); while (!textStream.atEnd()) { QString line = textStream.readLine(); line = line.replace(QStringLiteral("%ex_id%"), QString::number(exerciseId)); DEBUG_LOG(m_logger) << "Line: " << line; predefinedInserts << line; } predefinedSql.close(); DEBUG_LOG(m_logger) << "Executing predefined statements: \n" << predefinedInserts.join(QStringLiteral("\n")); if (!executeStmts(predefinedInserts)) return false; checkAliveTimer(); } INFO_LOG(m_logger) << "db-environment (tables/views/indexes/..) created for service '" << svcName << "'"; } // Commit all dbChanges trans.commit(); resetSearchPath(); return true; } // Execute sql-stmts for all exercise-independent services // -------------------------------------------------------------------------------------------------------- bool ExerciseTableGenerator::executeSql() { const QStringList services = getAvailableGlobalServices(); DEBUG_LOG(m_logger) << "Found global services :" << services.size() ; // Create dbSchema & tables, .. in a transaction // => can be reverted if an error occured DBTransaction trans(m_database); QStringList handledServices; NSqlQuery querySchemaExists(m_database); // querySchemaExists.prepare(QStringLiteral("SELECT count(*) FROM information_schema.schemata where schema_name = :sn")); querySchemaExists.prepare(QStringLiteral("SELECT count(*) FROM information_schema.tables where table_schema = :sn")); for (const QString &svc : services) { // we should not recreate already existing schemas since this will destroy FKs from ex_N.foo.bar_id to .bar.id querySchemaExists.bindValue(QStringLiteral(":sn"), svc); if (querySchemaExists.execAndFetch()) { const int num = querySchemaExists.value(0).toInt(); if (num > 0) { DEBUG_LOG(m_logger) << "Schema " << svc << " already exists - skip creating tables for this service"; continue; } } DEBUG_LOG(m_logger) << "-------------------------------------------------------------------------------"; DEBUG_LOG(m_logger) << "Create exercise independent tables/views/indexes/.. for service '" << svc << "'"; if (!executeSql(svc, svc, handledServices, CodeGen::ExerciseDependency::Global)) { ERROR_LOG(m_logger) << "Failed to create db-environment (tables/views/indexes/..) for service '" << svc << "'"; trans.rollback(); return false; } INFO_LOG(m_logger) << "db-environment (tables/views/indexes/..) created for service '" << svc << "'"; } // Commit all dbChanges trans.commit(); resetSearchPath(); return true; } // Convenience functions // -------------------------------------------------------------------------------------------------------- bool ExerciseTableGenerator::createExerciseSchema(qint32 exerciseId) { // Define schema for given exerciseId QString dbSchema = DBCommon::getExerciseSchemaName(exerciseId); QString dbUser = QLatin1String("ets_ex_00"); return createDbSchemaIfNotExists(dbSchema, dbUser); } // -------------------------------------------------------------------------------------------------------- bool ExerciseTableGenerator::dropExerciseSchema(qint32 exerciseId, bool bDropUser) { // Define schema for given exerciseId QString dbSchema = DBCommon::getExerciseSchemaName(exerciseId); // TODO: make product-depended return dropDbSchema(dbSchema, bDropUser ? QLatin1String("ets_") + dbSchema : QString()); } // -------------------------------------------------------------------------------------------------------- bool ExerciseTableGenerator::resetSearchPath() const { // Reset of SEARCH_PATH (has changed in the process of creating a db) NSqlQuery q(m_database); QString stmt = QLatin1String("SET SEARCH_PATH TO ") + m_ownSchemaName + QLatin1String(", PUBLIC"); return q.exec(stmt); } // -------------------------------------------------------------------------------------------------------- bool ExerciseTableGenerator::isServiceGlobal(const QString &svcAbbr) const { CodeGen::SqlGenerator *sqlGen = m_sqlGeneratorMap.value(svcAbbr); if (!sqlGen) return false; return sqlGen->isServiceGlobal(); } } // namespace DBAdministration