#include "SqlGenerator.h" #include "StructBuilder.h" #include "XmlReader.h" #include #include #include #include using namespace CodeGen; static QString getExerciseSchemaName(int exerciseId) { return QStringLiteral("ex_") + QString::number(exerciseId); } SqlGenerator::SqlGenerator(XmlReader *xmlReader, StructBuilder *structBuilder) : m_xmlReader(xmlReader) , m_structBuilder(structBuilder) , m_createdByXmlFile(false) , m_isValid(true) , m_exerciseId(0) , m_logger(NGL::NGLLoggerBootstrap::getNGLRootLogger().getNGLLogger("CodeGen.SqlGenerator")) {} SqlGenerator::SqlGenerator(const QString &xmlFile) : m_xmlReader(new XmlReader) , m_structBuilder(new StructBuilder(m_xmlReader)) , m_createdByXmlFile(true) , m_isValid(false) , m_exerciseId(0) , m_logger(NGL::NGLLoggerBootstrap::getNGLRootLogger().getNGLLogger("CodeGen.SqlGenerator")) { m_isValid = m_xmlReader->readServiceFile(xmlFile, QString()) && m_structBuilder->buildStructures(); } SqlGenerator::~SqlGenerator() { if (m_createdByXmlFile) { delete m_xmlReader; delete m_structBuilder; } } bool SqlGenerator::generateData(int exerciseId) { DEBUG_LOG(m_logger) << "Generating SQL data for service " << getServiceAbbr() << " in exercise " << exerciseId << ", this: " << this; m_exerciseId = exerciseId; m_sqlGlobal = SqlStatements(); m_sqlExercise = SqlStatements(); m_sqlItemsGlobal = SqlItems(); m_sqlItemsExercise = SqlItems(); m_referencedSevices.clear(); if (!handleTables()) return false; if (!handleViews()) return false; if (!handleEnumerations()) return false; return true; } bool SqlGenerator::createStatementsForTable(const QString &tableName, const QString &tableNameOut) { const QMap tables = m_structBuilder->getDbTables(); for (const CompleteTableInformation &cti : tables) { if (cti.name != tableName) continue; CompleteTableInformation ctiOut = cti; ctiOut.name = tableNameOut; return handleTable(ctiOut); } return false; } bool SqlGenerator::handleTables() { const QMap tables = m_structBuilder->getDbTables(); for (const CompleteTableInformation &cti : tables) { if (!handleTable(cti)) return false; } const QMap recTables = m_structBuilder->getRecordingTopicTables(); MemberInformation miColumnIsValid; miColumnIsValid.db.columnName = QStringLiteral("dds_is_alive"); miColumnIsValid.db.datatype = QStringLiteral("boolean"); miColumnIsValid.db.defaultValue = QStringLiteral("true"); MemberInformation miInsertionTime; miInsertionTime.cpp.type = QStringLiteral("timestamp"); miInsertionTime.db.columnName = QStringLiteral("insert_time"); miInsertionTime.db.defaultValue = QStringLiteral("now()"); miInsertionTime.db.usedForInsert = false; m_structBuilder->fixupQVariantConversions(miInsertionTime); ConstraintInformation ci; ci.name = QStringLiteral("IDX_sample_time"); ci.affectedColumns = QStringList({QStringLiteral("sample_time")}); ci.type = ConstraintInformation::Type::index; for (const CompleteTableInformation &cti : recTables) { CompleteTableInformation ctiTmp(cti); ctiTmp.columns.push_back(miColumnIsValid); ctiTmp.columns.push_back(miInsertionTime); ctiTmp.constraints.push_back(ci); if (!handleTable(ctiTmp)) return false; } return true; } bool SqlGenerator::handleViews() { const QMap views = m_structBuilder->getDbViews(); for (const ViewInformation &vi : views) { if (!handleView(vi)) return false; } return true; } bool SqlGenerator::handleView(const ViewInformation &vi) { DEBUG_LOG(m_logger) << "Handle view " << vi.name; const QString parent = vi.baseTable; DEBUG_LOG(m_logger) << "Parent = " << parent; if (!parent.isEmpty()) { const CompleteStructInformation csi = m_structBuilder->structInformationByTableName(parent); const QString createViewStmt(QStringLiteral("CREATE OR REPLACE VIEW %1 AS SELECT %2\nFROM %3\nWHERE %4;\n")); const QString dropViewStmt(QStringLiteral("DROP VIEW IF EXISTS %1;")); DEBUG_LOG(m_logger) << "csi.primaryTable = " << csi.primaryTable.data()->name; if (!csi.primaryTable.isNull()) { QStringList fromTables; fromTables += vi.baseSchema+QLatin1Char('.')+vi.baseTable; QStringList whereConditions; if (vi.filter.contains(QLatin1String("exercise_id"))) whereConditions += QLatin1String("exercise_id=")+QString::number(m_exerciseId); QStringList fqColumnNames; for (const MemberInformation &mi : csi.member) { if (mi.db.usedForView) fqColumnNames += mi.db.table + QLatin1Char('.') + mi.db.columnName + QLatin1String(" AS ") + mi.db.getColumnNameView(); } DEBUG_LOG(m_logger) << "fqColumnNames = " << fqColumnNames.join(QStringLiteral(", ")); if (!whereConditions.isEmpty()) addToCreateViews(createViewStmt.arg(csi.viewName, fqColumnNames.join(QStringLiteral(", ")), fromTables.join(QStringLiteral(", ")), whereConditions.join(QStringLiteral(" AND\n"))), vi.exerciseDependency); addToDropViews(dropViewStmt.arg(csi.viewName), vi.exerciseDependency); } } return true; } bool SqlGenerator::handleTable(const CompleteTableInformation &cti) { DEBUG_LOG(m_logger) << "Handle table " << cti.name; if (cti.columns.isEmpty()) { WARNING_LOG(m_logger) << "Table " << cti.name << " has no DatabaseServiceStruct - not generating anything for this!"; return true; } const QString dropTableStmt(QStringLiteral("DROP TABLE IF EXISTS %1 CASCADE;")); const QString createTableStmt(QStringLiteral("CREATE TABLE IF NOT EXISTS %1 (%2\n);")); const QString commentTableStmt(QStringLiteral("COMMENT ON TABLE %1 IS '%2';")); QStringList tableComments; addToDropTables(dropTableStmt.arg(cti.name), cti.exerciseDependency); if ((cti.exerciseDependency & Global) == Global) { addToCreateTables(createTableStmt.arg(cti.name, getTableColumnCreateStatements(cti.name, cti.columns, &tableComments, Global)), Global); } if ((cti.exerciseDependency & Exercise) == Exercise) { addToCreateTables(createTableStmt.arg(cti.name, getTableColumnCreateStatements(cti.name, cti.columns, &tableComments, Exercise)), Exercise); } addToTables(cti.name, cti.exerciseDependency); if (!cti.comment.isEmpty()) addToCreateTables(commentTableStmt.arg(cti.name, fixupComment(cti.comment)), cti.exerciseDependency); addToCreateTables(tableComments, cti.exerciseDependency); const QString parent = m_xmlReader->getStruct2BaseClassRelations().value(cti.name); const CompleteStructInformation csi = m_structBuilder->structInformationByTableName(cti.name); if (!parent.isEmpty()) { const QString createViewStmt(QStringLiteral("CREATE OR REPLACE VIEW %1 AS SELECT %2\nFROM %3\nWHERE %4;\n")); const QString dropViewStmt(QStringLiteral("DROP VIEW IF EXISTS %1;")); if (!csi.primaryTable.isNull()) { QStringList fqColumnNames; QStringList fromTables; QStringList whereConditions; fromTables += cti.name; for (const MemberInformation &mi : csi.member) { if (mi.db.usedForView) fqColumnNames += mi.db.table + QLatin1Char('.') + mi.db.columnName + QLatin1String(" AS ") + mi.db.getColumnNameView(); if (mi.cpp.isReadOnly && mi.db.isForeignKey) { for (const QSharedPointer &tiPtr : csi.tables) { if (tiPtr->name == mi.db.table) { const QSharedPointer fki = tiPtr->fkInformationForColumn(mi.db.table, mi.db.columnName); if (!fki.isNull()) { if (!fromTables.contains(fki->destination.tableName)) { whereConditions += fki->destination.tableName + QLatin1Char('.') + fki->destination.columnName + QLatin1String(" = ") + fki->source.tableName + QLatin1Char('.') + fki->source.columnName; fromTables += fki->destination.tableName; } } break; } } } } if (!whereConditions.isEmpty()) { addToCreateViews(createViewStmt.arg(csi.viewName, fqColumnNames.join(QStringLiteral(", ")), fromTables.join(QStringLiteral(", ")), whereConditions.join(QStringLiteral(" AND\n"))), cti.exerciseDependency); } addToDropViews(dropViewStmt.arg(csi.viewName), cti.exerciseDependency); } } if (!generateDbNotifications(cti.name, csi.dbNotifications, cti.exerciseDependency)) return false; generateForeignKeys(cti.foreignKeys, cti.exerciseDependency); generateConstraints(cti.name, cti.constraints, cti.exerciseDependency); return true; } QString SqlGenerator::getTableColumnCreateStatements(const QString &tableName, const QVector &columns, QStringList *tableComments, ExerciseDependency ed) { Q_ASSERT(ed == Global || ed == Exercise); const QString commentColumnStmt(QStringLiteral("COMMENT ON COLUMN %1 IS '%2';")); int maxLength = 1; for (const MemberInformation &mi : columns) maxLength = std::max(maxLength, mi.db.columnName.length()); maxLength += 1; QStringList retCols; retCols.reserve(columns.size() + 1); retCols += QString(); for (int i = 0; i < columns.size(); ++i) { const MemberInformation &mi = columns.at(i); const FullQualifiedColumnInformation &ci = mi.db; const QString spaces(maxLength - ci.columnName.length(), QLatin1Char(' ')); QString column = ci.columnName + spaces + ci.datatype; if (ci.hasSequence) { const QString seqDescr = generateSequence(tableName, ci, ed); QString schema; if (ed == Global) schema = m_xmlReader->getServiceAbbr(); else schema = getExerciseSchemaName(m_exerciseId); column += QLatin1String(" DEFAULT nextval(( '") + schema + QLatin1Char('.') + seqDescr + QLatin1String("'::text)::regclass)"); } if (!ci.canBeNull) column += QLatin1String(" NOT NULL"); if (!ci.hasSequence && !ci.defaultValue.isEmpty()) column += QLatin1String(" DEFAULT ") + ci.defaultValue; if (i < columns.size() - 1) column += QLatin1Char(','); if (!ci.comment.isEmpty()) { const QString comment(fixupComment(ci.comment)); const QString fqn = ci.table + QLatin1Char('.') + ci.columnName; *tableComments += commentColumnStmt.arg(fqn, comment); column += QLatin1String(" -- ") + comment; } retCols += column; if (ci.isPrimaryKey) generatePrimaryKey(tableName, ci, ed); } return retCols.join(QStringLiteral("\n ")); } QString SqlGenerator::generateSequence(const QString &tableName, const FullQualifiedColumnInformation &ci, ExerciseDependency ed) { const QString sequenceName = tableName + QLatin1Char('_') + ci.columnName + QLatin1String("_seq"); const QString createSequenceStmt(QLatin1String("CREATE SEQUENCE IF NOT EXISTS %1 INCREMENT %2 START %3 MINVALUE %4;")); const int minValue = ci.sequenceStart < 1 ? ci.sequenceStart : 1; addToCreateSequences(createSequenceStmt.arg(sequenceName).arg(ci.sequenceIncrement).arg(ci.sequenceStart).arg(minValue), ed); addToSequences(sequenceName, ed); return sequenceName; } void SqlGenerator::generatePrimaryKey(const QString &tableName, const FullQualifiedColumnInformation &ci, ExerciseDependency ed) { QString pkName = QLatin1String("PK_") + tableName + QLatin1Char('_') + ci.columnName; if (pkName.size() > 63) pkName.resize(63); addToPks(pkName, ed); const QString pkConstraint(QStringLiteral("ALTER TABLE IF EXISTS %1 ADD CONSTRAINT %2 PRIMARY KEY (%3);")); addToCreatePks(pkConstraint.arg(tableName, pkName, ci.columnName), ed); const QString pkDropConstraint(QStringLiteral("ALTER TABLE IF EXISTS %1 DROP CONSTRAINT IF EXISTS %2 CASCADE;")); addToDropConstraints(pkDropConstraint.arg(tableName, pkName), ed); } void SqlGenerator::generateForeignKeys(const QVector > &foreignKeys, ExerciseDependency ed) { for (const QSharedPointer &fk : foreignKeys) generateForeignKey(fk, ed); } void SqlGenerator::generateForeignKey(const QSharedPointer &foreignKey, ExerciseDependency ed) { QString fkName = QLatin1String("FK_") + foreignKey->source.tableName + QLatin1Char('_') + foreignKey->source.columnName + QLatin1Char('_') + foreignKey->destination.tableName + QLatin1Char('_') + foreignKey->destination.columnName; if (fkName.size() > 63) fkName = foreignKey->name; addToFks(fkName, ed); QString fkConstraint(QStringLiteral("ALTER TABLE %1 ADD CONSTRAINT %2 FOREIGN KEY (%3) REFERENCES %4 (%5)")); switch (foreignKey->deleteAction) { case ForeignKeyInformation::OnDeleteAction::NoAction: fkConstraint += QStringLiteral(" ON DELETE NO ACTION"); break; case ForeignKeyInformation::OnDeleteAction::Cascade: fkConstraint += QStringLiteral(" ON DELETE CASCADE ON UPDATE CASCADE"); break; case ForeignKeyInformation::OnDeleteAction::Restrict: fkConstraint += QStringLiteral(" ON DELETE RESTRICT"); break; case ForeignKeyInformation::OnDeleteAction::SetNull: fkConstraint += QStringLiteral(" ON DELETE SET NULL"); break; case ForeignKeyInformation::OnDeleteAction::SetDefault: fkConstraint += QStringLiteral(" ON DELETE SET DEFAULT"); break; } fkConstraint += QLatin1Char(';'); QString destination; if (!foreignKey->destination.schema.isEmpty()) { const QString &extSchema = foreignKey->destination.schema; destination = extSchema + QLatin1Char('.') + foreignKey->destination.tableName; } else destination = foreignKey->destination.tableName; const QString &extSvcAbbr = foreignKey->destination.serviceAbbr; if (!extSvcAbbr.isEmpty() && !m_referencedSevices.contains(extSvcAbbr)) m_referencedSevices += extSvcAbbr; addToCreateFks(fkConstraint.arg(foreignKey->source.tableName, fkName, foreignKey->source.columnName, destination, foreignKey->destination.columnName), ed); const QString fkDropConstraint(QStringLiteral("ALTER TABLE IF EXISTS %1 DROP CONSTRAINT IF EXISTS %2 CASCADE;")); addToDropConstraints(fkDropConstraint.arg(foreignKey->source.tableName, fkName), ed); } void SqlGenerator::generateConstraints(const QString &tableName, const QVector &constraints, ExerciseDependency ed) { for (const ConstraintInformation &constraint : constraints) generateConstraint(tableName, constraint, ed); } void SqlGenerator::generateConstraint(const QString &tableName, const ConstraintInformation &constraint, ExerciseDependency ed) { const QString dropConstraint(QStringLiteral("ALTER TABLE IF EXISTS %1 DROP CONSTRAINT IF EXISTS %2 CASCADE;")); switch (constraint.type) { case ConstraintInformation::Type::index: { QString constraintName = QLatin1String("IDX_") + tableName + QLatin1Char('_') + constraint.affectedColumns.join(QStringLiteral("_")); if (constraintName.size() > 63) constraintName.resize(63); const QString index(QStringLiteral("CREATE INDEX %1 ON %2 (%3);")); addToCreateConstraints(index.arg(constraintName, tableName, constraint.affectedColumns.join(QStringLiteral(", "))), ed); addToConstraints(constraintName, ed); addToDropConstraints(dropConstraint.arg(tableName, constraintName), ed); break; } case ConstraintInformation::Type::unique: { QString constraintName = QLatin1String("UQ_") + tableName + QLatin1Char('_') + constraint.affectedColumns.join(QStringLiteral("_")); if (constraintName.size() > 63) constraintName.resize(63); const QString unique(QStringLiteral("ALTER TABLE %1 ADD CONSTRAINT %2 UNIQUE (%3);")); addToCreateConstraints(unique.arg(tableName, constraintName, constraint.affectedColumns.join(QStringLiteral(", "))), ed); addToConstraints(constraintName, ed); addToDropConstraints(dropConstraint.arg(tableName, constraintName), ed); break; } case ConstraintInformation::Type::unknown: break; } } bool SqlGenerator::handleEnumerations() { const QVector enumerations = m_xmlReader->getEnumerations(); for (const EnumInformation &ei : enumerations) { if (!handleEnumeration(ei)) return false; } return true; } bool SqlGenerator::handleEnumeration(const EnumInformation &ei) { const QStringList sql = handleEnumeration(ei, QString()); if (m_sqlItemsGlobal.tables.contains(ei.tableName)) m_sqlGlobal.enumerationValues += sql; if (m_sqlItemsExercise.tables.contains(ei.tableName)) m_sqlExercise.enumerationValues += sql; return true; } QStringList SqlGenerator::handleEnumeration(const EnumInformation &ei, const QString &dbSchema) const { DEBUG_LOG(m_logger) << "Handle enumeration " << ei.enumName; if (ei.tableName.isEmpty()) { INFO_LOG(m_logger) << "No table for enumeration " << ei.enumName << " - not creating inserts for this enum"; return QStringList(); } const QString insertEnum(QStringLiteral("INSERT INTO %1 (%2) VALUES (%3);")); const QString columnNames(QStringLiteral("id, name")); const QString tablename = dbSchema.isEmpty() ? ei.tableName : dbSchema + QLatin1Char('.') + ei.tableName; QStringList enumerationValues; for (const EnumKeyValuePair &evp : ei.values) { const QString values(QString::number(evp.value) + QLatin1String(", '") + fixupComment(evp.key) + QLatin1Char('\'')); enumerationValues += insertEnum.arg(tablename, columnNames, values); } return enumerationValues; } bool SqlGenerator::generateDbNotifications(const QString &tableName, const QVector ¬ifications, ExerciseDependency ed) { for (const DbNotifcationInformation ¬ification : notifications) { if (notification.paramNames.isEmpty() || notification.paramNames.size() > 1) { ERROR_LOG(m_logger) << "Can not handle more than one parameter"; continue; } generateDbNotification(notification, ed); if (!generateDbNotificationTrigger(tableName, notification, ed)) return false; } return true; } void SqlGenerator::generateDbNotification(const DbNotifcationInformation ¬ification, ExerciseDependency ed) { const QString func(QStringLiteral("CREATE OR REPLACE FUNCTION public.%1()\n" "RETURNS TRIGGER AS $$\n" "DECLARE\n" " toNotify RECORD;\n" " toReturn RECORD;\n" "BEGIN\n" " IF (TG_OP = 'INSERT') THEN\n" " toNotify = NEW;\n" " toReturn = NEW;\n" " ELSIF (TG_OP = 'UPDATE') THEN\n" " toNotify = OLD;\n" " toReturn = NEW;\n" " ELSIF (TG_OP = 'DELETE') THEN\n" " toNotify = OLD;\n" " toReturn = OLD;\n" " END IF;\n" " \n" " PERFORM pg_notify('%2', CAST(toNotify.%3 AS text));\n" " RETURN toReturn;\n" "END;\n" "$$ LANGUAGE plpgsql;")); const QString triggerFuncName(getNotificationFunctionName(notification)); addToDbFunctions(func.arg(triggerFuncName, notification.methodName, notification.paramNames.first()), ed); } bool SqlGenerator::generateDbNotificationTrigger(const QString &tableName, const DbNotifcationInformation ¬ification, ExerciseDependency ed) { const QString drop(QStringLiteral("DROP TRIGGER IF EXISTS %1 ON %2;")); const QString func(QStringLiteral("CREATE TRIGGER %1\n") + QStringLiteral("%2 ON %3 FOR EACH ROW EXECUTE PROCEDURE public.%4();")); for (DbNotifcationInformation::NotificationType notifyType : notification.notificationType) { QString when; QString triggerNameShortcut; switch (notifyType) { case DbNotifcationInformation::NotificationType::BeforeInsert: when = QStringLiteral("BEFORE INSERT"); triggerNameShortcut = QStringLiteral("bi"); break; case DbNotifcationInformation::NotificationType::AfterInsert: when = QStringLiteral("AFTER INSERT"); triggerNameShortcut = QStringLiteral("ai"); break; case DbNotifcationInformation::NotificationType::BeforeUpdate: when = QStringLiteral("BEFORE UPDATE"); triggerNameShortcut = QStringLiteral("bu"); break; case DbNotifcationInformation::NotificationType::AfterUpdate: when = QStringLiteral("AFTER UPDATE"); triggerNameShortcut = QStringLiteral("au"); break; case DbNotifcationInformation::NotificationType::BeforeDelete: when = QStringLiteral("BEFORE DELETE"); triggerNameShortcut = QStringLiteral("bd"); break; case DbNotifcationInformation::NotificationType::AfterDelete: when = QStringLiteral("AFTER DELETE"); triggerNameShortcut = QStringLiteral("ad"); break; } const QString triggerName(QLatin1String("trg_") + triggerNameShortcut + QLatin1Char('_') + notification.methodName); const QString triggerFuncName(getNotificationFunctionName(notification)); addToDbFunctionTrigger(drop.arg(triggerName, tableName), ed); addToDbFunctionTrigger(func.arg(triggerName, when, tableName, triggerFuncName), ed); } return true; } QString SqlGenerator::getNotificationFunctionName(const DbNotifcationInformation ¬ification) const { return getServiceAbbr() + QLatin1Char('_') + notification.methodName; } QString SqlGenerator::fixupComment(const QString &comment) { QString ret = comment; ret = ret.remove(QLatin1Char('\'')); ret = ret.replace(QLatin1Char(';'), QLatin1Char(',')); return ret; } QStringList SqlGenerator::getDbSettings(const QString &dbSchema) const { QStringList data; QString dbUser(QStringLiteral("ets_")); if (m_exerciseId > 0) dbUser += QStringLiteral("ex_00"); else dbUser += m_xmlReader->getServiceAbbr().toLower(); data.append(QLatin1String("ALTER DEFAULT PRIVILEGES IN SCHEMA ") + dbSchema + QLatin1Char('\n') + QLatin1String(" GRANT SELECT,INSERT,UPDATE,DELETE,TRUNCATE ON TABLES TO ") + dbUser + QLatin1Char(';')); data.append(QLatin1String("ALTER DEFAULT PRIVILEGES IN SCHEMA ") + dbSchema + QLatin1Char('\n') + QLatin1String(" GRANT SELECT, REFERENCES ON TABLES TO ets_ro;")); data.append(QLatin1String("ALTER DEFAULT PRIVILEGES IN SCHEMA ") + dbSchema + QLatin1Char('\n') + QLatin1String(" GRANT EXECUTE ON FUNCTIONS TO ") + dbUser + QLatin1Char(';')); data.append(QLatin1String("ALTER DEFAULT PRIVILEGES IN SCHEMA ") + dbSchema + QLatin1Char('\n') + QLatin1String(" GRANT ALL ON SEQUENCES TO ") + dbUser + QLatin1Char(';')); data.append(QLatin1String("SET SEARCH_PATH TO ") + dbSchema + QLatin1String(", PUBLIC;")); QStringList deps; deps.reserve(m_referencedSevices.size()); for(const QString &svc : m_referencedSevices) deps += QStringLiteral("-- DEPENDS: ") + svc; return deps + data; } QStringList SqlGenerator::getAllSqlStmts(const QString &dbSchema, ExerciseDependency ed) const { DEBUG_LOG(m_logger) << "Creating statements for service " << getServiceAbbr() << " in exercise " << m_exerciseId << ", this: " << this; QStringList data; const SqlStatements &sql = getStatements(ed); if (sql.isEmpty()) return data; if (!dbSchema.isEmpty()) data << getDbSettings(dbSchema); data.append(QStringLiteral("\n-- Drop tables/views/sequences/constraints")); data.append(sql.dropViews); data.append(sql.dropConstraints); data.append(sql.dropTables); data.append(sql.dropSequences); data.append(QStringLiteral("\n-- Create tables")); data.append(sql.createTables); data.append(QStringLiteral("\n-- Create sequence(s)")); data.append(sql.createSequences); data.append(QStringLiteral("\n-- Create primary keys")); data.append(sql.createPrimaryKeys); data.append(QStringLiteral("\n-- Create foreign keys")); data.append(sql.createForeignKeys); data.append(QStringLiteral("\n-- Create views")); data.append(sql.createViews); data.append(QStringLiteral("\n-- --------------------------------------------------------------------------------")); data.append(QStringLiteral("\n-- Inserts for static data i.e. enums\n")); data.append(sql.enumerationValues); data.append(QStringLiteral("\n-- --------------------------------------------------------------------------------")); data.append(QStringLiteral("\n-- Notify functions for callbacks (signals in db interface)\n")); data.append(sql.dbFunctions); data.append(QStringLiteral("\n-- --------------------------------------------------------------------------------")); data.append(QStringLiteral("\n-- Notify function trigger for callbacks (signals in db interface)\n")); data.append(sql.dbFunctionTrigger); data.append(QStringLiteral("\n-- --------------------------------------------------------------------------------")); data.append(QStringLiteral("\n-- Indexes \n")); data.append(sql.createConstraints); return data; } QStringList SqlGenerator::getEnumerationInsertValues(const QString &table, const QString &dbSchema) const { const QVector enumerations = m_xmlReader->getEnumerations(); for (const EnumInformation &ei : enumerations) { if (ei.tableName == table) return handleEnumeration(ei, dbSchema); } return QStringList(); } bool SqlGenerator::writeSqlToFile(const QString &dbSchema, const QString &sqlFile, ExerciseDependency ed) const { return writeFile(sqlFile, getAllSqlStmts(dbSchema, ed).join(QLatin1Char('\n'))); } bool SqlGenerator::writeSql(const QString &filePath, ExerciseDependency ed) { Q_ASSERT(ed == Global || ed == Exercise); QString sqlFile = filePath + getServiceAbbr(); if (ed == ExerciseDependency::Exercise) sqlFile += QLatin1Char('_') + getExerciseSchemaName(m_exerciseId); sqlFile += QLatin1String(".sql"); const SqlStatements &sql = getStatements(ed); if (sql.isEmpty()) { DEBUG_LOG(m_logger) << "Sql file is emtpy: " << sqlFile; return writeFile(sqlFile, QString()); } const QString dbSchema = (ed == ExerciseDependency::Exercise ? getExerciseSchemaName(m_exerciseId) : getServiceAbbr()); INFO_LOG(m_logger) << "Creating sql-stmt for schema (" << dbSchema << ")"; return writeSqlToFile(dbSchema, sqlFile, ed); } template static void addInternal(QStringList &global, QStringList &exercise, const T &stmt, ExerciseDependency ed) { switch (ed) { case ExerciseDependency::Global: global += stmt; break; case ExerciseDependency::Exercise: exercise += stmt; break; case ExerciseDependency::GlobalAndExercise: global += stmt; exercise += stmt; break; case ExerciseDependency::Undefined: break; } } void SqlGenerator::addToDropSequences(const QString &stmt, ExerciseDependency ed) { addInternal(m_sqlGlobal.dropSequences, m_sqlExercise.dropSequences, stmt, ed); } void SqlGenerator::addToDropTables(const QString &stmt, ExerciseDependency ed) { addInternal(m_sqlGlobal.dropTables, m_sqlExercise.dropTables, stmt, ed); } void SqlGenerator::addToDropViews(const QString &stmt, ExerciseDependency ed) { addInternal(m_sqlGlobal.dropViews, m_sqlExercise.dropViews, stmt, ed); } void SqlGenerator::addToCreateConstraints(const QString &stmt, ExerciseDependency ed) { addInternal(m_sqlGlobal.createConstraints, m_sqlExercise.createConstraints, stmt, ed); } void SqlGenerator::addToDropConstraints(const QString &stmt, ExerciseDependency ed) { addInternal(m_sqlGlobal.dropConstraints, m_sqlExercise.dropConstraints, stmt, ed); } void SqlGenerator::addToCreateFks(const QString &stmt, ExerciseDependency ed) { addInternal(m_sqlGlobal.createForeignKeys, m_sqlExercise.createForeignKeys, stmt, ed); } void SqlGenerator::addToCreatePks(const QString &stmt, ExerciseDependency ed) { addInternal(m_sqlGlobal.createPrimaryKeys, m_sqlExercise.createPrimaryKeys, stmt, ed); } void SqlGenerator::addToCreateSequences(const QString &stmt, ExerciseDependency ed) { addInternal(m_sqlGlobal.createSequences, m_sqlExercise.createSequences, stmt, ed); } void SqlGenerator::addToCreateTables(const QString &stmt, ExerciseDependency ed) { addInternal(m_sqlGlobal.createTables, m_sqlExercise.createTables, stmt, ed); } void SqlGenerator::addToCreateTables(const QStringList &stmts, ExerciseDependency ed) { addInternal(m_sqlGlobal.createTables, m_sqlExercise.createTables, stmts, ed); } void SqlGenerator::addToCreateViews(const QString &stmt, ExerciseDependency ed) { addInternal(m_sqlGlobal.createViews, m_sqlExercise.createViews, stmt, ed); } void SqlGenerator::addToDbFunctions(const QString &stmt, ExerciseDependency ed) { addInternal(m_sqlGlobal.dbFunctions, m_sqlExercise.dbFunctions, stmt, ed); } void SqlGenerator::addToDbFunctionTrigger(const QString &stmt, ExerciseDependency ed) { addInternal(m_sqlGlobal.dbFunctionTrigger, m_sqlExercise.dbFunctionTrigger, stmt, ed); } void SqlGenerator::addToConstraints(const QString &name, ExerciseDependency ed) { addInternal(m_sqlItemsGlobal.constraints, m_sqlItemsExercise.constraints, name, ed); } void SqlGenerator::addToFks(const QString &name, ExerciseDependency ed) { addInternal(m_sqlItemsGlobal.foreignKeys, m_sqlItemsExercise.foreignKeys, name, ed); } void SqlGenerator::addToPks(const QString &name, ExerciseDependency ed) { addInternal(m_sqlItemsGlobal.primaryKeys, m_sqlItemsExercise.primaryKeys, name, ed); } void SqlGenerator::addToSequences(const QString &name, ExerciseDependency ed) { addInternal(m_sqlItemsGlobal.sequences, m_sqlItemsExercise.sequences, name, ed); } void SqlGenerator::addToTables(const QString &name, ExerciseDependency ed) { addInternal(m_sqlItemsGlobal.tables, m_sqlItemsExercise.tables, name, ed); } bool SqlGenerator::writeFile(const QString &fn, const QString &content) const { QFile f(fn); if (!f.open(QIODevice::WriteOnly | QIODevice::Truncate)) { ERROR_LOG(m_logger) << "Error opening file '" << f.fileName() << "' for writing: " << f.errorString(); return false; } f.write(content.toUtf8()); if (!content.isEmpty()) f.write("\n"); f.close(); return true; }