Lot of code for generating code. Working on unit tests.
This commit is contained in:
parent
da45929b12
commit
8f4845d4d2
42 changed files with 1089 additions and 267 deletions
119
pglablib/codebuilder/CodeBuilder.cpp
Normal file
119
pglablib/codebuilder/CodeBuilder.cpp
Normal file
|
|
@ -0,0 +1,119 @@
|
|||
#include "CodeBuilder.h"
|
||||
#include "Pgsql_Result.h"
|
||||
#include "IndentationConfig.h"
|
||||
#include "LanguageConfig.h"
|
||||
#include "StructureTemplate.h"
|
||||
#include "util.h"
|
||||
#include <QTextStream>
|
||||
|
||||
void FormatToStream(QTextStream &stream, QString format, std::function<void(QTextStream &, QString)> 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<const StructureTemplate> 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);
|
||||
}
|
||||
|
||||
Loading…
Add table
Add a link
Reference in a new issue