aboutsummaryrefslogtreecommitdiffstats
path: root/SQLiteStudio3/coreSQLiteStudio/common/readwritelocker.cpp
blob: 0eaca75ee05b0979412d5b811c419e69096aae5f (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
#include "readwritelocker.h"
#include "parser/lexer.h"
#include <QReadWriteLock>
#include <QReadLocker>
#include <QWriteLocker>

ReadWriteLocker::ReadWriteLocker(QReadWriteLock* lock, Mode mode)
{
    init(lock, mode);
}

ReadWriteLocker::ReadWriteLocker(QReadWriteLock* lock, const QString& query, Dialect dialect, bool noLock)
{
    init(lock, getMode(query, dialect, noLock));
}

ReadWriteLocker::~ReadWriteLocker()
{
    if (readLocker)
    {
        delete readLocker;
        readLocker = nullptr;
    }

    if (writeLocker)
    {
        delete writeLocker;
        writeLocker = nullptr;
    }
}

void ReadWriteLocker::init(QReadWriteLock* lock, ReadWriteLocker::Mode mode)
{
    switch (mode)
    {
        case ReadWriteLocker::READ:
            readLocker = new QReadLocker(lock);
            break;
        case ReadWriteLocker::WRITE:
            writeLocker = new QWriteLocker(lock);
            break;
        case ReadWriteLocker::NONE:
            // Nothing to lock.
            break;
    }
}

ReadWriteLocker::Mode ReadWriteLocker::getMode(const QString &query, Dialect dialect, bool noLock)
{
    static QStringList readOnlyCommands = {"ANALYZE", "EXPLAIN", "PRAGMA"};

    if (noLock)
        return ReadWriteLocker::NONE;

    TokenList tokens = Lexer::tokenize(query, dialect);
    int keywordIdx = tokens.indexOf(Token::KEYWORD);

    if (keywordIdx > -1 && readOnlyCommands.contains(tokens[keywordIdx]->value.toUpper()))
        return ReadWriteLocker::READ;

    if (keywordIdx > -1 && tokens[keywordIdx]->value.toUpper() == "WITH")
    {
        bool matched = false;
        bool isSelect = false;
        int depth = 0;
        for (TokenPtr token : tokens)
        {
            switch (token->type)
            {
                case Token::PAR_LEFT:
                    depth++;
                    break;
                case Token::PAR_RIGHT:
                    depth--;
                    break;
                case Token::KEYWORD:
                    if (depth == 0)
                    {
                        QString val = token->value.toUpper();
                        if (val == "SELECT")
                        {
                            matched = true;
                            isSelect = true;
                        }
                        else if (val == "DELETE" || val == "UPDATE" || val == "INSERT")
                        {
                            matched = true;
                        }
                    }
                    break;
                default:
                    break;
            }

            if (matched)
                break;
        }
        if (isSelect)
            return ReadWriteLocker::READ;
    }

    return ReadWriteLocker::WRITE;
}