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
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
|
#ifndef STATEMENTTOKENBUILDER_H
#define STATEMENTTOKENBUILDER_H
#include "token.h"
#include "ast/sqliteconflictalgo.h"
#include "ast/sqlitesortorder.h"
#include "dialect.h"
class SqliteStatement;
/**
* @brief Builder producing token list basing on certain inputs.
*
* This builder provides several methods to build list of tokens from various input values. It can produce
* token list for entire AST objects, or it can produce token list for list of names, etc.
*
* Token builder is used in SqliteStatement derived classes to rebuild SqliteStatement::tokens basing on the
* values in their class members.
*
* Typical use case:
* @code
* TokenList SqliteCreateView::rebuildTokensFromContents()
* {
* StatementTokenBuilder builder;
*
* builder.withKeyword("CREATE").withSpace();
* if (tempKw)
* builder.withKeyword("TEMP").withSpace();
* else if (temporaryKw)
* builder.withKeyword("TEMPORARY").withSpace();
*
* builder.withKeyword("VIEW").withSpace();
* if (ifNotExists)
* builder.withKeyword("IF").withSpace().withKeyword("NOT").withSpace().withKeyword("EXISTS").withSpace();
*
* if (dialect == Dialect::Sqlite3 && !database.isNull())
* builder.withOther(database, dialect).withOperator(".");
*
* builder.withOther(view, dialect).withSpace().withKeyword("AS").withStatement(select);
*
* return builder.build();
* }
* @endcode
*/
class StatementTokenBuilder
{
public:
/**
* @brief Adds keyword token.
* @param value Value of the keyword token.
* @return Reference to the builder for the further building.
*
* Keyword \p value gets converted to upper case.
*/
StatementTokenBuilder& withKeyword(const QString& value);
/**
* @brief Adds "other" token (some object name, or other word).
* @param value Value for the token.
* @return Reference to the builder for the further building.
*
* This is used for table names, etc. The \p value is quoted just as passed.
*/
StatementTokenBuilder& withOther(const QString& value);
/**
* @brief Adds "other" token (some object name, or other word).
* @param value Value for the token.
* @param dialect Dialect used for wrapping the value.
* @return Reference to the builder for the further building.
*
* The \p value is wrapped with the proper wrapper using wrapObjIfNeeded().
*
* @overload
*/
StatementTokenBuilder& withOther(const QString& value, Dialect dialect);
/**
* @brief Adds string using double-quote wrapping.
* @param value Value for the token.
* @param dialect Dialect used for wrapping the value if double-quote could not be used.
* @return Reference to the builder for the further building.
*
* The \p value is wrapped with double quote, but if it's not possible then the proper wrapper is used by wrapObjIfNeeded().
*
* @overload
*/
StatementTokenBuilder& withStringPossiblyOther(const QString& value, Dialect dialect);
/**
* @brief Adds list of "other" tokens.
* @param value List of values for tokens.
* @param dialect Dialect used for wrapping values.
* @param separator Optional value for separator tokens.
* @return Reference to the builder for the further building.
*
* Given the input \p value, this method produces list of tokens. Additionally it can put extra separator
* token between all produced tokens using the \p separator value. To skip separator tokens pass
* an empty string as the separator value.
*/
StatementTokenBuilder& withOtherList(const QList<QString>& value, Dialect dialect, const QString& separator = ",");
/**
* @brief Adds list of "other" tokens.
* @param value List of values for tokens.
* @param separator Optional value for separator tokens.
* @return Reference to the builder for the further building.
*
* Works just like the other withOtherList() method, except it doesn't wrap values with wrapObjIfNeeded().
*
* @overload
*/
StatementTokenBuilder& withOtherList(const QList<QString>& value, const QString& separator = ",");
/**
* @brief Adds operator token.
* @param value Value of the operator (";", "+", etc).
* @return Reference to the builder for the further building.
*/
StatementTokenBuilder& withOperator(const QString& value);
/**
* @brief Adds comment token.
* @param value Comment value, including start/end characters of the comment.
* @return Reference to the builder for the further building.
*/
StatementTokenBuilder& withComment(const QString& value);
/**
* @brief Adds decimal number token.
* @param value Value for the token.
* @return Reference to the builder for the further building.
*/
StatementTokenBuilder& withFloat(const QVariant &value);
/**
* @brief Add integer numer token.
* @param value Value for the token.
* @return Reference to the builder for the further building.
*/
StatementTokenBuilder& withInteger(qint64 value);
/**
* @brief Adds bind parameter token.
* @param value Name of the bind parameter, including ":" or "@" at the begining.
* @return Reference to the builder for the further building.
*/
StatementTokenBuilder& withBindParam(const QString& value);
/**
* @brief Adds left parenthesis token (<tt>"("</tt>).
* @return Reference to the builder for the further building.
*/
StatementTokenBuilder& withParLeft();
/**
* @brief Adds right parenthesis token (<tt>")"</tt>).
* @return Reference to the builder for the further building.
*/
StatementTokenBuilder& withParRight();
/**
* @brief Adds a single whitespace token.
* @return Reference to the builder for the further building.
*/
StatementTokenBuilder& withSpace();
/**
* @brief Adds BLOB value token.
* @param value BLOB value for the token.
* @return Reference to the builder for the further building.
*/
StatementTokenBuilder& withBlob(const QString& value);
/**
* @brief Adds string value token.
* @param value Value for the token.
* @return Reference to the builder for the further building.
*
* The string is wrapped with single quote characters if it's not wrapped yet.
*/
StatementTokenBuilder& withString(const QString& value);
/**
* @brief Adds set of tokens represeting "ON CONFLICT" statement.
* @param onConflict Conflict resolution algorithm to build for.
* @return Reference to the builder for the further building.
*
* If algorithm is SqliteConflictAlgo::null, no tokens are added.
*/
StatementTokenBuilder& withConflict(SqliteConflictAlgo onConflict);
/**
* @brief Adds space and <tt>"ASC"/"DESC"</tt> token.
* @param sortOrder Sort order to use.
* @return Reference to the builder for the further building.
*
* If the sort order is SqliteSortOrder::null, no tokens are added.
*/
StatementTokenBuilder& withSortOrder(SqliteSortOrder sortOrder);
/**
* @brief Adds set of tokens representing entire statement.
* @param stmt Statement to add tokens for.
* @return Reference to the builder for the further building.
*/
StatementTokenBuilder& withStatement(SqliteStatement* stmt);
/**
* @brief Adds already defined list of tokens to this builder.
* @param tokens Tokens to add.
* @return Reference to the builder for the further building.
*/
StatementTokenBuilder& withTokens(TokenList tokens);
/**
* @brief Adds literal value token (integer, decimal, string, BLOB).
* @param value Value for the token.
* @return Reference to the builder for the further building.
*
* This method tries to convert given \p value to integer,
* then to double, then it checks if the value has format <tt>X'...'</tt>
* and if if succeeded at any of those steps, then it adds appropriate
* token. If none of above succeeded, then the string token is added.
*/
StatementTokenBuilder& withLiteralValue(const QVariant& value);
/**
* @brief Adds tokens representing list of entire statements.
* @param stmtList List of statements to add tokens for.
* @param separator Optional separator to be used for separator tokens.
* @return Reference to the builder for the further building.
*
* This method is very similar to withOtherList(), except it works
* on the entire statements.
*/
template <class T>
StatementTokenBuilder& withStatementList(QList<T*> stmtList, const QString& separator = ",")
{
bool first = true;
foreach (T* stmt, stmtList)
{
if (!first)
{
if (!separator.isEmpty())
withOperator(separator);
withSpace();
}
withStatement(stmt);
first = false;
}
return *this;
}
/**
* @brief Provides all tokens added so far as a compat token list.
* @return List of tokens built so far.
*/
TokenList build() const;
/**
* @brief Cleans up all tokens added so far.
*/
void clear();
private:
/**
* @brief Adds token of given type and value.
* @param type Type of the token to add.
* @param value Value for the token to add.
* @return Reference to the builder for the further building.
*/
StatementTokenBuilder& with(Token::Type type, const QString& value);
/**
* @brief List of tokens added so far.
*/
TokenList tokens;
/**
* @brief Current character position index.
*
* This index is used to generate proper values for Token::start and Token::end.
* Each added token increments this index by the value length.
*/
int currentIdx = 0;
};
#endif // STATEMENTTOKENBUILDER_H
|