mirror of
https://github.com/FirebirdSQL/firebird.git
synced 2025-01-23 22:43:04 +01:00
use new classes to preparse 'create database' statement
This commit is contained in:
parent
41b2712dc2
commit
637e5ed95a
@ -32,6 +32,7 @@
|
||||
#include "../dsql/prepa_proto.h"
|
||||
#include "../dsql/utld_proto.h"
|
||||
#include "../jrd/gds_proto.h"
|
||||
#include "../common/classes/ClumpletWriter.h"
|
||||
|
||||
enum pp_vals {
|
||||
PP_CREATE = 0,
|
||||
@ -49,29 +50,10 @@ enum pp_vals {
|
||||
};
|
||||
|
||||
|
||||
const int MAX_TOKEN_SIZE = 1024;
|
||||
|
||||
|
||||
// use stuff_dpb in place of STUFF to avoid confusion with BLR STUFF
|
||||
// macro defined in dsql.h
|
||||
|
||||
static inline void stuff_dpb(SCHAR*& dpb, const SCHAR blr)
|
||||
{
|
||||
*dpb++ = blr;
|
||||
}
|
||||
|
||||
static inline void stuff_dpb_long(SCHAR*& dpb, const SLONG blr)
|
||||
{
|
||||
stuff_dpb(dpb, blr);
|
||||
stuff_dpb(dpb, blr >> 8);
|
||||
stuff_dpb(dpb, blr >> 16);
|
||||
stuff_dpb(dpb, blr >> 24);
|
||||
}
|
||||
|
||||
static void generate_error(ISC_STATUS*, const SCHAR*, SSHORT, SSHORT);
|
||||
static SSHORT get_next_token(const SCHAR**, const SCHAR*, SCHAR*, USHORT*);
|
||||
static void generate_error(ISC_STATUS*, const Firebird::string&, SSHORT, SSHORT);
|
||||
static SSHORT get_next_token(const SCHAR**, const SCHAR*, Firebird::string&);
|
||||
static SSHORT get_token(ISC_STATUS*, SSHORT, bool, const SCHAR**,
|
||||
const SCHAR* const, SCHAR*, USHORT*);
|
||||
const SCHAR* const, Firebird::string&);
|
||||
|
||||
struct pp_table {
|
||||
SCHAR symbol[10];
|
||||
@ -131,234 +113,179 @@ bool PREPARSE_execute(
|
||||
bool* stmt_eaten,
|
||||
USHORT dialect)
|
||||
{
|
||||
char* const token = (SCHAR *) gds__alloc((SLONG) MAX_TOKEN_SIZE + 1);
|
||||
// FREE: by return(s) in this procedure
|
||||
if (!token) { // NOMEM:
|
||||
user_status[0] = isc_arg_gds;
|
||||
user_status[1] = isc_virmemexh;
|
||||
user_status[2] = isc_arg_end;
|
||||
return false;
|
||||
}
|
||||
// no use creating separate pool for a couple of strings
|
||||
Firebird::ContextPoolHolder context(getDefaultMemoryPool());
|
||||
|
||||
if (!stmt_length)
|
||||
stmt_length = strlen(stmt);
|
||||
const char* const stmt_end = stmt + stmt_length;
|
||||
|
||||
USHORT token_length;
|
||||
if (get_token(user_status, SYMBOL, false, &stmt, stmt_end, token,
|
||||
&token_length) ||
|
||||
token_length != pp_symbols[PP_CREATE].length ||
|
||||
strcmp(token, pp_symbols[PP_CREATE].symbol))
|
||||
try
|
||||
{
|
||||
gds__free(token);
|
||||
return false;
|
||||
}
|
||||
if (!stmt_length)
|
||||
stmt_length = strlen(stmt);
|
||||
const char* const stmt_end = stmt + stmt_length;
|
||||
Firebird::string token;
|
||||
|
||||
if (get_token(user_status, SYMBOL, false, &stmt, stmt_end, token,
|
||||
&token_length) ||
|
||||
(token_length != pp_symbols[PP_DATABASE].length &&
|
||||
token_length != pp_symbols[PP_SCHEMA].length) ||
|
||||
(strcmp(token, pp_symbols[PP_DATABASE].symbol) &&
|
||||
strcmp(token, pp_symbols[PP_SCHEMA].symbol)))
|
||||
{
|
||||
gds__free(token);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (get_token(user_status, STRING, false, &stmt, stmt_end, token,
|
||||
&token_length))
|
||||
{
|
||||
gds__free(token);
|
||||
return true;
|
||||
}
|
||||
|
||||
TEXT file_name[MAX_TOKEN_SIZE + 1];
|
||||
strcpy(file_name, token);
|
||||
*stmt_eaten = false;
|
||||
char* const dpb_array = (SCHAR*) gds__alloc((SLONG) 2 * MAX_TOKEN_SIZE + 25);
|
||||
char* dpb = dpb_array;
|
||||
// FREE: by following return(s) in this procedure
|
||||
if (!dpb_array) { // NOMEM:
|
||||
user_status[0] = isc_arg_gds;
|
||||
user_status[1] = isc_virmemexh;
|
||||
user_status[2] = isc_arg_end;
|
||||
gds__free(token);
|
||||
return true;
|
||||
}
|
||||
|
||||
stuff_dpb(dpb, isc_dpb_version1);
|
||||
stuff_dpb(dpb, isc_dpb_overwrite);
|
||||
stuff_dpb(dpb, 1);
|
||||
stuff_dpb(dpb, 0);
|
||||
stuff_dpb(dpb, isc_dpb_sql_dialect);
|
||||
stuff_dpb(dpb, 4);
|
||||
stuff_dpb_long(dpb, dialect);
|
||||
|
||||
SLONG page_size = 0;
|
||||
bool matched;
|
||||
do {
|
||||
const SSHORT result = get_next_token(&stmt, stmt_end, token, &token_length);
|
||||
if (result == NO_MORE_TOKENS) {
|
||||
*stmt_eaten = true;
|
||||
break;
|
||||
if (get_token(user_status, SYMBOL, false, &stmt, stmt_end, token) ||
|
||||
token.length() != pp_symbols[PP_CREATE].length ||
|
||||
token != pp_symbols[PP_CREATE].symbol)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else if (result < 0)
|
||||
break;
|
||||
|
||||
matched = false;
|
||||
for (int i = 3; pp_symbols[i].length && !matched; i++) {
|
||||
if (token_length == pp_symbols[i].length &&
|
||||
!strcmp(token, pp_symbols[i].symbol))
|
||||
{
|
||||
bool get_out = false;
|
||||
const SCHAR* ch;
|
||||
SSHORT l;
|
||||
// CVC: What's strange, this routine doesn't check token_length
|
||||
// but it proceeds blindly, trying to exhaust the token itself.
|
||||
if (get_token(user_status, SYMBOL, false, &stmt, stmt_end, token) ||
|
||||
(token.length() != pp_symbols[PP_DATABASE].length &&
|
||||
token.length() != pp_symbols[PP_SCHEMA].length) ||
|
||||
(token != pp_symbols[PP_DATABASE].symbol &&
|
||||
token != pp_symbols[PP_SCHEMA].symbol))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
switch (pp_symbols[i].code) {
|
||||
case PP_PAGE_SIZE:
|
||||
case PP_PAGESIZE:
|
||||
if (get_token(user_status, '=', true, &stmt, stmt_end,
|
||||
token, &token_length) ||
|
||||
get_token(user_status, NUMERIC, false, &stmt,
|
||||
stmt_end, token, &token_length))
|
||||
{
|
||||
get_out = true;
|
||||
if (get_token(user_status, STRING, false, &stmt, stmt_end, token))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
Firebird::PathName file_name(token.ToPathName());
|
||||
*stmt_eaten = false;
|
||||
Firebird::ClumpletWriter dpb(true, 0x1000, isc_dpb_version1);
|
||||
|
||||
dpb.insertString(isc_dpb_overwrite, "\0", 1);
|
||||
dpb.insertInt(isc_dpb_sql_dialect, dialect);
|
||||
|
||||
SLONG page_size = 0;
|
||||
bool matched;
|
||||
do {
|
||||
const SSHORT result = get_next_token(&stmt, stmt_end, token);
|
||||
if (result == NO_MORE_TOKENS) {
|
||||
*stmt_eaten = true;
|
||||
break;
|
||||
}
|
||||
else if (result < 0)
|
||||
break;
|
||||
|
||||
matched = false;
|
||||
for (int i = 3; pp_symbols[i].length && !matched; i++) {
|
||||
if (token.length() == pp_symbols[i].length &&
|
||||
token == pp_symbols[i].symbol)
|
||||
{
|
||||
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.
|
||||
|
||||
switch (pp_symbols[i].code) {
|
||||
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;
|
||||
break;
|
||||
}
|
||||
page_size = atol(token);
|
||||
stuff_dpb(dpb, isc_dpb_page_size);
|
||||
stuff_dpb(dpb, 4);
|
||||
stuff_dpb_long(dpb, page_size);
|
||||
matched = true;
|
||||
break;
|
||||
|
||||
case PP_USER:
|
||||
if (get_token(user_status, STRING, false, &stmt, stmt_end,
|
||||
token, &token_length))
|
||||
{
|
||||
get_out = true;
|
||||
case PP_USER:
|
||||
if (get_token(user_status, STRING, false, &stmt, stmt_end, token))
|
||||
{
|
||||
get_out = true;
|
||||
break;
|
||||
}
|
||||
|
||||
dpb.insertString(isc_dpb_user_name, token);
|
||||
matched = true;
|
||||
break;
|
||||
|
||||
case PP_PASSWORD:
|
||||
if (get_token(user_status, STRING, false, &stmt, stmt_end, token))
|
||||
{
|
||||
get_out = true;
|
||||
break;
|
||||
}
|
||||
|
||||
dpb.insertString(isc_dpb_password, token);
|
||||
matched = true;
|
||||
break;
|
||||
|
||||
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;
|
||||
|
||||
case PP_LENGTH:
|
||||
// Skip a token for value
|
||||
|
||||
if (get_token(user_status, '=', true, &stmt, stmt_end, token) ||
|
||||
get_token(user_status, NUMERIC, false, &stmt, stmt_end, token))
|
||||
{
|
||||
get_out = true;
|
||||
break;
|
||||
}
|
||||
|
||||
matched = true;
|
||||
break;
|
||||
|
||||
case PP_PAGE:
|
||||
case PP_PAGES:
|
||||
matched = true;
|
||||
break;
|
||||
}
|
||||
|
||||
stuff_dpb(dpb, isc_dpb_user_name);
|
||||
l = token_length;
|
||||
stuff_dpb(dpb, l);
|
||||
ch = token;
|
||||
while (*ch)
|
||||
stuff_dpb(dpb, *ch++);
|
||||
matched = true;
|
||||
break;
|
||||
|
||||
case PP_PASSWORD:
|
||||
if (get_token(user_status, STRING, false, &stmt, stmt_end,
|
||||
token, &token_length))
|
||||
{
|
||||
get_out = true;
|
||||
break;
|
||||
if (get_out) {
|
||||
return true;
|
||||
}
|
||||
|
||||
stuff_dpb(dpb, isc_dpb_password);
|
||||
l = token_length;
|
||||
stuff_dpb(dpb, l);
|
||||
ch = token;
|
||||
while (*ch)
|
||||
stuff_dpb(dpb, *ch++);
|
||||
matched = true;
|
||||
break;
|
||||
|
||||
case PP_SET:
|
||||
if (get_token(user_status, SYMBOL, false, &stmt, stmt_end,
|
||||
token, &token_length) ||
|
||||
token_length != pp_symbols[PP_NAMES].length ||
|
||||
strcmp(token, pp_symbols[PP_NAMES].symbol) ||
|
||||
get_token(user_status, STRING, false, &stmt, stmt_end,
|
||||
token, &token_length))
|
||||
{
|
||||
get_out = true;
|
||||
break;
|
||||
}
|
||||
|
||||
stuff_dpb(dpb, isc_dpb_lc_ctype);
|
||||
l = token_length;
|
||||
stuff_dpb(dpb, l);
|
||||
ch = token;
|
||||
while (*ch)
|
||||
stuff_dpb(dpb, *ch++);
|
||||
matched = true;
|
||||
break;
|
||||
|
||||
case PP_LENGTH:
|
||||
// Skip a token for value
|
||||
|
||||
if (get_token(user_status, '=', true, &stmt, stmt_end,
|
||||
token, &token_length) ||
|
||||
get_token(user_status, NUMERIC, false, &stmt,
|
||||
stmt_end, token, &token_length))
|
||||
{
|
||||
get_out = true;
|
||||
break;
|
||||
}
|
||||
|
||||
matched = true;
|
||||
break;
|
||||
|
||||
case PP_PAGE:
|
||||
case PP_PAGES:
|
||||
matched = true;
|
||||
break;
|
||||
}
|
||||
|
||||
if (get_out) {
|
||||
gds__free(dpb_array);
|
||||
gds__free(token);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} while (matched);
|
||||
|
||||
const USHORT dpb_len = dpb - dpb_array;
|
||||
} while (matched);
|
||||
|
||||
// This code is because 3.3 server does not recognize isc_dpb_overwrite.
|
||||
FB_API_HANDLE temp_db_handle = 0;
|
||||
if (!isc_attach_database(user_status, 0, file_name, &temp_db_handle,
|
||||
dpb_len, dpb_array) ||
|
||||
user_status[1] != isc_io_error)
|
||||
FB_API_HANDLE temp_db_handle = 0;
|
||||
if (!isc_attach_database(user_status, 0, file_name.c_str(),
|
||||
&temp_db_handle, dpb.getBufferLength(),
|
||||
reinterpret_cast<const ISC_SCHAR*>(dpb.getBuffer())) ||
|
||||
user_status[1] != isc_io_error)
|
||||
{
|
||||
if (!user_status[1]) {
|
||||
// Swallow status from detach.
|
||||
ISC_STATUS_ARRAY temp_status;
|
||||
isc_detach_database(temp_status, &temp_db_handle);
|
||||
}
|
||||
if (!user_status[1] || user_status[1] == isc_bad_db_format) {
|
||||
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;
|
||||
}
|
||||
|
||||
isc_create_database(user_status, 0, file_name.c_str(),
|
||||
(db_handle), dpb.getBufferLength(),
|
||||
reinterpret_cast<const ISC_SCHAR*>(dpb.getBuffer()),
|
||||
0);
|
||||
}
|
||||
catch(const std::exception& ex)
|
||||
{
|
||||
if (!user_status[1]) {
|
||||
// Swallow status from detach.
|
||||
ISC_STATUS_ARRAY temp_status;
|
||||
isc_detach_database(temp_status, &temp_db_handle);
|
||||
}
|
||||
if (!user_status[1] || user_status[1] == isc_bad_db_format) {
|
||||
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;
|
||||
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);
|
||||
}
|
||||
if (dpb_array)
|
||||
gds__free(dpb_array);
|
||||
gds__free(token);
|
||||
Firebird::stuff_exception(user_status, ex);
|
||||
return true;
|
||||
}
|
||||
|
||||
isc_create_database(user_status, 0, file_name,
|
||||
(db_handle), dpb_len,
|
||||
dpb_array, 0);
|
||||
|
||||
if (dpb_array)
|
||||
gds__free(dpb_array);
|
||||
gds__free(token);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -377,9 +304,9 @@ bool PREPARSE_execute(
|
||||
**/
|
||||
static void generate_error(
|
||||
ISC_STATUS* user_status,
|
||||
const SCHAR* token, SSHORT error, SSHORT result)
|
||||
const Firebird::string& token, SSHORT error, SSHORT result)
|
||||
{
|
||||
TEXT err_string[MAX_TOKEN_SIZE + 3];
|
||||
Firebird::string err_string;
|
||||
|
||||
user_status[0] = isc_arg_gds;
|
||||
user_status[1] = isc_sqlerr;
|
||||
@ -396,19 +323,17 @@ static void generate_error(
|
||||
case UNEXPECTED_TOKEN:
|
||||
case TOKEN_TOO_LONG:
|
||||
if (result) {
|
||||
err_string[0] = (TEXT) result;
|
||||
strcpy(err_string + 1, token);
|
||||
const size_t length = strlen(token);
|
||||
err_string[length + 1] = (TEXT) result;
|
||||
err_string[length + 2] = '\0';
|
||||
err_string.assign(1, (TEXT) result);
|
||||
err_string += token;
|
||||
err_string += (TEXT) result;
|
||||
}
|
||||
else
|
||||
strcpy(err_string, token);
|
||||
err_string = token;
|
||||
user_status[5] = isc_token_err;
|
||||
user_status[6] = isc_arg_gds;
|
||||
user_status[7] = isc_random;
|
||||
user_status[8] = isc_arg_string;
|
||||
user_status[9] = (ISC_STATUS) err_string;
|
||||
user_status[9] = (ISC_STATUS)(err_string.c_str());
|
||||
user_status[10] = isc_arg_end;
|
||||
UTLD_save_status_strings(user_status);
|
||||
break;
|
||||
@ -425,17 +350,16 @@ static void generate_error(
|
||||
@param stmt
|
||||
@param stmt_end
|
||||
@param token
|
||||
@param token_length
|
||||
|
||||
**/
|
||||
static SSHORT get_next_token(
|
||||
const SCHAR** stmt,
|
||||
const SCHAR* stmt_end,
|
||||
SCHAR* token, USHORT* token_length)
|
||||
Firebird::string& token)
|
||||
{
|
||||
UCHAR c, char_class;
|
||||
|
||||
*token_length = 0;
|
||||
token.erase();
|
||||
const SCHAR* s = *stmt;
|
||||
|
||||
for (;;) {
|
||||
@ -467,12 +391,8 @@ static SSHORT get_next_token(
|
||||
/* In here we handle only 4 cases, STRING, INTEGER, arbitrary
|
||||
SYMBOL and single character punctuation. */
|
||||
|
||||
// token can be up to MAX_TOKEN_SIZE and 1 is for null termination
|
||||
|
||||
SCHAR* p = token;
|
||||
const char* const token_end = token + MAX_TOKEN_SIZE + 1;
|
||||
if (char_class & CHR_QUOTE) {
|
||||
for (; p < token_end; p++) {
|
||||
for (;;) {
|
||||
if (s >= stmt_end)
|
||||
return UNEXPECTED_END_OF_COMMAND;
|
||||
|
||||
@ -480,18 +400,9 @@ static SSHORT get_next_token(
|
||||
|
||||
if ((*s == c) && ((++s == stmt_end) || (*s != c)))
|
||||
break;
|
||||
*p = *s++;
|
||||
token += *s++;
|
||||
}
|
||||
*stmt = s;
|
||||
if (p >= token_end) {
|
||||
// '=' used as then there is no place for null termination
|
||||
|
||||
*token_length = MAX_TOKEN_SIZE;
|
||||
token[MAX_TOKEN_SIZE] = '\0';
|
||||
return TOKEN_TOO_LONG;
|
||||
}
|
||||
*token_length = (s - start_of_token) - 2;
|
||||
*p = '\0';
|
||||
return STRING;
|
||||
}
|
||||
|
||||
@ -501,36 +412,19 @@ static SSHORT get_next_token(
|
||||
for (; s < stmt_end && (classes[c = *s] & CHR_DIGIT); ++s); // empty body
|
||||
const ptrdiff_t length = (s - start_of_token);
|
||||
*stmt = s;
|
||||
if (length > MAX_TOKEN_SIZE) {
|
||||
memcpy(token, start_of_token, MAX_TOKEN_SIZE);
|
||||
token[MAX_TOKEN_SIZE] = '\0';
|
||||
*token_length = MAX_TOKEN_SIZE;
|
||||
return TOKEN_TOO_LONG;
|
||||
}
|
||||
memcpy(token, start_of_token, length);
|
||||
token[length] = '\0';
|
||||
*token_length = length;
|
||||
token.assign(start_of_token, length);
|
||||
return NUMERIC;
|
||||
}
|
||||
|
||||
// Is is a symbol
|
||||
|
||||
if (char_class & CHR_LETTER) {
|
||||
*p++ = UPPER(c);
|
||||
for (; s < stmt_end && (classes[static_cast<UCHAR>(*s)] & CHR_IDENT) && p < token_end; s++) {
|
||||
*p++ = UPPER(*s);
|
||||
token += UPPER(c);
|
||||
for (; s < stmt_end && (classes[static_cast<UCHAR>(*s)] & CHR_IDENT); s++) {
|
||||
token += UPPER(*s);
|
||||
}
|
||||
|
||||
// what happend to token size check. It went to hell
|
||||
|
||||
*stmt = s;
|
||||
if (p >= token_end) {
|
||||
*token_length = MAX_TOKEN_SIZE;
|
||||
token[MAX_TOKEN_SIZE] = '\0';
|
||||
return TOKEN_TOO_LONG;
|
||||
}
|
||||
*token_length = s - start_of_token;
|
||||
*p = 0;
|
||||
return SYMBOL;
|
||||
}
|
||||
|
||||
@ -554,7 +448,6 @@ static SSHORT get_next_token(
|
||||
@param stmt
|
||||
@param stmt_end
|
||||
@param token
|
||||
@param token_length
|
||||
|
||||
**/
|
||||
static SSHORT get_token(ISC_STATUS* status,
|
||||
@ -562,11 +455,10 @@ static SSHORT get_token(ISC_STATUS* status,
|
||||
bool optional,
|
||||
const SCHAR** stmt,
|
||||
const SCHAR* const stmt_end,
|
||||
SCHAR* token,
|
||||
USHORT* token_length)
|
||||
Firebird::string& token)
|
||||
{
|
||||
const SCHAR* temp_stmt = *stmt;
|
||||
const SSHORT result = get_next_token(&temp_stmt, stmt_end, token, token_length);
|
||||
const SSHORT result = get_next_token(&temp_stmt, stmt_end, token);
|
||||
|
||||
switch (result) {
|
||||
case NO_MORE_TOKENS:
|
||||
|
Loading…
Reference in New Issue
Block a user