diff --git a/generated/alice/makefile b/generated/alice/makefile new file mode 100644 index 0000000000..f03dc74db6 --- /dev/null +++ b/generated/alice/makefile @@ -0,0 +1,27 @@ +!IF "$(OS)" == "Windows_NT" + +# NOTE - If you enter an absolute path to gpre.exe, it MUST use +# MS-DOS style backslash! +GPRE_PGM=..\..\bin\gpre + +DELETE_COMMAND=del /q + +!ELSE + +!ERROR Running on unknown operating system. + +!ENDIF + +REL_SRC_DIR=../../src/alice + + +C_FILES=alice_meta.c + +all: $(C_FILES) + +clean : + -$(DELETE_COMMAND) *.c + +alice_meta.c : $(REL_SRC_DIR)/alice_meta.e + $(GPRE_PGM) -r -m -n $? -o >$@ + diff --git a/generated/alice/placeholder.txt b/generated/alice/placeholder.txt new file mode 100644 index 0000000000..e69de29bb2 diff --git a/generated/burp/makefile b/generated/burp/makefile new file mode 100644 index 0000000000..bfe1eba076 --- /dev/null +++ b/generated/burp/makefile @@ -0,0 +1,44 @@ +!IF "$(OS)" == "Windows_NT" + +# NOTE - If you enter an absolute path to gpre.exe, it MUST use +# MS-DOS style backslash! +GPRE_PGM=..\..\bin\gpre +SED_PGM=sed + +COMPILER_CFLAGS=-c -DNOMSG -DWIN32_LEAN_AND_MEAN -nologo +OBJEXT=obj +DELETE_COMMAND=del /q + +!ELSE + +!ERROR Running on unknown operating system. + +!ENDIF + +REL_SRC_DIR=../../src/burp +COMMON_CFLAGS=-I../../src -I../../src/include +CFLAGS=$(COMMON_CFLAGS) $(COMPILER_CFLAGS) + +# +# C_FILES could be useful as build target in case we are only +# generate the C files into this dir +# + +C_FILES=backup.c restore.c + +OBJ_FILES=backup.$(OBJEXT) restore.$(OBJEXT) + +all: $(OBJ_FILES) + +c_files : $(C_FILES) + +clean : + -$(DELETE_COMMAND) *.$(OBJEXT) + -$(DELETE_COMMAND) *.c + +backup.c : $(REL_SRC_DIR)/backup.e + $(GPRE_PGM) -r -m -n $? -o >$@ + +restore.c : $(REL_SRC_DIR)/restore.e + $(GPRE_PGM) -r -m -n $? -o >$@ + diff --git a/generated/burp/placeholder.txt b/generated/burp/placeholder.txt new file mode 100644 index 0000000000..e69de29bb2 diff --git a/generated/dsql/makefile b/generated/dsql/makefile new file mode 100644 index 0000000000..abff349498 --- /dev/null +++ b/generated/dsql/makefile @@ -0,0 +1,33 @@ +!IF "$(OS)" == "Windows_NT" + +# NOTE - If you enter an absolute path to gpre.exe, it MUST use +# MS-DOS style backslash! +GPRE_PGM=..\..\bin\gpre + +DELETE_COMMAND=del /q + +!ELSE + +!ERROR Running on unknown operating system. + +!ENDIF + +REL_SRC_DIR=../../src/dsql + + +C_FILES=array.c blob.c metd.c + +all: $(C_FILES) + +clean : + -$(DELETE_COMMAND) *.c + +array.c : $(REL_SRC_DIR)/array.e + $(GPRE_PGM) -r -m -n $? -o >$@ + +blob.c : $(REL_SRC_DIR)/blob.e + $(GPRE_PGM) -r -m -n $? -o >$@ + +metd.c : $(REL_SRC_DIR)/metd.e + $(GPRE_PGM) -r -m -n $? -o >$@ + diff --git a/generated/dsql/placeholder.txt b/generated/dsql/placeholder.txt new file mode 100644 index 0000000000..e69de29bb2 diff --git a/generated/dudley/makefile b/generated/dudley/makefile new file mode 100644 index 0000000000..aeaefb64f9 --- /dev/null +++ b/generated/dudley/makefile @@ -0,0 +1,30 @@ +!IF "$(OS)" == "Windows_NT" + +# NOTE - If you enter an absolute path to gpre.exe, it MUST use +# MS-DOS style backslash! +GPRE_PGM=..\..\bin\gpre + +DELETE_COMMAND=del /q + +!ELSE + +!ERROR Running on unknown operating system. + +!ENDIF + +REL_SRC_DIR=../../src/dudley + + +C_FILES=exe.c extract.c + +all: $(C_FILES) + +clean : + -$(DELETE_COMMAND) *.c + +exe.c : $(REL_SRC_DIR)/exe.e + $(GPRE_PGM) -r -m -n $? -o >$@ + +extract.c : $(REL_SRC_DIR)/extract.e + $(GPRE_PGM) -r -m -n $? -o >$@ + diff --git a/generated/dudley/placeholder.txt b/generated/dudley/placeholder.txt new file mode 100644 index 0000000000..e69de29bb2 diff --git a/generated/gpre/makefile b/generated/gpre/makefile new file mode 100644 index 0000000000..e2c4cf3dba --- /dev/null +++ b/generated/gpre/makefile @@ -0,0 +1,27 @@ +!IF "$(OS)" == "Windows_NT" + +# NOTE - If you enter an absolute path to gpre.exe, it MUST use +# MS-DOS style backslash! +GPRE_PGM=..\..\bin\gpre + +DELETE_COMMAND=del /q + +!ELSE + +!ERROR Running on unknown operating system. + +!ENDIF + +REL_SRC_DIR=../../src/gpre + + +C_FILES=gpre_meta.c + +all: $(C_FILES) + +clean : + -$(DELETE_COMMAND) *.c + +gpre_meta.c : $(REL_SRC_DIR)/gpre_meta.e + $(GPRE_PGM) -r -m -n $? -o >$@ + diff --git a/generated/gpre/placeholder.txt b/generated/gpre/placeholder.txt new file mode 100644 index 0000000000..e69de29bb2 diff --git a/generated/isql/makefile b/generated/isql/makefile new file mode 100644 index 0000000000..81250f596b --- /dev/null +++ b/generated/isql/makefile @@ -0,0 +1,33 @@ +!IF "$(OS)" == "Windows_NT" + +# NOTE - If you enter an absolute path to gpre.exe, it MUST use +# MS-DOS style backslash! +GPRE_PGM=..\..\bin\gpre + +DELETE_COMMAND=del /q + +!ELSE + +!ERROR Running on unknown operating system. + +!ENDIF + +REL_SRC_DIR=../../src/isql + + +C_FILES=extract.c isql.c show.c + +all: $(C_FILES) + +clean : + -$(DELETE_COMMAND) *.c + +extract.c : $(REL_SRC_DIR)/extract.e + $(GPRE_PGM) -r -m -n $? -o >$@ + +isql.c : $(REL_SRC_DIR)/isql.e + $(GPRE_PGM) -r -m -n $? -o >$@ + +show.c : $(REL_SRC_DIR)/show.e + $(GPRE_PGM) -r -m -n $? -o >$@ + diff --git a/generated/isql/placeholder.txt b/generated/isql/placeholder.txt new file mode 100644 index 0000000000..e69de29bb2 diff --git a/generated/journal/placeholder.txt b/generated/journal/placeholder.txt new file mode 100644 index 0000000000..e69de29bb2 diff --git a/generated/jrd/makefile b/generated/jrd/makefile new file mode 100644 index 0000000000..a1ffaebcf6 --- /dev/null +++ b/generated/jrd/makefile @@ -0,0 +1,48 @@ +REL_SRC_DIR=../../src/jrd +.SUFFIXES : .e + +!IF "$(OS)" == "Windows_NT" + +# NOTE - This should probably also be the path taken for other +# Win32 operating systems, like Win9x/Me. + +# NOTE - If you enter an absolute path to gpre.exe, it MUST use +# MS-DOS style backslash! +GPRE_PGM=..\..\bin\gpre +SED_PGM=sed + +DELETE_COMMAND=del /q + +# +# MS NMAKE specific directory-relative inference rule +# +{$(REL_SRC_DIR)}.e{}.c: + $(GPRE_PGM) -n -gds -raw -ids $? -o >$@ + +!ELSE + +!ERROR Running on unknown operating system. + +!ENDIF + +# +# NOTE: "stats" is temporarily removed since it isn't even used. +# +# stats.c +# + +C_FILES=blob_filter.c dfw.c dpm.c dyn.c dyn_def.c dyn_del.c dyn_mod.c \ + dyn_util.c fun.c grant.c ini.c met.c pcmet.c scl.c stats.c + +all: $(C_FILES) + +clean : + -$(DELETE_COMMAND) *.c + +blob_filter.c : + $(GPRE_PGM) -n -manual -raw $? -o >$@ + +dyn_def.c : + $(GPRE_PGM) -n -gds -raw -ids $? -o > dyn_def.c.tmp + $(SED_PGM) -f $(REL_SRC_DIR)/dyn_def.sed dyn_def.c.tmp > dyn_def.c + diff --git a/generated/jrd/placeholder.txt b/generated/jrd/placeholder.txt new file mode 100644 index 0000000000..e69de29bb2 diff --git a/generated/msgs/makefile b/generated/msgs/makefile new file mode 100644 index 0000000000..73cebb7bf5 --- /dev/null +++ b/generated/msgs/makefile @@ -0,0 +1,39 @@ +REL_SRC_DIR=../../src/msgs +.SUFFIXES : .e + +!IF "$(OS)" == "Windows_NT" + +# NOTE - This should probably also be the path taken for other +# Win32 operating systems, like Win9x/Me. + +# NOTE - If you enter an absolute path to gpre.exe, it MUST use +# MS-DOS style backslash! +GPRE_PGM=..\..\bin\gpre +SED_PGM=sed + +DELETE_COMMAND=del /q + +# +# MS NMAKE specific directory-relative inference rule +# +{$(REL_SRC_DIR)}.e{}.c: + $(GPRE_PGM) -r -m -n $? -o >$@ + +!ELSE + +!ERROR Running on unknown operating system. + +!ENDIF + +# +# load.c - disabled +# + +C_FILES=build_file.c check_msgs.c enter_msgs.c modify_msgs.c \ + change_msgs.c + +all: $(C_FILES) + +clean : + -$(DELETE_COMMAND) *.c + diff --git a/generated/msgs/placeholder.txt b/generated/msgs/placeholder.txt new file mode 100644 index 0000000000..e69de29bb2 diff --git a/generated/pyxis/placeholder.txt b/generated/pyxis/placeholder.txt new file mode 100644 index 0000000000..e69de29bb2 diff --git a/generated/qli/placeholder.txt b/generated/qli/placeholder.txt new file mode 100644 index 0000000000..e69de29bb2 diff --git a/generated/utilities/makefile b/generated/utilities/makefile new file mode 100644 index 0000000000..7d4b10a391 --- /dev/null +++ b/generated/utilities/makefile @@ -0,0 +1,39 @@ +!IF "$(OS)" == "Windows_NT" + +# NOTE - If you enter an absolute path to gpre.exe, it MUST use +# MS-DOS style backslash! +GPRE_PGM=..\..\bin\gpre + +DELETE_COMMAND=del /q + +!ELSE + +!ERROR Running on unknown operating system. + +!ENDIF + +REL_SRC_DIR=../../src/utilities + +# +# rstore.c - disabled +# +C_FILES=dba.c security.c rmet.c + +all: $(C_FILES) + +clean : + -$(DELETE_COMMAND) *.c + +dba.c : $(REL_SRC_DIR)/dba.e + $(GPRE_PGM) -r -m -n $? -o >$@ + +security.c : $(REL_SRC_DIR)/security.e + $(GPRE_PGM) -r -m -n $? -o >$@ + +rmet.c : $(REL_SRC_DIR)/rmet.e + $(GPRE_PGM) -r -m -n $? -o >$@ + +# Disabled - unused +#rstore.c : $(REL_SRC_DIR)/rstore.e +# $(GPRE_PGM) -r -m -n $? -o >$@ + diff --git a/generated/utilities/placeholder.txt b/generated/utilities/placeholder.txt new file mode 100644 index 0000000000..e69de29bb2 diff --git a/src/alice/alice.cpp b/src/alice/alice.cpp new file mode 100644 index 0000000000..9e66149eeb --- /dev/null +++ b/src/alice/alice.cpp @@ -0,0 +1,743 @@ +//____________________________________________________________ +// +// PROGRAM: Alice (All Else) Utility +// MODULE: alice.cpp +// DESCRIPTION: Neo-Debe (does everything but eat) +// +// 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): ______________________________________. +// +// +//____________________________________________________________ +// +// $Id: alice.cpp,v 1.1.1.1 2001-05-23 13:25:33 tamlin Exp $ +// + +#include +#include +#include + +#define FB_FROM_ALICE_CPP + +#include "../jrd/ib_stdio.h" +#include "../include/jrd/gds.h" +#include "../jrd/common.h" +#include "../jrd/license.h" +#include "../jrd/ibsetjmp.h" +#include "../jrd/msg_encode.h" +#include "../alice/alice.h" +#include "../alice/aliceswi.h" +#include "../alice/all.h" +#include "../alice/alice_proto.h" +#include "../alice/all_proto.h" +#include "../alice/exe_proto.h" +#include "../jrd/gds_proto.h" +#include "../jrd/svc.h" +#include "../jrd/svc_proto.h" +#include "../jrd/thd_proto.h" + +#ifdef SUPERSERVER +#include "../utilities/cmd_util_proto.h" +#endif + +#ifdef WIN_NT +#include +#endif + +#if (defined WIN_NT || defined PC_PLATFORM) +#include +#endif + + +extern "C" { + + +#ifndef MAXPATHLEN +#define MAXPATHLEN 1024 +#endif + +static USHORT val_err_table[] = { + 0, + 55, /* msg 55: \n\tNumber of record level errors\t: %ld */ + 56, /* msg 56: \tNumber of Blob page errors\t: %ld */ + 57, /* msg 57: \tNumber of data page errors\t: %ld */ + 58, /* msg 58: \tNumber of index page errors\t: %ld */ + 59, /* msg 59: \tNumber of pointer page errors\t: %ld */ + 60, /* msg 60: \tNumber of transaction page errors\t: %ld */ + 61 /* msg 61: \tNumber of database page errors\t: %ld */ +}; + +#ifndef NETWARE_386 +struct tgbl *gdgbl; +#endif + +#define ALICE_MSG_FAC 3 + +#if defined (WIN95) && !defined (GUI_TOOLS) +static BOOL fAnsiCP = FALSE; +#define TRANSLATE_CP(a) if (!fAnsiCP) AnsiToOem(a, a) +#else +#define TRANSLATE_CP(a) +#endif + +static void expand_filename(TEXT *, TEXT *); +static int output_thread(SLONG, UCHAR *); +static int output_main(SLONG, UCHAR *); +static int output_svc(SLONG, UCHAR *); +static void alice_output(CONST SCHAR *, ...); + + + +#ifdef SUPERSERVER + +//____________________________________________________________ +// +// Entry point for GFIX in case of service manager. +// + +int main_gfix(SVC service) +{ + int exit_code; + + exit_code = ALICE_gfix(service->svc_argc, service->svc_argv, + output_thread, (SLONG) service); + + service->svc_handle = 0; + if (service->svc_service->in_use != NULL) + *(service->svc_service->in_use) = FALSE; + +// Mark service thread as finished. +// If service is detached, cleanup memory being used by service. + SVC_finish(service, SVC_finished); + + return exit_code; +} + + +//____________________________________________________________ +// +// Routine which is passed to GFIX for calling back when there is output. +// + +static int output_thread(SLONG output_data, UCHAR * output_buf) +{ + SVC_fprintf((SVC) output_data, "%s", output_buf); + + return 0; +} + + +#else +#ifndef GUI_TOOLS + +//____________________________________________________________ +// +// Call the 'real' main. +// + +int CLIB_ROUTINE main(int argc, char *argv[]) +{ + int exit_code; + + exit_code = ALICE_gfix(argc, argv, output_main, (SLONG) NULL); + + return exit_code; +} + + +//____________________________________________________________ +// +// Routine which is passed to GFIX for calling back when there is output. +// + +static int output_main(SLONG output_data, UCHAR * output_buf) +{ + ib_fprintf(ib_stderr, "%s", output_buf); + return 0; +} +#endif +#endif + +//____________________________________________________________ +// +// Routine which is passed to GFIX for calling back when there is output +// if gfix is run as a service +// + +static int output_svc(SLONG output_data, UCHAR * output_buf) +{ + ib_fprintf(ib_stdout, "%s", output_buf); + return 0; +} + + +//____________________________________________________________ +// +// Routine called by command line utility, and server manager +// Parse switches and do work +// + +int DLL_EXPORT ALICE_gfix( + int argc, + char *argv[], + OUTPUTPROC output_proc, SLONG output_data) +{ + USHORT error, i; + IN_SW_TAB table = alice_in_sw_table; + TEXT *database, string[512], *p, *q; + ULONG switches; + SLONG redir_in, redir_out, redir_err; + JMP_BUF env; + USHORT ret; +#if defined (WIN95) && !defined (GUI_TOOLS) + BOOL fAnsiCP fAnsiCP = (GetConsoleCP() == GetACP()); +#endif + + VOLATILE tgbl *tdgbl = (struct tgbl *) gds__alloc(sizeof(*tdgbl)); +// NOMEM: return error, FREE: during function exit in the SETJMP + if (tdgbl == NULL) + return FINI_ERROR; +// TMN +#if 0 + SET_THREAD_DATA; +#else + tdgbl->tgbl_thd_data.thdd_type = THDD_TYPE_TALICE; +#endif + SVC_PUTSPECIFIC_DATA; + memset((void *) tdgbl, 0, sizeof(*tdgbl)); +// TMN: I can't for my life understand why the jmp_buf is defined +// as a UCHAR* in ' struct tgbl', but it forces this cast. + tdgbl->alice_env = (UCHAR * volatile) env; + tdgbl->output_proc = output_proc; + tdgbl->output_data = output_data; + tdgbl->ALICE_permanent_pool = NULL; + tdgbl->ALICE_default_pool = NULL; + tdgbl->pools = NULL; + + if (SETJMP(env)) { + int exit_code; + + /* All calls to EXIT(), normal and error exits, wind up here */ + + SVC_STARTED(tdgbl->service_blk); + tdgbl->alice_env = NULL; + exit_code = tdgbl->exit_code; + + /* Close the status output file */ + if (tdgbl->sw_redirect == TRUE && tdgbl->output_file != NULL) { + ib_fclose(tdgbl->output_file); + tdgbl->output_file = NULL; + } + + /* Free all unfreed memory used by Gfix itself */ + ALLA_fini(); + + RESTORE_THREAD_DATA; + if (tdgbl != NULL) { + gds__free((SLONG *) tdgbl); + } +#if defined(DEBUG_GDS_ALLOC) && !defined(SUPERSERVER) + gds_alloc_report(0, __FILE__, __LINE__); +#endif + + /* All returns occur from this point - even normal returns */ + return exit_code; + } + + +#ifdef VMS + argc = VMS_parse(&argv, argc); +#endif + +// Perform some special handling when run as an Interbase service. The +// first switch can be "-svc" (lower case!) or it can be "-svc_re" followed +// by 3 file descriptors to use in re-directing ib_stdin, ib_stdout, and ib_stderr. + + tdgbl->sw_service = FALSE; + tdgbl->sw_service_thd = FALSE; + tdgbl->service_blk = NULL; + tdgbl->status = + /* TMN: cast away volatile */ + (long *) tdgbl->status_vector; + + if (argc > 1 && !strcmp(argv[1], "-svc")) { + tdgbl->sw_service = TRUE; + argv++; + argc--; + } + else if (argc > 1 && !strcmp(argv[1], "-svc_thd")) { + tdgbl->sw_service = TRUE; + tdgbl->sw_service_thd = TRUE; + tdgbl->service_blk = (SVC) output_data; + tdgbl->status = tdgbl->service_blk->svc_status; + argv++; + argc--; + } +#ifndef NETWARE_386 + else if (argc > 4 && !strcmp(argv[1], "-svc_re")) { + tdgbl->sw_service = TRUE; + tdgbl->output_proc = output_svc; + redir_in = atol(argv[2]); + redir_out = atol(argv[3]); + redir_err = atol(argv[4]); +#ifdef WIN_NT +#if defined (WIN95) && !defined (GUI_TOOLS) + fAnsiCP = TRUE; +#endif + redir_in = _open_osfhandle(redir_in, 0); + redir_out = _open_osfhandle(redir_out, 0); + redir_err = _open_osfhandle(redir_err, 0); +#endif + if (redir_in != 0) + if (dup2((int) redir_in, 0)) + close((int) redir_in); + if (redir_out != 1) + if (dup2((int) redir_out, 1)) + close((int) redir_out); + if (redir_err != 2) + if (dup2((int) redir_err, 2)) + close((int) redir_err); + argv += 4; + argc -= 4; + } +#endif + + tdgbl->ALICE_data.ua_user = NULL; + tdgbl->ALICE_data.ua_password = NULL; + +// Start by parsing switches + + error = 0; + switches = 0; + tdgbl->ALICE_data.ua_shutdown_delay = 0; + database = NULL; + argv++; + + while (--argc > 0) { + if ((*argv)[0] != '-') { + if (database) + ALICE_error(1, database, 0, 0, 0, 0); /* msg 1: "data base file name (%s) already given", */ + database = *argv++; + +#if defined (WIN95) && !defined (GUI_TOOLS) +// There is a small problem with SuperServer on NT, since it is a +// Windows app, it uses the ANSI character set. All the console +// apps use the OEN character set. We need to pass the database +// name in the correct character set. +// if (GetConsoleCP != GetACP()) +// OemToAnsi(database, database); +// +#else +#endif + continue; + } + ALICE_down_case(*argv++, string); + if (!string[1]) + continue; + for (table = alice_in_sw_table; TRUE; table++) { + if (!(p = (TEXT *) table->in_sw_name)) { + ALICE_print(2, *--argv, 0, 0, 0, 0); /* msg 2: invalid switch %s */ + error = TRUE; + break; + } + q = &string[1]; + while (*q && *p++ == *q) + q++; + if (!*q) + break; + } + if (error) + break; + if (*table->in_sw_name == 'x') + tdgbl->ALICE_data.ua_debug++; + if (table->in_sw_value == sw_z) + ALICE_print(3, GDS_VERSION, 0, 0, 0, 0); /* msg 3: gfix version %s */ + if ((table->in_sw_incompatibilities & switches) || + (table->in_sw_requires && !(table->in_sw_requires & switches))) { + ALICE_print(4, 0, 0, 0, 0, 0); /* msg 4: incompatible switch combination */ + error = TRUE; + break; + } + switches |= table->in_sw_value; + + if (table->in_sw_value & sw_begin_log) { + if (--argc <= 0) + ALICE_error(5, 0, 0, 0, 0, 0); /* msg 5: replay log pathname required */ + expand_filename(*argv++, /* TMN: cast away volatile */ + (TEXT *) tdgbl->ALICE_data.ua_log_file); + } + + if (table->in_sw_value & (sw_buffers)) { + if (--argc <= 0) + ALICE_error(6, 0, 0, 0, 0, 0); /* msg 6: number of page buffers for cache required */ + ALICE_down_case(*argv++, string); + if ((!(tdgbl->ALICE_data.ua_page_buffers = atoi(string))) + && (strcmp(string, "0"))) + ALICE_error(7, 0, 0, 0, 0, 0); /* msg 7: numeric value required */ + if (tdgbl->ALICE_data.ua_page_buffers < 0) + ALICE_error(8, 0, 0, 0, 0, 0); /* msg 8: positive numeric value required */ + } + + if (table->in_sw_value & (sw_housekeeping)) { + if (--argc <= 0) + ALICE_error(9, 0, 0, 0, 0, 0); /* msg 9: number of transactions per sweep required */ + ALICE_down_case(*argv++, string); + if ((!(tdgbl->ALICE_data.ua_sweep_interval = atoi(string))) + && (strcmp(string, "0"))) + ALICE_error(7, 0, 0, 0, 0, 0); /* msg 7: numeric value required */ + if (tdgbl->ALICE_data.ua_sweep_interval < 0) + ALICE_error(8, 0, 0, 0, 0, 0); /* msg 8: positive numeric value required */ + } + + if (table->in_sw_value & (sw_set_db_dialect)) { + if (--argc <= 0) + ALICE_error(9, 0, 0, 0, 0, 0); /* msg 9: dialect info is required XXX */ + + ALICE_down_case(*argv++, string); + + if ((!(tdgbl->ALICE_data.ua_db_SQL_dialect = atoi(string))) && + (strcmp(string, "0"))) + ALICE_error(7, 0, 0, 0, 0, 0); /* msg 7: numeric value required */ + + if (tdgbl->ALICE_data.ua_db_SQL_dialect < 0) + ALICE_error(8, 0, 0, 0, 0, 0); /* msg 8: positive numeric value + required */ + } + + if (table->in_sw_value & (sw_commit | sw_rollback | sw_two_phase)) { + if (--argc <= 0) + ALICE_error(10, 0, 0, 0, 0, 0); /* msg 10: transaction number or "all" required */ + ALICE_down_case(*argv++, string); + if (!(tdgbl->ALICE_data.ua_transaction = atoi(string))) + if (strcmp(string, "all")) + ALICE_error(10, 0, 0, 0, 0, 0); /* msg 10: transaction number or "all" required */ + else + switches |= sw_list; + } + + if (table->in_sw_value & sw_write) { + if (--argc <= 0) + ALICE_error(11, 0, 0, 0, 0, 0); /* msg 11: "sync" or "async" required */ + ALICE_down_case(*argv++, string); + if (!strcmp(string, ALICE_SW_SYNC)) + tdgbl->ALICE_data.ua_force = TRUE; + else if (!strcmp(string, ALICE_SW_ASYNC)) + tdgbl->ALICE_data.ua_force = FALSE; + else + ALICE_error(11, 0, 0, 0, 0, 0); /* msg 11: "sync" or "async" required */ + } + + if (table->in_sw_value & sw_use) { + if (--argc <= 0) + ALICE_error(12, 0, 0, 0, 0, 0); /* msg 12: "full" or "reserve" required */ + ALICE_down_case(*argv++, string); + if (!strcmp(string, "full")) + tdgbl->ALICE_data.ua_use = TRUE; + else if (!strcmp(string, "reserve")) + tdgbl->ALICE_data.ua_use = FALSE; + else + ALICE_error(12, 0, 0, 0, 0, 0); /* msg 12: "full" or "reserve" required */ + } + + if (table->in_sw_value & sw_user) { + if (--argc <= 0) + ALICE_error(13, 0, 0, 0, 0, 0); /* msg 13: user name required */ + tdgbl->ALICE_data.ua_user = + const_cast < UCHAR * volatile >(reinterpret_cast < + UCHAR * >(*argv++)); + } + + if (table->in_sw_value & sw_password) { + if (--argc <= 0) + ALICE_error(14, 0, 0, 0, 0, 0); /* msg 14: password required */ + tdgbl->ALICE_data.ua_password = + const_cast < UCHAR * volatile >(reinterpret_cast < + UCHAR * >(*argv++)); + } + + if (table->in_sw_value & sw_disable) { + if (--argc <= 0) + ALICE_error(15, 0, 0, 0, 0, 0); /* msg 15: subsystem name */ + ALICE_down_case(*argv++, string); + if (strcmp(string, "wal")) + ALICE_error(16, 0, 0, 0, 0, 0); /* msg 16: "wal" required */ + } + + if (table->in_sw_value & (sw_attach | sw_force | sw_tran | sw_cache)) { + if (--argc <= 0) + ALICE_error(17, 0, 0, 0, 0, 0); /* msg 17: number of seconds required */ + ALICE_down_case(*argv++, string); + if ((!(tdgbl->ALICE_data.ua_shutdown_delay = atoi(string))) + && (strcmp(string, "0"))) + ALICE_error(7, 0, 0, 0, 0, 0); /* msg 7: numeric value required */ + if (tdgbl->ALICE_data.ua_shutdown_delay < 0 + || tdgbl->ALICE_data.ua_shutdown_delay > 32767) + ALICE_error(18, 0, 0, 0, 0, 0); /* msg 18: numeric value between 0 and 32767 inclusive required */ + } + +#ifdef READONLY_DATABASE + if (table->in_sw_value & sw_mode) { + if (--argc <= 0) + ALICE_error(110, 0, 0, 0, 0, 0); /* msg 110: "read_only" or "read_write" required */ + ALICE_down_case(*argv++, string); + if (!strcmp(string, ALICE_SW_MODE_RO)) + tdgbl->ALICE_data.ua_read_only = TRUE; + else if (!strcmp(string, ALICE_SW_MODE_RW)) + tdgbl->ALICE_data.ua_read_only = FALSE; + else + ALICE_error(110, 0, 0, 0, 0, 0); /* msg 110: "read_only" or "read_write" required */ + } +#endif + + } + +// put this here since to put it above overly complicates the parsing +// can't use tbl_requires since it only looks backwards on command line + if ((switches & sw_shut) + && !(switches & ((sw_attach | sw_force | sw_tran | sw_cache)))) + ALICE_error(19, 0, 0, 0, 0, 0); /* msg 19: must specify type of shutdown */ + +// catch the case where -z is only command line option +// switches is unset since sw_z == 0 + if (!switches && !error && table->in_sw_value == sw_z) + EXIT(FINI_OK); + + if (!switches || !(switches & ~(sw_user | sw_password))) { +#ifndef SUPERSERVER + ALICE_print(20, 0, 0, 0, 0, 0); /* msg 20: please retry, specifying an option */ +#endif + error = TRUE; + } + + if (error) { +#ifdef SUPERSERVER + CMD_UTIL_put_svc_status(tdgbl->service_blk->svc_status, ALICE_MSG_FAC, + 20, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0); + + SVC_STARTED(tdgbl->service_blk); +#else + ALICE_print(21, 0, 0, 0, 0, 0); /* msg 21: plausible options are:\n */ + for (table = alice_in_sw_table; table->in_sw_msg; table++) + ALICE_print(table->in_sw_msg, 0, 0, 0, 0, 0); + ALICE_print(22, 0, 0, 0, 0, 0); /* msg 22: \n qualifiers show the major option in parenthesis */ +#endif + EXIT(FINI_ERROR); + } + + if (!database) + ALICE_error(23, 0, 0, 0, 0, 0); /* msg 23: please retry, giving a database name */ + +// generate the database parameter block for the attach, +// based on the various switches + + if (switches & (sw_list | sw_commit | sw_rollback | sw_two_phase)) + ret = EXE_two_phase(database, switches); + else { + ret = EXE_action(database, switches); + + USHORT count = 0; + + for (i = 0; i < MAX_VAL_ERRORS; i++) + if (tdgbl->ALICE_data.ua_val_errors[i]) + count++; + + if ((count) + && !(tdgbl->ALICE_data.ua_val_errors[VAL_INVALID_DB_VERSION])) { + ALICE_print(24, 0, 0, 0, 0, 0); /* msg 24: Summary of validation errors\n */ + + for (i = 0; i < MAX_VAL_ERRORS; i++) + if (tdgbl->ALICE_data.ua_val_errors[i]) + ALICE_print(val_err_table[i], + reinterpret_cast < + char *>(tdgbl->ALICE_data.ua_val_errors[i]), + 0, 0, 0, 0); + } + } + + if (ret == FINI_ERROR) + ALICE_print_status(tdgbl->status); + + EXIT(FINI_OK); + + return 0; // compiler silencer +} + + +//____________________________________________________________ +// +// Copy a string, down casing as we go. +// + +void ALICE_down_case(TEXT * in, TEXT * out) +{ + TEXT c; + + while (c = *in++) + *out++ = (c >= 'A' && c <= 'Z') ? c - 'A' + 'a' : c; + + *out = 0; +} + + +//____________________________________________________________ +// +// Display a formatted error message +// + +void ALICE_print(USHORT number, + TEXT * arg1, + TEXT * arg2, TEXT * arg3, TEXT * arg4, TEXT * arg5) +{ + TEXT buffer[256]; + + gds__msg_format(0, ALICE_MSG_FAC, number, sizeof(buffer), buffer, arg1, + arg2, arg3, arg4, arg5); + TRANSLATE_CP(buffer); + alice_output("%s\n", buffer); +} + + +//____________________________________________________________ +// +// Print error message. Use isc_interprete +// to allow redirecting output. +// + +void ALICE_print_status(STATUS * status_vector) +{ +#ifdef SUPERSERVER + TGBL tdgbl; + STATUS *status; + int i = 0, j; +#endif + STATUS *vector; + SCHAR s[1024]; + + if (status_vector) { + vector = status_vector; +#ifdef SUPERSERVER + tdgbl = GET_THREAD_DATA; + status = tdgbl->service_blk->svc_status; + if (status != status_vector) { + while (*status && (++i < ISC_STATUS_LENGTH)) + status++; + for (j = 0; status_vector[j] && (i < ISC_STATUS_LENGTH); j++, i++) + *status++ = status_vector[j]; + } +#endif + isc_interprete(s, &vector); + TRANSLATE_CP(s); + alice_output("%s\n", s); + + /* Continuation of error */ + s[0] = '-'; + while (isc_interprete(s + 1, &vector)) { + TRANSLATE_CP(s); + alice_output("%s\n", s); + } + } +} + + +//____________________________________________________________ +// +// Format and print an error message, then punt. +// + +void ALICE_error(USHORT number, + TEXT * arg1, + TEXT * arg2, TEXT * arg3, TEXT * arg4, TEXT * arg5) +{ + TGBL tdgbl; + TEXT buffer[256]; +#ifdef SUPERSERVER + STATUS *status; +#endif + + tdgbl = GET_THREAD_DATA; + +#ifdef SUPERSERVER + status = tdgbl->service_blk->svc_status; + + CMD_UTIL_put_svc_status(status, ALICE_MSG_FAC, number, + isc_arg_string, arg1, + isc_arg_string, arg2, + isc_arg_string, arg3, + isc_arg_string, arg4, isc_arg_string, arg5); +#endif + + gds__msg_format(0, ALICE_MSG_FAC, number, sizeof(buffer), buffer, arg1, + arg2, arg3, arg4, arg5); + TRANSLATE_CP(buffer); + alice_output("%s\n", buffer); + EXIT(FINI_ERROR); +} + + +//____________________________________________________________ +// +// Platform independent output routine. +// + +static void alice_output(CONST SCHAR * format, ...) +{ + va_list arglist; + UCHAR buf[1000]; + int exit_code; + TGBL tdgbl; + + tdgbl = GET_THREAD_DATA; + + if (tdgbl->sw_redirect == NOOUTPUT || format[0] == '\0') { + exit_code = + tdgbl->output_proc(tdgbl->output_data, + reinterpret_cast < UCHAR * >("")); + } + else if (tdgbl->sw_redirect == TRUE && tdgbl->output_file != NULL) { + VA_START(arglist, format); + ib_vfprintf(tdgbl->output_file, format, arglist); + va_end(arglist); + exit_code = + tdgbl->output_proc(tdgbl->output_data, + reinterpret_cast < UCHAR * >("")); + } + else { + VA_START(arglist, format); + vsprintf((char *) buf, format, arglist); + va_end(arglist); + + exit_code = tdgbl->output_proc(tdgbl->output_data, buf); + } + + if (exit_code != 0) + EXIT(exit_code); +} + + +//____________________________________________________________ +// +// Fully expand a file name. If the file doesn't exist, do something +// intelligent. +// + +static void expand_filename(TEXT * filename, TEXT * expanded_name) +{ + strcpy(expanded_name, filename); +} + + +} // extern "C" diff --git a/src/alice/alice.h b/src/alice/alice.h new file mode 100644 index 0000000000..af0032f513 --- /dev/null +++ b/src/alice/alice.h @@ -0,0 +1,228 @@ +/* + * PROGRAM: Alice + * MODULE: alice.h + * DESCRIPTION: Block definitions + * + * 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): ______________________________________. + */ + +#ifndef _ALICE_ALICE_H_ +#define _ALICE_ALICE_H_ + +#include "../jrd/ib_stdio.h" + +#include "../jrd/ibase.h" +#include "../jrd/ibsetjmp.h" +#include "../jrd/thd.h" +#include "../alice/all.h" +#include "../alice/alice_proto.h" + + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef MAXPATHLEN +#define MAXPATHLEN 1024 +#endif + +#define BLKDEF(type, root, tail) type, +enum blk_t { + type_MIN = 0, +#include "../alice/blk.h" + type_MAX +}; +#undef BLKDEF + +#define VAL_INVALID_DB_VERSION 0 +#define VAL_RECORD_ERRORS 1 +#define VAL_BLOB_PAGE_ERRORS 2 +#define VAL_DATA_PAGE_ERRORS 3 +#define VAL_INDEX_PAGE_ERRORS 4 +#define VAL_POINTER_PAGE_ERRORS 5 +#define VAL_TIP_PAGE_ERRORS 6 +#define VAL_PAGE_ERRORS 7 +#define MAX_VAL_ERRORS 8 + +typedef struct user_action +{ + ULONG ua_switches; + UCHAR *ua_user; + UCHAR *ua_password; + UCHAR ua_use; + UCHAR ua_force; + BOOLEAN ua_read_only; + SLONG ua_shutdown_delay; + SLONG ua_sweep_interval; + SLONG ua_transaction; + SLONG ua_page_buffers; + USHORT ua_debug; + SLONG ua_val_errors[MAX_VAL_ERRORS]; + TEXT ua_log_file[MAXPATHLEN]; + USHORT ua_db_SQL_dialect; +} *USER_ACTION; + + + + +/* String block: used to store a string of constant length. */ + +typedef struct str { + struct blk str_header; + USHORT str_length; + UCHAR str_data[2]; +} *STR; + + +/* Transaction block: used to store info about a multidatabase transaction. */ + +typedef struct tdr +{ + struct blk tdr_header; + struct tdr *tdr_next; /* next subtransaction */ + SLONG tdr_id; /* database-specific transaction id */ + struct str *tdr_fullpath; /* full (possibly) remote pathname */ + TEXT *tdr_filename; /* filename within full pathname */ + struct str *tdr_host_site; /* host for transaction */ + struct str *tdr_remote_site; /* site for remote transaction */ + SLONG *tdr_handle; /* reconnected transaction handle */ + SLONG *tdr_db_handle; /* reattached database handle */ + USHORT tdr_db_caps; /* capabilities of database */ + USHORT tdr_state; /* see flags below */ +} *TDR; + +/* Transaction Description Record */ + +#define TDR_VERSION 1 +#define TDR_HOST_SITE 1 +#define TDR_DATABASE_PATH 2 +#define TDR_TRANSACTION_ID 3 +#define TDR_REMOTE_SITE 4 + +/* flags for tdr_db_caps */ + +#define CAP_none 0 +#define CAP_transactions 1 /* db has a RDB$TRANSACTIONS relation */ + +/* flags for tdr_state */ + +#define TRA_none 0 /* transaction description record is missing */ +#define TRA_limbo 1 /* has been prepared */ +#define TRA_commit 2 /* has committed */ +#define TRA_rollback 3 /* has rolled back */ +#define TRA_unknown 4 /* database couldn't be reattached, state is unknown */ + + + +/* a couple of obscure blocks used only in data allocator routines */ + +typedef struct vec +{ + struct blk vec_header; + ULONG vec_count; + struct blk *vec_object[1]; +} *VEC; + +typedef struct vcl +{ + struct blk vcl_header; + ULONG vcl_count; + SLONG vcl_long[1]; +} *VCL; + +/* Global switches and data */ + +#include "../jrd/svc.h" + +typedef struct tgbl +{ + struct thdd tgbl_thd_data; + struct user_action ALICE_data; + PLB ALICE_permanent_pool; + PLB ALICE_default_pool; + STATUS status_vector[ISC_STATUS_LENGTH]; + VEC pools; + UCHAR* alice_env; + int exit_code; + OUTPUTPROC output_proc; + SLONG output_data; + IB_FILE* output_file; + SVC service_blk; + isc_db_handle db_handle; + isc_tr_handle tr_handle; + STATUS* status; + USHORT sw_redirect; + USHORT sw_service; + USHORT sw_service_thd; + + +} *TGBL; + + +#ifdef GET_THREAD_DATA +#undef GET_THREAD_DATA +#endif + +#ifdef SUPERSERVER +#define GET_THREAD_DATA ((TGBL) THD_get_specific()) +#ifdef __cplusplus +#ifdef FB_FROM_ALICE_CPP +#define SET_THREAD_DATA THD_put_specific ((THDD) tdgbl); \ + tdgbl->tgbl_thd_data.thdd_type = \ + reinterpret_cast(THDD_TYPE_TALICE) +#else /* FB_FROM_ALICE_CPP */ +#define SET_THREAD_DATA THD_put_specific ((THDD) tdgbl); \ + tdgbl->tgbl_thd_data.thdd_type = \ + reinterpret_cast(THDD_TYPE_TALICE) +#endif /* FB_FROM_ALICE_CPP */ +#else +#define SET_THREAD_DATA THD_put_specific ((THDD) tdgbl); \ + tdgbl->tgbl_thd_data.thdd_type=THDD_TYPE_TALICE +#endif +#define RESTORE_THREAD_DATA THD_restore_specific(); +#else +extern struct tgbl *gdgbl; + +#define GET_THREAD_DATA (gdgbl) +#ifdef __cplusplus +#define SET_THREAD_DATA gdgbl = tdgbl; \ + tdgbl->tgbl_thd_data.thdd_type = const_cast(THDD_TYPE_TGBL) +#else +#define SET_THREAD_DATA gdgbl = tdgbl; \ + tdgbl->tgbl_thd_data.thdd_type = THDD_TYPE_TGBL +#endif +#define RESTORE_THREAD_DATA +#endif + +#if defined(__cplusplus) +#define EXIT(code) { tdgbl->exit_code = (code); \ + if (tdgbl->alice_env != NULL) \ + LONGJMP(reinterpret_cast(const_cast(tdgbl->alice_env)), 1); } +#else +#define EXIT(code) { tdgbl->exit_code = (code); \ + if (tdgbl->alice_env != NULL) \ + LONGJMP(tdgbl->alice_env, 1); } +#endif /* __cplusplus */ + +#define NOOUTPUT 2 + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* _ALICE_ALICE_H_ */ diff --git a/src/alice/alice_meta.e b/src/alice/alice_meta.e new file mode 100644 index 0000000000..ec66daee14 --- /dev/null +++ b/src/alice/alice_meta.e @@ -0,0 +1,510 @@ +/* + * tab=4 + * + *____________________________________________________________ + * + * PROGRAM: Alice (All Else) Utility + * MODULE: met.e + * DESCRIPTION: Metadata lookup routines + * + * 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): ______________________________________. + * + * + *____________________________________________________________ + * + * $Id: alice_meta.e,v 1.1.1.1 2001-05-23 13:25:33 tamlin Exp $ + */ + +#include +#include "../jrd/common.h" +#include "../jrd/ibsetjmp.h" +#include "../include/jrd/gds.h" +#include "../jrd/license.h" +#include "../alice/alice.h" +#include "../alice/all.h" +#include "../alice/alloc.h" +#include "../alice/all_proto.h" +#include "../alice/alice_meta.h" +#include "../jrd/gds_proto.h" +#include "../jrd/thd_proto.h" + +/* Transaction Description Record */ + +#define TDR_VERSION 1 +#define TDR_HOST_SITE 1 +#define TDR_DATABASE_PATH 2 +#define TDR_TRANSACTION_ID 3 +#define TDR_REMOTE_SITE 4 +#define TDR_PROTOCOL 5 + +/* For service APIs the follow DB handle is #defined to be a value stored + * in thread data. This is also done for other statics generated by + * GPRE. This is to avoid multiple threading problems with module + * level statics. + */ +DATABASE DB = STATIC FILENAME "yachts.lnk"; + +#define DB tdgbl->db_handle +#define gds__trans tdgbl->tr_handle + +static STR alloc_string(TEXT **); +static USHORT get_capabilities(STATUS *); +static TDR get_description(SLONG[2]); +static void parse_fullpath(TDR); +static USHORT snarf_blob(SLONG[2], USHORT, TEXT *); + + +/* + table used to determine capabilities, checking for specific + fields in system relations +*/ + +typedef struct rfr_tab_t { + TEXT *relation; + TEXT *field; + int bit_mask; +} *RFR_TAB; + +static struct rfr_tab_t rfr_table[] = { + "RDB$TRANSACTIONS", "RDB$TRANSACTION_DESCRIPTION", CAP_transactions, + 0, 0, 0 +}; + +#ifdef GUI_TOOLS +#define RETURN_ERROR(user_status) \ + { memcpy (user_status, gds__status, sizeof (gds__status)); \ + LONGJMP (tdgbl->alice_env, 1); } +#else +#define RETURN_ERROR(user_status) \ + { ALICE_print_status (gds__status); \ + LONGJMP (tdgbl->alice_env, 1); } +#endif + + +/*____________________________________________________________ + * + * Disable WAL for the database forcibly. + * Drop all the entries from RDB$LOG_FILES + */ + +void MET_disable_wal(STATUS * user_status, isc_db_handle handle) +{ + SLONG *request = NULL; + TGBL tdgbl; + + tdgbl = GET_THREAD_DATA; + + if (!(DB = handle)) + return; + + START_TRANSACTION ON_ERROR RETURN_ERROR(user_status) END_ERROR; + FOR(REQUEST_HANDLE request) + X IN RDB$LOG_FILES ERASE X; + END_FOR COMMIT ON_ERROR RETURN_ERROR(user_status) END_ERROR; +} + + +/*____________________________________________________________ + * + * Get the state of a transaction, + * assuming that the database has + * already been attached. + */ + +void MET_get_state(STATUS * user_status, TDR trans) +{ + SLONG *request = NULL; + TGBL tdgbl; + + tdgbl = GET_THREAD_DATA; + + if (!(DB = trans->tdr_db_handle) || + !(trans->tdr_db_caps & CAP_transactions)) { + trans->tdr_state = TRA_unknown; + return; + } + + START_TRANSACTION ON_ERROR RETURN_ERROR(user_status) END_ERROR; + + FOR(REQUEST_HANDLE request) + TRA IN RDB$TRANSACTIONS WITH + TRA.RDB$TRANSACTION_ID = trans->tdr_id + trans->tdr_state = TRA.RDB$TRANSACTION_STATE; + END_FOR ON_ERROR RETURN_ERROR(user_status) END_ERROR; + + gds__release_request(gds__status, GDS_REF(request)); + if (gds__status[1]) { + RETURN_ERROR(user_status) + } + + ROLLBACK ON_ERROR RETURN_ERROR(user_status) END_ERROR; +} + + +/*____________________________________________________________ + * + * Get the description of a transaction in + * limbo, including all associated transactions + * in other databases. + */ + +TDR MET_get_transaction(STATUS * user_status, isc_db_handle handle, SLONG id) +{ + SLONG *request = NULL; + TDR trans = NULL; + USHORT capabilities; + TGBL tdgbl; + + tdgbl = GET_THREAD_DATA; + + if (!(DB = handle)) + return 0; + + START_TRANSACTION ON_ERROR RETURN_ERROR(user_status) END_ERROR; + + capabilities = get_capabilities(user_status); + + if (capabilities & CAP_transactions) { + FOR(REQUEST_HANDLE request) + TRA IN RDB$TRANSACTIONS WITH + TRA.RDB$TRANSACTION_ID = id AND + TRA.RDB$TRANSACTION_DESCRIPTION NOT MISSING + trans = get_description(&TRA.RDB$TRANSACTION_DESCRIPTION); + END_FOR ON_ERROR RETURN_ERROR(user_status) END_ERROR; + + gds__release_request(gds__status, GDS_REF(request)); + if (gds__status[1]) { + RETURN_ERROR(user_status) + } + } + + ROLLBACK ON_ERROR RETURN_ERROR(user_status) END_ERROR; + + if (trans) + trans->tdr_db_caps = capabilities; + + return trans; +} + + +/*____________________________________________________________ + * + * Get the capabilities associated with + * the database for a particular transaction. + */ + +void MET_set_capabilities(STATUS * user_status, TDR trans) +{ + TGBL tdgbl; + + tdgbl = GET_THREAD_DATA; + + if (!(DB = trans->tdr_db_handle)) + return; + + START_TRANSACTION ON_ERROR RETURN_ERROR(user_status) END_ERROR; + + trans->tdr_db_caps = get_capabilities(user_status); + + ROLLBACK ON_ERROR RETURN_ERROR(user_status) END_ERROR; +} + + +/*____________________________________________________________ + * + * Eat a string with a byte-encoded length. + */ + +static STR alloc_string(TEXT ** ptr) +{ + TEXT *p, *q; + USHORT length; + STR string; + TGBL tdgbl; + + tdgbl = GET_THREAD_DATA; + + p = *ptr; + + length = (USHORT) * p++; + string = (STR) ALLOCDV(type_str, length + 1); + + q = (TEXT *) string->str_data; + while (length--) + *q++ = *p++; + *q = 0; + + *ptr = p; + + return string; +} + + + +/*____________________________________________________________ + * + * Get the capabilities associated with + * the database for a particular transaction. + */ + +static USHORT get_capabilities(STATUS * user_status) +{ + ULONG *req; + RFR_TAB rel_field_table; + USHORT capabilities = CAP_none; + TGBL tdgbl; + + tdgbl = GET_THREAD_DATA; + + req = NULL; + +/* Look for desired fields in system relations */ + + for (rel_field_table = rfr_table; rel_field_table->relation; + rel_field_table++) { + FOR(REQUEST_HANDLE req) x IN DB. + RDB$RELATION_FIELDS WITH x.RDB$RELATION_NAME = + rel_field_table->relation AND x.RDB$FIELD_NAME = + rel_field_table->field capabilities |= rel_field_table->bit_mask; + END_FOR ON_ERROR RETURN_ERROR(user_status) END_ERROR; + } + + gds__release_request(gds__status, GDS_REF(req)); + if (gds__status[1]) { + RETURN_ERROR(user_status) + } + + return capabilities; +} + + +/*____________________________________________________________ + * + * Get the description of a transaction in + * limbo, including all associated transactions + * in other databases. + */ + +static TDR get_description(SLONG blob_id[2]) +{ + SLONG id_length, id; + USHORT length; + TEXT buffer[1024], *bigger_buffer, *p; + TDR trans, ptr; + STR host_site, database_path; + TGBL tdgbl; + + tdgbl = GET_THREAD_DATA; + + p = buffer; + if (length = snarf_blob(blob_id, (USHORT) sizeof(buffer), buffer)) { + p = bigger_buffer = (TEXT *) gds__alloc((SLONG) length); + snarf_blob(blob_id, length, bigger_buffer); + } + + trans = NULL; + host_site = database_path = NULL; + +/* skip version number */ + + p++; + + while (*p) + switch (*p++) { + case TDR_HOST_SITE: + host_site = alloc_string(&p); + break; + + case TDR_DATABASE_PATH: + database_path = alloc_string(&p); + break; + + case TDR_TRANSACTION_ID: + id_length = *p++; + id = gds__vax_integer(p, id_length); + p += id_length; + if (!trans) + trans = ptr = (TDR) ALLOCD(type_tdr); + else { + ptr->tdr_next = (TDR) ALLOCD(type_tdr); + ptr = ptr->tdr_next; + } + ptr->tdr_host_site = host_site; + ptr->tdr_fullpath = database_path; + parse_fullpath(ptr); + ptr->tdr_id = id; + database_path = NULL; + break; + + default: + ALICE_print(108, 0, 0, 0, 0, 0); /* msg 108: Transaction description item unknown. */ + + if (length) + gds__free(bigger_buffer); + return NULL; + } + + if (length) + gds__free(bigger_buffer); + + return trans; +} + + +/*____________________________________________________________ + * + * Take apart a full remote path specification, + * finding the filename and the remote site. + */ + +static void parse_fullpath(TDR trans) +{ + TEXT *start, *p, *q, *end; + USHORT length; + TGBL tdgbl; + + tdgbl = GET_THREAD_DATA; + +/* start at the end of the full pathname */ + + start = p = trans->tdr_fullpath->str_data; + while (*p) + p++; + end = p; + +/* Check for a named pipes name - \\node\path\db or //node/path/db */ + while (p > start && + !(*p == '/' && *(p - 1) == '/') && + !(*p == '\\' && *(p - 1) == '\\')) p--; + + if (p > start) { + /* Increment p past slash, & search forward for end of node name */ + p = p + 1; + q = p; + + while (*q && *q != '/' && *q != '\\') + q++; + if (*q) { + trans->tdr_filename = q + 1; + + trans->tdr_remote_site = (STR) ALLOCDV(type_str, q - p + 1); + strncpy(trans->tdr_remote_site->str_data, p, q - p); + trans->tdr_remote_site->str_data[q - p] = '\0'; + } + } + else { + p = end; + + /* If not named pipes, check the other protocols + * work backwards until we find a remote protocol specifier + */ + + while (p >= start && (*p != '^' && *p != ':' && *p != '@')) + p--; + trans->tdr_filename = p + 1; + + /* now find the last remote node in the chain */ + + while (p > start && (*p == ':' || *p == '^' || *p == '@')) + p--; + for (length = 0; p >= start && (*p != '^' && *p != ':' && *p != '@'); + length++) + p--; + p++; + + if (length) { + trans->tdr_remote_site = (STR) ALLOCDV(type_str, length + 1); + q = (TEXT *) trans->tdr_remote_site->str_data; + while (length--) + *q++ = *p++; + *q = 0; + } + } +} + + +/*____________________________________________________________ + * + * Get a blob into a buffer, returning the + * size of the blob if the passed buffer + * is not big enough. + */ + +static USHORT snarf_blob(SLONG blob_id[2], + USHORT buffer_length, TEXT * buffer) +{ + USHORT returned_length; + TEXT *ptr, *end; + STATUS status; + int *blob; + TGBL tdgbl; + + tdgbl = GET_THREAD_DATA; + + if (buffer_length) + buffer[0] = 0; + if (buffer_length > 1) + buffer[1] = 0; + + blob = NULL; + if (gds__open_blob(gds__status, + GDS_REF(DB), + GDS_REF(gds__trans), GDS_REF(blob), GDS_VAL(blob_id))) { + ALICE_print_status(gds__status); + return 0; + } + +/* get the blob into the buffer, if it fits */ + + ptr = buffer; + end = buffer + buffer_length; + for (;;) { + if (ptr >= end) + break; + if (!(buffer_length = end - ptr)) + break; + status = gds__get_segment(gds__status, + GDS_REF(blob), + GDS_REF(returned_length), + buffer_length, GDS_VAL(ptr)); + if (status && status != gds__segment) + break; + ptr += returned_length; + } + +/* snarf failed, get length of blob for retry */ + + if (!buffer_length) + for (;;) { + status = gds__get_segment(gds__status, + GDS_REF(blob), + GDS_REF(returned_length), + buffer_length, buffer); + if (status && status != gds__segment) + break; + buffer_length += returned_length; + } + else + buffer_length = 0; + + gds__close_blob(gds__status, GDS_REF(blob)); + + *ptr = 0; + + return buffer_length; +} diff --git a/src/alice/alice_meta.h b/src/alice/alice_meta.h new file mode 100644 index 0000000000..afa461e8f2 --- /dev/null +++ b/src/alice/alice_meta.h @@ -0,0 +1,39 @@ +/* + * PROGRAM: Alice (All Else) Utility + * MODULE: alice_meta.h + * DESCRIPTION: Prototype header file for alice_meta.e + * + * 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): ______________________________________. + */ + +#ifndef ALICE_ALICE_META_H +#define ALICE_ALICE_META_H + +#ifdef __cplusplus +extern "C" { +#endif + +void MET_disable_wal(STATUS*, isc_db_handle); +void MET_get_state(STATUS*, TDR); +TDR MET_get_transaction(STATUS*, isc_db_handle, SLONG); +void MET_set_capabilities(STATUS*, TDR); + +#ifdef __cplusplus +} +#endif +#endif /* ALICE_ALICE_META_H */ diff --git a/src/alice/alice_proto.h b/src/alice/alice_proto.h new file mode 100644 index 0000000000..06701d8970 --- /dev/null +++ b/src/alice/alice_proto.h @@ -0,0 +1,43 @@ +/* + * PROGRAM: Alice (All Else) Utility + * MODULE: alice_proto.h + * DESCRIPTION: Prototype header file for alice.c + * + * 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): ______________________________________. + */ + +#ifndef _ALICE_ALICE_PROTO_H_ +#define _ALICE_ALICE_PROTO_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +typedef int (DLL_EXPORT * OUTPUTPROC) (SLONG, UCHAR *); + +void ALICE_down_case(TEXT*, TEXT*); +int ALICE_gfix(int, char**, OUTPUTPROC, SLONG); +void ALICE_print(USHORT, TEXT*, TEXT*, TEXT*, TEXT*, TEXT*); +void ALICE_error(USHORT, TEXT*, TEXT*, TEXT*, TEXT*, TEXT*); +void ALICE_print_status(STATUS*); + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* _ALICE_ALICE_PROTO_H_ */ diff --git a/src/alice/aliceswi.h b/src/alice/aliceswi.h new file mode 100644 index 0000000000..051512557d --- /dev/null +++ b/src/alice/aliceswi.h @@ -0,0 +1,264 @@ +/* + * 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): ______________________________________. + */ +#ifndef ALICE_ALICESWI_H +#define ALICE_ALICESWI_H + +#include "../jrd/common.h" +#include "../jrd/ibase.h" + +/* switch definitions */ + +#define sw_list 0x00000001L /* Byte 0, Bit 0 */ +#define sw_prompt 0x00000002L +#define sw_commit 0x00000004L +#define sw_rollback 0x00000008L +#define sw_sweep 0x00000010L +#define sw_validate 0x00000020L +#define sw_no_update 0x00000040L +#define sw_full 0x00000080L +#define sw_mend 0x00000100L /* Byte 1, Bit 0 */ +#define sw_all 0x00000200L +#define sw_enable 0x00000400L +#define sw_disable 0x00000800L +#define sw_ignore 0x00001000L +#define sw_activate 0x00002000L +#define sw_two_phase 0x00004000L +#define sw_housekeeping 0x00008000L +#define sw_kill 0x00010000L /* Byte 2, Bit 0 */ +#define sw_begin_log 0x00020000L +#define sw_quit_log 0x00040000L +#define sw_write 0x00080000L +#define sw_use 0x00100000L +#define sw_user 0x00200000L +#define sw_password 0x00400000L +#define sw_shut 0x00800000L +#define sw_online 0x01000000L /* Byte 3, Bit 0 */ +#define sw_cache 0x02000000L +#define sw_attach 0x04000000L +#define sw_force 0x08000000L +#define sw_tran 0x10000000L +#define sw_buffers 0x20000000L +#define sw_mode 0x40000000L +#define sw_set_db_dialect 0x80000000L +#define sw_z 0x0L + +#define SW_MEND sw_mend | sw_validate | sw_full + +#define IN_SW_ALICE_0 0 /* not a known switch */ +#define IN_SW_ALICE_LIST 1 +#define IN_SW_ALICE_PROMPT 2 +#define IN_SW_ALICE_COMMIT 3 +#define IN_SW_ALICE_ROLLBACK 4 +#define IN_SW_ALICE_SWEEP 5 +#define IN_SW_ALICE_VALIDATE 6 +#define IN_SW_ALICE_NO_UPDATE 7 +#define IN_SW_ALICE_FULL 8 +#define IN_SW_ALICE_MEND 9 +#define IN_SW_ALICE_ALL 10 +#define IN_SW_ALICE_ENABLE 11 +#define IN_SW_ALICE_DISABLE 12 +#define IN_SW_ALICE_IGNORE 13 +#define IN_SW_ALICE_ACTIVATE 14 +#define IN_SW_ALICE_TWO_PHASE 15 +#define IN_SW_ALICE_HOUSEKEEPING 16 +#define IN_SW_ALICE_KILL 17 +#define IN_SW_ALICE_BEGIN_LOG 18 +#define IN_SW_ALICE_QUIT_LOG 19 +#define IN_SW_ALICE_WRITE 20 +#define IN_SW_ALICE_USE 21 +#define IN_SW_ALICE_USER 22 +#define IN_SW_ALICE_PASSWORD 23 +#define IN_SW_ALICE_SHUT 24 +#define IN_SW_ALICE_ONLINE 25 +#define IN_SW_ALICE_CACHE 26 +#define IN_SW_ALICE_ATTACH 27 +#define IN_SW_ALICE_FORCE 28 +#define IN_SW_ALICE_TRAN 29 +#define IN_SW_ALICE_BUFFERS 30 +#define IN_SW_ALICE_Z 31 +#define IN_SW_ALICE_X 32 /* set debug mode on */ +#define IN_SW_ALICE_HIDDEN_ASYNC 33 +#define IN_SW_ALICE_HIDDEN_SYNC 34 +#define IN_SW_ALICE_HIDDEN_USEALL 35 +#define IN_SW_ALICE_HIDDEN_RESERVE 36 +#define IN_SW_ALICE_HIDDEN_RDONLY 37 +#define IN_SW_ALICE_HIDDEN_RDWRITE 38 +#define IN_SW_ALICE_MODE 39 +#define IN_SW_ALICE_HIDDEN_FORCE 40 +#define IN_SW_ALICE_HIDDEN_TRAN 41 +#define IN_SW_ALICE_HIDDEN_ATTACH 42 +#define IN_SW_ALICE_SET_DB_SQL_DIALECT 43 + +#define ALICE_SW_ASYNC "async" +#define ALICE_SW_SYNC "sync" +#define ALICE_SW_MODE_RO "read_only" +#define ALICE_SW_MODE_RW "read_write" + +/* Switch table */ +static struct in_sw_tab_t alice_in_sw_table[] = +{ + IN_SW_ALICE_ACTIVATE, isc_spb_prp_activate, "activate", sw_activate, + 0, ~sw_activate, FALSE, 25, 0, NULL, + /* msg 25: \t-activate shadow file for database usage */ + IN_SW_ALICE_ATTACH, 0, "attach", sw_attach, + sw_shut, 0, FALSE, 26, 0, NULL, + /* msg 26: \t-attach\tshutdown new database attachments */ +#ifdef DEV_BUILD + IN_SW_ALICE_BEGIN_LOG, 0, "begin_log", sw_begin_log, + 0, ~sw_begin_log, FALSE, 27, 0, NULL, + /* msg 27: \t-begin_log\tbegin logging for replay utility */ +#endif + IN_SW_ALICE_BUFFERS, isc_spb_prp_page_buffers, "buffers", sw_buffers, + 0, 0, FALSE, 28, 0, NULL, + /* msg 28: \t-buffers\tset page buffers */ + IN_SW_ALICE_COMMIT, isc_spb_rpr_commit_trans, "commit", sw_commit, + 0, ~sw_commit, FALSE, 29, 0, NULL, + /* msg 29: \t-commit\t\tcommit transaction */ + IN_SW_ALICE_CACHE, 0, "cache", sw_cache, + sw_shut, 0, FALSE, 30, 0, NULL, + /* msg 30: \t-cache\t\tshutdown cache manager */ +#ifdef DEV_BUILD + IN_SW_ALICE_DISABLE, 0, "disable", sw_disable, + 0, 0, FALSE, 31, 0, NULL, + /* msg 31: \t-disable\tdisable WAL */ +#endif + IN_SW_ALICE_FULL, isc_spb_rpr_full, "full", sw_full, + sw_validate, 0, FALSE, 32, 0, NULL, + /* msg 32: \t-full\t\tvalidate record fragments (-v) */ + IN_SW_ALICE_FORCE, 0, "force", sw_force, + sw_shut, 0, FALSE, 33, 0, NULL, + /* msg 33: \t-force\t\tforce database shutdown */ + IN_SW_ALICE_HOUSEKEEPING, isc_spb_prp_sweep_interval, "housekeeping", + sw_housekeeping, + 0, 0, FALSE, 34, 0, NULL, + /* msg 34: \t-housekeeping\tset sweep interval */ + IN_SW_ALICE_IGNORE, isc_spb_rpr_ignore_checksum, "ignore", sw_ignore, + 0, 0, FALSE, 35, 0, NULL, + /* msg 35: \t-ignore\t\tignore checksum errors */ + IN_SW_ALICE_KILL, isc_spb_rpr_kill_shadows, "kill", sw_kill, + 0, 0, FALSE, 36, 0, NULL, + /* msg 36: \t-kill\t\tkill all unavailable shadow files */ + IN_SW_ALICE_LIST, isc_spb_rpr_list_limbo_trans, "list", sw_list, + 0, ~sw_list, FALSE, 37, 0, NULL, + /* msg 37: \t-list\t\tshow limbo transactions */ + IN_SW_ALICE_MEND, isc_spb_rpr_mend_db, "mend", SW_MEND, + 0, ~sw_no_update, FALSE, 38, 0, NULL, + /* msg 38: \t-mend\t\tprepare corrupt database for backup */ +#ifdef READONLY_DATABASE + IN_SW_ALICE_MODE, 0, "mode", sw_mode, + 0, ~sw_mode, FALSE, 109, 0, NULL, + /* msg 109: \t-mode\t\tread_only or read_write */ +#endif /* READONLY_DATABASE */ + IN_SW_ALICE_NO_UPDATE, isc_spb_rpr_check_db, "no_update", sw_no_update, + sw_validate, 0, FALSE, 39, 0, NULL, + /* msg 39: \t-no_update\tread-only validation (-v) */ + IN_SW_ALICE_ONLINE, isc_spb_prp_db_online, "online", sw_online, + 0, 0, FALSE, 40, 0, NULL, + /* msg 40: \t-online\t\tdatabase online */ + IN_SW_ALICE_PROMPT, 0, "prompt", sw_prompt, + sw_list, 0, FALSE, 41, 0, NULL, + /* msg 41: \t-prompt\t\tprompt for commit/rollback (-l) */ + IN_SW_ALICE_PASSWORD, 0, "password", sw_password, + 0, 0, FALSE, 42, 0, NULL, + /* msg 42: \t-password\tdefault password */ +#ifdef DEV_BUILD + IN_SW_ALICE_QUIT_LOG, 0, "quit_log", sw_quit_log, + 0, ~sw_quit_log, FALSE, 43, 0, NULL, + /* msg 43: \t-quit_log\tquit logging for replay utility */ +#endif + IN_SW_ALICE_ROLLBACK, isc_spb_rpr_rollback_trans, "rollback", sw_rollback, + 0, ~sw_rollback, FALSE, 44, 0, NULL, + /* msg 44: \t-rollback\trollback transaction */ + IN_SW_ALICE_SET_DB_SQL_DIALECT, + isc_spb_prp_set_sql_dialect, + "sql_dialect", + sw_set_db_dialect, + 0, + 0, + FALSE, + 111, + 0, + NULL, + /* msg 111: \t-SQL_dialect\t\set dataabse dialect n */ + IN_SW_ALICE_SWEEP, isc_spb_rpr_sweep_db, "sweep", sw_sweep, + 0, ~sw_sweep, FALSE, 45, 0, NULL, + /* msg 45: \t-sweep\t\tforce garbage collection */ + IN_SW_ALICE_SHUT, 0, "shut", sw_shut, + 0, ~(sw_shut | + sw_attach | sw_cache | sw_force | sw_tran), FALSE, 46, 0, NULL, + /* msg 46: \t-shut\t\tshutdown */ + IN_SW_ALICE_TWO_PHASE, isc_spb_rpr_recover_two_phase, "two_phase", + sw_two_phase, + 0, ~sw_two_phase, FALSE, 47, 0, NULL, + /* msg 47: \t-two_phase\tperform automated two-phase recovery */ + IN_SW_ALICE_TRAN, 0, "tran", sw_tran, + sw_shut, 0, FALSE, 48, 0, NULL, + /* msg 48: \t-tran\t\tshutdown transaction startup */ + IN_SW_ALICE_USE, 0, "use", sw_use, + 0, ~sw_use, FALSE, 49, 0, NULL, + /* msg 49: \t-use\t\tuse full or reserve space for versions */ + IN_SW_ALICE_USER, 0, "user", sw_user, + 0, 0, FALSE, 50, 0, NULL, + /* msg 50: \t-user\t\tdefault user name */ + IN_SW_ALICE_VALIDATE, isc_spb_rpr_validate_db, "validate", sw_validate, + 0, ~sw_validate, FALSE, 51, 0, NULL, + /* msg 51: \t-validate\tvalidate database structure */ + IN_SW_ALICE_WRITE, 0, "write", sw_write, + 0, ~sw_write, FALSE, 52, 0, NULL, + /* msg 52: \t-write\t\twrite synchronously or asynchronously */ +#ifdef DEV_BUILD + IN_SW_ALICE_X, 0, "x", 0, + 0, 0, FALSE, 53, 0, NULL, + /* msg 53: \t-x\t\tset debug on */ +#endif + IN_SW_ALICE_Z, 0, "z", sw_z, + 0, 0, FALSE, 54, 0, NULL, + /* msg 54: \t-z\t\tprint software version number */ +/************************************************************************/ +/* WARNING: All new switches should be added right before this comments */ +/************************************************************************/ +/* The next nine 'virtual' switches are hidden from user and are needed + for services API + ************************************************************************/ + IN_SW_ALICE_HIDDEN_ASYNC, isc_spb_prp_wm_async, "write async", 0, 0, 0, + FALSE, 0, 0, NULL, + IN_SW_ALICE_HIDDEN_SYNC, isc_spb_prp_wm_sync, "write sync", 0, 0, 0, + FALSE, 0, 0, NULL, + IN_SW_ALICE_HIDDEN_USEALL, isc_spb_prp_res_use_full, "use full", 0, 0, 0, + FALSE, 0, 0, NULL, + IN_SW_ALICE_HIDDEN_RESERVE, isc_spb_prp_res, "use reserve", 0, 0, 0, + FALSE, 0, 0, NULL, + IN_SW_ALICE_HIDDEN_FORCE, isc_spb_prp_shutdown_db, "shut -force", 0, 0, 0, + FALSE, 0, 0, NULL, + IN_SW_ALICE_HIDDEN_TRAN, isc_spb_prp_deny_new_transactions, "shut -tran", + 0, 0, 0, FALSE, 0, 0, NULL, + IN_SW_ALICE_HIDDEN_ATTACH, isc_spb_prp_deny_new_attachments, + "shut -attach", 0, 0, 0, FALSE, 0, 0, NULL, +#ifdef READONLY_DATABASE + IN_SW_ALICE_HIDDEN_RDONLY, isc_spb_prp_am_readonly, "mode read_only", 0, + 0, 0, FALSE, 0, 0, NULL, + IN_SW_ALICE_HIDDEN_RDWRITE, isc_spb_prp_am_readwrite, "mode read_write", + 0, 0, 0, FALSE, 0, 0, NULL, +#endif /* READONLY_DATABASE */ +/************************************************************************/ + IN_SW_ALICE_0, 0, NULL, 0, + 0, 0, FALSE, 0, 0, NULL +}; + +#endif /* ALICE_ALICESWI_H */ diff --git a/src/alice/all.cpp b/src/alice/all.cpp new file mode 100644 index 0000000000..4c65a93b60 --- /dev/null +++ b/src/alice/all.cpp @@ -0,0 +1,548 @@ +//____________________________________________________________ +// +// PROGRAM: Alice (All Else) Utility +// MODULE: all.cpp +// DESCRIPTION: Block allocator +// +// 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): ______________________________________. +// +// +//____________________________________________________________ +// +// $Id: all.cpp,v 1.1.1.1 2001-05-23 13:25:33 tamlin Exp $ +// + +#include "../jrd/ib_stdio.h" +#include "../jrd/common.h" +#include "../jrd/ibsetjmp.h" +#include "../alice/alice.h" +#include "../alice/all.h" +#include "../alice/alloc.h" +#include "../alice/lls.h" +#include "../alice/all_proto.h" +#include "../jrd/gds_proto.h" +#include "../jrd/thd_proto.h" + +#define BLKDEF(type, root, tail) sizeof (struct root), tail, +static struct { + SSHORT typ_root_length; + SSHORT typ_tail_length; +} block_sizes[] = { + 0, 0, +#include "../alice/blk.h" +0}; + +#undef BLKDEF + +static void extend_pool(PLB, SLONG); +static PLB find_pool(BLK); +static void release(FRB, PLB); + + + +//____________________________________________________________ +// +// Allocate a block from a given pool and initialize the block. +// This is the primary block allocation routine. +// + +BLK ALLA_alloc(PLB pool, UCHAR type, int count) +{ + register BLK block; + FRB free, *best, *ptr; + SLONG best_tail, tail; + USHORT units; + register SLONG size; + register USHORT *p; + TGBL tdgbl; + + tdgbl = GET_THREAD_DATA; + + if (type <= (UCHAR) type_MIN || type >= (SCHAR) type_MAX) + ALICE_error(62, 0, 0, 0, 0, 0); /* msg 62: bad block type */ + +// Compute block length + + size = block_sizes[type].typ_root_length; + + if ((tail = block_sizes[type].typ_tail_length) && count >= FUDGE) + size += (count - FUDGE) * tail; + + size = (size + MIN_ALLOC - 1) & ~((ULONG) MIN_ALLOC - 1); +// TMN: Here we should probably have the following assert +// assert((size >> SHIFT) <= MAX_USHORT); + units = (USHORT) (size >> SHIFT); + + if (size >= MAX_BLOCK) + ALICE_error(63, 0, 0, 0, 0, 0); /* msg 63:internal block exceeds maximum size */ + +// Find best fit. Best fit is defined to be the free block of shortest +// tail. If there isn't a fit, extend the pool and try, try again. + + while (TRUE) { + best = NULL; + best_tail = MAX_BLOCK; + for (ptr = &pool->plb_free; (free = *ptr); ptr = &free->frb_next) { + if (free == free->frb_next) + ALICE_error(64, 0, 0, 0, 0, 0); /* msg 64: corrupt pool */ + if ((tail = (int) free->frb_header.blk_length - (int) units) >= 0 + && tail < best_tail) { + best = ptr; + best_tail = tail; + if (tail == 0) + break; + } + } + if (best) + break; + extend_pool(pool, size); + } + +// We've got our free block. If there's enough left of the free block +// after taking out our block, chop out out block. If not, allocate +// the entire free block as our block (a little extra won't hurt). + + best_tail <<= SHIFT; + free = *best; + + if (best_tail > sizeof(struct frb)) { + block = + (BLK) ((SCHAR *) free + (free->frb_header.blk_length << SHIFT)); + block = (BLK) ((SCHAR *) block - size); + free->frb_header.blk_length -= units; + } + else { + *best = free->frb_next; + units = free->frb_header.blk_length; + size = units << SHIFT; + block = (BLK) free; + } + +// zero out the allocated memory + + size >>= 1; + p = (USHORT *) block; + do + *p++ = 0; + while (--size); + + block->blk_type = type; +// TMN: Here we should probably have the following assert +// assert(pool->plb_pool_id <= MAX_UCHAR); + block->blk_pool_id = (UCHAR) pool->plb_pool_id; + block->blk_length = units; + + return block; +} + + +//____________________________________________________________ +// +// Extend a repeating block, copying the constant part. +// + +BLK ALLA_extend(BLK * pointer, int size) +{ + BLK block; + PLB pool; + SLONG old_length, new_length; + register SCHAR *from, *to; + register SSHORT length; + + block = *pointer; + pool = find_pool(block); + BLK new_ = ALLA_alloc(pool, block->blk_type, size); + + old_length = block->blk_length << SHIFT; + new_length = new_->blk_length << SHIFT; + + from = (SCHAR *) block + sizeof(struct blk); + to = (SCHAR *) new_ + sizeof(struct blk); + length = MIN(old_length, new_length) - sizeof(struct blk); + if (length) + do + *to++ = *from++; + while (--length); + + release(reinterpret_cast < frb * >(block), pool); + + if (new_->blk_type == (UCHAR) type_vec) + ((VEC) new_)->vec_count = size; + else if (new_->blk_type == (UCHAR) type_vcl) + ((VCL) new_)->vcl_count = size; + + *pointer = new_; + + return new_; +} + + +//____________________________________________________________ +// +// Get rid of everything. +// + +void ALLA_fini(void) +{ + register PLB pool, *vector, *until; + register HNK hunks, hunk; + TGBL tdgbl; + + tdgbl = GET_THREAD_DATA; + + if (tdgbl->pools) { + for (vector = + (PLB *) tdgbl->pools->vec_object + tdgbl->pools->vec_count, + until = (PLB *) tdgbl->pools->vec_object; --vector >= until;) + if (pool = *vector) + for (hunks = pool->plb_hunks; hunk = hunks;) { + hunks = hunk->hnk_next; + ALLA_free(hunk->hnk_address); + } + } + + tdgbl->pools = NULL; +} + + +//____________________________________________________________ +// +// Give space back to system. +// + +void ALLA_free(SCHAR * memory) +{ + + gds__free(memory); +} + + +//____________________________________________________________ +// +// Initialize the pool system. +// + +void ALLA_init(void) +{ + SLONG temp_vector[20]; + VEC vector; + PLB pool; + TGBL tdgbl; + + tdgbl = GET_THREAD_DATA; + + tdgbl->pools = vector = (VEC) temp_vector; + vector->vec_count = 1; + vector->vec_object[0] = NULL; + + tdgbl->ALICE_default_pool = tdgbl->ALICE_permanent_pool = pool = + ALLA_pool(); + tdgbl->pools = vector = (VEC) ALLA_alloc(pool, type_vec, 10); + vector->vec_count = 10; + vector->vec_object[0] = (BLK) pool; +} + + +//____________________________________________________________ +// +// Get memory from system. +// + +SCHAR *ALLA_malloc(SLONG size) +{ + register SCHAR *memory; + TGBL tdgbl; + + tdgbl = GET_THREAD_DATA; + + if (memory = (SCHAR *) gds__alloc(size)) + return memory; + + ALICE_error(65, 0, 0, 0, 0, 0); /* msg 65: virtual memory exhausted */ + return (SCHAR *) 0; /* Keep compilers happy. ALICE_error() never returns. */ +} + + +//____________________________________________________________ +// +// Allocate a new pool. This is done by creating a tempory +// pool block on the stack, then allocating a real pool block. +// In USHORT, by mirrors. +// + +PLB ALLA_pool(void) +{ + struct plb temp_pool; + register VEC vector; + register PLB pool; + register int pool_id; + TGBL tdgbl; + + tdgbl = GET_THREAD_DATA; + + +// Start by assigning a pool id + + vector = tdgbl->pools; + + for (pool_id = 0; pool_id < (int) vector->vec_count; pool_id++) + if (!(vector->vec_object[pool_id])) + break; + + if (pool_id >= (int) vector->vec_count) + vector = + (VEC) ALLA_extend(reinterpret_cast < blk ** >(&(tdgbl->pools)), + pool_id + 10); + + vector->vec_object[pool_id] = (BLK) & temp_pool; + temp_pool.plb_free = NULL; + temp_pool.plb_hunks = NULL; + temp_pool.plb_pool_id = pool_id; + if (pool_id == 0) + tdgbl->ALICE_permanent_pool = &temp_pool; + + pool = (PLB) ALLA_alloc(&temp_pool, type_plb, 0); + pool->plb_pool_id = pool_id; + pool->plb_free = temp_pool.plb_free; + pool->plb_hunks = temp_pool.plb_hunks; + vector->vec_object[pool_id] = (BLK) pool; + + if (pool_id == 0) + tdgbl->ALICE_permanent_pool = pool; + + return pool; +} + + +//____________________________________________________________ +// +// Push an object on an LLS stack. +// + +void ALLA_push(BLK object, register LLS * stack) +{ + register LLS node; + PLB pool; + TGBL tdgbl; + + tdgbl = GET_THREAD_DATA; + + pool = tdgbl->ALICE_default_pool; + + if (node = pool->plb_lls) + pool->plb_lls = node->lls_next; + else + node = (LLS) ALLA_alloc(pool, type_lls, 0); + + node->lls_object = object; + node->lls_next = *stack; + *stack = node; +} + + +//____________________________________________________________ +// +// Pop an object off a linked list stack. Save the node for +// further use. +// + +BLK ALLA_pop(register LLS * stack) +{ + register LLS node; + register PLB pool; + TGBL tdgbl; + + tdgbl = GET_THREAD_DATA; + + + node = *stack; + pool = (PLB) tdgbl->pools->vec_object[node->lls_header.blk_pool_id]; + *stack = node->lls_next; + node->lls_next = pool->plb_lls; + pool->plb_lls = node; + + return node->lls_object; +} + + +//____________________________________________________________ +// +// Release a block to its pool. If it is contiguous to +// another free block, combine them. Otherwise link it +// into the free block linked list (kept in ascending order +// of addresses). +// + +void ALLA_release(register FRB block) +{ + + release(block, find_pool(block ? &block->frb_header : (BLK) 0)); +} + + +//____________________________________________________________ +// +// Release a storage pool. This involves nothing more than returning +// hunks to the free hunk list. +// + +void ALLA_rlpool(PLB pool) +{ + register HNK hunk, hunks; + TGBL tdgbl; + + tdgbl = GET_THREAD_DATA; + + + tdgbl->pools->vec_object[pool->plb_pool_id] = NULL; + + for (hunks = pool->plb_hunks; hunk = hunks;) { + hunks = hunk->hnk_next; + ALLA_free(hunk->hnk_address); + } +} + + +//____________________________________________________________ +// +// Allocate, extend, or no-op a vector making sure it is "long enough". +// + +VEC ALLA_vector(PLB pool, VEC * ptr, USHORT count) +{ + VEC vector; + + ++count; + + if (!(vector = *ptr)) { + vector = *ptr = (VEC) ALLA_alloc(pool, type_vec, count); + vector->vec_count = count; + return vector; + } + + if (vector->vec_count >= count) + return vector; + + return (VEC) ALLA_extend(reinterpret_cast < blk ** >(ptr), count); +} + + +//____________________________________________________________ +// +// Extend a pool by at least enough to accomodate a block +// of given size. +// + +static void extend_pool(PLB pool, SLONG size) +{ + register HNK hunk; + register BLK block; + + size = + (size + sizeof(struct hnk) + MIN_ALLOCATION - + 1) & ~((ULONG) MIN_ALLOCATION - 1); + block = (BLK) ALLA_malloc((SLONG) size); +// TMN: Here we should probably have the following assert +// assert((size >> SHIFT) <= MAX_USHORT); + block->blk_length = (USHORT) size >> SHIFT; + block->blk_type = (UCHAR) type_frb; +// TMN: Here we should probably have the following assert +// assert(pool->plb_pool_id <= MAX_UCHAR); + block->blk_pool_id = (UCHAR) pool->plb_pool_id; + release(reinterpret_cast < frb * >(block), pool); + + hunk = (HNK) ALLA_alloc(pool, type_hnk, 0); + hunk->hnk_address = (SCHAR *) block; + hunk->hnk_length = size; + hunk->hnk_next = pool->plb_hunks; + pool->plb_hunks = hunk; +} + + +//____________________________________________________________ +// +// Find pool associate with block. +// + +static PLB find_pool(BLK block) +{ + PLB pool; + HNK hunk; + USHORT pool_id; + TGBL tdgbl; + + tdgbl = GET_THREAD_DATA; + + + for (pool_id = block->blk_pool_id; + pool_id < tdgbl->pools->vec_count + && (pool = (PLB) tdgbl->pools->vec_object[pool_id]); pool_id += 256) + for (hunk = pool->plb_hunks; hunk; hunk = hunk->hnk_next) + if ((SCHAR *) block >= hunk->hnk_address + && (SCHAR *) block < hunk->hnk_address + hunk->hnk_length) + return pool; + + ALICE_error(66, 0, 0, 0, 0, 0); /* msg 66: bad pool id */ + return (PLB) 0; /* Keep compilers happy. ALICE_error() never returns. */ +} + + +//____________________________________________________________ +// +// Release a block to its pool. If it is contiguous to +// another free block, combine them. Otherwise link it +// into the free block linked list (kept in ascending order +// of addresses). +// + +static void release(FRB block, PLB pool) +{ + register FRB prior, free; + FRB *ptr; + SLONG length; + + block->frb_header.blk_type = (UCHAR) type_frb; + prior = NULL; + + for (ptr = &pool->plb_free; free = *ptr; + prior = free, ptr = &free->frb_next) if (block < free) + break; + +// Merge block into list first, then try to combine blocks + + block->frb_next = free; + *ptr = block; + +// Try to merge the free block with the next one down. + + length = block->frb_header.blk_length << SHIFT; + + if (free && (SCHAR *) block + length == (SCHAR *) free) { + block->frb_header.blk_length += free->frb_header.blk_length; + block->frb_next = free->frb_next; + } + +// Try and merge the block with the prior free block + + if (prior && + (length = prior->frb_header.blk_length << SHIFT) && + (SCHAR *) prior + length == (SCHAR *) block) { + prior->frb_header.blk_length += block->frb_header.blk_length; + prior->frb_next = block->frb_next; + } +} diff --git a/src/alice/all.h b/src/alice/all.h new file mode 100644 index 0000000000..1299de30e2 --- /dev/null +++ b/src/alice/all.h @@ -0,0 +1,76 @@ +/* + * PROGRAM: Alice + * MODULE: all.h + * DESCRIPTION: Data allocation declarations + * + * 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): ______________________________________. + */ + +#ifndef _ALICE_ALL_H_ +#define _ALICE_ALL_H_ + +#if ALIGNMENT == 8 +#define MIN_ALLOC 8 +#else +#define MIN_ALLOC 4 +#endif + +#define MAX_BLOCK 256000 +#define FUDGE 1 +#define SHIFT 2 + +#define MIN_ALLOCATION 1024 /* Minimin allocation from operating system */ + +/* This is the header to be used by all blocks to + specify the type of block, the pool in which it has + been allocated, and the length of its tail. */ + +#ifndef INCLUDE_FB_BLK +#include "../include/fb_blk.h" +#endif + +/* Free block */ + +typedef struct frb +{ + struct blk frb_header; + struct frb *frb_next; /* Next free block in pool */ +} *FRB; + +/* Pool block */ + +typedef struct plb +{ + struct blk plb_header; + USHORT plb_pool_id; /* pool id */ + struct frb *plb_free; /* first free block */ + struct hnk *plb_hunks; /* first hunk block */ + struct lls *plb_lls; /* avaiable linked list stack nodes */ +} *PLB; + +/* Hunk blocks */ + +typedef struct hnk +{ + struct blk hnk_header; + SCHAR *hnk_address; /* start of memory hunk */ + SLONG hnk_length; /* length of memory hunk */ + struct hnk *hnk_next; /* next memory hunk in structure */ +} *HNK; + +#endif /* _ALICE_ALL_H_ */ diff --git a/src/alice/all_proto.h b/src/alice/all_proto.h new file mode 100644 index 0000000000..f608f0c1ad --- /dev/null +++ b/src/alice/all_proto.h @@ -0,0 +1,50 @@ +/* + * PROGRAM: Alice (All Else) Utility + * MODULE: all_proto.h + * DESCRIPTION: Prototype header file for all.c + * + * 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): ______________________________________. + */ + +#ifndef _ALICE_ALL_PROTO_H_ +#define _ALICE_ALL_PROTO_H_ + +#include "../alice/lls.h" + +#ifdef __cplusplus +extern "C" { +#endif + +BLK ALLA_alloc(PLB, UCHAR, int); +BLK ALLA_extend(BLK *, int); +void ALLA_fini(void); +void ALLA_free(SCHAR *); +void ALLA_init(void); +SCHAR* ALLA_malloc(SLONG); +PLB ALLA_pool(void); +void ALLA_push(BLK, register LLS *); +BLK ALLA_pop(register LLS *); +void ALLA_release(register FRB); +void ALLA_rlpool(PLB); +VEC ALLA_vector(PLB, VEC *, USHORT); + +#ifdef __cplusplus +}; +#endif + +#endif /* _ALICE_ALL_PROTO_H_ */ diff --git a/src/alice/alloc.h b/src/alice/alloc.h new file mode 100644 index 0000000000..ca74671a4c --- /dev/null +++ b/src/alice/alloc.h @@ -0,0 +1,35 @@ +/* + * PROGRAM: Alice + * MODULE: alloc.h + * DESCRIPTION: Data allocation macros + * + * 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): ______________________________________. + */ + +#ifndef _ALICE_ALLOC_H_ +#define _ALICE_ALLOC_H_ + +#define ALL_release(blk) ALLA_release (blk) +#define ALLOCD(type) ALLA_alloc (tdgbl->ALICE_default_pool, type, 0) +#define ALLOCDV(type,repeat) ALLA_alloc (tdgbl->ALICE_default_pool, type, repeat) +#define ALLOCP(type) ALLA_alloc (tdgbl->ALICE_permanent_pool, type, 0) +#define ALLOCPV(type,repeat) ALLA_alloc (tdgbl->ALICE_permanent_pool, type, repeat) +#define ALLOC(type,pool) ALLA_alloc (pool, type, 0) +#define ALLOCV(type,pool,repeat) ALLA_alloc (pool, type, repeat) + +#endif /* _ALICE_ALLOC_H_ */ diff --git a/src/alice/blk.h b/src/alice/blk.h new file mode 100644 index 0000000000..5999594c17 --- /dev/null +++ b/src/alice/blk.h @@ -0,0 +1,31 @@ +/* + * PROGRAM: Alice + * MODULE: blk.h + * DESCRIPTION: Block type definitions + * + * 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): ______________________________________. + */ + +BLKDEF(type_frb, frb, 0) +BLKDEF(type_hnk, hnk, 0) +BLKDEF(type_plb, plb, 0) +BLKDEF(type_vec, vec, sizeof(((VEC) 0)->vec_object[0])) +BLKDEF(type_vcl, vcl, sizeof(((VCL) 0)->vcl_long[0])) +BLKDEF(type_tdr, tdr, 0) /* transaction description */ +BLKDEF(type_str, str, 1) /* general string block */ +BLKDEF(type_lls, lls, 0) diff --git a/src/alice/exe.cpp b/src/alice/exe.cpp new file mode 100644 index 0000000000..88e2084712 --- /dev/null +++ b/src/alice/exe.cpp @@ -0,0 +1,447 @@ +//____________________________________________________________ +// +// PROGRAM: Alice (All Else) Utility +// MODULE: exe.cpp +// DESCRIPTION: Does the database calls +// +// 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): ______________________________________. +// +// +//____________________________________________________________ +// +// $Id: exe.cpp,v 1.1.1.1 2001-05-23 13:25:33 tamlin Exp $ +// + +#include "../jrd/ib_stdio.h" +#include +#include +#include "../include/jrd/gds.h" +#include "../jrd/common.h" +#include "../jrd/ibsetjmp.h" +#include "../alice/alice.h" +#include "../alice/aliceswi.h" +#include "../alice/all.h" +#include "../alice/all_proto.h" +#include "../alice/alice_meta.h" +#include "../alice/tdr_proto.h" +#include "../jrd/gds_proto.h" +#include "../jrd/thd_proto.h" + +#if (defined WIN_NT || defined PC_PLATFORM) +#include +#endif + + +extern "C" { + + +static USHORT build_dpb(UCHAR *, ULONG); +static void extract_db_info(UCHAR *); + +static TEXT val_errors[] = +{ + isc_info_page_errors, isc_info_record_errors, isc_info_bpage_errors, + isc_info_dpage_errors, isc_info_ipage_errors, isc_info_ppage_errors, + isc_info_tpage_errors, gds_info_end +}; + +#ifndef MAXPATHLEN +#define MAXPATHLEN 1024 +#endif + +#define STUFF_DPB(blr) {*d++ = (UCHAR)(blr);} +#define STUFF_DPB_INT(blr) \ + { \ + STUFF_DPB(blr); \ + STUFF_DPB((blr) >> 8); \ + STUFF_DPB((blr) >> 16); \ + STUFF_DPB((blr) >> 24); \ + } + + + +//____________________________________________________________ +// +// + +int EXE_action(TEXT * database, ULONG switches) +{ + UCHAR dpb[128]; + USHORT dpb_length, error; + SLONG *handle; + UCHAR error_string[128]; + USHORT i; + TGBL tdgbl; + + tdgbl = GET_THREAD_DATA; + + ALLA_init(); + + for (i = 0; i < MAX_VAL_ERRORS; i++) + tdgbl->ALICE_data.ua_val_errors[i] = 0; + +// generate the database parameter block for the attach, +// based on the various switches + + dpb_length = build_dpb(dpb, switches); + + error = FALSE; + handle = NULL; + gds__attach_database(tdgbl->status, + 0, + GDS_VAL(database), + reinterpret_cast < void **>(GDS_REF(handle)), + dpb_length, + reinterpret_cast < char *>(GDS_VAL(dpb))); + + SVC_STARTED(tdgbl->service_blk); + + if (tdgbl->status[1]) + error = TRUE; + + if (tdgbl->status[2] == isc_arg_warning) + ALICE_print_status(tdgbl->status); + + if (handle != NULL) { + if ((switches & sw_validate) && (tdgbl->status[1] != isc_bug_check)) { + gds__database_info(tdgbl->status, + reinterpret_cast < void **>(GDS_REF(handle)), + sizeof(val_errors), + val_errors, + sizeof(error_string), + reinterpret_cast < char *>(error_string)); + + extract_db_info(error_string); + } + + if (switches & sw_disable) + MET_disable_wal(tdgbl->status, handle); + + gds__detach_database(tdgbl->status, + reinterpret_cast < void **>(GDS_REF(handle))); + } + + ALLA_fini(); + + return ((error) ? FINI_ERROR : FINI_OK); +} + + +#ifndef GUI_TOOLS +//____________________________________________________________ +// +// + +int EXE_two_phase(TEXT * database, ULONG switches) +{ + UCHAR dpb[128]; + USHORT dpb_length, error; + SLONG *handle; + USHORT i; + TGBL tdgbl; + + tdgbl = GET_THREAD_DATA; + + ALLA_init(); + + for (i = 0; i < MAX_VAL_ERRORS; i++) + tdgbl->ALICE_data.ua_val_errors[i] = 0; + +// generate the database parameter block for the attach, +// based on the various switches + + dpb_length = build_dpb(dpb, switches); + + error = FALSE; + handle = NULL; + gds__attach_database(tdgbl->status, + 0, + GDS_VAL(database), + reinterpret_cast < void **>(GDS_REF(handle)), + dpb_length, + reinterpret_cast < char *>(GDS_VAL(dpb))); + + SVC_STARTED(tdgbl->service_blk); + + if (tdgbl->status[1]) + error = TRUE; + else if (switches & sw_list) + TDR_list_limbo(reinterpret_cast < int *>(handle), database, switches); + else if (switches & (sw_commit | sw_rollback | sw_two_phase)) + error = + TDR_reconnect_multiple(reinterpret_cast < int *>(handle), + tdgbl->ALICE_data.ua_transaction, database, + switches); + + if (handle) + gds__detach_database(tdgbl->status, + reinterpret_cast < void **>(GDS_REF(handle))); + + ALLA_fini(); + + return ((error) ? FINI_ERROR : FINI_OK); +} +#endif /* GUI_TOOLS */ + + + +//____________________________________________________________ +// +// +// generate the database parameter block for the attach, +// based on the various switches +// + +static USHORT build_dpb(UCHAR * dpb, ULONG switches) +{ + UCHAR *d; + USHORT dpb_length; + SSHORT i; + TEXT *q; + TGBL tdgbl; + + tdgbl = GET_THREAD_DATA; + + d = dpb; + *d++ = gds_dpb_version1; + *d++ = isc_dpb_gfix_attach; + *d++ = 0; + + if (switches & sw_sweep) { + *d++ = gds_dpb_sweep; + *d++ = 1; + *d++ = gds_dpb_records; + } + else if (switches & sw_activate) { + *d++ = gds_dpb_activate_shadow; + *d++ = 0; + } + else if (switches & sw_validate) { + *d++ = gds_dpb_verify; + *d++ = 1; + *d = gds_dpb_pages; + if (switches & sw_full) + *d |= gds_dpb_records; + if (switches & sw_no_update) + *d |= gds_dpb_no_update; + if (switches & sw_mend) + *d |= gds_dpb_repair; + if (switches & sw_ignore) + *d |= gds_dpb_ignore; + d++; + } + else if (switches & sw_housekeeping) { + *d++ = gds_dpb_sweep_interval; + *d++ = 4; + for (i = 0; i < 4; + i++, tdgbl->ALICE_data.ua_sweep_interval = + tdgbl->ALICE_data.ua_sweep_interval >> 8) + /* TMN: Here we should really have the following assert */ + /* assert(tdgbl->ALICE_data.ua_sweep_interval <= MAX_UCHAR); */ + *d++ = (UCHAR) tdgbl->ALICE_data.ua_sweep_interval; + } + else if (switches & sw_begin_log) { + *d++ = gds_dpb_begin_log; + *d++ = strlen(tdgbl->ALICE_data.ua_log_file); + for (q = tdgbl->ALICE_data.ua_log_file; *q;) + *d++ = *q++; + } + else if (switches & sw_buffers) { + *d++ = isc_dpb_set_page_buffers; + *d++ = 4; + for (i = 0; i < 4; + i++, tdgbl->ALICE_data.ua_page_buffers = + tdgbl->ALICE_data.ua_page_buffers >> 8) + /* TMN: Here we should really have the following assert */ + /* assert(tdgbl->ALICE_data.ua_page_buffers <= MAX_UCHAR); */ + *d++ = (UCHAR) tdgbl->ALICE_data.ua_page_buffers; + } + else if (switches & sw_quit_log) { + *d++ = gds_dpb_quit_log; + *d++ = 0; + } + else if (switches & sw_kill) { + *d++ = gds_dpb_delete_shadow; + *d++ = 0; + } + else if (switches & sw_write) { + *d++ = gds_dpb_force_write; + *d++ = 1; + *d++ = tdgbl->ALICE_data.ua_force; + } + else if (switches & sw_use) { + *d++ = gds_dpb_no_reserve; + *d++ = 1; + *d++ = tdgbl->ALICE_data.ua_use; + } +#ifdef READONLY_DATABASE + else if (switches & sw_mode) { + *d++ = isc_dpb_set_db_readonly; + *d++ = 1; + *d++ = tdgbl->ALICE_data.ua_read_only; + } +#endif /* READONLY_DATABASE */ + else if (switches & sw_shut) { + *d++ = gds_dpb_shutdown; + *d++ = 1; + *d = 0; + if (switches & sw_attach) + *d |= gds_dpb_shut_attachment; + else if (switches & sw_cache) + *d |= gds_dpb_shut_cache; + else if (switches & sw_force) + *d |= gds_dpb_shut_force; + else if (switches & sw_tran) + *d |= gds_dpb_shut_transaction; + d++; + *d++ = gds_dpb_shutdown_delay; + *d++ = 2; /* Build room for shutdown delay */ + /* TMN: Here we should really have the following assert */ + /* assert(tdgbl->ALICE_data.ua_page_buffers <= MAX_USHORT); */ + /* or maybe even compare with MAX_SSHORT */ + *d++ = (UCHAR) tdgbl->ALICE_data.ua_shutdown_delay; + *d++ = (UCHAR) (tdgbl->ALICE_data.ua_shutdown_delay >> 8); + } + else if (switches & sw_online) { + *d++ = gds_dpb_online; + *d++ = 0; + } + else if (switches & sw_disable) { + *d++ = isc_dpb_disable_wal; + *d++ = 0; + } + else if (switches & (sw_list | sw_commit | sw_rollback | sw_two_phase)) { + *d++ = gds_dpb_no_garbage_collect; + *d++ = 0; + } + else if (switches & sw_set_db_dialect) { + STUFF_DPB(isc_dpb_set_db_sql_dialect); + STUFF_DPB(4); + STUFF_DPB_INT(tdgbl->ALICE_data.ua_db_SQL_dialect); + } + + if (tdgbl->ALICE_data.ua_user) { + *d++ = gds_dpb_user_name; + *d++ = + strlen(reinterpret_cast < + const char *>(tdgbl->ALICE_data.ua_user)); + for (q = reinterpret_cast < TEXT * >(tdgbl->ALICE_data.ua_user); *q;) + *d++ = *q++; + } + + if (tdgbl->ALICE_data.ua_password) { + if (!tdgbl->sw_service_thd) + *d++ = gds_dpb_password; + else + *d++ = gds_dpb_password_enc; + *d++ = + strlen(reinterpret_cast < + const char *>(tdgbl->ALICE_data.ua_password)); + for (q = reinterpret_cast < TEXT * >(tdgbl->ALICE_data.ua_password); + *q;) + *d++ = *q++; + } + + dpb_length = d - dpb; + if (dpb_length == 1) + dpb_length = 0; + + return dpb_length; +} + + +//____________________________________________________________ +// +// Extract database info from string +// + +static void extract_db_info(UCHAR * db_info_buffer) +{ + UCHAR item; + UCHAR *p; + SLONG length; + TGBL tdgbl; + + tdgbl = GET_THREAD_DATA; + + p = db_info_buffer; + + while ((item = *p++) != gds_info_end) { + length = gds__vax_integer(p, 2); + p += 2; + + /* TMN: Here we should really have the following assert */ + /* assert(length <= MAX_SSHORT); */ + /* for all cases that use 'length' as input to 'gds__vax_integer' */ + switch (item) { + case isc_info_page_errors: + tdgbl->ALICE_data.ua_val_errors[VAL_PAGE_ERRORS] = + gds__vax_integer(p, (SSHORT) length); + p += length; + break; + + case isc_info_record_errors: + tdgbl->ALICE_data.ua_val_errors[VAL_RECORD_ERRORS] = + gds__vax_integer(p, (SSHORT) length); + p += length; + break; + + case isc_info_bpage_errors: + tdgbl->ALICE_data.ua_val_errors[VAL_BLOB_PAGE_ERRORS] = + gds__vax_integer(p, (SSHORT) length); + p += length; + break; + + case isc_info_dpage_errors: + tdgbl->ALICE_data.ua_val_errors[VAL_DATA_PAGE_ERRORS] = + gds__vax_integer(p, (SSHORT) length); + p += length; + break; + + case isc_info_ipage_errors: + tdgbl->ALICE_data.ua_val_errors[VAL_INDEX_PAGE_ERRORS] = + gds__vax_integer(p, (SSHORT) length); + p += length; + break; + + case isc_info_ppage_errors: + tdgbl->ALICE_data.ua_val_errors[VAL_POINTER_PAGE_ERRORS] = + gds__vax_integer(p, (SSHORT) length); + p += length; + break; + + case isc_info_tpage_errors: + tdgbl->ALICE_data.ua_val_errors[VAL_TIP_PAGE_ERRORS] = + gds__vax_integer(p, (SSHORT) length); + p += length; + break; + + case isc_info_error: + /* has to be a < V4 database. */ + + tdgbl->ALICE_data.ua_val_errors[VAL_INVALID_DB_VERSION] = 1; + return; + + default: + ; + } + } +} + + +} // extern "C" diff --git a/src/alice/exe_proto.h b/src/alice/exe_proto.h new file mode 100644 index 0000000000..7750c6e513 --- /dev/null +++ b/src/alice/exe_proto.h @@ -0,0 +1,38 @@ +/* + * PROGRAM: Alice (All Else) Utility + * MODULE: exe_proto.h + * DESCRIPTION: Prototype header file for exe.c + * + * 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): ______________________________________. + */ + +#ifndef _ALICE_EXE_PROTO_H_ +#define _ALICE_EXE_PROTO_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +extern int EXE_action(TEXT *, ULONG); +extern int EXE_two_phase(TEXT *, ULONG); + +#ifdef __cplusplus +}; +#endif + +#endif /* _ALICE_ALICE_PROTO_H_ */ diff --git a/src/alice/info.h b/src/alice/info.h new file mode 100644 index 0000000000..631c7fe9db --- /dev/null +++ b/src/alice/info.h @@ -0,0 +1,107 @@ +/* + * PROGRAM: Alice + * MODULE: info.h + * DESCRIPTION: gds__version definitions + * + * 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): ______________________________________. + */ + +#ifndef ALICE_INFO_H +#define ALICE_INFO_H + +static UCHAR info[] = +{ + gds_info_version, + gds_info_implementation, + gds_info_end +}; + + +/* this table describes the implementations that + require a node_name,protocol combination be + generated */ + +static BOOLEAN generate_protocol[] = +{ + 0, /* NULL, 0 */ + 0, /* access method */ + 0, /* Y-valve */ + 0, /* remote interface */ + 1, /* remote server */ + 0, + 0, + 0, /* pipe interface */ + 1, /* pipe server */ + 0, /* central interface */ + 1, /* central server */ + 1, /* gateway */ +}; + + + +/* this table describes the protocols that can be + used for getting from a node of a given type */ + +#define DECNET_PROTOCOL 1 /* :: */ +#define TCP_PROTOCOL 2 /* : */ +#define VMS_TCP_PROTOCOL 4 /* ^ */ +#define APOLLO_PROTOCOL 8 /* // */ +#define MSLAN_PROTOCOL 16 /* \\ */ + +static USHORT protocols_supported[] = +{ + 0, + DECNET_PROTOCOL | VMS_TCP_PROTOCOL, /* "Rdb/VMS" */ + DECNET_PROTOCOL | VMS_TCP_PROTOCOL, /* "Rdb/ELN target" */ + DECNET_PROTOCOL | VMS_TCP_PROTOCOL, /* "Rdb/ELN development" */ + DECNET_PROTOCOL | VMS_TCP_PROTOCOL, /* "Rdb/VMS Y" */ + DECNET_PROTOCOL | VMS_TCP_PROTOCOL, /* "Rdb/ELN Y" */ + DECNET_PROTOCOL | VMS_TCP_PROTOCOL, /* "JRI" */ + DECNET_PROTOCOL | VMS_TCP_PROTOCOL, /* "JSV" */ + 0, + 0, + TCP_PROTOCOL | APOLLO_PROTOCOL, /* "InterBase/apollo" */ + DECNET_PROTOCOL | TCP_PROTOCOL, /* "InterBase/ultrix" */ + DECNET_PROTOCOL | VMS_TCP_PROTOCOL, /* "InterBase/vms" */ + TCP_PROTOCOL, /* "InterBase/sun" */ + TCP_PROTOCOL, /* "InterBase/OS2" */ + 0, /* 15 */ + 0, /* 16 */ + 0, /* 17 */ + 0, /* 18 */ + 0, /* 19 */ + 0, /* 20 */ + 0, /* 21 */ + 0, /* 22 */ + 0, /* 23 */ + 0, /* 24 */ + TCP_PROTOCOL | APOLLO_PROTOCOL, /* "InterBase/apollo" */ + DECNET_PROTOCOL | TCP_PROTOCOL, /* "InterBase/ultrix" */ + DECNET_PROTOCOL | VMS_TCP_PROTOCOL, /* "InterBase/vms" */ + TCP_PROTOCOL, /* "InterBase/sun" */ + TCP_PROTOCOL, /* "InterBase/OS2" */ + TCP_PROTOCOL, /* "InterBase/sun4" */ + TCP_PROTOCOL, /* "InterBase/hpux" */ + TCP_PROTOCOL, /* "InterBase/sun386" */ + DECNET_PROTOCOL | VMS_TCP_PROTOCOL, /* "InterBase:ORACLE/vms" */ + TCP_PROTOCOL, /* "InterBase/mac/aux" */ + TCP_PROTOCOL, /* "InterBase/ibm/rt" */ + DECNET_PROTOCOL | TCP_PROTOCOL, /* "InterBase/mips/ultrix" */ +}; + +#endif /* ALICE_INFO_H */ diff --git a/src/alice/lls.h b/src/alice/lls.h new file mode 100644 index 0000000000..bcb6face13 --- /dev/null +++ b/src/alice/lls.h @@ -0,0 +1,36 @@ +/* + * PROGRAM: Alice + * MODULE: lls.h + * DESCRIPTION: Linked list stack definitions + * + * 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): ______________________________________. + */ + +#ifndef _ALICE_LLS_H_ +#define _ALICE_LLS_H_ + +#define LLS_PUSH(object, stack) ALL_push (object, stack) +#define LLS_POP(stack) ALL_pop (stack) + +typedef struct lls { + struct blk lls_header; + struct blk *lls_object; + struct lls *lls_next; +} *LLS; + +#endif /* _ALICE_LLS_H_ */ diff --git a/src/alice/tdr.cpp b/src/alice/tdr.cpp new file mode 100644 index 0000000000..6842066e79 --- /dev/null +++ b/src/alice/tdr.cpp @@ -0,0 +1,995 @@ +//____________________________________________________________ +// +// PROGRAM: Alice (All Else) Utility +// MODULE: tdr.cpp +// DESCRIPTION: Routines for automated transaction recovery +// +// 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): ______________________________________. +// +// +//____________________________________________________________ +// +// $Id: tdr.cpp,v 1.1.1.1 2001-05-23 13:25:33 tamlin Exp $ +// + +#include "../jrd/ib_stdio.h" +#include +#include "../include/jrd/gds.h" +#include "../jrd/common.h" +#include "../alice/alice.h" +#include "../alice/aliceswi.h" +#include "../alice/all.h" +#include "../alice/alloc.h" +#include "../alice/info.h" +#include "../alice/alice_proto.h" +#include "../alice/all_proto.h" +#include "../alice/alice_meta.h" +#include "../alice/tdr_proto.h" +#include "../jrd/gds_proto.h" +#include "../jrd/isc_proto.h" +#include "../jrd/svc_proto.h" +#include "../jrd/thd_proto.h" + +static ULONG ask(void); +static void print_description(TDR); +static void reattach_database(TDR); +static void reattach_databases(TDR); +static BOOLEAN reconnect(int *, SLONG, TEXT *, ULONG); + + +#ifdef GUI_TOOLS +#define NEWLINE "\r\n" +#else +#define NEWLINE "\n" +#endif + +static UCHAR limbo_info[] = { gds_info_limbo, gds_info_end }; + + + + + +// +// The following routines are shared by the command line gfix and +// the windows server manager. These routines should not contain +// any direct screen I/O (i.e. ib_printf/ib_getc statements). +// + + +//____________________________________________________________ +// +// Determine the proper action to take +// based on the state of the various +// transactions. +// + +USHORT TDR_analyze(TDR trans) +{ + USHORT state, advice = TRA_none; + + if (trans == NULL) + return TRA_none; + +// if the tdr for the first transaction is missing, +// we can assume it was committed + + state = trans->tdr_state; + if (state == TRA_none) + state = TRA_commit; + else if (state == TRA_unknown) + advice = TRA_unknown; + + for (trans = trans->tdr_next; trans; trans = trans->tdr_next) { + switch (trans->tdr_state) { + /* an explicitly committed transaction necessitates a check for the + perverse case of a rollback, otherwise a commit if at all possible */ + + case TRA_commit: + if (state == TRA_rollback) { + ALICE_print(105, 0, 0, 0, 0, 0); /* msg 105: Warning: Multidatabase transaction is in inconsistent state for recovery. */ + ALICE_print(106, reinterpret_cast < char *>(trans->tdr_id), 0, + 0, 0, 0); /* msg 106: Transaction %ld was committed, but prior ones were rolled back. */ + return 0; + } + else + advice = TRA_commit; + break; + + // a prepared transaction requires a commit if there are missing + // records up to now, otherwise only do something if somebody else + // already has + + case TRA_limbo: + if (state == TRA_none) + advice = TRA_commit; + else if (state == TRA_commit) + advice = TRA_commit; + else if (state == TRA_rollback) + advice = TRA_rollback; + break; + + // an explicitly rolled back transaction requires a rollback unless a + // transaction has committed or is assumed committed + + case TRA_rollback: + if ((state == TRA_commit) || (state == TRA_none)) { + ALICE_print(105, 0, 0, 0, 0, 0); /* msg 105: Warning: Multidatabase transaction is in inconsistent state for recovery. */ + ALICE_print(107, reinterpret_cast < char *>(trans->tdr_id), 0, + 0, 0, 0); /* msg 107: Transaction %ld was rolled back, but prior ones were committed. */ + + return 0; + } + else + advice = TRA_rollback; + break; + + // a missing TDR indicates a committed transaction if a limbo one hasn't + // been found yet, otherwise it implies that the transaction wasn't + // prepared + + case TRA_none: + if (state == TRA_commit) + advice = TRA_commit; + else if (state == TRA_limbo) + advice = TRA_rollback; + break; + + // specifically advise TRA_unknown to prevent assumption that all are + // in limbo + + case TRA_unknown: + if (!advice) + advice = TRA_unknown; + break; + + default: + ALICE_print(67, reinterpret_cast < char *>(trans->tdr_state), 0, + 0, 0, 0); /* msg 67: Transaction state %d not in valid range. */ + return 0; + } + } + + return advice; +} + + + +//____________________________________________________________ +// +// Attempt to attach a database with a given pathname. +// + +BOOLEAN TDR_attach_database(STATUS * status_vector, + TDR trans, TEXT * pathname) +{ + UCHAR dpb[128], *d, *q; + USHORT dpb_length; + TGBL tdgbl; + + tdgbl = GET_THREAD_DATA; + +#ifndef GUI_TOOLS + if (tdgbl->ALICE_data.ua_debug) + ALICE_print(68, pathname, 0, 0, 0, 0); /* msg 68: ATTACH_DATABASE: attempted attach of %s */ +#endif + + d = dpb; + + *d++ = gds_dpb_version1; + *d++ = gds_dpb_no_garbage_collect; + *d++ = 0; + *d++ = isc_dpb_gfix_attach; + *d++ = 0; + + if (tdgbl->ALICE_data.ua_user) { + *d++ = gds_dpb_user_name; + *d++ = + strlen(reinterpret_cast < + const char *>(tdgbl->ALICE_data.ua_user)); + for (q = tdgbl->ALICE_data.ua_user; *q;) + *d++ = *q++; + } + + if (tdgbl->ALICE_data.ua_password) { + if (!tdgbl->sw_service_thd) + *d++ = gds_dpb_password; + else + *d++ = gds_dpb_password_enc; + *d++ = + strlen(reinterpret_cast < + const char *>(tdgbl->ALICE_data.ua_password)); + for (q = tdgbl->ALICE_data.ua_password; *q;) + *d++ = *q++; + } + + dpb_length = d - dpb; + if (dpb_length == 1) + dpb_length = 0; + + trans->tdr_db_handle = NULL; + + gds__attach_database(status_vector, + 0, + GDS_VAL(pathname), + reinterpret_cast < + void **>(GDS_REF(trans->tdr_db_handle)), dpb_length, + reinterpret_cast < char *>(GDS_VAL(dpb))); + + if (status_vector[1]) { +#ifndef GUI_TOOLS + if (tdgbl->ALICE_data.ua_debug) { + ALICE_print(69, 0, 0, 0, 0, 0); /* msg 69: failed */ + ALICE_print_status(status_vector); + } +#endif + return FALSE; + } + + MET_set_capabilities(status_vector, trans); + +#ifndef GUI_TOOLS + if (tdgbl->ALICE_data.ua_debug) + ALICE_print(70, 0, 0, 0, 0, 0); /* msg 70: succeeded */ +#endif + + return TRUE; +} + + + +//____________________________________________________________ +// +// Get the state of the various transactions +// in a multidatabase transaction. +// + +void TDR_get_states(TDR trans) +{ + STATUS status_vector[20]; + TDR ptr; + + for (ptr = trans; ptr; ptr = ptr->tdr_next) + MET_get_state(status_vector, ptr); +} + + + +//____________________________________________________________ +// +// Detach all databases associated with +// a multidatabase transaction. +// + +void TDR_shutdown_databases(TDR trans) +{ + TDR ptr; + STATUS status_vector[20]; + + for (ptr = trans; ptr; ptr = ptr->tdr_next) + gds__detach_database(status_vector, + reinterpret_cast < + void **>(GDS_REF(ptr->tdr_db_handle))); +} + + + +#ifndef GUI_TOOLS +// +// The following routines are only for the command line utility. +// This should really be split into two files... +// + + +//____________________________________________________________ +// +// List transaction stuck in limbo. If the prompt switch is set, +// prompt for commit, rollback, or leave well enough alone. +// + +void TDR_list_limbo(int *handle, TEXT * name, ULONG switches) +{ + UCHAR buffer[1024], *ptr; + STATUS status_vector[20]; + SLONG id; + USHORT item, flag, length; + TDR trans; + TGBL tdgbl; + + tdgbl = GET_THREAD_DATA; + + if (gds__database_info(status_vector, + reinterpret_cast < void **>(GDS_REF(handle)), + sizeof(limbo_info), + reinterpret_cast < char *>(limbo_info), + sizeof(buffer), + reinterpret_cast < char *>(buffer))) { + ALICE_print_status(status_vector); + return; + } + + ptr = buffer; + flag = TRUE; + + while (flag) { + item = *ptr++; + length = (USHORT) gds__vax_integer(ptr, 2); + ptr += 2; + switch (item) { + case gds_info_limbo: + id = gds__vax_integer(ptr, length); + if (switches & + (sw_commit | sw_rollback | sw_two_phase | sw_prompt)) { + TDR_reconnect_multiple(handle, id, name, switches); + ptr += length; + break; + } + if (!tdgbl->sw_service_thd) + ALICE_print(71, reinterpret_cast < char *>(id), 0, 0, 0, 0); /* msg 71: Transaction %d is in limbo. */ + if (trans = MET_get_transaction(status_vector, handle, id)) { +#ifdef SUPERSERVER + SVC_putc(tdgbl->service_blk, (UCHAR) isc_spb_multi_tra_id); + SVC_putc(tdgbl->service_blk, (UCHAR) id); + SVC_putc(tdgbl->service_blk, (UCHAR) (id >> 8)); + SVC_putc(tdgbl->service_blk, (UCHAR) (id >> 16)); + SVC_putc(tdgbl->service_blk, (UCHAR) (id >> 24)); +#endif + reattach_databases(trans); + TDR_get_states(trans); + TDR_shutdown_databases(trans); + print_description(trans); + } +#ifdef SUPERSERVER + else { + SVC_putc(tdgbl->service_blk, (UCHAR) isc_spb_single_tra_id); + SVC_putc(tdgbl->service_blk, (UCHAR) id); + SVC_putc(tdgbl->service_blk, (UCHAR) (id >> 8)); + SVC_putc(tdgbl->service_blk, (UCHAR) (id >> 16)); + SVC_putc(tdgbl->service_blk, (UCHAR) (id >> 24)); + } +#endif + ptr += length; + break; + + case gds_info_truncated: + if (!tdgbl->sw_service_thd) + ALICE_print(72, 0, 0, 0, 0, 0); /* msg 72: More limbo transactions than fit. Try again */ + + case gds_info_end: + flag = FALSE; + break; + + default: + if (!tdgbl->sw_service_thd) + ALICE_print(73, reinterpret_cast < char *>(item), 0, 0, 0, 0); /* msg 73: Unrecognized info item %d */ + } + } +} + + + +//____________________________________________________________ +// +// Check a transaction's TDR to see if it is +// a multi-database transaction. If so, commit +// or rollback according to the user's wishes. +// Object strongly if the transaction is in a +// state that would seem to preclude committing +// or rolling back, but essentially do what the +// user wants. Intelligence is assumed for the +// gfix user. +// + +BOOLEAN TDR_reconnect_multiple(int *handle, + SLONG id, TEXT * name, ULONG switches) +{ + TDR trans, ptr; + STATUS status_vector[20]; + USHORT advice; + BOOLEAN error = FALSE; + +// get the state of all the associated transactions + + if (!(trans = MET_get_transaction(status_vector, handle, id))) + return reconnect(handle, id, name, switches); + + reattach_databases(trans); + TDR_get_states(trans); + +// analyze what to do with them; if the advice contradicts the user's +// desire, make them confirm it; otherwise go with the flow. + + advice = TDR_analyze(trans); + + if (!advice) { + print_description(trans); + switches = ask(); + } + else { + switch (advice) { + case TRA_rollback: + if (switches & sw_commit) { + ALICE_print(74, reinterpret_cast < char *>(trans->tdr_id), 0, + 0, 0, 0); /* msg 74: "A commit of transaction %ld will violate two-phase commit. */ + print_description(trans); + switches = ask(); + } + else if (switches & sw_rollback) + switches |= sw_rollback; + else if (switches & sw_two_phase) + switches |= sw_rollback; + else if (switches & sw_prompt) { + ALICE_print(75, reinterpret_cast < char *>(trans->tdr_id), 0, + 0, 0, 0); /* msg 75: A rollback of transaction %ld is needed to preserve two-phase commit. */ + print_description(trans); + switches = ask(); + } + break; + + case TRA_commit: + if (switches & sw_rollback) { + ALICE_print(76, reinterpret_cast < char *>(trans->tdr_id), 0, + 0, 0, 0); /* msg 76: Transaction %ld has already been partially committed. */ + ALICE_print(77, 0, 0, 0, 0, 0); /* msg 77: A rollback of this transaction will violate two-phase commit. */ + print_description(trans); + switches = ask(); + } + else if (switches & sw_commit) + switches |= sw_commit; + else if (switches & sw_two_phase) + switches |= sw_commit; + else if (switches & sw_prompt) { + ALICE_print(78, reinterpret_cast < char *>(trans->tdr_id), 0, + 0, 0, 0); /* msg 78: Transaction %ld has been partially committed. */ + ALICE_print(79, 0, 0, 0, 0, 0); /* msg 79: A commit is necessary to preserve the two-phase commit. */ + print_description(trans); + switches = ask(); + } + break; + + case TRA_unknown: + ALICE_print(80, 0, 0, 0, 0, 0); /* msg 80: Insufficient information is available to determine */ + ALICE_print(81, reinterpret_cast < char *>(trans->tdr_id), 0, 0, + 0, 0); /* msg 81: a proper action for transaction %ld. */ + print_description(trans); + switches = ask(); + break; + + default: + if (!(switches & (sw_commit | sw_rollback))) { + ALICE_print(82, reinterpret_cast < char *>(trans->tdr_id), 0, + 0, 0, 0); /* msg 82: Transaction %ld: All subtransactions have been prepared. */ + ALICE_print(83, 0, 0, 0, 0, 0); /* msg 83: Either commit or rollback is possible. */ + print_description(trans); + switches = ask(); + } + } + } + + if (switches != (ULONG) - 1) { + /* now do the required operation with all the subtransactions */ + + if (switches & (sw_commit | sw_rollback)) + for (ptr = trans; ptr; ptr = ptr->tdr_next) + if (ptr->tdr_state == TRA_limbo) + reconnect(reinterpret_cast < int *>(ptr->tdr_db_handle), + ptr->tdr_id, ptr->tdr_filename, switches); + } + else { + ALICE_print(84, 0, 0, 0, 0, 0); /* msg 84: unexpected end of input */ + error = TRUE; + } + +// shutdown all the databases for cleanliness' sake + + TDR_shutdown_databases(trans); + + return error; +} + + + +//____________________________________________________________ +// +// format and print description of a transaction in +// limbo, including all associated transactions +// in other databases. +// + +static void print_description(TDR trans) +{ + TDR ptr; + BOOLEAN prepared_seen; + TGBL tdgbl; + int i; + + tdgbl = GET_THREAD_DATA; + + if (!trans) + return; + + if (!tdgbl->sw_service_thd) + ALICE_print(92, 0, 0, 0, 0, 0); /* msg 92: Multidatabase transaction: */ + + prepared_seen = FALSE; + for (ptr = trans; ptr; ptr = ptr->tdr_next) { + if (ptr->tdr_host_site) { +#ifndef SUPERSERVER + ALICE_print(93, + reinterpret_cast < + char *>(ptr->tdr_host_site->str_data), 0, 0, 0, 0); /* msg 93: Host Site: %s */ +#else + SVC_putc(tdgbl->service_blk, (UCHAR) isc_spb_tra_host_site); + SVC_putc(tdgbl->service_blk, + (UCHAR) strlen(reinterpret_cast < + const char *>(ptr-> + tdr_host_site->str_data))); + SVC_putc(tdgbl->service_blk, + (UCHAR) (strlen + (reinterpret_cast < + const char *>(ptr-> + tdr_host_site->str_data)) >> 8)); + for (i = 0; + i < (int) strlen(reinterpret_cast < + const char *>(ptr-> + tdr_host_site->str_data)); + i++); + SVC_putc(tdgbl->service_blk, + (UCHAR) ptr->tdr_host_site->str_data[i]); +#endif + } + + if (ptr->tdr_id) +#ifndef SUPERSERVER + ALICE_print(94, reinterpret_cast < char *>(ptr->tdr_id), 0, 0, 0, 0); /* msg 94: Transaction %ld */ +#else + { + SVC_putc(tdgbl->service_blk, (UCHAR) isc_spb_tra_id); + SVC_putc(tdgbl->service_blk, (UCHAR) ptr->tdr_id); + SVC_putc(tdgbl->service_blk, (UCHAR) (ptr->tdr_id >> 8)); + SVC_putc(tdgbl->service_blk, (UCHAR) (ptr->tdr_id >> 16)); + SVC_putc(tdgbl->service_blk, (UCHAR) (ptr->tdr_id >> 24)); + } +#endif + + switch (ptr->tdr_state) { + case TRA_limbo: +#ifndef SUPERSERVER + ALICE_print(95, 0, 0, 0, 0, 0); /* msg 95: has been prepared. */ +#else + SVC_putc(tdgbl->service_blk, (UCHAR) isc_spb_tra_state); + SVC_putc(tdgbl->service_blk, (UCHAR) isc_spb_tra_state_limbo); +#endif + prepared_seen = TRUE; + break; + + case TRA_commit: +#ifndef SUPERSERVER + ALICE_print(96, 0, 0, 0, 0, 0); /* msg 96: has been committed. */ +#else + SVC_putc(tdgbl->service_blk, (UCHAR) isc_spb_tra_state); + SVC_putc(tdgbl->service_blk, (UCHAR) isc_spb_tra_state_commit); +#endif + break; + + case TRA_rollback: +#ifndef SUPERSERVER + ALICE_print(97, 0, 0, 0, 0, 0); /* msg 97: has been rolled back. */ +#else + SVC_putc(tdgbl->service_blk, (UCHAR) isc_spb_tra_state); + SVC_putc(tdgbl->service_blk, (UCHAR) isc_spb_tra_state_rollback); +#endif + break; + + case TRA_unknown: +#ifndef SUPERSERVER + ALICE_print(98, 0, 0, 0, 0, 0); /* msg 98: is not available. */ +#else + SVC_putc(tdgbl->service_blk, (UCHAR) isc_spb_tra_state); + SVC_putc(tdgbl->service_blk, (UCHAR) isc_spb_tra_state_unknown); +#endif + break; + + default: +#ifndef SUPERSERVER + if (prepared_seen) + ALICE_print(99, 0, 0, 0, 0, 0); /* msg 99: is not found, assumed not prepared. */ + else + ALICE_print(100, 0, 0, 0, 0, 0); /* msg 100: is not found, assumed to be committed. */ +#endif + break; + } + + if (ptr->tdr_remote_site) { +#ifndef SUPERSERVER + ALICE_print(101, + reinterpret_cast < + char *>(ptr->tdr_remote_site->str_data), 0, 0, 0, 0); /*msg 101: Remote Site: %s */ +#else + SVC_putc(tdgbl->service_blk, (UCHAR) isc_spb_tra_remote_site); + SVC_putc(tdgbl->service_blk, + (UCHAR) strlen(reinterpret_cast < + const char *>(ptr-> + tdr_remote_site->str_data))); + SVC_putc(tdgbl->service_blk, + (UCHAR) (strlen + (reinterpret_cast < + const char *>(ptr-> + tdr_remote_site->str_data)) >> + 8)); + for (i = 0; + i < (int) strlen(reinterpret_cast < + const char *>(ptr-> + tdr_remote_site->str_data)); + i++) SVC_putc(tdgbl->service_blk, + (UCHAR) ptr->tdr_remote_site->str_data[i]); +#endif + } + + if (ptr->tdr_fullpath) { +#ifndef SUPERSERVER + ALICE_print(102, + reinterpret_cast < + char *>(ptr->tdr_fullpath->str_data), 0, 0, 0, 0); /* msg 102: Database Path: %s */ +#else + SVC_putc(tdgbl->service_blk, (UCHAR) isc_spb_tra_db_path); + SVC_putc(tdgbl->service_blk, + (UCHAR) strlen(reinterpret_cast < + const char *>(ptr-> + tdr_fullpath->str_data))); + SVC_putc(tdgbl->service_blk, + (UCHAR) (strlen + (reinterpret_cast < + const char *>(ptr-> + tdr_fullpath->str_data)) >> 8)); + for (i = 0; + i < (int) strlen(reinterpret_cast < + const char *>(ptr->tdr_fullpath->str_data)); + i++) SVC_putc(tdgbl->service_blk, + (UCHAR) ptr->tdr_fullpath->str_data[i]); +#endif + } + + } + +// let the user know what the suggested action is + + switch (TDR_analyze(trans)) { + case TRA_commit: +#ifndef SUPERSERVER + ALICE_print(103, 0, 0, 0, 0, 0); /* msg 103: Automated recovery would commit this transaction. */ +#else + SVC_putc(tdgbl->service_blk, (UCHAR) isc_spb_tra_advise); + SVC_putc(tdgbl->service_blk, (UCHAR) isc_spb_tra_advise_commit); +#endif + break; + + case TRA_rollback: +#ifndef SUPERSERVER + ALICE_print(104, 0, 0, 0, 0, 0); /* msg 104: Automated recovery would rollback this transaction. */ +#else + SVC_putc(tdgbl->service_blk, (UCHAR) isc_spb_tra_advise); + SVC_putc(tdgbl->service_blk, (UCHAR) isc_spb_tra_advise_rollback); +#endif + break; + + default: +#ifdef SUPERSERVER + SVC_putc(tdgbl->service_blk, (UCHAR) isc_spb_tra_advise); + SVC_putc(tdgbl->service_blk, (UCHAR) isc_spb_tra_advise_unknown); +#endif + break; + } + +} + + + +//____________________________________________________________ +// +// Ask the user whether to commit or rollback. +// + +static ULONG ask(void) +{ + UCHAR response[32], *p; + int c; + ULONG switches; + TGBL tdgbl; + + tdgbl = GET_THREAD_DATA; + + switches = 0; + + while (TRUE) { + ALICE_print(85, 0, 0, 0, 0, 0); /* msg 85: Commit, rollback, or neither (c, r, or n)? */ + if (tdgbl->sw_service) + ib_putc('\001', ib_stdout); + ib_fflush(ib_stdout); + for (p = response; (c = ib_getchar()) != '\n' && c != EOF;) + *p++ = c; + if (c == EOF && p == response) + return (ULONG) - 1; + *p = 0; + ALICE_down_case(reinterpret_cast < char *>(response), + reinterpret_cast < char *>(response)); + if (!strcmp(reinterpret_cast < const char *>(response), "n") + || !strcmp(reinterpret_cast < const char *>(response), "c") + || !strcmp(reinterpret_cast < const char *>(response), "r")) + break; + } + + if (response[0] == 'c') + switches |= sw_commit; + else if (response[0] == 'r') + switches |= sw_rollback; + + return switches; +} + + +//____________________________________________________________ +// +// Generate pathnames for a given database +// until the database is successfully attached. +// + +static void reattach_database(TDR trans) +{ + STATUS status_vector[20]; + UCHAR buffer[1024], *p, *q, *start; + USHORT protocols = 0; + STR string; + TGBL tdgbl; + + tdgbl = GET_THREAD_DATA; + +// determine what protocols are allowable for this OS + +#ifdef VMS + protocols |= DECNET_PROTOCOL | VMS_TCP_PROTOCOL; +#else + protocols |= TCP_PROTOCOL; +#endif + +#ifdef ultrix + protocols |= DECNET_PROTOCOL; +#endif + + ISC_get_host(reinterpret_cast < char *>(buffer), sizeof(buffer)); + +// if this is being run from the same host, +// (or if this is apollo land), just +// try to reconnect using the same pathname + + if ((protocols & APOLLO_PROTOCOL) || + !strcmp(reinterpret_cast < const char *>(buffer), + reinterpret_cast < + const char *>(trans->tdr_host_site->str_data))) + { + if (TDR_attach_database(status_vector, + trans, + reinterpret_cast(trans->tdr_fullpath->str_data))) + { + return; + } + } + +// try going through the previous host with all available +// protocols, using chaining to try the same method of +// attachment originally used from that host + + else if (trans->tdr_host_site) { + for (p = buffer, q = trans->tdr_host_site->str_data; *q;) + *p++ = *q++; + start = p; + + if (protocols & DECNET_PROTOCOL) { + p = start; + *p++ = ':'; + *p++ = ':'; + for (q = trans->tdr_fullpath->str_data; *q;) + *p++ = *q++; + *q = 0; + if (TDR_attach_database + (status_vector, trans, + reinterpret_cast < char *>(buffer))) return; + } + + if (protocols & VMS_TCP_PROTOCOL) { + p = start; + *p++ = '^'; + for (q = trans->tdr_fullpath->str_data; *q;) + *p++ = *q++; + *q = 0; + if (TDR_attach_database + (status_vector, trans, + reinterpret_cast < char *>(buffer))) return; + } + + if (protocols & TCP_PROTOCOL) { + p = start; + *p++ = ':'; + for (q = trans->tdr_fullpath->str_data; *q;) + *p++ = *q++; + *q = 0; + if (TDR_attach_database + (status_vector, trans, + reinterpret_cast < char *>(buffer))) return; + } + } + +// attaching using the old method didn't work; +// try attaching to the remote node directly + + if (trans->tdr_remote_site) { + for (p = buffer, q = trans->tdr_remote_site->str_data; *q;) + *p++ = *q++; + start = p; + + if (protocols & DECNET_PROTOCOL) { + p = start; + *p++ = ':'; + *p++ = ':'; + for (q = (UCHAR *) trans->tdr_filename; *q;) + *p++ = *q++; + *q = 0; + if (TDR_attach_database + (status_vector, trans, + reinterpret_cast < char *>(buffer))) return; + } + + if (protocols & VMS_TCP_PROTOCOL) { + p = start; + *p++ = '^'; + for (q = (UCHAR *) trans->tdr_filename; *q;) + *p++ = *q++; + *q = 0; + if (TDR_attach_database + (status_vector, trans, + reinterpret_cast < char *>(buffer))) return; + } + + if (protocols & TCP_PROTOCOL) { + p = start; + *p++ = ':'; + for (q = (UCHAR *) trans->tdr_filename; *q;) + *p++ = *q++; + *q = 0; + if (TDR_attach_database + (status_vector, trans, + reinterpret_cast < char *>(buffer))) return; + } + + if (protocols & MSLAN_PROTOCOL) { + p = buffer; + *p++ = '\\'; + *p++ = '\\'; + for (q = trans->tdr_remote_site->str_data; *q;) + *p++ = *q++; + for (q = (UCHAR *) trans->tdr_filename; *q;) + *p++ = *q++; + *q = 0; + + if (TDR_attach_database + (status_vector, trans, + reinterpret_cast < char *>(buffer))) return; + } + } + +// we have failed to reattach; notify the user +// and let them try to succeed where we have failed + + ALICE_print(86, reinterpret_cast < char *>(trans->tdr_id), 0, 0, 0, 0); /* msg 86: Could not reattach to database for transaction %ld. */ + ALICE_print(87, reinterpret_cast < char *>(trans->tdr_fullpath->str_data), + 0, 0, 0, 0); /* msg 87: Original path: %s */ + + for (;;) { + ALICE_print(88, 0, 0, 0, 0, 0); /* msg 88: Enter a valid path: */ + for (p = buffer; (*p = ib_getchar()) != '\n'; p++); + *p = 0; + if (!buffer[0]) + break; + p = buffer; + while (*p == ' ') + *p++; + if (TDR_attach_database + (status_vector, trans, reinterpret_cast < char *>(p))) { + string = + (STR) ALLOCDV(type_str, + strlen(reinterpret_cast < + const char *>(p)) + 1); + strcpy(reinterpret_cast < char *>(string->str_data), + reinterpret_cast < const char *>(p)); + string->str_length = strlen(reinterpret_cast < const char *>(p)); + trans->tdr_fullpath = string; + trans->tdr_filename = (TEXT *) string->str_data; + return; + } + ALICE_print(89, 0, 0, 0, 0, 0); /* msg 89: Attach unsuccessful. */ + } +} + + + +//____________________________________________________________ +// +// Attempt to locate all databases used in +// a multidatabase transaction. +// + +static void reattach_databases(TDR trans) +{ + TDR ptr; + + for (ptr = trans; ptr; ptr = ptr->tdr_next) + reattach_database(ptr); +} + + + +//____________________________________________________________ +// +// Commit or rollback a named transaction. +// + +static BOOLEAN reconnect(int *handle, + SLONG number, TEXT * name, ULONG switches) +{ + int *transaction; + SLONG id; + STATUS status_vector[20]; + + id = gds__vax_integer((UCHAR *) & number, 4); + transaction = NULL; + if (gds__reconnect_transaction(status_vector, + reinterpret_cast < + void **>(GDS_REF(handle)), + reinterpret_cast (GDS_REF(transaction)), + sizeof(id), + reinterpret_cast (GDS_REF(id)))) { + ALICE_print(90, name, 0, 0, 0, 0); /* msg 90: failed to reconnect to a transaction in database %s */ + ALICE_print_status(status_vector); + return TRUE; + } + + if (!(switches & (sw_commit | sw_rollback))) { + ALICE_print(91, reinterpret_cast < char *>(number), 0, 0, 0, 0); /* msg 91: Transaction %ld: */ + switches = ask(); + if (switches == (ULONG) - 1) { + ALICE_print(84, 0, 0, 0, 0, 0); /* msg 84: unexpected end of input */ + return TRUE; + } + } + + if (switches & sw_commit) + gds__commit_transaction(status_vector, + reinterpret_cast (GDS_REF(transaction))); + else if (switches & sw_rollback) + gds__rollback_transaction(status_vector, + reinterpret_cast (GDS_REF(transaction))); + else + return FALSE; + + if (status_vector[1]) { + ALICE_print_status(status_vector); + return TRUE; + } + + return FALSE; +} +#endif /* GUI_TOOLS */ diff --git a/src/alice/tdr_proto.h b/src/alice/tdr_proto.h new file mode 100644 index 0000000000..92c6940439 --- /dev/null +++ b/src/alice/tdr_proto.h @@ -0,0 +1,42 @@ +/* + * PROGRAM: Alice (All Else) Utility + * MODULE: tdr_proto.h + * DESCRIPTION: Prototype header file for tdr.c + * + * 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): ______________________________________. + */ + +#ifndef ALICE_TDR_PROTO_H +#define ALICE_TDR_PROTO_H + +#ifdef __cplusplus +extern "C" { +#endif + +void TDR_list_limbo(int*, TEXT*, ULONG); +BOOLEAN TDR_reconnect_multiple(int*, SLONG, TEXT*, ULONG); +void TDR_shutdown_databases(TDR); +USHORT TDR_analyze(TDR); +BOOLEAN TDR_attach_database(STATUS*, TDR, TEXT*); +void TDR_get_states(TDR); + +#ifdef __cplusplus +}; +#endif + +#endif /* ALICE_TDR_PROTO_H */ diff --git a/src/burp/backu_proto.h b/src/burp/backu_proto.h new file mode 100644 index 0000000000..e90dd350d2 --- /dev/null +++ b/src/burp/backu_proto.h @@ -0,0 +1,37 @@ +/* + * PROGRAM: JRD Backup and Restore Program + * MODULE: backu_proto.h + * DESCRIPTION: Prototype Header file for backup.e + * + * 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): ______________________________________. + */ + +#ifndef _BURP_BACKU_PROTO_H_ +#define _BURP_BACKU_PROTO_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +extern int BACKUP_backup (TEXT *, TEXT *); + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* _BURP_BACKU_PROTO_H_ */ diff --git a/src/burp/backup.e b/src/burp/backup.e new file mode 100644 index 0000000000..f7375dcd2f --- /dev/null +++ b/src/burp/backup.e @@ -0,0 +1,4099 @@ +/* + * PROGRAM: JRD Backup and Restore Program + * MODULE: backup.e + * DESCRIPTION: Backup routine + * + * 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): ______________________________________. + * Toni Martir: Added verbose backup records as BACKUP_VERBOSE_INTERVAL + */ +/* +$Id: backup.e,v 1.1.1.1 2001-05-23 13:26:04 tamlin Exp $ +*/ + +#include "../jrd/ib_stdio.h" +#include +#include +#include "../burp/burp.h" +#include "../jrd/ods.h" +#include "../jrd/align.h" +#include "../jrd/gdsassert.h" +#include "../burp/backu_proto.h" +#include "../burp/burp_proto.h" +#include "../burp/canon_proto.h" +#include "../burp/mvol_proto.h" +#include "../remote/protocol.h" + +/* For netware the follow DB handle is #defined to be a value stored */ +/* in thread data. This is also done for other statics generated by */ +/* GPRE. This is to avoid multiple threading problems with module */ +/* level statics. */ +DATABASE DB = STATIC FILENAME "yachts.lnk" RUNTIME * dbb_file; +#define DB tdgbl->db_handle +#define isc_trans tdgbl->tr_handle +#define isc_status tdgbl->status + +/* +#define DEBUG 1 +*/ + +/* VERBOSE INTERVAL WHEN BACKING RECORDS */ +#define BACKUP_VERBOSE_INTERVAL 20000 + +#define STUFF(byte) *blr++ = (SCHAR) (byte) +#define STUFF_WORD(word) {STUFF (word); STUFF ((word) >> 8);} +#define STUFF_LONG(l) {STUFF_WORD (l); STUFF_WORD ((l) >> 16);} + +#define PUT(c) (--(tdgbl->io_cnt) >= 0 ? *(tdgbl->io_ptr)++ = (UCHAR) (c) : MVOL_write ((UCHAR) (c), &(tdgbl->io_cnt), &(tdgbl->io_ptr))) +#define PUT_BLOCK(p,n) MVOL_write_block (tdgbl, p, n) +#define PUT_ASCIZ(attribute, string) put_asciz ((attribute), (string)) +#define PUT_MESSAGE(attribute, message) put_message ((attribute), (message), sizeof (message)) +#define PUT_NUMERIC(attribute, value) put_numeric ((attribute), (value)) +#define PUT_INT64(attribute, value) put_int64 ((attribute), (value)) +#define PUT_TEXT(attribute, text) put_text ((attribute), (text), sizeof (text)) + + +static void compress(UCHAR *, ULONG); +static int copy(TEXT *, TEXT *, ULONG); +static FLD get_fields(REL); +static SINT64 get_gen_id(TEXT *); +static void get_ranges(FLD); +static void put_array(FLD, REL, ISC_QUAD *); +static void put_asciz(SCHAR, TEXT *); +static void put_blob(FLD, ISC_QUAD *, ULONG); +static int put_blr_blob(SCHAR, ISC_QUAD *); +static void put_data(REL); +static void put_index(REL); +static int put_message(SCHAR, TEXT *, ULONG); +static void put_numeric(SCHAR, SLONG); +static void put_relation(REL); +static int put_source_blob(SCHAR, SCHAR, ISC_QUAD *); +static int put_text(SCHAR, TEXT *, SSHORT); +static void put_trigger(enum trig_t, GDS__QUAD *, GDS__QUAD *, GDS_NAME); +static void set_capabilities(void); +static int symbol_length(TEXT *); +static void write_character_sets(void); +static void write_check_constraints(void); +static void write_collations(void); +static void write_database(TEXT *); +static void write_exceptions(void); +static void write_field_dimensions(void); +static void write_filters(void); +static void write_functions(void); +static void write_function_args(GDS_NAME); +static void write_generators(void); +static void write_sql_roles(void); +static void write_global_fields(void); +static void write_procedures(void); +static void write_procedure_prms(GDS_NAME); +static void write_ref_constraints(void); +static void write_rel_constraints(void); +static void write_relations(void); +static void write_shadow_files(void); +static void write_triggers(void); +static void write_trigger_messages(void); +static void write_types(void); +static void write_user_privileges(void); +static void general_on_error(void); + +#define BCK_security 1 +#define BCK_files 2 +#define BCK_external 4 +#define BCK_idx_inactive 8 +#define BCK_triggers 16 /* Obsolete - 1996-Aug-05 */ +#define BCK_context_name 32 +#define BCK_db_description 64 +#define BCK_ffmptt 128 /* rdb$functions, rdb$filters, rdb$trigger_messages, rdb$user_privileges, rdb$triggers, rdb$types */ +#define BCK_attributes_v3 256 /* attributes in various system relations new to v3 */ +#define BCK_rfr_sys_flag 512 /* system flag is missing from Rdb/VMS V3 RFR relation */ +#define BCK_ods6 1024 /* rdb$field_dimensions and shadow files */ +#define BCK_ods8 2048 /* stored procedures & exceptions & + constraints */ +#define BCK_ods9 4096 /* SQL roles */ +#define BCK_ods10 8192 /* FIELD_PRECISION */ + +#ifdef DEBUG +static UCHAR debug_on = 0; /* able to turn this on in debug mode */ +#endif + + +/* + table used to determine capabilities, checking for specific + fields in system relations +*/ + +typedef struct rfr_tab_t { + CONST TEXT *relation; + CONST TEXT *field; + int bit_mask; +} *RFR_TAB; + +static CONST struct rfr_tab_t rfr_table[] = { + "RDB$INDICES", "RDB$INDEX_INACTIVE", BCK_idx_inactive, +/* Backup of V2 triggers no longer supported 1996-Aug-05 David Schnepper + "RDB$RELATIONS", "RDB$STORE_TRIGGER", BCK_triggers, +*/ + "RDB$RELATIONS", "RDB$EXTERNAL_FILE", BCK_external, + "RDB$SECURITY_CLASSES", "RDB$SECURITY_CLASS", BCK_security, + "RDB$FILES", "RDB$FILE_NAME", BCK_files, + "RDB$VIEW_RELATIONS", "RDB$CONTEXT_NAME", BCK_context_name, + "RDB$DATABASE", "RDB$DESCRIPTION", BCK_db_description, + "RDB$FUNCTIONS", "RDB$FUNCTION_NAME", BCK_ffmptt, + "RDB$FIELDS", "RDB$EXTERNAL_LENGTH", BCK_attributes_v3, + "RDB$RELATION_FIELDS", "RDB$SYSTEM_FLAG", BCK_rfr_sys_flag, + "RDB$FIELD_DIMENSIONS", "RDB$DIMENSION", BCK_ods6, + "RDB$PROCEDURES", "RDB$PROCEDURE_NAME", BCK_ods8, + "RDB$ROLES", "RDB$ROLE_NAME", BCK_ods9, + "RDB$FIELDS", "RDB$FIELD_PRECISION", BCK_ods10, + 0, 0, 0 +}; + +static CONST SCHAR blob_items[] = +{ + isc_info_blob_max_segment, + isc_info_blob_num_segments +}; +static CONST SCHAR blr_items[] = +{ + isc_info_blob_max_segment, + isc_info_blob_total_length +}; +static CONST SCHAR source_items[] = +{ + isc_info_blob_max_segment, + isc_info_blob_total_length, + isc_info_blob_num_segments +}; +static CONST SCHAR db_info_items[] = +{ + isc_info_db_sql_dialect, + isc_info_page_size, + isc_info_sweep_interval, + isc_info_forced_writes, + isc_info_no_reserve, + isc_info_set_page_buffers, + isc_info_db_read_only, + isc_info_end +}; +static CONST SCHAR limbo_tpb[] = +{ + isc_tpb_version1, + isc_tpb_ignore_limbo +}; +static CONST SCHAR limbo_nau_tpb[] = +{ + isc_tpb_version1, + isc_tpb_ignore_limbo, + isc_tpb_no_auto_undo +}; + + + +int BACKUP_backup(TEXT* dbb_file, TEXT* file_name) +{ +/************************************** + * + * B A C K U P _ b a c k u p + * + ************************************** + * + * Functional description + * Backup a database. + * + **************************************/ + REL relation; + STATUS status_vector[ISC_STATUS_LENGTH]; + ULONG l; + TEXT temp[32]; + ULONG cumul_count_kb; + isc_req_handle req_handle1 = NULL; + long req_status[20]; + TGBL tdgbl; + FIL fil; + + tdgbl = GET_THREAD_DATA; + + tdgbl->gbl_database_file_name = dbb_file; + + tdgbl->io_ptr = (UCHAR *) NULL; + tdgbl->io_cnt = 0; + tdgbl->relations = (REL) NULL; + cumul_count_kb = tdgbl->BCK_capabilities = 0; + + isc_trans = NULL; + + BURP_verbose(130, NULL, NULL, NULL, NULL, NULL); + /* msg 130 starting transaction */ + + if (tdgbl->gbl_sw_ignore_limbo) + { + if (isc_start_transaction(status_vector, + GDS_REF(isc_trans), + 1, + GDS_REF(tdgbl->db_handle), + sizeof(limbo_nau_tpb), + limbo_nau_tpb)) + { + isc_start_transaction(status_vector, + GDS_REF(isc_trans), + 1, + GDS_REF(tdgbl->db_handle), + sizeof(limbo_tpb), limbo_tpb); + } + } + else + { + EXEC SQL SET TRANSACTION NO_AUTO_UNDO; + if (isc_status[1]) + EXEC SQL SET TRANSACTION; + } + + if (!isc_trans) + { + EXEC SQL SET TRANSACTION NAME isc_trans NO_AUTO_UNDO; + if (isc_status[1]) + EXEC SQL SET TRANSACTION NAME isc_trans; + } + + + + +/* decide what type of database we've got */ + + set_capabilities(); + +/* Write burp record first with other valuable information */ +/* In case of split operation, write a 'split' header first to all the files */ + + if (tdgbl->action->act_action == ACT_backup_split) + { + for (fil = tdgbl->gbl_sw_files; fil; fil = fil->fil_next) + { + tdgbl->action->act_file = fil; + if (MVOL_split_hdr_write() == FALSE) + { + BURP_error(269, tdgbl->action->act_file->fil_name, 0, 0, 0, + 0); + /* msg 269 can't write a header record to file %s */ + } + } + tdgbl->action->act_file = tdgbl->gbl_sw_files; + } + + MVOL_init_write(dbb_file, file_name, &tdgbl->io_cnt, &tdgbl->io_ptr); + +/* Write database record */ + + write_database(dbb_file); + +/* Write global fields */ + + BURP_verbose(150, NULL, NULL, NULL, NULL, NULL); + /* msg 150 writing global fields */ + write_global_fields(); + + if (tdgbl->BCK_capabilities & BCK_ods6) + { + write_field_dimensions(); + + BURP_verbose(162, NULL, NULL, NULL, NULL, NULL); + /* msg 162 writing shadow files */ + write_shadow_files(); + } + +/* Write relations */ + + BURP_verbose(154, NULL, NULL, NULL, NULL, NULL); + /* msg 154 writing relations */ + + write_relations(); + + if (tdgbl->BCK_capabilities & BCK_ffmptt) + { + /* Write functions */ + BURP_verbose(148, NULL, NULL, NULL, NULL, NULL); + /* msg 148 writing functions */ + write_functions(); + + /* Write types */ + BURP_verbose(161, NULL, NULL, NULL, NULL, NULL); + /* msg 161 writing types */ + write_types(); + + /* Write filters */ + BURP_verbose(146, NULL, NULL, NULL, NULL, NULL); + /* msg 146 writing filters */ + write_filters(); + + /* Write generators */ + + BURP_verbose(164, NULL, NULL, NULL, NULL, NULL); + /* msg 164 writing id generators */ + write_generators(); + } + + if (tdgbl->BCK_capabilities & BCK_ods8) + { + /* Write procedures */ + BURP_verbose(192, NULL, NULL, NULL, NULL, NULL); + /* msg 192 writing stored procedures */ + write_procedures(); + + /* Write exceptions */ + BURP_verbose(197, NULL, NULL, NULL, NULL, NULL); + /* msg 197 writing exceptions */ + write_exceptions(); + + /* Write Character Sets */ + BURP_verbose(msgVerbose_write_charsets, NULL, NULL, NULL, NULL, NULL); + write_character_sets(); + + /* Write Collations */ + BURP_verbose(msgVerbose_write_collations, NULL, NULL, NULL, NULL, + NULL); + write_collations(); + + } + +/* Now go back and write all data */ + + for (relation = tdgbl->relations; relation; relation = relation->rel_next) + { + PUT(rec_relation_data); + put_text(att_relation_name, relation->rel_name, + sizeof(relation->rel_name)); + PUT(att_end); + if (!(relation->rel_flags & REL_view) && + !(relation->rel_flags & REL_external)) + { + put_index(relation); + if (!tdgbl->gbl_sw_meta) + put_data(relation); + } + PUT(rec_relation_end); + } + +/* now for the new triggers in rdb$triggers */ + if (tdgbl->BCK_capabilities & BCK_ffmptt) + { + BURP_verbose(159, NULL, NULL, NULL, NULL, NULL); + /* msg 159 writing triggers */ + write_triggers(); + BURP_verbose(158, NULL, NULL, NULL, NULL, NULL); + /* msg 158 writing trigger messages */ + write_trigger_messages(); + write_user_privileges(); + } + +/* Last, but not least, go back and add any access control lists */ + + if (tdgbl->BCK_capabilities & BCK_security) + FOR (REQUEST_HANDLE req_handle1) + X IN RDB$SECURITY_CLASSES WITH X.RDB$SECURITY_CLASS NOT STARTING "SQL$" + PUT (rec_security_class); + l = PUT_TEXT (att_class_security_class, X.RDB$SECURITY_CLASS); + MISC_terminate (X.RDB$SECURITY_CLASS, temp, l, sizeof(temp)); + BURP_verbose (155, temp, NULL, NULL, NULL, NULL); + /* msg 155 writing security class %s */ + put_blr_blob (att_class_acl, (ISC_QUAD *)&X.RDB$ACL); + put_source_blob (att_class_description2, att_class_description, (ISC_QUAD *)&X.RDB$DESCRIPTION); + PUT (att_end); + END_FOR; + ON_ERROR + general_on_error (); + END_ERROR; + + if (req_handle1) + isc_release_request(req_status, &req_handle1); + + if (tdgbl->BCK_capabilities & BCK_ods8) + { + /* Write relation constraints */ + BURP_verbose(206, NULL, NULL, NULL, NULL, NULL); + /* msg 206 writing relation constraints */ + write_rel_constraints(); + + /* Write referential constraints */ + BURP_verbose(209, NULL, NULL, NULL, NULL, NULL); + /* msg 209 writing referential constraints */ + write_ref_constraints(); + + /* Write check constraints */ + BURP_verbose(210, NULL, NULL, NULL, NULL, NULL); + /* msg 210 writing check constraints */ + write_check_constraints(); + + } + + if (tdgbl->BCK_capabilities & BCK_ods9) + { + /* Write SQL roles */ + BURP_verbose(248, NULL, NULL, NULL, NULL, NULL); + /* msg 248 writing SQL roles */ + write_sql_roles(); + } + +/* Finish up */ + + PUT(rec_end); + MVOL_fini_write(&tdgbl->io_cnt, &tdgbl->io_ptr, &cumul_count_kb); + BURP_verbose(176, (TEXT *) cumul_count_kb, NULL, NULL, NULL, NULL); + /* msg 176 closing file, committing, and finishing. %ld bytes written */ + COMMIT; + ON_ERROR + general_on_error (); + END_ERROR; + + if (isc_trans) + COMMIT isc_trans; + ON_ERROR + general_on_error (); + END_ERROR; + + FINISH; + ON_ERROR + general_on_error (); + END_ERROR; + + return FINI_OK; +} + + +static void compress(UCHAR * data, ULONG length) +{ +/************************************** + * + * c o m p r e s s + * + ************************************** + * + * Functional description + * Write out data in compressed form. + * + **************************************/ + UCHAR *p, *q, *end; + USHORT l, run; + TGBL tdgbl; + + tdgbl = GET_THREAD_DATA; + + p = data; + end = p + length; + + while (p < end) + { + for (q = p + 2; q < end && (q[-2] != q[-1] || q[-1] != q[0]); q++) + ; + if (run = (q < end) ? q - p - 2 : end - p) + { + for (; run > 127; run -= 127) + { + l = 127; + PUT(l); + p = PUT_BLOCK(p, l); + } + if (run) + { + PUT(run); + p = PUT_BLOCK(p, run); + } + } + for (q = p; q < end && *q == *p; q++) + ; + if (run = q - p) + { + for (; run > 127; run -= 127) + { + PUT(-127); + PUT(*p); + } + if (run) + { + PUT(-run); + PUT(*p); + } + p = q; + } + } +} + + +static int copy( TEXT * from, TEXT * to, ULONG length) +{ +/************************************** + * + * c o p y + * + ************************************** + * + * Functional description + * Copy a blank or null terminated string into a null terminated + * string. + * + **************************************/ + TEXT *p; + ULONG l = 0; + +/* find end of string */ + p = from; + while (*p++ && (l < length)) + l++; + + length = MIN(l, length); + +/* skip trailing spaces */ + for (p = from + length - 1, l = 0; *p == ' ' && l < length; p--) + l++; + + l = length - l; + + MOVE_FAST(from, to, l); + *(to + l) = '\0'; + + return l; +} + + +static void general_on_error(void) +{ +/************************************** + * + * g e n e r a l _ o n _ e r r o r + * + ************************************** + * + * Functional description + * Handle any general ON_ERROR clause during backup. + * + **************************************/ + TGBL tdgbl; + + tdgbl = GET_THREAD_DATA; + + BURP_print_status(tdgbl->status); + BURP_abort(); +} + + +static FLD get_fields( REL relation) +{ +/************************************** + * + * g e t _ f i e l d s + * + ************************************** + * + * Functional description + * Get fields for a relation. Test + * capabilities and get system specific + * + **************************************/ + FLD field, fields; + ISC_QUAD *blob_id; + USHORT count; + TGBL tdgbl; + + tdgbl = GET_THREAD_DATA; + + count = 1; + fields = NULL; + +/* if we have all capabilities, use the first request to get the + most performance out of the latest engine; if we don't + have one of the capabilities we must use the second set of + requests--this requires more code but it is well worth it + for the performance benefits, especially remotely--deej */ + + if ((tdgbl->BCK_capabilities & BCK_attributes_v3) && + (tdgbl->BCK_capabilities & BCK_ods8) && + (tdgbl->BCK_capabilities & BCK_rfr_sys_flag) && + (tdgbl->BCK_capabilities & BCK_security)) + { + FOR (REQUEST_HANDLE tdgbl->handles_get_fields_req_handle1) + X IN RDB$RELATION_FIELDS CROSS + Y IN RDB$FIELDS WITH + X.RDB$FIELD_SOURCE = Y.RDB$FIELD_NAME AND + X.RDB$RELATION_NAME EQ relation->rel_name + + field = (FLD) BURP_ALLOC_ZERO (sizeof (struct fld)); + field->fld_number = count++; + field->fld_type = Y.RDB$FIELD_TYPE; + field->fld_sub_type = Y.RDB$FIELD_SUB_TYPE; + field->fld_length = Y.RDB$FIELD_LENGTH; + field->fld_scale = Y.RDB$FIELD_SCALE; + field->fld_id = X.RDB$FIELD_ID; + + if (!X.RDB$DESCRIPTION.NULL) + { + blob_id = &X.RDB$DESCRIPTION; + if (blob_id->gds_quad_low || blob_id->gds_quad_high) + field->fld_description = X.RDB$DESCRIPTION; + } + + if (!X.RDB$QUERY_HEADER.NULL) + { + blob_id = &X.RDB$QUERY_HEADER; + if (blob_id->gds_quad_low || blob_id->gds_quad_high) + field->fld_query_header = X.RDB$QUERY_HEADER; + } + + if (X.RDB$FIELD_POSITION.NULL) + field->fld_flags |= FLD_position_missing; + else + field->fld_position = X.RDB$FIELD_POSITION; + field->fld_view_context = X.RDB$VIEW_CONTEXT; + if (X.RDB$UPDATE_FLAG.NULL) + field->fld_flags |= FLD_update_missing; + else + field->fld_update_flag = X.RDB$UPDATE_FLAG; + + copy (X.RDB$FIELD_NAME, field->fld_name, GDS_NAME_LEN - 1); + copy (X.RDB$FIELD_SOURCE, field->fld_source, GDS_NAME_LEN - 1); + copy (X.RDB$BASE_FIELD, field->fld_base, GDS_NAME_LEN - 1); + copy (X.RDB$QUERY_NAME, field->fld_query_name, GDS_NAME_LEN - 1); + copy (X.RDB$EDIT_STRING, field->fld_edit_string, + sizeof (field->fld_edit_string) - 1); + copy (X.RDB$COMPLEX_NAME, field->fld_complex_name, GDS_NAME_LEN - 1); + + blob_id = &Y.RDB$COMPUTED_BLR; + if (blob_id->gds_quad_low || blob_id->gds_quad_high) + field->fld_flags |= FLD_computed; + field->fld_system_flag = X.RDB$SYSTEM_FLAG; + + copy (X.RDB$SECURITY_CLASS, field->fld_security_class, GDS_NAME_LEN - 1); + + /* use the fld_flags to mark the field as an array and + to differentiate it from other blobs */ + + if (Y.RDB$DIMENSIONS) + { + field->fld_flags |= FLD_array; + field->fld_dimensions = Y.RDB$DIMENSIONS; + if (field->fld_dimensions < 0) + BURP_error_redirect (NULL_PTR, 52, field->fld_name, NULL); + /* msg 52 array dimension for field %s is invalid */ + get_ranges (field); + } + + if (!X.RDB$NULL_FLAG.NULL) + { + field->fld_null_flag = X.RDB$NULL_FLAG; + field->fld_flags |= FLD_null_flag; + } + + if (!X.RDB$DEFAULT_VALUE.NULL) + { + blob_id = &X.RDB$DEFAULT_VALUE; + if (blob_id->gds_quad_low || blob_id->gds_quad_high) + field->fld_default_value = X.RDB$DEFAULT_VALUE; + } + + if (!X.RDB$DEFAULT_SOURCE.NULL) + { + blob_id = &X.RDB$DEFAULT_SOURCE; + if (blob_id->gds_quad_low || blob_id->gds_quad_high) + field->fld_default_source = X.RDB$DEFAULT_SOURCE; + } + + if (!(Y.RDB$CHARACTER_SET_ID.NULL)) + { + field->fld_character_set_id = Y.RDB$CHARACTER_SET_ID; + field->fld_flags |= FLD_charset_flag; + } + + if (!X.RDB$COLLATION_ID.NULL) + { + field->fld_collation_id = X.RDB$COLLATION_ID; + field->fld_flags |= FLD_collate_flag; + } + + field->fld_next = fields; + fields = field; + + END_FOR; + ON_ERROR + general_on_error (); + END_ERROR; + } + else + { + FOR (REQUEST_HANDLE tdgbl->handles_get_fields_req_handle1) + X IN RDB$RELATION_FIELDS CROSS Y IN RDB$FIELDS + WITH X.RDB$FIELD_SOURCE = Y.RDB$FIELD_NAME AND + X.RDB$RELATION_NAME EQ relation->rel_name + field = (FLD) BURP_ALLOC_ZERO (sizeof (struct fld)); + field->fld_number = count++; + field->fld_type = Y.RDB$FIELD_TYPE; + field->fld_sub_type = Y.RDB$FIELD_SUB_TYPE; + field->fld_length = Y.RDB$FIELD_LENGTH; + field->fld_scale = Y.RDB$FIELD_SCALE; + field->fld_id = X.RDB$FIELD_ID; + if (!X.RDB$DESCRIPTION.NULL) + { + blob_id = &X.RDB$DESCRIPTION; + if (blob_id->gds_quad_low || blob_id->gds_quad_high) + field->fld_description = X.RDB$DESCRIPTION; + } + if (!X.RDB$QUERY_HEADER.NULL) + { + blob_id = &X.RDB$QUERY_HEADER; + if (blob_id->gds_quad_low || blob_id->gds_quad_high) + field->fld_query_header = X.RDB$QUERY_HEADER; + } + if (X.RDB$FIELD_POSITION.NULL) + field->fld_flags |= FLD_position_missing; + else + field->fld_position = X.RDB$FIELD_POSITION; + field->fld_view_context = X.RDB$VIEW_CONTEXT; + if (X.RDB$UPDATE_FLAG.NULL) + field->fld_flags |= FLD_update_missing; + else + field->fld_update_flag = X.RDB$UPDATE_FLAG; + copy (X.RDB$FIELD_NAME, field->fld_name, GDS_NAME_LEN - 1); + copy (X.RDB$FIELD_SOURCE, field->fld_source, GDS_NAME_LEN - 1); + copy (X.RDB$BASE_FIELD, field->fld_base, GDS_NAME_LEN - 1); + copy (X.RDB$QUERY_NAME, field->fld_query_name, GDS_NAME_LEN - 1); + copy (X.RDB$EDIT_STRING, field->fld_edit_string, + sizeof (field->fld_edit_string) - 1); + if (tdgbl->BCK_capabilities & BCK_attributes_v3) + FOR (REQUEST_HANDLE tdgbl->handles_get_fields_req_handle2) + RFR IN RDB$RELATION_FIELDS WITH RFR.RDB$FIELD_NAME = X.RDB$FIELD_NAME AND + RFR.RDB$RELATION_NAME = X.RDB$RELATION_NAME + copy (RFR.RDB$COMPLEX_NAME, field->fld_complex_name, GDS_NAME_LEN - 1); + END_FOR; + ON_ERROR + general_on_error (); + END_ERROR; + blob_id = &Y.RDB$COMPUTED_BLR; + if (blob_id->gds_quad_low || blob_id->gds_quad_high) + field->fld_flags |= FLD_computed; + if (tdgbl->BCK_capabilities & BCK_rfr_sys_flag) + FOR (REQUEST_HANDLE tdgbl->handles_get_fields_req_handle3) + RFR IN RDB$RELATION_FIELDS WITH + RFR.RDB$RELATION_NAME = relation->rel_name + AND RFR.RDB$FIELD_NAME = X.RDB$FIELD_NAME + field->fld_system_flag = RFR.RDB$SYSTEM_FLAG; + END_FOR; + ON_ERROR + general_on_error (); + END_ERROR; + if (tdgbl->BCK_capabilities & BCK_security) + FOR (REQUEST_HANDLE tdgbl->handles_get_fields_req_handle4) + RFR IN RDB$RELATION_FIELDS WITH + RFR.RDB$RELATION_NAME = relation->rel_name + AND RFR.RDB$FIELD_NAME = X.RDB$FIELD_NAME + copy (RFR.RDB$SECURITY_CLASS, field->fld_security_class, GDS_NAME_LEN - 1); + END_FOR; + ON_ERROR + general_on_error (); + END_ERROR; + if (tdgbl->BCK_capabilities & BCK_attributes_v3) + FOR (REQUEST_HANDLE tdgbl->handles_get_fields_req_handle5) + RF IN RDB$FIELDS WITH RF.RDB$FIELD_NAME = X.RDB$FIELD_SOURCE + /* use the fld_flags to mark the field as an array and + to differentiate it from other blobs */ + if (RF.RDB$DIMENSIONS) + { + field->fld_flags |= FLD_array; + field->fld_dimensions = RF.RDB$DIMENSIONS; + if (field->fld_dimensions < 0) + BURP_error_redirect (NULL_PTR, 52, field->fld_name, NULL); + /* msg 52 array dimension for field %s is invalid */ + get_ranges (field); + } + END_FOR; + ON_ERROR + general_on_error (); + END_ERROR; + if (tdgbl->BCK_capabilities & BCK_ods8) + FOR (REQUEST_HANDLE tdgbl->handles_get_fields_req_handle6) + X2 IN RDB$RELATION_FIELDS CROSS F2 IN RDB$FIELDS + WITH X2.RDB$FIELD_NAME = X.RDB$FIELD_NAME + AND X2.RDB$RELATION_NAME EQ relation->rel_name + AND X2.RDB$FIELD_SOURCE EQ F2.RDB$FIELD_NAME + + if (!X2.RDB$NULL_FLAG.NULL) + { + field->fld_null_flag = X2.RDB$NULL_FLAG; + field->fld_flags |= FLD_null_flag; + } + if (!X2.RDB$DEFAULT_VALUE.NULL) + { + blob_id = &X2.RDB$DEFAULT_VALUE; + if (blob_id->gds_quad_low || blob_id->gds_quad_high) + field->fld_default_value = X2.RDB$DEFAULT_VALUE; + } + if (!X2.RDB$DEFAULT_SOURCE.NULL) + { + blob_id = &X2.RDB$DEFAULT_SOURCE; + if (blob_id->gds_quad_low || blob_id->gds_quad_high) + field->fld_default_source = X2.RDB$DEFAULT_SOURCE; + } + if (!(F2.RDB$CHARACTER_SET_ID.NULL)) + { + field->fld_character_set_id = F2.RDB$CHARACTER_SET_ID; + field->fld_flags |= FLD_charset_flag; + } + if (!X2.RDB$COLLATION_ID.NULL) + { + field->fld_collation_id = X2.RDB$COLLATION_ID; + field->fld_flags |= FLD_collate_flag; + } + END_FOR; + ON_ERROR + general_on_error (); + END_ERROR; + + field->fld_next = fields; + fields = field; + + END_FOR; + ON_ERROR + general_on_error (); + END_ERROR; + } + + return fields; +} + + +static SINT64 get_gen_id( TEXT * name) +{ +/************************************** + * + * g e t _ g e n _ i d + * + ************************************** + * + * Functional description + * Read id for a generator; + * + **************************************/ + UCHAR c, *blr, blr_buffer[100]; /* enough to fit blr */ + SLONG *gen_id_reqh, read_msg0; + SINT64 read_msg1; + SSHORT blr_length, name_len; + STATUS status_vector[ISC_STATUS_LENGTH]; + TGBL tdgbl; + + tdgbl = GET_THREAD_DATA; + + gen_id_reqh = NULL; + blr = blr_buffer; + name_len = symbol_length(name); + +/* If this is ODS 10 (IB version 6.0) or greater, build BLR to retrieve + the 64-bit value of the generator. If not, build BLR to retrieve the + 32-bit value, which we will cast to the expected INT64 format. +*/ + if (tdgbl->BCK_capabilities & BCK_ods10) + { + /* build the blr with the right relation name and 64-bit results. */ + STUFF(blr_version5); + STUFF(blr_begin); + STUFF(blr_message); + STUFF(0); + STUFF_WORD(1); + STUFF(blr_int64); + STUFF(0); + STUFF(blr_send); + STUFF(0); + STUFF(blr_assignment); + STUFF(blr_gen_id); + STUFF(name_len); + while (name_len--) + { + c = *name++; + STUFF(c); + } + STUFF(blr_literal); + STUFF(blr_long); + STUFF(0); + STUFF_WORD(0); + STUFF_WORD(0); + STUFF(blr_parameter); + STUFF(0); + STUFF_WORD(0); + STUFF(blr_end); + STUFF(blr_eoc); + } + else + { + /* build the blr with the right relation name and 32-bit results */ + STUFF(blr_version4); + STUFF(blr_begin); + STUFF(blr_message); + STUFF(0); + STUFF_WORD(1); + STUFF(blr_long); + STUFF(0); + STUFF(blr_send); + STUFF(0); + STUFF(blr_assignment); + STUFF(blr_gen_id); + STUFF(name_len); + while (name_len--) + { + c = *name++; + STUFF(c); + } + STUFF(blr_literal); + STUFF(blr_long); + STUFF(0); + STUFF_WORD(0); + STUFF_WORD(0); + STUFF(blr_parameter); + STUFF(0); + STUFF_WORD(0); + STUFF(blr_end); + STUFF(blr_eoc); + } + +#ifdef DEBUG + if (debug_on) + isc_print_blr(blr_buffer, NULL_PTR, NULL_PTR, 0); +#endif + + blr_length = blr - blr_buffer; + + if (isc_compile_request(status_vector, + GDS_REF(tdgbl->db_handle), + GDS_REF(gen_id_reqh), + blr_length, GDS_VAL(blr_buffer))) + /* if there's no gen_id, never mind ... */ + return 0; + + if (isc_start_request(status_vector, GDS_REF(gen_id_reqh), GDS_REF(isc_trans), /* use the same one generated by gpre */ + 0)) + BURP_error_redirect(status_vector, 25, NULL, NULL); + /* msg 25 Failed in put_blr_gen_id */ + + if (tdgbl->BCK_capabilities & BCK_ods10) + { + if (isc_receive(status_vector, + GDS_REF(gen_id_reqh), + 0, + sizeof(read_msg1), + GDS_REF(read_msg1), + 0)) + BURP_error_redirect(status_vector, 25, NULL, NULL); + /* msg 25 Failed in put_blr_gen_id */ + } + else + { + if (isc_receive(status_vector, + GDS_REF(gen_id_reqh), + 0, + sizeof(read_msg0), + GDS_REF(read_msg0), + 0)) + BURP_error_redirect(status_vector, 25, NULL, NULL); + /* msg 25 Failed in put_blr_gen_id */ + read_msg1 = (SINT64) read_msg0; + } + + isc_release_request(status_vector, GDS_REF(gen_id_reqh)); + + return read_msg1; +} + + +static void get_ranges( FLD field) +{ +/************************************** + * + * g e t _ r a n g e s + * + ************************************** + * + * Functional description + * Fill in the range low and high bounds by reading + * the ranges in rdb$field_dimensions. + * + **************************************/ + SLONG *rp; + USHORT count; + TGBL tdgbl; + + tdgbl = GET_THREAD_DATA; + + rp = field->fld_ranges; + count = 0; + +/* Get the array dimensions in the rdb$field_dimensions */ + + FOR (REQUEST_HANDLE tdgbl->handles_get_ranges_req_handle1) + X IN RDB$FIELD_DIMENSIONS + WITH X.RDB$FIELD_NAME EQ field->fld_source + SORTED BY X.RDB$DIMENSION + + if (count != X.RDB$DIMENSION) + BURP_error_redirect (NULL_PTR, 52, field->fld_name, NULL); + /* msg 52 array dimension for field %s is invalid */ + *rp++ = X.RDB$LOWER_BOUND; + *rp++ = X.RDB$UPPER_BOUND; + count++; + + END_FOR; + ON_ERROR + general_on_error (); + END_ERROR; + + if (count != field->fld_dimensions) + BURP_error_redirect(NULL_PTR, 52, field->fld_name, NULL); + /* msg 52 array dimension for field %s is invalid */ +} + + +static void put_array( FLD field, REL relation, ISC_QUAD * blob_id) +{ +/************************************** + * + * p u t _ a r r a y + * + ************************************** + * + * Functional description + * Write out an array. If, however, it's null, don't even bother. + * + **************************************/ + STATUS status_vector[ISC_STATUS_LENGTH]; + SLONG *range, *end_ranges, returned_elements; + ULONG return_length, slice_length; + SLONG *returned_range, range_buffer[16]; /* enough for 16 dimensions */ + LSTRING xdr_buffer, xdr_slice; + UCHAR blr_buffer[200]; /* enough for a sdl with 16 dimensions */ + UCHAR *blr, *slice, *p; + USHORT blr_length, count, field_length; + TGBL tdgbl; + + tdgbl = GET_THREAD_DATA; + +/* If the array is null, don't store it. It will be restored as null. */ + + if (!blob_id->isc_quad_low && !blob_id->isc_quad_high) + return; + + xdr_buffer.lstr_allocated = 0; + + blr = blr_buffer; + end_ranges = field->fld_ranges + 2 * field->fld_dimensions; + + field_length = field->fld_length; + if (tdgbl->gbl_sw_transportable) + xdr_buffer.lstr_length = field_length + 3; + +/* build the sdl */ + + STUFF(isc_sdl_version1); + + STUFF(isc_sdl_struct); + STUFF(1); + + STUFF(field->fld_type); + + if (field->fld_type == blr_short || + field->fld_type == blr_long || field->fld_type == blr_quad) + STUFF(field->fld_scale); + + if (field->fld_type == blr_text || field->fld_type == blr_varying) + STUFF_WORD(field->fld_length); + + if (field->fld_type == blr_varying) + field_length += sizeof(USHORT); + + STUFF(isc_sdl_rid); + STUFF_WORD(relation->rel_id); + STUFF(isc_sdl_fid); + STUFF_WORD(field->fld_id); + + for (range = field->fld_ranges, count = 0; range < end_ranges; + range += 2, count++) + { + STUFF(isc_sdl_do2); + STUFF(count); + STUFF(isc_sdl_long_integer); + STUFF_LONG(range[0]); + STUFF(isc_sdl_long_integer); + STUFF_LONG(range[1]); + } + + STUFF(isc_sdl_element); + STUFF(1); + STUFF(isc_sdl_scalar); + STUFF(0); + STUFF(field->fld_dimensions); + + for (count = 0; count < field->fld_dimensions; count++) + { + STUFF(isc_sdl_variable); + STUFF(count); + } + + STUFF(isc_sdl_eoc); + +#ifdef DEBUG + if (debug_on) + PRETTY_print_sdl(blr_buffer, NULL_PTR, NULL_PTR, NULL_PTR); +#endif + + blr_length = blr - blr_buffer; + +/* compute the range size for each dimension = high_range - low_range */ + + slice_length = field_length; + for (range = field->fld_ranges; range < end_ranges; range += 2) + { + slice_length *= (range[1] - range[0] + 1); + if (tdgbl->gbl_sw_transportable) + xdr_buffer.lstr_length *= (range[1] - range[0] + 1); + } + slice = BURP_ALLOC(slice_length); + +/* allocate space for the XDR representation */ + + if (tdgbl->gbl_sw_transportable) + { + xdr_buffer.lstr_address = BURP_ALLOC(xdr_buffer.lstr_length); + xdr_buffer.lstr_allocated = xdr_buffer.lstr_length; + } + + if (isc_get_slice(status_vector, + GDS_REF(tdgbl->db_handle), GDS_REF(isc_trans), GDS_VAL(blob_id), blr_length, blr_buffer, 0, /* param length for subset of an array handling */ + (ULONG *) 0, /* param for subset of an array handling */ + slice_length, GDS_VAL(slice), GDS_REF(return_length))) + { + BURP_print(81, field->fld_name, NULL, NULL, NULL, NULL); + /* msg 81 error accessing blob field %s -- continuing */ + BURP_print_status(status_vector); +#ifdef DEBUG + PRETTY_print_sdl(blr_buffer, NULL_PTR, NULL_PTR, NULL_PTR); +#endif + return; + } + + if (return_length != slice_length) + { + int divisor, i1, i2, i3; + /* Ugh. The full array wasn't returned. We must recompute the top + element to backup. */ + + returned_elements = (return_length / field_length) - 1; + + returned_range = range_buffer; + for (i1 = 0, i3 = 0, range = end_ranges - 2; + range >= field->fld_ranges; range -= 2, returned_range++, i1++) + { + divisor = 1; + for (i2 = (2 * (i1 + 1) + 1); i2 <= field->fld_dimensions * 2; + i2 += 2) + divisor *= + (field->fld_ranges[i2] - field->fld_ranges[i2 - 1] + 1); + *returned_range = + (returned_elements - 1) / divisor + field->fld_ranges[i3]; + returned_elements -= + (*returned_range - field->fld_ranges[i3]) * divisor; + i3 += 2; + } + } + + PUT(rec_array); + PUT_NUMERIC(att_blob_field_number, field->fld_number); + PUT_NUMERIC(att_array_dimensions, field->fld_dimensions); + + returned_range = range_buffer; + for (range = field->fld_ranges; range < end_ranges; + range += 2, returned_range++) + { + PUT_NUMERIC(att_array_range_low, (int) range[0]); + if (return_length == slice_length) + PUT_NUMERIC(att_array_range_high, (int) range[1]); + else + PUT_NUMERIC(att_array_range_high, (int) *returned_range); + } + + PUT(att_blob_data); + PUT(return_length); + PUT(return_length >> 8); + PUT(return_length >> 16); + PUT(return_length >> 24); + + if (return_length) + { + if (tdgbl->gbl_sw_transportable) + { + xdr_slice.lstr_allocated = xdr_slice.lstr_length = return_length; + xdr_slice.lstr_address = slice; + return_length = + CAN_slice(&xdr_buffer, &xdr_slice, TRUE, blr_length, + blr_buffer); + PUT(att_xdr_array); + PUT(return_length); + PUT(return_length >> 8); + PUT(return_length >> 16); + PUT(return_length >> 24); + p = xdr_buffer.lstr_address; + } + else + p = slice; + (void) PUT_BLOCK(p, return_length); + } + + BURP_FREE(slice); + if (xdr_buffer.lstr_allocated) + BURP_FREE(xdr_buffer.lstr_address); +} + + +static void put_asciz( SCHAR attribute, TEXT * string) +{ +/************************************** + * + * p u t _ a s c i z + * + ************************************** + * + * Functional description + * Write an attribute starting with a null terminated string. + * + **************************************/ + ULONG l; + TGBL tdgbl; + + tdgbl = GET_THREAD_DATA; + + l = strlen(string); + + PUT(attribute); + PUT(l); + if (l) + (void) PUT_BLOCK(string, l); +} + + +static void put_blob( FLD field, ISC_QUAD * blob_id, ULONG count) +{ +/************************************** + * + * p u t _ b l o b + * + ************************************** + * + * Functional description + * Write out a blob. If, however, it's null, don't even bother. + * + **************************************/ + STATUS status_vector[ISC_STATUS_LENGTH]; + void *blob; + ULONG segments; + UCHAR *p, blob_info[32], item, *buffer, static_buffer[1024]; + USHORT l, max_segment, n; + TGBL tdgbl; + + tdgbl = GET_THREAD_DATA; + +/* If the blob is null, don't store it. It will be restored as null. */ + + if (!blob_id->isc_quad_high && !blob_id->isc_quad_low) + return; + +/* Open the blob and get it's vital statistics */ + + blob = NULL; + + if (isc_open_blob(status_vector, + GDS_REF(tdgbl->db_handle), + GDS_REF(isc_trans), + GDS_REF(blob), + GDS_VAL(blob_id))) + { + BURP_print(81, field->fld_name, NULL, NULL, NULL, NULL); + /* msg 81 error accessing blob field %s -- continuing */ + BURP_print_status(status_vector); + return; + } + + if (isc_blob_info(status_vector, + GDS_REF(blob), + sizeof(blob_items), + (SCHAR *) blob_items, + sizeof(blob_info), + blob_info)) + { + BURP_error_redirect(status_vector, 20, NULL, NULL); + /* msg 20 isc_blob_info failed */ + } + + PUT(rec_blob); + PUT_NUMERIC(att_blob_field_number, field->fld_number); + + segments = max_segment = 0; + p = blob_info; + + while ((item = *p++) != isc_info_end) + { + l = (USHORT) isc_vax_integer(p, 2); + p += 2; + n = (USHORT) isc_vax_integer(p, l); + p += l; + switch (item) + { + case isc_info_blob_max_segment: + PUT_NUMERIC(att_blob_max_segment, (int) n); + max_segment = n; + break; + + case isc_info_blob_type: + PUT_NUMERIC(att_blob_type, (int) n); + break; + + case isc_info_blob_num_segments: + PUT_NUMERIC(att_blob_number_segments, (int) n); + segments = n; + break; + + default: + BURP_error_redirect(NULL_PTR, 21, (void *) item, NULL); + /* msg 21 don't understand blob info item %ld */ + } + } + +/* Allocate a buffer large enough for the largest segment and start grinding. */ + + if (!max_segment || max_segment <= sizeof(static_buffer)) + buffer = static_buffer; + else + buffer = BURP_ALLOC(max_segment); + + PUT(att_blob_data); + + while (segments > 0) + { + if (isc_get_segment(status_vector, + GDS_REF(blob), + GDS_REF(l), + max_segment, + GDS_VAL(buffer))) + { + BURP_error_redirect(status_vector, 22, NULL, NULL); + } + /* msg 22 gds__get_segment failed */ + + PUT(l); + PUT(l >> 8); + if (l) + { + (void) PUT_BLOCK(buffer, l); + } + --segments; + } + + if (isc_close_blob(status_vector, GDS_REF(blob))) + BURP_error_redirect(status_vector, 23, NULL, NULL); + /* msg 23 isc_close_blob failed */ + + if (buffer != static_buffer) + BURP_FREE(buffer); +} + + +static int put_blr_blob( SCHAR attribute, ISC_QUAD * blob_id) +{ +/************************************** + * + * p u t _ b l r _ b l o b + * + ************************************** + * + * Functional description + * Write out a blr blob, if present. Otherwise do nothing. + * Return TRUE is there was the blob was present, FALSE otherwise. + * + **************************************/ + STATUS status_vector[ISC_STATUS_LENGTH]; + ULONG length, n; + void *blob; + UCHAR *p, blob_info[32], item, *buffer, static_buffer[1024]; + ULONG max_segment; + USHORT l; + TGBL tdgbl; + + tdgbl = GET_THREAD_DATA; + +/* If the blob is null, don't store it. It will be restored as null. */ + + if (!blob_id->isc_quad_high && !blob_id->isc_quad_low) + return FALSE; + +/* Open the blob and get it's vital statistics */ + + blob = NULL; + + if (isc_open_blob(status_vector, + GDS_REF(tdgbl->db_handle), + GDS_REF(isc_trans), + GDS_REF(blob), + GDS_VAL(blob_id))) + { + BURP_error_redirect(status_vector, 24, NULL, NULL); + /* msg 24 isc_open_blob failed */ + } + + if (isc_blob_info(status_vector, + GDS_REF(blob), + sizeof(blr_items), + (SCHAR *) blr_items, + sizeof(blob_info), + blob_info)) + { + BURP_error_redirect(status_vector, 20, NULL, NULL); + /* msg 20 isc_blob_info failed */ + } + + length = 0; + p = blob_info; + + while ((item = *p++) != isc_info_end) + { + l = (USHORT) isc_vax_integer(p, 2); + p += 2; + n = (USHORT) isc_vax_integer(p, l); + p += l; + switch (item) + { + case isc_info_blob_max_segment: + max_segment = n; + break; + + case isc_info_blob_total_length: + length = n; + break; + + default: + BURP_print(79, (void *) item, NULL, NULL, NULL, NULL); + /* msg 79 don't understand blob info item %ld */ + return FALSE; + } + } + + if (!length) + { + if (isc_close_blob(status_vector, GDS_REF(blob))) + BURP_error_redirect(status_vector, 23, NULL, NULL); + /* msg 23 isc_close_blob failed */ + return FALSE; + } + +/* Rdb sometimes gets the length messed up */ + + if (length < max_segment) + length = max_segment; + + PUT_NUMERIC(attribute, (int) length); + +/* Allocate a buffer large enough for the largest segment and start grinding. */ + + if (!max_segment || max_segment <= sizeof(static_buffer)) + buffer = static_buffer; + else + buffer = BURP_ALLOC(max_segment); + + while (!isc_get_segment(status_vector, + GDS_REF(blob), + GDS_REF(l), + (USHORT) max_segment, GDS_VAL(buffer))) + { + if (l) + { + (void) PUT_BLOCK(buffer, l); + } + } + + if (isc_close_blob(status_vector, GDS_REF(blob))) + { + BURP_error_redirect(status_vector, 23, NULL, NULL); + } + /* msg 23 isc_close_blob failed */ + + if (buffer != static_buffer) + BURP_FREE(buffer); + + return TRUE; +} + + +static void put_data(REL relation) +{ +/************************************** + * + * p u t _ d a t a + * + ************************************** + * + * Functional description + * Write relation meta-data and data. + * + **************************************/ + FLD field; + int *request, records; + UCHAR *p, *blr, *blr_buffer, *buffer; + LSTRING xdr_buffer; + STATUS status_vector[ISC_STATUS_LENGTH]; + RCRD_OFFSET offset, eof_offset, record_length; + FLD_LENGTH length; + SSHORT alignment, blr_length, count, dtype, *eof, eof_parameter; + TGBL tdgbl; + + tdgbl = GET_THREAD_DATA; + + count = 1; + + for (field = relation->rel_fields; field; field = field->fld_next) + { + if (!(field->fld_flags & FLD_computed)) + { + count += 2; + } + } + +/* Time to generate blr to fetch data. Make sure we allocate a BLR buffer + large enough to handle the per field overhead */ + + blr = blr_buffer = BURP_ALLOC(200 + count * 9); + STUFF(blr_version4); + STUFF(blr_begin); + STUFF(blr_message); + STUFF(0); /* Message number */ + STUFF_WORD(count); /* Number of fields, counting eof */ + + offset = count = 0; + + for (field = relation->rel_fields; field; field = field->fld_next) + { + if (field->fld_flags & FLD_computed) + continue; + alignment = 4; + length = field->fld_length; + dtype = field->fld_type; + if (field->fld_flags & FLD_array) + { + dtype = blr_blob; + length = 8; + } + switch (dtype) + { + case blr_text: + alignment = type_alignments[dtype_text]; + STUFF(field->fld_type); + STUFF_WORD(field->fld_length); + break; + + case blr_varying: + alignment = type_alignments[dtype_varying]; + STUFF(field->fld_type); + STUFF_WORD(field->fld_length); + length += sizeof(USHORT); + break; + + case blr_short: + alignment = type_alignments[dtype_short]; + STUFF(field->fld_type); + STUFF(field->fld_scale); + break; + + case blr_long: + alignment = type_alignments[dtype_long]; + STUFF(field->fld_type); + STUFF(field->fld_scale); + break; + + case blr_quad: + alignment = type_alignments[dtype_quad]; + STUFF(field->fld_type); + STUFF(field->fld_scale); + break; + + case blr_int64: + alignment = type_alignments[dtype_int64]; + STUFF(field->fld_type); + STUFF(field->fld_scale); + break; + + case blr_double: + alignment = type_alignments[dtype_double]; + STUFF(field->fld_type); + break; + + case blr_timestamp: + alignment = type_alignments[dtype_timestamp]; + STUFF(field->fld_type); + break; + + case blr_sql_time: + alignment = type_alignments[dtype_sql_time]; + STUFF(field->fld_type); + break; + + case blr_sql_date: + alignment = type_alignments[dtype_sql_date]; + STUFF(field->fld_type); + break; + + case blr_float: + alignment = type_alignments[dtype_real]; + STUFF(field->fld_type); + break; + + case blr_blob: + alignment = type_alignments[dtype_blob]; + STUFF(blr_quad); + STUFF(0); + break; + + default: + BURP_error_redirect(NULL_PTR, 26, (void *) field->fld_type, NULL); + /* msg 26 datatype %ld not understood */ + break; + } + if (alignment) + offset = FB_ALIGN(offset, alignment); + field->fld_offset = offset; + field->fld_parameter = count++; + offset += length; + } + +/* Next, build fields for null flags */ + + for (field = relation->rel_fields; field; field = field->fld_next) + { + if (field->fld_flags & FLD_computed) + continue; + STUFF(blr_short); + STUFF(0); + offset = FB_ALIGN(offset, sizeof(SSHORT)); + field->fld_missing_parameter = count++; + offset += sizeof(SSHORT); + } + +/* Finally, make up an EOF field */ + + STUFF(blr_short); /* eof field */ + STUFF(0); /* scale for eof field */ + record_length = offset; + eof_parameter = count++; + eof_offset = FB_ALIGN(offset, sizeof(SSHORT)); + length = (USHORT) (eof_offset + sizeof(SSHORT)); + +/* Build FOR loop, body, and eof handler */ + + STUFF(blr_for); + STUFF(blr_rse); + STUFF(1); /* count of relations */ + STUFF(blr_rid); + STUFF_WORD(relation->rel_id); + STUFF(0); /* context variable */ + STUFF(blr_end); + + STUFF(blr_send); + STUFF(0); + STUFF(blr_begin); + STUFF(blr_assignment); + STUFF(blr_literal); + STUFF(blr_short); + STUFF(0); + STUFF_WORD(1); + STUFF(blr_parameter); + STUFF(0); + STUFF_WORD(eof_parameter); + + for (field = relation->rel_fields; field; field = field->fld_next) + { + if (field->fld_flags & FLD_computed) + continue; + STUFF(blr_assignment); + STUFF(blr_fid); + STUFF(0); + STUFF_WORD(field->fld_id); + STUFF(blr_parameter2); + STUFF(0); + STUFF_WORD(field->fld_parameter); + STUFF_WORD(field->fld_missing_parameter); + } + + STUFF(blr_end); + + STUFF(blr_send); + STUFF(0); + STUFF(blr_assignment); + STUFF(blr_literal); + STUFF(blr_short); + STUFF(0); + STUFF_WORD(0); + STUFF(blr_parameter); + STUFF(0); + STUFF_WORD(eof_parameter); + + STUFF(blr_end); + STUFF(blr_eoc); + +#ifdef DEBUG + if (debug_on) + isc_print_blr(blr_buffer, NULL_PTR, NULL_PTR, 0); +#endif + + +/* Compile request */ + + request = NULL; + blr_length = blr - blr_buffer; + if (isc_compile_request(status_vector, + GDS_REF(tdgbl->db_handle), + GDS_REF(request), + blr_length, GDS_VAL(blr_buffer))) + { + BURP_error_redirect(status_vector, 27, NULL, NULL); + /* msg 27 isc_compile_request failed */ + isc_print_blr(blr_buffer, NULL_PTR, NULL_PTR, 0); + } + + BURP_FREE(blr_buffer); + records = 0; + BURP_verbose(142, relation->rel_name, NULL, NULL, NULL, NULL); + /* msg 142 writing data for relation %s */ + + if (isc_start_request(status_vector, + GDS_REF(request), + GDS_REF(isc_trans), + 0)) + { + BURP_error_redirect(status_vector, 28, NULL, NULL); + /* msg 28 isc_start_request failed */ + } + +/* Here is the crux of the problem -- writing data. All this work + for the following small loop. */ + + buffer = BURP_ALLOC(length); + eof = (SSHORT *) (buffer + eof_offset); + +/* the XDR representation may be even fluffier */ + if (tdgbl->gbl_sw_transportable) + { + xdr_buffer.lstr_length = xdr_buffer.lstr_allocated = + length + count * 3; + xdr_buffer.lstr_address = BURP_ALLOC(xdr_buffer.lstr_length); + } + else + xdr_buffer.lstr_address = NULL; + + while (TRUE) + { + if (isc_receive(status_vector, + GDS_REF(request), + 0, + length, + GDS_VAL(buffer), + 0)) + { + BURP_error_redirect(status_vector, 29, NULL, NULL); + /* msg 29 isc_receive failed */ + } + if (!*eof) + break; + records++; + /* Verbose records */ + if ((records % BACKUP_VERBOSE_INTERVAL) == 0) + BURP_verbose(108, (void *) records, NULL, NULL, NULL, NULL); + + PUT(rec_data); + PUT_NUMERIC(att_data_length, record_length); + if (tdgbl->gbl_sw_transportable) + { + record_length = + CAN_encode_decode(relation, &xdr_buffer, buffer, TRUE); + PUT_NUMERIC(att_xdr_length, record_length); + p = xdr_buffer.lstr_address; + } + else + p = buffer; + PUT(att_data_data); + if (tdgbl->gbl_sw_compress) + compress(p, record_length); + else if (record_length) + (void) PUT_BLOCK(p, record_length); + + /* Look for any blobs to write */ + + for (field = relation->rel_fields; field; field = field->fld_next) + { + if (field->fld_type == blr_blob && + !(field->fld_flags & FLD_computed) && + !(field->fld_flags & FLD_array)) + { + put_blob(field, + (ISC_QUAD*) (buffer + field-> fld_offset), + records); + } + } + + /* Look for any array to write */ + /* we got back the blob_id for the array from isc_receive in the second param. */ + for (field = relation->rel_fields; field; field = field->fld_next) + { + if (field->fld_flags & FLD_array) + { + put_array(field, relation, + (ISC_QUAD *) (buffer + field->fld_offset)); + } + } + } + + BURP_FREE(buffer); + + if (xdr_buffer.lstr_address) + BURP_FREE(xdr_buffer.lstr_address); + + BURP_verbose(108, (void *) records, NULL, NULL, NULL, NULL); +/* msg 108 %ld records written */ + + if (isc_release_request(status_vector, GDS_REF(request))) + BURP_error_redirect(status_vector, 30, NULL, NULL); + /* msg 30 isc_release_request failed */ +} + + +static void put_index( REL relation) +{ +/************************************** + * + * p u t _ i n d e x + * + ************************************** + * + * Functional description + * Write information about an index. First + * check that all the segments of the + * index exist. + * + **************************************/ + ULONG l, count, match; + TEXT temp[32]; + TGBL tdgbl; + + tdgbl = GET_THREAD_DATA; + +/* if we have all capabilities, use the first request to get the + most performance out of the latest engine; if we don't + have one of the capabilities we must use the second set of + requests--this requires more code but it is well worth it + for the performance benefits, especially remotely--deej */ + + if ((tdgbl->BCK_capabilities & BCK_idx_inactive) && + (tdgbl->BCK_capabilities & BCK_attributes_v3) && + (tdgbl->BCK_capabilities & BCK_ods8)) + { + FOR (REQUEST_HANDLE tdgbl->handles_put_index_req_handle1) + X IN RDB$INDICES WITH + X.RDB$RELATION_NAME EQ relation->rel_name + + count = 0; + FOR (REQUEST_HANDLE tdgbl->handles_put_index_req_handle2) + I_S IN RDB$INDEX_SEGMENTS CROSS + RFR IN RDB$RELATION_FIELDS WITH + I_S.RDB$FIELD_NAME = RFR.RDB$FIELD_NAME AND + I_S.RDB$INDEX_NAME = X.RDB$INDEX_NAME AND + RFR.RDB$RELATION_NAME = relation->rel_name + + count++; + + END_FOR; + ON_ERROR + general_on_error (); + END_ERROR; + + if (count != (ULONG) X.RDB$SEGMENT_COUNT) + { + BURP_print (180, X.RDB$INDEX_NAME, (void*) count, (void*) X.RDB$SEGMENT_COUNT, NULL, NULL); + continue; + } + + PUT (rec_index); + l = PUT_TEXT (att_index_name, X.RDB$INDEX_NAME); + MISC_terminate (X.RDB$INDEX_NAME, temp, l, sizeof (temp)); + BURP_verbose (151, temp, NULL, NULL, NULL, NULL); + /* msg 151 writing index %s */ + PUT_NUMERIC (att_segment_count, X.RDB$SEGMENT_COUNT); + PUT_NUMERIC (att_index_inactive, X.RDB$INDEX_INACTIVE); + PUT_NUMERIC (att_index_unique_flag, X.RDB$UNIQUE_FLAG); + + FOR (REQUEST_HANDLE tdgbl->handles_put_index_req_handle5) + Y IN RDB$INDEX_SEGMENTS WITH + Y.RDB$INDEX_NAME EQ X.RDB$INDEX_NAME + SORTED BY Y.RDB$FIELD_POSITION + + PUT_TEXT (att_index_field_name, Y.RDB$FIELD_NAME); + + END_FOR; + ON_ERROR + general_on_error (); + END_ERROR; + + put_source_blob (att_index_description2, att_index_description, (ISC_QUAD *)&X.RDB$DESCRIPTION); + PUT_NUMERIC (att_index_type, X.RDB$INDEX_TYPE); + + if (!X.RDB$EXPRESSION_SOURCE.NULL) + put_source_blob (att_index_expression_source, att_index_expression_source, (ISC_QUAD *)&X.RDB$EXPRESSION_SOURCE); + if (!X.RDB$EXPRESSION_BLR.NULL) + put_blr_blob (att_index_expression_blr, (ISC_QUAD *)&X.RDB$EXPRESSION_BLR); + if (!X.RDB$FOREIGN_KEY.NULL) + PUT_TEXT (att_index_foreign_key, X.RDB$FOREIGN_KEY); + PUT (att_end); + + END_FOR; + ON_ERROR + general_on_error (); + END_ERROR; + } + else + { + FOR (REQUEST_HANDLE tdgbl->handles_put_index_req_handle1) + X IN RDB$INDICES WITH + X.RDB$RELATION_NAME EQ relation->rel_name + + count = 0; + FOR (REQUEST_HANDLE tdgbl->handles_put_index_req_handle2) + I_S IN RDB$INDEX_SEGMENTS WITH + I_S.RDB$INDEX_NAME = X.RDB$INDEX_NAME + match = FALSE; + + FOR (REQUEST_HANDLE tdgbl->handles_put_index_req_handle3) + RFR IN RDB$RELATION_FIELDS WITH + I_S.RDB$FIELD_NAME = RFR.RDB$FIELD_NAME AND + RFR.RDB$RELATION_NAME = relation->rel_name + match = TRUE; + END_FOR; + ON_ERROR + general_on_error (); + END_ERROR; + if (!match) + BURP_print (179, I_S.RDB$FIELD_NAME, X.RDB$INDEX_NAME, NULL, NULL, NULL); + else + count++; + END_FOR; + ON_ERROR + general_on_error (); + END_ERROR; + + if (count != (ULONG) X.RDB$SEGMENT_COUNT) + { + BURP_print (180, X.RDB$INDEX_NAME, (void*) count, (void*) X.RDB$SEGMENT_COUNT, NULL, NULL); + continue; + } + + PUT (rec_index); + l = PUT_TEXT (att_index_name, X.RDB$INDEX_NAME); + MISC_terminate (X.RDB$INDEX_NAME, temp, l, sizeof (temp)); + BURP_verbose (151, temp, NULL, NULL, NULL, NULL); + /* msg 151 writing index %s */ + PUT_NUMERIC (att_segment_count, X.RDB$SEGMENT_COUNT); + if (tdgbl->BCK_capabilities & BCK_idx_inactive) + FOR (REQUEST_HANDLE tdgbl->handles_put_index_req_handle4) + I IN RDB$INDICES WITH I.RDB$INDEX_NAME = X.RDB$INDEX_NAME + PUT_NUMERIC (att_index_inactive, I.RDB$INDEX_INACTIVE); + END_FOR; + ON_ERROR + general_on_error (); + END_ERROR; + PUT_NUMERIC (att_index_unique_flag, X.RDB$UNIQUE_FLAG); + FOR (REQUEST_HANDLE tdgbl->handles_put_index_req_handle5) + Y IN RDB$INDEX_SEGMENTS WITH Y.RDB$INDEX_NAME EQ X.RDB$INDEX_NAME + SORTED BY Y.RDB$FIELD_POSITION + PUT_TEXT (att_index_field_name, Y.RDB$FIELD_NAME); + END_FOR; + ON_ERROR + general_on_error (); + END_ERROR; + put_source_blob (att_index_description2, att_index_description, (ISC_QUAD *)&X.RDB$DESCRIPTION); + if (tdgbl->BCK_capabilities & BCK_attributes_v3) + FOR (REQUEST_HANDLE tdgbl->handles_put_index_req_handle6) + I IN RDB$INDICES WITH I.RDB$INDEX_NAME = X.RDB$INDEX_NAME + PUT_NUMERIC (att_index_type, I.RDB$INDEX_TYPE); + END_FOR; + ON_ERROR + general_on_error (); + END_ERROR; + if (tdgbl->BCK_capabilities & BCK_ods8) + FOR (REQUEST_HANDLE tdgbl->handles_put_index_req_handle7) + I IN RDB$INDICES WITH I.RDB$INDEX_NAME = X.RDB$INDEX_NAME + if (!I.RDB$EXPRESSION_SOURCE.NULL) + put_source_blob (att_index_expression_source, att_index_expression_source, (ISC_QUAD *)&I.RDB$EXPRESSION_SOURCE); + if (!I.RDB$EXPRESSION_BLR.NULL) + put_blr_blob (att_index_expression_blr, (ISC_QUAD *)&I.RDB$EXPRESSION_BLR); + if (!I.RDB$FOREIGN_KEY.NULL) + PUT_TEXT (att_index_foreign_key, I.RDB$FOREIGN_KEY); + END_FOR; + ON_ERROR + general_on_error (); + END_ERROR; + PUT (att_end); + + END_FOR; + ON_ERROR + general_on_error (); + END_ERROR; + } +} + + +static int put_message( SCHAR attribute, TEXT * text, ULONG length) +{ +/************************************** + * + * p u t _ m e s s a g e + * + ************************************** + * + * Functional description + * Write a variable length text string, with embedded + * blanks. Same as put_text but handles embedded blanks. + * + **************************************/ + TEXT *p; + ULONG l; + TGBL tdgbl; + + tdgbl = GET_THREAD_DATA; + + for (p = text, l = 0; *p && l < length; p++) + l++; + + l = length = MIN(l, length); + PUT(attribute); + PUT(l); + if (l) + (void) PUT_BLOCK(text, l); + + return length; +} + + +static void put_numeric( SCHAR attribute, SLONG value) +{ +/************************************** + * + * p u t _ n u m e r i c + * + ************************************** + * + * Functional description + * Write a numeric value as an attribute. The number is represented + * low byte first, high byte last, as in VAX. + * + **************************************/ + SLONG vax_value; + TGBL tdgbl; + + tdgbl = GET_THREAD_DATA; + + vax_value = (SLONG) isc_vax_integer((UCHAR *) & value, sizeof(value)); + + PUT(attribute); + PUT(sizeof(value)); + (void) PUT_BLOCK((UCHAR *) & vax_value, sizeof(vax_value)); +} + + +static void put_int64( SCHAR attribute, SINT64 value) +{ +/************************************** + * + * p u t _ i n t 6 4 + * + ************************************** + * + * Functional description + * Write a 64-bit numeric value as an attribute. + * The number is represented low byte first, high byte last, as in VAX. + * This function is just like put_numeric, except that it handles an + * INT64 value, while put_numeric handles a 32-bit value. + * + **************************************/ + UINT64 le_value; + TGBL tdgbl; + + tdgbl = GET_THREAD_DATA; + + le_value = + (UINT64) isc_portable_integer((UCHAR *) (&value), sizeof(value)); + + PUT(attribute); + PUT(sizeof(value)); + (void) PUT_BLOCK((UCHAR *) & le_value, sizeof(le_value)); +} + + +static void put_relation( REL relation) +{ +/************************************** + * + * p u t _ r e l a t i o n + * + ************************************** + * + * Functional description + * Write relation meta-data and data. + * + **************************************/ + FLD fields, field, aligned, unaligned, aligned4, aligned8; + TEXT temp[32]; + USHORT l, n; + SLONG *rp; + TGBL tdgbl; + + tdgbl = GET_THREAD_DATA; + +/* Write local field information. This is made slightly more complicated + by the requirement that computational fields be aligned. */ + + aligned = unaligned = aligned4 = aligned8 = NULL; + + fields = get_fields(relation); + +/* sort the list of fields into three lists, depending on alignment */ + + for (field = fields; field = fields;) + { + fields = field->fld_next; + l = field->fld_length; + if (field->fld_type == blr_varying) + l += sizeof(USHORT); + if (!(l & 7)) + { + field->fld_next = aligned8; + aligned8 = field; + } + else if (!(l & 3)) + { + field->fld_next = aligned4; + aligned4 = field; + } + else if (l & 1) + { + field->fld_next = unaligned; + unaligned = field; + } + else + { + field->fld_next = aligned; + aligned = field; + } + } + +/* Next, merge the aligned and unaligned sub-lists. In the process, + re-create (approximately) the original order of the fields. This is + not strictly required, but it certainly is polite. */ + + while (field = unaligned) + { + unaligned = field->fld_next; + field->fld_next = relation->rel_fields; + relation->rel_fields = field; + } + + while (field = aligned) + { + aligned = field->fld_next; + field->fld_next = relation->rel_fields; + relation->rel_fields = field; + } + + while (field = aligned4) + { + aligned4 = field->fld_next; + field->fld_next = relation->rel_fields; + relation->rel_fields = field; + } + + while (field = aligned8) + { + aligned8 = field->fld_next; + field->fld_next = relation->rel_fields; + relation->rel_fields = field; + } + +/* Now write the fields in what will become physical backup order */ + + for (field = relation->rel_fields; field; field = field->fld_next) + { + PUT(rec_field); + l = PUT_TEXT(att_field_name, field->fld_name); + MISC_terminate(field->fld_name, temp, l, sizeof(temp)); + BURP_verbose(144, temp, NULL, NULL, NULL, NULL); + /* msg 144 writing field %s */ + PUT_TEXT(att_field_source, field->fld_source); + if (field->fld_query_name[0]) + PUT_TEXT(att_field_query_name, field->fld_query_name); + if (field->fld_complex_name[0]) + PUT_TEXT(att_field_complex_name, field->fld_complex_name); + if (field->fld_edit_string[0]) + PUT_TEXT(att_field_edit_string, field->fld_edit_string); + put_source_blob(att_field_description2, att_field_description, + (ISC_QUAD *) & field->fld_description); + put_source_blob(att_field_query_header, att_field_query_header, + (ISC_QUAD *) & field->fld_query_header); + if (field->fld_security_class[0]) + PUT_TEXT(att_field_security_class, field->fld_security_class); + if (!(field->fld_flags & FLD_position_missing)) + PUT_NUMERIC(att_field_position, field->fld_position); + PUT_NUMERIC(att_field_type, field->fld_type); + PUT_NUMERIC(att_field_length, field->fld_length); + PUT_NUMERIC(att_field_sub_type, field->fld_sub_type); + PUT_NUMERIC(att_field_scale, field->fld_scale); + PUT_NUMERIC(att_field_number, field->fld_number); + PUT_NUMERIC(att_field_system_flag, field->fld_system_flag); + if (!(field->fld_flags & FLD_update_missing)) + PUT_NUMERIC(att_field_update_flag, field->fld_update_flag); + if (field->fld_flags & FLD_null_flag) + PUT_NUMERIC(att_field_null_flag, field->fld_null_flag); + if (field->fld_flags & FLD_charset_flag) + PUT_NUMERIC(att_field_character_set, field->fld_character_set_id); + if (field->fld_flags & FLD_collate_flag) + PUT_NUMERIC(att_field_collation_id, field->fld_collation_id); + put_blr_blob(att_field_default_value, + (ISC_QUAD *) & field->fld_default_value); + put_source_blob(att_field_default_source, att_field_default_source, + (ISC_QUAD *) & field->fld_default_source); + if (relation->rel_flags & REL_view) + { + PUT_NUMERIC(att_view_context, field->fld_view_context); + PUT_TEXT(att_base_field, field->fld_base); + } + if (field->fld_flags & FLD_computed) + PUT_NUMERIC(att_field_computed_flag, TRUE); + if (field->fld_flags & FLD_array) + { + PUT_NUMERIC(att_field_dimensions, field->fld_dimensions); + for (rp = field->fld_ranges, n = field->fld_dimensions; n; + rp += 2, n--) + { + PUT_NUMERIC(att_field_range_low, *rp); + PUT_NUMERIC(att_field_range_high, *(rp + 1)); + } + } + PUT(att_end); + } + +/* Write out view relations (if a view, of course) */ + + if (relation->rel_flags & REL_view) + { + if (tdgbl->BCK_capabilities & BCK_context_name) + { + FOR (REQUEST_HANDLE tdgbl->handles_put_relation_req_handle1) + X IN RDB$VIEW_RELATIONS WITH X.RDB$VIEW_NAME EQ relation->rel_name + PUT (rec_view); + PUT_TEXT (att_view_relation_name, X.RDB$RELATION_NAME); + PUT_NUMERIC (att_view_context_id, X.RDB$VIEW_CONTEXT); + PUT_TEXT (att_view_context_name, X.RDB$CONTEXT_NAME); + PUT (att_end); + END_FOR + ON_ERROR + general_on_error (); + END_ERROR; + } + else + { + FOR (REQUEST_HANDLE tdgbl->handles_put_relation_req_handle2) + X IN RDB$VIEW_RELATIONS WITH X.RDB$VIEW_NAME EQ relation->rel_name + PUT (rec_view); + PUT_TEXT (att_view_relation_name, X.RDB$RELATION_NAME); + PUT_NUMERIC (att_view_context_id, X.RDB$VIEW_CONTEXT); + PUT (att_end); + END_FOR; + ON_ERROR + general_on_error (); + END_ERROR; + } + } + PUT(rec_relation_end); +} + + +static int put_source_blob(SCHAR attribute, + SCHAR old_attribute, + ISC_QUAD* blob_id) +{ +/************************************** + * + * p u t _ s o u r c e _ b l o b + * + ************************************** + * + * Functional description + * Write out a source blob or query header if present. + * Return TRUE is there was the blob was present, FALSE otherwise. + * + **************************************/ + STATUS status_vector[ISC_STATUS_LENGTH]; + SLONG length, n; + void *blob; + UCHAR *p, blob_info[48], item, *buffer, static_buffer[1024]; + USHORT l, max_segment, num_seg; + TGBL tdgbl; + + tdgbl = GET_THREAD_DATA; + +/* If the blob is null, don't store it. It will be restored as null. */ + + if (!blob_id->isc_quad_high && !blob_id->isc_quad_low) + return FALSE; + + if (tdgbl->gbl_sw_old_descriptions && attribute != att_field_query_header) + return put_blr_blob(old_attribute, (ISC_QUAD *) blob_id); + +/* Open the blob and get it's vital statistics */ + + blob = NULL; + + if (isc_open_blob(status_vector, + GDS_REF(tdgbl->db_handle), + GDS_REF(isc_trans), + GDS_REF(blob), + GDS_VAL(blob_id))) + { + BURP_error_redirect(status_vector, 24, NULL, NULL); + /* msg 24 isc_open_blob failed */ + } + + if (isc_blob_info(status_vector, + GDS_REF(blob), + sizeof(source_items), + (SCHAR *) source_items, + sizeof(blob_info), + blob_info)) + { + BURP_error_redirect(status_vector, 20, NULL, NULL); + /* msg 20 isc_blob_info failed */ + } + + length = 0; + p = blob_info; + + while ((item = *p++) != isc_info_end) + { + l = (USHORT) isc_vax_integer(p, 2); + p += 2; + n = (USHORT) isc_vax_integer(p, l); + p += l; + switch (item) + { + case isc_info_blob_max_segment: + max_segment = (USHORT) n; + break; + + case isc_info_blob_total_length: + length = n; + break; + + case isc_info_blob_num_segments: + num_seg = (USHORT) n; + break; + + default: + BURP_print(79, (void *) item, NULL, NULL, NULL, NULL); + /* msg 79 don't understand blob info item %ld */ + return FALSE; + } + } + + if (!length) + { + if (isc_close_blob(status_vector, GDS_REF(blob))) + { + BURP_error_redirect(status_vector, 23, NULL, NULL); + /* msg 23 isc_close_blob failed */ + } + return FALSE; + } + +/* Rdb sometimes gets the length messed up */ + + if (length < max_segment) + length = max_segment; + + PUT_NUMERIC(attribute, length + num_seg); + +/* Allocate a buffer large enough for the largest segment and start grinding. */ + + if (!max_segment || max_segment <= sizeof(static_buffer)) + buffer = static_buffer; + else + buffer = BURP_ALLOC(max_segment); + + while (!isc_get_segment(status_vector, + GDS_REF(blob), + GDS_REF(l), + max_segment, + GDS_VAL(buffer))) + { + if (l) + { + (void) PUT_BLOCK(buffer, l); + } + PUT(NULL); + } + + if (isc_close_blob(status_vector, GDS_REF(blob))) + BURP_error_redirect(status_vector, 23, NULL, NULL); + /* msg 23 isc_close_blob failed */ + + if (buffer != static_buffer) + BURP_FREE(buffer); + + return TRUE; +} + + +static int put_text( SCHAR attribute, TEXT * text, SSHORT length) +{ +/************************************** + * + * p u t _ t e x t + * + ************************************** + * + * Functional description + * Write a variable length text string, with embedded spaces. + * Truncate trailing spaces. + * + **************************************/ + TEXT *p; + SSHORT l = 0; + TGBL tdgbl; + + tdgbl = GET_THREAD_DATA; + +/* find end of string */ + p = text; + while (*p++ && (l < length)) + l++; + + length = MIN(l, length); + +/* skip trailing spaces */ + for (p = text + length - 1, l = 0; *p == ' ' && l < length; p--) + l++; + + l = length = length - l; + PUT(attribute); + PUT(l); + if (l) + (void) PUT_BLOCK(text, l); + + return length; +} + + +static void put_trigger(enum trig_t type, + GDS__QUAD * blob_ident, + GDS__QUAD * source_blob, GDS_NAME rel_name) +{ +/************************************** + * + * p u t _ t r i g g e r + * + ************************************** + * + * Functional description + * Write a trigger to the output file. + * NOTE: This is used backup pre-V3 triggers only + * + **************************************/ + TGBL tdgbl; + + tdgbl = GET_THREAD_DATA; + + if (!blob_ident->gds_quad_low) + return; + + PUT(rec_trigger); + PUT_NUMERIC(att_trig_type, type); + put_blr_blob(att_trig_blr, (ISC_QUAD *) blob_ident); + put_source_blob(att_trig_source2, att_trig_source, + (ISC_QUAD *) source_blob); + PUT(att_end); +} + + +static void set_capabilities(void) +{ +/************************************** + * + * s e t _ c a p a b i l i t i e s + * + ************************************** + * + * Functional description + * + * set the capabilities bits for the + * database being extracted to avoid + * unpleasantness later. + * + **************************************/ + ULONG *req; + RFR_TAB rel_field_table; + TEXT *relation, *field; + TGBL tdgbl; + + tdgbl = GET_THREAD_DATA; + + req = NULL; + +/* Look for desireable fields in system relations */ + + for (rel_field_table = (RFR_TAB) rfr_table; rel_field_table->relation; + rel_field_table++) + { + field = (TEXT *) rel_field_table->field; + relation = (TEXT *) rel_field_table->relation; + FOR (REQUEST_HANDLE req) x IN RDB$RELATION_FIELDS + WITH x.RDB$RELATION_NAME = relation + AND x.RDB$FIELD_NAME = field + tdgbl->BCK_capabilities |= rel_field_table->bit_mask; + END_FOR; + ON_ERROR + general_on_error (); + END_ERROR; + } + + isc_release_request(isc_status, GDS_REF(req)); +} + + +static int symbol_length( TEXT * symbol) +{ +/************************************** + * + * s y m b o l _ l e n g t h + * + ************************************** + * + * Functional description + * Compute length of blank/null terminated symbol. + * + **************************************/ + TEXT *p; + + for (p = symbol; *p && *p != ' '; p++); + + return p - symbol; +} + + +static void write_character_sets(void) +{ +/************************************** + * + * w r i t e _ c h a r a c t e r _ s e t s + * + ************************************** + * + * Functional description + * write a record in the burp file for + * each user defined character set. + * + **************************************/ + isc_req_handle req_handle1 = NULL; + long req_status[20]; + TGBL tdgbl; + + tdgbl = GET_THREAD_DATA; + + FOR (REQUEST_HANDLE req_handle1) + X IN RDB$CHARACTER_SETS WITH X.RDB$SYSTEM_FLAG MISSING OR X.RDB$SYSTEM_FLAG NE 1 + PUT (rec_charset); + PUT_TEXT (att_charset_name, X.RDB$CHARACTER_SET_NAME); + if (!X.RDB$FORM_OF_USE.NULL) + PUT_TEXT (att_charset_form, X.RDB$FORM_OF_USE); + if (!X.RDB$NUMBER_OF_CHARACTERS.NULL) + PUT_NUMERIC (att_charset_numchar, X.RDB$NUMBER_OF_CHARACTERS); + PUT_TEXT (att_charset_coll, X.RDB$DEFAULT_COLLATE_NAME); + PUT_NUMERIC (att_charset_id, X.RDB$CHARACTER_SET_ID); + if (X.RDB$SYSTEM_FLAG) + PUT_NUMERIC (att_charset_sysflag, X.RDB$SYSTEM_FLAG); + if (!X.RDB$DESCRIPTION.NULL) + put_source_blob (att_charset_description, att_charset_description, (ISC_QUAD *)&X.RDB$DESCRIPTION); + if (!X.RDB$FUNCTION_NAME.NULL) + PUT_TEXT (att_charset_funct, X.RDB$FUNCTION_NAME); + PUT_NUMERIC (att_charset_bytes_char, X.RDB$BYTES_PER_CHARACTER); + + PUT (att_end); + END_FOR; + ON_ERROR + general_on_error (); + END_ERROR; + + if (req_handle1) + isc_release_request(req_status, &req_handle1); +} + + +static void write_check_constraints(void) +{ +/************************************** + * + * w r i t e _ c h e c k _ c o n s t r a i n t s + * + ************************************** + * + * Functional description + * write a record in the burp file for + * each check constraint. + * + **************************************/ + isc_req_handle req_handle1 = NULL; + long req_status[20]; + TGBL tdgbl; + + tdgbl = GET_THREAD_DATA; + + FOR (REQUEST_HANDLE req_handle1) + X IN RDB$CHECK_CONSTRAINTS + PUT (rec_chk_constraint); + + PUT_TEXT (att_chk_constraint_name, X.RDB$CONSTRAINT_NAME); + if (!(X.RDB$TRIGGER_NAME.NULL)) + PUT_TEXT (att_chk_trigger_name, X.RDB$TRIGGER_NAME); + PUT (att_end); + END_FOR; + ON_ERROR + general_on_error (); + END_ERROR; + + if (req_handle1) + isc_release_request(req_status, &req_handle1); +} + + +static void write_collations(void) +{ +/************************************** + * + * w r i t e _ c o l l a t i o n s + * + ************************************** + * + * Functional description + * write a record in the burp file for + * each user defined collation + * + **************************************/ + isc_req_handle req_handle1 = NULL; + long req_status[20]; + TGBL tdgbl; + + tdgbl = GET_THREAD_DATA; + + FOR (REQUEST_HANDLE req_handle1) + X IN RDB$COLLATIONS WITH X.RDB$SYSTEM_FLAG MISSING OR X.RDB$SYSTEM_FLAG NE 1 + PUT (rec_collation); + PUT_TEXT (att_coll_name, X.RDB$COLLATION_NAME); + PUT_NUMERIC (att_coll_id, X.RDB$COLLATION_ID); + PUT_NUMERIC (att_coll_cs_id, X.RDB$CHARACTER_SET_ID); + PUT_NUMERIC (att_coll_attr, X.RDB$COLLATION_ATTRIBUTES); + if (X.RDB$SYSTEM_FLAG) + PUT_NUMERIC (att_coll_sysflag, X.RDB$SYSTEM_FLAG); + if (!X.RDB$DESCRIPTION.NULL) + put_source_blob (att_coll_description, att_coll_description, (ISC_QUAD *)&X.RDB$DESCRIPTION); + if (!X.RDB$FUNCTION_NAME.NULL) + PUT_TEXT (att_coll_funct, X.RDB$FUNCTION_NAME); + + PUT (att_end); + END_FOR; + ON_ERROR + general_on_error (); + END_ERROR; + + if (req_handle1) + isc_release_request(req_status, &req_handle1); +} + +static void write_database( TEXT * dbb_file) +{ +/************************************** + * + * w r i t e _ d a t a b a s e + * + ************************************** + * + * Functional description + * write a physical database record and a + * logical database record in the burp file for + * the database itself. + * + **************************************/ + STATUS status_vector[ISC_STATUS_LENGTH]; + USHORT page_size, forced_writes, no_reserve, + length, SQL_dialect, db_read_only; + ULONG sweep_interval, page_buffers; + SCHAR buffer[256], *d, item; + isc_req_handle req_handle1 = NULL, req_handle2 = NULL, req_handle3 = NULL; + long req_status[20]; + TGBL tdgbl; + + tdgbl = GET_THREAD_DATA; + + PUT(rec_physical_db); + + page_size = 0; + if (isc_database_info(status_vector, + GDS_REF(tdgbl->db_handle), + sizeof(db_info_items), + (UCHAR *) db_info_items, sizeof(buffer), buffer)) + { + BURP_error_redirect(status_vector, 31, NULL, NULL); + /* msg 31 isc_database_info failed */ + } + + for (d = buffer; *d != isc_info_end; d += length) + { + item = *d++; + length = (USHORT) isc_vax_integer(d, 2); + d += 2; + switch (item) + { + case isc_info_end: + break; + + case isc_info_page_size: + page_size = (USHORT) isc_vax_integer(d, length); + PUT_NUMERIC(att_page_size, page_size); + break; + + case isc_info_sweep_interval: + sweep_interval = isc_vax_integer(d, length); + PUT_NUMERIC(att_sweep_interval, sweep_interval); + break; + + case isc_info_forced_writes: + forced_writes = (USHORT) isc_vax_integer(d, length); + PUT_NUMERIC(att_forced_writes, forced_writes); + break; + + case isc_info_no_reserve: + if (no_reserve = (USHORT) isc_vax_integer(d, length)) + PUT_NUMERIC(att_no_reserve, no_reserve); + break; + + case isc_info_set_page_buffers: + if (page_buffers = isc_vax_integer(d, length)) + PUT_NUMERIC(att_page_buffers, page_buffers); + break; + + case isc_info_error: /* old server does not understand new isc_info */ + break; /* parametere and returns isc_info_error. skip it */ + + case isc_info_db_sql_dialect: + SQL_dialect = (USHORT) isc_vax_integer(d, length); + PUT_NUMERIC(att_SQL_dialect, SQL_dialect); + break; + +#ifdef READONLY_DATABASE + case isc_info_db_read_only: + if (db_read_only = (USHORT) isc_vax_integer(d, length)) + PUT_NUMERIC(att_db_read_only, db_read_only); + break; +#endif /* READONLY_DATABASE */ + + default: + BURP_error_redirect(status_vector, 31, NULL, NULL); + /* msg 31 isc_database_info failed */ + break; + } + } + + PUT_ASCIZ(att_file_name, dbb_file); + + BURP_verbose(77, dbb_file, (void *) page_size, NULL, NULL, NULL); +/* msg 77 database %s has a page size of %ld bytes. */ + + PUT(att_end); + + PUT(rec_database); + +/* if we have all capabilities, use the first request to get the + most performance out of the latest engine; if we don't + have one of the capabilities we must use the second set of + requests--this requires more code but it is well worth it + for the performance benefits, especially remotely--deej */ + + if ((tdgbl->BCK_capabilities & BCK_security) && + (tdgbl->BCK_capabilities & BCK_db_description) && + (tdgbl->BCK_capabilities & BCK_ods8)) + { + FOR (REQUEST_HANDLE req_handle1) + D IN RDB$DATABASE + + if (!D.RDB$SECURITY_CLASS.NULL) + PUT_TEXT (att_database_security_class, D.RDB$SECURITY_CLASS); + put_source_blob (att_database_description2, att_database_description, (ISC_QUAD *)&D.RDB$DESCRIPTION); + if (!D.RDB$CHARACTER_SET_NAME.NULL) + PUT_TEXT (att_database_dfl_charset, D.RDB$CHARACTER_SET_NAME); + END_FOR; + ON_ERROR + general_on_error (); + END_ERROR; + } + else + { + if (tdgbl->BCK_capabilities & BCK_security) + { + FOR (REQUEST_HANDLE req_handle1) + D IN RDB$DATABASE + if (!D.RDB$SECURITY_CLASS.NULL) + PUT_TEXT (att_database_security_class, D.RDB$SECURITY_CLASS); + END_FOR; + ON_ERROR + general_on_error (); + END_ERROR; + } + + if (tdgbl->BCK_capabilities & BCK_db_description) + { + FOR (REQUEST_HANDLE req_handle2) + D IN RDB$DATABASE + put_source_blob (att_database_description2, att_database_description, (ISC_QUAD *)&D.RDB$DESCRIPTION); + END_FOR; + ON_ERROR + general_on_error (); + END_ERROR; + } + + if (tdgbl->BCK_capabilities & BCK_ods8) + { + FOR (REQUEST_HANDLE req_handle3) + D IN RDB$DATABASE + if (!D.RDB$CHARACTER_SET_NAME.NULL) + PUT_TEXT (att_database_dfl_charset, D.RDB$CHARACTER_SET_NAME); + END_FOR; + ON_ERROR + general_on_error (); + END_ERROR; + } + } + + if (req_handle1) + isc_release_request(req_status, &req_handle1); + if (req_handle2) + isc_release_request(req_status, &req_handle2); + if (req_handle3) + isc_release_request(req_status, &req_handle3); + + PUT(att_end); +} + + +static void write_exceptions(void) +{ +/************************************** + * + * w r i t e _ e x c e p t i o n s + * + ************************************** + * + * Functional description + * write a record in the burp file for + * each exception. + * + **************************************/ + SSHORT l; + TEXT temp[32]; + isc_req_handle req_handle1 = NULL; + long req_status[20]; + TGBL tdgbl; + + tdgbl = GET_THREAD_DATA; + + FOR (REQUEST_HANDLE req_handle1) + X IN RDB$EXCEPTIONS + PUT (rec_exception); + l = PUT_TEXT (att_exception_name, X.RDB$EXCEPTION_NAME); + MISC_terminate (X.RDB$EXCEPTION_NAME, temp, l, sizeof (temp)); + BURP_verbose (198, temp, NULL, NULL, NULL, NULL); + /* msg 198 writing exception %s */ + PUT_MESSAGE (att_exception_msg, X.RDB$MESSAGE); + put_source_blob (att_exception_description2, att_procedure_description, (ISC_QUAD *)&X.RDB$DESCRIPTION); + PUT (att_end); + END_FOR; + ON_ERROR + general_on_error (); + END_ERROR; + + if (req_handle1) + isc_release_request(req_status, &req_handle1); +} + +static void write_field_dimensions(void) +{ +/************************************** + * + * w r i t e _ f i e l d _ d i m e n s i o n s + * + ************************************** + * + * Functional description + * write a record in the burp file for + * each array field dimension. + * + **************************************/ + isc_req_handle req_handle1 = NULL; + long req_status[20]; + TGBL tdgbl; + + tdgbl = GET_THREAD_DATA; + + FOR (REQUEST_HANDLE req_handle1) + X IN RDB$FIELD_DIMENSIONS + PUT (rec_field_dimensions); + PUT_TEXT (att_field_name, X.RDB$FIELD_NAME); + PUT_NUMERIC (att_field_dimensions, X.RDB$DIMENSION); + PUT_NUMERIC (att_field_range_low, X.RDB$LOWER_BOUND); + PUT_NUMERIC (att_field_range_high, X.RDB$UPPER_BOUND); + PUT (att_end); + END_FOR; + ON_ERROR + general_on_error (); + END_ERROR; + + if (req_handle1) + isc_release_request(req_status, &req_handle1); +} + + +static void write_filters(void) +{ +/************************************** + * + * w r i t e _ f i l t e r s + * + ************************************** + * + * Functional description + * write a record in the burp file for + * each filter. + * + **************************************/ + SSHORT l; + TEXT temp[32]; + isc_req_handle req_handle1 = NULL; + long req_status[20]; + TGBL tdgbl; + + tdgbl = GET_THREAD_DATA; + + FOR (REQUEST_HANDLE req_handle1) + X IN RDB$FILTERS + PUT (rec_filter); + l = PUT_TEXT (att_filter_name, X.RDB$FUNCTION_NAME); + MISC_terminate (X.RDB$FUNCTION_NAME, temp, l, sizeof (temp)); + BURP_verbose (145, temp, NULL, NULL, NULL, NULL); + /* msg 145 writing filter %s */ + put_source_blob (att_filter_description2, att_filter_description, (ISC_QUAD *)&X.RDB$DESCRIPTION); + PUT_TEXT (att_filter_module_name, X.RDB$MODULE_NAME); + PUT_TEXT (att_filter_entrypoint, X.RDB$ENTRYPOINT); + PUT_NUMERIC (att_filter_input_sub_type, X.RDB$INPUT_SUB_TYPE); + PUT_NUMERIC (att_filter_output_sub_type, X.RDB$OUTPUT_SUB_TYPE); + PUT (att_end); + END_FOR; + ON_ERROR + general_on_error (); + END_ERROR; + + if (req_handle1) + isc_release_request(req_status, &req_handle1); +} + +static void write_functions(void) +{ +/************************************** + * + * w r i t e _ f u n c t i o n s + * + ************************************** + * + * Functional description + * write a record in the burp file for + * each function. + * + **************************************/ + SSHORT l; + GDS_NAME func; + TEXT temp[32]; + isc_req_handle req_handle1 = NULL; + long req_status[20]; + TGBL tdgbl; + + tdgbl = GET_THREAD_DATA; + + FOR (REQUEST_HANDLE req_handle1) + X IN RDB$FUNCTIONS + PUT (rec_function); + l = PUT_TEXT (att_function_name, X.RDB$FUNCTION_NAME); + MISC_terminate (X.RDB$FUNCTION_NAME, temp, l, sizeof (temp)); + BURP_verbose (147, temp, NULL, NULL, NULL, NULL); + /* msg 147 writing function %.*s */ + put_source_blob (att_function_description2, att_function_description, (ISC_QUAD *)&X.RDB$DESCRIPTION); + PUT_TEXT (att_function_module_name, X.RDB$MODULE_NAME); + PUT_TEXT (att_function_entrypoint, X.RDB$ENTRYPOINT); + PUT_NUMERIC (att_function_return_arg, X.RDB$RETURN_ARGUMENT); + PUT_NUMERIC (att_function_type, X.RDB$FUNCTION_TYPE); + PUT_TEXT (att_function_query_name, X.RDB$QUERY_NAME); + PUT (att_end); + copy (X.RDB$FUNCTION_NAME, func, GDS_NAME_LEN - 1); + write_function_args (func); + PUT (rec_function_end); + END_FOR; + ON_ERROR + general_on_error (); + END_ERROR; + + if (req_handle1) + isc_release_request(req_status, &req_handle1); +} + +static void write_function_args( GDS_NAME funcptr) +{ +/************************************** + * + * w r i t e _ f u n c t i o n _ a r g s + * + ************************************** + * + * Functional description + * write all arguments for a function. + * + **************************************/ + SSHORT l; + TEXT temp[32]; + TGBL tdgbl; + + tdgbl = GET_THREAD_DATA; + + /* if we have all capabilities, use the first request to get the + most performance out of the latest engine; if we don't + have one of the capabilities we must use the second set of + requests--this requires more code but it is well worth it + for the performance benefits, especially remotely--deej */ + + if (tdgbl->BCK_capabilities & BCK_ods10) + { + FOR (REQUEST_HANDLE tdgbl->handles_write_function_args_req_handle1) + X IN RDB$FUNCTION_ARGUMENTS WITH + X.RDB$FUNCTION_NAME EQ funcptr + + PUT (rec_function_arg); + l = PUT_TEXT (att_functionarg_name, X.RDB$FUNCTION_NAME); + MISC_terminate (X.RDB$FUNCTION_NAME, temp, l, sizeof (temp)); + BURP_verbose (141, temp, NULL, NULL, NULL, NULL); + /* msg 141 writing argument for function %s */ + PUT_NUMERIC (att_functionarg_position, X.RDB$ARGUMENT_POSITION); + PUT_NUMERIC (att_functionarg_mechanism, X.RDB$MECHANISM); + PUT_NUMERIC (att_functionarg_field_type, X.RDB$FIELD_TYPE); + PUT_NUMERIC (att_functionarg_field_scale, X.RDB$FIELD_SCALE); + PUT_NUMERIC (att_functionarg_field_length, X.RDB$FIELD_LENGTH); + PUT_NUMERIC (att_functionarg_field_sub_type, X.RDB$FIELD_SUB_TYPE); + if (!(X.RDB$CHARACTER_SET_ID.NULL)) + PUT_NUMERIC (att_functionarg_character_set, X.RDB$CHARACTER_SET_ID); + + if (!(X.RDB$FIELD_PRECISION.NULL)) + PUT_NUMERIC (att_functionarg_field_precision, X.RDB$FIELD_PRECISION); + PUT (att_end); + END_FOR; + ON_ERROR + general_on_error (); + END_ERROR; + } + else + { + FOR (REQUEST_HANDLE tdgbl->handles_write_function_args_req_handle1) + X IN RDB$FUNCTION_ARGUMENTS WITH + X.RDB$FUNCTION_NAME EQ funcptr + + PUT (rec_function_arg); + l = PUT_TEXT (att_functionarg_name, X.RDB$FUNCTION_NAME); + MISC_terminate (X.RDB$FUNCTION_NAME, temp, l, sizeof (temp)); + BURP_verbose (141, temp, NULL, NULL, NULL, NULL); + /* msg 141 writing argument for function %s */ + PUT_NUMERIC (att_functionarg_position, X.RDB$ARGUMENT_POSITION); + PUT_NUMERIC (att_functionarg_mechanism, X.RDB$MECHANISM); + PUT_NUMERIC (att_functionarg_field_type, X.RDB$FIELD_TYPE); + PUT_NUMERIC (att_functionarg_field_scale, X.RDB$FIELD_SCALE); + PUT_NUMERIC (att_functionarg_field_length, X.RDB$FIELD_LENGTH); + PUT_NUMERIC (att_functionarg_field_sub_type, X.RDB$FIELD_SUB_TYPE); + + if (tdgbl->BCK_capabilities & BCK_ods8) + { + FOR (REQUEST_HANDLE tdgbl->handles_write_function_args_req_handle2) + X2 IN RDB$FUNCTION_ARGUMENTS WITH + X2.RDB$FUNCTION_NAME EQ funcptr AND + X2.RDB$ARGUMENT_POSITION = X.RDB$ARGUMENT_POSITION; + + if (!(X2.RDB$CHARACTER_SET_ID.NULL)) + PUT_NUMERIC (att_functionarg_character_set, X2.RDB$CHARACTER_SET_ID); + /* Note that BCK_ods10 canNOT be set if we're in this + ``else'' branch. Hence there is no need to test that + bit and store the RDB$FIELD_PRECISION. */ + + END_FOR; + ON_ERROR + general_on_error (); + END_ERROR; + } + PUT (att_end); + + END_FOR; + ON_ERROR + general_on_error (); + END_ERROR; + } +} + + +static void write_generators(void) +{ +/************************************** + * + * w r i t e _ g e n e r a t o r s + * + ************************************** + * + * Functional description + * Write any defined generators. + * + **************************************/ + SINT64 value; + isc_req_handle req_handle1 = NULL; + long req_status[20]; + TGBL tdgbl; + + tdgbl = GET_THREAD_DATA; + + FOR (REQUEST_HANDLE req_handle1) + X IN RDB$GENERATORS WITH X.RDB$SYSTEM_FLAG MISSING OR X.RDB$SYSTEM_FLAG NE 1 + PUT (rec_generator); + PUT_TEXT (att_gen_generator, X.RDB$GENERATOR_NAME); + value = get_gen_id (X.RDB$GENERATOR_NAME); + PUT_INT64 (att_gen_value_int64, value); + PUT (att_end); + BURP_verbose (165, X.RDB$GENERATOR_NAME, (void*) value, NULL, NULL, NULL); + /* msg 165 writing generator %s value %ld */ + END_FOR; + ON_ERROR + general_on_error (); + END_ERROR; + + if (req_handle1) + isc_release_request(req_status, &req_handle1); +} + + +static void write_global_fields(void) +{ +/************************************** + * + * w r i t e _ g l o b a l _ f i e l d s + * + ************************************** + * + * Functional description + * write a record in the burp file for + * each global field. + * + **************************************/ + SSHORT l; + TEXT temp[32]; + isc_req_handle req_handle1 = NULL, req_handle2 = NULL, + req_handle3 = NULL, req_handle4 = NULL; + long req_status[20]; + TGBL tdgbl; + + tdgbl = GET_THREAD_DATA; + + /* if we have all capabilities, use the first request to get the + most performance out of the latest engine; if we don't + have one of the capabilities we must use the second set of + requests--this requires more code but it is well worth it + for the performance benefits, especially remotely--deej */ + + if ((tdgbl->BCK_capabilities & BCK_attributes_v3) && + (tdgbl->BCK_capabilities & BCK_ods8) && + (tdgbl->BCK_capabilities & BCK_ods10)) + { + FOR (REQUEST_HANDLE req_handle1) + X IN RDB$FIELDS WITH + X.RDB$SYSTEM_FLAG NE 1 OR + X.RDB$SYSTEM_FLAG MISSING + + PUT (rec_global_field); + l = PUT_TEXT (att_field_name, X.RDB$FIELD_NAME); + MISC_terminate (X.RDB$FIELD_NAME, temp, l, sizeof (temp)); + BURP_verbose (149, temp, NULL, NULL, NULL, NULL); + /* msg 149 writing global field %.*s */ + if (X.RDB$QUERY_NAME [0] != ' ') + PUT_TEXT (att_field_query_name, X.RDB$QUERY_NAME); + if (X.RDB$EDIT_STRING [0] != ' ') + PUT_TEXT (att_field_edit_string, X.RDB$EDIT_STRING); + put_source_blob (att_field_query_header, att_field_query_header, (ISC_QUAD *)&X.RDB$QUERY_HEADER); + PUT_NUMERIC (att_field_type, X.RDB$FIELD_TYPE); + PUT_NUMERIC (att_field_length, X.RDB$FIELD_LENGTH); + PUT_NUMERIC (att_field_sub_type, X.RDB$FIELD_SUB_TYPE); + PUT_NUMERIC (att_field_scale, X.RDB$FIELD_SCALE); + put_blr_blob (att_field_missing_value, (ISC_QUAD *)&X.RDB$MISSING_VALUE); + put_blr_blob (att_field_default_value, (ISC_QUAD *)&X.RDB$DEFAULT_VALUE); + put_blr_blob (att_field_validation_blr, (ISC_QUAD *)&X.RDB$VALIDATION_BLR); + put_source_blob (att_field_validation_source2, att_field_validation_source, (ISC_QUAD *)&X.RDB$VALIDATION_SOURCE); + put_blr_blob (att_field_computed_blr, &X.RDB$COMPUTED_BLR); + put_source_blob (att_field_computed_source2, att_field_computed_source, (ISC_QUAD *)&X.RDB$COMPUTED_SOURCE); + if (X.RDB$SEGMENT_LENGTH) + PUT_NUMERIC (att_field_segment_length, X.RDB$SEGMENT_LENGTH); + if (X.RDB$SYSTEM_FLAG) + PUT_NUMERIC (att_field_system_flag, X.RDB$SYSTEM_FLAG); + put_source_blob (att_field_description2, att_field_description, (ISC_QUAD *)&X.RDB$DESCRIPTION); + + if (X.RDB$EXTERNAL_LENGTH) + PUT_NUMERIC (att_field_external_length, X.RDB$EXTERNAL_LENGTH); + if (X.RDB$EXTERNAL_TYPE) + PUT_NUMERIC (att_field_external_type, X.RDB$EXTERNAL_TYPE); + if (X.RDB$EXTERNAL_SCALE) + PUT_NUMERIC (att_field_external_scale, X.RDB$EXTERNAL_SCALE); + if (X.RDB$DIMENSIONS) + PUT_NUMERIC (att_field_dimensions, X.RDB$DIMENSIONS); + if (!(X.RDB$NULL_FLAG.NULL)) + PUT_NUMERIC (att_field_null_flag, X.RDB$NULL_FLAG); + if (!(X.RDB$CHARACTER_LENGTH.NULL)) + PUT_NUMERIC (att_field_character_length, X.RDB$CHARACTER_LENGTH); + if (!(X.RDB$DEFAULT_SOURCE.NULL)) + put_source_blob (att_field_default_source, att_field_default_source, (ISC_QUAD *)&X.RDB$DEFAULT_SOURCE); + if (!(X.RDB$MISSING_SOURCE.NULL)) + put_source_blob (att_field_missing_source, att_field_missing_source, (ISC_QUAD *)&X.RDB$MISSING_SOURCE); + if (!(X.RDB$CHARACTER_SET_ID.NULL)) + PUT_NUMERIC (att_field_character_set, X.RDB$CHARACTER_SET_ID); + if (!(X.RDB$COLLATION_ID.NULL)) + PUT_NUMERIC (att_field_collation_id, X.RDB$COLLATION_ID); + + if (!(X.RDB$FIELD_PRECISION.NULL)) + PUT_NUMERIC (att_field_precision, X.RDB$FIELD_PRECISION); + + PUT (att_end); + + END_FOR; + ON_ERROR + general_on_error (); + END_ERROR; + } + else + { + FOR (REQUEST_HANDLE req_handle1) + X IN RDB$FIELDS WITH + X.RDB$SYSTEM_FLAG NE 1 OR + X.RDB$SYSTEM_FLAG MISSING + + PUT (rec_global_field); + l = PUT_TEXT (att_field_name, X.RDB$FIELD_NAME); + MISC_terminate (X.RDB$FIELD_NAME, temp, l, sizeof (temp)); + BURP_verbose (149, temp, NULL, NULL, NULL, NULL); + /* msg 149 writing global field %.*s */ + if (X.RDB$QUERY_NAME [0] != ' ') + PUT_TEXT (att_field_query_name, X.RDB$QUERY_NAME); + if (X.RDB$EDIT_STRING [0] != ' ') + PUT_TEXT (att_field_edit_string, X.RDB$EDIT_STRING); + put_source_blob (att_field_query_header, att_field_query_header, (ISC_QUAD *)&X.RDB$QUERY_HEADER); + PUT_NUMERIC (att_field_type, X.RDB$FIELD_TYPE); + PUT_NUMERIC (att_field_length, X.RDB$FIELD_LENGTH); + PUT_NUMERIC (att_field_sub_type, X.RDB$FIELD_SUB_TYPE); + PUT_NUMERIC (att_field_scale, X.RDB$FIELD_SCALE); + put_blr_blob (att_field_missing_value, (ISC_QUAD *)&X.RDB$MISSING_VALUE); + put_blr_blob (att_field_default_value, (ISC_QUAD *)&X.RDB$DEFAULT_VALUE); + put_blr_blob (att_field_validation_blr, (ISC_QUAD *)&X.RDB$VALIDATION_BLR); + put_source_blob (att_field_validation_source2, att_field_validation_source, (ISC_QUAD *)&X.RDB$VALIDATION_SOURCE); + put_blr_blob (att_field_computed_blr, (ISC_QUAD *)&X.RDB$COMPUTED_BLR); + put_source_blob (att_field_computed_source2, att_field_computed_source, (ISC_QUAD *)&X.RDB$COMPUTED_SOURCE); + if (X.RDB$SEGMENT_LENGTH) + PUT_NUMERIC (att_field_segment_length, X.RDB$SEGMENT_LENGTH); + if (X.RDB$SYSTEM_FLAG) + PUT_NUMERIC (att_field_system_flag, X.RDB$SYSTEM_FLAG); + put_source_blob (att_field_description2, att_field_description, (ISC_QUAD *)&X.RDB$DESCRIPTION); + if (tdgbl->BCK_capabilities & BCK_attributes_v3) + { + FOR (REQUEST_HANDLE req_handle2) + F IN RDB$FIELDS WITH F.RDB$FIELD_NAME = X.RDB$FIELD_NAME + + if (F.RDB$EXTERNAL_LENGTH) + PUT_NUMERIC (att_field_external_length, F.RDB$EXTERNAL_LENGTH); + if (F.RDB$EXTERNAL_TYPE) + PUT_NUMERIC (att_field_external_type, F.RDB$EXTERNAL_TYPE); + if (F.RDB$EXTERNAL_SCALE) + PUT_NUMERIC (att_field_external_scale, F.RDB$EXTERNAL_SCALE); + if (F.RDB$DIMENSIONS) + PUT_NUMERIC (att_field_dimensions, F.RDB$DIMENSIONS); + END_FOR; + ON_ERROR + general_on_error (); + END_ERROR; + } + if (tdgbl->BCK_capabilities & BCK_ods8) + { + FOR (REQUEST_HANDLE req_handle3) + F IN RDB$FIELDS WITH F.RDB$FIELD_NAME = X.RDB$FIELD_NAME + if (!(F.RDB$NULL_FLAG.NULL)) + PUT_NUMERIC (att_field_null_flag, F.RDB$NULL_FLAG); + if (!(F.RDB$CHARACTER_LENGTH.NULL)) + PUT_NUMERIC (att_field_character_length, F.RDB$CHARACTER_LENGTH); + if (!(F.RDB$DEFAULT_SOURCE.NULL)) + put_source_blob (att_field_default_source, att_field_default_source, (ISC_QUAD *)&F.RDB$DEFAULT_SOURCE); + if (!(F.RDB$MISSING_SOURCE.NULL)) + put_source_blob (att_field_missing_source, att_field_missing_source, (ISC_QUAD *)&F.RDB$MISSING_SOURCE); + if (!(F.RDB$CHARACTER_SET_ID.NULL)) + PUT_NUMERIC (att_field_character_set, F.RDB$CHARACTER_SET_ID); + if (!(F.RDB$COLLATION_ID.NULL)) + PUT_NUMERIC (att_field_collation_id, F.RDB$COLLATION_ID); + + if (tdgbl->BCK_capabilities & BCK_ods10) + { + FOR (REQUEST_HANDLE req_handle4) + K IN RDB$FIELDS WITH K.RDB$FIELD_NAME = X.RDB$FIELD_NAME + if (!(K.RDB$FIELD_PRECISION.NULL)) + PUT_NUMERIC (att_field_precision, K.RDB$FIELD_PRECISION); + END_FOR; + ON_ERROR + general_on_error (); + END_ERROR; + } + END_FOR; + ON_ERROR + general_on_error (); + END_ERROR; + } + PUT (att_end); + END_FOR; + ON_ERROR + general_on_error (); + END_ERROR; + } + + if (req_handle1) + isc_release_request(req_status, &req_handle1); + if (req_handle2) + isc_release_request(req_status, &req_handle2); + if (req_handle3) + isc_release_request(req_status, &req_handle3); + if (req_handle4) + isc_release_request(req_status, &req_handle4); +} + + +static void write_procedures(void) +{ +/************************************** + * + * w r i t e _ p r o c e d u r e s + * + ************************************** + * + * Functional description + * write a record in the burp file for + * each stored procedure. + * + **************************************/ + SSHORT l; + GDS_NAME proc; + TEXT temp[32]; + isc_req_handle req_handle1 = NULL; + long req_status[20]; + TGBL tdgbl; + + tdgbl = GET_THREAD_DATA; + + FOR (REQUEST_HANDLE req_handle1) + X IN RDB$PROCEDURES + PUT (rec_procedure); + l = PUT_TEXT (att_procedure_name, X.RDB$PROCEDURE_NAME); + MISC_terminate (X.RDB$PROCEDURE_NAME, temp, l, sizeof (temp)); + BURP_verbose (193, temp, NULL, NULL, NULL, NULL); + /* msg 193 writing stored procedure %.*s */ + PUT_NUMERIC (att_procedure_inputs, X.RDB$PROCEDURE_INPUTS); + PUT_NUMERIC (att_procedure_outputs, X.RDB$PROCEDURE_OUTPUTS); + put_source_blob (att_procedure_description2, att_procedure_description, (ISC_QUAD *)&X.RDB$DESCRIPTION); + put_source_blob (att_procedure_source2, att_procedure_source, (ISC_QUAD *)&X.RDB$PROCEDURE_SOURCE); + put_blr_blob (att_procedure_blr, (ISC_QUAD *)&X.RDB$PROCEDURE_BLR); + if (!X.RDB$SECURITY_CLASS.NULL) + PUT_TEXT (att_procedure_security_class, X.RDB$SECURITY_CLASS); + if (!X.RDB$SECURITY_CLASS.NULL) + PUT_TEXT (att_procedure_owner_name, X.RDB$OWNER_NAME); + PUT (att_end); + copy (X.RDB$PROCEDURE_NAME, proc, GDS_NAME_LEN - 1); + write_procedure_prms (proc); + PUT (rec_procedure_end); + END_FOR; + ON_ERROR + general_on_error (); + END_ERROR; + + if (req_handle1) + isc_release_request(req_status, &req_handle1); +} + + +static void write_procedure_prms( GDS_NAME procptr) +{ +/************************************** + * + * w r i t e _ p r o c e d u r e _ p r m s + * + ************************************** + * + * Functional description + * write all parameters of a stored procedure. + * + **************************************/ + + SSHORT l; + TEXT temp[32]; + TGBL tdgbl; + + tdgbl = GET_THREAD_DATA; + + FOR (REQUEST_HANDLE tdgbl->handles_write_procedure_prms_req_handle1) + X IN RDB$PROCEDURE_PARAMETERS WITH X.RDB$PROCEDURE_NAME EQ procptr + PUT (rec_procedure_prm); + l = PUT_TEXT (att_procedureprm_name, X.RDB$PARAMETER_NAME); + MISC_terminate (X.RDB$PARAMETER_NAME, temp, l, sizeof (temp)); + BURP_verbose (194, temp, NULL, NULL, NULL, NULL); + /* msg 194 writing parameter %s for stored procedure */ + PUT_NUMERIC (att_procedureprm_number, X.RDB$PARAMETER_NUMBER); + PUT_NUMERIC (att_procedureprm_type, X.RDB$PARAMETER_type); + PUT_TEXT (att_procedureprm_field_source, X.RDB$FIELD_SOURCE); + put_source_blob (att_procedureprm_description2, att_procedureprm_description, (ISC_QUAD *)&X.RDB$DESCRIPTION); + PUT (att_end); + END_FOR; + ON_ERROR + general_on_error (); + END_ERROR; +} + + +static void write_ref_constraints(void) +{ +/************************************** + * + * w r i t e _ r e f _ c o n s t r a i n t s + * + ************************************** + * + * Functional description + * write a record in the burp file for + * each referential constraint. + * + **************************************/ + isc_req_handle req_handle1 = NULL; + long req_status[20]; + TGBL tdgbl; + + tdgbl = GET_THREAD_DATA; + + FOR (REQUEST_HANDLE req_handle1) + X IN RDB$REF_CONSTRAINTS + PUT (rec_ref_constraint); + PUT_TEXT (att_ref_constraint_name, X.RDB$CONSTRAINT_NAME); + PUT_TEXT (att_ref_unique_const_name, X.RDB$CONST_NAME_UQ); + PUT_TEXT (att_ref_match_option, X.RDB$MATCH_OPTION); + PUT_MESSAGE (att_ref_update_rule, X.RDB$UPDATE_RULE); + PUT_MESSAGE (att_ref_delete_rule, X.RDB$DELETE_RULE); + PUT (att_end); + END_FOR; + ON_ERROR + general_on_error (); + END_ERROR; + + if (req_handle1) + isc_release_request(req_status, &req_handle1); +} + +static void write_rel_constraints(void) +{ +/************************************** + * + * w r i t e _ r e l _ c o n s t r a i n t s + * + ************************************** + * + * Functional description + * write a record in the burp file for + * each relation constraint. + * + **************************************/ + SSHORT l; + TEXT temp[32]; + isc_req_handle req_handle1 = NULL; + long req_status[20]; + TGBL tdgbl; + + tdgbl = GET_THREAD_DATA; + + FOR (REQUEST_HANDLE req_handle1) + X IN RDB$RELATION_CONSTRAINTS + PUT (rec_rel_constraint); + l = PUT_TEXT (att_rel_constraint_name, X.RDB$CONSTRAINT_NAME); + MISC_terminate (X.RDB$CONSTRAINT_NAME, temp, l, sizeof (temp)); + BURP_verbose (207, temp, NULL, NULL, NULL, NULL); + /* msg 207 writing constraint %s */ + PUT_MESSAGE (att_rel_constraint_type, X.RDB$CONSTRAINT_TYPE); + PUT_TEXT (att_rel_constraint_rel_name, X.RDB$RELATION_NAME); + PUT_TEXT (att_rel_constraint_defer, X.RDB$DEFERRABLE); + PUT_TEXT (att_rel_constraint_init, X.RDB$INITIALLY_DEFERRED); + if (!(X.RDB$INDEX_NAME.NULL)) + PUT_TEXT (att_rel_constraint_index, X.RDB$INDEX_NAME); + PUT (att_end); + END_FOR; + ON_ERROR + general_on_error (); + END_ERROR; + + if (req_handle1) + isc_release_request(req_status, &req_handle1); +} + +static void write_relations(void) +{ +/************************************** + * + * w r i t e _ r e l a t i o n s + * + ************************************** + * + * Functional description + * write a record in the burp file for + * each relation. + * + **************************************/ + SSHORT l, flags; + REL relation; + TEXT temp[32]; + isc_req_handle req_handle1 = NULL, req_handle2 = NULL, req_handle3 = + NULL, req_handle4 = NULL; + long req_status[20]; + TGBL tdgbl; + + tdgbl = GET_THREAD_DATA; + + /* if we have all capabilities, use the first request to get the + most performance out of the latest engine; if we don't + have one of the capabilities we must use the second set of + requests--this requires more code but it is well worth it + for the performance benefits, especially remotely--deej */ + + if ((tdgbl->BCK_capabilities & BCK_ods8) && + (tdgbl->BCK_capabilities & BCK_security) && + (tdgbl->BCK_capabilities & BCK_attributes_v3)) + { + FOR (REQUEST_HANDLE req_handle1) + X IN RDB$RELATIONS WITH X.RDB$SYSTEM_FLAG NE 1 OR + X.RDB$SYSTEM_FLAG MISSING + + flags = 0; + PUT (rec_relation); + l = put_text (att_relation_name, X.RDB$RELATION_NAME, 31); + MISC_terminate (X.RDB$RELATION_NAME, temp, l, sizeof (temp)); + BURP_verbose (153, temp, NULL, NULL, NULL, NULL); + /* msg 153 writing relation %.*s */ + + /* RDB$VIEW_BLR must be the forst blob field in the backup file. + * RESTORE.E makes this assumption in get_relation(). + */ + + if (put_blr_blob (att_relation_view_blr, (ISC_QUAD *)&X.RDB$VIEW_BLR)) + flags |= REL_view; + if (X.RDB$SYSTEM_FLAG) + PUT_NUMERIC (att_relation_system_flag, X.RDB$SYSTEM_FLAG); + if (!(X.RDB$FLAGS.NULL)) + PUT_NUMERIC (att_relation_flags, X.RDB$FLAGS); + if (!X.RDB$SECURITY_CLASS.NULL) + PUT_TEXT (att_relation_security_class, X.RDB$SECURITY_CLASS); + + put_source_blob (att_relation_description2, att_relation_description, (ISC_QUAD *)&X.RDB$DESCRIPTION); + put_source_blob (att_relation_view_source2, att_relation_view_source, (ISC_QUAD *)&X.RDB$VIEW_SOURCE); + + put_source_blob (att_relation_ext_description2, att_relation_ext_description, (ISC_QUAD *)&X.RDB$EXTERNAL_DESCRIPTION); + put_text (att_relation_owner_name, X.RDB$OWNER_NAME, 31); + if (!X.RDB$EXTERNAL_FILE.NULL) + if (!tdgbl->gbl_sw_convert_ext_tables) + { + put_text (att_relation_ext_file_name, X.RDB$EXTERNAL_FILE, 253); + flags |= REL_external; + } + + PUT (att_end); + relation = (REL) BURP_ALLOC_ZERO (sizeof (struct rel)); + relation->rel_next = tdgbl->relations; + tdgbl->relations = relation; + relation->rel_id = X.RDB$RELATION_ID; + relation->rel_name_length = copy (X.RDB$RELATION_NAME, relation->rel_name, GDS_NAME_LEN - 1); + relation->rel_flags |= flags; + put_relation (relation); + END_FOR; + ON_ERROR + general_on_error (); + END_ERROR; + } + else + { + FOR (REQUEST_HANDLE req_handle1) + X IN RDB$RELATIONS WITH X.RDB$SYSTEM_FLAG NE 1 OR + X.RDB$SYSTEM_FLAG MISSING + + flags = 0; + PUT (rec_relation); + l = put_text (att_relation_name, X.RDB$RELATION_NAME, 31); + MISC_terminate (X.RDB$RELATION_NAME, temp, l, sizeof (temp)); + BURP_verbose (153, temp, NULL, NULL, NULL, NULL); + /* msg 153 writing relation %.*s */ + + /* RDB$VIEW_BLR must be the first blob field in the backup file. + * RESTORE.E makes this assumption in get_relation(). + */ + + if (put_blr_blob (att_relation_view_blr, (ISC_QUAD *)&X.RDB$VIEW_BLR)) + flags |= REL_view; + if (X.RDB$SYSTEM_FLAG) + PUT_NUMERIC (att_relation_system_flag, X.RDB$SYSTEM_FLAG); + if (tdgbl->BCK_capabilities & BCK_ods8) + { + FOR (REQUEST_HANDLE req_handle2) + R IN RDB$RELATIONS WITH R.RDB$RELATION_NAME = X.RDB$RELATION_NAME + if (!(R.RDB$FLAGS.NULL)) + PUT_NUMERIC (att_relation_flags, R.RDB$FLAGS); + END_FOR; + ON_ERROR + general_on_error (); + END_ERROR; + } + if (tdgbl->BCK_capabilities & BCK_security) + { + FOR (REQUEST_HANDLE req_handle3) + R IN RDB$RELATIONS WITH R.RDB$RELATION_NAME = X.RDB$RELATION_NAME + if (!R.RDB$SECURITY_CLASS.NULL) + PUT_TEXT (att_relation_security_class, R.RDB$SECURITY_CLASS); + END_FOR; + ON_ERROR + general_on_error (); + END_ERROR; + } + put_source_blob (att_relation_description2, att_relation_description, (ISC_QUAD *)&X.RDB$DESCRIPTION); + put_source_blob (att_relation_view_source2, att_relation_view_source, (ISC_QUAD *)&X.RDB$VIEW_SOURCE); + if (tdgbl->BCK_capabilities & BCK_attributes_v3) + { + FOR (REQUEST_HANDLE req_handle4) + R IN RDB$RELATIONS WITH R.RDB$RELATION_NAME = X.RDB$RELATION_NAME + put_source_blob (att_relation_ext_description2, att_relation_ext_description, (ISC_QUAD *)&R.RDB$EXTERNAL_DESCRIPTION); + put_text (att_relation_owner_name, R.RDB$OWNER_NAME, 31); + if (!R.RDB$EXTERNAL_FILE.NULL) + { + if (!tdgbl->gbl_sw_convert_ext_tables) + { + put_text (att_relation_ext_file_name, R.RDB$EXTERNAL_FILE, 253); + flags |= REL_external; + } + } + END_FOR; + ON_ERROR + general_on_error (); + END_ERROR; + } + PUT (att_end); + relation = (REL) BURP_ALLOC_ZERO (sizeof (struct rel)); + relation->rel_next = tdgbl->relations; + tdgbl->relations = relation; + relation->rel_id = X.RDB$RELATION_ID; + relation->rel_name_length = copy (X.RDB$RELATION_NAME, relation->rel_name, GDS_NAME_LEN - 1); + relation->rel_flags |= flags; + put_relation (relation); + END_FOR; + ON_ERROR + general_on_error (); + END_ERROR; + } + + if (req_handle1) + isc_release_request(req_status, &req_handle1); + if (req_handle2) + isc_release_request(req_status, &req_handle2); + if (req_handle3) + isc_release_request(req_status, &req_handle3); + if (req_handle4) + isc_release_request(req_status, &req_handle4); +} + + +static void write_shadow_files(void) +{ +/************************************** + * + * w r i t e _ s h a d o w _ f i l e s + * + ************************************** + * + * Functional description + * Write out files to use as shadows. + * + **************************************/ + SSHORT l; + TEXT temp[32]; + isc_req_handle req_handle1 = NULL; + long req_status[20]; + TGBL tdgbl; + + tdgbl = GET_THREAD_DATA; + + FOR (REQUEST_HANDLE req_handle1) + X IN RDB$FILES + WITH X.RDB$SHADOW_NUMBER NOT MISSING + AND X.RDB$SHADOW_NUMBER NE 0 + PUT (rec_files); + l = PUT_TEXT (att_file_filename, X.RDB$FILE_NAME); + MISC_terminate (X.RDB$FILE_NAME, temp, l, sizeof (temp)); + BURP_verbose (163, temp, NULL, NULL, NULL, NULL); + /* msg 163 writing shadow file %s */ + PUT_NUMERIC (att_file_sequence, X.RDB$FILE_SEQUENCE); + PUT_NUMERIC (att_file_start, X.RDB$FILE_START); + PUT_NUMERIC (att_file_length, X.RDB$FILE_LENGTH); + PUT_NUMERIC (att_file_flags, X.RDB$FILE_FLAGS); + PUT_NUMERIC (att_shadow_number, X.RDB$SHADOW_NUMBER); + PUT (att_end); + END_FOR; + ON_ERROR + general_on_error (); + END_ERROR; + + if (req_handle1) + isc_release_request(req_status, &req_handle1); +} + +static void write_sql_roles(void) +{ +/************************************** + * + * w r i t e _ s q l _ r o l e s + * + ************************************** + * + * Functional description + * write a record in the burp file for + * each SQL roles. + * + **************************************/ + isc_req_handle req_handle1 = NULL; + long req_status[20]; + TGBL tdgbl; + TEXT temp[32]; + SSHORT l; + + tdgbl = GET_THREAD_DATA; + + FOR (REQUEST_HANDLE req_handle1) + X IN RDB$ROLES + + PUT (rec_sql_roles); + l = put_text (att_role_name, X.RDB$ROLE_NAME, 31); + PUT_TEXT (att_role_owner_name, X.RDB$OWNER_NAME); + PUT (att_end); + MISC_terminate (X.RDB$ROLE_NAME, temp, l, sizeof (temp)); + BURP_verbose (249, temp, NULL, NULL, NULL, NULL); + /* msg 249 writing SQL role: %s */ + + END_FOR; + ON_ERROR + general_on_error (); + END_ERROR; + + if (req_handle1) + isc_release_request(req_status, &req_handle1); +} + +static void write_triggers(void) +{ +/************************************** + * + * w r i t e _ t r i g g e r s + * + ************************************** + * + * Functional description + * write the triggers in rdb$triggers + * + **************************************/ + SSHORT l; + TEXT temp[32]; + isc_req_handle req_handle1 = NULL, req_handle2 = NULL; + long req_status[20]; + TGBL tdgbl; + + tdgbl = GET_THREAD_DATA; + + /* if we have all capabilities, use the first request to get the + most performance out of the latest engine; if we don't + have one of the capabilities we must use the second set of + requests--this requires more code but it is well worth it + for the performance benefits, especially remotely--deej */ + + if (tdgbl->BCK_capabilities & BCK_ods8) + { + FOR (REQUEST_HANDLE req_handle1) + X IN RDB$TRIGGERS WITH + X.RDB$SYSTEM_FLAG NE 1 OR + X.RDB$SYSTEM_FLAG MISSING + + PUT (rec_trigger); + l = PUT_TEXT (att_trig_name, X.RDB$TRIGGER_NAME); + MISC_terminate (X.RDB$TRIGGER_NAME, temp, l, sizeof (temp)); + BURP_verbose (156, temp, NULL, NULL, NULL, NULL); + /* msg 156 writing trigger %s */ + + PUT_TEXT (att_trig_relation_name, X.RDB$RELATION_NAME); + PUT_NUMERIC (att_trig_sequence, X.RDB$TRIGGER_SEQUENCE); + PUT_NUMERIC (att_trig_type, X.RDB$TRIGGER_TYPE); + put_blr_blob (att_trig_blr, (ISC_QUAD *)&X.RDB$TRIGGER_BLR); + put_source_blob (att_trig_source2, att_trig_source, (ISC_QUAD *)&X.RDB$TRIGGER_SOURCE); + put_source_blob (att_trig_description2, att_trig_description, (ISC_QUAD *)&X.RDB$DESCRIPTION); + PUT_NUMERIC (att_trig_system_flag, X.RDB$SYSTEM_FLAG); + PUT_NUMERIC (att_trig_inactive, X.RDB$TRIGGER_INACTIVE); + + if (!(X.RDB$FLAGS.NULL)) + PUT_NUMERIC (att_trig_flags, X.RDB$FLAGS); + + PUT (att_end); + + END_FOR; + ON_ERROR + general_on_error (); + END_ERROR; + } + else + { + FOR (REQUEST_HANDLE req_handle1) + X IN RDB$TRIGGERS WITH + X.RDB$SYSTEM_FLAG NE 1 OR + X.RDB$SYSTEM_FLAG MISSING + + PUT (rec_trigger); + l = PUT_TEXT (att_trig_name, X.RDB$TRIGGER_NAME); + MISC_terminate (X.RDB$TRIGGER_NAME, temp, l, sizeof (temp)); + BURP_verbose (156, temp, NULL, NULL, NULL, NULL); + /* msg 156 writing trigger %s */ + + PUT_TEXT (att_trig_relation_name, X.RDB$RELATION_NAME); + PUT_NUMERIC (att_trig_sequence, X.RDB$TRIGGER_SEQUENCE); + PUT_NUMERIC (att_trig_type, X.RDB$TRIGGER_TYPE); + put_blr_blob (att_trig_blr, (ISC_QUAD *)&X.RDB$TRIGGER_BLR); + put_source_blob (att_trig_source2, att_trig_source, (ISC_QUAD *)&X.RDB$TRIGGER_SOURCE); + put_source_blob (att_trig_description2, att_trig_description, (ISC_QUAD *)&X.RDB$DESCRIPTION); + PUT_NUMERIC (att_trig_system_flag, X.RDB$SYSTEM_FLAG); + PUT_NUMERIC (att_trig_inactive, X.RDB$TRIGGER_INACTIVE); + + if (tdgbl->BCK_capabilities & BCK_ods8) + { + FOR (REQUEST_HANDLE req_handle2) + Y IN RDB$TRIGGERS WITH + X.RDB$TRIGGER_NAME = Y.RDB$TRIGGER_NAME + + if (!(Y.RDB$FLAGS.NULL)) + PUT_NUMERIC (att_trig_flags, Y.RDB$FLAGS); + + END_FOR; + ON_ERROR + general_on_error (); + END_ERROR; + } + + PUT (att_end); + + END_FOR; + ON_ERROR + general_on_error (); + END_ERROR; + } + + if (req_handle1) + isc_release_request(req_status, &req_handle1); + if (req_handle2) + isc_release_request(req_status, &req_handle2); +} + + +static void write_trigger_messages(void) +{ +/************************************** + * + * w r i t e _ t r i g g e r _ m e s s a g e s + * + ************************************** + * + * Functional description + * write a record in the burp file for + * each trigger message. + * + **************************************/ + SSHORT l; + TEXT temp[32]; + isc_req_handle req_handle1 = NULL; + long req_status[20]; + TGBL tdgbl; + + tdgbl = GET_THREAD_DATA; + + FOR (REQUEST_HANDLE req_handle1) + T IN RDB$TRIGGERS CROSS X IN RDB$TRIGGER_MESSAGES + OVER RDB$TRIGGER_NAME + WITH T.RDB$SYSTEM_FLAG NE 1 OR T.RDB$SYSTEM_FLAG MISSING; + + PUT (rec_trigger_message); + l = PUT_TEXT (att_trigmsg_name, X.RDB$TRIGGER_NAME); + MISC_terminate (X.RDB$TRIGGER_NAME, temp, l, sizeof (temp)); + BURP_verbose (157, temp, NULL, NULL, NULL, NULL); + /* msg 157 writing trigger message for *s */ + PUT_NUMERIC (att_trigmsg_number, X.RDB$MESSAGE_NUMBER); + PUT_MESSAGE (att_trigmsg_text, X.RDB$MESSAGE); + PUT (att_end); + END_FOR; + ON_ERROR + general_on_error (); + END_ERROR; + + if (req_handle1) + isc_release_request(req_status, &req_handle1); +} + +static void write_types(void) +{ +/************************************** + * + * w r i t e _ t y p e s + * + ************************************** + * + * Functional description + * write a record in the burp file for + * each type. + * + **************************************/ + isc_req_handle req_handle1 = NULL; + long req_status[20]; + TGBL tdgbl; + + tdgbl = GET_THREAD_DATA; + + FOR (REQUEST_HANDLE req_handle1) + X IN RDB$TYPES WITH X.RDB$SYSTEM_FLAG NE 1 OR + X.RDB$SYSTEM_FLAG MISSING + PUT (rec_type); + PUT_TEXT (att_type_name, X.RDB$TYPE_NAME); + PUT_TEXT (att_type_field_name, X.RDB$FIELD_NAME); + BURP_verbose (160, X.RDB$TYPE_NAME, X.RDB$FIELD_NAME, NULL, NULL, NULL); + /* msg 160 writing type %s for field %s */ + PUT_NUMERIC (att_type_type, X.RDB$TYPE); + put_source_blob (att_type_description2, att_type_description, (ISC_QUAD *)&X.RDB$DESCRIPTION); + if (X.RDB$SYSTEM_FLAG) + PUT_NUMERIC (att_type_system_flag, X.RDB$SYSTEM_FLAG); + PUT (att_end); + END_FOR; + ON_ERROR + general_on_error (); + END_ERROR; + + if (req_handle1) + isc_release_request(req_status, &req_handle1); +} + + +static void write_user_privileges(void) +{ +/************************************** + * + * w r i t e _ u s e r _ p r i v i l e g e s + * + ************************************** + * + * Functional description + * write a record in the burp file for + * each user privilege. + * + **************************************/ + SSHORT l; + TEXT temp[32]; + isc_req_handle req_handle1 = NULL; + long req_status[20]; + TGBL tdgbl; + + tdgbl = GET_THREAD_DATA; + + if (tdgbl->BCK_capabilities & BCK_ods8) + { + FOR (REQUEST_HANDLE req_handle1) + X IN RDB$USER_PRIVILEGES + PUT (rec_user_privilege); + l = PUT_TEXT (att_priv_user, X.RDB$USER); + MISC_terminate (X.RDB$USER, temp, l, sizeof(temp)); + BURP_verbose (152, temp, NULL, NULL, NULL, NULL); + /* msg 152 writing privilege for user %s */ + PUT_TEXT (att_priv_grantor, X.RDB$GRANTOR); + PUT_TEXT (att_priv_privilege, X.RDB$PRIVILEGE); + PUT_NUMERIC (att_priv_grant_option, X.RDB$GRANT_OPTION); + PUT_TEXT (att_priv_object_name, X.RDB$RELATION_NAME); + if (!X.RDB$FIELD_NAME.NULL) + PUT_TEXT (att_priv_field_name, X.RDB$FIELD_NAME); + PUT_NUMERIC (att_priv_user_type, X.RDB$USER_TYPE); + PUT_NUMERIC (att_priv_obj_type, X.RDB$OBJECT_TYPE); + PUT (att_end); + END_FOR + ON_ERROR + general_on_error (); + END_ERROR; + } + else + { + FOR (REQUEST_HANDLE req_handle1) + X IN RDB$USER_PRIVILEGES + PUT (rec_user_privilege); + l = PUT_TEXT (att_priv_user, X.RDB$USER); + MISC_terminate (X.RDB$USER, temp, l, sizeof(temp)); + BURP_verbose (152, temp, NULL, NULL, NULL, NULL); + /* msg 152 writing privilege for user %s */ + PUT_TEXT (att_priv_grantor, X.RDB$GRANTOR); + PUT_TEXT (att_priv_privilege, X.RDB$PRIVILEGE); + PUT_NUMERIC (att_priv_grant_option, X.RDB$GRANT_OPTION); + PUT_TEXT (att_priv_object_name, X.RDB$RELATION_NAME); + if (!X.RDB$FIELD_NAME.NULL) + PUT_TEXT (att_priv_field_name, X.RDB$FIELD_NAME); + PUT (att_end); + END_FOR; + ON_ERROR + general_on_error (); + END_ERROR; + } + + if (req_handle1) + isc_release_request(req_status, &req_handle1); +} diff --git a/src/burp/burp.cpp b/src/burp/burp.cpp new file mode 100644 index 0000000000..699d8632de --- /dev/null +++ b/src/burp/burp.cpp @@ -0,0 +1,2258 @@ +/* + * PROGRAM: JRD Backup and Restore Program + * MODULE: burp.c + * DESCRIPTION: Command line interpreter for backup/restore + * + * The contents of this file are subject to the Interbase Public + * License Version 1.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy + * of the License at http://www.Inprise.com/IPL.html + * + * Software distributed under the License is distributed on an + * "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either express + * or implied. See the License for the specific language governing + * rights and limitations under the License. + * + * The Original Code was created by Inprise Corporation + * and its predecessors. Portions created by Inprise Corporation are + * Copyright (C) Inprise Corporation. + * + * All Rights Reserved. + * Contributor(s): ______________________________________. + */ + +#include "../jrd/ib_stdio.h" +#include +#include +#include +#include "../jrd/common.h" +#include "../jrd/ibase.h" +#include +#include "../jrd/ibsetjmp.h" +#include "../jrd/msg_encode.h" +#include "../jrd/thd_proto.h" +#include "../jrd/ods.h" // to get MAX_PAGE_SIZE +#include "../burp/burp.h" +#include "../burp/burpswi.h" +#ifdef WIN_NT +#include +#undef TEXT +#include +#define TEXT char +#endif + +/* +** Had to do this messy looking stuff to get the correct version string for +** Local Interbase Beta releases. +*/ +#if (defined WIN_NT && defined WIN95) +#define SUPERCLIENT +#endif +#include "../jrd/license.h" +#if (defined WIN_NT && defined WIN95) +#undef SUPERCLIENT +#undef WIN95 +#endif + +#include "../jrd/time.h" +#include "../burp/burp_proto.h" +#include "../burp/backu_proto.h" +#include "../burp/mvol_proto.h" +#include "../burp/resto_proto.h" +#include "../jrd/gds_proto.h" +#include "../jrd/why_proto.h" +#include "../jrd/gdsassert.h" + +#ifdef SUPERSERVER +#include "../utilities/cmd_util_proto.h" +#endif + + +#ifdef UNIX +#include +#endif + +#ifdef VMS +#include +#include +#include +#include +#define SWITCH_CHAR "/" +#endif + +#ifndef VMS +#include +#endif + +#ifdef PC_PLATFORM +#include +#include +#include +#endif + +#if (defined WIN_NT) +#include +#endif + +#ifdef EPSON +#include +#endif + +#ifndef O_CREAT +#include +#include +#endif + +#ifdef NETWARE_386 +#define MASK 0 +#else +struct tgbl *gdgbl; +#endif + +#ifndef FOPEN_WRITE_TYPE +#define FOPEN_WRITE_TYPE "w" +#endif + +#ifndef FOPEN_READ_TYPE +#define FOPEN_READ_TYPE "r" +#endif + +#ifndef MASK +#define MASK 0666 +#endif + +#ifndef SWITCH_CHAR +#define SWITCH_CHAR "-" +#endif + + +extern "C" { + + +#define OUTPUT_SUPPRESS "SUPPRESS" +#define BURP_MSG_FAC 12 + +static void close_out_transaction(VOLATILE SSHORT, isc_tr_handle *); +static void enable_signals(void); +static void excp_handler(void); +static SLONG get_number(SCHAR *); +static ULONG get_size(SCHAR *, FIL); +static SSHORT open_files(SCHAR *, SCHAR **, USHORT, USHORT, USHORT); +static int output_netware(SLONG, UCHAR *); +static int output_main(SLONG, UCHAR *); +static int output_svc(SLONG, UCHAR *); +static void burp_output(CONST SCHAR *, ...); + +#ifndef SUPERSERVER +#ifndef GUI_TOOLS +static int api_gbak(int, char**, USHORT, TEXT*, TEXT*, TEXT *, BOOLEAN, BOOLEAN); +#endif +#endif + +#define QUIT 0 +#define BACKUP 1 +#define RESTORE 2 +#define FDESC 3 + +#define DB tdgbl->db_handle + +#define GBAK_STDIN_DESC (int)0 +#define GBAK_STDOUT_DESC (int)1 + +#define KBYTE 1024 +#define MBYTE KBYTE * KBYTE +#define GBYTE MBYTE * KBYTE + +#if defined (WIN95) && !defined (GUI_TOOLS) +static BOOL fAnsiCP = FALSE; +#define TRANSLATE_CP(a) if (!fAnsiCP) AnsiToOem(a, a) +#else +#define TRANSLATE_CP(a) +#endif + + +#ifdef SUPERSERVER +int main_gbak(SVC service) +{ +/************************************** + * + * m a i n _ g b a k + * + ************************************** + * + * Functional description + * Netware entry point for GBAK. + * + **************************************/ + int exit_code; + + exit_code = BURP_gbak(service->svc_argc, service->svc_argv, + output_netware, (SLONG) service); + + service->svc_handle = 0; + if (service->svc_service->in_use != NULL) + *(service->svc_service->in_use) = FALSE; + +/* Mark service thread as finished. */ +/* If service is detached, cleanup memory being used by service. */ + SVC_finish(service, SVC_finished); + + return exit_code; +} + + +static int output_netware(SLONG output_data, UCHAR* output_buf) +{ +/************************************** + * + * o u t p u t _ n e t w a r e + * + ************************************** + * + * Functional description + * Routine which is passed to GBAK for calling back when there is output. + * + **************************************/ + SVC_fprintf((SVC) output_data, "%s", output_buf); + + return 0; +} + + +void BURP_svc_error(USHORT errcode, + USHORT arg1_t, + void *arg1, + USHORT arg2_t, + void *arg2, + USHORT arg3_t, + void *arg3, + USHORT arg4_t, + void *arg4, + USHORT arg5_t, + void *arg5) +{ +/************************************** + * + * B U R P _ s v c _ e r r o r + * + ************************************** + * + * Functional description + * + **************************************/ + TGBL tdgbl; + STATUS *status; + + tdgbl = GET_THREAD_DATA; + + status = tdgbl->service_blk->svc_status; + + CMD_UTIL_put_svc_status(status, BURP_MSG_FAC, errcode, + arg1_t, arg1, arg2_t, arg2, arg3_t, arg3, + arg4_t, arg4, arg5_t, arg5); + + SVC_STARTED(tdgbl->service_blk); + BURP_msg_partial(256, 0, 0, 0, 0, 0); /* msg 256: gbak: ERROR: */ + BURP_msg_put(errcode, + reinterpret_cast(arg1), + reinterpret_cast(arg2), + reinterpret_cast(arg3), + reinterpret_cast(arg4), + reinterpret_cast(arg5)); + BURP_abort(); +} + + +#else // SUPERSERVER + +#if !defined(GUI_TOOLS) + +int CLIB_ROUTINE main(int argc, char* argv[]) +{ +/************************************** + * + * m a i n + * + ************************************** + * + * Functional description + * Parse and interpret command line, then "do the right thing." + * + **************************************/ + int exit_code; + USHORT total; + TEXT **end, **argvp, *string, *p, *q, c; + IN_SW_TAB in_sw_tab; + BOOLEAN flag_restore, flag_verbose, err; + TEXT *sw_user, *sw_password, *sw_service; + TEXT *d_user, *d_password, *d_service; + +/* If a "-service" switch is specified then use Admin APIs */ + argvp = argv; + end = argvp + argc; + argvp++; + +/* Initialize data */ + total = 0; + flag_restore = flag_verbose = err = FALSE; + sw_user = sw_password = sw_service = d_user = d_password = d_service = NULL; + +/* Parse the command line for the -USER, -PASSWORD, -SERVICE, + and -VERBOSE options. Calculate a length for the new command line to be + passed to a server using services APIs */ + + while (argvp < end && !err) + { + string = *argvp++; + if (*string != '-') { + total += strlen(string) + 1; + continue; + } + if (!string[1]) + string = "-*NONE*"; + for (in_sw_tab = burp_in_sw_table; + q = in_sw_tab->in_sw_name; + in_sw_tab++) + { + for (p = string + 1; c = *p++;) + if (UPPER(c) != *q++) + break; + if (!c) + break; + } + switch (in_sw_tab->in_sw) + { + case IN_SW_BURP_C: /* create database */ + case IN_SW_BURP_R: /* replace database */ + total += strlen(string) + 1; + flag_restore = TRUE; + break; + case IN_SW_BURP_USER: /* default user name */ + if (argvp >= end) + err = TRUE; + else { + sw_user = string; + d_user = *argvp++; + } + break; + case IN_SW_BURP_PASS: /* default password */ + if (argvp >= end) + err = TRUE; + else { + sw_password = string; + d_password = *argvp++; + } + break; + case IN_SW_BURP_SE: /* service name */ + if (argvp >= end) { + err = TRUE; + } else { + sw_service = string; + d_service = *argvp++; + } + break; + case IN_SW_BURP_V: /* verify actions */ + total += strlen(string) + 1; + flag_verbose = TRUE; + break; + default: + total += strlen(string) + 1; + break; + } + } + + if (sw_service && !err) + { + /* Backup/restore operations will be running as a service thread. + * To make this more efficiently the isc_spb_options is used. + * This allows us to skip a conversion from the gbak command line + * switches to service parameter block in here as well as vice versa + * conversion within svc.c + * + * If -USER and -PASSWORD switches are used by the user within + * the gbak command line then we have to eliminate them from there. The + * password will be encrypted and added along with the user name + * within SVC_start function later on. We shall also eliminate + * the -SERVER switch because the switch has already been processed. + */ + + if (sw_user) + *sw_user = '\0'; + if (sw_password) + *sw_password = '\0'; + if (sw_service) + *sw_service = '\0'; + + exit_code = api_gbak(argc, argv, total, d_password, + d_user, d_service, flag_restore, flag_verbose); + } + else + exit_code = BURP_gbak(argc, argv, output_main, (SLONG) NULL); + + return exit_code; +} + + +static int output_main( SLONG output_data, UCHAR * output_buf) +{ +/************************************** + * + * o u t p u t _ m a i n + * + ************************************** + * + * Functional description + * Routine which is passed to GBAK for calling back when there is output. + * + **************************************/ + ib_fprintf(ib_stderr, "%s", output_buf); + return 0; +} + +#endif // !GUI_TOOLS + +#endif // SUPERSERVER + + +static int output_svc( SLONG output_data, UCHAR * output_buf) +{ +/************************************** + * + * o u t p u t _ s v c + * + ************************************** + * + * Functional description + * Routine which is passed to GBAK for calling back when there is output + * if gbak is run as a service + * + **************************************/ + ib_fprintf(ib_stdout, "%s", output_buf); + return 0; +} + +int DLL_EXPORT BURP_gbak(int argc, + char* argv[], + OUTPUTPROC output_proc, + SLONG output_data) +{ +/************************************** + * + * B U R P _ g b a k + * + ************************************** + * + * Functional description + * Routine called by command line utility, services API, and server manager. + * + **************************************/ + TEXT *file1, **end, *string, *p, *q, c, *device, *redirect; +/* This function runs within thread for services API, so here should not be + *any* static variables. I did not change an existing definition + for AIX PowerPC because of the problem (see comments below). So + whoever will do a port on AIX, must reconsider a static definition */ +#ifdef AIX_PPC + static TEXT *file2; /* SomeHow, making this VOLATILE does'nt give the + desired value in case of AIX PowerPC */ +#else + TEXT *file2; +#endif + UCHAR *dpb; + + IN_SW_TAB in_sw_tab; + FIL file, file_list, next_file; + int temp, result; + SLONG clock; + SLONG redir_in, redir_out, redir_err; + VOLATILE SSHORT action = QUIT; + BOOLEAN sw_ods7 = FALSE; + USHORT sw_replace; + USHORT sw_tape; + VOLATILE struct tgbl* tdgbl; + JMP_BUF env; + IB_FILE* tmp_outfile; + +/* TMN: This variable should probably be removed, but I left it in */ +/* in case some platform should redefine the BURP SET_THREAD_DATA. */ +/*struct tgbl thd_context;*/ + + tdgbl = (struct tgbl *) gds__alloc(sizeof(*tdgbl)); +/* NOMEM: return error, FREE: during function exit in the SETJMP */ + if (tdgbl == NULL) + { + SVC service; + service = (SVC) output_data; + SVC_STARTED(service); + return FINI_ERROR; + } + + SET_THREAD_DATA; + SVC_PUTSPECIFIC_DATA; + memset((void *) tdgbl, 0, sizeof(*tdgbl)); + tdgbl->burp_env = reinterpret_cast(env); + tdgbl->file_desc = INVALID_HANDLE_VALUE; + tdgbl->output_proc = output_proc; + tdgbl->output_data = output_data; + +/* Initialize static data. */ + for (in_sw_tab = burp_in_sw_table; in_sw_tab->in_sw_name; in_sw_tab++) + in_sw_tab->in_sw_state = FALSE; + + if (SETJMP(env)) { + int exit_code; + UCHAR *mem; + + /* All calls to EXIT(), normal and error exits, wind up here */ + + tdgbl->burp_env = NULL; + exit_code = tdgbl->exit_code; + + /* Close the gbak file handles if they still open */ + for (file = tdgbl->gbl_sw_backup_files; file; file = file->fil_next) { + if (file->fil_fd != INVALID_HANDLE_VALUE) + CLOSE(file->fil_fd); + if (exit_code != 0 + && (tdgbl->action->act_action == ACT_backup_split + || tdgbl->action->act_action == ACT_backup)) + UNLINK(file->fil_name); + } + + /* Detach from database to release system resources */ + if (tdgbl->db_handle != 0) { + close_out_transaction(action, + const_cast(&tdgbl->tr_handle)); + close_out_transaction(action, + const_cast(&tdgbl->global_trans)); + if (isc_detach_database(const_cast(tdgbl->status_vector), + const_cast(&tdgbl->db_handle))) + { + BURP_print_status(const_cast(tdgbl->status_vector)); + } + } + + /* Close the status output file */ + if (tdgbl->sw_redirect == TRUE && tdgbl->output_file != NULL) { + ib_fclose(tdgbl->output_file); + tdgbl->output_file = NULL; + } + + /* Free all unfreed memory used by Gbak itself */ + while (tdgbl->head_of_mem_list != NULL) { + mem = tdgbl->head_of_mem_list; + tdgbl->head_of_mem_list = *((UCHAR **) tdgbl->head_of_mem_list); + gds__free(mem); + } + + RESTORE_THREAD_DATA; + if (tdgbl != NULL) { + gds__free((SLONG *) tdgbl); + } + +#if defined(DEBUG_GDS_ALLOC) && !defined(SUPERSERVER) + gds_alloc_report(0, __FILE__, __LINE__); +#endif + + /* All returns occur from this point - even normal returns */ + return exit_code; + } + +#ifdef VMS + argc = VMS_parse(&argv, argc); +#endif + +/* Perform some special handling when run as an Interbase service. The + first switch can be "-svc" (lower case!) or it can be "-svc_re" followed + by 3 file descriptors to use in re-directing stdin, stdout, and stderr. + + If this utility is started as a thread in the engine, then the first switch + will be "-svc_thd". +*/ + + tdgbl->gbl_sw_service_gbak = FALSE; + tdgbl->gbl_sw_service_thd = FALSE; + tdgbl->service_blk = NULL; + tdgbl->status = const_cast(tdgbl->status_vector); + + if (argc > 1 && !strcmp(argv[1], "-svc")) { + tdgbl->gbl_sw_service_gbak = TRUE; + argv++; + argc--; + } + else if (argc > 1 && !strcmp(argv[1], "-svc_thd")) { + tdgbl->gbl_sw_service_gbak = TRUE; + tdgbl->gbl_sw_service_thd = TRUE; + tdgbl->service_blk = (SVC) output_data; + tdgbl->status = tdgbl->service_blk->svc_status; + argv++; + argc--; + } +#ifndef NETWARE_386 + else if (argc > 4 && !strcmp(argv[1], "-svc_re")) { + tdgbl->gbl_sw_service_gbak = TRUE; + tdgbl->output_proc = output_svc; + redir_in = atol(argv[2]); + redir_out = atol(argv[3]); + redir_err = atol(argv[4]); +#ifdef WIN_NT +#if defined (WIN95) && !defined (GUI_TOOLS) + fAnsiCP = TRUE; +#endif + redir_in = _open_osfhandle(redir_in, 0); + redir_out = _open_osfhandle(redir_out, 0); + redir_err = _open_osfhandle(redir_err, 0); +#endif + if (redir_in != 0) + if (dup2((int) redir_in, 0)) + close((int) redir_in); + if (redir_out != 1) + if (dup2((int) redir_out, 1)) + close((int) redir_out); + if (redir_err != 2) + if (dup2((int) redir_err, 2)) + close((int) redir_err); + argv += 4; + argc -= 4; + } +#endif + +#if defined (WIN95) && !defined (GUI_TOOLS) + if (!fAnsiCP) + fAnsiCP = (GetConsoleCP() == GetACP()); +#endif + + + sw_replace = sw_tape = FALSE; + + tdgbl->gbl_sw_compress = TRUE; + tdgbl->gbl_sw_convert_ext_tables = FALSE; + tdgbl->gbl_sw_transportable = TRUE; + tdgbl->gbl_sw_ignore_limbo = FALSE; + tdgbl->gbl_sw_blk_factor = 0; + tdgbl->gbl_sw_no_reserve = FALSE; +#ifdef READONLY_DATABASE + tdgbl->gbl_sw_mode = FALSE; +#endif /* READONLY_DATABASE */ + tdgbl->gbl_sw_skip_count = 0; + tdgbl->gbl_sw_bug8183 = FALSE; + tdgbl->action = NULL; + dpb = const_cast(tdgbl->dpb_string); + tdgbl->dpb_length = 0; + file1 = file2 = NULL; + file = file_list = NULL; + tdgbl->io_buffer_size = GBAK_IO_BUFFER_SIZE; + end = argv + argc; + ++argv; + + while (argv < end) { + string = *argv; + temp = strlen(string) - 1; + if (string[temp] == ',') + string[temp] = '\0'; + + if (*string != '-') { + if (!file || file->fil_length || !get_size(*argv, file)) { + /* Miserable thing must be a filename + (dummy in a length for the backup file */ + + file = (FIL) BURP_ALLOC_ZERO(FIL_LEN); + file->fil_name = string; + file->fil_fd = INVALID_HANDLE_VALUE; + if (!file_list) + file->fil_length = MAX_LENGTH; + else + file->fil_length = 0; + file->fil_next = file_list; + file_list = file; + } + argv++; + } + else { + ++argv; + if (!string[1]) + string = "-*NONE*"; + for (in_sw_tab = burp_in_sw_table; q = in_sw_tab->in_sw_name; + in_sw_tab++) { + for (p = string + 1; c = *p++;) + if (UPPER(c) != *q++) + break; + if (!c) + break; + } + in_sw_tab->in_sw_state = TRUE; + if (!in_sw_tab->in_sw) { + BURP_print(137, string + 1, 0, 0, 0, 0); /* msg 137 unknown switch %s */ + BURP_print(95, 0, 0, 0, 0, 0); /* msg 95 legal switches are */ + for (in_sw_tab = burp_in_sw_table; in_sw_tab->in_sw; + in_sw_tab++) + if (in_sw_tab->in_sw_msg) { + BURP_msg_put(in_sw_tab->in_sw_msg, SWITCH_CHAR, 0, 0, + 0, 0); + } + + BURP_print(132, 0, 0, 0, 0, 0); /* msg 132 switches can be abbreviated to one character */ + BURP_error(1, 0, 0, 0, 0, 0); /* msg 1: found unknown switch */ + } + else if (in_sw_tab->in_sw == IN_SW_BURP_S) { + if (argv >= end) + BURP_error(200, 0, 0, 0, 0, 0); + /* msg 200: missing parameter for the number of bytes to be skipped */ + tdgbl->gbl_sw_skip_count = get_number(*argv); + if (!tdgbl->gbl_sw_skip_count) + BURP_error(201, *argv, 0, 0, 0, 0); + /* msg 201: expected number of bytes to be skipped, encountered "%s" */ + argv++; + } + else if (in_sw_tab->in_sw == IN_SW_BURP_P) { + if (argv >= end) + BURP_error(2, 0, 0, 0, 0, 0); /* msg 2 page size parameter missing */ + tdgbl->gbl_sw_page_size = (USHORT) get_number(*argv); + if (!tdgbl->gbl_sw_page_size) + BURP_error(12, *argv, 0, 0, 0, 0); /* msg 12 expected page size, encountered "%s" */ + argv++; + } + else if (in_sw_tab->in_sw == IN_SW_BURP_BU) { + if (argv >= end) + BURP_error(258, 0, 0, 0, 0, 0); /* msg 258 page buffers parameter missing */ + tdgbl->gbl_sw_page_buffers = get_number(*argv); + if (!tdgbl->gbl_sw_page_buffers) + BURP_error(259, *argv, 0, 0, 0, 0); /* msg 259 expected page buffers, encountered "%s" */ + argv++; + } +#ifdef READONLY_DATABASE + else if (in_sw_tab->in_sw == IN_SW_BURP_MODE) { + if (argv >= end) + BURP_error(279, 0, 0, 0, 0, 0); /* msg 279: "read_only" or "read_write" required */ + string = *argv++; + if (!strcmp(string, BURP_SW_MODE_RO)) + tdgbl->gbl_sw_mode_val = TRUE; + else if (!strcmp(string, BURP_SW_MODE_RW)) + tdgbl->gbl_sw_mode_val = FALSE; + else + BURP_error(279, 0, 0, 0, 0, 0); /* msg 279: "read_only" or "read_write" required */ + tdgbl->gbl_sw_mode = TRUE; + } +#endif /* READONLY_DATABASE */ + else if (in_sw_tab->in_sw == IN_SW_BURP_PASS) { + if (argv >= end) + BURP_error(189, 0, 0, 0, 0, 0); /* password parameter missing */ + tdgbl->gbl_sw_password = *argv++; + } + else if (in_sw_tab->in_sw == IN_SW_BURP_USER) { + if (argv >= end) + BURP_error(188, 0, 0, 0, 0, 0); /* user name parameter missing */ + tdgbl->gbl_sw_user = *argv++; + } + else if (in_sw_tab->in_sw == IN_SW_BURP_ROLE) { + if (argv >= end) + BURP_error(253, 0, 0, 0, 0, 0); /* SQL role parameter missing */ + tdgbl->gbl_sw_sql_role = *argv++; + } + else if (in_sw_tab->in_sw == IN_SW_BURP_FA) { + if (argv >= end) + BURP_error(182, 0, 0, 0, 0, 0); /* msg 182 blocking factor parameter missing */ + tdgbl->gbl_sw_blk_factor = + (volatile USHORT) get_number(*argv); + if (!tdgbl->gbl_sw_blk_factor) + BURP_error(183, *argv, 0, 0, 0, 0); /* msg 183 expected blocking factor, encountered "%s" */ + argv++; + } + else if (in_sw_tab->in_sw == IN_SW_BURP_SE) { + if (argv >= end) { + BURP_error(273, 0, 0, 0, 0, 0); /* msg 273: service name parameter missing */ + } + in_sw_tab->in_sw_state = FALSE; + ++argv; /* skip a service specification */ + } + else if (in_sw_tab->in_sw == IN_SW_BURP_D) { + device = *argv; + if (argv >= end) /* device may equal NULL */ + device = NULL; + else if (*device == '-') + device = NULL; + else + ++argv; + } + /* want to do output redirect handling now instead of waiting */ + else if (in_sw_tab->in_sw == IN_SW_BURP_Y) { + redirect = *argv; + if (argv >= end) /* redirect may equal NULL */ + redirect = NULL; + else if (*redirect == '-') + redirect = NULL; + else + ++argv; + if (!redirect) + BURP_error(4, 0, 0, 0, 0, 0); /* msg 4 redirect location for output is not specified */ + + p = redirect; + string = OUTPUT_SUPPRESS; + tdgbl->sw_redirect = NOOUTPUT; + while (c = *p++) { + if (UPPER(c) != *string++) { + tdgbl->sw_redirect = TRUE; + break; + } + } + if (tdgbl->sw_redirect == TRUE) { /* not FALSE, and not NOOUTPUT */ + + /* Make sure the status file doesn't already exist */ + if (tmp_outfile = ib_fopen(redirect, FOPEN_READ_TYPE)) { + BURP_print(66, redirect, 0, 0, 0, 0); + /* msg 66 can't open status and error output file %s */ + ib_fclose(tmp_outfile); + EXIT(FINI_ERROR); + } + if (! + (tdgbl->output_file = + ib_fopen(redirect, FOPEN_WRITE_TYPE))) { + BURP_print(66, redirect, 0, 0, 0, 0); + /* msg 66 can't open status and error output file %s */ + EXIT(FINI_ERROR); + } + } + } /*else if (in_sw_tab->in_sw == IN_SW_BURP_Y) */ + } /* else */ + } /* while (argv < end) */ + +/* reverse the linked list of file blocks */ + + tdgbl->gbl_sw_files = NULL; + + for (file = file_list; file; file = next_file) { + next_file = file->fil_next; + file->fil_next = tdgbl->gbl_sw_files; + tdgbl->gbl_sw_files = file; + } + +/* pop off the obviously boring ones, plus do some checking */ + + for (file = tdgbl->gbl_sw_files; file; file = file->fil_next) { + if (!file1) + file1 = file->fil_name; + else if (!file2) + file2 = file->fil_name; + for (file_list = file->fil_next; file_list; + file_list = file_list->fil_next) { + if (!strcmp(file->fil_name, file_list->fil_name)) + BURP_error(9, 0, 0, 0, 0, 0); /* msg 9 mutiple sources or destinations specified */ + } + + } + +/* Initialize 'dpb' and 'dpb_length' */ + *dpb++ = gds_dpb_version1; + *dpb++ = isc_dpb_gbak_attach; + *dpb++ = strlen(GDS_VERSION); + for (q = GDS_VERSION; *q;) + *dpb++ = *q++; + tdgbl->dpb_length = dpb - tdgbl->dpb_string; + + for (in_sw_tab = burp_in_sw_table; in_sw_tab->in_sw_name; in_sw_tab++) { + if (in_sw_tab->in_sw_state) { + switch (in_sw_tab->in_sw) { + case (IN_SW_BURP_B): + if (sw_replace) + BURP_error(5, 0, 0, 0, 0, 0); /* msg 5 conflicting switches for backup/restore */ + sw_replace = IN_SW_BURP_B; + break; + + case (IN_SW_BURP_C): + if (sw_replace == IN_SW_BURP_B) + BURP_error(5, 0, 0, 0, 0, 0); /* msg 5 conflicting switches for backup/restore */ + if (sw_replace != IN_SW_BURP_R) + sw_replace = IN_SW_BURP_C; + break; + + case (IN_SW_BURP_CO): + tdgbl->gbl_sw_convert_ext_tables = TRUE; + break; + + case (IN_SW_BURP_E): + tdgbl->gbl_sw_compress = FALSE; + break; + + case (IN_SW_BURP_G): + if (!tdgbl->dpb_length) + *dpb++ = gds_dpb_version1; + *dpb++ = gds_dpb_no_garbage_collect; + *dpb++ = 0; + tdgbl->dpb_length = dpb - tdgbl->dpb_string; + break; + + case (IN_SW_BURP_I): + tdgbl->gbl_sw_deactivate_indexes = TRUE; + break; + + case (IN_SW_BURP_IG): + if (!tdgbl->dpb_length) + *dpb++ = gds_dpb_version1; + *dpb++ = gds_dpb_damaged; + *dpb++ = 1; + *dpb++ = 1; + tdgbl->dpb_length = dpb - tdgbl->dpb_string; + break; + + case (IN_SW_BURP_K): + tdgbl->gbl_sw_kill = TRUE; + break; + + case (IN_SW_BURP_L): + tdgbl->gbl_sw_ignore_limbo = TRUE; + break; + + case (IN_SW_BURP_M): + tdgbl->gbl_sw_meta = TRUE; + break; + +#ifdef READONLY_DATABASE + case (IN_SW_BURP_MODE): + tdgbl->gbl_sw_mode = TRUE; + break; +#endif /* READONLY_DATABASE */ + + case (IN_SW_BURP_N): + tdgbl->gbl_sw_novalidity = TRUE; + break; + + case (IN_SW_BURP_NT): /* Backup non-transportable format */ + tdgbl->gbl_sw_transportable = FALSE; + break; + + case (IN_SW_BURP_O): + tdgbl->gbl_sw_incremental = TRUE; + break; + + case (IN_SW_BURP_OL): + tdgbl->gbl_sw_old_descriptions = TRUE; + break; + + case (IN_SW_BURP_PASS): + if (!tdgbl->dpb_length) + *dpb++ = gds_dpb_version1; + if (!tdgbl->gbl_sw_service_thd) + *dpb++ = gds_dpb_password; + else + *dpb++ = gds_dpb_password_enc; + *dpb++ = strlen(tdgbl->gbl_sw_password); + for (q = tdgbl->gbl_sw_password; *q;) + *dpb++ = *q++; + tdgbl->dpb_length = dpb - tdgbl->dpb_string; + break; + + case (IN_SW_BURP_R): + if (sw_replace == IN_SW_BURP_B) + BURP_error(5, 0, 0, 0, 0, 0); /* msg 5 conflicting switches for backup/restore */ + sw_replace = IN_SW_BURP_R; + break; + + case (IN_SW_BURP_T): + tdgbl->gbl_sw_transportable = TRUE; + break; + + case (IN_SW_BURP_U): + BURP_error(7, 0, 0, 0, 0, 0); /* msg 7 protection isn't there yet */ + break; + + case (IN_SW_BURP_US): + tdgbl->gbl_sw_no_reserve = TRUE; + break; + + case (IN_SW_BURP_ROLE): + if (!tdgbl->dpb_length) + *dpb++ = isc_dpb_version1; + *dpb++ = isc_dpb_sql_role_name; + *dpb++ = strlen(tdgbl->gbl_sw_sql_role); + for (q = tdgbl->gbl_sw_sql_role; *q;) + *dpb++ = *q++; + tdgbl->dpb_length = dpb - tdgbl->dpb_string; + break; + + case (IN_SW_BURP_USER): + if (!tdgbl->dpb_length) + *dpb++ = gds_dpb_version1; + *dpb++ = gds_dpb_user_name; + *dpb++ = strlen(tdgbl->gbl_sw_user); + for (q = tdgbl->gbl_sw_user; *q;) + *dpb++ = *q++; + tdgbl->dpb_length = dpb - tdgbl->dpb_string; + break; + + case (IN_SW_BURP_V): + tdgbl->gbl_sw_verbose = TRUE; + break; + + case (IN_SW_BURP_Z): + BURP_print(91, GDS_VERSION, 0, 0, 0, 0); /* msg 91 gbak version %s */ + tdgbl->gbl_sw_version = TRUE; + break; + +#ifdef DEV_BUILD + case (IN_SW_BURP_7): + sw_ods7 = TRUE; + break; +#endif + case (IN_SW_BURP_BUG8183): + tdgbl->gbl_sw_bug8183 = TRUE; + break; + + default: + break; + } + } + } + + if (!sw_replace) + sw_replace = IN_SW_BURP_B; + + if (tdgbl->gbl_sw_page_size) + { + if (sw_replace == IN_SW_BURP_B) + BURP_error(8, 0, 0, 0, 0, 0); /* msg 8 page size is allowed only on restore or create */ + temp = tdgbl->gbl_sw_page_size; + { + int curr_pg_size = 1024; + while (curr_pg_size <= MAX_PAGE_SIZE) { + if (temp <= curr_pg_size) { + temp = curr_pg_size; + break; + } + curr_pg_size <<= 1; + } + } + if (temp > MAX_PAGE_SIZE) + { +#ifdef SUPERSERVER + BURP_svc_error( 3, + isc_arg_number, + reinterpret_cast(tdgbl->gbl_sw_page_size), + 0, NULL, 0, NULL, + 0, NULL, 0, NULL); +#else + /* msg 3 Page size specified (%ld) greater than limit (MAX_PAGE_SIZE bytes) */ + BURP_error(3, (void*)tdgbl->gbl_sw_page_size, 0, 0, 0, 0); +#endif + } + if (temp != tdgbl->gbl_sw_page_size) { + BURP_print(103, (TEXT *) tdgbl->gbl_sw_page_size, (TEXT *) temp, + 0, 0, 0); /* msg 103 page size specified (%ld bytes) rounded up to %ld bytes */ + tdgbl->gbl_sw_page_size = temp; + } + } + + if (tdgbl->gbl_sw_page_buffers) { + if (sw_replace == IN_SW_BURP_B) + BURP_error(260, 0, 0, 0, 0, 0); /* msg 260 page buffers is allowed only on restore or create */ + } + + if (!tdgbl->gbl_sw_blk_factor || sw_replace != IN_SW_BURP_B) + tdgbl->gbl_sw_blk_factor = 1; + + if (!file2) + BURP_error(10, 0, 0, 0, 0, 0); /* msg 10 requires both input and output filenames */ + + if (!strcmp(file1, file2)) + BURP_error(11, 0, 0, 0, 0, 0); /* msg 11 input and output have the same name. Disallowed. */ + + clock = time(NULL); + strcpy(const_cast(tdgbl->gbl_backup_start_time), ctime(&clock)); + p = + const_cast(tdgbl->gbl_backup_start_time + + strlen(const_cast(tdgbl->gbl_backup_start_time)) - 1); + if (*p == '\n') + *p = 0; + +#ifdef DEV_BUILD + if (sw_ods7) { + gds__enable_subsystem("GDSSHR5"); + gds__enable_subsystem("PIPE5"); + } +#endif + + tdgbl->action = (ACT) BURP_ALLOC_ZERO(ACT_LEN); + tdgbl->action->act_total = 0; + tdgbl->action->act_file = NULL; + tdgbl->action->act_action = ACT_unknown; + + action = + open_files(file1, &file2, tdgbl->gbl_sw_verbose, sw_replace, sw_tape); + + MVOL_init(tdgbl->io_buffer_size); + + switch (action) { + case (RESTORE): + SVC_STARTED(tdgbl->service_blk); + result = RESTORE_restore(file1, file2); + break; + + case (BACKUP): + SVC_STARTED(tdgbl->service_blk); + result = BACKUP_backup(file1, file2); + break; + + case (QUIT): + SVC_STARTED(tdgbl->service_blk); + BURP_abort(); + break; + } + if (result != FINI_OK && result != FINI_DB_NOT_ONLINE) + BURP_abort(); + + EXIT(result); + return result; +} + + +void BURP_abort(void) +{ +/************************************** + * + * B U R P _ a b o r t + * + ************************************** + * + * Functional description + * Abandon a failed operation. + * + **************************************/ + TGBL tdgbl; + + tdgbl = GET_THREAD_DATA; + + BURP_print(83, 0, 0, 0, 0, 0); /* msg 83 Exiting before completion due to errors */ + +#ifdef SUPERSERVER + SVC_STARTED(tdgbl->service_blk); +#endif + + EXIT(FINI_ERROR); +} + + +void BURP_error(USHORT errcode, + void* arg1, + void* arg2, + void* arg3, + void* arg4, + void* arg5) +{ +/************************************** + * + * B U R P _ e r r o r + * + ************************************** + * + * Functional description + * Format and print an error message, then punt. + * + **************************************/ +#ifdef SUPERSERVER + TGBL tdgbl; + STATUS *status; + + tdgbl = GET_THREAD_DATA; + status = tdgbl->service_blk->svc_status; + + CMD_UTIL_put_svc_status(status, BURP_MSG_FAC, errcode, + isc_arg_string, arg1, + isc_arg_string, arg2, + isc_arg_string, arg3, + isc_arg_string, arg4, isc_arg_string, arg5); + SVC_STARTED(tdgbl->service_blk); +#endif + + BURP_msg_partial(256, 0, 0, 0, 0, 0); /* msg 256: gbak: ERROR: */ + BURP_msg_put(errcode, arg1, arg2, arg3, arg4, arg5); + BURP_abort(); +} + + +void BURP_print_status( STATUS * status_vector) +{ +/************************************** + * + * B U R P _ p r i n t _ s t a t u s + * + ************************************** + * + * Functional description + * Print error message. Use isc_interprete + * to allow redirecting output. + * + **************************************/ +#ifdef SUPERSERVER + TGBL tdgbl; + STATUS *status; + int i = 0, j; +#endif + STATUS *vector; + SCHAR s[1024]; + + if (status_vector) { + vector = status_vector; +#ifdef SUPERSERVER + tdgbl = GET_THREAD_DATA; + status = tdgbl->service_blk->svc_status; + if (status != status_vector) { + while (*status && (++i < ISC_STATUS_LENGTH)) + status++; + for (j = 0; status_vector[j] && (i < ISC_STATUS_LENGTH); j++, i++) + *status++ = status_vector[j]; + } +#endif + + if (isc_interprete(s, &vector)) { + TRANSLATE_CP(s); + BURP_msg_partial(256, 0, 0, 0, 0, 0); /* msg 256: gbak: ERROR: */ + burp_output("%s\n", s); + while (isc_interprete(s, &vector)) { + TRANSLATE_CP(s); + BURP_msg_partial(256, 0, 0, 0, 0, 0); /* msg 256: gbak: ERROR: */ + burp_output(" %s\n", s); + } + } + } +} + + +void BURP_print_warning( STATUS * status_vector) +{ +/************************************** + * + * B U R P _ p r i n t _ w a r n i n g + * + ************************************** + * + * Functional description + * Print warning message. Use isc_interprete + * to allow redirecting output. + * + **************************************/ + STATUS *vector; + SCHAR s[1024]; + + if (status_vector) { + /* skip the error, assert that one does not exist */ + assert(status_vector[0] == gds_arg_gds); + assert(status_vector[1] == 0); + /* print the warning message */ + vector = &status_vector[2]; + if (isc_interprete(s, &vector)) { + TRANSLATE_CP(s); + BURP_msg_partial(255, 0, 0, 0, 0, 0); /* msg 255: gbak: WARNING: */ + burp_output("%s\n", s); + while (isc_interprete(s, &vector)) { + TRANSLATE_CP(s); + BURP_msg_partial(255, 0, 0, 0, 0, 0); /* msg 255: gbak: WARNING: */ + burp_output(" %s\n", s); + } + } + } +} + + +void BURP_error_redirect( STATUS* status_vector, + USHORT errcode, + void* arg1, + void* arg2) +{ +/************************************** + * + * B U R P _ e r r o r _ r e d i r e c t + * + ************************************** + * + * Functional description + * Issue error message. Output messages then abort. + * + **************************************/ + + BURP_print_status(status_vector); + BURP_error(errcode, arg1, arg2, NULL, NULL, NULL); +} + + +void BURP_msg_partial( USHORT number, + void* arg1, + void* arg2, + void* arg3, + void* arg4, + void* arg5) +{ +/************************************** + * + * B U R P _ m s g _ p a r t i a l + * + ************************************** + * + * Functional description + * Retrieve a message from the error file, + * format it, and print it without a newline. + * + **************************************/ + TEXT buffer[256]; + + gds__msg_format(NULL, + BURP_MSG_FAC, + number, + sizeof(buffer), + buffer, + reinterpret_cast(arg1), + reinterpret_cast(arg2), + reinterpret_cast(arg3), + reinterpret_cast(arg4), + reinterpret_cast(arg5)); + burp_output("%s", buffer); +} + + +void BURP_msg_put( USHORT number, + void* arg1, + void* arg2, + void* arg3, + void* arg4, + void* arg5) +{ +/************************************** + * + * B U R P _ m s g _ p u t + * + ************************************** + * + * Functional description + * Retrieve a message from the error file, format it, and print it. + * + **************************************/ + TEXT buffer[256]; + + gds__msg_format(NULL, + BURP_MSG_FAC, + number, + sizeof(buffer), + buffer, + reinterpret_cast(arg1), + reinterpret_cast(arg2), + reinterpret_cast(arg3), + reinterpret_cast(arg4), + reinterpret_cast(arg5)); + TRANSLATE_CP(buffer); + burp_output("%s\n", buffer); +} + + +void BURP_msg_get( USHORT number, + void* msg, + void* arg1, + void* arg2, + void* arg3, + void* arg4, + void* arg5) +{ +/************************************** + * + * B U R P _ m s g _ g e t + * + ************************************** + * + * Functional description + * Retrieve a message from the error file, format it and copy it to the buffer + * + **************************************/ + TEXT buffer[128]; + + gds__msg_format(NULL, + BURP_MSG_FAC, + number, + sizeof(buffer), + buffer, + reinterpret_cast(arg1), + reinterpret_cast(arg2), + reinterpret_cast(arg3), + reinterpret_cast(arg4), + reinterpret_cast(arg5)); + strcpy(reinterpret_cast(msg), buffer); +} + + +void BURP_output_version( TEXT * arg1, TEXT * arg2) +{ +/************************************** + * + * B U R P _ o u t p u t _ v e r s i o n + * + ************************************** + * + * Functional description + * Callback routine for access method + * printing (specifically show version); + * will accept. + * + **************************************/ + + burp_output(arg1, arg2); +} + + +void BURP_print(USHORT number, + void* arg1, void* arg2, void* arg3, void* arg4, void* arg5) +{ +/************************************** + * + * B U R P _ p r i n t + * + ************************************** + * + * Functional description + * Display a formatted error message + * in a way that VMS or civilized systems + * will accept. + * + **************************************/ + + BURP_msg_partial(169, 0, 0, 0, 0, 0); /* msg 169: gbak: */ + BURP_msg_put(number, arg1, arg2, arg3, arg4, arg5); +} + + +void BURP_verbose(USHORT number, + void* arg1, void* arg2, void* arg3, void* arg4, void* arg5) +{ +/************************************** + * + * B U R P _ v e r b o s e + * + ************************************** + * + * Functional description + * Calls BURP_print for displaying a formatted error message + * but only for verbose output. If not verbose then calls + * user defined yieding function. + * + **************************************/ + TGBL tdgbl; + + tdgbl = GET_THREAD_DATA; + + if (tdgbl->gbl_sw_verbose) + BURP_print(number, arg1, arg2, arg3, arg4, arg5); + else + burp_output(""); +} + + +static void close_out_transaction(VOLATILE SSHORT action, + isc_tr_handle* handle) +{ +/************************************** + * + * c l o s e _ o u t _ t r a n s a c t i o n + * + ************************************** + * + * Functional description + * Make a transaction go away. This is + * important as we need to detach from the + * database so all engine allocated memory is + * returned to the system. + * + **************************************/ + STATUS status_vector[ISC_STATUS_LENGTH]; + + if (*handle != 0) { + if (action == RESTORE) { + /* Even if the restore failed, commit the transaction so that + * a partial database is at least recovered. + */ + isc_commit_transaction(status_vector, handle); + if (status_vector[1]) { + /* If we can't commit - have to roll it back, as + * we need to close all outstanding transactions before + * we can detach from the database. + */ + isc_rollback_transaction(status_vector, handle); + if (status_vector[1]) + BURP_print_status(status_vector); + } + } + else + /* A backup shouldn't touch any data - we ensure that + * by never writing data during a backup, but let's double + * ensure it by doing a rollback + */ + if (isc_rollback_transaction(status_vector, handle)) + BURP_print_status(status_vector); + } +} + + +static SLONG get_number( SCHAR * string) +{ +/************************************** + * + * g e t _ n u m b e r + * + ************************************** + * + * Functional description + * Convert a string to binary, complaining bitterly if + * the string is bum. + * + **************************************/ + SCHAR c, *p; + SLONG value; + + for (value = 0, p = string; c = *p++;) { + if (c < '0' || c > '9') + return 0; + value *= 10; + value += c - '0'; + } + + return value; +} + + +static SSHORT open_files(TEXT * file1, + TEXT ** file2, + USHORT sw_verbose, USHORT sw_replace, USHORT sw_tape) +{ +/************************************** + * + * o p e n _ f i l e s + * + ************************************** + * + * Functional description + * from the input file names and + * positions, guess what the users + * intention was. Return the type + * of the first file, plus open file + * and db handle. + * + **************************************/ + STATUS *status_vector, status_vector2[ISC_STATUS_LENGTH]; + TGBL tdgbl; + FIL fil; + int seq, total; + SSHORT flag; + + + tdgbl = GET_THREAD_DATA; + status_vector = tdgbl->status; + +/* try to attach the database using the first file_name */ + + if (sw_replace != IN_SW_BURP_C && sw_replace != IN_SW_BURP_R) + if (!(isc_attach_database(status_vector, + (SSHORT) 0, + file1, + &tdgbl->db_handle, + tdgbl->dpb_length, + reinterpret_cast(tdgbl->dpb_string)))) + { + if (sw_replace != IN_SW_BURP_B) { + /* msg 13 REPLACE specified, but the first file %s is a database */ + BURP_error(13, file1, 0, 0, 0, 0); + if (isc_detach_database(status_vector, &tdgbl->db_handle)) { + BURP_print_status(status_vector); + } + return QUIT; + } + if (tdgbl->gbl_sw_version) { + /* msg 139 Version(s) for database "%s" */ + BURP_print(139, file1, 0, 0, 0, 0); + isc_version(&tdgbl->db_handle, + reinterpret_cast(BURP_output_version), + "\t%s\n"); + } + if (sw_verbose) + BURP_print(166, file1, 0, 0, 0, 0); /* msg 166: readied database %s for backup */ + } + else if (sw_replace == IN_SW_BURP_B || + (status_vector[1] != gds_io_error + && status_vector[1] != gds_bad_db_format)) { + BURP_print_status(status_vector); + return QUIT; + } + + if (sw_replace == IN_SW_BURP_B) { + + + /* Now it is safe to skip a db file */ + tdgbl->gbl_sw_backup_files = tdgbl->gbl_sw_files->fil_next; + tdgbl->gbl_sw_files = tdgbl->gbl_sw_files->fil_next; + assert(strcmp(tdgbl->gbl_sw_files->fil_name, *file2) == 0); + + flag = BACKUP; + tdgbl->action->act_action = ACT_backup; + for (fil = tdgbl->gbl_sw_files; fil; fil = fil->fil_next) + { + /* adjust the file size first */ + switch (fil->fil_size_code) + { + case size_n: + break; + case size_k: + fil->fil_length *= KBYTE; + break; + case size_m: + fil->fil_length *= MBYTE; + break; + case size_g: + fil->fil_length *= GBYTE; + break; + case size_e: + BURP_error(262, fil->fil_name, 0, 0, 0, 0); /* msg 262 size spe cification either missing or incorrect for file %s */ + break; + default: + assert(FALSE); + break; + } + + if ((fil->fil_seq = ++(tdgbl->action->act_total)) >= 2) + { + tdgbl->action->act_action = ACT_backup_split; + } + if (sw_verbose) + { + BURP_print(75, fil->fil_name, 0, 0, 0, 0); /* msg 75 creating file %s */ + } + if (!strcmp(fil->fil_name, "stdout")) + { + if (tdgbl->action->act_total >= 2 || fil->fil_next) + { + BURP_error(266, 0, 0, 0, 0, 0); /* msg 266 standard output is not supported when using split operation */ + flag = QUIT; + break; + } + /* We ignore SIGPIPE so that we can report an IO error when we + * try to write to the broken pipe. + */ +#ifndef WIN_NT + signal(SIGPIPE, SIG_IGN); +#endif + fil->fil_fd = reinterpret_cast(GBAK_STDOUT_DESC); + break; + } + else + { + +#ifdef WIN_NT + if ((fil->fil_fd = MVOL_open(fil->fil_name, MODE_WRITE, + CREATE_ALWAYS)) == INVALID_HANDLE_VALUE) +#else + if ((fil->fil_fd = open(fil->fil_name, MODE_WRITE, MASK)) == -1) +#endif /* WIN_NT */ + + { + +#ifdef SUPERSERVER + BURP_svc_error(65, isc_arg_string, fil->fil_name, + 0, NULL, 0, NULL, 0, NULL, 0, NULL); +#endif + BURP_print(65, fil->fil_name, 0, 0, 0, 0); /* msg 65 can't + open backup file %s */ + flag = QUIT; + break; + } + } + + if (fil->fil_length == 0) + { + if (fil->fil_next) + { + BURP_error(262, fil->fil_name, 0, 0, 0, 0); /* msg 262 size specification either missing or incorrect for file %s */ + flag = QUIT; + break; + } + else + { + fil->fil_length = MAX_LENGTH; /* Write as much as possible to + the last file */ + } + } + if (fil->fil_length < MIN_SPLIT_SIZE) + { +#ifdef SUPERSERVER + BURP_svc_error( 271, + isc_arg_number, + reinterpret_cast(fil->fil_length), + isc_arg_number, + reinterpret_cast(MIN_SPLIT_SIZE), + 0, NULL, 0, NULL, 0, NULL); +#else + BURP_error(271, (TEXT *) fil->fil_length, + (TEXT *) MIN_SPLIT_SIZE, 0, 0, 0); + /* msg file size given (%d) is less than minimum allowed (%d) */ +#endif + flag = QUIT; + break; + } + } + + if (flag == BACKUP) + { + tdgbl->action->act_file = tdgbl->gbl_sw_files; + tdgbl->file_desc = tdgbl->gbl_sw_files->fil_fd; + } + else + { + if (isc_detach_database(status_vector, &tdgbl->db_handle)) + { + BURP_print_status(status_vector); + } + } + + return flag; + } + + +/* + * If we got to here, then we're really not backing up a database, + * so open a backup file. + */ + +/* There are four possible cases such as: + * + * 1. restore single backup file to single db file + * 2. restore single backup file to multiple db files + * 3. restore multiple backup files (join operation) to single db file + * 4. restore multiple backup files (join operation) to multiple db files + * + * Just looking at the command line, we can't say for sure whether it is a + * specification of the last file to be join or it is a specification of the + * primary db file (case 4), for example: + * + * gbak -c gbk1 gbk2 gbk3 db1 200 db2 500 db3 -v + * ^^^ + * db1 could be either the last file to be join or primary db file + * + * Since 'gbk' and 'gsplit' formats are different (gsplit file has its own + * header record) hence we can use it as follows: + * + * - open first file + * - read & check a header record + * + * If a header is identified as a 'gsplit' one then we know exactly how + * many files need to be join and in which order. We keep opening a file by + * file till we reach the last one to be join. During this step we check + * that the files are accessible and are in proper order. It gives us + * possibility to let silly customer know about an error as soon as possible. + * Besides we have to find out which file is going to be a db file. + * + * If header is not identified as a 'gsplit' record then we assume that + * we got a single backup file. + */ + + fil = tdgbl->gbl_sw_files; + tdgbl->gbl_sw_backup_files = tdgbl->gbl_sw_files; + + tdgbl->action->act_action = ACT_restore; + if (!strcmp(fil->fil_name, "stdin")) { + fil->fil_fd = reinterpret_cast(GBAK_STDIN_DESC); + tdgbl->file_desc = fil->fil_fd; + tdgbl->gbl_sw_files = fil->fil_next; + } + else { + /* open first file */ +#ifdef WIN_NT + if ((fil->fil_fd = MVOL_open(fil->fil_name, MODE_READ, OPEN_EXISTING)) + == INVALID_HANDLE_VALUE) +#else + if ((fil->fil_fd = open(fil->fil_name, MODE_READ)) == + INVALID_HANDLE_VALUE) +#endif + { + BURP_error(65, fil->fil_name, 0, 0, 0, 0); /* msg 65 can't open backup file %s */ + return QUIT; + } + + if (sw_verbose) + BURP_print(100, fil->fil_name, 0, 0, 0, 0); /* msg 100 opened file + %s */ + /* read and check a header record */ + tdgbl->action->act_file = fil; + seq = 1; + if (MVOL_split_hdr_read() == TRUE) { + tdgbl->action->act_action = ACT_restore_join; + total = tdgbl->action->act_total; /* number of files to be join */ + if (fil->fil_seq != seq || seq > total) { + BURP_error(263, fil->fil_name, 0, 0, 0, 0); /* msg 263 file %s out of sequence */ + return QUIT; + } + + for (++seq, fil = fil->fil_next; seq <= total; + fil = fil->fil_next, seq++) { + if (!fil) { + BURP_error(264, 0, 0, 0, 0, 0); /* msg 264 can't join -- one of the files missing */ + return QUIT; + } + if (!strcmp(fil->fil_name, "stdin")) { + BURP_error(265, 0, 0, 0, 0, 0); /* msg 265 standard input is not supported when using join operation */ + return QUIT; + } + tdgbl->action->act_file = fil; +#ifdef WIN_NT + if ((fil->fil_fd = MVOL_open(fil->fil_name, MODE_READ, + OPEN_EXISTING)) == + INVALID_HANDLE_VALUE) +#else + if ((fil->fil_fd = open(fil->fil_name, MODE_READ)) + == INVALID_HANDLE_VALUE) +#endif + { +#ifdef SUPERSERVER + BURP_svc_error(65, isc_arg_string, fil->fil_name, + 0, NULL, 0, NULL, 0, NULL, 0, NULL); +#endif + BURP_print(65, fil->fil_name, 0, 0, 0, 0); /* msg 65 can't open + backup file %s */ + return QUIT; + } + + if (sw_verbose) + BURP_print(100, fil->fil_name, 0, 0, 0, 0); /* msg 100 + opened file + %s */ + if (MVOL_split_hdr_read() == TRUE) { + if ((total != tdgbl->action->act_total) || + (seq != fil->fil_seq) || (seq > total)) { + BURP_error(263, fil->fil_name, 0, 0, 0, 0); /* msg 263 file %s out of sequence */ + return QUIT; + } + } + else { + BURP_error(267, fil->fil_name, 0, 0, 0, 0); /* msg 267 backup file %s might be corrupt */ + return QUIT; + } + } + tdgbl->action->act_file = tdgbl->gbl_sw_files; + tdgbl->file_desc = tdgbl->action->act_file->fil_fd; + if ((tdgbl->gbl_sw_files = fil) == NULL) { + BURP_error(268, 0, 0, 0, 0, 0); /* msg 268 database file specification missing */ + return QUIT; + } + } + else { + /* Move pointer to the begining of the file. At this point we + assume -- this is a single backup file because we were + not able to read a split header record. + */ +#ifdef WIN_NT + if (strnicmp(fil->fil_name, "\\\\.\\tape", 8)) + SetFilePointer(fil->fil_fd, 0, NULL, FILE_BEGIN); + else + SetTapePosition(fil->fil_fd, TAPE_REWIND, 0, 0, 0, FALSE); +#else + lseek(fil->fil_fd, 0, SEEK_SET); +#endif + tdgbl->file_desc = fil->fil_fd; + tdgbl->gbl_sw_files = fil->fil_next; + } + } + + +/* If we got here, we've opened a backup file, and we're + thinking about creating or replacing a database. */ + + *file2 = tdgbl->gbl_sw_files->fil_name; + if (tdgbl->gbl_sw_files->fil_size_code != size_n) + BURP_error(262, *file2, 0, 0, 0, 0); /* msg 262 size specificati on either missing or incorrect for file %s */ + + if ((sw_replace == IN_SW_BURP_C || sw_replace == IN_SW_BURP_R) && + !isc_attach_database(status_vector, + (SSHORT) 0, + *file2, + &tdgbl->db_handle, + tdgbl->dpb_length, + reinterpret_cast(tdgbl->dpb_string))) + { + if (sw_replace == IN_SW_BURP_C) { + if (isc_detach_database(status_vector, &tdgbl->db_handle)) { + BURP_print_status(status_vector); + } + BURP_error(14, *file2, 0, 0, 0, 0); + /* msg 14 database %s already exists. To replace it, use the -R switch */ + } + else { + isc_drop_database(status_vector, &tdgbl->db_handle); + if (tdgbl->db_handle) { + if (isc_detach_database(status_vector2, &tdgbl->db_handle)) { + BURP_print_status(status_vector2); + } + + /* Complain only if the drop database entrypoint is available. + If it isn't, the database will simply be overwritten. */ + + if (status_vector[1] != gds_unavailable) + BURP_error(233, *file2, 0, 0, 0, 0); + /* msg 233 Cannot drop database %s, might be in use */ + } + } + } + if (sw_replace == IN_SW_BURP_R && status_vector[1] == isc_adm_task_denied) { + /* if we got an error from attach database and we have replace switch set + * then look for error from attach returned due to not owner, if we are + * not owner then return the error status back up + */ + BURP_error(274, 0, 0, 0, 0, 0); + /* msg # 274 : Cannot restore over current database, must be sysdba + * or owner of the existing database. + */ + } +/* if we got here, then all is well, remove any error condition from the + * status vector when running as a service thread. If we don't then the + * service will think that there is an error if isc_attach_database failed + * like it should have (if creating a database). + */ + if (tdgbl->gbl_sw_service_thd) + memset(tdgbl->status, 0, ISC_STATUS_LENGTH * sizeof(STATUS)); + +/* check the file size specification */ + for (fil = tdgbl->gbl_sw_files; fil; fil = fil->fil_next) { + if (fil->fil_size_code != size_n) + BURP_error(262, fil->fil_name, 0, 0, 0, 0); /* msg 262 size specification either missing or incorrect for file %s */ + } + + return RESTORE; +} + + +static void burp_output( CONST SCHAR * format, ...) +{ +/************************************** + * + * b u r p _ o u t p u t + * + ************************************** + * + * Functional description + * Platform independent output routine. + * + **************************************/ + va_list arglist; + UCHAR buf[1000]; + int exit_code; + TGBL tdgbl; + + tdgbl = GET_THREAD_DATA; + + if (tdgbl->sw_redirect == NOOUTPUT || format[0] == '\0') { + exit_code = + tdgbl->output_proc(tdgbl->output_data, + reinterpret_cast("")); + } + else if (tdgbl->sw_redirect == TRUE && tdgbl->output_file != NULL) { + VA_START(arglist, format); + ib_vfprintf(tdgbl->output_file, format, arglist); + va_end(arglist); + exit_code = + tdgbl->output_proc(tdgbl->output_data, + reinterpret_cast("")); + } + else { + VA_START(arglist, format); + vsprintf((char *) buf, format, arglist); + va_end(arglist); + + exit_code = tdgbl->output_proc(tdgbl->output_data, buf); + } + + if (exit_code != 0) + EXIT(exit_code); +} + + +static ULONG get_size( SCHAR * string, FIL file) +{ +/********************************************** + * + * g e t _ s i z e + * + ********************************************** + * + * Functional description + * Get size specification for either splitting or + * restoring to multiple files + * + **********************************************/ + SCHAR *num, c; + ULONG size; + BOOLEAN digit; + + + file->fil_size_code = size_n; + for (size = 0, digit = FALSE, num = string; c = *num++;) { + if (isdigit(c)) { + size = size * 10 + (c - '0'); + digit = TRUE; + } + else { + if (isalpha(c)) { + if (!digit) { + file->fil_size_code = size_e; + size = 0; + break; + } + switch (UPPER(c)) { + case 'K': + file->fil_size_code = size_k; + break; + case 'M': + file->fil_size_code = size_m; + break; + case 'G': + file->fil_size_code = size_g; + break; + default: + file->fil_size_code = size_e; + size = 0; + break; + } + if (*num) { + file->fil_size_code = size_e; + size = 0; + } + break; + } + } + } + + return (file->fil_length = size); +} + + +#ifndef SUPERSERVER +#ifndef GUI_TOOLS +static int api_gbak(int argc, + char *argv[], + USHORT length, + TEXT * password, + TEXT * user, + TEXT * service, + BOOLEAN restore, + BOOLEAN verbose) +{ +/********************************************** + * + * a p i _ g b a k + * + ********************************************** + * + * Functional description + * Run gbak using services APIs + * + **********************************************/ + STATUS status[ISC_STATUS_LENGTH]; + TEXT **end, *x, *p, *usr, *pswd; + USHORT spblen, thdlen; + char sendbuf[] = { isc_info_svc_line }; + char respbuf[1024]; + long *svc_handle = NULL; + char *spb_ptr, *spb, *svc_name, *thd_ptr, *thd; + struct tgbl *tdgbl, ldgbl; + + tdgbl = &ldgbl; + SET_THREAD_DATA; + memset((void *) tdgbl, 0, sizeof(*tdgbl)); + tdgbl->output_proc = output_main; + + if (!user) + usr = getenv("ISC_USER"); + else + usr = user; + + if (!password) + pswd = getenv("ISC_PASSWORD"); + else + pswd = password; + + spb = (char *) gds__alloc((SLONG) (2 + 2 + ((usr) ? strlen(usr) : 0) + + 2 + ((pswd) ? strlen(pswd) : 0)) + + 2 + length + strlen(SERVICE_THD_PARAM)); + /* 'isc_spb_version' + 'isc_spb_current_version' + 'isc_spb_user_name' + 'length' + "usr" + 'isc_spb_password' + 'length' + "pswd" + 'isc_spb_options' + 'length' + "options" */ + if (spb == NULL) { + status[0] = isc_arg_gds; + status[1] = isc_virmemexh; + status[2] = isc_arg_end; + BURP_print_status(status); + BURP_print(83, 0, 0, 0, 0, 0); /* msg 83 Exiting before completion due to errors */ + return FINI_ERROR; + } + + spb_ptr = spb; + *spb_ptr++ = isc_spb_version; + *spb_ptr++ = isc_spb_current_version; + + if (usr) { + *spb_ptr++ = isc_spb_user_name; + *spb_ptr++ = strlen(usr); + MEMMOVE(usr, spb_ptr, strlen(usr)); + spb_ptr += strlen(usr); + if (user) + *user = '\0'; + } + + if (pswd) { + *spb_ptr++ = isc_spb_password; + *spb_ptr++ = strlen(pswd); + MEMMOVE(pswd, spb_ptr, strlen(pswd)); + spb_ptr += strlen(pswd); + if (password) + *password = '\0'; + } + + svc_name = (char *) gds__alloc((SLONG) (strlen(service) + 1)); + + if (svc_name == NULL) { + status[0] = isc_arg_gds; + status[1] = isc_virmemexh; + status[2] = isc_arg_end; + BURP_print_status(status); + gds__free(spb); + BURP_print(83, 0, 0, 0, 0, 0); /* msg 83 Exiting before completion due to errors */ + return FINI_ERROR; + } + + if (service) { + strcpy(svc_name, service); + *service = '\0'; + } + + +/* Fill command line options started with internal switch SERVICE_THD_PARAM */ + + *spb_ptr++ = isc_spb_command_line; + end = argv + argc; + argv++; + + *spb_ptr++ = length + strlen(SERVICE_THD_PARAM); + for (x = SERVICE_THD_PARAM; *x;) + *spb_ptr++ = *x++; + while (argv < end) { + if (**argv) + *spb_ptr++ = ' '; + for (x = *argv++; *x;) + *spb_ptr++ = *x++; + } + + spblen = spb_ptr - spb; + + if (isc_service_attach( status, + 0, + svc_name, + reinterpret_cast(&svc_handle), + spblen, spb)) + { + BURP_print_status(status); + gds__free(spb); + gds__free(svc_name); + BURP_print(83, 0, 0, 0, 0, 0); /* msg 83 Exiting before completion due to errors */ + return FINI_ERROR; + } + + thd = (char *) gds__alloc((SLONG) (2)); + /* 'isc_action_svc_restore/isc_action_svc_backup' + 'isc_spb_verbose' */ + + if (thd == NULL) { + status[0] = isc_arg_gds; + status[1] = isc_virmemexh; + status[2] = isc_arg_end; + BURP_print_status(status); + isc_service_detach(status, reinterpret_cast(&svc_handle)); + gds__free(spb); + gds__free(svc_name); + BURP_print(83, 0, 0, 0, 0, 0); /* msg 83 Exiting before completion due to errors */ + return FINI_ERROR; + } + + thd_ptr = thd; + if (restore) + *thd_ptr++ = isc_action_svc_restore; + else + *thd_ptr++ = isc_action_svc_backup; + + if (verbose) + *thd_ptr++ = isc_spb_verbose; + + thdlen = thd_ptr - thd; + + if (isc_service_start( status, + reinterpret_cast(&svc_handle), + NULL, + thdlen, + thd)) + { + BURP_print_status(status); + gds__free(spb); + gds__free(svc_name); + gds__free(thd); + isc_service_detach(status, reinterpret_cast(&svc_handle)); + BURP_print(83, 0, 0, 0, 0, 0); /* msg 83 Exiting before completion due to errors */ + return FINI_ERROR; + } + + do { + if (isc_service_query( status, + reinterpret_cast(&svc_handle), + NULL, + 0, + NULL, + sizeof(sendbuf), + sendbuf, + sizeof(respbuf), + respbuf)) + { + BURP_print_status(status); + gds__free(spb); + gds__free(svc_name); + gds__free(thd); + isc_service_detach(status, + reinterpret_cast(&svc_handle)); + BURP_print(83, 0, 0, 0, 0, 0); /* msg 83 Exiting before completion due to errors */ + return FINI_ERROR; + } + + x = p = respbuf; + + if (*p++ == isc_info_svc_line) + { + ISC_USHORT len = 0; + + len = (ISC_USHORT) isc_vax_integer(p, sizeof(ISC_USHORT)); + p += sizeof(ISC_USHORT); + if (!len) + { + if (*p == isc_info_data_not_ready) + continue; + else if (*p == isc_info_end) + break; + } + + p[len] = '\0'; + burp_output("%s\n", p); + + p += len; + } + } + while (*x == isc_info_svc_line); + + isc_service_detach(status, reinterpret_cast(&svc_handle)); + return FINI_OK; +} +#endif // !GUI_TOOLS + +#endif // !SUPERSERVER + + +} // extern "C" diff --git a/src/burp/burp.def b/src/burp/burp.def new file mode 100644 index 0000000000..83008754ec --- /dev/null +++ b/src/burp/burp.def @@ -0,0 +1,45 @@ +; 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): ______________________________________. +;------------------------------------------------------------ +; GBAK DLL MODULE DEFINITION FILE +;------------------------------------------------------------ + +LIBRARY GBAK.DLL + +DESCRIPTION 'INTERBASE BACKUP/RESTORE DLL' +CODE MOVEABLE +DATA MOVEABLE +SEGMENTS + + _TEXT DISCARDABLE + BURP_TEXT DISCARDABLE + CANONICA_TEXT DISCARDABLE + MISC_TEXT DISCARDABLE + MVOL_TEXT DISCARDABLE + BACKUP_TEXT DISCARDABLE + RESTORE_TEXT DISCARDABLE + BURPWEP_TEXT PRELOAD + XDR_TEXT DISCARDABLE + NTOH_TEXT DISCARDABLE + DLLSHELL_TEXT PRELOAD + +EXPORTS + _BURP_gbak @2 +; WEP @1 RESIDENTNAME + +IMPORTS + _gds__start_transaction=gds32.gds__start_transaction diff --git a/src/burp/burp.h b/src/burp/burp.h new file mode 100644 index 0000000000..79008a2171 --- /dev/null +++ b/src/burp/burp.h @@ -0,0 +1,945 @@ +/* + * PROGRAM: JRD Backup and Restore Program + * MODULE: burp.h + * DESCRIPTION: Burp file format + * + * 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): ______________________________________. + */ + +#ifndef _BURP_BURP_H_ +#define _BURP_BURP_H_ + +#include "../jrd/ib_stdio.h" +#include "../jrd/common.h" +#include "../include/jrd/gds.h" +#include "../jrd/dsc.h" +#include "../burp/burp_proto.h" +#include "../burp/misc_proto.h" +#include "../jrd/gds_proto.h" +#include "../jrd/thd.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define BURP_ALLOC(size) MISC_alloc_burp((ULONG) size) +#define BURP_ALLOC_ZERO(size) MISC_alloc_burp((ULONG) size) +#define BURP_FREE(block) MISC_free_burp(block) + +#define GDS_NAME_LEN 32 +typedef TEXT GDS_NAME [GDS_NAME_LEN]; + +#define NOOUTPUT 2 + +/* Record types in backup file */ + +typedef enum rec_type { + rec_burp, /* Restore program attributes */ + rec_database, /* Logical database parameters */ + rec_global_field, /* Global field description */ + rec_relation, /* Relation description */ + rec_field, /* Local field description */ + rec_index, /* Index description */ + rec_data, /* Data for relation */ + rec_blob, /* Blob */ + rec_relation_data, /* Standalone data header */ + rec_relation_end, /* End of data for relation */ + rec_end, /* End of file */ + rec_view, /* View attributes */ + rec_security_class, /* Security class acl */ + rec_trigger, /* Trigger definition */ + rec_physical_db, /* Physical database parameters */ + rec_function, /* Function description */ + rec_function_arg, /* Function arguement description */ + rec_function_end, /* End of function and its args */ + rec_gen_id, /* From blr_gen_id */ + rec_type, /* Type of field */ + rec_filter, /* Filter */ + rec_trigger_message, /* Trigger message texts */ + rec_user_privilege, /* User privilege */ + rec_array, /* 23*/ /* Array blob */ + rec_field_dimensions, /* Array field dimensions */ + rec_files, /* files for shadowing */ + rec_generator, /* another format for gen-ids */ + rec_procedure, /* Stored procedure */ + rec_procedure_prm, /* Stored procedure parameters */ + rec_procedure_end, /* End of procedure and its args */ + rec_exception, /* Exception */ + rec_rel_constraint, /* Relation constraints */ + rec_ref_constraint, /* Referential constraints */ + rec_chk_constraint, /* Check constraints */ + rec_charset, /* Character sets */ + rec_collation, /* Collations */ + rec_sql_roles /* SQL roles */ +} REC_TYPE; + + +/* The order of battle for major records is: + + [] + rec_end + +where each relation is: + + + +where local fields is: + + +where each relation data is: + + + +where data is: + [] [ , ...] + +and is + + + +and is + + + [] + + +where each function is: + + [] + +and is + + + +where trigger-old is: + + +and trigger-new is: + + + + +*/ + +/* Attributes within major record */ + +/* CAREFUL not to pull the lastest version into maint version without + modifying the att_backup_format to be one version back */ + +/* ATT_BACKUP_FORMAT has been increased to 5. It allows us to distinguish + backup format between IB3.3/IB4.0 and IB4.5 in case of migration + problem */ +/* Version 6: Supports SQL Time & Date columns. + RDB$FIELD_PRECISION + SQL Dialect from database header + SQL_INT64 columns and generator values + */ +#define ATT_BACKUP_FORMAT 6 + +/* format version number for ranges for arrays */ + +#define GDS_NDA_VERSION 1 + +/* max array dimension */ + +#define MAX_DIMENSION 16 + +#define SERIES 1 + +enum att_type { + att_end = 0, /* end of major record */ + + /* Backup program attributes */ + + att_backup_date = SERIES, /* date of backup */ + att_backup_format, /* backup format version */ + att_backup_os, /* backup operating system */ + att_backup_compress, + att_backup_transportable, /* XDR datatypes for user data */ + att_backup_blksize, /* backup block size */ + att_backup_file, /* database file name */ + att_backup_volume, /* backup volume number */ + + /* Database attributes */ + + att_file_name = SERIES, /* database file name (physical) */ + att_file_size, /* size of original database (physical) */ + att_jrd_version, /* jrd version (physical) */ + att_creation_date, /* database creation date (physical) */ + att_page_size, /* page size of original database (physical) */ + att_database_description, /* description from RDB$DATABASE (logical) */ + att_database_security_class, /* database level security (logical) */ + att_sweep_interval, /* sweep interval */ + att_no_reserve, /* don't reserve space for versions */ + att_database_description2, + att_database_dfl_charset, /* default character set from RDB$DATABASE */ + att_forced_writes, /* syncronous writes flag */ + att_page_buffers, /* page buffers for buffer cache */ + att_SQL_dialect, /* SQL dialect that it speaks */ + att_db_read_only, /* Is the database ReadOnly? */ + + /* Relation attributes */ + + att_relation_name = SERIES, + att_relation_view_blr, + att_relation_description, + att_relation_record_length, /* Record length in file */ + att_relation_view_relation, + att_relation_view_context, + att_relation_system_flag, + att_relation_security_class, + att_relation_view_source, + att_relation_dummy, /* this space available */ + att_relation_ext_description, + att_relation_owner_name, + att_relation_description2, + att_relation_view_source2, + att_relation_ext_description2, + att_relation_flags, + att_relation_ext_file_name, /* name of file for external tables */ + + /* Field attributes (used for both global and local fields) */ + + att_field_name = SERIES, /* name of field */ + att_field_source, /* Global field name for local field */ + + att_base_field, /* Source field for view */ + att_view_context, /* Context variable for view definition */ + + att_field_query_name, /* Query attributes */ + att_field_query_header, + att_field_edit_string, + + att_field_type, /* Physical attributes */ + att_field_sub_type, + att_field_length, /* 10 */ + att_field_scale, + att_field_segment_length, + att_field_position, /* Field position in relation (not in file) */ + att_field_offset, /* Offset in data record (local fields only) */ + + att_field_default_value, /* Fluff */ + att_field_description, + att_field_missing_value, + att_field_computed_blr, + att_field_computed_source, + att_field_validation_blr, /* 20 */ + att_field_validation_source, + att_field_number, /* Field number to match up blobs */ + att_field_computed_flag, /* Field is computed, not real */ + att_field_system_flag, /* Interesting system flag */ + att_field_security_class, + + att_field_external_length, + att_field_external_type, + att_field_external_scale, + att_field_dimensions, /* 29 */ + att_field_ranges, /* this space for rent */ + att_field_complex_name, /* relation field attribute */ + att_field_range_low, /* low range for array */ + att_field_range_high, /* high range for array */ + att_field_update_flag, + att_field_description2, + att_field_validation_source2, + att_field_computed_source2, + att_field_null_flag, /* If field can be null */ + att_field_default_source, /* default source for field (new fmt only) */ + att_field_missing_source, /* missing source for field (new fmt only) */ + att_field_character_length, /* length of field in characters */ + att_field_character_set, /* Charset id of field */ + att_field_collation_id, /* Collation id of field */ + att_field_precision, /* numeric field precision of RDB$FIELDS */ + + /* Index attributes */ + + att_index_name = SERIES, + att_segment_count, + att_index_inactive, + att_index_unique_flag, + att_index_field_name, + att_index_description, + att_index_type, + att_index_foreign_key, + att_index_description2, + att_index_expression_source, + att_index_expression_blr, + + /* Data record */ + + att_data_length = SERIES, + att_data_data, + + /* Blob record */ + + att_blob_field_number = SERIES + 2, /* Field number of blob field */ + att_blob_type, /* Segmented = 0, stream = 1 */ + att_blob_number_segments, /* Number of segments */ + att_blob_max_segment, /* Longest segment */ + att_blob_data, + + /* View attributes */ + + att_view_relation_name = SERIES + 7, + att_view_context_id, + att_view_context_name, + + /* Security class attributes */ + + att_class_security_class = SERIES + 10, + att_class_acl, + att_class_description, + + + /* Array attributes */ + + att_array_dimensions = SERIES + 13, + att_array_range_low, + att_array_range_high, + + /* XDR encoded data attributes */ + + att_xdr_length = SERIES + 16, + att_xdr_array, + att_class_description2, + + /* Trigger attributes */ + + att_trig_type = SERIES, + att_trig_blr, + att_trig_source, + att_trig_name, + att_trig_relation_name, + att_trig_sequence, + att_trig_description, + att_trig_system_flag, + att_trig_inactive, + att_trig_source2, + att_trig_description2, + att_trig_flags, + + /* Function attributes */ + + att_function_name = SERIES, + att_function_description, + att_function_class, + att_function_module_name, + att_function_entrypoint, + att_function_return_arg, + att_function_query_name, + att_function_type, + att_function_description2, + + /* Function argument attributes */ + + att_functionarg_name = SERIES, + att_functionarg_position, + att_functionarg_mechanism, + att_functionarg_field_type, + att_functionarg_field_scale, + att_functionarg_field_length, + att_functionarg_field_sub_type, + att_functionarg_character_set, + att_functionarg_field_precision, + + /* TYPE relation attributes */ + att_type_name = SERIES, + att_type_type, + att_type_field_name, + att_type_description, + att_type_system_flag, + /* Also see att_type_description2 below! */ + + /* Filter attributes */ + att_filter_name, + att_filter_description, + att_filter_module_name, + att_filter_entrypoint, + att_filter_input_sub_type, + att_filter_output_sub_type, + att_filter_description2, + att_type_description2, + + /* Trigger message attributes */ + att_trigmsg_name = SERIES, + att_trigmsg_number, + att_trigmsg_text, + + /* User privilege attributes */ + att_priv_user = SERIES, + att_priv_grantor, + att_priv_privilege, + att_priv_grant_option, + att_priv_object_name, + att_priv_field_name, + att_priv_user_type, + att_priv_obj_type, + + /* files for shadowing purposes */ + att_file_filename = SERIES, + att_file_sequence, + att_file_start, + att_file_length, + att_file_flags, + att_shadow_number, + + /* Attributes for gen_id */ + att_gen_generator = SERIES, + att_gen_value, + att_gen_value_int64, + + /* Stored procedure attributes */ + + att_procedure_name = SERIES, + att_procedure_inputs, + att_procedure_outputs, + att_procedure_description, + att_procedure_description2, + att_procedure_source, + att_procedure_source2, + att_procedure_blr, + att_procedure_security_class, + att_procedure_owner_name, + + /* Stored procedure parameter attributes */ + + att_procedureprm_name = SERIES, + att_procedureprm_number, + att_procedureprm_type, + att_procedureprm_field_source, + att_procedureprm_description, + att_procedureprm_description2, + + /* Exception attributes */ + + att_exception_name = SERIES, + att_exception_msg, + att_exception_description, + att_exception_description2, + + /* Relation constraints attributes */ + + att_rel_constraint_name = SERIES, + att_rel_constraint_type, + att_rel_constraint_rel_name, + att_rel_constraint_defer, + att_rel_constraint_init, + att_rel_constraint_index, + + /* Referential constraints attributes */ + + att_ref_constraint_name = SERIES, + att_ref_unique_const_name, + att_ref_match_option, + att_ref_update_rule, + att_ref_delete_rule, + + /* SQL roles attributes */ + att_role_name = SERIES, + att_role_owner_name, + + /* Check constraints attributes */ + att_chk_constraint_name = SERIES, + att_chk_trigger_name, + + /* Character Set attributes */ + att_charset_name = SERIES, + att_charset_form, + att_charset_numchar, + att_charset_coll, + att_charset_id, + att_charset_sysflag, + att_charset_description, + att_charset_funct, + att_charset_bytes_char, + + att_coll_name = SERIES, + att_coll_id, + att_coll_cs_id, + att_coll_attr, + att_coll_subtype, /* Unused: 93-11-12 Daves */ + att_coll_sysflag, + att_coll_description, + att_coll_funct +}; + + +#define ATT_TYPE enum att_type + +// TMN: Why was this even added in the first place??? +//typedef SCHAR att_type; + + +/* Trigger types */ + +enum trig_t { + trig_pre_store = 1, /* default */ + trig_pre_modify, /* default */ + trig_post_erase /* default */ +}; + +/* these types to go away when recognized by gpre as + .. some time in the future */ + +#define TRIG_TYPE_PRE_STORE 1 +#define TRIG_TYPE_PRE_MODIFY 3 +#define TRIG_TYPE_POST_ERASE 6 + +/* default trigger name templates */ + +#define TRIGGER_SEQUENCE_DEFAULT 0 + +/* common structure definitions */ + +/* field block, used to hold local field definitions */ + +typedef struct fld { + struct fld *fld_next; + SSHORT fld_type; + SSHORT fld_sub_type; + FLD_LENGTH fld_length; + SSHORT fld_scale; + SSHORT fld_position; + SSHORT fld_parameter; + SSHORT fld_missing_parameter; + SSHORT fld_id; + RCRD_OFFSET fld_offset; + RCRD_OFFSET fld_old_offset; + SSHORT fld_number; + SSHORT fld_system_flag; + SSHORT fld_name_length; + TEXT fld_name [32]; + TEXT fld_source [32]; + TEXT fld_base [32]; + TEXT fld_query_name [32]; + TEXT fld_security_class [32]; + SSHORT fld_edit_length; + SSHORT fld_view_context; + SSHORT fld_update_flag; + SSHORT fld_flags; + TEXT fld_edit_string [256]; + GDS__QUAD fld_description; + GDS__QUAD fld_query_header; + TEXT fld_complex_name [32]; + SSHORT fld_dimensions; + SLONG fld_ranges [2 * MAX_DIMENSION]; + SSHORT fld_null_flag; + GDS__QUAD fld_default_value; + GDS__QUAD fld_default_source; + SSHORT fld_character_length; + SSHORT fld_character_set_id; + SSHORT fld_collation_id; +} *FLD; + +#define FLD_computed 1 +#define FLD_position_missing 2 +#define FLD_array 4 +#define FLD_update_missing 8 +#define FLD_null_flag 16 +#define FLD_charset_flag 32 /* column has global charset */ +#define FLD_collate_flag 64 /* local column has specific collation */ + +/* relation definition - holds useful relation type stuff */ + +typedef struct rel { + struct rel *rel_next; + struct fld *rel_fields; + SSHORT rel_flags; + SSHORT rel_id; + SSHORT rel_name_length; + GDS_NAME rel_name; + GDS_NAME rel_owner; /* relation owner, if not us */ + GDS__QUAD rel_store_blr; /* trigger blr blob id */ + GDS__QUAD rel_store_source; /* trigger source blob id */ + GDS__QUAD rel_modify_blr; /* trigger blr blob id */ + GDS__QUAD rel_modify_source; /* trigger source blob id */ + GDS__QUAD rel_erase_blr; /* trigger blr blob id */ + GDS__QUAD rel_erase_source; /* trigger source blob id */ +} *REL; + +#define REL_view 1 +#define REL_external 2 + +/* procedure definition - holds useful procedure type stuff */ + +typedef struct prc { + struct prc *prc_next; + SSHORT prc_name_length; + GDS_NAME prc_name; + GDS_NAME prc_owner; /* relation owner, if not us */ +} *PRC; + + +typedef struct gfld { + TEXT gfld_name [32]; + ISC_QUAD gfld_vb; + ISC_QUAD gfld_vs; + ISC_QUAD gfld_vs2; + struct gfld *gfld_next; + USHORT gfld_flags; +} *GFLD; + +#define GFLD_validation_blr 1 +#define GFLD_validation_source 2 +#define GFLD_validation_source2 4 + +#define MAX_FILE_NAME_LENGTH 256 + +/* Note that this typedef is also defined in JRD.H and REMOTE.H */ +/* but for some reason we are avoiding including JRD.H */ +/* and this typedef is needed to include SVC.H */ +#if !(defined REMOTE_REMOTE_H || defined JRD_JRD_H) +#ifndef INCLUDE_FB_BLK +#include "../include/fb_blk.h" +#endif +#endif + + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#include "../jrd/svc.h" +#include "../jrd/svc_proto.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* typedef the file descriptor type to make things cleaner */ + +#ifdef WIN_NT +typedef void *DESC; +#define CLOSE CloseHandle +#define UNLINK DeleteFile +#define FLUSH(x) FlushFileBuffers(x) +#else +typedef int DESC; +#define CLOSE close +#define UNLINK unlink +#define FLUSH(x) /* nothing */ +#define INVALID_HANDLE_VALUE -1 +#endif /* WIN_NT */ + +/* File block -- for multi-file databases */ + +typedef enum { + size_n = 0, /* none */ + size_k, /* k = 1024 */ + size_m, /* m = k x 1024 */ + size_g, /* g = m x 1024 */ + size_e /* error */ +} SIZE_CODE; + +typedef struct fil { + struct fil *fil_next; + TEXT *fil_name; + ULONG fil_length; + DESC fil_fd; + USHORT fil_seq; + SIZE_CODE fil_size_code; +} *FIL; + +#define FIL_LEN sizeof (struct fil) + +/* Split & Join stuff */ + +typedef enum act_t { + ACT_unknown, /* action is unknown */ + ACT_backup, + ACT_backup_split, + ACT_restore, + ACT_restore_join +} ACT_T; + +typedef struct act { + USHORT act_total; + FIL act_file; + ACT_T act_action; +} *ACT; + +#define ACT_LEN sizeof (struct act) + +#define MAX_LENGTH -1 + +/* This structure has been cloned from spit.c */ + +typedef struct hdr_split { + TEXT hdr_split_tag[18]; + TEXT hdr_split_timestamp[30]; + TEXT hdr_split_text1[11]; + TEXT hdr_split_sequence[4]; /* File sequence number */ + TEXT hdr_split_text2[4]; + TEXT hdr_split_total[4]; /* Total number of files */ + TEXT hdr_split_text3[2]; + TEXT hdr_split_name[27]; /* File name */ +} *HDR_SPLIT; + +/* NOTE: size of the hdr_split_tag and HDR_SPLIT_TAG must be the same and equal + to 18. Otherwise we will not be able to join the gbk files v5.x */ + +#define HDR_SPLIT_SIZE sizeof (struct hdr_split) +#define HDR_SPLIT_TAG5 "InterBase/gsplit, " +#define HDR_SPLIT_TAG6 "InterBase/gbak, " +#define HDR_SPLIT_TAG HDR_SPLIT_TAG6 +#define MIN_SPLIT_SIZE 2048 /* bytes */ + +/* Global switches and data */ + +typedef struct tgbl { + struct thdd tgbl_thd_data; + TEXT *gbl_database_file_name; + TEXT gbl_backup_start_time [30]; + USHORT gbl_sw_verbose; + USHORT gbl_sw_ignore_limbo; + USHORT gbl_sw_meta; + USHORT gbl_sw_novalidity; + USHORT gbl_sw_page_size; + USHORT gbl_sw_compress; + USHORT gbl_sw_version; + USHORT gbl_sw_transportable; + USHORT gbl_sw_incremental; + USHORT gbl_sw_deactivate_indexes; + USHORT gbl_sw_kill; + USHORT gbl_sw_blk_factor; + USHORT gbl_sw_no_reserve; + USHORT gbl_sw_old_descriptions; + USHORT gbl_sw_service_gbak; + USHORT gbl_sw_service_thd; + USHORT gbl_sw_bug8183; + USHORT gbl_sw_convert_ext_tables; + BOOLEAN gbl_sw_mode; + BOOLEAN gbl_sw_mode_val; + SCHAR *gbl_sw_sql_role; + SCHAR *gbl_sw_user; + SCHAR *gbl_sw_password; + SLONG gbl_sw_skip_count; + SLONG gbl_sw_page_buffers; + FIL gbl_sw_files; + FIL gbl_sw_backup_files; + GFLD gbl_global_fields; + ACT action; + ULONG io_buffer_size; + USHORT sw_redirect; + UCHAR dpb_string [100]; + SSHORT dpb_length; + UCHAR *burp_env; + UCHAR *io_ptr; + int io_cnt; + REL relations; + PRC procedures; + SLONG BCK_capabilities; + USHORT RESTORE_format; + ULONG mvol_io_buffer_size; + ULONG mvol_actual_buffer_size; + ULONG mvol_cumul_count_kb; + UCHAR *mvol_io_ptr; + int mvol_io_cnt; + UCHAR *mvol_io_buffer; + UCHAR *mvol_io_volume; + UCHAR *mvol_io_header; + UCHAR *mvol_io_data; + TEXT mvol_db_name_buffer [MAX_FILE_NAME_LENGTH]; + SCHAR mvol_old_file [MAX_FILE_NAME_LENGTH]; + int mvol_volume_count; + USHORT mvol_empty_file; + isc_db_handle db_handle; + isc_tr_handle tr_handle; + isc_tr_handle global_trans; + DESC file_desc; + long *status; /* points to either the tdgbl status or service status */ + long status_vector [ISC_STATUS_LENGTH]; + int exit_code; + UCHAR *head_of_mem_list; + OUTPUTPROC output_proc; + SLONG output_data; + IB_FILE *output_file; + SVC service_blk; + /* + * Link list of global fields that were converted from V3 sub_type + * to V4 char_set_id/collate_id. Needed for local fields conversion. + */ + FLD v3_cvt_fld_list; + + isc_req_handle handles_get_character_sets_req_handle1; + isc_req_handle handles_get_chk_constraint_req_handle1; + isc_req_handle handles_get_collation_req_handle1; + isc_req_handle handles_get_exception_req_handle1; + isc_req_handle handles_get_field_dimensions_req_handle1; + isc_req_handle handles_get_field_req_handle1; + isc_req_handle handles_get_fields_req_handle1; + isc_req_handle handles_get_fields_req_handle2; + isc_req_handle handles_get_fields_req_handle3; + isc_req_handle handles_get_fields_req_handle4; + isc_req_handle handles_get_fields_req_handle5; + isc_req_handle handles_get_fields_req_handle6; + isc_req_handle handles_get_files_req_handle1; + isc_req_handle handles_get_filter_req_handle1; + isc_req_handle handles_get_function_arg_req_handle1; + isc_req_handle handles_get_function_req_handle1; + isc_req_handle handles_get_global_field_req_handle1; + isc_req_handle handles_get_index_req_handle1; + isc_req_handle handles_get_index_req_handle2; + isc_req_handle handles_get_index_req_handle3; + isc_req_handle handles_get_index_req_handle4; + isc_req_handle handles_get_procedure_prm_req_handle1; + isc_req_handle handles_get_procedure_req_handle1; + isc_req_handle handles_get_ranges_req_handle1; + isc_req_handle handles_get_ref_constraint_req_handle1; + isc_req_handle handles_get_rel_constraint_req_handle1; + isc_req_handle handles_get_relation_req_handle1; + isc_req_handle handles_get_security_class_req_handle1; + isc_req_handle handles_get_sql_roles_req_handle1; + isc_req_handle handles_get_trigger_message_req_handle1; + isc_req_handle handles_get_trigger_message_req_handle2; + isc_req_handle handles_get_trigger_old_req_handle1; + isc_req_handle handles_get_trigger_req_handle1; + isc_req_handle handles_get_type_req_handle1; + isc_req_handle handles_get_user_privilege_req_handle1; + isc_req_handle handles_get_view_req_handle1; + isc_req_handle handles_put_index_req_handle1; + isc_req_handle handles_put_index_req_handle2; + isc_req_handle handles_put_index_req_handle3; + isc_req_handle handles_put_index_req_handle4; + isc_req_handle handles_put_index_req_handle5; + isc_req_handle handles_put_index_req_handle6; + isc_req_handle handles_put_index_req_handle7; + isc_req_handle handles_put_relation_req_handle1; + isc_req_handle handles_put_relation_req_handle2; + isc_req_handle handles_store_blr_gen_id_req_handle1; + isc_req_handle handles_write_function_args_req_handle1; + isc_req_handle handles_write_function_args_req_handle2; + isc_req_handle handles_write_procedure_prms_req_handle1; + USHORT hdr_forced_writes; + TEXT database_security_class[32]; /* To save database security class for deferred update */ +} *TGBL; + +#ifdef GET_THREAD_DATA +#undef GET_THREAD_DATA +#endif + +#ifdef SUPERSERVER +#define GET_THREAD_DATA ((TGBL) THD_get_specific()) +#define SET_THREAD_DATA THD_put_specific ((THDD) tdgbl); \ + tdgbl->tgbl_thd_data.thdd_type = THDD_TYPE_TGBL +#define RESTORE_THREAD_DATA THD_restore_specific(); +#else +extern struct tgbl *gdgbl; + +#define GET_THREAD_DATA (gdgbl) + +#ifdef __cplusplus + +#define SET_THREAD_DATA gdgbl = const_cast(tdgbl); \ + tdgbl->tgbl_thd_data.thdd_type = THDD_TYPE_TGBL +#else /* __cplusplus */ + +#define SET_THREAD_DATA gdgbl = (struct tgbl *)tdgbl; \ + tdgbl->tgbl_thd_data.thdd_type = (volatile)THDD_TYPE_TGBL + +#endif /* __cplusplus */ + +#define RESTORE_THREAD_DATA + +#endif + +#if defined(__cplusplus) +#define EXIT(code) { tdgbl->exit_code = ((volatile int)code); \ + if (tdgbl->burp_env != NULL) \ + LONGJMP(reinterpret_cast(const_cast(tdgbl->burp_env)), 1); } +#else +#define EXIT(code) { tdgbl->exit_code = ((volatile int)code); \ + if (tdgbl->burp_env != NULL) \ + LONGJMP(tdgbl->burp_env, 1); } +#endif /* __cplusplus */ + +#define FINI_DB_NOT_ONLINE 2 /* database is not on-line due to + failure to activate one or more + indices */ + +/* I/O definitions */ + +#ifndef IO_BUFFER_SIZE +#ifdef BUFSIZ +#define IO_BUFFER_SIZE BUFSIZ +#else +#define IO_BUFFER_SIZE 1024 +#endif +#endif + +#define GBAK_IO_BUFFER_SIZE (16 * (IO_BUFFER_SIZE)) + +/* Burp will always write a backup in multiples of the following number + * of bytes. The initial value is the smallest which ensures that writes + * to fixed-block SCSI tapes such as QIC-150 will work. The value should + * always be a multiple of 512 for that reason. + * If you change to a value which is NOT a power of 2, then change the + * BURP_UP_TO_BLOCK macro to use division and multiplication instead of + * bit masking. + */ + +#define BURP_BLOCK 512 +#define BURP_UP_TO_BLOCK(n) (((n) + BURP_BLOCK - 1) & ~(BURP_BLOCK - 1)) + +/* Move the read and write mode declarations in here from burp.c + so that other files can see them for multivolume opens */ + +#ifdef NETWARE_386 +#define MODE_WRITE O_RDWR | O_CREAT | O_TRUNC +#endif + +#ifdef PC_PLATFORM +#ifndef NETWARE_386 +#define MODE_READ O_RDONLY | O_BINARY +#define MODE_WRITE O_WRONLY | O_BINARY | O_CREAT +#endif +#endif + +#ifdef WIN_NT +#define MODE_READ GENERIC_READ +#define MODE_WRITE GENERIC_WRITE +#endif + +#ifdef VMS +#define MODE_WRITE O_WRONLY | O_CREAT | O_TRUNC +#endif + +#ifndef MODE_READ +#define MODE_READ O_RDONLY +#endif + +#ifndef MODE_WRITE +#define MODE_WRITE O_WRONLY | O_CREAT +#endif + +/* Burp Messages */ + +#define msgVerbose_write_charsets 211 +#define msgVerbose_write_collations 212 +#define msgErr_restore_charset 213 +#define msgVerbose_restore_charset 214 +#define msgErr_restore_collation 215 +#define msgVerbose_restore_collation 216 + + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#define GDS_VERSION "Firebird-W32-V1.0" + +#endif /* _BURP_BURP_H_ */ diff --git a/src/burp/burp32.def b/src/burp/burp32.def new file mode 100644 index 0000000000..9c96767511 --- /dev/null +++ b/src/burp/burp32.def @@ -0,0 +1,45 @@ +; 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): ______________________________________. +;------------------------------------------------------------ +; GBAK DLL MODULE DEFINITION FILE +;------------------------------------------------------------ + +LIBRARY GBAK32.DLL + +DESCRIPTION 'INTERBASE BACKUP/RESTORE DLL' +CODE MOVEABLE +DATA MOVEABLE +SEGMENTS + + _TEXT DISCARDABLE + BURP_TEXT DISCARDABLE + CANONICA_TEXT DISCARDABLE + MISC_TEXT DISCARDABLE + MVOL_TEXT DISCARDABLE + BACKUP_TEXT DISCARDABLE + RESTORE_TEXT DISCARDABLE + BURPWEP_TEXT PRELOAD + XDR_TEXT DISCARDABLE + NTOH_TEXT DISCARDABLE + DLLSHELL_TEXT PRELOAD + +EXPORTS + _BURP_gbak @2 +; WEP @1 RESIDENTNAME + +IMPORTS + _gds__start_transaction=gds32.gds__start_transaction diff --git a/src/burp/burp_proto.h b/src/burp/burp_proto.h new file mode 100644 index 0000000000..b916bc2ad1 --- /dev/null +++ b/src/burp/burp_proto.h @@ -0,0 +1,52 @@ +/* + * PROGRAM: JRD Backup and Restore program + * MODULE: burp_proto.h + * DESCRIPTION: Prototype header file for burp.c + * + * 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): ______________________________________. + */ + +#ifndef _BURP_BURP_PROTO_H_ +#define _BURP_BURP_PROTO_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +typedef int (DLL_EXPORT* OUTPUTPROC) (SLONG, UCHAR *); + +extern int BURP_gbak (int, char **, OUTPUTPROC, SLONG); +extern void BURP_abort (void); +extern void BURP_svc_error (USHORT, USHORT, void *, USHORT, void *, USHORT, void *, USHORT, void *, USHORT, void *); +extern void BURP_error (USHORT, void*, void*, void*, void*, void*); +extern void BURP_print_status (STATUS *); +extern void BURP_error_redirect (STATUS *, USHORT, void*, void*); +extern void BURP_msg_partial (USHORT, void*, void*, void*, void*, void*); +extern void BURP_msg_put (USHORT, void*, void*, void*, void*, void*); +extern void BURP_msg_get (USHORT, void*, void*, void*, void*, void*, void*); +extern void BURP_output_version (TEXT *, TEXT *); +extern void BURP_print (USHORT, void*, void*, void*, void*, void*); +extern void BURP_print_warning (STATUS*); +extern void BURP_verbose (USHORT, void*, void*, void*, void*, void*); + +#ifdef __cplusplus +} +#endif + +#endif /* _BURP_BURP_PROTO_H_ */ + diff --git a/src/burp/burpswi.h b/src/burp/burpswi.h new file mode 100644 index 0000000000..cf02cfa1e9 --- /dev/null +++ b/src/burp/burpswi.h @@ -0,0 +1,185 @@ +/* + * PROGRAM: JRD Backup and Restore Program + * MODULE: burpswi.h + * DESCRIPTION: Burp switches + * + * 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): ______________________________________. + */ + +#ifndef _BURP_BURPSWI_H_ +#define _BURP_BURPSWI_H_ + +#include "../jrd/common.h" +#include "../jrd/ibase.h" + +/* Local copies of global variables. They wil be copied into + a data structure. */ + +/* Local variables */ + +#define IN_SW_BURP_0 0 /* the unknowable switch */ +#define IN_SW_BURP_B 1 /* backup */ +#define IN_SW_BURP_C 2 /* create_database */ +#define IN_SW_BURP_F 3 /* file names and starting page */ +#define IN_SW_BURP_M 4 /* backup only metadata */ +#define IN_SW_BURP_N 5 /* do not restore validity conditions */ +#define IN_SW_BURP_P 6 /* specify output page size */ +#define IN_SW_BURP_R 7 /* replace existing database */ +#define IN_SW_BURP_U 9 /* don't back up security information */ +#define IN_SW_BURP_V 10 /* verify actions */ +#define IN_SW_BURP_Z 11 /* print version number */ +#define IN_SW_BURP_D 12 /* backup file on tape - APOLLO only */ +#define IN_SW_BURP_E 13 /* expand (no compress) */ +#define IN_SW_BURP_Y 14 /* redirect/suppress status and error output */ +#define IN_SW_BURP_L 15 /* ignore limbo transactions */ +#define IN_SW_BURP_T 16 /* build a 'transportable' backup (V4 default) */ +#define IN_SW_BURP_O 17 /* commit after each relation */ +#define IN_SW_BURP_I 18 /* deactivate indexes */ +#define IN_SW_BURP_K 19 /* kill any shadows defined on database*/ +#define IN_SW_BURP_G 20 /* inhibit garbage collection */ +#define IN_SW_BURP_IG 21 /* database is largely trash try anyway */ +#define IN_SW_BURP_FA 22 /* blocking factor */ +#define IN_SW_BURP_US 23 /* use all space on data page */ +#define IN_SW_BURP_OL 24 /* write RDB$DESCRIPTIONS & SOURCE in old manner */ +#define IN_SW_BURP_7 25 /* force creation of an ODS 7 database */ +#define IN_SW_BURP_USER 26 /* default user name to use on attach */ +#define IN_SW_BURP_PASS 27 /* default password to use on attach */ +#define IN_SW_BURP_S 28 /* skip some number of bytes if find a bad attribute */ +#define IN_SW_BURP_NT 29 /* build a "non-transportable" backup (V3 default) */ +#define IN_SW_BURP_BUG8183 30 /* use workaround to allow restore database + v3.3 with comment field inside of index + definition */ +#define IN_SW_BURP_ROLE 31 /* default SQL role to use on attach */ +#define IN_SW_BURP_CO 32 /* convert external tables to internal tables during backup */ +#define IN_SW_BURP_BU 33 /* specify page buffers for cache */ +#define IN_SW_BURP_SE 34 /* use services manager */ +#define IN_SW_BURP_MODE 35 /* database could be restored ReadOnly */ +/**************************************************************************/ +/* The next two 'virtual' switches are hidden from user and are needed */ +/* for services API */ +/**************************************************************************/ +#define IN_SW_BURP_HIDDEN_RDONLY 36 +#define IN_SW_BURP_HIDDEN_RDWRITE 37 +/**************************************************************************/ + /* used 0BCDEFGILMNOPRSTUVYZ available AHJQWX */ + +#define BURP_SW_MODE_RO "read_only" +#define BURP_SW_MODE_RW "read_write" + + +static struct in_sw_tab_t burp_in_sw_table [] = +{ + IN_SW_BURP_B, 0, "BACKUP_DATABASE", 0, 0, 0, FALSE, 60, 0, NULL, + /* msg 60: %sBACKUP_DATABASE backup database to file */ + IN_SW_BURP_BU, isc_spb_res_buffers, "BUFFERS", 0, 0, 0, FALSE, 257, 0, NULL, + /* msg 257: %sBU(FFERS) override default page buffers */ + IN_SW_BURP_C, isc_spb_res_create, "CREATE_DATABASE", 0, 0, 0, FALSE, 73, 0, NULL, + /* msg 73: %sCREATE_DATABASE create database from backup file */ + IN_SW_BURP_CO, isc_spb_bkp_convert, "CONVERT", 0, 0, 0, FALSE, 254, 0, NULL, + /* msg 254: %sCO(NVERT) backup external files as tables */ + IN_SW_BURP_E, isc_spb_bkp_expand, "EXPAND", 0, 0, 0, FALSE, 97, 0, NULL, + /* msg 97: %sEXPAND no data compression */ + IN_SW_BURP_F, 0, "FILE_NAMES", 0, 0, 0, FALSE, 0, 0, NULL, + IN_SW_BURP_FA, isc_spb_bkp_factor, "FACTOR", 0, 0, 0, FALSE, 181, 0, NULL, + /* msg 181; %sFACTOR blocking factor */ + IN_SW_BURP_G, isc_spb_bkp_no_garbage_collect, "GARBAGE_COLLECT", 0, 0, 0, FALSE, 177, 0, NULL, + /* msg 177:%sGARBAGE_COLLECT inhibit garbage collection */ + IN_SW_BURP_I, isc_spb_res_deactivate_idx, "INACTIVE", 0, 0, 0, FALSE, 78, 0, NULL, + /* msg 78:%sINACTIVE deactivate indexes during restore */ + IN_SW_BURP_IG, isc_spb_bkp_ignore_checksums, "IGNORE", 0, 0, 0, FALSE, 178, 0, NULL, + /* msg 178:%sIGNORE ignore bad checksums */ + IN_SW_BURP_K, isc_spb_res_no_shadow, "KILL", 0, 0, 0, FALSE, 172, 0, NULL, + /* msg 172:%sKILL restore without creating shadows */ + IN_SW_BURP_L, isc_spb_bkp_ignore_limbo, "LIMBO", 0, 0, 0, FALSE, 98, 0, NULL, + /* msg 98 ignore transactions in limbo */ + IN_SW_BURP_M, isc_spb_bkp_metadata_only, "METADATA", 0, 0, 0, FALSE, 0, 0, NULL, + IN_SW_BURP_M, 0, "META_DATA", 0, 0, 0, FALSE, 63, 0, NULL, + /* msg 63: %sMETA_DATA backup metadata only */ +#ifdef READONLY_DATABASE + IN_SW_BURP_MODE, 0, "MODE", 0, 0, 0, FALSE, 278, 0, NULL, + /* msg 278: %sMODE read_only or read_write access */ +#endif /* READONLY_DATABASE */ + IN_SW_BURP_N, isc_spb_res_no_validity, "NO_VALIDITY", 0, 0, 0, FALSE, 187, 0, NULL, + /* msg 187: %sN(O_VALIDITY) do not restore database validity conditions */ + IN_SW_BURP_NT, isc_spb_bkp_non_transportable, "NT", 0, 0, 0, FALSE, 239, 0, NULL, + /* msg 239: %sNT Non-Transportable backup file format */ + IN_SW_BURP_O, isc_spb_res_one_at_a_time, "ONE_AT_A_TIME", 0, 0, 0, FALSE, 99, 0, NULL, + /* msg 99: %sONE_AT_A_TIME restore one relation at a time */ + IN_SW_BURP_OL, isc_spb_bkp_old_descriptions, "OLD_DESCRIPTIONS", 0, 0, 0, FALSE, 186, 0, NULL, + /* msg 186: %sOLD_DESCRIPTIONS save old style metadata descriptions */ + IN_SW_BURP_P, isc_spb_res_page_size, "PAGE_SIZE", 0, 0, 0, FALSE, 101, 0, NULL, + /* msg 101: %sPAGE_SIZE override default page size */ + IN_SW_BURP_PASS, 0, "PASSWORD", 0, 0, 0, FALSE, 190, 0, NULL, + /* msg 190: %sPA(SSWORD) InterBase password */ + IN_SW_BURP_R, isc_spb_res_replace, "REPLACE_DATABASE", 0, 0, 0, FALSE, 112, 0, NULL, + /* msg 112: %sREPLACE_DATABASE replace database from backup file */ +/************************************************************** +** msg 252: %sRO(LE) InterBase SQL role +***************************************************************/ + IN_SW_BURP_ROLE, isc_spb_sql_role_name, "ROLE", 0, 0, 0, FALSE, 252, 0, NULL, + IN_SW_BURP_S, 0, "SKIP_BAD_DATA", 0, 0, 0, FALSE, 0, 0, NULL, + IN_SW_BURP_SE, 0, "SERVICE", 0, 0, 0, FALSE, 277, 0, NULL, + /* msg 277: %sSE(RVICE) use services manager */ + IN_SW_BURP_T, 0, "TRANSPORTABLE", 0, 0, 0, FALSE, 175, 0, NULL, + /* msg 175: %sTRANSPORTABLE transportable backup -- data in XDR format */ +/* + IN_SW_BURP_U, 0, "UNPROTECTED", 0, 0, 0, FALSE, 0, 0, NULL, +*/ + IN_SW_BURP_US, isc_spb_res_use_all_space, "USE_ALL_SPACE", 0, 0, 0, FALSE, 276, 0, NULL, + /* msg 276: %sUSE_(ALL_SPACE) do not reserve space for record versions */ + IN_SW_BURP_USER, 0, "USER", 0, 0, 0, FALSE, 191, 0, NULL, + /* msg 191: %sUSER InterBase user name */ + IN_SW_BURP_V, isc_spb_verbose, "VERBOSE", 0, 0, 0, FALSE, 0, 0, NULL, + IN_SW_BURP_V, 0, "VERIFY", 0, 0, 0, FALSE, 113, 0, NULL, + /* msg 113: %sVERIFY report each action taken */ + IN_SW_BURP_Y, 0, "Y", 0, 0, 0, FALSE, 109, 0, NULL, + /* msg 109: %sY redirect/suppress output (file path or OUTPUT_SUPPRESS) */ + IN_SW_BURP_Z, 0, "Z", 0, 0, 0, FALSE, 104, 0, NULL, + /* msg 104: %sZ print version number */ +#ifdef DEV_BUILD + IN_SW_BURP_7, 0, "7", 0, 0, 0, FALSE, 0, 0, NULL, +#endif +/* next switch is a hidden option in case of bug_no 8183 */ + IN_SW_BURP_BUG8183, 0, "BUG_8183", 0, 0, 0, FALSE, 0, 0, NULL, +#ifdef READONLY_DATABASE +/**************************************************************************/ +/* The next two 'virtual' switches are hidden from user and are needed */ +/* for services API */ +/**************************************************************************/ + IN_SW_BURP_HIDDEN_RDONLY, isc_spb_res_am_readonly, "mode read_only", 0, 0, 0, FALSE, 0, 0, NULL, + IN_SW_BURP_HIDDEN_RDWRITE, isc_spb_res_am_readwrite, "mode read_write", 0, 0, 0, FALSE, 0, 0, NULL, +/**************************************************************************/ +#endif + IN_SW_BURP_0, 0, NULL, 0, 0, 0, FALSE, 0, 0, NULL +}; + + +/* Definitions for GSPLIT */ +#define IN_SW_SPIT_0 0 /* the unknowable switch */ +#define IN_SW_SPIT_SP 30 /* splits back up files */ +#define IN_SW_SPIT_JT 31 /* joins back up files */ + + +static struct in_sw_tab_t spit_in_sw_table [] = +{ + IN_SW_SPIT_SP, 0, "SPLIT_BK_FILE", 0, 0, 0, FALSE, 0, 0, NULL, + IN_SW_SPIT_JT, 0, "JOIN_BK_FILE", 0, 0, 0, FALSE, 0, 0, NULL, + IN_SW_SPIT_0, 0, NULL, 0, 0, 0, FALSE, 0, 0, NULL +}; + +#endif /* _BURP_BURP_H_ */ diff --git a/src/burp/canon_proto.h b/src/burp/canon_proto.h new file mode 100644 index 0000000000..a860a80061 --- /dev/null +++ b/src/burp/canon_proto.h @@ -0,0 +1,38 @@ +/* + * PROGRAM: JRD Backup and Restore Program + * MODULE: canon_proto.h + * DESCRIPTION: Prototype Header file for canonical.c + * + * 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): ______________________________________. + */ + +#ifndef _BURP_CANON_PROTO_H_ +#define _BURP_CANON_PROTO_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +extern ULONG CAN_encode_decode (struct rel *, struct lstring *, UCHAR *, int); +extern ULONG CAN_slice (struct lstring *, struct lstring *, int, USHORT, UCHAR *); + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* _BURP_CANON_PROTO_H_ */ diff --git a/src/burp/canonical.cpp b/src/burp/canonical.cpp new file mode 100644 index 0000000000..a7f34f1c30 --- /dev/null +++ b/src/burp/canonical.cpp @@ -0,0 +1,1005 @@ +/* + * PROGRAM: JRD Backup and Restore Program + * MODULE: canonical.c + * DESCRIPTION: + * + * 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): ______________________________________. + */ +/* +$Id: canonical.cpp,v 1.1.1.1 2001-05-23 13:26:05 tamlin Exp $ +*/ + +#include "../jrd/ib_stdio.h" +#include +#include "../remote/remote.h" +#include "../burp/burp.h" +#include "../jrd/align.h" +#include "../jrd/sdl.h" +#include "../burp/canon_proto.h" +#include "../jrd/sdl_proto.h" +#include "../remote/xdr_proto.h" +#include "../jrd/gdsassert.h" + +#ifdef PC_PLATFORM +#ifndef NETWARE_386 +extern SLONG ntohl(), htonl(); +#endif +#endif + + +extern "C" { + + +#ifdef mips +#define LOC_DOUBLE +#endif + + +static XDR_INT burp_destroy(XDR*); +static bool_t burp_getbytes(XDR*, SCHAR *, u_int); +static bool_t burp_getlong(XDR*, SLONG *); +static u_int burp_getpostn(XDR*); +static caddr_t burp_inline(XDR*, u_int); +static bool_t burp_putbytes(XDR*, SCHAR *, u_int); +static bool_t burp_putlong(XDR*, SLONG *); +static bool_t burp_setpostn(XDR*, u_int); +static bool_t expand_buffer(XDR*); +static bool_t xdr_datum(XDR*, DSC*, UCHAR*); +#if (defined DEBUG_XDR_MEMORY) && (defined SUPERCLIENT) && (defined WIN_NT) +extern void xdr_debug_memory(XDR*, enum xdr_op, void*, void*, ULONG); +#endif +static bool_t xdr_quad(register XDR*, register SLONG*); +static int xdr_init(XDR*, LSTRING*, enum xdr_op); +static bool_t xdr_slice(XDR*, LSTRING*, USHORT, UCHAR*); + +#ifdef PLATFORM_SUPPLIES_XDR_HYPER +extern bool_t xdr_hyper(register XDR *, SINT64 *); +#else +static bool_t xdr_hyper(register XDR *, SINT64 *); +#endif + +static xdr_t::xdr_ops burp_ops = +{ + burp_getlong, + burp_putlong, + burp_getbytes, + burp_putbytes, + burp_getpostn, + burp_setpostn, + burp_inline, + burp_destroy +}; + +#define INCREMENT 1024 + + +ULONG CAN_encode_decode(REL relation, + LSTRING * buffer, + UCHAR * data, + bool_t direction) +{ +/************************************** + * + * C A N _ e n c o d e _ d e c o d e + * + ************************************** + * + * Functional description + * encode and decode canonical backup. + * + **************************************/ + XDR xdr, *xdrs; + FLD field; + FLD_LENGTH length; + SSHORT dtype, n; + UCHAR *p; + RCRD_OFFSET offset; + USHORT array_fld; + + xdrs = &xdr; + + xdr_init(xdrs, buffer, (direction) ? XDR_ENCODE : XDR_DECODE); + + offset = 0; + for (field = relation->rel_fields; field; field = field->fld_next) + { + if (field->fld_flags & FLD_computed) + continue; + p = data + field->fld_offset; + if (array_fld = field->fld_flags & FLD_array) + length = 8; + else + length = field->fld_length; + if (field->fld_offset >= offset) + offset = field->fld_offset + length; + if (field->fld_type == blr_varying && !array_fld) + offset += sizeof(SSHORT); + if (field->fld_type == blr_blob || array_fld) + dtype = dtype_blob; + else + dtype = (SSHORT) gds_cvt_blr_dtype[field->fld_type]; + switch (dtype) + { + case dtype_text: + if (!xdr_opaque(xdrs, reinterpret_cast(p), length)) + { + return FALSE; + } + break; + + case dtype_varying: + { + VARY pVary = reinterpret_cast(p); + if (!xdr_short( xdrs, + reinterpret_cast(&pVary->vary_length))) + { + return FALSE; + } + if (!xdr_opaque(xdrs, + reinterpret_cast(pVary->vary_string), + pVary->vary_length)) + { + return FALSE; + } + } + break; + + case dtype_cstring: + if (xdrs->x_op == XDR_ENCODE) + n = (strlen(reinterpret_cast(p))); + if (!xdr_short(xdrs, &n)) + return FALSE; + if (!xdr_opaque(xdrs, reinterpret_cast(p), n)) + return FALSE; + if (xdrs->x_op == XDR_DECODE) + p[n] = 0; + break; + + case dtype_short: + if (!xdr_short(xdrs, (SSHORT *) p)) + return FALSE; + break; + + case dtype_long: + case dtype_sql_time: + case dtype_sql_date: + if (!xdr_long(xdrs, (SLONG *) p)) + return FALSE; + break; + + case dtype_real: + if (!xdr_float(xdrs, (float *) p)) + return FALSE; + break; + + case dtype_double: + if (!xdr_double(xdrs, (double *) p)) + return FALSE; + break; + + case dtype_timestamp: + if (!xdr_long(xdrs, &((SLONG *) p)[0])) + return FALSE; + if (!xdr_long(xdrs, &((SLONG *) p)[1])) + return FALSE; + break; + + case dtype_quad: + case dtype_blob: + if (!xdr_quad(xdrs, (SLONG *) p)) + return FALSE; + break; + + case dtype_int64: + if (!xdr_hyper(xdrs, (SINT64 *) p)) + return FALSE; + break; + + default: + assert(FALSE); + return FALSE; + } + } + +/* Next, get null flags */ + + for (field = relation->rel_fields; field; field = field->fld_next) + { + if (field->fld_flags & FLD_computed) + continue; + offset = FB_ALIGN(offset, sizeof(SSHORT)); + p = data + offset; + if (!xdr_short(xdrs, (SSHORT *) p)) + return FALSE; + offset += sizeof(SSHORT); + } + return (xdrs->x_private - xdrs->x_base); +} + + +ULONG CAN_slice(LSTRING * buffer, + LSTRING * slice, + bool_t direction, + USHORT sdl_length, + UCHAR * sdl) +{ +/************************************** + * + * C A N _ s l i c e + * + ************************************** + * + * Functional description + * encode and decode canonical backup. + * + **************************************/ + XDR xdr, *xdrs; + + xdrs = &xdr; + + xdr_init(xdrs, buffer, (direction) ? XDR_ENCODE : XDR_DECODE); + + xdr_slice(xdrs, slice, sdl_length, sdl); + return (xdrs->x_private - xdrs->x_base); +} + + +static XDR_INT burp_destroy( XDR * xdrs) +{ +/************************************** + * + * b u r p _ d e s t r o y + * + ************************************** + * + * Functional description + * Destroy a stream. A no-op. + * + **************************************/ + return 0; +} + + +static bool_t burp_getbytes(XDR* xdrs, SCHAR* buff, u_int bytecount) +{ +/************************************** + * + * b u r p _ g e t b y t e s + * + ************************************** + * + * Functional description + * Fetch a bunch of bytes into a memory stream if it fits. + * + **************************************/ + + if (bytecount && xdrs->x_handy >= bytecount) + { + xdrs->x_handy -= bytecount; + do { + *buff++ = *xdrs->x_private++; + } while (--bytecount); + return TRUE; + } + + while (bytecount) + { + if (!xdrs->x_handy && !expand_buffer(xdrs)) + return FALSE; + *buff++ = *xdrs->x_private++; + --xdrs->x_handy; + --bytecount; + } + + return TRUE; +} + + +static bool_t burp_getlong( XDR * xdrs, SLONG * lp) +{ +/************************************** + * + * b u r p _ g e t l o n g + * + ************************************** + * + * Functional description + * Fetch a longword into a memory stream if it fits. + * + **************************************/ + + SLONG l; + + if (!(*xdrs->x_ops->x_getbytes) (xdrs, reinterpret_cast(&l), 4)) + return FALSE; + + *lp = ntohl(l); + + return TRUE; +} + + +static u_int burp_getpostn( XDR * xdrs) +{ +/************************************** + * + * b u r p _ g e t p o s t n + * + ************************************** + * + * Functional description + * Get the current position (which is also current length) from stream. + * + **************************************/ + + return xdrs->x_private - xdrs->x_base; +} + + +static caddr_t burp_inline( XDR * xdrs, u_int bytecount) +{ +/************************************** + * + * b u r p _ i n l i n e + * + ************************************** + * + * Functional description + * Return a pointer to somewhere in the buffer. + * + **************************************/ + + if (bytecount > (u_int) xdrs->x_handy) + return FALSE; + + return xdrs->x_base + bytecount; +} + + +static bool_t burp_putbytes(XDR* xdrs, SCHAR* buff, u_int bytecount) +{ +/************************************** + * + * b u r p _ p u t b y t e s + * + ************************************** + * + * Functional description + * Fetch a bunch of bytes into a memory stream if it fits. + * + **************************************/ + + if (bytecount && xdrs->x_handy >= bytecount) + { + xdrs->x_handy -= bytecount; + do { + *xdrs->x_private++ = *buff++; + } while (--bytecount); + return TRUE; + } + + while (bytecount) + { + if (xdrs->x_handy <= 0 && !expand_buffer(xdrs)) + { + return FALSE; + } + --xdrs->x_handy; + *xdrs->x_private++ = *buff++; + --bytecount; + } + + return TRUE; +} + + +static bool_t burp_putlong( XDR * xdrs, SLONG * lp) +{ +/************************************** + * + * b u r p _ p u t l o n g + * + ************************************** + * + * Functional description + * Fetch a longword into a memory stream if it fits. + * + **************************************/ + SLONG l; + + l = htonl(*lp); + return (*xdrs->x_ops->x_putbytes) (xdrs, + reinterpret_cast(AOF32L(l)), + 4); +} + + +static bool_t burp_setpostn( XDR * xdrs, u_int bytecount) +{ +/************************************** + * + * b u r p _ s e t p o s t n + * + ************************************** + * + * Functional description + * Set the current position (which is also current length) from stream. + * + **************************************/ + + if (bytecount > (u_int) xdrs->x_handy) + return FALSE; + + xdrs->x_private = xdrs->x_base + bytecount; + + return TRUE; +} + + +static bool_t expand_buffer( XDR * xdrs) +{ +/************************************** + * + * e x p a n d _ b u f f e r + * + ************************************** + * + * Functional description + * Allocate a new, larger buffer, copy + * everything we've got, and release the + * old one. + * + **************************************/ + + caddr_t p, q; + SSHORT length; + LSTRING *buffer; + + buffer = (LSTRING *) xdrs->x_public; + length = (xdrs->x_private - xdrs->x_base) + xdrs->x_handy + INCREMENT; + buffer->lstr_allocated = buffer->lstr_length = length; + + caddr_t new_ = (caddr_t) BURP_ALLOC(length); + + for (p = new_, q = xdrs->x_base; q < xdrs->x_private; *p++ = *q++) + ; + + BURP_FREE(xdrs->x_base); + + xdrs->x_base = new_; + xdrs->x_private = p; + xdrs->x_handy += INCREMENT; + + buffer->lstr_address = (UCHAR *) new_; + + return TRUE; +} + + + +static bool_t xdr_datum( XDR * xdrs, DSC * desc, UCHAR * buffer) +{ +/************************************** + * + * x d r _ d a t u m + * + ************************************** + * + * Functional description + * Handle a data item by relative descriptor and buffer. + * + **************************************/ + UCHAR *p; + SSHORT n; + + p = buffer + (int) desc->dsc_address; + + switch (desc->dsc_dtype) + { + case dtype_text: + if (!xdr_opaque(xdrs, reinterpret_cast(p), desc->dsc_length)) + { + return FALSE; + } + break; + + case dtype_varying: + { + VARY pVary = reinterpret_cast(p); + if (!xdr_short(xdrs, + reinterpret_cast(&pVary->vary_length))) + { + return FALSE; + } + if (!xdr_opaque(xdrs, + reinterpret_cast(pVary->vary_string), + MIN(desc->dsc_length - 2, + pVary->vary_length))) + { + return FALSE; + } + } + break; + + case dtype_cstring: + if (xdrs->x_op == XDR_ENCODE) + n = MIN(strlen(reinterpret_cast(p)), + (size_t) (desc->dsc_length - 1)); + if (!xdr_short(xdrs, &n)) + return FALSE; + if (!xdr_opaque(xdrs, reinterpret_cast(p), n)) + return FALSE; + if (xdrs->x_op == XDR_DECODE) + p[n] = 0; + break; + + case dtype_short: + if (!xdr_short(xdrs, (SSHORT *) p)) + return FALSE; + break; + + case dtype_sql_date: + case dtype_sql_time: + case dtype_long: + if (!xdr_long(xdrs, (SLONG *) p)) + return FALSE; + break; + + case dtype_real: + if (!xdr_float(xdrs, (float *) p)) + return FALSE; + break; + + case dtype_double: + if (!xdr_double(xdrs, (double *) p)) + return FALSE; + break; + + case dtype_timestamp: + if (!xdr_long(xdrs, &((SLONG *) p)[0])) + return FALSE; + if (!xdr_long(xdrs, &((SLONG *) p)[1])) + return FALSE; + break; + + case dtype_quad: + case dtype_blob: + if (!xdr_quad(xdrs, (SLONG *) p)) + return FALSE; + break; + + case dtype_int64: + if (!xdr_hyper(xdrs, (SINT64 *) p)) + return FALSE; + break; + + default: + assert(FALSE); + return FALSE; + } + + return TRUE; +} + +#if (defined DEBUG_XDR_MEMORY) && (defined SUPERCLIENT) && (defined WIN_NT) +void xdr_debug_memory( XDR* xdrs, + enum xdr_op xop, + void* xdrvar, + void* address, + ULONG length) +{ +/************************************** + * + * x d r _ d e b u g _ m e m o r y + * + ************************************** + * + * Functional description + * Track memory allocation patterns of XDR aggregate + * types (i.e. xdr_cstring, xdr_string, etc.) to + * validate that memory is not leaked by overwriting + * XDR aggregate pointers and that freeing a packet + * with REMOTE_free_packet() does not miss anything. + * + * All memory allocations due to marshalling XDR + * variables are recorded in a debug memory alloca- + * tion table stored at the front of a packet. + * + * Once a packet is being tracked it is an assertion + * error if a memory allocation can not be recorded + * due to space limitations or if a previous memory + * allocation being freed cannot be found. At most + * P_MALLOC_SIZE entries can be stored in the memory + * allocation table. A rough estimate of the number + * of XDR aggregates that can hang off a packet can + * be obtained by examining the subpackets defined + * in : A guestimate of 36 at this + * time includes 10 strings used to decode an xdr + * status vector. + * + **************************************/ + + /* This function does nothing here. It is implemented in protocol.c. This + * stub exists for compiling GBAK on windows NT only + * 25-November-1997*/ + +} +#endif + + +static bool_t xdr_quad(register XDR* xdrs, register SLONG* ip) +{ +/************************************** + * + * x d r _ q u a d + * + ************************************** + * + * Functional description + * Map from external to internal representation (or vice versa). + * + **************************************/ + + switch (xdrs->x_op) + { + case XDR_ENCODE: + if ((*xdrs->x_ops->x_putlong) (xdrs, &ip[0]) && + (*xdrs->x_ops->x_putlong) (xdrs, &ip[1])) + return TRUE; + return FALSE; + + case XDR_DECODE: + if (!(*xdrs->x_ops->x_getlong) (xdrs, &ip[0])) + return FALSE; + return (*xdrs->x_ops->x_getlong) (xdrs, &ip[1]); + + case XDR_FREE: + return TRUE; + + default: + assert(FALSE); + return FALSE; + } +} + + + + +#ifdef LOC_DOUBLE +static bool_t xdr_double(xdrs, ip) + register XDR *xdrs; + register double *ip; +{ +/************************************** + * + * x d r _ d o u b l e + * + ************************************** + * + * Functional description + * Map from external to internal representation (or vice versa). + * + **************************************/ + SSHORT t1; + union + { + double temp_double; + SLONG temp_long[2]; + SSHORT temp_short[4]; + } temp; + + switch (xdrs->x_op) + { + case XDR_ENCODE: + temp.temp_double = *ip; +#ifndef IEEE + if (*ip != 0) + temp.temp_short[0] -= 0x20; + t1 = temp.temp_short[0]; + temp.temp_short[0] = temp.temp_short[1]; + temp.temp_short[1] = t1; + t1 = temp.temp_short[2]; + temp.temp_short[2] = temp.temp_short[3]; + temp.temp_short[3] = t1; +#endif +#ifdef VAX + if ((*xdrs->x_ops->x_putlong) (xdrs, &temp.temp_long[1]) && + (*xdrs->x_ops->x_putlong) (xdrs, &temp.temp_long[0])) + return TRUE; +#else + if ((*xdrs->x_ops->x_putlong) (xdrs, &temp.temp_long[0]) && + (*xdrs->x_ops->x_putlong) (xdrs, &temp.temp_long[1])) + return TRUE; +#endif + return FALSE; + + case XDR_DECODE: +#ifdef VAX + if (!(*xdrs->x_ops->x_getlong) (xdrs, &temp.temp_long[1]) || + !(*xdrs->x_ops->x_getlong) (xdrs, &temp.temp_long[0])) + return FALSE; +#else + if (!(*xdrs->x_ops->x_getlong) (xdrs, &temp.temp_long[0]) || + !(*xdrs->x_ops->x_getlong) (xdrs, &temp.temp_long[1])) + return FALSE; +#endif +#ifndef IEEE + t1 = temp.temp_short[0]; + temp.temp_short[0] = temp.temp_short[1]; + temp.temp_short[1] = t1; + t1 = temp.temp_short[2]; + temp.temp_short[2] = temp.temp_short[3]; + temp.temp_short[3] = t1; + if (temp.temp_double != 0) + temp.temp_short[0] += 0x20; +#endif + *ip = temp.temp_double; + return TRUE; + + case XDR_FREE: + return TRUE; + + default: + assert(FALSE); + return FALSE; + } +} + + +static bool_t xdr_float(xdrs, ip) + register XDR *xdrs; + register float *ip; +{ +/************************************** + * + * x d r _ f l o a t + * + ************************************** + * + * Functional description + * Map from external to internal representation (or vice versa). + * + **************************************/ + SSHORT t1; + union + { + float temp_float; + unsigned short temp_short[2]; + } temp; + + switch (xdrs->x_op) + { + case XDR_ENCODE: +#ifndef IEEE + temp.temp_float = *ip; + if (temp.temp_float) + { + t1 = temp.temp_short[0]; + temp.temp_short[0] = temp.temp_short[1]; + temp.temp_short[1] = t1 - 0x100; + } + if (!(*xdrs->x_ops->x_putlong) (xdrs, &temp)) + return FALSE; + return TRUE; +#else + return (*xdrs->x_ops->x_putlong) (xdrs, ip); +#endif + + case XDR_DECODE: +#ifndef IEEE + if (!(*xdrs->x_ops->x_getlong) (xdrs, &temp)) + return FALSE; + if (temp.temp_short[0] | temp.temp_short[1]) + { + t1 = temp.temp_short[1]; + temp.temp_short[1] = temp.temp_short[0]; + temp.temp_short[0] = t1 + 0x100; + } + *ip = temp.temp_float; + return TRUE; +#else + return (*xdrs->x_ops->x_getlong) (xdrs, ip); +#endif + + case XDR_FREE: + return TRUE; + + default: + assert(FALSE); + return FALSE; + } +} +#endif + + + +static int xdr_init( XDR * xdrs, LSTRING * buffer, enum xdr_op x_op) +{ +/************************************** + * + * x d r _ i n i t + * + ************************************** + * + * Functional description + * Initialize an XDR stream for Apollo mailboxes. + * + **************************************/ + + xdrs->x_public = (caddr_t) buffer; + xdrs->x_base = xdrs->x_private = (caddr_t) buffer->lstr_address; + xdrs->x_handy = buffer->lstr_length; + xdrs->x_ops = &burp_ops; + xdrs->x_op = x_op; + + return TRUE; +} + + + +static bool_t xdr_slice(XDR* xdrs, + LSTRING* slice, + USHORT sdl_length, + UCHAR* sdl) +{ +/************************************** + * + * x d r _ s l i c e + * + ************************************** + * + * Functional description + * Move a slice of an array under + * + **************************************/ + STATUS status_vector[20]; + ULONG n; + UCHAR *p, *end; + DSC *desc; + struct sdl_info info; + + if (!xdr_long(xdrs, reinterpret_cast(&slice->lstr_length))) + return FALSE; + +/* Handle operation specific stuff, particularly memory allocation/deallocation */ + + switch (xdrs->x_op) + { + case XDR_ENCODE: + break; + + case XDR_DECODE: + if (!slice->lstr_length) + return TRUE; + if (slice->lstr_length > slice->lstr_allocated && + slice->lstr_allocated) + { + BURP_FREE(slice->lstr_address); + slice->lstr_address = NULL; + } + if (!slice->lstr_address) + { + slice->lstr_address = BURP_ALLOC((SLONG) slice->lstr_length); + if (!slice->lstr_address) { + return FALSE; + } + slice->lstr_allocated = slice->lstr_length; + } + break; + + case XDR_FREE: + if (slice->lstr_allocated) + BURP_FREE(slice->lstr_address); + slice->lstr_address = NULL; + slice->lstr_allocated = 0; + return TRUE; + + default: + assert(FALSE); + return FALSE; + } + +/* Get descriptor of array element */ + + if (SDL_info(status_vector, sdl, &info, 0)) + return FALSE; + + desc = &info.sdl_info_element; + n = slice->lstr_length / desc->dsc_length; + p = slice->lstr_address; + + for (end = p + n * desc->dsc_length; p < end; p += desc->dsc_length) { + if (!xdr_datum(xdrs, desc, p)) { + return FALSE; + } + } + + return TRUE; +} + + +#ifndef PLATFORM_SUPPLIES_XDR_HYPER +static bool_t xdr_hyper(register XDR* xdrs, SINT64* pi64) +{ +/************************************** + * + * x d r _ h y p e r + * + ************************************** + * + * Functional description + * Map a 64-bit Integer from external to internal representation + * (or vice versa). + * + * Enable this for all platforms except those which supply + * xdr_hyper as part of the system xdr library (initially only + * Solaris, but we expect more over time). This function (normally) + * would have been implemented in REMOTE/xdr.c. Since some system + * XDR libraries (HP-UX) do not implement this function, we have it + * in this module. At a later date, when the function is available + * on all platforms, we can start using the system-provided version. + * + * Handles "swapping" of the 2 long's to be "Endian" sensitive. + * + **************************************/ + union + { + SINT64 temp_int64; + SLONG temp_long[2]; + } temp; + + switch (xdrs->x_op) + { + case XDR_ENCODE: + temp.temp_int64 = *pi64; +#ifdef VAX + if ((*xdrs->x_ops->x_putlong) (xdrs, &temp.temp_long[1]) && + (*xdrs->x_ops->x_putlong) (xdrs, &temp.temp_long[0])) + return TRUE; +#else + if ((*xdrs->x_ops->x_putlong) (xdrs, &temp.temp_long[0]) && + (*xdrs->x_ops->x_putlong) (xdrs, &temp.temp_long[1])) + return TRUE; +#endif + return FALSE; + + case XDR_DECODE: +#ifdef VAX + if (!(*xdrs->x_ops->x_getlong) (xdrs, &temp.temp_long[1]) || + !(*xdrs->x_ops->x_getlong) (xdrs, &temp.temp_long[0])) + return FALSE; +#else + if (!(*xdrs->x_ops->x_getlong) (xdrs, &temp.temp_long[0]) || + !(*xdrs->x_ops->x_getlong) (xdrs, &temp.temp_long[1])) + return FALSE; +#endif + *pi64 = temp.temp_int64; + return TRUE; + + case XDR_FREE: + return TRUE; + } + +/* TMN: Now what? 'assert(0)'? 'abort()'? Anyone having a */ +/* clue, feel free to fix the cludge 'return FALSE' */ + return FALSE; +} +#endif /* PLATFORM_SUPPLIES_XDR_HYPER */ + + +} // extern "C" diff --git a/src/burp/depends.mak b/src/burp/depends.mak new file mode 100644 index 0000000000..8aa73d5000 --- /dev/null +++ b/src/burp/depends.mak @@ -0,0 +1,203 @@ +# 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): ______________________________________. +# depends.mak - burp +# Created by 'make depends.mak' +# Created on 1998-11-17 +backup.c.o: backu_proto.h +backup.c.o: backup.c.c +backup.c.o: burp.h +backup.c.o: burp_proto.h +backup.c.o: canon_proto.h +backup.c.o: misc_proto.h +backup.c.o: mvol_proto.h +backup.c.o: source/interbase/include/iberror.h +backup.c.o: source/jrd/align.h +backup.c.o: source/jrd/common.h +backup.c.o: source/jrd/dsc.h +backup.c.o: source/jrd/fil.h +backup.c.o: source/jrd/gds.h +backup.c.o: source/jrd/gds_proto.h +backup.c.o: source/jrd/isc.h +backup.c.o: source/jrd/isc_s_proto.h +backup.c.o: source/jrd/pwd.h +backup.c.o: source/jrd/svc.h +backup.c.o: source/jrd/svc_proto.h +backup.c.o: source/jrd/svc_undoc.h +backup.c.o: source/jrd/thd.h +backup.c.o: source/remote/protocol.h +backup.o: backu_proto.h +backup.o: backup.c +backup.o: burp.h +backup.o: burp_proto.h +backup.o: canon_proto.h +backup.o: misc_proto.h +backup.o: mvol_proto.h +backup.o: source/interbase/include/iberror.h +backup.o: source/jrd/align.h +backup.o: source/jrd/common.h +backup.o: source/jrd/dsc.h +backup.o: source/jrd/fil.h +backup.o: source/jrd/gds.h +backup.o: source/jrd/gds_proto.h +backup.o: source/jrd/gdsassert.h +backup.o: source/jrd/isc.h +backup.o: source/jrd/isc_s_proto.h +backup.o: source/jrd/ods.h +backup.o: source/jrd/pwd.h +backup.o: source/jrd/svc.h +backup.o: source/jrd/svc_proto.h +backup.o: source/jrd/svc_undoc.h +backup.o: source/jrd/thd.h +backup.o: source/remote/protocol.h +burp.o: backu_proto.h +burp.o: burp.c +burp.o: burp.h +burp.o: burp_proto.h +burp.o: burpswi.h +burp.o: misc_proto.h +burp.o: mvol_proto.h +burp.o: resto_proto.h +burp.o: source/interbase/include/iberror.h +burp.o: source/jrd/build_no.h +burp.o: source/jrd/common.h +burp.o: source/jrd/dsc.h +burp.o: source/jrd/fil.h +burp.o: source/jrd/gds.h +burp.o: source/jrd/gds_proto.h +burp.o: source/jrd/gdsassert.h +burp.o: source/jrd/ibase.h +burp.o: source/jrd/ibsetjmp.h +burp.o: source/jrd/isc.h +burp.o: source/jrd/isc_s_proto.h +burp.o: source/jrd/license.h +burp.o: source/jrd/msg_encode.h +burp.o: source/jrd/pwd.h +burp.o: source/jrd/svc.h +burp.o: source/jrd/svc_proto.h +burp.o: source/jrd/svc_undoc.h +burp.o: source/jrd/thd.h +burp.o: source/jrd/time.h +burp.o: source/jrd/why_proto.h +burpwep.o: burpwep.c +burpwep.o: source/jrd/common.h +canonical.o: burp.h +canonical.o: burp_proto.h +canonical.o: canon_proto.h +canonical.o: canonical.c +canonical.o: misc_proto.h +canonical.o: source/interbase/include/iberror.h +canonical.o: source/jrd/align.h +canonical.o: source/jrd/common.h +canonical.o: source/jrd/dsc.h +canonical.o: source/jrd/fil.h +canonical.o: source/jrd/gds.h +canonical.o: source/jrd/gds_proto.h +canonical.o: source/jrd/ibsetjmp.h +canonical.o: source/jrd/isc.h +canonical.o: source/jrd/isc_s_proto.h +canonical.o: source/jrd/pwd.h +canonical.o: source/jrd/sdl.h +canonical.o: source/jrd/sdl_proto.h +canonical.o: source/jrd/svc.h +canonical.o: source/jrd/svc_proto.h +canonical.o: source/jrd/svc_undoc.h +canonical.o: source/jrd/thd.h +canonical.o: source/remote/allr_proto.h +canonical.o: source/remote/blk.h +canonical.o: source/remote/protocol.h +canonical.o: source/remote/remote.h +canonical.o: source/remote/remote_def.h +canonical.o: source/remote/xdr.h +gsplit.o: gsplit.c +gsplit.o: gsplit.h +gsplit.o: source/jrd/common.h +misc.o: burp.h +misc.o: burp_proto.h +misc.o: misc.c +misc.o: misc_proto.h +misc.o: source/interbase/include/iberror.h +misc.o: source/jrd/common.h +misc.o: source/jrd/dsc.h +misc.o: source/jrd/fil.h +misc.o: source/jrd/gds.h +misc.o: source/jrd/gds_proto.h +misc.o: source/jrd/isc.h +misc.o: source/jrd/isc_s_proto.h +misc.o: source/jrd/pwd.h +misc.o: source/jrd/svc.h +misc.o: source/jrd/svc_proto.h +misc.o: source/jrd/svc_undoc.h +misc.o: source/jrd/thd.h +mvol.o: burp.h +mvol.o: burp_proto.h +mvol.o: misc_proto.h +mvol.o: mvol.c +mvol.o: mvol_proto.h +mvol.o: source/interbase/include/iberror.h +mvol.o: source/jrd/common.h +mvol.o: source/jrd/dsc.h +mvol.o: source/jrd/fil.h +mvol.o: source/jrd/gds.h +mvol.o: source/jrd/gds_proto.h +mvol.o: source/jrd/gdsassert.h +mvol.o: source/jrd/isc.h +mvol.o: source/jrd/isc_s_proto.h +mvol.o: source/jrd/pwd.h +mvol.o: source/jrd/svc.h +mvol.o: source/jrd/svc_proto.h +mvol.o: source/jrd/svc_undoc.h +mvol.o: source/jrd/thd.h +restore.o: burp.h +restore.o: burp_proto.h +restore.o: canon_proto.h +restore.o: misc_proto.h +restore.o: mvol_proto.h +restore.o: resto_proto.h +restore.o: restore.c +restore.o: source/interbase/include/iberror.h +restore.o: source/intl/charsets.h +restore.o: source/jrd/align.h +restore.o: source/jrd/build_no.h +restore.o: source/jrd/common.h +restore.o: source/jrd/dsc.h +restore.o: source/jrd/fil.h +restore.o: source/jrd/flags.h +restore.o: source/jrd/gds.h +restore.o: source/jrd/gds_proto.h +restore.o: source/jrd/gdsassert.h +restore.o: source/jrd/isc.h +restore.o: source/jrd/isc_s_proto.h +restore.o: source/jrd/license.h +restore.o: source/jrd/obj.h +restore.o: source/jrd/pwd.h +restore.o: source/jrd/svc.h +restore.o: source/jrd/svc_proto.h +restore.o: source/jrd/svc_undoc.h +restore.o: source/jrd/thd.h +restore.o: source/remote/protocol.h +spit.o: burpswi.h +spit.o: source/interbase/include/iberror.h +spit.o: source/jrd/blk.h +spit.o: source/jrd/common.h +spit.o: source/jrd/dsc.h +spit.o: source/jrd/gdsassert.h +spit.o: source/jrd/ibase.h +spit.o: source/jrd/isc.h +spit.o: source/jrd/jrd.h +spit.o: source/jrd/thd.h +spit.o: spit.c +spit.o: spit.h diff --git a/src/burp/makefile.mak b/src/burp/makefile.mak new file mode 100644 index 0000000000..7a1094ccdc --- /dev/null +++ b/src/burp/makefile.mak @@ -0,0 +1,173 @@ +# 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): ______________________________________. +# 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): ______________________________________. +#use $(Cx) to have command line options appear in DLL_CFLAGS +#NOTE: USE -DCx=-DWINBETA for BETA version string (see code in burp.c) +#--------------------------- PC BURP MAKEFILE --------------------------- +ROOT=.. +.path.c=$(ROOT)\burp + +# Path to devkit and clisdk +!if !$d(TPATH) +TPATH=.. +!endif + +# CRM - temporarily disabled until warnings are cleaned up!!! +LCFLAGS=-N- + +# These are C flags if you want to enable useful warnings. +#LCFLAGS=$(LCFLAGS) -w -w-ucp -w-sus -w-par -w-stu -w-aus -w-pia + +!include $(ROOT)\std.mk + +#--------------------------- SOURCE COMPONENTS ----------------------------- +WINSRCS = dllshell.c + +S1 = burp.c canonical.c misc.c mvol.c backup.c restore.c +S2 = burpwep.c +SRCS = $(S1) $(S2) + +# sources from the remote component used to build gbak.dll +REMSRCS = xdr.c ntoh.c + +# NOTE: Object list - c0dx must be the first obj in the list! +!if $d(WIN32) +OBJS=$(TOOLPATH)\lib\c0d32.obj $(SRCS:.c=.obj) \ + $(REMSRCS:.c=.obj) $(WINSRCS:.c=.obj) +!else +OBJS=$(TOOLPATH)\lib\c0d$(MODEL).obj $(SRCS:.c=.obj) \ + $(REMSRCS:.c=.obj) $(WINSRCS:.c=.obj) +!endif + +# import libraries +# NOTE: Client libs should now be taken from generic directories - Jeevan +#BURPLIBS = $(ROOT)\jrd\$(.path.obj)\gds.lib $(ROOT)\jrd\$(.path.obj)\iutls.lib +!if $d(WIN32) +BURPLIBS = $(SHRLIB_PATH)\gds32.lib +LIBS = import32 cw32 +!else +BURPLIBS = $(INSTALLLIB)\gds.lib $(INSTALLLIB)\iutls.lib +LIBS = mathw$(MODEL) import cw$(MODEL) +!endif + +# Extra files which are used to build the product +XFILES = burp.def makefile.mak backup.e restore.e + +#---------------------------- TARGET LIST -------------------------------- +!if $d(WIN32) +ALLBIN = gbak32.dll +ALLLIBS = gbak32.lib +!else +ALLBIN = gbak.dll +ALLLIBS = gbak.lib +!endif + +gbk_dll: makefile.tmp $(ALLBIN) $(ALLLIB) +all: makefile.tmp extern_libs $(ALLLIBS) $(ALLBIN) +allbin: makefile.tmp extern_libs $(ALLBIN) +alllibs: makefile.tmp $(ALLLIBS) + + +#------------------------------ RULES ------------------------------------- + +!if $d(WIN32) +gbak32.dll: $(OBJS) $(BURPLIBS) $(ROOT)\jrd\version_95.rc burp.rsp burp32.def + $(TLINK) $(LFLAGS) @$(.path.obj)\burp.rsp, @&&| + $< + $*.map + $(LIBS) $(BURPLIBS) + burp32.def +| + $(RC) -i $(TOOLPATH)\include $(ROOT)\jrd\version_95.rc $< +!else +gbak.dll: $(OBJS) $(BURPLIBS) $(ROOT)\jrd\version.rc burp.rsp burp.def + $(TLINK) $(LFLAGS) @$(.path.obj)\burp.rsp, @&&| + $< + $*.map + $(LIBS) $(BURPLIBS) + burp.def +| + $(RC) -i $(TOOLPATH)\include $(ROOT)\jrd\version.rc $< +!endif + +#----------- Response files ----------- +burp.rsp: $(OBJS) + copy &&| + $** +| $< + +#----------- Import Libraries ----------- +!if $d(WIN32) +gbak32.lib: burp32.def + $(IMPLIB) $< $** +!else +gbak.lib: burp.def + $(IMPLIB) -o $< $** +!endif + +# Because of the circular "architecture" here, we need to build +# required libraries within each of the makefiles. +extern_libs: + cd $(ROOT)\jrd + make $(MAKEFLAGS) alllibs + cd $(ROOT)\burp + +# ---- Rules for source files which require special handling ---- +xdr.obj: $(ROOT)\remote\xdr.c + $(CC) -c -DBURP @&&< + $(CFLAGS) +< $** + +ntoh.obj: $(ROOT)\remote\ntoh.c + $(CC) -c @&&< + $(CFLAGS) +< $** + +dllshell.obj: $(ROOT)\jrd\dllshell.c + $(CC) -c @&&< + $(CFLAGS) +< $** + +#---------------------------- UTILITIES --------------------------------- + +# Copy targets to a known installation directory +install: extern_libs $(ALLBIN) $(ALLLIBS) + for %i in ($(ALLBIN)) do copy $(.path.dll)\%i $(INSTALLBIN) + for %i in ($(ALLLIBS)) do copy $(.path.dll)\%i $(INSTALLLIB) + +# Refresh all the source & header files from the DEVSRC directory +srcs:: + copy $(DEVSRC)\burp\*.h + for %i in ($(S1)) do copy $(DEVSRC)\burp\%i + for %i in ($(S2)) do copy $(DEVSRC)\burp\%i + for %i in ($(XFILES)) do copy $(DEVSRC)\burp\%i + diff --git a/src/burp/misc.cpp b/src/burp/misc.cpp new file mode 100644 index 0000000000..8afbb817cb --- /dev/null +++ b/src/burp/misc.cpp @@ -0,0 +1,154 @@ +/* + * PROGRAM: JRD Backup and Restore Program + * MODULE: misc.c + * DESCRIPTION: Miscellaneous useful routines + * + * The contents of this file are subject to the Interbase Public + * License Version 1.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy + * of the License at http://www.Inprise.com/IPL.html + * + * Software distributed under the License is distributed on an + * "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either express + * or implied. See the License for the specific language governing + * rights and limitations under the License. + * + * The Original Code was created by Inprise Corporation + * and its predecessors. Portions created by Inprise Corporation are + * Copyright (C) Inprise Corporation. + * + * All Rights Reserved. + * Contributor(s): ______________________________________. + */ + +#include "../jrd/ib_stdio.h" +#include +#ifdef UNIXWARE +#include +#endif + +#include "../burp/burp.h" +#include "../burp/burp_proto.h" +#include "../burp/misc_proto.h" + +#ifdef SUPERSERVER +#include "../jrd/thd_proto.h" +#endif + + +extern "C" { + + +UCHAR *MISC_alloc_burp(ULONG size) +{ +/************************************** + * + * M I S C _ a l l o c _ b u r p + * + ************************************** + * + * Functional description + * Allocate block of memory. Note that it always zeros out memory. + * This could be optimized. + * + **************************************/ + UCHAR *block; + TGBL tdgbl; + + tdgbl = GET_THREAD_DATA; + +/* Add some header space to store a list of blocks allocated for this gbak */ + size += ROUNDUP(sizeof(UCHAR *), ALIGNMENT); + + if (!(block = gds__alloc(size))) + /* NOMEM: message & abort FREE: all items freed at gbak exit */ + { + BURP_error(238, NULL, NULL, NULL, NULL, NULL); /* msg 238: System memory exhaused */ + return NULL; + } + + memset(block, 0, size); + +/* FREE: We keep a linked list of all gbak memory allocations, which + * are then freed when gbak exits. This is important for + * NETWARE in particular. + */ + *((UCHAR **) block) = tdgbl->head_of_mem_list; + tdgbl->head_of_mem_list = block; + + return (block + ROUNDUP(sizeof(UCHAR *), ALIGNMENT)); +} + + +void MISC_free_burp( void *free) +{ +/************************************** + * + * M I S C _ f r e e _ b u r p + * + ************************************** + * + * Functional description + * Release an unwanted block. + * + **************************************/ + UCHAR **block; + TGBL tdgbl; + UCHAR **ptr; + + tdgbl = GET_THREAD_DATA; + + if (free != NULL) { + /* Point at the head of the allocated block */ + block = + (UCHAR **) ((UCHAR *) free - ROUNDUP(sizeof(UCHAR *), ALIGNMENT)); + + /* Scan for this block in the list of blocks */ + for (ptr = &tdgbl->head_of_mem_list; *ptr; ptr = (UCHAR **) * ptr) { + if (*ptr == (UCHAR *) block) { + /* Found it - remove it from the list */ + *ptr = *block; + + /* and free it */ + gds__free((SLONG *) block); + return; + } + } + + /* We should always find the block in the list */ + BURP_error(238, NULL, NULL, NULL, NULL, NULL); /* msg 238: System memory exhausted */ + /* (too lazy to add a better message) */ + } +} + + +void MISC_terminate(UCHAR* from, UCHAR* to, ULONG length, ULONG max_length) +{ +/************************************** + * + * M I S C _ t e r m i n a t e + * + ************************************** + * + * Functional description + * Null-terminate a possibly non- + * null-terminated string with max + * buffer room. + * + **************************************/ + + if (length) { + length = MIN(length, max_length - 1); + do + *to++ = *from++; + while (--length); + *to++ = '\0'; + } + else { + while (max_length-- && (*to++ = *from++)); + *--to = '\0'; + } +} + + +} // extern "C" diff --git a/src/burp/misc_proto.h b/src/burp/misc_proto.h new file mode 100644 index 0000000000..549b82cabb --- /dev/null +++ b/src/burp/misc_proto.h @@ -0,0 +1,39 @@ +/* + * PROGRAM: JRD Backup and Restore Program + * MODULE: misc_proto.h + * DESCRIPTION: Prototype Header file for misc.c + * + * 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): ______________________________________. + */ + +#ifndef _BURP_MISC_PROTO_H_ +#define _BURP_MISC_PROTO_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +extern UCHAR *MISC_alloc_burp (ULONG); +extern void MISC_free_burp (void *); +extern void MISC_terminate (UCHAR *, UCHAR *, ULONG, ULONG); + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* _BURP_MISC_PROTO_H_ */ diff --git a/src/burp/mvol.cpp b/src/burp/mvol.cpp new file mode 100644 index 0000000000..7918eabd70 --- /dev/null +++ b/src/burp/mvol.cpp @@ -0,0 +1,1453 @@ +/* + * PROGRAM: JRD Backup and Restore Program + * MODULE: multivol.c + * DESCRIPTION: + * + * The contents of this file are subject to the Interbase Public + * License Version 1.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy + * of the License at http://www.Inprise.com/IPL.html + * + * Software distributed under the License is distributed on an + * "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either express + * or implied. See the License for the specific language governing + * rights and limitations under the License. + * + * The Original Code was created by Inprise Corporation + * and its predecessors. Portions created by Inprise Corporation are + * Copyright (C) Inprise Corporation. + * + * All Rights Reserved. + * Contributor(s): ______________________________________. + */ + +#include "../jrd/ib_stdio.h" +#include +#include +#include +#include +#include "../burp/burp.h" +#ifdef WIN_NT +#include +#include +#ifdef TEXT +#undef TEXT +#endif +#define TEXT char +#endif +#include "../burp/burp_proto.h" +#include "../burp/mvol_proto.h" +#include "../jrd/gds_proto.h" +#include "../jrd/gdsassert.h" +#include "../jrd/thd_proto.h" +#ifndef VMS +#include +#include +#else +#include +#include +#endif +#if (defined WIN_NT || (defined PC_PLATFORM && !defined NETWARE_386)) +#include +#endif +#ifdef unix +#include +#endif + + +extern "C" { + + +#define OPEN_MASK ((int) 0666) + +#ifdef VMS +#define TERM_INPUT "sys$input" +#define TERM_OUTPUT "sys$error" +#endif + +#ifdef WIN_NT +#define TERM_INPUT "CONIN$" +#define TERM_OUTPUT "CONOUT$" +#endif + +#ifndef TERM_INPUT +#define TERM_INPUT "/dev/tty" +#define TERM_OUTPUT "/dev/tty" +#endif + +#define MAX_HEADER_SIZE 512 + +#define BITS_ON(word,bits) ((bits) == ((word)&(bits))) + +#define GET() (--(tdgbl->mvol_io_cnt) >= 0 ? *(tdgbl->mvol_io_ptr)++ : 255) +#define GET_ATTRIBUTE(att) ((att) = (ATT_TYPE) GET()) + +#define PUT(c) --(tdgbl->mvol_io_cnt); *(tdgbl->mvol_io_ptr)++ = (UCHAR) (c) +#define PUT_NUMERIC(attribute, value) put_numeric ((attribute), (value)) +#define PUT_ASCIZ(attribute, string) put_asciz ((attribute), (string)) + +static void bad_attribute(USHORT, USHORT); +static void file_not_empty(void); +static SLONG get_numeric(void); +static int get_text(UCHAR*, SSHORT); +static void prompt_for_name(SCHAR*, int); +static void put_asciz(SCHAR, SCHAR*); +static void put_numeric(SCHAR, int); +static BOOLEAN read_header(DESC, ULONG*, USHORT*, USHORT); +static BOOLEAN write_header(DESC, ULONG, USHORT); +static void* next_volume(DESC, int, USHORT); + + +//____________________________________________________________ +// +// +void MVOL_fini_read(int* count_kb) +{ + TGBL tdgbl = GET_THREAD_DATA; + + if (strcmp(tdgbl->mvol_old_file, "stdin") != 0) + { + CLOSE(tdgbl->file_desc); + + FIL file; + for (file = tdgbl->gbl_sw_backup_files; file; file = file->fil_next) + { + if (file->fil_fd == tdgbl->file_desc) { + file->fil_fd = INVALID_HANDLE_VALUE; + } + } + } + + tdgbl->file_desc = INVALID_HANDLE_VALUE; + *count_kb = tdgbl->mvol_cumul_count_kb; + BURP_FREE(tdgbl->mvol_io_buffer); + tdgbl->mvol_io_buffer = NULL; + tdgbl->io_cnt = 0; + tdgbl->io_ptr = NULL; +} + + +//____________________________________________________________ +// +// +void MVOL_fini_write(int* io_cnt, UCHAR** io_ptr, int* count_kb) +{ + TGBL tdgbl; + FIL file; + + tdgbl = GET_THREAD_DATA; + + (void) MVOL_write(rec_end, io_cnt, io_ptr); + FLUSH(tdgbl->file_desc); + if (strcmp(tdgbl->mvol_old_file, "stdout") != 0) + { + CLOSE(tdgbl->file_desc); + for (file = tdgbl->gbl_sw_backup_files; file; file = file->fil_next) + { + if (file->fil_fd == tdgbl->file_desc) + file->fil_fd = INVALID_HANDLE_VALUE; + } + } + tdgbl->file_desc = INVALID_HANDLE_VALUE; + *count_kb = tdgbl->mvol_cumul_count_kb; + BURP_FREE(tdgbl->mvol_io_header); + tdgbl->mvol_io_header = NULL; + tdgbl->mvol_io_buffer = NULL; + tdgbl->io_cnt = 0; + tdgbl->io_ptr = NULL; +} + + +//____________________________________________________________ +// +// +void MVOL_init(ULONG io_buf_size) +{ + TGBL tdgbl = GET_THREAD_DATA; + + tdgbl->mvol_io_buffer_size = io_buf_size; +} + + +//____________________________________________________________ +// +// Read init record from backup file +// +void MVOL_init_read(UCHAR* database_name, + UCHAR* file_name, + USHORT* format, + int* cnt, + UCHAR** ptr) +{ + ULONG temp_buffer_size; + UCHAR *new_buffer; + TGBL tdgbl; + + tdgbl = GET_THREAD_DATA; + + tdgbl->mvol_volume_count = 1; + tdgbl->mvol_empty_file = TRUE; + + if (file_name != (UCHAR *) NULL) + { + strncpy(tdgbl->mvol_old_file, (char*) file_name, MAX_FILE_NAME_LENGTH); + tdgbl->mvol_old_file[MAX_FILE_NAME_LENGTH - 1] = 0; + } + else + { + tdgbl->mvol_old_file[0] = 0; + } + + tdgbl->mvol_actual_buffer_size = temp_buffer_size = + tdgbl->mvol_io_buffer_size; + tdgbl->mvol_io_buffer = BURP_ALLOC(temp_buffer_size); + tdgbl->gbl_backup_start_time[0] = 0; + + read_header(tdgbl->file_desc, &temp_buffer_size, format, TRUE); + + if (temp_buffer_size > tdgbl->mvol_actual_buffer_size) + { + new_buffer = BURP_ALLOC(temp_buffer_size); + memcpy(new_buffer, tdgbl->mvol_io_buffer, tdgbl->mvol_io_buffer_size); + BURP_FREE(tdgbl->mvol_io_buffer); + tdgbl->mvol_io_ptr = + new_buffer + (tdgbl->mvol_io_ptr - tdgbl->mvol_io_buffer); + tdgbl->mvol_io_buffer = new_buffer; + } + + tdgbl->mvol_actual_buffer_size = tdgbl->mvol_io_buffer_size = + temp_buffer_size; + *cnt = tdgbl->mvol_io_cnt; + *ptr = tdgbl->mvol_io_ptr; +} + + +//____________________________________________________________ +// +// Write init record to the backup file +// +void MVOL_init_write(UCHAR* database_name, + UCHAR* file_name, + int* cnt, + UCHAR** ptr) +{ + ULONG temp_buffer_size; + TGBL tdgbl; + + tdgbl = GET_THREAD_DATA; + + tdgbl->mvol_volume_count = 1; + tdgbl->mvol_empty_file = TRUE; + + if (file_name != (UCHAR *) NULL) + { + strncpy(tdgbl->mvol_old_file, (char*)file_name, MAX_FILE_NAME_LENGTH); + tdgbl->mvol_old_file[MAX_FILE_NAME_LENGTH - 1] = 0; + } + else + { + tdgbl->mvol_old_file[0] = 0; + } + + tdgbl->mvol_actual_buffer_size = tdgbl->mvol_io_buffer_size; + temp_buffer_size = tdgbl->mvol_io_buffer_size * tdgbl->gbl_sw_blk_factor; + tdgbl->mvol_io_ptr = tdgbl->mvol_io_buffer = + BURP_ALLOC(temp_buffer_size + MAX_HEADER_SIZE); + tdgbl->mvol_io_cnt = tdgbl->mvol_actual_buffer_size; + + while (!write_header(tdgbl->file_desc, temp_buffer_size, FALSE)) + { + if (tdgbl->action->act_action == ACT_backup_split) + { + BURP_error(269, tdgbl->action->act_file->fil_name, 0, 0, 0, 0); + /* msg 269 can't write a header record to file %s */ + } + tdgbl->file_desc = next_volume(tdgbl->file_desc, MODE_WRITE, FALSE); + } + + tdgbl->mvol_actual_buffer_size = temp_buffer_size; + + *cnt = tdgbl->mvol_io_cnt; + *ptr = tdgbl->mvol_io_ptr; +} + + +#ifndef WIN_NT +//____________________________________________________________ +// +// Read a buffer's worth of data. (non-WIN_NT) +// +int MVOL_read(int* cnt, UCHAR** ptr) +{ + TGBL tdgbl; + + tdgbl = GET_THREAD_DATA; + + for (;;) + { + tdgbl->mvol_io_cnt = + read( tdgbl->file_desc, + tdgbl->mvol_io_buffer, + tdgbl->mvol_io_buffer_size); + tdgbl->mvol_io_ptr = tdgbl->mvol_io_buffer; + + if (tdgbl->mvol_io_cnt > 0) { + break; + } + + { + if (!tdgbl->mvol_io_cnt || errno == EIO) + { + tdgbl->file_desc = next_volume(tdgbl->file_desc, MODE_READ, FALSE); + if (tdgbl->mvol_io_cnt > 0) + { + break; + } + } + +#ifndef NETWARE_386 + else if (!SYSCALL_INTERRUPTED(errno)) + { + if (cnt) + { + BURP_error_redirect(0, 220, NULL, NULL); + /* msg 220 Unexpected I/O error while reading from backup file */ + } + else + { + BURP_error_redirect(0, 50, NULL, NULL); + /* msg 50 unexpected end of file on backup file */ + } + } +#endif + + } + } + + tdgbl->mvol_cumul_count_kb += tdgbl->mvol_io_cnt / 1024; + file_not_empty(); + + *ptr = tdgbl->mvol_io_ptr + 1; + *cnt = tdgbl->mvol_io_cnt - 1; + + return *(tdgbl->mvol_io_ptr); +} + + +#else +//____________________________________________________________ +// +// Read a buffer's worth of data. (WIN_NT) +// +int MVOL_read(int* cnt, UCHAR** ptr) +{ + BOOL ret; + + TGBL tdgbl = GET_THREAD_DATA; + + for (;;) + { + ret = ReadFile(tdgbl->file_desc, + tdgbl->mvol_io_buffer, + tdgbl->mvol_io_buffer_size, + reinterpret_cast(&tdgbl->mvol_io_cnt), + NULL); + tdgbl->mvol_io_ptr = tdgbl->mvol_io_buffer; + if (tdgbl->mvol_io_cnt > 0) + break; + else + { + if (!tdgbl->mvol_io_cnt) + { + tdgbl->file_desc = next_volume(tdgbl->file_desc, MODE_READ, FALSE); + if (tdgbl->mvol_io_cnt > 0) + break; + } + else if (GetLastError() != ERROR_HANDLE_EOF) + { + if (cnt) + BURP_error_redirect(0, 220, NULL, NULL); + /* msg 220 Unexpected I/O error while reading from backup file */ + else + BURP_error_redirect(0, 50, NULL, NULL); + /* msg 50 unexpected end of file on backup file */ + } + } + } + + tdgbl->mvol_cumul_count_kb += tdgbl->mvol_io_cnt / 1024; + file_not_empty(); + + *ptr = tdgbl->mvol_io_ptr + 1; + *cnt = tdgbl->mvol_io_cnt - 1; + + return *(tdgbl->mvol_io_ptr); +} +#endif /* !WIN_NT */ + + +//____________________________________________________________ +// +// Read a chunk of data from the IO buffer. +// Return a pointer to the first position NOT read into. +// +UCHAR* MVOL_read_block(TGBL tdgbl, UCHAR * ptr, ULONG count) +{ +/* To handle tape drives & Multi-volume boundaries, use the normal + * read function, instead of doing a more optimal bulk read. + */ + + while (count) + { + ULONG n; + + /* If buffer empty, reload it */ + if (tdgbl->io_cnt <= 0) + { + *ptr++ = MVOL_read(&tdgbl->io_cnt, &tdgbl->io_ptr); + + /* One byte was "read" by MVOL_read */ + count--; + } + + n = MIN(count, (ULONG) tdgbl->io_cnt); + + /* Copy data from the IO buffer */ + + (void) memcpy(ptr, tdgbl->io_ptr, n); + ptr += n; + + /* Skip ahead in current buffer */ + + count -= n; + tdgbl->io_cnt -= n; + tdgbl->io_ptr += n; + + } + return ptr; +} + + +//____________________________________________________________ +// +// Skip head in the IO buffer. Often used when only +// doing partial restores. +// +void MVOL_skip_block( TGBL tdgbl, ULONG count) +{ +/* To handle tape drives & Multi-volume boundaries, use the normal + * read function, instead of doing a more optimal seek. + */ + + while (count) + { + ULONG n; + + /* If buffer empty, reload it */ + if (tdgbl->io_cnt <= 0) + { + (void) MVOL_read(&tdgbl->io_cnt, &tdgbl->io_ptr); + + /* One byte was "read" by MVOL_read */ + count--; + } + + n = MIN(count, (ULONG) tdgbl->io_cnt); + + /* Skip ahead in current buffer */ + + count -= n; + tdgbl->io_cnt -= n; + tdgbl->io_ptr += n; + } +} + + +#ifdef WIN_NT +//____________________________________________________________ +// +// detect if it's a tape, rewind if so +// and set the buffer size +// +HANDLE MVOL_open(TEXT * name, DWORD mode, DWORD create) +{ + TGBL tdgbl; + HANDLE handle; + TAPE_GET_MEDIA_PARAMETERS param; + DWORD size = sizeof(param); + + tdgbl = GET_THREAD_DATA; + + if (strnicmp(name, "\\\\.\\tape", 8)) + handle = CreateFile(name, mode, + mode == MODE_WRITE ? 0 : FILE_SHARE_READ, + NULL, create, FILE_ATTRIBUTE_NORMAL, NULL); + else + { + /* it's a tape device */ + /* Note: we *want* to open the tape in Read-only mode or in + * write-only mode, but it turns out that on NT SetTapePosition + * will fail (thereby not rewinding the tape) if the tape is + * opened write-only, so we will make sure that we always have + * read access. So much for standards! + * Ain't Windows wonderful??? + */ + /* Note: we *want* to open the tape in FILE_EXCLUSIVE_WRITE, but + * during testing discovered that several NT tape drives do not + * work unless we specify FILE_SHARE_WRITE as the open mode. + * So it goes... + */ + handle = CreateFile(name, + mode | MODE_READ, + mode == + MODE_WRITE ? FILE_SHARE_WRITE : FILE_SHARE_READ, + 0, OPEN_EXISTING, 0, NULL); + if (handle != INVALID_HANDLE_VALUE) + { + /* emulate UNIX rewinding the tape on open: + * This MUST be done since Windows does NOT have anything + * like mt to allow the user to do tape management. The + * implication here is that we will be able to write ONLY + * one (1) database per tape. This is bad if the user wishes to + * backup several small databases. + * Note: We are intentionally NOT trapping for errors during + * rewind, since if we can not rewind, we are either a non-rewind + * device (then it is user controlled) or we have a problem with + * the physical media. In the latter case I would rather wait for + * the write to fail so that we can loop and prompt the user for + * a different file/device. + */ + SetTapePosition(handle, TAPE_REWIND, 0, 0, 0, FALSE); + if (GetTapeParameters( handle, + GET_TAPE_MEDIA_INFORMATION, + &size, + ¶m) == NO_ERROR) + { + tdgbl->io_buffer_size = param.BlockSize; + } + } + } + return handle; +} +#endif /* WIN_NT */ + + +//____________________________________________________________ +// +// Write a buffer's worth of data. +// +UCHAR MVOL_write(UCHAR c, int *io_cnt, UCHAR ** io_ptr) +{ + UCHAR *ptr; + int left, cnt, size_to_write; + USHORT full_buffer; + TGBL tdgbl; + FIL file; + +#ifdef WIN_NT + DWORD err; +#endif /* WIN_NT */ + + tdgbl = GET_THREAD_DATA; + + size_to_write = BURP_UP_TO_BLOCK(*io_ptr - tdgbl->mvol_io_buffer); + + for (ptr = tdgbl->mvol_io_buffer, left = size_to_write; + left > 0; + ptr += cnt, left -= cnt) + { + if (tdgbl->action->act_action == ACT_backup_split) + { + /* Write to the current file till fil_lingth > 0, otherwise + switch to the next one */ + if (tdgbl->action->act_file->fil_length == 0) + { + if (tdgbl->action->act_file->fil_next) + { + CLOSE(tdgbl->file_desc); + for (file = tdgbl->gbl_sw_backup_files; file; file = file->fil_next) + { + if (file->fil_fd == tdgbl->file_desc) + file->fil_fd = INVALID_HANDLE_VALUE; + } + tdgbl->action->act_file->fil_fd = INVALID_HANDLE_VALUE; + tdgbl->action->act_file = tdgbl->action->act_file->fil_next; + tdgbl->file_desc = tdgbl->action->act_file->fil_fd; + } + else + { + /* This is a last file. Keep writing in a hope that there is + enough free disk space ... */ + tdgbl->action->act_file->fil_length = MAX_LENGTH; + } + } + } +#ifndef WIN_NT + cnt = write(tdgbl->file_desc, ptr, + ((tdgbl->action->act_action == ACT_backup_split) && + (tdgbl->action->act_file->fil_length < left) ? + tdgbl->action->act_file->fil_length : left)); +#else + const DWORD nBytesToWrite = + (tdgbl->action->act_action == ACT_backup_split && + ((int) tdgbl->action->act_file->fil_length < left) ? + tdgbl->action->act_file->fil_length : left); + if (!WriteFile(tdgbl->file_desc, + ptr, + nBytesToWrite, + reinterpret_cast < DWORD * >(&cnt), NULL)) { + err = GetLastError(); + } +#endif /* !WIN_NT */ + tdgbl->mvol_io_buffer = tdgbl->mvol_io_data; + if (cnt > 0) + { + tdgbl->mvol_cumul_count_kb += cnt / 1024; + file_not_empty(); + if (tdgbl->action->act_action == ACT_backup_split) + { + if ((int) tdgbl->action->act_file->fil_length < left) + tdgbl->action->act_file->fil_length = 0; + else + tdgbl->action->act_file->fil_length -= left; + } + } + else + { + if (!cnt || +#ifndef WIN_NT + errno == ENOSPC || errno == EIO || errno == ENXIO || +#ifndef NETWARE_386 + errno == EFBIG) +#else + FALSE) +#endif /* !NETWARE_386 */ +#else + err == ERROR_DISK_FULL || err == ERROR_HANDLE_DISK_FULL) +#endif /* !WIN_NT */ + { + if (tdgbl->action->act_action == ACT_backup_split) + { + /* Close the current file and switch to the next one. + If there is no more specified files left then + issue an error and give up */ + if (tdgbl->action->act_file->fil_next) + { + CLOSE(tdgbl->file_desc); + for (file = tdgbl->gbl_sw_backup_files; file; file = file->fil_next) + { + if (file->fil_fd == tdgbl->file_desc) + file->fil_fd = INVALID_HANDLE_VALUE; + } + + tdgbl->action->act_file->fil_fd = INVALID_HANDLE_VALUE; + BURP_print(272, + tdgbl->action->act_file->fil_name, + (TEXT *) tdgbl->action->act_file->fil_length, + tdgbl->action->act_file->fil_next->fil_name, + 0, 0); /* msg 272 Warning -- free disk space exhausted for file %s, the rest of the bytes (%d) will be written to file %s */ + tdgbl->action->act_file->fil_next->fil_length += + tdgbl->action->act_file->fil_length; + tdgbl->action->act_file = + tdgbl->action->act_file->fil_next; + tdgbl->file_desc = tdgbl->action->act_file->fil_fd; + } + else + { + BURP_error(270, 0, 0, 0, 0, 0); /* msg 270 free disk space exhausted */ + } + cnt = 0; + continue; + } + /* Note: there is an assumption here, that if header data is being + written, it is really being rewritten, so at least all the + header data will be written */ + + if (left != size_to_write) + { + /* Wrote some, move remainder up in buffer. */ + + /* NOTE: We should NOT use memcpy here. We're moving overlapped + data and memcpy does not guanantee the order the data + is moved in */ + memcpy(tdgbl->mvol_io_data, ptr, left); + } + left += tdgbl->mvol_io_data - tdgbl->mvol_io_header; + if (left >= (int) tdgbl->mvol_io_buffer_size) + full_buffer = TRUE; + else + full_buffer = FALSE; + tdgbl->file_desc = + next_volume(tdgbl->file_desc, MODE_WRITE, full_buffer); + if (full_buffer) + { + left -= tdgbl->mvol_io_buffer_size; + memcpy(tdgbl->mvol_io_data, + tdgbl->mvol_io_header + tdgbl->mvol_io_buffer_size, + left); + tdgbl->mvol_cumul_count_kb += + tdgbl->mvol_io_buffer_size / 1024; + tdgbl->mvol_io_buffer = tdgbl->mvol_io_data; + } + else + tdgbl->mvol_io_buffer = tdgbl->mvol_io_header; + break; + } +#ifndef NETWARE_386 + else if (!SYSCALL_INTERRUPTED(errno)) + { + BURP_error_redirect(0, 221, NULL, NULL); + /* msg 221 Unexpected I/O error while writing to backup file */ + } +#endif + } + } + +#ifdef DEBUG + { + int dbg_cnt; + if (debug_on) + for (dbg_cnt = 0; dbg_cnt < cnt; dbg_cnt++) + ib_printf("%d,\n", *(ptr + dbg_cnt)); + } +#endif + +/* After the first block of first volume is written (using a default block size) + change the block size to one that reflects the user's blocking factor. By + making the first block a standard size we will avoid restore problems. */ + + tdgbl->mvol_io_buffer_size = tdgbl->mvol_actual_buffer_size; + + ptr = tdgbl->mvol_io_buffer + left; + *ptr++ = c; + *io_ptr = ptr; + *io_cnt = tdgbl->mvol_io_buffer_size - 1 - left; + + return c; +} + + +//____________________________________________________________ +// +// Read a chunk of data from the IO buffer. +// Return a pointer to the first position NOT written from. +// +UCHAR *MVOL_write_block(TGBL tdgbl, UCHAR * ptr, ULONG count) +{ +/* To handle tape drives & Multi-volume boundaries, use the normal + * write function, instead of doing a more optimal bulk write. + */ + + while (count) + { + ULONG n; + + /* If buffer full, dump it */ + if (tdgbl->io_cnt <= 0) + { + (void) MVOL_write(*ptr++, &tdgbl->io_cnt, &tdgbl->io_ptr); + + /* One byte was written by MVOL_write */ + count--; + } + + n = MIN(count, (ULONG) tdgbl->io_cnt); + + /* Copy data to the IO buffer */ + + (void) memcpy(tdgbl->io_ptr, ptr, n); + ptr += n; + + /* Skip ahead in current buffer */ + + count -= n; + tdgbl->io_cnt -= n; + tdgbl->io_ptr += n; + + } + return ptr; +} + + +//____________________________________________________________ +// +// We ran into an unsupported attribute. This shouldn't happen, +// but it isn't the end of the world. +// +static void bad_attribute(USHORT attribute, USHORT type) +{ + SSHORT l; + TEXT name[128]; + TGBL tdgbl; + + tdgbl = GET_THREAD_DATA; + + gds__msg_format(0, 12, type, sizeof(name), name, 0, 0, 0, 0, 0); + BURP_print(80, name, (TEXT *) attribute, NULL, NULL, NULL); + /* msg 80 don't recognize %s attribute %ld -- continuing */ + l = GET(); + if (l) + { + do { + GET(); + } while (--l); + } +} + + +//____________________________________________________________ +// +// +static void file_not_empty(void) +{ + TGBL tdgbl = GET_THREAD_DATA; + + tdgbl->mvol_empty_file = FALSE; +} + + +//____________________________________________________________ +// +// Get a numeric value from the input stream. +// +static SLONG get_numeric(void) +{ + SLONG value[2]; + + SSHORT length = get_text((UCHAR*) value, sizeof(value)); + + return gds__vax_integer((UCHAR*) value, length); +} + + +//____________________________________________________________ +// +// Move a text attribute to a string and fill. +// +static int get_text(UCHAR* text, SSHORT length) +{ + unsigned int l, l2; + TGBL tdgbl; + + tdgbl = GET_THREAD_DATA; + + l = GET(); + length -= l; + l2 = l; + + if (length < 0) + { + BURP_error_redirect(0, 46, NULL, NULL); /* msg 46 string truncated */ + } + + if (l) + { + do { + *text++ = GET(); + } while (--l); + } + + *text = 0; + + return l2; +} + + +//____________________________________________________________ +// +// Get specification for the next volume (tape). +// Try to open it. Return file descriptor. +// +static void* next_volume( DESC handle, int mode, USHORT full_buffer) +{ + SCHAR new_file[MAX_FILE_NAME_LENGTH]; + DESC new_desc; + ULONG temp_buffer_size; + USHORT format; + TGBL tdgbl; + + tdgbl = GET_THREAD_DATA; + +/* We must close the old handle before the user inserts + another tape, or something. */ + +#ifdef WIN_NT + if (handle != INVALID_HANDLE_VALUE) +#else + if (handle > -1) +#endif /* WIN_NT */ + { + CLOSE(handle); + } + + if (tdgbl->action->act_action == ACT_restore_join) { + tdgbl->action->act_file->fil_fd = INVALID_HANDLE_VALUE; + if ((tdgbl->action->act_total > tdgbl->action->act_file->fil_seq) && + (tdgbl->action->act_file = tdgbl->action->act_file->fil_next) && + (tdgbl->action->act_file->fil_fd != INVALID_HANDLE_VALUE)) + { + return tdgbl->action->act_file->fil_fd; + } + + BURP_error_redirect(0, 50, NULL, NULL); /* msg 50 unexpected end of file on backup file */ + } + +/* If we got here, we've got a live one... Up the volume number unless + the old file was empty */ + + if (!tdgbl->mvol_empty_file) + (tdgbl->mvol_volume_count)++; + + tdgbl->mvol_empty_file = TRUE; + +/* Loop until we have opened a file successfully */ + + for (new_desc = INVALID_HANDLE_VALUE;;) { + /* We aim to keep our descriptors clean */ + + if (new_desc != INVALID_HANDLE_VALUE) { + CLOSE(new_desc); + new_desc = INVALID_HANDLE_VALUE; + } + + /* Get file name to try */ + + prompt_for_name(new_file, sizeof(new_file)); + +#ifdef WIN_NT + if ((new_desc = MVOL_open(new_file, mode, OPEN_ALWAYS)) + == INVALID_HANDLE_VALUE) +#else + if ((new_desc = open(new_file, mode, OPEN_MASK)) < 0) +#endif /* WIN_NT */ + { + BURP_print(222, new_file, 0, 0, 0, 0); + /* msg 222 \n\nCould not open file name \"%s\"\n */ + continue; + } + + /* If the file is to be writable, probe it, and make sure it is... */ + +#ifdef WIN_NT + if (mode == MODE_WRITE) +#else + if (BITS_ON(mode, O_WRONLY) || BITS_ON(mode, O_RDWR)) +#endif /* WIN_NT */ + { + if (!write_header(new_desc, 0L, full_buffer)) + { + BURP_print(223, new_file, 0, 0, 0, 0); + /* msg223 \n\nCould not write to file \"%s\"\n */ + continue; + } + else + { + BURP_msg_put(261, (TEXT *) (tdgbl->mvol_volume_count), + new_file, 0, 0, 0); + /* Starting with volume #vol_count, new_file */ + BURP_verbose(75, new_file, 0, 0, 0, 0); /* msg 75 creating file %s */ + } + } + else + { + /* File is open for read only. Read the header. */ + + if (!read_header(new_desc, &temp_buffer_size, &format, FALSE)) + { + BURP_print(224, new_file, 0, 0, 0, 0); + continue; + } + else + { + BURP_msg_put(261, (TEXT *) (tdgbl->mvol_volume_count), + new_file, 0, 0, 0); + /* Starting with volume #vol_count, new_file */ + BURP_verbose(100, new_file, 0, 0, 0, 0); /* msg 100 opened file %s */ + } + } + + strcpy(tdgbl->mvol_old_file, new_file); + return reinterpret_cast(new_desc); + } +} + + +//____________________________________________________________ +// +// +static void prompt_for_name(SCHAR* name, int length) +{ + IB_FILE* term_in; + IB_FILE* term_out; + SCHAR* name_ptr; + TEXT msg[128]; + + TGBL tdgbl = GET_THREAD_DATA; + +/* Unless we are operating as a service, stdin can't necessarily be trusted. + Get a location to read from. */ + + if (tdgbl->gbl_sw_service_gbak || + isatty(ib_fileno(ib_stdout)) || + !(term_out = ib_fopen(TERM_OUTPUT, "w"))) + { + term_out = ib_stdout; + } + if (tdgbl->gbl_sw_service_gbak || + isatty(ib_fileno(ib_stdin)) || + !(term_in = ib_fopen(TERM_INPUT, "r"))) + { + term_in = ib_stdin; + } + +/* Loop until we have a file name to try */ + + for (;;) + { + /* If there was an old file name, use that prompt */ + + if (strlen(tdgbl->mvol_old_file) > 0) + { + BURP_msg_get(225, msg, (TEXT*) (tdgbl->mvol_volume_count - 1), + tdgbl->mvol_old_file, 0, 0, 0); + ib_fprintf(term_out, msg); + BURP_msg_get(226, msg, 0, 0, 0, 0, 0); + /* \tPress return to reopen that file, or type a new\n\tname followed by return to open a different file.\n */ + ib_fprintf(term_out, msg); + } + else /* First volume */ + { + BURP_msg_get(227, msg, 0, 0, 0, 0, 0); + /* Type a file name to open and hit return */ + ib_fprintf(term_out, msg); + } + BURP_msg_get(228, msg, 0, 0, 0, 0, 0); /* " Name: " */ + ib_fprintf(term_out, msg); + + if (tdgbl->gbl_sw_service_gbak) + { + ib_putc('\001', term_out); + } + ib_fflush(term_out); + if (ib_fgets(name, length, term_in) == (SCHAR*) NULL) + { + BURP_msg_get(229, msg, 0, 0, 0, 0, 0); + /* \n\nERROR: Backup incomplete\n */ + ib_fprintf(term_out, msg); + exit(FINI_ERROR); + } + + /* If the user typed just a carriage return, they + want the old file. If there isn't one, reprompt */ + + if (name[0] == '\n') + { + if (strlen(tdgbl->mvol_old_file) > 0) + { + strcpy(name, tdgbl->mvol_old_file); + break; + } + else /* reprompt */ + continue; + } + + /* OK, its a file name, strip the carriage return */ + + name_ptr = name; + while (*name_ptr && *name_ptr != '\n') + { + name_ptr++; + } + *name_ptr = 0; + break; + } + + if (term_out != ib_stdout) { + ib_fclose(term_out); + } + if (term_in != ib_stdin) { + ib_fclose(term_in); + } +} + + +//____________________________________________________________ +// +// Write an attribute starting with a null terminated string. +// +static void put_asciz( SCHAR attribute, TEXT * string) +{ + TEXT *p; + SSHORT l; + TGBL tdgbl; + + tdgbl = GET_THREAD_DATA; + + for (p = string, l = 0; *p; p++) + { + l++; + } + + PUT(attribute); + PUT(l); + if (l) + { + do + { + PUT(*string++); + } while (--l); + } +} + + +//____________________________________________________________ +// +// Write a numeric value as an attribute. The number is represented +// low byte first, high byte last, as in VAX. +// +static void put_numeric( SCHAR attribute, int value) +{ + ULONG vax_value; + USHORT i; + UCHAR *p; + TGBL tdgbl; + + tdgbl = GET_THREAD_DATA; + + vax_value = gds__vax_integer((UCHAR *) & value, sizeof(value)); + p = (UCHAR *) & vax_value; + + PUT(attribute); + PUT(sizeof(value)); + + for (i = 0; i < sizeof(value); i++) { + PUT(*p++); + } +} + + +//____________________________________________________________ +// +// Functional description +// +static BOOLEAN read_header(DESC handle, + ULONG* buffer_size, + USHORT* format, + USHORT init_flag) +{ + int attribute, temp; + SSHORT l; + ULONG temp_buffer_size; + TEXT buffer[256], *p, msg[128]; + TGBL tdgbl; + + tdgbl = GET_THREAD_DATA; + +/* Headers are a version number, and a volume number */ + +#ifndef WIN_NT + tdgbl->mvol_io_cnt = + read(handle, tdgbl->mvol_io_buffer, tdgbl->mvol_actual_buffer_size); +#else + ReadFile(handle, tdgbl->mvol_io_buffer, tdgbl->mvol_actual_buffer_size, + reinterpret_cast < DWORD * >(&tdgbl->mvol_io_cnt), NULL); +#endif + tdgbl->mvol_io_ptr = tdgbl->mvol_io_buffer; + + if (GET_ATTRIBUTE(attribute) != rec_burp) + BURP_error_redirect(0, 45, NULL, NULL); /* msg 45 expected backup description record */ + + while (GET_ATTRIBUTE(attribute) != att_end) + { + switch (attribute) + { + case att_backup_blksize: + temp_buffer_size = get_numeric(); + if (init_flag) + *buffer_size = temp_buffer_size; + break; + + case att_backup_compress: + temp = get_numeric(); + if (init_flag) + tdgbl->gbl_sw_compress = temp; + break; + + case att_backup_date: + l = GET(); + if (init_flag) + p = tdgbl->gbl_backup_start_time; + else + p = buffer; + if (l) + do + *p++ = GET(); + while (--l); + *p = 0; + if (!init_flag && strcmp(buffer, tdgbl->gbl_backup_start_time)) + { + BURP_msg_get(230, msg, + tdgbl->gbl_backup_start_time, buffer, 0, 0, 0); + /* Expected backup start time %s, found %s\n */ + ib_printf(msg); + return FALSE; + } + break; + + case att_backup_file: + l = GET(); + if (init_flag) + { + p = tdgbl->mvol_db_name_buffer; + } + else + { + p = buffer; + } + if (l) + { + do { + *p++ = GET(); + } while (--l); + } + *p = 0; + if (!init_flag && strcmp(buffer, tdgbl->gbl_database_file_name)) + { + BURP_msg_get(231, msg, + tdgbl->gbl_database_file_name, buffer, 0, 0, 0); + /* Expected backup database %s, found %s\n */ + ib_printf(msg); + return FALSE; + } + if (init_flag) + { + tdgbl->gbl_database_file_name = tdgbl->mvol_db_name_buffer; + } + break; + + case att_backup_format: + temp = get_numeric(); + if (init_flag) + *format = temp; + break; + + case att_backup_transportable: + temp = get_numeric(); + if (init_flag) + { + tdgbl->gbl_sw_transportable = temp; + } + break; + + case att_backup_volume: + temp = get_numeric(); + if (temp != tdgbl->mvol_volume_count) + { + BURP_msg_get(232, msg, + (TEXT*) (tdgbl->mvol_volume_count), + (TEXT*) temp, 0, 0, 0); + /* Expected volume number %d, found volume %d\n */ + ib_printf(msg); + return FALSE; + } + break; + + default: + /* TMN: Here we should really have the following assert */ + /* assert(attribute <= MAX_USHORT); */ + bad_attribute((USHORT) attribute, 59); /* msg 59 backup */ + } + } + + return TRUE; +} + + +//____________________________________________________________ +// +// +static BOOLEAN write_header(DESC handle, + ULONG backup_buffer_size, + USHORT full_buffer) +{ + ULONG vax_value; + USHORT i; + UCHAR *p, *q; + TGBL tdgbl; +#ifdef WIN_NT + DWORD bytes_written, err; +#else + ULONG bytes_written; +#endif + + tdgbl = GET_THREAD_DATA; + + if (backup_buffer_size) + { + tdgbl->mvol_io_header = tdgbl->mvol_io_buffer; + + PUT(rec_burp); + PUT_NUMERIC(att_backup_format, ATT_BACKUP_FORMAT); + + if (tdgbl->gbl_sw_compress) + PUT_NUMERIC(att_backup_compress, 1); + + if (tdgbl->gbl_sw_transportable) + PUT_NUMERIC(att_backup_transportable, 1); + + PUT_NUMERIC(att_backup_blksize, backup_buffer_size); + + tdgbl->mvol_io_volume = tdgbl->mvol_io_ptr + 2; + PUT_NUMERIC(att_backup_volume, tdgbl->mvol_volume_count); + + PUT_ASCIZ(att_backup_file, tdgbl->gbl_database_file_name); + PUT_ASCIZ(att_backup_date, tdgbl->gbl_backup_start_time); + PUT(att_end); + + tdgbl->mvol_io_data = tdgbl->mvol_io_ptr; + } + else + { + vax_value = + gds__vax_integer((UCHAR*) &(tdgbl->mvol_volume_count), + sizeof(tdgbl->mvol_volume_count)); + p = (UCHAR *) & vax_value; + q = tdgbl->mvol_io_volume; + for (i = 0; i < sizeof(int); i++) + { + *q++ = *p++; + } + } + + if (full_buffer) + { +#ifdef WIN_NT + err = + WriteFile(handle, tdgbl->mvol_io_header, + tdgbl->mvol_io_buffer_size, &bytes_written, NULL); +#else + bytes_written = write(handle, tdgbl->mvol_io_header, + tdgbl->mvol_io_buffer_size); +#endif /* WIN_NT */ + + if (bytes_written != tdgbl->mvol_io_buffer_size) + { + return FALSE; + } + + if (tdgbl->action->act_action == ACT_backup_split) + { + if (tdgbl->action->act_file->fil_length > bytes_written) + { + tdgbl->action->act_file->fil_length -= bytes_written; + } + else + { + tdgbl->action->act_file->fil_length = 0; + } + } + tdgbl->mvol_empty_file = FALSE; + } + + return TRUE; +} + + +//____________________________________________________________ +// +// Write a header record for split operation +// +BOOLEAN MVOL_split_hdr_write(void) +{ + TGBL tdgbl; + TEXT buffer[HDR_SPLIT_SIZE + 1]; + time_t seconds; +#ifdef WIN_NT + DWORD bytes_written; +#else + ULONG bytes_written; +#endif + + + tdgbl = GET_THREAD_DATA; + + assert(tdgbl->action->act_action == ACT_backup_split); + assert(tdgbl->action->act_file->fil_fd != INVALID_HANDLE_VALUE); + + if (tdgbl->action->act_file->fil_length < HDR_SPLIT_SIZE) { + return FALSE; + } + + seconds = time((time_t *) NULL); + + sprintf(buffer, "%s%.24s , file No. %4d of %4d, %-27.27s", + HDR_SPLIT_TAG, + ctime(&seconds), + tdgbl->action->act_file->fil_seq, + tdgbl->action->act_total, + tdgbl->action->act_file->fil_name); + +#ifdef WIN_NT + DWORD err = WriteFile(tdgbl->action->act_file->fil_fd, + buffer, + HDR_SPLIT_SIZE, + &bytes_written, + NULL); +#else + bytes_written = + write(tdgbl->action->act_file->fil_fd, buffer, HDR_SPLIT_SIZE); +#endif /* WIN_NT */ + + if (bytes_written != HDR_SPLIT_SIZE) { + return FALSE; + } + + tdgbl->action->act_file->fil_length -= bytes_written; + return TRUE; +} + + +//____________________________________________________________ +// +// Read a header record for join operation +// +BOOLEAN MVOL_split_hdr_read(void) +{ + TGBL tdgbl = GET_THREAD_DATA; + + assert(tdgbl->action->act_file->fil_fd != INVALID_HANDLE_VALUE); + + if (tdgbl->action && tdgbl->action->act_file && + (tdgbl->action->act_file->fil_fd != INVALID_HANDLE_VALUE)) + { + TEXT buffer[HDR_SPLIT_SIZE]; + int cnt; + +#ifdef WIN_NT + ReadFile(tdgbl->action->act_file->fil_fd, + buffer, + HDR_SPLIT_SIZE, + reinterpret_cast(&cnt), + NULL); +#else + cnt = read(tdgbl->action->act_file->fil_fd, buffer, HDR_SPLIT_SIZE); +#endif + if ((cnt == HDR_SPLIT_SIZE) && + ((strncmp(buffer, HDR_SPLIT_TAG, (sizeof(HDR_SPLIT_TAG) - 1)) == 0) || + (strncmp(buffer, HDR_SPLIT_TAG5, (sizeof(HDR_SPLIT_TAG) - 1)) == 0))) + { + HDR_SPLIT hdr = (HDR_SPLIT) buffer; + if ((tdgbl->action->act_file->fil_seq = atoi(hdr->hdr_split_sequence)) > 0 && + (tdgbl->action->act_total = atoi(hdr->hdr_split_total)) > 0 && + (tdgbl->action->act_file->fil_seq <= tdgbl->action->act_total)) + { + return TRUE; + } + } + } + + return FALSE; +} + + +} // extern "C" diff --git a/src/burp/mvol_proto.h b/src/burp/mvol_proto.h new file mode 100644 index 0000000000..94a451c06a --- /dev/null +++ b/src/burp/mvol_proto.h @@ -0,0 +1,54 @@ +/* + * PROGRAM: JRD Backup and Restore Program + * MODULE: mvol_proto.h + * DESCRIPTION: Prototype Header file for mvol.c + * + * 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): ______________________________________. + */ + +#ifndef _BURP_MVOL_PROTO_H_ +#define _BURP_MVOL_PROTO_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +extern void MVOL_fini_read (int *); +extern void MVOL_fini_write (int *, UCHAR * *, int *); +extern void MVOL_init (ULONG); +extern void MVOL_init_read (UCHAR *, UCHAR *, USHORT *, int *, UCHAR * *); +extern void MVOL_init_write (UCHAR *, UCHAR *, int *, UCHAR * *); +extern BOOLEAN MVOL_split_hdr_write (void); +extern BOOLEAN MVOL_split_hdr_read (void); +extern int MVOL_read (int *, UCHAR * *); +extern UCHAR * MVOL_read_block (TGBL, UCHAR *, ULONG); +extern void MVOL_skip_block (TGBL, ULONG); +extern UCHAR MVOL_write (UCHAR, int *, UCHAR * *); +extern UCHAR * MVOL_write_block (TGBL, UCHAR *, ULONG); +#if defined WIN_NT && defined _WINNT_ +/* the _WINNT_ symbol is defined if winnt.h has been included */ +/* it contains the definition of a HANDLE */ +/* those files that don't include winnt.h don't need MVOL_open either */ +extern HANDLE MVOL_open (TEXT*,DWORD,DWORD); +#endif + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* _BURP_MVOL_PROTO_H_ */ diff --git a/src/burp/resto_proto.h b/src/burp/resto_proto.h new file mode 100644 index 0000000000..e3ca73d756 --- /dev/null +++ b/src/burp/resto_proto.h @@ -0,0 +1,37 @@ +/* + * PROGRAM: JRD Backup and Restore Program + * MODULE: resto_proto.h + * DESCRIPTION: Prototype Header file for restore.e + * + * 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): ______________________________________. + */ + +#ifndef _BURP_RESTO_PROTO_H_ +#define _BURP_RESTO_PROTO_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +extern int RESTORE_restore (TEXT *, TEXT *); + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* _BURP_RESTO_PROTO_H_ */ diff --git a/src/burp/restore.e b/src/burp/restore.e new file mode 100644 index 0000000000..66fb2c357c --- /dev/null +++ b/src/burp/restore.e @@ -0,0 +1,7164 @@ +/* + * PROGRAM: JRD Backup and Restore Program + * MODULE: restore.e + * DESCRIPTION: Restore routine + * + * 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): ______________________________________. + * Toni Martir: Verbose records restored as RESTORE_VERBOSE_INTERVAL, + * also verbose restoring indexes as DEFERRED when verbose + */ +/* +$Id: restore.e,v 1.1.1.1 2001-05-23 13:26:04 tamlin Exp $ +*/ + +#include "../jrd/ib_stdio.h" +#include +#include +#include "../burp/burp.h" +#include "../jrd/align.h" +#include "../jrd/common.h" +#include "../jrd/flags.h" +#include "../jrd/license.h" +#include "../jrd/obj.h" +#include "../jrd/ods.h" +#include "../burp/burp_proto.h" +#include "../burp/canon_proto.h" +#include "../burp/misc_proto.h" +#include "../burp/mvol_proto.h" +#include "../burp/resto_proto.h" +#include "../intl/charsets.h" +#include "../jrd/gdsassert.h" +#include "../remote/protocol.h" + +/* For netware the follow DB handle is #defined to be a value stored */ +/* in thread data. This is also done for other statics generated by */ +/* GPRE. This is to avoid multiple threading problems with module */ +/* level statics. */ +DATABASE DB = STATIC FILENAME "yachts.lnk"; + +#define DB tdgbl->db_handle +#define gds__trans tdgbl->tr_handle +#define isc_status tdgbl->status + +/*** +#define DEBUG 1 +***/ + +#define STUFF(byte) {*blr++ = (UCHAR) (byte);} +#define STUFF_WORD(word) {STUFF (word); STUFF ((word) >> 8);} +#define STUFF_LONG(lword) {STUFF_WORD (lword); STUFF_WORD ((lword) >> 16);} +#define STUFF_INT64(i64) {STUFF_LONG (i64); STUFF_LONG ((i64) >> 32);} + +#define DB_VERSION_DDL4 4 /* ods4 db */ +#define DB_VERSION_DDL5 5 /* ods5 db */ +#define DB_VERSION_DDL8 8 /* ods8 db */ +#define DB_VERSION_CURRENT DB_VERSION_DDL8 /* v4.0 is ods8 */ +#define FOREIGN_KEY "FOREIGN KEY" + +#define DEFERRED_ACTIVE 3 /* RDB$INDEX_INACTIVE setting for Foreign Keys + * This setting is used temporarily while + * restoring a database. This was required + * in order to differentiate a partial + * "inactive" state of SOME indices from + * "inactive" state of ALL indices (gbak -i) + * -bsriram, 11-May-1999 BUG: 10016 + */ + +#define RESTORE_VERBOSE_INTERVAL 10000 +#define cvtbl_len 28 +static CONST struct s_t_cvtbl { + SSHORT sub_type; + SSHORT character_set_id; + SSHORT collation_id; +} sub_type_cvtbl[] = { + /* NOTE: The magic numbers for collation_id come from ordinal + * position of the COLLATION() entries in jrd/intlnames.h + */ + 101, CS_DOS_437, 1, /* PDOX_ASCII */ + 102, CS_DOS_437, 2, /* PDOX_INTL */ + 106, CS_DOS_437, 3, /* PDOX_SWEDFIN */ + 160, CS_DOS_850, 0, /* codepoint collation */ + 107, CS_DOS_865, 0, /* codepoint collation */ + 105, CS_DOS_865, 1, /* PDOX_NORDAN4 */ + 139, CS_LATIN1, 1, /* DA_DA */ + 140, CS_LATIN1, 2, /* DU_NL */ + 141, CS_LATIN1, 3, /* FI_FI */ + 142, CS_LATIN1, 4, /* FR_FR */ + 143, CS_LATIN1, 5, /* FR_CA */ + 144, CS_LATIN1, 6, /* DE_DE */ + 145, CS_LATIN1, 7, /* IS_IS */ + 146, CS_LATIN1, 8, /* IT_IT */ + 148, CS_LATIN1, 9, /* NO_NO */ + 149, CS_LATIN1, 10, /* ES_ES */ + 151, CS_LATIN1, 11, /* SV_SV */ + 152, CS_LATIN1, 12, /* EN_UK */ + 153, CS_LATIN1, 14, /* EN_US */ + 154, CS_LATIN1, 15, /* PT_PT */ + 180, CS_NEXT, 1, /* NXT_US */ + 181, CS_NEXT, 2, /* NXT_GERMANY */ + 182, CS_NEXT, 3, /* NXT_FRANCE */ + 183, CS_NEXT, 4, /* NXT_ITALY */ + 184, CS_NEXT, 5, /* NXT_SPAIN */ + 201, CS_UNICODE_FSS, 0, /* codepoint collation */ + 220, CS_SJIS, 0, /* codepoint collation */ + 230, CS_EUCJ, 0 /* codepoint collation */ +}; + +static void add_files(UCHAR *); +static void bad_attribute(UCHAR, ATT_TYPE, USHORT); +static USHORT check_db_version(void); +static void create_database(UCHAR *); +static void decompress(UCHAR *, USHORT); +static void eat_blob(void); +static REL find_relation(TEXT *); +static int get_acl(TEXT *, ISC_QUAD *, ISC_QUAD *); +static void get_array(REL, UCHAR *); +static void get_blob(FLD, UCHAR *); +static void get_blr_blob(ISC_QUAD *, USHORT); +static BOOLEAN get_character_set(void); +static BOOLEAN get_chk_constraint(void); +static BOOLEAN get_collation(void); +static REC_TYPE get_data(REL); +static BOOLEAN get_exception(void); +static FLD get_field(REL); +static BOOLEAN get_field_dimensions(void); +static BOOLEAN get_files(void); +static BOOLEAN get_filter(void); +static BOOLEAN get_function(void); +static void get_function_arg(GDS_NAME); +static BOOLEAN get_generator(void); +static BOOLEAN get_global_field(void); +static BOOLEAN get_index(REL); +static void get_misc_blob(ISC_QUAD *, USHORT, USHORT); +static SLONG get_numeric(void); +static SINT64 get_int64(void); +static BOOLEAN get_procedure(void); +static BOOLEAN get_procedure_prm(GDS_NAME); +static BOOLEAN get_ref_constraint(void); +static BOOLEAN get_rel_constraint(void); +static BOOLEAN get_relation(void); +static BOOLEAN get_relation_data(void); +static BOOLEAN get_sql_roles(void); +static BOOLEAN get_security_class(void); +static void get_source_blob(ISC_QUAD *, USHORT); +static USHORT get_text(TEXT *, ULONG); +static BOOLEAN get_trigger(void); +static BOOLEAN get_trigger_message(void); +static BOOLEAN get_trigger_old(REL); +static BOOLEAN get_type(void); +static BOOLEAN get_user_privilege(void); +static BOOLEAN get_view(REL); +static void ignore_array(REL); +static void ignore_blob(void); +static REC_TYPE ignore_data(REL); +static void realign(UCHAR *, REL); +static USHORT recompute_length(REL); +static BOOLEAN restore(TEXT *, TEXT *); +static void restore_security_class(TEXT *, TEXT *); +static void store_blr_gen_id(GDS_NAME, SINT64); +static void stuff_string(SCHAR **, TEXT *); +static void update_global_field(void); +static void general_on_error(void); +static BOOLEAN bug_8183(TGBL); + +static UCHAR debug_on = 0; /* able to turn this on in the debugger */ + +static USHORT flag_on_line = TRUE; /* indicates whether we will bring + the database on-line : + TRUE - we will + FALSE - we will not */ +#ifdef sparc +static CONST SSHORT old_sparcs[] = + { 0, 0, 0, 2, 0, 0, 0, 0, 2, 4, 4, 4, 8, 8, 0, 0, 8, 8, 8 }; +#endif + +#define GET() (--(tdgbl->io_cnt) >= 0 ? *(tdgbl->io_ptr)++ : MVOL_read (&tdgbl->io_cnt, &tdgbl->io_ptr)) +#define GET_SKIP(n) MVOL_skip_block (tdgbl, n) +#define GET_BLOCK(p,n) MVOL_read_block (tdgbl, (p), (n)) +#define GET_ATTRIBUTE(att) ((att) = (ATT_TYPE) GET()) +#define GET_RECORD(att) ((att) = (REC_TYPE) GET()) + +/* When skipping started, scan_next_attr will be changed from NO_SKIP */ +/* to BEFORE_SKIP. When scanning for next valid attribute after skipping, */ +/* it will flip-flop between BEFORE_SKIP and AFTER_SKIP. When next valid */ +/* attribute is found, it will be changed back to NO_SKIP by 'SKIP_SCAN' */ + +#define NO_SKIP 0 /* Not in skipping and scanning mode */ +#define BEFORE_SKIP 1 /* After skipping, before scanning next byte for valid attribute */ +#define AFTER_SKIP 2 /* After skipping and after scanning next byte for valid attribute */ + +#define SKIP_INIT (scan_next_attr = NO_SKIP) +#define SKIP_SCAN (scan_next_attr == AFTER_SKIP ? \ + scan_next_attr = BEFORE_SKIP : \ + scan_next_attr == BEFORE_SKIP ? \ + scan_next_attr = NO_SKIP : 0) +/* User Privilege Flags */ + +#define USER_PRIV_USER 1 +#define USER_PRIV_GRANTOR 2 +#define USER_PRIV_PRIVILEGE 4 +#define USER_PRIV_GRANT_OPTION 8 +#define USER_PRIV_OBJECT_NAME 16 +#define USER_PRIV_FIELD_NAME 32 +#define USER_PRIV_USER_TYPE 64 +#define USER_PRIV_OBJECT_TYPE 128 + + + +int RESTORE_restore( TEXT * file_name, TEXT * database_name) +{ +/************************************** + * + * R E S T O R E _ r e s t o r e + * + ************************************** + * + * Functional description + * Recreate a database from a backup. + * + **************************************/ + int cumul_count_kb; + REL relation; + PRC procedure; + isc_req_handle req_handle1 = NULL, req_handle2 = NULL, req_handle4 = NULL; + long req_status[20]; + TGBL tdgbl; + long db_handle; + UCHAR dpb[128], *d, *q; + SSHORT l; + isc_req_handle req_handle3 = NULL; + TEXT index_name[32]; + long error_code; + + tdgbl = GET_THREAD_DATA; + + tdgbl->io_ptr = (UCHAR *) NULL; + tdgbl->io_cnt = 0; + + tdgbl->relations = (REL) NULL; + tdgbl->procedures = (PRC) 0; + tdgbl->RESTORE_format = 0; + tdgbl->global_trans = 0; + + tdgbl->gbl_sw_transportable = tdgbl->gbl_sw_compress = FALSE; + + if (!restore(file_name, database_name)) + return FINI_ERROR; + + BURP_verbose(76, NULL_PTR, NULL_PTR, NULL_PTR, NULL_PTR, NULL_PTR); +/* msg 76 creating indexes */ + + COMMIT; + ON_ERROR + /* Fix for bug_no 8055: + don't throw away the database just because an index + could not be made */ + while (error_code = tdgbl->status_vector[1]) + { + switch (error_code) + { + case gds__sort_mem_err: + case gds__no_dup: + strcpy(index_name, (TEXT *)tdgbl->status_vector[3]); + BURP_print_status(tdgbl->status_vector); + FOR (REQUEST_HANDLE req_handle3) + IDX IN RDB$INDICES WITH IDX.RDB$INDEX_NAME EQ index_name + { + BURP_verbose(243,index_name,NULL_PTR,NULL_PTR,NULL_PTR,NULL_PTR); + MODIFY IDX USING IDX.RDB$INDEX_INACTIVE = TRUE; + } + BURP_print(240, index_name, NULL_PTR, NULL_PTR, NULL_PTR, NULL_PTR); + /* msg 240 Index \"%s\" failed to activate because: */ + if ( error_code == gds__no_dup ) + { + BURP_print(241, NULL_PTR, NULL_PTR, NULL_PTR, NULL_PTR, NULL_PTR); + /* msg 241 The unique index has duplicate values or NULLs */ + BURP_print(242, NULL_PTR, NULL_PTR, NULL_PTR, NULL_PTR, NULL_PTR); + /* msg 242 Delete or Update duplicate values or NULLs, and activate index with */ + } + else + { + BURP_print(244, NULL_PTR, NULL_PTR, NULL_PTR, NULL_PTR, NULL_PTR); + /* msg 244 Not enough disk space to create the sort file for an index */ + BURP_print(245, NULL_PTR, NULL_PTR, NULL_PTR, NULL_PTR, NULL_PTR); + /* msg 245 Set the TMP environment variable to a directory on a filesystem that does have enough space, and activate index with */ + } + BURP_print(243, index_name, NULL_PTR, NULL_PTR, NULL_PTR, NULL_PTR); + /* msg 243 ALTER INDEX \"%s\" ACTIVE; */ + END_MODIFY; + END_FOR; + /* don't bring the database on-line */ + flag_on_line = FALSE; + break; + default: + general_on_error (); + break; + } + COMMIT + ON_ERROR + continue; + END_ERROR + } + END_ERROR; +/* Activate the indices for foreign keys and do another commit */ + if (!(tdgbl->gbl_sw_deactivate_indexes)) { + + /* Block added to verbose index creation by Toni Martir */ + if (tdgbl->gbl_sw_verbose) { + EXEC SQL SET TRANSACTION ISOLATION LEVEL READ COMMITTED + NO_AUTO_UNDO; + if (gds__status[1]) + EXEC SQL SET TRANSACTION; + + /* Activate first indexes that are not foreign keys */ + FOR (REQUEST_HANDLE req_handle1) IDS IN RDB$INDICES WITH + IDS.RDB$INDEX_INACTIVE EQ DEFERRED_ACTIVE AND + IDS.RDB$FOREIGN_KEY MISSING + MODIFY IDS USING IDS.RDB$INDEX_INACTIVE=FALSE; + END_MODIFY; + ON_ERROR + general_on_error(); + END_ERROR; + + SAVE + /* existing ON_ERROR continues past error, beck */ + ON_ERROR + BURP_print (173, IDS.RDB$INDEX_NAME, NULL_PTR, NULL_PTR, NULL_PTR, NULL_PTR); + BURP_print_status (tdgbl->status); + MODIFY IDS USING + IDS.RDB$INDEX_INACTIVE = TRUE; + END_MODIFY; + ON_ERROR + general_on_error (); + END_ERROR; + END_ERROR; + BURP_verbose(122,IDS.RDB$INDEX_NAME,NULL_PTR,NULL_PTR,NULL_PTR,NULL_PTR); + END_FOR; + ON_ERROR + general_on_error (); + END_ERROR; + if (req_handle1) + isc_release_request(req_status, &req_handle1); + COMMIT; + ON_ERROR + general_on_error (); + END_ERROR; + } + + + EXEC SQL SET TRANSACTION ISOLATION LEVEL READ COMMITTED NO_AUTO_UNDO; + if (gds__status[1]) + EXEC SQL SET TRANSACTION; + + /* Only activate Foreign keys that have been marked for deferred + * activation. + * -bsriram, 11-May-1999 BUG: 10016 + */ + FOR (REQUEST_HANDLE req_handle1) + CNST IN RDB$RELATION_CONSTRAINTS + CROSS IDS IN RDB$INDICES WITH + CNST.RDB$CONSTRAINT_TYPE EQ FOREIGN_KEY AND + CNST.RDB$INDEX_NAME EQ IDS.RDB$INDEX_NAME AND + IDS.RDB$INDEX_INACTIVE EQ DEFERRED_ACTIVE + + + MODIFY IDS USING + IDS.RDB$INDEX_INACTIVE = FALSE; + END_MODIFY; + ON_ERROR + general_on_error (); + END_ERROR; + + SAVE + /* existing ON_ERROR continues past error, beck */ + ON_ERROR + BURP_print (173, IDS.RDB$INDEX_NAME, NULL_PTR, NULL_PTR, NULL_PTR, NULL_PTR); + BURP_print_status (tdgbl->status); + MODIFY IDS USING + IDS.RDB$INDEX_INACTIVE = TRUE; + END_MODIFY; + ON_ERROR + general_on_error (); + END_ERROR; + END_ERROR; + BURP_verbose(122,IDS.RDB$INDEX_NAME,NULL_PTR,NULL_PTR,NULL_PTR,NULL_PTR); + END_FOR; + ON_ERROR + general_on_error (); + END_ERROR; + if (req_handle1) + isc_release_request(req_status, &req_handle1); + COMMIT; + ON_ERROR + general_on_error (); + END_ERROR; + } + + if (tdgbl->global_trans) + { + EXEC SQL COMMIT TRANSACTION tdgbl->global_trans; + if (gds__status[1]) + general_on_error(); + /* Check to see if there is a warning */ + if (gds__status[0] == gds_arg_gds && + gds__status[1] == 0 && + gds__status[2] != gds_arg_end) + { + BURP_print_warning(gds__status); + } + } + + EXEC SQL SET TRANSACTION ISOLATION LEVEL READ COMMITTED NO_AUTO_UNDO; + if (gds__status[1]) + EXEC SQL SET TRANSACTION; +/* +** Change ownership of any procedures necessary +*/ + + for (procedure = tdgbl->procedures; procedure; + procedure = procedure->prc_next) + if (procedure->prc_owner[0]) + FOR (REQUEST_HANDLE req_handle4) + X IN RDB$PROCEDURES WITH X.RDB$PROCEDURE_NAME EQ procedure->prc_name + + MODIFY X + strcpy (X.RDB$OWNER_NAME, procedure->prc_owner); + END_MODIFY; + ON_ERROR + if (req_handle4) + isc_release_request (req_status, &req_handle4); + general_on_error (); + END_ERROR; + + restore_security_class (procedure->prc_owner, X.RDB$SECURITY_CLASS); + + END_FOR; + ON_ERROR + if (req_handle4) + { + isc_release_request(req_status, &req_handle4); + } + general_on_error (); + END_ERROR; + + if (req_handle4) + isc_release_request(req_status, &req_handle4); + +/* Change ownership of any relations necessary */ + + for (relation = tdgbl->relations; relation; relation = relation->rel_next) + if (relation->rel_owner[0]) + FOR (REQUEST_HANDLE req_handle2) + X IN RDB$RELATIONS WITH X.RDB$RELATION_NAME EQ relation->rel_name + MODIFY X + strcpy (X.RDB$OWNER_NAME, relation->rel_owner); + END_MODIFY; + ON_ERROR + if (req_handle2) + isc_release_request (req_status, &req_handle2); + general_on_error (); + END_ERROR; + + restore_security_class (relation->rel_owner, X.RDB$SECURITY_CLASS); + restore_security_class (relation->rel_owner, X.RDB$DEFAULT_CLASS); + + END_FOR; + ON_ERROR + if (req_handle2) + isc_release_request (req_status, &req_handle2); + general_on_error (); + END_ERROR; + if (req_handle2) + isc_release_request(req_status, &req_handle2); + +/* Now that changing ownership of tables is over, it is safe to + update the database security class in RDB$DATABASE */ + + if (tdgbl->database_security_class[0]) { /* Do it only if it's not NULL */ + FOR (REQUEST_HANDLE req_handle1) + X IN RDB$DATABASE + MODIFY X USING + strncpy(X.RDB$SECURITY_CLASS, tdgbl->database_security_class, + sizeof(X.RDB$SECURITY_CLASS)); + END_MODIFY; + ON_ERROR + if (req_handle1) + isc_release_request (req_status, &req_handle1); + general_on_error (); + END_ERROR; + END_FOR; + ON_ERROR + if (req_handle1) + isc_release_request (req_status, &req_handle1); + general_on_error (); + END_ERROR; + + if (req_handle1) + isc_release_request(req_status, &req_handle1); + } + + COMMIT; + ON_ERROR + general_on_error (); + END_ERROR; + +/* Check to see if there is a warning */ + if (gds__status[0] == gds_arg_gds && gds__status[1] == 0 + && gds__status[2] != gds_arg_end) + BURP_print_warning(gds__status); + + BURP_verbose(88, NULL_PTR, NULL_PTR, NULL_PTR, NULL_PTR, NULL_PTR); +/* msg 88 finishing, closing, and going home */ + + MVOL_fini_read(&cumul_count_kb); + +/* attach database again to put it online */ + + d = dpb; + *d++ = (UCHAR) gds__dpb_version1; + + if (flag_on_line) { + *d++ = (UCHAR) gds__dpb_online; + *d++ = 0; + } + + if (tdgbl->gbl_sw_user) { + *d++ = (UCHAR) gds__dpb_user_name; + *d++ = (UCHAR) strlen(tdgbl->gbl_sw_user); + for (q = tdgbl->gbl_sw_user; *q;) + *d++ = *q++; + } + + if (tdgbl->gbl_sw_password) { + if (!tdgbl->gbl_sw_service_thd) + *d++ = (UCHAR) gds__dpb_password; + else + *d++ = (UCHAR) gds__dpb_password_enc; + *d++ = (UCHAR) strlen(tdgbl->gbl_sw_password); + for (q = tdgbl->gbl_sw_password; *q;) + *d++ = *q++; + } + +/* set sync writes to engine default */ + *d++ = (UCHAR) gds__dpb_force_write; + *d++ = 1; + *d++ = (UCHAR) tdgbl->hdr_forced_writes; /* set forced writes to the value which was in the header */ + + l = d - dpb; + db_handle = 0; + if (isc_attach_database(tdgbl->status_vector, 0, GDS_VAL(database_name), + (isc_db_handle *) GDS_REF(db_handle), l, dpb)) + general_on_error(); + if (isc_detach_database + (tdgbl->status_vector, + (isc_db_handle *) GDS_REF(db_handle))) general_on_error(); + + FINISH; + ON_ERROR + general_on_error (); + END_ERROR; + + if (!flag_on_line) { + BURP_print(246, NULL_PTR, NULL_PTR, NULL_PTR, NULL_PTR, NULL_PTR); + /* msg 246 Database is not online due to failure to activate one or more indices. */ + BURP_print(247, NULL_PTR, NULL_PTR, NULL_PTR, NULL_PTR, NULL_PTR); + /* msg 247 Run gfix -online to bring database online without active indices. */ + return FINI_DB_NOT_ONLINE; + } + +#ifdef READONLY_DATABASE +/* If the database is to be restored ReadOnly, set it to read_only now! */ + if (tdgbl->gbl_sw_mode == TRUE && tdgbl->gbl_sw_mode_val == TRUE) { + BURP_verbose(280, NULL_PTR, NULL_PTR, NULL_PTR, NULL_PTR, NULL_PTR); + /* msg 280: setting database to read-only access */ + + d = dpb; + *d++ = (UCHAR) gds__dpb_version1; + if (tdgbl->gbl_sw_user) { + *d++ = (UCHAR) gds__dpb_user_name; + *d++ = (UCHAR) strlen(tdgbl->gbl_sw_user); + for (q = tdgbl->gbl_sw_user; *q;) + *d++ = *q++; + } + + if (tdgbl->gbl_sw_password) { + if (!tdgbl->gbl_sw_service_thd) + *d++ = (UCHAR) gds__dpb_password; + else + *d++ = (UCHAR) gds__dpb_password_enc; + *d++ = (UCHAR) strlen(tdgbl->gbl_sw_password); + for (q = tdgbl->gbl_sw_password; *q;) + *d++ = *q++; + } + + *d++ = (UCHAR) isc_dpb_set_db_readonly; + *d++ = 1; + *d++ = TRUE; /* set database to readOnly mode */ + l = d - dpb; + db_handle = 0; + if (isc_attach_database + (tdgbl->status_vector, 0, GDS_VAL(database_name), + (isc_db_handle *) GDS_REF(db_handle), l, dpb)) + general_on_error(); + if (isc_detach_database + (tdgbl->status_vector, (isc_db_handle *) GDS_REF(db_handle))) + general_on_error(); + + } +#endif /* READONLY_DATABASE */ + + return FINI_OK; +} + + +static void add_files( UCHAR * file_name) +{ +/************************************** + * + * a d d _ f i l e s + * + ************************************** + * + * Functional description + * This should be a multi-file database. + * Store files and starting + * addresses & commit this much. + * + **************************************/ + SLONG start, count; + FIL file; + isc_req_handle req_handle1 = NULL; + long req_status[20]; + TGBL tdgbl; + + tdgbl = GET_THREAD_DATA; + +/* store the RDB$FILES records */ + + start = 201; + count = 0; + + for (file = tdgbl->gbl_sw_files; file; file = file->fil_next) { + if (strcmp(file->fil_name, file_name)) { + count++; + STORE (REQUEST_HANDLE req_handle1) + X IN RDB$FILES + strcpy (X.RDB$FILE_NAME, file->fil_name); + X.RDB$FILE_START = start; + END_STORE; + ON_ERROR + general_on_error (); + END_ERROR; + if (req_handle1) + isc_release_request(req_status, &req_handle1); + BURP_verbose(57, file->fil_name, (void *) start, NULL_PTR, + NULL_PTR, NULL_PTR); + /* msg 57 adding file %s, starting at page %ld */ + } + else if (((signed long) file->fil_length) >= start - 1) + ((signed long) file->fil_length) -= start - 1; + else { + BURP_print(96, (void *) file->fil_length, (void *) (start - 1), + NULL_PTR, NULL_PTR, NULL_PTR); + /* msg 96 length given for initial file (%ld) is less than minimum (%ld) */ + file->fil_length = 0; + } + + start += file->fil_length; + } + + if (count) { + BURP_verbose(70, NULL_PTR, NULL_PTR, NULL_PTR, NULL_PTR, NULL_PTR); + /* msg 70 committing secondary files */ + COMMIT + /* existing ON_ERROR continues past error, beck */ + ON_ERROR + BURP_print (174, NULL_PTR, NULL_PTR, NULL_PTR, NULL_PTR, NULL_PTR); + /* msg 174 cannot commit files */ + BURP_print_status (tdgbl->status_vector); + ROLLBACK; + ON_ERROR + general_on_error (); + END_ERROR; + END_ERROR; + EXEC SQL SET TRANSACTION NO_AUTO_UNDO; + if (gds__status[1]) + EXEC SQL SET TRANSACTION; + } +} + + +static void bad_attribute( + UCHAR scan_next_attr, + ATT_TYPE bad_attr, USHORT type) +{ +/************************************** + * + * b a d _ a t t r i b u t e + * + ************************************** + * + * Functional description + * We ran into an unsupported attribute. + * but it isn't the end of the world. + * We will try to skip some bad data and + * look for next valid attribute to continue the process. + * + **************************************/ + SSHORT skip_l; + TEXT t_name[128]; + SLONG skip_count; + TGBL tdgbl; + + tdgbl = GET_THREAD_DATA; + + skip_count = 0; + + if (!tdgbl->gbl_sw_skip_count) { + gds__msg_format(NULL_PTR, 12, type, sizeof(t_name), t_name, NULL_PTR, + NULL_PTR, NULL_PTR, NULL_PTR, NULL_PTR); + BURP_print(80, t_name, (void *) bad_attr, NULL_PTR, NULL_PTR, + NULL_PTR); + /* msg 80 don't recognize %s attribute %ld -- continuing */ + skip_l = GET(); + if (skip_l) + GET_SKIP(skip_l); + } + else { + if (scan_next_attr == NO_SKIP) { + skip_count = tdgbl->gbl_sw_skip_count; + GET_SKIP(skip_count); + BURP_print(203, (void *) skip_count, (void *) bad_attr, NULL_PTR, + NULL_PTR, NULL_PTR); + /*msg 203: skipped %d bytes after reading a bad attribute %d */ + } + else { + skip_count++; + BURP_print(205, (void *) skip_count, (void *) bad_attr, NULL_PTR, + NULL_PTR, NULL_PTR); + /* msg 205: skipped %d bytes looking for next valid attribute, encountered attribute %d */ + } + scan_next_attr = AFTER_SKIP; + } +} + + +static USHORT check_db_version(void) +{ +/************************************** + * + * c h e c k _ d b _ v e r s i o n + * + ************************************** + * + * Functional description + * Find the version number of the database. + * + **************************************/ + USHORT db_version; + isc_req_handle req_handle1 = NULL, req_handle2 = NULL; + long req_status[20]; + TGBL tdgbl; + + tdgbl = GET_THREAD_DATA; + + db_version = DB_VERSION_DDL4; + FOR (REQUEST_HANDLE req_handle1) + FIRST 1 X IN RDB$RELATIONS + WITH X.RDB$RELATION_NAME = "RDB$TRIGGERS" + db_version = DB_VERSION_DDL5; + END_FOR; + ON_ERROR + general_on_error (); + END_ERROR; + if (req_handle1) + isc_release_request(req_status, &req_handle1); + FOR (REQUEST_HANDLE req_handle2) + FIRST 1 X IN RDB$RELATIONS + WITH X.RDB$RELATION_NAME = "RDB$PROCEDURES" + db_version = DB_VERSION_DDL8; + END_FOR; + ON_ERROR + general_on_error (); + END_ERROR; + if (req_handle2) + isc_release_request(req_status, &req_handle2); + + return db_version; +} + + +static void create_database( UCHAR * file_name) +{ +/************************************** + * + * c r e a t e _ d a t a b a s e + * + ************************************** + * + * Functional description + * create the new database, looking + * to see if there are any interesting + * things to do. + * + **************************************/ + SSHORT l; + STATUS status_vector[ISC_STATUS_LENGTH]; + REC_TYPE record; + ATT_TYPE attribute; + UCHAR dpb[128], *d, *q; + ULONG page_size, sweep_interval, page_buffers; + USHORT no_reserve, forced_writes; + TGBL tdgbl; + USHORT SQL_dialect; + BOOLEAN db_read_only, SQL_dialect_flag = FALSE; + + tdgbl = GET_THREAD_DATA; + +/* Get (physical) database record */ + + page_size = DEFAULT_PAGE_SIZE; + sweep_interval = -1; + no_reserve = FALSE; + db_read_only = FALSE; + forced_writes = 2; /* default for the current platform */ + page_buffers = 0; + + if (GET_RECORD(record) == rec_physical_db) { + while (GET_ATTRIBUTE(attribute) != att_end) + switch (attribute) { + case att_SQL_dialect: + SQL_dialect_flag = (USHORT) TRUE; + SQL_dialect = get_numeric(); + break; + + case att_page_size: + page_size = get_numeric(); + break; + + case att_sweep_interval: + sweep_interval = get_numeric(); + break; + + case att_forced_writes: + forced_writes = (USHORT) get_numeric(); + break; + + case att_no_reserve: + no_reserve = (USHORT) get_numeric(); + break; + +#ifdef READONLY_DATABASE + case att_db_read_only: + db_read_only = (UCHAR) get_numeric(); + break; +#endif /* READONLY_DATABASE */ + + case att_page_buffers: + page_buffers = get_numeric(); + break; + + default: + l = GET(); + if (l) + GET_SKIP(l); + break; + } + GET_RECORD(record); + } + + if (record != rec_database) + BURP_error_redirect(NULL_PTR, 32, 0, 0); +/* msg 32 Expected database description record */ + + if (tdgbl->gbl_sw_page_size && (tdgbl->gbl_sw_page_size < page_size)) { + BURP_print(110, (void *) page_size, (void *) tdgbl->gbl_sw_page_size, + NULL_PTR, NULL_PTR, NULL_PTR); +/* msg 110 Reducing the database page size from %ld bytes to %ld bytes */ + } + + if (tdgbl->gbl_sw_page_size) + page_size = tdgbl->gbl_sw_page_size; + + tdgbl->hdr_forced_writes = forced_writes; + + if (tdgbl->gbl_sw_no_reserve) + no_reserve = tdgbl->gbl_sw_no_reserve; + +#ifdef READONLY_DATABASE +/* Override attribute setting with user requirement */ + if (tdgbl->gbl_sw_mode == TRUE) + db_read_only = tdgbl->gbl_sw_mode_val; + else { + /* No access mode specified by user. Use attribute settings. Since the + * database is set to readOnly only after making it Online in + * RESTORE_restore(), pass on this information through Global structures */ + tdgbl->gbl_sw_mode = TRUE; + tdgbl->gbl_sw_mode_val = db_read_only; + } +#endif /* READONLY_DATABASE */ + + if (tdgbl->gbl_sw_page_buffers) + page_buffers = tdgbl->gbl_sw_page_buffers; + + d = dpb; + *d++ = (UCHAR) isc_dpb_version1; + *d++ = (UCHAR) isc_dpb_page_size; + *d++ = 2; + *d++ = 0; + *d++ = (UCHAR) (page_size >> 8); + *d++ = (UCHAR) isc_dpb_gbak_attach; + *d++ = (UCHAR) strlen(GDS_VERSION); + for (q = GDS_VERSION; *q;) + *d++ = *q++; + + if (sweep_interval != -1) { + *d++ = (UCHAR) isc_dpb_sweep_interval; + *d++ = 4; + *d++ = (UCHAR) sweep_interval; + *d++ = (UCHAR) (sweep_interval >> 8); + *d++ = (UCHAR) (sweep_interval >> 16); + *d++ = (UCHAR) (sweep_interval >> 24); + } +#ifdef READONLY_DATABASE +/* If the database is to be restored "read_only", fillup the data pages */ + if (no_reserve || db_read_only) +#else + if (no_reserve) +#endif /* READONLY_DATABASE */ + { + *d++ = (UCHAR) isc_dpb_no_reserve; + *d++ = 1; + *d++ = TRUE; + } + if (tdgbl->gbl_sw_user) { + *d++ = (UCHAR) isc_dpb_user_name; + *d++ = (UCHAR) strlen(tdgbl->gbl_sw_user); + for (q = tdgbl->gbl_sw_user; *q;) + *d++ = *q++; + } + if (tdgbl->gbl_sw_password) { + if (!tdgbl->gbl_sw_service_thd) + *d++ = (UCHAR) isc_dpb_password; + else + *d++ = (UCHAR) isc_dpb_password_enc; + *d++ = (UCHAR) strlen(tdgbl->gbl_sw_password); + for (q = tdgbl->gbl_sw_password; *q;) + *d++ = *q++; + } + if (page_buffers) { + *d++ = (UCHAR) isc_dpb_set_page_buffers; + *d++ = 4; + *d++ = (UCHAR) page_buffers; + *d++ = (UCHAR) (page_buffers >> 8); + *d++ = (UCHAR) (page_buffers >> 16); + *d++ = (UCHAR) (page_buffers >> 24); + } + +/* Turn off sync writes during restore */ + *d++ = (UCHAR) isc_dpb_force_write; + *d++ = 1; + *d++ = 0; + +/* +** +** which SQL dialect that this database speaks +** When we restore backup files that came from prior +** to V6, we force the SQL database dialect to 1 +** +*/ + + *d++ = (UCHAR) isc_dpb_sql_dialect; + *d++ = 1; + if (SQL_dialect_flag == TRUE) + *d++ = (UCHAR) SQL_dialect; + else + *d++ = (UCHAR) SQL_DIALECT_V5; + +/* start database up shut down */ + *d++ = (UCHAR) isc_dpb_shutdown; + *d++ = 1; + *d++ = (UCHAR) isc_dpb_shut_attachment; + *d++ = (UCHAR) isc_dpb_shutdown_delay; + *d++ = 2; + *d++ = 0; + *d++ = 0; + + l = d - dpb; + + if (isc_create_database(status_vector, + 0, + GDS_VAL(file_name), + GDS_REF(tdgbl->db_handle), l, dpb, 0)) { + BURP_error_redirect(status_vector, 33, file_name, 0); +/* msg 33 failed to create database %s */ + } + + if (tdgbl->gbl_sw_version) { + BURP_print(139, file_name, NULL_PTR, NULL_PTR, NULL_PTR, NULL_PTR); + /* msg 139 Version(s) for database "%s" */ + isc_version(&tdgbl->db_handle, BURP_output_version, "\t%s\n"); + } + + BURP_verbose(74, file_name, (void *) page_size, NULL_PTR, NULL_PTR, + NULL_PTR); +/* msg 74 created database %s, page_size %ld bytes */ +} + + +static void decompress( UCHAR * buffer, USHORT length) +{ +/************************************** + * + * d e c o m p r e s s + * + ************************************** + * + * Functional description + * Get a number of compressed bytes. + * + **************************************/ + UCHAR c, *p, *end; + SSHORT count; + TGBL tdgbl; + + tdgbl = GET_THREAD_DATA; + + p = buffer; + end = p + length; + + while (p < end) { + /* This change was made to restore National Semi-Conductor's corrupted */ + /* gbak file and it is in the code base now. -Andrew */ + + count = (SCHAR) GET(); + if (count > 0) { + if (end - p < count) { + BURP_print(202, (void *) count, (void *) (end - p), NULL_PTR, + NULL_PTR, NULL_PTR); + /* msg 202: adjusting a decompression length error: invalid length %d was adjusted to %d */ + count = end - p; + } + p = GET_BLOCK(p, count); + } + else if (count < 0) { + if (end + count < p) { + BURP_print(202, (void *) count, (void *) (p - end), NULL_PTR, + NULL_PTR, NULL_PTR); + /* msg 202: adjusting a decompression length error: invalid length %d was adjusted to %d */ + count = p - end; + } + c = GET(); + memset(p, c, -count); + p += -count; + } + } + + if (p > end) + BURP_error_redirect(NULL_PTR, 34, 0, 0); + /* msg 34 RESTORE: decompression length error */ +} + + +static void eat_blob(void) +{ +/************************************** + * + * e a t _ b l o b + * + ************************************** + * + * Functional description + * Discard a blob from backup file + * + **************************************/ + SLONG length; + TGBL tdgbl; + + tdgbl = GET_THREAD_DATA; + + length = get_numeric(); + + GET_SKIP(length); +} + + +static REL find_relation( TEXT * name) +{ +/************************************** + * + * f i n d _ r e l a t i o n + * + ************************************** + * + * Functional description + * Given a relation name, find the relation block. If there isn't + * one, produce a fatal error. + * + **************************************/ + TEXT *p, *q; + REL relation; + TGBL tdgbl; + + tdgbl = GET_THREAD_DATA; + + for (relation = tdgbl->relations; relation; relation = relation->rel_next) + for (p = relation->rel_name, q = name; *p == *q; p++, q++) + if (!*p) + return relation; + + BURP_error_redirect(NULL_PTR, 35, name, 0); +/* msg 35 can't find relation %s */ + + return NULL; +} + + +static void general_on_error(void) +{ +/************************************** + * + * g e n e r a l _ o n _ e r r o r + * + ************************************** + * + * Functional description + * Handle any general ON_ERROR clause during restore. + * + **************************************/ + TGBL tdgbl; + + tdgbl = GET_THREAD_DATA; + + BURP_print_status(tdgbl->status); + BURP_abort(); +} + + +static int get_acl( + TEXT * owner_nm, + ISC_QUAD * blob_id, ISC_QUAD * new_blob_id) +{ +/************************************** + * + * g e t _ a c l + * + ************************************** + * + * Functional description + * + * open the blob that contains the ACL list + * get the ACL list of a relation + * replace the owner of the relation in the ACL list with + * the creator of the relation + * create a new blob + * store the new ACL list in the new blob + * + **************************************/ + + static CONST SCHAR blr_items[] = { + isc_info_blob_max_segment, + isc_info_blob_total_length, + isc_info_blob_num_segments + }; + STATUS status_vector[ISC_STATUS_LENGTH]; + SLONG length, n; + SLONG *blob; + UCHAR *p, blob_info[32], item, *buffer, static_buffer[1024], + *new_buffer, *end_buffer; + USHORT l, max_segment, num_segments, new_len = 0; + TGBL tdgbl; + UCHAR *c_1, *from, *to, owner_nm_len; + SLONG id_person_len, cnt; + isc_blob_handle blob_handle = NULL; + + tdgbl = GET_THREAD_DATA; + new_buffer = NULL_PTR; + +/* If the blob is null, don't store it. It will be restored as null. */ + + if (!blob_id->isc_quad_high && !blob_id->isc_quad_low) + return FALSE; + +/* Open the blob and get it's vital statistics */ + + blob = NULL; + + if (isc_open_blob(status_vector, GDS_REF(tdgbl->db_handle), + GDS_REF(gds__trans), GDS_REF(blob), GDS_VAL(blob_id))) + /* msg 24 gds__open_blob failed */ + BURP_error_redirect(status_vector, 24, NULL, NULL); + + if (isc_blob_info(status_vector, GDS_REF(blob), sizeof(blr_items), + (UCHAR *) blr_items, sizeof(blob_info), blob_info)) + /* msg 20 gds__blob_info failed */ + BURP_error_redirect(status_vector, 20, NULL, NULL); + + length = 0; + p = blob_info; + + while ((item = *p++) != gds__info_end) { + l = (USHORT) gds__vax_integer(p, 2); + p += 2; + n = gds__vax_integer(p, l); + p += l; + switch (item) { + case isc_info_blob_max_segment: + max_segment = (USHORT) n; + break; + + case isc_info_blob_total_length: + length = n; + break; + + case isc_info_blob_num_segments: + num_segments = (USHORT) n; + /* + ** we assume that the ACL list was written out as + ** in one big segment + ** + */ + if (num_segments > 1) + assert(num_segments > 1); + break; + + default: + /* msg 79 don't understand blob info item %ld */ + BURP_print(79, (void *) item, NULL, NULL, NULL, NULL); + return FALSE; + } + } + + if (!length) { + if (isc_close_blob(status_vector, GDS_REF(blob))) + /* msg 23 gds__close_blob failed */ + BURP_error_redirect(status_vector, 23, NULL, NULL); + return FALSE; + } + +/* Rdb sometimes gets the length messed up */ + + if (length < max_segment) + length = max_segment; + +/* +** Allocate a buffer large enough for the largest segment and start +** grinding. +*/ + + if (!max_segment || max_segment <= sizeof(static_buffer)) + buffer = static_buffer; + else + buffer = BURP_ALLOC(max_segment); + + isc_get_segment(status_vector, + GDS_REF(blob), GDS_REF(l), max_segment, GDS_VAL(buffer)); + + if (isc_close_blob(status_vector, GDS_REF(blob))) { + if (buffer != static_buffer) + BURP_FREE(buffer); + /* msg 23 gds__close_blob failed */ + BURP_error_redirect(status_vector, 23, NULL, NULL); + } + + from = buffer + 3; /* skip ACL_version, ACL_id_list, and id_person */ + id_person_len = (int) *from; + + c_1 = owner_nm; + owner_nm_len = strlen(owner_nm); + + new_buffer = BURP_ALLOC(length - id_person_len + owner_nm_len); + + from = buffer; + to = new_buffer; + *to++ = *from++; /* copy ACL_verion */ + *to++ = *from++; /* copy ACL_id_list */ + *to++ = *from++; /* copy id_person */ + *to++ = owner_nm_len; + new_len = new_len + 4; + from = buffer + id_person_len + 4; + for (cnt = 0; cnt < owner_nm_len; cnt++) { + *to++ = *c_1++; + new_len++; + } + + end_buffer = buffer + length; + for (from = buffer + id_person_len + 4; from < end_buffer; from++) { + *to++ = *from; + new_len++; + } + + if (isc_create_blob2(status_vector, + GDS_REF(tdgbl->db_handle), + GDS_REF(gds__trans), + GDS_REF(blob_handle), + GDS_VAL(new_blob_id), 0, NULL)) { + if (buffer != static_buffer) + BURP_FREE(buffer); + if (new_buffer != NULL_PTR) + BURP_FREE(new_buffer); + /* msg 37 gds__create_blob failed */ + BURP_error_redirect(status_vector, 37, 0, 0); + } + + if (isc_put_segment(status_vector, + GDS_REF(blob_handle), new_len, GDS_VAL(new_buffer))) { + if (buffer != static_buffer) + BURP_FREE(buffer); + if (new_buffer != NULL_PTR) + BURP_FREE(new_buffer); + /* msg 38 gds__put_segment failed */ + BURP_error_redirect(status_vector, 38, 0, 0); + } + + if (isc_close_blob(status_vector, GDS_REF(blob_handle))) { + if (buffer != static_buffer) + BURP_FREE(buffer); + if (new_buffer != NULL_PTR) + BURP_FREE(new_buffer); + /* msg 23 gds__close_blob failed */ + BURP_error_redirect(status_vector, 23, 0, 0); + } + + if (buffer != static_buffer) + BURP_FREE(buffer); + + if (new_buffer != NULL_PTR) + BURP_FREE(new_buffer); + + return TRUE; +} + + +static void get_array( REL relation, UCHAR * record_buffer) +{ +/************************************** + * + * g e t _ a r r a y + * + ************************************** + * + * Functional description + * Read array data from input file to nice, + * shiney, new array. + * + **************************************/ + FLD field; + ATT_TYPE attribute; + STATUS status_vector[ISC_STATUS_LENGTH]; + SLONG last_element_dim[MAX_DIMENSION]; + SLONG fld_ranges[2 * MAX_DIMENSION]; + SLONG *blob_id, return_length, slice_length, *range, *end_ranges, lcount; + USHORT blr_length, count, field_number, field_length; + UCHAR *buffer, *p; + SCHAR *blr, blr_buffer[200]; /* enough for a sdl with 16 dimensions */ + LSTRING xdr_buffer, xdr_slice; + UCHAR scan_next_attr; + TGBL tdgbl; + + tdgbl = GET_THREAD_DATA; + +/* don't free something you don't allocate */ + + xdr_buffer.lstr_allocated = 0; + +/* Pick up attributes */ + + SKIP_INIT; + while (SKIP_SCAN, GET_ATTRIBUTE(attribute) != att_blob_data) + switch (attribute) { + case att_blob_field_number: + field_number = (USHORT) get_numeric(); + for (field = relation->rel_fields; field; field = field->fld_next) + if (field->fld_number == field_number) + break; + if (!field) + BURP_error_redirect(NULL_PTR, 36, 0, 0); + /* msg 36 Can't find field for blob */ + + field_length = field->fld_length; + if (field->fld_type == blr_varying) + field_length += sizeof(USHORT); + slice_length = field_length; + /** + Copy the ranges onto a buffer and let the program + mess with the copy rather than the original + **/ + memcpy(fld_ranges, field->fld_ranges, sizeof(fld_ranges)); + break; + + case att_array_dimensions: + field->fld_dimensions = (SSHORT) get_numeric(); + end_ranges = fld_ranges + 2 * field->fld_dimensions; + for (range = fld_ranges; range < end_ranges; range += 2) { + if (GET_ATTRIBUTE(attribute) != att_array_range_low) + bad_attribute(scan_next_attr, attribute, 58); + /* msg 58 array */ + else + range[0] = get_numeric(); + if (GET_ATTRIBUTE(attribute) != att_array_range_high) + bad_attribute(scan_next_attr, attribute, 58); + /* msg 58 array */ + else + range[1] = get_numeric(); + slice_length *= (range[1] - range[0] + 1); + } + break; + + default: + bad_attribute(scan_next_attr, attribute, 58); + /* msg 58 array */ + break; + } + + return_length = GET(); + return_length |= GET() << 8; + return_length |= GET() << 16; + return_length |= GET() << 24; + + blob_id = (SLONG *) ((UCHAR *) record_buffer + field->fld_offset); + + if (return_length != slice_length) { + int current_dim, divisor, elements_remaining; + int data_at, elements_written, upper, lower, dont_write; + int i1, i2, i3; + /* Ugh! The full array wasn't returned and versions of gbak prior to + V3.2I don't explicitly signal this. We must recompute the top + element to restore. + + Double Ugh! gbak (Versions prior to 5.0) while backing up calculates + the top dimensions incorrectly So whatever was written as top dimensions + is useless. 5.0 gbak has written correct dimensions, but what the heck + we'll calculate it again + */ + + elements_remaining = return_length / field_length; + /** + Backup (versions prior to 5.0) has surely written wrong dimensions. + Ignore whatever is read in fld_ranges and calculate the dimensions + of the last element. field->fld_ranges has the max dimensions. + last_element_dim holds only the upper bounds of each dimension. + **/ + for (i1 = 0, i3 = 0; i1 < field->fld_dimensions; i1++) { + divisor = 1; + for (i2 = (2 * (i1 + 1) + 1); i2 <= field->fld_dimensions * 2; + i2 += 2) + divisor *= + (field->fld_ranges[i2] - field->fld_ranges[i2 - 1] + 1); + last_element_dim[i1] = + (elements_remaining - 1) / divisor + field->fld_ranges[i3]; + elements_remaining -= + (last_element_dim[i1] - field->fld_ranges[i3]) * divisor; + i3 += 2; + } +#ifdef DEBUG + ib_fprintf(ib_stderr, + "\nLast element upper bounds read from backup file:\n"); + for (current_dim = 1; current_dim < field->fld_dimensions * 2; + current_dim += 2) + ib_fprintf(ib_stderr, "%d ", fld_ranges[current_dim]); + ib_fprintf(ib_stderr, "\nCalculated Last element upper bounds :\n"); + for (current_dim = 0; current_dim < field->fld_dimensions; + current_dim++) + ib_fprintf(ib_stderr, "%d ", last_element_dim[current_dim]); + ib_fprintf(ib_stderr, "return_length = %d\n", return_length); + ib_fprintf(ib_stderr, "elements_returned = %d\n", + return_length / field_length); + ib_fprintf(ib_stderr, "Max dims["); + for (current_dim = 1; current_dim < field->fld_dimensions * 2; + current_dim += 2) + ib_fprintf(ib_stderr, "%d ", field->fld_ranges[current_dim]); + ib_fprintf(ib_stderr, "]"); +#endif + data_at = 0; + /** + We have an irregurlar shaped slice to write. The following for loop + chops the array into writable rectangular/square slice and sends it + to the engine. When the loop cycles through all dimensions, we would + have written the whole of the irregular slice. + **/ + for (current_dim = 0; current_dim < field->fld_dimensions; + current_dim++) { + blr = blr_buffer; + dont_write = 0; + + /* build the sdl */ + + STUFF(gds__sdl_version1); + + STUFF(gds__sdl_struct); + STUFF(1); + + if (field->fld_type == blr_text || field->fld_type == blr_varying) { + if (field->fld_type == blr_text) + STUFF(blr_text2) + else + STUFF(blr_varying2); + STUFF_WORD(field->fld_character_set_id); + STUFF_WORD(field->fld_length); + } + else if (field->fld_type == blr_short || + field->fld_type == blr_long || + field->fld_type == blr_quad) { + STUFF(field->fld_type); + STUFF(field->fld_scale); + } + else + STUFF(field->fld_type); + + + STUFF(gds__sdl_relation); + stuff_string(&blr, relation->rel_name); + STUFF(gds__sdl_field); + stuff_string(&blr, field->fld_name); + + /* each element spec starts here */ + +#ifdef DEBUG + ib_fprintf(ib_stderr, "\nBounds written ["); +#endif + elements_written = 1; + end_ranges = field->fld_ranges + 2 * field->fld_dimensions; + /** + Here is the important work. Calculate the the bounds to be written + so that the resulting slice is a rectangular/square slice. + For a 2 dimensional array of size 1..N, 1..M, which is partially + filled, we have already calculated the dims of last element. Say + if this was x,y (x is row, y is column) then we do + isc_put_slice(1..x-1, 1..M); + isc_put_slice(x..x, 1..y); + similarly for a 3D array [N,M,K] whose last element dims are (x,y,z) + isc_put_slice(1..x-1, 1..M, 1..K); + isc_put_slice(x..x, 1..y-1, 1..K); + isc_put_slice(x..x, y..y, 1..z); + This is applicable for any number of dimensions. + Special cases: + for example in case of a 2D array (10,10) and if the last element + dims were (1,2), we would just do a isc_put_slice(1..1, 1..2). + This is applied for any number of dimensions. + **/ + for (range = field->fld_ranges, count = 0; range < end_ranges; + range += 2, count++) { + STUFF(gds__sdl_do2); + STUFF(count); + /** + Normally we loop through all dimensions chopping off slices + and writing them. This works fine but this also means that + we blindly put slices without actually figuring out if we + really need to do so. For eg: if we have a 2D array of + size [10,4] and the last element dims are [6,4] then all + we need to do is is to put one slice as + isc_put_slice(1..6,1..4) + rather than looping through the dimensions and putting + isc_put_slice(1..5,1..4) + isc_put_slice(6..6,1..4) + we could extend this logic to any no of dims. The following + if condition figures out such cases. This combined with + the Special case should optimize the no of isc_put_slice + we perform. + **/ + if (current_dim + 1 == field->fld_dimensions - 1 && + field->fld_dimensions - count == 2 && + last_element_dim[count + 1] == range[3]) { + STUFF(gds__sdl_long_integer); + STUFF_LONG(range[0]); + lower = range[0]; + STUFF(gds__sdl_long_integer); + STUFF_LONG(last_element_dim[count]); + upper = last_element_dim[count]; + elements_written *= (upper - lower + 1); + range += 2; + count++; + STUFF(gds__sdl_do2); + STUFF(count); + STUFF(gds__sdl_long_integer); + STUFF_LONG(range[0]); + lower = range[0]; + STUFF(gds__sdl_long_integer); + STUFF_LONG(last_element_dim[count]); + upper = last_element_dim[count]; + elements_written *= (upper - lower + 1); + ++current_dim; + break; + + } + if (current_dim == count) { + STUFF(gds__sdl_long_integer); + STUFF_LONG(range[0]); + lower = range[0]; + STUFF(gds__sdl_long_integer); + upper = (current_dim == field->fld_dimensions - 1) ? + last_element_dim[count] : (last_element_dim[count] - + 1); + if (upper < range[0]) { + /** + see Special Case above + **/ + dont_write = 1; + break; + } + STUFF_LONG(upper); + } + else if (current_dim < count) { + STUFF(gds__sdl_long_integer); + STUFF_LONG(range[0]); + STUFF(gds__sdl_long_integer); + STUFF_LONG(range[1]); + upper = range[1]; + lower = range[0]; + } + else if (current_dim > count) { + STUFF(gds__sdl_long_integer); + STUFF_LONG(last_element_dim[count]); + STUFF(gds__sdl_long_integer); + STUFF_LONG(last_element_dim[count]); + upper = lower = last_element_dim[count]; + } + elements_written *= (upper - lower + 1); +#ifdef DEBUG + ib_fprintf(ib_stderr, "%d..%d ", lower, upper); +#endif + } + if (dont_write) + continue; +#ifdef DEBUG + ib_fprintf(ib_stderr, "]"); + ib_fprintf(ib_stderr, "\n Elements Written=%d ", + elements_written); +#endif + + STUFF(gds__sdl_element); + STUFF(1); + STUFF(gds__sdl_scalar); + STUFF(0); + STUFF(field->fld_dimensions); + + for (count = 0; count < field->fld_dimensions; count++) { + STUFF(gds__sdl_variable); + STUFF(count); + } + + STUFF(gds__sdl_eoc); + +#ifdef DEBUG + if (debug_on) + PRETTY_print_sdl(blr_buffer, NULL_PTR, NULL_PTR, NULL_PTR, + NULL_PTR); +#endif + + blr_length = blr - blr_buffer; + + if (data_at == 0) { + buffer = BURP_ALLOC(return_length); + + if (tdgbl->gbl_sw_transportable) { + if (GET_ATTRIBUTE(attribute) != att_xdr_array) + /* msg 55 Expected XDR record length */ + BURP_error_redirect(NULL_PTR, 55, 0, 0); + else { + xdr_buffer.lstr_allocated = GET(); + xdr_buffer.lstr_allocated |= GET() << 8; + xdr_buffer.lstr_allocated |= GET() << 16; + xdr_buffer.lstr_allocated |= GET() << 24; + lcount = xdr_buffer.lstr_length = + xdr_buffer.lstr_allocated; + xdr_buffer.lstr_address = + BURP_ALLOC(xdr_buffer.lstr_allocated); + xdr_slice.lstr_allocated = xdr_slice.lstr_length = + return_length; + xdr_slice.lstr_address = buffer; + p = xdr_buffer.lstr_address; + } + } + else { + p = buffer; + lcount = return_length; + } + + if (lcount) + (void) GET_BLOCK(p, lcount); + + if (tdgbl->gbl_sw_transportable) + CAN_slice(&xdr_buffer, &xdr_slice, FALSE, blr_length, + blr_buffer); + } + + if (isc_put_slice(status_vector, + GDS_REF(tdgbl->db_handle), + GDS_REF(gds__trans), (ISC_QUAD *) GDS_VAL(blob_id), blr_length, blr_buffer, 0, /* param length for subset of an array handling */ + (ISC_LONG *) 0, /* param for subset of an array handling */ + elements_written * field->fld_length, + GDS_VAL(buffer + data_at))) { + BURP_print(81, field->fld_name, NULL_PTR, NULL_PTR, NULL_PTR, + NULL_PTR); + /* msg 81 error accessing blob field %s -- continuing */ + BURP_print_status(status_vector); +#ifdef DEBUG + PRETTY_print_sdl(blr_buffer, NULL_PTR, NULL_PTR, NULL_PTR, + NULL_PTR); +#endif + return; + } + data_at += elements_written * field->fld_length; +#ifdef DEBUG + ib_fprintf(ib_stderr, "next data_at = %d\n", data_at); +#endif + } + } + else { + /** This is the regular case we've got the entire array **/ + blr = blr_buffer; + + /* build the sdl */ + + STUFF(gds__sdl_version1); + + STUFF(gds__sdl_struct); + STUFF(1); + + if (field->fld_type == blr_text || field->fld_type == blr_varying) { + if (field->fld_type == blr_text) + STUFF(blr_text2) + else + STUFF(blr_varying2); + STUFF_WORD(field->fld_character_set_id); + STUFF_WORD(field->fld_length); + } + else if (field->fld_type == blr_short || + field->fld_type == blr_long || field->fld_type == blr_quad) { + STUFF(field->fld_type); + STUFF(field->fld_scale); + } + else + STUFF(field->fld_type); + + + STUFF(gds__sdl_relation); + stuff_string(&blr, relation->rel_name); + STUFF(gds__sdl_field); + stuff_string(&blr, field->fld_name); + + /* each element spec starts here */ + + for (range = fld_ranges, count = 0; range < end_ranges; + range += 2, count++) { + STUFF(gds__sdl_do2); + STUFF(count); + STUFF(gds__sdl_long_integer); + STUFF_LONG(range[0]); + STUFF(gds__sdl_long_integer); + STUFF_LONG(range[1]); + } + + STUFF(gds__sdl_element); + STUFF(1); + STUFF(gds__sdl_scalar); + STUFF(0); + STUFF(field->fld_dimensions); + + for (count = 0; count < field->fld_dimensions; count++) { + STUFF(gds__sdl_variable); + STUFF(count); + } + + STUFF(gds__sdl_eoc); + +#ifdef DEBUG + if (debug_on) + PRETTY_print_sdl(blr_buffer, NULL_PTR, NULL_PTR, NULL_PTR, + NULL_PTR); +#endif + + blr_length = blr - blr_buffer; + + buffer = BURP_ALLOC(return_length); + + if (tdgbl->gbl_sw_transportable) { + if (GET_ATTRIBUTE(attribute) != att_xdr_array) + BURP_error_redirect(NULL_PTR, 55, 0, 0); + /* msg 55 Expected XDR record length */ + else { + xdr_buffer.lstr_allocated = GET(); + xdr_buffer.lstr_allocated |= GET() << 8; + xdr_buffer.lstr_allocated |= GET() << 16; + xdr_buffer.lstr_allocated |= GET() << 24; + lcount = xdr_buffer.lstr_length = xdr_buffer.lstr_allocated; + xdr_buffer.lstr_address = + BURP_ALLOC(xdr_buffer.lstr_allocated); + xdr_slice.lstr_allocated = xdr_slice.lstr_length = + return_length; + xdr_slice.lstr_address = buffer; + p = xdr_buffer.lstr_address; + } + } + else { + p = buffer; + lcount = return_length; + } + + if (lcount) + (void) GET_BLOCK(p, lcount); + + if (tdgbl->gbl_sw_transportable) + CAN_slice(&xdr_buffer, &xdr_slice, FALSE, blr_length, blr_buffer); + + + if (isc_put_slice(status_vector, + GDS_REF(tdgbl->db_handle), + GDS_REF(gds__trans), (ISC_QUAD *) GDS_VAL(blob_id), blr_length, blr_buffer, 0, /* param length for subset of an array handling */ + (ISC_LONG *) 0, /* param for subset of an array handling */ + return_length, GDS_VAL(buffer))) { + BURP_print(81, field->fld_name, NULL_PTR, NULL_PTR, NULL_PTR, + NULL_PTR); + /* msg 81 error accessing blob field %s -- continuing */ + BURP_print_status(status_vector); +#ifdef DEBUG + PRETTY_print_sdl(blr_buffer, NULL_PTR, NULL_PTR, NULL_PTR, + NULL_PTR); +#endif + return; + } + } + + BURP_FREE(buffer); + if (tdgbl->gbl_sw_transportable && xdr_buffer.lstr_allocated) + BURP_FREE(xdr_buffer.lstr_address); +} + + +static void get_blob( FLD fields, UCHAR * record_buffer) +{ +/************************************** + * + * g e t _ b l o b + * + ************************************** + * + * Functional description + * Read blob attributes and copy data from input file to nice, + * shiny, new blob. + * + **************************************/ + FLD field; + ATT_TYPE attribute; + STATUS status_vector[ISC_STATUS_LENGTH]; + ULONG segments; + ISC_QUAD *blob_id; + SLONG *blob; + USHORT field_number, max_segment, length; + UCHAR *buffer, static_buffer[1024]; + UCHAR scan_next_attr; + TGBL tdgbl; + + tdgbl = GET_THREAD_DATA; + +/* Pick up attributes */ + + segments = 0; + + SKIP_INIT; + while (SKIP_SCAN, GET_ATTRIBUTE(attribute) != att_blob_data) + switch (attribute) { + case att_blob_field_number: + field_number = (USHORT) get_numeric(); + break; + + case att_blob_max_segment: + max_segment = (USHORT) get_numeric(); + break; + + case att_blob_number_segments: + segments = get_numeric(); + break; + + case att_blob_type: + (void) get_numeric(); + break; + + default: + bad_attribute(scan_next_attr, attribute, 64); + /* msg 64 blob */ + break; + } + +/* Find the field associated with the blob */ + + for (field = fields; field; field = field->fld_next) + if (field->fld_number == field_number) + break; + + if (!field) + BURP_error_redirect(NULL_PTR, 36, 0, 0); + /* msg 36 Can't find field for blob */ + +/* Create new blob */ + + blob_id = (ISC_QUAD*) ((UCHAR *) record_buffer + field->fld_offset); + blob = NULL; + + if (isc_create_blob(status_vector, + GDS_REF(tdgbl->db_handle), + GDS_REF(gds__trans), + GDS_REF(blob), (ISC_QUAD *) GDS_VAL(blob_id))) + BURP_error_redirect(status_vector, 37, 0, 0); + /* msg 37 gds__create_blob failed */ + +/* Allocate blob buffer is static buffer is too short */ + + if (!max_segment || max_segment <= sizeof(static_buffer)) + buffer = static_buffer; + else + buffer = BURP_ALLOC(max_segment); + +/* Eat up blob segments */ + + for (; segments > 0; --segments) { + length = GET(); + length |= GET() << 8; + if (length) { + (void) GET_BLOCK(buffer, length); + } + if (isc_put_segment(status_vector, + GDS_REF(blob), + length, + GDS_VAL(buffer))) + BURP_error_redirect(status_vector, 38, 0, 0); + /* msg 38 gds__put_segment failed */ + } + + if (isc_close_blob(status_vector, GDS_REF(blob))) + BURP_error_redirect(status_vector, 23, 0, 0); + /* msg 23 gds__close_blob failed */ + + if (buffer != static_buffer) + BURP_FREE(buffer); +} + + +static void get_blr_blob( ISC_QUAD * blob_id, USHORT glb_trans) +{ +/************************************** + * + * g e t _ b l r _ b l o b + * + ************************************** + * + * Functional description + * Read blob attributes and copy data from input file to nice, + * shiney, new blob. + * + **************************************/ + STATUS status_vector[ISC_STATUS_LENGTH]; + SLONG *blob; + USHORT length, l; + UCHAR *buffer, static_buffer[1024], *p; + isc_tr_handle local_trans; + TGBL tdgbl; + + tdgbl = GET_THREAD_DATA; + + length = (USHORT) get_numeric(); + +/* Create new blob */ + + blob = NULL; + + if (glb_trans && tdgbl->global_trans) + local_trans = tdgbl->global_trans; + else + local_trans = gds__trans; + + if (isc_create_blob(status_vector, + GDS_REF(tdgbl->db_handle), + GDS_REF(local_trans), + GDS_REF(blob), + GDS_VAL(blob_id))) + BURP_error_redirect(status_vector, 37, 0, 0); + /* msg 37 gds__create_blob failed */ + +/* Allocate blob buffer is static buffer is too short */ + + if (!length || length + 1 <= sizeof(static_buffer)) + buffer = static_buffer; + else { + l = length + 1; + buffer = BURP_ALLOC(l); + } + + if (l = length) { + p = buffer; + p = GET_BLOCK(p, l); + } + +/* Make sure it has an eoc */ + if ((*--p) != blr_eoc) { + length++; + *++p = blr_eoc; + } + + if (isc_put_segment(status_vector, + GDS_REF(blob), + length, + GDS_VAL(buffer))) + BURP_error_redirect(status_vector, 38, 0, 0); + /* msg 38 gds__put_segment failed */ + + if (isc_close_blob(status_vector, GDS_REF(blob))) + BURP_error_redirect(status_vector, 23, 0, 0); + /* msg 23 gds__close_blob failed */ + + if (buffer != static_buffer) + BURP_FREE(buffer); +} + + +static BOOLEAN get_character_set(void) +{ +/************************************** + * + * g e t _ c h a r a c t e r _ s e t s + * + ************************************** + * + * Functional description + * Restore data for user defined character sets + * + **************************************/ + ATT_TYPE attribute; + UCHAR scan_next_attr; + TGBL tdgbl; + + tdgbl = GET_THREAD_DATA; + + STORE (REQUEST_HANDLE tdgbl->handles_get_character_sets_req_handle1) + X IN RDB$CHARACTER_SETS + X.RDB$CHARACTER_SET_NAME.NULL = TRUE; + X.RDB$FORM_OF_USE.NULL = TRUE; + X.RDB$NUMBER_OF_CHARACTERS.NULL = TRUE; + X.RDB$DEFAULT_COLLATE_NAME.NULL = TRUE; + X.RDB$CHARACTER_SET_ID.NULL = TRUE; + X.RDB$SYSTEM_FLAG.NULL = TRUE; + X.RDB$DESCRIPTION.NULL = TRUE; + X.RDB$FUNCTION_NAME.NULL = TRUE; + X.RDB$BYTES_PER_CHARACTER.NULL = TRUE; + + SKIP_INIT; + while (SKIP_SCAN, GET_ATTRIBUTE (attribute) != att_end) + switch (attribute) + { + + case att_charset_name: + X.RDB$CHARACTER_SET_NAME.NULL = FALSE; + get_text (X.RDB$CHARACTER_SET_NAME, sizeof (X.RDB$CHARACTER_SET_NAME)); + BURP_verbose (msgVerbose_restore_charset, X.RDB$CHARACTER_SET_NAME, NULL_PTR, NULL_PTR, NULL_PTR, NULL_PTR); + break; + + case att_charset_form: + X.RDB$FORM_OF_USE.NULL = FALSE; + get_text (X.RDB$FORM_OF_USE, sizeof (X.RDB$FORM_OF_USE)); + break; + + case att_charset_numchar: + X.RDB$NUMBER_OF_CHARACTERS.NULL = FALSE; + X.RDB$NUMBER_OF_CHARACTERS = (USHORT) get_numeric(); + break; + + case att_charset_coll: + X.RDB$DEFAULT_COLLATE_NAME.NULL = FALSE; + get_text (X.RDB$DEFAULT_COLLATE_NAME, sizeof (X.RDB$DEFAULT_COLLATE_NAME)); + break; + + case att_charset_id: + X.RDB$CHARACTER_SET_ID.NULL = FALSE; + X.RDB$CHARACTER_SET_ID = (USHORT) get_numeric(); + break; + + case att_charset_sysflag: + X.RDB$SYSTEM_FLAG.NULL = FALSE; + X.RDB$SYSTEM_FLAG = (USHORT) get_numeric(); + break; + + case att_charset_description: + X.RDB$DESCRIPTION.NULL = FALSE; + get_source_blob (&X.RDB$DESCRIPTION, 0); + break; + + case att_charset_funct: + X.RDB$FUNCTION_NAME.NULL = FALSE; + get_text (X.RDB$FUNCTION_NAME, sizeof (X.RDB$FUNCTION_NAME)); + break; + + case att_charset_bytes_char: + X.RDB$BYTES_PER_CHARACTER.NULL = FALSE; + X.RDB$BYTES_PER_CHARACTER = (USHORT) get_numeric(); + break; + + default: + bad_attribute (scan_next_attr, attribute, msgErr_restore_charset); + /* RDB$CHARSETS */ + break; + } + END_STORE; + ON_ERROR + general_on_error (); + END_ERROR; + + return TRUE; +} + + +static BOOLEAN get_chk_constraint(void) +{ +/************************************** + * + * g e t _ c h k _ c o n s t r a i n t + * + ************************************** + * + * Functional description + * Restore data for check constraints. + * + **************************************/ + ATT_TYPE attribute; + UCHAR scan_next_attr; + TGBL tdgbl; + + tdgbl = GET_THREAD_DATA; + + STORE (REQUEST_HANDLE tdgbl->handles_get_chk_constraint_req_handle1) + X IN RDB$CHECK_CONSTRAINTS + X.RDB$CONSTRAINT_NAME.NULL = TRUE; + X.RDB$TRIGGER_NAME.NULL = TRUE; + SKIP_INIT; + while (SKIP_SCAN, GET_ATTRIBUTE (attribute) != att_end) + switch (attribute) + { + case att_chk_constraint_name: + X.RDB$CONSTRAINT_NAME.NULL = FALSE; + get_text (X.RDB$CONSTRAINT_NAME, sizeof (X.RDB$CONSTRAINT_NAME)); + break; + + case att_chk_trigger_name: + X.RDB$TRIGGER_NAME.NULL = FALSE; + get_text (X.RDB$TRIGGER_NAME, sizeof (X.RDB$TRIGGER_NAME)); + break; + + default: + bad_attribute (scan_next_attr, attribute, 208); + /* msg 208 relation constraint */ + break; + } + END_STORE; + ON_ERROR + general_on_error (); + END_ERROR; + + return TRUE; +} + + +static BOOLEAN get_collation(void) +{ +/************************************** + * + * g e t _ c o l l a t i o n + * + ************************************** + * + * Functional description + * Restore data for user defined collations + * + **************************************/ + ATT_TYPE attribute; + UCHAR scan_next_attr; + TGBL tdgbl; + + tdgbl = GET_THREAD_DATA; + + STORE (REQUEST_HANDLE tdgbl->handles_get_collation_req_handle1) + X IN RDB$COLLATIONS + X.RDB$COLLATION_NAME.NULL = TRUE; + X.RDB$COLLATION_ID.NULL = TRUE; + X.RDB$CHARACTER_SET_ID.NULL = TRUE; + X.RDB$COLLATION_ATTRIBUTES.NULL = TRUE; + X.RDB$SYSTEM_FLAG.NULL = TRUE; + X.RDB$DESCRIPTION.NULL = TRUE; + X.RDB$FUNCTION_NAME.NULL = TRUE; + + SKIP_INIT; + while (SKIP_SCAN, GET_ATTRIBUTE (attribute) != att_end) + switch (attribute) + { + + case att_coll_name: + X.RDB$COLLATION_NAME.NULL = FALSE; + get_text (X.RDB$COLLATION_NAME, sizeof (X.RDB$COLLATION_NAME)); + BURP_verbose (msgVerbose_restore_collation, X.RDB$COLLATION_NAME, NULL_PTR, NULL_PTR, NULL_PTR, NULL_PTR); + break; + + case att_coll_id: + X.RDB$COLLATION_ID.NULL = FALSE; + X.RDB$COLLATION_ID = (USHORT) get_numeric(); + break; + + case att_coll_cs_id: + X.RDB$CHARACTER_SET_ID.NULL = FALSE; + X.RDB$CHARACTER_SET_ID = (USHORT) get_numeric(); + break; + + case att_coll_attr: + X.RDB$COLLATION_ATTRIBUTES.NULL = FALSE; + X.RDB$COLLATION_ATTRIBUTES = (USHORT) get_numeric(); + break; + + case att_coll_subtype: /* No longer used: 93-11-15 DBS */ + /* still present to handle V4 R&D + gbak files */ + (void) get_numeric(); + break; + + case att_coll_sysflag: + X.RDB$SYSTEM_FLAG.NULL = FALSE; + X.RDB$SYSTEM_FLAG = (USHORT) get_numeric(); + break; + + case att_coll_description: + X.RDB$DESCRIPTION.NULL = FALSE; + get_source_blob (&X.RDB$DESCRIPTION, 0); + break; + + case att_coll_funct: + X.RDB$FUNCTION_NAME.NULL = FALSE; + get_text (X.RDB$FUNCTION_NAME, sizeof (X.RDB$FUNCTION_NAME)); + break; + + default: + bad_attribute (scan_next_attr, attribute, msgErr_restore_collation); + /* Bad RDB$COLLATION */ + break; + } + END_STORE; + ON_ERROR + general_on_error (); + END_ERROR; + + return TRUE; +} + + +static REC_TYPE get_data( REL relation) +{ +/************************************** + * + * g e t _ d a t a + * + ************************************** + * + * Functional description + * Write data records for a relation. + * + **************************************/ + FLD field; + int *request, records; + TEXT *p; + SCHAR *blr, *blr_buffer; + RCRD_OFFSET offset; + USHORT l; + ULONG length, old_length; + SSHORT count, blr_length, alignment, dtype; + SSHORT *buffer; + LSTRING data; + STATUS status_vector[ISC_STATUS_LENGTH]; + SLONG *blob_id; + REC_TYPE record; + TGBL tdgbl; + isc_req_handle req_handle = NULL; + TEXT index_name[32]; + long error_code; + + tdgbl = GET_THREAD_DATA; + +/* If we're only doing meta-data, ignore data records */ + + if (tdgbl->gbl_sw_meta) + return ignore_data(relation); + +/* Start by counting the interesting fields */ + + offset = length = 0; + count = 0; + + for (field = relation->rel_fields; field; field = field->fld_next) + if (!(field->fld_flags & FLD_computed)) { + count++; + length += field->fld_name_length; + } + + if (tdgbl->RESTORE_format >= 2) + count += count; + +/* Time to generate blr to store data. Whoppee. */ + + blr = blr_buffer = (SCHAR *) BURP_ALLOC(200 + length + count * 18); + STUFF(blr_version4); + STUFF(blr_begin); + STUFF(blr_message); + STUFF(0); /* Message number */ + STUFF_WORD(count); /* Number of fields, counting eof */ + count = 0; + + for (field = relation->rel_fields; field; field = field->fld_next) { + if (field->fld_flags & FLD_computed) + continue; + + /* arrays are of various fld_types but are really blobs */ + + dtype = field->fld_type; + length = field->fld_length; + alignment = 4; + + if (field->fld_flags & FLD_array) + dtype = blr_blob; + + if (dtype <= DTYPE_BLR_MAX) { + l = gds_cvt_blr_dtype[dtype]; + alignment = type_alignments[l]; + if (l = type_lengths[l]) + length = l; + } + + switch (dtype) { + case blr_text: + case blr_varying: + if (dtype == blr_text) + STUFF(blr_text2) + else + STUFF(blr_varying2); + STUFF_WORD(field->fld_character_set_id); + STUFF_WORD(field->fld_length); + if (dtype == blr_varying) + length += sizeof(USHORT); + break; + + case blr_short: + case blr_long: + case blr_quad: + case blr_int64: + STUFF(field->fld_type); + STUFF(field->fld_scale); + break; + + case blr_float: + case blr_double: + case blr_timestamp: + case blr_sql_time: + case blr_sql_date: + STUFF(field->fld_type); + break; + + case blr_blob: + alignment = type_alignments[dtype_blob]; + length = type_lengths[dtype_blob]; + STUFF(blr_quad); + STUFF(0); + break; + + default: +#ifdef SUPERSERVER + BURP_svc_error(26, isc_arg_number, field->fld_type, + 0, NULL, 0, NULL, 0, NULL, 0, NULL); +#else + BURP_error(26, (void *) field->fld_type, 0, 0, 0, 0); + /* msg 26 datatype %ld not understood */ +#endif + break; + } + if (alignment) + offset = FB_ALIGN(offset, alignment); + field->fld_offset = offset; + field->fld_parameter = count++; + offset += length; + } + +/* If this is format version 2, build fields for null flags */ + + if (tdgbl->RESTORE_format >= 2) + for (field = relation->rel_fields; field; field = field->fld_next) { + if (field->fld_flags & FLD_computed) + continue; + STUFF(blr_short); + STUFF(0); + offset = FB_ALIGN(offset, sizeof(SSHORT)); + field->fld_missing_parameter = count++; + offset += sizeof(SSHORT); + } + + length = offset; + +/* Build STORE statement */ + + STUFF(blr_receive); + STUFF(0); + STUFF(blr_store); + STUFF(blr_relation); + stuff_string(&blr, relation->rel_name); + STUFF(0); /* context variable */ + STUFF(blr_begin); + + for (field = relation->rel_fields; field; field = field->fld_next) { + if (field->fld_flags & FLD_computed) + continue; + STUFF(blr_assignment); + if (tdgbl->RESTORE_format >= 2) { + STUFF(blr_parameter2); + STUFF(0); + STUFF_WORD(field->fld_parameter); + STUFF_WORD(field->fld_missing_parameter); + } + else { + STUFF(blr_parameter); + STUFF(0); + STUFF_WORD(field->fld_parameter); + } + STUFF(blr_field); + STUFF(0); + stuff_string(&blr, field->fld_name); + } + + STUFF(blr_end); + STUFF(blr_end); + STUFF(blr_eoc); + +/* Compile request */ + +#ifdef DEBUG + isc_print_blr(blr_buffer, NULL_PTR, NULL_PTR, 0); +#endif + + request = NULL; + blr_length = blr - blr_buffer; + if (isc_compile_request(status_vector, + GDS_REF(tdgbl->db_handle), + GDS_REF(request), + blr_length, GDS_VAL(blr_buffer))) { + isc_print_blr(blr_buffer, NULL_PTR, NULL_PTR, 0); + if (!tdgbl->gbl_sw_incremental) + BURP_error_redirect(status_vector, 27, 0, 0); +/* msg 27 gds__compile_request failed */ + else { + BURP_print_status(status_vector); + BURP_FREE(blr_buffer); + return ignore_data(relation); + } + } + + BURP_FREE(blr_buffer); + buffer = NULL; + + data.lstr_allocated = records = 0; + + BURP_verbose(124, relation->rel_name, NULL_PTR, NULL_PTR, NULL_PTR, + NULL_PTR); +/* msg 124 restoring data for relation %s */ + + data.lstr_address = NULL; + old_length = 0; + + while (TRUE) { + if (GET() != att_data_length) + BURP_error_redirect(NULL_PTR, 39, 0, 0); +/* msg 39 expected record length */ + l = (USHORT) get_numeric(); + if (!tdgbl->gbl_sw_transportable && l != length) { + if (!old_length) + old_length = recompute_length(relation); + if (l != old_length) +#ifdef SUPERSERVER + BURP_svc_error(40, isc_arg_number, length, isc_arg_number, l, + 0, NULL, 0, NULL, 0, NULL); +#else + BURP_error(40, (void *) length, (void *) l, 0, 0, 0); +/* msg 40 wrong length record, expected %ld encountered %ld */ +#endif + } + if (!buffer) + buffer = (SSHORT *) BURP_ALLOC(MAX(length, l)); + + if (tdgbl->gbl_sw_transportable) + if (GET() != att_xdr_length) + BURP_error_redirect(NULL_PTR, 55, 0, 0); +/* msg 55 Expected XDR record length */ + else { + data.lstr_length = l = (USHORT) get_numeric(); + if (l > data.lstr_allocated) { + data.lstr_allocated = l; + if (data.lstr_address) + BURP_FREE(data.lstr_address); + data.lstr_address = + (UCHAR *) BURP_ALLOC(data.lstr_allocated); + } + p = (TEXT *) data.lstr_address; + } + else + p = (TEXT *) buffer; + if (GET() != att_data_data) + BURP_error_redirect(NULL_PTR, 41, 0, 0); +/* msg 41 expected data attribute */ + + if (tdgbl->gbl_sw_compress) + decompress(p, l); + else { + (void) GET_BLOCK(p, l); + } + + if (old_length) + realign((UCHAR *) buffer, relation); + + if (tdgbl->gbl_sw_transportable) + CAN_encode_decode(relation, &data, (UCHAR *) buffer, FALSE); + + records++; + + if ((records % RESTORE_VERBOSE_INTERVAL) == 0) + BURP_verbose(107, (void *) records, NULL_PTR, NULL_PTR, NULL_PTR, + NULL_PTR); + + for (field = relation->rel_fields; field; field = field->fld_next) + if ((field->fld_type == blr_blob) + || (field->fld_flags & FLD_array)) { + blob_id = (SLONG *) ((SCHAR *) buffer + field->fld_offset); + blob_id[0] = blob_id[1] = 0; + } + + GET_RECORD(record); + while (record == rec_blob || record == rec_array) { + if (record == rec_blob) + get_blob(relation->rel_fields, (UCHAR *) buffer); + + else if (record == rec_array) + get_array(relation, (UCHAR *) buffer); + + GET_RECORD(record); + } + + + if (isc_start_and_send(status_vector, + GDS_REF(request), + GDS_REF(gds__trans), + 0, + (USHORT) length, + GDS_VAL(buffer), + 0)) + if (status_vector[1] == gds__not_valid) + if (tdgbl->gbl_sw_incremental) { + BURP_print(138, relation->rel_name, NULL_PTR, NULL_PTR, + NULL_PTR, NULL_PTR); +/* msg 138 validation error on field in relation %s */ + BURP_print_status(status_vector); + } + else + BURP_error_redirect(status_vector, 47, 0, 0); +/* msg 47 warning -- record could not be restored */ + else if (tdgbl->gbl_sw_incremental) { + BURP_print(114, relation->rel_name, NULL_PTR, NULL_PTR, + NULL_PTR, NULL_PTR); +/* msg 114 restore failed for record in relation %s */ + BURP_print_status(status_vector); + } + else + BURP_error_redirect(status_vector, 48, 0, 0); +/* msg 48 gds__send failed */ + if (record != rec_data) + break; + } + + BURP_FREE(buffer); + if (data.lstr_address) + BURP_FREE(data.lstr_address); + + isc_release_request(status_vector, GDS_REF(request)); + if (tdgbl->gbl_sw_incremental) { + BURP_verbose(72, relation->rel_name, NULL_PTR, NULL_PTR, NULL_PTR, + NULL_PTR); + /* msg 72 committing data for relation %s */ + COMMIT + /* existing ON_ERROR continues past error, beck */ + ON_ERROR + + /* Fix for bug_no 8055: + don't throw away the database just because an index + could not be made */ + while (error_code = tdgbl->status_vector[1]) + { + switch (error_code) + { + case gds__sort_mem_err: + case gds__no_dup: + strcpy(index_name, (TEXT *)tdgbl->status_vector[3]); + BURP_print_status(tdgbl->status_vector); + FOR (REQUEST_HANDLE req_handle) + IDX IN RDB$INDICES WITH IDX.RDB$INDEX_NAME EQ index_name + MODIFY IDX USING IDX.RDB$INDEX_INACTIVE = TRUE; + BURP_print(240, index_name, NULL_PTR, NULL_PTR, NULL_PTR, NULL_PTR); + /* msg 240 Index \"%s\" failed to activate because: */ + if ( error_code == gds__no_dup ) + { + BURP_print(241, NULL_PTR, NULL_PTR, NULL_PTR, NULL_PTR, NULL_PTR); + /* msg 241 The unique index has duplicate values or NULLs */ + BURP_print(242, NULL_PTR, NULL_PTR, NULL_PTR, NULL_PTR, NULL_PTR); + /* msg 242 Delete or Update duplicate values or NULLs, and activate index with */ + } + else + { + BURP_print(244, NULL_PTR, NULL_PTR, NULL_PTR, NULL_PTR, NULL_PTR); + /* msg 244 Not enough disk space to create the sort file for an index */ + BURP_print(245, NULL_PTR, NULL_PTR, NULL_PTR, NULL_PTR, NULL_PTR); + /* msg 245 Set the TMP environment variable to a directory on a filesystem that does have enough space, and activate index with */ + } + BURP_print(243, index_name, NULL_PTR, NULL_PTR, NULL_PTR, NULL_PTR); + /* msg 243 ALTER INDEX \"%s\" ACTIVE; */ + END_MODIFY; + END_FOR; + /* don't bring the database on-line */ + flag_on_line = FALSE; + /* commit one more time */ + COMMIT + ON_ERROR + continue; + END_ERROR + break; + default: + BURP_print (69, relation->rel_name, NULL_PTR, NULL_PTR, NULL_PTR, NULL_PTR); + /* msg 69 commit failed on relation %s */ + BURP_print_status (tdgbl->status_vector); + ROLLBACK; + ON_ERROR + general_on_error (); + END_ERROR; + break; + } /* end of switch */ + } /* end of while */ + END_ERROR; + EXEC SQL SET TRANSACTION NO_AUTO_UNDO; + if (gds__status[1]) + EXEC SQL SET TRANSACTION; + } + BURP_verbose(107, (void *) records, NULL_PTR, NULL_PTR, NULL_PTR, + NULL_PTR); +/* msg 107 %ld records restored */ + + return record; +} + + +static BOOLEAN get_exception(void) +{ +/************************************** + * + * g e t _ e x c e p t i o n + * + ************************************** + * + * Functional description + * Reconstruct a exception. + * + **************************************/ + ATT_TYPE attribute; + TEXT temp[32]; + ULONG l; + UCHAR scan_next_attr; + TGBL tdgbl; + + tdgbl = GET_THREAD_DATA; + + STORE (REQUEST_HANDLE tdgbl->handles_get_exception_req_handle1) + X IN RDB$EXCEPTIONS + X.RDB$DESCRIPTION.NULL = TRUE; + X.RDB$MESSAGE.NULL = TRUE; + SKIP_INIT; + while (SKIP_SCAN, GET_ATTRIBUTE (attribute) != att_end) + switch (attribute) + { + case att_exception_name: + l = get_text (X.RDB$EXCEPTION_NAME, sizeof (X.RDB$EXCEPTION_NAME)); + MISC_terminate (X.RDB$EXCEPTION_NAME, temp, l, sizeof (temp)); + BURP_verbose (199, temp, NULL_PTR, NULL_PTR, NULL_PTR, NULL_PTR); + /* msg 199 restoring exception %s */ + break; + + case att_exception_description: + get_misc_blob (&X.RDB$DESCRIPTION, 0, 0); + X.RDB$DESCRIPTION.NULL = FALSE; + break; + + case att_exception_description2: + get_source_blob (&X.RDB$DESCRIPTION, 0); + X.RDB$DESCRIPTION.NULL = FALSE; + break; + + case att_exception_msg: + get_text (X.RDB$MESSAGE, sizeof (X.RDB$MESSAGE)); + X.RDB$MESSAGE.NULL = FALSE; + break; + + default: + bad_attribute (scan_next_attr, attribute, 89); + /* msg 89 function */ + break; + } + END_STORE; + ON_ERROR + general_on_error (); + END_ERROR; + + return TRUE; +} + + +static BOOLEAN cvt_v3_to_v4_intl( + SSHORT dtype, + SSHORT * scale, + SSHORT * sub_type, + SSHORT * character_set_id, +SSHORT * collation_id) +{ +/************************************** + * + * c v t _ v 3 _ t o _ v 4 _ i n t l + * + ************************************** + * + * Functional description + * Convert a V3 scale / sub_type to V4 character_set / collate_sequence + * + * Version 3.2 & 3.3 of InterBase used different methodologies + * to tag international text character sets and country collation + * sequences. This routine will convert a V3 metadata definition + * into the corresponding V4 definition. + * + * In V3.x, sub-type 0 is normal text, subtype 1 is "text that can + * contain a NULL byte, and can therefore not be represented + * using a c-string datatype, always use fixed". + * + * Return TRUE if a conversion was performed, FALSE otherwise. + * + **************************************/ + SSHORT i; + + switch (dtype) { + case blr_text2: + case blr_varying2: + case blr_cstring2: + + case blr_text: + case blr_varying: + case blr_cstring: + + /* Japanese V3.2 used SCALE 5 to indicate SJIS text */ + if (*scale == 5 && ((*sub_type == 0) || (*sub_type == 1))) { + *character_set_id = CS_JIS_0208; + *collation_id = 0; + BURP_verbose(237, (void *) *scale, (void *) *character_set_id, + (void *) *collation_id, NULL_PTR, NULL_PTR); + /* msg 237: Converted V3 scale: %d to + character_set_id: %d and callate_id: %d. */ + *scale = 0; + return TRUE; + } + + /* Japanese V3.2 used SCALE 6 to indicate EUCJ text */ + if (*scale == 6 && (*sub_type == 0 || *sub_type == 1)) { + *character_set_id = CS_EUCJ; + *collation_id = 0; + BURP_verbose(237, (void *) *scale, (void *) *character_set_id, + (void *) *collation_id, NULL_PTR, NULL_PTR); + /* msg 237: Converted V3 scale: %d to + character_set_id: %d and callate_id: %d. */ + *scale = 0; + return TRUE; + } + + /* V3.3 used SCALE 0 and Subtypes in range 100 - 255 for + European text types and collations */ + + if (*scale != 0) + return FALSE; + + if (*sub_type == 0 || *sub_type == 1) + /* Do not process sub_type 0,1 conversion */ + return FALSE; + + for (i = 0; i < cvtbl_len; i++) + if (sub_type_cvtbl[i].sub_type == *sub_type) { + *character_set_id = sub_type_cvtbl[i].character_set_id; + *collation_id = sub_type_cvtbl[i].collation_id; + + BURP_verbose(236, (void *) *sub_type, + (void *) *character_set_id, + (void *) *collation_id, NULL_PTR, NULL_PTR); + /* msg 236: Converted V3 sub_type: %d to + character_set_id: %d and callate_id: %d. */ + + *sub_type = 0; + return TRUE; + } + return FALSE; + + default: + /* Do not process sub_type conversion on non-text field */ + return FALSE; + } + +} + +static FLD get_field( REL relation) +{ +/************************************** + * + * g e t _ f i e l d + * + ************************************** + * + * Functional description + * Reconstruct a local field. + * + **************************************/ + FLD field; + ATT_TYPE attribute; + USHORT n, global_tr = FALSE; + SLONG *rp; + UCHAR scan_next_attr; + TGBL tdgbl; + FLD f; + isc_tr_handle local_trans; + + tdgbl = GET_THREAD_DATA; + +/* If it is a view and there is a global transaction then use it */ + + if ((relation->rel_flags & REL_view) && (tdgbl->global_trans)) { + local_trans = tdgbl->global_trans; + global_tr = TRUE; + } + else + local_trans = gds__trans; + + field = (FLD) BURP_ALLOC_ZERO(sizeof(struct fld)); + + STORE (TRANSACTION_HANDLE local_trans + REQUEST_HANDLE tdgbl->handles_get_field_req_handle1) + X IN RDB$RELATION_FIELDS + strcpy (X.RDB$RELATION_NAME, relation->rel_name); + X.RDB$FIELD_POSITION = 0; + memset (X.RDB$QUERY_NAME, ' ', sizeof (X.RDB$QUERY_NAME)); + X.RDB$VIEW_CONTEXT.NULL = TRUE; + X.RDB$BASE_FIELD.NULL = TRUE; + X.RDB$SECURITY_CLASS.NULL = TRUE; + X.RDB$QUERY_NAME.NULL = TRUE; + X.RDB$QUERY_HEADER.NULL = TRUE; + X.RDB$EDIT_STRING.NULL = TRUE; + X.RDB$DESCRIPTION.NULL = TRUE; + X.RDB$FIELD_POSITION.NULL = TRUE; + X.RDB$SYSTEM_FLAG.NULL = TRUE; + X.RDB$COMPLEX_NAME.NULL = TRUE; + X.RDB$UPDATE_FLAG.NULL = TRUE; + X.RDB$DEFAULT_SOURCE.NULL = TRUE; + X.RDB$DEFAULT_VALUE.NULL = TRUE; + X.RDB$NULL_FLAG.NULL = TRUE; + X.RDB$COLLATION_ID.NULL = TRUE; + SKIP_INIT; + while (GET_ATTRIBUTE (attribute) != att_end) + switch (SKIP_SCAN, attribute) + { + case att_field_name: + field->fld_name_length = + get_text (field->fld_name, sizeof (field->fld_name)); + BURP_verbose (115, field->fld_name, NULL_PTR, NULL_PTR, NULL_PTR, NULL_PTR); + /* msg 115 restoring field %s */ + strcpy (X.RDB$FIELD_NAME, field->fld_name); + break; + + case att_field_source: + get_text (X.RDB$FIELD_SOURCE, sizeof (X.RDB$FIELD_SOURCE)); + break; + + case att_field_security_class: + get_text (X.RDB$SECURITY_CLASS, sizeof (X.RDB$SECURITY_CLASS)); + X.RDB$SECURITY_CLASS.NULL = FALSE; + break; + + case att_field_query_name: + get_text (X.RDB$QUERY_NAME, sizeof (X.RDB$QUERY_NAME)); + X.RDB$QUERY_NAME.NULL = FALSE; + break; + + case att_field_query_header: + X.RDB$QUERY_HEADER.NULL = FALSE; + get_source_blob (&X.RDB$QUERY_HEADER, global_tr); + break; + + case att_field_edit_string: + get_text (X.RDB$EDIT_STRING, sizeof (X.RDB$EDIT_STRING)); + X.RDB$EDIT_STRING.NULL = FALSE; + break; + + case att_field_position: + X.RDB$FIELD_POSITION.NULL = FALSE; + X.RDB$FIELD_POSITION = (USHORT) get_numeric(); + break; + + case att_field_number: + field->fld_number = (USHORT) get_numeric(); + break; + + case att_field_type: + field->fld_type = (USHORT) get_numeric(); + break; + + case att_field_length: + field->fld_length = (USHORT) get_numeric(); + break; + + case att_field_scale: + field->fld_scale = (USHORT) get_numeric(); + break; + + case att_field_sub_type: + field->fld_sub_type = (USHORT) get_numeric(); + break; + + case att_field_system_flag: + X.RDB$SYSTEM_FLAG = (USHORT) get_numeric(); + X.RDB$SYSTEM_FLAG.NULL = FALSE; + break; + + case att_view_context: + X.RDB$VIEW_CONTEXT = (USHORT) get_numeric(); + X.RDB$VIEW_CONTEXT.NULL = FALSE; + break; + + case att_field_computed_flag: + if (get_numeric()) + field->fld_flags |= FLD_computed; + break; + + case att_base_field: + get_text (X.RDB$BASE_FIELD, sizeof (X.RDB$BASE_FIELD)); + X.RDB$BASE_FIELD.NULL = FALSE; + break; + + case att_field_description: + X.RDB$DESCRIPTION.NULL = FALSE; + get_misc_blob (&X.RDB$DESCRIPTION, 1, global_tr); + break; + + case att_field_description2: + X.RDB$DESCRIPTION.NULL = FALSE; + get_source_blob (&X.RDB$DESCRIPTION, (UCHAR) global_tr); + break; + + case att_field_complex_name: + get_text (X.RDB$COMPLEX_NAME, sizeof (X.RDB$COMPLEX_NAME)); + X.RDB$COMPLEX_NAME.NULL = FALSE; + break; + + case att_field_dimensions: + field->fld_dimensions = (USHORT) get_numeric(); + field->fld_flags |= FLD_array; + for (rp = field->fld_ranges, n = field->fld_dimensions; n; rp+=2, n--) + { + if (GET_ATTRIBUTE (attribute) != att_field_range_low) + bad_attribute (scan_next_attr, attribute, 58); + /* msg 58 array */ + else + *rp = get_numeric(); + if (GET_ATTRIBUTE (attribute) != att_field_range_high) + bad_attribute (scan_next_attr, attribute, 58); + /* msg 58 array */ + else + *(rp+1) = get_numeric(); + } + break; + + case att_field_update_flag: + X.RDB$UPDATE_FLAG.NULL = FALSE; + X.RDB$UPDATE_FLAG = (USHORT) get_numeric(); + break; + + case att_field_character_length: + field->fld_character_length = (USHORT) get_numeric(); + break; + + case att_field_default_source: + X.RDB$DEFAULT_SOURCE.NULL = FALSE; + get_source_blob (&X.RDB$DEFAULT_SOURCE, global_tr); + break; + + case att_field_default_value: + X.RDB$DEFAULT_VALUE.NULL = FALSE; + get_blr_blob (&X.RDB$DEFAULT_VALUE, global_tr); + break; + + case att_field_null_flag: + X.RDB$NULL_FLAG.NULL = FALSE; + X.RDB$NULL_FLAG = (USHORT) get_numeric(); + break; + + case att_field_character_set: + field->fld_character_set_id = (USHORT) get_numeric(); + break; + + case att_field_collation_id: + field->fld_collation_id = (USHORT) get_numeric(); + X.RDB$COLLATION_ID.NULL = FALSE; + X.RDB$COLLATION_ID = field->fld_collation_id; + break; + + default: + bad_attribute (scan_next_attr, attribute, 84); + /* msg 84 field */ + break; + } + + /* For migration from V3.3 to V4.0 format of International text + * information - search the list of global fields which were + * remapped from V3.3 format into V4.0 format. If we find that + * this local field's source is one of those global fields, then + * remap the local field's information. This is used to compose + * the BLR which sends data to the newly restored database. + */ + for (f = tdgbl->v3_cvt_fld_list; f; f = f->fld_next) + { + if (!strcmp(X.RDB$FIELD_SOURCE, f->fld_name)) + { + field->fld_sub_type = f->fld_sub_type; + field->fld_scale = f->fld_scale; + field->fld_character_set_id = f->fld_character_set_id; + field->fld_collation_id = f->fld_collation_id; + break; + } + } + + END_STORE; + ON_ERROR + general_on_error (); + END_ERROR; + + return field; +} + + +static BOOLEAN get_field_dimensions(void) +{ +/************************************** + * + * g e t _ f i e l d _ d i m e n s i o n s + * + ************************************** + * + * Functional description + * Get array field dimensions in rdb$field_dimensions. + * + **************************************/ + ATT_TYPE attribute; + UCHAR scan_next_attr; + TGBL tdgbl; + + tdgbl = GET_THREAD_DATA; + + STORE (REQUEST_HANDLE tdgbl->handles_get_field_dimensions_req_handle1) + X IN RDB$FIELD_DIMENSIONS + SKIP_INIT; + while (SKIP_SCAN, GET_ATTRIBUTE (attribute) != att_end) + { + switch (attribute) + { + case att_field_name: + get_text (X.RDB$FIELD_NAME, sizeof (X.RDB$FIELD_NAME)); + break; + + case att_field_dimensions: + X.RDB$DIMENSION = (USHORT) get_numeric(); + break; + + case att_field_range_low: + X.RDB$LOWER_BOUND = get_numeric(); + break; + + case att_field_range_high: + X.RDB$UPPER_BOUND = get_numeric(); + break; + + default: + bad_attribute (scan_next_attr, attribute, 58); + /* msg 58 array */ + break; + } + } + END_STORE; + ON_ERROR + general_on_error (); + END_ERROR; + + return TRUE; +} + + +static BOOLEAN get_files(void) +{ +/************************************** + * + * g e t _ f i l e s + * + ************************************** + * + * Functional description + * Get any files that were stored; let + * somebody else worry about what to do with them. + * + **************************************/ + ATT_TYPE attribute; + UCHAR scan_next_attr; + TGBL tdgbl; + + tdgbl = GET_THREAD_DATA; + + STORE (REQUEST_HANDLE tdgbl->handles_get_files_req_handle1) + X IN RDB$FILES + X.RDB$FILE_FLAGS = 0; + SKIP_INIT; + while (SKIP_SCAN, GET_ATTRIBUTE (attribute) != att_end) + switch (attribute) + { + case att_file_filename: + get_text (X.RDB$FILE_NAME, sizeof (X.RDB$FILE_NAME)); + BURP_verbose (116, + /* msg 116 restoring file %s */ + X.RDB$FILE_NAME, NULL_PTR, NULL_PTR, NULL_PTR, NULL_PTR); + break; + + case att_file_sequence: + X.RDB$FILE_SEQUENCE = (USHORT) get_numeric(); + break; + + case att_file_start: + X.RDB$FILE_START = get_numeric(); + break; + + case att_file_length: + X.RDB$FILE_LENGTH = get_numeric(); + break; + + case att_file_flags: + X.RDB$FILE_FLAGS |= get_numeric(); + break; + + case att_shadow_number: + X.RDB$SHADOW_NUMBER = (USHORT) get_numeric(); + if (tdgbl->gbl_sw_kill && X.RDB$SHADOW_NUMBER) + X.RDB$FILE_FLAGS |= FILE_inactive; + break; + + default: + bad_attribute (scan_next_attr, attribute, 85); + /* msg 85 file */ + break; + } + END_STORE; + ON_ERROR + general_on_error (); + END_ERROR; + + return TRUE; +} + + +static BOOLEAN get_filter(void) +{ +/************************************** + * + * g e t _ f i l t e r + * + ************************************** + * + * Functional description + * Get a type definition in rdb$filters. + * + **************************************/ + ATT_TYPE attribute; + UCHAR scan_next_attr; + TGBL tdgbl; + + tdgbl = GET_THREAD_DATA; + + STORE (REQUEST_HANDLE tdgbl->handles_get_filter_req_handle1) + X IN RDB$FILTERS + X.RDB$DESCRIPTION.NULL = TRUE; + SKIP_INIT; + while (SKIP_SCAN, GET_ATTRIBUTE (attribute) != att_end) + { + switch (attribute) + { + case att_filter_name: + get_text (X.RDB$FUNCTION_NAME, sizeof (X.RDB$FUNCTION_NAME)); + BURP_verbose (117, X.RDB$FUNCTION_NAME, NULL_PTR, NULL_PTR, NULL_PTR, NULL_PTR); + /* msg 117 restoring filter %s */ + break; + + case att_filter_description: + X.RDB$DESCRIPTION.NULL = FALSE; + get_misc_blob (&X.RDB$DESCRIPTION, 1, 0); + break; + + case att_filter_description2: + X.RDB$DESCRIPTION.NULL = FALSE; + get_source_blob (&X.RDB$DESCRIPTION, 0); + break; + + case att_filter_module_name: + get_text (X.RDB$MODULE_NAME, sizeof (X.RDB$MODULE_NAME)); + break; + + case att_filter_entrypoint: + get_text (X.RDB$ENTRYPOINT, sizeof (X.RDB$ENTRYPOINT)); + break; + + case att_filter_input_sub_type: + X.RDB$INPUT_SUB_TYPE = (USHORT) get_numeric(); + break; + + case att_filter_output_sub_type: + X.RDB$OUTPUT_SUB_TYPE = (USHORT) get_numeric(); + break; + + default: + bad_attribute (scan_next_attr, attribute, 87); + /* msg 87 filter */ + break; + } + } + END_STORE; + ON_ERROR + general_on_error (); + END_ERROR; + + return TRUE; +} + + +static BOOLEAN get_function(void) +{ +/************************************** + * + * g e t _ f u n c t i o n + * + ************************************** + * + * Functional description + * Reconstruct a function. + * + **************************************/ + ATT_TYPE attribute; + GDS_NAME function_name; + TEXT temp[32]; + SSHORT l; + UCHAR scan_next_attr; + TGBL tdgbl; + + tdgbl = GET_THREAD_DATA; + + STORE (REQUEST_HANDLE tdgbl->handles_get_function_req_handle1) + X IN RDB$FUNCTIONS + SKIP_INIT; + while (SKIP_SCAN, GET_ATTRIBUTE (attribute) != att_end) + switch (attribute) + { + case att_function_name: + l = get_text (X.RDB$FUNCTION_NAME, sizeof (X.RDB$FUNCTION_NAME)); + MISC_terminate (X.RDB$FUNCTION_NAME, temp, l, sizeof (temp)); + BURP_verbose (118, temp, NULL_PTR, NULL_PTR, NULL_PTR, NULL_PTR); + /* msg 118 restoring function %s */ + break; + + case att_function_description: + get_misc_blob (&X.RDB$DESCRIPTION, 0, 0); + break; + + case att_function_description2: + get_source_blob (&X.RDB$DESCRIPTION, 0); + break; + + case att_function_module_name: + get_text (X.RDB$MODULE_NAME, sizeof (X.RDB$MODULE_NAME)); + break; + + case att_function_entrypoint: + get_text (X.RDB$ENTRYPOINT, sizeof (X.RDB$ENTRYPOINT)); + break; + + case att_function_return_arg: + X.RDB$RETURN_ARGUMENT = (USHORT) get_numeric(); + break; + + case att_function_query_name: + get_text (X.RDB$QUERY_NAME, sizeof (X.RDB$QUERY_NAME)); + break; + + case att_function_type: + X.RDB$FUNCTION_TYPE = (USHORT) get_numeric(); + break; + + default: + bad_attribute (scan_next_attr, attribute, 89); + /* msg 89 function */ + break; + } + strcpy (function_name, X.RDB$FUNCTION_NAME); + END_STORE; + ON_ERROR + general_on_error (); + END_ERROR; + +/* at the end of args for a function is the rec_function_end marker */ + while (GET() == rec_function_arg) + get_function_arg(function_name); + + return TRUE; +} + + +static void get_function_arg( GDS_NAME funcptr) +{ +/************************************** + * + * g e t _ f u n c t i o n _ a r g + * + ************************************** + * + * Functional description + * Reconstruct function argument. + * + **************************************/ + ATT_TYPE attribute; + SSHORT l; + TEXT temp[32]; + UCHAR scan_next_attr; + TGBL tdgbl; + + tdgbl = GET_THREAD_DATA; + + if (tdgbl->RESTORE_format >= 6) { + /* with RDB$FIELD_PRECISION */ + STORE (REQUEST_HANDLE tdgbl->handles_get_function_arg_req_handle1) + X IN RDB$FUNCTION_ARGUMENTS + X.RDB$FIELD_SUB_TYPE.NULL = TRUE; + X.RDB$CHARACTER_SET_ID.NULL = TRUE; + X.RDB$FIELD_PRECISION.NULL = TRUE; + + SKIP_INIT; + while (SKIP_SCAN, GET_ATTRIBUTE (attribute) != att_end) + switch (attribute) + { + case att_functionarg_name: + l = get_text (X.RDB$FUNCTION_NAME, sizeof (X.RDB$FUNCTION_NAME)); + MISC_terminate (X.RDB$FUNCTION_NAME, temp, l, sizeof (temp)); + BURP_verbose (119, temp, NULL_PTR, NULL_PTR, NULL_PTR, NULL_PTR); + /* msg 119 restoring argument for function %s */ + break; + + case att_functionarg_position: + X.RDB$ARGUMENT_POSITION = (USHORT) get_numeric(); + break; + + case att_functionarg_mechanism: + X.RDB$MECHANISM = (USHORT) get_numeric(); + break; + + case att_functionarg_field_type: + X.RDB$FIELD_TYPE = (USHORT) get_numeric(); + break; + + case att_functionarg_field_scale: + X.RDB$FIELD_SCALE = (USHORT) get_numeric(); + break; + + case att_functionarg_field_length: + X.RDB$FIELD_LENGTH = (USHORT) get_numeric(); + break; + + case att_functionarg_field_sub_type: + X.RDB$FIELD_SUB_TYPE.NULL = FALSE; + X.RDB$FIELD_SUB_TYPE = (USHORT) get_numeric(); + break; + + case att_functionarg_character_set: + X.RDB$CHARACTER_SET_ID.NULL = FALSE; + X.RDB$CHARACTER_SET_ID = (USHORT) get_numeric(); + break; + + case att_functionarg_field_precision: + X.RDB$FIELD_PRECISION.NULL = FALSE; + X.RDB$FIELD_PRECISION = (USHORT) get_numeric(); + break; + + default: + bad_attribute (scan_next_attr, attribute, 90); + /* msg 90 function argument */ + break; + } + END_STORE; + ON_ERROR + general_on_error (); + END_ERROR; + } + else { + /* without RDB$FIELD_PRECISION */ + STORE (REQUEST_HANDLE tdgbl->handles_get_function_arg_req_handle1) + X IN RDB$FUNCTION_ARGUMENTS + X.RDB$FIELD_SUB_TYPE.NULL = TRUE; + X.RDB$CHARACTER_SET_ID.NULL = TRUE; + + SKIP_INIT; + while (SKIP_SCAN, GET_ATTRIBUTE (attribute) != att_end) + switch (attribute) + { + case att_functionarg_name: + l = get_text (X.RDB$FUNCTION_NAME, sizeof (X.RDB$FUNCTION_NAME)); + MISC_terminate (X.RDB$FUNCTION_NAME, temp, l, sizeof (temp)); + BURP_verbose (119, temp, NULL_PTR, NULL_PTR, NULL_PTR, NULL_PTR); + /* msg 119 restoring argument for function %s */ + break; + + case att_functionarg_position: + X.RDB$ARGUMENT_POSITION = (USHORT) get_numeric(); + break; + + case att_functionarg_mechanism: + X.RDB$MECHANISM = (USHORT) get_numeric(); + break; + + case att_functionarg_field_type: + X.RDB$FIELD_TYPE = (USHORT) get_numeric(); + break; + + case att_functionarg_field_scale: + X.RDB$FIELD_SCALE = (USHORT) get_numeric(); + break; + + case att_functionarg_field_length: + X.RDB$FIELD_LENGTH = (USHORT) get_numeric(); + break; + + case att_functionarg_field_sub_type: + X.RDB$FIELD_SUB_TYPE.NULL = FALSE; + X.RDB$FIELD_SUB_TYPE = (USHORT) get_numeric(); + break; + + case att_functionarg_character_set: + X.RDB$CHARACTER_SET_ID.NULL = FALSE; + X.RDB$CHARACTER_SET_ID = (USHORT) get_numeric(); + break; + + default: + bad_attribute (scan_next_attr, attribute, 90); + /* msg 90 function argument */ + break; + } + END_STORE; + ON_ERROR + general_on_error (); + END_ERROR; + } +} + + +static BOOLEAN get_generator(void) +{ +/************************************** + * + * g e t _ g e n e r a t o r + * + ************************************** + * + * Functional description + * Pick up a gen-id. Like most things, there is ughly history. + * In the modern world, gen_id are free floating records. In the + * bad old days they were attributes of relations. Handle both + * nicely. + * + **************************************/ + SINT64 value; + TEXT name[32]; + ATT_TYPE attribute; + UCHAR scan_next_attr; + TGBL tdgbl; + + tdgbl = GET_THREAD_DATA; + + SKIP_INIT; + while (SKIP_SCAN, GET_ATTRIBUTE(attribute) != att_end) + switch (attribute) { + case att_gen_generator: + get_text(name, sizeof(name)); + break; + + case att_gen_value: + /* IB v5 or earlier, gen_id value is an SLONG */ + value = (SINT64) get_numeric(); + break; + + case att_gen_value_int64: + /* IB v6 or later, gen_id value is an SINT64 */ + value = get_int64(); + break; + + default: + bad_attribute(scan_next_attr, attribute, 93); + /* msg 93 index */ + break; + } + + store_blr_gen_id(name, value); + + return TRUE; +} + + +static BOOLEAN get_global_field(void) +{ +/************************************** + * + * g e t _ g l o b a l _ f i e l d + * + ************************************** + * + * Functional description + * Reconstruct a global field. + * + **************************************/ + ATT_TYPE attribute; + TEXT temp[32]; + SSHORT l; + GFLD gfld; + UCHAR scan_next_attr; + TGBL tdgbl; + SSHORT save_subtype; + + tdgbl = GET_THREAD_DATA; + + gfld = (GFLD) NULL_PTR; + + if (tdgbl->RESTORE_format >= 6) { + /* with rdb$field_precision */ + STORE (REQUEST_HANDLE tdgbl->handles_get_global_field_req_handle1) + X IN RDB$FIELDS + + X.RDB$FIELD_SCALE = X.RDB$SEGMENT_LENGTH = 0; + X.RDB$CHARACTER_SET_ID = X.RDB$COLLATION_ID = 0; + X.RDB$FIELD_SUB_TYPE = 0; + X.RDB$COMPUTED_BLR.NULL = TRUE; + X.RDB$COMPUTED_SOURCE.NULL = TRUE; + X.RDB$QUERY_NAME.NULL = TRUE; + X.RDB$EDIT_STRING.NULL = TRUE; + X.RDB$QUERY_HEADER.NULL = TRUE; + X.RDB$MISSING_VALUE.NULL = TRUE; + X.RDB$DEFAULT_VALUE.NULL = TRUE; + X.RDB$VALIDATION_BLR.NULL = TRUE; + X.RDB$VALIDATION_SOURCE.NULL = TRUE; + X.RDB$SYSTEM_FLAG.NULL = TRUE; + X.RDB$NULL_FLAG.NULL = TRUE; + X.RDB$DESCRIPTION.NULL = TRUE; + X.RDB$DIMENSIONS.NULL = TRUE; + X.RDB$EXTERNAL_LENGTH.NULL = TRUE; + X.RDB$EXTERNAL_TYPE.NULL = TRUE; + X.RDB$EXTERNAL_SCALE.NULL = TRUE; + X.RDB$SEGMENT_LENGTH.NULL = TRUE; + X.RDB$CHARACTER_LENGTH.NULL = TRUE; + X.RDB$MISSING_SOURCE.NULL = TRUE; + X.RDB$DEFAULT_SOURCE.NULL = TRUE; + X.RDB$FIELD_SUB_TYPE.NULL = TRUE; + X.RDB$CHARACTER_SET_ID.NULL = TRUE; + X.RDB$COLLATION_ID.NULL = TRUE; + X.RDB$FIELD_PRECISION.NULL = TRUE; + + memset (X.RDB$QUERY_NAME, ' ', sizeof (X.RDB$QUERY_NAME)); + SKIP_INIT; + while (SKIP_SCAN, GET_ATTRIBUTE (attribute) != att_end) + switch (attribute) + { + case att_field_name: + l = get_text (X.RDB$FIELD_NAME, sizeof (X.RDB$FIELD_NAME)); + MISC_terminate (X.RDB$FIELD_NAME, temp, l, sizeof (temp)); + BURP_verbose (121, temp, NULL_PTR, NULL_PTR, NULL_PTR, NULL_PTR); + /* msg 121 restoring global field %s */ + break; + + case att_field_query_name: + get_text (X.RDB$QUERY_NAME, sizeof (X.RDB$QUERY_NAME)); + X.RDB$QUERY_NAME.NULL = FALSE; + break; + + case att_field_edit_string: + get_text (X.RDB$EDIT_STRING, sizeof (X.RDB$EDIT_STRING)); + X.RDB$EDIT_STRING.NULL = FALSE; + break; + + case att_field_query_header: + X.RDB$QUERY_HEADER.NULL = FALSE; + get_source_blob (&X.RDB$QUERY_HEADER, 0); + break; + + case att_field_type: + X.RDB$FIELD_TYPE = (USHORT) get_numeric(); + break; + + case att_field_length: + X.RDB$FIELD_LENGTH = (USHORT) get_numeric(); + break; + + case att_field_scale: + X.RDB$FIELD_SCALE = (USHORT) get_numeric(); + X.RDB$FIELD_SCALE.NULL = FALSE; + break; + + case att_field_sub_type: + X.RDB$FIELD_SUB_TYPE = (USHORT) get_numeric(); + X.RDB$FIELD_SUB_TYPE.NULL = FALSE; + break; + + case att_field_segment_length: + X.RDB$SEGMENT_LENGTH = (USHORT) get_numeric(); + if (X.RDB$SEGMENT_LENGTH) + X.RDB$SEGMENT_LENGTH.NULL = FALSE; + break; + + case att_field_computed_blr: + X.RDB$COMPUTED_BLR.NULL = FALSE; + get_blr_blob (&X.RDB$COMPUTED_BLR, 0); + break; + + case att_field_computed_source: + X.RDB$COMPUTED_SOURCE.NULL = FALSE; + get_misc_blob (&X.RDB$COMPUTED_SOURCE, 1, 0); + break; + + case att_field_computed_source2: + X.RDB$COMPUTED_SOURCE.NULL = FALSE; + get_source_blob (&X.RDB$COMPUTED_SOURCE, 0); + break; + + case att_field_validation_blr: + if (tdgbl->gbl_sw_novalidity) + eat_blob(); + else + { + /* if we are going against a V4.0 database, + * restore the global fields in 2 phases. + */ + + if (tdgbl->global_trans) + { + if (!gfld) + gfld = (GFLD) BURP_ALLOC_ZERO (sizeof (struct gfld)); + + get_blr_blob (&gfld->gfld_vb, 1); + gfld->gfld_flags |= GFLD_validation_blr; + } + else + { + X.RDB$VALIDATION_BLR.NULL = FALSE; + get_blr_blob (&X.RDB$VALIDATION_BLR, 0); + } + } + break; + + case att_field_validation_source: + if (tdgbl->gbl_sw_novalidity) + eat_blob(); + else + { + /* if we are going against a V4.0 database, + * restore the global fields in 2 phases. + */ + + if (tdgbl->global_trans) + { + if (!gfld) + gfld = (GFLD) BURP_ALLOC_ZERO (sizeof (struct gfld)); + + get_misc_blob (&gfld->gfld_vs, 0, 1); + gfld->gfld_flags |= GFLD_validation_source; + } + else + { + X.RDB$VALIDATION_SOURCE.NULL = FALSE; + get_misc_blob (&X.RDB$VALIDATION_SOURCE, 0, 0); + } + } + break; + + case att_field_validation_source2: + if (tdgbl->gbl_sw_novalidity) + eat_blob(); + else + { + /* if we are going against a V4.0 database, + * restore the global fields in 2 phases. + */ + + if (tdgbl->global_trans) + { + if (!gfld) + gfld = (GFLD) BURP_ALLOC_ZERO (sizeof (struct gfld)); + + get_source_blob (&gfld->gfld_vs2, 1); + gfld->gfld_flags |= GFLD_validation_source2; + } + else + { + X.RDB$VALIDATION_SOURCE.NULL = FALSE; + get_source_blob (&X.RDB$VALIDATION_SOURCE, 0); + } + } + break; + + case att_field_missing_value: + X.RDB$MISSING_VALUE.NULL = FALSE; + get_blr_blob (&X.RDB$MISSING_VALUE, 0); + break; + + case att_field_default_value: + X.RDB$DEFAULT_VALUE.NULL = FALSE; + get_blr_blob (&X.RDB$DEFAULT_VALUE, 0); + break; + + case att_field_system_flag: + X.RDB$SYSTEM_FLAG = (USHORT) get_numeric(); + X.RDB$SYSTEM_FLAG.NULL = FALSE; + break; + + case att_field_null_flag: + X.RDB$NULL_FLAG = (USHORT) get_numeric(); + X.RDB$NULL_FLAG.NULL = FALSE; + break; + + case att_field_description: + X.RDB$DESCRIPTION.NULL = FALSE; + get_misc_blob (&X.RDB$DESCRIPTION, 1, 0); + break; + + case att_field_description2: + X.RDB$DESCRIPTION.NULL = FALSE; + get_source_blob (&X.RDB$DESCRIPTION, 0); + break; + + case att_field_external_length: + X.RDB$EXTERNAL_LENGTH.NULL = FALSE; + X.RDB$EXTERNAL_LENGTH = (USHORT) get_numeric(); + break; + + case att_field_external_scale: + X.RDB$EXTERNAL_SCALE.NULL = FALSE; + X.RDB$EXTERNAL_SCALE = (USHORT) get_numeric(); + break; + + case att_field_external_type: + X.RDB$EXTERNAL_TYPE.NULL = FALSE; + X.RDB$EXTERNAL_TYPE = (USHORT) get_numeric(); + break; + + case att_field_dimensions: + X.RDB$DIMENSIONS.NULL = FALSE; + X.RDB$DIMENSIONS = (USHORT) get_numeric(); + break; + + case att_field_character_length: + X.RDB$CHARACTER_LENGTH.NULL = FALSE; + X.RDB$CHARACTER_LENGTH = (USHORT) get_numeric(); + break; + + case att_field_default_source: + X.RDB$DEFAULT_SOURCE.NULL = FALSE; + get_source_blob (&X.RDB$DEFAULT_SOURCE, 0); + break; + + case att_field_missing_source: + X.RDB$MISSING_SOURCE.NULL = FALSE; + get_source_blob (&X.RDB$MISSING_SOURCE, 0); + break; + + case att_field_character_set: + X.RDB$CHARACTER_SET_ID.NULL = FALSE; + X.RDB$CHARACTER_SET_ID = (USHORT) get_numeric(); + break; + + case att_field_collation_id: + X.RDB$COLLATION_ID.NULL = FALSE; + X.RDB$COLLATION_ID = (USHORT) get_numeric(); + break; + + case att_field_precision: + X.RDB$FIELD_PRECISION.NULL = FALSE; + X.RDB$FIELD_PRECISION = (USHORT) get_numeric(); + break; + + default: + bad_attribute (scan_next_attr, attribute, 92); /* msg 92 global field */ + break; + } + if (X.RDB$FIELD_TYPE <= DTYPE_BLR_MAX) + { + l = gds_cvt_blr_dtype [X.RDB$FIELD_TYPE]; + if (l = type_lengths [l]) + X.RDB$FIELD_LENGTH = l; + } + + if (gfld) + strcpy (gfld->gfld_name, X.RDB$FIELD_NAME); + + /* V3.3 used a different method from V4.0 for encoding International + * text character set & collation information. Here we are + * looking at the metadata information, deciding if it is + * the V3.3 method, and converting it to the V4.0 method so + * V3.3 customers can migrate their database to V4.0. + */ + save_subtype = X.RDB$FIELD_SUB_TYPE; + if (X.RDB$CHARACTER_SET_ID.NULL && + X.RDB$COLLATION_ID.NULL && + cvt_v3_to_v4_intl (X.RDB$FIELD_TYPE, &X.RDB$FIELD_SCALE, + &X.RDB$FIELD_SUB_TYPE, &X.RDB$CHARACTER_SET_ID, + &X.RDB$COLLATION_ID)) + { + FLD f; + + /* If some value was reset, set the NULL flag */ + if (save_subtype && !X.RDB$FIELD_SUB_TYPE) + X.RDB$FIELD_SUB_TYPE.NULL = TRUE; + X.RDB$CHARACTER_SET_ID.NULL = FALSE; + X.RDB$COLLATION_ID.NULL = FALSE; + + /* Add an entry to the converted-field link list + * so we can also convert local fields based on this + * global field + */ + + f = (FLD) BURP_ALLOC_ZERO (sizeof(struct fld)); + strcpy (f->fld_name, X.RDB$FIELD_NAME); + f->fld_sub_type = X.RDB$FIELD_SUB_TYPE; + f->fld_scale = X.RDB$FIELD_SCALE; + f->fld_character_set_id = X.RDB$CHARACTER_SET_ID; + f->fld_collation_id = X.RDB$COLLATION_ID; + f->fld_next = tdgbl->v3_cvt_fld_list; + tdgbl->v3_cvt_fld_list = f; + } + + END_STORE; + ON_ERROR + general_on_error (); + END_ERROR; + + } + else { /* RESTORE_format < 6 */ + + /* without rdb$field_precision */ + + STORE (REQUEST_HANDLE tdgbl->handles_get_global_field_req_handle1) + X IN RDB$FIELDS + + X.RDB$FIELD_SCALE = X.RDB$SEGMENT_LENGTH = 0; + X.RDB$CHARACTER_SET_ID = X.RDB$COLLATION_ID = 0; + X.RDB$FIELD_SUB_TYPE = 0; + X.RDB$COMPUTED_BLR.NULL = TRUE; + X.RDB$COMPUTED_SOURCE.NULL = TRUE; + X.RDB$QUERY_NAME.NULL = TRUE; + X.RDB$EDIT_STRING.NULL = TRUE; + X.RDB$QUERY_HEADER.NULL = TRUE; + X.RDB$MISSING_VALUE.NULL = TRUE; + X.RDB$DEFAULT_VALUE.NULL = TRUE; + X.RDB$VALIDATION_BLR.NULL = TRUE; + X.RDB$VALIDATION_SOURCE.NULL = TRUE; + X.RDB$SYSTEM_FLAG.NULL = TRUE; + X.RDB$NULL_FLAG.NULL = TRUE; + X.RDB$DESCRIPTION.NULL = TRUE; + X.RDB$DIMENSIONS.NULL = TRUE; + X.RDB$EXTERNAL_LENGTH.NULL = TRUE; + X.RDB$EXTERNAL_TYPE.NULL = TRUE; + X.RDB$EXTERNAL_SCALE.NULL = TRUE; + X.RDB$SEGMENT_LENGTH.NULL = TRUE; + X.RDB$CHARACTER_LENGTH.NULL = TRUE; + X.RDB$MISSING_SOURCE.NULL = TRUE; + X.RDB$DEFAULT_SOURCE.NULL = TRUE; + X.RDB$FIELD_SUB_TYPE.NULL = TRUE; + X.RDB$CHARACTER_SET_ID.NULL = TRUE; + X.RDB$COLLATION_ID.NULL = TRUE; + + memset (X.RDB$QUERY_NAME, ' ', sizeof (X.RDB$QUERY_NAME)); + SKIP_INIT; + while (SKIP_SCAN, GET_ATTRIBUTE (attribute) != att_end) + switch (attribute) + { + case att_field_name: + l = get_text (X.RDB$FIELD_NAME, sizeof (X.RDB$FIELD_NAME)); + MISC_terminate (X.RDB$FIELD_NAME, temp, l, sizeof (temp)); + BURP_verbose (121, temp, NULL_PTR, NULL_PTR, NULL_PTR, NULL_PTR); + /* msg 121 restoring global field %s */ + break; + + case att_field_query_name: + get_text (X.RDB$QUERY_NAME, sizeof (X.RDB$QUERY_NAME)); + X.RDB$QUERY_NAME.NULL = FALSE; + break; + + case att_field_edit_string: + get_text (X.RDB$EDIT_STRING, sizeof (X.RDB$EDIT_STRING)); + X.RDB$EDIT_STRING.NULL = FALSE; + break; + + case att_field_query_header: + X.RDB$QUERY_HEADER.NULL = FALSE; + get_source_blob (&X.RDB$QUERY_HEADER, 0); + break; + + case att_field_type: + X.RDB$FIELD_TYPE = (USHORT) get_numeric(); + break; + + case att_field_length: + X.RDB$FIELD_LENGTH = (USHORT) get_numeric(); + break; + + case att_field_scale: + X.RDB$FIELD_SCALE = (USHORT) get_numeric(); + X.RDB$FIELD_SCALE.NULL = FALSE; + break; + + case att_field_sub_type: + X.RDB$FIELD_SUB_TYPE = (USHORT) get_numeric(); + X.RDB$FIELD_SUB_TYPE.NULL = FALSE; + break; + + case att_field_segment_length: + X.RDB$SEGMENT_LENGTH = (USHORT) get_numeric(); + if (X.RDB$SEGMENT_LENGTH) + X.RDB$SEGMENT_LENGTH.NULL = FALSE; + break; + + case att_field_computed_blr: + X.RDB$COMPUTED_BLR.NULL = FALSE; + get_blr_blob (&X.RDB$COMPUTED_BLR, 0); + break; + + case att_field_computed_source: + X.RDB$COMPUTED_SOURCE.NULL = FALSE; + get_misc_blob (&X.RDB$COMPUTED_SOURCE, 1, 0); + break; + + case att_field_computed_source2: + X.RDB$COMPUTED_SOURCE.NULL = FALSE; + get_source_blob (&X.RDB$COMPUTED_SOURCE, 0); + break; + + case att_field_validation_blr: + if (tdgbl->gbl_sw_novalidity) + eat_blob(); + else + { + /* if we are going against a V4.0 database, + * restore the global fields in 2 phases. + */ + + if (tdgbl->global_trans) + { + if (!gfld) + gfld = (GFLD) BURP_ALLOC_ZERO (sizeof (struct gfld)); + + get_blr_blob (&gfld->gfld_vb, 1); + gfld->gfld_flags |= GFLD_validation_blr; + } + else + { + X.RDB$VALIDATION_BLR.NULL = FALSE; + get_blr_blob (&X.RDB$VALIDATION_BLR, 0); + } + } + break; + + case att_field_validation_source: + if (tdgbl->gbl_sw_novalidity) + eat_blob(); + else + { + /* if we are going against a V4.0 database, + * restore the global fields in 2 phases. + */ + + if (tdgbl->global_trans) + { + if (!gfld) + gfld = (GFLD) BURP_ALLOC_ZERO (sizeof (struct gfld)); + + get_misc_blob (&gfld->gfld_vs, 0, 1); + gfld->gfld_flags |= GFLD_validation_source; + } + else + { + X.RDB$VALIDATION_SOURCE.NULL = FALSE; + get_misc_blob (&X.RDB$VALIDATION_SOURCE, 0, 0); + } + } + break; + + case att_field_validation_source2: + if (tdgbl->gbl_sw_novalidity) + eat_blob(); + else + { + /* if we are going against a V4.0 database, + * restore the global fields in 2 phases. + */ + + if (tdgbl->global_trans) + { + if (!gfld) + gfld = (GFLD) BURP_ALLOC_ZERO (sizeof (struct gfld)); + + get_source_blob (&gfld->gfld_vs2, 1); + gfld->gfld_flags |= GFLD_validation_source2; + } + else + { + X.RDB$VALIDATION_SOURCE.NULL = FALSE; + get_source_blob (&X.RDB$VALIDATION_SOURCE, 0); + } + } + break; + + case att_field_missing_value: + X.RDB$MISSING_VALUE.NULL = FALSE; + get_blr_blob (&X.RDB$MISSING_VALUE, 0); + break; + + case att_field_default_value: + X.RDB$DEFAULT_VALUE.NULL = FALSE; + get_blr_blob (&X.RDB$DEFAULT_VALUE, 0); + break; + + case att_field_system_flag: + X.RDB$SYSTEM_FLAG = (USHORT) get_numeric(); + X.RDB$SYSTEM_FLAG.NULL = FALSE; + break; + + case att_field_null_flag: + X.RDB$NULL_FLAG = (USHORT) get_numeric(); + X.RDB$NULL_FLAG.NULL = FALSE; + break; + + case att_field_description: + X.RDB$DESCRIPTION.NULL = FALSE; + get_misc_blob (&X.RDB$DESCRIPTION, 1, 0); + break; + + case att_field_description2: + X.RDB$DESCRIPTION.NULL = FALSE; + get_source_blob (&X.RDB$DESCRIPTION, 0); + break; + + case att_field_external_length: + X.RDB$EXTERNAL_LENGTH.NULL = FALSE; + X.RDB$EXTERNAL_LENGTH = (USHORT) get_numeric(); + break; + + case att_field_external_scale: + X.RDB$EXTERNAL_SCALE.NULL = FALSE; + X.RDB$EXTERNAL_SCALE = (USHORT) get_numeric(); + break; + + case att_field_external_type: + X.RDB$EXTERNAL_TYPE.NULL = FALSE; + X.RDB$EXTERNAL_TYPE = (USHORT) get_numeric(); + break; + + case att_field_dimensions: + X.RDB$DIMENSIONS.NULL = FALSE; + X.RDB$DIMENSIONS = (USHORT) get_numeric(); + break; + + case att_field_character_length: + X.RDB$CHARACTER_LENGTH.NULL = FALSE; + X.RDB$CHARACTER_LENGTH = (USHORT) get_numeric(); + break; + + case att_field_default_source: + X.RDB$DEFAULT_SOURCE.NULL = FALSE; + get_source_blob (&X.RDB$DEFAULT_SOURCE, 0); + break; + + case att_field_missing_source: + X.RDB$MISSING_SOURCE.NULL = FALSE; + get_source_blob (&X.RDB$MISSING_SOURCE, 0); + break; + + case att_field_character_set: + X.RDB$CHARACTER_SET_ID.NULL = FALSE; + X.RDB$CHARACTER_SET_ID = (USHORT) get_numeric(); + break; + + case att_field_collation_id: + X.RDB$COLLATION_ID.NULL = FALSE; + X.RDB$COLLATION_ID = (USHORT) get_numeric(); + break; + + default: + bad_attribute (scan_next_attr, attribute, 92); /* msg 92 global field */ + break; + } + if (X.RDB$FIELD_TYPE <= DTYPE_BLR_MAX) + { + l = gds_cvt_blr_dtype [X.RDB$FIELD_TYPE]; + if (l = type_lengths [l]) + X.RDB$FIELD_LENGTH = l; + } + + if (gfld) + strcpy (gfld->gfld_name, X.RDB$FIELD_NAME); + + /* V3.3 used a different method from V4.0 for encoding International + * text character set & collation information. Here we are + * looking at the metadata information, deciding if it is + * the V3.3 method, and converting it to the V4.0 method so + * V3.3 customers can migrate their database to V4.0. + */ + save_subtype = X.RDB$FIELD_SUB_TYPE; + if (X.RDB$CHARACTER_SET_ID.NULL && + X.RDB$COLLATION_ID.NULL && + cvt_v3_to_v4_intl (X.RDB$FIELD_TYPE, &X.RDB$FIELD_SCALE, + &X.RDB$FIELD_SUB_TYPE, &X.RDB$CHARACTER_SET_ID, + &X.RDB$COLLATION_ID)) + { + FLD f; + + /* If some value was reset, set the NULL flag */ + if (save_subtype && !X.RDB$FIELD_SUB_TYPE) + X.RDB$FIELD_SUB_TYPE.NULL = TRUE; + X.RDB$CHARACTER_SET_ID.NULL = FALSE; + X.RDB$COLLATION_ID.NULL = FALSE; + + /* Add an entry to the converted-field link list + * so we can also convert local fields based on this + * global field + */ + + f = (FLD) BURP_ALLOC_ZERO (sizeof(struct fld)); + strcpy (f->fld_name, X.RDB$FIELD_NAME); + f->fld_sub_type = X.RDB$FIELD_SUB_TYPE; + f->fld_scale = X.RDB$FIELD_SCALE; + f->fld_character_set_id = X.RDB$CHARACTER_SET_ID; + f->fld_collation_id = X.RDB$COLLATION_ID; + f->fld_next = tdgbl->v3_cvt_fld_list; + tdgbl->v3_cvt_fld_list = f; + } + + END_STORE; + ON_ERROR + general_on_error (); + END_ERROR; + + } + if (gfld) { + gfld->gfld_next = tdgbl->gbl_global_fields; + tdgbl->gbl_global_fields = gfld; + } + + return TRUE; +} + + +static BOOLEAN get_index( REL relation) +{ +/************************************** + * + * g e t _ i n d e x + * + ************************************** + * + * Functional description + * Build an index. At the end stop + * and check that all fields are defined. + * If any fields are missing, delete the + * index. + * + **************************************/ + SSHORT count, segments; + TEXT index_name[32]; + ATT_TYPE attribute; + BOOLEAN foreign_index = FALSE; + UCHAR scan_next_attr; + TGBL tdgbl; + + tdgbl = GET_THREAD_DATA; + + count = segments = 0; + + STORE (REQUEST_HANDLE tdgbl->handles_get_index_req_handle1) + X IN RDB$INDICES + strcpy (X.RDB$RELATION_NAME, relation->rel_name); + X.RDB$UNIQUE_FLAG = 0; + if (!tdgbl->gbl_sw_deactivate_indexes) + X.RDB$INDEX_INACTIVE = FALSE; + else + X.RDB$INDEX_INACTIVE = TRUE; + X.RDB$INDEX_TYPE.NULL = TRUE; + X.RDB$DESCRIPTION.NULL = TRUE; + X.RDB$FOREIGN_KEY.NULL = TRUE; + X.RDB$EXPRESSION_SOURCE.NULL = TRUE; + X.RDB$EXPRESSION_BLR.NULL = TRUE; + SKIP_INIT; + while (SKIP_SCAN, GET_ATTRIBUTE (attribute) != att_end) + switch (attribute) + { + case att_index_name: + (void) get_text (X.RDB$INDEX_NAME, sizeof (X.RDB$INDEX_NAME)); + strcpy (index_name, X.RDB$INDEX_NAME); + BURP_verbose (122, X.RDB$INDEX_NAME, NULL_PTR, NULL_PTR, NULL_PTR, NULL_PTR); + break; + + case att_segment_count: + X.RDB$SEGMENT_COUNT = segments = (USHORT) get_numeric(); + break; + + case att_index_unique_flag: + X.RDB$UNIQUE_FLAG = (USHORT) get_numeric(); + break; + + case att_index_inactive: + X.RDB$INDEX_INACTIVE = (USHORT) get_numeric(); + /* Defer foreign key index activation */ + /* Modified by Toni Martir, all index deferred when verbose */ + if (tdgbl->gbl_sw_verbose) + { + if (X.RDB$INDEX_INACTIVE == FALSE) + X.RDB$INDEX_INACTIVE = DEFERRED_ACTIVE; + } + else + { + if (X.RDB$INDEX_INACTIVE == FALSE && foreign_index) + X.RDB$INDEX_INACTIVE = DEFERRED_ACTIVE; + } + if (tdgbl->gbl_sw_deactivate_indexes) + X.RDB$INDEX_INACTIVE = TRUE; + break; + + case att_index_type: + X.RDB$INDEX_TYPE.NULL = FALSE; + X.RDB$INDEX_TYPE = (USHORT) get_numeric(); + break; + + case att_index_field_name: + STORE (REQUEST_HANDLE tdgbl->handles_get_index_req_handle2) + Y IN RDB$INDEX_SEGMENTS + get_text (Y.RDB$FIELD_NAME, sizeof (Y.RDB$FIELD_NAME)); + strcpy (Y.RDB$INDEX_NAME, X.RDB$INDEX_NAME); + Y.RDB$FIELD_POSITION = count++; + END_STORE; + ON_ERROR + general_on_error (); + END_ERROR; + break; + + case att_index_description: + X.RDB$DESCRIPTION.NULL = FALSE; + get_misc_blob (&X.RDB$DESCRIPTION, 0, 0); + break; + + case att_index_description2: + X.RDB$DESCRIPTION.NULL = FALSE; + get_source_blob (&X.RDB$DESCRIPTION, 0); + break; + + case att_index_expression_source: + X.RDB$EXPRESSION_SOURCE.NULL = FALSE; + get_source_blob (&X.RDB$EXPRESSION_SOURCE, 0); + break; + + case att_index_expression_blr: + X.RDB$EXPRESSION_BLR.NULL = FALSE; + get_blr_blob (&X.RDB$EXPRESSION_BLR, 0); + break; + + case att_index_foreign_key: + /* BUG 8183: For more information see description of */ + /* "bug_8183" function below. */ + /* Note: ATT_BACKUP_FORMAT for IB3.3 is equal 4 */ + if ( (tdgbl->RESTORE_format <= 4) && + (tdgbl->gbl_sw_bug8183 || bug_8183(tdgbl)) ) + { + X.RDB$DESCRIPTION.NULL = FALSE; + get_source_blob (&X.RDB$DESCRIPTION, 0); + } + else + { + foreign_index = TRUE; + /* Defer foreign key index activation */ + if (X.RDB$INDEX_INACTIVE == FALSE) + X.RDB$INDEX_INACTIVE = DEFERRED_ACTIVE; + if (tdgbl->gbl_sw_deactivate_indexes) + X.RDB$INDEX_INACTIVE = TRUE; + X.RDB$FOREIGN_KEY.NULL = FALSE; + get_text (X.RDB$FOREIGN_KEY, sizeof (X.RDB$FOREIGN_KEY)); + } + break; + + default: + bad_attribute (scan_next_attr, attribute, 93); + /* msg 93 index */ + break; + } + count = 0; + FOR (REQUEST_HANDLE tdgbl->handles_get_index_req_handle3) + RFR IN RDB$RELATION_FIELDS CROSS I_S IN RDB$INDEX_SEGMENTS + OVER RDB$FIELD_NAME WITH I_S.RDB$INDEX_NAME = index_name AND + RFR.RDB$RELATION_NAME = relation->rel_name + count++; + END_FOR; + ON_ERROR + general_on_error (); + END_ERROR; + + if (count != segments) + { + FOR (REQUEST_HANDLE tdgbl->handles_get_index_req_handle4) + I_S IN RDB$INDEX_SEGMENTS WITH I_S.RDB$INDEX_NAME = index_name + ERASE I_S; + ON_ERROR + general_on_error (); + END_ERROR; + END_FOR; + ON_ERROR + general_on_error (); + END_ERROR; + return FALSE; + } + END_STORE; + ON_ERROR + general_on_error (); + END_ERROR; + + return TRUE; +} + + +static void get_misc_blob( + ISC_QUAD * blob_id, + USHORT sub_type, USHORT glb_trans) +{ +/************************************** + * + * g e t _ m i s c _ b l o b + * + ************************************** + * + * Functional description + * Read blob attributes and copy data from input file to nice, + * shiney, new blob. + * + **************************************/ + STATUS status_vector[ISC_STATUS_LENGTH]; + SLONG *blob; + USHORT length; + UCHAR *buffer, static_buffer[1024]; + USHORT bpb_length; + UCHAR *bpb; + isc_tr_handle local_trans; + TGBL tdgbl; + + tdgbl = GET_THREAD_DATA; + + length = (USHORT) get_numeric(); + +/* Create new blob */ + + blob = NULL; + + bpb_length = 0; + bpb = NULL; + + if (glb_trans && tdgbl->global_trans) + local_trans = tdgbl->global_trans; + else + local_trans = gds__trans; + + if (isc_create_blob2(status_vector, + GDS_REF(tdgbl->db_handle), + GDS_REF(local_trans), + GDS_REF(blob), + GDS_VAL(blob_id), + bpb_length, + bpb)) BURP_error_redirect(status_vector, 37, 0, 0); + /* msg 37 gds__create_blob failed */ + +/* Allocate blob buffer is static buffer is too short */ + + if (!length || length <= sizeof(static_buffer)) + buffer = static_buffer; + else + buffer = BURP_ALLOC(length); + + if (length) { + (void) GET_BLOCK(buffer, length); + } + + if (isc_put_segment(status_vector, + GDS_REF(blob), + length, + GDS_VAL(buffer))) + BURP_error_redirect(status_vector, 38, 0, 0); + /* msg 38 gds__put_segment failed */ + + if (isc_close_blob(status_vector, GDS_REF(blob))) + BURP_error_redirect(status_vector, 23, 0, 0); + /* msg 23 gds__close_blob failed */ + + if (buffer != static_buffer) + BURP_FREE(buffer); +} + + +static SLONG get_numeric(void) +{ +/************************************** + * + * g e t _ n u m e r i c + * + ************************************** + * + * Functional description + * Get a numeric value from the input stream. + * + **************************************/ + SLONG value[2]; + SSHORT length; + + length = get_text((UCHAR *) value, sizeof(value)); + + return isc_vax_integer((UCHAR *) value, length); +} + + +static SINT64 get_int64(void) +{ +/************************************** + * + * g e t _ i n t 6 4 + * + ************************************** + * + * Functional description + * Get a possibly-64-bit numeric value from the input stream. + * + **************************************/ + SLONG value[4]; + SSHORT length; + + length = get_text((UCHAR *) value, sizeof(value)); + + return isc_portable_integer((UCHAR *) value, length); +} + + +static BOOLEAN get_procedure(void) +{ +/************************************** + * + * g e t _ p r o c e d u r e + * + ************************************** + * + * Functional description + * Reconstruct a stored procedure. + * Use the global_trans so we don't have to commit + * until after the indices are activated. This + * will allow us to use a PLAN in a SP. + * + **************************************/ + ATT_TYPE attribute; + GDS_NAME procedure_name; + TEXT temp[32]; + SSHORT l; + PRC procedure; + UCHAR scan_next_attr; + isc_tr_handle local_trans; + TGBL tdgbl; + + tdgbl = GET_THREAD_DATA; + + local_trans = tdgbl->global_trans ? tdgbl->global_trans : gds__trans; + + procedure = (PRC) BURP_ALLOC_ZERO(sizeof(struct prc)); + procedure->prc_next = tdgbl->procedures; + tdgbl->procedures = procedure; + + STORE (TRANSACTION_HANDLE local_trans + REQUEST_HANDLE tdgbl->handles_get_procedure_req_handle1) + X IN RDB$PROCEDURES + X.RDB$PROCEDURE_SOURCE.NULL = TRUE; + X.RDB$DESCRIPTION.NULL = TRUE; + X.RDB$SECURITY_CLASS.NULL = TRUE; + X.RDB$OWNER_NAME.NULL = TRUE; + SKIP_INIT; + while (SKIP_SCAN, GET_ATTRIBUTE (attribute) != att_end) + switch (attribute) + { + case att_procedure_name: + l = get_text (X.RDB$PROCEDURE_NAME, sizeof (X.RDB$PROCEDURE_NAME)); + procedure->prc_name_length = l; + strcpy (procedure->prc_name, X.RDB$PROCEDURE_NAME); + MISC_terminate (X.RDB$PROCEDURE_NAME, temp, l, sizeof (temp)); + BURP_verbose (195, temp, NULL_PTR, NULL_PTR, NULL_PTR, NULL_PTR); + /* msg 195 restoring stored procedure %s */ + break; + + case att_procedure_description: + get_misc_blob (&X.RDB$DESCRIPTION, 0, 1); + X.RDB$DESCRIPTION.NULL = FALSE; + break; + + case att_procedure_description2: + get_source_blob (&X.RDB$DESCRIPTION, 1); + X.RDB$DESCRIPTION.NULL = FALSE; + break; + + case att_procedure_source: + get_misc_blob (&X.RDB$PROCEDURE_SOURCE, 0, 1); + X.RDB$PROCEDURE_SOURCE.NULL = FALSE; + break; + + case att_procedure_source2: + get_source_blob (&X.RDB$PROCEDURE_SOURCE, 1); + X.RDB$PROCEDURE_SOURCE.NULL = FALSE; + break; + + case att_procedure_blr: + get_blr_blob (&X.RDB$PROCEDURE_BLR, 1); + break; + + case att_procedure_security_class: + get_text (X.RDB$SECURITY_CLASS, sizeof (X.RDB$SECURITY_CLASS)); + X.RDB$SECURITY_CLASS.NULL = FALSE; + break; + + case att_procedure_owner_name: + get_text (procedure->prc_owner, sizeof (procedure->prc_owner)); + break; + + case att_procedure_inputs: + X.RDB$PROCEDURE_INPUTS = (USHORT) get_numeric(); + if (X.RDB$PROCEDURE_INPUTS == 0) + X.RDB$PROCEDURE_INPUTS.NULL = TRUE; + else + X.RDB$PROCEDURE_INPUTS.NULL = FALSE; + break; + + case att_procedure_outputs: + X.RDB$PROCEDURE_OUTPUTS = (USHORT) get_numeric(); + break; + + default: + bad_attribute (scan_next_attr, attribute, 89); + /* msg 89 function */ + break; + } + strcpy (procedure_name, X.RDB$PROCEDURE_NAME); + END_STORE; + ON_ERROR + general_on_error (); + END_ERROR; + +/* at the end of prms for a procedure is the rec_procedure_end marker */ + + while (GET() == rec_procedure_prm) + get_procedure_prm(procedure_name); + + return TRUE; +} + + +static BOOLEAN get_procedure_prm( GDS_NAME procptr) +{ +/************************************** + * + * g e t _ p r o c e d u r e _ p r m + * + ************************************** + * + * Functional description + * Reconstruct stored procedure parameter. + * Use the global_trans so we don't have to commit + * until after the indices are activated. This + * will allow us to use a PLAN in a SP. + * + **************************************/ + ATT_TYPE attribute; + SSHORT l; + TEXT temp[32]; + UCHAR scan_next_attr; + isc_tr_handle local_trans; + TGBL tdgbl; + + tdgbl = GET_THREAD_DATA; + + local_trans = tdgbl->global_trans ? tdgbl->global_trans : gds__trans; + + STORE (TRANSACTION_HANDLE local_trans + REQUEST_HANDLE tdgbl->handles_get_procedure_prm_req_handle1) + X IN RDB$PROCEDURE_PARAMETERS + X.RDB$DESCRIPTION.NULL = TRUE; + strcpy (X.RDB$PROCEDURE_NAME, procptr); + SKIP_INIT; + while (SKIP_SCAN, GET_ATTRIBUTE (attribute) != att_end) + switch (attribute) + { + case att_procedureprm_name: + l = get_text (X.RDB$PARAMETER_NAME, sizeof (X.RDB$PARAMETER_NAME)); + MISC_terminate (X.RDB$PARAMETER_NAME, temp, l, sizeof (temp)); + BURP_verbose (196, temp, NULL_PTR, NULL_PTR, NULL_PTR, NULL_PTR); + /* msg 196 restoring parameter %s for stored procedure */ + break; + + case att_procedureprm_type: + X.RDB$PARAMETER_TYPE= (USHORT) get_numeric(); + break; + + case att_procedureprm_number: + X.RDB$PARAMETER_NUMBER= (USHORT) get_numeric(); + break; + + case att_procedureprm_field_source: + get_text (X.RDB$FIELD_SOURCE, sizeof (X.RDB$FIELD_SOURCE)); + break; + + case att_procedureprm_description: + get_misc_blob (&X.RDB$DESCRIPTION, 0, 1); + X.RDB$DESCRIPTION.NULL = FALSE; + break; + + case att_procedureprm_description2: + get_source_blob (&X.RDB$DESCRIPTION, 1); + X.RDB$DESCRIPTION.NULL = FALSE; + break; + + default: + bad_attribute (scan_next_attr, attribute, 90); + /* msg 90 function argument */ + break; + } + END_STORE; + ON_ERROR + general_on_error (); + END_ERROR; + + return TRUE; +} + + +static BOOLEAN get_ref_constraint(void) +{ +/************************************** + * + * g e t _ r e f _ c o n s t r a i n t + * + ************************************** + * + * Functional description + * Restore data for referential constraints. + * + **************************************/ + ATT_TYPE attribute; + UCHAR scan_next_attr; + TGBL tdgbl; + + tdgbl = GET_THREAD_DATA; + + STORE (REQUEST_HANDLE tdgbl->handles_get_ref_constraint_req_handle1) + X IN RDB$REF_CONSTRAINTS + X.RDB$CONSTRAINT_NAME.NULL = TRUE; + X.RDB$CONST_NAME_UQ.NULL = TRUE; + X.RDB$MATCH_OPTION.NULL = TRUE; + X.RDB$UPDATE_RULE.NULL = TRUE; + X.RDB$DELETE_RULE.NULL = TRUE; + SKIP_INIT; + while (SKIP_SCAN, GET_ATTRIBUTE (attribute) != att_end) + switch (attribute) + { + case att_ref_constraint_name: + X.RDB$CONSTRAINT_NAME.NULL = FALSE; + get_text (X.RDB$CONSTRAINT_NAME, sizeof (X.RDB$CONSTRAINT_NAME)); + break; + + case att_ref_unique_const_name: + X.RDB$CONST_NAME_UQ.NULL = FALSE; + get_text (X.RDB$CONST_NAME_UQ, sizeof (X.RDB$CONST_NAME_UQ)); + break; + + case att_ref_match_option: + X.RDB$MATCH_OPTION.NULL = FALSE; + get_text (X.RDB$MATCH_OPTION, sizeof (X.RDB$MATCH_OPTION)); + break; + + case att_ref_update_rule: + X.RDB$UPDATE_RULE.NULL = FALSE; + get_text (X.RDB$UPDATE_RULE, sizeof (X.RDB$UPDATE_RULE)); + break; + + case att_ref_delete_rule: + X.RDB$DELETE_RULE.NULL = FALSE; + get_text (X.RDB$DELETE_RULE, sizeof (X.RDB$DELETE_RULE)); + break; + + default: + bad_attribute (scan_next_attr, attribute, 208); + /* msg 208 relation constraint */ + break; + } + END_STORE; + ON_ERROR + general_on_error (); + END_ERROR; + + return TRUE; +} + + +static BOOLEAN get_relation(void) +{ +/************************************** + * + * g e t _ r e l a t i o n + * + ************************************** + * + * Functional description + * Write relation meta-data and data. + * Use the default transaction for RELATIONS, + * and use the global_trans for VIEWS. This + * enables us to have views of SP and views + * with plans. Assume it is a view if it has + * RDB$VIEW_BLR, also assume RDB$VIEW_BLR is + * the first blob in the backup file. + * + * + **************************************/ + REL relation; + FLD field, *ptr; + TEXT temp[32]; + SSHORT l; + ATT_TYPE attribute; + REC_TYPE record; + UCHAR scan_next_attr; + TGBL tdgbl; + + SLONG rel_flags, sys_flag; + short rel_flags_null, sys_flag_null; + GDS_QUAD view_blr, view_src, rel_desc, ext_desc; + USHORT view_blr_null, view_src_null, rel_desc_null, ext_desc_null; + char sec_class[32]; + short sec_class_null; + TEXT ext_file_name[253]; + short ext_file_name_null; + + isc_tr_handle local_trans; + tdgbl = GET_THREAD_DATA; + +/* Pick up relation attributes */ + + relation = (REL) BURP_ALLOC_ZERO(sizeof(struct rel)); + relation->rel_next = tdgbl->relations; + tdgbl->relations = relation; + + rel_flags_null = TRUE; + sys_flag_null = TRUE; + view_blr_null = TRUE; + view_src_null = TRUE; + rel_desc_null = TRUE; + ext_desc_null = TRUE; + sec_class_null = TRUE; + ext_file_name_null = TRUE; + + sec_class[0] = '\0'; + ext_file_name[0] = '\0'; + + view_blr = isc_blob_null; + view_src = isc_blob_null; + ext_desc = isc_blob_null; + rel_desc = isc_blob_null; + +/* + *STORE (REQUEST_HANDLE tdgbl->handles_get_relation_req_handle1) + X IN RDB$RELATIONS + X.RDB$SYSTEM_FLAG.NULL = TRUE; + X.RDB$FLAGS.NULL = TRUE; + X.RDB$SECURITY_CLASS.NULL = TRUE; + X.RDB$VIEW_BLR.NULL = TRUE; + X.RDB$VIEW_SOURCE.NULL = TRUE; + X.RDB$DESCRIPTION.NULL = TRUE; + X.RDB$RUNTIME.NULL = TRUE; + X.RDB$EXTERNAL_DESCRIPTION.NULL = TRUE; +*/ + + SKIP_INIT; + while (SKIP_SCAN, GET_ATTRIBUTE(attribute) != att_end) + switch (attribute) { + case att_relation_name: + l = get_text(relation->rel_name, 31); + relation->rel_name_length = l; + MISC_terminate(relation->rel_name, temp, l, sizeof(temp)); + BURP_verbose(167, temp, NULL_PTR, NULL_PTR, NULL_PTR, NULL_PTR); + /* msg 167 restoring relation %s */ + break; + + case att_relation_security_class: + sec_class_null = FALSE; + get_text(sec_class, sizeof(sec_class)); + break; + + case att_relation_view_blr: + view_blr_null = FALSE; + get_blr_blob(&view_blr, 1); + relation->rel_flags |= REL_view; + break; + + case att_relation_view_source: + view_src_null = FALSE; + get_misc_blob(&view_src, 1, (USHORT) ! view_blr_null); + break; + + case att_relation_view_source2: + view_src_null = FALSE; + get_source_blob(&view_src, (USHORT) ! view_blr_null); + break; + + case att_relation_description: + rel_desc_null = FALSE; + get_misc_blob(&rel_desc, 1, (USHORT) ! view_blr_null); + break; + + case att_relation_description2: + rel_desc_null = FALSE; + get_source_blob(&rel_desc, (USHORT) ! view_blr_null); + break; + + + case att_relation_flags: + rel_flags_null = FALSE; + rel_flags = get_numeric(); + break; + + case att_relation_system_flag: + sys_flag_null = FALSE; + sys_flag = get_numeric(); + break; + + case att_relation_ext_description: + ext_desc_null = FALSE; + get_misc_blob(&ext_desc, 1, (USHORT) ! view_blr_null); + break; + + case att_relation_ext_description2: + ext_desc_null = FALSE; + get_source_blob(&ext_desc, (USHORT) ! view_blr_null); + break; + + case att_relation_owner_name: + get_text(relation->rel_owner, sizeof(relation->rel_owner)); + break; + + case att_relation_ext_file_name: + ext_file_name_null = FALSE; + get_text(ext_file_name, sizeof(ext_file_name)); + break; + + default: + bad_attribute(scan_next_attr, attribute, 111); + /* msg 111 relation */ + break; + } + +/* If this is a view and there is a global transaction then use it */ + + if (view_blr_null || !tdgbl->global_trans) + local_trans = gds__trans; + else + local_trans = tdgbl->global_trans; + + STORE (TRANSACTION_HANDLE local_trans + REQUEST_HANDLE tdgbl->handles_get_relation_req_handle1) + X IN RDB$RELATIONS + + X.RDB$SYSTEM_FLAG.NULL = sys_flag_null; + X.RDB$FLAGS.NULL = rel_flags_null; + X.RDB$SECURITY_CLASS.NULL = sec_class_null; + X.RDB$VIEW_BLR.NULL = view_blr_null; + X.RDB$VIEW_SOURCE.NULL = view_src_null; + X.RDB$DESCRIPTION.NULL = rel_desc_null; + X.RDB$RUNTIME.NULL = TRUE; + X.RDB$EXTERNAL_DESCRIPTION.NULL = ext_desc_null; + X.RDB$EXTERNAL_FILE.NULL = ext_file_name_null; + + X.RDB$SYSTEM_FLAG = (USHORT) sys_flag; + X.RDB$FLAGS = (USHORT) rel_flags; + X.RDB$VIEW_BLR = view_blr; + X.RDB$VIEW_SOURCE = view_src; + X.RDB$DESCRIPTION = rel_desc; + X.RDB$EXTERNAL_DESCRIPTION = ext_desc; + + strcpy(X.RDB$SECURITY_CLASS, sec_class); + strcpy(X.RDB$RELATION_NAME, relation->rel_name); + strcpy(X.RDB$EXTERNAL_FILE, ext_file_name); + + END_STORE; + ON_ERROR + general_on_error (); + END_ERROR; + +/* Eat up misc. records */ + ptr = &relation->rel_fields; + + while (GET_RECORD(record) != rec_data) + switch (record) { + case rec_relation_end: + if (tdgbl->gbl_sw_incremental) { + BURP_verbose(170, relation->rel_name, NULL_PTR, NULL_PTR, + NULL_PTR, NULL_PTR); + /* msg 170: committing metadata for relation %s */ + COMMIT + /* existing ON_ERROR continues past error, beck */ + ON_ERROR + BURP_print (171, relation->rel_name, NULL_PTR, NULL_PTR, NULL_PTR, NULL_PTR); + /* msg 171: error committing metadata for relation %s */ + BURP_print_status (tdgbl->status_vector); + ROLLBACK; + ON_ERROR + general_on_error (); + END_ERROR; + END_ERROR; + EXEC SQL SET TRANSACTION NO_AUTO_UNDO; + if (gds__status[1]) + EXEC SQL SET TRANSACTION; + } + return TRUE; + + case rec_field: + *ptr = field = get_field(relation); + if (!field) + return FALSE; + ptr = &field->fld_next; + break; + + case rec_view: + get_view(relation); + break; + + default: +#ifdef SUPERSERVER + BURP_svc_error(43, isc_arg_number, (void *) record, + 0, NULL, 0, NULL, 0, NULL, 0, NULL); +#else + BURP_error(43, (void *) record, 0, 0, 0, 0); + /* msg 43 don't recognize record type %ld */ +#endif + break; + } + +/* If we fall thru, there are data records to be gotten */ +/* we can get here only when restoring ancient gbak'ed files where rec_data + was once embedded into rec_relation ... otherwise, meta commit happens + when we see the first rec_relation_data */ + + BURP_verbose(68, NULL_PTR, NULL_PTR, NULL_PTR, NULL_PTR, NULL_PTR); +/* msg 68 committing meta data */ + + COMMIT; + ON_ERROR + general_on_error (); + END_ERROR; + + EXEC SQL SET TRANSACTION NO_AUTO_UNDO; + if (gds__status[1]) + EXEC SQL SET TRANSACTION; + + get_data(relation); + + return TRUE; +} + + +static BOOLEAN get_rel_constraint(void) +{ +/************************************** + * + * g e t _ r e l _ c o n s t r a i n t + * + ************************************** + * + * Functional description + * Restore data for relation constraints. + * + **************************************/ + ATT_TYPE attribute; + UCHAR scan_next_attr; + TGBL tdgbl; + + tdgbl = GET_THREAD_DATA; + + STORE (REQUEST_HANDLE tdgbl->handles_get_rel_constraint_req_handle1) + X IN RDB$RELATION_CONSTRAINTS + X.RDB$CONSTRAINT_NAME.NULL = TRUE; + X.RDB$CONSTRAINT_TYPE.NULL = TRUE; + X.RDB$RELATION_NAME.NULL = TRUE; + X.RDB$DEFERRABLE.NULL = TRUE; + X.RDB$INITIALLY_DEFERRED.NULL = TRUE; + X.RDB$INDEX_NAME.NULL = TRUE; + SKIP_INIT; + while (SKIP_SCAN, GET_ATTRIBUTE (attribute) != att_end) + switch (attribute) + { + case att_rel_constraint_name: + X.RDB$CONSTRAINT_NAME.NULL = FALSE; + get_text (X.RDB$CONSTRAINT_NAME, sizeof (X.RDB$CONSTRAINT_NAME)); + break; + + case att_rel_constraint_type: + X.RDB$CONSTRAINT_TYPE.NULL = FALSE; + get_text (X.RDB$CONSTRAINT_TYPE, sizeof (X.RDB$CONSTRAINT_TYPE)); + break; + + case att_rel_constraint_rel_name: + X.RDB$RELATION_NAME.NULL = FALSE; + get_text (X.RDB$RELATION_NAME, sizeof (X.RDB$RELATION_NAME)); + break; + + case att_rel_constraint_defer: + X.RDB$DEFERRABLE.NULL = FALSE; + get_text (X.RDB$DEFERRABLE, sizeof (X.RDB$DEFERRABLE)); + break; + + case att_rel_constraint_init: + X.RDB$INITIALLY_DEFERRED.NULL = FALSE; + get_text (X.RDB$INITIALLY_DEFERRED, sizeof (X.RDB$INITIALLY_DEFERRED)); + break; + + case att_rel_constraint_index: + X.RDB$INDEX_NAME.NULL = FALSE; + get_text (X.RDB$INDEX_NAME, sizeof (X.RDB$INDEX_NAME)); + break; + + default: + bad_attribute (scan_next_attr, attribute, 208); + /* msg 208 relation constraint */ + break; + } + END_STORE; + ON_ERROR + general_on_error (); + END_ERROR; + + return TRUE; +} + + +static BOOLEAN get_relation_data(void) +{ +/************************************** + * + * g e t _ r e l a t i o n _ d a t a + * + ************************************** + * + * Functional description + * Restore data for a relation. This is called when the data is + * standing free from the relation definition. We first need to + * find the relation named. If we can't find it, give up. + * + **************************************/ + REL relation; + TEXT name[32]; + SLONG gen_id; + ATT_TYPE attribute; + REC_TYPE record; + UCHAR scan_next_attr; + TGBL tdgbl; + + tdgbl = GET_THREAD_DATA; + + relation = NULL; + + SKIP_INIT; + while (SKIP_SCAN, GET_ATTRIBUTE(attribute) != att_end) + switch (attribute) { + case att_relation_name: + get_text(name, sizeof(name)); + relation = find_relation(name); + break; + + default: + bad_attribute(scan_next_attr, attribute, 111); + /* msg 111 relation */ + break; + } + + if (!relation) + BURP_error_redirect(NULL_PTR, 49, 0, 0); +/* msg 49 no relation name for data */ + +/* Eat up misc. records */ + + GET_RECORD(record); + + SKIP_INIT; + while (SKIP_SCAN, TRUE) + switch (record) { + case rec_relation_end: + return TRUE; + + case rec_data: + record = get_data(relation); + /* get_data does a GET_RECORD */ + break; + + case rec_gen_id: + gen_id = get_numeric(); + store_blr_gen_id(name, gen_id); + GET_RECORD(record); + break; + + case rec_index: + get_index(relation); + GET_RECORD(record); + break; + + case rec_trigger: /* old style trigger */ + get_trigger_old(relation); + GET_RECORD(record); + break; + + default: + bad_attribute(scan_next_attr, attribute, 111); + /* msg 111 relation */ + GET_RECORD(record); + break; + } +} + + +static BOOLEAN get_sql_roles(void) +{ +/************************************** + * + * g e t _ s q l _ r o l e s + * + ************************************** + * + * Functional description + * Restore data for SQL roles + * + **************************************/ + ATT_TYPE attribute; + UCHAR scan_next_attr; + TGBL tdgbl; + TEXT temp[32]; + SSHORT l; + + tdgbl = GET_THREAD_DATA; + + STORE (REQUEST_HANDLE tdgbl->handles_get_sql_roles_req_handle1) + X IN RDB$ROLES + + X.RDB$ROLE_NAME.NULL = TRUE; + X.RDB$OWNER_NAME.NULL = TRUE; + SKIP_INIT; + + while (SKIP_SCAN, GET_ATTRIBUTE (attribute) != att_end) + switch (attribute) + { + case att_role_name: + X.RDB$ROLE_NAME.NULL = FALSE; + l = get_text (X.RDB$ROLE_NAME, sizeof (X.RDB$ROLE_NAME)); + MISC_terminate (X.RDB$ROLE_NAME, temp, l, sizeof (temp)); + /************************************************ + ** + ** msg 251, restoring SQL role: %s + ** + *************************************************/ + BURP_verbose (251, temp, NULL_PTR, NULL_PTR, + NULL_PTR, NULL_PTR); + break; + + case att_role_owner_name: + X.RDB$OWNER_NAME.NULL = FALSE; + get_text (X.RDB$OWNER_NAME, sizeof (X.RDB$OWNER_NAME)); + break; + + default: + /************************************************* + ** + ** msg 250, Bad attribute for restoring SQL role + ** + **************************************************/ + bad_attribute (scan_next_attr, attribute, 250); + break; + } + END_STORE + ON_ERROR + general_on_error (); + END_ERROR; + + return TRUE; +} + + +static BOOLEAN is_ascii_name(TEXT * name, SSHORT len) +{ +/************************************** + * + * i s _ a s c i i _ n a m e + * + ************************************** + * + * Functional description + * Check if the input text is valid ASCII name + * + **************************************/ + SSHORT i; + + for (i = 0; + i < len && + ((name[i] >= 'A' && name[i] <= 'Z') || + (name[i] >= '0' && name[i] <= '9') || + name[i] == '_' || name[i] == '$'); i++); + if (i != len) + return FALSE; + else + return TRUE; +} + +static BOOLEAN get_security_class(void) +{ +/************************************** + * + * g e t _ s e c u r i t y _ c l a s s + * + ************************************** + * + * Functional description + * Restore a security class record including access control list. + * + **************************************/ + ATT_TYPE attribute; + TEXT temp[32]; + SSHORT l = 0; + UCHAR scan_next_attr; + TGBL tdgbl; + BOOLEAN is_valid_sec_class = FALSE; + + tdgbl = GET_THREAD_DATA; + + STORE (REQUEST_HANDLE tdgbl->handles_get_security_class_req_handle1) + X IN RDB$SECURITY_CLASSES + SKIP_INIT; + while (SKIP_SCAN, GET_ATTRIBUTE (attribute) != att_end) + { + X.RDB$DESCRIPTION.NULL = TRUE; + switch (attribute) + { + case att_class_security_class: + l = get_text (X.RDB$SECURITY_CLASS, + sizeof (X.RDB$SECURITY_CLASS)); + + /* Bug fix for bug_no 7299: There was a V3 bug that inserted + garbage security class entry when doing GBAK. In order to + restore the V3 gbak file with this bad security entry to + V4 database. We should check if the security class is a + valid ASCII name. If not, skip this entry by setting + 'is_valid_sec_class' to FALSE. + */ + is_valid_sec_class = is_ascii_name(X.RDB$SECURITY_CLASS, l); + if (!is_valid_sec_class) + { + MISC_terminate (X.RDB$SECURITY_CLASS, temp, l, sizeof (temp)); + BURP_print (234, temp, NULL_PTR, NULL_PTR, NULL_PTR, NULL_PTR); + /* msg 234 Skipped bad security class entry: %s */ + break; + } + + MISC_terminate (X.RDB$SECURITY_CLASS, temp, l, sizeof (temp)); + BURP_verbose (125, temp, NULL_PTR, NULL_PTR, NULL_PTR, NULL_PTR); + /* msg 125 restoring security class %s */ + break; + + case att_class_acl: + get_misc_blob (&X.RDB$ACL, 0, 0); + break; + + case att_class_description: + X.RDB$DESCRIPTION.NULL = FALSE; + get_misc_blob (&X.RDB$DESCRIPTION, 0, 0); + break; + + case att_class_description2: + X.RDB$DESCRIPTION.NULL = FALSE; + get_source_blob (&X.RDB$DESCRIPTION, 0); + break; + + default: + bad_attribute (scan_next_attr, attribute, 131); + /* msg 131 security class */ + break; + } + } + /* If the security class is not valid ASCII name, don't store it to the */ + /* database. Simply return from here and the entry is discarded. */ + if (!is_valid_sec_class) + { + return TRUE; + } + END_STORE; + ON_ERROR + general_on_error (); + END_ERROR; + + return TRUE; +} + + +static void get_source_blob( ISC_QUAD * blob_id, USHORT glb_trans) +{ +/************************************** + * + * g e t _ s o u r c e _ b l o b + * + ************************************** + * + * Functional description + * Read source blob and query header attributes and copy data from + * input file to nice, shiney, new blob. + * + **************************************/ + STATUS status_vector[ISC_STATUS_LENGTH]; + SLONG length, *blob; + USHORT seg_len; + UCHAR *buffer, static_buffer[1024], *p; + isc_tr_handle local_trans; + TGBL tdgbl; + + tdgbl = GET_THREAD_DATA; + + length = get_numeric(); + +/* Create new blob */ + + blob = NULL; + + if (glb_trans && tdgbl->global_trans) + local_trans = tdgbl->global_trans; + else + local_trans = gds__trans; + + if (isc_create_blob(status_vector, + GDS_REF(tdgbl->db_handle), + GDS_REF(local_trans), + GDS_REF(blob), + GDS_VAL(blob_id))) + BURP_error_redirect(status_vector, 37, 0, 0); + /* msg 37 gds__create_blob failed */ + +/* Allocate blob buffer is static buffer is too short */ + + if (!length || length <= sizeof(static_buffer)) + buffer = static_buffer; + else + buffer = BURP_ALLOC(length); + + while (length) { + p = buffer; + while (*p++ = GET()) + length--; + --p; + --length; + seg_len = p - buffer; + + if (isc_put_segment(status_vector, + GDS_REF(blob), + seg_len, + GDS_VAL(buffer))) + BURP_error_redirect(status_vector, 38, 0, 0); + /* msg 38 gds__put_segment failed */ + } + + if (isc_close_blob(status_vector, GDS_REF(blob))) + BURP_error_redirect(status_vector, 23, 0, 0); + /* msg 23 gds__close_blob failed */ + + if (buffer != static_buffer) + BURP_FREE(buffer); +} + + +static USHORT get_text( TEXT * text, ULONG length) +{ +/************************************** + * + * g e t _ t e x t + * + ************************************** + * + * Functional description + * Move a text attribute to a string and fill. + * + **************************************/ + ULONG l, l2; + TGBL tdgbl; + + tdgbl = GET_THREAD_DATA; + + l2 = l = GET(); + + if (length < l) + BURP_error_redirect(NULL_PTR, 46, 0, 0); + /* msg 46 string truncated */ + + if (l) + text = GET_BLOCK(text, l); + + *text = 0; + + return (USHORT) l2; +} + + +static BOOLEAN get_trigger_old( REL relation) +{ +/************************************** + * + * g e t _ t r i g g e r _ o l d + * + ************************************** + * + * Functional description + * Get a trigger definition for a relation. + * + **************************************/ + enum trig_t type; + ATT_TYPE attribute; + TEXT *q, *p, *end, name[32]; + UCHAR scan_next_attr; + TGBL tdgbl; + + tdgbl = GET_THREAD_DATA; + + STORE (REQUEST_HANDLE tdgbl->handles_get_trigger_old_req_handle1) + X IN RDB$TRIGGERS + X.RDB$DESCRIPTION.NULL = TRUE; + X.RDB$TRIGGER_BLR.NULL = TRUE; + X.RDB$TRIGGER_SOURCE.NULL = TRUE; + SKIP_INIT; + while (SKIP_SCAN, GET_ATTRIBUTE (attribute) != att_end) + { + switch (attribute) + { + case att_trig_type: + type = (enum trig_t) get_numeric(); + break; + + case att_trig_blr: + X.RDB$TRIGGER_BLR.NULL = FALSE; + get_blr_blob (&X.RDB$TRIGGER_BLR, 0); + break; + + case att_trig_source: + X.RDB$TRIGGER_SOURCE.NULL = FALSE; + get_misc_blob (&X.RDB$TRIGGER_SOURCE, 1, 0); + break; + + case att_trig_source2: + X.RDB$TRIGGER_SOURCE.NULL = FALSE; + get_source_blob (&X.RDB$TRIGGER_SOURCE, 0); + break; + + default: + bad_attribute (scan_next_attr, attribute, 134); + /* msg 134 trigger */ + break; + } + } + + /* fill in rest of attributes unique to new trigger format */ + + p = X.RDB$TRIGGER_NAME; + end = p + 31; + q = relation->rel_name; + while (*q) + *p++ = *q++; + + if (type == trig_pre_store) + { + X.RDB$TRIGGER_TYPE = TRIG_TYPE_PRE_STORE; + q = "$STORE"; + } + else if (type == trig_pre_modify) + { + X.RDB$TRIGGER_TYPE = TRIG_TYPE_PRE_MODIFY; + q = "$MODIFY"; + } + else if (type == trig_post_erase) + { + X.RDB$TRIGGER_TYPE = TRIG_TYPE_POST_ERASE; + q = "$ERASE"; + } + else + { + bad_attribute (scan_next_attr, attribute, 136); + /* msg 136 trigger type */ + return 0; + } + + while (*q && p < end) + *p++ = *q++; + *p = 0; + BURP_verbose (126, X.RDB$TRIGGER_NAME, NULL_PTR, NULL_PTR, NULL_PTR, NULL_PTR); + /* msg 126 restoring trigger %s */ + strncpy (X.RDB$RELATION_NAME, relation->rel_name, GDS_NAME_LEN); + strcpy (name, X.RDB$TRIGGER_NAME); + X.RDB$TRIGGER_SEQUENCE = TRIGGER_SEQUENCE_DEFAULT; + X.RDB$SYSTEM_FLAG = 0; /* restore as vanilla user type */ + + END_STORE; + ON_ERROR + general_on_error (); + END_ERROR; + + if (tdgbl->gbl_sw_incremental) { + COMMIT + /* existing ON_ERROR continues past error, beck */ + ON_ERROR + BURP_print (94, name, NULL_PTR, NULL_PTR, NULL_PTR, NULL_PTR); + /* msg 94 trigger %s is invalid */ + BURP_print_status (tdgbl->status_vector); + ROLLBACK; + ON_ERROR + general_on_error (); + END_ERROR; + END_ERROR; + EXEC SQL SET TRANSACTION NO_AUTO_UNDO; + if (gds__status[1]) + EXEC SQL SET TRANSACTION; + } + + return TRUE; +} + + +static BOOLEAN get_trigger(void) +{ +/************************************** + * + * g e t _ t r i g g e r + * + ************************************** + * + * Functional description + * Get a trigger definition in rdb$triggers. + * + **************************************/ + ATT_TYPE attribute; + TEXT name[32]; + UCHAR scan_next_attr; + isc_tr_handle local_trans; + TGBL tdgbl; + + tdgbl = GET_THREAD_DATA; + + local_trans = tdgbl->global_trans ? tdgbl->global_trans : gds__trans; + + STORE (TRANSACTION_HANDLE local_trans + REQUEST_HANDLE tdgbl->handles_get_trigger_req_handle1) + X IN RDB$TRIGGERS + X.RDB$DESCRIPTION.NULL = TRUE; + X.RDB$TRIGGER_BLR.NULL = TRUE; + X.RDB$TRIGGER_SOURCE.NULL = TRUE; + X.RDB$SYSTEM_FLAG.NULL = TRUE; + X.RDB$FLAGS.NULL = TRUE; + SKIP_INIT; + while (SKIP_SCAN, GET_ATTRIBUTE (attribute) != att_end) + { + switch (attribute) + { + case att_trig_type: + X.RDB$TRIGGER_TYPE = (USHORT) get_numeric(); + break; + + case att_trig_flags: + X.RDB$FLAGS = (USHORT) get_numeric(); + X.RDB$FLAGS.NULL = FALSE; + break; + + case att_trig_blr: + X.RDB$TRIGGER_BLR.NULL = FALSE; + get_blr_blob (&X.RDB$TRIGGER_BLR, 1); + break; + + case att_trig_source: + X.RDB$TRIGGER_SOURCE.NULL = FALSE; + get_misc_blob (&X.RDB$TRIGGER_SOURCE, 1, 1); + break; + + case att_trig_source2: + X.RDB$TRIGGER_SOURCE.NULL = FALSE; + get_source_blob (&X.RDB$TRIGGER_SOURCE, 1); + break; + + case att_trig_name: + get_text (X.RDB$TRIGGER_NAME, sizeof (X.RDB$TRIGGER_NAME)); + strcpy (name, X.RDB$TRIGGER_NAME); + BURP_verbose (126, X.RDB$TRIGGER_NAME, NULL_PTR, NULL_PTR, NULL_PTR, NULL_PTR); + /* msg 126 restoring trigger %s */ + break; + + case att_trig_relation_name: + get_text (X.RDB$RELATION_NAME, sizeof (X.RDB$RELATION_NAME)); + break; + + case att_trig_sequence: + X.RDB$TRIGGER_SEQUENCE = (USHORT) get_numeric(); + break; + + case att_trig_description: + X.RDB$DESCRIPTION.NULL = FALSE; + get_misc_blob (&X.RDB$DESCRIPTION, 1, 1); + break; + + case att_trig_description2: + X.RDB$DESCRIPTION.NULL = FALSE; + get_source_blob (&X.RDB$DESCRIPTION, 1); + break; + + case att_trig_system_flag: + X.RDB$SYSTEM_FLAG = (USHORT) get_numeric(); + X.RDB$SYSTEM_FLAG.NULL = FALSE; + break; + + case att_trig_inactive: + X.RDB$TRIGGER_INACTIVE = (USHORT) get_numeric(); + break; + + default: + bad_attribute (scan_next_attr, attribute, 134); + /* msg 134 trigger */ + break; + } + } + END_STORE; + ON_ERROR + general_on_error (); + END_ERROR; + + if (tdgbl->gbl_sw_incremental) { + COMMIT + /* existing ON_ERROR continues past error, beck */ + ON_ERROR + BURP_print (94, name, NULL_PTR, NULL_PTR, NULL_PTR, NULL_PTR); + /* msg 94 trigger %s is invalid */ + BURP_print_status (tdgbl->status_vector); + ROLLBACK; + ON_ERROR + general_on_error (); + END_ERROR; + END_ERROR; + EXEC SQL SET TRANSACTION NO_AUTO_UNDO; + if (gds__status[1]) + EXEC SQL SET TRANSACTION; + } + + return TRUE; +} + + +static BOOLEAN get_trigger_message(void) +{ +/************************************** + * + * g e t _ t r i g g e r _ m e s s a g e + * + ************************************** + * + * Functional description + * Get a trigger message text. + * + **************************************/ + ATT_TYPE attribute; + SSHORT flag; + UCHAR scan_next_attr; + isc_tr_handle local_trans; + TGBL tdgbl; + + BASED_ON RDB$TRIGGER_MESSAGES.RDB$TRIGGER_NAME name; + BASED_ON RDB$TRIGGER_MESSAGES.RDB$MESSAGE_NUMBER number; + BASED_ON RDB$TRIGGER_MESSAGES.RDB$MESSAGE message; + + tdgbl = GET_THREAD_DATA; + + flag = FALSE; + SKIP_INIT; + while (SKIP_SCAN, GET_ATTRIBUTE(attribute) != att_end) { + switch (attribute) { + case att_trigmsg_name: + get_text(name, sizeof(name)); + flag = FALSE; + FOR (REQUEST_HANDLE tdgbl->handles_get_trigger_message_req_handle1) + FIRST 1 X IN RDB$TRIGGERS WITH + X.RDB$SYSTEM_FLAG EQ 1 AND X.RDB$TRIGGER_NAME EQ name + flag = TRUE; + END_FOR; + ON_ERROR + general_on_error (); + END_ERROR; + BURP_verbose(127, name, NULL_PTR, NULL_PTR, NULL_PTR, NULL_PTR); + /* msg 127 restoring trigger message for %s */ + break; + + case att_trigmsg_number: + number = (USHORT) get_numeric(); + break; + + case att_trigmsg_text: + get_text(message, sizeof(message)); + break; + + default: + bad_attribute(scan_next_attr, attribute, 135); + /* msg 135 trigger message */ + break; + } + } + + if (flag) + return TRUE; + + local_trans = tdgbl->global_trans ? tdgbl->global_trans : gds__trans; + + STORE (TRANSACTION_HANDLE local_trans + REQUEST_HANDLE tdgbl->handles_get_trigger_message_req_handle2) + X IN RDB$TRIGGER_MESSAGES + strcpy (X.RDB$TRIGGER_NAME, name); + X.RDB$MESSAGE_NUMBER = number; + strcpy (X.RDB$MESSAGE, message); + END_STORE; + ON_ERROR + general_on_error (); + END_ERROR; + + if (tdgbl->gbl_sw_incremental) { + COMMIT + /* existing ON_ERROR continues past error, beck */ + ON_ERROR + BURP_print (94, name, NULL_PTR, NULL_PTR, NULL_PTR, NULL_PTR); + /* msg 94 trigger %s is invalid */ + BURP_print_status (tdgbl->status_vector); + ROLLBACK; + ON_ERROR + general_on_error (); + END_ERROR; + END_ERROR; + EXEC SQL SET TRANSACTION NO_AUTO_UNDO; + if (gds__status[1]) + EXEC SQL SET TRANSACTION; + } + + return TRUE; +} + + +static BOOLEAN get_type(void) +{ +/************************************** + * + * g e t _ t y p e + * + ************************************** + * + * Functional description + * Get a type definition in rdb$types. + * + **************************************/ + ATT_TYPE attribute; + ULONG l; + TEXT temp[32]; + UCHAR scan_next_attr; + TGBL tdgbl; + + tdgbl = GET_THREAD_DATA; + + STORE (REQUEST_HANDLE tdgbl->handles_get_type_req_handle1) + X IN RDB$TYPES + X.RDB$DESCRIPTION.NULL = TRUE; + X.RDB$SYSTEM_FLAG.NULL = TRUE; + SKIP_INIT; + while (SKIP_SCAN, GET_ATTRIBUTE (attribute) != att_end) + { + switch (attribute) + { + case att_type_name: + l = get_text (X.RDB$TYPE_NAME, sizeof (X.RDB$TYPE_NAME)); + break; + + case att_type_type: + X.RDB$TYPE = (USHORT) get_numeric(); + break; + + case att_type_field_name: + get_text (X.RDB$FIELD_NAME, sizeof (X.RDB$FIELD_NAME)); + break; + + case att_type_description: + X.RDB$DESCRIPTION.NULL = FALSE; + get_misc_blob (&X.RDB$DESCRIPTION, 1, 0); + break; + + case att_type_description2: + X.RDB$DESCRIPTION.NULL = FALSE; + get_source_blob (&X.RDB$DESCRIPTION, 0); + break; + + case att_type_system_flag: + X.RDB$SYSTEM_FLAG = (USHORT) get_numeric(); + X.RDB$SYSTEM_FLAG.NULL = FALSE; + break; + + default: + bad_attribute (scan_next_attr, attribute, 136); + /* msg 136 trigger type */ + break; + } + } + + MISC_terminate (X.RDB$TYPE_NAME, temp, l, sizeof (temp)); + BURP_verbose (128, temp, X.RDB$FIELD_NAME, NULL_PTR, NULL_PTR, NULL_PTR); + /* msg 128 restoring type %s for field %s */ + + END_STORE; + ON_ERROR + general_on_error (); + END_ERROR; + + return TRUE; +} + + +static BOOLEAN get_user_privilege(void) +{ +/************************************** + * + * g e t _ u s e r _ p r i v i l e g e + * + ************************************** + * + * Functional description + * Get a user privilege. + * Get next interesting user privilege. + * + **************************************/ + ATT_TYPE attribute; + UCHAR scan_next_attr; + UCHAR exists = 0; + USHORT flags = 0; + TGBL tdgbl; + isc_tr_handle local_trans = NULL; + + BASED_ON RDB$USER_PRIVILEGES.RDB$USER user; + BASED_ON RDB$USER_PRIVILEGES.RDB$GRANTOR grantor; + BASED_ON RDB$USER_PRIVILEGES.RDB$PRIVILEGE privilege; + BASED_ON RDB$USER_PRIVILEGES.RDB$GRANT_OPTION grant_option; + BASED_ON RDB$USER_PRIVILEGES.RDB$RELATION_NAME relation_name; + BASED_ON RDB$USER_PRIVILEGES.RDB$FIELD_NAME field_name; + BASED_ON RDB$USER_PRIVILEGES.RDB$USER_TYPE user_type; + BASED_ON RDB$USER_PRIVILEGES.RDB$OBJECT_TYPE object_type; + + tdgbl = GET_THREAD_DATA; + user_type = obj_user; + object_type = obj_relation; + + SKIP_INIT; + while (SKIP_SCAN, GET_ATTRIBUTE(attribute) != att_end) { + switch (attribute) { + case att_priv_user: + /* default USER_TYPE to USER */ + flags |= USER_PRIV_USER; + get_text(user, sizeof(user)); + BURP_verbose(123, user, NULL_PTR, NULL_PTR, NULL_PTR, NULL_PTR); + /* msg 123 restoring privilege for user %s */ + break; + + case att_priv_grantor: + flags |= USER_PRIV_GRANTOR; + get_text(grantor, sizeof(grantor)); + break; + + case att_priv_privilege: + flags |= USER_PRIV_PRIVILEGE; + get_text(privilege, sizeof(privilege)); + break; + + case att_priv_grant_option: + flags |= USER_PRIV_GRANT_OPTION; + grant_option = (USHORT) get_numeric(); + break; + + case att_priv_object_name: + flags |= USER_PRIV_OBJECT_NAME; + /* default OBJECT_TYPE to RELATION */ + get_text(relation_name, sizeof(relation_name)); + break; + + case att_priv_field_name: + flags |= USER_PRIV_FIELD_NAME; + get_text(field_name, sizeof(field_name)); + break; + + case att_priv_user_type: + flags |= USER_PRIV_USER_TYPE; + user_type = (USHORT) get_numeric(); + break; + + case att_priv_obj_type: + flags |= USER_PRIV_OBJECT_TYPE; + object_type = (USHORT) get_numeric(); + break; + + default: + bad_attribute(scan_next_attr, attribute, 105); + /* msg 105 privilege */ + break; + } + } + +/* Check if object exists */ + + switch (object_type) { + case obj_procedure: + { + PRC proc; + + for (proc = tdgbl->procedures; proc; proc = proc->prc_next) + if (!strcmp(proc->prc_name, relation_name)) { + exists = 1; + local_trans = + tdgbl->global_trans ? tdgbl-> + global_trans : gds__trans; + break; + } + } + break; + + case obj_relation: + { + REL rel; + + for (rel = tdgbl->relations; rel; rel = rel->rel_next) + if (!strcmp(rel->rel_name, relation_name)) { + exists = 1; + if (rel->rel_flags & REL_view) + local_trans = + tdgbl->global_trans ? tdgbl-> + global_trans : gds__trans; + break; + } + } + break; + + default: + exists = 1; + break; + } + + if (exists) { + if (!local_trans) + local_trans = gds__trans; + + STORE (TRANSACTION_HANDLE local_trans + REQUEST_HANDLE tdgbl->handles_get_user_privilege_req_handle1) X + IN RDB$USER_PRIVILEGES + + X.RDB$FIELD_NAME.NULL = TRUE; + X.RDB$OBJECT_TYPE.NULL = TRUE; + + if (flags & USER_PRIV_USER) + strcpy (X.RDB$USER, user); + + if (flags & USER_PRIV_GRANTOR) + strcpy (X.RDB$GRANTOR, grantor); + + if (flags & USER_PRIV_PRIVILEGE) + strcpy (X.RDB$PRIVILEGE, privilege); + + if (flags & USER_PRIV_GRANT_OPTION) + { + X.RDB$GRANT_OPTION = grant_option; + if (grant_option == 0) + X.RDB$GRANT_OPTION.NULL = TRUE; + else + X.RDB$GRANT_OPTION.NULL = FALSE; + } + + if (flags & USER_PRIV_OBJECT_NAME) + strcpy (X.RDB$RELATION_NAME, relation_name); + + if (flags & USER_PRIV_FIELD_NAME) + { + X.RDB$FIELD_NAME.NULL = FALSE; + strcpy (X.RDB$FIELD_NAME, field_name); + } + + /* + * USER_TYPE & OBJECT_TYPE are fields that did not exist before + * V4.0. So, we have to reconstruct them and initialize them to + * reasonable values. If they existed before then user_type and + * object_type contain the proper values. If they didn't exist + * then user_type and object_type contain the reasonable default + * values. + */ + + X.RDB$USER_TYPE.NULL = FALSE; + X.RDB$USER_TYPE = user_type; + + X.RDB$OBJECT_TYPE.NULL = FALSE; + X.RDB$OBJECT_TYPE = object_type; + + + /* + * If OBJECT_TYPE didn't exist before and we have a field level + * user privileges, then use obj_field instead. + * + * NOTE: Scanning the V4.0 code base, obj_field has never been + * used at all. The following code should be uncommented + * in case we ever introduce obj_field to the picture. + */ + /*********************************************************** + if ( !(flags & USER_PRIV_OBJECT_TYPE) ) + { + if ( flags & USER_PRIV_FIELD_NAME ) + { + X.RDB$OBJECT_TYPE = obj_field; + } + } + ***********************************************************/ + + + END_STORE; + ON_ERROR + general_on_error (); + END_ERROR; + } + + return TRUE; +} + + +static BOOLEAN get_view( REL relation) +{ +/************************************** + * + * g e t _ v i e w + * + ************************************** + * + * Functional description + * Store a record in RDB$VIEW_RELATIONS. + * + **************************************/ + ATT_TYPE attribute; + UCHAR scan_next_attr; + isc_tr_handle local_trans; + TGBL tdgbl; + + tdgbl = GET_THREAD_DATA; + +/* If there is a global transaction then use it */ + + local_trans = tdgbl->global_trans ? tdgbl->global_trans : gds__trans; + + STORE (TRANSACTION_HANDLE local_trans + REQUEST_HANDLE tdgbl->handles_get_view_req_handle1) + X IN RDB$VIEW_RELATIONS + strcpy (X.RDB$VIEW_NAME, relation->rel_name); + SKIP_INIT; + while (SKIP_SCAN, GET_ATTRIBUTE (attribute) != att_end) + switch (attribute) + { + case att_view_relation_name: + get_text (X.RDB$RELATION_NAME, sizeof (X.RDB$RELATION_NAME)); + break; + + case att_view_context_name: + get_text (X.RDB$CONTEXT_NAME, sizeof (X.RDB$CONTEXT_NAME)); + break; + + case att_view_context_id: + X.RDB$VIEW_CONTEXT = (USHORT) get_numeric(); + break; + + default: + bad_attribute (scan_next_attr, attribute, 140); + /* msg 140 view */ + break; + } + END_STORE; + ON_ERROR + general_on_error (); + END_ERROR; + + return TRUE; +} + + +static void ignore_array( REL relation) +{ +/************************************** + * + * i g n o r e _ a r r a y + * + ************************************** + * + * Functional description + * Read array data from input file to nice, + * shiney, new array. + * + **************************************/ + FLD field; + ATT_TYPE attribute; + SLONG length, lcount, *range, *end_ranges; + USHORT field_number; + LSTRING xdr_buffer; + UCHAR scan_next_attr; + TGBL tdgbl; + + tdgbl = GET_THREAD_DATA; + +/* don't free something you don't allocate */ + xdr_buffer.lstr_allocated = 0; + +/* Pick up attributes */ + + SKIP_INIT; + while (SKIP_SCAN, GET_ATTRIBUTE(attribute) != att_blob_data) + switch (attribute) { + case att_blob_field_number: + field_number = (USHORT) get_numeric(); + for (field = relation->rel_fields; field; field = field->fld_next) + if (field->fld_number == field_number) + break; + if (!field) + BURP_error_redirect(NULL_PTR, 36, 0, 0); +/* msg 36 Can't find field for blob */ + break; + + case att_array_dimensions: + field->fld_dimensions = (SSHORT) get_numeric(); + end_ranges = field->fld_ranges + 2 * field->fld_dimensions; + for (range = field->fld_ranges; range < end_ranges; range += 2) { + if (GET_ATTRIBUTE(attribute) != att_array_range_low) + bad_attribute(scan_next_attr, attribute, 58); + /* msg 58 array */ + else + range[0] = get_numeric(); + if (GET_ATTRIBUTE(attribute) != att_array_range_high) + bad_attribute(scan_next_attr, attribute, 58); + /* msg 58 array */ + else + range[1] = get_numeric(); + } + break; + + default: + bad_attribute(scan_next_attr, attribute, 58); + /* msg 58 array */ + break; + } + + length = GET(); + length |= GET() << 8; + length |= GET() << 16; + length |= GET() << 24; + + if (tdgbl->gbl_sw_transportable) { + if (GET_ATTRIBUTE(attribute) != att_xdr_array) + BURP_error_redirect(NULL_PTR, 55, 0, 0); +/* msg 55 Expected XDR record length */ + else { + xdr_buffer.lstr_allocated = GET(); + xdr_buffer.lstr_allocated |= GET() << 8; + xdr_buffer.lstr_allocated |= GET() << 16; + xdr_buffer.lstr_allocated |= GET() << 24; + lcount = xdr_buffer.lstr_length = xdr_buffer.lstr_allocated; + } + } + else { + lcount = length; + } + + if (lcount) + GET_SKIP(lcount); +} + + +static void ignore_blob(void) +{ +/************************************** + * + * i g n o r e _ b l o b + * + ************************************** + * + * Functional description + * Skip over blob data records. + * + **************************************/ + ATT_TYPE attribute; + SLONG segments; + USHORT length; + UCHAR scan_next_attr; + TGBL tdgbl; + + tdgbl = GET_THREAD_DATA; + +/* Pick up attributes */ + + segments = 0; + + SKIP_INIT; + while (SKIP_SCAN, GET_ATTRIBUTE(attribute) != att_blob_data) + switch (attribute) { + case att_blob_field_number: + (void) get_numeric(); + break; + + case att_blob_max_segment: + (void) get_numeric(); + break; + + case att_blob_number_segments: + segments = get_numeric(); + break; + + case att_blob_type: + (void) get_numeric(); + break; + + default: + bad_attribute(scan_next_attr, attribute, 64); + /* msg 64 blob */ + break; + } + +/* Eat up blob segments */ + + while (--segments >= 0) { + length = GET(); + length |= GET() << 8; + if (length) + GET_SKIP(length); + } +} + + +static REC_TYPE ignore_data( REL relation) +{ +/************************************** + * + * i g n o r e _ d a t a + * + ************************************** + * + * Functional description + * Ignore data records for a relation. + * + **************************************/ + UCHAR *buffer; + USHORT l, records; + REC_TYPE record; + TGBL tdgbl; + + tdgbl = GET_THREAD_DATA; + + records = 0; + + while (TRUE) { + if (GET() != att_data_length) + BURP_error_redirect(NULL_PTR, 39, 0, 0); +/* msg 39 expected record length */ + l = (USHORT) get_numeric(); + if (tdgbl->gbl_sw_transportable) + if (GET() != att_xdr_length) + BURP_error_redirect(NULL_PTR, 55, 0, 0); +/* msg 55 Expected XDR record length */ + else + l = (USHORT) get_numeric(); + if (GET() != att_data_data) + BURP_error_redirect(NULL_PTR, 41, 0, 0); +/* msg 41 expected data attribute */ + if (l) + if (tdgbl->gbl_sw_compress) { + buffer = (UCHAR *) BURP_ALLOC(l); + decompress(buffer, l); + BURP_FREE(buffer); + } + else + GET_SKIP(l); + records++; + while (GET_RECORD(record)) { + if (record == rec_blob) + ignore_blob(); + else if (record == rec_array) + ignore_array(relation); + else + break; + } + if (record != rec_data) + break; + } + + BURP_verbose(106, (void *) records, NULL_PTR, NULL_PTR, NULL_PTR, + NULL_PTR); +/* msg 106 %ld records ignored */ + + return record; +} + + +static void realign( UCHAR * buffer, REL relation) +{ +/************************************** + * + * r e a l i g n + * + ************************************** + * + * Functional description + * Miserable input record is misaligned. + * Shuffle fields around. N.B. this one + * only works if the old buffer is longer + * than the new. + * + **************************************/ + FLD field; + UCHAR *p, *q; + USHORT l; + TGBL tdgbl; + + tdgbl = GET_THREAD_DATA; + + for (field = relation->rel_fields; field; field = field->fld_next) { + if (field->fld_flags & FLD_computed) + continue; + + p = buffer + field->fld_offset; + q = buffer + field->fld_old_offset; + l = field->fld_length; + + /* Beware of overlaps here - don't use memcpy */ + while (l--) + *p++ = *q++; + + if (field->fld_type == blr_varying) { + *p++ = *q++; + *p++ = *q++; + } + } + +/* If this is format version 2, build fields for null flags */ + + if (tdgbl->RESTORE_format >= 2) + for (field = relation->rel_fields; field; field = field->fld_next) { + if (field->fld_flags & FLD_computed) + continue; + p = buffer + FB_ALIGN(p - buffer, sizeof(SSHORT)); + q = buffer + FB_ALIGN(q - buffer, sizeof(SSHORT)); + *p++ = *q++; + *p++ = *q++; + } +} + + +static USHORT recompute_length( REL relation) +{ +/************************************** + * + * r e c o m p u t e _ l e n g t h + * + ************************************** + * + * Functional description + * Recompute length of a record using an old + * alignment if there is one. At the moment, + * only SPARC has one. + * + **************************************/ + +#ifdef sparc + FLD field; + ULONG offset, length, dtype, alignment; + SSHORT *alignments; + TGBL tdgbl; + + tdgbl = GET_THREAD_DATA; + + alignments = old_sparcs; + + for (field = relation->rel_fields; field; field = field->fld_next) { + if (field->fld_flags & FLD_computed) + continue; + length = field->fld_length; + alignment = 4; + + /* arrays are of various fld_types but are really blobs */ + + dtype = field->fld_type; + + if (field->fld_flags & FLD_array) { + dtype = blr_blob; + length = 8; + } + + alignment = alignments[gds_cvt_blr_dtype[field->fld_type]]; + if (dtype == blr_varying) + length += sizeof(USHORT); + + if (alignment) + offset = FB_ALIGN(offset, alignment); + field->fld_old_offset = offset; + offset += length; + } + +/* If this is format version 2, build fields for null flags */ + + if (tdgbl->RESTORE_format >= 2) + for (field = relation->rel_fields; field; field = field->fld_next) { + if (field->fld_flags & FLD_computed) + continue; + offset = FB_ALIGN(offset, sizeof(SSHORT)); + offset += sizeof(SSHORT); + } + + return offset; +#else + return FALSE; +#endif +} + + +static BOOLEAN restore( TEXT * file_name, TEXT * database_name) +{ +/************************************** + * + * r e s t o r e + * + ************************************** + * + * Functional description + * Perform the body of restore. + * + **************************************/ + SSHORT l, flag; + REC_TYPE record; + ATT_TYPE attribute; + USHORT db_version; + isc_req_handle req_handle1 = NULL, req_handle2 = NULL, req_handle3 = NULL, + req_handle4 = NULL, req_handle5 = NULL; + long req_status[20]; + TGBL tdgbl; + SSHORT flag_norel = TRUE; /* To fix bug 10098 */ + + tdgbl = GET_THREAD_DATA; + +/* Read burp record first */ + + MVOL_init_read(tdgbl->gbl_database_file_name, file_name, + &tdgbl->RESTORE_format, &tdgbl->io_cnt, &tdgbl->io_ptr); + + if (tdgbl->gbl_sw_transportable) + BURP_verbose(133, NULL_PTR, NULL_PTR, NULL_PTR, NULL_PTR, NULL_PTR); + /* msg 133 transportable backup -- data in XDR format */ + if (tdgbl->gbl_sw_compress) + BURP_verbose(61, NULL_PTR, NULL_PTR, NULL_PTR, NULL_PTR, NULL_PTR); + /* msg 61 backup file is compressed */ + + flag = FALSE; + +/* restore only from those backup files created by current or previous GBAK */ + + if (tdgbl->RESTORE_format < 1 + || tdgbl->RESTORE_format > ATT_BACKUP_FORMAT) +#ifdef SUPERSERVER + BURP_svc_error(44, isc_arg_number, tdgbl->RESTORE_format, + 0, NULL, 0, NULL, 0, NULL, 0, NULL); +#else + BURP_error(44, (void *) tdgbl->RESTORE_format, 0, 0, 0, 0); +/* msg 44 Expected backup version 1, 2, or 3. Found %ld */ +#endif + + create_database(database_name); + + EXEC SQL SET TRANSACTION NO_AUTO_UNDO; + if (gds__status[1]) + EXEC SQL SET TRANSACTION; + +/* For V4.0, start a read commited transaction. This will be used + * to create blobs for global fields and update the record in the + * RDB$FIELDS table. + */ + + EXEC SQL SET TRANSACTION NAME tdgbl-> + global_trans ISOLATION LEVEL READ COMMITTED; + + db_version = check_db_version(); + if (db_version < DB_VERSION_CURRENT) +#ifdef SUPERSERVER + BURP_svc_error(51, isc_arg_number, db_version, + 0, NULL, 0, NULL, 0, NULL, 0, NULL); +#else + BURP_error(51, (void *) db_version, 0, 0, 0, 0); +/* msg 51 database format %ld is too old to restore to */ +#endif + + BURP_verbose(129, NULL_PTR, NULL_PTR, NULL_PTR, NULL_PTR, NULL_PTR); +/* msg 129 started transaction */ + + while (GET_ATTRIBUTE(attribute) != att_end) + switch (attribute) { + case att_database_security_class: + /* Instead of updating the security class in RDB$DATABASE, + just store the value in tdgbl. It will be updated at + the very end to prevent security class validation + failures during change table ownership operation */ + get_text(tdgbl->database_security_class, + sizeof(tdgbl->database_security_class)); + break; + + case att_database_description: + case att_database_description2: + FOR (REQUEST_HANDLE req_handle2) + X IN RDB$DATABASE + MODIFY X USING + if (attribute == att_database_description2) + get_source_blob (&X.RDB$DESCRIPTION, 0); + else + get_misc_blob (&X.RDB$DESCRIPTION, 1, 0); + END_MODIFY; + ON_ERROR + general_on_error (); + END_ERROR; + END_FOR; + ON_ERROR + general_on_error (); + END_ERROR; + break; + + case att_database_dfl_charset: + FOR (REQUEST_HANDLE req_handle3) + X IN RDB$DATABASE + MODIFY X USING + get_text (X.RDB$CHARACTER_SET_NAME, sizeof (X.RDB$CHARACTER_SET_NAME)); + END_MODIFY; + ON_ERROR + general_on_error (); + END_ERROR; + END_FOR; + ON_ERROR + general_on_error (); + END_ERROR; + break; + + default: + l = GET(); + if (l) + GET_SKIP(l); + break; + } + if (req_handle1) + isc_release_request(req_status, &req_handle1); + if (req_handle2) + isc_release_request(req_status, &req_handle2); + if (req_handle3) + isc_release_request(req_status, &req_handle3); + +/* If this should be a multi-file database, add the files */ + + if (tdgbl->gbl_sw_files && tdgbl->gbl_sw_files->fil_next) + add_files(database_name); + +/* Get global fields and relations */ + + while (GET_RECORD(record) != rec_end) + switch (record) { + case rec_charset: + if (!get_character_set()) + return FALSE; + flag = TRUE; + break; + + case rec_collation: + if (!get_collation()) + return FALSE; + flag = TRUE; + break; + + case rec_chk_constraint: + if (!get_chk_constraint()) + return FALSE; + flag = TRUE; + break; + + case rec_global_field: + if (!get_global_field()) + return FALSE; + flag = TRUE; + break; + + case rec_field_dimensions: + if (!get_field_dimensions()) + return FALSE; + flag = TRUE; + break; + + case rec_relation: + if (!get_relation()) + return FALSE; + flag = TRUE; + flag_norel = FALSE; + break; + + case rec_ref_constraint: + if (!get_ref_constraint()) + return FALSE; + flag = TRUE; + break; + + case rec_rel_constraint: + if (!get_rel_constraint()) + return FALSE; + flag = TRUE; + break; + + case rec_function: + if (!get_function()) + return FALSE; + flag = TRUE; + break; + + case rec_procedure: + if (!get_procedure()) + return FALSE; + flag = TRUE; + break; + + case rec_exception: + if (!get_exception()) + return FALSE; + flag = TRUE; + break; + + case rec_type: /* rdb$types */ + if (!get_type()) + return FALSE; + flag = TRUE; + break; + + case rec_filter: /* rdb$filters */ + if (!get_filter()) + return FALSE; + flag = TRUE; + break; + + case rec_generator: + if (!get_generator()) + return FALSE; + flag = TRUE; + break; + + case rec_relation_data: + if (flag) { + BURP_verbose(68, NULL_PTR, NULL_PTR, NULL_PTR, NULL_PTR, + NULL_PTR); + /* msg 68 committing meta data */ + COMMIT; + ON_ERROR + general_on_error (); + END_ERROR; + EXEC SQL SET TRANSACTION NO_AUTO_UNDO; + if (gds__status[1]) + EXEC SQL SET TRANSACTION; + flag = FALSE; + } + if (!get_relation_data()) + return FALSE; + break; + + case rec_trigger: /* new trigger type */ + if (!get_trigger()) + return FALSE; + flag = TRUE; + break; + + case rec_trigger_message: + if (!get_trigger_message()) + return FALSE; + flag = TRUE; + break; + + case rec_user_privilege: + if (!get_user_privilege()) + return FALSE; + flag = TRUE; + break; + + case rec_security_class: + if (!get_security_class()) + return FALSE; + flag = TRUE; + break; + + case rec_files: + if (!get_files()) + return FALSE; + flag = TRUE; + break; + + case rec_sql_roles: + if (!get_sql_roles()) + return FALSE; + flag = TRUE; + break; + + default: +#ifdef SUPERSERVER + BURP_svc_error(43, isc_arg_number, (void *) record, + 0, NULL, 0, NULL, 0, NULL, 0, NULL); +#else + BURP_error(43, (void *) record, 0, 0, 0, 0); + /* msg 43 don't recognize record type %ld */ +#endif + break; + } + +/* This piece of code is to fix bug 10098: restore of database with +only domains and no relations aborts with the message ERROR: deadlock +This is because insertion of domains into RDB$FIELDS is happening in +the default transaction, whereas updation of RDB$FIELDS to add +constraints to the domains is done in tdgbl->global_trans. In case of +no relations, no COMMIT of default transaction occurs till this point +because of which rows in RDB$FIELDS for domains are still locked by +default transaction. The below code COMMITs the default transaction +in that particular situation */ + + if (flag_norel) { + COMMIT; + ON_ERROR + general_on_error (); + END_ERROR; + EXEC SQL SET TRANSACTION NO_AUTO_UNDO; + if (gds__status[1]) + EXEC SQL SET TRANSACTION; + } + +/* put validation clauses for global fields */ + + update_global_field(); + +/* Purge shadow metadata if necessary */ + + if (tdgbl->gbl_sw_kill) + FOR (REQUEST_HANDLE req_handle5) + FIL IN RDB$FILES WITH FIL.RDB$SHADOW_NUMBER NOT MISSING + AND FIL.RDB$SHADOW_NUMBER NE 0 + ERASE FIL; + ON_ERROR + general_on_error (); + END_ERROR; + END_FOR; + ON_ERROR + general_on_error (); + END_ERROR; + + if (req_handle5) + isc_release_request(req_status, &req_handle5); + + return TRUE; +} + + +static void restore_security_class( TEXT * owner_nm, TEXT * sec_class_nm) +{ +/************************************** + * + * r e s t o r e _ s e c u r i t y _ c l a s s + * + ************************************** + * + * Functional description + * restore the ownership of the relation in the ACL list + * + **************************************/ + isc_tr_handle local_trans; + isc_req_handle req_handle2 = NULL; + ISC_QUAD new_blob_id; + long req_status[20]; + TGBL tdgbl; + + tdgbl = GET_THREAD_DATA; + local_trans = gds__trans; + + FOR (REQUEST_HANDLE req_handle2) + X IN RDB$SECURITY_CLASSES WITH X.RDB$SECURITY_CLASS EQ sec_class_nm + + new_blob_id.gds_quad_high = 0; + new_blob_id.gds_quad_low = 0; + get_acl (owner_nm, &X.RDB$ACL, &new_blob_id); + + MODIFY X; + MOVE_FAST (&new_blob_id, &X.RDB$ACL, sizeof (ISC_QUAD)); + END_MODIFY; + ON_ERROR + if (req_handle2) + isc_release_request (req_status, &req_handle2); + general_on_error (); + END_ERROR; + + END_FOR; + + ON_ERROR + if (req_handle2) + isc_release_request (req_status, &req_handle2); + general_on_error (); + END_ERROR; + + if (req_handle2) + isc_release_request(req_status, &req_handle2); +} + + +static void store_blr_gen_id( GDS_NAME gen_name, SINT64 value) +{ +/************************************** + * + * s t o r e _ b l r _ g e n _ i d + * + ************************************** + * + * Functional description + * Store the blr_gen_id for the relation. + * + **************************************/ + UCHAR *blr; + UCHAR blr_buffer[100]; /* enough to fit blr */ + SSHORT blr_length; + SLONG *gen_id_reqh; + STATUS status_vector[ISC_STATUS_LENGTH]; + TGBL tdgbl; + + tdgbl = GET_THREAD_DATA; + + STORE (REQUEST_HANDLE tdgbl->handles_store_blr_gen_id_req_handle1) + X IN RDB$GENERATORS + strcpy (X.RDB$GENERATOR_NAME, gen_name); + END_STORE; + ON_ERROR + general_on_error (); + END_ERROR; + + if (!value) { + BURP_verbose(185, gen_name, (void *) value, NULL_PTR, NULL_PTR, + NULL_PTR); + /* msg 185 restoring generator %s value: %ld */ + return; + } + + + gen_id_reqh = NULL; + blr = blr_buffer; + +/* build the blr with the right relation name */ + + if (tdgbl->RESTORE_format >= 6) { + STUFF(blr_version5); + } + else { + STUFF(blr_version4); + } + STUFF(blr_begin); + if (tdgbl->RESTORE_format >= 6) { + STUFF(blr_dcl_variable); + STUFF_WORD(0); + STUFF(blr_int64); + STUFF(0); + } + else { + STUFF(blr_dcl_variable); + STUFF_WORD(0); + STUFF(blr_long); + STUFF(0); + } + STUFF(blr_begin); + STUFF(blr_assignment); + STUFF(blr_gen_id); + stuff_string(&blr, gen_name); + if (tdgbl->RESTORE_format >= 6) { + STUFF(blr_literal); + STUFF(blr_int64); + STUFF(0); + STUFF_INT64(value); + } + else { + STUFF(blr_literal); + STUFF(blr_long); + STUFF(0); + STUFF_LONG((SLONG) value); + } + STUFF(blr_variable); + STUFF_WORD(0); + STUFF(blr_end); + STUFF(blr_end); + STUFF(blr_eoc); + + blr_length = blr - blr_buffer; + + if (isc_compile_request(status_vector, + GDS_REF(tdgbl->db_handle), + GDS_REF(gen_id_reqh), + blr_length, GDS_VAL(blr_buffer))) { + isc_print_blr(blr_buffer, NULL_PTR, NULL_PTR, 0); + BURP_error_redirect(status_vector, 42, 0, 0); +/* msg 42 Failed in store_blr_gen_id */ + } + + if (isc_start_request(status_vector, GDS_REF(gen_id_reqh), GDS_REF(gds__trans), /* use the same one generated by gpre */ + 0)) { + isc_print_blr(blr_buffer, NULL_PTR, NULL_PTR, 0); + BURP_error_redirect(status_vector, 42, 0, 0); +/* msg 42 Failed in store_blr_gen_id */ + } + + BURP_verbose(185, gen_name, (void *) value, NULL_PTR, NULL_PTR, NULL_PTR); +/* msg 185 restoring generator %s value: %ld */ + + isc_release_request(status_vector, GDS_REF(gen_id_reqh)); +} + + +static void stuff_string( SCHAR ** ptr, TEXT * string) +{ +/************************************** + * + * s t u f f _ s t r i n g + * + ************************************** + * + * Functional description + * Stuff a name input a BLR string -- byte count first. + * + **************************************/ + SCHAR *blr; + + blr = *ptr; + STUFF(strlen(string)); + + while (*string) + STUFF(*string++); + + *ptr = blr; +} + + +static void update_global_field(void) +{ +/************************************** + * + * u p d a t e _ g l o b a l _ f i e l d + * + ************************************** + * + * Functional description + * Update the global field definition to add constraints. + * The blobs have been created already. + * + **************************************/ + GFLD n_gfld, gfld; + USHORT length; + UCHAR *p, *q; + isc_req_handle req_handle1 = NULL; + long req_status[20]; + TGBL tdgbl; + + tdgbl = GET_THREAD_DATA; + + for (gfld = tdgbl->gbl_global_fields; gfld;) { + FOR (TRANSACTION_HANDLE tdgbl->global_trans REQUEST_HANDLE req_handle1) + X IN RDB$FIELDS WITH X.RDB$FIELD_NAME EQ gfld->gfld_name + MODIFY X + + if (gfld->gfld_flags & GFLD_validation_blr) + { + X.RDB$VALIDATION_BLR.NULL = FALSE; + + if (length = sizeof (ISC_QUAD)) + { + p = (UCHAR *)&X.RDB$VALIDATION_BLR; + q = (UCHAR *)&gfld->gfld_vb; + + do *p++ = *q++; while (--length); + } + } + + if (gfld->gfld_flags & GFLD_validation_source) + { + X.RDB$VALIDATION_SOURCE.NULL = FALSE; + + if (length = sizeof (ISC_QUAD)) + { + p = (UCHAR *)&X.RDB$VALIDATION_SOURCE; + q = (UCHAR *)&gfld->gfld_vs; + + do *p++ = *q++; while (--length); + } + } + + if (gfld->gfld_flags & GFLD_validation_source2) + { + X.RDB$VALIDATION_SOURCE.NULL = FALSE; + + if (length = sizeof (ISC_QUAD)) + { + p = (UCHAR *)&X.RDB$VALIDATION_SOURCE; + q = (UCHAR *)&gfld->gfld_vs2; + + do *p++ = *q++; while (--length); + } + } + + END_MODIFY; + ON_ERROR + general_on_error (); + END_ERROR; + + END_FOR; + ON_ERROR + general_on_error (); + END_ERROR; + n_gfld = gfld->gfld_next; + BURP_FREE(gfld); + gfld = n_gfld; + } + if (req_handle1) + isc_release_request(req_status, &req_handle1); + + tdgbl->gbl_global_fields = (GFLD) 0; +} + + +static BOOLEAN bug_8183( TGBL tdgbl) +{ +/************************************** + * + * b u g _ 8 1 8 3 + * + ************************************** + * + * Name: bug_8183 + * + * Function: Bug fix for bug_no 8183: It is a migration bug between IB3.3 + * and IB4.0. Gbak v4.0 can't restore database v3.3 if + * database has an index definition with comment field. + * It happens because of att_index_description2 attribute + * (which indicates that index contains a comment field + * and is equal 8 under IB3.3) accidently got new value 9 + * under IB4.0. + * At the same time, new attribute att_index_foreign_key + * has been added with value 8. Thus, when gbak v4.0 faces + * index attribute with value 8 during restore procedure of + * database v3.3, it will recognize this attribute as + * att_index_foreign_key instead of att_index_description2. + * + * This function is trying to recognize the next + * data in tdgbl->io_ptr buffer as either name of foreign + * key or comment field. Function returns TRUE in case of + * comment field, otherwise FALSE. + * + * Usage: result = bug_8183(tdgbl); + * + * Parameters: tdgbl - pointer to the structure of global switches + * and data + * + * Returns: result [TRUE/FALSE] + * + * Pre: gbak got an index attribute with value "8" from + * backup database. + * + * Post: none + * + * Remarks: For more information see bug_no 8183 + * + **************************************/ + + UCHAR tmp[sizeof(ULONG) + 1], *p; + USHORT io_cnt, i; + UCHAR *io_ptr; + ULONG len1, len2; + BOOLEAN result = FALSE; + + + io_cnt = tdgbl->io_cnt; + io_ptr = tdgbl->io_ptr; + + if (io_cnt > 0) { + len1 = len2 = *io_ptr++; + --io_cnt; + /* len1 can be either length of att_index_foreign_key OR quantity of byte + where seats length of att_index_description2. In case of + att_index_description2, len1 should be as even and no bigger + than sizeof(ULONG). Let's check it out */ + if ((len1 % 2) == 0 && (len1 <= sizeof(ULONG))) { + /* it still can be a foreign key; so + try to read it. Note: if internal buffer is over, then + we wan't read next block */ + + memset(tmp, '\0', sizeof(tmp)); + + p = tmp; + while (len1-- && io_cnt--) + *p++ = *io_ptr++; + + /* if read array is a foreign key then it should contain following chars + only : ['A'..'Z', '0'..'9', '_', '$'] */ + for (p = tmp, i = 0; + *p && *p != ' ' && ((*p >= 'A' && *p <= 'Z') || (*p == '_') + || (*p >= '0' && *p <= '9') + || (*p == '$')); p++) + i++; + if ((len2 - len1) != i) + result = TRUE; + } + } + + return result; +} diff --git a/src/burp/spit.cpp b/src/burp/spit.cpp new file mode 100644 index 0000000000..519f808176 --- /dev/null +++ b/src/burp/spit.cpp @@ -0,0 +1,1431 @@ +/************************************************************************* +** +** PROGRAM: JRD file split utility program +** MODULE: spit.c +** DESCRIPTION: Command line interpreter for backup file split/join +** utility program +** +** +************************************************************************** + * The contents of this file are subject to the Interbase Public + * License Version 1.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy + * of the License at http://www.Inprise.com/IPL.html + * + * Software distributed under the License is distributed on an + * "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either express + * or implied. See the License for the specific language governing + * rights and limitations under the License. + * + * The Original Code was created by Inprise Corporation + * and its predecessors. Portions created by Inprise Corporation are + * Copyright (C) Inprise Corporation. + * + * All Rights Reserved. + * Contributor(s): ______________________________________. +*/ + +#include "../jrd/ib_stdio.h" +#include +#include +#include +#include +#include "../jrd/common.h" +#include "../jrd/jrd.h" +#include + +#include +#include +#include +#include "../burp/spit.h" +#include "../burp/burpswi.h" + +#ifdef WIN_NT +#include /* usage of non-ANSI open/read/write/close functions */ +#endif + +#define MODE_READ O_RDONLY + +#define MODE_WRITE O_WRONLY | O_CREAT +#define MASK 0666 + +/************************************* +** backup files header record +************************************** +*/ + +typedef struct header_rec { + TEXT name[18]; + TEXT date_time[30]; + TEXT text1[11]; + TEXT num[4]; + TEXT text2[4]; + TEXT total[4]; + TEXT text3[2]; + TEXT fl_name[27]; +} HEADER_REC; + +#define HEADER_REC_LEN sizeof (struct header_rec) +#define HEADER_REC_NAME "InterBase/gsplit, " + +/************************************* +** backup files structure +************************************** +*/ + +typedef struct b_fil { + struct b_fil *b_fil_next; + TEXT *b_fil_name; + SLONG b_fil_number; + double b_fil_size; +} *B_FIL; + +#define B_FIL_LEN sizeof (struct b_fil) + + + /***************************************************** + ** + ** local function declarations + ** + ****************************************************** + */ + +static int conv_ntoc(SLONG, TEXT *); + +static int free_file_list(B_FIL); + +static int final_flush_io_buff(UCHAR *, SLONG, FILE_DESC); + +static int final_read_and_write(FILE_DESC, FILE_DESC, + TEXT *, SLONG, UCHAR **, BOOLEAN *); + +static int flush_io_buff(UCHAR *, SLONG, + FILE_DESC, double, SLONG *, BOOLEAN *); + +static int get_file_name(SCHAR *, double, B_FIL *); + +static int get_file_size(SCHAR *, SCHAR *, double *); + +static int get_function_option(SCHAR *, USHORT *, SCHAR *, IN_SW_TAB); + +static int gen_multy_bakup_files(B_FIL, FILE_DESC, SLONG); + +static int set_hdr_str(TEXT *, TEXT *, SLONG, SLONG); + +static int join_multy_bakup_files(B_FIL); + +static int print_clo(TEXT *); + +static int read_and_write(FILE_DESC, FILE_DESC, + TEXT *, SLONG, + double, UCHAR **, BOOLEAN *, double *, SLONG *); + +static int read_and_write_for_join(FILE_DESC, TEXT *, + UCHAR **, SLONG, SLONG *); + +static int write_header(B_FIL, HEADER_REC, FILE_DESC, TEXT *); + + + + /***************************************************** + ** + ** M A I N P R O G R A M + ** + ****************************************************** + */ + +SLONG main( int argc, char *argv[]) +{ + + SCHAR **end, *prog_name, *string; + IN_SW_TAB in_sw_tab; + USHORT sw_replace; + B_FIL file_ptr, file_list, prev_file; + BOOLEAN file_nm_sw = FALSE; + SLONG ret_cd, file_num = 0; + double file_size; + FILE_DESC input_file_desc; + + prog_name = argv[0]; + + if (argc < 2) { + ib_fprintf(ib_stderr, "%s: No Command Line Option Specified\n", + argv[0]); + ret_cd = print_clo(prog_name); + return FAILURE; + } + +/************************ +** Fields initialization +************************* +*/ + + file_ptr = file_list = prev_file = NULL_B_FIL; + file_size = -1; + sw_replace = FALSE; + end = argv + argc; + ++argv; + +/******************************* + ** Initialize in_sw_table table. + ******************************* +*/ + + for (in_sw_tab = spit_in_sw_table; in_sw_tab->in_sw_name; in_sw_tab++) + in_sw_tab->in_sw_state = FALSE; + + /********************************** + ** validating command line options + *********************************** + */ + + while (argv < end) { + string = *argv; + if (*string == '-') { + argv++; + ret_cd = get_function_option(prog_name, &sw_replace, + string, spit_in_sw_table); + if (ret_cd == FAILURE) { + ret_cd = free_file_list(file_list); + return FAILURE; + } + } /* end of processing (*string == '-') */ + else { /* processing function specific command line options */ + + switch (sw_replace) { + case (IN_SW_SPIT_SP): + if (file_nm_sw == FALSE) { /* process file name */ + file_size = 0; + file_num = file_num + 1; + + if (file_num > MAX_NUM_OF_FILES) { + ib_fprintf(ib_stderr, + "%s: maximum of files is %d\n", + prog_name, MAX_NUM_OF_FILES); + ret_cd = print_clo(prog_name); + ret_cd = free_file_list(file_list); + return FAILURE; + } + + if (strlen(string) > MAX_FILE_NM_LEN) { + ib_fprintf(ib_stderr, + "%s: file name %s is too long\n", + prog_name, string); + ib_fprintf(ib_stderr, + "%s: maximum length of file name is %d bytes\n", + prog_name, MAX_FILE_NM_LEN); + ret_cd = print_clo(prog_name); + ret_cd = free_file_list(file_list); + return FAILURE; + } + + ret_cd = get_file_name(string, file_size, &file_ptr); + + if (ret_cd == FAILURE) { + ret_cd = free_file_list(file_list); + return FAILURE; + } + + file_nm_sw = TRUE; + file_ptr->b_fil_number = file_num; + + if (file_list == NULL_B_FIL) + file_list = prev_file = file_ptr; + else { + prev_file->b_fil_next = file_ptr; + prev_file = file_ptr; + } + + } /* processing file name */ + else { /* processing file size */ + + file_nm_sw = FALSE; + ret_cd = get_file_size(prog_name, string, &file_size); + if (ret_cd == FAILURE) { + ret_cd = free_file_list(file_list); + return FAILURE; + } + } /* end of processing file size specification */ + file_ptr->b_fil_size = file_size; + break; + + case (IN_SW_SPIT_JT): + ret_cd = get_file_name(string, file_size, &file_ptr); + + if (ret_cd == FAILURE) { + ret_cd = free_file_list(file_list); + return FAILURE; + } + + file_num = file_num + 1; + file_ptr->b_fil_number = file_num; + + if (file_list == NULL_B_FIL) + file_list = prev_file = file_ptr; + else { + prev_file->b_fil_next = file_ptr; + prev_file = file_ptr; + } /* end of processing file size specification */ + break; + + default: + ib_fprintf(ib_stderr, "%s: invalid option '%s'\n", + prog_name, string); + ret_cd = print_clo(prog_name); + ret_cd = free_file_list(file_list); + return FAILURE; + break; + } /* and of switch (sw_replace) */ + + argv++; + } /* processing function specific command line options */ + } /* while (argv < end) */ + + if ((file_list == NULL_B_FIL) && (sw_replace != FALSE)) { + ib_fprintf(ib_stderr, + "%s: invalid option '%s', rest of parameters is missing\n", + prog_name, string); + ret_cd = print_clo(prog_name); + ret_cd = free_file_list(file_list); + return FAILURE; + } + + switch (sw_replace) { + case (IN_SW_SPIT_SP): + input_file_desc = GBAK_STDIN_DESC; + ret_cd = gen_multy_bakup_files(file_list, input_file_desc, file_num); + if (ret_cd == FAILURE) { + ib_fprintf(ib_stderr, + "%s: progam fails to generate multi-volumn back-up files\n", + prog_name); + ret_cd = free_file_list(file_list); + return FAILURE; + } + break; + + case (IN_SW_SPIT_JT): + ret_cd = join_multy_bakup_files(file_list); + if (ret_cd == FAILURE) { + ib_fprintf(ib_stderr, + "%s: progam fails to join multi-volumn back-up files\n", + prog_name); + ret_cd = free_file_list(file_list); + return FAILURE; + } + break; + + default: + break; + } + +/******************************************************** +** +** free all the storage allocated for backup files +** +********************************************************* +*/ + + ret_cd = free_file_list(file_list); + return SUCCESS; + +} /* end of man() */ + + +static int get_function_option(SCHAR* prog_name, + USHORT* sw_replace, + SCHAR* string, + IN_SW_TAB in_sw_table) +{ +/******************************************************************** +** +** g e t _ f u n c t i o n _ o p t i o n +** +********************************************************************* +** +** Functional description: +** +** processing command line option and set proper function switch +** +********************************************************************* +*/ + + SCHAR c, *p, *q; + USHORT op_specified; + IN_SW_TAB in_sw_tab; + SLONG ret_cd; + + if (strlen(string) == 1) { + ib_fprintf(ib_stderr, "%s: invalid option '%s'\n", prog_name, string); + ret_cd = print_clo(prog_name); + return FAILURE; + } + + op_specified = *sw_replace; + + for (in_sw_tab = in_sw_table; q = in_sw_tab->in_sw_name; in_sw_tab++) { + for (p = string + 1; c = *p++;) + if (UPPER(c) != *q++) + break; + if (!c) { + if (*sw_replace == FALSE) { + *sw_replace = in_sw_tab->in_sw; + return SUCCESS; + } + else { + if (*sw_replace != in_sw_tab->in_sw) { + ib_fprintf(ib_stderr, + "%s: invalid option '%s', incompatible option\n", + prog_name, string); + ret_cd = print_clo(prog_name); + return FAILURE; + } + else /* compatible option */ + break; + } + } /* end of if (!c) */ + } /* end of for loop */ + + if (!in_sw_tab->in_sw) { + ib_fprintf(ib_stderr, "%s: invalid option '%s'\n", prog_name, string); + ret_cd = print_clo(prog_name); + return FAILURE; + } + + return SUCCESS; +} + + +static int get_file_size(SCHAR * prog_name, SCHAR * string, double *file_size) +{ +/******************************************************************** +** +** g e t _ f i l e _ s i z e +** +********************************************************************* +** +** Functional description: +** +** processing file size specification and returns calculated +** file size. +** +********************************************************************* +*/ + + SCHAR c, *p; + SLONG size_indicator; + SLONG ret_cd; + + size_indicator = 0; + + for (p = string; c = *p++;) { + if (c == '\0') + break; + + if (isdigit(c)) + *file_size = *file_size * 10 + (c - '0'); + else { + if ((isalpha(c)) && + (UPPER(c) == 'G' || UPPER(c) == 'K' || UPPER(c) == 'M')) { + switch (UPPER(c)) { + case ('K'): + size_indicator = K_BYTES; + break; + + case ('M'): + size_indicator = M_BYTES; + break; + + case ('G'): + size_indicator = G_BYTES; + break; + + default: + break; + } /* end of switch( UPPER (c) ) */ + + *file_size = *file_size * size_indicator; + } + else { /* invalid size indicator */ + + ib_fprintf(ib_stderr, + "%s: invalid size indicator '%s'\n", prog_name, + string); + ret_cd = print_clo(prog_name); + return FAILURE; + } + } + } + if (*file_size < MIN_FILE_SIZE) { /* handling user specifies file size 0 */ + ib_fprintf(ib_stderr, + "%s: invalid option '%s', minimum file size is 1 megabyte\n", + prog_name, string); + ret_cd = print_clo(prog_name); + return FAILURE; + } + + return SUCCESS; +} + + +static int get_file_name( SCHAR * string, double file_size, B_FIL * file_ptr) +{ +/******************************************************************** +** +** g e t _ f i l e _ n a m e +** +********************************************************************* +** +** Functional description: +** +** processing file name specification and returns file structure +** pointer. +** +********************************************************************* +*/ + + B_FIL temp_ptr; + + *file_ptr = temp_ptr = (B_FIL) malloc(B_FIL_LEN); + temp_ptr->b_fil_name = (TEXT *) malloc(strlen(string) + 1); + + temp_ptr->b_fil_next = NULL_B_FIL; + strcpy(temp_ptr->b_fil_name, string); + temp_ptr->b_fil_number = 0; + temp_ptr->b_fil_size = file_size; + + return SUCCESS; +} + + +static int gen_multy_bakup_files(B_FIL file_list, + FILE_DESC input_file_desc, SLONG file_num) +{ +/******************************************************************** +** +** g e n _ m u l t y _ b a c k u p _ f i l e s +** +********************************************************************* +** +** Functional description: +** +** processing input data from ib_stdin and splits the data into +** multiple back-up files. +** +** allocates an 16K bytes I/O buffer +** intilializes header record common fields +** do forever +** walk through the backup file chain +** intilializes header record unique fields +** open backup file +** writes out header record to backup file +** points to the next backup file in the chain +** calculates the actual file size ( minus header record length ) +** if the actual file size less than 16K bytes +** set I/O size to actual file size +** otherwise +** set I/O size to 16K byte long +** when it is the last backup file +** reads data from standard input as much as indicated by I/O size +** and writes it out to the last backup file until no EOF. +** issues error message when disk space full condition is detected +** otherwise reads and writes to backup files util EOF +** if disk full cobdition is detected +** flush the remaining data in the I/O buffer to subsequence +** backup files +** go back to normal read and write process util EOF +** +********************************************************************* +*/ + + FILE_DESC output_fl_desc; + SLONG byte_write, clock, indx, io_size, remaining_io_len, ret_cd, pos; + B_FIL fl_ptr = file_list; + BOOLEAN end_of_input = FALSE, flush_done = FALSE; + TEXT *file_name, header_str[HEADER_REC_LEN], num_arr[5]; + UCHAR *remaining_io; + HEADER_REC hdr_rec; + double byte_read, file_size; + UCHAR *io_buffer = (UCHAR *) malloc(IO_BUFFER_SIZE); + + + if (io_buffer == 0) { + ib_fprintf(ib_stderr, "I/O buffer allocation failed\n"); + return FAILURE; + } + + for (pos = 0; pos < HEADER_REC_LEN; pos++) + header_str[pos] = BLANK; + + pos = 0; + ret_cd = set_hdr_str(header_str, HEADER_REC_NAME, + pos, sizeof(hdr_rec.name)); + for (indx = 0; indx < sizeof(hdr_rec.name); indx++) + hdr_rec.name[indx] = BLANK; + + pos = pos + sizeof(hdr_rec.name); + clock = time(0); + ret_cd = set_hdr_str(header_str, ctime(&clock), + pos, sizeof(hdr_rec.date_time)); + for (indx = 0; indx < sizeof(hdr_rec.date_time); indx++) + hdr_rec.date_time[indx] = BLANK; + + pos = pos + sizeof(hdr_rec.date_time); + ret_cd = set_hdr_str(header_str, ", file No. ", + pos, sizeof(hdr_rec.text1)); + for (indx = 0; indx < sizeof(hdr_rec.text1); indx++) + hdr_rec.text1[indx] = BLANK; + + for (indx = 0; indx < sizeof(hdr_rec.num); indx++) + hdr_rec.num[indx] = BLANK; + + pos = pos + sizeof(hdr_rec.text1) + sizeof(hdr_rec.num); + ret_cd = set_hdr_str(header_str, " of ", pos, sizeof(hdr_rec.text2)); + for (indx = 0; indx < sizeof(hdr_rec.text2); indx++) + hdr_rec.text2[indx] = BLANK; + + ret_cd = conv_ntoc(file_num, num_arr); + if (ret_cd == FAILURE) { + free(io_buffer); + ib_fprintf(ib_stderr, + "gsplit could not convert numeric data to character data\n"); + return FAILURE; + } + num_arr[sizeof(num_arr) - 1] = TERMINAL; + pos = pos + sizeof(hdr_rec.text2); + ret_cd = set_hdr_str(header_str, num_arr, pos, sizeof(hdr_rec.total)); + for (indx = 0; indx < sizeof(hdr_rec.total); indx++) + hdr_rec.total[indx] = BLANK; + + pos = pos + sizeof(hdr_rec.total); + ret_cd = set_hdr_str(header_str, ", ", pos, sizeof(hdr_rec.text3)); + for (indx = 0; indx < sizeof(hdr_rec.text3); indx++) + hdr_rec.text3[indx] = BLANK; + + for (indx = 0; indx < sizeof(hdr_rec.fl_name); indx++) + hdr_rec.fl_name[indx] = BLANK; + + while (TRUE) { + if (fl_ptr != NULL_B_FIL) { + byte_read = 0; + byte_write = 0; + if ((fl_ptr->b_fil_next == NULL_B_FIL) + && (fl_ptr->b_fil_size == 0)) fl_ptr->b_fil_size = + MIN_FILE_SIZE; + + file_size = fl_ptr->b_fil_size - HEADER_REC_LEN; + file_name = fl_ptr->b_fil_name; + + output_fl_desc = open(file_name, MODE_WRITE, MASK); + if (output_fl_desc == -1) { + free(io_buffer); + ib_fprintf(ib_stderr, "can not open back up file %s\n", + file_name); + return FAILURE; + } + + ret_cd = + write_header(fl_ptr, hdr_rec, output_fl_desc, header_str); + if (ret_cd == FAILURE) { + free(io_buffer); + ib_fprintf(ib_stderr, + "could not write header record to file %s\n", + file_name); + return FAILURE; + } + + fl_ptr = fl_ptr->b_fil_next; + } + + if (file_size < IO_BUFFER_SIZE) + io_size = (SLONG) file_size; + else + io_size = IO_BUFFER_SIZE; + + if (fl_ptr == NULL_B_FIL) { + while (end_of_input == FALSE) { + ret_cd = final_read_and_write(input_file_desc, output_fl_desc, + file_name, io_size, &io_buffer, + &end_of_input); + if (ret_cd == FAILURE) { + free(io_buffer); + return FAILURE; + } + + if (end_of_input == TRUE) { + free(io_buffer); + return SUCCESS; + } + } + } + else { + while ((file_size > byte_read) && (fl_ptr != NULL_B_FIL)) { + ret_cd = read_and_write(input_file_desc, output_fl_desc, + file_name, io_size, file_size, + &io_buffer, &end_of_input, + &byte_read, &byte_write); + switch (ret_cd) { + case (FAILURE): + { + free(io_buffer); + return FAILURE; + break; + } + + case (FILE_IS_FULL): + { + byte_read = 0; /* reset byte read count, + ** prepare for next read + */ + remaining_io = io_buffer + byte_write; + remaining_io_len = IO_BUFFER_SIZE - byte_write; + + while ((flush_done == FALSE) + && (fl_ptr != NULL_B_FIL)) { + if ((fl_ptr->b_fil_next == NULL_B_FIL) + && (fl_ptr->b_fil_size == 0)) + fl_ptr->b_fil_size = MIN_FILE_SIZE; + + file_size = fl_ptr->b_fil_size - HEADER_REC_LEN; + file_name = fl_ptr->b_fil_name; + + output_fl_desc = + open(file_name, MODE_WRITE, MASK); + if (output_fl_desc == -1) { + free(io_buffer); + ib_fprintf(ib_stderr, + "can not open back up file %s\n", + file_name); + return FAILURE; + } + ret_cd = write_header(fl_ptr, hdr_rec, + output_fl_desc, header_str); + if (ret_cd == FAILURE) { + free(io_buffer); + ib_fprintf(ib_stderr, + "fail to write header rec to file %s\n", + file_name); + return FAILURE; + } + + fl_ptr = fl_ptr->b_fil_next; + if (fl_ptr == NULL_B_FIL) { + ret_cd = final_flush_io_buff(remaining_io, + remaining_io_len, + output_fl_desc); + if (ret_cd == FAILURE) { + ib_fprintf(ib_stderr, + "gsplit could not do backup due"); + ib_fprintf(ib_stderr, + " to lack of space or I/O problem\n"); + free(io_buffer); + return FAILURE; + } + } + else { /* got a lot of backup files */ + + ret_cd = flush_io_buff(remaining_io, + remaining_io_len, + output_fl_desc, + file_size, + &byte_write, + &flush_done); + if (ret_cd == FAILURE) { + ib_fprintf(ib_stderr, + "gsplit could not do backup due"); + ib_fprintf(ib_stderr, " I/O problem\n"); + free(io_buffer); + return FAILURE; + } + if (flush_done == TRUE) { + file_size = file_size - byte_write; + byte_write = 0; + } + else { + remaining_io = remaining_io + byte_write; + remaining_io_len = remaining_io_len - + byte_write; + } + } + } /* end of while loop */ + break; + } + + default: + break; + } + + if (end_of_input == TRUE) { + free(io_buffer); + return SUCCESS; + } + } + } + } /* end of while ( TRUE ) */ +} + + +static int read_and_write(FILE_DESC input_file_desc, + FILE_DESC output_fl_desc, + TEXT * file_name, + SLONG io_size, + double file_size, + UCHAR ** io_buffer, + BOOLEAN * end_of_input, + double *byte_read, + SLONG * byte_write) +{ + +/******************************************************************** +** +** r e a d _ a n d _ w r i t e +** +********************************************************************* +** +** Functional description: +** +** Read data from input file and write the data out to output +** file. +** +********************************************************************* +*/ + + SLONG read_cnt, last_read_size, write_cnt; + + /******************************************************** + ** when number of byte read + number of byte goint to + ** be read is greater then file size, then calculate + ** the size for the last read and do the last read for + ** the current backup file. Otherwise read as mush data + ** as will fit in the current backup file. + ********************************************************** + */ + + if (*byte_read + io_size > file_size) { + last_read_size = (SLONG) (file_size - *byte_read); + read_cnt = read(input_file_desc, *io_buffer, last_read_size); + } + else + read_cnt = read(input_file_desc, *io_buffer, io_size); + + switch (read_cnt) { + case (0): /* no more data to be read */ + close(output_fl_desc); + *end_of_input = TRUE; + *byte_read = *byte_read + read_cnt; + return SUCCESS; + break; + + case (-1): /* read failed */ + close(output_fl_desc); + ib_fprintf(ib_stderr, + "fail to read input from ib_stdin, errno = %d\n", errno); + return FAILURE; + break; + + default: /* read ok */ + *byte_read = *byte_read + read_cnt; + break; + } + + write_cnt = write(output_fl_desc, *io_buffer, read_cnt); + + switch (write_cnt) { + case (-1): /* write failed */ + close(output_fl_desc); + return FAILURE; + break; + + default: + if (write_cnt == read_cnt) /* write ok */ + return SUCCESS; + else { /* write less data then it reads in */ + + close(output_fl_desc); + *byte_write = write_cnt; + return FILE_IS_FULL; + } + break; + } +} + + +static int final_read_and_write(FILE_DESC input_file_desc, + FILE_DESC output_fl_desc, + TEXT * file_name, + SLONG io_size, + UCHAR ** io_buffer, + BOOLEAN * end_of_input) +{ + +/******************************************************************** +** +** f i n a l _ r e a d _ a n d _ w r i t e +** +********************************************************************* +** +** Functional description: +** +** Read all data from input file and write the data out to output +** file. +** +********************************************************************* +*/ + + SLONG read_cnt, write_cnt; + + read_cnt = read(input_file_desc, *io_buffer, io_size); + + switch (read_cnt) { + case (0): /* no more data to be read */ + close(output_fl_desc); + *end_of_input = TRUE; + return SUCCESS; + break; + + case (-1): /* read failed */ + close(output_fl_desc); + ib_fprintf(ib_stderr, + "problem when reading input file, errno = %d\n", errno); + return FAILURE; + break; + + default: /* read ok */ + break; + } + + write_cnt = write(output_fl_desc, *io_buffer, read_cnt); + + switch (write_cnt) { + case (-1): /* write failed */ + close(output_fl_desc); + return FAILURE; + break; + + default: + if (write_cnt == read_cnt) /* write ok */ + return SUCCESS; + else { /* write less data then it reads in */ + + ib_fprintf(ib_stderr, + "There is no enough space to write to back up file %s\n", + file_name); + close(output_fl_desc); + return FAILURE; + } + break; + } +} + + +static int join_multy_bakup_files( B_FIL file_list) +{ +/******************************************************************** +** +** j o i n _ m u l t y _ b a c k u p _ f i l e s +** +********************************************************************* +** +** Functional description: +** +** allocates I/O buffer and walks through backup files` chain. +** calls read_and_write_for_join routine to read data from +** backup file and to write data to standard output file +** which later to be processed by gbak. Finally frees up +** I/O buffer +** +********************************************************************* +*/ + + FILE_DESC output_fl_desc; + B_FIL fl_ptr, next_fl; + TEXT *file_name; + UCHAR* io_buffer = 0; + SLONG cnt, ret_cd, total_int; + + output_fl_desc = GBAK_STDOUT_DESC; + + io_buffer = (UCHAR *) malloc(IO_BUFFER_SIZE); + cnt = total_int = 0; + + if (io_buffer == 0) { + ib_fprintf(ib_stderr, "I/O buffer allocation failed\n"); + return FAILURE; + } + + for (fl_ptr = file_list; fl_ptr; fl_ptr = next_fl) { + cnt++; + next_fl = fl_ptr->b_fil_next; + file_name = fl_ptr->b_fil_name; + + ret_cd = + read_and_write_for_join(output_fl_desc, file_name, &io_buffer, + cnt, &total_int); + + if (ret_cd == FAILURE) { + free(io_buffer); + return FAILURE; + } + + } /* end of for loop */ + + free(io_buffer); + return SUCCESS; +} + + +static int read_and_write_for_join(FILE_DESC output_fl_desc, + TEXT * file_name, + UCHAR ** io_buffer, +SLONG cnt, SLONG * total_int) +{ +/******************************************************************** +** +** r e a d _ a n d _ w r i t e _ f o r _ j o i n +** +********************************************************************* +** +** Functional description: +** +** Reads data from backup files and writes to standard +** output file. +** +********************************************************************* +*/ + + FILE_DESC input_fl_desc; + BOOLEAN end_of_input = FALSE; + SLONG indx, num_int, ret_cd, read_cnt, write_cnt; + SLONG skip_to_num, skip_to_total; + TEXT *char_ptr1, *char_ptr2, num_arr[5], total_arr[5]; + HEADER_REC hdr_rec; + + input_fl_desc = open(file_name, MODE_READ); + + if (input_fl_desc == -1) { + ib_fprintf(ib_stderr, "can not open input file %s\n", file_name); + return FAILURE; + } + + read_cnt = read(input_fl_desc, *io_buffer, HEADER_REC_LEN); + if (read_cnt != HEADER_REC_LEN) { + close(input_fl_desc); + ib_fprintf(ib_stderr, + "progam fails to read gsplit header record in back-up file%s\n", + file_name); + return FAILURE; + } + + char_ptr1 = reinterpret_cast(*io_buffer); + ret_cd = strncmp(char_ptr1, HEADER_REC_NAME, sizeof(hdr_rec.name) - 1); + if (ret_cd != 0) { + close(input_fl_desc); + ib_fprintf(ib_stderr, "gsplit: expected GSPLIT description record\n"); + ib_fprintf(ib_stderr, + "gsplit: Exiting before completion due to errors\n"); + return FAILURE; + } + + skip_to_num = sizeof(hdr_rec.name) + sizeof(hdr_rec.date_time) + + sizeof(hdr_rec.text1); + skip_to_total = skip_to_num + sizeof(hdr_rec.num) + sizeof(hdr_rec.text2); + + char_ptr1 = reinterpret_cast(*io_buffer + skip_to_num); + char_ptr2 = reinterpret_cast(*io_buffer + skip_to_total); + for (indx = 0; indx < sizeof(hdr_rec.num); indx++) { + num_arr[indx] = *char_ptr1; + char_ptr1++; + if (cnt == 1) { + total_arr[indx] = *char_ptr2; + char_ptr2++; + } + } + num_arr[indx] = '\0'; + num_int = atoi(num_arr); + if (cnt == 1) { + total_arr[indx] = '\0'; + *total_int = atoi(total_arr); + } + + if ((num_int != cnt) || (num_int > *total_int)) { + close(input_fl_desc); + ib_fprintf(ib_stderr, + "gsplit: join backup file is out of sequence\n"); + ib_fprintf(ib_stderr, + "gsplit: Exiting before completion due to errors\n"); + return FAILURE; + } + + read_cnt = read(input_fl_desc, *io_buffer, IO_BUFFER_SIZE); + + + while (TRUE) { + switch (read_cnt) { + case (0): /* no more data to be read */ + close(input_fl_desc); + return SUCCESS; + break; + + case (-1): /* read failed */ + close(input_fl_desc); + return FAILURE; + break; + + default: /* this is the last read */ + break; + } + + write_cnt = write(output_fl_desc, *io_buffer, read_cnt); + + switch (write_cnt) { + case (-1): /* write failed */ + close(input_fl_desc); + return FAILURE; + break; + + default: + assert(write_cnt == read_cnt); + break; + } + + read_cnt = read(input_fl_desc, *io_buffer, IO_BUFFER_SIZE); + + } /* end of while (TRUE) loop */ +} + + +static int conv_ntoc( SLONG numeric_in, TEXT char_out[]) +{ +/******************************************************************** +** +** c o n v _ n t o c +** +********************************************************************* +** +** Functional description: +** +** Convert 4-digit numeric data to a 4-byte character array. +** +********************************************************************* +*/ + + SLONG indx, mod, i; + + i = numeric_in; + indx = 3; + + while (TRUE) { + mod = i % 10; + switch (mod) { + case (0): + char_out[indx] = '0'; + break; + + case (1): + char_out[indx] = '1'; + break; + + case (2): + char_out[indx] = '2'; + break; + + case (3): + char_out[indx] = '3'; + break; + + case (4): + char_out[indx] = '4'; + break; + + case (5): + char_out[indx] = '5'; + break; + + case (6): + char_out[indx] = '6'; + break; + + case (7): + char_out[indx] = '7'; + break; + + case (8): + char_out[indx] = '8'; + break; + + default: + char_out[indx] = '9'; + break; + } + indx = indx - 1; + i = i / 10; + if (i <= 0) + break; + } + + for (; indx >= 0; indx--) { + char_out[indx] = ' '; + } + return SUCCESS; +} + + +static int write_header(B_FIL fl_ptr, + HEADER_REC hdr_rec, + FILE_DESC output_fl_desc, + TEXT header_str[]) +{ +/******************************************************************** +** +** w r i t e _ h e a d e r +** +********************************************************************* +** +** Functional description: +** +** writes out gsplit header record +** +** assigns backup file name to header record file name array +** calls conv_ntoc routine to convert numeric data to char data +** and assigns to header record file number field +** writes gsplit header record out to backup file +** +********************************************************************* +*/ + TEXT *file_name, num_arr[5]; + SLONG end, indx, pos, ret_cd, write_cnt; + + ret_cd = conv_ntoc(fl_ptr->b_fil_number, num_arr); + if (ret_cd == FAILURE) { + ib_printf + ("gsplit could not convert numeric data to character data\n"); + return FAILURE; + } + num_arr[sizeof(num_arr) - 1] = TERMINAL; + pos = sizeof(hdr_rec.name) + sizeof(hdr_rec.date_time) + + sizeof(hdr_rec.text1); + ret_cd = set_hdr_str(header_str, num_arr, pos, sizeof(hdr_rec.num)); + + file_name = fl_ptr->b_fil_name; + pos = sizeof(hdr_rec.name) + sizeof(hdr_rec.date_time) + + sizeof(hdr_rec.text1) + sizeof(hdr_rec.num) + + sizeof(hdr_rec.text2) + sizeof(hdr_rec.total) + sizeof(hdr_rec.text3); + ret_cd = set_hdr_str(header_str, file_name, pos, strlen(file_name)); + write_cnt = write(output_fl_desc, header_str, HEADER_REC_LEN); + + switch (write_cnt) + { + case (-1): /* write failed */ + close(output_fl_desc); + return FAILURE; + break; + + default: + end = pos + strlen(file_name); + for (indx = pos; indx < end; indx++) + header_str[indx] = BLANK; + return SUCCESS; + break; + } +} + + +static int flush_io_buff(UCHAR* remaining_io, + SLONG remaining_io_len, + FILE_DESC output_fl_desc, + double file_size, + SLONG* byte_write, + BOOLEAN* flush_done) +{ +/******************************************************************** +** +** f l u s h _ i o _ b u f f +** +********************************************************************* +** +** Functional description: +** +** flush out the remaining data in the I/O buffer +** +** when the file_size is truly has space to write out the remaining data +** then +** set and returns flush_done TRUE +** otherwise +** closes the output file, set and returns flush_done FALSE. +** otherwise +** we can only writes out as much data as file_size indicated +** if it was able to write out the remaining data +** then +** set and returns flush_done TRUE +** otherwise +** closes the output file, set and returns flush_done FALSE. +** +********************************************************************* +*/ + + SLONG write_cnt; + + if (file_size > remaining_io_len) { + write_cnt = write(output_fl_desc, remaining_io, remaining_io_len); + } + else { /* file_size <= remaining_io_len */ + + write_cnt = + write(output_fl_desc, remaining_io, (unsigned int) file_size); + } + + switch (write_cnt) { + case (-1): /* write failed */ + close(output_fl_desc); + *flush_done = FALSE; + return FAILURE; + break; + + default: + if (write_cnt == remaining_io_len) /* write ok */ + *flush_done = TRUE; + else { /* could not write out all remaining data */ + + close(output_fl_desc); + *flush_done = FALSE; + } + *byte_write = write_cnt; + return SUCCESS; + break; + } +} + + +static int final_flush_io_buff(UCHAR * remaining_io, + SLONG remaining_io_len, + FILE_DESC output_fl_desc) +{ +/******************************************************************** +** +** f i n a l _ f l u s h _ i o _ b u f f +** +********************************************************************* +** +** Functional description: +** +** flush out the remaining data in the I/O buffer +** +********************************************************************* +*/ + + SLONG write_cnt; + + write_cnt = write(output_fl_desc, remaining_io, remaining_io_len); + switch (write_cnt) + { + case (-1): /* write failed */ + close(output_fl_desc); + return FAILURE; + break; + + default: + if (write_cnt == remaining_io_len) /* write ok */ + return SUCCESS; + else { /* could not write out all remaining data */ + + close(output_fl_desc); + return FAILURE; + } + break; + } +} + + +static int print_clo(TEXT* prog_name) +{ +/******************************************************************** +** +** p r i n t _ c l o +** +********************************************************************* +** +** Functional description: +** +** print out gsplit utility command line options +** +********************************************************************* +*/ + + ib_fprintf(ib_stderr, "%s: Command Line Options Are:\n", prog_name); + ib_fprintf(ib_stderr, + " gsplit -S[PLIT_BK_FILE] {k|m|g} [... [{k|m|g}]] or\n"); + ib_fprintf(ib_stderr, " gsplit -J[OINT_BK_FILE] [... ]\n"); + ib_fprintf(ib_stderr, + "%s: option can be abbreviated to the unparenthesized characters\n", + prog_name); + ib_fprintf(ib_stderr, + "%s: Exiting before completion due to errors\n", prog_name); + + return SUCCESS; + +} + + +static int set_hdr_str(TEXT header_str[], TEXT * in_str, SLONG pos, SLONG len) +{ +/******************************************************************** +** +** s e t _ h d r _ s t r +** +********************************************************************* +** +** Functional description: +** +** initialyze header string +** +********************************************************************* +*/ + + TEXT *t_str; + SLONG end, indx; + + t_str = in_str; + end = pos + len; + + for (indx = pos; indx < end; indx++) { + switch (*t_str) { + case (NEW_LINE): + header_str[indx] = ' '; + break; + case (TERMINAL): + header_str[indx] = ' '; + break; + default: + header_str[indx] = *t_str; + break; + } + *t_str++; + } + return SUCCESS; +} + + +static int free_file_list( B_FIL file_list) +{ +/******************************************************************** +** +** f r e e _ f i l e _ l i s t +** +********************************************************************* +** +** Functional description: +** +** free all the storage allocated for backup files structure +** +********************************************************************* +*/ + + B_FIL file_ptr, next_file; + + for (file_ptr = file_list; file_ptr != NULL_B_FIL; file_ptr = next_file) { + next_file = file_ptr->b_fil_next; + free(file_ptr->b_fil_name); + free(file_ptr); + } + + return SUCCESS; +} diff --git a/src/burp/spit.h b/src/burp/spit.h new file mode 100644 index 0000000000..1c1b9f994c --- /dev/null +++ b/src/burp/spit.h @@ -0,0 +1,50 @@ +/* + ************************************************************************** + * + * PROGRAM: JRD file split utility program + * MODULE: spit.h + * DESCRIPTION: file split utility program main header file + * + * + ************************************************************************** + * The contents of this file are subject to the Interbase Public + * License Version 1.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy + * of the License at http://www.Inprise.com/IPL.html + * + * Software distributed under the License is distributed on an + * "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either express + * or implied. See the License for the specific language governing + * rights and limitations under the License. + * + * The Original Code was created by Inprise Corporation + * and its predecessors. Portions created by Inprise Corporation are + * Copyright (C) Inprise Corporation. + * + * All Rights Reserved. + * Contributor(s): ______________________________________. + */ + +#include "../jrd/common.h" + +#define BLANK ' ' +#define BURP_ALLOC_ZERO(size) MISC_alloc_burp((ULONG) size) +#define FILE_IS_FULL -9 +#define FILE_NM_ARR_LEN 20 +#define GBAK_STDIN_DESC (int)0 /* standart input file descriptor */ +#define GBAK_STDOUT_DESC (int)1 /* standart output file descriptor */ +#define GSPLIT_HDR_REC_NM "InterBase/Gsplit" + +#define IO_BUFFER_SIZE (16 * K_BYTES) + +#define K_BYTES 1024 +#define M_BYTES (K_BYTES * K_BYTES) +#define G_BYTES (K_BYTES * M_BYTES) +#define MAX_FILE_NM_LEN 27 /* size of header_rec.fl_name */ +#define MAX_NUM_OF_FILES 9999 +#define MIN_FILE_SIZE M_BYTES +#define NEW_LINE '\n' +#define NULL_B_FIL ((B_FIL) NULL) +#define TERMINAL '\0' + +typedef int FILE_DESC; diff --git a/src/csv/csi.cpp b/src/csv/csi.cpp new file mode 100644 index 0000000000..4b34d7e912 --- /dev/null +++ b/src/csv/csi.cpp @@ -0,0 +1,3774 @@ +/* + * PROGRAM: Central Server + * MODULE: csi.c + * DESCRIPTION: Central Server Interface + * + * The contents of this file are subject to the Interbase Public + * License Version 1.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy + * of the License at http://www.Inprise.com/IPL.html + * + * Software distributed under the License is distributed on an + * "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either express + * or implied. See the License for the specific language governing + * rights and limitations under the License. + * + * The Original Code was created by Inprise Corporation + * and its predecessors. Portions created by Inprise Corporation are + * Copyright (C) Inprise Corporation. + * + * All Rights Reserved. + * Contributor(s): ______________________________________. + */ + +#include "../jrd/common.h" +#include +#include "../csv/csi.h" +#include "../jrd/license.h" +#include "../jrd/codes.h" +#include "../jrd/inf.h" +#include "../jrd/thd.h" +#include "../csv/csi_proto.h" +#include "../csv/css_proto.h" +#include "../jrd/gds_proto.h" +#include "../remote/merge_proto.h" + +#ifdef PIPE_SERVER +#ifdef VMS +#include descrip +#include libdef +#include ssdef +#include jpidef +#include pqldef +#include "../jrd/lnmdef.h" + +#ifndef ORACLE_ALT +#define GDS_PIPE "[sysexe]gds_pipe_orcl.exe" +#else +#define GDS_PIPE "[sysexe]gds_pipe_orcl_alt.exe" +#endif +#endif +#endif // PIPE_SERVER + +#define ALLOC(type, length) CSS_alloc_local (type, length) +#define FREE(block) CSS_free_local (block) + +#ifndef MOVE +#define MOVE(from, to, length) move (from, to, length) +#endif + +static RDB CSI_databases, CSI_free_servers; +static TEXT error_buffer[1024]; + +#ifdef PIPE_SERVER +#ifdef VMS +/* Define logical names that sub-process should inherit */ + +static SCHAR *inherit_logicals[] = { + "SYS$LOGIN", + "SYS$SCRATCH", + "SYS$NODE", + "SYS$INTERBASE", + 0 +}; + +typedef struct itm { + SSHORT itm_length; + SSHORT itm_code; + SCHAR *itm_buffer; + SSHORT *itm_return_length; +} ITM; + +typedef struct itmq { + SCHAR itmq_code; + SLONG itmq_value; +} ITMQ; +#endif + +#endif + +#ifndef MULTI_THREAD +static void event_handler(PTR); +#else +static void event_thread(PTR); +#endif + +static STATUS get_response(RDB, MSG, STATUS *, HANDLE *, USHORT, UCHAR *, + USHORT *); +static STATUS handle_error(STATUS *, STATUS); +static STATUS info(RDB, STATUS *, HANDLE, MSG_T, USHORT, USHORT, UCHAR *, + USHORT, UCHAR *); +#ifndef GATEWAY +static RDB init(STATUS *, MSG_T, UCHAR *, USHORT, UCHAR *, USHORT, TEXT *, + USHORT, USHORT, TEXT *, USHORT *); +#else +static RDB init(STATUS *, MSG_T, UCHAR *, USHORT, UCHAR *, USHORT, TEXT *); +#endif +static RTR make_transaction(RDB, HANDLE); +static void move(UCHAR *, UCHAR *, USHORT); +static USHORT name_length(TEXT *); +static STATUS process_response(MSG_RESP, STATUS *, HANDLE *, USHORT, UCHAR *, + USHORT *); +static void release_blob(RBL); +static STATUS release_object(RDB, MSG_T, HANDLE, STATUS *); +static void release_request(RRQ); +static void release_sql_request(RSR); +static void release_transaction(RTR); +static STATUS send_blob(STATUS *, RBL, USHORT, UCHAR *); +#ifdef PIPE_SERVER +#ifdef VMS +static void setup_creprc_info(SLONG **, ITMQ **, SLONG *); +static int spawn(STATUS *, UCHAR *, PTR *); +static void trans_logicals(void); +#endif +#endif + +#define CHECK_HANDLE(blk, type, error) if (!blk || ((BLK) blk)->blk_type != (SCHAR) type) \ + return handle_error (user_status, error) + +#define NULL_CHECK(ptr, code) if (*ptr) return handle_error (user_status, code) + +/* Transaction element block */ + +typedef struct teb { + RDB *teb_database; + int teb_tpb_length; + UCHAR *teb_tpb; +} TEB; + + +#ifndef PIPE_SERVER +#define GDS_ATTACH_DATABASE CSI_attach_database +#define GDS_BLOB_INFO CSI_blob_info +#define GDS_CANCEL_BLOB CSI_cancel_blob +#define GDS_CLOSE_BLOB CSI_close_blob +#define GDS_COMMIT CSI_commit_transaction +#define GDS_COMMIT_RETAINING CSI_commit_retaining +#define GDS_COMPILE CSI_compile_request +#define GDS_CREATE_BLOB2 CSI_create_blob2 +#define GDS_CREATE_DATABASE CSI_create_database +#define GDS_DATABASE_INFO CSI_database_info +#define GDS_DDL CSI_ddl +#define GDS_DETACH CSI_detach_database +#define GDS_DROP_DATABASE CSI_drop_database +#define GDS_GET_SEGMENT CSI_get_segment +#define GDS_GET_SLICE CSI_get_slice +#define GDS_OPEN_BLOB2 CSI_open_blob2 +#define GDS_PREPARE CSI_prepare_transaction +#define GDS_PUT_SEGMENT CSI_put_segment +#define GDS_PUT_SLICE CSI_put_slice +#define GDS_RECEIVE CSI_receive +#define GDS_RECONNECT CSI_reconnect_transaction +#define GDS_RELEASE_REQUEST CSI_release_request +#define GDS_REQUEST_INFO CSI_request_info +#define GDS_ROLLBACK CSI_rollback_transaction +#define GDS_SEEK_BLOB CSI_seek_blob +#define GDS_SEND CSI_send +#define GDS_START_AND_SEND CSI_start_and_send +#define GDS_START CSI_start_request +#define GDS_START_MULTIPLE CSI_start_multiple +#define GDS_START_TRANSACTION CSI_start_transaction +#define GDS_TRANSACT_REQUEST CSI_transact_request +#define GDS_TRANSACTION_INFO CSI_transaction_info +#define GDS_UNWIND CSI_unwind_request +#define GDS_QUE_EVENTS CSI_que_events +#define GDS_CANCEL_EVENTS CSI_cancel_events + +#define GDS_DSQL_ALLOCATE CSI_allocate_statement +#define GDS_DSQL_EXECUTE CSI_execute +#define GDS_DSQL_EXECUTE2 CSI_execute2 +#define GDS_DSQL_EXECUTE_IMMED CSI_execute_immediate +#define GDS_DSQL_EXECUTE_IMMED2 CSI_execute_immediate2 +#define GDS_DSQL_FETCH CSI_fetch +#define GDS_DSQL_FREE CSI_free_statement +#define GDS_DSQL_INSERT CSI_insert +#define GDS_DSQL_PREPARE CSI_prepare +#define GDS_DSQL_SET_CURSOR CSI_set_cursor_name +#define GDS_DSQL_SQL_INFO CSI_sql_info +#else +#define GDS_ATTACH_DATABASE PSI_attach_database +#define GDS_BLOB_INFO PSI_blob_info +#define GDS_CANCEL_BLOB PSI_cancel_blob +#define GDS_CLOSE_BLOB PSI_close_blob +#define GDS_COMMIT PSI_commit_transaction +#define GDS_COMMIT_RETAINING PSI_commit_retaining +#define GDS_COMPILE PSI_compile_request +#define GDS_CREATE_BLOB PSI_create_blob +#define GDS_CREATE_BLOB2 PSI_create_blob2 +#define GDS_CREATE_DATABASE PSI_create_database +#define GDS_DATABASE_INFO PSI_database_info +#define GDS_DDL PSI_ddl +#define GDS_DETACH PSI_detach_database +#define GDS_DROP_DATABASE PSI_drop_database +#define GDS_GET_SEGMENT PSI_get_segment +#define GDS_GET_SLICE PSI_get_slice +#define GDS_OPEN_BLOB PSI_open_blob +#define GDS_OPEN_BLOB2 PSI_open_blob2 +#define GDS_PREPARE PSI_prepare_transaction +#define GDS_PUT_SEGMENT PSI_put_segment +#define GDS_PUT_SLICE PSI_put_slice +#define GDS_RECEIVE PSI_receive +#define GDS_RECONNECT PSI_reconnect_transaction +#define GDS_RELEASE_REQUEST PSI_release_request +#define GDS_REQUEST_INFO PSI_request_info +#define GDS_ROLLBACK PSI_rollback_transaction +#define GDS_SEND PSI_send +#define GDS_SEEK_BLOB PSI_seek_blob +#define GDS_START_AND_SEND PSI_start_and_send +#define GDS_START PSI_start_request +#define GDS_START_MULTIPLE PSI_start_multiple +#define GDS_START_TRANSACTION PSI_start_transaction +#define GDS_TRANSACT_REQUEST PSI_transact_request +#define GDS_TRANSACTION_INFO PSI_transaction_info +#define GDS_UNWIND PSI_unwind_request +#define GDS_QUE_EVENTS PSI_que_events +#define GDS_CANCEL_EVENTS PSI_cancel_events + +#define GDS_DSQL_ALLOCATE PSI_allocate_statement +#define GDS_DSQL_EXECUTE PSI_execute +#define GDS_DSQL_EXECUTE2 PSI_execute2 +#define GDS_DSQL_EXECUTE_IMMED PSI_execute_immediate +#define GDS_DSQL_EXECUTE_IMMED2 PSI_execute_immediate2 +#define GDS_DSQL_FETCH PSI_fetch +#define GDS_DSQL_FREE PSI_free_statement +#define GDS_DSQL_INSERT PSI_insert +#define GDS_DSQL_PREPARE PSI_prepare +#define GDS_DSQL_SET_CURSOR PSI_set_cursor_name +#define GDS_DSQL_SQL_INFO PSI_sql_info +#endif + + +#ifndef GATEWAY +STATUS GDS_ATTACH_DATABASE( + STATUS * user_status, + SSHORT file_length, + SCHAR * file_name, + RDB * handle, +SSHORT dpb_length, SCHAR * dpb, TEXT * expanded_filename) +{ +/************************************** + * + * g d s _ A T T A C H _ D A T A B A S E ( n o n - G a t e w a y ) + * + ************************************** + * + * Functional description + * Create a nice, squeeky clean database, uncorrupted by user data. + * + **************************************/ + RDB rdb; + + NULL_CHECK(handle, gds__bad_db_handle); + + if (rdb = init(user_status, MSG_attach_database, + file_name, file_length, dpb, dpb_length, expanded_filename, + 0, 0, 0, 0)) + *handle = rdb; + + return user_status[1]; +} +#endif + + +#ifdef GATEWAY +#ifdef PIPE_SERVER +STATUS GDS_ATTACH_DATABASE(STATUS * user_status, + SSHORT file_length, + SCHAR * file_name, + RDB * handle, + SSHORT dpb_length, + SCHAR * dpb, TEXT * expanded_filename) +{ +/************************************** + * + * g d s _ A T T A C H _ D A T A B A S E ( G a t e w a y _ s e r v e r ) + * + ************************************** + * + * Functional description + * Create a nice, squeeky clean database, uncorrupted by user data. + * + **************************************/ + RDB rdb; + + NULL_CHECK(handle, gds__bad_db_handle); + + if (rdb = init(user_status, MSG_attach_database, + file_name, file_length, dpb, dpb_length, + expanded_filename)) *handle = rdb; + + return user_status[1]; +} +#endif +#endif + + +STATUS GDS_BLOB_INFO(STATUS * user_status, + RBL * blob, + SSHORT item_length, + UCHAR * items, SSHORT buffer_length, UCHAR * buffer) +{ +/************************************** + * + * g d s _ b l o b _ i n f o + * + ************************************** + * + * Functional description + * Provide information on blob object. + * + **************************************/ + RDB rdb; + + CHECK_HANDLE((*blob), type_rbl, gds__bad_segstr_handle); + rdb = (*blob)->rbl_rdb; + CHECK_HANDLE(rdb, type_rdb, gds__bad_db_handle); + + return info(rdb, user_status, (*blob)->rbl_handle, MSG_blob_info, 0, + item_length, items, buffer_length, buffer); +} + + +STATUS GDS_CANCEL_BLOB(STATUS * user_status, RBL * blob_handle) +{ +/************************************** + * + * g d s _ c a n c e l _ b l o b + * + ************************************** + * + * Functional description + * Abort a partially completed blob. + * + **************************************/ + RDB rdb; + RBL blob; + + if (!(blob = *blob_handle)) { + if (user_status) { + *user_status++ = gds_arg_gds; + *user_status++ = SUCCESS; + *user_status = 0; + } + return SUCCESS; + } + + CHECK_HANDLE(blob, type_rbl, gds__bad_segstr_handle); + rdb = blob->rbl_rdb; + CHECK_HANDLE(rdb, type_rdb, gds__bad_db_handle); + + if (release_object(rdb, MSG_cancel_blob, blob->rbl_handle, user_status)) + return user_status[1]; + + release_blob(blob); + *blob_handle = NULL; + + return user_status[1]; +} + + +STATUS GDS_CANCEL_EVENTS(STATUS * user_status, RDB * handle, SLONG * id) +{ +/************************************** + * + * g d s _ C A N C E L _ E V E N T S + * + ************************************** + * + * Functional description + * Cancel a remove event. + * + **************************************/ + RDB rdb; + MSG_OP message; + + rdb = *handle; + CHECK_HANDLE(rdb, type_rdb, gds__bad_db_handle); + + message = (MSG_OP) CSS_alloc_message(type_msg, sizeof(struct msg_op)); + message->msg_op_header.msg_type = MSG_cancel_events; + message->msg_op_handle = rdb->rdb_handle; + message->msg_op_level = *id; + + return get_response(rdb, message, user_status, 0, 0, 0, 0); +} + + +STATUS GDS_CLOSE_BLOB(STATUS * user_status, RBL * blob_handle) +{ +/************************************** + * + * g d s _ c l o s e _ b l o b + * + ************************************** + * + * Functional description + * Abort a partially completed blob. + * + **************************************/ + RDB rdb; + RBL blob; + + blob = *blob_handle; + CHECK_HANDLE(blob, type_rbl, gds__bad_segstr_handle); + rdb = blob->rbl_rdb; + CHECK_HANDLE(rdb, type_rdb, gds__bad_db_handle); + +/* If a portion of the blob hasn't been sent to the pipe yet, + do so now. */ + + if ((blob->rbl_flags & RBL_create) && blob->rbl_ptr != blob->rbl_buffer) + send_blob(user_status, blob, 0, NULL); + + if (release_object(rdb, MSG_close_blob, blob->rbl_handle, user_status)) + return user_status[1]; + + release_blob(blob); + *blob_handle = NULL; + + return user_status[1]; +} + + +STATUS GDS_COMMIT(STATUS * user_status, RTR * rtr_handle) +{ +/************************************** + * + * g d s _ C O M M I T + * + ************************************** + * + * Functional description + * Commit a transaction. + * + **************************************/ + RDB rdb; + RTR transaction; + + transaction = *rtr_handle; + CHECK_HANDLE(transaction, type_rtr, gds__bad_trans_handle); + rdb = transaction->rtr_rdb; + CHECK_HANDLE(rdb, type_rdb, gds__bad_db_handle); + + if (release_object(rdb, MSG_commit, transaction->rtr_handle, user_status)) + return user_status[1]; + + release_transaction(transaction); + *rtr_handle = NULL; + + return user_status[1]; +} + + +STATUS GDS_COMMIT_RETAINING(STATUS * user_status, RTR * rtr_handle) +{ +/************************************** + * + * g d s _ c o m m i t _ r e t a i n i n g + * + ************************************** + * + * Functional description + * Prepare a transaction for commit. First phase of a two + * phase commit. + * + **************************************/ + RDB rdb; + RTR transaction; + + transaction = *rtr_handle; + CHECK_HANDLE(transaction, type_rtr, gds__bad_trans_handle); + rdb = transaction->rtr_rdb; + CHECK_HANDLE(rdb, type_rdb, gds__bad_db_handle); + + if (release_object + (rdb, MSG_commit_retaining, transaction->rtr_handle, + user_status)) return user_status[1]; + + return user_status[1]; +} + + +STATUS GDS_COMPILE(STATUS * user_status, + RDB * db_handle, + RRQ * req_handle, USHORT blr_length, UCHAR * blr) +{ +/************************************** + * + * g d s _ C O M P I L E + * + ************************************** + * + * Functional description + * + **************************************/ + RDB rdb; + RRQ request; + MSG_OP message; + HANDLE handle; + +/* Check and validate handles, etc. */ + + NULL_CHECK(req_handle, gds__bad_req_handle); + rdb = *db_handle; + CHECK_HANDLE(rdb, type_rdb, gds__bad_db_handle); + + message = + (MSG_OP) CSS_alloc_message(type_msg, + (int) (sizeof(struct msg_op) + + blr_length)); + message->msg_op_header.msg_type = MSG_compile; + message->msg_op_handle = rdb->rdb_handle; + message->msg_op_length = blr_length; + MOVE(blr, message->msg_op_data, blr_length); + + if (!get_response(rdb, message, user_status, &handle, 0, 0, 0)) { + *req_handle = request = (RRQ) ALLOC(type_rrq, sizeof(struct rrq)); + request->rrq_handle = handle; + request->rrq_rdb = rdb; + request->rrq_next = rdb->rdb_requests; + rdb->rdb_requests = request; + } + + return user_status[1]; +} + + +STATUS GDS_CREATE_BLOB2(STATUS * user_status, + RDB * db_handle, + RTR * rtr_handle, + RBL * blob_handle, + BID blob_id, USHORT bpb_length, UCHAR * bpb) +{ +/************************************** + * + * g d s _ c r e a t e _ b l o b + * + ************************************** + * + * Functional description + * Open an existing blob. + * + **************************************/ + RDB rdb; + RTR transaction; + RBL blob; + HANDLE handle; + MSG_BLOB message; + + NULL_CHECK(blob_handle, gds__bad_segstr_handle); + rdb = *db_handle; + CHECK_HANDLE(rdb, type_rdb, gds__bad_db_handle); + transaction = *rtr_handle; + CHECK_HANDLE(transaction, type_rtr, gds__bad_trans_handle); + + message = + (MSG_BLOB) CSS_alloc_message(type_msg, + (int) (sizeof(struct msg_blob) + + bpb_length - 1)); + message->msg_blob_header.msg_type = MSG_create_blob; + message->msg_blob_database = rdb->rdb_handle; + message->msg_blob_transaction = transaction->rtr_handle; + message->msg_blob_bpb_length = bpb_length; + MOVE(bpb, message->msg_blob_bpb, bpb_length); + + if (!get_response(rdb, message, user_status, &handle, 8, blob_id, 0)) { + *blob_handle = blob = + (RBL) ALLOC(type_rbl, sizeof(struct rbl) + BLOB_LENGTH); + blob->rbl_handle = handle; + blob->rbl_buffer_length = BLOB_LENGTH; + blob->rbl_rdb = rdb; + blob->rbl_rtr = transaction; + blob->rbl_ptr = blob->rbl_buffer; + blob->rbl_flags |= RBL_create; + blob->rbl_next = transaction->rtr_blobs; + transaction->rtr_blobs = blob; + } + + return user_status[1]; +} + + +#ifdef PIPE_SERVER +STATUS GDS_CREATE_BLOB(STATUS * user_status, + RDB * db_handle, + RTR * rtr_handle, RBL * blob_handle, BID blob_id) +{ +/************************************** + * + * g d s _ c r e a t e _ b l o b + * + ************************************** + * + * Functional description + * Open an existing blob. + * + **************************************/ + + return GDS_CREATE_BLOB2(user_status, db_handle, rtr_handle, blob_handle, + blob_id, 0, NULL); +} +#endif + + +#ifndef GATEWAY +STATUS GDS_CREATE_DATABASE(STATUS * user_status, + SSHORT file_length, + SCHAR * file_name, + RDB * handle, + SSHORT dpb_length, + SCHAR * dpb, + SSHORT db_type, TEXT * expanded_filename) +{ +/************************************** + * + * g d s _ C R E A T E _ D A T A B A S E + * + ************************************** + * + * Functional description + * Create a nice, squeeky clean database, uncorrupted by user data. + * + **************************************/ + RDB rdb; + + NULL_CHECK(handle, gds__bad_db_handle); + + if (rdb = init(user_status, MSG_create_database, + file_name, file_length, dpb, dpb_length, expanded_filename, + 0, 0, 0, 0)) + *handle = rdb; + + return user_status[1]; +} +#endif + + +STATUS GDS_DATABASE_INFO(STATUS * user_status, + RDB * handle, + SSHORT item_length, + UCHAR * items, SSHORT buffer_length, UCHAR * buffer) +{ +/************************************** + * + * g d s _ d a t a b a s e _ i n f o + * + ************************************** + * + * Functional description + * Provide information on database object. + * + **************************************/ + RDB rdb; + UCHAR temp[1024]; + + rdb = *handle; + CHECK_HANDLE(rdb, type_rdb, gds__bad_db_handle); + + if (info + (rdb, user_status, rdb->rdb_handle, MSG_database_info, 0, item_length, + items, sizeof(temp), temp)) + return user_status[1]; + + MERGE_database_info(temp, buffer, buffer_length, +#ifndef PIPE_SERVER + IMPLEMENTATION, 9, 1, GDS_VERSION, "", 0); +#else + IMPLEMENTATION, 7, 1, GDS_VERSION, "", 0); +#endif + + return user_status[1]; +} + + +STATUS GDS_DDL(STATUS * user_status, + RDB * db_handle, + RTR * rtr_handle, USHORT ddl_length, UCHAR * ddl) +{ +/************************************** + * + * g d s _ d d l + * + ************************************** + * + * Functional description + * Do meta-data update. + * + **************************************/ + RDB rdb; + RTR transaction; + MSG_DDL message; + +/* Check and validate handles, etc. */ + + transaction = *rtr_handle; + CHECK_HANDLE(transaction, type_rtr, gds__bad_trans_handle); + rdb = *db_handle; + CHECK_HANDLE(rdb, type_rdb, gds__bad_db_handle); + + message = + (MSG_DDL) CSS_alloc_message(type_msg, + (int) (sizeof(struct msg_ddl) + + ddl_length)); + message->msg_ddl_header.msg_type = MSG_ddl; + message->msg_ddl_database = rdb->rdb_handle; + message->msg_ddl_transaction = transaction->rtr_handle; + message->msg_ddl_length = ddl_length; + MOVE(ddl, message->msg_ddl_data, ddl_length); + + return get_response(rdb, message, user_status, 0, 0, 0, 0); +} + + +STATUS GDS_DETACH(STATUS * user_status, RDB * handle) +{ +/************************************** + * + * g d s _ D E T A C H + * + ************************************** + * + * Functional description + * Close down a database. + * + **************************************/ + RDB rdb, *ptr; + RRQ rrq; + + rdb = *handle; + CHECK_HANDLE(rdb, type_rdb, gds__bad_db_handle); + + if (release_object(rdb, MSG_detach, rdb->rdb_handle, user_status)) + return user_status[1]; + + while (rrq = rdb->rdb_requests) { + if (rrq->rrq_user_handle) + *(rrq->rrq_user_handle) = NULL; + release_request(rrq); + } + + while (rdb->rdb_sql_requests) + release_sql_request(rdb->rdb_sql_requests); + + while (rdb->rdb_transactions) + release_transaction(rdb->rdb_transactions); + + if (rdb->rdb_connection2) { +#ifndef GATEWAY +#ifndef MULTI_THREAD + ISC_signal_cancel(EVENT_SIGNAL, event_handler, rdb->rdb_connection2); +#endif +#endif + CSS_disconnect(rdb->rdb_connection2); + } + +#ifdef GATEWAY +#ifdef PIPE_SERVER +#define GWAY_PIPE + rdb->rdb_connection2 = NULL; + rdb->rdb_handle = NULL; + +/* Unhook the rdb block and then add it to the free server list */ + + for (ptr = &CSI_databases; *ptr; ptr = &(*ptr)->rdb_next) + if (*ptr == rdb) { + *ptr = rdb->rdb_next; + break; + } + + rdb->rdb_next = CSI_free_servers; + CSI_free_servers = rdb; +#endif +#endif + +#ifndef GWAY_PIPE + CSS_disconnect(rdb->rdb_connection); + + FREE(rdb); +#endif + + *handle = NULL; + + return user_status[1]; +} + + +STATUS GDS_DROP_DATABASE(STATUS * user_status, RDB * handle) +{ +/************************************** + * + * i s c _ d r o p _ d a t a b a s e + * + ************************************** + * + * Functional description + * Close down and then purge a database. + * + **************************************/ + RDB rdb, *ptr; + RRQ rrq; + + rdb = *handle; + CHECK_HANDLE(rdb, type_rdb, gds__bad_db_handle); + + if (release_object(rdb, MSG_drop_database, rdb->rdb_handle, user_status)) + return user_status[1]; + + while (rrq = rdb->rdb_requests) { + if (rrq->rrq_user_handle) + *(rrq->rrq_user_handle) = NULL; + release_request(rrq); + } + + while (rdb->rdb_sql_requests) + release_sql_request(rdb->rdb_sql_requests); + + while (rdb->rdb_transactions) + release_transaction(rdb->rdb_transactions); + + if (rdb->rdb_connection2) { +#ifndef GATEWAY +#ifndef MULTI_THREAD + ISC_signal_cancel(EVENT_SIGNAL, event_handler, rdb->rdb_connection2); +#endif +#endif + CSS_disconnect(rdb->rdb_connection2); + } + +#ifdef GATEWAY +#ifdef PIPE_SERVER +#define GWAY_PIPE + rdb->rdb_connection2 = NULL; + rdb->rdb_handle = NULL; + +/* Unhook the rdb block and then add it to the free server list */ + + for (ptr = &CSI_databases; *ptr; ptr = &(*ptr)->rdb_next) + if (*ptr == rdb) { + *ptr = rdb->rdb_next; + break; + } + + rdb->rdb_next = CSI_free_servers; + CSI_free_servers = rdb; +#endif +#endif + +#ifndef GWAY_PIPE + CSS_disconnect(rdb->rdb_connection); + + FREE(rdb); +#endif + + *handle = NULL; + + return user_status[1]; +} + + +STATUS GDS_DSQL_ALLOCATE(STATUS * user_status, + RDB * db_handle, RSR * stmt_handle) +{ +/************************************** + * + * d s q l _ a l l o c a t e _ s t a t e m e n t + * + ************************************** + * + * Functional description + * Allocate a statement handle. + * + **************************************/ + RDB rdb; + RSR statement; + MSG_OP message; + HANDLE handle; + +/* Check and validate handles, etc. */ + + NULL_CHECK(stmt_handle, gds__bad_req_handle); + rdb = *db_handle; + CHECK_HANDLE(rdb, type_rdb, gds__bad_db_handle); + +/* set up the packet for the other guy... */ + + message = (MSG_OP) CSS_alloc_message(type_msg, sizeof(struct msg_op)); + message->msg_op_header.msg_type = MSG_allocate_stmt; + message->msg_op_handle = rdb->rdb_handle; + + if (!get_response(rdb, message, user_status, &handle, 0, 0, 0)) { + *stmt_handle = statement = (RSR) ALLOC(type_rsr, sizeof(struct rsr)); + statement->rsr_handle = handle; + statement->rsr_rdb = rdb; + statement->rsr_next = rdb->rdb_sql_requests; + rdb->rdb_sql_requests = statement; + } + + return user_status[1]; +} + + +STATUS GDS_DSQL_EXECUTE(STATUS * user_status, + RTR * rtr_handle, + RSR * stmt_handle, + USHORT blr_length, + UCHAR * blr, + USHORT msg_type, USHORT msg_length, UCHAR * msg) +{ +/************************************** + * + * d s q l _ e x e c u t e + * + ************************************** + * + * Functional description + * Execute a dynamic SQL statement. + * + **************************************/ + RTR transaction; + RSR statement; + HANDLE handle; + MSG_SQLMSG message; + +/* Check and validate handles, etc. */ + + statement = *stmt_handle; + CHECK_HANDLE(statement, type_rsr, gds__bad_req_handle); + if (transaction = *rtr_handle) + CHECK_HANDLE(transaction, type_rtr, gds__bad_trans_handle); + +/* set up the packet for the other guy... */ + + message = + (MSG_SQLMSG) CSS_alloc_message(type_msg, + (int) (sizeof(struct msg_sqlmsg) + + blr_length + msg_length)); + message->msg_sqlmsg_header.msg_type = MSG_execute; + message->msg_sqlmsg_transaction = + (transaction) ? transaction->rtr_handle : NULL; + message->msg_sqlmsg_statement = statement->rsr_handle; + message->msg_sqlmsg_in_blr_length = blr_length; + MOVE(blr, message->msg_sqlmsg_data + msg_length, blr_length); + message->msg_sqlmsg_in_msg_type = msg_type; + message->msg_sqlmsg_in_msg_length = msg_length; + MOVE(msg, message->msg_sqlmsg_data, msg_length); + + if (!get_response + (statement->rsr_rdb, message, user_status, &handle, 0, 0, + 0)) if (transaction && !handle) { + release_transaction(transaction); + *rtr_handle = NULL; + } + else if (!transaction && handle) + *rtr_handle = make_transaction(statement->rsr_rdb, handle); + + return user_status[1]; +} + + +STATUS GDS_DSQL_EXECUTE2(STATUS * user_status, + RTR * rtr_handle, + RSR * stmt_handle, + USHORT in_blr_length, + UCHAR * in_blr, + USHORT in_msg_type, + USHORT in_msg_length, + UCHAR * in_msg, + USHORT out_blr_length, + UCHAR * out_blr, + USHORT out_msg_type, + USHORT out_msg_length, UCHAR * out_msg) +{ +/************************************** + * + * d s q l _ e x e c u t e 2 + * + ************************************** + * + * Functional description + * Execute a dynamic SQL statement. + * + **************************************/ + RTR transaction; + RSR statement; + HANDLE handle; + MSG_SQLMSG message; + +/* Check and validate handles, etc. */ + + statement = *stmt_handle; + CHECK_HANDLE(statement, type_rsr, gds__bad_req_handle); + if (transaction = *rtr_handle) + CHECK_HANDLE(transaction, type_rtr, gds__bad_trans_handle); + +/* set up the packet for the other guy... */ + + message = + (MSG_SQLMSG) CSS_alloc_message(type_msg, + (int) (sizeof(struct msg_sqlmsg) + + in_blr_length + in_msg_length + + out_blr_length)); + message->msg_sqlmsg_header.msg_type = MSG_execute2; + message->msg_sqlmsg_transaction = + (transaction) ? transaction->rtr_handle : NULL; + message->msg_sqlmsg_statement = statement->rsr_handle; + message->msg_sqlmsg_in_blr_length = in_blr_length; + MOVE(in_blr, message->msg_sqlmsg_data + in_msg_length, in_blr_length); + message->msg_sqlmsg_in_msg_type = in_msg_type; + message->msg_sqlmsg_in_msg_length = in_msg_length; + MOVE(in_msg, message->msg_sqlmsg_data, in_msg_length); + message->msg_sqlmsg_out_blr_length = out_blr_length; + MOVE(out_blr, message->msg_sqlmsg_data + in_msg_length + in_blr_length, + out_blr_length); + message->msg_sqlmsg_out_msg_type = out_msg_type; + message->msg_sqlmsg_out_msg_length = out_msg_length; + + if (!get_response + (statement->rsr_rdb, message, user_status, &handle, out_msg_length, + out_msg, 0)) + if (transaction && !handle) { + release_transaction(transaction); + *rtr_handle = NULL; + } + else if (!transaction && handle) + *rtr_handle = make_transaction(statement->rsr_rdb, handle); + + return user_status[1]; +} + + +STATUS GDS_DSQL_EXECUTE_IMMED(STATUS * user_status, + RDB * db_handle, + RTR * rtr_handle, + USHORT length, + TEXT * string, + USHORT dialect, + USHORT blr_length, + UCHAR * blr, + USHORT msg_type, USHORT msg_length, UCHAR * msg) +{ +/************************************** + * + * d s q l _ e x e c u t e _ i m m e d i a t e + * + ************************************** + * + * Functional description + * Prepare and execute a statement. + * + **************************************/ + RDB rdb; + RTR transaction; + HANDLE handle; + MSG_EXNOW message; + +/* Check and validate handles, etc. */ + + rdb = *db_handle; + CHECK_HANDLE(rdb, type_rdb, gds__bad_db_handle); + if (transaction = *rtr_handle) + CHECK_HANDLE(transaction, type_rtr, gds__bad_trans_handle); + + if (!length) + length = strlen(string); + +/* set up the packet for the other guy... */ + + message = (MSG_EXNOW) CSS_alloc_message(type_msg, + (int) (sizeof(struct msg_exnow) + + length + blr_length + + msg_length)); + message->msg_exnow_header.msg_type = MSG_execute_immediate; + message->msg_exnow_database = rdb->rdb_handle; + message->msg_exnow_transaction = + (transaction) ? transaction->rtr_handle : NULL; + message->msg_exnow_SQL_length = length; + MOVE(string, message->msg_exnow_data + length + blr_length, length); + message->msg_exnow_SQL_dialect = dialect; + message->msg_exnow_in_blr_length = blr_length; + MOVE(blr, message->msg_exnow_data + length, blr_length); + message->msg_exnow_in_msg_type = msg_type; + message->msg_exnow_in_msg_length = msg_length; + MOVE(msg, message->msg_exnow_data, msg_length); + + if (!get_response(rdb, message, user_status, &handle, 0, 0, 0)) + if (transaction && !handle) { + release_transaction(transaction); + *rtr_handle = NULL; + } + else if (!transaction && handle) + *rtr_handle = make_transaction(rdb, handle); + + return user_status[1]; +} + + +STATUS GDS_DSQL_EXECUTE_IMMED2(STATUS * user_status, + RDB * db_handle, + RTR * rtr_handle, + USHORT length, + TEXT * string, + USHORT dialect, + USHORT in_blr_length, + UCHAR * in_blr, + USHORT in_msg_type, + USHORT in_msg_length, + UCHAR * in_msg, + USHORT out_blr_length, + UCHAR * out_blr, + USHORT out_msg_type, + USHORT out_msg_length, UCHAR * out_msg) +{ +/************************************** + * + * d s q l _ e x e c u t e _ i m m e d i a t e 2 + * + ************************************** + * + * Functional description + * Prepare and execute a statement. + * + **************************************/ + RDB rdb; + RTR transaction; + HANDLE handle; + MSG_EXNOW message; + +/* Check and validate handles, etc. */ + + rdb = *db_handle; + CHECK_HANDLE(rdb, type_rdb, gds__bad_db_handle); + if (transaction = *rtr_handle) + CHECK_HANDLE(transaction, type_rtr, gds__bad_trans_handle); + + if (!length) + length = strlen(string); + +/* set up the packet for the other guy... */ + + message = (MSG_EXNOW) CSS_alloc_message(type_msg, + (int) (sizeof(struct msg_exnow) + + length + in_blr_length + + in_msg_length + + out_blr_length)); + message->msg_exnow_header.msg_type = MSG_execute_immediate2; + message->msg_exnow_database = rdb->rdb_handle; + message->msg_exnow_transaction = + (transaction) ? transaction->rtr_handle : NULL; + message->msg_exnow_SQL_length = length; + MOVE(string, message->msg_exnow_data + length + in_blr_length, length); + message->msg_exnow_SQL_dialect = dialect; + message->msg_exnow_in_blr_length = in_blr_length; + MOVE(in_blr, message->msg_exnow_data + length, in_blr_length); + message->msg_exnow_in_msg_type = in_msg_type; + message->msg_exnow_in_msg_length = in_msg_length; + MOVE(in_msg, message->msg_exnow_data, in_msg_length); + message->msg_exnow_out_blr_length = out_blr_length; + MOVE(out_blr, + message->msg_exnow_data + length + in_blr_length + in_msg_length, + out_blr_length); + message->msg_exnow_out_msg_type = out_msg_type; + message->msg_exnow_out_msg_length = out_msg_length; + + if (!get_response + (rdb, message, user_status, &handle, out_msg_length, out_msg, 0)) + if (transaction && !handle) { + release_transaction(transaction); + *rtr_handle = NULL; + } + else if (!transaction && handle) + *rtr_handle = make_transaction(rdb, handle); + + return user_status[1]; +} + + +STATUS GDS_DSQL_FETCH(STATUS * user_status, + RSR * stmt_handle, + USHORT blr_length, + UCHAR * blr, + USHORT msg_type, USHORT msg_length, UCHAR * msg) +{ +/************************************** + * + * d s q l _ f e t c h + * + ************************************** + * + * Functional description + * Fetch next record from a dynamic SQL cursor. + * + **************************************/ + RSR statement; + MSG_SQLMSG message; + SLONG status; + +/* Check and validate handles, etc. */ + + statement = *stmt_handle; + CHECK_HANDLE(statement, type_rsr, gds__bad_req_handle); + +/* set up the packet for the other guy... */ + + message = + (MSG_SQLMSG) CSS_alloc_message(type_msg, + (int) (sizeof(struct msg_sqlmsg) + + blr_length)); + message->msg_sqlmsg_header.msg_type = MSG_fetch; + message->msg_sqlmsg_statement = statement->rsr_handle; + message->msg_sqlmsg_in_blr_length = blr_length; + MOVE(blr, message->msg_sqlmsg_data, blr_length); + message->msg_sqlmsg_in_msg_type = msg_type; + message->msg_sqlmsg_in_msg_length = msg_length; + + get_response(statement->rsr_rdb, message, user_status, &status, + msg_length, msg, 0); + + if (status == 100) + return 100; + + return user_status[1]; +} + + +STATUS GDS_DSQL_FREE(STATUS * user_status, RSR * stmt_handle, USHORT option) +{ +/************************************** + * + * d s q l _ f r e e _ s t a t e m e n t + * + ************************************** + * + * Functional description + * Release request for a Dynamic SQL statement + * + **************************************/ + RDB rdb; + RSR statement; + MSG_OP message; + HANDLE handle; + +/* Check and validate handles, etc. */ + + statement = *stmt_handle; + CHECK_HANDLE(statement, type_rsr, gds__bad_req_handle); + rdb = statement->rsr_rdb; + + message = (MSG_OP) CSS_alloc_message(type_msg, sizeof(struct msg_op)); + message->msg_op_header.msg_type = MSG_free_stmt; + message->msg_op_handle = statement->rsr_handle; + message->msg_op_level = option; + + if (get_response(rdb, message, user_status, &handle, 0, 0, 0)) + return user_status[1]; + + if (!(statement->rsr_handle = handle)) { + release_sql_request(statement); + *stmt_handle = NULL; + } + + return user_status[1]; +} + + +STATUS GDS_DSQL_INSERT(STATUS * user_status, + RSR * stmt_handle, + USHORT blr_length, + UCHAR * blr, + USHORT msg_type, USHORT msg_length, UCHAR * msg) +{ +/************************************** + * + * d s q l _ i n s e r t + * + ************************************** + * + * Functional description + * Insert next record into a dynamic SQL cursor. + * + **************************************/ + RSR statement; + MSG_SQLMSG message; + SLONG status; + +/* Check and validate handles, etc. */ + + statement = *stmt_handle; + CHECK_HANDLE(statement, type_rsr, gds__bad_req_handle); + +/* set up the packet for the other guy... */ + + message = + (MSG_SQLMSG) CSS_alloc_message(type_msg, + (int) (sizeof(struct msg_sqlmsg) + + blr_length + msg_length)); + message->msg_sqlmsg_header.msg_type = MSG_insert; + message->msg_sqlmsg_statement = statement->rsr_handle; + message->msg_sqlmsg_in_blr_length = blr_length; + MOVE(blr, message->msg_sqlmsg_data + msg_length, blr_length); + message->msg_sqlmsg_in_msg_type = msg_type; + message->msg_sqlmsg_in_msg_length = msg_length; + MOVE(msg, message->msg_sqlmsg_data, msg_length); + + return get_response(statement->rsr_rdb, message, user_status, 0, 0, 0, 0); +} + + +STATUS GDS_DSQL_PREPARE(STATUS * user_status, + RTR * rtr_handle, + RSR * stmt_handle, + USHORT length, + TEXT * string, + USHORT dialect, + USHORT item_length, + SCHAR * items, USHORT buffer_length, SCHAR * buffer) +{ +/************************************** + * + * d s q l _ p r e p a r e + * + ************************************** + * + * Functional description + * Prepare a dynamic SQL statement for execution. + * + **************************************/ + RDB rdb; + RTR transaction; + RSR statement; + MSG_PSTMT message; + +/* Check and validate handles, etc. */ + + statement = *stmt_handle; + CHECK_HANDLE(statement, type_rsr, gds__bad_req_handle); + rdb = statement->rsr_rdb; + CHECK_HANDLE(rdb, type_rdb, gds__bad_db_handle); + if (transaction = *rtr_handle) + CHECK_HANDLE(transaction, type_rtr, gds__bad_trans_handle); + + if (!length) + length = strlen(string); + +/* set up the packet for the other guy... */ + + message = + (MSG_PSTMT) CSS_alloc_message(type_msg, + (int) (sizeof(struct msg_pstmt) + + length + item_length)); + message->msg_pstmt_header.msg_type = MSG_prepare_stmt; + message->msg_pstmt_transaction = + ((transaction) ? transaction->rtr_handle : NULL); + message->msg_pstmt_statement = statement->rsr_handle; + message->msg_pstmt_SQL_length = length; + MOVE(string, message->msg_pstmt_data, length); + message->msg_pstmt_SQL_dialect = dialect; + message->msg_pstmt_item_length = item_length; + MOVE(items, message->msg_pstmt_data + length, item_length); + message->msg_pstmt_buffer_length = buffer_length; + + return get_response(rdb, message, user_status, 0, buffer_length, buffer, + 0); +} + + +STATUS GDS_DSQL_SET_CURSOR(STATUS * user_status, + RSR * stmt_handle, TEXT * cursor, USHORT type) +{ +/***************************************** + * + * d s q l _ s e t _ c u r s o r + * + ***************************************** + * + * Functional Description + * Set a cursor name for a dynamic request. + * + *****************************************/ + RSR statement; + USHORT length; + MSG_SETCUR message; + +/* Check and validate handles, etc. */ + + statement = *stmt_handle; + CHECK_HANDLE(statement, type_rsr, gds__bad_req_handle); + + length = name_length(cursor) + 1; + +/* set up the packet for the other guy... */ + + message = + (MSG_SETCUR) CSS_alloc_message(type_msg, + (int) (sizeof(struct msg_setcur) + + length - 1)); + message->msg_setcur_header.msg_type = MSG_set_cursor; + message->msg_setcur_statement = statement->rsr_handle; + MOVE(cursor, message->msg_setcur_cursor, length); + message->msg_setcur_type = type; + + return get_response(statement->rsr_rdb, message, user_status, 0, 0, 0, 0); +} + + +STATUS GDS_DSQL_SQL_INFO(STATUS * user_status, + RSR * stmt_handle, + SSHORT item_length, + SCHAR * items, SSHORT buffer_length, SCHAR * buffer) +{ +/************************************** + * + * d s q l _ s q l _ i n f o + * + ************************************** + * + * Functional description + * Provide information on sql object. + * + **************************************/ + RDB rdb; + RSR statement; + +/* Check and validate handles, etc. */ + + statement = *stmt_handle; + CHECK_HANDLE(statement, type_rsr, gds__bad_req_handle); + rdb = statement->rsr_rdb; + + return info(rdb, user_status, statement->rsr_handle, MSG_sql_info, 0, + item_length, items, buffer_length, buffer); +} + + +STATUS GDS_GET_SEGMENT(STATUS * user_status, + RBL * blob_handle, + USHORT * length, USHORT buffer_length, UCHAR * buffer) +{ +/************************************** + * + * g d s _ g e t _ s e g m e n t + * + ************************************** + * + * Functional description + * Abort a partially completed blob. + * + **************************************/ + RDB rdb; + RBL blob; + USHORT l; + UCHAR *p; + STATUS *v; + HANDLE state; + MSG_SEG message; + +/* Sniff out handles, etc, and find the various blocks. */ + + blob = *blob_handle; + CHECK_HANDLE(blob, type_rbl, gds__bad_segstr_handle); + rdb = blob->rbl_rdb; + CHECK_HANDLE(rdb, type_rdb, gds__bad_db_handle); + +/* Ask for a 1K chunk of blob and fill segment requests from it until its time to + get the next section. In other words, get a bunch, pass it out piece by piece, + then when there isn't enough left, ask for more. */ + +/* set up the status vector for the calls we're going to fake */ + + v = user_status; + *v++ = gds_arg_gds; + v[0] = 0; + v[1] = 0; + *length = 0; + +/* if we're already done, stop now */ + + if (blob->rbl_flags & RBL_eof) { + *v = gds__segstr_eof; + return user_status[1]; + } + +/* Here's the loop, passing out data from our basket & refilling it. + Our buffer (described by the structure blob) is counted strings + ... */ + + while (TRUE) { + /* If there's data to be given away, give some away (p points to the local data) */ + + if (blob->rbl_length) { + p = blob->rbl_ptr; + + /* If there was a fragment left over last time use it */ + + if (l = blob->rbl_fragment_length) + blob->rbl_fragment_length = 0; + + /* otherwise pick up the count word as the length, & decrement the local length */ + + else { + l = *p++; + l += *p++ << 8; + blob->rbl_length -= 2; + } + + /* Now check that what we've got fits. + If not, set up the fragment pointer and set the status vector */ + + if (l > buffer_length) { + blob->rbl_fragment_length = l - buffer_length; + l = buffer_length; + *v = gds__segment; + } + + /* and, just for yucks, see if we're exactly using up the fragment + part of a previous incomplete read - if so mark this as an incomplete read */ + + if (l == buffer_length && + l == blob->rbl_length && (blob->rbl_flags & RBL_segment)) + *v = gds__segment; + + /* finally set up the return length, decrement the current length, + copy the data, and indicate where to start next time. */ + + *length += l; + blob->rbl_length -= l; + buffer_length -= l; + + if (l) + do + *buffer++ = *p++; + while (--l); + + blob->rbl_ptr = p; + + /* return if we've filled up the caller's buffer, or completed a segment */ + + if (!buffer_length || + blob->rbl_length || !(blob->rbl_flags & RBL_segment)) break; + } + + /* We're done with buffer. If this was the last, we're done */ + + if (blob->rbl_flags & RBL_eof_pending) { + blob->rbl_flags |= RBL_eof; + *v = gds__segstr_eof; + break; + } + + /* We need more data. Ask for it politely */ + + message = + (MSG_SEG) CSS_alloc_message(type_msg, sizeof(struct msg_seg)); + message->msg_seg_header.msg_type = MSG_get_segment_buf; + message->msg_seg_handle = blob->rbl_handle; + message->msg_seg_buffer_length = blob->rbl_buffer_length; + + if (get_response + (rdb, message, user_status, &state, blob->rbl_buffer_length, + blob->rbl_buffer, &blob->rbl_length)) + return user_status[1]; + + blob->rbl_ptr = blob->rbl_buffer; + blob->rbl_flags &= ~RBL_segment; + if ((SLONG) state == 1) + blob->rbl_flags |= RBL_segment; + else if ((SLONG) state == 2) + blob->rbl_flags |= RBL_eof_pending; + } + + return user_status[1]; +} + + +STATUS GDS_GET_SLICE(STATUS * user_status, + RDB * db_handle, + RTR * rtr_handle, + BID array_id, + USHORT sdl_length, + UCHAR * sdl, + USHORT param_length, + UCHAR * param, + SLONG slice_length, UCHAR * slice, SLONG * return_length) +{ +/************************************** + * + * g d s _ g e t _ s l i c e + * + ************************************** + * + * Functional description + * Snatch a slice of an array. + * + **************************************/ + RDB rdb; + RTR transaction; + MSG_SLICE message; + HANDLE handle; + USHORT max_length, actual_length; + + rdb = *db_handle; + CHECK_HANDLE(rdb, type_rdb, gds__bad_db_handle); + transaction = *rtr_handle; + CHECK_HANDLE(transaction, type_rtr, gds__bad_trans_handle); + + if (!array_id->bid_relation_id && !array_id->bid_number) { + while (slice_length--) + *slice++ = 0; + *return_length = 0; + return SUCCESS; + } + +/* Since arrays can be more than 64K bytes and we limit central + server messages to that length, we must be able to retrieve + an array in pieces. The fact of multiple messages will be + transparent to the user. */ + + handle = NULL; + *return_length = 0; + + do { + message = (MSG_SLICE) CSS_alloc_message(type_msg, + (int) (sizeof + (struct msg_slice) + + sdl_length + + param_length - 1)); + message->msg_slice_header.msg_type = MSG_get_slice; + message->msg_slice_database = rdb->rdb_handle; + message->msg_slice_transaction = transaction->rtr_handle; + message->msg_slice_sdl_length = sdl_length; + message->msg_slice_param_length = param_length; + message->msg_slice_slice_length = slice_length; + MOVE(array_id, message->msg_slice_id, 8); + message->msg_slice_handle = handle; + MOVE(sdl, message->msg_slice_data, sdl_length); + MOVE(param, message->msg_slice_data + sdl_length, param_length); + + max_length = MIN(slice_length, MAX_ARRAY_MESSAGE); + if (!get_response + (rdb, message, user_status, &handle, max_length, slice, + &actual_length)) { + slice_length -= (SLONG) actual_length; + slice += actual_length; + *return_length += actual_length; + } + + /* Once one message has been sent, don't bother to send + the sdl or param arguments. */ + + sdl_length = param_length = 0; + + } while (!user_status[1] && handle); + + return user_status[1]; +} + + +#ifdef GATEWAY +#ifndef PIPE_SERVER +STATUS CSI_mdi_attach_db(STATUS * user_status, + SSHORT file_length, SCHAR * file_name, RDB * handle) +{ +/************************************** + * + * C S I _ m d i _ a t t a c h _ d b + * + ************************************** + * + * Functional description + * Create a nice, squeeky clean database, uncorrupted by user data. + * + **************************************/ + RDB rdb; + MSG_OP message; + +/* Check and validate handles, etc. */ + + rdb = *handle; + CHECK_HANDLE(rdb, type_rdb, gds__bad_db_handle); + + message = + (MSG_OP) CSS_alloc_message(type_msg, + (int) (sizeof(struct msg_op) + + file_length)); + message->msg_op_header.msg_type = MSG_mdi_attach_db; + message->msg_op_handle = rdb->rdb_handle; + message->msg_op_length = file_length; + MOVE(file_name, message->msg_op_data, file_length); + + return get_response(rdb, message, user_status, 0, 0, 0, 0); +} +#endif +#endif + + +STATUS GDS_OPEN_BLOB2(STATUS * user_status, + RDB * db_handle, + RTR * rtr_handle, + RBL * blob_handle, + BID blob_id, USHORT bpb_length, UCHAR * bpb) +{ +/************************************** + * + * g d s _ o p e n _ b l o b + * + ************************************** + * + * Functional description + * Open an existing blob. + * + **************************************/ + RDB rdb; + RTR transaction; + RBL blob; + HANDLE handle; + MSG_BLOB message; + + NULL_CHECK(blob_handle, gds__bad_segstr_handle); + rdb = *db_handle; + CHECK_HANDLE(rdb, type_rdb, gds__bad_db_handle); + transaction = *rtr_handle; + CHECK_HANDLE(transaction, type_rtr, gds__bad_trans_handle); + + message = + (MSG_BLOB) CSS_alloc_message(type_msg, + (int) (sizeof(struct msg_blob) + + bpb_length - 1)); + message->msg_blob_header.msg_type = MSG_open_blob; + message->msg_blob_database = rdb->rdb_handle; + message->msg_blob_transaction = transaction->rtr_handle; + message->msg_blob_bpb_length = bpb_length; + MOVE(bpb, message->msg_blob_bpb, bpb_length); + MOVE(blob_id, message->msg_blob_id, 8); + + if (!get_response(rdb, message, user_status, &handle, 8, blob_id, 0)) { + *blob_handle = blob = + (RBL) ALLOC(type_rbl, sizeof(struct rbl) + BLOB_LENGTH); + blob->rbl_handle = handle; + blob->rbl_buffer_length = BLOB_LENGTH; + blob->rbl_rdb = rdb; + blob->rbl_rtr = transaction; + blob->rbl_ptr = blob->rbl_buffer; + blob->rbl_next = transaction->rtr_blobs; + transaction->rtr_blobs = blob; + } + + return user_status[1]; +} + + +#ifdef PIPE_SERVER +STATUS GDS_OPEN_BLOB(STATUS * user_status, + RDB * db_handle, + RTR * rtr_handle, RBL * blob_handle, BID blob_id) +{ +/************************************** + * + * g d s _ o p e n _ b l o b + * + ************************************** + * + * Functional description + * Open an existing blob. + * + **************************************/ + + return GDS_OPEN_BLOB2(user_status, db_handle, rtr_handle, blob_handle, + blob_id, 0, NULL); +} +#endif + + +STATUS GDS_PREPARE(STATUS * user_status, + RTR * rtr_handle, USHORT buffer_length, UCHAR * buffer) +{ +/************************************** + * + * g d s _ p r e p a r e + * + ************************************** + * + * Functional description + * Prepare a transaction for commit. First phase of a two + * phase commit. + * + **************************************/ + RDB rdb; + RTR transaction; + MSG_OP message; + + transaction = *rtr_handle; + CHECK_HANDLE(transaction, type_rtr, gds__bad_trans_handle); + rdb = transaction->rtr_rdb; + CHECK_HANDLE(rdb, type_rdb, gds__bad_db_handle); + + message = + (MSG_OP) CSS_alloc_message(type_msg, + (int) (sizeof(struct msg_op) + + buffer_length)); + message->msg_op_header.msg_type = MSG_prepare; + message->msg_op_handle = transaction->rtr_handle; + message->msg_op_length = buffer_length; + MOVE(buffer, message->msg_op_data, buffer_length); + + return get_response(rdb, message, user_status, 0, 0, 0, 0); +} + + +STATUS GDS_PUT_SEGMENT(STATUS * user_status, + RBL * blob_handle, + USHORT buffer_length, UCHAR * buffer) +{ +/************************************** + * + * g d s _ p u t _ s e g m e n t + * + ************************************** + * + * Functional description + * Abort a partially completed blob. + * + **************************************/ + RDB rdb; + RBL blob; + USHORT l; + UCHAR *p; + +/* Sniff out handles, etc, and find the various blocks. */ + + blob = *blob_handle; + CHECK_HANDLE(blob, type_rbl, gds__bad_segstr_handle); + rdb = blob->rbl_rdb; + CHECK_HANDLE(rdb, type_rdb, gds__bad_db_handle); + +/* set up the status vector for the calls we're going to fake */ + + user_status[0] = gds_arg_gds; + user_status[1] = 0; + user_status[2] = gds_arg_end; + +/* If the buffer is too full to even hold the length of the incoming + segment, flush out the old buffer. If the segment is too large + to fit into the blob buffer, just send it as a single segment. */ + + p = blob->rbl_ptr; + l = blob->rbl_buffer_length - (p - blob->rbl_buffer); + + if (buffer_length + 2 > l) { + if (blob->rbl_ptr > blob->rbl_buffer) + send_blob(user_status, blob, 0, NULL); + if (buffer_length + 2 > blob->rbl_buffer_length) + return send_blob(user_status, blob, buffer_length, buffer); + p = blob->rbl_buffer; + } + +/* Move segment length and data into blob buffer */ + + *p++ = buffer_length; + *p++ = buffer_length >> 8; + + if (buffer_length) + do + *p++ = *buffer++; + while (--buffer_length); + + blob->rbl_ptr = p; + + return user_status[1]; +} + + +STATUS GDS_PUT_SLICE(STATUS * user_status, + RDB * db_handle, + RTR * rtr_handle, + BID array_id, + USHORT sdl_length, + UCHAR * sdl, + USHORT param_length, + UCHAR * param, SLONG slice_length, UCHAR * slice) +{ +/************************************** + * + * g d s _ p u t _ s l i c e + * + ************************************** + * + * Functional description + * Snatch a slice of an array. + * + **************************************/ + RDB rdb; + RTR transaction; + MSG_SLICE message; + HANDLE handle; + USHORT length, use_sdl_length, use_param_length; + + rdb = *db_handle; + CHECK_HANDLE(rdb, type_rdb, gds__bad_db_handle); + transaction = *rtr_handle; + CHECK_HANDLE(transaction, type_rtr, gds__bad_trans_handle); + +/* Since arrays can be more than 64K bytes and we limit central + server messages to that length, we must be able to send + an array in pieces. The fact of multiple messages will be + transparent to the user. */ + + handle = NULL; + + do { + if ((length = MIN(slice_length, MAX_ARRAY_MESSAGE)) < slice_length) + use_sdl_length = use_param_length = 0; + else { + use_sdl_length = sdl_length; + use_param_length = param_length; + } + + message = (MSG_SLICE) CSS_alloc_message(type_msg, + (int) (sizeof + (struct msg_slice) + + use_sdl_length + + use_param_length + + length - 1)); + message->msg_slice_header.msg_type = MSG_put_slice; + message->msg_slice_database = rdb->rdb_handle; + message->msg_slice_transaction = transaction->rtr_handle; + message->msg_slice_sdl_length = use_sdl_length; + message->msg_slice_param_length = use_param_length; + message->msg_slice_slice_length = slice_length; + MOVE(array_id, message->msg_slice_id, 8); + message->msg_slice_handle = handle; + MOVE(sdl, message->msg_slice_data, use_sdl_length); + MOVE(param, message->msg_slice_data + use_sdl_length, + use_param_length); + + MOVE(slice, + message->msg_slice_data + use_sdl_length + use_param_length, + length); + + if (!get_response(rdb, message, user_status, &handle, 8, array_id, 0)) { + slice_length -= (SLONG) length; + slice += length; + } + + } while (!user_status[1] && handle); + + return user_status[1]; +} + + +STATUS GDS_QUE_EVENTS(STATUS * user_status, + RDB * handle, + SLONG * id, + SSHORT length, UCHAR * items, void (*ast) (), void *arg) +{ +/************************************** + * + * g d s _ $ q u e _ e v e n t s + * + ************************************** + * + * Functional description + * Que a request for event notification. + * + **************************************/ + MSG_EVENT message; + MSG_OP connect; + MSG_RESP response; + RDB rdb; + + rdb = *handle; + CHECK_HANDLE(rdb, type_rdb, gds__bad_db_handle); + + if (!rdb->rdb_connection2) { + rdb->rdb_connection2 = CSS_connect(rdb->rdb_server); + connect = + (MSG_OP) CSS_alloc_message(type_msg, + (int) (sizeof(struct msg_op) + + length)); + connect->msg_op_header.msg_type = MSG_alt_connection; + connect->msg_op_handle = rdb->rdb_handle; + CSS_put_message(rdb->rdb_connection2, connect, 0); + response = CSS_get_message(rdb->rdb_connection2, 0, 0); + CSS_free_global(response); +#ifndef GATEWAY +#ifdef MULTI_THREAD + gds__thread_start(event_thread, rdb->rdb_connection2); +#else + ISC_signal(EVENT_SIGNAL, event_handler, rdb->rdb_connection2); +#endif +#endif + } + + message = + (MSG_EVENT) CSS_alloc_message(type_msg, + (int) (sizeof(struct msg_event) + + length)); + message->msg_event_header.msg_type = MSG_que_events; + message->msg_event_database = rdb->rdb_handle; + message->msg_event_ast = ast; + message->msg_event_arg = arg; + message->msg_event_length = length; + MOVE(items, message->msg_event_data, length); + + return get_response(rdb, message, user_status, id, 0, 0, 0); +} + + +#ifdef GATEWAY +#ifndef PIPE_SERVER +STATUS CSI_query_connect(STATUS * user_status, + SSHORT file_length, + SCHAR * file_name, + RDB * handle, + TEXT * connect_method, SSHORT * connect_length) +{ +/************************************** + * + * C S I _ q u e r y _ c o n n e c t + * + ************************************** + * + * Functional description + * Attach to a central server and return the "connection method". + * + **************************************/ + RDB rdb; + + NULL_CHECK(handle, gds__bad_db_handle); + + if (rdb = init(user_status, MSG_query_connect, + file_name, file_length, 0, 0, file_name, + PRB_server_t1, PRB_client_t1, connect_method, + connect_length)) *handle = rdb; + + return user_status[1]; +} +#endif +#endif + + +STATUS GDS_RECEIVE(STATUS * user_status, + RRQ * req_handle, + USHORT msg_type, + USHORT msg_length, UCHAR * msg, SSHORT level) +{ +/************************************** + * + * g d s _ R E C E I V E + * + ************************************** + * + * Functional description + * Get a record from the host program. + * + **************************************/ + RRQ request; + RTR transaction; + RDB rdb; + MSG_MSG message; + + request = *req_handle; + CHECK_HANDLE(request, type_rrq, gds__bad_req_handle); + rdb = request->rrq_rdb; + CHECK_HANDLE(rdb, type_rdb, gds__bad_db_handle); + + message = (MSG_MSG) CSS_alloc_message(type_msg, sizeof(struct msg_msg)); + message->msg_msg_header.msg_type = MSG_receive; + message->msg_msg_request = request->rrq_handle; + message->msg_msg_length = msg_length; + message->msg_msg_level = level; + message->msg_msg_type = msg_type; + + return get_response(rdb, message, user_status, 0, msg_length, msg, 0); +} + + +STATUS GDS_RECONNECT(STATUS * user_status, + RDB * db_handle, + RTR * rtr_handle, SSHORT length, UCHAR * id) +{ +/************************************** + * + * G D S _ R E C O N N E C T + * + ************************************** + * + * Functional description + * Connect to a transaction in limbo. + * + **************************************/ + RDB rdb; + RTR transaction; + HANDLE handle; + MSG_OP message; + + NULL_CHECK(rtr_handle, gds__bad_trans_handle); + rdb = *db_handle; + CHECK_HANDLE(rdb, type_rdb, gds__bad_db_handle); + + message = + (MSG_OP) CSS_alloc_message(type_msg, + (int) (sizeof(struct msg_op) + length)); + message->msg_op_header.msg_type = MSG_reconnect; + message->msg_op_handle = rdb->rdb_handle; + message->msg_op_length = length; + MOVE(id, message->msg_op_data, length); + + if (!get_response(rdb, message, user_status, &handle, 0, 0, 0)) + *rtr_handle = make_transaction(rdb, handle); + + return user_status[1]; +} + + +STATUS GDS_RELEASE_REQUEST(STATUS * user_status, RRQ * req_handle) +{ +/************************************** + * + * g d s _ R E L E A S E _ R E Q U E S T + * + ************************************** + * + * Functional description + * Release a request. + * + **************************************/ + RDB rdb; + RRQ request; + + request = *req_handle; + CHECK_HANDLE(request, type_rrq, gds__bad_req_handle); + rdb = request->rrq_rdb; + CHECK_HANDLE(rdb, type_rdb, gds__bad_db_handle); + + if (!release_object(rdb, MSG_release, request->rrq_handle, user_status)) { + release_request(request); + *req_handle = NULL; + } + + return user_status[1]; +} + + +STATUS GDS_REQUEST_INFO(STATUS * user_status, + RRQ * request, + USHORT level, + SSHORT item_length, + UCHAR * items, SSHORT buffer_length, UCHAR * buffer) +{ +/************************************** + * + * g d s _ r e q u e s t _ i n f o + * + ************************************** + * + * Functional description + * Provide information on request object. + * + **************************************/ + RDB rdb; + + CHECK_HANDLE((*request), type_rrq, gds__bad_req_handle); + rdb = (*request)->rrq_rdb; + CHECK_HANDLE(rdb, type_rdb, gds__bad_db_handle); + + return info(rdb, user_status, (*request)->rrq_handle, MSG_request_info, + level, item_length, items, buffer_length, buffer); +} + + +STATUS GDS_ROLLBACK(STATUS * user_status, RTR * rtr_handle) +{ +/************************************** + * + * g d s _ R O L L B A C K + * + ************************************** + * + * Functional description + * Abort a transaction. + * + **************************************/ + RDB rdb; + RTR transaction; + + transaction = *rtr_handle; + CHECK_HANDLE(transaction, type_rtr, gds__bad_trans_handle); + rdb = transaction->rtr_rdb; + CHECK_HANDLE(rdb, type_rdb, gds__bad_db_handle); + + if (!release_object + (rdb, MSG_rollback, transaction->rtr_handle, user_status)) { + release_transaction(transaction); + *rtr_handle = NULL; + } + + return user_status[1]; +} + + +STATUS GDS_SEND(STATUS * user_status, + RRQ * req_handle, + USHORT msg_type, USHORT msg_length, UCHAR * msg, SSHORT level) +{ +/************************************** + * + * g d s _ s e n d + * + ************************************** + * + * Functional description + * Get a record from the host program. + * + **************************************/ + RRQ request; + RTR transaction; + RDB rdb; + MSG_MSG message; + + request = *req_handle; + CHECK_HANDLE(request, type_rrq, gds__bad_req_handle); + rdb = request->rrq_rdb; + CHECK_HANDLE(rdb, type_rdb, gds__bad_db_handle); + + message = + (MSG_MSG) CSS_alloc_message(type_msg, + (int) (sizeof(struct msg_msg) + + msg_length)); + message->msg_msg_header.msg_type = MSG_send; + message->msg_msg_request = request->rrq_handle; + message->msg_msg_length = msg_length; + message->msg_msg_level = level; + message->msg_msg_type = msg_type; + MOVE(msg, message->msg_msg_data, msg_length); + + return get_response(rdb, message, user_status, 0, 0, 0, 0); +} + + +STATUS GDS_SEEK_BLOB(STATUS * user_status, + RBL * blob_handle, + SSHORT mode, SLONG offset, SLONG * result) +{ +/************************************** + * + * g d s _ s e e k _ b l o b + * + ************************************** + * + * Functional description + * Abort a partially completed blob. + * + **************************************/ + RDB rdb; + RBL blob; + MSG_SEEK message; + +/* Sniff out handles, etc, and find the various blocks. */ + + blob = *blob_handle; + CHECK_HANDLE(blob, type_rbl, gds__bad_segstr_handle); + rdb = blob->rbl_rdb; + CHECK_HANDLE(rdb, type_rdb, gds__bad_db_handle); + + message = (MSG_SEEK) CSS_alloc_message(type_msg, sizeof(struct msg_seek)); + message->msg_seek_header.msg_type = MSG_seek_blob; + message->msg_seek_handle = blob->rbl_handle; + message->msg_seek_mode = mode; + message->msg_seek_offset = offset; + + return get_response(rdb, message, user_status, 0, sizeof(*result), result, + 0); +} + + + +STATUS GDS_START_AND_SEND(STATUS * user_status, + RRQ * req_handle, + RTR * rtr_handle, + USHORT msg_type, + USHORT msg_length, UCHAR * msg, SSHORT level) +{ +/************************************** + * + * g d s _ s t a r t _ a n d _ s e n d + * + ************************************** + * + * Functional description + * Get a record from the host program. + * + **************************************/ + RRQ request; + RTR transaction; + RDB rdb; + MSG_MSG message; + + request = *req_handle; + CHECK_HANDLE(request, type_rrq, gds__bad_req_handle); + transaction = *rtr_handle; + CHECK_HANDLE(transaction, type_rtr, gds__bad_trans_handle); + rdb = request->rrq_rdb; + CHECK_HANDLE(rdb, type_rdb, gds__bad_db_handle); + + message = + (MSG_MSG) CSS_alloc_message(type_msg, + (int) (sizeof(struct msg_msg) + + msg_length)); + message->msg_msg_header.msg_type = MSG_start_and_send; + message->msg_msg_request = request->rrq_handle; + message->msg_msg_transaction = transaction->rtr_handle; + message->msg_msg_length = msg_length; + message->msg_msg_level = level; + message->msg_msg_type = msg_type; + MOVE(msg, message->msg_msg_data, msg_length); + + return get_response(rdb, message, user_status, 0, 0, 0, 0); +} + + +STATUS GDS_START(STATUS * user_status, + register RRQ * req_handle, + register RTR * rtr_handle, SSHORT level) +{ +/************************************** + * + * g d s _ S T A R T + * + ************************************** + * + * Functional description + * Get a record from the host program. + * + **************************************/ + RRQ request; + RTR transaction; + RDB rdb; + MSG_MSG message; + + request = *req_handle; + CHECK_HANDLE(request, type_rrq, gds__bad_req_handle); + transaction = *rtr_handle; + CHECK_HANDLE(transaction, type_rtr, gds__bad_trans_handle); + rdb = request->rrq_rdb; + CHECK_HANDLE(rdb, type_rdb, gds__bad_db_handle); + + message = (MSG_MSG) CSS_alloc_message(type_msg, sizeof(struct msg_msg)); + message->msg_msg_header.msg_type = MSG_start_request; + message->msg_msg_request = request->rrq_handle; + message->msg_msg_transaction = transaction->rtr_handle; + message->msg_msg_level = level; + + return get_response(rdb, message, user_status, 0, 0, 0, 0); +} + + +STATUS GDS_START_TRANSACTION(STATUS * user_status, + RTR * tra_handle, SSHORT count, ...) +{ +/************************************** + * + * g d s _ $ s t a r t _ t r a n s a c t i o n + * + ************************************** + * + * Functional description + * Start a transaction. + * + **************************************/ + TEB tebs[16], *teb, *end; + va_list ptr; + + teb = tebs; + end = teb + count; + VA_START(ptr, count); + + for (; teb < end; teb++) { + teb->teb_database = va_arg(ptr, RDB *); + teb->teb_tpb_length = va_arg(ptr, int); + teb->teb_tpb = va_arg(ptr, UCHAR *); + } + + return GDS_START_MULTIPLE(user_status, tra_handle, count, tebs); +} + + +STATUS GDS_TRANSACT_REQUEST(STATUS * user_status, + RDB * db_handle, + RTR * tra_handle, + USHORT blr_length, + UCHAR * blr, + USHORT in_msg_length, + UCHAR * in_msg, + USHORT out_msg_length, UCHAR * out_msg) +{ +/************************************** + * + * i s c _ t r a n s a c t _ r e q u e s t + * + ************************************** + * + * Functional description + * Execute a stored procedure. + * + **************************************/ + RTR transaction; + RDB rdb; + MSG_TRRQ message; + HANDLE handle; + +/* Check and validate handles, etc. */ + + rdb = *db_handle; + CHECK_HANDLE(rdb, type_rdb, gds__bad_db_handle); + if (transaction = *tra_handle) + CHECK_HANDLE(transaction, type_rtr, gds__bad_trans_handle); + +/* set up the packet for the other guy... */ + + message = + (MSG_TRRQ) CSS_alloc_message(type_msg, blr_length + in_msg_length); + message->msg_trrq_header.msg_type = MSG_transact_request; + message->msg_trrq_database = rdb->rdb_handle; + message->msg_trrq_transaction = + (transaction) ? transaction->rtr_handle : NULL; + message->msg_trrq_blr_length = blr_length; + MOVE(blr, message->msg_trrq_data, blr_length); + message->msg_trrq_in_msg_length = in_msg_length; + MOVE(in_msg, message->msg_trrq_data + blr_length, in_msg_length); + message->msg_trrq_out_msg_length = out_msg_length; + + if (!get_response + (rdb, message, user_status, &handle, out_msg_length, out_msg, 0)) + if (transaction && !handle) { + release_transaction(transaction); + *tra_handle = NULL; + } + else if (!transaction && handle) + *tra_handle = make_transaction(rdb, handle); + + return user_status[1]; +} + + +STATUS GDS_TRANSACTION_INFO(STATUS * user_status, + RTR * transaction, + SSHORT item_length, + UCHAR * items, + SSHORT buffer_length, UCHAR * buffer) +{ +/************************************** + * + * g d s _ t r a n s a c t i o n _ i n f o + * + ************************************** + * + * Functional description + * Provide information on transaction object. + * + **************************************/ + RDB rdb; + + CHECK_HANDLE((*transaction), type_rtr, gds__bad_trans_handle); + rdb = (*transaction)->rtr_rdb; + CHECK_HANDLE(rdb, type_rdb, gds__bad_db_handle); + + return info(rdb, user_status, (*transaction)->rtr_handle, + MSG_transaction_info, 0, item_length, items, buffer_length, + buffer); +} + + +STATUS GDS_UNWIND(STATUS * user_status, RRQ * req_handle, SSHORT level) +{ +/************************************** + * + * g d s _ U N W I N D + * + ************************************** + * + * Functional description + * Unwind a running request. + * + **************************************/ + RDB rdb; + RRQ request; + MSG_OP message; + + request = *req_handle; + CHECK_HANDLE(request, type_rrq, gds__bad_req_handle); + rdb = request->rrq_rdb; + CHECK_HANDLE(rdb, type_rdb, gds__bad_db_handle); + + user_status[0] = gds_arg_gds; + user_status[1] = 0; + user_status[2] = 0; + +#ifdef GATEWAY +#ifndef PIPE_SERVER + message = (MSG_OP) CSS_alloc_message(type_msg, sizeof(struct msg_op)); + message->msg_op_header.msg_type = MSG_unwind; + message->msg_op_handle = request->rrq_handle; + message->msg_op_level = level; + + get_response(rdb, message, user_status, 0, 0, 0, 0); +#endif +#endif + + return user_status[1]; +} + + +#ifndef MULTI_THREAD +static void event_handler( PTR connection) +{ +/************************************** + * + * e v e n t _ h a n d l e r + * + ************************************** + * + * Functional description + * Wait for something to happen. + * + **************************************/ + MSG_EVENT message; + + if (!(message = (MSG_EVENT) CSS_get_message(connection, 0, -1)) || + message->msg_event_header.msg_type != MSG_event) + return; + + (*message->msg_event_ast) (message->msg_event_arg, + message->msg_event_length, + message->msg_event_data); + + CSS_free_global(message); +} +#endif + + +#ifdef MULTI_THREAD +static void event_thread( PTR connection) +{ +/************************************** + * + * e v e n t _ t h r e a d + * + ************************************** + * + * Functional description + * Wait for something to happen. + * + **************************************/ + MSG_EVENT message; + + THREAD_ENTER; + + for (;;) { + if (!(message = (MSG_EVENT) CSS_get_message(connection, 0, 0)) || + message->msg_event_header.msg_type != MSG_event) + break; + (*message->msg_event_ast) (message->msg_event_arg, + message->msg_event_length, + message->msg_event_data); + CSS_free_global(message); + } +} +#endif + + +static STATUS get_response( + RDB rdb, + MSG message, + STATUS * user_status, + HANDLE * handle, +USHORT buffer_length, UCHAR * buffer, USHORT * return_length) +{ +/************************************** + * + * g e t _ r e s p o n s e + * + ************************************** + * + * Functional description + * Send message to server, await and process reply. + * + **************************************/ + MSG_RESP response; + + if (CSS_put_message(rdb->rdb_connection, message, 0)) + response = CSS_get_message(rdb->rdb_connection, 0, 0); + else + response = NULL; + + return process_response(response, user_status, handle, buffer_length, + buffer, return_length); +} + + +static STATUS handle_error( STATUS * user_status, STATUS code) +{ +/************************************** + * + * h a n d l e _ e r r o r + * + ************************************** + * + * Functional description + * An invalid handle has been passed in. If there is a user status + * vector, make it reflect the error. If not, emulate the routine + * "error" and abort. + * + **************************************/ + STATUS local_status[20]; + STATUS *vector; + + vector = (user_status) ? user_status : local_status; + vector[0] = 1; + vector[1] = code; + vector[2] = 0; + + return vector[1]; +} + + +static STATUS info( + RDB rdb, + STATUS * user_status, + HANDLE handle, + MSG_T type, + USHORT level, +USHORT item_length, UCHAR * items, USHORT buffer_length, UCHAR * buffer) +{ +/************************************** + * + * i n f o + * + ************************************** + * + * Functional description + * Generic information call. + * + **************************************/ + MSG_INFO message; + + message = + (MSG_INFO) CSS_alloc_message(type_msg, + (int) (sizeof(struct msg_info) + + item_length)); + message->msg_info_header.msg_type = type; + message->msg_info_handle = handle; + message->msg_info_level = level; + message->msg_info_length = item_length; + message->msg_info_buffer_length = buffer_length; + MOVE(items, message->msg_info_data, item_length); + + return get_response(rdb, message, user_status, 0, buffer_length, buffer, + 0); +} + + +#ifndef PIPE_SERVER +static RDB init( + STATUS * user_status, + MSG_T type, + UCHAR * file_name, + USHORT file_length, + UCHAR * dpb, +USHORT dpb_length, +TEXT * expanded_filename, +USHORT server_flags, +USHORT client_flags, TEXT * buffer, USHORT * buffer_length) +{ +/************************************** + * + * i n i t ( c e n t r a l _ s e r v e r ) + * + ************************************** + * + * Functional description + * Initialize for database access. First call from both CREATE and + * OPEN. + * + **************************************/ + MSG_ATT message; + MSG_RESP response; + RDB rdb; + SRQ *que; + PRB process; + PTR connection, server, client; + CSH CSS_header; + HANDLE handle; + USHORT expanded_length, length; + SLONG process_numbers[256], *ptr, *end; + + if (!CSS_init(user_status, FALSE)) { + *user_status++ = gds_arg_gds; + *user_status++ = gds__unavailable; + *user_status = 0; + return NULL; + } + + if (!file_length) + file_length = strlen(file_name); + + expanded_length = strlen(expanded_filename); + + client_flags |= PRB_client; + server_flags |= PRB_server; + + client = CSS_create_process(client_flags); + +/* Search for potential cooperative server processes. Keep track of their + server numbers until later. */ + + CSS_header = ACQUIRE; + CSS_probe_processes(); + end = process_numbers; + + QUE_LOOP(CSS_header->csh_processes, que) { + process = (PRB) ((UCHAR *) que - OFFSET(PRB, prb_processes)); + if (client != REL_PTR(process) && + (process->prb_flags & server_flags) == server_flags && + process->prb_protocol_version == CSI_PROTOCOL_VERSION) + *end++ = process->prb_process_number; + } + + RELEASE; + +/* We've got the candidate servers numbers. Now go back and talk to them */ + + for (ptr = process_numbers; ptr < end; ptr++) { + if (server = CSS_find_process(*ptr)) { + connection = CSS_connect(server); + length = + sizeof(struct msg_att) + file_length + expanded_length + + dpb_length; + message = (MSG_ATT) CSS_alloc_message(type_msg, (int) length); + message->msg_att_header.msg_type = type; + message->msg_att_dpb_length = dpb_length; + message->msg_att_file_length = file_length; + message->msg_att_expanded_length = expanded_length; + MOVE(file_name, message->msg_att_data, file_length); + MOVE(expanded_filename, message->msg_att_data + file_length, + expanded_length); + MOVE(dpb, message->msg_att_data + file_length + expanded_length, + dpb_length); + if (CSS_put_message(connection, message, 0) + && (response = CSS_get_message(connection, 0, 0)) && +#ifdef GATEWAY + !process_response(response, user_status, &handle, + *buffer_length, buffer, buffer_length)) +#else + !process_response(response, user_status, &handle, 0, 0, 0)) +#endif + { + rdb = (RDB) ALLOC(type_rdb, sizeof(struct rdb)); + rdb->rdb_handle = handle; + rdb->rdb_connection = connection; + rdb->rdb_server = server; + return rdb; + } + CSS_disconnect(connection); + } + } + + return NULL; +} +#endif + + +#ifdef PIPE_SERVER +#ifndef GATEWAY +static RDB init( + STATUS * user_status, + MSG_T type, + UCHAR * file_name, + USHORT file_length, + UCHAR * dpb, +USHORT dpb_length, +TEXT * expanded_filename, +USHORT server_flags, +USHORT client_flags, TEXT * buffer, USHORT * buffer_length) +{ +/************************************** + * + * i n i t ( p i p e _ s e r v e r ) + * + ************************************** + * + * Functional description + * Initialize for database access. First call from both CREATE and + * OPEN. + * + **************************************/ + MSG_ATT message; + MSG_RESP response; + RDB rdb; + SRQ *que; + PRB process; + PTR connection, client; + CSH CSS_header; + HANDLE handle; + USHORT expanded_length, length; + TEXT local_expanded[256], pipe_xl[64], version[16], var_name[16]; + SLONG status, len, itemnums[4], items[4], isc_level, var_type; + STATUS local_status[20]; + +/* Assume that we don't want a PIPE. */ + + user_status[0] = gds_arg_gds; + user_status[1] = gds__unavailable; + user_status[2] = 0; + +/* If the user has explicitly asked for a local attachment, skip the pipe */ + + if (! + (length = + strncmp(expanded_filename, AM_SENTINEL, sizeof(AM_SENTINEL) - 1)) +|| !CSS_init(local_status, FALSE, 0)) { + if (!length) { + /* Re-expand the filename, minus the sentinel */ + + strcpy(local_expanded, + expanded_filename + sizeof(AM_SENTINEL) - 1); + ISC_expand_filename(local_expanded, strlen(local_expanded), + expanded_filename); + } + return NULL; + } + +/* See if the user explicitly requested a pipe attachment! */ + + if (!strncmp(expanded_filename, PIPE_SENTINEL, sizeof(PIPE_SENTINEL) - 1)) { + /* Re-expand the filename, minus the sentinel */ + + expanded_filename += sizeof(PIPE_SENTINEL) - 1; + ISC_expand_filename(expanded_filename, strlen(expanded_filename), + local_expanded); + expanded_filename = local_expanded; + } + else { + /* Don't use a pipe for non-interactive attachments */ + + HPCIGETVAR("HPINTERACTIVE", &status, VAR_BOOL_VALUE, &isc_level, + VAR_TYPE, &var_type); + if (!status && var_type == VAR_BOOL_VALUE && isc_level == 0) + return NULL; + + /* Also don't use a pipe when variable ISC_LOCAL[ISC_UDCLVL] is true */ + + HPCIGETVAR("ISC_UDCLVL", &status, VAR_INT_VALUE, &isc_level, VAR_TYPE, + &var_type); + if (!status && var_type == VAR_INT_VALUE) { + sprintf(var_name, "ISC_LOCAL%d", isc_level); + HPCIGETVAR(var_name, &status, VAR_BOOL_VALUE, &isc_level, + VAR_TYPE, &var_type); + if (!status && var_type == VAR_BOOL_VALUE && isc_level == 1) + return NULL; + } + } + + if (!file_length) + file_length = strlen(file_name); + + expanded_length = strlen(expanded_filename); + client_flags |= PRB_client; + client = CSS_create_process(client_flags); + +/* Check to see if child process has already been created. + If so, make sure it is still alive. */ + + if (child_pin) { + GETPROCINFO(child_pin); + if (ccode() != CCE) + child_pin = 0; + } + +/* Create a child process if one doesn't yet exist. */ + + if (child_pin) + connection = CSS_connect(pipe_server); + else { + /* Create a program fid and an XL fid for the CREATEPROCESS call */ + + version[0] = 0; + HPCIGETVAR("ISC_UDCLVL", &status, VAR_INT_VALUE, &isc_level, VAR_TYPE, + &var_type); + if (!status && var_type == VAR_INT_VALUE) { + sprintf(var_name, "ISC_VERS%d", isc_level); + HPCIGETVAR(var_name, &status, VAR_STR_VALUE, version, VAR_LENGTH, + &len); + if (!status && len) + version[len] = 0; + } + + sprintf(pipe_program, GDS_PIPE, version); + + HPMYFILE(pipe_xl, &status, &len); + if (!status && len + && !strncmp(pipe_xl, " GDSLIB.", sizeof(" GDSLIB.") - 1)) + pipe_xl[len - 1] = 0; + else + pipe_xl[0] = pipe_xl[1] = 0; + + /* Create an item list for CREATEPROCESS. The XL items must be at the + end because if we can't lookup the XL name we don't pass one in. */ + + itemnums[0] = CP_ACT; + items[0] = 0; + itemnums[1] = pipe_xl[0] ? CP_XL : ITM_END; + items[1] = (SLONG) & pipe_xl[1]; + itemnums[2] = CP_XLLEN; + items[2] = strlen(items[1]); + itemnums[3] = ITM_END; + + CREATEPROCESS(&status, &child_pin, pipe_program, itemnums, items); + if (ccode() == CCL) { + *user_status++ = gds_arg_gds; + *user_status++ = gds__sys_request; + *user_status++ = gds_arg_string; + *user_status++ = (STATUS) "CREATEPROCESS"; + *user_status++ = gds_arg_string; + *user_status++ = (STATUS) pipe_program; + *user_status = gds_arg_end; + return NULL; + } + + gds__register_cleanup(exit_handler, 0); + + /* Wait for a message from our dear child. */ + + while (!(response = CSS_get_message((SLONG) 0, 0, 10))) { + GETPROCINFO(child_pin); + if (ccode() != CCE) { + child_pin = 0; + return NULL; + } + } + + connection = response->msg_resp_header.msg_connection; + CSS_free_global(response); + + /* Search for the child process. */ + + CSS_header = ACQUIRE; + pipe_server = 0; + pipe_util_cnct = 0; + + QUE_LOOP(CSS_header->csh_processes, que) { + process = (PRB) ((UCHAR *) que - OFFSET(PRB, prb_processes)); + if (client != REL_PTR(process) && + process->prb_protocol_version == CSI_PROTOCOL_VERSION && + process->prb_process_id == child_pin) { + pipe_server = REL_PTR(process); + break; + } + } + + RELEASE; + } + +/* We've got a candidate server. Now go back and talk to it. */ + + if (pipe_server) { + length = + sizeof(struct msg_att) + file_length + expanded_length + + dpb_length; + message = (MSG_ATT) CSS_alloc_message(type_msg, (int) length); + message->msg_att_header.msg_type = type; + message->msg_att_dpb_length = dpb_length; + message->msg_att_file_length = file_length; + message->msg_att_expanded_length = expanded_length; + MOVE(file_name, message->msg_att_data, file_length); + MOVE(expanded_filename, message->msg_att_data + file_length, + expanded_length); + MOVE(dpb, message->msg_att_data + file_length + expanded_length, + dpb_length); + if (CSS_put_message(connection, message, 0) + && (response = CSS_get_message(connection, 0, 0)) + && !process_response(response, user_status, &handle, 0, 0, 0)) { + rdb = (RDB) ALLOC(type_rdb, sizeof(struct rdb)); + rdb->rdb_handle = handle; + rdb->rdb_connection = connection; + rdb->rdb_server = pipe_server; + + /* At all times, keep a utility connection open to the pipe server. + This is for the sake of efficiency. It will force the port to + remain open. */ + + if (!pipe_util_cnct) { + pipe_util_cnct = CSS_connect(pipe_server); + message = + (MSG_ATT) CSS_alloc_message(type_msg, + sizeof(struct msg_util)); + ((MSG_UTIL) message)->msg_util_header.msg_type = MSG_util_cmd; + ((MSG_UTIL) message)->msg_util_cmd = UTIL_noop; + if (CSS_put_message(pipe_util_cnct, message, 0) && + (response = CSS_get_message(pipe_util_cnct, 0, 0))) + CSS_free_global(response); + else { + CSS_disconnect(pipe_util_cnct); + pipe_util_cnct = 0; + } + } + + return rdb; + } + CSS_disconnect(connection); + } + + return NULL; +} +#endif + + +#ifdef GATEWAY +static RDB init( + STATUS * user_status, + MSG_T type, + UCHAR * file_name, + USHORT file_length, + UCHAR * dpb, USHORT dpb_length, TEXT * expanded_filename) +{ +/************************************** + * + * i n i t ( g a t e w a y _ p i p e _ s e r v e r ) + * + ************************************** + * + * Functional description + * Initialize for database access. First call from both CREATE and + * OPEN. + * + **************************************/ + MSG_ATT message; + MSG_RESP response; + RDB rdb; + SRQ *que; + PRB process; + PTR connection, server, client; + CSH CSS_header; + HANDLE handle; + UCHAR gbl_file[16], csi_file[256], *p, *q; + USHORT expanded_length, length; + int pid; + + sprintf(gbl_file, "ISC_%08lx", getpid()); + q = csi_file - 1; + if (ISC_expand_logical_once + ("SYS$LOGIN", sizeof("SYS$LOGIN") - 1, csi_file)) { + for (p = csi_file; *p; p++) + if (*p == ':' || *p == ']') + q = p; + if (q < csi_file) + *(q = p) = ':'; + } + strcpy(++q, gbl_file); +#ifdef ORACLE_ALT + strcat(q, "_ALT"); +#endif + + if (!CSS_init(user_status, TRUE, csi_file)) { + *user_status++ = gds_arg_gds; + *user_status++ = gds__unavailable; + *user_status = 0; + return NULL; + } + + if (!file_length) + file_length = strlen(file_name); + + expanded_length = strlen(expanded_filename); + + client = CSS_create_process(PRB_server); + CSS_header = ACQUIRE; + +/* Try to find a user on the free list whose connection is + still around. Delete any that aren't. */ + + CSS_probe_processes(); + while (CSI_free_servers) { + QUE_LOOP(CSS_header->csh_processes, que) + if (CSI_free_servers->rdb_server == + REL_PTR((PRB) ((UCHAR *) que - OFFSET(PRB, prb_processes)))) + break; + + if (que != &CSS_header->csh_processes) + break; + else { + rdb = CSI_free_servers; + CSI_free_servers = rdb->rdb_next; + FREE(rdb); + } + } + + RELEASE; + +/* If there aren't any available users, create one and then find it */ + + if (!CSI_free_servers) { + if ((pid = spawn(user_status, gbl_file, &connection)) > 0) { + ACQUIRE; + + QUE_LOOP(CSS_header->csh_processes, que) { + process = (PRB) ((UCHAR *) que - OFFSET(PRB, prb_processes)); + if (client != REL_PTR(process) && + process->prb_protocol_version == CSI_PROTOCOL_VERSION && + process->prb_process_id == pid) { + rdb = (RDB) ALLOC(type_rdb, sizeof(struct rdb)); + rdb->rdb_next = CSI_free_servers; + CSI_free_servers = rdb; + rdb->rdb_connection = connection; + rdb->rdb_server = REL_PTR(process); + break; + } + } + + RELEASE; + } + } + + if (CSI_free_servers) { + connection = CSI_free_servers->rdb_connection; + + length = + sizeof(struct msg_att) + file_length + expanded_length + + dpb_length; + message = (MSG_ATT) CSS_alloc_message(type_msg, (int) length); + message->msg_att_header.msg_type = type; + message->msg_att_dpb_length = dpb_length; + message->msg_att_file_length = file_length; + message->msg_att_expanded_length = expanded_length; + MOVE(file_name, message->msg_att_data, file_length); + MOVE(expanded_filename, message->msg_att_data + file_length, + expanded_length); + MOVE(dpb, message->msg_att_data + file_length + expanded_length, + dpb_length); + if (CSS_put_message(connection, message, 0) + && (response = CSS_get_message(connection, 0, 0)) + && !process_response(response, user_status, &handle, 0, 0, 0)) { + rdb = CSI_free_servers; + CSI_free_servers = rdb->rdb_next; + + rdb->rdb_handle = handle; + rdb->rdb_next = CSI_databases; + CSI_databases = rdb; + return rdb; + } + } + + return NULL; +} +#endif +#endif + + +static RTR make_transaction( RDB rdb, HANDLE handle) +{ +/************************************** + * + * m a k e _ t r a n s a c t i o n + * + ************************************** + * + * Functional description + * Create a local transaction handle. + * + **************************************/ + RTR transaction; + + transaction = (RTR) ALLOC(type_rtr, sizeof(struct rtr)); + transaction->rtr_rdb = rdb; + transaction->rtr_handle = handle; + transaction->rtr_next = rdb->rdb_transactions; + rdb->rdb_transactions = transaction; + + return transaction; +} + + +static void move( UCHAR * from, UCHAR * to, USHORT length) +{ +/************************************** + * + * m o v e + * + ************************************** + * + * Functional description + * Move some bytes. + * + **************************************/ + + if (length) + do + *to++ = *from++; + while (--length); +} + + +static USHORT name_length( TEXT * name) +{ +/***************************************** + * + * n a m e _ l e n g t h + * + ***************************************** + * + * Functional Description + * Compute length of user-supplied (blank-padded) or + * NULL-terminated buffer + * + *****************************************/ + TEXT *p; + + for (p = name; *p && *p != ' '; p++); + + return (USHORT) (p - name); +} + + +static STATUS process_response( + MSG_RESP message, + STATUS * user_status, + HANDLE * handle, +USHORT buffer_length, UCHAR * buffer, USHORT * return_length) +{ +/************************************** + * + * p r o c e s s _ r e s p o n s e + * + ************************************** + * + * Functional description + * Process a response packet from server. + * + **************************************/ + TEXT *p, *q; + STATUS *status, *stuff, *end; + USHORT l; + +/* If there isn't a message, the server has disappeared */ + + if (!message || message->msg_resp_header.msg_type != MSG_response) { + status = user_status; + *status++ = gds_arg_gds; + *status++ = gds__random; + *status++ = gds_arg_string; +#ifndef PIPE_SERVER + *status++ = (STATUS) "connection lost to central server"; +#else + *status++ = (STATUS) "connection lost to pipe server"; +#endif + *status = gds_arg_end; + return user_status[1]; + } + +/* Well, at least we got something -- eat it up */ + + p = error_buffer; + + for (status = user_status, end = status + 20, stuff = + message->msg_resp_status; (*status = *stuff++) && status < end;) + switch (*status++) { + case gds_arg_interpreted: + case gds_arg_string: + q = (TEXT *) message + *stuff++; + *status++ = (STATUS) p; + while (*p++ = *q++); + break; + + case gds_arg_cstring: + l = *status++ = *stuff++; + q = (TEXT *) message + *stuff++; + *status++ = (STATUS) p; + if (l) + do + *p++ = *q++; + while (--l); + break; + + default: + *status++ = *stuff++; + } + + if (!user_status[1] || user_status[1] == gds__segment) { + if (handle) + *handle = message->msg_resp_handle; + if (buffer && (l = MIN(buffer_length, message->msg_resp_length))) + MOVE(message->msg_resp_data, buffer, l); + if (return_length) + *return_length = message->msg_resp_length; + } + + CSS_free_global(message); + + return user_status[1]; +} + + +static void release_blob( RBL blob) +{ +/************************************** + * + * r e l e a s e _ b l o b + * + ************************************** + * + * Functional description + * Release a blob block and friends. + * + **************************************/ + RTR transaction; + RBL *p; + + transaction = blob->rbl_rtr; + + for (p = &transaction->rtr_blobs; *p; p = &(*p)->rbl_next) + if (*p == blob) { + *p = blob->rbl_next; + break; + } + + FREE(blob); +} + + +static STATUS release_object( + RDB rdb, + MSG_T type, HANDLE handle, STATUS * user_status) +{ +/************************************** + * + * r e l e a s e _ o b j e c t + * + ************************************** + * + * Functional description + * Tell the server to zap an object. This doesn't not necessary + * release the object, but usually does. + * + **************************************/ + MSG_OP message; + + message = (MSG_OP) CSS_alloc_message(type_msg, sizeof(struct msg_op)); + message->msg_op_header.msg_type = type; + message->msg_op_handle = handle; + + return get_response(rdb, message, user_status, 0, 0, 0, 0); +} + + +static void release_request( RRQ request) +{ +/************************************** + * + * r e l e a s e _ r e q u e s t + * + ************************************** + * + * Functional description + * Release a request block and friends. + * + **************************************/ + RDB rdb; + RRQ *p; + + rdb = request->rrq_rdb; + + for (p = &rdb->rdb_requests; *p; p = &(*p)->rrq_next) + if (*p == request) { + *p = request->rrq_next; + break; + } + + FREE(request); +} + + +static void release_sql_request( RSR stmt) +{ +/************************************** + * + * r e l e a s e _ s q l _ r e q u e s t + * + ************************************** + * + * Functional description + * Release an SQL request block. + * + **************************************/ + RDB rdb; + RSR *p; + + rdb = stmt->rsr_rdb; + + for (p = &rdb->rdb_sql_requests; *p; p = &(*p)->rsr_next) + if (*p == stmt) { + *p = stmt->rsr_next; + break; + } + + FREE(stmt); +} + + +static void release_transaction( RTR transaction) +{ +/************************************** + * + * r e l e a s e _ t r a n s a c t i o n + * + ************************************** + * + * Functional description + * Release a transaction block and friends. + * + **************************************/ + RDB rdb; + RTR *p; + + rdb = transaction->rtr_rdb; + + for (p = &rdb->rdb_transactions; *p; p = &(*p)->rtr_next) + if (*p == transaction) { + *p = transaction->rtr_next; + break; + } + + FREE(transaction); +} + + +static STATUS send_blob( + STATUS * user_status, + RBL blob, USHORT buffer_length, UCHAR * buffer) +{ +/************************************** + * + * s e n d _ b l o b + * + ************************************** + * + * Functional description + * Send a partially completed blob. + * + **************************************/ + MSG_T op; + MSG_SEG message; + +/* If there is no buffer address, this is a batch send. Pick up the + address and length from the blob buffer and blast away */ + + if (!buffer) { + buffer = blob->rbl_buffer; + buffer_length = blob->rbl_ptr - buffer; + blob->rbl_ptr = buffer; + op = MSG_put_segment_buf; + } + else + op = MSG_put_segment; + + message = + (MSG_SEG) CSS_alloc_message(type_msg, + (int) (sizeof(struct msg_seg) + + buffer_length)); + message->msg_seg_header.msg_type = op; + message->msg_seg_handle = blob->rbl_handle; + message->msg_seg_length = buffer_length; + MOVE(buffer, message->msg_seg_data, buffer_length); + + return get_response(blob->rbl_rdb, message, user_status, 0, 0, 0, 0); +} + + +#ifdef PIPE_SERVER +#ifdef VMS +static void setup_creprc_info( + SLONG ** privileges, + ITMQ ** quotas, SLONG * priority) +{ +/************************************** + * + * s e t u p _ c r e p r c _ i n f o + * + ************************************** + * + * Functional description + * Get the information relating to process quotas and + * privileges that is useful when creating a sub-process. + * + **************************************/ + ITM items[16]; + SLONG values[16]; + SSHORT i, lengths[16]; + ITMQ *quota; + + for (i = sizeof(items) / sizeof(ITM); i--;) { + items[i].itm_length = sizeof(SLONG); + items[i].itm_buffer = &values[i]; + items[i].itm_return_length = &lengths[i]; + } + items[10].itm_buffer = priority; + items[11].itm_length = sizeof(SLONG) * 2; + items[11].itm_buffer = *privileges; + + items[0].itm_code = JPI$_ASTLM; + items[1].itm_code = JPI$_BIOLM; + items[2].itm_code = JPI$_BYTLM; + items[3].itm_code = JPI$_CPULIM; + items[4].itm_code = JPI$_DIOLM; + items[5].itm_code = JPI$_ENQLM; + items[6].itm_code = JPI$_DFWSCNT; + items[7].itm_code = JPI$_FILLM; + items[8].itm_code = JPI$_PGFLQUOTA; + items[9].itm_code = JPI$_PRCLM; + items[10].itm_code = JPI$_PRIB; + items[11].itm_code = JPI$_PROCPRIV; + items[12].itm_code = JPI$_TQLM; + items[13].itm_code = JPI$_WSQUOTA; + items[14].itm_code = JPI$_WSAUTHEXT; + + items[15].itm_code = 0; + items[15].itm_length = 0; + + if (sys$getjpiw(NULL, NULL, NULL, items, NULL, NULL, NULL) & 1) { + for (quota = *quotas, i = sizeof(items) / sizeof(ITM); i--;) + if (lengths[i]) { + switch (items[i].itm_code) { + case JPI$_ASTLM: + quota->itmq_code = PQL$_ASTLM; + break; + + case JPI$_BIOLM: + quota->itmq_code = PQL$_BIOLM; + break; + + case JPI$_BYTLM: + quota->itmq_code = PQL$_BYTLM; + break; + + case JPI$_CPULIM: + quota->itmq_code = PQL$_CPULM; + break; + + case JPI$_DIOLM: + quota->itmq_code = PQL$_DIOLM; + break; + + case JPI$_ENQLM: + quota->itmq_code = PQL$_ENQLM; + break; + + case JPI$_DFWSCNT: + quota->itmq_code = PQL$_FILLM; + break; + + case JPI$_FILLM: + quota->itmq_code = PQL$_PGFLQUOTA; + break; + + case JPI$_PGFLQUOTA: + quota->itmq_code = PQL$_PRCLM; + break; + + case JPI$_PRCLM: + quota->itmq_code = PQL$_TQELM; + break; + + case JPI$_TQLM: + quota->itmq_code = PQL$_WSDEFAULT; + break; + + case JPI$_WSQUOTA: + quota->itmq_code = PQL$_WSEXTENT; + break; + + case JPI$_WSAUTHEXT: + quota->itmq_code = PQL$_WSQUOTA; + break; + + default: + continue; + } + + (quota++)->itmq_value = + (lengths[i] == 4) ? values[i] : (SSHORT) (values[i]); + } + else { + if (items[i].itm_code == JPI$_PRIB) + *priority = 4; + else if (items[i].itm_code == JPI$_PROCPRIV) + *privileges = NULL; + } + + quota->itmq_code = PQL$_LISTEND; + } + else { + *privileges = NULL; + *quotas = NULL; + *priority = 4; + } +} +#endif +#endif + + +#ifdef PIPE_SERVER +#ifdef VMS +static int spawn( STATUS * user_status, UCHAR * gbl_file, PTR * connection) +{ +/************************************** + * + * s p a w n + * + ************************************** + * + * Functional description + * Spawn a sub-process using SYS$CREPRC. Return > 0 if a + * process was created, -1 if create process failed. Also + * return a pointer to the created process's connection block. + * + **************************************/ + UCHAR output[128], error[128], *p, *q, process_name[16], + pipe_temp[256], pipe_file[256]; + USHORT i, len; + ULONG status, pid, flags, item; + SLONG *privileges, procpriv[2], priority; + ITMQ quota_list[14], *quotas; + MSG_RESP response; + struct dsc$descriptor_s desc1, desc2, desc3, desc4; + + privileges = procpriv; + quotas = quota_list; + setup_creprc_info(&privileges, "as, &priority); + +/* Tell the sub-process about the values of some important logical names. */ + + trans_logicals(); + + gds__prefix(pipe_temp, GDS_PIPE); + for (p = pipe_temp, q = p - 1; *p; p++) + if (*p == ':') + q = p; + if (q < pipe_temp) + strcpy(pipe_file, pipe_temp); + else { + len = ISC_expand_logical(pipe_temp, q - pipe_temp, pipe_file); + if (pipe_file[len - 1] != ']') + pipe_file[len++] = ':'; + strcpy(&pipe_file[len], q + 1); + } + + q = error - 1; + if (ISC_expand_logical_once("SYS$LOGIN", sizeof("SYS$LOGIN") - 1, error)) { + for (p = error; *p; p++) + if (*p == ':' || *p == ']') + q = p; + if (q < error) + *(q = p) = ':'; + } + for (p = "GDS_PIPE.ERR"; *++q = *p++;); + len = q - error; + + strcpy(output, error); + strcpy(&output[len - 3], "OUT"); + + ISC_make_desc(pipe_file, &desc1, 0); + ISC_make_desc(output, &desc2, len); + ISC_make_desc(error, &desc3, len); + i = 0; + do { + /* Loop until the sub-process is given a unique process name */ + + sprintf(process_name, "%s_%02x", gbl_file, i++); + ISC_make_desc(process_name, &desc4, 0); + status = sys$creprc(&pid, /* pidadr */ + &desc1, /* image */ + NULL, /* input */ + &desc2, /* output */ + &desc3, /* error */ + privileges, /* prvadr */ + quotas, /* quota */ + &desc4, /* prcnam */ + priority, /* baspri */ + NULL, NULL, NULL); + } while (status == SS$_DUPLNAM); + + if (!(status & 1)) { + /* We failed. Fill in the status vector. */ + + *user_status++ = gds_arg_gds; + *user_status++ = gds__sys_request; + *user_status++ = gds_arg_string; + *user_status++ = (STATUS) "sys$creprc"; + *user_status++ = gds_arg_vms; + *user_status++ = status; + *user_status = gds_arg_end; + return -1; + } + + while (!(response = CSS_get_message((SLONG) 0, 0, 10))) { + item = JPI$_PID; + if (lib$getjpi(&item, &pid, NULL, NULL, NULL, NULL) != SS$_NORMAL) + return -1; + } + + *connection = response->msg_resp_header.msg_connection; + CSS_free_global(response); + + return pid; +} +#endif +#endif + + +#ifdef PIPE_SERVER +#ifdef VMS +static void trans_logicals(void) +{ +/************************************** + * + * t r a n s _ l o g i c a l s + * + ************************************** + * + * Functional description + * Translate some logicals and write their values + * to the job logical table. + * + **************************************/ + UCHAR **logicals, value[256], job_logical[32], buffer[256]; + int attr; + SSHORT len; + ITM items[2]; + struct dsc$descriptor_s tab_desc, log_desc; + + for (logicals = inherit_logicals; *logicals; logicals++) { + items[0].itm_code = LNM$_STRING; + items[0].itm_buffer = value; + items[0].itm_length = sizeof(value); + items[0].itm_return_length = &len; + items[1].itm_length = 0; + items[1].itm_code = 0; + + attr = LNM$M_CASE_BLIND; + + ISC_make_desc(*logicals, &log_desc, 0); + ISC_make_desc("LNM$PROCESS", &tab_desc, sizeof("LNM$PROCESS") - 1); + if (!(sys$trnlnm(&attr, &tab_desc, &log_desc, NULL, items) & 1)) + continue; + + /* We have a value to set. Do so in the JOB table. */ + + items[0].itm_length = len; + + sprintf(job_logical, "GDS_PIPE_%s", *logicals); + ISC_make_desc(job_logical, &log_desc, 0); + ISC_make_desc("LNM$JOB", &tab_desc, sizeof("LNM$JOB") - 1); + + sys$crelnm(NULL, &tab_desc, &log_desc, NULL, items); + } +} +#endif +#endif diff --git a/src/csv/csi.h b/src/csv/csi.h new file mode 100644 index 0000000000..2344f0097c --- /dev/null +++ b/src/csv/csi.h @@ -0,0 +1,590 @@ +/* + * PROGRAM: JRD Access Method + * MODULE: csi.h + * DESCRIPTION: Central Server Interface definitions + * + * 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): ______________________________________. + */ + +#ifndef _CSV_CSI_H_ +#define _CSV_CSI_H_ + +#include "../jrd/common.h" +#include "../jrd/thd.h" +#include "../jrd/isc.h" + +#ifdef VMS +#define CSI_FILE "[000000]isc_csv_gbl.%s" +#if !(defined MULTI_THREAD) || defined (__ALPHA) +#ifndef PIPE_SERVER +#define CSV_SIGNAL 33 +#define EVENT_SIGNAL 3 +#else +#define CSV_SIGNAL 34 +#define EVENT_SIGNAL 4 +#endif +#endif +#endif + +#ifdef UNIX +#include +#define EVENT_SIGNAL SIGUSR2 +#define CSI_FILE "/usr/interbase/isc_csv.%s" +#endif + +#ifdef WIN_NT +#define CSI_FILE "/interbas/%s.csv" +#endif + +#ifdef PC_PLATFORM +#define CSI_DEFAULT_SIZE 65535 +#endif + +#ifdef GATEWAY +#ifdef PIPE_SERVER +#define CSI_DEFAULT_SIZE 69632 +#endif +#endif + +#ifndef CSI_DEFAULT_SIZE +#define CSI_DEFAULT_SIZE 262144 +#endif + +#define CSI_EXTEND_SIZE 32768 + +#ifndef MAX_ARRAY_MESSAGE +#define MAX_ARRAY_MESSAGE 64000 +#endif + +#ifndef BLOB_LENGTH +#define BLOB_LENGTH 4096 +#endif + +#define PTR SLONG +#define BASE ((UCHAR*) CSS_header) +#define REL_PTR(item) ((UCHAR*) item - BASE) +#define ABS_PTR(item) (BASE + item) +#define ACQUIRE CSS_acquire() +#define RELEASE CSS_release() + +#define QUE_INIT(que) {que.srq_forward = que.srq_backward = REL_PTR (&que);} +#define QUE_EMPTY(que) (que.srq_forward == REL_PTR (&que)) +#define QUE_NEXT(que) ABS_PTR (que.srq_forward) + +#define QUE_LOOP(header,que) for (que = (SRQ*) QUE_NEXT (header);\ + que != &header; que = (SRQ*) QUE_NEXT ((*que))) + + +#define SIZE_SHIFT 2 +#define FAST_ALLOC 16 + +/* Self-relative que block. Offsets are from the block itself. */ + +typedef struct srq { + PTR srq_forward; /* Forward offset */ + PTR srq_backward; /* Backward offset */ +} SRQ; + +/* Global section header */ + +#define CSI_VERSION 3 +#define CSI_PROTOCOL_VERSION 1 + +typedef struct csh { + SLONG csh_length; /* Current length of global section */ + UCHAR csh_version; /* Version number of global section */ + SRQ csh_processes; /* Known processes */ + PTR csh_free; /* Free blocks */ + PTR csh_current_process; /* Process "acquiring" section */ + SLONG csh_mutex[2]; /* Mutex controlling access */ + SLONG csh_semid; /* Semaphore id for UNIX */ + SLONG csh_process_number; /* Assigned sequentially */ + UCHAR csh_semaphores[16]; /* Free semaphores */ + PTR csh_free_vector[FAST_ALLOC]; +} *CSH; + +/* Common block header */ + +typedef struct hdr { + SLONG hdr_length; /* Length of block */ + UCHAR hdr_type; /* Type of block */ +} HDR; + +#define type_hdr 1 +#define type_frb 2 +#define type_prb 3 +#define type_cnct 4 +#define type_msg 5 + +/* Free blocks */ + +typedef struct frb { + HDR frb_header; + SLONG frb_next; /* Next block */ +} *FRB; + +/* Process blocks */ + +typedef struct prb { + HDR prb_header; + USHORT prb_flags; /* Misc */ + USHORT prb_protocol_version; /* Protocol version */ + ULONG prb_group_id; /* Group id for VMS */ + USHORT prb_semaphore; /* Semaphore used for event on UNIX */ + SRQ prb_processes; /* Process que ownerd by header */ + SRQ prb_connections; /* Connections to server process */ + SRQ prb_messages; /* Messages to be processed */ + SLONG prb_process_id; /* Process id */ + SLONG prb_process_number; /* Relative process number */ + EVENT_T prb_event[1]; /* Event on which to wait */ +} *PRB; + +#define PRB_client 0 /* Process is client */ +#define PRB_server 1 /* Process is server (otherwise client) */ +#define PRB_disconnect 2 /* Some connection was broken */ +#define PRB_client_t1 4 /* Process is client type 1 */ +#define PRB_server_t1 8 /* Process is server type 1 */ +#define PRB_timeout 32 /* Process has waited too long for a message */ +#define PRB_wait 64 /* Process is awaiting acquisition */ +#define PRB_signal_me 128 /* Process is waiting for a signal */ + +/* Connection block */ + +typedef struct cnct { + HDR cnct_header; + PTR cnct_parent; /* Client process block */ + PTR cnct_partner; /* Server process lock */ + PTR cnct_mirror; /* Mirror of connection */ + SRQ cnct_connections; /* Que of parents connections */ +} *CNCT; + +/* Message block */ + +typedef enum { + MSG_attach_database, + MSG_blob_info, + MSG_cancel_blob, + MSG_close_blob, + MSG_commit, + MSG_commit_retaining, + MSG_compile, + MSG_create_blob, + MSG_create_database, + MSG_database_info, + MSG_detach, + MSG_get_segment, + MSG_open_blob, + MSG_prepare, + MSG_put_segment, + MSG_reconnect, + MSG_receive, + MSG_release, + MSG_request_info, + MSG_rollback, + MSG_send, + MSG_start_and_send, + MSG_start_request, + MSG_start_transaction, + MSG_transaction_info, + MSG_unwind, + MSG_disconnect, + MSG_response, + MSG_que_events, + MSG_cancel_events, + MSG_event, + MSG_alt_connection, + MSG_util_cmd, + MSG_query_connect, + MSG_mdi_attach_db, + MSG_ping, + MSG_get_slice, + MSG_put_slice, + MSG_ddl, + MSG_seek_blob, + MSG_get_segment_buf, + MSG_put_segment_buf, + MSG_allocate_stmt, + MSG_execute, + MSG_execute_immediate, + MSG_fetch, + MSG_free_stmt, + MSG_prepare_stmt, + MSG_set_cursor, + MSG_sql_info, + MSG_insert, + MSG_execute2, + MSG_execute_immediate2, + MSG_transact_request, + MSG_drop_database +} MSG_T; + +typedef struct msg { + HDR msg_header; + SRQ msg_que; + MSG_T msg_type; /* Specific message type */ + PTR msg_connection; /* Process sending message */ +} *MSG; + +/* Miscellaneous message formats */ + +typedef struct msg_att { + struct msg msg_att_header; /* Common message header */ + USHORT msg_att_file_length; /* Length of file name */ + USHORT msg_att_dpb_length; /* Length of database parameter block */ + USHORT msg_att_expanded_length; /* Length of expanded file name */ + USHORT msg_att_type; /* Create type */ + UCHAR msg_att_data[1]; /* File name and dpb */ +} *MSG_ATT; + +typedef struct msg_info { + struct msg msg_info_header; + SLONG msg_info_handle; + USHORT msg_info_level; + USHORT msg_info_length; /* Item length */ + USHORT msg_info_buffer_length; /* Length of return buffer */ + UCHAR msg_info_data[1]; +} *MSG_INFO; + +typedef struct msg_op { + struct msg msg_op_header; + SLONG msg_op_handle; + SLONG msg_op_level; + USHORT msg_op_length; + USHORT msg_op_data[1]; +} *MSG_OP; + +typedef struct msg_blob { + struct msg msg_blob_header; + SLONG msg_blob_database; + SLONG msg_blob_transaction; + USHORT msg_blob_bpb_length; + SLONG msg_blob_id[2]; + UCHAR msg_blob_bpb[1]; +} *MSG_BLOB; + +typedef struct msg_seg { + struct msg msg_seg_header; + SLONG msg_seg_handle; + USHORT msg_seg_length; + USHORT msg_seg_buffer_length; + UCHAR msg_seg_data[1]; +} *MSG_SEG; + +typedef struct msg_msg { + struct msg msg_msg_header; + SLONG msg_msg_request; + SLONG msg_msg_transaction; + USHORT msg_msg_level; + USHORT msg_msg_type; + USHORT msg_msg_length; + UCHAR msg_msg_data[1]; +} *MSG_MSG; + +typedef struct tdb { + SLONG tdb_database; + USHORT tdb_tpb_length; + PTR tdb_tpb; +} TDB; + +typedef struct msg_trans { + struct msg msg_trans_header; + USHORT msg_trans_count; + TDB msg_trans_tdb[1]; +} *MSG_TRANS; + +typedef struct msg_resp { + struct msg msg_resp_header; + SLONG msg_resp_handle; + STATUS msg_resp_status[20]; + USHORT msg_resp_length; + UCHAR msg_resp_data[1]; +} *MSG_RESP; + +typedef struct msg_event { + struct msg msg_event_header; + SLONG msg_event_database; + void (*msg_event_ast) (); + void *msg_event_arg; + USHORT msg_event_length; + UCHAR msg_event_data[1]; +} *MSG_EVENT; + +typedef struct msg_slice { + struct msg msg_slice_header; + SLONG msg_slice_database; + SLONG msg_slice_transaction; + USHORT msg_slice_sdl_length; + USHORT msg_slice_param_length; + SLONG msg_slice_slice_length; + SLONG msg_slice_id[2]; + SLONG msg_slice_handle; + UCHAR msg_slice_data[1]; /* sdl, param, and slice */ +} *MSG_SLICE; + +typedef struct msg_ddl { + struct msg msg_ddl_header; + SLONG msg_ddl_database; + SLONG msg_ddl_transaction; + USHORT msg_ddl_length; + USHORT msg_ddl_data[1]; +} *MSG_DDL; + +typedef struct msg_util { + struct msg msg_util_header; + USHORT msg_util_cmd; + USHORT msg_util_csn_len; + USHORT msg_util_dbn_len; + TEXT msg_util_data[2]; +} *MSG_UTIL; + +typedef struct msg_seek { + struct msg msg_seek_header; + SLONG msg_seek_handle; + SSHORT msg_seek_mode; + SLONG msg_seek_offset; +} *MSG_SEEK; + +typedef struct msg_exnow { + struct msg msg_exnow_header; + SLONG msg_exnow_database; + SLONG msg_exnow_transaction; + USHORT msg_exnow_SQL_length; + USHORT msg_exnow_SQL_dialect; + USHORT msg_exnow_in_blr_length; + USHORT msg_exnow_in_msg_type; + USHORT msg_exnow_in_msg_length; + USHORT msg_exnow_out_blr_length; + USHORT msg_exnow_out_msg_type; + USHORT msg_exnow_out_msg_length; + UCHAR msg_exnow_data[1]; +} *MSG_EXNOW; + +typedef struct msg_pstmt { + struct msg msg_pstmt_header; + SLONG msg_pstmt_transaction; + SLONG msg_pstmt_statement; + USHORT msg_pstmt_SQL_length; + USHORT msg_pstmt_SQL_dialect; + USHORT msg_pstmt_item_length; + USHORT msg_pstmt_buffer_length; + UCHAR msg_pstmt_data[1]; +} *MSG_PSTMT; + +typedef struct msg_setcur { + struct msg msg_setcur_header; + SLONG msg_setcur_statement; + USHORT msg_setcur_type; + TEXT msg_setcur_cursor[1]; +} *MSG_SETCUR; + +typedef struct msg_sqlmsg { + struct msg msg_sqlmsg_header; + SLONG msg_sqlmsg_transaction; + SLONG msg_sqlmsg_statement; + USHORT msg_sqlmsg_in_blr_length; + USHORT msg_sqlmsg_in_msg_type; + USHORT msg_sqlmsg_in_msg_length; + USHORT msg_sqlmsg_out_blr_length; + USHORT msg_sqlmsg_out_msg_type; + USHORT msg_sqlmsg_out_msg_length; + UCHAR msg_sqlmsg_data[1]; +} *MSG_SQLMSG; + +typedef struct msg_trrq { + struct msg msg_trrq_header; + SLONG msg_trrq_database; + SLONG msg_trrq_transaction; + USHORT msg_trrq_blr_length; + USHORT msg_trrq_in_msg_length; + USHORT msg_trrq_out_msg_length; + UCHAR msg_trrq_data[1]; +} *MSG_TRRQ; + +#define UTIL_noop 0 +#define UTIL_list 1 +#define UTIL_disable 2 +#define UTIL_kill 3 +#define UTIL_reenable 4 +#define UTIL_enable 5 + +/* Local blocks */ + +typedef struct bid { + ULONG bid_relation_id; /* Relation id (or null) */ + ULONG bid_number; /* Record number */ +} *BID; + +typedef SLONG HANDLE; + +/* Block types */ + +#ifndef INCLUDE_FB_BLK +#include "../include/fb_blk.h" +#endif + +#define type_rdb 10 +#define type_rbl 11 +#define type_rrq 12 +#define type_rtr 13 +#define type_evnt 14 +#define type_dbn 15 +#define type_csn 16 +#define type_array 17 +#define type_rsr 18 + +/* Block types */ + +typedef struct rdb { + struct blk rdb_header; + PTR rdb_connection; /* Connection to partner */ + PTR rdb_connection2; /* Connection to partner for async events */ + PTR rdb_server; /* Server process */ + HANDLE rdb_handle; /* database handle */ + struct rtr *rdb_transactions; /* linked list of transactions */ + struct rrq *rdb_requests; /* compiled requests */ + struct rsr *rdb_sql_requests; /* SQL requests */ + struct rdb *rdb_next; /* next database in system */ + struct evnt *rdb_events; /* list of allocated event blocks */ + struct dbn *rdb_dbn; /* database name */ + int *rdb_status_vector; + SCHAR *rdb_setjmp; +} *RDB; + +typedef struct rtr { + struct blk rtr_header; + struct rdb *rtr_rdb; + struct rtr *rtr_next; + struct rbl *rtr_blobs; + struct array *rtr_arrays; + HANDLE rtr_handle; + USHORT rtr_flags; +} *RTR; + +#define RTR_limbo 1 + +typedef struct rbl { + struct blk rbl_header; + struct rdb *rbl_rdb; + struct rtr *rbl_rtr; + struct rbl *rbl_next; + HANDLE rbl_handle; + USHORT rbl_flags; + UCHAR *rbl_ptr; + USHORT rbl_length; + USHORT rbl_fragment_length; + USHORT rbl_buffer_length; + UCHAR rbl_buffer[1]; +} *RBL; + +#define RBL_eof 1 +#define RBL_segment 2 +#define RBL_eof_pending 4 +#define RBL_create 8 + +typedef struct rrq { + struct blk rrq_header; + struct rdb *rrq_rdb; + struct rrq *rrq_next; + struct rrq **rrq_user_handle; + HANDLE rrq_handle; +} *RRQ; + +typedef struct evnt { + struct blk evnt_header; + struct evnt *evnt_next; + struct rdb *evnt_rdb; + SLONG evnt_id; + void (*evnt_ast) (); + void *evnt_arg; +} *EVNT; + +typedef struct array { + struct blk array_header; + struct rdb *array_rdb; + struct rtr *array_rtr; + struct array *array_next; + SLONG array_length; + UCHAR *array_slice; + UCHAR array_data[1]; +} *ARRAY; + +typedef struct rsr { + struct blk rsr_header; + struct rdb *rsr_rdb; + struct rsr *rsr_next; + HANDLE rsr_handle; +} *RSR; + +typedef struct dbn { + struct blk dbn_header; + USHORT dbn_attaches; /* number of attachments */ + USHORT dbn_max_attaches; /* maximum number of allowable attachments */ + USHORT dbn_flags; + struct rdb *dbn_server; /* central server attachment */ + struct dbn *dbn_next; + USHORT dbn_length; /* length of name string */ + TEXT dbn_name[1]; /* name string */ +} *DBN; + +#define DBN_disable 1 /* disable new attachments */ +#define DBN_kill 2 /* kill all attachments */ +#define DBN_server_att 4 /* server has its own attachment to database */ +#define DBN_cmd_process 8 /* used by central server utility */ +#define DBN_reenable 16 /* re-enable new attachments */ +#define DBN_enable 32 /* enable a new database */ + +typedef struct csn { + struct blk csn_header; + USHORT csn_flags; + struct csn *csn_next; + USHORT csn_cs_len; /* length of server name string */ + USHORT csn_db_len; /* length of database name string */ + TEXT *csn_cs_name; /* server name string */ + TEXT *csn_db_name; /* database name string */ + TEXT csn_data[2]; +} *CSN; + +/* Central server utility list command data */ + +typedef struct csu_list { + USHORT csu_list_attaches; + USHORT csu_list_flags; + USHORT csu_list_length; + TEXT csu_list_name[1]; +} *CSU_LIST; + +#ifdef PIPE_SERVER +#define CSS_acquire PSS_acquire +#define CSS_alloc_message PSS_alloc_message +#define CSS_alloc_local PSS_alloc_local +#define CSS_check_partner PSS_check_partner +#define CSS_connect PSS_connect +#define CSS_create_process PSS_create_process +#define CSS_disconnect PSS_disconnect +#define CSS_free_global PSS_free_global +#define CSS_free_local PSS_free_local +#define CSS_find_process PSS_find_process +#define CSS_get_message PSS_get_message +#define CSS_init PSS_init +#define CSS_probe_processes PSS_probe_processes +#define CSS_put_message PSS_put_message +#define CSS_release PSS_release +#define CSS_validate PSS_validate +#endif + +#endif /* _CSV_CSI_H_ */ diff --git a/src/csv/csi_proto.h b/src/csv/csi_proto.h new file mode 100644 index 0000000000..67c9db67f7 --- /dev/null +++ b/src/csv/csi_proto.h @@ -0,0 +1,108 @@ +/* + * PROGRAM: Central Server + * MODULE: csi_proto.h + * DESCRIPTION: Prototype Header file for csi.c + * + * 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): ______________________________________. + */ + +#ifndef _CSV_CSI_PROTO_H_ +#define _CSV_CSI_PROTO_H_ + +extern STATUS CSI_attach_database(STATUS *, SSHORT, SCHAR *, struct rdb **, + SSHORT, SCHAR *, TEXT *); +extern STATUS CSI_blob_info(STATUS *, struct rbl **, SSHORT, UCHAR *, SSHORT, + UCHAR *); +extern STATUS CSI_cancel_blob(STATUS *, struct rbl **); +extern STATUS CSI_cancel_events(STATUS *, struct rdb **, SLONG *); +extern STATUS CSI_close_blob(STATUS *, struct rbl **); +extern STATUS CSI_commit_transaction(STATUS *, struct rtr **); +extern STATUS CSI_commit_retaining(STATUS *, struct rtr **); +extern STATUS CSI_compile_request(STATUS *, struct rdb **, struct rrq **, + USHORT, UCHAR *); +extern STATUS CSI_create_blob2(STATUS *, struct rdb **, struct rtr **, + struct rbl **, BID, USHORT, UCHAR *); +extern STATUS CSI_create_database(STATUS *, SSHORT, SCHAR *, struct rdb **, + SSHORT, SCHAR *, SSHORT, TEXT *); +extern STATUS CSI_database_info(STATUS *, struct rdb **, SSHORT, UCHAR *, + SSHORT, UCHAR *); +extern STATUS CSI_ddl(STATUS *, struct rdb **, struct rtr **, USHORT, + UCHAR *); +extern STATUS CSI_detach_database(STATUS *, struct rdb **); +extern STATUS CSI_drop_database(STATUS *, struct rdb **); +extern STATUS CSI_allocate_statement(STATUS *, struct rdb **, struct rsr **); +extern STATUS CSI_execute(STATUS *, struct rtr **, struct rsr **, USHORT, + UCHAR *, USHORT, USHORT, UCHAR *); +extern STATUS CSI_execute2(STATUS *, struct rtr **, struct rsr **, USHORT, + UCHAR *, USHORT, USHORT, UCHAR *, USHORT, UCHAR *, + USHORT, USHORT, UCHAR *); +extern STATUS CSI_execute_immediate(STATUS *, struct rdb **, struct rtr **, + USHORT, TEXT *, USHORT, USHORT, UCHAR *, + USHORT, USHORT, UCHAR *); +extern STATUS CSI_execute_immediate2(STATUS *, struct rdb **, struct rtr **, + USHORT, TEXT *, USHORT, USHORT, UCHAR *, + USHORT, USHORT, UCHAR *, USHORT, UCHAR *, + USHORT, USHORT, UCHAR *); +extern STATUS CSI_fetch(STATUS *, struct rsr **, USHORT, UCHAR *, USHORT, + USHORT, UCHAR *); +extern STATUS CSI_free_statement(STATUS *, struct rsr **, USHORT); +extern STATUS CSI_insert(STATUS *, struct rsr **, USHORT, UCHAR *, USHORT, + USHORT, UCHAR *); +extern STATUS CSI_prepare(STATUS *, struct rtr **, struct rsr **, USHORT, + TEXT *, USHORT, USHORT, SCHAR *, USHORT, SCHAR *); +extern STATUS CSI_set_cursor_name(STATUS *, struct rsr **, TEXT *, USHORT); +extern STATUS CSI_sql_info(STATUS *, struct rsr **, SSHORT, SCHAR *, SSHORT, + SCHAR *); +extern STATUS CSI_get_segment(STATUS *, struct rbl **, USHORT *, USHORT, + UCHAR *); +extern STATUS CSI_get_slice(STATUS *, struct rdb **, struct rtr **, BID, + USHORT, UCHAR *, USHORT, UCHAR *, SLONG, UCHAR *, + SLONG *); +extern STATUS CSI_open_blob2(STATUS *, struct rdb **, struct rtr **, + struct rbl **, BID, USHORT, UCHAR *); +extern STATUS CSI_prepare_transaction(STATUS *, struct rtr **, USHORT, + UCHAR *); +extern STATUS CSI_put_segment(STATUS *, struct rbl **, USHORT, UCHAR *); +extern STATUS CSI_put_slice(STATUS *, struct rdb **, struct rtr **, BID, + USHORT, UCHAR *, USHORT, UCHAR *, SLONG, UCHAR *); +extern STATUS CSI_que_events(STATUS *, struct rdb **, SLONG *, SSHORT, + UCHAR *, FPTR_VOID, void *); +extern STATUS CSI_receive(STATUS *, struct rrq **, USHORT, USHORT, UCHAR *, + SSHORT); +extern STATUS CSI_reconnect_transaction(STATUS *, struct rdb **, + struct rtr **, SSHORT, UCHAR *); +extern STATUS CSI_release_request(STATUS *, struct rrq **); +extern STATUS CSI_request_info(STATUS *, struct rrq **, USHORT, SSHORT, + UCHAR *, SSHORT, UCHAR *); +extern STATUS CSI_rollback_transaction(STATUS *, struct rtr **); +extern STATUS CSI_seek_blob(STATUS *, struct rbl **, SSHORT, SLONG, SLONG *); +extern STATUS CSI_send(STATUS *, struct rrq **, USHORT, USHORT, UCHAR *, + SSHORT); +extern STATUS CSI_start_and_send(STATUS *, struct rrq **, struct rtr **, + USHORT, USHORT, UCHAR *, SSHORT); +extern STATUS CSI_start_request(STATUS *, register struct rrq **, + register struct rtr **, SSHORT); +extern STATUS CSI_start_transaction(STATUS *, struct rtr **, SSHORT, ...); +extern STATUS CSI_transact_request(STATUS *, struct rdb **, struct rtr **, + USHORT, UCHAR *, USHORT, UCHAR *, USHORT, + UCHAR *); +extern STATUS CSI_transaction_info(STATUS *, struct rtr **, SSHORT, UCHAR *, + SSHORT, UCHAR *); +extern STATUS CSI_unwind_request(STATUS *, struct rrq **, SSHORT); + +#endif /* _CSV_CSI_PROTO_H_ */ diff --git a/src/csv/css.cpp b/src/csv/css.cpp new file mode 100644 index 0000000000..37e335cb48 --- /dev/null +++ b/src/csv/css.cpp @@ -0,0 +1,1691 @@ +/* + * PROGRAM: Central Server Interface + * MODULE: css.c + * DESCRIPTION: Central Servers services + * + * The contents of this file are subject to the Interbase Public + * License Version 1.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy + * of the License at http://www.Inprise.com/IPL.html + * + * Software distributed under the License is distributed on an + * "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either express + * or implied. See the License for the specific language governing + * rights and limitations under the License. + * + * The Original Code was created by Inprise Corporation + * and its predecessors. Portions created by Inprise Corporation are + * Copyright (C) Inprise Corporation. + * + * All Rights Reserved. + * Contributor(s): ______________________________________. + */ + +#include "../jrd/ib_stdio.h" +#include "../csv/csi.h" +#include "../jrd/codes.h" +#include "../jrd/thd.h" +#include "../csv/css_proto.h" +#include "../jrd/gds_proto.h" +#include "../jrd/isc_proto.h" + +#ifdef UNIX +#include +#ifdef sun +#include +#endif +#define SYS_ARG gds_arg_unix +#endif + +#ifdef PC_PLATFORM +#define SYS_ARG gds_arg_dos +#endif + +#ifdef WIN_NT +#define SYS_ARG gds_arg_win32 +#endif + +#ifdef VMS +#include lckdef +#ifdef PIPE_SERVER +#include rms +#endif +#define SYS_ARG gds_arg_vms + +static LKSB CSS_lksb; +#endif + +#define MAX_PROCESSES 16 +#define EVENT_FLAG 15 + +extern SCHAR *getenv(); + +#ifdef VMS +static void acquire_ast(void); +#endif +static FRB alloc_global(USHORT, int); +#ifdef UNIX +static int alloc_semaphore(void); +#endif +static void cancel_timer(PRB); +static void delete_process(SLONG); +static void disconnect(SLONG, SLONG); +static void dump(void); +static void dump_que(UCHAR *, SRQ *); +#ifdef VMS +static void error(STATUS *, TEXT *, STATUS); +#endif +static void exit_handler(void *); +static void free_global(FRB); +static void init(void *, SH_MEM, int); +static void insert_tail(SRQ *, SRQ *); +static void mutex_bugcheck(UCHAR *, int); +static void punt(TEXT *); +static int put_message(PTR, MSG, int); +#ifdef UNIX +static void release_semaphore(USHORT); +#endif +static void remove_que(SRQ *); +static void set_timer(USHORT, PRB); + +#ifdef PIPE_SERVER +#ifdef VMS +static void shutdown_section(void); +#endif +#endif // PIPE_SERVER + +#ifdef VMS +static void timeout_ast(PRB); +#endif + +static USHORT acquire_count, resignal; +static CSH CSS_header, CSS_region; +static SLONG CSS_length, CSS_process; + +#ifdef PIPE_SERVER +#ifdef VMS +static SH_MEM_T shmem_data; +#endif +#endif + + +CSH CSS_acquire(void) +{ +/************************************** + * + * C S S _ a c q u i r e + * + ************************************** + * + * Functional description + * Acquire exclusive access to shared global region. + * + **************************************/ + STATUS status; + int mutex_state; + + ++acquire_count; + +#ifdef VMS + if (!CSS_lksb.lksb_status) + ISC_wait(&CSS_lksb.lksb_status, EVENT_FLAG); + + status = sys$enq(EVENT_FLAG, /* event flag */ + LCK$K_EXMODE, /* lock mode */ + &CSS_lksb, /* Lock status block */ + LCK$M_CONVERT | LCK$M_SYNCSTS | LCK$M_NODLCKWT, /* flags */ + 0, /* resource name */ + 0, /* parent id */ + acquire_ast, /* ast address */ + 0, /* ast argument */ + 0, /* blocking ast */ + 0, /* access mode */ + 0); + + if (!(status & 1)) + lib$signal(status); + + if (!CSS_lksb.lksb_status) + ISC_wait(&CSS_lksb.lksb_status, EVENT_FLAG); +#else + + if (mutex_state = ISC_mutex_lock(CSS_region->csh_mutex)) + mutex_bugcheck("mutex lock", mutex_state); + +#endif + +#ifdef DEBUG + if (CSS_region->csh_current_process) + punt("CSS_acquire: another process has region acquired"); +#endif + + CSS_region->csh_current_process = CSS_process; + CSS_header = CSS_region; + + if (CSS_header->csh_current_process != CSS_process) + punt("CSS_acquire: lost acquisition"); + + return CSS_header; +} + + +FRB CSS_alloc_message(USHORT type, int length) +{ +/************************************** + * + * C S S _ a l l o c _ m e s s a g e + * + ************************************** + * + * Functional description + * Allocate a message in shared global region. Leave the + * region acquired until the subsequent CSS_put_message. + * + **************************************/ + FRB block; + + ACQUIRE; + block = alloc_global(type, length); + + return block; +} + + +BLK CSS_alloc_local(USHORT type, USHORT length) +{ +/************************************** + * + * C S S _ a l l o c _ l o c a l + * + ************************************** + * + * Functional description + * Allocate a local piece of memory. + * + **************************************/ + BLK block; + UCHAR *p, *end; + + block = (BLK) gds__alloc((SLONG) length); + + for (p = (UCHAR *) block, end = p + length; p < end;) + *p++ = 0; + + block->blk_type = type; + + return block; +} + + +#ifdef GATEWAY +#ifndef PIPE_SERVER +int CSS_check_partner( PTR connection_id, USHORT process_flags) +{ +/************************************** + * + * C S S _ c h e c k _ p a r t n e r + * + ************************************** + * + * Functional description + * Make sure the process flags of a connection + * match the specified flags. + * + **************************************/ + CNCT connection; + PRB partner; + USHORT result; + + ACQUIRE; + connection = (CNCT) ABS_PTR(connection_id); + + if (!connection->cnct_partner) { + RELEASE; + return FALSE; + } + + partner = (PRB) ABS_PTR(connection->cnct_partner); + result = + ((partner->prb_flags & process_flags) == + process_flags) ? TRUE : FALSE; + RELEASE; + + return result; +} +#endif +#endif + + +PTR CSS_connect(SLONG proc_offset) +{ +/************************************** + * + * C S S _ c o n n e c t + * + ************************************** + * + * Functional description + * Establish communication connection between client (us) and server. + * + **************************************/ + SLONG cnct_id; + CNCT connection, mirror; + PRB local, remote; + + ACQUIRE; + + local = (PRB) ABS_PTR(CSS_process); + remote = (PRB) ABS_PTR(proc_offset); + + connection = (CNCT) alloc_global(type_cnct, sizeof(struct cnct)); + connection->cnct_parent = CSS_process; + connection->cnct_partner = proc_offset; + insert_tail(&local->prb_connections, &connection->cnct_connections); + + mirror = (CNCT) alloc_global(type_cnct, sizeof(struct cnct)); + mirror->cnct_parent = proc_offset; + mirror->cnct_partner = CSS_process; + mirror->cnct_mirror = REL_PTR(connection); + insert_tail(&remote->prb_connections, &mirror->cnct_connections); + + connection->cnct_mirror = REL_PTR(mirror); + cnct_id = REL_PTR(connection); + RELEASE; + + return cnct_id; +} + + +PTR CSS_create_process(USHORT process_flag) +{ +/************************************** + * + * C S S _ c r e a t e _ p r o c e s s + * + ************************************** + * + * Functional description + * Create process block unless it already exists. + * + **************************************/ + PRB process, server; + SLONG semaphore; + SRQ *que; + UCHAR *p, *end; + + if (CSS_process) + return (PTR) CSS_process; + + ACQUIRE; + +#ifdef UNIX + if ((semaphore = alloc_semaphore()) == -1) + return NULL; +#else + semaphore = 0; +#endif + + process = (PRB) alloc_global(type_prb, sizeof(struct prb)); + process->prb_flags = process_flag; + process->prb_protocol_version = CSI_PROTOCOL_VERSION; + process->prb_semaphore = semaphore; + process->prb_process_number = ++CSS_header->csh_process_number; + process->prb_process_id = getpid(); + + insert_tail(&CSS_header->csh_processes, &process->prb_processes); + QUE_INIT(process->prb_connections); + QUE_INIT(process->prb_messages); + ISC_event_init(process->prb_event, CSS_header->csh_semid, + process->prb_semaphore); + CSS_header->csh_current_process = CSS_process = REL_PTR(process); + RELEASE; + + return (PTR) CSS_process; +} + + +void CSS_disconnect( SLONG cnct_id) +{ +/************************************** + * + * C S S _ d i s c o n n e c t + * + ************************************** + * + * Functional description + * Break one side of a connection. If the other side + * is inactive, finish cleanup. + * + **************************************/ + + ACQUIRE; + disconnect(CSS_process, cnct_id); + RELEASE; +} + + +void CSS_free_global( FRB block) +{ +/************************************** + * + * C S S _ f r e e _ g l o b a l + * + ************************************** + * + * Functional description + * Free a previous allocated block. + * + **************************************/ + + ACQUIRE; + free_global(block); + RELEASE; +} + + +void CSS_free_local( BLK block) +{ +/************************************** + * + * C S S _ f r e e _ l o c a l + * + ************************************** + * + * Functional description + * + **************************************/ + + gds__free(block); +} + + +PTR CSS_find_process( SLONG process_number) +{ +/************************************** + * + * C S S _ f i n d _ p r o c e s s + * + ************************************** + * + * Functional description + * Find a process block based on a known number. Since + * independent processes are at work, process blocks can + * disappear unexpectedly. Looping through the blocks each + * time let's us know if this has happened. + * + **************************************/ + SRQ *que; + PRB process; + PTR offset; + + ACQUIRE; + offset = 0; + + QUE_LOOP(CSS_header->csh_processes, que) { + process = (PRB) ((UCHAR *) que - OFFSET(PRB, prb_processes)); + if ((process->prb_flags & PRB_server) && + process->prb_protocol_version == CSI_PROTOCOL_VERSION) + if (process->prb_process_number == process_number) { + offset = REL_PTR(process); + break; + } + } + + RELEASE; + + return offset; +} + + +MSG CSS_get_message(PTR partner, MSG old_message, SSHORT timeout) +{ +/************************************** + * + * C S S _ g e t _ m e s s a g e + * + ************************************** + * + * Functional description + * Get next available message. If necessary, wait on event. If + * a partner connection id is given, wait for message from that + * connection only; otherwise accept message from anybody. If the + * timeout is negative, return immediately if no message is pending. + * + **************************************/ + PRB process; + MSG message; + CNCT connection; + SRQ *que; + SLONG count, status; + EVENT events; + + if (timeout < 0 && acquire_count) { + resignal = TRUE; + return NULL; + } + + ACQUIRE; + + if (old_message) + free_global(old_message); + + process = (PRB) ABS_PTR(CSS_process); + events = process->prb_event; + +/* If a timeout period has been specified, establish a timer. + A process block flag will be set if the timer goes off. + This method permits only one active timer per process block. */ + + if (timeout > 0) { + process->prb_flags &= ~PRB_timeout; + set_timer(timeout, process); + } + + for (;;) { + process->prb_flags &= ~PRB_signal_me; + QUE_LOOP(process->prb_messages, que) { + message = (MSG) ((UCHAR *) que - OFFSET(MSG, msg_que)); + connection = (CNCT) ABS_PTR(message->msg_connection); + if (!partner || connection->cnct_mirror == partner) + goto got_one; + } + if (timeout < 0) { + RELEASE; + return NULL; + } + if (partner) { + connection = (CNCT) ABS_PTR(partner); + if (!connection->cnct_partner) { + if (timeout > 0) + cancel_timer(process); + RELEASE; + return NULL; + } + } + if (timeout > 0 && process->prb_flags & PRB_timeout) { + RELEASE; + return NULL; + } + count = ISC_event_clear(process->prb_event); + process->prb_flags |= PRB_signal_me; + RELEASE; + THREAD_EXIT; + ISC_event_wait(1, &events, &count, -1, 0, 0); + THREAD_ENTER; + ACQUIRE; + process = (PRB) ABS_PTR(CSS_process); + events = process->prb_event; + } + + got_one: + message->msg_connection = connection->cnct_mirror; + remove_que(&message->msg_que); + + if (timeout > 0) + cancel_timer(process); + + RELEASE; + + return message; +} + + +#ifdef PIPE_SERVER +#ifdef GATEWAY +CSH CSS_init(STATUS * status_vector, USHORT server_flag, TEXT * filename) +{ +/************************************** + * + * C S S _ i n i t ( g a t e w a y _ p i p e _ s e r v e r ) + * + ************************************** + * + * Functional description + * Initialize for access to shared global region. Return + * address of header if region exits, otherwise return NULL. + * + **************************************/ + TEXT csi_file[128], *p; + int (*init_routine) (); +#ifndef VMS + SH_MEM_T shmem_data; +#endif + SLONG desc[2]; + STATUS status; + +/* If we're already initialized, there's nothing to do */ + + if (CSS_region) + return CSS_region; + + init_routine = server_flag ? init : NULL; + + for (p = csi_file; *p = *filename++; p++); + for (filename = ".%s"; *p++ = *filename++;); + filename = csi_file; + +#ifdef VMS + shmem_data->sh_mem_system_flag = TRUE; +#endif + if (!(CSS_header = ISC_map_file(status_vector, + filename, init_routine, 0, + CSI_DEFAULT_SIZE, + &shmem_data))) return NULL; + + CSS_length = shmem_data.sh_mem_length_mapped; + +#ifdef VMS +/* Strip off any device prefix. */ + + for (p = filename; *p; p++) + if (*p == ':' || *p == ']') + filename = p + 1; + ISC_make_desc(filename, desc, 0); + + status = sys$enqw(EVENT_FLAG, LCK$K_NLMODE, &CSS_lksb, LCK$M_NODLCKWT, &desc, NULL, /* Lock parent (not used) */ + 0, /* AST routine when granted */ + 0, 0, NULL, NULL); + + if (!(status & 1) || !((status = CSS_lksb.lksb_status) & 1)) { + error(status_vector, "sys$enqw", status); + return NULL; + } +#endif + + CSS_region = CSS_header; + gds__register_cleanup(exit_handler, 0); + + return CSS_header; +} +#endif + + +#ifndef GATEWAY +CSH CSS_init(STATUS * status_vector, USHORT server_flag, SSHORT id) +{ +/************************************** + * + * C S S _ i n i t ( p i p e _ s e r v e r ) + * + ************************************** + * + * Functional description + * Initialize for access to shared global region. Return + * address of header if region exits, otherwise return NULL. + * + **************************************/ + TEXT *filename, csi_file[128], *p; + int (*init_routine) (); + SH_MEM_T shmem_data; + SLONG desc[2]; + STATUS status; + +/* If we're already initialized, there's nothing to do */ + + if (CSS_region) + return CSS_region; + + init_routine = server_flag ? init : NULL; + + if (!(filename = getenv("CSS_DEBUG"))) + filename = CSI_FILE; + +#ifdef VMS + shmem_data->sh_mem_system_flag = FALSE; +#endif +#ifdef UNIX + shmem_data->sh_mem_semaphores = MAX_PROCESSES; +#endif + if (!(CSS_header = ISC_map_file(status_vector, + filename, init_routine, 0, + CSI_DEFAULT_SIZE, + &shmem_data))) return NULL; + + CSS_length = shmem_data.sh_mem_length_mapped; + +#ifdef VMS +/* Strip off any device prefix. */ + + for (p = filename; *p; p++) + if (*p == ':' || *p == ']') + filename = p + 1; + ISC_make_desc(filename, desc, 0); + + status = sys$enqw(EVENT_FLAG, LCK$K_NLMODE, &CSS_lksb, LCK$M_SYSTEM | LCK$M_NODLCKWT, &desc, NULL, /* Lock parent (not used) */ + 0, /* AST routine when granted */ + 0, 0, NULL, NULL); + + if (!(status & 1) || !((status = CSS_lksb.lksb_status) & 1)) { + error(status_vector, "sys$enqw", status); + return NULL; + } +#endif + + CSS_region = CSS_header; + gds__register_cleanup(exit_handler, 0); + + return CSS_header; +} +#endif +#endif + + +#ifndef PIPE_SERVER +CSH CSS_init(STATUS * status_vector, USHORT server_flag) +{ +/************************************** + * + * C S S _ i n i t ( c e n t r a l _ s e r v e r ) + * + ************************************** + * + * Functional description + * Initialize for access to shared global region. Return + * address of header if region exits, otherwise return NULL. + * + **************************************/ + TEXT *filename, *p; + int (*init_routine) (); + SH_MEM_T shmem_data; + SLONG desc[2]; + STATUS status; + +/* If we're already initialized, there's nothing to do */ + + if (CSS_region) + return CSS_region; + + init_routine = server_flag ? init : NULL; + + if (!(filename = getenv("CSS_DEBUG"))) + filename = CSI_FILE; + +#ifdef VMS + shmem_data.sh_mem_system_flag = FALSE; +#endif +#ifdef UNIX + shmem_data.sh_mem_semaphores = MAX_PROCESSES; +#endif + if (!(CSS_header = ISC_map_file(status_vector, + filename, init_routine, 0, + CSI_DEFAULT_SIZE, + &shmem_data))) return NULL; + + CSS_length = shmem_data.sh_mem_length_mapped; + +#ifdef VMS +/* Strip off any device prefix. */ + + for (p = filename; *p; p++) + if (*p == ':' || *p == ']') + filename = p + 1; + ISC_make_desc(filename, desc, 0); + + status = sys$enqw(EVENT_FLAG, LCK$K_NLMODE, &CSS_lksb, LCK$M_SYSTEM | LCK$M_NODLCKWT, &desc, NULL, /* Lock parent (not used) */ + 0, /* AST routine when granted */ + 0, 0, NULL, NULL); + + if (!(status & 1) || !((status = CSS_lksb.lksb_status) & 1)) { + error(status_vector, "sys$enqw", status); + return NULL; + } +#endif + + CSS_region = CSS_header; + gds__register_cleanup(exit_handler, 0); + + return CSS_header; +} +#endif + + +void CSS_probe_processes(void) +{ +/************************************** + * + * C S S _ p r o b e _ p r o c e s s e s + * + ************************************** + * + * Functional description + * Probe to see if processes still exists. If not, zap it. + * + **************************************/ + SRQ *que, *next; + PRB process; + PTR ptr; + ULONG item; + + QUE_LOOP(CSS_header->csh_processes, que) { + process = (PRB) ((UCHAR *) que - OFFSET(PRB, prb_processes)); + ptr = REL_PTR(process); + if (ptr != CSS_process) + if (!ISC_check_process_existence + (process->prb_process_id, process->prb_group_id, FALSE)) { + que = (SRQ *) ABS_PTR(que->srq_backward); + delete_process(ptr); + } + } +} + + +int CSS_put_message( PTR connection_id, MSG message, MSG old_message) +{ +/************************************** + * + * C S S _ p u t _ m e s s a g e + * + ************************************** + * + * Functional description + * Post a message (formatted) to a given processes msg que. + * Assume that the region has already been acquired by + * CSS_alloc_message. + * + **************************************/ + + if (old_message) + free_global(old_message); + + return put_message(connection_id, message, TRUE); +} + + +void CSS_release(void) +{ +/************************************** + * + * C S S _ r e l e a s e + * + ************************************** + * + * Functional description + * Release exclusive control of shared global region. + * + **************************************/ + STATUS status; + int mutex_state; + PRB process; + SLONG pid, port; + +#ifdef DEBUG_VALIDATE + CSS_validate(); +#endif + + if (!acquire_count || CSS_process + && CSS_header->csh_current_process != + CSS_process) punt("CSS_release: lost acquisition"); + + CSS_header->csh_current_process = 0; + +#ifndef MULTI_THREAD +/* In case we get (or have gotten) signaled, save our + process id so we can signal ourselves */ + + process = (PRB) ABS_PTR(CSS_process); + pid = process->prb_process_id; +#endif + +#ifdef VMS + status = sys$enqw(0, /* event flag */ + LCK$K_NLMODE, /* lock mode */ + &CSS_lksb, /* Lock status block */ + LCK$M_CONVERT, /* flags */ + 0, /* resource name */ + 0, /* parent id */ + 0, /* ast address */ + 0, /* ast argument */ + 0, /* blocking ast */ + 0, /* access mode */ + 0); + + if (!(status & 1)) + lib$signal(status); +#else + + if (mutex_state = ISC_mutex_unlock(CSS_header->csh_mutex)) + mutex_bugcheck("mutex unlock", mutex_state); + +#endif + + CSS_header = (CSH) - 1; + --acquire_count; + +#ifndef GATEWAY +#ifndef MULTI_THREAD + if (resignal) { + resignal = FALSE; + ISC_kill(pid, EVENT_SIGNAL); + } +#endif +#endif +} + + +#ifdef DEBUG_VALIDATE +void CSS_validate(void) +{ +/************************************** + * + * C S S _ v a l i d a t e + * + ************************************** + * + * Functional description + * Make sure everything looks ok. + * + **************************************/ + SLONG offset; + HDR *block; + PTR next_free; + +/* Check consistency of global region (debugging only) */ + + next_free = 0; + + for (offset = sizeof(struct csh); offset < CSS_header->csh_length; + offset += block->hdr_length) { + block = (HDR *) ABS_PTR(offset); + if (!block->hdr_length || !block->hdr_type + || block->hdr_type > type_msg) { + punt("bad block length or type"); + break; + } + if (next_free) + if (offset == next_free) + next_free = 0; + else if (offset > next_free) + punt("bad free chain"); + if (block->hdr_type == type_frb) { + next_free = ((FRB) block)->frb_next; + if (next_free >= CSS_header->csh_length) + punt("bad frb_next"); + } + } + + if (offset != CSS_header->csh_length) + punt("bad block length"); +} +#endif + + +#ifdef VMS +static void acquire_ast(void) +{ +/************************************** + * + * a c q u i r e _ a s t + * + ************************************** + * + * Functional description + * Completion ast for acquire. + * + **************************************/ + + sys$wake(0, 0); +} +#endif + + +static FRB alloc_global( USHORT type, int length) +{ +/************************************** + * + * a l l o c _ g l o b a l + * + ************************************** + * + * Functional description + * Allocate a block in shared global region. + * + **************************************/ + PTR *ptr, *best; + FRB free; + SLONG tail, best_tail; + + length = (length + 3) & ~3; + best = NULL; + + for (ptr = &CSS_header->csh_free; (free = (FRB) ABS_PTR(*ptr)) && *ptr; + ptr = &free->frb_next) { + tail = free->frb_header.hdr_length - length; + if (tail >= 0 && (!best || tail < best_tail)) { + best = ptr; + best_tail = tail; + } + } + + if (!best) { + RELEASE; + gds__put_error("(CSS) alloc_global: space exhausted"); + abort(1); + } + + free = (FRB) ABS_PTR(*best); + + if (best_tail < sizeof(struct frb)) + *best = free->frb_next; + else { + free->frb_header.hdr_length -= length; + free = (FRB) ((UCHAR *) free + free->frb_header.hdr_length); + free->frb_header.hdr_length = length; + } + + free->frb_header.hdr_type = type; + + return free; +} + + +#ifdef UNIX +static int alloc_semaphore(void) +{ +/************************************** + * + * a l l o c _ s e m a p h o r e + * + ************************************** + * + * Functional description + * Release a no-longer needed (or newly allocated) semaphore. + * + **************************************/ + SSHORT n, bit; + UCHAR *byte; + + for (n = 0; n < MAX_PROCESSES; n++) { + byte = CSS_header->csh_semaphores + (n >> 3); + bit = 1 << (n & 7); + if (*byte & bit) { + *byte &= ~bit; + return n; + } + } + + return -1; +} +#endif + + +#ifdef VMS +static void cancel_timer( PRB process) +{ +/************************************** + * + * c a n c e l _ t i m e r ( V M S ) + * + ************************************** + * + * Functional description + * Cancel an unexpired timer. + * + **************************************/ + + if (!(process->prb_flags & PRB_timeout)) + sys$cantim(process, 0); +} +#endif + + +#ifndef VMS +static void cancel_timer( PRB process) +{ +/************************************** + * + * c a n c e l _ t i m e r ( g e n e r i c ) + * + ************************************** + * + * Functional description + * Cancel an unexpired timer. + * + **************************************/ +} +#endif + + +static void delete_process( SLONG process_offset) +{ +/************************************** + * + * d e l e t e _ p r o c e s s + * + ************************************** + * + * Functional description + * Delete a process block including friends and relations. + * + **************************************/ + PRB process; + MSG message; + + process = (PRB) ABS_PTR(process_offset); + +/* Eliminate all connections */ + + while (!QUE_EMPTY(process->prb_connections)) + disconnect(process_offset, + process->prb_connections.srq_forward - OFFSET(CNCT, + cnct_connections)); + +/* Eliminating any messages */ + + while (!QUE_EMPTY(process->prb_messages)) { + message = + (MSG) ABS_PTR(process->prb_messages.srq_forward - + OFFSET(MSG, msg_que)); + remove_que(&message->msg_que); + free_global(message); + } + +#ifdef UNIX +/* Release semaphore */ + + release_semaphore(process->prb_semaphore); +#endif + +/* Untangle and release process block */ + + remove_que(&process->prb_processes); + free_global(process); + + if (CSS_process == process_offset) + CSS_process = NULL; +} + + +static void disconnect( SLONG process_offset, SLONG cnct_id) +{ +/************************************** + * + * d i s c o n n e c t + * + ************************************** + * + * Functional description + * Break one side of a connection. If the other side + * is inactive, finish cleanup. + * + **************************************/ + CNCT connection, mirror; + MSG message; + PRB process, partner; + SRQ *que; + + connection = (CNCT) ABS_PTR(cnct_id); + mirror = (CNCT) ABS_PTR(connection->cnct_mirror); + process = (PRB) ABS_PTR(process_offset); + +/* Purge message que of any traffic from connection */ + + QUE_LOOP(process->prb_messages, que) { + message = (MSG) ((UCHAR *) que - OFFSET(MSG, msg_que)); + if (message->msg_connection == connection->cnct_mirror) { + que = (SRQ *) ABS_PTR(que->srq_backward); + remove_que(&message->msg_que); + free_global(message); + } + } + +/* Untangle the two connection blocks from our process block */ + + remove_que(&connection->cnct_connections); + +/* If the partner connection is still active, post a disconnect + message and break our side of the connection */ + + if (connection->cnct_partner) { + partner = (PRB) ABS_PTR(connection->cnct_partner); + partner->prb_flags |= PRB_signal_me; + message = (MSG) alloc_global(type_msg, sizeof(struct msg)); + message->msg_type = MSG_disconnect; + put_message(cnct_id, message, FALSE); + mirror->cnct_partner = 0; + connection->cnct_parent = 0; + } + else { + free_global(connection); + free_global(mirror); + } +} + + +#ifdef DEBUG +static void dump(void) +{ +/************************************** + * + * d u m p + * + ************************************** + * + * Functional description + * + **************************************/ + SRQ *que; + HDR *block; + PRB process; + MSG message; + FRB free; + CNCT connection; + SLONG offset; + + printf("%.5d GLOBAL REGION HEADER\n", 0); + printf("\tLength: %ld, version: %d, free: %ld\n", CSS_region->csh_length, + CSS_region->csh_version, CSS_region->csh_free); + printf("\tSemid: %ld, current process: %ld\n", + CSS_region->csh_semid, CSS_region->csh_current_process); + dump_que("\tProcesses", &CSS_region->csh_processes); + + for (offset = sizeof(struct csh); offset < CSS_region->csh_length; + offset += block->hdr_length) { + printf("\n%.5ld ", offset); + block = (HDR *) ABS_PTR(offset); + switch (block->hdr_type) { + case type_prb: + printf("PROCESS_BLOCK (%ld)\n", block->hdr_length); + process = (PRB) block; + printf("\tProcess_id: %ld, flags: %d, protocol: %d\n", + process->prb_process_id, process->prb_flags, + process->prb_protocol_version); + printf("\tSemaphore: %d, group_id: %ld, number: %ld\n", + process->prb_semaphore, process->prb_group_id, + process->prb_process_number); + dump_que("\tProcesses", &process->prb_processes); + dump_que("\tConnections", &process->prb_connections); + dump_que("\tMessages", &process->prb_messages); + break; + + case type_frb: + printf("FREE BLOCK (%ld)\n", block->hdr_length); + free = (FRB) block; + printf("\tNext: %ld\n", free->frb_next); + break; + + case type_msg: + printf("MSG (%ld)\n", block->hdr_length); + message = (MSG) block; + printf("\tType: %d, connection: %ld\n", message->msg_type, + message->msg_connection); + dump_que("\tMsg que", &message->msg_que); + break; + + case type_cnct: + printf("CONNECTION (%ld)\n", block->hdr_length); + connection = (CNCT) block; + printf("\tParent: %ld, partner: %ld, mirror: %ld\n", + connection->cnct_parent, connection->cnct_partner, + connection->cnct_mirror); + dump_que("\tConnections", &connection->cnct_connections); + break; + + default: + printf("*** UNKNOWN *** (%ld)\n", block->hdr_length); + break; + } + if (!block->hdr_length) + break; + } + + return 1; +} + + +static void dump_que( UCHAR * string, SRQ * que) +{ +/************************************** + * + * d u m p _ q u e + * + ************************************** + * + * Functional description + * Print the contents of a self-relative que. + * + **************************************/ + SLONG offset; + + offset = REL_PTR(que); + + if (offset == que->srq_forward && offset == que->srq_backward) + printf("%s: *empty*\n", string); + else + printf("%s: forward: %d, backward: %d\n", + string, que->srq_forward, que->srq_backward); +} +#endif + + +#ifdef VMS +static void error( STATUS * status_vector, TEXT * string, STATUS status) +{ +/************************************** + * + * e r r o r + * + ************************************** + * + * Functional description + * We've encountered an error, report it. + * + **************************************/ + + *status_vector++ = gds_arg_gds; + *status_vector++ = gds__sys_request; + *status_vector++ = gds_arg_string; + *status_vector++ = (STATUS) string; + *status_vector++ = SYS_ARG; + *status_vector++ = status; + *status_vector++ = gds_arg_end; +} +#endif + + + +static void exit_handler( void *arg) +{ +/************************************** + * + * e x i t _ h a n d l e r + * + ************************************** + * + * Functional description + * Cleanup on exit. + * + **************************************/ + + if (CSS_process) { + if (!acquire_count) + ACQUIRE; + delete_process(CSS_process); + } + + while (acquire_count > 0) + RELEASE; + +#ifdef PIPE_SERVER +#ifdef VMS + shutdown_section(); +#endif +#endif + +#ifdef UNIX +#ifndef MAP_TYPE + shmdt(CSS_region); +#endif +#endif + + CSS_region = NULL; +} + + +static void free_global( FRB block) +{ +/************************************** + * + * f r e e _ g l o b a l + * + ************************************** + * + * Functional description + * Free a previous allocated block. + * + **************************************/ + PTR *ptr, offset; + FRB free, prior; + + prior = NULL; + offset = REL_PTR(block); + block->frb_header.hdr_type = type_frb; + + for (ptr = &CSS_header->csh_free; free = (FRB) ABS_PTR(*ptr); + prior = free, ptr = &free->frb_next) + if (!*ptr) { + free = NULL; + break; + } + else if ((SCHAR HUGE_PTR *) block < (SCHAR HUGE_PTR *) free) + break; + + if (offset <= 0 || offset > CSS_header->csh_length || + (prior + && (UCHAR HUGE_PTR *) block < + (UCHAR HUGE_PTR *) prior + prior->frb_header.hdr_length)) { + punt("(CSS)free_global: bad block"); + return; + } + +/* Start by linking block into chain */ + + block->frb_next = *ptr; + *ptr = offset; + +/* Try to merge free block with next block */ + + if (free + && (SCHAR *) block + block->frb_header.hdr_length == (SCHAR *) free) { + block->frb_header.hdr_length += free->frb_header.hdr_length; + block->frb_next = free->frb_next; + } + +/* Next, try to merge the free block with the prior block */ + + if (prior + && (SCHAR *) prior + prior->frb_header.hdr_length == + (SCHAR *) block) { + prior->frb_header.hdr_length += block->frb_header.hdr_length; + prior->frb_next = block->frb_next; + } +} + + +static void init( void *arg, SH_MEM shmem_data, int initialize) +{ +/************************************** + * + * i n i t + * + ************************************** + * + * Functional description + * Initialize global region header. + * + **************************************/ + USHORT n; + UCHAR *p, *end; + FRB free; + int mutex_state; + + if (!initialize) + return; + + p = (UCHAR *) shmem_data->sh_mem_address; + for (end = p + sizeof(struct csh); p < end;) + *p++ = 0; + + CSS_header = shmem_data->sh_mem_address; + CSS_header->csh_length = shmem_data->sh_mem_length_mapped; + CSS_header->csh_version = CSI_VERSION; + CSS_header->csh_semid = shmem_data->sh_mem_mutex_arg; + QUE_INIT(CSS_header->csh_processes); + if (mutex_state = + ISC_mutex_init(CSS_header->csh_mutex, + shmem_data-> + sh_mem_mutex_arg)) mutex_bugcheck("mutex init", + mutex_state); + + free = (FRB) ((UCHAR *) CSS_header + sizeof(struct csh)); + free->frb_header.hdr_length = + shmem_data->sh_mem_length_mapped - sizeof(struct csh); + free->frb_header.hdr_type = type_frb; + free->frb_next = 0; + + CSS_header->csh_free = (UCHAR *) free - (UCHAR *) CSS_header; + +#ifdef UNIX +/* Initialize free semaphore bit vector for UNIX. Semaphore + 0 is used implicitly for the mutex on the global section */ + + for (n = 1; n < shmem_data->sh_mem_semaphores; n++) + release_semaphore(n); +#endif +} + + +static void insert_tail( SRQ * que, SRQ * node) +{ +/************************************** + * + * i n s e r t _ t a i l + * + ************************************** + * + * Functional description + * Insert a node at the tail of a que. + * + **************************************/ + SRQ *prior; + + node->srq_forward = REL_PTR(que); + node->srq_backward = que->srq_backward; + + prior = (SRQ *) ABS_PTR(que->srq_backward); + prior->srq_forward = REL_PTR(node); + que->srq_backward = REL_PTR(node); +} + + +static void mutex_bugcheck( UCHAR * string, int number) +{ +/************************************** + * + * m u t e x _ b u g c h e c k + * + ************************************** + * + * Functional description + * Disasterous mutex bug. Issue message and abort process. + * + **************************************/ + SCHAR msg[128]; + + sprintf(msg, "CSS error: %s, status: %d", string, number); + gds__log(msg); + fprintf(stderr, "%s\n", string); + abort(); +} + + +static void punt( TEXT * string) +{ +/************************************** + * + * p u n t + * + ************************************** + * + * Functional description + * + **************************************/ + + printf("(CSS)punt: global region corrupt -- %s\n", string); + +#ifdef DEBUG + dump(); +#endif +} + + +static int put_message( PTR connection_id, MSG message, int release_flag) +{ +/************************************** + * + * p u t _ m e s s a g e + * + ************************************** + * + * Functional description + * Post a message (formatted) to a given processes msg que. + * + **************************************/ + CNCT connection; + PRB process; + int sig_flag; + + connection = (CNCT) ABS_PTR(connection_id); + + if (!connection->cnct_partner) { + free_global(message); + return FALSE; + } + + message->msg_connection = connection_id; + process = (PRB) ABS_PTR(connection->cnct_partner); + insert_tail(&process->prb_messages, &message->msg_que); + + if (release_flag) + RELEASE; + + ISC_event_post(process->prb_event); + +#ifndef GATEWAY +#ifndef MULTI_THREAD + if (message->msg_type == MSG_event) + ISC_kill(process->prb_process_id, EVENT_SIGNAL); +#endif +#endif + + return TRUE; +} + + +#ifdef UNIX +static void release_semaphore( USHORT semaphore) +{ +/************************************** + * + * r e l e a s e _ s e m a p h o r e + * + ************************************** + * + * Functional description + * Release a no-longer needed (or newly allocated) semaphore. + * + **************************************/ + + CSS_header->csh_semaphores[semaphore >> 3] |= (1 << (semaphore & 7)); +} +#endif + + +static void remove_que( SRQ * node) +{ +/************************************** + * + * r e m o v e _ q u e + * + ************************************** + * + * Functional description + * Remove a node from a self-relative que. + * + **************************************/ + SRQ *que; + + que = (SRQ *) ABS_PTR(node->srq_forward); + que->srq_backward = node->srq_backward; + + que = (SRQ *) ABS_PTR(node->srq_backward); + que->srq_forward = node->srq_forward; + node->srq_forward = node->srq_backward = 0; +} + + +#ifdef VMS +static void set_timer( USHORT timeout, PRB process) +{ +/************************************** + * + * s e t _ t i m e r ( V M S ) + * + ************************************** + * + * Functional description + * Establish a timer to go off after a specified period + * of time. The timeout period is given in seconds and + * is assumed to be less than 3600 seconds. + * + **************************************/ + UCHAR asc_time[10]; + SLONG bin_time[2], desc[2]; + + sprintf(asc_time, "0 :%d:%d", timeout / 60, timeout % 60); + ISC_make_desc(asc_time, desc, 0); + if (sys$bintim(desc, bin_time) & 1) + sys$setimr(EVENT_FLAG, bin_time, timeout_ast, process, 0); +} +#endif + + +#ifndef VMS +static void set_timer( USHORT timeout, PRB process) +{ +/************************************** + * + * s e t _ t i m e r ( g e n e r i c ) + * + ************************************** + * + * Functional description + * Establish a timer to go off after a specified period + * of time. The timeout period is given in seconds and + * is assumed to be less than 3600 seconds. + * + **************************************/ +} +#endif + + +#ifdef PIPE_SERVER +#ifdef VMS +static void shutdown_section(void) +{ +/************************************** + * + * s h u t d o w n _ s e c t i o n + * + ************************************** + * + * Functional description + * Remove the mapped file. + * + **************************************/ + struct FAB fab; + +/* Delete the shared address space and deassign the file. */ + + sys$deltva(shmem_data->sh_mem_retadr, 0, 0); + sys$dassgn((USHORT) shmem_data->sh_mem_channel); + +/* Now try to delete the file by opening it with the delete flag set. */ + + fab = cc$rms_fab; + fab.fab$l_fna = shmem_data->sh_mem_filename + fab.fab$b_fns = strlen(shmem_data->sh_mem_filename); + fab.fab$l_fop = FAB$M_DLT; + fab.fab$b_fac = FAB$M_UPD | FAB$M_PUT; + fab.fab$b_shr = FAB$M_SHRGET | FAB$M_SHRPUT | FAB$M_UPI; + fab.fab$b_rfm = FAB$C_UDF; + + if (sys$open(&fab) & 1) + sys$dassgn(fab.fab$l_stv); +} +#endif +#endif + + +#ifdef VMS +static void timeout_ast( PRB process) +{ +/************************************** + * + * t i m e o u t _ a s t ( V M S ) + * + ************************************** + * + * Functional description + * Completion ast for set_timer. + * + **************************************/ + + ACQUIRE; + + process->prb_flags |= PRB_timeout; + ISC_event_post(process->prb_event); + + RELEASE; +} +#endif + diff --git a/src/csv/css_proto.h b/src/csv/css_proto.h new file mode 100644 index 0000000000..3cdfeeab18 --- /dev/null +++ b/src/csv/css_proto.h @@ -0,0 +1,52 @@ +/* + * PROGRAM: Central Server Interface + * MODULE: css_proto.h + * DESCRIPTION: Prototype header file for css.c + * + * 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): ______________________________________. + */ + +#ifndef _CSV_CSS_PROTO_H_ +#define _CSV_CSS_PROTO_H_ + +extern CSH CSS_acquire(void); +extern FRB CSS_alloc_message(USHORT, int); +extern BLK CSS_alloc_local(USHORT, USHORT); +extern int CSS_check_partner(PTR, USHORT); +extern PTR CSS_connect(SLONG); +extern PTR CSS_create_process(USHORT); +extern void CSS_disconnect(SLONG); +extern void CSS_free_global(FRB); +extern void CSS_free_local(BLK); +extern PTR CSS_find_process(SLONG); +extern MSG CSS_get_message(PTR, MSG, SSHORT); +#ifndef PIPE_SERVER +extern CSH CSS_init(STATUS *, USHORT); +#else +#ifdef GATEWAY +extern CSH CSS_init(STATUS *, USHORT, TEXT *); +#else +extern CSH CSS_init(STATUS *, USHORT, SSHORT); +#endif +#endif +extern void CSS_probe_processes(void); +extern int CSS_put_message(PTR, MSG, MSG); +extern void CSS_release(void); +extern void CSS_validate(void); + +#endif /* _CSV_CSS_PROTO_H_ */ diff --git a/src/csv/csu.cpp b/src/csv/csu.cpp new file mode 100644 index 0000000000..564ad696aa --- /dev/null +++ b/src/csv/csu.cpp @@ -0,0 +1,570 @@ +/* + * PROGRAM: Central Server + * MODULE: csu.c + * DESCRIPTION: Central Server Utility + * + * 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): ______________________________________. + */ +/* +$Id: csu.cpp,v 1.1.1.1 2001-05-23 13:25:33 tamlin Exp $ +*/ + +#include "../csv/csi.h" +#include "../jrd/license.h" +#include "../jrd/codes.h" +#include "../jrd/thd.h" +#include "../csv/css_proto.h" +#include "../jrd/gds_proto.h" + +#define ALLOC(type, length) CSS_alloc_local (type, length) +#define FREE(block) CSS_free_local (block) + +static void move(UCHAR *, UCHAR *, USHORT); +static void parse_command_line(int, TEXT **, USHORT *, CSN *, CSN *); +static void process_list_cmd(PTR, USHORT *); +static void process_nonlist_cmd(PTR, CSN); +static STATUS process_response(MSG_RESP, STATUS *, USHORT *); + +static TEXT error_buffer[1024]; +static USHORT return_buffer_length; +static UCHAR *return_buffer; + + +int CLIB_ROUTINE main( int argc, char **argv) +{ +/************************************** + * + * m a i n + * + ************************************** + * + * Functional description + * Run the central server utility. + * + **************************************/ + USHORT sw_list, listed_flag, flags; + CSN csv_enable_names, csv_other_names, cs_name; + STATUS status_vector[20]; + SRQ *que; + PRB process; + PTR connection, server, client; + CSH CSS_header; + SLONG process_number, process_numbers[256], *ptr, *end; + +#ifdef VMS + argc = VMS_parse(&argv, argc); +#endif + + csv_enable_names = csv_other_names = NULL; + sw_list = FALSE; + + THREAD_ENTER; + parse_command_line(argc, argv, &sw_list, &csv_enable_names, + &csv_other_names); + + if (!CSS_init(status_vector, FALSE)) { + printf("It's likely that no central servers are operating.\n"); + gds__print_status(status_vector); + exit(FINI_ERROR); + } + + client = CSS_create_process(PRB_client); + +/* Start by finding all server processes */ + + CSS_header = ACQUIRE; + CSS_probe_processes(); + + end = process_numbers; + + QUE_LOOP(CSS_header->csh_processes, que) { + process = (PRB) ((UCHAR *) que - OFFSET(PRB, prb_processes)); + server = REL_PTR(process); + if (client != server && + (process->prb_flags & PRB_server) && + process->prb_protocol_version == CSI_PROTOCOL_VERSION) + *end++ = process->prb_process_number; + } + + RELEASE; + +/* Go back and talk to servers */ + +/* Initialize a flag that indicates if anything has been listed */ + + listed_flag = FALSE; + + for (ptr = process_numbers; ptr < end; ptr++) { + process_number = *ptr; + if (server = CSS_find_process(process_number)) { + connection = CSS_connect(server); + + /* For each server name specified by the user, ask this + server to process it. */ + + for (cs_name = csv_enable_names; + cs_name && CSS_find_process(process_number); + cs_name = + cs_name->csn_next) process_nonlist_cmd(connection, cs_name); + + /* For each database name specified by the user, ask this + server to process it. */ + + for (cs_name = csv_other_names; + cs_name && CSS_find_process(process_number); + cs_name = cs_name->csn_next) if (cs_name->csn_flags) + process_nonlist_cmd(connection, cs_name); + + /* We may also need to do a list of this server's databases */ + + if (sw_list && CSS_find_process(process_number)) + process_list_cmd(connection, &listed_flag); + + CSS_disconnect(connection); + } + } + + if (sw_list && !listed_flag) + printf("\nNo central servers are operating.\n\n"); + + for (cs_name = csv_other_names; cs_name; cs_name = cs_name->csn_next) + if ((flags = cs_name->csn_flags) && !(flags & DBN_cmd_process)) { + printf("Database \"%s\" ", cs_name->csn_db_name); + if (cs_name->csn_cs_len) + printf("running on server\n \"%s\" could not be ", + cs_name->csn_cs_name); + else + printf("could not be\n "); + printf("found and so it was not %sed.\n", + (flags & DBN_reenable) ? "re-enabl" : (flags & DBN_kill) ? + "kill" : "disabl"); + } + + for (cs_name = csv_enable_names; cs_name; cs_name = cs_name->csn_next) + if (!(cs_name->csn_flags & DBN_cmd_process)) + printf + ("Server \"%s\" could not be found and so database\n \"%s\" was not enabled.\n", + cs_name->csn_cs_name, cs_name->csn_db_name); + + exit(FINI_OK); +} + + +static void move( UCHAR * from_ptr, UCHAR * to_ptr, USHORT length) +{ +/************************************** + * + * m o v e + * + ************************************** + * + * Functional description + * Move some bytes. + * + **************************************/ + USHORT l; + UCHAR *from, *to; + + from = from_ptr; + to = to_ptr; + + if (length) + do + *to++ = *from++; + while (--length); +} + + +static void parse_command_line( + int argc, + TEXT ** argv, + USHORT * sw_list, + CSN * csv_enable_names, CSN * csv_other_names) +{ +/************************************** + * + * p a r s e _ c o m m a n d _ l i n e + * + ************************************** + * + * Functional description + * Parse a Unix style command line. + * + **************************************/ + USHORT sw_enable, sw_name, sw_all, dbn_flag, all_flag; + USHORT length, listed_flag, csv_name_len; + TEXT **end, *p, c, expanded_name[256], *q, csv_name[256]; + CSN other_names, enable_names, name, *head; + + other_names = enable_names = NULL; + dbn_flag = all_flag = sw_enable = 0; + sw_name = FALSE; + + for (end = argv++ + argc; argv < end;) { + p = *argv++; + if (*p++ == '-') { + /* Clear -a, -e, and -n indicators for each new switch. Clear -d, + -k, and -r indicators except when the switch is "-a" or "-n". */ + + sw_name = sw_all = FALSE; + if (((c = UPPER(*p)) != 'A' && c != 'N') || *(p + 1) || sw_enable) + dbn_flag = 0; + sw_enable = csv_name_len = 0; + + while (c = *p++) + switch (UPPER(c)) { + case 'L': + *sw_list = TRUE; + break; + + case 'D': + dbn_flag |= DBN_disable; + break; + + case 'K': + dbn_flag |= DBN_kill; + break; + + case 'R': + dbn_flag |= DBN_reenable; + break; + + case 'E': + sw_enable = 1; + break; + + case 'A': + sw_all = TRUE; + break; + + case 'N': + sw_name = TRUE; + break; + + case 'Z': + printf("csu version %s\n", GDS_VERSION); + break; + } + + if (sw_enable && (dbn_flag || sw_all || sw_name)) { + printf + ("The -e switch may only be used in conjunction with\nthe -l and -z switches.\n"); + exit(FINI_ERROR); + } + else if (sw_all) + if (!dbn_flag) + printf + ("No command indicated with the -a switch. It will be ignored.\n"); + else { + all_flag |= dbn_flag; + dbn_flag = 0; + } + } + else if (!dbn_flag && !sw_enable && !sw_name) + printf + ("No command indicated for database \"%s\".\nThe name will be ignored.\n", + p - 1); + else { + if (sw_enable == 1 || sw_name) { + if ((length = strlen(--p)) >= sizeof(csv_name)) + length = sizeof(csv_name) - 1; + csv_name_len = length; + for (q = csv_name; length--;) { + c = *p++; + *q++ = UPPER(c); + } + if (sw_name) + sw_name = FALSE; + else + sw_enable++; + continue; + } + + length = ISC_expand_filename(p - 1, 0, expanded_name); + +#ifdef VMS + /* The next expansion is Gateway specific but we'll do it + for everyone. Since '@' isn't valid in a VMS filename, + this is ok. */ + + /* Look for an '@' after the first SCHARacter in expanded_name. + If one is found, try to expand the remainder of the string. */ + + if (*(p = expanded_name) != '@') + while (*p) + if (*p++ == '@' && *p) { + length = + ISC_expand_logical(p, + length - (p - expanded_name), + p) + (p - expanded_name); + break; + } +#endif + csv_name[csv_name_len] = 0; + + head = (sw_enable) ? &enable_names : &other_names; + for (name = *head; name; name = name->csn_next) + if (!strcmp(name->csn_db_name, expanded_name) && + !strcmp(name->csn_cs_name, csv_name)) + break; + + if (!name) { + name = + (CSN) ALLOC(type_csn, + sizeof(struct csn) + length + csv_name_len); + name->csn_next = *head; + *head = name; + name->csn_cs_name = name->csn_data; + name->csn_db_name = name->csn_data + csv_name_len + 1; + move(csv_name, name->csn_cs_name, csv_name_len + 1); + move(expanded_name, name->csn_db_name, length + 1); + name->csn_cs_len = csv_name_len; + name->csn_db_len = length; + } + + name->csn_flags |= (sw_enable) ? DBN_enable : dbn_flag; + } + } + + if (all_flag) { + /* We're to do something to all central server databases. + But first, make sure we won't try to do the same thing + or something similar to a specific database. Reenable + takes precedence over kill and disable. Kill takes + precedence over disable. */ + + if (all_flag & DBN_reenable) + all_flag |= DBN_kill | DBN_disable; + else if (all_flag & DBN_kill) + all_flag |= DBN_disable; + + for (name = other_names; name; name = name->csn_next) + name->csn_flags &= ~all_flag; + + /* The -a switch gets saved as a name with a zero length */ + + name = (CSN) ALLOC(type_csn, sizeof(struct csn)); + name->csn_next = other_names; + other_names = name; + name->csn_cs_name = name->csn_db_name = name->csn_data; + name->csn_cs_len = name->csn_db_len = 0; + name->csn_flags = all_flag; + } + + *csv_other_names = other_names; + *csv_enable_names = enable_names; +} + + +static void process_list_cmd( PTR connection, USHORT * listed_flag) +{ +/************************************** + * + * p r o c e s s _ l i s t _ c m d + * + ************************************** + * + * Functional description + * Process a list command for a server. + * + **************************************/ + MSG_UTIL message; + MSG_RESP response; + STATUS status_vector[20]; + USHORT l; + CSU_LIST list_msg; + + message = (MSG_UTIL) CSS_alloc_message(type_msg, sizeof(struct msg_util)); + message->msg_util_header.msg_type = MSG_util_cmd; + message->msg_util_cmd = UTIL_list; + message->msg_util_csn_len = message->msg_util_dbn_len = 0; + + if (!CSS_put_message(connection, message, 0) || + !(response = CSS_get_message(connection, 0, 0))) + return; + + if (!process_response(response, status_vector, 0)) { + list_msg = (CSU_LIST) return_buffer; + + /* Print a header before the first name listed */ + + if (!*listed_flag) { + printf("\nServer\n\tAttaches Flags Database Name\n\n"); + *listed_flag = TRUE; + } + + /* Print the server name */ + + printf("%s\n", list_msg->csu_list_name); + list_msg = + (CSU_LIST) (list_msg->csu_list_name + + FB_ALIGN(list_msg->csu_list_length + 1, 2)); + + /* Loop over returned database name info */ + + while (l = list_msg->csu_list_length) { + printf("\t%7d%s %c %c %s\n", list_msg->csu_list_attaches, + (list_msg->csu_list_flags & DBN_server_att) ? "+s" : " ", + (list_msg->csu_list_flags & DBN_disable) ? 'D' : ' ', + (list_msg->csu_list_flags & DBN_kill) ? 'K' : ' ', + list_msg->csu_list_name); + list_msg = + (CSU_LIST) (list_msg->csu_list_name + FB_ALIGN(l + 1, 2)); + } + } + else if (status_vector[1] != gds__unavailable) { + /**** DO SOMETHING MORE INTELLIGENT ****/ + } +} + + +static void process_nonlist_cmd( PTR connection, CSN cs_name) +{ +/************************************** + * + * p r o c e s s _ n o n l i s t _ c m d + * + ************************************** + * + * Functional description + * Process a disable, enable, kill, or re-enable command + * for a server. + * + **************************************/ + SLONG length; + MSG_UTIL message; + MSG_RESP response; + USHORT l, cmd; + STATUS status_vector[20]; + + l = cs_name->csn_cs_len + cs_name->csn_db_len; + length = sizeof(struct msg_util) + l; + message = (MSG_UTIL) CSS_alloc_message(type_msg, (int) length); + message->msg_util_header.msg_type = MSG_util_cmd; + if (cs_name->csn_flags & DBN_enable) + cmd = UTIL_enable; + else if (cs_name->csn_flags & DBN_reenable) + cmd = UTIL_reenable; + else if (cs_name->csn_flags & DBN_kill) + cmd = UTIL_kill; + else + cmd = UTIL_disable; + message->msg_util_cmd = cmd; + message->msg_util_csn_len = cs_name->csn_cs_len; + message->msg_util_dbn_len = cs_name->csn_db_len; + move(cs_name->csn_data, message->msg_util_data, l + 2); + + if (!CSS_put_message(connection, message, 0) || + !(response = CSS_get_message(connection, 0, 0))) + return; + + if (!process_response(response, status_vector, 0)) { + /* Set a flag to indicate that a central server has processed + the command relating to this server and database name */ + + cs_name->csn_flags |= DBN_cmd_process; + } + else if (status_vector[1] != gds__unavailable) + if (cs_name->csn_flags & DBN_enable) { + printf + ("Server \"%s\" could not enable database \"%s\" because:\n", + cs_name->csn_cs_name, cs_name->csn_db_name); + gds__print_status(status_vector); + cs_name->csn_flags |= DBN_cmd_process; + } + else { + /**** DO SOMETHING MORE INTELLIGENT ****/ + } +} + + +static STATUS process_response( + MSG_RESP message, + STATUS * user_status, USHORT * return_length) +{ +/************************************** + * + * p r o c e s s _ r e s p o n s e + * + ************************************** + * + * Functional description + * Process a response packet from server. + * + **************************************/ + TEXT *p, *q; + STATUS *status, *stuff, *end; + USHORT l; + +/* If there isn't a message, the server has disappeared */ + + if (!message || message->msg_resp_header.msg_type != MSG_response) { + status = user_status; + *status++ = gds_arg_gds; + *status++ = gds__random; + *status++ = gds_arg_string; + *status++ = (STATUS) "connection lost to central server"; + *status = gds_arg_end; + return user_status[1]; + } + +/* Well, at least we got something -- eat it up */ + + p = error_buffer; + + for (status = user_status, end = status + 20, stuff = + message->msg_resp_status; (*status = *stuff++) && status < end;) + switch (*status++) { + case gds_arg_interpreted: + case gds_arg_string: + q = (TEXT *) message + *stuff++; + *status++ = (STATUS) p; + while (*p++ = *q++); + break; + + case gds_arg_cstring: + l = *status++ = *stuff++; + q = (TEXT *) message + *stuff++; + *status++ = (STATUS) p; + if (l) + do + *p++ = *q++; + while (--l); + break; + + default: + *status++ = *stuff++; + } + + if (!user_status[1] || user_status[1] == gds__segment) { + if (return_buffer_length < (l = message->msg_resp_length)) { + if (return_buffer) + FREE(return_buffer); + return_buffer = (UCHAR *) ALLOC(0, l); + return_buffer_length = l; + } + if (l) + move(message->msg_resp_data, return_buffer, l); + if (return_length) + *return_length = l; + } + + CSS_free_global(message); + + return user_status[1]; +} diff --git a/src/csv/csv.cpp b/src/csv/csv.cpp new file mode 100644 index 0000000000..4714f53700 --- /dev/null +++ b/src/csv/csv.cpp @@ -0,0 +1,3612 @@ +/* + * PROGRAM: Central Server + * MODULE: csv.c + * DESCRIPTION: Central Server + * + * 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): ______________________________________. + */ +/* +$Id: csv.cpp,v 1.1.1.1 2001-05-23 13:25:34 tamlin Exp $ +*/ + +#include "../jrd/ib_stdio.h" +#include "../csv/csi.h" +#include "../include/jrd/gds.h" +#include "../jrd/license.h" +#include "../csv/css_proto.h" +#include "../jrd/gds_proto.h" +#include "../remote/merge_proto.h" + +#ifdef PIPE_SERVER +#ifdef VMS +#include "../jrd/lnmdef.h" +#include jpidef +#include descrip +#endif + +#endif + +#define ALLOC(type, length) CSS_alloc_local (type, length) +#define FREE(block) CSS_free_local (block) +#define EVENT_FLAG 65 + +static void allocate_statement(MSG_OP); +static void alt_connection(MSG_OP); +static void attach_database(MSG_ATT); +#ifndef PIPE_SERVER +static STATUS attach_for_servicing(DBN, STATUS *, USHORT); +#endif +static void cancel_events(MSG_OP); +static void check_if_done(DBN); +static void compile(MSG_OP); +static void ddl(MSG_DDL); +static void disable_or_kill(DBN, USHORT); +static void disconnect(PTR); + +#ifndef GATEWAY +static void drop_database(MSG_OP); +#endif + +static void end_blob(MSG_OP); +static void end_database(MSG_OP); +static void end_request(MSG_OP); +static void end_statement(MSG_OP); +static void end_transaction(MSG_OP); +static void execute_immediate(MSG_EXNOW); +static void execute_statement(MSG_SQLMSG); +#ifdef GATEWAY +#ifndef PIPE_SERVER +static int expand_filename(TEXT *, TEXT *); +#endif +#endif +static void fetch(MSG_SQLMSG); +static DBN find_dbname(TEXT *, USHORT, USHORT); +static void free_buffer(UCHAR *, USHORT); +static UCHAR *get_buffer(USHORT *); +static void get_segment(MSG_SEG); +static void get_slice(MSG_SLICE); +static void info(MSG_INFO); +static void insert(MSG_SQLMSG); +static RTR make_transaction(RDB, HANDLE); +#ifdef GATEWAY +#ifndef PIPE_SERVER +static void mdi_attach_db(MSG_OP); +#endif +#endif +static void move(UCHAR *, UCHAR *, USHORT); +static void multi_thread(void); +static void open_blob(MSG_BLOB); +static void ping(MSG_OP); +static void prepare_statement(MSG_PSTMT); +static void process_message(MSG); +static void put_segment(MSG_SEG); +static void put_slice(MSG_SLICE); +static void que_events(MSG_EVENT); +#ifdef GATEWAY +#ifndef PIPE_SERVER +static void query_connect(MSG_ATT); +#endif +#endif +static void receive_msg(MSG_MSG); +static void reconnect(MSG_OP); +static void release_array(ARRAY); +static void release_blob(RBL); +static void release_database(RDB); +static void release_request(RRQ); +static void release_sql_request(RSR); +static void release_transaction(RTR); +static void seek_blob(MSG_SEEK); +static void send_msg(MSG_MSG); +static int send_response(MSG, STATUS *, SLONG, USHORT, UCHAR *); +static void server_ast(EVNT, USHORT, UCHAR *); +static void set_cursor(MSG_SETCUR); +static void start(MSG_MSG); +static void start_and_send(MSG_MSG); +static void start_transaction(MSG_TRANS); +static void server_utility(MSG_UTIL); +#ifdef MULTI_THREAD +static void thread(void); +#endif +static void transact_request(MSG_TRRQ); +#ifdef PIPE_SERVER +#ifdef VMS +static void trans_logicals(void); +#endif +#endif +static void unwind(MSG_OP); + +static RDB CSV_databases; +static DBN CSV_dbnames; +static TEXT *CSV_name; +static USHORT CSV_name_len; +static UCHAR *blob_buffer; +static USHORT blob_buffer_length; +static USHORT sw_attach, sw_version, num_buffers, sw_shutdown; + +static SLONG threads_waiting; + +/* Transaction element block */ + +typedef struct teb { + SLONG *teb_database; + int teb_tpb_length; + UCHAR *teb_tpb; +} TEB; + +typedef struct req { + struct req *req_next; + MSG req_message; +} *REQ; + +static REQ request_que, free_requests; +static EVENT_T thread_event[1]; + +#ifdef PIPE_SERVER +#ifdef VMS +/* Define logical names that sub-process should inherit */ + +static SCHAR *inherit_logicals[] = { + "SYS$LOGIN", + "SYS$SCRATCH", + "SYS$NODE", + "SYS$INTERBASE", + 0 +}; +#endif +#endif + + +#ifndef GATEWAY +#define GDS_ATTACH_DATABASE gds__attach_database +#define GDS_BLOB_INFO gds__blob_info +#define GDS_CANCEL_BLOB gds__cancel_blob +#define GDS_CLOSE_BLOB gds__close_blob +#define GDS_COMMIT gds__commit_transaction +#define GDS_COMMIT_RETAINING gds__commit_retaining +#define GDS_COMPILE gds__compile_request +#define GDS_CREATE_BLOB gds__create_blob2 +#define GDS_CREATE_DATABASE gds__create_database +#define GDS_DATABASE_INFO gds__database_info +#define GDS_DDL gds__ddl +#define GDS_DETACH gds__detach_database +#define GDS_DROP_DATABASE isc_drop_database +#define GDS_GET_SEGMENT gds__get_segment +#define GDS_GET_SLICE gds__get_slice +#define GDS_OPEN_BLOB gds__open_blob2 +#define GDS_PREPARE2 gds__prepare_transaction2 +#define GDS_PUT_SEGMENT gds__put_segment +#define GDS_PUT_SLICE gds__put_slice +#define GDS_RECONNECT gds__reconnect_transaction +#define GDS_RECEIVE gds__receive +#define GDS_RELEASE_REQUEST gds__release_request +#define GDS_REQUEST_INFO gds__request_info +#define GDS_ROLLBACK gds__rollback_transaction +#define GDS_SEND gds__send +#define GDS_SEEK_BLOB gds__seek_blob +#define GDS_START_AND_SEND gds__start_and_send +#define GDS_START gds__start_request +#define GDS_START_MULTIPLE gds__start_multiple +#define GDS_START_TRANSACTION gds__start_transaction +#define GDS_TRANSACT_REQUEST isc_transact_request +#define GDS_TRANSACTION_INFO gds__transaction_info +#define GDS_UNWIND gds__unwind_request +#define GDS_QUE_EVENTS gds__que_events +#define GDS_CANCEL_EVENTS gds__cancel_events + +#define GDS_DSQL_ALLOCATE isc_dsql_allocate_statement +#define GDS_DSQL_EXECUTE isc_dsql_execute2_m +#define GDS_DSQL_EXECUTE_IMMED isc_dsql_exec_immed3_m +#define GDS_DSQL_FETCH isc_dsql_fetch_m +#define GDS_DSQL_FREE isc_dsql_free_statement +#define GDS_DSQL_INSERT isc_dsql_insert_m +#define GDS_DSQL_PREPARE isc_dsql_prepare_m +#define GDS_DSQL_SET_CURSOR isc_dsql_set_cursor_name +#define GDS_DSQL_SQL_INFO isc_dsql_sql_info +#else +#define GDS_ATTACH_DATABASE GWAY_attach_database +#define GDS_BLOB_INFO GWAY_blob_info +#define GDS_CANCEL_BLOB GWAY_cancel_blob +#define GDS_CLOSE_BLOB GWAY_close_blob +#define GDS_COMMIT GWAY_commit_transaction +#define GDS_COMMIT_RETAINING GWAY_commit_retaining +#define GDS_COMPILE GWAY_compile_request +#define GDS_CREATE_BLOB GWAY_create_blob2 +#define GDS_CREATE_DATABASE GWAY_create_database +#define GDS_DATABASE_INFO GWAY_database_info +#define GDS_DETACH GWAY_detach_database +#define GDS_GET_SEGMENT GWAY_get_segment +#define GDS_OPEN_BLOB GWAY_open_blob2 +#define GDS_PREPARE2 GWAY_prepare_transaction +#define GDS_PUT_SEGMENT GWAY_put_segment +#define GDS_RECONNECT GWAY_reconnect_transaction +#define GDS_RECEIVE GWAY_receive +#define GDS_RELEASE_REQUEST GWAY_release_request +#define GDS_REQUEST_INFO GWAY_request_info +#define GDS_ROLLBACK GWAY_rollback_transaction +#define GDS_SEEK_BLOB GWAY_seek_blob +#define GDS_SEND GWAY_send +#define GDS_START_AND_SEND GWAY_start_and_send +#define GDS_START GWAY_start_request +#define GDS_START_MULTIPLE GWAY_start_multiple +#define GDS_START_TRANSACTION GWAY_start_transaction +#define GDS_TRANSACT_REQUEST GWAY_transact_request +#define GDS_TRANSACTION_INFO GWAY_transaction_info +#define GDS_UNWIND GWAY_unwind_request +#define GDS_QUE_EVENTS GWAY_que_events +#define GDS_CANCEL_EVENTS GWAY_cancel_events +#define GDS_DDL GWAY_ddl +#endif + + +#ifndef PIPE_SERVER +int CLIB_ROUTINE main( int argc, char **argv) +{ +/************************************** + * + * m a i n ( c e n t r a l _ s e r v e r ) + * + ************************************** + * + * Functional description + * Start up central server. + * + **************************************/ + MSG message; + STATUS status_vector[20]; + USHORT sw_multi, sw_name, sw_buffers, sw_limit, length; + TEXT **end, *p, *q, c, expanded_name[256], dflt_name[16]; + DBN db_name; + CSH CSS_header; + PRB process; + SLONG debug_value; + +#ifdef VMS + argc = VMS_parse(&argv, argc); +#endif + + THREAD_ENTER; + +#ifndef GATEWAY + gds__enable_subsystem("GDSSHR"); + gds__enable_subsystem("GDSSHR5"); + num_buffers = 500; /* choose a liberal number of buffers */ +#else + num_buffers = 0; +#endif + +#ifdef MULTI_THREAD + sw_multi = TRUE; +#else + sw_multi = FALSE; +#endif + + sw_attach = sw_version = FALSE; + sw_name = sw_buffers = sw_limit = FALSE; + + for (end = argv++ + argc; argv < end;) { + p = *argv++; + if (*p == '-') { + sw_name = sw_buffers = sw_limit = FALSE; + while (c = *++p) + switch (UPPER(c)) { +#ifdef MULTI_THREAD + case 'M': + sw_multi = TRUE; + break; + + case 'S': + sw_multi = FALSE; + break; +#endif + + case 'A': + sw_attach = TRUE; + break; + + case 'N': + sw_name = TRUE; + break; + + case 'B': + sw_buffers = TRUE; + break; + + case 'L': + sw_limit = TRUE; + break; + + + case 'Z': + if (!sw_version) + printf("csv version %s\n", GDS_VERSION); + sw_version = TRUE; + break; + } + } + else if (!sw_name && !sw_buffers && !sw_limit) { +#ifndef GATEWAY + length = ISC_expand_filename(p, 0, expanded_name); +#else + if (!(length = expand_filename(p, expanded_name))) { + printf + ("%s is not a database that can be serviced\nby this central server.\n", + expanded_name); + exit(FINI_ERROR); + } +#endif + db_name = (DBN) ALLOC(type_dbn, sizeof(struct dbn) + length); + db_name->dbn_next = CSV_dbnames; + CSV_dbnames = db_name; + move(expanded_name, db_name->dbn_name, length + 1); + db_name->dbn_length = length; + } + else { + if (sw_name) { + CSV_name_len = length = strlen(p); + CSV_name = q = (TEXT *) ALLOC(0, CSV_name_len + 1); + do { + c = *p++; + *q++ = UPPER(c); + } + while (length--); + } + + if (sw_buffers) + num_buffers = atoi(p); + + if (sw_limit && CSV_dbnames) + CSV_dbnames->dbn_max_attaches = atoi(p); + + sw_name = sw_buffers = sw_limit = FALSE; + } + } + + if (!(CSS_header = CSS_init(status_vector, TRUE))) { + gds__print_status(status_vector); + exit(FINI_ERROR); + } + + if (sw_attach) + for (db_name = CSV_dbnames; db_name; db_name = db_name->dbn_next) + if (attach_for_servicing(db_name, status_vector, TRUE)) { + gds__print_status(status_vector); + exit(FINI_ERROR); + } + +#ifndef GATEWAY + process = (PRB) ABS_PTR(CSS_create_process(PRB_server)); +#else + process = (PRB) ABS_PTR(CSS_create_process(PRB_server | PRB_server_t1)); +#endif + + if (!CSV_name) { + sprintf(dflt_name, "CSV_%ld", process->prb_process_number); + CSV_name = dflt_name; + CSV_name_len = strlen(CSV_name); + } + + if (sw_multi) + multi_thread(); + else + while (!sw_shutdown) { + message = CSS_get_message((SLONG) 0, 0, 0); + process_message(message); + } +} +#endif + + +#ifdef PIPE_SERVER +#ifndef GATEWAY +int CLIB_ROUTINE main( int argc, char **argv) +{ +/************************************** + * + * m a i n ( p i p e _ s e r v e r ) + * + ************************************** + * + * Functional description + * Start up single user server. + * + **************************************/ + STATUS status_vector[20]; + CSH CSS_header; + SRQ *que; + PRB process; + PTR connection, server, client; + MSG_RESP response; + MSG message; + SSHORT parent_pin; + + THREAD_ENTER; + + gds__enable_subsystem("GDSSHR"); + gds__enable_subsystem("GDSSHR5"); + + parent_pin = FATHER(); + + if (!(CSS_header = CSS_init(status_vector, FALSE, parent_pin))) { + gds__print_status(status_vector); + exit(FINI_ERROR); + } + + client = (PRB) ABS_PTR(CSS_create_process(PRB_client)); + + CSS_header = ACQUIRE; + server = (PTR) 0; + + QUE_LOOP(CSS_header->csh_processes, que) { + process = (PRB) ((UCHAR *) que - OFFSET(PRB, prb_processes)); + if (client != REL_PTR(process) && + process->prb_protocol_version == CSI_PROTOCOL_VERSION && + process->prb_process_id == parent_pin) { + server = REL_PTR(process); + break; + } + } + + RELEASE; + + if (!server) { + printf("Unable to find parent process.\n"); + exit(FINI_ERROR); + } + + connection = CSS_connect(server); + response = CSS_alloc_message(type_msg, sizeof(struct msg_resp)); + response->msg_resp_header.msg_type = MSG_response; + response->msg_resp_length = 0; + if (CSS_put_message(connection, response, 0)) + while (!sw_shutdown && (message = CSS_get_message((SLONG) 0, 0, 0))) + process_message(message); +} +#endif + + +#ifdef GATEWAY +int CLIB_ROUTINE main( int argc, char **argv) +{ +/************************************** + * + * m a i n ( g a t e w a y _ p i p e _ s e r v e r ) + * + ************************************** + * + * Functional description + * Start up single user server. + * + **************************************/ + STATUS status_vector[20]; + TEXT csi_file[256], *p, *q; + CSH CSS_header; + SRQ *que; + PRB process; + PTR connection, server, client; + MSG_RESP response; + MSG message; + SLONG code; + USHORT len; + struct dsc$descriptor desc; + + THREAD_ENTER; + +/* Create the name of the global section. */ + + trans_logicals(); + q = csi_file - 1; + if (ISC_expand_logical_once + ("SYS$LOGIN", sizeof("SYS$LOGIN") - 1, csi_file)) { + for (p = csi_file; *p; p++) + if (*p == ':' || *p == ']') + q = p; + if (q < csi_file) + *(q = p) = ':'; + } + ISC_make_desc(++q, &desc, 15); + code = JPI$_PRCNAM; + if (!(lib$getjpi(&code, NULL, NULL, NULL, &desc, &len) & 1)) { + printf("Unable to find parent process.\n"); + exit(FINI_ERROR); + } + q[len] = 0; + while (*q) + if (*q++ == '_') { + while (*q && *q != '_') + q++; + *q = 0; + } + +#ifdef ORACLE_ALT + strcpy(q, "_ALT"); +#endif + + if (!(CSS_header = CSS_init(status_vector, FALSE, csi_file))) { + gds__print_status(status_vector); + exit(FINI_ERROR); + } + + client = (PRB) ABS_PTR(CSS_create_process(0)); + + CSS_header = ACQUIRE; + server = (PTR) 0; + + QUE_LOOP(CSS_header->csh_processes, que) { + process = (PRB) ((UCHAR *) que - OFFSET(PRB, prb_processes)); + if (client != REL_PTR(process) && + process->prb_flags & PRB_server && + process->prb_protocol_version == CSI_PROTOCOL_VERSION) { + server = REL_PTR(process); + break; + } + } + + RELEASE; + + if (!server) { + printf("Unable to find parent process.\n"); + exit(FINI_ERROR); + } + + connection = CSS_connect(server); + response = CSS_alloc_message(type_msg, sizeof(struct msg_resp)); + response->msg_resp_header.msg_type = MSG_response; + response->msg_resp_length = 0; + if (CSS_put_message(connection, response, 0)) + while (message = CSS_get_message(connection, 0, 0)) + process_message(message); + +/* If nothing was written to stderr, delete + the file. Ditto about stdout. */ + + if (!ftell(stderr)) { + fgetname(stderr, csi_file); + fclose(stderr); + remove(csi_file); + } + if (!ftell(stdout)) { + fgetname(stdout, csi_file); + fclose(stdout); + remove(csi_file); + } +} +#endif +#endif + + +static void allocate_statement( MSG_OP message) +{ +/************************************** + * + * a l l o c a t e _ s t a t e m e n t + * + ************************************** + * + * Functional description + * Allocate a statement handle. + * + **************************************/ + RDB rdb; + RSR statement; + HANDLE handle; + STATUS status_vector[20]; + + rdb = (RDB) message->msg_op_handle; + handle = NULL; + + THREAD_EXIT; + GDS_DSQL_ALLOCATE(status_vector, &rdb->rdb_handle, &handle); + THREAD_ENTER; + + if (!status_vector[1]) { + /* Allocate SQL request block */ + + statement = (RSR) ALLOC(type_rsr, sizeof(struct rsr)); + statement->rsr_handle = handle; + statement->rsr_rdb = rdb; + statement->rsr_next = rdb->rdb_sql_requests; + rdb->rdb_sql_requests = statement; + } + + send_response(message, status_vector, statement, 0, 0); +} + + +static void alt_connection( MSG_OP message) +{ +/************************************** + * + * a l t _ c o n n e c t i o n + * + ************************************** + * + * Functional description + * Accept a secondary connection for asynchronous events. + * + **************************************/ + RDB rdb; + STATUS status_vector[20]; + + rdb = (RDB) message->msg_op_handle; + rdb->rdb_connection2 = message->msg_op_header.msg_connection; + status_vector[0] = gds_arg_gds; + status_vector[1] = 0; + status_vector[2] = gds_arg_end; + send_response(message, status_vector, rdb, 0, 0); +} + + +static void attach_database( MSG_ATT message) +{ +/************************************** + * + * a t t a c h _ d a t a b a s e + * + ************************************** + * + * Functional description + * Process an attach or create packet. + * + **************************************/ + HANDLE handle; + STATUS status_vector[20], *sv; + RDB rdb; + TEXT *expanded_name; + UCHAR *dpb; + DBN db_name; + +/* Assume this is going to fail! */ + + status_vector[0] = gds_arg_gds; + status_vector[1] = gds__unavailable; + status_vector[2] = gds_arg_end; + +#ifdef GATEWAY +#ifndef PIPE_SERVER +/* Make sure we're willing to talk to this client */ + + if (!CSS_check_partner + (message->msg_att_header.msg_connection, PRB_client_t1)) { + send_response(message, status_vector, 0, 0, 0); + return; + } +#endif +#endif + + handle = NULL; + expanded_name = + (TEXT *) message->msg_att_data + message->msg_att_file_length; + dpb = + message->msg_att_data + message->msg_att_file_length + + message->msg_att_expanded_length; + + if (! + (db_name = + find_dbname(expanded_name, message->msg_att_expanded_length, FALSE)) +|| (db_name->dbn_max_attaches + && db_name->dbn_attaches >= db_name->dbn_max_attaches)) { + send_response(message, status_vector, 0, 0, 0); + return; + } + +/* Try initially with expanded name. If that fails, try once more with + original name */ + + rdb = NULL; + THREAD_EXIT; + + if (message->msg_att_header.msg_type == MSG_attach_database) + GDS_ATTACH_DATABASE(status_vector, + message->msg_att_expanded_length, + GDS_VAL(expanded_name), + GDS_REF(handle), + message->msg_att_dpb_length, GDS_VAL(dpb)); + else + GDS_CREATE_DATABASE(status_vector, + message->msg_att_expanded_length, + GDS_VAL(expanded_name), + GDS_REF(handle), + message->msg_att_dpb_length, GDS_VAL(dpb), 0); + + THREAD_ENTER; + + if (!status_vector[1]) { + rdb = (RDB) ALLOC(type_rdb, sizeof(struct rdb)); + rdb->rdb_handle = handle; + rdb->rdb_connection = message->msg_att_header.msg_connection; + rdb->rdb_dbn = db_name; + rdb->rdb_next = CSV_databases; + CSV_databases = rdb; + ++db_name->dbn_attaches; + } + + send_response(message, status_vector, rdb, 0, 0); +} + + +#ifndef PIPE_SERVER +static STATUS attach_for_servicing( + DBN db_name, + STATUS * status_vector, USHORT detach_flag) +{ +/************************************** + * + * a t t a c h _ f o r _ s e r v i c i n g + * + ************************************** + * + * Functional description + * Attach a database that is to be serviced + * by this central server. + * + **************************************/ + TEXT dummy[128]; + USHORT length; + HANDLE handle; + RDB rdb; + STATUS local_status[20]; + SCHAR string[16], *dpb, *p; + +/* generate the proper database parameter block */ + + p = dpb = string; + if (num_buffers) { + *p++ = gds_dpb_version1; + *p++ = gds__dpb_num_buffers; + *p++ = 2; + *p++ = num_buffers; + *p++ = num_buffers >> 8; + } + + handle = NULL; + length = (USHORT) (p - dpb); + THREAD_EXIT; + GDS_ATTACH_DATABASE(GDS_VAL(status_vector), + db_name->dbn_length, + GDS_VAL(db_name->dbn_name), + GDS_REF(handle), length, GDS_VAL(dpb)); +#ifndef GATEWAY + if (sw_version && !status_vector[1]) + gds__version(&handle, NULL, NULL); +#endif + THREAD_ENTER; + + if (!status_vector[1]) { + db_name->dbn_server = rdb = (RDB) ALLOC(type_rdb, sizeof(struct rdb)); + rdb->rdb_handle = handle; +#ifdef GATEWAY + db_name->dbn_length = + FRGN_analyze_attach(db_name->dbn_name, db_name->dbn_name, dummy, + dummy); +#endif + db_name->dbn_flags |= DBN_server_att; + } + else if (detach_flag) { + printf("Unable to attach to database %s.\n", db_name->dbn_name); + for (db_name = CSV_dbnames; db_name; db_name = db_name->dbn_next) + if (rdb = db_name->dbn_server) { + THREAD_EXIT; + GDS_DETACH(local_status, GDS_REF(rdb->rdb_handle)); + THREAD_ENTER; + FREE(rdb); + db_name->dbn_server = NULL; + } + } + + return status_vector[1]; +} +#endif + + +static void cancel_events( MSG_OP message) +{ +/************************************** + * + * c a n c e l _ e v e n t s + * + ************************************** + * + * Functional description + * Try to cancel an outstanding event. + * + **************************************/ + RDB rdb; + EVNT event; + SLONG id; + STATUS status_vector[20]; + + rdb = (RDB) message->msg_op_handle; + id = message->msg_op_level; + THREAD_EXIT; + + if (!GDS_CANCEL_EVENTS(status_vector, + GDS_REF(rdb->rdb_handle), + GDS_REF(id))) + for (event = rdb->rdb_events; event; event = event->evnt_next) + if (event->evnt_id == id) { + event->evnt_id = 0; + break; + } + + THREAD_ENTER; + + send_response(message, status_vector, rdb, 0, 0); +} + + +static void check_if_done( DBN db_name) +{ +/************************************** + * + * c h e c k _ i f _ d o n e + * + ************************************** + * + * Functional description + * Determine whether central server can exit. + * If it can, do so. + * + **************************************/ + RDB rdb; + STATUS status_vector[20]; + DBN *ptr; + +/* If the database isn't disabled, in the process of being + killed, or there are still attachments remaining, there's + nothing to do but return. */ + + if (!(db_name->dbn_flags & (DBN_disable | DBN_kill)) + || db_name->dbn_attaches) return; + +/* We're through with this database. If the central server had + its own attachment, get rid of it. */ + + if (rdb = db_name->dbn_server) { + THREAD_EXIT; + GDS_DETACH(status_vector, GDS_REF(rdb->rdb_handle)); + THREAD_ENTER; + FREE(rdb); + } + +/* Untangle database name block from global list of names */ + + for (ptr = &CSV_dbnames; *ptr; ptr = &(*ptr)->dbn_next) + if (*ptr == db_name) { + *ptr = db_name->dbn_next; + break; + } + + FREE(db_name); + +/* If no more name blocks remain, it's time to go! */ + + if (!CSV_dbnames) + sw_shutdown = TRUE; +} + + +static void compile( MSG_OP message) +{ +/************************************** + * + * c o m p i l e + * + ************************************** + * + * Functional description + * Compile and request. + * + **************************************/ + RDB rdb; + RRQ request; + HANDLE handle; + STATUS status_vector[20]; + + rdb = (RDB) message->msg_op_handle; + handle = NULL; + request = NULL; + + THREAD_EXIT; + GDS_COMPILE(status_vector, + GDS_REF(rdb->rdb_handle), + GDS_REF(handle), + message->msg_op_length, message->msg_op_data); + THREAD_ENTER; + + if (!status_vector[1]) { + request = (RRQ) ALLOC(type_rrq, sizeof(struct rrq)); + request->rrq_handle = handle; + request->rrq_rdb = rdb; + request->rrq_next = rdb->rdb_requests; + rdb->rdb_requests = request; + } + + send_response(message, status_vector, request, 0, 0); +} + + +static void ddl( MSG_DDL message) +{ +/************************************** + * + * d d l + * + ************************************** + * + * Functional description + * Do meta-data update. + * + **************************************/ + RDB rdb; + RTR transaction; + STATUS status_vector[20]; + + rdb = (RDB) message->msg_ddl_database; + transaction = (RTR) message->msg_ddl_transaction; + + THREAD_EXIT; + GDS_DDL(status_vector, + GDS_REF(rdb->rdb_handle), + GDS_REF(transaction->rtr_handle), + message->msg_ddl_length, message->msg_ddl_data); + THREAD_ENTER; + + send_response(message, status_vector, 0, 0, 0); +} + + +#ifndef PIPE_SERVER +static void disable_or_kill( DBN db_name, USHORT cmd) +{ +/************************************** + * + * d i s a b l e _ o r _ k i l l + * + ************************************** + * + * Functional description + * Disable, Kill, or Re-enable a database name for the + * central server utility. + * + **************************************/ + RDB *ptr, rdb; + +/* Set or clear the appropriate flag */ + + if (cmd == UTIL_reenable) { + db_name->dbn_flags &= ~DBN_disable; + return; + } + + db_name->dbn_flags |= (cmd == UTIL_kill) ? DBN_kill : DBN_disable; + +/* Do a quick check to see if we're done */ + + check_if_done(db_name); + + if (cmd == UTIL_disable) + return; + +/* For the kill command loop through the database attachments + and zap the ones attached to this database name */ + + for (ptr = &CSV_databases; rdb = *ptr;) + if (rdb->rdb_dbn == db_name) + disconnect(rdb->rdb_connection); + else + ptr = &(*ptr)->rdb_next; +} +#endif + + +static void disconnect( PTR connection) +{ +/************************************** + * + * d i s c o n n e c t + * + ************************************** + * + * Functional description + * Handle a disconnect message. + * + **************************************/ + RDB rdb; + RTR *ptr, transaction; + STATUS status_vector[20]; + +/* Start by breaking connection to remote */ + + CSS_disconnect(connection); + +/* Find related database block. If we can't find it, just + break connection. Otherwise, shutdown the database */ + + for (rdb = CSV_databases; rdb; rdb = rdb->rdb_next) + if (rdb->rdb_connection == connection) + break; + + if (!rdb) + return; + +/* Abort any outstanding, non-limbo transactions */ + + for (ptr = &rdb->rdb_transactions; transaction = *ptr;) + if (!(transaction->rtr_flags & RTR_limbo)) { + THREAD_EXIT; + GDS_ROLLBACK(status_vector, GDS_REF(transaction->rtr_handle)); + THREAD_ENTER; + release_transaction(transaction); + } + else + ptr = &(*ptr)->rtr_next; + + THREAD_EXIT; + GDS_DETACH(status_vector, GDS_REF(rdb->rdb_handle)); + THREAD_ENTER; + release_database(rdb); +} + + +#ifndef GATEWAY +static void drop_database( MSG_OP message) +{ +/************************************** + * + * d r o p _ d a t a b a s e + * + ************************************** + * + * Functional description + * End a request.. + * + **************************************/ + RDB rdb; + STATUS status_vector[20]; + + rdb = (RDB) message->msg_op_handle; + + THREAD_EXIT; + GDS_DROP_DATABASE(status_vector, rdb->rdb_handle); + THREAD_ENTER; + + send_response(message, status_vector, 0, 0, 0); + + if (!status_vector[1]) { + /* Start by breaking connection to remote */ + + CSS_disconnect(rdb->rdb_connection); + release_database(rdb); + } +} +#endif + + +static void end_blob( MSG_OP message) +{ +/************************************** + * + * e n d _ b l o b + * + ************************************** + * + * Functional description + * End a blob.. + * + **************************************/ + RBL blob; + STATUS status_vector[20]; + + blob = (RBL) message->msg_op_handle; + THREAD_EXIT; + + if (message->msg_op_header.msg_type == MSG_close_blob) + GDS_CLOSE_BLOB(status_vector, GDS_REF(blob->rbl_handle)); + else + GDS_CANCEL_BLOB(status_vector, GDS_REF(blob->rbl_handle)); + + THREAD_ENTER; + + if (!status_vector[1]) + release_blob(blob); + + send_response(message, status_vector, 0, 0, 0); +} + + +static void end_database( MSG_OP message) +{ +/************************************** + * + * e n d _ d a t a b a s e + * + ************************************** + * + * Functional description + * End a request.. + * + **************************************/ + RDB rdb; + STATUS status_vector[20]; + + rdb = (RDB) message->msg_op_handle; + +#ifdef GATEWAY +/* If there isn't a handle, then the server was queried but a + database wasn't opened. In that case, act as if everything + is normal so that we can release the rdb. */ + + if (!rdb->rdb_handle) { + status_vector[0] = gds_arg_gds; + status_vector[1] = 0; + } + else +#endif + { + THREAD_EXIT; + GDS_DETACH(status_vector, GDS_REF(rdb->rdb_handle)); + THREAD_ENTER; + } + + send_response(message, status_vector, 0, 0, 0); + + if (!status_vector[1]) { +#ifdef GATEWAY +#ifdef PIPE_SERVER +#define GWAY_PIPE +#endif +#endif + +#ifndef GWAY_PIPE + /* Start by breaking connection to remote */ + + CSS_disconnect(rdb->rdb_connection); +#endif + release_database(rdb); + } +} + + +static void end_request( MSG_OP message) +{ +/************************************** + * + * e n d _ r e q u e s t + * + ************************************** + * + * Functional description + * End a request.. + * + **************************************/ + RDB rdb; + RRQ request; + STATUS status_vector[20]; + + request = (RRQ) message->msg_op_handle; + THREAD_EXIT; + GDS_RELEASE_REQUEST(status_vector, GDS_REF(request->rrq_handle)); + THREAD_ENTER; + + if (!status_vector[1]) + release_request(request); + + send_response(message, status_vector, 0, 0, 0); +} + + +static void end_statement( MSG_OP message) +{ +/************************************** + * + * e n d _ s t a t e m e n t + * + ************************************** + * + * Functional description + * Free a statement. + * + **************************************/ + RDB rdb; + RSR statement; + STATUS status_vector[20]; + + statement = (RSR) message->msg_op_handle; + + THREAD_EXIT; + GDS_DSQL_FREE(status_vector, + &statement->rsr_handle, (USHORT) message->msg_op_level); + THREAD_ENTER; + + if (!status_vector[1] && !statement->rsr_handle) { + release_sql_request(statement); + statement = NULL; + } + + send_response(message, status_vector, statement, 0, 0); +} + + +static void end_transaction( MSG_OP message) +{ +/************************************** + * + * e n d _ t r a n s a c t i o n + * + ************************************** + * + * Functional description + * End a transaction. + * + **************************************/ + RDB rdb; + RTR transaction; + STATUS status_vector[20]; + + transaction = (RTR) message->msg_op_handle; + THREAD_EXIT; + + switch (message->msg_op_header.msg_type) { + case MSG_commit: + if (!GDS_COMMIT(status_vector, GDS_REF(transaction->rtr_handle))) + release_transaction(transaction); + break; + + case MSG_rollback: + if (!GDS_ROLLBACK(status_vector, GDS_REF(transaction->rtr_handle))) + release_transaction(transaction); + break; + + case MSG_commit_retaining: + if (!GDS_COMMIT_RETAINING(status_vector, + GDS_REF(transaction->rtr_handle))) + transaction->rtr_flags |= RTR_limbo; + break; + + case MSG_prepare: + if (!GDS_PREPARE2(status_vector, + GDS_REF(transaction->rtr_handle), + message->msg_op_length, + message->msg_op_data)) + transaction->rtr_flags |= RTR_limbo; + break; + } + + THREAD_ENTER; + send_response(message, status_vector, 0, 0, 0); +} + + +static void execute_immediate( MSG_EXNOW message) +{ +/************************************** + * + * e x e c u t e _ i m m e d i a t e + * + ************************************** + * + * Functional description + * Prepare and execute a statement + * + **************************************/ + RDB rdb; + RTR transaction; + SCHAR *string, *in_blr, *in_msg, *out_blr, *out_msg; + USHORT buffer_length; + HANDLE handle; + STATUS status_vector[20]; + + rdb = (RDB) message->msg_exnow_database; + transaction = (RTR) message->msg_exnow_transaction; + + in_msg = (SCHAR *) message->msg_exnow_data; + in_blr = in_msg + message->msg_exnow_in_msg_length; + string = in_blr + message->msg_exnow_in_blr_length; + out_blr = string + message->msg_exnow_SQL_length; + + buffer_length = message->msg_exnow_out_msg_length; + out_msg = get_buffer(&buffer_length); + + handle = (transaction) ? transaction->rtr_handle : (HANDLE) 0; + + THREAD_EXIT; + GDS_DSQL_EXECUTE_IMMED(status_vector, + &rdb->rdb_handle, + &handle, + message->msg_exnow_SQL_length, + string, + message->msg_exnow_SQL_dialect, + message->msg_exnow_in_blr_length, + in_blr, + message->msg_exnow_in_msg_type, + message->msg_exnow_in_msg_length, + in_msg, + message->msg_exnow_out_blr_length, + out_blr, + message->msg_exnow_out_msg_type, + message->msg_exnow_out_msg_length, out_msg); + THREAD_ENTER; + + if (!status_vector[1]) + if (transaction && !handle) { + release_transaction(transaction); + transaction = NULL; + } + else if (!transaction && handle) + transaction = make_transaction(rdb, handle); + + send_response(message, status_vector, transaction, + message->msg_exnow_out_msg_length, out_msg); + free_buffer(out_msg, buffer_length); +} + + +static void execute_statement( MSG_SQLMSG message) +{ +/************************************** + * + * e x e c u t e _ s t a t e m e n t + * + ************************************** + * + * Functional description + * Execute a non-SELECT dynamic SQL statement. + * + **************************************/ + RTR transaction; + RSR statement; + SCHAR *in_blr, *in_msg, *out_blr, *out_msg; + USHORT buffer_length; + HANDLE handle; + STATUS status_vector[20]; + + transaction = (RTR) message->msg_sqlmsg_transaction; + statement = (RSR) message->msg_sqlmsg_statement; + + in_msg = (SCHAR *) message->msg_sqlmsg_data; + in_blr = in_msg + message->msg_sqlmsg_in_msg_length; + out_blr = in_blr + message->msg_sqlmsg_in_blr_length; + + buffer_length = message->msg_sqlmsg_out_msg_length; + out_msg = get_buffer(&buffer_length); + + handle = (transaction) ? transaction->rtr_handle : (HANDLE) 0; + + THREAD_EXIT; + GDS_DSQL_EXECUTE(status_vector, + &handle, + &statement->rsr_handle, + message->msg_sqlmsg_in_blr_length, + in_blr, + message->msg_sqlmsg_in_msg_type, + message->msg_sqlmsg_in_msg_length, + in_msg, + message->msg_sqlmsg_out_blr_length, + out_blr, + message->msg_sqlmsg_out_msg_type, + message->msg_sqlmsg_out_msg_length, out_msg); + THREAD_ENTER; + + if (!status_vector[1]) + if (transaction && !handle) { + release_transaction(transaction); + transaction = NULL; + } + else if (!transaction && handle) + transaction = make_transaction(statement->rsr_rdb, handle); + + send_response(message, status_vector, transaction, + message->msg_sqlmsg_out_msg_length, out_msg); + free_buffer(out_msg, buffer_length); +} + + +#ifdef GATEWAY +#ifndef PIPE_SERVER +static expand_filename( TEXT * file_name, TEXT * expanded_name) +{ +/************************************** + * + * e x p a n d _ f i l e n a m e + * + ************************************** + * + * Functional description + * Fully expand a Gateway database name. + * + **************************************/ + USHORT length; + TEXT dummy[128], *p; + + length = ISC_expand_filename(file_name, 0, expanded_name); + +#ifdef VMS +/* Look for an '@' after the first character in expanded_filename. + If one is found, try to expand the remainder of the string. */ + + if (*(p = expanded_name) != '@') { + while (*p) + if (*p++ == '@' && *p) { + length = + ISC_expand_logical(p, length - (p - expanded_name), + p) + (p - expanded_name); + break; + } + } +#endif + + if (!FRGN_analyze_attach(expanded_name, dummy, dummy, dummy)) + return 0; + + return length; +} +#endif +#endif + + +static void fetch( MSG_SQLMSG message) +{ +/************************************** + * + * f e t c h + * + ************************************** + * + * Functional description + * Fetch next record from a dynamic SQL cursor. + * + **************************************/ + RSR statement; + USHORT msg_length; + UCHAR *msg; + STATUS s, status_vector[20]; + + statement = (RSR) message->msg_sqlmsg_statement; + msg_length = message->msg_sqlmsg_in_msg_length; + msg = get_buffer(&msg_length); + + THREAD_EXIT; + s = GDS_DSQL_FETCH(status_vector, + &statement->rsr_handle, + message->msg_sqlmsg_in_blr_length, + message->msg_sqlmsg_data, + message->msg_sqlmsg_in_msg_type, + message->msg_sqlmsg_in_msg_length, msg); + THREAD_ENTER; + + send_response(message, status_vector, s, + message->msg_sqlmsg_in_msg_length, msg); + free_buffer(msg, msg_length); +} + + +#ifndef PIPE_SERVER +static DBN find_dbname( + TEXT * expanded_name, + USHORT expanded_length, USHORT search_flag) +{ +/************************************** + * + * f i n d _ d b n a m e ( c e n t r a l _ s e r v e r ) + * + ************************************** + * + * Functional description + * Find a database name in the list of database that + * can be serviced by this server. + * + **************************************/ + DBN db_name; + + for (db_name = CSV_dbnames; db_name; db_name = db_name->dbn_next) + if (search_flag || !(db_name->dbn_flags & (DBN_disable | DBN_kill))) +#ifndef GATEWAY + if (!strncmp(db_name->dbn_name, expanded_name, expanded_length)) +#else + if (!strncmp + (db_name->dbn_name, expanded_name, db_name->dbn_length)) +#endif + return db_name; + + return NULL; +} +#endif + + +#ifdef PIPE_SERVER +static DBN find_dbname( + TEXT * expanded_name, + USHORT expanded_length, USHORT search_flag) +{ +/************************************** + * + * f i n d _ d b n a m e ( p i p e _ s e r v e r ) + * + ************************************** + * + * Functional description + * Find a database name in the list of database that + * can be serviced by this server. + * + **************************************/ + + if (!CSV_dbnames) + CSV_dbnames = (DBN) ALLOC(type_dbn, sizeof(struct dbn)); + + return CSV_dbnames; +} +#endif + + +static void free_buffer( UCHAR * buffer, USHORT length) +{ +/************************************** + * + * f r e e _ b u f f e r + * + ************************************** + * + * Functional description + * Release a buffer. Keep one hanging + * around for the next guy. If + * another thread has already released + * one, let the smallest one go. + * + **************************************/ + + if (!buffer) + return; + + if (!blob_buffer) { + blob_buffer = buffer; + blob_buffer_length = length; + return; + } + + if (blob_buffer_length < length) { + FREE(blob_buffer); + blob_buffer = buffer; + blob_buffer_length = length; + return; + } + + FREE(buffer); +} + + +static UCHAR *get_buffer( USHORT * length) +{ +/************************************** + * + * g e t _ b u f f e r + * + ************************************** + * + * Functional description + * Get a buffer "big enough". + * Multiple buffers could be in use by + * multiple threads, so be sure to + * allocate a new one in that case. + * + **************************************/ + UCHAR *buffer; + + if (!*length) + return NULL; + + if (buffer = blob_buffer) { + blob_buffer = NULL; + if (blob_buffer_length >= *length) { + *length = blob_buffer_length; + return buffer; + } + else + FREE(buffer); + } + + return (UCHAR *) ALLOC(0, *length); +} + + +static void get_segment( MSG_SEG message) +{ +/************************************** + * + * g e t _ s e g m e n t + * + ************************************** + * + * Functional description + * Get a single blob segment. + * + **************************************/ + RDB rdb; + RBL blob; + USHORT l, buffer_length, length; + UCHAR *p, *buffer; + HANDLE state; + STATUS status_vector[20]; + + blob = (RBL) message->msg_seg_handle; + buffer_length = message->msg_seg_buffer_length; + buffer = get_buffer(&buffer_length); + + if (message->msg_seg_header.msg_type == MSG_get_segment) { + /* Do the call. We basically don't care whether it succeeds or not */ + + length = 0; + THREAD_EXIT; + GDS_GET_SEGMENT(status_vector, + GDS_REF(blob->rbl_handle), + GDS_REF(length), + message->msg_seg_buffer_length, GDS_VAL(buffer)); + THREAD_ENTER; + + send_response(message, status_vector, 0, length, buffer); + } + else { /* msg_type == MSG_get_segment_buf */ + + /* Gobble up a buffer's worth of segments */ + + p = buffer; + state = (HANDLE) 0; + + while (buffer_length > 2) { + buffer_length -= 2; + p += 2; + THREAD_EXIT; + GDS_GET_SEGMENT(status_vector, + GDS_REF(blob->rbl_handle), + GDS_REF(length), buffer_length, GDS_VAL(p)); + THREAD_ENTER; + if (status_vector[1] == gds__segstr_eof) { + state = (HANDLE) 2; + status_vector[1] = SUCCESS; + p -= 2; + break; + } + if (status_vector[1] && (status_vector[1] != gds__segment)) { + p -= 2; + break; + } + p[-2] = length; + p[-1] = length >> 8; + p += length; + buffer_length -= length; + if (status_vector[1] == gds__segment) { + state = (HANDLE) 1; + status_vector[1] = SUCCESS; + break; + } + } + + length = p - buffer; + send_response(message, status_vector, state, length, buffer); + } + + free_buffer(buffer, buffer_length); +} + + +static void get_slice( MSG_SLICE message) +{ +/************************************** + * + * g e t _ s l i c e + * + ************************************** + * + * Functional description + * Snatch a slice of an array. + * + **************************************/ + RDB rdb; + RTR transaction; + UCHAR *sdl, *param, *slice; + ARRAY array, handle; + USHORT length; + STATUS status_vector[20]; + + rdb = (RDB) message->msg_slice_database; + transaction = (RTR) message->msg_slice_transaction; + +/* Arrays can be longer than 64k bytes but we limit central server + messages to that length. Therefore we may need to send the + slice back to the user in pieces. */ + + if (!(array = (ARRAY) message->msg_slice_handle)) { + /* This is the first time through for this array. Allocate memory + to hold the array and link it in with the transaction. Then get + the data from the access method. */ + + array = + (ARRAY) gds__alloc(sizeof(struct array) + + message->msg_slice_slice_length - 1); + array->array_rdb = rdb; + array->array_rtr = transaction; + array->array_length = 0; + array->array_slice = array->array_data; + array->array_next = transaction->rtr_arrays; + transaction->rtr_arrays = array; + + sdl = message->msg_slice_data; + param = sdl + message->msg_slice_sdl_length; + + THREAD_EXIT; + + GDS_GET_SLICE(status_vector, + GDS_REF(rdb->rdb_handle), + GDS_REF(transaction->rtr_handle), + message->msg_slice_id, + message->msg_slice_sdl_length, + GDS_VAL(sdl), + message->msg_slice_param_length, + GDS_VAL(param), + message->msg_slice_slice_length, + GDS_VAL(array->array_data), + GDS_REF(array->array_length)); + + THREAD_ENTER; + } + else { + status_vector[0] = gds_arg_gds; + status_vector[1] = 0; + status_vector[2] = gds_arg_end; + + /* We've already retrieved the array. Figure out where we + saved the stuff and then send the next piece. */ + + for (handle = transaction->rtr_arrays; handle; + handle = handle->array_next) if (handle == array) + break; + + if (!handle || + handle->array_rdb != rdb || handle->array_rtr != transaction) { + array = NULL; + status_vector[1] = gds__segstr_no_op; + } + } + + if (!status_vector[1]) { + length = MIN(array->array_length, MAX_ARRAY_MESSAGE); + slice = array->array_slice; + handle = (length < array->array_length) ? array : NULL; + } + else { + length = 0; + handle = NULL; + } + + send_response(message, status_vector, handle, length, slice); + +/* If we're done with the array, get rid of the memory that was used to + hold it. If there's more left to send, increment the pointers. */ + + if (array) + if (length && length < array->array_length) { + array->array_length -= (SLONG) length; + array->array_slice += length; + } + else + release_array(array); +} + + +static void info( MSG_INFO message) +{ +/************************************** + * + * i n f o + * + ************************************** + * + * Functional description + * Issue information. + * + **************************************/ + HANDLE handle; + STATUS status_vector[20]; + USHORT buffer_length; + UCHAR *buffer, temp[1024]; + RDB rdb; + RTR transaction; + RBL blob; + RRQ request; + RSR statement; + + handle = message->msg_info_handle; + buffer_length = message->msg_info_buffer_length; + buffer = get_buffer(&buffer_length); + THREAD_EXIT; + + switch (message->msg_info_header.msg_type) { + case MSG_database_info: + rdb = (RDB) handle; + if (!GDS_DATABASE_INFO(status_vector, + GDS_REF(rdb->rdb_handle), + message->msg_info_length, + message->msg_info_data, + sizeof(temp), + temp)) + MERGE_database_info(temp, buffer, + message->msg_info_buffer_length, +#ifndef PIPE_SERVER + IMPLEMENTATION, 10, 1, GDS_VERSION, "", + 0); +#else + IMPLEMENTATION, 8, 1, GDS_VERSION, "", 0); +#endif + break; + + case MSG_blob_info: + blob = (RBL) handle; + GDS_BLOB_INFO(status_vector, + GDS_REF(blob->rbl_handle), + message->msg_info_length, + message->msg_info_data, + message->msg_info_buffer_length, GDS_VAL(buffer)); + break; + + case MSG_request_info: + request = (RRQ) handle; + GDS_REQUEST_INFO(status_vector, + GDS_REF(request->rrq_handle), + message->msg_info_level, + message->msg_info_length, + message->msg_info_data, + message->msg_info_buffer_length, GDS_VAL(buffer)); + break; + + case MSG_transaction_info: + transaction = (RTR) handle; + GDS_TRANSACTION_INFO(status_vector, + GDS_REF(transaction->rtr_handle), + message->msg_info_length, + message->msg_info_data, + message->msg_info_buffer_length, + GDS_VAL(buffer)); + break; + + case MSG_sql_info: + statement = (RSR) handle; + GDS_DSQL_SQL_INFO(status_vector, + &statement->rsr_handle, + message->msg_info_length, + message->msg_info_data, + message->msg_info_buffer_length, buffer); + break; + } + + THREAD_ENTER; + send_response(message, status_vector, 0, message->msg_info_buffer_length, + buffer); + free_buffer(buffer, buffer_length); +} + + +static void insert( MSG_SQLMSG message) +{ +/************************************** + * + * i n s e r t + * + ************************************** + * + * Functional description + * Insert next record into a dynamic SQL cursor. + * + **************************************/ + RSR statement; + UCHAR *blr, *msg; + STATUS status_vector[20]; + + statement = (RSR) message->msg_sqlmsg_statement; + + msg = (SCHAR *) message->msg_sqlmsg_data; + blr = msg + message->msg_sqlmsg_in_msg_length; + + THREAD_EXIT; + GDS_DSQL_INSERT(status_vector, + &statement->rsr_handle, + message->msg_sqlmsg_in_blr_length, + blr, + message->msg_sqlmsg_in_msg_type, + message->msg_sqlmsg_in_msg_length, msg); + THREAD_ENTER; + + send_response(message, status_vector, 0, 0, 0); +} + + +static RTR make_transaction( RDB rdb, HANDLE handle) +{ +/************************************** + * + * m a k e _ t r a n s a c t i o n + * + ************************************** + * + * Functional description + * Create a local transaction handle. + * + **************************************/ + RTR transaction; + + transaction = (RTR) ALLOC(type_rtr, sizeof(struct rtr)); + transaction->rtr_rdb = rdb; + transaction->rtr_handle = handle; + transaction->rtr_next = rdb->rdb_transactions; + rdb->rdb_transactions = transaction; + + return transaction; +} + + +#ifdef GATEWAY +#ifndef PIPE_SERVER +static void mdi_attach_db( MSG_OP message) +{ +/************************************** + * + * m d i _ a t t a c h _ d b + * + ************************************** + * + * Functional description + * Process an alternative attach packet. + * + **************************************/ + HANDLE handle; + STATUS status_vector[20]; + RDB rdb; + DBN db_name; + + rdb = (RDB) message->msg_op_handle; + + if (! + (db_name = + find_dbname(message->msg_op_data, message->msg_op_length, FALSE)) +|| db_name != rdb->rdb_dbn) { + status_vector[0] = gds_arg_gds; + status_vector[1] = gds__unavailable; + status_vector[2] = gds_arg_end; + { + send_response(message, status_vector, rdb, 0, 0); + return; + } + } + +/* Try initially with expanded name. If that fails, try once more with + original name */ + + handle = NULL; + THREAD_EXIT; + GDS_ATTACH_DATABASE(status_vector, + message->msg_op_length, + message->msg_op_data, GDS_REF(handle), 0, 0); + THREAD_ENTER; + + if (!status_vector[1]) + rdb->rdb_handle = handle; + + send_response(message, status_vector, rdb, 0, 0); +} +#endif +#endif + + +static void move( UCHAR * from_ptr, UCHAR * to_ptr, USHORT length) +{ +/************************************** + * + * m o v e + * + ************************************** + * + * Functional description + * Move some bytes. + * + **************************************/ + USHORT l; + SCHAR *from, *to; + + from = from_ptr; + to = to_ptr; + + if (length) + do + *to++ = *from++; + while (--length); +} + + +static void multi_thread(void) +{ +/************************************** + * + * m u l t i _ t h r e a d + * + ************************************** + * + * Functional description + * Multi-threaded flavor of server. + * + **************************************/ +#ifdef MULTI_THREAD + REQ request, *ptr; + SLONG pending_requests; + + gds__thread_enable(TRUE); + ISC_event_init(thread_event, EVENT_FLAG); + + while (!sw_shutdown) { + if (request = free_requests) + free_requests = request->req_next; + if (!request) + request = (REQ) gds__alloc((SLONG) sizeof(struct req)); + request->req_next = NULL; + request->req_message = CSS_get_message((SLONG) 0, 0, 0); + + for (pending_requests = 1, ptr = &request_que; *ptr; + ptr = &(*ptr)->req_next) ++pending_requests; + *ptr = request; + + if (pending_requests > threads_waiting) + gds__thread_start(thread); + ISC_event_post(thread_event); + } + + while (request = request_que) { + request_que = request->req_next; + CSS_free_global(request->req_message); + } +#endif +} + + +static void open_blob( MSG_BLOB message) +{ +/************************************** + * + * o p e n _ b l o b + * + ************************************** + * + * Functional description + * Open or create a new blob. + * + **************************************/ + RDB rdb; + RBL blob; + RTR transaction; + USHORT length; + HANDLE handle; + STATUS status_vector[20]; + + rdb = (RDB) message->msg_blob_database; + transaction = (RTR) message->msg_blob_transaction; + handle = NULL; + blob = NULL; + length = 0; + THREAD_EXIT; + + if (message->msg_blob_header.msg_type == MSG_open_blob) { + GDS_OPEN_BLOB(status_vector, + GDS_REF(rdb->rdb_handle), + GDS_REF(transaction->rtr_handle), + GDS_REF(handle), + message->msg_blob_id, + message->msg_blob_bpb_length, message->msg_blob_bpb); + } + else { + GDS_CREATE_BLOB(status_vector, + GDS_REF(rdb->rdb_handle), + GDS_REF(transaction->rtr_handle), + GDS_REF(handle), + message->msg_blob_id, + message->msg_blob_bpb_length, message->msg_blob_bpb); + length = sizeof(message->msg_blob_id); + } + + THREAD_ENTER; + + if (!status_vector[1]) { + blob = (RBL) ALLOC(type_rbl, sizeof(struct rbl)); + blob->rbl_handle = handle; + blob->rbl_rdb = rdb; + blob->rbl_rtr = transaction; + blob->rbl_next = transaction->rtr_blobs; + transaction->rtr_blobs = blob; + } + + send_response(message, status_vector, blob, length, message->msg_blob_id); +} + + +static void ping( MSG_OP message) +{ +/************************************** + * + * p i n g + * + ************************************** + * + * Functional description + * Bounce a message straight back. + * + **************************************/ + RRQ request; + STATUS status_vector[20]; + + status_vector[0] = gds_arg_gds; + status_vector[1] = 0; + send_response(message, status_vector, 0, 0, 0); +} + + +static void prepare_statement( MSG_PSTMT message) +{ +/************************************** + * + * p r e p a r e _ s t a t e m e n t + * + ************************************** + * + * Functional description + * Prepare a dynamic SQL statement for execution. + * + **************************************/ + RTR transaction; + RSR statement; + SCHAR *string, *items; + USHORT buffer_length; + UCHAR *buffer; + STATUS status_vector[20]; + HANDLE handle; + + transaction = (RTR) message->msg_pstmt_transaction; + statement = (RSR) message->msg_pstmt_statement; + + handle = (transaction) ? transaction->rtr_handle : (HANDLE) 0; + + string = (SCHAR *) message->msg_pstmt_data; + items = string + message->msg_pstmt_SQL_length; + buffer_length = message->msg_pstmt_buffer_length; + buffer = get_buffer(&buffer_length); + + THREAD_EXIT; + GDS_DSQL_PREPARE(status_vector, + &handle, + &statement->rsr_handle, + message->msg_pstmt_SQL_length, + string, + message->msg_pstmt_SQL_dialect, + message->msg_pstmt_item_length, + items, buffer_length, buffer); + THREAD_ENTER; + + send_response(message, status_vector, 0, message->msg_pstmt_buffer_length, + buffer); + free_buffer(buffer, buffer_length); +} + + +static void process_message( MSG message) +{ +/************************************** + * + * p r o c e s s _ m e s s a g e + * + ************************************** + * + * Functional description + * Handle a single message. + * + **************************************/ + + switch (message->msg_type) { + case MSG_alt_connection: + alt_connection(message); + break; + + case MSG_attach_database: + case MSG_create_database: + attach_database(message); + break; + + case MSG_compile: + compile(message); + break; + + case MSG_ddl: + ddl(message); + break; + + case MSG_cancel_events: + cancel_events(message); + break; + + case MSG_detach: + end_database(message); + break; + + case MSG_drop_database: + drop_database(message); + break; + + case MSG_que_events: + que_events(message); + break; + + case MSG_receive: + receive_msg(message); + break; + + case MSG_send: + send_msg(message); + break; + + case MSG_start_request: + start(message); + break; + + case MSG_start_and_send: + start_and_send(message); + break; + + case MSG_start_transaction: + start_transaction(message); + break; + + case MSG_prepare: + case MSG_rollback: + case MSG_commit: + case MSG_commit_retaining: + end_transaction(message); + break; + + case MSG_create_blob: + case MSG_open_blob: + open_blob(message); + break; + + case MSG_put_segment: + case MSG_put_segment_buf: + put_segment(message); + break; + + case MSG_get_segment: + case MSG_get_segment_buf: + get_segment(message); + break; + + case MSG_seek_blob: + seek_blob(message); + break; + + case MSG_get_slice: + get_slice(message); + break; + + case MSG_put_slice: + put_slice(message); + break; + + case MSG_cancel_blob: + case MSG_close_blob: + end_blob(message); + break; + + case MSG_release: + end_request(message); + break; + + case MSG_reconnect: + reconnect(message); + break; + + case MSG_transact_request: + transact_request(message); + break; + + case MSG_unwind: + unwind(message); + break; + + case MSG_blob_info: + case MSG_request_info: + case MSG_database_info: + case MSG_transaction_info: + case MSG_sql_info: + info(message); + break; + + case MSG_ping: + ping(message); + break; + +#ifdef GATEWAY +#ifndef PIPE_SERVER + case MSG_mdi_attach_db: + mdi_attach_db(message); + break; + + case MSG_query_connect: + query_connect(message); + break; +#endif +#endif + + case MSG_allocate_stmt: + allocate_statement(message); + break; + + case MSG_execute: + execute_statement(message); + break; + + case MSG_execute_immediate: + execute_immediate(message); + break; + + case MSG_fetch: + fetch(message); + break; + + case MSG_free_stmt: + end_statement(message); + break; + + case MSG_insert: + insert(message); + break; + + case MSG_prepare_stmt: + prepare_statement(message); + break; + + case MSG_set_cursor: + set_cursor(message); + break; + + case MSG_util_cmd: + server_utility(message); + break; + + case MSG_disconnect: + disconnect(message->msg_connection); + CSS_free_global(message); + break; + + default: + printf("Unknown message type %d\n", message->msg_type); + } +} + + +static void put_segment( MSG_SEG message) +{ +/************************************** + * + * p u t _ s e g m e n t + * + ************************************** + * + * Functional description + * Write a single blob segment. + * + **************************************/ + RDB rdb; + RBL blob; + USHORT buffer_length, length; + UCHAR *buffer, *end; + STATUS status_vector[20]; + + blob = (RBL) message->msg_seg_handle; + buffer_length = message->msg_seg_length; + buffer = message->msg_seg_data; + + if (message->msg_seg_header.msg_type == MSG_put_segment) { + /* Do the signal segment version. If it failed, just pass on the + bad news. */ + + THREAD_EXIT; + GDS_PUT_SEGMENT(status_vector, + GDS_REF(blob->rbl_handle), + buffer_length, GDS_VAL(buffer)); + THREAD_ENTER; + } + else { /* msg_type == MSG_put_segment_buf */ + + /* We've got a batch of segments. This is only slightly more complicated */ + + end = buffer + buffer_length; + + while (buffer < end) { + length = *buffer++; + length += *buffer++ << 8; + THREAD_EXIT; + GDS_PUT_SEGMENT(status_vector, + GDS_REF(blob->rbl_handle), + length, GDS_VAL(buffer)); + THREAD_ENTER; + if (status_vector[1]) + break; + buffer += length; + } + } + + send_response(message, status_vector, 0, 0, 0); +} + + +static void put_slice( MSG_SLICE message) +{ +/************************************** + * + * p u t _ s l i c e + * + ************************************** + * + * Functional description + * Put a slice of an array. + * + **************************************/ + RDB rdb; + RTR transaction; + UCHAR *sdl, *param; + ARRAY array, handle; + USHORT length; + STATUS status_vector[20]; + register UCHAR *from, *to; + + status_vector[0] = gds_arg_gds; + status_vector[1] = 0; + status_vector[2] = gds_arg_end; + + rdb = (RDB) message->msg_slice_database; + transaction = (RTR) message->msg_slice_transaction; + +/* Arrays can be longer than 64k bytes but we limit central server + messages to that length. Therefore we may need to get the slice + from the user in pieces. */ + + if (!(array = (ARRAY) message->msg_slice_handle)) { + /* This is the first time through for this array. Allocate memory + to hold the array and link it in with the transaction. */ + + array = + (ARRAY) gds__alloc(sizeof(struct array) + + message->msg_slice_slice_length - 1); + array->array_rdb = rdb; + array->array_rtr = transaction; + array->array_length = message->msg_slice_slice_length; + array->array_slice = array->array_data; + array->array_next = transaction->rtr_arrays; + transaction->rtr_arrays = array; + } + else { + /* We've already allocated the array. Figure out where we + were and save the next piece. */ + + for (handle = transaction->rtr_arrays; handle; + handle = handle->array_next) if (handle == array) + break; + + if (!handle || + handle->array_rdb != rdb || handle->array_rtr != transaction) { + array = NULL; + status_vector[1] = gds__segstr_no_op; + } + } + + if (!status_vector[1]) { + length = MIN(array->array_length, MAX_ARRAY_MESSAGE); + array->array_length -= (SLONG) length; + + sdl = message->msg_slice_data; + param = sdl + message->msg_slice_sdl_length; + from = param + message->msg_slice_param_length;; + + to = array->array_slice; + if (length) + do + *to++ = *from++; + while (--length); + + array->array_slice = to; + + if (!array->array_length) { + array->array_length = array->array_slice - array->array_data; + THREAD_EXIT; + + GDS_PUT_SLICE(status_vector, + GDS_REF(rdb->rdb_handle), + GDS_REF(transaction->rtr_handle), + message->msg_slice_id, + message->msg_slice_sdl_length, + GDS_VAL(sdl), + message->msg_slice_param_length, + GDS_VAL(param), + array->array_length, GDS_VAL(array->array_data)); + + THREAD_ENTER; + + release_array(array); + array = NULL; + } + } + + length = status_vector[1] ? 0 : 8; + send_response(message, status_vector, array, length, + message->msg_slice_id); +} + + +static void que_events( MSG_EVENT message) +{ +/************************************** + * + * q u e _ e v e n t s + * + ************************************** + * + * Functional description + * Que a request for event notification. + * + **************************************/ + RDB rdb; + EVNT event; + STATUS status_vector[20]; + + rdb = (RDB) message->msg_event_database; + +/* Find available event block */ + + for (event = rdb->rdb_events; event; event = event->evnt_next) + if (!event->evnt_id) + break; + + if (!event) { + event = (EVNT) CSS_alloc_local(type_evnt, sizeof(struct evnt)); + event->evnt_next = rdb->rdb_events; + rdb->rdb_events = event; + event->evnt_rdb = rdb; + } + + event->evnt_ast = message->msg_event_ast; + event->evnt_arg = message->msg_event_arg; + + THREAD_EXIT; + GDS_QUE_EVENTS(status_vector, + GDS_REF(rdb->rdb_handle), + GDS_REF(event->evnt_id), + message->msg_event_length, + message->msg_event_data, server_ast, GDS_VAL(event)); + THREAD_ENTER; + + send_response(message, status_vector, event->evnt_id, 0, 0); +} + + +#ifdef GATEWAY +#ifndef PIPE_SERVER +static void query_connect( MSG_ATT message) +{ +/************************************** + * + * q u e r y _ c o n n e c t + * + ************************************** + * + * Functional description + * Process an server attach packet. + * + **************************************/ + STATUS status_vector[20]; + RDB rdb; + TEXT *expanded_name, connect_method[128]; + DBN db_name; + USHORT connect_length; + +/* Assume this is going to fail! */ + + status_vector[0] = gds_arg_gds; + status_vector[1] = gds__unavailable; + status_vector[2] = gds_arg_end; + +/* Make sure we're willing to talk to this client */ + + if (!CSS_check_partner + (message->msg_att_header.msg_connection, PRB_client_t1)) { + send_response(message, status_vector, 0, 0, 0); + return; + } + + expanded_name = + (TEXT *) message->msg_att_data + message->msg_att_file_length; + + if (! + (db_name = + find_dbname(expanded_name, message->msg_att_expanded_length, + FALSE))) { + send_response(message, status_vector, 0, 0, 0); + return; + } + +/* Try initially with expanded name. If that fails, try once more with + original name */ + + rdb = NULL; + + if (!GWAY_query_connect(status_vector, + GDS_VAL(expanded_name), + connect_method, &connect_length)) { + rdb = (RDB) ALLOC(type_rdb, sizeof(struct rdb)); + rdb->rdb_connection = message->msg_att_header.msg_connection; + rdb->rdb_dbn = db_name; + rdb->rdb_next = CSV_databases; + CSV_databases = rdb; + ++db_name->dbn_attaches; + } + + send_response(message, status_vector, rdb, connect_length, + connect_method); +} +#endif +#endif + + +static void receive_msg( MSG_MSG message) +{ +/************************************** + * + * r e c e i v e _ m s g + * + ************************************** + * + * Functional description + * Receive a message. + * + **************************************/ + STATUS status_vector[20]; + RRQ request; + USHORT buffer_length; + UCHAR *buffer; + + request = (RRQ) message->msg_msg_request; + buffer_length = message->msg_msg_length; + buffer = get_buffer(&buffer_length); + + THREAD_EXIT; + GDS_RECEIVE(status_vector, + GDS_REF(request->rrq_handle), + message->msg_msg_type, + message->msg_msg_length, + GDS_VAL(buffer), message->msg_msg_level); + THREAD_ENTER; + + send_response(message, status_vector, 0, message->msg_msg_length, buffer); + free_buffer(buffer, buffer_length); +} + + +static void reconnect( MSG_OP message) +{ +/************************************** + * + * r e c o n n e c t + * + ************************************** + * + * Functional description + * Reconnect to a transaction in limbo. + * + **************************************/ + RDB rdb; + RTR transaction; + HANDLE handle; + STATUS status_vector[20]; + + rdb = (RDB) message->msg_op_handle; + handle = NULL; + transaction = NULL; + + THREAD_EXIT; + GDS_RECONNECT(status_vector, + GDS_REF(rdb->rdb_handle), + GDS_REF(handle), + message->msg_op_length, message->msg_op_data); + THREAD_ENTER; + + if (!status_vector[1]) + transaction = make_transaction(rdb, handle); + + send_response(message, status_vector, transaction, 0, 0); +} + + +static void release_array( ARRAY array) +{ +/************************************** + * + * r e l e a s e _ a r r a y + * + ************************************** + * + * Functional description + * Release an array block and friends. + * + **************************************/ + RTR transaction; + ARRAY *p; + + transaction = array->array_rtr; + + for (p = &transaction->rtr_arrays; *p; p = &(*p)->array_next) + if (*p == array) { + *p = array->array_next; + break; + } + + FREE(array); +} + + +static void release_blob( RBL blob) +{ +/************************************** + * + * r e l e a s e _ b l o b + * + ************************************** + * + * Functional description + * Release a blob block and friends. + * + **************************************/ + RTR transaction; + RBL *p; + + transaction = blob->rbl_rtr; + + for (p = &transaction->rtr_blobs; *p; p = &(*p)->rbl_next) + if (*p == blob) { + *p = blob->rbl_next; + break; + } + + FREE(blob); +} + + +static void release_database( RDB rdb) +{ +/************************************** + * + * r e l e a s e _ d a t a b a s e + * + ************************************** + * + * Functional description + * Release database block and friends and relations. + * + **************************************/ + EVNT event; + RDB *ptr; + DBN db_name; + + db_name = rdb->rdb_dbn; + +/* Next, get rid of any request or transaction block */ + + while (rdb->rdb_requests) + release_request(rdb->rdb_requests); + + while (rdb->rdb_sql_requests) + release_sql_request(rdb->rdb_sql_requests); + + while (rdb->rdb_transactions) + release_transaction(rdb->rdb_transactions); + + while (event = rdb->rdb_events) { + rdb->rdb_events = event->evnt_next; + FREE(event); + } + +/* Untangle database block from global list of databases */ + + for (ptr = &CSV_databases; *ptr; ptr = &(*ptr)->rdb_next) + if (*ptr == rdb) { + *ptr = rdb->rdb_next; + break; + } + +/* Finally, release the database block itself */ + + FREE(rdb); + +/* Decrement the attachment counter and see if the server can exit */ + + --db_name->dbn_attaches; + check_if_done(db_name); +} + + +static void release_request( RRQ request) +{ +/************************************** + * + * r e l e a s e _ r e q u e s t + * + ************************************** + * + * Functional description + * Release a request block and friends. + * + **************************************/ + RDB rdb; + RRQ *p; + USHORT i; + + rdb = request->rrq_rdb; + + for (p = &rdb->rdb_requests; *p; p = &(*p)->rrq_next) + if (*p == request) { + *p = request->rrq_next; + break; + } + + FREE(request); +} + + +static void release_sql_request( RSR stmt) +{ +/************************************** + * + * r e l e a s e _ s q l _ r e q u e s t + * + ************************************** + * + * Functional description + * Release an SQL request block. + * + **************************************/ + RDB rdb; + RSR *p; + + rdb = stmt->rsr_rdb; + + for (p = &rdb->rdb_sql_requests; *p; p = &(*p)->rsr_next) + if (*p == stmt) { + *p = stmt->rsr_next; + break; + } + + FREE(stmt); +} + + +static void release_transaction( RTR transaction) +{ +/************************************** + * + * r e l e a s e _ t r a n s a c t i o n + * + ************************************** + * + * Functional description + * Release a transaction block and friends. + * + **************************************/ + RDB rdb; + RTR *p; + + rdb = transaction->rtr_rdb; + + while (transaction->rtr_blobs) + release_blob(transaction->rtr_blobs); + + while (transaction->rtr_arrays) + release_array(transaction->rtr_arrays); + + for (p = &rdb->rdb_transactions; *p; p = &(*p)->rtr_next) + if (*p == transaction) { + *p = transaction->rtr_next; + break; + } + + FREE(transaction); +} + + +static void seek_blob( MSG_SEEK message) +{ +/************************************** + * + * g e t _ s e e k _ b l o b + * + ************************************** + * + * Functional description + * Perform a blob seek. + * + **************************************/ + RDB rdb; + RBL blob; + SLONG result; + STATUS status_vector[20]; + + blob = (RBL) message->msg_seek_handle; + +/* Do the call. We basically don't care whether it succeeds or not */ + + THREAD_EXIT; + GDS_SEEK_BLOB(status_vector, + GDS_REF(blob->rbl_handle), + message->msg_seek_mode, + message->msg_seek_offset, GDS_REF(result)); + THREAD_ENTER; + + send_response(message, status_vector, 0, sizeof(result), &result); +} + + +static void send_msg( MSG_MSG message) +{ +/************************************** + * + * s e n d _ m s g + * + ************************************** + * + * Functional description + * + **************************************/ + STATUS status_vector[20]; + RRQ request; + RTR transaction; + RDB rdb; + + request = (RRQ) message->msg_msg_request; + + THREAD_EXIT; + GDS_SEND(status_vector, + GDS_REF(request->rrq_handle), + message->msg_msg_type, + message->msg_msg_length, + message->msg_msg_data, message->msg_msg_level); + THREAD_ENTER; + + send_response(message, status_vector, 0, 0, 0); +} + + +static int send_response( + MSG source, + STATUS * status_vector, + SLONG handle, USHORT length, UCHAR * data) +{ +/************************************** + * + * s e n d _ r e s p o n s e + * + ************************************** + * + * Functional description + * Send a response packet. + * + **************************************/ + STATUS code, *status, *target; + UCHAR *p, *q, **s; + MSG_RESP message; + USHORT l, msg_length; + +#ifdef DEBUG + fprintf(stderr, "send_response: %x %x %x %x\n", + status_vector[0], status_vector[1], status_vector[2], + status_vector[3]); +#endif + +/* Compute variable length portion of message vector */ + + msg_length = 0; + + if (!status_vector[1]) + status_vector[2] = gds_arg_end; + else + for (status = status_vector; *status && status < status_vector + 20;) + switch (*status++) { + case gds_arg_interpreted: + case gds_arg_string: + s = (UCHAR **) status; + p = *s++; + status = (STATUS *) s; + msg_length += strlen(p) + 1; + break; + + case gds_arg_cstring: + msg_length += *status++; + ++status; + break; + + default: + ++status; + } + +/* Allocate and format response message */ + + l = sizeof(struct msg_resp) + msg_length + length; + message = CSS_alloc_message(type_msg, (int) l); + message->msg_resp_header.msg_type = MSG_response; + message->msg_resp_handle = handle; + p = message->msg_resp_data; + + if (l = message->msg_resp_length = length) { + q = data; + do + *p++ = *q++; + while (--l); + } + + +/* Start by translating the status vector into "generic" form */ + + for (status = status_vector, target = message->msg_resp_status; + (*target++ = *status) && status < status_vector + 20;) + switch (*status++) { + case gds_arg_interpreted: + case gds_arg_string: + s = (UCHAR **) status; + q = *s++; + status = (STATUS *) s; + *target++ = p - (UCHAR *) message; + while (*p++ = *q++); + break; + + case gds_arg_cstring: + *target++ = l = *status++; + s = (UCHAR **) status; + q = *s++; + status = (STATUS *) s; + *target++ = p - (UCHAR *) message; + if (l) + do + *p++ = *q++; + while (--l); + break; + + default: + *target++ = *status++; + break; + } + + return CSS_put_message(source->msg_connection, message, source); +} + + +static void server_ast( EVNT event, USHORT length, UCHAR * data) +{ +/************************************** + * + * s e r v e r _ a s t + * + ************************************** + * + * Functional description + * Send a message across central interface. + * + **************************************/ + RDB rdb; + MSG_EVENT message; + + rdb = event->evnt_rdb; + message = + (MSG_EVENT) CSS_alloc_message(type_msg, + (int) (sizeof(struct msg_event) + + length)); + message->msg_event_header.msg_type = MSG_event; + message->msg_event_ast = event->evnt_ast; + message->msg_event_arg = event->evnt_arg; + message->msg_event_length = length; + move(data, message->msg_event_data, length); + CSS_put_message(rdb->rdb_connection2, message, NULL); + event->evnt_id = 0; +} + + +static void set_cursor( MSG_SETCUR message) +{ +/************************************** + * + * s e t _ c u r s o r + * + ************************************** + * + * Functional description + * Declare a cursor for a dynamic request. + * + **************************************/ + RSR statement; + STATUS status_vector[20]; + + statement = (RSR) message->msg_setcur_statement; + + THREAD_EXIT; + GDS_DSQL_SET_CURSOR(status_vector, + &statement->rsr_handle, + message->msg_setcur_cursor, message->msg_setcur_type); + THREAD_ENTER; + + send_response(message, status_vector, 0, 0, 0); +} + + +static void start( MSG_MSG message) +{ +/************************************** + * + * s t a r t + * + ************************************** + * + * Functional description + * + **************************************/ + STATUS status_vector[20]; + RRQ request; + RTR transaction; + + request = (RRQ) message->msg_msg_request; + transaction = (RTR) message->msg_msg_transaction; + + THREAD_EXIT; + GDS_START(status_vector, + GDS_REF(request->rrq_handle), + GDS_REF(transaction->rtr_handle), message->msg_msg_level); + THREAD_ENTER; + + send_response(message, status_vector, 0, 0, 0); +} + + +static void start_and_send( MSG_MSG message) +{ +/************************************** + * + * s t a r t _ a n d _ s e n d + * + ************************************** + * + * Functional description + * + **************************************/ + STATUS status_vector[20]; + RRQ request; + RTR transaction; + + request = (RRQ) message->msg_msg_request; + transaction = (RTR) message->msg_msg_transaction; + + THREAD_EXIT; + GDS_START_AND_SEND(status_vector, + GDS_REF(request->rrq_handle), + GDS_REF(transaction->rtr_handle), + message->msg_msg_type, + message->msg_msg_length, + message->msg_msg_data, message->msg_msg_level); + THREAD_ENTER; + + send_response(message, status_vector, 0, 0, 0); +} + + +static void start_transaction( MSG_TRANS message) +{ +/************************************** + * + * s t a r t _ t r a n s a c t i o n + * + ************************************** + * + * Functional description + * Start a transaction. + * + **************************************/ + RDB rdb; + RTR transaction; + TEB tebs[16], *teb; + TDB *tdb, *end; + HANDLE handle; + STATUS status_vector[20]; + + for (tdb = message->msg_trans_tdb, end = + tdb + message->msg_trans_count, teb = tebs; tdb < end; tdb++, teb++) { + rdb = (RDB) tdb->tdb_database; + teb->teb_database = &rdb->rdb_handle; + teb->teb_tpb_length = tdb->tdb_tpb_length; + teb->teb_tpb = (UCHAR *) message + tdb->tdb_tpb; + } + + handle = NULL; + transaction = NULL; + + THREAD_EXIT; + GDS_START_MULTIPLE(status_vector, + GDS_REF(handle), message->msg_trans_count, tebs); + THREAD_ENTER; + + if (!status_vector[1]) + transaction = make_transaction(rdb, handle); + + send_response(message, status_vector, transaction, 0, 0); +} + + +#ifndef PIPE_SERVER +static void server_utility( MSG_UTIL message) +{ +/************************************** + * + * s e r v e r _ u t i l i t y + * + ************************************** + * + * Functional description + * Process a message from the central server utility. + * + **************************************/ + STATUS status_vector[20]; + USHORT cmd, length, l; + CSU_LIST list_msg, ptr_data; + DBN db_name, *ptr; + TEXT *name; + +/* Assume everything is going to go ok */ + + status_vector[0] = gds_arg_gds; + status_vector[1] = 0; + status_vector[2] = gds_arg_end; + + if (message->msg_util_csn_len && strcmp(message->msg_util_data, CSV_name)) { + /* The message is not meant for us */ + + status_vector[1] = gds__unavailable; + send_response(message, status_vector, (SLONG) 0, 0, 0); + return; + } + + name = message->msg_util_data + message->msg_util_csn_len + 1; + + switch (cmd = message->msg_util_cmd) { + case UTIL_list: + length = + FB_ALIGN(CSV_name_len + sizeof(struct csu_list), + 2) + sizeof(struct csu_list); + for (db_name = CSV_dbnames; db_name; db_name = db_name->dbn_next) + length += + FB_ALIGN(sizeof(struct csu_list) + db_name->dbn_length, 2); + list_msg = ptr_data = (CSU_LIST) ALLOC(0, length); + + ptr_data->csu_list_length = CSV_name_len; + move(CSV_name, ptr_data->csu_list_name, CSV_name_len + 1); + ptr_data = + (CSU_LIST) (ptr_data->csu_list_name + + FB_ALIGN(CSV_name_len + 1, 2)); + + for (db_name = CSV_dbnames; db_name; db_name = db_name->dbn_next) { + ptr_data->csu_list_attaches = db_name->dbn_attaches; + ptr_data->csu_list_flags = db_name->dbn_flags; + ptr_data->csu_list_length = l = db_name->dbn_length; + move(db_name->dbn_name, ptr_data->csu_list_name, l + 1); + ptr_data = + (CSU_LIST) (ptr_data->csu_list_name + FB_ALIGN(l + 1, 2)); + } + ptr_data->csu_list_length = 0; + + send_response(message, status_vector, (SLONG) 0, length, list_msg); + FREE(list_msg); + break; + + case UTIL_disable: + case UTIL_kill: + case UTIL_reenable: + /* See if the utility specified a particular database name + and if so, whether we know about it. If we don't know + about it, return "unavailable". */ + + if ((length = message->msg_util_dbn_len) && + !(db_name = find_dbname(name, length, TRUE))) + status_vector[1] = gds__unavailable; + + send_response(message, status_vector, (SLONG) 0, 0, 0); + + /* If we know about the database name, disable, re-enable, or kill + one or all databases */ + + if (!status_vector[1]) + if (length) + disable_or_kill(db_name, cmd); + else + for (ptr = &CSV_dbnames; db_name = *ptr;) { + disable_or_kill(db_name, cmd); + if (db_name == *ptr) + ptr = &(*ptr)->dbn_next; + } + break; + + case UTIL_enable: + /* Let's make sure that we're not already servicing the database. + If we're not and the -a switch was specified, try to attach the + database. */ + + length = message->msg_util_dbn_len; + if (!find_dbname(name, length, TRUE)) { + db_name = (DBN) ALLOC(type_dbn, sizeof(struct dbn) + length); + move(name, db_name->dbn_name, length + 1); + db_name->dbn_length = length; + if (!sw_attach + || !attach_for_servicing(db_name, status_vector, FALSE)) { + db_name->dbn_next = CSV_dbnames; + CSV_dbnames = db_name; + } + } + else { + status_vector[1] = gds__random; + status_vector[2] = gds_arg_string; + status_vector[3] = (STATUS) "Database already known to server"; + status_vector[4] = gds_arg_end; + } + send_response(message, status_vector, (SLONG) 0, 0, 0); + break; + + default: + printf("Unknown server utility command %d\n", message->msg_util_cmd); + status_vector[1] = gds__unavailable; + send_response(message, status_vector, (SLONG) 0, 0, 0); + break; + } +} +#endif + + +#ifdef PIPE_SERVER +static void server_utility( MSG_UTIL message) +{ +/************************************** + * + * s e r v e r _ u t i l i t y + * + ************************************** + * + * Functional description + * Process a message from the central server utility. + * + **************************************/ + STATUS status_vector[20]; + RDB *ptr, rdb; + PTR connection; + + switch (message->msg_util_cmd) { + case UTIL_kill: + /* Loop through the database attachments and zap them all */ + + for (ptr = &CSV_databases; rdb = *ptr;) + disconnect(rdb->rdb_connection); + + sw_shutdown = TRUE; + + connection = message->msg_util_header.msg_connection; + CSS_free_global(message); + CSS_disconnect(connection); + return; + + default: + status_vector[0] = gds_arg_gds; + status_vector[1] = gds__unavailable; + status_vector[2] = gds_arg_end; + send_response(message, status_vector, (SLONG) 0, 0, 0); + break; + } +} +#endif + + +#ifdef MULTI_THREAD +static void thread(void) +{ +/************************************** + * + * t h r e a d + * + ************************************** + * + * Functional description + * Execute requests in a happy loop. + * + **************************************/ + REQ request; + SLONG value; + EVENT ptr; + + request = NULL; + ++threads_waiting; + THREAD_ENTER; + + while (!sw_shutdown) { + value = ISC_event_clear(thread_event); + + if (request) { + request->req_next = free_requests; + free_requests = request; + } + + if (request = request_que) + request_que = request->req_next; + + if (request) { + --threads_waiting; + process_message(request->req_message); + ++threads_waiting; + } + else { + ptr = thread_event; + THREAD_EXIT; + ISC_event_wait(1, &ptr, &value, -1, 0, 0); + THREAD_ENTER; + } + } + + ISC_event_clear(thread_event); + ISC_event_post(thread_event); + + THREAD_EXIT; + --threads_waiting; +} +#endif + + +static void transact_request( MSG_TRRQ message) +{ +/************************************** + * + * t r a n s a c t _ r e q u e s t + * + ************************************** + * + * Functional description + * Execute a stored procedure. + * + **************************************/ + RDB rdb; + RTR transaction; + SCHAR *blr, *in_msg, *out_msg; + USHORT buffer_length; + HANDLE handle; + STATUS status_vector[20]; + + rdb = (RDB) message->msg_trrq_database; + transaction = (RTR) message->msg_trrq_transaction; + + blr = in_msg; + in_msg = blr + message->msg_trrq_blr_length; + + buffer_length = message->msg_trrq_out_msg_length; + out_msg = get_buffer(&buffer_length); + + handle = (transaction) ? transaction->rtr_handle : (HANDLE) 0; + + THREAD_EXIT; + GDS_TRANSACT_REQUEST(status_vector, + &rdb->rdb_handle, + &handle, + message->msg_trrq_blr_length, + blr, + message->msg_trrq_in_msg_length, + in_msg, message->msg_trrq_out_msg_length, out_msg); + THREAD_ENTER; + + if (!status_vector[1]) + if (transaction && !handle) { + release_transaction(transaction); + transaction = NULL; + } + else if (!transaction && handle) + transaction = make_transaction(rdb, handle); + + send_response(message, status_vector, transaction, + message->msg_trrq_out_msg_length, out_msg); + free_buffer(out_msg, buffer_length); +} + + +#ifdef PIPE_SERVER +#ifdef VMS +static void trans_logicals(void) +{ +/************************************** + * + * t r a n s _ l o g i c a l s + * + ************************************** + * + * Functional description + * Translate logicals and write their values to + * the process logical table. + * + **************************************/ + UCHAR **logicals, value[256], job_logical[32]; + int attr, status; + SSHORT len; + ITM items[2]; + struct dsc$descriptor_s tab_desc, log_desc; + + for (logicals = inherit_logicals; *logicals; logicals++) { + items[0].itm_length = sizeof(value); + items[0].itm_code = LNM$_STRING; + items[0].itm_buffer = value; + items[0].itm_return_length = &len; + items[1].itm_length = 0; + items[1].itm_code = 0; + + attr = LNM$M_CASE_BLIND; + + sprintf(job_logical, "GDS_PIPE_%s", *logicals); + ISC_make_desc(job_logical, &log_desc, 0); + + ISC_make_desc("LNM$JOB", &tab_desc, sizeof("LNM$JOB") - 1); + if (!(sys$trnlnm(&attr, &tab_desc, &log_desc, NULL, items) & 1)) + continue; + + /* Logical must be copied into the process logical table */ + + items[0].itm_length = len; + ISC_make_desc(*logicals, &log_desc, 0); + ISC_make_desc("LNM$PROCESS", &tab_desc, sizeof("LNM$PROCESS") - 1); + sys$crelnm(NULL, &tab_desc, &log_desc, NULL, items); + } +} +#endif +#endif + + +static void unwind( MSG_OP message) +{ +/************************************** + * + * u n w i n d + * + ************************************** + * + * Functional description + * Unwind a request. + * + **************************************/ + RRQ request; + STATUS status_vector[20]; + + request = (RRQ) message->msg_op_handle; + + THREAD_EXIT; + GDS_UNWIND(status_vector, + GDS_REF(request->rrq_handle), (SSHORT) message->msg_op_level); + THREAD_ENTER; + + send_response(message, status_vector, 0, 0, 0); +} diff --git a/src/csv/depends.mak b/src/csv/depends.mak new file mode 100644 index 0000000000..191c3a04e7 --- /dev/null +++ b/src/csv/depends.mak @@ -0,0 +1,75 @@ +# 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): ______________________________________. +# depends.mak - csv +# Created by 'make depends.mak' +# Created on 1998-11-17 +csi.o: csi.c +csi.o: csi.h +csi.o: csi_proto.h +csi.o: css_proto.h +csi.o: source/jrd/build_no.h +csi.o: source/jrd/codes.h +csi.o: source/jrd/common.h +csi.o: source/jrd/fil.h +csi.o: source/jrd/gds_proto.h +csi.o: source/jrd/inf.h +csi.o: source/jrd/isc.h +csi.o: source/jrd/license.h +csi.o: source/jrd/thd.h +csi.o: source/remote/merge_proto.h +css.o: csi.h +css.o: css.c +css.o: css_proto.h +css.o: source/jrd/codes.h +css.o: source/jrd/common.h +css.o: source/jrd/fil.h +css.o: source/jrd/gds_proto.h +css.o: source/jrd/isc.h +css.o: source/jrd/isc_proto.h +css.o: source/jrd/thd.h +csu.o: csi.h +csu.o: css_proto.h +csu.o: csu.c +csu.o: source/jrd/build_no.h +csu.o: source/jrd/codes.h +csu.o: source/jrd/common.h +csu.o: source/jrd/fil.h +csu.o: source/jrd/gds_proto.h +csu.o: source/jrd/isc.h +csu.o: source/jrd/license.h +csu.o: source/jrd/thd.h +csv.o: csi.h +csv.o: css_proto.h +csv.o: csv.c +csv.o: source/interbase/include/iberror.h +csv.o: source/jrd/build_no.h +csv.o: source/jrd/common.h +csv.o: source/jrd/fil.h +csv.o: source/jrd/gds.h +csv.o: source/jrd/gds_proto.h +csv.o: source/jrd/isc.h +csv.o: source/jrd/license.h +csv.o: source/jrd/thd.h +csv.o: source/remote/merge_proto.h +print.o: csi.h +print.o: css_proto.h +print.o: print.c +print.o: source/jrd/common.h +print.o: source/jrd/fil.h +print.o: source/jrd/gds_proto.h +print.o: source/jrd/isc.h +print.o: source/jrd/thd.h diff --git a/src/csv/print.cpp b/src/csv/print.cpp new file mode 100644 index 0000000000..f52fe45acc --- /dev/null +++ b/src/csv/print.cpp @@ -0,0 +1,141 @@ +/* + * PROGRAM: Central Server + * MODULE: print.c + * DESCRIPTION: Global region print utility + * + * The contents of this file are subject to the Interbase Public + * License Version 1.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy + * of the License at http://www.Inprise.com/IPL.html + * + * Software distributed under the License is distributed on an + * "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either express + * or implied. See the License for the specific language governing + * rights and limitations under the License. + * + * The Original Code was created by Inprise Corporation + * and its predecessors. Portions created by Inprise Corporation are + * Copyright (C) Inprise Corporation. + * + * All Rights Reserved. + * Contributor(s): ______________________________________. + */ + +#include "../csv/csi.h" +#include "../csv/css_proto.h" +#include "../jrd/gds_proto.h" + +static void prt_que(UCHAR *, SRQ *); + +static CSH CSS_header; + + +void main( int argc, char **argv) +{ +/************************************** + * + * m a i n + * + ************************************** + * + * Functional description + * + **************************************/ + SRQ *que; + HDR *block; + PRB process; + MSG message; + FRB free; + CNCT connection; + SLONG offset; + STATUS status_vector[20]; + + if (!(CSS_header = CSS_init(status_vector, TRUE))) { + printf("Can't access global region\n"); + gds__print_status(status_vector); + exit(FINI_ERROR); + } + + printf("%.5d GLOBAL REGION HEADER\n", 0); + printf("\tLength: %ld, version: %d, free: %ld\n", CSS_header->csh_length, + CSS_header->csh_version, CSS_header->csh_free); + printf("\tSemid: %ld, current process: %ld\n", + CSS_header->csh_semid, CSS_header->csh_current_process); + prt_que("\tProcesses", &CSS_header->csh_processes); + + for (offset = sizeof(struct csh); offset < CSS_header->csh_length; + offset += block->hdr_length) { + printf("\n%.5ld ", offset); + block = (HDR *) ABS_PTR(offset); + switch (block->hdr_type) { + case type_prb: + printf("PROCESS_BLOCK (%ld)\n", block->hdr_length); + process = (PRB) block; + printf("\tProcess_id: %ld, flags: %d, protocol: %d\n", + process->prb_process_id, process->prb_flags, + process->prb_protocol_version); + printf("\tSemaphore: %d, group_id: %ld, number: %ld\n", + process->prb_semaphore, process->prb_group_id, + process->prb_process_number); + prt_que("\tProcesses", &process->prb_processes); + prt_que("\tConnections", &process->prb_connections); + prt_que("\tMessages", &process->prb_messages); + break; + + case type_frb: + printf("FREE BLOCK (%ld)\n", block->hdr_length); + free = (FRB) block; + printf("\tNext: %ld\n", free->frb_next); + break; + + case type_msg: + printf("MSG (%ld)\n", block->hdr_length); + message = (MSG) block; + printf("\tType: %d, connection: %ld\n", message->msg_type, + message->msg_connection); + prt_que("\tMsg que", &message->msg_que); + break; + + case type_cnct: + printf("CONNECTION (%ld)\n", block->hdr_length); + connection = (CNCT) block; + printf("\tParent: %ld, partner: %ld, mirror: %ld\n", + connection->cnct_parent, connection->cnct_partner, + connection->cnct_mirror); + prt_que("\tConnections", &connection->cnct_connections); + break; + + default: + printf("*** UNKNOWN *** (%ld)\n", block->hdr_length); + break; + } + if (!block->hdr_length) + break; + } + + exit(FINI_OK); +} + + +static void prt_que( UCHAR * string, SRQ * que) +{ +/************************************** + * + * p r t _ q u e + * + ************************************** + * + * Functional description + * Print the contents of a self-relative que. + * + **************************************/ + SLONG offset; + + offset = REL_PTR(que); + + if (offset == que->srq_forward && offset == que->srq_backward) + printf("%s: *empty*\n", string); + else + printf("%s: forward: %d, backward: %d\n", + string, que->srq_forward, que->srq_backward); +} diff --git a/src/dsql/all.h b/src/dsql/all.h new file mode 100644 index 0000000000..c84599280c --- /dev/null +++ b/src/dsql/all.h @@ -0,0 +1,40 @@ +/* + * PROGRAM: Dynamic SQL runtime support + * MODULE: all.h + * DESCRIPTION: Allocator prototypes + * + * The contents of this file are subject to the Interbase Public + * License Version 1.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy + * of the License at http://www.Inprise.com/IPL.html + * + * Software distributed under the License is distributed on an + * "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either express + * or implied. See the License for the specific language governing + * rights and limitations under the License. + * + * The Original Code was created by Inprise Corporation + * and its predecessors. Portions created by Inprise Corporation are + * Copyright (C) Inprise Corporation. + * + * All Rights Reserved. + * Contributor(s): ______________________________________. + */ + +#include "../dsql/dsql.h" + +BLK ALLD_alloc( /* alloc and init a block from a pool */ + PLB pool, UCHAR type, int count); + +BLK ALLD_extend( /* extend a block by given size */ + BLK * pointer, int size); + +int ALLD_fini(); /* get rid of everything */ +int ALLD_free(SCHAR * memory); /* give space back to system */ +int ALLD_init(); /* initialize pool system */ +UCHAR *ALLD_malloc(int size); /* get memory from system */ +PLB ALLD_pool(); /* allocate a new pool */ +int ALLD_push(BLK object, LLS * stack); /* push object on LLS stack */ +BLK ALLD_pop(LLS * stack); /* pop object off LLS stack */ +int ALLD_release(FRB block); /* release a block to its pool */ +int ALLD_rlpool(PLB pool); /* release a pool */ diff --git a/src/dsql/alld.cpp b/src/dsql/alld.cpp new file mode 100644 index 0000000000..16db842647 --- /dev/null +++ b/src/dsql/alld.cpp @@ -0,0 +1,919 @@ +/* + * PROGRAM: Dynamic SQL runtime support + * MODULE: all.c + * DESCRIPTION: Internal block allocator + * + * 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): ______________________________________. + */ + + +/*************************************************** + THIS MODULE HAS SEVERAL KISSING COUSINS; IF YOU + SHOULD CHANGE ONE OF THE MODULES IN THE FOLLOWING + LIST, PLEASE BE SURE TO CHECK THE OTHERS FOR + SIMILAR CHANGES: + + /gds/maint/pyxis/all.c + /dsql/all.c + /jrd/all.c + /pipe/allp.c + /qli/all.c + /remote/allr.c + /gpre/msc.c + + - THANK YOU +***************************************************/ + + +/************************************************************** +V4 Multi-threading changes. + +-- direct calls to gds__ () & isc_ () entry points + + THREAD_EXIT; + gds__ () or isc_ () call. + THREAD_ENTER; + +-- calls through embedded GDML. + +the following protocol will be used. Care should be taken if +nested FOR loops are added. + + THREAD_EXIT; // last statment before FOR loop + + FOR ............... + + THREAD_ENTER; // First statment in FOR loop + .....some C code.... + .....some C code.... + THREAD_EXIT; // last statment in FOR loop + + END_FOR; + + THREAD_ENTER; // First statment after FOR loop +***************************************************************/ + +#include +#include "../jrd/ib_stdio.h" +#include "../dsql/dsql.h" +#include "../dsql/node.h" +#include "../dsql/sym.h" +#include "../jrd/codes.h" +#include "../dsql/alld_proto.h" +#include "../dsql/errd_proto.h" +#include "../jrd/gds_proto.h" +#include "../jrd/thd_proto.h" +#include "../jrd/gdsassert.h" + +ASSERT_FILENAME /* Define things dsql/assert needed */ +extern "C" { +#define MIN_ALLOCATION 1024 +#if (defined PC_PLATFORM && !defined NETWARE_386) +#define MAX_BLOCK 65520 +#else +#define MAX_BLOCK (262144 - MIN_ALLOCATION - sizeof (struct hnk) - 8) +#endif +#define SHIFT SHIFTLONG +#define BLOCK_FACTOR (1 << SHIFT) +#define BLOCKS_TO_SIZE(blocks) ((blocks) << SHIFT) +#define SIZE_TO_BLOCKS(size) ((size) >> SHIFT) +#define BLOCK_ROUNDUP(size) ROUNDUP (size, MAX(BLOCK_FACTOR,ALIGNMENT)) +#define EXPAND_BLOCKSIZE(b) (BLOCKS_TO_SIZE ((b)->blk_length)) + +#define BLKDEF(type, root, tail) sizeof (struct root), tail, + static CONST struct { + USHORT typ_root_length; + USHORT typ_tail_length; +} block_sizes[] = { + 0, 0, +#include "../dsql/blk.h" +0}; + + +#undef BLKDEF + +#ifdef SUPERSERVER +extern SLONG trace_pools; +SLONG alld_delta_alloc = 0; + +#define BLKDEF(type, root, tail) 0, + +SLONG alld_block_type_count[] = { +#include "../dsql/blk.h" + 0 +}; +#undef BLKDEF +#define BLKDEF(type, root, tail) #type, +char ALLD_types[][24] = { + "type_MIN", +#include "../dsql/blk.h" + "type_MAX" +}; +#undef BLKDEF +int alld_type_MAX = type_MAX; +#endif + +static void extend_pool(PLB, ULONG); +static PLB find_pool(BLK); +static void release(FRB, PLB); + +static USHORT init_flag = FALSE; +static VEC pools = NULL; + + +BLK ALLD_alloc( PLB pool, UCHAR type, ULONG count) +{ +/************************************** + * + * A L L D _ a l l o c + * + ************************************** + * + * Functional description + * Allocate a block from a given pool and initialize the block. + * This is the primary block allocation routine. + * + **************************************/ + register BLK block; + FRB free, *best, *ptr; + register ULONG size; + ULONG l; + SLONG best_tail, tail; + ULONG needed_blocks; + + assert(pool != NULL); + DEV_BLKCHK(pool, type_plb); + + if (type <= (SCHAR) type_MIN || type >= (SCHAR) type_MAX) + BUGCHECK("bad block type"); + +/* Compute block length */ + + size = block_sizes[type].typ_root_length; + + if ((tail = block_sizes[type].typ_tail_length) && count >= 1) + size += (count - 1) * tail; + +#ifdef DEV_BUILD + if (size <= sizeof(struct blk) || size >= MAX_BLOCK) + BUGCHECK("bad block size"); +#endif + + needed_blocks = SIZE_TO_BLOCKS(BLOCK_ROUNDUP(size)); + + assert(BLOCKS_TO_SIZE(needed_blocks) >= size); + +/* Find best fit. Best fit is defined to be the free block of shortest + tail. If there isn't a fit, extend the pool and try, try again. */ + + while (TRUE) { + best = NULL; + best_tail = MAX_BLOCK; + for (ptr = &pool->plb_free; ((free = *ptr) != NULL); + ptr = + &free->frb_next) if ((SCHAR HUGE_PTR *) free == + (SCHAR HUGE_PTR *) free->frb_next) { + BUGCHECK("corrupt pool"); + } + /* Is this block big enough? + * And have less leftover than the best one found? */ + else + if ( + ((tail + = + ((ULONG) free->frb_header.blk_length - + needed_blocks)) >= 0) && (tail < best_tail)) { + best = ptr; + best_tail = tail; + if (tail == 0) + break; + } + if (best) + break; + + extend_pool(pool, size); + } + +/* We've got our free block. If there's enough left of the free block + after taking out our block, chop out out block. If not, allocate + the entire free block as our block (a little extra won't hurt). */ + + free = *best; + if (best_tail > SIZE_TO_BLOCKS(BLOCK_ROUNDUP(sizeof(struct frb)))) { + /* Carve off the needed size from the bottom of the free block */ + l = free->frb_header.blk_length - needed_blocks; + + block = (BLK) ((UCHAR *) free + BLOCKS_TO_SIZE(l)); + + /* Reset the length of the free block */ + assert(l <= MAX_USHORT); + free->frb_header.blk_length = (USHORT) l; + } + else { + /* There isn't left over in the free block to save in the free list */ + /* So give the client the whole free block */ + + /* Unhook the free block from the free chain */ + *best = free->frb_next; + + /* Client gets the whole block */ + needed_blocks = free->frb_header.blk_length; + block = (BLK) free; + } + +/* Zero the whole allocated structure */ + memset(block, 0, BLOCKS_TO_SIZE(needed_blocks)); + +/* Now set the block header (yeah, we just zero'ed it, sue me) */ + block->blk_type = type; + assert(pool->plb_pool_id <= MAX_UCHAR); + block->blk_pool_id = (UCHAR) pool->plb_pool_id; + assert(needed_blocks <= MAX_USHORT); + block->blk_length = (USHORT) needed_blocks; + +#ifdef SUPERSERVER + if (trace_pools) { + ++alld_block_type_count[type]; + ++pool->plb_blk_type_count[type]; + } +#endif + + return block; +} + + +BLK ALLD_extend(BLK * pointer, ULONG size) +{ +/************************************** + * + * A L L D _ e x t e n d + * + ************************************** + * + * Functional description + * Extend a repeating block, copying the constant part. + * + **************************************/ + BLK block, new_; + PLB pool; + register ULONG length, l; + register SLONG *p1, *p2; + register SCHAR *c1, *c2; + + assert(pointer != NULL); + assert(*pointer != NULL); + + block = *pointer; + pool = find_pool(block); + new_ = ALLD_alloc(pool, block->blk_type, size); + length = MIN(EXPAND_BLOCKSIZE(block), EXPAND_BLOCKSIZE(new_)) + - sizeof(struct blk); + p1 = (SLONG *) ((UCHAR *) new_ + sizeof(struct blk)); + p2 = (SLONG *) ((UCHAR *) block + sizeof(struct blk)); + +/* Copy the bytes a longword at a time */ + if ((l = length >> SHIFTLONG) != 0) + do + *p1++ = *p2++; + while (--l); + +/* Copy any remaining bytes */ + if (length &= 3) { + c1 = (SCHAR *) p1; + c2 = (SCHAR *) p2; + do + *c1++ = *c2++; + while (--length); + } + + release(reinterpret_cast(block), pool); + + if (new_->blk_type == (SCHAR) type_vec) + ((VEC) new_)->vec_count = size; + else if (new_->blk_type == (SCHAR) type_vcl) + ((VCL) new_)->vcl_count = size; + + *pointer = new_; + + return new_; +} + + +void ALLD_fini(void) +{ +/************************************** + * + * A L L D _ f i n i + * + ************************************** + * + * Functional description + * Get rid of everything. + * + **************************************/ + BLK *vector, *until; + PLB pool; + + assert(init_flag); /* Must _init before _fini */ + +/* if there are no pools, we've already finished. */ + + if (!pools) + return; + +/* vec_object [0] is the memory pool which contains pools, so release + * objects in reverse order, and be careful not to refer to pools + * during the process. + */ + for (vector = pools->vec_object + pools->vec_count, until = + pools->vec_object; --vector >= until;) + if ((pool = (PLB) * vector) != NULL) + ALLD_rlpool(pool); + + pools = NULL; + init_flag = FALSE; +} + + +void ALLD_free( SCHAR * memory) +{ +/************************************** + * + * A L L D _ f r e e + * + ************************************** + * + * Functional description + * Give space back to system. + * + **************************************/ + +#ifdef SUPERSERVER + alld_delta_alloc -= gds__free(memory); +#else + gds__free(memory); +#endif +} + + +USHORT ALLD_init(void) +{ +/************************************** + * + * A L L D _ i n i t + * + ************************************** + * + * Functional description + * Initialize the pool system. Return + * TRUE if initialization took place, FALSE if + * we had been previous initialized. + * + **************************************/ + SLONG temp_vector[20]; + PLB pool; + USHORT init; + TSQL tdsql; + + tdsql = GET_THREAD_DATA; + + init = (init_flag == FALSE); + if (!init_flag) { + init_flag = TRUE; + + pools = (VEC) temp_vector; + pools->vec_count = 1; + pools->vec_object[0] = NULL; + + tdsql->tsql_default = DSQL_permanent_pool = pool = ALLD_pool(); + pools = (VEC) ALLD_alloc(pool, type_vec, 10); + pools->vec_count = 10; + + /* Note: ALLD_fini() assumes the this master pool is in vec_object [0] */ + pools->vec_object[0] = (BLK) pool; + } + + return init; +} + + +UCHAR *ALLD_malloc(ULONG size) +{ +/************************************** + * + * A L L D _ m a l l o c + * + ************************************** + * + * Functional description + * Get memory from system. + * + **************************************/ + register UCHAR *memory; + TSQL tdsql; + + tdsql = GET_THREAD_DATA; + + if ((memory = gds__alloc((SLONG) size)) != NULL) +#ifdef SUPERSERVER + { + alld_delta_alloc += size; + return memory; + } +#else + return memory; +#endif +/* FREE: by ALLD_free, called during DSQL cleanup */ +/* NOMEM: post a user level error - if we can */ + + if (tdsql && tdsql->tsql_setjmp) + ERRD_post(gds__sys_request, gds_arg_string, "gds__alloc", gds_arg_gds, + gds__virmemexh, gds_arg_end); + +/* Commentary: This expands out to a call to ERRD_error - which + * promply depends on tdsql being non-NULL. Knock, knock, anyone home? + */ + IBERROR(-1, "out of memory"); + return ((UCHAR *) NULL); /* Added to remove warnings */ +} + + +PLB ALLD_pool(void) +{ +/************************************** + * + * A L L D _ p o o l + * + ************************************** + * + * Functional description + * Allocate a new pool. This is done by creating a tempory + * pool block on the stack, then allocating a real pool block. + * In short, by mirrors. + * + **************************************/ + struct plb temp_pool; + register PLB pool; + register USHORT pool_id; + +/* Start by assigning a pool id */ + + for (pool_id = 0; pool_id < pools->vec_count; pool_id++) + if (!(pools->vec_object[pool_id])) + break; + + if (pool_id >= pools->vec_count) + ALLD_extend((BLK *) & pools, (ULONG) (pool_id + 10)); + + memset((UCHAR *) & temp_pool, 0, sizeof(temp_pool)); + temp_pool.plb_header.blk_type = type_plb; + pools->vec_object[pool_id] = (BLK) & temp_pool; + temp_pool.plb_free = NULL; + temp_pool.plb_hunks = NULL; + temp_pool.plb_pool_id = pool_id; + temp_pool.plb_blk_type_count = NULL; +#ifdef SUPERSERVER + if (trace_pools) + { + temp_pool.plb_blk_type_count = + reinterpret_cast (gds__alloc(sizeof(alld_block_type_count))); + if (!temp_pool.plb_blk_type_count) { + trace_pools = 0; /* No memory!! stop tracing pool info */ + } else { + memset(temp_pool.plb_blk_type_count, 0, sizeof(alld_block_type_count)); + } + } +#endif + + if (pool_id == 0) + DSQL_permanent_pool = &temp_pool; + + pool = (PLB) ALLD_alloc(&temp_pool, type_plb, 0); + pool->plb_pool_id = pool_id; + pool->plb_free = temp_pool.plb_free; + pool->plb_hunks = temp_pool.plb_hunks; +#ifdef SUPERSERVER + pool->plb_blk_type_count = temp_pool.plb_blk_type_count; +#endif + pools->vec_object[pool_id] = (BLK) pool; + + if (pool_id == 0) + DSQL_permanent_pool = pool; + + return pool; +} + + +#ifdef SUPERSERVER +void ALLD_print_memory_pool_info( IB_FILE * fptr) +{ +/************************************************************* + * + * A L L D _ p r i n t _ m e m o r y _ p o o l _ i n f o + * + ************************************************************* + * + * Functional description + * Print the various block types allocated with in the pools + * + *************************************************************/ + VEC vector; + PLB myPool; + HNK hnk; + int i, j, col; + + if (!trace_pools) + return; + + ib_fprintf(fptr, "\n\tALLD_xx block types\n"); + ib_fprintf(fptr, "\t--------------------"); + for (i = 0, col = 0; i < type_MAX; i++) + if (alld_block_type_count[i]) { + if (col % 5 == 0) + ib_fprintf(fptr, "\n\t"); + ib_fprintf(fptr, "%s = %d ", ALLD_types[i], + alld_block_type_count[i]); + ++col; + } + ib_fprintf(fptr, "\n"); + + if (!pools) { + ib_fprintf(fptr, "\t No pools allocated"); + return; + } + vector = pools; + for (j = 0, i = 0; i < (int) vector->vec_count; i++) { + myPool = (PLB) vector->vec_object[i]; + if (myPool) + ++j; + } + ib_fprintf(fptr, "\t There are %d pools", j); + for (i = 0; i < (int) vector->vec_count; i++) { + myPool = (PLB) vector->vec_object[i]; + if (!myPool) + continue; + ib_fprintf(fptr, "\n\t Pool %d", myPool->plb_pool_id); + for (j = 0, hnk = myPool->plb_hunks; hnk; hnk = hnk->hnk_next) + j++; + if (j) + ib_fprintf(fptr, " has %d hunks", j); + for (j = 0, col = 0; j < type_MAX; j++) + if (myPool->plb_blk_type_count[j]) { + if (col % 5 == 0) + ib_fprintf(fptr, "\n\t "); + ib_fprintf(fptr, "%s = %d ", ALLD_types[j], + myPool->plb_blk_type_count[j]); + ++col; + } + } +} +#endif + + +void ALLD_push( BLK object, register LLS * stack) +{ +/************************************** + * + * A L L D _ p u s h + * + ************************************** + * + * Functional description + * Push an object on an LLS stack. + * + **************************************/ + register LLS node; + PLB pool; + TSQL tdsql; + + tdsql = GET_THREAD_DATA; + + pool = tdsql->tsql_default; + + if ((node = pool->plb_lls) != NULL) { + /* Pull a stack block off the list of free stack blocks */ + pool->plb_lls = node->lls_next; + } + else { + /* No free stack blocks - allocate a new one */ + node = (LLS) ALLD_alloc(pool, type_lls, 0); + } + + DEV_BLKCHK(node, type_lls); + DEV_BLKCHK(*stack, type_lls); + + node->lls_object = object; + node->lls_next = *stack; + *stack = node; +} + + +BLK ALLD_pop(register LLS * stack) +{ +/************************************** + * + * A L L D _ p o p + * + ************************************** + * + * Functional description + * Pop an object off a linked list stack. Save the node for + * further use. + * + **************************************/ + register LLS node; + register PLB pool; + BLK block; + + DEV_BLKCHK(*stack, type_lls); + + node = *stack; + + DEV_BLKCHK(node, type_lls); + + + +/* what we are doing below is .... block = &(node->lls_header) */ + block = (BLK) node; + pool = find_pool(block); + +/* Pop item off the stack */ + *stack = node->lls_next; + +/* Add the popped stack lls block to the list of free stack blocks */ + node->lls_next = pool->plb_lls; + pool->plb_lls = node; + + return node->lls_object; +} + + +void ALLD_release( register FRB block) +{ +/************************************** + * + * A L L D _ r e l e a s e + * + ************************************** + * + * Functional description + * Release a block to its pool. If it is contiguous to + * another free block, combine them. Otherwise link it + * into the free block linked list (kept in ascending order + * of addresses). + * + **************************************/ + + release(block, find_pool(&block->frb_header)); +} + + +void ALLD_rlpool( PLB pool) +{ +/************************************** + * + * A L L D _ r l p o o l + * + ************************************** + * + * Functional description + * Release a storage pool. This involves nothing more than returning + * hunks to the free hunk list. + * + **************************************/ + register HNK hunk, hunks; + +/* if there are no pools, there's no point in releasing anything... */ + + if (!pools) + return; + + DEV_BLKCHK(pool, type_plb); + + pools->vec_object[pool->plb_pool_id] = NULL; + +/* Have to release hunks carefully, as one of the hunks being released + * contains pool itself + */ +#ifdef SUPERSERVER + if (trace_pools && pool->plb_blk_type_count) { + int i; + for (i = 0; i < type_MAX; i++) + alld_block_type_count[i] -= pool->plb_blk_type_count[i]; + gds__free(pool->plb_blk_type_count); + } +#endif + + for (hunks = pool->plb_hunks; hunk = hunks;) { + hunks = hunk->hnk_next; + ALLD_free(reinterpret_cast < char *>(hunk->hnk_address)); + } +} + + +static void extend_pool( PLB pool, ULONG size) +{ +/************************************** + * + * e x t e n d _ p o o l + * + ************************************** + * + * Functional description + * Extend a pool by at least enough to accomodate a block + * of given size. + * + **************************************/ + register HNK hunk; + register BLK block; + + DEV_BLKCHK(pool, type_plb); + + size = + (size + sizeof(struct hnk) + MIN_ALLOCATION - + 1) & ~((ULONG) MIN_ALLOCATION - 1); + block = (BLK) ALLD_malloc(size); + assert(SIZE_TO_BLOCKS(size) <= MAX_USHORT); + block->blk_length = (USHORT) SIZE_TO_BLOCKS(size); + block->blk_type = (SCHAR) type_frb; + assert(pool->plb_pool_id <= MAX_UCHAR); + block->blk_pool_id = (UCHAR) pool->plb_pool_id; +#ifdef SUPERSERVER + if (trace_pools) { + ++alld_block_type_count[block->blk_type]; + ++pool->plb_blk_type_count[block->blk_type]; + } +#endif + release(reinterpret_cast(block), pool); + + hunk = (HNK) ALLD_alloc(pool, type_hnk, 0); + hunk->hnk_address = (UCHAR *) block; + hunk->hnk_length = size; + hunk->hnk_next = pool->plb_hunks; + pool->plb_hunks = hunk; +} + + +static PLB find_pool( BLK block) +{ +/************************************** + * + * f i n d _ p o o l + * + ************************************** + * + * Functional description + * Find pool associate with block. + * + **************************************/ + PLB pool; + HNK hunk; + USHORT pool_id; + + if (pools->vec_count < 256) + if ((pool_id = block->blk_pool_id) < pools->vec_count && + (pool = (PLB) pools->vec_object[pool_id])) + return pool; + else + BUGCHECK("bad pool id"); + + for (pool_id = block->blk_pool_id; pool_id < pools->vec_count; + pool_id += 256) if (pool = (PLB) pools->vec_object[pool_id]) { + hunk = pool->plb_hunks; + for (; hunk; hunk = hunk->hnk_next) + if ((SCHAR HUGE_PTR *) block >= + (SCHAR HUGE_PTR *) hunk->hnk_address + && (SCHAR HUGE_PTR *) block < + (SCHAR HUGE_PTR *) hunk->hnk_address + hunk->hnk_length) + return pool; + } + BUGCHECK("bad pool id"); + return (PLB) NULL; /* Added to remove warnings */ +} + + +static void release( FRB block, PLB pool) +{ +/************************************** + * + * r e l e a s e + * + ************************************** + * + * Functional description + * Release a block to its pool. If it is contiguous to + * another free block, combine them. Otherwise link it + * into the free block linked list (kept in ascending order + * of addresses). + * + **************************************/ + register FRB prior, free; + FRB *ptr; +#ifdef SUPERSERVER + UCHAR blk_header_type; +#endif + + DEV_BLKCHK(pool, type_plb); + +#ifdef SUPERSERVER + blk_header_type = block->frb_header.blk_type; +#endif + block->frb_header.blk_type = (SCHAR) type_frb; + prior = NULL; + + for (ptr = &pool->plb_free; (free = *ptr) != NULL; + prior = free, ptr = + &free->frb_next) if ((SCHAR HUGE_PTR *) block <= + (SCHAR HUGE_PTR *) free) break; + +#ifdef DEBUG_GDS_ALLOC +/* Debugging code to erase memory locations after a release - + * this will assist in catching dangling memory references to + * freed locations. + * Note that the header parts of the freed block may still be used, + * for the free chain, so we don't zap those locations. + */ + +#define ALLD_RELEASED_PATTERN 0xEE + { + ULONG size; + size = BLOCKS_TO_SIZE(block->frb_header.blk_length); + assert(size >= sizeof(struct blk)); + size -= sizeof(struct blk); + if (size) + memset((SCHAR *) block + sizeof(struct blk), + ALLD_RELEASED_PATTERN, size); + }; +#endif /* DEBUG_GDS_ALLOC */ + + if ((SCHAR HUGE_PTR *) block == (SCHAR HUGE_PTR *) free) { + BUGCHECK("block released twice"); + } + +/* Merge block into list first, then try to combine blocks */ + + block->frb_next = free; + *ptr = block; + +/* Try to merge the free block with the next one down. */ + + if (free) { + if ((SCHAR HUGE_PTR *) block + EXPAND_BLOCKSIZE(&block->frb_header) == + (SCHAR HUGE_PTR *) free) { + block->frb_header.blk_length += free->frb_header.blk_length; + block->frb_next = free->frb_next; +#ifdef DEBUG_GDS_ALLOC + /* Paint the freed header of the merged-away block */ + memset((UCHAR *) free, ALLD_RELEASED_PATTERN, sizeof(*free)); +#endif /* DEBUG_GDS_ALLOC */ + } + else if ((SCHAR HUGE_PTR *) block + + EXPAND_BLOCKSIZE(&block->frb_header) > + (SCHAR HUGE_PTR *) free) { + BUGCHECK("released block overlaps following free block"); + } + } + +/* Try and merge the block with the prior free block */ + + if (prior) { + if ((SCHAR HUGE_PTR *) prior + EXPAND_BLOCKSIZE(&prior->frb_header) == + (SCHAR HUGE_PTR *) block) { + prior->frb_header.blk_length += block->frb_header.blk_length; + prior->frb_next = block->frb_next; +#ifdef DEBUG_GDS_ALLOC + /* Paint the freed header of the merged-away block */ + memset((UCHAR *) block, ALLD_RELEASED_PATTERN, sizeof(*block)); +#endif /* DEBUG_GDS_ALLOC */ + } + else if ((SCHAR HUGE_PTR *) prior + + EXPAND_BLOCKSIZE(&prior->frb_header) > + (SCHAR HUGE_PTR *) block) { + BUGCHECK("released block overlaps prior free block"); + } + } +#ifdef SUPERSERVER + if (trace_pools) { + --alld_block_type_count[blk_header_type]; + --pool->plb_blk_type_count[blk_header_type]; + } +#endif +} + + +} // extern "C" diff --git a/src/dsql/alld_proto.h b/src/dsql/alld_proto.h new file mode 100644 index 0000000000..cd40ec5ea0 --- /dev/null +++ b/src/dsql/alld_proto.h @@ -0,0 +1,47 @@ +/* + * PROGRAM: Dynamic SQL RUNTIME SUPPORT + * MODULE: alld_proto.h + * DESCRIPTION: Prototype Header file for alld.c + * + * 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): ______________________________________. + */ + +#ifndef _DSQL_ALLD_PROTO_H_ +#define _DSQL_ALLD_PROTO_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +extern BLK ALLD_alloc(struct plb *, UCHAR, ULONG); +extern BLK ALLD_extend(struct blk **, ULONG); +extern void ALLD_fini(void); +extern void ALLD_free(SCHAR *); +extern USHORT ALLD_init(void); +extern UCHAR *ALLD_malloc(ULONG); +extern PLB ALLD_pool(void); +extern BLK ALLD_pop(register struct lls **); +extern void ALLD_push(struct blk *, register struct lls **); +extern void ALLD_release(register struct frb *); +extern void ALLD_rlpool(struct plb *); + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* _DSQL_ALLD_PROTO_H_ */ diff --git a/src/dsql/array.e b/src/dsql/array.e new file mode 100644 index 0000000000..00a69da5d2 --- /dev/null +++ b/src/dsql/array.e @@ -0,0 +1,631 @@ +/* + * PROGRAM: Interbase layered support library + * MODULE: array.e + * DESCRIPTION: Dynamic array support + * + * The contents of this file are subject to the Interbase Public + * License Version 1.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy + * of the License at http://www.Inprise.com/IPL.html + * + * Software distributed under the License is distributed on an + * "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either express + * or implied. See the License for the specific language governing + * rights and limitations under the License. + * + * The Original Code was created by Inprise Corporation + * and its predecessors. Portions created by Inprise Corporation are + * Copyright (C) Inprise Corporation. + * + * All Rights Reserved. + * Contributor(s): ______________________________________. + */ + +#include +#include "../jrd/common.h" +#include +#include "../include/jrd/gds.h" +#include "../dsql/array_proto.h" +#include "../jrd/gds_proto.h" + +#ifdef WINDOWS_ONLY +#include "../jrd/seg_proto.h" +#endif + +DATABASE DB = STATIC "yachts.lnk"; + +#define ARRAY_DESC_COLUMN_MAJOR 1 /* Set for FORTRAN */ + +typedef struct gen { + SCHAR *gen_sdl; + SCHAR **gen_sdl_ptr; + SCHAR *gen_end; + STATUS *gen_status; + SSHORT gen_internal; +} *GEN; + +static void adjust_length(ISC_ARRAY_DESC *); +static STATUS copy_status(STATUS *, STATUS *); +static STATUS error(STATUS *, SSHORT, ...); +static STATUS gen_sdl(STATUS *, ISC_ARRAY_DESC *, SSHORT *, SCHAR **, + SSHORT *, BOOLEAN); +static void get_name(SCHAR *, SCHAR *); +static STATUS lookup_desc(STATUS *, void **, void **, SCHAR *, SCHAR *, + ISC_ARRAY_DESC *, SCHAR *); +static STATUS stuff(GEN, SSHORT, ...); +static STATUS stuff_literal(GEN, SLONG); +static STATUS stuff_string(GEN, SCHAR, SCHAR *); + +/* STUFF_SDL used in place of STUFF to avoid confusion with BLR STUFF + macro defined in dsql.h */ + +#define STUFF_SDL(byte) if (stuff (gen, 1, byte)) return status [1] +#define STUFF_SDL_WORD(word) if (stuff (gen, 2, word, word >> 8)) return status [1] +#define STUFF_SDL_LONG(word) if (stuff (gen, 4, word, word >> 8, word >> 16, word >> 24)) return status [1] + + +STATUS API_ROUTINE isc_array_gen_sdl(STATUS * status, + ISC_ARRAY_DESC * desc, + SSHORT * sdl_buffer_length, + SCHAR * sdl_buffer, SSHORT * sdl_length) +{ +/************************************** + * + * i s c _ a r r a y _ g e n _ s d l + * + ************************************** + * + * Functional description + * + **************************************/ + + return gen_sdl(status, desc, sdl_buffer_length, &sdl_buffer, sdl_length, + FALSE); +} + + +STATUS API_ROUTINE isc_array_get_slice(STATUS * status, + void **db_handle, + void **trans_handle, + GDS_QUAD * array_id, + ISC_ARRAY_DESC * desc, + void *array, SLONG * slice_length) +{ +/************************************** + * + * i s c _ a r r a y _ g e t _ s l i c e + * + ************************************** + * + * Functional description + * + **************************************/ + SCHAR *sdl, sdl_buffer[512]; + SSHORT sdl_length; + + sdl_length = sizeof(sdl_buffer); + sdl = sdl_buffer; + + if (gen_sdl(status, desc, &sdl_length, &sdl, &sdl_length, TRUE)) + return status[1]; + + isc_get_slice(status, db_handle, trans_handle, array_id, + sdl_length, sdl, 0, NULL_PTR, + *slice_length, array, slice_length); + + if (sdl != sdl_buffer) + gds__free((SLONG *) sdl); + + return status[1]; +} + + +STATUS API_ROUTINE isc_array_lookup_bounds(STATUS * status, + void **db_handle, + void **trans_handle, + SCHAR * relation_name, + SCHAR * field_name, + ISC_ARRAY_DESC * desc) +{ +/************************************** + * + * i s c _ a r r a y _ l o o k u p _ b o u n d s + * + ************************************** + * + * Functional description + * + **************************************/ + ISC_ARRAY_BOUND *tail; + SCHAR global[32]; + + if (lookup_desc(status, db_handle, trans_handle, + field_name, relation_name, desc, global)) + return status[1]; + + tail = desc->array_desc_bounds; + + FOR X IN RDB$FIELD_DIMENSIONS WITH X.RDB$FIELD_NAME EQ global SORTED BY X.RDB$DIMENSION + tail->array_bound_lower = (SSHORT)X.RDB$LOWER_BOUND; + tail->array_bound_upper = (SSHORT)X.RDB$UPPER_BOUND; + ++tail; + END_FOR + ON_ERROR + return copy_status(gds__status, status); + END_ERROR; + + return status[1]; +} + + +STATUS API_ROUTINE isc_array_lookup_desc(STATUS * status, + void **db_handle, + void **trans_handle, + SCHAR * relation_name, + SCHAR * field_name, + ISC_ARRAY_DESC * desc) +{ +/************************************** + * + * i s c _ a r r a y _ l o o k u p _ d e s c + * + ************************************** + * + * Functional description + * + **************************************/ + + return lookup_desc(status, db_handle, trans_handle, + field_name, relation_name, desc, NULL_PTR); +} + + +STATUS API_ROUTINE isc_array_put_slice(STATUS * status, + void **db_handle, + void **trans_handle, + GDS_QUAD * array_id, + ISC_ARRAY_DESC * desc, + void *array, SLONG * slice_length) +{ +/************************************** + * + * i s c _ a r r a y _ p u t _ s l i c e + * + ************************************** + * + * Functional description + * + **************************************/ + SCHAR *sdl, sdl_buffer[512]; + SSHORT sdl_length; + + sdl_length = sizeof(sdl_buffer); + sdl = sdl_buffer; + + if (gen_sdl(status, desc, &sdl_length, &sdl, &sdl_length, TRUE)) + return status[1]; + + isc_put_slice(status, db_handle, trans_handle, array_id, + sdl_length, sdl, 0, NULL_PTR, *slice_length, array); + + if (sdl != sdl_buffer) + gds__free((SLONG *) sdl); + + return status[1]; +} + + +STATUS API_ROUTINE isc_array_set_desc(STATUS * status, + SCHAR * relation_name, + SCHAR * field_name, + SSHORT * sql_dtype, + SSHORT * sql_length, + SSHORT * dimensions, + ISC_ARRAY_DESC * desc) +{ +/************************************** + * + * i s c _ a r r a y _ s e t _ d e s c + * + ************************************** + * + * Functional description + * + **************************************/ + SSHORT dtype; + + get_name(field_name, desc->array_desc_field_name); + get_name(relation_name, desc->array_desc_relation_name); + desc->array_desc_flags = 0; + desc->array_desc_dimensions = *dimensions; + desc->array_desc_length = *sql_length; + desc->array_desc_scale = 0; + + dtype = *sql_dtype & ~1; + + if (dtype == SQL_VARYING) + desc->array_desc_dtype = blr_varying; + else if (dtype == SQL_TEXT) + desc->array_desc_dtype = blr_text; + else if (dtype == SQL_DOUBLE) + desc->array_desc_dtype = blr_double; + else if (dtype == SQL_FLOAT) + desc->array_desc_dtype = blr_float; + else if (dtype == SQL_D_FLOAT) + desc->array_desc_dtype = blr_d_float; + else if (dtype == SQL_TIMESTAMP) + desc->array_desc_dtype = blr_timestamp; + else if (dtype == SQL_TYPE_DATE) + desc->array_desc_dtype = blr_sql_date; + else if (dtype == SQL_TYPE_TIME) + desc->array_desc_dtype = blr_sql_time; + else if (dtype == SQL_LONG) + desc->array_desc_dtype = blr_long; + else if (dtype == SQL_SHORT) + desc->array_desc_dtype = blr_short; + else if (dtype == SQL_INT64) + desc->array_desc_dtype = blr_int64; + else if (dtype == SQL_QUAD) + desc->array_desc_dtype = blr_quad; + else + return error(status, 7, (STATUS) gds__sqlerr, + (STATUS) gds_arg_number, (STATUS) - 804, + (STATUS) gds_arg_gds, (STATUS) gds__random, + (STATUS) gds_arg_string, + (STATUS) "data type not understood"); + + return error(status, 1, (STATUS) SUCCESS); +} + + +static void adjust_length( ISC_ARRAY_DESC * desc) +{ +/************************************** + * + * a d j u s t _ l e n g t h + * + ************************************** + * + * Functional description + * Make architectural adjustment to fixed datatypes. + * + **************************************/ +} + + +static STATUS copy_status( STATUS * from, STATUS * to) +{ +/************************************** + * + * c o p y _ s t a t u s + * + ************************************** + * + * Functional description + * Copy a status vector. + * + **************************************/ + STATUS status, *end; + + status = from[1]; + + for (end = from + ISC_STATUS_LENGTH; from < end;) + *to++ = *from++; + + return status; +} + + +static STATUS error( STATUS * status, SSHORT count, ...) +{ +/************************************** + * + * e r r o r + * + ************************************** + * + * Functional description + * Stuff a status vector. + * + **************************************/ + STATUS *stat; + va_list ptr; + + VA_START(ptr, count); + stat = status; + *stat++ = gds_arg_gds; + + for (; count; --count) + *stat++ = (STATUS) va_arg(ptr, STATUS); + + *stat = gds_arg_end; + + return status[1]; +} + + +static STATUS gen_sdl(STATUS * status, + ISC_ARRAY_DESC * desc, + SSHORT * sdl_buffer_length, + SCHAR ** sdl_buffer, + SSHORT * sdl_length, BOOLEAN internal_flag) +{ +/************************************** + * + * g e n _ s d l + * + ************************************** + * + * Functional description + * + **************************************/ + SSHORT n, from, to, increment, dimensions; + ISC_ARRAY_BOUND *tail; + struct gen *gen, gen_block; + + + dimensions = desc->array_desc_dimensions; + + if (dimensions > 16) + return error(status, 5, (STATUS) gds__invalid_dimension, + (STATUS) gds_arg_number, (STATUS) dimensions, + (STATUS) gds_arg_number, (STATUS) 16); + + gen = &gen_block; + gen->gen_sdl = *sdl_buffer; + gen->gen_sdl_ptr = sdl_buffer; + gen->gen_end = *sdl_buffer + *sdl_buffer_length; + gen->gen_status = status; + gen->gen_internal = internal_flag ? 0 : -1; + + if (stuff + (gen, 4, gds__sdl_version1, gds__sdl_struct, 1, + desc->array_desc_dtype)) return status[1]; + + switch (desc->array_desc_dtype) { + case blr_short: + case blr_long: + case blr_int64: + case blr_quad: + STUFF_SDL(desc->array_desc_scale); + break; + + case blr_text: + case blr_cstring: + case blr_varying: + STUFF_SDL_WORD(desc->array_desc_length); + break; + default: + break; + } + + if (stuff_string(gen, gds__sdl_relation, desc->array_desc_relation_name)) + return status[1]; + + if (stuff_string(gen, gds__sdl_field, desc->array_desc_field_name)) + return status[1]; + + if (desc->array_desc_flags & ARRAY_DESC_COLUMN_MAJOR) { + from = dimensions - 1; + to = -1; + increment = -1; + } + else { + from = 0; + to = dimensions; + increment = 1; + } + + for (n = from; n != to; n += increment) { + tail = desc->array_desc_bounds + n; + if (tail->array_bound_lower == 1) { + if (stuff(gen, 2, gds__sdl_do1, n)) + return status[1]; + } + else { + if (stuff(gen, 2, gds__sdl_do2, n)) + return status[1]; + if (stuff_literal(gen, (SLONG) tail->array_bound_lower)) + return status[1]; + } + if (stuff_literal(gen, (SLONG) tail->array_bound_upper)) + return status[1]; + } + + if (stuff(gen, 5, gds__sdl_element, 1, gds__sdl_scalar, 0, dimensions)) + return status[1]; + + for (n = 0; n < dimensions; n++) + if (stuff(gen, 2, gds__sdl_variable, n)) + return status[1]; + + STUFF_SDL(gds__sdl_eoc); + *sdl_length = gen->gen_sdl - *gen->gen_sdl_ptr; + + return error(status, 1, (STATUS) SUCCESS); +} + + +static void get_name( SCHAR * from, SCHAR * to) +{ +/************************************** + * + * g e t _ n a m e + * + ************************************** + * + * Functional description + * Copy null or blank terminated name. + * + **************************************/ + + while (*from && *from != ' ') { + *to++ = UPPER7(*from); + from++; + } + + *to = 0; +} + + +static STATUS lookup_desc(STATUS * status, + void **db_handle, + void **trans_handle, + SCHAR * field_name, +SCHAR * relation_name, ISC_ARRAY_DESC * desc, SCHAR * global) +{ +/************************************** + * + * l o o k u p _ d e s c + * + ************************************** + * + * Functional description + * + **************************************/ + SSHORT flag; + + if (DB && DB != *db_handle) + RELEASE_REQUESTS; + + DB = *db_handle; + gds__trans = *trans_handle; + get_name(field_name, desc->array_desc_field_name); + get_name(relation_name, desc->array_desc_relation_name); + desc->array_desc_flags = 0; + + flag = FALSE; + + FOR X IN RDB$RELATION_FIELDS CROSS Y IN RDB$FIELDS + WITH X.RDB$FIELD_SOURCE EQ Y.RDB$FIELD_NAME AND + X.RDB$RELATION_NAME EQ desc->array_desc_relation_name AND + X.RDB$FIELD_NAME EQ desc->array_desc_field_name + flag = TRUE; + desc->array_desc_dtype = (UCHAR)Y.RDB$FIELD_TYPE; + desc->array_desc_scale = (SCHAR)Y.RDB$FIELD_SCALE; + desc->array_desc_length = Y.RDB$FIELD_LENGTH; + adjust_length(desc); + desc->array_desc_dimensions = Y.RDB$DIMENSIONS; + if (global) + get_name(Y.RDB$FIELD_NAME, global); + END_FOR + ON_ERROR + return copy_status(gds__status, status); + END_ERROR; + + if (!flag) + return error(status, 5, (STATUS) gds__fldnotdef, + (STATUS) gds_arg_string, + (STATUS) desc->array_desc_field_name, + (STATUS) gds_arg_string, + (STATUS) desc->array_desc_relation_name); + + return error(status, 1, (STATUS) SUCCESS); +} + + +static STATUS stuff( GEN gen, SSHORT count, ...) +{ +/************************************** + * + * s t u f f + * + ************************************** + * + * Functional description + * Stuff a SDL byte. + * + **************************************/ + SCHAR c, *new_sdl; + va_list ptr; + SSHORT new_len, current_len; + + if (gen->gen_sdl + count >= gen->gen_end) { + if (gen->gen_internal < 0) + return error(gen->gen_status, 3, (STATUS) gds__misc_interpreted, + (STATUS) gds_arg_string, + (STATUS) "SDL buffer overflow"); + + /* The sdl buffer is too small. Allocate a larger one. */ + + new_len = gen->gen_end - *gen->gen_sdl_ptr + 512 + count; + new_sdl = (SCHAR *) gds__alloc((SLONG) new_len); + if (!new_sdl) + return error(gen->gen_status, 5, (STATUS) gds__misc_interpreted, + (STATUS) gds_arg_string, + (STATUS) "SDL buffer overflow", (STATUS) gds_arg_gds, + (STATUS) gds__virmemexh); + + current_len = gen->gen_sdl - *gen->gen_sdl_ptr; + memcpy(new_sdl, *gen->gen_sdl_ptr, current_len); + if (gen->gen_internal++) + gds__free(*gen->gen_sdl_ptr); + gen->gen_sdl = new_sdl + current_len; + *gen->gen_sdl_ptr = new_sdl; + gen->gen_end = new_sdl + new_len; + } + + VA_START(ptr, count); + + for (; count; --count) { + c = va_arg(ptr, int); + *(gen->gen_sdl)++ = c; + } + + return 0; +} + + +static STATUS stuff_literal( GEN gen, SLONG literal) +{ +/************************************** + * + * s t u f f _ l i t e r a l + * + ************************************** + * + * Functional description + * Stuff an SDL literal. + * + **************************************/ + STATUS *status; + + status = gen->gen_status; + + if (literal >= -128 && literal <= 127) + return stuff(gen, 2, gds__sdl_tiny_integer, literal); + + if (literal >= -32768 && literal <= 32767) + return stuff(gen, 3, gds__sdl_short_integer, literal, literal >> 8); + + STUFF_SDL(gds__sdl_long_integer); + STUFF_SDL_LONG(literal); + + return SUCCESS; +} + + +static STATUS stuff_string( GEN gen, SCHAR sdl, SCHAR * string) +{ +/************************************** + * + * s t u f f _ s t r i n g + * + ************************************** + * + * Functional description + * Stuff a "thing" then a counted string. + * + **************************************/ + STATUS *status; + + status = gen->gen_status; + + STUFF_SDL(sdl); + STUFF_SDL(strlen(string)); + + while (*string) + STUFF_SDL(*string++); + + return SUCCESS; +} diff --git a/src/dsql/array_proto.h b/src/dsql/array_proto.h new file mode 100644 index 0000000000..ed584cff36 --- /dev/null +++ b/src/dsql/array_proto.h @@ -0,0 +1,45 @@ +/* + * PROGRAM: Dynamic SQL runtime support + * MODULE: array_proto.h + * DESCRIPTION: Prototype Header file for array.e + * + * 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): ______________________________________. + */ + +#ifndef _DSQL_ARRAY_PROTO_H_ +#define _DSQL_ARRAY_PROTO_H_ + +extern STATUS API_ROUTINE isc_array_gen_sdl(STATUS *, ISC_ARRAY_DESC *, + SSHORT *, SCHAR *, SSHORT *); +extern STATUS API_ROUTINE isc_array_get_slice(STATUS *, void **, void **, + GDS_QUAD *, ISC_ARRAY_DESC *, + void *, SLONG *); +extern STATUS API_ROUTINE isc_array_lookup_bounds(STATUS *, void **, void **, + SCHAR *, SCHAR *, + ISC_ARRAY_DESC *); +extern STATUS API_ROUTINE isc_array_lookup_desc(STATUS *, void **, void **, + SCHAR *, SCHAR *, + ISC_ARRAY_DESC *); +extern STATUS API_ROUTINE isc_array_put_slice(STATUS *, void **, void **, + GDS_QUAD *, ISC_ARRAY_DESC *, + void *, SLONG *); +extern STATUS API_ROUTINE isc_array_set_desc(STATUS *, SCHAR *, SCHAR *, + SSHORT *, SSHORT *, SSHORT *, + ISC_ARRAY_DESC *); + +#endif /*_DSQL_ARRAY_PROTO_H_ */ diff --git a/src/dsql/blk.h b/src/dsql/blk.h new file mode 100644 index 0000000000..5618f43cf2 --- /dev/null +++ b/src/dsql/blk.h @@ -0,0 +1,49 @@ +/* + * PROGRAM: Dynamic SQL runtime support + * MODULE: blk.h + * DESCRIPTION: Block type definitions + * + * 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): ______________________________________. + */ + +BLKDEF(type_ctx, ctx, 0) + BLKDEF(type_par, par, 0) + BLKDEF(type_map, map, 0) + BLKDEF(type_req, req, 0) + BLKDEF(type_dbb, dbb, 0) + BLKDEF(type_dsql_rel, dsql_rel, 1) + BLKDEF(type_fld, fld, 1) + BLKDEF(type_fil, fil, 0) + BLKDEF(type_nod, nod, sizeof(((NOD) 0)->nod_arg[0])) + BLKDEF(type_msg, msg, 0) + BLKDEF(type_frb, frb, 0) + BLKDEF(type_hnk, hnk, 0) + BLKDEF(type_plb, plb, 0) + BLKDEF(type_vec, vec, sizeof(((VEC) 0)->vec_object[0])) + BLKDEF(type_vcl, vcl, sizeof(((VCL) 0)->vcl_long[0])) + BLKDEF(type_lls, lls, 0) /* linked list stack */ + BLKDEF(type_str, str, 1) /* random string block */ + BLKDEF(type_sym, sym, 1) /* symbol block */ + BLKDEF(type_err, err, 0) + BLKDEF(type_opn, opn, 0) + BLKDEF(type_tra, tra, 0) + BLKDEF(type_udf, udf, 1) + BLKDEF(type_var, var, 1) + BLKDEF(type_blb, blb, 0) + BLKDEF(type_prc, prc, 1) + BLKDEF(type_intlsym, intlsym, 1) diff --git a/src/dsql/blob.e b/src/dsql/blob.e new file mode 100644 index 0000000000..f16124825e --- /dev/null +++ b/src/dsql/blob.e @@ -0,0 +1,277 @@ +/* + * PROGRAM: InterBase layered support library + * MODULE: blob.e + * DESCRIPTION: Dynamic blob support + * + * The contents of this file are subject to the Interbase Public + * License Version 1.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy + * of the License at http://www.Inprise.com/IPL.html + * + * Software distributed under the License is distributed on an + * "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either express + * or implied. See the License for the specific language governing + * rights and limitations under the License. + * + * The Original Code was created by Inprise Corporation + * and its predecessors. Portions created by Inprise Corporation are + * Copyright (C) Inprise Corporation. + * + * All Rights Reserved. + * Contributor(s): ______________________________________. + */ + +#include "../jrd/common.h" +#include +#include "../include/jrd/gds.h" +#include "../jrd/intl.h" +#include "../jrd/constants.h" +#include "../dsql/blob_proto.h" + +DATABASE DB = STATIC "yachts.lnk"; + +static STATUS copy_status(STATUS *, STATUS *); +static STATUS error(STATUS *, SSHORT, ...); +static void get_name(UCHAR *, UCHAR *); + + +void API_ROUTINE isc_blob_default_desc( + ISC_BLOB_DESC * desc, + UCHAR * relation_name, + UCHAR * field_name) +{ +/************************************** + * + * i s c _ b l o b _ d e f a u l t _ d e s c + * + ************************************** + * + * Functional description + * + * This function will set the default + * values in the blob_descriptor. + * + **************************************/ + + desc->blob_desc_subtype = BLOB_text; + desc->blob_desc_charset = CS_dynamic; + desc->blob_desc_segment_size = 80; + get_name(field_name, desc->blob_desc_field_name); + get_name(relation_name, desc->blob_desc_relation_name); +} + + +STATUS API_ROUTINE isc_blob_gen_bpb( + STATUS * status, + ISC_BLOB_DESC * to_desc, + ISC_BLOB_DESC * from_desc, +USHORT bpb_buffer_length, UCHAR * bpb_buffer, USHORT * bpb_length) +{ +/************************************** + * + * i s c _ b l o b _ g e n _ b p b + * + ************************************** + * + * Functional description + * + * This function will generate a bpb + * given a to_desc and a from_desc + * which contain the subtype and + * character set information. + * + **************************************/ + UCHAR *p; + + if (bpb_buffer_length < 17) + return error(status, 3, (STATUS) gds__random, + (STATUS) gds_arg_string, + (STATUS) "BPB buffer too small"); + + p = bpb_buffer; + *p++ = isc_bpb_version1; + *p++ = isc_bpb_target_type; + *p++ = 2; + *p++ = (UCHAR)to_desc->blob_desc_subtype; + *p++ = (UCHAR)(to_desc->blob_desc_subtype >> 8); + *p++ = isc_bpb_source_type; + *p++ = 2; + *p++ = (UCHAR)from_desc->blob_desc_subtype; + *p++ = (UCHAR)(from_desc->blob_desc_subtype >> 8); + *p++ = isc_bpb_target_interp; + *p++ = 2; + *p++ = (UCHAR)to_desc->blob_desc_charset; + *p++ = (UCHAR)(to_desc->blob_desc_charset >> 8); + *p++ = isc_bpb_source_interp; + *p++ = 2; + *p++ = (UCHAR)from_desc->blob_desc_charset; + *p++ = (UCHAR)(from_desc->blob_desc_charset >> 8); + + *bpb_length = p - bpb_buffer; + + return error(status, 1, (STATUS) SUCCESS); +} + + +STATUS API_ROUTINE isc_blob_lookup_desc(STATUS * status, + void **db_handle, + void **trans_handle, + UCHAR * relation_name, + UCHAR * field_name, + ISC_BLOB_DESC * desc, UCHAR * global) +{ +/*********************************************** + * + * i s c _ b l o b _ l o o k u p _ d e s c + * + *********************************************** + * + * Functional description + * + * This routine will lookup the subtype, + * character set and segment size information + * from the metadata, given a relation name + * and column name. it will fill in the information + * in the BLOB_DESC. + * + ***********************************************/ + SSHORT flag; + + if (DB && DB != *db_handle) + RELEASE_REQUESTS; + + DB = *db_handle; + gds__trans = *trans_handle; + get_name(field_name, desc->blob_desc_field_name); + get_name(relation_name, desc->blob_desc_relation_name); + + flag = FALSE; + + FOR X IN RDB$RELATION_FIELDS CROSS Y IN RDB$FIELDS + WITH X.RDB$FIELD_SOURCE EQ Y.RDB$FIELD_NAME AND + X.RDB$RELATION_NAME EQ desc->blob_desc_relation_name AND + X.RDB$FIELD_NAME EQ desc->blob_desc_field_name flag = TRUE; + + desc->blob_desc_subtype = Y.RDB$FIELD_SUB_TYPE; + desc->blob_desc_charset = Y.RDB$CHARACTER_SET_ID; + desc->blob_desc_segment_size = Y.RDB$SEGMENT_LENGTH; + + if (global) + get_name((UCHAR *) Y.RDB$FIELD_NAME, global); + END_FOR ON_ERROR return copy_status(gds__status, status); + END_ERROR; + + if (!flag) + return error(status, 5, (STATUS) gds__fldnotdef, + (STATUS) gds_arg_string, + (STATUS) desc->blob_desc_field_name, + (STATUS) gds_arg_string, + (STATUS) desc->blob_desc_relation_name); + + return error(status, 1, (STATUS) SUCCESS); +} + + +STATUS API_ROUTINE isc_blob_set_desc(STATUS * status, + UCHAR * relation_name, + UCHAR * field_name, + SSHORT subtype, + SSHORT charset, + SSHORT segment_size, + ISC_BLOB_DESC * desc) +{ +/************************************** + * + * i s c _ b l o b _ s e t _ d e s c + * + ************************************** + * + * Functional description + * + * This routine will set the subtype + * and character set information in the + * BLOB_DESC based on the information + * specifically passed in by the user. + * + **************************************/ + + get_name(field_name, desc->blob_desc_field_name); + get_name(relation_name, desc->blob_desc_relation_name); + + desc->blob_desc_subtype = subtype; + desc->blob_desc_charset = charset; + desc->blob_desc_segment_size = segment_size; + + return error(status, 1, (STATUS) SUCCESS); +} + + +static STATUS copy_status( STATUS * from, STATUS * to) +{ +/************************************** + * + * c o p y _ s t a t u s + * + ************************************** + * + * Functional description + * Copy a status vector. + * + **************************************/ + STATUS status, *end; + + status = from[1]; + + for (end = from + 20; from < end;) + *to++ = *from++; + + return status; +} + + +static STATUS error( STATUS * status, SSHORT count, ...) +{ +/************************************** + * + * e r r o r + * + ************************************** + * + * Functional description + * Stuff a status vector. + * + **************************************/ + STATUS *stat; + va_list ptr; + + VA_START(ptr, count); + stat = status; + *stat++ = gds_arg_gds; + + for (; count; --count) + *stat++ = (STATUS) va_arg(ptr, STATUS); + + *stat = gds_arg_end; + + return status[1]; +} + + +static void get_name( UCHAR * from, UCHAR * to) +{ +/************************************** + * + * g e t _ n a m e + * + ************************************** + * + * Functional description + * Copy null or blank terminated name. + * + **************************************/ + + while (*from && *from != ' ') + *to++ = *from++; + + *to = 0; +} diff --git a/src/dsql/blob_proto.h b/src/dsql/blob_proto.h new file mode 100644 index 0000000000..a48386c54b --- /dev/null +++ b/src/dsql/blob_proto.h @@ -0,0 +1,37 @@ +/* + * PROGRAM: Dynamic SQL runtime support + * MODULE: blob_proto.h + * DESCRIPTION: Prototype Header file for blob.e + * + * 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): ______________________________________. + */ + +#ifndef _DSQL_BLOB_PROTO_H_ +#define _DSQL_BLOB_PROTO_H_ + +extern STATUS API_ROUTINE isc_blob_gen_bpb(STATUS *, ISC_BLOB_DESC *, + ISC_BLOB_DESC *, USHORT, UCHAR *, + USHORT *); +extern STATUS API_ROUTINE isc_blob_lookup_desc(STATUS *, void **, void **, + UCHAR *, UCHAR *, + ISC_BLOB_DESC *, UCHAR *); +extern STATUS API_ROUTINE isc_blob_set_desc(STATUS *, UCHAR *, UCHAR *, + SSHORT, SSHORT, SSHORT, + ISC_BLOB_DESC *); + +#endif /*_DSQL_BLOB_PROTO_H_ */ diff --git a/src/dsql/chars.h b/src/dsql/chars.h new file mode 100644 index 0000000000..8a990a2e5e --- /dev/null +++ b/src/dsql/chars.h @@ -0,0 +1,289 @@ +/* + * PROGRAM: Dynamic SQL runtime support + * MODULE: chars.h + * DESCRIPTION: character classification + * + * 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): ______________________________________. + */ + +#define CHR_LETTER 1 +#define CHR_DIGIT 2 +#define CHR_IDENT 4 +#define CHR_QUOTE 8 +#define CHR_WHITE 16 +#define CHR_HEX 32 +#define CHR_INTRODUCER 64 + +static CONST SCHAR classes[] = { +/* 000 */ 0, +/* 001 */ 0, +/* 002 */ 0, +/* 003 */ 0, +/* 004 */ 0, +/* 005 */ 0, +/* 006 */ 0, +/* 007 */ 0, +/* 008 */ 0, +/* 009 */ 0 | CHR_WHITE, +/* 010 */ 0 | CHR_WHITE, +/* 011 */ 0, +/* 012 */ 0, +/* 013 */ 0 | CHR_WHITE, +/* 014 */ 0, +/* 015 */ 0, +/* 016 */ 0, +/* 017 */ 0, +/* 018 */ 0, +/* 019 */ 0, +/* 020 */ 0, +/* 021 */ 0, +/* 022 */ 0, +/* 023 */ 0, +/* 024 */ 0, +/* 025 */ 0, +/* 026 */ 0, +/* 027 */ 0, +/* 028 */ 0, +/* 029 */ 0, +/* 030 */ 0, +/* 031 */ 0, +/* 032 */ 0 | CHR_WHITE, +/* 033 ! */ 0, +/* 034 " */ 0 | CHR_QUOTE, +/* 035 # */ 0, +/* 036 $ */ 0 | CHR_IDENT, +/* 037 % */ 0, +/* 038 & */ 0, +/* 039 ' */ 0 | CHR_QUOTE, +/* 040 ( */ 0, +/* 041 ) */ 0, +/* 042 * */ 0, +/* 043 + */ 0, +/* 044 , */ 0, +/* 045 - */ 0, +/* 046 . */ 0, +/* 047 / */ 0, +/* 048 0 */ 0 | CHR_DIGIT | CHR_IDENT | CHR_HEX, +/* 049 1 */ 0 | CHR_DIGIT | CHR_IDENT | CHR_HEX, +/* 050 2 */ 0 | CHR_DIGIT | CHR_IDENT | CHR_HEX, +/* 051 3 */ 0 | CHR_DIGIT | CHR_IDENT | CHR_HEX, +/* 052 4 */ 0 | CHR_DIGIT | CHR_IDENT | CHR_HEX, +/* 053 5 */ 0 | CHR_DIGIT | CHR_IDENT | CHR_HEX, +/* 054 6 */ 0 | CHR_DIGIT | CHR_IDENT | CHR_HEX, +/* 055 7 */ 0 | CHR_DIGIT | CHR_IDENT | CHR_HEX, +/* 056 8 */ 0 | CHR_DIGIT | CHR_IDENT | CHR_HEX, +/* 057 9 */ 0 | CHR_DIGIT | CHR_IDENT | CHR_HEX, +/* 058 : */ 0, +/* 059 ; */ 0, +/* 060 < */ 0, +/* 061 = */ 0, +/* 062 > */ 0, +/* 063 ? */ 0, +/* 064 @ */ 0, +/* 065 A */ 0 | CHR_LETTER | CHR_IDENT | CHR_HEX, +/* 066 B */ 0 | CHR_LETTER | CHR_IDENT | CHR_HEX, +/* 067 C */ 0 | CHR_LETTER | CHR_IDENT | CHR_HEX, +/* 068 D */ 0 | CHR_LETTER | CHR_IDENT | CHR_HEX, +/* 069 E */ 0 | CHR_LETTER | CHR_IDENT | CHR_HEX, +/* 070 F */ 0 | CHR_LETTER | CHR_IDENT | CHR_HEX, +/* 071 G */ 0 | CHR_LETTER | CHR_IDENT, +/* 072 H */ 0 | CHR_LETTER | CHR_IDENT, +/* 073 I */ 0 | CHR_LETTER | CHR_IDENT, +/* 074 J */ 0 | CHR_LETTER | CHR_IDENT, +/* 075 K */ 0 | CHR_LETTER | CHR_IDENT, +/* 076 L */ 0 | CHR_LETTER | CHR_IDENT, +/* 077 M */ 0 | CHR_LETTER | CHR_IDENT, +/* 078 N */ 0 | CHR_LETTER | CHR_IDENT, +/* 079 O */ 0 | CHR_LETTER | CHR_IDENT, +/* 080 P */ 0 | CHR_LETTER | CHR_IDENT, +/* 081 Q */ 0 | CHR_LETTER | CHR_IDENT, +/* 082 R */ 0 | CHR_LETTER | CHR_IDENT, +/* 083 S */ 0 | CHR_LETTER | CHR_IDENT, +/* 084 T */ 0 | CHR_LETTER | CHR_IDENT, +/* 085 U */ 0 | CHR_LETTER | CHR_IDENT, +/* 086 V */ 0 | CHR_LETTER | CHR_IDENT, +/* 087 W */ 0 | CHR_LETTER | CHR_IDENT, +/* 088 X */ 0 | CHR_LETTER | CHR_IDENT, +/* 089 Y */ 0 | CHR_LETTER | CHR_IDENT, +/* 090 Z */ 0 | CHR_LETTER | CHR_IDENT, +/* 091 [ */ 0, +/* 092 \ */ 0, +/* 093 ] */ 0, +/* 094 ^ */ 0, +/* 095 _ */ 0 | CHR_IDENT | CHR_INTRODUCER, +/* 096 ` */ 0, +/* 097 a */ 0 | CHR_LETTER | CHR_IDENT | CHR_HEX, +/* 098 b */ 0 | CHR_LETTER | CHR_IDENT | CHR_HEX, +/* 099 c */ 0 | CHR_LETTER | CHR_IDENT | CHR_HEX, +/* 100 d */ 0 | CHR_LETTER | CHR_IDENT | CHR_HEX, +/* 101 e */ 0 | CHR_LETTER | CHR_IDENT | CHR_HEX, +/* 102 f */ 0 | CHR_LETTER | CHR_IDENT | CHR_HEX, +/* 103 g */ 0 | CHR_LETTER | CHR_IDENT, +/* 104 h */ 0 | CHR_LETTER | CHR_IDENT, +/* 105 i */ 0 | CHR_LETTER | CHR_IDENT, +/* 106 j */ 0 | CHR_LETTER | CHR_IDENT, +/* 107 k */ 0 | CHR_LETTER | CHR_IDENT, +/* 108 l */ 0 | CHR_LETTER | CHR_IDENT, +/* 109 m */ 0 | CHR_LETTER | CHR_IDENT, +/* 110 n */ 0 | CHR_LETTER | CHR_IDENT, +/* 111 o */ 0 | CHR_LETTER | CHR_IDENT, +/* 112 p */ 0 | CHR_LETTER | CHR_IDENT, +/* 113 q */ 0 | CHR_LETTER | CHR_IDENT, +/* 114 r */ 0 | CHR_LETTER | CHR_IDENT, +/* 115 s */ 0 | CHR_LETTER | CHR_IDENT, +/* 116 t */ 0 | CHR_LETTER | CHR_IDENT, +/* 117 u */ 0 | CHR_LETTER | CHR_IDENT, +/* 118 v */ 0 | CHR_LETTER | CHR_IDENT, +/* 119 w */ 0 | CHR_LETTER | CHR_IDENT, +/* 120 x */ 0 | CHR_LETTER | CHR_IDENT, +/* 121 y */ 0 | CHR_LETTER | CHR_IDENT, +/* 122 z */ 0 | CHR_LETTER | CHR_IDENT, +/* 123 { */ 0 | CHR_LETTER | CHR_IDENT, +/* 124 | */ 0, +/* 125 } */ 0 | CHR_LETTER | CHR_IDENT, +/* 126 ~ */ 0, +/* 127 */ 0, +/* 128 */ 0, +/* 129 */ 0, +/* 130 */ 0, +/* 131 */ 0, +/* 132 */ 0, +/* 133 */ 0, +/* 134 */ 0, +/* 135 */ 0, +/* 136 */ 0, +/* 137 */ 0, +/* 138 */ 0, +/* 139 */ 0, +/* 140 */ 0, +/* 141 */ 0, +/* 142 */ 0, +/* 143 */ 0, +/* 144 */ 0, +/* 145 */ 0, +/* 146 */ 0, +/* 147 */ 0, +/* 148 */ 0, +/* 149 */ 0, +/* 150 */ 0, +/* 151 */ 0, +/* 152 */ 0, +/* 153 */ 0, +/* 154 */ 0, +/* 155 */ 0, +/* 156 */ 0, +/* 157 */ 0, +/* 158 */ 0, +/* 159 */ 0, +/* 160 */ 0, +/* 161 */ 0, +/* 162 */ 0, +/* 163 */ 0, +/* 164 */ 0, +/* 165 */ 0, +/* 166 */ 0, +/* 167 */ 0, +/* 168 */ 0, +/* 169 */ 0, +/* 170 */ 0, +/* 171 */ 0, +/* 172 */ 0, +/* 173 */ 0, +/* 174 */ 0, +/* 175 */ 0, +/* 176 */ 0, +/* 177 */ 0, +/* 178 */ 0, +/* 179 */ 0, +/* 180 */ 0, +/* 181 */ 0, +/* 182 */ 0, +/* 183 */ 0, +/* 184 */ 0, +/* 185 */ 0, +/* 186 */ 0, +/* 187 */ 0, +/* 188 */ 0, +/* 189 */ 0, +/* 190 */ 0, +/* 191 */ 0, +/* 192 */ 0, +/* 193 */ 0, +/* 194 */ 0, +/* 195 */ 0, +/* 196 */ 0, +/* 197 */ 0, +/* 198 */ 0, +/* 199 */ 0, +/* 200 */ 0, +/* 201 */ 0, +/* 202 */ 0, +/* 203 */ 0, +/* 204 */ 0, +/* 205 */ 0, +/* 206 */ 0, +/* 207 */ 0, +/* 208 */ 0, +/* 209 */ 0, +/* 210 */ 0, +/* 211 */ 0, +/* 212 */ 0, +/* 213 */ 0, +/* 214 */ 0, +/* 215 */ 0, +/* 216 */ 0, +/* 217 */ 0, +/* 218 */ 0, +/* 219 */ 0, +/* 220 */ 0, +/* 221 */ 0, +/* 222 */ 0, +/* 223 */ 0, +/* 224 */ 0, +/* 225 */ 0, +/* 226 */ 0, +/* 227 */ 0, +/* 228 */ 0, +/* 229 */ 0, +/* 230 */ 0, +/* 231 */ 0, +/* 232 */ 0, +/* 233 */ 0, +/* 234 */ 0, +/* 235 */ 0, +/* 236 */ 0, +/* 237 */ 0, +/* 238 */ 0, +/* 239 */ 0, +/* 240 */ 0, +/* 241 */ 0, +/* 242 */ 0, +/* 243 */ 0, +/* 244 */ 0, +/* 245 */ 0, +/* 246 */ 0, +/* 247 */ 0, +/* 248 */ 0, +/* 249 */ 0, +/* 250 */ 0, +/* 251 */ 0, +/* 252 */ 0, +/* 253 */ 0, +/* 254 */ 0, +/* 255 */ 0 +}; diff --git a/src/dsql/ddl.cpp b/src/dsql/ddl.cpp new file mode 100644 index 0000000000..baaf46cd9f --- /dev/null +++ b/src/dsql/ddl.cpp @@ -0,0 +1,5526 @@ +/* + * PROGRAM: Dynamic SQL runtime support + * MODULE: ddl.c + * DESCRIPTION: Utilities for generating ddl + * + * 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): ______________________________________. + * + * $Id: ddl.cpp,v 1.1.1.1 2001-05-23 13:25:35 tamlin Exp $ + * 2001.5.20 Claudio Valderrama: Stop null pointer that leads to a crash, + * caused by incomplete yacc syntax that allows ALTER DOMAIN dom SET; + */ + +#include "../jrd/ib_stdio.h" +#include +#include "../dsql/dsql.h" +#include "../dsql/node.h" +#include "../include/jrd/gds.h" +#include "../jrd/thd.h" +#include "../jrd/intl.h" +#include "../jrd/flags.h" +#include "../jrd/constants.h" +#include "../jrd/codes.h" +#include "../dsql/alld_proto.h" +#include "../dsql/errd_proto.h" +#include "../dsql/ddl_proto.h" +#include "../dsql/gen_proto.h" +#include "../dsql/make_proto.h" +#include "../dsql/metd_proto.h" +#include "../dsql/pass1_proto.h" +#include "../jrd/sch_proto.h" +#include "../jrd/thd_proto.h" + +#if defined(DEBUG) && !(defined REQUESTER && defined SUPERCLIENT) +#include "../gpre/prett_proto.h" +#endif + + +extern "C" { + + +#define BLOB_BUFFER_SIZE 4096 /* to read in blr blob for default values */ + +static void begin_blr(REQ, UCHAR); +static USHORT check_array_or_blob(NOD); +static void check_constraint(REQ, NOD, SSHORT); +static void create_view_triggers(REQ, NOD, NOD); +static void define_computed(REQ, NOD, FLD, NOD); +static void define_constraint_trigger(REQ, NOD); +static void define_database(REQ); +static void define_del_cascade_trg(REQ, NOD, NOD, NOD, TEXT *, TEXT *); +static void define_del_default_trg(REQ, NOD, NOD, NOD, TEXT *, TEXT *); +static void define_dimensions(REQ, FLD); +static void define_domain(REQ); +static void define_exception(REQ, NOD_TYPE); +static void define_field(REQ, NOD, SSHORT, STR); +static void define_filter(REQ); +static void define_generator(REQ); +static void define_role(REQ); +static void define_index(REQ); +static NOD define_insert_action(REQ); +static void define_procedure(REQ, NOD_TYPE); +static void define_rel_constraint(REQ, NOD); +static void define_relation(REQ); +static void define_set_null_trg(REQ, NOD, NOD, NOD, TEXT *, TEXT *, BOOLEAN); +static void define_shadow(REQ); +static void define_trigger(REQ, NOD); +static void define_udf(REQ); +static void define_update_action(REQ, NOD *, NOD *); +static void define_upd_cascade_trg(REQ, NOD, NOD, NOD, TEXT *, TEXT *); +static void define_view(REQ); +static void define_view_trigger(REQ, NOD, NOD, NOD); +static void end_blr(REQ); +static void foreign_key(REQ, NOD); +static void generate_dyn(REQ, NOD); +static void grant_revoke(REQ); +static void make_index(REQ, NOD, NOD, NOD, SCHAR *); +static void make_index_trg_ref_int(REQ, NOD, NOD, NOD, SCHAR *); +static void modify_database(REQ); +static void modify_domain(REQ); +static void modify_field(REQ, NOD, SSHORT, STR); +static void modify_index(REQ); +static void modify_privilege(REQ, NOD_TYPE, SSHORT, UCHAR *, NOD, NOD, STR); +static void process_role_nm_list(REQ, SSHORT, NOD, NOD, NOD_TYPE); +static SCHAR modify_privileges(REQ, NOD_TYPE, SSHORT, NOD, NOD, NOD); +static void modify_relation(REQ); +static void put_cstring(REQ, UCHAR, char *); +static void put_descriptor(REQ, DSC *); +static void put_dtype(REQ, FLD, USHORT); +static void put_field(REQ, FLD, BOOLEAN); +static void put_local_variable(REQ, VAR); +static SSHORT put_local_variables(REQ, NOD, SSHORT); +static void put_msg_field(REQ, FLD); +static void put_number(REQ, UCHAR, SSHORT); +static void put_string(REQ, UCHAR, UCHAR *, USHORT); +static NOD replace_field_names(NOD, NOD, NOD, SSHORT); +static void reset_context_stack(REQ); +static void save_field(REQ, SCHAR *); +static void save_relation(REQ, STR); +static void set_statistics(REQ); +static void stuff_default_blr(REQ, TEXT *, USHORT); +static void stuff_matching_blr(REQ, NOD, NOD); +static void stuff_trg_firing_cond(REQ, NOD); +static void set_nod_value_attributes(NOD, FLD); + +#ifdef BLKCHK +#undef BLKCHK +#endif + +#ifdef DEV_BUILD +#define BLKCHK(blk, typ) \ + { \ + if ((blk) && (((BLK) (blk))->blk_type != (typ))) \ + ERRD_bugcheck ("Invalid block type"); /* NTX: dev build */ \ + } +#else +#define BLKCHK(blk, typ) +#endif + + +/* STUFF is defined in dsql.h for use in common with gen.c */ + +#define STUFF_WORD(n) {STUFF (n); STUFF ((n) >> 8);} +#define STUFF_DWORD(n) {STUFF (n); STUFF ((n) >> 8);\ + STUFF ((n) >> 16); STUFF ((n) >> 24);} + +#define PRE_STORE_TRIGGER 1 +#define POST_STORE_TRIGGER 2 +#define PRE_MODIFY_TRIGGER 3 +#define POST_MODIFY_TRIGGER 4 +#define PRE_ERASE_TRIGGER 5 +#define POST_ERASE_TRIGGER 6 + +#define OLD_CONTEXT "OLD" +#define NEW_CONTEXT "NEW" +#define TEMP_CONTEXT "TEMP" + +#define DEFAULT_BUFFER 2048 + +#define DEFAULT_BLOB_SEGMENT_SIZE 80 /* bytes */ + + +static CONST USHORT blr_dtypes[] = { + 0, + blr_text, /* dtype_text */ + blr_cstring, /* dtype_cstring */ + blr_varying, /* dtype_varying */ + 0, + 0, + 0, /* dtype_packed */ + 0, /* dtype_byte */ + blr_short, /* dtype_short */ + blr_long, /* dtype_long */ + blr_quad, /* dtype_quad */ + blr_float, /* dtype_real */ + blr_double, /* dtype_double */ + blr_double, /* dtype_d_float */ + blr_sql_date, /* dtype_sql_date */ + blr_sql_time, /* dtype_sql_time */ + blr_timestamp, /* dtype_timestamp */ + blr_blob, /* dtype_blob */ + blr_short, /* dtype_array */ + blr_int64 /* dtype_int64 */ +}; + +static CONST UCHAR nonnull_validation_blr[] = { + blr_version5, + blr_not, + blr_missing, + blr_fid, 0, 0, 0, + blr_eoc +}; + +ASSERT_FILENAME void DDL_execute( REQ request) +{ +/************************************** + * + * D D L _ e x e c u t e + * + ************************************** + * + * Functional description + * Call access method layered service DYN + * to interpret dyn string and perform + * metadata updates. + * + **************************************/ + USHORT length; + STR string; + STATUS s; + NOD relation_node; + TSQL tdsql; + + tdsql = GET_THREAD_DATA; + +#ifdef DEBUG +#if !(defined REQUESTER && defined SUPERCLIENT) + if (DSQL_debug > 0) + PRETTY_print_dyn(reinterpret_cast < + char *>(request->req_blr_string->str_data), NULL, + "%4d %s\n", NULL); +#endif +#endif + + length = request->req_blr - request->req_blr_string->str_data; + + THREAD_EXIT; + + s = isc_ddl(GDS_VAL(tdsql->tsql_status), + reinterpret_cast < + void **>(GDS_REF(request->req_dbb->dbb_database_handle)), + reinterpret_cast < void **>(GDS_REF(request->req_trans)), + length, + reinterpret_cast < + char *>(GDS_VAL(request->req_blr_string->str_data))); + + THREAD_ENTER; + +/* for delete & modify, get rid of the cached relation metadata */ + + if ((request->req_ddl_node->nod_type == nod_mod_relation) || + (request->req_ddl_node->nod_type == nod_del_relation)) { + if (request->req_ddl_node->nod_type == nod_mod_relation) { + relation_node = request->req_ddl_node->nod_arg[e_alt_name]; + string = (STR) relation_node->nod_arg[e_rln_name]; + } + else + string = (STR) request->req_ddl_node->nod_arg[e_alt_name]; + METD_drop_relation(request, string); + } + +/* for delete & modify, get rid of the cached procedure metadata */ + + if ((request->req_ddl_node->nod_type == nod_mod_procedure) || + (request->req_ddl_node->nod_type == nod_del_procedure)) { + string = (STR) request->req_ddl_node->nod_arg[e_prc_name]; + METD_drop_procedure(request, string); + } + + if (s) + LONGJMP(*tdsql->tsql_setjmp, (int) tdsql->tsql_status[1]); +} + + +void DDL_generate( REQ request, NOD node) +{ +/************************************** + * + * D D L _ g e n e r a t e + * + ************************************** + * + * Functional description + * Generate the DYN string for a + * metadata update. Done during the + * prepare phase. + * + **************************************/ + +#ifdef READONLY_DATABASE + if (request->req_dbb->dbb_flags & DBB_read_only) { + ERRD_post(isc_read_only_database, 0); + return; + } +#endif /* READONLY_DATABASE */ + + STUFF(gds_dyn_version_1); + generate_dyn(request, node); + STUFF(gds_dyn_eoc); +} + + +int DDL_ids( REQ request) +{ +/************************************** + * + * D D L _ i d s + * + ************************************** + * + * Functional description + * Determine whether ids or names should be + * referenced when generating blr for fields + * and relations. + * + **************************************/ + NOD ddl_node; + + if (!(ddl_node = request->req_ddl_node)) + return TRUE; + + if (ddl_node->nod_type == nod_def_view || + ddl_node->nod_type == nod_def_constraint || + ddl_node->nod_type == nod_def_trigger || + ddl_node->nod_type == nod_mod_trigger || + ddl_node->nod_type == nod_def_procedure || + ddl_node->nod_type == nod_def_computed || + ddl_node->nod_type == nod_mod_procedure) return FALSE; + + return TRUE; +} + + +void DDL_put_field_dtype( REQ request, FLD field, USHORT use_subtype) +{ +/************************************** + * + * D D L _ p u t _ f i e l d _ d t y p e + * + ************************************** + * + * Functional description + * Emit blr that describes a descriptor. + * Note that this depends on the same STUFF variant + * as used in gen.c + * + **************************************/ + + put_dtype(request, field, use_subtype); +} + + +void DDL_resolve_intl_type( REQ request, FLD field, STR collation_name) +{ +/************************************** + * + * D D L _ r e s o l v e _ i n t l _ t y p e + * + ************************************** + * + * Function + * If the field is defined with a character set or collation, + * resolve the names to a subtype now. + * + * Also resolve the field length & whatnot. + * + * For International text fields, this is a good time to calculate + * their actual size - when declared they were declared in + * lengths of CHARACTERs, not BYTES. + * + **************************************/ + UCHAR *charset_name; + INTLSYM resolved_type; + INTLSYM resolved_charset; + INTLSYM resolved_collation; + STR dfl_charset; + SSHORT blob_sub_type; + ULONG field_length; + + if ((field->fld_dtype > dtype_any_text) && field->fld_dtype != dtype_blob) { + if (field->fld_character_set || collation_name || + field->fld_flags & FLD_national) + ERRD_post(gds__sqlerr, gds_arg_number, (SLONG) - 204, + gds_arg_gds, gds__dsql_datatype_err, + gds_arg_gds, gds__collation_requires_text, 0); + + return; + } + + if (field->fld_dtype == dtype_blob) + { + if (field->fld_sub_type_name) + { + if (!METD_get_type(request, + reinterpret_cast(field->fld_sub_type_name), + reinterpret_cast < UCHAR * >("RDB$FIELD_SUB_TYPE"), + &blob_sub_type)) + { + ERRD_post( gds__sqlerr, + gds_arg_number, + (SLONG) - 204, + gds_arg_gds, + gds__dsql_datatype_err, + gds_arg_gds, + gds__dsql_blob_type_unknown, + gds_arg_string, + ((STR) field->fld_sub_type_name)->str_data, + 0); + } + field->fld_sub_type = blob_sub_type; + } + if (field->fld_character_set && (field->fld_sub_type == BLOB_untyped)) + { + field->fld_sub_type = BLOB_text; + } + if (field->fld_character_set && (field->fld_sub_type != BLOB_text)) + { + ERRD_post( gds__sqlerr, + gds_arg_number, + (SLONG) - 204, + gds_arg_gds, + gds__dsql_datatype_err, + gds_arg_gds, + gds__collation_requires_text, + 0); + } + if (collation_name) + { + ERRD_post( gds__sqlerr, + gds_arg_number, + (SLONG) - 204, + gds_arg_gds, + gds__dsql_datatype_err, + gds_arg_gds, + gds__collation_requires_text, + 0); + } + if (field->fld_sub_type != BLOB_text) { + return; + } + } + + if (field->fld_character_set_id != 0 && !collation_name) { + /* This field has already been resolved once, and the collation hasn't + * changed. Therefore, no need to do it again. + */ + return; + } + + if (!(field->fld_character_set || field->fld_character_set_id || /* set if a domain */ + (field->fld_flags & FLD_national))) { + + /* Attach the database default character set, if not otherwise specified */ + + if (dfl_charset = METD_get_default_charset(request)) { + field->fld_character_set = (NOD) dfl_charset; + } + else { + /* If field is not specified with NATIONAL, or CHARACTER SET + * treat it as a single-byte-per-character field of character set NONE. + */ + if (field->fld_character_length) { + field_length = field->fld_character_length; + if (field->fld_dtype == dtype_varying) + field_length += sizeof(USHORT); + if (field_length > (ULONG) MAX_COLUMN_SIZE) + ERRD_post(gds__sqlerr, gds_arg_number, (SLONG) - 204, + gds_arg_gds, gds__dsql_datatype_err, + gds_arg_gds, gds__imp_exc, + gds_arg_gds, gds__field_name, gds_arg_string, + field->fld_name, 0); + field->fld_length = (USHORT) field_length; + }; + field->fld_ttype = 0; + if (!collation_name) + return; + } + } + + if (field->fld_flags & FLD_national) + charset_name = (UCHAR *) NATIONAL_CHARACTER_SET; + else if (field->fld_character_set) + charset_name = (UCHAR *) ((STR) field->fld_character_set)->str_data; + else + charset_name = (UCHAR *) NULL; + + +/* Find an intlsym for any specified character set name & collation name */ + + resolved_charset = NULL; + if (charset_name) { + resolved_charset = + METD_get_charset(request, + (USHORT) strlen(reinterpret_cast < + char *>(charset_name)), + charset_name); + + /* Error code -204 (IBM's DB2 manual) is close enough */ + if (!resolved_charset) + /* specified character set not found */ + ERRD_post(gds__sqlerr, gds_arg_number, (SLONG) - 204, + gds_arg_gds, gds__dsql_datatype_err, + gds_arg_gds, gds__charset_not_found, gds_arg_string, + charset_name, 0); + field->fld_character_set_id = resolved_charset->intlsym_charset_id; + resolved_type = resolved_charset; + } + + resolved_collation = NULL; + if (collation_name) { + resolved_collation = METD_get_collation(request, collation_name); + if (!resolved_collation) + /* Specified collation not found */ + ERRD_post(gds__sqlerr, gds_arg_number, (SLONG) - 204, + gds_arg_gds, gds__dsql_datatype_err, + gds_arg_gds, gds__collation_not_found, gds_arg_string, + collation_name->str_data, 0); + + /* If both specified, must be for same character set */ + /* A "literal constant" must be handled (charset as ttype_dynamic) */ + + resolved_type = resolved_collation; + if ((field->fld_character_set_id != resolved_type->intlsym_charset_id) + && (field->fld_character_set_id != ttype_dynamic)) + ERRD_post(gds__sqlerr, gds_arg_number, (SLONG) - 204, gds_arg_gds, + gds__dsql_datatype_err, gds_arg_gds, + gds__collation_not_for_charset, gds_arg_string, + collation_name->str_data, 0); + } + + + if (field->fld_character_length) { + field_length = (ULONG) resolved_type->intlsym_bytes_per_char * + field->fld_character_length; + + if (field->fld_dtype == dtype_varying) + field_length += sizeof(USHORT); + if (field_length > (ULONG) MAX_COLUMN_SIZE) + ERRD_post(gds__sqlerr, gds_arg_number, (SLONG) - 204, + gds_arg_gds, gds__dsql_datatype_err, + gds_arg_gds, gds__imp_exc, + gds_arg_gds, gds__field_name, gds_arg_string, + field->fld_name, 0); + field->fld_length = (USHORT) field_length; + }; + + field->fld_ttype = resolved_type->intlsym_ttype; + field->fld_character_set_id = resolved_type->intlsym_charset_id; + field->fld_collation_id = resolved_type->intlsym_collate_id; +} + + +static void begin_blr( REQ request, UCHAR verb) +{ +/************************************** + * + * b e g i n _ b l r + * + ************************************** + * + * Function + * Write out a string of blr as part of a ddl string, + * as in a view or computed field definition. + * + **************************************/ + + if (verb) + STUFF(verb); + + request->req_base_offset = + request->req_blr - request->req_blr_string->str_data; + +/* put in a place marker for the size of the blr, since it is unknown */ + + STUFF_WORD(0); + if (request->req_flags & REQ_blr_version4) + STUFF(blr_version4); + else + STUFF(blr_version5); +} + + +static USHORT check_array_or_blob( NOD node) +{ +/************************************** + * + * c h e c k _ a r r a y _ o r _ b l o b + * + ************************************** + * + * Functional description + * return TRUE if there is an array or blob in expression, else FALSE. + * Array and blob expressions have limited usefullness in a computed + * expression - so we detect it here to report a syntax error at + * definition time, rather than a runtime error at execution. + * + **************************************/ + MAP map; + UDF udf; + FLD fld; + NOD *ptr, *end; + + BLKCHK(node, type_nod); + + switch (node->nod_type) { + case nod_agg_count: + case nod_count: + case nod_gen_id: + case nod_gen_id2: + case nod_dbkey: + case nod_current_date: + case nod_current_time: + case nod_current_timestamp: + case nod_constant: + case nod_via: + return FALSE; + + case nod_map: + map = (MAP) node->nod_arg[e_map_map]; + return check_array_or_blob(map->map_node); + + case nod_agg_max: + case nod_agg_min: + case nod_agg_average: + case nod_agg_total: + case nod_agg_average2: + case nod_agg_total2: + case nod_upcase: + case nod_negate: + return check_array_or_blob(node->nod_arg[0]); + + case nod_cast: + fld = (FLD) node->nod_arg[e_cast_target]; + if ((fld->fld_dtype == dtype_blob) || (fld->fld_dtype == dtype_array)) + return TRUE; + + return check_array_or_blob(node->nod_arg[e_cast_source]); + + case nod_add: + case nod_subtract: + case nod_concatenate: + case nod_multiply: + case nod_divide: + case nod_add2: + case nod_subtract2: + case nod_multiply2: + case nod_divide2: + + if (check_array_or_blob(node->nod_arg[0])) + return TRUE; + return check_array_or_blob(node->nod_arg[1]); + + case nod_alias: + return check_array_or_blob(node->nod_arg[e_alias_value]); + + case nod_udf: + udf = (UDF) node->nod_arg[0]; + if ((udf->udf_dtype == dtype_blob) || (udf->udf_dtype == dtype_array)) + return TRUE; + + /* parameters to UDF don't need checking, an blob or array can be passed */ + return FALSE; + + case nod_extract: + case nod_list: + for (ptr = node->nod_arg, end = ptr + node->nod_count; + ptr < end; ptr++) + if (check_array_or_blob(*ptr)) + return TRUE; + + return FALSE; + + case nod_field: + if ((node->nod_desc.dsc_dtype == dtype_blob) || + (node->nod_desc.dsc_dtype == dtype_array)) + return TRUE; + + return FALSE; + + default: + assert(FALSE); + return FALSE; + } +} + + +static void check_constraint( + REQ request, + NOD element, SSHORT delete_trigger_required) +{ +/* ************************************* + * + * c h e c k _ c o n s t r a i n t + * + ************************************** + * + * Function + * Generate triggers to implement the CHECK + * clause, either at the field or table level. + * + **************************************/ + NOD ddl_node, list_node; + NOD *errorcode_node; + + ddl_node = request->req_ddl_node; + if (!(element->nod_arg[e_cnstr_table])) + element->nod_arg[e_cnstr_table] = ddl_node->nod_arg[e_drl_name]; + +/* specify that the trigger should abort if the condition is not met */ + + element->nod_arg[e_cnstr_actions] = list_node = + MAKE_node(nod_list, (int) 1); + list_node->nod_arg[0] = MAKE_node(nod_gdscode, (int) 1); + errorcode_node = &list_node->nod_arg[0]->nod_arg[0]; + *errorcode_node = (NOD) MAKE_cstring("check_constraint"); + element->nod_arg[e_cnstr_message] = NULL; + +/* create the INSERT trigger */ + +/* element->nod_arg [e_cnstr_message] = + * (NOD) MAKE_cstring ("insert violates CHECK constraint on table"); + */ + element->nod_arg[e_cnstr_type] = + MAKE_constant((STR) PRE_STORE_TRIGGER, 1); + define_constraint_trigger(request, element); + +/* create the UPDATE trigger */ + +/* element->nod_arg [e_cnstr_message] = + * (NOD) MAKE_cstring ("update violates CHECK constraint on table"); + */ + element->nod_arg[e_cnstr_type] = + MAKE_constant((STR) PRE_MODIFY_TRIGGER, 1); + define_constraint_trigger(request, element); + +/* create the DELETE trigger, if required */ + + if (delete_trigger_required) { +/* + * element->nod_arg [e_cnstr_message] = + * (NOD) MAKE_cstring ("delete violates CHECK constraint on table"); + */ + element->nod_arg[e_cnstr_type] = + MAKE_constant((STR) PRE_ERASE_TRIGGER, 1); + define_constraint_trigger(request, element); + } + + STUFF(gds_dyn_end); /* For CHECK constraint definition */ +} + + +static void create_view_triggers( REQ request, NOD element, NOD items) +{ /* Fields in the VIEW actually */ +/* ************************************* + * + * c r e a t e _ v i e w _ t r i g g e r s + * + ************************************** + * + * Function + * Generate triggers to implement the WITH CHECK OPTION + * clause for a VIEW + * + **************************************/ + NOD temp, rse, base_relation, base_and_node, ddl_node, list_node; + NOD *errorcode_node; + + ddl_node = request->req_ddl_node; + + if (!(element->nod_arg[e_cnstr_table])) + element->nod_arg[e_cnstr_table] = ddl_node->nod_arg[e_drl_name]; + +/* specify that the trigger should abort if the condition is not met */ + + element->nod_arg[e_cnstr_actions] = list_node = + MAKE_node(nod_list, (int) 1); + list_node->nod_arg[0] = MAKE_node(nod_gdscode, (int) 1); + errorcode_node = &list_node->nod_arg[0]->nod_arg[0]; + *errorcode_node = (NOD) MAKE_cstring("check_constraint"); + element->nod_arg[e_cnstr_message] = NULL; + +/* create the UPDATE trigger */ + +/* element->nod_arg [e_cnstr_message] = + * (NOD) MAKE_cstring ("update violates CHECK constraint on view"); + */ + element->nod_arg[e_cnstr_type] = + MAKE_constant((STR) PRE_MODIFY_TRIGGER, 1); + define_update_action(request, &base_and_node, &base_relation); + + rse = MAKE_node(nod_rse, e_rse_count); + rse->nod_arg[e_rse_boolean] = base_and_node; + rse->nod_arg[e_rse_streams] = temp = MAKE_node(nod_list, 1); + temp->nod_arg[0] = base_relation; + define_view_trigger(request, element, rse, items); + +/* create the INSERT trigger */ + +/* element->nod_arg [e_cnstr_message] = + * (NOD) MAKE_cstring ("insert violates CHECK constraint on view"); + */ + element->nod_arg[e_cnstr_type] = + MAKE_constant((STR) PRE_STORE_TRIGGER, 1); + define_view_trigger(request, element, NULL, items); + + STUFF(gds_dyn_end); /* For triggers definition */ +} + + +static void define_computed( + REQ request, + NOD relation_node, FLD field, NOD node) +{ +/************************************** + * + * d e f i n e _ c o m p u t e d + * + ************************************** + * + * Function + * Create the ddl to define a computed field + * or an expression index. + * + **************************************/ + NOD input, ddl_node; + STR source; + DSC save_desc, desc; + + ddl_node = request->req_ddl_node; + request->req_ddl_node = node; + +/* Get the table node & set up correct context */ + + if (request->req_context_number) + reset_context_stack(request); + +/* Save the size of the field if it is specified */ + + save_desc.dsc_dtype = 0; + + if (field && field->fld_dtype) { + assert(field->fld_dtype <= MAX_UCHAR); + save_desc.dsc_dtype = (UCHAR) field->fld_dtype; + save_desc.dsc_length = field->fld_length; + assert(field->fld_scale <= MAX_SCHAR); + save_desc.dsc_scale = (SCHAR) field->fld_scale; + + field->fld_dtype = 0; + field->fld_length = 0; + field->fld_scale = 0; + } + + PASS1_make_context(request, relation_node); + + input = PASS1_node(request, node->nod_arg[e_cmp_expr], 0); + +/* check if array or blobs are used in expression */ + + if (check_array_or_blob(input)) + ERRD_post(gds__sqlerr, gds_arg_number, (SLONG) - 607, + gds_arg_gds, gds__dsql_no_blob_array, 0); + +/* generate the blr expression */ + + STUFF(gds_dyn_fld_computed_blr); + begin_blr(request, 0); + GEN_expr(request, input); + end_blr(request); + +/* try to calculate size of the computed field. The calculated size + may be ignored, but it will catch self references */ + + MAKE_desc(&desc, input); + + if (save_desc.dsc_dtype) { + /* restore the field size/type overrides */ + + field->fld_dtype = save_desc.dsc_dtype; + field->fld_length = save_desc.dsc_length; + field->fld_scale = save_desc.dsc_scale; + } + else if (field) { + /* use size calculated */ + + field->fld_dtype = desc.dsc_dtype; + field->fld_length = desc.dsc_length; + field->fld_scale = desc.dsc_scale; + } + + request->req_type = REQ_DDL; + request->req_ddl_node = ddl_node; + reset_context_stack(request); + +/* generate the source text */ + + source = (STR) node->nod_arg[e_cmp_text]; + assert(source->str_length <= MAX_USHORT); + put_string(request, gds_dyn_fld_computed_source, source->str_data, + (USHORT) source->str_length); +} + + +static void define_constraint_trigger(REQ request, NOD node) +{ +/************************************** + * + * d e f i n e _ c o n s t r a i n t _ t r i g g e r + * + ************************************** + * + * Function + * Create the ddl to define or alter a constraint trigger. + * + **************************************/ + NOD actions, *ptr, *end, constant; + +/* make the "define trigger" node the current request ddl node so + that generating of BLR will be appropriate for trigger */ + + NOD ddl_node = request->req_ddl_node; + + request->req_ddl_node = node; + + if (node->nod_type != nod_def_constraint) + { + return; + } + + STR trigger_name = (STR) node->nod_arg[e_cnstr_name]; + + assert(trigger_name->str_length <= MAX_USHORT); + + put_string(request, + gds_dyn_def_trigger, + trigger_name->str_data, + (USHORT) trigger_name->str_length); + + NOD relation_node = node->nod_arg[e_cnstr_table]; + STR relation_name = (STR) relation_node->nod_arg[e_rln_name]; + + assert(trigger_name->str_length <= MAX_USHORT); + + put_string(request, + gds_dyn_rel_name, + relation_name->str_data, + (USHORT) relation_name->str_length); + + STR source = (STR) node->nod_arg[e_cnstr_source]; + if (source) + { + assert(source->str_length <= MAX_USHORT); + put_string(request, gds_dyn_trg_source, source->str_data, + (USHORT) source->str_length); + } + + if ((constant = node->nod_arg[e_cnstr_position]) != NULL) + { + put_number(request, gds_dyn_trg_sequence, + (SSHORT) (constant ? constant->nod_arg[0] : 0)); + } + + if ((constant = node->nod_arg[e_cnstr_type]) != NULL) + { + const SSHORT type = (SSHORT) constant->nod_arg[0]; + put_number(request, gds_dyn_trg_type, type); + } + + STUFF(gds_dyn_sql_object); + + STR message = (STR) node->nod_arg[e_cnstr_message]; + if (message) + { + put_number(request, gds_dyn_def_trigger_msg, 0); + assert(message->str_length <= MAX_USHORT); + put_string(request, gds_dyn_trg_msg, message->str_data, + (USHORT) message->str_length); + STUFF(gds_dyn_end); + } + +/* generate the trigger blr */ + + if (node->nod_arg[e_cnstr_condition] && node->nod_arg[e_cnstr_actions]) + { + begin_blr(request, gds_dyn_trg_blr); + STUFF(blr_begin); + + /* create the "OLD" and "NEW" contexts for the trigger -- + the new one could be a dummy place holder to avoid resolving + fields to that context but prevent relations referenced in + the trigger actions from referencing the predefined "1" context */ + + if (request->req_context_number) + { + reset_context_stack(request); + } + relation_node->nod_arg[e_rln_alias] = (NOD) MAKE_cstring(OLD_CONTEXT); + PASS1_make_context(request, relation_node); + relation_node->nod_arg[e_rln_alias] = (NOD) MAKE_cstring(NEW_CONTEXT); + PASS1_make_context(request, relation_node); + + /* generate the condition for firing the trigger */ + + STUFF(blr_if); + GEN_expr(request, + PASS1_node(request, node->nod_arg[e_cnstr_condition], 0)); + + STUFF(blr_begin); + STUFF(blr_end); /* of begin */ + + /* generate the action statements for the trigger */ + actions = node->nod_arg[e_cnstr_actions]; + for (ptr = actions->nod_arg, end = ptr + actions->nod_count; + ptr < end; ptr++) + { + GEN_statement(request, PASS1_statement(request, *ptr, 0)); + } + + /* generate the action statements for the trigger */ + + if ((actions = node->nod_arg[e_cnstr_else]) != NULL) + { + STUFF(blr_begin); + for (ptr = actions->nod_arg, end = ptr + actions->nod_count; + ptr < end; ptr++) + { + GEN_statement(request, PASS1_statement(request, *ptr, 0)); + } + STUFF(blr_end); /* of begin */ + } + else + { + STUFF(blr_end); /* of if */ + } + + end_blr(request); + } + + STUFF(gds_dyn_end); + +/* the request type may have been set incorrectly when parsing + the trigger actions, so reset it to reflect the fact that this + is a data definition request; also reset the ddl node */ + + request->req_type = REQ_DDL; + request->req_ddl_node = ddl_node; + reset_context_stack(request); +} + + +static void define_database( REQ request) +{ +/************************************** + * + * d e f i n e _ d a t a b a s e + * + ************************************** + * + * Function + * Create a database. Assumes that + * database is created elsewhere with + * initial options. Modify the + * database using DYN to add the remaining + * options. + * + **************************************/ + NOD ddl_node, elements, element, *ptr, *end; + STR name; + SLONG start = 0; + FIL file; + SSHORT number = 0; + SLONG temp_long; + SSHORT temp_short; + + ddl_node = request->req_ddl_node; + + STUFF(gds_dyn_mod_database); +/* +put_number (request, gds_dyn_rel_sql_protection, 1); +*/ + + elements = ddl_node->nod_arg[e_database_initial_desc]; + + if (elements) + for (ptr = elements->nod_arg, end = ptr + elements->nod_count; + ptr < end; ptr++) { + element = *ptr; + + switch (element->nod_type) { + case nod_file_length: + start = (SLONG) (element->nod_arg[0]) + 1; + break; + + default: + break; + } + } + + elements = ddl_node->nod_arg[e_database_rem_desc]; + if (elements) + for (ptr = elements->nod_arg, end = ptr + elements->nod_count; + ptr < end; ptr++) { + element = *ptr; + + switch (element->nod_type) { + case nod_file_desc: + file = (FIL) element->nod_arg[0]; + put_cstring(request, gds_dyn_def_file, + reinterpret_cast < + char *>(file->fil_name->str_data)); + STUFF(gds_dyn_file_start); + STUFF_WORD(4); + start = MAX(start, file->fil_start); + + STUFF_DWORD(start); + + STUFF(gds_dyn_file_length); + STUFF_WORD(4); + STUFF_DWORD(file->fil_length); + STUFF(gds_dyn_end); + start += file->fil_length; + break; + + case nod_log_file_desc: + file = (FIL) element->nod_arg[0]; + + if (file->fil_flags & LOG_default) { + STUFF(gds_dyn_def_default_log); + break; + } + put_cstring(request, gds_dyn_def_log_file, + reinterpret_cast < + char *>(file->fil_name->str_data)); + STUFF(gds_dyn_file_length); + STUFF_WORD(4); + STUFF_DWORD(file->fil_length); + STUFF(gds_dyn_log_file_sequence); + STUFF_WORD(2); + STUFF_WORD(number); + number++; + STUFF(gds_dyn_log_file_partitions); + STUFF_WORD(2); + STUFF_WORD(file->fil_partitions); + if (file->fil_flags & LOG_serial) + STUFF(gds_dyn_log_file_serial); + if (file->fil_flags & LOG_overflow) + STUFF(gds_dyn_log_file_overflow); + if (file->fil_flags & LOG_raw) + STUFF(gds_dyn_log_file_raw); + STUFF(gds_dyn_end); + break; + + case nod_cache_file_desc: + file = (FIL) element->nod_arg[0]; + put_cstring(request, gds_dyn_def_cache_file, + reinterpret_cast < + char *>(file->fil_name->str_data)); + STUFF(gds_dyn_file_length); + STUFF_WORD(4); + STUFF_DWORD(file->fil_length); + STUFF(gds_dyn_end); + break; + + case nod_group_commit_wait: + STUFF(gds_dyn_log_group_commit_wait); + temp_long = (SLONG) (element->nod_arg[0]); + STUFF_WORD(4); + STUFF_DWORD(temp_long); + break; + + case nod_check_point_len: + STUFF(gds_dyn_log_check_point_length); + temp_long = (SLONG) (element->nod_arg[0]); + STUFF_WORD(4); + STUFF_DWORD(temp_long); + break; + + case nod_num_log_buffers: + STUFF(gds_dyn_log_num_of_buffers); + temp_short = (SSHORT) (element->nod_arg[0]); + STUFF_WORD(2); + STUFF_WORD(temp_short); + break; + + case nod_log_buffer_size: + STUFF(gds_dyn_log_buffer_size); + temp_short = (SSHORT) (element->nod_arg[0]); + STUFF_WORD(2); + STUFF_WORD(temp_short); + break; + + case nod_dfl_charset: + name = (STR) element->nod_arg[0]; + put_cstring(request, gds_dyn_fld_character_set_name, + reinterpret_cast(name->str_data)); + break; + + default: + break; + } + } + + STUFF(gds_dyn_end); +} + + +static void define_del_cascade_trg( + REQ request, + NOD element, + NOD for_columns, + NOD prim_columns, +TEXT * prim_rel_name, TEXT * for_rel_name) +{ +/***************************************************** + * + * d e f i n e _ d e l _ c a s c a d e _ t r g + * + ***************************************************** + * + * Function + * define "on delete cascade" trigger (for referential integrity) + * along with its blr + * + *****************************************************/ + USHORT num_fields = 0; + + if (element->nod_type != nod_foreign) + return; + +/* stuff a trigger_name of size 0. So the dyn-parser will make one up. */ + put_string(request, gds_dyn_def_trigger, reinterpret_cast < UCHAR * >(""), + (USHORT) 0); + + put_number(request, gds_dyn_trg_type, (SSHORT) POST_ERASE_TRIGGER); + + STUFF(gds_dyn_sql_object); + put_number(request, gds_dyn_trg_sequence, (SSHORT) 1); + put_number(request, gds_dyn_trg_inactive, (SSHORT) 0); + put_cstring(request, gds_dyn_rel_name, prim_rel_name); + +/* the trigger blr */ + begin_blr(request, gds_dyn_trg_blr); + STUFF(blr_for); + STUFF(blr_rse); + +/* the context for the prim. key relation */ + STUFF(1); + + STUFF(blr_relation); + put_cstring(request, 0, for_rel_name); +/* the context for the foreign key relation */ + STUFF(2); + + stuff_matching_blr(request, for_columns, prim_columns); + + STUFF(blr_erase); + STUFF((SSHORT) 2); + end_blr(request); +/* end of the blr */ + +/* no trg_source and no trg_description */ + STUFF(gds_dyn_end); + +} + + +static void define_set_default_trg( + REQ request, + NOD element, + NOD for_columns, + NOD prim_columns, +TEXT * prim_rel_name, TEXT * for_rel_name, BOOLEAN on_upd_trg) +{ +/***************************************************** + * + * d e f i n e _ s e t _ d e f a u l t _ t r g + * + ***************************************************** + * + * Function + * define "on delete|update set default" trigger (for + * referential integrity) along with its blr + * + *****************************************************/ + NOD *for_key_flds; + USHORT num_fields = 0; + STR for_key_fld_name_str; + UCHAR default_val[BLOB_BUFFER_SIZE]; + BOOLEAN found_default, search_for_default; + + NOD ddl_node, elem, *ptr, *end, default_node; + NOD domain_node, tmp_node; + FLD field; + STR domain_name_str; + TEXT *domain_name; + + if (element->nod_type != nod_foreign) + return; + +/* stuff a trigger_name of size 0. So the dyn-parser will make one up. */ + put_string(request, gds_dyn_def_trigger, reinterpret_cast < UCHAR * >(""), + (USHORT) 0); + + put_number(request, gds_dyn_trg_type, + (SSHORT) (on_upd_trg ? POST_MODIFY_TRIGGER : + POST_ERASE_TRIGGER)); + + STUFF(gds_dyn_sql_object); + put_number(request, gds_dyn_trg_sequence, (SSHORT) 1); + put_number(request, gds_dyn_trg_inactive, (SSHORT) 0); + put_cstring(request, gds_dyn_rel_name, prim_rel_name); + +/* the trigger blr */ + begin_blr(request, gds_dyn_trg_blr); + +/* for ON UPDATE TRIGGER only: generate the trigger firing condition: + if prim_key.old_value != prim_key.new value. + Note that the key could consist of multiple columns */ + + if (on_upd_trg) { + stuff_trg_firing_cond(request, prim_columns); + STUFF(blr_begin); + STUFF(blr_begin); + } + + STUFF(blr_for); + STUFF(blr_rse); + +/* the context for the prim. key relation */ + STUFF(1); + STUFF(blr_relation); + put_cstring(request, 0, for_rel_name); +/* the context for the foreign key relation */ + STUFF(2); + + stuff_matching_blr(request, for_columns, prim_columns); + + STUFF(blr_modify); + STUFF((SSHORT) 2); + STUFF((SSHORT) 2); + STUFF(blr_begin); + + num_fields = 0; + for_key_flds = for_columns->nod_arg; + + ddl_node = request->req_ddl_node; + + do { + /* for every column in the foreign key .... */ + for_key_fld_name_str = (STR) (*for_key_flds)->nod_arg[1]; + + STUFF(blr_assignment); + + /* here stuff the default value as blr_literal .... or blr_null + if this col. does not have an applicable default */ + + /* the default is determined in many cases: + (1) the info. for the column is in memory. (This is because + the column is being created in this ddl statement) + (1-a) the table has a column level default. We get this by + searching the dsql parse tree starting from the ddl node. + (1-b) the table does not have a column level default, but + has a domain default. We get the domain name from the dsql + parse tree and call METD_get_domain_default to read the + default from the system tables. + (2) The default-info for this column is not in memory (This is + because this is an alter table ddl statement). The table + already exists; therefore we get the column and/or domain + default value from the system tables by calling: + METD_get_col_default(). */ + + found_default = FALSE; + search_for_default = TRUE; + + /* search the parse tree to find the column */ + + elem = ddl_node->nod_arg[e_drl_elements]; + for (ptr = elem->nod_arg, end = ptr + elem->nod_count; ptr < end; + ptr++) { + elem = *ptr; + if (elem->nod_type != nod_def_field) + continue; + field = (FLD) elem->nod_arg[e_dfl_field]; + if (strcmp + (field->fld_name, + reinterpret_cast < + char *>(for_key_fld_name_str->str_data))) continue; + + /* Now, we have the right column in the parse tree. case (1) above */ + + if ((default_node = elem->nod_arg[e_dfl_default]) != NULL) { + /* case (1-a) above: there is a col. level default */ + GEN_expr(request, default_node); + found_default = TRUE; + search_for_default = FALSE; + } + else { + if (!(domain_node = elem->nod_arg[e_dfl_domain]) || + !(tmp_node = domain_node->nod_arg[e_dom_name]) || + !(domain_name_str = (STR) tmp_node->nod_arg[e_fln_name]) + || !(domain_name = + reinterpret_cast < + char *>(domain_name_str->str_data))) break; + + /* case: (1-b): domain name is available. Column level default + is not declared. so get the domain default */ + METD_get_domain_default(request, domain_name, &found_default, + reinterpret_cast < + char *>(default_val), + sizeof(default_val)); + + search_for_default = FALSE; + if (found_default) + stuff_default_blr(request, + reinterpret_cast(default_val), + sizeof(default_val)); + else + /* neither col level nor domain level default exists */ + STUFF(blr_null); + } + break; + } + + if (search_for_default) { + /* case 2: see if the column/domain has already been created */ + + METD_get_col_default(request, for_rel_name, + reinterpret_cast < + char *>(for_key_fld_name_str->str_data), + &found_default, + reinterpret_cast(default_val), + sizeof(default_val)); + + if (found_default) + stuff_default_blr(request, + reinterpret_cast(default_val), + sizeof(default_val)); + else + STUFF(blr_null); + + } + + /* the context for the foreign key relation */ + STUFF(blr_field); + STUFF((SSHORT) 2); + put_cstring(request, 0, + reinterpret_cast < + char *>(for_key_fld_name_str->str_data)); + + num_fields++; + for_key_flds++; + } + while (num_fields < for_columns->nod_count); + STUFF(blr_end); + + if (on_upd_trg) { + STUFF(blr_end); + STUFF(blr_end); + STUFF(blr_end); + } + + end_blr(request); +/* no trg_source and no trg_description */ + STUFF(gds_dyn_end); +} + + +static void define_dimensions( REQ request, FLD field) +{ +/***************************************** + * + * d e f i n e _ d i m e n s i o n s + * + ***************************************** + * + * Function + * Define dimensions of an array + * + **************************************/ + NOD elements, element; + USHORT dims; + SLONG lrange, hrange; + + elements = field->fld_ranges; + dims = elements->nod_count / 2; + + if (dims > MAX_ARRAY_DIMENSIONS) + { + ERRD_post(gds__sqlerr, gds_arg_number, (SLONG) - 604, + gds_arg_gds, gds__dsql_max_arr_dim_exceeded, 0); + } + + put_number(request, gds_dyn_fld_dimensions, (SSHORT) dims); + + SSHORT position = 0; + NOD* ptr = elements->nod_arg; + NOD* end = ptr + elements->nod_count; + for (; ptr < end; ++ptr, ++position) + { + put_number(request, gds_dyn_def_dimension, position); + element = *ptr++; + STUFF(gds_dyn_dim_lower); + lrange = (SLONG) (element->nod_arg[0]); + STUFF_WORD(4); + STUFF_DWORD(lrange); + element = *ptr; + STUFF(gds_dyn_dim_upper); + hrange = (SLONG) (element->nod_arg[0]); + STUFF_WORD(4); + STUFF_DWORD(hrange); + STUFF(gds_dyn_end); + if (lrange >= hrange) + { + ERRD_post(gds__sqlerr, gds_arg_number, (SLONG) - 604, + gds_arg_gds, gds__dsql_arr_range_error, 0); + } + } +} + + +static void define_domain( REQ request) +{ +/************************************** + * + * d e f i n e _ d o m a i n + * + ************************************** + * + * Function + * Define a domain (global field) + * + **************************************/ + NOD node; + STR string; + BOOLEAN null_flag = FALSE; + BOOLEAN check_flag = FALSE; + + NOD element = request->req_ddl_node; + FLD field = (FLD) element->nod_arg[e_dom_name]; + + put_cstring(request, gds_dyn_def_global_fld, field->fld_name); + + DDL_resolve_intl_type(request, field, + (STR) element->nod_arg[e_dom_collate]); + put_field(request, field, FALSE); + +/* check for a default value */ + + node = element->nod_arg[e_dom_default]; + if (node) + { + node = PASS1_node(request, node, 0); + begin_blr(request, gds_dyn_fld_default_value); + GEN_expr(request, node); + end_blr(request); + + string = (STR) element->nod_arg[e_dom_default_source]; + if (string) + { + assert(string->str_length <= MAX_USHORT); + put_string(request, + gds_dyn_fld_default_source, + string->str_data, + (USHORT) string->str_length); + } + } + + if (field->fld_ranges) + { + define_dimensions(request, field); + } + +/* check for constraints */ + node = element->nod_arg[e_dom_constraint]; + if (node) + { + NOD* ptr = node->nod_arg; + NOD* end_ptr = ptr + node->nod_count; + for (; ptr < end_ptr; ++ptr) + { + if ((*ptr)->nod_type == nod_rel_constraint) + { + NOD node1 = (*ptr)->nod_arg[e_rct_type]; + if (node1->nod_type == nod_null) + { + if (!(null_flag)) + { + STUFF(gds_dyn_fld_not_null); + null_flag = TRUE; + } + else + { + ERRD_post(gds__sqlerr, gds_arg_number, (SLONG) - 637, + gds_arg_gds, gds__dsql_duplicate_spec, + gds_arg_string, "NOT NULL", 0); + } + } + else if (node1->nod_type == nod_def_constraint) + { + if (check_flag) + { + ERRD_post(gds__sqlerr, gds_arg_number, (SLONG) - 637, + gds_arg_gds, gds__dsql_duplicate_spec, + gds_arg_string, "DOMAIN CHECK CONSTRAINT", + 0); + } + check_flag = TRUE; + + string = (STR) node1->nod_arg[e_cnstr_source]; + if (string) + { + assert(string->str_length <= MAX_USHORT); + put_string(request, + gds_dyn_fld_validation_source, + string->str_data, + (USHORT) string->str_length); + } + begin_blr(request, gds_dyn_fld_validation_blr); + + /* Set any VALUE nodes to the type of the domain being defined. */ + if (node1->nod_arg[e_cnstr_condition]) + { + set_nod_value_attributes(node1->nod_arg[e_cnstr_condition], + field); + } + + /* Increment the context level for this request, so + that the context number for any RSE generated for a + SELECT within the CHECK clause will be greater than + 0. In the environment of a domain check + constraint, context number 0 is reserved for the + "blr_fid, 0, 0,0," which is emitted for a + nod_dom_value, corresponding to an occurance of the + VALUE keyword in the bod of the check constraint. + -- chrisj 1999-08-20 */ + + request->req_context_number++; + + GEN_expr(request, + PASS1_node(request, + node1->nod_arg[e_cnstr_condition], + 0)); + + end_blr(request); + } + } + } + } + + STUFF(gds_dyn_end); +} + + +static void define_exception( REQ request, NOD_TYPE op) +{ +/************************************** + * + * d e f i n e _ e x c e p t i o n + * + ************************************** + * + * Function + * Generate ddl to create an exception code. + * + **************************************/ + NOD ddl_node; + STR text, name; + + ddl_node = request->req_ddl_node; + name = (STR) ddl_node->nod_arg[e_xcp_name]; + text = (STR) ddl_node->nod_arg[e_xcp_text]; + + if (op == nod_def_exception) + put_cstring(request, gds_dyn_def_exception, + reinterpret_cast(name->str_data)); + else if (op == nod_mod_exception) + put_cstring(request, gds_dyn_mod_exception, + reinterpret_cast(name->str_data)); + else + put_cstring(request, gds_dyn_del_exception, + reinterpret_cast(name->str_data)); + + if (op != nod_del_exception) { + assert(text->str_length <= MAX_USHORT); + put_string(request, gds_dyn_xcp_msg, text->str_data, + (USHORT) text->str_length); + STUFF(gds_dyn_end); + } +} + + +static void define_field( + REQ request, + NOD element, SSHORT position, STR relation_name) +{ +/************************************** + * + * d e f i n e _ f i e l d + * + ************************************** + * + * Function + * Define a field, either as part of a create + * table or an alter table statement. + * + **************************************/ + NOD domain_node, node, node1, *ptr, *end_ptr; + FLD field; + DSQL_REL relation; + STR string, domain_name; + USHORT cnstrt_flag = FALSE; + NOD computed_node; + BOOLEAN default_null_flag = FALSE; + + field = (FLD) element->nod_arg[e_dfl_field]; + +/* add the field to the relation being defined for parsing purposes */ + + if ((relation = request->req_relation) != NULL) { + field->fld_next = relation->rel_fields; + relation->rel_fields = field; + } + + if (domain_node = element->nod_arg[e_dfl_domain]) { + put_cstring(request, gds_dyn_def_local_fld, field->fld_name); + node1 = domain_node->nod_arg[e_dom_name]; + domain_name = (STR) node1->nod_arg[e_fln_name]; + put_cstring(request, gds_dyn_fld_source, + reinterpret_cast(domain_name->str_data)); + + /* Get the domain information */ + + if (!(METD_get_domain(request, field, domain_name->str_data))) + ERRD_post(gds__sqlerr, gds_arg_number, (SLONG) - 607, + gds_arg_gds, gds__dsql_command_err, + gds_arg_gds, gds__dsql_domain_not_found, + /* Specified domain or source field does not exist */ + 0); + + DDL_resolve_intl_type( request, + field, + reinterpret_cast(element->nod_arg[e_dfl_collate])); + if (element->nod_arg[e_dfl_collate]) { + put_number(request, gds_dyn_fld_collation, + field->fld_collation_id); + } + } + else { + put_cstring(request, gds_dyn_def_sql_fld, field->fld_name); + if (relation_name) + put_cstring(request, gds_dyn_rel_name, + reinterpret_cast(relation_name->str_data)); + + if (element->nod_arg[e_dfl_computed]) { + field->fld_flags |= FLD_computed; + computed_node = element->nod_arg[e_dfl_computed]; + define_computed(request, + request->req_ddl_node->nod_arg[e_drl_name], field, + computed_node); + } + + DDL_resolve_intl_type(request, field, + reinterpret_cast(element->nod_arg[e_dfl_collate])); + put_field(request, field, FALSE); + } + + if (position != -1) + put_number(request, gds_dyn_fld_position, position); + +/* check for a default value */ + + if ((node = element->nod_arg[e_dfl_default]) != NULL) { + node = PASS1_node(request, node, 0); + begin_blr(request, gds_dyn_fld_default_value); + if (node->nod_type == nod_null) + default_null_flag = TRUE; + GEN_expr(request, node); + end_blr(request); + if ((string = (STR) element->nod_arg[e_dfl_default_source]) != NULL) { + assert(string->str_length <= MAX_USHORT); + put_string(request, gds_dyn_fld_default_source, string->str_data, + (USHORT) string->str_length); + } + } + + if (field->fld_ranges) + define_dimensions(request, field); + +/* check for constraints */ + + if (node = element->nod_arg[e_dfl_constraint]) { + for (ptr = node->nod_arg, end_ptr = ptr + node->nod_count; + ptr < end_ptr; ptr++) { + if ((*ptr)->nod_type == nod_rel_constraint) { + string = (STR) (*ptr)->nod_arg[e_rct_name]; + node1 = (*ptr)->nod_arg[e_rct_type]; + + if (node1->nod_type == nod_null) { + if (default_null_flag == TRUE) + ERRD_post(gds__sqlerr, gds_arg_number, (SLONG) - 204, + gds_arg_gds, isc_bad_default_value, + gds_arg_gds, isc_invalid_clause, + gds_arg_string, "default null not null", 0); + STUFF(gds_dyn_fld_not_null); + if (cnstrt_flag == FALSE) { + STUFF(gds_dyn_end); /* For field definition */ + cnstrt_flag = TRUE; + } + put_cstring(request, gds_dyn_rel_constraint, + reinterpret_cast < + char *>((string) ? string->str_data : NULL)); + STUFF(gds_dyn_fld_not_null); + STUFF(gds_dyn_end); /* For NOT NULL Constraint definition */ + } + else if (node1->nod_type == nod_primary + || node1->nod_type == nod_unique) { + if (cnstrt_flag == FALSE) { + STUFF(gds_dyn_end); /* For field definition */ + cnstrt_flag = TRUE; + } + put_cstring(request, gds_dyn_rel_constraint, + reinterpret_cast < + char *>((string) ? string->str_data : NULL)); + if (node1->nod_type == nod_primary) + STUFF(gds_dyn_def_primary_key); + else if (node1->nod_type == nod_unique) + STUFF(gds_dyn_def_unique); + + STUFF_WORD(0); /* So index name is generated */ + put_number(request, gds_dyn_idx_unique, 1); + put_cstring(request, gds_dyn_fld_name, field->fld_name); + STUFF(gds_dyn_end); + } + else if (node1->nod_type == nod_foreign) { + if (cnstrt_flag == FALSE) { + STUFF(gds_dyn_end); /* For field definition */ + cnstrt_flag = TRUE; + } + put_cstring(request, gds_dyn_rel_constraint, + reinterpret_cast < + char *>((string) ? string->str_data : NULL)); + foreign_key(request, node1); + } + else if (node1->nod_type == nod_def_constraint) { + if (cnstrt_flag == FALSE) { + STUFF(gds_dyn_end); /* For field definition */ + cnstrt_flag = TRUE; + } + put_cstring(request, gds_dyn_rel_constraint, + reinterpret_cast < + char *>((string) ? string->str_data : NULL)); + check_constraint(request, node1, + FALSE /* No delete trigger */ ); + } + } + } + } + + if (cnstrt_flag == FALSE) + STUFF(gds_dyn_end); +} + + +static void define_filter( REQ request) +{ +/************************************** + * + * d e f i n e _ f i l t e r + * + ************************************** + * + * Function + * define a filter to the database. + * + **************************************/ + NOD *ptr, filter_node; + + filter_node = request->req_ddl_node; + ptr = filter_node->nod_arg; + put_cstring(request, gds_dyn_def_filter, + reinterpret_cast < + char *>(((STR) (ptr[e_filter_name]))->str_data)); + put_number(request, gds_dyn_filter_in_subtype, + (SSHORT) ((ptr[e_filter_in_type])->nod_arg[0])); + put_number(request, gds_dyn_filter_out_subtype, + (SSHORT) ((ptr[e_filter_out_type])->nod_arg[0])); + put_cstring(request, gds_dyn_func_entry_point, + reinterpret_cast < + char *>(((STR) (ptr[e_filter_entry_pt]))->str_data)); + put_cstring(request, gds_dyn_func_module_name, + reinterpret_cast < + char *>(((STR) (ptr[e_filter_module]))->str_data)); + + STUFF(gds_dyn_end); +} + + +static void define_generator( REQ request) +{ +/************************************** + * + * d e f i n e _ g e n e r a t o r + * + ************************************** + * + * Function + * create a generator. + * + **************************************/ + STR gen_name; + + gen_name = (STR) request->req_ddl_node->nod_arg[e_gen_name]; + put_cstring(request, gds_dyn_def_generator, + reinterpret_cast(gen_name->str_data)); + STUFF(gds_dyn_end); +} + + +static void define_index( REQ request) +{ +/************************************** + * + * d e f i n e _ i n d e x + * + ************************************** + * + * Function + * Generate ddl to create an index. + * + **************************************/ + NOD ddl_node, relation_node, field_list, *ptr, *end; + STR relation_name, index_name; + + STUFF(gds_dyn_begin); + + ddl_node = request->req_ddl_node; + relation_node = (NOD) ddl_node->nod_arg[e_idx_table]; + relation_name = (STR) relation_node->nod_arg[e_rln_name]; + field_list = ddl_node->nod_arg[e_idx_fields]; + index_name = (STR) ddl_node->nod_arg[e_idx_name]; + + put_cstring(request, gds_dyn_def_idx, + reinterpret_cast(index_name->str_data)); + put_cstring(request, gds_dyn_rel_name, + reinterpret_cast(relation_name->str_data)); + +/* go through the fields list, making an index segment for each field, + unless we have a computation, in which case generate an expression index */ + + if (field_list->nod_type == nod_list) + for (ptr = field_list->nod_arg, end = ptr + field_list->nod_count; + ptr < end; ptr++) + put_cstring(request, gds_dyn_fld_name, + reinterpret_cast < + char *>(((STR) (*ptr)->nod_arg[1])->str_data)); +#ifdef EXPRESSION_INDICES + else if (field_list->nod_type == nod_def_computed) + define_computed(request, relation_node, NULL, field_list); +#endif + +/* check for a unique index */ + + if (ddl_node->nod_arg[e_idx_unique]) + put_number(request, gds_dyn_idx_unique, 1); + + if (ddl_node->nod_arg[e_idx_asc_dsc]) + put_number(request, gds_dyn_idx_type, 1); + + STUFF(gds_dyn_end); /* of define index */ + STUFF(gds_dyn_end); /* of begin */ +} + + +static NOD define_insert_action( REQ request) +{ +/************************************** + * + * d e f i n e _ i n s e r t _ a c t i o n + * + ************************************** + * + * Function + * Define an action statement which, given a view + * definition, will store a record from + * a view of a single relation into the + * base relation. + * + **************************************/ + NOD ddl_node, action_node, insert_node; + NOD select_node, select_expr, from_list, relation_node; + NOD fields_node, values_node, field_node, value_node; + NOD *ptr, *end, *ptr2, *end2; + LLS field_stack, value_stack; + DSQL_REL relation; + FLD field; + + ddl_node = request->req_ddl_node; + +/* check whether this is an updatable view definition */ + + if (ddl_node->nod_type != nod_def_view || + !(select_node = ddl_node->nod_arg[e_view_select]) || + /* + Handle VIEWS with UNION : nod_select now points to nod_list + which in turn points to nod_select_expr + */ + !(select_expr = select_node->nod_arg[0]->nod_arg[0]) || + !(from_list = select_expr->nod_arg[e_sel_from]) || + from_list->nod_count != 1) + return NULL; + +/* make up an action node consisting of a list of 1 insert statement */ + + action_node = MAKE_node(nod_list, (int) 1); + action_node->nod_arg[0] = insert_node = + MAKE_node(nod_insert, (int) e_ins_count); + +/* use the relation referenced in the select statement to insert into */ + + relation_node = MAKE_node(nod_relation_name, (int) e_rln_count); + insert_node->nod_arg[e_ins_relation] = relation_node; + relation_node->nod_arg[e_rln_name] = + from_list->nod_arg[0]->nod_arg[e_rln_name]; + relation_node->nod_arg[e_rln_alias] = (NOD) MAKE_cstring(TEMP_CONTEXT); + +/* get the list of values and fields to assign to -- if there is + no list of fields, get all fields in the base relation that + are not computed */ + + values_node = ddl_node->nod_arg[e_view_fields]; + fields_node = select_expr->nod_arg[e_sel_list]; + if (!fields_node) + { + relation = + METD_get_relation(request, + reinterpret_cast(relation_node->nod_arg[e_rln_name])); + field_stack = NULL; + for (field = relation->rel_fields; field; field = field->fld_next) + { + if (field->fld_flags & FLD_computed) + continue; + field_node = MAKE_node(nod_field_name, (int) e_fln_count); + field_node->nod_arg[e_fln_name] = (NOD)MAKE_cstring(field->fld_name); + LLS_PUSH(field_node, &field_stack); + } + fields_node = MAKE_list(field_stack); + } + + if (!values_node) + values_node = fields_node; + +/* generate the list of assignments to fields in the base relation */ + + ptr = fields_node->nod_arg; + end = ptr + fields_node->nod_count; + ptr2 = values_node->nod_arg; + end2 = ptr2 + values_node->nod_count; + value_stack = field_stack = NULL; + for (; (ptr < end) && (ptr2 < end2); ptr++, ptr2++) { + field_node = *ptr; + if (field_node->nod_type == nod_alias) + field_node = field_node->nod_arg[e_alias_value]; + + /* generate the actual assignment, assigning from a field in the "NEW" context */ + + if (field_node->nod_type == nod_field_name) { + field_node->nod_arg[e_fln_context] = + (NOD) MAKE_cstring(TEMP_CONTEXT); + LLS_PUSH(field_node, &field_stack); + + value_node = MAKE_node(nod_field_name, (int) e_fln_count); + value_node->nod_arg[e_fln_name] = (*ptr2)->nod_arg[e_fln_name]; + value_node->nod_arg[e_fln_context] = + (NOD) MAKE_cstring(NEW_CONTEXT); + LLS_PUSH(value_node, &value_stack); + } + } + + insert_node->nod_arg[e_ins_values] = MAKE_list(value_stack); + insert_node->nod_arg[e_ins_fields] = MAKE_list(field_stack); + + return action_node; +} + + +static void define_procedure( REQ request, NOD_TYPE op) +{ +/************************************** + * + * d e f i n e _ p r o c e d u r e + * + ************************************** + * + * Function + * Create DYN to store a procedure + * + **************************************/ + NOD parameters, parameter, *ptr, *end, procedure_node; + STR procedure_name, source; + PRC procedure; + FLD field, *field_ptr; + SSHORT position, inputs, outputs, locals; + VAR variable; + TSQL tdsql; + + tdsql = GET_THREAD_DATA; + + inputs = outputs = locals = 0; + procedure_node = request->req_ddl_node; + procedure_name = (STR) procedure_node->nod_arg[e_prc_name]; + if (op == nod_def_procedure) { + put_cstring(request, gds_dyn_def_procedure, + reinterpret_cast(procedure_name->str_data)); + put_number(request, gds_dyn_rel_sql_protection, 1); + } + else { + put_cstring(request, gds_dyn_mod_procedure, + reinterpret_cast(procedure_name->str_data)); + if (procedure = METD_get_procedure(request, procedure_name)) { + for (field = procedure->prc_inputs; field; + field = field->fld_next) { + put_cstring(request, gds_dyn_delete_parameter, + field->fld_name); + STUFF(gds_dyn_end); + } + for (field = procedure->prc_outputs; field; + field = field->fld_next) { + put_cstring(request, gds_dyn_delete_parameter, + field->fld_name); + STUFF(gds_dyn_end); + } + } + } + if ((source = (STR) procedure_node->nod_arg[e_prc_source]) != NULL) { + assert(source->str_length <= MAX_USHORT); + put_string(request, gds_dyn_prc_source, source->str_data, + (USHORT) source->str_length); + } + +/* Fill req_procedure to allow procedure to self reference */ + procedure = + (PRC) ALLOCDV(type_prc, + strlen(reinterpret_cast < + char *>(procedure_name->str_data)) + 1); + procedure->prc_name = procedure->prc_data; + procedure->prc_owner = + procedure->prc_data + procedure_name->str_length + 1; + strcpy(procedure->prc_name, (SCHAR *) procedure_name->str_data); + *procedure->prc_owner = '\0'; + request->req_procedure = procedure; + + +/* now do the input parameters */ + + field_ptr = &procedure->prc_inputs; + + if (parameters = procedure_node->nod_arg[e_prc_inputs]) { + position = 0; + for (ptr = parameters->nod_arg, end = ptr + parameters->nod_count; + ptr < end; ptr++) { + parameter = *ptr; + field = (FLD) parameter->nod_arg[e_dfl_field]; + + put_cstring(request, gds_dyn_def_parameter, field->fld_name); + put_number(request, gds_dyn_prm_number, position); + put_number(request, gds_dyn_prm_type, 0); + DDL_resolve_intl_type(request, field, NULL); + put_field(request, field, FALSE); + + *ptr = MAKE_variable(field, field->fld_name, + VAR_input, 0, (USHORT) (2 * position), + locals); + /* Put the field in a field list which will be stored to allow + procedure self referencing */ + *field_ptr = field; + field_ptr = &field->fld_next; + position++; + + STUFF(gds_dyn_end); + put_number(request, gds_dyn_prc_inputs, position); + } + inputs = position; + } + +/* Terminate the input list */ + + *field_ptr = NULL; + +/* now do the output parameters */ + field_ptr = &procedure->prc_outputs; + + if (parameters = procedure_node->nod_arg[e_prc_outputs]) { + position = 0; + for (ptr = parameters->nod_arg, end = ptr + parameters->nod_count; + ptr < end; ptr++) { + parameter = *ptr; + field = (FLD) parameter->nod_arg[e_dfl_field]; + put_cstring(request, gds_dyn_def_parameter, field->fld_name); + put_number(request, gds_dyn_prm_number, position); + put_number(request, gds_dyn_prm_type, 1); + DDL_resolve_intl_type(request, field, NULL); + put_field(request, field, FALSE); + + *ptr = MAKE_variable(field, field->fld_name, + VAR_output, 1, (USHORT) (2 * position), + locals); + *field_ptr = field; + field_ptr = &field->fld_next; + position++; + locals++; + + STUFF(gds_dyn_end); + put_number(request, gds_dyn_prc_outputs, position); + } + outputs = position; + } + + *field_ptr = NULL; + procedure->prc_out_count = outputs; + procedure->prc_in_count = inputs; + + begin_blr(request, gds_dyn_prc_blr); + STUFF(blr_begin); + if (inputs) { + STUFF(blr_message); + STUFF(0); + STUFF_WORD(2 * inputs); + parameters = procedure_node->nod_arg[e_prc_inputs]; + for (ptr = parameters->nod_arg, end = ptr + parameters->nod_count; + ptr < end; ptr++) { + parameter = *ptr; + variable = (VAR) parameter->nod_arg[e_var_variable]; + field = variable->var_field; + put_msg_field(request, field); + } + } + STUFF(blr_message); + STUFF(1); + STUFF_WORD(2 * outputs + 1); + if (outputs) { + parameters = procedure_node->nod_arg[e_prc_outputs]; + for (ptr = parameters->nod_arg, end = ptr + parameters->nod_count; + ptr < end; ptr++) { + parameter = *ptr; + variable = (VAR) parameter->nod_arg[e_var_variable]; + field = variable->var_field; + put_msg_field(request, field); + } + } + +/* add slot for EOS */ + + STUFF(blr_short); + STUFF(0); + + if (inputs) { + STUFF(blr_receive); + STUFF(0); + } + STUFF(blr_begin); + if (outputs) { + parameters = procedure_node->nod_arg[e_prc_outputs]; + for (ptr = parameters->nod_arg, end = ptr + parameters->nod_count; + ptr < end; ptr++) { + parameter = *ptr; + variable = (VAR) parameter->nod_arg[e_var_variable]; + put_local_variable(request, variable); + } + } + + locals = put_local_variables(request, procedure_node->nod_arg[e_prc_dcls], + locals); + + STUFF(blr_stall); +/* Put a label before body of procedure, so that + any exit statement can get out */ + STUFF(blr_label); + STUFF(0); + request->req_loop_number = 1; + GEN_statement(request, + PASS1_statement(request, + procedure_node->nod_arg[e_prc_body], 1)); + request->req_type = REQ_DDL; + STUFF(blr_end); + GEN_return(request, procedure_node, TRUE); + STUFF(blr_end); + end_blr(request); + + STUFF(gds_dyn_end); +} + + +static void define_rel_constraint( REQ request, NOD element) +{ +/************************************** + * + * d e f i n e _ r e l _ c o n s t r a i n t + * + ************************************** + * + * Function + * Define a constraint , either as part of a create + * table or an alter table statement. + * + **************************************/ + NOD node; + STR string; + + string = (STR) element->nod_arg[e_rct_name]; + put_cstring(request, gds_dyn_rel_constraint, + reinterpret_cast < + char *>((string) ? string->str_data : NULL)); + node = element->nod_arg[e_rct_type]; + + if (node->nod_type == nod_unique || node->nod_type == nod_primary) + make_index(request, node, node->nod_arg[0], 0, 0); + else if (node->nod_type == nod_foreign) + foreign_key(request, node); + else if (node->nod_type == nod_def_constraint) + check_constraint(request, node, FALSE /* No delete trigger */ ); +} + + +static void define_relation( REQ request) +{ +/************************************** + * + * d e f i n e _ r e l a t i o n + * + ************************************** + * + * Function + * Create an SQL table, relying on DYN to generate + * global fields for the local fields. + * + **************************************/ + NOD ddl_node, elements, element, *ptr, *end, relation_node; + STR relation_name, external_file; + SSHORT position; + + ddl_node = request->req_ddl_node; + + relation_node = ddl_node->nod_arg[e_drl_name]; + relation_name = (STR) relation_node->nod_arg[e_rln_name]; + put_cstring(request, gds_dyn_def_rel, + reinterpret_cast(relation_name->str_data)); + if (external_file = (STR) ddl_node->nod_arg[e_drl_ext_file]) + put_cstring(request, gds_dyn_rel_ext_file, + reinterpret_cast(external_file->str_data)); + save_relation(request, relation_name); + put_number(request, gds_dyn_rel_sql_protection, 1); + +/* now do the actual metadata definition */ + + elements = ddl_node->nod_arg[e_drl_elements]; + for (ptr = elements->nod_arg, end = ptr + elements->nod_count, position = + 0; ptr < end; ptr++) { + element = *ptr; + switch (element->nod_type) { + case nod_def_field: + define_field(request, element, position, relation_name); + position++; + break; + + case nod_rel_constraint: + define_rel_constraint(request, element); + break; + + default: + break; + } + } + + STUFF(gds_dyn_end); +} + + +static void define_role( REQ request) +{ +/************************************** + * + * d e f i n e _ r o l e + * + ************************************** + * + * Function + * create a SQL role. + * + **************************************/ + STR gen_name; + + gen_name = (STR) request->req_ddl_node->nod_arg[e_gen_name]; + put_cstring(request, isc_dyn_def_sql_role, + reinterpret_cast(gen_name->str_data)); + STUFF(gds_dyn_end); +} + + +static void define_set_null_trg( + REQ request, + NOD element, + NOD for_columns, + NOD prim_columns, +TEXT * prim_rel_name, TEXT * for_rel_name, BOOLEAN on_upd_trg) +{ +/***************************************************** + * + * d e f i n e _ s e t _ n u l l _ t r g + * + ***************************************************** + * + * Function + * define "on delete/update set null" trigger (for referential integrity) + * The trigger blr is the same for both the delete and update + * cases. Only difference is its TRIGGER_TYPE (ON DELETE or ON UPDATE) + * The on_upd_trg parameter == TRUE is an update trigger. + * + *****************************************************/ + NOD *for_key_flds; + USHORT num_fields = 0; + STR for_key_fld_name_str; + + + if (element->nod_type != nod_foreign) + return; + +/* count of foreign key columns */ + assert(prim_columns->nod_count == for_columns->nod_count); + assert(prim_columns->nod_count != 0); + +/* no trigger name. It is generated by the engine */ + put_string(request, gds_dyn_def_trigger, reinterpret_cast < UCHAR * >(""), + (USHORT) 0); + + put_number(request, gds_dyn_trg_type, + (SSHORT) (on_upd_trg ? POST_MODIFY_TRIGGER : + POST_ERASE_TRIGGER)); + + STUFF(gds_dyn_sql_object); + put_number(request, gds_dyn_trg_sequence, (SSHORT) 1); + put_number(request, gds_dyn_trg_inactive, (SSHORT) 0); + put_cstring(request, gds_dyn_rel_name, prim_rel_name); + +/* the trigger blr */ + begin_blr(request, gds_dyn_trg_blr); + +/* for ON UPDATE TRIGGER only: generate the trigger firing condition: + if prim_key.old_value != prim_key.new value. + Note that the key could consist of multiple columns */ + + if (on_upd_trg) { + stuff_trg_firing_cond(request, prim_columns); + STUFF(blr_begin); + STUFF(blr_begin); + } + + STUFF(blr_for); + STUFF(blr_rse); + +/* the context for the prim. key relation */ + STUFF(1); + + STUFF(blr_relation); + put_cstring(request, 0, for_rel_name); +/* the context for the foreign key relation */ + STUFF(2); + + stuff_matching_blr(request, for_columns, prim_columns); + + STUFF(blr_modify); + STUFF((SSHORT) 2); + STUFF((SSHORT) 2); + STUFF(blr_begin); + + num_fields = 0; + for_key_flds = for_columns->nod_arg; + + do { + for_key_fld_name_str = (STR) (*for_key_flds)->nod_arg[1]; + + STUFF(blr_assignment); + STUFF(blr_null); + STUFF(blr_field); + STUFF((SSHORT) 2); + put_cstring(request, 0, + reinterpret_cast < + char *>(for_key_fld_name_str->str_data)); + + num_fields++; + for_key_flds++; + } + while (num_fields < for_columns->nod_count); + STUFF(blr_end); + + if (on_upd_trg) { + STUFF(blr_end); + STUFF(blr_end); + STUFF(blr_end); + } + end_blr(request); +/* end of the blr */ + +/* no trg_source and no trg_description */ + STUFF(gds_dyn_end); + +} + + +static void define_shadow( REQ request) +{ +/************************************** + * + * d e f i n e _ s h a d o w + * + ************************************** + * + * Function + * create a shadow for the database + * + **************************************/ + NOD shadow_node, elements, element, *ptr, *end; + SLONG start; + FIL file; + SLONG length; + + shadow_node = request->req_ddl_node; + ptr = shadow_node->nod_arg; + if (!ptr[e_shadow_number]) + ERRD_post(gds__sqlerr, gds_arg_number, (SLONG) - 607, + gds_arg_gds, gds__dsql_command_err, + gds_arg_gds, gds__dsql_shadow_number_err, 0); + + put_number(request, gds_dyn_def_shadow, (SSHORT) (ptr[e_shadow_number])); + put_cstring(request, gds_dyn_def_file, + reinterpret_cast < + char *>(((STR) (ptr[e_shadow_name]))->str_data)); + put_number(request, gds_dyn_shadow_man_auto, + (SSHORT) ((ptr[e_shadow_man_auto])->nod_arg[0])); + put_number(request, gds_dyn_shadow_conditional, + (SSHORT) ((ptr[e_shadow_conditional])->nod_arg[0])); + + STUFF(gds_dyn_file_start); + STUFF_WORD(4); + STUFF_DWORD(0); + + length = (SLONG) ptr[e_shadow_length]; + STUFF(gds_dyn_file_length); + STUFF_WORD(4); + STUFF_DWORD(length); + + STUFF(gds_dyn_end); + elements = ptr[e_shadow_sec_files]; + if (elements) + for (ptr = elements->nod_arg, end = ptr + elements->nod_count; + ptr < end; ptr++) { + element = *ptr; + file = (FIL) element->nod_arg[0]; + put_cstring(request, gds_dyn_def_file, + reinterpret_cast(file->fil_name->str_data)); + + if (!length && !file->fil_start) + ERRD_post(gds__sqlerr, gds_arg_number, (SLONG) - 607, + gds_arg_gds, gds__dsql_command_err, + gds_arg_gds, gds__dsql_file_length_err, + gds_arg_number, (SLONG) file->fil_name->str_data, + /* Preceding file did not specify length, so %s must include starting page number */ + 0); + + STUFF(gds_dyn_file_start); + STUFF_WORD(4); + start = file->fil_start; + STUFF_DWORD(start); + STUFF(gds_dyn_file_length); + STUFF_WORD(4); + length = file->fil_length; + STUFF_DWORD(length); + STUFF(gds_dyn_end); + } + + STUFF(gds_dyn_end); +} + + +static void define_trigger( REQ request, NOD node) +{ +/************************************** + * + * d e f i n e _ t r i g g e r + * + ************************************** + * + * Function + * Create the ddl to define or alter a trigger. + * + **************************************/ + STR trigger_name, relation_name, source, message_text; + NOD temp, actions, *ptr, *end, constant, relation_node, message; + SSHORT number; + USHORT trig_type; + TSQL tdsql; + + tdsql = GET_THREAD_DATA; + +/* make the "define trigger" node the current request ddl node so + that generating of BLR will be appropriate for trigger */ + + request->req_ddl_node = node; + + trigger_name = (STR) node->nod_arg[e_trg_name]; + + if (node->nod_type == nod_def_trigger) { + assert(trigger_name->str_length <= MAX_USHORT); + put_string(request, gds_dyn_def_trigger, trigger_name->str_data, + (USHORT) trigger_name->str_length); + relation_node = node->nod_arg[e_trg_table]; + relation_name = (STR) relation_node->nod_arg[e_rln_name]; + assert(relation_name->str_length <= MAX_USHORT); + put_string(request, gds_dyn_rel_name, relation_name->str_data, + (USHORT) relation_name->str_length); + STUFF(gds_dyn_sql_object); + } + else { /* if (node->nod_type == nod_mod_trigger) */ + + assert(node->nod_type == nod_mod_trigger); + assert(trigger_name->str_length <= MAX_USHORT); + put_string(request, gds_dyn_mod_trigger, trigger_name->str_data, + (USHORT) trigger_name->str_length); + if (node->nod_arg[e_trg_actions]) { + /* Since we will be updating the body of the trigger, we need + to know what relation the trigger relates to. */ + + if (! + (relation_name = + METD_get_trigger_relation(request, trigger_name, + &trig_type))) + ERRD_post(gds__sqlerr, gds_arg_number, (SLONG) - 204, + gds_arg_gds, gds__dsql_trigger_err, gds_arg_gds, + gds__random, gds_arg_string, + trigger_name->str_data, 0); + relation_node = (NOD) ALLOCDV(type_nod, e_rln_count); + node->nod_arg[e_trg_table] = relation_node; + relation_node->nod_type = nod_relation_name; + relation_node->nod_count = e_rln_count; + relation_node->nod_arg[e_rln_name] = (NOD) relation_name; + } + } + + source = (STR) node->nod_arg[e_trg_source]; + actions = (node->nod_arg[e_trg_actions]) ? + node->nod_arg[e_trg_actions]->nod_arg[1] : NULL; + + if (source && actions) { + assert(source->str_length <= MAX_USHORT); + put_string(request, gds_dyn_trg_source, source->str_data, + (USHORT) source->str_length); + } + + if (constant = node->nod_arg[e_trg_active]) + put_number(request, gds_dyn_trg_inactive, + (SSHORT) constant->nod_arg[0]); + + if (constant = node->nod_arg[e_trg_position]) + put_number(request, gds_dyn_trg_sequence, + (SSHORT) constant->nod_arg[0]); + + if (constant = node->nod_arg[e_trg_type]) { + put_number(request, gds_dyn_trg_type, (SSHORT) constant->nod_arg[0]); + trig_type = (USHORT) constant->nod_arg[0]; + } + else { + assert(node->nod_type == nod_mod_trigger); + } + + if (actions) { + /* create the "OLD" and "NEW" contexts for the trigger -- + the new one could be a dummy place holder to avoid resolving + fields to that context but prevent relations referenced in + the trigger actions from referencing the predefined "1" context */ + + if (request->req_context_number) + reset_context_stack(request); + + temp = relation_node->nod_arg[e_rln_alias]; + if ((trig_type != PRE_STORE_TRIGGER) + && (trig_type != POST_STORE_TRIGGER)) { + relation_node->nod_arg[e_rln_alias] = + (NOD) MAKE_cstring(OLD_CONTEXT); + PASS1_make_context(request, relation_node); + } + else + request->req_context_number++; + + if ((trig_type != PRE_ERASE_TRIGGER) + && (trig_type != POST_ERASE_TRIGGER)) { + relation_node->nod_arg[e_rln_alias] = + (NOD) MAKE_cstring(NEW_CONTEXT); + PASS1_make_context(request, relation_node); + } + else + request->req_context_number++; + + relation_node->nod_arg[e_rln_alias] = temp; + + /* generate the trigger blr */ + + begin_blr(request, gds_dyn_trg_blr); + STUFF(blr_begin); + + put_local_variables(request, + node->nod_arg[e_trg_actions]->nod_arg[0], 0); + + request->req_scope_level++; + GEN_statement(request, PASS1_statement(request, actions, 1)); + request->req_scope_level--; + STUFF(blr_end); + end_blr(request); + + /* the request type may have been set incorrectly when parsing + the trigger actions, so reset it to reflect the fact that this + is a data definition request; also reset the ddl node */ + + request->req_type = REQ_DDL; + } + + if (temp = node->nod_arg[e_trg_messages]) + for (ptr = temp->nod_arg, end = ptr + temp->nod_count; ptr < end; + ptr++) { + message = *ptr; + number = (SSHORT) message->nod_arg[e_msg_number]; + if (message->nod_type == nod_del_trigger_msg) { + put_number(request, gds_dyn_delete_trigger_msg, number); + STUFF(gds_dyn_end); + } + else { + message_text = (STR) message->nod_arg[e_msg_text]; + if (message->nod_type == nod_def_trigger_msg) + put_number(request, gds_dyn_def_trigger_msg, number); + else + put_number(request, gds_dyn_mod_trigger_msg, number); + assert(message_text->str_length <= MAX_USHORT); + put_string(request, gds_dyn_trg_msg, message_text->str_data, + (USHORT) message_text->str_length); + STUFF(gds_dyn_end); + } + } + + STUFF(gds_dyn_end); +} + + +static void define_udf( REQ request) +{ +/************************************** + * + * d e f i n e _ u d f + * + ************************************** + * + * Function + * define a udf to the database. + * + **************************************/ + NOD *ptr, *end, *ret_val_ptr, arguments, udf_node; + UCHAR *udf_name; + FLD field; + SSHORT position, blob_position; + + udf_node = request->req_ddl_node; + arguments = udf_node->nod_arg[e_udf_args]; + ptr = udf_node->nod_arg; + udf_name = ((STR) (ptr[e_udf_name]))->str_data; + put_cstring(request, gds_dyn_def_function, + reinterpret_cast(udf_name)); + put_cstring(request, gds_dyn_func_entry_point, + reinterpret_cast < + char *>(((STR) (ptr[e_udf_entry_pt]))->str_data)); + put_cstring(request, gds_dyn_func_module_name, + reinterpret_cast < + char *>(((STR) (ptr[e_udf_module]))->str_data)); + + ret_val_ptr = ptr[e_udf_return_value]->nod_arg; + + + if (field = (FLD) ret_val_ptr[0]) { + + /* Some data types can not be returned as value */ + + if (((int) (ret_val_ptr[1]->nod_arg[0]) == FUN_value) && + (field->fld_dtype == dtype_text || + field->fld_dtype == dtype_varying || + field->fld_dtype == dtype_cstring || + field->fld_dtype == dtype_blob || + field->fld_dtype == dtype_timestamp)) + ERRD_post(gds__sqlerr, gds_arg_number, (SLONG) - 607, + gds_arg_gds, gds__dsql_command_err, + gds_arg_gds, gds__return_mode_err, + /* Return mode by value not allowed for this data type */ + 0); + + /* For functions returning a blob, coerce return argument position to + be the last parameter. */ + + if (field->fld_dtype == dtype_blob) { + blob_position = (arguments) ? arguments->nod_count + 1 : 1; + if (blob_position > MAX_UDF_ARGUMENTS) + ERRD_post(gds__sqlerr, gds_arg_number, (SLONG) - 607, + gds_arg_gds, gds__dsql_command_err, + gds_arg_gds, gds__extern_func_err, + /* External functions can not have more than 10 parameters */ + /* Or 9 if the function returns a BLOB */ + 0); + + put_number(request, gds_dyn_func_return_argument, blob_position); + } + else + put_number(request, gds_dyn_func_return_argument, (SSHORT) 0); + + position = 0; + } + else { + position = (SSHORT) (ret_val_ptr[1]->nod_arg[0]); + /* Function modifies an argument whose value is the function return value */ + + if (!arguments || position > arguments->nod_count || position < 1) + ERRD_post(gds__sqlerr, gds_arg_number, (SLONG) - 607, + gds_arg_gds, gds__dsql_command_err, + gds_arg_gds, gds__extern_func_err, + /* External functions can not have more than 10 parameters */ + /* Not strictly correct -- return position error */ + 0); + + put_number(request, gds_dyn_func_return_argument, position); + position = 1; + } + +/* Now define all the arguments */ + if (!position) { + if (field->fld_dtype == dtype_blob) { + BOOLEAN free_it = ((SSHORT) ret_val_ptr[1]->nod_arg[0] < 0); + put_number(request, gds_dyn_def_function_arg, blob_position); + put_number(request, gds_dyn_func_mechanism, + (SSHORT) ((free_it ? -1 : 1) * FUN_blob_struct)); + /* if we have the free_it set then the blob has + to be freed on return */ + } + else { + put_number(request, gds_dyn_def_function_arg, (SSHORT) 0); + put_number(request, gds_dyn_func_mechanism, + (SSHORT) (ret_val_ptr[1]->nod_arg[0])); + } + + put_cstring(request, gds_dyn_function_name, + reinterpret_cast(udf_name)); + DDL_resolve_intl_type(request, field, NULL); + put_field(request, field, TRUE); + STUFF(gds_dyn_end); + position = 1; + } + + assert(position == 1); + if (arguments) + for (ptr = arguments->nod_arg, end = ptr + arguments->nod_count; + ptr < end; ptr++, position++) { + if (position > MAX_UDF_ARGUMENTS) + ERRD_post(gds__sqlerr, gds_arg_number, (SLONG) - 607, + gds_arg_gds, gds__dsql_command_err, + gds_arg_gds, gds__extern_func_err, + /* External functions can not have more than 10 parameters */ + 0); + + field = (FLD) * ptr; + put_number(request, gds_dyn_def_function_arg, (SSHORT) position); + + if (field->fld_dtype == dtype_blob) + put_number(request, gds_dyn_func_mechanism, + (SSHORT) FUN_blob_struct); + else + put_number(request, gds_dyn_func_mechanism, + (SSHORT) FUN_reference); + + put_cstring(request, gds_dyn_function_name, + reinterpret_cast(udf_name)); + DDL_resolve_intl_type(request, field, NULL); + put_field(request, field, TRUE); + STUFF(gds_dyn_end); + } + + STUFF(gds_dyn_end); +} + + + +static void define_update_action( + REQ request, + NOD * base_and_node, NOD * base_relation) +{ +/* ************************************* + * + * d e f i n e _ u p d a t e _ a c t i o n + * + ************************************** + * + * Function + * Define an action statement which, given a view + * definition, will map a update to a record from + * a view of a single relation into the + * base relation. + * + **************************************/ + NOD ddl_node, eql_node, and_node, old_and; + NOD select_node, select_expr, from_list, relation_node; + NOD fields_node, values_node, field_node, value_node, old_value_node; + NOD *ptr, *end, *ptr2, *end2; + NOD iand_node, or_node, anull_node, bnull_node; + LLS field_stack; + DSQL_REL relation; + FLD field; + SSHORT and_arg = 0; + + ddl_node = request->req_ddl_node; + +/* check whether this is an updatable view definition */ + + if (ddl_node->nod_type != nod_def_view || + !(select_node = ddl_node->nod_arg[e_view_select]) || + /* + Handle VIEWS with UNION : nod_select now points to nod_list + which in turn points to nod_select_expr + */ + !(select_expr = select_node->nod_arg[0]->nod_arg[0]) || + !(from_list = select_expr->nod_arg[e_sel_from]) || + from_list->nod_count != 1) + return; + +/* use the relation referenced in the select statement for rse*/ + + relation_node = MAKE_node(nod_relation_name, (int) e_rln_count); + relation_node->nod_arg[e_rln_name] = + from_list->nod_arg[0]->nod_arg[e_rln_name]; + relation_node->nod_arg[e_rln_alias] = (NOD) MAKE_cstring(TEMP_CONTEXT); + *base_relation = relation_node; + +/* get the list of values and fields to compare to -- if there is + no list of fields, get all fields in the base relation that + are not computed */ + + values_node = ddl_node->nod_arg[e_view_fields]; + if (!(fields_node = select_expr->nod_arg[e_sel_list])) + { + relation = + METD_get_relation(request, + reinterpret_cast(relation_node->nod_arg[e_rln_name])); + field_stack = NULL; + for (field = relation->rel_fields; field; field = field->fld_next) + { + if (field->fld_flags & FLD_computed) + continue; + field_node = MAKE_node(nod_field_name, (int) e_fln_count); + field_node->nod_arg[e_fln_name] = + (NOD) MAKE_cstring(field->fld_name); + LLS_PUSH(field_node, &field_stack); + } + fields_node = MAKE_list(field_stack); + } + if (!values_node) + values_node = fields_node; + +/* generate the list of assignments to fields in the base relation */ + + ptr = fields_node->nod_arg; + end = ptr + fields_node->nod_count; + ptr2 = values_node->nod_arg; + end2 = ptr2 + values_node->nod_count; + field_stack = NULL; + and_node = MAKE_node(nod_and, (int) 2); + and_arg = 0; + for (; (ptr < end) && (ptr2 < end2); ptr++, ptr2++) { + field_node = *ptr; + if (field_node->nod_type == nod_alias) + field_node = field_node->nod_arg[e_alias_value]; + + /* generate the actual comparisons */ + + if (field_node->nod_type == nod_field_name) { + field_node->nod_arg[e_fln_context] = + (NOD) MAKE_cstring(TEMP_CONTEXT); + + value_node = MAKE_node(nod_field_name, (int) e_fln_count); + value_node->nod_arg[e_fln_name] = (*ptr2)->nod_arg[e_fln_name]; + value_node->nod_arg[e_fln_context] = + (NOD) MAKE_cstring(NEW_CONTEXT); + + old_value_node = MAKE_node(nod_field_name, (int) e_fln_count); + old_value_node->nod_arg[e_fln_name] = + (*ptr2)->nod_arg[e_fln_name]; + old_value_node->nod_arg[e_fln_context] = + (NOD) MAKE_cstring(OLD_CONTEXT); + eql_node = MAKE_node(nod_eql, (int) 2); + eql_node->nod_arg[0] = old_value_node; + eql_node->nod_arg[1] = field_node; + + anull_node = MAKE_node(nod_missing, 1); + anull_node->nod_arg[0] = old_value_node; + bnull_node = MAKE_node(nod_missing, 1); + bnull_node->nod_arg[0] = field_node; + + iand_node = MAKE_node(nod_and, (int) 2); + iand_node->nod_arg[0] = anull_node; + iand_node->nod_arg[1] = bnull_node; + + or_node = MAKE_node(nod_or, (int) 2); + or_node->nod_arg[0] = eql_node; + or_node->nod_arg[1] = iand_node; + + if (and_arg <= 1) + and_node->nod_arg[and_arg++] = or_node; + else { + old_and = and_node; + and_node = MAKE_node(nod_and, (int) 2); + and_node->nod_arg[0] = old_and; + and_node->nod_arg[1] = or_node; + } + } + } + + if (and_arg <= 1) + and_node->nod_arg[and_arg] = select_expr->nod_arg[e_sel_where]; + else { + old_and = and_node; + and_node = MAKE_node(nod_and, (int) 2); + and_node->nod_arg[0] = old_and; + and_node->nod_arg[1] = select_expr->nod_arg[e_sel_where]; + } + *base_and_node = and_node; +} + + +static void define_upd_cascade_trg( + REQ request, + NOD element, + NOD for_columns, + NOD prim_columns, +TEXT * prim_rel_name, TEXT * for_rel_name) +{ +/***************************************************** + * + * d e f i n e _ u p d _ c a s c a d e _ t r g + * + ***************************************************** + * + * Function + * define "on update cascade" trigger (for referential integrity) + * along with the trigger blr. + * + *****************************************************/ + NOD *for_key_flds, *prim_key_flds; + USHORT num_fields = 0; + STR for_key_fld_name_str, prim_key_fld_name_str; + + if (element->nod_type != nod_foreign) + return; + +/* count of foreign key columns */ + assert(prim_columns->nod_count == for_columns->nod_count); + assert(prim_columns->nod_count != 0); + +/* no trigger name is generated here. Let the engine make one up */ + put_string(request, gds_dyn_def_trigger, reinterpret_cast < UCHAR * >(""), + (USHORT) 0); + + put_number(request, gds_dyn_trg_type, (SSHORT) POST_MODIFY_TRIGGER); + + STUFF(gds_dyn_sql_object); + put_number(request, gds_dyn_trg_sequence, (SSHORT) 1); + put_number(request, gds_dyn_trg_inactive, (SSHORT) 0); + put_cstring(request, gds_dyn_rel_name, prim_rel_name); + +/* the trigger blr */ + begin_blr(request, gds_dyn_trg_blr); + +/* generate the trigger firing condition: foreign_key == primary_key */ + stuff_trg_firing_cond(request, prim_columns); + + STUFF(blr_begin); + STUFF(blr_begin); + + STUFF(blr_for); + STUFF(blr_rse); + +/* the new context for the prim. key relation */ + STUFF(1); + + STUFF(blr_relation); + put_cstring(request, 0, for_rel_name); +/* the context for the foreign key relation */ + STUFF(2); + +/* generate the blr for: foreign_key == primary_key */ + stuff_matching_blr(request, for_columns, prim_columns); + + STUFF(blr_modify); + STUFF((SSHORT) 2); + STUFF((SSHORT) 2); + STUFF(blr_begin); + + num_fields = 0; + for_key_flds = for_columns->nod_arg; + prim_key_flds = prim_columns->nod_arg; + + do { + for_key_fld_name_str = (STR) (*for_key_flds)->nod_arg[1]; + prim_key_fld_name_str = (STR) (*prim_key_flds)->nod_arg[1]; + + STUFF(blr_assignment); + STUFF(blr_field); + STUFF((SSHORT) 1); + put_cstring(request, 0, + reinterpret_cast < + char *>(prim_key_fld_name_str->str_data)); + STUFF(blr_field); + STUFF((SSHORT) 2); + put_cstring(request, 0, + reinterpret_cast < + char *>(for_key_fld_name_str->str_data)); + + num_fields++; + prim_key_flds++; + for_key_flds++; + } + while (num_fields < for_columns->nod_count); + + STUFF(blr_end); + STUFF(blr_end); + STUFF(blr_end); + STUFF(blr_end); + end_blr(request); +/* end of the blr */ + +/* no trg_source and no trg_description */ + STUFF(gds_dyn_end); + +} + + +static void define_view( REQ request) +{ +/************************************** + * + * d e f i n e _ v i e w + * + ************************************** + * + * Function + * Create the ddl to define a view, using a SELECT + * statement as the source of the view. + * + **************************************/ + NOD node, select, select_expr, rse, field_node; + NOD check, relation_node; + NOD view_fields, *ptr, *end; + NOD items, *i_ptr, *i_end; + DSQL_REL relation; + FLD field; + CTX context; + STR view_name, field_name, source; + SSHORT position, updatable = TRUE; + TEXT *field_string; + LLS temp; + + node = request->req_ddl_node; + view_name = (STR) node->nod_arg[e_view_name]; + put_cstring(request, gds_dyn_def_view, + reinterpret_cast(view_name->str_data)); + save_relation(request, view_name); + put_number(request, gds_dyn_rel_sql_protection, 1); + +/* compile the SELECT statement into a record selection expression, + making sure to bump the context number since view contexts start + at 1 (except for computed fields) -- note that calling PASS1_rse + directly rather than PASS1_statement saves the context stack */ + + if (request->req_context_number) + reset_context_stack(request); + request->req_context_number++; + select = node->nod_arg[e_view_select]; + select_expr = select->nod_arg[0]; + rse = PASS1_rse(request, select_expr, select->nod_arg[1]); + +/* store the blr and source string for the view definition */ + + begin_blr(request, gds_dyn_view_blr); + GEN_expr(request, rse); + end_blr(request); + +/* Store source for view. gdef -e cannot cope with it. + We need to add something to rdb$views to indicate source type. + Source will be for documentation purposes. */ + + source = (STR) node->nod_arg[e_view_source]; + assert(source->str_length <= MAX_USHORT); + put_string(request, gds_dyn_view_source, source->str_data, + (USHORT) source->str_length); + +/* define the view source relations from the request contexts */ + + for (temp = request->req_context; temp; temp = temp->lls_next) { + context = (CTX) temp->lls_object; + if (relation = context->ctx_relation) { + put_cstring(request, gds_dyn_view_relation, relation->rel_name); + put_number(request, gds_dyn_view_context, context->ctx_context); + put_cstring(request, gds_dyn_view_context_name, + context->ctx_alias ? context->ctx_alias : relation-> + rel_name); + STUFF(gds_dyn_end); + } + } + +/* if there are field names defined for the view, match them in order + with the items from the SELECT. Otherwise use all the fields from + the rse node that was created from the select expression */ + + items = rse->nod_arg[e_rse_items]; + i_ptr = items->nod_arg; + i_end = i_ptr + items->nod_count; + + ptr = end = NULL; + if ((view_fields = node->nod_arg[e_view_fields]) != NULL) { + ptr = view_fields->nod_arg; + end = ptr + view_fields->nod_count; + } + +/* go through the fields list, defining the local fields; + if an expression is specified rather than a field, define + a global field for the computed value as well */ + + for (position = 0; i_ptr < i_end; i_ptr++, position++) { + field_node = *i_ptr; + + /* check if this is a field or an expression */ + + field = NULL; + context = NULL; + if (field_node->nod_type == nod_field) { + field = (FLD) field_node->nod_arg[e_fld_field]; + context = (CTX) field_node->nod_arg[e_fld_context]; + } + else + updatable = FALSE; + + /* if this is an expression, check to make sure there is a name specified */ + + if (!ptr && !field) + ERRD_post(gds__sqlerr, gds_arg_number, (SLONG) - 607, + gds_arg_gds, gds__dsql_command_err, + gds_arg_gds, gds__specify_field_err, + /* must specify field name for view select expression */ + 0); + + /* determine the proper field name, replacing the default if necessary */ + + if (field) + field_string = field->fld_name; + if (ptr && ptr < end) { + field_name = (STR) (*ptr)->nod_arg[1]; + field_string = (TEXT *) field_name->str_data; + ptr++; + } + + /* if not an expression, point to the proper base relation field, + else make up an SQL field with generated global field for calculations */ + + if (field) { + put_cstring(request, gds_dyn_def_local_fld, field_string); + put_cstring(request, gds_dyn_fld_base_fld, field->fld_name); + put_number(request, gds_dyn_view_context, context->ctx_context); + } + else { + put_cstring(request, gds_dyn_def_sql_fld, field_string); + MAKE_desc(&field_node->nod_desc, field_node); + put_descriptor(request, &field_node->nod_desc); + begin_blr(request, gds_dyn_fld_computed_blr); + GEN_expr(request, field_node); + end_blr(request); + put_number(request, gds_dyn_view_context, (SSHORT) 0); + } + + save_field(request, field_string); + + put_number(request, gds_dyn_fld_position, position); + STUFF(gds_dyn_end); + } + + if (ptr != end) + ERRD_post(gds__sqlerr, gds_arg_number, (SLONG) - 607, + gds_arg_gds, gds__dsql_command_err, + gds_arg_gds, gds__num_field_err, + /* number of fields does not match select list */ + 0); + +/* setup to define triggers for WITH CHECK OPTION */ + + if ((check = node->nod_arg[e_view_check]) != NULL) { + if (!updatable) + ERRD_post(gds__sqlerr, gds_arg_number, (SLONG) - 607, + gds_arg_gds, gds__dsql_command_err, + gds_arg_gds, gds__col_name_err, + /* Only simple column names permitted for VIEW WITH CHECK OPTION */ + 0); + + if (select_expr->nod_count != 1) + ERRD_post(gds__sqlerr, gds_arg_number, (SLONG) - 607, + gds_arg_gds, gds__dsql_command_err, + gds_arg_gds, gds__table_view_err, + /* Only one table allowed for VIEW WITH CHECK OPTION */ + 0); + /* + Handle VIEWS with UNION : nod_select now points to nod_list + which in turn points to nod_select_expr + */ + else if (select_expr->nod_arg[0]->nod_arg[e_sel_from]->nod_count != 1) + ERRD_post(gds__sqlerr, gds_arg_number, (SLONG) - 607, + gds_arg_gds, gds__dsql_command_err, + gds_arg_gds, gds__table_view_err, + /* Only one table allowed for VIEW WITH CHECK OPTION */ + 0); + + /* + Handle VIEWS with UNION : nod_select now points to nod_list + which in turn points to nod_select_expr + */ + select_expr = select_expr->nod_arg[0]; + if (!(select_expr->nod_arg[e_sel_where])) + ERRD_post(gds__sqlerr, gds_arg_number, (SLONG) - 607, + gds_arg_gds, gds__dsql_command_err, + gds_arg_gds, gds__where_err, + /* No where clause for VIEW WITH CHECK OPTION */ + 0); + + + if (select_expr->nod_arg[e_sel_distinct] || + select_expr->nod_arg[e_sel_group] || + select_expr->nod_arg[e_sel_having]) + ERRD_post(gds__sqlerr, gds_arg_number, (SLONG) - 607, + gds_arg_gds, gds__dsql_command_err, + gds_arg_gds, gds__distinct_err, + /* DISTINCT, GROUP or HAVING not permitted for VIEW WITH CHECK OPTION */ + 0); + + relation_node = MAKE_node(nod_relation_name, e_rln_count); + relation_node->nod_arg[e_rln_name] = (NOD) view_name; + check->nod_arg[e_cnstr_table] = relation_node; + + check->nod_arg[e_cnstr_source] = (NOD) source; + + /* the condition for the trigger is the converse of the selection + criteria for the view, suitably fixed up so that the fields in + the view are referenced */ + + check->nod_arg[e_cnstr_condition] = select_expr->nod_arg[e_sel_where]; + + /* Define the triggers */ + + create_view_triggers(request, check, rse->nod_arg[e_rse_items]); + } + + STUFF(gds_dyn_end); + reset_context_stack(request); +} + + +static void define_view_trigger( REQ request, NOD node, NOD rse, NOD items) +{ /* The fields in VIEW actually */ +/************************************** + * + * d e f i n e _ v i e w _ t r i g g e r + * + ************************************** + * + * Function + * Create the ddl to define a trigger for a VIEW WITH CHECK OPTION. + * + **************************************/ + STR trigger_name, relation_name, message; + NOD temp_rse, temp, ddl_node, actions, *ptr, *end, constant; + NOD relation_node; + USHORT trig_type; + NOD action_node, condition, select, select_expr, view_fields; + CTX context, sav_context = 0; + LLS stack; + TSQL tdsql; + + tdsql = GET_THREAD_DATA; + + ddl_node = request->req_ddl_node; + + select = ddl_node->nod_arg[e_view_select]; +/* + Handle VIEWS with UNION : nod_select now points to nod_list + which in turn points to nod_select_expr +*/ + select_expr = select->nod_arg[0]->nod_arg[0]; + view_fields = ddl_node->nod_arg[e_view_fields]; + +/* make the "define trigger" node the current request ddl node so + that generating of BLR will be appropriate for trigger */ + + request->req_ddl_node = node; + + trigger_name = (STR) node->nod_arg[e_cnstr_name]; + + if (node->nod_type == nod_def_constraint) { + assert(trigger_name->str_length <= MAX_USHORT); + put_string(request, gds_dyn_def_trigger, trigger_name->str_data, + (USHORT) trigger_name->str_length); + relation_node = node->nod_arg[e_cnstr_table]; + relation_name = (STR) relation_node->nod_arg[e_rln_name]; + assert(relation_name->str_length <= MAX_USHORT); + put_string(request, gds_dyn_rel_name, relation_name->str_data, + (USHORT) relation_name->str_length); + } + else + return; + + if ((constant = node->nod_arg[e_cnstr_position]) != NULL) + put_number(request, gds_dyn_trg_sequence, + (SSHORT) (constant ? constant->nod_arg[0] : 0)); + + if ((constant = node->nod_arg[e_cnstr_type]) != NULL) { + trig_type = (USHORT) constant->nod_arg[0]; + put_number(request, gds_dyn_trg_type, trig_type); + } + else { + /* If we don't have a trigger type assigned, then this is just a template + definition for use with domains. The real triggers are defined when + the domain is used. */ + trig_type = 0; + } + + STUFF(gds_dyn_sql_object); + + if ((message = (STR) node->nod_arg[e_cnstr_message]) != NULL) { + put_number(request, gds_dyn_def_trigger_msg, 0); + assert(message->str_length <= MAX_USHORT); + put_string(request, gds_dyn_trg_msg, message->str_data, + (USHORT) message->str_length); + STUFF(gds_dyn_end); + } + +/* generate the trigger blr */ + + if (node->nod_arg[e_cnstr_condition] && node->nod_arg[e_cnstr_actions]) { + begin_blr(request, gds_dyn_trg_blr); + STUFF(blr_begin); + + /* create the "OLD" and "NEW" contexts for the trigger -- + the new one could be a dummy place holder to avoid resolving + fields to that context but prevent relations referenced in + the trigger actions from referencing the predefined "1" context */ + + if (request->req_context_number) { + /* If an alias is specified for the single base table involved, + save and then add the context */ + + stack = request->req_context; + context = (CTX) stack->lls_object; + if (context->ctx_alias) { + sav_context = (CTX) ALLOCD(type_ctx); + *sav_context = *context; + } + } + reset_context_stack(request); + temp = relation_node->nod_arg[e_rln_alias]; + relation_node->nod_arg[e_rln_alias] = (NOD) MAKE_cstring(OLD_CONTEXT); + PASS1_make_context(request, relation_node); + relation_node->nod_arg[e_rln_alias] = (NOD) MAKE_cstring(NEW_CONTEXT); + PASS1_make_context(request, relation_node); + relation_node->nod_arg[e_rln_alias] = temp; + + if (sav_context) { + sav_context->ctx_context = request->req_context_number++; + context->ctx_scope_level = request->req_scope_level; + LLS_PUSH(sav_context, &request->req_context); + } + + if (trig_type == PRE_MODIFY_TRIGGER) { + STUFF(blr_for); + temp = rse->nod_arg[e_rse_streams]; + temp->nod_arg[0] = PASS1_node(request, temp->nod_arg[0], 0); + temp = rse->nod_arg[e_rse_boolean]; + rse->nod_arg[e_rse_boolean] = PASS1_node(request, temp, 0); + GEN_expr(request, rse); + + condition = MAKE_node(nod_not, 1); + condition->nod_arg[0] = + replace_field_names(select_expr->nod_arg[e_sel_where], items, + view_fields, FALSE); + STUFF(blr_begin); + STUFF(blr_if); + GEN_expr(request, PASS1_node(request, condition->nod_arg[0], 0)); + STUFF(blr_begin); + STUFF(blr_end); + } + + if (trig_type == PRE_STORE_TRIGGER) { + condition = MAKE_node(nod_not, 1); + condition->nod_arg[0] = + replace_field_names(select_expr->nod_arg[e_sel_where], items, + view_fields, TRUE); + STUFF(blr_if); + GEN_expr(request, PASS1_node(request, condition->nod_arg[0], 0)); + STUFF(blr_begin); + STUFF(blr_end); + } + + /* generate the action statements for the trigger */ + + actions = node->nod_arg[e_cnstr_actions]; + for (ptr = actions->nod_arg, end = ptr + actions->nod_count; + ptr < end; ptr++) + GEN_statement(request, PASS1_statement(request, *ptr, 0)); + + /* generate the action statements for the trigger */ + + if ((actions = node->nod_arg[e_cnstr_else]) != NULL) { + STUFF(blr_begin); + for (ptr = actions->nod_arg, end = ptr + actions->nod_count; + ptr < end; ptr++) { + action_node = PASS1_statement(request, *ptr, 0); + if (action_node->nod_type == nod_modify) { + temp_rse = action_node->nod_arg[e_mod_rse]; + temp_rse->nod_arg[e_rse_first] = + MAKE_constant((STR) 1, 1); + } + GEN_statement(request, action_node); + } + STUFF(blr_end); /* of begin */ + } + + STUFF(blr_end); /* of if */ + if (trig_type == PRE_MODIFY_TRIGGER) + STUFF(blr_end); /* of for */ + end_blr(request); + } + + STUFF(gds_dyn_end); + +/* the request type may have been set incorrectly when parsing + the trigger actions, so reset it to reflect the fact that this + is a data definition request; also reset the ddl node */ + + request->req_type = REQ_DDL; + request->req_ddl_node = ddl_node; + reset_context_stack(request); +} + + +static void end_blr( REQ request) +{ +/************************************** + * + * e n d _ b l r + * + ************************************** + * + * Function + * Complete the stuffing of a piece of + * blr by going back and inserting the length. + * + **************************************/ + UCHAR *blr_base; + USHORT length; + + STUFF(blr_eoc); + +/* go back and stuff in the proper length */ + + blr_base = request->req_blr_string->str_data + request->req_base_offset; + length = (SSHORT) (request->req_blr - blr_base) - 2; + *blr_base++ = (UCHAR) length; + *blr_base = (UCHAR) (length >> 8); +} + + +static void foreign_key( REQ request, NOD element) +{ +/* ************************************* + * + * f o r e i g n _ k e y + * + ************************************** + * + * Function + * Generate ddl to create a foreign key + * constraint. + * + **************************************/ + NOD relation2_node; + NOD columns1, columns2; + STR relation2; + + columns1 = element->nod_arg[e_for_columns]; + + relation2_node = element->nod_arg[e_for_reftable]; + relation2 = (STR) relation2_node->nod_arg[e_rln_name]; + +/* If there is a referenced table name but no referenced field names, the + primary key of the referenced table designates the referenced fields. */ + if (!(columns2 = element->nod_arg[e_for_refcolumns])) { + element->nod_arg[e_for_refcolumns] = + columns2 = METD_get_primary_key(request, relation2); + + /* If there is NEITHER an explicitly referenced field name, NOR does + the referenced table have a primary key to serve as the implicitly + referenced field, fail. */ + if (!columns2) + ERRD_post(gds__sqlerr, gds_arg_number, (SLONG) - 607, + gds_arg_gds, gds__dsql_command_err, + gds_arg_gds, gds__reftable_requires_pk, + /* "REFERENCES table" without "(column)" requires PRIMARY + KEY on referenced table */ + 0); + } + + if (columns2 && (columns1->nod_count != columns2->nod_count)) + ERRD_post(gds__sqlerr, gds_arg_number, (SLONG) - 607, + gds_arg_gds, gds__dsql_command_err, + gds_arg_gds, gds__key_field_count_err, + /* foreign key field count does not match primary key */ + 0); + +/* define the foreign key index and the triggers that may be needed + for referential integrity action. */ + + make_index_trg_ref_int(request, element, columns1, + element->nod_arg[e_for_refcolumns], + reinterpret_cast(relation2->str_data)); +} + + +static void generate_dyn( REQ request, NOD node) +{ +/************************************** + * + * g e n e r a t e _ d y n + * + ************************************** + * + * Functional description + * Switch off the type of node to generate a + * DYN string. + * + **************************************/ + STR string; + + request->req_ddl_node = node; + + switch (node->nod_type) { + case nod_def_domain: + define_domain(request); + break; + + case nod_mod_domain: + modify_domain(request); + break; + + case nod_def_index: + define_index(request); + break; + + case nod_def_relation: + define_relation(request); + break; + + case nod_def_view: + define_view(request); + break; + + case nod_def_exception: + case nod_mod_exception: + case nod_del_exception: + define_exception(request, node->nod_type); + break; + + case nod_def_procedure: + case nod_mod_procedure: + define_procedure(request, node->nod_type); + break; + + case nod_def_constraint: + define_constraint_trigger(request, node); + break; + + case nod_def_trigger: + case nod_mod_trigger: + define_trigger(request, node); + break; + + case nod_mod_relation: + modify_relation(request); + break; + + case nod_del_domain: + string = (STR) node->nod_arg[0]; + put_cstring(request, gds_dyn_delete_global_fld, + reinterpret_cast(string->str_data)); + STUFF(gds_dyn_end); + break; + + case nod_del_index: + string = (STR) node->nod_arg[0]; + put_cstring(request, gds_dyn_delete_idx, + reinterpret_cast(string->str_data)); + STUFF(gds_dyn_end); + break; + + case nod_del_relation: + string = (STR) node->nod_arg[0]; + put_cstring(request, gds_dyn_delete_rel, + reinterpret_cast(string->str_data)); + STUFF(gds_dyn_end); + break; + + case nod_del_procedure: + string = (STR) node->nod_arg[0]; + put_cstring(request, gds_dyn_delete_procedure, + reinterpret_cast(string->str_data)); + STUFF(gds_dyn_end); + break; + + case nod_del_trigger: + string = (STR) node->nod_arg[0]; + put_cstring(request, gds_dyn_delete_trigger, + reinterpret_cast(string->str_data)); + STUFF(gds_dyn_end); + break; + + case nod_del_role: + string = (STR) node->nod_arg[0]; + put_cstring(request, isc_dyn_del_sql_role, + reinterpret_cast(string->str_data)); + STUFF(gds_dyn_end); + break; + + case nod_grant: + case nod_revoke: + grant_revoke(request); + break; + + case nod_def_generator: + define_generator(request); + break; + + case nod_def_role: + define_role(request); + break; + + case nod_def_filter: + define_filter(request); + break; + + case nod_del_generator: + string = (STR) node->nod_arg[0]; + /**********FIX -- nothing like delete_generator exists as yet + put_cstring (request, gds_dyn_def_generator, string->str_data); + STUFF (gds_dyn_end); */ + break; + + case nod_del_filter: + string = (STR) node->nod_arg[0]; + put_cstring(request, gds_dyn_delete_filter, + reinterpret_cast(string->str_data)); + STUFF(gds_dyn_end); + break; + + case nod_def_udf: + define_udf(request); + break; + + case nod_del_udf: + string = (STR) node->nod_arg[0]; + put_cstring(request, gds_dyn_delete_function, + reinterpret_cast(string->str_data)); + STUFF(gds_dyn_end); + break; + + case nod_def_shadow: + define_shadow(request); + break; + + case nod_del_shadow: + put_number(request, gds_dyn_delete_shadow, + (SSHORT) (node->nod_arg[0])); + STUFF(gds_dyn_end); + break; + + case nod_mod_database: + modify_database(request); + break; + + case nod_def_database: + define_database(request); + break; + + case nod_mod_index: + modify_index(request); + break; + + case nod_set_statistics: + set_statistics(request); + break; + + default: + break; + } +} + + +static void grant_revoke( REQ request) +{ +/************************************** + * + * g r a n t _ r e v o k e + * + ************************************** + * + * Functional description + * Build DYN string for GRANT/REVOKE statements + * + **************************************/ + NOD ddl_node, privs, table; + NOD users, *uptr, *uend; + SSHORT option; + NOD role_list, *role_ptr, *role_end; + BOOLEAN process_grant_role = FALSE; + + option = FALSE; + ddl_node = request->req_ddl_node; + + privs = ddl_node->nod_arg[e_grant_privs]; + + if (privs->nod_arg[0] != NULL) { + if (privs->nod_arg[0]->nod_type == nod_role_name) + process_grant_role = TRUE; + } + + if (!process_grant_role) + { + table = ddl_node->nod_arg[e_grant_table]; + users = ddl_node->nod_arg[e_grant_users]; + if (ddl_node->nod_arg[e_grant_grant]) { + option = TRUE; + } + + STUFF(gds_dyn_begin); + + for (uptr = users->nod_arg, uend = uptr + users->nod_count; + uptr < uend; + uptr++) + { + modify_privileges( request, + ddl_node->nod_type, + option, + privs, + table, + *uptr); + } + } + else + { + role_list = ddl_node->nod_arg[0]; + users = ddl_node->nod_arg[1]; + if (ddl_node->nod_arg[3]) + option = 2; + STUFF(isc_dyn_begin); + + for (role_ptr = role_list->nod_arg, + role_end = role_ptr + role_list->nod_count; + role_ptr < role_end; role_ptr++) { + for (uptr = users->nod_arg, uend = uptr + users->nod_count; + uptr < uend; uptr++) { + process_role_nm_list(request, option, *uptr, *role_ptr, + ddl_node->nod_type); + } + } + } + + STUFF(gds_dyn_end); +} + + +static void make_index( + REQ request, + NOD element, + NOD columns, + NOD referenced_columns, TEXT * relation_name) +{ +/* ************************************* + * + * m a k e _ i n d e x + * + ************************************** + * + * Function + * Generate ddl to create an index for a unique + * or primary key constraint. + * This is not called for a foreign key constraint. + * The func. make_index_trf_ref_int handles foreign key constraint + * + **************************************/ + NOD *ptr, *end; + STR field_name; + +/* stuff a zero-length name, indicating that an index + name should be generated */ + + assert(element->nod_type != nod_foreign) + + if (element->nod_type == nod_primary) + STUFF(gds_dyn_def_primary_key); + else if (element->nod_type == nod_unique) + STUFF(gds_dyn_def_unique); + STUFF_WORD(0); + + put_number(request, gds_dyn_idx_unique, 1); + + for (ptr = columns->nod_arg, end = ptr + columns->nod_count; ptr < end; + ptr++) { + field_name = (STR) (*ptr)->nod_arg[1]; + put_cstring(request, gds_dyn_fld_name, + reinterpret_cast(field_name->str_data)); + } + + STUFF(gds_dyn_end); +} + + +static void make_index_trg_ref_int( + REQ request, + NOD element, + NOD columns, + NOD referenced_columns, + TEXT * relation_name) +{ +/****************************************************** + * + * m a k e _ i n d e x _ t r g _ r e f _ i n t + * + ****************************************************** + * + * Function + * This is called only when the element->nod_type == nod_foreign_key + * + * o Generate ddl to create an index for a unique + * or primary key constraint. + * o Also make an index for the foreign key constraint + * o in the caase of foreign key, also generate an appropriate trigger for + * cascading referential integrity. + * + * + *****************************************************/ + NOD *ptr, *end; + STR field_name; + NOD for_rel_node, ddl_node; + STR for_rel_name_str; + NOD nod_for_action, nod_ref_upd_action, nod_ref_del_action; + + assert(element->nod_type == nod_foreign) + +/* for_rel_name_str is the name of the relation on which the ddl operation + is being done, in this case the foreign key table */ + ddl_node = request->req_ddl_node; + for_rel_node = ddl_node->nod_arg[e_drl_name]; + for_rel_name_str = (STR) for_rel_node->nod_arg[e_rln_name]; + + +/* stuff a zero-length name, indicating that an index + name should be generated */ + + STUFF(gds_dyn_def_foreign_key); + STUFF_WORD(0); + + + if (element->nod_arg[e_for_action]) { + nod_for_action = element->nod_arg[e_for_action]; + assert(nod_for_action->nod_type == nod_ref_upd_del); + + nod_ref_upd_action = nod_for_action->nod_arg[e_ref_upd]; + if (nod_ref_upd_action) { + assert(nod_ref_upd_action->nod_type == nod_ref_trig_action); + + STUFF(gds_dyn_foreign_key_update); + switch (nod_ref_upd_action->nod_flags) { + case REF_ACTION_CASCADE: + STUFF(gds_dyn_foreign_key_cascade); + define_upd_cascade_trg(request, element, columns, + referenced_columns, relation_name, + reinterpret_cast < + char *>(for_rel_name_str->str_data)); + break; + case REF_ACTION_SET_DEFAULT: + STUFF(gds_dyn_foreign_key_default); + define_set_default_trg(request, element, columns, + referenced_columns, relation_name, + reinterpret_cast < + char *>(for_rel_name_str->str_data), + TRUE); + break; + case REF_ACTION_SET_NULL: + STUFF(gds_dyn_foreign_key_null); + define_set_null_trg(request, element, columns, + referenced_columns, relation_name, + reinterpret_cast < + char *>(for_rel_name_str->str_data), + TRUE); + break; + case REF_ACTION_NONE: + STUFF(gds_dyn_foreign_key_none); + break; + default: + assert(0); + STUFF(gds_dyn_foreign_key_none); /* just in case */ + break; + } + } + + nod_ref_del_action = nod_for_action->nod_arg[e_ref_del]; + if (nod_ref_del_action) { + assert(nod_ref_del_action->nod_type == nod_ref_trig_action); + + STUFF(gds_dyn_foreign_key_delete); + switch (nod_ref_del_action->nod_flags) { + case REF_ACTION_CASCADE: + STUFF(gds_dyn_foreign_key_cascade); + define_del_cascade_trg(request, element, columns, + referenced_columns, relation_name, + reinterpret_cast < + char *>(for_rel_name_str->str_data)); + break; + case REF_ACTION_SET_DEFAULT: + STUFF(gds_dyn_foreign_key_default); + define_set_default_trg(request, element, columns, + referenced_columns, relation_name, + reinterpret_cast < + char *>(for_rel_name_str->str_data), + FALSE); + break; + case REF_ACTION_SET_NULL: + STUFF(gds_dyn_foreign_key_null); + define_set_null_trg(request, element, columns, + referenced_columns, relation_name, + reinterpret_cast < + char *>(for_rel_name_str->str_data), + FALSE); + break; + case REF_ACTION_NONE: + STUFF(gds_dyn_foreign_key_none); + break; + default: + assert(0); + STUFF(gds_dyn_foreign_key_none); /* just in case */ + break; + /* Error */ + } + } + } + + + for (ptr = columns->nod_arg, end = ptr + columns->nod_count; ptr < end; + ptr++) { + field_name = (STR) (*ptr)->nod_arg[1]; + put_cstring(request, gds_dyn_fld_name, + reinterpret_cast(field_name->str_data)); + } + + put_cstring(request, gds_dyn_idx_foreign_key, relation_name); + if (referenced_columns) + for (ptr = referenced_columns->nod_arg, end = + ptr + referenced_columns->nod_count; ptr < end; ptr++) { + field_name = (STR) (*ptr)->nod_arg[1]; + put_cstring(request, gds_dyn_idx_ref_column, + reinterpret_cast(field_name->str_data)); + } + + STUFF(gds_dyn_end); +} + + +static void modify_database( REQ request) +{ +/************************************** + * + * m o d i f y _ d a t a b a s e + * + ************************************** + * + * Function + * Modify a database. + * + **************************************/ + NOD ddl_node, elements, element, *ptr, *end; + SLONG start = 0; + FIL file; + SSHORT number = 0; + SLONG temp_long; + SSHORT temp_short; + SSHORT drop_log = FALSE; + SSHORT drop_cache = FALSE; + + ddl_node = request->req_ddl_node; + + STUFF(gds_dyn_mod_database); +/* +put_number (request, gds_dyn_rel_sql_protection, 1); +*/ + elements = ddl_node->nod_arg[e_adb_all]; + for (ptr = elements->nod_arg, end = ptr + elements->nod_count; + ptr < end; ptr++) { + element = *ptr; + switch (element->nod_type) { + case nod_drop_log: + drop_log = TRUE; + break; + case nod_drop_cache: + drop_cache = TRUE; + break; + + default: + break; + } + } + + if (drop_log) + STUFF(gds_dyn_drop_log); + if (drop_cache) + STUFF(gds_dyn_drop_cache); + + elements = ddl_node->nod_arg[e_adb_all]; + for (ptr = elements->nod_arg, end = ptr + elements->nod_count; + ptr < end; ptr++) { + element = *ptr; + + switch (element->nod_type) { + case nod_file_desc: + file = (FIL) element->nod_arg[0]; + put_cstring(request, gds_dyn_def_file, + reinterpret_cast(file->fil_name->str_data)); + STUFF(gds_dyn_file_start); + STUFF_WORD(4); + start = MAX(start, file->fil_start); + + STUFF_DWORD(start); + + STUFF(gds_dyn_file_length); + STUFF_WORD(4); + STUFF_DWORD(file->fil_length); + STUFF(gds_dyn_end); + start += file->fil_length; + break; + + case nod_log_file_desc: + file = (FIL) element->nod_arg[0]; + + if (file->fil_flags & LOG_default) { + STUFF(gds_dyn_def_default_log); + break; + } + put_cstring(request, gds_dyn_def_log_file, + reinterpret_cast(file->fil_name->str_data)); + STUFF(gds_dyn_file_length); + STUFF_WORD(4); + STUFF_DWORD(file->fil_length); + STUFF(gds_dyn_log_file_sequence); + STUFF_WORD(2); + STUFF_WORD(number); + number++; + STUFF(gds_dyn_log_file_partitions); + STUFF_WORD(2); + STUFF_WORD(file->fil_partitions); + if (file->fil_flags & LOG_serial) + STUFF(gds_dyn_log_file_serial); + if (file->fil_flags & LOG_overflow) + STUFF(gds_dyn_log_file_overflow); + if (file->fil_flags & LOG_raw) + STUFF(gds_dyn_log_file_raw); + STUFF(gds_dyn_end); + break; + + case nod_cache_file_desc: + file = (FIL) element->nod_arg[0]; + put_cstring(request, gds_dyn_def_cache_file, + reinterpret_cast(file->fil_name->str_data)); + STUFF(gds_dyn_file_length); + STUFF_WORD(4); + STUFF_DWORD(file->fil_length); + STUFF(gds_dyn_end); + break; + + case nod_group_commit_wait: + STUFF(gds_dyn_log_group_commit_wait); + temp_long = (SLONG) (element->nod_arg[0]); + STUFF_WORD(4); + STUFF_DWORD(temp_long); + break; + + case nod_check_point_len: + STUFF(gds_dyn_log_check_point_length); + temp_long = (SLONG) (element->nod_arg[0]); + STUFF_WORD(4); + STUFF_DWORD(temp_long); + break; + + case nod_num_log_buffers: + STUFF(gds_dyn_log_num_of_buffers); + temp_short = (SSHORT) (element->nod_arg[0]); + STUFF_WORD(2); + STUFF_WORD(temp_short); + break; + + case nod_log_buffer_size: + STUFF(gds_dyn_log_buffer_size); + temp_short = (SSHORT) (element->nod_arg[0]); + STUFF_WORD(2); + STUFF_WORD(temp_short); + break; + case nod_drop_log: + case nod_drop_cache: + break; + + default: + break; + } + } + + STUFF(gds_dyn_end); +} + + +static void modify_domain( REQ request) +{ +/************************************** + * + * m o d i f y _ d o m a i n + * + ************************************** + * + * Function + * Alter an SQL domain. + * + **************************************/ + NOD ddl_node, domain_node, ops, element, *ptr, *end; + STR string, domain_name; + FLD field; + struct fld local_field; + + ddl_node = request->req_ddl_node; + + domain_node = ddl_node->nod_arg[e_alt_dom_name]; + domain_name = (STR) domain_node->nod_arg[e_fln_name]; + + put_cstring(request, gds_dyn_mod_global_fld, + reinterpret_cast(domain_name->str_data)); + + ops = ddl_node->nod_arg[e_alt_dom_ops]; + for (ptr = ops->nod_arg, end = ptr + ops->nod_count; ptr < end; ptr++) + { + element = *ptr; + switch (element->nod_type) + { + case nod_def_default: + /* CVC: So do you want to crash me with ALTER DOMAIN dom SET; ??? */ + if (!element->nod_arg[e_dft_default]) + { + ERRD_post (gds__sqlerr, gds_arg_number, (SLONG) -104, + gds_arg_gds, gds__command_end_err, /* Unexpected end of command */ + 0); + } + /* CVC End modification. */ + element->nod_arg[e_dft_default] = + PASS1_node(request, element->nod_arg[e_dft_default], 0); + begin_blr(request, gds_dyn_fld_default_value); + GEN_expr(request, element->nod_arg[e_dft_default]); + end_blr(request); + if ((string = (STR) element->nod_arg[e_dft_default_source]) != + NULL) { + assert(string->str_length <= MAX_USHORT); + put_string(request, gds_dyn_fld_default_source, + string->str_data, (USHORT) string->str_length); + } + break; + + case nod_def_constraint: + STUFF(gds_dyn_single_validation); + begin_blr(request, gds_dyn_fld_validation_blr); + + /* Get the attributes of the domain, and set any occurances of + nod_dom_value (corresponding to the keyword VALUE) to the + correct type, length, scale, etc. */ + if (!METD_get_domain(request, &local_field, + domain_name->str_data)) + ERRD_post(gds__sqlerr, gds_arg_number, (SLONG) - 607, + gds_arg_gds, gds__dsql_command_err, + gds_arg_gds, gds__dsql_domain_not_found, + /* Specified domain or source field does not exist */ + 0); + if (element->nod_arg[e_cnstr_condition]) + set_nod_value_attributes(element->nod_arg[e_cnstr_condition], + &local_field); + + /* Increment the context level for this request, so that + the context number for any RSE generated for a SELECT + within the CHECK clause will be greater than 0. In the + environment of a domain check constraint, context + number 0 is reserved for the "blr_fid, 0, 0,0," which + is emitted for a nod_dom_value, corresponding to an + occurance of the VALUE keyword in the body of the check + constraint. -- chrisj 1999-08-20 */ + + request->req_context_number++; + + GEN_expr(request, + PASS1_node(request, element->nod_arg[e_cnstr_condition], + 0)); + + end_blr(request); + if ((string = (STR) element->nod_arg[e_cnstr_source]) != NULL) { + assert(string->str_length <= MAX_USHORT); + put_string(request, gds_dyn_fld_validation_source, + string->str_data, (USHORT) string->str_length); + } + break; + + case nod_mod_domain_type: + field = (FLD) element->nod_arg[e_mod_dom_new_dom_type]; + DDL_resolve_intl_type(request, field, NULL); + put_field(request, field, FALSE); + break; + + case nod_field_name: + { + STR new_dom_name; + + new_dom_name = (STR) element->nod_arg[e_fln_name]; + put_cstring(request, gds_dyn_fld_name, + reinterpret_cast < + char *>(new_dom_name->str_data)); + break; + } + + case nod_delete_rel_constraint: + STUFF(gds_dyn_del_validation); + break; + + case nod_del_default: + STUFF(gds_dyn_del_default); + break; + + default: + break; + } + } + + STUFF(gds_dyn_end); +} + + +static void modify_index( REQ request) +{ +/************************************** + * + * m o d i f y _ i n d e x + * + ************************************** + * + * Function + * Alter an index (only active or inactive for now) + * + **************************************/ + NOD ddl_node, index_node; + STR index_name; + + ddl_node = request->req_ddl_node; + + index_node = ddl_node->nod_arg[e_alt_index]; + index_name = (STR) index_node->nod_arg[e_alt_idx_name]; + + put_cstring(request, gds_dyn_mod_idx, + reinterpret_cast(index_name->str_data)); + + if (index_node->nod_type == nod_idx_active) + put_number(request, gds_dyn_idx_inactive, 0); + else if (index_node->nod_type == nod_idx_inactive) + put_number(request, gds_dyn_idx_inactive, 1); + + STUFF(gds_dyn_end); +} + + +static void modify_privilege( + REQ request, + NOD_TYPE type, + SSHORT option, + UCHAR * privs, +NOD table, NOD user, STR field_name) +{ +/************************************** + * + * m o d i f y _ p r i v i l e g e + * + ************************************** + * + * Functional description + * Stuff a single grant/revoke verb and all its options. + * + **************************************/ + SSHORT priv_count, i; + UCHAR *dynsave; + STR name; + + if (type == nod_grant) + STUFF(gds_dyn_grant); + else + STUFF(gds_dyn_revoke); + +/* stuff the privileges string */ + + priv_count = 0; + STUFF_WORD(0); + for (; *privs; privs++) { + priv_count++; + STUFF(*privs); + } + + dynsave = request->req_blr; + for (i = priv_count + 2; i; i--) + --dynsave; + + *dynsave++ = (UCHAR) priv_count; + *dynsave = (UCHAR) (priv_count >> 8); + + name = (STR) table->nod_arg[0]; + if (table->nod_type == nod_procedure_name) + put_cstring(request, gds_dyn_prc_name, + reinterpret_cast(name->str_data)); + else + put_cstring(request, gds_dyn_rel_name, + reinterpret_cast(name->str_data)); + name = (STR) user->nod_arg[0]; + switch (user->nod_type) { + case nod_user_group: /* GRANT priv ON tbl TO GROUP unix_group */ + put_cstring(request, isc_dyn_grant_user_group, + reinterpret_cast(name->str_data)); + break; + + case nod_user_name: + put_cstring(request, gds_dyn_grant_user, + reinterpret_cast(name->str_data)); + break; + + case nod_proc_obj: + put_cstring(request, gds_dyn_grant_proc, + reinterpret_cast(name->str_data)); + break; + + case nod_trig_obj: + put_cstring(request, gds_dyn_grant_trig, + reinterpret_cast(name->str_data)); + break; + + case nod_view_obj: + put_cstring(request, gds_dyn_grant_view, + reinterpret_cast(name->str_data)); + break; + + default: + break; + } + + if (field_name) { + put_cstring(request, + gds_dyn_fld_name, + reinterpret_cast(field_name->str_data)); + } + + if ((option) && + ((type == nod_grant) || + (!(request->req_dbb->dbb_flags & DBB_v3)))) + { + put_number(request, gds_dyn_grant_options, option); + } + + STUFF(gds_dyn_end); +} + + + +static SCHAR modify_privileges(REQ request, + NOD_TYPE type, + SSHORT option, + NOD privs, + NOD table, + NOD user) +{ +/************************************** + * + * m o d i f y _ p r i v i l e g e s + * + ************************************** + * + * Functional description + * Return a SCHAR indicating the privilege to be modified + * + **************************************/ + SCHAR privileges[10], *p; + NOD fields, *ptr, *end; + + switch (privs->nod_type) { + case nod_all: + p = "A"; + break; + + case nod_select: + return 'S'; + + case nod_execute: + return 'X'; + + case nod_insert: + return 'I'; + + case nod_references: + case nod_update: + p = (privs->nod_type == nod_references) ? "R" : "U"; + if (!(fields = privs->nod_arg[0])) + return *p; + + for (ptr = fields->nod_arg, end = ptr + fields->nod_count; ptr < end; + ptr++) + modify_privilege(request, type, option, + reinterpret_cast < UCHAR * >(p), table, user, + reinterpret_cast < str * >((*ptr)->nod_arg[1])); + return 0; + + case nod_delete: + return 'D'; + + case nod_list: + p = privileges; + for (ptr = privs->nod_arg, end = ptr + privs->nod_count; ptr < end; + ptr++) + if (*p = + modify_privileges(request, type, option, *ptr, table, + user)) p++; + *p = 0; + p = privileges; + break; + + default: + break; + } + + if (*p) { + modify_privilege( request, + type, + option, + reinterpret_cast(p), + table, + user, + 0); + } + + return 0; +} + + +static void modify_relation( REQ request) +{ +/************************************** + * + * m o d i f y _ r e l a t i o n + * + ************************************** + * + * Function + * Alter an SQL table, relying on DYN to generate + * global fields for the local fields. + * + **************************************/ + NOD ddl_node, ops, element, *ptr, *end, relation_node, field_node; + STR relation_name, field_name; + JMP_BUF *old_env, env; + TSQL tdsql; + + + tdsql = GET_THREAD_DATA; + + ddl_node = request->req_ddl_node; + + relation_node = ddl_node->nod_arg[e_alt_name]; + relation_name = (STR) relation_node->nod_arg[e_rln_name]; + + put_cstring(request, gds_dyn_mod_rel, + reinterpret_cast(relation_name->str_data)); + save_relation(request, relation_name); + +/* need to handle error that occur in generating dyn string. + * If there is an error, get rid of the cached data + */ + + old_env = tdsql->tsql_setjmp; + tdsql->tsql_setjmp = &env; + + if (SETJMP(*tdsql->tsql_setjmp)) { + METD_drop_relation(request, relation_name); + request->req_relation = 0; + tdsql->tsql_setjmp = old_env; + LONGJMP(*tdsql->tsql_setjmp, (int) tdsql->tsql_status[1]); + } + + ops = ddl_node->nod_arg[e_alt_ops]; + for (ptr = ops->nod_arg, end = ptr + ops->nod_count; ptr < end; ptr++) { + element = *ptr; + switch (element->nod_type) { + case nod_mod_field_name: + { + NOD old_field, new_field; + STR old_field_name, new_field_name; + + old_field = element->nod_arg[e_mod_fld_name_orig_name]; + old_field_name = (STR) old_field->nod_arg[e_fln_name]; + put_cstring(request, gds_dyn_mod_local_fld, + reinterpret_cast < + char *>(old_field_name->str_data)); + + new_field = element->nod_arg[e_mod_fld_name_new_name]; + new_field_name = (STR) new_field->nod_arg[e_fln_name]; + put_cstring(request, gds_dyn_rel_name, + reinterpret_cast < + char *>(relation_name->str_data)); + put_cstring(request, isc_dyn_new_fld_name, + reinterpret_cast < + char *>(new_field_name->str_data)); + STUFF(gds_dyn_end); + break; + } + + case nod_mod_field_pos: + { + SSHORT constant; + NOD const_node; + + field_node = element->nod_arg[e_mod_fld_pos_orig_name]; + field_name = (STR) field_node->nod_arg[e_fln_name]; + put_cstring(request, gds_dyn_mod_local_fld, + reinterpret_cast(field_name->str_data)); + + const_node = element->nod_arg[e_mod_fld_pos_new_position]; + constant = (SSHORT) const_node->nod_arg[0]; + put_cstring(request, gds_dyn_rel_name, + reinterpret_cast < + char *>(relation_name->str_data)); + put_number(request, gds_dyn_fld_position, constant); + STUFF(gds_dyn_end); + break; + } + + + case nod_mod_field_type: + modify_field(request, element, (SSHORT) - 1, relation_name); + break; + + case nod_def_field: + define_field(request, element, (SSHORT) - 1, relation_name); + break; + + case nod_del_field: + + /* Fix for bug 8054: + + [CASCADE | RESTRICT] syntax is available in IB4.5, but not + required until v5.0. + + Option CASCADE causes an error : + unsupported DSQL construct + + Option RESTRICT is default behaviour. + */ + + field_node = element->nod_arg[0]; + field_name = (STR) field_node->nod_arg[e_fln_name]; + + if ((element->nod_arg[1])->nod_type == nod_cascade) + /* Unsupported DSQL construct */ + ERRD_post(gds__sqlerr, gds_arg_number, (SLONG) - 901, + gds_arg_gds, gds__dsql_command_err, + gds_arg_gds, gds__dsql_construct_err, 0); + + assert((element->nod_arg[1])->nod_type == nod_restrict); + put_cstring(request, gds_dyn_delete_local_fld, + reinterpret_cast(field_name->str_data)); + STUFF(gds_dyn_end); + break; + + case nod_delete_rel_constraint: + field_name = (STR) element->nod_arg[0]; + put_cstring(request, gds_dyn_delete_rel_constraint, + reinterpret_cast(field_name->str_data)); + break; + + case nod_rel_constraint: + define_rel_constraint(request, element); + break; + + default: + break; + } + } + + STUFF(gds_dyn_end); + +/* restore the setjmp pointer */ + + tdsql->tsql_setjmp = old_env; +} + + +static void process_role_nm_list( + REQ request, + SSHORT option, + NOD user_ptr, NOD role_ptr, NOD_TYPE type) +{ +/************************************** + * + * p r o c e s s _ r o l e _ n m _ l i s t + * + ************************************** + * + * Functional description + * Build req_blr for grant & revoke role stmt + * + **************************************/ + STR role_nm, user_nm; + + if (type == nod_grant) + STUFF(isc_dyn_grant); + else + STUFF(isc_dyn_revoke); + + STUFF_WORD(1); + STUFF('M'); + + role_nm = (STR) role_ptr->nod_arg[0]; + put_cstring(request, isc_dyn_sql_role_name, + reinterpret_cast(role_nm->str_data)); + + user_nm = (STR) user_ptr->nod_arg[0]; + put_cstring(request, isc_dyn_grant_user, + reinterpret_cast(user_nm->str_data)); + + if (option) + put_number(request, isc_dyn_grant_admin_options, option); + + STUFF(gds_dyn_end); +} + + +static void put_descriptor( REQ request, DSC * desc) +{ +/************************************** + * + * p u t _ d e s c r i p t o r + * + ************************************** + * + * Function + * Write out field description in ddl, given the + * input descriptor. + * + **************************************/ + + put_number(request, gds_dyn_fld_type, blr_dtypes[desc->dsc_dtype]); + if (desc->dsc_dtype == dtype_varying) + put_number(request, gds_dyn_fld_length, + (SSHORT) (desc->dsc_length - sizeof(USHORT))); + else + put_number(request, gds_dyn_fld_length, desc->dsc_length); + put_number(request, gds_dyn_fld_scale, desc->dsc_scale); +} + + +static void put_dtype( REQ request, FLD field, USHORT use_subtype) +{ +/************************************** + * + * p u t _ d t y p e + * + ************************************** + * + * Function + * Write out field data type + * Taking special care to declare international text. + * + **************************************/ + +#ifdef DEV_BUILD +/* Check if the field describes a known datatype */ + + if (field->fld_dtype > sizeof(blr_dtypes) / sizeof(blr_dtypes[0]) || + !blr_dtypes[field->fld_dtype]) { + SCHAR buffer[100]; + + sprintf(buffer, "Invalid dtype %d in put_dtype", field->fld_dtype); + ERRD_bugcheck(buffer); + } +#endif + + if (field->fld_dtype == dtype_cstring || + field->fld_dtype == dtype_text || field->fld_dtype == dtype_varying) { + if (!use_subtype || (request->req_dbb->dbb_flags & DBB_v3)) { + STUFF(blr_dtypes[field->fld_dtype]); + } + else if (field->fld_dtype == dtype_varying) { + STUFF(blr_varying2); + STUFF_WORD(field->fld_ttype); + } + else if (field->fld_dtype == dtype_cstring) { + STUFF(blr_cstring2); + STUFF_WORD(field->fld_ttype); + } + else { + STUFF(blr_text2); + STUFF_WORD(field->fld_ttype); + } + + if (field->fld_dtype == dtype_varying) + STUFF_WORD(field->fld_length - sizeof(USHORT)) + else + STUFF_WORD(field->fld_length); + } + else { + STUFF(blr_dtypes[field->fld_dtype]); + if (DTYPE_IS_EXACT(field->fld_dtype) || + (dtype_quad == field->fld_dtype)) { + STUFF(field->fld_scale); + } + } +} + + +static void put_field( REQ request, FLD field, BOOLEAN udf_flag) +{ +/************************************** + * + * p u t _ f i e l d + * + ************************************** + * + * Function + * Emit dyn which describes a field data type. + * This field could be a column, a procedure input, + * or a procedure output. + * + **************************************/ + + put_number(request, gds_dyn_fld_type, blr_dtypes[field->fld_dtype]); + if (field->fld_dtype == dtype_blob) { + put_number(request, gds_dyn_fld_sub_type, field->fld_sub_type); + put_number(request, gds_dyn_fld_scale, 0); + if (!udf_flag) { + if (!field->fld_seg_length) + field->fld_seg_length = DEFAULT_BLOB_SEGMENT_SIZE; + put_number(request, gds_dyn_fld_segment_length, + field->fld_seg_length); + } + if (!(request->req_dbb->dbb_flags & DBB_v3)) + if (field->fld_sub_type == BLOB_text) + put_number(request, gds_dyn_fld_character_set, + field->fld_character_set_id); + } + else if (field->fld_dtype <= dtype_any_text) { + if (field->fld_sub_type) + put_number(request, gds_dyn_fld_sub_type, field->fld_sub_type); + put_number(request, gds_dyn_fld_scale, 0); + if (field->fld_dtype == dtype_varying) { + assert((field->fld_length - sizeof(USHORT)) <= MAX_SSHORT); + put_number(request, gds_dyn_fld_length, + (SSHORT) (field->fld_length - sizeof(USHORT))); + } + else + put_number(request, gds_dyn_fld_length, field->fld_length); + if (!(request->req_dbb->dbb_flags & DBB_v3)) { + put_number(request, gds_dyn_fld_char_length, + field->fld_character_length); + put_number(request, gds_dyn_fld_character_set, + field->fld_character_set_id); + if (!udf_flag) + put_number(request, gds_dyn_fld_collation, + field->fld_collation_id); + } + } + else { + put_number(request, gds_dyn_fld_scale, field->fld_scale); + put_number(request, gds_dyn_fld_length, field->fld_length); + if (DTYPE_IS_EXACT(field->fld_dtype)) { + put_number(request, isc_dyn_fld_precision, field->fld_precision); + if (field->fld_sub_type) + put_number(request, isc_dyn_fld_sub_type, + field->fld_sub_type); + } + } +} + + +static void put_local_variable( REQ request, VAR variable) +{ +/************************************** + * + * p u t _ l o c a l _ v a r i a b l e + * + ************************************** + * + * Function + * Write out local variable field data type + * + **************************************/ + FLD field; + USHORT dtype; + + field = variable->var_field; + STUFF(blr_dcl_variable); + STUFF_WORD(variable->var_variable_number); + DDL_resolve_intl_type(request, field, NULL); + if ((dtype = field->fld_dtype) == dtype_blob) + field->fld_dtype = dtype_quad; + put_dtype(request, field, TRUE); + field->fld_dtype = dtype; + +/* Initialize variable to NULL */ + + STUFF(blr_assignment); + STUFF(blr_null); + STUFF(blr_variable); + STUFF_WORD(variable->var_variable_number); +} + + +static SSHORT put_local_variables( REQ request, NOD parameters, SSHORT locals) +{ +/************************************** + * + * p u t _ l o c a l _ v a r i a b l e s + * + ************************************** + * + * Function + * Emit dyn for the local variables declared + * in a procedure or trigger. + * + **************************************/ + NOD *rest, parameter, *ptr, *end, var_node; + FLD field, rest_field; + VAR variable; + + if (parameters) { + ptr = parameters->nod_arg; + for (end = ptr + parameters->nod_count; ptr < end; ptr++) { + parameter = *ptr; + field = (FLD) parameter->nod_arg[e_dfl_field]; + rest = ptr; + while ((++rest) != end) { + rest_field = (FLD) (*rest)->nod_arg[e_dfl_field]; + if (!strcmp(field->fld_name, rest_field->fld_name)) + ERRD_post(gds__sqlerr, gds_arg_number, (SLONG) - 637, + gds_arg_gds, gds__dsql_duplicate_spec, + gds_arg_string, field->fld_name, 0); + } + *ptr = var_node = + MAKE_variable(field, field->fld_name, VAR_output, 0, 0, + locals); + variable = (VAR) var_node->nod_arg[e_var_variable]; + put_local_variable(request, variable); + locals++; + } + } + + return locals; +} + + +static void put_msg_field( REQ request, FLD field) +{ +/************************************** + * + * p u t _ m s g _ f i e l d + * + ************************************** + * + * Function + * Write out message field data type + * + **************************************/ + USHORT dtype; + + if ((dtype = field->fld_dtype) == dtype_blob) + field->fld_dtype = dtype_quad; + put_dtype(request, field, TRUE); + field->fld_dtype = dtype; + +/* add slot for null flag (parameter2) */ + + STUFF(blr_short); + STUFF(0); +} + + +static void put_number( REQ request, UCHAR verb, SSHORT number) +{ +/************************************** + * + * p u t _ n u m b e r + * + ************************************** + * + * Input + * blr_ptr: current position of blr being generated + * verb: blr byte of which number is an argument + * number: value to be written to blr + * Function + * Write out a numeric valued attribute. + * + **************************************/ + + if (verb) + STUFF(verb); + + STUFF_WORD(2); + STUFF_WORD(number); +} + + +static void put_cstring( REQ request, UCHAR verb, char *string) +{ +/************************************** + * + * p u t _ c s t r i n g + * + ************************************** + * + * Function + * Write out a string valued attribute. + * + **************************************/ + USHORT length; + + if (string) + length = strlen(string); + else + length = 0; + + put_string(request, verb, reinterpret_cast < UCHAR * >(string), length); +} + + +static void put_string( + REQ request, UCHAR verb, UCHAR * string, USHORT length) +{ +/************************************** + * + * p u t _ s t r i n g + * + ************************************** + * + * Function + * Write out a string valued attribute. + * + **************************************/ + + if (verb) { + STUFF(verb); + STUFF_WORD(length); + } + else + STUFF(length); + + if (string) + for (; *string && length--; string++) + STUFF(*string); +} + + +static NOD replace_field_names( + NOD input, + NOD search_fields, + NOD replace_fields, SSHORT null_them) +{ +/* ************************************* + * + * r e p l a c e _ f i e l d _ n a m e s + * + ************************************** + * + * Function + * Given an input node tree, find any field name nodes + * and replace them according to the mapping provided. + * Used to create view WITH CHECK OPTION. + * + **************************************/ + NOD *endo, field_node, *ptr, *end, *search, *replace; + STR field_name, replace_name; + TEXT *search_name; + FLD field; + SSHORT found; + + if (!input) + return input; + + if (input->nod_header.blk_type != type_nod) + return input; + + for (ptr = input->nod_arg, endo = ptr + input->nod_count; ptr < endo; + ptr++) { + + if ((*ptr)->nod_type == nod_select_expr) + ERRD_post(gds__sqlerr, gds_arg_number, (SLONG) - 607, + gds_arg_gds, gds__dsql_command_err, + gds_arg_gds, gds__subquery_err, + /* No subqueries permitted for VIEW WITH CHECK OPTION */ + 0); + + if ((*ptr)->nod_type == nod_field_name) { + /* found a field node, check if it needs to be replaced */ + + field_name = (STR) (*ptr)->nod_arg[e_fln_name]; + + search = search_fields->nod_arg; + end = search + search_fields->nod_count; + if (replace_fields) + replace = replace_fields->nod_arg; + found = FALSE; + for (; search < end; + search++, (replace_fields) ? replace++ : NULL) { + if (replace_fields) { + replace_name = (STR) (*replace)->nod_arg[e_fln_name]; + } + field_node = *search; + field = (FLD) field_node->nod_arg[e_fld_field]; + search_name = field->fld_name; + if (!strcmp + ((SCHAR *) field_name->str_data, (SCHAR *) search_name)) { + found = TRUE; + if (replace_fields) + (*ptr)->nod_arg[e_fln_name] = (*replace)->nod_arg[e_fln_name]; + (*ptr)->nod_arg[e_fln_context] = (NOD) MAKE_cstring(NEW_CONTEXT); + + } + if (null_them && + replace_fields && + !strcmp((SCHAR *) field_name->str_data, + (SCHAR *) replace_name->str_data)) found = TRUE; + } + if (null_them && !found) { + (*ptr) = MAKE_node(nod_null, (int) 0); + } + } + else + /* recursively go through the input tree looking for field name nodes */ + replace_field_names(*ptr, search_fields, replace_fields, + null_them); + } + + return input; +} + + +static void reset_context_stack( REQ request) +{ +/************************************** + * + * r e s e t _ c o n t e x t _ s t a c k + * + ************************************** + * + * Function + * Get rid of any predefined contexts created + * for a view or trigger definition. + * + **************************************/ + + while (request->req_context) + LLS_POP(&request->req_context); + request->req_context_number = 0; +} + + +static void save_field( REQ request, TEXT * field_name) +{ +/************************************** + * + * s a v e _ f i e l d + * + ************************************** + * + * Function + * Save the name of a field in the relation or view currently + * being defined. This is done to support definition + * of triggers which will depend on the metadata created + * in this request. + * + **************************************/ + DSQL_REL relation; + FLD field; + TSQL tdsql; + + tdsql = GET_THREAD_DATA; + + if (!(relation = request->req_relation)) + return; + + field = (FLD) ALLOCDV(type_fld, strlen(field_name) + 1); + strcpy(field->fld_name, field_name); + field->fld_next = relation->rel_fields; + relation->rel_fields = field; +} + + +static void save_relation( REQ request, STR relation_name) +{ +/************************************** + * + * s a v e _ r e l a t i o n + * + ************************************** + * + * Function + * Save the name of the relation or view currently + * being defined. This is done to support definition + * of triggers which will depend on the metadata created + * in this request. + * + **************************************/ + DSQL_REL relation; + NOD ddl_node; + TSQL tdsql; + + tdsql = GET_THREAD_DATA; + + if (request->req_flags & REQ_save_metadata) + return; + + request->req_flags |= REQ_save_metadata; + ddl_node = request->req_ddl_node; + + if (ddl_node->nod_type == nod_mod_relation) + { + relation = METD_get_relation(request, relation_name); + } + else + { + relation = + (DSQL_REL) ALLOCDV(type_dsql_rel, relation_name->str_length); + relation->rel_name = relation->rel_data; + relation->rel_owner = + relation->rel_data + relation_name->str_length + 1; + strcpy(relation->rel_name, (SCHAR *) relation_name->str_data); + *relation->rel_owner = 0; + } + request->req_relation = relation; +} + + +static void set_statistics( REQ request) +{ +/************************************** + * + * s e t _ s t a t i s t i c s + * + ************************************** + * + * Function + * Alter an index/.. statistics + * + **************************************/ + NOD ddl_node; + STR index_name; + + ddl_node = request->req_ddl_node; + + index_name = (STR) ddl_node->nod_arg[e_stat_name]; + + put_cstring(request, gds_dyn_mod_idx, + reinterpret_cast(index_name->str_data)); + + STUFF(gds_dyn_idx_statistic); + + STUFF(gds_dyn_end); +} + + +static void stuff_default_blr( + REQ request, + TEXT * default_buff, USHORT buff_size) +{ +/******************************************** + * + * s t u f f _ d e f a u l t _ b l r + * + ******************************************** + * Function: + * The default_blr is passed in default_buffer. It is of the form: + * blr_version4 blr_literal ..... blr_eoc. + * strip the blr_version4 and blr_eoc verbs and stuff the remaining + * blr in the blr stream in the request. + * + *********************************************/ + unsigned int i; + assert((*default_buff == blr_version4) + || (*default_buff == blr_version5)); + + for (i = 1; ((i < buff_size) && (default_buff[i] != blr_eoc)); i++) + STUFF(default_buff[i]); + + assert(default_buff[i] == blr_eoc); +} + + +static void stuff_matching_blr( + REQ request, NOD for_columns, NOD prim_columns) +{ +/******************************************** + * + * s t u f f _ m a t c h i n g _ b l r + * + ******************************************** + * + * Function + * Generate blr to express: foreign_key == primary_key + * ie., for_key.column_1 = prim_key.column_1 and + * for_key.column_2 = prim_key.column_2 and .... so on.. + * + **************************************/ + NOD *for_key_flds, *prim_key_flds; + USHORT num_fields = 0; + STR for_key_fld_name_str, prim_key_fld_name_str; + +/* count of foreign key columns */ + assert(prim_columns->nod_count == for_columns->nod_count); + assert(prim_columns->nod_count != 0); + + STUFF(blr_boolean); + if (prim_columns->nod_count > 1) + STUFF(blr_and); + + num_fields = 0; + for_key_flds = for_columns->nod_arg; + prim_key_flds = prim_columns->nod_arg; + + do { + STUFF(blr_eql); + + for_key_fld_name_str = (STR) (*for_key_flds)->nod_arg[1]; + prim_key_fld_name_str = (STR) (*prim_key_flds)->nod_arg[1]; + + STUFF(blr_field); + STUFF((SSHORT) 2); + put_cstring(request, 0, + reinterpret_cast < + char *>(for_key_fld_name_str->str_data)); + STUFF(blr_field); + STUFF((SSHORT) 0); + put_cstring(request, 0, + reinterpret_cast < + char *>(prim_key_fld_name_str->str_data)); + + num_fields++; + + if (prim_columns->nod_count - num_fields >= (unsigned) 2) + STUFF(blr_and); + + for_key_flds++; + prim_key_flds++; + + } + while (num_fields < for_columns->nod_count); + + STUFF(blr_end); +} + + +static void stuff_trg_firing_cond( REQ request, NOD prim_columns) +{ +/******************************************** + * + * s t u f f _ t r g _ f i r i n g _ c o n d + * + ******************************************** + * + * Function + * Generate blr to express: if (old.primary_key != new.primary_key). + * do a column by column comparison. + * + **************************************/ + NOD *prim_key_flds; + USHORT num_fields = 0; + STR prim_key_fld_name_str; + + STUFF(blr_if); + if (prim_columns->nod_count > 1) + STUFF(blr_or); + + num_fields = 0; + prim_key_flds = prim_columns->nod_arg; + do { + STUFF(blr_neq); + + prim_key_fld_name_str = (STR) (*prim_key_flds)->nod_arg[1]; + + STUFF(blr_field); + STUFF((SSHORT) 0); + put_cstring(request, 0, + reinterpret_cast < + char *>(prim_key_fld_name_str->str_data)); + STUFF(blr_field); + STUFF((SSHORT) 1); + put_cstring(request, 0, + reinterpret_cast < + char *>(prim_key_fld_name_str->str_data)); + + num_fields++; + + if (prim_columns->nod_count - num_fields >= (unsigned) 2) + STUFF(blr_or); + + prim_key_flds++; + + } + while (num_fields < prim_columns->nod_count); +} + + +static void modify_field( + REQ request, + NOD element, SSHORT position, STR relation_name) +{ +/************************************** + * + * m o d i f y _ f i e l d + * + ************************************** + * + * Function + * Modify a field, either as part of an + * alter table or alter domain statement. + * + **************************************/ + NOD domain_node; + FLD field; + DSQL_REL relation; + + field = (FLD) element->nod_arg[e_dfl_field]; + put_cstring(request, isc_dyn_mod_sql_fld, + reinterpret_cast(field->fld_name)); + +/* add the field to the relation being defined for parsing purposes */ + if ((relation = request->req_relation) != NULL) { + field->fld_next = relation->rel_fields; + relation->rel_fields = field; + } + + if (domain_node = element->nod_arg[e_mod_fld_type_dom_name]) { + NOD node1; + STR domain_name; + + node1 = domain_node->nod_arg[e_dom_name]; + domain_name = (STR) node1->nod_arg[e_fln_name]; + put_cstring(request, gds_dyn_fld_source, + reinterpret_cast(domain_name->str_data)); + + /* Get the domain information */ + + if (!(METD_get_domain(request, field, domain_name->str_data))) + ERRD_post(gds__sqlerr, gds_arg_number, (SLONG) - 607, + gds_arg_gds, gds__dsql_command_err, + gds_arg_gds, gds__dsql_domain_not_found, + /* Specified domain or source field does not exist */ + 0); + DDL_resolve_intl_type(request, field, NULL); + } + else { + if (relation_name) + put_cstring(request, gds_dyn_rel_name, + reinterpret_cast(relation_name->str_data)); + + DDL_resolve_intl_type(request, field, NULL); + put_field(request, field, FALSE); + } + STUFF(gds_dyn_end); +} + + +static void set_nod_value_attributes( NOD node, FLD field) +{ +/************************************** + * + * s e t _ n o d _ v a l u e _ a t t r i b u t e s + * + ************************************** + * + * Function + * Examine all the children of the argument node: + * if any is a nod_dom_value, set its dtype, size, and scale + * to those of the field argument + * + **************************************/ + ULONG child_number; + NOD child; + + for (child_number = 0; child_number < node->nod_count; ++child_number) { + child = node->nod_arg[child_number]; + if (child && (type_nod == child->nod_header.blk_type)) { + if (nod_dom_value == child->nod_type) { + assert(field->fld_dtype <= MAX_UCHAR); + child->nod_desc.dsc_dtype = (UCHAR) field->fld_dtype; + child->nod_desc.dsc_length = field->fld_length; + assert(field->fld_scale <= MAX_SCHAR); + child->nod_desc.dsc_scale = (SCHAR) field->fld_scale; + } + else if ((nod_constant != child->nod_type) && + (child->nod_count > 0)) { + /* A nod_constant can have nod_arg entries which are not really + pointers to other nodes, but rather integer values, so + it is not safe to scan through its children. Fortunately, + it cannot have a nod_dom_value as a child in any case, so + we lose nothing by skipping it. + */ + + set_nod_value_attributes(child, field); + } + } /* if it's a node */ + } /* for (child_number ... */ + return; +} + + +} // extern "C" diff --git a/src/dsql/ddl_proto.h b/src/dsql/ddl_proto.h new file mode 100644 index 0000000000..57adfca0e6 --- /dev/null +++ b/src/dsql/ddl_proto.h @@ -0,0 +1,41 @@ +/* + * PROGRAM: Dynamic SQL runtime support + * MODULE: ddl_proto.h + * DESCRIPTION: Prototype Header file for ddl_proto.h + * + * 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): ______________________________________. + */ + +#ifndef _DSQL_DDL_PROTO_H_ +#define _DSQL_DDL_PROTO_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +void DDL_execute(struct req *); +void DDL_generate(struct req *, struct nod *); +int DDL_ids(struct req *); +void DDL_put_field_dtype(struct req *, struct fld *, USHORT); +void DDL_resolve_intl_type(struct req *, struct fld *, struct str *); + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* _DSQL_DDL_PROTO_H_ */ diff --git a/src/dsql/depends.mak b/src/dsql/depends.mak new file mode 100644 index 0000000000..13a0ba01ad --- /dev/null +++ b/src/dsql/depends.mak @@ -0,0 +1,613 @@ +# 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): ______________________________________. +# depends.mak - dsql +# Created by 'make depends.mak' +# Created on 1998-11-17 +alld.o: alld.c +alld.o: alld_proto.h +alld.o: blk.h +alld.o: dsql.h +alld.o: errd_proto.h +alld.o: node.h +alld.o: source/jrd/codes.h +alld.o: source/jrd/common.h +alld.o: source/jrd/dsc.h +alld.o: source/jrd/fil.h +alld.o: source/jrd/gds_proto.h +alld.o: source/jrd/gdsassert.h +alld.o: source/jrd/ibsetjmp.h +alld.o: source/jrd/isc.h +alld.o: source/jrd/thd.h +alld.o: source/jrd/thd_proto.h +alld.o: sym.h +array.o: array.c +array.o: array_proto.h +array.o: source/interbase/include/iberror.h +array.o: source/jrd/common.h +array.o: source/jrd/fil.h +array.o: source/jrd/gds.h +array.o: source/jrd/gds_proto.h +array.o: source/jrd/thd.h +blob.o: blob.c +blob.o: blob_proto.h +blob.o: source/interbase/include/iberror.h +blob.o: source/intl/charsets.h +blob.o: source/jrd/common.h +blob.o: source/jrd/constants.h +blob.o: source/jrd/gds.h +blob.o: source/jrd/intl.h +ddl.o: alld_proto.h +ddl.o: blk.h +ddl.o: ddl.c +ddl.o: ddl_proto.h +ddl.o: dsql.h +ddl.o: errd_proto.h +ddl.o: gen_proto.h +ddl.o: make_proto.h +ddl.o: metd_proto.h +ddl.o: node.h +ddl.o: pass1_proto.h +ddl.o: source/interbase/include/iberror.h +ddl.o: source/intl/charsets.h +ddl.o: source/jrd/codes.h +ddl.o: source/jrd/common.h +ddl.o: source/jrd/constants.h +ddl.o: source/jrd/dsc.h +ddl.o: source/jrd/flags.h +ddl.o: source/jrd/gds.h +ddl.o: source/jrd/ibsetjmp.h +ddl.o: source/jrd/intl.h +ddl.o: source/jrd/isc.h +ddl.o: source/jrd/sch_proto.h +ddl.o: source/jrd/thd.h +ddl.o: source/jrd/thd_proto.h +ddl.o: sym.h +dsql.o: alld_proto.h +dsql.o: blk.h +dsql.o: ddl_proto.h +dsql.o: dsql.c +dsql.o: dsql.h +dsql.o: dsql_proto.h +dsql.o: errd_proto.h +dsql.o: gen_proto.h +dsql.o: hsh_proto.h +dsql.o: make_proto.h +dsql.o: movd_proto.h +dsql.o: node.h +dsql.o: parse_proto.h +dsql.o: pass1_proto.h +dsql.o: source/interbase/include/iberror.h +dsql.o: source/intl/charsets.h +dsql.o: source/jrd/align.h +dsql.o: source/jrd/common.h +dsql.o: source/jrd/dsc.h +dsql.o: source/jrd/fil.h +dsql.o: source/jrd/gds.h +dsql.o: source/jrd/gds_proto.h +dsql.o: source/jrd/ibsetjmp.h +dsql.o: source/jrd/intl.h +dsql.o: source/jrd/isc.h +dsql.o: source/jrd/sch_proto.h +dsql.o: source/jrd/thd.h +dsql.o: source/jrd/thd_proto.h +dsql.o: source/jrd/why_proto.h +dsql.o: sqlda.h +dsql.o: sym.h +dsqlwep.o: dsqlwep.c +dsqlwep.o: source/jrd/common.h +errd.o: blk.h +errd.o: dsql.h +errd.o: errd.c +errd.o: errd_proto.h +errd.o: source/jrd/codes.h +errd.o: source/jrd/common.h +errd.o: source/jrd/dsc.h +errd.o: source/jrd/fil.h +errd.o: source/jrd/gds_proto.h +errd.o: source/jrd/iberr.h +errd.o: source/jrd/ibsetjmp.h +errd.o: source/jrd/isc.h +errd.o: source/jrd/thd.h +errd.o: source/jrd/thd_proto.h +errd.o: sqlda.h +errd.o: utld_proto.h +gen.o: alld_proto.h +gen.o: blk.h +gen.o: ddl_proto.h +gen.o: dsql.h +gen.o: errd_proto.h +gen.o: gen.c +gen.o: gen_proto.h +gen.o: make_proto.h +gen.o: metd_proto.h +gen.o: node.h +gen.o: source/interbase/include/iberror.h +gen.o: source/intl/charsets.h +gen.o: source/jrd/align.h +gen.o: source/jrd/common.h +gen.o: source/jrd/dsc.h +gen.o: source/jrd/gds.h +gen.o: source/jrd/ibsetjmp.h +gen.o: source/jrd/intl.h +gen.o: source/jrd/isc.h +gen.o: source/jrd/thd.h +gen.o: source/jrd/thd_proto.h +gen.o: sym.h +hsh.o: alld_proto.h +hsh.o: blk.h +hsh.o: dsql.h +hsh.o: errd_proto.h +hsh.o: hsh.c +hsh.o: hsh_proto.h +hsh.o: source/interbase/include/iberror.h +hsh.o: source/jrd/common.h +hsh.o: source/jrd/dsc.h +hsh.o: source/jrd/gds.h +hsh.o: source/jrd/ibsetjmp.h +hsh.o: source/jrd/isc.h +hsh.o: source/jrd/sch_proto.h +hsh.o: source/jrd/thd.h +hsh.o: source/jrd/thd_proto.h +hsh.o: sym.h +make.o: alld_proto.h +make.o: blk.h +make.o: dsql.h +make.o: errd_proto.h +make.o: hsh_proto.h +make.o: make.c +make.o: make_proto.h +make.o: node.h +make.o: source/interbase/include/iberror.h +make.o: source/intl/charsets.h +make.o: source/jrd/align.h +make.o: source/jrd/common.h +make.o: source/jrd/constants.h +make.o: source/jrd/dsc.h +make.o: source/jrd/dsc_proto.h +make.o: source/jrd/gds.h +make.o: source/jrd/ibsetjmp.h +make.o: source/jrd/intl.h +make.o: source/jrd/isc.h +make.o: source/jrd/thd.h +make.o: source/jrd/thd_proto.h +make.o: sym.h +metd.o: alld_proto.h +metd.o: blk.h +metd.o: ddl_proto.h +metd.o: dsql.h +metd.o: hsh_proto.h +metd.o: make_proto.h +metd.o: metd.c +metd.o: metd_proto.h +metd.o: node.h +metd.o: source/interbase/include/iberror.h +metd.o: source/intl/charsets.h +metd.o: source/jrd/align.h +metd.o: source/jrd/common.h +metd.o: source/jrd/constants.h +metd.o: source/jrd/dsc.h +metd.o: source/jrd/fil.h +metd.o: source/jrd/gds.h +metd.o: source/jrd/gds_proto.h +metd.o: source/jrd/ibsetjmp.h +metd.o: source/jrd/intl.h +metd.o: source/jrd/isc.h +metd.o: source/jrd/sch_proto.h +metd.o: source/jrd/thd.h +metd.o: source/jrd/thd_proto.h +metd.o: sym.h +movd.o: blk.h +movd.o: dsql.h +movd.o: errd_proto.h +movd.o: movd.c +movd.o: movd_proto.h +movd.o: source/jrd/codes.h +movd.o: source/jrd/common.h +movd.o: source/jrd/cvt_proto.h +movd.o: source/jrd/dsc.h +movd.o: source/jrd/iberr.h +movd.o: source/jrd/ibsetjmp.h +movd.o: source/jrd/isc.h +movd.o: source/jrd/thd.h +movd.o: source/jrd/thd_proto.h +parse.o: alld_proto.h +parse.o: blk.h +parse.o: chars.h +parse.o: dsql.h +parse.o: errd_proto.h +parse.o: hsh_proto.h +parse.o: keywords.h +parse.o: make_proto.h +parse.o: node.h +parse.o: parse.c +parse.o: parse_proto.h +parse.o: source/interbase/include/iberror.h +parse.o: source/jrd/common.h +parse.o: source/jrd/dsc.h +parse.o: source/jrd/fil.h +parse.o: source/jrd/flags.h +parse.o: source/jrd/gds.h +parse.o: source/jrd/gds_proto.h +parse.o: source/jrd/ibsetjmp.h +parse.o: source/jrd/isc.h +parse.o: source/jrd/misc.h +parse.o: source/jrd/thd.h +parse.o: source/jrd/thd_proto.h +parse.o: source/wal/wal.h +parse.o: sym.h +pass1.o: alld_proto.h +pass1.o: blk.h +pass1.o: ddl_proto.h +pass1.o: dsql.h +pass1.o: errd_proto.h +pass1.o: hsh_proto.h +pass1.o: make_proto.h +pass1.o: metd_proto.h +pass1.o: node.h +pass1.o: pass1.c +pass1.o: pass1_proto.h +pass1.o: source/intl/charsets.h +pass1.o: source/jrd/blr.h +pass1.o: source/jrd/codes.h +pass1.o: source/jrd/common.h +pass1.o: source/jrd/dsc.h +pass1.o: source/jrd/dsc_proto.h +pass1.o: source/jrd/ibsetjmp.h +pass1.o: source/jrd/intl.h +pass1.o: source/jrd/isc.h +pass1.o: source/jrd/thd.h +pass1.o: source/jrd/thd_proto.h +pass1.o: sym.h +preparse.o: chars.h +preparse.o: prepa_proto.h +preparse.o: preparse.c +preparse.o: source/interbase/include/iberror.h +preparse.o: source/jrd/common.h +preparse.o: source/jrd/fil.h +preparse.o: source/jrd/gds.h +preparse.o: source/jrd/gds_proto.h +preparse.o: source/jrd/thd.h +preparse.o: utld_proto.h +user_dsql.o: blk.h +user_dsql.o: chars.h +user_dsql.o: dsql.h +user_dsql.o: source/jrd/align.h +user_dsql.o: source/jrd/blr.h +user_dsql.o: source/jrd/codes.h +user_dsql.o: source/jrd/common.h +user_dsql.o: source/jrd/dsc.h +user_dsql.o: source/jrd/fil.h +user_dsql.o: source/jrd/gds_proto.h +user_dsql.o: source/jrd/ibsetjmp.h +user_dsql.o: source/jrd/inf.h +user_dsql.o: source/jrd/thd.h +user_dsql.o: source/jrd/why_proto.h +user_dsql.o: sqlda.h +user_dsql.o: user__proto.h +user_dsql.o: user_dsql.c +utld.o: blk.h +utld.o: dsql.h +utld.o: source/jrd/align.h +utld.o: source/jrd/blr.h +utld.o: source/jrd/codes.h +utld.o: source/jrd/common.h +utld.o: source/jrd/dsc.h +utld.o: source/jrd/fil.h +utld.o: source/jrd/gds_proto.h +utld.o: source/jrd/ibsetjmp.h +utld.o: source/jrd/inf.h +utld.o: source/jrd/thd.h +utld.o: sqlda.h +utld.o: utld.c +utld.o: utld_proto.h +alld.bin: alld.c +alld.bin: alld_proto.h +alld.bin: blk.h +alld.bin: dsql.h +alld.bin: errd_proto.h +alld.bin: node.h +alld.bin: source/jrd/codes.h +alld.bin: source/jrd/common.h +alld.bin: source/jrd/dsc.h +alld.bin: source/jrd/fil.h +alld.bin: source/jrd/gds_proto.h +alld.bin: source/jrd/gdsassert.h +alld.bin: source/jrd/ibsetjmp.h +alld.bin: source/jrd/isc.h +alld.bin: source/jrd/thd.h +alld.bin: source/jrd/thd_proto.h +alld.bin: sym.h +array.bin: array.c +array.bin: array_proto.h +array.bin: source/interbase/include/iberror.h +array.bin: source/jrd/common.h +array.bin: source/jrd/fil.h +array.bin: source/jrd/gds.h +array.bin: source/jrd/gds_proto.h +array.bin: source/jrd/thd.h +blob.bin: blob.c +blob.bin: blob_proto.h +blob.bin: source/interbase/include/iberror.h +blob.bin: source/intl/charsets.h +blob.bin: source/jrd/common.h +blob.bin: source/jrd/constants.h +blob.bin: source/jrd/gds.h +blob.bin: source/jrd/intl.h +ddl.bin: alld_proto.h +ddl.bin: blk.h +ddl.bin: ddl.c +ddl.bin: ddl_proto.h +ddl.bin: dsql.h +ddl.bin: errd_proto.h +ddl.bin: gen_proto.h +ddl.bin: make_proto.h +ddl.bin: metd_proto.h +ddl.bin: node.h +ddl.bin: pass1_proto.h +ddl.bin: source/interbase/include/iberror.h +ddl.bin: source/intl/charsets.h +ddl.bin: source/jrd/codes.h +ddl.bin: source/jrd/common.h +ddl.bin: source/jrd/constants.h +ddl.bin: source/jrd/dsc.h +ddl.bin: source/jrd/flags.h +ddl.bin: source/jrd/gds.h +ddl.bin: source/jrd/ibsetjmp.h +ddl.bin: source/jrd/intl.h +ddl.bin: source/jrd/isc.h +ddl.bin: source/jrd/sch_proto.h +ddl.bin: source/jrd/thd.h +ddl.bin: source/jrd/thd_proto.h +ddl.bin: sym.h +dsql.bin: alld_proto.h +dsql.bin: blk.h +dsql.bin: ddl_proto.h +dsql.bin: dsql.c +dsql.bin: dsql.h +dsql.bin: dsql_proto.h +dsql.bin: errd_proto.h +dsql.bin: gen_proto.h +dsql.bin: hsh_proto.h +dsql.bin: make_proto.h +dsql.bin: movd_proto.h +dsql.bin: node.h +dsql.bin: parse_proto.h +dsql.bin: pass1_proto.h +dsql.bin: source/interbase/include/iberror.h +dsql.bin: source/intl/charsets.h +dsql.bin: source/jrd/align.h +dsql.bin: source/jrd/common.h +dsql.bin: source/jrd/dsc.h +dsql.bin: source/jrd/fil.h +dsql.bin: source/jrd/gds.h +dsql.bin: source/jrd/gds_proto.h +dsql.bin: source/jrd/ibsetjmp.h +dsql.bin: source/jrd/intl.h +dsql.bin: source/jrd/isc.h +dsql.bin: source/jrd/sch_proto.h +dsql.bin: source/jrd/thd.h +dsql.bin: source/jrd/thd_proto.h +dsql.bin: source/jrd/why_proto.h +dsql.bin: sqlda.h +dsql.bin: sym.h +dsqlwep.bin: dsqlwep.c +dsqlwep.bin: source/jrd/common.h +errd.bin: blk.h +errd.bin: dsql.h +errd.bin: errd.c +errd.bin: errd_proto.h +errd.bin: source/jrd/codes.h +errd.bin: source/jrd/common.h +errd.bin: source/jrd/dsc.h +errd.bin: source/jrd/fil.h +errd.bin: source/jrd/gds_proto.h +errd.bin: source/jrd/iberr.h +errd.bin: source/jrd/ibsetjmp.h +errd.bin: source/jrd/isc.h +errd.bin: source/jrd/thd.h +errd.bin: source/jrd/thd_proto.h +errd.bin: sqlda.h +errd.bin: utld_proto.h +gen.bin: alld_proto.h +gen.bin: blk.h +gen.bin: ddl_proto.h +gen.bin: dsql.h +gen.bin: errd_proto.h +gen.bin: gen.c +gen.bin: gen_proto.h +gen.bin: make_proto.h +gen.bin: metd_proto.h +gen.bin: node.h +gen.bin: source/interbase/include/iberror.h +gen.bin: source/intl/charsets.h +gen.bin: source/jrd/align.h +gen.bin: source/jrd/common.h +gen.bin: source/jrd/dsc.h +gen.bin: source/jrd/gds.h +gen.bin: source/jrd/ibsetjmp.h +gen.bin: source/jrd/intl.h +gen.bin: source/jrd/isc.h +gen.bin: source/jrd/thd.h +gen.bin: source/jrd/thd_proto.h +gen.bin: sym.h +hsh.bin: alld_proto.h +hsh.bin: blk.h +hsh.bin: dsql.h +hsh.bin: errd_proto.h +hsh.bin: hsh.c +hsh.bin: hsh_proto.h +hsh.bin: source/interbase/include/iberror.h +hsh.bin: source/jrd/common.h +hsh.bin: source/jrd/dsc.h +hsh.bin: source/jrd/gds.h +hsh.bin: source/jrd/ibsetjmp.h +hsh.bin: source/jrd/isc.h +hsh.bin: source/jrd/sch_proto.h +hsh.bin: source/jrd/thd.h +hsh.bin: source/jrd/thd_proto.h +hsh.bin: sym.h +make.bin: alld_proto.h +make.bin: blk.h +make.bin: dsql.h +make.bin: errd_proto.h +make.bin: hsh_proto.h +make.bin: make.c +make.bin: make_proto.h +make.bin: node.h +make.bin: source/interbase/include/iberror.h +make.bin: source/intl/charsets.h +make.bin: source/jrd/align.h +make.bin: source/jrd/common.h +make.bin: source/jrd/constants.h +make.bin: source/jrd/dsc.h +make.bin: source/jrd/dsc_proto.h +make.bin: source/jrd/gds.h +make.bin: source/jrd/ibsetjmp.h +make.bin: source/jrd/intl.h +make.bin: source/jrd/isc.h +make.bin: source/jrd/thd.h +make.bin: source/jrd/thd_proto.h +make.bin: sym.h +metd.bin: alld_proto.h +metd.bin: blk.h +metd.bin: ddl_proto.h +metd.bin: dsql.h +metd.bin: hsh_proto.h +metd.bin: make_proto.h +metd.bin: metd.c +metd.bin: metd_proto.h +metd.bin: node.h +metd.bin: source/interbase/include/iberror.h +metd.bin: source/intl/charsets.h +metd.bin: source/jrd/align.h +metd.bin: source/jrd/common.h +metd.bin: source/jrd/constants.h +metd.bin: source/jrd/dsc.h +metd.bin: source/jrd/fil.h +metd.bin: source/jrd/gds.h +metd.bin: source/jrd/gds_proto.h +metd.bin: source/jrd/ibsetjmp.h +metd.bin: source/jrd/intl.h +metd.bin: source/jrd/isc.h +metd.bin: source/jrd/sch_proto.h +metd.bin: source/jrd/thd.h +metd.bin: source/jrd/thd_proto.h +metd.bin: sym.h +movd.bin: blk.h +movd.bin: dsql.h +movd.bin: errd_proto.h +movd.bin: movd.c +movd.bin: movd_proto.h +movd.bin: source/jrd/codes.h +movd.bin: source/jrd/common.h +movd.bin: source/jrd/cvt_proto.h +movd.bin: source/jrd/dsc.h +movd.bin: source/jrd/iberr.h +movd.bin: source/jrd/ibsetjmp.h +movd.bin: source/jrd/isc.h +movd.bin: source/jrd/thd.h +movd.bin: source/jrd/thd_proto.h +parse.bin: alld_proto.h +parse.bin: blk.h +parse.bin: chars.h +parse.bin: dsql.h +parse.bin: errd_proto.h +parse.bin: hsh_proto.h +parse.bin: keywords.h +parse.bin: make_proto.h +parse.bin: node.h +parse.bin: parse.c +parse.bin: parse_proto.h +parse.bin: source/interbase/include/iberror.h +parse.bin: source/jrd/common.h +parse.bin: source/jrd/dsc.h +parse.bin: source/jrd/fil.h +parse.bin: source/jrd/flags.h +parse.bin: source/jrd/gds.h +parse.bin: source/jrd/gds_proto.h +parse.bin: source/jrd/ibsetjmp.h +parse.bin: source/jrd/isc.h +parse.bin: source/jrd/misc.h +parse.bin: source/jrd/thd.h +parse.bin: source/jrd/thd_proto.h +parse.bin: source/wal/wal.h +parse.bin: sym.h +pass1.bin: alld_proto.h +pass1.bin: blk.h +pass1.bin: ddl_proto.h +pass1.bin: dsql.h +pass1.bin: errd_proto.h +pass1.bin: hsh_proto.h +pass1.bin: make_proto.h +pass1.bin: metd_proto.h +pass1.bin: node.h +pass1.bin: pass1.c +pass1.bin: pass1_proto.h +pass1.bin: source/intl/charsets.h +pass1.bin: source/jrd/blr.h +pass1.bin: source/jrd/codes.h +pass1.bin: source/jrd/common.h +pass1.bin: source/jrd/dsc.h +pass1.bin: source/jrd/dsc_proto.h +pass1.bin: source/jrd/ibsetjmp.h +pass1.bin: source/jrd/intl.h +pass1.bin: source/jrd/isc.h +pass1.bin: source/jrd/thd.h +pass1.bin: source/jrd/thd_proto.h +pass1.bin: sym.h +preparse.bin: chars.h +preparse.bin: prepa_proto.h +preparse.bin: preparse.c +preparse.bin: source/interbase/include/iberror.h +preparse.bin: source/jrd/common.h +preparse.bin: source/jrd/fil.h +preparse.bin: source/jrd/gds.h +preparse.bin: source/jrd/gds_proto.h +preparse.bin: source/jrd/thd.h +preparse.bin: utld_proto.h +user_dsql.bin: blk.h +user_dsql.bin: chars.h +user_dsql.bin: dsql.h +user_dsql.bin: source/jrd/align.h +user_dsql.bin: source/jrd/blr.h +user_dsql.bin: source/jrd/codes.h +user_dsql.bin: source/jrd/common.h +user_dsql.bin: source/jrd/dsc.h +user_dsql.bin: source/jrd/fil.h +user_dsql.bin: source/jrd/gds_proto.h +user_dsql.bin: source/jrd/ibsetjmp.h +user_dsql.bin: source/jrd/inf.h +user_dsql.bin: source/jrd/thd.h +user_dsql.bin: source/jrd/why_proto.h +user_dsql.bin: sqlda.h +user_dsql.bin: user__proto.h +user_dsql.bin: user_dsql.c +utld.bin: blk.h +utld.bin: dsql.h +utld.bin: source/jrd/align.h +utld.bin: source/jrd/blr.h +utld.bin: source/jrd/codes.h +utld.bin: source/jrd/common.h +utld.bin: source/jrd/dsc.h +utld.bin: source/jrd/fil.h +utld.bin: source/jrd/gds_proto.h +utld.bin: source/jrd/ibsetjmp.h +utld.bin: source/jrd/inf.h +utld.bin: source/jrd/thd.h +utld.bin: sqlda.h +utld.bin: utld.c +utld.bin: utld_proto.h diff --git a/src/dsql/dsql.cpp b/src/dsql/dsql.cpp new file mode 100644 index 0000000000..fb2ef40ee6 --- /dev/null +++ b/src/dsql/dsql.cpp @@ -0,0 +1,3936 @@ +/* + * PROGRAM: Dynamic SQL runtime support + * MODULE: dsql.c + * DESCRIPTION: Local processing for External entry points. + * + * 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): ______________________________________. + */ +/* +$Id: dsql.cpp,v 1.1.1.1 2001-05-23 13:25:36 tamlin Exp $ +*/ +/************************************************************** +V4 Multi-threading changes. + +-- direct calls to gds__ () & isc_ () entrypoints + + THREAD_EXIT; + gds__ () or isc_ () call. + THREAD_ENTER; + +-- calls through embedded GDML. + +the following protocol will be used. Care should be taken if +nested FOR loops are added. + + THREAD_EXIT; // last statment before FOR loop + + FOR ............... + + THREAD_ENTER; // First statment in FOR loop + .....some C code.... + .....some C code.... + THREAD_EXIT; // last statment in FOR loop + + END_FOR; + + THREAD_ENTER; // First statment after FOR loop +***************************************************************/ + +#define DSQL_MAIN + +#include "../jrd/ib_stdio.h" +#include +#include +#include "../dsql/dsql.h" +#include "../dsql/node.h" +#include "../dsql/sym.h" +#include "../include/jrd/gds.h" +#include "../jrd/thd.h" +#include "../jrd/align.h" +#include "../jrd/intl.h" +#include "../jrd/iberr.h" +#include "../dsql/sqlda.h" +#include "../dsql/alld_proto.h" +#include "../dsql/ddl_proto.h" +#include "../dsql/dsql_proto.h" +#include "../dsql/errd_proto.h" +#include "../dsql/gen_proto.h" +#include "../dsql/hsh_proto.h" +#include "../dsql/make_proto.h" +#include "../dsql/movd_proto.h" +#include "../dsql/parse_proto.h" +#include "../dsql/pass1_proto.h" +#include "../jrd/gds_proto.h" +#include "../jrd/sch_proto.h" +#include "../jrd/thd_proto.h" +#include "../jrd/why_proto.h" +#include "../include/jrd/gds.h" + +ASSERT_FILENAME +#ifdef VMS +#include +#endif +extern "C" { +#ifdef NETWARE_386 +#define PRINTF ConsolePrintf +#endif +#ifndef PRINTF +#define PRINTF ib_printf +#endif +#define ERROR_INIT(env) {\ + tdsql->tsql_status = user_status;\ + tdsql->tsql_setjmp = &env;\ + tdsql->tsql_default = NULL;\ + if (SETJMP (env))\ + {\ + RESTORE_THREAD_DATA;\ + return tdsql->tsql_status [1];\ + };\ + } +#define RETURN_SUCCESS return return_success() +#define SET_THREAD_DATA {\ + tdsql = &thd_context;\ + THD_put_specific ((THDD) tdsql);\ + tdsql->tsql_thd_data.thdd_type = THDD_TYPE_TSQL;\ + } +#define RESTORE_THREAD_DATA THD_restore_specific() +#ifdef STACK_REDUCTION +#define FREE_MEM_RETURN {\ + if (buffer)\ + {\ + gds__free ((SLONG *)buffer);\ + buffer = (TEXT*) NULL;\ + }\ + return;\ + } +#else +#define FREE_MEM_RETURN return +#endif +static void cleanup(void *); +static void cleanup_database(SLONG **, SLONG); +static void cleanup_transaction(SLONG *, SLONG); +static void close_cursor(REQ); +static USHORT convert(SLONG, SCHAR *); +static STATUS error(void); +static void execute_blob(REQ, USHORT, UCHAR *, USHORT, UCHAR *, + USHORT, UCHAR *, USHORT, UCHAR *); +static STATUS execute_request(REQ, isc_tr_handle*, USHORT, UCHAR *, USHORT, UCHAR *, + USHORT, UCHAR *, USHORT, UCHAR *, USHORT); +static SSHORT filter_sub_type(REQ, NOD); +static BOOLEAN get_indices(SSHORT *, SCHAR **, SSHORT *, SCHAR **); +static USHORT get_plan_info(REQ, SSHORT, SCHAR **); +static USHORT get_request_info(REQ, SSHORT, SCHAR *); +static BOOLEAN get_rsb_item(SSHORT *, SCHAR **, SSHORT *, SCHAR **, USHORT *, + USHORT *); +static DBB init(SLONG **); +static void map_in_out(REQ, MSG, USHORT, UCHAR *, USHORT, UCHAR *); +static USHORT name_length(TEXT *); +static USHORT parse_blr(USHORT, UCHAR *, USHORT, PAR); +static REQ prepare(REQ, USHORT, TEXT *, USHORT, USHORT); +static void punt(void); +static SCHAR *put_item(SCHAR, USHORT, SCHAR *, SCHAR *, SCHAR *); +static void release_request(REQ, USHORT); +static STATUS return_success(void); +static SCHAR *var_info(MSG, SCHAR *, SCHAR *, SCHAR *, SCHAR *, USHORT); + +extern NOD DSQL_parse; + +static USHORT init_flag; +static DBB databases; +static OPN open_cursors; +static CONST SCHAR db_hdr_info_items[] = { + isc_info_db_sql_dialect, + gds_info_ods_version, + gds_info_base_level, +#ifdef READONLY_DATABASE + isc_info_db_read_only, +#endif /* READONLY_DATABASE */ + frb_info_att_charset, + gds_info_end +}; +static CONST SCHAR explain_info[] = { + gds_info_access_path +}; +static CONST SCHAR record_info[] = { + gds_info_req_update_count, gds_info_req_delete_count, + gds_info_req_select_count, gds_info_req_insert_count +}; +static CONST SCHAR sql_records_info[] = { + gds_info_sql_records +}; + +#ifdef ANY_THREADING +static MUTX_T databases_mutex; +static MUTX_T cursors_mutex; +static USHORT mutex_inited = 0; +#endif + +/* STUFF_INFO used in place of STUFF to avoid confusion with BLR STUFF + macro defined in dsql.h */ + +#define STUFF_INFO_WORD(p,value) {*p++ = (UCHAR)value; *p++ = value >> 8;} +#define STUFF_INFO(p,value) *p++ = value; + +#define GDS_DSQL_ALLOCATE dsql8_allocate_statement +#define GDS_DSQL_EXECUTE dsql8_execute +#define GDS_DSQL_EXECUTE_IMMED dsql8_execute_immediate +#define GDS_DSQL_FETCH dsql8_fetch +#define GDS_DSQL_FREE dsql8_free_statement +#define GDS_DSQL_INSERT dsql8_insert +#define GDS_DSQL_PREPARE dsql8_prepare +#define GDS_DSQL_SET_CURSOR dsql8_set_cursor +#define GDS_DSQL_SQL_INFO dsql8_sql_info + + +STATUS DLL_EXPORT GDS_DSQL_ALLOCATE(STATUS* user_status, + int** db_handle, + REQ* req_handle) +{ +/************************************** + * + * d s q l _ a l l o c a t e _ s t a t e m e n t + * + ************************************** + * + * Functional description + * Allocate a statement handle. + * + **************************************/ + DBB database; + REQ request; + struct tsql thd_context, *tdsql; + JMP_BUF env; + + SET_THREAD_DATA; + + ERROR_INIT(env); + init(0); + +/* If we haven't been initialized yet, do it now */ + + database = init((SLONG **) db_handle); + + tdsql->tsql_default = ALLD_pool(); + +/* allocate the request block */ + + request = (REQ) ALLOCD(type_req); + request->req_dbb = database; + request->req_pool = tdsql->tsql_default; + + *req_handle = request; + + RETURN_SUCCESS; +} + + +STATUS DLL_EXPORT GDS_DSQL_EXECUTE(STATUS * user_status, + isc_tr_handle * trans_handle, + REQ * req_handle, + USHORT in_blr_length, + UCHAR * in_blr, + USHORT in_msg_type, + USHORT in_msg_length, + UCHAR * in_msg, + USHORT out_blr_length, + UCHAR * out_blr, + USHORT out_msg_type, + USHORT out_msg_length, UCHAR * out_msg) +{ +/************************************** + * + * d s q l _ e x e c u t e + * + ************************************** + * + * Functional description + * Execute a non-SELECT dynamic SQL statement. + * + **************************************/ + REQ request; + OPN open_cursor; + STATUS local_status[ISC_STATUS_LENGTH]; + struct tsql thd_context, *tdsql; + JMP_BUF env; + USHORT singleton; + STATUS sing_status; + + SET_THREAD_DATA; + + ERROR_INIT(env); + init(0); + sing_status = 0; + + request = *req_handle; + tdsql->tsql_default = request->req_pool; + + if ((SSHORT) in_msg_type == -1) + request->req_type = REQ_EMBED_SELECT; + +/* Only allow NULL trans_handle if we're starting a transaction */ + + if (*trans_handle == NULL && request->req_type != REQ_START_TRANS) + ERRD_post(gds_sqlerr, gds_arg_number, (SLONG) - 901, + gds_arg_gds, gds_bad_trans_handle, 0); + +/* If the request is a SELECT or blob statement then this is an open. +/* If the request is a SELECT or blob statement then this is an open. + Make sure the cursor is not already open. */ + + if (request->req_type == REQ_SELECT || + request->req_type == REQ_SELECT_UPD || + request->req_type == REQ_EMBED_SELECT || + request->req_type == REQ_GET_SEGMENT || + request->req_type == REQ_PUT_SEGMENT) + if (request->req_flags & REQ_cursor_open) { + ERRD_post(gds_sqlerr, gds_arg_number, (SLONG) - 502, + gds_arg_gds, gds_dsql_cursor_open_err, 0); + } + +/* A select with a non zero output length is a singleton select */ + + if (request->req_type == REQ_SELECT && out_msg_length != 0) + singleton = TRUE; + else + singleton = FALSE; + + if (request->req_type != REQ_EMBED_SELECT) + sing_status = + execute_request(request, + trans_handle, + in_blr_length, + in_blr, + in_msg_length, + in_msg, + out_blr_length, + out_blr, + out_msg_length, + out_msg, + singleton); + +/* If the output message length is zero on a REQ_SELECT then we must + * be doing an OPEN cursor operation. + * If we do have an output message length, then we're doing + * a singleton SELECT. In that event, we don't add the cursor + * to the list of open cursors (it's not really open). + */ + if ((request->req_type == REQ_SELECT && out_msg_length == 0) || + request->req_type == REQ_SELECT_UPD || + request->req_type == REQ_EMBED_SELECT || + request->req_type == REQ_GET_SEGMENT || + request->req_type == REQ_PUT_SEGMENT) { + request->req_flags |= REQ_cursor_open | + ((request-> + req_type == REQ_EMBED_SELECT) ? REQ_embedded_sql_cursor : 0); + + request->req_open_cursor = open_cursor = (OPN) ALLOCP(type_opn); + open_cursor->opn_request = request; + open_cursor->opn_transaction = (SLONG *) * trans_handle; + THD_MUTEX_LOCK(&cursors_mutex); + open_cursor->opn_next = open_cursors; + open_cursors = open_cursor; + THD_MUTEX_UNLOCK(&cursors_mutex); + THREAD_EXIT; + gds__transaction_cleanup(local_status, + reinterpret_cast < hndl ** >(trans_handle), + (isc_callback) cleanup_transaction, 0); + THREAD_ENTER; + } + + if (!sing_status) + RETURN_SUCCESS; + else { + RESTORE_THREAD_DATA; + return sing_status; + } +} + + +STATUS DLL_EXPORT GDS_DSQL_EXECUTE_IMMED(STATUS * user_status, + int **db_handle, + int **trans_handle, + USHORT length, + TEXT * string, + USHORT dialect, + USHORT in_blr_length, + UCHAR * in_blr, + USHORT in_msg_type, + USHORT in_msg_length, + UCHAR * in_msg, + USHORT out_blr_length, + UCHAR * out_blr, + USHORT out_msg_type, + USHORT out_msg_length, + UCHAR * out_msg) +{ +/************************************** + * + * d s q l _ e x e c u t e _ i m m e d i a t e + * + ************************************** + * + * Functional description + * Prepare and execute a statement. + * + **************************************/ + REQ request; + DBB database; + USHORT parser_version; + STATUS status; + struct tsql thd_context, *tdsql; + JMP_BUF env; + + SET_THREAD_DATA; + + ERROR_INIT(env); + database = init(reinterpret_cast < long **>(db_handle)); + + tdsql->tsql_default = ALLD_pool(); + +/* allocate the request block, then prepare the request */ + + request = (REQ) ALLOCD(type_req); + request->req_dbb = database; + request->req_pool = tdsql->tsql_default; + request->req_trans = (int *) *trans_handle; + + if (SETJMP(*tdsql->tsql_setjmp)) { + status = error(); + release_request(request, TRUE); + RESTORE_THREAD_DATA; + return status; + } + + if (!length) + length = strlen(string); + +/* Figure out which parser version to use */ +/* Since the API to GDS_DSQL_EXECUTE_IMMED is public and can not be changed, there needs to + * be a way to send the parser version to DSQL so that the parser can compare the keyword + * version to the parser version. To accomplish this, the parser version is combined with + * the client dialect and sent across that way. In dsql8_execute_immediate, the parser version + * and client dialect are separated and passed on to their final desintations. The information + * is combined as follows: + * Dialect * 10 + parser_version + * + * and is extracted in dsql8_execute_immediate as follows: + * parser_version = ((dialect *10)+parser_version)%10 + * client_dialect = ((dialect *10)+parser_version)/10 + * + * For example, parser_version = 1 and client dialect = 1 + * + * combined = (1 * 10) + 1 == 11 + * + * parser = (combined) %10 == 1 + * dialect = (combined) / 19 == 1 + * + * If the parser version is not part of the dialect, then assume that the + * connection being made is a local classic connection. + */ + + if ((dialect / 10) == 0) + parser_version = 2; + else { + parser_version = dialect % 10; + dialect /= 10; + } + + request->req_client_dialect = dialect; + + request = prepare(request, length, string, dialect, parser_version); + execute_request(request, + reinterpret_cast(trans_handle), + in_blr_length, + in_blr, + in_msg_length, + in_msg, + out_blr_length, + out_blr, + out_msg_length, + out_msg, + FALSE); + + release_request(request, TRUE); + + RETURN_SUCCESS; +} + + +STATUS DLL_EXPORT GDS_DSQL_FETCH(STATUS * user_status, + REQ * req_handle, + USHORT blr_length, + UCHAR * blr, + USHORT msg_type, + USHORT msg_length, UCHAR * msg +#ifdef SCROLLABLE_CURSORS + , USHORT direction, SLONG offset) +#else + ) +#endif +{ +/************************************** + * + * d s q l _ f e t c h + * + ************************************** + * + * Functional description + * Fetch next record from a dynamic SQL cursor + * + **************************************/ + REQ request; + MSG message; + PAR eof, parameter, null; + STATUS s; + USHORT *ret_length; + UCHAR *buffer; + struct tsql thd_context, *tdsql; + JMP_BUF env; + + SET_THREAD_DATA; + + ERROR_INIT(env); + init(0); + + request = *req_handle; + tdsql->tsql_default = request->req_pool; + +/* if the cursor isn't open, we've got a problem */ + + if (request->req_type == REQ_SELECT || + request->req_type == REQ_SELECT_UPD || + request->req_type == REQ_EMBED_SELECT || + request->req_type == REQ_GET_SEGMENT) + if (!(request->req_flags & REQ_cursor_open)) + ERRD_post(gds_sqlerr, gds_arg_number, (SLONG) - 504, + gds_arg_gds, gds_dsql_cursor_err, 0); + +#ifdef SCROLLABLE_CURSORS + +/* check whether we need to send an asynchronous scrolling message + to the engine; the engine will automatically advance one record + in the same direction as before, so optimize out messages of that + type */ + + if (request->req_type == REQ_SELECT && + request->req_dbb->dbb_base_level >= 5) { + switch (direction) { + case isc_fetch_next: + if (!(request->req_flags & REQ_backwards)) + offset = 0; + else { + direction = blr_forward; + offset = 1; + request->req_flags &= ~REQ_backwards; + } + break; + + case isc_fetch_prior: + if (request->req_flags & REQ_backwards) + offset = 0; + else { + direction = blr_backward; + offset = 1; + request->req_flags |= REQ_backwards; + } + break; + + case isc_fetch_first: + direction = blr_bof_forward; + offset = 1; + request->req_flags &= ~REQ_backwards; + break; + + case isc_fetch_last: + direction = blr_eof_backward; + offset = 1; + request->req_flags |= REQ_backwards; + break; + + case isc_fetch_absolute: + direction = blr_bof_forward; + request->req_flags &= ~REQ_backwards; + break; + + case isc_fetch_relative: + if (offset < 0) { + direction = blr_backward; + offset = -offset; + request->req_flags |= REQ_backwards; + } + else { + direction = blr_forward; + request->req_flags &= ~REQ_backwards; + } + break; + + default: + ERRD_post(gds_sqlerr, gds_arg_number, (SLONG) - 804, + gds_arg_gds, gds_dsql_sqlda_err, 0); + } + + if (offset) { + PAR offset_parameter; + DSC desc; + + message = (MSG) request->req_async; + + desc.dsc_dtype = dtype_short; + desc.dsc_scale = 0; + desc.dsc_length = sizeof(USHORT); + desc.dsc_flags = 0; + desc.dsc_address = (UCHAR *) & direction; + + offset_parameter = message->msg_parameters; + parameter = offset_parameter->par_next; + MOVD_move(&desc, ¶meter->par_desc); + + desc.dsc_dtype = dtype_long; + desc.dsc_scale = 0; + desc.dsc_length = sizeof(SLONG); + desc.dsc_flags = 0; + desc.dsc_address = (UCHAR *) & offset; + + MOVD_move(&desc, &offset_parameter->par_desc); + + THREAD_EXIT; + s = isc_receive2(GDS_VAL(tdsql->tsql_status), + GDS_REF(request->req_handle), + message->msg_number, + message->msg_length, + GDS_VAL(message->msg_buffer), + 0, direction, offset); + THREAD_ENTER; + + if (s) + punt(); + } + } +#endif + + message = (MSG) request->req_receive; + +/* Insure that the blr for the message is parsed, regardless of + whether anything is found by the call to receive. */ + + if (blr_length) + parse_blr(blr_length, blr, msg_length, message->msg_parameters); + + if (request->req_type == REQ_GET_SEGMENT) { + /* For get segment, use the user buffer and indicator directly. */ + + parameter = request->req_blob->blb_segment; + null = parameter->par_null; + ret_length = + (USHORT *) (msg + (SLONG) null->par_user_desc.dsc_address); + buffer = msg + (SLONG) parameter->par_user_desc.dsc_address; + THREAD_EXIT; + s = isc_get_segment(tdsql->tsql_status, + reinterpret_cast < + void **>(GDS_REF(request->req_handle)), + GDS_VAL(ret_length), + parameter->par_user_desc.dsc_length, + reinterpret_cast < char *>(GDS_VAL(buffer))); + THREAD_ENTER; + if (!s) { + RESTORE_THREAD_DATA; + return 0; + } + else if (s == gds_segment) { + RESTORE_THREAD_DATA; + return 101; + } + else if (s == gds_segstr_eof) { + RESTORE_THREAD_DATA; + return 100; + } + else + punt(); + } + + THREAD_EXIT; + s = isc_receive(GDS_VAL(tdsql->tsql_status), + reinterpret_cast < void **>(GDS_REF(request->req_handle)), + message->msg_number, + message->msg_length, GDS_VAL(message->msg_buffer), 0); + THREAD_ENTER; + + if (s) + punt(); + + if (eof = request->req_eof) + if (!*((USHORT *) eof->par_desc.dsc_address)) { + RESTORE_THREAD_DATA; + return 100; + } + + map_in_out(NULL, message, 0, blr, msg_length, msg); + + RETURN_SUCCESS; +} + + +STATUS DLL_EXPORT GDS_DSQL_FREE(STATUS * user_status, + REQ * req_handle, USHORT option) +{ +/************************************** + * + * d s q l _ f r e e _ s t a t e m e n t + * + ************************************** + * + * Functional description + * Release request for a dsql statement + * + **************************************/ + REQ request; + struct tsql thd_context, *tdsql; + JMP_BUF env; + + SET_THREAD_DATA; + + ERROR_INIT(env); + init(0); + + request = *req_handle; + tdsql->tsql_default = request->req_pool; + + if (option & DSQL_drop) { + /* Release everything associate with the request. */ + + release_request(request, TRUE); + *req_handle = NULL; + } + else if (option & DSQL_close) { + /* Just close the cursor associated with the request. */ + + if (!(request->req_flags & REQ_cursor_open)) + ERRD_post(gds_sqlerr, gds_arg_number, (SLONG) - 501, + gds_arg_gds, gds_dsql_cursor_close_err, 0); + + close_cursor(request); + } + + RETURN_SUCCESS; +} + + +STATUS DLL_EXPORT GDS_DSQL_INSERT(STATUS * user_status, + REQ * req_handle, + USHORT blr_length, + UCHAR * blr, + USHORT msg_type, + USHORT msg_length, UCHAR * msg) +{ +/************************************** + * + * d s q l _ i n s e r t + * + ************************************** + * + * Functional description + * Insert next record into a dynamic SQL cursor + * + **************************************/ + REQ request; + MSG message; + PAR parameter; + SCHAR *buffer; + STATUS s; + struct tsql thd_context, *tdsql; + JMP_BUF env; + + SET_THREAD_DATA; + + ERROR_INIT(env); + init(0); + + request = *req_handle; + tdsql->tsql_default = request->req_pool; + +/* if the cursor isn't open, we've got a problem */ + + if (request->req_type == REQ_PUT_SEGMENT) + if (!(request->req_flags & REQ_cursor_open)) + ERRD_post(gds_sqlerr, gds_arg_number, (SLONG) - 504, + gds_arg_gds, gds_dsql_cursor_err, 0); + + message = (MSG) request->req_receive; + +/* Insure that the blr for the message is parsed, regardless of + whether anything is found by the call to receive. */ + + if (blr_length) + parse_blr(blr_length, blr, msg_length, message->msg_parameters); + + if (request->req_type == REQ_PUT_SEGMENT) { + /* For put segment, use the user buffer and indicator directly. */ + + parameter = request->req_blob->blb_segment; + buffer = + reinterpret_cast < + SCHAR * >(msg + (SLONG) parameter->par_user_desc.dsc_address); + THREAD_EXIT; + s = isc_put_segment(tdsql->tsql_status, + reinterpret_cast < + void **>(GDS_REF(request->req_handle)), + parameter->par_user_desc.dsc_length, + GDS_VAL(buffer)); + THREAD_ENTER; + if (s) + punt(); + } + + RETURN_SUCCESS; +} + + +STATUS DLL_EXPORT GDS_DSQL_PREPARE(STATUS * user_status, + isc_tr_handle * trans_handle, + REQ * req_handle, + USHORT length, + TEXT * string, + USHORT dialect, + USHORT item_length, + UCHAR * items, + USHORT buffer_length, UCHAR * buffer) +{ +/************************************** + * + * d s q l _ p r e p a r e + * + ************************************** + * + * Functional description + * Prepare a statement for execution. + * + **************************************/ + REQ old_request, request; + USHORT parser_version; + DBB database; + STATUS status; + struct tsql thd_context, *tdsql; + JMP_BUF env; + + SET_THREAD_DATA; + + ERROR_INIT(env); + init(0); + + old_request = *req_handle; + database = old_request->req_dbb; + +/* check to see if old request has an open cursor */ + + if (old_request && (old_request->req_flags & REQ_cursor_open)) { + ERRD_post(gds_sqlerr, gds_arg_number, (SLONG) - 519, + gds_arg_gds, gds_dsql_open_cursor_request, 0); + } + +/* Allocate a new request block and then prepare the request. We want to + keep the old request around, as is, until we know that we are able + to prepare the new one. */ +/* It would be really *nice* to know *why* we want to + keep the old request around -- 1994-October-27 David Schnepper */ + + tdsql->tsql_default = ALLD_pool(); + + request = (REQ) ALLOCD(type_req); + request->req_dbb = database; + request->req_pool = tdsql->tsql_default; + request->req_trans = (int *) *trans_handle; + + if (SETJMP(*tdsql->tsql_setjmp)) { + status = error(); + release_request(request, TRUE); + RESTORE_THREAD_DATA; + return status; + } + + if (!length) + length = strlen(string); + +/* Figure out which parser version to use */ +/* Since the API to GDS_DSQL_PREPARE is public and can not be changed, there needs to + * be a way to send the parser version to DSQL so that the parser can compare the keyword + * version to the parser version. To accomplish this, the parser version is combined with + * the client dialect and sent across that way. In dsql8_prepare_statement, the parser version + * and client dialect are separated and passed on to their final desintations. The information + * is combined as follows: + * Dialect * 10 + parser_version + * + * and is extracted in dsql8_prepare_statement as follows: + * parser_version = ((dialect *10)+parser_version)%10 + * client_dialect = ((dialect *10)+parser_version)/10 + * + * For example, parser_version = 1 and client dialect = 1 + * + * combined = (1 * 10) + 1 == 11 + * + * parser = (combined) %10 == 1 + * dialect = (combined) / 19 == 1 + * + * If the parser version is not part of the dialect, then assume that the + * connection being made is a local classic connection. + */ + + if ((dialect / 10) == 0) + parser_version = 2; + else { + parser_version = dialect % 10; + dialect /= 10; + } + + request->req_client_dialect = dialect; + + request = prepare(request, length, string, dialect, parser_version); + +/* Can not prepare a CREATE DATABASE/SCHEMA statement */ + + if ((request->req_type == REQ_DDL) && + (request->req_ddl_node->nod_type == nod_def_database)) + ERRD_post(gds_sqlerr, gds_arg_number, (SLONG) - 530, + gds_arg_gds, gds_dsql_crdb_prepare_err, 0); + + request->req_flags |= REQ_prepared; + +/* Now that we know that the new request exists, zap the old one. */ + + tdsql->tsql_default = old_request->req_pool; + release_request(old_request, TRUE); + tdsql->tsql_default = NULL; + +/* The request was sucessfully prepared, and the old request was + * successfully zapped, so set the client's handle to the new request */ + + *req_handle = request; + + RESTORE_THREAD_DATA; + + return GDS_DSQL_SQL_INFO(user_status, req_handle, item_length, + reinterpret_cast < char *>(items), buffer_length, + reinterpret_cast < char *>(buffer)); +} + + +STATUS DLL_EXPORT GDS_DSQL_SET_CURSOR(STATUS * user_status, + REQ * req_handle, + TEXT * input_cursor, USHORT type) +{ +/***************************************** + * + * d s q l _ s e t _ c u r s o r _ n a m e + * + ************************************** + * + * Functional Description + * Set a cursor name for a dynamic request + * + *****************************************/ + REQ request; + SYM symbol; + USHORT length; + struct tsql thd_context, *tdsql; + JMP_BUF env; + TEXT cursor[132]; + + + SET_THREAD_DATA; + + ERROR_INIT(env); + init(0); + + request = *req_handle; + tdsql->tsql_default = request->req_pool; + if (input_cursor[0] == '\"') { + /** Quoted cursor names eh? Strip'em. + Note that "" will be replaced with ". + **/ + int ij; + for (ij = 0; *input_cursor; input_cursor++) { + if (*input_cursor == '\"') + input_cursor++; + cursor[ij++] = *input_cursor; + } + cursor[ij] = 0; + } + else { + USHORT i; + for (i = 0; i < sizeof(cursor) - 1 /* PJPG 20001013 */ + && input_cursor[i] /* PJPG 20001013 */ + &&input_cursor[i] != ' '; i++) /* PJPG 20001013 */ + cursor[i] = UPPER7(input_cursor[i]); + cursor[i] = '\0'; + } + length = name_length(cursor); + + if (length == 0) + ERRD_post(gds_sqlerr, gds_arg_number, (SLONG) - 502, + gds_arg_gds, gds_dsql_decl_err, 0); + +/* If there already is a different cursor by the same name, bitch */ + + if (symbol = HSHD_lookup(request->req_dbb, cursor, length, SYM_cursor, 0)) { + if (request->req_cursor == symbol) + RETURN_SUCCESS; + else + ERRD_post(gds_sqlerr, gds_arg_number, (SLONG) - 502, + gds_arg_gds, gds_dsql_decl_err, 0); + } + +/* If there already is a cursor and its name isn't the same, ditto. + We already know there is no cursor by this name in the hash table */ + + if (!request->req_cursor) + request->req_cursor = MAKE_symbol(request->req_dbb, cursor, + length, SYM_cursor, request); + else { + assert(request->req_cursor != symbol); + ERRD_post(gds_sqlerr, gds_arg_number, (SLONG) - 502, + gds_arg_gds, gds_dsql_decl_err, 0); + }; + + RETURN_SUCCESS; +} + + +STATUS DLL_EXPORT GDS_DSQL_SQL_INFO(STATUS * user_status, + REQ * req_handle, + USHORT item_length, + SCHAR * items, + USHORT info_length, SCHAR * info) +{ +/************************************** + * + * d s q l _ s q l _ i n f o + * + ************************************** + * + * Functional description + * Provide information on dsql statement + * + **************************************/ + REQ request; + MSG *message; + SCHAR item, *end_items, *end_info, *end_describe, buffer[256], + *buffer_ptr; + USHORT length, number, first_index; + struct tsql thd_context, *tdsql; + JMP_BUF env; + + SET_THREAD_DATA; + + ERROR_INIT(env); + init(0); + memset(buffer, 0, sizeof(buffer)); + + request = *req_handle; + + end_items = items + item_length; + end_info = info + info_length; + + message = NULL; + first_index = 0; + + while (items < end_items && *items != gds_info_end) { + item = *items++; + if (item == gds_info_sql_select || item == gds_info_sql_bind) { + message = (item == gds_info_sql_select) ? + &request->req_receive : &request->req_send; + if (info + 1 >= end_info) { + *info = gds_info_truncated; + RETURN_SUCCESS; + } + *info++ = item; + } + else if (item == gds_info_sql_stmt_type) { + switch (request->req_type) { + case REQ_SELECT: + case REQ_EMBED_SELECT: + number = gds_info_sql_stmt_select; + break; + case REQ_SELECT_UPD: + number = gds_info_sql_stmt_select_for_upd; + break; + case REQ_DDL: + number = gds_info_sql_stmt_ddl; + break; + case REQ_GET_SEGMENT: + number = gds_info_sql_stmt_get_segment; + break; + case REQ_PUT_SEGMENT: + number = gds_info_sql_stmt_put_segment; + break; + case REQ_COMMIT: + case REQ_COMMIT_RETAIN: + number = gds_info_sql_stmt_commit; + break; + case REQ_ROLLBACK: + number = gds_info_sql_stmt_rollback; + break; + case REQ_START_TRANS: + number = gds_info_sql_stmt_start_trans; + break; + case REQ_INSERT: + number = gds_info_sql_stmt_insert; + break; + case REQ_UPDATE: + case REQ_UPDATE_CURSOR: + number = gds_info_sql_stmt_update; + break; + case REQ_DELETE: + case REQ_DELETE_CURSOR: + number = gds_info_sql_stmt_delete; + break; + case REQ_EXEC_PROCEDURE: + number = gds_info_sql_stmt_exec_procedure; + break; + case REQ_SET_GENERATOR: + number = isc_info_sql_stmt_set_generator; + break; + default: + number = 0; + break; + } + length = convert((SLONG) number, buffer); + if (!(info = put_item(item, length, buffer, info, end_info))) { + RETURN_SUCCESS; + } + } + else if (item == gds_info_sql_sqlda_start) { + length = *items++; + first_index = + static_cast < USHORT > + (gds__vax_integer + (reinterpret_cast < UCHAR * >(items), length)); + items += length; + } + else if (item == isc_info_sql_batch_fetch) { + if (request->req_flags & REQ_no_batch) + number = FALSE; + else + number = TRUE; + length = convert((SLONG) number, buffer); + if (!(info = put_item(item, length, buffer, info, end_info))) { + RETURN_SUCCESS; + } + } + else if (item == gds_info_sql_records) { + length = + get_request_info(request, (SSHORT) sizeof(buffer), buffer); + if (length + && !(info = put_item(item, length, buffer, info, end_info))) { + RETURN_SUCCESS; + } + } + else if (item == gds_info_sql_get_plan) { + /* be careful, get_plan_info() will reallocate the buffer to a + larger size if it is not big enough */ + + buffer_ptr = buffer; + length = + get_plan_info(request, (SSHORT) sizeof(buffer), &buffer_ptr); + + if (length) + info = put_item(item, length, buffer_ptr, info, end_info); + + if (length > sizeof(buffer)) + gds__free(buffer_ptr); + + if (!info) + RETURN_SUCCESS; + } + else if (!message || + (item != gds_info_sql_num_variables + && item != gds_info_sql_describe_vars)) { + buffer[0] = item; + item = gds_info_error; + length = 1 + convert((SLONG) gds_infunk, buffer + 1); + if (!(info = put_item(item, length, buffer, info, end_info))) { + RETURN_SUCCESS; + } + } + else { + number = (*message) ? (*message)->msg_index : 0; + length = convert((SLONG) number, buffer); + if (!(info = put_item(item, length, buffer, info, end_info))) { + RETURN_SUCCESS; + } + if (item == gds_info_sql_num_variables) + continue; + + end_describe = items; + while (end_describe < end_items && + *end_describe != gds_info_end && + *end_describe != gds_info_sql_describe_end) end_describe++; + + if (! + (info = + var_info(*message, items, end_describe, info, end_info, + first_index))) { + RETURN_SUCCESS; + } + + items = end_describe; + if (*items == gds_info_sql_describe_end) + items++; + } + } + + *info++ = gds_info_end; + + RETURN_SUCCESS; +} + + +#ifdef DEV_BUILD +void DSQL_pretty( NOD node, int column) +{ +/************************************** + * + * D S Q L _ p r e t t y + * + ************************************** + * + * Functional description + * Pretty print a node tree. +* + **************************************/ + MAP map; + NOD *ptr, *end; + DSQL_REL relation; + CTX context; + FLD field; + STR string; + VAR variable; +#ifdef STACK_REDUCTION + TEXT *buffer, *p, *verb, s[64]; +#else + TEXT buffer[1024], *p, *verb, s[64]; +#endif + USHORT l; + +#ifdef STACK_REDUCTION + buffer = (TEXT *) gds__alloc(BUFFER_LARGE); +#endif + + p = buffer; + sprintf(p, "%.7X ", node); + while (*p) + p++; + + if ((l = column * 3) != NULL) + do + *p++ = ' '; + while (--l); + + *p = 0; + + if (!node) { + PRINTF("%s *** null ***\n", buffer); + FREE_MEM_RETURN; + } + + switch (node->nod_header.blk_type) { + case (TEXT) type_str: + PRINTF("%sSTRING: \"%s\"\n", buffer, ((STR) node)->str_data); + FREE_MEM_RETURN; + + case (TEXT) type_fld: + PRINTF("%sFIELD: %s\n", buffer, ((FLD) node)->fld_name); + FREE_MEM_RETURN; + + case (TEXT) type_sym: + PRINTF("%sSYMBOL: %s\n", buffer, ((SYM) node)->sym_string); + FREE_MEM_RETURN; + + case (TEXT) type_nod: + break; + + default: + PRINTF("%sUNKNOWN BLOCK TYPE\n", buffer); + FREE_MEM_RETURN; + } + + ptr = node->nod_arg; + end = ptr + node->nod_count; + + switch (node->nod_type) { + case nod_abort: + verb = "abort"; + break; + case nod_agg_average: + verb = "agg_average"; + break; + case nod_agg_count: + verb = "agg_count"; + break; +/* count2 + case nod_agg_distinct: verb = "agg_distinct"; break; +*/ + case nod_agg_item: + verb = "agg_item"; + break; + case nod_agg_max: + verb = "agg_max"; + break; + case nod_agg_min: + verb = "agg_min"; + break; + case nod_agg_total: + verb = "agg_total"; + break; + case nod_add: + verb = "add"; + break; + case nod_alias: + verb = "alias"; + break; + case nod_ansi_all: + case nod_all: + verb = "all"; + break; + case nod_and: + verb = "and"; + break; + case nod_ansi_any: + case nod_any: + verb = "any"; + break; + case nod_array: + verb = "array element"; + break; + case nod_assign: + verb = "assign"; + break; + case nod_average: + verb = "average"; + break; + case nod_between: + verb = "between"; + break; + case nod_cast: + verb = "cast"; + break; + case nod_close: + verb = "close"; + break; + case nod_collate: + verb = "collate"; + break; + case nod_concatenate: + verb = "concatenate"; + break; + case nod_containing: + verb = "containing"; + break; + case nod_count: + verb = "count"; + break; + case nod_current_date: + verb = "current_date"; + break; + case nod_current_time: + verb = "current_time"; + break; + case nod_current_timestamp: + verb = "current_timestamp"; + break; + case nod_cursor: + verb = "cursor"; + break; + case nod_dbkey: + verb = "dbkey"; + break; + case nod_rec_version: + verb = "record_version"; + break; + case nod_def_database: + verb = "define database"; + break; + case nod_def_field: + verb = "define field"; + break; + case nod_def_generator: + verb = "define generator"; + break; + case nod_def_filter: + verb = "define filter"; + break; + case nod_def_index: + verb = "define index"; + break; + case nod_def_relation: + verb = "define relation"; + break; + case nod_def_view: + verb = "define view"; + break; + case nod_delete: + verb = "delete"; + break; + case nod_del_field: + verb = "delete field"; + break; + case nod_del_filter: + verb = "delete filter"; + break; + case nod_del_generator: + verb = "delete generator"; + break; + case nod_del_index: + verb = "delete index"; + break; + case nod_del_relation: + verb = "delete relation"; + break; + case nod_def_procedure: + verb = "define procedure"; + break; + case nod_del_procedure: + verb = "delete porcedure"; + break; + case nod_def_trigger: + verb = "define trigger"; + break; + case nod_mod_trigger: + verb = "modify trigger"; + break; + case nod_del_trigger: + verb = "delete trigger"; + break; + case nod_divide: + verb = "divide"; + break; + case nod_eql_all: + case nod_eql_any: + case nod_eql: + verb = "eql"; + break; + case nod_erase: + verb = "erase"; + break; + case nod_execute: + verb = "execute"; + break; + case nod_exec_procedure: + verb = "execute procedure"; + break; + case nod_exists: + verb = "exists"; + break; + case nod_extract: + verb = "extract"; + break; + case nod_fetch: + verb = "fetch"; + break; + case nod_flag: + verb = "flag"; + break; + case nod_for: + verb = "for"; + break; + case nod_foreign: + verb = "foreign key"; + break; + case nod_gen_id: + verb = "gen_id"; + break; + case nod_geq_all: + case nod_geq_any: + case nod_geq: + verb = "geq"; + break; + case nod_get_segment: + verb = "get segment"; + break; + case nod_grant: + verb = "grant"; + break; + case nod_gtr_all: + case nod_gtr_any: + case nod_gtr: + verb = "gtr"; + break; + case nod_insert: + verb = "insert"; + break; + case nod_join: + verb = "join"; + break; + case nod_join_full: + verb = "join_full"; + break; + case nod_join_left: + verb = "join_left"; + break; + case nod_join_right: + verb = "join_right"; + break; + case nod_leq_all: + case nod_leq_any: + case nod_leq: + verb = "leq"; + break; + case nod_like: + verb = "like"; + break; + case nod_list: + verb = "list"; + break; + case nod_lss_all: + case nod_lss_any: + case nod_lss: + verb = "lss"; + break; + case nod_max: + verb = "max"; + break; + case nod_min: + verb = "min"; + break; + case nod_missing: + verb = "missing"; + break; + case nod_modify: + verb = "modify"; + break; + case nod_mod_database: + verb = "modify database"; + break; + case nod_mod_field: + verb = "modify field"; + break; + case nod_mod_relation: + verb = "modify relation"; + break; + case nod_multiply: + verb = "multiply"; + break; + case nod_negate: + verb = "negate"; + break; + case nod_neq_all: + case nod_neq_any: + case nod_neq: + verb = "neq"; + break; + case nod_not: + verb = "not"; + break; + case nod_null: + verb = "null"; + break; + case nod_open: + verb = "open"; + break; + case nod_or: + verb = "or"; + break; + case nod_order: + verb = "order"; + break; + case nod_parameter: + verb = "parameter"; + break; + case nod_primary: + verb = "primary key"; + break; + case nod_procedure_name: + verb = "procedure name"; + break; + case nod_put_segment: + verb = "put segment"; + break; + case nod_relation_name: + verb = "relation name"; + break; + case nod_rel_proc_name: + verb = "rel/proc name"; + break; + case nod_retrieve: + verb = "retrieve"; + break; + case nod_return: + verb = "return"; + break; + case nod_revoke: + verb = "revoke"; + break; + case nod_rse: + verb = "rse"; + break; + case nod_select: + verb = "select"; + break; + case nod_select_expr: + verb = "select expr"; + break; + case nod_starting: + verb = "starting"; + break; + case nod_store: + verb = "store"; + break; + case nod_substr: + verb = "substr"; + break; + case nod_subtract: + verb = "subtract"; + break; + case nod_total: + verb = "total"; + break; + case nod_update: + verb = "update"; + break; + case nod_union: + verb = "union"; + break; + case nod_unique: + verb = "unique"; + break; + case nod_upcase: + verb = "upcase"; + break; + case nod_singular: + verb = "singular"; + break; + case nod_user_name: + verb = "user_name"; + break; + case nod_values: + verb = "values"; + break; + case nod_via: + verb = "via"; + break; + + case nod_add2: + verb = "add2"; + break; + case nod_agg_total2: + verb = "agg_total2"; + break; + case nod_divide2: + verb = "divide2"; + break; + case nod_gen_id2: + verb = "gen_id2"; + break; + case nod_multiply2: + verb = "multiply2"; + break; + case nod_subtract2: + verb = "subtract2"; + break; + + case nod_aggregate: + verb = "aggregate"; + PRINTF("%s%s\n", buffer, verb); + context = (CTX) node->nod_arg[e_agg_context]; + PRINTF("%s context %d\n", buffer, context->ctx_context); + if ((map = context->ctx_map) != NULL) + PRINTF("%s map\n", buffer); + while (map) { + PRINTF("%s position %d\n", buffer, map->map_position); + DSQL_pretty(map->map_node, column + 2); + map = map->map_next; + } + DSQL_pretty(node->nod_arg[e_agg_group], column + 1); + DSQL_pretty(node->nod_arg[e_agg_rse], column + 1); + FREE_MEM_RETURN; + + case nod_constant: + verb = "constant"; + if (node->nod_desc.dsc_address) { + if (node->nod_desc.dsc_dtype == dtype_text) + sprintf(s, "constant \"%s\"", node->nod_desc.dsc_address); + else + sprintf(s, "constant %d", + *(SLONG *) (node->nod_desc.dsc_address)); + verb = s; + } + break; + + case nod_field: + context = (CTX) node->nod_arg[e_fld_context]; + relation = context->ctx_relation; + field = (FLD) node->nod_arg[e_fld_field]; + PRINTF("%sfield %s.%s, context %d\n", buffer, + relation->rel_name, field->fld_name, context->ctx_context); + FREE_MEM_RETURN; + + case nod_field_name: + PRINTF("%sfield name: \"", buffer); + string = (STR) node->nod_arg[e_fln_context]; + if (string) + PRINTF("%s.", string->str_data); + string = (STR) node->nod_arg[e_fln_name]; + PRINTF("%s\"\n", string->str_data); + FREE_MEM_RETURN; + + case nod_map: + verb = "map"; + PRINTF("%s%s\n", buffer, verb); + context = (CTX) node->nod_arg[e_map_context]; + PRINTF("%s context %d\n", buffer, context->ctx_context); + for (map = (MAP) node->nod_arg[e_map_map]; map; map = map->map_next) { + PRINTF("%s position %d\n", buffer, map->map_position); + DSQL_pretty(map->map_node, column + 1); + } + FREE_MEM_RETURN; + + case nod_position: + PRINTF("%sposition %d\n", buffer, *ptr); + FREE_MEM_RETURN; + + case nod_relation: + context = (CTX) node->nod_arg[e_rel_context]; + relation = context->ctx_relation; + PRINTF("%srelation %s, context %d\n", + buffer, relation->rel_name, context->ctx_context); + FREE_MEM_RETURN; + + case nod_variable: + variable = (VAR) node->nod_arg[e_var_variable]; + PRINTF("%svariable %s %d\n", buffer, variable->var_name); + FREE_MEM_RETURN; + + case nod_var_name: + PRINTF("%svariable name: \"", buffer); + string = (STR) node->nod_arg[e_vrn_name]; + PRINTF("%s\"\n", string->str_data); + FREE_MEM_RETURN; + + case nod_udf: + PRINTF("%sfunction: \"%s\"\n", buffer, *ptr++); + if (node->nod_count == 2) + DSQL_pretty(*ptr, column + 1); + FREE_MEM_RETURN; + + default: + sprintf(s, "unknown type %d", node->nod_type); + verb = s; + } + + if (node->nod_desc.dsc_dtype) + PRINTF("%s%s (%d,%d,%x)\n", + buffer, verb, + node->nod_desc.dsc_dtype, + node->nod_desc.dsc_length, node->nod_desc.dsc_address); + else + PRINTF("%s%s\n", buffer, verb); + ++column; + + while (ptr < end) + DSQL_pretty(*ptr++, column); + + FREE_MEM_RETURN; +} +#endif + + +static void cleanup( void *arg) +{ +/************************************** + * + * c l e a n u p + * + ************************************** + * + * Functional Description + * exit handler for local dsql image + * + **************************************/ + + if (init_flag) { + init_flag = FALSE; + databases = 0; + open_cursors = 0; + HSHD_fini(); + ALLD_fini(); + } +} + + +static void cleanup_database( SLONG ** db_handle, SLONG flag) +{ +/************************************** + * + * c l e a n u p _ d a t a b a s e + * + ************************************** + * + * Functional description + * Clean up DSQL globals. + * + * N.B., the cleanup handlers (registered with gds__database_cleanup) + * are called outside of the ISC thread mechanism... + * + * These do not make use of the context at this time. + * + **************************************/ + DBB *dbb_ptr, dbb; + STATUS user_status[ISC_STATUS_LENGTH]; + USHORT i; + + if (!db_handle || !databases) + return; + + THD_MUTEX_LOCK(&databases_mutex); + for (dbb_ptr = &databases; dbb = *dbb_ptr; dbb_ptr = &dbb->dbb_next) + if (dbb->dbb_database_handle == *db_handle) { + *dbb_ptr = dbb->dbb_next; + dbb->dbb_next = NULL; + break; + } + + if (dbb) { + if (flag) { + THREAD_EXIT; + for (i = 0; i < irq_MAX; i++) + if (dbb->dbb_requests[i]) + isc_release_request(user_status, + reinterpret_cast < + void + **>(GDS_REF(dbb->dbb_requests[i]))); + THREAD_ENTER; + } + HSHD_finish(dbb); + ALLD_rlpool(dbb->dbb_pool); + } + + if (!databases) { + if (!databases) { + cleanup(0); + gds__unregister_cleanup(cleanup, 0); + } + } + THD_MUTEX_UNLOCK(&databases_mutex); +} + + +static void cleanup_transaction( SLONG * tra_handle, SLONG arg) +{ +/************************************** + * + * c l e a n u p _ t r a n s a c t i o n + * + ************************************** + * + * Functional description + * Clean up after a transaction. This means + * closing all open cursors. + * + **************************************/ + STATUS local_status[ISC_STATUS_LENGTH]; + OPN *open_cursor_ptr, open_cursor; + +/* find this transaction/request pair in the list of pairs */ + + THD_MUTEX_LOCK(&cursors_mutex); + open_cursor_ptr = &open_cursors; + while (open_cursor = *open_cursor_ptr) + if (open_cursor->opn_transaction == tra_handle) { + /* Found it, close the cursor but don't remove it from the list. + The close routine will have done that. */ + + THD_MUTEX_UNLOCK(&cursors_mutex); + /* + * we are expected to be within the subsystem when we do this + * cleanup, for now do a thread_enter/thread_exit here. + * Note that the function GDS_DSQL_FREE() calls the local function. + * Over the long run, it might be better to move the subsystem_exit() + * call in why.c below the cleanup handlers. smistry 9-27-98 + */ + THREAD_ENTER; + GDS_DSQL_FREE(local_status, &open_cursor->opn_request, + DSQL_close); + THREAD_EXIT; + THD_MUTEX_LOCK(&cursors_mutex); + open_cursor_ptr = &open_cursors; + } + else + open_cursor_ptr = &open_cursor->opn_next; + + THD_MUTEX_UNLOCK(&cursors_mutex); +} + + +static void close_cursor( REQ request) +{ +/************************************** + * + * c l o s e _ c u r s o r + * + ************************************** + * + * Functional description + * Close an open cursor. + * + **************************************/ + OPN *open_cursor_ptr, open_cursor; + STATUS status_vector[ISC_STATUS_LENGTH]; + + if (request->req_handle) { + THREAD_EXIT; + if (request->req_type == REQ_GET_SEGMENT || + request->req_type == REQ_PUT_SEGMENT) + isc_close_blob(status_vector, + reinterpret_cast < + void **>(GDS_REF(request->req_handle))); + else + isc_unwind_request(status_vector, + reinterpret_cast < + void **>(GDS_REF(request->req_handle)), 0); + THREAD_ENTER; + } + + request->req_flags &= ~(REQ_cursor_open | REQ_embedded_sql_cursor); + +/* Remove the open cursor from the list */ + + THD_MUTEX_LOCK(&cursors_mutex); + open_cursor_ptr = &open_cursors; + for (; open_cursor = *open_cursor_ptr; + open_cursor_ptr = + &open_cursor->opn_next) if (open_cursor == + request->req_open_cursor) { + *open_cursor_ptr = open_cursor->opn_next; + break; + } + + THD_MUTEX_UNLOCK(&cursors_mutex); + + if (open_cursor) { + ALLD_release(reinterpret_cast(open_cursor)); + request->req_open_cursor = NULL; + } +} + + +static USHORT convert( SLONG number, SCHAR * buffer) +{ +/************************************** + * + * c o n v e r t + * + ************************************** + * + * Functional description + * Convert a number to VAX form -- least significant bytes first. + * Return the length. + * + **************************************/ + SLONG n; + SCHAR *p; + +#ifdef VAX + n = number; + p = (SCHAR *) & n; + *buffer++ = *p++; + *buffer++ = *p++; + *buffer++ = *p++; + *buffer++ = *p++; + +#else + + p = (SCHAR *) (&number + 1); + *buffer++ = *--p; + *buffer++ = *--p; + *buffer++ = *--p; + *buffer++ = *--p; + +#endif + + return 4; +} + + +static STATUS error(void) +{ +/************************************** + * + * e r r o r + * + ************************************** + * + * Functional description + * An error returned has been trapped. + * + **************************************/ + TSQL tdsql; + + tdsql = GET_THREAD_DATA; + + return tdsql->tsql_status[1]; +} + + +static void execute_blob( REQ request, + USHORT in_blr_length, + UCHAR* in_blr, + USHORT in_msg_length, + UCHAR* in_msg, + USHORT out_blr_length, + UCHAR* out_blr, + USHORT out_msg_length, + UCHAR* out_msg) +{ +/************************************** + * + * e x e c u t e _ b l o b + * + ************************************** + * + * Functional description + * Open or create a blob. + * + **************************************/ + BLB blob; + SSHORT filter; + USHORT bpb_length; + GDS__QUAD *blob_id; + UCHAR bpb[24], *p; + PAR parameter, null; + STATUS s; + TSQL tdsql; + + tdsql = GET_THREAD_DATA; + + blob = request->req_blob; + map_in_out(request, blob->blb_open_in_msg, in_blr_length, in_blr, + in_msg_length, in_msg); + + p = bpb; + *p++ = gds_bpb_version1; + if (filter = filter_sub_type(request, blob->blb_to)) { + *p++ = gds_bpb_target_type; + *p++ = 2; + *p++ = static_cast(filter); + *p++ = filter >> 8; + } + if (filter = filter_sub_type(request, blob->blb_from)) { + *p++ = gds_bpb_source_type; + *p++ = 2; + *p++ = static_cast(filter); + *p++ = filter >> 8; + } + bpb_length = p - bpb; + if (bpb_length == 1) + bpb_length = 0; + + parameter = blob->blb_blob_id; + null = parameter->par_null; + + if (request->req_type == REQ_GET_SEGMENT) { + blob_id = (GDS__QUAD *) parameter->par_desc.dsc_address; + if (null && *((SSHORT *) null->par_desc.dsc_address) < 0) + memset(blob_id, 0, sizeof(GDS__QUAD)); + THREAD_EXIT; + s = isc_open_blob2(tdsql->tsql_status, + reinterpret_cast(GDS_REF(request->req_dbb->dbb_database_handle)), + reinterpret_cast(GDS_REF(request->req_trans)), + reinterpret_cast(GDS_REF(request->req_handle)), + GDS_VAL(blob_id), + bpb_length, + reinterpret_cast(bpb)); + THREAD_ENTER; + if (s) + punt(); + } + else { + request->req_handle = NULL; + blob_id = (GDS__QUAD *) parameter->par_desc.dsc_address; + memset(blob_id, 0, sizeof(GDS__QUAD)); + THREAD_EXIT; + s = isc_create_blob2(tdsql->tsql_status, + reinterpret_cast < + void + **>(GDS_REF + (request->req_dbb->dbb_database_handle)), + reinterpret_cast < + void **>(GDS_REF(request->req_trans)), + reinterpret_cast < + void **>(GDS_REF(request->req_handle)), + GDS_VAL(blob_id), bpb_length, + reinterpret_cast < char *>(bpb)); + THREAD_ENTER; + if (s) + punt(); + map_in_out(NULL, blob->blb_open_out_msg, out_blr_length, out_blr, + out_msg_length, out_msg); + } +} + + +static STATUS execute_request(REQ request, + isc_tr_handle* trans_handle, + USHORT in_blr_length, + UCHAR* in_blr, + USHORT in_msg_length, + UCHAR* in_msg, + USHORT out_blr_length, + UCHAR* out_blr, + USHORT out_msg_length, + UCHAR* out_msg, + USHORT singleton) +{ +/************************************** + * + * e x e c u t e _ r e q u e s t + * + ************************************** + * + * Functional description + * Execute a dynamic SQL statement. + * + **************************************/ + MSG message; + USHORT use_msg_length; + UCHAR *use_msg; + SCHAR buffer[20]; + STATUS s, local_status[ISC_STATUS_LENGTH]; + TSQL tdsql; + STATUS return_status; + + tdsql = GET_THREAD_DATA; + + request->req_trans = (int *) *trans_handle; + return_status = SUCCESS; + + switch (request->req_type) { + case REQ_START_TRANS: + THREAD_EXIT; + s = isc_start_transaction( tdsql->tsql_status, + reinterpret_cast(&request->req_trans), + 1, + &request->req_dbb->dbb_database_handle, + (int)(request->req_blr - request->req_blr_string->str_data), + request->req_blr_string->str_data); + THREAD_ENTER; + if (s) + punt(); + *trans_handle = request->req_trans; + return SUCCESS; + + case REQ_COMMIT: + THREAD_EXIT; + s = isc_commit_transaction(tdsql->tsql_status, + reinterpret_cast < + void **>(&request->req_trans)); + THREAD_ENTER; + if (s) + punt(); + *trans_handle = NULL; + return SUCCESS; + + case REQ_COMMIT_RETAIN: + THREAD_EXIT; + s = isc_commit_retaining(tdsql->tsql_status, + reinterpret_cast < + void **>(&request->req_trans)); + THREAD_ENTER; + if (s) + punt(); + return SUCCESS; + + case REQ_ROLLBACK: + THREAD_EXIT; + s = isc_rollback_transaction(tdsql->tsql_status, + reinterpret_cast < + void **>(&request->req_trans)); + THREAD_ENTER; + if (s) + punt(); + *trans_handle = NULL; + return SUCCESS; + + case REQ_DDL: + DDL_execute(request); + return SUCCESS; + + case REQ_GET_SEGMENT: + execute_blob( request, + in_blr_length, + in_blr, + in_msg_length, + in_msg, + out_blr_length, + out_blr, + out_msg_length, + out_msg); + return SUCCESS; + + case REQ_PUT_SEGMENT: + execute_blob( request, + in_blr_length, + in_blr, + in_msg_length, + in_msg, + out_blr_length, + out_blr, + out_msg_length, + out_msg); + return SUCCESS; + + case REQ_EXEC_PROCEDURE: + if (message = (MSG) request->req_send) { + map_in_out(request, message, in_blr_length, in_blr, + in_msg_length, in_msg); + in_msg_length = message->msg_length; + in_msg = message->msg_buffer; + } + else { + in_msg_length = 0; + in_msg = NULL; + } + if (out_msg_length && (message = (MSG) request->req_receive)) { + if (out_blr_length) + parse_blr(out_blr_length, out_blr, out_msg_length, + message->msg_parameters); + use_msg_length = message->msg_length; + use_msg = message->msg_buffer; + } + else { + use_msg_length = 0; + use_msg = NULL; + } + THREAD_EXIT; + s = isc_transact_request(tdsql->tsql_status, + reinterpret_cast < + void **>(&request->req_dbb-> + dbb_database_handle), + reinterpret_cast < + void **>(&request->req_trans), + (USHORT) (request->req_blr - + request->req_blr_string->str_data), + reinterpret_cast < + char *>(request->req_blr_string->str_data), + in_msg_length, + reinterpret_cast < char *>(in_msg), + use_msg_length, + reinterpret_cast < char *>(use_msg)); + THREAD_ENTER; + + if (s) + punt(); + if (out_msg_length && message) + map_in_out(NULL, message, 0, out_blr, out_msg_length, out_msg); + return SUCCESS; + + default: + /* Catch invalid request types */ + assert(FALSE); + /* Fall into ... */ + + case REQ_SELECT: + case REQ_SELECT_UPD: + case REQ_INSERT: + case REQ_DELETE: + case REQ_UPDATE: + case REQ_UPDATE_CURSOR: + case REQ_DELETE_CURSOR: + case REQ_EMBED_SELECT: + case REQ_SET_GENERATOR: + break; + } + +/* If there is no data required, just start the request */ + + if (!(message = (MSG) request->req_send)) { + THREAD_EXIT; + s = isc_start_request(tdsql->tsql_status, + reinterpret_cast < + void **>(&request->req_handle), + reinterpret_cast < + void **>(&request->req_trans), 0); + THREAD_ENTER; + if (s) + punt(); + } + else { + map_in_out(request, message, in_blr_length, in_blr, in_msg_length, + in_msg); + + THREAD_EXIT; + s = isc_start_and_send(tdsql->tsql_status, + reinterpret_cast < + void **>(&request->req_handle), + reinterpret_cast < + void **>(&request->req_trans), + message->msg_number, message->msg_length, + message->msg_buffer, 0); + THREAD_ENTER; + if (s) + punt(); + } + + if (out_msg_length && (message = (MSG) request->req_receive)) { + /* Insure that the blr for the message is parsed, regardless of + whether anything is found by the call to receive. */ + + if (out_blr_length) + parse_blr(out_blr_length, out_blr, out_msg_length, + message->msg_parameters); + + THREAD_EXIT; + s = isc_receive(tdsql->tsql_status, + reinterpret_cast < void **>(&request->req_handle), + message->msg_number, + message->msg_length, message->msg_buffer, 0); + THREAD_ENTER; + if (s) + punt(); + + map_in_out(NULL, message, 0, out_blr, out_msg_length, out_msg); + + /* if this is a singleton select, make sure there's in fact one record */ + + if (singleton) { + UCHAR *message_buffer; + USHORT counter; + + /* Create a temp message buffer and try two more receives. + If both succeed then the first is the next record and the + second is either another record or the end of record message. + In either case, there's more than one record. */ + + message_buffer = ALLD_malloc((ULONG) message->msg_length); + s = 0; + THREAD_EXIT; + for (counter = 0; counter < 2 && !s; counter++) { + s = isc_receive(local_status, + reinterpret_cast < + void **>(&request->req_handle), + message->msg_number, message->msg_length, + message_buffer, 0); + } + THREAD_ENTER; + ALLD_free(reinterpret_cast < SCHAR * >(message_buffer)); + + /* two successful receives means more than one record + a req_sync error on the first pass above means no records + a non-req_sync error on any of the passes above is an error */ + + if (!s) { + tdsql->tsql_status[0] = gds_arg_gds; + tdsql->tsql_status[1] = gds_sing_select_err; + tdsql->tsql_status[2] = gds_arg_end; + return_status = gds_sing_select_err; + } + else if (s == gds_req_sync && counter == 1) { + tdsql->tsql_status[0] = gds_arg_gds; + tdsql->tsql_status[1] = gds_stream_eof; + tdsql->tsql_status[2] = gds_arg_end; + return_status = gds_stream_eof; + } + else if (s != gds_req_sync) + punt(); + } + } + + if (!(request->req_dbb->dbb_flags & DBB_v3)) { + if (request->req_type == REQ_UPDATE_CURSOR) { + GDS_DSQL_SQL_INFO(local_status, &request, + sizeof(sql_records_info), + const_cast < char *>(sql_records_info), + sizeof(buffer), buffer); + if (!request->req_updates) + ERRD_post(isc_sqlerr, isc_arg_number, (SLONG) - 913, + isc_arg_gds, isc_deadlock, isc_arg_gds, + isc_update_conflict, 0); + } + else if (request->req_type == REQ_DELETE_CURSOR) { + GDS_DSQL_SQL_INFO(local_status, &request, + sizeof(sql_records_info), + const_cast < char *>(sql_records_info), + sizeof(buffer), buffer); + if (!request->req_deletes) + ERRD_post(isc_sqlerr, isc_arg_number, (SLONG) - 913, + isc_arg_gds, isc_deadlock, isc_arg_gds, + isc_update_conflict, 0); + } + } + return return_status; +} + + +static SSHORT filter_sub_type( REQ request, NOD node) +{ +/************************************** + * + * f i l t e r _ s u b _ t y p e + * + ************************************** + * + * Functional description + * Determine the sub_type to use in filtering + * a blob. + * + **************************************/ + PAR parameter, null; + + if (node->nod_type == nod_constant) + return (SSHORT) node->nod_arg[0]; + + parameter = (PAR) node->nod_arg[e_par_parameter]; + if (null = parameter->par_null) + if (*((SSHORT *) null->par_desc.dsc_address)) + return 0; + + return *((SSHORT *) parameter->par_desc.dsc_address); +} + + +static BOOLEAN get_indices( + SSHORT * explain_length_ptr, + SCHAR ** explain_ptr, + SSHORT * plan_length_ptr, SCHAR ** plan_ptr) +{ +/************************************** + * + * g e t _ i n d i c e s + * + ************************************** + * + * Functional description + * Retrieve the indices from the index tree in + * the request info buffer, and print them out + * in the plan buffer. + * + **************************************/ + SCHAR *explain, *plan; + SSHORT explain_length, plan_length; + USHORT length; + + explain_length = *explain_length_ptr; + explain = *explain_ptr; + plan_length = *plan_length_ptr; + plan = *plan_ptr; + +/* go through the index tree information, just + extracting the indices used */ + + explain_length--; + switch (*explain++) { + case gds_info_rsb_and: + case gds_info_rsb_or: + if (get_indices(&explain_length, &explain, &plan_length, &plan)) + return FAILURE; + if (get_indices(&explain_length, &explain, &plan_length, &plan)) + return FAILURE; + break; + + case gds_info_rsb_dbkey: + break; + + case gds_info_rsb_index: + explain_length--; + length = *explain++; + + /* if this isn't the first index, put out a comma */ + + if (plan[-1] != '(' && plan[-1] != ' ') { + if (--plan_length < 0) + return FAILURE; + *plan++ = ','; + } + + /* now put out the index name */ + + if ((plan_length -= length) < 0) + return FAILURE; + explain_length -= length; + while (length--) + *plan++ = *explain++; + break; + + default: + return FAILURE; + } + + *explain_length_ptr = explain_length; + *explain_ptr = explain; + *plan_length_ptr = plan_length; + *plan_ptr = plan; + + return SUCCESS; +} + + +static USHORT get_plan_info( + REQ request, + SSHORT buffer_length, SCHAR ** buffer) +{ +/************************************** + * + * g e t _ p l a n _ i n f o + * + ************************************** + * + * Functional description + * Get the access plan for the request and turn + * it into a textual representation suitable for + * human reading. + * + **************************************/ + SCHAR explain_buffer[256], *explain, *plan, *explain_ptr, *buffer_ptr; + SSHORT explain_length, i; + USHORT join_count = 0, level = 0; + TSQL tdsql; + STATUS s; + + tdsql = GET_THREAD_DATA; + memset(explain_buffer, 0, sizeof(explain_buffer)); + explain_ptr = explain_buffer; + buffer_ptr = *buffer; + +/* get the access path info for the underlying request from the engine */ + + THREAD_EXIT; + s = isc_request_info(tdsql->tsql_status, + reinterpret_cast < void **>(&request->req_handle), + 0, + sizeof(explain_info), + const_cast < char *>(explain_info), + sizeof(explain_buffer), explain_buffer); + THREAD_ENTER; + + if (s) + return 0; + explain = explain_buffer; + + if (*explain == gds_info_truncated) { + explain_ptr = (SCHAR *) gds__alloc(BUFFER_XLARGE); + + THREAD_EXIT; + s = isc_request_info(tdsql->tsql_status, + reinterpret_cast < + void **>(&request->req_handle), 0, + sizeof(explain_info), + const_cast < char *>(explain_info), + BUFFER_XLARGE, explain_ptr); + THREAD_ENTER; + + if (s) + return 0; + } + + for (i = 0; i < 2; i++) { + explain = explain_ptr; + if (*explain++ != gds_info_access_path) + return 0; + + explain_length = (UCHAR) * explain++; + explain_length += (UCHAR) (*explain++) << 8; + + plan = buffer_ptr; + + /* keep going until we reach the end of the explain info */ + + while (explain_length > 0 && buffer_length > 0) + if (get_rsb_item + (&explain_length, &explain, &buffer_length, &plan, + &join_count, &level)) { + /* assume we have run out of room in the buffer, try again with a larger one */ + + buffer_ptr = + reinterpret_cast < char *>(gds__alloc(BUFFER_XLARGE)); + buffer_length = BUFFER_XLARGE; + break; + } + + if (buffer_ptr == *buffer) + break; + } + + + if (explain_ptr != explain_buffer) + gds__free(explain_ptr); + + *buffer = buffer_ptr; + return plan - *buffer; +} + + +static USHORT get_request_info( + REQ request, + SSHORT buffer_length, SCHAR * buffer) +{ +/************************************** + * + * g e t _ r e q u e s t _ i n f o + * + ************************************** + * + * Functional description + * Get the records updated/deleted for record + * + **************************************/ + SCHAR *data; + UCHAR p; + TSQL tdsql; + STATUS s; + USHORT data_length; + + tdsql = GET_THREAD_DATA; + +/* get the info for the request from the engine */ + + THREAD_EXIT; + s = isc_request_info(tdsql->tsql_status, + reinterpret_cast < void **>(&request->req_handle), + 0, + sizeof(record_info), + const_cast < char *>(record_info), + buffer_length, buffer); + THREAD_ENTER; + + if (s) + return 0; + + data = buffer; + + request->req_updates = request->req_deletes = 0; + request->req_selects = request->req_inserts = 0; + + while ((p = *data++) != gds_info_end) { + data_length = + static_cast < USHORT > + (gds__vax_integer(reinterpret_cast < UCHAR * >(data), 2)); + data += 2; + + switch (p) { + case gds_info_req_update_count: + request->req_updates = + gds__vax_integer(reinterpret_cast < UCHAR * >(data), + data_length); + break; + + case gds_info_req_delete_count: + request->req_deletes = + gds__vax_integer(reinterpret_cast < UCHAR * >(data), + data_length); + break; + + case gds_info_req_select_count: + request->req_selects = + gds__vax_integer(reinterpret_cast < UCHAR * >(data), + data_length); + break; + + case gds_info_req_insert_count: + request->req_inserts = + gds__vax_integer(reinterpret_cast < UCHAR * >(data), + data_length); + break; + + default: + break; + }; + + data += data_length; + } + + return data - buffer; +} + + +static BOOLEAN get_rsb_item( + SSHORT * explain_length_ptr, + SCHAR ** explain_ptr, + SSHORT * plan_length_ptr, +SCHAR ** plan_ptr, USHORT * parent_join_count, USHORT * level_ptr) +{ +/************************************** + * + * g e t _ r s b _ i t e m + * + ************************************** + * + * Functional description + * Use recursion to print out a reverse-polish + * access plan of joins and join types. + * + **************************************/ + SCHAR *explain, *plan; + CONST SCHAR *p; + SSHORT explain_length, plan_length, rsb_type, length; + USHORT join_count, union_count, union_level, union_join_count, save_level; + + explain_length = *explain_length_ptr; + explain = *explain_ptr; + plan_length = *plan_length_ptr; + plan = *plan_ptr; + + explain_length--; + switch (*explain++) { + case gds_info_rsb_begin: + if (!*level_ptr) { + /* put out the PLAN prefix */ + + p = "\nPLAN "; + if ((plan_length -= strlen(p)) < 0) + return FAILURE; + while (*p) + *plan++ = *p++; + } + + (*level_ptr)++; + break; + + case gds_info_rsb_end: + (*level_ptr)--; + break; + + case gds_info_rsb_relation: + + /* for the single relation case, initiate + the relation with a parenthesis */ + + if (!*parent_join_count) { + if (--plan_length < 0) + return FAILURE; + *plan++ = '('; + } + + /* if this isn't the first relation, put out a comma */ + + if (plan[-1] != '(') { + if (--plan_length < 0) + return FAILURE; + *plan++ = ','; + } + + /* put out the relation name */ + + explain_length--; + explain_length -= (length = (UCHAR) * explain++); + if ((plan_length -= length) < 0) + return FAILURE; + while (length--) + *plan++ = *explain++; + break; + + case gds_info_rsb_type: + explain_length--; + switch (rsb_type = *explain++) { + /* for stream types which have multiple substreams, print out + the stream type and recursively print out the substreams so + we will know where to put the parentheses */ + + case gds_info_rsb_union: + + /* put out all the substreams of the join */ + + explain_length--; + union_count = (USHORT) * explain++ - 1; + + /* finish the first union member */ + + union_level = *level_ptr; + union_join_count = 0; + while (TRUE) { + if (get_rsb_item + (&explain_length, &explain, &plan_length, &plan, + &union_join_count, &union_level)) return FAILURE; + if (union_level == *level_ptr) + break; + } + + /* for the rest of the members, start the level at 0 so each + gets its own "PLAN ... " line */ + + while (union_count) { + union_join_count = 0; + union_level = 0; + while (TRUE) { + if (get_rsb_item + (&explain_length, &explain, &plan_length, &plan, + &union_join_count, &union_level)) return FAILURE; + if (!union_level) + break; + } + union_count--; + } + break; + + case gds_info_rsb_cross: + case gds_info_rsb_left_cross: + case gds_info_rsb_merge: + + /* if this join is itself part of a join list, + but not the first item, then put out a comma */ + + if (*parent_join_count && plan[-1] != '(') { + if (--plan_length < 0) + return FAILURE; + *plan++ = ','; + } + + /* put out the join type */ + + if (rsb_type == gds_info_rsb_cross || + rsb_type == gds_info_rsb_left_cross) p = "JOIN ("; + else + p = "MERGE ("; + + if ((plan_length -= strlen(p)) < 0) + return FAILURE; + while (*p) + *plan++ = *p++; + + /* put out all the substreams of the join */ + + explain_length--; + join_count = (USHORT) * explain++; + while (join_count) + if (get_rsb_item(&explain_length, &explain, &plan_length, + &plan, &join_count, level_ptr)) + return FAILURE; + + /* put out the final parenthesis for the join */ + + if (--plan_length < 0) + return FAILURE; + else + *plan++ = ')'; + + /* this qualifies as a stream, so decrement the join count */ + + if (*parent_join_count) + -- * parent_join_count; + break; + + case gds_info_rsb_indexed: + case gds_info_rsb_navigate: + case gds_info_rsb_sequential: + case gds_info_rsb_ext_sequential: + case gds_info_rsb_ext_indexed: + if (rsb_type == gds_info_rsb_indexed || + rsb_type == gds_info_rsb_ext_indexed) p = " INDEX ("; + else if (rsb_type == gds_info_rsb_navigate) + p = " ORDER "; + else + p = " NATURAL"; + + if ((plan_length -= strlen(p)) < 0) + return FAILURE; + while (*p) + *plan++ = *p++; + + /* print out additional index information */ + + if (rsb_type == gds_info_rsb_indexed || + rsb_type == gds_info_rsb_navigate || + rsb_type == gds_info_rsb_ext_indexed) { + if (get_indices + (&explain_length, &explain, &plan_length, + &plan)) return FAILURE; + } + + if (rsb_type == gds_info_rsb_indexed || + rsb_type == gds_info_rsb_ext_indexed) { + if (--plan_length < 0) + return FAILURE; + *plan++ = ')'; + } + + /* detect the end of a single relation and put out a final parenthesis */ + + if (!*parent_join_count) + if (--plan_length < 0) + return FAILURE; + else + *plan++ = ')'; + + /* this also qualifies as a stream, so decrement the join count */ + + if (*parent_join_count) + -- * parent_join_count; + break; + + case gds_info_rsb_sort: + + /* if this sort is on behalf of a union, don't bother to + print out the sort, because unions handle the sort on all + substreams at once, and a plan maps to each substream + in the union, so the sort doesn't really apply to a particular plan */ + + if (explain_length > 2 && + (explain[0] == gds_info_rsb_begin) && + (explain[1] == gds_info_rsb_type) && + (explain[2] == gds_info_rsb_union)) break; + + /* if this isn't the first item in the list, put out a comma */ + + if (*parent_join_count && plan[-1] != '(') { + if (--plan_length < 0) + return FAILURE; + *plan++ = ','; + } + + p = "SORT ("; + + if ((plan_length -= strlen(p)) < 0) + return FAILURE; + while (*p) + *plan++ = *p++; + + /* the rsb_sort should always be followed by a begin...end block, + allowing us to include everything inside the sort in parentheses */ + + save_level = *level_ptr; + while (explain_length > 0 && plan_length > 0) { + if (get_rsb_item(&explain_length, &explain, &plan_length, + &plan, parent_join_count, level_ptr)) + return FAILURE; + if (*level_ptr == save_level) + break; + } + + if (--plan_length < 0) + return FAILURE; + *plan++ = ')'; + break; + + default: + break; + } + break; + + default: + break; + } + + *explain_length_ptr = explain_length; + *explain_ptr = explain; + *plan_length_ptr = plan_length; + *plan_ptr = plan; + + return SUCCESS; +} + + +static DBB init( SLONG ** db_handle) +{ +/************************************** + * + * i n i t + * + ************************************** + * + * Functional description + * Initialize dynamic SQL. This is called only once. + * + **************************************/ + DBB database; + STATUS user_status[ISC_STATUS_LENGTH]; + PLB pool; + SCHAR buffer[128], *data; + UCHAR p; + SSHORT l; + STATUS s; + +#ifdef ANY_THREADING + if (!mutex_inited) { + mutex_inited = TRUE; + THD_MUTEX_INIT(&databases_mutex); + THD_MUTEX_INIT(&cursors_mutex); + } +#endif + + THD_MUTEX_LOCK(&databases_mutex); + + if (!init_flag) { + init_flag = TRUE; + ALLD_init(); + HSHD_init(); + +#ifdef DEV_BUILD + DSQL_debug = 0; +#endif + + LEX_dsql_init(); + + gds__register_cleanup(cleanup, 0); + } + + if (!db_handle) { + THD_MUTEX_UNLOCK(&databases_mutex); + return NULL; + } + +/* Look for database block. If we don't find one, allocate one. */ + + for (database = databases; database; database = database->dbb_next) + if (database->dbb_database_handle == *db_handle) { + THD_MUTEX_UNLOCK(&databases_mutex); + return database; + } + + pool = ALLD_pool(); + database = (DBB) ALLOC(type_dbb, pool); + database->dbb_pool = pool; + database->dbb_next = databases; + databases = database; + database->dbb_database_handle = *db_handle; + THD_MUTEX_UNLOCK(&databases_mutex); + + THREAD_EXIT; + gds__database_cleanup(user_status, + reinterpret_cast < hndl ** >(db_handle), + reinterpret_cast < void (*)() > (cleanup_database), + (SLONG) FALSE); + THREAD_ENTER; + +/* Determine if the database is V3 or V4 */ + + database->dbb_flags |= DBB_v3; + THREAD_EXIT; + s = isc_database_info(user_status, reinterpret_cast < void **>(db_handle), + sizeof(db_hdr_info_items), + const_cast < char *>(db_hdr_info_items), + sizeof(buffer), buffer); + THREAD_ENTER; + + if (s) + return database; + +/* assume that server can not report current character set, + and if not then emulate pre-patch actions. */ + database->dbb_att_charset = 127; + + data = buffer; + while ((p = *data++) != gds_info_end) { + l = + static_cast < + short >(gds__vax_integer(reinterpret_cast < UCHAR * >(data), 2)); + data += 2; + + switch (p) { + case isc_info_db_sql_dialect: + database->dbb_db_SQL_dialect = (USHORT) data[0]; + break; + + case gds_info_ods_version: + if (gds__vax_integer(reinterpret_cast < UCHAR * >(data), l) > 7) + database->dbb_flags &= ~DBB_v3; + break; + + /* This flag indicates the version level of the engine + itself, so we can tell what capabilities the engine + code itself (as opposed to the on-disk structure). + Apparently the base level up to now indicated the major + version number, but for 4.1 the base level is being + incremented, so the base level indicates an engine version + as follows: + 1 == v1.x + 2 == v2.x + 3 == v3.x + 4 == v4.0 only + 5 == v4.1 + Note: this info item is so old it apparently uses an + archaic format, not a standard vax integer format. + */ + + case gds_info_base_level: + database->dbb_base_level = (USHORT) data[1]; + break; + +#ifdef READONLY_DATABASE + case isc_info_db_read_only: + if ((USHORT) data[0]) + database->dbb_flags |= DBB_read_only; + else + database->dbb_flags &= ~DBB_read_only; + break; +#endif /* READONLY_DATABASE */ + + case frb_info_att_charset: + database->dbb_att_charset = + static_cast < + short + >(gds__vax_integer(reinterpret_cast < UCHAR * >(data), 2)); + break; + + default: + break; + } + + data += l; + } + + + + return database; +} + + +static void map_in_out( REQ request, + MSG message, + USHORT blr_length, + UCHAR* blr, + USHORT msg_length, + UCHAR* msg) +{ +/************************************** + * + * m a p _ i n _ o u t + * + ************************************** + * + * Functional description + * Map data from external world into message or + * from message to external world. + * + **************************************/ + PAR parameter, dbkey, null, rec_version; + USHORT count, length, null_offset; + DSC desc; + SSHORT *flag; + + count = parse_blr(blr_length, blr, msg_length, message->msg_parameters); + +/* When mapping data from the external world, request will be non-NULL. + When mapping data from an internal message, request will be NULL. */ + + for (parameter = message->msg_parameters; parameter; + parameter = parameter->par_next) if (parameter->par_index) { + /* Make sure the message given to us is long enough */ + + desc = parameter->par_user_desc; + length = (SLONG) desc.dsc_address + desc.dsc_length; + if (length > msg_length) + break; + + flag = NULL; + if ((null = parameter->par_null) != NULL) { + null_offset = reinterpret_cast(null->par_user_desc.dsc_address); + length = null_offset + sizeof(SSHORT); + if (length > msg_length) + break; + + if (!request) { + flag = (SSHORT *) (msg + null_offset); + *flag = *((SSHORT *) null->par_desc.dsc_address); + } + else { + flag = (SSHORT *) null->par_desc.dsc_address; + *flag = *((SSHORT *) (msg + null_offset)); + } + } + + desc.dsc_address = msg + (SLONG) desc.dsc_address; + if (!request) + MOVD_move(¶meter->par_desc, &desc); + else if (!flag || *flag >= 0) + MOVD_move(&desc, ¶meter->par_desc); + + count--; + } + +/* If we got here because the loop was exited early or if part of the + message given to us hasn't been used, complain. */ + + if (parameter || count) + ERRD_post(gds_sqlerr, gds_arg_number, (SLONG) - 804, + gds_arg_gds, gds_dsql_sqlda_err, 0); + + if (request && + ((dbkey = request->req_parent_dbkey) != NULL) && + ((parameter = request->req_dbkey) != NULL)) { + MOVD_move(&dbkey->par_desc, ¶meter->par_desc); + if ((null = parameter->par_null) != NULL) { + flag = (SSHORT *) null->par_desc.dsc_address; + *flag = 0; + } + } + + if (request && + ((rec_version = request->req_parent_rec_version) != NULL) && + ((parameter = request->req_rec_version) != NULL)) { + MOVD_move(&rec_version->par_desc, ¶meter->par_desc); + if ((null = parameter->par_null) != NULL) { + flag = (SSHORT *) null->par_desc.dsc_address; + *flag = 0; + } + } +} + + +static USHORT name_length( TEXT * name) +{ +/************************************** + * + * n a m e _ l e n g t h + * + ************************************** + * + * Functional description + * Compute length of user supplied name. + * + **************************************/ + TEXT *p, *q; +#define BLANK '\040' + + q = name - 1; + for (p = name; *p; p++) { + if (*p != BLANK) + q = p; + } + + return (USHORT) ((q + 1) - name); +} + + +static USHORT parse_blr( + USHORT blr_length, + UCHAR * blr, USHORT msg_length, PAR parameters) +{ +/************************************** + * + * p a r s e _ b l r + * + ************************************** + * + * Functional description + * Parse the message of a blr request. + * + **************************************/ + PAR parameter, null; + USHORT count, index, offset, align, null_offset; + DSC desc; + +/* If there's no blr length, then the format of the current message buffer + is identical to the format of the previous one. */ + + if (!blr_length) { + count = 0; + for (parameter = parameters; parameter; + parameter = parameter->par_next) if (parameter->par_index) + count++; + return count; + } + + if (*blr != blr_version4 && *blr != blr_version5) + ERRD_post(gds_sqlerr, gds_arg_number, (SLONG) - 804, + gds_arg_gds, gds_dsql_sqlda_err, 0); + blr++; /* skip the blr_version */ + if (*blr++ != blr_begin || *blr++ != blr_message) + ERRD_post(gds_sqlerr, gds_arg_number, (SLONG) - 804, + gds_arg_gds, gds_dsql_sqlda_err, 0); + + blr++; /* skip the message number */ + count = *blr++; + count += (*blr++) << 8; + count /= 2; + + offset = 0; + for (index = 1; index <= count; index++) { + desc.dsc_scale = 0; + desc.dsc_sub_type = 0; + desc.dsc_flags = 0; + switch (*blr++) { + case blr_text: + desc.dsc_dtype = dtype_text; + desc.dsc_sub_type = ttype_dynamic; + desc.dsc_length = *blr++; + desc.dsc_length += (*blr++) << 8; + break; + + case blr_varying: + desc.dsc_dtype = dtype_varying; + desc.dsc_sub_type = ttype_dynamic; + desc.dsc_length = *blr++ + sizeof(USHORT); + desc.dsc_length += (*blr++) << 8; + break; + + case blr_text2: + desc.dsc_dtype = dtype_text; + desc.dsc_sub_type = *blr++; + desc.dsc_sub_type += (*blr++) << 8; + desc.dsc_length = *blr++; + desc.dsc_length += (*blr++) << 8; + break; + + case blr_varying2: + desc.dsc_dtype = dtype_varying; + desc.dsc_sub_type = *blr++; + desc.dsc_sub_type += (*blr++) << 8; + desc.dsc_length = *blr++ + sizeof(USHORT); + desc.dsc_length += (*blr++) << 8; + break; + + case blr_short: + desc.dsc_dtype = dtype_short; + desc.dsc_length = sizeof(SSHORT); + desc.dsc_scale = *blr++; + break; + + case blr_long: + desc.dsc_dtype = dtype_long; + desc.dsc_length = sizeof(SLONG); + desc.dsc_scale = *blr++; + break; + + case blr_int64: + desc.dsc_dtype = dtype_int64; + desc.dsc_length = sizeof(SINT64); + desc.dsc_scale = *blr++; + break; + + case blr_quad: + desc.dsc_dtype = dtype_quad; + desc.dsc_length = sizeof(SLONG) * 2; + desc.dsc_scale = *blr++; + break; + + case blr_float: + desc.dsc_dtype = dtype_real; + desc.dsc_length = sizeof(float); + break; + + case blr_double: +#ifndef VMS + case blr_d_float: +#endif + desc.dsc_dtype = dtype_double; + desc.dsc_length = sizeof(double); + break; + +#ifdef VMS + case blr_d_float: + desc.dsc_dtype = dtype_d_float; + desc.dsc_length = sizeof(double); + break; +#endif + + case blr_timestamp: + desc.dsc_dtype = dtype_timestamp; + desc.dsc_length = sizeof(SLONG) * 2; + break; + + case blr_sql_date: + desc.dsc_dtype = dtype_sql_date; + desc.dsc_length = sizeof(SLONG); + break; + + case blr_sql_time: + desc.dsc_dtype = dtype_sql_time; + desc.dsc_length = sizeof(SLONG); + break; + + default: + ERRD_post(gds_sqlerr, gds_arg_number, (SLONG) - 804, + gds_arg_gds, gds_dsql_sqlda_err, 0); + } + + align = type_alignments[desc.dsc_dtype]; + if (align) + offset = FB_ALIGN(offset, align); + desc.dsc_address = (UCHAR *) offset; + offset += desc.dsc_length; + + if (*blr++ != blr_short || *blr++ != 0) + ERRD_post(gds_sqlerr, gds_arg_number, (SLONG) - 804, + gds_arg_gds, gds_dsql_sqlda_err, 0); + + align = type_alignments[dtype_short]; + if (align) + offset = FB_ALIGN(offset, align); + null_offset = offset; + offset += sizeof(SSHORT); + + for (parameter = parameters; parameter; + parameter = + parameter->par_next) if (parameter->par_index == index) { + parameter->par_user_desc = desc; + if (null = parameter->par_null) { + null->par_user_desc.dsc_dtype = dtype_short; + null->par_user_desc.dsc_scale = 0; + null->par_user_desc.dsc_length = sizeof(SSHORT); + null->par_user_desc.dsc_address = (UCHAR *) null_offset; + } + } + } + + if (*blr++ != (UCHAR) blr_end || offset != msg_length) + ERRD_post(gds_sqlerr, gds_arg_number, (SLONG) - 804, + gds_arg_gds, gds_dsql_sqlda_err, 0); + + return count; +} + + +static REQ prepare( + REQ request, + USHORT string_length, + TEXT * string, + USHORT client_dialect, USHORT parser_version) +{ +/************************************** + * + * p r e p a r e + * + ************************************** + * + * Functional description + * Prepare a statement for execution. Return SQL status + * code. Note: caller is responsible for pool handing. + * + **************************************/ + STATUS status; + NOD node; + MSG message; + TEXT *p; + USHORT length; + TSQL tdsql; + BOOLEAN stmt_ambiguous = FALSE; + STATUS local_status[ISC_STATUS_LENGTH]; + + tdsql = GET_THREAD_DATA; + + MOVE_CLEAR(local_status, sizeof(STATUS) * ISC_STATUS_LENGTH); + + if (client_dialect > SQL_DIALECT_CURRENT) + ERRD_post(gds_sqlerr, gds_arg_number, (SLONG) - 901, + gds_arg_gds, gds_wish_list, 0); + + if (!string_length) + string_length = strlen(string); + +/* Get rid of the trailing ";" if there is one. */ + + for (p = string + string_length; p-- > string;) + if (*p != ' ') { + if (*p == ';') + string_length = p - string; + break; + } + +/* Allocate a storage pool and get ready to parse */ + + LEX_string(string, string_length, request->req_dbb->dbb_att_charset); + +/* Parse the SQL statement. If it croaks, return */ + + if (dsql_yyparse + (client_dialect, request->req_dbb->dbb_db_SQL_dialect, parser_version, + &stmt_ambiguous)) { + ERRD_post(gds_sqlerr, gds_arg_number, (SLONG) - 104, gds_arg_gds, gds_command_end_err, /* Unexpected end of command */ + 0); + } + +/* allocate the send and receive messages */ + + request->req_send = (MSG) ALLOCD(type_msg); + request->req_receive = message = (MSG) ALLOCD(type_msg); + message->msg_number = 1; + +#ifdef SCROLLABLE_CURSORS + if (request->req_dbb->dbb_base_level >= 5) { + /* allocate a message in which to send scrolling information + outside of the normal send/receive protocol */ + + request->req_async = message = (MSG) ALLOCD(type_msg); + message->msg_number = 2; + } +#endif + + request->req_type = REQ_SELECT; + request->req_flags &= ~(REQ_cursor_open | REQ_embedded_sql_cursor); + +/* + * No work is done during pass1 for set transaction - like + * checking for valid table names. This is because that will + * require a valid transaction handle. + * Error will be caught at execute time. + */ + + node = PASS1_statement(request, DSQL_parse, 0); + if (!node) + return request; + +/* stop here for requests not requiring code generation */ + + if (request->req_type == REQ_DDL && stmt_ambiguous && + request->req_dbb->dbb_db_SQL_dialect != client_dialect) + ERRD_post(gds_sqlerr, gds_arg_number, (SLONG) - 817, + gds_arg_gds, isc_ddl_not_allowed_by_db_sql_dial, + gds_arg_number, + (SLONG) request->req_dbb->dbb_db_SQL_dialect, 0); + + if (request->req_type == REQ_COMMIT || + request->req_type == REQ_COMMIT_RETAIN || + request->req_type == REQ_ROLLBACK) return request; + +/* Work on blob segment requests */ + + if (request->req_type == REQ_GET_SEGMENT || + request->req_type == REQ_PUT_SEGMENT) { + GEN_port(request, request->req_blob->blb_open_in_msg); + GEN_port(request, request->req_blob->blb_open_out_msg); + GEN_port(request, request->req_blob->blb_segment_msg); + return request; + } + +/* Generate BLR, DDL or TPB for request */ + + if (request->req_type == REQ_START_TRANS || + request->req_type == REQ_DDL || + request->req_type == REQ_EXEC_PROCEDURE) { + /* Allocate persistent blr string from request's pool. */ + + request->req_blr_string = (STR) ALLOCDV(type_str, 980); + } + else { + /* Allocate transient blr string from permanent pool so + as not to unnecessarily bloat the request's pool. */ + + request->req_blr_string = (STR) ALLOCPV(type_str, 980); + } + request->req_blr_string->str_length = 980; + request->req_blr = request->req_blr_string->str_data; + request->req_blr_yellow = + request->req_blr + request->req_blr_string->str_length; + +/* Start transactions takes parameters via a parameter block. + The request blr string is used for that. */ + + if (request->req_type == REQ_START_TRANS) { + GEN_start_transaction(request, node); + return request; + } + + if (client_dialect > SQL_DIALECT_V5) + request->req_flags |= REQ_blr_version5; + else + request->req_flags |= REQ_blr_version4; + GEN_request(request, node); + length = request->req_blr - request->req_blr_string->str_data; + +/* stop here for ddl requests */ + + if (request->req_type == REQ_DDL) + return request; + +/* have the access method compile the request */ + +#ifdef DEV_BUILD + if (DSQL_debug) + gds__print_blr(request->req_blr_string->str_data, 0, 0, 0); +#endif + +/* stop here for execute procedure requests */ + + if (request->req_type == REQ_EXEC_PROCEDURE) + return request; + +/* check for warnings */ + if (tdsql->tsql_status[2] == isc_arg_warning) { + /* save a status vector */ + MOVE_FASTER(tdsql->tsql_status, local_status, + sizeof(STATUS) * ISC_STATUS_LENGTH); + } + + THREAD_EXIT; + status = isc_compile_request(GDS_VAL(tdsql->tsql_status), + reinterpret_cast < + void + **>(GDS_REF + (request->req_dbb->dbb_database_handle)), + reinterpret_cast < + void **>(GDS_REF(request->req_handle)), + length, + reinterpret_cast < + char *>(request->req_blr_string->str_data)); + THREAD_ENTER; + +/* restore warnings (if there are any) */ + if (local_status[2] == isc_arg_warning) { + int indx, len, warning; + + /* find end of a status vector */ + PARSE_STATUS(tdsql->tsql_status, indx, warning); + if (indx) + --indx; + + /* calculate length of saved warnings */ + PARSE_STATUS(local_status, len, warning); + len -= 2; + + if ((len + indx - 1) < ISC_STATUS_LENGTH) + MOVE_FASTER(&local_status[2], &tdsql->tsql_status[indx], + sizeof(STATUS) * len); + } + + ALLD_release(reinterpret_cast(request->req_blr_string)); + request->req_blr_string = NULL; + + if (status) + punt(); + + return request; +} + + +static void punt(void) +{ +/************************************** + * + * p u n t + * + ************************************** + * + * Functional description + * Report a signification error. + * + **************************************/ + struct tsql *tdsql; + + tdsql = GET_THREAD_DATA; + + LONGJMP(*tdsql->tsql_setjmp, (int) tdsql->tsql_status[1]); +} + + +static SCHAR *put_item( + SCHAR item, + USHORT length, + SCHAR * string, SCHAR * ptr, SCHAR * end) +{ +/************************************** + * + * p u t _ i t e m + * + ************************************** + * + * Functional description + * Put information item in output buffer if there is room, and + * return an updated pointer. If there isn't room for the item, + * indicate truncation and return NULL. + * + **************************************/ + + if (ptr + length + 3 >= end) { + *ptr = gds_info_truncated; + return NULL; + } + + *ptr++ = item; + STUFF_INFO_WORD(ptr, length); + + if (length) + do + *ptr++ = *string++; + while (--length); + + return ptr; +} + + +static void release_request( REQ request, USHORT top_level) +{ +/************************************** + * + * r e l e a s e _ r e q u e s t + * + ************************************** + * + * Functional description + * Release a dynamic request. + * + **************************************/ + REQ *ptr, child, parent; + STATUS status_vector[ISC_STATUS_LENGTH]; + PLB save_default; + struct tsql *tdsql; + + tdsql = GET_THREAD_DATA; + +/* If request is parent, orphan the children and release a portion + of their requests */ + + for (child = request->req_offspring; child; child = child->req_sibling) { + child->req_flags |= REQ_orphan; + child->req_parent = NULL; + save_default = tdsql->tsql_default; + tdsql->tsql_default = child->req_pool; + release_request(child, FALSE); + tdsql->tsql_default = save_default; + } + +/* For top level requests that are linked to a parent, unlink it */ + + if (top_level && (parent = request->req_parent) != NULL) + for (ptr = &parent->req_offspring; *ptr; ptr = &(*ptr)->req_sibling) + if (*ptr == request) { + *ptr = request->req_sibling; + break; + } + +/* If the request had an open cursor, close it */ + + if (request->req_open_cursor) + close_cursor(request); + +/* If request is named, clear it from the hash table */ + + if (request->req_name) { + HSHD_remove(request->req_name); + request->req_name = NULL; + } + + if (request->req_cursor) { + HSHD_remove(request->req_cursor); + request->req_cursor = NULL; + } + + if (request->req_blr_string) { + ALLD_release(reinterpret_cast(request->req_blr_string)); + request->req_blr_string = NULL; + } + +/* If a request has been compiled, release it now */ + + if (request->req_handle) { + THREAD_EXIT; + isc_release_request(status_vector, + reinterpret_cast < + void **>(GDS_REF(request->req_handle))); + THREAD_ENTER; + } + +/* Only release the entire request for top level requests */ + + if (top_level) + ALLD_rlpool(request->req_pool); +} + + +static STATUS return_success(void) +{ +/************************************** + * + * r e t u r n _ s u c c e s s + * + ************************************** + * + * Functional description + * Set up status vector to reflect successful execution. + * + **************************************/ + TSQL tdsql; + STATUS *p; + + tdsql = GET_THREAD_DATA; + + p = tdsql->tsql_status; + *p++ = gds_arg_gds; + *p++ = SUCCESS; +/* do not overwrite warnings */ + if (*p != isc_arg_warning) + *p = gds_arg_end; + + RESTORE_THREAD_DATA; + + return SUCCESS; +} + + +static SCHAR *var_info( + MSG message, + SCHAR * items, + SCHAR * end_describe, + SCHAR * info, SCHAR * end, USHORT first_index) +{ +/************************************** + * + * v a r _ i n f o + * + ************************************** + * + * Functional description + * Provide information on an internal message. + * + **************************************/ + PAR par; + SCHAR item, *describe, *buffer, buf[128]; + USHORT length; + SLONG sql_type, sql_sub_type, sql_scale, sql_len; + + if (!message || !message->msg_index) + return info; + + for (par = message->msg_par_ordered; par; par = par->par_ordered) + if (par->par_index && par->par_index >= first_index) { + sql_len = par->par_desc.dsc_length; + sql_sub_type = 0; + sql_scale = 0; + switch (par->par_desc.dsc_dtype) { + case dtype_real: + sql_type = SQL_FLOAT; + break; + case dtype_array: + sql_type = SQL_ARRAY; + break; + + case dtype_timestamp: + sql_type = SQL_TIMESTAMP; + break; + case dtype_sql_date: + sql_type = SQL_TYPE_DATE; + break; + case dtype_sql_time: + sql_type = SQL_TYPE_TIME; + break; + + case dtype_double: + sql_type = SQL_DOUBLE; + sql_scale = par->par_desc.dsc_scale; + break; + + case dtype_text: + sql_type = SQL_TEXT; + sql_sub_type = par->par_desc.dsc_sub_type; + break; + + case dtype_blob: + sql_type = SQL_BLOB; + sql_sub_type = par->par_desc.dsc_sub_type; + break; + + case dtype_varying: + sql_type = SQL_VARYING; + sql_len -= sizeof(USHORT); + sql_sub_type = par->par_desc.dsc_sub_type; + break; + + case dtype_short: + case dtype_long: + case dtype_int64: + if (par->par_desc.dsc_dtype == dtype_short) + sql_type = SQL_SHORT; + else if (par->par_desc.dsc_dtype == dtype_long) + sql_type = SQL_LONG; + else + sql_type = SQL_INT64; + sql_scale = par->par_desc.dsc_scale; + if (par->par_desc.dsc_sub_type) + sql_sub_type = par->par_desc.dsc_sub_type; + break; + + case dtype_quad: + sql_type = SQL_QUAD; + sql_scale = par->par_desc.dsc_scale; + break; + + default: + ERRD_post(gds_sqlerr, gds_arg_number, (SLONG) - 804, + gds_arg_gds, gds_dsql_datatype_err, 0); + } + + if (sql_type && (par->par_desc.dsc_flags & DSC_nullable)) + sql_type++; + + for (describe = items; describe < end_describe;) { + buffer = buf; + switch (item = *describe++) { + case gds_info_sql_sqlda_seq: + length = convert((SLONG) par->par_index, buffer); + break; + + case gds_info_sql_message_seq: + length = 0; + break; + + case gds_info_sql_type: + length = convert((SLONG) sql_type, buffer); + break; + + case gds_info_sql_sub_type: + length = convert((SLONG) sql_sub_type, buffer); + break; + + case gds_info_sql_scale: + length = convert((SLONG) sql_scale, buffer); + break; + + case gds_info_sql_length: + length = convert((SLONG) sql_len, buffer); + break; + + case gds_info_sql_null_ind: + length = convert((SLONG) (sql_type & 1), buffer); + break; + + case gds_info_sql_field: + if (buffer = par->par_name) + length = strlen(buffer); + else + length = 0; + break; + + case gds_info_sql_relation: + if (buffer = par->par_rel_name) + length = strlen(buffer); + else + length = 0; + break; + + case gds_info_sql_owner: + if (buffer = par->par_owner_name) + length = strlen(buffer); + else + length = 0; + break; + + case gds_info_sql_alias: + if (buffer = par->par_alias) + length = strlen(buffer); + else + length = 0; + break; + + default: + buffer[0] = item; + item = gds_info_error; + length = 1 + convert((SLONG) gds_infunk, buffer + 1); + break; + } + + if (!(info = put_item(item, length, buffer, info, end))) + return info; + } + + if (info + 1 >= end) { + *info = gds_info_truncated; + return NULL; + } + *info++ = gds_info_sql_describe_end; + } + + return info; +} + + +} // extern "C" diff --git a/src/dsql/dsql.def b/src/dsql/dsql.def new file mode 100644 index 0000000000..64b12b42e7 --- /dev/null +++ b/src/dsql/dsql.def @@ -0,0 +1,57 @@ +; 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): ______________________________________. +;------------------------------------------------------------ +; DSQL DLL MODULE DEFINITION FILE +;------------------------------------------------------------ + +LIBRARY DSQL + +DESCRIPTION 'DSQL INTERBASE MODULE' +CODE MOVEABLE +DATA MOVEABLE +SEGMENTS + _TEXT DISCARDABLE + ALLD_TEXT DISCARDABLE + DDL_TEXT DISCARDABLE + DSQL_TEXT DISCARDABLE + DSQLWEP_TEXT PRELOAD + ERRD_TEXT DISCARDABLE + GEN_TEXT DISCARDABLE + HSH_TEXT DISCARDABLE + MAKE_TEXT DISCARDABLE + METD_TEXT DISCARDABLE + MOVD_TEXT DISCARDABLE + PARSE_TEXT DISCARDABLE + PASS1_TEXT DISCARDABLE + CVT_TEXT DISCARDABLE + DLLSHELL_TEXT PRELOAD + +EXPORTS + +; ../DSQL/DSQL.C + _dsql8_sql_info @2 + _dsql8_set_cursor @3 + _dsql8_prepare @4 + _dsql8_free_statement @5 + _dsql8_fetch @6 + _dsql8_execute_immediate @7 + _dsql8_execute @8 + _dsql8_allocate_statement @9 + _dsql8_insert @10 + +;----------------------------------------------- + WEP @1 RESIDENTNAME diff --git a/src/dsql/dsql.h b/src/dsql/dsql.h new file mode 100644 index 0000000000..5a90f7cb8f --- /dev/null +++ b/src/dsql/dsql.h @@ -0,0 +1,634 @@ +/* + * PROGRAM: Dynamic SQL runtime support + * MODULE: dsql.h + * DESCRIPTION: General Definitions for V4 DSQL module + * + * + * 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): ______________________________________. + */ + +#ifndef _DSQL_DSQL_H_ +#define _DSQL_DSQL_H_ + +#include "../jrd/ibsetjmp.h" +#include "../jrd/common.h" + +/* Dynamic SQL Error Status Block */ + +typedef struct err { + STATUS *dsql_status; + STATUS *dsql_user_status; + JMP_BUF dsql_env; /* Error return environment */ +} *ERR; + +/* macros for block allocation */ + +#define ALL_release(blk) ALLD_release (blk) +#define ALLOCD(type) ALLD_alloc (tdsql->tsql_default, type, 0) +#define ALLOCDV(type,repeat) ALLD_alloc (tdsql->tsql_default, type, repeat) +#define ALLOCP(type) ALLD_alloc (DSQL_permanent_pool, type, 0) +#define ALLOCPV(type,repeat) ALLD_alloc (DSQL_permanent_pool, type, repeat) +#define ALLOC(type,pool) ALLD_alloc (pool, type, 0) +#define ALLOCV(type,pool,repeat) ALLD_alloc (pool, type, repeat) + +/* this table is used in data allocation to determine + whether a block has a variable length tail */ + +#define BLKDEF(type, root, tail) type, + +ENUM blk_t { + type_MIN = 0, +#include "../dsql/blk.h" +type_MAX}; + +#undef BLKDEF + +/* generic block used as header to all allocated structures */ + +#ifndef INCLUDE_FB_BLK +#include "../include/fb_blk.h" +#endif + +/* aggregate blocks to store vectors of objects */ + +typedef struct vec { + struct blk vec_header; + ULONG vec_count; + struct blk *vec_object[1]; +} *VEC; + +typedef struct vcl { + struct blk vcl_header; + ULONG vcl_count; + SLONG vcl_long[1]; +} *VCL; + +/* generic structure used to store strings */ + +typedef struct str +{ + struct blk str_header; + TEXT* str_charset; /* ASCIIZ Character set identifier for string */ + USHORT str_flags; + USHORT str_length; /* length of string in BYTES */ + UCHAR str_data[2]; /* one for ALLOC and one for the NULL */ +} *STR; + +/* values used in str_flags */ + +#define STR_delimited_id 0x1L + +/* macros and block used to implement a generic stack mechanism */ + +#define LLS_PUSH(object,stack) ALLD_push ((BLK) object, stack) +#define LLS_POP(stack) ALLD_pop (stack) + +typedef struct lls { + struct blk lls_header; + struct blk *lls_object; + struct lls *lls_next; +} *LLS; + + +/* blocks used internally to the data allocator */ + +/* free block used to point to an unused block */ + +typedef struct frb { + struct blk frb_header; + struct frb *frb_next; /* Next free block in pool */ +} *FRB; + +/* Pool block used to store a set of blocks to be treated as an aggregate */ + +typedef struct plb { + struct blk plb_header; + USHORT plb_pool_id; /* pool id */ + struct frb *plb_free; /* first free block */ + struct hnk *plb_hunks; /* first hunk block */ + struct lls *plb_lls; /* available linked list stack nodes */ + SLONG *plb_blk_type_count; /* array to keep track of block types */ +} *PLB; + +/* Hunk blocks, used to allow dynamic expansion of an allocated pool */ + +typedef struct hnk { + struct blk hnk_header; + UCHAR *hnk_address; /* start of memory hunk */ + int hnk_length; /* length of memory hunk */ + struct hnk *hnk_next; /* next memory hunk in structure */ +} *HNK; + + + +/*====================================================================== + remaining node definitions for local processing +*/ + +/* Include definition of descriptor */ + +#include "../jrd/dsc.h" + +/* internal DSQL requests */ + + +#if 0 + +#define irq_relation 0 /* lookup a relation */ +#define irq_fields 1 /* lookup a relation's fields */ +#define irq_dimensions 2 /* lookup a field's dimensions */ +#define irq_primary_key 3 /* lookup a primary key */ +#define irq_view 4 /* lookup a view's base relations */ +#define irq_function 5 /* lookup a user defined function */ +#define irq_func_return 6 /* lookup a function's return argument */ +#define irq_procedure 7 /* lookup a stored procedure */ +#define irq_parameters 8 /* lookup a procedure's parameters */ +#define irq_collation 9 /* lookup a collation name */ +#define irq_charset 10 /* lookup a character set name */ +#define irq_trigger 11 /* lookup a trigger */ +#define irq_domain 12 /* lookup a domain */ +#define irq_type 13 /* lookup a symbolic name in RDB$TYPES */ +#define irq_col_default 14 /* lookup default for a column */ +#define irq_domain_2 15 /* lookup a domain */ + +#define irq_MAX 16 + +#else + +enum irq_type_t { + irq_relation = 0, + irq_fields = 1, + irq_dimensions = 2, + irq_primary_key = 3, + irq_view = 4, + irq_function = 5, + irq_func_return = 6, + irq_procedure = 7, + irq_parameters = 8, + irq_collation = 9, + irq_charset = 10, + irq_trigger = 11, + irq_domain = 12, + irq_type = 13, + irq_col_default = 14, + irq_domain_2 = 15, + + irq_MAX = 16 +}; + +#endif + + +/* blocks used to cache metadata */ + +typedef struct dbb { /* Database Block */ + struct blk dbb_header; + struct dbb *dbb_next; + struct dsql_rel *dbb_relations; /* known relations in database */ + struct prc *dbb_procedures; /* known procedures in database */ + struct udf *dbb_functions; /* known functions in database */ + struct plb *dbb_pool; + SLONG *dbb_database_handle; + SLONG *dbb_requests[irq_MAX]; + struct str *dbb_dfl_charset; + USHORT dbb_base_level; /* indicates the version of the engine code itself */ + USHORT dbb_flags; + USHORT dbb_db_SQL_dialect; + SSHORT dbb_att_charset; /* characterset at time of attachment */ +} *DBB; + +/* values used in dbb_flags */ + +#define DBB_no_arrays 0x1 +#define DBB_v3 0x2 +#define DBB_no_charset 0x4 +#define DBB_read_only 0x8 + +typedef struct dsql_rel /* Relation block */ +{ + struct blk rel_header; + struct dsql_rel* rel_next; /* Next relation in database */ + struct sym* rel_symbol; /* Hash symbol for relation */ + struct fld* rel_fields; /* Field block */ + struct dsql_rel* rel_base_relation; /* base relation for an updatable view */ + TEXT* rel_name; /* Name of relation */ + TEXT* rel_owner; /* Owner of relation */ + USHORT rel_id; /* Relation id */ + USHORT rel_dbkey_length; + USHORT rel_flags; + TEXT rel_data[3]; +} *DSQL_REL; + +/* rel_flags bits */ + +#define REL_new_relation 1 /* relation is newly defined, not committed yet */ +#define REL_dropped 2 /* relation has been dropped */ + +typedef struct fld /* Field block */ +{ + struct blk fld_header; + struct fld* fld_next; /* Next field in relation */ + struct dsql_rel* fld_relation; /* Parent relation */ + struct prc* fld_procedure; /* Parent procedure */ + struct nod* fld_ranges; /* ranges for multi dimension array */ + struct nod* fld_character_set; /* null means not specified */ + struct nod* fld_sub_type_name; /* Subtype name for later resolution */ + USHORT fld_flags; + USHORT fld_id; /* Field in in database */ + USHORT fld_dtype; /* Data type of field */ + FLD_LENGTH fld_length; /* Length of field */ + USHORT fld_element_dtype; /* Data type of array element */ + USHORT fld_element_length; /* Length of array element */ + SSHORT fld_scale; /* Scale factor of field */ + SSHORT fld_sub_type; /* Subtype for text & blob fields */ + USHORT fld_precision; /* Precision for exact numeric types */ + USHORT fld_character_length; /* length of field in characters */ + USHORT fld_seg_length; /* Segment length for blobs */ + SSHORT fld_dimensions; /* Non-zero means array */ + SSHORT fld_character_set_id; /* ID of field's character set */ + SSHORT fld_collation_id; /* ID of field's collation */ + SSHORT fld_ttype; /* ID of field's language_driver */ + TEXT fld_name[2]; +} *FLD; + +/* values used in fld_flags */ + +#define FLD_computed 1 +#define FLD_drop 2 +#define FLD_dbkey 4 +#define FLD_national 8 /* field uses NATIONAL character set */ +#define FLD_nullable 16 + +#define MAX_ARRAY_DIMENSIONS 16 /* max array dimensions */ + +/* database/log/cache file block */ + +typedef struct fil { + struct blk fil_header; + SLONG fil_length; /* File length in pages */ + SLONG fil_start; /* Starting page */ + struct str *fil_name; /* File name */ + struct fil *fil_next; /* next file */ + SSHORT fil_shadow_number; /* shadow number if part of shadow */ + SSHORT fil_manual; /* flag to indicate manual shadow */ + SSHORT fil_partitions; /* number of log file partitions */ + USHORT fil_flags; +} *FIL; + +/* Stored Procedure block */ + +typedef struct prc { /* Relation block */ + struct blk prc_header; + struct prc *prc_next; /* Next relation in database */ + struct sym *prc_symbol; /* Hash symbol for procedure */ + struct fld *prc_inputs; /* Input parameters */ + struct fld *prc_outputs; /* Output parameters */ + TEXT *prc_name; /* Name of procedure */ + TEXT *prc_owner; /* Owner of procedure */ + SSHORT prc_in_count; + SSHORT prc_out_count; + USHORT prc_id; /* Procedure id */ + USHORT prc_flags; + TEXT prc_data[3]; +} *PRC; + +/* prc_flags bits */ + +#define PRC_new_procedure 1 /* procedure is newly defined, not committed yet */ +#define PRC_dropped 2 /* procedure has been dropped */ + +/* User defined function block */ + +typedef struct udf { + struct blk udf_header; + struct udf *udf_next; + struct sym *udf_symbol; /* Hash symbol for udf */ + USHORT udf_dtype; + SSHORT udf_scale; + SSHORT udf_sub_type; + USHORT udf_length; + SSHORT udf_character_set_id; + USHORT udf_character_length; + TEXT udf_name[2]; +} *UDF; + +/* these values are multiplied by -1 to indicate that server frees them + on return from the udf */ +typedef ENUM { + FUN_value, + FUN_reference, + FUN_descriptor, FUN_blob_struct, FUN_scalar_array} FUN_T; + +/* Variables - input, output & local */ + +typedef struct var { /* Variable block */ + struct blk var_header; + struct fld *var_field; /* Field on which variable is based */ + USHORT var_flags; + USHORT var_msg_number; /* Message number containing variable */ + USHORT var_msg_item; /* Item number in message */ + USHORT var_variable_number; /* Local variable number */ + TEXT var_name[2]; +} *VAR; + +/* values used in var_flags */ + +#define VAR_input 1 +#define VAR_output 2 +#define VAR_local 4 + + +/* Symbolic names for international text types */ +/* (either collation or character set name) */ + +typedef struct intlsym { /* International symbol */ + struct blk intlsym_header; + struct sym *intlsym_symbol; /* Hash symbol for intlsym */ + USHORT intlsym_type; /* what type of name */ + USHORT intlsym_flags; + SSHORT intlsym_ttype; /* id of implementation */ + SSHORT intlsym_charset_id; + SSHORT intlsym_collate_id; + USHORT intlsym_bytes_per_char; + TEXT intlsym_name[2]; +} *INTLSYM; + +/* values used in intlsym_type */ + +#define INTLSYM_collation 1 +#define INTLSYM_charset 2 + +/* values used in intlsym_flags */ + + + +/* Request information */ + +typedef enum { + REQ_SELECT, REQ_SELECT_UPD, REQ_INSERT, REQ_DELETE, REQ_UPDATE, + REQ_UPDATE_CURSOR, REQ_DELETE_CURSOR, + REQ_COMMIT, REQ_ROLLBACK, REQ_DDL, REQ_EMBED_SELECT, + REQ_START_TRANS, REQ_GET_SEGMENT, REQ_PUT_SEGMENT, REQ_EXEC_PROCEDURE, + REQ_COMMIT_RETAIN, REQ_SET_GENERATOR +} REQ_TYPE; + +typedef struct req { + struct blk req_header; + struct req *req_parent; /* Source request, if cursor update */ + struct req *req_sibling; /* Next sibling request, if cursor update */ + struct req *req_offspring; /* Cursor update requests */ + struct plb *req_pool; + struct lls *req_context; + struct sym *req_name; /* Name of request */ + struct sym *req_cursor; /* Cursor symbol. if any */ + struct dbb *req_dbb; /* Database handle */ + int *req_trans; /* Database transaction handle */ + struct opn *req_open_cursor; + struct nod *req_ddl_node; /* Store metadata request */ + struct blb *req_blob; /* Blob info for blob requests */ + int *req_handle; /* OSRI request handle */ + struct str *req_blr_string; /* String block during BLR generation */ + struct msg *req_send; /* Message to be sent to start request */ + struct msg *req_receive; /* Per record message to be received */ + struct msg *req_async; /* Message for sending scrolling information */ + struct par *req_eof; /* End of file parameter */ + struct par *req_dbkey; /* Database key for current of */ + struct par *req_rec_version; /* Record Version for current of */ + struct par *req_parent_rec_version; /* parent record version */ + struct par *req_parent_dbkey; /* Parent database key for current of */ + struct dsql_rel *req_relation; /* relation created by this request (for DDL) */ + struct prc *req_procedure; /* procedure created by this request (for DDL) */ + struct ctx *req_outer_agg_context; /* agg context for outer ref */ + BLOB_PTR *req_blr; /* Running blr address */ + BLOB_PTR *req_blr_yellow; /* Threshold for upping blr buffer size */ + ULONG req_inserts; /* records processed in request */ + ULONG req_deletes; + ULONG req_updates; + ULONG req_selects; + REQ_TYPE req_type; /* Type of request */ + USHORT req_base_offset; /* place to go back and stuff in blr length */ + USHORT req_context_number; /* Next available context number */ + USHORT req_scope_level; /* Scope level for parsing aliases in subqueries */ + USHORT req_message_number; /* Next available message number */ + USHORT req_loop_number; /* Next available loop number */ + SSHORT req_inhibit_map; /* Map inhibit count */ + USHORT req_in_select_list; /* now processing "select list" */ + USHORT req_in_where_clause; /* processing "where clause" */ + USHORT req_in_having_clause; /* processing "having clause" */ + USHORT req_in_order_by_clause; /* processing "order by clause" */ + USHORT req_error_handlers; /* count of active error handlers */ + USHORT req_flags; /* generic flag */ + USHORT req_client_dialect; /* dialect passed into the API call */ +} *REQ; + +/* values used in req_flags */ + +#define REQ_cursor_open 1 +#define REQ_save_metadata 2 +#define REQ_prepared 4 +#define REQ_embedded_sql_cursor 8 +#define REQ_procedure 16 +#define REQ_trigger 32 +#define REQ_orphan 64 +#define REQ_enforce_scope 128 +#define REQ_no_batch 256 +#define REQ_backwards 512 +#define REQ_blr_version4 1024 +#define REQ_blr_version5 2048 + +/* Blob */ + +typedef struct blb { + struct blk blb_header; + struct nod *blb_field; /* Related blob field */ + struct par *blb_blob_id; /* Parameter to hold blob id */ + struct par *blb_segment; /* Parameter for segments */ + struct nod *blb_from; + struct nod *blb_to; + struct msg *blb_open_in_msg; /* Input message to open cursor */ + struct msg *blb_open_out_msg; /* Output message from open cursor */ + struct msg *blb_segment_msg; /* Segment message */ +} *BLB; + +/* List of open cursors */ + +typedef struct opn { + struct blk opn_header; + struct opn *opn_next; /* Next open cursor */ + struct req *opn_request; /* Request owning the cursor */ + SLONG *opn_transaction; /* Transaction executing request */ +} *OPN; + +/* Transaction block */ + +typedef struct tra { + struct blk tra_header; + struct tra *tra_next; /* Next open transaction */ +} *TRA; + + +/* Context block used to create an instance of a relation reference */ + +typedef struct ctx { + struct blk ctx_header; + struct req *ctx_request; /* Parent request */ + struct dsql_rel *ctx_relation; /* Relation for context */ + struct prc *ctx_procedure; /* Procedure for context */ + struct nod *ctx_proc_inputs; /* Procedure input parameters */ + struct map *ctx_map; /* Map for aggregates */ + struct nod *ctx_rse; /* Sub-rse for aggregates */ + struct ctx *ctx_parent; /* Parent context for aggregates */ + TEXT *ctx_alias; /* Context alias */ + USHORT ctx_context; /* Context id */ + USHORT ctx_scope_level; /* Subquery level within this request */ + USHORT ctx_flags; /* Various flag values */ +} *CTX; + +/* Flag values for ctx_flags */ + +#define CTX_outer_join (1<<0) /* reference is part of an outer join */ + +/* Aggregate/union map block to map virtual fields to their base */ + +typedef struct map { + struct blk map_header; + struct map *map_next; /* Next map in item */ + struct nod *map_node; /* Value for map item */ + USHORT map_position; /* Position in map */ +} *MAP; + + +/* Message block used in communicating with a running request */ + +#ifndef GUI_TOOLS +typedef struct msg { + struct blk msg_header; + struct par *msg_parameters; /* Parameter list */ + struct par *msg_par_ordered; /* Ordered parameter list */ + UCHAR *msg_buffer; /* Message buffer */ + USHORT msg_number; /* Message number */ + USHORT msg_length; /* Message length */ + USHORT msg_parameter; /* Next parameter number */ + USHORT msg_index; /* Next index into SQLDA */ +} *MSG; +#endif + +/* Parameter block used to describe a parameter of a message */ + +typedef struct par { + struct blk par_header; + struct msg *par_message; /* Parent message */ + struct par *par_next; /* Next parameter in linked list */ + struct par *par_ordered; /* Next parameter in order of index */ + struct par *par_null; /* Null parameter, if used */ + struct nod *par_node; /* Associated value node, if any */ + struct ctx *par_dbkey_ctx; /* Context of internally requested dbkey */ + struct ctx *par_rec_version_ctx; /* Context of internally requested record version */ + TEXT *par_name; /* Parameter name, if any */ + TEXT *par_rel_name; /* Relation name, if any */ + TEXT *par_owner_name; /* Owner name, if any */ + TEXT *par_alias; /* Alias, if any */ + DSC par_desc; /* Field data type */ + DSC par_user_desc; /* SQLDA data type */ + USHORT par_parameter; /* BLR parameter number */ + USHORT par_index; /* Index into SQLDA, if appropriate */ +} *PAR; + + +#include "../jrd/thd.h" + +/* DSQL threading declarations */ + +typedef struct tsql +{ + struct thdd tsql_thd_data; + struct plb* tsql_default; + STATUS* tsql_status; + STATUS* tsql_user_status; + jmp_buf* tsql_setjmp; +} *TSQL; + +#ifdef GET_THREAD_DATA +#undef GET_THREAD_DATA +#endif + +#define GET_THREAD_DATA ((TSQL) THD_get_specific()) + + +#ifndef SHLIB_DEFS +#ifdef DSQL_MAIN +PLB DSQL_permanent_pool; +int DSQL_debug; +#else +extern PLB DSQL_permanent_pool; +extern int DSQL_debug; +#endif +#else +extern PLB DSQL_permanent_pool; +extern int DSQL_debug; +#endif + + + +/* macros for error generation */ + +#define BUGCHECK(string) ERRD_bugcheck(string) +#define IBERROR(code, string) ERRD_error(code, string) +#define BLKCHK(blk, type) if (blk->blk_type != (SCHAR) type) BUGCHECK ("expected type") + +/* macro to stuff blr */ +/* this is used in both ddl.cpp and gen.cpp, and is put here for commonality */ + +#define STUFF(byte) ((BLOB_PTR*)request->req_blr < (BLOB_PTR*)request->req_blr_yellow) ?\ + (*request->req_blr++ = (UCHAR)(byte)) : GEN_expand_buffer (request, (UCHAR)(byte)) + + +/* Macros for DEV_BUILD internal consistancy checking */ + +#ifdef DEV_BUILD + +/* Verifies that a pointed to block matches the expected type. + * Useful to find coding errors & memory globbers. + */ +#define DEV_BLKCHK(blk, typ) \ + { \ + if ((blk) && (((BLK) (blk))->blk_type != (typ))) \ + ERRD_assert_msg (assert_blkchk_msg, assert_filename, (ULONG) __LINE__); \ + } + +#define _assert(ex) {if (!(ex)){(void) ERRD_assert_msg (NULL, assert_filename, __LINE__);}} +#define assert(ex) _assert(ex) +#define ASSERT_FAIL ERRD_assert_msg (NULL, assert_filename, __LINE__) + +/* Define the assert_filename as a static variable to save on codespace */ + +#define ASSERT_FILENAME \ + static UCHAR assert_filename[] = __FILE__; \ + static UCHAR assert_blkchk_msg[] = "Unexpected memory block type"; /* NTX: dev */ + +#else /* PROD_BUILD */ + +#define DEV_BLKCHK(blk, typ) +#define _assert(ex) +#define assert(ex) +#define ASSERT_FAIL +#define ASSERT_FILENAME + +#endif /* DEV_BUILD */ + +#endif /* _DSQL_DSQL_H_ */ diff --git a/src/dsql/dsql_proto.h b/src/dsql/dsql_proto.h new file mode 100644 index 0000000000..04def92b89 --- /dev/null +++ b/src/dsql/dsql_proto.h @@ -0,0 +1,68 @@ +/* + * PROGRAM: Dynamic SQL runtime support + * MODULE: dsql_proto.h + * DESCRIPTION: Prototype Header file for dsql.c + * + * 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): ______________________________________. + */ + +#ifndef _DSQL_DSQL_PROTO_H_ +#define _DSQL_DSQL_PROTO_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +extern STATUS DLL_EXPORT dsql8_allocate_statement( STATUS*, + int**, + struct req**); +extern STATUS DLL_EXPORT dsql8_execute(STATUS *, void **, struct req**, + USHORT, UCHAR *, USHORT, USHORT, + UCHAR *, USHORT, UCHAR *, USHORT, + USHORT, UCHAR *); +extern STATUS DLL_EXPORT dsql8_execute_immediate(STATUS *, int **, int **, + USHORT, TEXT *, USHORT, + USHORT, UCHAR *, USHORT, + USHORT, UCHAR *, USHORT, + UCHAR *, USHORT, USHORT, + UCHAR *); +#ifdef SCROLLABLE_CURSORS +extern STATUS DLL_EXPORT dsql8_fetch(STATUS *, struct req **, USHORT, UCHAR *, + USHORT, USHORT, UCHAR *, USHORT, SLONG); +#else +extern STATUS DLL_EXPORT dsql8_fetch(STATUS *, struct req **, USHORT, UCHAR *, + USHORT, USHORT, UCHAR *); +#endif /* SCROLLABLE_CURSORS */ +extern STATUS DLL_EXPORT dsql8_free_statement(STATUS *, struct req **, + USHORT); +extern STATUS DLL_EXPORT dsql8_insert(STATUS *, struct req **, USHORT, + UCHAR *, USHORT, USHORT, UCHAR *); +extern STATUS DLL_EXPORT dsql8_prepare(STATUS *, void **, struct req **, + USHORT, TEXT *, USHORT, USHORT, + UCHAR *, USHORT, UCHAR *); +extern STATUS DLL_EXPORT dsql8_set_cursor(STATUS *, struct req **, TEXT *, + USHORT); +extern STATUS DLL_EXPORT dsql8_sql_info(STATUS *, struct req **, USHORT, + SCHAR *, USHORT, SCHAR *); +extern void DSQL_pretty(struct nod *, int); + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* _DSQL_DSQL_PROTO_H_ */ diff --git a/src/dsql/errd.cpp b/src/dsql/errd.cpp new file mode 100644 index 0000000000..54330d451d --- /dev/null +++ b/src/dsql/errd.cpp @@ -0,0 +1,327 @@ +/* + * PROGRAM: Dynamic SQL runtime support + * MODULE: err.c + * DESCRIPTION: Error handlers + * + * The contents of this file are subject to the Interbase Public + * License Version 1.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy + * of the License at http://www.Inprise.com/IPL.html + * + * Software distributed under the License is distributed on an + * "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either express + * or implied. See the License for the specific language governing + * rights and limitations under the License. + * + * The Original Code was created by Inprise Corporation + * and its predecessors. Portions created by Inprise Corporation are + * Copyright (C) Inprise Corporation. + * + * All Rights Reserved. + * Contributor(s): ______________________________________. + */ + +#include "../jrd/ib_stdio.h" +#include +#include "../jrd/common.h" +#include + +#include "../dsql/dsql.h" +#include "../dsql/sqlda.h" +#include "../jrd/codes.h" +#include "../jrd/iberr.h" +#include "../dsql/errd_proto.h" +#include "../dsql/utld_proto.h" +#include "../jrd/gds_proto.h" +#include "../jrd/thd_proto.h" + + +extern "C" { + + +ASSERT_FILENAME /* Define things assert() needs */ +#ifdef DEV_BUILD +void ERRD_assert_msg( CONST UCHAR * msg, CONST UCHAR * file, ULONG lineno) +{ +/************************************** + * + * E R R D _ a s s e r t _ m s g + * + ************************************** + * + * Functional description + * Generate an assertion failure with a message + * + **************************************/ + + char buffer[100]; + + sprintf(buffer, "Assertion failure: %s File: %s Line: %ld\n", /* NTX: dev build */ + (msg ? msg : (UCHAR *) ""), (file ? file : (UCHAR *) ""), lineno); + ERRD_bugcheck(buffer); +} +#endif /* DEV_BUILD */ + + +void ERRD_bugcheck( CONST TEXT * text) +{ +/************************************** + * + * E R R D _ b u g c h e c k + * + ************************************** + * + * Functional description + * Somebody has screwed up. Bugcheck. + * + **************************************/ + TEXT s[128]; + + sprintf(s, "INTERNAL: %s", text); /* TXNN */ + ERRD_error(-1, s); +} + + +void ERRD_error( int code, CONST TEXT * text) +{ +/************************************** + * + * E R R D _ e r r o r + * + ************************************** + * + * Functional description + * This routine should only be used by fatal + * error messages, those that cannot use the + * normal error routines because something + * is very badly wrong. ERRD_post() should + * be used by most error messages, especially + * so that strings will be handled. + * + **************************************/ + TEXT s[256]; + TSQL tdsql; + + tdsql = GET_THREAD_DATA; + + sprintf(s, "** DSQL error: %s **\n", text); + TRACE(s); + + LONGJMP(*tdsql->tsql_setjmp, code); +} + + +BOOLEAN ERRD_post_warning(STATUS status, ...) +{ +/************************************** + * + * E R R D _ p o s t _ w a r n i n g + * + ************************************** + * + * Functional description + * Post a warning to the current status vector. + * + **************************************/ + va_list args; + int type, len; + +#pragma FB_COMPILER_MESSAGE("Warning, using STATUS array to hold pointers to STATUSes!") + + STATUS *status_vector; + int indx = 0, warning_indx = 0; + + VA_START(args, status); + status_vector = ((TSQL) GET_THREAD_DATA)->tsql_status; + + if (status_vector[0] != gds_arg_gds || + (status_vector[0] == gds_arg_gds && status_vector[1] == 0 && + status_vector[2] != gds_arg_warning)) { + /* this is a blank status vector */ + status_vector[0] = gds_arg_gds; + status_vector[1] = 0; + status_vector[2] = gds_arg_end; + indx = 2; + } + else + { + /* find end of a status vector */ + PARSE_STATUS(status_vector, indx, warning_indx); + if (indx) + --indx; + } + +/* stuff the warning */ + if (indx + 3 < ISC_STATUS_LENGTH) { + status_vector[indx++] = gds_arg_warning; + status_vector[indx++] = status; + while ((type = va_arg(args, int)) && (indx + 3 < ISC_STATUS_LENGTH)) + switch (status_vector[indx++] = type) { + case gds_arg_warning: + status_vector[indx++] = (STATUS) va_arg(args, STATUS); + break; + + case gds_arg_string: + { + const char* pszTmp = va_arg(args, char*); + if (strlen(pszTmp) >= MAX_ERRSTR_LEN) { + status_vector[(indx - 1)] = gds_arg_cstring; + status_vector[indx++] = MAX_ERRSTR_LEN; + } + status_vector[indx++] = reinterpret_cast(pszTmp); + } + break; + + case gds_arg_interpreted: + status_vector[indx++] = (STATUS) va_arg(args, TEXT *); + break; + + case gds_arg_cstring: + len = va_arg(args, int); + status_vector[indx++] = + (STATUS) (len >= MAX_ERRSTR_LEN) ? MAX_ERRSTR_LEN : len; + status_vector[indx++] = (STATUS) va_arg(args, TEXT *); + break; + + case gds_arg_number: + status_vector[indx++] = (STATUS) va_arg(args, SLONG); + break; + + case gds_arg_vms: + case gds_arg_unix: + case gds_arg_domain: + case gds_arg_dos: + case gds_arg_mpexl: + case gds_arg_next_mach: + case gds_arg_netware: + case gds_arg_win32: + default: + status_vector[indx++] = (STATUS) va_arg(args, int); + break; + } + status_vector[indx] = gds_arg_end; + return TRUE; + } + else { + /* not enough free space */ + return FALSE; + } +} + + +void ERRD_post( STATUS status, ...) +{ +/************************************** + * + * E R R D _ p o s t + * + ************************************** + * + * Functional description + * Post an error, copying any potentially + * transient data before we do the longjmp. + * + **************************************/ + STATUS *status_vector; + STATUS tmp_status[ISC_STATUS_LENGTH], warning_status[ISC_STATUS_LENGTH]; + int i, tmp_status_len = 0, status_len = 0, err_status_len = 0; + int warning_count = 0, warning_indx = 0; + + status_vector = ((TSQL) GET_THREAD_DATA)->tsql_status; + +/* stuff the status into temp buffer */ + MOVE_CLEAR(tmp_status, sizeof(tmp_status)); + STUFF_STATUS(tmp_status, status); + +/* calculate length of the status */ + PARSE_STATUS(tmp_status, tmp_status_len, warning_indx); + assert(warning_indx == 0); + + if (status_vector[0] != gds_arg_gds || + (status_vector[0] == gds_arg_gds && status_vector[1] == 0 && + status_vector[2] != gds_arg_warning)) { + /* this is a blank status vector */ + status_vector[0] = gds_arg_gds; + status_vector[1] = gds__dsql_error; + status_vector[2] = gds_arg_end; + } + + PARSE_STATUS(status_vector, status_len, warning_indx); + if (status_len) + --status_len; + +/* check for duplicated error code */ + for (i = 0; i < ISC_STATUS_LENGTH; i++) { + if (status_vector[i] == gds_arg_end && i == status_len) + break; /* end of argument list */ + + if (i && i == warning_indx) + break; /* vector has no more errors */ + + if (status_vector[i] == tmp_status[1] && i && + status_vector[i - 1] != gds_arg_warning && + i + tmp_status_len - 2 < ISC_STATUS_LENGTH && + (memcmp(&status_vector[i], &tmp_status[1], + sizeof(STATUS) * (tmp_status_len - 2)) == 0)) { + /* duplicate found */ + ERRD_punt(); + } + } + +/* if the status_vector has only warnings then adjust err_status_len */ + if ((err_status_len = i) == 2 && warning_indx) + err_status_len = 0; + + if (warning_indx) { + /* copy current warning(s) to a temp buffer */ + MOVE_CLEAR(warning_status, sizeof(warning_status)); + MOVE_FASTER(&status_vector[warning_indx], warning_status, + sizeof(STATUS) * (ISC_STATUS_LENGTH - warning_indx)); + PARSE_STATUS(warning_status, warning_count, warning_indx); + } + +/* add the status into a real buffer right in between last error + and first warning */ + + if ((i = err_status_len + tmp_status_len) < ISC_STATUS_LENGTH) { + MOVE_FASTER(tmp_status, &status_vector[err_status_len], + sizeof(STATUS) * tmp_status_len); + /* copy current warning(s) to the status_vector */ + if (warning_count && i + warning_count - 1 < ISC_STATUS_LENGTH) { + MOVE_FASTER(warning_status, &status_vector[i - 1], + sizeof(STATUS) * warning_count); + + } + } + ERRD_punt(); +} + + +void ERRD_punt(void) +{ +/************************************** + * + * E R R D _ p u n t + * + ************************************** + * + * Functional description + * Error stuff has been copied to + * status vector. Now punt. + * + **************************************/ + TSQL tdsql; + + tdsql = GET_THREAD_DATA; + +/* Save any strings in a permanent location */ + + UTLD_save_status_strings(tdsql->tsql_status); + +/* Give up whatever we were doing and return to the user. */ + + LONGJMP(*tdsql->tsql_setjmp, (int) tdsql->tsql_status[1]); +} + + +} // extern "C" diff --git a/src/dsql/errd_proto.h b/src/dsql/errd_proto.h new file mode 100644 index 0000000000..a606294be8 --- /dev/null +++ b/src/dsql/errd_proto.h @@ -0,0 +1,45 @@ +/* + * PROGRAM: Dynamic SQL RUNTIME SUPPORT + * MODULE: errd_proto.h + * DESCRIPTION: Prototype Header file for errd_proto.h + * + * 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): ______________________________________. + */ + +#ifndef DSQL_ERRD_PROTO_H +#define DSQL_ERRD_PROTO_H + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef DEV_BUILD +void ERRD_assert_msg(CONST UCHAR *, CONST UCHAR *, ULONG); +#endif + +void ERRD_bugcheck(CONST TEXT *); +void ERRD_error(int, CONST TEXT *); +void ERRD_post(STATUS, ...); +BOOLEAN ERRD_post_warning(STATUS, ...); +void ERRD_punt(void); + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* DSQL_ERRD_PROTO_H */ diff --git a/src/dsql/gen.cpp b/src/dsql/gen.cpp new file mode 100644 index 0000000000..04b7a56e3b --- /dev/null +++ b/src/dsql/gen.cpp @@ -0,0 +1,2398 @@ +/* + * PROGRAM: Dynamic SQL runtime support + * MODULE: gen.c + * DESCRIPTION: Routines to generate BLR. + * + * 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): ______________________________________. + */ +/* +$Id: gen.cpp,v 1.1.1.1 2001-05-23 13:25:34 tamlin Exp $ +*/ + +#include +#include +#include "../dsql/dsql.h" +#include "../include/jrd/gds.h" +#include "../jrd/align.h" +#include "../dsql/node.h" +#include "../jrd/intl.h" +#include "../dsql/sym.h" +#include "../dsql/alld_proto.h" +#include "../dsql/ddl_proto.h" +#include "../dsql/errd_proto.h" +#include "../dsql/gen_proto.h" +#include "../dsql/make_proto.h" +#include "../dsql/metd_proto.h" +#include "../jrd/thd_proto.h" +#include "../jrd/dsc_proto.h" +#include "../include/iberror.h" + +ASSERT_FILENAME static void gen_aggregate(REQ, NOD); +static void gen_cast(REQ, NOD); +static void gen_constant(REQ, DSC *, BOOLEAN); +static void gen_descriptor(REQ, DSC *, USHORT); +static void gen_error_condition(REQ, NOD); +static void gen_field(REQ, CTX, FLD, NOD); +static void gen_for_select(REQ, NOD); +static void gen_gen_id(REQ, NOD); +static void gen_join_rse(REQ, NOD); +static void gen_map(REQ, MAP); +static void gen_parameter(REQ, PAR); +static void gen_plan(REQ, NOD); +static void gen_relation(REQ, CTX); +static void gen_rse(REQ, NOD); +static void gen_select(REQ, NOD); +static void gen_sort(REQ, NOD); +static void gen_table_lock(REQ, NOD, USHORT); +static void gen_udf(REQ, NOD); +static void gen_union(REQ, NOD); +static void stuff_cstring(REQ, char *); +static void stuff_word(REQ, USHORT); + +static CONST SCHAR db_key_name[] = "DB_KEY"; + +/* STUFF is defined in dsql.h for use in common with ddl.c */ + +#define STUFF_WORD(word) stuff_word (request, (USHORT)(word)) +#define STUFF_CSTRING(cstring) stuff_cstring (request, (char*) (cstring)) + +/* The following are passed as the third argument to gen_constant */ +#define NEGATE_VALUE TRUE +#define USE_VALUE FALSE + + + +UCHAR GEN_expand_buffer( REQ request, UCHAR byte) +{ +/************************************** + * + * G E N _ e x p a n d _ b u f f e r + * + ************************************** + * + * Functional description + * The blr buffer needs to be expanded. + * + **************************************/ + ULONG length; + ULONG copy_length; + PLB pool; + STR new_buffer; + BLOB_PTR *p; /* one huge pointer per line for LIBS */ + BLOB_PTR *q; /* one huge pointer per line for LIBS */ + BLOB_PTR *end; /* one huge pointer per line for LIBS */ + TSQL tdsql; + + tdsql = GET_THREAD_DATA; + + length = request->req_blr_string->str_length + 2048; + pool = (request->req_blr_string->str_header.blk_pool_id == + DSQL_permanent_pool->plb_pool_id) ? + DSQL_permanent_pool : tdsql->tsql_default; + new_buffer = (STR) ALLOCV(type_str, pool, length); + new_buffer->str_length = length; + + p = new_buffer->str_data; + q = request->req_blr_string->str_data; + end = request->req_blr; + copy_length = (ULONG) (end - q); + memcpy(p, q, copy_length); + + ALLD_release(reinterpret_cast(request->req_blr_string)); + request->req_blr_string = new_buffer; + request->req_blr = p + copy_length; + request->req_blr_yellow = new_buffer->str_data + length; + + return (*request->req_blr++ = byte); +} + + +void GEN_expr( REQ request, NOD node) +{ +/************************************** + * + * G E N _ e x p r + * + ************************************** + * + * Functional description + * Generate blr for an arbitrary expression. + * + **************************************/ + UCHAR operator_; + NOD *ptr, *end, ddl_node; + CTX context; + MAP map; + VAR variable; + + switch (node->nod_type) { + case nod_alias: + GEN_expr(request, node->nod_arg[e_alias_value]); + return; + + case nod_aggregate: + gen_aggregate(request, node); + return; + + case nod_constant: + gen_constant(request, &node->nod_desc, USE_VALUE); + return; + + case nod_extract: + STUFF(blr_extract); + STUFF(*(SLONG *) node->nod_arg[e_extract_part]->nod_desc.dsc_address); + GEN_expr(request, node->nod_arg[e_extract_value]); + return; + + case nod_dbkey: + node = node->nod_arg[0]; + context = (CTX) node->nod_arg[e_rel_context]; + STUFF(blr_dbkey); + STUFF(context->ctx_context); + return; + + case nod_rec_version: + node = node->nod_arg[0]; + context = (CTX) node->nod_arg[e_rel_context]; + STUFF(blr_record_version); + STUFF(context->ctx_context); + return; + + case nod_dom_value: + if ((request->req_type != REQ_DDL) || + !(ddl_node = request->req_ddl_node) || + !(ddl_node->nod_type == nod_def_domain || + ddl_node->nod_type == nod_mod_domain)) + ERRD_post(gds_sqlerr, gds_arg_number, (SLONG) - 901, + gds_arg_gds, gds_dsql_domain_err, 0); + STUFF(blr_fid); + STUFF(0); /* Context */ + STUFF_WORD(0); /* Field id */ + return; + + case nod_field: + gen_field(request, + (CTX) node->nod_arg[e_fld_context], + (FLD) node->nod_arg[e_fld_field], + node->nod_arg[e_fld_indices]); + return; + + case nod_user_name: + STUFF(blr_user_name); + return; + + case nod_current_time: + STUFF(blr_current_time); + return; + + case nod_current_timestamp: + STUFF(blr_current_timestamp); + return; + + case nod_current_date: + STUFF(blr_current_date); + return; + + case nod_udf: + gen_udf(request, node); + return; + + case nod_variable: + variable = (VAR) node->nod_arg[e_var_variable]; + if (variable->var_flags & VAR_input) { + STUFF(blr_parameter2); + STUFF(variable->var_msg_number); + STUFF_WORD(variable->var_msg_item); + STUFF_WORD(variable->var_msg_item + 1); + } + else { + STUFF(blr_variable); + STUFF_WORD(variable->var_variable_number); + } + return; + + case nod_join: + gen_join_rse(request, node); + return; + + case nod_map: + map = (MAP) node->nod_arg[e_map_map]; + context = (CTX) node->nod_arg[e_map_context]; + STUFF(blr_fid); + STUFF(context->ctx_context); + STUFF_WORD(map->map_position); + return; + + case nod_parameter: + gen_parameter(request, (PAR) node->nod_arg[e_par_parameter]); + return; + + case nod_relation: + gen_relation(request, (CTX) node->nod_arg[e_rel_context]); + return; + + case nod_rse: + gen_rse(request, node); + return; + + case nod_exists: + STUFF(blr_any); + gen_rse(request, node->nod_arg[0]); + return; + + case nod_singular: + STUFF(blr_unique); + gen_rse(request, node->nod_arg[0]); + return; + + case nod_agg_count: + if (!(request->req_dbb->dbb_flags & DBB_v3)) { + if (node->nod_count) + operator_ = (node->nod_flags & NOD_AGG_DISTINCT) ? + blr_agg_count_distinct : blr_agg_count2; + else + operator_ = blr_agg_count; + } + else + operator_ = blr_agg_count; + break; + + case nod_agg_min: + operator_ = blr_agg_min; + break; + case nod_agg_max: + operator_ = blr_agg_max; + break; + + case nod_agg_average: + if (!(request->req_dbb->dbb_flags & DBB_v3)) + operator_ = (node->nod_flags & NOD_AGG_DISTINCT) ? + blr_agg_average_distinct : blr_agg_average; + else + operator_ = blr_agg_average; + break; + + case nod_agg_total: + if (!(request->req_dbb->dbb_flags & DBB_v3)) + operator_ = (node->nod_flags & NOD_AGG_DISTINCT) ? + blr_agg_total_distinct : blr_agg_total; + else + operator_ = blr_agg_total; + break; + + case nod_agg_average2: + operator_ = (node->nod_flags & NOD_AGG_DISTINCT) ? + blr_agg_average_distinct : blr_agg_average; + break; + + case nod_agg_total2: + operator_ = (node->nod_flags & NOD_AGG_DISTINCT) ? + blr_agg_total_distinct : blr_agg_total; + break; + + case nod_and: + operator_ = blr_and; + break; + case nod_or: + operator_ = blr_or; + break; + case nod_not: + operator_ = blr_not; + break; + case nod_eql_all: + case nod_eql_any: + case nod_eql: + operator_ = blr_eql; + break; + case nod_neq_all: + case nod_neq_any: + case nod_neq: + operator_ = blr_neq; + break; + case nod_gtr_all: + case nod_gtr_any: + case nod_gtr: + operator_ = blr_gtr; + break; + case nod_leq_all: + case nod_leq_any: + case nod_leq: + operator_ = blr_leq; + break; + case nod_geq_all: + case nod_geq_any: + case nod_geq: + operator_ = blr_geq; + break; + case nod_lss_all: + case nod_lss_any: + case nod_lss: + operator_ = blr_lss; + break; + case nod_between: + operator_ = blr_between; + break; + case nod_containing: + operator_ = blr_containing; + break; + case nod_starting: + operator_ = blr_starting; + break; + case nod_missing: + operator_ = blr_missing; + break; + + case nod_like: + operator_ = (node->nod_count == 2) ? blr_like : blr_ansi_like; + break; + + case nod_add: + operator_ = blr_add; + break; + case nod_subtract: + operator_ = blr_subtract; + break; + case nod_multiply: + operator_ = blr_multiply; + break; + + case nod_negate: + { + NOD child = node->nod_arg[0]; + if (child->nod_type == nod_constant && + DTYPE_IS_NUMERIC(child->nod_desc.dsc_dtype)) { + gen_constant(request, &child->nod_desc, NEGATE_VALUE); + return; + } + } + operator_ = blr_negate; + break; + + case nod_divide: + operator_ = blr_divide; + break; + case nod_add2: + operator_ = blr_add; + break; + case nod_subtract2: + operator_ = blr_subtract; + break; + case nod_multiply2: + operator_ = blr_multiply; + break; + case nod_divide2: + operator_ = blr_divide; + break; + case nod_concatenate: + operator_ = blr_concatenate; + break; + case nod_null: + operator_ = blr_null; + break; + case nod_any: + operator_ = blr_any; + break; + case nod_ansi_any: + if (!(request->req_dbb->dbb_flags & DBB_v3)) + operator_ = blr_ansi_any; + else + operator_ = blr_any; + break; + case nod_ansi_all: + operator_ = blr_ansi_all; + break; + case nod_via: + operator_ = blr_via; + break; + + case nod_upcase: + operator_ = blr_upcase; + break; + case nod_cast: + gen_cast(request, node); + return; + case nod_gen_id: + case nod_gen_id2: + gen_gen_id(request, node); + return; + case nod_average: + case nod_count: + case nod_from: + case nod_max: + case nod_min: + case nod_total: + switch (node->nod_type) { + case nod_average: + operator_ = blr_average; + break; + case nod_count: + operator_ = blr_count; +/* count2 + operator_ = node->nod_arg [0]->nod_arg [e_rse_items] ? blr_count2 : blr_count; +*/ + break; + case nod_from: + operator_ = blr_from; + break; + case nod_max: + operator_ = blr_maximum; + break; + case nod_min: + operator_ = blr_minimum; + break; + case nod_total: + operator_ = blr_total; + break; + + default: + break; + } + + STUFF(operator_); + gen_rse(request, node->nod_arg[0]); + if (operator_ != blr_count) + GEN_expr(request, node->nod_arg[0]->nod_arg[e_rse_items]); + return; + + default: + ERRD_post(gds_sqlerr, gds_arg_number, (SLONG) - 901, + gds_arg_gds, gds_dsql_internal_err, + gds_arg_gds, gds_expression_eval_err, + /* expression evaluation not supported */ + 0); + } + + STUFF(operator_); + + for (ptr = node->nod_arg, end = ptr + node->nod_count; ptr < end; ptr++) + GEN_expr(request, *ptr); + +/* Check whether the node we just processed is for a dialect 3 + operation which gives a different result than the corresponding + operation in dialect 1. If it is, and if the client dialect is 2, + issue a warning about the difference. */ + + if (node->nod_type == nod_add2 || + node->nod_type == nod_subtract2 || + node->nod_type == nod_multiply2 || + node->nod_type == nod_divide2 || + node->nod_type == nod_agg_total2 || + node->nod_type == nod_agg_average2) { + DSC desc; + char *s; + char message_buf[8]; + + MAKE_desc(&desc, node); + if ((node->nod_flags & NOD_COMP_DIALECT) && + (request->req_client_dialect == SQL_DIALECT_V6_TRANSITION)) { + switch (node->nod_type) { + case nod_add2: + s = "add"; + break; + case nod_subtract2: + s = "subtract"; + break; + case nod_multiply2: + s = "multiply"; + break; + case nod_divide2: + s = "divide"; + break; + case nod_agg_total2: + s = "sum"; + break; + case nod_agg_average2: + s = "avg"; + break; + default: + sprintf(message_buf, "blr %d", operator_); + s = message_buf; + } + ERRD_post_warning(isc_dsql_dialect_warning_expr, + gds_arg_string, s, gds_arg_end); + } + } + +} + + +void GEN_port( REQ request, MSG message) +{ +/************************************** + * + * G E N _ p o r t + * + ************************************** + * + * Functional description + * Generate a port from a message. Feel free to rearrange the + * order of parameters. + * + **************************************/ + STR buffer; + PAR parameter; + USHORT number, align; + TSQL tdsql; + + tdsql = GET_THREAD_DATA; + + if (request->req_blr_string) { + STUFF(blr_message); + STUFF(message->msg_number); + STUFF_WORD(message->msg_parameter); + } + + for (parameter = message->msg_parameters, number = 0; parameter; + parameter = parameter->par_next) { + parameter->par_parameter = number++; + + /* For older clients - generate an error should they try and + access data types which did not exist in the older dialect */ + if (request->req_client_dialect <= SQL_DIALECT_V5) + switch (parameter->par_desc.dsc_dtype) { +#ifdef SQL_DIALECT_1_NEW_DATATYPES_CONVERT_TO_TEXT + /* An early design of how to handle access of new datatype + fields by older clients determined that conversion of + the new types to TEXT was the proper way. A later design + meeting decided that SQL Dialect 1 should forbid all + access to the newer datatypes. + Should this decision be revisited during v6.0 BETA, + this is the code that converts select & insert + references to the new datatypes to TEXT. + 1999-Mar-17 David Schnepper */ + + case dtype_sql_date: + parameter->par_desc.dsc_dtype = dtype_text; + parameter->par_desc.dsc_scale = 0; + parameter->par_desc.dsc_ttype = ttype_ascii; + parameter->par_desc.dsc_length = + DSC_convert_to_text_length(dtype_sql_date); + break; + case dtype_sql_time: + parameter->par_desc.dsc_dtype = dtype_text; + parameter->par_desc.dsc_scale = 0; + parameter->par_desc.dsc_ttype = ttype_ascii; + parameter->par_desc.dsc_length = + DSC_convert_to_text_length(dtype_sql_time); + break; + case dtype_int64: + if (parameter->par_desc.dsc_scale < 0) + length = 1; /* For decimal point */ + else + length = parameter->par_desc.dsc_scale; + length += DSC_convert_to_text_length(dtype_int64) - 1; + parameter->par_desc.dsc_dtype = dtype_text; + parameter->par_desc.dsc_scale = 0; + parameter->par_desc.dsc_ttype = ttype_ascii; + parameter->par_desc.dsc_length = length; + break; +#else + + /* In V6.0 - older clients, which we distinguish by + their use of SQL DIALECT 0 or 1, are forbidden + from selecting values of new datatypes */ + + case dtype_sql_date: + case dtype_sql_time: + case dtype_int64: + ERRD_post(gds_sqlerr, gds_arg_number, (SLONG) - 804, + gds_arg_gds, gds_dsql_datatype_err, + gds_arg_gds, isc_sql_dialect_datatype_unsupport, + gds_arg_number, request->req_client_dialect, + gds_arg_string, + DSC_dtype_tostring(parameter->par_desc.dsc_dtype), + 0); + break; +#endif /* SQL_DIALECT_1_NEW_DATATYPES_CONVERT_TO_TEXT */ + default: + /* No special action for other data types */ + break; + }; + align = type_alignments[parameter->par_desc.dsc_dtype]; + if (align) + message->msg_length = FB_ALIGN(message->msg_length, align); + parameter->par_desc.dsc_address = + (UCHAR *) (SLONG) message->msg_length; + message->msg_length += parameter->par_desc.dsc_length; + if (request->req_blr_string) + gen_descriptor(request, ¶meter->par_desc, FALSE); + } + +/* Allocate buffer for message */ + + buffer = (STR) ALLOCDV(type_str, message->msg_length + DOUBLE_ALIGN - 1); + message->msg_buffer = + (UCHAR *) FB_ALIGN((U_IPTR) buffer->str_data, DOUBLE_ALIGN); + +/* Relocate parameter descriptors to point direction into message buffer */ + + for (parameter = message->msg_parameters; parameter; + parameter = parameter->par_next) + parameter->par_desc.dsc_address = + message->msg_buffer + (SLONG) parameter->par_desc.dsc_address; +} + + +void GEN_request( REQ request, NOD node) +{ +/************************************** + * + * G E N _ r e q u e s t + * + ************************************** + * + * Functional description + * Generate complete blr for a request. + * + **************************************/ + MSG message; + + if (request->req_type == REQ_DDL) { + DDL_generate(request, node); + return; + } + + if (request->req_flags & REQ_blr_version4) + STUFF(blr_version4); + else + STUFF(blr_version5); + STUFF(blr_begin); + + if (request->req_type == REQ_SELECT || + request->req_type == REQ_SELECT_UPD) gen_select(request, node); + else { + message = request->req_send; + if (!message->msg_parameter) + request->req_send = NULL; + else { + GEN_port(request, message); + if (request->req_type != REQ_EXEC_PROCEDURE) { + STUFF(blr_receive); + STUFF(message->msg_number); + } + } + message = request->req_receive; + if (!message->msg_parameter) + request->req_receive = NULL; + else + GEN_port(request, message); + GEN_statement(request, node); + } + + STUFF(blr_end); + STUFF(blr_eoc); +} + + +void GEN_start_transaction( REQ request, NOD tran_node) +{ +/************************************** + * + * G E N _ s t a r t _ t r a n s a c t i o n + * + ************************************** + * + * Functional description + * Generate tpb for set transaction. Use blr string of request. + * If a value is not specified, default is not STUFF'ed, let the + * engine handle it. + * Do not allow an option to be specified more than once. + * + **************************************/ + NOD *temp, ptr, *end; + SSHORT count; + NOD reserve, node; + SSHORT sw_access, sw_wait, sw_isolation, sw_reserve; + USHORT lock_level; + + count = tran_node->nod_count; + + if (!count) + return; + + sw_access = sw_wait = sw_isolation = sw_reserve = 0; + + node = tran_node->nod_arg[0]; + + if (!node) + return; + +/* find out isolation level - if specified. This is required for + * specifying the correct lock level in reserving clause. */ + + lock_level = gds_tpb_shared; + + if (count = node->nod_count) { + while (count--) { + ptr = node->nod_arg[count]; + + if ((!ptr) || (ptr->nod_type != nod_isolation)) + continue; + + lock_level = + (ptr-> + nod_flags & NOD_CONSISTENCY) ? gds_tpb_protected : + gds_tpb_shared; + } + } + +/* Stuff some version info. */ + + if (count = node->nod_count) + STUFF(gds_tpb_version1); + + while (count--) { + ptr = node->nod_arg[count]; + + if (!ptr) + continue; + + switch (ptr->nod_type) { + case nod_access: + if (sw_access) + ERRD_post(gds_sqlerr, gds_arg_number, (SLONG) - 104, + gds_arg_gds, gds_dsql_dup_option, 0); + + sw_access = 1; + if (ptr->nod_flags & NOD_READ_ONLY) + STUFF(gds_tpb_read); + else + STUFF(gds_tpb_write); + break; + + case nod_wait: + if (sw_wait) + ERRD_post(gds_sqlerr, gds_arg_number, (SLONG) - 104, + gds_arg_gds, gds_dsql_dup_option, 0); + + sw_wait = 1; + if (ptr->nod_flags & NOD_NO_WAIT) + STUFF(gds_tpb_nowait); + else + STUFF(gds_tpb_wait); + break; + + case nod_isolation: + if (sw_isolation) + ERRD_post(gds_sqlerr, gds_arg_number, (SLONG) - 104, + gds_arg_gds, gds_dsql_dup_option, 0); + + sw_isolation = 1; + + if (ptr->nod_flags & NOD_CONCURRENCY) + STUFF(gds_tpb_concurrency); + else if (ptr->nod_flags & NOD_CONSISTENCY) + STUFF(gds_tpb_consistency); + else { + STUFF(gds_tpb_read_committed); + + if ((ptr->nod_count) && (ptr->nod_arg[0]) && + (ptr->nod_arg[0]->nod_type == nod_version)) { + if (ptr->nod_arg[0]->nod_flags & NOD_VERSION) + STUFF(gds_tpb_rec_version); + else + STUFF(gds_tpb_no_rec_version); + } + else + STUFF(gds_tpb_no_rec_version); + } + + break; + + case nod_reserve: + if (sw_reserve) + ERRD_post(gds_sqlerr, gds_arg_number, (SLONG) - 104, + gds_arg_gds, gds_dsql_dup_option, 0); + + sw_reserve = 1; + reserve = ptr->nod_arg[0]; + + if (reserve) + for (temp = reserve->nod_arg, end = temp + reserve->nod_count; + temp < end; temp++) + gen_table_lock(request, *temp, lock_level); + + break; + + default: + ERRD_post(gds_sqlerr, gds_arg_number, (SLONG) - 104, + gds_arg_gds, gds_dsql_tran_err, 0); + } + } +} + + +void GEN_statement( REQ request, NOD node) +{ +/************************************** + * + * G E N _ s t a t e m e n t + * + ************************************** + * + * Functional description + * Generate blr for an arbitrary expression. + * + **************************************/ + NOD temp, *ptr, *end; + CTX context; + MSG message; + STR name; + STR string; + TEXT *p; + ULONG id_length; + + switch (node->nod_type) { + case nod_assign: + STUFF(blr_assignment); + GEN_expr(request, node->nod_arg[0]); + GEN_expr(request, node->nod_arg[1]); + return; + + case nod_block: + STUFF(blr_block); + GEN_statement(request, node->nod_arg[e_blk_action]); + if (node->nod_count > 1) { + temp = node->nod_arg[e_blk_errs]; + for (ptr = temp->nod_arg, end = ptr + temp->nod_count; + ptr < end; ptr++) + GEN_statement(request, *ptr); + } + STUFF(blr_end); + return; + + case nod_erase: + if ((temp = node->nod_arg[e_era_rse]) != NULL) { + STUFF(blr_for); + GEN_expr(request, temp); + } + temp = node->nod_arg[e_era_relation]; + context = (CTX) temp->nod_arg[e_rel_context]; + STUFF(blr_erase); + STUFF(context->ctx_context); + return; + + case nod_erase_current: + STUFF(blr_erase); + context = (CTX) node->nod_arg[e_erc_context]; + STUFF(context->ctx_context); + return; + + case nod_exec_procedure: + if (request->req_type == REQ_EXEC_PROCEDURE) { + if (message = request->req_receive) { + STUFF(blr_begin); + STUFF(blr_send); + STUFF(message->msg_number); + } + } + else + message = NULL; + STUFF(blr_exec_proc); + name = (STR) node->nod_arg[e_exe_procedure]; + STUFF_CSTRING(name->str_data); + if (temp = node->nod_arg[e_exe_inputs]) { + STUFF_WORD(temp->nod_count); + for (ptr = temp->nod_arg, end = ptr + temp->nod_count; + ptr < end; ptr++) + GEN_expr(request, *ptr); + } + else + STUFF_WORD(0); + if (temp = node->nod_arg[e_exe_outputs]) { + STUFF_WORD(temp->nod_count); + for (ptr = temp->nod_arg, end = ptr + temp->nod_count; + ptr < end; ptr++) + GEN_expr(request, *ptr); + } + else + STUFF_WORD(0); + if (message) + STUFF(blr_end); + return; + + case nod_for_select: + gen_for_select(request, node); + return; + + case nod_set_generator: + case nod_set_generator2: + STUFF(blr_set_generator); + string = (STR) node->nod_arg[e_gen_id_name]; + STUFF_CSTRING(string->str_data); + GEN_expr(request, node->nod_arg[e_gen_id_value]); + return; + + case nod_if: + STUFF(blr_if); + GEN_expr(request, node->nod_arg[e_if_condition]); + GEN_statement(request, node->nod_arg[e_if_true]); + if (node->nod_arg[e_if_false]) + GEN_statement(request, node->nod_arg[e_if_false]); + else + STUFF(blr_end); + return; + + case nod_list: + STUFF(blr_begin); + for (ptr = node->nod_arg, end = ptr + node->nod_count; ptr < end; + ptr++) + GEN_statement(request, *ptr); + STUFF(blr_end); + return; + + case nod_modify: + if ((temp = node->nod_arg[e_mod_rse]) != NULL) { + STUFF(blr_for); + GEN_expr(request, temp); + } + STUFF(blr_modify); + temp = node->nod_arg[e_mod_source]; + context = (CTX) temp->nod_arg[e_rel_context]; + STUFF(context->ctx_context); + temp = node->nod_arg[e_mod_update]; + context = (CTX) temp->nod_arg[e_rel_context]; + STUFF(context->ctx_context); + GEN_statement(request, node->nod_arg[e_mod_statement]); + return; + + case nod_modify_current: + STUFF(blr_modify); + context = (CTX) node->nod_arg[e_mdc_context]; + STUFF(context->ctx_context); + temp = node->nod_arg[e_mdc_update]; + context = (CTX) temp->nod_arg[e_rel_context]; + STUFF(context->ctx_context); + GEN_statement(request, node->nod_arg[e_mdc_statement]); + return; + + case nod_on_error: + STUFF(blr_error_handler); + temp = node->nod_arg[e_err_errs]; + STUFF_WORD(temp->nod_count); + for (ptr = temp->nod_arg, end = ptr + temp->nod_count; ptr < end; + ptr++) + gen_error_condition(request, *ptr); + GEN_statement(request, node->nod_arg[e_err_action]); + return; + + case nod_post: + STUFF(blr_post); + GEN_expr(request, node->nod_arg[e_pst_event]); + return; + + case nod_return: + GEN_return(request, node->nod_arg[e_rtn_procedure], FALSE); + return; + + case nod_exit: + STUFF(blr_leave); + STUFF(0); + return; + + case nod_store: + if ((temp = node->nod_arg[e_sto_rse]) != NULL) { + STUFF(blr_for); + GEN_expr(request, temp); + } + STUFF(blr_store); + GEN_expr(request, node->nod_arg[e_sto_relation]); + GEN_statement(request, node->nod_arg[e_sto_statement]); + return; + + case nod_abort: + STUFF(blr_leave); + STUFF((int) node->nod_arg[e_abrt_number]); + return; + + case nod_start_savepoint: + STUFF(blr_start_savepoint); + return; + + case nod_end_savepoint: + STUFF(blr_end_savepoint); + return; + + case nod_exception_stmt: + STUFF(blr_abort); + STUFF(blr_exception); + string = (STR) node->nod_arg[0]; + if (!(string->str_flags & STR_delimited_id)) { + id_length = string->str_length; + for (p = reinterpret_cast < char *>(string->str_data); *p; + id_length--) { + *p = UPPER(*p); + *p++; + } + } + STUFF_CSTRING(string->str_data); + return; + + case nod_while: + STUFF(blr_label); + STUFF((int) node->nod_arg[e_while_number]); + STUFF(blr_loop); + STUFF(blr_begin); + STUFF(blr_if); + GEN_expr(request, node->nod_arg[e_while_cond]); + GEN_statement(request, node->nod_arg[e_while_action]); + STUFF(blr_leave); + STUFF((int) node->nod_arg[e_while_number]); + STUFF(blr_end); + return; + + case nod_sqlcode: + case nod_gdscode: + STUFF(blr_abort); + gen_error_condition(request, node); + return; + + default: + ERRD_post(gds_sqlerr, gds_arg_number, (SLONG) - 901, + gds_arg_gds, gds_dsql_internal_err, gds_arg_gds, gds_node_err, /* gen.c: node not supported */ + 0); + } +} + + +static void gen_aggregate( REQ request, NOD node) +{ +/************************************** + * + * g e n _ a g g r e g a t e + * + ************************************** + * + * Functional description + * Generate blr for a relation reference. + * + **************************************/ + NOD list, *ptr, *end; + CTX context; + + context = (CTX) node->nod_arg[e_agg_context]; + STUFF(blr_aggregate); + STUFF(context->ctx_context); + gen_rse(request, node->nod_arg[e_agg_rse]); + +/* Handle GROUP BY clause */ + + STUFF(blr_group_by); + + if ((list = node->nod_arg[e_agg_group]) != NULL) { + STUFF(list->nod_count); + for (ptr = list->nod_arg, end = ptr + list->nod_count; ptr < end; + ptr++) + GEN_expr(request, *ptr); + } + else + STUFF(0); + +/* Generate value map */ + + gen_map(request, context->ctx_map); +} + + +static void gen_cast( REQ request, NOD node) +{ +/************************************** + * + * g e n _ c a s t + * + ************************************** + * + * Functional description + * Generate BLR for a data-type cast operation + * + **************************************/ + FLD field; + + STUFF(blr_cast); + field = (FLD) node->nod_arg[e_cast_target]; + DDL_put_field_dtype(request, field, TRUE); + GEN_expr(request, node->nod_arg[e_cast_source]); +} + + +static void gen_constant( REQ request, DSC * desc, BOOLEAN negate_value) +{ +/************************************** + * + * g e n _ c o n s t a n t + * + ************************************** + * + * Functional description + * Generate BLR for a constant. + * + **************************************/ + UCHAR *p; + USHORT l; + SLONG value; + SINT64 i64value; + + DSC tmp_desc; + + STUFF(blr_literal); + + if ((desc->dsc_dtype == dtype_double) + && (request->req_dbb->dbb_flags & DBB_v3)) + /* v3 doesn't understand blr_double literal, generate blr_text instead */ + { + tmp_desc = *desc; + tmp_desc.dsc_dtype = dtype_text; + tmp_desc.dsc_length = desc->dsc_scale; /* length of string literal */ + tmp_desc.dsc_scale = 0; + desc = &tmp_desc; + } + + l = desc->dsc_length; + p = desc->dsc_address; + + switch (desc->dsc_dtype) { + case dtype_short: + gen_descriptor(request, desc, TRUE); + value = *(SSHORT *) p; + if (negate_value) + value = -value; + STUFF_WORD(value); + break; + + case dtype_long: + gen_descriptor(request, desc, TRUE); + value = *(SLONG *) p; + if (negate_value) + value = -value; + STUFF_WORD(value); + STUFF_WORD(value >> 16); + break; + + case dtype_sql_time: + case dtype_sql_date: + gen_descriptor(request, desc, TRUE); + value = *(SLONG *) p; + STUFF_WORD(value); + STUFF_WORD(value >> 16); + break; + + case dtype_double: + /* this is used for approximate/large numeric literal + which is transmitted to the engine as a string. + */ + gen_descriptor(request, desc, TRUE); + l = (USHORT) desc->dsc_scale; /* length of string literal */ + if (negate_value) { + STUFF_WORD(l + 1); + STUFF('-'); + } + else { + STUFF_WORD(l); + } + + if (l) + do + STUFF(*p++); + while (--l); + break; + + case dtype_int64: + i64value = *(SINT64 *) p; + + if (negate_value) + i64value = -i64value; + else if (i64value == MIN_SINT64) { + /* UH OH! + * yylex correctly recognized the digits as the most-negative + * possible INT64 value, but unfortunately, there was no + * preceding '-' (a fact which the lexer could not know). + * The value is too big for a positive INT64 value, and it + * didn't contain an exponent so it's not a valid DOUBLE + * PRECISION literal either, so we have to bounce it. + */ + ERRD_post(isc_sqlerr, + gds_arg_number, (SLONG) - 104, + gds_arg_gds, isc_arith_except, 0); + } + + /* We and the lexer both agree that this is an SINT64 constant, + * and if the value needed to be negated, it already has been. + * If the value will fit into a 32-bit signed integer, generate + * it that way, else as an INT64. + */ + + if ((i64value >= (SINT64) MIN_SLONG) && + (i64value <= (SINT64) MAX_SLONG)) { + STUFF(blr_long); + STUFF(desc->dsc_scale); + STUFF_WORD(i64value); + STUFF_WORD(i64value >> 16); + break; + } + else { + STUFF(blr_int64); + STUFF(desc->dsc_scale); + STUFF_WORD(i64value); + STUFF_WORD(i64value >> 16); + STUFF_WORD(i64value >> 32); + STUFF_WORD(i64value >> 48); + } + break; + + case dtype_quad: + case dtype_blob: + case dtype_array: + case dtype_timestamp: + gen_descriptor(request, desc, TRUE); + value = *(SLONG *) p; + STUFF_WORD(value); + STUFF_WORD(value >> 16); + value = *(SLONG *) (p + 4); + STUFF_WORD(value); + STUFF_WORD(value >> 16); + break; + + case dtype_text: + gen_descriptor(request, desc, TRUE); + if (l) + do + STUFF(*p++); + while (--l); + break; + + default: + /* gen_constant: datatype not understood */ + ERRD_post(gds_sqlerr, gds_arg_number, (SLONG) - 103, + gds_arg_gds, gds_dsql_constant_err, 0); + } +} + + +static void gen_descriptor( REQ request, DSC * desc, USHORT texttype) +{ +/************************************** + * + * g e n _ d e s c r i p t o r + * + ************************************** + * + * Functional description + * Generate a blr descriptor from an internal descriptor. + * + **************************************/ + + switch (desc->dsc_dtype) { + case dtype_text: + if (request->req_dbb->dbb_flags & DBB_v3) + STUFF(blr_text); + else if (texttype || desc->dsc_ttype == ttype_binary) { + STUFF(blr_text2); + STUFF_WORD(desc->dsc_ttype); + } + else { + STUFF(blr_text2); /* automatic transliteration */ + STUFF_WORD(ttype_dynamic); + } + + STUFF_WORD(desc->dsc_length); + break; + + case dtype_varying: + if (request->req_dbb->dbb_flags & DBB_v3) + STUFF(blr_varying); + else if (texttype || desc->dsc_ttype == ttype_binary) { + STUFF(blr_varying2); + STUFF_WORD(desc->dsc_ttype); + } + else { + STUFF(blr_varying2); /* automatic transliteration */ + STUFF_WORD(ttype_dynamic); + } + STUFF_WORD(desc->dsc_length - sizeof(USHORT)); + break; + + case dtype_short: + STUFF(blr_short); + STUFF(desc->dsc_scale); + break; + + case dtype_long: + STUFF(blr_long); + STUFF(desc->dsc_scale); + break; + + case dtype_quad: + STUFF(blr_quad); + STUFF(desc->dsc_scale); + break; + + case dtype_int64: + STUFF(blr_int64); + STUFF(desc->dsc_scale); + break; + + case dtype_real: + STUFF(blr_float); + break; + + case dtype_double: + STUFF(blr_double); + break; + + case dtype_sql_date: + STUFF(blr_sql_date); + break; + + case dtype_sql_time: + STUFF(blr_sql_time); + break; + + case dtype_timestamp: + STUFF(blr_timestamp); + break; + + case dtype_blob: + case dtype_array: + STUFF(blr_quad); + STUFF(0); + break; + + default: + /* don't understand dtype */ + ERRD_post(gds_sqlerr, gds_arg_number, (SLONG) - 804, + gds_arg_gds, gds_dsql_datatype_err, 0); + } +} + + +static void gen_error_condition( REQ request, NOD node) +{ +/************************************** + * + * g e n _ e r r o r _ c o n d i t i o n + * + ************************************** + * + * Functional description + * Generate blr for an error condtion + * + **************************************/ + STR string; + + switch (node->nod_type) { + case nod_sqlcode: + STUFF(blr_sql_code); + STUFF_WORD(node->nod_arg[0]); + return; + + case nod_gdscode: + STUFF(blr_gds_code); + string = (STR) node->nod_arg[0]; + STUFF_CSTRING(string->str_data); + return; + + case nod_exception: + STUFF(blr_exception); + string = (STR) node->nod_arg[0]; + STUFF_CSTRING(string->str_data); + return; + + case nod_default: + STUFF(blr_default_code); + return; + + default: + assert(FALSE); + return; + } +} + + +static void gen_field( REQ request, CTX context, FLD field, NOD indices) +{ +/************************************** + * + * g e n _ f i e l d + * + ************************************** + * + * Functional description + * Generate blr for a field - field id's + * are preferred but not for trigger or view blr. + * + **************************************/ + NOD *ptr, *end; + +/* For older clients - generate an error should they try and + * access data types which did not exist in the older dialect */ + if (request->req_client_dialect <= SQL_DIALECT_V5) { + switch (field->fld_dtype) { + case dtype_sql_date: + case dtype_sql_time: + case dtype_int64: + ERRD_post(gds_sqlerr, gds_arg_number, (SLONG) - 804, + gds_arg_gds, gds_dsql_datatype_err, + gds_arg_gds, isc_sql_dialect_datatype_unsupport, + gds_arg_number, request->req_client_dialect, + gds_arg_string, + DSC_dtype_tostring(static_cast < UCHAR > + (field->fld_dtype)), 0); + break; + default: + /* No special action for other data types */ + break; + } + } + + + + if (indices) + STUFF(blr_index); + + if (DDL_ids(request)) { + STUFF(blr_fid); + STUFF(context->ctx_context); + STUFF_WORD(field->fld_id); + } + else { + STUFF(blr_field); + STUFF(context->ctx_context); + STUFF_CSTRING(field->fld_name); + } + + if (indices) { + STUFF(indices->nod_count); + for (ptr = indices->nod_arg, end = ptr + indices->nod_count; + ptr < end; ptr++) + GEN_expr(request, *ptr); + } +} + + +static void gen_for_select( REQ request, NOD for_select) +{ +/************************************** + * + * g e n _ f o r _ s e l e c t + * + ************************************** + * + * Functional description + * Generate BLR for a SELECT statement. + * + **************************************/ + NOD list, list_to, *ptr, *ptr_to, *end, rse; + + rse = for_select->nod_arg[e_flp_select]; + +/* Generate FOR loop */ + + STUFF(blr_for); + + if (!for_select->nod_arg[e_flp_action] && + !(request->req_dbb->dbb_flags & DBB_v3)) STUFF(blr_singular); + gen_rse(request, rse); + STUFF(blr_begin); + +/* Build body of FOR loop */ + + list = rse->nod_arg[e_rse_items]; + list_to = for_select->nod_arg[e_flp_into]; + if (list->nod_count != list_to->nod_count) + ERRD_post(gds_sqlerr, gds_arg_number, (SLONG) - 313, + gds_arg_gds, gds_dsql_count_mismatch, 0); + for (ptr = list->nod_arg, ptr_to = list_to->nod_arg, + end = ptr + list->nod_count; ptr < end; ptr++, ptr_to++) { + STUFF(blr_assignment); + GEN_expr(request, *ptr); + GEN_expr(request, *ptr_to); + } + if (for_select->nod_arg[e_flp_action]) + GEN_statement(request, for_select->nod_arg[e_flp_action]); + STUFF(blr_end); +} + + +static void gen_gen_id( REQ request, NOD node) +{ +/************************************** + * + * g e n _ g e n _ i d + * + ************************************** + * + * Functional description + * Generate BLR for gen_id + * + **************************************/ + STR string; + + STUFF(blr_gen_id); + string = (STR) node->nod_arg[e_gen_id_name]; + STUFF_CSTRING(string->str_data); + GEN_expr(request, node->nod_arg[e_gen_id_value]); +} + + +static void gen_join_rse( REQ request, NOD rse) +{ +/************************************** + * + * g e n _ j o i n _ r s e + * + ************************************** + * + * Functional description + * Generate a record selection expression + * with an explicit join type. + * + **************************************/ + NOD node; + + STUFF(blr_rs_stream); + STUFF(2); + + GEN_expr(request, rse->nod_arg[e_join_left_rel]); + GEN_expr(request, rse->nod_arg[e_join_rght_rel]); + + node = rse->nod_arg[e_join_type]; + if (node->nod_type != nod_join_inner) { + STUFF(blr_join_type); + if (node->nod_type == nod_join_left) + STUFF(blr_left); + else if (node->nod_type == nod_join_right) + STUFF(blr_right); + else + STUFF(blr_full); + } + + STUFF(blr_boolean); + GEN_expr(request, rse->nod_arg[e_join_boolean]); + + STUFF(blr_end); +} + + +static void gen_map( REQ request, MAP map) +{ +/************************************** + * + * g e n _ m a p + * + ************************************** + * + * Functional description + * Generate a value map for a record selection expression. + * + **************************************/ + USHORT count; + MAP temp; + + count = 0; + for (temp = map; temp; temp = temp->map_next) + temp->map_position = count++; + + STUFF(blr_map); + STUFF_WORD(count); + + for (temp = map; temp; temp = temp->map_next) { + STUFF_WORD(temp->map_position); + GEN_expr(request, temp->map_node); + } +} + + +static void gen_parameter( REQ request, PAR parameter) +{ +/************************************** + * + * g e n _ p a r a m e t e r + * + ************************************** + * + * Functional description + * Generate a parameter reference. + * + **************************************/ + MSG message; + PAR null; + + message = parameter->par_message; + + if ((null = parameter->par_null) != NULL) { + STUFF(blr_parameter2); + STUFF(message->msg_number); + STUFF_WORD(parameter->par_parameter); + STUFF_WORD(null->par_parameter); + return; + } + + STUFF(blr_parameter); + STUFF(message->msg_number); + STUFF_WORD(parameter->par_parameter); +} + + + +static void gen_plan( REQ request, NOD plan_expression) +{ +/************************************** + * + * g e n _ p l a n + * + ************************************** + * + * Functional description + * Generate blr for an access plan expression. + * + **************************************/ + NOD list, node, arg, *ptr, *end, *ptr2, *end2; + STR index_string; + +/* stuff the join type */ + + list = plan_expression->nod_arg[1]; + if (list->nod_count > 1) { + if (node = plan_expression->nod_arg[0]) + STUFF(blr_merge); + else + STUFF(blr_join); + STUFF(list->nod_count); + } + +/* stuff one or more plan items */ + + for (ptr = list->nod_arg, end = ptr + list->nod_count; ptr < end; ptr++) { + node = *ptr; + if (node->nod_type == nod_plan_expr) { + gen_plan(request, node); + continue; + } + + /* if we're here, it must be a nod_plan_item */ + + STUFF(blr_retrieve); + + /* stuff the relation--the relation id itself is redundant except + when there is a need to differentiate the base tables of views */ + + arg = node->nod_arg[0]; + gen_relation(request, (CTX) arg->nod_arg[e_rel_context]); + + /* now stuff the access method for this stream */ + + arg = node->nod_arg[1]; + switch (arg->nod_type) { + case nod_natural: + STUFF(blr_sequential); + break; + + case nod_index_order: + STUFF(blr_navigational); + index_string = (STR) arg->nod_arg[0]; + STUFF_CSTRING(index_string->str_data); + break; + + case nod_index: + STUFF(blr_indices); + arg = arg->nod_arg[0]; + STUFF(arg->nod_count); + for (ptr2 = arg->nod_arg, end2 = ptr2 + arg->nod_count; + ptr2 < end2; ptr2++) { + index_string = (STR) * ptr2; + STUFF_CSTRING(index_string->str_data); + } + break; + + default: + assert(FALSE); + break; + } + + } +} + + + +static void gen_relation( REQ request, CTX context) +{ +/************************************** + * + * g e n _ r e l a t i o n + * + ************************************** + * + * Functional description + * Generate blr for a relation reference. + * + **************************************/ + DSQL_REL relation; + PRC procedure; + NOD inputs, *ptr, *end; + + relation = context->ctx_relation; + procedure = context->ctx_procedure; + +/* if this is a trigger or procedure , don't want relation id used */ + if (relation) { + if (DDL_ids(request)) { + if (context->ctx_alias) + STUFF(blr_rid2); + else + STUFF(blr_rid); + STUFF_WORD(relation->rel_id); + } + else { + if (context->ctx_alias) + STUFF(blr_relation2); + else + STUFF(blr_relation); + STUFF_CSTRING(relation->rel_name); + } + + if (context->ctx_alias) + STUFF_CSTRING(context->ctx_alias); + STUFF(context->ctx_context); + } + else { + if (DDL_ids(request)) { + STUFF(blr_pid); + STUFF_WORD(procedure->prc_id); + } + else { + STUFF(blr_procedure); + STUFF_CSTRING(procedure->prc_name); + } + STUFF(context->ctx_context); + STUFF_WORD(procedure->prc_in_count); + if (inputs = context->ctx_proc_inputs) + for (ptr = inputs->nod_arg, end = ptr + inputs->nod_count; + ptr < end; ptr++) + GEN_expr(request, *ptr); + } +} + + +void GEN_return( REQ request, NOD procedure, BOOLEAN eos_flag) +{ +/************************************** + * + * g e n _ r e t u r n + * + ************************************** + * + * Functional description + * Generate blr for a procedure return. + * + **************************************/ + NOD parameters, parameter, *ptr, *end; + VAR variable; + USHORT outputs; + + if (!procedure) + return; + + if (!eos_flag) + STUFF(blr_begin); + STUFF(blr_send); + STUFF(1); + STUFF(blr_begin); + outputs = 0; + if (parameters = procedure->nod_arg[e_prc_outputs]) { + for (ptr = parameters->nod_arg, end = ptr + parameters->nod_count; + ptr < end; ptr++) { + outputs++; + parameter = *ptr; + variable = (VAR) parameter->nod_arg[e_var_variable]; + STUFF(blr_assignment); + STUFF(blr_variable); + STUFF_WORD(variable->var_variable_number); + STUFF(blr_parameter2); + STUFF(variable->var_msg_number); + STUFF_WORD(variable->var_msg_item); + STUFF_WORD(variable->var_msg_item + 1); + } + } + STUFF(blr_assignment); + STUFF(blr_literal); + STUFF(blr_short); + STUFF(0); + if (eos_flag) + STUFF_WORD(0); + else + STUFF_WORD(1); + STUFF(blr_parameter); + STUFF(1); + STUFF_WORD(2 * outputs); + STUFF(blr_end); + if (!eos_flag) { + STUFF(blr_stall); + STUFF(blr_end); + } +} + + +static void gen_rse( REQ request, NOD rse) +{ +/************************************** + * + * g e n _ r s e + * + ************************************** + * + * Functional description + * Generate a record selection expression. + * + **************************************/ + NOD node, list, *ptr, *end; + + if (rse->nod_arg[e_rse_singleton] + && !(request->req_dbb->dbb_flags & DBB_v3)) STUFF(blr_singular); + + STUFF(blr_rse); + + list = rse->nod_arg[e_rse_streams]; + +/* Handle source streams */ + + if (list->nod_type == nod_union) { + STUFF(1); + gen_union(request, rse); + } + else if (list->nod_type == nod_list) { + STUFF(list->nod_count); + for (ptr = list->nod_arg, end = ptr + list->nod_count; ptr < end; + ptr++) { + node = *ptr; + if (node->nod_type == nod_relation || + node->nod_type == nod_aggregate || node->nod_type == nod_join) + GEN_expr(request, node); + } + } + else { + STUFF(1); + GEN_expr(request, list); + } + + if ((node = rse->nod_arg[e_rse_first]) != NULL) { + STUFF(blr_first); + GEN_expr(request, node); + } + + if ((node = rse->nod_arg[e_rse_boolean]) != NULL) { + STUFF(blr_boolean); + GEN_expr(request, node); + } + + if ((list = rse->nod_arg[e_rse_sort]) != NULL) + gen_sort(request, list); + + if ((list = rse->nod_arg[e_rse_reduced]) != NULL) { + STUFF(blr_project); + STUFF(list->nod_count); + for (ptr = list->nod_arg, end = ptr + list->nod_count; ptr < end; + ptr++) + GEN_expr(request, *ptr); + } + +/* if the user specified an access plan to use, add it here */ + + if ((node = rse->nod_arg[e_rse_plan]) != NULL) { + STUFF(blr_plan); + gen_plan(request, node); + } + +#ifdef SCROLLABLE_CURSORS +/* generate a statement to be executed if the user scrolls + in a direction other than forward; a message is sent outside + the normal send/receive protocol to specify the direction + and offset to scroll; note that we do this only on a SELECT + type statement and only when talking to a 4.1 engine or greater */ + + if (request->req_type == REQ_SELECT && + request->req_dbb->dbb_base_level >= 5) { + STUFF(blr_receive); + STUFF(request->req_async->msg_number); + STUFF(blr_seek); + PAR parameter = request->req_async->msg_parameters; + gen_parameter(request, parameter->par_next); + gen_parameter(request, parameter); + } +#endif + + STUFF(blr_end); +} + + +static void gen_select( REQ request, NOD rse) +{ +/************************************** + * + * g e n _ s e l e c t + * + ************************************** + * + * Functional description + * Generate BLR for a SELECT statement. + * + **************************************/ + NOD list, *ptr, *end, item, alias, map_node; + PAR parameter; + MSG message; + FLD field; + DSC constant_desc; + DSQL_REL relation; + UDF udf; + CTX context; + STR string; + SSHORT constant; + MAP map; + + constant_desc.dsc_dtype = dtype_short; + constant_desc.dsc_scale = 0; + constant_desc.dsc_sub_type = 0; + constant_desc.dsc_flags = 0; + constant_desc.dsc_length = sizeof(SSHORT); + constant_desc.dsc_address = (UCHAR *) & constant; + +/* Set up parameter for things in the select list */ + + list = rse->nod_arg[e_rse_items]; + for (ptr = list->nod_arg, end = ptr + list->nod_count; ptr < end; ptr++) { + item = *ptr; + parameter = MAKE_parameter(request->req_receive, TRUE, TRUE); + parameter->par_node = item; + MAKE_desc(¶meter->par_desc, item); + if (item->nod_type == nod_field) { + field = (FLD) item->nod_arg[e_fld_field]; + parameter->par_name = parameter->par_alias = field->fld_name; + context = (CTX) item->nod_arg[e_fld_context]; + if (context->ctx_relation) { + parameter->par_rel_name = context->ctx_relation->rel_name; + parameter->par_owner_name = context->ctx_relation->rel_owner; + } + else if (context->ctx_procedure) { + parameter->par_rel_name = context->ctx_procedure->prc_name; + parameter->par_owner_name = context->ctx_procedure->prc_owner; + } + } + else if (item->nod_type == nod_dbkey) { + parameter->par_alias = const_cast < char *>(db_key_name); + parameter->par_name = const_cast < char *>(db_key_name); + context = (CTX) item->nod_arg[0]->nod_arg[0]; + parameter->par_rel_name = context->ctx_relation->rel_name; + parameter->par_owner_name = context->ctx_relation->rel_owner; + } + else if (item->nod_type == nod_alias) { + string = (STR) item->nod_arg[e_alias_alias]; + parameter->par_alias = (TEXT *) string->str_data; + alias = item->nod_arg[e_alias_value]; + if (alias->nod_type == nod_field) { + field = (FLD) alias->nod_arg[e_fld_field]; + parameter->par_name = field->fld_name; + context = (CTX) alias->nod_arg[e_fld_context]; + if (context->ctx_relation) { + parameter->par_rel_name = context->ctx_relation->rel_name; + parameter->par_owner_name = + context->ctx_relation->rel_owner; + } + else if (context->ctx_procedure) { + parameter->par_rel_name = + context->ctx_procedure->prc_name; + parameter->par_owner_name = + context->ctx_procedure->prc_owner; + } + } + else if (alias->nod_type == nod_dbkey) { + parameter->par_name = const_cast < char *>(db_key_name); + context = (CTX) alias->nod_arg[0]->nod_arg[0]; + parameter->par_rel_name = context->ctx_relation->rel_name; + parameter->par_owner_name = context->ctx_relation->rel_owner; + } + } + else if (item->nod_type == nod_map) { + map = (MAP) item->nod_arg[e_map_map]; + map_node = map->map_node; + while (map_node->nod_type == nod_map) { + /* skip all the nod_map nodes */ + map = (MAP) map_node->nod_arg[e_map_map]; + map_node = map->map_node; + } + if (map_node->nod_type == nod_field) { + field = (FLD) map_node->nod_arg[e_fld_field]; + parameter->par_name = parameter->par_alias = field->fld_name; + } + else if (map_node->nod_type == nod_alias) { + string = (STR) map_node->nod_arg[e_alias_alias]; + parameter->par_alias = (TEXT *) string->str_data; + alias = map_node->nod_arg[e_alias_value]; + if (alias->nod_type == nod_field) { + field = (FLD) alias->nod_arg[e_fld_field]; + parameter->par_name = field->fld_name; + } + } + else if (map_node->nod_type == nod_agg_count) + parameter->par_name = parameter->par_alias = "COUNT"; + else if (map_node->nod_type == nod_agg_total) + parameter->par_name = parameter->par_alias = "SUM"; + else if (map_node->nod_type == nod_agg_average) + parameter->par_name = parameter->par_alias = "AVG"; + else if (map_node->nod_type == nod_agg_total2) + parameter->par_name = parameter->par_alias = "SUM"; + else if (map_node->nod_type == nod_agg_average2) + parameter->par_name = parameter->par_alias = "AVG"; + else if (map_node->nod_type == nod_agg_min) + parameter->par_name = parameter->par_alias = "MIN"; + else if (map_node->nod_type == nod_agg_max) + parameter->par_name = parameter->par_alias = "MAX"; + } + else if (item->nod_type == nod_udf) { + udf = (UDF) item->nod_arg[0]; + parameter->par_name = parameter->par_alias = udf->udf_name; + } + else if (item->nod_type == nod_gen_id) + parameter->par_name = parameter->par_alias = "GEN_ID"; + else if (item->nod_type == nod_gen_id2) + parameter->par_name = parameter->par_alias = "GEN_ID"; + } + +/* Set up parameter to handle EOF */ + + request->req_eof = parameter = + MAKE_parameter(request->req_receive, FALSE, FALSE); + parameter->par_desc.dsc_dtype = dtype_short; + parameter->par_desc.dsc_scale = 0; + parameter->par_desc.dsc_length = sizeof(SSHORT); + +/* Save DBKEYs for possible update later */ + + list = rse->nod_arg[e_rse_streams]; + + if (!rse->nod_arg[e_rse_reduced]) { + for (ptr = list->nod_arg, end = ptr + list->nod_count; ptr < end; + ptr++) + if ((item = *ptr) && (item->nod_type == nod_relation)) { + context = (CTX) item->nod_arg[e_rel_context]; + if (relation = context->ctx_relation) { + /* Set up dbkey */ + parameter = + MAKE_parameter(request->req_receive, FALSE, FALSE); + parameter->par_dbkey_ctx = context; + parameter->par_desc.dsc_dtype = dtype_text; + parameter->par_desc.dsc_ttype = ttype_binary; + parameter->par_desc.dsc_length = + relation->rel_dbkey_length; + + /* Set up record version - for post v33 databases */ + + if (!(request->req_dbb->dbb_flags & DBB_v3)) { + parameter = + MAKE_parameter(request->req_receive, FALSE, + FALSE); + parameter->par_rec_version_ctx = context; + parameter->par_desc.dsc_dtype = dtype_text; + parameter->par_desc.dsc_ttype = ttype_binary; + parameter->par_desc.dsc_length = + relation->rel_dbkey_length / 2; + } + } + } + } + +#ifdef SCROLLABLE_CURSORS +/* define the parameters for the scrolling message--offset and direction, + in that order to make it easier to generate the request */ + + if (request->req_type == REQ_SELECT && + request->req_dbb->dbb_base_level >= 5) { + parameter = MAKE_parameter(request->req_async, FALSE, FALSE); + parameter->par_desc.dsc_dtype = dtype_short; + parameter->par_desc.dsc_length = sizeof(USHORT); + parameter->par_desc.dsc_scale = 0; + parameter->par_desc.dsc_flags = 0; + parameter->par_desc.dsc_sub_type = 0; + + parameter = MAKE_parameter(request->req_async, FALSE, FALSE); + parameter->par_desc.dsc_dtype = dtype_long; + parameter->par_desc.dsc_length = sizeof(ULONG); + parameter->par_desc.dsc_scale = 0; + parameter->par_desc.dsc_flags = 0; + parameter->par_desc.dsc_sub_type = 0; + } +#endif + +/* Generate definitions for the messages */ + + GEN_port(request, request->req_receive); + message = request->req_send; + if (message->msg_parameter) + GEN_port(request, message); + else + request->req_send = NULL; +#ifdef SCROLLABLE_CURSORS + if (request->req_type == REQ_SELECT && + request->req_dbb->dbb_base_level >= 5) + GEN_port(request, request->req_async); +#endif + +/* If there is a send message, build a RECEIVE */ + + if ((message = request->req_send) != NULL) { + STUFF(blr_receive); + STUFF(message->msg_number); + } + +/* Generate FOR loop */ + + message = request->req_receive; + + STUFF(blr_for); + if (!(request->req_dbb->dbb_flags & DBB_v3)) + STUFF(blr_stall); + gen_rse(request, rse); + STUFF(blr_send); + STUFF(message->msg_number); + STUFF(blr_begin); + +/* Build body of FOR loop */ + + STUFF(blr_assignment); + constant = 1; + gen_constant(request, &constant_desc, USE_VALUE); + gen_parameter(request, request->req_eof); + + for (parameter = message->msg_parameters; parameter; + parameter = parameter->par_next) { + if (parameter->par_node) { + STUFF(blr_assignment); + GEN_expr(request, parameter->par_node); + gen_parameter(request, parameter); + } + if (context = parameter->par_dbkey_ctx) { + STUFF(blr_assignment); + STUFF(blr_dbkey); + STUFF(context->ctx_context); + gen_parameter(request, parameter); + } + if (context = parameter->par_rec_version_ctx) { + STUFF(blr_assignment); + STUFF(blr_record_version); + STUFF(context->ctx_context); + gen_parameter(request, parameter); + } + } + + STUFF(blr_end); + STUFF(blr_send); + STUFF(message->msg_number); + STUFF(blr_assignment); + constant = 0; + gen_constant(request, &constant_desc, USE_VALUE); + gen_parameter(request, request->req_eof); +} + + +static void gen_sort( REQ request, NOD list) +{ +/************************************** + * + * g e n _ s o r t + * + ************************************** + * + * Functional description + * Generate a sort clause. + * + **************************************/ + NOD *ptr, *end; + + STUFF(blr_sort); + STUFF(list->nod_count); + + for (ptr = list->nod_arg, end = ptr + list->nod_count; ptr < end; ptr++) { + if ((*ptr)->nod_arg[1]) + STUFF(blr_descending); + else + STUFF(blr_ascending); + GEN_expr(request, (*ptr)->nod_arg[0]); + } +} + + +static void gen_table_lock( REQ request, NOD tbl_lock, USHORT lock_level) +{ +/************************************** + * + * g e n _ t a b l e _ l o c k + * + ************************************** + * + * Functional description + * Generate tpb for table lock. + * If lock level is specified, it overrrides the transaction lock level. + * + **************************************/ + NOD tbl_names, *ptr, *end; + STR temp; + SSHORT flags; + USHORT lock_mode; + + if ((!tbl_lock) || (tbl_lock->nod_type != nod_table_lock)) + return; + + tbl_names = tbl_lock->nod_arg[e_lock_tables]; + flags = 0; + + if (tbl_lock->nod_arg[e_lock_mode]) + flags = tbl_lock->nod_arg[e_lock_mode]->nod_flags; + + if (flags & NOD_PROTECTED) + lock_level = gds_tpb_protected; + else if (flags & NOD_SHARED) + lock_level = gds_tpb_shared; + + lock_mode = (flags & NOD_WRITE) ? gds_tpb_lock_write : gds_tpb_lock_read; + + for (ptr = tbl_names->nod_arg, end = ptr + tbl_names->nod_count; + ptr < end; ptr++) { + if ((*ptr)->nod_type != nod_relation_name) + continue; + + STUFF(lock_mode); + + /* stuff table name */ + temp = (STR) ((*ptr)->nod_arg[e_rln_name]); + stuff_cstring(request, reinterpret_cast < char *>(temp->str_data)); + + STUFF(lock_level); + } +} + + +static void gen_udf( REQ request, NOD node) +{ +/************************************** + * + * g e n _ u d f + * + ************************************** + * + * Functional description + * Generate a user defined function. + * + **************************************/ + UDF udf; + NOD list, *ptr, *end; + + udf = (UDF) node->nod_arg[0]; + STUFF(blr_function); + STUFF_CSTRING(udf->udf_name); + + if ((node->nod_count == 2) && (list = node->nod_arg[1])) { + STUFF(list->nod_count); + for (ptr = list->nod_arg, end = ptr + list->nod_count; ptr < end; + ptr++) + GEN_expr(request, *ptr); + } + else + STUFF(0); +} + + +static void gen_union( REQ request, NOD union_node) +{ +/************************************** + * + * g e n _ u n i o n + * + ************************************** + * + * Functional description + * Generate a union of substreams. + * + **************************************/ + NOD sub_rse, *ptr, *end, streams; + NOD items, *iptr, *iend; + USHORT count; + CTX union_context; + + STUFF(blr_union); + +/* Obtain the context for UNION from the first MAP node */ + items = union_node->nod_arg[e_rse_items]; + union_context = (CTX) items->nod_arg[0]->nod_arg[e_map_context]; + STUFF(union_context->ctx_context); + + streams = union_node->nod_arg[e_rse_streams]; + STUFF(streams->nod_count); /* number of substreams */ + + for (ptr = streams->nod_arg, end = ptr + streams->nod_count; ptr < end; + ptr++) { + sub_rse = *ptr; + gen_rse(request, sub_rse); + items = sub_rse->nod_arg[e_rse_items]; + STUFF(blr_map); + STUFF_WORD(items->nod_count); + count = 0; + for (iptr = items->nod_arg, iend = iptr + items->nod_count; + iptr < iend; iptr++) { + STUFF_WORD(count); + GEN_expr(request, *iptr); + count++; + } + } +} + + +static void stuff_cstring( REQ request, char *string) +{ +/************************************** + * + * s t u f f _ c s t r i n g + * + ************************************** + * + * Functional description + * Write out a string with one byte of length. + * + **************************************/ + UCHAR c; + + STUFF(strlen(string)); + while ((c = *string++)) + STUFF(c); +} + + +static void stuff_word( REQ request, USHORT word) +{ +/************************************** + * + * s t u f f _ w o r d + * + ************************************** + * + * Functional description + * Cram a word into the blr buffer. If the buffer is getting + * ready to overflow, expand it. + * + **************************************/ + + STUFF(word); + STUFF(word >> 8); +} diff --git a/src/dsql/gen_proto.h b/src/dsql/gen_proto.h new file mode 100644 index 0000000000..2300745a05 --- /dev/null +++ b/src/dsql/gen_proto.h @@ -0,0 +1,35 @@ +/* + * PROGRAM: Dynamic SQL runtime support + * MODULE: gen_proto.h + * DESCRIPTION: Prototype Header file for gen.c + * + * 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): ______________________________________. + */ + +#ifndef _DSQL_GEN_PROTO_H_ +#define _DSQL_GEN_PROTO_H_ + +extern UCHAR GEN_expand_buffer(struct req *, UCHAR); +extern void GEN_expr(struct req *, struct nod *); +extern void GEN_port(struct req *, struct msg *); +extern void GEN_request(struct req *, struct nod *); +extern void GEN_return(REQ, NOD, BOOLEAN); +extern void GEN_start_transaction(struct req *, struct nod *); +extern void GEN_statement(struct req *, struct nod *); + +#endif /* _DSQL_GEN_PROTO_H_ */ diff --git a/src/dsql/hsh.cpp b/src/dsql/hsh.cpp new file mode 100644 index 0000000000..2d2529c605 --- /dev/null +++ b/src/dsql/hsh.cpp @@ -0,0 +1,505 @@ +/* + * PROGRAM: Dynamic SQL runtime support + * MODULE: hsh.c + * DESCRIPTION: Hash table and symbol manager + * + * The contents of this file are subject to the Interbase Public + * License Version 1.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy + * of the License at http://www.Inprise.com/IPL.html + * + * Software distributed under the License is distributed on an + * "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either express + * or implied. See the License for the specific language governing + * rights and limitations under the License. + * + * The Original Code was created by Inprise Corporation + * and its predecessors. Portions created by Inprise Corporation are + * Copyright (C) Inprise Corporation. + * + * All Rights Reserved. + * Contributor(s): ______________________________________. + */ + +#include +#include "../dsql/dsql.h" +#include "../dsql/sym.h" +#include "../include/jrd/gds.h" +#include "../dsql/alld_proto.h" +#include "../dsql/errd_proto.h" +#include "../dsql/hsh_proto.h" +#include "../jrd/sch_proto.h" +#include "../jrd/thd_proto.h" + + +extern "C" { + + +ASSERT_FILENAME +#define HASH_SIZE 211 +static SSHORT hash(register SCHAR *, register USHORT); +static BOOLEAN remove_symbol(struct sym **, struct sym *); +static BOOLEAN scompare(register TEXT *, register USHORT, register TEXT *, + USHORT); + +static SYM *hash_table; + +/* + SUPERSERVER can end up with many hands in the pie, so some + protection is provided via a mutex. This ensures the integrity + of lookups, inserts and removals, and also allows teh traversing + of symbol chains for marking of relations and procedures. + Otherwise, one DSQL user won't know what the other is doing + to the same object. +*/ + +#ifdef SUPERSERVER +static MUTX_T hash_mutex; +static USHORT hash_mutex_inited = 0; +#define LOCK_HASH THD_mutex_lock (&hash_mutex) +#define UNLOCK_HASH THD_mutex_unlock (&hash_mutex); +#else +#define LOCK_HASH +#define UNLOCK_HASH +#endif + + +void HSHD_init(void) +{ +/************************************* + * + * H S H D _ i n i t + * + ************************************* + * + * functional description + * create a new hash table + * + ************************************/ + UCHAR *p; + +#ifdef SUPERSERVER + if (!hash_mutex_inited) { + hash_mutex_inited = 1; + THD_mutex_init(&hash_mutex); + } +#endif + + p = (UCHAR *) ALLD_malloc(sizeof(SYM) * HASH_SIZE); + memset(p, 0, sizeof(SYM) * HASH_SIZE); + + hash_table = (SYM *) p; +} + + +#ifdef DEV_BUILD + +#include "../jrd/ib_stdio.h" + +void HSHD_debug(void) +{ +/************************************** + * + * H S H D _ d e b u g + * + ************************************** + * + * Functional description + * Print out the hash table for debugging. + * + **************************************/ + SYM collision; + SYM homptr; + SSHORT h; + +/* dump each hash table entry */ + + LOCK_HASH; + for (h = 0; h < HASH_SIZE; h++) { + for (collision = hash_table[h]; collision; + collision = collision->sym_collision) { + /* check any homonyms first */ + + ib_fprintf(ib_stderr, "Symbol type %d: %s %x\n", + collision->sym_type, collision->sym_string, + collision->sym_dbb); + for (homptr = collision->sym_homonym; homptr; + homptr = homptr->sym_homonym) { + ib_fprintf(ib_stderr, "Homonym Symbol type %d: %s %x\n", + homptr->sym_type, homptr->sym_string, + homptr->sym_dbb); + } + } + } + UNLOCK_HASH; +} +#endif + + +void HSHD_fini(void) +{ +/************************************** + * + * H S H D _ f i n i + * + ************************************** + * + * Functional description + * Clear out the symbol table. All the + * symbols are deallocated with their pools. + * + **************************************/ + SSHORT i; + + for (i = 0; i < HASH_SIZE; i++) + hash_table[i] = NULL; + + ALLD_free(reinterpret_cast < SCHAR * >(hash_table)); + hash_table = NULL; +} + + +void HSHD_finish( void *database) +{ +/************************************** + * + * H S H D _ f i n i s h + * + ************************************** + * + * Functional description + * Remove symbols used by a particular database. + * Don't bother to release them since their pools + * will be released. + * + **************************************/ + SYM *collision; + SYM *homptr; + SYM symbol; + SYM chain; + SSHORT h; + +/* check each hash table entry */ + + LOCK_HASH; + for (h = 0; h < HASH_SIZE; h++) { + for (collision = &hash_table[h]; *collision;) { + /* check any homonyms first */ + + chain = *collision; + for (homptr = &chain->sym_homonym; *homptr;) { + symbol = *homptr; + if (symbol->sym_dbb == database) { + *homptr = symbol->sym_homonym; + symbol = symbol->sym_homonym; + } + else + homptr = &symbol->sym_homonym; + } + + /* now, see if the root entry has to go */ + + if (chain->sym_dbb == database) { + if (chain->sym_homonym) { + chain->sym_homonym->sym_collision = chain->sym_collision; + *collision = chain->sym_homonym; + } + else + *collision = chain->sym_collision; + chain = *collision; + } + else + collision = &chain->sym_collision; + } + } + UNLOCK_HASH; +} + + +void HSHD_insert( register SYM symbol) +{ +/************************************** + * + * H S H D _ i n s e r t + * + ************************************** + * + * Functional description + * Insert a symbol into the hash table. + * + **************************************/ + register SSHORT h; + register void *database; + register SYM old; + + LOCK_HASH; + h = hash(symbol->sym_string, symbol->sym_length); + database = symbol->sym_dbb; + + assert(symbol->sym_type >= SYM_statement && symbol->sym_type <= SYM_eof); + + for (old = hash_table[h]; old; old = old->sym_collision) + if ((!database || (database == old->sym_dbb)) && + scompare(symbol->sym_string, symbol->sym_length, old->sym_string, + old->sym_length)) { + symbol->sym_homonym = old->sym_homonym; + old->sym_homonym = symbol; + UNLOCK_HASH; + return; + } + + symbol->sym_collision = hash_table[h]; + hash_table[h] = symbol; + UNLOCK_HASH; +} + + +SYM HSHD_lookup(void *database, + TEXT * string, + SSHORT length, SYM_TYPE type, USHORT parser_version) +{ +/************************************** + * + * H S H D _ l o o k u p + * + ************************************** + * + * Functional description + * Perform a string lookup against hash table. + * Make sure to only return a symbol of the desired type. + * + **************************************/ + register SYM symbol; + SSHORT h; + + LOCK_HASH; + h = hash(string, length); + for (symbol = hash_table[h]; symbol; symbol = symbol->sym_collision) + if ((database == symbol->sym_dbb) && + scompare(string, length, symbol->sym_string, symbol->sym_length)) { + /* Search for a symbol of the proper type */ + while (symbol && symbol->sym_type != type) + symbol = symbol->sym_homonym; + UNLOCK_HASH; + + /* If the symbol found was not part of the list of keywords for the + * client connecting, then assume nothing was found + */ + if (symbol) { + if (parser_version < symbol->sym_version + && type == SYM_keyword) return NULL; + } + return symbol; + } + + UNLOCK_HASH; + return NULL; +} + + +void HSHD_remove( SYM symbol) +{ +/************************************** + * + * H S H D _ r e m o v e + * + ************************************** + * + * Functional description + * Remove a symbol from the hash table. + * + **************************************/ + SYM *collision; + SSHORT h; + + LOCK_HASH; + h = hash(symbol->sym_string, symbol->sym_length); + + for (collision = &hash_table[h]; *collision; + collision = + &(*collision)->sym_collision) if (remove_symbol(collision, symbol)) { + UNLOCK_HASH; + return; + } + + UNLOCK_HASH; + IBERROR(-1, "HSHD_remove failed"); +} + + +void HSHD_set_flag( + void *database, + TEXT * string, SSHORT length, SYM_TYPE type, SSHORT flag) +{ +/************************************** + * + * H S H D _ s e t _ f l a g + * + ************************************** + * + * Functional description + * Set a flag in all similar objects in a chain. This + * is used primarily to mark relations and procedures + * as deleted. The object must have the same name and + * type, but not the same database, and must belong to + * some database. Later access to such an object by + * another user or thread should result in that object's + * being refreshed. Note that even if the relation name + * and ID, or the procedure name and ID both match, it + * may still not represent an exact match. This is because + * there's no way at present for DSQL to tell if two databases + * as represented in DSQL are attachments to the same physical + * database. + * + **************************************/ + SYM symbol, homonym; + SSHORT h; + DSQL_REL sym_rel; + PRC sym_prc; + + +/* as of now, there's no work to do if there is no database or if + the type is not a relation or procedure */ + + if (!database) + return; + switch (type) { + case SYM_relation: + case SYM_procedure: + break; + default: + return; + } + + LOCK_HASH; + h = hash(string, length); + for (symbol = hash_table[h]; symbol; symbol = symbol->sym_collision) { + if (symbol->sym_dbb && (database != symbol->sym_dbb) && + scompare(string, length, symbol->sym_string, symbol->sym_length)) { + + /* the symbol name matches and it's from a different database */ + + for (homonym = symbol; homonym; homonym = homonym->sym_homonym) { + if (homonym->sym_type == type) { + + /* the homonym is of the correct type */ + + /* the next check is for the same relation or procedure ID, + which indicates that it MAY be the same relation or + procedure */ + + switch (type) { + case SYM_relation: + sym_rel = (DSQL_REL) homonym->sym_object; + sym_rel->rel_flags |= flag; + break; + + case SYM_procedure: + sym_prc = (PRC) homonym->sym_object; + sym_prc->prc_flags |= flag; + break; + } + } + } + } + } + UNLOCK_HASH; +} + + +static SSHORT hash( register SCHAR * string, register USHORT length) +{ +/************************************** + * + * h a s h + * + ************************************** + * + * Functional description + * Returns the hash function of a string. + * + **************************************/ + register SLONG value; + SCHAR c; + + value = 0; + + while (length--) { + c = *string++; + value = (value << 1) + (c); + } + + return ((value >= 0) ? value : -value) % HASH_SIZE; +} + + +static BOOLEAN remove_symbol( SYM * collision, SYM symbol) +{ +/************************************** + * + * r e m o v e _ s y m b o l + * + ************************************** + * + * Functional description + * Given the address of a collision, + * remove a symbol from the collision + * and homonym linked lists. + * + **************************************/ + register SYM *ptr, homonym; + + if (symbol == *collision) { + if ((homonym = symbol->sym_homonym) != NULL) { + homonym->sym_collision = symbol->sym_collision; + *collision = homonym; + } + else + *collision = symbol->sym_collision; + + return TRUE; + } + + for (ptr = &(*collision)->sym_homonym; *ptr; ptr = &(*ptr)->sym_homonym) + if (symbol == *ptr) { + *ptr = symbol->sym_homonym; + return TRUE; + } + + return FALSE; +} + + +static BOOLEAN scompare( + register TEXT * string1, + register USHORT length1, + register TEXT * string2, USHORT length2) +{ +/************************************** + * + * s c o m p a r e + * + ************************************** + * + * Functional description + * Compare two symbolic strings + * The character set for these strings is either ASCII or + * Unicode in UTF format. + * Symbols are case-significant - so no uppercase operation + * is performed. + * + **************************************/ + + if (length1 != length2) + return FALSE; + + while (length1--) { + if ((*string1++) != (*string2++)) + return FALSE; + } + + return TRUE; +} + + +} // extern "C" diff --git a/src/dsql/hsh_proto.h b/src/dsql/hsh_proto.h new file mode 100644 index 0000000000..50eb865347 --- /dev/null +++ b/src/dsql/hsh_proto.h @@ -0,0 +1,43 @@ +/* + * PROGRAM: Dynamic SQL runtime support + * MODULE: hsh_proto.h + * DESCRIPTION: Prototype Header file for hsh.c + * + * 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): ______________________________________. + */ + +#ifndef DSQL_HSH_PROTO_H +#define DSQL_HSH_PROTO_H + +#ifdef __cplusplus +extern "C" { +#endif + +void HSHD_fini(void); +void HSHD_finish(void *); +void HSHD_init(void); +void HSHD_insert(struct sym *); +SYM HSHD_lookup(void *, TEXT *, SSHORT, SYM_TYPE, USHORT); +void HSHD_remove(struct sym *); +void HSHD_set_flag(void *, TEXT *, SSHORT, SYM_TYPE, SSHORT); + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /*DSQL_HSH_PROTO_H*/ diff --git a/src/dsql/keywords.h b/src/dsql/keywords.h new file mode 100644 index 0000000000..3b3ac9fc58 --- /dev/null +++ b/src/dsql/keywords.h @@ -0,0 +1,235 @@ +/* This list is maintained in alphabetical sorted order by 2nd column. + The following command will resort the list -- except for this comment + sort -t , -k 2b,2b keywords.h + * 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): ______________________________________. + See dsql/parse.y for a chronological list. */ +NOT_LSS, "!<", 1, + NEQ, "!=", 1, + NOT_GTR, "!>", 1, + LPAREN, "(", 1, + RPAREN, ")", 1, COMMA, ",", 1, LSS, "<", 1, LEQ, "<=", 1, NEQ, "<>", 1, /* Alias of != */ + EQL, "=", 1, + GTR, ">", 1, + GEQ, ">=", 1, + ACTION, "ACTION", 1, + ACTIVE, "ACTIVE", 1, + ADD, "ADD", 1, + ADMIN, "ADMIN", 1, + AFTER, "AFTER", 1, + ALL, "ALL", 1, + ALTER, "ALTER", 1, AND, "AND", 1, ANY, "ANY", 1, AS, "AS", 1, ASC, "ASC", 1, /* Alias of ASCENDING */ + ASC, "ASCENDING", 1, + AT, "AT", 1, + AUTO, "AUTO", 1, + AVG, "AVG", 1, + BASENAME, "BASE_NAME", 1, + BEFORE, "BEFORE", 1, + BEGIN, "BEGIN", 1, + BETWEEN, "BETWEEN", 1, + BLOB, "BLOB", 1, + BY, "BY", 1, + CACHE, "CACHE", 1, + CASCADE, "CASCADE", 1, + CAST, "CAST", 1, + KW_CHAR, "CHAR", 1, + CHARACTER, "CHARACTER", 1, + CHECK, "CHECK", 1, + CHECK_POINT_LEN, "CHECK_POINT_LENGTH", 1, + COLLATE, "COLLATE", 1, + COLUMN, "COLUMN", 2, + COMMIT, "COMMIT", 1, + COMMITTED, "COMMITTED", 1, + COMPUTED, "COMPUTED", 1, + CONDITIONAL, "CONDITIONAL", 1, + CONSTRAINT, "CONSTRAINT", 1, + CONTAINING, "CONTAINING", 1, + COUNT, "COUNT", 1, + CREATE, "CREATE", 1, + CSTRING, "CSTRING", 1, + CURRENT, "CURRENT", 1, + CURRENT_DATE, "CURRENT_DATE", 2, + CURRENT_TIME, "CURRENT_TIME", 2, + CURRENT_TIMESTAMP, "CURRENT_TIMESTAMP", 2, + CURSOR, "CURSOR", 1, + DATABASE, "DATABASE", 1, + DATE, "DATE", 1, + DAY, "DAY", 2, + DEBUG, "DEBUG", 1, + KW_DEC, "DEC", 1, + DECIMAL, "DECIMAL", 1, + DECLARE, "DECLARE", 1, + DEFAULT, "DEFAULT", 1, DELETE, "DELETE", 1, DESC, "DESC", 1, /* Alias of DESCENDING */ + DESC, "DESCENDING", 1, + DISTINCT, "DISTINCT", 1, + DO, "DO", 1, + DOMAIN, "DOMAIN", 1, + KW_DOUBLE, "DOUBLE", 1, + DROP, "DROP", 1, + ELSE, "ELSE", 1, + END, "END", 1, + ENTRY_POINT, "ENTRY_POINT", 1, + ESCAPE, "ESCAPE", 1, + EXCEPTION, "EXCEPTION", 1, + EXECUTE, "EXECUTE", 1, + EXISTS, "EXISTS", 1, + EXIT, "EXIT", 1, + EXTERNAL, "EXTERNAL", 1, + EXTRACT, "EXTRACT", 2, + KW_FILE, "FILE", 1, + FILTER, "FILTER", 1, + KW_FLOAT, "FLOAT", 1, + FOR, "FOR", 1, + FOREIGN, "FOREIGN", 1, + FREE_IT, "FREE_IT", 1, + FROM, "FROM", 1, + FULL, "FULL", 1, + FUNCTION, "FUNCTION", 1, + GDSCODE, "GDSCODE", 1, + GENERATOR, "GENERATOR", 1, + GEN_ID, "GEN_ID", 1, + GRANT, "GRANT", 1, + GROUP, "GROUP", 1, + GROUP_COMMIT_WAIT, "GROUP_COMMIT_WAIT_TIME", 1, + HAVING, "HAVING", 1, + HOUR, "HOUR", 2, + IF, "IF", 1, + IN, "IN", 1, + INACTIVE, "INACTIVE", 1, + INDEX, "INDEX", 1, + INNER, "INNER", 1, + INPUT_TYPE, "INPUT_TYPE", 1, + INSERT, "INSERT", 1, + KW_INT, "INT", 1, + INTEGER, "INTEGER", 1, + INTO, "INTO", 1, + IS, "IS", 1, + ISOLATION, "ISOLATION", 1, + JOIN, "JOIN", 1, + KEY, "KEY", 1, + LEFT, "LEFT", 1, + LENGTH, "LENGTH", 1, + LEVEL, "LEVEL", 1, + LIKE, "LIKE", 1, + LOGFILE, "LOGFILE", 1, + LOG_BUF_SIZE, "LOG_BUFFER_SIZE", 1, + KW_LONG, "LONG", 1, + MANUAL, "MANUAL", 1, + MAXIMUM, "MAX", 1, + MAX_SEGMENT, "MAXIMUM_SEGMENT", 1, + MERGE, "MERGE", 1, + MESSAGE, "MESSAGE", 1, + MINIMUM, "MIN", 1, + MINUTE, "MINUTE", 2, + MODULE_NAME, "MODULE_NAME", 1, + MONTH, "MONTH", 2, + NAMES, "NAMES", 1, + NATIONAL, "NATIONAL", 1, + NATURAL, "NATURAL", 1, + NCHAR, "NCHAR", 1, + NO, "NO", 1, + NOT, "NOT", 1, + KW_NULL, "NULL", 1, + KW_NUMERIC, "NUMERIC", 1, + NUM_LOG_BUFS, "NUM_LOG_BUFFERS", 1, + OF, "OF", 1, + ON, "ON", 1, + ONLY, "ONLY", 1, + OPTION, "OPTION", 1, + OR, "OR", 1, + ORDER, "ORDER", 1, + OUTER, "OUTER", 1, + OUTPUT_TYPE, "OUTPUT_TYPE", 1, + OVERFLOW, "OVERFLOW", 1, + PAGE, "PAGE", 1, + PAGES, "PAGES", 1, + PAGE_SIZE, "PAGE_SIZE", 1, + PARAMETER, "PARAMETER", 1, + PASSWORD, "PASSWORD", 1, + PLAN, "PLAN", 1, + POSITION, "POSITION", 1, + POST_EVENT, "POST_EVENT", 1, + PRECISION, "PRECISION", 1, + PRIMARY, "PRIMARY", 1, + PRIVILEGES, "PRIVILEGES", 1, + PROCEDURE, "PROCEDURE", 1, + PROTECTED, "PROTECTED", 1, + RAW_PARTITIONS, "RAW_PARTITIONS", 1, + DB_KEY, "RDB$DB_KEY", 1, + READ, "READ", 1, + REAL, "REAL", 1, + VERSION, "RECORD_VERSION", 1, + REFERENCES, "REFERENCES", 1, RESERVING, "RESERV", 1, /* Alias of RESERVING */ + RESERVING, "RESERVING", 1, + RESTRICT, "RESTRICT", 1, + RETAIN, "RETAIN", 1, + RETURNING_VALUES, "RETURNING_VALUES", 1, + RETURNS, "RETURNS", 1, + REVOKE, "REVOKE", 1, + RIGHT, "RIGHT", 1, + ROLE, "ROLE", 1, ROLLBACK, "ROLLBACK", 1, DATABASE, "SCHEMA", 1, /* Alias of DATABASE */ + SECOND, "SECOND", 2, + SEGMENT, "SEGMENT", 1, + SELECT, "SELECT", 1, + SET, "SET", 1, + SHADOW, "SHADOW", 1, + SHARED, "SHARED", 1, + SINGULAR, "SINGULAR", 1, + SIZE, "SIZE", 1, + SMALLINT, "SMALLINT", 1, + SNAPSHOT, "SNAPSHOT", 1, + SOME, "SOME", 1, + SORT, "SORT", 1, + SQLCODE, "SQLCODE", 1, + STABILITY, "STABILITY", 1, STARTING, "STARTING", 1, STARTING, "STARTS", 1, /* Alias of STARTING */ + STATISTICS, "STATISTICS", 1, + SUB_TYPE, "SUB_TYPE", 1, + SUM, "SUM", 1, + SUSPEND, "SUSPEND", 1, + TABLE, "TABLE", 1, + THEN, "THEN", 1, + TIME, "TIME", 2, + TIMESTAMP, "TIMESTAMP", 2, + TO, "TO", 1, + TRANSACTION, "TRANSACTION", 1, + TRIGGER, "TRIGGER", 1, + TYPE, "TYPE", 2, + UNCOMMITTED, "UNCOMMITTED", 1, + UNION, "UNION", 1, + UNIQUE, "UNIQUE", 1, + UPDATE, "UPDATE", 1, + KW_UPPER, "UPPER", 1, + USER, "USER", 1, + KW_VALUE, "VALUE", 1, + VALUES, "VALUES", 1, + VARCHAR, "VARCHAR", 1, + VARIABLE, "VARIABLE", 1, + VARYING, "VARYING", 1, + VIEW, "VIEW", 1, + WAIT, "WAIT", 1, + WEEKDAY, "WEEKDAY", 2, + WHEN, "WHEN", 1, + WHERE, "WHERE", 1, + WHILE, "WHILE", 1, + WITH, "WITH", 1, + WORK, "WORK", 1, + WRITE, "WRITE", 1, YEAR, "YEAR", 2, YEARDAY, "YEARDAY", 2, NOT_LSS, "^<", 1, /* Alias of !< */ + NEQ, "^=", 1, /* Alias of != */ + NOT_GTR, "^>", 1, /* Alias of !> */ + CONCATENATE, "||", 1, NOT_LSS, "~<", 1, /* Alias of !< */ + NEQ, "~=", 1, /* Alias of != */ + NOT_GTR, "~>", 1, /* Alias of !> */ diff --git a/src/dsql/make.cpp b/src/dsql/make.cpp new file mode 100644 index 0000000000..2d053f4cb6 --- /dev/null +++ b/src/dsql/make.cpp @@ -0,0 +1,1266 @@ +/* + * PROGRAM: Dynamic SQL runtime support + * MODULE: make.c + * DESCRIPTION: Routines to make various blocks. + * + * The contents of this file are subject to the Interbase Public + * License Version 1.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy + * of the License at http://www.Inprise.com/IPL.html + * + * Software distributed under the License is distributed on an + * "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either express + * or implied. See the License for the specific language governing + * rights and limitations under the License. + * + * The Original Code was created by Inprise Corporation + * and its predecessors. Portions created by Inprise Corporation are + * Copyright (C) Inprise Corporation. + * + * All Rights Reserved. + * Contributor(s): ______________________________________. + */ + +#include +#include +#include "../dsql/dsql.h" +#include "../dsql/node.h" +#include "../dsql/sym.h" +#include "../include/jrd/gds.h" +#include "../jrd/intl.h" +#include "../jrd/constants.h" +#include "../jrd/align.h" +#include "../dsql/alld_proto.h" +#include "../dsql/errd_proto.h" +#include "../dsql/hsh_proto.h" +#include "../dsql/make_proto.h" +#include "../jrd/thd_proto.h" +#include "../jrd/dsc_proto.h" +#include "../jrd/cvt_proto.h" + + +extern "C" { + + +ASSERT_FILENAME /* declare things assert() needs */ +/* InterBase provides transparent conversion from string to date in + * contexts where it makes sense. This macro checks a descriptor to + * see if it is something that *could* represent a date value + */ +#define COULD_BE_DATE(d) (DTYPE_IS_DATE((d).dsc_dtype) || ((d).dsc_dtype <= dtype_any_text)) +/* One of d1,d2 is time, the other is date */ +#define IS_DATE_AND_TIME(d1,d2) \ + ((((d1).dsc_dtype==dtype_sql_time)&&((d2).dsc_dtype==dtype_sql_date)) || \ + (((d2).dsc_dtype==dtype_sql_time)&&((d1).dsc_dtype==dtype_sql_date))) + NOD MAKE_constant(STR constant, int numeric_flag) +{ +/************************************** + * + * M A K E _ c o n s t a n t + * + ************************************** + * + * Functional description + * Make a constant node. + * + **************************************/ + NOD node; + TSQL tdsql; + + tdsql = GET_THREAD_DATA; + + node = (NOD) ALLOCDV(type_nod, + (numeric_flag == CONSTANT_TIMESTAMP || + numeric_flag == CONSTANT_SINT64) ? 2 : 1); + node->nod_type = nod_constant; + + if (numeric_flag == CONSTANT_SLONG) { + node->nod_desc.dsc_dtype = dtype_long; + node->nod_desc.dsc_length = sizeof(SLONG); + node->nod_desc.dsc_scale = 0; + node->nod_desc.dsc_sub_type = 0; + node->nod_desc.dsc_address = (UCHAR *) node->nod_arg; + node->nod_arg[0] = (NOD) constant; + } + else if (numeric_flag == CONSTANT_DOUBLE) { + DEV_BLKCHK(constant, type_str); + + /* This is a numeric value which is transported to the engine as + * a string. The engine will convert it. Use dtype_double so that + the engine can distinguish it from an actual string. + Note: Due to the size of dsc_scale we are limited to numeric + constants of less than 256 bytes. + */ + node->nod_desc.dsc_dtype = dtype_double; + node->nod_desc.dsc_scale = static_cast < char >(constant->str_length); /* Scale has no use for double */ + node->nod_desc.dsc_sub_type = 0; + node->nod_desc.dsc_length = sizeof(double); + node->nod_desc.dsc_address = constant->str_data; + node->nod_desc.dsc_ttype = ttype_ascii; + node->nod_arg[0] = (NOD) constant; + } + else if (numeric_flag == CONSTANT_SINT64) { + /* We convert the string to an int64. We treat the two adjacent + 32-bit words node->nod_arg[0] and node->nod_arg[1] as a + 64-bit integer: if we ever port to a platform which requires + 8-byte alignment of int64 data, we will have to force 8-byte + alignment of node->nod_arg, which is now only guaranteed + 4-byte alignment. -- ChrisJ 1999-02-20 */ + + UINT64 value = 0; + BOOLEAN point_seen = 0; + UCHAR *p = constant->str_data; + + node->nod_desc.dsc_dtype = dtype_int64; + node->nod_desc.dsc_length = sizeof(SINT64); + node->nod_desc.dsc_scale = 0; + node->nod_desc.dsc_sub_type = 0; + node->nod_desc.dsc_address = (UCHAR *) node->nod_arg; + + /* Now convert the string to an int64. We can omit testing for + overflow, because we would never have gotten here if yylex + hadn't recognized the string as a valid 64-bit integer value. + We *might* have "9223372936854775808", which works an an int64 + only if preceded by a '-', but that issue is handled in GEN_expr, + and need not be addressed here. */ + + while (isdigit(*p)) + value = 10 * value + (*(p++) - '0'); + if (*p++ = '.') { + while (isdigit(*p)) { + value = 10 * value + (*p++ - '0'); + node->nod_desc.dsc_scale--; + } + } + + *(UINT64 *) (node->nod_desc.dsc_address) = value; + + } + else if (numeric_flag == CONSTANT_DATE || + numeric_flag == CONSTANT_TIME || + numeric_flag == CONSTANT_TIMESTAMP) { + DSC tmp; + + /* Setup the constant's descriptor */ + + switch (numeric_flag) { + case CONSTANT_DATE: + node->nod_desc.dsc_dtype = dtype_sql_date; + break; + case CONSTANT_TIME: + node->nod_desc.dsc_dtype = dtype_sql_time; + break; + case CONSTANT_TIMESTAMP: + node->nod_desc.dsc_dtype = dtype_timestamp; + break; + }; + node->nod_desc.dsc_sub_type = 0; + node->nod_desc.dsc_scale = 0; + node->nod_desc.dsc_length = type_lengths[node->nod_desc.dsc_dtype]; + node->nod_desc.dsc_address = (UCHAR *) node->nod_arg; + + /* Set up a descriptor to point to the string */ + + tmp.dsc_dtype = dtype_text; + tmp.dsc_scale = 0; + tmp.dsc_flags = 0; + tmp.dsc_ttype = ttype_ascii; + tmp.dsc_length = static_cast < USHORT > (constant->str_length); + tmp.dsc_address = constant->str_data; + + /* Now invoke the string_to_date/time/timestamp routines */ + + CVT_move(&tmp, &node->nod_desc, + reinterpret_cast < void (*)() > (ERRD_post)); + } + else { + assert(numeric_flag == CONSTANT_STRING); + DEV_BLKCHK(constant, type_str); + + node->nod_desc.dsc_dtype = dtype_text; + node->nod_desc.dsc_sub_type = 0; + node->nod_desc.dsc_scale = 0; + node->nod_desc.dsc_length = + static_cast < USHORT > (constant->str_length); + node->nod_desc.dsc_address = constant->str_data; + node->nod_desc.dsc_ttype = ttype_dynamic; + /* carry a pointer to the constant to resolve character set in pass1 */ + node->nod_arg[0] = (NOD) constant; + } + + return node; +} + + +NOD MAKE_str_constant(STR constant, SSHORT character_set) +{ +/************************************** + * + * M A K E _ s t r - c o n s t a n t + * + ************************************** + * + * Functional description + * Make a constant node when the + * character set ID is already known. + * + **************************************/ + NOD node; + TSQL tdsql; + + tdsql = GET_THREAD_DATA; + + node = (NOD) ALLOCDV(type_nod, 1); + node->nod_type = nod_constant; + + DEV_BLKCHK(constant, type_str); + + node->nod_desc.dsc_dtype = dtype_text; + node->nod_desc.dsc_sub_type = 0; + node->nod_desc.dsc_scale = 0; + node->nod_desc.dsc_length = static_cast < USHORT > (constant->str_length); + node->nod_desc.dsc_address = constant->str_data; + node->nod_desc.dsc_ttype = character_set; +/* carry a pointer to the constant to resolve character set in pass1 */ + node->nod_arg[0] = (NOD) constant; + + return node; +} + + +STR MAKE_cstring(CONST SCHAR * str) +{ +/************************************** + * + * M A K E _ c s t r i n g + * + ************************************** + * + * Functional description + * Make a string node for a string whose + * length is not known, but is null-terminated. + * + **************************************/ + + return MAKE_string((UCHAR *) str, strlen(str)); +} + + +void MAKE_desc( DSC * desc, NOD node) +{ +/************************************** + * + * M A K E _ d e s c + * + ************************************** + * + * Functional description + * Make a descriptor from input node. + * + **************************************/ + DSC desc1, desc2; + USHORT dtype, dtype1, dtype2; + MAP map; + CTX context; + DSQL_REL relation; + UDF udf; + FLD field; + + DEV_BLKCHK(node, type_nod); + +/* If we already know the datatype, don't worry about anything */ + + if (node->nod_desc.dsc_dtype) { + *desc = node->nod_desc; + return; + } + + switch (node->nod_type) { + case nod_agg_count: +/* count2 + case nod_agg_distinct: +*/ + desc->dsc_dtype = dtype_long; + desc->dsc_length = sizeof(SLONG); + desc->dsc_sub_type = 0; + desc->dsc_scale = 0; + desc->dsc_flags = 0; + return; + + case nod_map: + map = (MAP) node->nod_arg[e_map_map]; + MAKE_desc(desc, map->map_node); + return; + + case nod_agg_min: + case nod_agg_max: + MAKE_desc(desc, node->nod_arg[0]); + desc->dsc_flags = DSC_nullable; + return; + + case nod_agg_average: + MAKE_desc(desc, node->nod_arg[0]); + desc->dsc_flags = DSC_nullable; + dtype = desc->dsc_dtype; + if (!DTYPE_CAN_AVERAGE(dtype)) + ERRD_post(gds_expression_eval_err, 0); + return; + + case nod_agg_average2: + MAKE_desc(desc, node->nod_arg[0]); + desc->dsc_flags = DSC_nullable; + dtype = desc->dsc_dtype; + if (!DTYPE_CAN_AVERAGE(dtype)) + ERRD_post(gds_expression_eval_err, 0); + if (DTYPE_IS_EXACT(dtype)) { + desc->dsc_dtype = dtype_int64; + desc->dsc_length = sizeof(SINT64); + node->nod_flags |= NOD_COMP_DIALECT; + } + else { + desc->dsc_dtype = dtype_double; + desc->dsc_length = sizeof(double); + } + return; + + case nod_agg_total: + MAKE_desc(desc, node->nod_arg[0]); + if (desc->dsc_dtype == dtype_short) { + desc->dsc_dtype = dtype_long; + desc->dsc_length = sizeof(SLONG); + } + else if (desc->dsc_dtype == dtype_int64) { + desc->dsc_dtype = dtype_double; + desc->dsc_length = sizeof(double); + } + desc->dsc_flags = DSC_nullable; + return; + + case nod_agg_total2: + MAKE_desc(desc, node->nod_arg[0]); + dtype = desc->dsc_dtype; + if (DTYPE_IS_EXACT(dtype)) { + desc->dsc_dtype = dtype_int64; + desc->dsc_length = sizeof(SINT64); + node->nod_flags |= NOD_COMP_DIALECT; + } + else { + desc->dsc_dtype = dtype_double; + desc->dsc_length = sizeof(double); + } + desc->dsc_flags = DSC_nullable; + return; + + case nod_concatenate: + MAKE_desc(&desc1, node->nod_arg[0]); + MAKE_desc(&desc2, node->nod_arg[1]); + desc->dsc_scale = 0; + desc->dsc_dtype = dtype_varying; + if (desc1.dsc_dtype <= dtype_any_text) + desc->dsc_ttype = desc1.dsc_ttype; + else + desc->dsc_ttype = ttype_ascii; + desc->dsc_length = sizeof(USHORT) + + DSC_string_length(&desc1) + DSC_string_length(&desc2); + desc->dsc_flags = (desc1.dsc_flags | desc2.dsc_flags) & DSC_nullable; + return; + + case nod_upcase: + MAKE_desc(&desc1, node->nod_arg[0]); + if (desc1.dsc_dtype <= dtype_any_text) { + *desc = desc1; + return; + } + desc->dsc_dtype = dtype_varying; + desc->dsc_scale = 0; + desc->dsc_ttype = ttype_ascii; + desc->dsc_length = sizeof(USHORT) + DSC_string_length(&desc1); + desc->dsc_flags = desc1.dsc_flags & DSC_nullable; + return; + + case nod_cast: + field = (FLD) node->nod_arg[e_cast_target]; + MAKE_desc_from_field(desc, field); + MAKE_desc(&desc1, node->nod_arg[e_cast_source]); + desc->dsc_flags = desc1.dsc_flags & DSC_nullable; + return; + +#ifdef DEV_BUILD + case nod_collate: + ERRD_bugcheck("Not expecting nod_collate in dsql/MAKE_desc"); + return; +#endif + + case nod_add: + case nod_subtract: + MAKE_desc(&desc1, node->nod_arg[0]); + MAKE_desc(&desc2, node->nod_arg[1]); + dtype1 = desc1.dsc_dtype; + dtype2 = desc2.dsc_dtype; + + if (dtype_int64 == dtype1) + dtype1 = dtype_double; + if (dtype_int64 == dtype2) + dtype2 = dtype_double; + + dtype = MAX(dtype1, dtype2); + + if (DTYPE_IS_BLOB(dtype)) + ERRD_post(gds_sqlerr, gds_arg_number, (SLONG) - 607, + gds_arg_gds, gds_dsql_no_blob_array, 0); + + desc->dsc_flags = (desc1.dsc_flags | desc2.dsc_flags) & DSC_nullable; + switch (dtype) { + case dtype_sql_time: + case dtype_sql_date: + /* Forbid +- */ + if (IS_DTYPE_ANY_TEXT(desc1.dsc_dtype) || + IS_DTYPE_ANY_TEXT(desc2.dsc_dtype)) + ERRD_post(gds_expression_eval_err, 0); + + case dtype_timestamp: + + /* Allow +- (historical) */ + if (COULD_BE_DATE(desc1) && COULD_BE_DATE(desc2)) { + if (node->nod_type == nod_subtract) { + /* - */ + + /* Legal permutations are: + - + - + - + - +