#include "CodeBuilder.h" #include "Pgsql_Result.h" #include "IndentationConfig.h" #include "LanguageConfig.h" #include "StructureTemplate.h" #include "util.h" #include void FormatToStream(QTextStream &stream, QString format, std::function field_callback) { // Use static to optimize only once static QRegularExpression cached_find_var_re("(?:[^\\\\]|^)(\\/%([a-zA-Z0-9_-]+)%\\/)", QRegularExpression::OptimizeOnFirstUsageOption); int from = 0; QRegularExpressionMatch match; while (format.indexOf(cached_find_var_re, from, &match) >= 0) { if (from > 0) { // Because the regex has to check for backslash in front we have the from position // one position before where we actually should continue for the second match and later ie when from > 0 // Therefor increase from by 1 to make the substring (midRef) calculation work ++from; } // copy code before the var to the stream stream << format.midRef(from, match.capturedStart(1) - from); field_callback(stream, match.captured(2)); from = match.capturedEnd()-1; // -1 because it wants to match one character before or start of line to make sure there is no backslash } if (from > 0) { // same reason as at the start of the loop ++from; } stream << format.midRef(from); } void CodeBuilder::GenCodeForExecutingQuery(QTextStream &q, const QString &query, const Pgsql::Result &result) { // %1 query string // %2 struct_name // %3 list of field assignments QString exec_query_template = "Pgsql::Result result = conn.query(%1);"; QString loop_start_template = "Pgsql::Result result = conn.query(/%query_literal%/);" "for (auto row: result) {\n" " Pgsql::Col col(row);\n" " /%struct_type%/ v;\n" "/%assign_fields%/" "}"; // %field_name% // %column_name% // %column_index% QString assign_result_field_template = "col >> v./%field_name%/"; QString query_string_literal = ConvertToMultiLineCString(query); // [optional] Gen declaration of result structure GenReturnStructDefinition(q, result); // assume we have connection? What name??? // gen code for creating and executing statement // - bind parameters // Gen code for iterating through result and filling the result struct/or local variables } void CodeBuilder::GenReturnStructDefinition(QTextStream &q, const Pgsql::Result &result) const { std::shared_ptr templ = m_configuration->structureTemplate(); QString struct_name = "TODO"; QString field_format = templ->m_fieldTemplate; int field_indent_levels = templ->m_fieldIndentation; QString field_indent_string = m_configuration->indentationConfig()->getIndentString(field_indent_levels); // Make struct start q << QString(templ->m_startTemplate).arg(struct_name); // Process fields for (int column = 0; column < result.cols(); ++column) { QString res_col_name = result.getColName(column); Oid type_oid = result.type(column); q << field_indent_string; genFieldDeclaration(q, field_format, res_col_name, type_oid); q << "\n"; // Unfortunatly there is no easy reliable way to determine if a column can contain null values // For simple result columns we can have a look at the original column and if that has a NOT NULL constraint // then we can assume NOT NULL for the result if there are no LEFT/RIGHT JOINS or other constructs that can introduce NULL // values // Any way at generation time we might want to be able to specify the null handle // - exception/error return // - magic value // - boost::optional // - boolean flags // - null pointer (useful for languages where this has no cost, other cases boolean flags will be more performant) } // Finish struct q << QString(templ->m_endTemplate).arg(struct_name); } QString CodeBuilder::columnNameToVariableName(QString column_name) const { return m_configuration->columnNameToFieldName(column_name); } QString CodeBuilder::getTypeName(Oid dbtype) const { return m_configuration->getTypeName(dbtype); } void CodeBuilder::genFieldDeclaration(QTextStream &q, const QString &format, const QString &column_name, Oid column_type) const { QString field_name = columnNameToVariableName(column_name); QString type_name = getTypeName(column_type); q << QString(format).arg(field_name).arg(type_name); }