aboutsummaryrefslogtreecommitdiffstats
path: root/SQLiteStudio3/coreSQLiteStudio/parser/ast/sqliteorderby.cpp
blob: 4db4b5d6c0dfe6a054f6a7cc0a69f0b28b1a1abb (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
#include "sqliteorderby.h"
#include "sqliteexpr.h"
#include "parser/statementtokenbuilder.h"
#include "common/global.h"
#include <QDebug>

SqliteOrderBy::SqliteOrderBy()
{
}

SqliteOrderBy::SqliteOrderBy(const SqliteOrderBy& other) :
    SqliteStatement(other), order(other.order), nulls(other.nulls)
{
    DEEP_COPY_FIELD(SqliteExpr, expr);
}

SqliteOrderBy::SqliteOrderBy(SqliteExpr *expr, SqliteSortOrder order, SqliteNulls nulls)
{
    this->expr = expr;
    this->order = order;
    this->nulls = nulls;
    if (expr)
        expr->setParent(this);
}

SqliteOrderBy::~SqliteOrderBy()
{
}

SqliteStatement*SqliteOrderBy::clone()
{
    return new SqliteOrderBy(*this);
}

bool SqliteOrderBy::isSimpleColumn() const
{
    return !getColumnName().isEmpty();
}

QString SqliteOrderBy::getColumnName() const
{
    if (!expr)
        return QString();

    if (expr->mode == SqliteExpr::Mode::ID)
        return expr->column;

    if (expr->mode == SqliteExpr::Mode::COLLATE && expr->expr1 && expr->expr1->mode == SqliteExpr::Mode::ID)
        return expr->expr1->column;

    return QString();
}

QString SqliteOrderBy::getCollation() const
{
    if (expr->mode == SqliteExpr::Mode::COLLATE)
        return expr->collation;

    return QString();
}

QString SqliteOrderBy::getColumnString() const
{
    QString res = getColumnName();
    if (res.isNull())
        return expr->detokenize();

    return res;
}

void SqliteOrderBy::setColumnName(const QString& name)
{
    if (expr && expr->mode == SqliteExpr::Mode::COLLATE)
    {
        safe_delete(expr->expr1);
        expr->expr1 = new SqliteExpr();
        expr->expr1->setParent(expr);
        expr->expr1->initId(name);
    }
    else
    {
        safe_delete(expr);
        expr = new SqliteExpr();
        expr->setParent(this);
        expr->initId(name);
    }
}

void SqliteOrderBy::setCollation(const QString& name)
{
    if (!expr)
        return;

    if (expr->mode == SqliteExpr::Mode::COLLATE)
    {
        expr->collation = name;
        return;
    }

    SqliteExpr* collationExpr = new SqliteExpr();
    collationExpr->initCollate(expr, name);
    expr->setParent(collationExpr);
    collationExpr->setParent(this);
    expr = collationExpr;
}

TokenList SqliteOrderBy::rebuildTokensFromContents()
{
    StatementTokenBuilder builder;
    builder.withStatement(expr);
    if (order != SqliteSortOrder::null)
        builder.withSpace().withKeyword(sqliteSortOrder(order));

    if (nulls != SqliteNulls::null)
        builder.withSpace().withKeyword("NULLS").withSpace().withKeyword(sqliteNulls(nulls));

    return builder.build();
}

void SqliteOrderBy::evaluatePostParsing()
{
    pullLastCollationAsOuterExpr();
}

void SqliteOrderBy::pullLastCollationAsOuterExpr()
{
    /*
     * If the order statement is like: columnName + 2 COLLATE BINARY ASC
     * then the COLLATE is associated with the "2" subexpr, instead of the most outer expr.
     * Looks like SQLite's parser does the same, but they don't care about the depth as we do here.
     * Therefore if we idenfity this case, we need to pull the inner expr to outside.
     */
    TokenPtr collateToken = expr->tokens.findLast(Token::KEYWORD, "COLLATE", Qt::CaseInsensitive);
    if (collateToken.isNull())
        return;

    int lastCollateIdx = expr->tokens.indexOf(collateToken);
    if (expr->tokens.mid(lastCollateIdx).filterWhiteSpaces().size() != 2)
        return;

    // This is the case. We need to pull the expr to the top level.
    SqliteStatement* stmt = expr->findStatementWithToken(collateToken);
    SqliteExpr* collateExpr = dynamic_cast<SqliteExpr*>(stmt);
    if (!collateExpr)
    {
        qCritical() << "Could not cast statement to SqliteExpr, even though it's identified as COLLATE expr. The actual contents:"
                    << collateExpr->detokenize();
        return;
    }

    if (collateExpr == expr)
        return; // it's already the top-level expr, we're fine.

    SqliteExpr* parentExpr = dynamic_cast<SqliteExpr*>(collateExpr->parentStatement());
    if (!parentExpr)
    {
        qCritical() << "Could not cast parent statement to SqliteExpr, even though parent of COLLATE should be another expr at this stage."
                    << "The qobject type of parent:" << collateExpr->parentStatement()->metaObject()->className();
        return;
    }

    // Take out COLLATE from its current place
    collateExpr->expr1->setParent(parentExpr);             // New parent of COLLATE's expr is now parent of COLLATE
    parentExpr->replace(collateExpr, collateExpr->expr1);  // New child expr of COLLATE's parent is now child of COLLATE

    // Put it at top level
    collateExpr->expr1 = expr;      // COLLATE's child is set to the old top level expr
    expr->setParent(collateExpr);   // Old top level expr gets COLLATE as parent
    expr = collateExpr;             // New top level is now COLLATE
    collateExpr->setParent(this);   // COLLATE's new parent is this

    rebuildTokens();
}

void SqliteOrderBy::clearCollation()
{
    if (expr->mode != SqliteExpr::Mode::COLLATE)
        return;

    SqliteExpr* tmpExpr = expr;
    expr = tmpExpr->expr1;
    expr->setParent(this);
    delete tmpExpr;
}