#include "CodeBuilder.h" #include "FormatToStream.h" #include "Pgsql_Result.h" #include "IndentationConfig.h" #include "LanguageConfig.h" #include "ResultLoopTemplate.h" #include "StructureTemplate.h" #include "util.h" #include void CodeBuilder::GenCodeForExecutingQuery(QTextStream &q, const QString &query, const Pgsql::Result &result, QString structname) { QString loop_template = m_configuration->resultLoopTemplate()->m_loopTemplate;; QString query_string_literal = ConvertToMultiLineCString(query); ColumnDataList columnData = createColumnDataList(result); auto get_var_func = [&] (QTextStream &out, QString var) { if (var == "structname") out << structname; else if (var == "queryliteral") out << query; else if (var == "assignfields") genFieldRetrieval(out, columnData); }; // [optional] Gen declaration of result structure GenReturnStructDefinition(q, columnData, structname); // assume we have connection? What name??? FormatToStream(q, loop_template, get_var_func); // 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 ColumnDataList &columns, QString structname) const { std::shared_ptr templ = m_configuration->structureTemplate(); auto struct_callback = [&] (QTextStream &out, QString var) { if (var == "structname") out << structname; else if (var == "structfields") genStructFields(out, columns); }; FormatToStream(q, templ->m_structTemplate, struct_callback); } void CodeBuilder::genStructFields(QTextStream &q, const ColumnDataList &columns) const { std::shared_ptr templ = m_configuration->structureTemplate(); QString field_format = templ->m_fieldTemplate; const int field_indent_levels = templ->m_fieldIndentation; QString field_indent_string = m_configuration->indentationConfig()->getIndentString(field_indent_levels); for (auto col : columns) { QString field_name = col.varName; Oid type_oid = col.oid; q << field_indent_string; genFieldDeclaration(q, field_format, field_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 // - std::optional // - boolean flags // - null pointer (useful for languages where this has no cost, other cases boolean flags will be more performant) } } 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::genFieldRetrieval(QTextStream &q, const ColumnDataList &columns) const { QString assign_result_field_template = m_configuration->resultLoopTemplate()->m_retrieveValueTemplate; for (auto col : columns) { FormatToStream(q, assign_result_field_template, [&col] (QTextStream &out, QString var) { if (var == "varname") out << col.varName; }); } } void CodeBuilder::genFieldDeclaration(QTextStream &q, const QString &format, const QString &field_name, Oid column_type) const { QString type_name = getTypeName(column_type); FormatToStream(q, format, [&] (QTextStream &out, QString var) { if (var == "typename") out << type_name; else if (var == "varname") out << field_name; }); } CodeBuilder::ColumnDataList CodeBuilder::createColumnDataList(const Pgsql::Result &result) const { ColumnDataList list; list.reserve(static_cast(result.cols())); for (int column = 0; column < result.cols(); ++column) { Oid type_oid = result.type(column); QString column_name = result.getColName(column); QString field_name = columnNameToVariableName(column_name); list.push_back({type_oid, column_name, field_name}); } return list; }