 396b81f336
			
		
	
	396b81f336
	
	
	
		
			
			The internal header directory conflicted with in-source builds causing a build failure. This is fixed by renaming the internal header directory to something other than maxscale. The renaming pointed out a few problems in a couple of source files that appeared to include internal headers when the headers were in fact public headers. Fixed maxctrl in-source builds by making the copying of the sources optional.
		
			
				
	
	
		
			807 lines
		
	
	
		
			19 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			807 lines
		
	
	
		
			19 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| #pragma once
 | |
| /*
 | |
|  * Copyright (c) 2016 MariaDB Corporation Ab
 | |
|  *
 | |
|  * Use of this software is governed by the Business Source License included
 | |
|  * in the LICENSE.TXT file and at www.mariadb.com/bsl11.
 | |
|  *
 | |
|  * Change Date: 2020-01-01
 | |
|  *
 | |
|  * On the date above, in accordance with the Business Source License, use
 | |
|  * of this software will be governed by version 2 or later of the General
 | |
|  * Public License.
 | |
|  */
 | |
| 
 | |
| #include <maxscale/cppdefs.hh>
 | |
| #include <ctype.h>
 | |
| #include <maxscale/customparser.hh>
 | |
| #include <maxscale/query_classifier.h>
 | |
| 
 | |
| namespace maxscale
 | |
