diff --git a/LiteCore/Query/N1QL_Parser/n1ql.cc b/LiteCore/Query/N1QL_Parser/n1ql.cc index bb3cb4d27..2527c61e1 100644 --- a/LiteCore/Query/N1QL_Parser/n1ql.cc +++ b/LiteCore/Query/N1QL_Parser/n1ql.cc @@ -706,7 +706,7 @@ YY_ACTION(void) yy_1_IDENTIFIER(yycontext *yy, char *yytext, int yyleng) yyprintf((stderr, "do yy_1_IDENTIFIER\n")); { #line 397 - y_ = string(yytext);; + y_ = warnOnServerReservedWord(yytext);; } #undef yythunkpos #undef yypos diff --git a/LiteCore/Query/N1QL_Parser/n1ql.leg b/LiteCore/Query/N1QL_Parser/n1ql.leg index 77e074715..b8903d3c8 100644 --- a/LiteCore/Query/N1QL_Parser/n1ql.leg +++ b/LiteCore/Query/N1QL_Parser/n1ql.leg @@ -394,7 +394,7 @@ parenExprs = # In N1QL, unlike SQL, `"` delimits a string, while "`" is used to quote identifiers. IDENTIFIER = !reservedWord <[a-zA-Z_] [a-zA-Z0-9_$]*> - _ { $$ = string(yytext);} + _ { $$ = warnOnServerReservedWord(yytext);} | "`" <( [^`] | "``" )*> "`" _ { $$ = unquote(yytext, '`');} # Note: the 'i' suffix on strings makes them case-insensitive. diff --git a/LiteCore/Query/N1QL_Parser/n1ql_parser_internal.hh b/LiteCore/Query/N1QL_Parser/n1ql_parser_internal.hh index 8d4361638..ed4583086 100644 --- a/LiteCore/Query/N1QL_Parser/n1ql_parser_internal.hh +++ b/LiteCore/Query/N1QL_Parser/n1ql_parser_internal.hh @@ -16,8 +16,10 @@ #pragma once #include "fleece/Mutable.hh" #include "Any.hh" +#include "Logging.hh" #include #include +#include #include #include #include @@ -204,6 +206,13 @@ namespace litecore::n1ql { return str; } + static bool isServerReservedWord(std::string word); + + static string warnOnServerReservedWord(const char* input) { + if ( isServerReservedWord(input) ) { Warn(R"("%s" is a reserved word in the Server SQL++)", input); } + return input; + } + // Property-path operations: static string quoteIdentity(string id) { @@ -349,6 +358,66 @@ namespace litecore::n1ql { return collate; } + // constexpr const char* UnabridgedServerReservedWords = + // "ADVISE ALL ALTER ANALYZE ARRAY AT BEGIN BINARY BOOLEAN BREAK BUCKET BUILD CACHE CALL CAST CLUSTER " + // "COLLECTION COMMIT COMMITTED CONNECT CONTINUE CORRELATED COVER CREATE CURRENT" + // " CYCLE DATABASE DATASET DATASTORE DECLARE DECREMENT DEFAULT DELETE DERIVED DESCRIBE DO DROP EACH ELEMENT " + // "ESCAPE EXCEPT EXCLUDE EXECUTE EXISTS EXPLAIN FETCH FILTER FIRST FLATTEN FLATTEN_KEYS" + // " FLUSH FOLLOWING FOR FORCE FTS FUNCTION GOLANG GRANT GROUPS GSI HASH IF IGNORE ILIKE INCLUDE INCREMENT " + // "INDEX INFER INLINE INSERT INTERSECT INTO ISOLATION JAVASCRIPT KEY" + // " KEYS KEYSPACE KNOWN LANGUAGE LAST LATERAL LET LETTING LEVEL LSM MAP MAPPING MATCHED MATERIALIZED " + // "MAXVALUE MERGE MINUS MINVALUE NAMESPACE NAMESPACE_ID NEST NEXT NEXTVAL NL NO" + // " NOT_A_TOKEN NTH_VALUE NULLS NUMBER OBJECT OPTION OPTIONS OTHERS OVER PARSE PARTITION PASSWORD PATH POOL " + // "PRECEDING PREPARE PREV PREVIOUS PREVVAL PRIMARY PRIVATE PRIVILEGE PROBE PROCEDURE PUBLIC" + // " RANGE RAW READ REALM RECURSIVE REDUCE RENAME REPLACE RESPECT RESTART RESTRICT RETURN RETURNING REVOKE " + // "ROLE ROLES ROLLBACK ROW ROWS SAVEPOINT SCHEMA SCOPE SELF SEMI SEQUENCE" + // " SET SHOW SOME START STATISTICS STRING SYSTEM TIES TO TRAN TRANSACTION TRIGGER TRUNCATE UNBOUNDED UNDER " + // "UNION UNIQUE UNKNOWN UNSET UPDATE UPSERT USE USER USERS VALIDATE" + // " VALUE VALUES VECTOR VIA VIEW WHILE WINDOW WITH WITHIN WORK XOR"; + + constexpr const char* kServerReservedWords = + " ALL ARRAY AT BEGIN CAST CORRELATED COVER CURRENT" + " DECREMENT DEFAULT DERIVED DESCRIBE DO EACH ELEMENT ESCAPE" + " EXCEPT EXCLUDE EXECUTE EXISTS EXPLAIN FETCH FILTER FIRST" + " FLATTEN FLATTEN_KEYS FOLLOWING FOR FORCE FUNCTION GRANT GROUPS" + " HASH IF IGNORE ILIKE INCLUDE INCREMENT INDEX INFER" + " INLINE INTERSECT ISOLATION KEY KEYS KEYSPACE KNOWN LAST" + " LATERAL LET LETTING LEVEL LSM MAP MAPPING MATCHED" + " MATERIALIZED MAXVALUE MERGE MINUS MINVALUE NAMESPACE NAMESPACE_ID NEST" + " EXT NEXTVAL NL NO NOT_A_TOKEN NTH_VALUE NULLS NUMBER" + " OBJECT OPTION OPTIONS OTHERS OVER PARSE PARTITION PASSWORD" + " PATH POOL PRECEDING PREPARE PREV PREVIOUS PREVVAL PRIMARY" + " PRIVATE PRIVILEGE PROBE PROCEDURE PUBLIC RANGE RAW READ" + " REALM RECURSIVE REDUCE RENAME REPLACE RESPECT RESTART RESTRICT" + " RETURN RETURNING REVOKE ROLE ROLES ROLLBACK ROW ROWS" + " SAVEPOINT SCHEMA SCOPE SELF SEMI SEQUENCE SHOW SOME" + " START STATISTICS STRING SYSTEM TIES TO TRAN TRIGGER" + " TRUNCATE UNBOUNDED UNDER UNION UNIQUE UNKNOWN UNSET UPDATE" + " UPSERT USE USER USERS VALIDATE VALUE VALUES VECTOR" + " VIA VIEW WHILE WINDOW WITH WITHIN WORK XOR"; + + bool isServerReservedWord(std::string word) { + static std::unordered_set serverReserved; + if ( serverReserved.empty() ) { + const char* p = kServerReservedWords; + const char* start = nullptr; + for ( ; *p != '\0'; ++p ) { + if ( *p == ' ' ) { + if ( start != nullptr ) { + //start -> p + serverReserved.emplace(start, p); + start = nullptr; + } + } else if ( start == nullptr ) { + start = p; + } + } + if ( start != nullptr ) { serverReserved.emplace(start, p); } + } + uppercase(word); + return serverReserved.contains(word); + } + } // namespace litecore::n1ql // The code generator produces some unreachable code; keep Clang from warning about it: