8
0
mirror of https://github.com/FirebirdSQL/firebird.git synced 2025-01-25 02:03:03 +01:00
firebird-mirror/src/dsql/preparse.cpp

605 lines
13 KiB
C++
Raw Normal View History

2001-05-23 15:26:42 +02:00
/*
* PROGRAM: Dynamic SQL runtime support
2003-10-05 08:37:26 +02:00
* MODULE: preparse.cpp
2001-05-23 15:26:42 +02:00
* DESCRIPTION: Dynamic SQL pre parser / parser on client side.
* 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): ______________________________________.
*/
#include "firebird.h"
2001-05-23 15:26:42 +02:00
#include <stdlib.h>
#include <string.h>
#include "../jrd/common.h"
#include "../jrd/gds.h"
2001-05-23 15:26:42 +02:00
#include "../dsql/chars.h"
#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"
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
2003-09-28 02:36:28 +02:00
const int MAX_TOKEN_SIZE = 1024;
2001-05-23 15:26:42 +02:00
2003-10-05 08:37:26 +02:00
// use stuff_dpb in place of STUFF to avoid confusion with BLR STUFF
// macro defined in dsql.h
2001-05-23 15:26:42 +02:00
2003-10-05 08:37:26 +02:00
static inline void stuff_dpb(SCHAR*& dpb, const SCHAR blr)
{
2003-10-01 20:11:23 +02:00
*dpb++ = blr;
}
2003-10-05 08:37:26 +02:00
static inline void stuff_dpb_long(SCHAR*& dpb, const SLONG blr)
{
2003-10-01 20:11:23 +02:00
stuff_dpb(dpb, blr);
stuff_dpb(dpb, blr >> 8);
stuff_dpb(dpb, blr >> 16);
stuff_dpb(dpb, blr >> 24);
}
2001-05-23 15:26:42 +02:00
2003-04-10 08:32:58 +02:00
static void generate_error(ISC_STATUS *, SCHAR *, SSHORT, SSHORT);
2001-05-23 15:26:42 +02:00
static SSHORT get_next_token(SCHAR **, SCHAR *, SCHAR *, USHORT *);
2003-09-04 23:26:15 +02:00
static SSHORT get_token(ISC_STATUS *, SSHORT, bool, SCHAR **, SCHAR *,
2001-05-23 15:26:42 +02:00
SCHAR *, USHORT *);
struct pp_table {
2001-05-23 15:26:42 +02:00
SCHAR symbol[10];
SSHORT length;
SSHORT code;
};
2001-05-23 15:26:42 +02:00
static const pp_table pp_symbols[] = {
{"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},
{"", 0, 0}
2001-05-23 15:26:42 +02:00
};
/* define the tokens */
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
/**
PREPARSE_execute
@brief
@param user_status
@param db_handle
@param trans_handle
@param stmt_length
@param stmt
@param stmt_eaten
@param dialect
**/
int PREPARSE_execute(
2003-10-05 08:37:26 +02:00
ISC_STATUS* user_status,
FRBRD** db_handle,
FRBRD** trans_handle,
USHORT stmt_length,
2003-10-05 08:37:26 +02:00
SCHAR* stmt,
BOOLEAN* stmt_eaten,
USHORT dialect)
2001-05-23 15:26:42 +02:00
{
TEXT file_name[MAX_TOKEN_SIZE + 1];
SCHAR *token, *dpb_array, *dpb, *ch, *stmt_end;
SLONG page_size = 0;
USHORT dpb_len = 0, token_length;
SSHORT i, l, result;
2003-09-04 23:26:15 +02:00
bool matched;
bool get_out;
2003-04-16 12:18:51 +02:00
ISC_STATUS_ARRAY temp_status;
FRBRD *temp_db_handle = NULL;
2001-05-23 15:26:42 +02:00
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;
}
if (!stmt_length)
stmt_length = strlen(stmt);
stmt_end = stmt + stmt_length;
2003-09-04 23:26:15 +02:00
if (get_token(user_status, SYMBOL, false, &stmt, stmt_end, token,
2001-05-23 15:26:42 +02:00
&token_length) ||
token_length != pp_symbols[PP_CREATE].length ||
2003-09-04 23:26:15 +02:00
strcmp(token, pp_symbols[PP_CREATE].symbol))
{
2001-05-23 15:26:42 +02:00
gds__free((SLONG *) token);
return FALSE;
}
2003-09-04 23:26:15 +02:00
if (get_token(user_status, SYMBOL, false, &stmt, stmt_end, token,
2001-05-23 15:26:42 +02:00
&token_length) ||
(token_length != pp_symbols[PP_DATABASE].length &&
token_length != pp_symbols[PP_SCHEMA].length) ||
(strcmp(token, pp_symbols[PP_DATABASE].symbol) &&
2003-09-04 23:26:15 +02:00
strcmp(token, pp_symbols[PP_SCHEMA].symbol)))
{
2001-05-23 15:26:42 +02:00
gds__free((SLONG *) token);
return FALSE;
}
2003-09-04 23:26:15 +02:00
if (get_token(user_status, STRING, false, &stmt, stmt_end, token,
&token_length))
{
2001-05-23 15:26:42 +02:00
gds__free((SLONG *) token);
return TRUE;
}
strcpy(file_name, token);
*stmt_eaten = FALSE;
dpb = dpb_array = (SCHAR *) gds__alloc((SLONG) 2 * MAX_TOKEN_SIZE + 25);
/* 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((SLONG *) token);
return TRUE;
}
2003-10-01 20:11:23 +02:00
stuff_dpb(dpb, gds_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);
2003-10-05 08:37:26 +02:00
stuff_dpb_long(dpb, dialect);
2001-05-23 15:26:42 +02:00
do {
result = get_next_token(&stmt, stmt_end, token, &token_length);
if (result == NO_MORE_TOKENS) {
*stmt_eaten = TRUE;
break;
}
else if (result < 0)
break;
2003-09-04 23:26:15 +02:00
matched = false;
2001-05-23 15:26:42 +02:00
for (i = 3; pp_symbols[i].length && !matched; i++) {
if (token_length == pp_symbols[i].length &&
!strcmp(token, pp_symbols[i].symbol)) {
2003-09-04 23:26:15 +02:00
get_out = false;
2001-05-23 15:26:42 +02:00
switch (pp_symbols[i].code) {
case PP_PAGE_SIZE:
case PP_PAGESIZE:
2003-09-04 23:26:15 +02:00
if (get_token(user_status, '=', true, &stmt, stmt_end,
2001-05-23 15:26:42 +02:00
token, &token_length) ||
2003-09-04 23:26:15 +02:00
get_token(user_status, NUMERIC, false, &stmt,
stmt_end, token, &token_length))
{
get_out = true;
2001-05-23 15:26:42 +02:00
break;
}
page_size = atol(token);
2003-10-01 20:11:23 +02:00
stuff_dpb(dpb, gds_dpb_page_size);
stuff_dpb(dpb, 4);
2003-10-05 08:37:26 +02:00
stuff_dpb_long(dpb, page_size);
2003-09-04 23:26:15 +02:00
matched = true;
2001-05-23 15:26:42 +02:00
break;
case PP_USER:
2003-09-04 23:26:15 +02:00
if (get_token(user_status, STRING, false, &stmt, stmt_end,
token, &token_length))
{
get_out = true;
2001-05-23 15:26:42 +02:00
break;
}
2003-10-01 20:11:23 +02:00
stuff_dpb(dpb, gds_dpb_user_name);
2001-05-23 15:26:42 +02:00
l = token_length;
2003-10-01 20:11:23 +02:00
stuff_dpb(dpb, l);
2001-05-23 15:26:42 +02:00
ch = token;
while (*ch)
2003-10-01 20:11:23 +02:00
stuff_dpb(dpb, *ch++);
2003-09-04 23:26:15 +02:00
matched = true;
2001-05-23 15:26:42 +02:00
break;
case PP_PASSWORD:
2003-09-04 23:26:15 +02:00
if (get_token(user_status, STRING, false, &stmt, stmt_end,
token, &token_length))
{
get_out = true;
2001-05-23 15:26:42 +02:00
break;
}
2003-10-01 20:11:23 +02:00
stuff_dpb(dpb, gds_dpb_password);
2001-05-23 15:26:42 +02:00
l = token_length;
2003-10-01 20:11:23 +02:00
stuff_dpb(dpb, l);
2001-05-23 15:26:42 +02:00
ch = token;
while (*ch)
2003-10-01 20:11:23 +02:00
stuff_dpb(dpb, *ch++);
2003-09-04 23:26:15 +02:00
matched = true;
2001-05-23 15:26:42 +02:00
break;
case PP_SET:
2003-09-04 23:26:15 +02:00
if (get_token(user_status, SYMBOL, false, &stmt, stmt_end,
2001-05-23 15:26:42 +02:00
token, &token_length) ||
token_length != pp_symbols[PP_NAMES].length ||
strcmp(token, pp_symbols[PP_NAMES].symbol) ||
2003-09-04 23:26:15 +02:00
get_token(user_status, STRING, false, &stmt, stmt_end,
token, &token_length))
{
get_out = true;
2001-05-23 15:26:42 +02:00
break;
}
2003-10-01 20:11:23 +02:00
stuff_dpb(dpb, gds_dpb_lc_ctype);
2001-05-23 15:26:42 +02:00
l = token_length;
2003-10-01 20:11:23 +02:00
stuff_dpb(dpb, l);
2001-05-23 15:26:42 +02:00
ch = token;
while (*ch)
2003-10-01 20:11:23 +02:00
stuff_dpb(dpb, *ch++);
2003-09-04 23:26:15 +02:00
matched = true;
2001-05-23 15:26:42 +02:00
break;
case PP_LENGTH:
2001-05-23 15:26:42 +02:00
/* Skip a token for value */
2003-09-04 23:26:15 +02:00
if (get_token(user_status, '=', true, &stmt, stmt_end,
2001-05-23 15:26:42 +02:00
token, &token_length) ||
2003-09-04 23:26:15 +02:00
get_token(user_status, NUMERIC, false, &stmt,
stmt_end, token, &token_length))
{
get_out = true;
2001-05-23 15:26:42 +02:00
break;
}
2003-09-04 23:26:15 +02:00
matched = true;
2001-05-23 15:26:42 +02:00
break;
case PP_PAGE:
case PP_PAGES:
2003-09-04 23:26:15 +02:00
matched = true;
2001-05-23 15:26:42 +02:00
break;
}
if (get_out) {
gds__free((SLONG *) dpb_array);
gds__free((SLONG *) token);
return TRUE;
}
}
}
} while (matched);
dpb_len = dpb - dpb_array;
/* This code is because 3.3 server does not recognize isc_dpb_overwrite. */
2003-08-30 03:54:25 +02:00
if (!isc_attach_database(user_status, 0, file_name, &temp_db_handle,
2001-05-23 15:26:42 +02:00
dpb_len, dpb_array) ||
user_status[1] != gds_io_error) {
if (!user_status[1])
isc_detach_database(temp_status, &temp_db_handle);
if (!user_status[1] || user_status[1] == gds_bad_db_format) {
user_status[0] = gds_arg_gds;
user_status[1] = gds_io_error;
user_status[2] = gds_arg_string;
2003-04-10 08:32:58 +02:00
user_status[3] = (ISC_STATUS) "open";
2001-05-23 15:26:42 +02:00
user_status[4] = gds_arg_string;
2003-04-10 08:32:58 +02:00
user_status[5] = (ISC_STATUS) file_name;
2001-05-23 15:26:42 +02:00
user_status[6] = gds_arg_gds;
user_status[7] = gds_db_or_file_exists;
user_status[8] = gds_arg_end;
UTLD_save_status_strings(user_status);
}
if (dpb_array)
gds__free((SLONG *) dpb_array);
gds__free((SLONG *) token);
return TRUE;
}
isc_create_database(user_status, 0, file_name,
(db_handle), dpb_len,
2001-05-23 15:26:42 +02:00
dpb_array, 0);
if (dpb_array)
gds__free((SLONG *) dpb_array);
gds__free((SLONG *) token);
return TRUE;
}
/**
generate_error
@brief
@param user_status
@param token
@param error
@param result
**/
2001-05-23 15:26:42 +02:00
static void generate_error(
2003-04-10 08:32:58 +02:00
ISC_STATUS * user_status,
2001-05-23 15:26:42 +02:00
SCHAR * token, SSHORT error, SSHORT result)
{
TEXT err_string[MAX_TOKEN_SIZE + 3];
SSHORT length;
user_status[0] = gds_arg_gds;
user_status[1] = gds_sqlerr;
user_status[2] = gds_arg_number;
user_status[3] = -104;
user_status[4] = gds_arg_gds;
switch (error) {
case UNEXPECTED_END_OF_COMMAND:
user_status[5] = gds_command_end_err;
user_status[6] = gds_arg_end;
break;
case UNEXPECTED_TOKEN:
case TOKEN_TOO_LONG:
if (result) {
err_string[0] = (TEXT) result;
strcpy(err_string + 1, token);
length = strlen(token);
err_string[length + 1] = (TEXT) result;
err_string[length + 2] = '\0';
}
else
strcpy(err_string, token);
user_status[5] = gds_token_err;
user_status[6] = gds_arg_gds;
user_status[7] = gds_random;
user_status[8] = gds_arg_string;
2003-04-10 08:32:58 +02:00
user_status[9] = (ISC_STATUS) err_string;
2001-05-23 15:26:42 +02:00
user_status[10] = gds_arg_end;
UTLD_save_status_strings(user_status);
break;
}
}
/**
get_next_token
@brief
@param stmt
@param stmt_end
@param token
@param token_length
**/
2001-05-23 15:26:42 +02:00
static SSHORT get_next_token(
SCHAR ** stmt,
SCHAR * stmt_end,
SCHAR * token, USHORT * token_length)
{
SCHAR *s, *p, *token_end, *start_of_token;
UCHAR c, class_;
SSHORT length;
*token_length = 0;
s = *stmt;
for (;;) {
if (s >= stmt_end) {
*stmt = s;
return NO_MORE_TOKENS;
}
c = *s++;
if (c == '/' && s < stmt_end && *s == '*') {
s++;
while (s < stmt_end) {
c = *s++;
if (c == '*' && s < stmt_end && *s == '/')
break;
}
s++;
continue;
}
class_ = classes[c];
if (!(class_ & CHR_WHITE))
break;
}
/* At this point c contains character and class contains character class.
s is pointing to next character. */
start_of_token = s - 1;
/* 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 */
p = token;
token_end = token + MAX_TOKEN_SIZE + 1;
if (class_ & CHR_QUOTE) {
for (; p < token_end; p++) {
if (s >= stmt_end)
return UNEXPECTED_END_OF_COMMAND;
/* *s is quote - if next != quote we're at the end */
if ((*s == c) && ((++s == stmt_end) || (*s != c)))
break;
*p = *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;
}
/* Is it an integer? */
if (class_ & CHR_DIGIT) {
for (; s < stmt_end && (classes[c = *s] & CHR_DIGIT); ++s);
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;
return NUMERIC;
}
/* Is is a symbol */
if (class_ & CHR_LETTER) {
*p++ = UPPER(c);
for (; s < stmt_end && classes[*s] & CHR_IDENT && p < token_end; s++) {
*p++ = 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;
}
/* What remains at this point for us is the single character punctuation.*/
*stmt = s;
return (c == ';' ? NO_MORE_TOKENS : c);
}
/**
get_token
@brief
@param status
@param token_type
@param optional
@param stmt
@param stmt_end
@param token
@param token_length
**/
2003-09-04 23:26:15 +02: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,
2001-05-23 15:26:42 +02:00
SCHAR ** stmt,
2003-09-04 23:26:15 +02:00
SCHAR * stmt_end,
SCHAR * token,
USHORT * token_length)
2001-05-23 15:26:42 +02:00
{
SSHORT result;
SCHAR *temp_stmt;
temp_stmt = *stmt;
result = get_next_token(&temp_stmt, stmt_end, token, token_length);
switch (result) {
case NO_MORE_TOKENS:
*stmt = temp_stmt;
generate_error(status, token, UNEXPECTED_END_OF_COMMAND, 0);
return FB_FAILURE;
2001-05-23 15:26:42 +02:00
case UNEXPECTED_END_OF_COMMAND:
case TOKEN_TOO_LONG:
*stmt = temp_stmt;
/* generate error here */
generate_error(status, token, result, 0);
return FB_FAILURE;
2001-05-23 15:26:42 +02:00
}
/* Some token was found */
if (result == token_type) {
*stmt = temp_stmt;
return FB_SUCCESS;
2001-05-23 15:26:42 +02:00
}
if (optional)
return FB_SUCCESS;
2001-05-23 15:26:42 +02:00
/* generate error here and return failure; */
*stmt = temp_stmt;
generate_error(status, token, UNEXPECTED_TOKEN,
(result == STRING) ? *(temp_stmt - 1) : 0);
return FB_FAILURE;
2001-05-23 15:26:42 +02:00
}