summaryrefslogtreecommitdiffstats
path: root/Plugins/SqlEnterpriseFormatter/formatstatement.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'Plugins/SqlEnterpriseFormatter/formatstatement.cpp')
-rw-r--r--Plugins/SqlEnterpriseFormatter/formatstatement.cpp1064
1 files changed, 1064 insertions, 0 deletions
diff --git a/Plugins/SqlEnterpriseFormatter/formatstatement.cpp b/Plugins/SqlEnterpriseFormatter/formatstatement.cpp
new file mode 100644
index 0000000..04c6aae
--- /dev/null
+++ b/Plugins/SqlEnterpriseFormatter/formatstatement.cpp
@@ -0,0 +1,1064 @@
+#include "formatstatement.h"
+#include "formatselect.h"
+#include "formatexpr.h"
+#include "formatlimit.h"
+#include "formatraise.h"
+#include "formatwith.h"
+#include "formatcreatetable.h"
+#include "formatcreatevirtualtable.h"
+#include "formatforeignkey.h"
+#include "formatcolumntype.h"
+#include "formatindexedcolumn.h"
+#include "formatinsert.h"
+#include "formatempty.h"
+#include "formataltertable.h"
+#include "formatanalyze.h"
+#include "formatattach.h"
+#include "formatbegintrans.h"
+#include "formatcommittrans.h"
+#include "formatcopy.h"
+#include "formatcreateindex.h"
+#include "formatcreatetrigger.h"
+#include "formatdelete.h"
+#include "formatupdate.h"
+#include "formatdropindex.h"
+#include "formatdroptable.h"
+#include "formatdroptrigger.h"
+#include "formatdropview.h"
+#include "formatorderby.h"
+#include "parser/ast/sqliteselect.h"
+#include "parser/ast/sqliteexpr.h"
+#include "parser/ast/sqlitelimit.h"
+#include "parser/ast/sqliteraise.h"
+#include "parser/ast/sqlitewith.h"
+#include "parser/ast/sqlitecreatetable.h"
+#include "parser/ast/sqlitecreatevirtualtable.h"
+#include "parser/ast/sqliteforeignkey.h"
+#include "parser/ast/sqlitecolumntype.h"
+#include "parser/ast/sqliteindexedcolumn.h"
+#include "parser/ast/sqliteinsert.h"
+#include "parser/ast/sqliteemptyquery.h"
+#include "parser/ast/sqlitealtertable.h"
+#include "parser/ast/sqliteanalyze.h"
+#include "parser/ast/sqliteattach.h"
+#include "parser/ast/sqlitebegintrans.h"
+#include "parser/ast/sqlitecommittrans.h"
+#include "parser/ast/sqlitecopy.h"
+#include "parser/ast/sqlitecreateindex.h"
+#include "parser/ast/sqlitecreatetrigger.h"
+#include "parser/ast/sqliteupdate.h"
+#include "parser/ast/sqlitedelete.h"
+#include "parser/ast/sqlitedropindex.h"
+#include "parser/ast/sqlitedroptable.h"
+#include "parser/ast/sqlitedroptrigger.h"
+#include "parser/ast/sqlitedropview.h"
+#include "parser/ast/sqliteorderby.h"
+#include "sqlenterpriseformatter.h"
+#include "common/utils_sql.h"
+#include "common/global.h"
+#include <QRegularExpression>
+#include <QDebug>
+
+#define FORMATTER_FACTORY_ENTRY(query, Type, FormatType) \
+ if (dynamic_cast<Type*>(query)) \
+ return new FormatType(dynamic_cast<Type*>(query))
+
+const QString FormatStatement::SPACE = " ";
+const QString FormatStatement::NEWLINE = "\n";
+qint64 FormatStatement::nameSeq = 0;
+
+FormatStatement::FormatStatement()
+{
+ static_qstring(nameTpl, "statement_%1");
+
+ indents.push(0);
+ statementName = nameTpl.arg(QString::number(nameSeq++));
+}
+
+FormatStatement::~FormatStatement()
+{
+ cleanup();
+}
+
+QString FormatStatement::format()
+{
+ buildTokens();
+ return detokenize() + NEWLINE; // extra space when formatting multiple top level (not in CREATE TRIGGER) queries
+}
+
+void FormatStatement::setSelectedWrapper(NameWrapper wrapper)
+{
+ this->wrapper = wrapper;
+}
+
+void FormatStatement::setConfig(Cfg::SqlEnterpriseFormatterConfig* cfg)
+{
+ this->cfg = cfg;
+}
+
+void FormatStatement::buildTokens()
+{
+ cleanup();
+ resetInternal();
+ formatInternal();
+}
+
+FormatStatement *FormatStatement::forQuery(SqliteStatement *query)
+{
+ FormatStatement* stmt = nullptr;
+ FORMATTER_FACTORY_ENTRY(query, SqliteSelect, FormatSelect);
+ FORMATTER_FACTORY_ENTRY(query, SqliteSelect::Core, FormatSelectCore);
+ FORMATTER_FACTORY_ENTRY(query, SqliteSelect::Core::ResultColumn, FormatSelectCoreResultColumn);
+ FORMATTER_FACTORY_ENTRY(query, SqliteSelect::Core::JoinConstraint, FormatSelectCoreJoinConstraint);
+ FORMATTER_FACTORY_ENTRY(query, SqliteSelect::Core::JoinOp, FormatSelectCoreJoinOp);
+ FORMATTER_FACTORY_ENTRY(query, SqliteSelect::Core::JoinSource, FormatSelectCoreJoinSource);
+ FORMATTER_FACTORY_ENTRY(query, SqliteSelect::Core::JoinSourceOther, FormatSelectCoreJoinSourceOther);
+ FORMATTER_FACTORY_ENTRY(query, SqliteSelect::Core::SingleSource, FormatSelectCoreSingleSource);
+ FORMATTER_FACTORY_ENTRY(query, SqliteExpr, FormatExpr);
+ FORMATTER_FACTORY_ENTRY(query, SqliteWith, FormatWith);
+ FORMATTER_FACTORY_ENTRY(query, SqliteWith::CommonTableExpression, FormatWithCommonTableExpression);
+ FORMATTER_FACTORY_ENTRY(query, SqliteRaise, FormatRaise);
+ FORMATTER_FACTORY_ENTRY(query, SqliteLimit, FormatLimit);
+ FORMATTER_FACTORY_ENTRY(query, SqliteCreateTable, FormatCreateTable);
+ FORMATTER_FACTORY_ENTRY(query, SqliteCreateTable::Column, FormatCreateTableColumn);
+ FORMATTER_FACTORY_ENTRY(query, SqliteCreateTable::Column::Constraint, FormatCreateTableColumnConstraint);
+ FORMATTER_FACTORY_ENTRY(query, SqliteCreateTable::Constraint, FormatCreateTableConstraint);
+ FORMATTER_FACTORY_ENTRY(query, SqliteForeignKey, FormatForeignKey);
+ FORMATTER_FACTORY_ENTRY(query, SqliteForeignKey::Condition, FormatForeignKeyCondition);
+ FORMATTER_FACTORY_ENTRY(query, SqliteColumnType, FormatColumnType);
+ FORMATTER_FACTORY_ENTRY(query, SqliteIndexedColumn, FormatIndexedColumn);
+ FORMATTER_FACTORY_ENTRY(query, SqliteInsert, FormatInsert);
+ FORMATTER_FACTORY_ENTRY(query, SqliteEmptyQuery, FormatEmpty);
+ FORMATTER_FACTORY_ENTRY(query, SqliteAlterTable, FormatAlterTable);
+ FORMATTER_FACTORY_ENTRY(query, SqliteAnalyze, FormatAnalyze);
+ FORMATTER_FACTORY_ENTRY(query, SqliteAttach, FormatAttach);
+ FORMATTER_FACTORY_ENTRY(query, SqliteBeginTrans, FormatBeginTrans);
+ FORMATTER_FACTORY_ENTRY(query, SqliteCommitTrans, FormatCommitTrans);
+ FORMATTER_FACTORY_ENTRY(query, SqliteCopy, FormatCopy);
+ FORMATTER_FACTORY_ENTRY(query, SqliteCreateVirtualTable, FormatCreateVirtualTable);
+ FORMATTER_FACTORY_ENTRY(query, SqliteCreateIndex, FormatCreateIndex);
+ FORMATTER_FACTORY_ENTRY(query, SqliteCreateTrigger, FormatCreateTrigger);
+ FORMATTER_FACTORY_ENTRY(query, SqliteCreateTrigger::Event, FormatCreateTriggerEvent);
+ FORMATTER_FACTORY_ENTRY(query, SqliteUpdate, FormatUpdate);
+ FORMATTER_FACTORY_ENTRY(query, SqliteDelete, FormatDelete);
+ FORMATTER_FACTORY_ENTRY(query, SqliteDropIndex, FormatDropIndex);
+ FORMATTER_FACTORY_ENTRY(query, SqliteDropTable, FormatDropTable);
+ FORMATTER_FACTORY_ENTRY(query, SqliteDropTrigger, FormatDropTrigger);
+ FORMATTER_FACTORY_ENTRY(query, SqliteDropView, FormatDropView);
+ FORMATTER_FACTORY_ENTRY(query, SqliteOrderBy, FormatOrderBy);
+
+ if (stmt)
+ stmt->dialect = query->dialect;
+ else if (query)
+ qWarning() << "Unhandled query passed to enterprise formatter!";
+ else
+ qWarning() << "Null query passed to enterprise formatter!";
+
+ return stmt;
+}
+
+void FormatStatement::resetInternal()
+{
+}
+
+FormatStatement& FormatStatement::withKeyword(const QString& kw)
+{
+ withToken(FormatToken::KEYWORD, kw);
+ return *this;
+}
+
+FormatStatement& FormatStatement::withLinedUpKeyword(const QString& kw, const QString& lineUpName)
+{
+ withToken(FormatToken::LINED_UP_KEYWORD, kw, getFinalLineUpName(lineUpName));
+ return *this;
+}
+
+FormatStatement& FormatStatement::withId(const QString& id)
+{
+ withToken(FormatToken::ID, id);
+ return *this;
+}
+
+FormatStatement& FormatStatement::withOperator(const QString& oper)
+{
+ withToken(FormatToken::OPERATOR, oper);
+ return *this;
+}
+
+FormatStatement&FormatStatement::withStringOrId(const QString& id)
+{
+ withToken(FormatToken::STRING_OR_ID, id);
+ return *this;
+}
+
+FormatStatement& FormatStatement::withIdDot()
+{
+ withToken(FormatToken::ID_DOT, ".");
+ return *this;
+}
+
+FormatStatement& FormatStatement::withStar()
+{
+ withToken(FormatToken::STAR, "*");
+ return *this;
+}
+
+FormatStatement& FormatStatement::withFloat(double value)
+{
+ withToken(FormatToken::FLOAT, value);
+ return *this;
+}
+
+FormatStatement& FormatStatement::withInteger(qint64 value)
+{
+ withToken(FormatToken::INTEGER, value);
+ return *this;
+}
+
+FormatStatement& FormatStatement::withString(const QString& value)
+{
+ withToken(FormatToken::STRING, value);
+ return *this;
+}
+
+FormatStatement& FormatStatement::withBlob(const QString& value)
+{
+ withToken(FormatToken::BLOB, value);
+ return *this;
+}
+
+FormatStatement& FormatStatement::withBindParam(const QString& name)
+{
+ withToken(FormatToken::BIND_PARAM, name);
+ return *this;
+}
+
+FormatStatement& FormatStatement::withParDefLeft()
+{
+ withToken(FormatToken::PAR_DEF_LEFT, "(");
+ return *this;
+}
+
+FormatStatement& FormatStatement::withParDefRight()
+{
+ withToken(FormatToken::PAR_DEF_RIGHT, ")");
+ return *this;
+}
+
+FormatStatement& FormatStatement::withParExprLeft()
+{
+ withToken(FormatToken::PAR_EXPR_LEFT, "(");
+ return *this;
+}
+
+FormatStatement& FormatStatement::withParExprRight()
+{
+ withToken(FormatToken::PAR_EXPR_RIGHT, ")");
+ return *this;
+}
+
+FormatStatement& FormatStatement::withParFuncLeft()
+{
+ withToken(FormatToken::PAR_FUNC_LEFT, "(");
+ return *this;
+}
+
+FormatStatement& FormatStatement::withParFuncRight()
+{
+ withToken(FormatToken::PAR_FUNC_RIGHT, ")");
+ return *this;
+}
+
+FormatStatement& FormatStatement::withSemicolon()
+{
+ FormatToken* lastRealToken = getLastRealToken();
+ if ((lastRealToken && lastRealToken->type != FormatToken::SEMICOLON) || tokens.size() == 0)
+ withToken(FormatToken::SEMICOLON, ";");
+
+ return *this;
+}
+
+FormatStatement& FormatStatement::withListComma()
+{
+ withToken(FormatToken::COMMA_LIST, ",");
+ return *this;
+}
+
+FormatStatement& FormatStatement::withCommaOper()
+{
+ withToken(FormatToken::COMMA_OPER, ",");
+ return *this;
+}
+
+FormatStatement&FormatStatement::withSortOrder(SqliteSortOrder sortOrder)
+{
+ if (sortOrder != SqliteSortOrder::null)
+ withKeyword(sqliteSortOrder(sortOrder));
+
+ return *this;
+}
+
+FormatStatement&FormatStatement::withConflict(SqliteConflictAlgo onConflict)
+{
+ if (onConflict != SqliteConflictAlgo::null)
+ withKeyword("ON").withKeyword("CONFLICT").withKeyword(sqliteConflictAlgo(onConflict));
+
+ return *this;
+}
+
+FormatStatement& FormatStatement::withFuncId(const QString& func)
+{
+ withToken(FormatToken::FUNC_ID, func);
+ return *this;
+}
+
+FormatStatement& FormatStatement::withDataType(const QString& dataType)
+{
+ withToken(FormatToken::DATA_TYPE, dataType);
+ return *this;
+}
+
+FormatStatement& FormatStatement::withNewLine()
+{
+ withToken(FormatToken::NEW_LINE, NEWLINE);
+ return *this;
+}
+
+FormatStatement& FormatStatement::withLiteral(const QVariant& value)
+{
+ if (value.isNull())
+ return *this;
+
+ bool ok;
+ if (value.userType() == QVariant::Double)
+ {
+ value.toDouble(&ok);
+ if (ok)
+ {
+ withFloat(value.toDouble());
+ return *this;
+ }
+ }
+
+ value.toInt(&ok);
+ if (ok)
+ {
+ withInteger(value.toInt());
+ return *this;
+ }
+
+ QString str = value.toString();
+ if (str.startsWith("x'", Qt::CaseInsensitive) && str.endsWith("'"))
+ {
+ withBlob(str);
+ return *this;
+ }
+
+ withString(str);
+ return *this;
+}
+
+FormatStatement& FormatStatement::withStatement(SqliteStatement* stmt, const QString& indentName, FormatStatementEnricher enricher)
+{
+ if (!stmt)
+ return *this;
+
+ FormatStatement* formatStmt = forQuery(stmt, dialect, wrapper, cfg);
+ if (!formatStmt)
+ return *this;
+
+ formatStmt->parentFormatStatement = this;
+
+ if (enricher)
+ enricher(formatStmt);
+
+ formatStmt->buildTokens();
+ formatStmt->deleteTokens = false;
+
+ if (!indentName.isNull())
+ markAndKeepIndent(indentName);
+
+ tokens += formatStmt->tokens;
+
+ if (!indentName.isNull())
+ withDecrIndent();
+
+ delete formatStmt;
+ return *this;
+}
+
+FormatStatement& FormatStatement::markIndent(const QString& name)
+{
+ withToken(FormatToken::INDENT_MARKER, statementName + "_" + name);
+ return *this;
+}
+
+FormatStatement& FormatStatement::markAndKeepIndent(const QString& name)
+{
+ markIndent(name);
+ withIncrIndent(name);
+ return *this;
+}
+
+FormatStatement&FormatStatement::withIncrIndent(int newIndent)
+{
+ withToken(FormatToken::SET_INDENT, newIndent);
+ return *this;
+}
+
+FormatStatement& FormatStatement::withIncrIndent(const QString& name)
+{
+ if (name.isNull())
+ withToken(FormatToken::INCR_INDENT, name);
+ else
+ withToken(FormatToken::INCR_INDENT, statementName + "_" + name);
+
+ return *this;
+}
+
+FormatStatement& FormatStatement::withDecrIndent()
+{
+ withToken(FormatToken::DECR_INDENT, QString());
+ return *this;
+}
+
+FormatStatement&FormatStatement::markKeywordLineUp(const QString& keyword, const QString& lineUpName)
+{
+ withToken(FormatToken::MARK_KEYWORD_LINEUP, getFinalLineUpName(lineUpName), keyword.length());
+ return *this;
+}
+
+FormatStatement&FormatStatement::withSeparator(FormatStatement::ListSeparator sep)
+{
+ switch (sep)
+ {
+ case ListSeparator::COMMA:
+ withListComma();
+ break;
+ case ListSeparator::EXPR_COMMA:
+ withCommaOper();
+ break;
+ case ListSeparator::NEW_LINE:
+ withNewLine();
+ break;
+ case ListSeparator::SEMICOLON:
+ withSemicolon();
+ break;
+ case ListSeparator::NONE:
+ break;
+ }
+ return *this;
+}
+
+FormatStatement& FormatStatement::withIdList(const QStringList& names, const QString& indentName, ListSeparator sep)
+{
+ if (!indentName.isNull())
+ markAndKeepIndent(indentName);
+
+ bool first = true;
+ foreach (const QString& name, names)
+ {
+ if (!first)
+ withSeparator(sep);
+
+ withId(name);
+ first = false;
+ }
+
+ if (!indentName.isNull())
+ withDecrIndent();
+
+ return *this;
+}
+
+void FormatStatement::withToken(FormatStatement::FormatToken::Type type, const QVariant& value, const QVariant& additionalValue)
+{
+ FormatToken* token = new FormatToken;
+ token->type = type;
+ token->value = value;
+ token->additionalValue = additionalValue;
+ tokens << token;
+}
+
+void FormatStatement::cleanup()
+{
+ kwLineUpPosition.clear();
+ line = "";
+ lines.clear();
+ namedIndents.clear();
+ resetIndents();
+ if (deleteTokens)
+ {
+ for (FormatToken* token : tokens)
+ delete token;
+ }
+
+ tokens.clear();
+}
+
+int FormatStatement::getLineUpValue(const QString& lineUpName)
+{
+ if (kwLineUpPosition.contains(lineUpName))
+ return kwLineUpPosition[lineUpName];
+
+ return 0;
+}
+
+QString FormatStatement::detokenize()
+{
+ bool uppercaseKeywords = cfg->SqlEnterpriseFormatter.UppercaseKeywords.get();
+
+ for (FormatToken* token : tokens)
+ {
+ applySpace(token->type);
+ switch (token->type)
+ {
+ case FormatToken::LINED_UP_KEYWORD:
+ {
+ if (cfg->SqlEnterpriseFormatter.LineUpKeywords.get())
+ {
+ QString kw = token->value.toString();
+ QString lineUpName = token->additionalValue.toString();
+ int lineUpValue = getLineUpValue(lineUpName);
+
+ int indentLength = lineUpValue - kw.length();
+ if (indentLength > 0)
+ line += SPACE.repeated(indentLength);
+
+ line += uppercaseKeywords ? kw.toUpper() : kw.toLower();
+
+ break;
+ }
+ else
+ {
+ // No 'break', so we go to next case, the regular KEYWORD
+ }
+ }
+ case FormatToken::KEYWORD:
+ {
+ applyIndent();
+ line += uppercaseKeywords ? token->value.toString().toUpper() : token->value.toString().toLower();
+ break;
+ }
+ case FormatToken::FUNC_ID:
+ case FormatToken::DATA_TYPE:
+ {
+ applyIndent();
+ line += wrapObjIfNeeded(token->value.toString(), dialect, wrapper);
+ break;
+ }
+ case FormatToken::ID:
+ {
+ applyIndent();
+ formatId(token->value.toString());
+ break;
+ }
+ case FormatToken::STRING_OR_ID:
+ {
+ applyIndent();
+ QString val = token->value.toString();
+ if (val.contains("\""))
+ formatId(token->value.toString());
+ else
+ line += wrapObjName(token->value.toString(), NameWrapper::DOUBLE_QUOTE);
+
+ break;
+ }
+ case FormatToken::STAR:
+ case FormatToken::FLOAT:
+ case FormatToken::INTEGER:
+ case FormatToken::BLOB:
+ case FormatToken::BIND_PARAM:
+ case FormatToken::STRING:
+ {
+ applyIndent();
+ line += token->value.toString();
+ break;
+ }
+ case FormatToken::OPERATOR:
+ {
+ bool spaceAdded = endsWithSpace() || applyIndent();
+ if (cfg->SqlEnterpriseFormatter.SpaceBeforeMathOp.get() && !spaceAdded)
+ line += SPACE;
+
+ line += token->value.toString();
+ if (cfg->SqlEnterpriseFormatter.SpaceAfterMathOp.get())
+ line += SPACE;
+
+ break;
+ }
+ case FormatToken::ID_DOT:
+ {
+ bool spaceAdded = endsWithSpace() || applyIndent();
+ if (cfg->SqlEnterpriseFormatter.SpaceBeforeDot.get() && !spaceAdded)
+ line += SPACE;
+
+ line += token->value.toString();
+ if (cfg->SqlEnterpriseFormatter.SpaceAfterDot.get())
+ line += SPACE;
+
+ break;
+ }
+ case FormatToken::PAR_DEF_LEFT:
+ {
+ bool spaceBefore = cfg->SqlEnterpriseFormatter.SpaceBeforeOpenPar.get();
+ bool spaceAfter = cfg->SqlEnterpriseFormatter.SpaceAfterOpenPar.get();
+ bool nlBefore = cfg->SqlEnterpriseFormatter.NlBeforeOpenParDef.get();
+ bool nlAfter = cfg->SqlEnterpriseFormatter.NlAfterOpenParDef.get();
+ detokenizeLeftPar(token, spaceBefore, spaceAfter, nlBefore, nlAfter);
+ break;
+ }
+ case FormatToken::PAR_DEF_RIGHT:
+ {
+ bool spaceBefore = cfg->SqlEnterpriseFormatter.SpaceBeforeClosePar.get();
+ bool spaceAfter = cfg->SqlEnterpriseFormatter.SpaceAfterClosePar.get();
+ bool nlBefore = cfg->SqlEnterpriseFormatter.NlBeforeCloseParDef.get();
+ bool nlAfter = cfg->SqlEnterpriseFormatter.NlAfterCloseParDef.get();
+ detokenizeRightPar(token, spaceBefore, spaceAfter, nlBefore, nlAfter);
+ break;
+ }
+ case FormatToken::PAR_EXPR_LEFT:
+ {
+ bool spaceBefore = cfg->SqlEnterpriseFormatter.SpaceBeforeOpenPar.get();
+ bool spaceAfter = cfg->SqlEnterpriseFormatter.SpaceAfterOpenPar.get();
+ bool nlBefore = cfg->SqlEnterpriseFormatter.NlBeforeOpenParExpr.get();
+ bool nlAfter = cfg->SqlEnterpriseFormatter.NlAfterOpenParExpr.get();
+ detokenizeLeftPar(token, spaceBefore, spaceAfter, nlBefore, nlAfter);
+ break;
+ }
+ case FormatToken::PAR_EXPR_RIGHT:
+ {
+ bool spaceBefore = cfg->SqlEnterpriseFormatter.SpaceBeforeClosePar.get();
+ bool spaceAfter = cfg->SqlEnterpriseFormatter.SpaceAfterClosePar.get();
+ bool nlBefore = cfg->SqlEnterpriseFormatter.NlBeforeCloseParExpr.get();
+ bool nlAfter = cfg->SqlEnterpriseFormatter.NlAfterCloseParExpr.get();
+ detokenizeRightPar(token, spaceBefore, spaceAfter, nlBefore, nlAfter);
+ break;
+ }
+ case FormatToken::PAR_FUNC_LEFT:
+ {
+ bool spaceBefore = cfg->SqlEnterpriseFormatter.SpaceBeforeOpenPar.get() && !cfg->SqlEnterpriseFormatter.NoSpaceAfterFunctionName.get();
+ bool spaceAfter = cfg->SqlEnterpriseFormatter.SpaceAfterOpenPar.get();
+ bool nlBefore = cfg->SqlEnterpriseFormatter.NlBeforeOpenParExpr.get();
+ bool nlAfter = cfg->SqlEnterpriseFormatter.NlAfterOpenParExpr.get();
+ detokenizeLeftPar(token, spaceBefore, spaceAfter, nlBefore, nlAfter);
+ break;
+ }
+ case FormatToken::PAR_FUNC_RIGHT:
+ {
+ bool spaceBefore = cfg->SqlEnterpriseFormatter.SpaceBeforeClosePar.get();
+ bool spaceAfter = cfg->SqlEnterpriseFormatter.SpaceAfterClosePar.get();
+ bool nlBefore = cfg->SqlEnterpriseFormatter.NlBeforeCloseParExpr.get();
+ bool nlAfter = cfg->SqlEnterpriseFormatter.NlAfterCloseParExpr.get();
+ detokenizeRightPar(token, spaceBefore, spaceAfter, nlBefore, nlAfter);
+ break;
+ }
+ case FormatToken::SEMICOLON:
+ {
+ if (cfg->SqlEnterpriseFormatter.SpaceNeverBeforeSemicolon.get())
+ {
+ removeAllSpaces();
+ }
+ else
+ {
+ bool spaceAdded = endsWithSpace() || applyIndent();
+ if (cfg->SqlEnterpriseFormatter.SpaceBeforeMathOp.get() && !spaceAdded)
+ line += SPACE;
+ }
+
+ line += token->value.toString();
+ if (cfg->SqlEnterpriseFormatter.NlAfterSemicolon.get())
+ newLine();
+ else if (cfg->SqlEnterpriseFormatter.SpaceAfterMathOp.get())
+ line += SPACE;
+
+ break;
+ }
+ case FormatToken::COMMA_LIST:
+ {
+ if (cfg->SqlEnterpriseFormatter.SpaceNeverBeforeComma.get())
+ {
+ removeAllSpaces();
+ }
+ else
+ {
+ bool spaceAdded = endsWithSpace() || applyIndent();
+ if (cfg->SqlEnterpriseFormatter.SpaceBeforeCommaInList.get() && !spaceAdded)
+ line += SPACE;
+ }
+
+ line += token->value.toString();
+ if (cfg->SqlEnterpriseFormatter.NlAfterComma.get())
+ newLine();
+ else if (cfg->SqlEnterpriseFormatter.SpaceAfterCommaInList.get())
+ line += SPACE;
+
+ break;
+ }
+ case FormatToken::COMMA_OPER:
+ {
+ if (cfg->SqlEnterpriseFormatter.SpaceNeverBeforeComma.get())
+ {
+ removeAllSpaces();
+ }
+ else
+ {
+ bool spaceAdded = endsWithSpace() || applyIndent();
+ if (cfg->SqlEnterpriseFormatter.SpaceBeforeCommaInList.get() && !spaceAdded)
+ line += SPACE;
+ }
+
+ line += token->value.toString();
+ if (cfg->SqlEnterpriseFormatter.NlAfterCommaInExpr.get())
+ newLine();
+ else if (cfg->SqlEnterpriseFormatter.SpaceAfterCommaInList.get())
+ line += SPACE;
+
+ break;
+ }
+ case FormatToken::NEW_LINE:
+ {
+ newLine();
+ break;
+ }
+ case FormatToken::INDENT_MARKER:
+ {
+ QString indentName = token->value.toString();
+ namedIndents[indentName] = predictCurrentIndent(token);
+ break;
+ }
+ case FormatToken::INCR_INDENT:
+ {
+ if (!token->value.isNull())
+ incrIndent(token->value.toString());
+ else
+ incrIndent();
+
+ break;
+ }
+ case FormatToken::SET_INDENT:
+ {
+ setIndent(indents.top() + token->value.toInt());
+ break;
+ }
+ case FormatToken::DECR_INDENT:
+ {
+ decrIndent();
+ break;
+ }
+ case FormatToken::MARK_KEYWORD_LINEUP:
+ {
+ QString lineUpName = token->value.toString();
+ int lineUpLength = predictCurrentIndent(token) + token->additionalValue.toInt();
+ if (!kwLineUpPosition.contains(lineUpName) || lineUpLength > kwLineUpPosition[lineUpName])
+ kwLineUpPosition[lineUpName] = lineUpLength;
+
+ break;
+ }
+ }
+ updateLastToken(token);
+ }
+ newLine();
+ return lines.join(NEWLINE);
+}
+
+bool FormatStatement::applyIndent()
+{
+ int indentToAdd = indents.top() - line.length();
+ if (indentToAdd <= 0)
+ return false;
+
+ line += SPACE.repeated(indentToAdd);
+ return true;
+}
+
+void FormatStatement::applySpace(FormatToken::Type type)
+{
+ if (lastToken && isSpaceExpectingType(type) && isSpaceExpectingType(lastToken->type) && !endsWithSpace())
+ line += SPACE;
+}
+
+bool FormatStatement::isSpaceExpectingType(FormatStatement::FormatToken::Type type)
+{
+ switch (type)
+ {
+ case FormatToken::KEYWORD:
+ case FormatToken::LINED_UP_KEYWORD:
+ case FormatToken::ID:
+ case FormatToken::STRING_OR_ID:
+ case FormatToken::FLOAT:
+ case FormatToken::STRING:
+ case FormatToken::INTEGER:
+ case FormatToken::BLOB:
+ case FormatToken::BIND_PARAM:
+ case FormatToken::FUNC_ID:
+ case FormatToken::DATA_TYPE:
+ case FormatToken::STAR:
+ return true;
+ case FormatToken::OPERATOR:
+ case FormatToken::ID_DOT:
+ case FormatToken::PAR_DEF_LEFT:
+ case FormatToken::PAR_DEF_RIGHT:
+ case FormatToken::PAR_EXPR_LEFT:
+ case FormatToken::PAR_EXPR_RIGHT:
+ case FormatToken::PAR_FUNC_LEFT:
+ case FormatToken::PAR_FUNC_RIGHT:
+ case FormatToken::SEMICOLON:
+ case FormatToken::COMMA_LIST:
+ case FormatToken::COMMA_OPER:
+ case FormatToken::NEW_LINE:
+ case FormatToken::INDENT_MARKER:
+ case FormatToken::INCR_INDENT:
+ case FormatToken::DECR_INDENT:
+ case FormatToken::SET_INDENT:
+ case FormatToken::MARK_KEYWORD_LINEUP:
+ break;
+ }
+ return false;
+}
+
+bool FormatStatement::isMetaType(FormatStatement::FormatToken::Type type)
+{
+ switch (type)
+ {
+ case FormatToken::INDENT_MARKER:
+ case FormatToken::INCR_INDENT:
+ case FormatToken::DECR_INDENT:
+ case FormatToken::SET_INDENT:
+ case FormatToken::MARK_KEYWORD_LINEUP:
+ return true;
+ case FormatToken::KEYWORD:
+ case FormatToken::LINED_UP_KEYWORD:
+ case FormatToken::ID:
+ case FormatToken::STRING_OR_ID:
+ case FormatToken::FLOAT:
+ case FormatToken::STRING:
+ case FormatToken::INTEGER:
+ case FormatToken::BLOB:
+ case FormatToken::BIND_PARAM:
+ case FormatToken::FUNC_ID:
+ case FormatToken::DATA_TYPE:
+ case FormatToken::OPERATOR:
+ case FormatToken::STAR:
+ case FormatToken::ID_DOT:
+ case FormatToken::PAR_DEF_LEFT:
+ case FormatToken::PAR_DEF_RIGHT:
+ case FormatToken::PAR_EXPR_LEFT:
+ case FormatToken::PAR_EXPR_RIGHT:
+ case FormatToken::PAR_FUNC_LEFT:
+ case FormatToken::PAR_FUNC_RIGHT:
+ case FormatToken::SEMICOLON:
+ case FormatToken::COMMA_LIST:
+ case FormatToken::COMMA_OPER:
+ case FormatToken::NEW_LINE:
+ break;
+ }
+ return false;
+}
+
+void FormatStatement::newLine()
+{
+ if (line.length() == 0) // prevents double new-line when for example "))" occurs and it has new-line before and after
+ return;
+
+ lines << line;
+ line = "";
+}
+
+void FormatStatement::incrIndent(const QString& name)
+{
+ if (!name.isNull())
+ {
+ if (namedIndents.contains(name))
+ {
+ indents.push(namedIndents[name]);
+ }
+ else
+ {
+ indents.push(indents.top() + cfg->SqlEnterpriseFormatter.TabSize.get());
+ qCritical() << __func__ << "No named indent found:" << name;
+ }
+ }
+ else
+ indents.push(indents.top() + cfg->SqlEnterpriseFormatter.TabSize.get());
+}
+
+void FormatStatement::decrIndent()
+{
+ if (indents.size() <= 1)
+ return;
+
+ indents.pop();
+}
+
+void FormatStatement::setIndent(int newIndent)
+{
+ indents.push(newIndent);
+}
+
+bool FormatStatement::endsWithSpace()
+{
+ return line.length() == 0 || line[line.length() - 1].isSpace();
+}
+
+FormatStatement::FormatToken* FormatStatement::getLastRealToken(bool skipNewLines)
+{
+ for (FormatToken* tk : reverse(tokens))
+ {
+ if (!isMetaType(tk->type) && (!skipNewLines || tk->type != FormatToken::NEW_LINE))
+ return tk;
+ }
+ return nullptr;
+}
+
+void FormatStatement::detokenizeLeftPar(FormatToken* token, bool spaceBefore, bool spaceAfter, bool nlBefore, bool nlAfter)
+{
+ bool spaceAdded = endsWithSpace();
+ if (nlBefore)
+ {
+ newLine();
+ spaceAdded = true;
+ }
+
+ spaceAdded |= applyIndent();
+ if (spaceBefore && !spaceAdded)
+ line += SPACE;
+
+ line += token->value.toString();
+ if (nlAfter)
+ {
+ newLine();
+ if (cfg->SqlEnterpriseFormatter.IndentParenthesisBlock.get())
+ incrIndent();
+ }
+ else if (spaceAfter)
+ line += SPACE;
+}
+
+void FormatStatement::detokenizeRightPar(FormatStatement::FormatToken* token, bool spaceBefore, bool spaceAfter, bool nlBefore, bool nlAfter)
+{
+ bool spaceAdded = endsWithSpace();
+ if (nlBefore)
+ {
+ newLine();
+ spaceAdded = true;
+ if (cfg->SqlEnterpriseFormatter.IndentParenthesisBlock.get())
+ decrIndent();
+ }
+
+ spaceAdded |= applyIndent();
+ if (spaceBefore && !spaceAdded)
+ line += SPACE;
+
+ line += token->value.toString();
+ if (nlAfter)
+ newLine();
+ else if (spaceAfter)
+ line += SPACE;
+}
+
+void FormatStatement::resetIndents()
+{
+ indents.clear();
+ indents.push(0);
+}
+
+void FormatStatement::removeAllSpaces()
+{
+ removeAllSpacesFromLine();
+ while (endsWithSpace() && lines.size() > 0)
+ {
+ line = lines.takeLast();
+ removeAllSpacesFromLine();
+
+ if (lines.size() == 0)
+ break;
+ }
+}
+
+void FormatStatement::removeAllSpacesFromLine()
+{
+ while (endsWithSpace() && line.length() > 0)
+ line.chop(1);
+}
+
+void FormatStatement::updateLastToken(FormatStatement::FormatToken* token)
+{
+ if (!isMetaType(token->type))
+ lastToken = token;
+}
+
+QString FormatStatement::getFinalLineUpName(const QString& lineUpName)
+{
+ QString finalName = statementName;
+ if (!lineUpName.isNull())
+ finalName += "_" + lineUpName;
+
+ return finalName;
+}
+
+int FormatStatement::predictCurrentIndent(FormatToken* currentMetaToken)
+{
+ QString lineBackup = line;
+ bool isSpace = applyIndent() || endsWithSpace();
+
+ if (!isSpace)
+ {
+ // We haven't added any space and there is no space currently at the end of line.
+ // We need to predict if next real (printable) token will require space to be added.
+ // If yes, we add it virtually here, so we know the indent required afterwards.
+ // First we need to find next real token:
+ int tokenIdx = tokens.indexOf(currentMetaToken);
+ FormatToken* nextRealToken = nullptr;
+ for (FormatToken* tk : tokens.mid(tokenIdx + 1))
+ {
+ if (!isMetaType(tk->type))
+ {
+ nextRealToken = tk;
+ break;
+ }
+ }
+
+ // If the real token was found we can see if it will require additional space for indent:
+ if ((nextRealToken && isSpaceExpectingType(lastToken->type) && isSpaceExpectingType(nextRealToken->type)) || willStartWithNewLine(nextRealToken))
+ {
+ // Next real token does not start with new line, but it does require additional space:
+ line += SPACE;
+ }
+ }
+
+ int result = line.length();
+ line = lineBackup;
+ return result;
+}
+
+bool FormatStatement::willStartWithNewLine(FormatStatement::FormatToken* token)
+{
+ return (token->type == FormatToken::PAR_DEF_LEFT && cfg->SqlEnterpriseFormatter.NlBeforeOpenParDef) ||
+ (token->type == FormatToken::PAR_EXPR_LEFT && cfg->SqlEnterpriseFormatter.NlBeforeOpenParExpr) ||
+ (token->type == FormatToken::PAR_FUNC_LEFT && cfg->SqlEnterpriseFormatter.NlBeforeOpenParExpr) ||
+ (token->type == FormatToken::PAR_DEF_RIGHT && cfg->SqlEnterpriseFormatter.NlBeforeCloseParDef) ||
+ (token->type == FormatToken::PAR_EXPR_RIGHT && cfg->SqlEnterpriseFormatter.NlBeforeCloseParExpr) ||
+ (token->type == FormatToken::PAR_FUNC_RIGHT && cfg->SqlEnterpriseFormatter.NlBeforeCloseParExpr) ||
+ (token->type == FormatToken::NEW_LINE);
+}
+
+void FormatStatement::formatId(const QString& value)
+{
+ if (cfg->SqlEnterpriseFormatter.AlwaysUseNameWrapping.get())
+ line += wrapObjName(value, dialect, wrapper);
+ else
+ line += wrapObjIfNeeded(value, dialect, wrapper);
+}
+
+FormatStatement* FormatStatement::forQuery(SqliteStatement* query, Dialect dialect, NameWrapper wrapper, Cfg::SqlEnterpriseFormatterConfig* cfg)
+{
+ FormatStatement* formatStmt = forQuery(query);
+ if (formatStmt)
+ {
+ formatStmt->dialect = dialect;
+ formatStmt->wrapper = wrapper;
+ formatStmt->cfg = cfg;
+ }
+ return formatStmt;
+}