2001-05-23 15:26:42 +02:00
|
|
|
//____________________________________________________________
|
|
|
|
//
|
|
|
|
// PROGRAM: C Preprocessor
|
|
|
|
// MODULE: sql.cpp
|
|
|
|
// DESCRIPTION: SQL 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): ______________________________________.
|
|
|
|
// TMN (Mike Nordell) 11.APR.2001 - Reduce compiler warnings
|
|
|
|
//
|
|
|
|
//
|
|
|
|
//____________________________________________________________
|
|
|
|
//
|
2003-07-04 01:02:45 +02:00
|
|
|
// $Id: sql.cpp,v 1.12 2003-07-03 23:02:45 brodsom Exp $
|
2001-05-23 15:26:42 +02:00
|
|
|
//
|
|
|
|
|
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 "../gpre/gpre.h"
|
2001-07-30 01:43:24 +02:00
|
|
|
#include "../jrd/gds.h"
|
2001-05-23 15:26:42 +02:00
|
|
|
#include "../gpre/parse.h"
|
|
|
|
#include "../jrd/intl.h"
|
|
|
|
#include "../wal/wal.h"
|
|
|
|
#include "../jrd/constants.h"
|
|
|
|
#include "../gpre/cme_proto.h"
|
|
|
|
#include "../gpre/cmp_proto.h"
|
|
|
|
#include "../gpre/exp_proto.h"
|
|
|
|
#include "../gpre/gpre_proto.h"
|
|
|
|
#include "../gpre/hsh_proto.h"
|
|
|
|
#include "../gpre/gpre_meta.h"
|
|
|
|
#include "../gpre/msc_proto.h"
|
|
|
|
#include "../gpre/par_proto.h"
|
|
|
|
#include "../gpre/sqe_proto.h"
|
|
|
|
#include "../gpre/sql_proto.h"
|
|
|
|
|
|
|
|
|
|
|
|
extern "C" {
|
|
|
|
|
|
|
|
|
|
|
|
#define ERROR_LENGTH 128
|
2003-03-09 03:25:58 +01:00
|
|
|
#ifdef FLINT_CACHE
|
2001-05-23 15:26:42 +02:00
|
|
|
#define MIN_CACHE_BUFFERS 250
|
|
|
|
#define DEF_CACHE_BUFFERS 1000
|
2003-03-09 03:25:58 +01:00
|
|
|
#endif
|
2001-05-23 15:26:42 +02:00
|
|
|
#define DEFAULT_BLOB_SEGMENT_LENGTH 80 /* bytes */
|
|
|
|
|
|
|
|
extern ACT cur_routine;
|
|
|
|
extern TEXT *database_name;
|
|
|
|
|
|
|
|
TEXT *module_lc_ctype = NULL;
|
|
|
|
|
|
|
|
static ACT act_alter(void);
|
|
|
|
static ACT act_alter_database(void);
|
|
|
|
static ACT act_alter_domain(void);
|
|
|
|
static ACT act_alter_index(void);
|
|
|
|
static ACT act_alter_table(void);
|
|
|
|
static ACT act_comment(void);
|
|
|
|
static ACT act_connect(void);
|
|
|
|
static ACT act_create(void);
|
|
|
|
static ACT act_create_database(void);
|
|
|
|
static ACT act_create_domain(void);
|
|
|
|
static ACT act_create_generator(void);
|
|
|
|
static ACT act_create_index(SSHORT, BOOLEAN);
|
|
|
|
static ACT act_create_shadow(void);
|
|
|
|
static ACT act_create_table(void);
|
|
|
|
static ACT act_create_view(void);
|
|
|
|
static ACT act_d_section(enum act_t);
|
|
|
|
static ACT act_declare(void);
|
|
|
|
static ACT act_declare_filter(void);
|
|
|
|
static ACT act_declare_table(SYM, DBB);
|
|
|
|
static ACT act_declare_udf(void);
|
|
|
|
static ACT act_delete(void);
|
|
|
|
static ACT act_describe(void);
|
|
|
|
static ACT act_disconnect(void);
|
|
|
|
static ACT act_drop(void);
|
|
|
|
static ACT act_event(void);
|
|
|
|
static ACT act_execute(void);
|
|
|
|
static ACT act_fetch(void);
|
|
|
|
static ACT act_grant_revoke(enum act_t);
|
|
|
|
static ACT act_include(void);
|
|
|
|
static ACT act_insert(void);
|
|
|
|
static ACT act_insert_blob(TEXT *);
|
|
|
|
static ACT act_lock(void);
|
|
|
|
static ACT act_openclose(enum act_t);
|
|
|
|
static ACT act_open_blob(ACT_T, SYM);
|
|
|
|
static ACT act_prepare(void);
|
|
|
|
static ACT act_procedure(void);
|
|
|
|
static ACT act_select(void);
|
|
|
|
static ACT act_set(void);
|
|
|
|
static ACT act_set_dialect(void);
|
|
|
|
static ACT act_set_generator(void);
|
|
|
|
static ACT act_set_names(void);
|
|
|
|
static ACT act_set_statistics(void);
|
|
|
|
static ACT act_set_transaction(void);
|
|
|
|
static ACT act_transaction(enum act_t);
|
|
|
|
static ACT act_update(void);
|
|
|
|
static ACT act_whenever(void);
|
|
|
|
static BOOLEAN check_filename(TEXT *);
|
|
|
|
static void connect_opts(TEXT **, TEXT **, TEXT **, TEXT **, USHORT *);
|
2003-03-03 09:26:35 +01:00
|
|
|
#ifdef FLINT_CACHE
|
2001-05-23 15:26:42 +02:00
|
|
|
static FIL define_cache(void);
|
2003-03-03 09:26:35 +01:00
|
|
|
#endif
|
2001-05-23 15:26:42 +02:00
|
|
|
static FIL define_file(void);
|
|
|
|
static FIL define_log_file(BOOLEAN);
|
|
|
|
static DBB dup_dbb(DBB);
|
|
|
|
static void error(TEXT *, TEXT *);
|
|
|
|
static TEXT *extract_string(BOOLEAN);
|
|
|
|
static SWE gen_whenever(void);
|
2002-11-17 01:04:19 +01:00
|
|
|
static void into(GPRE_REQ, GPRE_NOD, GPRE_NOD);
|
2002-11-30 18:45:02 +01:00
|
|
|
static GPRE_FLD make_field(GPRE_REL);
|
2002-11-17 01:04:19 +01:00
|
|
|
static IND make_index(GPRE_REQ, TEXT *);
|
|
|
|
static GPRE_REL make_relation(GPRE_REQ, TEXT *);
|
2002-11-11 20:19:43 +01:00
|
|
|
static void pair(GPRE_NOD, GPRE_NOD);
|
2002-11-30 18:45:02 +01:00
|
|
|
static void par_array(GPRE_FLD);
|
2001-05-23 15:26:42 +02:00
|
|
|
static SSHORT par_char_set(void);
|
2002-11-30 18:45:02 +01:00
|
|
|
static void par_computed(GPRE_REQ, GPRE_FLD);
|
2002-11-17 01:04:19 +01:00
|
|
|
static GPRE_REQ par_cursor(SYM *);
|
2001-05-23 15:26:42 +02:00
|
|
|
static DYN par_dynamic_cursor(void);
|
2002-11-30 18:45:02 +01:00
|
|
|
static GPRE_FLD par_field(GPRE_REQ, GPRE_REL);
|
|
|
|
static CNSTRT par_field_constraint(GPRE_REQ, GPRE_FLD, GPRE_REL);
|
2001-05-23 15:26:42 +02:00
|
|
|
static void par_fkey_extension(CNSTRT);
|
|
|
|
static BOOLEAN par_into(DYN);
|
|
|
|
static void par_options(TEXT **);
|
|
|
|
static int par_page_size(void);
|
2002-11-17 01:04:19 +01:00
|
|
|
static GPRE_REL par_relation(GPRE_REQ);
|
2001-05-23 15:26:42 +02:00
|
|
|
static DYN par_statement(void);
|
2002-11-17 01:04:19 +01:00
|
|
|
static CNSTRT par_table_constraint(GPRE_REQ, GPRE_REL);
|
|
|
|
static BOOLEAN par_transaction_modes(GPRE_TRA, BOOLEAN);
|
2001-05-23 15:26:42 +02:00
|
|
|
static BOOLEAN par_using(DYN);
|
|
|
|
static USHORT resolve_dtypes(KWWORDS, BOOLEAN);
|
|
|
|
static BOOLEAN tail_database(enum act_t, DBB);
|
|
|
|
static void to_upcase(TEXT *, TEXT *);
|
|
|
|
static void dialect1_bad_type(USHORT);
|
|
|
|
|
|
|
|
static SWE whenever[SWE_max], whenever_list;
|
|
|
|
|
|
|
|
#define END_OF_COMMAND \
|
|
|
|
(((sw_language != lang_cobol) && \
|
|
|
|
((int) token.tok_keyword == (int) KW_SEMI_COLON)) || \
|
|
|
|
((sw_language == lang_cobol) && \
|
|
|
|
((int) token.tok_keyword == (int) KW_END_EXEC)))
|
|
|
|
|
|
|
|
|
|
|
|
//____________________________________________________________
|
|
|
|
//
|
|
|
|
// Parse and return a sequel action.
|
|
|
|
//
|
|
|
|
|
|
|
|
ACT SQL_action(void)
|
|
|
|
{
|
|
|
|
ACT action;
|
|
|
|
enum kwwords keyword;
|
|
|
|
|
|
|
|
sw_gen_sql = TRUE;
|
|
|
|
|
|
|
|
switch (keyword = token.tok_keyword) {
|
|
|
|
case KW_ALTER:
|
|
|
|
case KW_COMMENT:
|
|
|
|
case KW_CONNECT:
|
|
|
|
case KW_CREATE:
|
|
|
|
case KW_DROP:
|
|
|
|
case KW_EVENT:
|
|
|
|
case KW_GRANT:
|
|
|
|
case KW_REVOKE:
|
|
|
|
case KW_BEGIN:
|
|
|
|
case KW_CLOSE:
|
|
|
|
case KW_COMMIT:
|
|
|
|
case KW_DECLARE:
|
|
|
|
case KW_DELETE:
|
|
|
|
case KW_DESCRIBE:
|
|
|
|
case KW_DISCONNECT:
|
|
|
|
case KW_END:
|
|
|
|
case KW_EXECUTE:
|
|
|
|
case KW_FETCH:
|
|
|
|
case KW_INCLUDE:
|
|
|
|
case KW_INSERT:
|
|
|
|
case KW_LOCK:
|
|
|
|
case KW_OPEN:
|
|
|
|
case KW_PREPARE:
|
|
|
|
case KW_ROLLBACK:
|
|
|
|
case KW_SELECT:
|
|
|
|
case KW_SET:
|
|
|
|
case KW_UPDATE:
|
|
|
|
case KW_WHENEVER:
|
|
|
|
case KW_DATABASE:
|
|
|
|
ADVANCE_TOKEN;
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
SYNTAX_ERROR("SQL operation");
|
|
|
|
}
|
|
|
|
|
|
|
|
switch (keyword) {
|
|
|
|
case KW_ALTER:
|
|
|
|
action = act_alter();
|
|
|
|
break;
|
|
|
|
|
|
|
|
case KW_BEGIN:
|
|
|
|
action = act_d_section(ACT_b_declare);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case KW_CLOSE:
|
|
|
|
action = act_openclose(ACT_close);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case KW_CONNECT:
|
|
|
|
action = act_connect();
|
|
|
|
break;
|
|
|
|
|
|
|
|
case KW_COMMENT:
|
|
|
|
action = act_comment();
|
|
|
|
break;
|
|
|
|
|
|
|
|
case KW_COMMIT:
|
|
|
|
action = act_transaction(ACT_commit);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case KW_CREATE:
|
|
|
|
action = act_create();
|
|
|
|
break;
|
|
|
|
|
|
|
|
case KW_DATABASE:
|
2003-07-04 01:02:45 +02:00
|
|
|
action = PAR_database((USHORT) TRUE, NULL);
|
2001-05-23 15:26:42 +02:00
|
|
|
break;
|
|
|
|
|
|
|
|
case KW_DROP:
|
|
|
|
action = act_drop();
|
|
|
|
break;
|
|
|
|
|
|
|
|
case KW_DECLARE:
|
|
|
|
action = act_declare();
|
|
|
|
break;
|
|
|
|
|
|
|
|
case KW_DELETE:
|
|
|
|
action = act_delete();
|
|
|
|
break;
|
|
|
|
|
|
|
|
case KW_DESCRIBE:
|
|
|
|
action = act_describe();
|
|
|
|
break;
|
|
|
|
|
|
|
|
case KW_DISCONNECT:
|
|
|
|
action = act_disconnect();
|
|
|
|
break;
|
|
|
|
|
|
|
|
case KW_EVENT:
|
|
|
|
action = act_event();
|
|
|
|
break;
|
|
|
|
|
|
|
|
case KW_END:
|
|
|
|
action = act_d_section(ACT_e_declare);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case KW_EXECUTE:
|
|
|
|
action = act_execute();
|
|
|
|
break;
|
|
|
|
|
|
|
|
case KW_FETCH:
|
|
|
|
action = act_fetch();
|
|
|
|
break;
|
|
|
|
|
|
|
|
case KW_GRANT:
|
|
|
|
action = act_grant_revoke(ACT_dyn_grant);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case KW_INCLUDE:
|
|
|
|
action = act_include();
|
|
|
|
break;
|
|
|
|
|
|
|
|
case KW_INSERT:
|
|
|
|
action = act_insert();
|
|
|
|
break;
|
|
|
|
|
|
|
|
case KW_LOCK:
|
|
|
|
action = act_lock();
|
|
|
|
break;
|
|
|
|
|
|
|
|
case KW_OPEN:
|
|
|
|
action = act_openclose(ACT_open);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case KW_PREPARE:
|
|
|
|
action = act_prepare();
|
|
|
|
break;
|
|
|
|
|
|
|
|
case KW_REVOKE:
|
|
|
|
action = act_grant_revoke(ACT_dyn_revoke);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case KW_ROLLBACK:
|
|
|
|
action = act_transaction(ACT_rollback);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case KW_SELECT:
|
|
|
|
action = act_select();
|
|
|
|
break;
|
|
|
|
|
|
|
|
case KW_SET:
|
|
|
|
action = act_set();
|
|
|
|
break;
|
|
|
|
|
|
|
|
case KW_UPDATE:
|
|
|
|
action = act_update();
|
|
|
|
break;
|
|
|
|
|
|
|
|
case KW_WHENEVER:
|
|
|
|
action = act_whenever();
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
MATCH(KW_END_EXEC);
|
|
|
|
PAR_end();
|
|
|
|
action->act_flags |= ACT_sql;
|
|
|
|
|
|
|
|
return action;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//____________________________________________________________
|
|
|
|
//
|
|
|
|
// Given a field datatype, remap it as needed to
|
|
|
|
// a user datatype, and set the length field.
|
|
|
|
//
|
|
|
|
|
2002-11-30 18:45:02 +01:00
|
|
|
void SQL_adjust_field_dtype( GPRE_FLD field)
|
2001-05-23 15:26:42 +02:00
|
|
|
{
|
|
|
|
ULONG field_length;
|
|
|
|
|
|
|
|
|
|
|
|
if (field->fld_dtype <= dtype_any_text) {
|
|
|
|
/* Adjust the string data types and their lengths */
|
|
|
|
if (field->fld_collate) {
|
|
|
|
if (field->fld_char_length)
|
|
|
|
field_length = (ULONG) field->fld_char_length *
|
|
|
|
field->fld_collate->intlsym_bytes_per_char;
|
|
|
|
else
|
|
|
|
field_length = field->fld_length;
|
|
|
|
field->fld_collate_id = field->fld_collate->intlsym_collate_id;
|
|
|
|
field->fld_charset_id = field->fld_collate->intlsym_charset_id;
|
|
|
|
field->fld_ttype = field->fld_collate->intlsym_ttype;
|
|
|
|
}
|
|
|
|
else if (field->fld_character_set) {
|
|
|
|
if (field->fld_char_length)
|
|
|
|
field_length = (ULONG) field->fld_char_length *
|
|
|
|
field->fld_character_set->intlsym_bytes_per_char;
|
|
|
|
else
|
|
|
|
field_length = field->fld_length;
|
|
|
|
field->fld_collate_id =
|
|
|
|
field->fld_character_set->intlsym_collate_id;
|
|
|
|
field->fld_charset_id =
|
|
|
|
field->fld_character_set->intlsym_charset_id;
|
|
|
|
field->fld_ttype = field->fld_character_set->intlsym_ttype;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
if (field->fld_char_length)
|
|
|
|
field_length = (ULONG) field->fld_char_length * 1;
|
|
|
|
else
|
|
|
|
field_length = field->fld_length;
|
|
|
|
field->fld_collate_id = 0;
|
|
|
|
field->fld_charset_id = 0;
|
|
|
|
field->fld_ttype = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!(field->fld_flags & FLD_meta)) { /* field for meta operation? */
|
|
|
|
/* Field isn't for meta-data operation, so adjust it's
|
|
|
|
* type definition for local use
|
|
|
|
*/
|
|
|
|
if (field->fld_dtype != dtype_cstring)
|
|
|
|
field->fld_dtype = (sw_cstring
|
|
|
|
&& !FIELD_ALLOWS_NULLS(field)) ?
|
|
|
|
dtype_cstring : dtype_text;
|
|
|
|
if (field->fld_dtype == dtype_cstring)
|
|
|
|
field_length++;
|
|
|
|
field->fld_length = (USHORT) field_length;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
field->fld_length = (USHORT) field_length;
|
|
|
|
if (field->fld_dtype == dtype_varying)
|
|
|
|
field_length += sizeof(USHORT);
|
|
|
|
|
|
|
|
if (field_length > MAX_COLUMN_SIZE)
|
|
|
|
error("Size of column %s exceeds implementation limit",
|
|
|
|
field->fld_symbol->sym_string);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
switch (field->fld_dtype) {
|
|
|
|
case dtype_short:
|
|
|
|
field->fld_length = sizeof(SSHORT);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case dtype_long:
|
|
|
|
field->fld_length = sizeof(SLONG);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case dtype_float:
|
|
|
|
field->fld_length = sizeof(float);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case dtype_double:
|
|
|
|
field->fld_length = sizeof(double);
|
|
|
|
break;
|
|
|
|
|
|
|
|
// ** Begin sql/date/time/timestamp *
|
|
|
|
case dtype_sql_date:
|
|
|
|
field->fld_length = sizeof(ISC_DATE);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case dtype_sql_time:
|
|
|
|
field->fld_length = sizeof(ISC_TIME);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case dtype_timestamp:
|
|
|
|
field->fld_length = sizeof(ISC_TIMESTAMP);
|
|
|
|
break;
|
|
|
|
// ** End sql/date/time/timestamp *
|
|
|
|
|
|
|
|
case dtype_int64:
|
|
|
|
field->fld_length = sizeof(ISC_INT64);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case dtype_blob:
|
|
|
|
field->fld_length = sizeof(GDS_QUAD);
|
|
|
|
field->fld_flags |= FLD_blob;
|
|
|
|
if (field->fld_character_set) {
|
|
|
|
field->fld_charset_id =
|
|
|
|
field->fld_character_set->intlsym_charset_id;
|
|
|
|
field->fld_ttype = field->fld_character_set->intlsym_ttype;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
CPR_bugcheck("datatype not recognized");
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//____________________________________________________________
|
|
|
|
//
|
|
|
|
// Initialize (or re-initialize) to process a module.
|
|
|
|
//
|
|
|
|
|
|
|
|
void SQL_init(void)
|
|
|
|
{
|
|
|
|
USHORT i;
|
|
|
|
|
|
|
|
whenever_list = NULL;
|
|
|
|
|
|
|
|
for (i = 0; i < SWE_max; i++)
|
|
|
|
whenever[i] = NULL;
|
|
|
|
|
|
|
|
module_lc_ctype = default_lc_ctype;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//____________________________________________________________
|
|
|
|
//
|
|
|
|
//
|
|
|
|
|
2002-11-30 18:45:02 +01:00
|
|
|
void SQL_par_field_collate( GPRE_REQ request, GPRE_FLD field)
|
2001-05-23 15:26:42 +02:00
|
|
|
{
|
|
|
|
SYM symbol;
|
|
|
|
|
|
|
|
if (MATCH(KW_COLLATE)) {
|
|
|
|
|
|
|
|
if ((field->fld_dtype != dtype_text) &&
|
|
|
|
(field->fld_dtype != dtype_cstring) &&
|
|
|
|
(field->fld_dtype != dtype_varying))
|
|
|
|
PAR_error("COLLATE applies only to character columns");
|
|
|
|
if (token.tok_type != tok_ident)
|
|
|
|
SYNTAX_ERROR("<collation name>");
|
|
|
|
if (!(symbol = MSC_find_symbol(token.tok_symbol, SYM_collate)))
|
|
|
|
PAR_error("The named COLLATION was not found");
|
|
|
|
field->fld_collate = (INTLSYM) symbol->sym_object;
|
|
|
|
|
|
|
|
/* Is the collation valid for declared character set?
|
|
|
|
* The character set is either declared (fld_character_set) or inferered
|
|
|
|
* from the global domain (fld_global & fld_charset_id)
|
|
|
|
*/
|
|
|
|
|
|
|
|
if ((field->fld_character_set &&
|
|
|
|
(field->fld_character_set->intlsym_charset_id !=
|
|
|
|
field->fld_collate->intlsym_charset_id))
|
|
|
|
|| (field->fld_global &&
|
|
|
|
(field->fld_charset_id !=
|
|
|
|
field->fld_collate->
|
|
|
|
intlsym_charset_id)))
|
|
|
|
PAR_error
|
|
|
|
("Specified COLLATION is incompatible with column CHARACTER SET");
|
|
|
|
ADVANCE_TOKEN;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//____________________________________________________________
|
|
|
|
//
|
|
|
|
// Handle an SQL field datatype definition for
|
|
|
|
// field CREATE, DECLARE or ALTER TABLE statement.
|
|
|
|
// Also for CAST statement
|
|
|
|
//
|
|
|
|
|
2002-11-30 18:45:02 +01:00
|
|
|
void SQL_par_field_dtype( GPRE_REQ request, GPRE_FLD field, BOOLEAN udf)
|
2001-05-23 15:26:42 +02:00
|
|
|
{
|
|
|
|
int l, p, q;
|
|
|
|
enum kwwords keyword;
|
|
|
|
SYM symbol;
|
|
|
|
char s[ERROR_LENGTH];
|
|
|
|
BOOLEAN sql_date = FALSE;
|
|
|
|
|
|
|
|
switch (keyword = token.tok_keyword) {
|
|
|
|
case KW_SMALLINT:
|
|
|
|
case KW_INT:
|
|
|
|
case KW_INTEGER:
|
|
|
|
case KW_FLOAT:
|
|
|
|
case KW_REAL:
|
|
|
|
case KW_DOUBLE:
|
|
|
|
case KW_LONG:
|
|
|
|
// ** Begin sql/date/time/timestamp *
|
|
|
|
case KW_TIMESTAMP:
|
|
|
|
// ** End sql/date/time/timestamp *
|
|
|
|
case KW_CHAR:
|
|
|
|
case KW_NCHAR:
|
|
|
|
case KW_VARCHAR:
|
|
|
|
case KW_DECIMAL:
|
|
|
|
case KW_NUMERIC:
|
|
|
|
case KW_BLOB:
|
|
|
|
case KW_NATIONAL:
|
|
|
|
ADVANCE_TOKEN;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case KW_DATE:
|
|
|
|
if (sw_sql_dialect == 2)
|
|
|
|
PAR_error
|
|
|
|
("DATE is ambiguous in dialect 2 use SQL DATE or TIMESTAMP");
|
|
|
|
ADVANCE_TOKEN;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case KW_TIME:
|
|
|
|
if (sw_sql_dialect == 1)
|
|
|
|
SYNTAX_ERROR("<data type>");
|
|
|
|
ADVANCE_TOKEN;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case KW_SQL:
|
|
|
|
if (sw_sql_dialect == 1)
|
|
|
|
SYNTAX_ERROR("<data type>");
|
|
|
|
ADVANCE_TOKEN;
|
|
|
|
if (token.tok_keyword == KW_DATE)
|
|
|
|
ADVANCE_TOKEN;
|
|
|
|
else
|
|
|
|
SYNTAX_ERROR("<data type>");
|
|
|
|
keyword = KW_DATE;
|
|
|
|
sql_date = TRUE;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case KW_COMPUTED:
|
|
|
|
if (udf)
|
|
|
|
SYNTAX_ERROR("<data type>");
|
|
|
|
/* just return - actual parse is done later */
|
|
|
|
return;
|
|
|
|
|
|
|
|
default:
|
|
|
|
if (udf) {
|
|
|
|
if (keyword == KW_CSTRING)
|
|
|
|
ADVANCE_TOKEN;
|
|
|
|
else
|
|
|
|
SYNTAX_ERROR("<data type>");
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
SQL_resolve_identifier("<domain name>", s);
|
|
|
|
field->fld_global = symbol =
|
2002-11-17 01:04:19 +01:00
|
|
|
MSC_symbol(SYM_field, s, (USHORT) strlen(s), (GPRE_CTX) field);
|
2001-05-23 15:26:42 +02:00
|
|
|
if (!MET_domain_lookup(request, field, s))
|
|
|
|
PAR_error("Specified DOMAIN or source column not found");
|
|
|
|
ADVANCE_TOKEN;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
switch (keyword) {
|
|
|
|
case KW_SMALLINT:
|
|
|
|
field->fld_dtype = dtype_short;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case KW_INT:
|
|
|
|
case KW_INTEGER:
|
|
|
|
field->fld_dtype = dtype_long;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case KW_REAL:
|
|
|
|
field->fld_dtype = dtype_float;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case KW_FLOAT:
|
|
|
|
if (MATCH(KW_LEFT_PAREN)) {
|
|
|
|
l = EXP_USHORT_ordinal(TRUE);
|
|
|
|
EXP_match_paren();
|
|
|
|
if (l < 17)
|
|
|
|
field->fld_dtype = dtype_float;
|
|
|
|
else
|
|
|
|
field->fld_dtype = dtype_double;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
field->fld_dtype = dtype_float;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case KW_LONG:
|
|
|
|
if (!KEYWORD(KW_FLOAT))
|
|
|
|
SYNTAX_ERROR("FLOAT");
|
|
|
|
ADVANCE_TOKEN;
|
|
|
|
field->fld_dtype = dtype_double;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case KW_DOUBLE:
|
|
|
|
if (!KEYWORD(KW_PRECISION))
|
|
|
|
SYNTAX_ERROR("PRECISION");
|
|
|
|
ADVANCE_TOKEN;
|
|
|
|
field->fld_dtype = dtype_double;
|
|
|
|
break;
|
|
|
|
|
|
|
|
// ** Begin sql/date/time/timestamp *
|
|
|
|
case KW_DATE:
|
|
|
|
case KW_TIME:
|
|
|
|
case KW_TIMESTAMP:
|
|
|
|
field->fld_dtype = resolve_dtypes(keyword, sql_date);
|
|
|
|
break;
|
|
|
|
// ** End sql/date/time/timestamp *
|
|
|
|
|
|
|
|
case KW_NCHAR:
|
|
|
|
field->fld_flags |= FLD_national;
|
|
|
|
field->fld_dtype = dtype_text;
|
|
|
|
if (MATCH(KW_LEFT_PAREN)) {
|
|
|
|
field->fld_char_length = EXP_pos_USHORT_ordinal(TRUE);
|
|
|
|
EXP_match_paren();
|
|
|
|
}
|
|
|
|
else
|
|
|
|
field->fld_char_length = 1;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case KW_NATIONAL:
|
|
|
|
if (!KEYWORD(KW_CHAR))
|
|
|
|
SYNTAX_ERROR("CHARACTER");
|
|
|
|
ADVANCE_TOKEN;
|
|
|
|
field->fld_flags |= FLD_national;
|
|
|
|
/* Fall into KW_CHAR */
|
|
|
|
case KW_CHAR:
|
|
|
|
if (MATCH(KW_VARYING)) {
|
|
|
|
field->fld_dtype = dtype_varying;
|
|
|
|
EXP_left_paren(0);
|
|
|
|
field->fld_char_length = EXP_pos_USHORT_ordinal(TRUE);
|
|
|
|
EXP_match_paren();
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case KW_CSTRING:
|
|
|
|
if (keyword == KW_CSTRING) {
|
|
|
|
field->fld_dtype = dtype_cstring;
|
|
|
|
field->fld_flags |= FLD_meta_cstring;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
field->fld_dtype = dtype_text;
|
|
|
|
if (MATCH(KW_LEFT_PAREN)) {
|
|
|
|
field->fld_char_length = EXP_pos_USHORT_ordinal(TRUE);
|
|
|
|
EXP_match_paren();
|
|
|
|
}
|
|
|
|
else
|
|
|
|
field->fld_char_length = 1;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case KW_VARCHAR:
|
|
|
|
field->fld_dtype = dtype_varying;
|
|
|
|
EXP_left_paren(0);
|
|
|
|
field->fld_char_length = EXP_pos_USHORT_ordinal(TRUE);
|
|
|
|
EXP_match_paren();
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
|
|
case KW_NUMERIC:
|
|
|
|
case KW_DECIMAL:
|
|
|
|
field->fld_dtype = dtype_long;
|
|
|
|
field->fld_scale = 0;
|
|
|
|
field->fld_precision = 9;
|
|
|
|
field->fld_sub_type = (keyword == KW_NUMERIC) ? 1 : 2;
|
|
|
|
if (MATCH(KW_LEFT_PAREN)) {
|
|
|
|
p = EXP_USHORT_ordinal(TRUE);
|
|
|
|
if ((p <= 0) || (p > 18))
|
|
|
|
PAR_error("Precision must be from 1 to 18");
|
|
|
|
|
|
|
|
if ((keyword == KW_NUMERIC) && (p < 5))
|
|
|
|
field->fld_dtype = dtype_short;
|
|
|
|
else if (p > 9) {
|
|
|
|
if (sw_sql_dialect == SQL_DIALECT_V5)
|
|
|
|
field->fld_dtype = dtype_double;
|
|
|
|
else
|
|
|
|
field->fld_dtype = dtype_int64;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (MATCH(KW_COMMA)) {
|
|
|
|
q = EXP_USHORT_ordinal(TRUE);
|
|
|
|
|
|
|
|
if (q > p)
|
|
|
|
PAR_error("Scale can not be greater than precision");
|
|
|
|
field->fld_scale = -q;
|
|
|
|
}
|
|
|
|
field->fld_precision = p;
|
|
|
|
EXP_match_paren();
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case KW_BLOB:
|
|
|
|
field->fld_dtype = dtype_blob;
|
|
|
|
field->fld_seg_length = DEFAULT_BLOB_SEGMENT_LENGTH;
|
|
|
|
if (MATCH(KW_LEFT_PAREN)) {
|
|
|
|
if (MATCH(KW_COMMA))
|
|
|
|
field->fld_sub_type = EXP_SSHORT_ordinal(TRUE);
|
|
|
|
else {
|
|
|
|
field->fld_seg_length = EXP_USHORT_ordinal(TRUE);
|
|
|
|
if (MATCH(KW_COMMA))
|
|
|
|
field->fld_sub_type = EXP_SSHORT_ordinal(TRUE);
|
|
|
|
}
|
|
|
|
EXP_match_paren();
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
if (MATCH(KW_SUB_TYPE)) {
|
|
|
|
field->fld_sub_type = PAR_blob_subtype(request->req_database);
|
|
|
|
}
|
|
|
|
if (MATCH(KW_SEGMENT)) {
|
|
|
|
MATCH(KW_SIZE);
|
|
|
|
field->fld_seg_length = EXP_USHORT_ordinal(TRUE);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
SYNTAX_ERROR("<data type>");
|
|
|
|
}
|
|
|
|
|
|
|
|
// Check for array declaration
|
|
|
|
|
|
|
|
if ((keyword != KW_BLOB) && !udf && (MATCH(KW_L_BRCKET))) {
|
|
|
|
field->fld_array_info = (struct ary *) ALLOC(sizeof(struct ary));
|
|
|
|
par_array(field);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (MATCH(KW_CHAR)) {
|
|
|
|
SYM symbol;
|
|
|
|
if ((field->fld_dtype != dtype_text) &&
|
|
|
|
(field->fld_dtype != dtype_cstring) &&
|
|
|
|
(field->fld_dtype != dtype_varying) &&
|
|
|
|
(field->fld_dtype != dtype_blob))
|
|
|
|
PAR_error("CHARACTER SET applies only to character columns");
|
|
|
|
|
|
|
|
if (field->fld_dtype == dtype_blob
|
|
|
|
&& field->fld_sub_type == BLOB_untyped) field->fld_sub_type =
|
|
|
|
BLOB_text;
|
|
|
|
|
|
|
|
if (field->fld_dtype == dtype_blob
|
|
|
|
&& field->fld_sub_type !=
|
|
|
|
BLOB_text)
|
|
|
|
PAR_error("CHARACTER SET applies only to character columns");
|
|
|
|
|
|
|
|
if (field->fld_flags & FLD_national)
|
|
|
|
PAR_error("cannot specify CHARACTER SET with NATIONAL");
|
|
|
|
|
|
|
|
if (!MATCH(KW_SET))
|
|
|
|
SYNTAX_ERROR("SET");
|
|
|
|
if (token.tok_type != tok_ident)
|
|
|
|
SYNTAX_ERROR("<character set name>");
|
|
|
|
if (!(symbol = MSC_find_symbol(token.tok_symbol, SYM_charset)))
|
|
|
|
PAR_error("The named CHARACTER SET was not found");
|
|
|
|
field->fld_character_set = (INTLSYM) symbol->sym_object;
|
|
|
|
ADVANCE_TOKEN;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (field->fld_flags & FLD_national) {
|
|
|
|
if (!
|
|
|
|
(symbol =
|
|
|
|
MSC_find_symbol(HSH_lookup(DEFAULT_CHARACTER_SET_NAME),
|
|
|
|
SYM_charset)))
|
|
|
|
PAR_error("NATIONAL character set missing");
|
|
|
|
field->fld_character_set = (INTLSYM) symbol->sym_object;
|
|
|
|
}
|
|
|
|
else if ((field->fld_dtype <= dtype_any_text ||
|
|
|
|
(field->fld_dtype == dtype_blob
|
|
|
|
&& field->fld_sub_type == BLOB_text))
|
|
|
|
&& !field->fld_character_set && !field->fld_collate && request
|
|
|
|
&& request->req_database
|
|
|
|
&& request->req_database->dbb_def_charset) {
|
|
|
|
/* Use database default character set */
|
|
|
|
if (symbol =
|
|
|
|
MSC_find_symbol(HSH_lookup
|
|
|
|
(request->req_database->dbb_def_charset),
|
|
|
|
SYM_charset)) field->fld_character_set =
|
|
|
|
(INTLSYM) symbol->sym_object;
|
|
|
|
else
|
|
|
|
PAR_error("Could not find database default character set");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//____________________________________________________________
|
|
|
|
//
|
|
|
|
// Find procedure for request. If request already has a database,
|
|
|
|
// find the procedure in that database only.
|
|
|
|
//
|
|
|
|
|
2002-11-17 01:04:19 +01:00
|
|
|
GPRE_PRC SQL_procedure(GPRE_REQ request,
|
2001-05-23 15:26:42 +02:00
|
|
|
TEXT * prc_string,
|
|
|
|
TEXT * db_string, TEXT * owner_string, BOOLEAN err_flag)
|
|
|
|
{
|
|
|
|
DBB db;
|
2002-11-17 01:04:19 +01:00
|
|
|
GPRE_PRC procedure, tmp_procedure;
|
2001-05-23 15:26:42 +02:00
|
|
|
SYM symbol;
|
|
|
|
SCHAR s[ERROR_LENGTH];
|
|
|
|
|
|
|
|
procedure = NULL;
|
|
|
|
|
|
|
|
if (db_string && db_string[0]) {
|
|
|
|
/* a database was specified for the procedure
|
|
|
|
search the known symbols for the database name */
|
|
|
|
|
|
|
|
if (!(symbol = MSC_find_symbol(HSH_lookup(db_string), SYM_database)))
|
|
|
|
PAR_error("Unknown database specifier.");
|
|
|
|
if (request->req_database) {
|
|
|
|
if ((DBB) symbol->sym_object != request->req_database)
|
|
|
|
PAR_error("Inconsistent database specifier");
|
|
|
|
}
|
|
|
|
else
|
|
|
|
request->req_database = (DBB) symbol->sym_object;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (request->req_database)
|
|
|
|
procedure =
|
|
|
|
MET_get_procedure(request->req_database, prc_string,
|
|
|
|
owner_string);
|
|
|
|
else {
|
|
|
|
/* no database was specified, check the metadata for all the databases
|
|
|
|
for the existence of the procedure */
|
|
|
|
|
|
|
|
procedure = NULL;
|
|
|
|
for (db = isc_databases; db; db = db->dbb_next)
|
|
|
|
if (tmp_procedure =
|
|
|
|
MET_get_procedure(db, prc_string,
|
|
|
|
owner_string)) if (procedure) {
|
|
|
|
/* relation was found in more than one database */
|
|
|
|
|
|
|
|
sprintf(s, "PROCEDURE %s is ambiguous", prc_string);
|
|
|
|
PAR_error(s);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
procedure = tmp_procedure;
|
|
|
|
request->req_database = db;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!procedure) {
|
|
|
|
if (!err_flag)
|
|
|
|
return NULL;
|
|
|
|
if (owner_string[0])
|
|
|
|
sprintf(s, "PROCEDURE %s.%s not defined", owner_string,
|
|
|
|
prc_string);
|
|
|
|
else
|
|
|
|
sprintf(s, "PROCEDURE %s not defined", prc_string);
|
|
|
|
PAR_error(s);
|
|
|
|
}
|
|
|
|
|
|
|
|
return procedure;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//____________________________________________________________
|
|
|
|
//
|
|
|
|
// Find relation for request. If request already has a database,
|
|
|
|
// find the relation in that database only.
|
|
|
|
//
|
|
|
|
|
2002-11-17 01:04:19 +01:00
|
|
|
GPRE_REL SQL_relation(GPRE_REQ request,
|
2001-05-23 15:26:42 +02:00
|
|
|
TEXT * rel_string,
|
|
|
|
TEXT * db_string, TEXT * owner_string, BOOLEAN err_flag)
|
|
|
|
{
|
|
|
|
DBB db;
|
2002-11-17 01:04:19 +01:00
|
|
|
GPRE_REL relation, tmp_relation;
|
2001-05-23 15:26:42 +02:00
|
|
|
SYM symbol;
|
|
|
|
SCHAR s[ERROR_LENGTH];
|
|
|
|
|
|
|
|
relation = NULL;
|
|
|
|
|
|
|
|
if (db_string && db_string[0]) {
|
|
|
|
/* a database was specified for the relation,
|
|
|
|
search the known symbols for the database name */
|
|
|
|
|
|
|
|
if (!(symbol = MSC_find_symbol(HSH_lookup(db_string), SYM_database)))
|
|
|
|
PAR_error("Unknown database specifier.");
|
|
|
|
if (request->req_database) {
|
|
|
|
if ((DBB) symbol->sym_object != request->req_database)
|
|
|
|
PAR_error("Inconsistent database specifier");
|
|
|
|
}
|
|
|
|
else
|
|
|
|
request->req_database = (DBB) symbol->sym_object;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (request->req_database)
|
|
|
|
relation =
|
|
|
|
MET_get_relation(request->req_database, rel_string, owner_string);
|
|
|
|
else {
|
|
|
|
/* no database was specified, check the metadata for all the databases
|
|
|
|
for the existence of the relation */
|
|
|
|
|
|
|
|
relation = NULL;
|
|
|
|
for (db = isc_databases; db; db = db->dbb_next)
|
|
|
|
if (tmp_relation = MET_get_relation(db, rel_string, owner_string))
|
|
|
|
if (relation) {
|
|
|
|
/* relation was found in more than one database */
|
|
|
|
|
|
|
|
sprintf(s, "TABLE %s is ambiguous", rel_string);
|
|
|
|
PAR_error(s);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
relation = tmp_relation;
|
|
|
|
request->req_database = db;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!relation) {
|
|
|
|
if (!err_flag)
|
|
|
|
return (NULL);
|
|
|
|
if (owner_string[0])
|
|
|
|
sprintf(s, "TABLE %s.%s not defined", owner_string, rel_string);
|
|
|
|
else
|
|
|
|
sprintf(s, "TABLE %s not defined", rel_string);
|
|
|
|
PAR_error(s);
|
|
|
|
}
|
|
|
|
|
|
|
|
return relation;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//____________________________________________________________
|
|
|
|
//
|
|
|
|
// Get a relation name (checking for database specifier)
|
|
|
|
//
|
|
|
|
|
|
|
|
void SQL_relation_name( TEXT * r_name, TEXT * db_name, TEXT * owner_name)
|
|
|
|
{
|
|
|
|
SYM symbol;
|
|
|
|
TEXT *t_str;
|
|
|
|
|
|
|
|
db_name[0] = 0;
|
|
|
|
owner_name[0] = 0;
|
|
|
|
|
|
|
|
t_str = (TEXT *) ALLOC(NAME_SIZE + 1);
|
|
|
|
SQL_resolve_identifier("<Table name>", t_str);
|
|
|
|
|
|
|
|
if (symbol = MSC_find_symbol(token.tok_symbol, SYM_database)) {
|
|
|
|
strcpy(db_name, symbol->sym_name);
|
|
|
|
ADVANCE_TOKEN;
|
|
|
|
if (!MATCH(KW_DOT))
|
|
|
|
SYNTAX_ERROR(". (period)");
|
|
|
|
}
|
|
|
|
|
|
|
|
SQL_resolve_identifier("<Table name>", t_str);
|
|
|
|
if (token.tok_length > NAME_SIZE)
|
|
|
|
PAR_error("Table, owner, or database name too long");
|
|
|
|
|
|
|
|
strcpy(r_name, token.tok_string);
|
|
|
|
ADVANCE_TOKEN;
|
|
|
|
|
|
|
|
if (MATCH(KW_DOT)) {
|
|
|
|
/* the table name was really a owner specifier */
|
|
|
|
|
|
|
|
if (token.tok_length > NAME_SIZE)
|
|
|
|
PAR_error("TABLE name too long");
|
|
|
|
strcpy(owner_name, r_name);
|
|
|
|
SQL_resolve_identifier("<Table name>", t_str);
|
|
|
|
strcpy(r_name, token.tok_string);
|
|
|
|
ADVANCE_TOKEN;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//____________________________________________________________
|
|
|
|
//
|
|
|
|
// Extract SQL va
|
|
|
|
//
|
|
|
|
|
|
|
|
TEXT *SQL_var_or_string(BOOLEAN string_only)
|
|
|
|
{
|
|
|
|
|
|
|
|
if ((!SINGLE_QUOTED(token.tok_type) && sw_sql_dialect == 3) ||
|
|
|
|
(!QUOTED(token.tok_type) && sw_sql_dialect == 1)) {
|
|
|
|
if (string_only)
|
|
|
|
SYNTAX_ERROR("<quoted string>");
|
|
|
|
if (!MATCH(KW_COLON))
|
|
|
|
SYNTAX_ERROR("<colon> or <quoted string>");
|
|
|
|
}
|
|
|
|
return PAR_native_value(FALSE, FALSE);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//____________________________________________________________
|
|
|
|
//
|
|
|
|
// Handle an SQL alter statement.
|
|
|
|
//
|
|
|
|
|
|
|
|
static ACT act_alter(void)
|
|
|
|
{
|
|
|
|
|
|
|
|
switch (token.tok_keyword) {
|
|
|
|
|
|
|
|
case KW_DATABASE:
|
|
|
|
case KW_SCHEMA:
|
|
|
|
return act_alter_database();
|
|
|
|
|
|
|
|
case KW_DOMAIN:
|
|
|
|
return act_alter_domain();
|
|
|
|
|
|
|
|
case KW_INDEX:
|
|
|
|
ADVANCE_TOKEN;
|
|
|
|
return act_alter_index();
|
|
|
|
|
|
|
|
case KW_STOGROUP:
|
|
|
|
PAR_error("ALTER STOGROUP not supported");
|
|
|
|
|
|
|
|
case KW_TABLE:
|
|
|
|
ADVANCE_TOKEN;
|
|
|
|
return act_alter_table();
|
|
|
|
|
|
|
|
case KW_TABLESPACE:
|
|
|
|
PAR_error("ALTER TABLESPACE not supported");
|
|
|
|
|
|
|
|
default:
|
|
|
|
PAR_error("Invalid ALTER request");
|
|
|
|
}
|
|
|
|
return NULL; /* silence compiler */
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//____________________________________________________________
|
|
|
|
//
|
|
|
|
// Handle an SQL alter database statement
|
|
|
|
//
|
|
|
|
|
|
|
|
static ACT act_alter_database(void)
|
|
|
|
{
|
|
|
|
ACT action;
|
2002-11-17 01:04:19 +01:00
|
|
|
GPRE_REQ request;
|
2001-05-23 15:26:42 +02:00
|
|
|
DBB database;
|
|
|
|
FIL file;
|
|
|
|
SSHORT logdefined;
|
|
|
|
|
|
|
|
request = MAKE_REQUEST(REQ_ddl);
|
|
|
|
if (isc_databases && !isc_databases->dbb_next)
|
|
|
|
request->req_database = isc_databases;
|
|
|
|
else
|
|
|
|
PAR_error("Can only alter database in context of single database");
|
|
|
|
|
|
|
|
ADVANCE_TOKEN;
|
|
|
|
|
|
|
|
// create action block
|
|
|
|
|
|
|
|
action = MAKE_ACTION(request, ACT_alter_database);
|
|
|
|
action->act_whenever = gen_whenever();
|
|
|
|
database = (DBB) ALLOC(DBB_LEN);
|
|
|
|
database->dbb_grp_cmt_wait = -1;
|
|
|
|
action->act_object = (REF) database;
|
|
|
|
|
|
|
|
while (TRUE) {
|
|
|
|
if (MATCH(KW_DROP)) {
|
|
|
|
if (MATCH(KW_LOG_FILE))
|
|
|
|
database->dbb_flags |= DBB_drop_log;
|
|
|
|
else if (MATCH(KW_CASCADE))
|
|
|
|
database->dbb_flags |= DBB_cascade;
|
|
|
|
#ifdef FLINT_CACHE
|
|
|
|
else if (MATCH(KW_CACHE))
|
|
|
|
database->dbb_flags |= DBB_drop_cache;
|
|
|
|
else
|
|
|
|
PAR_error("only log or cache can be dropped");
|
|
|
|
#else
|
|
|
|
else
|
|
|
|
PAR_error("only log file can be dropped");
|
|
|
|
#endif /* FLINT_CACHE */
|
|
|
|
}
|
|
|
|
else if (MATCH(KW_ADD)) {
|
|
|
|
if (MATCH(KW_FILE)) {
|
|
|
|
do {
|
|
|
|
file = define_file();
|
|
|
|
file->fil_next = database->dbb_files;
|
|
|
|
database->dbb_files = file;
|
|
|
|
}
|
|
|
|
while (MATCH(KW_FILE));
|
|
|
|
}
|
|
|
|
else if (MATCH(KW_LOG_FILE)) {
|
|
|
|
if (logdefined)
|
|
|
|
PAR_error("log redefinition");
|
|
|
|
logdefined = TRUE;
|
|
|
|
if (MATCH(KW_LEFT_PAREN)) {
|
|
|
|
while (TRUE) {
|
|
|
|
file = define_log_file(FALSE);
|
|
|
|
file->fil_next = database->dbb_logfiles;
|
|
|
|
database->dbb_logfiles = file;
|
|
|
|
if (!MATCH(KW_COMMA)) {
|
|
|
|
EXP_match_paren();
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (MATCH(KW_OVERFLOW))
|
|
|
|
database->dbb_overflow = define_log_file(TRUE);
|
|
|
|
else
|
|
|
|
PAR_error
|
|
|
|
("Overflow log specification required for this configuration");
|
|
|
|
|
|
|
|
}
|
|
|
|
else if (MATCH(KW_BASE_NAME)) {
|
|
|
|
database->dbb_flags |= DBB_log_serial;
|
|
|
|
database->dbb_logfiles = file = define_log_file(TRUE);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
database->dbb_flags |= DBB_log_default;
|
|
|
|
}
|
|
|
|
#ifdef FLINT_CACHE
|
|
|
|
else if (MATCH(KW_CACHE))
|
|
|
|
database->dbb_cache_file = define_cache();
|
|
|
|
#endif /* FLINT_CACHE */
|
|
|
|
}
|
|
|
|
else if (MATCH(KW_SET)) {
|
|
|
|
while (TRUE) {
|
|
|
|
if (MATCH(KW_CHECK_POINT_LEN)) {
|
|
|
|
MATCH(KW_EQUALS);
|
|
|
|
database->dbb_chkptlen = EXP_ULONG_ordinal(TRUE);
|
|
|
|
MATCH(KW_COMMA);
|
|
|
|
}
|
|
|
|
else if (MATCH(KW_NUM_LOG_BUFS)) {
|
|
|
|
MATCH(KW_EQUALS);
|
|
|
|
database->dbb_numbufs = EXP_USHORT_ordinal(TRUE);
|
|
|
|
MATCH(KW_COMMA);
|
|
|
|
}
|
|
|
|
else if (MATCH(KW_LOG_BUF_SIZE)) {
|
|
|
|
MATCH(KW_EQUALS);
|
|
|
|
database->dbb_bufsize = EXP_USHORT_ordinal(TRUE);
|
|
|
|
MATCH(KW_COMMA);
|
|
|
|
}
|
|
|
|
else if (MATCH(KW_GROUP_COMMIT_WAIT)) {
|
|
|
|
MATCH(KW_EQUALS);
|
|
|
|
database->dbb_grp_cmt_wait = EXP_ULONG_ordinal(TRUE);
|
|
|
|
MATCH(KW_COMMA);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
return action;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//____________________________________________________________
|
|
|
|
//
|
|
|
|
// Handle altering of a domain (global field).
|
|
|
|
//
|
|
|
|
|
|
|
|
static ACT act_alter_domain(void)
|
|
|
|
{
|
|
|
|
ACT action;
|
2002-11-17 01:04:19 +01:00
|
|
|
GPRE_REQ request;
|
2002-11-30 18:45:02 +01:00
|
|
|
GPRE_FLD field;
|
2001-05-23 15:26:42 +02:00
|
|
|
CNSTRT *cnstrt_ptr, cnstrt;
|
2002-11-11 20:19:43 +01:00
|
|
|
GPRE_NOD literal_node;
|
2001-05-23 15:26:42 +02:00
|
|
|
|
|
|
|
// create request block
|
|
|
|
|
|
|
|
request = MAKE_REQUEST(REQ_ddl);
|
|
|
|
if (isc_databases && !isc_databases->dbb_next)
|
|
|
|
request->req_database = isc_databases;
|
|
|
|
else
|
|
|
|
PAR_error("Can only ALTER a domain in context of single database");
|
|
|
|
|
|
|
|
// create action block
|
|
|
|
|
|
|
|
action = MAKE_ACTION(request, ACT_alter_domain);
|
|
|
|
|
|
|
|
ADVANCE_TOKEN;
|
|
|
|
field = make_field(0);
|
|
|
|
cnstrt_ptr = &field->fld_constraints;
|
|
|
|
|
|
|
|
// Check if default value was specified
|
|
|
|
|
|
|
|
while (!END_OF_COMMAND) {
|
|
|
|
if (MATCH(KW_SET)) {
|
|
|
|
if (token.tok_keyword == KW_DEFAULT) {
|
|
|
|
field->fld_default_source = CPR_start_text();
|
|
|
|
ADVANCE_TOKEN;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
SYNTAX_ERROR("DEFAULT");
|
|
|
|
|
|
|
|
if (MATCH(KW_USER))
|
|
|
|
field->fld_default_value = MAKE_NODE(nod_user_name, 0);
|
|
|
|
else if (MATCH(KW_NULL))
|
|
|
|
field->fld_default_value = MAKE_NODE(nod_null, 0);
|
|
|
|
else {
|
|
|
|
if (MATCH(KW_MINUS)) {
|
|
|
|
if (token.tok_type != tok_number)
|
|
|
|
SYNTAX_ERROR("<number>");
|
|
|
|
|
|
|
|
literal_node = EXP_literal();
|
|
|
|
field->fld_default_value = MSC_unary(nod_negate,
|
|
|
|
literal_node);
|
|
|
|
}
|
|
|
|
else if ((field->fld_default_value = EXP_literal()) == NULL)
|
|
|
|
SYNTAX_ERROR("<constant>");
|
|
|
|
}
|
|
|
|
CPR_end_text(field->fld_default_source);
|
|
|
|
}
|
|
|
|
else if (MATCH(KW_ADD)) {
|
|
|
|
MATCH(KW_CONSTRAINT);
|
|
|
|
if (token.tok_keyword == KW_CHECK) {
|
|
|
|
cnstrt = par_field_constraint(request, field, 0);
|
|
|
|
*cnstrt_ptr = cnstrt;
|
|
|
|
cnstrt_ptr = &cnstrt->cnstrt_next;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
PAR_error("Invalid constraint.");
|
|
|
|
}
|
|
|
|
else if (MATCH(KW_DROP)) {
|
|
|
|
if (MATCH(KW_CONSTRAINT)) {
|
|
|
|
cnstrt = (CNSTRT) ALLOC(CNSTRT_LEN);
|
|
|
|
cnstrt->cnstrt_flags |= CNSTRT_delete;
|
|
|
|
*cnstrt_ptr = cnstrt;
|
|
|
|
cnstrt_ptr = &cnstrt->cnstrt_next;
|
|
|
|
}
|
|
|
|
else if (MATCH(KW_DEFAULT)) {
|
|
|
|
field->fld_default_value = MAKE_NODE(nod_erase, 0);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
PAR_error("Invalid attribute for DROP");
|
|
|
|
}
|
|
|
|
else
|
|
|
|
SYNTAX_ERROR("SET, ADD, or DROP");
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
action->act_whenever = gen_whenever();
|
|
|
|
action->act_object = (REF) field;
|
|
|
|
|
|
|
|
return action;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//____________________________________________________________
|
|
|
|
//
|
|
|
|
// Handle an SQL alter index statement.
|
|
|
|
//
|
|
|
|
|
|
|
|
static ACT act_alter_index(void)
|
|
|
|
{
|
|
|
|
ACT action;
|
2002-11-17 01:04:19 +01:00
|
|
|
GPRE_REQ request;
|
2001-05-23 15:26:42 +02:00
|
|
|
IND index;
|
|
|
|
SCHAR i_name[NAME_SIZE + 1];
|
|
|
|
|
|
|
|
// create request block
|
|
|
|
|
|
|
|
request = MAKE_REQUEST(REQ_ddl);
|
|
|
|
|
|
|
|
if (token.tok_length > NAME_SIZE)
|
|
|
|
PAR_error("Index name too long");
|
|
|
|
|
|
|
|
SQL_resolve_identifier("<column name>", i_name);
|
|
|
|
|
|
|
|
ADVANCE_TOKEN;
|
|
|
|
|
|
|
|
index = make_index(request, i_name);
|
|
|
|
|
|
|
|
if (MATCH(KW_ACTIVE))
|
|
|
|
index->ind_flags |= IND_active;
|
|
|
|
else if (MATCH(KW_INACTIVE))
|
|
|
|
index->ind_flags |= IND_inactive;
|
|
|
|
else
|
|
|
|
PAR_error("Unsupported ALTER INDEX option");
|
|
|
|
|
|
|
|
action = MAKE_ACTION(request, ACT_alter_index);
|
|
|
|
action->act_object = (REF) index;
|
|
|
|
action->act_whenever = gen_whenever();
|
|
|
|
|
|
|
|
return action;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//____________________________________________________________
|
|
|
|
//
|
|
|
|
// Handle an SQL alter table statement.
|
|
|
|
//
|
|
|
|
|
|
|
|
static ACT act_alter_table(void)
|
|
|
|
{
|
|
|
|
ACT action;
|
2002-11-30 18:45:02 +01:00
|
|
|
GPRE_FLD field, *ptr;
|
2002-11-17 01:04:19 +01:00
|
|
|
GPRE_REQ request;
|
|
|
|
GPRE_REL relation;
|
2001-05-23 15:26:42 +02:00
|
|
|
CNSTRT cnstrt, *cnstrt_ptr;
|
2002-11-17 01:04:19 +01:00
|
|
|
GPRE_CTX context;
|
2001-05-23 15:26:42 +02:00
|
|
|
|
|
|
|
// create request block
|
|
|
|
|
|
|
|
request = MAKE_REQUEST(REQ_ddl);
|
|
|
|
|
|
|
|
// get table name and create relation block
|
|
|
|
|
|
|
|
relation = par_relation(request);
|
|
|
|
|
|
|
|
// CHECK Constraints require the context to be set to the
|
|
|
|
// current relation
|
|
|
|
|
|
|
|
request->req_contexts = context = MAKE_CONTEXT(request);
|
|
|
|
context->ctx_relation = relation;
|
|
|
|
|
|
|
|
// Reserve context 1 for relation on which constraint is
|
|
|
|
// being defined
|
|
|
|
|
|
|
|
context->ctx_internal++;
|
|
|
|
request->req_internal++;
|
|
|
|
|
|
|
|
// create action block
|
|
|
|
|
|
|
|
action = MAKE_ACTION(request, ACT_alter_table);
|
|
|
|
action->act_whenever = gen_whenever();
|
|
|
|
action->act_object = (REF) relation;
|
|
|
|
|
|
|
|
// parse action list and create corresponding field blocks
|
|
|
|
|
|
|
|
ptr = &relation->rel_fields;
|
|
|
|
cnstrt_ptr = &relation->rel_constraints;
|
|
|
|
|
|
|
|
while (!END_OF_COMMAND) {
|
|
|
|
if (MATCH(KW_ADD)) {
|
|
|
|
switch (token.tok_keyword) {
|
|
|
|
case KW_CONSTRAINT:
|
|
|
|
case KW_PRIMARY:
|
|
|
|
case KW_UNIQUE:
|
|
|
|
case KW_FOREIGN:
|
|
|
|
case KW_CHECK:
|
|
|
|
cnstrt = par_table_constraint(request, relation);
|
|
|
|
*cnstrt_ptr = cnstrt;
|
|
|
|
cnstrt_ptr = &cnstrt->cnstrt_next;
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
field = par_field(request, relation);
|
|
|
|
*ptr = field;
|
|
|
|
ptr = &field->fld_next;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (MATCH(KW_DROP)) {
|
|
|
|
if (token.tok_keyword == KW_CONSTRAINT) {
|
|
|
|
ADVANCE_TOKEN;
|
|
|
|
cnstrt = (CNSTRT) ALLOC(CNSTRT_LEN);
|
|
|
|
cnstrt->cnstrt_flags |= CNSTRT_delete;
|
|
|
|
cnstrt->cnstrt_name = (STR) ALLOC(NAME_SIZE + 1);
|
|
|
|
SQL_resolve_identifier("<constraint name>",
|
|
|
|
(TEXT *) cnstrt->cnstrt_name);
|
|
|
|
if (token.tok_length > NAME_SIZE)
|
|
|
|
PAR_error("Constraint name too long");
|
|
|
|
*cnstrt_ptr = cnstrt;
|
|
|
|
cnstrt_ptr = &cnstrt->cnstrt_next;
|
|
|
|
ADVANCE_TOKEN;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
field = make_field(relation);
|
|
|
|
field->fld_flags |= FLD_delete;
|
|
|
|
*ptr = field;
|
|
|
|
ptr = &field->fld_next;
|
|
|
|
/* Fix for bug 8054:
|
|
|
|
|
|
|
|
[CASCADE | RESTRICT] syntax is available in IB4.5, but not
|
|
|
|
required until v5.0.
|
|
|
|
|
|
|
|
Option CASCADE causes an error :
|
|
|
|
unsupported construct
|
|
|
|
|
|
|
|
Option RESTRICT is default behaviour.
|
|
|
|
*/
|
|
|
|
if (token.tok_keyword == KW_CASCADE) {
|
|
|
|
PAR_error("Unsupported construct CASCADE");
|
|
|
|
ADVANCE_TOKEN;
|
|
|
|
}
|
|
|
|
else if (token.tok_keyword == KW_RESTRICT) {
|
|
|
|
ADVANCE_TOKEN;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
SYNTAX_ERROR("ADD or DROP");
|
|
|
|
if (!MATCH(KW_COMMA))
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
return action;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//____________________________________________________________
|
|
|
|
//
|
|
|
|
// Handle an SQL comment statement.
|
|
|
|
// Reject
|
|
|
|
//
|
|
|
|
|
|
|
|
static ACT act_comment(void)
|
|
|
|
{
|
|
|
|
|
|
|
|
PAR_error("SQL COMMENT ON request not allowed");
|
|
|
|
return NULL; /* silence compiler */
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//____________________________________________________________
|
|
|
|
//
|
|
|
|
// Parse a CONNECT statement.
|
|
|
|
//
|
|
|
|
|
|
|
|
static ACT act_connect(void)
|
|
|
|
{
|
2003-02-10 14:28:35 +01:00
|
|
|
ACT action;
|
|
|
|
GPRE_REQ request;
|
2001-05-23 15:26:42 +02:00
|
|
|
SYM symbol;
|
|
|
|
RDY ready;
|
|
|
|
DBB dbb;
|
|
|
|
BOOLEAN need_handle;
|
|
|
|
USHORT default_buffers, buffers = 0;
|
|
|
|
TEXT *user = NULL, *password = NULL, *sql_role = NULL;
|
|
|
|
TEXT *lc_messages = NULL;
|
|
|
|
|
|
|
|
action = MAKE_ACTION(0, ACT_ready);
|
|
|
|
action->act_whenever = gen_whenever();
|
|
|
|
need_handle = FALSE;
|
|
|
|
default_buffers = 0;
|
|
|
|
|
|
|
|
MATCH(KW_TO);
|
|
|
|
|
|
|
|
if (!MATCH(KW_ALL) && !MATCH(KW_DEFAULT))
|
|
|
|
while (TRUE) {
|
|
|
|
ready = (RDY) ALLOC(RDY_LEN);
|
|
|
|
ready->rdy_next = (RDY) action->act_object;
|
|
|
|
action->act_object = (REF) ready;
|
|
|
|
|
|
|
|
if (!(symbol = token.tok_symbol)
|
|
|
|
|| symbol->sym_type != SYM_database) {
|
|
|
|
ready->rdy_filename = SQL_var_or_string(FALSE);
|
|
|
|
if (MATCH(KW_AS))
|
|
|
|
need_handle = TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!(symbol = token.tok_symbol)
|
|
|
|
|| symbol->sym_type != SYM_database) {
|
|
|
|
if (!isc_databases || isc_databases->dbb_next || need_handle) {
|
|
|
|
need_handle = FALSE;
|
|
|
|
SYNTAX_ERROR("<database handle>");
|
|
|
|
}
|
|
|
|
ready->rdy_database = dup_dbb(isc_databases);
|
|
|
|
}
|
|
|
|
|
|
|
|
need_handle = FALSE;
|
|
|
|
if (!ready->rdy_database) {
|
|
|
|
ready->rdy_database = dup_dbb((DBB) symbol->sym_object);
|
|
|
|
ADVANCE_TOKEN;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* pick up the possible parameters, in any order */
|
|
|
|
|
|
|
|
buffers = 0;
|
|
|
|
dbb = ready->rdy_database;
|
|
|
|
connect_opts(&dbb->dbb_r_user, &dbb->dbb_r_password,
|
|
|
|
&dbb->dbb_r_sql_role, &dbb->dbb_r_lc_messages,
|
|
|
|
&buffers);
|
|
|
|
|
|
|
|
request = NULL;
|
|
|
|
if (buffers)
|
|
|
|
request = PAR_set_up_dpb_info(ready, action, buffers);
|
|
|
|
|
|
|
|
/* if there are any options that take host variables as arguments,
|
|
|
|
make sure that we generate variables for the request so that the
|
|
|
|
dpb can be extended at runtime */
|
|
|
|
|
|
|
|
if (dbb->dbb_r_user || dbb->dbb_r_password || dbb->dbb_r_sql_role
|
|
|
|
|| dbb->dbb_r_lc_messages || dbb->dbb_r_lc_ctype) {
|
|
|
|
if (!request)
|
|
|
|
request =
|
|
|
|
PAR_set_up_dpb_info(ready, action, default_buffers);
|
|
|
|
request->req_flags |= REQ_extend_dpb;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* ...and if there are compile time user or password specified,
|
|
|
|
make sure there will be a dpb generated for them */
|
|
|
|
|
|
|
|
if (!request && (dbb->dbb_c_user || dbb->dbb_c_password ||
|
|
|
|
dbb->dbb_c_sql_role ||
|
|
|
|
dbb->dbb_c_lc_messages || dbb->dbb_c_lc_ctype))
|
|
|
|
request =
|
|
|
|
PAR_set_up_dpb_info(ready, action, default_buffers);
|
|
|
|
|
|
|
|
if (!MATCH(KW_COMMA))
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (action->act_object)
|
|
|
|
return action;
|
|
|
|
|
|
|
|
// No explicit databases -- pick up all known
|
|
|
|
|
|
|
|
connect_opts(&user, &password, &sql_role, &lc_messages, &buffers);
|
|
|
|
|
|
|
|
for (dbb = isc_databases; dbb; dbb = dbb->dbb_next)
|
|
|
|
if (dbb->dbb_runtime || !(dbb->dbb_flags & DBB_sqlca)) {
|
|
|
|
ready = (RDY) ALLOC(RDY_LEN);
|
|
|
|
ready->rdy_next = (RDY) action->act_object;
|
|
|
|
action->act_object = (REF) ready;
|
|
|
|
ready->rdy_database = dup_dbb(dbb);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!action->act_object)
|
|
|
|
PAR_error("no database available to CONNECT");
|
|
|
|
else
|
|
|
|
for (ready = (RDY) action->act_object; ready; ready = ready->rdy_next) {
|
|
|
|
if (buffers)
|
|
|
|
request = PAR_set_up_dpb_info(ready, action, buffers);
|
|
|
|
else
|
|
|
|
request = ready->rdy_request;
|
|
|
|
|
|
|
|
/* if there are any options that take host variables as arguments,
|
|
|
|
make sure that we generate variables for the request so that the
|
|
|
|
dpb can be extended at runtime */
|
|
|
|
|
|
|
|
dbb = ready->rdy_database;
|
|
|
|
if (user || password || lc_messages || dbb->dbb_r_lc_ctype) {
|
|
|
|
dbb->dbb_r_user = user;
|
|
|
|
dbb->dbb_r_password = password;
|
|
|
|
dbb->dbb_r_lc_messages = lc_messages;
|
|
|
|
if (!request)
|
|
|
|
request =
|
|
|
|
PAR_set_up_dpb_info(ready, action, default_buffers);
|
|
|
|
request->req_flags |= REQ_extend_dpb;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* ...and if there are compile time user or password specified,
|
|
|
|
make sure there will be a dpb generated for them */
|
|
|
|
|
|
|
|
if (!request && (dbb->dbb_c_user || dbb->dbb_c_password ||
|
|
|
|
dbb->dbb_c_sql_role ||
|
|
|
|
dbb->dbb_c_lc_ctype || dbb->dbb_c_lc_messages))
|
|
|
|
request =
|
|
|
|
PAR_set_up_dpb_info(ready, action, default_buffers);
|
|
|
|
}
|
|
|
|
|
|
|
|
return action;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//____________________________________________________________
|
|
|
|
//
|
|
|
|
// Handle an SQL create statement.
|
|
|
|
//
|
|
|
|
|
|
|
|
static ACT act_create(void)
|
|
|
|
{
|
|
|
|
BOOLEAN descending;
|
|
|
|
BOOLEAN unique;
|
|
|
|
|
|
|
|
if (MATCH(KW_DATABASE) || MATCH(KW_SCHEMA))
|
|
|
|
return act_create_database();
|
|
|
|
|
|
|
|
if (MATCH(KW_DOMAIN))
|
|
|
|
return act_create_domain();
|
|
|
|
|
|
|
|
if (MATCH(KW_GENERATOR))
|
|
|
|
return act_create_generator();
|
|
|
|
|
|
|
|
if (MATCH(KW_SHADOW))
|
|
|
|
return act_create_shadow();
|
|
|
|
|
|
|
|
if (MATCH(KW_STOGROUP))
|
|
|
|
PAR_error("CREATE STOGROUP not supported");
|
|
|
|
|
|
|
|
if (MATCH(KW_SYNONYM))
|
|
|
|
PAR_error("CREATE SYNONYM not supported");
|
|
|
|
|
|
|
|
if (MATCH(KW_TABLE))
|
|
|
|
return act_create_table();
|
|
|
|
|
|
|
|
if (MATCH(KW_TABLESPACE))
|
|
|
|
PAR_error("CREATE TABLESPACE not supported");
|
|
|
|
|
|
|
|
if (MATCH(KW_VIEW))
|
|
|
|
return (act_create_view());
|
|
|
|
|
|
|
|
if (KEYWORD(KW_UNIQUE) || KEYWORD(KW_ASCENDING) ||
|
|
|
|
KEYWORD(KW_DESCENDING) || KEYWORD(KW_INDEX)) {
|
|
|
|
descending = FALSE;
|
|
|
|
unique = FALSE;
|
|
|
|
while (TRUE) {
|
|
|
|
if (MATCH(KW_UNIQUE))
|
|
|
|
unique = TRUE;
|
|
|
|
else if (MATCH(KW_ASCENDING))
|
|
|
|
descending = FALSE;
|
|
|
|
else if (MATCH(KW_DESCENDING))
|
|
|
|
descending = TRUE;
|
|
|
|
else if (MATCH(KW_INDEX))
|
|
|
|
return act_create_index(unique, descending);
|
|
|
|
else
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
PAR_error("Invalid CREATE request");
|
|
|
|
return NULL; /* silence compiler */
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//____________________________________________________________
|
|
|
|
//
|
|
|
|
// Handle an SQLish create database statement.
|
|
|
|
//
|
|
|
|
|
|
|
|
static ACT act_create_database(void)
|
|
|
|
{
|
|
|
|
ACT action;
|
|
|
|
SYM symbol;
|
|
|
|
DBB db;
|
|
|
|
MDBB mdb;
|
2002-11-17 01:04:19 +01:00
|
|
|
GPRE_REQ request;
|
2001-05-23 15:26:42 +02:00
|
|
|
SCHAR *string;
|
|
|
|
BOOLEAN dummy, extend_dpb;
|
|
|
|
|
|
|
|
if (isc_databases && isc_databases->dbb_next)
|
|
|
|
PAR_error
|
|
|
|
("CREATE DATABASE only allowed in context of a single database");
|
|
|
|
|
|
|
|
if (!isc_databases) {
|
|
|
|
/* generate a dummy db */
|
|
|
|
|
|
|
|
dummy = TRUE;
|
|
|
|
isc_databases = (DBB) MSC_alloc_permanent(DBB_LEN);
|
|
|
|
|
|
|
|
/* allocate symbol block */
|
|
|
|
|
|
|
|
symbol = (SYM) MSC_alloc_permanent(SYM_LEN);
|
|
|
|
|
|
|
|
/* make it the default database */
|
|
|
|
|
|
|
|
symbol->sym_type = SYM_database;
|
2002-11-17 01:04:19 +01:00
|
|
|
symbol->sym_object = (GPRE_CTX) isc_databases;
|
2001-05-23 15:26:42 +02:00
|
|
|
symbol->sym_string = database_name;
|
|
|
|
|
|
|
|
/* database block points to the symbol block */
|
|
|
|
|
|
|
|
isc_databases->dbb_name = symbol;
|
|
|
|
isc_databases->dbb_filename = NULL;
|
|
|
|
isc_databases->dbb_c_lc_ctype = module_lc_ctype;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
dummy = FALSE;
|
|
|
|
|
|
|
|
// get database name
|
|
|
|
|
|
|
|
if (QUOTED(token.tok_type)) {
|
|
|
|
db = dup_dbb(isc_databases);
|
|
|
|
db->dbb_filename = string = (TEXT *) ALLOC(token.tok_length + 1);
|
|
|
|
COPY(token.tok_string, token.tok_length, string);
|
|
|
|
if (!isc_databases->dbb_filename)
|
|
|
|
isc_databases->dbb_filename = string;
|
|
|
|
ADVANCE_TOKEN;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
SYNTAX_ERROR("<quoted database name>");
|
|
|
|
|
|
|
|
// Create a request for generating DYN to add files to database.
|
|
|
|
|
|
|
|
// request = MAKE_REQUEST (REQ_create_database);
|
|
|
|
request = MAKE_REQUEST(REQ_ddl);
|
|
|
|
request->req_flags |= REQ_sql_database_dyn;
|
|
|
|
request->req_database = db;
|
|
|
|
|
|
|
|
// create action block
|
|
|
|
|
|
|
|
action = MAKE_ACTION(request, ACT_create_database);
|
|
|
|
|
|
|
|
mdb = (MDBB) ALLOC(sizeof(struct mdbb));
|
|
|
|
mdb->mdbb_database = db;
|
|
|
|
action->act_object = (REF) mdb;
|
|
|
|
action->act_whenever = gen_whenever();
|
|
|
|
|
|
|
|
if (dummy) {
|
|
|
|
/* Create a ACT_database action */
|
|
|
|
|
|
|
|
action->act_rest = MAKE_ACTION(0, ACT_database);
|
|
|
|
action->act_rest->act_flags |= ACT_mark;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Get optional specifications
|
|
|
|
|
|
|
|
extend_dpb = tail_database(ACT_create_database, db);
|
|
|
|
|
|
|
|
// Create a request to generate dpb
|
|
|
|
|
|
|
|
ada_flags |= ADA_create_database;
|
|
|
|
|
|
|
|
request = MAKE_REQUEST(REQ_create_database);
|
|
|
|
request->req_actions = action;
|
|
|
|
mdb->mdbb_dpb_request = request;
|
|
|
|
request->req_database = db;
|
|
|
|
if (extend_dpb)
|
|
|
|
request->req_flags |= REQ_extend_dpb;
|
|
|
|
|
|
|
|
return action;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//____________________________________________________________
|
|
|
|
//
|
|
|
|
// Handle creation of a domain (global field).
|
|
|
|
//
|
|
|
|
|
|
|
|
static ACT act_create_domain(void)
|
|
|
|
{
|
|
|
|
ACT action;
|
2002-11-17 01:04:19 +01:00
|
|
|
GPRE_REQ request;
|
2002-11-30 18:45:02 +01:00
|
|
|
GPRE_FLD field;
|
2001-05-23 15:26:42 +02:00
|
|
|
CNSTRT *cnstrt;
|
|
|
|
int in_constraints;
|
2002-11-11 20:19:43 +01:00
|
|
|
GPRE_NOD literal_node;
|
2001-05-23 15:26:42 +02:00
|
|
|
|
|
|
|
// create request block
|
|
|
|
|
|
|
|
request = MAKE_REQUEST(REQ_ddl);
|
|
|
|
if (isc_databases && !isc_databases->dbb_next)
|
|
|
|
request->req_database = isc_databases;
|
|
|
|
else
|
|
|
|
PAR_error("Can only CREATE DOMAIN in context of single database");
|
|
|
|
|
|
|
|
// create action block
|
|
|
|
|
|
|
|
action = MAKE_ACTION(request, ACT_create_domain);
|
|
|
|
|
|
|
|
field = make_field(0);
|
|
|
|
MATCH(KW_AS);
|
|
|
|
SQL_par_field_dtype(request, field, FALSE);
|
|
|
|
|
|
|
|
// Check if default value was specified
|
|
|
|
|
|
|
|
if (token.tok_keyword == KW_DEFAULT) {
|
|
|
|
field->fld_default_source = CPR_start_text();
|
|
|
|
ADVANCE_TOKEN;
|
|
|
|
|
|
|
|
if (MATCH(KW_USER))
|
|
|
|
field->fld_default_value = MAKE_NODE(nod_user_name, 0);
|
|
|
|
else if (MATCH(KW_NULL))
|
|
|
|
field->fld_default_value = MAKE_NODE(nod_null, 0);
|
|
|
|
else {
|
|
|
|
if (MATCH(KW_MINUS)) {
|
|
|
|
if (token.tok_type != tok_number)
|
|
|
|
SYNTAX_ERROR("<number>");
|
|
|
|
|
|
|
|
literal_node = EXP_literal();
|
|
|
|
field->fld_default_value = MSC_unary(nod_negate,
|
|
|
|
literal_node);
|
|
|
|
}
|
|
|
|
else if ((field->fld_default_value = EXP_literal()) == NULL)
|
|
|
|
SYNTAX_ERROR("<constant>");
|
|
|
|
}
|
|
|
|
CPR_end_text(field->fld_default_source);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Check for any column level constraints
|
|
|
|
|
|
|
|
cnstrt = &field->fld_constraints;
|
|
|
|
in_constraints = TRUE;
|
|
|
|
|
|
|
|
while (in_constraints) {
|
|
|
|
switch (token.tok_keyword) {
|
|
|
|
case KW_CONSTRAINT:
|
|
|
|
case KW_CHECK:
|
|
|
|
case KW_NOT:
|
|
|
|
*cnstrt = par_field_constraint(request, field, 0);
|
|
|
|
cnstrt = &(*cnstrt)->cnstrt_next;
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
in_constraints = FALSE;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
SQL_par_field_collate(request, field);
|
|
|
|
SQL_adjust_field_dtype(field);
|
|
|
|
|
|
|
|
action->act_whenever = gen_whenever();
|
|
|
|
action->act_object = (REF) field;
|
|
|
|
|
|
|
|
return action;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//____________________________________________________________
|
|
|
|
//
|
|
|
|
// Handle an SQL create generator statement
|
|
|
|
//
|
|
|
|
|
|
|
|
static ACT act_create_generator(void)
|
|
|
|
{
|
|
|
|
|
|
|
|
ACT action;
|
|
|
|
TEXT *generator_name;
|
2002-11-17 01:04:19 +01:00
|
|
|
GPRE_REQ request;
|
2001-05-23 15:26:42 +02:00
|
|
|
|
|
|
|
|
|
|
|
generator_name = (TEXT *) ALLOC(NAME_SIZE + 1);
|
|
|
|
SQL_resolve_identifier("<identifier>", generator_name);
|
|
|
|
|
|
|
|
request = MAKE_REQUEST(REQ_ddl);
|
|
|
|
if (isc_databases && !isc_databases->dbb_next)
|
|
|
|
request->req_database = isc_databases;
|
|
|
|
else
|
|
|
|
PAR_error("Can only CREATE GENERATOR in context of single database");
|
|
|
|
|
|
|
|
if (token.tok_length > NAME_SIZE)
|
|
|
|
PAR_error("Generator name too long");
|
|
|
|
|
|
|
|
|
|
|
|
// create action block
|
|
|
|
action = MAKE_ACTION(request, ACT_create_generator);
|
|
|
|
action->act_whenever = gen_whenever();
|
|
|
|
action->act_object = (REF) generator_name;
|
|
|
|
|
|
|
|
ADVANCE_TOKEN;
|
|
|
|
return action;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//____________________________________________________________
|
|
|
|
//
|
|
|
|
// Handle an SQL create index statement.
|
|
|
|
//
|
|
|
|
|
|
|
|
static ACT act_create_index( SSHORT dups, BOOLEAN descending)
|
|
|
|
{
|
|
|
|
ACT action;
|
2002-11-30 18:45:02 +01:00
|
|
|
GPRE_FLD *ptr;
|
2001-05-23 15:26:42 +02:00
|
|
|
IND index;
|
2002-11-17 01:04:19 +01:00
|
|
|
GPRE_REQ request;
|
|
|
|
GPRE_REL relation;
|
2001-05-23 15:26:42 +02:00
|
|
|
SCHAR i_name[NAME_SIZE + 1];
|
|
|
|
|
|
|
|
// create request block
|
|
|
|
|
|
|
|
request = MAKE_REQUEST(REQ_ddl);
|
|
|
|
|
|
|
|
// get index and table names and create index and relation blocks
|
|
|
|
|
|
|
|
if (token.tok_length > NAME_SIZE)
|
|
|
|
PAR_error("Index name too long");
|
|
|
|
|
|
|
|
|
|
|
|
SQL_resolve_identifier("<column name>", i_name);
|
|
|
|
|
|
|
|
ADVANCE_TOKEN;
|
|
|
|
|
|
|
|
if (!MATCH(KW_ON))
|
|
|
|
SYNTAX_ERROR("ON");
|
|
|
|
|
|
|
|
relation = par_relation(request);
|
|
|
|
index = make_index(request, i_name);
|
|
|
|
index->ind_relation = relation;
|
|
|
|
index->ind_flags |= (dups) ? IND_dup_flag : 0;
|
|
|
|
index->ind_flags |= (descending) ? IND_descend : 0;
|
|
|
|
|
|
|
|
// create action block
|
|
|
|
|
|
|
|
action = MAKE_ACTION(request, ACT_create_index);
|
|
|
|
action->act_whenever = gen_whenever();
|
|
|
|
action->act_object = (REF) index;
|
|
|
|
|
|
|
|
// parse field list and create corresponding field blocks
|
|
|
|
|
|
|
|
EXP_left_paren(0);
|
|
|
|
ptr = &index->ind_fields;
|
|
|
|
|
|
|
|
for (;;) {
|
|
|
|
*ptr = make_field(relation);
|
|
|
|
ptr = &(*ptr)->fld_next;
|
|
|
|
if (!MATCH(KW_COMMA))
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
EXP_match_paren();
|
|
|
|
|
|
|
|
return action;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//____________________________________________________________
|
|
|
|
//
|
|
|
|
// Handle an SQL create shadow statement
|
|
|
|
//
|
|
|
|
|
|
|
|
static ACT act_create_shadow(void)
|
|
|
|
{
|
|
|
|
|
|
|
|
ACT action;
|
2002-11-17 01:04:19 +01:00
|
|
|
GPRE_REQ request;
|
2001-05-23 15:26:42 +02:00
|
|
|
FIL file_list, file;
|
|
|
|
SLONG shadow_number;
|
|
|
|
USHORT file_flags = 0;
|
|
|
|
SLONG length;
|
|
|
|
TEXT err_string[1024];
|
|
|
|
|
|
|
|
request = MAKE_REQUEST(REQ_ddl);
|
|
|
|
if (isc_databases && !isc_databases->dbb_next)
|
|
|
|
request->req_database = isc_databases;
|
|
|
|
else
|
|
|
|
PAR_error("Can only CREATE SHADOW in context of single database");
|
|
|
|
|
|
|
|
action = MAKE_ACTION(request, ACT_create_shadow);
|
|
|
|
action->act_whenever = gen_whenever();
|
|
|
|
shadow_number = EXP_USHORT_ordinal((USHORT) TRUE);
|
|
|
|
|
|
|
|
if (!RANGE_POSITIVE_SHORT_INTEGER(shadow_number))
|
|
|
|
PAR_error("Shadow number out of range");
|
|
|
|
|
|
|
|
if (MATCH(KW_MANUAL))
|
|
|
|
file_flags |= FIL_manual;
|
|
|
|
else
|
|
|
|
MATCH(KW_AUTO);
|
|
|
|
|
|
|
|
if (MATCH(KW_CONDITIONAL))
|
|
|
|
file_flags |= FIL_conditional;
|
|
|
|
|
|
|
|
file_list = file = define_file();
|
|
|
|
if (file->fil_start)
|
|
|
|
PAR_error("Can not specify file start for first file");
|
|
|
|
length = file->fil_length;
|
|
|
|
file->fil_flags = file_flags;
|
|
|
|
file->fil_shadow_number = (SSHORT) shadow_number;
|
|
|
|
while (MATCH(KW_FILE)) {
|
|
|
|
file = define_file();
|
|
|
|
if (!length && !file->fil_start) {
|
|
|
|
sprintf(err_string,
|
|
|
|
"Preceding file did not specify length, so %s must include starting page number",
|
|
|
|
file->fil_name);
|
|
|
|
PAR_error(err_string);
|
|
|
|
}
|
|
|
|
length = file->fil_length;
|
|
|
|
file->fil_flags = file_flags;
|
|
|
|
file->fil_next = file_list;
|
|
|
|
file_list = file;
|
|
|
|
}
|
|
|
|
action->act_object = (REF) file_list;
|
|
|
|
return action;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//____________________________________________________________
|
|
|
|
//
|
|
|
|
// Handle an SQL create table statement.
|
|
|
|
//
|
|
|
|
|
|
|
|
static ACT act_create_table(void)
|
|
|
|
{
|
|
|
|
ACT action;
|
2002-11-30 18:45:02 +01:00
|
|
|
GPRE_FLD *ptr;
|
2002-11-17 01:04:19 +01:00
|
|
|
GPRE_REQ request;
|
|
|
|
GPRE_REL relation;
|
2001-05-23 15:26:42 +02:00
|
|
|
CNSTRT *cnstrt;
|
2002-11-17 01:04:19 +01:00
|
|
|
GPRE_CTX context;
|
2001-05-23 15:26:42 +02:00
|
|
|
TEXT *string;
|
|
|
|
|
|
|
|
request = MAKE_REQUEST(REQ_ddl);
|
|
|
|
relation = par_relation(request);
|
|
|
|
|
|
|
|
if (MATCH(KW_EXTERNAL)) {
|
|
|
|
MATCH(KW_FILE);
|
|
|
|
if (QUOTED(token.tok_type)) {
|
|
|
|
relation->rel_ext_file = string =
|
|
|
|
(TEXT *) ALLOC(token.tok_length + 1);
|
|
|
|
COPY(token.tok_string, token.tok_length, string);
|
|
|
|
ADVANCE_TOKEN;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
SYNTAX_ERROR("<quoted filename>");
|
|
|
|
|
|
|
|
if (!check_filename(string))
|
|
|
|
PAR_error("node name not permitted"); /* a node name is not permitted in external file name */
|
|
|
|
}
|
|
|
|
|
|
|
|
// CHECK Constraints require the context to be set to the
|
|
|
|
// current relation
|
|
|
|
|
|
|
|
request->req_contexts = context = MAKE_CONTEXT(request);
|
|
|
|
context->ctx_relation = relation;
|
|
|
|
|
|
|
|
// Reserve context 1 for relation on which constraint is
|
|
|
|
// being defined
|
|
|
|
context->ctx_internal++;
|
|
|
|
request->req_internal++;
|
|
|
|
|
|
|
|
// create action block
|
|
|
|
|
|
|
|
action = MAKE_ACTION(request, ACT_create_table);
|
|
|
|
action->act_whenever = gen_whenever();
|
|
|
|
action->act_object = (REF) relation;
|
|
|
|
|
|
|
|
EXP_left_paren(0);
|
|
|
|
ptr = &relation->rel_fields;
|
|
|
|
cnstrt = &relation->rel_constraints;
|
|
|
|
|
|
|
|
for (;;) {
|
|
|
|
switch (token.tok_keyword) {
|
|
|
|
case KW_CONSTRAINT:
|
|
|
|
case KW_PRIMARY:
|
|
|
|
case KW_UNIQUE:
|
|
|
|
case KW_FOREIGN:
|
|
|
|
case KW_CHECK:
|
|
|
|
*cnstrt = par_table_constraint(request, relation);
|
|
|
|
cnstrt = &(*cnstrt)->cnstrt_next;
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
/* parse field list and create corresponding field blocks */
|
|
|
|
|
|
|
|
*ptr = par_field(request, relation);
|
|
|
|
ptr = &(*ptr)->fld_next;
|
|
|
|
}
|
|
|
|
if (!MATCH(KW_COMMA))
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
EXP_match_paren();
|
|
|
|
|
|
|
|
return action;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//____________________________________________________________
|
|
|
|
//
|
|
|
|
// Handle an SQL create view statement.
|
|
|
|
//
|
|
|
|
|
|
|
|
static ACT act_create_view(void)
|
|
|
|
{
|
|
|
|
ACT action;
|
2002-11-30 18:45:02 +01:00
|
|
|
GPRE_FLD *ptr;
|
2002-11-17 01:04:19 +01:00
|
|
|
GPRE_REQ request;
|
|
|
|
GPRE_REL relation;
|
2001-05-23 15:26:42 +02:00
|
|
|
RSE select;
|
|
|
|
|
|
|
|
request = MAKE_REQUEST(REQ_ddl);
|
|
|
|
relation = par_relation(request);
|
|
|
|
|
|
|
|
// create action block
|
|
|
|
|
|
|
|
action = MAKE_ACTION(request, ACT_create_view);
|
|
|
|
action->act_whenever = gen_whenever();
|
|
|
|
action->act_object = (REF) relation;
|
|
|
|
|
|
|
|
// if field list is present parse it and create corresponding field blocks
|
|
|
|
|
|
|
|
if (MATCH(KW_LEFT_PAREN)) {
|
|
|
|
ptr = &relation->rel_fields;
|
|
|
|
for (;;) {
|
|
|
|
*ptr = make_field(relation);
|
|
|
|
ptr = &(*ptr)->fld_next;
|
|
|
|
if (MATCH(KW_RIGHT_PAREN))
|
|
|
|
break;
|
|
|
|
MATCH(KW_COMMA);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// skip 'AS SELECT'
|
|
|
|
|
|
|
|
if (!MATCH(KW_AS))
|
|
|
|
SYNTAX_ERROR("AS");
|
|
|
|
|
|
|
|
relation->rel_view_text = CPR_start_text();
|
|
|
|
if (!MATCH(KW_SELECT))
|
|
|
|
SYNTAX_ERROR("SELECT");
|
|
|
|
|
|
|
|
// reserve context variable 0 for view
|
|
|
|
|
|
|
|
request->req_internal++;
|
|
|
|
|
|
|
|
// parse the view SELECT
|
|
|
|
|
|
|
|
relation->rel_view_rse = select = SQE_select(request, TRUE);
|
|
|
|
EXP_rse_cleanup(select);
|
|
|
|
|
|
|
|
if (MATCH(KW_WITH)) {
|
|
|
|
if (!MATCH(KW_CHECK))
|
|
|
|
SYNTAX_ERROR("CHECK");
|
|
|
|
if (!MATCH(KW_OPTION))
|
|
|
|
SYNTAX_ERROR("OPTION");
|
|
|
|
relation->rel_flags |= REL_view_check;
|
|
|
|
}
|
|
|
|
|
|
|
|
CPR_end_text(relation->rel_view_text);
|
|
|
|
|
|
|
|
return action;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//____________________________________________________________
|
|
|
|
//
|
|
|
|
// Recognize BEGIN/END DECLARE SECTION,
|
|
|
|
// and mark it as a good place to put miscellaneous
|
|
|
|
// global routine stuff.
|
|
|
|
//
|
|
|
|
|
|
|
|
static ACT act_d_section( enum act_t type)
|
|
|
|
{
|
|
|
|
ACT action;
|
|
|
|
SYM symbol;
|
|
|
|
|
|
|
|
if (!MATCH(KW_DECLARE))
|
|
|
|
SYNTAX_ERROR("DECLARE SECTION");
|
|
|
|
|
|
|
|
if (!MATCH(KW_SECTION))
|
|
|
|
SYNTAX_ERROR("SECTION");
|
|
|
|
|
|
|
|
MATCH(KW_SEMI_COLON);
|
|
|
|
|
|
|
|
action = (ACT) ALLOC(ACT_LEN);
|
|
|
|
action->act_type = type;
|
|
|
|
|
|
|
|
if (type == ACT_b_declare)
|
|
|
|
cur_routine = action;
|
|
|
|
|
|
|
|
if (!isc_databases) {
|
|
|
|
/* allocate database block and link to db chain */
|
|
|
|
|
|
|
|
isc_databases = (DBB) MSC_alloc_permanent(DBB_LEN);
|
|
|
|
|
|
|
|
/* allocate symbol block */
|
|
|
|
|
|
|
|
symbol = (SYM) MSC_alloc_permanent(SYM_LEN);
|
|
|
|
|
|
|
|
/* make it a database, specifically this one */
|
|
|
|
|
|
|
|
symbol->sym_type = SYM_database;
|
2002-11-17 01:04:19 +01:00
|
|
|
symbol->sym_object = (GPRE_CTX) isc_databases;
|
2001-05-23 15:26:42 +02:00
|
|
|
symbol->sym_string = database_name;
|
|
|
|
|
|
|
|
/* database block points to the symbol block */
|
|
|
|
|
|
|
|
isc_databases->dbb_name = symbol;
|
|
|
|
isc_databases->dbb_filename = NULL;
|
|
|
|
isc_databases->dbb_flags = DBB_sqlca;
|
|
|
|
isc_databases->dbb_c_lc_ctype = module_lc_ctype;
|
|
|
|
if (sw_external)
|
|
|
|
isc_databases->dbb_scope = DBB_EXTERN;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
/* Load up the symbol (hash) table with relation names from this database */
|
|
|
|
|
|
|
|
MET_load_hash_table(isc_databases);
|
|
|
|
}
|
|
|
|
|
|
|
|
HSH_insert(isc_databases->dbb_name);
|
|
|
|
|
|
|
|
if (MATCH(KW_SQL)) {
|
|
|
|
if (!MATCH(KW_NAMES))
|
|
|
|
SYNTAX_ERROR("NAMES ARE");
|
|
|
|
if (!MATCH(KW_ARE))
|
|
|
|
SYNTAX_ERROR("NAMES ARE");
|
|
|
|
if (!(symbol = MSC_find_symbol(token.tok_symbol, SYM_charset)))
|
|
|
|
PAR_error("The named CHARACTER SET was not found");
|
|
|
|
if (module_lc_ctype && !strcmp(module_lc_ctype, symbol->sym_string))
|
|
|
|
PAR_error("Duplicate specification of module CHARACTER SET.");
|
|
|
|
module_lc_ctype = symbol->sym_string;
|
|
|
|
isc_databases->dbb_c_lc_ctype = symbol->sym_string;
|
|
|
|
CPR_token();
|
|
|
|
}
|
|
|
|
|
|
|
|
return action;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//____________________________________________________________
|
|
|
|
//
|
|
|
|
// Parse the SQL cursor declaration.
|
|
|
|
//
|
|
|
|
|
|
|
|
static ACT act_declare(void)
|
|
|
|
{
|
|
|
|
DBB db;
|
2002-11-17 01:04:19 +01:00
|
|
|
GPRE_REQ request;
|
2001-05-23 15:26:42 +02:00
|
|
|
ACT action;
|
|
|
|
SYM symbol;
|
|
|
|
DYN statement;
|
|
|
|
TEXT t_str[132];
|
|
|
|
BOOLEAN delimited = FALSE;
|
|
|
|
#ifdef SCROLLABLE_CURSORS
|
|
|
|
BOOLEAN scroll = FALSE;
|
|
|
|
#endif
|
|
|
|
|
|
|
|
db = NULL;
|
|
|
|
|
|
|
|
if (token.tok_symbol && (token.tok_symbol->sym_type == SYM_database)) {
|
|
|
|
/* must be a database specifier in a DECLARE TABLE statement */
|
|
|
|
|
|
|
|
db = (DBB) token.tok_symbol->sym_object;
|
|
|
|
ADVANCE_TOKEN;
|
|
|
|
if (!MATCH(KW_DOT))
|
|
|
|
SYNTAX_ERROR(". (period)");
|
|
|
|
symbol = PAR_symbol(SYM_dummy);
|
|
|
|
if (token.tok_keyword != KW_TABLE)
|
|
|
|
SYNTAX_ERROR("TABLE");
|
|
|
|
return (act_declare_table(symbol, db));
|
|
|
|
}
|
|
|
|
|
|
|
|
switch (token.tok_keyword) {
|
|
|
|
case KW_FILTER:
|
|
|
|
return (act_declare_filter());
|
|
|
|
break;
|
|
|
|
|
|
|
|
case KW_EXTERNAL:
|
|
|
|
ADVANCE_TOKEN;
|
|
|
|
if (MATCH(KW_FUNCTION))
|
|
|
|
return (act_declare_udf());
|
|
|
|
else
|
|
|
|
SYNTAX_ERROR("FUNCTION");
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
SQL_resolve_identifier("<Cursor Name>", t_str);
|
|
|
|
if (token.tok_type == tok_dblquoted)
|
|
|
|
delimited = TRUE;
|
|
|
|
else {
|
|
|
|
SYM symb;
|
|
|
|
if ((symb = HSH_lookup2(token.tok_string)) &&
|
|
|
|
(symb->sym_type == SYM_cursor ||
|
|
|
|
symb->sym_type == SYM_delimited_cursor)) {
|
|
|
|
char s[64];
|
|
|
|
sprintf(s, "symbol %s is already in use", t_str);
|
|
|
|
PAR_error(s);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
symbol = PAR_symbol(SYM_cursor);
|
|
|
|
|
|
|
|
switch (token.tok_keyword) {
|
|
|
|
case KW_TABLE:
|
|
|
|
return (act_declare_table(symbol, 0));
|
|
|
|
|
|
|
|
#ifdef SCROLLABLE_CURSORS
|
|
|
|
case KW_SCROLL:
|
|
|
|
ADVANCE_TOKEN;
|
|
|
|
scroll = TRUE;
|
|
|
|
if (token.tok_keyword != KW_CURSOR)
|
|
|
|
SYNTAX_ERROR("CURSOR");
|
|
|
|
#endif
|
|
|
|
case KW_CURSOR:
|
|
|
|
ADVANCE_TOKEN;
|
|
|
|
if (!MATCH(KW_FOR))
|
|
|
|
SYNTAX_ERROR("FOR");
|
|
|
|
if (MATCH(KW_SELECT)) {
|
|
|
|
request = MAKE_REQUEST(REQ_cursor);
|
|
|
|
request->req_flags |= REQ_sql_cursor | REQ_sql_declare_cursor;
|
|
|
|
#ifdef SCROLLABLE_CURSORS
|
|
|
|
if (scroll)
|
|
|
|
request->req_flags |= REQ_scroll;
|
|
|
|
#endif
|
2002-11-17 01:04:19 +01:00
|
|
|
symbol->sym_object = (GPRE_CTX) request;
|
2001-05-23 15:26:42 +02:00
|
|
|
action = MAKE_ACTION(request, ACT_cursor);
|
|
|
|
action->act_object = (REF) symbol;
|
|
|
|
symbol->sym_type =
|
|
|
|
(delimited) ? SYM_delimited_cursor : SYM_cursor;
|
|
|
|
request->req_rse = SQE_select(request, FALSE);
|
|
|
|
if (MATCH(KW_FOR)) {
|
|
|
|
if (!MATCH(KW_UPDATE))
|
|
|
|
SYNTAX_ERROR("UPDATE");
|
|
|
|
if (!MATCH(KW_OF))
|
|
|
|
SYNTAX_ERROR("OF");
|
|
|
|
|
|
|
|
do
|
|
|
|
CPR_token();
|
|
|
|
while (MATCH(KW_COMMA));
|
|
|
|
}
|
|
|
|
EXP_rse_cleanup(request->req_rse);
|
|
|
|
}
|
|
|
|
else if (MATCH(KW_READ)) {
|
|
|
|
action = act_open_blob(ACT_blob_open, symbol);
|
|
|
|
symbol->sym_type =
|
|
|
|
(delimited) ? SYM_delimited_cursor : SYM_cursor;
|
|
|
|
}
|
|
|
|
else if (MATCH(KW_INSERT)) {
|
|
|
|
action = act_open_blob(ACT_blob_create, symbol);
|
|
|
|
symbol->sym_type =
|
|
|
|
(delimited) ? SYM_delimited_cursor : SYM_cursor;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
statement = par_statement();
|
2002-11-17 01:04:19 +01:00
|
|
|
symbol->sym_object = (GPRE_CTX) statement;
|
2001-05-23 15:26:42 +02:00
|
|
|
if (MATCH(KW_FOR)) {
|
|
|
|
if (!MATCH(KW_UPDATE))
|
|
|
|
SYNTAX_ERROR("UPDATE");
|
|
|
|
if (!MATCH(KW_OF))
|
|
|
|
SYNTAX_ERROR("OF");
|
|
|
|
|
|
|
|
do
|
|
|
|
CPR_token();
|
|
|
|
while (MATCH(KW_COMMA));
|
|
|
|
}
|
|
|
|
symbol->sym_type = SYM_dyn_cursor;
|
|
|
|
statement->dyn_cursor_name = symbol;
|
|
|
|
action = (ACT) ALLOC(ACT_LEN);
|
|
|
|
action->act_type = ACT_dyn_cursor;
|
|
|
|
action->act_object = (REF) statement;
|
|
|
|
action->act_whenever = gen_whenever();
|
|
|
|
}
|
|
|
|
HSH_insert(symbol);
|
|
|
|
return action;
|
|
|
|
|
|
|
|
default:
|
|
|
|
while (MATCH(KW_COMMA));
|
|
|
|
if (MATCH(KW_STATEMENT)) {
|
|
|
|
action = (ACT) ALLOC(ACT_LEN);
|
|
|
|
action->act_type = ACT_dyn_statement;
|
|
|
|
return action;
|
|
|
|
}
|
|
|
|
SYNTAX_ERROR("CURSOR, STATEMENT or TABLE");
|
|
|
|
}
|
|
|
|
return NULL; /* silence compiler */
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//____________________________________________________________
|
|
|
|
//
|
|
|
|
// Handle an SQL declare filter statement
|
|
|
|
//
|
|
|
|
|
|
|
|
static ACT act_declare_filter(void)
|
|
|
|
{
|
|
|
|
ACT action;
|
2002-11-17 01:04:19 +01:00
|
|
|
GPRE_REQ request;
|
2001-05-23 15:26:42 +02:00
|
|
|
FLTR filter;
|
|
|
|
SLONG input_type, output_type;
|
|
|
|
|
|
|
|
request = MAKE_REQUEST(REQ_ddl);
|
|
|
|
|
|
|
|
if (isc_databases && !isc_databases->dbb_next)
|
|
|
|
request->req_database = isc_databases;
|
|
|
|
else
|
|
|
|
PAR_error("Can only DECLARE FILTER in context of single database");
|
|
|
|
|
|
|
|
ADVANCE_TOKEN;
|
|
|
|
filter = (FLTR) ALLOC(FLTR_LEN);
|
|
|
|
filter->fltr_name = (TEXT *) ALLOC(NAME_SIZE + 1);
|
|
|
|
SQL_resolve_identifier("<identifier>", filter->fltr_name);
|
|
|
|
if (token.tok_length > NAME_SIZE)
|
|
|
|
PAR_error("Filter name too long");
|
|
|
|
|
|
|
|
ADVANCE_TOKEN;
|
|
|
|
|
|
|
|
// create action block
|
|
|
|
action = MAKE_ACTION(request, ACT_declare_filter);
|
|
|
|
action->act_whenever = gen_whenever();
|
|
|
|
action->act_object = (REF) filter;
|
|
|
|
|
|
|
|
if (MATCH(KW_INPUT_TYPE))
|
|
|
|
input_type = EXP_SSHORT_ordinal((USHORT) TRUE);
|
|
|
|
else
|
|
|
|
SYNTAX_ERROR("INPUT_TYPE");
|
|
|
|
|
|
|
|
if (MATCH(KW_OUTPUT_TYPE))
|
|
|
|
output_type = EXP_SSHORT_ordinal((USHORT) TRUE);
|
|
|
|
else
|
|
|
|
SYNTAX_ERROR("OUTPUT_TYPE");
|
|
|
|
|
|
|
|
if (!RANGE_SHORT_INTEGER(input_type)
|
|
|
|
|| !RANGE_SHORT_INTEGER(output_type))
|
|
|
|
PAR_error("Blob sub_type out of range");
|
|
|
|
|
|
|
|
filter->fltr_input_type = (SSHORT) input_type;
|
|
|
|
filter->fltr_output_type = (SSHORT) output_type;
|
|
|
|
|
|
|
|
if (MATCH(KW_ENTRY_POINT))
|
|
|
|
filter->fltr_entry_point = extract_string(TRUE);
|
|
|
|
else
|
|
|
|
SYNTAX_ERROR("ENTRY_POINT");
|
|
|
|
|
|
|
|
if (MATCH(KW_MODULE_NAME))
|
|
|
|
filter->fltr_module_name = extract_string(TRUE);
|
|
|
|
else
|
|
|
|
SYNTAX_ERROR("MODULE_NAME");
|
|
|
|
|
|
|
|
return action;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//____________________________________________________________
|
|
|
|
//
|
|
|
|
// Handle an SQL declare table statement.
|
|
|
|
//
|
|
|
|
|
|
|
|
static ACT act_declare_table( SYM symbol, DBB db)
|
|
|
|
{
|
|
|
|
ACT action;
|
2002-11-30 18:45:02 +01:00
|
|
|
GPRE_FLD field, dbkey, *ptr;
|
2002-11-17 01:04:19 +01:00
|
|
|
GPRE_REL relation, tmp_relation;
|
2001-05-23 15:26:42 +02:00
|
|
|
SYM old_symbol, tmp_symbol;
|
|
|
|
USHORT count;
|
2002-11-17 01:04:19 +01:00
|
|
|
GPRE_REQ request;
|
2001-05-23 15:26:42 +02:00
|
|
|
|
|
|
|
// create a local request block
|
|
|
|
|
2002-11-17 01:04:19 +01:00
|
|
|
request = (GPRE_REQ) ALLOC(REQ_LEN);
|
2001-05-23 15:26:42 +02:00
|
|
|
request->req_type = REQ_ddl;
|
|
|
|
|
|
|
|
// create relation block
|
|
|
|
|
|
|
|
relation = make_relation(0, symbol->sym_string);
|
|
|
|
|
|
|
|
if (!db)
|
|
|
|
db = relation->rel_database;
|
|
|
|
|
|
|
|
request->req_database = db;
|
|
|
|
|
|
|
|
relation->rel_next = db->dbb_relations;
|
|
|
|
db->dbb_relations = relation;
|
|
|
|
relation->rel_dbkey = dbkey =
|
|
|
|
MET_make_field("rdb$db_key", dtype_text, 8, FALSE);
|
|
|
|
dbkey->fld_flags |= FLD_dbkey | FLD_text | FLD_charset;
|
|
|
|
dbkey->fld_ttype = ttype_binary;
|
|
|
|
|
|
|
|
// if relation name already in incore metadata, remove it & its fields
|
|
|
|
|
|
|
|
old_symbol = HSH_lookup(relation->rel_symbol->sym_string);
|
|
|
|
|
|
|
|
while (old_symbol)
|
|
|
|
if (old_symbol->sym_type == SYM_relation) {
|
2002-11-17 01:04:19 +01:00
|
|
|
tmp_relation = (GPRE_REL) old_symbol->sym_object;
|
2001-05-23 15:26:42 +02:00
|
|
|
if (tmp_relation->rel_meta)
|
|
|
|
PAR_error("Multiple DECLARE TABLE statements for table");
|
|
|
|
tmp_symbol = old_symbol->sym_homonym;
|
|
|
|
HSH_remove(old_symbol);
|
|
|
|
for (field = tmp_relation->rel_fields; field;
|
|
|
|
field = field->fld_next) HSH_remove(field->fld_symbol);
|
|
|
|
old_symbol = tmp_symbol;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
old_symbol = old_symbol->sym_homonym;
|
|
|
|
|
|
|
|
// add new symbol to incore metadata
|
|
|
|
|
|
|
|
HSH_insert(relation->rel_symbol);
|
|
|
|
|
|
|
|
// create action block
|
|
|
|
|
|
|
|
action = (ACT) ALLOC(ACT_LEN);
|
|
|
|
action->act_type = ACT_noop;
|
|
|
|
action->act_object = (REF) relation;
|
|
|
|
|
|
|
|
// parse field list and create corresponding field blocks
|
|
|
|
// include size information for message length calculations
|
|
|
|
|
|
|
|
ADVANCE_TOKEN;
|
|
|
|
EXP_left_paren(0);
|
|
|
|
count = 0;
|
|
|
|
ptr = &relation->rel_fields;
|
|
|
|
|
|
|
|
for (;;) {
|
|
|
|
*ptr = field = par_field(request, relation);
|
|
|
|
ptr = &field->fld_next;
|
|
|
|
HSH_insert(field->fld_symbol);
|
|
|
|
field->fld_position = count++;
|
|
|
|
field->fld_flags &= ~FLD_meta;
|
|
|
|
SQL_adjust_field_dtype(field);
|
|
|
|
|
|
|
|
if (!MATCH(KW_COMMA))
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
EXP_match_paren();
|
|
|
|
|
|
|
|
return action;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//____________________________________________________________
|
|
|
|
//
|
|
|
|
// Handle an SQL declare external statement
|
|
|
|
//
|
|
|
|
|
|
|
|
static ACT act_declare_udf(void)
|
|
|
|
{
|
|
|
|
ACT action;
|
2002-11-17 01:04:19 +01:00
|
|
|
GPRE_REQ request;
|
2001-05-23 15:26:42 +02:00
|
|
|
DECL_UDF udf;
|
2002-11-30 18:45:02 +01:00
|
|
|
GPRE_FLD *ptr, field;
|
2001-05-23 15:26:42 +02:00
|
|
|
SLONG return_parameter;
|
|
|
|
|
|
|
|
request = MAKE_REQUEST(REQ_ddl);
|
|
|
|
|
|
|
|
if (isc_databases && !isc_databases->dbb_next)
|
|
|
|
request->req_database = isc_databases;
|
|
|
|
else
|
|
|
|
PAR_error
|
|
|
|
("Can only DECLARE EXTERNAL FUNCTION in context of single database");
|
|
|
|
|
|
|
|
udf = (DECL_UDF) ALLOC(DECL_UDF_LEN);
|
|
|
|
udf->decl_udf_name = (TEXT *) ALLOC(NAME_SIZE + 1);
|
|
|
|
SQL_resolve_identifier("<identifier>", udf->decl_udf_name);
|
|
|
|
if (token.tok_length > NAME_SIZE)
|
|
|
|
PAR_error("external function name too long");
|
|
|
|
ADVANCE_TOKEN;
|
|
|
|
|
|
|
|
// create action block
|
|
|
|
action = MAKE_ACTION(request, ACT_declare_udf);
|
|
|
|
action->act_whenever = gen_whenever();
|
|
|
|
action->act_object = (REF) udf;
|
|
|
|
|
|
|
|
ptr = &udf->decl_udf_arg_list;
|
|
|
|
while (TRUE) {
|
|
|
|
if (MATCH(KW_RETURNS)) {
|
|
|
|
if (MATCH(KW_PARAMETER)) {
|
|
|
|
return_parameter = EXP_pos_USHORT_ordinal((USHORT) TRUE);
|
|
|
|
if (return_parameter > 10)
|
|
|
|
PAR_error("return parameter not in range");
|
|
|
|
assert(return_parameter <= MAX_SSHORT);
|
|
|
|
udf->decl_udf_return_parameter = (SSHORT) return_parameter;
|
|
|
|
}
|
|
|
|
else {
|
2002-11-30 18:45:02 +01:00
|
|
|
field = (GPRE_FLD) ALLOC(FLD_LEN);
|
2001-05-23 15:26:42 +02:00
|
|
|
field->fld_flags |= (FLD_meta | FLD_meta_cstring);
|
|
|
|
SQL_par_field_dtype(request, field, TRUE);
|
|
|
|
SQL_adjust_field_dtype(field);
|
|
|
|
udf->decl_udf_return_type = field;
|
|
|
|
MATCH(KW_BY);
|
|
|
|
if (MATCH(KW_VALUE))
|
|
|
|
udf->decl_udf_return_mode = FUN_value;
|
|
|
|
else
|
|
|
|
udf->decl_udf_return_mode = FUN_reference;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
else {
|
2002-11-30 18:45:02 +01:00
|
|
|
field = (GPRE_FLD) ALLOC(FLD_LEN);
|
2001-05-23 15:26:42 +02:00
|
|
|
field->fld_flags |= (FLD_meta | FLD_meta_cstring);
|
|
|
|
SQL_par_field_dtype(request, field, TRUE);
|
|
|
|
SQL_adjust_field_dtype(field);
|
|
|
|
*ptr = field;
|
|
|
|
ptr = &(field->fld_next);
|
|
|
|
MATCH(KW_COMMA);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (MATCH(KW_ENTRY_POINT))
|
|
|
|
udf->decl_udf_entry_point = extract_string(TRUE);
|
|
|
|
else
|
|
|
|
SYNTAX_ERROR("ENTRY_POINT");
|
|
|
|
|
|
|
|
if (MATCH(KW_MODULE_NAME))
|
|
|
|
udf->decl_udf_module_name = extract_string(TRUE);
|
|
|
|
else
|
|
|
|
SYNTAX_ERROR("MODULE_NAME");
|
|
|
|
|
|
|
|
return action;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//____________________________________________________________
|
|
|
|
//
|
|
|
|
// Parse an update action. This is a little more complicated
|
|
|
|
// because SQL confuses the update of a cursor with a mass update.
|
|
|
|
// The syntax, and therefor the code, I fear, is a mess.
|
|
|
|
//
|
|
|
|
|
|
|
|
static ACT act_delete(void)
|
|
|
|
{
|
|
|
|
ACT action;
|
2002-11-17 01:04:19 +01:00
|
|
|
GPRE_REQ request;
|
|
|
|
GPRE_REL relation;
|
2001-05-23 15:26:42 +02:00
|
|
|
RSE rse;
|
2002-11-17 01:04:19 +01:00
|
|
|
GPRE_CTX context;
|
2001-05-23 15:26:42 +02:00
|
|
|
UPD update;
|
|
|
|
SYM alias;
|
|
|
|
TEXT *transaction, r_name[NAME_SIZE + 1], db_name[NAME_SIZE + 1],
|
|
|
|
owner_name[NAME_SIZE + 1], *str_1, *str_2;
|
|
|
|
SSHORT where, i, trans_nm_len;
|
|
|
|
SSHORT hsh_rm = 0;
|
|
|
|
|
|
|
|
par_options(&transaction);
|
|
|
|
|
|
|
|
if (!MATCH(KW_FROM))
|
|
|
|
SYNTAX_ERROR("FROM");
|
|
|
|
|
|
|
|
// First comes the relation. Unfortunately, there is no way to identify
|
|
|
|
// its database until the cursor is known. Sigh. Save the token.
|
|
|
|
|
|
|
|
SQL_relation_name(r_name, db_name, owner_name);
|
|
|
|
|
|
|
|
// Parse the optional alias (context variable)
|
|
|
|
|
|
|
|
alias = (token.tok_symbol) ? NULL : PAR_symbol(SYM_dummy);
|
|
|
|
|
|
|
|
// Now the moment of truth. If the next few tokens are WHERE CURRENT OF
|
|
|
|
// then this is a sub-action of an existing request. If not, then it is
|
|
|
|
// a free standing request
|
|
|
|
|
|
|
|
request = MAKE_REQUEST(REQ_mass_update);
|
|
|
|
update = (UPD) ALLOC(UPD_LEN);
|
|
|
|
|
|
|
|
if ((where = MATCH(KW_WITH)) && MATCH(KW_CURRENT)) {
|
|
|
|
if (!MATCH(KW_OF))
|
|
|
|
SYNTAX_ERROR("OF <cursor>");
|
|
|
|
requests = request->req_next;
|
|
|
|
cur_routine->act_object = (REF) request->req_routine;
|
|
|
|
FREE((UCHAR *) request);
|
|
|
|
request = par_cursor(NULL);
|
|
|
|
if ((transaction || request->req_trans) &&
|
|
|
|
(!transaction || !request->req_trans ||
|
|
|
|
strcmp(transaction, request->req_trans))) {
|
|
|
|
if (transaction)
|
|
|
|
PAR_error("different transaction for select and delete");
|
|
|
|
else { /* does not specify transaction clause in */
|
|
|
|
/* "delete ... where cuurent of cursor" stmt */
|
|
|
|
trans_nm_len = strlen(request->req_trans);
|
|
|
|
str_2 = transaction = (SCHAR *) ALLOC(trans_nm_len + 1);
|
|
|
|
str_1 = request->req_trans;
|
|
|
|
do
|
|
|
|
*str_2++ = *str_1++;
|
|
|
|
while (--trans_nm_len);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
request->req_trans = transaction;
|
|
|
|
relation = SQL_relation(request, r_name, db_name, owner_name, TRUE);
|
|
|
|
rse = request->req_rse;
|
|
|
|
for (i = 0; i < rse->rse_count; i++) {
|
|
|
|
context = rse->rse_context[i];
|
|
|
|
if (context->ctx_relation == relation)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (i == rse->rse_count)
|
|
|
|
PAR_error("table not in request");
|
|
|
|
update->upd_request = request;
|
|
|
|
update->upd_source = context;
|
|
|
|
action = MAKE_ACTION(request, ACT_erase);
|
|
|
|
action->act_object = (REF) update;
|
|
|
|
action->act_whenever = gen_whenever();
|
|
|
|
return action;
|
|
|
|
}
|
|
|
|
|
|
|
|
request->req_trans = transaction;
|
|
|
|
|
|
|
|
// How amusing. After all that work, it wasn't a sub-action at all.
|
|
|
|
// Neat. Take the pieces and build a complete request. Start by
|
|
|
|
// figuring out what database is involved.
|
|
|
|
|
|
|
|
relation = SQL_relation(request, r_name, db_name, owner_name, TRUE);
|
|
|
|
|
|
|
|
request->req_rse = rse = (RSE) ALLOC(RSE_LEN(1));
|
|
|
|
rse->rse_count = 1;
|
|
|
|
rse->rse_context[0] = context = MAKE_CONTEXT(request);
|
|
|
|
context->ctx_relation = relation;
|
|
|
|
|
|
|
|
if (alias && !token.tok_symbol) {
|
|
|
|
alias->sym_type = SYM_context;
|
|
|
|
alias->sym_object = context;
|
|
|
|
context->ctx_symbol = alias;
|
|
|
|
token.tok_symbol = alias;
|
|
|
|
HSH_insert(alias);
|
|
|
|
hsh_rm = 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (where)
|
|
|
|
rse->rse_boolean = SQE_boolean(request, 0);
|
|
|
|
|
|
|
|
request->req_node = MAKE_NODE(nod_erase, 0);
|
|
|
|
action = MAKE_ACTION(request, ACT_loop);
|
|
|
|
action->act_whenever = gen_whenever();
|
|
|
|
|
|
|
|
if (hsh_rm)
|
|
|
|
HSH_remove(alias);
|
|
|
|
|
|
|
|
return action;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//____________________________________________________________
|
|
|
|
//
|
|
|
|
// Handle an SQL describe statement.
|
|
|
|
// Reject
|
|
|
|
//
|
|
|
|
|
|
|
|
static ACT act_describe(void)
|
|
|
|
{
|
|
|
|
DYN statement;
|
|
|
|
ACT action;
|
|
|
|
BOOLEAN in_sqlda;
|
|
|
|
|
|
|
|
if (MATCH(KW_INPUT))
|
|
|
|
in_sqlda = TRUE;
|
|
|
|
else {
|
|
|
|
MATCH(KW_OUTPUT);
|
|
|
|
in_sqlda = FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
statement = par_statement();
|
|
|
|
|
|
|
|
if (!MATCH(KW_INTO)) {
|
|
|
|
/* check for SQL2 syntax
|
|
|
|
"USING SQL DESCRIPTOR sqlda" */
|
|
|
|
|
|
|
|
if (!MATCH(KW_USING) || !MATCH(KW_SQL) || !MATCH(KW_DESCRIPTOR))
|
|
|
|
SYNTAX_ERROR("INTO or USING SQL DESCRIPTOR sqlda");
|
|
|
|
}
|
|
|
|
else if (MATCH(KW_SQL) && !MATCH(KW_DESCRIPTOR))
|
|
|
|
SYNTAX_ERROR("INTO SQL DESCRIPTOR sqlda");
|
|
|
|
|
|
|
|
statement->dyn_sqlda = PAR_native_value(FALSE, FALSE);
|
|
|
|
action = (ACT) ALLOC(ACT_LEN);
|
|
|
|
if (in_sqlda)
|
|
|
|
action->act_type = ACT_dyn_describe_input;
|
|
|
|
else
|
|
|
|
action->act_type = ACT_dyn_describe;
|
|
|
|
action->act_object = (REF) statement;
|
|
|
|
action->act_whenever = gen_whenever();
|
|
|
|
|
|
|
|
return action;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//____________________________________________________________
|
|
|
|
//
|
|
|
|
// Parse a FINISH statement.
|
|
|
|
//
|
|
|
|
|
|
|
|
static ACT act_disconnect(void)
|
|
|
|
{
|
2003-02-10 14:28:35 +01:00
|
|
|
ACT action;
|
2001-05-23 15:26:42 +02:00
|
|
|
SYM symbol;
|
|
|
|
RDY ready;
|
|
|
|
USHORT all;
|
|
|
|
|
|
|
|
action = MAKE_ACTION(0, ACT_disconnect);
|
|
|
|
action->act_whenever = gen_whenever();
|
|
|
|
all = MATCH(KW_ALL) || MATCH(KW_DEFAULT);
|
|
|
|
|
|
|
|
if (!all) {
|
|
|
|
if (MATCH(KW_CURRENT))
|
|
|
|
PAR_error("DISCONNECT CURRENT not supported");
|
|
|
|
if (!
|
|
|
|
((symbol = token.tok_symbol)
|
|
|
|
&& (symbol->sym_type ==
|
|
|
|
SYM_database)))
|
|
|
|
SYNTAX_ERROR("ALL, DEFAULT, or <database handle>");
|
|
|
|
while (TRUE) {
|
|
|
|
if ((symbol = token.tok_symbol)
|
|
|
|
&& (symbol->sym_type == SYM_database)) {
|
|
|
|
ready = (RDY) ALLOC(RDY_LEN);
|
|
|
|
ready->rdy_next = (RDY) action->act_object;
|
|
|
|
action->act_object = (REF) ready;
|
|
|
|
ready->rdy_database = (DBB) symbol->sym_object;
|
|
|
|
ADVANCE_TOKEN;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
SYNTAX_ERROR("<database handle>");
|
|
|
|
if (!MATCH(KW_COMMA))
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (sw_language == lang_ada)
|
|
|
|
MATCH(KW_SEMI_COLON);
|
|
|
|
return action;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//____________________________________________________________
|
|
|
|
//
|
|
|
|
// Handle an SQL drop statement.
|
|
|
|
//
|
|
|
|
|
|
|
|
static ACT act_drop(void)
|
|
|
|
{
|
|
|
|
ACT action;
|
|
|
|
DBB db;
|
|
|
|
IND index;
|
2002-11-17 01:04:19 +01:00
|
|
|
GPRE_REQ request;
|
|
|
|
GPRE_REL relation;
|
2001-05-23 15:26:42 +02:00
|
|
|
SYM symbol;
|
|
|
|
SCHAR *db_string;
|
|
|
|
TEXT *str;
|
|
|
|
SLONG num;
|
|
|
|
|
|
|
|
switch (token.tok_keyword) {
|
|
|
|
case KW_DATABASE:
|
|
|
|
PAR_error("DROP DATABASE not supported");
|
|
|
|
|
|
|
|
request = MAKE_REQUEST(REQ_ddl);
|
|
|
|
ADVANCE_TOKEN;
|
|
|
|
if (!QUOTED(token.tok_type))
|
|
|
|
SYNTAX_ERROR("<quoted database name>");
|
|
|
|
db = (DBB) ALLOC(DBB_LEN);
|
|
|
|
db->dbb_filename = db_string = (TEXT *) ALLOC(token.tok_length + 1);
|
|
|
|
COPY(token.tok_string, token.tok_length, db_string);
|
|
|
|
symbol = PAR_symbol(SYM_dummy);
|
|
|
|
db->dbb_name = symbol;
|
|
|
|
symbol->sym_type = SYM_database;
|
2002-11-17 01:04:19 +01:00
|
|
|
symbol->sym_object = (GPRE_CTX) db;
|
2001-05-23 15:26:42 +02:00
|
|
|
action = MAKE_ACTION(request, ACT_drop_database);
|
|
|
|
action->act_whenever = gen_whenever();
|
|
|
|
action->act_object = (REF) db;
|
|
|
|
ADVANCE_TOKEN;
|
|
|
|
return action;
|
|
|
|
|
|
|
|
case KW_DOMAIN:
|
|
|
|
request = MAKE_REQUEST(REQ_ddl);
|
|
|
|
if (isc_databases && !isc_databases->dbb_next)
|
|
|
|
request->req_database = isc_databases;
|
|
|
|
else
|
|
|
|
PAR_error("Can only DROP DOMAIN in context of single database");
|
|
|
|
ADVANCE_TOKEN;
|
|
|
|
str = (TEXT *) ALLOC(NAME_SIZE + 1);
|
|
|
|
SQL_resolve_identifier("<identifier>", str);
|
|
|
|
action = MAKE_ACTION(request, ACT_drop_domain);
|
|
|
|
action->act_whenever = gen_whenever();
|
|
|
|
action->act_object = (REF) str;
|
|
|
|
ADVANCE_TOKEN;
|
|
|
|
return action;
|
|
|
|
|
|
|
|
case KW_FILTER:
|
|
|
|
request = MAKE_REQUEST(REQ_ddl);
|
|
|
|
if (isc_databases && !isc_databases->dbb_next)
|
|
|
|
request->req_database = isc_databases;
|
|
|
|
else
|
|
|
|
PAR_error("Can only DROP FILTER in context of single database");
|
|
|
|
ADVANCE_TOKEN;
|
|
|
|
str = (TEXT *) ALLOC(NAME_SIZE + 1);
|
|
|
|
SQL_resolve_identifier("<identifier>", str);
|
|
|
|
action = MAKE_ACTION(request, ACT_drop_filter);
|
|
|
|
action->act_whenever = gen_whenever();
|
|
|
|
action->act_object = (REF) str;
|
|
|
|
ADVANCE_TOKEN;
|
|
|
|
return action;
|
|
|
|
|
|
|
|
case KW_EXTERNAL:
|
|
|
|
|
|
|
|
ADVANCE_TOKEN;
|
|
|
|
if (!MATCH(KW_FUNCTION))
|
|
|
|
SYNTAX_ERROR("FUNCTION");
|
|
|
|
request = MAKE_REQUEST(REQ_ddl);
|
|
|
|
if (isc_databases && !isc_databases->dbb_next)
|
|
|
|
request->req_database = isc_databases;
|
|
|
|
else
|
|
|
|
PAR_error
|
|
|
|
("Can only DROP EXTERNAL FUNCTION in context of a single database");
|
|
|
|
|
|
|
|
str = (TEXT *) ALLOC(NAME_SIZE + 1);
|
|
|
|
SQL_resolve_identifier("<identifier>", str);
|
|
|
|
action = MAKE_ACTION(request, ACT_drop_udf);
|
|
|
|
action->act_whenever = gen_whenever();
|
|
|
|
action->act_object = (REF) str;
|
|
|
|
ADVANCE_TOKEN;
|
|
|
|
return action;
|
|
|
|
|
|
|
|
case KW_INDEX:
|
|
|
|
request = MAKE_REQUEST(REQ_ddl);
|
|
|
|
ADVANCE_TOKEN;
|
|
|
|
str = (TEXT *) ALLOC(NAME_SIZE + 1);
|
|
|
|
SQL_resolve_identifier("<identifier>", str);
|
|
|
|
index = make_index(request, token.tok_string);
|
|
|
|
action = MAKE_ACTION(request, ACT_drop_index);
|
|
|
|
action->act_whenever = gen_whenever();
|
|
|
|
action->act_object = (REF) index;
|
|
|
|
ADVANCE_TOKEN;
|
|
|
|
return action;
|
|
|
|
|
|
|
|
case KW_STOGROUP:
|
|
|
|
PAR_error("DROP STOGROUP not supported");
|
|
|
|
|
|
|
|
case KW_TABLESPACE:
|
|
|
|
PAR_error("DROP TABLESPACE not supported");
|
|
|
|
|
|
|
|
case KW_SYNONYM:
|
|
|
|
PAR_error("DROP SYNONYM request not allowed");
|
|
|
|
|
|
|
|
case KW_SHADOW:
|
|
|
|
request = MAKE_REQUEST(REQ_ddl);
|
|
|
|
if (isc_databases && !isc_databases->dbb_next)
|
|
|
|
request->req_database = isc_databases;
|
|
|
|
else
|
|
|
|
PAR_error("Can only DROP SHADOW in context of a single database");
|
|
|
|
ADVANCE_TOKEN;
|
|
|
|
action = MAKE_ACTION(request, ACT_drop_shadow);
|
|
|
|
action->act_whenever = gen_whenever();
|
|
|
|
num = EXP_USHORT_ordinal(TRUE);
|
|
|
|
if (!RANGE_POSITIVE_SHORT_INTEGER(num))
|
|
|
|
PAR_error("Shadow number out of range");
|
|
|
|
action->act_object = (REF) num;
|
|
|
|
return action;
|
|
|
|
|
|
|
|
case KW_TABLE:
|
|
|
|
request = MAKE_REQUEST(REQ_ddl);
|
|
|
|
ADVANCE_TOKEN;
|
|
|
|
relation = par_relation(request);
|
|
|
|
action = MAKE_ACTION(request, ACT_drop_table);
|
|
|
|
action->act_whenever = gen_whenever();
|
|
|
|
action->act_object = (REF) relation;
|
|
|
|
return action;
|
|
|
|
|
|
|
|
case KW_VIEW:
|
|
|
|
request = MAKE_REQUEST(REQ_ddl);
|
|
|
|
ADVANCE_TOKEN;
|
|
|
|
relation = par_relation(request);
|
|
|
|
action = MAKE_ACTION(request, ACT_drop_view);
|
|
|
|
action->act_whenever = gen_whenever();
|
|
|
|
action->act_object = (REF) relation;
|
|
|
|
return action;
|
|
|
|
|
|
|
|
default:
|
|
|
|
PAR_error("Invalid DROP request");
|
|
|
|
}
|
|
|
|
return NULL; /* silence compiler */
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//____________________________________________________________
|
|
|
|
//
|
|
|
|
// Handle an SQL event statement
|
|
|
|
//
|
|
|
|
|
|
|
|
static ACT act_event(void)
|
|
|
|
{
|
|
|
|
ACT action;
|
|
|
|
|
|
|
|
if (MATCH(KW_INIT))
|
|
|
|
action = PAR_event_init((USHORT) TRUE);
|
|
|
|
else if (MATCH(KW_WAIT))
|
|
|
|
action = PAR_event_wait((USHORT) TRUE);
|
|
|
|
else
|
|
|
|
SYNTAX_ERROR("INIT or WAIT");
|
|
|
|
action->act_whenever = gen_whenever();
|
|
|
|
return action;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//____________________________________________________________
|
|
|
|
//
|
|
|
|
// Handle an SQL execute statement.
|
|
|
|
// Reject
|
|
|
|
//
|
|
|
|
|
|
|
|
static ACT act_execute(void)
|
|
|
|
{
|
|
|
|
DYN statement;
|
|
|
|
ACT action;
|
|
|
|
TEXT *transaction, s[ERROR_LENGTH];
|
|
|
|
|
|
|
|
if (MATCH(KW_PROCEDURE))
|
|
|
|
return act_procedure();
|
|
|
|
|
|
|
|
// EXECUTE IMMEDIATE is a different sort of duck
|
|
|
|
|
|
|
|
if (MATCH(KW_IMMEDIATE)) {
|
|
|
|
if (isc_databases && isc_databases->dbb_next) {
|
|
|
|
sprintf(s,
|
|
|
|
"Executing dynamic SQL statement in context of database %s",
|
|
|
|
isc_databases->dbb_name->sym_string);
|
|
|
|
CPR_warn(s);
|
|
|
|
}
|
|
|
|
statement = (DYN) ALLOC(DYN_LEN);
|
|
|
|
par_options(&statement->dyn_trans);
|
|
|
|
|
|
|
|
switch (sw_sql_dialect) {
|
|
|
|
case 1:
|
|
|
|
if ((!QUOTED(token.tok_type)) && (!MATCH(KW_COLON)))
|
|
|
|
SYNTAX_ERROR(": <string expression>");
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
if ((!SINGLE_QUOTED(token.tok_type)) && (!MATCH(KW_COLON)))
|
|
|
|
SYNTAX_ERROR(": <string expression>");
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
statement->dyn_string = PAR_native_value(FALSE, FALSE);
|
|
|
|
par_using(statement);
|
|
|
|
if (statement->dyn_using)
|
|
|
|
PAR_error("Using host-variable list not supported.");
|
|
|
|
par_into(statement);
|
|
|
|
statement->dyn_database = isc_databases;
|
|
|
|
action = (ACT) ALLOC(ACT_LEN);
|
|
|
|
action->act_type = ACT_dyn_immediate;
|
|
|
|
action->act_object = (REF) statement;
|
|
|
|
action->act_whenever = gen_whenever();
|
|
|
|
return action;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Ordinary form of EXECUTE
|
|
|
|
|
|
|
|
par_options(&transaction);
|
|
|
|
statement = par_statement();
|
|
|
|
statement->dyn_trans = transaction;
|
|
|
|
|
|
|
|
par_using(statement);
|
|
|
|
if (statement->dyn_using)
|
|
|
|
PAR_error("Using host-variable list not supported.");
|
|
|
|
par_into(statement);
|
|
|
|
|
|
|
|
action = (ACT) ALLOC(ACT_LEN);
|
|
|
|
action->act_type = ACT_dyn_execute;
|
|
|
|
action->act_object = (REF) statement;
|
|
|
|
action->act_whenever = gen_whenever();
|
|
|
|
|
|
|
|
return action;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//____________________________________________________________
|
|
|
|
//
|
|
|
|
// Parse the SQL fetch statement.
|
|
|
|
//
|
|
|
|
|
|
|
|
static ACT act_fetch(void)
|
|
|
|
{
|
2002-11-17 01:04:19 +01:00
|
|
|
GPRE_REQ request;
|
2001-05-23 15:26:42 +02:00
|
|
|
ACT action;
|
|
|
|
RSE select;
|
|
|
|
DYN statement, cursor;
|
|
|
|
#ifdef SCROLLABLE_CURSORS
|
|
|
|
USHORT direction;
|
|
|
|
REF reference;
|
|
|
|
VAL value;
|
|
|
|
TEXT *direction_string, *offset_string, *string;
|
2002-11-11 20:19:43 +01:00
|
|
|
GPRE_NOD offset_node = NULL;
|
2002-12-06 14:44:53 +01:00
|
|
|
#endif
|
2001-05-23 15:26:42 +02:00
|
|
|
|
|
|
|
// Handle dynamic SQL statement, if appropriate
|
|
|
|
|
|
|
|
if (cursor = par_dynamic_cursor()) {
|
|
|
|
statement = (DYN) ALLOC(DYN_LEN);
|
|
|
|
statement->dyn_statement_name = cursor->dyn_statement_name;
|
|
|
|
statement->dyn_cursor_name = cursor->dyn_cursor_name;
|
|
|
|
if (MATCH(KW_USING) || MATCH(KW_INTO)) {
|
|
|
|
MATCH(KW_SQL); /* optional for backward compatibility */
|
|
|
|
|
|
|
|
#pragma FB_COMPILER_MESSAGE("Fix! Wrong function ptr type!")
|
|
|
|
//
|
|
|
|
// Please search for "reinterpret_cast<pfn_SQE_list_cb>",
|
|
|
|
// there are more in this file.
|
|
|
|
//
|
|
|
|
if (MATCH(KW_DESCRIPTOR))
|
|
|
|
statement->dyn_sqlda = PAR_native_value(FALSE, FALSE);
|
|
|
|
else
|
|
|
|
statement->dyn_using =
|
2002-11-11 20:19:43 +01:00
|
|
|
(GPRE_NOD) SQE_list(reinterpret_cast < pfn_SQE_list_cb >
|
2001-05-23 15:26:42 +02:00
|
|
|
(SQE_variable), 0, FALSE);
|
|
|
|
if (statement->dyn_using)
|
|
|
|
PAR_error("Using host-variable list not supported.");
|
|
|
|
}
|
|
|
|
action = (ACT) ALLOC(ACT_LEN);
|
|
|
|
action->act_type = ACT_dyn_fetch;
|
|
|
|
action->act_object = (REF) statement;
|
|
|
|
action->act_whenever = gen_whenever();
|
|
|
|
return action;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Statement is static SQL
|
|
|
|
|
|
|
|
#ifdef SCROLLABLE_CURSORS
|
|
|
|
// parse the fetch orientation
|
|
|
|
|
|
|
|
direction = blr_forward;
|
|
|
|
|
|
|
|
if (!MATCH(KW_NEXT)) {
|
|
|
|
if (MATCH(KW_PRIOR)) {
|
|
|
|
direction = blr_backward;
|
|
|
|
direction_string = "1";
|
|
|
|
}
|
|
|
|
else if (MATCH(KW_FIRST)) {
|
|
|
|
direction = blr_bof_forward;
|
|
|
|
direction_string = "2";
|
|
|
|
}
|
|
|
|
else if (MATCH(KW_LAST)) {
|
|
|
|
direction = blr_eof_backward;
|
|
|
|
direction_string = "3";
|
|
|
|
}
|
|
|
|
else if (MATCH(KW_RELATIVE)) {
|
|
|
|
direction = blr_forward;
|
|
|
|
direction_string = "0";
|
|
|
|
offset_node = SQE_value(0, FALSE, 0, 0);
|
|
|
|
ADVANCE_TOKEN;
|
|
|
|
}
|
|
|
|
else if (MATCH(KW_ABSOLUTE)) {
|
|
|
|
direction = blr_bof_forward;
|
|
|
|
direction_string = "2";
|
|
|
|
offset_node = SQE_value(0, FALSE, 0, 0);
|
|
|
|
ADVANCE_TOKEN;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
MATCH(KW_FROM);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
request = par_cursor(NULL);
|
|
|
|
|
|
|
|
#ifdef SCROLLABLE_CURSORS
|
|
|
|
// if scrolling is required, set up the offset and direction parameters
|
|
|
|
// to be passed to the running request via the asynchronous message--
|
|
|
|
// there could be multiple FETCH statements, so we need to store multiple
|
|
|
|
// value blocks, one for each FETCH statement
|
|
|
|
|
|
|
|
if (direction != blr_forward) {
|
|
|
|
if (!(request->req_flags & REQ_scroll))
|
|
|
|
PAR_error
|
|
|
|
("Must use SCROLL modifier for DECLARE CURSOR to enable scrolling.");
|
|
|
|
|
|
|
|
/* create a literal for the direction parameter */
|
|
|
|
|
|
|
|
if (!(reference = request->req_avalues))
|
|
|
|
reference = request->req_avalues = (REF) ALLOC(REF_LEN);
|
|
|
|
|
|
|
|
if (!(value = reference->ref_values))
|
|
|
|
reference->ref_values = value = (VAL) ALLOC(VAL_LEN);
|
|
|
|
else {
|
|
|
|
while (value->val_next)
|
|
|
|
value = value->val_next;
|
|
|
|
value->val_next = (VAL) ALLOC(VAL_LEN);
|
|
|
|
value = value->val_next;
|
|
|
|
}
|
|
|
|
|
|
|
|
value->val_value = string = (TEXT *) ALLOC(2);
|
|
|
|
COPY(direction_string, 1, string);
|
|
|
|
|
|
|
|
/* create a reference to the offset variable or literal */
|
|
|
|
|
|
|
|
if (!reference->ref_next)
|
|
|
|
reference->ref_next = (REF) ALLOC(REF_LEN);
|
|
|
|
reference = reference->ref_next;
|
|
|
|
|
|
|
|
if (!(value = reference->ref_values))
|
|
|
|
reference->ref_values = value = (VAL) ALLOC(VAL_LEN);
|
|
|
|
else {
|
|
|
|
while (value->val_next)
|
|
|
|
value = value->val_next;
|
|
|
|
value->val_next = (VAL) ALLOC(VAL_LEN);
|
|
|
|
value = value->val_next;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (offset_node)
|
|
|
|
value->val_value = ((REF) offset_node->nod_arg[0])->ref_value;
|
|
|
|
else {
|
|
|
|
offset_string = "1";
|
|
|
|
value->val_value = string = (TEXT *) ALLOC(2);
|
|
|
|
COPY(offset_string, 1, string);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
if (request->req_flags & REQ_sql_blob_create)
|
|
|
|
PAR_error("Fetching from a blob being created is not allowed.");
|
|
|
|
|
|
|
|
action = MAKE_ACTION(request, ACT_fetch);
|
|
|
|
action->act_whenever = gen_whenever();
|
|
|
|
|
|
|
|
if (request->req_flags & REQ_sql_blob_open) {
|
|
|
|
if (!MATCH(KW_INTO))
|
|
|
|
SYNTAX_ERROR("INTO");
|
|
|
|
action->act_object = SQE_variable(0, FALSE);
|
|
|
|
action->act_type = ACT_get_segment;
|
|
|
|
}
|
|
|
|
else if (MATCH(KW_INTO)) {
|
|
|
|
action->act_object =
|
|
|
|
(REF) SQE_list(reinterpret_cast < pfn_SQE_list_cb >
|
|
|
|
(SQE_variable), request, FALSE);
|
|
|
|
select = request->req_rse;
|
2002-11-11 20:19:43 +01:00
|
|
|
into(request, select->rse_fields, (GPRE_NOD) action->act_object);
|
2001-05-23 15:26:42 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
return action;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//____________________________________________________________
|
|
|
|
//
|
|
|
|
// Parse an SQL grant or revoke statement. Set up grant/revoke
|
|
|
|
// blocks, fill in all of the privilege information, and
|
|
|
|
// attach them to an action block of type GRANT or REVOKE.
|
|
|
|
//
|
|
|
|
|
|
|
|
static ACT act_grant_revoke( enum act_t type)
|
|
|
|
{
|
2002-11-17 01:04:19 +01:00
|
|
|
GPRE_REQ request;
|
2001-05-23 15:26:42 +02:00
|
|
|
ACT action;
|
|
|
|
USN user, usernames;
|
|
|
|
PRV priv_block, last_priv_block;
|
2002-11-17 01:04:19 +01:00
|
|
|
GPRE_REL relation;
|
|
|
|
GPRE_PRC procedure;
|
2001-05-23 15:26:42 +02:00
|
|
|
USHORT user_dyn;
|
|
|
|
SCHAR col_name[NAME_SIZE + 1], r_name[NAME_SIZE + 1],
|
|
|
|
db_name[NAME_SIZE + 1], owner_name[NAME_SIZE + 1];
|
|
|
|
LLS *fields;
|
|
|
|
STR field_name, relation_name;
|
|
|
|
BOOLEAN first;
|
|
|
|
BOOLEAN grant_option_legal = TRUE;
|
|
|
|
BOOLEAN execute_priv = FALSE;
|
|
|
|
SCHAR s[ERROR_LENGTH];
|
|
|
|
|
|
|
|
request = MAKE_REQUEST(REQ_ddl);
|
|
|
|
|
|
|
|
priv_block = MAKE_PRIVILEGE_BLOCK();
|
|
|
|
fields = &priv_block->prv_fields;
|
|
|
|
|
|
|
|
// if it is revoke action, parse the optional grant option for
|
|
|
|
|
|
|
|
if (type == ACT_dyn_revoke)
|
|
|
|
if (MATCH(KW_GRANT)) {
|
|
|
|
if (!MATCH(KW_OPTION))
|
|
|
|
SYNTAX_ERROR("OPTION");
|
|
|
|
if (!MATCH(KW_FOR))
|
|
|
|
SYNTAX_ERROR("FOR");
|
|
|
|
priv_block->prv_privileges |= PRV_grant_option;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (MATCH(KW_ALL)) {
|
|
|
|
MATCH(KW_PRIVILEGES); /* Keyword 'privileges' is optional */
|
|
|
|
priv_block->prv_privileges = PRV_all;
|
|
|
|
}
|
|
|
|
else if (MATCH(KW_EXECUTE)) {
|
|
|
|
priv_block->prv_privileges |= PRV_execute;
|
|
|
|
execute_priv = TRUE;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
while (TRUE) {
|
|
|
|
if (MATCH(KW_SELECT))
|
|
|
|
priv_block->prv_privileges |= PRV_select;
|
|
|
|
else if (MATCH(KW_INSERT))
|
|
|
|
priv_block->prv_privileges |= PRV_insert;
|
|
|
|
else if (MATCH(KW_DELETE))
|
|
|
|
priv_block->prv_privileges |= PRV_delete;
|
|
|
|
else if (MATCH(KW_UPDATE)) {
|
|
|
|
priv_block->prv_privileges |= PRV_update;
|
|
|
|
if (MATCH(KW_LEFT_PAREN)) {
|
|
|
|
do {
|
|
|
|
SQL_resolve_identifier("<column name>", col_name);
|
|
|
|
field_name = (STR) MAKE_STRING(col_name);
|
2002-11-11 20:19:43 +01:00
|
|
|
PUSH((GPRE_NOD) field_name, fields);
|
2001-05-23 15:26:42 +02:00
|
|
|
fields = &(*fields)->lls_next;
|
|
|
|
CPR_token();
|
|
|
|
} while (MATCH(KW_COMMA));
|
|
|
|
|
|
|
|
if (!MATCH(KW_RIGHT_PAREN))
|
|
|
|
SYNTAX_ERROR("<right parenthesis>");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!MATCH(KW_COMMA))
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!MATCH(KW_ON))
|
|
|
|
SYNTAX_ERROR("ON");
|
|
|
|
|
|
|
|
if (execute_priv) {
|
|
|
|
if (!MATCH(KW_PROCEDURE))
|
|
|
|
SYNTAX_ERROR("PROCEDURE");
|
|
|
|
SQL_relation_name(r_name, db_name, owner_name);
|
|
|
|
procedure = SQL_procedure(request, r_name, db_name, owner_name, TRUE);
|
|
|
|
relation_name = (STR) MAKE_STRING(r_name);
|
|
|
|
priv_block->prv_relation = relation_name;
|
|
|
|
priv_block->prv_object_dyn = gds_dyn_prc_name;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
MATCH(KW_TABLE); /* filler word */
|
|
|
|
SQL_relation_name(r_name, db_name, owner_name);
|
|
|
|
relation = SQL_relation(request, r_name, db_name, owner_name, TRUE);
|
|
|
|
relation_name = (STR) MAKE_STRING(r_name);
|
|
|
|
priv_block->prv_relation = relation_name;
|
|
|
|
priv_block->prv_object_dyn = gds_dyn_rel_name;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (type == ACT_dyn_grant) {
|
|
|
|
if (!MATCH(KW_TO))
|
|
|
|
SYNTAX_ERROR("TO");
|
|
|
|
}
|
|
|
|
else { /* type == ACT_dyn_revoke */
|
|
|
|
|
|
|
|
if (!MATCH(KW_FROM))
|
|
|
|
SYNTAX_ERROR("FROM");
|
|
|
|
}
|
|
|
|
|
|
|
|
usernames = 0;
|
|
|
|
user = 0;
|
|
|
|
while (TRUE) {
|
|
|
|
if (MATCH(KW_PROCEDURE)) {
|
|
|
|
SQL_relation_name(r_name, db_name, owner_name);
|
|
|
|
procedure =
|
|
|
|
SQL_procedure(request, r_name, db_name, owner_name, TRUE);
|
|
|
|
user_dyn = gds_dyn_grant_proc;
|
|
|
|
grant_option_legal = FALSE;
|
|
|
|
}
|
|
|
|
else if (MATCH(KW_TRIGGER)) {
|
|
|
|
SQL_relation_name(r_name, db_name, owner_name);
|
|
|
|
if (!MET_trigger_exists(request->req_database, r_name)) {
|
|
|
|
sprintf(s, "TRIGGER %s not defined", r_name);
|
|
|
|
PAR_error(s);
|
|
|
|
}
|
|
|
|
user_dyn = gds_dyn_grant_trig;
|
|
|
|
grant_option_legal = FALSE;
|
|
|
|
}
|
|
|
|
else if (MATCH(KW_VIEW)) {
|
|
|
|
SQL_relation_name(r_name, db_name, owner_name);
|
|
|
|
if (!MET_get_view_relation
|
|
|
|
(request, r_name, (char *) relation_name, 0)) {
|
|
|
|
sprintf(s, "VIEW %s not defined on table %s", r_name,
|
|
|
|
relation_name);
|
|
|
|
PAR_error(s);
|
|
|
|
}
|
|
|
|
user_dyn = gds_dyn_grant_view;
|
|
|
|
grant_option_legal = FALSE;
|
|
|
|
}
|
|
|
|
else if (MATCH(KW_PUBLIC)) {
|
|
|
|
strcpy(r_name, "PUBLIC");
|
|
|
|
user_dyn = gds_dyn_grant_user;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
MATCH(KW_USER);
|
|
|
|
if (token.tok_type != tok_ident)
|
|
|
|
SYNTAX_ERROR("<user name identifier>");
|
|
|
|
else
|
|
|
|
to_upcase(token.tok_string, r_name);
|
|
|
|
user_dyn = gds_dyn_grant_user;
|
|
|
|
CPR_token();
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!usernames) {
|
|
|
|
usernames = MAKE_USERNAME(r_name, user_dyn);
|
|
|
|
user = usernames;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
user->usn_next = MAKE_USERNAME(r_name, user_dyn);
|
|
|
|
user = user->usn_next;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!MATCH(KW_COMMA))
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
// If this is a grant, do we have the optional WITH GRANT OPTION specification?
|
|
|
|
|
|
|
|
if ((type == ACT_dyn_grant) && grant_option_legal)
|
|
|
|
if (MATCH(KW_WITH)) {
|
|
|
|
if (!MATCH(KW_GRANT))
|
|
|
|
SYNTAX_ERROR("GRANT");
|
|
|
|
if (!MATCH(KW_OPTION))
|
|
|
|
SYNTAX_ERROR("OPTION");
|
|
|
|
priv_block->prv_privileges |= PRV_grant_option;
|
|
|
|
}
|
|
|
|
|
|
|
|
// create action block
|
|
|
|
|
|
|
|
action = MAKE_ACTION(request, type);
|
|
|
|
action->act_next = 0;
|
|
|
|
|
|
|
|
last_priv_block = priv_block;
|
|
|
|
first = TRUE;
|
|
|
|
|
|
|
|
for (user = usernames; user; user = user->usn_next) {
|
|
|
|
/* create and fill privilege block */
|
|
|
|
|
|
|
|
priv_block = MAKE_PRIVILEGE_BLOCK();
|
|
|
|
priv_block->prv_username = user->usn_name;
|
|
|
|
priv_block->prv_user_dyn = user->usn_dyn;
|
|
|
|
priv_block->prv_privileges = last_priv_block->prv_privileges;
|
|
|
|
priv_block->prv_relation = last_priv_block->prv_relation;
|
|
|
|
priv_block->prv_object_dyn = last_priv_block->prv_object_dyn;
|
|
|
|
priv_block->prv_fields = last_priv_block->prv_fields;
|
|
|
|
if (first) {
|
|
|
|
action->act_object = (REF) priv_block;
|
|
|
|
first = FALSE;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
last_priv_block->prv_next = priv_block;
|
|
|
|
last_priv_block = priv_block;
|
|
|
|
}
|
|
|
|
action->act_request = request;
|
|
|
|
action->act_whenever = gen_whenever();
|
|
|
|
|
|
|
|
return action;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//____________________________________________________________
|
|
|
|
//
|
|
|
|
//
|
|
|
|
|
|
|
|
static ACT act_include(void)
|
|
|
|
{
|
|
|
|
ACT action;
|
|
|
|
SYM symbol;
|
|
|
|
|
|
|
|
ADVANCE_TOKEN;
|
|
|
|
MATCH(KW_SEMI_COLON);
|
|
|
|
|
|
|
|
action = (ACT) ALLOC(ACT_LEN);
|
|
|
|
action->act_type = ACT_b_declare;
|
|
|
|
cur_routine = action;
|
|
|
|
|
|
|
|
if (!isc_databases) {
|
|
|
|
/* allocate database block and link to db chain */
|
|
|
|
|
|
|
|
isc_databases = (DBB) MSC_alloc_permanent(DBB_LEN);
|
|
|
|
|
|
|
|
/* allocate symbol block */
|
|
|
|
|
|
|
|
symbol = (SYM) MSC_alloc_permanent(SYM_LEN);
|
|
|
|
|
|
|
|
/* make it a database, specifically this one */
|
|
|
|
|
|
|
|
symbol->sym_type = SYM_database;
|
2002-11-17 01:04:19 +01:00
|
|
|
symbol->sym_object = (GPRE_CTX) isc_databases;
|
2001-05-23 15:26:42 +02:00
|
|
|
symbol->sym_string = database_name;
|
|
|
|
|
|
|
|
/* database block points to the symbol block */
|
|
|
|
|
|
|
|
isc_databases->dbb_name = symbol;
|
|
|
|
isc_databases->dbb_filename = NULL;
|
|
|
|
isc_databases->dbb_flags = DBB_sqlca;
|
|
|
|
isc_databases->dbb_c_lc_ctype = module_lc_ctype;
|
|
|
|
if (sw_external)
|
|
|
|
isc_databases->dbb_scope = DBB_EXTERN;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
/* Load the symbol (hash) table with relation names from this database. */
|
|
|
|
MET_load_hash_table(isc_databases);
|
|
|
|
|
|
|
|
HSH_insert(isc_databases->dbb_name);
|
|
|
|
|
|
|
|
return action;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//____________________________________________________________
|
|
|
|
//
|
|
|
|
// Process SQL INSERT statement.
|
|
|
|
//
|
|
|
|
|
|
|
|
static ACT act_insert(void)
|
|
|
|
{
|
2002-11-17 01:04:19 +01:00
|
|
|
GPRE_REQ request;
|
2001-05-23 15:26:42 +02:00
|
|
|
ACT action;
|
2002-11-17 01:04:19 +01:00
|
|
|
GPRE_CTX context;
|
2001-05-23 15:26:42 +02:00
|
|
|
LLS fields, values;
|
|
|
|
RSE select;
|
2002-11-11 20:19:43 +01:00
|
|
|
GPRE_NOD node, store, assignment, list, select_list, *ptr, *ptr2;
|
2001-05-23 15:26:42 +02:00
|
|
|
TEXT *transaction;
|
|
|
|
int count, count2, i;
|
|
|
|
|
|
|
|
par_options(&transaction);
|
|
|
|
|
|
|
|
if (MATCH(KW_CURSOR))
|
|
|
|
return act_insert_blob(transaction);
|
|
|
|
|
|
|
|
if (!MATCH(KW_INTO))
|
|
|
|
SYNTAX_ERROR("INTO");
|
|
|
|
|
|
|
|
request = MAKE_REQUEST(REQ_insert);
|
|
|
|
request->req_trans = transaction;
|
|
|
|
context = SQE_context(request);
|
|
|
|
count = count2 = 0;
|
|
|
|
fields = values = NULL;
|
|
|
|
|
|
|
|
// Pick up a field list
|
|
|
|
|
|
|
|
if (!MATCH(KW_LEFT_PAREN)) {
|
|
|
|
list = MET_fields(context);
|
|
|
|
count = list->nod_count;
|
|
|
|
for (i = 0; i < count; i++)
|
|
|
|
PUSH(list->nod_arg[i], &fields);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
do {
|
|
|
|
node = SQE_field(request, FALSE);
|
|
|
|
if (node->nod_type == nod_array) {
|
|
|
|
node->nod_type = nod_field;
|
|
|
|
|
|
|
|
/* Make sure no subscripts are specified */
|
|
|
|
|
|
|
|
if (node->nod_arg[1]) {
|
|
|
|
PAR_error("Partial insert of arrays not permitted");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Dialect 1 program may not insert new datatypes */
|
|
|
|
if ((SQL_DIALECT_V5 == sw_sql_dialect) &&
|
|
|
|
(nod_field == node->nod_type)) {
|
|
|
|
USHORT field_dtype;
|
|
|
|
|
|
|
|
field_dtype =
|
|
|
|
((REF) (node->nod_arg[0]))->ref_field->fld_dtype;
|
|
|
|
if ((dtype_sql_date == field_dtype)
|
|
|
|
|| (dtype_sql_time == field_dtype)
|
|
|
|
|| (dtype_int64 == field_dtype)) {
|
|
|
|
dialect1_bad_type(field_dtype);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
PUSH(node, &fields);
|
|
|
|
count++;
|
|
|
|
} while (MATCH(KW_COMMA));
|
|
|
|
|
|
|
|
EXP_match_paren();
|
|
|
|
}
|
|
|
|
|
|
|
|
if (MATCH(KW_VALUES)) {
|
|
|
|
/* Now pick up a value list */
|
|
|
|
|
|
|
|
EXP_left_paren(0);
|
|
|
|
for (;;) {
|
|
|
|
if (MATCH(KW_NULL))
|
|
|
|
PUSH(MAKE_NODE(nod_null, 0), &values);
|
|
|
|
else
|
|
|
|
PUSH(SQE_value(request, FALSE, 0, 0), &values);
|
|
|
|
count2++;
|
|
|
|
if (!(MATCH(KW_COMMA)))
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
EXP_match_paren();
|
|
|
|
|
|
|
|
/* Make an assignment list */
|
|
|
|
|
|
|
|
if (count != count2)
|
|
|
|
PAR_error("count of values doesn't match count of columns");
|
|
|
|
|
|
|
|
request->req_node = list = MAKE_NODE(nod_list, (SSHORT) count);
|
|
|
|
ptr = &list->nod_arg[count];
|
|
|
|
|
|
|
|
while (values) {
|
|
|
|
assignment = MAKE_NODE(nod_assignment, 2);
|
2002-11-11 20:19:43 +01:00
|
|
|
assignment->nod_arg[0] = (GPRE_NOD) POP(&values);
|
|
|
|
assignment->nod_arg[1] = (GPRE_NOD) POP(&fields);
|
2001-05-23 15:26:42 +02:00
|
|
|
pair(assignment->nod_arg[0], assignment->nod_arg[1]);
|
|
|
|
*--ptr = assignment;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (context->ctx_symbol)
|
|
|
|
HSH_remove(context->ctx_symbol);
|
|
|
|
action = MAKE_ACTION(request, ACT_insert);
|
|
|
|
action->act_whenever = gen_whenever();
|
|
|
|
return action;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!MATCH(KW_SELECT))
|
|
|
|
SYNTAX_ERROR("VALUES or SELECT");
|
|
|
|
|
|
|
|
// OK, we've got a mass insert on our hands. Start by picking
|
|
|
|
// up the select statement. First, however, remove the INSERT
|
|
|
|
// context to avoid resolving SELECT fields to the insert relation.
|
|
|
|
|
|
|
|
request->req_type = REQ_mass_update;
|
|
|
|
request->req_contexts = NULL;
|
|
|
|
request->req_update = context;
|
|
|
|
request->req_rse = select = SQE_select(request, FALSE);
|
|
|
|
context->ctx_next = request->req_contexts;
|
|
|
|
request->req_contexts = context;
|
|
|
|
|
|
|
|
// Build an assignment list from select expressions into target list
|
|
|
|
|
|
|
|
select_list = select->rse_fields;
|
|
|
|
|
|
|
|
if (count != select_list->nod_count)
|
|
|
|
PAR_error("count of values doesn't match count of columns");
|
|
|
|
|
|
|
|
request->req_node = list = MAKE_NODE(nod_list, (SSHORT) count);
|
|
|
|
ptr = &list->nod_arg[count];
|
|
|
|
ptr2 = &select_list->nod_arg[count];
|
|
|
|
|
|
|
|
while (fields) {
|
|
|
|
assignment = MAKE_NODE(nod_assignment, 2);
|
|
|
|
assignment->nod_arg[0] = *--ptr2;
|
2002-11-11 20:19:43 +01:00
|
|
|
assignment->nod_arg[1] = (GPRE_NOD) POP(&fields);
|
2001-05-23 15:26:42 +02:00
|
|
|
pair(assignment->nod_arg[0], assignment->nod_arg[1]);
|
|
|
|
*--ptr = assignment;
|
|
|
|
}
|
|
|
|
|
2002-11-11 20:19:43 +01:00
|
|
|
store = MSC_binary(nod_store, (GPRE_NOD) context, list);
|
2001-05-23 15:26:42 +02:00
|
|
|
request->req_node = store;
|
|
|
|
EXP_rse_cleanup(select);
|
|
|
|
if (context->ctx_symbol)
|
|
|
|
HSH_remove(context->ctx_symbol);
|
|
|
|
|
|
|
|
action = MAKE_ACTION(request, ACT_loop);
|
|
|
|
action->act_whenever = gen_whenever();
|
|
|
|
|
|
|
|
return action;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//____________________________________________________________
|
|
|
|
//
|
|
|
|
// Process SQL INSERT statement.
|
|
|
|
//
|
|
|
|
|
|
|
|
static ACT act_insert_blob( TEXT * transaction)
|
|
|
|
{
|
2002-11-17 01:04:19 +01:00
|
|
|
GPRE_REQ request;
|
2001-05-23 15:26:42 +02:00
|
|
|
ACT action;
|
|
|
|
DYN statement, cursor;
|
|
|
|
|
|
|
|
// Handle dynamic SQL statement, if appropriate
|
|
|
|
|
|
|
|
if (cursor = par_dynamic_cursor()) {
|
|
|
|
statement = (DYN) ALLOC(DYN_LEN);
|
|
|
|
statement->dyn_statement_name = cursor->dyn_statement_name;
|
|
|
|
statement->dyn_cursor_name = cursor->dyn_cursor_name;
|
|
|
|
par_using(statement);
|
|
|
|
if (statement->dyn_using)
|
|
|
|
PAR_error("Using host-variable list not supported.");
|
|
|
|
action = (ACT) ALLOC(ACT_LEN);
|
|
|
|
action->act_type = ACT_dyn_insert;
|
|
|
|
action->act_object = (REF) statement;
|
|
|
|
action->act_whenever = gen_whenever();
|
|
|
|
return action;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Statement is static SQL
|
|
|
|
|
|
|
|
request = par_cursor(NULL);
|
|
|
|
if (request->req_flags & REQ_sql_blob_open)
|
|
|
|
PAR_error("Inserting into a blob being opened is not allowed.");
|
|
|
|
action = MAKE_ACTION(request, ACT_put_segment);
|
|
|
|
action->act_whenever = gen_whenever();
|
|
|
|
|
|
|
|
if (!MATCH(KW_VALUES))
|
|
|
|
SYNTAX_ERROR("VALUES");
|
|
|
|
|
|
|
|
EXP_left_paren(0);
|
|
|
|
action->act_object = SQE_variable(0, FALSE);
|
|
|
|
if (!action->act_object->ref_null_value)
|
|
|
|
PAR_error("A segment length is required.");
|
|
|
|
EXP_match_paren();
|
|
|
|
|
|
|
|
return action;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//____________________________________________________________
|
|
|
|
//
|
|
|
|
// Handle an SQL lock statement.
|
|
|
|
// Reject
|
|
|
|
//
|
|
|
|
|
|
|
|
static ACT act_lock(void)
|
|
|
|
{
|
|
|
|
|
|
|
|
PAR_error("SQL LOCK TABLE request not allowed");
|
|
|
|
return NULL; /* silence compiler */
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//____________________________________________________________
|
|
|
|
//
|
|
|
|
// Handle the SQL actions OPEN and CLOSE cursors.
|
|
|
|
//
|
|
|
|
|
|
|
|
static ACT act_openclose( enum act_t type)
|
|
|
|
{
|
|
|
|
ACT action;
|
2002-11-17 01:04:19 +01:00
|
|
|
GPRE_REQ request;
|
2001-05-23 15:26:42 +02:00
|
|
|
DYN statement, cursor;
|
|
|
|
SYM symbol;
|
|
|
|
OPN open;
|
|
|
|
REF using_;
|
|
|
|
TEXT *transaction;
|
|
|
|
|
|
|
|
if (type == ACT_open)
|
|
|
|
par_options(&transaction);
|
|
|
|
|
|
|
|
// Handle dynamic SQL statement, if appropriate
|
|
|
|
|
|
|
|
if (cursor = par_dynamic_cursor()) {
|
|
|
|
statement = (DYN) ALLOC(DYN_LEN);
|
|
|
|
statement->dyn_statement_name = cursor->dyn_statement_name;
|
|
|
|
statement->dyn_cursor_name = cursor->dyn_cursor_name;
|
|
|
|
action = (ACT) ALLOC(ACT_LEN);
|
|
|
|
action->act_object = (REF) statement;
|
|
|
|
action->act_whenever = gen_whenever();
|
|
|
|
if (type == ACT_open) {
|
|
|
|
action->act_type = ACT_dyn_open;
|
|
|
|
statement->dyn_trans = transaction;
|
|
|
|
par_using(statement);
|
|
|
|
if (statement->dyn_using)
|
|
|
|
PAR_error("Using host-variable list not supported.");
|
|
|
|
par_into(statement);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
action->act_type = ACT_dyn_close;
|
|
|
|
return action;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Statement is static SQL
|
|
|
|
|
|
|
|
request = par_cursor(&symbol);
|
|
|
|
|
|
|
|
action = MAKE_ACTION(request, type);
|
|
|
|
if (type == ACT_open) {
|
|
|
|
open = (OPN) ALLOC(OPN_LEN);
|
|
|
|
open->opn_trans = transaction;
|
|
|
|
open->opn_cursor = symbol;
|
|
|
|
action->act_object = (REF) open;
|
|
|
|
if (transaction != NULL)
|
|
|
|
request->req_trans = transaction;
|
|
|
|
if (request->req_flags & (REQ_sql_blob_open | REQ_sql_blob_create)) {
|
|
|
|
if (request->req_flags & REQ_sql_blob_open) {
|
|
|
|
if (!MATCH(KW_USING))
|
|
|
|
SYNTAX_ERROR("USING");
|
|
|
|
}
|
|
|
|
else if (!MATCH(KW_INTO))
|
|
|
|
SYNTAX_ERROR("INTO");
|
|
|
|
open->opn_using = using_ = SQE_variable(0, FALSE);
|
|
|
|
using_->ref_next = request->req_blobs->blb_reference;
|
|
|
|
request->req_blobs->blb_reference = using_;
|
|
|
|
using_->ref_context = request->req_contexts;
|
|
|
|
using_->ref_field = request->req_references->ref_field;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
action->act_object = (REF) symbol;
|
|
|
|
action->act_whenever = gen_whenever();
|
|
|
|
|
|
|
|
if (request->req_flags & (REQ_sql_blob_open | REQ_sql_blob_create))
|
|
|
|
action->act_type = (type == ACT_close) ? ACT_blob_close :
|
|
|
|
(request->
|
|
|
|
req_flags & REQ_sql_blob_open) ? ACT_blob_open : ACT_blob_create;
|
|
|
|
|
|
|
|
return action;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//____________________________________________________________
|
|
|
|
//
|
|
|
|
// Parse an "open blob" type statement.
|
|
|
|
// These include READ BLOB and INSERT BLOB.
|
|
|
|
//
|
|
|
|
|
|
|
|
static ACT act_open_blob( ACT_T act_op, SYM symbol)
|
|
|
|
{
|
|
|
|
TOK f_token;
|
|
|
|
SCHAR r_name[NAME_SIZE + 1], db_name[NAME_SIZE + 1],
|
|
|
|
owner_name[NAME_SIZE + 1];
|
2002-11-17 01:04:19 +01:00
|
|
|
GPRE_REQ request;
|
|
|
|
GPRE_REL relation;
|
2002-11-30 18:45:02 +01:00
|
|
|
GPRE_FLD field;
|
2001-05-23 15:26:42 +02:00
|
|
|
REF reference;
|
|
|
|
BLB blob;
|
2002-11-17 01:04:19 +01:00
|
|
|
GPRE_CTX context;
|
2001-05-23 15:26:42 +02:00
|
|
|
SCHAR s[128];
|
|
|
|
ACT action;
|
|
|
|
|
|
|
|
if (!MATCH(KW_BLOB))
|
|
|
|
SYNTAX_ERROR("BLOB");
|
|
|
|
|
|
|
|
// if the token isn't an identifier, complain
|
|
|
|
|
|
|
|
f_token = (TOK) ALLOC(TOK_LEN);
|
|
|
|
f_token->tok_length = token.tok_length;
|
|
|
|
|
|
|
|
SQL_resolve_identifier("<column_name>", f_token->tok_string);
|
|
|
|
CPR_token();
|
|
|
|
|
|
|
|
if (act_op == ACT_blob_open) {
|
|
|
|
if (!MATCH(KW_FROM))
|
|
|
|
SYNTAX_ERROR("FROM");
|
|
|
|
}
|
|
|
|
else if (!MATCH(KW_INTO))
|
|
|
|
SYNTAX_ERROR("INTO");
|
|
|
|
|
|
|
|
request = MAKE_REQUEST(REQ_cursor);
|
|
|
|
request->req_flags =
|
|
|
|
(act_op == ACT_blob_open) ? REQ_sql_blob_open : REQ_sql_blob_create;
|
|
|
|
|
|
|
|
SQL_relation_name(r_name, db_name, owner_name);
|
|
|
|
relation = SQL_relation(request, r_name, db_name, owner_name, TRUE);
|
|
|
|
|
|
|
|
if (!(field = MET_field(relation, f_token->tok_string))) {
|
|
|
|
sprintf(s, "column \"%s\" not in context", f_token->tok_string);
|
|
|
|
PAR_error(s);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!(field->fld_flags & FLD_blob)) {
|
|
|
|
sprintf(s, "column %s is not a BLOB", field->fld_symbol->sym_string);
|
|
|
|
PAR_error(s);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (field->fld_array_info) {
|
|
|
|
sprintf(s, "column %s is an array and can not be opened as a BLOB",
|
|
|
|
field->fld_symbol->sym_string);
|
|
|
|
PAR_error(s);
|
|
|
|
}
|
|
|
|
|
|
|
|
reference = MAKE_REFERENCE(0);
|
|
|
|
reference->ref_field = field;
|
|
|
|
|
|
|
|
context = MAKE_CONTEXT(request);
|
|
|
|
context->ctx_relation = relation;
|
|
|
|
|
|
|
|
blob = (BLB) ALLOC(BLB_LEN);
|
|
|
|
blob->blb_symbol = symbol;
|
|
|
|
blob->blb_request = request;
|
|
|
|
blob->blb_next = request->req_blobs;
|
2002-11-17 01:04:19 +01:00
|
|
|
symbol->sym_object = (GPRE_CTX) request;
|
2001-05-23 15:26:42 +02:00
|
|
|
symbol->sym_type = SYM_cursor;
|
|
|
|
|
|
|
|
request->req_references = reference;
|
|
|
|
request->req_blobs = blob;
|
|
|
|
|
|
|
|
if (MATCH(KW_FILTER)) {
|
|
|
|
if (MATCH(KW_FROM)) {
|
|
|
|
blob->blb_const_from_type =
|
|
|
|
PAR_blob_subtype(request->req_database);
|
|
|
|
if (token.tok_keyword == KW_CHAR)
|
|
|
|
if (blob->blb_const_from_type == BLOB_text) {
|
|
|
|
blob->blb_from_charset = par_char_set();
|
|
|
|
if (act_op == ACT_blob_open
|
|
|
|
&& blob->blb_from_charset != field->fld_charset_id)
|
|
|
|
PAR_error
|
|
|
|
("Specified CHARACTER SET does not match BLOB column declaration.");
|
|
|
|
}
|
|
|
|
else
|
|
|
|
PAR_error("Only text BLOBS can specify CHARACTER SET");
|
|
|
|
else if (blob->blb_const_from_type == BLOB_text)
|
|
|
|
if (act_op == ACT_blob_create)
|
|
|
|
blob->blb_from_charset = CS_dynamic;
|
|
|
|
else
|
|
|
|
blob->blb_from_charset = field->fld_charset_id;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
blob->blb_const_from_type = field->fld_sub_type;
|
|
|
|
if (blob->blb_const_from_type == BLOB_text)
|
|
|
|
if (act_op == ACT_blob_create)
|
|
|
|
blob->blb_from_charset = CS_dynamic;
|
|
|
|
else
|
|
|
|
blob->blb_from_charset = field->fld_charset_id;
|
|
|
|
}
|
|
|
|
if (!MATCH(KW_TO))
|
|
|
|
SYNTAX_ERROR("TO");
|
|
|
|
blob->blb_const_to_type = PAR_blob_subtype(request->req_database);
|
|
|
|
if (token.tok_keyword == KW_CHAR)
|
|
|
|
if (blob->blb_const_to_type == BLOB_text) {
|
|
|
|
blob->blb_to_charset = par_char_set();
|
|
|
|
if (act_op == ACT_blob_create
|
|
|
|
&& blob->blb_to_charset != field->fld_charset_id)
|
|
|
|
PAR_error
|
|
|
|
("Specified CHARACTER SET does not match BLOB column declaration.");
|
|
|
|
}
|
|
|
|
else
|
|
|
|
PAR_error("Only text BLOBS can specify CHARACTER SET");
|
|
|
|
else if (blob->blb_const_to_type == BLOB_text)
|
|
|
|
if (act_op == ACT_blob_create)
|
|
|
|
blob->blb_to_charset = field->fld_charset_id;
|
|
|
|
else
|
|
|
|
blob->blb_to_charset = CS_dynamic;
|
|
|
|
}
|
|
|
|
else { /* No FILTER keyword seen */
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Even if no FILTER was specified, we set one up for the special
|
|
|
|
* case of InterBase TEXT blobs, in order to do character set
|
|
|
|
* transliteration from the column-declared character set of the
|
|
|
|
* blob to the process character set (CS_dynamic).
|
|
|
|
*
|
|
|
|
* It is necessary to pass this information in the bpb as blob
|
|
|
|
* operations are done using blob_ids, and we cannot determine
|
|
|
|
* this information from the blob_id within the engine.
|
|
|
|
*/
|
|
|
|
if (field->fld_sub_type == BLOB_text
|
|
|
|
&& (field->fld_charset_id != CS_NONE)) {
|
|
|
|
blob->blb_const_from_type = BLOB_text;
|
|
|
|
blob->blb_const_to_type = BLOB_text;
|
|
|
|
if (act_op == ACT_blob_create) {
|
|
|
|
blob->blb_from_charset = CS_dynamic;
|
|
|
|
blob->blb_to_charset = field->fld_charset_id;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
blob->blb_from_charset = field->fld_charset_id;
|
|
|
|
blob->blb_to_charset = CS_dynamic;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (MATCH(KW_MAX_SEGMENT))
|
|
|
|
blob->blb_seg_length = EXP_USHORT_ordinal(TRUE);
|
|
|
|
else
|
|
|
|
blob->blb_seg_length = field->fld_seg_length;
|
|
|
|
|
|
|
|
if (!blob->blb_seg_length)
|
|
|
|
blob->blb_seg_length = 512;
|
|
|
|
|
|
|
|
action = MAKE_ACTION(request, ACT_cursor);
|
|
|
|
action->act_object = (REF) blob;
|
|
|
|
|
|
|
|
return action;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//____________________________________________________________
|
|
|
|
//
|
|
|
|
// Handle an SQL prepare statement.
|
|
|
|
//
|
|
|
|
|
|
|
|
static ACT act_prepare(void)
|
|
|
|
{
|
|
|
|
DYN statement;
|
|
|
|
ACT action;
|
|
|
|
TEXT s[ERROR_LENGTH];
|
|
|
|
TEXT *transaction;
|
|
|
|
|
|
|
|
if (isc_databases && isc_databases->dbb_next) {
|
|
|
|
sprintf(s,
|
|
|
|
"Executing dynamic SQL statement in context of database %s",
|
|
|
|
isc_databases->dbb_name->sym_string);
|
|
|
|
CPR_warn(s);
|
|
|
|
}
|
|
|
|
|
|
|
|
par_options(&transaction);
|
|
|
|
statement = par_statement();
|
|
|
|
statement->dyn_database = isc_databases;
|
|
|
|
statement->dyn_trans = transaction;
|
|
|
|
|
|
|
|
if (MATCH(KW_INTO)) {
|
|
|
|
if (MATCH(KW_SQL) && !MATCH(KW_DESCRIPTOR))
|
|
|
|
SYNTAX_ERROR("INTO SQL DESCRIPTOR sqlda");
|
|
|
|
statement->dyn_sqlda = PAR_native_value(FALSE, FALSE);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!MATCH(KW_FROM))
|
|
|
|
SYNTAX_ERROR("FROM");
|
|
|
|
|
|
|
|
switch (sw_sql_dialect) {
|
|
|
|
case 1:
|
|
|
|
if ((!QUOTED(token.tok_type)) && (!MATCH(KW_COLON)))
|
|
|
|
SYNTAX_ERROR(": <string expression>");
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
if ((!SINGLE_QUOTED(token.tok_type)) && (!MATCH(KW_COLON)))
|
|
|
|
SYNTAX_ERROR(": <string expression>");
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
statement->dyn_string = PAR_native_value(FALSE, FALSE);
|
|
|
|
action = (ACT) ALLOC(ACT_LEN);
|
|
|
|
action->act_type = ACT_dyn_prepare;
|
|
|
|
action->act_object = (REF) statement;
|
|
|
|
action->act_whenever = gen_whenever();
|
|
|
|
sw_dsql = TRUE;
|
|
|
|
|
|
|
|
return action;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//____________________________________________________________
|
|
|
|
//
|
|
|
|
// Handle the EXECUTE PROCEDURE statement.
|
|
|
|
//
|
|
|
|
|
|
|
|
static ACT act_procedure(void)
|
|
|
|
{
|
2002-11-17 01:04:19 +01:00
|
|
|
GPRE_PRC procedure;
|
|
|
|
GPRE_REQ request;
|
2001-05-23 15:26:42 +02:00
|
|
|
ACT action;
|
|
|
|
REF reference, *ref_ptr;
|
2002-11-30 18:45:02 +01:00
|
|
|
GPRE_FLD field;
|
2001-05-23 15:26:42 +02:00
|
|
|
SSHORT inputs, outputs;
|
|
|
|
LLS values;
|
2002-11-11 20:19:43 +01:00
|
|
|
GPRE_NOD list, *ptr;
|
2001-05-23 15:26:42 +02:00
|
|
|
SCHAR p_name[NAME_SIZE + 1], db_name[NAME_SIZE + 1],
|
|
|
|
owner_name[NAME_SIZE + 1];
|
|
|
|
BOOLEAN paren;
|
|
|
|
|
|
|
|
request = MAKE_REQUEST(REQ_procedure);
|
|
|
|
par_options(&request->req_trans);
|
|
|
|
SQL_relation_name(p_name, db_name, owner_name);
|
|
|
|
procedure = SQL_procedure(request, p_name, db_name, owner_name, TRUE);
|
|
|
|
inputs = outputs = 0;
|
|
|
|
values = NULL;
|
|
|
|
|
|
|
|
if (!KEYWORD(KW_RETURNING) && !KEYWORD(KW_SEMI_COLON)) {
|
|
|
|
/* parse input references */
|
|
|
|
|
|
|
|
paren = MATCH(KW_LEFT_PAREN);
|
|
|
|
field = procedure->prc_inputs;
|
|
|
|
ref_ptr = &request->req_values;
|
|
|
|
do {
|
|
|
|
if (MATCH(KW_NULL))
|
|
|
|
PUSH(MAKE_NODE(nod_null, 0), &values);
|
|
|
|
else {
|
|
|
|
*ref_ptr = reference = SQE_parameter(request, FALSE);
|
|
|
|
reference->ref_field = field;
|
2002-11-11 20:19:43 +01:00
|
|
|
PUSH(MSC_unary(nod_value, (GPRE_NOD) reference), &values);
|
2001-05-23 15:26:42 +02:00
|
|
|
ref_ptr = &reference->ref_next;
|
|
|
|
}
|
|
|
|
if (field)
|
|
|
|
field = field->fld_next;
|
|
|
|
inputs++;
|
|
|
|
} while (MATCH(KW_COMMA));
|
|
|
|
if (paren)
|
|
|
|
EXP_match_paren();
|
|
|
|
}
|
|
|
|
|
|
|
|
if (MATCH(KW_RETURNING)) {
|
|
|
|
/* parse output references */
|
|
|
|
|
|
|
|
paren = MATCH(KW_LEFT_PAREN);
|
|
|
|
field = procedure->prc_outputs;
|
|
|
|
ref_ptr = &request->req_references;
|
|
|
|
do {
|
|
|
|
*ref_ptr = reference = SQE_variable(request, FALSE);
|
|
|
|
if (reference->ref_field = field)
|
|
|
|
field = field->fld_next;
|
|
|
|
ref_ptr = &reference->ref_next;
|
|
|
|
outputs++;
|
|
|
|
} while (MATCH(KW_COMMA));
|
|
|
|
if (paren)
|
|
|
|
EXP_match_paren();
|
|
|
|
}
|
|
|
|
|
|
|
|
if (procedure->prc_in_count != inputs)
|
|
|
|
PAR_error("count of input values doesn't match count of parameters");
|
|
|
|
if (procedure->prc_out_count != outputs)
|
|
|
|
PAR_error("count of output values doesn't match count of parameters");
|
|
|
|
|
|
|
|
request->req_node = list = MAKE_NODE(nod_list, inputs);
|
|
|
|
ptr = &list->nod_arg[inputs];
|
|
|
|
while (values)
|
2002-11-11 20:19:43 +01:00
|
|
|
*--ptr = (GPRE_NOD) POP(&values);
|
2001-05-23 15:26:42 +02:00
|
|
|
|
|
|
|
action = MAKE_ACTION(request, ACT_procedure);
|
|
|
|
action->act_object = (REF) procedure;
|
|
|
|
action->act_whenever = gen_whenever();
|
|
|
|
|
|
|
|
return action;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//____________________________________________________________
|
|
|
|
//
|
|
|
|
// Handle the stand alone SQL select statement.
|
|
|
|
//
|
|
|
|
|
|
|
|
static ACT act_select(void)
|
|
|
|
{
|
2002-11-17 01:04:19 +01:00
|
|
|
GPRE_REQ request;
|
2001-05-23 15:26:42 +02:00
|
|
|
ACT action;
|
|
|
|
RSE select;
|
|
|
|
TEXT s[ERROR_LENGTH];
|
|
|
|
|
|
|
|
request = MAKE_REQUEST(REQ_for);
|
|
|
|
par_options(&request->req_trans);
|
|
|
|
request->req_rse = select = SQE_select(request, FALSE);
|
|
|
|
|
|
|
|
if (!MATCH(KW_SEMI_COLON)) {
|
|
|
|
sprintf(s, "Expected ';', got %s.", token.tok_string);
|
|
|
|
CPR_warn(s);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (select->rse_into)
|
|
|
|
into(request, select->rse_fields, select->rse_into);
|
|
|
|
|
|
|
|
action = MAKE_ACTION(request, ACT_select);
|
|
|
|
action->act_object = (REF) select->rse_into;
|
|
|
|
action->act_whenever = gen_whenever();
|
|
|
|
EXP_rse_cleanup(select);
|
|
|
|
return action;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//____________________________________________________________
|
|
|
|
//
|
|
|
|
// Parse a SET <something>
|
|
|
|
//
|
|
|
|
|
|
|
|
static ACT act_set(void)
|
|
|
|
{
|
|
|
|
|
|
|
|
if (MATCH(KW_TRANSACTION))
|
|
|
|
return act_set_transaction();
|
|
|
|
|
|
|
|
if (MATCH(KW_NAMES))
|
|
|
|
return act_set_names();
|
|
|
|
|
|
|
|
if (MATCH(KW_STATISTICS))
|
|
|
|
return act_set_statistics();
|
|
|
|
|
|
|
|
if (MATCH(KW_SCHEMA) || MATCH(KW_DATABASE))
|
2003-07-04 01:02:45 +02:00
|
|
|
return PAR_database((USHORT) TRUE, NULL);
|
2001-05-23 15:26:42 +02:00
|
|
|
|
|
|
|
if (MATCH(KW_GENERATOR))
|
|
|
|
return act_set_generator();
|
|
|
|
|
|
|
|
if (MATCH(KW_SQL)) {
|
|
|
|
if (MATCH(KW_DIALECT))
|
|
|
|
return act_set_dialect();
|
|
|
|
}
|
|
|
|
|
|
|
|
SYNTAX_ERROR
|
|
|
|
("TRANSACTION, NAMES, SCHEMA, DATABASE, GENERATOR, DIALECT or STATISTICS");
|
|
|
|
return NULL; /* silence compiler */
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//____________________________________________________________
|
|
|
|
//
|
|
|
|
// Parse a SET SQL DIALECT
|
|
|
|
//
|
|
|
|
|
|
|
|
static ACT act_set_dialect(void)
|
|
|
|
{
|
|
|
|
ACT action;
|
|
|
|
USHORT dialect;
|
|
|
|
|
|
|
|
action = (ACT) ALLOC(ACT_LEN);
|
|
|
|
action->act_type = ACT_sql_dialect;
|
|
|
|
|
|
|
|
dialect = EXP_USHORT_ordinal(0 /*FALSE*/);
|
|
|
|
if ((dialect < 1) || (dialect > 3))
|
|
|
|
SYNTAX_ERROR("SQL DIALECT 1,2 or 3");
|
|
|
|
|
|
|
|
if (isc_databases && dialect != compiletime_db_dialect
|
|
|
|
&& sw_ods_version < 10) {
|
|
|
|
char warn_mesg[100];
|
|
|
|
sprintf(warn_mesg,
|
|
|
|
"Pre 6.0 database. Cannot use dialect %d, Resetting to %d\n",
|
|
|
|
dialect, SQL_DIALECT_V5);
|
|
|
|
dialect = SQL_DIALECT_V5;
|
|
|
|
CPR_warn(warn_mesg);
|
|
|
|
}
|
|
|
|
else if (isc_databases && dialect != compiletime_db_dialect) {
|
|
|
|
char warn_mesg[100];
|
|
|
|
sprintf(warn_mesg,
|
|
|
|
"Client dialect set to %d. Compiletime database dialect is %d\n",
|
|
|
|
dialect, compiletime_db_dialect);
|
|
|
|
CPR_warn(warn_mesg);
|
|
|
|
}
|
|
|
|
|
|
|
|
action->act_object = (REF) ALLOC(SDT_LEN);
|
|
|
|
((SDT) action->act_object)->sdt_dialect = dialect;
|
|
|
|
|
|
|
|
// Needed because subsequent parsing pass1 looks at sw_Sql_dialect value
|
|
|
|
sw_sql_dialect = dialect;
|
|
|
|
dialect_specified = 1;
|
|
|
|
|
|
|
|
ADVANCE_TOKEN;
|
|
|
|
return action;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//____________________________________________________________
|
|
|
|
//
|
|
|
|
// Parse a SET generator
|
|
|
|
//
|
|
|
|
|
|
|
|
static ACT act_set_generator(void)
|
|
|
|
{
|
|
|
|
ACT action;
|
|
|
|
SGEN setgen;
|
2002-11-17 01:04:19 +01:00
|
|
|
GPRE_REQ request;
|
2001-05-23 15:26:42 +02:00
|
|
|
SCHAR s[128];
|
|
|
|
|
|
|
|
request = MAKE_REQUEST(REQ_set_generator);
|
|
|
|
action = (ACT) MAKE_ACTION(request, ACT_s_start);
|
|
|
|
setgen = (SGEN) ALLOC(SGEN_LEN);
|
|
|
|
|
|
|
|
if (isc_databases && !isc_databases->dbb_next)
|
|
|
|
request->req_database = isc_databases;
|
|
|
|
else
|
|
|
|
PAR_error("Can SET GENERATOR in context of single database only");
|
|
|
|
|
|
|
|
if (token.tok_length > NAME_SIZE)
|
|
|
|
PAR_error("Generator name too long");
|
|
|
|
|
|
|
|
setgen->sgen_name = (TEXT *) ALLOC(token.tok_length + 1);
|
|
|
|
SQL_resolve_identifier("<identifier>", setgen->sgen_name);
|
|
|
|
if (!MET_generator(setgen->sgen_name, request->req_database)) {
|
|
|
|
sprintf(s, "generator %s not found", token.tok_string);
|
|
|
|
PAR_error(s);
|
|
|
|
}
|
|
|
|
ADVANCE_TOKEN;
|
|
|
|
MATCH(KW_TO);
|
|
|
|
if ((sw_sql_dialect == SQL_DIALECT_V5) || (sw_server_version < 6)) {
|
|
|
|
setgen->sgen_value = EXP_SLONG_ordinal(TRUE);
|
|
|
|
setgen->sgen_dialect = SQL_DIALECT_V5;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
// ** dialect is > 1. Server can handle int64 *
|
|
|
|
|
|
|
|
setgen->sgen_int64value = EXP_SINT64_ordinal(TRUE);
|
|
|
|
setgen->sgen_dialect = SQL_DIALECT_V5 + 1;
|
|
|
|
}
|
|
|
|
action->act_whenever = gen_whenever();
|
|
|
|
action->act_object = (REF) setgen;
|
|
|
|
return action;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//____________________________________________________________
|
|
|
|
//
|
|
|
|
// Parse a SET NAMES <charset>;
|
|
|
|
//
|
|
|
|
|
|
|
|
static ACT act_set_names(void)
|
|
|
|
{
|
|
|
|
ACT action;
|
|
|
|
TEXT *value;
|
|
|
|
DBB db;
|
|
|
|
char buffer[256];
|
|
|
|
|
|
|
|
if (sw_auto)
|
|
|
|
CPR_warn("SET NAMES requires -manual switch to gpre.");
|
|
|
|
|
|
|
|
action = (ACT) ALLOC(ACT_LEN);
|
|
|
|
action->act_type = ACT_noop;
|
|
|
|
|
|
|
|
if (MATCH(KW_COLON)) {
|
|
|
|
/* User is specifying a host variable or string as
|
|
|
|
* the character set. Make this the run-time set.
|
|
|
|
*/
|
|
|
|
value = PAR_native_value(FALSE, FALSE);
|
|
|
|
for (db = isc_databases; db; db = db->dbb_next) {
|
|
|
|
if (db->dbb_r_lc_ctype) {
|
|
|
|
sprintf(buffer,
|
|
|
|
"Supersedes runtime character set for database %s",
|
|
|
|
(db->dbb_filename) ? db->dbb_filename : db->dbb_name->
|
|
|
|
sym_string);
|
|
|
|
CPR_warn(buffer);
|
|
|
|
}
|
|
|
|
db->dbb_r_lc_ctype = value;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (token.tok_type == tok_ident) {
|
|
|
|
/* User is specifying the name of a character set */
|
|
|
|
/* Make this the compile time character set */
|
|
|
|
|
|
|
|
value = (TEXT *) ALLOC(token.tok_length + 1);
|
|
|
|
COPY(token.tok_string, token.tok_length, value);
|
|
|
|
value[token.tok_length] = '\0';
|
|
|
|
|
|
|
|
/* Due to the ambiguities involved in having modules expressed
|
|
|
|
* in multiple compile-time character sets, we disallow it.
|
|
|
|
*/
|
|
|
|
if (module_lc_ctype && strcmp(module_lc_ctype, value) != 0)
|
|
|
|
PAR_error("Duplicate declaration of module CHARACTER SET");
|
|
|
|
|
|
|
|
module_lc_ctype = value;
|
|
|
|
for (db = isc_databases; db; db = db->dbb_next) {
|
|
|
|
if (db->dbb_c_lc_ctype) {
|
|
|
|
sprintf(buffer, "Supersedes character set for database %s",
|
|
|
|
(db->dbb_filename) ? db->dbb_filename : db->dbb_name->
|
|
|
|
sym_string);
|
|
|
|
CPR_warn(buffer);
|
|
|
|
}
|
|
|
|
|
|
|
|
db->dbb_c_lc_ctype = value;
|
|
|
|
if (!(db->dbb_flags & DBB_sqlca)) {
|
|
|
|
/* Verify that character set name is valid,
|
|
|
|
* this requires a database to be previously declared
|
|
|
|
* so we can resolve against it.
|
|
|
|
* So what if we go through this code once for each database...
|
|
|
|
*/
|
|
|
|
if (!(MSC_find_symbol(token.tok_symbol, SYM_charset)))
|
|
|
|
PAR_error("The named CHARACTER SET was not found");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
CPR_token();
|
|
|
|
}
|
|
|
|
else
|
|
|
|
SYNTAX_ERROR("<character set name> or :<hostvar>");
|
|
|
|
|
|
|
|
return action;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//____________________________________________________________
|
|
|
|
//
|
|
|
|
// Parse a SET statistics
|
|
|
|
//
|
|
|
|
|
|
|
|
static ACT act_set_statistics(void)
|
|
|
|
{
|
|
|
|
ACT action;
|
|
|
|
STS stats;
|
2002-11-17 01:04:19 +01:00
|
|
|
GPRE_REQ request;
|
2001-05-23 15:26:42 +02:00
|
|
|
|
|
|
|
request = MAKE_REQUEST(REQ_ddl);
|
|
|
|
action = (ACT) MAKE_ACTION(request, ACT_statistics);
|
|
|
|
stats = (STS) ALLOC(STS_LEN);
|
|
|
|
|
|
|
|
if (isc_databases && !isc_databases->dbb_next)
|
|
|
|
request->req_database = isc_databases;
|
|
|
|
else
|
|
|
|
PAR_error("Can SET STATISTICS in context of single database only");
|
|
|
|
|
|
|
|
if (MATCH(KW_INDEX)) {
|
|
|
|
stats->sts_flags = STS_index;
|
|
|
|
stats->sts_name = (STR) ALLOC(NAME_SIZE + 1);
|
|
|
|
SQL_resolve_identifier("<index name>", (TEXT *) stats->sts_name);
|
|
|
|
if (token.tok_length > NAME_SIZE)
|
|
|
|
PAR_error("Index name too long");
|
|
|
|
ADVANCE_TOKEN;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
SYNTAX_ERROR("INDEX");
|
|
|
|
|
|
|
|
action->act_object = (REF) stats;
|
|
|
|
return action;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//____________________________________________________________
|
|
|
|
//
|
|
|
|
// Generate a set transaction
|
|
|
|
//
|
|
|
|
|
|
|
|
static ACT act_set_transaction(void)
|
|
|
|
{
|
|
|
|
ACT action;
|
2002-11-17 01:04:19 +01:00
|
|
|
GPRE_TRA trans;
|
2001-05-23 15:26:42 +02:00
|
|
|
|
|
|
|
action = (ACT) ALLOC(ACT_LEN);
|
|
|
|
action->act_type = ACT_start;
|
|
|
|
|
2002-11-17 01:04:19 +01:00
|
|
|
trans = (GPRE_TRA) ALLOC(TRA_LEN);
|
2001-05-23 15:26:42 +02:00
|
|
|
|
|
|
|
if (MATCH(KW_NAME))
|
|
|
|
trans->tra_handle = PAR_native_value(FALSE, TRUE);
|
|
|
|
|
|
|
|
// Get all the transaction parameters
|
|
|
|
|
|
|
|
while (TRUE) {
|
|
|
|
if (MATCH(KW_ISOLATION)) {
|
|
|
|
MATCH(KW_LEVEL);
|
|
|
|
if (!par_transaction_modes(trans, TRUE))
|
|
|
|
SYNTAX_ERROR("SNAPSHOT");
|
|
|
|
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (par_transaction_modes(trans, FALSE))
|
|
|
|
continue;
|
|
|
|
|
|
|
|
if (MATCH(KW_NO)) {
|
|
|
|
if (MATCH(KW_WAIT)) {
|
|
|
|
trans->tra_flags |= TRA_nw;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
SYNTAX_ERROR("WAIT");
|
|
|
|
}
|
|
|
|
|
|
|
|
if (MATCH(KW_WAIT))
|
|
|
|
continue;
|
|
|
|
|
|
|
|
#ifdef DEV_BUILD
|
|
|
|
if (MATCH(KW_AUTOCOMMIT)) {
|
|
|
|
trans->tra_flags |= TRA_autocommit;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
if (MATCH(KW_NO_AUTO_UNDO)) {
|
|
|
|
trans->tra_flags |= TRA_no_auto_undo;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
// send out for the list of reserved relations
|
|
|
|
|
|
|
|
if (MATCH(KW_RESERVING)) {
|
|
|
|
trans->tra_flags |= TRA_rrl;
|
|
|
|
PAR_reserving(trans->tra_flags, 1);
|
|
|
|
}
|
|
|
|
else if (MATCH(KW_USING)) {
|
|
|
|
trans->tra_flags |= TRA_inc;
|
|
|
|
PAR_using_db();
|
|
|
|
}
|
|
|
|
|
|
|
|
CMP_t_start(trans);
|
|
|
|
action->act_object = (REF) trans;
|
|
|
|
action->act_whenever = gen_whenever();
|
|
|
|
|
|
|
|
return action;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//____________________________________________________________
|
|
|
|
//
|
|
|
|
// Generate a COMMIT, FINISH, or ROLLBACK.
|
|
|
|
//
|
|
|
|
|
|
|
|
static ACT act_transaction( enum act_t type)
|
|
|
|
{
|
|
|
|
ACT action;
|
|
|
|
TEXT *transaction;
|
|
|
|
|
|
|
|
par_options(&transaction);
|
|
|
|
MATCH(KW_WORK);
|
|
|
|
action = (ACT) ALLOC(ACT_LEN);
|
|
|
|
action->act_type = type;
|
|
|
|
action->act_whenever = gen_whenever();
|
|
|
|
action->act_object = (REF) transaction;
|
|
|
|
|
|
|
|
if (MATCH(KW_RELEASE)) {
|
|
|
|
type = (type == ACT_commit) ? ACT_finish : ACT_rfinish;
|
|
|
|
if (transaction) {
|
|
|
|
action->act_rest = (ACT) ALLOC(ACT_LEN);
|
|
|
|
action->act_rest->act_type = type;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
action->act_type = type;
|
|
|
|
}
|
|
|
|
else if ((type == ACT_commit) && (MATCH(KW_RETAIN))) {
|
|
|
|
MATCH(KW_SNAPSHOT);
|
|
|
|
action->act_type = type = ACT_commit_retain_context;
|
|
|
|
}
|
|
|
|
|
|
|
|
return action;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//____________________________________________________________
|
|
|
|
//
|
|
|
|
// Parse an update action. This is a little more complicated
|
|
|
|
// because SQL confuses the update of a cursor with a mass update.
|
|
|
|
// The syntax, and therefor the code, I fear, is a mess.
|
|
|
|
//
|
|
|
|
|
|
|
|
static ACT act_update(void)
|
|
|
|
{
|
|
|
|
ACT action, slice_action;
|
2002-11-17 01:04:19 +01:00
|
|
|
GPRE_REQ request, slice_request;
|
|
|
|
GPRE_REL relation;
|
2001-05-23 15:26:42 +02:00
|
|
|
RSE rse;
|
2002-11-17 01:04:19 +01:00
|
|
|
GPRE_CTX input_context, update_context;
|
2002-11-11 20:19:43 +01:00
|
|
|
GPRE_NOD set_list, *end_list, set_item, modify, *ptr;
|
2001-05-23 15:26:42 +02:00
|
|
|
UPD update;
|
|
|
|
LLS stack;
|
|
|
|
SYM alias;
|
|
|
|
REF field_ref, req_ref;
|
|
|
|
SLC slice;
|
|
|
|
SCHAR *transaction, r_name[NAME_SIZE + 1], db_name[NAME_SIZE + 1],
|
|
|
|
owner_name[NAME_SIZE + 1], *str_1, *str_2;
|
|
|
|
SSHORT where, count, i, found = FALSE, trans_nm_len;
|
|
|
|
|
|
|
|
par_options(&transaction);
|
|
|
|
|
|
|
|
// First comes the relation. Unfortunately, there is no way to identify
|
|
|
|
// its database until the cursor is known. Sigh. Save the token.
|
|
|
|
|
|
|
|
SQL_relation_name(r_name, db_name, owner_name);
|
|
|
|
|
|
|
|
// Parse the optional alias (context variable)
|
|
|
|
|
|
|
|
alias = (token.tok_symbol) ? NULL : PAR_symbol(SYM_dummy);
|
|
|
|
|
|
|
|
// Now we need the SET list list. Do this thru a linked list stack
|
|
|
|
|
|
|
|
if (!MATCH(KW_SET))
|
|
|
|
SYNTAX_ERROR("SET");
|
|
|
|
|
|
|
|
request = MAKE_REQUEST(REQ_mass_update);
|
|
|
|
relation = SQL_relation(request, r_name, db_name, owner_name, TRUE);
|
|
|
|
input_context = MAKE_CONTEXT(request);
|
|
|
|
input_context->ctx_relation = relation;
|
|
|
|
if (alias) {
|
|
|
|
alias->sym_type = SYM_context;
|
|
|
|
alias->sym_object = input_context;
|
|
|
|
HSH_insert(alias);
|
|
|
|
token.tok_symbol = HSH_lookup(token.tok_string);
|
|
|
|
}
|
|
|
|
|
|
|
|
stack = NULL;
|
|
|
|
count = 0;
|
|
|
|
|
|
|
|
do {
|
|
|
|
set_item = MAKE_NODE(nod_assignment, 2);
|
|
|
|
set_item->nod_arg[1] = SQE_field(NULL, FALSE);
|
|
|
|
if (!MATCH(KW_EQUALS))
|
|
|
|
SYNTAX_ERROR("assignment operator");
|
|
|
|
if (MATCH(KW_NULL))
|
|
|
|
set_item->nod_arg[0] = MAKE_NODE(nod_null, 0);
|
|
|
|
else
|
|
|
|
set_item->nod_arg[0] = SQE_value(request, FALSE, 0, 0);
|
|
|
|
PUSH(set_item, &stack);
|
|
|
|
count++;
|
|
|
|
} while (MATCH(KW_COMMA));
|
|
|
|
|
|
|
|
set_list = MAKE_NODE(nod_list, count);
|
|
|
|
ptr = end_list = set_list->nod_arg + count;
|
|
|
|
|
|
|
|
while (stack)
|
2002-11-11 20:19:43 +01:00
|
|
|
*--ptr = (GPRE_NOD) POP(&stack);
|
2001-05-23 15:26:42 +02:00
|
|
|
|
|
|
|
// Now the moment of truth. If the next few tokens are WHERE CURRENT OF
|
|
|
|
// then this is a sub-action of an existing request. If not, then it is
|
|
|
|
// a free standing request
|
|
|
|
|
|
|
|
if ((where = MATCH(KW_WITH)) && MATCH(KW_CURRENT)) {
|
|
|
|
if (!MATCH(KW_OF))
|
|
|
|
SYNTAX_ERROR("OF cursor");
|
|
|
|
|
|
|
|
/* Allocate update block, flush old request block, then
|
|
|
|
find the right request block, and the target relation */
|
|
|
|
|
|
|
|
update = (UPD) ALLOC(UPD_LEN);
|
|
|
|
update->upd_references = request->req_values;
|
|
|
|
MSC_free_request(request);
|
|
|
|
|
|
|
|
request = par_cursor(NULL);
|
|
|
|
if (request->req_flags == REQ_sql_blob_create)
|
|
|
|
PAR_error("expected a TABLE cursor, got a BLOB cursor");
|
|
|
|
if ((transaction || request->req_trans) &&
|
|
|
|
(!transaction || !request->req_trans ||
|
|
|
|
strcmp(transaction, request->req_trans))) {
|
|
|
|
if (transaction)
|
|
|
|
PAR_error("different transaction for select and update");
|
|
|
|
else { /* does not specify transaction clause in */
|
|
|
|
/* "update ... where cuurent of cursor" stmt */
|
|
|
|
trans_nm_len = strlen(request->req_trans);
|
|
|
|
str_2 = transaction = (SCHAR *) ALLOC(trans_nm_len + 1);
|
|
|
|
str_1 = request->req_trans;
|
|
|
|
do
|
|
|
|
*str_2++ = *str_1++;
|
|
|
|
while (--trans_nm_len);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
request->req_trans = transaction;
|
|
|
|
relation = SQL_relation(request, r_name, db_name, owner_name, TRUE);
|
|
|
|
|
|
|
|
/* Given the target relation, find the input context for the modify */
|
|
|
|
|
|
|
|
rse = request->req_rse;
|
|
|
|
for (i = 0; i < rse->rse_count; i++) {
|
|
|
|
input_context = rse->rse_context[i];
|
|
|
|
if (input_context->ctx_relation == relation)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (i == rse->rse_count)
|
|
|
|
PAR_error("table not in request");
|
|
|
|
|
|
|
|
/* Resolve input fields first */
|
|
|
|
|
|
|
|
if (alias) {
|
|
|
|
alias->sym_type = SYM_context;
|
|
|
|
alias->sym_object = input_context;
|
|
|
|
HSH_insert(alias);
|
|
|
|
}
|
|
|
|
|
|
|
|
for (ptr = set_list->nod_arg; ptr < end_list; ptr++) {
|
|
|
|
set_item = *ptr;
|
|
|
|
SQE_resolve(set_item->nod_arg[0], request, 0);
|
|
|
|
pair(set_item->nod_arg[0], set_item->nod_arg[1]);
|
|
|
|
}
|
|
|
|
|
|
|
|
update->upd_request = request;
|
|
|
|
update->upd_source = input_context;
|
|
|
|
update->upd_update = update_context = MAKE_CONTEXT(request);
|
|
|
|
update_context->ctx_relation = relation;
|
|
|
|
update->upd_assignments = set_list;
|
|
|
|
|
|
|
|
action = MAKE_ACTION(request, ACT_update);
|
|
|
|
|
|
|
|
/* Resolve update fields next */
|
|
|
|
|
|
|
|
if (alias)
|
|
|
|
alias->sym_object = update_context;
|
|
|
|
|
|
|
|
for (ptr = set_list->nod_arg; ptr < end_list; ptr++) {
|
|
|
|
set_item = *ptr;
|
|
|
|
SQE_resolve(set_item->nod_arg[1], request, 0);
|
|
|
|
field_ref = (REF) ((set_item->nod_arg[1])->nod_arg[0]);
|
|
|
|
if ((slice_action = (ACT) field_ref->ref_slice) &&
|
|
|
|
(slice = (SLC) slice_action->act_object)) {
|
|
|
|
/* These requests got lost in freeing the main request */
|
|
|
|
|
|
|
|
slice_request = slice_action->act_request;
|
|
|
|
slice_request->req_next = requests;
|
|
|
|
requests = slice_request;
|
|
|
|
slice->slc_field_ref = field_ref;
|
2002-11-11 20:19:43 +01:00
|
|
|
slice->slc_array = (GPRE_NOD) set_item->nod_arg[0];
|
2001-05-23 15:26:42 +02:00
|
|
|
slice->slc_parent_request = request;
|
|
|
|
slice_action->act_type = ACT_put_slice;
|
|
|
|
|
|
|
|
/* If slice ref is not yet posted, post it.
|
|
|
|
This is required to receive the handle for the
|
|
|
|
array being updated */
|
|
|
|
|
|
|
|
found = FALSE;
|
|
|
|
for (req_ref = request->req_references; req_ref;
|
|
|
|
req_ref = req_ref->ref_next) {
|
|
|
|
if (req_ref == field_ref) {
|
2002-11-11 20:19:43 +01:00
|
|
|
set_item->nod_arg[1]->nod_arg[0] = (GPRE_NOD) req_ref;
|
2001-05-23 15:26:42 +02:00
|
|
|
found = TRUE;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if ((req_ref->ref_field == field_ref->ref_field) &&
|
|
|
|
(req_ref->ref_context == field_ref->ref_context))
|
|
|
|
PAR_error
|
|
|
|
("Can't update multiple slices of same column");
|
|
|
|
}
|
|
|
|
if (!found) {
|
|
|
|
field_ref->ref_next = request->req_references;
|
|
|
|
request->req_references = field_ref;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
pair(set_item->nod_arg[0], set_item->nod_arg[1]);
|
|
|
|
}
|
|
|
|
|
|
|
|
action->act_whenever = gen_whenever();
|
|
|
|
action->act_object = (REF) update;
|
|
|
|
if (alias)
|
|
|
|
HSH_remove(alias);
|
|
|
|
return action;
|
|
|
|
}
|
|
|
|
|
|
|
|
request->req_trans = transaction;
|
|
|
|
|
|
|
|
// How amusing. After all that work, it wasn't a sub-action at all.
|
|
|
|
// Neat. Take the pieces and build a complete request. Start by
|
|
|
|
// figuring out what database is involved.
|
|
|
|
|
|
|
|
// Generate record select expression, then resolve input values
|
|
|
|
|
|
|
|
request->req_rse = rse = (RSE) ALLOC(RSE_LEN(1));
|
|
|
|
rse->rse_count = 1;
|
|
|
|
rse->rse_context[0] = input_context;
|
|
|
|
|
|
|
|
if (!alias && !token.tok_symbol)
|
|
|
|
/* may have a relation name put parser didn't know it when it parsed it */
|
|
|
|
token.tok_symbol = HSH_lookup(token.tok_string);
|
|
|
|
|
|
|
|
for (ptr = set_list->nod_arg; ptr < end_list; ptr++) {
|
|
|
|
set_item = *ptr;
|
|
|
|
SQE_resolve(set_item->nod_arg[0], request, rse);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Process boolean, if any
|
|
|
|
|
|
|
|
if (where)
|
|
|
|
rse->rse_boolean = SQE_boolean(request, 0);
|
|
|
|
|
|
|
|
// Resolve update fields to update context
|
|
|
|
|
|
|
|
request->req_update = update_context = MAKE_CONTEXT(request);
|
|
|
|
update_context->ctx_relation = relation;
|
|
|
|
|
|
|
|
if (alias)
|
|
|
|
alias->sym_object = update_context;
|
|
|
|
|
|
|
|
for (ptr = set_list->nod_arg; ptr < end_list; ptr++) {
|
|
|
|
set_item = *ptr;
|
|
|
|
SQE_resolve(set_item->nod_arg[1], request, 0);
|
|
|
|
field_ref = (REF) ((set_item->nod_arg[1])->nod_arg[0]);
|
|
|
|
if (slice_action = (ACT) field_ref->ref_slice) {
|
|
|
|
/* Slices not allowed in searched updates */
|
|
|
|
|
|
|
|
PAR_error("Updates of slices not allowed in searched updates");
|
|
|
|
}
|
|
|
|
|
|
|
|
/* In dialect 1, neither the value being assigned (nod_arg[0])
|
|
|
|
nor the field to which it is being assigned (nod_arg[1]) may
|
|
|
|
be of a data type added in V6. */
|
|
|
|
if (SQL_DIALECT_V5 == sw_sql_dialect) {
|
|
|
|
USHORT field_dtype;
|
|
|
|
int arg_num;
|
|
|
|
for (arg_num = 0; arg_num <= 1; arg_num++)
|
|
|
|
if (nod_field == set_item->nod_arg[arg_num]->nod_type) {
|
|
|
|
field_dtype =
|
|
|
|
((REF)
|
|
|
|
(set_item->nod_arg[arg_num]->
|
|
|
|
nod_arg[0]))->ref_field->fld_dtype;
|
|
|
|
if ((dtype_sql_date == field_dtype)
|
|
|
|
|| (dtype_sql_time == field_dtype)
|
|
|
|
|| (dtype_int64 == field_dtype)) {
|
|
|
|
dialect1_bad_type(field_dtype);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pair(set_item->nod_arg[0], set_item->nod_arg[1]);
|
|
|
|
}
|
|
|
|
|
|
|
|
request->req_node = modify = MAKE_NODE(nod_modify, 1);;
|
|
|
|
modify->nod_arg[0] = set_list;
|
|
|
|
|
|
|
|
action = MAKE_ACTION(request, ACT_loop);
|
|
|
|
action->act_whenever = gen_whenever();
|
|
|
|
|
|
|
|
if (alias)
|
|
|
|
HSH_remove(alias);
|
|
|
|
|
|
|
|
return action;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//____________________________________________________________
|
|
|
|
//
|
|
|
|
// Handle an SQL whenever statement. This is declaratory,
|
|
|
|
// rather than a significant action.
|
|
|
|
//
|
|
|
|
|
|
|
|
static ACT act_whenever(void)
|
|
|
|
{
|
|
|
|
USHORT condition, l;
|
|
|
|
TEXT *p, *q;
|
|
|
|
SWE label;
|
|
|
|
ACT action;
|
|
|
|
|
|
|
|
whenever_list = NULL;
|
|
|
|
|
|
|
|
// Pick up condition
|
|
|
|
|
|
|
|
if (MATCH(KW_SQLERROR))
|
|
|
|
condition = SWE_error;
|
|
|
|
else if (MATCH(KW_SQLWARNING))
|
|
|
|
condition = SWE_warning;
|
|
|
|
else if (MATCH(KW_NOT) && MATCH(KW_FOUND))
|
|
|
|
condition = SWE_not_found;
|
|
|
|
else
|
|
|
|
SYNTAX_ERROR("WHENEVER condition");
|
|
|
|
|
|
|
|
// Pick up action
|
|
|
|
|
|
|
|
if (MATCH(KW_CONTINUE))
|
|
|
|
label = NULL;
|
|
|
|
else if ((MATCH(KW_GO) && MATCH(KW_TO)) || MATCH(KW_GOTO)) {
|
|
|
|
MATCH(KW_COLON);
|
|
|
|
l = token.tok_length;
|
|
|
|
label = (SWE) ALLOC(sizeof(struct swe) + l);
|
|
|
|
label->swe_condition = condition;
|
|
|
|
if (label->swe_length = l) {
|
|
|
|
p = label->swe_label;
|
|
|
|
q = token.tok_string;
|
|
|
|
do
|
|
|
|
*p++ = *q++;
|
|
|
|
while (--l);
|
|
|
|
}
|
|
|
|
ADVANCE_TOKEN;
|
|
|
|
label->swe_condition = condition;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
SYNTAX_ERROR("GO TO or CONTINUE");
|
|
|
|
|
|
|
|
// Set up condition vector
|
|
|
|
|
|
|
|
whenever[condition] = label;
|
|
|
|
|
|
|
|
action = (ACT) ALLOC(ACT_LEN);
|
|
|
|
action->act_type = ACT_noop;
|
|
|
|
|
|
|
|
MATCH(KW_SEMI_COLON);
|
|
|
|
return action;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//____________________________________________________________
|
|
|
|
//
|
|
|
|
// Make sure that a file path doesn't contain a
|
|
|
|
// Decnet node name.
|
|
|
|
//
|
|
|
|
|
|
|
|
static BOOLEAN check_filename( TEXT * name)
|
|
|
|
{
|
|
|
|
USHORT l;
|
|
|
|
TEXT *p, file_name[256];
|
|
|
|
|
|
|
|
if (!(l = strlen(name)))
|
|
|
|
return TRUE;
|
|
|
|
|
|
|
|
for (p = name; *p; p++)
|
|
|
|
if (p[0] == ':' && p[1] == ':')
|
|
|
|
return FALSE;
|
|
|
|
|
|
|
|
l = MIN(l, sizeof(file_name) - 1);
|
|
|
|
strncpy(file_name, name, l);
|
|
|
|
file_name[l] = '\0';
|
|
|
|
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//____________________________________________________________
|
|
|
|
//
|
|
|
|
// Parse connect options
|
|
|
|
//
|
|
|
|
|
|
|
|
static void connect_opts(
|
|
|
|
TEXT ** user,
|
|
|
|
TEXT ** password,
|
|
|
|
TEXT ** sql_role,
|
|
|
|
TEXT ** lc_messages, USHORT * buffers)
|
|
|
|
{
|
|
|
|
TEXT *s;
|
|
|
|
|
|
|
|
for (;;) {
|
|
|
|
if (MATCH(KW_CACHE)) {
|
|
|
|
*buffers = atoi(token.tok_string);
|
|
|
|
ADVANCE_TOKEN;
|
|
|
|
MATCH(KW_BUFFERS);
|
|
|
|
}
|
|
|
|
else if (MATCH(KW_USER))
|
|
|
|
*user = SQL_var_or_string(FALSE);
|
|
|
|
else if (MATCH(KW_PASSWORD))
|
|
|
|
*password = SQL_var_or_string(FALSE);
|
|
|
|
else if (MATCH(KW_ROLE)) {
|
|
|
|
if (token.tok_type == tok_ident) {
|
|
|
|
s = (TEXT *) ALLOC(token.tok_length + 3);
|
|
|
|
/** extra bytes for quotes and NULL **/
|
|
|
|
SQL_resolve_identifier("<Role Name>", s);
|
|
|
|
s[0] = '\"';
|
|
|
|
strcpy(s + 1, token.tok_string);
|
|
|
|
strcat(s, "\"");
|
|
|
|
*sql_role = s;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
*sql_role = SQL_var_or_string(FALSE);
|
|
|
|
}
|
|
|
|
else if (MATCH(KW_LC_MESSAGES))
|
|
|
|
*lc_messages = SQL_var_or_string(FALSE);
|
|
|
|
else
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2003-03-03 09:26:35 +01:00
|
|
|
#ifdef FLINT_CACHE
|
2001-05-23 15:26:42 +02:00
|
|
|
//____________________________________________________________
|
|
|
|
//
|
|
|
|
// Add a shared cache to an existing database.
|
|
|
|
//
|
|
|
|
|
|
|
|
static FIL define_cache(void)
|
|
|
|
{
|
|
|
|
FIL file;
|
|
|
|
TEXT *string;
|
|
|
|
TEXT err_string[256];
|
|
|
|
|
|
|
|
file = (FIL) ALLOC(sizeof(struct fil));
|
|
|
|
if (QUOTED(token.tok_type)) {
|
|
|
|
file->fil_name = string = (TEXT *) ALLOC(token.tok_length + 1);
|
|
|
|
COPY(token.tok_string, token.tok_length, string);
|
|
|
|
ADVANCE_TOKEN;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
SYNTAX_ERROR("<quoted filename>");
|
|
|
|
if (!check_filename(file->fil_name))
|
|
|
|
PAR_error("node name not permitted"); /* a node name is not permitted in a shared cache file name */
|
|
|
|
|
|
|
|
if (MATCH(KW_LENGTH)) {
|
|
|
|
file->fil_length = EXP_ULONG_ordinal(TRUE);
|
|
|
|
if (file->fil_length < MIN_CACHE_BUFFERS) {
|
|
|
|
sprintf(err_string, "Minimum of %d cache pages required",
|
|
|
|
MIN_CACHE_BUFFERS);
|
|
|
|
PAR_error(err_string);
|
|
|
|
}
|
|
|
|
MATCH(KW_PAGES);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
file->fil_length = DEF_CACHE_BUFFERS; /* default cache buffers */
|
|
|
|
|
|
|
|
return file;
|
|
|
|
}
|
2003-03-03 09:26:35 +01:00
|
|
|
#endif
|
2001-05-23 15:26:42 +02:00
|
|
|
|
|
|
|
//____________________________________________________________
|
|
|
|
//
|
|
|
|
// Add a new file to an existing database.
|
|
|
|
//
|
|
|
|
|
|
|
|
static FIL define_file(void)
|
|
|
|
{
|
|
|
|
FIL file;
|
|
|
|
TEXT *string;
|
|
|
|
|
|
|
|
file = (FIL) ALLOC(sizeof(struct fil));
|
|
|
|
if (QUOTED(token.tok_type)) {
|
|
|
|
file->fil_name = string = (TEXT *) ALLOC(token.tok_length + 1);
|
|
|
|
COPY(token.tok_string, token.tok_length, string);
|
|
|
|
token.tok_length += 2;
|
|
|
|
ADVANCE_TOKEN;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
SYNTAX_ERROR("<quoted filename>");
|
|
|
|
|
|
|
|
if (!check_filename(file->fil_name))
|
|
|
|
PAR_error("node name not permitted"); /* A node name is not permitted in a shadow or secondary file name */
|
|
|
|
|
|
|
|
while (TRUE) {
|
|
|
|
if (MATCH(KW_LENGTH)) {
|
|
|
|
MATCH(KW_EQUALS);
|
|
|
|
file->fil_length = EXP_ULONG_ordinal(TRUE);
|
|
|
|
MATCH(KW_PAGES);
|
|
|
|
MATCH(KW_PAGE);
|
|
|
|
}
|
|
|
|
else if (MATCH(KW_STARTS) || MATCH(KW_STARTING)) {
|
|
|
|
MATCH(KW_AT);
|
|
|
|
MATCH(KW_PAGE);
|
|
|
|
file->fil_start = EXP_ULONG_ordinal(TRUE);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
return file;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//____________________________________________________________
|
|
|
|
//
|
|
|
|
// define a log file
|
|
|
|
//
|
|
|
|
|
|
|
|
static FIL define_log_file( BOOLEAN log_serial)
|
|
|
|
{
|
|
|
|
FIL file;
|
|
|
|
TEXT *string;
|
|
|
|
TEXT err_string[256];
|
|
|
|
|
|
|
|
file = (FIL) ALLOC(sizeof(struct fil));
|
|
|
|
if (QUOTED(token.tok_type)) {
|
|
|
|
file->fil_name = string = (TEXT *) ALLOC(token.tok_length + 1);
|
|
|
|
COPY(token.tok_string, token.tok_length, string);
|
|
|
|
ADVANCE_TOKEN;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
SYNTAX_ERROR("<quoted filename>");
|
|
|
|
|
|
|
|
if (!check_filename(file->fil_name))
|
|
|
|
PAR_error("node name not permitted"); /* A node name is not permitted in a shadow or secondary file name */
|
|
|
|
|
|
|
|
while (TRUE) {
|
|
|
|
if (MATCH(KW_SIZE)) {
|
|
|
|
MATCH(KW_EQUALS);
|
|
|
|
file->fil_length = EXP_ULONG_ordinal(TRUE);
|
|
|
|
}
|
|
|
|
// **** REMOVED for now from the product.
|
|
|
|
// else if (MATCH (KW_RAW_PARTITIONS))
|
|
|
|
// {
|
|
|
|
// if (log_serial)
|
|
|
|
// PAR_error ("Partitions not supported in series of log file specification");
|
|
|
|
// MATCH (KW_EQUALS);
|
|
|
|
// file->fil_partitions = EXP_USHORT_ordinal (TRUE);
|
|
|
|
// file->fil_flags |= FIL_raw;
|
|
|
|
// }
|
|
|
|
//***
|
|
|
|
else
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (file->fil_partitions) {
|
|
|
|
if (!file->fil_length) {
|
|
|
|
sprintf(err_string,
|
|
|
|
"Total length of the partitioned log %s must be specified",
|
|
|
|
file->fil_name);
|
|
|
|
PAR_error(err_string);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (PARTITION_SIZE(OneK * file->fil_length, file->fil_partitions) <
|
|
|
|
OneK * MIN_LOG_LENGTH) {
|
|
|
|
sprintf(err_string, "log partition size too small for %s",
|
|
|
|
file->fil_name);
|
|
|
|
PAR_error(err_string);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
if ((file->fil_length) && (file->fil_length < MIN_LOG_LENGTH)) {
|
|
|
|
/* msg 336: Minimum log length should be MIN_LOG_LENGTH Kbytes */
|
|
|
|
sprintf(err_string,
|
|
|
|
"Minimum log length should be %d Kbytes", MIN_LOG_LENGTH);
|
|
|
|
PAR_error(err_string);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return file;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static DBB dup_dbb( DBB db)
|
|
|
|
{
|
|
|
|
|
|
|
|
// ****************************************
|
|
|
|
//
|
|
|
|
// d u p _ d b b
|
|
|
|
//
|
|
|
|
// ****************************************
|
|
|
|
//
|
|
|
|
// dirty duplication of a dbb.
|
|
|
|
// just memcpy as no memory
|
|
|
|
// is freed in gpre.
|
|
|
|
//
|
|
|
|
// *************************************
|
|
|
|
DBB newdb;
|
|
|
|
|
|
|
|
if (!db)
|
|
|
|
return NULL;
|
|
|
|
newdb = (DBB) ALLOC(DBB_LEN);
|
|
|
|
memcpy((SCHAR *) newdb, (SCHAR *) db, (size_t) DBB_LEN);
|
|
|
|
|
|
|
|
return newdb;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//____________________________________________________________
|
|
|
|
//
|
|
|
|
// Report an error with parameter
|
|
|
|
//
|
|
|
|
|
|
|
|
static void error( TEXT * string, TEXT * string2)
|
|
|
|
{
|
|
|
|
TEXT buffer[256];
|
|
|
|
|
|
|
|
sprintf(buffer, string, string2);
|
|
|
|
PAR_error(buffer);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//____________________________________________________________
|
|
|
|
//
|
|
|
|
// Extract string from "string" in
|
|
|
|
// token.
|
|
|
|
//
|
|
|
|
|
|
|
|
static TEXT *extract_string( BOOLEAN advance_token)
|
|
|
|
{
|
|
|
|
TEXT *string = NULL;
|
|
|
|
|
|
|
|
switch (sw_sql_dialect) {
|
|
|
|
case 1:
|
|
|
|
if (!QUOTED(token.tok_type))
|
|
|
|
SYNTAX_ERROR("<string>");
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
if (!SINGLE_QUOTED(token.tok_type))
|
|
|
|
SYNTAX_ERROR("<string>");
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
string = (TEXT *) ALLOC(token.tok_length + 1);
|
|
|
|
COPY(token.tok_string, token.tok_length, string);
|
|
|
|
if (advance_token)
|
|
|
|
ADVANCE_TOKEN;
|
|
|
|
return string;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//____________________________________________________________
|
|
|
|
//
|
|
|
|
// Generate a linked list of SQL WHENEVER items for error
|
|
|
|
// handling.
|
|
|
|
//
|
|
|
|
|
|
|
|
static SWE gen_whenever(void)
|
|
|
|
{
|
|
|
|
SWE label, prior, proto;
|
|
|
|
USHORT i, l;
|
|
|
|
TEXT *p, *q;
|
|
|
|
|
|
|
|
if (whenever_list)
|
|
|
|
return whenever_list;
|
|
|
|
|
|
|
|
whenever_list = label = NULL;
|
|
|
|
|
|
|
|
for (i = 0; i < SWE_max; i++)
|
|
|
|
if (proto = whenever[i]) {
|
|
|
|
prior = label;
|
|
|
|
l = proto->swe_length;
|
|
|
|
label = (SWE) ALLOC(sizeof(struct swe) + l);
|
|
|
|
label->swe_next = prior;
|
|
|
|
label->swe_condition = proto->swe_condition;
|
|
|
|
if (l) {
|
|
|
|
p = label->swe_label;
|
|
|
|
q = proto->swe_label;
|
|
|
|
do
|
|
|
|
*p++ = *q++;
|
|
|
|
while (--l);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return label;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//____________________________________________________________
|
|
|
|
//
|
|
|
|
// Correlate the into list with the select expression list
|
|
|
|
// to form full references (post same against request).
|
|
|
|
//
|
|
|
|
|
2002-11-17 01:04:19 +01:00
|
|
|
static void into( GPRE_REQ request, GPRE_NOD field_list, GPRE_NOD var_list)
|
2001-05-23 15:26:42 +02:00
|
|
|
{
|
|
|
|
REF var_ref, field_ref, reference;
|
2002-11-11 20:19:43 +01:00
|
|
|
GPRE_NOD *var_ptr, *fld_ptr, *end;
|
2002-11-30 18:45:02 +01:00
|
|
|
GPRE_FLD field;
|
2002-11-17 01:04:19 +01:00
|
|
|
GPRE_REQ slice_req;
|
2001-05-23 15:26:42 +02:00
|
|
|
SSHORT found = FALSE;
|
|
|
|
|
|
|
|
if (!var_list)
|
|
|
|
PAR_error("INTO list is required");
|
|
|
|
|
|
|
|
if (!field_list || field_list->nod_count != var_list->nod_count)
|
|
|
|
PAR_error
|
|
|
|
("column count and number of INTO list host-variables unequal");
|
|
|
|
|
|
|
|
var_ptr = var_list->nod_arg;
|
|
|
|
fld_ptr = field_list->nod_arg;
|
|
|
|
|
|
|
|
for (end = fld_ptr + var_list->nod_count; fld_ptr < end;
|
|
|
|
fld_ptr++, var_ptr++) {
|
|
|
|
var_ref = (REF) * var_ptr;
|
|
|
|
if (((*fld_ptr)->nod_type == nod_field)
|
|
|
|
|| ((*fld_ptr)->nod_type == nod_array)) {
|
|
|
|
field_ref = (REF) (*fld_ptr)->nod_arg[0];
|
2002-11-17 01:04:19 +01:00
|
|
|
slice_req = (GPRE_REQ) (*fld_ptr)->nod_arg[2];
|
2001-05-23 15:26:42 +02:00
|
|
|
}
|
|
|
|
else
|
|
|
|
field_ref = 0;
|
|
|
|
if (field_ref && slice_req && (field = field_ref->ref_field)
|
|
|
|
&& field->fld_array) {
|
|
|
|
EXP_post_array(field_ref);
|
|
|
|
|
|
|
|
/* If field ref not posted yet, post it */
|
|
|
|
|
|
|
|
found = FALSE;
|
|
|
|
for (reference = request->req_references; reference;
|
|
|
|
reference = reference->ref_next) {
|
|
|
|
if (reference == field_ref) {
|
|
|
|
found = TRUE;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (!found) {
|
|
|
|
field_ref->ref_next = request->req_references;
|
|
|
|
request->req_references = field_ref;
|
|
|
|
}
|
|
|
|
reference = field_ref;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
reference = SQE_post_reference(request, 0, 0, *fld_ptr);
|
|
|
|
var_ref->ref_friend = reference;
|
|
|
|
reference->ref_null_value = var_ref->ref_null_value;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//____________________________________________________________
|
|
|
|
//
|
|
|
|
// Create field in a relation for a metadata request.
|
|
|
|
//
|
|
|
|
|
2002-11-30 18:45:02 +01:00
|
|
|
static GPRE_FLD make_field( GPRE_REL relation)
|
2001-05-23 15:26:42 +02:00
|
|
|
{
|
2002-11-30 18:45:02 +01:00
|
|
|
GPRE_FLD field;
|
2001-05-23 15:26:42 +02:00
|
|
|
char s[ERROR_LENGTH];
|
|
|
|
|
|
|
|
SQL_resolve_identifier("<column name>", s);
|
|
|
|
field = MET_make_field(s, 0, 0, TRUE);
|
|
|
|
field->fld_relation = relation;
|
|
|
|
field->fld_flags |= FLD_meta;
|
|
|
|
ADVANCE_TOKEN;
|
|
|
|
|
|
|
|
return field;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//____________________________________________________________
|
|
|
|
//
|
|
|
|
// Create index for metadata request.
|
|
|
|
//
|
|
|
|
|
2002-11-17 01:04:19 +01:00
|
|
|
static IND make_index( GPRE_REQ request, TEXT * string)
|
2001-05-23 15:26:42 +02:00
|
|
|
{
|
|
|
|
IND index;
|
|
|
|
TEXT s[ERROR_LENGTH];
|
|
|
|
|
|
|
|
if ((isc_databases) && (!(isc_databases->dbb_next))) {
|
|
|
|
strcpy(s, string);
|
|
|
|
index = MET_make_index(s);
|
|
|
|
if (request)
|
|
|
|
request->req_database = isc_databases;
|
|
|
|
index->ind_flags |= IND_meta;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
PAR_error("Can only reference INDEX in context of single database");
|
|
|
|
|
|
|
|
return index;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//____________________________________________________________
|
|
|
|
//
|
|
|
|
// Create relation for a metadata request.
|
|
|
|
//
|
|
|
|
|
2002-11-17 01:04:19 +01:00
|
|
|
static GPRE_REL make_relation( GPRE_REQ request, TEXT * relation_name)
|
2001-05-23 15:26:42 +02:00
|
|
|
{
|
2002-11-17 01:04:19 +01:00
|
|
|
GPRE_REL relation;
|
2001-05-23 15:26:42 +02:00
|
|
|
TEXT r[ERROR_LENGTH];
|
|
|
|
|
|
|
|
if (isc_databases && !isc_databases->dbb_next) {
|
|
|
|
strcpy(r, relation_name);
|
|
|
|
|
|
|
|
relation = MET_make_relation(r);
|
|
|
|
relation->rel_database = isc_databases;
|
|
|
|
relation->rel_meta = TRUE;
|
|
|
|
|
|
|
|
if (request)
|
|
|
|
request->req_database = isc_databases;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
PAR_error("Can only reference TABLE in context of single database");
|
|
|
|
|
|
|
|
return relation;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//____________________________________________________________
|
|
|
|
//
|
|
|
|
// Given two value expressions associated in a relational
|
|
|
|
// expression, see if one is a field reference and the other
|
|
|
|
// is a host language variable.. If so, match the field to the
|
|
|
|
// host language variable.
|
|
|
|
// In other words, here we are guessing what the datatype is
|
|
|
|
// of a host language variable.
|
|
|
|
//
|
|
|
|
|
2002-11-11 20:19:43 +01:00
|
|
|
static void pair( GPRE_NOD expr, GPRE_NOD field_expr)
|
2001-05-23 15:26:42 +02:00
|
|
|
{
|
2002-11-11 20:19:43 +01:00
|
|
|
GPRE_NOD *ptr, *end_ptr;
|
2001-05-23 15:26:42 +02:00
|
|
|
REF ref1, ref2;
|
|
|
|
MEL element;
|
|
|
|
|
|
|
|
if (field_expr->nod_type != nod_field)
|
|
|
|
return;
|
|
|
|
|
|
|
|
switch (expr->nod_type) {
|
|
|
|
case nod_value:
|
|
|
|
ref1 = (REF) field_expr->nod_arg[0];
|
|
|
|
ref2 = (REF) expr->nod_arg[0];
|
|
|
|
|
|
|
|
/* We're done if we've already determined the type of this reference
|
|
|
|
* (if, for instance, it's a parameter to a UDF or PROCEDURE)
|
|
|
|
*/
|
|
|
|
if (ref2->ref_field)
|
|
|
|
return;
|
|
|
|
|
|
|
|
ref2->ref_field = ref1->ref_field;
|
|
|
|
if (ref1->ref_field->fld_array) {
|
|
|
|
ref2->ref_context = ref1->ref_context;
|
|
|
|
ref2->ref_friend = ref1;
|
|
|
|
EXP_post_array(ref1);
|
|
|
|
EXP_post_array(ref2);
|
|
|
|
}
|
|
|
|
return;
|
|
|
|
|
|
|
|
case nod_map_ref:
|
|
|
|
element = (MEL) expr->nod_arg[0];
|
|
|
|
pair(element->mel_expr, field_expr);
|
|
|
|
return;
|
|
|
|
|
|
|
|
case nod_field:
|
|
|
|
case nod_literal:
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
ptr = expr->nod_arg;
|
|
|
|
end_ptr = ptr + expr->nod_count;
|
|
|
|
|
|
|
|
for (; ptr < end_ptr; ptr++)
|
|
|
|
pair(*ptr, field_expr);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//____________________________________________________________
|
|
|
|
//
|
|
|
|
// Parse the multi-dimensional array specification.
|
|
|
|
//
|
|
|
|
|
2002-11-30 18:45:02 +01:00
|
|
|
static void par_array( GPRE_FLD field)
|
2001-05-23 15:26:42 +02:00
|
|
|
{
|
|
|
|
SLONG rangeh, rangel;
|
|
|
|
SSHORT i = 0;
|
|
|
|
ARY array_info;
|
|
|
|
|
|
|
|
// Pick up ranges
|
|
|
|
|
|
|
|
array_info = field->fld_array_info;
|
|
|
|
|
|
|
|
while (TRUE) {
|
|
|
|
rangel = EXP_SSHORT_ordinal(TRUE);
|
|
|
|
if (MATCH(KW_COLON))
|
|
|
|
rangeh = EXP_SSHORT_ordinal(TRUE);
|
|
|
|
else {
|
|
|
|
if (rangel < 1)
|
|
|
|
rangeh = 1;
|
|
|
|
else {
|
|
|
|
rangeh = rangel;
|
|
|
|
rangel = 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (rangel >= rangeh)
|
|
|
|
PAR_error("Start of array range must be less than end of range");
|
|
|
|
|
|
|
|
if (i > MAX_ARRAY_DIMENSIONS)
|
|
|
|
PAR_error("Array has too many dimensions");
|
|
|
|
|
|
|
|
array_info->ary_rpt[i].ary_lower = rangel;
|
|
|
|
array_info->ary_rpt[i++].ary_upper = rangeh;
|
|
|
|
|
|
|
|
if (MATCH(KW_R_BRCKET))
|
|
|
|
break;
|
|
|
|
|
|
|
|
if (!MATCH(KW_COMMA))
|
|
|
|
SYNTAX_ERROR(", (comma)");
|
|
|
|
}
|
|
|
|
|
|
|
|
array_info->ary_dimension_count = i;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//____________________________________________________________
|
|
|
|
//
|
|
|
|
//
|
|
|
|
// Read in the specified character set on
|
|
|
|
// a READ BLOB or an INSERT BLOB.
|
|
|
|
//
|
|
|
|
|
|
|
|
static SSHORT par_char_set(void)
|
|
|
|
{
|
|
|
|
SYM symbol;
|
|
|
|
|
|
|
|
if (!MATCH(KW_CHAR))
|
|
|
|
SYNTAX_ERROR("CHARACTER SET");
|
|
|
|
|
|
|
|
if (!MATCH(KW_SET))
|
|
|
|
SYNTAX_ERROR("CHARACTER SET");
|
|
|
|
|
|
|
|
if (token.tok_type != tok_ident)
|
|
|
|
SYNTAX_ERROR("<character set name>");
|
|
|
|
|
|
|
|
if (!(symbol = MSC_find_symbol(token.tok_symbol, SYM_charset)))
|
|
|
|
PAR_error("The named CHARACTER SET was not found");
|
|
|
|
|
|
|
|
ADVANCE_TOKEN;
|
|
|
|
|
|
|
|
return (((INTLSYM) symbol->sym_object)->intlsym_charset_id);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//____________________________________________________________
|
|
|
|
//
|
|
|
|
// Create a computed field
|
|
|
|
//
|
|
|
|
|
2002-11-30 18:45:02 +01:00
|
|
|
static void par_computed( GPRE_REQ request, GPRE_FLD field)
|
2001-05-23 15:26:42 +02:00
|
|
|
{
|
|
|
|
CMPF cmp;
|
2002-11-30 18:45:02 +01:00
|
|
|
struct gpre_fld save_fld;
|
2001-05-23 15:26:42 +02:00
|
|
|
|
|
|
|
MATCH(KW_BY);
|
|
|
|
|
|
|
|
// If field size has been specified, save it. Then NULL it
|
|
|
|
// till the new size is calculated from the specified expression.
|
|
|
|
// This will catch self references.
|
|
|
|
//
|
|
|
|
// The user specified values will be restored to override calculated
|
|
|
|
// values.
|
|
|
|
//
|
|
|
|
|
|
|
|
save_fld = *field;
|
|
|
|
|
|
|
|
cmp = (CMPF) ALLOC(CMPF_LEN);
|
|
|
|
cmp->cmpf_text = CPR_start_text();
|
|
|
|
cmp->cmpf_boolean = SQE_value(request, FALSE, 0, 0);
|
|
|
|
CPR_end_text(cmp->cmpf_text);
|
|
|
|
|
|
|
|
field->fld_computed = cmp;
|
|
|
|
field->fld_flags |= FLD_computed;
|
|
|
|
|
|
|
|
CME_get_dtype(field->fld_computed->cmpf_boolean, field);
|
|
|
|
|
|
|
|
// If there was user specified data type/size, restore it
|
|
|
|
|
|
|
|
if (save_fld.fld_dtype) {
|
|
|
|
field->fld_dtype = save_fld.fld_dtype;
|
|
|
|
field->fld_length = save_fld.fld_length;
|
|
|
|
field->fld_scale = save_fld.fld_scale;
|
|
|
|
field->fld_sub_type = save_fld.fld_sub_type;
|
|
|
|
field->fld_ttype = save_fld.fld_ttype;
|
|
|
|
field->fld_charset_id = save_fld.fld_charset_id;
|
|
|
|
field->fld_collate_id = save_fld.fld_collate_id;
|
|
|
|
field->fld_char_length = save_fld.fld_char_length;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//____________________________________________________________
|
|
|
|
//
|
|
|
|
// Parse the next token as a cursor name.
|
|
|
|
// If it is, return the request associated with the cursor. If
|
|
|
|
// not, produce an error and return NULL.
|
|
|
|
//
|
|
|
|
|
2002-11-17 01:04:19 +01:00
|
|
|
static GPRE_REQ par_cursor( SYM * symbol_ptr)
|
2001-05-23 15:26:42 +02:00
|
|
|
{
|
|
|
|
SYM symbol;
|
|
|
|
TEXT t_cur[128];
|
|
|
|
|
|
|
|
// **
|
|
|
|
// par_cursor() is called to use a previously declared cursor.
|
|
|
|
// tok_symbol == NULL means one of the two things.
|
|
|
|
// a) The name does not belong to a cursor. OR
|
|
|
|
// b) get_token() function in gpre.c was not able to find the cursor
|
|
|
|
// in hash table.
|
|
|
|
//
|
|
|
|
// case a) is an error condition.
|
|
|
|
// case b) Could have resulted because the cursor name was upcased and
|
|
|
|
// inserted into hash table since it was not quoted and it is
|
|
|
|
// now being refered as it was declared.
|
|
|
|
//
|
|
|
|
// Hence, Try and lookup the cursor name after resolving it once more. If
|
|
|
|
// it still cannot be located, Its an error
|
|
|
|
//*
|
|
|
|
|
|
|
|
SQL_resolve_identifier("<cursor name>", t_cur);
|
|
|
|
token.tok_symbol = symbol = HSH_lookup(token.tok_string);
|
|
|
|
if (symbol && symbol->sym_type == SYM_keyword)
|
|
|
|
token.tok_keyword = (KWWORDS) symbol->sym_keyword;
|
|
|
|
else
|
|
|
|
token.tok_keyword = KW_none;
|
|
|
|
|
|
|
|
symbol = MSC_find_symbol(token.tok_symbol, SYM_cursor);
|
|
|
|
if (!symbol)
|
|
|
|
symbol = MSC_find_symbol(token.tok_symbol, SYM_delimited_cursor);
|
|
|
|
if (symbol) {
|
|
|
|
ADVANCE_TOKEN;
|
|
|
|
if (symbol_ptr)
|
|
|
|
*symbol_ptr = symbol;
|
2002-11-17 01:04:19 +01:00
|
|
|
return (GPRE_REQ) symbol->sym_object;
|
2001-05-23 15:26:42 +02:00
|
|
|
}
|
|
|
|
else if (symbol = MSC_find_symbol(token.tok_symbol, SYM_dyn_cursor))
|
|
|
|
PAR_error("DSQL cursors require DSQL update & delete statements");
|
|
|
|
SYNTAX_ERROR("<cursor name>");
|
|
|
|
return NULL; /* silence compiler */
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//____________________________________________________________
|
|
|
|
//
|
|
|
|
// If this is a dynamic curser, return dynamic statement block.
|
|
|
|
//
|
|
|
|
|
|
|
|
static DYN par_dynamic_cursor(void)
|
|
|
|
{
|
|
|
|
SYM symbol;
|
|
|
|
TEXT t_cur[128];
|
|
|
|
|
|
|
|
if (token.tok_symbol == NULL) {
|
|
|
|
SQL_resolve_identifier("<cursor name>", t_cur);
|
|
|
|
token.tok_symbol = symbol = HSH_lookup(token.tok_string);
|
|
|
|
if (symbol && symbol->sym_type == SYM_keyword)
|
|
|
|
token.tok_keyword = (KWWORDS) symbol->sym_keyword;
|
|
|
|
else
|
|
|
|
token.tok_keyword = KW_none;
|
|
|
|
}
|
|
|
|
if (symbol = MSC_find_symbol(token.tok_symbol, SYM_dyn_cursor)) {
|
|
|
|
ADVANCE_TOKEN;
|
|
|
|
return (DYN) symbol->sym_object;
|
|
|
|
}
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//____________________________________________________________
|
|
|
|
//
|
|
|
|
// Handle an SQL field definition in CREATE, DECLARE or
|
|
|
|
// ALTER TABLE statement.
|
|
|
|
//
|
|
|
|
|
2002-11-30 18:45:02 +01:00
|
|
|
static GPRE_FLD par_field( GPRE_REQ request, GPRE_REL relation)
|
2001-05-23 15:26:42 +02:00
|
|
|
{
|
2002-11-30 18:45:02 +01:00
|
|
|
GPRE_FLD field;
|
2001-05-23 15:26:42 +02:00
|
|
|
// *IND index;
|
|
|
|
CNSTRT *cnstrt;
|
|
|
|
int in_constraints;
|
2002-11-11 20:19:43 +01:00
|
|
|
GPRE_NOD literal_node;
|
2001-05-23 15:26:42 +02:00
|
|
|
|
|
|
|
field = make_field(relation);
|
|
|
|
|
|
|
|
SQL_par_field_dtype(request, field, FALSE);
|
|
|
|
|
|
|
|
if (MATCH(KW_COMPUTED)) {
|
|
|
|
if (field->fld_global)
|
|
|
|
PAR_error("Cannot use domains to override computed column size");
|
|
|
|
if (field->fld_array_info)
|
|
|
|
PAR_error("Computed columns cannot be arrays");
|
|
|
|
|
|
|
|
par_computed(request, field);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Check if default value was specified
|
|
|
|
|
|
|
|
if (token.tok_keyword == KW_DEFAULT) {
|
|
|
|
field->fld_default_source = CPR_start_text();
|
|
|
|
ADVANCE_TOKEN;
|
|
|
|
|
|
|
|
if (MATCH(KW_USER))
|
|
|
|
field->fld_default_value = MAKE_NODE(nod_user_name, 0);
|
|
|
|
// ** Begin sql/date/time/timestamp *
|
|
|
|
else if (MATCH(KW_CURRENT_DATE))
|
|
|
|
field->fld_default_value = MAKE_NODE(nod_current_date, 0);
|
|
|
|
else if (MATCH(KW_CURRENT_TIME))
|
|
|
|
field->fld_default_value = MAKE_NODE(nod_current_time, 0);
|
|
|
|
else if (MATCH(KW_CURRENT_TIMESTAMP))
|
|
|
|
field->fld_default_value = MAKE_NODE(nod_current_timestamp, 0);
|
|
|
|
// ** End sql/date/time/timestamp *
|
|
|
|
else if (MATCH(KW_NULL))
|
|
|
|
field->fld_default_value = MAKE_NODE(nod_null, 0);
|
|
|
|
else {
|
|
|
|
if (MATCH(KW_MINUS)) {
|
|
|
|
if (token.tok_type != tok_number)
|
|
|
|
SYNTAX_ERROR("<number>");
|
|
|
|
|
|
|
|
literal_node = EXP_literal();
|
|
|
|
field->fld_default_value = MSC_unary(nod_negate,
|
|
|
|
literal_node);
|
|
|
|
}
|
|
|
|
else if ((field->fld_default_value = EXP_literal()) == NULL)
|
|
|
|
SYNTAX_ERROR("<constant>");
|
|
|
|
}
|
|
|
|
CPR_end_text(field->fld_default_source);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Check for any column level constraints
|
|
|
|
|
|
|
|
cnstrt = &field->fld_constraints;
|
|
|
|
in_constraints = TRUE;
|
|
|
|
|
|
|
|
while (in_constraints) {
|
|
|
|
switch (token.tok_keyword) {
|
|
|
|
case KW_CONSTRAINT:
|
|
|
|
case KW_PRIMARY:
|
|
|
|
case KW_UNIQUE:
|
|
|
|
case KW_REFERENCES:
|
|
|
|
case KW_CHECK:
|
|
|
|
case KW_NOT:
|
|
|
|
*cnstrt = par_field_constraint(request, field, relation);
|
|
|
|
cnstrt = &(*cnstrt)->cnstrt_next;
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
in_constraints = FALSE;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// if (MATCH (KW_NOT))
|
|
|
|
// if (MATCH (KW_NULL))
|
|
|
|
// {
|
|
|
|
// field->fld_flags |= FLD_not_null;
|
|
|
|
// if (MATCH (KW_UNIQUE))
|
|
|
|
// {
|
|
|
|
// index = (IND) ALLOC (IND_LEN);
|
|
|
|
// index->ind_relation = relation;
|
|
|
|
// index->ind_fields = field;
|
|
|
|
// index->ind_flags |= IND_dup_flag | IND_meta;
|
|
|
|
// index->ind_flags &= ~IND_descend;
|
|
|
|
// field->fld_index = index;
|
|
|
|
// }
|
|
|
|
// }
|
|
|
|
// else
|
|
|
|
// SYNTAX_ERROR ("NULL");
|
|
|
|
|
|
|
|
SQL_par_field_collate(request, field);
|
|
|
|
SQL_adjust_field_dtype(field);
|
|
|
|
|
|
|
|
return field;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//____________________________________________________________
|
|
|
|
//
|
|
|
|
// Create a field level constraint as part of CREATE TABLE or
|
|
|
|
// ALTER TABLE statement. Constraint maybe table or column level.
|
|
|
|
//
|
|
|
|
|
2002-11-30 18:45:02 +01:00
|
|
|
static CNSTRT par_field_constraint( GPRE_REQ request, GPRE_FLD for_field, GPRE_REL relation)
|
2001-05-23 15:26:42 +02:00
|
|
|
{
|
|
|
|
enum kwwords keyword;
|
|
|
|
CNSTRT cnstrt;
|
|
|
|
STR field_name;
|
|
|
|
|
|
|
|
cnstrt = (CNSTRT) ALLOC(CNSTRT_LEN);
|
|
|
|
|
|
|
|
if (token.tok_keyword == KW_CONSTRAINT) {
|
|
|
|
ADVANCE_TOKEN;
|
|
|
|
cnstrt->cnstrt_name = (STR) ALLOC(NAME_SIZE + 1);
|
|
|
|
SQL_resolve_identifier("<constraint name>",
|
|
|
|
(TEXT *) cnstrt->cnstrt_name);
|
|
|
|
if (token.tok_length > NAME_SIZE)
|
|
|
|
PAR_error("Constraint name too long");
|
|
|
|
ADVANCE_TOKEN;
|
|
|
|
}
|
|
|
|
|
|
|
|
switch (keyword = token.tok_keyword) {
|
|
|
|
case KW_NOT:
|
|
|
|
ADVANCE_TOKEN;
|
|
|
|
if (!MATCH(KW_NULL))
|
|
|
|
SYNTAX_ERROR("NULL");
|
|
|
|
cnstrt->cnstrt_type = CNSTRT_NOT_NULL;
|
|
|
|
for_field->fld_flags |= FLD_not_null;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case KW_PRIMARY:
|
|
|
|
case KW_UNIQUE:
|
|
|
|
case KW_REFERENCES:
|
|
|
|
ADVANCE_TOKEN;
|
|
|
|
if (keyword == KW_PRIMARY) {
|
|
|
|
if (!MATCH(KW_KEY))
|
|
|
|
SYNTAX_ERROR("KEY");
|
|
|
|
cnstrt->cnstrt_type = CNSTRT_PRIMARY_KEY;
|
|
|
|
}
|
|
|
|
else if (keyword == KW_REFERENCES) {
|
|
|
|
cnstrt->cnstrt_type = CNSTRT_FOREIGN_KEY;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
cnstrt->cnstrt_type = CNSTRT_UNIQUE;
|
|
|
|
|
|
|
|
/* Set field for PRIMARY KEY or FOREIGN KEY or UNIQUE constraint */
|
|
|
|
|
|
|
|
field_name = (STR) ALLOC(NAME_SIZE + 1);
|
|
|
|
strcpy((char *) field_name, for_field->fld_symbol->sym_string);
|
2002-11-11 20:19:43 +01:00
|
|
|
PUSH((GPRE_NOD) field_name, &cnstrt->cnstrt_fields);
|
2001-05-23 15:26:42 +02:00
|
|
|
|
|
|
|
if (keyword == KW_REFERENCES) {
|
|
|
|
/* Relation name for foreign key */
|
|
|
|
|
|
|
|
cnstrt->cnstrt_referred_rel = (STR) ALLOC(NAME_SIZE + 1);
|
|
|
|
SQL_resolve_identifier("referred <table name>",
|
|
|
|
(TEXT *) cnstrt->cnstrt_referred_rel);
|
|
|
|
if (token.tok_length > NAME_SIZE)
|
|
|
|
PAR_error("Referred table name too long");
|
|
|
|
ADVANCE_TOKEN;
|
|
|
|
|
|
|
|
if (MATCH(KW_LEFT_PAREN)) {
|
|
|
|
/* Field specified for referred relation */
|
|
|
|
|
|
|
|
field_name = (STR) ALLOC(NAME_SIZE + 1);
|
|
|
|
SQL_resolve_identifier("<column name>", (TEXT *) field_name);
|
2002-11-11 20:19:43 +01:00
|
|
|
PUSH((GPRE_NOD) field_name, &cnstrt->cnstrt_referred_fields);
|
2001-05-23 15:26:42 +02:00
|
|
|
CPR_token();
|
|
|
|
EXP_match_paren();
|
|
|
|
}
|
|
|
|
|
|
|
|
if (token.tok_keyword == KW_ON) {
|
|
|
|
par_fkey_extension(cnstrt);
|
|
|
|
ADVANCE_TOKEN;
|
|
|
|
if (token.tok_keyword == KW_ON) {
|
|
|
|
par_fkey_extension(cnstrt);
|
|
|
|
ADVANCE_TOKEN;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case KW_CHECK:
|
|
|
|
ADVANCE_TOKEN;
|
|
|
|
cnstrt->cnstrt_type = CNSTRT_CHECK;
|
|
|
|
cnstrt->cnstrt_text = CPR_start_text();
|
|
|
|
cnstrt->cnstrt_boolean = SQE_boolean(request, 0);
|
|
|
|
CPR_end_text(cnstrt->cnstrt_text);
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
PAR_error("Invalid constraint type");
|
|
|
|
}
|
|
|
|
|
|
|
|
return cnstrt;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//____________________________________________________________
|
|
|
|
//
|
|
|
|
// Parse the INTO clause for a dynamic SQL statement.
|
|
|
|
//
|
|
|
|
|
|
|
|
static BOOLEAN par_into( DYN statement)
|
|
|
|
{
|
|
|
|
|
|
|
|
if (!MATCH(KW_INTO))
|
|
|
|
return FALSE;
|
|
|
|
|
|
|
|
MATCH(KW_SQL); /* "SQL" keyword is optional for backward compatibility */
|
|
|
|
|
|
|
|
if (!MATCH(KW_DESCRIPTOR))
|
|
|
|
SYNTAX_ERROR("DESCRIPTOR");
|
|
|
|
|
|
|
|
statement->dyn_sqlda2 = PAR_native_value(FALSE, FALSE);
|
|
|
|
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//____________________________________________________________
|
|
|
|
//
|
|
|
|
// Parse request options.
|
|
|
|
//
|
|
|
|
|
|
|
|
static void par_options( TEXT ** transaction)
|
|
|
|
{
|
|
|
|
|
|
|
|
*transaction = NULL;
|
|
|
|
|
|
|
|
if (!MATCH(KW_TRANSACTION))
|
|
|
|
return;
|
|
|
|
|
|
|
|
*transaction = PAR_native_value(FALSE, TRUE);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//____________________________________________________________
|
|
|
|
//
|
|
|
|
// parse the page_size clause of a
|
|
|
|
// create database statement
|
|
|
|
//
|
|
|
|
|
|
|
|
static int par_page_size(void)
|
|
|
|
{
|
|
|
|
int n1, n2;
|
|
|
|
|
|
|
|
MATCH(KW_EQUALS);
|
|
|
|
n2 = n1 = EXP_USHORT_ordinal(FALSE);
|
|
|
|
|
|
|
|
if (n1 <= 1024)
|
|
|
|
n2 = 1024;
|
|
|
|
else if (n1 <= 2048)
|
|
|
|
n2 = 2048;
|
|
|
|
else if (n1 <= 4096)
|
|
|
|
n2 = 4096;
|
|
|
|
else if (n1 <= 8192)
|
|
|
|
n2 = 8192;
|
|
|
|
else
|
|
|
|
SYNTAX_ERROR("<page size> in range 1024 to 8096");
|
|
|
|
ADVANCE_TOKEN;
|
|
|
|
|
|
|
|
return n2;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//____________________________________________________________
|
|
|
|
//
|
|
|
|
// Parse the next thing as a relation.
|
|
|
|
//
|
|
|
|
|
2002-11-17 01:04:19 +01:00
|
|
|
static GPRE_REL par_relation( GPRE_REQ request)
|
2001-05-23 15:26:42 +02:00
|
|
|
{
|
|
|
|
SCHAR r_name[NAME_SIZE + 1], db_name[NAME_SIZE + 1],
|
|
|
|
owner_name[NAME_SIZE + 1];
|
|
|
|
|
|
|
|
SQL_relation_name(r_name, db_name, owner_name);
|
|
|
|
|
|
|
|
return make_relation(request, r_name);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//____________________________________________________________
|
|
|
|
//
|
|
|
|
// Parse a dynamic statement name returning a dynamic
|
|
|
|
// statement block.
|
|
|
|
//
|
|
|
|
|
|
|
|
static DYN par_statement(void)
|
|
|
|
{
|
|
|
|
DYN statement;
|
|
|
|
|
|
|
|
statement = (DYN) ALLOC(DYN_LEN);
|
|
|
|
statement->dyn_statement_name = PAR_symbol(SYM_dummy);
|
|
|
|
statement->dyn_database = isc_databases;
|
|
|
|
|
|
|
|
return statement;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//____________________________________________________________
|
|
|
|
//
|
|
|
|
// Handle an extended foreign key definition as part of CREATE TABLE
|
|
|
|
// or ALTER TABLE statements.
|
|
|
|
//
|
|
|
|
|
|
|
|
static void par_fkey_extension( CNSTRT cnstrt)
|
|
|
|
{
|
|
|
|
enum kwwords keyword;
|
|
|
|
|
|
|
|
//
|
|
|
|
// Extended foreign key definition could be as follows :
|
|
|
|
//
|
|
|
|
// [ON DELETE { NO ACTION | CASCADE | SET DEFAULT | SET NULL } ]
|
|
|
|
// [ON UPDATE { NO ACTION | CASCADE | SET DEFAULT | SET NULL } ]
|
|
|
|
//
|
|
|
|
|
|
|
|
assert(token.tok_keyword == KW_ON)
|
|
|
|
assert(cnstrt != NULL);
|
|
|
|
|
|
|
|
ADVANCE_TOKEN;
|
|
|
|
|
|
|
|
switch (keyword = token.tok_keyword) {
|
|
|
|
case KW_DELETE:
|
|
|
|
/* NOTE: action must be defined only once */
|
|
|
|
if (cnstrt->cnstrt_fkey_def_type & REF_DELETE_ACTION)
|
|
|
|
SYNTAX_ERROR("UPDATE");
|
|
|
|
else
|
|
|
|
cnstrt->cnstrt_fkey_def_type |= REF_DELETE_ACTION;
|
|
|
|
break;
|
|
|
|
case KW_UPDATE:
|
|
|
|
/* NOTE: action must be defined only once */
|
|
|
|
if (cnstrt->cnstrt_fkey_def_type & REF_UPDATE_ACTION)
|
|
|
|
SYNTAX_ERROR("DELETE");
|
|
|
|
else
|
|
|
|
cnstrt->cnstrt_fkey_def_type |= REF_UPDATE_ACTION;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
/* unexpected keyword */
|
|
|
|
SYNTAX_ERROR("UPDATE or DELETE");
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
ADVANCE_TOKEN;
|
|
|
|
|
|
|
|
switch (token.tok_keyword) {
|
|
|
|
case KW_NO:
|
|
|
|
ADVANCE_TOKEN;
|
|
|
|
if (token.tok_keyword != KW_ACTION)
|
|
|
|
SYNTAX_ERROR("ACTION");
|
|
|
|
else if (keyword == KW_DELETE)
|
|
|
|
cnstrt->cnstrt_fkey_def_type |= REF_DEL_NONE;
|
|
|
|
else
|
|
|
|
cnstrt->cnstrt_fkey_def_type |= REF_UPD_NONE;
|
|
|
|
break;
|
|
|
|
case KW_CASCADE:
|
|
|
|
if (keyword == KW_DELETE)
|
|
|
|
cnstrt->cnstrt_fkey_def_type |= REF_DEL_CASCADE;
|
|
|
|
else
|
|
|
|
cnstrt->cnstrt_fkey_def_type |= REF_UPD_CASCADE;
|
|
|
|
break;
|
|
|
|
case KW_SET:
|
|
|
|
ADVANCE_TOKEN;
|
|
|
|
switch (token.tok_keyword) {
|
|
|
|
case KW_DEFAULT:
|
|
|
|
if (keyword == KW_DELETE)
|
|
|
|
cnstrt->cnstrt_fkey_def_type |= REF_DEL_SET_DEFAULT;
|
|
|
|
else
|
|
|
|
cnstrt->cnstrt_fkey_def_type |= REF_UPD_SET_DEFAULT;
|
|
|
|
break;
|
|
|
|
case KW_NULL:
|
|
|
|
if (keyword == KW_DELETE)
|
|
|
|
cnstrt->cnstrt_fkey_def_type |= REF_DEL_SET_NULL;
|
|
|
|
else
|
|
|
|
cnstrt->cnstrt_fkey_def_type |= REF_UPD_SET_NULL;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
/* unexpected keyword */
|
|
|
|
SYNTAX_ERROR("NULL or DEFAULT");
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
/* unexpected keyword */
|
|
|
|
SYNTAX_ERROR("NO ACTION or CASCADE or SET DEFAULT or SET NULL");
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//____________________________________________________________
|
|
|
|
//
|
|
|
|
// Handle a create constraint verb as part of CREATE TABLE or
|
|
|
|
// ALTER TABLE statement. Constraint maybe table or column level.
|
|
|
|
//
|
|
|
|
|
2002-11-17 01:04:19 +01:00
|
|
|
static CNSTRT par_table_constraint( GPRE_REQ request, GPRE_REL relation)
|
2001-05-23 15:26:42 +02:00
|
|
|
{
|
|
|
|
enum kwwords keyword;
|
|
|
|
CNSTRT cnstrt;
|
|
|
|
LLS *fields;
|
|
|
|
STR field_name;
|
|
|
|
USHORT num_for_key_flds = 0, num_prim_key_flds = 0;
|
|
|
|
|
|
|
|
cnstrt = (CNSTRT) ALLOC(CNSTRT_LEN);
|
|
|
|
|
|
|
|
if (token.tok_keyword == KW_CONSTRAINT) {
|
|
|
|
ADVANCE_TOKEN;
|
|
|
|
cnstrt->cnstrt_name = (STR) ALLOC(NAME_SIZE + 1);
|
|
|
|
SQL_resolve_identifier("<constraint name>",
|
|
|
|
(TEXT *) cnstrt->cnstrt_name);
|
|
|
|
if (token.tok_length > NAME_SIZE)
|
|
|
|
PAR_error("Constraint name too long");
|
|
|
|
ADVANCE_TOKEN;
|
|
|
|
}
|
|
|
|
|
|
|
|
switch (keyword = token.tok_keyword) {
|
|
|
|
case KW_PRIMARY:
|
|
|
|
case KW_UNIQUE:
|
|
|
|
case KW_FOREIGN:
|
|
|
|
ADVANCE_TOKEN;
|
|
|
|
if (keyword == KW_PRIMARY) {
|
|
|
|
if (!MATCH(KW_KEY))
|
|
|
|
SYNTAX_ERROR("KEY");
|
|
|
|
cnstrt->cnstrt_type = CNSTRT_PRIMARY_KEY;
|
|
|
|
}
|
|
|
|
else if (keyword == KW_FOREIGN) {
|
|
|
|
if (!MATCH(KW_KEY))
|
|
|
|
SYNTAX_ERROR("KEY");
|
|
|
|
cnstrt->cnstrt_type = CNSTRT_FOREIGN_KEY;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
cnstrt->cnstrt_type = CNSTRT_UNIQUE;
|
|
|
|
|
|
|
|
EXP_left_paren(0);
|
|
|
|
|
|
|
|
/* Get list of fields for PRIMARY KEY or FOREIGN KEY or UNIQUE
|
|
|
|
constraint */
|
|
|
|
|
|
|
|
fields = &cnstrt->cnstrt_fields;
|
|
|
|
do {
|
|
|
|
field_name = (STR) ALLOC(NAME_SIZE + 1);
|
|
|
|
SQL_resolve_identifier("<column name>", (TEXT *) field_name);
|
2002-11-11 20:19:43 +01:00
|
|
|
PUSH((GPRE_NOD) field_name, fields);
|
2001-05-23 15:26:42 +02:00
|
|
|
fields = &(*fields)->lls_next;
|
|
|
|
++num_for_key_flds;
|
|
|
|
CPR_token();
|
|
|
|
} while (MATCH(KW_COMMA));
|
|
|
|
|
|
|
|
if (keyword != KW_FOREIGN)
|
|
|
|
num_for_key_flds = 0;
|
|
|
|
|
|
|
|
EXP_match_paren();
|
|
|
|
if (keyword == KW_FOREIGN) {
|
|
|
|
if (!MATCH(KW_REFERENCES))
|
|
|
|
SYNTAX_ERROR("REFERENCES");
|
|
|
|
|
|
|
|
/* Relation name for foreign key */
|
|
|
|
|
|
|
|
cnstrt->cnstrt_referred_rel = (STR) ALLOC(NAME_SIZE + 1);
|
|
|
|
SQL_resolve_identifier("referred <table name>",
|
|
|
|
(TEXT *) cnstrt->cnstrt_referred_rel);
|
|
|
|
if (token.tok_length > NAME_SIZE)
|
|
|
|
PAR_error("Referred table name too long");
|
|
|
|
ADVANCE_TOKEN;
|
|
|
|
|
|
|
|
cnstrt->cnstrt_referred_fields = NULL;
|
|
|
|
|
|
|
|
if (MATCH(KW_LEFT_PAREN)) {
|
|
|
|
/* Fields specified for referred relation */
|
|
|
|
|
|
|
|
fields = &cnstrt->cnstrt_referred_fields;
|
|
|
|
do {
|
|
|
|
field_name = (STR) ALLOC(NAME_SIZE + 1);
|
|
|
|
SQL_resolve_identifier("<column name>",
|
|
|
|
(TEXT *) field_name);
|
2002-11-11 20:19:43 +01:00
|
|
|
PUSH((GPRE_NOD) field_name, fields);
|
2001-05-23 15:26:42 +02:00
|
|
|
fields = &(*fields)->lls_next;
|
|
|
|
++num_prim_key_flds;
|
|
|
|
CPR_token();
|
|
|
|
} while (MATCH(KW_COMMA));
|
|
|
|
|
|
|
|
EXP_match_paren();
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Don't print error message in case if <referenced column list>
|
|
|
|
is not specified. Try to catch them in cmd.c[create_constraint]
|
|
|
|
later on */
|
|
|
|
if (cnstrt->cnstrt_referred_fields != NULL &&
|
|
|
|
num_prim_key_flds != num_for_key_flds)
|
|
|
|
PAR_error
|
|
|
|
("FOREIGN KEY column count does not match PRIMARY KEY");
|
|
|
|
if (token.tok_keyword == KW_ON) {
|
|
|
|
par_fkey_extension(cnstrt);
|
|
|
|
ADVANCE_TOKEN;
|
|
|
|
if (token.tok_keyword == KW_ON) {
|
|
|
|
par_fkey_extension(cnstrt);
|
|
|
|
ADVANCE_TOKEN;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} /* if KW_FOREIGN */
|
|
|
|
break;
|
|
|
|
|
|
|
|
case KW_CHECK:
|
|
|
|
ADVANCE_TOKEN;
|
|
|
|
cnstrt->cnstrt_type = CNSTRT_CHECK;
|
|
|
|
cnstrt->cnstrt_text = CPR_start_text();
|
|
|
|
cnstrt->cnstrt_boolean = SQE_boolean(request, 0);
|
|
|
|
CPR_end_text(cnstrt->cnstrt_text);
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
PAR_error("Invalid constraint type");
|
|
|
|
}
|
|
|
|
|
|
|
|
return cnstrt;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//____________________________________________________________
|
|
|
|
//
|
|
|
|
// Parse the isolation level part.
|
|
|
|
// If expect_iso is true, an isolation level is required.
|
|
|
|
// Returns TRUE if found a match else FALSE.
|
|
|
|
//
|
|
|
|
|
2002-11-17 01:04:19 +01:00
|
|
|
static BOOLEAN par_transaction_modes( GPRE_TRA trans, BOOLEAN expect_iso)
|
2001-05-23 15:26:42 +02:00
|
|
|
{
|
|
|
|
|
|
|
|
if (MATCH(KW_READ)) {
|
|
|
|
if (MATCH(KW_ONLY)) {
|
|
|
|
if (expect_iso)
|
|
|
|
SYNTAX_ERROR("SNAPSHOT");
|
|
|
|
|
|
|
|
trans->tra_flags |= TRA_ro;
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
else if (MATCH(KW_WRITE)) {
|
|
|
|
if (expect_iso)
|
|
|
|
SYNTAX_ERROR("SNAPSHOT");
|
|
|
|
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!(MATCH(KW_COMMITTED) || MATCH(KW_UNCOMMITTED)))
|
|
|
|
SYNTAX_ERROR("COMMITTED");
|
|
|
|
|
|
|
|
trans->tra_flags |= TRA_read_committed;
|
|
|
|
|
|
|
|
if (MATCH(KW_NO)) {
|
|
|
|
if (MATCH(KW_VERSION))
|
|
|
|
return TRUE;
|
|
|
|
else if (MATCH(KW_WAIT)) {
|
|
|
|
trans->tra_flags |= TRA_nw;
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
SYNTAX_ERROR("WAIT or VERSION");
|
|
|
|
}
|
|
|
|
|
|
|
|
if (MATCH(KW_VERSION))
|
|
|
|
trans->tra_flags |= TRA_rec_version;
|
|
|
|
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
else if (MATCH(KW_SNAPSHOT)) {
|
|
|
|
if (MATCH(KW_TABLE)) {
|
|
|
|
trans->tra_flags |= TRA_con;
|
|
|
|
|
|
|
|
MATCH(KW_STABILITY);
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//____________________________________________________________
|
|
|
|
//
|
|
|
|
// Parse the USING clause for a dynamic SQL statement.
|
|
|
|
//
|
|
|
|
|
|
|
|
static BOOLEAN par_using( DYN statement)
|
|
|
|
{
|
|
|
|
|
|
|
|
if (!MATCH(KW_USING))
|
|
|
|
return FALSE;
|
|
|
|
|
|
|
|
MATCH(KW_SQL); /* keyword "SQL" is optional for backward compatibility */
|
|
|
|
|
|
|
|
if (MATCH(KW_DESCRIPTOR))
|
|
|
|
statement->dyn_sqlda = PAR_native_value(FALSE, FALSE);
|
|
|
|
else
|
|
|
|
statement->dyn_using =
|
2002-11-11 20:19:43 +01:00
|
|
|
(GPRE_NOD) SQE_list(reinterpret_cast < pfn_SQE_list_cb >
|
2001-05-23 15:26:42 +02:00
|
|
|
(SQE_variable), 0, FALSE);
|
|
|
|
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//____________________________________________________________
|
|
|
|
//
|
|
|
|
// Figure out the correct dtypes
|
|
|
|
//
|
|
|
|
|
|
|
|
static USHORT resolve_dtypes( KWWORDS typ, BOOLEAN sql_date)
|
|
|
|
{
|
|
|
|
TEXT err_mesg[128];
|
|
|
|
|
|
|
|
switch (typ) {
|
|
|
|
case KW_DATE:
|
|
|
|
if ((sw_ods_version < 10) || (sw_server_version < 6))
|
|
|
|
switch (sw_sql_dialect) {
|
|
|
|
case 1:
|
|
|
|
return dtype_timestamp;
|
|
|
|
case 2:
|
|
|
|
sprintf(err_mesg,
|
|
|
|
"Encountered column type DATE which is ambiguous in dialect %d\n",
|
|
|
|
sw_sql_dialect);
|
|
|
|
PAR_error(err_mesg);
|
|
|
|
return dtype_null; /* TMN: FIX FIX */
|
|
|
|
/* return; */
|
|
|
|
default:
|
|
|
|
sprintf(err_mesg,
|
|
|
|
"Encountered column type DATE which is not supported in ods version %d\n",
|
|
|
|
sw_ods_version);
|
|
|
|
PAR_error(err_mesg);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
/** column definition SQL DATE is unambiguous in any dialect **/
|
|
|
|
if (sql_date)
|
|
|
|
return dtype_sql_date;
|
|
|
|
switch (sw_sql_dialect) {
|
|
|
|
case 1:
|
|
|
|
return dtype_timestamp;
|
|
|
|
case 2:
|
|
|
|
sprintf(err_mesg,
|
|
|
|
"Encountered column type DATE which is ambiguous in dialect %d\n",
|
|
|
|
sw_sql_dialect);
|
|
|
|
PAR_error(err_mesg);
|
|
|
|
return dtype_null; /* TMN: FIX FIX */
|
|
|
|
/* return; */
|
|
|
|
default:
|
|
|
|
return dtype_sql_date;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case KW_TIME:
|
|
|
|
if ((sw_ods_version < 10) || (sw_server_version < 6)) {
|
|
|
|
sprintf(err_mesg,
|
|
|
|
"Encountered column type TIME which is not supported by pre 6.0 Servers\n");
|
|
|
|
PAR_error(err_mesg);
|
|
|
|
return dtype_null; /* TMN: FIX FIX */
|
|
|
|
/* return; */
|
|
|
|
}
|
|
|
|
else
|
|
|
|
return dtype_sql_time;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case KW_TIMESTAMP:
|
|
|
|
return dtype_timestamp;
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
sprintf(err_mesg, "resolve_dtypes(): Unknown dtype %d\n", typ);
|
|
|
|
PAR_error(err_mesg);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
//
|
|
|
|
// TMN: FIX FIX Added "return dtype_null;" to silence compiler, but
|
|
|
|
// this is really a logic error we have to fix.
|
|
|
|
//
|
|
|
|
return dtype_null;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//____________________________________________________________
|
|
|
|
//
|
|
|
|
// Parse the tail of a CREATE DATABASE statement.
|
|
|
|
//
|
|
|
|
|
|
|
|
static BOOLEAN tail_database( enum act_t action_type, DBB database)
|
|
|
|
{
|
|
|
|
FIL file;
|
|
|
|
TEXT *string;
|
|
|
|
BOOLEAN extend_dpb, logdefined;
|
|
|
|
|
|
|
|
// parse options for the database parameter block
|
|
|
|
|
|
|
|
logdefined = FALSE;
|
|
|
|
extend_dpb = FALSE;
|
|
|
|
database->dbb_grp_cmt_wait = -1;
|
|
|
|
|
|
|
|
while (TRUE) {
|
|
|
|
if (MATCH(KW_LENGTH)) {
|
|
|
|
database->dbb_length = EXP_ULONG_ordinal(TRUE);
|
|
|
|
MATCH(KW_PAGES);
|
|
|
|
}
|
|
|
|
else if (MATCH(KW_USER)) {
|
|
|
|
if (QUOTED(token.tok_type)) {
|
|
|
|
database->dbb_c_user = string =
|
|
|
|
(TEXT *) ALLOC(token.tok_length + 1);
|
|
|
|
COPY(token.tok_string, token.tok_length, string);
|
|
|
|
|
|
|
|
string[token.tok_length - 2] = '\0';
|
|
|
|
ADVANCE_TOKEN;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
/* Some non-C languages (such as Fortran) have problems
|
|
|
|
* with extending the static DPB to add the runtime options
|
|
|
|
* needed for a runtime user name or password.
|
|
|
|
* 11 April 1995
|
|
|
|
*/
|
|
|
|
if (sw_language == lang_c) {
|
|
|
|
if (!MATCH(KW_COLON))
|
|
|
|
SYNTAX_ERROR("<colon> or <quoted string>");
|
|
|
|
/** Should I bother about dialects here ?? **/
|
|
|
|
if (QUOTED(token.tok_type))
|
|
|
|
SYNTAX_ERROR("<host variable>");
|
|
|
|
database->dbb_r_user = PAR_native_value(FALSE, FALSE);
|
|
|
|
extend_dpb = TRUE;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
SYNTAX_ERROR("<quoted string>");
|
|
|
|
|
|
|
|
}
|
|
|
|
else if (MATCH(KW_PASSWORD)) {
|
|
|
|
if (QUOTED(token.tok_type)) {
|
|
|
|
database->dbb_c_password = string =
|
|
|
|
(TEXT *) ALLOC(token.tok_length + 1);
|
|
|
|
COPY(token.tok_string, token.tok_length, string);
|
|
|
|
string[token.tok_length] = '\0';
|
|
|
|
ADVANCE_TOKEN;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
/* Some non-C languages (such as Fortran) have problems
|
|
|
|
* with extending the static DPB to add the runtime options
|
|
|
|
* needed for a runtime user name or password.
|
|
|
|
* 11 April 1995
|
|
|
|
*/
|
|
|
|
if (sw_language == lang_c) {
|
|
|
|
if (!MATCH(KW_COLON))
|
|
|
|
SYNTAX_ERROR("<colon> or <quoted string>");
|
|
|
|
if (QUOTED(token.tok_type))
|
|
|
|
SYNTAX_ERROR("<host variable>");
|
|
|
|
database->dbb_r_password = PAR_native_value(FALSE, FALSE);
|
|
|
|
extend_dpb = TRUE;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
SYNTAX_ERROR("<quoted string>");
|
|
|
|
}
|
|
|
|
else if (MATCH(KW_DEFAULT)) {
|
|
|
|
if (!MATCH(KW_CHAR) || !MATCH(KW_SET))
|
|
|
|
SYNTAX_ERROR("CHARACTER SET");
|
|
|
|
database->dbb_def_charset = PAR_native_value(FALSE, FALSE);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (action_type == ACT_drop_database) {
|
|
|
|
if (MATCH(KW_CASCADE))
|
|
|
|
database->dbb_flags |= DBB_cascade;
|
|
|
|
/* TMN: ERROR ERROR we cant return _nothing* from a function returning */
|
|
|
|
/* a BOOLEAN. I changed this to FALSE to falg an error, but we have to */
|
|
|
|
/* look into this. */
|
|
|
|
return FALSE;
|
|
|
|
/* return; */
|
|
|
|
}
|
|
|
|
|
|
|
|
// parse add/drop items
|
|
|
|
|
|
|
|
while (TRUE) {
|
|
|
|
MATCH(KW_ADD);
|
|
|
|
if (MATCH(KW_DROP)) {
|
|
|
|
if (MATCH(KW_LOG_FILE))
|
|
|
|
database->dbb_flags |= DBB_drop_log;
|
|
|
|
else if (MATCH(KW_CASCADE))
|
|
|
|
database->dbb_flags |= DBB_cascade;
|
|
|
|
#ifdef FLINT_CACHE
|
|
|
|
else if (MATCH(KW_CACHE))
|
|
|
|
database->dbb_flags |= DBB_drop_cache;
|
|
|
|
else
|
|
|
|
PAR_error("only log files or shared cache can be dropped"); /* msg 121 only SECURITY_CLASS, DESCRIPTION and CACHE can be dropped */
|
|
|
|
#else
|
|
|
|
else
|
|
|
|
PAR_error("only log files can be dropped"); /* msg 121 only SECURITY_CLASS, DESCRIPTION and CACHE can be dropped */
|
|
|
|
#endif /* FLINT_CACHE */
|
|
|
|
|
|
|
|
// ****
|
|
|
|
// else if (MATCH (KW_DESCRIP))
|
|
|
|
// database->dbb_flags |= DBB_null_description;
|
|
|
|
// else if (MATCH (KW_SECURITY_CLASS))
|
|
|
|
// database->dbb_flags |= DBB_null_security_class;
|
|
|
|
//***
|
|
|
|
}
|
|
|
|
// ****
|
|
|
|
// else if (KEYWORD (KW_DESCRIPTION))
|
|
|
|
// database->dbb_description = parse_description();
|
|
|
|
// else if (MATCH (KW_SECURITY_CLASS))
|
|
|
|
// database->dbb_security_class = PARSE_symbol (tok_ident);
|
|
|
|
//***
|
|
|
|
else if (MATCH(KW_FILE)) {
|
|
|
|
file = define_file();
|
|
|
|
file->fil_next = database->dbb_files;
|
|
|
|
database->dbb_files = file;
|
|
|
|
}
|
|
|
|
else if (MATCH(KW_PAGE_SIZE) || MATCH(KW_PAGESIZE)) {
|
|
|
|
if (action_type == ACT_alter_database)
|
|
|
|
PAR_error("PAGE SIZE can not be modified");
|
|
|
|
database->dbb_pagesize = par_page_size();
|
|
|
|
}
|
|
|
|
else if (MATCH(KW_CHECK_POINT_LEN)) {
|
|
|
|
MATCH(KW_EQUALS);
|
|
|
|
database->dbb_chkptlen = EXP_ULONG_ordinal(TRUE);
|
|
|
|
}
|
|
|
|
else if (MATCH(KW_NUM_LOG_BUFS)) {
|
|
|
|
MATCH(KW_EQUALS);
|
|
|
|
database->dbb_numbufs = EXP_USHORT_ordinal(TRUE);
|
|
|
|
}
|
|
|
|
else if (MATCH(KW_LOG_BUF_SIZE)) {
|
|
|
|
MATCH(KW_EQUALS);
|
|
|
|
database->dbb_bufsize = EXP_USHORT_ordinal(TRUE);
|
|
|
|
}
|
|
|
|
else if (MATCH(KW_GROUP_COMMIT_WAIT)) {
|
|
|
|
MATCH(KW_EQUALS);
|
|
|
|
database->dbb_grp_cmt_wait = EXP_ULONG_ordinal(TRUE);
|
|
|
|
}
|
|
|
|
else if (MATCH(KW_LOG_FILE)) {
|
|
|
|
if (logdefined)
|
|
|
|
PAR_error("log redefinition");
|
|
|
|
logdefined = TRUE;
|
|
|
|
if (MATCH(KW_LEFT_PAREN)) {
|
|
|
|
while (TRUE) {
|
|
|
|
file = define_log_file(FALSE);
|
|
|
|
file->fil_next = database->dbb_logfiles;
|
|
|
|
database->dbb_logfiles = file;
|
|
|
|
if (!MATCH(KW_COMMA)) {
|
|
|
|
EXP_match_paren();
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (MATCH(KW_OVERFLOW))
|
|
|
|
database->dbb_overflow = define_log_file(TRUE);
|
|
|
|
else
|
|
|
|
PAR_error
|
|
|
|
("Overflow log specification required for this configuration");
|
|
|
|
}
|
|
|
|
else if (MATCH(KW_BASE_NAME)) {
|
|
|
|
database->dbb_flags |= DBB_log_serial;
|
|
|
|
database->dbb_logfiles = file = define_log_file(TRUE);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
database->dbb_flags |= DBB_log_default;
|
|
|
|
}
|
|
|
|
#ifdef FLINT_CACHE
|
|
|
|
else if (MATCH(KW_CACHE))
|
|
|
|
database->dbb_cache_file = define_cache();
|
|
|
|
#endif /* FLINT_CACHE */
|
|
|
|
else
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
return extend_dpb;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//____________________________________________________________
|
|
|
|
//
|
|
|
|
// Upcase a string into another string.
|
|
|
|
//
|
|
|
|
|
|
|
|
static void to_upcase( TEXT * p, TEXT * q)
|
|
|
|
{
|
|
|
|
UCHAR c;
|
|
|
|
USHORT l;
|
|
|
|
|
|
|
|
l = 0;
|
|
|
|
while ((c = *p++) && (++l <= NAME_SIZE)) {
|
|
|
|
*q++ = UPPER(c);
|
|
|
|
}
|
|
|
|
*q = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//____________________________________________________________
|
|
|
|
//
|
|
|
|
//
|
|
|
|
|
|
|
|
void SQL_resolve_identifier( TEXT * err_mesg, TEXT * str)
|
|
|
|
{
|
|
|
|
|
|
|
|
switch (sw_sql_dialect) {
|
|
|
|
case 2:
|
|
|
|
if (DOUBLE_QUOTED(token.tok_type))
|
|
|
|
PAR_error("Ambiguous use of double quotes in dialect 2");
|
|
|
|
case 1:
|
|
|
|
if (token.tok_type != tok_ident)
|
|
|
|
SYNTAX_ERROR(err_mesg);
|
|
|
|
else
|
|
|
|
to_upcase(token.tok_string, str);
|
|
|
|
break;
|
|
|
|
case 3:
|
|
|
|
if (DOUBLE_QUOTED(token.tok_type)) {
|
|
|
|
if (DOUBLE_QUOTES_ON(token.tok_string))
|
|
|
|
STRIP_QUOTES(token)
|
|
|
|
strcpy(str, token.tok_string);
|
|
|
|
}
|
|
|
|
else if (token.tok_type == tok_ident)
|
|
|
|
to_upcase(token.tok_string, str);
|
|
|
|
else
|
|
|
|
SYNTAX_ERROR(err_mesg);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
strcpy(token.tok_string, str);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void dialect1_bad_type(USHORT field_dtype)
|
|
|
|
{
|
|
|
|
char buffer[200];
|
|
|
|
char *s;
|
|
|
|
|
|
|
|
switch (field_dtype) {
|
|
|
|
case dtype_sql_date:
|
|
|
|
s = "SQL DATE";
|
|
|
|
break;
|
|
|
|
case dtype_sql_time:
|
|
|
|
s = "SQL TIME";
|
|
|
|
break;
|
|
|
|
case dtype_int64:
|
|
|
|
s = "64-bit numeric";
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
sprintf(buffer,
|
|
|
|
"Client SQL dialect 1 does not support reference to the %s datatype",
|
|
|
|
s);
|
|
|
|
PAR_error(buffer);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
} // extern "C"
|