| {
 | |
| 
 | |
| #define TBP_EXPECT_TOKEN(string_literal) string_literal, (sizeof(string_literal) - 1)
 | |
| 
 | |
| // For debugging purposes.
 | |
| // #define TBP_LOG_UNEXPECTED_AND_EXHAUSTED
 | |
| #undef TBP_LOG_UNEXPECTED_AND_EXHAUSTED
 | |
| 
 | |
| /**
 | |
|  * @class TrxBoundaryParser
 | |
|  *
 | |
|  * @ TrxBoundaryParser is a class capable of parsing and returning the
 | |
|  * correct type mask of statements affecting the transaction state and
 | |
|  * autocommit mode.
 | |
|  *
 | |
|  * The class is intended to be used in context where the performance is
 | |
|  * of utmost importance; consequently it is defined in its entirety
 | |
|  * in the header to allow for aggressive inlining.
 | |
|  */
 | |
| class TrxBoundaryParser : public maxscale::CustomParser
 | |
| {
 | |
|     TrxBoundaryParser(const TrxBoundaryParser&);
 | |
|     TrxBoundaryParser& operator = (const TrxBoundaryParser&);
 | |
| 
 | |
| public:
 | |
|     enum token_t
 | |
|     {
 | |
|         TK_AUTOCOMMIT,
 | |
|         TK_BEGIN,
 | |
|         TK_COMMA,
 | |
|         TK_COMMIT,
 | |
|         TK_CONSISTENT,
 | |
|         TK_DOT,
 | |
|         TK_EQ,
 | |
|         TK_FALSE,
 | |
|         TK_GLOBAL,
 | |
|         TK_GLOBAL_VAR,
 | |
|         TK_ONE,
 | |
|         TK_ONLY,
 | |
|         TK_READ,
 | |
|         TK_ROLLBACK,
 | |
|         TK_SESSION,
 | |
|         TK_SESSION_VAR,
 | |
|         TK_SET,
 | |
|         TK_SNAPSHOT,
 | |
|         TK_START,
 | |
|         TK_TRANSACTION,
 | |
|         TK_TRUE,
 | |
|         TK_WITH,
 | |
|         TK_WORK,
 | |
|         TK_WRITE,
 | |
|         TK_ZERO,
 | |
| 
 | |
|         PARSER_UNKNOWN_TOKEN,
 | |
|         PARSER_EXHAUSTED,
 | |
|     };
 | |
| 
 | |
|     /**
 | |
|      * TrxBoundaryParser is not thread-safe. As a very lightweight class,
 | |
|      * the intention is that an instance is created on the stack whenever
 | |
|      * parsing needs to be performed.
 | |
|      *
 | |
|      * @code
 | |
|      *     void f(GWBUF *pBuf)
 | |
|      *     {
 | |
|      *         TrxBoundaryParser tbp;
 | |
|      *
 | |
|      *         uint32_t type_mask = tbp.parse(pBuf);
 | |
|      *         ...
 | |
|      *     }
 | |
|      * @endcode
 | |
|      */
 | |
|     TrxBoundaryParser()
 | |
|     {
 | |
|     }
 | |
| 
 | |
|     /**
 | |
|      * Return the type mask of a statement, provided the statement affects
 | |
|      * transaction state or autocommit mode.
 | |
|      *
 | |
|      * @param pSql  SQL statament.
 | |
|      * @param len   Length of pSql.
 | |
|      *
 | |
|      * @return The corresponding type mask or 0, if the statement does not
 | |
|      *         affect transaction state or autocommit mode.
 | |
|      */
 | |
|     uint32_t type_mask_of(const char* pSql, size_t len)
 | |
|     {
 | |
|         uint32_t type_mask = 0;
 | |
| 
 | |
|         m_pSql = pSql;
 | |
|         m_len = len;
 | |
| 
 | |
|         m_pI = m_pSql;
 | |
|         m_pEnd = m_pI + m_len;
 | |
| 
 | |
|         return parse();
 | |
|     }
 | |
| 
 | |
|     /**
 | |
|      * Return the type mask of a statement, provided the statement affects
 | |
|      * transaction state or autocommit mode.
 | |
|      *
 | |
|      * @param pBuf A COM_QUERY
 | |
|      *
 | |
|      * @return The corresponding type mask or 0, if the statement does not
 | |
|      *         affect transaction state or autocommit mode.
 | |
|      */
 | |
|     uint32_t type_mask_of(GWBUF* pBuf)
 | |
|     {
 | |
|         uint32_t type_mask = 0;
 | |
| 
 | |
|         char* pSql;
 | |
|         if (modutil_extract_SQL(pBuf, &pSql, &m_len))
 | |
|         {
 | |
|             m_pSql = pSql;
 | |
|             m_pI = m_pSql;
 | |
|             m_pEnd = m_pI + m_len;
 | |
| 
 | |
|             type_mask = parse();
 | |
|         }
 | |
| 
 | |
|         return type_mask;
 | |
|     }
 | |
| 
 | |
| private:
 | |
|     enum token_required_t
 | |
|     {
 | |
|         TOKEN_REQUIRED,
 | |
|         TOKEN_NOT_REQUIRED,
 | |
|     };
 | |
| 
 | |
|     void log_unexpected()
 | |
|     {
 | |
| #ifdef TBP_LOG_UNEXPECTED_AND_EXHAUSTED
 | |
|         MXS_NOTICE("Transaction tracking: In statement '%.*s', unexpected token at '%.*s'.",
 | |
|                    (int)m_len, m_pSql, (int)(m_pEnd - m_pI), m_pI);
 | |
| #endif
 | |
|     }
 | |
| 
 | |
|     void log_exhausted()
 | |
|     {
 | |
| #ifdef TBP_LOG_UNEXPECTED_AND_EXHAUSTED
 | |
|         MXS_NOTICE("Transaction tracking: More tokens expected in statement '%.*s'.", (int)m_len, m_pSql);
 | |
| #endif
 | |
|     }
 | |
| 
 | |
|     uint32_t parse()
 | |
|     {
 | |
|         uint32_t type_mask = 0;
 | |
| 
 | |
|         token_t token = next_token();
 | |
| 
 | |
|         switch (token)
 | |
|         {
 | |
|         case TK_BEGIN:
 | |
|             type_mask = parse_begin(type_mask);
 | |
|             break;
 | |
| 
 | |
|         case TK_COMMIT:
 | |
|             type_mask = parse_commit(type_mask);
 | |
|             break;
 | |
| 
 | |
|         case TK_ROLLBACK:
 | |
|             type_mask = parse_rollback(type_mask);
 | |
|             break;
 | |
| 
 | |
|         case TK_START:
 | |
|             type_mask = parse_start(type_mask);
 | |
|             break;
 | |
| 
 | |
|         case TK_SET:
 | |
|             type_mask = parse_set(0);
 | |
|             break;
 | |
| 
 | |
|         default:
 | |
|             ;
 | |
|         }
 | |
| 
 | |
|         return type_mask;
 | |
|     }
 | |
| 
 | |
|     uint32_t parse_begin(uint32_t type_mask)
 | |
|     {
 | |
|         type_mask |= QUERY_TYPE_BEGIN_TRX;
 | |
| 
 | |
|         token_t token = next_token();
 | |
| 
 | |
|         switch (token)
 | |
|         {
 | |
|         case TK_WORK:
 | |
|             type_mask = parse_work(type_mask);
 | |
|             break;
 | |
| 
 | |
|         case PARSER_EXHAUSTED:
 | |
|             break;
 | |
| 
 | |
|         default:
 | |
|             type_mask = 0;
 | |
|             log_unexpected();
 | |
|         }
 | |
| 
 | |
|         return type_mask;
 | |
|     }
 | |
| 
 | |
|     uint32_t parse_commit(uint32_t type_mask)
 | |
|     {
 | |
|         type_mask |= QUERY_TYPE_COMMIT;
 | |
| 
 | |
|         token_t token = next_token();
 | |
| 
 | |
|         switch (token)
 | |
|         {
 | |
|         case TK_WORK:
 | |
|             type_mask = parse_work(type_mask);
 | |
|             break;
 | |
| 
 | |
|         case PARSER_EXHAUSTED:
 | |
|             break;
 | |
| 
 | |
|         default:
 | |
|             type_mask = 0;
 | |
|             log_unexpected();
 | |
|         }
 | |
| 
 | |
|         return type_mask;
 | |
|     }
 | |
| 
 | |
|     uint32_t parse_only(uint32_t type_mask)
 | |
|     {
 | |
|         type_mask |= QUERY_TYPE_READ;
 | |
| 
 | |
|         token_t token = next_token();
 | |
| 
 | |
|         switch (token)
 | |
|         {
 | |
|         case TK_COMMA:
 | |
|             type_mask = parse_transaction(type_mask);
 | |
|             break;
 | |
| 
 | |
|         case PARSER_EXHAUSTED:
 | |
|             break;
 | |
| 
 | |
|         default:
 | |
|             type_mask = 0;
 | |
|             log_unexpected();
 | |
|         }
 | |
| 
 | |
|         return type_mask;
 | |
|     }
 | |
| 
 | |
|     uint32_t parse_read(uint32_t type_mask)
 | |
|     {
 | |
|         token_t token = next_token(TOKEN_REQUIRED);
 | |
| 
 | |
|         switch (token)
 | |
|         {
 | |
|         case TK_ONLY:
 | |
|             type_mask = parse_only(type_mask);
 | |
|             break;
 | |
| 
 | |
|         case TK_WRITE:
 | |
|             type_mask = parse_write(type_mask);
 | |
|             break;
 | |
| 
 | |
|         case PARSER_EXHAUSTED:
 | |
|             type_mask = 0;
 | |
|             break;
 | |
| 
 | |
|         default:
 | |
|             type_mask = 0;
 | |
|             log_unexpected();
 | |
|         }
 | |
| 
 | |
|         return type_mask;
 | |
|     }
 | |
| 
 | |
|     uint32_t parse_rollback(uint32_t type_mask)
 | |
|     {
 | |
|         type_mask |= QUERY_TYPE_ROLLBACK;
 | |
| 
 | |
|         token_t token = next_token();
 | |
| 
 | |
|         switch (token)
 | |
|         {
 | |
|         case TK_WORK:
 | |
|             type_mask = parse_work(type_mask);
 | |
|             break;
 | |
| 
 | |
|         case PARSER_EXHAUSTED:
 | |
|             break;
 | |
| 
 | |
|         default:
 | |
|             type_mask = 0;
 | |
|             log_unexpected();
 | |
|         }
 | |
| 
 | |
|         return type_mask;
 | |
|     }
 | |
| 
 | |
|     uint32_t parse_set_autocommit(uint32_t type_mask)
 | |
|     {
 | |
|         token_t token = next_token(TOKEN_REQUIRED);
 | |
| 
 | |
|         switch (token)
 | |
|         {
 | |
|         case TK_EQ:
 | |
|             token = next_token(TOKEN_REQUIRED);
 | |
|             if (token == TK_ONE || token == TK_TRUE)
 | |
|             {
 | |
|                 type_mask |= (QUERY_TYPE_COMMIT | QUERY_TYPE_ENABLE_AUTOCOMMIT);
 | |
|             }
 | |
|             else if (token == TK_ZERO || token == TK_FALSE)
 | |
|             {
 | |
|                 type_mask = (QUERY_TYPE_BEGIN_TRX | QUERY_TYPE_DISABLE_AUTOCOMMIT);
 | |
|             }
 | |
|             else
 | |
|             {
 | |
|                 type_mask = 0;
 | |
| 
 | |
|                 if (token != PARSER_EXHAUSTED)
 | |
|                 {
 | |
|                     log_unexpected();
 | |
|                 }
 | |
|             }
 | |
|             break;
 | |
| 
 | |
|         case PARSER_EXHAUSTED:
 | |
|             type_mask = 0;
 | |
|             break;
 | |
| 
 | |
|         default:
 | |
|             type_mask = 0;
 | |
|             log_unexpected();
 | |
|         }
 | |
| 
 | |
|         return type_mask;
 | |
|     }
 | |
| 
 | |
|     uint32_t parse_set(uint32_t type_mask)
 | |
|     {
 | |
|         token_t token = next_token(TOKEN_REQUIRED);
 | |
| 
 | |
|         switch (token)
 | |
|         {
 | |
|         case TK_AUTOCOMMIT:
 | |
|             type_mask = parse_set_autocommit(type_mask);
 | |
|             break;
 | |
| 
 | |
|         case TK_GLOBAL:
 | |
|         case TK_SESSION:
 | |
|             token = next_token(TOKEN_REQUIRED);
 | |
|             if (token == TK_AUTOCOMMIT)
 | |
|             {
 | |
|                 type_mask = parse_set_autocommit(type_mask);
 | |
|             }
 | |
|             else
 | |
|             {
 | |
|                 type_mask = 0;
 | |
|                 if (token != PARSER_EXHAUSTED)
 | |
|                 {
 | |
|                     log_unexpected();
 | |
|                 }
 | |
|             }
 | |
|             break;
 | |
| 
 | |
|         case TK_GLOBAL_VAR:
 | |
|         case TK_SESSION_VAR:
 | |
|             token = next_token(TOKEN_REQUIRED);
 | |
|             if (token == TK_DOT)
 | |
|             {
 | |
|                 token = next_token(TOKEN_REQUIRED);
 | |
|                 if (token == TK_AUTOCOMMIT)
 | |
|                 {
 | |
|                     type_mask = parse_set_autocommit(type_mask);
 | |
|                 }
 | |
|                 else
 | |
|                 {
 | |
|                     type_mask = 0;
 | |
|                     if (token != PARSER_EXHAUSTED)
 | |
|                     {
 | |
|                         log_unexpected();
 | |
|                     }
 | |
|                 }
 | |
|             }
 | |
|             else
 | |
|             {
 | |
|                 type_mask = 0;
 | |
|                 if (token != PARSER_EXHAUSTED)
 | |
|                 {
 | |
|                     log_unexpected();
 | |
|                 }
 | |
|             }
 | |
|             break;
 | |
| 
 | |
|         case PARSER_EXHAUSTED:
 | |
|             type_mask = 0;
 | |
|             break;
 | |
| 
 | |
|         default:
 | |
|             type_mask = 0;
 | |
|             log_unexpected();
 | |
|         }
 | |
| 
 | |
|         return type_mask;
 | |
|     }
 | |
| 
 | |
|     uint32_t parse_start(uint32_t type_mask)
 | |
|     {
 | |
|         token_t token = next_token(TOKEN_REQUIRED);
 | |
| 
 | |
|         switch (token)
 | |
|         {
 | |
|         case TK_TRANSACTION:
 | |
|             type_mask = parse_transaction(type_mask);
 | |
|             break;
 | |
| 
 | |
|         case PARSER_EXHAUSTED:
 | |
|             type_mask = 0;
 | |
|             break;
 | |
| 
 | |
|         default:
 | |
|             type_mask = 0;
 | |
|             log_unexpected();
 | |
|         }
 | |
| 
 | |
|         return type_mask;
 | |
|     }
 | |
| 
 | |
|     uint32_t parse_transaction(uint32_t type_mask)
 | |
|     {
 | |
|         type_mask |= QUERY_TYPE_BEGIN_TRX;
 | |
| 
 | |
|         token_t token = next_token();
 | |
| 
 | |
|         switch (token)
 | |
|         {
 | |
|         case TK_READ:
 | |
|             type_mask = parse_read(type_mask);
 | |
|             break;
 | |
| 
 | |
|         case TK_WITH:
 | |
|             type_mask = parse_with_consistent_snapshot(type_mask);
 | |
|             break;
 | |
| 
 | |
|         case PARSER_EXHAUSTED:
 | |
|             break;
 | |
| 
 | |
|         default:
 | |
|             type_mask = 0;
 | |
|             log_unexpected();
 | |
|         }
 | |
| 
 | |
|         return type_mask;
 | |
|     }
 | |
| 
 | |
|     uint32_t parse_with_consistent_snapshot(uint32_t type_mask)
 | |
|     {
 | |
|         token_t token = next_token(TOKEN_REQUIRED);
 | |
| 
 | |
|         if (token == TK_CONSISTENT)
 | |
|         {
 | |
|             token = next_token(TOKEN_REQUIRED);
 | |
| 
 | |
|             if (token == TK_SNAPSHOT)
 | |
|             {
 | |
|                 token = next_token();
 | |
| 
 | |
|                 switch (token)
 | |
|                 {
 | |
|                 case TK_COMMA:
 | |
|                     type_mask = parse_transaction(type_mask);
 | |
|                     break;
 | |
| 
 | |
|                 case PARSER_EXHAUSTED:
 | |
|                     break;
 | |
| 
 | |
|                 default:
 | |
|                     type_mask = 0;
 | |
|                     log_unexpected();
 | |
|                 }
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         return type_mask;
 | |
|     }
 | |
| 
 | |
|     uint32_t parse_work(uint32_t type_mask)
 | |
|     {
 | |
|         token_t token = next_token();
 | |
| 
 | |
|         switch (token)
 | |
|         {
 | |
|         case PARSER_EXHAUSTED:
 | |
|             break;
 | |
| 
 | |
|         default:
 | |
|             type_mask = 0;
 | |
|             log_unexpected();
 | |
|         }
 | |
| 
 | |
|         return type_mask;
 | |
|     }
 | |
| 
 | |
|     uint32_t parse_write(uint32_t type_mask)
 | |
|     {
 | |
|         type_mask |= QUERY_TYPE_WRITE;
 | |
| 
 | |
|         token_t token = next_token();
 | |
| 
 | |
|         switch (token)
 | |
|         {
 | |
|         case TK_COMMA:
 | |
|             type_mask = parse_transaction(type_mask);
 | |
|             break;
 | |
| 
 | |
|         case PARSER_EXHAUSTED:
 | |
|             break;
 | |
| 
 | |
|         default:
 | |
|             type_mask = 0;
 | |
|             log_unexpected();
 | |
|         }
 | |
| 
 | |
|         return type_mask;
 | |
|     }
 | |
| 
 | |
|     // Significantly faster than library version.
 | |
|     static char toupper(char c)
 | |
|     {
 | |
|         return (c >= 'a' && c <= 'z') ? c - ('a' - 'A') : c;
 | |
|     }
 | |
| 
 | |
|     token_t expect_token(const char* zWord, int len, token_t token)
 | |
|     {
 | |
|         const char* pI = m_pI;
 | |
|         const char* pEnd = zWord + len;
 | |
| 
 | |
|         while ((pI < m_pEnd) && (zWord < pEnd) && (toupper(*pI) == *zWord))
 | |
|         {
 | |
|             ++pI;
 | |
|             ++zWord;
 | |
|         }
 | |
| 
 | |
|         if (zWord == pEnd)
 | |
|         {
 | |
|             if ((pI == m_pEnd) || (!isalpha(*pI))) // Handwritten isalpha not faster than library version.
 | |
|             {
 | |
|                 m_pI = pI;
 | |
|             }
 | |
|             else
 | |
|             {
 | |
|                 token = PARSER_UNKNOWN_TOKEN;
 | |
|             }
 | |
|         }
 | |
|         else
 | |
|         {
 | |
|             token = PARSER_UNKNOWN_TOKEN;
 | |
|         }
 | |
| 
 | |
|         return token;
 | |
|     }
 | |
| 
 | |
|     void bypass_whitespace()
 | |
|     {
 | |
|         m_pI = modutil_MySQL_bypass_whitespace(const_cast<char*>(m_pI), m_pEnd - m_pI);
 | |
|     }
 | |
| 
 | |
|     token_t next_token(token_required_t required = TOKEN_NOT_REQUIRED)
 | |
|     {
 | |
|         token_t token = PARSER_UNKNOWN_TOKEN;
 | |
| 
 | |
|         bypass_whitespace();
 | |
| 
 | |
|         if (m_pI == m_pEnd)
 | |
|         {
 | |
|             token = PARSER_EXHAUSTED;
 | |
|         }
 | |
|         else if (*m_pI == ';')
 | |
|         {
 | |
|             ++m_pI;
 | |
| 
 | |
|             while ((m_pI != m_pEnd) && isspace(*m_pI))
 | |
|             {
 | |
|                 ++m_pI;
 | |
|             }
 | |
| 
 | |
|             if (m_pI != m_pEnd)
 | |
|             {
 | |
|                 MXS_WARNING("Non-space data found after semi-colon: '%.*s'.",
 | |
|                             (int)(m_pEnd - m_pI), m_pI);
 | |
|             }
 | |
| 
 | |
|             token = PARSER_EXHAUSTED;
 | |
|         }
 | |
|         else
 | |
|         {
 | |
|             switch (*m_pI)
 | |
|             {
 | |
|             case '@':
 | |
|                 if (is_next_alpha('A', 2))
 | |
|                 {
 | |
|                     token = expect_token(TBP_EXPECT_TOKEN("@@AUTOCOMMIT"), TK_AUTOCOMMIT);
 | |
|                 }
 | |
|                 else if (is_next_alpha('S', 2))
 | |
|                 {
 | |
|                     token = expect_token(TBP_EXPECT_TOKEN("@@SESSION"), TK_SESSION_VAR);
 | |
|                 }
 | |
|                 else if (is_next_alpha('G', 2))
 | |
|                 {
 | |
|                     token = expect_token(TBP_EXPECT_TOKEN("@@GLOBAL"), TK_GLOBAL_VAR);
 | |
|                 }
 | |
|                 break;
 | |
| 
 | |
|             case 'a':
 | |
|             case 'A':
 | |
|                 token = expect_token(TBP_EXPECT_TOKEN("AUTOCOMMIT"), TK_AUTOCOMMIT);
 | |
|                 break;
 | |
| 
 | |
|             case 'b':
 | |
|             case 'B':
 | |
|                 token = expect_token(TBP_EXPECT_TOKEN("BEGIN"), TK_BEGIN);
 | |
|                 break;
 | |
| 
 | |
|             case ',':
 | |
|                 ++m_pI;
 | |
|                 token = TK_COMMA;
 | |
|                 break;
 | |
| 
 | |
|             case 'c':
 | |
|             case 'C':
 | |
|                 if (is_next_alpha('O'))
 | |
|                 {
 | |
|                     if (is_next_alpha('M', 2))
 | |
|                     {
 | |
|                         token = expect_token(TBP_EXPECT_TOKEN("COMMIT"), TK_COMMIT);
 | |
|                     }
 | |
|                     else if (is_next_alpha('N', 2))
 | |
|                     {
 | |
|                         token = expect_token(TBP_EXPECT_TOKEN("CONSISTENT"), TK_CONSISTENT);
 | |
|                     }
 | |
|                 }
 | |
|                 break;
 | |
| 
 | |
|             case '.':
 | |
|                 ++m_pI;
 | |
|                 token = TK_DOT;
 | |
|                 break;
 | |
| 
 | |
|             case '=':
 | |
|                 ++m_pI;
 | |
|                 token = TK_EQ;
 | |
|                 break;
 | |
| 
 | |
|             case 'f':
 | |
|             case 'F':
 | |
|                 token = expect_token(TBP_EXPECT_TOKEN("FALSE"), TK_FALSE);
 | |
|                 break;
 | |
| 
 | |
|             case 'g':
 | |
|             case 'G':
 | |
|                 token = expect_token(TBP_EXPECT_TOKEN("GLOBAL"), TK_GLOBAL);
 | |
|                 break;
 | |
| 
 | |
|             case '1':
 | |
|                 {
 | |
|                     char c;
 | |
|                     if (!peek_next_char(&c) || !isdigit(c))
 | |
|                     {
 | |
|                         ++m_pI;
 | |
|                         token = TK_ONE;
 | |
|                     }
 | |
|                 }
 | |
|                 break;
 | |
| 
 | |
|             case 'o':
 | |
|             case 'O':
 | |
|                 if (is_next_alpha('F'))
 | |
|                 {
 | |
|                     token = expect_token(TBP_EXPECT_TOKEN("OFF"), TK_ZERO);
 | |
|                 }
 | |
|                 else if (is_next_alpha('N'))
 | |
|                 {
 | |
|                     if (is_next_alpha('L', 2))
 | |
|                     {
 | |
|                         token = expect_token(TBP_EXPECT_TOKEN("ONLY"), TK_ONLY);
 | |
|                     }
 | |
|                     else
 | |
|                     {
 | |
|                         token = expect_token(TBP_EXPECT_TOKEN("ON"), TK_ONE);
 | |
|                     }
 | |
|                 }
 | |
|                 break;
 | |
| 
 | |
|             case 'r':
 | |
|             case 'R':
 | |
|                 if (is_next_alpha('E'))
 | |
|                 {
 | |
|                     token = expect_token(TBP_EXPECT_TOKEN("READ"), TK_READ);
 | |
|                 }
 | |
|                 else if (is_next_alpha('O'))
 | |
|                 {
 | |
|                     token = expect_token(TBP_EXPECT_TOKEN("ROLLBACK"), TK_ROLLBACK);
 | |
|                 }
 | |
|                 break;
 | |
| 
 | |
|             case 's':
 | |
|             case 'S':
 | |
|                 if (is_next_alpha('E'))
 | |
|                 {
 | |
|                     if (is_next_alpha('S', 2))
 | |
|                     {
 | |
|                         token = expect_token(TBP_EXPECT_TOKEN("SESSION"), TK_SESSION);
 | |
|                     }
 | |
|                     else
 | |
|                     {
 | |
|                         token = expect_token(TBP_EXPECT_TOKEN("SET"), TK_SET);
 | |
|                     }
 | |
|                 }
 | |
|                 else if (is_next_alpha('N'))
 | |
|                 {
 | |
|                     token = expect_token(TBP_EXPECT_TOKEN("SNAPSHOT"), TK_SNAPSHOT);
 | |
|                 }
 | |
|                 else if (is_next_alpha('T'))
 | |
|                 {
 | |
|                     token = expect_token(TBP_EXPECT_TOKEN("START"), TK_START);
 | |
|                 }
 | |
|                 break;
 | |
| 
 | |
|             case 't':
 | |
|             case 'T':
 | |
|                 if (is_next_alpha('R'))
 | |
|                 {
 | |
|                     if (is_next_alpha('A', 2))
 | |
|                     {
 | |
|                         token = expect_token(TBP_EXPECT_TOKEN("TRANSACTION"), TK_TRANSACTION);
 | |
|                     }
 | |
|                     else if (is_next_alpha('U', 2))
 | |
|                     {
 | |
|                         token = expect_token(TBP_EXPECT_TOKEN("TRUE"), TK_TRUE);
 | |
|                     }
 | |
|                 }
 | |
|                 break;
 | |
| 
 | |
|             case 'w':
 | |
|             case 'W':
 | |
|                 if (is_next_alpha('I'))
 | |
|                 {
 | |
|                     token = expect_token(TBP_EXPECT_TOKEN("WITH"), TK_WITH);
 | |
|                 }
 | |
|                 else if (is_next_alpha('O'))
 | |
|                 {
 | |
|                     token = expect_token(TBP_EXPECT_TOKEN("WORK"), TK_WORK);
 | |
|                 }
 | |
|                 else if (is_next_alpha('R'))
 | |
|                 {
 | |
|                     token = expect_token(TBP_EXPECT_TOKEN("WRITE"), TK_WRITE);
 | |
|                 }
 | |
|                 break;
 | |
| 
 | |
|             case '0':
 | |
|                 {
 | |
|                     char c;
 | |
|                     if (!peek_next_char(&c) || !isdigit(c))
 | |
|                     {
 | |
|                         ++m_pI;
 | |
|                         token = TK_ZERO;
 | |
|                     }
 | |
|                 }
 | |
|                 break;
 | |
| 
 | |
|             default:
 | |
|                 ;
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         if ((token == PARSER_EXHAUSTED) && (required == TOKEN_REQUIRED))
 | |
|         {
 | |
|             log_exhausted();
 | |
|         }
 | |
| 
 | |
|         return token;
 | |
|     }
 | |
| };
 | |
| 
 | |
| }
 |