2001-05-23 15:26:42 +02:00
|
|
|
/*
|
|
|
|
* PROGRAM: Dynamic SQL runtime support
|
2003-10-05 08:37:26 +02:00
|
|
|
* MODULE: preparse.cpp
|
2008-12-05 02:20:14 +01:00
|
|
|
* DESCRIPTION: Dynamic SQL pre parser / parser on client side.
|
2001-05-23 15:26:42 +02:00
|
|
|
* This module will probably change to a YACC parser.
|
|
|
|
*
|
|
|
|
* The contents of this file are subject to the Interbase Public
|
|
|
|
* License Version 1.0 (the "License"); you may not use this file
|
|
|
|
* except in compliance with the License. You may obtain a copy
|
|
|
|
* of the License at http://www.Inprise.com/IPL.html
|
|
|
|
*
|
|
|
|
* Software distributed under the License is distributed on an
|
|
|
|
* "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either express
|
|
|
|
* or implied. See the License for the specific language governing
|
|
|
|
* rights and limitations under the License.
|
|
|
|
*
|
|
|
|
* The Original Code was created by Inprise Corporation
|
|
|
|
* and its predecessors. Portions created by Inprise Corporation are
|
|
|
|
* Copyright (C) Inprise Corporation.
|
|
|
|
*
|
|
|
|
* All Rights Reserved.
|
|
|
|
* Contributor(s): ______________________________________.
|
|
|
|
*/
|
|
|
|
|
2001-07-30 01:43:24 +02:00
|
|
|
#include "firebird.h"
|
2001-05-23 15:26:42 +02:00
|
|
|
#include <stdlib.h>
|
|
|
|
#include <string.h>
|
|
|
|
#include "../jrd/common.h"
|
2003-11-08 00:27:24 +01:00
|
|
|
#include "../jrd/ibase.h"
|
2001-05-23 15:26:42 +02:00
|
|
|
#include "../dsql/chars.h"
|
2003-10-01 12:58:07 +02:00
|
|
|
#include "../dsql/sqlda.h"
|
2001-05-23 15:26:42 +02:00
|
|
|
#include "../dsql/prepa_proto.h"
|
|
|
|
#include "../dsql/utld_proto.h"
|
|
|
|
#include "../jrd/gds_proto.h"
|
2004-12-05 12:57:25 +01:00
|
|
|
#include "../common/classes/ClumpletWriter.h"
|
2008-09-09 13:27:10 +02:00
|
|
|
#include "../common/StatusArg.h"
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2003-09-28 02:36:28 +02:00
|
|
|
enum pp_vals {
|
|
|
|
PP_CREATE = 0,
|
|
|
|
PP_DATABASE = 1,
|
|
|
|
PP_SCHEMA = 2,
|
|
|
|
PP_PAGE_SIZE = 3,
|
|
|
|
PP_USER = 4,
|
|
|
|
PP_PASSWORD = 5,
|
|
|
|
PP_PAGESIZE = 6,
|
|
|
|
PP_LENGTH = 7,
|
|
|
|
PP_PAGES = 8,
|
|
|
|
PP_PAGE = 9,
|
|
|
|
PP_SET = 10,
|
|
|
|
PP_NAMES = 11
|
|
|
|
};
|
2001-05-23 15:26:42 +02:00
|
|
|
|
|
|
|
|
2007-04-11 11:28:50 +02:00
|
|
|
const size_t MAX_TOKEN_SIZE = 1024;
|
2004-12-05 12:57:25 +01:00
|
|
|
static void generate_error(ISC_STATUS*, const Firebird::string&, SSHORT, SSHORT);
|
|
|
|
static SSHORT get_next_token(const SCHAR**, const SCHAR*, Firebird::string&);
|
2009-01-08 10:26:06 +01:00
|
|
|
static SSHORT get_token(ISC_STATUS*, SSHORT, bool, const SCHAR**, const SCHAR* const,
|
|
|
|
Firebird::string&);
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2009-01-08 10:26:06 +01:00
|
|
|
struct pp_table
|
|
|
|
{
|
2001-05-23 15:26:42 +02:00
|
|
|
SCHAR symbol[10];
|
2007-04-11 11:28:50 +02:00
|
|
|
USHORT length;
|
2001-05-23 15:26:42 +02:00
|
|
|
SSHORT code;
|
2003-10-01 12:58:07 +02:00
|
|
|
};
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2009-11-16 16:54:17 +01:00
|
|
|
// This should be kept in sync with the rule db_initial_desc of parse.y for CREATE DATABASE.
|
2009-11-14 10:25:10 +01:00
|
|
|
// Should delete SCHEMA in the future.
|
2009-01-08 10:26:06 +01:00
|
|
|
static const pp_table pp_symbols[] =
|
|
|
|
{
|
2002-10-16 10:40:01 +02:00
|
|
|
{"CREATE", 6, PP_CREATE},
|
|
|
|
{"DATABASE", 8, PP_DATABASE},
|
|
|
|
{"SCHEMA", 6, PP_SCHEMA},
|
|
|
|
{"PAGE_SIZE", 9, PP_PAGE_SIZE},
|
|
|
|
{"USER", 4, PP_USER},
|
|
|
|
{"PASSWORD", 8, PP_PASSWORD},
|
|
|
|
{"PAGESIZE", 8, PP_PAGESIZE},
|
|
|
|
{"LENGTH", 6, PP_LENGTH},
|
|
|
|
{"PAGES", 5, PP_PAGES},
|
|
|
|
{"PAGE", 4, PP_PAGE},
|
|
|
|
{"SET", 3, PP_SET},
|
|
|
|
{"NAMES", 5, PP_NAMES},
|
2002-01-04 12:34:22 +01:00
|
|
|
{"", 0, 0}
|
2001-05-23 15:26:42 +02:00
|
|
|
};
|
|
|
|
|
2008-12-05 02:20:14 +01:00
|
|
|
// define the tokens
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2003-09-28 02:36:28 +02:00
|
|
|
enum token_vals {
|
|
|
|
NO_MORE_TOKENS = -1,
|
|
|
|
TOKEN_TOO_LONG = -2,
|
|
|
|
UNEXPECTED_END_OF_COMMAND = -3,
|
|
|
|
UNEXPECTED_TOKEN = -4,
|
|
|
|
STRING = 257,
|
|
|
|
NUMERIC = 258,
|
|
|
|
SYMBOL = 259
|
|
|
|
};
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2008-09-09 13:27:10 +02:00
|
|
|
using namespace Firebird;
|
|
|
|
|
|
|
|
|
2003-02-15 04:01:51 +01:00
|
|
|
/**
|
2008-12-05 02:20:14 +01:00
|
|
|
|
2003-02-15 04:01:51 +01:00
|
|
|
PREPARSE_execute
|
2008-12-05 02:20:14 +01:00
|
|
|
|
2003-02-15 04:01:51 +01:00
|
|
|
@brief
|
|
|
|
|
|
|
|
@param user_status
|
|
|
|
@param db_handle
|
|
|
|
@param trans_handle
|
|
|
|
@param stmt_length
|
|
|
|
@param stmt
|
|
|
|
@param stmt_eaten
|
|
|
|
@param dialect
|
|
|
|
|
|
|
|
**/
|
2009-04-26 12:24:44 +02:00
|
|
|
bool PREPARSE_execute(ISC_STATUS* user_status, FB_API_HANDLE* db_handle,
|
|
|
|
FB_API_HANDLE*, // trans_handle,
|
2009-01-08 10:26:06 +01:00
|
|
|
USHORT stmt_length, const SCHAR* stmt, bool* stmt_eaten, USHORT dialect)
|
2001-05-23 15:26:42 +02:00
|
|
|
{
|
2004-12-05 12:57:25 +01:00
|
|
|
// no use creating separate pool for a couple of strings
|
2008-09-09 13:27:10 +02:00
|
|
|
ContextPoolHolder context(getDefaultMemoryPool());
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2004-12-05 12:57:25 +01:00
|
|
|
try
|
2003-09-04 23:26:15 +02:00
|
|
|
{
|
2008-09-09 13:27:10 +02:00
|
|
|
if (!stmt)
|
|
|
|
{
|
|
|
|
Arg::Gds(isc_command_end_err).raise();
|
|
|
|
}
|
|
|
|
|
2004-12-05 12:57:25 +01:00
|
|
|
if (!stmt_length)
|
|
|
|
stmt_length = strlen(stmt);
|
|
|
|
const char* const stmt_end = stmt + stmt_length;
|
2008-09-09 13:27:10 +02:00
|
|
|
string token;
|
2004-12-05 12:57:25 +01:00
|
|
|
|
|
|
|
if (get_token(user_status, SYMBOL, false, &stmt, stmt_end, token) ||
|
2009-01-08 10:26:06 +01:00
|
|
|
token.length() != pp_symbols[PP_CREATE].length || token != pp_symbols[PP_CREATE].symbol)
|
2004-12-05 12:57:25 +01:00
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2004-12-05 12:57:25 +01:00
|
|
|
if (get_token(user_status, SYMBOL, false, &stmt, stmt_end, token) ||
|
|
|
|
(token.length() != pp_symbols[PP_DATABASE].length &&
|
2009-01-08 10:26:06 +01:00
|
|
|
token.length() != pp_symbols[PP_SCHEMA].length) ||
|
|
|
|
(token != pp_symbols[PP_DATABASE].symbol && token != pp_symbols[PP_SCHEMA].symbol))
|
2004-12-05 12:57:25 +01:00
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2004-12-05 12:57:25 +01:00
|
|
|
if (get_token(user_status, STRING, false, &stmt, stmt_end, token))
|
|
|
|
{
|
|
|
|
return true;
|
|
|
|
}
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2008-09-09 13:27:10 +02:00
|
|
|
PathName file_name(token.ToPathName());
|
2004-12-05 12:57:25 +01:00
|
|
|
*stmt_eaten = false;
|
2008-09-09 13:27:10 +02:00
|
|
|
ClumpletWriter dpb(ClumpletReader::Tagged, MAX_DPB_SIZE, isc_dpb_version1);
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2004-12-09 20:19:47 +01:00
|
|
|
dpb.insertByte(isc_dpb_overwrite, 0);
|
2004-12-05 12:57:25 +01:00
|
|
|
dpb.insertInt(isc_dpb_sql_dialect, dialect);
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2004-12-05 12:57:25 +01:00
|
|
|
SLONG page_size = 0;
|
|
|
|
bool matched;
|
|
|
|
do {
|
|
|
|
const SSHORT result = get_next_token(&stmt, stmt_end, token);
|
2009-11-21 10:24:20 +01:00
|
|
|
if (result == NO_MORE_TOKENS)
|
|
|
|
{
|
2004-12-05 12:57:25 +01:00
|
|
|
*stmt_eaten = true;
|
|
|
|
break;
|
|
|
|
}
|
2009-01-09 02:50:54 +01:00
|
|
|
|
2009-01-08 10:26:06 +01:00
|
|
|
if (result < 0)
|
2004-12-05 12:57:25 +01:00
|
|
|
break;
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2004-12-05 12:57:25 +01:00
|
|
|
matched = false;
|
2009-01-08 10:26:06 +01:00
|
|
|
for (int i = 3; pp_symbols[i].length && !matched; i++)
|
|
|
|
{
|
|
|
|
if (token.length() == pp_symbols[i].length && token == pp_symbols[i].symbol)
|
2004-12-05 12:57:25 +01:00
|
|
|
{
|
|
|
|
bool get_out = false;
|
|
|
|
// CVC: What's strange, this routine doesn't check token.length()
|
|
|
|
// but it proceeds blindly, trying to exhaust the token itself.
|
|
|
|
|
2009-01-08 10:26:06 +01:00
|
|
|
switch (pp_symbols[i].code)
|
|
|
|
{
|
2004-12-05 12:57:25 +01:00
|
|
|
case PP_PAGE_SIZE:
|
|
|
|
case PP_PAGESIZE:
|
|
|
|
if (get_token(user_status, '=', true, &stmt, stmt_end, token) ||
|
|
|
|
get_token(user_status, NUMERIC, false, &stmt, stmt_end, token))
|
|
|
|
{
|
|
|
|
get_out = true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
page_size = atol(token.c_str());
|
|
|
|
dpb.insertInt(isc_dpb_page_size, page_size);
|
|
|
|
matched = true;
|
2001-05-23 15:26:42 +02:00
|
|
|
break;
|
|
|
|
|
2004-12-05 12:57:25 +01:00
|
|
|
case PP_USER:
|
|
|
|
if (get_token(user_status, STRING, false, &stmt, stmt_end, token))
|
|
|
|
{
|
|
|
|
get_out = true;
|
|
|
|
break;
|
|
|
|
}
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2004-12-05 12:57:25 +01:00
|
|
|
dpb.insertString(isc_dpb_user_name, token);
|
|
|
|
matched = true;
|
2001-05-23 15:26:42 +02:00
|
|
|
break;
|
|
|
|
|
2004-12-05 12:57:25 +01:00
|
|
|
case PP_PASSWORD:
|
|
|
|
if (get_token(user_status, STRING, false, &stmt, stmt_end, token))
|
|
|
|
{
|
|
|
|
get_out = true;
|
|
|
|
break;
|
|
|
|
}
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2004-12-05 12:57:25 +01:00
|
|
|
dpb.insertString(isc_dpb_password, token);
|
|
|
|
matched = true;
|
2001-05-23 15:26:42 +02:00
|
|
|
break;
|
|
|
|
|
2004-12-05 12:57:25 +01:00
|
|
|
case PP_SET:
|
|
|
|
if (get_token(user_status, SYMBOL, false, &stmt, stmt_end, token) ||
|
|
|
|
token.length() != pp_symbols[PP_NAMES].length ||
|
|
|
|
token != pp_symbols[PP_NAMES].symbol ||
|
|
|
|
get_token(user_status, STRING, false, &stmt, stmt_end, token))
|
|
|
|
{
|
|
|
|
get_out = true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
dpb.insertString(isc_dpb_lc_ctype, token);
|
|
|
|
matched = true;
|
|
|
|
break;
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2004-12-05 12:57:25 +01:00
|
|
|
case PP_LENGTH:
|
2008-12-05 02:20:14 +01:00
|
|
|
// Skip a token for value
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2004-12-05 12:57:25 +01:00
|
|
|
if (get_token(user_status, '=', true, &stmt, stmt_end, token) ||
|
|
|
|
get_token(user_status, NUMERIC, false, &stmt, stmt_end, token))
|
|
|
|
{
|
|
|
|
get_out = true;
|
|
|
|
break;
|
|
|
|
}
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2004-12-05 12:57:25 +01:00
|
|
|
matched = true;
|
|
|
|
break;
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2004-12-05 12:57:25 +01:00
|
|
|
case PP_PAGE:
|
|
|
|
case PP_PAGES:
|
|
|
|
matched = true;
|
|
|
|
break;
|
|
|
|
}
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2004-12-05 12:57:25 +01:00
|
|
|
if (get_out) {
|
|
|
|
return true;
|
|
|
|
}
|
2009-01-08 10:26:06 +01:00
|
|
|
} // if
|
|
|
|
} // for
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2004-12-05 12:57:25 +01:00
|
|
|
} while (matched);
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2009-04-19 12:06:32 +02:00
|
|
|
// This code is because 3.3 server does not recognize isc_dpb_overwrite.
|
2004-12-05 12:57:25 +01:00
|
|
|
FB_API_HANDLE temp_db_handle = 0;
|
2009-01-07 10:30:57 +01:00
|
|
|
if (!isc_attach_database(user_status, 0, file_name.c_str(), &temp_db_handle,
|
|
|
|
dpb.getBufferLength(), reinterpret_cast<const ISC_SCHAR*>(dpb.getBuffer())) ||
|
2009-03-05 16:18:38 +01:00
|
|
|
(user_status[1] != isc_io_error && user_status[1] != isc_conf_access_denied))
|
2009-01-07 10:30:57 +01:00
|
|
|
{
|
2009-04-04 18:39:31 +02:00
|
|
|
if (!user_status[1])
|
2009-03-05 16:18:38 +01:00
|
|
|
{
|
2004-12-05 12:57:25 +01:00
|
|
|
// Swallow status from detach.
|
|
|
|
ISC_STATUS_ARRAY temp_status;
|
|
|
|
isc_detach_database(temp_status, &temp_db_handle);
|
|
|
|
}
|
2009-01-08 10:26:06 +01:00
|
|
|
if (!user_status[1] || user_status[1] == isc_bad_db_format)
|
|
|
|
{
|
2004-12-05 12:57:25 +01:00
|
|
|
user_status[0] = isc_arg_gds;
|
|
|
|
user_status[1] = isc_io_error;
|
|
|
|
user_status[2] = isc_arg_string;
|
|
|
|
user_status[3] = (ISC_STATUS) "open";
|
|
|
|
user_status[4] = isc_arg_string;
|
|
|
|
user_status[5] = (ISC_STATUS) file_name.c_str();
|
|
|
|
user_status[6] = isc_arg_gds;
|
|
|
|
user_status[7] = isc_db_or_file_exists;
|
|
|
|
user_status[8] = isc_arg_end;
|
|
|
|
UTLD_save_status_strings(user_status);
|
|
|
|
}
|
|
|
|
return true;
|
2001-05-23 15:26:42 +02:00
|
|
|
}
|
2004-12-05 12:57:25 +01:00
|
|
|
|
2009-01-07 10:30:57 +01:00
|
|
|
isc_create_database(user_status, 0, file_name.c_str(), db_handle,
|
|
|
|
dpb.getBufferLength(), reinterpret_cast<const ISC_SCHAR*>(dpb.getBuffer()),
|
2004-12-05 12:57:25 +01:00
|
|
|
0);
|
|
|
|
}
|
2008-09-09 13:27:10 +02:00
|
|
|
catch (const Exception& ex)
|
2004-12-05 12:57:25 +01:00
|
|
|
{
|
2008-09-09 13:27:10 +02:00
|
|
|
ex.stuff_exception(user_status);
|
2003-11-18 08:58:35 +01:00
|
|
|
return true;
|
2001-05-23 15:26:42 +02:00
|
|
|
}
|
|
|
|
|
2003-11-18 08:58:35 +01:00
|
|
|
return true;
|
2001-05-23 15:26:42 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2003-02-15 04:01:51 +01:00
|
|
|
/**
|
2008-12-05 02:20:14 +01:00
|
|
|
|
2003-02-15 04:01:51 +01:00
|
|
|
generate_error
|
2008-12-05 02:20:14 +01:00
|
|
|
|
2003-02-15 04:01:51 +01:00
|
|
|
@brief
|
|
|
|
|
|
|
|
@param user_status
|
|
|
|
@param token
|
|
|
|
@param error
|
|
|
|
@param result
|
|
|
|
|
|
|
|
**/
|
2009-01-08 10:26:06 +01:00
|
|
|
static void generate_error(ISC_STATUS* user_status, const string& token, SSHORT error, SSHORT result)
|
2001-05-23 15:26:42 +02:00
|
|
|
{
|
2008-09-09 13:27:10 +02:00
|
|
|
string err_string;
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2003-11-08 00:27:24 +01:00
|
|
|
user_status[0] = isc_arg_gds;
|
|
|
|
user_status[1] = isc_sqlerr;
|
|
|
|
user_status[2] = isc_arg_number;
|
2001-05-23 15:26:42 +02:00
|
|
|
user_status[3] = -104;
|
2003-11-08 00:27:24 +01:00
|
|
|
user_status[4] = isc_arg_gds;
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2009-01-08 10:26:06 +01:00
|
|
|
switch (error)
|
|
|
|
{
|
2001-05-23 15:26:42 +02:00
|
|
|
case UNEXPECTED_END_OF_COMMAND:
|
2003-11-08 00:27:24 +01:00
|
|
|
user_status[5] = isc_command_end_err;
|
|
|
|
user_status[6] = isc_arg_end;
|
2001-05-23 15:26:42 +02:00
|
|
|
break;
|
|
|
|
|
|
|
|
case UNEXPECTED_TOKEN:
|
|
|
|
case TOKEN_TOO_LONG:
|
2009-11-21 10:24:20 +01:00
|
|
|
if (result)
|
|
|
|
{
|
2004-12-05 12:57:25 +01:00
|
|
|
err_string.assign(1, (TEXT) result);
|
|
|
|
err_string += token;
|
|
|
|
err_string += (TEXT) result;
|
2001-05-23 15:26:42 +02:00
|
|
|
}
|
|
|
|
else
|
2004-12-05 12:57:25 +01:00
|
|
|
err_string = token;
|
2003-11-08 00:27:24 +01:00
|
|
|
user_status[5] = isc_token_err;
|
|
|
|
user_status[6] = isc_arg_gds;
|
|
|
|
user_status[7] = isc_random;
|
|
|
|
user_status[8] = isc_arg_string;
|
2004-12-05 12:57:25 +01:00
|
|
|
user_status[9] = (ISC_STATUS)(err_string.c_str());
|
2003-11-08 00:27:24 +01:00
|
|
|
user_status[10] = isc_arg_end;
|
2001-05-23 15:26:42 +02:00
|
|
|
UTLD_save_status_strings(user_status);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2003-02-15 04:01:51 +01:00
|
|
|
/**
|
2008-12-05 02:20:14 +01:00
|
|
|
|
2003-02-15 04:01:51 +01:00
|
|
|
get_next_token
|
2008-12-05 02:20:14 +01:00
|
|
|
|
2003-02-15 04:01:51 +01:00
|
|
|
@brief
|
|
|
|
|
|
|
|
@param stmt
|
|
|
|
@param stmt_end
|
|
|
|
@param token
|
|
|
|
|
|
|
|
**/
|
2009-01-08 10:26:06 +01:00
|
|
|
static SSHORT get_next_token(const SCHAR** stmt, const SCHAR* stmt_end, string& token)
|
2001-05-23 15:26:42 +02:00
|
|
|
{
|
2008-06-05 13:02:42 +02:00
|
|
|
UCHAR c, char_class = 0;
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2004-12-05 12:57:25 +01:00
|
|
|
token.erase();
|
2003-11-28 07:48:34 +01:00
|
|
|
const SCHAR* s = *stmt;
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2009-01-08 10:26:06 +01:00
|
|
|
for (;;)
|
|
|
|
{
|
2009-01-09 02:50:54 +01:00
|
|
|
if (s >= stmt_end)
|
|
|
|
{
|
2001-05-23 15:26:42 +02:00
|
|
|
*stmt = s;
|
|
|
|
return NO_MORE_TOKENS;
|
|
|
|
}
|
2009-01-09 02:50:54 +01:00
|
|
|
|
2001-05-23 15:26:42 +02:00
|
|
|
c = *s++;
|
2009-01-09 02:50:54 +01:00
|
|
|
|
2009-01-08 10:26:06 +01:00
|
|
|
if (c == '/' && s < stmt_end && *s == '*')
|
|
|
|
{
|
2001-05-23 15:26:42 +02:00
|
|
|
s++;
|
2009-11-21 10:24:20 +01:00
|
|
|
while (s < stmt_end)
|
|
|
|
{
|
2001-05-23 15:26:42 +02:00
|
|
|
c = *s++;
|
|
|
|
if (c == '*' && s < stmt_end && *s == '/')
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
s++;
|
|
|
|
continue;
|
|
|
|
}
|
2009-01-09 02:50:54 +01:00
|
|
|
|
2005-06-11 02:22:47 +02:00
|
|
|
// CVC: Dmitry told me to leave this in peace, but if somebody wants
|
|
|
|
// to experiment ignoring single line comments, here's an idea.
|
|
|
|
if (c == '-' && s < stmt_end && *s == '-')
|
|
|
|
{
|
|
|
|
s++;
|
|
|
|
while (s < stmt_end)
|
|
|
|
{
|
|
|
|
c = *s++;
|
|
|
|
if (c == '\n')
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
// CVC: End modification.
|
2009-01-09 02:50:54 +01:00
|
|
|
|
2006-06-07 07:39:46 +02:00
|
|
|
char_class = classes(c);
|
2003-11-18 08:58:35 +01:00
|
|
|
if (!(char_class & CHR_WHITE))
|
2001-05-23 15:26:42 +02:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2009-04-19 12:06:32 +02:00
|
|
|
// At this point c contains character and class contains character class.
|
|
|
|
// s is pointing to next character.
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2003-11-28 07:48:34 +01:00
|
|
|
const SCHAR* const start_of_token = s - 1;
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2009-04-19 12:06:32 +02:00
|
|
|
// In here we handle only 4 cases, STRING, INTEGER, arbitrary
|
|
|
|
// SYMBOL and single character punctuation.
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2009-01-08 10:26:06 +01:00
|
|
|
if (char_class & CHR_QUOTE)
|
|
|
|
{
|
|
|
|
for (;;)
|
|
|
|
{
|
2001-05-23 15:26:42 +02:00
|
|
|
if (s >= stmt_end)
|
|
|
|
return UNEXPECTED_END_OF_COMMAND;
|
|
|
|
|
2009-04-19 12:06:32 +02:00
|
|
|
// *s is quote - if next != quote we're at the end
|
2001-05-23 15:26:42 +02:00
|
|
|
|
|
|
|
if ((*s == c) && ((++s == stmt_end) || (*s != c)))
|
|
|
|
break;
|
2004-12-05 12:57:25 +01:00
|
|
|
token += *s++;
|
2001-05-23 15:26:42 +02:00
|
|
|
}
|
|
|
|
*stmt = s;
|
2009-11-21 10:24:20 +01:00
|
|
|
if (token.length() > MAX_TOKEN_SIZE)
|
|
|
|
{
|
2008-12-05 02:20:14 +01:00
|
|
|
// '=' used as then there is no place for null termination
|
2004-12-12 02:57:19 +01:00
|
|
|
token.erase(MAX_TOKEN_SIZE);
|
2004-12-11 01:08:55 +01:00
|
|
|
return TOKEN_TOO_LONG;
|
|
|
|
}
|
2001-05-23 15:26:42 +02:00
|
|
|
return STRING;
|
|
|
|
}
|
|
|
|
|
2009-04-19 12:06:32 +02:00
|
|
|
// Is it an integer?
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2009-01-08 10:26:06 +01:00
|
|
|
if (char_class & CHR_DIGIT)
|
|
|
|
{
|
2006-06-07 07:39:46 +02:00
|
|
|
for (; s < stmt_end && (classes(c = *s) & CHR_DIGIT); ++s); // empty body
|
2007-04-11 18:05:40 +02:00
|
|
|
fb_assert(s >= start_of_token);
|
|
|
|
const size_t length = (s - start_of_token);
|
2001-05-23 15:26:42 +02:00
|
|
|
*stmt = s;
|
2009-11-21 10:24:20 +01:00
|
|
|
if (length > MAX_TOKEN_SIZE)
|
|
|
|
{
|
2004-12-12 02:57:19 +01:00
|
|
|
token.assign(start_of_token, MAX_TOKEN_SIZE);
|
2004-12-11 01:08:55 +01:00
|
|
|
return TOKEN_TOO_LONG;
|
|
|
|
}
|
2004-12-05 12:57:25 +01:00
|
|
|
token.assign(start_of_token, length);
|
2001-05-23 15:26:42 +02:00
|
|
|
return NUMERIC;
|
|
|
|
}
|
|
|
|
|
2009-04-19 12:06:32 +02:00
|
|
|
// Is is a symbol?
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2009-01-08 10:26:06 +01:00
|
|
|
if (char_class & CHR_LETTER)
|
|
|
|
{
|
2004-12-05 12:57:25 +01:00
|
|
|
token += UPPER(c);
|
2006-06-07 07:39:46 +02:00
|
|
|
for (; s < stmt_end && (classes(*s) & CHR_IDENT); s++) {
|
2004-12-05 12:57:25 +01:00
|
|
|
token += UPPER(*s);
|
2001-05-23 15:26:42 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
*stmt = s;
|
2009-11-21 10:24:20 +01:00
|
|
|
if (token.length() > MAX_TOKEN_SIZE)
|
|
|
|
{
|
2004-12-12 02:57:19 +01:00
|
|
|
token.erase(MAX_TOKEN_SIZE);
|
2004-12-11 01:08:55 +01:00
|
|
|
return TOKEN_TOO_LONG;
|
|
|
|
}
|
2001-05-23 15:26:42 +02:00
|
|
|
return SYMBOL;
|
|
|
|
}
|
|
|
|
|
2009-04-19 12:06:32 +02:00
|
|
|
// What remains at this point for us is the single character punctuation.
|
2001-05-23 15:26:42 +02:00
|
|
|
|
|
|
|
*stmt = s;
|
|
|
|
|
|
|
|
return (c == ';' ? NO_MORE_TOKENS : c);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2003-02-15 04:01:51 +01:00
|
|
|
/**
|
2008-12-05 02:20:14 +01:00
|
|
|
|
2003-02-15 04:01:51 +01:00
|
|
|
get_token
|
2008-12-05 02:20:14 +01:00
|
|
|
|
2003-02-15 04:01:51 +01:00
|
|
|
@brief
|
|
|
|
|
|
|
|
@param status
|
|
|
|
@param token_type
|
|
|
|
@param optional
|
|
|
|
@param stmt
|
|
|
|
@param stmt_end
|
|
|
|
@param token
|
|
|
|
|
|
|
|
**/
|
2003-11-18 08:58:35 +01:00
|
|
|
static SSHORT get_token(ISC_STATUS* status,
|
2001-05-23 15:26:42 +02:00
|
|
|
SSHORT token_type,
|
2003-09-04 23:26:15 +02:00
|
|
|
bool optional,
|
2003-11-28 07:48:34 +01:00
|
|
|
const SCHAR** stmt,
|
2003-11-18 08:58:35 +01:00
|
|
|
const SCHAR* const stmt_end,
|
2008-09-09 13:27:10 +02:00
|
|
|
string& token)
|
2001-05-23 15:26:42 +02:00
|
|
|
{
|
2003-11-28 07:48:34 +01:00
|
|
|
const SCHAR* temp_stmt = *stmt;
|
2004-12-05 12:57:25 +01:00
|
|
|
const SSHORT result = get_next_token(&temp_stmt, stmt_end, token);
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2009-01-08 10:26:06 +01:00
|
|
|
switch (result)
|
|
|
|
{
|
2001-05-23 15:26:42 +02:00
|
|
|
case NO_MORE_TOKENS:
|
|
|
|
*stmt = temp_stmt;
|
|
|
|
generate_error(status, token, UNEXPECTED_END_OF_COMMAND, 0);
|
2002-11-14 09:33:08 +01:00
|
|
|
return FB_FAILURE;
|
2001-05-23 15:26:42 +02:00
|
|
|
|
|
|
|
case UNEXPECTED_END_OF_COMMAND:
|
|
|
|
case TOKEN_TOO_LONG:
|
|
|
|
*stmt = temp_stmt;
|
|
|
|
|
2008-12-05 02:20:14 +01:00
|
|
|
// generate error here
|
2001-05-23 15:26:42 +02:00
|
|
|
|
|
|
|
generate_error(status, token, result, 0);
|
2002-11-14 09:33:08 +01:00
|
|
|
return FB_FAILURE;
|
2001-05-23 15:26:42 +02:00
|
|
|
}
|
|
|
|
|
2009-04-19 12:06:32 +02:00
|
|
|
// Some token was found
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2009-11-21 10:24:20 +01:00
|
|
|
if (result == token_type)
|
|
|
|
{
|
2001-05-23 15:26:42 +02:00
|
|
|
*stmt = temp_stmt;
|
2002-11-14 09:33:08 +01:00
|
|
|
return FB_SUCCESS;
|
2001-05-23 15:26:42 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
if (optional)
|
2002-11-14 09:33:08 +01:00
|
|
|
return FB_SUCCESS;
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2009-04-19 12:06:32 +02:00
|
|
|
// generate error here and return failure;
|
2001-05-23 15:26:42 +02:00
|
|
|
|
|
|
|
*stmt = temp_stmt;
|
|
|
|
generate_error(status, token, UNEXPECTED_TOKEN,
|
|
|
|
(result == STRING) ? *(temp_stmt - 1) : 0);
|
2002-11-14 09:33:08 +01:00
|
|
|
return FB_FAILURE;
|
2001-05-23 15:26:42 +02:00
|
|
|
}
|