Branch data Line data Source code
1 : : #pragma once
2 : :
3 : : /*
4 : : MIT License
5 : :
6 : : Copyright (c) 2014-2024 Stephane Cuillerdier (aka aiekick)
7 : :
8 : : Permission is hereby granted, free of charge, to any person obtaining a copy
9 : : of this software and associated documentation files (the "Software"), to deal
10 : : in the Software without restriction, including without limitation the rights
11 : : to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12 : : copies of the Software, and to permit persons to whom the Software is
13 : : furnished to do so, subject to the following conditions:
14 : :
15 : : The above copyright notice and this permission notice shall be included in all
16 : : copies or substantial portions of the Software.
17 : :
18 : : THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19 : : IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20 : : FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21 : : AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22 : : LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23 : : OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
24 : : SOFTWARE.
25 : : */
26 : :
27 : : // ezSqlite is part of the ezLibs project : https://github.com/aiekick/ezLibs.git
28 : :
29 : : #include <cstdint>
30 : : #include <cstring>
31 : : #include <string>
32 : : #include <vector>
33 : : #include <map>
34 : : #include <sstream> //stringstream
35 : : #include <cstdarg> // variadic
36 : : #include <memory>
37 : : #include <limits>
38 : :
39 : : #include "ezStr.hpp"
40 : :
41 : : namespace ez {
42 : : namespace sqlite {
43 : :
44 : : #ifdef SQLITE_API
45 : : inline std::string readStringColumn(sqlite3_stmt* vStmt, int32_t vColumn) {
46 : : const char* str = reinterpret_cast<const char*>(sqlite3_column_text(vStmt, vColumn));
47 : : if (str != nullptr) {
48 : : return str;
49 : : }
50 : : return {};
51 : : }
52 : : #endif
53 : :
54 : : // to test
55 : : // Optionnal Insert Query
56 : : // this query replace INSERT OR IGNORE who cause the auto increment
57 : : // of the AUTOINCREMENT KEY even if not inserted and ignored
58 : : /*
59 : : * INSERT INTO banks (number, name)
60 : : * SELECT '30003', 'LCL'
61 : : * WHERE NOT EXISTS (
62 : : * SELECT 1 FROM banks WHERE number = '30002' and name = 'LCL'
63 : : * );
64 : : *
65 : : * give :
66 : : * QueryBuilder().
67 : : * setTable("banks").
68 : : * addOrSetField("number", 30002).
69 : : * addOrSetField("name", "LCL").
70 : : * build(QueryType::INSERT_IF_NOT_EXIST);
71 : : */
72 : : // ex : build("banks", {{"number", "30002"},{"name","LCL"}});
73 : :
74 : : enum class QueryType { INSERT = 0, UPDATE, INSERT_IF_NOT_EXIST, Count };
75 : :
76 : : class QueryBuilder {
77 : : private:
78 : : class Field {
79 : : private:
80 : : std::string m_key;
81 : : std::string m_value;
82 : : bool m_subQuery = false;
83 : :
84 : : public:
85 : : Field() = default;
86 : 0 : explicit Field(const std::string& vKey, const std::string& vValue, const bool vSubQuery) : m_key(vKey), m_value(vValue), m_subQuery(vSubQuery) {
87 : 0 : const auto zero_pos = m_value.find('\0');
88 : 0 : if (zero_pos != std::string::npos) {
89 : 0 : m_value.clear();
90 : 0 : }
91 : 0 : }
92 : 0 : const std::string& getRawKey() const { return m_key; }
93 : 0 : const std::string& getRawValue() const { return m_value; }
94 : 0 : std::string getFinalValue() const {
95 : 0 : if (m_subQuery) {
96 : 0 : return "(" + m_value + ")";
97 : 0 : }
98 : 0 : return "\"" + m_value + "\"";
99 : 0 : }
100 : : };
101 : :
102 : : std::string m_table;
103 : : std::map<std::string, Field> m_dicoFields;
104 : : std::vector<std::string> m_fields;
105 : : std::vector<std::string> m_where;
106 : :
107 : : public:
108 : 0 : QueryBuilder& setTable(const std::string& vTable) {
109 : 0 : m_table = vTable;
110 : 0 : return *this;
111 : 0 : }
112 : 0 : QueryBuilder& addOrSetField(const std::string& vKey, const std::string& vValue) {
113 : 0 : m_addKeyIfNotExist(vKey);
114 : 0 : m_dicoFields[vKey] = Field(vKey, vValue, false);
115 : 0 : return *this;
116 : 0 : }
117 : 0 : QueryBuilder& addOrSetField(const std::string& vKey, const char* fmt, ...) {
118 : 0 : va_list args;
119 : 0 : va_start(args, fmt);
120 : 0 : static char TempBuffer[1024 + 1];
121 : 0 : const int w = vsnprintf(TempBuffer, 1024, fmt, args);
122 : 0 : va_end(args);
123 : 0 : if (w > 0) {
124 : 0 : m_addKeyIfNotExist(vKey);
125 : 0 : m_dicoFields[vKey] = Field(vKey, std::string(TempBuffer), false);
126 : 0 : }
127 : 0 : return *this;
128 : 0 : }
129 : : template <typename T>
130 : : QueryBuilder& addOrSetField(const std::string& vKey, const T& vValue) {
131 : : m_addKeyIfNotExist(vKey);
132 : : m_dicoFields[vKey] = Field(vKey, ez::str::toStr<T>(vValue), false);
133 : : return *this;
134 : : }
135 : 0 : QueryBuilder& addOrSetFieldQuery(const std::string& vKey, const std::string& vValue) {
136 : 0 : m_addKeyIfNotExist(vKey);
137 : 0 : m_dicoFields[vKey] = Field(vKey, vValue, true);
138 : 0 : return *this;
139 : 0 : }
140 : 0 : QueryBuilder& addOrSetFieldQuery(const std::string& vKey, const char* fmt, ...) {
141 : 0 : va_list args;
142 : 0 : va_start(args, fmt);
143 : 0 : static char TempBuffer[1024 + 1];
144 : 0 : const int w = vsnprintf(TempBuffer, 1024, fmt, args);
145 : 0 : va_end(args);
146 : 0 : if (w > 0) {
147 : 0 : m_addKeyIfNotExist(vKey);
148 : 0 : m_dicoFields[vKey] = Field(vKey, std::string(TempBuffer), true);
149 : 0 : }
150 : 0 : return *this;
151 : 0 : }
152 : : template <typename T>
153 : : QueryBuilder& addWhere(const T& vValue) {
154 : : m_where.emplace_back(ez::str::toStr<T>(vValue));
155 : : return *this;
156 : : }
157 : 0 : QueryBuilder& addWhere(const std::string& vValue) {
158 : 0 : m_where.emplace_back(vValue);
159 : 0 : return *this;
160 : 0 : }
161 : 0 : QueryBuilder& addWhere(const char* fmt, ...) {
162 : 0 : va_list args;
163 : 0 : va_start(args, fmt);
164 : 0 : static char TempBuffer[1024 + 1];
165 : 0 : const int w = vsnprintf(TempBuffer, 1024, fmt, args);
166 : 0 : va_end(args);
167 : 0 : if (w) {
168 : 0 : m_where.emplace_back(std::string(TempBuffer));
169 : 0 : }
170 : 0 : return *this;
171 : 0 : }
172 : 0 : std::string build(const QueryType vType) {
173 : 0 : switch (vType) {
174 : 0 : case QueryType::INSERT: return m_buildTypeInsert();
175 : 0 : case QueryType::UPDATE: return m_buildTypeUpdate();
176 : 0 : case QueryType::INSERT_IF_NOT_EXIST: return m_buildTypeInsertIfNotExist();
177 : 0 : case QueryType::Count:
178 : 0 : default: break;
179 : 0 : }
180 : 0 : return {};
181 : 0 : }
182 : :
183 : : private:
184 : 0 : void m_addKeyIfNotExist(const std::string& vKey) {
185 : 0 : if (m_dicoFields.find(vKey) == m_dicoFields.end()) {
186 : 0 : m_fields.emplace_back(vKey);
187 : 0 : }
188 : 0 : }
189 : 0 : std::string m_buildTypeInsert() {
190 : 0 : std::string query = "INSERT INTO " + m_table + " (\n\t";
191 : 0 : size_t idx = 0;
192 : 0 : for (const auto& field : m_fields) {
193 : 0 : if (idx != 0) {
194 : 0 : query += ",\n\t";
195 : 0 : }
196 : 0 : query += m_dicoFields.at(field).getRawKey();
197 : 0 : ++idx;
198 : 0 : }
199 : 0 : query += "\n) VALUES (\n\t";
200 : 0 : idx = 0;
201 : 0 : for (const auto& field : m_fields) {
202 : 0 : if (idx != 0) {
203 : 0 : query += ",\n\t";
204 : 0 : }
205 : 0 : query += m_dicoFields.at(field).getFinalValue();
206 : 0 : ++idx;
207 : 0 : }
208 : 0 : query += "\n);";
209 : 0 : return query;
210 : 0 : }
211 : 0 : std::string m_buildTypeInsertIfNotExist() {
212 : 0 : std::string query = "INSERT INTO " + m_table + " (\n\t";
213 : 0 : size_t idx = 0;
214 : 0 : for (const auto& field : m_fields) {
215 : 0 : if (idx != 0) {
216 : 0 : query += ",\n\t";
217 : 0 : }
218 : 0 : query += m_dicoFields.at(field).getRawKey();
219 : 0 : ++idx;
220 : 0 : }
221 : 0 : query += "\n) SELECT \n\t";
222 : 0 : idx = 0;
223 : 0 : for (const auto& field : m_fields) {
224 : 0 : if (idx != 0) {
225 : 0 : query += ",\n\t";
226 : 0 : }
227 : 0 : query += m_dicoFields.at(field).getFinalValue();
228 : 0 : ++idx;
229 : 0 : }
230 : 0 : query += " WHERE NOT EXISTS (SELECT 1 FROM " + m_table + "\nWHERE\n\t";
231 : 0 : idx = 0;
232 : 0 : for (const auto& field : m_fields) {
233 : 0 : if (idx != 0) {
234 : 0 : query += "\n\tAND ";
235 : 0 : }
236 : 0 : query += m_dicoFields.at(field).getRawKey() + " = " + m_dicoFields.at(field).getFinalValue();
237 : 0 : ++idx;
238 : 0 : }
239 : 0 : query += "\n);";
240 : 0 : return query;
241 : 0 : }
242 : 0 : std::string m_buildTypeUpdate() {
243 : 0 : std::string query = "UPDATE " + m_table + " SET\n\t";
244 : 0 : size_t idx = 0;
245 : 0 : for (const auto& field : m_fields) {
246 : 0 : if (idx != 0) {
247 : 0 : query += ",\n\t";
248 : 0 : }
249 : 0 : query += m_dicoFields.at(field).getRawKey() + " = " + m_dicoFields.at(field).getFinalValue();
250 : 0 : ++idx;
251 : 0 : }
252 : 0 : query += "\nWHERE\n\t";
253 : 0 : idx = 0;
254 : 0 : for (const auto& where : m_where) {
255 : 0 : if (idx != 0) {
256 : 0 : query += "\tAND ";
257 : 0 : }
258 : 0 : query += "(" + where + ")\n";
259 : 0 : ++idx;
260 : 0 : }
261 : 0 : query += ";";
262 : 0 : return query;
263 : 0 : }
264 : : };
265 : :
266 : : //---------------------------------------------
267 : : // Parser
268 : : //---------------------------------------------
269 : : class Parser {
270 : : public:
271 : : struct StringRef {
272 : : const char* data;
273 : : size_t size;
274 : 101 : StringRef() : data(NULL), size(0u) {}
275 : 152 : StringRef(const char* vData, size_t vSize) : data(vData), size(vSize) {}
276 : 0 : bool empty() const { return size == 0u; }
277 : 0 : std::string toString() const { return size ? std::string(data, size) : std::string(); }
278 : : };
279 : :
280 : : struct SourcePos {
281 : : uint32_t offset; // octets depuis le d?but
282 : : uint32_t line; // 1-based
283 : : uint32_t column; // 1-based (en octets)
284 : 215 : SourcePos() : offset(0u), line(0u), column(0u) {}
285 : : };
286 : :
287 : : struct Error {
288 : : SourcePos pos;
289 : : std::string message; // ex: "token inexpected"
290 : : std::string expectedHint; // ex: "expected: FROM | VALUES"
291 : 13 : Error() {}
292 : : };
293 : :
294 : : // clang-format off
295 : : enum class TokenKind : uint16_t {
296 : : Identifier, String, Number, Blob,
297 : : Parameter, // ?, ?123, :name, @name, $name
298 : :
299 : : // Mots-cl?s (sous-ensemble utile)
300 : : KwSelect, KwFrom, KwWhere, KwGroup, KwBy, KwHaving,
301 : : KwOrder, KwLimit, KwOffset, KwWith,
302 : : KwInsert, KwInto, KwValues, KwUpdate, KwSet, KwDelete,
303 : : KwCreate, KwTable, KwIf, KwNot, KwExists, KwPrimary, KwKey,
304 : : KwUnique, KwCheck, KwReferences, KwWithout, KwRowid, KwOn, KwConflict,
305 : : KwAs,
306 : :
307 : : // Op?rateurs / d?limiteurs
308 : : Plus, Minus, Star, Slash, Percent, PipePipe, Amp, Pipe, Tilde,
309 : : Shl, Shr, Eq, EqEq, Ne, Ne2, Lt, Le, Gt, Ge, Assign,
310 : : Comma, Dot, LParen, RParen, Semicolon,
311 : :
312 : : EndOfFile,
313 : : Unknown
314 : : };
315 : : // clang-format on
316 : :
317 : : struct Token {
318 : : TokenKind kind{TokenKind::Unknown};
319 : : SourcePos start;
320 : : SourcePos end; // end.offset = 1 + dernier octet inclus (pour affichage)
321 : : StringRef lex; // vue sur vSql (pas de copie)
322 : : };
323 : :
324 : : struct StatementRange {
325 : : uint32_t beginOffset{}; // inclus
326 : : uint32_t endOffset{}; // exclus
327 : : };
328 : :
329 : : enum class StatementKind : uint8_t { Select, Insert, Update, Delete, CreateTable, Other };
330 : :
331 : : struct Statement {
332 : : StatementKind kind{StatementKind::Other};
333 : : StatementRange range;
334 : : };
335 : :
336 : : struct Options {
337 : : bool allowNestedBlockComments = false; // /* ... /* ... */ ... */ (si true)
338 : : bool trackAllTokens = true; // remplir Report.tokens
339 : : bool caseInsensitiveKeywords = true; // LIKE SQLite
340 : : };
341 : :
342 : : struct Report {
343 : : bool ok{true};
344 : : std::vector<Error> errors;
345 : : std::vector<Statement> statements;
346 : : std::vector<Token> tokens; // rempli si trackAllTokens = true
347 : : };
348 : :
349 : : private:
350 : : // --------- private (static utils)
351 [ + + ][ - + ]: 153 : static bool m_isSpace(char c) { return c == ' ' || c == '\t' || c == '\n' || c == '\r' || c == '\f' || c == '\v'; }
[ + + ][ - + ]
[ - + ][ - + ]
352 [ + + ][ + + ]: 158 : static bool m_isDigit(char c) { return c >= '0' && c <= '9'; }
353 [ # # ][ # # ]: 0 : static bool m_isHex(char c) { return (c >= '0' && c <= '9') || (c >= 'A' && c <= 'F') || (c >= 'a' && c <= 'f'); }
[ # # ][ # # ]
[ # # ][ # # ]
354 [ + + ][ + + ]: 290 : static bool m_isAlpha(char c) { return (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || (c == '_'); }
[ + + ][ + - ]
[ + + ]
355 [ + + ][ - + ]: 213 : static bool m_isAlnum(char c) { return m_isAlpha(c) || m_isDigit(c); }
356 [ + + ][ + - ]: 506 : static char m_up(char c) { return (c >= 'a' && c <= 'z') ? static_cast<char>(c - 32) : c; }
357 : 878 : static bool m_ieq(const StringRef& a, const char* b) {
358 [ - + ]: 878 : if (!b)
359 : 0 : return false;
360 : 878 : size_t blen = 0u;
361 [ + + ]: 5240 : while (b[blen] != '\0')
362 : 4362 : ++blen;
363 [ + + ]: 878 : if (a.size != blen)
364 : 761 : return false;
365 [ + + ]: 284 : for (size_t i = 0; i < a.size; ++i) {
366 [ + + ]: 253 : if (m_up(a.data[i]) != m_up(b[i]))
367 : 86 : return false;
368 : 253 : }
369 : 31 : return true;
370 : 117 : }
371 : :
372 : : private:
373 : : // --------- private (vars)
374 : : Options m_options;
375 : : uint32_t m_sourceSize{};
376 : : std::vector<uint32_t> m_lineStarts; // offset du d?but de chaque ligne
377 : :
378 : : public:
379 : : // --------- public (methods)
380 : : Parser() = default;
381 : 14 : Parser(const Options& vOptions) : m_options(vOptions) {}
382 : :
383 : : // API principale
384 : 14 : bool parse(const std::string& vSql, Report& vOut) {
385 : 14 : m_sourceSize = static_cast<uint32_t>(vSql.size());
386 : 14 : m_buildLineStarts(vSql);
387 : :
388 : 14 : vOut.ok = true;
389 : 14 : vOut.errors.clear();
390 : 14 : vOut.statements.clear();
391 [ + + ]: 14 : if (m_options.trackAllTokens)
392 : 13 : vOut.tokens.clear();
393 : :
394 : : // 1) Lexing
395 : 14 : std::vector<Token> toks;
396 : 14 : std::vector<Error> lexErrs;
397 : 14 : m_lex(vSql, toks, lexErrs);
398 : :
399 [ + + ]: 14 : if (m_options.trackAllTokens) {
400 : 13 : vOut.tokens = toks;
401 : 13 : }
402 [ - + ]: 14 : if (!lexErrs.empty()) {
403 [ # # ]: 0 : for (size_t i = 0; i < lexErrs.size(); ++i)
404 : 0 : vOut.errors.push_back(lexErrs[i]);
405 : 0 : }
406 : :
407 : : // 2) Split statements
408 : 14 : std::vector<StatementRange> ranges;
409 : 14 : m_splitStatements(toks, ranges);
410 : :
411 : : // 3) D?tection kind + v?rifs structurelles
412 [ + + ]: 28 : for (size_t i = 0; i < ranges.size(); ++i) {
413 : 14 : const StatementRange& r = ranges[i];
414 : 14 : Statement st;
415 : 14 : st.range = r;
416 : 14 : st.kind = m_detectKind(toks, r);
417 : : // V?rifs g?n?rales: parenth?ses
418 : 14 : m_checkParens(toks, r, vOut);
419 : :
420 : : // V?rifs par kind
421 : 14 : switch (st.kind) {
422 [ + + ]: 2 : case StatementKind::CreateTable: m_checkCreateTable(toks, r, vSql, vOut); break;
423 [ + + ]: 2 : case StatementKind::Insert: m_checkInsert(toks, r, vOut); break;
424 [ + + ]: 1 : case StatementKind::Update: m_checkUpdate(toks, r, vOut); break;
425 [ + + ]: 1 : case StatementKind::Delete: m_checkDelete(toks, r, vOut); break;
426 [ + + ]: 8 : case StatementKind::Select: m_checkSelect(toks, r, vOut); break;
427 [ - + ]: 0 : default: break;
428 : 14 : }
429 : 14 : vOut.statements.push_back(st);
430 : 14 : }
431 : :
432 : 14 : vOut.ok = vOut.errors.empty();
433 : 14 : return true; // true = le parse a tourn? ; vOut.ok dit s'il y a des erreurs
434 : 14 : }
435 : :
436 : 201 : bool computeLineColumn(uint32_t vOffset, uint32_t& vOutLine, uint32_t& vOutColumn) const {
437 [ - + ]: 201 : if (m_lineStarts.empty())
438 : 0 : return false;
439 [ - + ]: 201 : if (vOffset > m_sourceSize)
440 : 0 : return false;
441 : : // recherche binaire
442 : 201 : uint32_t lo = 0u;
443 : 201 : uint32_t hi = static_cast<uint32_t>(m_lineStarts.size());
444 [ + + ]: 327 : while (lo + 1u < hi) {
445 : 126 : uint32_t mid = lo + ((hi - lo) >> 1);
446 [ + + ]: 126 : if (m_lineStarts[mid] <= vOffset)
447 : 59 : lo = mid;
448 : 67 : else
449 : 67 : hi = mid;
450 : 126 : }
451 : 201 : vOutLine = lo + 1u; // 1-based
452 : 201 : vOutColumn = vOffset - m_lineStarts[lo] + 1u;
453 : 201 : return true;
454 : 201 : }
455 : :
456 : : private:
457 : : // --------- private (methods)
458 : 14 : void m_buildLineStarts(const std::string& vSql) {
459 : 14 : m_lineStarts.clear();
460 : 14 : m_lineStarts.push_back(0u);
461 [ + + ]: 329 : for (uint32_t i = 0; i < static_cast<uint32_t>(vSql.size()); ++i) {
462 : 315 : char c = vSql[i];
463 [ + + ]: 315 : if (c == '\n') {
464 : 7 : m_lineStarts.push_back(i + 1u);
465 [ - + ]: 308 : } else if (c == '\r') {
466 [ # # ][ # # ]: 0 : if (i + 1u < vSql.size() && vSql[i + 1u] == '\n') {
467 : 0 : m_lineStarts.push_back(i + 2u);
468 : 0 : ++i;
469 : 0 : } else {
470 : 0 : m_lineStarts.push_back(i + 1u);
471 : 0 : }
472 : 0 : }
473 : 315 : }
474 : 14 : }
475 : :
476 : 13 : void m_addError(std::vector<Error>& vErrs, uint32_t vOffset, const std::string& vMsg, const std::string& vExpected) const {
477 : 13 : Error e;
478 : 13 : e.pos.offset = vOffset;
479 : 13 : m_assignLineCol(vOffset, e.pos.line, e.pos.column);
480 : 13 : e.message = vMsg;
481 : 13 : e.expectedHint = vExpected;
482 : 13 : vErrs.push_back(e);
483 : 13 : }
484 : :
485 : 201 : void m_assignLineCol(uint32_t vOffset, uint32_t& vOutLine, uint32_t& vOutCol) const {
486 [ - + ]: 201 : if (!computeLineColumn(vOffset, vOutLine, vOutCol)) {
487 : 0 : vOutLine = 1u;
488 : 0 : vOutCol = vOffset + 1u;
489 : 0 : }
490 : 201 : }
491 : :
492 : : // --- Lexing ---
493 : 14 : void m_lex(const std::string& vSql, std::vector<Token>& vOutToks, std::vector<Error>& vOutErrs) const {
494 : 14 : vOutToks.clear();
495 : 14 : const uint32_t n = static_cast<uint32_t>(vSql.size());
496 : 14 : uint32_t i = 0u;
497 : :
498 : : // petit lambda pour ?mettre un token
499 : 14 : struct Emitter {
500 : 14 : const std::string* src;
501 : 14 : const Parser* self;
502 : 14 : std::vector<Token>* out;
503 : 87 : void emit(TokenKind k, uint32_t s, uint32_t e) {
504 : 87 : Token t;
505 : 87 : t.kind = k;
506 : 87 : t.start.offset = s;
507 : 87 : t.end.offset = e;
508 : 87 : self->m_assignLineCol(s, t.start.line, t.start.column);
509 [ + - ]: 87 : self->m_assignLineCol(e ? (e - 1u) : 0u, t.end.line, t.end.column);
510 [ + - ]: 87 : t.lex = StringRef(src->c_str() + s, (e >= s) ? (e - s) : 0u);
511 : 87 : out->push_back(t);
512 : 87 : }
513 : 14 : } emit = {&vSql, this, &vOutToks};
514 : :
515 [ + + ]: 167 : while (i < n) {
516 : 153 : char c = vSql[i];
517 : :
518 : : // espaces
519 [ + + ]: 153 : if (m_isSpace(c)) {
520 : 66 : ++i;
521 : 66 : continue;
522 : 66 : }
523 : :
524 : : // commentaires --
525 [ - + ]: 87 : if (c == '-') {
526 [ # # ][ # # ]: 0 : if (i + 1u < n && vSql[i + 1u] == '-') {
527 : 0 : i += 2u;
528 [ # # ][ # # ]: 0 : while (i < n && vSql[i] != '\n' && vSql[i] != '\r')
[ # # ]
529 : 0 : ++i;
530 : 0 : continue;
531 : 0 : }
532 : 0 : }
533 : : // commentaires /* ... */
534 [ - + ]: 87 : if (c == '/') {
535 [ # # ][ # # ]: 0 : if (i + 1u < n && vSql[i + 1u] == '*') {
536 : 0 : uint32_t depth = 1u;
537 : 0 : i += 2u;
538 [ # # ][ # # ]: 0 : while (i < n && depth > 0u) {
539 [ # # ][ # # ]: 0 : if (vSql[i] == '/' && i + 1u < n && vSql[i + 1u] == '*') {
[ # # ]
540 [ # # ]: 0 : if (m_options.allowNestedBlockComments) {
541 : 0 : ++depth;
542 : 0 : i += 2u;
543 : 0 : continue;
544 : 0 : }
545 : 0 : }
546 [ # # ][ # # ]: 0 : if (vSql[i] == '*' && i + 1u < n && vSql[i + 1u] == '/') {
[ # # ]
547 : 0 : --depth;
548 : 0 : i += 2u;
549 : 0 : continue;
550 : 0 : }
551 : 0 : ++i;
552 : 0 : }
553 [ # # ]: 0 : if (depth > 0u) {
554 [ # # ]: 0 : m_addError(vOutErrs, i ? (i - 1u) : 0u, "comment /* ... */ not closed", "");
555 : 0 : }
556 : 0 : continue;
557 : 0 : }
558 : 0 : }
559 : :
560 : : // cha?nes '...'
561 [ - + ]: 87 : if (c == '\'') {
562 : 0 : const uint32_t s = i++;
563 : 0 : bool closed = false;
564 [ # # ]: 0 : while (i < n) {
565 [ # # ]: 0 : if (vSql[i] == '\'') {
566 [ # # ][ # # ]: 0 : if (i + 1u < n && vSql[i + 1u] == '\'') {
567 : 0 : i += 2u;
568 : 0 : continue;
569 : 0 : } // quote doubl?e
570 : 0 : ++i;
571 : 0 : closed = true;
572 : 0 : break;
573 : 0 : }
574 : 0 : ++i;
575 : 0 : }
576 [ # # ]: 0 : if (!closed) {
577 : 0 : m_addError(vOutErrs, s, "string not close", "expected: '");
578 : 0 : emit.emit(TokenKind::String, s, n);
579 : 0 : } else {
580 : 0 : emit.emit(TokenKind::String, s, i);
581 : 0 : }
582 : 0 : continue;
583 : 0 : }
584 : :
585 : : // blob X'ABCD'
586 [ - + ][ + + ]: 87 : if (c == 'X' || c == 'x') {
587 [ + - ][ - + ]: 4 : if (i + 1u < n && vSql[i + 1u] == '\'') {
588 : 0 : const uint32_t s = i;
589 : 0 : i += 2u;
590 : 0 : bool closed = false, badHex = false;
591 : 0 : uint32_t hexCount = 0u;
592 [ # # ]: 0 : while (i < n) {
593 [ # # ]: 0 : if (vSql[i] == '\'') {
594 : 0 : ++i;
595 : 0 : closed = true;
596 : 0 : break;
597 : 0 : }
598 [ # # ]: 0 : if (!m_isHex(vSql[i])) {
599 : 0 : badHex = true;
600 : 0 : ++i;
601 : 0 : continue;
602 : 0 : }
603 : 0 : ++hexCount;
604 : 0 : ++i;
605 : 0 : }
606 : 0 : emit.emit(TokenKind::Blob, s, i);
607 [ # # ]: 0 : if (!closed) {
608 : 0 : m_addError(vOutErrs, s, "blob not closed", "expected: '");
609 [ # # ]: 0 : } else if ((hexCount % 2u) != 0u) {
610 : 0 : m_addError(vOutErrs, s, "blob hexa of odd length", "");
611 [ # # ]: 0 : } else if (badHex) {
612 : 0 : m_addError(vOutErrs, s, "char not-hexa in blob", "");
613 : 0 : }
614 : 0 : continue;
615 : 0 : }
616 : 4 : }
617 : :
618 : : // param?tres
619 [ - + ][ - + ]: 87 : if (c == '?' || c == ':' || c == '@' || c == '$') {
[ - + ][ - + ]
620 : 0 : const uint32_t s = i++;
621 [ # # ]: 0 : if (c == '?') {
622 [ # # ][ # # ]: 0 : while (i < n && m_isDigit(vSql[i]))
623 : 0 : ++i; // ?123
624 : 0 : } else {
625 [ # # ][ # # ]: 0 : while (i < n && (m_isAlnum(vSql[i]) || vSql[i] == '_'))
[ # # ]
626 : 0 : ++i; // :name @name $name
627 : 0 : }
628 : 0 : emit.emit(TokenKind::Parameter, s, i);
629 : 0 : continue;
630 : 0 : }
631 : :
632 : : // nombres
633 [ + + ][ - + ]: 87 : if (m_isDigit(c) || (c == '.' && i + 1u < n && m_isDigit(vSql[i + 1u]))) {
[ # # ][ # # ]
634 : 10 : const uint32_t s = i;
635 : 10 : bool hasDot = false;
636 [ - + ]: 10 : if (c == '.') {
637 : 0 : hasDot = true;
638 : 0 : ++i;
639 : 0 : }
640 [ + - ][ + + ]: 20 : while (i < n && m_isDigit(vSql[i]))
641 : 10 : ++i;
642 [ + - ][ - + ]: 10 : if (i < n && vSql[i] == '.' && !hasDot) {
[ # # ]
643 : 0 : hasDot = true;
644 : 0 : ++i;
645 [ # # ][ # # ]: 0 : while (i < n && m_isDigit(vSql[i]))
646 : 0 : ++i;
647 : 0 : }
648 [ + - ][ - + ]: 10 : if (i < n && (vSql[i] == 'e' || vSql[i] == 'E')) {
[ - + ]
649 : 0 : ++i;
650 [ # # ][ # # ]: 0 : if (i < n && (vSql[i] == '+' || vSql[i] == '-'))
[ # # ]
651 : 0 : ++i;
652 [ # # ][ # # ]: 0 : while (i < n && m_isDigit(vSql[i]))
653 : 0 : ++i;
654 : 0 : }
655 : 10 : emit.emit(TokenKind::Number, s, i);
656 : 10 : continue;
657 : 10 : }
658 : :
659 : : // identifiers quot?s "..."
660 [ - + ]: 77 : if (c == '"') {
661 : 0 : const uint32_t s = i++;
662 : 0 : bool closed = false;
663 [ # # ]: 0 : while (i < n) {
664 [ # # ]: 0 : if (vSql[i] == '"') {
665 [ # # ][ # # ]: 0 : if (i + 1u < n && vSql[i + 1u] == '"') {
666 : 0 : i += 2u;
667 : 0 : continue;
668 : 0 : }
669 : 0 : ++i;
670 : 0 : closed = true;
671 : 0 : break;
672 : 0 : }
673 : 0 : ++i;
674 : 0 : }
675 [ # # ]: 0 : if (!closed) {
676 : 0 : m_addError(vOutErrs, s, "identifier \"...\" not closed", "expected: \"");
677 : 0 : }
678 : 0 : emit.emit(TokenKind::Identifier, s, i);
679 : 0 : continue;
680 : 0 : }
681 : : // identifiers `...` or [ ... ]
682 [ - + ][ - + ]: 77 : if (c == '`' || c == '[') {
683 [ # # ]: 0 : const char closing = (c == '`') ? '`' : ']';
684 : 0 : const uint32_t s = i++;
685 : 0 : bool closed = false;
686 [ # # ]: 0 : while (i < n) {
687 [ # # ]: 0 : if (vSql[i] == closing) {
688 : 0 : ++i;
689 : 0 : closed = true;
690 : 0 : break;
691 : 0 : }
692 : 0 : ++i;
693 : 0 : }
694 [ # # ]: 0 : if (!closed) {
695 : 0 : std::string msg("identifier not closed (expected: ");
696 : 0 : msg.push_back(closing);
697 : 0 : msg.push_back(')');
698 : 0 : m_addError(vOutErrs, s, msg, "");
699 : 0 : }
700 : 0 : emit.emit(TokenKind::Identifier, s, i);
701 : 0 : continue;
702 : 0 : }
703 : :
704 : : // clang-format off
705 : : // identifiers / mots-cl?s non quot?s
706 [ + + ]: 77 : if (m_isAlpha(c)) {
707 : 51 : const uint32_t s = i++;
708 [ + - ][ + + ]: 213 : while (i<n && (m_isAlnum(vSql[i]) || vSql[i]=='$')) ++i;
[ - + ]
709 : :
710 [ + - ]: 51 : StringRef v(vSql.c_str() + s, (i >= s) ? (i - s) : 0u);
711 : 51 : TokenKind k = TokenKind::Identifier;
712 : :
713 [ + + ]: 51 : if (m_ieq(v,"SELECT")) k = TokenKind::KwSelect;
714 [ + + ]: 43 : else if (m_ieq(v,"FROM")) k = TokenKind::KwFrom;
715 [ + + ]: 36 : else if (m_ieq(v,"WHERE")) k = TokenKind::KwWhere;
716 [ - + ]: 35 : else if (m_ieq(v,"GROUP")) k = TokenKind::KwGroup;
717 [ - + ]: 35 : else if (m_ieq(v,"BY")) k = TokenKind::KwBy;
718 [ - + ]: 35 : else if (m_ieq(v,"HAVING")) k = TokenKind::KwHaving;
719 [ + + ]: 35 : else if (m_ieq(v,"ORDER")) k = TokenKind::KwOrder;
720 [ + + ]: 34 : else if (m_ieq(v,"LIMIT")) k = TokenKind::KwLimit;
721 [ - + ]: 33 : else if (m_ieq(v,"OFFSET")) k = TokenKind::KwOffset;
722 [ - + ]: 33 : else if (m_ieq(v,"WITH")) k = TokenKind::KwWith;
723 [ - + ]: 33 : else if (m_ieq(v,"AS")) k = TokenKind::KwAs;
724 [ + + ]: 33 : else if (m_ieq(v,"INSERT")) k = TokenKind::KwInsert;
725 [ + + ]: 31 : else if (m_ieq(v,"INTO")) k = TokenKind::KwInto;
726 [ + + ]: 30 : else if (m_ieq(v,"VALUES")) k = TokenKind::KwValues;
727 [ + + ]: 28 : else if (m_ieq(v,"UPDATE")) k = TokenKind::KwUpdate;
728 [ - + ]: 27 : else if (m_ieq(v,"SET")) k = TokenKind::KwSet;
729 [ + + ]: 27 : else if (m_ieq(v,"DELETE")) k = TokenKind::KwDelete;
730 [ + + ]: 26 : else if (m_ieq(v,"CREATE")) k = TokenKind::KwCreate;
731 [ + + ]: 24 : else if (m_ieq(v,"TABLE")) k = TokenKind::KwTable;
732 [ - + ]: 22 : else if (m_ieq(v,"IF")) k = TokenKind::KwIf;
733 [ - + ]: 22 : else if (m_ieq(v,"NOT")) k = TokenKind::KwNot;
734 [ - + ]: 22 : else if (m_ieq(v,"EXISTS")) k = TokenKind::KwExists;
735 [ + + ]: 22 : else if (m_ieq(v,"PRIMARY")) k = TokenKind::KwPrimary;
736 [ + + ]: 21 : else if (m_ieq(v,"KEY")) k = TokenKind::KwKey;
737 [ - + ]: 20 : else if (m_ieq(v,"UNIQUE")) k = TokenKind::KwUnique;
738 [ - + ]: 20 : else if (m_ieq(v,"CHECK")) k = TokenKind::KwCheck;
739 [ - + ]: 20 : else if (m_ieq(v,"REFERENCES")) k = TokenKind::KwReferences;
740 [ - + ]: 20 : else if (m_ieq(v,"WITHOUT")) k = TokenKind::KwWithout;
741 [ - + ]: 20 : else if (m_ieq(v,"ROWID")) k = TokenKind::KwRowid;
742 [ - + ]: 20 : else if (m_ieq(v,"ON")) k = TokenKind::KwOn;
743 [ - + ]: 20 : else if (m_ieq(v,"CONFLICT")) k = TokenKind::KwConflict;
744 : :
745 : 51 : emit.emit(k, s, i);
746 : 51 : continue;
747 : 51 : }
748 : :
749 : : // op?rateurs / ponctuation
750 [ + + ]: 26 : if (i+1u<n) {
751 : 14 : char c2 = vSql[i+1u];
752 [ - + ][ # # ]: 14 : if (c=='|' && c2=='|') { emit.emit(TokenKind::PipePipe, i, i+2u); i+=2u; continue; }
753 [ - + ][ # # ]: 14 : if (c=='<' && c2=='<') { emit.emit(TokenKind::Shl, i, i+2u); i+=2u; continue; }
754 [ - + ][ # # ]: 14 : if (c=='>' && c2=='>') { emit.emit(TokenKind::Shr, i, i+2u); i+=2u; continue; }
755 [ + + ][ - + ]: 14 : if (c=='=' && c2=='=') { emit.emit(TokenKind::EqEq, i, i+2u); i+=2u; continue; }
756 [ - + ][ # # ]: 14 : if (c=='!' && c2=='=') { emit.emit(TokenKind::Ne, i, i+2u); i+=2u; continue; }
757 [ - + ][ # # ]: 14 : if (c=='<' && c2=='>') { emit.emit(TokenKind::Ne2, i, i+2u); i+=2u; continue; }
758 [ - + ][ # # ]: 14 : if (c=='<' && c2=='=') { emit.emit(TokenKind::Le, i, i+2u); i+=2u; continue; }
759 [ - + ][ # # ]: 14 : if (c=='>' && c2=='=') { emit.emit(TokenKind::Ge, i, i+2u); i+=2u; continue; }
760 : 14 : }
761 : 26 : switch (c) {
762 [ - + ]: 0 : case '+': emit.emit(TokenKind::Plus, i, i+1u); ++i; continue;
763 [ - + ]: 0 : case '-': emit.emit(TokenKind::Minus, i, i+1u); ++i; continue;
764 [ - + ]: 0 : case '*': emit.emit(TokenKind::Star, i, i+1u); ++i; continue;
765 [ - + ]: 0 : case '/': emit.emit(TokenKind::Slash, i, i+1u); ++i; continue;
766 [ - + ]: 0 : case '%': emit.emit(TokenKind::Percent, i, i+1u); ++i; continue;
767 [ - + ]: 0 : case '&': emit.emit(TokenKind::Amp, i, i+1u); ++i; continue;
768 [ - + ]: 0 : case '|': emit.emit(TokenKind::Pipe, i, i+1u); ++i; continue;
769 [ - + ]: 0 : case '~': emit.emit(TokenKind::Tilde, i, i+1u); ++i; continue;
770 [ + + ]: 1 : case '=': emit.emit(TokenKind::Assign, i, i+1u); ++i; continue;
771 [ - + ]: 0 : case '<': emit.emit(TokenKind::Lt, i, i+1u); ++i; continue;
772 [ - + ]: 0 : case '>': emit.emit(TokenKind::Gt, i, i+1u); ++i; continue;
773 [ + + ]: 5 : case ',': emit.emit(TokenKind::Comma, i, i+1u); ++i; continue;
774 [ - + ]: 0 : case '.': emit.emit(TokenKind::Dot, i, i+1u); ++i; continue;
775 [ + + ]: 3 : case '(': emit.emit(TokenKind::LParen, i, i+1u); ++i; continue;
776 [ + + ]: 3 : case ')': emit.emit(TokenKind::RParen, i, i+1u); ++i; continue;
777 [ + + ]: 14 : case ';': emit.emit(TokenKind::Semicolon, i, i+1u); ++i; continue;
778 [ - + ]: 0 : default: break;
779 : 26 : }
780 : : // clang-format on
781 : :
782 : : // inconnu
783 : 0 : m_addError(vOutErrs, i, "char unknown", "");
784 : 0 : emit.emit(TokenKind::Unknown, i, i + 1u);
785 : 0 : ++i;
786 : 0 : }
787 : :
788 : : // EOF
789 : 14 : Token eof;
790 : 14 : eof.kind = TokenKind::EndOfFile;
791 : 14 : eof.start.offset = n;
792 : 14 : eof.end.offset = n;
793 : 14 : m_assignLineCol(n, eof.start.line, eof.start.column);
794 : 14 : eof.end = eof.start;
795 : 14 : eof.lex = StringRef(NULL, 0u);
796 : 14 : vOutToks.push_back(eof);
797 : 14 : }
798 : :
799 : 14 : void m_splitStatements(const std::vector<Token>& vToks, std::vector<StatementRange>& vOut) const {
800 : 14 : vOut.clear();
801 : 14 : uint32_t curStart = 0u;
802 : 14 : uint32_t lastNonSpace = 0u;
803 : 14 : bool hasContent = false;
804 : :
805 [ + - ]: 101 : for (size_t i = 0; i < vToks.size(); ++i) {
806 : 101 : const Token& t = vToks[i];
807 [ + + ]: 101 : if (t.kind == TokenKind::EndOfFile) {
808 [ - + ]: 14 : if (hasContent) {
809 : 0 : StatementRange r;
810 : 0 : r.beginOffset = curStart;
811 : 0 : r.endOffset = lastNonSpace + 1u;
812 [ # # ]: 0 : if (r.endOffset <= r.beginOffset)
813 : 0 : r.endOffset = r.beginOffset;
814 : 0 : vOut.push_back(r);
815 : 0 : }
816 : 14 : break;
817 : 14 : }
818 : : // pas d'espaces ici (d?j? consomm?s au lexing)
819 [ + + ]: 87 : if (!hasContent) {
820 : 14 : hasContent = true;
821 : 14 : curStart = t.start.offset;
822 : 14 : }
823 [ + - ]: 87 : lastNonSpace = (t.end.offset > 0u) ? (t.end.offset - 1u) : t.end.offset;
824 : :
825 [ + + ]: 87 : if (t.kind == TokenKind::Semicolon) {
826 [ + - ]: 14 : if (hasContent) {
827 : 14 : StatementRange r;
828 : 14 : r.beginOffset = curStart;
829 : 14 : r.endOffset = t.start.offset; // before le ';'
830 [ - + ]: 14 : if (r.endOffset < r.beginOffset)
831 : 0 : r.endOffset = r.beginOffset;
832 : 14 : vOut.push_back(r);
833 : 14 : }
834 : 14 : hasContent = false;
835 : 14 : }
836 : 87 : }
837 : 14 : }
838 : :
839 : 14 : StatementKind m_detectKind(const std::vector<Token>& vToks, const StatementRange& vRng) const {
840 [ + - ]: 27 : for (size_t i = 0; i < vToks.size(); ++i) {
841 : 27 : const Token& t = vToks[i];
842 [ + + ]: 27 : if (t.start.offset < vRng.beginOffset)
843 : 13 : continue;
844 [ - + ]: 14 : if (t.start.offset >= vRng.endOffset)
845 : 0 : break;
846 : 14 : switch (t.kind) {
847 [ + + ]: 8 : case TokenKind::KwSelect: return StatementKind::Select;
848 [ + + ]: 2 : case TokenKind::KwInsert: return StatementKind::Insert;
849 [ + + ]: 1 : case TokenKind::KwUpdate: return StatementKind::Update;
850 [ + + ]: 1 : case TokenKind::KwDelete: return StatementKind::Delete;
851 [ + + ]: 2 : case TokenKind::KwCreate: return StatementKind::CreateTable; // affin? in check
852 [ - + ]: 0 : default: return StatementKind::Other;
853 : 14 : }
854 : 14 : }
855 : 0 : return StatementKind::Other;
856 : 14 : }
857 : :
858 : : // --- v?rifs g?n?rales
859 : 14 : void m_checkParens(const std::vector<Token>& vToks, const StatementRange& vRng, Report& vOut) const {
860 : 14 : int32_t depth = 0;
861 [ + - ]: 100 : for (size_t i = 0; i < vToks.size(); ++i) {
862 : 100 : const Token& t = vToks[i];
863 [ + + ]: 100 : if (t.start.offset < vRng.beginOffset)
864 : 13 : continue;
865 [ + + ]: 87 : if (t.start.offset >= vRng.endOffset)
866 : 14 : break;
867 [ + + ]: 73 : if (t.kind == TokenKind::LParen) {
868 : 3 : ++depth;
869 [ + + ]: 70 : } else if (t.kind == TokenKind::RParen) {
870 : 3 : --depth;
871 [ + + ]: 3 : if (depth < 0) {
872 : 1 : m_addError(vOut.errors, t.start.offset, "closing parenthesis without opening parenthesis", "delete ')'");
873 : 1 : depth = 0;
874 : 1 : }
875 : 3 : }
876 : 73 : }
877 [ + + ]: 14 : if (depth > 0) {
878 : 1 : m_addError(vOut.errors, vRng.endOffset, "missing closing parenthesis", "expected: ')'");
879 : 1 : }
880 : 14 : }
881 : :
882 : : // --- v?rifs par kind
883 : 2 : void m_checkCreateTable(const std::vector<Token>& vToks, const StatementRange& vRng, const std::string& /*vSql*/, Report& vOut) const {
884 : 2 : bool sawCreate = false, sawTable = false;
885 : 2 : const Token* nameTok = NULL;
886 : 2 : const Token* afterName = NULL;
887 : :
888 [ + - ]: 8 : for (size_t i = 0; i < vToks.size(); ++i) {
889 : 8 : const Token& t = vToks[i];
890 [ - + ]: 8 : if (t.start.offset < vRng.beginOffset)
891 : 0 : continue;
892 [ - + ]: 8 : if (t.start.offset >= vRng.endOffset)
893 : 0 : break;
894 : :
895 [ + + ]: 8 : if (!sawCreate) {
896 [ + - ]: 2 : if (t.kind == TokenKind::KwCreate) {
897 : 2 : sawCreate = true;
898 : 2 : } else {
899 : 0 : return;
900 : 0 : }
901 : 2 : continue;
902 : 2 : }
903 [ + + ]: 6 : if (!sawTable) {
904 [ + - ]: 2 : if (t.kind == TokenKind::KwTable) {
905 : 2 : sawTable = true;
906 : 2 : }
907 : 2 : continue;
908 : 2 : }
909 [ + + ]: 4 : if (!nameTok) {
910 [ + - ]: 2 : if (t.kind == TokenKind::Identifier) {
911 : 2 : nameTok = &t;
912 : 2 : continue;
913 : 2 : }
914 [ # # ]: 0 : if (t.kind == TokenKind::KwIf)
915 : 0 : continue;
916 [ # # ]: 0 : if (t.kind == TokenKind::KwNot)
917 : 0 : continue;
918 [ # # ]: 0 : if (t.kind == TokenKind::KwExists)
919 : 0 : continue;
920 : 0 : m_addError(vOut.errors, t.start.offset, "table name expected after CREATE TABLE", "identifier");
921 : 0 : return;
922 : 2 : } else {
923 : 2 : afterName = &t;
924 : 2 : break;
925 : 2 : }
926 : 4 : }
927 : :
928 [ - + ]: 2 : if (!nameTok) {
929 : 0 : m_addError(vOut.errors, vRng.beginOffset, "table name missing", "identifier");
930 : 0 : return;
931 : 0 : }
932 [ - + ]: 2 : if (!afterName) {
933 : 0 : m_addError(vOut.errors, nameTok->end.offset, "expected '(' or AS after table name", "(' | AS");
934 : 0 : return;
935 : 0 : }
936 : :
937 : 2 : bool hasParen = false, hasAs = false;
938 [ + - ]: 9 : for (size_t i = 0; i < vToks.size(); ++i) {
939 : 9 : const Token& t = vToks[i];
940 [ + + ]: 9 : if (t.start.offset < afterName->start.offset)
941 : 6 : continue;
942 [ + + ]: 3 : if (t.start.offset >= vRng.endOffset)
943 : 1 : break;
944 [ + + ]: 2 : if (t.kind == TokenKind::LParen) {
945 : 1 : hasParen = true;
946 : 1 : break;
947 : 1 : }
948 [ - + ]: 1 : if (t.kind == TokenKind::KwAs) {
949 : 0 : hasAs = true;
950 : 0 : break;
951 : 0 : }
952 : 1 : }
953 [ + + ][ + - ]: 2 : if (!hasParen && !hasAs) {
954 : 1 : m_addError(vOut.errors, afterName->start.offset, "expected '(' or AS after table name", "(' | AS");
955 : 1 : }
956 : 2 : }
957 : :
958 : 2 : void m_checkInsert(const std::vector<Token>& vToks, const StatementRange& vRng, Report& vOut) const {
959 : 2 : bool sawInsert = false, sawInto = false, sawValues = false, sawSelect = false;
960 : 2 : const Token* afterValues = NULL;
961 : :
962 [ + - ]: 12 : for (size_t i = 0; i < vToks.size(); ++i) {
963 : 12 : const Token& t = vToks[i];
964 [ - + ]: 12 : if (t.start.offset < vRng.beginOffset)
965 : 0 : continue;
966 [ + + ]: 12 : if (t.start.offset >= vRng.endOffset)
967 : 1 : break;
968 : :
969 [ + + ]: 11 : if (!sawInsert) {
970 [ + - ]: 2 : if (t.kind == TokenKind::KwInsert) {
971 : 2 : sawInsert = true;
972 : 2 : } else {
973 : 0 : return;
974 : 0 : }
975 : 2 : continue;
976 : 2 : }
977 [ + + ]: 9 : if (!sawInto) {
978 [ + + ]: 6 : if (t.kind == TokenKind::KwInto) {
979 : 1 : sawInto = true;
980 : 1 : continue;
981 : 1 : }
982 : 5 : continue;
983 : 6 : }
984 [ + + ][ + - ]: 3 : if (!sawValues && !sawSelect) {
985 [ + + ]: 2 : if (t.kind == TokenKind::KwValues) {
986 : 1 : sawValues = true;
987 : 1 : continue;
988 : 1 : }
989 [ - + ]: 1 : if (t.kind == TokenKind::KwSelect) {
990 : 0 : sawSelect = true;
991 : 0 : continue;
992 : 0 : }
993 : 1 : continue;
994 : 1 : }
995 [ + - ][ + - ]: 1 : if (sawValues && !afterValues) {
996 : 1 : afterValues = &t;
997 : 1 : break;
998 : 1 : }
999 : 1 : }
1000 : :
1001 [ - + ]: 2 : if (!sawInsert)
1002 : 0 : return;
1003 [ + + ]: 2 : if (!sawInto) {
1004 : 1 : m_addError(vOut.errors, vRng.beginOffset, "keyword INTO missing in INSERT", "INTO");
1005 : 1 : return;
1006 : 1 : }
1007 [ - + ][ # # ]: 1 : if (!sawValues && !sawSelect) {
1008 : 0 : m_addError(vOut.errors, vRng.beginOffset, "INSERT incomplete", "VALUES | SELECT");
1009 : 0 : return;
1010 : 0 : }
1011 [ + - ][ + - ]: 1 : if (sawValues && afterValues) {
1012 : 1 : int32_t depth = 0;
1013 : 1 : bool hasPar = false;
1014 [ + - ]: 10 : for (size_t i = 0; i < vToks.size(); ++i) {
1015 : 10 : const Token& t = vToks[i];
1016 [ + + ]: 10 : if (t.start.offset < afterValues->start.offset)
1017 : 4 : continue;
1018 [ + + ]: 6 : if (t.start.offset >= vRng.endOffset)
1019 : 1 : break;
1020 [ - + ]: 5 : if (t.kind == TokenKind::LParen) {
1021 : 0 : ++depth;
1022 : 0 : hasPar = true;
1023 [ - + ]: 5 : } else if (t.kind == TokenKind::RParen) {
1024 : 0 : --depth;
1025 [ # # ]: 0 : if (depth < 0)
1026 : 0 : depth = 0;
1027 : 0 : }
1028 : 5 : }
1029 [ + - ]: 1 : if (!hasPar) {
1030 : 1 : m_addError(vOut.errors, afterValues->start.offset, "VALUES without parentheses list", "('...')");
1031 : 1 : }
1032 : 1 : }
1033 : 1 : }
1034 : :
1035 : 1 : void m_checkUpdate(const std::vector<Token>& vToks, const StatementRange& vRng, Report& vOut) const {
1036 : 1 : bool sawUpdate = false, sawSet = false;
1037 [ + - ]: 7 : for (size_t i = 0; i < vToks.size(); ++i) {
1038 : 7 : const Token& t = vToks[i];
1039 [ - + ]: 7 : if (t.start.offset < vRng.beginOffset)
1040 : 0 : continue;
1041 [ + + ]: 7 : if (t.start.offset >= vRng.endOffset)
1042 : 1 : break;
1043 [ + + ]: 6 : if (!sawUpdate) {
1044 [ + - ]: 1 : if (t.kind == TokenKind::KwUpdate) {
1045 : 1 : sawUpdate = true;
1046 : 1 : } else {
1047 : 0 : return;
1048 : 0 : }
1049 : 1 : continue;
1050 : 1 : }
1051 [ + - ]: 5 : if (!sawSet) {
1052 [ - + ]: 5 : if (t.kind == TokenKind::KwSet) {
1053 : 0 : sawSet = true;
1054 : 0 : break;
1055 : 0 : }
1056 : 5 : }
1057 : 5 : }
1058 [ - + ]: 1 : if (!sawUpdate)
1059 : 0 : return;
1060 [ + - ]: 1 : if (!sawSet) {
1061 : 1 : m_addError(vOut.errors, vRng.beginOffset, "UPDATE without SET", "SET");
1062 : 1 : }
1063 : 1 : }
1064 : :
1065 : 1 : void m_checkDelete(const std::vector<Token>& vToks, const StatementRange& vRng, Report& vOut) const {
1066 : 1 : bool sawDelete = false, sawFrom = false;
1067 [ + - ]: 3 : for (size_t i = 0; i < vToks.size(); ++i) {
1068 : 3 : const Token& t = vToks[i];
1069 [ - + ]: 3 : if (t.start.offset < vRng.beginOffset)
1070 : 0 : continue;
1071 [ + + ]: 3 : if (t.start.offset >= vRng.endOffset)
1072 : 1 : break;
1073 [ + + ]: 2 : if (!sawDelete) {
1074 [ + - ]: 1 : if (t.kind == TokenKind::KwDelete) {
1075 : 1 : sawDelete = true;
1076 : 1 : } else {
1077 : 0 : return;
1078 : 0 : }
1079 : 1 : continue;
1080 : 1 : }
1081 [ + - ]: 1 : if (!sawFrom) {
1082 [ - + ]: 1 : if (t.kind == TokenKind::KwFrom) {
1083 : 0 : sawFrom = true;
1084 : 0 : break;
1085 : 0 : }
1086 : 1 : }
1087 : 1 : }
1088 [ - + ]: 1 : if (!sawDelete)
1089 : 0 : return;
1090 [ + - ]: 1 : if (!sawFrom) {
1091 : 1 : m_addError(vOut.errors, vRng.beginOffset, "DELETE without FROM", "FROM");
1092 : 1 : }
1093 : 1 : }
1094 : :
1095 : 8 : void m_checkSelect(const std::vector<Token>& vToks, const StatementRange& vRng, Report& vOut) const {
1096 : : // 1) Trouver SELECT
1097 : 8 : size_t selIdx = static_cast<size_t>(-1);
1098 [ + - ]: 21 : for (size_t i = 0u; i < vToks.size(); ++i) {
1099 : 21 : const Token& t = vToks[i];
1100 [ + + ]: 21 : if (t.start.offset < vRng.beginOffset) {
1101 : 13 : continue;
1102 : 13 : }
1103 [ - + ]: 8 : if (t.start.offset >= vRng.endOffset) {
1104 : 0 : break;
1105 : 0 : }
1106 [ + - ]: 8 : if (t.kind == TokenKind::KwSelect) {
1107 : 8 : selIdx = i;
1108 : 8 : break;
1109 : 8 : }
1110 : 8 : }
1111 [ - + ]: 8 : if (selIdx == static_cast<size_t>(-1)) {
1112 : 0 : return; // pas un SELECT (s?curit?)
1113 : 0 : }
1114 : :
1115 : : // 2) Scanner la projection: SELECT <expr> [, <expr> ...] [FROM ... | ...]
1116 : 8 : bool seenAny = false;
1117 : 8 : bool expectingExpr = true; // vrai au d?but / after chaque virgule
1118 : 8 : size_t i = selIdx + 1u;
1119 : :
1120 [ + - ]: 19 : for (; i < vToks.size(); ++i) {
1121 : 19 : const Token& t = vToks[i];
1122 [ + + ]: 19 : if (t.start.offset >= vRng.endOffset) {
1123 : 1 : break;
1124 : 1 : }
1125 : :
1126 : 18 : const TokenKind k = t.kind;
1127 : :
1128 : : // Fin de projection : mots-cl?s majeurs or fin
1129 [ + + ][ - + ]: 18 : const bool endOfProjection = (k == TokenKind::KwFrom) || (k == TokenKind::KwWhere) || (k == TokenKind::KwGroup) || (k == TokenKind::KwOrder) ||
[ - + ][ - + ]
1130 [ - + ][ - + ]: 18 : (k == TokenKind::KwLimit) || (k == TokenKind::KwOffset) || (k == TokenKind::Semicolon) || (k == TokenKind::EndOfFile);
[ - + ][ - + ]
1131 : :
1132 [ + + ]: 18 : if (endOfProjection) {
1133 [ + + ]: 7 : if (!seenAny) {
1134 : : // Rien after SELECT
1135 : 1 : m_addError(vOut.errors, vToks[selIdx].start.offset, "projected SELECT missing", "*, identifier, expression");
1136 [ + + ]: 6 : } else if (expectingExpr) {
1137 : : // Virgule tra?nante: ex. "SELECT id, FROM ..."
1138 : 2 : m_addError(vOut.errors, t.start.offset, "expression of projection missing before thistoken", "expression after ','");
1139 : 2 : }
1140 : 7 : break;
1141 : 7 : }
1142 : :
1143 [ + + ]: 11 : if (k == TokenKind::Comma) {
1144 [ - + ]: 2 : if (expectingExpr) {
1145 : : // Cas ",," or ", FROM" (doublement signal? ici)
1146 : 0 : m_addError(vOut.errors, t.start.offset, "expression of projection missing after ','", "expression");
1147 : 0 : }
1148 : 2 : expectingExpr = true;
1149 : 2 : continue;
1150 : 2 : }
1151 : :
1152 : : // T?tes possibles d'expression (simplifi?es)
1153 [ - + ][ + + ]: 9 : const bool isExprHead = (k == TokenKind::Star) || (k == TokenKind::Identifier) || (k == TokenKind::Number) || (k == TokenKind::String) ||
[ + + ][ - + ]
1154 [ - + ][ + + ]: 9 : (k == TokenKind::Parameter) || (k == TokenKind::LParen);
1155 : :
1156 [ + + ]: 9 : if (isExprHead) {
1157 : 8 : seenAny = true;
1158 : 8 : expectingExpr = false;
1159 : 8 : continue;
1160 : 8 : }
1161 : :
1162 : : // Token inexpected en position d'expression
1163 [ - + ][ # # ]: 1 : if (expectingExpr && !seenAny) {
1164 : 0 : m_addError(vOut.errors, t.start.offset, "token inexpected in projection SELECT", "*, identifier, expression");
1165 : : // On continue pour tenter de rep?rer FROM/ORDER/LIMIT
1166 : 0 : expectingExpr = false; // ?vite cascade sur ce jeton
1167 : 0 : continue;
1168 : 0 : }
1169 : 1 : }
1170 : :
1171 : : // Si on a fini la boucle without rencontrer fin de projection
1172 [ - + ][ + + ]: 8 : if (i >= vToks.size() || vToks[i].start.offset >= vRng.endOffset) {
1173 [ - + ]: 1 : if (!seenAny) {
1174 : 0 : m_addError(vOut.errors, vToks[selIdx].start.offset, "projection SELECT missing", "*, identifier, expression");
1175 [ - + ]: 1 : } else if (expectingExpr) {
1176 : 0 : m_addError(vOut.errors, vRng.endOffset, "expression of projection missing at end of SELECT", "expression after ','");
1177 : 0 : }
1178 : 1 : }
1179 : :
1180 : : // 3) V?rifier FROM (facultatif en SQLite, donc on ne l'exige pas, on valide seulement sa forme)
1181 [ + - ]: 19 : for (size_t idx = selIdx + 1u; idx < vToks.size(); ++idx) {
1182 : 19 : const Token& t = vToks[idx];
1183 [ - + ]: 19 : if (t.start.offset < vRng.beginOffset) {
1184 : 0 : continue;
1185 : 0 : }
1186 [ + + ]: 19 : if (t.start.offset >= vRng.endOffset) {
1187 : 1 : break;
1188 : 1 : }
1189 : :
1190 [ + + ]: 18 : if (t.kind == TokenKind::KwFrom) {
1191 : 7 : size_t j = idx + 1u;
1192 [ - + ][ + + ]: 7 : if (j >= vToks.size() || vToks[j].start.offset >= vRng.endOffset) {
1193 : 1 : m_addError(vOut.errors, t.start.offset, "table expectede after FROM", "identifier or sub-query");
1194 : 6 : } else {
1195 : 6 : const TokenKind nk = vToks[j].kind;
1196 [ + - ][ # # ]: 6 : if (!(nk == TokenKind::Identifier || nk == TokenKind::LParen)) {
1197 : 0 : m_addError(vOut.errors, vToks[j].start.offset, "element invalid after FROM", "identifier or sub-query");
1198 : 0 : }
1199 : 6 : }
1200 : 7 : break; // un seul FROM principal ici (checker l?ger)
1201 : 7 : }
1202 : 18 : }
1203 : :
1204 : : // 4) ORDER BY et LIMIT/OFFSET (inchang?)
1205 [ + - ]: 34 : for (size_t idx = selIdx + 1u; idx < vToks.size(); ++idx) {
1206 : 34 : const Token& t = vToks[idx];
1207 [ - + ]: 34 : if (t.start.offset < vRng.beginOffset) {
1208 : 0 : continue;
1209 : 0 : }
1210 [ + + ]: 34 : if (t.start.offset >= vRng.endOffset) {
1211 : 8 : break;
1212 : 8 : }
1213 : :
1214 [ + + ]: 26 : if (t.kind == TokenKind::KwOrder) {
1215 : 1 : size_t j = idx + 1u;
1216 : 1 : bool hasBy = false;
1217 [ + - ][ - + ]: 1 : while (j < vToks.size() && vToks[j].start.offset < vRng.endOffset) {
1218 [ # # ]: 0 : if (vToks[j].kind == TokenKind::KwBy) {
1219 : 0 : hasBy = true;
1220 : 0 : ++j;
1221 : 0 : break;
1222 : 0 : }
1223 [ # # ]: 0 : if (vToks[j].kind != TokenKind::Comma) {
1224 : 0 : break;
1225 : 0 : }
1226 : 0 : ++j;
1227 : 0 : }
1228 [ + - ]: 1 : if (!hasBy) {
1229 : 1 : m_addError(vOut.errors, t.start.offset, "ORDER without BY", "BY");
1230 : 1 : } else {
1231 [ # # ][ # # ]: 0 : if (j >= vToks.size() || vToks[j].start.offset >= vRng.endOffset) {
1232 : 0 : m_addError(vOut.errors, t.start.offset, "ORDER BY incomplete", "");
1233 : 0 : }
1234 : 0 : }
1235 : 1 : }
1236 : :
1237 [ + + ][ - + ]: 26 : if (t.kind == TokenKind::KwLimit || t.kind == TokenKind::KwOffset) {
1238 : 1 : size_t j = idx + 1u;
1239 [ + - ][ - + ]: 1 : while (j < vToks.size() && vToks[j].start.offset < vRng.endOffset) {
1240 : 0 : const TokenKind k2 = vToks[j].kind;
1241 [ # # ][ # # ]: 0 : if (k2 == TokenKind::Number || k2 == TokenKind::Parameter) {
1242 : 0 : break;
1243 : 0 : }
1244 [ # # ]: 0 : if (k2 == TokenKind::Comma) {
1245 : 0 : ++j;
1246 : 0 : continue; // LIMIT x, y accept?
1247 : 0 : }
1248 : 0 : m_addError(vOut.errors, vToks[j].start.offset, "invalid value for LIMIT/OFFSET", "number of parameters");
1249 : 0 : break;
1250 : 0 : }
1251 [ - + ][ + - ]: 1 : if (j >= vToks.size() || vToks[j].start.offset >= vRng.endOffset) {
1252 : 1 : m_addError(vOut.errors, t.start.offset, "LIMIT/OFFSET without value", "number of parameters");
1253 : 1 : }
1254 : 1 : }
1255 : 26 : }
1256 : 8 : }
1257 : : };
1258 : :
1259 : : } // namespace sqlite
1260 : : } // namespace ez
|