in modules/wb.model/src/reporting.cpp [902:1301]
ssize_t WbModelImpl::generateReport(workbench_physical_ModelRef model, const grt::DictRef &options) {
// get pointer to the GRT
std::string basedir = bec::GRTManager::get()->get_basedir();
std::string template_base_dir = base::makePath(basedir, "modules/data/wb_model_reporting");
db_mysql_CatalogRef catalog = db_mysql_CatalogRef::cast_from(model->catalog());
// helper variables
std::map<std::string, std::vector<db_mysql_ForeignKeyRef> > tbl_fk_map;
// Process options
std::string template_name = "HTML Basic Frames";
std::string template_style_name = "";
std::string title = "MySQL Model Report";
std::string output_path = "";
bool columns_show = true;
bool indices_show = true;
bool fks_show = true;
bool fks_show_referred_fks = true;
bool show_ddl = true;
bool use_highlighting = false;
std::unique_ptr<mtemplate::TemplateOutputFile> single_file_output;
if (options.is_valid()) {
read_option(template_name, "template_name", options);
read_option(template_style_name, "template_style_name", options);
read_option(title, "title", options);
read_option(output_path, "output_path", options);
read_option(columns_show, "columns_show", options);
read_option(indices_show, "indices_show", options);
read_option(fks_show, "fks_show", options);
read_option(fks_show_referred_fks, "fks_show_referred_fks", options);
read_option(show_ddl, "show_ddl", options);
read_option(use_highlighting, "use_highlighting", options);
}
bool single_file_report = count_template_files(getTemplateDirFromName(template_name)) == 1;
const Scintilla::LexerModule *lexer = NULL;
if (use_highlighting)
lexer = setup_syntax_highlighter(model->rdbms());
// Ensure the output dir exists.
{
int r;
if (!g_file_test(output_path.c_str(), G_FILE_TEST_EXISTS)) {
r = g_mkdir_with_parents(output_path.c_str(), 0700);
if (r < 0) {
logError("Could not create report directory %s: %s", output_path.c_str(), g_strerror(errno));
return 0;
}
}
}
// Start report generation
grt::GRT::get()->send_info("Generating schema report...");
// Build FK dictionary if required
if (fks_show && fks_show_referred_fks) {
// build schema_dict by loop over all schemata, add it to the main_dict
for (std::size_t i = 0; i < catalog->schemata().count(); i++) {
db_mysql_SchemaRef schema = catalog->schemata().get(i);
// loop over all tables
for (std::size_t j = 0; j < schema->tables().count(); j++) {
db_mysql_TableRef table = schema->tables().get(j);
// loop over all foreign keys
for (std::size_t k = 0; k < table->foreignKeys().count(); k++) {
db_mysql_ForeignKeyRef fk = table->foreignKeys().get(k);
db_mysql_TableRef ref_tbl = fk->referencedTable();
if (!ref_tbl.is_valid())
continue;
// look for table in tbl_fk_map
std::map<std::string, std::vector<db_mysql_ForeignKeyRef> >::iterator tbl_fk_map_it = tbl_fk_map.find(ref_tbl.id());
if (tbl_fk_map_it != tbl_fk_map.end()) {
// if found, add fk reference to table
tbl_fk_map_it->second.push_back(fk);
} else {
// if table is not found, create entry
std::vector<db_mysql_ForeignKeyRef> new_tbl_fk_map_v;
// add fk reference to table
new_tbl_fk_map_v.push_back(fk);
tbl_fk_map.insert(make_pair(ref_tbl.id(), new_tbl_fk_map_v));
}
}
}
}
}
// create main dictionary that will be used to expand the templates
mtemplate::DictionaryInterface *main_dictionary = mtemplate::CreateMainDictionary();
// Set some global project info.
main_dictionary->setValue(REPORT_TITLE, title);
std::string time = base::fmttime(0, DATETIME_FMT);
main_dictionary->setValue(REPORT_GENERATED, time);
workbench_DocumentRef document = workbench_DocumentRef::cast_from(model->owner());
main_dictionary->setValue(REPORT_PROJECT_NAME, (std::string)document->info()->project());
main_dictionary->setValue(REPORT_PROJECT_AUTHOR, (std::string)document->info()->author());
main_dictionary->setValue(REPORT_PROJECT_TITLE, (std::string)document->info()->caption());
main_dictionary->setValue(REPORT_PROJECT_CHANGED, (std::string)document->info()->dateChanged());
main_dictionary->setValue(REPORT_PROJECT_CREATED, (std::string)document->info()->dateCreated());
main_dictionary->setValue(REPORT_PROJECT_DESCRIPTION, (std::string)document->info()->description());
main_dictionary->setValue(REPORT_PROJECT_VERSION, (std::string)document->info()->version());
main_dictionary->dump();
workbench_model_reporting_TemplateStyleInfoRef styleInfo =
get_template_style_from_name(template_name, template_style_name);
if (styleInfo.is_valid())
main_dictionary->setValue(REPORT_STYLE_NAME, (std::string)styleInfo->styleTagValue());
main_dictionary->setIntValue(REPORT_SCHEMA_COUNT, (long int)catalog->schemata().count());
int total_column_count = 0;
int total_index_count = 0;
int total_fk_count = 0;
int total_table_count = 0;
int total_view_count = 0;
int total_sp_count = 0;
int total_trigger_count = 0;
SQLGeneratorInterfaceImpl *sqlgenModule = NULL;
if (show_ddl) {
sqlgenModule = dynamic_cast<SQLGeneratorInterfaceImpl *>(grt::GRT::get()->get_module("DbMySQL"));
if (!sqlgenModule)
throw std::logic_error("could not find SQL generation module for mysql");
}
// Build schema_dict by looping over all schemata, add it to the main_dict.
for (int i = 0; i < (int)catalog->schemata().count(); i++) {
db_mysql_SchemaRef schema = catalog->schemata().get(i);
mtemplate::DictionaryInterface *schema_dictionary = main_dictionary->addSectionDictionary(REPORT_SCHEMATA);
schema_dictionary->setIntValue(REPORT_SCHEMA_ID, i);
schema_dictionary->setIntValue(REPORT_SCHEMA_NUMBER, i + 1);
schema_dictionary->setValue(REPORT_SCHEMA_NAME, *schema->name());
set_ddl(schema_dictionary, sqlgenModule, schema, lexer, show_ddl);
schema_dictionary->setIntValue(REPORT_TABLE_COUNT, (int)schema->tables().count());
// Loop over all tables. Build the nested tables sub groups and at the same time the
// full collection of all columns, indices and foreign keys.
for (int j = 0; j < (int)schema->tables().count(); j++) {
db_mysql_TableRef table = schema->tables().get(j);
mtemplate::DictionaryInterface *table_dictionary = schema_dictionary->addSectionDictionary(REPORT_TABLES);
// The table id is used as unique id, e.g. in HTML anchors.
table_dictionary->setIntValue(REPORT_TABLE_ID, total_table_count++);
// The table number is used in visible counts like "Table 1 of 20".
table_dictionary->setIntValue(REPORT_TABLE_NUMBER, j + 1);
table_dictionary->setValue(REPORT_TABLE_NAME, *table->name());
table_dictionary->setValueAndShowSection(REPORT_TABLE_COMMENT, *table->comment(), REPORT_TABLE_COMMENT_LISTING);
fillTablePropertyDict(table, table_dictionary);
set_ddl(table_dictionary, sqlgenModule, table, lexer, show_ddl);
if (columns_show) {
mtemplate::DictionaryInterface *columns_list_dictionary = NULL;
schema_dictionary->setIntValue(REPORT_COLUMN_COUNT, (long)table->columns().count());
for (int k = 0; k < (int)table->columns().count(); k++) {
// Create the dict for the outer section (including header)
if (k == 0)
columns_list_dictionary = table_dictionary->addSectionDictionary(REPORT_COLUMNS_LISTING);
db_mysql_ColumnRef col = table->columns().get(k);
// Fill data for table details.
mtemplate::DictionaryInterface *col_dictionary =
columns_list_dictionary->addSectionDictionary(REPORT_COLUMNS);
fillColumnDict(col, table, col_dictionary, false);
// Fill data for full details.
col_dictionary = schema_dictionary->addSectionDictionary(REPORT_COLUMNS);
fillColumnDict(col, table, col_dictionary, true);
col_dictionary->setIntValue(REPORT_COLUMN_ID, total_column_count++);
col_dictionary->setIntValue(REPORT_COLUMN_NUMBER, k + 1);
}
}
if (indices_show) {
mtemplate::DictionaryInterface *idx_list_dictionary = NULL;
schema_dictionary->setIntValue(REPORT_INDEX_COUNT, (long)table->indices().count());
for (int k = 0; k < (int)table->indices().count(); k++) {
// Create the dict for the outer section (including header)
if (k == 0)
idx_list_dictionary = table_dictionary->addSectionDictionary(REPORT_INDICES_LISTING);
db_mysql_IndexRef idx = table->indices().get(k);
mtemplate::DictionaryInterface *idx_dictionary = idx_list_dictionary->addSectionDictionary(REPORT_INDICES);
fillIndexDict(idx, table, idx_dictionary, false);
idx_dictionary = schema_dictionary->addSectionDictionary(REPORT_INDICES);
fillIndexDict(idx, table, idx_dictionary, true);
idx_dictionary->setIntValue(REPORT_INDEX_ID, total_index_count++);
idx_dictionary->setIntValue(REPORT_INDEX_NUMBER, k + 1);
}
}
if (fks_show) {
mtemplate::DictionaryInterface *fk_list_dictionary = NULL;
schema_dictionary->setIntValue(REPORT_FOREIGN_KEY_COUNT, (long)table->foreignKeys().count());
for (int k = 0; k < (int)table->foreignKeys().count(); k++) {
// Create the dict for the outer section (inluding header)
if (k == 0)
fk_list_dictionary = table_dictionary->addSectionDictionary(REPORT_REL_LISTING);
db_mysql_ForeignKeyRef fk = table->foreignKeys().get(k);
mtemplate::DictionaryInterface *fk_dictionary = fk_list_dictionary->addSectionDictionary(REPORT_REL);
fillForeignKeyDict(fk, table, fk_dictionary, false);
fk_dictionary = schema_dictionary->addSectionDictionary(REPORT_FOREIGN_KEYS);
fillForeignKeyDict(fk, table, fk_dictionary, true);
fk_dictionary->setIntValue(REPORT_FOREIGN_KEY_ID, total_fk_count++);
fk_dictionary->setIntValue(REPORT_FOREIGN_KEY_NUMBER, k + 1);
}
if (fks_show_referred_fks) {
std::map<std::string, std::vector<db_mysql_ForeignKeyRef> >::iterator tbl_fk_map_it = tbl_fk_map.find(table->id());
if (tbl_fk_map_it != tbl_fk_map.end()) {
std::vector<db_mysql_ForeignKeyRef>::iterator fk_it = tbl_fk_map_it->second.begin();
for (; fk_it != tbl_fk_map_it->second.end(); fk_it++) {
if (fk_list_dictionary == NULL)
fk_list_dictionary = table_dictionary->addSectionDictionary(REPORT_REL_LISTING);
db_mysql_ForeignKeyRef fk = *fk_it;
mtemplate::DictionaryInterface *fk_dictionary = fk_list_dictionary->addSectionDictionary(REPORT_REL);
fk_dictionary->setValue(REPORT_REL_NAME, *fk->name());
fk_dictionary->setValue(REPORT_REL_TYPE, bec::TableHelper::is_identifying_foreign_key(table, fk)
? "Identifying"
: "Non-Identifying");
fk_dictionary->setValue(REPORT_REL_PARENTTABLE, *table->name());
fk_dictionary->setValue(REPORT_REL_CHILDTABLE, *fk->owner()->name());
fk_dictionary->setValue(REPORT_REL_CARD, (fk->many() == 1) ? "1:n" : "1:1");
}
}
}
// Triggers.
schema_dictionary->setIntValue(REPORT_TRIGGER_COUNT, (long)table->triggers().count());
for (int k = 0; k < (int)table->triggers().count(); k++) {
db_mysql_TriggerRef trigger = table->triggers().get(k);
mtemplate::DictionaryInterface *trigger_dictionary = schema_dictionary->addSectionDictionary(REPORT_TRIGGERS);
fillTriggerDict(trigger, table, trigger_dictionary);
set_ddl(trigger_dictionary, sqlgenModule, trigger, lexer, show_ddl);
trigger_dictionary->setIntValue(REPORT_TRIGGER_ID, total_trigger_count++);
trigger_dictionary->setIntValue(REPORT_TRIGGER_NUMBER, k + 1);
}
}
}
// View section.
schema_dictionary->setIntValue(REPORT_VIEW_COUNT, (long)schema->views().count());
for (int j = 0; j < (int)schema->views().count(); j++) {
db_mysql_ViewRef view = schema->views().get(j);
mtemplate::DictionaryInterface *view_dictionary = schema_dictionary->addSectionDictionary(REPORT_VIEWS);
view_dictionary->setIntValue(REPORT_VIEW_ID, total_view_count++);
view_dictionary->setIntValue(REPORT_VIEW_NUMBER, j + 1);
set_ddl(view_dictionary, sqlgenModule, view, lexer, show_ddl);
fillViewDict(view, view_dictionary);
}
// Routine section.
schema_dictionary->setIntValue(REPORT_ROUTINE_COUNT, (long)schema->routines().count());
for (int j = 0; j < (int)schema->routines().count(); j++) {
db_mysql_RoutineRef routine = schema->routines().get(j);
mtemplate::DictionaryInterface *routine_dictionary = schema_dictionary->addSectionDictionary(REPORT_ROUTINES);
routine_dictionary->setIntValue(REPORT_ROUTINE_ID, total_sp_count++);
routine_dictionary->setIntValue(REPORT_ROUTINE_NUMBER, j + 1);
set_ddl(routine_dictionary, sqlgenModule, routine, lexer, show_ddl);
fillRoutineDict(routine, routine_dictionary);
}
}
main_dictionary->setIntValue(REPORT_TOTAL_COLUMN_COUNT, total_column_count);
main_dictionary->setIntValue(REPORT_TOTAL_INDEX_COUNT, total_index_count);
main_dictionary->setIntValue(REPORT_TOTAL_FK_COUNT, total_fk_count);
main_dictionary->setIntValue(REPORT_TOTAL_TABLE_COUNT, total_table_count);
main_dictionary->setIntValue(REPORT_TOTAL_VIEW_COUNT, total_view_count);
main_dictionary->setIntValue(REPORT_TOTAL_TRIGGER_COUNT, total_trigger_count);
main_dictionary->setIntValue(REPORT_TOTAL_ROUTINE_COUNT, total_sp_count);
// Process template files
std::string template_dir = getTemplateDirFromName(template_name);
// loop over all files in the template dir
const char *entry;
GDir *dir = g_dir_open(template_dir.c_str(), 0, NULL);
if (dir) {
while ((entry = g_dir_read_name(dir)) != NULL) {
char *path = g_build_filename(template_dir.c_str(), entry, NULL);
// skip the info.xml file and preview pngs
if (strcmp(entry, "info.xml") == 0 || (g_str_has_prefix(entry, "preview_") && g_str_has_suffix(entry, ".png"))) {
g_free(path);
continue;
}
if (g_file_test(path, (GFileTest)(G_FILE_TEST_EXISTS | G_FILE_TEST_IS_REGULAR))) {
if (g_str_has_suffix(entry, ".tpl")) {
// load template file
mtemplate::Template *template_index = mtemplate::GetTemplate(path, mtemplate::DO_NOT_STRIP);
if (template_index == 0) {
grt::GRT::get()->send_error(
"Error while loading template files. Please check the log for more information.");
grt::GRT::get()->send_error(path);
g_free(path);
return 0;
}
// expand the template based on the dictionary
mtemplate::TemplateOutputString string_output;
template_index->expand(main_dictionary, &string_output);
// build output file name
std::string output_filename;
if (single_file_report) {
// For single file reports the target file name is constructed from the report title.
output_filename = base::makePath(output_path, title);
std::string template_filename(entry);
// Remove the .tpl suffix.
std::string name = template_filename.substr(0, template_filename.size() - 4);
// Find the file's target suffix. If there is one use this for the target file too.
std::string::size_type p = name.rfind('.');
if (p != std::string::npos)
output_filename += name.substr(p);
if (single_file_output == nullptr)
single_file_output =
std::unique_ptr<mtemplate::TemplateOutputFile>(new mtemplate::TemplateOutputFile(output_filename));
template_index->expand(main_dictionary, single_file_output.get());
} else {
std::string template_filename(entry);
output_filename = base::makePath(output_path, template_filename.substr(0, template_filename.size() - 4));
mtemplate::TemplateOutputFile output(output_filename);
template_index->expand(main_dictionary, &output);
}
} else {
// Copy files/folders.
std::string target = base::makePath(output_path, entry);
if (g_file_test(path, G_FILE_TEST_IS_DIR))
copy_folder(path, target.c_str());
else
base::copyFile(path, target.c_str());
}
}
g_free(path);
}
g_dir_close(dir);
}
if (use_highlighting)
cleanup_syntax_highlighter();
grt::GRT::get()->send_info(
strfmt("Schema report written to %s %s", single_file_report ? "file" : "folder", output_path.c_str()));
return 1;
}